2

I want to use argp in my C program to parse command line options.

One requirement is that an option does not have a short name (like -f bar) but only a long name (like --foo=bar).

My approach so far is to set the key field of the argp_option struct to 0 to make it not show a short name.

I have looked at the provided examples and argp.h in detail but I can't find a means to parse an option that only has a long name in the parser function that is given to argp.

What I did find out is that, theoretically, in the parser I could use

case ARGP_KEY_ARG:
  printf("%s\n", arg);

to find the value of long options (e.g. when called with --foo=bar, bar would be printed here). However, this doesn't seem like the right approach since I don't see a simple way to actually tell which option the value belongs to. and this also shows actual command line arguments (not option values).

I'd be thankful for any tips about where I need to look. Cheers.

ngmir
  • 450
  • 6
  • 26
  • You can use negative numbers (e.g. `-1`) as 'short' option and then handle it in switch. – keltar Dec 09 '17 at 11:06
  • 2
    The `key` field of an option can be any `int` value. The docs state that if `isascii(key)` is true, then `key` is also a short option `-char` (`char` is the character associated with character code `key`). Technically, `isascii` ensures `0x00 <= key && key < 0x80`, so you can use any `int` value outside that range for the key. This `key` is still used as the value of the associated long option (e.g. `--foo`), so you'd handle it the same as any key/short opt. BTW, I collect all of my option keys in an `enum` as constants, so I'm not wondering what option `0x100` represents in a `switch` ;-) –  Dec 09 '17 at 11:21
  • @ChronoKitsune That could make an answer, 狐. Btw, why not using `-opt` as a key (I mean, the *negative* value of what would be the short option). E.g. if `s` and `d` are meaningful short options, use `-'s'` and `-'d'` which have both an absolute value > 0x80. – Déjà vu Dec 09 '17 at 11:44
  • @RingØ Done. Regarding `-opt` as a key, there might not be any meaningful short option, or the short option is already in use, and adding `-opt` could get confusing. I'm a simple person, so dealing with `case 's'` and `case -'s'` could easily be confusing for me. It might be more viable when used in conjunction with the "assign the value to a mnemonic constant" idea, but that also limits you to 4 possible options using that character: `'o'`, `-'o'`, `'O'`, and `-'O'`. After that, you still have the same problem. I included an example in my answer of how one might easily remove that limitation. –  Dec 09 '17 at 11:58
  • @ChronoKitsune Fair enough - OP might be less confused, but you seem to be knowledgeable in that segment. – Déjà vu Dec 09 '17 at 12:06
  • OP is not confused. OP is pleased. @RingØ: However, I don't quite understand what I would pick these values as opposites *of*. Specifically, I am looking for an identifier for a long option name - that is not the opposite of any other option. Could you rephrase? – ngmir Dec 09 '17 at 14:17
  • 1
    @ngmir Chrono was talking about an enum to keep keys meaningfull, depending on your options, there is a simple way to have keys > 0x80 and remain "readable" ; that is to use the negative value of a character (eg --store-disk would be -'s', etc...) – Déjà vu Dec 09 '17 at 14:33
  • Of course. Thank you. – ngmir Dec 09 '17 at 15:52

2 Answers2

4

From "The GNU C Library: Argp Option Vectors — Specifying Options in an Argp Parser":

int key

The integer key provided by the current option to the option parser. If key has a value that is a printable ASCII character (i.e., isascii (key) is true), it also specifies a short option -char, where char is the ASCII character with the code key.

In other words, the key field of an option can be any int value, and if isascii(key) is nonzero, then it also specifies a short option—meaning you can use non-ASCII values (values outside the range 0x00..0x7F) to avoid the short option. Despite not being a short option, the value of key is still used as the value of the associated long option (e.g. --foo), so you'd handle it the same as any key/short option.

O/T: I collect all of my option keys in an enum as constants, so I'm not wondering what option 0x100 represents in a switch, e.g. for GNU tar, it might be something like this for its compression options:

enum compress_options {
    // Archive file extension determines compression program
    COMP_FILTER_AUTO = 'a',
    // Option arg is program used to deal with compression
    COMP_FILTER_ARG = 'I',

    COMP_FILTER_BZIP2 = 'j',
    COMP_FILTER_XZ = 'J',
    COMP_FILTER_GZIP = 'z',
    COMP_FILTER_COMPRESS = 'Z',

    COMP_FILTER_LZIP = 0x100,
    COMP_FILTER_LZMA,
    COMP_FILTER_LZOP,

    // Do not use archive suffix to determine compression program.
    COMP_FILTER_NOAUTO,
};

Then you'd just need to ensure that the next set of options uses 0x200, then 0x300, etc. to avoid issues with options having the same value. If you needed, you could also use 0x100, 0x180, 0x200, 0x280, etc. (e.g. 0x100 might represent a subcommand and 0x180 might be the first option for that subcommand).

It's important to group the short options and non-short options separately. The implicitly assigned values in an enum depend upon the value of the last explicitly assigned value. Had I placed COMP_FILTER_NOAUTO immediately after COMP_FILTER_AUTO, the --no-auto-compress long option would have an associated short option -b, which it does not in reality.

Neuron
  • 5,141
  • 5
  • 38
  • 59
  • Thank you, that answers my question. I really did not guess that I could just use values outside of that range and still compare them in the switch case. I am not used to this degree of "freedom" (semantic abiguity) in C programming. – ngmir Dec 09 '17 at 14:18
  • Also thank you for the additional notes, I am happy for some insight. I have integrated this thought aswell. – ngmir Dec 09 '17 at 14:20
1

While the accepted answer is correct, it is missing a concrete example.

Here we create two arguments, one can be used normally (-b) and one only via the long name (--foo).

#define ARGUMENT_BAR_SHORT 'b'
#define ARGUMENT_FOO_SHORT 0x80

static struct argp_option options[] =
{
    { "bar", ARGUMENT_BAR_SHORT, "BAR_VAL", 0, "The bar value" },
    { "foo", ARGUMENT_FOO_SHORT, "FOO_VAL", 0, "The foo value" },
}

And then parse it here:

error_t parse_opt (int option, char* arg, struct argp_state* state)
{
    switch (option)
    {
        case ARGUMENT_BAR_SHORT:
            printf("got bar: %s", arg);
            break;
        case ARGUMENT_FOO_SHORT:
            printf("got foo: %s", arg);
            break;
    }
}

For anyone who hasn't read the other answer: just use values outside the range of normal characters, which 0x80 already is. If you need more parameters, just increment this value. I also like the solution in the other answer to use an enum.


Also I haven't compiled this ;)

Neuron
  • 5,141
  • 5
  • 38
  • 59