Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

GetOpt for .NET

0.00/5 (No votes)
29 May 2008 15  
A GetOpt implementation for .NET.

Introduction

"getopt" is a familiar function in C programming on UNIX-like operating systems, but outside of that (i.e., Windows, Mac OS), it is nearly non-existent. The purpose of getopt is to help the programmer parse options and their arguments when passed to the application via the command line.

CpGetOpt is the name of this little project that provides a (partial for now) getopt implementation for .NET, that is written in C#. CpGetOpt is POSIX compliant and can, optionally, emulate glibc's version of getopt almost exactly. Currently, long options are not supported, but will be in the very near future.

For more information on getopt, visit the glibc documentation at www.gnu.org.

Also, visit my blog for more of my work.

Background

Getopt has been around for a very long time, and helps deal with what can turn into a complex task: parsing program arguments, and the arguments that they, themselves, can take. Getopt is part of most C runtime libraries, and implementations exist in many programming languages. Normally, getopt supports only Unix-style command options, but support for Windows style options isn't that difficult to implement (see the last paragraph in this section).

What are options?

Options allow the programmer to handle optional program arguments in a more structured manner. In essence, options are special kinds of command line arguments that are meant to give meaning to flags or variables in the program itself, and as their name implies, are optional. Options allow the programmer to handle optional program arguments in a more structured manner.

The same kind of functionality can be achieved by just checking the number of program arguments given, and interpreting them based on that. However, obviously, that is much more complex and time consuming, whereas using getopt is much more simplistic.

How are options passed to an application via the command line?

Simple; an option is just prefixed with a dash ("-"), and then a character that identifies the option being specified, and then an argument to give that option, if any arguments are allowed. Additionally, an argument can be supplied to an option in two separate ways. In the first: the option is followed by a white space, and then the argument; in the second form: the option is followed by the argument with no white space (when using this form, the option character must immediately follow the dash, and the character following must not be an option itself; "-afoo" would not work as expected if 'f' were also a valid option).

# In this example, both commands are equivalent.
Example:
    app.exe -a foo
    app.exe -afoo

Furthermore, assuming that you use the first form to specify the options being passed to a program, you can combine multiple options into one string.

# In this example, both commands are equivalent.
Example:
    app.exe -abc
    app.exe -a -b -c

In the event in which options 'b' and 'c' require an argument, the option string can stay as it is, and the arguments need only follow the options in their respective order.

# In this example, both commands are equivalent.
Example:
    app.exe -abc foo bar
    app.exe -a -b foo -c bar

What are long options?

Long options are exactly like regular options, with the exception that they can be more than one character in length (i.e., --verb instead of -v). And, when including an option argument in the option string, you separate the two by an equals sign ("=") with no white space.

# For an application where options 'i' and 'include' have the same meaning,
# the commands below are all equivalent.
Example:
    app.exe -iarg
    app.exe -i arg  
    app.exe --include=arg 
    app.exe --include arg

Unfortunately, CpGetOpt does not yet support long options, but will very soon.

"getopt" is a standard component of most libc libraries, and is also a part of the POSIX specification; but as previously stated, it is absent from Microsoft's C run-time. Windows does use the concept of options when invoking commands, with one slight difference: instead of specifying an option with a prefixed "-" or long options with a "-", all options are simply prefixed with a single forward slash ("/"); and instead of using an "=" where supplying an option argument with the option itself, a colon (":") is used.

    # UNIX-style
    app -a -b -c foo --longopt=arg
    # Windows-style
    app.exe /a /b /c foo /longopt:arg

Using the code

Currently, CpGetOpt defines two types: GetOptionsSettings and GetOpt.

GetOptionsSettings provides an enumeration that can be given as a set of flags to the GetOpt.GetOptions function to control how the options are parsed.

  • GlibcCorrect - Specifies that GetOptions should behave exactly as glibc's getopt function does.
  • PosixCorrect - Specifies that GetOptions should behave in a manner that is compliant to the POSIX standard.
  • ThrowOnError - Throws an ApplicationException when an error occurs during parsing.
  • PrintOnError - Prints an error message to the command line when an error occurs.
  • None - No options are set; use default behavior.

