Mercurial > hg > th-libs
view th_args.c @ 776:680324e43852
Rename a number of config parser variables etc.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Sun, 02 Jul 2023 00:43:59 +0300 |
parents | 56594b498180 |
children |
line wrap: on
line source
/* * Simple commandline argument processing * Programmed and designed by Matti 'ccr' Hamalainen * (C) Copyright 2002-2022 Tecnic Software productions (TNSP) * * Please read file 'COPYING' for information on license and distribution. */ #include "th_util.h" #include "th_args.h" #include "th_string.h" /** * Parse and optionally handle the given long or short option argument. * @param currArg current argument string * @param[in,out] argIndex pointer to index of current argument in argv[] * @param argc number of arguments * @param argv argument string array * @param[in] opts options list array * @param[in] nopts number of elements in options list array * @param[in] handle_option function pointer to callback that handles option arguments * @param[in] process if true, actually handle the argument, aka call the handle_option() function. if false, only validity of options are checked. * @param[in] isLong true if the option is a --long-format one * @returns returns @c true if option processing was successful */ static bool th_args_process_opt( char *currArg, int *argIndex, int argc, char *argv[], const th_optarg opts[], int nopts, bool (*handle_option)(int id, char *, char *), bool process, bool isLong) { const th_optarg *opt = NULL; char *optArg = NULL; for (int optIndex = 0; optIndex < nopts; optIndex++) { const th_optarg *node = &opts[optIndex]; if (isLong && node->o_long != NULL) { if (strcmp(currArg, node->o_long) == 0) { opt = node; optArg = NULL; break; } size_t len = strlen(node->o_long); if (strncmp(currArg, node->o_long, len) == 0 && currArg[len] == '=') { opt = node; optArg = (currArg[len+1] != 0) ? &currArg[len+1] : NULL; break; } } else if (!isLong && node->o_short != 0) { if (*currArg == node->o_short) { opt = node; optArg = (currArg[1] != 0) ? &currArg[1] : NULL; } } } if (opt != NULL) { // Check for the possible option argument if ((opt->flags & OPT_ARGMASK) == OPT_ARGREQ && optArg == NULL) { if (*argIndex < argc) { (*argIndex)++; optArg = argv[*argIndex]; } if (optArg == NULL) { THERR("Option '%s%s' requires an argument.\n", isLong ? "--" : "-", currArg); return false; } } // Option was given succesfully, try to process it if (process && !handle_option(opt->id, optArg, currArg)) return false; } else { THERR("Unknown %s option '%s%s'\n", isLong ? "long" : "short", isLong ? "--" : "-", currArg); return false; } return true; } /** * Process given array of commandline arguments, handling short * and long options by calling the respective callback functions. * * @param argc number of arguments * @param argv argument list * @param[in] opts supported option list array * @param[in] nopts number of elements in the option list array * @param[in] handle_option callback function * @param[in] handle_other callback function * @param[in] flags processing flags * @return returns @c true if arguments were processed successfully */ bool th_args_process(int argc, char *argv[], const th_optarg *opts, const int nopts, bool(*handle_option)(int id, char *, char *), bool(*handle_other)(char *), const int flags) { int handleFlags = flags & OPTH_ONLY_MASK; bool optionsOK = true, endOfOptions = false; for (int argIndex = 1; argIndex < argc; argIndex++) { char *str = argv[argIndex]; if (*str == '-' && !endOfOptions) { // Should we process options? bool process = (handleFlags & OPTH_ONLY_OPTS) || handleFlags == 0; bool isLong; str++; if (*str == '-') { // Check for "--", which ends the options-list str++; if (*str == 0) { endOfOptions = true; continue; } // We have a long option isLong = true; } else isLong = false; if (!th_args_process_opt(str, &argIndex, argc, argv, opts, nopts, handle_option, process, isLong)) optionsOK = false; } else if (handleFlags == OPTH_ONLY_OTHER || handleFlags == 0) { // Was not option argument if (handle_other == NULL || (handle_other != NULL && !handle_other(str))) { optionsOK = false; } } // Check if we bail out on invalid argument if (!optionsOK && (flags & OPTH_BAILOUT)) return false; } return optionsOK; } void th_print_pad(FILE *fh, int count, const char och) { while (count--) fputc(och, fh); } /** * Print given string indented in such a way that it is automatically * line-wrapped as necessary, taking hard linefeeds into account as well. * @param fh stdio file handle to output to * @param[in] spad starting pad/indent of the first line * @param[in] rpad how much to pad the other lines * @param[in] width total line width to wrap at * @param[in] str string to output */ void th_print_wrap(FILE *fh, const int spad, int const rpad, const int width, const char *str) { size_t pos = 0; bool first = true; while (str[pos]) { // Pre-pad line int linelen = first ? spad : rpad; th_print_pad(fh, first ? 0 : rpad, ' '); first = false; // Skip whitespace at line start while (th_isspace(str[pos]) || str[pos] == '\n') pos++; // Handle each word while (str[pos] && str[pos] != '\n') { size_t next; int wlen; // Find word length and next break for (wlen = 0, next = pos; str[next] && !th_isspace(str[next]) && str[next] != '\n'; next++, wlen++); // Check if we have too much of text? if (linelen + wlen > width) break; // Print what we have for (;pos < next; pos++, linelen++) fputc(str[pos], fh); // Check if we are at end of input or hard linefeed if (str[next] == '\n' || str[next] == 0) break; else { fputc(str[pos], fh); pos++; linelen++; } } fprintf(fh, "\n"); } } static inline const char *th_args_get_optarg(const th_optarg *opt) { #ifdef TH_USE_OPT_ARG return opt->o_arg != NULL ? opt->o_arg : "ARG"; #else (void) opt; return "ARG"; #endif } static void th_args_help_print_item(FILE *fh, const th_optarg *opt, int *optWidth, const int maxOptWidth, const int termWidth, const bool doPrint) { const char *arg = th_args_get_optarg(opt); char fmtBuf[32]; int padWidth; bool hasLongOpt = opt->o_long != NULL; if (opt->o_short != 0) { if (!hasLongOpt && (opt->flags & OPT_ARGREQ)) { snprintf(fmtBuf, sizeof(fmtBuf), " -%c <%s>", opt->o_short, arg); } else { snprintf(fmtBuf, sizeof(fmtBuf), " -%c%s", opt->o_short, hasLongOpt ? "," : ""); } *optWidth = strlen(fmtBuf); if (doPrint) padWidth = hasLongOpt ? 2 : maxOptWidth - *optWidth; else padWidth = 2; } else { fmtBuf[0] = 0; *optWidth = 0; padWidth = 4 + 2; } if (doPrint) { fputs(fmtBuf, fh); th_print_pad(fh, padWidth, ' '); } *optWidth += padWidth; if (hasLongOpt) { if (opt->flags & OPT_ARGREQ) { snprintf(fmtBuf, sizeof(fmtBuf), "--%s=<%s>", opt->o_long, arg); } else { snprintf(fmtBuf, sizeof(fmtBuf), "--%s", opt->o_long); } *optWidth += strlen(fmtBuf); } else fmtBuf[0] = 0; if (doPrint) { padWidth = hasLongOpt ? maxOptWidth - *optWidth : 0; *optWidth += padWidth; fputs(fmtBuf, fh); th_print_pad(fh, padWidth, ' '); th_print_wrap(fh, *optWidth, *optWidth, termWidth, opt->desc); } } /** * Print neatly formatted help for specified array of commandline options. * The function will attempt to find the best indentation and formatting of * the output using a simple fitting algorithm. Option descriptions will be * automatically split over several lines if necessary. * @param fh stdio file handle to output to * @param[in] opts options list array * @param[in] nopts number of elements in the options list array @p opts * @param[in] flags (currently unused) * @param[in] width width of the output terminal or the desired maximum width of the printout */ void th_args_help(FILE *fh, const th_optarg *opts, const int nopts, const int flags, const int width) { int index, maxOptWidth; (void) flags; // Determine width of the options and arguments maxOptWidth = 0; for (index = 0; index < nopts; index++) { int optWidth = 0; th_args_help_print_item(NULL, &opts[index], &optWidth, 0, width, false); if (optWidth > maxOptWidth) maxOptWidth = optWidth; } maxOptWidth += 2; // Print out the formatted option list for (index = 0; index < nopts; index++) { int optWidth; th_args_help_print_item(fh, &opts[index], &optWidth, maxOptWidth, width, true); } }