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 |
454c2aa3 | 9 | static char sccsid[] = "@(#)dir.c 5.13 (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 | |
0aec749d CZ |
30 | static struct directory |
31 | *dfind __P((Char *)); | |
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); | |
454c2aa3 | 62 | (void) fprintf(csherr, emsg, tcp); |
6e37afca | 63 | if (chdir(tcp) == -1) |
0aec749d | 64 | cp = NULL; |
6e37afca | 65 | else |
106427f7 | 66 | cp = 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 | */ | |
94 | if (cwd = getenv("PWD")) { | |
95 | if (stat(cwd, &shp) != -1 && swd.st_dev == shp.st_dev && | |
96 | swd.st_ino == shp.st_ino) | |
97 | tcp = cwd; | |
98 | } | |
99 | cp = dcanon(str2short(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 | |
118 | * other junk characters glob will fail. | |
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: | |
153 | stderror(ERR_DIRUS, short2str(**v), str); | |
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) && | |
195 | prefix(hp, dp->di_name)) | |
196 | len = Strlen(s = (dp->di_name + Strlen(hp))) + 2; | |
106427f7 | 197 | else |
6e37afca KB |
198 | len = Strlen(s = dp->di_name) + 1; |
199 | ||
200 | cur += len; | |
201 | if ((dirflag & DIR_LINE) && cur >= 80 - 1 && len < 80) { | |
454c2aa3 | 202 | (void) fprintf(cshout, "\n"); |
6e37afca KB |
203 | cur = len; |
204 | } | |
454c2aa3 | 205 | (void) fprintf(cshout, s != dp->di_name ? "~%s%c" : "%s%c", |
6e37afca KB |
206 | short2str(s), (dirflag & DIR_VERT) ? '\n' : ' '); |
207 | } while ((dp = dp->di_prev) != dcwd); | |
208 | if (!(dirflag & DIR_VERT)) | |
454c2aa3 | 209 | (void) fprintf(cshout, "\n"); |
106427f7 BJ |
210 | } |
211 | ||
6e37afca | 212 | void |
106427f7 | 213 | dtildepr(home, dir) |
6e37afca | 214 | register Char *home, *dir; |
106427f7 BJ |
215 | { |
216 | ||
6e37afca | 217 | if (!eq(home, STRslash) && prefix(home, dir)) |
454c2aa3 | 218 | (void) fprintf(cshout, "~%s", short2str(dir + Strlen(home))); |
6e37afca | 219 | else |
454c2aa3 | 220 | (void) fprintf(cshout, "%s", short2str(dir)); |
6e37afca KB |
221 | } |
222 | ||
223 | void | |
224 | dtilde() | |
225 | { | |
226 | struct directory *d = dcwd; | |
227 | ||
228 | do { | |
229 | if (d == &dhead) | |
230 | continue; | |
231 | d->di_name = dcanon(d->di_name, STRNULL); | |
232 | } while ((d = d->di_prev) != dcwd); | |
233 | ||
234 | dset(dcwd->di_name); | |
235 | } | |
236 | ||
237 | ||
238 | /* dnormalize(): | |
239 | * If the name starts with . or .. then we might need to normalize | |
240 | * it depending on the symbolic link flags | |
241 | */ | |
242 | Char * | |
243 | dnormalize(cp) | |
244 | Char *cp; | |
245 | { | |
246 | ||
247 | #define UC (unsigned char) | |
248 | #define ISDOT(c) (UC(c)[0] == '.' && ((UC(c)[1] == '\0') || (UC(c)[1] == '/'))) | |
249 | #define ISDOTDOT(c) (UC(c)[0] == '.' && ISDOT(&((c)[1]))) | |
250 | ||
251 | if ((unsigned char) cp[0] == '/') | |
252 | return (Strsave(cp)); | |
253 | ||
254 | if (adrof(STRignore_symlinks)) { | |
255 | int dotdot = 0; | |
256 | Char *dp, *cwd; | |
257 | ||
258 | cwd = (Char *) xmalloc((size_t) ((Strlen(dcwd->di_name) + 3) * | |
259 | sizeof(Char))); | |
260 | (void) Strcpy(cwd, dcwd->di_name); | |
261 | ||
262 | /* | |
263 | * Ignore . and count ..'s | |
264 | */ | |
265 | while (*cp) { | |
266 | if (ISDOT(cp)) { | |
267 | if (*++cp) | |
268 | cp++; | |
269 | } | |
270 | else if (ISDOTDOT(cp)) { | |
271 | dotdot++; | |
272 | cp += 2; | |
273 | if (*cp) | |
274 | cp++; | |
275 | } | |
276 | else | |
277 | break; | |
278 | } | |
279 | while (dotdot > 0) | |
280 | if ((dp = Strrchr(cwd, '/'))) { | |
281 | *dp = '\0'; | |
282 | dotdot--; | |
283 | } | |
284 | else | |
285 | break; | |
286 | ||
287 | if (*cp) { | |
288 | cwd[dotdot = Strlen(cwd)] = '/'; | |
289 | cwd[dotdot + 1] = '\0'; | |
290 | dp = Strspl(cwd, cp); | |
291 | xfree((ptr_t) cwd); | |
292 | return dp; | |
293 | } | |
294 | else { | |
295 | if (!*cwd) { | |
296 | cwd[0] = '/'; | |
297 | cwd[1] = '\0'; | |
298 | } | |
299 | return cwd; | |
300 | } | |
301 | } | |
302 | return Strsave(cp); | |
106427f7 BJ |
303 | } |
304 | ||
305 | /* | |
306 | * dochngd - implement chdir command. | |
307 | */ | |
6e37afca | 308 | void |
454c2aa3 CZ |
309 | /*ARGSUSED*/ |
310 | dochngd(v, t) | |
311 | Char **v; | |
312 | struct command *t; | |
106427f7 | 313 | { |
6e37afca KB |
314 | register Char *cp; |
315 | register struct directory *dp; | |
316 | ||
317 | skipargs(&v, " [<dir>]"); | |
318 | printd = 0; | |
319 | if (*v == NULL) { | |
320 | if ((cp = value(STRhome)) == NULL || *cp == 0) | |
321 | stderror(ERR_NAME | ERR_NOHOMEDIR); | |
322 | if (chdir(short2str(cp)) < 0) | |
323 | stderror(ERR_NAME | ERR_CANTCHANGE); | |
324 | cp = Strsave(cp); | |
325 | } | |
326 | else if (v[1] != NULL) { | |
327 | stderror(ERR_NAME | ERR_TOOMANY); | |
328 | /* NOTREACHED */ | |
329 | return; | |
330 | } | |
331 | else if ((dp = dfind(*v)) != 0) { | |
332 | char *tmp; | |
333 | ||
334 | printd = 1; | |
335 | if (chdir(tmp = short2str(dp->di_name)) < 0) | |
336 | stderror(ERR_SYSTEM, tmp, strerror(errno)); | |
337 | dcwd->di_prev->di_next = dcwd->di_next; | |
338 | dcwd->di_next->di_prev = dcwd->di_prev; | |
106427f7 BJ |
339 | dfree(dcwd); |
340 | dnewcwd(dp); | |
6e37afca KB |
341 | return; |
342 | } | |
343 | else | |
344 | cp = dfollow(*v); | |
345 | dp = (struct directory *) xcalloc(sizeof(struct directory), 1); | |
346 | dp->di_name = cp; | |
347 | dp->di_count = 0; | |
348 | dp->di_next = dcwd->di_next; | |
349 | dp->di_prev = dcwd->di_prev; | |
350 | dp->di_prev->di_next = dp; | |
351 | dp->di_next->di_prev = dp; | |
352 | dfree(dcwd); | |
353 | dnewcwd(dp); | |
354 | } | |
355 | ||
356 | static Char * | |
357 | dgoto(cp) | |
358 | Char *cp; | |
359 | { | |
360 | Char *dp; | |
361 | ||
362 | if (*cp != '/') { | |
363 | register Char *p, *q; | |
364 | int cwdlen; | |
365 | ||
366 | for (p = dcwd->di_name; *p++;); | |
367 | if ((cwdlen = p - dcwd->di_name - 1) == 1) /* root */ | |
368 | cwdlen = 0; | |
369 | for (p = cp; *p++;); | |
370 | dp = (Char *) xmalloc((size_t)((cwdlen + (p - cp) + 1) * sizeof(Char))); | |
371 | for (p = dp, q = dcwd->di_name; *p++ = *q++;); | |
372 | if (cwdlen) | |
373 | p[-1] = '/'; | |
374 | else | |
375 | p--; /* don't add a / after root */ | |
376 | for (q = cp; *p++ = *q++;); | |
377 | xfree((ptr_t) cp); | |
378 | cp = dp; | |
379 | dp += cwdlen; | |
380 | } | |
381 | else | |
382 | dp = cp; | |
383 | ||
384 | cp = dcanon(cp, dp); | |
385 | return cp; | |
106427f7 BJ |
386 | } |
387 | ||
388 | /* | |
389 | * dfollow - change to arg directory; fall back on cdpath if not valid | |
390 | */ | |
6e37afca | 391 | static Char * |
106427f7 | 392 | dfollow(cp) |
6e37afca | 393 | register Char *cp; |
106427f7 | 394 | { |
6e37afca KB |
395 | register Char *dp; |
396 | struct varent *c; | |
397 | char ebuf[MAXPATHLEN]; | |
8f9189be | 398 | int serrno; |
106427f7 | 399 | |
6e37afca KB |
400 | cp = globone(cp, G_ERROR); |
401 | /* | |
402 | * if we are ignoring symlinks, try to fix relatives now. | |
403 | */ | |
404 | dp = dnormalize(cp); | |
405 | if (chdir(short2str(dp)) >= 0) { | |
406 | xfree((ptr_t) cp); | |
407 | return dgoto(dp); | |
408 | } | |
409 | else { | |
410 | xfree((ptr_t) dp); | |
411 | if (chdir(short2str(cp)) >= 0) | |
412 | return dgoto(cp); | |
8f9189be | 413 | serrno = errno; |
6e37afca | 414 | } |
1f7436a6 | 415 | |
6e37afca KB |
416 | if (cp[0] != '/' && !prefix(STRdotsl, cp) && !prefix(STRdotdotsl, cp) |
417 | && (c = adrof(STRcdpath))) { | |
418 | Char **cdp; | |
419 | register Char *p; | |
420 | Char buf[MAXPATHLEN]; | |
421 | ||
422 | for (cdp = c->vec; *cdp; cdp++) { | |
423 | for (dp = buf, p = *cdp; *dp++ = *p++;); | |
424 | dp[-1] = '/'; | |
425 | for (p = cp; *dp++ = *p++;); | |
426 | if (chdir(short2str(buf)) >= 0) { | |
427 | printd = 1; | |
428 | xfree((ptr_t) cp); | |
429 | cp = Strsave(buf); | |
430 | return dgoto(cp); | |
431 | } | |
432 | } | |
433 | } | |
434 | dp = value(cp); | |
435 | if ((dp[0] == '/' || dp[0] == '.') && chdir(short2str(dp)) >= 0) { | |
436 | xfree((ptr_t) cp); | |
437 | cp = Strsave(dp); | |
438 | printd = 1; | |
439 | return dgoto(cp); | |
440 | } | |
441 | (void) strcpy(ebuf, short2str(cp)); | |
442 | xfree((ptr_t) cp); | |
8f9189be | 443 | stderror(ERR_SYSTEM, ebuf, strerror(serrno)); |
6e37afca | 444 | return (NULL); |
106427f7 BJ |
445 | } |
446 | ||
6e37afca | 447 | |
106427f7 BJ |
448 | /* |
449 | * dopushd - push new directory onto directory stack. | |
450 | * with no arguments exchange top and second. | |
451 | * with numeric argument (+n) bring it to top. | |
452 | */ | |
6e37afca | 453 | void |
454c2aa3 CZ |
454 | /*ARGSUSED*/ |
455 | dopushd(v, t) | |
456 | Char **v; | |
457 | struct command *t; | |
106427f7 | 458 | { |
6e37afca | 459 | register struct directory *dp; |
106427f7 | 460 | |
6e37afca KB |
461 | skipargs(&v, " [<dir>|+<n>]"); |
462 | printd = 1; | |
463 | if (*v == NULL) { | |
464 | char *tmp; | |
465 | ||
466 | if ((dp = dcwd->di_prev) == &dhead) | |
467 | dp = dhead.di_prev; | |
468 | if (dp == dcwd) | |
469 | stderror(ERR_NAME | ERR_NODIR); | |
470 | if (chdir(tmp = short2str(dp->di_name)) < 0) | |
471 | stderror(ERR_SYSTEM, tmp, strerror(errno)); | |
472 | dp->di_prev->di_next = dp->di_next; | |
473 | dp->di_next->di_prev = dp->di_prev; | |
474 | dp->di_next = dcwd->di_next; | |
475 | dp->di_prev = dcwd; | |
476 | dcwd->di_next->di_prev = dp; | |
477 | dcwd->di_next = dp; | |
478 | } | |
479 | else if (v[1] != NULL) { | |
480 | stderror(ERR_NAME | ERR_TOOMANY); | |
481 | /* NOTREACHED */ | |
482 | return; | |
483 | } | |
484 | else if (dp = dfind(*v)) { | |
485 | char *tmp; | |
486 | ||
487 | if (chdir(tmp = short2str(dp->di_name)) < 0) | |
488 | stderror(ERR_SYSTEM, tmp, strerror(errno)); | |
489 | } | |
490 | else { | |
491 | register Char *ccp; | |
492 | ||
493 | ccp = dfollow(*v); | |
494 | dp = (struct directory *) xcalloc(sizeof(struct directory), 1); | |
495 | dp->di_name = ccp; | |
496 | dp->di_count = 0; | |
497 | dp->di_prev = dcwd; | |
498 | dp->di_next = dcwd->di_next; | |
499 | dcwd->di_next = dp; | |
500 | dp->di_next->di_prev = dp; | |
501 | } | |
502 | dnewcwd(dp); | |
106427f7 BJ |
503 | } |
504 | ||
505 | /* | |
506 | * dfind - find a directory if specified by numeric (+n) argument | |
507 | */ | |
6e37afca | 508 | static struct directory * |
106427f7 | 509 | dfind(cp) |
6e37afca | 510 | register Char *cp; |
106427f7 | 511 | { |
6e37afca KB |
512 | register struct directory *dp; |
513 | register int i; | |
514 | register Char *ep; | |
515 | ||
516 | if (*cp++ != '+') | |
517 | return (0); | |
518 | for (ep = cp; Isdigit(*ep); ep++) | |
519 | continue; | |
520 | if (*ep) | |
521 | return (0); | |
522 | i = getn(cp); | |
523 | if (i <= 0) | |
524 | return (0); | |
525 | for (dp = dcwd; i != 0; i--) { | |
526 | if ((dp = dp->di_prev) == &dhead) | |
527 | dp = dp->di_prev; | |
528 | if (dp == dcwd) | |
529 | stderror(ERR_NAME | ERR_DEEP); | |
530 | } | |
531 | return (dp); | |
106427f7 BJ |
532 | } |
533 | ||
534 | /* | |
535 | * dopopd - pop a directory out of the directory stack | |
536 | * with a numeric argument just discard it. | |
537 | */ | |
6e37afca | 538 | void |
454c2aa3 CZ |
539 | /*ARGSUSED*/ |
540 | dopopd(v, t) | |
541 | Char **v; | |
542 | struct command *t; | |
106427f7 | 543 | { |
6e37afca | 544 | register struct directory *dp, *p = NULL; |
106427f7 | 545 | |
6e37afca KB |
546 | skipargs(&v, " [+<n>]"); |
547 | printd = 1; | |
548 | if (*v == NULL) | |
549 | dp = dcwd; | |
550 | else if (v[1] != NULL) { | |
551 | stderror(ERR_NAME | ERR_TOOMANY); | |
552 | /* NOTREACHED */ | |
553 | return; | |
554 | } | |
555 | else if ((dp = dfind(*v)) == 0) | |
556 | stderror(ERR_NAME | ERR_BADDIR); | |
557 | if (dp->di_prev == &dhead && dp->di_next == &dhead) | |
558 | stderror(ERR_NAME | ERR_EMPTY); | |
559 | if (dp == dcwd) { | |
560 | char *tmp; | |
561 | ||
562 | if ((p = dp->di_prev) == &dhead) | |
563 | p = dhead.di_prev; | |
564 | if (chdir(tmp = short2str(p->di_name)) < 0) | |
565 | stderror(ERR_SYSTEM, tmp, strerror(errno)); | |
566 | } | |
567 | dp->di_prev->di_next = dp->di_next; | |
568 | dp->di_next->di_prev = dp->di_prev; | |
569 | if (dp == dcwd) | |
570 | dnewcwd(p); | |
571 | else { | |
572 | printdirs(); | |
573 | } | |
574 | dfree(dp); | |
106427f7 BJ |
575 | } |
576 | ||
577 | /* | |
578 | * dfree - free the directory (or keep it if it still has ref count) | |
579 | */ | |
6e37afca | 580 | void |
106427f7 | 581 | dfree(dp) |
6e37afca | 582 | register struct directory *dp; |
106427f7 BJ |
583 | { |
584 | ||
6e37afca KB |
585 | if (dp->di_count != 0) { |
586 | dp->di_next = dp->di_prev = 0; | |
587 | } | |
588 | else { | |
589 | xfree((char *) dp->di_name); | |
590 | xfree((ptr_t) dp); | |
591 | } | |
106427f7 BJ |
592 | } |
593 | ||
594 | /* | |
595 | * dcanon - canonicalize the pathname, removing excess ./ and ../ etc. | |
596 | * we are of course assuming that the file system is standardly | |
597 | * constructed (always have ..'s, directories have links) | |
598 | */ | |
6e37afca | 599 | Char * |
1f7436a6 | 600 | dcanon(cp, p) |
6e37afca | 601 | register Char *cp, *p; |
106427f7 | 602 | { |
6e37afca KB |
603 | register Char *sp; |
604 | register Char *p1, *p2; /* general purpose */ | |
605 | bool slash; | |
606 | ||
607 | Char link[MAXPATHLEN]; | |
608 | char tlink[MAXPATHLEN]; | |
609 | int cc; | |
610 | Char *newcp; | |
611 | ||
612 | /* | |
613 | * christos: if the path given does not start with a slash prepend cwd. If | |
614 | * cwd does not start with a path or the result would be too long abort(). | |
615 | */ | |
616 | if (*cp != '/') { | |
617 | Char tmpdir[MAXPATHLEN]; | |
618 | ||
619 | p1 = value(STRcwd); | |
0aec749d | 620 | if (p1 == NULL || *p1 != '/') |
6e37afca KB |
621 | abort(); |
622 | if (Strlen(p1) + Strlen(cp) + 1 >= MAXPATHLEN) | |
623 | abort(); | |
624 | (void) Strcpy(tmpdir, p1); | |
625 | (void) Strcat(tmpdir, STRslash); | |
626 | (void) Strcat(tmpdir, cp); | |
627 | xfree((ptr_t) cp); | |
628 | cp = p = Strsave(tmpdir); | |
629 | } | |
630 | ||
631 | while (*p) { /* for each component */ | |
632 | sp = p; /* save slash address */ | |
633 | while (*++p == '/') /* flush extra slashes */ | |
634 | ; | |
635 | if (p != ++sp) | |
636 | for (p1 = sp, p2 = p; *p1++ = *p2++;); | |
637 | p = sp; /* save start of component */ | |
638 | slash = 0; | |
639 | while (*++p) /* find next slash or end of path */ | |
640 | if (*p == '/') { | |
641 | slash = 1; | |
642 | *p = 0; | |
643 | break; | |
644 | } | |
645 | ||
646 | if (*sp == '\0') /* if component is null */ | |
647 | if (--sp == cp) /* if path is one char (i.e. /) */ | |
648 | break; | |
649 | else | |
650 | *sp = '\0'; | |
651 | else if (sp[0] == '.' && sp[1] == 0) { | |
652 | if (slash) { | |
653 | for (p1 = sp, p2 = p + 1; *p1++ = *p2++;); | |
654 | p = --sp; | |
655 | } | |
656 | else if (--sp != cp) | |
657 | *sp = '\0'; | |
106427f7 | 658 | } |
6e37afca KB |
659 | else if (sp[0] == '.' && sp[1] == '.' && sp[2] == 0) { |
660 | /* | |
661 | * We have something like "yyy/xxx/..", where "yyy" can be null or | |
662 | * a path starting at /, and "xxx" is a single component. Before | |
663 | * compressing "xxx/..", we want to expand "yyy/xxx", if it is a | |
664 | * symbolic link. | |
665 | */ | |
666 | *--sp = 0; /* form the pathname for readlink */ | |
667 | if (sp != cp && !adrof(STRignore_symlinks) && | |
668 | (cc = readlink(short2str(cp), tlink, | |
669 | sizeof tlink)) >= 0) { | |
670 | (void) Strcpy(link, str2short(tlink)); | |
671 | link[cc] = '\0'; | |
672 | ||
673 | if (slash) | |
674 | *p = '/'; | |
675 | /* | |
676 | * Point p to the '/' in "/..", and restore the '/'. | |
677 | */ | |
678 | *(p = sp) = '/'; | |
679 | /* | |
680 | * find length of p | |
681 | */ | |
682 | for (p1 = p; *p1++;); | |
683 | if (*link != '/') { | |
684 | /* | |
685 | * Relative path, expand it between the "yyy/" and the | |
686 | * "/..". First, back sp up to the character past "yyy/". | |
687 | */ | |
688 | while (*--sp != '/'); | |
689 | sp++; | |
690 | *sp = 0; | |
691 | /* | |
692 | * New length is "yyy/" + link + "/.." and rest | |
693 | */ | |
694 | p1 = newcp = (Char *) xmalloc((size_t) | |
695 | (((sp - cp) + cc + (p1 - p)) * | |
696 | sizeof(Char))); | |
697 | /* | |
698 | * Copy new path into newcp | |
699 | */ | |
700 | for (p2 = cp; *p1++ = *p2++;); | |
701 | for (p1--, p2 = link; *p1++ = *p2++;); | |
702 | for (p1--, p2 = p; *p1++ = *p2++;); | |
703 | /* | |
704 | * Restart canonicalization at expanded "/xxx". | |
705 | */ | |
706 | p = sp - cp - 1 + newcp; | |
707 | } | |
708 | else { | |
709 | /* | |
710 | * New length is link + "/.." and rest | |
711 | */ | |
712 | p1 = newcp = (Char *) xmalloc((size_t) | |
713 | ((cc + (p1 - p)) * sizeof(Char))); | |
714 | /* | |
715 | * Copy new path into newcp | |
716 | */ | |
717 | for (p2 = link; *p1++ = *p2++;); | |
718 | for (p1--, p2 = p; *p1++ = *p2++;); | |
719 | /* | |
720 | * Restart canonicalization at beginning | |
721 | */ | |
722 | p = newcp; | |
723 | } | |
724 | xfree((ptr_t) cp); | |
725 | cp = newcp; | |
726 | continue; /* canonicalize the link */ | |
727 | } | |
728 | *sp = '/'; | |
729 | if (sp != cp) | |
730 | while (*--sp != '/'); | |
731 | if (slash) { | |
732 | for (p1 = sp + 1, p2 = p + 1; *p1++ = *p2++;); | |
733 | p = sp; | |
734 | } | |
735 | else if (cp == sp) | |
736 | *++sp = '\0'; | |
737 | else | |
738 | *sp = '\0'; | |
739 | } | |
740 | else { /* normal dir name (not . or .. or nothing) */ | |
741 | ||
742 | if (sp != cp && adrof(STRchase_symlinks) && | |
743 | !adrof(STRignore_symlinks) && | |
744 | (cc = readlink(short2str(cp), tlink, | |
745 | sizeof tlink)) >= 0) { | |
746 | (void) Strcpy(link, str2short(tlink)); | |
747 | link[cc] = '\0'; | |
748 | ||
749 | /* | |
750 | * restore the '/'. | |
751 | */ | |
752 | if (slash) | |
753 | *p = '/'; | |
754 | ||
755 | /* | |
756 | * point sp to p (rather than backing up). | |
757 | */ | |
758 | sp = p; | |
759 | ||
760 | /* | |
761 | * find length of p | |
762 | */ | |
763 | for (p1 = p; *p1++;); | |
764 | if (*link != '/') { | |
765 | /* | |
766 | * Relative path, expand it between the "yyy/" and the | |
767 | * remainder. First, back sp up to the character past | |
768 | * "yyy/". | |
769 | */ | |
770 | while (*--sp != '/'); | |
771 | sp++; | |
772 | *sp = 0; | |
773 | /* | |
774 | * New length is "yyy/" + link + "/.." and rest | |
775 | */ | |
776 | p1 = newcp = (Char *) xmalloc((size_t) | |
777 | (((sp - cp) + cc + (p1 - p)) | |
778 | * sizeof(Char))); | |
779 | /* | |
780 | * Copy new path into newcp | |
781 | */ | |
782 | for (p2 = cp; *p1++ = *p2++;); | |
783 | for (p1--, p2 = link; *p1++ = *p2++;); | |
784 | for (p1--, p2 = p; *p1++ = *p2++;); | |
785 | /* | |
786 | * Restart canonicalization at expanded "/xxx". | |
787 | */ | |
788 | p = sp - cp - 1 + newcp; | |
789 | } | |
790 | else { | |
791 | /* | |
792 | * New length is link + the rest | |
793 | */ | |
794 | p1 = newcp = (Char *) xmalloc((size_t) | |
795 | ((cc + (p1 - p)) * sizeof(Char))); | |
796 | /* | |
797 | * Copy new path into newcp | |
798 | */ | |
799 | for (p2 = link; *p1++ = *p2++;); | |
800 | for (p1--, p2 = p; *p1++ = *p2++;); | |
801 | /* | |
802 | * Restart canonicalization at beginning | |
803 | */ | |
804 | p = newcp; | |
805 | } | |
806 | xfree((ptr_t) cp); | |
807 | cp = newcp; | |
808 | continue; /* canonicalize the link */ | |
809 | } | |
810 | if (slash) | |
811 | *p = '/'; | |
812 | } | |
813 | } | |
814 | ||
815 | /* | |
816 | * fix home... | |
817 | */ | |
818 | p1 = value(STRhome); | |
819 | cc = Strlen(p1); | |
820 | /* | |
821 | * See if we're not in a subdir of STRhome | |
822 | */ | |
15d0c357 | 823 | if (p1 && *p1 == '/' && |
6e37afca KB |
824 | (Strncmp(p1, cp, cc) != 0 || (cp[cc] != '/' && cp[cc] != '\0'))) { |
825 | static ino_t home_ino = -1; | |
826 | static dev_t home_dev = -1; | |
0aec749d | 827 | static Char *home_ptr = NULL; |
6e37afca KB |
828 | struct stat statbuf; |
829 | ||
830 | /* | |
831 | * Get dev and ino of STRhome | |
832 | */ | |
833 | if (home_ptr != p1 && | |
834 | stat(short2str(p1), &statbuf) != -1) { | |
835 | home_dev = statbuf.st_dev; | |
836 | home_ino = statbuf.st_ino; | |
837 | home_ptr = p1; | |
838 | } | |
839 | /* | |
840 | * Start comparing dev & ino backwards | |
841 | */ | |
842 | p2 = Strcpy(link, cp); | |
0aec749d | 843 | for (sp = NULL; *p2 && stat(short2str(p2), &statbuf) != -1;) { |
6e37afca KB |
844 | if (statbuf.st_dev == home_dev && |
845 | statbuf.st_ino == home_ino) { | |
846 | sp = (Char *) - 1; | |
847 | break; | |
848 | } | |
849 | if (sp = Strrchr(p2, '/')) | |
850 | *sp = '\0'; | |
851 | } | |
852 | /* | |
853 | * See if we found it | |
854 | */ | |
0aec749d | 855 | if (*p2 && sp == (Char *) -1) { |
6e37afca KB |
856 | /* |
857 | * Use STRhome to make '~' work | |
858 | */ | |
859 | p2 = cp + Strlen(p2); | |
860 | sp = newcp = (Char *) xmalloc((size_t) | |
861 | ((cc + Strlen(p2)) * sizeof(Char))); | |
862 | while (*p1) | |
863 | *sp++ = *p1++; | |
864 | while (*p2) | |
865 | *sp++ = *p2++; | |
866 | *sp = '\0'; | |
867 | xfree((ptr_t) cp); | |
868 | cp = newcp; | |
869 | } | |
870 | } | |
871 | return cp; | |
106427f7 BJ |
872 | } |
873 | ||
6e37afca | 874 | |
106427f7 BJ |
875 | /* |
876 | * dnewcwd - make a new directory in the loop the current one | |
877 | */ | |
6e37afca | 878 | static void |
106427f7 | 879 | dnewcwd(dp) |
6e37afca | 880 | register struct directory *dp; |
106427f7 | 881 | { |
6e37afca KB |
882 | dcwd = dp; |
883 | dset(dcwd->di_name); | |
884 | if (printd && !(adrof(STRpushdsilent))) | |
885 | printdirs(); | |
106427f7 | 886 | } |