Last active
May 27, 2019 21:15
-
-
Save lelanthran/4d50105c2d0594b5c15aeaeed72f84c3 to your computer and use it in GitHub Desktop.
Single function to get command-line options in both long and short form.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <stdlib.h> | |
#include <stdbool.h> | |
#include <string.h> | |
/* ***************************************************************** | |
* A more robust command-line handling function than I normally use. | |
* There are only two functions, so this is suitable for copying and | |
* pasting into the file containing main(). | |
* | |
* ======================================================================= | |
* Skip all options on the command line, and set argc/argv to point to the | |
* program's non-option arguments: | |
* void cline_skipopt (int *argc, char ***argv); | |
* | |
* NOTE: the argc/argv parameters are modified by this function. | |
* | |
* | |
* | |
* ======================================================================= | |
* Get an option from the command-line: | |
* const char *cline_getopt (int argc, | |
* char **argv, | |
* const char *longopt, | |
* char shortopt); | |
* | |
* argc: number of arguments | |
* argv: array of arguments | |
* longopt: string containing name of option. If NULL, short-opt is | |
* used to find the option. | |
* shortopt: character of the option to search for. If zero, longopt is | |
* used to find the option. | |
* | |
* RETURNS: If option is not found, then NULL is returned. If option is | |
* found then the string that is returned: | |
* 1. Will be empty if option did not have an argument | |
* such as "--option=" or "--option". | |
* 2. Will contain the value of the argument if the option had an | |
* argument, such as "--option=value", or "-o value", or | |
* "-ovalue". | |
* | |
* For short options, the caller must determine whether to use | |
* the returned string's value or not. If, for example, the | |
* option "-c" does not have arguments and the user entered | |
* "-cab" then the caller must only check for nullness in the | |
* return value. | |
* | |
* If the caller expects "-c" to have arguments, then the | |
* returned string for "-cab" will contain "ab". | |
* | |
* See EXAMPLES below for clarification. | |
* | |
* EXAMPLES: | |
* | |
* 1. Get a long option using cline_getopt() | |
* --long-option Returns empty string "" | |
* --long-option= Returns empty string "" | |
* --long-option=value Returns const string "value" | |
* | |
* 2. Get a short option using cline_getopt() | |
* -a -b -c Returns non-NULL for a, b and c | |
* -abc Same as above | |
* -ofoo Returns "foo" for o | |
* -o foo Same as above | |
* -abco foo Returns non-NULL for a, b and c, AND returns foo for o | |
* -abcofoo Same as above | |
* | |
* 3. When the same long-option and short-option is specified the | |
* long-option takes precedence. | |
* | |
* 4. Options processing ends with "--". Any arguments after a "--" is | |
* encountered must be processed by the caller. | |
*/ | |
static const char *cline_getopt (int argc, char **argv, | |
const char *longopt, | |
char shortopt) | |
{ | |
for (int i=1; i<argc; i++) { | |
if (argv[i][0]!='-') | |
continue; | |
if ((memcmp (argv[i], "--", 3))==0) | |
return NULL; | |
char *value = NULL; | |
if (argv[i][1]=='-' && longopt) { | |
char *name = &argv[i][2]; | |
if ((memcmp (name, longopt, strlen (longopt)))==0) { | |
argv[i][0] = 0; | |
value = strchr (name, '='); | |
if (!value) | |
return ""; | |
*value++ = 0; | |
return value; | |
} | |
} | |
if (!shortopt || argv[i][1]=='-') | |
continue; | |
for (size_t j=1; argv[i][j]; j++) { | |
if (argv[i][j] == shortopt) { | |
memmove (&argv[i][j], &argv[i][j+1], strlen (&argv[i][j+1])+1); | |
if (argv[i][j] == 0) { | |
return argv[i+1] ? argv[i+1] : ""; | |
} else { | |
return &argv[i][j]; | |
} | |
} | |
} | |
} | |
return NULL; | |
} | |
static void cline_skipopt (int *argc, char ***argv) | |
{ | |
for (int i=1; i<(*argc); i++) { | |
if ((memcmp ((*argv)[i], "--", 3))==0) { | |
(*argv) = &(*argv)[i+1]; | |
(*argc) = (*argc) - i - 1; | |
return; | |
} | |
} | |
} | |
static bool valcmp (const char *value, const char *expected) | |
{ | |
if (value==NULL && expected==NULL) | |
return true; | |
if (value==NULL || expected==NULL) | |
return false; | |
return strcmp (value, expected)==0; | |
} | |
/* *********************************************************** | |
* Test program. Execute with: | |
* cline_test --long-one=long-one \ | |
* -abcd foo \ | |
* -fghbar \ | |
* --long-two= \ | |
* --long-three \ | |
* --long-five \ | |
* -- all the normal args go here -e | |
*/ | |
int main (int argc, char **argv) | |
{ | |
static const struct { | |
const char *l; | |
char s; | |
const char *result; | |
} testcases[] = { | |
{ "long-one", 0, "long-one" }, // --long-one=long-one | |
{ "long-two", 0, "" }, // --long-two= | |
{ "long-three", 0, "" }, // --long-three | |
{ "long-four", 0, NULL }, // --long-five | |
{ NULL, 'a', "bcd" }, // -abcd foo | |
{ NULL, 'b', "cd" }, // -abcd foo | |
{ NULL, 'c', "d" }, // -abcd foo | |
{ NULL, 'd', "foo" }, // -abcd foo | |
{ NULL, 'e', NULL }, // -abcd foo | |
{ NULL, 'h', "bar" }, // -fghbar | |
}; | |
for (size_t i=0; i<sizeof testcases/sizeof testcases[0]; i++) { | |
const char *value = | |
cline_getopt (argc, argv, testcases[i].l, testcases[i].s); | |
const char *msg = valcmp (value, testcases[i].result) ? "pass" : "fail"; | |
printf ("%s %zu: [%s/%c]: [%s:%s]\n", msg, | |
i, | |
testcases[i].l, | |
testcases[i].s, | |
testcases[i].result, | |
value); | |
} | |
// Find the start of the program's non-option arguments | |
cline_skipopt (&argc, &argv); | |
for (int i=0; i<argc; i++) { | |
printf ("[%i]:[%s]\n", i, argv[i]); | |
} | |
return 0; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment