xref: /original-bsd/bin/csh/dir.c (revision 241757c4)
1 /*
2  * Copyright (c) 1980 Regents of the University of California.
3  * All rights reserved.  The Berkeley Software License Agreement
4  * specifies the terms and conditions for redistribution.
5  */
6 
7 #ifndef lint
8 static char *sccsid = "@(#)dir.c	5.4 (Berkeley) 04/14/88";
9 #endif
10 
11 #include "sh.h"
12 #include "sh.dir.h"
13 
14 /*
15  * C Shell - directory management
16  */
17 
18 struct	directory *dfind();
19 char	*dfollow();
20 char	*dcanon();
21 struct	directory dhead;		/* "head" of loop */
22 int	printd;				/* force name to be printed */
23 static	char *fakev[] = { "dirs", NOSTR };
24 
25 /*
26  * dinit - initialize current working directory
27  */
28 dinit(hp)
29 	char *hp;
30 {
31 	register char *cp;
32 	register struct directory *dp;
33 	char path[MAXPATHLEN];
34 
35 	if (loginsh && hp)
36 		cp = hp;
37 	else {
38 		cp = getwd(path);
39 		if (cp == NULL) {
40 #define	WDERR	"csh: can't get current directory.\n"
41 			(void) write(SHDIAG, WDERR, strlen(WDERR));
42 			exit(1);
43 		}
44 	}
45 	dp = (struct directory *)calloc(sizeof (struct directory), 1);
46 	dp->di_name = savestr(cp);
47 	dp->di_count = 0;
48 	dhead.di_next = dhead.di_prev = dp;
49 	dp->di_next = dp->di_prev = &dhead;
50 	printd = 0;
51 	dnewcwd(dp);
52 }
53 
54 /*
55  * dodirs - list all directories in directory loop
56  */
57 dodirs(v)
58 	char **v;
59 {
60 	register struct directory *dp;
61 	bool lflag;
62 	char *hp = value("home");
63 
64 	if (*hp == '\0')
65 		hp = NOSTR;
66 	if (*++v != NOSTR)
67 		if (eq(*v, "-l") && *++v == NOSTR)
68 			lflag = 1;
69 		else
70 			error("Usage: dirs [ -l ]");
71 	else
72 		lflag = 0;
73 	dp = dcwd;
74 	do {
75 		if (dp == &dhead)
76 			continue;
77 		if (!lflag && hp != NOSTR) {
78 			dtildepr(hp, dp->di_name);
79 		} else
80 			printf("%s", dp->di_name);
81 		printf(" ");
82 	} while ((dp = dp->di_prev) != dcwd);
83 	printf("\n");
84 }
85 
86 dtildepr(home, dir)
87 	register char *home, *dir;
88 {
89 
90 	if (!eq(home, "/") && prefix(home, dir))
91 		printf("~%s", dir + strlen(home));
92 	else
93 		printf("%s", dir);
94 }
95 
96 /*
97  * dochngd - implement chdir command.
98  */
99 dochngd(v)
100 	char **v;
101 {
102 	register char *cp;
103 	register struct directory *dp;
104 
105 	printd = 0;
106 	if (*++v == NOSTR) {
107 		if ((cp = value("home")) == NOSTR || *cp == 0)
108 			bferr("No home directory");
109 		if (chdir(cp) < 0)
110 			bferr("Can't change to home directory");
111 		cp = savestr(cp);
112 	} else if ((dp = dfind(*v)) != 0) {
113 		printd = 1;
114 		if (chdir(dp->di_name) < 0)
115 			Perror(dp->di_name);
116 		dcwd->di_prev->di_next = dcwd->di_next;
117 		dcwd->di_next->di_prev = dcwd->di_prev;
118 		goto flushcwd;
119 	} else
120 		cp = dfollow(*v);
121 	dp = (struct directory *)calloc(sizeof (struct directory), 1);
122 	dp->di_name = cp;
123 	dp->di_count = 0;
124 	dp->di_next = dcwd->di_next;
125 	dp->di_prev = dcwd->di_prev;
126 	dp->di_prev->di_next = dp;
127 	dp->di_next->di_prev = dp;
128 flushcwd:
129 	dfree(dcwd);
130 	dnewcwd(dp);
131 }
132 
133 /*
134  * dfollow - change to arg directory; fall back on cdpath if not valid
135  */
136 char *
137 dfollow(cp)
138 	register char *cp;
139 {
140 	register char *dp;
141 	struct varent *c;
142 
143 	cp = globone(cp);
144 	if (chdir(cp) >= 0)
145 		goto gotcha;
146 	if (cp[0] != '/' && !prefix("./", cp) && !prefix("../", cp)
147 	    && (c = adrof("cdpath"))) {
148 		char **cdp;
149 		register char *p;
150 		char buf[MAXPATHLEN];
151 
152 		for (cdp = c->vec; *cdp; cdp++) {
153 			for (dp = buf, p = *cdp; *dp++ = *p++;)
154 				;
155 			dp[-1] = '/';
156 			for (p = cp; *dp++ = *p++;)
157 				;
158 			if (chdir(buf) >= 0) {
159 				printd = 1;
160 				xfree(cp);
161 				cp = savestr(buf);
162 				goto gotcha;
163 			}
164 		}
165 	}
166 	dp = value(cp);
167 	if ((dp[0] == '/' || dp[0] == '.') && chdir(dp) >= 0) {
168 		xfree(cp);
169 		cp = savestr(dp);
170 		printd = 1;
171 		goto gotcha;
172 	}
173 	xfree(cp);			/* XXX, use after free */
174 	Perror(cp);
175 
176 gotcha:
177 	if (*cp != '/') {
178 		register char *p, *q;
179 		int cwdlen;
180 
181 		/*
182 		 * All in the name of efficiency?
183 		 */
184 		for (p = dcwd->di_name; *p++;)
185 			;
186 		if ((cwdlen = p - dcwd->di_name - 1) == 1)	/* root */
187 			cwdlen = 0;
188 		for (p = cp; *p++;)
189 			;
190 		dp = xalloc((unsigned) (cwdlen + (p - cp) + 1));
191 		for (p = dp, q = dcwd->di_name; *p++ = *q++;)
192 			;
193 		if (cwdlen)
194 			p[-1] = '/';
195 		else
196 			p--;			/* don't add a / after root */
197 		for (q = cp; *p++ = *q++;)
198 			;
199 		xfree(cp);
200 		cp = dp;
201 		dp += cwdlen;
202 	} else
203 		dp = cp;
204 	return dcanon(cp, dp);
205 }
206 
207 /*
208  * dopushd - push new directory onto directory stack.
209  *	with no arguments exchange top and second.
210  *	with numeric argument (+n) bring it to top.
211  */
212 dopushd(v)
213 	char **v;
214 {
215 	register struct directory *dp;
216 
217 	printd = 1;
218 	if (*++v == NOSTR) {
219 		if ((dp = dcwd->di_prev) == &dhead)
220 			dp = dhead.di_prev;
221 		if (dp == dcwd)
222 			bferr("No other directory");
223 		if (chdir(dp->di_name) < 0)
224 			Perror(dp->di_name);
225 		dp->di_prev->di_next = dp->di_next;
226 		dp->di_next->di_prev = dp->di_prev;
227 		dp->di_next = dcwd->di_next;
228 		dp->di_prev = dcwd;
229 		dcwd->di_next->di_prev = dp;
230 		dcwd->di_next = dp;
231 	} else if (dp = dfind(*v)) {
232 		if (chdir(dp->di_name) < 0)
233 			Perror(dp->di_name);
234 	} else {
235 		register char *cp;
236 
237 		cp = dfollow(*v);
238 		dp = (struct directory *)calloc(sizeof (struct directory), 1);
239 		dp->di_name = cp;
240 		dp->di_count = 0;
241 		dp->di_prev = dcwd;
242 		dp->di_next = dcwd->di_next;
243 		dcwd->di_next = dp;
244 		dp->di_next->di_prev = dp;
245 	}
246 	dnewcwd(dp);
247 }
248 
249 /*
250  * dfind - find a directory if specified by numeric (+n) argument
251  */
252 struct directory *
253 dfind(cp)
254 	register char *cp;
255 {
256 	register struct directory *dp;
257 	register int i;
258 	register char *ep;
259 
260 	if (*cp++ != '+')
261 		return (0);
262 	for (ep = cp; digit(*ep); ep++)
263 		continue;
264 	if (*ep)
265 		return (0);
266 	i = getn(cp);
267 	if (i <= 0)
268 		return (0);
269 	for (dp = dcwd; i != 0; i--) {
270 		if ((dp = dp->di_prev) == &dhead)
271 			dp = dp->di_prev;
272 		if (dp == dcwd)
273 			bferr("Directory stack not that deep");
274 	}
275 	return (dp);
276 }
277 
278 /*
279  * dopopd - pop a directory out of the directory stack
280  *	with a numeric argument just discard it.
281  */
282 dopopd(v)
283 	char **v;
284 {
285 	register struct directory *dp, *p;
286 
287 	printd = 1;
288 	if (*++v == NOSTR)
289 		dp = dcwd;
290 	else if ((dp = dfind(*v)) == 0)
291 		bferr("Bad directory");
292 	if (dp->di_prev == &dhead && dp->di_next == &dhead)
293 		bferr("Directory stack empty");
294 	if (dp == dcwd) {
295 		if ((p = dp->di_prev) == &dhead)
296 			p = dhead.di_prev;
297 		if (chdir(p->di_name) < 0)
298 			Perror(p->di_name);
299 	}
300 	dp->di_prev->di_next = dp->di_next;
301 	dp->di_next->di_prev = dp->di_prev;
302 	if (dp == dcwd)
303 		dnewcwd(p);
304 	else
305 		dodirs(fakev);
306 	dfree(dp);
307 }
308 
309 /*
310  * dfree - free the directory (or keep it if it still has ref count)
311  */
312 dfree(dp)
313 	register struct directory *dp;
314 {
315 
316 	if (dp->di_count != 0)
317 		dp->di_next = dp->di_prev = 0;
318 	else
319 		xfree(dp->di_name), xfree((char *)dp);
320 }
321 
322 /*
323  * dcanon - canonicalize the pathname, removing excess ./ and ../ etc.
324  *	we are of course assuming that the file system is standardly
325  *	constructed (always have ..'s, directories have links)
326  */
327 char *
328 dcanon(cp, p)
329 	register char *cp, *p;
330 {
331 	register char *sp;
332 	register char *p1, *p2;		/* general purpose */
333 	bool slash;
334 
335 	if (*cp != '/')
336 		abort();
337 	while (*p) {			/* for each component */
338 		sp = p;			/* save slash address */
339 		while (*++p == '/')	/* flush extra slashes */
340 			;
341 		if (p != ++sp)
342 			for (p1 = sp, p2 = p; *p1++ = *p2++;)
343 				;
344 		p = sp;			/* save start of component */
345 		slash = 0;
346 		while (*++p)		/* find next slash or end of path */
347 			if (*p == '/') {
348 				slash = 1;
349 				*p = 0;
350 				break;
351 			}
352 		if (*sp == '\0')	/* if component is null */
353 			if (--sp == cp)	/* if path is one char (i.e. /) */
354 				break;
355 			else
356 				*sp = '\0';
357 		else if (sp[0] == '.' && sp[1] == 0) {
358 			if (slash) {
359 				for (p1 = sp, p2 = p + 1; *p1++ = *p2++;)
360 					;
361 				p = --sp;
362 			} else if (--sp != cp)
363 				*sp = '\0';
364 		} else if (sp[0] == '.' && sp[1] == '.' && sp[2] == 0) {
365 			char link[MAXPATHLEN];
366 			int cc;
367 			char *newcp;
368 
369 			/*
370 			 * We have something like "yyy/xxx/..", where "yyy"
371 			 * can be null or a path starting at /, and "xxx"
372 			 * is a single component.
373 			 * Before compressing "xxx/..", we want to expand
374 			 * "yyy/xxx", if it is a symbolic link.
375 			 */
376 			*--sp = 0;	/* form the pathname for readlink */
377 			if (sp != cp &&
378 			    (cc = readlink(cp, link, sizeof link)) >= 0) {
379 				link[cc] = '\0';
380 				if (slash)
381 					*p = '/';
382 				/*
383 				 * Point p to the '/' in "/..", and restore
384 				 * the '/'.
385 				 */
386 				*(p = sp) = '/';
387 				/*
388 				 * find length of p
389 				 */
390 				for (p1 = p; *p1++;)
391 					;
392 				if (*link != '/') {
393 					/*
394 					 * Relative path, expand it between
395 					 * the "yyy/" and the "/..".
396 					 * First, back sp up to the character
397 					 * past "yyy/".
398 					 */
399 					while (*--sp != '/')
400 						;
401 					sp++;
402 					*sp = 0;
403 					/*
404 					 * New length is
405 					 * "yyy/" + link + "/.." and rest
406 					 */
407 					p1 = newcp = xalloc((unsigned)
408 						((sp - cp) + cc + (p1 - p)));
409 					/*
410 					 * Copy new path into newcp
411 					 */
412 					for (p2 = cp; *p1++ = *p2++;)
413 						;
414 					for (p1--, p2 = link; *p1++ = *p2++;)
415 						;
416 					for (p1--, p2 = p; *p1++ = *p2++;)
417 						;
418 					/*
419 					 * Restart canonicalization at
420 					 * expanded "/xxx".
421 					 */
422 					p = sp - cp - 1 + newcp;
423 				} else {
424 					/*
425 					 * New length is link + "/.." and rest
426 					 */
427 					p1 = newcp = xalloc((unsigned)
428 						(cc + (p1 - p)));
429 					/*
430 					 * Copy new path into newcp
431 					 */
432 					for (p2 = link; *p1++ = *p2++;)
433 						;
434 					for (p1--, p2 = p; *p1++ = *p2++;)
435 						;
436 					/*
437 					 * Restart canonicalization at beginning
438 					 */
439 					p = newcp;
440 				}
441 				xfree(cp);
442 				cp = newcp;
443 				continue;	/* canonicalize the link */
444 			}
445 			*sp = '/';
446 			if (sp != cp)
447 				while (*--sp != '/')
448 					;
449 			if (slash) {
450 				for (p1 = sp + 1, p2 = p + 1; *p1++ = *p2++;)
451 					;
452 				p = sp;
453 			} else if (cp == sp)
454 				*++sp = '\0';
455 			else
456 				*sp = '\0';
457 		} else if (slash)
458 			*p = '/';
459 	}
460 	return cp;
461 }
462 
463 /*
464  * dnewcwd - make a new directory in the loop the current one
465  */
466 dnewcwd(dp)
467 	register struct directory *dp;
468 {
469 
470 	dcwd = dp;
471 	set("cwd", savestr(dcwd->di_name));
472 	if (printd)
473 		dodirs(fakev);
474 }
475