1: /*
2: * sh.dir.c: Directory manipulation functions
3: */
4: /*-
5: * Copyright (c) 1980, 1991 The Regents of the University of California.
6: * All rights reserved.
7: *
8: * Redistribution and use in source and binary forms, with or without
9: * modification, are permitted provided that the following conditions
10: * are met:
11: * 1. Redistributions of source code must retain the above copyright
12: * notice, this list of conditions and the following disclaimer.
13: * 2. Redistributions in binary form must reproduce the above copyright
14: * notice, this list of conditions and the following disclaimer in the
15: * documentation and/or other materials provided with the distribution.
16: * 3. All advertising materials mentioning features or use of this software
17: * must display the following acknowledgement:
18: * This product includes software developed by the University of
19: * California, Berkeley and its contributors.
20: * 4. Neither the name of the University nor the names of its contributors
21: * may be used to endorse or promote products derived from this software
22: * without specific prior written permission.
23: *
24: * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27: * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34: * SUCH DAMAGE.
35: */
36: #include "config.h"
37: #if !defined(lint) && !defined(pdp11)
38: static char *rcsid()
39: { return "$Id: sh.dir.c,v 3.0.1 1996/04/04 21:49:28 sms Exp $"; }
40: #endif
41:
42:
43: #include "sh.h"
44: /*
45: * C Shell - directory management
46: */
47:
48: static struct directory *dfind __P((Char *));
49: static Char *dfollow __P((Char *));
50: static void printdirs __P((void));
51: static Char *dgoto __P((Char *));
52: static void dnewcwd __P((struct directory *));
53: static void dset __P((Char *));
54:
55: struct directory dhead; /* "head" of loop */
56: int printd; /* force name to be printed */
57:
58: #ifdef CSHDIRS
59: int bequiet = 0; /* do not print dir stack -strike */
60:
61: #endif
62: static int dirflag = 0;
63:
64: /*
65: * dinit - initialize current working directory
66: */
67: void
68: dinit(hp)
69: Char *hp;
70: {
71: register char *tcp;
72: register Char *cp;
73: register struct directory *dp;
74: char path[MAXPATHLEN];
75: static char *emsg = "tcsh: Trying to start from \"%s\"\n";
76:
77: /* Don't believe the login shell home, because it may be a symlink */
78: tcp = getwd(path); /* see ngetwd.c for System V version */
79: if (tcp == NULL || *tcp == '\0') {
80: (void) xprintf("tcsh: %s\n", path);
81: if (hp && *hp) {
82: tcp = short2str(hp);
83: (void) xprintf(emsg, tcp);
84: if (chdir(tcp) == -1)
85: cp = NULL;
86: else
87: cp = hp;
88: }
89: else
90: cp = NULL;
91: if (cp == NULL) {
92: (void) xprintf(emsg, "/");
93: if (chdir("/") == -1)
94: /* I am not even try to print an error message! */
95: xexit(1);
96: cp = SAVE("/");
97: }
98: }
99: else {
100: #ifdef S_IFLNK
101: struct stat swd, shp;
102:
103: /*
104: * See if $HOME is the working directory we got and use that
105: */
106: if (hp && *hp &&
107: stat(tcp, &swd) != -1 && stat(short2str(hp), &shp) != -1 &&
108: swd.st_dev == shp.st_dev && swd.st_ino == shp.st_ino)
109: cp = hp;
110: else {
111: char *cwd;
112:
113: /*
114: * use PWD if we have it (for subshells)
115: */
116: if (cwd = getenv("PWD")) {
117: if (stat(cwd, &shp) != -1 && swd.st_dev == shp.st_dev &&
118: swd.st_ino == shp.st_ino)
119: tcp = cwd;
120: }
121: cp = dcanon(str2short(tcp), STRNULL);
122: }
123: #else /* S_IFLNK */
124: cp = dcanon(str2short(tcp), STRNULL);
125: #endif /* S_IFLNK */
126: }
127:
128: dp = (struct directory *) xcalloc(sizeof(struct directory), 1);
129: dp->di_name = Strsave(cp);
130: dp->di_count = 0;
131: dhead.di_next = dhead.di_prev = dp;
132: dp->di_next = dp->di_prev = &dhead;
133: printd = 0;
134: dnewcwd(dp);
135: }
136:
137: static void
138: dset(dp)
139: Char *dp;
140: {
141: /*
142: * Don't call set() directly cause if the directory contains ` or
143: * other junk characters glob will fail.
144: */
145: register Char **vec = (Char **) xmalloc((size_t) (2 * sizeof(Char **)));
146:
147: vec[0] = Strsave(dp);
148: vec[1] = 0;
149: setq(STRcwd, vec, &shvhed);
150: Setenv(STRPWD, dp);
151: }
152:
153: #define DIR_LONG 1
154: #define DIR_VERT 2
155: #define DIR_LINE 4
156:
157: static void
158: skipargs(v, str)
159: Char ***v;
160: char *str;
161: {
162: register Char **n = *v, *s;
163:
164: dirflag = 0;
165: for (n++; *n != NOSTR && (*n)[0] == '-'; n++)
166: for (s = &((*n)[1]); *s; s++)
167: switch (*s) {
168: case 'l':
169: dirflag |= DIR_LONG;
170: break;
171: case 'v':
172: dirflag |= DIR_VERT;
173: break;
174: case 'n':
175: dirflag |= DIR_LINE;
176: break;
177: default:
178: stderror(ERR_DIRUS, short2str(**v), str);
179: break;
180: }
181: *v = n;
182: }
183:
184: /*
185: * dodirs - list all directories in directory loop
186: */
187: void
188: dodirs(v)
189: Char **v;
190: {
191: skipargs(&v, "");
192:
193: if (*v != NOSTR)
194: stderror(ERR_DIRUS, "dirs", "");
195: printdirs();
196: }
197:
198: static void
199: printdirs()
200: {
201: register struct directory *dp;
202: Char *s, *hp = value(STRhome);
203: int idx, len, cur;
204: extern int T_Cols;
205:
206: if (*hp == '\0')
207: hp = NOSTR;
208: dp = dcwd;
209: idx = 0;
210: cur = 0;
211: do {
212: if (dp == &dhead)
213: continue;
214: if (dirflag & DIR_VERT) {
215: xprintf("%d\t", idx++);
216: cur = 0;
217: }
218: if (!(dirflag & DIR_LONG) && hp != NOSTR && !eq(hp, STRslash) &&
219: prefix(hp, dp->di_name))
220: len = Strlen(s = (dp->di_name + Strlen(hp))) + 2;
221: else
222: len = Strlen(s = dp->di_name) + 1;
223:
224: cur += len;
225: if ((dirflag & DIR_LINE) && cur >= T_Cols - 1 && len < T_Cols) {
226: xprintf("\n");
227: cur = len;
228: }
229: xprintf(s != dp->di_name ? "~%s%c" : "%s%c",
230: short2str(s), (dirflag & DIR_VERT) ? '\n' : ' ');
231: } while ((dp = dp->di_prev) != dcwd);
232: if (!(dirflag & DIR_VERT))
233: xprintf("\n");
234: }
235:
236: void
237: dtildepr(home, dir)
238: register Char *home, *dir;
239: {
240:
241: if (!eq(home, STRslash) && prefix(home, dir))
242: xprintf("~%s", short2str(dir + Strlen(home)));
243: else
244: xprintf("%s", short2str(dir));
245: }
246:
247: void
248: dtilde()
249: {
250: register struct directory *d = dcwd;
251:
252: do {
253: if (d == &dhead)
254: continue;
255: d->di_name = dcanon(d->di_name, STRNULL);
256: } while ((d = d->di_prev) != dcwd);
257:
258: dset(dcwd->di_name);
259: }
260:
261:
262: /* dnormalize():
263: * If the name starts with . or .. then we might need to normalize
264: * it depending on the symbolic link flags
265: */
266: Char *
267: dnormalize(cp)
268: register Char *cp;
269: {
270:
271: #define UC (unsigned char)
272: #define ISDOT(c) (UC(c)[0] == '.' && ((UC(c)[1] == '\0') || (UC(c)[1] == '/')))
273: #define ISDOTDOT(c) (UC(c)[0] == '.' && ISDOT(&((c)[1])))
274:
275: if ((unsigned char) cp[0] == '/')
276: return (Strsave(cp));
277:
278: #ifdef S_IFLNK
279: if (adrof(STRig__symlinks)) {
280: int dotdot = 0;
281: register Char *dp, *cwd;
282: #ifdef apollo
283: bool slashslash;
284: #endif
285:
286: cwd = (Char *) xmalloc((size_t) ((Strlen(dcwd->di_name) + 3) *
287: sizeof(Char)));
288: (void) Strcpy(cwd, dcwd->di_name);
289: #ifdef apollo
290: slashslash = cwd[0] == '/' && cwd[1] == '/';
291: #endif
292:
293: /*
294: * Ignore . and count ..'s
295: */
296: while (*cp) {
297: if (ISDOT(cp)) {
298: if (*++cp)
299: cp++;
300: }
301: else if (ISDOTDOT(cp)) {
302: dotdot++;
303: cp += 2;
304: if (*cp)
305: cp++;
306: }
307: else
308: break;
309: }
310: while (dotdot > 0)
311: if ((dp = Strrchr(cwd, '/'))) {
312: #ifdef apollo
313: if (dp == &cwd[1])
314: slashslash = 1;
315: #endif
316: *dp = '\0';
317: dotdot--;
318: }
319: else
320: break;
321:
322: if (*cp) {
323: if (((unsigned char) cwd[(dotdot = Strlen(cwd)) - 1]) != '/')
324: cwd[dotdot++] = '/';
325: cwd[dotdot] = '\0';
326: dp = Strspl(cwd, cp);
327: xfree((ptr_t) cwd);
328: return dp;
329: }
330: else {
331: if (!*cwd) {
332: cwd[0] = '/';
333: #ifdef apollo
334: cwd[1] = '/';
335: cwd[2] = '\0';
336: #else
337: cwd[1] = '\0';
338: #endif
339: }
340: #ifdef apollo
341: else if (slashslash && cwd[1] == '\0') {
342: cwd[1] = '/';
343: cwd[2] = '\0';
344: }
345: #endif
346: return cwd;
347: }
348: }
349: #endif
350: return Strsave(cp);
351: }
352:
353: /*
354: * dochngd - implement chdir command.
355: */
356: void
357: dochngd(v)
358: Char **v;
359: {
360: register Char *cp;
361: register struct directory *dp;
362:
363: skipargs(&v, " [<dir>]");
364: printd = 0;
365: if (*v == NOSTR) {
366: if ((cp = value(STRhome)) == NOSTR || *cp == 0)
367: stderror(ERR_NAME | ERR_NOHOMEDIR);
368: if (chdir(short2str(cp)) < 0)
369: stderror(ERR_NAME | ERR_CANTCHANGE);
370: cp = Strsave(cp);
371: }
372: else if (v[1] != NOSTR) {
373: stderror(ERR_NAME | ERR_TOOMANY);
374: /* NOTREACHED */
375: return;
376: }
377: else if ((dp = dfind(*v)) != 0) {
378: char *tmp;
379:
380: printd = 1;
381: if (chdir(tmp = short2str(dp->di_name)) < 0)
382: stderror(ERR_SYSTEM, tmp, strerror(errno));
383: dcwd->di_prev->di_next = dcwd->di_next;
384: dcwd->di_next->di_prev = dcwd->di_prev;
385: dfree(dcwd);
386: dnewcwd(dp);
387: return;
388: }
389: else
390: cp = dfollow(*v);
391: dp = (struct directory *) xcalloc(sizeof(struct directory), 1);
392: dp->di_name = cp;
393: dp->di_count = 0;
394: dp->di_next = dcwd->di_next;
395: dp->di_prev = dcwd->di_prev;
396: dp->di_prev->di_next = dp;
397: dp->di_next->di_prev = dp;
398: dfree(dcwd);
399: dnewcwd(dp);
400: }
401:
402: static Char *
403: dgoto(cp)
404: Char *cp;
405: {
406: Char *dp;
407:
408: if (*cp != '/') {
409: register Char *p, *q;
410: int cwdlen;
411:
412: for (p = dcwd->di_name; *p++;);
413: if ((cwdlen = p - dcwd->di_name - 1) == 1) /* root */
414: cwdlen = 0;
415: for (p = cp; *p++;);
416: dp = (Char *) xmalloc((size_t)((cwdlen + (p - cp) + 1) * sizeof(Char)));
417: for (p = dp, q = dcwd->di_name; *p++ = *q++;);
418: if (cwdlen)
419: p[-1] = '/';
420: else
421: p--; /* don't add a / after root */
422: for (q = cp; *p++ = *q++;);
423: xfree((ptr_t) cp);
424: cp = dp;
425: dp += cwdlen;
426: }
427: else
428: dp = cp;
429:
430: cp = dcanon(cp, dp);
431: return cp;
432: }
433:
434: /*
435: * dfollow - change to arg directory; fall back on cdpath if not valid
436: */
437: static Char *
438: dfollow(cp)
439: register Char *cp;
440: {
441: register Char *dp;
442: struct varent *c;
443: char ebuf[MAXPATHLEN];
444: int serrno;
445:
446: cp = globone(cp, G_ERROR);
447: #ifdef apollo
448: if (Strchr(cp, '`')) {
449: char *dptr, *ptr;
450: if (chdir(dptr = short2str(cp)) < 0)
451: stderror(ERR_SYSTEM, dptr, strerror(errno));
452: else if ((ptr = getwd(ebuf)) && *ptr != '\0') {
453: xfree((ptr_t) cp);
454: cp = Strsave(str2short(ptr));
455: return dgoto(cp);
456: }
457: else
458: stderror(ERR_SYSTEM, dptr, ebuf);
459: }
460: #endif
461:
462: /*
463: * if we are ignoring symlinks, try to fix relatives now.
464: */
465: dp = dnormalize(cp);
466: if (chdir(short2str(dp)) >= 0) {
467: xfree((ptr_t) cp);
468: return dgoto(dp);
469: }
470: else {
471: xfree((ptr_t) dp);
472: if (chdir(short2str(cp)) >= 0)
473: return dgoto(cp);
474: serrno = errno;
475: }
476:
477: if (cp[0] != '/' && !prefix(STRdt_l, cp) && !prefix(STRdotdotsl, cp)
478: && (c = adrof(STRcdpath))) {
479: Char **cdp;
480: register Char *p;
481: Char buf[MAXPATHLEN];
482:
483: for (cdp = c->vec; *cdp; cdp++) {
484: for (dp = buf, p = *cdp; *dp++ = *p++;);
485: dp[-1] = '/';
486: for (p = cp; *dp++ = *p++;);
487: if (chdir(short2str(buf)) >= 0) {
488: printd = 1;
489: xfree((ptr_t) cp);
490: cp = Strsave(buf);
491: return dgoto(cp);
492: }
493: }
494: }
495: dp = value(cp);
496: if ((dp[0] == '/' || dp[0] == '.') && chdir(short2str(dp)) >= 0) {
497: xfree((ptr_t) cp);
498: cp = Strsave(dp);
499: printd = 1;
500: return dgoto(cp);
501: }
502: (void) strcpy(ebuf, short2str(cp));
503: xfree((ptr_t) cp);
504: #ifdef CSHDIRS
505: /*
506: * on login source of ~/.cshdirs, errors are eaten. the dir stack is all
507: * directories we could get to.
508: */
509: if (!bequiet)
510: stderror(ERR_SYSTEM, ebuf, strerror(serrno));
511: else
512: return (NOSTR);
513: #else
514: stderror(ERR_SYSTEM, ebuf, strerror(serrno));
515: #endif
516: /* NOTREACHED */
517: return (NOSTR);
518: }
519:
520:
521: /*
522: * dopushd - push new directory onto directory stack.
523: * with no arguments exchange top and second.
524: * with numeric argument (+n) bring it to top.
525: */
526: void
527: dopushd(v)
528: Char **v;
529: {
530: register struct directory *dp;
531: register Char *cp;
532:
533: skipargs(&v, " [<dir>|+<n>]");
534: printd = 1;
535: if (*v == NOSTR) {
536: if (adrof(STRputohome)) {
537: if ((cp = value(STRhome)) == NOSTR || *cp == 0)
538: stderror(ERR_NAME | ERR_NOHOMEDIR);
539: if (chdir(short2str(cp)) < 0)
540: stderror(ERR_NAME | ERR_CANTCHANGE);
541: cp = Strsave(cp); /* hmmm... PWP */
542: cp = dfollow(cp);
543: dp = (struct directory *) xcalloc(sizeof(struct directory), 1);
544: dp->di_name = cp;
545: dp->di_count = 0;
546: dp->di_prev = dcwd;
547: dp->di_next = dcwd->di_next;
548: dcwd->di_next = dp;
549: dp->di_next->di_prev = dp;
550: }
551: else {
552: char *tmp;
553:
554: if ((dp = dcwd->di_prev) == &dhead)
555: dp = dhead.di_prev;
556: if (dp == dcwd)
557: stderror(ERR_NAME | ERR_NODIR);
558: if (chdir(tmp = short2str(dp->di_name)) < 0)
559: stderror(ERR_SYSTEM, tmp, strerror(errno));
560: dp->di_prev->di_next = dp->di_next;
561: dp->di_next->di_prev = dp->di_prev;
562: dp->di_next = dcwd->di_next;
563: dp->di_prev = dcwd;
564: dcwd->di_next->di_prev = dp;
565: dcwd->di_next = dp;
566: }
567: }
568: else if (v[1] != NOSTR) {
569: stderror(ERR_NAME | ERR_TOOMANY);
570: /* NOTREACHED */
571: return;
572: }
573: else if (dp = dfind(*v)) {
574: char *tmp;
575:
576: if (chdir(tmp = short2str(dp->di_name)) < 0)
577: stderror(ERR_SYSTEM, tmp, strerror(errno));
578: /*
579: * kfk - 10 Feb 1984 - added new "extraction style" pushd +n
580: */
581: if (adrof(STRdextract))
582: dextract(dp);
583: }
584: else {
585: register Char *ccp;
586:
587: ccp = dfollow(*v);
588: dp = (struct directory *) xcalloc(sizeof(struct directory), 1);
589: dp->di_name = ccp;
590: dp->di_count = 0;
591: dp->di_prev = dcwd;
592: dp->di_next = dcwd->di_next;
593: dcwd->di_next = dp;
594: dp->di_next->di_prev = dp;
595: }
596: dnewcwd(dp);
597: }
598:
599: /*
600: * dfind - find a directory if specified by numeric (+n) argument
601: */
602: static struct directory *
603: dfind(cp)
604: register Char *cp;
605: {
606: register struct directory *dp;
607: register int i;
608: register Char *ep;
609:
610: if (*cp++ != '+')
611: return (0);
612: for (ep = cp; Isdigit(*ep); ep++)
613: continue;
614: if (*ep)
615: return (0);
616: i = getn(cp);
617: if (i <= 0)
618: return (0);
619: for (dp = dcwd; i != 0; i--) {
620: if ((dp = dp->di_prev) == &dhead)
621: dp = dp->di_prev;
622: if (dp == dcwd)
623: stderror(ERR_NAME | ERR_DEEP);
624: }
625: return (dp);
626: }
627:
628: /*
629: * dopopd - pop a directory out of the directory stack
630: * with a numeric argument just discard it.
631: */
632: void
633: dopopd(v)
634: Char **v;
635: {
636: register struct directory *dp, *p = NULL;
637:
638: skipargs(&v, " [+<n>]");
639: printd = 1;
640: if (*v == NOSTR)
641: dp = dcwd;
642: else if (v[1] != NOSTR) {
643: stderror(ERR_NAME | ERR_TOOMANY);
644: /* NOTREACHED */
645: return;
646: }
647: else if ((dp = dfind(*v)) == 0)
648: stderror(ERR_NAME | ERR_BADDIR);
649: if (dp->di_prev == &dhead && dp->di_next == &dhead)
650: stderror(ERR_NAME | ERR_EMPTY);
651: if (dp == dcwd) {
652: char *tmp;
653:
654: if ((p = dp->di_prev) == &dhead)
655: p = dhead.di_prev;
656: if (chdir(tmp = short2str(p->di_name)) < 0)
657: stderror(ERR_SYSTEM, tmp, strerror(errno));
658: }
659: dp->di_prev->di_next = dp->di_next;
660: dp->di_next->di_prev = dp->di_prev;
661: if (dp == dcwd)
662: dnewcwd(p);
663: else {
664: printdirs();
665: }
666: dfree(dp);
667: }
668:
669: /*
670: * dfree - free the directory (or keep it if it still has ref count)
671: */
672: void
673: dfree(dp)
674: register struct directory *dp;
675: {
676:
677: if (dp->di_count != 0) {
678: dp->di_next = dp->di_prev = 0;
679: }
680: else {
681: xfree((char *) dp->di_name);
682: xfree((ptr_t) dp);
683: }
684: }
685:
686: /*
687: * dcanon - canonicalize the pathname, removing excess ./ and ../ etc.
688: * we are of course assuming that the file system is standardly
689: * constructed (always have ..'s, directories have links)
690: */
691: Char *
692: dcanon(cp, p)
693: register Char *cp, *p;
694: {
695: register Char *sp;
696: register Char *p1, *p2; /* general purpose */
697: bool slash;
698: #ifdef apollo
699: bool slashslash;
700: #endif
701:
702: #ifdef S_IFLNK /* if we have symlinks */
703: Char link[MAXPATHLEN];
704: char tlink[MAXPATHLEN];
705: int cc;
706: Char *newcp;
707: #endif /* S_IFLNK */
708:
709: /*
710: * christos: if the path given does not start with a slash prepend cwd. If
711: * cwd does not start with a slash or the result would be too long abort().
712: */
713: if (*cp != '/') {
714: Char tmpdir[MAXPATHLEN];
715:
716: p1 = value(STRcwd);
717: if (p1 == NULL || *p1 != '/')
718: abort();
719: if (Strlen(p1) + Strlen(cp) + 1 >= MAXPATHLEN)
720: abort();
721: (void) Strcpy(tmpdir, p1);
722: (void) Strcat(tmpdir, STRslash);
723: (void) Strcat(tmpdir, cp);
724: xfree((ptr_t) cp);
725: cp = p = Strsave(tmpdir);
726: }
727:
728: #ifdef COMMENT
729: if (*cp != '/')
730: abort();
731: #endif
732:
733: #ifdef apollo
734: slashslash = (cp[0] == '/' && cp[1] == '/');
735: #endif
736:
737: while (*p) { /* for each component */
738: sp = p; /* save slash address */
739: while (*++p == '/') /* flush extra slashes */
740: ;
741: if (p != ++sp)
742: for (p1 = sp, p2 = p; *p1++ = *p2++;);
743: p = sp; /* save start of component */
744: slash = 0;
745: while (*++p) /* find next slash or end of path */
746: if (*p == '/') {
747: slash = 1;
748: *p = 0;
749: break;
750: }
751:
752: #ifdef apollo
753: if (&cp[1] == sp && sp[0] == '.' && sp[1] == '.' && sp[2] == '\0')
754: slashslash = 1;
755: #endif
756: if (*sp == '\0') /* if component is null */
757: if (--sp == cp) /* if path is one char (i.e. /) */
758: break;
759: else
760: *sp = '\0';
761: else if (sp[0] == '.' && sp[1] == 0) {
762: if (slash) {
763: for (p1 = sp, p2 = p + 1; *p1++ = *p2++;);
764: p = --sp;
765: }
766: else if (--sp != cp)
767: *sp = '\0';
768: }
769: else if (sp[0] == '.' && sp[1] == '.' && sp[2] == 0) {
770: /*
771: * We have something like "yyy/xxx/..", where "yyy" can be null or
772: * a path starting at /, and "xxx" is a single component. Before
773: * compressing "xxx/..", we want to expand "yyy/xxx", if it is a
774: * symbolic link.
775: */
776: *--sp = 0; /* form the pathname for readlink */
777: #ifdef S_IFLNK /* if we have symlinks */
778: if (sp != cp && !adrof(STRig__symlinks) &&
779: (cc = readlink(short2str(cp), tlink,
780: sizeof tlink)) >= 0) {
781: (void) Strcpy(link, str2short(tlink));
782: link[cc] = '\0';
783:
784: if (slash)
785: *p = '/';
786: /*
787: * Point p to the '/' in "/..", and restore the '/'.
788: */
789: *(p = sp) = '/';
790: /*
791: * find length of p
792: */
793: for (p1 = p; *p1++;);
794: if (*link != '/') {
795: /*
796: * Relative path, expand it between the "yyy/" and the
797: * "/..". First, back sp up to the character past "yyy/".
798: */
799: while (*--sp != '/');
800: sp++;
801: *sp = 0;
802: /*
803: * New length is "yyy/" + link + "/.." and rest
804: */
805: p1 = newcp = (Char *) xmalloc((size_t)
806: (((sp - cp) + cc + (p1 - p)) *
807: sizeof(Char)));
808: /*
809: * Copy new path into newcp
810: */
811: for (p2 = cp; *p1++ = *p2++;);
812: for (p1--, p2 = link; *p1++ = *p2++;);
813: for (p1--, p2 = p; *p1++ = *p2++;);
814: /*
815: * Restart canonicalization at expanded "/xxx".
816: */
817: p = sp - cp - 1 + newcp;
818: }
819: else {
820: /*
821: * New length is link + "/.." and rest
822: */
823: p1 = newcp = (Char *) xmalloc((size_t)
824: ((cc + (p1 - p)) * sizeof(Char)));
825: /*
826: * Copy new path into newcp
827: */
828: for (p2 = link; *p1++ = *p2++;);
829: for (p1--, p2 = p; *p1++ = *p2++;);
830: /*
831: * Restart canonicalization at beginning
832: */
833: p = newcp;
834: }
835: xfree((ptr_t) cp);
836: cp = newcp;
837: continue; /* canonicalize the link */
838: }
839: #endif /* S_IFLNK */
840: *sp = '/';
841: if (sp != cp)
842: while (*--sp != '/');
843: if (slash) {
844: for (p1 = sp + 1, p2 = p + 1; *p1++ = *p2++;);
845: p = sp;
846: }
847: else if (cp == sp)
848: *++sp = '\0';
849: else
850: *sp = '\0';
851: }
852: else { /* normal dir name (not . or .. or nothing) */
853:
854: #ifdef S_IFLNK /* if we have symlinks */
855: if (sp != cp && adrof(STRchase_symlinks) &&
856: !adrof(STRig__symlinks) &&
857: (cc = readlink(short2str(cp), tlink,
858: sizeof tlink)) >= 0) {
859: (void) Strcpy(link, str2short(tlink));
860: link[cc] = '\0';
861:
862: /*
863: * restore the '/'.
864: */
865: if (slash)
866: *p = '/';
867:
868: /*
869: * point sp to p (rather than backing up).
870: */
871: sp = p;
872:
873: /*
874: * find length of p
875: */
876: for (p1 = p; *p1++;);
877: if (*link != '/') {
878: /*
879: * Relative path, expand it between the "yyy/" and the
880: * remainder. First, back sp up to the character past
881: * "yyy/".
882: */
883: while (*--sp != '/');
884: sp++;
885: *sp = 0;
886: /*
887: * New length is "yyy/" + link + "/.." and rest
888: */
889: p1 = newcp = (Char *) xmalloc((size_t)
890: (((sp - cp) + cc + (p1 - p))
891: * sizeof(Char)));
892: /*
893: * Copy new path into newcp
894: */
895: for (p2 = cp; *p1++ = *p2++;);
896: for (p1--, p2 = link; *p1++ = *p2++;);
897: for (p1--, p2 = p; *p1++ = *p2++;);
898: /*
899: * Restart canonicalization at expanded "/xxx".
900: */
901: p = sp - cp - 1 + newcp;
902: }
903: else {
904: /*
905: * New length is link + the rest
906: */
907: p1 = newcp = (Char *) xmalloc((size_t)
908: ((cc + (p1 - p)) * sizeof(Char)));
909: /*
910: * Copy new path into newcp
911: */
912: for (p2 = link; *p1++ = *p2++;);
913: for (p1--, p2 = p; *p1++ = *p2++;);
914: /*
915: * Restart canonicalization at beginning
916: */
917: p = newcp;
918: }
919: xfree((ptr_t) cp);
920: cp = newcp;
921: continue; /* canonicalize the link */
922: }
923: #endif /* S_IFLNK */
924: if (slash)
925: *p = '/';
926: }
927: }
928:
929: /*
930: * fix home...
931: */
932: #ifdef S_IFLNK
933: p1 = value(STRhome);
934: cc = Strlen(p1);
935: /*
936: * See if we're not in a subdir of STRhome
937: */
938: if (p1 && *p1 == '/' &&
939: (Strncmp(p1, cp, cc) != 0 || (cp[cc] != '/' && cp[cc] != '\0'))) {
940: static ino_t home_ino = -1;
941: static dev_t home_dev = -1;
942: static Char *home_ptr = NULL;
943: struct stat statbuf;
944:
945: /*
946: * Get dev and ino of STRhome
947: */
948: if (home_ptr != p1 &&
949: stat(short2str(p1), &statbuf) != -1) {
950: home_dev = statbuf.st_dev;
951: home_ino = statbuf.st_ino;
952: home_ptr = p1;
953: }
954: /*
955: * Start comparing dev & ino backwards
956: */
957: p2 = Strcpy(link, cp);
958: for (sp = NULL; *p2 && stat(short2str(p2), &statbuf) != -1;) {
959: if (statbuf.st_dev == home_dev &&
960: statbuf.st_ino == home_ino) {
961: sp = (Char *) - 1;
962: break;
963: }
964: if (sp = Strrchr(p2, '/'))
965: *sp = '\0';
966: }
967: /*
968: * See if we found it
969: */
970: if (*p2 && sp == (Char *) -1) {
971: /*
972: * Use STRhome to make '~' work
973: */
974: p2 = cp + Strlen(p2);
975: sp = newcp = (Char *) xmalloc((size_t)
976: ((cc + Strlen(p2) + 1) * sizeof(Char)));
977: while (*p1)
978: *sp++ = *p1++;
979: while (*p2)
980: *sp++ = *p2++;
981: *sp = '\0';
982: xfree((ptr_t) cp);
983: cp = newcp;
984: }
985: }
986: #endif /* S_IFLNK */
987:
988: #ifdef apollo
989: if (slashslash) {
990: if (cp[1] != '/') {
991: p = (Char *) xmalloc((size_t) (Strlen(cp) + 2) * sizeof(Char));
992: *p = '/';
993: (void) Strcpy(&p[1], cp);
994: xfree((ptr_t) cp);
995: cp = p;
996: }
997: }
998: if (cp[1] == '/' && cp[2] == '/')
999: (void) Strcpy(&cp[1], &cp[2]);
1000: #endif
1001: return cp;
1002: }
1003:
1004:
1005: /*
1006: * dnewcwd - make a new directory in the loop the current one
1007: */
1008: static void
1009: dnewcwd(dp)
1010: register struct directory *dp;
1011: {
1012: dcwd = dp;
1013: dset(dcwd->di_name);
1014: if (printd && !(adrof(STRpusilent)) /* PWP: pushdsilent */
1015: #ifdef CSHDIRS
1016: && !bequiet /* be quite while restoring stack -strike */
1017: #endif
1018: )
1019: printdirs();
1020: cwd_cmd(); /* PWP: run the defined cwd command */
1021: }
1022:
1023: /*
1024: * getstakd - added by kfk 17 Jan 1984
1025: * Support routine for the stack hack. Finds nth directory in
1026: * the directory stack, or finds last directory in stack.
1027: */
1028: int
1029: getstakd(s, cnt)
1030: Char *s;
1031: int cnt;
1032: {
1033: register struct directory *dp;
1034:
1035: dp = dcwd;
1036: if (cnt < 0) { /* < 0 ==> last dir requested. */
1037: dp = dp->di_next;
1038: if (dp == &dhead)
1039: dp = dp->di_next;
1040: }
1041: else {
1042: while (cnt-- > 0) {
1043: dp = dp->di_prev;
1044: if (dp == &dhead)
1045: dp = dp->di_prev;
1046: if (dp == dcwd)
1047: return (0);
1048: }
1049: }
1050: (void) Strcpy(s, dp->di_name);
1051: return (1);
1052: }
1053:
1054: /*
1055: * Karl Kleinpaste - 10 Feb 1984
1056: * Added dextract(), which is used in pushd +n.
1057: * Instead of just rotating the entire stack around, dextract()
1058: * lets the user have the nth dir extracted from its current
1059: * position, and pushes it onto the top.
1060: */
1061:
1062: dextract(dp)
1063: register struct directory *dp;
1064: {
1065: if (dp == dcwd)
1066: return;
1067: dp->di_next->di_prev = dp->di_prev;
1068: dp->di_prev->di_next = dp->di_next;
1069: dp->di_next = dcwd->di_next;
1070: dp->di_prev = dcwd;
1071: dp->di_next->di_prev = dp;
1072: dcwd->di_next = dp;
1073: }
1074:
1075: #ifdef CSHDIRS
1076: /*
1077: * create a file called ~/.cshdirs which has a sequence
1078: * of pushd commands which will restore the dir stack to
1079: * its state before exit/logout. remember that the order
1080: * is reversed in the file because we are pushing.
1081: * -strike
1082: */
1083: void
1084: recdirs()
1085: {
1086: int fp, ftmp, oldidfds;
1087: int cdflag = 0;
1088: extern int fast;
1089: Char buf[BUFSIZ];
1090:
1091: if (!fast) {
1092: if (!adrof(STRsvdirs))/* does it exist */
1093: return;
1094: (void) Strcpy(buf, value(STRhome));
1095: (void) Strcat(buf, STRs_dirs);
1096: if ((fp = creat(short2str(buf), 0666)) == -1)
1097: return;
1098: oldidfds = didfds;
1099: didfds = 0;
1100: ftmp = SHOUT;
1101: SHOUT = fp;
1102: {
1103: extern struct directory dhead;
1104: extern struct directory *dcwd;
1105: struct directory *dp = dcwd->di_next;
1106:
1107: do {
1108: if (dp == &dhead)
1109: continue;
1110: if (cdflag == 0)
1111: cdflag++, xprintf("cd %s\n",
1112: short2str(dp->di_name));
1113: else
1114: xprintf("pushd %s\n",
1115: short2str(dp->di_name));
1116: } while ((dp = dp->di_next) != dcwd->di_next);
1117: }
1118: xprintf("dirs\n"); /* show the dir stack */
1119:
1120: (void) close(fp);
1121: SHOUT = ftmp;
1122: didfds = oldidfds;
1123: }
1124: }
1125:
1126: #endif