sh: Implement the cd -e flag proposed for the next POSIX issue.
This reflects failure to determine the pathname of the new directory in the exit status (1). Normally, cd returns successfully if it did chdir() and the call was successful. In POSIX, -e only has meaning with -P; because our -L is not entirely compliant and may fall back to -P mode, -e has some effect with -L as well.
This commit is contained in:
parent
dd4aae65f1
commit
d6ee26ad02
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=222154
25
bin/sh/cd.c
25
bin/sh/cd.c
@ -84,12 +84,16 @@ cdcmd(int argc, char **argv)
|
||||
const char *path;
|
||||
char *p;
|
||||
struct stat statb;
|
||||
int ch, phys, print = 0;
|
||||
int ch, phys, print = 0, getcwderr = 0;
|
||||
int rc;
|
||||
|
||||
optreset = 1; optind = 1; opterr = 0; /* initialize getopt */
|
||||
phys = Pflag;
|
||||
while ((ch = getopt(argc, argv, "LP")) != -1) {
|
||||
while ((ch = getopt(argc, argv, "eLP")) != -1) {
|
||||
switch (ch) {
|
||||
case 'e':
|
||||
getcwderr = 1;
|
||||
break;
|
||||
case 'L':
|
||||
phys = 0;
|
||||
break;
|
||||
@ -131,8 +135,9 @@ cdcmd(int argc, char **argv)
|
||||
else
|
||||
print = strcmp(p, dest);
|
||||
}
|
||||
if (docd(p, print, phys) >= 0)
|
||||
return 0;
|
||||
rc = docd(p, print, phys);
|
||||
if (rc >= 0)
|
||||
return getcwderr ? rc : 0;
|
||||
}
|
||||
}
|
||||
error("can't cd to %s", dest);
|
||||
@ -148,17 +153,18 @@ cdcmd(int argc, char **argv)
|
||||
static int
|
||||
docd(char *dest, int print, int phys)
|
||||
{
|
||||
int rc;
|
||||
|
||||
TRACE(("docd(\"%s\", %d, %d) called\n", dest, print, phys));
|
||||
|
||||
/* If logical cd fails, fall back to physical. */
|
||||
if ((phys || cdlogical(dest) < 0) && cdphysical(dest) < 0)
|
||||
if ((phys || (rc = cdlogical(dest)) < 0) && (rc = cdphysical(dest)) < 0)
|
||||
return (-1);
|
||||
|
||||
if (print && iflag && curdir)
|
||||
out1fmt("%s\n", curdir);
|
||||
|
||||
return 0;
|
||||
return (rc);
|
||||
}
|
||||
|
||||
static int
|
||||
@ -216,6 +222,7 @@ static int
|
||||
cdphysical(char *dest)
|
||||
{
|
||||
char *p;
|
||||
int rc = 0;
|
||||
|
||||
INTOFF;
|
||||
if (chdir(dest) < 0) {
|
||||
@ -223,11 +230,13 @@ cdphysical(char *dest)
|
||||
return (-1);
|
||||
}
|
||||
p = findcwd(NULL);
|
||||
if (p == NULL)
|
||||
if (p == NULL) {
|
||||
warning("warning: failed to get name of current directory");
|
||||
rc = 1;
|
||||
}
|
||||
updatepwd(p);
|
||||
INTON;
|
||||
return (0);
|
||||
return (rc);
|
||||
}
|
||||
|
||||
/*
|
||||
|
13
bin/sh/sh.1
13
bin/sh/sh.1
@ -32,7 +32,7 @@
|
||||
.\" from: @(#)sh.1 8.6 (Berkeley) 5/4/95
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd May 8, 2011
|
||||
.Dd May 20, 2011
|
||||
.Dt SH 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
@ -1729,7 +1729,7 @@ Execute the specified built-in command,
|
||||
.Ar cmd .
|
||||
This is useful when the user wishes to override a shell function
|
||||
with the same name as a built-in command.
|
||||
.It Ic cd Oo Fl L | P Oc Op Ar directory
|
||||
.It Ic cd Oo Fl L | P Oc Oo Fl e Oc Op Ar directory
|
||||
Switch to the specified
|
||||
.Ar directory ,
|
||||
or to the directory specified in the
|
||||
@ -1778,6 +1778,15 @@ option is specified,
|
||||
.Pa ..
|
||||
is handled logically.
|
||||
This is the default.
|
||||
.Pp
|
||||
The
|
||||
.Fl e
|
||||
option causes
|
||||
.Ic cd
|
||||
to return exit status 1 if the full pathname of the new directory
|
||||
cannot be determined reliably or at all.
|
||||
Normally this is not considered an error,
|
||||
although a warning is printed.
|
||||
.It Ic chdir
|
||||
A synonym for the
|
||||
.Ic cd
|
||||
|
21
tools/regression/bin/sh/builtins/cd3.0
Normal file
21
tools/regression/bin/sh/builtins/cd3.0
Normal file
@ -0,0 +1,21 @@
|
||||
# $FreeBSD$
|
||||
|
||||
# If fully successful, cd -Pe must be like cd -P.
|
||||
|
||||
set -e
|
||||
|
||||
cd "${TMPDIR:-/tmp}"
|
||||
cd -Pe /
|
||||
[ "$PWD" = / ]
|
||||
[ "$(pwd)" = / ]
|
||||
cd "${TMPDIR:-/tmp}"
|
||||
cd -eP /
|
||||
[ "$PWD" = / ]
|
||||
[ "$(pwd)" = / ]
|
||||
|
||||
set +e
|
||||
|
||||
# If cd -Pe cannot chdir, the exit status must be greater than 1.
|
||||
|
||||
v=$( (cd -Pe /var/empty/nonexistent) 2>&1 >/dev/null)
|
||||
[ $? -gt 1 ] && [ -n "$v" ]
|
38
tools/regression/bin/sh/builtins/cd4.0
Normal file
38
tools/regression/bin/sh/builtins/cd4.0
Normal file
@ -0,0 +1,38 @@
|
||||
# $FreeBSD$
|
||||
|
||||
# This test assumes that whatever mechanism cd -P uses to determine the
|
||||
# pathname to the current directory if it is longer than PATH_MAX requires
|
||||
# read permission on all parent directories. It also works if this
|
||||
# requirement always applies.
|
||||
|
||||
set -e
|
||||
L=$(getconf PATH_MAX / 2>/dev/null) || L=4096
|
||||
[ "$L" -lt 100000 ] 2>/dev/null || L=4096
|
||||
L=$((L+100))
|
||||
T=$(mktemp -d ${TMPDIR:-/tmp}/sh-test.XXXXXX)
|
||||
trap 'chmod u+r ${T}; rm -rf ${T}' 0
|
||||
cd -Pe $T
|
||||
D=$(pwd)
|
||||
chmod u-r "$D"
|
||||
if [ -r "$D" ]; then
|
||||
# Running as root, cannot test.
|
||||
exit 0
|
||||
fi
|
||||
set +e
|
||||
while [ ${#D} -lt $L ]; do
|
||||
mkdir veryverylongdirectoryname || exit
|
||||
cd -Pe veryverylongdirectoryname 2>/dev/null
|
||||
r=$?
|
||||
[ $r -gt 1 ] && exit $r
|
||||
if [ $r -eq 1 ]; then
|
||||
# Verify that the directory was changed correctly.
|
||||
cd -Pe .. || exit
|
||||
[ "$(pwd)" = "$D" ] || exit
|
||||
# Verify that omitting -e results in success.
|
||||
cd -P veryverylongdirectoryname 2>/dev/null || exit
|
||||
exit 0
|
||||
fi
|
||||
D=$D/veryverylongdirectoryname
|
||||
done
|
||||
echo "cd -Pe never returned 1"
|
||||
exit 0
|
Loading…
Reference in New Issue
Block a user