1 /* $NetBSD: dir.c,v 1.30 2013/07/16 17:47:43 christos Exp $ */
4 * Copyright (c) 1980, 1991, 1993
5 * The Regents of the University of California. All rights reserved.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 #include <sys/cdefs.h>
35 static char sccsid
[] = "@(#)dir.c 8.1 (Berkeley) 5/31/93";
37 __RCSID("$NetBSD: dir.c,v 1.30 2013/07/16 17:47:43 christos Exp $");
41 #include <sys/param.h>
54 /* Directory management. */
56 static struct directory
*dfind(Char
*);
57 static Char
*dfollow(Char
*);
58 static void printdirs(void);
59 static Char
*dgoto(Char
*);
60 static void skipargs(Char
***, const char *);
61 static void dnewcwd(struct directory
*);
62 static void dset(Char
*);
64 struct directory dhead
; /* "head" of loop */
65 int printd
; /* force name to be printed */
67 static int dirflag
= 0;
70 * dinit - initialize current working directory
75 static const char emsg
[] = "csh: Trying to start from \"%s\"\n";
76 char path
[MAXPATHLEN
];
81 /* Don't believe the login shell home, because it may be a symlink */
82 ecp
= getcwd(path
, MAXPATHLEN
);
83 if (ecp
== NULL
|| *ecp
== '\0') {
84 (void)fprintf(csherr
, "csh: %s\n", strerror(errno
));
91 (void)fprintf(csherr
, emsg
, vis_str(hp
));
96 (void)fprintf(csherr
, emsg
, "/");
97 if (chdir("/") == -1) {
98 /* I am not even try to print an error message! */
105 struct stat swd
, shp
;
108 * See if $HOME is the working directory we got and use that
111 stat(ecp
, &swd
) != -1 && stat(short2str(hp
), &shp
) != -1 &&
112 swd
.st_dev
== shp
.st_dev
&& swd
.st_ino
== shp
.st_ino
)
118 * use PWD if we have it (for subshells)
120 if ((cwd
= getenv("PWD")) != NULL
) {
121 if (stat(cwd
, &shp
) != -1 && swd
.st_dev
== shp
.st_dev
&&
122 swd
.st_ino
== shp
.st_ino
)
125 cp
= dcanon(SAVE(ecp
), STRNULL
);
129 dp
= (struct directory
*)xcalloc(1, sizeof(struct directory
));
132 dhead
.di_next
= dhead
.di_prev
= dp
;
133 dp
->di_next
= dp
->di_prev
= &dhead
;
144 * Don't call set() directly cause if the directory contains ` or
145 * other junk characters glob will fail.
148 vec
= xmalloc((size_t)(2 * sizeof(Char
**)));
149 vec
[0] = Strsave(dp
);
151 setq(STRcwd
, vec
, &shvhed
);
160 skipargs(Char
***v
, const char *str
)
166 for (n
++; *n
!= NULL
&& (*n
)[0] == '-'; n
++)
167 for (s
= &((*n
)[1]); *s
; s
++)
179 stderror(ERR_DIRUS
, vis_str(**v
), str
);
186 * dodirs - list all directories in directory loop
190 dodirs(Char
**v
, struct command
*t
)
195 stderror(ERR_DIRUS
, "dirs", "");
202 struct directory
*dp
;
204 size_t cur
, idx
, len
;
215 if (dirflag
& DIR_VERT
) {
216 (void)fprintf(cshout
, "%zu\t", idx
++);
219 if (!(dirflag
& DIR_LONG
) && hp
!= NULL
&& !eq(hp
, STRslash
) &&
220 (len
= Strlen(hp
), Strncmp(hp
, dp
->di_name
, len
) == 0) &&
221 (dp
->di_name
[len
] == '\0' || dp
->di_name
[len
] == '/'))
222 len
= Strlen(s
= (dp
->di_name
+ len
)) + 2;
224 len
= Strlen(s
= dp
->di_name
) + 1;
227 if ((dirflag
& DIR_LINE
) && cur
>= 80 - 1 && len
< 80) {
228 (void)fprintf(cshout
, "\n");
231 (void) fprintf(cshout
, "%s%s%c", (s
!= dp
->di_name
)? "~" : "",
232 vis_str(s
), (dirflag
& DIR_VERT
) ? '\n' : ' ');
233 } while ((dp
= dp
->di_prev
) != dcwd
);
234 if (!(dirflag
& DIR_VERT
))
235 (void)fprintf(cshout
, "\n");
239 dtildepr(Char
*home
, Char
*dir
)
241 if (!eq(home
, STRslash
) && prefix(home
, dir
))
242 (void)fprintf(cshout
, "~%s", vis_str(dir
+ Strlen(home
)));
244 (void)fprintf(cshout
, "%s", vis_str(dir
));
256 d
->di_name
= dcanon(d
->di_name
, STRNULL
);
257 } while ((d
= d
->di_prev
) != dcwd
);
264 * If the name starts with . or .. then we might need to normalize
265 * it depending on the symbolic link flags
270 #define UC (unsigned char)
271 #define ISDOT(c) (UC(c)[0] == '.' && ((UC(c)[1] == '\0') || (UC(c)[1] == '/')))
272 #define ISDOTDOT(c) (UC(c)[0] == '.' && ISDOT(&((c)[1])))
273 if ((unsigned char) cp
[0] == '/')
274 return (Strsave(cp
));
276 if (adrof(STRignore_symlinks
)) {
280 cwd
= xmalloc((size_t)((Strlen(dcwd
->di_name
) + 3) *
282 (void)Strcpy(cwd
, dcwd
->di_name
);
285 * Ignore . and count ..'s
292 else if (ISDOTDOT(cp
)) {
302 dp
= Strrchr(cwd
, '/');
312 cwd
[dotdot
= Strlen(cwd
)] = '/';
313 cwd
[dotdot
+ 1] = '\0';
314 dp
= Strspl(cwd
, cp
);
330 * dochngd - implement chdir command.
334 dochngd(Char
**v
, struct command
*t
)
336 struct directory
*dp
;
339 skipargs(&v
, " [<dir>]");
342 if ((cp
= value(STRhome
)) == NULL
|| *cp
== 0)
343 stderror(ERR_NAME
| ERR_NOHOMEDIR
);
344 if (chdir(short2str(cp
)) < 0)
345 stderror(ERR_NAME
| ERR_CANTCHANGE
);
348 else if (v
[1] != NULL
)
349 stderror(ERR_NAME
| ERR_TOOMANY
);
350 else if ((dp
= dfind(*v
)) != 0) {
354 if (chdir(tmp
= short2str(dp
->di_name
)) < 0)
355 stderror(ERR_SYSTEM
, tmp
, strerror(errno
));
356 dcwd
->di_prev
->di_next
= dcwd
->di_next
;
357 dcwd
->di_next
->di_prev
= dcwd
->di_prev
;
364 dp
= (struct directory
*)xcalloc(1, sizeof(struct directory
));
367 dp
->di_next
= dcwd
->di_next
;
368 dp
->di_prev
= dcwd
->di_prev
;
369 dp
->di_prev
->di_next
= dp
;
370 dp
->di_next
->di_prev
= dp
;
384 for (p
= dcwd
->di_name
; *p
++;)
386 if ((cwdlen
= (size_t)(p
- dcwd
->di_name
- 1)) == 1) /* root */
390 dp
= xmalloc((size_t)(cwdlen
+ (size_t)(p
- cp
) + 1) * sizeof(Char
));
391 for (p
= dp
, q
= dcwd
->di_name
; (*p
++ = *q
++) != '\0';)
396 p
--; /* don't add a / after root */
397 for (q
= cp
; (*p
++ = *q
++) != '\0';)
411 * dfollow - change to arg directory; fall back on cdpath if not valid
416 char ebuf
[MAXPATHLEN
];
421 cp
= globone(cp
, G_ERROR
);
423 * if we are ignoring symlinks, try to fix relatives now.
426 if (chdir(short2str(dp
)) >= 0) {
432 if (chdir(short2str(cp
)) >= 0)
437 if (cp
[0] != '/' && !prefix(STRdotsl
, cp
) && !prefix(STRdotdotsl
, cp
)
438 && (c
= adrof(STRcdpath
))) {
441 Char buf
[MAXPATHLEN
];
443 for (cdp
= c
->vec
; *cdp
; cdp
++) {
444 for (dp
= buf
, p
= *cdp
; (*dp
++ = *p
++) != '\0';)
447 for (p
= cp
; (*dp
++ = *p
++) != '\0';)
449 if (chdir(short2str(buf
)) >= 0) {
458 if ((dp
[0] == '/' || dp
[0] == '.') && chdir(short2str(dp
)) >= 0) {
464 (void)strcpy(ebuf
, short2str(cp
));
466 stderror(ERR_SYSTEM
, ebuf
, strerror(serrno
));
471 * dopushd - push new directory onto directory stack.
472 * with no arguments exchange top and second.
473 * with numeric argument (+n) bring it to top.
477 dopushd(Char
**v
, struct command
*t
)
479 struct directory
*dp
;
481 skipargs(&v
, " [<dir>|+<n>]");
486 if ((dp
= dcwd
->di_prev
) == &dhead
)
489 stderror(ERR_NAME
| ERR_NODIR
);
490 if (chdir(tmp
= short2str(dp
->di_name
)) < 0)
491 stderror(ERR_SYSTEM
, tmp
, strerror(errno
));
492 dp
->di_prev
->di_next
= dp
->di_next
;
493 dp
->di_next
->di_prev
= dp
->di_prev
;
494 dp
->di_next
= dcwd
->di_next
;
496 dcwd
->di_next
->di_prev
= dp
;
499 else if (v
[1] != NULL
)
500 stderror(ERR_NAME
| ERR_TOOMANY
);
501 else if ((dp
= dfind(*v
)) != NULL
) {
504 if (chdir(tmp
= short2str(dp
->di_name
)) < 0)
505 stderror(ERR_SYSTEM
, tmp
, strerror(errno
));
511 dp
= (struct directory
*)xcalloc(1, sizeof(struct directory
));
515 dp
->di_next
= dcwd
->di_next
;
517 dp
->di_next
->di_prev
= dp
;
523 * dfind - find a directory if specified by numeric (+n) argument
525 static struct directory
*
528 struct directory
*dp
;
534 for (ep
= cp
; Isdigit(*ep
); ep
++)
541 for (dp
= dcwd
; i
!= 0; i
--) {
542 if ((dp
= dp
->di_prev
) == &dhead
)
545 stderror(ERR_NAME
| ERR_DEEP
);
551 * dopopd - pop a directory out of the directory stack
552 * with a numeric argument just discard it.
556 dopopd(Char
**v
, struct command
*t
)
558 struct directory
*dp
, *p
= NULL
;
560 skipargs(&v
, " [+<n>]");
564 else if (v
[1] != NULL
)
565 stderror(ERR_NAME
| ERR_TOOMANY
);
566 else if ((dp
= dfind(*v
)) == 0)
567 stderror(ERR_NAME
| ERR_BADDIR
);
568 if (dp
->di_prev
== &dhead
&& dp
->di_next
== &dhead
)
569 stderror(ERR_NAME
| ERR_EMPTY
);
573 if ((p
= dp
->di_prev
) == &dhead
)
575 if (chdir(tmp
= short2str(p
->di_name
)) < 0)
576 stderror(ERR_SYSTEM
, tmp
, strerror(errno
));
578 dp
->di_prev
->di_next
= dp
->di_next
;
579 dp
->di_next
->di_prev
= dp
->di_prev
;
589 * dfree - free the directory (or keep it if it still has ref count)
592 dfree(struct directory
*dp
)
595 if (dp
->di_count
!= 0) {
596 dp
->di_next
= dp
->di_prev
= 0;
599 xfree((char *) dp
->di_name
);
605 * dcanon - canonicalize the pathname, removing excess ./ and ../ etc.
606 * we are of course assuming that the file system is standardly
607 * constructed (always have ..'s, directories have links)
610 dcanon(Char
*cp
, Char
*p
)
612 Char slink
[MAXPATHLEN
];
613 char tlink
[MAXPATHLEN
];
615 Char
*p1
, *p2
; /* general purpose */
621 * christos: if the path given does not start with a slash prepend cwd. If
622 * cwd does not start with a path or the result would be too long abort().
625 Char tmpdir
[MAXPATHLEN
];
628 if (p1
== NULL
|| *p1
!= '/')
630 if (Strlen(p1
) + Strlen(cp
) + 1 >= MAXPATHLEN
)
632 (void)Strcpy(tmpdir
, p1
);
633 (void)Strcat(tmpdir
, STRslash
);
634 (void)Strcat(tmpdir
, cp
);
636 cp
= p
= Strsave(tmpdir
);
639 while (*p
) { /* for each component */
640 sp
= p
; /* save slash address */
641 while (*++p
== '/') /* flush extra slashes */
644 for (p1
= sp
, p2
= p
; (*p1
++ = *p2
++) != '\0';)
646 p
= sp
; /* save start of component */
648 while (*++p
) /* find next slash or end of path */
655 if (*sp
== '\0') { /* if component is null */
656 if (--sp
== cp
) /* if path is one char (i.e. /) */
660 } else if (sp
[0] == '.' && sp
[1] == 0) {
662 for (p1
= sp
, p2
= p
+ 1; (*p1
++ = *p2
++) != '\0';)
669 else if (sp
[0] == '.' && sp
[1] == '.' && sp
[2] == 0) {
671 * We have something like "yyy/xxx/..", where "yyy" can be null or
672 * a path starting at /, and "xxx" is a single component. Before
673 * compressing "xxx/..", we want to expand "yyy/xxx", if it is a
676 *--sp
= 0; /* form the pathname for readlink */
677 if (sp
!= cp
&& !adrof(STRignore_symlinks
) &&
678 (cc
= readlink(short2str(cp
), tlink
,
679 sizeof(tlink
) - 1)) >= 0) {
681 (void)Strcpy(slink
, str2short(tlink
));
686 * Point p to the '/' in "/..", and restore the '/'.
696 * Relative path, expand it between the "yyy/" and the
697 * "/..". First, back sp up to the character past "yyy/".
704 * New length is "yyy/" + slink + "/.." and rest
706 p1
= newcp
= xmalloc(
707 (size_t)((sp
- cp
) + cc
+ (p1
- p
)) * sizeof(Char
));
709 * Copy new path into newcp
711 for (p2
= cp
; (*p1
++ = *p2
++) != '\0';)
713 for (p1
--, p2
= slink
; (*p1
++ = *p2
++) != '\0';)
715 for (p1
--, p2
= p
; (*p1
++ = *p2
++) != '\0';)
718 * Restart canonicalization at expanded "/xxx".
720 p
= sp
- cp
- 1 + newcp
;
724 * New length is slink + "/.." and rest
726 p1
= newcp
= xmalloc(
727 (size_t)(cc
+ (p1
- p
)) * sizeof(Char
));
729 * Copy new path into newcp
731 for (p2
= slink
; (*p1
++ = *p2
++) != '\0';)
733 for (p1
--, p2
= p
; (*p1
++ = *p2
++) != '\0';)
736 * Restart canonicalization at beginning
742 continue; /* canonicalize the link */
749 for (p1
= sp
+ 1, p2
= p
+ 1; (*p1
++ = *p2
++) != '\0';)
758 else { /* normal dir name (not . or .. or nothing) */
760 if (sp
!= cp
&& adrof(STRchase_symlinks
) &&
761 !adrof(STRignore_symlinks
) &&
762 (cc
= readlink(short2str(cp
), tlink
, sizeof(tlink
)-1)) >= 0) {
764 (void)Strcpy(slink
, str2short(tlink
));
773 * point sp to p (rather than backing up).
784 * Relative path, expand it between the "yyy/" and the
785 * remainder. First, back sp up to the character past
793 * New length is "yyy/" + slink + "/.." and rest
795 p1
= newcp
= xmalloc(
796 (size_t)((sp
- cp
) + cc
+ (p1
- p
)) * sizeof(Char
));
798 * Copy new path into newcp
800 for (p2
= cp
; (*p1
++ = *p2
++) != '\0';)
802 for (p1
--, p2
= slink
; (*p1
++ = *p2
++) != '\0';)
804 for (p1
--, p2
= p
; (*p1
++ = *p2
++) != '\0';)
807 * Restart canonicalization at expanded "/xxx".
809 p
= sp
- cp
- 1 + newcp
;
813 * New length is slink + the rest
815 p1
= newcp
= xmalloc(
816 (size_t)(cc
+ (p1
- p
)) * sizeof(Char
));
818 * Copy new path into newcp
820 for (p2
= slink
; (*p1
++ = *p2
++) != '\0';)
822 for (p1
--, p2
= p
; (*p1
++ = *p2
++) != '\0';)
825 * Restart canonicalization at beginning
831 continue; /* canonicalize the link */
844 * See if we're not in a subdir of STRhome
846 if (p1
&& *p1
== '/' &&
847 (Strncmp(p1
, cp
, len
) != 0 || (cp
[len
] != '/' && cp
[len
] != '\0'))) {
848 static ino_t home_ino
;
849 static dev_t home_dev
= NODEV
;
850 static Char
*home_ptr
= NULL
;
854 * Get dev and ino of STRhome
856 if (home_ptr
!= p1
&&
857 stat(short2str(p1
), &statbuf
) != -1) {
858 home_dev
= statbuf
.st_dev
;
859 home_ino
= statbuf
.st_ino
;
863 * Start comparing dev & ino backwards
865 p2
= Strcpy(slink
, cp
);
866 for (sp
= NULL
; *p2
&& stat(short2str(p2
), &statbuf
) != -1;) {
867 if (statbuf
.st_dev
== home_dev
&&
868 statbuf
.st_ino
== home_ino
) {
872 if ((sp
= Strrchr(p2
, '/')) != NULL
)
878 if (*p2
&& sp
== (Char
*) -1) {
880 * Use STRhome to make '~' work
882 newcp
= Strspl(p1
, cp
+ Strlen(p2
));
892 * dnewcwd - make a new directory in the loop the current one
895 dnewcwd(struct directory
*dp
)
899 if (printd
&& !(adrof(STRpushdsilent
)))