#ifndef argparser_H #define argparser_H /* Facilities for parsing command line arguments. */ /* Last edited on 2005-01-16 15:00:23 by stolfi */ /* Copyright © 2005 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. */ #endif /* COPYRIGHT AND AUTHORSHIP NOTICE Copyright © 2005 Jorge Stolfi, Universidade Estadual de Campinas (UNICAMP). Created by Jorge Stolfi in 1992--2005. This source file can be freely distributed, used, and modified, provided that this copyright and authorship notice is preserved in all copies, and that any modified versions of this file are clearly marked as such. This software has NO WARRANTY of correctness or applicability for any purpose. Neither the author nor his employers shall be held responsible for any losses or damages that may result from its use. END OF NOTICE */