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