* Copyright (c) 1980 Regents of the University of California.
* All rights reserved. The Berkeley Software License Agreement
* specifies the terms and conditions for redistribution.
static char *sccsid
= "@(#)dir.c 5.4 (Berkeley) %G%";
* C Shell - directory management
struct directory
*dfind();
struct directory dhead
; /* "head" of loop */
int printd
; /* force name to be printed */
static char *fakev
[] = { "dirs", NOSTR
};
* dinit - initialize current working directory
register struct directory
*dp
;
#define WDERR "csh: can't get current directory.\n"
(void) write(SHDIAG
, WDERR
, strlen(WDERR
));
dp
= (struct directory
*)calloc(sizeof (struct directory
), 1);
dp
->di_name
= savestr(cp
);
dhead
.di_next
= dhead
.di_prev
= dp
;
dp
->di_next
= dp
->di_prev
= &dhead
;
* dodirs - list all directories in directory loop
register struct directory
*dp
;
char *hp
= value("home");
if (eq(*v
, "-l") && *++v
== NOSTR
)
error("Usage: dirs [ -l ]");
if (!lflag
&& hp
!= NOSTR
) {
dtildepr(hp
, dp
->di_name
);
printf("%s", dp
->di_name
);
} while ((dp
= dp
->di_prev
) != dcwd
);
register char *home
, *dir
;
if (!eq(home
, "/") && prefix(home
, dir
))
printf("~%s", dir
+ strlen(home
));
* dochngd - implement chdir command.
register struct directory
*dp
;
if ((cp
= value("home")) == NOSTR
|| *cp
== 0)
bferr("No home directory");
bferr("Can't change to home directory");
} else if ((dp
= dfind(*v
)) != 0) {
if (chdir(dp
->di_name
) < 0)
dcwd
->di_prev
->di_next
= dcwd
->di_next
;
dcwd
->di_next
->di_prev
= dcwd
->di_prev
;
dp
= (struct directory
*)calloc(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
;
* dfollow - change to arg directory; fall back on cdpath if not valid
if (cp
[0] != '/' && !prefix("./", cp
) && !prefix("../", cp
)
&& (c
= adrof("cdpath"))) {
for (cdp
= c
->vec
; *cdp
; cdp
++) {
for (dp
= buf
, p
= *cdp
; *dp
++ = *p
++;)
for (p
= cp
; *dp
++ = *p
++;)
if ((dp
[0] == '/' || dp
[0] == '.') && chdir(dp
) >= 0) {
xfree(cp
); /* XXX, use after free */
* All in the name of efficiency?
for (p
= dcwd
->di_name
; *p
++;)
if ((cwdlen
= p
- dcwd
->di_name
- 1) == 1) /* root */
dp
= xalloc((unsigned) (cwdlen
+ (p
- cp
) + 1));
for (p
= dp
, q
= dcwd
->di_name
; *p
++ = *q
++;)
p
--; /* don't add a / after root */
for (q
= cp
; *p
++ = *q
++;)
* 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
;
if ((dp
= dcwd
->di_prev
) == &dhead
)
bferr("No other directory");
if (chdir(dp
->di_name
) < 0)
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
;
} else if (dp
= dfind(*v
)) {
if (chdir(dp
->di_name
) < 0)
dp
= (struct directory
*)calloc(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
register struct directory
*dp
;
for (ep
= cp
; digit(*ep
); ep
++)
for (dp
= dcwd
; i
!= 0; i
--) {
if ((dp
= dp
->di_prev
) == &dhead
)
bferr("Directory stack not that deep");
* dopopd - pop a directory out of the directory stack
* with a numeric argument just discard it.
register struct directory
*dp
, *p
;
else if ((dp
= dfind(*v
)) == 0)
if (dp
->di_prev
== &dhead
&& dp
->di_next
== &dhead
)
bferr("Directory stack empty");
if ((p
= dp
->di_prev
) == &dhead
)
if (chdir(p
->di_name
) < 0)
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(dp
->di_name
), xfree((char *)dp
);
* 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 */
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"
* Before compressing "xxx/..", we want to expand
* "yyy/xxx", if it is a symbolic link.
*--sp
= 0; /* form the pathname for readlink */
(cc
= readlink(cp
, link
, sizeof link
)) >= 0) {
* Point p to the '/' in "/..", and restore
* Relative path, expand it between
* the "yyy/" and the "/..".
* First, back sp up to the character
* "yyy/" + link + "/.." and rest
p1
= newcp
= xalloc((unsigned)
((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
* New length is link + "/.." and rest
p1
= newcp
= xalloc((unsigned)
* 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
++;)
* dnewcwd - make a new directory in the loop the current one
register struct directory
*dp
;
set("cwd", savestr(dcwd
->di_name
));