From cad65de2a9a9b7d29b98f0d2997772c057f92e29 Mon Sep 17 00:00:00 2001 From: David Oberhollenzer Date: Wed, 24 Mar 2021 16:21:24 +0100 Subject: Provide Musl derived fallbacks for getopt/getopt_long/getsubopt Signed-off-by: David Oberhollenzer --- COPYING.md | 10 ++++- configure.ac | 2 +- include/compat.h | 25 +++++++++++++ lib/compat/Makemodule.am | 2 + lib/compat/getopt.c | 96 ++++++++++++++++++++++++++++++++++++++++++++++++ lib/compat/getopt_long.c | 95 +++++++++++++++++++++++++++++++++++++++++++++++ lib/compat/getsubopt.c | 42 ++++++--------------- 7 files changed, 239 insertions(+), 33 deletions(-) create mode 100644 lib/compat/getopt.c create mode 100644 lib/compat/getopt_long.c diff --git a/COPYING.md b/COPYING.md index 107fabd..19f8c5d 100644 --- a/COPYING.md +++ b/COPYING.md @@ -22,8 +22,14 @@ The rest of squashfs-tools-ng is released under the terms and conditions of the **GNU General Public License version 3 or later**, with the following exceptions: - - `lib/compat/fnmatch.c` has been copied from Musl libc, which is subject to - an MIT style license. See `liceneses/musl.txt` for details. + - `lib/compat/fnmatch.c` has been copied from Musl libc. + - `lib/compat/getopt.c` has been copied from Musl libc. + - `lib/compat/getopt_long.c` has been copied from Musl libc. + - `lib/compat/getsubopt.c` has been copied from Musl libc. + +The components copied from Musl libc are subejct to an MIT style license. +See `liceneses/musl.txt` for details and only compiled into executable programs +if the target system does not provide an implementation. Copies of the LGPLv3 and GPLv3 are included in `licenses/LGPLv3.txt` and `licenses/GPLv3.txt` respectively. diff --git a/configure.ac b/configure.ac index 593734b..ddef2c5 100644 --- a/configure.ac +++ b/configure.ac @@ -269,7 +269,7 @@ AC_CHECK_HEADERS([sys/xattr.h], [], []) AC_CHECK_HEADERS([sys/sysinfo.h], [], []) AC_CHECK_HEADERS([alloca.h], [], []) -AC_CHECK_FUNCS([strndup getsubopt fnmatch]) +AC_CHECK_FUNCS([strndup getopt getopt_long getsubopt fnmatch]) ##### generate output ##### diff --git a/include/compat.h b/include/compat.h index e5785d3..8a2522e 100644 --- a/include/compat.h +++ b/include/compat.h @@ -181,6 +181,31 @@ void w32_perror(const char *str); char *strndup(const char *str, size_t max_len); #endif +#ifndef HAVE_GETOPT +extern char *optarg; +extern int optind, opterr, optopt, optpos, optreset; + +void __getopt_msg(const char *a, const char *b, const char *c, size_t l); + +int getopt(int argc, char * const argv[], const char *optstring); +#endif + +#ifndef HAVE_GETOPT_LONG +struct option { + const char *name; + int has_arg; + int *flag; + int val; +}; + +#define no_argument 0 +#define required_argument 1 +#define optional_argument 2 + +int getopt_long(int, char *const *, const char *, + const struct option *, int *); +#endif + #ifndef HAVE_GETSUBOPT int getsubopt(char **opt, char *const *keys, char **val); #endif diff --git a/lib/compat/Makemodule.am b/lib/compat/Makemodule.am index 6c3c2b4..6ea0750 100644 --- a/lib/compat/Makemodule.am +++ b/lib/compat/Makemodule.am @@ -4,5 +4,7 @@ libcompat_a_SOURCES += lib/compat/chdir.c include/compat.h libcompat_a_SOURCES += lib/compat/path_to_windows.c libcompat_a_SOURCES += lib/compat/w32_perror.c libcompat_a_SOURCES += lib/compat/fnmatch.c +libcompat_a_SOURCES += lib/compat/getopt.c +libcompat_a_SOURCES += lib/compat/getopt_long.c noinst_LIBRARIES += libcompat.a diff --git a/lib/compat/getopt.c b/lib/compat/getopt.c new file mode 100644 index 0000000..9876a87 --- /dev/null +++ b/lib/compat/getopt.c @@ -0,0 +1,96 @@ +#include "compat.h" + +#include +#include +#include +#include + +#ifndef HAVE_GETOPT +char *optarg; +int optind=1, opterr=1, optopt, optpos, optreset=0; + +void __getopt_msg(const char *a, const char *b, const char *c, size_t l) +{ + fputs(a, stderr); + fwrite(b, strlen(b), 1, stderr); + fwrite(c, 1, l, stderr); + putc('\n', stderr); +} + +int getopt(int argc, char * const argv[], const char *optstring) +{ + int i; + wchar_t c, d; + int k, l; + char *optchar; + + if (!optind || __optreset) { + optreset = 0; + optpos = 0; + optind = 1; + } + + if (optind >= argc || !argv[optind]) + return -1; + + if (argv[optind][0] != '-') { + if (optstring[0] == '-') { + optarg = argv[optind++]; + return 1; + } + return -1; + } + + if (!argv[optind][1]) + return -1; + + if (argv[optind][1] == '-' && !argv[optind][2]) + return optind++, -1; + + if (!optpos) optpos++; + if ((k = mbtowc(&c, argv[optind]+optpos, MB_LEN_MAX)) < 0) { + k = 1; + c = 0xfffd; /* replacement char */ + } + optchar = argv[optind]+optpos; + optpos += k; + + if (!argv[optind][optpos]) { + optind++; + optpos = 0; + } + + if (optstring[0] == '-' || optstring[0] == '+') + optstring++; + + i = 0; + d = 0; + do { + l = mbtowc(&d, optstring+i, MB_LEN_MAX); + if (l>0) i+=l; else i++; + } while (l && d != c); + + if (d != c || c == ':') { + optopt = c; + if (optstring[0] != ':' && opterr) + __getopt_msg(argv[0], ": unrecognized option: ", optchar, k); + return '?'; + } + if (optstring[i] == ':') { + optarg = 0; + if (optstring[i+1] != ':' || optpos) { + optarg = argv[optind++] + optpos; + optpos = 0; + } + if (optind > argc) { + optopt = c; + if (optstring[0] == ':') return ':'; + if (opterr) __getopt_msg(argv[0], + ": option requires an argument: ", + optchar, k); + return '?'; + } + } + return c; +} +#endif diff --git a/lib/compat/getopt_long.c b/lib/compat/getopt_long.c new file mode 100644 index 0000000..58354c3 --- /dev/null +++ b/lib/compat/getopt_long.c @@ -0,0 +1,95 @@ +#include "compat.h" + +#include +#include +#include +#include +#include +#include + +#ifndef HAVE_GETOPT_LONG +static void permute(char *const *argv, int dest, int src) +{ + char **av = (char **)argv; + char *tmp = av[src]; + int i; + for (i=src; i>dest; i--) + av[i] = av[i-1]; + av[dest] = tmp; +} + +int getopt_long(int argc, char *const *argv, const char *optstring, const struct option *longopts, int *idx) +{ + optarg = 0; + if (longopts && argv[optind][0] == '-' && + (argv[optind][1] == '-' && argv[optind][2])) + { + int colon = optstring[optstring[0]=='+'||optstring[0]=='-']==':'; + int i, cnt, match; + char *arg, *opt, *start = argv[optind]+1; + for (cnt=i=0; longopts[i].name; i++) { + const char *name = longopts[i].name; + opt = start; + if (*opt == '-') opt++; + while (*opt && *opt != '=' && *opt == *name) + name++, opt++; + if (*opt && *opt != '=') continue; + arg = opt; + match = i; + if (!*name) { + cnt = 1; + break; + } + cnt++; + } + if (cnt==1) { + i = match; + opt = arg; + optind++; + if (*opt == '=') { + if (!longopts[i].has_arg) { + optopt = longopts[i].val; + if (colon || !opterr) + return '?'; + __getopt_msg(argv[0], + ": option does not take an argument: ", + longopts[i].name, + strlen(longopts[i].name)); + return '?'; + } + optarg = opt+1; + } else if (longopts[i].has_arg == required_argument) { + if (!(optarg = argv[optind])) { + optopt = longopts[i].val; + if (colon) return ':'; + if (!opterr) return '?'; + __getopt_msg(argv[0], + ": option requires an argument: ", + longopts[i].name, + strlen(longopts[i].name)); + return '?'; + } + optind++; + } + if (idx) *idx = i; + if (longopts[i].flag) { + *longopts[i].flag = longopts[i].val; + return 0; + } + return longopts[i].val; + } + if (argv[optind][1] == '-') { + optopt = 0; + if (!colon && opterr) + __getopt_msg(argv[0], cnt ? + ": option is ambiguous: " : + ": unrecognized option: ", + argv[optind]+2, + strlen(argv[optind]+2)); + optind++; + return '?'; + } + } + return getopt(argc, argv, optstring); +} +#endif diff --git a/lib/compat/getsubopt.c b/lib/compat/getsubopt.c index d53a37d..e6fea1a 100644 --- a/lib/compat/getsubopt.c +++ b/lib/compat/getsubopt.c @@ -1,10 +1,3 @@ -/* SPDX-License-Identifier: GPL-3.0-or-later */ -/* - * getsubopt.c - * - * Copyright (C) 2019 David Oberhollenzer - */ -#include "config.h" #include "compat.h" #include @@ -13,33 +6,22 @@ #ifndef HAVE_GETSUBOPT int getsubopt(char **opt, char *const *keys, char **val) { - char *str = *opt; - size_t i, len; + char *s = *opt; + int i; *val = NULL; - *opt = strchr(str, ','); - - if (*opt == NULL) { - *opt = str + strlen(str); - } else { - *(*opt)++ = '\0'; - } - - for (i = 0; keys[i]; ++i) { - len = strlen(keys[i]); - - if (strncmp(keys[i], str, len) != 0) - continue; - - if (str[len] != '=' && str[len] != '\0') - continue; - - if (str[len] == '=') - *val = str + len + 1; - + *opt = strchr(s, ','); + if (*opt) *(*opt)++ = 0; + else *opt = s + strlen(s); + + for (i=0; keys[i]; i++) { + size_t l = strlen(keys[i]); + if (strncmp(keys[i], s, l)) continue; + if (s[l] == '=') + *val = s + l + 1; + else if (s[l]) continue; return i; } - return -1; } #endif -- cgit v1.2.3