1

I am working on a program that will scan directories for file information. There will be optional switches the user can put it in to scan for certain file groups or to scan in certain ways, or to output in certain ways.

For example if the user enters: filesum -rRc, this will do a recursive scan, output the files in ascending order and only scan for C++ related file extensions (.cpp|.h|.hpp etc).

How do I do this in the cleanest way possible? I have looked into getopt but I don't understand its implementation at all, and finding good examples with documentation is difficult.

I was thinking of converting the command line argument that the switches are entered in on to a string and parsing through that but wouldn't that require multiple if statements to see if its just one switch in the argument or if there is something following that is able to be done such as -rc? Or I was also thinking of a while loop with different flags based on what switches were entered, but if there is a cleaner, and easier way I would greatly appreciate any suggestions. Thanks.

Would prefer to not use any third party libraries, if that is at all possible.

Delete
  • 289
  • 1
  • 2
  • 19
  • Of course, I'm going to plug my own argument-parsing library too: http://www.taenarum.com/software/dropt/ – jamesdlin Aug 03 '13 at 01:18

3 Answers3

0

The cleanest way to do this is to use Boost program_options.

johnsyweb
  • 136,902
  • 23
  • 188
  • 247
  • Could you give me a quick example of how something like this is implemented? Also, since its part of the Boost library, would I have to download and add it? – Delete Aug 03 '13 at 01:21
0

I checked how gnu cat handles command line options. I wrote example in C. You can pass flags like: cli -ab file or cli -b -ac file or cli -bc file -a

#include <stdio.h>

int main (int argc, char *argv) {
    int c;
    while ((c = getopt(argc, argv, "abc")) != -1) {
        switch (c) {
            case 'a':
                printf("selected: a\n");
                break;
            case 'b':
                printf("selected: b\n");
                break;
            case 'c':
                printf("selected: c\n");
                break;
            default:
                printf("Usage: cli -a/-b/-c\n");
                break;
        }
    }
}
Ari Malinen
  • 576
  • 2
  • 6
  • 20
0

It's funny, I was doing this right now. I'll only describe the general idea here. The full code is more than 500 lines currently, and using more utilities for formatting etc.

As an example, I'm making program help (which is quite dummy). A plain struct contains all options the program needs, along with default values:

struct help_opt
{
    string owner;
    string path;
    bool rec;
    int depth;
    help_opt() : owner(""), path("/"), rec(false), depth(0) { }
};

Another struct models the command-line arguments:

struct help_args : public help_opt, public arguments <help_args>
{
    template <typename S>
    void define(S& s)
    {
        set(s, owner,   "owner",    'w',  "force <name> as owner",         "name");
        set(s, path,    "output",   'o',  "save all output to <path>",     "path");
        set(s, rec,     "recurse",  'r',  "recurse into sub-topics");
        set(s, depth,   "depth",    'd',  "set <number> as maximum depth", "number");
    }

    string title() const { return "my help"; }
    string usage() const { return "help [<options>] [<topic>]"; }

    string info() const
    {
        return
            "Display help on <topic>, "
            "or list available topics if no <topic> is given.";
    }

    help_args(int argc, char* argv[]) : arguments(argc, argv) { }
};

where class arguments is all the infrastructure I need. All arguments along with metadata (name, abbreviation etc.) are defined in define(), each by a call to method set().

Argument s is used to support multiple operations, e.g. collect user input, build the help text, or display the values given by the user. We then pass on the reference to each member, which may be given an appropriate value according to user input, or left with the default value. Then follows the argument name, abbreviation, full help text, and optionally a parameter name. Boolean arguments are treated separately as flags, with no parameter.

Additional methods title(), info() etc. specify information messages that are custom to the program.

Actual processing is done by the constructor of arguments. This calls define() in help_args, which in turn calls set() in arguments. For each option, the command line input is scanned and the variables are updated as needed. At the same time, metadata are collected and output is generated automatically for options like --help, --usage, --version as in gnu programs.

The fact that help_args is passed as a template parameter to arguments is to allow define() to be called from the base class, even though it is a template hence not virtual. If a single operation is only needed, then we can switch back to plain virtual methods.

I hope this is quite clean. I know there are many tools and libraries available, but I really prefer it this way. I can share the implementation when ready, it's almost done.

iavr
  • 7,547
  • 1
  • 18
  • 53