1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 /*
6 ** Pathname subroutines.
7 */
8 #if defined(FREEBSD) || defined(BSDI) || defined(DARWIN)
9 #include <sys/types.h>
10 #endif /* FREEBSD */
11 #include <dirent.h>
12 #include <errno.h>
13 #include <stdarg.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <unistd.h>
18 #include <sys/types.h>
19 #include <sys/stat.h>
20 #include "pathsub.h"
21 #ifdef USE_REENTRANT_LIBC
22 #include "libc_r.h"
23 #endif /* USE_REENTRANT_LIBC */
24 
25 char *program;
26 
27 void
fail(char * format,...)28 fail(char *format, ...)
29 {
30     int error;
31     va_list ap;
32 
33 #ifdef USE_REENTRANT_LIBC
34     R_STRERROR_INIT_R();
35 #endif
36 
37     error = errno;
38     fprintf(stderr, "%s: ", program);
39     va_start(ap, format);
40     vfprintf(stderr, format, ap);
41     va_end(ap);
42     if (error) {
43 
44 #ifdef USE_REENTRANT_LIBC
45     R_STRERROR_R(errno);
46 	fprintf(stderr, ": %s", r_strerror_r);
47 #else
48 	fprintf(stderr, ": %s", strerror(errno));
49 #endif
50     }
51 
52     putc('\n', stderr);
53     abort();
54     exit(1);
55 }
56 
57 char *
getcomponent(char * path,char * name)58 getcomponent(char *path, char *name)
59 {
60     if (*path == '\0')
61 	return 0;
62     if (*path == '/') {
63 	*name++ = '/';
64     } else {
65 	do {
66 	    *name++ = *path++;
67 	} while (*path != '/' && *path != '\0');
68     }
69     *name = '\0';
70     while (*path == '/')
71 	path++;
72     return path;
73 }
74 
75 #ifdef UNIXWARE
76 /* The static buffer in Unixware's readdir is too small. */
readdir(DIR * d)77 struct dirent * readdir(DIR *d)
78 {
79     static struct dirent *buf = NULL;
80 #define MAX_PATH_LEN 1024
81 
82     if (buf == NULL)
83 	buf = (struct dirent *)xmalloc(sizeof(struct dirent) + MAX_PATH_LEN) ;
84     return readdir_r(d, buf);
85 }
86 #endif
87 
88 /* APPARENT BUG - ignores argument "dir", uses ".." instead. */
89 char *
ino2name(ino_t ino,char * dir)90 ino2name(ino_t ino, char *dir)
91 {
92     DIR *dp;
93     struct dirent *ep;
94     char *name;
95 
96     dp = opendir("..");		/* XXX */
97     if (!dp)
98 	fail("cannot read parent directory");
99     for (;;) {
100 	if (!(ep = readdir(dp)))
101 	    fail("cannot find current directory");
102 	if (ep->d_ino == ino)
103 	    break;
104     }
105     name = xstrdup(ep->d_name);
106     closedir(dp);
107     return name;
108 }
109 
110 void *
xmalloc(size_t size)111 xmalloc(size_t size)
112 {
113     void *p;
114 
115     if (size <= 0)
116 	fail("attempted to allocate %u bytes", size);
117     p = malloc(size);
118     if (!p)
119 	fail("cannot allocate %u bytes", size);
120     return p;
121 }
122 
123 char *
xstrdup(char * s)124 xstrdup(char *s)
125 {
126     if (!s || !s[0])
127 	fail("Null pointer or empty string passed to xstrdup()");
128     return strcpy((char*)xmalloc(strlen(s) + 1), s);
129 }
130 
131 char *
xbasename(char * path)132 xbasename(char *path)
133 {
134     char *cp;
135 
136     if (!path || !path[0])
137 	fail("Null pointer or empty string passed to xbasename()");
138     while ((cp = strrchr(path, '/')) && cp[1] == '\0')
139 	*cp = '\0';
140     if (!cp) return path;
141     return cp + 1;
142 }
143 
144 void
xchdir(char * dir)145 xchdir(char *dir)
146 {
147     if (!dir || !dir[0])
148 	fail("Null pointer or empty string passed to xchdir()");
149     if (chdir(dir) < 0)
150 	fail("cannot change directory to %s", dir);
151 }
152 
153 int
relatepaths(char * from,char * to,char * outpath)154 relatepaths(char *from, char *to, char *outpath)
155 {
156     char *cp, *cp2;
157     int len;
158     char buf[NAME_MAX];
159 
160     if (!from || *from != '/')
161 	fail("relatepaths: from path does not start with /");
162     if (!to || *to != '/')
163 	fail("relatepaths: to   path does not start with /");
164 
165     for (cp = to, cp2 = from; *cp == *cp2; cp++, cp2++)
166 	if (*cp == '\0')
167 	    break;
168     while (cp[-1] != '/')
169 	cp--, cp2--;
170     if (cp - 1 == to) {
171 	/* closest common ancestor is /, so use full pathname */
172 	len = strlen(strcpy(outpath, to));
173 	if (outpath[len] != '/') {
174 	    outpath[len++] = '/';
175 	    outpath[len] = '\0';
176 	}
177     } else {
178 	len = 0;
179 	while ((cp2 = getcomponent(cp2, buf)) != 0) {
180 	    strcpy(outpath + len, "../");
181 	    len += 3;
182 	}
183 	while ((cp = getcomponent(cp, buf)) != 0) {
184 	    sprintf(outpath + len, "%s/", buf);
185 	    len += strlen(outpath + len);
186 	}
187     }
188     return len;
189 }
190 
191 void
reversepath(char * inpath,char * name,int len,char * outpath)192 reversepath(char *inpath, char *name, int len, char *outpath)
193 {
194     char *cp, *cp2;
195     char buf[NAME_MAX];
196     struct stat sb;
197 
198     cp = strcpy(outpath + PATH_MAX - (len + 1), name);
199     cp2 = inpath;
200     while ((cp2 = getcomponent(cp2, buf)) != 0) {
201 	if (strcmp(buf, ".") == 0)
202 	    continue;
203 	if (strcmp(buf, "..") == 0) {
204 	    if (stat(".", &sb) < 0)
205 		fail("cannot stat current directory");
206 	    name = ino2name(sb.st_ino, "..");
207 	    len = strlen(name);
208 	    cp -= len + 1;
209 	    strcpy(cp, name);
210 	    cp[len] = '/';
211 	    free(name);
212 	    xchdir("..");
213 	} else {
214 	    cp -= 3;
215 	    strncpy(cp, "../", 3);
216 	    xchdir(buf);
217 	}
218     }
219     strcpy(outpath, cp);
220 }
221 
222 void
diagnosePath(const char * path)223 diagnosePath(const char * path)
224 {
225     char *	myPath;
226     char *      slash;
227     int		rv;
228     struct stat sb;
229     char 	buf[BUFSIZ];
230 
231     if (!path || !path[0])
232 	fail("Null pointer or empty string passed to mkdirs()");
233     myPath = strdup(path);
234     if (!myPath)
235 	fail("strdup() failed!");
236     do {
237     	rv = lstat(myPath, &sb);
238 	if (rv < 0) {
239 	    perror(myPath);
240 	} else if (S_ISLNK(sb.st_mode)) {
241 	    rv = readlink(myPath, buf, sizeof(buf) - 1);
242 	    if (rv < 0) {
243 	    	perror("readlink");
244 		buf[0] = 0;
245 	    } else {
246 	    	buf[rv] = 0;
247 	    }
248 	    fprintf(stderr, "%s is a link to %s\n", myPath, buf);
249 	} else if (S_ISDIR(sb.st_mode)) {
250 	    fprintf(stderr, "%s is a directory\n", myPath);
251 	    rv = access(myPath, X_OK);
252 	    if (rv < 0) {
253 	    	fprintf(stderr, "%s: no search permission\n", myPath);
254 	    }
255 	} else {
256 	    fprintf(stderr, "%s is a file !?!\n", myPath);
257 	    rv = access(myPath, F_OK);
258 	    if (rv < 0) {
259 	    	fprintf(stderr, "%s does not exist\n", myPath);
260 	    }
261 	}
262 
263 	/* chop path off one level. */
264 	slash = strrchr(myPath, '/');
265 	if (!slash)
266 	    slash = strrchr(myPath, '\\');
267 	if (!slash)
268 	    slash = myPath;
269 	*slash = 0;
270     } while (myPath[0]);
271     free(myPath);
272 }
273