1 /* $Header: /src/pub/tcsh/sh.dir.c,v 3.66 2005/03/03 16:40:53 kim 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
36 RCSID("$Id: sh.dir.c,v 3.66 2005/03/03 16:40:53 kim Exp $")
39 * C Shell - directory management
42 static void dstart __P((const char *));
43 static struct directory *dfind __P((Char *));
44 static Char *dfollow __P((Char *));
45 static void printdirs __P((int));
46 static Char *dgoto __P((Char *));
47 static void dnewcwd __P((struct directory *, int));
48 static void dset __P((Char *));
49 static void dextract __P((struct directory *));
50 static int skipargs __P((Char ***, const char *,
52 static void dgetstack __P((void));
54 static struct directory dhead INIT_ZERO_STRUCT; /* "head" of loop */
55 static int printd; /* force name to be printed */
57 int bequiet = 0; /* do not print dir stack -strike */
63 xprintf(CGETS(12, 1, "%s: Trying to start from \"%s\"\n"), progname, from);
67 * dinit - initialize current working directory
76 char path[MAXPATHLEN];
78 /* Don't believe the login shell home, because it may be a symlink */
79 tcp = (char *) getcwd(path, sizeof(path));
80 if (tcp == NULL || *tcp == '\0') {
81 xprintf("%s: %s\n", progname, strerror(errno));
95 /* I am not even try to print an error message! */
102 struct stat swd, shp;
105 * See if $HOME is the working directory we got and use that
108 stat(tcp, &swd) != -1 && stat(short2str(hp), &shp) != -1 &&
109 DEV_DEV_COMPARE(swd.st_dev, shp.st_dev) &&
110 swd.st_ino == shp.st_ino)
116 * use PWD if we have it (for subshells)
118 if ((cwd = getenv("PWD")) != NULL) {
119 if (stat(cwd, &shp) != -1 &&
120 DEV_DEV_COMPARE(swd.st_dev, shp.st_dev) &&
121 swd.st_ino == shp.st_ino)
124 cp = dcanon(SAVE(tcp), STRNULL);
127 cp = dcanon(SAVE(tcp), STRNULL);
131 dp = (struct directory *) xcalloc(sizeof(struct directory), 1);
134 dhead.di_next = dhead.di_prev = dp;
135 dp->di_next = dp->di_prev = &dhead;
138 set(STRdirstack, Strsave(dp->di_name), VAR_READWRITE|VAR_NOGLOB);
146 * Don't call set() directly cause if the directory contains ` or
147 * other junk characters glob will fail.
149 set(STRowd, Strsave(varval(STRcwd)), VAR_READWRITE|VAR_NOGLOB);
150 set(STRcwd, Strsave(dp), VAR_READWRITE|VAR_NOGLOB);
155 #define DIR_PRINT 0x01 /* -p */
156 #define DIR_LONG 0x02 /* -l */
157 #define DIR_VERT 0x04 /* -v */
158 #define DIR_LINE 0x08 /* -n */
159 #define DIR_SAVE 0x10 /* -S */
160 #define DIR_LOAD 0x20 /* -L */
161 #define DIR_CLEAR 0x40 /* -c */
162 #define DIR_OLD 0x80 /* - */
165 skipargs(v, dstr, str)
172 int dflag = 0, loop = 1;
173 for (n++; loop && *n != NULL && (*n)[0] == '-'; n++)
174 if (*(s = &((*n)[1])) == '\0') /* test for bare "-" argument */
178 while (loop && *s != '\0') /* examine flags */
180 if ((p = strchr(dstr, *s++)) != NULL)
181 dflag |= (1 << (p - dstr));
183 stderror(ERR_DIRUS, short2str(**v), dstr, str);
184 loop = 0; /* break from both loops */
189 if (*n && (dflag & DIR_OLD))
190 stderror(ERR_DIRUS, short2str(**v), dstr, str);
192 /* make -l, -v, and -n imply -p */
193 if (dflag & (DIR_LONG|DIR_VERT|DIR_LINE))
199 * dodirs - list all directories in directory loop
207 static char flags[] = "plvnSLc";
208 int dflag = skipargs(&v, flags, "");
211 if ((dflag & DIR_CLEAR) != 0) {
212 struct directory *dp, *fdp;
213 for (dp = dcwd->di_next; dp != dcwd; ) {
219 dhead.di_next = dhead.di_prev = dp;
220 dp->di_next = dp->di_prev = &dhead;
222 if ((dflag & DIR_LOAD) != 0)
224 else if ((dflag & DIR_SAVE) != 0)
227 if (*v && (dflag & (DIR_SAVE|DIR_LOAD)))
230 if (*v != NULL || (dflag & DIR_OLD))
231 stderror(ERR_DIRUS, "dirs", flags, "");
232 if ((dflag & (DIR_CLEAR|DIR_LOAD|DIR_SAVE)) == 0 || (dflag & DIR_PRINT))
240 struct directory *dp;
250 if (dflag & DIR_VERT) {
251 xprintf("%d\t", idx++);
256 if (!(dflag & DIR_LONG) && (user = getusername(&s)) != NULL)
257 len = (int) (Strlen(user) + Strlen(s) + 2);
259 len = (int) (Strlen(s) + 1);
262 if ((dflag & DIR_LINE) && cur >= T_Cols - 1 && len < T_Cols) {
267 xprintf("~%S", user);
268 xprintf("%-S%c", s, (dflag & DIR_VERT) ? '\n' : ' ');
269 } while ((dp = dp->di_prev) != dcwd);
270 if (!(dflag & DIR_VERT))
279 if ((user = getusername(&dir)) != NULL)
280 xprintf("~%-S%S", user, dir);
288 struct directory *d = dcwd;
293 d->di_name = dcanon(d->di_name, STRNULL);
294 } while ((d = d->di_prev) != dcwd);
301 * The path will be normalized if it
303 * 2) or starts with "../",
304 * 3) or ends with "/..",
305 * 4) or contains the string "/../",
306 * then it will be normalized, unless those strings are quoted.
307 * Otherwise, a copy is made and sent back.
310 dnormalize(cp, expnd)
315 /* return true if dp is of the form "../xxx" or "/../xxx" */
316 #define IS_DOTDOT(sp, p) (ISDOTDOT(p) && ((p) == (sp) || *((p) - 1) == '/'))
317 #define IS_DOT(sp, p) (ISDOT(p) && ((p) == (sp) || *((p) - 1) == '/'))
322 Char *dp, *cwd, *start = cp, buf[MAXPATHLEN];
329 * count the number of "../xxx" or "xxx/../xxx" in the path
331 for (dp=start; *dp && *(dp+1); dp++)
332 if (IS_DOTDOT(start, dp))
335 * if none, we are done.
338 return (Strsave(cp));
341 * If the path doesn't exist, we are done too.
343 if (lstat(short2str(cp), &sb) != 0 && errno == ENOENT)
344 return (Strsave(cp));
347 cwd = (Char *) xmalloc((size_t) (((int) Strlen(dcwd->di_name) + 3) *
349 (void) Strcpy(cwd, dcwd->di_name);
352 * If the path starts with a slash, we are not relative to
353 * the current working directory.
355 if (ABSOLUTEP(start))
358 slashslash = cwd[0] == '/' && cwd[1] == '/';
362 * Ignore . and count ..'s
369 if (IS_DOT(start, cp)) {
373 else if (IS_DOTDOT(start, cp)) {
375 break; /* finish analyzing .././../xxx/[..] */
386 if ((dp = Strrchr(cwd, '/')) != NULL) {
397 if (!*cwd) { /* too many ..'s, starts with "/" */
407 else if (slashslash && cwd[1] == '\0') {
414 if ((TRM(cwd[(dotdot = (int) Strlen(cwd)) - 1])) != '/')
417 dp = Strspl(cwd, TRM(buf[0]) == '/' ? &buf[1] : buf);
420 if ((TRM(cwd[(dotdot = (int) Strlen(cwd)) - 1])) == '/')
421 cwd[--dotdot] = '\0';
423 /* Reduction of ".." following the stuff we collected in buf
424 * only makes sense if the directory item in buf really exists.
425 * Avoid reduction of "-I../.." (typical compiler call) to ""
426 * or "/usr/nonexistant/../bin" to "/usr/bin":
430 if (0 != stat(short2str(cwd), &exists)) {
432 return Strsave(start);
446 * dochngd - implement chdir command.
455 struct directory *dp;
456 int dflag = skipargs(&v, "plvn", "[-|<dir>]");
460 cp = (dflag & DIR_OLD) ? varval(STRowd) : *v;
463 if ((cp = varval(STRhome)) == STRNULL || *cp == 0)
464 stderror(ERR_NAME | ERR_NOHOMEDIR);
465 if (chdir(short2str(cp)) < 0)
466 stderror(ERR_NAME | ERR_CANTCHANGE);
469 else if ((dflag & DIR_OLD) == 0 && v[1] != NULL) {
470 stderror(ERR_NAME | ERR_TOOMANY);
474 else if ((dp = dfind(cp)) != 0) {
478 if (chdir(tmp = short2str(dp->di_name)) < 0)
479 stderror(ERR_SYSTEM, tmp, strerror(errno));
480 dcwd->di_prev->di_next = dcwd->di_next;
481 dcwd->di_next->di_prev = dcwd->di_prev;
487 if ((cp = dfollow(cp)) == NULL)
489 dp = (struct directory *) xcalloc(sizeof(struct directory), 1);
492 dp->di_next = dcwd->di_next;
493 dp->di_prev = dcwd->di_prev;
494 dp->di_prev->di_next = dp;
495 dp->di_next->di_prev = dp;
511 for (p = dcwd->di_name; *p++;)
513 if ((cwdlen = (int) (p - dcwd->di_name - 1)) == 1) /* root */
517 dp = (Char *) xmalloc((size_t)((cwdlen + (p - cp) + 1) * sizeof(Char)));
518 for (p = dp, q = dcwd->di_name; (*p++ = *q++) != '\0';)
523 p--; /* don't add a / after root */
524 for (q = cp; (*p++ = *q++) != '\0';)
533 #if defined(WINNT_NATIVE)
534 cp = SAVE(getcwd(NULL, 0));
535 #elif defined(__CYGWIN__)
536 if (ABSOLUTEP(cp) && cp[1] == ':') /* Only DOS paths are treated that way */
537 cp = SAVE(getcwd(NULL, 0));
540 #else /* !WINNT_NATIVE */
542 #endif /* WINNT_NATIVE */
547 * dfollow - change to arg directory; fall back on cdpath if not valid
555 char ebuf[MAXPATHLEN];
558 cp = globone(cp, G_ERROR);
560 if (Strchr(cp, '`')) {
562 if (chdir(dptr = short2str(cp)) < 0)
563 stderror(ERR_SYSTEM, dptr, strerror(errno));
564 else if ((ptr = getcwd(ebuf, sizeof(ebuf))) && *ptr != '\0') {
566 cp = Strsave(str2short(ptr));
570 stderror(ERR_SYSTEM, dptr, ebuf);
574 (void) strncpy(ebuf, short2str(cp), MAXPATHLEN);
575 ebuf[MAXPATHLEN-1] = '\0';
577 * if we are ignoring symlinks, try to fix relatives now.
578 * if we are expading symlinks, it should be done by now.
580 dp = dnormalize(cp, symlinks == SYM_IGNORE);
581 if (chdir(short2str(dp)) >= 0) {
587 if (chdir(short2str(cp)) >= 0)
589 else if (errno != ENOENT && errno != ENOTDIR)
590 stderror(ERR_SYSTEM, ebuf, strerror(errno));
594 if (cp[0] != '/' && !prefix(STRdotsl, cp) && !prefix(STRdotdotsl, cp)
595 && (c = adrof(STRcdpath)) && c->vec != NULL) {
598 Char buf[MAXPATHLEN];
600 for (cdp = c->vec; *cdp; cdp++) {
601 for (dp = buf, p = *cdp; (*dp++ = *p++) != '\0';)
604 for (p = cp; (*dp++ = *p++) != '\0';)
607 * We always want to fix the directory here
608 * If we are normalizing symlinks
610 dp = dnormalize(buf, symlinks == SYM_IGNORE ||
611 symlinks == SYM_EXPAND);
612 if (chdir(short2str(dp)) >= 0) {
617 else if (chdir(short2str(cp)) >= 0) {
625 if ((dp[0] == '/' || dp[0] == '.') && chdir(short2str(dp)) >= 0) {
633 * on login source of ~/.cshdirs, errors are eaten. the dir stack is all
634 * directories we could get to.
637 stderror(ERR_SYSTEM, ebuf, strerror(serrno));
646 * dopushd - push new directory onto directory stack.
647 * with no arguments exchange top and second.
648 * with numeric argument (+n) bring it to top.
656 struct directory *dp;
658 int dflag = skipargs(&v, "plvn", " [-|<dir>|+<n>]");
662 cp = (dflag & DIR_OLD) ? varval(STRowd) : *v;
665 if (adrof(STRpushdtohome)) {
666 if ((cp = varval(STRhome)) == STRNULL || *cp == 0)
667 stderror(ERR_NAME | ERR_NOHOMEDIR);
668 if (chdir(short2str(cp)) < 0)
669 stderror(ERR_NAME | ERR_CANTCHANGE);
670 cp = Strsave(cp); /* hmmm... PWP */
671 if ((cp = dfollow(cp)) == NULL)
673 dp = (struct directory *) xcalloc(sizeof(struct directory), 1);
677 dp->di_next = dcwd->di_next;
679 dp->di_next->di_prev = dp;
684 if ((dp = dcwd->di_prev) == &dhead)
687 stderror(ERR_NAME | ERR_NODIR);
688 if (chdir(tmp = short2str(dp->di_name)) < 0)
689 stderror(ERR_SYSTEM, tmp, strerror(errno));
690 dp->di_prev->di_next = dp->di_next;
691 dp->di_next->di_prev = dp->di_prev;
692 dp->di_next = dcwd->di_next;
694 dcwd->di_next->di_prev = dp;
698 else if ((dflag & DIR_OLD) == 0 && v[1] != NULL) {
699 stderror(ERR_NAME | ERR_TOOMANY);
703 else if ((dp = dfind(cp)) != NULL) {
706 if (chdir(tmp = short2str(dp->di_name)) < 0)
707 stderror(ERR_SYSTEM, tmp, strerror(errno));
709 * kfk - 10 Feb 1984 - added new "extraction style" pushd +n
711 if (adrof(STRdextract))
717 if ((ccp = dfollow(cp)) == NULL)
719 dp = (struct directory *) xcalloc(sizeof(struct directory), 1);
723 dp->di_next = dcwd->di_next;
725 dp->di_next->di_prev = dp;
731 * dfind - find a directory if specified by numeric (+n) argument
733 static struct directory *
737 struct directory *dp;
743 for (ep = cp; Isdigit(*ep); ep++)
750 for (dp = dcwd; i != 0; i--) {
751 if ((dp = dp->di_prev) == &dhead)
754 stderror(ERR_NAME | ERR_DEEP);
760 * dopopd - pop a directory out of the directory stack
761 * with a numeric argument just discard it.
770 struct directory *dp, *p = NULL;
771 int dflag = skipargs(&v, "plvn", " [-|+<n>]");
775 cp = (dflag & DIR_OLD) ? varval(STRowd) : *v;
779 else if ((dflag & DIR_OLD) == 0 && v[1] != NULL) {
780 stderror(ERR_NAME | ERR_TOOMANY);
784 else if ((dp = dfind(cp)) == 0)
785 stderror(ERR_NAME | ERR_BADDIR);
786 if (dp->di_prev == &dhead && dp->di_next == &dhead)
787 stderror(ERR_NAME | ERR_EMPTY);
791 if ((p = dp->di_prev) == &dhead)
793 if (chdir(tmp = short2str(p->di_name)) < 0)
794 stderror(ERR_SYSTEM, tmp, strerror(errno));
796 dp->di_prev->di_next = dp->di_next;
797 dp->di_next->di_prev = dp->di_prev;
808 * dfree - free the directory (or keep it if it still has ref count)
812 struct directory *dp;
815 if (dp->di_count != 0) {
816 dp->di_next = dp->di_prev = 0;
819 xfree((ptr_t) dp->di_name);
825 * dcanon - canonicalize the pathname, removing excess ./ and ../ etc.
826 * we are of course assuming that the file system is standardly
827 * constructed (always have ..'s, directories have links)
834 Char *p1, *p2; /* general purpose */
841 #ifdef S_IFLNK /* if we have symlinks */
842 Char mlink[MAXPATHLEN];
843 char tlink[MAXPATHLEN];
849 * if the path given is too long truncate it!
851 if ((clen = Strlen(cp)) >= MAXPATHLEN)
852 cp[clen = MAXPATHLEN - 1] = '\0';
855 * christos: if the path given does not start with a slash prepend cwd. If
856 * cwd does not start with a slash or the result would be too long try to
859 if (!ABSOLUTEP(cp)) {
860 Char tmpdir[MAXPATHLEN];
864 if (p1 == STRNULL || !ABSOLUTEP(p1)) {
865 char *tmp = (char *)getcwd((char *)tmpdir, sizeof(tmpdir));
866 if (tmp == NULL || *tmp == '\0') {
867 xprintf("%s: %s\n", progname, strerror(errno));
868 set(STRcwd, SAVE("/"), VAR_READWRITE|VAR_NOGLOB);
870 set(STRcwd, SAVE(tmp), VAR_READWRITE|VAR_NOGLOB);
875 if (len + clen + 1 >= MAXPATHLEN)
876 cp[MAXPATHLEN - (len + 1)] = '\0';
877 (void) Strcpy(tmpdir, p1);
878 (void) Strcat(tmpdir, STRslash);
879 (void) Strcat(tmpdir, cp);
881 cp = p = Strsave(tmpdir);
885 slashslash = (cp[0] == '/' && cp[1] == '/');
888 while (*p) { /* for each component */
889 sp = p; /* save slash address */
890 while (*++p == '/') /* flush extra slashes */
893 for (p1 = sp, p2 = p; (*p1++ = *p2++) != '\0';)
895 p = sp; /* save start of component */
898 while (*++p) /* find next slash or end of path */
906 if (&cp[1] == sp && sp[0] == '.' && sp[1] == '.' && sp[2] == '\0')
909 if (*sp == '\0') { /* if component is null */
910 if (--sp == cp) /* if path is one char (i.e. /) */
915 else if (sp[0] == '.' && sp[1] == 0) {
917 for (p1 = sp, p2 = p + 1; (*p1++ = *p2++) != '\0';)
926 else if (sp[0] == '.' && sp[1] == '.' && sp[2] == 0) {
928 * We have something like "yyy/xxx/..", where "yyy" can be null or
929 * a path starting at /, and "xxx" is a single component. Before
930 * compressing "xxx/..", we want to expand "yyy/xxx", if it is a
933 *--sp = 0; /* form the pathname for readlink */
934 #ifdef S_IFLNK /* if we have symlinks */
935 if (sp != cp && /* symlinks != SYM_IGNORE && */
936 (cc = readlink(short2str(cp), tlink,
937 sizeof(tlink) - 1)) >= 0) {
939 (void) Strncpy(mlink, str2short(tlink),
940 sizeof(mlink) / sizeof(Char));
941 mlink[sizeof(mlink) / sizeof(Char) - 1] = '\0';
946 * Point p to the '/' in "/..", and restore the '/'.
956 * Relative path, expand it between the "yyy/" and the
957 * "/..". First, back sp up to the character past "yyy/".
964 * New length is "yyy/" + mlink + "/.." and rest
966 p1 = newcp = (Char *) xmalloc((size_t)
967 (((sp - cp) + cc + (p1 - p)) *
970 * Copy new path into newcp
972 for (p2 = cp; (*p1++ = *p2++) != '\0';)
974 for (p1--, p2 = mlink; (*p1++ = *p2++) != '\0';)
976 for (p1--, p2 = p; (*p1++ = *p2++) != '\0';)
979 * Restart canonicalization at expanded "/xxx".
981 p = sp - cp - 1 + newcp;
985 * New length is mlink + "/.." and rest
987 p1 = newcp = (Char *) xmalloc((size_t)
988 ((cc + (p1 - p)) * sizeof(Char)));
990 * Copy new path into newcp
992 for (p2 = mlink; (*p1++ = *p2++) != '\0';)
994 for (p1--, p2 = p; (*p1++ = *p2++) != '\0';)
997 * Restart canonicalization at beginning
1004 slashslash = (cp[0] == '/' && cp[1] == '/');
1006 continue; /* canonicalize the link */
1008 #endif /* S_IFLNK */
1011 while (*--sp != '/')
1014 for (p1 = sp + 1, p2 = p + 1; (*p1++ = *p2++) != '\0';)
1023 else { /* normal dir name (not . or .. or nothing) */
1025 #ifdef S_IFLNK /* if we have symlinks */
1026 if (sp != cp && symlinks == SYM_CHASE &&
1027 (cc = readlink(short2str(cp), tlink,
1028 sizeof(tlink) - 1)) >= 0) {
1030 (void) Strncpy(mlink, str2short(tlink),
1031 sizeof(mlink) / sizeof(Char));
1032 mlink[sizeof(mlink) / sizeof(Char) - 1] = '\0';
1041 * point sp to p (rather than backing up).
1048 for (p1 = p; *p1++;)
1050 if (*mlink != '/') {
1052 * Relative path, expand it between the "yyy/" and the
1053 * remainder. First, back sp up to the character past
1056 while (*--sp != '/')
1061 * New length is "yyy/" + mlink + "/.." and rest
1063 p1 = newcp = (Char *) xmalloc((size_t)
1064 (((sp - cp) + cc + (p1 - p))
1067 * Copy new path into newcp
1069 for (p2 = cp; (*p1++ = *p2++) != '\0';)
1071 for (p1--, p2 = mlink; (*p1++ = *p2++) != '\0';)
1073 for (p1--, p2 = p; (*p1++ = *p2++) != '\0';)
1076 * Restart canonicalization at expanded "/xxx".
1078 p = sp - cp - 1 + newcp;
1082 * New length is mlink + the rest
1084 p1 = newcp = (Char *) xmalloc((size_t)
1085 ((cc + (p1 - p)) * sizeof(Char)));
1087 * Copy new path into newcp
1089 for (p2 = mlink; (*p1++ = *p2++) != '\0';)
1091 for (p1--, p2 = p; (*p1++ = *p2++) != '\0';)
1094 * Restart canonicalization at beginning
1101 slashslash = (cp[0] == '/' && cp[1] == '/');
1103 continue; /* canonicalize the mlink */
1105 #endif /* S_IFLNK */
1115 p1 = varval(STRhome);
1116 cc = (int) Strlen(p1);
1118 * See if we're not in a subdir of STRhome
1120 if (p1 && *p1 == '/' && (Strncmp(p1, cp, (size_t) cc) != 0 ||
1121 (cp[cc] != '/' && cp[cc] != '\0'))) {
1122 static ino_t home_ino = (ino_t) -1;
1123 static dev_t home_dev = (dev_t) -1;
1124 static Char *home_ptr = NULL;
1125 struct stat statbuf;
1129 * Get dev and ino of STRhome
1131 if (home_ptr != p1 &&
1132 stat(short2str(p1), &statbuf) != -1) {
1133 home_dev = statbuf.st_dev;
1134 home_ino = statbuf.st_ino;
1138 * Start comparing dev & ino backwards
1140 p2 = Strncpy(mlink, cp, sizeof(mlink) / sizeof(Char));
1141 mlink[sizeof(mlink) / sizeof(Char) - 1] = '\0';
1143 while (*p2 && stat(short2str(p2), &statbuf) != -1) {
1144 if (DEV_DEV_COMPARE(statbuf.st_dev, home_dev) &&
1145 statbuf.st_ino == home_ino) {
1149 if ((sp = Strrchr(p2, '/')) != NULL)
1153 * See if we found it
1157 * Use STRhome to make '~' work
1159 newcp = Strspl(p1, cp + Strlen(p2));
1164 #endif /* S_IFLNK */
1169 p = (Char *) xmalloc((size_t) (Strlen(cp) + 2) * sizeof(Char));
1171 (void) Strcpy(&p[1], cp);
1176 if (cp[1] == '/' && cp[2] == '/')
1177 (void) Strcpy(&cp[1], &cp[2]);
1184 * dnewcwd - make a new directory in the loop the current one
1188 struct directory *dp;
1193 if (adrof(STRdunique)) {
1194 struct directory *dn;
1196 for (dn = dhead.di_prev; dn != &dhead; dn = dn->di_prev)
1197 if (dn != dp && Strcmp(dn->di_name, dp->di_name) == 0) {
1198 dn->di_next->di_prev = dn->di_prev;
1199 dn->di_prev->di_next = dn->di_next;
1205 dset(dcwd->di_name);
1207 print = printd; /* if printd is set, print dirstack... */
1208 if (adrof(STRpushdsilent)) /* but pushdsilent overrides printd... */
1210 if (dflag & DIR_PRINT) /* but DIR_PRINT overrides pushdsilent... */
1212 if (bequiet) /* and bequiet overrides everything */
1216 cwd_cmd(); /* PWP: run the defined cwd command */
1224 struct directory *dn, *dp;
1226 if ((vp = adrof(STRdirstack)) == NULL || vp->vec == NULL)
1229 /* Free the whole stack */
1230 while ((dn = dhead.di_prev) != &dhead) {
1231 dn->di_next->di_prev = dn->di_prev;
1232 dn->di_prev->di_next = dn->di_next;
1237 /* thread the current working directory */
1238 dhead.di_prev = dhead.di_next = dcwd;
1239 dcwd->di_next = dcwd->di_prev = &dhead;
1241 /* put back the stack */
1242 for (cp = vp->vec; cp && *cp && **cp; cp++) {
1243 dp = (struct directory *) xcalloc(sizeof(struct directory), 1);
1244 dp->di_name = Strsave(*cp);
1247 dp->di_next = dcwd->di_next;
1249 dp->di_next->di_prev = dp;
1251 dgetstack(); /* Make $dirstack reflect the current state */
1259 struct directory *dn;
1261 if (adrof(STRdirstack) == NULL)
1264 for (dn = dhead.di_prev; dn != &dhead; dn = dn->di_prev, i++)
1266 dbp = dblk = (Char**) xmalloc((size_t) (i + 1) * sizeof(Char *));
1267 for (dn = dhead.di_prev; dn != &dhead; dn = dn->di_prev, dbp++)
1268 *dbp = Strsave(dn->di_name);
1270 setq(STRdirstack, dblk, &shvhed, VAR_READWRITE);
1274 * getstakd - added by kfk 17 Jan 1984
1275 * Support routine for the stack hack. Finds nth directory in
1276 * the directory stack, or finds last directory in stack.
1283 struct directory *dp;
1286 if (cnt < 0) { /* < 0 ==> last dir requested. */
1300 (void) Strncpy(s, dp->di_name, BUFSIZE);
1301 s[BUFSIZE - 1] = '\0';
1306 * Karl Kleinpaste - 10 Feb 1984
1307 * Added dextract(), which is used in pushd +n.
1308 * Instead of just rotating the entire stack around, dextract()
1309 * lets the user have the nth dir extracted from its current
1310 * position, and pushes it onto the top.
1314 struct directory *dp;
1318 dp->di_next->di_prev = dp->di_prev;
1319 dp->di_prev->di_next = dp->di_next;
1320 dp->di_next = dcwd->di_next;
1322 dp->di_next->di_prev = dp;
1330 static Char *loaddirs_cmd[] = { STRsource, NULL, NULL };
1334 loaddirs_cmd[1] = fname;
1335 else if ((fname = varval(STRdirsfile)) != STRNULL)
1336 loaddirs_cmd[1] = fname;
1338 loaddirs_cmd[1] = STRtildotdirs;
1339 dosource(loaddirs_cmd, (struct command *)0);
1344 * create a file called ~/.cshdirs which has a sequence
1345 * of pushd commands which will restore the dir stack to
1346 * its state before exit/logout. remember that the order
1347 * is reversed in the file because we are pushing.
1355 int fp, ftmp, oldidfds;
1357 struct directory *dp;
1360 Char qname[MAXPATHLEN*2];
1362 if (fname == NULL && !def)
1365 if (fname == NULL) {
1366 if ((fname = varval(STRdirsfile)) == STRNULL)
1367 fname = Strspl(varval(STRhome), &STRtildotdirs[1]);
1369 fname = Strsave(fname);
1372 fname = globone(fname, G_ERROR);
1374 if ((fp = creat(short2str(fname), 0600)) == -1) {
1375 xfree((ptr_t) fname);
1379 if ((snum = varval(STRsavedirs)) == STRNULL || snum[0] == '\0')
1380 num = (unsigned int) ~0;
1382 num = (unsigned int) atoi(short2str(snum));
1396 xprintf("cd %S\n", quote_meta(qname, dp->di_name));
1399 xprintf("pushd %S\n", quote_meta(qname, dp->di_name));
1404 } while ((dp = dp->di_next) != dcwd->di_next);
1409 xfree((ptr_t) fname);