1: /* 2: * Copyright (c) 1980 Regents of the University of California. 3: * All rights reserved. The Berkeley Software License Agreement 4: * specifies the terms and conditions for redistribution. 5: */ 6: 7: #if !defined(lint) && defined(DOSCCS) 8: static char *sccsid = "@(#)sh.dir.c 5.3 (Berkeley) 6/11/85"; 9: #endif 10: 11: #include "sh.h" 12: #include "sh.dir.h" 13: 14: /* 15: * C Shell - directory management 16: */ 17: 18: struct directory *dfind(); 19: char *dfollow(); 20: char *dcanon(); 21: struct directory dhead; /* "head" of loop */ 22: int printd; /* force name to be printed */ 23: static char *fakev[] = { "dirs", NOSTR }; 24: 25: /* 26: * dinit - initialize current working directory 27: */ 28: dinit(hp) 29: char *hp; 30: { 31: register char *cp; 32: register struct directory *dp; 33: char path[MAXPATHLEN]; 34: 35: if (loginsh && hp) 36: cp = hp; 37: else { 38: cp = getwd(path); 39: if (cp == NULL) { 40: (void) write(2, path, strlen(path)); 41: exit(1); 42: } 43: } 44: dp = (struct directory *)calloc(sizeof (struct directory), 1); 45: dp->di_name = savestr(cp); 46: dp->di_count = 0; 47: dhead.di_next = dhead.di_prev = dp; 48: dp->di_next = dp->di_prev = &dhead; 49: printd = 0; 50: dnewcwd(dp); 51: } 52: 53: /* 54: * dodirs - list all directories in directory loop 55: */ 56: dodirs(v) 57: char **v; 58: { 59: register struct directory *dp; 60: bool lflag; 61: char *hp = value("home"); 62: 63: if (*hp == '\0') 64: hp = NOSTR; 65: if (*++v != NOSTR) 66: if (eq(*v, "-l") && *++v == NOSTR) 67: lflag = 1; 68: else 69: error("Usage: dirs [ -l ]"); 70: else 71: lflag = 0; 72: dp = dcwd; 73: do { 74: if (dp == &dhead) 75: continue; 76: if (!lflag && hp != NOSTR) { 77: dtildepr(hp, dp->di_name); 78: } else 79: printf("%s", dp->di_name); 80: printf(" "); 81: } while ((dp = dp->di_prev) != dcwd); 82: printf("\n"); 83: } 84: 85: dtildepr(home, dir) 86: register char *home, *dir; 87: { 88: 89: if (!eq(home, "/") && prefix(home, dir)) 90: printf("~%s", dir + strlen(home)); 91: else 92: printf("%s", dir); 93: } 94: 95: /* 96: * dochngd - implement chdir command. 97: */ 98: dochngd(v) 99: char **v; 100: { 101: register char *cp; 102: register struct directory *dp; 103: 104: printd = 0; 105: if (*++v == NOSTR) { 106: if ((cp = value("home")) == NOSTR || *cp == 0) 107: bferr("No home directory"); 108: if (chdir(cp) < 0) 109: bferr("Can't change to home directory"); 110: cp = savestr(cp); 111: } else if ((dp = dfind(*v)) != 0) { 112: printd = 1; 113: if (chdir(dp->di_name) < 0) 114: Perror(dp->di_name); 115: dcwd->di_prev->di_next = dcwd->di_next; 116: dcwd->di_next->di_prev = dcwd->di_prev; 117: goto flushcwd; 118: } else 119: cp = dfollow(*v); 120: dp = (struct directory *)calloc(sizeof (struct directory), 1); 121: dp->di_name = cp; 122: dp->di_count = 0; 123: dp->di_next = dcwd->di_next; 124: dp->di_prev = dcwd->di_prev; 125: dp->di_prev->di_next = dp; 126: dp->di_next->di_prev = dp; 127: flushcwd: 128: dfree(dcwd); 129: dnewcwd(dp); 130: } 131: 132: /* 133: * dfollow - change to arg directory; fall back on cdpath if not valid 134: */ 135: char * 136: dfollow(cp) 137: register char *cp; 138: { 139: register char *dp; 140: struct varent *c; 141: 142: cp = globone(cp); 143: if (chdir(cp) >= 0) 144: goto gotcha; 145: if (cp[0] != '/' && !prefix("./", cp) && !prefix("../", cp) 146: && (c = adrof("cdpath"))) { 147: char **cdp; 148: register char *p; 149: char buf[MAXPATHLEN]; 150: 151: for (cdp = c->vec; *cdp; cdp++) { 152: for (dp = buf, p = *cdp; *dp++ = *p++;) 153: ; 154: dp[-1] = '/'; 155: for (p = cp; *dp++ = *p++;) 156: ; 157: if (chdir(buf) >= 0) { 158: printd = 1; 159: xfree(cp); 160: cp = savestr(buf); 161: goto gotcha; 162: } 163: } 164: } 165: dp = value(cp); 166: if ((dp[0] == '/' || dp[0] == '.') && chdir(dp) >= 0) { 167: xfree(cp); 168: cp = savestr(dp); 169: printd = 1; 170: goto gotcha; 171: } 172: xfree(cp); /* XXX, use after free */ 173: Perror(cp); 174: 175: gotcha: 176: if (*cp != '/') { 177: register char *p, *q; 178: int cwdlen; 179: 180: /* 181: * All in the name of efficiency? 182: */ 183: for (p = dcwd->di_name; *p++;) 184: ; 185: if ((cwdlen = p - dcwd->di_name - 1) == 1) /* root */ 186: cwdlen = 0; 187: for (p = cp; *p++;) 188: ; 189: dp = xalloc((unsigned) (cwdlen + (p - cp) + 1)); 190: for (p = dp, q = dcwd->di_name; *p++ = *q++;) 191: ; 192: if (cwdlen) 193: p[-1] = '/'; 194: else 195: p--; /* don't add a / after root */ 196: for (q = cp; *p++ = *q++;) 197: ; 198: xfree(cp); 199: cp = dp; 200: dp += cwdlen; 201: } else 202: dp = cp; 203: return dcanon(cp, dp); 204: } 205: 206: /* 207: * dopushd - push new directory onto directory stack. 208: * with no arguments exchange top and second. 209: * with numeric argument (+n) bring it to top. 210: */ 211: dopushd(v) 212: char **v; 213: { 214: register struct directory *dp; 215: 216: printd = 1; 217: if (*++v == NOSTR) { 218: if ((dp = dcwd->di_prev) == &dhead) 219: dp = dhead.di_prev; 220: if (dp == dcwd) 221: bferr("No other directory"); 222: if (chdir(dp->di_name) < 0) 223: Perror(dp->di_name); 224: dp->di_prev->di_next = dp->di_next; 225: dp->di_next->di_prev = dp->di_prev; 226: dp->di_next = dcwd->di_next; 227: dp->di_prev = dcwd; 228: dcwd->di_next->di_prev = dp; 229: dcwd->di_next = dp; 230: } else if (dp = dfind(*v)) { 231: if (chdir(dp->di_name) < 0) 232: Perror(dp->di_name); 233: } else { 234: register char *cp; 235: 236: cp = dfollow(*v); 237: dp = (struct directory *)calloc(sizeof (struct directory), 1); 238: dp->di_name = cp; 239: dp->di_count = 0; 240: dp->di_prev = dcwd; 241: dp->di_next = dcwd->di_next; 242: dcwd->di_next = dp; 243: dp->di_next->di_prev = dp; 244: } 245: dnewcwd(dp); 246: } 247: 248: /* 249: * dfind - find a directory if specified by numeric (+n) argument 250: */ 251: struct directory * 252: dfind(cp) 253: register char *cp; 254: { 255: register struct directory *dp; 256: register int i; 257: register char *ep; 258: 259: if (*cp++ != '+') 260: return (0); 261: for (ep = cp; digit(*ep); ep++) 262: continue; 263: if (*ep) 264: return (0); 265: i = getn(cp); 266: if (i <= 0) 267: return (0); 268: for (dp = dcwd; i != 0; i--) { 269: if ((dp = dp->di_prev) == &dhead) 270: dp = dp->di_prev; 271: if (dp == dcwd) 272: bferr("Directory stack not that deep"); 273: } 274: return (dp); 275: } 276: 277: /* 278: * dopopd - pop a directory out of the directory stack 279: * with a numeric argument just discard it. 280: */ 281: dopopd(v) 282: char **v; 283: { 284: register struct directory *dp, *p; 285: 286: printd = 1; 287: if (*++v == NOSTR) 288: dp = dcwd; 289: else if ((dp = dfind(*v)) == 0) 290: bferr("Bad directory"); 291: if (dp->di_prev == &dhead && dp->di_next == &dhead) 292: bferr("Directory stack empty"); 293: if (dp == dcwd) { 294: if ((p = dp->di_prev) == &dhead) 295: p = dhead.di_prev; 296: if (chdir(p->di_name) < 0) 297: Perror(p->di_name); 298: } 299: dp->di_prev->di_next = dp->di_next; 300: dp->di_next->di_prev = dp->di_prev; 301: if (dp == dcwd) 302: dnewcwd(p); 303: else 304: dodirs(fakev); 305: dfree(dp); 306: } 307: 308: /* 309: * dfree - free the directory (or keep it if it still has ref count) 310: */ 311: dfree(dp) 312: register struct directory *dp; 313: { 314: 315: if (dp->di_count != 0) 316: dp->di_next = dp->di_prev = 0; 317: else 318: xfree(dp->di_name), xfree((char *)dp); 319: } 320: 321: /* 322: * dcanon - canonicalize the pathname, removing excess ./ and ../ etc. 323: * we are of course assuming that the file system is standardly 324: * constructed (always have ..'s, directories have links) 325: */ 326: char * 327: dcanon(cp, p) 328: register char *cp, *p; 329: { 330: register char *sp; 331: register char *p1, *p2; /* general purpose */ 332: bool slash; 333: 334: if (*cp != '/') 335: abort(); 336: while (*p) { /* for each component */ 337: sp = p; /* save slash address */ 338: while (*++p == '/') /* flush extra slashes */ 339: ; 340: if (p != ++sp) 341: for (p1 = sp, p2 = p; *p1++ = *p2++;) 342: ; 343: p = sp; /* save start of component */ 344: slash = 0; 345: while (*++p) /* find next slash or end of path */ 346: if (*p == '/') { 347: slash = 1; 348: *p = 0; 349: break; 350: } 351: if (*sp == '\0') /* if component is null */ 352: if (--sp == cp) /* if path is one char (i.e. /) */ 353: break; 354: else 355: *sp = '\0'; 356: else if (sp[0] == '.' && sp[1] == 0) { 357: if (slash) { 358: for (p1 = sp, p2 = p + 1; *p1++ = *p2++;) 359: ; 360: p = --sp; 361: } else if (--sp != cp) 362: *sp = '\0'; 363: } else if (sp[0] == '.' && sp[1] == '.' && sp[2] == 0) { 364: char link[MAXPATHLEN]; 365: int cc; 366: char *newcp; 367: 368: /* 369: * We have something like "yyy/xxx/..", where "yyy" 370: * can be null or a path starting at /, and "xxx" 371: * is a single component. 372: * Before compressing "xxx/..", we want to expand 373: * "yyy/xxx", if it is a symbolic link. 374: */ 375: *--sp = 0; /* form the pathname for readlink */ 376: if (sp != cp && 377: (cc = readlink(cp, link, sizeof link)) >= 0) { 378: link[cc] = '\0'; 379: if (slash) 380: *p = '/'; 381: /* 382: * Point p to the '/' in "/..", and restore 383: * the '/'. 384: */ 385: *(p = sp) = '/'; 386: /* 387: * find length of p 388: */ 389: for (p1 = p; *p1++;) 390: ; 391: if (*link != '/') { 392: /* 393: * Relative path, expand it between 394: * the "yyy/" and the "/..". 395: * First, back sp up to the character 396: * past "yyy/". 397: */ 398: while (*--sp != '/') 399: ; 400: sp++; 401: *sp = 0; 402: /* 403: * New length is 404: * "yyy/" + link + "/.." and rest 405: */ 406: p1 = newcp = xalloc((unsigned) 407: ((sp - cp) + cc + (p1 - p))); 408: /* 409: * Copy new path into newcp 410: */ 411: for (p2 = cp; *p1++ = *p2++;) 412: ; 413: for (p1--, p2 = link; *p1++ = *p2++;) 414: ; 415: for (p1--, p2 = p; *p1++ = *p2++;) 416: ; 417: /* 418: * Restart canonicalization at 419: * expanded "/xxx". 420: */ 421: p = sp - cp - 1 + newcp; 422: } else { 423: /* 424: * New length is link + "/.." and rest 425: */ 426: p1 = newcp = xalloc((unsigned) 427: (cc + (p1 - p))); 428: /* 429: * Copy new path into newcp 430: */ 431: for (p2 = link; *p1++ = *p2++;) 432: ; 433: for (p1--, p2 = p; *p1++ = *p2++;) 434: ; 435: /* 436: * Restart canonicalization at beginning 437: */ 438: p = newcp; 439: } 440: xfree(cp); 441: cp = newcp; 442: continue; /* canonicalize the link */ 443: } 444: *sp = '/'; 445: if (sp != cp) 446: while (*--sp != '/') 447: ; 448: if (slash) { 449: for (p1 = sp + 1, p2 = p + 1; *p1++ = *p2++;) 450: ; 451: p = sp; 452: } else if (cp == sp) 453: *++sp = '\0'; 454: else 455: *sp = '\0'; 456: } else if (slash) 457: *p = '/'; 458: } 459: return cp; 460: } 461: 462: /* 463: * dnewcwd - make a new directory in the loop the current one 464: */ 465: dnewcwd(dp) 466: register struct directory *dp; 467: { 468: 469: dcwd = dp; 470: set("cwd", savestr(dcwd->di_name)); 471: if (printd) 472: dodirs(fakev); 473: }