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