mirror of
https://codeberg.org/landley/toybox.git
synced 2026-01-26 06:07:55 +00:00
Add getopt(1).
Includes new tests.
This commit is contained in:
parent
176c6fa458
commit
c77b664557
@ -105,6 +105,9 @@ static inline char *basename(char *path) { return __xpg_basename(path); }
|
||||
char *strcasestr(const char *haystack, const char *needle);
|
||||
#endif // defined(glibc)
|
||||
|
||||
// getopt_long(), getopt_long_only(), and struct option.
|
||||
#include <getopt.h>
|
||||
|
||||
#if !defined(__GLIBC__)
|
||||
// POSIX basename.
|
||||
#include <libgen.h>
|
||||
|
||||
44
tests/getopt.test
Executable file
44
tests/getopt.test
Executable file
@ -0,0 +1,44 @@
|
||||
#!/bin/bash
|
||||
|
||||
[ -f testing.sh ] && . testing.sh
|
||||
|
||||
#testing "name" "command" "result" "infile" "stdin"
|
||||
|
||||
function test_getopt() {
|
||||
testcmd "$1" "$1" "$2\n" "" ""
|
||||
}
|
||||
|
||||
# Traditional behavior was to take the first argument as OPTSTR and not quote.
|
||||
test_getopt "a b c" " -- b c"
|
||||
test_getopt "a -a b c" " -a -- b c"
|
||||
test_getopt "a -- -a b c" " -- -a b c"
|
||||
|
||||
# Modern -o mode.
|
||||
test_getopt "-o a -- " " --"
|
||||
test_getopt "-o a -- -a b c" " -a -- 'b' 'c'"
|
||||
test_getopt "-o a: -- -a b c" " -a 'b' -- 'c'"
|
||||
|
||||
# Long options (--like --this).
|
||||
test_getopt "-o a -l long -- -a --long a" " -a --long -- 'a'"
|
||||
test_getopt "-o a -l one -l two -- -a --one --two" " -a --one --two --"
|
||||
test_getopt "-o a -l one,two -- -a --one --two" " -a --one --two --"
|
||||
# -l arg: (required)
|
||||
test_getopt "-o a -l one: -- -a --one arg" " -a --one 'arg' --"
|
||||
# -l arg:: (optional)
|
||||
test_getopt "-o a -l one:: -- -a --one" " -a --one '' --"
|
||||
test_getopt "-o a -l one:: -- -a --one arg" " -a --one '' -- 'arg'"
|
||||
test_getopt "-o a -l one:: -- -a --one=arg" " -a --one 'arg' --"
|
||||
|
||||
# "Alternative" long options (-like -this but also --like --this).
|
||||
test_getopt "-o a -a -l long -- -long --long a" " --long --long -- 'a'"
|
||||
|
||||
# -u lets you avoid quoting even with modern -o.
|
||||
test_getopt "-u -o a: -- -a b c" " -a b -- c"
|
||||
|
||||
# Do we quote quotes right?
|
||||
test_getopt "-o a -- \"it\'s\"" " -- 'it\'\''s'"
|
||||
test_getopt "-o a -u -- \"it\'s\"" " -- it\'s"
|
||||
|
||||
# Odds and ends.
|
||||
testcmd "-T" "-T ; echo \$?" "4\n" "" ""
|
||||
testcmd "-n" "-n unlikely a -x 2>&1 | grep -o unlikely:" "unlikely:\n" "" ""
|
||||
104
toys/pending/getopt.c
Normal file
104
toys/pending/getopt.c
Normal file
@ -0,0 +1,104 @@
|
||||
/* getopt.c - Parse command-line options
|
||||
*
|
||||
* Copyright 2019 The Android Open Source Project
|
||||
|
||||
USE_GETOPT(NEWTOY(getopt, "^(alternative)a(name)n:(options)o:(long)(longoptions)l*Tu", TOYFLAG_USR|TOYFLAG_BIN))
|
||||
|
||||
config GETOPT
|
||||
bool "getopt"
|
||||
default y
|
||||
help
|
||||
usage: getopt [OPTIONS] [--] ARG...
|
||||
|
||||
Parse command-line options for use in shell scripts.
|
||||
|
||||
-a Allow long options starting with a single -.
|
||||
-l OPTS Specify long options.
|
||||
-n NAME Command name for error messages.
|
||||
-o OPTS Specify short options.
|
||||
-T Test whether this is a modern getopt.
|
||||
-u Output options unquoted.
|
||||
*/
|
||||
|
||||
#define FOR_getopt
|
||||
#include "toys.h"
|
||||
|
||||
GLOBALS(
|
||||
struct arg_list *l;
|
||||
char *o, *n;
|
||||
)
|
||||
|
||||
static void out(char *s)
|
||||
{
|
||||
if (FLAG(u)) printf(" %s", s);
|
||||
else {
|
||||
printf(" '");
|
||||
for (; *s; s++) {
|
||||
if (*s == '\'') printf("'\\''");
|
||||
else putchar(*s);
|
||||
}
|
||||
printf("'");
|
||||
}
|
||||
}
|
||||
|
||||
static char *parse_long_opt(void *data, char *str, int len)
|
||||
{
|
||||
struct option **lopt_ptr = data, *lopt = *lopt_ptr;
|
||||
|
||||
// Trailing : or :: means this option takes a required or optional argument.
|
||||
// no_argument = 0, required_argument = 1, optional_argument = 2.
|
||||
for (lopt->has_arg = 0; len>0 && str[len-1] == ':'; lopt->has_arg++) len--;
|
||||
if (!len || lopt->has_arg>2) return str;
|
||||
|
||||
lopt->name = xstrndup(str, len);
|
||||
|
||||
(*lopt_ptr)++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void getopt_main(void)
|
||||
{
|
||||
int argc = toys.optc+1;
|
||||
char **argv = xzalloc(sizeof(char *)*(argc+1));
|
||||
struct option *lopts = xzalloc(sizeof(struct option)*argc), *lopt = lopts;
|
||||
int i = 0, j = 0, ch;
|
||||
|
||||
if (FLAG(T)) {
|
||||
toys.exitval = 4;
|
||||
return;
|
||||
}
|
||||
|
||||
comma_args(TT.l, &lopt, "bad -l", parse_long_opt);
|
||||
argv[j++] = TT.n ? TT.n : "getopt";
|
||||
|
||||
// Legacy mode: don't quote output and take the first argument as OPTSTR.
|
||||
if (!FLAG(o)) {
|
||||
toys.optflags |= FLAG_u;
|
||||
TT.o = toys.optargs[i++];
|
||||
if (!TT.o) error_exit("no OPTSTR");
|
||||
--argc;
|
||||
}
|
||||
|
||||
while (i<toys.optc) argv[j++] = toys.optargs[i++];
|
||||
|
||||
// BSD getopts don't honor argv[0] (for -n), so handle errors ourselves.
|
||||
opterr = 0;
|
||||
optind = 1;
|
||||
while ((ch = (FLAG(a)?getopt_long_only:getopt_long)(argc, argv, TT.o,
|
||||
lopts, &i)) != -1) {
|
||||
if (ch == '?') {
|
||||
fprintf(stderr, "%s: invalid option '%c'\n", argv[0], optopt);
|
||||
toys.exitval = 1;
|
||||
} else if (!ch) {
|
||||
printf(" --%s", lopts[i].name);
|
||||
if (lopts[i].has_arg) out(optarg ? optarg : "");
|
||||
} else {
|
||||
printf(" -%c", ch);
|
||||
if (optarg) out(optarg);
|
||||
}
|
||||
}
|
||||
|
||||
printf(" --");
|
||||
for (; optind<argc; optind++) out(argv[optind]);
|
||||
printf("\n");
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user