[Mono-list] A vaguely POSIXy Getopt class.

Juli Mallett jmallett@FreeBSD.ORG
Fri, 13 Dec 2002 14:51:02 -0800


I wanted to write some apps that'd take arguments in the POSIXy way,
and so I decided to implement a Getopt class which provides all the
common things, and aside from the fact that I use an exception to
show the end of args (other than using an output parameter boolean
and checking that, I couldn't think of a saneish way), I'm fairly
happy with it.  I have no idea if anyone but me would even begin to
find this useful, but it follows in this message, and the usage is
such (it will even do a lazy usage() style message for you, if you
don't want to construct a prettier one yourself):

% public static void Main(string[] args)
% {
% 	Getopt getopt = new Getopt(args, "a:b");
% 	char ch;
% 	for (;;) {
% 		try {
% 			ch = getopt.Next();
% 		} catch (IndexOutOfRangeException) {
% 			goto EndOfArgs;
% 		} catch (Exception e) {
% 			Console.WriteLine("Getopt got {0}", e);
% 			System.Environment.Exit(1);
% 			//Notreached.
% 			return;
% 		}
% 		switch (ch) {
% 		case 'a':
% 			Console.WriteLine("Got -a with arg {0}", getopt.OptionArgument);
% 			break;
% 		case 'b':
% 			Console.WriteLine("Got -b");
% 			break;
% 		case '?':
% 		default:
% 			getopt.Usage();
% 			System.Environment.Exit(1);
% 			//Notreached.
% 			return;
% 		}
% 	}
% EndOfArgs:
% 	// Finished argument parsing.

And here's getopt.cs:

%%%
using System;
using System.Reflection;

class Getopt
{
	public static char BadCharacter = '?';
	public static char BadArgument = ':';

	public bool Error = true;
	public int Index = 0;
	public char Option;
	public bool Reset;
	public string OptionArgument;

	public string ProgramName;

	private Assembly CallingAssembly;

	private int Length;
	private string[] Array;
	private string Options;

	private string Place;

	public Getopt(string progname, int argc, string[] argv, string ostr)
	{
		CallingAssembly = System.Reflection.Assembly.GetEntryAssembly();
		Length = argc;
		Array = argv;
		Options = ostr;
		if (progname == null)
			progname = CallingAssembly.ToString();
		ProgramName = progname;
	}

	public Getopt(string progname, string[] argv, string ostr)
	: this(progname, argv.Length, argv, ostr)
	{
	}

	public Getopt(int argc, string[] argv, string ostr)
	: this(null, argc, argv, ostr)
	{
	}

	public Getopt(string[] argv, string ostr)
	: this(argv.Length, argv, ostr)
	{
	}

	public void Usage()
	{
		string iterator;
		string usageString;

		usageString = String.Format("usage: {0}", ProgramName);

		iterator = Options;
		if (iterator == null) {
			Console.WriteLine(usageString);
			return;
		}
		if (iterator[0] == ':') {
			iterator = GetStringNext(iterator);
		}
		if (iterator == null) {
			Console.WriteLine(usageString);
			return;
		}
		do {
			if (iterator.Length == 0)
				break;
			char optionFlag = iterator[0];
			usageString += String.Format(" -{0}", optionFlag);
			if (iterator.Length > 1) {
				if (iterator[1] == ':') {
					usageString += " <argument>";
					iterator = GetStringNext(iterator);
				}
			}
			iterator = GetStringNext(iterator);
		} while (iterator != null);
		Console.WriteLine(usageString);
	}

	public char Next()
	{
		if (Place == null)
			Place = "";
		if (Reset || Place.Length == 0) {
			Reset = false;
			if (Index >= Length || (Place = Array[Index])[0] != '-') {
				Place = null;
				throw new IndexOutOfRangeException();
			}
			if (Place[0] == '-' && Place[1] == '-') {
				Index++;
				Place = null;
				throw new IndexOutOfRangeException();
			}
			Place = GetStringNext(Place);
		}

		Option = Place[0];
		Place = GetStringNext(Place);
		if (Place == null)
			throw new IndexOutOfRangeException();
		int optionLetterIndex = Options.IndexOf(Option);
		if (Option == ':' || optionLetterIndex == -1) {
			if (Option == '-')
				throw new IndexOutOfRangeException();
			if (Place.Length == 0)
				Index++;
			if (Error && Options[0] != ':' && Option != BadCharacter)
				Console.WriteLine("{0}: illegal option -- {1}", ProgramName, Option);
			return BadCharacter;
		}
		if (Options[optionLetterIndex + 1] != ':') {
			OptionArgument = null;
			if (Place.Length == 0)
				Index++;
		} else {
			if (Place.Length != 0)
				OptionArgument = Place;
			else if (Length <= ++Index) {
				Place = null;
				if (Options[0] != ':')
					return BadArgument;
				if (Error)
					Console.WriteLine("{0}: option requires an argument -- {1}", ProgramName, Option);
				return BadCharacter;
			} else
				OptionArgument = Array[Index];
			Place = null;
			Index++;
		}
		return Option;
	}

	private string GetStringNext(string ThisString)
	{
		try {
			ThisString = ThisString.Substring(1);
		} catch {
			ThisString = null;
		}
		return ThisString;

	}
}
%%%

Thanx & let me know if you find it useful,
juli.
-- 
Juli Mallett <jmallett@FreeBSD.org>
OpenDarwin, Mono, FreeBSD Developer.
ircd-hybrid Developer, EFnet addict.
FreeBSD on MIPS-Anything on FreeBSD.