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 #ifndef lint 8 static char *sccsid = "@(#)dir.c 5.3 (Berkeley) 06/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 } 474