date and time created 91/03/07 20:27:30 by bostic
[unix-history] / usr / src / bin / sh / cd.c
CommitLineData
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
12static 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__
34STATIC int docd(char *, int);
35STATIC void updatepwd(char *);
36STATIC void getpwd(void);
37STATIC char *getcomponent(void);
38#else
39STATIC int docd();
40STATIC void updatepwd();
41STATIC void getpwd();
42STATIC char *getcomponent();
43#endif
44
45
46char *curdir; /* current working directory */
47STATIC char *cdcomppath;
48
49#if UDIR
50extern int didudir; /* set if /u/logname expanded */
51#endif
52
53
54int
55cdcmd(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
87STATIC int
88docd(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
111STATIC int
112docd(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
129top:
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
209STATIC char *
210getcomponent() {
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
236void hashcd();
237
238STATIC void
239updatepwd(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
277int
278pwdcmd(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
296STATIC void
297getpwd() {
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}