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