michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* michael@0: ** Pathname subroutines. michael@0: */ michael@0: #include michael@0: #if defined(FREEBSD) || defined(BSDI) || defined(DARWIN) michael@0: #include michael@0: #endif /* FREEBSD */ michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include "pathsub.h" michael@0: #ifdef USE_REENTRANT_LIBC michael@0: #include "libc_r.h" michael@0: #endif /* USE_REENTRANT_LIBC */ michael@0: michael@0: char *program; michael@0: michael@0: void michael@0: fail(char *format, ...) michael@0: { michael@0: int error; michael@0: va_list ap; michael@0: michael@0: #ifdef USE_REENTRANT_LIBC michael@0: R_STRERROR_INIT_R(); michael@0: #endif michael@0: michael@0: error = errno; michael@0: fprintf(stderr, "%s: ", program); michael@0: va_start(ap, format); michael@0: vfprintf(stderr, format, ap); michael@0: va_end(ap); michael@0: if (error) { michael@0: michael@0: #ifdef USE_REENTRANT_LIBC michael@0: R_STRERROR_R(errno); michael@0: fprintf(stderr, ": %s", r_strerror_r); michael@0: #else michael@0: fprintf(stderr, ": %s", strerror(errno)); michael@0: #endif michael@0: } michael@0: michael@0: putc('\n', stderr); michael@0: abort(); michael@0: exit(1); michael@0: } michael@0: michael@0: char * michael@0: getcomponent(char *path, char *name) michael@0: { michael@0: if (*path == '\0') michael@0: return 0; michael@0: if (*path == '/') { michael@0: *name++ = '/'; michael@0: } else { michael@0: do { michael@0: *name++ = *path++; michael@0: } while (*path != '/' && *path != '\0'); michael@0: } michael@0: *name = '\0'; michael@0: while (*path == '/') michael@0: path++; michael@0: return path; michael@0: } michael@0: michael@0: #ifdef UNIXWARE michael@0: /* The static buffer in Unixware's readdir is too small. */ michael@0: struct dirent * readdir(DIR *d) michael@0: { michael@0: static struct dirent *buf = NULL; michael@0: #define MAX_PATH_LEN 1024 michael@0: michael@0: if (buf == NULL) michael@0: buf = (struct dirent *)xmalloc(sizeof(struct dirent) + MAX_PATH_LEN) ; michael@0: return readdir_r(d, buf); michael@0: } michael@0: #endif michael@0: michael@0: /* APPARENT BUG - ignores argument "dir", uses ".." instead. */ michael@0: char * michael@0: ino2name(ino_t ino, char *dir) michael@0: { michael@0: DIR *dp; michael@0: struct dirent *ep; michael@0: char *name; michael@0: michael@0: dp = opendir(".."); /* XXX */ michael@0: if (!dp) michael@0: fail("cannot read parent directory"); michael@0: for (;;) { michael@0: if (!(ep = readdir(dp))) michael@0: fail("cannot find current directory"); michael@0: if (ep->d_ino == ino) michael@0: break; michael@0: } michael@0: name = xstrdup(ep->d_name); michael@0: closedir(dp); michael@0: return name; michael@0: } michael@0: michael@0: void * michael@0: xmalloc(size_t size) michael@0: { michael@0: void *p; michael@0: michael@0: if (size <= 0) michael@0: fail("attempted to allocate %u bytes", size); michael@0: p = malloc(size); michael@0: if (!p) michael@0: fail("cannot allocate %u bytes", size); michael@0: return p; michael@0: } michael@0: michael@0: char * michael@0: xstrdup(char *s) michael@0: { michael@0: if (!s || !s[0]) michael@0: fail("Null pointer or empty string passed to xstrdup()"); michael@0: return strcpy((char*)xmalloc(strlen(s) + 1), s); michael@0: } michael@0: michael@0: char * michael@0: xbasename(char *path) michael@0: { michael@0: char *cp; michael@0: michael@0: if (!path || !path[0]) michael@0: fail("Null pointer or empty string passed to xbasename()"); michael@0: while ((cp = strrchr(path, '/')) && cp[1] == '\0') michael@0: *cp = '\0'; michael@0: if (!cp) return path; michael@0: return cp + 1; michael@0: } michael@0: michael@0: void michael@0: xchdir(char *dir) michael@0: { michael@0: if (!dir || !dir[0]) michael@0: fail("Null pointer or empty string passed to xchdir()"); michael@0: if (chdir(dir) < 0) michael@0: fail("cannot change directory to %s", dir); michael@0: } michael@0: michael@0: int michael@0: relatepaths(char *from, char *to, char *outpath) michael@0: { michael@0: char *cp, *cp2; michael@0: int len; michael@0: char buf[NAME_MAX]; michael@0: michael@0: assert(*from == '/' && *to == '/'); michael@0: if (!from || *from != '/') michael@0: fail("relatepaths: from path does not start with /"); michael@0: if (!to || *to != '/') michael@0: fail("relatepaths: to path does not start with /"); michael@0: michael@0: for (cp = to, cp2 = from; *cp == *cp2; cp++, cp2++) michael@0: if (*cp == '\0') michael@0: break; michael@0: while (cp[-1] != '/') michael@0: cp--, cp2--; michael@0: if (cp - 1 == to) { michael@0: /* closest common ancestor is /, so use full pathname */ michael@0: len = strlen(strcpy(outpath, to)); michael@0: if (outpath[len] != '/') { michael@0: outpath[len++] = '/'; michael@0: outpath[len] = '\0'; michael@0: } michael@0: } else { michael@0: len = 0; michael@0: while ((cp2 = getcomponent(cp2, buf)) != 0) { michael@0: strcpy(outpath + len, "../"); michael@0: len += 3; michael@0: } michael@0: while ((cp = getcomponent(cp, buf)) != 0) { michael@0: sprintf(outpath + len, "%s/", buf); michael@0: len += strlen(outpath + len); michael@0: } michael@0: } michael@0: return len; michael@0: } michael@0: michael@0: void michael@0: reversepath(char *inpath, char *name, int len, char *outpath) michael@0: { michael@0: char *cp, *cp2; michael@0: char buf[NAME_MAX]; michael@0: struct stat sb; michael@0: michael@0: cp = strcpy(outpath + PATH_MAX - (len + 1), name); michael@0: cp2 = inpath; michael@0: while ((cp2 = getcomponent(cp2, buf)) != 0) { michael@0: if (strcmp(buf, ".") == 0) michael@0: continue; michael@0: if (strcmp(buf, "..") == 0) { michael@0: if (stat(".", &sb) < 0) michael@0: fail("cannot stat current directory"); michael@0: name = ino2name(sb.st_ino, ".."); michael@0: len = strlen(name); michael@0: cp -= len + 1; michael@0: strcpy(cp, name); michael@0: cp[len] = '/'; michael@0: free(name); michael@0: xchdir(".."); michael@0: } else { michael@0: cp -= 3; michael@0: strncpy(cp, "../", 3); michael@0: xchdir(buf); michael@0: } michael@0: } michael@0: strcpy(outpath, cp); michael@0: } michael@0: michael@0: void michael@0: diagnosePath(const char * path) michael@0: { michael@0: char * myPath; michael@0: char * slash; michael@0: int rv; michael@0: struct stat sb; michael@0: char buf[BUFSIZ]; michael@0: michael@0: if (!path || !path[0]) michael@0: fail("Null pointer or empty string passed to mkdirs()"); michael@0: myPath = strdup(path); michael@0: if (!myPath) michael@0: fail("strdup() failed!"); michael@0: do { michael@0: rv = lstat(myPath, &sb); michael@0: if (rv < 0) { michael@0: perror(myPath); michael@0: } else if (S_ISLNK(sb.st_mode)) { michael@0: rv = readlink(myPath, buf, sizeof(buf) - 1); michael@0: if (rv < 0) { michael@0: perror("readlink"); michael@0: buf[0] = 0; michael@0: } else { michael@0: buf[rv] = 0; michael@0: } michael@0: fprintf(stderr, "%s is a link to %s\n", myPath, buf); michael@0: } else if (S_ISDIR(sb.st_mode)) { michael@0: fprintf(stderr, "%s is a directory\n", myPath); michael@0: rv = access(myPath, X_OK); michael@0: if (rv < 0) { michael@0: fprintf(stderr, "%s: no search permission\n", myPath); michael@0: } michael@0: } else { michael@0: fprintf(stderr, "%s is a file !?!\n", myPath); michael@0: rv = access(myPath, F_OK); michael@0: if (rv < 0) { michael@0: fprintf(stderr, "%s does not exist\n", myPath); michael@0: } michael@0: } michael@0: michael@0: /* chop path off one level. */ michael@0: slash = strrchr(myPath, '/'); michael@0: if (!slash) michael@0: slash = strrchr(myPath, '\\'); michael@0: if (!slash) michael@0: slash = myPath; michael@0: *slash = 0; michael@0: } while (myPath[0]); michael@0: free(myPath); michael@0: }