Mercurial > hg > dmlib
view dmargs_int.c @ 495:30145d17aebd
Move certain editor related targets to the actual TARGETS variable, and
remove them from being separately specified in the "make clean" target,
thusly cleaning them only when editor is enabled in build configuration.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Fri, 16 Nov 2012 19:18:03 +0200 |
parents | 32250b436bca |
children | 4d6769bd8cfb |
line wrap: on
line source
/* * Simple commandline argument processing * Programmed and designed by Matti 'ccr' Hamalainen * (C) Copyright 2002-2008 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 bailOut argument of th_args_process() is TRUE! If bailOut is TRUE, any error/failure in argument processing (including callbacks) immediately stops the argument processing and FALSE is returned from th_args_process(). If bailOut is FALSE, 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 optional argument { 2, 'f', "foobar", "Use foobar, with optional string", OPT_ARGOPT }, // This option is required to be given, though without other flags // it may not make much sense. { 4, 'S', "stupid", "You must give this option", OPT_REQUIRED }, // The flags can be combined with OR operator: this option is both // required to be specified, and also requires argument (the filename) { 5, 'i', "input", "Input file name", OPT_REQUIRED | 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" #include "th_string.h" #endif /* Check if option requires an argument */ 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, BOOL *wasGiven, int argc, char *argv[], optarg_t optList[], int optListN, BOOL (*handleOpt)(int, char *, char *)) { char *tmpArg = currArg, *optArg; int optN; BOOL isFound; // Short options can be combined: -a -b -c == -abc while (*tmpArg) { for (optN = 0, isFound = FALSE; (optN < optListN) && !isFound; 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; // Check if option argument is required if (!th_args_check_arg(&optList[optN], optArg)) return FALSE; else { char tmpStr[2] = { 0, 0 }; // Option was given succesfully, try to handle it wasGiven[optN] = TRUE; tmpStr[0] = *tmpArg; if (!handleOpt(optList[optN].id, optArg, tmpStr)) return FALSE; } isFound = TRUE; } if (!isFound) { THERR("Unknown short option '%c' in argument '-%s'\n", *tmpArg, currArg); return FALSE; } tmpArg++; } return TRUE; } /* Handle long options */ static BOOL th_args_process_long(char *currArg, int *newArgIndex, BOOL *wasGiven, int argc, char *argv[], optarg_t optList[], int optListN, BOOL (*handleOpt)(int, char *, char *)) { 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++) if (optList[i].optLong) { optLen = strlen(optList[i].optLong); if (strncmp(currArg, optList[i].optLong, optLen) == 0) optN = i; } // Get possible option argument, if needed if (optN >= 0) { if ((optList[optN].flags & OPT_ARGMASK) != 0) { if (currArg[optLen] == '=') optArg = &currArg[optLen + 1]; else optArg = 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 wasGiven[optN] = TRUE; if (!handleOpt(optList[optN].id, optArg, currArg)) return FALSE; } } else { THERR("Unknown long option '--%s'\n", currArg); return FALSE; } return TRUE; } /* Process arguments, handling short and long options by * 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 *), BOOL bailOut) { BOOL endOptions, optionsOK; int argIndex, newArgIndex, i; char *currArg; BOOL *wasGiven; // Allocate wasGiven wasGiven = (BOOL *) th_calloc(optListN, sizeof(BOOL)); if (!wasGiven) { THERR("FATAL ERROR! Could not allocate wasGiven in th_args_process()!\n"); exit(128); } // Parse arguments argIndex = 1; optionsOK = TRUE; endOptions = FALSE; while (argIndex < argc) { currArg = argv[argIndex]; if ((currArg[0] == '-') && !endOptions) { newArgIndex = argIndex; currArg++; if (*currArg == '-') { // Check for "--", which ends the options-list currArg++; if (*currArg == 0) { endOptions = TRUE; continue; } // Long options if (!th_args_process_long(currArg, &newArgIndex, wasGiven, argc, argv, optList, optListN, handleOpt)) optionsOK = FALSE; } else { // Short options if (!th_args_process_short(currArg, &newArgIndex, wasGiven, argc, argv, optList, optListN, handleOpt)) optionsOK = FALSE; } argIndex = newArgIndex; } else { // Was not option argument if (handleNonOption == NULL || (handleNonOption != NULL && !handleNonOption(currArg))) { THERR("Invalid argument '%s'\n", currArg); optionsOK = FALSE; } } // Check if we bail out on invalid argument if (!optionsOK && bailOut) { th_free(wasGiven); return FALSE; } argIndex++; } // Check wasGiven by isRequired for (i = 0; i < optListN; i++) if ((optList[i].flags & OPT_REQUIRED) != 0 && !wasGiven[i]) { THERR("Option -%s (--%s) is required.\n", optList[i].optShort, optList[i].optLong); optionsOK = FALSE; if (bailOut) break; } th_free(wasGiven); return optionsOK; } /* Print help for commandline arguments/options */ void th_args_help(FILE *outFile, optarg_t optList[], int optListN) { int i, nrequired; for (i = nrequired = 0; i < optListN; i++) { optarg_t *o = &optList[i]; // Print short option if (o->optShort != 0) fprintf(outFile, " -%c, ", o->optShort); else fprintf(outFile, " "); // Print long option if (o->optLong) { char tmpStr[64], *p; if ((o->flags & OPT_ARGMASK) == OPT_ARGOPT) { snprintf(tmpStr, sizeof(tmpStr), "%s[=ARG]", optList[i].optLong); p = tmpStr; } else if ((o->flags & OPT_ARGMASK) == OPT_ARGREQ) { snprintf(tmpStr, sizeof(tmpStr), "%s=ARG", optList[i].optLong); p = tmpStr; } else p = o->optLong; fprintf(outFile, "--%-15s", p); } else fprintf(outFile, " "); fprintf(outFile, " %s.", optList[i].desc); if (o->flags & OPT_REQUIRED) { fprintf(outFile, " [*]\n"); nrequired++; } else fprintf(outFile, "\n"); } if (nrequired > 0) fprintf(outFile, "(Options marked with [*] are required)\n"); }