xref: /original-bsd/bin/csh/dir.c (revision 8d9f23e0)
1 static	char *sccsid = "@(#)dir.c 4.1 10/09/80";
2 
3 #include "sh.h"
4 #include "sh.dir.h"
5 
6 /*
7  * C Shell - directory management
8  */
9 
10 struct	directory *dfind();
11 char	*dfollow();
12 struct	directory dhead;		/* "head" of loop */
13 int	printd;				/* force name to be printed */
14 static	char *fakev[] = { "dirs", NOSTR };
15 
16 /*
17  * dinit - initialize current working directory
18  */
19 dinit(hp)
20 	char *hp;
21 {
22 	register char *cp;
23 	register struct directory *dp;
24 	char path[BUFSIZ];
25 
26 	if (loginsh && hp)
27 		cp = hp;
28 	else
29 		cp = getwd(path);
30 	dp = (struct directory *)calloc(sizeof (struct directory), 1);
31 	dp->di_name = savestr(cp);
32 	dp->di_count = 0;
33 	dhead.di_next = dhead.di_prev = dp;
34 	dp->di_next = dp->di_prev = &dhead;
35 	printd = 0;
36 	dnewcwd(dp);
37 }
38 
39 /*
40  * dodirs - list all directories in directory loop
41  */
42 dodirs(v)
43 	char **v;
44 {
45 	register struct directory *dp;
46 	bool lflag;
47 	char *hp = value("home");
48 
49 	if (*hp == '\0')
50 		hp = NOSTR;
51 	if (*++v != NOSTR)
52 		if (eq(*v, "-l") && *++v == NOSTR)
53 			lflag = 1;
54 		else
55 			error("Usage: dirs [ -l ]");
56 	else
57 		lflag = 0;
58 	dp = dcwd;
59 	do {
60 		if (dp == &dhead)
61 			continue;
62 		if (!lflag && hp != NOSTR) {
63 			dtildepr(hp, dp->di_name);
64 		} else
65 			printf("%s", dp->di_name);
66 		printf(" ");
67 	} while ((dp = dp->di_prev) != dcwd);
68 	printf("\n");
69 }
70 
71 dtildepr(home, dir)
72 	register char *home, *dir;
73 {
74 
75 	if (!eq(home, "/") && prefix(home, dir))
76 		printf("~%s", dir + strlen(home));
77 	else
78 		printf("%s", dir);
79 }
80 
81 /*
82  * dochngd - implement chdir command.
83  */
84 dochngd(v)
85 	char **v;
86 {
87 	register char *cp;
88 	register struct directory *dp;
89 
90 	printd = 0;
91 	if (*++v == NOSTR) {
92 		if ((cp = value("home")) == NOSTR || *cp == 0)
93 			bferr("No home directory");
94 		if (chdir(cp) < 0)
95 			bferr("Can't change to home directory");
96 		cp = savestr(cp);
97 	} else if ((dp = dfind(*v)) != 0) {
98 		printd = 1;
99 		if (chdir(dp->di_name) < 0)
100 			Perror(dp->di_name);
101 		dcwd->di_prev->di_next = dcwd->di_next;
102 		dcwd->di_next->di_prev = dcwd->di_prev;
103 		goto flushcwd;
104 	} else
105 		cp = dfollow(*v);
106 	dp = (struct directory *)calloc(sizeof (struct directory), 1);
107 	dp->di_name = cp;
108 	dp->di_count = 0;
109 	dp->di_next = dcwd->di_next;
110 	dp->di_prev = dcwd->di_prev;
111 	dp->di_prev->di_next = dp;
112 	dp->di_next->di_prev = dp;
113 flushcwd:
114 	dfree(dcwd);
115 	dnewcwd(dp);
116 }
117 
118 /*
119  * dfollow - change to arg directory; fall back on cdpath if not valid
120  */
121 char *
122 dfollow(cp)
123 	register char *cp;
124 {
125 	register char **cdp;
126 	struct varent *c;
127 
128 	cp = globone(cp);
129 	if (chdir(cp) == 0)
130 		goto gotcha;
131 	if (cp[0] != '/' && !prefix("./", cp) && !prefix("../", cp)
132 	    && (c = adrof("cdpath"))) {
133 		for (cdp = c->vec; *cdp; cdp++) {
134 			char buf[BUFSIZ];
135 
136 			strcpy(buf, *cdp);
137 			strcat(buf, "/");
138 			strcat(buf, cp);
139 			if (chdir(buf) >= 0) {
140 				printd = 1;
141 				xfree(cp);
142 				cp = savestr(buf);
143 				goto gotcha;
144 			}
145 		}
146 	}
147 	if (adrof(cp)) {
148 		char *dp = value(cp);
149 
150 		if (dp[0] == '/' || dp[0] == '.')
151 			if (chdir(dp) >= 0) {
152 				xfree(cp);
153 				cp = savestr(dp);
154 				printd = 1;
155 				goto gotcha;
156 			}
157 	}
158 	xfree(cp);
159 	Perror(cp);
160 
161 gotcha:
162 	if (*cp != '/') {
163 		char *dp = calloc(strlen(cp) + strlen(dcwd->di_name) + 2, 1);
164 		strcpy(dp, dcwd->di_name);
165 		strcat(dp, "/");
166 		strcat(dp, cp);
167 		xfree(cp);
168 		cp = dp;
169 	}
170 	dcanon(cp);
171 	return (cp);
172 }
173 
174 /*
175  * dopushd - push new directory onto directory stack.
176  *	with no arguments exchange top and second.
177  *	with numeric argument (+n) bring it to top.
178  */
179 dopushd(v)
180 	char **v;
181 {
182 	register struct directory *dp;
183 
184 	printd = 1;
185 	if (*++v == NOSTR) {
186 		if ((dp = dcwd->di_prev) == &dhead)
187 			dp = dhead.di_prev;
188 		if (dp == dcwd)
189 			bferr("No other directory");
190 		if (chdir(dp->di_name) < 0)
191 			Perror(dp->di_name);
192 		dp->di_prev->di_next = dp->di_next;
193 		dp->di_next->di_prev = dp->di_prev;
194 		dp->di_next = dcwd->di_next;
195 		dp->di_prev = dcwd;
196 		dcwd->di_next->di_prev = dp;
197 		dcwd->di_next = dp;
198 	} else if (dp = dfind(*v)) {
199 		if (chdir(dp->di_name) < 0)
200 			Perror(dp->di_name);
201 	} else {
202 		register char *cp;
203 
204 		cp = dfollow(*v);
205 		dp = (struct directory *)calloc(sizeof (struct directory), 1);
206 		dp->di_name = cp;
207 		dp->di_count = 0;
208 		dp->di_prev = dcwd;
209 		dp->di_next = dcwd->di_next;
210 		dcwd->di_next = dp;
211 		dp->di_next->di_prev = dp;
212 	}
213 	dnewcwd(dp);
214 }
215 
216 /*
217  * dfind - find a directory if specified by numeric (+n) argument
218  */
219 struct directory *
220 dfind(cp)
221 	register char *cp;
222 {
223 	register struct directory *dp;
224 	register int i;
225 	register char *ep;
226 
227 	if (*cp++ != '+')
228 		return (0);
229 	for (ep = cp; digit(*ep); ep++)
230 		continue;
231 	if (*ep)
232 		return (0);
233 	i = getn(cp);
234 	if (i <= 0)
235 		return (0);
236 	for (dp = dcwd; i != 0; i--) {
237 		if ((dp = dp->di_prev) == &dhead)
238 			dp = dp->di_prev;
239 		if (dp == dcwd)
240 			bferr("Directory stack not that deep");
241 	}
242 	return (dp);
243 }
244 
245 /*
246  * dopopd - pop a directory out of the directory stack
247  *	with a numeric argument just discard it.
248  */
249 dopopd(v)
250 	char **v;
251 {
252 	register struct directory *dp, *p;
253 
254 	printd = 1;
255 	if (*++v == NOSTR)
256 		dp = dcwd;
257 	else if ((dp = dfind(*v)) == 0)
258 		bferr("Bad directory");
259 	if (dp->di_prev == &dhead && dp->di_next == &dhead)
260 		bferr("Directory stack empty");
261 	if (dp == dcwd) {
262 		if ((p = dp->di_prev) == &dhead)
263 			p = dhead.di_prev;
264 		if (chdir(p->di_name) < 0)
265 			Perror(p->di_name);
266 	}
267 	dp->di_prev->di_next = dp->di_next;
268 	dp->di_next->di_prev = dp->di_prev;
269 	if (dp == dcwd)
270 		dnewcwd(p);
271 	else
272 		dodirs(fakev);
273 	dfree(dp);
274 }
275 
276 /*
277  * dfree - free the directory (or keep it if it still has ref count)
278  */
279 dfree(dp)
280 	register struct directory *dp;
281 {
282 
283 	if (dp->di_count != 0)
284 		dp->di_next = dp->di_prev = 0;
285 	else
286 		xfree(dp->di_name), xfree((char *)dp);
287 }
288 
289 /*
290  * dcanon - canonicalize the pathname, removing excess ./ and ../ etc.
291  *	we are of course assuming that the file system is standardly
292  *	constructed (always have ..'s, directories have links)
293  */
294 dcanon(cp)
295 	char *cp;
296 {
297 	register char *p, *sp;
298 	register bool slash;
299 
300 	if (*cp != '/')
301 		abort();
302 	for (p = cp; *p; ) {		/* for each component */
303 		sp = p;			/* save slash address */
304 		while(*++p == '/')	/* flush extra slashes */
305 			;
306 		if (p != ++sp)
307 			strcpy(sp, p);
308 		p = sp;			/* save start of component */
309 		slash = 0;
310 		while(*++p)		/* find next slash or end of path */
311 			if (*p == '/') {
312 				slash = 1;
313 				*p = 0;
314 				break;
315 			}
316 		if (*sp == '\0')	/* if component is null */
317 			if (--sp == cp)	/* if path is one char (i.e. /) */
318 				break;
319 			else
320 				*sp = '\0';
321 		else if (eq(".", sp)) {
322 			if (slash) {
323 				strcpy(sp, ++p);
324 				p = --sp;
325 			} else if (--sp != cp)
326 				*sp = '\0';
327 		} else if (eq("..", sp)) {
328 			if (--sp != cp)
329 				while (*--sp != '/')
330 					;
331 			if (slash) {
332 				strcpy(++sp, ++p);
333 				p = --sp;
334 			} else if (cp == sp)
335 				*++sp = '\0';
336 			else
337 				*sp = '\0';
338 		} else if (slash)
339 			*p = '/';
340 	}
341 }
342 
343 /*
344  * dnewcwd - make a new directory in the loop the current one
345  */
346 dnewcwd(dp)
347 	register struct directory *dp;
348 {
349 
350 	dcwd = dp;
351 	set("cwd", savestr(dcwd->di_name));
352 	if (printd)
353 		dodirs(fakev);
354 }
355