GetOpt is the container class that provides the getopt implementation.

The GetOptions method of the GetOpt class is what must be used to consecutively parse command line arguments, and it takes three arguments, with the third being optional.

int GetOptions(string[] args, string options, [GetOptionsSettings settings])
  • args - A string array; the arguments array given to you by the Main method.
  • options - A string specifying the valid options and their argument requirements. This string must be in the same format as the string used by the original getopt. "An option character in this string can be followed by a colon (:) to indicate that it takes a required argument. If an option character is followed by two colons (::), its argument is optional."
  • settings - A bitwise OR combination of the GetOptionsSettings enumeration values that can optionally alter the parsing method to other behaviors.

GetOptions returns the character (cast as an int) that identifies the option, '?' when the current option has resulted in an error, and returns -1 when all options have been parsed.

By default, after every successful call to GetOptions, the option name can be accessed through the GetOpt.Item property. However, when GetOptionsSettings.GlibcCorrect is specified, this behavior is only true when parsing that option that resulted in an error.

If the option returned has an argument, then that argument can be accessed using the GetOpt.Text property.

After all options have been parsed and -1 has been returned by GetOptions, access the GetOpt.Index property to get the index within the args array at which you can resume normal argument processing.

Important: All state information maintained by the GetOptions method is thread-static. This means that calls to GetOptions in different threads of execution will behave independently of one another.

So how does it work?

Simple; call GetOpt.GetOptions in a loop until it returns -1, and use the character returned to handle the options given to the application.

//
// Normally, getopt is called in a loop. When getopt returns -1, indicating
// no more options are present, the loop terminates.
//
// A switch statement is used to dispatch on the return value from getopt.
// In typical use, each case just sets a variable that is used later in the program.
//
// A second loop is used to process the remaining non-option arguments. 
//

// Make sure to add CpGetOpt.dll as an assembly reference in your project
// and then just add a "using CodePoints;" statement.
using CodePoints;
using System;

...
public static void Main ( string [] args ) {
    int c = 0, aflag = 0, bflag = 0;
    string cvalue = "(null)";

    while ( ( c = GetOpt.GetOptions(args, "abc:") ) != ( -1 ) ) {
        switch ( ( char ) c ) {
            case 'a':
                aflag = 1;
                break;
            case 'b':
                bflag = 1;
                break;
            case 'c':
                cvalue = GetOpt.Text;
                break;
            case '?':
                Console.WriteLine("Error in parsing option '{0}'", GetOpt.Item);
                break;
            default:
                return;
        }
    }

    Console.WriteLine("aflag = {0}, bflag = {1}, cvalue = {2}", aflag, bflag, cvalue);

    for ( int n = GetOpt.Index ; n < args.Length ; n++ )
        Console.WriteLine("Non-option argument: {0}", args [n]);
}
...

Here are some examples showing what this program prints with different combinations of arguments:

% testopt.exe
aflag = 0, bflag = 0, cvalue = (null)

% testopt.exe -a -b
aflag = 1, bflag = 1, cvalue = (null)

% testopt.exe -ab
aflag = 1, bflag = 1, cvalue = (null)

% testopt.exe -c foo
aflag = 0, bflag = 0, cvalue = foo

% testopt.exe -cfoo
aflag = 0, bflag = 0, cvalue = foo

% testopt.exe arg1
aflag = 0, bflag = 0, cvalue = (null)
Non-option argument: arg1

% testopt.exe -a arg1
aflag = 1, bflag = 0, cvalue = (null)
Non-option argument: arg1

% testopt.exe -c foo arg1
aflag = 0, bflag = 0, cvalue = foo
Non-option argument: arg1

% testopt.exe -a -- -b
aflag = 1, bflag = 0, cvalue = (null)
Non-option argument: -b

% testopt.exe -a -
aflag = 1, bflag = 0, cvalue = (null)
Non-option argument: -

Points of interest

Take a look at the source if you want to see how I implemented getopt. I'll say this: implementing getopt correctly and functionally is not as easy as it seems at first glance. Anyways, I hope someone can utilize and find some use for CpGetOpt.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here