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