/* This file is part of GNUnet Copyright (C) 2006, 2011, 2020 GNUnet e.V. GNUnet is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. GNUnet 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 Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . SPDX-License-Identifier: AGPL3.0-or-later */ /** * @file src/util/getopt_helpers.c * @brief implements command line that sets option * @author Christian Grothoff */ #include "platform.h" #include "gnunet_util_lib.h" #define LOG(kind, ...) GNUNET_log_from (kind, "util-getopt", __VA_ARGS__) /** * Print out program version (implements --version). * * @param ctx command line processing context * @param scls additional closure (points to version string) * @param option name of the option * @param value not used (NULL) * @return #GNUNET_NO (do not continue, not an error) */ static int print_version (struct GNUNET_GETOPT_CommandLineProcessorContext *ctx, void *scls, const char *option, const char *value) { const char *version = scls; (void) option; (void) value; printf ("%s v%s\n", ctx->binaryName, version); return GNUNET_NO; } /** * Define the option to print the version of * the application (-v option) * * @param version string with the version number */ struct GNUNET_GETOPT_CommandLineOption GNUNET_GETOPT_option_version (const char *version) { struct GNUNET_GETOPT_CommandLineOption clo = { .shortName = 'v', .name = "version", .description = gettext_noop ( "print the version number"), .option_exclusive = 1, .processor = &print_version, .scls = (void *) version }; return clo; } /** * At what offset does the help text start? */ #define BORDER 29 /** * Print out details on command line options (implements --help). * * @param ctx command line processing context * @param scls additional closure (points to about text) * @param option name of the option * @param value not used (NULL) * @return #GNUNET_NO (do not continue, not an error) */ static int format_help (struct GNUNET_GETOPT_CommandLineProcessorContext *ctx, void *scls, const char *option, const char *value) { const char *about = scls; size_t slen; unsigned int i; int j; size_t ml; size_t p; char *scp; const char *trans; const struct GNUNET_GETOPT_CommandLineOption *opt; const struct GNUNET_OS_ProjectData *pd; (void) option; (void) value; if (NULL != about) { printf ("%s\n%s\n", ctx->binaryOptions, gettext (about)); printf (_ ( "Arguments mandatory for long options are also mandatory for short options.\n")); } i = 0; opt = ctx->allOptions; while (NULL != opt[i].description) { if (opt[i].shortName == '\0') printf (" "); else printf (" -%c, ", opt[i].shortName); printf ("--%s", opt[i].name); slen = 8 + strlen (opt[i].name); if (NULL != opt[i].argumentHelp) { printf ("=%s", opt[i].argumentHelp); slen += 1 + strlen (opt[i].argumentHelp); } if (slen > BORDER) { printf ("\n%*s", BORDER, ""); slen = BORDER; } if (slen < BORDER) { printf ("%*s", (int) (BORDER - slen), ""); slen = BORDER; } if (0 < strlen (opt[i].description)) trans = gettext (opt[i].description); else trans = ""; ml = strlen (trans); p = 0; OUTER: while (ml - p > 78 - slen) { for (j = p + 78 - slen; j > (int) p; j--) { if (isspace ((unsigned char) trans[j])) { scp = GNUNET_malloc (j - p + 1); GNUNET_memcpy (scp, &trans[p], j - p); scp[j - p] = '\0'; printf ("%s\n%*s", scp, BORDER + 2, ""); GNUNET_free (scp); p = j + 1; slen = BORDER + 2; goto OUTER; } } /* could not find space to break line */ scp = GNUNET_malloc (78 - slen + 1); GNUNET_memcpy (scp, &trans[p], 78 - slen); scp[78 - slen] = '\0'; printf ("%s\n%*s", scp, BORDER + 2, ""); GNUNET_free (scp); slen = BORDER + 2; p = p + 78 - slen; } /* print rest */ if (p < ml) printf ("%s\n", &trans[p]); if (strlen (trans) == 0) printf ("\n"); i++; } pd = GNUNET_OS_project_data_get (); printf ("Report bugs to %s.\n" "Home page: %s\n", pd->bug_email, pd->homepage); if (0 != pd->is_gnu) printf ("General help using GNU software: http://www.gnu.org/gethelp/\n"); return GNUNET_NO; } /** * Defining the option to print the command line * help text (-h option). * * @param about string with brief description of the application */ struct GNUNET_GETOPT_CommandLineOption GNUNET_GETOPT_option_help (const char *about) { struct GNUNET_GETOPT_CommandLineOption clo = { .shortName = 'h', .name = "help", .description = gettext_noop ( "print this help"), .option_exclusive = 1, .processor = format_help, .scls = (void *) about }; return clo; } /** * Set an option of type 'unsigned int' from the command line. Each * time the option flag is given, the value is incremented by one. * A pointer to this function should be passed as part of the * 'struct GNUNET_GETOPT_CommandLineOption' array to initialize options * of this type. It should be followed by a pointer to a value of * type 'int'. * * @param ctx command line processing context * @param scls additional closure (will point to the 'unsigned int') * @param option name of the option * @param value not used (NULL) * @return #GNUNET_OK */ static int increment_value (struct GNUNET_GETOPT_CommandLineProcessorContext *ctx, void *scls, const char *option, const char *value) { unsigned int *val = scls; (void) ctx; (void) option; (void) value; (*val)++; return GNUNET_OK; } /** * Increment @a val each time the option flag is given by one. * * @param shortName short name of the option * @param name long name of the option * @param argumentHelp help text for the option argument * @param description long help text for the option * @param[out] val increment by 1 each time the option is present */ struct GNUNET_GETOPT_CommandLineOption GNUNET_GETOPT_option_increment_uint (char shortName, const char *name, const char *description, unsigned int *val) { struct GNUNET_GETOPT_CommandLineOption clo = { .shortName = shortName, .name = name, .description = description, .processor = &increment_value, .scls = (void *) val }; return clo; } /** * Define the '-V' verbosity option. Using the option more * than once increments @a level each time. * * @param[out] level set to the verbosity level */ struct GNUNET_GETOPT_CommandLineOption GNUNET_GETOPT_option_verbose (unsigned int *level) { struct GNUNET_GETOPT_CommandLineOption clo = { .shortName = 'V', .name = "verbose", .description = gettext_noop ("be verbose"), .processor = &increment_value, .scls = (void *) level }; return clo; } /** * Set an option of type 'int' from the command line to 1 if the * given option is present. * A pointer to this function should be passed as part of the * 'struct GNUNET_GETOPT_CommandLineOption' array to initialize options * of this type. It should be followed by a pointer to a value of * type 'int'. * * @param ctx command line processing context * @param scls additional closure (will point to the 'int') * @param option name of the option * @param value not used (NULL) * @return #GNUNET_OK */ static int set_one (struct GNUNET_GETOPT_CommandLineProcessorContext *ctx, void *scls, const char *option, const char *value) { int *val = scls; (void) ctx; (void) option; (void) value; *val = 1; return GNUNET_OK; } /** * Allow user to specify a flag (which internally means setting * an integer to 1/#GNUNET_YES/#GNUNET_OK. * * @param shortName short name of the option * @param name long name of the option * @param argumentHelp help text for the option argument * @param description long help text for the option * @param[out] val set to 1 if the option is present */ struct GNUNET_GETOPT_CommandLineOption GNUNET_GETOPT_option_flag (char shortName, const char *name, const char *description, int *val) { struct GNUNET_GETOPT_CommandLineOption clo = { .shortName = shortName, .name = name, .description = description, .processor = &set_one, .scls = (void *) val }; return clo; } /** * Set an option of type 'char *' from the command line. * A pointer to this function should be passed as part of the * 'struct GNUNET_GETOPT_CommandLineOption' array to initialize options * of this type. It should be followed by a pointer to a value of * type 'char *', which will be allocated with the requested string. * * @param ctx command line processing context * @param scls additional closure (will point to the 'char *', * which will be allocated) * @param option name of the option * @param value actual value of the option (a string) * @return #GNUNET_OK */ static int set_string (struct GNUNET_GETOPT_CommandLineProcessorContext *ctx, void *scls, const char *option, const char *value) { char **val = scls; (void) ctx; (void) option; GNUNET_assert (NULL != value); GNUNET_free (*val); *val = GNUNET_strdup (value); return GNUNET_OK; } /** * Allow user to specify a string. * * @param shortName short name of the option * @param name long name of the option * @param argumentHelp help text for the option argument * @param description long help text for the option * @param[out] str set to the string */ struct GNUNET_GETOPT_CommandLineOption GNUNET_GETOPT_option_string (char shortName, const char *name, const char *argumentHelp, const char *description, char **str) { struct GNUNET_GETOPT_CommandLineOption clo = { .shortName = shortName, .name = name, .argumentHelp = argumentHelp, .description = description, .require_argument = 1, .processor = &set_string, .scls = (void *) str }; return clo; } /** * Define the '-L' log level option. Note that we do not check * that the log level is valid here. * * @param[out] level set to the log level */ struct GNUNET_GETOPT_CommandLineOption GNUNET_GETOPT_option_loglevel (char **level) { struct GNUNET_GETOPT_CommandLineOption clo = { .shortName = 'L', .name = "log", .argumentHelp = "LOGLEVEL", .description = gettext_noop ("configure logging to use LOGLEVEL"), .require_argument = 1, .processor = &set_string, .scls = (void *) level }; return clo; } /** * Set an option of type 'char *' from the command line with * filename expansion a la #GNUNET_STRINGS_filename_expand(). * * @param ctx command line processing context * @param scls additional closure (will point to the `char *`, * which will be allocated) * @param option name of the option * @param value actual value of the option (a string) * @return #GNUNET_OK */ static int set_filename (struct GNUNET_GETOPT_CommandLineProcessorContext *ctx, void *scls, const char *option, const char *value) { char **val = scls; (void) ctx; (void) option; GNUNET_assert (NULL != value); GNUNET_free (*val); *val = GNUNET_STRINGS_filename_expand (value); return GNUNET_OK; } /** * Allow user to specify a filename (automatically path expanded). * * @param shortName short name of the option * @param name long name of the option * @param argumentHelp help text for the option argument * @param description long help text for the option * @param[out] str set to the string */ struct GNUNET_GETOPT_CommandLineOption GNUNET_GETOPT_option_filename (char shortName, const char *name, const char *argumentHelp, const char *description, char **str) { struct GNUNET_GETOPT_CommandLineOption clo = { .shortName = shortName, .name = name, .argumentHelp = argumentHelp, .description = description, .require_argument = 1, .processor = &set_filename, .scls = (void *) str }; return clo; } /** * Allow user to specify log file name (-l option) * * @param[out] logfn set to the name of the logfile */ struct GNUNET_GETOPT_CommandLineOption GNUNET_GETOPT_option_logfile (char **logfn) { struct GNUNET_GETOPT_CommandLineOption clo = { .shortName = 'l', .name = "logfile", .argumentHelp = "FILENAME", .description = gettext_noop ("configure logging to write logs to FILENAME"), .require_argument = 1, .processor = &set_filename, .scls = (void *) logfn }; return clo; } /** * Allow user to specify configuration file name (-c option) * * @param[out] fn set to the name of the configuration file */ struct GNUNET_GETOPT_CommandLineOption GNUNET_GETOPT_option_cfgfile (char **fn) { struct GNUNET_GETOPT_CommandLineOption clo = { .shortName = 'c', .name = "config", .argumentHelp = "FILENAME", .description = gettext_noop ("use configuration file FILENAME"), .require_argument = 1, .processor = &set_filename, .scls = (void *) fn }; return clo; } /** * Set an option of type 'unsigned long long' from the command line. * A pointer to this function should be passed as part of the * 'struct GNUNET_GETOPT_CommandLineOption' array to initialize options * of this type. It should be followed by a pointer to a value of * type 'unsigned long long'. * * @param ctx command line processing context * @param scls additional closure (will point to the 'unsigned long long') * @param option name of the option * @param value actual value of the option as a string. * @return #GNUNET_OK if parsing the value worked */ static int set_ulong (struct GNUNET_GETOPT_CommandLineProcessorContext *ctx, void *scls, const char *option, const char *value) { unsigned long long *val = scls; char dummy[2]; (void) ctx; if (1 != sscanf (value, "%llu%1s", val, dummy)) { fprintf (stderr, _ ("You must pass a number to the `%s' option.\n"), option); return GNUNET_SYSERR; } return GNUNET_OK; } /** * Allow user to specify an `unsigned long long` * * @param shortName short name of the option * @param name long name of the option * @param argumentHelp help text for the option argument * @param description long help text for the option * @param[out] val set to the value specified at the command line */ struct GNUNET_GETOPT_CommandLineOption GNUNET_GETOPT_option_ulong (char shortName, const char *name, const char *argumentHelp, const char *description, unsigned long long *val) { struct GNUNET_GETOPT_CommandLineOption clo = { .shortName = shortName, .name = name, .argumentHelp = argumentHelp, .description = description, .require_argument = 1, .processor = &set_ulong, .scls = (void *) val }; return clo; } /** * Set an option of type 'struct GNUNET_TIME_Relative' from the command line. * A pointer to this function should be passed as part of the * 'struct GNUNET_GETOPT_CommandLineOption' array to initialize options * of this type. It should be followed by a pointer to a value of * type 'struct GNUNET_TIME_Relative'. * * @param ctx command line processing context * @param scls additional closure (will point to the 'struct GNUNET_TIME_Relative') * @param option name of the option * @param value actual value of the option as a string. * @return #GNUNET_OK if parsing the value worked */ static int set_timetravel_time (struct GNUNET_GETOPT_CommandLineProcessorContext *ctx, void *scls, const char *option, const char *value) { long long delta; long long minus; struct GNUNET_TIME_Relative rt; (void) scls; (void) ctx; while (isspace (value[0])) value++; minus = 1; if (value[0] == '-') { minus = -1; value++; } else if (value[0] == '+') { value++; } if (GNUNET_OK != GNUNET_STRINGS_fancy_time_to_relative (value, &rt)) { fprintf (stderr, _ ( "You must pass a relative time (optionally with sign) to the `%s' option.\n"), option); return GNUNET_SYSERR; } if (rt.rel_value_us > LLONG_MAX) { fprintf (stderr, _ ("Value given for time travel `%s' option is too big.\n"), option); return GNUNET_SYSERR; } delta = (long long) rt.rel_value_us; delta *= minus; GNUNET_TIME_set_offset (delta); return GNUNET_OK; } /** * Allow user to specify a `long long` with an offset to add to the current * system time to construct the time seen by the application. Used for * debugging / testing. * * @param shortName short name of the option * @param name long name of the option * @param[out] val set to the time specified at the command line */ struct GNUNET_GETOPT_CommandLineOption GNUNET_GETOPT_option_timetravel (char shortName, const char *name) { struct GNUNET_GETOPT_CommandLineOption clo = { .shortName = shortName, .name = name, .argumentHelp = _ ("[+/-]MICROSECONDS"), .description = _ ( "modify system time by given offset (for debugging/testing only)"), .require_argument = 1, .processor = &set_timetravel_time }; return clo; } /** * Set an option of type 'struct GNUNET_TIME_Relative' from the command line. * A pointer to this function should be passed as part of the * 'struct GNUNET_GETOPT_CommandLineOption' array to initialize options * of this type. It should be followed by a pointer to a value of * type 'struct GNUNET_TIME_Relative'. * * @param ctx command line processing context * @param scls additional closure (will point to the 'struct GNUNET_TIME_Relative') * @param option name of the option * @param value actual value of the option as a string. * @return #GNUNET_OK if parsing the value worked */ static int set_relative_time (struct GNUNET_GETOPT_CommandLineProcessorContext *ctx, void *scls, const char *option, const char *value) { struct GNUNET_TIME_Relative *val = scls; (void) ctx; if (GNUNET_OK != GNUNET_STRINGS_fancy_time_to_relative (value, val)) { fprintf (stderr, _ ("You must pass relative time to the `%s' option.\n"), option); return GNUNET_SYSERR; } return GNUNET_OK; } /** * Allow user to specify a `struct GNUNET_TIME_Relative` * (using human-readable "fancy" time). * * @param shortName short name of the option * @param name long name of the option * @param argumentHelp help text for the option argument * @param description long help text for the option * @param[out] val set to the time specified at the command line */ struct GNUNET_GETOPT_CommandLineOption GNUNET_GETOPT_option_relative_time (char shortName, const char *name, const char *argumentHelp, const char *description, struct GNUNET_TIME_Relative *val) { struct GNUNET_GETOPT_CommandLineOption clo = { .shortName = shortName, .name = name, .argumentHelp = argumentHelp, .description = description, .require_argument = 1, .processor = &set_relative_time, .scls = (void *) val }; return clo; } /** * Set an option of type 'struct GNUNET_TIME_Absolute' from the command line. * A pointer to this function should be passed as part of the * 'struct GNUNET_GETOPT_CommandLineOption' array to initialize options * of this type. It should be followed by a pointer to a value of * type 'struct GNUNET_TIME_Absolute'. * * @param ctx command line processing context * @param scls additional closure (will point to the `struct GNUNET_TIME_Absolute`) * @param option name of the option * @param value actual value of the option as a string. * @return #GNUNET_OK if parsing the value worked */ static int set_absolute_time (struct GNUNET_GETOPT_CommandLineProcessorContext *ctx, void *scls, const char *option, const char *value) { struct GNUNET_TIME_Absolute *val = scls; (void) ctx; if (GNUNET_OK != GNUNET_STRINGS_fancy_time_to_absolute (value, val)) { fprintf (stderr, _ ("You must pass absolute time to the `%s' option.\n"), option); return GNUNET_SYSERR; } return GNUNET_OK; } /** * Allow user to specify a `struct GNUNET_TIME_Absolute` * (using human-readable "fancy" time). * * @param shortName short name of the option * @param name long name of the option * @param argumentHelp help text for the option argument * @param description long help text for the option * @param[out] val set to the time specified at the command line */ struct GNUNET_GETOPT_CommandLineOption GNUNET_GETOPT_option_absolute_time (char shortName, const char *name, const char *argumentHelp, const char *description, struct GNUNET_TIME_Absolute *val) { struct GNUNET_GETOPT_CommandLineOption clo = { .shortName = shortName, .name = name, .argumentHelp = argumentHelp, .description = description, .require_argument = 1, .processor = &set_absolute_time, .scls = (void *) val }; return clo; } /** * Set an option of type 'unsigned int' from the command line. * A pointer to this function should be passed as part of the * 'struct GNUNET_GETOPT_CommandLineOption' array to initialize options * of this type. It should be followed by a pointer to a value of * type 'unsigned int'. * * @param ctx command line processing context * @param scls additional closure (will point to the 'unsigned int') * @param option name of the option * @param value actual value of the option as a string. * @return #GNUNET_OK if parsing the value worked */ static int set_uint (struct GNUNET_GETOPT_CommandLineProcessorContext *ctx, void *scls, const char *option, const char *value) { unsigned int *val = scls; char dummy[2]; (void) ctx; if ('-' == *value) { fprintf (stderr, _ ( "Your input for the '%s' option has to be a non negative number\n"), option); return GNUNET_SYSERR; } if (1 != sscanf (value, "%u%1s", val, dummy)) { fprintf (stderr, _ ("You must pass a number to the `%s' option.\n"), option); return GNUNET_SYSERR; } return GNUNET_OK; } /** * Allow user to specify an unsigned integer. * * @param shortName short name of the option * @param name long name of the option * @param argumentHelp help text for the option argument * @param description long help text for the option * @param[out] val set to the value specified at the command line */ struct GNUNET_GETOPT_CommandLineOption GNUNET_GETOPT_option_uint (char shortName, const char *name, const char *argumentHelp, const char *description, unsigned int *val) { struct GNUNET_GETOPT_CommandLineOption clo = { .shortName = shortName, .name = name, .argumentHelp = argumentHelp, .description = description, .require_argument = 1, .processor = &set_uint, .scls = (void *) val }; return clo; } /** * Set an option of type 'uint16_t' from the command line. * A pointer to this function should be passed as part of the * 'struct GNUNET_GETOPT_CommandLineOption' array to initialize options * of this type. It should be followed by a pointer to a value of * type 'uint16_t'. * * @param ctx command line processing context * @param scls additional closure (will point to the 'unsigned int') * @param option name of the option * @param value actual value of the option as a string. * @return #GNUNET_OK if parsing the value worked */ static int set_uint16 (struct GNUNET_GETOPT_CommandLineProcessorContext *ctx, void *scls, const char *option, const char *value) { uint16_t *val = scls; unsigned int v; char dummy[2]; (void) ctx; if (1 != sscanf (value, "%u%1s", &v, dummy)) { fprintf (stderr, _ ("You must pass a number to the `%s' option.\n"), option); return GNUNET_SYSERR; } if (v > UINT16_MAX) { fprintf (stderr, _ ("You must pass a number below %u to the `%s' option.\n"), (unsigned int) UINT16_MAX, option); return GNUNET_SYSERR; } *val = (uint16_t) v; return GNUNET_OK; } /** * Allow user to specify an uint16_t. * * @param shortName short name of the option * @param name long name of the option * @param argumentHelp help text for the option argument * @param description long help text for the option * @param[out] val set to the value specified at the command line */ struct GNUNET_GETOPT_CommandLineOption GNUNET_GETOPT_option_uint16 (char shortName, const char *name, const char *argumentHelp, const char *description, uint16_t *val) { struct GNUNET_GETOPT_CommandLineOption clo = { .shortName = shortName, .name = name, .argumentHelp = argumentHelp, .description = description, .require_argument = 1, .processor = &set_uint16, .scls = (void *) val }; return clo; } /** * Closure for #set_base32(). */ struct Base32Context { /** * Value to initialize (already allocated) */ void *val; /** * Number of bytes expected for @e val. */ size_t val_size; }; /** * Set an option of type 'unsigned int' from the command line. * A pointer to this function should be passed as part of the * 'struct GNUNET_GETOPT_CommandLineOption' array to initialize options * of this type. It should be followed by a pointer to a value of * type 'unsigned int'. * * @param ctx command line processing context * @param scls additional closure (will point to the 'unsigned int') * @param option name of the option * @param value actual value of the option as a string. * @return #GNUNET_OK if parsing the value worked */ static int set_base32 (struct GNUNET_GETOPT_CommandLineProcessorContext *ctx, void *scls, const char *option, const char *value) { struct Base32Context *bc = scls; (void) ctx; if (GNUNET_OK != GNUNET_STRINGS_string_to_data (value, strlen (value), bc->val, bc->val_size)) { fprintf ( stderr, _ ( "Argument `%s' malformed. Expected base32 (Crockford) encoded value.\n"), option); return GNUNET_SYSERR; } return GNUNET_OK; } /** * Helper function to clean up after * #GNUNET_GETOPT_option_base32_fixed_size. * * @param cls value to GNUNET_free() */ static void free_bc (void *cls) { GNUNET_free (cls); } /** * Allow user to specify a binary value using Crockford * Base32 encoding. * * @param shortName short name of the option * @param name long name of the option * @param argumentHelp help text for the option argument * @param description long help text for the option * @param[out] val binary value decoded from Crockford Base32-encoded argument * @param val_size size of @a val in bytes */ struct GNUNET_GETOPT_CommandLineOption GNUNET_GETOPT_option_base32_fixed_size (char shortName, const char *name, const char *argumentHelp, const char *description, void *val, size_t val_size) { struct Base32Context *bc = GNUNET_new (struct Base32Context); struct GNUNET_GETOPT_CommandLineOption clo = { .shortName = shortName, .name = name, .argumentHelp = argumentHelp, .description = description, .require_argument = 1, .processor = &set_base32, .cleaner = &free_bc, .scls = (void *) bc }; bc->val = val; bc->val_size = val_size; return clo; } /** * Make the given option mandatory. * * @param opt option to modify * @return @a opt with the mandatory flag set. */ struct GNUNET_GETOPT_CommandLineOption GNUNET_GETOPT_option_mandatory (struct GNUNET_GETOPT_CommandLineOption opt) { opt.option_mandatory = 1; return opt; } /** * Make the given option mutually exclusive with other options. * * @param opt option to modify * @return @a opt with the exclusive flag set. */ struct GNUNET_GETOPT_CommandLineOption GNUNET_GETOPT_option_exclusive (struct GNUNET_GETOPT_CommandLineOption opt) { opt.option_exclusive = 1; return opt; } /* end of getopt_helpers.c */