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.
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.