Skip to content

Commit 2172834

Browse files
committed
Implement POSIX mode for the getopts built-in
When a POSIX shell script that uses getopts is passed one of the AST self-documentation options (like --help), then the script aborts and ksh prints a usage message generated from the options string, which will not contain any self-documentation if it's a POSIX script. The script is prevented from throwing an error and showing its own usage message, which may be a lot more informative. This is a clear incompatibility with the POSIX spec that should be fixed in the POSIX mode. We can't fix this purely in ksh93; the libast optget function also needs to learn about the POSIX mode. src/lib/libast/include/option.h, src/lib/libast/misc/optlib.h, src/lib/libast/misc/optget.c: - Remove useless padding from Optstate_t; it was ostensibly for ABI compatibility with old libast versions, but we abandoned that. - Add a 'posix' flag to Optstate_t, so ksh93 can set opt_info.posix to activate the POSIX mode in optget. - optget(): - If the posix flag is set, only process short options with a single '-' prefix, as specified by POSIX. This disables the AST self-doc options, too. - Let the posix flag disable special handling of '-?' as well. src/cmd/ksh93/bltins/getopts.c: - b_getopts(): Set the opt_info.posix flag to 1 if the 'posix' shell option is on *and* the options string passed does not use the extended ksh interface (as flagged by the 'extended' variable). This way, scripts running in POSIX mode may still optionally offer ksh-style long options and self-documentation (for example, many of ksh's own build scripts do this).
1 parent a3aae7f commit 2172834

7 files changed

Lines changed: 42 additions & 15 deletions

File tree

‎NEWS‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@ Uppercase BUG_* IDs are shell bug IDs as used by the Modernish shell library.
99
such as --help or --man, 'getopts' will no longer stop the execution of any
1010
commands that follow the function call on the same command line.
1111

12+
- The shell's POSIX mode has been amended to disable long options (including
13+
--help, etc., which would abort the script), which are incompatible with
14+
the POSIX spec and may prevent POSIX scripts from printing their own usage
15+
message. This is only disabled if the --posix option is on *and* getopts is
16+
given an options string that does not use ksh's extended getopts interface.
17+
1218
2026-04-22:
1319

1420
- Fixed a bug that causes the loss of the contents of all here-documents

‎src/cmd/ksh93/bltins/getopts.c‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,13 +115,15 @@ int b_getopts(int argc,char *argv[],Shbltin_t *context)
115115
{
116116
sh_popcontext(&buff);
117117
sh.st.opterror = 1;
118+
opt_info.posix = 0;
118119
if(r==0)
119120
return 2;
120121
pp = (struct checkpt*)sh.jmplist;
121122
pp->mode = longjmpmode;
122123
sh_exit(2);
123124
}
124125
opt_info.disc = &disc;
126+
opt_info.posix = sh_isoption(SH_POSIX) && !extended;
125127
switch(opt_info.index>=0 && opt_info.index<=argc?(opt_info.num= LONG_MIN,flag=optget(argv,options)):0)
126128
{
127129
case '?':
@@ -210,5 +212,6 @@ int b_getopts(int argc,char *argv[],Shbltin_t *context)
210212
nv_putval(np, opt_info.arg, NV_RDONLY);
211213
sh_popcontext(&buff);
212214
opt_info.disc = 0;
215+
opt_info.posix = 0;
213216
return r;
214217
}

