{"thread":{"id":"14078","subject":"[PATCH 2/2] Extend parse-options test suite","startedAt":"2008-06-22T00:00:01Z","lastAt":"2008-06-22T15:04:26Z","messageCount":7,"participants":["Stephan Beyer","Michele Ballabio"],"isPatch":true,"patchVersion":1,"patchTotal":2},"messages":[{"id":"80583","messageId":"1214092802-8175-1-git-send-email-s-beyer@gmx.net","threadId":"14078","inReplyTo":null,"subject":"[PATCH 1/2] api-parse-options.txt: Introduce documentation for parse options API","fromName":"Stephan Beyer","fromEmail":"s-beyer@gmx.net","sentAt":"2008-06-22T00:00:01Z","receivedAt":"2008-06-22T00:00:01Z","isPatch":true,"sender":{"key":"s-beyer@gmx.net","avatar":"https://avatars.githubusercontent.com/u/143889?v=4"},"body":"Add some documentation of basics, macros and callback\nimplementation of the parse-options API.\n\nSigned-off-by: Stephan Beyer <s-beyer@gmx.net>\n---\n Documentation/technical/api-parse-options.txt |  206 ++++++++++++++++++++++++-\n 1 files changed, 204 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/technical/api-parse-options.txt b/Documentation/technical/api-parse-options.txt\nindex b7cda94..a916db4 100644\n--- a/Documentation/technical/api-parse-options.txt\n+++ b/Documentation/technical/api-parse-options.txt\n@@ -1,6 +1,208 @@\n parse-options API\n =================\n \n-Talk about <parse-options.h>\n+The parse-options API is used to parse and massage options in git\n+and to provide a usage help with consistent look.\n \n-(Pierre)\n+Basics\n+------\n+\n+The argument vector `argv[]` may usually contain mandatory or optional\n+'non-option arguments', e.g. a filename or a branch, and 'options'.\n+Options are optional arguments that start with a dash and\n+that allow to change the behavior of a command.\n+\n+* There are basically three types of options:\n+  'boolean' options,\n+  options with (mandatory) 'arguments' and\n+  options with 'optional arguments'\n+  (i.e. a boolean option that can be adjusted).\n+\n+* There are basically two forms of options: \n+  'Short options' consist of one dash (`-`) and one alphanumeric\n+  character.\n+  'Long options' begin with two dashes (`\\--`) and some \n+  alphanumeric characters.\n+\n+* Options are case-sensitive.\n+  Please define 'lower-case long options' only.\n+\n+The parse-options API allows:\n+\n+* 'sticked' and 'separate form' of options with arguments.\n+  `-oArg` is sticked, `-o Arg` is separate form.\n+  `\\--option=Arg` is sticked, `\\--option Arg` is separate form.\n+\n+* Long options may be 'abbreviated', as long as the abbreviation\n+  is unambiguous.\n+\n+* Short options may be bundled, e.g. `-a -b` can be specified as `-ab`.\n+\n+* Boolean long options can be 'negated' (or 'unset') by prepending\n+  `no-`, e.g. `\\--no-abbrev` instead of `\\--abbrev`.\n+\n+* Options and other arguments can clearly be separated using the `\\--`\n+  option, e.g. `-a -b \\--option \\-- \\--this-is-a-file` indicates that\n+  `\\--this-is-a-file` must not be processed as an option.\n+\n+Steps to parse options\n+----------------------\n+\n+. `#include \"parse-options.h\"`\n+\n+. define a NULL-terminated\n+  `static const char * const builtin_foo_usage[]` array\n+  containing alternative usage strings\n+\n+. define `builtin_foo_options` array as described below\n+  in section 'Data Structure'.\n+\n+. in `cmd_foo(int argc, const char **argv, const char *prefix)`\n+  call\n+\n+\targc = parse_options(argc, argv, builtin_foo_options, builtin_foo_usage, flags);\n++\n+`parse_options()` will filter out the processed options of `argv[]` and leave the\n+non-option arguments in `argv[]`.\n+`argc` is updated appropriately because of the assignment.\n++\n+Flags are the bitwise-or of:\n+\n+`PARSE_OPT_KEEP_DASHDASH`::\n+\tUsually long options (`\\--long-opt`) are boiled down to their\n+\tshort option equivalent, if available.\n+\tUsing this flag, long options are kept.\n+\n+`PARSE_OPT_STOP_AT_NON_OPTION`::\n+\tUsually all options the whole argument vector is massaged\n+\tand reordered.\n+\tUsing this flag, processing is stopped at the first non-option\n+\targument.\n+\n+Data Structure\n+--------------\n+\n+The main data structure is an array of the `option` struct,\n+say `static struct option builtin_add_options[]`.\n+There are some macros to easily define options:\n+\n+`OPT__ABBREV(&int_var)`::\n+\tAdd `\\--abbrev [<n>]`.\n+\n+`OPT__DRY_RUN(&int_var)`::\n+\tAdd `-n, \\--dry-run`.\n+\n+`OPT__QUIET(&int_var)`::\n+\tAdd `-q, \\--quiet`.\n+\n+`OPT__VERBOSE(&int_var)`::\n+\tAdd `-v, \\--verbose`.\n+\n+`OPT_GROUP(description)`::\n+\tStart an option group. `description` is a short string that\n+\tdescribes the group or an empty string.\n+\tStart the description with an upper-case letter.\n+\n+`OPT_BOOLEAN(short, long, &int_var, description)`::\n+\tIntroduce a boolean option.\n+\t`int_var` is incremented on each use.\n+\n+`OPT_BIT(short, long, &int_var, description, mask)`::\n+\tIntroduce a boolean option.\n+\tIf used, `int_var` is bitwise-ored with `mask`.\n+\n+`OPT_SET_INT(short, long, &int_var, description, integer)`::\n+\tIntroduce a boolean option.\n+\tIf used, set `int_var` to `integer`.\n+\n+`OPT_SET_PTR(short, long, &ptr_var, description, ptr)`::\n+\tIntroduce a boolean option.\n+\tIf used, set `ptr_var` to `ptr`.\n+\n+`OPT_STRING(short, long, &str_var, arg_str, description)`::\n+\tIntroduce an option with string argument.\n+\tThe string argument is put into `str_var`.\n+\n+`OPT_INTEGER(short, long, &int_var, description)`::\n+\tIntroduce an option with integer argument.\n+\tThe integer is put into `int_var`.\n+\n+`OPT_DATE(short, long, &int_var, description)`::\n+\tIntroduce an option with date argument, see `approxidate()`.\n+\tThe timestamp is put into `int_var`.\n+\n+`OPT_CALLBACK(short, long, &var, arg_str, description, func_ptr)`::\n+\tIntroduce an option with argument.\n+\tThe argument will be fed into the function given by `func_ptr`\n+\tand the result will be put into `var`.\n+\tSee 'Option Callbacks' below for a more elaborate description.\n+\n+`OPT_ARGUMENT(long, description)`::\n+\tIntroduce a long-option argument that will be kept in `argv[]`.\n+\n+\n+The last element of the array must be `OPT_END()`.\n+\n+If not stated otherwise, interpret the arguments as follows:\n+\n+* `short` is a character for the short option\n+  (e.g. `\\'e\\'` for `-e`, use `0` to omit),\n+\n+* `long` is a string for the long option\n+  (e.g. `\"example\"` for `\\--example`, use `NULL` to omit),\n+\n+* `int_var` is an integer variable,\n+\n+* `str_var` is a string variable (`char *`),\n+\n+* `arg_str` is the string that is shown as argument\n+  (e.g. `\"branch\"` will result in `<branch>`).\n+  If set to `NULL`, three dots (`...`) will be displayed.\n+\n+* `description` is a short string to describe the effect of the option.\n+  It shall begin with a lower-case letter and a full stop (`.`) shall be\n+  omitted at the end.\n+\n+Option Callbacks\n+----------------\n+\n+The function must be defined in this form:\n+\n+\tint func(const struct option *opt, const char *arg, int unset)\n+\n+The callback mechanism is as follows:\n+\n+* Inside `funct`, the only interesting member of the structure\n+  given by `opt` is the void pointer `opt->value`.\n+  `\\*opt->value` will be the value that is saved into `var`, if you\n+  use `OPT_CALLBACK()`.\n+  For example, do `*(unsigned long *)opt->value = 42;` to get 42\n+  into an `unsigned long` variable.\n+\n+* Return value `0` indicates success and non-zero return\n+  value will invoke `usage_with_options()` and, thus, die.\n+\n+* If the user negates the option, `arg` is `NULL` and `unset` is 1.\n+\n+Sophisticated option parsing\n+----------------------------\n+\n+If you need, for example, option callbacks with optional arguments\n+or without arguments at all, or if you need other special cases,\n+that are not handled by the macros above, you need to specify the\n+members of the `option` structure manually.\n+\n+This is not covered in this document, but well documented\n+in `parse-options.h` itself.\n+\n+Examples\n+--------\n+\n+See `test-parse-options.c` and\n+`builtin-add.c`,\n+`builtin-clone.c`,\n+`builtin-commit.c`,\n+`builtin-fetch.c`,\n+`builtin-fsck.c`,\n+`builtin-rm.c`\n+for real-world examples.\n-- \n1.5.6.310.g344d\n"},{"id":"80582","messageId":"1214092802-8175-2-git-send-email-s-beyer@gmx.net","threadId":"14078","inReplyTo":"1214092802-8175-1-git-send-email-s-beyer@gmx.net","subject":"[PATCH 2/2] Extend parse-options test suite","fromName":"Stephan Beyer","fromEmail":"s-beyer@gmx.net","sentAt":"2008-06-22T00:00:02Z","receivedAt":"2008-06-22T00:00:02Z","isPatch":true,"sender":{"key":"s-beyer@gmx.net","avatar":"https://avatars.githubusercontent.com/u/143889?v=4"},"body":"This patch serves two purposes:\n 1. test-parse-option.c should be a more complete\n    example for the parse-options API, and\n 2. there have been no tests for OPT_CALLBACK,\n    OPT_DATE, OPT_BIT, OPT_SET_INT and OPT_SET_PTR\n    before.\n\nSigned-off-by: Stephan Beyer <s-beyer@gmx.net>\n---\nOn reviewing you should perhaps first have a look at the .c file,\nthen the changes of t0040 make more sense.\n\n t/t0040-parse-options.sh |  116 +++++++++++++++++++++++++++++++++++++++++++---\n test-parse-options.c     |   39 ++++++++++++++--\n 2 files changed, 144 insertions(+), 11 deletions(-)\n\ndiff --git a/t/t0040-parse-options.sh b/t/t0040-parse-options.sh\nindex 9965cfa..87c6164 100755\n--- a/t/t0040-parse-options.sh\n+++ b/t/t0040-parse-options.sh\n@@ -11,23 +11,35 @@ cat > expect.err << EOF\n usage: test-parse-options <options>\n \n     -b, --boolean         get a boolean\n+    -4, --or4             bitwise-or boolean with ...0100\n+\n     -i, --integer <n>     get a integer\n     -j <n>                get a integer, too\n+    --set23               set integer to 23\n+    -t <time>             get timestamp of <time>\n+    -L, --length <str>    get length of <str>\n \n-string options\n+String options\n     -s, --string <string>\n                           get a string\n     --string2 <str>       get another string\n     --st <st>             get another string (pervert ordering)\n     -o <str>              get another string\n+    --default-string      set string to default\n \n-magic arguments\n+Magic arguments\n     --quux                means --quux\n \n+Standard options\n+    --abbrev [<n>]        use <n> digits to display SHA-1s\n+    -v, --verbose         be verbose\n+    -n, --dry-run         dry run\n+    -q, --quiet           be quiet\n+\n EOF\n \n test_expect_success 'test help' '\n-\t! test-parse-options -h > output 2> output.err &&\n+\ttest_must_fail test-parse-options -h > output 2> output.err &&\n \ttest ! -s output &&\n \ttest_cmp expect.err output.err\n '\n@@ -36,21 +48,31 @@ cat > expect << EOF\n boolean: 2\n integer: 1729\n string: 123\n+abbrev: 7\n+verbose: 2\n+quiet: no\n+dry run: yes\n EOF\n \n test_expect_success 'short options' '\n-\ttest-parse-options -s123 -b -i 1729 -b > output 2> output.err &&\n+\ttest-parse-options -s123 -b -i 1729 -b -vv -n > output 2> output.err &&\n \ttest_cmp expect output &&\n \ttest ! -s output.err\n '\n+\n cat > expect << EOF\n boolean: 2\n integer: 1729\n string: 321\n+abbrev: 10\n+verbose: 2\n+quiet: no\n+dry run: no\n EOF\n \n test_expect_success 'long options' '\n \ttest-parse-options --boolean --integer 1729 --boolean --string2=321 \\\n+\t\t--verbose --verbose --no-dry-run --abbrev=10 \\\n \t\t> output 2> output.err &&\n \ttest ! -s output.err &&\n \ttest_cmp expect output\n@@ -60,6 +82,10 @@ cat > expect << EOF\n boolean: 1\n integer: 13\n string: 123\n+abbrev: 7\n+verbose: 0\n+quiet: no\n+dry run: no\n arg 00: a1\n arg 01: b1\n arg 02: --boolean\n@@ -76,6 +102,10 @@ cat > expect << EOF\n boolean: 0\n integer: 2\n string: (not set)\n+abbrev: 7\n+verbose: 0\n+quiet: no\n+dry run: no\n EOF\n \n test_expect_success 'unambiguously abbreviated option' '\n@@ -99,6 +129,10 @@ cat > expect << EOF\n boolean: 0\n integer: 0\n string: 123\n+abbrev: 7\n+verbose: 0\n+quiet: no\n+dry run: no\n EOF\n \n test_expect_success 'non ambiguous option (after two options it abbreviates)' '\n@@ -107,20 +141,24 @@ test_expect_success 'non ambiguous option (after two options it abbreviates)' '\n \ttest_cmp expect output\n '\n \n-cat > expect.err << EOF\n+cat > typo.err << EOF\n error: did you mean \\`--boolean\\` (with two dashes ?)\n EOF\n \n test_expect_success 'detect possible typos' '\n-\t! test-parse-options -boolean > output 2> output.err &&\n+\ttest_must_fail test-parse-options -boolean > output 2> output.err &&\n \ttest ! -s output &&\n-\ttest_cmp expect.err output.err\n+\ttest_cmp typo.err output.err\n '\n \n cat > expect <<EOF\n boolean: 0\n integer: 0\n string: (not set)\n+abbrev: 7\n+verbose: 0\n+quiet: no\n+dry run: no\n arg 00: --quux\n EOF\n \n@@ -130,4 +168,68 @@ test_expect_success 'keep some options as arguments' '\n         test_cmp expect output\n '\n \n+cat > expect <<EOF\n+boolean: 0\n+integer: 1\n+string: default\n+abbrev: 7\n+verbose: 0\n+quiet: yes\n+dry run: no\n+arg 00: foo\n+EOF\n+\n+test_expect_success 'OPT_DATE() and OPT_SET_PTR() work' '\n+\ttest-parse-options -t \"1970-01-01 00:00:01 +0000\" --default-string \\\n+\t\tfoo -q > output 2> output.err &&\n+\ttest ! -s output.err &&\n+\ttest_cmp expect output\n+'\n+\n+cat > expect <<EOF\n+Callback: \"four\", 0\n+boolean: 5\n+integer: 4\n+string: (not set)\n+abbrev: 7\n+verbose: 0\n+quiet: no\n+dry run: no\n+EOF\n+\n+test_expect_success 'OPT_CALLBACK() and OPT_BIT() work' '\n+\ttest-parse-options --length=four -b -4 > output 2> output.err &&\n+\ttest ! -s output.err &&\n+\ttest_cmp expect output\n+'\n+\n+cat > expect <<EOF\n+Callback: \"not set\", 1\n+EOF\n+\n+test_expect_success 'OPT_CALLBACK() and callback errors work' '\n+\ttest_must_fail test-parse-options --no-length > output 2> output.err &&\n+\ttest_cmp expect output &&\n+\ttest_cmp expect.err output.err\n+'\n+\n+cat > expect <<EOF\n+boolean: 1\n+integer: 23\n+string: (not set)\n+abbrev: 7\n+verbose: 0\n+quiet: no\n+dry run: no\n+EOF\n+\n+test_expect_success 'OPT_BIT() and OPT_SET_INT() work' '\n+\ttest-parse-options --set23 -bbbbb --no-or4 > output 2> output.err &&\n+\ttest ! -s output.err &&\n+\ttest_cmp expect output\n+'\n+\n+# --or4\n+# --no-or4\n+\n test_done\ndiff --git a/test-parse-options.c b/test-parse-options.c\nindex 73360d7..2a79e72 100644\n--- a/test-parse-options.c\n+++ b/test-parse-options.c\n@@ -2,9 +2,22 @@\n #include \"parse-options.h\"\n \n static int boolean = 0;\n-static int integer = 0;\n+static unsigned long integer = 0;\n+static int abbrev = 7;\n+static int verbose = 0, dry_run = 0, quiet = 0;\n static char *string = NULL;\n \n+int length_callback(const struct option *opt, const char *arg, int unset)\n+{\n+\tprintf(\"Callback: \\\"%s\\\", %d\\n\",\n+\t\t(arg ? arg : \"not set\"), unset);\n+\tif (unset)\n+\t\treturn 1; /* do not support unset */\n+\n+\t*(unsigned long *)opt->value = strlen(arg);\n+\treturn 0;\n+}\n+\n int main(int argc, const char **argv)\n {\n \tconst char *usage[] = {\n@@ -13,15 +26,29 @@ int main(int argc, const char **argv)\n \t};\n \tstruct option options[] = {\n \t\tOPT_BOOLEAN('b', \"boolean\", &boolean, \"get a boolean\"),\n+\t\tOPT_BIT('4', \"or4\", &boolean,\n+\t\t\t\"bitwise-or boolean with ...0100\", 4),\n+\t\tOPT_GROUP(\"\"),\n \t\tOPT_INTEGER('i', \"integer\", &integer, \"get a integer\"),\n \t\tOPT_INTEGER('j', NULL, &integer, \"get a integer, too\"),\n-\t\tOPT_GROUP(\"string options\"),\n+\t\tOPT_SET_INT(0, \"set23\", &integer, \"set integer to 23\", 23),\n+\t\tOPT_DATE('t', NULL, &integer, \"get timestamp of <time>\"),\n+\t\tOPT_CALLBACK('L', \"length\", &integer, \"str\",\n+\t\t\t\"get length of <str>\", length_callback),\n+\t\tOPT_GROUP(\"String options\"),\n \t\tOPT_STRING('s', \"string\", &string, \"string\", \"get a string\"),\n \t\tOPT_STRING(0, \"string2\", &string, \"str\", \"get another string\"),\n \t\tOPT_STRING(0, \"st\", &string, \"st\", \"get another string (pervert ordering)\"),\n \t\tOPT_STRING('o', NULL, &string, \"str\", \"get another string\"),\n-\t\tOPT_GROUP(\"magic arguments\"),\n+\t\tOPT_SET_PTR(0, \"default-string\", &string,\n+\t\t\t\"set string to default\", (unsigned long)\"default\"),\n+\t\tOPT_GROUP(\"Magic arguments\"),\n \t\tOPT_ARGUMENT(\"quux\", \"means --quux\"),\n+\t\tOPT_GROUP(\"Standard options\"),\n+\t\tOPT__ABBREV(&abbrev),\n+\t\tOPT__VERBOSE(&verbose),\n+\t\tOPT__DRY_RUN(&dry_run),\n+\t\tOPT__QUIET(&quiet),\n \t\tOPT_END(),\n \t};\n \tint i;\n@@ -29,8 +56,12 @@ int main(int argc, const char **argv)\n \targc = parse_options(argc, argv, options, usage, 0);\n \n \tprintf(\"boolean: %d\\n\", boolean);\n-\tprintf(\"integer: %d\\n\", integer);\n+\tprintf(\"integer: %lu\\n\", integer);\n \tprintf(\"string: %s\\n\", string ? string : \"(not set)\");\n+\tprintf(\"abbrev: %d\\n\", abbrev);\n+\tprintf(\"verbose: %d\\n\", verbose);\n+\tprintf(\"quiet: %s\\n\", quiet ? \"yes\" : \"no\");\n+\tprintf(\"dry run: %s\\n\", dry_run ? \"yes\" : \"no\");\n \n \tfor (i = 0; i < argc; i++)\n \t\tprintf(\"arg %02d: %s\\n\", i, argv[i]);\n-- \n1.5.6.310.g344d\n"},{"id":"80605","messageId":"200806221449.08307.barra_cuda@katamail.com","threadId":"14078","inReplyTo":"1214092802-8175-1-git-send-email-s-beyer@gmx.net","subject":"Re: [PATCH 1/2] api-parse-options.txt: Introduce documentation for parse options API","fromName":"Michele Ballabio","fromEmail":"barra_cuda@katamail.com","sentAt":"2008-06-22T12:49:08Z","receivedAt":"2008-06-22T12:49:08Z","isPatch":true,"sender":{"key":"barra_cuda@katamail.com","avatar":"https://avatars.githubusercontent.com/u/16371673?v=4"},"body":"On Sunday 22 June 2008, Stephan Beyer wrote:\n> +`PARSE_OPT_KEEP_DASHDASH`::\n> +       Usually long options (`\\--long-opt`) are boiled down to their\n> +       short option equivalent, if available.\n> +       Using this flag, long options are kept.\n\nNo: PARSE_OPT_KEEP_DASHDASH keeps the \"--\" that usually separates options\nfrom files, as in\n\n\tgit ls-files [options] -- [files]\n\ni.e. after parsing, the new argv will still contain \"--\".\n\n> +`OPT__ABBREV(&int_var)`::\n> +       Add `\\--abbrev [<n>]`.\n\nbetter:\n+       Add `\\--abbrev[=<n>]`.\n\nsince the <n> is optional.\n"},{"id":"80610","messageId":"20080622134550.GA5279@leksak.fem-net","threadId":"14078","inReplyTo":"200806221449.08307.barra_cuda@katamail.com","subject":"Re: [PATCH 1/2] api-parse-options.txt: Introduce documentation for parse options API","fromName":"Stephan Beyer","fromEmail":"s-beyer@gmx.net","sentAt":"2008-06-22T13:45:50Z","receivedAt":"2008-06-22T13:45:50Z","isPatch":true,"sender":{"key":"s-beyer@gmx.net","avatar":"https://avatars.githubusercontent.com/u/143889?v=4"},"body":"> > +`PARSE_OPT_KEEP_DASHDASH`::\n> > +       Usually long options (`\\--long-opt`) are boiled down to their\n> > +       short option equivalent, if available.\n> > +       Using this flag, long options are kept.\n> \n> No: PARSE_OPT_KEEP_DASHDASH keeps the \"--\" that usually separates options\n> from files, as in\n\nEh, right, of course!  What I wrote doesn't make any sense. ;-)\n\n> > +`OPT__ABBREV(&int_var)`::\n> > +       Add `\\--abbrev [<n>]`.\n> \n> better:\n> +       Add `\\--abbrev[=<n>]`.\n> \n> since the <n> is optional.\n\nThat's right and I also expected that print_usage_with_options() would\ndo so, since the other variant is ambiguous, BUT:\n\n$ ./test-parse-options -h 2>&1 | grep abbrev\n    --abbrev [<n>]        use <n> digits to display SHA-1s\n\nAnd I didn't want to change parse-options.c ;-)\n\n\nAny other comments?\n\nRegards,\n  Stephan\n\n-- \nStephan Beyer <s-beyer@gmx.net>, PGP 0x6EDDD207FCC5040F\n"},{"id":"80618","messageId":"200806221639.04811.barra_cuda@katamail.com","threadId":"14078","inReplyTo":"20080622134550.GA5279@leksak.fem-net","subject":"[PATCH] parse-options.c: fix documentation syntax of optional arguments","fromName":"Michele Ballabio","fromEmail":"barra_cuda@katamail.com","sentAt":"2008-06-22T14:39:04Z","receivedAt":"2008-06-22T14:39:04Z","isPatch":true,"sender":{"key":"barra_cuda@katamail.com","avatar":"https://avatars.githubusercontent.com/u/16371673?v=4"},"body":"When an argument for an option is optional, short options don't need a\nspace between the option and the argument, and long options need a \"=\".\nOtherwise, arguments are misinterpreted.\n\nSigned-off-by: Michele Ballabio <barra_cuda@katamail.com>\n---\n\nOn Sunday 22 June 2008, Stephan Beyer wrote:\n> > > +`OPT__ABBREV(&int_var)`::\n> > > +       Add `\\--abbrev [<n>]`.\n> > \n> > better:\n> > +       Add `\\--abbrev[=<n>]`.\n> > \n> > since the <n> is optional.\n> \n> That's right and I also expected that print_usage_with_options() would\n> do so, since the other variant is ambiguous, BUT:\n> \n> $ ./test-parse-options -h 2>&1 | grep abbrev\n>     --abbrev [<n>]        use <n> digits to display SHA-1s\n> \n> And I didn't want to change parse-options.c ;-)\n\nHere it is.\n\n parse-options.c               |   15 ++++++++++++---\n t/t1502-rev-parse-parseopt.sh |    2 +-\n 2 files changed, 13 insertions(+), 4 deletions(-)\n\ndiff --git a/parse-options.c b/parse-options.c\nindex acf3fe3..f8d52e2 100644\n--- a/parse-options.c\n+++ b/parse-options.c\n@@ -344,7 +344,10 @@ void usage_with_options_internal(const char * const *usagestr,\n \t\t\tbreak;\n \t\tcase OPTION_INTEGER:\n \t\t\tif (opts->flags & PARSE_OPT_OPTARG)\n-\t\t\t\tpos += fprintf(stderr, \"[<n>]\");\n+\t\t\t\tif (opts->long_name)\n+\t\t\t\t\tpos += fprintf(stderr, \"[=<n>]\");\n+\t\t\t\telse\n+\t\t\t\t\tpos += fprintf(stderr, \"[<n>]\");\n \t\t\telse\n \t\t\t\tpos += fprintf(stderr, \" <n>\");\n \t\t\tbreak;\n@@ -355,12 +358,18 @@ void usage_with_options_internal(const char * const *usagestr,\n \t\tcase OPTION_STRING:\n \t\t\tif (opts->argh) {\n \t\t\t\tif (opts->flags & PARSE_OPT_OPTARG)\n-\t\t\t\t\tpos += fprintf(stderr, \" [<%s>]\", opts->argh);\n+\t\t\t\t\tif (opts->long_name)\n+\t\t\t\t\t\tpos += fprintf(stderr, \"[=<%s>]\", opts->argh);\n+\t\t\t\t\telse\n+\t\t\t\t\t\tpos += fprintf(stderr, \"[<%s>]\", opts->argh);\n \t\t\t\telse\n \t\t\t\t\tpos += fprintf(stderr, \" <%s>\", opts->argh);\n \t\t\t} else {\n \t\t\t\tif (opts->flags & PARSE_OPT_OPTARG)\n-\t\t\t\t\tpos += fprintf(stderr, \" [...]\");\n+\t\t\t\t\tif (opts->long_name)\n+\t\t\t\t\t\tpos += fprintf(stderr, \"[=...]\");\n+\t\t\t\t\telse\n+\t\t\t\t\t\tpos += fprintf(stderr, \"[...]\");\n \t\t\t\telse\n \t\t\t\t\tpos += fprintf(stderr, \" ...\");\n \t\t\t}\ndiff --git a/t/t1502-rev-parse-parseopt.sh b/t/t1502-rev-parse-parseopt.sh\nindex d24a47d..3508d0a 100755\n--- a/t/t1502-rev-parse-parseopt.sh\n+++ b/t/t1502-rev-parse-parseopt.sh\n@@ -13,7 +13,7 @@ usage: some-command [options] <args>...\n     --bar ...             some cool option --bar with an argument\n \n An option group Header\n-    -C [...]              option C with an optional argument\n+    -C[...]               option C with an optional argument\n \n Extras\n     --extra1              line above used to cause a segfault but no longer does\n-- \n1.5.6\n"},{"id":"80621","messageId":"1214147066-10979-1-git-send-email-s-beyer@gmx.net","threadId":"14078","inReplyTo":"200806221639.04811.barra_cuda@katamail.com","subject":"[PATCH v2 1/2] api-parse-options.txt: Introduce documentation for parse options API","fromName":"Stephan Beyer","fromEmail":"s-beyer@gmx.net","sentAt":"2008-06-22T15:04:25Z","receivedAt":"2008-06-22T15:04:25Z","isPatch":true,"sender":{"key":"s-beyer@gmx.net","avatar":"https://avatars.githubusercontent.com/u/143889?v=4"},"body":"Add some documentation of basics, macros and callback\nimplementation of the parse-options API.\n\nSigned-off-by: Stephan Beyer <s-beyer@gmx.net>\n---\nHa, great ;)\n\nSo here are my patches, now based on your one.\n\nRegards,\n  Stephan\n\n Documentation/technical/api-parse-options.txt |  204 ++++++++++++++++++++++++-\n 1 files changed, 202 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/technical/api-parse-options.txt b/Documentation/technical/api-parse-options.txt\nindex b7cda94..60a433b 100644\n--- a/Documentation/technical/api-parse-options.txt\n+++ b/Documentation/technical/api-parse-options.txt\n@@ -1,6 +1,206 @@\n parse-options API\n =================\n \n-Talk about <parse-options.h>\n+The parse-options API is used to parse and massage options in git\n+and to provide a usage help with consistent look.\n \n-(Pierre)\n+Basics\n+------\n+\n+The argument vector `argv[]` may usually contain mandatory or optional\n+'non-option arguments', e.g. a filename or a branch, and 'options'.\n+Options are optional arguments that start with a dash and\n+that allow to change the behavior of a command.\n+\n+* There are basically three types of options:\n+  'boolean' options,\n+  options with (mandatory) 'arguments' and\n+  options with 'optional arguments'\n+  (i.e. a boolean option that can be adjusted).\n+\n+* There are basically two forms of options: \n+  'Short options' consist of one dash (`-`) and one alphanumeric\n+  character.\n+  'Long options' begin with two dashes (`\\--`) and some \n+  alphanumeric characters.\n+\n+* Options are case-sensitive.\n+  Please define 'lower-case long options' only.\n+\n+The parse-options API allows:\n+\n+* 'sticked' and 'separate form' of options with arguments.\n+  `-oArg` is sticked, `-o Arg` is separate form.\n+  `\\--option=Arg` is sticked, `\\--option Arg` is separate form.\n+\n+* Long options may be 'abbreviated', as long as the abbreviation\n+  is unambiguous.\n+\n+* Short options may be bundled, e.g. `-a -b` can be specified as `-ab`.\n+\n+* Boolean long options can be 'negated' (or 'unset') by prepending\n+  `no-`, e.g. `\\--no-abbrev` instead of `\\--abbrev`.\n+\n+* Options and non-option arguments can clearly be separated using the `\\--`\n+  option, e.g. `-a -b \\--option \\-- \\--this-is-a-file` indicates that\n+  `\\--this-is-a-file` must not be processed as an option.\n+\n+Steps to parse options\n+----------------------\n+\n+. `#include \"parse-options.h\"`\n+\n+. define a NULL-terminated\n+  `static const char * const builtin_foo_usage[]` array\n+  containing alternative usage strings\n+\n+. define `builtin_foo_options` array as described below\n+  in section 'Data Structure'.\n+\n+. in `cmd_foo(int argc, const char **argv, const char *prefix)`\n+  call\n+\n+\targc = parse_options(argc, argv, builtin_foo_options, builtin_foo_usage, flags);\n++\n+`parse_options()` will filter out the processed options of `argv[]` and leave the\n+non-option arguments in `argv[]`.\n+`argc` is updated appropriately because of the assignment.\n++\n+Flags are the bitwise-or of:\n+\n+`PARSE_OPT_KEEP_DASHDASH`::\n+\tKeep the `\\--` that usually separates options from\n+\tnon-option arguments.\n+\n+`PARSE_OPT_STOP_AT_NON_OPTION`::\n+\tUsually the whole argument vector is massaged and reordered.\n+\tUsing this flag, processing is stopped at the first non-option\n+\targument.\n+\n+Data Structure\n+--------------\n+\n+The main data structure is an array of the `option` struct,\n+say `static struct option builtin_add_options[]`.\n+There are some macros to easily define options:\n+\n+`OPT__ABBREV(&int_var)`::\n+\tAdd `\\--abbrev[=<n>]`.\n+\n+`OPT__DRY_RUN(&int_var)`::\n+\tAdd `-n, \\--dry-run`.\n+\n+`OPT__QUIET(&int_var)`::\n+\tAdd `-q, \\--quiet`.\n+\n+`OPT__VERBOSE(&int_var)`::\n+\tAdd `-v, \\--verbose`.\n+\n+`OPT_GROUP(description)`::\n+\tStart an option group. `description` is a short string that\n+\tdescribes the group or an empty string.\n+\tStart the description with an upper-case letter.\n+\n+`OPT_BOOLEAN(short, long, &int_var, description)`::\n+\tIntroduce a boolean option.\n+\t`int_var` is incremented on each use.\n+\n+`OPT_BIT(short, long, &int_var, description, mask)`::\n+\tIntroduce a boolean option.\n+\tIf used, `int_var` is bitwise-ored with `mask`.\n+\n+`OPT_SET_INT(short, long, &int_var, description, integer)`::\n+\tIntroduce a boolean option.\n+\tIf used, set `int_var` to `integer`.\n+\n+`OPT_SET_PTR(short, long, &ptr_var, description, ptr)`::\n+\tIntroduce a boolean option.\n+\tIf used, set `ptr_var` to `ptr`.\n+\n+`OPT_STRING(short, long, &str_var, arg_str, description)`::\n+\tIntroduce an option with string argument.\n+\tThe string argument is put into `str_var`.\n+\n+`OPT_INTEGER(short, long, &int_var, description)`::\n+\tIntroduce an option with integer argument.\n+\tThe integer is put into `int_var`.\n+\n+`OPT_DATE(short, long, &int_var, description)`::\n+\tIntroduce an option with date argument, see `approxidate()`.\n+\tThe timestamp is put into `int_var`.\n+\n+`OPT_CALLBACK(short, long, &var, arg_str, description, func_ptr)`::\n+\tIntroduce an option with argument.\n+\tThe argument will be fed into the function given by `func_ptr`\n+\tand the result will be put into `var`.\n+\tSee 'Option Callbacks' below for a more elaborate description.\n+\n+`OPT_ARGUMENT(long, description)`::\n+\tIntroduce a long-option argument that will be kept in `argv[]`.\n+\n+\n+The last element of the array must be `OPT_END()`.\n+\n+If not stated otherwise, interpret the arguments as follows:\n+\n+* `short` is a character for the short option\n+  (e.g. `\\'e\\'` for `-e`, use `0` to omit),\n+\n+* `long` is a string for the long option\n+  (e.g. `\"example\"` for `\\--example`, use `NULL` to omit),\n+\n+* `int_var` is an integer variable,\n+\n+* `str_var` is a string variable (`char *`),\n+\n+* `arg_str` is the string that is shown as argument\n+  (e.g. `\"branch\"` will result in `<branch>`).\n+  If set to `NULL`, three dots (`...`) will be displayed.\n+\n+* `description` is a short string to describe the effect of the option.\n+  It shall begin with a lower-case letter and a full stop (`.`) shall be\n+  omitted at the end.\n+\n+Option Callbacks\n+----------------\n+\n+The function must be defined in this form:\n+\n+\tint func(const struct option *opt, const char *arg, int unset)\n+\n+The callback mechanism is as follows:\n+\n+* Inside `funct`, the only interesting member of the structure\n+  given by `opt` is the void pointer `opt->value`.\n+  `\\*opt->value` will be the value that is saved into `var`, if you\n+  use `OPT_CALLBACK()`.\n+  For example, do `*(unsigned long *)opt->value = 42;` to get 42\n+  into an `unsigned long` variable.\n+\n+* Return value `0` indicates success and non-zero return\n+  value will invoke `usage_with_options()` and, thus, die.\n+\n+* If the user negates the option, `arg` is `NULL` and `unset` is 1.\n+\n+Sophisticated option parsing\n+----------------------------\n+\n+If you need, for example, option callbacks with optional arguments\n+or without arguments at all, or if you need other special cases,\n+that are not handled by the macros above, you need to specify the\n+members of the `option` structure manually.\n+\n+This is not covered in this document, but well documented\n+in `parse-options.h` itself.\n+\n+Examples\n+--------\n+\n+See `test-parse-options.c` and\n+`builtin-add.c`,\n+`builtin-clone.c`,\n+`builtin-commit.c`,\n+`builtin-fetch.c`,\n+`builtin-fsck.c`,\n+`builtin-rm.c`\n+for real-world examples.\n-- \n1.5.6.310.g344d\n"},{"id":"80622","messageId":"1214147066-10979-2-git-send-email-s-beyer@gmx.net","threadId":"14078","inReplyTo":"1214147066-10979-1-git-send-email-s-beyer@gmx.net","subject":"[PATCH v2 2/2] Extend parse-options test suite","fromName":"Stephan Beyer","fromEmail":"s-beyer@gmx.net","sentAt":"2008-06-22T15:04:26Z","receivedAt":"2008-06-22T15:04:26Z","isPatch":true,"sender":{"key":"s-beyer@gmx.net","avatar":"https://avatars.githubusercontent.com/u/143889?v=4"},"body":"This patch serves two purposes:\n 1. test-parse-option.c should be a more complete\n    example for the parse-options API, and\n 2. there have been no tests for OPT_CALLBACK,\n    OPT_DATE, OPT_BIT, OPT_SET_INT and OPT_SET_PTR\n    before.\n\nSigned-off-by: Stephan Beyer <s-beyer@gmx.net>\n---\n t/t0040-parse-options.sh |  116 +++++++++++++++++++++++++++++++++++++++++++---\n test-parse-options.c     |   39 ++++++++++++++--\n 2 files changed, 144 insertions(+), 11 deletions(-)\n\ndiff --git a/t/t0040-parse-options.sh b/t/t0040-parse-options.sh\nindex 9965cfa..6309aed 100755\n--- a/t/t0040-parse-options.sh\n+++ b/t/t0040-parse-options.sh\n@@ -11,23 +11,35 @@ cat > expect.err << EOF\n usage: test-parse-options <options>\n \n     -b, --boolean         get a boolean\n+    -4, --or4             bitwise-or boolean with ...0100\n+\n     -i, --integer <n>     get a integer\n     -j <n>                get a integer, too\n+    --set23               set integer to 23\n+    -t <time>             get timestamp of <time>\n+    -L, --length <str>    get length of <str>\n \n-string options\n+String options\n     -s, --string <string>\n                           get a string\n     --string2 <str>       get another string\n     --st <st>             get another string (pervert ordering)\n     -o <str>              get another string\n+    --default-string      set string to default\n \n-magic arguments\n+Magic arguments\n     --quux                means --quux\n \n+Standard options\n+    --abbrev[=<n>]        use <n> digits to display SHA-1s\n+    -v, --verbose         be verbose\n+    -n, --dry-run         dry run\n+    -q, --quiet           be quiet\n+\n EOF\n \n test_expect_success 'test help' '\n-\t! test-parse-options -h > output 2> output.err &&\n+\ttest_must_fail test-parse-options -h > output 2> output.err &&\n \ttest ! -s output &&\n \ttest_cmp expect.err output.err\n '\n@@ -36,21 +48,31 @@ cat > expect << EOF\n boolean: 2\n integer: 1729\n string: 123\n+abbrev: 7\n+verbose: 2\n+quiet: no\n+dry run: yes\n EOF\n \n test_expect_success 'short options' '\n-\ttest-parse-options -s123 -b -i 1729 -b > output 2> output.err &&\n+\ttest-parse-options -s123 -b -i 1729 -b -vv -n > output 2> output.err &&\n \ttest_cmp expect output &&\n \ttest ! -s output.err\n '\n+\n cat > expect << EOF\n boolean: 2\n integer: 1729\n string: 321\n+abbrev: 10\n+verbose: 2\n+quiet: no\n+dry run: no\n EOF\n \n test_expect_success 'long options' '\n \ttest-parse-options --boolean --integer 1729 --boolean --string2=321 \\\n+\t\t--verbose --verbose --no-dry-run --abbrev=10 \\\n \t\t> output 2> output.err &&\n \ttest ! -s output.err &&\n \ttest_cmp expect output\n@@ -60,6 +82,10 @@ cat > expect << EOF\n boolean: 1\n integer: 13\n string: 123\n+abbrev: 7\n+verbose: 0\n+quiet: no\n+dry run: no\n arg 00: a1\n arg 01: b1\n arg 02: --boolean\n@@ -76,6 +102,10 @@ cat > expect << EOF\n boolean: 0\n integer: 2\n string: (not set)\n+abbrev: 7\n+verbose: 0\n+quiet: no\n+dry run: no\n EOF\n \n test_expect_success 'unambiguously abbreviated option' '\n@@ -99,6 +129,10 @@ cat > expect << EOF\n boolean: 0\n integer: 0\n string: 123\n+abbrev: 7\n+verbose: 0\n+quiet: no\n+dry run: no\n EOF\n \n test_expect_success 'non ambiguous option (after two options it abbreviates)' '\n@@ -107,20 +141,24 @@ test_expect_success 'non ambiguous option (after two options it abbreviates)' '\n \ttest_cmp expect output\n '\n \n-cat > expect.err << EOF\n+cat > typo.err << EOF\n error: did you mean \\`--boolean\\` (with two dashes ?)\n EOF\n \n test_expect_success 'detect possible typos' '\n-\t! test-parse-options -boolean > output 2> output.err &&\n+\ttest_must_fail test-parse-options -boolean > output 2> output.err &&\n \ttest ! -s output &&\n-\ttest_cmp expect.err output.err\n+\ttest_cmp typo.err output.err\n '\n \n cat > expect <<EOF\n boolean: 0\n integer: 0\n string: (not set)\n+abbrev: 7\n+verbose: 0\n+quiet: no\n+dry run: no\n arg 00: --quux\n EOF\n \n@@ -130,4 +168,68 @@ test_expect_success 'keep some options as arguments' '\n         test_cmp expect output\n '\n \n+cat > expect <<EOF\n+boolean: 0\n+integer: 1\n+string: default\n+abbrev: 7\n+verbose: 0\n+quiet: yes\n+dry run: no\n+arg 00: foo\n+EOF\n+\n+test_expect_success 'OPT_DATE() and OPT_SET_PTR() work' '\n+\ttest-parse-options -t \"1970-01-01 00:00:01 +0000\" --default-string \\\n+\t\tfoo -q > output 2> output.err &&\n+\ttest ! -s output.err &&\n+\ttest_cmp expect output\n+'\n+\n+cat > expect <<EOF\n+Callback: \"four\", 0\n+boolean: 5\n+integer: 4\n+string: (not set)\n+abbrev: 7\n+verbose: 0\n+quiet: no\n+dry run: no\n+EOF\n+\n+test_expect_success 'OPT_CALLBACK() and OPT_BIT() work' '\n+\ttest-parse-options --length=four -b -4 > output 2> output.err &&\n+\ttest ! -s output.err &&\n+\ttest_cmp expect output\n+'\n+\n+cat > expect <<EOF\n+Callback: \"not set\", 1\n+EOF\n+\n+test_expect_success 'OPT_CALLBACK() and callback errors work' '\n+\ttest_must_fail test-parse-options --no-length > output 2> output.err &&\n+\ttest_cmp expect output &&\n+\ttest_cmp expect.err output.err\n+'\n+\n+cat > expect <<EOF\n+boolean: 1\n+integer: 23\n+string: (not set)\n+abbrev: 7\n+verbose: 0\n+quiet: no\n+dry run: no\n+EOF\n+\n+test_expect_success 'OPT_BIT() and OPT_SET_INT() work' '\n+\ttest-parse-options --set23 -bbbbb --no-or4 > output 2> output.err &&\n+\ttest ! -s output.err &&\n+\ttest_cmp expect output\n+'\n+\n+# --or4\n+# --no-or4\n+\n test_done\ndiff --git a/test-parse-options.c b/test-parse-options.c\nindex 73360d7..2a79e72 100644\n--- a/test-parse-options.c\n+++ b/test-parse-options.c\n@@ -2,9 +2,22 @@\n #include \"parse-options.h\"\n \n static int boolean = 0;\n-static int integer = 0;\n+static unsigned long integer = 0;\n+static int abbrev = 7;\n+static int verbose = 0, dry_run = 0, quiet = 0;\n static char *string = NULL;\n \n+int length_callback(const struct option *opt, const char *arg, int unset)\n+{\n+\tprintf(\"Callback: \\\"%s\\\", %d\\n\",\n+\t\t(arg ? arg : \"not set\"), unset);\n+\tif (unset)\n+\t\treturn 1; /* do not support unset */\n+\n+\t*(unsigned long *)opt->value = strlen(arg);\n+\treturn 0;\n+}\n+\n int main(int argc, const char **argv)\n {\n \tconst char *usage[] = {\n@@ -13,15 +26,29 @@ int main(int argc, const char **argv)\n \t};\n \tstruct option options[] = {\n \t\tOPT_BOOLEAN('b', \"boolean\", &boolean, \"get a boolean\"),\n+\t\tOPT_BIT('4', \"or4\", &boolean,\n+\t\t\t\"bitwise-or boolean with ...0100\", 4),\n+\t\tOPT_GROUP(\"\"),\n \t\tOPT_INTEGER('i', \"integer\", &integer, \"get a integer\"),\n \t\tOPT_INTEGER('j', NULL, &integer, \"get a integer, too\"),\n-\t\tOPT_GROUP(\"string options\"),\n+\t\tOPT_SET_INT(0, \"set23\", &integer, \"set integer to 23\", 23),\n+\t\tOPT_DATE('t', NULL, &integer, \"get timestamp of <time>\"),\n+\t\tOPT_CALLBACK('L', \"length\", &integer, \"str\",\n+\t\t\t\"get length of <str>\", length_callback),\n+\t\tOPT_GROUP(\"String options\"),\n \t\tOPT_STRING('s', \"string\", &string, \"string\", \"get a string\"),\n \t\tOPT_STRING(0, \"string2\", &string, \"str\", \"get another string\"),\n \t\tOPT_STRING(0, \"st\", &string, \"st\", \"get another string (pervert ordering)\"),\n \t\tOPT_STRING('o', NULL, &string, \"str\", \"get another string\"),\n-\t\tOPT_GROUP(\"magic arguments\"),\n+\t\tOPT_SET_PTR(0, \"default-string\", &string,\n+\t\t\t\"set string to default\", (unsigned long)\"default\"),\n+\t\tOPT_GROUP(\"Magic arguments\"),\n \t\tOPT_ARGUMENT(\"quux\", \"means --quux\"),\n+\t\tOPT_GROUP(\"Standard options\"),\n+\t\tOPT__ABBREV(&abbrev),\n+\t\tOPT__VERBOSE(&verbose),\n+\t\tOPT__DRY_RUN(&dry_run),\n+\t\tOPT__QUIET(&quiet),\n \t\tOPT_END(),\n \t};\n \tint i;\n@@ -29,8 +56,12 @@ int main(int argc, const char **argv)\n \targc = parse_options(argc, argv, options, usage, 0);\n \n \tprintf(\"boolean: %d\\n\", boolean);\n-\tprintf(\"integer: %d\\n\", integer);\n+\tprintf(\"integer: %lu\\n\", integer);\n \tprintf(\"string: %s\\n\", string ? string : \"(not set)\");\n+\tprintf(\"abbrev: %d\\n\", abbrev);\n+\tprintf(\"verbose: %d\\n\", verbose);\n+\tprintf(\"quiet: %s\\n\", quiet ? \"yes\" : \"no\");\n+\tprintf(\"dry run: %s\\n\", dry_run ? \"yes\" : \"no\");\n \n \tfor (i = 0; i < argc; i++)\n \t\tprintf(\"arg %02d: %s\\n\", i, argv[i]);\n-- \n1.5.6.310.g344d\n"}]}