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