Skip to content

Commit abf87cb

Browse files
authored
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.
1 parent f919df6 commit abf87cb

File tree

5 files changed

+111
-25
lines changed

5 files changed

+111
-25
lines changed

NEWS

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ Any uppercase BUG_* names are modernish shell bug IDs.
88
- Fixed an issue on illumos that caused some parameters in the getconf
99
builtin to fail.
1010

11+
- The cd built-in command now supports a -e option (as specified in
12+
https://www.austingroupbugs.net/view.php?id=253). Passing -e alongside -P
13+
is used to guarantee the cd built-in returns with exit status 1 if the
14+
current working directory couldn't be determined after successfully changing
15+
the directory.
16+
1117
2021-12-01:
1218

1319
- Fixed a memory fault that occurred when a discipline function exited

src/cmd/ksh93/bltins/cd_pwd.c

Lines changed: 35 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@
2020
***********************************************************************/
2121
#pragma prototyped
2222
/*
23-
* cd [-LP] [dirname]
24-
* cd [-LP] [old] [new]
23+
* cd [-L] [-Pe] [dirname]
24+
* cd [-L] [-Pe] [old] [new]
2525
* pwd [-LP]
2626
*
2727
* David Korn
@@ -57,33 +57,43 @@ int b_cd(int argc, char *argv[],Shbltin_t *context)
5757
register const char *dp;
5858
register Shell_t *shp = context->shp;
5959
int saverrno=0;
60-
int rval,flag=0;
60+
int rval,pflag=0,eflag=0,ret=1;
6161
char *oldpwd;
6262
Namval_t *opwdnod, *pwdnod;
63-
if(sh_isoption(SH_RESTRICTED))
64-
{
65-
errormsg(SH_DICT,ERROR_exit(1),e_restricted+4);
66-
UNREACHABLE();
67-
}
6863
while((rval = optget(argv,sh_optcd))) switch(rval)
6964
{
65+
case 'e':
66+
eflag = 1;
67+
break;
7068
case 'L':
71-
flag = 0;
69+
pflag = 0;
7270
break;
7371
case 'P':
74-
flag = 1;
72+
pflag = 1;
7573
break;
7674
case ':':
75+
if(sh_isoption(SH_RESTRICTED))
76+
break;
7777
errormsg(SH_DICT,2, "%s", opt_info.arg);
7878
break;
7979
case '?':
80+
if(sh_isoption(SH_RESTRICTED))
81+
break;
8082
errormsg(SH_DICT,ERROR_usage(2), "%s", opt_info.arg);
8183
UNREACHABLE();
8284
}
85+
if(pflag && eflag)
86+
ret = 2; /* exit status is 2 if -eP are both on and chdir failed */
87+
if(sh_isoption(SH_RESTRICTED))
88+
{
89+
/* restricted shells cannot change the directory */
90+
errormsg(SH_DICT,ERROR_exit(ret),e_restricted+4);
91+
UNREACHABLE();
92+
}
8393
argv += opt_info.index;
8494
argc -= opt_info.index;
8595
dir = argv[0];
86-
if(error_info.errors>0 || argc >2)
96+
if(error_info.errors>0 || argc>2)
8797
{
8898
errormsg(SH_DICT,ERROR_usage(2),"%s",optusage((char*)0));
8999
UNREACHABLE();
@@ -106,7 +116,7 @@ int b_cd(int argc, char *argv[],Shbltin_t *context)
106116
dir = nv_getval(opwdnod);
107117
if(!dir || *dir==0)
108118
{
109-
errormsg(SH_DICT,ERROR_exit(1),argc==2?e_subst+4:e_direct);
119+
errormsg(SH_DICT,ERROR_exit(ret),argc==2?e_subst+4:e_direct);
110120
UNREACHABLE();
111121
}
112122
/*
@@ -179,7 +189,7 @@ int b_cd(int argc, char *argv[],Shbltin_t *context)
179189
stakputs(last+PATH_OFFSET);
180190
stakputc(0);
181191
}
182-
if(!flag)
192+
if(!pflag)
183193
{
184194
register char *cp;
185195
stakseek(PATH_MAX+PATH_OFFSET);
@@ -200,19 +210,19 @@ int b_cd(int argc, char *argv[],Shbltin_t *context)
200210
{
201211
if(saverrno)
202212
errno = saverrno;
203-
errormsg(SH_DICT,ERROR_system(1),"%s:",dir);
213+
errormsg(SH_DICT,ERROR_system(ret),"%s:",dir);
204214
UNREACHABLE();
205215
}
206216
success:
207217
if(dir == nv_getval(opwdnod) || argc==2)
208218
dp = dir; /* print out directory for cd - */
209-
if(flag)
219+
if(pflag)
210220
{
211221
dir = stakptr(PATH_OFFSET);
212222
if (!(dir=pathcanon(dir,PATH_PHYSICAL)))
213223
{
214224
dir = stakptr(PATH_OFFSET);
215-
errormsg(SH_DICT,ERROR_system(1),"%s:",dir);
225+
errormsg(SH_DICT,ERROR_system(ret),"%s:",dir);
216226
UNREACHABLE();
217227
}
218228
stakseek(dir-stakptr(0));
@@ -223,10 +233,10 @@ int b_cd(int argc, char *argv[],Shbltin_t *context)
223233
nv_putval(opwdnod,oldpwd,NV_RDONLY);
224234
if(*dir == '/')
225235
{
226-
flag = strlen(dir);
236+
size_t len = strlen(dir);
227237
/* delete trailing '/' */
228-
while(--flag>0 && dir[flag]=='/')
229-
dir[flag] = 0;
238+
while(--len>0 && dir[len]=='/')
239+
dir[len] = 0;
230240
nv_putval(pwdnod,dir,NV_RDONLY);
231241
nv_onattr(pwdnod,NV_EXPORT);
232242
if(shp->pwd)
@@ -243,13 +253,18 @@ int b_cd(int argc, char *argv[],Shbltin_t *context)
243253
path_pwd(shp,0);
244254
if(*shp->pwd != '/')
245255
{
246-
errormsg(SH_DICT,ERROR_system(1),e_direct);
256+
errormsg(SH_DICT,ERROR_system(ret),e_direct);
247257
UNREACHABLE();
248258
}
249259
}
250260
nv_scan(sh_subtracktree(1),rehash,(void*)0,NV_TAGGED,NV_TAGGED);
251261
path_newdir(shp,shp->pathlist);
252262
path_newdir(shp,shp->cdpathlist);
263+
if(pflag && eflag)
264+
{
265+
/* Verify the current working directory matches $PWD */
266+
return(!test_inode(e_dot,nv_getval(pwdnod)));
267+
}
253268
return(0);
254269
}
255270

src/cmd/ksh93/data/builtins.c

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -441,7 +441,7 @@ const char sh_optbuiltin[] =
441441
;
442442

443443
const char sh_optcd[] =
444-
"[-1c?\n@(#)$Id: cd (ksh 93u+m) 2021-01-19 $\n]"
444+
"[-1c?\n@(#)$Id: cd (ksh 93u+m) 2021-12-02 $\n]"
445445
"[--catalog?" SH_DICT "]"
446446
"[+NAME?cd - change working directory ]"
447447
"[+DESCRIPTION?\bcd\b changes the current working directory of the "
@@ -482,13 +482,22 @@ const char sh_optcd[] =
482482
"[P?The present working directory is first converted to an absolute pathname "
483483
"that does not contain symbolic link components and symbolic name "
484484
"components are expanded in the resulting directory name.]"
485+
"[e?If the \b-P\b option is in effect and the correct \bPWD\b cannot be "
486+
"determined, exit with status 1. All other errors encountered while "
487+
"both \b-e\b and \b-P\b are active result in exit status >1 (i.e., "
488+
"exit status 2 unless an out of memory error occurred).]"
485489
"\n"
486490
"\n[directory]\n"
487491
"old new\n"
488492
"\n"
489493
"[+EXIT STATUS?]{"
490-
"[+0?Directory successfully changed.]"
491-
"[+>0?An error occurred.]"
494+
"[+0?Directory successfully changed and the \bPWD\b is correct.]"
495+
"[+0?Directory successfully changed, the \bPWD\b couldn't be obtained "
496+
"and a combination of \b-eP\b is not active.]"
497+
"[+>0?An error occurred and a combination of \b-eP\b is not active.]"
498+
"[+1?Directory successfully changed, the \bPWD\b couldn't be obtained "
499+
"and a combination of \b-eP\b is active.]"
500+
"[+>1?An error occurred and a combination of \b-eP\b is active.]"
492501
"}"
493502
"[+SEE ALSO?\bpwd\b(1), \bgetconf\b(1)]"
494503
;

src/cmd/ksh93/sh.1

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5803,9 +5803,9 @@ and invokes this function with an argument of
58035803
.BR 0 .
58045804
.TP
58055805
.PD 0
5806-
\f3cd\fP \*(OK \f3\-LP\fP \*(CK \*(OK \f2arg\^\fP \*(CK
5806+
\f3cd\fP \*(OK \f3\-L\fP \*(CK \*(OK \f3-eP\fP \*(CK \*(OK \f2arg\^\fP \*(CK
58075807
.TP
5808-
\f3cd\fP \*(OK \f3\-LP\fP \*(CK \f2old\^\fP \f2new\^\fP
5808+
\f3cd\fP \*(OK \f3\-L\fP \*(CK \*(OK \f3-eP\fP \*(CK \f2old\^\fP \f2new\^\fP
58095809
.PD
58105810
This command can be in either of two forms.
58115811
In the first form it
@@ -5877,6 +5877,20 @@ or
58775877
on the command line
58785878
determines which method is used.
58795879
.sp .5
5880+
If
5881+
.B \-e
5882+
and
5883+
.B \-P
5884+
are both in effect and the correct
5885+
.BR PWD
5886+
could not be determined after successfully changing the directory,
5887+
.B cd
5888+
will return with exit status one and produce no output.
5889+
If any other error occurs while both flags are active,
5890+
the exit status is greater than one.
5891+
This means the exit status is effectively
5892+
two unless an out of memory error occurs.
5893+
.sp .5
58805894
The
58815895
.B cd\^
58825896
command may not be executed by

src/cmd/ksh93/tests/builtins.sh

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1354,5 +1354,47 @@ if builtin rm 2> /dev/null; then
13541354
[[ -f $tmp/nonemptydir2/shouldexist || -d $tmp/nonemptydir2 ]] && err_exit 'rm builtin fails to remove all folders and files with -rd options'
13551355
fi
13561356
1357+
# ======
1358+
# These are regression tests for the cd command's -e and -P flags
1359+
mkdir -p "$tmp/failpwd1"
1360+
cd "$tmp/failpwd1"
1361+
rmdir ../failpwd1
1362+
cd -P .
1363+
got=$?; exp=0
1364+
(( got == exp )) || err_exit "cd -P without -e exits with error status if \$PWD doesn't exist (expected $exp, got $got)"
1365+
cd -eP .
1366+
got=$?; exp=1
1367+
(( got == exp )) || err_exit "cd -eP doesn't fail if \$PWD doesn't exist (expected $exp, got $got)"
1368+
cd "$tmp"
1369+
cd -P "$tmp/notadir" >/dev/null 2>&1
1370+
got=$?; exp=1
1371+
(( got == exp )) || err_exit "cd -P without -e fails with wrong exit status on nonexistent dir (expected $exp, got $got)"
1372+
cd -eP "$tmp/notadir" >/dev/null 2>&1
1373+
got=$?; exp=2
1374+
(( got == exp )) || err_exit "cd -eP fails with wrong exit status on nonexistent dir (expected $exp, got $got)"
1375+
OLDPWD="$tmp/baddir"
1376+
cd -P - >/dev/null 2>&1
1377+
got=$?; exp=1
1378+
(( got == exp )) || err_exit "cd -P without -e fails with wrong exit status on \$OLDPWD (expected $exp, got $got)"
1379+
cd -eP - >/dev/null 2>&1
1380+
got=$?; exp=2
1381+
(( got == exp )) || err_exit "cd -eP fails with wrong exit status on \$OLDPWD (expected $exp, got $got)"
1382+
cd "$tmp" || err_exit "couldn't change directory from nonexistent dir"
1383+
(set -o restricted; cd -P /) >/dev/null 2>&1
1384+
got=$?; exp=1
1385+
(( got == exp )) || err_exit "cd -P in restricted shell has wrong exit status (expected $exp, got $got)"
1386+
(set -o restricted; cd -eP /) >/dev/null 2>&1
1387+
got=$?; exp=2
1388+
(( got == exp )) || err_exit "cd -eP in restricted shell has wrong exit status (expected $exp, got $got)"
1389+
(set -o restricted; cd -?) >/dev/null 2>&1
1390+
got=$?; exp=1
1391+
(( got == exp )) || err_exit "cd -? shows usage info in restricted shell and has wrong exit status (expected $exp, got $got)"
1392+
(cd -P '') >/dev/null 2>&1
1393+
got=$?; exp=1
1394+
(( got == exp )) || err_exit "cd -P to empty string has wrong exit status (expected $exp, got $got)"
1395+
(cd -eP '') >/dev/null 2>&1
1396+
got=$?; exp=2
1397+
(( got == exp )) || err_exit "cd -eP to empty string has wrong exit status (expected $exp, got $got)"
1398+
13571399
# ======
13581400
exit $((Errors<125?Errors:125))

0 commit comments

Comments
 (0)