8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / cmd / csh / sh.dir.c
blob29031692f5b97027315e5e45cb057a0a982168c5
1 /*
2 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 * Copyright (c) 2016 by Delphix. All rights reserved.
5 */
7 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
8 /* All Rights Reserved */
11 * Copyright (c) 1980 Regents of the University of California.
12 * All rights reserved. The Berkeley Software License Agreement
13 * specifies the terms and conditions for redistribution.
16 #pragma ident "%Z%%M% %I% %E% SMI"
18 #include "sh.h"
19 #include "sh.dir.h"
20 #include "sh.tconst.h"
23 * C Shell - directory management
26 struct directory *dfind(tchar *);
27 tchar *dfollow(tchar *);
28 tchar *dcanon(tchar *, tchar *);
29 void dtildepr(tchar *, tchar *);
30 void dfree(struct directory *);
31 void dnewcwd(struct directory *);
33 struct directory dhead; /* "head" of loop */
34 int printd; /* force name to be printed */
35 static tchar *fakev[] = { S_dirs, NOSTR };
38 * dinit - initialize current working directory
40 void
41 dinit(tchar *hp)
43 tchar *cp;
44 struct directory *dp;
45 tchar path[MAXPATHLEN];
47 #ifdef TRACE
48 tprintf("TRACE- dinit()\n");
49 #endif
51 * If this is a login shell, we should have a home directory. But,
52 * if we got here via 'su - <user>' where the user has no directory
53 * in their passwd file, then su has passed HOME=<nothing>, so hp is
54 * non-null, but has zero length. Thus, we do not know the current
55 * working directory based on the home directory.
57 if (loginsh && hp && *hp)
58 cp = hp;
59 else {
60 cp = getwd_(path);
61 if (cp == NULL) {
62 printf("Warning: cannot determine current directory\n");
63 cp = S_DOT;
66 dp = (struct directory *)xcalloc(sizeof (struct directory), 1);
67 dp->di_name = savestr(cp);
68 dp->di_count = 0;
69 dhead.di_next = dhead.di_prev = dp;
70 dp->di_next = dp->di_prev = &dhead;
71 printd = 0;
72 dnewcwd(dp);
76 * dodirs - list all directories in directory loop
78 void
79 dodirs(tchar **v)
81 struct directory *dp;
82 bool lflag;
83 tchar *hp = value(S_home);
85 #ifdef TRACE
86 tprintf("TRACE- dodirs()\n");
87 #endif
88 if (*hp == '\0')
89 hp = NOSTR;
90 if (*++v != NOSTR)
91 if (eq(*v, S_MINl /* "-l" */) && *++v == NOSTR)
92 lflag = 1;
93 else
94 error("Usage: dirs [ -l ]");
95 else
96 lflag = 0;
97 dp = dcwd;
98 do {
99 if (dp == &dhead)
100 continue;
101 if (!lflag && hp != NOSTR) {
102 dtildepr(hp, dp->di_name);
103 } else
104 printf("%t", dp->di_name);
105 printf(" ");
106 } while ((dp = dp->di_prev) != dcwd);
107 printf("\n");
110 void
111 dtildepr(tchar *home, tchar *dir)
114 #ifdef TRACE
115 tprintf("TRACE- dtildepr()\n");
116 #endif
117 if (!eq(home, S_SLASH /* "/" */) && prefix(home, dir))
118 printf("~%t", dir + strlen_(home));
119 else
120 printf("%t", dir);
124 * dochngd - implement chdir command.
126 void
127 dochngd(tchar **v)
129 tchar *cp;
130 struct directory *dp;
132 #ifdef TRACE
133 tprintf("TRACE- dochngd()\n");
134 #endif
135 printd = 0;
136 if (*++v == NOSTR) {
137 if ((cp = value(S_home)) == NOSTR || *cp == 0)
138 bferr("No home directory");
139 if (chdir_(cp) < 0)
140 bferr("Can't change to home directory");
141 cp = savestr(cp);
142 } else if ((dp = dfind(*v)) != 0) {
143 printd = 1;
144 if (chdir_(dp->di_name) < 0)
145 Perror(dp->di_name);
146 dcwd->di_prev->di_next = dcwd->di_next;
147 dcwd->di_next->di_prev = dcwd->di_prev;
148 goto flushcwd;
149 } else
150 cp = dfollow(*v);
151 dp = (struct directory *)xcalloc(sizeof (struct directory), 1);
152 dp->di_name = cp;
153 dp->di_count = 0;
154 dp->di_next = dcwd->di_next;
155 dp->di_prev = dcwd->di_prev;
156 dp->di_prev->di_next = dp;
157 dp->di_next->di_prev = dp;
158 flushcwd:
159 dfree(dcwd);
160 dnewcwd(dp);
164 * dfollow - change to arg directory; fall back on cdpath if not valid
166 tchar *
167 dfollow(tchar *cp)
169 tchar *dp;
170 struct varent *c;
171 int cdhashval, cdhashval1;
172 int index;
173 int slash; /* slashes in the argument */
174 tchar *fullpath;
175 tchar *slashcp; /* cp string prepended with a slash */
177 #ifdef TRACE
178 tprintf("TRACE- dfollow()\n");
179 #endif
180 cp = globone(cp);
181 if (chdir_(cp) >= 0)
182 goto gotcha;
185 * If the directory argument has a slash in it,
186 * for example, directory/directory, then can't
187 * find that in the cache table.
189 slash = any('/', cp);
192 * Try interpreting wrt successive components of cdpath.
193 * cdpath caching is turned off or directory argument
194 * has a slash in it.
196 if (cp[0] != '/'
197 && !prefix(S_DOTSLA /* "./" */, cp)
198 && !prefix(S_DOTDOTSLA /* "../" */, cp)
199 && (c = adrof(S_cdpath))
200 && (!havhash2 || slash)) {
201 tchar **cdp;
202 tchar *p;
203 tchar buf[MAXPATHLEN];
205 for (cdp = c->vec; *cdp; cdp++) {
206 for (dp = buf, p = *cdp; *dp++ = *p++; )
208 dp[-1] = '/';
209 for (p = cp; *dp++ = *p++; )
211 if (chdir_(buf) >= 0) {
212 printd = 1;
213 xfree(cp);
214 cp = savestr(buf);
215 goto gotcha;
220 /* cdpath caching turned on */
221 if (cp[0] != '/'
222 && !prefix(S_DOTSLA /* "./" */, cp)
223 && !prefix(S_DOTDOTSLA /* "../" */, cp)
224 && (c = adrof(S_cdpath))
225 && havhash2 && !slash) {
226 tchar **pv;
228 /* If no cdpath or no paths in cdpath, leave */
229 if (c == 0 || c->vec[0] == 0)
230 pv = justabs;
231 else
232 pv = c->vec;
234 slashcp = strspl(S_SLASH, cp);
236 cdhashval = hashname(cp);
238 /* index points to next path component to test */
239 index = 0;
242 * Look at each path in cdpath until get a match.
243 * Only look at those path beginning with a slash
245 do {
246 /* only check cache for absolute pathnames */
247 if (pv[0][0] == '/') {
248 cdhashval1 = hash(cdhashval, index);
249 if (bit(xhash2, cdhashval1)) {
251 * concatenate found path with
252 * arg directory
254 fullpath = strspl(*pv, slashcp);
255 if (chdir_(fullpath) >= 0) {
256 printd = 1;
257 xfree(cp);
258 cp = savestr(fullpath);
259 xfree(slashcp);
260 xfree(fullpath);
261 goto gotcha;
266 * relative pathnames are not cached, and must be
267 * checked manually
269 else {
270 tchar *p;
271 tchar buf[MAXPATHLEN];
273 for (dp = buf, p = *pv; *dp++ = *p++; )
275 dp[-1] = '/';
276 for (p = cp; *dp++ = *p++; )
278 if (chdir_(buf) >= 0) {
279 printd = 1;
280 xfree(cp);
281 cp = savestr(buf);
282 xfree(slashcp);
283 goto gotcha;
286 pv++;
287 index++;
288 } while (*pv);
292 * Try dereferencing the variable named by the argument.
294 dp = value(cp);
295 if ((dp[0] == '/' || dp[0] == '.') && chdir_(dp) >= 0) {
296 xfree(cp);
297 cp = savestr(dp);
298 printd = 1;
299 goto gotcha;
301 xfree(cp); /* XXX, use after free */
302 Perror(cp);
304 gotcha:
305 if (*cp != '/') {
306 tchar *p, *q;
307 int cwdlen;
308 int len;
311 * All in the name of efficiency?
314 if ((cwdlen = (strlen_(dcwd->di_name))) == 1) {
315 if (*dcwd->di_name == '/') /* root */
316 cwdlen = 0;
317 else
320 * if we are here, when the shell started
321 * it was unable to getwd(), lets try it again
323 tchar path[MAXPATHLEN];
325 p = getwd_(path);
326 if (p == NULL)
327 error("cannot determine current directory");
328 else
330 xfree(dcwd->di_name);
331 dcwd->di_name = savestr(p);
332 xfree(cp);
333 cp = savestr(p);
334 return dcanon(cp, cp);
341 * for (p = cp; *p++;)
343 * dp = (tchar *)xalloc((unsigned) (cwdlen + (p - cp) + 1)*sizeof (tchar))
345 len = strlen_(cp);
346 dp = (tchar *)xalloc((unsigned)(cwdlen + len + 2) * sizeof (tchar));
347 for (p = dp, q = dcwd->di_name; *p++ = *q++; )
349 if (cwdlen)
350 p[-1] = '/';
351 else
352 p--; /* don't add a / after root */
353 for (q = cp; *p++ = *q++; )
355 xfree(cp);
356 cp = dp;
357 dp += cwdlen;
358 } else
359 dp = cp;
360 return dcanon(cp, dp);
364 * dopushd - push new directory onto directory stack.
365 * with no arguments exchange top and second.
366 * with numeric argument (+n) bring it to top.
368 void
369 dopushd(tchar **v)
371 struct directory *dp;
373 #ifdef TRACE
374 tprintf("TRACE- dopushd()\n");
375 #endif
376 printd = 1;
377 if (*++v == NOSTR) {
378 if ((dp = dcwd->di_prev) == &dhead)
379 dp = dhead.di_prev;
380 if (dp == dcwd)
381 bferr("No other directory");
382 if (chdir_(dp->di_name) < 0)
383 Perror(dp->di_name);
384 dp->di_prev->di_next = dp->di_next;
385 dp->di_next->di_prev = dp->di_prev;
386 dp->di_next = dcwd->di_next;
387 dp->di_prev = dcwd;
388 dcwd->di_next->di_prev = dp;
389 dcwd->di_next = dp;
390 } else if (dp = dfind(*v)) {
391 if (chdir_(dp->di_name) < 0)
392 Perror(dp->di_name);
393 } else {
394 tchar *cp;
396 cp = dfollow(*v);
397 dp = (struct directory *)xcalloc(sizeof (struct directory), 1);
398 dp->di_name = cp;
399 dp->di_count = 0;
400 dp->di_prev = dcwd;
401 dp->di_next = dcwd->di_next;
402 dcwd->di_next = dp;
403 dp->di_next->di_prev = dp;
405 dnewcwd(dp);
409 * dfind - find a directory if specified by numeric (+n) argument
411 struct directory *
412 dfind(tchar *cp)
414 struct directory *dp;
415 int i;
416 tchar *ep;
418 #ifdef TRACE
419 tprintf("TRACE- dfind()\n");
420 #endif
421 if (*cp++ != '+')
422 return (0);
423 for (ep = cp; digit(*ep); ep++)
424 continue;
425 if (*ep)
426 return (0);
427 i = getn(cp);
428 if (i <= 0)
429 return (0);
430 for (dp = dcwd; i != 0; i--) {
431 if ((dp = dp->di_prev) == &dhead)
432 dp = dp->di_prev;
433 if (dp == dcwd)
434 bferr("Directory stack not that deep");
436 return (dp);
440 * dopopd - pop a directory out of the directory stack
441 * with a numeric argument just discard it.
443 void
444 dopopd(tchar **v)
446 struct directory *dp, *p;
448 #ifdef TRACE
449 tprintf("TRACE- dopopd()\n");
450 #endif
451 printd = 1;
452 if (*++v == NOSTR)
453 dp = dcwd;
454 else if ((dp = dfind(*v)) == 0)
455 bferr("Invalid argument");
456 if (dp->di_prev == &dhead && dp->di_next == &dhead)
457 bferr("Directory stack empty");
458 if (dp == dcwd) {
459 if ((p = dp->di_prev) == &dhead)
460 p = dhead.di_prev;
461 if (chdir_(p->di_name) < 0)
462 Perror(p->di_name);
464 dp->di_prev->di_next = dp->di_next;
465 dp->di_next->di_prev = dp->di_prev;
466 if (dp == dcwd)
467 dnewcwd(p);
468 else
469 dodirs(fakev);
470 dfree(dp);
474 * dfree - free the directory (or keep it if it still has ref count)
476 void
477 dfree(struct directory *dp)
480 #ifdef TRACE
481 tprintf("TRACE- dfree()\n");
482 #endif
483 if (dp->di_count != 0)
484 dp->di_next = dp->di_prev = 0;
485 else
486 xfree(dp->di_name), xfree((tchar *)dp);
490 * dcanon - canonicalize the pathname, removing excess ./ and ../ etc.
491 * We are of course assuming that the file system is standardly
492 * constructed (always have ..'s, directories have links).
494 * If the hardpaths shell variable is set, resolve the
495 * resulting pathname to contain no symbolic link components.
497 tchar *
498 dcanon(tchar *cp, tchar *p)
500 tchar *sp; /* rightmost component currently under
501 consideration */
502 tchar *p1, /* general purpose */
503 *p2;
504 bool slash, dotdot, hardpaths;
506 #ifdef TRACE
507 tprintf("TRACE- dcannon()\n");
508 #endif
510 if (*cp != '/')
511 abort();
513 if (hardpaths = (adrof(S_hardpaths) != NULL)) {
515 * Be paranoid: don't trust the initial prefix
516 * to be symlink-free.
518 p = cp;
522 * Loop invariant: cp points to the overall path start,
523 * p to its as yet uncanonicalized trailing suffix.
525 while (*p) { /* for each component */
526 sp = p; /* save slash address */
528 while (*++p == '/') /* flush extra slashes */
530 if (p != ++sp)
531 for (p1 = sp, p2 = p; *p1++ = *p2++; )
534 p = sp; /* save start of component */
535 slash = 0;
536 if (*p)
537 while (*++p) /* find next slash or end of path */
538 if (*p == '/') {
539 slash = 1;
540 *p = '\0';
541 break;
544 if (*sp == '\0') {
545 /* component is null */
546 if (--sp == cp) /* if path is one tchar (i.e. /) */
547 break;
548 else
549 *sp = '\0';
550 continue;
553 if (sp[0] == '.' && sp[1] == '\0') {
554 /* Squeeze out component consisting of "." */
555 if (slash) {
556 for (p1 = sp, p2 = p + 1; *p1++ = *p2++; )
558 p = --sp;
559 } else if (--sp != cp)
560 *sp = '\0';
561 continue;
565 * At this point we have a path of the form "x/yz",
566 * where "x" is null or rooted at "/", "y" is a single
567 * component, and "z" is possibly null. The pointer cp
568 * points to the start of "x", sp to the start of "y",
569 * and p to the beginning of "z", which has been forced
570 * to a null.
573 * Process symbolic link component. Provided that either
574 * the hardpaths shell variable is set or "y" is really
575 * ".." we replace the symlink with its contents. The
576 * second condition for replacement is necessary to make
577 * the command "cd x/.." produce the same results as the
578 * sequence "cd x; cd ..".
580 * Note that the two conditions correspond to different
581 * potential symlinks. When hardpaths is set, we must
582 * check "x/y"; otherwise, when "y" is known to be "..",
583 * we check "x".
585 dotdot = sp[0] == '.' && sp[1] == '.' && sp[2] == '\0';
586 if (hardpaths || dotdot) {
587 tchar link[MAXPATHLEN];
588 int cc;
589 tchar *newcp;
592 * Isolate the end of the component that is to
593 * be checked for symlink-hood.
595 sp--;
596 if (! hardpaths)
597 *sp = '\0';
600 * See whether the component is really a symlink by
601 * trying to read it. If the read succeeds, it is.
603 if ((hardpaths || sp > cp) &&
604 (cc = readlink_(cp, link, MAXPATHLEN)) >= 0) {
606 * readlink_ put null, so we don't need this.
608 /* link[cc] = '\0'; */
610 /* Restore path. */
611 if (slash)
612 *p = '/';
615 * Point p at the start of the trailing
616 * path following the symlink component.
617 * It's already there is hardpaths is set.
619 if (! hardpaths) {
620 /* Restore path as well. */
621 *(p = sp) = '/';
625 * Find length of p.
627 for (p1 = p; *p1++; )
630 if (*link != '/') {
632 * Relative path: replace the symlink
633 * component with its value. First,
634 * set sp to point to the slash at
635 * its beginning. If hardpaths is
636 * set, this is already the case.
638 if (! hardpaths) {
639 while (*--sp != '/')
644 * Terminate the leading part of the
645 * path, including trailing slash.
647 sp++;
648 *sp = '\0';
651 * New length is: "x/" + link + "z"
653 p1 = newcp = (tchar *)xalloc((unsigned)
654 ((sp - cp) + cc + (p1 - p)) * sizeof (tchar));
656 * Copy new path into newcp
658 for (p2 = cp; *p1++ = *p2++; )
660 for (p1--, p2 = link; *p1++ = *p2++; )
662 for (p1--, p2 = p; *p1++ = *p2++; )
665 * Restart canonicalization at
666 * expanded "/y".
668 p = sp - cp - 1 + newcp;
669 } else {
671 * New length is: link + "z"
673 p1 = newcp = (tchar *)xalloc((unsigned)
674 (cc + (p1 - p))*sizeof (tchar));
676 * Copy new path into newcp
678 for (p2 = link; *p1++ = *p2++; )
680 for (p1--, p2 = p; *p1++ = *p2++; )
683 * Restart canonicalization at beginning
685 p = newcp;
687 xfree(cp);
688 cp = newcp;
689 continue; /* canonicalize the link */
692 /* The component wasn't a symlink after all. */
693 if (! hardpaths)
694 *sp = '/';
697 if (dotdot) {
698 if (sp != cp)
699 while (*--sp != '/')
701 if (slash) {
702 for (p1 = sp + 1, p2 = p + 1; *p1++ = *p2++; )
704 p = sp;
705 } else if (cp == sp)
706 *++sp = '\0';
707 else
708 *sp = '\0';
709 continue;
712 if (slash)
713 *p = '/';
715 return cp;
719 * dnewcwd - make a new directory in the loop the current one
720 * and export its name to the PWD environment variable.
722 void
723 dnewcwd(struct directory *dp)
726 #ifdef TRACE
727 tprintf("TRACE- dnewcwd()\n");
728 #endif
729 dcwd = dp;
730 #ifdef notdef
732 * If we have a fast version of getwd available
733 * and hardpaths is set, it would be reasonable
734 * here to verify that dcwd->di_name really does
735 * name the current directory. Later...
737 #endif /* notdef */
739 didchdir = 1;
740 set(S_cwd, savestr(dcwd->di_name));
741 didchdir = 0;
742 local_setenv(S_PWD, dcwd->di_name);
743 if (printd)
744 dodirs(fakev);