# HG changeset patch # User Matti Hamalainen # Date 1416692264 -7200 # Node ID 0a4fd9cfb92975fdb48c63af993a1e9caea8c9dd # Parent 3d555015ada4b16f2d3d8d8a53a92374578b1454 Revise the argument handling API. Breaks compatibility. diff -r 3d555015ada4 -r 0a4fd9cfb929 th_args.c --- a/th_args.c Mon Oct 13 06:36:15 2014 +0300 +++ b/th_args.c Sat Nov 22 23:37:44 2014 +0200 @@ -1,109 +1,10 @@ /* * Simple commandline argument processing * Programmed and designed by Matti 'ccr' Hamalainen - * (C) Copyright 2002-2008 Tecnic Software productions (TNSP) + * (C) Copyright 2002-2014 Tecnic Software productions (TNSP) * * Please read file 'COPYING' for information on license and distribution. */ -/* -Description -=========== -Structures and functions for simple commandline argument processing, -option argument handling (short and long forms supported). - -Output function for printing out short on-line help for said options -and program commandline usage. - - -Format for typical commandline: - -$ program -a -b -c --long-d -e argument-for-e --long-f="argument for f" - -where -a, -b and -c are short options without required arguments; ---long-d is a long option without argument; -e is short option with -argument and finally --long-f is long option with argument. - - -Introduction -============ -Handling of commandline options in th_args_process() has various -options, which effect how an option is handled. Also the error -situations (unknown option/no required argument) can be handled -in different ways. - -Typically th_args_process() is called as follows: - -BOOL optionHandlerCallback(int optionNumber, char *optionArgument, char *optionName) -{ - if (option is OK) - return TRUE; - else - return FALSE; -} - -BOOL nonoptionHandlerCallback(char *argumentString) -{ - // return value same as in optionHandlerCallback -} - - -int main(int argc, char *argv[]) -{ - if (th_args_process(argc, argv, optList, optListN, - optionHandlerCallback, nonoptionHandlerCallback)) { - ... arguments OK ... - } else { - ... arguments invalid or required arguments missing ... - } -} - - -NOTICE! -------- -The return value from handler callbacks affects the return value of -th_args_process(). Additionally, a failure in callback (returns FALSE) -effects the argument processing if OPTH_BAILOUT flag for th_args_process() -is set. - -If OPTH_BAILOUT is set, any error/failure in argument processing (including -callbacks) immediately stops the argument processing and FALSE is -returned from th_args_process(). - -If OPTH_BAILOUT is NOT set, most errors are "ignored", but FALSE is still -returned if any errors occured. - - -NOTICE #2! ----------- -A small temporary buffer of N*sizeof(BOOL) (where N is number of -options in optList[]) is allocated for processing required options. -If this allocation fails, the program is immediately exited with -code 128. - - -Examples -======== -Example of different options, in a fictional optionlist struct: - -optarg_t optList[] = { - // Option without arguments - { 0, '?', "help", "Show this help", OPT_NONE }, - - // Option with a required argument - { 1, 'o', "output", "Output file name", OPT_ARGREQ }, - - // Option with only long form - { 0, 0, "only-long", "Long option", OPT_NONE }, - - // Option with only short form - { 0, 's', NULL, "Short option", OPT_NONE }, - -}; - -const int optListN = (sizeof(optList) / sizeof(optarg_t)); - - -*/ #ifndef TH_EXTERNAL #include "th_util.h" #include "th_args.h" @@ -111,134 +12,81 @@ #endif -/* Check if option requires an argument +/* Parse long and short options */ -static BOOL th_args_check_arg(const optarg_t *o, const char *optArg) -{ - if ((o->flags & OPT_ARGMASK) == OPT_ARGREQ && optArg == NULL) - { - if (o->optShort != 0 && o->optLong != NULL) - { - THERR("Option '-%c ARG' (--%s=ARG) requires an argument!\n", - o->optShort, o->optLong); - } - else if (o->optShort != 0) - { - THERR("Option '-%c ARG' requires an argument!\n", o->optShort); - } - else if (o->optLong != NULL) - { - THERR("Option --%s=ARG requires an argument!\n", o->optLong); - } - - return FALSE; - } - else - return TRUE; -} - - -/* Handle short options - */ -static BOOL th_args_process_short(char *currArg, int *newArgIndex, +static BOOL th_args_process_opt( + char *currArg, int *argIndex, int argc, char *argv[], - optarg_t optList[], int optListN, - BOOL (*handleOpt)(int, char *, char *), BOOL process) + const th_optarg_t opts[], int numOpts, + BOOL (*handleOptionCB)(int, char *, char *), + BOOL doProcess, BOOL isLong) { - char *tmpArg = currArg, *optArg; - int optN; - BOOL found; - - // Short options can be combined: -a -b -c == -abc - while (*tmpArg) - { - for (optN = 0, found = FALSE; optN < optListN && !found; optN++) - if (*tmpArg == optList[optN].optShort) - { - // Get possible option argument, if needed - if ((optList[optN].flags & OPT_ARGMASK) != 0 && - (++(*newArgIndex) < argc)) - optArg = argv[*newArgIndex]; - else - optArg = NULL; + const th_optarg_t *opt = NULL; + char *optArg = NULL; + int optIndex; - // Check if option argument is required - if (!th_args_check_arg(&optList[optN], optArg)) - return FALSE; - else - if (process) - { - char tmpStr[2] = { 0, 0 }; - - // Option was given succesfully, try to handle it - tmpStr[0] = *tmpArg; - - if (!handleOpt(optList[optN].id, optArg, tmpStr)) - return FALSE; - } - - found = TRUE; + for (optIndex = 0; optIndex < numOpts; optIndex++) + { + const th_optarg_t *node = &opts[optIndex]; + if (isLong && node->optLong != NULL) + { + if (strcmp(currArg, node->optLong) == 0) + { + opt = node; + optArg = NULL; + break; } - if (!found) - { - THERR("Unknown short option '%c' in argument '-%s'\n", - *tmpArg, currArg); - return FALSE; + size_t len = strlen(node->optLong); + if (strncmp(currArg, node->optLong, len) == 0 && + currArg[len] == '=') + { + opt = node; + optArg = (&currArg[len+1] != 0) ? &currArg[len+1] : NULL; + break; + } } - - tmpArg++; - } - - return TRUE; -} - - -/* Handle long options - */ -static BOOL th_args_process_long(char *currArg, int *newArgIndex, - int argc, char *argv[], - optarg_t optList[], int optListN, - BOOL (*handleOpt)(int, char *, char *), BOOL process) -{ - int optN, optLen, i; - char *optArg; - - (void) argc; - (void) argv; - (void) newArgIndex; - - // Long option - for (optN = -1, optLen = i = 0; i < optListN && optN < 0; i++) - { - optarg_t *opt = &optList[i]; - if (opt->optLong) + else + if (!isLong && node->optShort != 0) { - optLen = strlen(opt->optLong); - if (strncmp(currArg, opt->optLong, optLen) == 0) - optN = i; + if (*currArg == node->optShort) + { + opt = node; + optArg = (currArg[1] != 0) ? &currArg[1] : NULL; + } } } - // Get possible option argument, if needed - if (optN >= 0) + if (opt != NULL) { - if ((optList[optN].flags & OPT_ARGMASK) != 0) - optArg = (currArg[optLen] == '=') ? &currArg[optLen + 1] : NULL; - else - optArg = NULL; - - // Check if option argument is required - if (!th_args_check_arg(&optList[optN], optArg)) - return FALSE; - else - // Option was given succesfully, try to handle it - if (process && !handleOpt(optList[optN].id, optArg, currArg)) + // Check for the possible option argument + if ((opt->flags & OPT_ARGMASK) == OPT_ARGREQ && optArg == NULL) + { + if (*argIndex < argc) + { + (*argIndex)++; + optArg = argv[*argIndex]; + } + else + { + THERR("Option '%s%s' requires an argument.\n", + isLong ? "--" : "-", + currArg); + return FALSE; + } + } + + // Option was given succesfully, try to process it + if (doProcess && !handleOptionCB(opt->id, optArg, currArg)) return FALSE; } else { - THERR("Unknown long option '--%s'\n", currArg); + THERR("Unknown %s option '%s%s'\n", + isLong ? "long" : "short", + isLong ? "--" : "-", + currArg); + return FALSE; } @@ -250,58 +98,51 @@ * calling the given callback functions. */ BOOL th_args_process(int argc, char *argv[], - optarg_t optList[], int optListN, - BOOL(*handleOpt) (int, char *, char *), - BOOL(*handleNonOption) (char *), int flags) + const th_optarg_t *opts, const int numOpts, + BOOL(*handleOptionCB) (int, char *, char *), + BOOL(*handleOther) (char *), const int flags) { - int handle = flags & OPTH_ONLY_MASK, - argIndex = 1; - BOOL optionsOK = TRUE, - endOptions = FALSE; + int argIndex, handleFlags = flags & OPTH_ONLY_MASK; + BOOL optionsOK = TRUE, endOfOptions = FALSE; - while (argIndex < argc) + for (argIndex = 1; argIndex < argc; argIndex++) { - char *currArg = argv[argIndex]; - if (*currArg == '-' && !endOptions) + char *str = argv[argIndex]; + if (*str == '-' && !endOfOptions) { - BOOL process = (handle & OPTH_ONLY_OPTS) || handle == 0; - int newArgIndex = argIndex; - currArg++; - if (*currArg == '-') + // Should we process options? + BOOL doProcess = (handleFlags & OPTH_ONLY_OPTS) || handleFlags == 0; + BOOL isLong; + + str++; + if (*str == '-') { // Check for "--", which ends the options-list - currArg++; - if (*currArg == 0) + str++; + if (*str == 0) { - endOptions = TRUE; + endOfOptions = TRUE; continue; } - // Long options - if (!th_args_process_long(currArg, &newArgIndex, - argc, argv, optList, - optListN, handleOpt, process)) - optionsOK = FALSE; + // We have a long option + isLong = TRUE; } else - { - // Short options - if (!th_args_process_short(currArg, &newArgIndex, - argc, argv, optList, - optListN, handleOpt, process)) - optionsOK = FALSE; - } + isLong = FALSE; - argIndex = newArgIndex; + if (!th_args_process_opt(str, &argIndex, argc, argv, + opts, numOpts, handleOptionCB, doProcess, isLong)) + optionsOK = FALSE; } else - if (handle == OPTH_ONLY_OTHER || handle == 0) + if (handleFlags == OPTH_ONLY_OTHER || handleFlags == 0) { // Was not option argument - if (handleNonOption == NULL - || (handleNonOption != NULL && !handleNonOption(currArg))) + if (handleOther == NULL || + (handleOther != NULL && !handleOther(str))) { - THERR("Invalid argument '%s'\n", currArg); + THERR("Invalid argument '%s'\n", str); optionsOK = FALSE; } } @@ -309,8 +150,6 @@ // Check if we bail out on invalid argument if (!optionsOK && (flags & OPTH_BAILOUT)) return FALSE; - - argIndex++; } return optionsOK; @@ -319,39 +158,98 @@ /* Print help for commandline arguments/options */ -void th_args_help(FILE *outFile, optarg_t optList[], int optListN) +static void th_pad(FILE *outFile, int count) +{ + while (count--) + fputc(' ', outFile); +} + + +static void th_print_wrap(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; + th_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; + for (wlen = 0, next = pos; str[next] && !th_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; + } + } + } +} + + +void th_args_help(FILE *fh, + const th_optarg_t *opts, const int numOpts, + const int flags) { int index; + (void) flags; - for (index = 0; index < optListN; index++) + // Print out option list + for (index = 0; index < numOpts; index++) { - optarg_t *opt = &optList[index]; + const th_optarg_t *opt = &opts[index]; + char tmpStr[128]; // Print short option if (opt->optShort != 0) - fprintf(outFile, " -%c, ", opt->optShort); + { + snprintf(tmpStr, sizeof(tmpStr), + "-%c,", opt->optShort); + } else - fprintf(outFile, " "); + tmpStr[0] = 0; + + fprintf(fh, " %-5s", tmpStr); // Print long option - if (opt->optLong) + if (opt->optLong != NULL) { - char tmpStr[64], *p; - - if ((opt->flags & OPT_ARGMASK) == OPT_ARGREQ) - { - snprintf(tmpStr, sizeof(tmpStr), "%s=ARG", - opt->optLong); - p = tmpStr; - } - else - p = opt->optLong; - - fprintf(outFile, "--%-15s", p); + snprintf(tmpStr, sizeof(tmpStr), "--%s%s", + opt->optLong, + (opt->flags & OPT_ARGREQ) ? "=ARG" : ""); } else - fprintf(outFile, " "); + tmpStr[0] = 0; - fprintf(outFile, " %s.\n", opt->desc); + fprintf(fh, "%-20s", tmpStr); + + th_print_wrap(fh, opt->desc, 26, 26, 73); } } diff -r 3d555015ada4 -r 0a4fd9cfb929 th_args.h --- a/th_args.h Mon Oct 13 06:36:15 2014 +0300 +++ b/th_args.h Sat Nov 22 23:37:44 2014 +0200 @@ -1,7 +1,7 @@ /* * Simple commandline argument processing function * Programmed and designed by Matti 'ccr' Hamalainen - * (C) Copyright 2002-2012 Tecnic Software productions (TNSP) + * (C) Copyright 2002-2014 Tecnic Software productions (TNSP) * * Please read file 'COPYING' for information on license and distribution. */ @@ -19,10 +19,9 @@ */ #define OPT_NONE (0) // Simple option with no arguments #define OPT_ARGREQ (1) // Option requires an argument -#define OPT_ARGMASK (3) // Mask for option argument flags +#define OPT_ARGMASK (1) // Mask for option argument flags #define OPTH_BAILOUT 0x0001 // Bail out on errors - #define OPTH_ONLY_OPTS 0x0010 // Handle only options #define OPTH_ONLY_OTHER 0x0020 // Handle only "non-options" #define OPTH_ONLY_MASK 0x00f0 // Mask @@ -37,15 +36,16 @@ char *optLong; char *desc; int flags; -} optarg_t; +} th_optarg_t; BOOL th_args_process(int argc, char *argv[], - optarg_t argList[], int argListN, - BOOL (*handleOpt)(int, char *, char *), - BOOL (*handleFile)(char *), int flags); + const th_optarg_t *opts, const int nopts, + BOOL (*handleOption)(int, char *, char *), + BOOL (*handleOther)(char *), const int flags); -void th_args_help(FILE *, optarg_t optList[], int optListN); +void th_args_help(FILE *, const th_optarg_t *opts, + const int nopts, const int flags); #ifdef __cplusplus }