Mercurial > hg > dmlib
diff src/dmargs.c @ 1742:ddec147d1f90
Bring in changes from the th-libs version of commandline argument handling.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Sat, 09 Jun 2018 17:56:07 +0300 |
parents | 985225a93aeb |
children | bcdea45a14cb |
line wrap: on
line diff
--- a/src/dmargs.c Sat Jun 09 17:04:33 2018 +0300 +++ b/src/dmargs.c Sat Jun 09 17:56:07 2018 +0300 @@ -1,12 +1,279 @@ +/* + * Simple commandline argument processing + * Programmed and designed by Matti 'ccr' Hamalainen + * (C) Copyright 2002-2018 Tecnic Software productions (TNSP) + * + * Please read file 'COPYING' for information on license and distribution. + */ +/// @file +/// @brief Simple commandline argument processing functions #include "dmargs.h" -#define TH_EXTERNAL 1 -#define th_optarg_t DMOptArg -#define th_args_process dmArgsProcess -#define th_args_help dmArgsPrintHelp + + +/** + * Parse and optionally handle the given long or short option argument. + * @param currArg current argument string + * @param argIndex pointer to index of current argument in argv[] + * @param argc number of arguments + * @param argv argument string array + * @param opts options list array + * @param nopts number of elements in options list array + * @param handle_option function pointer to callback that handles option arguments + * @param doProcess if TRUE, actually handle the argument, aka call the handle_option() function. if FALSE, only validity of options are checked. + * @param isLong TRUE if the option is a --long-format one + */ +static BOOL dmArgsProcessOpt( + char *currArg, int *argIndex, + int argc, char *argv[], + const DMOptArg opts[], int nopts, + BOOL (*handle_option)(int id, char *, char *), + BOOL doProcess, BOOL isLong) +{ + const DMOptArg *opt = NULL; + char *optArg = NULL; + int optIndex; + + for (optIndex = 0; optIndex < nopts; optIndex++) + { + const DMOptArg *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) + { + dmErrorMsg("Option '%s%s' requires an argument.\n", + isLong ? "--" : "-", + currArg); + return FALSE; + } + } + + // Option was given succesfully, try to process it + if (doProcess && !handle_option(opt->id, optArg, currArg)) + return FALSE; + } + else + { + dmErrorMsg("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 opts supported option list array + * @param nopts number of elements in the option list array + * @param handle_option callback function + * @param handle_other callback function + * @param flags processing flags + * @return return TRUE if all is well + */ +BOOL dmArgsProcess(int argc, char *argv[], + const DMOptArg *opts, const int nopts, + BOOL(*handle_option)(int id, char *, char *), + BOOL(*handle_other)(char *), const int flags) +{ + int argIndex, handleFlags = flags & OPTH_ONLY_MASK; + BOOL optionsOK = TRUE, endOfOptions = FALSE; + + for (argIndex = 1; argIndex < argc; argIndex++) + { + char *str = argv[argIndex]; + if (*str == '-' && !endOfOptions) + { + // Should we process options? + BOOL doProcess = (handleFlags & OPTH_ONLY_OPTS) || handleFlags == 0; + BOOL isLong; -#define THERR dmErrorMsg -#define th_calloc dmCalloc -#define th_malloc dmMalloc -#define th_free dmFree + 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 (!dmArgsProcessOpt(str, &argIndex, argc, argv, + opts, nopts, handle_option, doProcess, 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))) + { + dmErrorMsg("Invalid argument '%s'\n", str); + optionsOK = FALSE; + } + } + + // Check if we bail out on invalid argument + if (!optionsOK && (flags & OPTH_BAILOUT)) + return FALSE; + } + + return optionsOK; +} + + +static void dmPad(FILE *outFile, int count) +{ + while (count--) + fputc(' ', outFile); +} + + +static void dmPrintWrap(FILE *fh, const char *str, int spad, int rpad, int width) +{ + size_t pos = 0; + BOOL first = TRUE; + + while (str[pos]) + { + // Pre-pad line + int linelen = first ? spad : rpad; + dmPad(fh, first ? 0 : rpad); + first = FALSE; + + // Skip whitespace at line start + while (isspace(str[pos]) || str[pos] == '\n') pos++; -#include "dmargs_int.c" + // Handle each word + while (str[pos] && str[pos] != '\n') + { + size_t next; + int wlen; + for (wlen = 0, next = pos; str[next] && !isspace(str[next]) && str[next] != '\n'; next++, wlen++); +// fprintf(stdout, "X '%c', %d .. linelen=%d/%d, wlen=%d\n", str[pos], pos, linelen, width, wlen); + if (linelen + wlen < width) + { + for (;pos < next; pos++, linelen++) + fputc(str[pos], fh); + + if (str[next] == '\n' || str[next] == 0) + { + fprintf(fh, "\n"); + break; + } + else + { + fputc(str[pos], fh); + pos++; + linelen++; + } + } + else + { + fprintf(fh, "\n"); + break; + } + } + } +} + + +/** + * Print help for commandline arguments/options + * @param fh stdio file handle to output to + * @param opts options list array + * @param nopts number of elements in options list array + * @param flags flags (currently unused) + */ +void dmArgsPrintHelp(FILE *fh, + const DMOptArg *opts, const int nopts, + const int flags) +{ + int index; + (void) flags; + + // Print out option list + for (index = 0; index < nopts; index++) + { + const DMOptArg *opt = &opts[index]; + char tmpStr[128]; + + // Print short option + if (opt->o_short != 0) + { + snprintf(tmpStr, sizeof(tmpStr), + "-%c,", opt->o_short); + } + else + tmpStr[0] = 0; + + fprintf(fh, " %-5s", tmpStr); + + // Print long option + if (opt->o_long != NULL) + { + snprintf(tmpStr, sizeof(tmpStr), "--%s%s", + opt->o_long, + (opt->flags & OPT_ARGREQ) ? "=ARG" : ""); + } + else + tmpStr[0] = 0; + + fprintf(fh, "%-20s", tmpStr); + + dmPrintWrap(fh, opt->desc, 26, 26, 79); + } +}