Commit | Line | Data |
---|---|---|
d003fee2 KB |
1 | /*- |
2 | * Copyright (c) 1991 The Regents of the University of California. | |
3 | * All rights reserved. | |
4 | * | |
5 | * This code is derived from software contributed to Berkeley by | |
6 | * Kenneth Almquist. | |
7 | * | |
8 | * %sccs.include.redist.c% | |
9 | */ | |
10 | ||
11 | #ifndef lint | |
12 | static char sccsid[] = "@(#)cd.c 5.1 (Berkeley) %G%"; | |
13 | #endif /* not lint */ | |
14 | ||
15 | /* | |
16 | * The cd and pwd commands. | |
17 | */ | |
18 | ||
19 | #include "shell.h" | |
20 | #include "var.h" | |
21 | #include "nodes.h" /* for jobs.h */ | |
22 | #include "jobs.h" | |
23 | #include "options.h" | |
24 | #include "output.h" | |
25 | #include "memalloc.h" | |
26 | #include "error.h" | |
27 | #include "mystring.h" | |
28 | #include <sys/types.h> | |
29 | #include <sys/stat.h> | |
30 | #include <errno.h> | |
31 | ||
32 | ||
33 | #ifdef __STDC__ | |
34 | STATIC int docd(char *, int); | |
35 | STATIC void updatepwd(char *); | |
36 | STATIC void getpwd(void); | |
37 | STATIC char *getcomponent(void); | |
38 | #else | |
39 | STATIC int docd(); | |
40 | STATIC void updatepwd(); | |
41 | STATIC void getpwd(); | |
42 | STATIC char *getcomponent(); | |
43 | #endif | |
44 | ||
45 | ||
46 | char *curdir; /* current working directory */ | |
47 | STATIC char *cdcomppath; | |
48 | ||
49 | #if UDIR | |
50 | extern int didudir; /* set if /u/logname expanded */ | |
51 | #endif | |
52 | ||
53 | ||
54 | int | |
55 | cdcmd(argc, argv) char **argv; { | |
56 | char *dest; | |
57 | char *path; | |
58 | char *p; | |
59 | struct stat statb; | |
60 | char *padvance(); | |
61 | ||
62 | if ((dest = argv[1]) == NULL && (dest = bltinlookup("HOME", 1)) == NULL) | |
63 | error("HOME not set"); | |
64 | if (*dest == '/' || (path = bltinlookup("CDPATH", 1)) == NULL) | |
65 | path = nullstr; | |
66 | while ((p = padvance(&path, dest)) != NULL) { | |
67 | if (stat(p, &statb) >= 0 | |
68 | && (statb.st_mode & S_IFMT) == S_IFDIR | |
69 | && docd(p, strcmp(p, dest)) >= 0) | |
70 | return 0; | |
71 | } | |
72 | error("can't cd to %s", dest); | |
73 | } | |
74 | ||
75 | ||
76 | /* | |
77 | * Actually do the chdir. If the name refers to symbolic links, we | |
78 | * compute the actual directory name before doing the cd. In an | |
79 | * interactive shell, print the directory name if "print" is nonzero | |
80 | * or if the name refers to a symbolic link. We also print the name | |
81 | * if "/u/logname" was expanded in it, since this is similar to a | |
82 | * symbolic link. (The check for this breaks if the user gives the | |
83 | * cd command some additional, unused arguments.) | |
84 | */ | |
85 | ||
86 | #if SYMLINKS == 0 | |
87 | STATIC int | |
88 | docd(dest, print) | |
89 | char *dest; | |
90 | { | |
91 | #if UDIR | |
92 | if (didudir) | |
93 | print = 1; | |
94 | #endif | |
95 | INTOFF; | |
96 | if (chdir(dest) < 0) { | |
97 | INTON; | |
98 | return -1; | |
99 | } | |
100 | updatepwd(dest); | |
101 | INTON; | |
102 | if (print && iflag) | |
103 | out1fmt("%s\n", stackblock()); | |
104 | return 0; | |
105 | } | |
106 | ||
107 | #else | |
108 | ||
109 | ||
110 | ||
111 | STATIC int | |
112 | docd(dest, print) | |
113 | char *dest; | |
114 | { | |
115 | register char *p; | |
116 | register char *q; | |
117 | char *symlink; | |
118 | char *component; | |
119 | struct stat statb; | |
120 | int first; | |
121 | int i; | |
122 | ||
123 | TRACE(("docd(\"%s\", %d) called\n", dest, print)); | |
124 | #if UDIR | |
125 | if (didudir) | |
126 | print = 1; | |
127 | #endif | |
128 | ||
129 | top: | |
130 | cdcomppath = dest; | |
131 | STARTSTACKSTR(p); | |
132 | if (*dest == '/') { | |
133 | STPUTC('/', p); | |
134 | cdcomppath++; | |
135 | } | |
136 | first = 1; | |
137 | while ((q = getcomponent()) != NULL) { | |
138 | if (q[0] == '\0' || q[0] == '.' && q[1] == '\0') | |
139 | continue; | |
140 | if (! first) | |
141 | STPUTC('/', p); | |
142 | first = 0; | |
143 | component = q; | |
144 | while (*q) | |
145 | STPUTC(*q++, p); | |
146 | if (equal(component, "..")) | |
147 | continue; | |
148 | STACKSTRNUL(p); | |
149 | if (lstat(stackblock(), &statb) < 0) | |
150 | error("lstat %s failed", stackblock()); | |
151 | if ((statb.st_mode & S_IFMT) != S_IFLNK) | |
152 | continue; | |
153 | ||
154 | /* Hit a symbolic link. We have to start all over again. */ | |
155 | print = 1; | |
156 | STPUTC('\0', p); | |
157 | symlink = grabstackstr(p); | |
158 | i = (int)statb.st_size + 2; /* 2 for '/' and '\0' */ | |
159 | if (cdcomppath != NULL) | |
160 | i += strlen(cdcomppath); | |
161 | p = stalloc(i); | |
162 | if (readlink(symlink, p, (int)statb.st_size) < 0) { | |
163 | error("readlink %s failed", stackblock()); | |
164 | } | |
165 | if (cdcomppath != NULL) { | |
166 | p[(int)statb.st_size] = '/'; | |
167 | scopy(cdcomppath, p + (int)statb.st_size + 1); | |
168 | } else { | |
169 | p[(int)statb.st_size] = '\0'; | |
170 | } | |
171 | if (p[0] != '/') { /* relative path name */ | |
172 | char *r; | |
173 | q = r = symlink; | |
174 | while (*q) { | |
175 | if (*q++ == '/') | |
176 | r = q; | |
177 | } | |
178 | *r = '\0'; | |
179 | dest = stalloc(strlen(symlink) + strlen(p) + 1); | |
180 | scopy(symlink, dest); | |
181 | strcat(dest, p); | |
182 | } else { | |
183 | dest = p; | |
184 | } | |
185 | goto top; | |
186 | } | |
187 | STPUTC('\0', p); | |
188 | p = grabstackstr(p); | |
189 | INTOFF; | |
190 | if (chdir(p) < 0) { | |
191 | INTON; | |
192 | return -1; | |
193 | } | |
194 | updatepwd(p); | |
195 | INTON; | |
196 | if (print && iflag) | |
197 | out1fmt("%s\n", p); | |
198 | return 0; | |
199 | } | |
200 | #endif /* SYMLINKS */ | |
201 | ||
202 | ||
203 | ||
204 | /* | |
205 | * Get the next component of the path name pointed to by cdcomppath. | |
206 | * This routine overwrites the string pointed to by cdcomppath. | |
207 | */ | |
208 | ||
209 | STATIC char * | |
210 | getcomponent() { | |
211 | register char *p; | |
212 | char *start; | |
213 | ||
214 | if ((p = cdcomppath) == NULL) | |
215 | return NULL; | |
216 | start = cdcomppath; | |
217 | while (*p != '/' && *p != '\0') | |
218 | p++; | |
219 | if (*p == '\0') { | |
220 | cdcomppath = NULL; | |
221 | } else { | |
222 | *p++ = '\0'; | |
223 | cdcomppath = p; | |
224 | } | |
225 | return start; | |
226 | } | |
227 | ||
228 | ||
229 | ||
230 | /* | |
231 | * Update curdir (the name of the current directory) in response to a | |
232 | * cd command. We also call hashcd to let the routines in exec.c know | |
233 | * that the current directory has changed. | |
234 | */ | |
235 | ||
236 | void hashcd(); | |
237 | ||
238 | STATIC void | |
239 | updatepwd(dir) | |
240 | char *dir; | |
241 | { | |
242 | char *new; | |
243 | char *p; | |
244 | ||
245 | hashcd(); /* update command hash table */ | |
246 | cdcomppath = stalloc(strlen(dir) + 1); | |
247 | scopy(dir, cdcomppath); | |
248 | STARTSTACKSTR(new); | |
249 | if (*dir != '/') { | |
250 | if (curdir == NULL) | |
251 | return; | |
252 | p = curdir; | |
253 | while (*p) | |
254 | STPUTC(*p++, new); | |
255 | if (p[-1] == '/') | |
256 | STUNPUTC(new); | |
257 | } | |
258 | while ((p = getcomponent()) != NULL) { | |
259 | if (equal(p, "..")) { | |
260 | while (new > stackblock() && (STUNPUTC(new), *new) != '/'); | |
261 | } else if (*p != '\0' && ! equal(p, ".")) { | |
262 | STPUTC('/', new); | |
263 | while (*p) | |
264 | STPUTC(*p++, new); | |
265 | } | |
266 | } | |
267 | if (new == stackblock()) | |
268 | STPUTC('/', new); | |
269 | STACKSTRNUL(new); | |
270 | if (curdir) | |
271 | ckfree(curdir); | |
272 | curdir = savestr(stackblock()); | |
273 | } | |
274 | ||
275 | ||
276 | ||
277 | int | |
278 | pwdcmd(argc, argv) char **argv; { | |
279 | getpwd(); | |
280 | out1str(curdir); | |
281 | out1c('\n'); | |
282 | return 0; | |
283 | } | |
284 | ||
285 | ||
286 | ||
287 | /* | |
288 | * Run /bin/pwd to find out what the current directory is. We suppress | |
289 | * interrupts throughout most of this, but the user can still break out | |
290 | * of it by killing the pwd program. If we already know the current | |
291 | * directory, this routine returns immediately. | |
292 | */ | |
293 | ||
294 | #define MAXPWD 256 | |
295 | ||
296 | STATIC void | |
297 | getpwd() { | |
298 | char buf[MAXPWD]; | |
299 | char *p; | |
300 | int i; | |
301 | int status; | |
302 | struct job *jp; | |
303 | int pip[2]; | |
304 | ||
305 | if (curdir) | |
306 | return; | |
307 | INTOFF; | |
308 | if (pipe(pip) < 0) | |
309 | error("Pipe call failed"); | |
310 | jp = makejob((union node *)NULL, 1); | |
311 | if (forkshell(jp, (union node *)NULL, FORK_NOJOB) == 0) { | |
312 | close(pip[0]); | |
313 | if (pip[1] != 1) { | |
314 | close(1); | |
315 | copyfd(pip[1], 1); | |
316 | close(pip[1]); | |
317 | } | |
318 | execl("/bin/pwd", "pwd", (char *)0); | |
319 | error("Cannot exec /bin/pwd"); | |
320 | } | |
321 | close(pip[1]); | |
322 | pip[1] = -1; | |
323 | p = buf; | |
324 | while ((i = read(pip[0], p, buf + MAXPWD - p)) > 0 | |
325 | || i == -1 && errno == EINTR) { | |
326 | if (i > 0) | |
327 | p += i; | |
328 | } | |
329 | close(pip[0]); | |
330 | pip[0] = -1; | |
331 | status = waitforjob(jp); | |
332 | if (status != 0) | |
333 | error((char *)0); | |
334 | if (i < 0 || p == buf || p[-1] != '\n') | |
335 | error("pwd command failed"); | |
336 | p[-1] = '\0'; | |
337 | curdir = savestr(buf); | |
338 | INTON; | |
339 | } |