summaryrefslogtreecommitdiff
path: root/src/options.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/options.c')
-rw-r--r--src/options.c428
1 files changed, 428 insertions, 0 deletions
diff --git a/src/options.c b/src/options.c
new file mode 100644
index 0000000000..b50d4e3eb5
--- /dev/null
+++ b/src/options.c
@@ -0,0 +1,428 @@
+/*
+ * Copyright(c) 2012 Tim Ruehsen
+ * Copyright(c) 2015-2019 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <c-ctype.h> // c_tolower, c_isalnum
+
+#include <gnutls/gnutls.h>
+#include "options.h"
+
+int parse_integer(option_t opt, const char *val, const char invert)
+{
+ *((int *)opt->var) = val ? atoi(val) : 0;
+
+ return 0;
+}
+
+static int parse_filename(option_t opt, const char *val, const char invert)
+{
+ free(*((char **)opt->var));
+// *((const char **)opt->var) = val ? shell_expand(val) : NULL;
+ *((char **)opt->var) = val ? strdup(val) : NULL;
+
+ return 0;
+}
+
+int parse_string(option_t opt, const char *val, const char invert)
+{
+ free(*((char **)opt->var));
+ *((char **)opt->var) = val ? strdup(val) : NULL;
+
+ return 0;
+}
+
+/*
+static int parse_stringset(option_t opt, const char *val, const char invert)
+{
+ wget_stringmap_t *map = *((wget_stringmap_t **)opt->var);
+
+ if (val) {
+ const char *s, *p;
+
+ wget_stringmap_clear(map);
+
+ for (s = p = val; *p; s = p + 1) {
+ if ((p = strchrnul(s, ',')) != s)
+ wget_stringmap_put_noalloc(map, wget_strmemdup(s, p - s), NULL);
+ }
+ } else {
+ wget_stringmap_clear(map);
+ }
+
+ return 0;
+}
+
+static const char *_strchrnul_esc(const char *s, char c)
+{
+ const char *p;
+
+ for (p = s; *p; p++) {
+ if (*p == '\\' && (p[1] == '\\' || p[1] == c))
+ p++;
+ else if (*p == c)
+ return p;
+ }
+
+ return p; // pointer to trailing \0
+}
+
+static char *_strmemdup_esc(const char *s, size_t size)
+{
+ const char *p, *e;
+ size_t newsize = 0;
+
+ for (p = s, e = s + size; p < e; p++) {
+ if (*p == '\\') {
+ if (p < e - 1) {
+ newsize++;
+ p++;
+ }
+ } else
+ newsize++;
+ }
+
+ char *ret = malloc(newsize + 1);
+ char *dst = ret;
+
+ for (p = s, e = s + size; p < e; p++) {
+ if (*p == '\\') {
+ if (p < e - 1)
+ *dst++ = *++p;
+ } else
+ *dst++ = *p;
+ }
+ *dst = 0;
+
+ return ret;
+}
+
+static int parse_stringlist_expand(option_t opt, const char *val, int expand, int max_entries)
+{
+ if (val && *val) {
+ wget_vector_t *v = *((wget_vector_t **)opt->var);
+ const char *s, *p;
+
+ if (!v)
+ v = *((wget_vector_t **)opt->var) = wget_vector_create(8, (wget_vector_compare_t)strcmp);
+
+ for (s = p = val; *p; s = p + 1) {
+ if ((p = _strchrnul_esc(s, ',')) != s) {
+ if (wget_vector_size(v) >= max_entries) {
+ wget_debug_printf("%s: More than %d entries, ignoring overflow\n", __func__, max_entries);
+ return -1;
+ }
+
+ const char *fname = _strmemdup_esc(s, p - s);
+
+ if (expand && *s == '~') {
+ wget_vector_add_noalloc(v, shell_expand(fname));
+ xfree(fname);
+ } else
+ wget_vector_add_noalloc(v, fname);
+ }
+ }
+ } else {
+ wget_vector_free(opt->var);
+ }
+
+ return 0;
+}
+
+static int parse_stringlist(option_t opt, const char *val, const char invert)
+{
+ // max number of 1024 entries to avoid out-of-memory
+ return parse_stringlist_expand(opt, val, 0, 1024);
+}
+*/
+
+int parse_bool(option_t opt, const char *val, const char invert)
+{
+ if (opt->var) {
+ if (!val || !strcmp(val, "1") || !strcmp(val, "y") || !strcmp(val, "yes") || !strcmp(val, "on"))
+ *((char *) opt->var) = !invert;
+ else if (!*val || !strcmp(val, "0") || !strcmp(val, "n") || !strcmp(val, "no") || !strcmp(val, "off"))
+ *((char *) opt->var) = invert;
+ else {
+ fprintf(stderr, "Invalid boolean value '%s'\n", val);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static inline void print_first(const char s, const char *l, const char *msg)
+{
+ if (s)
+ printf(" -%c, --%-20s %s", s, l, msg);
+ else
+ printf(" --%-20s %s", l, msg);
+}
+
+static inline void print_next(const char *msg)
+{
+ printf("%32s%s", "", msg);
+}
+
+void print_options(const struct optionw *options, int noptions)
+{
+ for (int it = 0; it < noptions; it++) {
+ print_first(options[it].short_name,
+ options[it].long_name,
+ options[it].help_str[0]);
+ for (unsigned i = 1; i < countof(options[it].help_str) && options[it].help_str[i]; i++)
+ print_next(options[it].help_str[i]);
+ }
+}
+
+static inline void print_first_md(const char s, const char *l, const char *msg)
+{
+ if (s)
+ printf("## `-%c`, `--%s`\n\n%s", s, l, msg);
+ else
+ printf("## `--%s`\n\n%s", l, msg);
+}
+
+static void print_options_md(const struct optionw *options, int noptions)
+{
+ for (int it = 0; it < noptions; it++) {
+ print_first_md(options[it].short_name,
+ options[it].long_name,
+ options[it].help_str[0]);
+ for (unsigned i = 1; i < countof(options[it].help_str) && options[it].help_str[i]; i++)
+ printf("%s", options[it].help_str[i]);
+ printf("\n");
+ }
+}
+
+static int opt_compare(const void *key, const void *option)
+{
+ return strcmp(key, ((option_t) option)->long_name);
+}
+
+static int opt_compare_config_linear(const char *key, const char *command)
+{
+ const char *s1 = key, *s2 = command;
+
+ for (; *s1 && *s2; s1++, s2++) {
+ if (*s2 == '-' || *s2 == '_') {
+ if (*s1 == '-' || *s1 == '_')
+ s1++;
+ s2++;
+ }
+
+ if (!*s1 || !*s2 || c_tolower(*s1) != *s2) break;
+ // *s2 is guaranteed to be lower case so convert *s1 to lower case
+ }
+
+ return *s1 != *s2; // no need for tolower() here
+}
+
+// return values:
+// < 0 : parse error
+// >= 0 : number of arguments processed
+static int set_long_option(const char *name, const char *value, const struct optionw *options, int noptions)
+{
+ option_t opt;
+ char invert = 0, value_present = 0, case_insensitive = 1;
+ char namebuf[strlen(name) + 1], *p;
+ int ret = 0, rc;
+
+ if ((p = strchr(name, '='))) {
+ // option with appended value
+ memcpy(namebuf, name, p - name);
+ namebuf[p - name] = 0;
+ name = namebuf;
+ value = p + 1;
+ value_present = 1;
+ }
+
+ // If the option is passed from .wget2rc (--*), delete the "--" prefix
+ if (!strncmp(name, "--", 2)) {
+ case_insensitive = 0;
+ name += 2;
+ }
+ // If the option is negated (--no-) delete the "no-" prefix
+ if (!strncmp(name, "no-", 3)) {
+ invert = 1;
+ name += 3;
+ }
+
+ if (case_insensitive) {
+ opt = bsearch(name, options, noptions, sizeof(options[0]), opt_compare);
+ if (!opt) {
+ // Fallback to linear search for 'unsharp' searching.
+ // Maybe the user asked for e.g. https_only or httpsonly instead of https-only
+ // opt_compare_config_linear() will find these. Wget -e/--execute compatibility.
+ for (int it = 0; it < noptions && !opt; it++)
+ if (opt_compare_config_linear(name, options[it].long_name) == 0)
+ opt = &options[it];
+ }
+ } else
+ opt = bsearch(name, options, noptions, sizeof(options[0]), opt_compare);
+
+ if (!opt) {
+ fprintf(stderr, "Unknown option '%s'\n", name);
+ return -1;
+ }
+
+ if (value_present) {
+ // "option=*"
+ if (invert) {
+ if (!opt->nargs || opt->parser == parse_string ||
+// opt->parser == parse_stringset ||
+// opt->parser == parse_stringlist ||
+ opt->parser == parse_filename)
+// || opt->parser == parse_filenames)
+ {
+ fprintf(stderr, "Option 'no-%s' doesn't allow an argument\n", name);
+ return -1;
+ }
+ } else if (!opt->nargs) {
+ printf("Option '%s' doesn't allow an argument\n", name);
+ return -1;
+ }
+ } else {
+ // "option"
+ switch (opt->nargs) {
+ case 0:
+ value = NULL;
+ break;
+ case 1:
+ if (!value) {
+ fprintf(stderr, "Missing argument for option '%s'\n", name);
+ // empty string is allowed in value i.e. *value = '\0'
+ return -1;
+ }
+
+ if (invert && (opt->parser == parse_string ||
+// opt->parser == parse_stringset ||
+// opt->parser == parse_stringlist ||
+ opt->parser == parse_filename))
+// || opt->parser == parse_filenames))
+ value = NULL;
+ else
+ ret = opt->nargs;
+ break;
+ case -1:
+ if(value)
+ ret = 1;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if ((rc = opt->parser(opt, value, invert)) < 0)
+ return rc;
+
+ return ret;
+}
+
+int parse_command_line(int argc, const char **argv, const struct optionw *options, int noptions)
+{
+ static short shortcut_to_option[128];
+ const char *first_arg = NULL;
+ int n, rc;
+
+ if (argc == 2 && !strcmp(argv[1], "--options-md")) {
+ print_options_md(options, noptions);
+ exit(EXIT_SUCCESS);
+ }
+
+ // init the short option lookup table
+ if (!shortcut_to_option[0]) {
+ for (int it = 0; it < noptions; it++) {
+ if (options[it].short_name)
+ shortcut_to_option[(unsigned char)options[it].short_name] = it + 1;
+ }
+ }
+
+ // I like the idea of getopt() but not it's implementation (e.g. global variables).
+ // Therefore I implement my own getopt() behavior.
+ for (n = 1; n < argc && first_arg != argv[n]; n++) {
+ const char *argp = argv[n];
+
+ if (argp[0] != '-') {
+ // Move args behind options to allow mixed args/options like getopt().
+ // In the end, the order of the args is as before.
+ const char *cur = argv[n];
+ for (int it = n; it < argc - 1; it++)
+ argv[it] = argv[it + 1];
+ argv[argc - 1] = cur;
+
+ // Once we see the first arg again, we are done
+ if (!first_arg)
+ first_arg = cur;
+
+ n--;
+ continue;
+ }
+
+ if (argp[1] == '-') {
+ // long option
+ if (argp[2] == 0)
+ return n + 1;
+
+ if ((rc = set_long_option(argp + 2, n < argc - 1 ? argv[n+1] : NULL, options, noptions)) < 0)
+ return rc;
+
+ n += rc;
+
+ } else if (argp[1]) {
+ // short option(s)
+ for (int pos = 1; argp[pos]; pos++) {
+ option_t opt;
+ int idx;
+
+ if (c_isalnum(argp[pos]) && (idx = shortcut_to_option[(unsigned char)argp[pos]])) {
+ opt = &options[idx - 1];
+ // printf("opt=%p [%c]\n",(void *)opt,argp[pos]);
+ // printf("name=%s\n",opt->long_name);
+ if (opt->nargs > 0) {
+ const char *val;
+
+ if (!argp[pos + 1] && argc <= n + opt->nargs) {
+ fprintf(stderr,"Missing argument(s) for option '-%c'\n", argp[pos]);
+ return -1;
+ }
+ val = argp[pos + 1] ? argp + pos + 1 : argv[++n];
+ if ((rc = opt->parser(opt, val, 0)) < 0)
+ return rc;
+ n += rc;
+ break;
+ } else {//if (opt->args == 0)
+ if ((rc = opt->parser(opt, NULL, 0)) < 0)
+ return rc;
+ }
+ } else {
+ fprintf(stderr,"Unknown option '-%c'\n", argp[pos]);
+ return -1;
+ }
+ }
+ }
+ }
+
+ return n;
+}