‎src/cmd/ksh93/sh.1‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6852,7 +6852,7 @@ is used for each of the parameters that requires
68526852
\f3getopts\fP \*(OK \f3\-a\fP \f2name\^\fP \*(CK \f2optstring vname\^\fP \*(OK \f2arg\^\fP .\|.\|. \*(CK
68536853
Checks
68546854
.I arg
6855-
for legal options.
6855+
for valid options.
68566856
If
68576857
.I arg
68586858
is omitted,
@@ -8137,6 +8137,11 @@ or
81378137
.B TERM
81388138
signal;
81398139
.IP \[bu]
8140+
causes the \fBgetopts\fR built-in command to disable the built-in handling
8141+
of \fB-?\fR and long-form options (including the self-documentation options
8142+
such as \fB--help\fR), unless the options string given to \fBgetopts\fR uses
8143+
the extended interface;
8144+
.IP \[bu]
81408145
disables the special handling of \f3/dev/fd/\fP\f2n\fP in file existence and
81418146
access test operators in \fBtest\fR/\fB[\fR and \fB[[\fR, causing these to
81428147
operate on the file \f3/dev/fd/\fP\f2n\fP in the file system;

‎src/cmd/ksh93/tests/posix.sh‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,14 @@ for c in a e f g A E F G
248248
do printf "%$c" 1.5 2>/dev/null || err_exit "POSIX printf %$c fails to accept floating point operand"
249249
done >/dev/null
250250
251+
# causes the getopts built-in command to disable the built-in handling of '-?' and long-form
252+
# options (including the self-documentation options such as --help), unless the options string
253+
# given to getopts uses the extended interface;
254+
got=$("$SHELL" -o posix -c 'while getopts x o; do case $o in \?) echo "MY USAGE MESSAGE";; esac; done' sh -\? 2>&1)
255+
[[ $got == *$'\nMY USAGE MESSAGE' ]] || err_exit "POSIX getopts fails to let script handle -? itself (got $(printf %q "$got"))"
256+
got=$("$SHELL" -o posix -c 'while getopts x o; do case $o in \?) echo "MY USAGE MESSAGE";; esac; done' sh --help 2>&1)
257+
[[ $got == *$'\nMY USAGE MESSAGE' ]] || err_exit "POSIX getopts fails to let script handle --help itself (got $(printf %q "$got"))"
258+
251259
# changes the test/[ built-in command to make its deprecated expr1 -a expr2 and expr1 -o expr2 operators work
252260
# even if expr1 equals "!" or "(" (which means the nonstandard unary -a file and -o option operators cannot
253261
# be directly negated using ! or wrapped in parentheses);

‎src/lib/libast/include/option.h‎

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,8 @@ typedef struct Optdisc_s
4646
Optinfo_f infof; /* runtime info function */
4747
} Optdisc_t;
4848

49-
/* NOTE: Opt_t member order fixed by a previous binary release */
50-
5149
#ifndef _OPT_PRIVATE_
52-
#define _OPT_PRIVATE_ \
53-
char pad[3*sizeof(void*)];
50+
#define _OPT_PRIVATE_
5451
#endif
5552

5653
typedef struct Opt_s
@@ -67,7 +64,7 @@ typedef struct Opt_s
6764
Optdisc_t* disc; /* user discipline */
6865
intmax_t number; /* # numeric argument */
6966
unsigned char assignment; /* option arg assignment op */
70-
unsigned char pads[sizeof(void*)-1];
67+
char posix; /* set for POSIX compliance */
7168
_OPT_PRIVATE_
7269
} Opt_t;
7370

‎src/lib/libast/misc/optget.c‎

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -323,12 +323,7 @@ static Msg_t C_LC_MESSAGES_libast[] =
323323
{ C("version") },
324324
};
325325

326-
/*
327-
* 2007-03-19 move opt_info from _opt_info_ to (*_opt_data_)
328-
* to allow future Opt_t growth
329-
*/
330-
331-
static Opt_t _opt_info_ = { 0,0,0,0,0,0,0,{0},{0},0,0,0,{0},{0},&state };
326+
static Opt_t _opt_info_ = { 0,NULL,NULL,0,NULL,0,0,{0},{0},NULL,0,0,0,&state };
332327
Opt_t* _opt_infop_ = &_opt_info_;
333328

334329
Optstate_t*
@@ -4404,7 +4399,21 @@ optget(char** argv, const char* oopts)
44044399
}
44054400
if (!(s = argv[opt_info.index]))
44064401
return 0;
4407-
if (!prefix)
4402+
if (opt_info.posix)
4403+
{
4404+
/*
4405+
* POSIX mode: only short options with single '-' prefix are recognized
4406+
*/
4407+
4408+
if (!prefix || *s != '-')
4409+
{
4410+
opt_info.index++;
4411+
return 0;
4412+
}
4413+
prefix = 1;
4414+
n = 1;
4415+
}
4416+
else if (!prefix)
44084417
{
44094418
/*
44104419
* long with no prefix (dd style)
@@ -4552,7 +4561,7 @@ optget(char** argv, const char* oopts)
45524561
* v long option value (via =) if w != 0
45534562
*/
45544563

4555-
if (c == '?')
4564+
if (c == '?' && !opt_info.posix)
45564565
{
45574566
/*
45584567
* ? always triggers internal help

‎src/lib/libast/misc/optlib.h‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,6 @@ typedef struct Optstate_s
103103
} Optstate_t;
104104

105105
#define _OPT_PRIVATE_ \
106-
char pad[2*sizeof(void*)]; \
107106
Optstate_t* state;
108107

109108
#include <error.h>

0 commit comments

Comments
 (0)