1 /* $Header: /src/pub/tcsh/sh.dir.c,v 3.60 2002/07/08 21:03:04 christos Exp $ */
3 * sh.dir.c: Directory manipulation functions
6 * Copyright (c) 1980, 1991 The Regents of the University of California.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. 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.
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
35 RCSID("$Id: sh.dir.c,v 3.60 2002/07/08 21:03:04 christos Exp $")
38 * C Shell - directory management
41 static void dstart
__P((const char *));
42 static struct directory
*dfind
__P((Char
*));
43 static Char
*dfollow
__P((Char
*));
44 static void printdirs
__P((int));
45 static Char
*dgoto
__P((Char
*));
46 static void dnewcwd
__P((struct directory
*, int));
47 static void dset
__P((Char
*));
48 static void dextract
__P((struct directory
*));
49 static int skipargs
__P((Char
***, char *, char *));
50 static void dgetstack
__P((void));
52 static struct directory dhead INIT_ZERO_STRUCT
; /* "head" of loop */
53 static int printd
; /* force name to be printed */
55 int bequiet
= 0; /* do not print dir stack -strike */
61 xprintf(CGETS(12, 1, "%s: Trying to start from \"%s\"\n"), progname
, from
);
65 * dinit - initialize current working directory
73 register struct directory
*dp
;
74 char path
[MAXPATHLEN
];
76 /* Don't believe the login shell home, because it may be a symlink */
77 tcp
= (char *) getcwd(path
, sizeof(path
));
78 if (tcp
== NULL
|| *tcp
== '\0') {
79 xprintf("%s: %s\n", progname
, strerror(errno
));
93 /* I am not even try to print an error message! */
100 struct stat swd
, shp
;
103 * See if $HOME is the working directory we got and use that
106 stat(tcp
, &swd
) != -1 && stat(short2str(hp
), &shp
) != -1 &&
107 DEV_DEV_COMPARE(swd
.st_dev
, shp
.st_dev
) &&
108 swd
.st_ino
== shp
.st_ino
)
114 * use PWD if we have it (for subshells)
116 if ((cwd
= getenv("PWD")) != NULL
) {
117 if (stat(cwd
, &shp
) != -1 &&
118 DEV_DEV_COMPARE(swd
.st_dev
, shp
.st_dev
) &&
119 swd
.st_ino
== shp
.st_ino
)
122 cp
= dcanon(SAVE(tcp
), STRNULL
);
125 cp
= dcanon(SAVE(tcp
), STRNULL
);
129 dp
= (struct directory
*) xcalloc(sizeof(struct directory
), 1);
132 dhead
.di_next
= dhead
.di_prev
= dp
;
133 dp
->di_next
= dp
->di_prev
= &dhead
;
136 set(STRdirstack
, Strsave(dp
->di_name
), VAR_READWRITE
|VAR_NOGLOB
);
144 * Don't call set() directly cause if the directory contains ` or
145 * other junk characters glob will fail.
147 set(STRowd
, Strsave(varval(STRcwd
)), VAR_READWRITE
|VAR_NOGLOB
);
148 set(STRcwd
, Strsave(dp
), VAR_READWRITE
|VAR_NOGLOB
);
153 #define DIR_PRINT 0x01 /* -p */
154 #define DIR_LONG 0x02 /* -l */
155 #define DIR_VERT 0x04 /* -v */
156 #define DIR_LINE 0x08 /* -n */
157 #define DIR_SAVE 0x10 /* -S */
158 #define DIR_LOAD 0x20 /* -L */
159 #define DIR_CLEAR 0x40 /* -c */
160 #define DIR_OLD 0x80 /* - */
163 skipargs(v
, dstr
, str
)
170 int dflag
= 0, loop
= 1;
171 for (n
++; loop
&& *n
!= NULL
&& (*n
)[0] == '-'; n
++)
172 if (*(s
= &((*n
)[1])) == '\0') /* test for bare "-" argument */
176 while (loop
&& *s
!= '\0') /* examine flags */
178 if ((p
= strchr(dstr
, *s
++)) != NULL
)
179 dflag
|= (1 << (p
- dstr
));
181 stderror(ERR_DIRUS
, short2str(**v
), dstr
, str
);
182 loop
= 0; /* break from both loops */
187 if (*n
&& (dflag
& DIR_OLD
))
188 stderror(ERR_DIRUS
, short2str(**v
), dstr
, str
);
190 /* make -l, -v, and -n imply -p */
191 if (dflag
& (DIR_LONG
|DIR_VERT
|DIR_LINE
))
197 * dodirs - list all directories in directory loop
205 static char flags
[] = "plvnSLc";
206 int dflag
= skipargs(&v
, flags
, "");
209 if ((dflag
& DIR_CLEAR
) != 0) {
210 struct directory
*dp
, *fdp
;
211 for (dp
= dcwd
->di_next
; dp
!= dcwd
; ) {
217 dhead
.di_next
= dhead
.di_prev
= dp
;
218 dp
->di_next
= dp
->di_prev
= &dhead
;
220 if ((dflag
& DIR_LOAD
) != 0)
222 else if ((dflag
& DIR_SAVE
) != 0)
225 if (*v
&& (dflag
& (DIR_SAVE
|DIR_LOAD
)))
228 if (*v
!= NULL
|| (dflag
& DIR_OLD
))
229 stderror(ERR_DIRUS
, "dirs", flags
, "");
230 if ((dflag
& (DIR_CLEAR
|DIR_LOAD
|DIR_SAVE
)) == 0 || (dflag
& DIR_PRINT
))
238 register struct directory
*dp
;
249 if (dflag
& DIR_VERT
) {
250 xprintf("%d\t", idx
++);
255 if (!(dflag
& DIR_LONG
) && (user
= getusername(&s
)) != NULL
)
256 len
= (int) (Strlen(user
) + Strlen(s
) + 2);
258 len
= (int) (Strlen(s
) + 1);
261 if ((dflag
& DIR_LINE
) && cur
>= T_Cols
- 1 && len
< T_Cols
) {
266 xprintf("~%S", user
);
267 xprintf("%S%c", s
, (dflag
& DIR_VERT
) ? '\n' : ' ');
268 } while ((dp
= dp
->di_prev
) != dcwd
);
269 if (!(dflag
& DIR_VERT
))
278 if ((user
= getusername(&dir
)) != NULL
)
279 xprintf("~%S%S", user
, dir
);
287 struct directory
*d
= dcwd
;
292 d
->di_name
= dcanon(d
->di_name
, STRNULL
);
293 } while ((d
= d
->di_prev
) != dcwd
);
300 * The path will be normalized if it
302 * 2) or starts with "../",
303 * 3) or ends with "/..",
304 * 4) or contains the string "/../",
305 * then it will be normalized, unless those strings are quoted.
306 * Otherwise, a copy is made and sent back.
314 /* return true if dp is of the form "../xxx" or "/../xxx" */
315 #define IS_DOTDOT(sp, p) (ISDOTDOT(p) && ((p) == (sp) || *((p) - 1) == '/'))
316 #define IS_DOT(sp, p) (ISDOT(p) && ((p) == (sp) || *((p) - 1) == '/'))
321 Char
*dp
, *cwd
, *start
= cp
, buf
[MAXPATHLEN
];
328 * count the number of "../xxx" or "xxx/../xxx" in the path
330 for (dp
=start
; *dp
&& *(dp
+1); dp
++)
331 if (IS_DOTDOT(start
, dp
))
334 * if none, we are done.
337 return (Strsave(cp
));
340 * If the path doesn't exist, we are done too.
342 if (lstat(short2str(cp
), &sb
) != 0 && errno
== ENOENT
)
343 return (Strsave(cp
));
346 cwd
= (Char
*) xmalloc((size_t) (((int) Strlen(dcwd
->di_name
) + 3) *
348 (void) Strcpy(cwd
, dcwd
->di_name
);
351 * If the path starts with a slash, we are not relative to
352 * the current working directory.
354 if (ABSOLUTEP(start
))
357 slashslash
= cwd
[0] == '/' && cwd
[1] == '/';
361 * Ignore . and count ..'s
368 if (IS_DOT(start
, cp
)) {
372 else if (IS_DOTDOT(start
, cp
)) {
374 break; /* finish analyzing .././../xxx/[..] */
385 if ((dp
= Strrchr(cwd
, '/')) != NULL
) {
396 if (!*cwd
) { /* too many ..'s, starts with "/" */
406 else if (slashslash
&& cwd
[1] == '\0') {
413 if ((TRM(cwd
[(dotdot
= (int) Strlen(cwd
)) - 1])) != '/')
416 dp
= Strspl(cwd
, TRM(buf
[0]) == '/' ? &buf
[1] : buf
);
419 if ((TRM(cwd
[(dotdot
= (int) Strlen(cwd
)) - 1])) == '/')
420 cwd
[--dotdot
] = '\0';
422 /* Reduction of ".." following the stuff we collected in buf
423 * only makes sense if the directory item in buf really exists.
424 * Avoid reduction of "-I../.." (typical compiler call) to ""
425 * or "/usr/nonexistant/../bin" to "/usr/bin":
429 if (0 != stat(short2str(cwd
), &exists
)) {
431 return Strsave(start
);
445 * dochngd - implement chdir command.
454 register struct directory
*dp
;
455 int dflag
= skipargs(&v
, "plvn", "[-|<dir>]");
459 cp
= (dflag
& DIR_OLD
) ? varval(STRowd
) : *v
;
462 if ((cp
= varval(STRhome
)) == STRNULL
|| *cp
== 0)
463 stderror(ERR_NAME
| ERR_NOHOMEDIR
);
464 if (chdir(short2str(cp
)) < 0)
465 stderror(ERR_NAME
| ERR_CANTCHANGE
);
468 else if ((dflag
& DIR_OLD
) == 0 && v
[1] != NULL
) {
469 stderror(ERR_NAME
| ERR_TOOMANY
);
473 else if ((dp
= dfind(cp
)) != 0) {
477 if (chdir(tmp
= short2str(dp
->di_name
)) < 0)
478 stderror(ERR_SYSTEM
, tmp
, strerror(errno
));
479 dcwd
->di_prev
->di_next
= dcwd
->di_next
;
480 dcwd
->di_next
->di_prev
= dcwd
->di_prev
;
486 if ((cp
= dfollow(cp
)) == NULL
)
488 dp
= (struct directory
*) xcalloc(sizeof(struct directory
), 1);
491 dp
->di_next
= dcwd
->di_next
;
492 dp
->di_prev
= dcwd
->di_prev
;
493 dp
->di_prev
->di_next
= dp
;
494 dp
->di_next
->di_prev
= dp
;
507 register Char
*p
, *q
;
510 for (p
= dcwd
->di_name
; *p
++;)
512 if ((cwdlen
= (int) (p
- dcwd
->di_name
- 1)) == 1) /* root */
516 dp
= (Char
*) xmalloc((size_t)((cwdlen
+ (p
- cp
) + 1) * sizeof(Char
)));
517 for (p
= dp
, q
= dcwd
->di_name
; (*p
++ = *q
++) != '\0';)
522 p
--; /* don't add a / after root */
523 for (q
= cp
; (*p
++ = *q
++) != '\0';)
533 cp
= SAVE(getcwd(NULL
, 0));
534 #else /* !WINNT_NATIVE */
536 #endif /* WINNT_NATIVE */
541 * dfollow - change to arg directory; fall back on cdpath if not valid
549 char ebuf
[MAXPATHLEN
];
552 cp
= globone(cp
, G_ERROR
);
554 if (Strchr(cp
, '`')) {
556 if (chdir(dptr
= short2str(cp
)) < 0)
557 stderror(ERR_SYSTEM
, dptr
, strerror(errno
));
558 else if ((ptr
= getcwd(ebuf
, sizeof(ebuf
))) && *ptr
!= '\0') {
560 cp
= Strsave(str2short(ptr
));
564 stderror(ERR_SYSTEM
, dptr
, ebuf
);
568 (void) strncpy(ebuf
, short2str(cp
), MAXPATHLEN
);
569 ebuf
[MAXPATHLEN
-1] = '\0';
571 * if we are ignoring symlinks, try to fix relatives now.
572 * if we are expading symlinks, it should be done by now.
574 dp
= dnormalize(cp
, symlinks
== SYM_IGNORE
);
575 if (chdir(short2str(dp
)) >= 0) {
581 if (chdir(short2str(cp
)) >= 0)
583 else if (errno
!= ENOENT
&& errno
!= ENOTDIR
)
584 stderror(ERR_SYSTEM
, ebuf
, strerror(errno
));
588 if (cp
[0] != '/' && !prefix(STRdotsl
, cp
) && !prefix(STRdotdotsl
, cp
)
589 && (c
= adrof(STRcdpath
)) && c
->vec
!= NULL
) {
592 Char buf
[MAXPATHLEN
];
594 for (cdp
= c
->vec
; *cdp
; cdp
++) {
595 for (dp
= buf
, p
= *cdp
; (*dp
++ = *p
++) != '\0';)
598 for (p
= cp
; (*dp
++ = *p
++) != '\0';)
601 * We always want to fix the directory here
602 * If we are normalizing symlinks
604 dp
= dnormalize(buf
, symlinks
== SYM_IGNORE
||
605 symlinks
== SYM_EXPAND
);
606 if (chdir(short2str(dp
)) >= 0) {
611 else if (chdir(short2str(cp
)) >= 0) {
619 if ((dp
[0] == '/' || dp
[0] == '.') && chdir(short2str(dp
)) >= 0) {
627 * on login source of ~/.cshdirs, errors are eaten. the dir stack is all
628 * directories we could get to.
631 stderror(ERR_SYSTEM
, ebuf
, strerror(serrno
));
640 * dopushd - push new directory onto directory stack.
641 * with no arguments exchange top and second.
642 * with numeric argument (+n) bring it to top.
650 register struct directory
*dp
;
652 int dflag
= skipargs(&v
, "plvn", " [-|<dir>|+<n>]");
656 cp
= (dflag
& DIR_OLD
) ? varval(STRowd
) : *v
;
659 if (adrof(STRpushdtohome
)) {
660 if ((cp
= varval(STRhome
)) == STRNULL
|| *cp
== 0)
661 stderror(ERR_NAME
| ERR_NOHOMEDIR
);
662 if (chdir(short2str(cp
)) < 0)
663 stderror(ERR_NAME
| ERR_CANTCHANGE
);
664 cp
= Strsave(cp
); /* hmmm... PWP */
665 if ((cp
= dfollow(cp
)) == NULL
)
667 dp
= (struct directory
*) xcalloc(sizeof(struct directory
), 1);
671 dp
->di_next
= dcwd
->di_next
;
673 dp
->di_next
->di_prev
= dp
;
678 if ((dp
= dcwd
->di_prev
) == &dhead
)
681 stderror(ERR_NAME
| ERR_NODIR
);
682 if (chdir(tmp
= short2str(dp
->di_name
)) < 0)
683 stderror(ERR_SYSTEM
, tmp
, strerror(errno
));
684 dp
->di_prev
->di_next
= dp
->di_next
;
685 dp
->di_next
->di_prev
= dp
->di_prev
;
686 dp
->di_next
= dcwd
->di_next
;
688 dcwd
->di_next
->di_prev
= dp
;
692 else if ((dflag
& DIR_OLD
) == 0 && v
[1] != NULL
) {
693 stderror(ERR_NAME
| ERR_TOOMANY
);
697 else if ((dp
= dfind(cp
)) != NULL
) {
700 if (chdir(tmp
= short2str(dp
->di_name
)) < 0)
701 stderror(ERR_SYSTEM
, tmp
, strerror(errno
));
703 * kfk - 10 Feb 1984 - added new "extraction style" pushd +n
705 if (adrof(STRdextract
))
711 if ((ccp
= dfollow(cp
)) == NULL
)
713 dp
= (struct directory
*) xcalloc(sizeof(struct directory
), 1);
717 dp
->di_next
= dcwd
->di_next
;
719 dp
->di_next
->di_prev
= dp
;
725 * dfind - find a directory if specified by numeric (+n) argument
727 static struct directory
*
731 register struct directory
*dp
;
737 for (ep
= cp
; Isdigit(*ep
); ep
++)
744 for (dp
= dcwd
; i
!= 0; i
--) {
745 if ((dp
= dp
->di_prev
) == &dhead
)
748 stderror(ERR_NAME
| ERR_DEEP
);
754 * dopopd - pop a directory out of the directory stack
755 * with a numeric argument just discard it.
764 register struct directory
*dp
, *p
= NULL
;
765 int dflag
= skipargs(&v
, "plvn", " [-|+<n>]");
769 cp
= (dflag
& DIR_OLD
) ? varval(STRowd
) : *v
;
773 else if ((dflag
& DIR_OLD
) == 0 && v
[1] != NULL
) {
774 stderror(ERR_NAME
| ERR_TOOMANY
);
778 else if ((dp
= dfind(cp
)) == 0)
779 stderror(ERR_NAME
| ERR_BADDIR
);
780 if (dp
->di_prev
== &dhead
&& dp
->di_next
== &dhead
)
781 stderror(ERR_NAME
| ERR_EMPTY
);
785 if ((p
= dp
->di_prev
) == &dhead
)
787 if (chdir(tmp
= short2str(p
->di_name
)) < 0)
788 stderror(ERR_SYSTEM
, tmp
, strerror(errno
));
790 dp
->di_prev
->di_next
= dp
->di_next
;
791 dp
->di_next
->di_prev
= dp
->di_prev
;
802 * dfree - free the directory (or keep it if it still has ref count)
806 register struct directory
*dp
;
809 if (dp
->di_count
!= 0) {
810 dp
->di_next
= dp
->di_prev
= 0;
813 xfree((ptr_t
) dp
->di_name
);
819 * dcanon - canonicalize the pathname, removing excess ./ and ../ etc.
820 * we are of course assuming that the file system is standardly
821 * constructed (always have ..'s, directories have links)
825 register Char
*cp
, *p
;
828 register Char
*p1
, *p2
; /* general purpose */
835 #ifdef S_IFLNK /* if we have symlinks */
836 Char link
[MAXPATHLEN
];
837 char tlink
[MAXPATHLEN
];
843 * if the path given is too long truncate it!
845 if ((clen
= Strlen(cp
)) >= MAXPATHLEN
)
846 cp
[clen
= MAXPATHLEN
- 1] = '\0';
849 * christos: if the path given does not start with a slash prepend cwd. If
850 * cwd does not start with a slash or the result would be too long try to
853 if (!ABSOLUTEP(cp
)) {
854 Char tmpdir
[MAXPATHLEN
];
858 if (p1
== STRNULL
|| !ABSOLUTEP(p1
)) {
859 char *tmp
= (char *)getcwd((char *)tmpdir
, sizeof(tmpdir
));
860 if (tmp
== NULL
|| *tmp
== '\0') {
861 xprintf("%s: %s\n", progname
, strerror(errno
));
862 set(STRcwd
, SAVE("/"), VAR_READWRITE
|VAR_NOGLOB
);
864 set(STRcwd
, SAVE(tmp
), VAR_READWRITE
|VAR_NOGLOB
);
869 if (len
+ clen
+ 1 >= MAXPATHLEN
)
870 cp
[MAXPATHLEN
- (len
+ 1)] = '\0';
871 (void) Strcpy(tmpdir
, p1
);
872 (void) Strcat(tmpdir
, STRslash
);
873 (void) Strcat(tmpdir
, cp
);
875 cp
= p
= Strsave(tmpdir
);
879 slashslash
= (cp
[0] == '/' && cp
[1] == '/');
882 while (*p
) { /* for each component */
883 sp
= p
; /* save slash address */
884 while (*++p
== '/') /* flush extra slashes */
887 for (p1
= sp
, p2
= p
; (*p1
++ = *p2
++) != '\0';)
889 p
= sp
; /* save start of component */
892 while (*++p
) /* find next slash or end of path */
900 if (&cp
[1] == sp
&& sp
[0] == '.' && sp
[1] == '.' && sp
[2] == '\0')
903 if (*sp
== '\0') { /* if component is null */
904 if (--sp
== cp
) /* if path is one char (i.e. /) */
909 else if (sp
[0] == '.' && sp
[1] == 0) {
911 for (p1
= sp
, p2
= p
+ 1; (*p1
++ = *p2
++) != '\0';)
920 else if (sp
[0] == '.' && sp
[1] == '.' && sp
[2] == 0) {
922 * We have something like "yyy/xxx/..", where "yyy" can be null or
923 * a path starting at /, and "xxx" is a single component. Before
924 * compressing "xxx/..", we want to expand "yyy/xxx", if it is a
927 *--sp
= 0; /* form the pathname for readlink */
928 #ifdef S_IFLNK /* if we have symlinks */
929 if (sp
!= cp
&& /* symlinks != SYM_IGNORE && */
930 (cc
= readlink(short2str(cp
), tlink
,
931 sizeof tlink
)) >= 0) {
933 (void) Strncpy(link
, str2short(tlink
),
934 sizeof(link
) / sizeof(Char
));
935 link
[sizeof(link
) / sizeof(Char
) - 1] = '\0';
940 * Point p to the '/' in "/..", and restore the '/'.
950 * Relative path, expand it between the "yyy/" and the
951 * "/..". First, back sp up to the character past "yyy/".
958 * New length is "yyy/" + link + "/.." and rest
960 p1
= newcp
= (Char
*) xmalloc((size_t)
961 (((sp
- cp
) + cc
+ (p1
- p
)) *
964 * Copy new path into newcp
966 for (p2
= cp
; (*p1
++ = *p2
++) != '\0';)
968 for (p1
--, p2
= link
; (*p1
++ = *p2
++) != '\0';)
970 for (p1
--, p2
= p
; (*p1
++ = *p2
++) != '\0';)
973 * Restart canonicalization at expanded "/xxx".
975 p
= sp
- cp
- 1 + newcp
;
979 * New length is link + "/.." and rest
981 p1
= newcp
= (Char
*) xmalloc((size_t)
982 ((cc
+ (p1
- p
)) * sizeof(Char
)));
984 * Copy new path into newcp
986 for (p2
= link
; (*p1
++ = *p2
++) != '\0';)
988 for (p1
--, p2
= p
; (*p1
++ = *p2
++) != '\0';)
991 * Restart canonicalization at beginning
998 slashslash
= (cp
[0] == '/' && cp
[1] == '/');
1000 continue; /* canonicalize the link */
1002 #endif /* S_IFLNK */
1005 while (*--sp
!= '/')
1008 for (p1
= sp
+ 1, p2
= p
+ 1; (*p1
++ = *p2
++) != '\0';)
1017 else { /* normal dir name (not . or .. or nothing) */
1019 #ifdef S_IFLNK /* if we have symlinks */
1020 if (sp
!= cp
&& symlinks
== SYM_CHASE
&&
1021 (cc
= readlink(short2str(cp
), tlink
,
1022 sizeof tlink
)) >= 0) {
1024 (void) Strncpy(link
, str2short(tlink
),
1025 sizeof(link
) / sizeof(Char
));
1026 link
[sizeof(link
) / sizeof(Char
) - 1] = '\0';
1035 * point sp to p (rather than backing up).
1042 for (p1
= p
; *p1
++;)
1046 * Relative path, expand it between the "yyy/" and the
1047 * remainder. First, back sp up to the character past
1050 while (*--sp
!= '/')
1055 * New length is "yyy/" + link + "/.." and rest
1057 p1
= newcp
= (Char
*) xmalloc((size_t)
1058 (((sp
- cp
) + cc
+ (p1
- p
))
1061 * Copy new path into newcp
1063 for (p2
= cp
; (*p1
++ = *p2
++) != '\0';)
1065 for (p1
--, p2
= link
; (*p1
++ = *p2
++) != '\0';)
1067 for (p1
--, p2
= p
; (*p1
++ = *p2
++) != '\0';)
1070 * Restart canonicalization at expanded "/xxx".
1072 p
= sp
- cp
- 1 + newcp
;
1076 * New length is link + the rest
1078 p1
= newcp
= (Char
*) xmalloc((size_t)
1079 ((cc
+ (p1
- p
)) * sizeof(Char
)));
1081 * Copy new path into newcp
1083 for (p2
= link
; (*p1
++ = *p2
++) != '\0';)
1085 for (p1
--, p2
= p
; (*p1
++ = *p2
++) != '\0';)
1088 * Restart canonicalization at beginning
1095 slashslash
= (cp
[0] == '/' && cp
[1] == '/');
1097 continue; /* canonicalize the link */
1099 #endif /* S_IFLNK */
1109 p1
= varval(STRhome
);
1110 cc
= (int) Strlen(p1
);
1112 * See if we're not in a subdir of STRhome
1114 if (p1
&& *p1
== '/' && (Strncmp(p1
, cp
, (size_t) cc
) != 0 ||
1115 (cp
[cc
] != '/' && cp
[cc
] != '\0'))) {
1116 static ino_t home_ino
= (ino_t
) -1;
1117 static dev_t home_dev
= (dev_t
) -1;
1118 static Char
*home_ptr
= NULL
;
1119 struct stat statbuf
;
1123 * Get dev and ino of STRhome
1125 if (home_ptr
!= p1
&&
1126 stat(short2str(p1
), &statbuf
) != -1) {
1127 home_dev
= statbuf
.st_dev
;
1128 home_ino
= statbuf
.st_ino
;
1132 * Start comparing dev & ino backwards
1134 p2
= Strncpy(link
, cp
, sizeof(link
) / sizeof(Char
));
1135 link
[sizeof(link
) / sizeof(Char
) - 1] = '\0';
1137 while (*p2
&& stat(short2str(p2
), &statbuf
) != -1) {
1138 if (DEV_DEV_COMPARE(statbuf
.st_dev
, home_dev
) &&
1139 statbuf
.st_ino
== home_ino
) {
1143 if ((sp
= Strrchr(p2
, '/')) != NULL
)
1147 * See if we found it
1151 * Use STRhome to make '~' work
1153 newcp
= Strspl(p1
, cp
+ Strlen(p2
));
1158 #endif /* S_IFLNK */
1163 p
= (Char
*) xmalloc((size_t) (Strlen(cp
) + 2) * sizeof(Char
));
1165 (void) Strcpy(&p
[1], cp
);
1170 if (cp
[1] == '/' && cp
[2] == '/')
1171 (void) Strcpy(&cp
[1], &cp
[2]);
1178 * dnewcwd - make a new directory in the loop the current one
1182 register struct directory
*dp
;
1187 if (adrof(STRdunique
)) {
1188 struct directory
*dn
;
1190 for (dn
= dhead
.di_prev
; dn
!= &dhead
; dn
= dn
->di_prev
)
1191 if (dn
!= dp
&& Strcmp(dn
->di_name
, dp
->di_name
) == 0) {
1192 dn
->di_next
->di_prev
= dn
->di_prev
;
1193 dn
->di_prev
->di_next
= dn
->di_next
;
1199 dset(dcwd
->di_name
);
1201 print
= printd
; /* if printd is set, print dirstack... */
1202 if (adrof(STRpushdsilent
)) /* but pushdsilent overrides printd... */
1204 if (dflag
& DIR_PRINT
) /* but DIR_PRINT overrides pushdsilent... */
1206 if (bequiet
) /* and bequiet overrides everything */
1210 cwd_cmd(); /* PWP: run the defined cwd command */
1218 struct directory
*dn
, *dp
;
1220 if ((vp
= adrof(STRdirstack
)) == NULL
|| vp
->vec
== NULL
)
1223 /* Free the whole stack */
1224 while ((dn
= dhead
.di_prev
) != &dhead
) {
1225 dn
->di_next
->di_prev
= dn
->di_prev
;
1226 dn
->di_prev
->di_next
= dn
->di_next
;
1231 /* thread the current working directory */
1232 dhead
.di_prev
= dhead
.di_next
= dcwd
;
1233 dcwd
->di_next
= dcwd
->di_prev
= &dhead
;
1235 /* put back the stack */
1236 for (cp
= vp
->vec
; cp
&& *cp
&& **cp
; cp
++) {
1237 dp
= (struct directory
*) xcalloc(sizeof(struct directory
), 1);
1238 dp
->di_name
= Strsave(*cp
);
1241 dp
->di_next
= dcwd
->di_next
;
1243 dp
->di_next
->di_prev
= dp
;
1245 dgetstack(); /* Make $dirstack reflect the current state */
1253 struct directory
*dn
;
1255 if (adrof(STRdirstack
) == NULL
)
1258 for (dn
= dhead
.di_prev
; dn
!= &dhead
; dn
= dn
->di_prev
, i
++)
1260 dbp
= dblk
= (Char
**) xmalloc((size_t) (i
+ 1) * sizeof(Char
*));
1261 for (dn
= dhead
.di_prev
; dn
!= &dhead
; dn
= dn
->di_prev
, dbp
++)
1262 *dbp
= Strsave(dn
->di_name
);
1264 setq(STRdirstack
, dblk
, &shvhed
, VAR_READWRITE
);
1268 * getstakd - added by kfk 17 Jan 1984
1269 * Support routine for the stack hack. Finds nth directory in
1270 * the directory stack, or finds last directory in stack.
1277 struct directory
*dp
;
1280 if (cnt
< 0) { /* < 0 ==> last dir requested. */
1294 (void) Strncpy(s
, dp
->di_name
, BUFSIZE
);
1295 s
[BUFSIZE
- 1] = '\0';
1300 * Karl Kleinpaste - 10 Feb 1984
1301 * Added dextract(), which is used in pushd +n.
1302 * Instead of just rotating the entire stack around, dextract()
1303 * lets the user have the nth dir extracted from its current
1304 * position, and pushes it onto the top.
1308 struct directory
*dp
;
1312 dp
->di_next
->di_prev
= dp
->di_prev
;
1313 dp
->di_prev
->di_next
= dp
->di_next
;
1314 dp
->di_next
= dcwd
->di_next
;
1316 dp
->di_next
->di_prev
= dp
;
1324 static Char
*loaddirs_cmd
[] = { STRsource
, NULL
, NULL
};
1328 loaddirs_cmd
[1] = fname
;
1329 else if ((fname
= varval(STRdirsfile
)) != STRNULL
)
1330 loaddirs_cmd
[1] = fname
;
1332 loaddirs_cmd
[1] = STRtildotdirs
;
1333 dosource(loaddirs_cmd
, (struct command
*)0);
1338 * create a file called ~/.cshdirs which has a sequence
1339 * of pushd commands which will restore the dir stack to
1340 * its state before exit/logout. remember that the order
1341 * is reversed in the file because we are pushing.
1349 int fp
, ftmp
, oldidfds
;
1351 extern struct directory
*dcwd
;
1352 struct directory
*dp
;
1355 Char qname
[MAXPATHLEN
*2];
1357 if (fname
== NULL
&& !def
)
1360 if (fname
== NULL
) {
1361 if ((fname
= varval(STRdirsfile
)) == STRNULL
)
1362 fname
= Strspl(varval(STRhome
), &STRtildotdirs
[1]);
1364 fname
= Strsave(fname
);
1367 fname
= globone(fname
, G_ERROR
);
1369 if ((fp
= creat(short2str(fname
), 0600)) == -1) {
1370 xfree((ptr_t
) fname
);
1374 if ((snum
= varval(STRsavedirs
)) == STRNULL
|| snum
[0] == '\0')
1375 num
= (unsigned int) ~0;
1377 num
= (unsigned int) atoi(short2str(snum
));
1391 xprintf("cd %S\n", quote_meta(qname
, dp
->di_name
));
1394 xprintf("pushd %S\n", quote_meta(qname
, dp
->di_name
));
1399 } while ((dp
= dp
->di_next
) != dcwd
->di_next
);
1404 xfree((ptr_t
) fname
);