Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/bin/sh/cd.c
39475 views
1
/*-
2
* Copyright (c) 1991, 1993
3
* The Regents of the University of California. All rights reserved.
4
*
5
* This code is derived from software contributed to Berkeley by
6
* Kenneth Almquist.
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/types.h>
34
#include <sys/stat.h>
35
#include <stdlib.h>
36
#include <string.h>
37
#include <unistd.h>
38
#include <errno.h>
39
#include <limits.h>
40
41
/*
42
* The cd and pwd commands.
43
*/
44
45
#include "shell.h"
46
#include "var.h"
47
#include "nodes.h" /* for jobs.h */
48
#include "jobs.h"
49
#include "options.h"
50
#include "output.h"
51
#include "memalloc.h"
52
#include "error.h"
53
#include "exec.h"
54
#include "redir.h"
55
#include "mystring.h"
56
#include "show.h"
57
#include "cd.h"
58
#include "builtins.h"
59
60
static int cdlogical(char *);
61
static int cdphysical(char *);
62
static int docd(char *, int, int);
63
static char *getcomponent(char **);
64
static char *findcwd(char *);
65
static void updatepwd(char *);
66
static char *getpwd(void);
67
static char *getpwd2(void);
68
69
static char *curdir = NULL; /* current working directory */
70
71
int
72
cdcmd(int argc __unused, char **argv __unused)
73
{
74
const char *dest;
75
const char *path;
76
char *p;
77
struct stat statb;
78
int ch, phys, print = 0, getcwderr = 0;
79
int rc;
80
int errno1 = ENOENT;
81
82
phys = Pflag;
83
while ((ch = nextopt("eLP")) != '\0') {
84
switch (ch) {
85
case 'e':
86
getcwderr = 1;
87
break;
88
case 'L':
89
phys = 0;
90
break;
91
case 'P':
92
phys = 1;
93
break;
94
}
95
}
96
97
if (*argptr != NULL && argptr[1] != NULL)
98
error("too many arguments");
99
100
if ((dest = *argptr) == NULL && (dest = bltinlookup("HOME", 1)) == NULL)
101
error("HOME not set");
102
if (dest[0] == '-' && dest[1] == '\0') {
103
dest = bltinlookup("OLDPWD", 1);
104
if (dest == NULL)
105
error("OLDPWD not set");
106
print = 1;
107
}
108
if (dest[0] == '/' ||
109
(dest[0] == '.' && (dest[1] == '/' || dest[1] == '\0')) ||
110
(dest[0] == '.' && dest[1] == '.' && (dest[2] == '/' || dest[2] == '\0')) ||
111
(path = bltinlookup("CDPATH", 1)) == NULL)
112
path = "";
113
while ((p = padvance(&path, NULL, dest)) != NULL) {
114
if (stat(p, &statb) < 0) {
115
if (errno != ENOENT)
116
errno1 = errno;
117
} else if (!S_ISDIR(statb.st_mode))
118
errno1 = ENOTDIR;
119
else {
120
if (!print) {
121
/*
122
* XXX - rethink
123
*/
124
if (p[0] == '.' && p[1] == '/' && p[2] != '\0')
125
print = strcmp(p + 2, dest);
126
else
127
print = strcmp(p, dest);
128
}
129
rc = docd(p, print, phys);
130
if (rc >= 0)
131
return getcwderr ? rc : 0;
132
if (errno != ENOENT)
133
errno1 = errno;
134
}
135
}
136
error("%s: %s", dest, strerror(errno1));
137
/*NOTREACHED*/
138
return 0;
139
}
140
141
142
/*
143
* Actually change the directory. In an interactive shell, print the
144
* directory name if "print" is nonzero.
145
*/
146
static int
147
docd(char *dest, int print, int phys)
148
{
149
int rc;
150
151
TRACE(("docd(\"%s\", %d, %d) called\n", dest, print, phys));
152
153
/* If logical cd fails, fall back to physical. */
154
if ((phys || (rc = cdlogical(dest)) < 0) && (rc = cdphysical(dest)) < 0)
155
return (-1);
156
157
if (print && iflag && curdir) {
158
out1fmt("%s\n", curdir);
159
/*
160
* Ignore write errors to preserve the invariant that the
161
* current directory is changed iff the exit status is 0
162
* (or 1 if -e was given and the full pathname could not be
163
* determined).
164
*/
165
flushout(out1);
166
outclearerror(out1);
167
}
168
169
return (rc);
170
}
171
172
static int
173
cdlogical(char *dest)
174
{
175
char *p;
176
char *q;
177
char *component;
178
char *path;
179
struct stat statb;
180
int first;
181
int badstat;
182
183
/*
184
* Check each component of the path. If we find a symlink or
185
* something we can't stat, clear curdir to force a getcwd()
186
* next time we get the value of the current directory.
187
*/
188
badstat = 0;
189
path = stsavestr(dest);
190
STARTSTACKSTR(p);
191
if (*dest == '/') {
192
STPUTC('/', p);
193
path++;
194
}
195
first = 1;
196
while ((q = getcomponent(&path)) != NULL) {
197
if (q[0] == '\0' || (q[0] == '.' && q[1] == '\0'))
198
continue;
199
if (! first)
200
STPUTC('/', p);
201
first = 0;
202
component = q;
203
STPUTS(q, p);
204
if (equal(component, ".."))
205
continue;
206
STACKSTRNUL(p);
207
if (lstat(stackblock(), &statb) < 0) {
208
badstat = 1;
209
break;
210
}
211
}
212
213
INTOFF;
214
if ((p = findcwd(badstat ? NULL : dest)) == NULL || chdir(p) < 0) {
215
INTON;
216
return (-1);
217
}
218
updatepwd(p);
219
INTON;
220
return (0);
221
}
222
223
static int
224
cdphysical(char *dest)
225
{
226
char *p;
227
int rc = 0;
228
229
INTOFF;
230
if (chdir(dest) < 0) {
231
INTON;
232
return (-1);
233
}
234
p = findcwd(NULL);
235
if (p == NULL) {
236
warning("warning: failed to get name of current directory");
237
rc = 1;
238
}
239
updatepwd(p);
240
INTON;
241
return (rc);
242
}
243
244
/*
245
* Get the next component of the path name pointed to by *path.
246
* This routine overwrites *path and the string pointed to by it.
247
*/
248
static char *
249
getcomponent(char **path)
250
{
251
char *p;
252
char *start;
253
254
if ((p = *path) == NULL)
255
return NULL;
256
start = *path;
257
while (*p != '/' && *p != '\0')
258
p++;
259
if (*p == '\0') {
260
*path = NULL;
261
} else {
262
*p++ = '\0';
263
*path = p;
264
}
265
return start;
266
}
267
268
269
static char *
270
findcwd(char *dir)
271
{
272
char *new;
273
char *p;
274
char *path;
275
276
/*
277
* If our argument is NULL, we don't know the current directory
278
* any more because we traversed a symbolic link or something
279
* we couldn't stat().
280
*/
281
if (dir == NULL || curdir == NULL)
282
return getpwd2();
283
path = stsavestr(dir);
284
STARTSTACKSTR(new);
285
if (*dir != '/') {
286
STPUTS(curdir, new);
287
if (STTOPC(new) == '/')
288
STUNPUTC(new);
289
}
290
while ((p = getcomponent(&path)) != NULL) {
291
if (equal(p, "..")) {
292
while (new > stackblock() && (STUNPUTC(new), *new) != '/');
293
} else if (*p != '\0' && ! equal(p, ".")) {
294
STPUTC('/', new);
295
STPUTS(p, new);
296
}
297
}
298
if (new == stackblock())
299
STPUTC('/', new);
300
STACKSTRNUL(new);
301
return stackblock();
302
}
303
304
/*
305
* Update curdir (the name of the current directory) in response to a
306
* cd command. We also call hashcd to let the routines in exec.c know
307
* that the current directory has changed.
308
*/
309
static void
310
updatepwd(char *dir)
311
{
312
char *prevdir;
313
314
hashcd(); /* update command hash table */
315
316
setvar("PWD", dir, VEXPORT);
317
setvar("OLDPWD", curdir, VEXPORT);
318
prevdir = curdir;
319
curdir = dir ? savestr(dir) : NULL;
320
ckfree(prevdir);
321
}
322
323
int
324
pwdcmd(int argc __unused, char **argv __unused)
325
{
326
char *p;
327
int ch, phys;
328
329
phys = Pflag;
330
while ((ch = nextopt("LP")) != '\0') {
331
switch (ch) {
332
case 'L':
333
phys = 0;
334
break;
335
case 'P':
336
phys = 1;
337
break;
338
}
339
}
340
341
if (*argptr != NULL)
342
error("too many arguments");
343
344
if (!phys && getpwd()) {
345
out1str(curdir);
346
out1c('\n');
347
} else {
348
if ((p = getpwd2()) == NULL)
349
error(".: %s", strerror(errno));
350
out1str(p);
351
out1c('\n');
352
}
353
354
return 0;
355
}
356
357
/*
358
* Get the current directory and cache the result in curdir.
359
*/
360
static char *
361
getpwd(void)
362
{
363
char *p;
364
365
if (curdir)
366
return curdir;
367
368
p = getpwd2();
369
if (p != NULL) {
370
INTOFF;
371
curdir = savestr(p);
372
INTON;
373
}
374
375
return curdir;
376
}
377
378
#define MAXPWD 256
379
380
/*
381
* Return the current directory.
382
*/
383
static char *
384
getpwd2(void)
385
{
386
char *pwd;
387
int i;
388
389
for (i = MAXPWD;; i *= 2) {
390
pwd = stalloc(i);
391
if (getcwd(pwd, i) != NULL)
392
return pwd;
393
stunalloc(pwd);
394
if (errno != ERANGE)
395
break;
396
}
397
398
return NULL;
399
}
400
401
/*
402
* Initialize PWD in a new shell.
403
* If the shell is interactive, we need to warn if this fails.
404
*/
405
void
406
pwd_init(int warn)
407
{
408
char *pwd;
409
struct stat stdot, stpwd;
410
411
pwd = lookupvar("PWD");
412
if (pwd && *pwd == '/' && stat(".", &stdot) != -1 &&
413
stat(pwd, &stpwd) != -1 &&
414
stdot.st_dev == stpwd.st_dev &&
415
stdot.st_ino == stpwd.st_ino) {
416
if (curdir)
417
ckfree(curdir);
418
curdir = savestr(pwd);
419
}
420
if (getpwd() == NULL && warn)
421
out2fmt_flush("sh: cannot determine working directory\n");
422
setvar("PWD", curdir, VEXPORT);
423
}
424
425