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