hush: implement "silent" optstrings of ":opts"

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
Denys Vlasenko 2017-08-11 17:21:14 +02:00
parent 129e1ce72c
commit 419db0391e
5 changed files with 89 additions and 17 deletions

View File

@ -0,0 +1,6 @@
*** optstring:':ac' args:-a -b -c
1 rc:0 var:'a' OPTIND:2 OPTARG:''
2 rc:0 var:'?' OPTIND:3 OPTARG:'b'
3 rc:0 var:'c' OPTIND:4 OPTARG:''
4 rc:1 var:'?' OPTIND:4 OPTARG:''
5 rc:1 var:'?' OPTIND:4 OPTARG:''

View File

@ -0,0 +1,23 @@
# Open Group Base Specifications Issue 7:
# """
# If an unknown option is met, VAR shall be set to "?". In this case,
# if the first character in optstring is ":", OPTARG shall be set
# to the option character found, but no output shall be written
# to standard error; otherwise, the shell variable OPTARG shall be
# unset and a diagnostic message shall be written to standard error."
# ...
# If an option-argument is missing:
# If the first character of optstring is ":", VAR shall be set to ":"
# and OPTARG shall be set to the option character found.
# """
echo "*** optstring:':ac' args:-a -b -c"
getopts ":ac" var -a -b -c; echo "1 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'"
getopts ":ac" var -a -b -c; echo "2 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'"
getopts ":ac" var -a -b -c; echo "3 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'"
getopts ":ac" var -a -b -c; echo "4 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'"
# Previous line should result in "rc:1", which is normally treated
# in getopts loops as exit condition.
# Nevertheless, let's verify that calling it yet another time doesn't do
# anything weird:
getopts ":ac" var -a -b -c; echo "5 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'"

View File

@ -9877,11 +9877,6 @@ static int FAST_FUNC builtin_getopts(char **argv)
/* http://pubs.opengroup.org/onlinepubs/9699919799/utilities/getopts.html /* http://pubs.opengroup.org/onlinepubs/9699919799/utilities/getopts.html
TODO: TODO:
If an invalid option is seen, getopts places ? into VAR and, if
not silent, prints an error message and unsets OPTARG. If
getopts is silent, the option character found is placed in
OPTARG and no diagnostic message is printed.
If a required argument is not found, and getopts is not silent, If a required argument is not found, and getopts is not silent,
a question mark (?) is placed in VAR, OPTARG is unset, and a a question mark (?) is placed in VAR, OPTARG is unset, and a
diagnostic message is printed. If getopts is silent, then a diagnostic message is printed. If getopts is silent, then a
@ -9902,11 +9897,16 @@ Test that VAR is a valid variable name?
return EXIT_FAILURE; return EXIT_FAILURE;
} }
if (optstring[0] == ':') {
opterr = 0;
} else {
cp = get_local_var_value("OPTERR"); cp = get_local_var_value("OPTERR");
opterr = cp ? atoi(cp) : 1; opterr = cp ? atoi(cp) : 1;
}
cp = get_local_var_value("OPTIND"); cp = get_local_var_value("OPTIND");
optind = cp ? atoi(cp) : 0; optind = cp ? atoi(cp) : 0;
optarg = NULL; optarg = NULL;
cbuf[1] = '\0';
/* getopts stops on first non-option. Add "+" to force that */ /* getopts stops on first non-option. Add "+" to force that */
/*if (optstring[0] != '+')*/ { /*if (optstring[0] != '+')*/ {
@ -9920,25 +9920,39 @@ Test that VAR is a valid variable name?
else else
argv = G.global_argv; argv = G.global_argv;
c = getopt(string_array_len(argv), argv, optstring); c = getopt(string_array_len(argv), argv, optstring);
/* Set OPTARG */
/* Always set or unset, never left as-is, even on exit/error:
* "If no option was found, or if the option that was found
* does not have an option-argument, OPTARG shall be unset."
*/
cp = optarg;
if (c == '?') {
/* If ":optstring" and unknown option is seen,
* it is stored to OPTARG.
*/
if (optstring[1] == ':') {
cbuf[0] = optopt;
cp = cbuf;
}
}
if (cp)
set_local_var_from_halves("OPTARG", cp);
else
unset_local_var("OPTARG");
/* Convert -1 to "?" */
exitcode = EXIT_SUCCESS; exitcode = EXIT_SUCCESS;
if (c < 0) { /* -1: end of options */ if (c < 0) { /* -1: end of options */
exitcode = EXIT_FAILURE; exitcode = EXIT_FAILURE;
c = '?'; c = '?';
} }
/* Set OPTIND */
cbuf[0] = c; cbuf[0] = c;
cbuf[1] = '\0';
set_local_var_from_halves(var, cbuf); set_local_var_from_halves(var, cbuf);
set_local_var_from_halves("OPTIND", utoa(optind)); set_local_var_from_halves("OPTIND", utoa(optind));
/* Always set or unset, never left as-is, even on exit/error:
* "If no option was found, or if the option that was found
* does not have an option-argument, OPTARG shall be unset."
*/
if (optarg)
set_local_var_from_halves("OPTARG", optarg);
else
unset_local_var("OPTARG");
return exitcode; return exitcode;
} }
#endif #endif

View File

@ -0,0 +1,6 @@
*** optstring:':ac' args:-a -b -c
1 rc:0 var:'a' OPTIND:2 OPTARG:''
2 rc:0 var:'?' OPTIND:3 OPTARG:'b'
3 rc:0 var:'c' OPTIND:4 OPTARG:''
4 rc:1 var:'?' OPTIND:4 OPTARG:''
5 rc:1 var:'?' OPTIND:4 OPTARG:''

View File

@ -0,0 +1,23 @@
# Open Group Base Specifications Issue 7:
# """
# If an unknown option is met, VAR shall be set to "?". In this case,
# if the first character in optstring is ":", OPTARG shall be set
# to the option character found, but no output shall be written
# to standard error; otherwise, the shell variable OPTARG shall be
# unset and a diagnostic message shall be written to standard error."
# ...
# If an option-argument is missing:
# If the first character of optstring is ":", VAR shall be set to ":"
# and OPTARG shall be set to the option character found.
# """
echo "*** optstring:':ac' args:-a -b -c"
getopts ":ac" var -a -b -c; echo "1 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'"
getopts ":ac" var -a -b -c; echo "2 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'"
getopts ":ac" var -a -b -c; echo "3 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'"
getopts ":ac" var -a -b -c; echo "4 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'"
# Previous line should result in "rc:1", which is normally treated
# in getopts loops as exit condition.
# Nevertheless, let's verify that calling it yet another time doesn't do
# anything weird:
getopts ":ac" var -a -b -c; echo "5 rc:$? var:'$var' OPTIND:$OPTIND OPTARG:'$OPTARG'"