File: | src/bin/csh/dir.c |
Warning: | line 566, column 9 Access to field 'di_prev' results in a dereference of a null pointer (loaded from variable 'dp') |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* $OpenBSD: dir.c,v 1.25 2020/08/30 22:23:47 mortimer Exp $ */ | |||
2 | /* $NetBSD: dir.c,v 1.9 1995/03/21 09:02:42 cgd Exp $ */ | |||
3 | ||||
4 | /*- | |||
5 | * Copyright (c) 1980, 1991, 1993 | |||
6 | * The Regents of the University of California. All rights reserved. | |||
7 | * | |||
8 | * Redistribution and use in source and binary forms, with or without | |||
9 | * modification, are permitted provided that the following conditions | |||
10 | * are met: | |||
11 | * 1. Redistributions of source code must retain the above copyright | |||
12 | * notice, this list of conditions and the following disclaimer. | |||
13 | * 2. Redistributions in binary form must reproduce the above copyright | |||
14 | * notice, this list of conditions and the following disclaimer in the | |||
15 | * documentation and/or other materials provided with the distribution. | |||
16 | * 3. Neither the name of the University nor the names of its contributors | |||
17 | * may be used to endorse or promote products derived from this software | |||
18 | * without specific prior written permission. | |||
19 | * | |||
20 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |||
21 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |||
22 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |||
23 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |||
24 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |||
25 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |||
26 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |||
27 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |||
28 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |||
29 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||
30 | * SUCH DAMAGE. | |||
31 | */ | |||
32 | ||||
33 | #include <sys/stat.h> | |||
34 | #include <errno(*__errno()).h> | |||
35 | #include <stdlib.h> | |||
36 | #include <string.h> | |||
37 | #include <unistd.h> | |||
38 | #include <limits.h> | |||
39 | #include <stdarg.h> | |||
40 | ||||
41 | #include "csh.h" | |||
42 | #include "dir.h" | |||
43 | #include "extern.h" | |||
44 | ||||
45 | /* Directory management. */ | |||
46 | ||||
47 | static struct directory | |||
48 | *dfind(Char *); | |||
49 | static Char *dfollow(Char *); | |||
50 | static void printdirs(void); | |||
51 | static Char *dgoto(Char *); | |||
52 | static void dnewcwd(struct directory *); | |||
53 | static void dset(Char *); | |||
54 | ||||
55 | struct directory dhead; /* "head" of loop */ | |||
56 | struct directory *dcwd; /* the one we are in now */ | |||
57 | int printd; /* force name to be printed */ | |||
58 | ||||
59 | static int dirflag = 0; | |||
60 | ||||
61 | /* | |||
62 | * dinit - initialize current working directory | |||
63 | */ | |||
64 | void | |||
65 | dinit(Char *hp) | |||
66 | { | |||
67 | char *tcp; | |||
68 | Char *cp = NULL((void *)0); | |||
69 | struct directory *dp; | |||
70 | char path[PATH_MAX1024]; | |||
71 | static const char emsg[] = "csh: Trying to start from \"%s\"\n"; | |||
72 | ||||
73 | /* Don't believe the login shell home, because it may be a symlink */ | |||
74 | tcp = getcwd(path, PATH_MAX1024); | |||
75 | if (tcp == NULL((void *)0) || *tcp == '\0') { | |||
76 | (void) fprintf(csherr, "csh: %s\n", strerror(errno(*__errno()))); | |||
77 | if (hp && *hp) { | |||
78 | tcp = short2str(hp); | |||
79 | if (chdir(tcp) == 0) | |||
80 | cp = hp; | |||
81 | (void) fprintf(csherr, emsg, vis_str(hp)); | |||
82 | } | |||
83 | } | |||
84 | else { | |||
85 | struct stat swd, shp; | |||
86 | ||||
87 | if (stat(tcp, &swd) == -1) { | |||
88 | (void) fprintf(csherr, "csh: %s: %s\n", tcp, strerror(errno(*__errno()))); | |||
89 | } else { | |||
90 | /* | |||
91 | * See if $HOME is the working directory we got and use that | |||
92 | */ | |||
93 | if (hp && *hp && stat(short2str(hp), &shp) != -1 && | |||
94 | swd.st_dev == shp.st_dev && swd.st_ino == shp.st_ino) | |||
95 | cp = hp; | |||
96 | else { | |||
97 | char *cwd; | |||
98 | ||||
99 | /* | |||
100 | * use PWD if we have it (for subshells) | |||
101 | */ | |||
102 | if ((cwd = getenv("PWD")) != NULL((void *)0)) { | |||
103 | if (stat(cwd, &shp) != -1 && swd.st_dev == shp.st_dev && | |||
104 | swd.st_ino == shp.st_ino) | |||
105 | tcp = cwd; | |||
106 | } | |||
107 | cp = dcanon(SAVE(tcp)(Strsave(str2short(tcp))), STRNULL); | |||
108 | } | |||
109 | } | |||
110 | } | |||
111 | ||||
112 | if (cp == NULL((void *)0)) { | |||
113 | (void) fprintf(csherr, emsg, "/"); | |||
114 | if (chdir("/") == -1) | |||
115 | /* I am not even try to print an error message! */ | |||
116 | xexit(1); | |||
117 | cp = SAVE("/")(Strsave(str2short("/"))); | |||
118 | } | |||
119 | ||||
120 | dp = xcalloc(1, sizeof(struct directory)); | |||
121 | dp->di_name = Strsave(cp); | |||
122 | dp->di_count = 0; | |||
123 | dhead.di_next = dhead.di_prev = dp; | |||
124 | dp->di_next = dp->di_prev = &dhead; | |||
125 | printd = 0; | |||
126 | dnewcwd(dp); | |||
127 | } | |||
128 | ||||
129 | static void | |||
130 | dset(Char *dp) | |||
131 | { | |||
132 | /* | |||
133 | * Don't call set() directly cause if the directory contains ` or | |||
134 | * other junk characters glob will fail. | |||
135 | */ | |||
136 | Char **vec = xreallocarray(NULL((void *)0), 2, sizeof(*vec)); | |||
137 | ||||
138 | vec[0] = Strsave(dp); | |||
139 | vec[1] = 0; | |||
140 | setq(STRcwd, vec, &shvhed); | |||
141 | Setenv(STRPWD, dp); | |||
142 | } | |||
143 | ||||
144 | #define DIR_LONG1 1 | |||
145 | #define DIR_VERT2 2 | |||
146 | #define DIR_LINE4 4 | |||
147 | ||||
148 | static void | |||
149 | skipargs(Char ***v, char *str) | |||
150 | { | |||
151 | Char **n = *v, *s; | |||
152 | ||||
153 | dirflag = 0; | |||
154 | for (n++; *n != NULL((void *)0) && (*n)[0] == '-'; n++) | |||
155 | for (s = &((*n)[1]); *s; s++) | |||
156 | switch (*s) { | |||
157 | case 'l': | |||
158 | dirflag |= DIR_LONG1; | |||
159 | break; | |||
160 | case 'v': | |||
161 | dirflag |= DIR_VERT2; | |||
162 | break; | |||
163 | case 'n': | |||
164 | dirflag |= DIR_LINE4; | |||
165 | break; | |||
166 | default: | |||
167 | stderror(ERR_DIRUS69, vis_str(**v), str); | |||
168 | break; | |||
169 | } | |||
170 | *v = n; | |||
171 | } | |||
172 | ||||
173 | /* | |||
174 | * dodirs - list all directories in directory loop | |||
175 | */ | |||
176 | void | |||
177 | /*ARGSUSED*/ | |||
178 | dodirs(Char **v, struct command *t) | |||
179 | { | |||
180 | skipargs(&v, ""); | |||
181 | ||||
182 | if (*v != NULL((void *)0)) | |||
183 | stderror(ERR_DIRUS69, "dirs", ""); | |||
184 | printdirs(); | |||
185 | } | |||
186 | ||||
187 | static void | |||
188 | printdirs(void) | |||
189 | { | |||
190 | struct directory *dp; | |||
191 | Char *s, *hp = value(STRhome)value1(STRhome, &shvhed); | |||
192 | int idx, len, cur; | |||
193 | ||||
194 | if (*hp == '\0') | |||
195 | hp = NULL((void *)0); | |||
196 | dp = dcwd; | |||
197 | idx = 0; | |||
198 | cur = 0; | |||
199 | do { | |||
200 | if (dp == &dhead) | |||
201 | continue; | |||
202 | if (dirflag & DIR_VERT2) { | |||
203 | (void) fprintf(cshout, "%d\t", idx++); | |||
204 | cur = 0; | |||
205 | } | |||
206 | if (!(dirflag & DIR_LONG1) && hp != NULL((void *)0) && !eq(hp, STRslash)(Strcmp(hp, STRslash) == 0) && | |||
207 | (len = Strlen(hp), Strncmp(hp, dp->di_name, len) == 0) && | |||
208 | (dp->di_name[len] == '\0' || dp->di_name[len] == '/')) | |||
209 | len = Strlen(s = (dp->di_name + len)) + 2; | |||
210 | else | |||
211 | len = Strlen(s = dp->di_name) + 1; | |||
212 | ||||
213 | cur += len; | |||
214 | if ((dirflag & DIR_LINE4) && cur >= 80 - 1 && len < 80) { | |||
215 | (void) fprintf(cshout, "\n"); | |||
216 | cur = len; | |||
217 | } | |||
218 | (void) fprintf(cshout, s != dp->di_name ? "~%s%c" : "%s%c", | |||
219 | vis_str(s), (dirflag & DIR_VERT2) ? '\n' : ' '); | |||
220 | } while ((dp = dp->di_prev) != dcwd); | |||
221 | if (!(dirflag & DIR_VERT2)) | |||
222 | (void) fprintf(cshout, "\n"); | |||
223 | } | |||
224 | ||||
225 | void | |||
226 | dtildepr(Char *home, Char *dir) | |||
227 | { | |||
228 | ||||
229 | if (!eq(home, STRslash)(Strcmp(home, STRslash) == 0) && prefix(home, dir)) | |||
230 | (void) fprintf(cshout, "~%s", vis_str(dir + Strlen(home))); | |||
231 | else | |||
232 | (void) fprintf(cshout, "%s", vis_str(dir)); | |||
233 | } | |||
234 | ||||
235 | void | |||
236 | dtilde(void) | |||
237 | { | |||
238 | struct directory *d = dcwd; | |||
239 | ||||
240 | do { | |||
241 | if (d == &dhead) | |||
242 | continue; | |||
243 | d->di_name = dcanon(d->di_name, STRNULL); | |||
244 | } while ((d = d->di_prev) != dcwd); | |||
245 | ||||
246 | dset(dcwd->di_name); | |||
247 | } | |||
248 | ||||
249 | ||||
250 | /* dnormalize(): | |||
251 | * If the name starts with . or .. then we might need to normalize | |||
252 | * it depending on the symbolic link flags | |||
253 | */ | |||
254 | Char * | |||
255 | dnormalize(Char *cp) | |||
256 | { | |||
257 | ||||
258 | #define UC(unsigned char) (unsigned char) | |||
259 | #define ISDOT(c)((unsigned char)(c)[0] == '.' && (((unsigned char)(c) [1] == '\0') || ((unsigned char)(c)[1] == '/'))) (UC(unsigned char)(c)[0] == '.' && ((UC(unsigned char)(c)[1] == '\0') || (UC(unsigned char)(c)[1] == '/'))) | |||
260 | #define ISDOTDOT(c)((unsigned char)(c)[0] == '.' && ((unsigned char)(& ((c)[1]))[0] == '.' && (((unsigned char)(&((c)[1] ))[1] == '\0') || ((unsigned char)(&((c)[1]))[1] == '/')) )) (UC(unsigned char)(c)[0] == '.' && ISDOT(&((c)[1]))((unsigned char)(&((c)[1]))[0] == '.' && (((unsigned char)(&((c)[1]))[1] == '\0') || ((unsigned char)(&(( c)[1]))[1] == '/')))) | |||
261 | ||||
262 | if ((unsigned char) cp[0] == '/') | |||
263 | return (Strsave(cp)); | |||
264 | ||||
265 | if (adrof(STRignore_symlinks)adrof1(STRignore_symlinks, &shvhed)) { | |||
266 | int dotdot = 0; | |||
267 | Char *dp, *cwd; | |||
268 | size_t len; | |||
269 | ||||
270 | len = (size_t) (Strlen(dcwd->di_name) + 3); | |||
271 | cwd = xreallocarray(NULL((void *)0), len, sizeof(Char)); | |||
272 | (void) Strlcpy(cwd, dcwd->di_name, len); | |||
273 | ||||
274 | /* | |||
275 | * Ignore . and count ..'s | |||
276 | */ | |||
277 | while (*cp) { | |||
278 | if (ISDOT(cp)((unsigned char)(cp)[0] == '.' && (((unsigned char)(cp )[1] == '\0') || ((unsigned char)(cp)[1] == '/')))) { | |||
279 | if (*++cp) | |||
280 | cp++; | |||
281 | } | |||
282 | else if (ISDOTDOT(cp)((unsigned char)(cp)[0] == '.' && ((unsigned char)(& ((cp)[1]))[0] == '.' && (((unsigned char)(&((cp)[ 1]))[1] == '\0') || ((unsigned char)(&((cp)[1]))[1] == '/' ))))) { | |||
283 | dotdot++; | |||
284 | cp += 2; | |||
285 | if (*cp) | |||
286 | cp++; | |||
287 | } | |||
288 | else | |||
289 | break; | |||
290 | } | |||
291 | while (dotdot > 0) | |||
292 | if ((dp = Strrchr(cwd, '/'))) { | |||
293 | *dp = '\0'; | |||
294 | dotdot--; | |||
295 | } | |||
296 | else | |||
297 | break; | |||
298 | ||||
299 | if (*cp) { | |||
300 | cwd[dotdot = Strlen(cwd)] = '/'; | |||
301 | cwd[dotdot + 1] = '\0'; | |||
302 | dp = Strspl(cwd, cp); | |||
303 | free(cwd); | |||
304 | return dp; | |||
305 | } | |||
306 | else { | |||
307 | if (!*cwd) { | |||
308 | cwd[0] = '/'; | |||
309 | cwd[1] = '\0'; | |||
310 | } | |||
311 | return cwd; | |||
312 | } | |||
313 | } | |||
314 | return Strsave(cp); | |||
315 | } | |||
316 | ||||
317 | /* | |||
318 | * dochngd - implement chdir command. | |||
319 | */ | |||
320 | void | |||
321 | /*ARGSUSED*/ | |||
322 | dochngd(Char **v, struct command *t) | |||
323 | { | |||
324 | Char *cp; | |||
325 | struct directory *dp; | |||
326 | ||||
327 | skipargs(&v, " [<dir>]"); | |||
328 | printd = 0; | |||
329 | if (*v == NULL((void *)0)) { | |||
330 | if ((cp = value(STRhome)value1(STRhome, &shvhed)) == NULL((void *)0) || *cp == 0) | |||
331 | stderror(ERR_NAME0x10000000 | ERR_NOHOMEDIR35); | |||
332 | if (chdir(short2str(cp)) == -1) | |||
333 | stderror(ERR_NAME0x10000000 | ERR_CANTCHANGE36); | |||
334 | cp = Strsave(cp); | |||
335 | } | |||
336 | else if (v[1] != NULL((void *)0)) { | |||
337 | stderror(ERR_NAME0x10000000 | ERR_TOOMANY16); | |||
338 | /* NOTREACHED */ | |||
339 | return; | |||
340 | } | |||
341 | else if ((dp = dfind(*v)) != 0) { | |||
342 | char *tmp; | |||
343 | ||||
344 | printd = 1; | |||
345 | if (chdir(tmp = short2str(dp->di_name)) == -1) | |||
346 | stderror(ERR_SYSTEM55, tmp, strerror(errno(*__errno()))); | |||
347 | dcwd->di_prev->di_next = dcwd->di_next; | |||
348 | dcwd->di_next->di_prev = dcwd->di_prev; | |||
349 | dfree(dcwd); | |||
350 | dnewcwd(dp); | |||
351 | return; | |||
352 | } | |||
353 | else | |||
354 | cp = dfollow(*v); | |||
355 | dp = xcalloc(1, sizeof(struct directory)); | |||
356 | dp->di_name = cp; | |||
357 | dp->di_count = 0; | |||
358 | dp->di_next = dcwd->di_next; | |||
359 | dp->di_prev = dcwd->di_prev; | |||
360 | dp->di_prev->di_next = dp; | |||
361 | dp->di_next->di_prev = dp; | |||
362 | dfree(dcwd); | |||
363 | dnewcwd(dp); | |||
364 | } | |||
365 | ||||
366 | static Char * | |||
367 | dgoto(Char *cp) | |||
368 | { | |||
369 | Char *dp; | |||
370 | ||||
371 | if (*cp != '/') { | |||
372 | Char *p, *q; | |||
373 | int cwdlen; | |||
374 | ||||
375 | for (p = dcwd->di_name; *p++;) | |||
376 | continue; | |||
377 | if ((cwdlen = p - dcwd->di_name - 1) == 1) /* root */ | |||
378 | cwdlen = 0; | |||
379 | for (p = cp; *p++;) | |||
380 | continue; | |||
381 | dp = xreallocarray(NULL((void *)0), (cwdlen + (p - cp) + 1), sizeof(Char)); | |||
382 | for (p = dp, q = dcwd->di_name; (*p++ = *q++) != '\0';) | |||
383 | continue; | |||
384 | if (cwdlen) | |||
385 | p[-1] = '/'; | |||
386 | else | |||
387 | p--; /* don't add a / after root */ | |||
388 | for (q = cp; (*p++ = *q++) != '\0';) | |||
389 | continue; | |||
390 | free(cp); | |||
391 | cp = dp; | |||
392 | dp += cwdlen; | |||
393 | } | |||
394 | else | |||
395 | dp = cp; | |||
396 | ||||
397 | cp = dcanon(cp, dp); | |||
398 | return cp; | |||
399 | } | |||
400 | ||||
401 | /* | |||
402 | * dfollow - change to arg directory; fall back on cdpath if not valid | |||
403 | */ | |||
404 | static Char * | |||
405 | dfollow(Char *cp) | |||
406 | { | |||
407 | Char *dp; | |||
408 | struct varent *c; | |||
409 | char ebuf[PATH_MAX1024]; | |||
410 | int serrno; | |||
411 | ||||
412 | cp = globone(cp, G_ERROR0); | |||
413 | /* | |||
414 | * if we are ignoring symlinks, try to fix relatives now. | |||
415 | */ | |||
416 | dp = dnormalize(cp); | |||
417 | if (chdir(short2str(dp)) >= 0) { | |||
418 | free(cp); | |||
419 | return dgoto(dp); | |||
420 | } | |||
421 | else { | |||
422 | free(dp); | |||
423 | if (chdir(short2str(cp)) >= 0) | |||
424 | return dgoto(cp); | |||
425 | serrno = errno(*__errno()); | |||
426 | } | |||
427 | ||||
428 | if (cp[0] != '/' && !prefix(STRdotsl, cp) && !prefix(STRdotdotsl, cp) | |||
429 | && (c = adrof(STRcdpath)adrof1(STRcdpath, &shvhed))) { | |||
430 | Char **cdp; | |||
431 | Char *p; | |||
432 | Char buf[PATH_MAX1024]; | |||
433 | ||||
434 | for (cdp = c->vec; *cdp; cdp++) { | |||
435 | for (dp = buf, p = *cdp; (*dp++ = *p++) != '\0';) | |||
436 | continue; | |||
437 | dp[-1] = '/'; | |||
438 | for (p = cp; (*dp++ = *p++) != '\0';) | |||
439 | continue; | |||
440 | if (chdir(short2str(buf)) >= 0) { | |||
441 | printd = 1; | |||
442 | free(cp); | |||
443 | cp = Strsave(buf); | |||
444 | return dgoto(cp); | |||
445 | } | |||
446 | } | |||
447 | } | |||
448 | dp = value(cp)value1(cp, &shvhed); | |||
449 | if ((dp[0] == '/' || dp[0] == '.') && chdir(short2str(dp)) >= 0) { | |||
450 | free(cp); | |||
451 | cp = Strsave(dp); | |||
452 | printd = 1; | |||
453 | return dgoto(cp); | |||
454 | } | |||
455 | (void) strlcpy(ebuf, short2str(cp), sizeof ebuf); | |||
456 | free(cp); | |||
457 | stderror(ERR_SYSTEM55, ebuf, strerror(serrno)); | |||
458 | return (NULL((void *)0)); | |||
459 | } | |||
460 | ||||
461 | ||||
462 | /* | |||
463 | * dopushd - push new directory onto directory stack. | |||
464 | * with no arguments exchange top and second. | |||
465 | * with numeric argument (+n) bring it to top. | |||
466 | */ | |||
467 | void | |||
468 | /*ARGSUSED*/ | |||
469 | dopushd(Char **v, struct command *t) | |||
470 | { | |||
471 | struct directory *dp; | |||
472 | ||||
473 | skipargs(&v, " [<dir>|+<n>]"); | |||
474 | printd = 1; | |||
475 | if (*v == NULL((void *)0)) { | |||
476 | char *tmp; | |||
477 | ||||
478 | if ((dp = dcwd->di_prev) == &dhead) | |||
479 | dp = dhead.di_prev; | |||
480 | if (dp == dcwd) | |||
481 | stderror(ERR_NAME0x10000000 | ERR_NODIR66); | |||
482 | if (chdir(tmp = short2str(dp->di_name)) == -1) | |||
483 | stderror(ERR_SYSTEM55, tmp, strerror(errno(*__errno()))); | |||
484 | dp->di_prev->di_next = dp->di_next; | |||
485 | dp->di_next->di_prev = dp->di_prev; | |||
486 | dp->di_next = dcwd->di_next; | |||
487 | dp->di_prev = dcwd; | |||
488 | dcwd->di_next->di_prev = dp; | |||
489 | dcwd->di_next = dp; | |||
490 | } | |||
491 | else if (v[1] != NULL((void *)0)) { | |||
492 | stderror(ERR_NAME0x10000000 | ERR_TOOMANY16); | |||
493 | /* NOTREACHED */ | |||
494 | return; | |||
495 | } | |||
496 | else if ((dp = dfind(*v)) != NULL((void *)0)) { | |||
497 | char *tmp; | |||
498 | ||||
499 | if (chdir(tmp = short2str(dp->di_name)) == -1) | |||
500 | stderror(ERR_SYSTEM55, tmp, strerror(errno(*__errno()))); | |||
501 | } | |||
502 | else { | |||
503 | Char *ccp; | |||
504 | ||||
505 | ccp = dfollow(*v); | |||
506 | dp = xcalloc(1, sizeof(struct directory)); | |||
507 | dp->di_name = ccp; | |||
508 | dp->di_count = 0; | |||
509 | dp->di_prev = dcwd; | |||
510 | dp->di_next = dcwd->di_next; | |||
511 | dcwd->di_next = dp; | |||
512 | dp->di_next->di_prev = dp; | |||
513 | } | |||
514 | dnewcwd(dp); | |||
515 | } | |||
516 | ||||
517 | /* | |||
518 | * dfind - find a directory if specified by numeric (+n) argument | |||
519 | */ | |||
520 | static struct directory * | |||
521 | dfind(Char *cp) | |||
522 | { | |||
523 | struct directory *dp; | |||
524 | int i; | |||
525 | Char *ep; | |||
526 | ||||
527 | if (*cp++ != '+') | |||
528 | return (0); | |||
529 | for (ep = cp; Isdigit(*ep)(((*ep) & 0100000U) ? 0 : isdigit((unsigned char) (*ep))); ep++) | |||
530 | continue; | |||
531 | if (*ep) | |||
532 | return (0); | |||
533 | i = getn(cp); | |||
534 | if (i <= 0) | |||
535 | return (0); | |||
536 | for (dp = dcwd; i != 0; i--) { | |||
537 | if ((dp = dp->di_prev) == &dhead) | |||
538 | dp = dp->di_prev; | |||
539 | if (dp == dcwd) | |||
540 | stderror(ERR_NAME0x10000000 | ERR_DEEP27); | |||
541 | } | |||
542 | return (dp); | |||
543 | } | |||
544 | ||||
545 | /* | |||
546 | * dopopd - pop a directory out of the directory stack | |||
547 | * with a numeric argument just discard it. | |||
548 | */ | |||
549 | void | |||
550 | /*ARGSUSED*/ | |||
551 | dopopd(Char **v, struct command *t) | |||
552 | { | |||
553 | struct directory *dp, *p = NULL((void *)0); | |||
554 | ||||
555 | skipargs(&v, " [+<n>]"); | |||
556 | printd = 1; | |||
557 | if (*v == NULL((void *)0)) | |||
| ||||
558 | dp = dcwd; | |||
559 | else if (v[1] != NULL((void *)0)) { | |||
560 | stderror(ERR_NAME0x10000000 | ERR_TOOMANY16); | |||
561 | /* NOTREACHED */ | |||
562 | return; | |||
563 | } | |||
564 | else if ((dp = dfind(*v)) == 0) | |||
565 | stderror(ERR_NAME0x10000000 | ERR_BADDIR68); | |||
566 | if (dp->di_prev == &dhead && dp->di_next == &dhead) | |||
| ||||
567 | stderror(ERR_NAME0x10000000 | ERR_EMPTY67); | |||
568 | if (dp == dcwd) { | |||
569 | char *tmp; | |||
570 | ||||
571 | if ((p = dp->di_prev) == &dhead) | |||
572 | p = dhead.di_prev; | |||
573 | if (chdir(tmp = short2str(p->di_name)) == -1) | |||
574 | stderror(ERR_SYSTEM55, tmp, strerror(errno(*__errno()))); | |||
575 | } | |||
576 | dp->di_prev->di_next = dp->di_next; | |||
577 | dp->di_next->di_prev = dp->di_prev; | |||
578 | if (dp == dcwd) | |||
579 | dnewcwd(p); | |||
580 | else { | |||
581 | printdirs(); | |||
582 | } | |||
583 | dfree(dp); | |||
584 | } | |||
585 | ||||
586 | /* | |||
587 | * dfree - free the directory (or keep it if it still has ref count) | |||
588 | */ | |||
589 | void | |||
590 | dfree(struct directory *dp) | |||
591 | { | |||
592 | ||||
593 | if (dp->di_count != 0) { | |||
594 | dp->di_next = dp->di_prev = 0; | |||
595 | } | |||
596 | else { | |||
597 | free((char *) dp->di_name); | |||
598 | free(dp); | |||
599 | } | |||
600 | } | |||
601 | ||||
602 | /* | |||
603 | * dcanon - canonicalize the pathname, removing excess ./ and ../ etc. | |||
604 | * we are of course assuming that the file system is standardly | |||
605 | * constructed (always have ..'s, directories have links) | |||
606 | */ | |||
607 | Char * | |||
608 | dcanon(Char *cp, Char *p) | |||
609 | { | |||
610 | Char *sp; | |||
611 | Char *p1, *p2; /* general purpose */ | |||
612 | bool slash; | |||
613 | ||||
614 | Char link[PATH_MAX1024]; | |||
615 | char tlink[PATH_MAX1024]; | |||
616 | int cc; | |||
617 | Char *newcp; | |||
618 | ||||
619 | /* | |||
620 | * christos: if the path given does not start with a slash prepend cwd. If | |||
621 | * cwd does not start with a path or the result would be too long abort(). | |||
622 | */ | |||
623 | if (*cp != '/') { | |||
624 | Char tmpdir[PATH_MAX1024]; | |||
625 | ||||
626 | p1 = value(STRcwd)value1(STRcwd, &shvhed); | |||
627 | if (p1 == NULL((void *)0) || *p1 != '/') | |||
628 | abort(); | |||
629 | if (Strlen(p1) + Strlen(cp) + 1 >= PATH_MAX1024) | |||
630 | abort(); | |||
631 | (void) Strlcpy(tmpdir, p1, sizeof tmpdir/sizeof(Char)); | |||
632 | (void) Strlcat(tmpdir, STRslash, sizeof tmpdir/sizeof(Char)); | |||
633 | (void) Strlcat(tmpdir, cp, sizeof tmpdir/sizeof(Char)); | |||
634 | free(cp); | |||
635 | cp = p = Strsave(tmpdir); | |||
636 | } | |||
637 | ||||
638 | while (*p) { /* for each component */ | |||
639 | sp = p; /* save slash address */ | |||
640 | while (*++p == '/') /* flush extra slashes */ | |||
641 | continue; | |||
642 | if (p != ++sp) | |||
643 | for (p1 = sp, p2 = p; (*p1++ = *p2++) != '\0';) | |||
644 | continue; | |||
645 | p = sp; /* save start of component */ | |||
646 | slash = 0; | |||
647 | while (*p) /* find next slash or end of path */ | |||
648 | if (*++p == '/') { | |||
649 | slash = 1; | |||
650 | *p = 0; | |||
651 | break; | |||
652 | } | |||
653 | ||||
654 | if (*sp == '\0') /* if component is null */ | |||
655 | if (--sp == cp) /* if path is one char (i.e. /) */ | |||
656 | break; | |||
657 | else | |||
658 | *sp = '\0'; | |||
659 | else if (sp[0] == '.' && sp[1] == 0) { | |||
660 | if (slash) { | |||
661 | for (p1 = sp, p2 = p + 1; (*p1++ = *p2++) != '\0';) | |||
662 | continue; | |||
663 | p = --sp; | |||
664 | } | |||
665 | else if (--sp != cp) | |||
666 | *sp = '\0'; | |||
667 | } | |||
668 | else if (sp[0] == '.' && sp[1] == '.' && sp[2] == 0) { | |||
669 | /* | |||
670 | * We have something like "yyy/xxx/..", where "yyy" can be null or | |||
671 | * a path starting at /, and "xxx" is a single component. Before | |||
672 | * compressing "xxx/..", we want to expand "yyy/xxx", if it is a | |||
673 | * symbolic link. | |||
674 | */ | |||
675 | *--sp = 0; /* form the pathname for readlink */ | |||
676 | if (sp != cp && !adrof(STRignore_symlinks)adrof1(STRignore_symlinks, &shvhed) && | |||
677 | (cc = readlink(short2str(cp), tlink, | |||
678 | sizeof tlink-1)) >= 0) { | |||
679 | tlink[cc] = '\0'; | |||
680 | (void) Strlcpy(link, str2short(tlink), sizeof link/sizeof(Char)); | |||
681 | ||||
682 | if (slash) | |||
683 | *p = '/'; | |||
684 | /* | |||
685 | * Point p to the '/' in "/..", and restore the '/'. | |||
686 | */ | |||
687 | *(p = sp) = '/'; | |||
688 | /* | |||
689 | * find length of p | |||
690 | */ | |||
691 | for (p1 = p; *p1++;) | |||
692 | continue; | |||
693 | if (*link != '/') { | |||
694 | /* | |||
695 | * Relative path, expand it between the "yyy/" and the | |||
696 | * "/..". First, back sp up to the character past "yyy/". | |||
697 | */ | |||
698 | while (*--sp != '/') | |||
699 | continue; | |||
700 | sp++; | |||
701 | *sp = 0; | |||
702 | /* | |||
703 | * New length is "yyy/" + link + "/.." and rest | |||
704 | */ | |||
705 | p1 = newcp = xreallocarray(NULL((void *)0), | |||
706 | (sp - cp) + cc + (p1 - p), sizeof(Char)); | |||
707 | /* | |||
708 | * Copy new path into newcp | |||
709 | */ | |||
710 | for (p2 = cp; (*p1++ = *p2++) != '\0';) | |||
711 | continue; | |||
712 | for (p1--, p2 = link; (*p1++ = *p2++) != '\0';) | |||
713 | continue; | |||
714 | for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) | |||
715 | continue; | |||
716 | /* | |||
717 | * Restart canonicalization at expanded "/xxx". | |||
718 | */ | |||
719 | p = sp - cp - 1 + newcp; | |||
720 | } | |||
721 | else { | |||
722 | /* | |||
723 | * New length is link + "/.." and rest | |||
724 | */ | |||
725 | p1 = newcp = xreallocarray(NULL((void *)0), cc + (p1 - p), | |||
726 | sizeof(Char)); | |||
727 | /* | |||
728 | * Copy new path into newcp | |||
729 | */ | |||
730 | for (p2 = link; (*p1++ = *p2++) != '\0';) | |||
731 | continue; | |||
732 | for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) | |||
733 | continue; | |||
734 | /* | |||
735 | * Restart canonicalization at beginning | |||
736 | */ | |||
737 | p = newcp; | |||
738 | } | |||
739 | free(cp); | |||
740 | cp = newcp; | |||
741 | continue; /* canonicalize the link */ | |||
742 | } | |||
743 | *sp = '/'; | |||
744 | if (sp != cp) | |||
745 | while (*--sp != '/') | |||
746 | continue; | |||
747 | if (slash) { | |||
748 | for (p1 = sp + 1, p2 = p + 1; (*p1++ = *p2++) != '\0';) | |||
749 | continue; | |||
750 | p = sp; | |||
751 | } | |||
752 | else if (cp == sp) | |||
753 | *++sp = '\0'; | |||
754 | else | |||
755 | *sp = '\0'; | |||
756 | } | |||
757 | else { /* normal dir name (not . or .. or nothing) */ | |||
758 | ||||
759 | if (sp != cp && adrof(STRchase_symlinks)adrof1(STRchase_symlinks, &shvhed) && | |||
760 | !adrof(STRignore_symlinks)adrof1(STRignore_symlinks, &shvhed) && | |||
761 | (cc = readlink(short2str(cp), tlink, | |||
762 | sizeof tlink-1)) >= 0) { | |||
763 | tlink[cc] = '\0'; | |||
764 | (void) Strlcpy(link, str2short(tlink), sizeof link/sizeof(Char)); | |||
765 | ||||
766 | /* | |||
767 | * restore the '/'. | |||
768 | */ | |||
769 | if (slash) | |||
770 | *p = '/'; | |||
771 | ||||
772 | /* | |||
773 | * point sp to p (rather than backing up). | |||
774 | */ | |||
775 | sp = p; | |||
776 | ||||
777 | /* | |||
778 | * find length of p | |||
779 | */ | |||
780 | for (p1 = p; *p1++;) | |||
781 | continue; | |||
782 | if (*link != '/') { | |||
783 | /* | |||
784 | * Relative path, expand it between the "yyy/" and the | |||
785 | * remainder. First, back sp up to the character past | |||
786 | * "yyy/". | |||
787 | */ | |||
788 | while (*--sp != '/') | |||
789 | continue; | |||
790 | sp++; | |||
791 | *sp = 0; | |||
792 | /* | |||
793 | * New length is "yyy/" + link + "/.." and rest | |||
794 | */ | |||
795 | p1 = newcp = xreallocarray(NULL((void *)0), | |||
796 | (sp - cp) + cc + (p1 - p), sizeof(Char)); | |||
797 | /* | |||
798 | * Copy new path into newcp | |||
799 | */ | |||
800 | for (p2 = cp; (*p1++ = *p2++) != '\0';) | |||
801 | continue; | |||
802 | for (p1--, p2 = link; (*p1++ = *p2++) != '\0';) | |||
803 | continue; | |||
804 | for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) | |||
805 | continue; | |||
806 | /* | |||
807 | * Restart canonicalization at expanded "/xxx". | |||
808 | */ | |||
809 | p = sp - cp - 1 + newcp; | |||
810 | } | |||
811 | else { | |||
812 | /* | |||
813 | * New length is link + the rest | |||
814 | */ | |||
815 | p1 = newcp = xreallocarray(NULL((void *)0), cc + (p1 - p), sizeof(Char)); | |||
816 | /* | |||
817 | * Copy new path into newcp | |||
818 | */ | |||
819 | for (p2 = link; (*p1++ = *p2++) != '\0';) | |||
820 | continue; | |||
821 | for (p1--, p2 = p; (*p1++ = *p2++) != '\0';) | |||
822 | continue; | |||
823 | /* | |||
824 | * Restart canonicalization at beginning | |||
825 | */ | |||
826 | p = newcp; | |||
827 | } | |||
828 | free(cp); | |||
829 | cp = newcp; | |||
830 | continue; /* canonicalize the link */ | |||
831 | } | |||
832 | if (slash) | |||
833 | *p = '/'; | |||
834 | } | |||
835 | } | |||
836 | ||||
837 | /* | |||
838 | * fix home... | |||
839 | */ | |||
840 | p1 = value(STRhome)value1(STRhome, &shvhed); | |||
841 | cc = Strlen(p1); | |||
842 | /* | |||
843 | * See if we're not in a subdir of STRhome | |||
844 | */ | |||
845 | if (p1 && *p1 == '/' && | |||
846 | (Strncmp(p1, cp, cc) != 0 || (cp[cc] != '/' && cp[cc] != '\0'))) { | |||
847 | static ino_t home_ino = -1; | |||
848 | static dev_t home_dev = -1; | |||
849 | static Char *home_ptr = NULL((void *)0); | |||
850 | struct stat statbuf; | |||
851 | ||||
852 | /* | |||
853 | * Get dev and ino of STRhome | |||
854 | */ | |||
855 | if (home_ptr != p1 && | |||
856 | stat(short2str(p1), &statbuf) != -1) { | |||
857 | home_dev = statbuf.st_dev; | |||
858 | home_ino = statbuf.st_ino; | |||
859 | home_ptr = p1; | |||
860 | } | |||
861 | /* | |||
862 | * Start comparing dev & ino backwards | |||
863 | */ | |||
864 | Strlcpy(link, cp, sizeof link/sizeof(Char)); | |||
865 | p2 = link; | |||
866 | for (sp = NULL((void *)0); *p2 && stat(short2str(p2), &statbuf) != -1;) { | |||
867 | if (statbuf.st_dev == home_dev && | |||
868 | statbuf.st_ino == home_ino) { | |||
869 | sp = (Char *) - 1; | |||
870 | break; | |||
871 | } | |||
872 | if ((sp = Strrchr(p2, '/')) != NULL((void *)0)) | |||
873 | *sp = '\0'; | |||
874 | } | |||
875 | /* | |||
876 | * See if we found it | |||
877 | */ | |||
878 | if (*p2 && sp == (Char *) -1) { | |||
879 | /* | |||
880 | * Use STRhome to make '~' work | |||
881 | */ | |||
882 | newcp = Strspl(p1, cp + Strlen(p2)); | |||
883 | free(cp); | |||
884 | cp = newcp; | |||
885 | } | |||
886 | } | |||
887 | return cp; | |||
888 | } | |||
889 | ||||
890 | ||||
891 | /* | |||
892 | * dnewcwd - make a new directory in the loop the current one | |||
893 | */ | |||
894 | static void | |||
895 | dnewcwd(struct directory *dp) | |||
896 | { | |||
897 | dcwd = dp; | |||
898 | dset(dcwd->di_name); | |||
899 | if (printd && !(adrof(STRpushdsilent)adrof1(STRpushdsilent, &shvhed))) | |||
900 | printdirs(); | |||
901 | } |