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