Thread: getopt thinks next arg is operand to previous arg!

  1. #1
    Registered User
    Join Date
    Jun 2009
    Posts
    101

    getopt thinks next arg is operand to previous arg!

    I am using MinGW on Windows 32 bit and am trying to parse some command line arguments. I have an arg for input file (-i). I also have some other args that don't take operands. The problem is that if I pass -i without an argument, and then follow up with another argument (-a, for example), getopt thinks -a is the operand to -i! This of course works as expected if -i is the last argument on the command line.

    Example: ./program.exe -i -a

    It thinks the filename is "-a" instead of empty.

    Here's my simplified code:

    Code:
    #include <getopt.h>
    
    char *input_filepath = NULL;
    int do_something_else = 0;
    
    int op;
    
    while((op = getopt(argc, argv, "ai:")) != -1){
    	switch(op){
    		case 'i':
    			input_filepath = optarg;
    			break;
    		case 'a':
    			do_something_else = 1;
    			break;
    		case ':':
    			fprintf(stderr,"Option -%c requires an operand\n", optopt);
    			return -1;
    			break;
    		case '?':
    			fprintf(stderr,"Unknown argument: %c\n", optopt);
    			return -1;
    			break;
    	}
    }
    Last edited by synthetix; 12-11-2010 at 04:15 PM.

  2. #2
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    What if you wanted the filename to be "-a"? You'd be stuck otherwise. Granted, you'd also be insane, but that's not the point. IOW, just having a - at the front won't automatically make getopt think it has to be an argument. (If you want, you can look at using :: instead of :, which may help in your case.)

  3. #3
    Registered User
    Join Date
    Jun 2009
    Posts
    101
    Quote Originally Posted by tabstop View Post
    (If you want, you can look at using :: instead of :, which may help in your case.)
    I tried this, but now getopt won't recognize anything passed with -i. This is strange because it says in the docs that :: just means the argument is optional. Well, if it's optional, then how does getopt determine whether one has been passed or not? If -a was being interpreted as an operand before, is it not now just because -i's argument is now optional? How does it know the difference?

  4. #4
    Registered User
    Join Date
    Jun 2009
    Posts
    101
    Quote Originally Posted by synthetix View Post
    I tried this, but now getopt won't recognize anything passed with -i.
    Oh I see, you need to pass it without any spaces:

    prog.exe -ifilename.txt

    Not sure I like this. Am I really going to have to check for a "-" on the returned argument myself?

  5. #5
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    I suppose it depends on how user-obsequious you want to be. If -i required an input file, then I would expect, if I typed "./program -i -a" to get the error message "file -a could not be opened for reading" or however it's written, which then means that I can try to run the program again, but right this time. If you want to try to save your user from every possible way to screw it up, then it's going to take a lot of annoyance on your part I would think.

  6. #6
    Registered User
    Join Date
    Jun 2009
    Posts
    101
    Quote Originally Posted by tabstop View Post
    I suppose it depends on how user-obsequious you want to be. If -i required an input file, then I would expect, if I typed "./program -i -a" to get the error message "file -a could not be opened for reading" or however it's written, which then means that I can try to run the program again, but right this time. If you want to try to save your user from every possible way to screw it up, then it's going to take a lot of annoyance on your part I would think.
    Grrr. I just added this at the end:

    Code:
    if(!input_filepath || input_filepath[0] == '-'){
    	fprintf(stderr,"You must specify a valid file name\n");
    	return -1;
    }
    I guess I could just instruct the user to specify the input file last. But that won't help if I have other options that require arguments too.

  7. #7
    Registered User hellork's Avatar
    Join Date
    Nov 2010
    Posts
    39
    I just did something like this, so the user could mix, match, and combine long and short options.
    -p -q -d -t
    -pdquiet -tabs
    -pullq -droptabs

    Code:
    #define HAS(a) strstr(argv[argc],#a)
        if (argc > 1){ /* parse command line options */
            for (--argc;argc;argc--){
                if (argv[argc][0]=='-'){
                    if (HAS(p)&&!HAS(drop)) pull = 1;
                    if (HAS(q)) loud = 0;
                    if (HAS(d)) drop = 1;
                    if (HAS(t)) tabs = 1;
                    if (!(pull|drop|tabs|!loud)) {
                        help();
                        return 0;
                    }
                } else fname = argv[argc];
            }
    Of course more detailed checking would be needed if more letters were in common than just 'p'!

    Note about operating on files that start with a dash '-'
    If you decide not to handle the latter option you would be in good company. It is difficult to even create such a file on unix. I'll leave you to figure out how to create it. Even when successful, few of the bash commands or utilities work on such a file. e.g. `ls'
    $ myuglyfilecreator -pull.txt
    $ ls '-pull.txt'
    $ ls: invalid option -- '.'
    $ ls *ull.txt
    $ ls: invalid option -- '.'
    $ ls
    -pull.txt
    $ rm *pull.txt
    $ rm: invalid option -- 'p'
    $ ls
    -pull.txt

    To remove it:
    find . -name \*pull.txt -exec rm {} \;

  8. #8
    Registered User hellork's Avatar
    Join Date
    Nov 2010
    Posts
    39
    Nit-pickers will notice that the letter 't' is also in common. Free code is never guaranteed to be bug free!

  9. #9
    Registered User
    Join Date
    Jun 2009
    Posts
    101
    Quote Originally Posted by hellork View Post
    Note about operating on files that start with a dash '-'
    If you decide not to handle the latter option you would be in good company. It is difficult to even create such a file on unix. I'll leave you to figure out how to create it. Even when successful, few of the bash commands or utilities work on such a file. e.g. `ls'
    Ha! Try this:

    touch test

    mv test -test

    mv -test test
    mv: illegal option -- t
    usage: mv [-f | -i | -n] [-v] source target
    mv [-f | -i | -n] [-v] source ... directory

    mv "-test" test
    mv: illegal option -- t
    usage: mv [-f | -i | -n] [-v] source target
    mv [-f | -i | -n] [-v] source ... directory

    I can't rename it back!

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Quantum Random Bit Generator
    By shawnt in forum C++ Programming
    Replies: 62
    Last Post: 06-18-2008, 10:17 AM
  2. Ranged numbers
    By Desolation in forum Game Programming
    Replies: 8
    Last Post: 07-25-2006, 10:02 PM
  3. Dikumud
    By maxorator in forum C++ Programming
    Replies: 1
    Last Post: 10-01-2005, 06:39 AM