* Copyright (c) 1980, 1991 The Regents of the University of California.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* PATCHES MAGIC LEVEL PATCH THAT GOT US HERE
* -------------------- ----- ----------------------
* CURRENT PATCH LEVEL: 1 00013
* -------------------- ----- ----------------------
* 19 Aug 92 Kevin Lahey Fixed csh segmentation violation
static char sccsid
[] = "@(#)dir.c 5.12 (Berkeley) 6/27/91";
/* Directory management. */
static Char
*dfollow
__P((Char
*));
static void printdirs
__P((void));
static Char
*dgoto
__P((Char
*));
static void dnewcwd
__P((struct directory
*));
static void dset
__P((Char
*));
struct directory dhead
; /* "head" of loop */
int printd
; /* force name to be printed */
* dinit - initialize current working directory
register struct directory
*dp
;
static char *emsg
= "csh: Trying to start from \"%s\"\n";
/* Don't believe the login shell home, because it may be a symlink */
tcp
= getwd(path
); /* see ngetwd.c for System V version */
if (tcp
== NULL
|| *tcp
== '\0') {
(void) xprintf("csh: %s\n", path
);
(void) xprintf(emsg
, tcp
);
(void) xprintf(emsg
, "/");
/* I am not even try to print an error message! */
* See if $HOME is the working directory we got and use that
stat(tcp
, &swd
) != -1 && stat(short2str(hp
), &shp
) != -1 &&
swd
.st_dev
== shp
.st_dev
&& swd
.st_ino
== shp
.st_ino
)
* use PWD if we have it (for subshells)
if (cwd
= getenv("PWD")) {
if (stat(cwd
, &shp
) != -1 && swd
.st_dev
== shp
.st_dev
&&
swd
.st_ino
== shp
.st_ino
)
/* EWS: dcanon frees its argument; can't just use str2short */
cp
= dcanon(Strsave(str2short(tcp
)), STRNULL
);
dp
= (struct directory
*) xcalloc(sizeof(struct directory
), 1);
dp
->di_name
= Strsave(cp
);
dhead
.di_next
= dhead
.di_prev
= dp
;
dp
->di_next
= dp
->di_prev
= &dhead
;
* Don't call set() directly cause if the directory contains ` or
* other junk characters glob will fail.
register Char
**vec
= (Char
**) xmalloc((size_t) (2 * sizeof(Char
**)));
setq(STRcwd
, vec
, &shvhed
);
for (n
++; *n
!= NULL
&& (*n
)[0] == '-'; n
++)
for (s
= &((*n
)[1]); *s
; s
++)
stderror(ERR_DIRUS
, short2str(**v
), str
);
* dodirs - list all directories in directory loop
stderror(ERR_DIRUS
, "dirs", "");
register struct directory
*dp
;
Char
*s
, *hp
= value(STRhome
);
if (dirflag
& DIR_VERT
) {
if (!(dirflag
& DIR_LONG
) && hp
!= NULL
&& !eq(hp
, STRslash
) &&
len
= Strlen(s
= (dp
->di_name
+ Strlen(hp
))) + 2;
len
= Strlen(s
= dp
->di_name
) + 1;
if ((dirflag
& DIR_LINE
) && cur
>= 80 - 1 && len
< 80) {
xprintf(s
!= dp
->di_name
? "~%s%c" : "%s%c",
short2str(s
), (dirflag
& DIR_VERT
) ? '\n' : ' ');
} while ((dp
= dp
->di_prev
) != dcwd
);
if (!(dirflag
& DIR_VERT
))
register Char
*home
, *dir
;
if (!eq(home
, STRslash
) && prefix(home
, dir
))
xprintf("~%s", short2str(dir
+ Strlen(home
)));
xprintf("%s", short2str(dir
));
struct directory
*d
= dcwd
;
d
->di_name
= dcanon(d
->di_name
, STRNULL
);
} while ((d
= d
->di_prev
) != dcwd
);
* If the name starts with . or .. then we might need to normalize
* it depending on the symbolic link flags
#define UC (unsigned char)
#define ISDOT(c) (UC(c)[0] == '.' && ((UC(c)[1] == '\0') || (UC(c)[1] == '/')))
#define ISDOTDOT(c) (UC(c)[0] == '.' && ISDOT(&((c)[1])))
if ((unsigned char) cp
[0] == '/')
if (adrof(STRignore_symlinks
)) {
cwd
= (Char
*) xmalloc((size_t) ((Strlen(dcwd
->di_name
) + 3) *
(void) Strcpy(cwd
, dcwd
->di_name
);
* Ignore . and count ..'s
if ((dp
= Strrchr(cwd
, '/'))) {
cwd
[dotdot
= Strlen(cwd
)] = '/';
* dochngd - implement chdir command.
register struct directory
*dp
;
skipargs(&v
, " [<dir>]");
if ((cp
= value(STRhome
)) == NULL
|| *cp
== 0)
stderror(ERR_NAME
| ERR_NOHOMEDIR
);
if (chdir(short2str(cp
)) < 0)
stderror(ERR_NAME
| ERR_CANTCHANGE
);
stderror(ERR_NAME
| ERR_TOOMANY
);
else if ((dp
= dfind(*v
)) != 0) {
if (chdir(tmp
= short2str(dp
->di_name
)) < 0)
stderror(ERR_SYSTEM
, tmp
, strerror(errno
));
dcwd
->di_prev
->di_next
= dcwd
->di_next
;
dcwd
->di_next
->di_prev
= dcwd
->di_prev
;
dp
= (struct directory
*) xcalloc(sizeof(struct directory
), 1);
dp
->di_next
= dcwd
->di_next
;
dp
->di_prev
= dcwd
->di_prev
;
dp
->di_prev
->di_next
= dp
;
dp
->di_next
->di_prev
= dp
;
for (p
= dcwd
->di_name
; *p
++;);
if ((cwdlen
= p
- dcwd
->di_name
- 1) == 1) /* root */
dp
= (Char
*) xmalloc((size_t)((cwdlen
+ (p
- cp
) + 1) * sizeof(Char
)));
for (p
= dp
, q
= dcwd
->di_name
; *p
++ = *q
++;);
p
--; /* don't add a / after root */
for (q
= cp
; *p
++ = *q
++;);
* dfollow - change to arg directory; fall back on cdpath if not valid
cp
= globone(cp
, G_ERROR
);
* if we are ignoring symlinks, try to fix relatives now.
if (chdir(short2str(dp
)) >= 0) {
if (chdir(short2str(cp
)) >= 0)
if (cp
[0] != '/' && !prefix(STRdotsl
, cp
) && !prefix(STRdotdotsl
, cp
)
&& (c
= adrof(STRcdpath
))) {
for (cdp
= c
->vec
; *cdp
; cdp
++) {
for (dp
= buf
, p
= *cdp
; *dp
++ = *p
++;);
for (p
= cp
; *dp
++ = *p
++;);
if (chdir(short2str(buf
)) >= 0) {
if ((dp
[0] == '/' || dp
[0] == '.') && chdir(short2str(dp
)) >= 0) {
(void) strcpy(ebuf
, short2str(cp
));
stderror(ERR_SYSTEM
, ebuf
, strerror(serrno
));
* dopushd - push new directory onto directory stack.
* with no arguments exchange top and second.
* with numeric argument (+n) bring it to top.
register struct directory
*dp
;
skipargs(&v
, " [<dir>|+<n>]");
if ((dp
= dcwd
->di_prev
) == &dhead
)
stderror(ERR_NAME
| ERR_NODIR
);
if (chdir(tmp
= short2str(dp
->di_name
)) < 0)
stderror(ERR_SYSTEM
, tmp
, strerror(errno
));
dp
->di_prev
->di_next
= dp
->di_next
;
dp
->di_next
->di_prev
= dp
->di_prev
;
dp
->di_next
= dcwd
->di_next
;
dcwd
->di_next
->di_prev
= dp
;
stderror(ERR_NAME
| ERR_TOOMANY
);
else if (dp
= dfind(*v
)) {
if (chdir(tmp
= short2str(dp
->di_name
)) < 0)
stderror(ERR_SYSTEM
, tmp
, strerror(errno
));
dp
= (struct directory
*) xcalloc(sizeof(struct directory
), 1);
dp
->di_next
= dcwd
->di_next
;
dp
->di_next
->di_prev
= dp
;
* dfind - find a directory if specified by numeric (+n) argument
static struct directory
*
register struct directory
*dp
;
for (ep
= cp
; Isdigit(*ep
); ep
++)
for (dp
= dcwd
; i
!= 0; i
--) {
if ((dp
= dp
->di_prev
) == &dhead
)
stderror(ERR_NAME
| ERR_DEEP
);
* dopopd - pop a directory out of the directory stack
* with a numeric argument just discard it.
register struct directory
*dp
, *p
= NULL
;
stderror(ERR_NAME
| ERR_TOOMANY
);
else if ((dp
= dfind(*v
)) == 0)
stderror(ERR_NAME
| ERR_BADDIR
);
if (dp
->di_prev
== &dhead
&& dp
->di_next
== &dhead
)
stderror(ERR_NAME
| ERR_EMPTY
);
if ((p
= dp
->di_prev
) == &dhead
)
if (chdir(tmp
= short2str(p
->di_name
)) < 0)
stderror(ERR_SYSTEM
, tmp
, strerror(errno
));
dp
->di_prev
->di_next
= dp
->di_next
;
dp
->di_next
->di_prev
= dp
->di_prev
;
* dfree - free the directory (or keep it if it still has ref count)
register struct directory
*dp
;
dp
->di_next
= dp
->di_prev
= 0;
xfree((char *) dp
->di_name
);
* dcanon - canonicalize the pathname, removing excess ./ and ../ etc.
* we are of course assuming that the file system is standardly
* constructed (always have ..'s, directories have links)
register Char
*p1
, *p2
; /* general purpose */
* christos: if the path given does not start with a slash prepend cwd. If
* cwd does not start with a path or the result would be too long abort().
if (p1
== NULL
|| *p1
!= '/')
if (Strlen(p1
) + Strlen(cp
) + 1 >= MAXPATHLEN
)
(void) Strcpy(tmpdir
, p1
);
(void) Strcat(tmpdir
, STRslash
);
(void) Strcat(tmpdir
, cp
);
cp
= p
= Strsave(tmpdir
);
while (*p
) { /* for each component */
sp
= p
; /* save slash address */
while (*++p
== '/') /* flush extra slashes */
for (p1
= sp
, p2
= p
; *p1
++ = *p2
++;);
p
= sp
; /* save start of component */
while (*++p
) /* find next slash or end of path */
if (*sp
== '\0') /* if component is null */
if (--sp
== cp
) /* if path is one char (i.e. /) */
else if (sp
[0] == '.' && sp
[1] == 0) {
for (p1
= sp
, p2
= p
+ 1; *p1
++ = *p2
++;);
else if (sp
[0] == '.' && sp
[1] == '.' && sp
[2] == 0) {
* We have something like "yyy/xxx/..", where "yyy" can be null or
* a path starting at /, and "xxx" is a single component. Before
* compressing "xxx/..", we want to expand "yyy/xxx", if it is a
*--sp
= 0; /* form the pathname for readlink */
if (sp
!= cp
&& !adrof(STRignore_symlinks
) &&
(cc
= readlink(short2str(cp
), tlink
,
(void) Strcpy(link
, str2short(tlink
));
* Point p to the '/' in "/..", and restore the '/'.
* Relative path, expand it between the "yyy/" and the
* "/..". First, back sp up to the character past "yyy/".
* New length is "yyy/" + link + "/.." and rest
p1
= newcp
= (Char
*) xmalloc((size_t)
(((sp
- cp
) + cc
+ (p1
- p
)) *
* Copy new path into newcp
for (p2
= cp
; *p1
++ = *p2
++;);
for (p1
--, p2
= link
; *p1
++ = *p2
++;);
for (p1
--, p2
= p
; *p1
++ = *p2
++;);
* Restart canonicalization at expanded "/xxx".
* New length is link + "/.." and rest
p1
= newcp
= (Char
*) xmalloc((size_t)
((cc
+ (p1
- p
)) * sizeof(Char
)));
* Copy new path into newcp
for (p2
= link
; *p1
++ = *p2
++;);
for (p1
--, p2
= p
; *p1
++ = *p2
++;);
* Restart canonicalization at beginning
continue; /* canonicalize the link */
for (p1
= sp
+ 1, p2
= p
+ 1; *p1
++ = *p2
++;);
else { /* normal dir name (not . or .. or nothing) */
if (sp
!= cp
&& adrof(STRchase_symlinks
) &&
!adrof(STRignore_symlinks
) &&
(cc
= readlink(short2str(cp
), tlink
,
(void) Strcpy(link
, str2short(tlink
));
* point sp to p (rather than backing up).
* Relative path, expand it between the "yyy/" and the
* remainder. First, back sp up to the character past
* New length is "yyy/" + link + "/.." and rest
p1
= newcp
= (Char
*) xmalloc((size_t)
(((sp
- cp
) + cc
+ (p1
- p
))
* Copy new path into newcp
for (p2
= cp
; *p1
++ = *p2
++;);
for (p1
--, p2
= link
; *p1
++ = *p2
++;);
for (p1
--, p2
= p
; *p1
++ = *p2
++;);
* Restart canonicalization at expanded "/xxx".
* New length is link + the rest
p1
= newcp
= (Char
*) xmalloc((size_t)
((cc
+ (p1
- p
)) * sizeof(Char
)));
* Copy new path into newcp
for (p2
= link
; *p1
++ = *p2
++;);
for (p1
--, p2
= p
; *p1
++ = *p2
++;);
* Restart canonicalization at beginning
continue; /* canonicalize the link */
* See if we're not in a subdir of STRhome
(Strncmp(p1
, cp
, cc
) != 0 || (cp
[cc
] != '/' && cp
[cc
] != '\0'))) {
static ino_t home_ino
= -1;
static dev_t home_dev
= -1;
static Char
*home_ptr
= NULL
;
* Get dev and ino of STRhome
stat(short2str(p1
), &statbuf
) != -1) {
home_dev
= statbuf
.st_dev
;
home_ino
= statbuf
.st_ino
;
* Start comparing dev & ino backwards
for (sp
= NULL
; *p2
&& stat(short2str(p2
), &statbuf
) != -1;) {
if (statbuf
.st_dev
== home_dev
&&
statbuf
.st_ino
== home_ino
) {
if (sp
= Strrchr(p2
, '/'))
if (*p2
&& sp
== (Char
*) -1) {
* Use STRhome to make '~' work
sp
= newcp
= (Char
*) xmalloc((size_t)
/* 19 Sep 92*/ ((cc
+ Strlen(p2
) + 1) * sizeof(Char
)));
* dnewcwd - make a new directory in the loop the current one
register struct directory
*dp
;
if (printd
&& !(adrof(STRpushdsilent
)))