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:
Jilles Tjoelker 2011-05-20 22:55:18 +00:00
parent dd4aae65f1
commit d6ee26ad02
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=222154
4 changed files with 87 additions and 10 deletions

View File

@ -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);
}
/*

View File

@ -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

View 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" ]

View 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