Search code examples
cgetopt

catching missing argument from getopt


I'm trying to catch a missing argument when starting my program. This example is a simple file copy. the correct command line would be:

# prog -i in_file -o out_file

If the user forgets to enter an argument the program should return an information and exit

wrong input examples

example 1

# prog -i in_file -o

example 2

# prog -i -o outfile

The following code works for the wrong input example 1 but not for example 2. In example 2 "-o" is taken as the argument for the option -i.

   while((opt = getopt(argc, argv, ":i:o:h?")) != -1)
{
    switch (opt)
    {
    case 'i':

        in_file = optarg ;
        break;
    case 'o':
        out_file = optarg ;
        break;
    case 'h':
        printf ("usage: %s -i <input file> -o <output file>\n", argv[0]);
        exit(13) ;
    case '?':  // wrong usage                                                                                                                     
        printf ("Wrong usage!!\n");
        printf ("usage: %s -i <input file> -o <output file>\n", argv[0]);
        exit(14) ;
    }

One possibility to mitigate this behavior would be to check in "case i:" and "case o:" if the argument given is sensible, in this case check if file name exists. But I really thought that this kind of failure is addressed by getopt itself so maybe I'm just doing it wrong.


Solution

  • I really thought that this kind of failure is addressed by getopt itself so maybe I'm just doing it wrong.

    getopt() does recognize missing option arguments, but they can occur only for the last option given on the command line. It is conventional that when an option that requires an argument is parsed, the next bit of the command line is interpreted as its argument, even if that starts with a hyphen (-). This is the behavior implemented by getopt(). Otherwise, option arguments that begin with a hyphen would need to be made an exception to the rule that option arguments may be presented in a separate command-line argument than the option to which they belong, or else they would need to be disallowed altogether.

    To put it more directly, what do you think the user should be required to do if they want to name the input file -o? Probably you would accept that they can express that on the command line via -i-o (no space), but it is conventional that they are free to choose between that combined form and the separated form, -i -o, without changing the meaning. This is among the reasons that POSIX says that option arguments should not be optional.

    One possibility to mitigate this behavior would be to check in "case i:" and "case o:" if the argument given is sensible, in this case check if file name exists.

    It does make sense under many circumstances to validate options and option arguments, though if you have implemented good error handling then that's sometimes redundant. But here, it's not clear how properly to validate. Suppose a file named -o actually exists, and the user wants to use that as the input? Or suppose the options are given in the opposite order as -o -i input, where the program's normal behavior is to create the requested output file if it doesn't already exist?

    So, the first thing to do is to implement good, comprehensive error handling. Your user will have a good clue about what they did wrong if the program assumes that they meant what they said, with the result that it ultimately fails with ...

    -o: file not found
    

    Or if there is no default output file, then perhaps the validation that catches this particular misuse is one that confirms that all required options have been provided. Or if no non-option arguments are expected then maybe the program objects when it sees one (out_file).

    I don't think there's a universal solution, independent of the details of each program's specific argument language.