/* argparser.h -- facilities for parsing command line arguments. */ /* Last edited on 2004-08-17 23:06:29 by stolfi */ #ifndef argparser_H #define argparser_H /* Copyright © 2003 Jorge Stolfi, Unicamp. See note at end of file. */ /* Based on Params.i3 by J.Stolfi, DEC-SRC, 1988. */ /* This interface provides simple and robust tools for parsing the command line arguments given to a process when it is started. Check the usage example at the end of this interface. */ #include #include #include typedef struct argparser_t /* A parser for command line arguments. */ { string_vec_t arg; /* Command line arguments; {arg[0]} is prog name. */ bool_vec_t parsed; /* parsed[i] is {TRUE} if {arg[i]} has been parsed. */ unsigned next; /* The next argument to parse is {arg[next]} */ FILE *wr; /* File for errors */ unsigned nusage; /* Number of lines of error help text. */ string_vec_t usage; /* {usage.el[0..nusage-1]} is the help text for errors. */ } argparser_t; argparser_t *argparser_new(FILE *wr, int argc, char **argv); /* Saves pointers to the given command line arguments. Marks the command name {arg[0]} as parsed, all other arguments as unparsed. The next argument to be parsed will be {arg[1]}. Any parsing errors will be printed to {wr}. */ void argparser_set_usage(argparser_t *pp, char *msg); /* Appends {msg} to the ``usage'' help text. If a syntax error is found during argument parsing, this text will be written to {pp->wr}, just before halting the program. */ bool argparser_keyword_present(argparser_t *pp, char *key); /* Looks for the first unparsed argument {arg[i]} that is equal to {key}. If found, marks it as parsed, sets {pp->next} to {i+1}, and returns {TRUE}. Otherwise returns {FALSE} and leaves {pp->next} unchanged. */ void argparser_get_keyword(argparser_t *pp, char *key); /* Same as {argparser_keyword_present}, but raises error if the keyword is not found. */ bool argparser_is_next(argparser_t *pp, char *key); /* Returns TRUE if and only if {arg[pp->next]} exists, is still unparsed, and is equal to {key}. */ bool argparser_test_next(argparser_t *pp, char *key); /* If {argparser_is_next(pp, key)} is true, marks the next argument as parsed, increments {pp->next} and returns TRUE. Otherwise does none of these things and returns {FALSE}. */ void argparser_match_next(argparser_t *pp, char *key); /* If {argparser_is_next(pp, key)} is true, marks the next argument as parsed and increments {pp->next}. Otherwise raises an error. */ char *argparser_get_next(argparser_t *pp); /* Returns {arg[pp->next]}, marks it as parsed and increments {pp->next}. Raises error if {arg[pp->next]} does not exist or has already been parsed. */ int argparser_get_next_int(argparser_t *pp, int min, int max); double argparser_get_next_double(argparser_t *pp, double min, double max); /* Same as {argparser_get_next}, but converts the result to the approriate type (using {strtol} and {strtod}, respectively). Raises error if the parameter is not a valid literal, or lies outside of the range {[min..max]}. */ int_vec_t argparser_get_int_list(argparser_t *pp, char *key, int min, int max); /* Parses all (zero or more) unparsed occurrences of the keyword {key}, not necessarily in consecutive positions. Requires that each occurrence is immediately followed by an integer in {[min..max]}. Returns an array with those integers, in the order found. */ void argparser_error(argparser_t *pp, char *msg); /* Prints the given message, the help text, and terminates the program. */ void argparser_skip_parsed(argparser_t *pp); /* Points {pp->next} at the first unparsed argument. If there are any parsed arguments beyond that one, prints a message and raises error. */ void argparser_finish(argparser_t *pp); /* Checks whether all parameters have been parsed; if not, prints a message and raises error. Also reclaims {*pp} and all its internal storage. */ /* Most Unix programs expect their command-line arguments to consist of a string of keywords and keyword-labeled arguments (`options', `switches', etc.), followed by a list of positional arguments. For the user's convenience, programs generally allow the switches and keyword-labeled arguments to be given in any order. Some of those parameters may be optional and/or repeatable, some may be mandatory; some may be required or forbidden depending on the values of the other parameters. Furthermore, the value of an argument may be just a number or a text string, or may be a cluster of two or more values with their own little syntax. This module simplifies the parsing of such command-line parameters, by allowing the program to scan the arguments in the order which most suits the program. This module also detects automatically many kinds of common mistakes --- such as arguments that are missing, repeated, extraneous, malformed, or out of range --- and prints the appropriate error messages. For example, here is how this module could be used by an hypothetical program {prt} that concatenates a bunch of files and prints selected line ranges of the result, possibly in reverse order, with several formatting options. #define MaxLines MAX_INT #define MaxRanges 100 #define MaxFiles 100 #define MinFontSize 1 #define MaxFontSize 100 / * Arguments from command line: * / int fontSize; bool landscape; int nRanges = 0; int ini[MaxRanges], fin[MaxRanges]; bool reverse[MaxRanges]; int nFiles = 0; char *files[MaxFiles]; void parse_args(int argc, char **argv) { static char *usage = "prt \\\n" " -fontSize NUM \\\n" " [ -landscape | -portrait ] \\\n" " [ -lines NUM NUM [ -reverse ] ]..." " FNAME..."; / * Initialize the argument parser: * / argparser_t *pp = argparser_new(stderr, argc, argv); argparser_set_usage(pp, usage); / * The "-fontSize" parameter is mandatory: * / argparser_get_keyword(pp, "-fontSize"); fontSize = argparser_get_next_int(pp, MinFontsize, MaxFontSize); / * Either "-landscape" or "-portrait", but not both: * / if (argparser_keyword_present(pp, "-landscape")) { landscape = TRUE; } else if (argparser_keyword_present(pp, "-portrait")) { landscape = FALSE; } else { / * Default is "-portrait" unless font is too big. * / landscape = (fontSize > 8); } / * Parse the line ranges: * / nRanges = 0; while (argparser_keyword_present(pp, "-lines")) { if (nRanges >= MaxRanges) { argparser_error(pp, "Too many page ranges"); } ini[nRanges] = argparser_get_next_int(pp, 1,MaxLines); fin[nRanges] = argparser_get_next_int(pp, ini[nRanges],MaxLines); rev[nRanges] = argparser_test_next(pp, "-reverse"); nRanges = nRanges+1; } / * By default, print all lines: * / if (nRanges == 0) { ini[0] = 1; fin[0] = MaxLines; rev[0] = FALSE; nRanges = 1; } / * Parse the file list (after all keyword options): * / argparser_skip_parsed(pp); nFiles = argc - pp->next; if (nFiles == 0) { argparser_error(pp, "no files specified"); } for (i = 0; i < nFiles; i++) { files[i] = argparser_get_next(pp); } / * Check for any unparsed parameters: * / argparser_finish(pp); } Note that this code allows the user to give the options "-fontSize" and "-landscape"/"-portrait" in any order, even anywhere among or after the "-range" arguments. However, each "-range" flag must be immediately followed by two numbers; and the "-reverse" flag, if given, must immediately follow the second number. Also, all file names must follow all the other options and arguments. */ /* Copyright © 2003 by Jorge Stolfi. ** ** Permission to use, copy, modify, and distribute this software and its ** documentation for any purpose and without fee is hereby granted, provided ** that the above copyright notice appears in all copies and that both that ** copyright notice and this permission notice appear in supporting ** documentation. This software is provided "as is" without express or ** implied warranty of any kind. */ #endif