dir.c revision 1.29
1/* $NetBSD: dir.c,v 1.29 2007/07/16 18:26:09 christos Exp $ */ 2 3/*- 4 * Copyright (c) 1980, 1991, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32#include <sys/cdefs.h> 33#ifndef lint 34#if 0 35static char sccsid[] = "@(#)dir.c 8.1 (Berkeley) 5/31/93"; 36#else 37__RCSID("$NetBSD: dir.c,v 1.29 2007/07/16 18:26:09 christos Exp $"); 38#endif 39#endif /* not lint */ 40 41#include <sys/param.h> 42#include <sys/stat.h> 43 44#include <errno.h> 45#include <stdarg.h> 46#include <stdlib.h> 47#include <string.h> 48#include <unistd.h> 49 50#include "csh.h" 51#include "dir.h" 52#include "extern.h" 53 54/* Directory management. */ 55 56static struct directory *dfind(Char *); 57static Char *dfollow(Char *); 58static void printdirs(void); 59static Char *dgoto(Char *); 60static void skipargs(Char ***, const char *); 61static void dnewcwd(struct directory *); 62static void dset(Char *); 63 64struct directory dhead; /* "head" of loop */ 65int printd; /* force name to be printed */ 66 67static int dirflag = 0; 68 69/* 70 * dinit - initialize current working directory 71 */ 72void 73dinit(Char *hp) 74{ 75 static const char emsg[] = "csh: Trying to start from \"%s\"\n"; 76 char path[MAXPATHLEN]; 77 struct directory *dp; 78 const char *ecp; 79 Char *cp; 80 81 /* Don't believe the login shell home, because it may be a symlink */ 82 ecp = getcwd(path, MAXPATHLEN); 83 if (ecp == NULL || *ecp == '\0') { 84 (void)fprintf(csherr, "csh: %s\n", strerror(errno)); 85 if (hp && *hp) { 86 ecp = short2str(hp); 87 if (chdir(ecp) == -1) 88 cp = NULL; 89 else 90 cp = Strsave(hp); 91 (void)fprintf(csherr, emsg, vis_str(hp)); 92 } 93 else 94 cp = NULL; 95 if (cp == NULL) { 96 (void)fprintf(csherr, emsg, "/"); 97 if (chdir("/") == -1) { 98 /* I am not even try to print an error message! */ 99 xexit(1); 100 } 101 cp = SAVE("/"); 102 } 103 } 104 else { 105 struct stat swd, shp; 106 107 /* 108 * See if $HOME is the working directory we got and use that 109 */ 110 if (hp && *hp && 111 stat(ecp, &swd) != -1 && stat(short2str(hp), &shp) != -1 && 112 swd.st_dev == shp.st_dev && swd.st_ino == shp.st_ino) 113 cp = Strsave(hp); 114 else { 115 const char *cwd; 116 117 /* 118 * use PWD if we have it (for subshells) 119 */ 120 if ((cwd = getenv("PWD")) != NULL) { 121 if (stat(cwd, &shp) != -1 && swd.st_dev == shp.st_dev && 122 swd.st_ino == shp.st_ino) 123 ecp = cwd; 124 } 125 cp = dcanon(SAVE(ecp), STRNULL); 126 } 127 } 128 129 dp = (struct directory *)xcalloc(1, sizeof(struct directory)); 130 dp->di_name = cp; 131 dp->di_count = 0; 132 dhead.di_next = dhead.di_prev = dp; 133 dp->di_next = dp->di_prev = &dhead; 134 printd = 0; 135 dnewcwd(dp); 136} 137 138static void 139dset(Char *dp) 140{ 141 Char **vec; 142 143 /* 144 * Don't call set() directly cause if the directory contains ` or 145 * other junk characters glob will fail. 146 */ 147 148 vec = (Char **)xmalloc((size_t)(2 * sizeof(Char **))); 149 vec[0] = Strsave(dp); 150 vec[1] = 0; 151 setq(STRcwd, vec, &shvhed); 152 Setenv(STRPWD, dp); 153} 154 155#define DIR_LONG 1 156#define DIR_VERT 2 157#define DIR_LINE 4 158 159static void 160skipargs(Char ***v, const char *str) 161{ 162 Char **n, *s; 163 164 n = *v; 165 dirflag = 0; 166 for (n++; *n != NULL && (*n)[0] == '-'; n++) 167 for (s = &((*n)[1]); *s; s++) 168 switch (*s) { 169 case 'l': 170 dirflag |= DIR_LONG; 171 break; 172 case 'n': 173 dirflag |= DIR_LINE; 174 break; 175 case 'v': 176 dirflag |= DIR_VERT; 177 break; 178 default: 179 stderror(ERR_DIRUS, vis_str(**v), str); 180 /* NOTREACHED */ 181 } 182 *v = n; 183} 184 185/* 186 * dodirs - list all directories in directory loop 187 */ 188void 189/*ARGSUSED*/ 190dodirs(Char **v, struct command *t) 191{ 192 skipargs(&v, ""); 193 194 if (*v != NULL) 195 stderror(ERR_DIRUS, "dirs", ""); 196 printdirs(); 197} 198 199static void 200printdirs(void) 201{ 202 struct directory *dp; 203 Char *hp, *s; 204 int cur, idx, len; 205 206 hp = value(STRhome); 207 if (*hp == '\0') 208 hp = NULL; 209 dp = dcwd; 210 idx = 0; 211 cur = 0; 212 do { 213 if (dp == &dhead) 214 continue; 215 if (dirflag & DIR_VERT) { 216 (void)fprintf(cshout, "%d\t", idx++); 217 cur = 0; 218 } 219 if (!(dirflag & DIR_LONG) && hp != NULL && !eq(hp, STRslash) && 220 (len = Strlen(hp), Strncmp(hp, dp->di_name, len) == 0) && 221 (dp->di_name[len] == '\0' || dp->di_name[len] == '/')) 222 len = Strlen(s = (dp->di_name + len)) + 2; 223 else 224 len = Strlen(s = dp->di_name) + 1; 225 226 cur += len; 227 if ((dirflag & DIR_LINE) && cur >= 80 - 1 && len < 80) { 228 (void)fprintf(cshout, "\n"); 229 cur = len; 230 } 231 (void) fprintf(cshout, "%s%s%c", (s != dp->di_name)? "~" : "", 232 vis_str(s), (dirflag & DIR_VERT) ? '\n' : ' '); 233 } while ((dp = dp->di_prev) != dcwd); 234 if (!(dirflag & DIR_VERT)) 235 (void)fprintf(cshout, "\n"); 236} 237 238void 239dtildepr(Char *home, Char *dir) 240{ 241 if (!eq(home, STRslash) && prefix(home, dir)) 242 (void)fprintf(cshout, "~%s", vis_str(dir + Strlen(home))); 243 else 244 (void)fprintf(cshout, "%s", vis_str(dir)); 245} 246 247void 248dtilde(void) 249{ 250 struct directory *d; 251 252 d = dcwd; 253 do { 254 if (d == &dhead) 255 continue; 256 d->di_name = dcanon(d->di_name, STRNULL); 257 } while ((d = d->di_prev) != dcwd); 258 259 dset(dcwd->di_name); 260} 261 262 263/* dnormalize(): 264 * If the name starts with . or .. then we might need to normalize 265 * it depending on the symbolic link flags 266 */ 267Char * 268dnormalize(Char *cp) 269{ 270#define UC (unsigned char) 271#define ISDOT(c) (UC(c)[0] == '.' && ((UC(c)[1] == '\0') || (UC(c)[1] == '/'))) 272#define ISDOTDOT(c) (UC(c)[0] == '.' && ISDOT(&((c)[1]))) 273 if ((unsigned char) cp[0] == '/') 274 return (Strsave(cp)); 275 276 if (adrof(STRignore_symlinks)) { 277 int dotdot = 0; 278 Char *dp, *cwd; 279 280 cwd = (Char *)xmalloc((size_t)((Strlen(dcwd->di_name) + 3) * 281 sizeof(Char))); 282 (void)Strcpy(cwd, dcwd->di_name); 283 284 /* 285 * Ignore . and count ..'s 286 */ 287 while (*cp) { 288 if (ISDOT(cp)) { 289 if (*++cp) 290 cp++; 291 } 292 else if (ISDOTDOT(cp)) { 293 dotdot++; 294 cp += 2; 295 if (*cp) 296 cp++; 297 } 298 else 299 break; 300 } 301 while (dotdot > 0) { 302 dp = Strrchr(cwd, '/'); 303 if (dp) { 304 *dp = '\0'; 305 dotdot--; 306 } 307 else 308 break; 309 } 310 311 if (*cp) { 312 cwd[dotdot = Strlen(cwd)] = '/'; 313 cwd[dotdot + 1] = '\0'; 314 dp = Strspl(cwd, cp); 315 xfree((ptr_t) cwd); 316 return dp; 317 } 318 else { 319 if (!*cwd) { 320 cwd[0] = '/'; 321 cwd[1] = '\0'; 322 } 323 return cwd; 324 } 325 } 326 return Strsave(cp); 327} 328 329/* 330 * dochngd - implement chdir command. 331 */ 332void 333/*ARGSUSED*/ 334dochngd(Char **v, struct command *t) 335{ 336 struct directory *dp; 337 Char *cp; 338 339 skipargs(&v, " [<dir>]"); 340 printd = 0; 341 if (*v == NULL) { 342 if ((cp = value(STRhome)) == NULL || *cp == 0) 343 stderror(ERR_NAME | ERR_NOHOMEDIR); 344 if (chdir(short2str(cp)) < 0) 345 stderror(ERR_NAME | ERR_CANTCHANGE); 346 cp = Strsave(cp); 347 } 348 else if (v[1] != NULL) 349 stderror(ERR_NAME | ERR_TOOMANY); 350 else if ((dp = dfind(*v)) != 0) { 351 char *tmp; 352 353 printd = 1; 354 if (chdir(tmp = short2str(dp->di_name)) < 0) 355 stderror(ERR_SYSTEM, tmp, strerror(errno)); 356 dcwd->di_prev->di_next = dcwd->di_next; 357 dcwd->di_next->di_prev = dcwd->di_prev; 358 dfree(dcwd); 359 dnewcwd(dp); 360 return; 361 } 362 else 363 cp = dfollow(*v); 364 dp = (struct directory *)xcalloc(1, sizeof(struct directory)); 365 dp->di_name = cp; 366 dp->di_count = 0; 367 dp->di_next = dcwd->di_next; 368 dp->di_prev = dcwd->di_prev; 369 dp->di_prev->di_next = dp; 370 dp->di_next->di_prev = dp; 371 dfree(dcwd); 372 dnewcwd(dp); 373} 374 375static Char * 376dgoto(Char *cp) 377{ 378 Char *dp; 379 380 if (*cp != '/') { 381 Char *p, *q; 382 int cwdlen; 383 384 for (p = dcwd->di_name; *p++;) 385 continue; 386 if ((cwdlen = p - dcwd->di_name - 1) == 1) /* root */ 387 cwdlen = 0; 388 for (p = cp; *p++;) 389 continue; 390 dp = (Char *)xmalloc((size_t)((cwdlen + (p - cp) + 1) * sizeof(Char))); 391 for (p = dp, q = dcwd->di_name; (*p++ = *q++) != '\0';) 392 continue; 393 if (cwdlen) 394 p[-1] = '/'; 395 else 396 p--; /* don't add a / after root */ 397 for (q = cp; (*p++ = *q++) != '\0';) 398 continue; 399 xfree((ptr_t) cp); 400 cp = dp; 401 dp += cwdlen; 402 } 403 else 404 dp = cp; 405 406 cp = dcanon(cp, dp); 407 return cp; 408} 409 410/* 411 * dfollow - change to arg directory; fall back on cdpath if not valid 412 */ 413static Char * 414dfollow(Char *cp) 415{ 416 char ebuf[MAXPATHLEN]; 417 struct varent *c; 418 Char *dp; 419 int serrno; 420 421 cp = globone(cp, G_ERROR); 422 /* 423 * if we are ignoring symlinks, try to fix relatives now. 424 */ 425 dp = dnormalize(cp); 426 if (chdir(short2str(dp)) >= 0) { 427 xfree((ptr_t) cp); 428 return dgoto(dp); 429 } 430 else { 431 xfree((ptr_t) dp); 432 if (chdir(short2str(cp)) >= 0) 433 return dgoto(cp); 434 serrno = errno; 435 } 436 437 if (cp[0] != '/' && !prefix(STRdotsl, cp) && !prefix(STRdotdotsl, cp) 438 && (c = adrof(STRcdpath))) { 439 Char **cdp; 440 Char *p; 441 Char buf[MAXPATHLEN]; 442 443 for (cdp = c->vec; *cdp; cdp++) { 444 for (dp = buf, p = *cdp; (*dp++ = *p++) != '\0';) 445 continue; 446 dp[-1] = '/'; 447 for (p = cp; (*dp++ = *p++) != '\0';) 448 continue; 449 if (chdir(short2str(buf)) >= 0) { 450 printd = 1; 451 xfree((ptr_t) cp); 452 cp = Strsave(buf); 453 return dgoto(cp); 454 } 455 } 456 } 457 dp = value(cp); 458 if ((dp[0] == '/' || dp[0] == '.') && chdir(short2str(dp)) >= 0) { 459 xfree((ptr_t) cp); 460 cp = Strsave(dp); 461 printd = 1; 462 return dgoto(cp); 463 } 464 (void)strcpy(ebuf, short2str(cp)); 465 xfree((ptr_t) cp); 466 stderror(ERR_SYSTEM, ebuf, strerror(serrno)); 467 /* NOTREACHED */ 468} 469 470/* 471 * dopushd - push new directory onto directory stack. 472 * with no arguments exchange top and second. 473 * with numeric argument (+n) bring it to top. 474 */ 475void 476/*ARGSUSED*/ 477dopushd(Char **v, struct command *t) 478{ 479 struct directory *dp; 480 481 skipargs(&v, " [<dir>|+<n>]"); 482 printd = 1; 483 if (*v == NULL) { 484 char *tmp; 485 486 if ((dp = dcwd->di_prev) == &dhead) 487 dp = dhead.di_prev; 488 if (dp == dcwd) 489 stderror(ERR_NAME | ERR_NODIR); 490 if (chdir(tmp = short2str(dp->di_name)) < 0) 491 stderror(ERR_SYSTEM, tmp, strerror(errno)); 492 dp->di_prev->di_next = dp->di_next; 493 dp->di_next->di_prev = dp->di_prev; 494 dp->di_next = dcwd->di_next; 495 dp->di_prev = dcwd; 496 dcwd->di_next->di_prev = dp; 497 dcwd->di_next = dp; 498 } 499 else if (v[1] != NULL) 500 stderror(ERR_NAME | ERR_TOOMANY); 501 else if ((dp = dfind(*v)) != NULL) { 502 char *tmp; 503 504 if (chdir(tmp = short2str(dp->di_name)) < 0) 505 stderror(ERR_SYSTEM, tmp, strerror(errno)); 506 } 507 else { 508 Char *ccp; 509 510 ccp = dfollow(*v); 511 dp = (struct directory *)xcalloc(1, sizeof(struct directory)); 512 dp->di_name = ccp; 513 dp->di_count = 0; 514 dp->di_prev = dcwd; 515 dp->di_next = dcwd->di_next; 516 dcwd->di_next = dp; 517 dp->di_next->di_prev = dp; 518 } 519 dnewcwd(dp); 520} 521 522/* 523 * dfind - find a directory if specified by numeric (+n) argument 524 */ 525static struct directory * 526dfind(Char *cp) 527{ 528 struct directory *dp; 529 Char *ep; 530 int i; 531 532 if (*cp++ != '+') 533 return (0); 534 for (ep = cp; Isdigit(*ep); ep++) 535 continue; 536 if (*ep) 537 return (0); 538 i = getn(cp); 539 if (i <= 0) 540 return (0); 541 for (dp = dcwd; i != 0; i--) { 542 if ((dp = dp->di_prev) == &dhead) 543 dp = dp->di_prev; 544 if (dp == dcwd) 545 stderror(ERR_NAME | ERR_DEEP); 546 } 547 return (dp); 548} 549 550/* 551 * dopopd - pop a directory out of the directory stack 552 * with a numeric argument just discard it. 553 */ 554void 555/*ARGSUSED*/ 556dopopd(Char **v, struct command *t) 557{ 558 struct directory *dp, *p = NULL; 559 560 skipargs(&v, " [+<n>]"); 561 printd = 1; 562 if (*v == NULL) 563 dp = dcwd; 564 else if (v[1] != NULL) 565 stderror(ERR_NAME | ERR_TOOMANY); 566 else if ((dp = dfind(*v)) == 0) 567 stderror(ERR_NAME | ERR_BADDIR); 568 if (dp->di_prev == &dhead && dp->di_next == &dhead) 569 stderror(ERR_NAME | ERR_EMPTY); 570 if (dp == dcwd) { 571 char *tmp; 572 573 if ((p = dp->di_prev) == &dhead) 574 p = dhead.di_prev; 575 if (chdir(tmp = short2str(p->di_name)) < 0) 576 stderror(ERR_SYSTEM, tmp, strerror(errno)); 577 } 578 dp->di_prev->di_next = dp->di_next; 579 dp->di_next->di_prev = dp->di_prev; 580 if (dp == dcwd) 581 dnewcwd(p); 582 else { 583 printdirs(); 584 } 585 dfree(dp); 586} 587 588/* 589 * dfree - free the directory (or keep it if it still has ref count) 590 */ 591void 592dfree(struct directory *dp) 593{ 594 595 if (dp->di_count != 0) { 596 dp->di_next = dp->di_prev = 0; 597 } 598 else { 599 xfree((char *) dp->di_name); 600 xfree((ptr_t) dp); 601 } 602} 603 604/* 605 * dcanon - canonicalize the pathname, removing excess ./ and ../ etc. 606 * we are of course assuming that the file system is standardly 607 * constructed (always have ..'s, directories have links) 608 */ 609Char * 610dcanon(Char *cp, Char *p) 611{ 612 Char slink[MAXPATHLEN]; 613 char tlink[MAXPATHLEN]; 614 Char *newcp, *sp; 615 Char *p1, *p2; /* general purpose */ 616 int cc; 617 int slash; 618 619 /* 620 * christos: if the path given does not start with a slash prepend cwd. If 621 * cwd does not start with a path or the result would be too long abort(). 622 */ 623 if (*cp != '/') { 624 Char tmpdir[MAXPATHLEN]; 625 626 p1 = value(STRcwd); 627 if (p1 == NULL || *p1 != '/') 628 abort(); 629 if (Strlen(p1) + Strlen(cp) + 1 >= MAXPATHLEN) 630 abort(); 631 (void)Strcpy(tmpdir, p1); 632 (void)Strcat(tmpdir, STRslash); 633 (void)Strcat(tmpdir, cp); 634 xfree((ptr_t) cp); 635 cp = p = Strsave(tmpdir); 636 } 637 638 while (*p) { /* for each component */ 639 sp = p; /* save slash address */ 640 while (*++p == '/') /* flush extra slashes */ 641 continue; 642 if (p != ++sp) 643 for (p1 = sp, p2 = p; (*p1++ = *p2++) != '\0';) 644 continue; 645 p = sp; /* save start of component */ 646 slash = 0; 647 while (*++p) /* find next slash or end of path */ 648 if (*p == '/') { 649 slash = 1; 650 *p = 0; 651 break; 652 } 653 654 if (*sp == '\0') { /* if component is null */ 655 if (--sp == cp) /* if path is one char (i.e. /) */ 656 break; 657 else 658 *sp = '\0'; 659 } else if (sp[0] == '.' && sp[1] == 0) { 660 if (slash) { 661 for (p1 = sp, p2 = p + 1; (*p1++ = *p2++) != '\0';) 662 continue; 663 p = --sp; 664 } 665 else if (--sp != cp) 666 *sp = '\0'; 667 } 668 else if (sp[0] == '.' && sp[1] == '.' && sp[2] == 0) { 669 /* 670 * We have something like "yyy/xxx/..", where "yyy" can be null or 671 * a path starting at /, and "xxx" is a single component. Before 672 * compressing "xxx/..", we want to expand "yyy/xxx", if it is a 673 * symbolic link. 674 */ 675 *--sp = 0; /* form the pathname for readlink */ 676 if (sp != cp && !adrof(STRignore_symlinks) && 677 (cc = readlink(short2str(cp), tlink, 678 sizeof(tlink) - 1)) >= 0) { 679 tlink[cc] = '\0'; 680 (void)Strcpy(slink, str2short(tlink)); 681 682 if (slash) 683 *p = '/'; 684 /* 685 * Point p to the '/' in "/..", and restore the '/'. 686 */ 687 *(p = sp) = '/'; 688 /* 689 * find length of p 690 */ 691 for (p1 = p; *p1++;) 692 continue; 693 if (*slink != '/') { 694 /* 695 * Relative path, expand it between the "yyy/" and the 696 * "/..". First, back sp up to the character past "yyy/". 697 */ 698 while (*--sp != '/') 699 continue; 700 sp++; 701 *sp = 0; 702 /* 703 * New length is "yyy/" + slink + "/.." and rest 704 */ 705 p1 = newcp = (Char *)xmalloc( 706 (size_t)(((sp - cp) + cc + (p1 - p)) * sizeof(Char))); 707 /* 708 * Copy new path into newcp 709 */ 710 for (p2 = cp; (*p1++ = *p2++) != '\0';) 711 continue; 712 for (p1--, p2 = slink; (*p1++ = *p2++) != '\0';) 713 continue; 714 for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) 715 continue; 716 /* 717 * Restart canonicalization at expanded "/xxx". 718 */ 719 p = sp - cp - 1 + newcp; 720 } 721 else { 722 /* 723 * New length is slink + "/.." and rest 724 */ 725 p1 = newcp = (Char *)xmalloc( 726 (size_t)((cc + (p1 - p)) * sizeof(Char))); 727 /* 728 * Copy new path into newcp 729 */ 730 for (p2 = slink; (*p1++ = *p2++) != '\0';) 731 continue; 732 for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) 733 continue; 734 /* 735 * Restart canonicalization at beginning 736 */ 737 p = newcp; 738 } 739 xfree((ptr_t) cp); 740 cp = newcp; 741 continue; /* canonicalize the link */ 742 } 743 *sp = '/'; 744 if (sp != cp) 745 while (*--sp != '/') 746 continue; 747 if (slash) { 748 for (p1 = sp + 1, p2 = p + 1; (*p1++ = *p2++) != '\0';) 749 continue; 750 p = sp; 751 } 752 else if (cp == sp) 753 *++sp = '\0'; 754 else 755 *sp = '\0'; 756 } 757 else { /* normal dir name (not . or .. or nothing) */ 758 759 if (sp != cp && adrof(STRchase_symlinks) && 760 !adrof(STRignore_symlinks) && 761 (cc = readlink(short2str(cp), tlink, sizeof(tlink)-1)) >= 0) { 762 tlink[cc] = '\0'; 763 (void)Strcpy(slink, str2short(tlink)); 764 765 /* 766 * restore the '/'. 767 */ 768 if (slash) 769 *p = '/'; 770 771 /* 772 * point sp to p (rather than backing up). 773 */ 774 sp = p; 775 776 /* 777 * find length of p 778 */ 779 for (p1 = p; *p1++;) 780 continue; 781 if (*slink != '/') { 782 /* 783 * Relative path, expand it between the "yyy/" and the 784 * remainder. First, back sp up to the character past 785 * "yyy/". 786 */ 787 while (*--sp != '/') 788 continue; 789 sp++; 790 *sp = 0; 791 /* 792 * New length is "yyy/" + slink + "/.." and rest 793 */ 794 p1 = newcp = (Char *)xmalloc( 795 (size_t)(((sp - cp) + cc + (p1 - p)) * sizeof(Char))); 796 /* 797 * Copy new path into newcp 798 */ 799 for (p2 = cp; (*p1++ = *p2++) != '\0';) 800 continue; 801 for (p1--, p2 = slink; (*p1++ = *p2++) != '\0';) 802 continue; 803 for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) 804 continue; 805 /* 806 * Restart canonicalization at expanded "/xxx". 807 */ 808 p = sp - cp - 1 + newcp; 809 } 810 else { 811 /* 812 * New length is slink + the rest 813 */ 814 p1 = newcp = (Char *)xmalloc( 815 (size_t)((cc + (p1 - p)) * sizeof(Char))); 816 /* 817 * Copy new path into newcp 818 */ 819 for (p2 = slink; (*p1++ = *p2++) != '\0';) 820 continue; 821 for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) 822 continue; 823 /* 824 * Restart canonicalization at beginning 825 */ 826 p = newcp; 827 } 828 xfree((ptr_t) cp); 829 cp = newcp; 830 continue; /* canonicalize the link */ 831 } 832 if (slash) 833 *p = '/'; 834 } 835 } 836 837 /* 838 * fix home... 839 */ 840 p1 = value(STRhome); 841 cc = Strlen(p1); 842 /* 843 * See if we're not in a subdir of STRhome 844 */ 845 if (p1 && *p1 == '/' && 846 (Strncmp(p1, cp, cc) != 0 || (cp[cc] != '/' && cp[cc] != '\0'))) { 847 static ino_t home_ino; 848 static dev_t home_dev = NODEV; 849 static Char *home_ptr = NULL; 850 struct stat statbuf; 851 852 /* 853 * Get dev and ino of STRhome 854 */ 855 if (home_ptr != p1 && 856 stat(short2str(p1), &statbuf) != -1) { 857 home_dev = statbuf.st_dev; 858 home_ino = statbuf.st_ino; 859 home_ptr = p1; 860 } 861 /* 862 * Start comparing dev & ino backwards 863 */ 864 p2 = Strcpy(slink, cp); 865 for (sp = NULL; *p2 && stat(short2str(p2), &statbuf) != -1;) { 866 if (statbuf.st_dev == home_dev && 867 statbuf.st_ino == home_ino) { 868 sp = (Char *) - 1; 869 break; 870 } 871 if ((sp = Strrchr(p2, '/')) != NULL) 872 *sp = '\0'; 873 } 874 /* 875 * See if we found it 876 */ 877 if (*p2 && sp == (Char *) -1) { 878 /* 879 * Use STRhome to make '~' work 880 */ 881 newcp = Strspl(p1, cp + Strlen(p2)); 882 xfree((ptr_t) cp); 883 cp = newcp; 884 } 885 } 886 return cp; 887} 888 889 890/* 891 * dnewcwd - make a new directory in the loop the current one 892 */ 893static void 894dnewcwd(struct directory *dp) 895{ 896 dcwd = dp; 897 dset(dcwd->di_name); 898 if (printd && !(adrof(STRpushdsilent))) 899 printdirs(); 900} 901