Commit | Line | Data |
---|---|---|
ecc449eb KB |
1 | /*- |
2 | * Copyright (c) 1980, 1991 The Regents of the University of California. | |
3 | * All rights reserved. | |
4 | * | |
5 | * %sccs.include.redist.c% | |
b79f4fa9 DF |
6 | */ |
7 | ||
35371dec | 8 | #ifndef lint |
37999c01 | 9 | static char sccsid[] = "@(#)dir.c 5.20 (Berkeley) %G%"; |
ecc449eb | 10 | #endif /* not lint */ |
106427f7 | 11 | |
b9c4f741 KB |
12 | #include <sys/param.h> |
13 | #include <sys/stat.h> | |
14 | #include <errno.h> | |
15 | #include <stdlib.h> | |
16 | #include <string.h> | |
17 | #include <unistd.h> | |
4df6491c CZ |
18 | #if __STDC__ |
19 | # include <stdarg.h> | |
20 | #else | |
21 | # include <varargs.h> | |
22 | #endif | |
b9c4f741 | 23 | |
4d7b2685 KB |
24 | #include "csh.h" |
25 | #include "dir.h" | |
26 | #include "extern.h" | |
27 | ||
28 | /* Directory management. */ | |
106427f7 | 29 | |
c28e64a2 | 30 | static struct directory |
0aec749d | 31 | *dfind __P((Char *)); |
c28e64a2 CL |
32 | static Char *dfollow __P((Char *)); |
33 | static void printdirs __P((void)); | |
34 | static Char *dgoto __P((Char *)); | |
35 | static void dnewcwd __P((struct directory *)); | |
36 | static void dset __P((Char *)); | |
6e37afca KB |
37 | |
38 | struct directory dhead; /* "head" of loop */ | |
39 | int printd; /* force name to be printed */ | |
40 | ||
41 | static int dirflag = 0; | |
106427f7 BJ |
42 | |
43 | /* | |
44 | * dinit - initialize current working directory | |
45 | */ | |
6e37afca | 46 | void |
106427f7 | 47 | dinit(hp) |
6e37afca | 48 | Char *hp; |
106427f7 | 49 | { |
6e37afca KB |
50 | register char *tcp; |
51 | register Char *cp; | |
52 | register struct directory *dp; | |
53 | char path[MAXPATHLEN]; | |
54 | static char *emsg = "csh: Trying to start from \"%s\"\n"; | |
106427f7 | 55 | |
6e37afca KB |
56 | /* Don't believe the login shell home, because it may be a symlink */ |
57 | tcp = getwd(path); /* see ngetwd.c for System V version */ | |
58 | if (tcp == NULL || *tcp == '\0') { | |
454c2aa3 | 59 | (void) fprintf(csherr, "csh: %s\n", path); |
6e37afca KB |
60 | if (hp && *hp) { |
61 | tcp = short2str(hp); | |
6e37afca | 62 | if (chdir(tcp) == -1) |
0aec749d | 63 | cp = NULL; |
6e37afca | 64 | else |
106427f7 | 65 | cp = hp; |
abf583a4 | 66 | (void) fprintf(csherr, emsg, vis_str(hp)); |
6e37afca KB |
67 | } |
68 | else | |
0aec749d CZ |
69 | cp = NULL; |
70 | if (cp == NULL) { | |
454c2aa3 | 71 | (void) fprintf(csherr, emsg, "/"); |
6e37afca KB |
72 | if (chdir("/") == -1) |
73 | /* I am not even try to print an error message! */ | |
74 | xexit(1); | |
75 | cp = SAVE("/"); | |
76 | } | |
77 | } | |
78 | else { | |
79 | struct stat swd, shp; | |
80 | ||
81 | /* | |
82 | * See if $HOME is the working directory we got and use that | |
83 | */ | |
84 | if (hp && *hp && | |
85 | stat(tcp, &swd) != -1 && stat(short2str(hp), &shp) != -1 && | |
86 | swd.st_dev == shp.st_dev && swd.st_ino == shp.st_ino) | |
87 | cp = hp; | |
010af04a | 88 | else { |
6e37afca KB |
89 | char *cwd; |
90 | ||
91 | /* | |
92 | * use PWD if we have it (for subshells) | |
93 | */ | |
37999c01 | 94 | if ((cwd = getenv("PWD")) != NULL) { |
6e37afca KB |
95 | if (stat(cwd, &shp) != -1 && swd.st_dev == shp.st_dev && |
96 | swd.st_ino == shp.st_ino) | |
97 | tcp = cwd; | |
98 | } | |
e779bce3 | 99 | cp = dcanon(SAVE(tcp), STRNULL); |
010af04a | 100 | } |
6e37afca KB |
101 | } |
102 | ||
103 | dp = (struct directory *) xcalloc(sizeof(struct directory), 1); | |
104 | dp->di_name = Strsave(cp); | |
105 | dp->di_count = 0; | |
106 | dhead.di_next = dhead.di_prev = dp; | |
107 | dp->di_next = dp->di_prev = &dhead; | |
108 | printd = 0; | |
109 | dnewcwd(dp); | |
110 | } | |
111 | ||
112 | static void | |
113 | dset(dp) | |
114 | Char *dp; | |
115 | { | |
116 | /* | |
117 | * Don't call set() directly cause if the directory contains ` or | |
c28e64a2 | 118 | * other junk characters glob will fail. |
6e37afca KB |
119 | */ |
120 | register Char **vec = (Char **) xmalloc((size_t) (2 * sizeof(Char **))); | |
121 | ||
122 | vec[0] = Strsave(dp); | |
123 | vec[1] = 0; | |
124 | setq(STRcwd, vec, &shvhed); | |
125 | Setenv(STRPWD, dp); | |
126 | } | |
127 | ||
128 | #define DIR_LONG 1 | |
129 | #define DIR_VERT 2 | |
130 | #define DIR_LINE 4 | |
131 | ||
132 | static void | |
133 | skipargs(v, str) | |
134 | Char ***v; | |
135 | char *str; | |
136 | { | |
137 | Char **n = *v, *s; | |
138 | ||
139 | dirflag = 0; | |
140 | for (n++; *n != NULL && (*n)[0] == '-'; n++) | |
141 | for (s = &((*n)[1]); *s; s++) | |
142 | switch (*s) { | |
143 | case 'l': | |
144 | dirflag |= DIR_LONG; | |
145 | break; | |
146 | case 'v': | |
147 | dirflag |= DIR_VERT; | |
148 | break; | |
149 | case 'n': | |
150 | dirflag |= DIR_LINE; | |
151 | break; | |
152 | default: | |
abf583a4 | 153 | stderror(ERR_DIRUS, vis_str(**v), str); |
6e37afca KB |
154 | break; |
155 | } | |
156 | *v = n; | |
106427f7 BJ |
157 | } |
158 | ||
159 | /* | |
160 | * dodirs - list all directories in directory loop | |
161 | */ | |
6e37afca | 162 | void |
454c2aa3 CZ |
163 | /*ARGSUSED*/ |
164 | dodirs(v, t) | |
165 | Char **v; | |
166 | struct command *t; | |
106427f7 | 167 | { |
6e37afca KB |
168 | skipargs(&v, ""); |
169 | ||
170 | if (*v != NULL) | |
171 | stderror(ERR_DIRUS, "dirs", ""); | |
172 | printdirs(); | |
173 | } | |
174 | ||
175 | static void | |
176 | printdirs() | |
177 | { | |
178 | register struct directory *dp; | |
179 | Char *s, *hp = value(STRhome); | |
180 | int idx, len, cur; | |
181 | ||
182 | if (*hp == '\0') | |
183 | hp = NULL; | |
184 | dp = dcwd; | |
185 | idx = 0; | |
186 | cur = 0; | |
187 | do { | |
188 | if (dp == &dhead) | |
189 | continue; | |
190 | if (dirflag & DIR_VERT) { | |
454c2aa3 | 191 | (void) fprintf(cshout, "%d\t", idx++); |
6e37afca KB |
192 | cur = 0; |
193 | } | |
194 | if (!(dirflag & DIR_LONG) && hp != NULL && !eq(hp, STRslash) && | |
ae24d422 | 195 | (len = Strlen(hp), Strncmp(hp, dp->di_name, len) == 0) && |
6c7cc8ce CZ |
196 | (dp->di_name[len] == '\0' || dp->di_name[len] == '/')) |
197 | len = Strlen(s = (dp->di_name + len)) + 2; | |
106427f7 | 198 | else |
6e37afca KB |
199 | len = Strlen(s = dp->di_name) + 1; |
200 | ||
201 | cur += len; | |
202 | if ((dirflag & DIR_LINE) && cur >= 80 - 1 && len < 80) { | |
454c2aa3 | 203 | (void) fprintf(cshout, "\n"); |
6e37afca KB |
204 | cur = len; |
205 | } | |
454c2aa3 | 206 | (void) fprintf(cshout, s != dp->di_name ? "~%s%c" : "%s%c", |
abf583a4 | 207 | vis_str(s), (dirflag & DIR_VERT) ? '\n' : ' '); |
6e37afca KB |
208 | } while ((dp = dp->di_prev) != dcwd); |
209 | if (!(dirflag & DIR_VERT)) | |
454c2aa3 | 210 | (void) fprintf(cshout, "\n"); |
106427f7 BJ |
211 | } |
212 | ||
6e37afca | 213 | void |
106427f7 | 214 | dtildepr(home, dir) |
6e37afca | 215 | register Char *home, *dir; |
106427f7 BJ |
216 | { |
217 | ||
6e37afca | 218 | if (!eq(home, STRslash) && prefix(home, dir)) |
abf583a4 | 219 | (void) fprintf(cshout, "~%s", vis_str(dir + Strlen(home))); |
6e37afca | 220 | else |
abf583a4 | 221 | (void) fprintf(cshout, "%s", vis_str(dir)); |
6e37afca KB |
222 | } |
223 | ||
224 | void | |
225 | dtilde() | |
226 | { | |
227 | struct directory *d = dcwd; | |
228 | ||
229 | do { | |
230 | if (d == &dhead) | |
231 | continue; | |
232 | d->di_name = dcanon(d->di_name, STRNULL); | |
233 | } while ((d = d->di_prev) != dcwd); | |
234 | ||
235 | dset(dcwd->di_name); | |
236 | } | |
237 | ||
238 | ||
239 | /* dnormalize(): | |
240 | * If the name starts with . or .. then we might need to normalize | |
241 | * it depending on the symbolic link flags | |
242 | */ | |
243 | Char * | |
244 | dnormalize(cp) | |
245 | Char *cp; | |
246 | { | |
247 | ||
248 | #define UC (unsigned char) | |
249 | #define ISDOT(c) (UC(c)[0] == '.' && ((UC(c)[1] == '\0') || (UC(c)[1] == '/'))) | |
250 | #define ISDOTDOT(c) (UC(c)[0] == '.' && ISDOT(&((c)[1]))) | |
251 | ||
252 | if ((unsigned char) cp[0] == '/') | |
253 | return (Strsave(cp)); | |
254 | ||
255 | if (adrof(STRignore_symlinks)) { | |
256 | int dotdot = 0; | |
257 | Char *dp, *cwd; | |
258 | ||
259 | cwd = (Char *) xmalloc((size_t) ((Strlen(dcwd->di_name) + 3) * | |
260 | sizeof(Char))); | |
261 | (void) Strcpy(cwd, dcwd->di_name); | |
262 | ||
263 | /* | |
264 | * Ignore . and count ..'s | |
265 | */ | |
266 | while (*cp) { | |
267 | if (ISDOT(cp)) { | |
268 | if (*++cp) | |
269 | cp++; | |
270 | } | |
271 | else if (ISDOTDOT(cp)) { | |
272 | dotdot++; | |
273 | cp += 2; | |
274 | if (*cp) | |
275 | cp++; | |
276 | } | |
277 | else | |
278 | break; | |
279 | } | |
280 | while (dotdot > 0) | |
281 | if ((dp = Strrchr(cwd, '/'))) { | |
282 | *dp = '\0'; | |
283 | dotdot--; | |
284 | } | |
285 | else | |
286 | break; | |
287 | ||
288 | if (*cp) { | |
289 | cwd[dotdot = Strlen(cwd)] = '/'; | |
290 | cwd[dotdot + 1] = '\0'; | |
291 | dp = Strspl(cwd, cp); | |
292 | xfree((ptr_t) cwd); | |
293 | return dp; | |
294 | } | |
295 | else { | |
296 | if (!*cwd) { | |
297 | cwd[0] = '/'; | |
298 | cwd[1] = '\0'; | |
299 | } | |
300 | return cwd; | |
301 | } | |
302 | } | |
303 | return Strsave(cp); | |
106427f7 BJ |
304 | } |
305 | ||
306 | /* | |
307 | * dochngd - implement chdir command. | |
308 | */ | |
6e37afca | 309 | void |
454c2aa3 CZ |
310 | /*ARGSUSED*/ |
311 | dochngd(v, t) | |
312 | Char **v; | |
313 | struct command *t; | |
106427f7 | 314 | { |
6e37afca KB |
315 | register Char *cp; |
316 | register struct directory *dp; | |
317 | ||
318 | skipargs(&v, " [<dir>]"); | |
319 | printd = 0; | |
320 | if (*v == NULL) { | |
321 | if ((cp = value(STRhome)) == NULL || *cp == 0) | |
322 | stderror(ERR_NAME | ERR_NOHOMEDIR); | |
323 | if (chdir(short2str(cp)) < 0) | |
324 | stderror(ERR_NAME | ERR_CANTCHANGE); | |
325 | cp = Strsave(cp); | |
326 | } | |
327 | else if (v[1] != NULL) { | |
328 | stderror(ERR_NAME | ERR_TOOMANY); | |
329 | /* NOTREACHED */ | |
330 | return; | |
331 | } | |
332 | else if ((dp = dfind(*v)) != 0) { | |
333 | char *tmp; | |
334 | ||
335 | printd = 1; | |
336 | if (chdir(tmp = short2str(dp->di_name)) < 0) | |
337 | stderror(ERR_SYSTEM, tmp, strerror(errno)); | |
338 | dcwd->di_prev->di_next = dcwd->di_next; | |
339 | dcwd->di_next->di_prev = dcwd->di_prev; | |
106427f7 BJ |
340 | dfree(dcwd); |
341 | dnewcwd(dp); | |
6e37afca KB |
342 | return; |
343 | } | |
344 | else | |
345 | cp = dfollow(*v); | |
346 | dp = (struct directory *) xcalloc(sizeof(struct directory), 1); | |
347 | dp->di_name = cp; | |
348 | dp->di_count = 0; | |
349 | dp->di_next = dcwd->di_next; | |
350 | dp->di_prev = dcwd->di_prev; | |
351 | dp->di_prev->di_next = dp; | |
352 | dp->di_next->di_prev = dp; | |
353 | dfree(dcwd); | |
354 | dnewcwd(dp); | |
355 | } | |
356 | ||
357 | static Char * | |
358 | dgoto(cp) | |
359 | Char *cp; | |
360 | { | |
361 | Char *dp; | |
362 | ||
363 | if (*cp != '/') { | |
364 | register Char *p, *q; | |
365 | int cwdlen; | |
366 | ||
c28e64a2 CL |
367 | for (p = dcwd->di_name; *p++;) |
368 | continue; | |
6e37afca KB |
369 | if ((cwdlen = p - dcwd->di_name - 1) == 1) /* root */ |
370 | cwdlen = 0; | |
c28e64a2 CL |
371 | for (p = cp; *p++;) |
372 | continue; | |
6e37afca | 373 | dp = (Char *) xmalloc((size_t)((cwdlen + (p - cp) + 1) * sizeof(Char))); |
37999c01 | 374 | for (p = dp, q = dcwd->di_name; (*p++ = *q++) != '\0';) |
c28e64a2 | 375 | continue; |
6e37afca KB |
376 | if (cwdlen) |
377 | p[-1] = '/'; | |
378 | else | |
379 | p--; /* don't add a / after root */ | |
37999c01 | 380 | for (q = cp; (*p++ = *q++) != '\0';) |
c28e64a2 | 381 | continue; |
6e37afca KB |
382 | xfree((ptr_t) cp); |
383 | cp = dp; | |
384 | dp += cwdlen; | |
385 | } | |
386 | else | |
387 | dp = cp; | |
388 | ||
389 | cp = dcanon(cp, dp); | |
390 | return cp; | |
106427f7 BJ |
391 | } |
392 | ||
393 | /* | |
394 | * dfollow - change to arg directory; fall back on cdpath if not valid | |
395 | */ | |
6e37afca | 396 | static Char * |
106427f7 | 397 | dfollow(cp) |
6e37afca | 398 | register Char *cp; |
106427f7 | 399 | { |
6e37afca KB |
400 | register Char *dp; |
401 | struct varent *c; | |
402 | char ebuf[MAXPATHLEN]; | |
8f9189be | 403 | int serrno; |
106427f7 | 404 | |
6e37afca KB |
405 | cp = globone(cp, G_ERROR); |
406 | /* | |
407 | * if we are ignoring symlinks, try to fix relatives now. | |
408 | */ | |
409 | dp = dnormalize(cp); | |
410 | if (chdir(short2str(dp)) >= 0) { | |
411 | xfree((ptr_t) cp); | |
412 | return dgoto(dp); | |
413 | } | |
414 | else { | |
415 | xfree((ptr_t) dp); | |
416 | if (chdir(short2str(cp)) >= 0) | |
417 | return dgoto(cp); | |
8f9189be | 418 | serrno = errno; |
6e37afca | 419 | } |
1f7436a6 | 420 | |
6e37afca KB |
421 | if (cp[0] != '/' && !prefix(STRdotsl, cp) && !prefix(STRdotdotsl, cp) |
422 | && (c = adrof(STRcdpath))) { | |
423 | Char **cdp; | |
424 | register Char *p; | |
425 | Char buf[MAXPATHLEN]; | |
426 | ||
427 | for (cdp = c->vec; *cdp; cdp++) { | |
37999c01 | 428 | for (dp = buf, p = *cdp; (*dp++ = *p++) != '\0';) |
c28e64a2 | 429 | continue; |
6e37afca | 430 | dp[-1] = '/'; |
37999c01 | 431 | for (p = cp; (*dp++ = *p++) != '\0';) |
c28e64a2 | 432 | continue; |
6e37afca KB |
433 | if (chdir(short2str(buf)) >= 0) { |
434 | printd = 1; | |
435 | xfree((ptr_t) cp); | |
436 | cp = Strsave(buf); | |
437 | return dgoto(cp); | |
438 | } | |
439 | } | |
440 | } | |
441 | dp = value(cp); | |
442 | if ((dp[0] == '/' || dp[0] == '.') && chdir(short2str(dp)) >= 0) { | |
443 | xfree((ptr_t) cp); | |
444 | cp = Strsave(dp); | |
445 | printd = 1; | |
446 | return dgoto(cp); | |
447 | } | |
448 | (void) strcpy(ebuf, short2str(cp)); | |
449 | xfree((ptr_t) cp); | |
8f9189be | 450 | stderror(ERR_SYSTEM, ebuf, strerror(serrno)); |
6e37afca | 451 | return (NULL); |
106427f7 BJ |
452 | } |
453 | ||
6e37afca | 454 | |
106427f7 BJ |
455 | /* |
456 | * dopushd - push new directory onto directory stack. | |
457 | * with no arguments exchange top and second. | |
458 | * with numeric argument (+n) bring it to top. | |
459 | */ | |
6e37afca | 460 | void |
454c2aa3 CZ |
461 | /*ARGSUSED*/ |
462 | dopushd(v, t) | |
463 | Char **v; | |
464 | struct command *t; | |
106427f7 | 465 | { |
6e37afca | 466 | register struct directory *dp; |
106427f7 | 467 | |
6e37afca KB |
468 | skipargs(&v, " [<dir>|+<n>]"); |
469 | printd = 1; | |
470 | if (*v == NULL) { | |
471 | char *tmp; | |
472 | ||
473 | if ((dp = dcwd->di_prev) == &dhead) | |
474 | dp = dhead.di_prev; | |
475 | if (dp == dcwd) | |
476 | stderror(ERR_NAME | ERR_NODIR); | |
477 | if (chdir(tmp = short2str(dp->di_name)) < 0) | |
478 | stderror(ERR_SYSTEM, tmp, strerror(errno)); | |
479 | dp->di_prev->di_next = dp->di_next; | |
480 | dp->di_next->di_prev = dp->di_prev; | |
481 | dp->di_next = dcwd->di_next; | |
482 | dp->di_prev = dcwd; | |
483 | dcwd->di_next->di_prev = dp; | |
484 | dcwd->di_next = dp; | |
485 | } | |
486 | else if (v[1] != NULL) { | |
487 | stderror(ERR_NAME | ERR_TOOMANY); | |
488 | /* NOTREACHED */ | |
489 | return; | |
490 | } | |
37999c01 | 491 | else if ((dp = dfind(*v)) != NULL) { |
6e37afca KB |
492 | char *tmp; |
493 | ||
494 | if (chdir(tmp = short2str(dp->di_name)) < 0) | |
495 | stderror(ERR_SYSTEM, tmp, strerror(errno)); | |
496 | } | |
497 | else { | |
498 | register Char *ccp; | |
499 | ||
500 | ccp = dfollow(*v); | |
501 | dp = (struct directory *) xcalloc(sizeof(struct directory), 1); | |
502 | dp->di_name = ccp; | |
503 | dp->di_count = 0; | |
504 | dp->di_prev = dcwd; | |
505 | dp->di_next = dcwd->di_next; | |
506 | dcwd->di_next = dp; | |
507 | dp->di_next->di_prev = dp; | |
508 | } | |
509 | dnewcwd(dp); | |
106427f7 BJ |
510 | } |
511 | ||
512 | /* | |
513 | * dfind - find a directory if specified by numeric (+n) argument | |
514 | */ | |
6e37afca | 515 | static struct directory * |
106427f7 | 516 | dfind(cp) |
6e37afca | 517 | register Char *cp; |
106427f7 | 518 | { |
6e37afca KB |
519 | register struct directory *dp; |
520 | register int i; | |
521 | register Char *ep; | |
522 | ||
523 | if (*cp++ != '+') | |
524 | return (0); | |
525 | for (ep = cp; Isdigit(*ep); ep++) | |
526 | continue; | |
527 | if (*ep) | |
528 | return (0); | |
529 | i = getn(cp); | |
530 | if (i <= 0) | |
531 | return (0); | |
532 | for (dp = dcwd; i != 0; i--) { | |
533 | if ((dp = dp->di_prev) == &dhead) | |
534 | dp = dp->di_prev; | |
535 | if (dp == dcwd) | |
536 | stderror(ERR_NAME | ERR_DEEP); | |
537 | } | |
538 | return (dp); | |
106427f7 BJ |
539 | } |
540 | ||
541 | /* | |
542 | * dopopd - pop a directory out of the directory stack | |
543 | * with a numeric argument just discard it. | |
544 | */ | |
6e37afca | 545 | void |
454c2aa3 CZ |
546 | /*ARGSUSED*/ |
547 | dopopd(v, t) | |
548 | Char **v; | |
549 | struct command *t; | |
106427f7 | 550 | { |
6e37afca | 551 | register struct directory *dp, *p = NULL; |
106427f7 | 552 | |
6e37afca KB |
553 | skipargs(&v, " [+<n>]"); |
554 | printd = 1; | |
555 | if (*v == NULL) | |
556 | dp = dcwd; | |
557 | else if (v[1] != NULL) { | |
558 | stderror(ERR_NAME | ERR_TOOMANY); | |
559 | /* NOTREACHED */ | |
560 | return; | |
561 | } | |
562 | else if ((dp = dfind(*v)) == 0) | |
563 | stderror(ERR_NAME | ERR_BADDIR); | |
564 | if (dp->di_prev == &dhead && dp->di_next == &dhead) | |
565 | stderror(ERR_NAME | ERR_EMPTY); | |
566 | if (dp == dcwd) { | |
567 | char *tmp; | |
568 | ||
569 | if ((p = dp->di_prev) == &dhead) | |
570 | p = dhead.di_prev; | |
571 | if (chdir(tmp = short2str(p->di_name)) < 0) | |
572 | stderror(ERR_SYSTEM, tmp, strerror(errno)); | |
573 | } | |
574 | dp->di_prev->di_next = dp->di_next; | |
575 | dp->di_next->di_prev = dp->di_prev; | |
576 | if (dp == dcwd) | |
577 | dnewcwd(p); | |
578 | else { | |
579 | printdirs(); | |
580 | } | |
581 | dfree(dp); | |
106427f7 BJ |
582 | } |
583 | ||
584 | /* | |
585 | * dfree - free the directory (or keep it if it still has ref count) | |
586 | */ | |
6e37afca | 587 | void |
106427f7 | 588 | dfree(dp) |
6e37afca | 589 | register struct directory *dp; |
106427f7 BJ |
590 | { |
591 | ||
6e37afca KB |
592 | if (dp->di_count != 0) { |
593 | dp->di_next = dp->di_prev = 0; | |
594 | } | |
595 | else { | |
596 | xfree((char *) dp->di_name); | |
597 | xfree((ptr_t) dp); | |
598 | } | |
106427f7 BJ |
599 | } |
600 | ||
601 | /* | |
602 | * dcanon - canonicalize the pathname, removing excess ./ and ../ etc. | |
603 | * we are of course assuming that the file system is standardly | |
604 | * constructed (always have ..'s, directories have links) | |
605 | */ | |
6e37afca | 606 | Char * |
1f7436a6 | 607 | dcanon(cp, p) |
6e37afca | 608 | register Char *cp, *p; |
106427f7 | 609 | { |
6e37afca KB |
610 | register Char *sp; |
611 | register Char *p1, *p2; /* general purpose */ | |
612 | bool slash; | |
613 | ||
614 | Char link[MAXPATHLEN]; | |
615 | char tlink[MAXPATHLEN]; | |
616 | int cc; | |
617 | Char *newcp; | |
618 | ||
619 | /* | |
620 | * christos: if the path given does not start with a slash prepend cwd. If | |
621 | * cwd does not start with a path or the result would be too long abort(). | |
622 | */ | |
623 | if (*cp != '/') { | |
624 | Char tmpdir[MAXPATHLEN]; | |
625 | ||
626 | p1 = value(STRcwd); | |
0aec749d | 627 | if (p1 == NULL || *p1 != '/') |
6e37afca KB |
628 | abort(); |
629 | if (Strlen(p1) + Strlen(cp) + 1 >= MAXPATHLEN) | |
630 | abort(); | |
631 | (void) Strcpy(tmpdir, p1); | |
632 | (void) Strcat(tmpdir, STRslash); | |
633 | (void) Strcat(tmpdir, cp); | |
634 | xfree((ptr_t) cp); | |
635 | cp = p = Strsave(tmpdir); | |
636 | } | |
637 | ||
638 | while (*p) { /* for each component */ | |
639 | sp = p; /* save slash address */ | |
640 | while (*++p == '/') /* flush extra slashes */ | |
c28e64a2 | 641 | continue; |
6e37afca | 642 | if (p != ++sp) |
37999c01 | 643 | for (p1 = sp, p2 = p; (*p1++ = *p2++) != '\0';) |
c28e64a2 | 644 | continue; |
6e37afca KB |
645 | p = sp; /* save start of component */ |
646 | slash = 0; | |
647 | while (*++p) /* find next slash or end of path */ | |
648 | if (*p == '/') { | |
649 | slash = 1; | |
650 | *p = 0; | |
651 | break; | |
652 | } | |
653 | ||
654 | if (*sp == '\0') /* if component is null */ | |
655 | if (--sp == cp) /* if path is one char (i.e. /) */ | |
656 | break; | |
657 | else | |
658 | *sp = '\0'; | |
659 | else if (sp[0] == '.' && sp[1] == 0) { | |
660 | if (slash) { | |
37999c01 | 661 | for (p1 = sp, p2 = p + 1; (*p1++ = *p2++) != '\0';) |
c28e64a2 | 662 | continue; |
6e37afca KB |
663 | p = --sp; |
664 | } | |
665 | else if (--sp != cp) | |
666 | *sp = '\0'; | |
106427f7 | 667 | } |
6e37afca KB |
668 | else if (sp[0] == '.' && sp[1] == '.' && sp[2] == 0) { |
669 | /* | |
670 | * We have something like "yyy/xxx/..", where "yyy" can be null or | |
671 | * a path starting at /, and "xxx" is a single component. Before | |
672 | * compressing "xxx/..", we want to expand "yyy/xxx", if it is a | |
673 | * symbolic link. | |
674 | */ | |
675 | *--sp = 0; /* form the pathname for readlink */ | |
676 | if (sp != cp && !adrof(STRignore_symlinks) && | |
677 | (cc = readlink(short2str(cp), tlink, | |
678 | sizeof tlink)) >= 0) { | |
679 | (void) Strcpy(link, str2short(tlink)); | |
680 | link[cc] = '\0'; | |
681 | ||
682 | if (slash) | |
683 | *p = '/'; | |
684 | /* | |
685 | * Point p to the '/' in "/..", and restore the '/'. | |
686 | */ | |
687 | *(p = sp) = '/'; | |
688 | /* | |
689 | * find length of p | |
690 | */ | |
c28e64a2 CL |
691 | for (p1 = p; *p1++;) |
692 | continue; | |
6e37afca KB |
693 | if (*link != '/') { |
694 | /* | |
695 | * Relative path, expand it between the "yyy/" and the | |
696 | * "/..". First, back sp up to the character past "yyy/". | |
697 | */ | |
c28e64a2 CL |
698 | while (*--sp != '/') |
699 | continue; | |
6e37afca KB |
700 | sp++; |
701 | *sp = 0; | |
702 | /* | |
703 | * New length is "yyy/" + link + "/.." and rest | |
704 | */ | |
705 | p1 = newcp = (Char *) xmalloc((size_t) | |
706 | (((sp - cp) + cc + (p1 - p)) * | |
707 | sizeof(Char))); | |
708 | /* | |
709 | * Copy new path into newcp | |
710 | */ | |
37999c01 | 711 | for (p2 = cp; (*p1++ = *p2++) != '\0';) |
c28e64a2 | 712 | continue; |
37999c01 | 713 | for (p1--, p2 = link; (*p1++ = *p2++) != '\0';) |
c28e64a2 | 714 | continue; |
37999c01 | 715 | for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) |
c28e64a2 | 716 | continue; |
6e37afca KB |
717 | /* |
718 | * Restart canonicalization at expanded "/xxx". | |
719 | */ | |
720 | p = sp - cp - 1 + newcp; | |
721 | } | |
722 | else { | |
723 | /* | |
724 | * New length is link + "/.." and rest | |
725 | */ | |
726 | p1 = newcp = (Char *) xmalloc((size_t) | |
727 | ((cc + (p1 - p)) * sizeof(Char))); | |
728 | /* | |
729 | * Copy new path into newcp | |
730 | */ | |
37999c01 | 731 | for (p2 = link; (*p1++ = *p2++) != '\0';) |
c28e64a2 | 732 | continue; |
37999c01 | 733 | for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) |
c28e64a2 | 734 | continue; |
6e37afca KB |
735 | /* |
736 | * Restart canonicalization at beginning | |
737 | */ | |
738 | p = newcp; | |
739 | } | |
740 | xfree((ptr_t) cp); | |
741 | cp = newcp; | |
742 | continue; /* canonicalize the link */ | |
743 | } | |
744 | *sp = '/'; | |
745 | if (sp != cp) | |
c28e64a2 CL |
746 | while (*--sp != '/') |
747 | continue; | |
6e37afca | 748 | if (slash) { |
37999c01 | 749 | for (p1 = sp + 1, p2 = p + 1; (*p1++ = *p2++) != '\0';) |
c28e64a2 | 750 | continue; |
6e37afca KB |
751 | p = sp; |
752 | } | |
753 | else if (cp == sp) | |
754 | *++sp = '\0'; | |
755 | else | |
756 | *sp = '\0'; | |
757 | } | |
758 | else { /* normal dir name (not . or .. or nothing) */ | |
759 | ||
760 | if (sp != cp && adrof(STRchase_symlinks) && | |
761 | !adrof(STRignore_symlinks) && | |
762 | (cc = readlink(short2str(cp), tlink, | |
763 | sizeof tlink)) >= 0) { | |
764 | (void) Strcpy(link, str2short(tlink)); | |
765 | link[cc] = '\0'; | |
766 | ||
767 | /* | |
768 | * restore the '/'. | |
769 | */ | |
770 | if (slash) | |
771 | *p = '/'; | |
772 | ||
773 | /* | |
774 | * point sp to p (rather than backing up). | |
775 | */ | |
776 | sp = p; | |
777 | ||
778 | /* | |
779 | * find length of p | |
780 | */ | |
c28e64a2 CL |
781 | for (p1 = p; *p1++;) |
782 | continue; | |
6e37afca KB |
783 | if (*link != '/') { |
784 | /* | |
785 | * Relative path, expand it between the "yyy/" and the | |
786 | * remainder. First, back sp up to the character past | |
787 | * "yyy/". | |
788 | */ | |
c28e64a2 CL |
789 | while (*--sp != '/') |
790 | continue; | |
6e37afca KB |
791 | sp++; |
792 | *sp = 0; | |
793 | /* | |
794 | * New length is "yyy/" + link + "/.." and rest | |
795 | */ | |
796 | p1 = newcp = (Char *) xmalloc((size_t) | |
797 | (((sp - cp) + cc + (p1 - p)) | |
798 | * sizeof(Char))); | |
799 | /* | |
800 | * Copy new path into newcp | |
801 | */ | |
37999c01 | 802 | for (p2 = cp; (*p1++ = *p2++) != '\0';) |
c28e64a2 | 803 | continue; |
37999c01 | 804 | for (p1--, p2 = link; (*p1++ = *p2++) != '\0';) |
c28e64a2 | 805 | continue; |
37999c01 | 806 | for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) |
c28e64a2 | 807 | continue; |
6e37afca KB |
808 | /* |
809 | * Restart canonicalization at expanded "/xxx". | |
810 | */ | |
811 | p = sp - cp - 1 + newcp; | |
812 | } | |
813 | else { | |
814 | /* | |
815 | * New length is link + the rest | |
816 | */ | |
817 | p1 = newcp = (Char *) xmalloc((size_t) | |
818 | ((cc + (p1 - p)) * sizeof(Char))); | |
819 | /* | |
820 | * Copy new path into newcp | |
821 | */ | |
37999c01 | 822 | for (p2 = link; (*p1++ = *p2++) != '\0';) |
c28e64a2 | 823 | continue; |
37999c01 | 824 | for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) |
c28e64a2 | 825 | continue; |
6e37afca KB |
826 | /* |
827 | * Restart canonicalization at beginning | |
828 | */ | |
829 | p = newcp; | |
830 | } | |
831 | xfree((ptr_t) cp); | |
832 | cp = newcp; | |
833 | continue; /* canonicalize the link */ | |
834 | } | |
835 | if (slash) | |
836 | *p = '/'; | |
837 | } | |
838 | } | |
839 | ||
840 | /* | |
841 | * fix home... | |
842 | */ | |
843 | p1 = value(STRhome); | |
844 | cc = Strlen(p1); | |
845 | /* | |
846 | * See if we're not in a subdir of STRhome | |
847 | */ | |
15d0c357 | 848 | if (p1 && *p1 == '/' && |
6e37afca KB |
849 | (Strncmp(p1, cp, cc) != 0 || (cp[cc] != '/' && cp[cc] != '\0'))) { |
850 | static ino_t home_ino = -1; | |
851 | static dev_t home_dev = -1; | |
0aec749d | 852 | static Char *home_ptr = NULL; |
6e37afca KB |
853 | struct stat statbuf; |
854 | ||
855 | /* | |
856 | * Get dev and ino of STRhome | |
857 | */ | |
858 | if (home_ptr != p1 && | |
859 | stat(short2str(p1), &statbuf) != -1) { | |
860 | home_dev = statbuf.st_dev; | |
861 | home_ino = statbuf.st_ino; | |
862 | home_ptr = p1; | |
863 | } | |
864 | /* | |
865 | * Start comparing dev & ino backwards | |
866 | */ | |
867 | p2 = Strcpy(link, cp); | |
0aec749d | 868 | for (sp = NULL; *p2 && stat(short2str(p2), &statbuf) != -1;) { |
6e37afca KB |
869 | if (statbuf.st_dev == home_dev && |
870 | statbuf.st_ino == home_ino) { | |
871 | sp = (Char *) - 1; | |
872 | break; | |
873 | } | |
37999c01 | 874 | if ((sp = Strrchr(p2, '/')) != NULL) |
6e37afca KB |
875 | *sp = '\0'; |
876 | } | |
877 | /* | |
878 | * See if we found it | |
879 | */ | |
0aec749d | 880 | if (*p2 && sp == (Char *) -1) { |
6e37afca KB |
881 | /* |
882 | * Use STRhome to make '~' work | |
883 | */ | |
67735bc1 | 884 | newcp = Strspl(p1, cp + Strlen(p2)); |
6e37afca KB |
885 | xfree((ptr_t) cp); |
886 | cp = newcp; | |
887 | } | |
888 | } | |
889 | return cp; | |
106427f7 BJ |
890 | } |
891 | ||
6e37afca | 892 | |
106427f7 BJ |
893 | /* |
894 | * dnewcwd - make a new directory in the loop the current one | |
895 | */ | |
6e37afca | 896 | static void |
106427f7 | 897 | dnewcwd(dp) |
6e37afca | 898 | register struct directory *dp; |
106427f7 | 899 | { |
6e37afca KB |
900 | dcwd = dp; |
901 | dset(dcwd->di_name); | |
902 | if (printd && !(adrof(STRpushdsilent))) | |
903 | printdirs(); | |
106427f7 | 904 | } |