mirror of
git://git.code.sf.net/p/cdesktopenv/code
synced 2025-03-09 15:50:02 +00:00
Add the '-e' flag to the 'cd' builtin (#358)
This change adds the -e flag to the cd builtin, as specified in <https://www.austingroupbugs.net/view.php?id=253>. The -e flag is used to verify if the the current working directory after 'cd -P' successfully changes the directory, and returns with exit status 1 if the cwd couldn't be determined. Additionally, it causes all other errors to return with exit status >1 (i.e., status 2 unless ENOMEM occurs) if -e and -P are both active. src/cmd/ksh93/bltins/cd_pwd.c: - Add -e option to the cd builtin command. It verifies $PWD by using test_inode() to execute the equivalent of [[ . -ef $PWD ]]. - The check for restricted mode has been moved after optget to allow 'cd -eP' to return with exit status 2 when in restricted mode. To avoid changing the previous behavior of cd when -e isn't passed, extra checks have been added to prevent cd from printing usage information in restricted mode. src/cmd/ksh93/tests/builtins.sh: - Add regression tests for the exit status when using the cd -P flag with and without -e. src/cmd/ksh93/data/builtins.c, src/cmd/ksh93/sh.1: - Document the addition of -e to the cd builtin.
This commit is contained in:
parent
a3f4b5efd1
commit
cd8c48cc5a
5 changed files with 111 additions and 25 deletions
6
NEWS
6
NEWS
|
@ -8,6 +8,12 @@ Any uppercase BUG_* names are modernish shell bug IDs.
|
|||
- Fixed an issue on illumos that caused some parameters in the getconf
|
||||
builtin to fail.
|
||||
|
||||
- The cd built-in command now supports a -e option (as specified in
|
||||
https://www.austingroupbugs.net/view.php?id=253). Passing -e alongside -P
|
||||
is used to guarantee the cd built-in returns with exit status 1 if the
|
||||
current working directory couldn't be determined after successfully changing
|
||||
the directory.
|
||||
|
||||
2021-12-01:
|
||||
|
||||
- Fixed a memory fault that occurred when a discipline function exited
|
||||
|
|
|
@ -20,8 +20,8 @@
|
|||
***********************************************************************/
|
||||
#pragma prototyped
|
||||
/*
|
||||
* cd [-LP] [dirname]
|
||||
* cd [-LP] [old] [new]
|
||||
* cd [-L] [-Pe] [dirname]
|
||||
* cd [-L] [-Pe] [old] [new]
|
||||
* pwd [-LP]
|
||||
*
|
||||
* David Korn
|
||||
|
@ -57,33 +57,43 @@ int b_cd(int argc, char *argv[],Shbltin_t *context)
|
|||
register const char *dp;
|
||||
register Shell_t *shp = context->shp;
|
||||
int saverrno=0;
|
||||
int rval,flag=0;
|
||||
int rval,pflag=0,eflag=0,ret=1;
|
||||
char *oldpwd;
|
||||
Namval_t *opwdnod, *pwdnod;
|
||||
if(sh_isoption(SH_RESTRICTED))
|
||||
{
|
||||
errormsg(SH_DICT,ERROR_exit(1),e_restricted+4);
|
||||
UNREACHABLE();
|
||||
}
|
||||
while((rval = optget(argv,sh_optcd))) switch(rval)
|
||||
{
|
||||
case 'e':
|
||||
eflag = 1;
|
||||
break;
|
||||
case 'L':
|
||||
flag = 0;
|
||||
pflag = 0;
|
||||
break;
|
||||
case 'P':
|
||||
flag = 1;
|
||||
pflag = 1;
|
||||
break;
|
||||
case ':':
|
||||
if(sh_isoption(SH_RESTRICTED))
|
||||
break;
|
||||
errormsg(SH_DICT,2, "%s", opt_info.arg);
|
||||
break;
|
||||
case '?':
|
||||
if(sh_isoption(SH_RESTRICTED))
|
||||
break;
|
||||
errormsg(SH_DICT,ERROR_usage(2), "%s", opt_info.arg);
|
||||
UNREACHABLE();
|
||||
}
|
||||
if(pflag && eflag)
|
||||
ret = 2; /* exit status is 2 if -eP are both on and chdir failed */
|
||||
if(sh_isoption(SH_RESTRICTED))
|
||||
{
|
||||
/* restricted shells cannot change the directory */
|
||||
errormsg(SH_DICT,ERROR_exit(ret),e_restricted+4);
|
||||
UNREACHABLE();
|
||||
}
|
||||
argv += opt_info.index;
|
||||
argc -= opt_info.index;
|
||||
dir = argv[0];
|
||||
if(error_info.errors>0 || argc >2)
|
||||
if(error_info.errors>0 || argc>2)
|
||||
{
|
||||
errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0));
|
||||
UNREACHABLE();
|
||||
|
@ -106,7 +116,7 @@ int b_cd(int argc, char *argv[],Shbltin_t *context)
|
|||
dir = nv_getval(opwdnod);
|
||||
if(!dir || *dir==0)
|
||||
{
|
||||
errormsg(SH_DICT,ERROR_exit(1),argc==2?e_subst+4:e_direct);
|
||||
errormsg(SH_DICT,ERROR_exit(ret),argc==2?e_subst+4:e_direct);
|
||||
UNREACHABLE();
|
||||
}
|
||||
/*
|
||||
|
@ -179,7 +189,7 @@ int b_cd(int argc, char *argv[],Shbltin_t *context)
|
|||
stakputs(last+PATH_OFFSET);
|
||||
stakputc(0);
|
||||
}
|
||||
if(!flag)
|
||||
if(!pflag)
|
||||
{
|
||||
register char *cp;
|
||||
stakseek(PATH_MAX+PATH_OFFSET);
|
||||
|
@ -200,19 +210,19 @@ int b_cd(int argc, char *argv[],Shbltin_t *context)
|
|||
{
|
||||
if(saverrno)
|
||||
errno = saverrno;
|
||||
errormsg(SH_DICT,ERROR_system(1),"%s:",dir);
|
||||
errormsg(SH_DICT,ERROR_system(ret),"%s:",dir);
|
||||
UNREACHABLE();
|
||||
}
|
||||
success:
|
||||
if(dir == nv_getval(opwdnod) || argc==2)
|
||||
dp = dir; /* print out directory for cd - */
|
||||
if(flag)
|
||||
if(pflag)
|
||||
{
|
||||
dir = stakptr(PATH_OFFSET);
|
||||
if (!(dir=pathcanon(dir,PATH_PHYSICAL)))
|
||||
{
|
||||
dir = stakptr(PATH_OFFSET);
|
||||
errormsg(SH_DICT,ERROR_system(1),"%s:",dir);
|
||||
errormsg(SH_DICT,ERROR_system(ret),"%s:",dir);
|
||||
UNREACHABLE();
|
||||
}
|
||||
stakseek(dir-stakptr(0));
|
||||
|
@ -223,10 +233,10 @@ success:
|
|||
nv_putval(opwdnod,oldpwd,NV_RDONLY);
|
||||
if(*dir == '/')
|
||||
{
|
||||
flag = strlen(dir);
|
||||
size_t len = strlen(dir);
|
||||
/* delete trailing '/' */
|
||||
while(--flag>0 && dir[flag]=='/')
|
||||
dir[flag] = 0;
|
||||
while(--len>0 && dir[len]=='/')
|
||||
dir[len] = 0;
|
||||
nv_putval(pwdnod,dir,NV_RDONLY);
|
||||
nv_onattr(pwdnod,NV_EXPORT);
|
||||
if(shp->pwd)
|
||||
|
@ -243,13 +253,18 @@ success:
|
|||
path_pwd(shp,0);
|
||||
if(*shp->pwd != '/')
|
||||
{
|
||||
errormsg(SH_DICT,ERROR_system(1),e_direct);
|
||||
errormsg(SH_DICT,ERROR_system(ret),e_direct);
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
nv_scan(sh_subtracktree(1),rehash,(void*)0,NV_TAGGED,NV_TAGGED);
|
||||
path_newdir(shp,shp->pathlist);
|
||||
path_newdir(shp,shp->cdpathlist);
|
||||
if(pflag && eflag)
|
||||
{
|
||||
/* Verify the current working directory matches $PWD */
|
||||
return(!test_inode(e_dot,nv_getval(pwdnod)));
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
|
|
@ -441,7 +441,7 @@ const char sh_optbuiltin[] =
|
|||
;
|
||||
|
||||
const char sh_optcd[] =
|
||||
"[-1c?\n@(#)$Id: cd (ksh 93u+m) 2021-01-19 $\n]"
|
||||
"[-1c?\n@(#)$Id: cd (ksh 93u+m) 2021-12-02 $\n]"
|
||||
"[--catalog?" SH_DICT "]"
|
||||
"[+NAME?cd - change working directory ]"
|
||||
"[+DESCRIPTION?\bcd\b changes the current working directory of the "
|
||||
|
@ -482,13 +482,22 @@ const char sh_optcd[] =
|
|||
"[P?The present working directory is first converted to an absolute pathname "
|
||||
"that does not contain symbolic link components and symbolic name "
|
||||
"components are expanded in the resulting directory name.]"
|
||||
"[e?If the \b-P\b option is in effect and the correct \bPWD\b cannot be "
|
||||
"determined, exit with status 1. All other errors encountered while "
|
||||
"both \b-e\b and \b-P\b are active result in exit status >1 (i.e., "
|
||||
"exit status 2 unless an out of memory error occurred).]"
|
||||
"\n"
|
||||
"\n[directory]\n"
|
||||
"old new\n"
|
||||
"\n"
|
||||
"[+EXIT STATUS?]{"
|
||||
"[+0?Directory successfully changed.]"
|
||||
"[+>0?An error occurred.]"
|
||||
"[+0?Directory successfully changed and the \bPWD\b is correct.]"
|
||||
"[+0?Directory successfully changed, the \bPWD\b couldn't be obtained "
|
||||
"and a combination of \b-eP\b is not active.]"
|
||||
"[+>0?An error occurred and a combination of \b-eP\b is not active.]"
|
||||
"[+1?Directory successfully changed, the \bPWD\b couldn't be obtained "
|
||||
"and a combination of \b-eP\b is active.]"
|
||||
"[+>1?An error occurred and a combination of \b-eP\b is active.]"
|
||||
"}"
|
||||
"[+SEE ALSO?\bpwd\b(1), \bgetconf\b(1)]"
|
||||
;
|
||||
|
|
|
@ -5772,9 +5772,9 @@ and invokes this function with an argument of
|
|||
.BR 0 .
|
||||
.TP
|
||||
.PD 0
|
||||
\f3cd\fP \*(OK \f3\-LP\fP \*(CK \*(OK \f2arg\^\fP \*(CK
|
||||
\f3cd\fP \*(OK \f3\-L\fP \*(CK \*(OK \f3-eP\fP \*(CK \*(OK \f2arg\^\fP \*(CK
|
||||
.TP
|
||||
\f3cd\fP \*(OK \f3\-LP\fP \*(CK \f2old\^\fP \f2new\^\fP
|
||||
\f3cd\fP \*(OK \f3\-L\fP \*(CK \*(OK \f3-eP\fP \*(CK \f2old\^\fP \f2new\^\fP
|
||||
.PD
|
||||
This command can be in either of two forms.
|
||||
In the first form it
|
||||
|
@ -5846,6 +5846,20 @@ or
|
|||
on the command line
|
||||
determines which method is used.
|
||||
.sp .5
|
||||
If
|
||||
.B \-e
|
||||
and
|
||||
.B \-P
|
||||
are both in effect and the correct
|
||||
.BR PWD
|
||||
could not be determined after successfully changing the directory,
|
||||
.B cd
|
||||
will return with exit status one and produce no output.
|
||||
If any other error occurs while both flags are active,
|
||||
the exit status is greater than one.
|
||||
This means the exit status is effectively
|
||||
two unless an out of memory error occurs.
|
||||
.sp .5
|
||||
The
|
||||
.B cd\^
|
||||
command may not be executed by
|
||||
|
|
|
@ -1354,5 +1354,47 @@ if builtin rm 2> /dev/null; then
|
|||
[[ -f $tmp/nonemptydir2/shouldexist || -d $tmp/nonemptydir2 ]] && err_exit 'rm builtin fails to remove all folders and files with -rd options'
|
||||
fi
|
||||
|
||||
# ======
|
||||
# These are regression tests for the cd command's -e and -P flags
|
||||
mkdir -p "$tmp/failpwd1"
|
||||
cd "$tmp/failpwd1"
|
||||
rmdir ../failpwd1
|
||||
cd -P .
|
||||
got=$?; exp=0
|
||||
(( got == exp )) || err_exit "cd -P without -e exits with error status if \$PWD doesn't exist (expected $exp, got $got)"
|
||||
cd -eP .
|
||||
got=$?; exp=1
|
||||
(( got == exp )) || err_exit "cd -eP doesn't fail if \$PWD doesn't exist (expected $exp, got $got)"
|
||||
cd "$tmp"
|
||||
cd -P "$tmp/notadir" >/dev/null 2>&1
|
||||
got=$?; exp=1
|
||||
(( got == exp )) || err_exit "cd -P without -e fails with wrong exit status on nonexistent dir (expected $exp, got $got)"
|
||||
cd -eP "$tmp/notadir" >/dev/null 2>&1
|
||||
got=$?; exp=2
|
||||
(( got == exp )) || err_exit "cd -eP fails with wrong exit status on nonexistent dir (expected $exp, got $got)"
|
||||
OLDPWD="$tmp/baddir"
|
||||
cd -P - >/dev/null 2>&1
|
||||
got=$?; exp=1
|
||||
(( got == exp )) || err_exit "cd -P without -e fails with wrong exit status on \$OLDPWD (expected $exp, got $got)"
|
||||
cd -eP - >/dev/null 2>&1
|
||||
got=$?; exp=2
|
||||
(( got == exp )) || err_exit "cd -eP fails with wrong exit status on \$OLDPWD (expected $exp, got $got)"
|
||||
cd "$tmp" || err_exit "couldn't change directory from nonexistent dir"
|
||||
(set -o restricted; cd -P /) >/dev/null 2>&1
|
||||
got=$?; exp=1
|
||||
(( got == exp )) || err_exit "cd -P in restricted shell has wrong exit status (expected $exp, got $got)"
|
||||
(set -o restricted; cd -eP /) >/dev/null 2>&1
|
||||
got=$?; exp=2
|
||||
(( got == exp )) || err_exit "cd -eP in restricted shell has wrong exit status (expected $exp, got $got)"
|
||||
(set -o restricted; cd -?) >/dev/null 2>&1
|
||||
got=$?; exp=1
|
||||
(( got == exp )) || err_exit "cd -? shows usage info in restricted shell and has wrong exit status (expected $exp, got $got)"
|
||||
(cd -P '') >/dev/null 2>&1
|
||||
got=$?; exp=1
|
||||
(( got == exp )) || err_exit "cd -P to empty string has wrong exit status (expected $exp, got $got)"
|
||||
(cd -eP '') >/dev/null 2>&1
|
||||
got=$?; exp=2
|
||||
(( got == exp )) || err_exit "cd -eP to empty string has wrong exit status (expected $exp, got $got)"
|
||||
|
||||
# ======
|
||||
exit $((Errors<125?Errors:125))
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue