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