pages tagged command-line
yakking
http://yakking.branchable.com/tags/command-line/
yakking
ikiwiki
2016-06-23T21:33:58Z
argp, for getopt users
http://yakking.branchable.com/posts/converting-to-argp/
Richard Maw
2016-06-23T21:33:58Z
2016-06-22T11:00:07Z
<p>I previously spoke about command-line parsing with <a href="http://man7.org/linux/man-pages/man3/getopt.3.html">getopt</a>
and mentioned an alternative called <a href="http://www.gnu.org/software/libc/manual/html_node/Argp.html#Argp">argp</a>.</p>
<p>Using <a href="http://www.gnu.org/software/libc/manual/html_node/Argp.html#Argp">argp</a> is convenient
because it automatically generates <code>--help</code> and <code>--usage</code>,
your help text won't get out of sync with your options;
and <a href="http://www.gnu.org/software/libc/manual/html_node/Argp.html#Argp">argp</a> also combines the long option specification with the short options,
so your short options won't get out of sync with your long options.</p>
<p>Using <a href="http://www.gnu.org/software/libc/manual/html_node/Argp.html#Argp">argp</a> <em>does</em> require you to restructure your argument parsing though,
and while the <a href="http://www.nongnu.org/argpbook/">argpbook</a> is a good guide to learn how to write new programs,
if you're already familiar with command-line parsing in general
a lot of what it has to say is redundant.</p>
<p>So this article is about how to translate a program written to use <a href="http://man7.org/linux/man-pages/man3/getopt.3.html">getopt</a>
into a program that uses <a href="http://www.gnu.org/software/libc/manual/html_node/Argp.html#Argp">argp</a>.</p>
<h1>Converting programs that parse with getopt to use argp</h1>
<p>This is a relatively simple program,
that reports the positional arguments
and the value passed to the <code>--foo</code> option.</p>
<div class="highlight-c"><pre class="hl"><span class="hl com">/* test0.c */</span>
<span class="hl ppc">#include <stdio.h></span> <span class="hl com">/* fprintf */</span><span class="hl ppc"></span>
<span class="hl ppc">#include <getopt.h></span> <span class="hl com">/* getopt_long, struct option */</span><span class="hl ppc"></span>
<span class="hl kwb">int</span> <span class="hl kwd">main</span><span class="hl opt">(</span><span class="hl kwb">int</span> argc<span class="hl opt">,</span> <span class="hl kwb">char</span> <span class="hl opt">*</span>argv<span class="hl opt">[]){</span>
<span class="hl kwb">enum</span> opt <span class="hl opt">{</span>
OPT_END <span class="hl opt">= -</span><span class="hl num">1</span><span class="hl opt">,</span>
OPT_FOO <span class="hl opt">=</span> <span class="hl str">'f'</span><span class="hl opt">,</span>
OPT_NOFOO <span class="hl opt">=</span> <span class="hl num">0x100</span><span class="hl opt">,</span>
OPT_UNEXPECTED <span class="hl opt">=</span> <span class="hl str">'?'</span><span class="hl opt">,</span>
<span class="hl opt">};</span>
<span class="hl kwb">static const struct</span> option longopts<span class="hl opt">[] = {</span>
<span class="hl opt">{.</span>name <span class="hl opt">=</span> <span class="hl str">"foo"</span><span class="hl opt">, .</span>has_arg <span class="hl opt">=</span> required_argument<span class="hl opt">, .</span>val <span class="hl opt">=</span> OPT_FOO<span class="hl opt">},</span>
<span class="hl opt">{.</span>name <span class="hl opt">=</span> <span class="hl str">"also-foo"</span><span class="hl opt">, .</span>has_arg <span class="hl opt">=</span> required_argument<span class="hl opt">, .</span>val <span class="hl opt">=</span> OPT_FOO<span class="hl opt">},</span>
<span class="hl opt">{.</span>name <span class="hl opt">=</span> <span class="hl str">"no-foo"</span><span class="hl opt">, .</span>has_arg <span class="hl opt">=</span> no_argument<span class="hl opt">, .</span>val <span class="hl opt">=</span> OPT_NOFOO<span class="hl opt">},</span>
<span class="hl opt">{},</span>
<span class="hl opt">};</span>
<span class="hl kwb">char</span> <span class="hl opt">**</span>positionals<span class="hl opt">;</span>
<span class="hl kwb">char</span> <span class="hl opt">*</span>foo <span class="hl opt">=</span> NULL<span class="hl opt">;</span>
<span class="hl kwa">for</span> <span class="hl opt">(;;) {</span>
<span class="hl kwb">int</span> longindex <span class="hl opt">= -</span><span class="hl num">1</span><span class="hl opt">;</span>
<span class="hl kwb">enum</span> opt opt <span class="hl opt">=</span> <span class="hl kwd">getopt_long</span><span class="hl opt">(</span>argc<span class="hl opt">,</span> argv<span class="hl opt">,</span> <span class="hl str">"f:"</span><span class="hl opt">,</span> longopts<span class="hl opt">, &</span>longindex<span class="hl opt">);</span>
<span class="hl kwa">switch</span> <span class="hl opt">(</span>opt<span class="hl opt">) {</span>
<span class="hl kwa">case</span> OPT_END<span class="hl opt">:</span>
<span class="hl kwa">goto</span> end_optparse<span class="hl opt">;</span>
<span class="hl kwa">case</span> OPT_FOO<span class="hl opt">:</span>
foo <span class="hl opt">=</span> optarg<span class="hl opt">;</span>
<span class="hl kwa">break</span><span class="hl opt">;</span>
<span class="hl kwa">case</span> OPT_NOFOO<span class="hl opt">:</span>
foo <span class="hl opt">=</span> NULL<span class="hl opt">;</span>
<span class="hl kwa">break</span><span class="hl opt">;</span>
<span class="hl kwa">case</span> OPT_UNEXPECTED<span class="hl opt">:</span>
<span class="hl kwa">return</span> <span class="hl num">1</span><span class="hl opt">;</span>
<span class="hl opt">}</span>
<span class="hl opt">}</span>
end_optparse<span class="hl opt">:</span>
positionals <span class="hl opt">= &</span>argv<span class="hl opt">[</span>optind<span class="hl opt">];</span>
<span class="hl kwa">if</span> <span class="hl opt">(</span>foo <span class="hl opt">==</span> NULL<span class="hl opt">) {</span>
<span class="hl kwd">fprintf</span><span class="hl opt">(</span>stdout<span class="hl opt">,</span> <span class="hl str">"Got no Foo</span><span class="hl esc">\n</span><span class="hl str">"</span><span class="hl opt">);</span>
<span class="hl opt">}</span> <span class="hl kwa">else</span> <span class="hl opt">{</span>
<span class="hl kwd">fprintf</span><span class="hl opt">(</span>stdout<span class="hl opt">,</span> <span class="hl str">"Foo is %s</span><span class="hl esc">\n</span><span class="hl str">"</span><span class="hl opt">,</span> foo<span class="hl opt">);</span>
<span class="hl opt">}</span>
<span class="hl kwa">for</span> <span class="hl opt">(; *</span>positionals<span class="hl opt">;</span> positionals<span class="hl opt">++)</span>
<span class="hl kwd">fprintf</span><span class="hl opt">(</span>stdout<span class="hl opt">,</span> <span class="hl str">"Positional: %s</span><span class="hl esc">\n</span><span class="hl str">"</span><span class="hl opt">, *</span>positionals<span class="hl opt">);</span>
<span class="hl kwa">return</span> <span class="hl num">0</span><span class="hl opt">;</span>
<span class="hl opt">}</span>
</pre></div>
<h2>Fixing control flow</h2>
<p>The control flow for <a href="http://man7.org/linux/man-pages/man3/getopt.3.html">getopt_long</a> is different to <a href="http://www.gnu.org/software/libc/manual/html_node/Argp.html#Argp">argp</a>.</p>
<p>The <a href="http://man7.org/linux/man-pages/man3/getopt.3.html">getopt_long</a> is called in a loop until it has finished,
effectively acting as a form of iterator,
while <a href="http://www.gnu.org/software/libc/manual/html_node/Argp.html#Argp">argp</a> is called once, passing it a callback function.</p>
<h3>Returning parsed arguments in a struct</h3>
<p>A side-effect of this change is that we need to change how we store our results,
since we only get to pass one pointer to the parse function,
we need to have a state structure.</p>
<div class="highlight-diff"><pre class="hl"><span class="hl kwb">--- test0.c 2016-05-30 11:58:27.799321266 +0100</span>
<span class="hl kwa">+++ test1.c 2016-05-30 12:01:06.533529250 +0100</span>
<span class="hl kwd">@@ -1,7 +1,12 @@</span>
<span class="hl kwb">-/* test0.c */</span>
<span class="hl kwa">+/* test1.c */</span>
#include <stdio.h> /* fprintf */
#include <getopt.h> /* getopt_long, struct option */
<span class="hl kwa">+struct arguments {</span>
<span class="hl kwa">+ char *foo;</span>
<span class="hl kwa">+ char **positionals;</span>
<span class="hl kwa">+};</span>
<span class="hl kwa">+</span>
int main(int argc, char *argv[]){
enum opt {
OPT_END = -1,
<span class="hl kwd">@@ -15,8 +20,9 @@</span>
{.name = "no-foo", .has_arg = no_argument, .val = OPT_NOFOO},
{},
};
<span class="hl kwb">- char **positionals;</span>
<span class="hl kwb">- char *foo = NULL;</span>
<span class="hl kwa">+ struct arguments args = {</span>
<span class="hl kwa">+ .foo = NULL,</span>
<span class="hl kwa">+ };</span>
for (;;) {
int longindex = -1;
enum opt opt = getopt_long(argc, argv, "f:", longopts, &longindex);
<span class="hl kwd">@@ -24,24 +30,24 @@</span>
case OPT_END:
goto end_optparse;
case OPT_FOO:
<span class="hl kwb">- foo = optarg;</span>
<span class="hl kwa">+ args.foo = optarg;</span>
break;
case OPT_NOFOO:
<span class="hl kwb">- foo = NULL;</span>
<span class="hl kwa">+ args.foo = NULL;</span>
break;
case OPT_UNEXPECTED:
return 1;
}
}
end_optparse:
<span class="hl kwb">- positionals = &argv[optind];</span>
<span class="hl kwa">+ args.positionals = &argv[optind];</span>
<span class="hl kwb">- if (foo == NULL) {</span>
<span class="hl kwa">+ if (args.foo == NULL) {</span>
fprintf(stdout, "Got no Foo\n");
} else {
<span class="hl kwb">- fprintf(stdout, "Foo is %s\n", foo);</span>
<span class="hl kwa">+ fprintf(stdout, "Foo is %s\n", args.foo);</span>
}
<span class="hl kwb">- for (; *positionals; positionals++)</span>
<span class="hl kwa">+ for (char **positionals = args.positionals; *positionals; positionals++)</span>
fprintf(stdout, "Positional: %s\n", *positionals);
return 0;
}
</pre></div>
<h3>Adding a handler function</h3>
<p>To make the switch-over to calling <code>argp_parse</code> easier,
we're going to split out the argument parsing into a function,
while calls <a href="http://man7.org/linux/man-pages/man3/getopt.3.html">getopt_long</a> in a loop,
and calls a second function to actually handle the argument.</p>
<div class="highlight-diff"><pre class="hl"><span class="hl kwb">--- test1.c 2016-05-30 12:01:06.533529250 +0100</span>
<span class="hl kwa">+++ test2.c 2016-05-30 13:35:19.414248340 +0100</span>
<span class="hl kwd">@@ -1,4 +1,4 @@</span>
<span class="hl kwb">-/* test1.c */</span>
<span class="hl kwa">+/* test2.c */</span>
#include <stdio.h> /* fprintf */
#include <getopt.h> /* getopt_long, struct option */
<span class="hl kwd">@@ -7,40 +7,59 @@</span>
char **positionals;
};
<span class="hl kwa">+enum opt {</span>
<span class="hl kwa">+ OPT_END = -1,</span>
<span class="hl kwa">+ OPT_FOO = 'f',</span>
<span class="hl kwa">+ OPT_NOFOO = 0x100,</span>
<span class="hl kwa">+ OPT_UNEXPECTED = '?',</span>
<span class="hl kwa">+};</span>
<span class="hl kwa">+static const struct option longopts[] = {</span>
<span class="hl kwa">+ {.name = "foo", .has_arg = required_argument, .val = OPT_FOO},</span>
<span class="hl kwa">+ {.name = "also-foo", .has_arg = required_argument, .val = OPT_FOO},</span>
<span class="hl kwa">+ {.name = "no-foo", .has_arg = no_argument, .val = OPT_NOFOO},</span>
<span class="hl kwa">+ {},</span>
<span class="hl kwa">+};</span>
<span class="hl kwa">+const char optstring[] = "f:";</span>
<span class="hl kwa">+</span>
<span class="hl kwa">+int parse_arg(int opt, char *arg, struct arguments *args){</span>
<span class="hl kwa">+ switch (opt) {</span>
<span class="hl kwa">+ case OPT_FOO:</span>
<span class="hl kwa">+ args->foo = arg;</span>
<span class="hl kwa">+ return 0;</span>
<span class="hl kwa">+ case OPT_NOFOO:</span>
<span class="hl kwa">+ args->foo = NULL;</span>
<span class="hl kwa">+ return 0;</span>
<span class="hl kwa">+ default:</span>
<span class="hl kwa">+ return 1;</span>
<span class="hl kwa">+ }</span>
<span class="hl kwa">+}</span>
<span class="hl kwa">+</span>
<span class="hl kwa">+int parse_args(int argc, char *argv[], struct arguments *args){</span>
<span class="hl kwa">+ for (;;) {</span>
<span class="hl kwa">+ int opt = getopt_long(argc, argv, optstring, longopts, NULL);</span>
<span class="hl kwa">+</span>
<span class="hl kwa">+ if (opt == OPT_END) {</span>
<span class="hl kwa">+ args->positionals = &argv[optind];</span>
<span class="hl kwa">+ return 0;</span>
<span class="hl kwa">+ }</span>
<span class="hl kwa">+</span>
<span class="hl kwa">+ int ret = parse_arg(opt, optarg, args);</span>
<span class="hl kwa">+ if (ret != 0) {</span>
<span class="hl kwa">+ return ret;</span>
<span class="hl kwa">+ }</span>
<span class="hl kwa">+ }</span>
<span class="hl kwa">+}</span>
<span class="hl kwa">+</span>
int main(int argc, char *argv[]){
<span class="hl kwb">- enum opt {</span>
<span class="hl kwb">- OPT_END = -1,</span>
<span class="hl kwb">- OPT_FOO = 'f',</span>
<span class="hl kwb">- OPT_NOFOO = 0x100,</span>
<span class="hl kwb">- OPT_UNEXPECTED = '?',</span>
<span class="hl kwb">- };</span>
<span class="hl kwb">- static const struct option longopts[] = {</span>
<span class="hl kwb">- {.name = "foo", .has_arg = required_argument, .val = OPT_FOO},</span>
<span class="hl kwb">- {.name = "also-foo", .has_arg = required_argument, .val = OPT_FOO},</span>
<span class="hl kwb">- {.name = "no-foo", .has_arg = no_argument, .val = OPT_NOFOO},</span>
<span class="hl kwb">- {},</span>
<span class="hl kwb">- };</span>
<span class="hl kwa">+ int ret = 0;</span>
struct arguments args = {
.foo = NULL,
};
<span class="hl kwb">- for (;;) {</span>
<span class="hl kwb">- int longindex = -1;</span>
<span class="hl kwb">- enum opt opt = getopt_long(argc, argv, "f:", longopts, &longindex);</span>
<span class="hl kwb">- switch (opt) {</span>
<span class="hl kwb">- case OPT_END:</span>
<span class="hl kwb">- goto end_optparse;</span>
<span class="hl kwb">- case OPT_FOO:</span>
<span class="hl kwb">- args.foo = optarg;</span>
<span class="hl kwb">- break;</span>
<span class="hl kwb">- case OPT_NOFOO:</span>
<span class="hl kwb">- args.foo = NULL;</span>
<span class="hl kwb">- break;</span>
<span class="hl kwb">- case OPT_UNEXPECTED:</span>
<span class="hl kwb">- return 1;</span>
<span class="hl kwb">- }</span>
<span class="hl kwa">+</span>
<span class="hl kwa">+ ret = parse_args(argc, argv, &args);</span>
<span class="hl kwa">+ if (ret != 0) {</span>
<span class="hl kwa">+ return ret;</span>
}
<span class="hl kwb">-end_optparse:</span>
<span class="hl kwb">- args.positionals = &argv[optind];</span>
if (args.foo == NULL) {
fprintf(stdout, "Got no Foo\n");
</pre></div>
<p>You may have noticed the inconsistency
from some parameters being passed in to the handler function
and some being globals.</p>
<p>This is a side-effect of emulating the API that argp exposes,
with minimal changes to the flow of data.</p>
<h3>Handling positional parameters as options</h3>
<p><a href="http://www.gnu.org/software/libc/manual/html_node/Argp-Parser-Functions.html#Argp-Parser-Functions">argp parser functions</a> typically handle parsing the positional arguments,
rather than the caller.</p>
<p>Unfortunately we don't currently pass the argv in to the handler function,
so we'll need to change the API a little to meet that,
by adding a <code>struct parse_state</code>
that includes the argv.</p>
<div class="highlight-diff"><pre class="hl"><span class="hl kwb">--- test2.c 2016-05-30 13:42:20.638208254 +0100</span>
<span class="hl kwa">+++ test3.c 2016-05-30 13:45:52.232178791 +0100</span>
<span class="hl kwd">@@ -1,4 +1,4 @@</span>
<span class="hl kwb">-/* test2.c */</span>
<span class="hl kwa">+/* test3.c */</span>
#include <stdio.h> /* fprintf */
#include <getopt.h> /* getopt_long, struct option */
<span class="hl kwd">@@ -21,7 +21,13 @@</span>
};
const char optstring[] = "f:";
<span class="hl kwb">-int parse_arg(int opt, char *arg, struct arguments *args){</span>
<span class="hl kwa">+struct parse_state {</span>
<span class="hl kwa">+ char **argv;</span>
<span class="hl kwa">+ struct arguments *input;</span>
<span class="hl kwa">+};</span>
<span class="hl kwa">+</span>
<span class="hl kwa">+int parse_arg(int opt, char *arg, struct parse_state *state){</span>
<span class="hl kwa">+ struct arguments *args = state->input; </span>
switch (opt) {
case OPT_FOO:
args->foo = arg;
<span class="hl kwd">@@ -29,24 +35,29 @@</span>
case OPT_NOFOO:
args->foo = NULL;
return 0;
<span class="hl kwa">+ case OPT_END:</span>
<span class="hl kwa">+ args->positionals = &state->argv[optind];</span>
<span class="hl kwa">+ return 0;</span>
default:
return 1;
}
}
int parse_args(int argc, char *argv[], struct arguments *args){
<span class="hl kwa">+ struct parse_state state = {</span>
<span class="hl kwa">+ .argv = argv,</span>
<span class="hl kwa">+ .input = args,</span>
<span class="hl kwa">+ };</span>
for (;;) {
int opt = getopt_long(argc, argv, optstring, longopts, NULL);
<span class="hl kwb">-</span>
<span class="hl kwb">- if (opt == OPT_END) {</span>
<span class="hl kwb">- args->positionals = &argv[optind];</span>
<span class="hl kwb">- return 0;</span>
<span class="hl kwb">- }</span>
<span class="hl kwb">-</span>
<span class="hl kwb">- int ret = parse_arg(opt, optarg, args);</span>
<span class="hl kwa">+ int ret = parse_arg(opt, optarg, &state);</span>
if (ret != 0) {
return ret;
}
<span class="hl kwa">+</span>
<span class="hl kwa">+ if (opt == OPT_END) {</span>
<span class="hl kwa">+ return 0;</span>
<span class="hl kwa">+ }</span>
}
}
</pre></div>
<h2>Switching over to <code>argp_parse</code></h2>
<p>Now that we've changed the logic flow,
we can effectively substitute <code>parse_args</code> for <code>argp_parse</code>.</p>
<p>The result is now mostly deleting code we added to change the logic flow.</p>
<h3>Replacing <code>parse_args</code></h3>
<div class="highlight-diff"><pre class="hl"><span class="hl kwb">-</span>
<span class="hl kwb">-int parse_args(int argc, char *argv[], struct arguments *args){</span>
<span class="hl kwb">- struct parse_state state = {</span>
<span class="hl kwb">- .argv = argv,</span>
<span class="hl kwb">- .input = args,</span>
<span class="hl kwb">- };</span>
<span class="hl kwb">- for (;;) {</span>
<span class="hl kwb">- int opt = getopt_long(argc, argv, optstring, longopts, NULL);</span>
<span class="hl kwb">- int ret = parse_arg(opt, optarg, &state);</span>
<span class="hl kwb">- if (ret != 0) {</span>
<span class="hl kwb">- return ret;</span>
<span class="hl kwb">- }</span>
<span class="hl kwb">-</span>
<span class="hl kwb">- if (opt == -1) {</span>
<span class="hl kwb">- return 0;</span>
<span class="hl kwb">- }</span>
<span class="hl kwb">- }</span>
<span class="hl kwb">-}</span>
int main(int argc, char *argv[]){
<span class="hl kwa">+ static const struct argp argp = {</span>
<span class="hl kwa">+ .options = opts,</span>
<span class="hl kwa">+ .parser = parse_arg,</span>
<span class="hl kwa">+ };</span>
int ret = 0;
struct arguments args = {
.foo = NULL,
};
<span class="hl kwb">- ret = parse_args(argc, argv, &args);</span>
<span class="hl kwa">+ ret = argp_parse(&argp, argc, argv, 0, NULL, &args);</span>
if (ret != 0) {
return ret;
}
</pre></div>
<p>This effectively replaces the code we had for parsing how we wanted
with a call to <code>argp_parse</code> with appropriate configuration.</p>
<p>The <code>static const struct argp argp</code> is in main
just to keep its definition local to its only user.</p>
<p>Strictly the <code>static const struct argp_option opts[]</code>
could also be moved here,
but it's easier to compare how options are specified
if it's changed in its current location
rather than moved.</p>
<h3>Changing the options vector</h3>
<div class="highlight-diff"><pre class="hl"><span class="hl kwd">@@ -8,25 +10,17 @@</span>
};
enum opt {
<span class="hl kwb">- OPT_END = -1,</span>
OPT_FOO = 'f',
OPT_NOFOO = 0x100,
<span class="hl kwb">- OPT_UNEXPECTED = '?',</span>
};
<span class="hl kwb">-static const struct option longopts[] = {</span>
<span class="hl kwb">- {.name = "foo", .has_arg = required_argument, .val = OPT_FOO},</span>
<span class="hl kwb">- {.name = "also-foo", .has_arg = required_argument, .val = OPT_FOO},</span>
<span class="hl kwb">- {.name = "no-foo", .has_arg = no_argument, .val = OPT_NOFOO},</span>
<span class="hl kwa">+static const struct argp_option opts[] = {</span>
<span class="hl kwa">+ {.name = "foo", .key = OPT_FOO, .arg = "value"},</span>
<span class="hl kwa">+ {.name = "also-foo", .key = OPT_FOO, .flags = OPTION_ALIAS},</span>
<span class="hl kwa">+ {.name = "no-foo", .key = OPT_NOFOO},</span>
{},
};
<span class="hl kwb">-const char optstring[] = "f:";</span>
</pre></div>
<p>Because <code>argp_parse</code> handles termination and unexpected options internally
we don't need <code>OPT_END</code> or <code>OPT_UNEXPECTED</code> any more.</p>
<p><code>struct argp_option</code> has similar behaviour to <code>struct option</code>,
but it does not have a <code>.flag</code> parameter,
so the <code>.val</code> equivalent is called <code>.key</code>
and is used to determine which value to pass through to <code>parse_arg</code>.</p>
<p>Rather than having <code>.has_arg</code> defining whether it takes values,
the <code>.arg</code> field defines whether it expects a value,
and labels it in the help output.</p>
<p>If an option's value is optional, then add <code>OPTION_ARG_OPTIONAL</code> to <code>.flags</code>.</p>
<p>Because <code>argp_parse</code> treats any key which <a href="http://man7.org/linux/man-pages/man3/isalpha.3.html">is printable</a>
as a short option,
we don't need the separate option string.</p>
<h3>Changes to <code>parse_arg</code></h3>
<div class="highlight-diff"><pre class="hl"><span class="hl kwb">-int parse_arg(int opt, char *arg, struct parse_state *state){</span>
<span class="hl kwa">+error_t parse_arg(int opt, char *arg, struct argp_state *state){</span>
struct arguments *args = state->input;
switch (opt) {
case OPT_FOO:
<span class="hl kwd">@@ -35,39 +29,26 @@</span>
case OPT_NOFOO:
args->foo = NULL;
return 0;
<span class="hl kwb">- case OPT_END:</span>
<span class="hl kwb">- args->positionals = &state->argv[optind];</span>
<span class="hl kwa">+ case ARGP_KEY_ARGS:</span>
<span class="hl kwa">+ case ARGP_KEY_NO_ARGS:</span>
<span class="hl kwa">+ args->positionals = &state->argv[state->next];</span>
return 0;
default:
<span class="hl kwb">- return 1;</span>
<span class="hl kwa">+ return ARGP_ERR_UNKNOWN;</span>
}
}
</pre></div>
<p>This is mostly the same.</p>
<p>The function signature has changed slightly since we pass argp's state instead,
and rather than using <code>optind</code>, we use <code>state->next</code>.</p>
<p><a href="http://www.gnu.org/software/libc/manual/html_node/Argp-Parser-Functions.html#Argp-Parser-Functions">argp parser functions</a> can handle arguments individually with <code>ARGP_KEY_ARG</code>
or them all together as <code>ARGP_KEY_ARGS</code>,
and can handle being given no arguments with <code>ARGP_KEY_NO_ARGS</code>.</p>
<p>Since we want to treat all subsequent arguments as the positionals,
we wouldn't do this by handling <code>ARGP_KEY_ARG</code>,
since then we'd need to pick the arguments individually.</p>
<p>We need to handle <code>ARGP_KEY_NO_ARGS</code>
since we haven't initialised <code>args->positionals</code> to anything,
and to be a valid argument vector we need to point to something
even if it is just a pointer to a <code>NULL</code> (signifying an empty vector).</p>
<p>Since <code>&state->argv[state->next]</code> points to the end of the array
if there were no positional parameters,
or to the next parameter if there was one,
the code is actually the same.</p>
<p><a href="http://www.gnu.org/software/libc/manual/html_node/Argp-Parser-Functions.html#Argp-Parser-Functions">argp parser functions</a> may be chained together,
so a parser function that doesn't recognise a particular option
should return <code>ARGP_ERR_UNKNOWN</code>
so that <code>argp_parse</code> can either try a different parser function
or it can report it being unhandled as an error.</p>
<h3>The full diff</h3>
<div class="highlight-diff"><pre class="hl"><span class="hl kwb">--- test3.c 2016-05-30 13:47:01.431515079 +0100</span>
<span class="hl kwa">+++ test4.c 2016-05-30 14:14:25.315137258 +0100</span>
<span class="hl kwd">@@ -1,6 +1,8 @@</span>
<span class="hl kwb">-/* test3.c */</span>
<span class="hl kwa">+/* test4.c */</span>
#include <stdio.h> /* fprintf */
<span class="hl kwb">-#include <getopt.h> /* getopt_long, struct option */</span>
<span class="hl kwa">+#include <argp.h> /* argp_parse, error_t, struct argp, struct argp_option,</span>
<span class="hl kwa">+ struct argp_state, OPTION_ALIAS,</span>
<span class="hl kwa">+ ARGP_KEY_ARGS, ARGP_KEY_NO_ARGS, ARGP_ERR_UNKNOWN */</span>
struct arguments {
char *foo;
<span class="hl kwd">@@ -8,25 +10,17 @@</span>
};
enum opt {
<span class="hl kwb">- OPT_END = -1,</span>
OPT_FOO = 'f',
OPT_NOFOO = 0x100,
<span class="hl kwb">- OPT_UNEXPECTED = '?',</span>
};
<span class="hl kwb">-static const struct option longopts[] = {</span>
<span class="hl kwb">- {.name = "foo", .has_arg = required_argument, .val = OPT_FOO},</span>
<span class="hl kwb">- {.name = "also-foo", .has_arg = required_argument, .val = OPT_FOO},</span>
<span class="hl kwb">- {.name = "no-foo", .has_arg = no_argument, .val = OPT_NOFOO},</span>
<span class="hl kwa">+static const struct argp_option opts[] = {</span>
<span class="hl kwa">+ {.name = "foo", .key = OPT_FOO, .arg = "value"},</span>
<span class="hl kwa">+ {.name = "also-foo", .key = OPT_FOO, .flags = OPTION_ALIAS},</span>
<span class="hl kwa">+ {.name = "no-foo", .key = OPT_NOFOO},</span>
{},
};
<span class="hl kwb">-const char optstring[] = "f:";</span>
<span class="hl kwb">-struct parse_state {</span>
<span class="hl kwb">- char **argv;</span>
<span class="hl kwb">- struct arguments *input;</span>
<span class="hl kwb">-};</span>
<span class="hl kwb">-</span>
<span class="hl kwb">-int parse_arg(int opt, char *arg, struct parse_state *state){</span>
<span class="hl kwa">+error_t parse_arg(int opt, char *arg, struct argp_state *state){</span>
struct arguments *args = state->input;
switch (opt) {
case OPT_FOO:
<span class="hl kwd">@@ -35,39 +29,26 @@</span>
case OPT_NOFOO:
args->foo = NULL;
return 0;
<span class="hl kwb">- case OPT_END:</span>
<span class="hl kwb">- args->positionals = &state->argv[optind];</span>
<span class="hl kwa">+ case ARGP_KEY_ARGS:</span>
<span class="hl kwa">+ case ARGP_KEY_NO_ARGS:</span>
<span class="hl kwa">+ args->positionals = &state->argv[state->next];</span>
return 0;
default:
<span class="hl kwb">- return 1;</span>
<span class="hl kwa">+ return ARGP_ERR_UNKNOWN;</span>
}
}
<span class="hl kwb">-</span>
<span class="hl kwb">-int parse_args(int argc, char *argv[], struct arguments *args){</span>
<span class="hl kwb">- struct parse_state state = {</span>
<span class="hl kwb">- .argv = argv,</span>
<span class="hl kwb">- .input = args,</span>
<span class="hl kwb">- };</span>
<span class="hl kwb">- for (;;) {</span>
<span class="hl kwb">- int opt = getopt_long(argc, argv, optstring, longopts, NULL);</span>
<span class="hl kwb">- int ret = parse_arg(opt, optarg, &state);</span>
<span class="hl kwb">- if (ret != 0) {</span>
<span class="hl kwb">- return ret;</span>
<span class="hl kwb">- }</span>
<span class="hl kwb">-</span>
<span class="hl kwb">- if (opt == -1) {</span>
<span class="hl kwb">- return 0;</span>
<span class="hl kwb">- }</span>
<span class="hl kwb">- }</span>
<span class="hl kwb">-}</span>
int main(int argc, char *argv[]){
<span class="hl kwa">+ static const struct argp argp = {</span>
<span class="hl kwa">+ .options = opts,</span>
<span class="hl kwa">+ .parser = parse_arg,</span>
<span class="hl kwa">+ };</span>
int ret = 0;
struct arguments args = {
.foo = NULL,
};
<span class="hl kwb">- ret = parse_args(argc, argv, &args);</span>
<span class="hl kwa">+ ret = argp_parse(&argp, argc, argv, 0, NULL, &args);</span>
if (ret != 0) {
return ret;
}
</pre></div>
<p>Now we can see the fruits of our labour:</p>
<div class="highlight-sh"><pre class="hl">$ <span class="hl kwc">make</span> test4
cc test4.c <span class="hl kwb">-o</span> test4
$ .<span class="hl opt">/</span>test4 <span class="hl kwb">--help</span>
Usage<span class="hl opt">:</span> test4 <span class="hl opt">[</span>OPTION...<span class="hl opt">]</span>
<span class="hl kwb">-f</span><span class="hl opt">,</span> <span class="hl kwb">--foo</span><span class="hl opt">=</span>value<span class="hl opt">,</span> <span class="hl kwb">--also-foo</span><span class="hl opt">=</span>value
<span class="hl kwb">--no-foo</span>
<span class="hl opt">-</span>?<span class="hl opt">,</span> <span class="hl kwb">--help</span> Give this <span class="hl kwb">help</span> list
<span class="hl kwb">--usage</span> Give a short usage message
Mandatory or optional arguments to long options are also mandatory or optional
<span class="hl kwa">for</span> any corresponding short options.
</pre></div>