date and time created 91/03/14 12:01:54 by donn
[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
84b24505 8static char *sccsid = "@(#)dir.c 5.4 (Berkeley) %G%";
094e80ed 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) {
84b24505
KB
40#define WDERR "csh: can't get current directory.\n"
41 (void) write(SHDIAG, WDERR, strlen(WDERR));
010af04a
SL
42 exit(1);
43 }
44 }
106427f7
BJ
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 */
57dodirs(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
86dtildepr(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 */
99dochngd(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;
128flushcwd:
129 dfree(dcwd);
130 dnewcwd(dp);
131}
132
133/*
134 * dfollow - change to arg directory; fall back on cdpath if not valid
135 */
136char *
137dfollow(cp)
138 register char *cp;
139{
1f7436a6 140 register char *dp;
106427f7 141 struct varent *c;
1f7436a6 142
106427f7 143 cp = globone(cp);
1f7436a6 144 if (chdir(cp) >= 0)
106427f7
BJ
145 goto gotcha;
146 if (cp[0] != '/' && !prefix("./", cp) && !prefix("../", cp)
147 && (c = adrof("cdpath"))) {
1f7436a6
EW
148 char **cdp;
149 register char *p;
150 char buf[MAXPATHLEN];
106427f7 151
1f7436a6
EW
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 ;
106427f7
BJ
158 if (chdir(buf) >= 0) {
159 printd = 1;
160 xfree(cp);
161 cp = savestr(buf);
162 goto gotcha;
163 }
164 }
165 }
c30acc02
EW
166 dp = value(cp);
167 if ((dp[0] == '/' || dp[0] == '.') && chdir(dp) >= 0) {
1f7436a6
EW
168 xfree(cp);
169 cp = savestr(dp);
170 printd = 1;
171 goto gotcha;
106427f7 172 }
1f7436a6 173 xfree(cp); /* XXX, use after free */
106427f7
BJ
174 Perror(cp);
175
176gotcha:
177 if (*cp != '/') {
1f7436a6
EW
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 ;
106427f7
BJ
199 xfree(cp);
200 cp = dp;
1f7436a6
EW
201 dp += cwdlen;
202 } else
203 dp = cp;
204 return dcanon(cp, dp);
106427f7
BJ
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 */
212dopushd(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 */
252struct directory *
253dfind(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 */
282dopopd(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 */
312dfree(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 */
1f7436a6
EW
327char *
328dcanon(cp, p)
329 register char *cp, *p;
106427f7 330{
1f7436a6
EW
331 register char *sp;
332 register char *p1, *p2; /* general purpose */
333 bool slash;
106427f7
BJ
334
335 if (*cp != '/')
336 abort();
1f7436a6 337 while (*p) { /* for each component */
106427f7 338 sp = p; /* save slash address */
1f7436a6 339 while (*++p == '/') /* flush extra slashes */
106427f7
BJ
340 ;
341 if (p != ++sp)
1f7436a6
EW
342 for (p1 = sp, p2 = p; *p1++ = *p2++;)
343 ;
106427f7
BJ
344 p = sp; /* save start of component */
345 slash = 0;
1f7436a6 346 while (*++p) /* find next slash or end of path */
106427f7
BJ
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';
1f7436a6 357 else if (sp[0] == '.' && sp[1] == 0) {
106427f7 358 if (slash) {
1f7436a6
EW
359 for (p1 = sp, p2 = p + 1; *p1++ = *p2++;)
360 ;
106427f7
BJ
361 p = --sp;
362 } else if (--sp != cp)
363 *sp = '\0';
1f7436a6
EW
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)
106427f7
BJ
447 while (*--sp != '/')
448 ;
449 if (slash) {
1f7436a6
EW
450 for (p1 = sp + 1, p2 = p + 1; *p1++ = *p2++;)
451 ;
452 p = sp;
106427f7
BJ
453 } else if (cp == sp)
454 *++sp = '\0';
455 else
456 *sp = '\0';
457 } else if (slash)
458 *p = '/';
459 }
1f7436a6 460 return cp;
106427f7
BJ
461}
462
463/*
464 * dnewcwd - make a new directory in the loop the current one
465 */
466dnewcwd(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}