views:

71

answers:

2

I have tried to read up on the subject but when I try to run what I thought would be the correct thing it doesn't do what I want it to :)

I have a main program, that should take three mandatory arguments, let's call them t, p and s. Then I have several modules that, as a part of their initialization, also rely on command line arguments. These modules have a command line argument that signals that they are to be used at all.

When I build my string of possible commandline options in my main program I make it look like t:p:s: and I also loop all the modules and request their "triggering" option character. So the finished string might look like t:p:s:iv, where i and v are triggers for two different modules.

Then in the while loop I do something like this (options is the above mentioned string)

while ((opt = getopt(argc, argv, options)) != -1) {
        switch (opt) {
            case 't':
                break;
            case 's':
                break;
            case 'p':
                break;
            case 'h':
            default:
                ShowHelp(this, argc, argv);
                exit(EXIT_FAILURE);
        }

        /*
         * Check all available modules and see if options should be passed to it
         */
        i = 0;
        while((module = this->availablemodules[i++]) != NULL) {
            if(opt == module->triggerArg) {
                output = module->ParseOptions(module, argc, argv);
                AddActiveModule(this, module);
            }
        }
    }

The this->availablemodules variable is a NULL terminated array of pointers to existing modules. As you can see, I first check for the options used by the main program and then check if given option is a triggering option for a module and in that case I send the arguments to be parsed by that module.

That module might then in turn have a different set of options it takes. So the entered arguments might have looked like this

./myprog -t foo -s bar -p 10 -i -c 2 -v -c 1 -t bar

where the -i and -v are module "triggers" and the options after those should be handled in that modules parsing function.

How do I best make this work? And how do I best handle the fact that a modules triggering option might be the same as one of the options needed by the main program (i.e. t: is used both as an option in the main program and t is used as a module "trigger")? And is there an ability to force some commands to be mandatory, or do I have to check that manually after parsing all options?

First I constructed the main programs optionstring using the optionstring of every module, but if one module was not to be called that would confuse the system.

I hope everything is clear, but if it's not just ask... I'm a bit confused about how this should be handled so might not explain it very well.

Update: I just read a bit about getopt_long and was thinking, would it be possible to do something like this with that

./myprog -t foo -s bar -p 10 --module=module1 -c 2 --module=module2 -c 1 -t bar

and have module1 and module2 being what identifies the modules to activate instead? Or would it confuse the parser to use --module several times?

+1  A: 

One way is to enclose the arguments to modules in quotes:

./myprog -t foo -s bar -p 10 -i="-c 2" -v="-c1 -t bar"

and then pass the quoted argument to the module. This allows you to repeat parameter identifiers in each module's set of valid parameters.

Alternatively, use an .ini file:

[global]
t=foo
s=bar
p=10

[i]
c=2

[v]
c1=true
t=bar

and pass this as the only parameter to the application. There are severaly libraries that can parse this kind of file (I think it's even part of the Win32 API).

As an alternative to the .ini file is the .xml format but they're more complex to parse but you can validate the file using a schema. Again, there are libraries that can do this for you.

Skizz
As a clarification (that might not make much difference), the target is a Slackware Linux distro. The enclosing of options in quotes would probably work, but it would be nice if the user didn't have to type options in like that (I guess it's a fairly unusual way to type arguments?)
inquam
A: 

I did as I stated in my initial update. I now pass --module=nameofModule followed by all options for that module. When a --module option has been recognized I stop evaluating the options in the normal while loop and instead put aside all following options in a new array to be passed to the parseOptions function of the named module. I keep doing this until we reach a new --module option or EOF. As suggested I don't use a printable character for the --module option since that could confuse the parsing algorithm if a existing option would have the same character, instead I use the character 0x07f (DEL).

 struct option long_options[] = {
      { "module", required_argument, &long_val, 0x7f   },
      { "src", required_argument, &long_val, 's'  },
      { "dst", required_argument, &long_val, 'd' },
      { 0,    0,    0,    0   }       /* terminating -0 item */
  };

My loop for parsing options then looks a bit like this

      // Hold pointer to a module activated by being declared in the arguments
      module* found_module = NULL;
      // Hold options to pass to module
      char* module_options[256];
      int  module_argc = 0;

      int arg_index = 0;

        while ((opt = getopt_long(argc, argv, "-0x7fsd", long_options, &long_opt_index)) != EOF) {
            ++arg_index;
            /*
             * If we have found a module declaration store all following options until EOF or a new
             * module is declared to be sent to the modules argument parsing function
             */
            if(found_module && long_val != 0x7f) {
                strcpy(module_options[module_argc], argv[arg_index]);
                ++module_argc;
            }
            else {

                if(found_module) {
                    output = found_module->ParseOptions(found_module, module_argc, module_options);
                    fprintf(stderr, "Adding module %s as active!\n", found_module->name);
                    AddActiveModule(this, found_module);
                    found_module = NULL;
module_argc = 0;
// todo: also empty the module_options array
                }

                switch (opt) {
                    case 0: /* this is returned for long options with option[i].flag set (not NULL).
    the flag itself will point out the option recognized, and long_opt_index is now relevant */
                        switch(long_val)
                        {
                            case 0x7f:
                                printf("Option --module,  Argument: %s.\n", long_opt_index, optarg);
                                i = 0;
                                while((module = this->availablemodules[i++]) != NULL) {
                                    if(strcmp(optarg, module->optmoduleName)) {
                                        found_module = module;
                                        continue;
                                    }
                                }

                                break;
                                /* there's no default here */
                        }
                        break;
                    case 'h':
                    default:
                        ShowHelp(this, argc, argv);
                        exit(EXIT_FAILURE);
                } // switch
            } // else
        } // while

    /*
     * Have a trailing check here since EOF will terminate while loop but we might have a
     * module left to activate
     */
        if(found_module) {
            output = found_module->ParseOptions(found_module, module_argc, module_options);
            fprintf(stderr, "Adding module %s as active!\n", found_module->name);
            AddActiveModule(this, found_module);
            found_module = NULL;
        }

This seems to work perfect, but I might have missed something anyways. As I stated before, I kinda get a bit confused of getopt and the automatic reordering that seemed to be able to take place. As I gathered the - at the beginning of the option string prevents this, is that correct?

inquam