1 0 stevel /* 2 0 stevel * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 3 0 stevel * Use is subject to license terms. 4 0 stevel */ 5 0 stevel 6 0 stevel /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 7 0 stevel /* All Rights Reserved */ 8 0 stevel 9 0 stevel /* 10 0 stevel * Copyright (c) 1980 Regents of the University of California. 11 0 stevel * All rights reserved. The Berkeley Software License Agreement 12 0 stevel * specifies the terms and conditions for redistribution. 13 0 stevel */ 14 0 stevel 15 0 stevel #pragma ident "%Z%%M% %I% %E% SMI" 16 0 stevel 17 0 stevel #include "sh.h" 18 0 stevel #include "sh.dir.h" 19 0 stevel #include "sh.tconst.h" 20 0 stevel 21 0 stevel /* 22 0 stevel * C Shell - directory management 23 0 stevel */ 24 0 stevel 25 356 muffin struct directory *dfind(tchar *); 26 356 muffin tchar *dfollow(tchar *); 27 356 muffin tchar *dcanon(tchar *, tchar *); 28 356 muffin void dtildepr(tchar *, tchar *); 29 356 muffin void dfree(struct directory *); 30 356 muffin void dnewcwd(struct directory *); 31 356 muffin 32 0 stevel struct directory dhead; /* "head" of loop */ 33 0 stevel int printd; /* force name to be printed */ 34 0 stevel static tchar *fakev[] = { S_dirs, NOSTR }; 35 0 stevel 36 0 stevel /* 37 0 stevel * dinit - initialize current working directory 38 0 stevel */ 39 356 muffin void 40 356 muffin dinit(tchar *hp) 41 0 stevel { 42 356 muffin tchar *cp; 43 356 muffin struct directory *dp; 44 0 stevel tchar path[MAXPATHLEN]; 45 0 stevel 46 0 stevel #ifdef TRACE 47 0 stevel tprintf("TRACE- dinit()\n"); 48 0 stevel #endif 49 0 stevel /* 50 0 stevel * If this is a login shell, we should have a home directory. But, 51 0 stevel * if we got here via 'su - <user>' where the user has no directory 52 0 stevel * in his passwd file, then su has passed HOME=<nothing>, so hp is 53 0 stevel * non-null, but has zero length. Thus, we do not know the current 54 0 stevel * working directory based on the home directory. 55 0 stevel */ 56 0 stevel if (loginsh && hp && *hp) 57 0 stevel cp = hp; 58 0 stevel else { 59 0 stevel cp = getwd_(path); 60 0 stevel if (cp == NULL) { 61 559 nakanon printf("Warning: cannot determine current directory\n"); 62 0 stevel cp = S_DOT; 63 0 stevel } 64 0 stevel } 65 559 nakanon dp = (struct directory *)xcalloc(sizeof (struct directory), 1); 66 0 stevel dp->di_name = savestr(cp); 67 0 stevel dp->di_count = 0; 68 0 stevel dhead.di_next = dhead.di_prev = dp; 69 0 stevel dp->di_next = dp->di_prev = &dhead; 70 0 stevel printd = 0; 71 0 stevel dnewcwd(dp); 72 0 stevel } 73 0 stevel 74 0 stevel /* 75 0 stevel * dodirs - list all directories in directory loop 76 0 stevel */ 77 356 muffin void 78 356 muffin dodirs(tchar **v) 79 0 stevel { 80 356 muffin struct directory *dp; 81 0 stevel bool lflag; 82 0 stevel tchar *hp = value(S_home); 83 0 stevel 84 0 stevel #ifdef TRACE 85 0 stevel tprintf("TRACE- dodirs()\n"); 86 0 stevel #endif 87 0 stevel if (*hp == '\0') 88 0 stevel hp = NOSTR; 89 0 stevel if (*++v != NOSTR) 90 0 stevel if (eq(*v, S_MINl /* "-l" */) && *++v == NOSTR) 91 0 stevel lflag = 1; 92 0 stevel else 93 0 stevel error("Usage: dirs [ -l ]"); 94 0 stevel else 95 0 stevel lflag = 0; 96 0 stevel dp = dcwd; 97 0 stevel do { 98 0 stevel if (dp == &dhead) 99 0 stevel continue; 100 0 stevel if (!lflag && hp != NOSTR) { 101 0 stevel dtildepr(hp, dp->di_name); 102 0 stevel } else 103 0 stevel printf("%t", dp->di_name); 104 0 stevel printf(" "); 105 0 stevel } while ((dp = dp->di_prev) != dcwd); 106 0 stevel printf("\n"); 107 0 stevel } 108 0 stevel 109 356 muffin void 110 356 muffin dtildepr(tchar *home, tchar *dir) 111 0 stevel { 112 0 stevel 113 0 stevel #ifdef TRACE 114 0 stevel tprintf("TRACE- dtildepr()\n"); 115 0 stevel #endif 116 0 stevel if (!eq(home, S_SLASH /* "/" */) && prefix(home, dir)) 117 0 stevel printf("~%t", dir + strlen_(home)); 118 0 stevel else 119 0 stevel printf("%t", dir); 120 0 stevel } 121 0 stevel 122 0 stevel /* 123 0 stevel * dochngd - implement chdir command. 124 0 stevel */ 125 356 muffin void 126 356 muffin dochngd(tchar **v) 127 0 stevel { 128 356 muffin tchar *cp; 129 356 muffin struct directory *dp; 130 0 stevel 131 0 stevel #ifdef TRACE 132 0 stevel tprintf("TRACE- dochngd()\n"); 133 0 stevel #endif 134 0 stevel printd = 0; 135 0 stevel if (*++v == NOSTR) { 136 0 stevel if ((cp = value(S_home)) == NOSTR || *cp == 0) 137 0 stevel bferr("No home directory"); 138 0 stevel if (chdir_(cp) < 0) 139 0 stevel bferr("Can't change to home directory"); 140 0 stevel cp = savestr(cp); 141 0 stevel } else if ((dp = dfind(*v)) != 0) { 142 0 stevel printd = 1; 143 0 stevel if (chdir_(dp->di_name) < 0) 144 0 stevel Perror(dp->di_name); 145 0 stevel dcwd->di_prev->di_next = dcwd->di_next; 146 0 stevel dcwd->di_next->di_prev = dcwd->di_prev; 147 0 stevel goto flushcwd; 148 0 stevel } else 149 0 stevel cp = dfollow(*v); 150 559 nakanon dp = (struct directory *)xcalloc(sizeof (struct directory), 1); 151 0 stevel dp->di_name = cp; 152 0 stevel dp->di_count = 0; 153 0 stevel dp->di_next = dcwd->di_next; 154 0 stevel dp->di_prev = dcwd->di_prev; 155 0 stevel dp->di_prev->di_next = dp; 156 0 stevel dp->di_next->di_prev = dp; 157 0 stevel flushcwd: 158 0 stevel dfree(dcwd); 159 0 stevel dnewcwd(dp); 160 0 stevel } 161 0 stevel 162 0 stevel /* 163 0 stevel * dfollow - change to arg directory; fall back on cdpath if not valid 164 0 stevel */ 165 0 stevel tchar * 166 356 muffin dfollow(tchar *cp) 167 0 stevel { 168 356 muffin tchar *dp; 169 0 stevel struct varent *c; 170 0 stevel int cdhashval, cdhashval1; 171 0 stevel int index; 172 559 nakanon int slash; /* slashes in the argument */ 173 0 stevel tchar *fullpath; 174 559 nakanon tchar *slashcp; /* cp string prepended with a slash */ 175 0 stevel 176 0 stevel #ifdef TRACE 177 0 stevel tprintf("TRACE- dfollow()\n"); 178 0 stevel #endif 179 0 stevel cp = globone(cp); 180 0 stevel if (chdir_(cp) >= 0) 181 0 stevel goto gotcha; 182 0 stevel 183 0 stevel /* 184 0 stevel * If the directory argument has a slash in it, 185 0 stevel * for example, directory/directory, then can't 186 559 nakanon * find that in the cache table. 187 0 stevel */ 188 0 stevel slash = any('/', cp); 189 0 stevel 190 0 stevel /* 191 0 stevel * Try interpreting wrt successive components of cdpath. 192 0 stevel * cdpath caching is turned off or directory argument 193 0 stevel * has a slash in it. 194 0 stevel */ 195 559 nakanon if (cp[0] != '/' 196 559 nakanon && !prefix(S_DOTSLA /* "./" */, cp) 197 0 stevel && !prefix(S_DOTDOTSLA /* "../" */, cp) 198 0 stevel && (c = adrof(S_cdpath)) 199 559 nakanon && (!havhash2 || slash)) { 200 356 muffin tchar **cdp; 201 356 muffin tchar *p; 202 356 muffin tchar buf[MAXPATHLEN]; 203 0 stevel 204 0 stevel for (cdp = c->vec; *cdp; cdp++) { 205 559 nakanon for (dp = buf, p = *cdp; *dp++ = *p++; ) 206 0 stevel ; 207 0 stevel dp[-1] = '/'; 208 559 nakanon for (p = cp; *dp++ = *p++; ) 209 0 stevel ; 210 0 stevel if (chdir_(buf) >= 0) { 211 0 stevel printd = 1; 212 0 stevel xfree(cp); 213 0 stevel cp = savestr(buf); 214 0 stevel goto gotcha; 215 0 stevel } 216 0 stevel } 217 0 stevel } 218 559 nakanon 219 0 stevel /* cdpath caching turned on */ 220 559 nakanon if (cp[0] != '/' 221 559 nakanon && !prefix(S_DOTSLA /* "./" */, cp) 222 0 stevel && !prefix(S_DOTDOTSLA /* "../" */, cp) 223 0 stevel && (c = adrof(S_cdpath)) 224 559 nakanon && havhash2 && !slash) { 225 0 stevel tchar **pv; 226 0 stevel 227 0 stevel /* If no cdpath or no paths in cdpath, leave */ 228 559 nakanon if (c == 0 || c->vec[0] == 0) 229 0 stevel pv = justabs; 230 0 stevel else 231 0 stevel pv = c->vec; 232 0 stevel 233 0 stevel slashcp = strspl(S_SLASH, cp); 234 0 stevel 235 0 stevel cdhashval = hashname(cp); 236 0 stevel 237 559 nakanon /* index points to next path component to test */ 238 559 nakanon index = 0; 239 0 stevel 240 0 stevel /* 241 0 stevel * Look at each path in cdpath until get a match. 242 0 stevel * Only look at those path beginning with a slash 243 0 stevel */ 244 0 stevel do { 245 0 stevel /* only check cache for absolute pathnames */ 246 559 nakanon if (pv[0][0] == '/') { 247 0 stevel cdhashval1 = hash(cdhashval, index); 248 0 stevel if (bit(xhash2, cdhashval1)) { 249 0 stevel /* 250 0 stevel * concatenate found path with 251 0 stevel * arg directory 252 0 stevel */ 253 0 stevel fullpath = strspl(*pv, slashcp); 254 0 stevel if (chdir_(fullpath) >= 0) { 255 0 stevel printd = 1; 256 0 stevel xfree(cp); 257 0 stevel cp = savestr(fullpath); 258 0 stevel xfree(slashcp); 259 0 stevel xfree(fullpath); 260 0 stevel goto gotcha; 261 0 stevel } 262 0 stevel } 263 0 stevel } 264 0 stevel /* 265 0 stevel * relative pathnames are not cached, and must be 266 0 stevel * checked manually 267 0 stevel */ 268 0 stevel else { 269 356 muffin tchar *p; 270 0 stevel tchar buf[MAXPATHLEN]; 271 0 stevel 272 0 stevel for (dp = buf, p = *pv; *dp++ = *p++; ) 273 0 stevel ; 274 0 stevel dp[-1] = '/'; 275 0 stevel for (p = cp; *dp++ = *p++; ) 276 0 stevel ; 277 0 stevel if (chdir_(buf) >= 0) { 278 0 stevel printd = 1; 279 0 stevel xfree(cp); 280 0 stevel cp = savestr(buf); 281 0 stevel xfree(slashcp); 282 0 stevel goto gotcha; 283 0 stevel } 284 0 stevel } 285 0 stevel pv++; 286 0 stevel index++; 287 0 stevel } while (*pv); 288 0 stevel } 289 559 nakanon 290 0 stevel /* 291 0 stevel * Try dereferencing the variable named by the argument. 292 0 stevel */ 293 0 stevel dp = value(cp); 294 0 stevel if ((dp[0] == '/' || dp[0] == '.') && chdir_(dp) >= 0) { 295 0 stevel xfree(cp); 296 0 stevel cp = savestr(dp); 297 0 stevel printd = 1; 298 0 stevel goto gotcha; 299 0 stevel } 300 0 stevel xfree(cp); /* XXX, use after free */ 301 0 stevel Perror(cp); 302 0 stevel 303 0 stevel gotcha: 304 0 stevel if (*cp != '/') { 305 356 muffin tchar *p, *q; 306 0 stevel int cwdlen; 307 0 stevel int len; 308 0 stevel 309 0 stevel /* 310 0 stevel * All in the name of efficiency? 311 0 stevel */ 312 0 stevel 313 559 nakanon if ((cwdlen = (strlen_(dcwd->di_name))) == 1) { 314 559 nakanon if (*dcwd->di_name == '/') /* root */ 315 0 stevel cwdlen = 0; 316 0 stevel else 317 0 stevel { 318 0 stevel /* 319 559 nakanon * if we are here, when the shell started 320 0 stevel * it was unable to getwd(), lets try it again 321 0 stevel */ 322 0 stevel tchar path[MAXPATHLEN]; 323 0 stevel 324 0 stevel p = getwd_(path); 325 0 stevel if (p == NULL) 326 0 stevel error("cannot determine current directory"); 327 0 stevel else 328 0 stevel { 329 0 stevel xfree(dcwd->di_name); 330 0 stevel dcwd->di_name = savestr(p); 331 0 stevel xfree(cp); 332 0 stevel cp = savestr(p); 333 0 stevel return dcanon(cp, cp); 334 0 stevel } 335 0 stevel 336 0 stevel } 337 0 stevel } 338 0 stevel /* 339 0 stevel * 340 0 stevel * for (p = cp; *p++;) 341 0 stevel * ; 342 0 stevel * dp = (tchar *)xalloc((unsigned) (cwdlen + (p - cp) + 1)*sizeof (tchar)) 343 0 stevel */ 344 0 stevel len = strlen_(cp); 345 559 nakanon dp = (tchar *)xalloc((unsigned)(cwdlen + len + 2) * sizeof (tchar)); 346 559 nakanon for (p = dp, q = dcwd->di_name; *p++ = *q++; ) 347 0 stevel ; 348 0 stevel if (cwdlen) 349 0 stevel p[-1] = '/'; 350 0 stevel else 351 0 stevel p--; /* don't add a / after root */ 352 559 nakanon for (q = cp; *p++ = *q++; ) 353 0 stevel ; 354 0 stevel xfree(cp); 355 0 stevel cp = dp; 356 0 stevel dp += cwdlen; 357 0 stevel } else 358 0 stevel dp = cp; 359 0 stevel return dcanon(cp, dp); 360 0 stevel } 361 0 stevel 362 0 stevel /* 363 0 stevel * dopushd - push new directory onto directory stack. 364 0 stevel * with no arguments exchange top and second. 365 0 stevel * with numeric argument (+n) bring it to top. 366 0 stevel */ 367 356 muffin void 368 356 muffin dopushd(tchar **v) 369 0 stevel { 370 356 muffin struct directory *dp; 371 0 stevel 372 0 stevel #ifdef TRACE 373 0 stevel tprintf("TRACE- dopushd()\n"); 374 0 stevel #endif 375 0 stevel printd = 1; 376 0 stevel if (*++v == NOSTR) { 377 0 stevel if ((dp = dcwd->di_prev) == &dhead) 378 0 stevel dp = dhead.di_prev; 379 0 stevel if (dp == dcwd) 380 0 stevel bferr("No other directory"); 381 0 stevel if (chdir_(dp->di_name) < 0) 382 0 stevel Perror(dp->di_name); 383 0 stevel dp->di_prev->di_next = dp->di_next; 384 0 stevel dp->di_next->di_prev = dp->di_prev; 385 0 stevel dp->di_next = dcwd->di_next; 386 0 stevel dp->di_prev = dcwd; 387 0 stevel dcwd->di_next->di_prev = dp; 388 0 stevel dcwd->di_next = dp; 389 0 stevel } else if (dp = dfind(*v)) { 390 0 stevel if (chdir_(dp->di_name) < 0) 391 0 stevel Perror(dp->di_name); 392 0 stevel } else { 393 356 muffin tchar *cp; 394 0 stevel 395 0 stevel cp = dfollow(*v); 396 559 nakanon dp = (struct directory *)xcalloc(sizeof (struct directory), 1); 397 0 stevel dp->di_name = cp; 398 0 stevel dp->di_count = 0; 399 0 stevel dp->di_prev = dcwd; 400 0 stevel dp->di_next = dcwd->di_next; 401 0 stevel dcwd->di_next = dp; 402 0 stevel dp->di_next->di_prev = dp; 403 0 stevel } 404 0 stevel dnewcwd(dp); 405 0 stevel } 406 0 stevel 407 0 stevel /* 408 0 stevel * dfind - find a directory if specified by numeric (+n) argument 409 0 stevel */ 410 0 stevel struct directory * 411 356 muffin dfind(tchar *cp) 412 0 stevel { 413 356 muffin struct directory *dp; 414 356 muffin int i; 415 356 muffin tchar *ep; 416 0 stevel 417 0 stevel #ifdef TRACE 418 0 stevel tprintf("TRACE- dfind()\n"); 419 0 stevel #endif 420 0 stevel if (*cp++ != '+') 421 0 stevel return (0); 422 0 stevel for (ep = cp; digit(*ep); ep++) 423 0 stevel continue; 424 0 stevel if (*ep) 425 0 stevel return (0); 426 0 stevel i = getn(cp); 427 0 stevel if (i <= 0) 428 0 stevel return (0); 429 0 stevel for (dp = dcwd; i != 0; i--) { 430 0 stevel if ((dp = dp->di_prev) == &dhead) 431 0 stevel dp = dp->di_prev; 432 0 stevel if (dp == dcwd) 433 0 stevel bferr("Directory stack not that deep"); 434 0 stevel } 435 0 stevel return (dp); 436 0 stevel } 437 0 stevel 438 0 stevel /* 439 0 stevel * dopopd - pop a directory out of the directory stack 440 0 stevel * with a numeric argument just discard it. 441 0 stevel */ 442 356 muffin void 443 356 muffin dopopd(tchar **v) 444 0 stevel { 445 356 muffin struct directory *dp, *p; 446 0 stevel 447 0 stevel #ifdef TRACE 448 0 stevel tprintf("TRACE- dopopd()\n"); 449 0 stevel #endif 450 0 stevel printd = 1; 451 0 stevel if (*++v == NOSTR) 452 0 stevel dp = dcwd; 453 0 stevel else if ((dp = dfind(*v)) == 0) 454 0 stevel bferr("Invalid argument"); 455 0 stevel if (dp->di_prev == &dhead && dp->di_next == &dhead) 456 0 stevel bferr("Directory stack empty"); 457 0 stevel if (dp == dcwd) { 458 0 stevel if ((p = dp->di_prev) == &dhead) 459 0 stevel p = dhead.di_prev; 460 0 stevel if (chdir_(p->di_name) < 0) 461 0 stevel Perror(p->di_name); 462 0 stevel } 463 0 stevel dp->di_prev->di_next = dp->di_next; 464 0 stevel dp->di_next->di_prev = dp->di_prev; 465 0 stevel if (dp == dcwd) 466 0 stevel dnewcwd(p); 467 0 stevel else 468 0 stevel dodirs(fakev); 469 0 stevel dfree(dp); 470 0 stevel } 471 0 stevel 472 0 stevel /* 473 0 stevel * dfree - free the directory (or keep it if it still has ref count) 474 0 stevel */ 475 356 muffin void 476 356 muffin dfree(struct directory *dp) 477 0 stevel { 478 0 stevel 479 0 stevel #ifdef TRACE 480 0 stevel tprintf("TRACE- dfree()\n"); 481 0 stevel #endif 482 0 stevel if (dp->di_count != 0) 483 0 stevel dp->di_next = dp->di_prev = 0; 484 0 stevel else 485 559 nakanon xfree(dp->di_name), xfree((tchar *)dp); 486 0 stevel } 487 0 stevel 488 0 stevel /* 489 0 stevel * dcanon - canonicalize the pathname, removing excess ./ and ../ etc. 490 0 stevel * We are of course assuming that the file system is standardly 491 0 stevel * constructed (always have ..'s, directories have links). 492 0 stevel * 493 0 stevel * If the hardpaths shell variable is set, resolve the 494 0 stevel * resulting pathname to contain no symbolic link components. 495 0 stevel */ 496 0 stevel tchar * 497 356 muffin dcanon(tchar *cp, tchar *p) 498 0 stevel { 499 356 muffin tchar *sp; /* rightmost component currently under 500 559 nakanon consideration */ 501 356 muffin tchar *p1, /* general purpose */ 502 356 muffin *p2; 503 0 stevel bool slash, dotdot, hardpaths; 504 0 stevel 505 0 stevel #ifdef TRACE 506 0 stevel tprintf("TRACE- dcannon()\n"); 507 0 stevel #endif 508 0 stevel 509 0 stevel if (*cp != '/') 510 0 stevel abort(); 511 0 stevel 512 0 stevel if (hardpaths = (adrof(S_hardpaths) != NULL)) { 513 0 stevel /* 514 0 stevel * Be paranoid: don't trust the initial prefix 515 0 stevel * to be symlink-free. 516 0 stevel */ 517 0 stevel p = cp; 518 0 stevel } 519 0 stevel 520 0 stevel /* 521 0 stevel * Loop invariant: cp points to the overall path start, 522 0 stevel * p to its as yet uncanonicalized trailing suffix. 523 0 stevel */ 524 0 stevel while (*p) { /* for each component */ 525 0 stevel sp = p; /* save slash address */ 526 0 stevel 527 0 stevel while (*++p == '/') /* flush extra slashes */ 528 0 stevel ; 529 0 stevel if (p != ++sp) 530 559 nakanon for (p1 = sp, p2 = p; *p1++ = *p2++; ) 531 0 stevel ; 532 0 stevel 533 0 stevel p = sp; /* save start of component */ 534 0 stevel slash = 0; 535 0 stevel if (*p) 536 0 stevel while (*++p) /* find next slash or end of path */ 537 0 stevel if (*p == '/') { 538 0 stevel slash = 1; 539 0 stevel *p = '\0'; 540 0 stevel break; 541 0 stevel } 542 0 stevel 543 0 stevel if (*sp == '\0') { 544 0 stevel /* component is null */ 545 0 stevel if (--sp == cp) /* if path is one tchar (i.e. /) */ 546 0 stevel break; 547 0 stevel else 548 0 stevel *sp = '\0'; 549 0 stevel continue; 550 0 stevel } 551 0 stevel 552 0 stevel if (sp[0] == '.' && sp[1] == '\0') { 553 0 stevel /* Squeeze out component consisting of "." */ 554 0 stevel if (slash) { 555 559 nakanon for (p1 = sp, p2 = p + 1; *p1++ = *p2++; ) 556 0 stevel ; 557 0 stevel p = --sp; 558 0 stevel } else if (--sp != cp) 559 0 stevel *sp = '\0'; 560 0 stevel continue; 561 0 stevel } 562 0 stevel 563 0 stevel /* 564 0 stevel * At this point we have a path of the form "x/yz", 565 0 stevel * where "x" is null or rooted at "/", "y" is a single 566 0 stevel * component, and "z" is possibly null. The pointer cp 567 0 stevel * points to the start of "x", sp to the start of "y", 568 0 stevel * and p to the beginning of "z", which has been forced 569 0 stevel * to a null. 570 0 stevel */ 571 0 stevel /* 572 0 stevel * Process symbolic link component. Provided that either 573 0 stevel * the hardpaths shell variable is set or "y" is really 574 0 stevel * ".." we replace the symlink with its contents. The 575 0 stevel * second condition for replacement is necessary to make 576 0 stevel * the command "cd x/.." produce the same results as the 577 0 stevel * sequence "cd x; cd ..". 578 0 stevel * 579 0 stevel * Note that the two conditions correspond to different 580 0 stevel * potential symlinks. When hardpaths is set, we must 581 0 stevel * check "x/y"; otherwise, when "y" is known to be "..", 582 0 stevel * we check "x". 583 0 stevel */ 584 0 stevel dotdot = sp[0] == '.' && sp[1] == '.' && sp[2] == '\0'; 585 0 stevel if (hardpaths || dotdot) { 586 0 stevel tchar link[MAXPATHLEN]; 587 0 stevel int cc; 588 0 stevel tchar *newcp; 589 0 stevel 590 0 stevel /* 591 0 stevel * Isolate the end of the component that is to 592 0 stevel * be checked for symlink-hood. 593 0 stevel */ 594 0 stevel sp--; 595 0 stevel if (! hardpaths) 596 0 stevel *sp = '\0'; 597 0 stevel 598 0 stevel /* 599 0 stevel * See whether the component is really a symlink by 600 0 stevel * trying to read it. If the read succeeds, it is. 601 0 stevel */ 602 0 stevel if ((hardpaths || sp > cp) && 603 0 stevel (cc = readlink_(cp, link, MAXPATHLEN)) >= 0) { 604 0 stevel /* 605 0 stevel * readlink_ put null, so we don't need this. 606 0 stevel */ 607 0 stevel /* link[cc] = '\0'; */ 608 0 stevel 609 0 stevel /* Restore path. */ 610 0 stevel if (slash) 611 0 stevel *p = '/'; 612 0 stevel 613 0 stevel /* 614 0 stevel * Point p at the start of the trailing 615 0 stevel * path following the symlink component. 616 0 stevel * It's already there is hardpaths is set. 617 0 stevel */ 618 0 stevel if (! hardpaths) { 619 0 stevel /* Restore path as well. */ 620 0 stevel *(p = sp) = '/'; 621 0 stevel } 622 0 stevel 623 0 stevel /* 624 0 stevel * Find length of p. 625 0 stevel */ 626 559 nakanon for (p1 = p; *p1++; ) 627 0 stevel ; 628 0 stevel 629 0 stevel if (*link != '/') { 630 0 stevel /* 631 0 stevel * Relative path: replace the symlink 632 0 stevel * component with its value. First, 633 0 stevel * set sp to point to the slash at 634 0 stevel * its beginning. If hardpaths is 635 0 stevel * set, this is already the case. 636 0 stevel */ 637 0 stevel if (! hardpaths) { 638 0 stevel while (*--sp != '/') 639 0 stevel ; 640 0 stevel } 641 0 stevel 642 0 stevel /* 643 0 stevel * Terminate the leading part of the 644 0 stevel * path, including trailing slash. 645 0 stevel */ 646 0 stevel sp++; 647 0 stevel *sp = '\0'; 648 0 stevel 649 0 stevel /* 650 0 stevel * New length is: "x/" + link + "z" 651 0 stevel */ 652 0 stevel p1 = newcp = (tchar *)xalloc((unsigned) 653 559 nakanon ((sp - cp) + cc + (p1 - p)) * sizeof (tchar)); 654 0 stevel /* 655 0 stevel * Copy new path into newcp 656 0 stevel */ 657 559 nakanon for (p2 = cp; *p1++ = *p2++; ) 658 0 stevel ; 659 559 nakanon for (p1--, p2 = link; *p1++ = *p2++; ) 660 0 stevel ; 661 559 nakanon for (p1--, p2 = p; *p1++ = *p2++; ) 662 0 stevel ; 663 0 stevel /* 664 0 stevel * Restart canonicalization at 665 0 stevel * expanded "/y". 666 0 stevel */ 667 0 stevel p = sp - cp - 1 + newcp; 668 0 stevel } else { 669 0 stevel /* 670 0 stevel * New length is: link + "z" 671 0 stevel */ 672 0 stevel p1 = newcp = (tchar *)xalloc((unsigned) 673 0 stevel (cc + (p1 - p))*sizeof (tchar)); 674 0 stevel /* 675 0 stevel * Copy new path into newcp 676 0 stevel */ 677 559 nakanon for (p2 = link; *p1++ = *p2++; ) 678 0 stevel ; 679 559 nakanon for (p1--, p2 = p; *p1++ = *p2++; ) 680 0 stevel ; 681 0 stevel /* 682 0 stevel * Restart canonicalization at beginning 683 0 stevel */ 684 0 stevel p = newcp; 685 0 stevel } 686 0 stevel xfree(cp); 687 0 stevel cp = newcp; 688 0 stevel continue; /* canonicalize the link */ 689 0 stevel } 690 0 stevel 691 0 stevel /* The component wasn't a symlink after all. */ 692 0 stevel if (! hardpaths) 693 0 stevel *sp = '/'; 694 0 stevel } 695 0 stevel 696 0 stevel if (dotdot) { 697 0 stevel if (sp != cp) 698 0 stevel while (*--sp != '/') 699 0 stevel ; 700 0 stevel if (slash) { 701 559 nakanon for (p1 = sp + 1, p2 = p + 1; *p1++ = *p2++; ) 702 0 stevel ; 703 0 stevel p = sp; 704 0 stevel } else if (cp == sp) 705 0 stevel *++sp = '\0'; 706 0 stevel else 707 0 stevel *sp = '\0'; 708 0 stevel continue; 709 0 stevel } 710 0 stevel 711 0 stevel if (slash) 712 0 stevel *p = '/'; 713 0 stevel } 714 0 stevel return cp; 715 0 stevel } 716 559 nakanon 717 0 stevel /* 718 0 stevel * dnewcwd - make a new directory in the loop the current one 719 0 stevel * and export its name to the PWD environment variable. 720 0 stevel */ 721 356 muffin void 722 356 muffin dnewcwd(struct directory *dp) 723 0 stevel { 724 0 stevel 725 0 stevel #ifdef TRACE 726 0 stevel tprintf("TRACE- dnewcwd()\n"); 727 0 stevel #endif 728 0 stevel dcwd = dp; 729 0 stevel #ifdef notdef 730 0 stevel /* 731 0 stevel * If we have a fast version of getwd available 732 0 stevel * and hardpaths is set, it would be reasonable 733 0 stevel * here to verify that dcwd->di_name really does 734 0 stevel * name the current directory. Later... 735 0 stevel */ 736 356 muffin #endif /* notdef */ 737 0 stevel 738 559 nakanon didchdir = 1; 739 0 stevel set(S_cwd, savestr(dcwd->di_name)); 740 559 nakanon didchdir = 0; 741 0 stevel local_setenv(S_PWD, dcwd->di_name); 742 0 stevel if (printd) 743 0 stevel dodirs(fakev); 744 0 stevel } 745