/*************************************************************************
 * File Name: MiscCommandLineParameterParser.m
 * Version  : 0.6 beta
 * Date     : 08-jul-1995
 *************************************************************************
 *                 COPYRIGHT (C) 1995, 1996 by SciTools Project          *
 *                           and Georg Tuparev                           *
 *                          ALL RIGHTS RESERVED.                         *
 *                                                                       *
 *************************************************************************
 * This software is furnished without a license and may be freely used,  *
 * copied, modified and redistributed, provided that the above copyright *
 * notice is not removed.  This software or any other copies thereof may *
 * be provided or otherwise made available to any other person in any    *
 * form.  No title to and ownership of the software is hereby            *
 * transferred.  It is not permitted to sell this software.  The         *
 * information in this software is subject to change without notice and  *
 * should not be construed as a commitment by the SciTools Project or by *
 * the authors.                                                          *
 * NEITHER THE SCITOOLS PROJECT, NOR THE AUTHORS ASSUME  RESPONSIBILITY  *
 * FOR THE USE OR RELIABILITY OF THIS SOFTWARE PRODUCT. WE DO HOPE,      *
 * HOWEVER, TO GET RESPONSES FROM USERS, ESPECIALLY WHEN IMPROVEMENTS    *
 * HAVE BEEN MADE.                                                       *
 *************************************************************************
 * Description: 
 * Notes      : 
 * To Do      : 
 * Bugs       : 
 * Author(s)  : Georg Tuparev, EMBL & Academia Naturalis, 
 *              Heidelberg, Germany
 * Last update: 08-jul-1995
 * History    : 10-jun-1995    - Birth;
 *              07-jul-1995    - First beta (more or less tested) version;
 *              08-jul-1995    - Full implementation of NSCoding stuff & 
 *                               description method;
 *************************************************************************/

#import "MiscCommandLineParameterParser.h"

#import <stdarg.h>
#import <string.h>
//#import <objc/objc-runtime.h>

#import <Foundation/NSZone.h>
#import <Foundation/NSException.h>
#import <Foundation/NSCoder.h>



#define MISC_CLASS_VERSION 2   // Current MiscCommandLineParameterParser version
#define MISC_CLASS_NAME @"MiscCommandLineParameterParser"  // Class name
#define MISC_EMPTY      @"Undefined object"
#define MISC_YES_STR    @"Yes"
#define MISC_NO_STR     @"No"
#define MISC_UNKNOWN_POINTER @"Pointer with unknown type"

/*
 *  __MiscCommandLineParameter is a private class. One should never attempt to
 * use it from outside MiscCommandLineParameterParser. 
 * There is no guarantee, that the class interface will not be changed in 
 * the future!
 * This class provides a wrapper around a single parameter and its argument 
 * list. It provide methods for notifying the delegate and is a placeholder
 * for a call-back function. Checking for conflicting parameters is also 
 * __MiscCommandLineParameter's responsibility.
 * Care was taken to avoid multiple calls of the call-back function and 
 * the delegate's action.
 */
 
@interface __MiscCommandLineParameter:NSObject <NSCoding>
{
	BOOL             acceptsArguments;
	BOOL             isUsed;
	BOOL             isActionPerformed;
	NSMutableArray*  values;
	
	Misc_ArgProc       handler;
	SEL 	           method;
	id               delegate;
	NSMutableArray*  conflicts;
}
+ (void)initialize;

+ (NSString *)className;
+ (NSString *)description;
- (id)init;
- (void)dealloc;
- (id)initWithCoder:(NSCoder *)aDecoder;
- (void)encodeWithCoder:(NSCoder *)aCoder;
- (NSString *)description;
- (NSString *)className;
- (void)setDelegate:(id)aDelegate;
- (void)setAcceptsArgumentsEnabled:(BOOL)flag;
- (BOOL)isAcceptsArgumentsEnabled;
- (void)setConflictingParameter:(char *)param;
- (NSArray *)conflictingParameters;

- (void)setHandler:(Misc_ArgProc)aHandler;
- (void)setSelector:(SEL)aMethod;

- (void)useIt;
- (BOOL)isUsed;
- (void)addValue:(NSString *)aValue;
- (NSArray *)values;

- (void)performAction;

@end

@implementation __MiscCommandLineParameter
#define __MISC_CLASS_VERSION 1                         // Current MiscObject version
#define __MISC_CLASS_NAME @"__MiscCommandLineParameter - Private class"  // Class name

+ (void)initialize
{
	if (self == [__MiscCommandLineParameter class])  {
		[self setVersion:__MISC_CLASS_VERSION]; // Set the class version
	}
	else
		[self doesNotRecognizeSelector:_cmd]; // Prints an error and aborts the app
	return;
}

+ (NSString *)className
{
	return [NSString stringWithFormat:@"%@",__MISC_CLASS_NAME];
}

+ (NSString *)description
{
	return [NSString stringWithFormat:@"%@", __MISC_CLASS_NAME];
}

- (id)init
{
	[super init];
	
	acceptsArguments = NO;
	isUsed = NO;
	
	handler = NULL;
	method = NULL;
	delegate = nil;
	
	isActionPerformed = NO;
	
	conflicts = [NSMutableArray arrayWithCapacity:0];
	[conflicts retain];
	
	return self;
}

- (void)dealloc
{
	if (delegate)
		[delegate release];
	[conflicts removeAllObjects];
	[conflicts release];
	
	return [super dealloc];
}

- (void)encodeWithCoder:(NSCoder *)aCoder
{
	[aCoder encodeValuesOfObjCTypes:"ccc", &acceptsArguments, &isUsed, 
		&isActionPerformed];
	[aCoder encodeObject:values];
	[aCoder encodeObject:conflicts];
	return;
}

- (id)initWithCoder:(NSCoder *)aDecoder
{
	[aDecoder decodeValuesOfObjCTypes:"ccc", &acceptsArguments, &isUsed, 
		&isActionPerformed];
	values = [[aDecoder decodeObject] retain];
	handler = NULL;
	method = NULL;
	delegate = nil;
	conflicts = [[aDecoder decodeObject] retain];
	return self;
}

- (NSString *)description
{
	NSMutableDictionary* descDict = 
		[NSMutableDictionary dictionaryWithCapacity:8];

	if (acceptsArguments)
		[descDict setObject:MISC_YES_STR
			forKey:[NSString stringWithCString:"acceptsArguments"]];
	else
		[descDict setObject:MISC_NO_STR
			forKey:[NSString stringWithCString:"acceptsArguments"]];
	if (isUsed)
		[descDict setObject:MISC_YES_STR
			forKey:[NSString stringWithCString:"isUsed"]];
	else
		[descDict setObject:MISC_NO_STR
			forKey:[NSString stringWithCString:"isUsed"]];
	if (isActionPerformed)
		[descDict setObject:MISC_YES_STR
			forKey:[NSString stringWithCString:"isActionPerformed"]];
	else
		[descDict setObject:MISC_NO_STR
			forKey:[NSString stringWithCString:"isActionPerformed"]];
	if (values)
		[descDict setObject:values 
			forKey:[NSString stringWithCString:"values"]];
	else
		[descDict setObject:MISC_EMPTY
			forKey:[NSString stringWithCString:"values"]];
	if (delegate)
		[descDict setObject:delegate
			forKey:[NSString stringWithCString:"delegate"]];
	else
		[descDict setObject:MISC_EMPTY
			forKey:[NSString stringWithCString:"delegate"]];
	if (conflicts)
		[descDict setObject:conflicts
			forKey:[NSString stringWithCString:"conflicts"]];
	else
		[descDict setObject:MISC_EMPTY
			forKey:[NSString stringWithCString:"conflicts"]];
	if (method)
		[descDict setObject:[NSString stringWithCString:sel_getName(method)]
			forKey:[NSString stringWithCString:"method"]];
	else
		[descDict setObject:MISC_EMPTY
			forKey:[NSString stringWithCString:"method"]];
	if (handler)
		[descDict setObject:MISC_UNKNOWN_POINTER
			forKey:[NSString stringWithCString:"handler"]];
	else
		[descDict setObject:MISC_EMPTY
			forKey:[NSString stringWithCString:"handler"]];
	
	return [descDict description];
}

- (NSString *)className
{
	return [NSString stringWithFormat:@"%@",__MISC_CLASS_NAME];
}

- (void)setDelegate:(id)aDelegate
{
	delegate = aDelegate;
	[delegate retain];
	return;
}

- (void)setAcceptsArgumentsEnabled:(BOOL)flag
{
	acceptsArguments = flag;
	return;
}

- (BOOL)isAcceptsArgumentsEnabled
{
	return acceptsArguments;
}

- (void)setConflictingParameter:(char *)param
{
	NSString *newP;
	
	if (param) {
		newP = [NSString stringWithCString:param];
		[conflicts addObject:newP];
	}
	
	return;
}

- (NSArray *)conflictingParameters
{
	return conflicts;
}

- (void)setHandler:(Misc_ArgProc)aHandler
{
	handler = aHandler;
	return ;
}

- (void)setSelector:(SEL)aMethod
{
	method = aMethod;
	return;
}

- (void)addValue:(NSString *)aValue
{
	if (!values) {
		values = [NSMutableArray arrayWithCapacity:1];
		[values retain];
	}
	[values addObject:aValue];
	
	return;
}

- (NSArray *)values
{
	return values;
}

- (void)useIt
{
	isUsed = YES;
	return;
}

- (BOOL)isUsed
{
	return isUsed;
}

- (void)performAction
{
	char** arguments;
	int    i;
	
	if (isActionPerformed)
		return;
		
	if (handler) {
		if (values && ([values count] > 0)) {
			// Create a NULL-term array of strings
			arguments = (char **)NSZoneMalloc([self zone],
				sizeof(char **) * ([values count]+1));
			for (i = 0; i < [values count]; i++) {
				arguments[i] = NSZoneMalloc([self zone],
					[[values objectAtIndex:i] cStringLength]);
				strcpy(arguments[i], [[values objectAtIndex:i] cString]);
			}
			arguments[[values count]] = NULL;
			handler(arguments); 
		}
		else
			handler(NULL);
	}
	if (method && delegate && [delegate respondsToSelector:method]) {
		if (values && ([values count] > 0))
			[delegate performSelector:method withObject:values];
		else
			[delegate performSelector:method withObject:nil];
	}
	
	// Prevent repetitive execution (e.g. help, ...)
	isActionPerformed = YES;
		
	return;
}

@end

@interface MiscCommandLineParameterParser(__Local_MiscCommandLineParameterParser)
// Errors
typedef enum __errorCodes {
	ERR_AppName,
	ERR_MultipleParameters,
	ERR_WrongParameter,
	ERR_UnknownParameter,
	ERR_ArgumentNotAllowed,
	ERR_ConflictingParameters
} ErrorCodes;

static NSString* __errorMessages[] = {
	@"Bad first parameter (application name)",
	@"Multiple parameters are not allowed",
	@"Wrong parameter",
	@"Unknown parameter",
	@"Argument not allowed",
	@"Conflicting parameters"
};

static const numberOfIvars = 17;
- (void)__freeArgVector;
- (void)__freeIvars;
- (void)__addAllowedParameter:(char *)param hasValue:(BOOL)flag
	handler:(Misc_ArgProc)proc selector:(SEL)method;
- (void)__parseAppName;
- (void)__addError:(ErrorCodes)errNumb withString:(NSString *)comment;
- (void)__prepareSeparators;
- (void)__prepareHelpParsing;
- (BOOL)__isParameter:(int)paramNumb;
- (__MiscCommandLineParameter *)__pushParameter:(char *)param;
- (__MiscCommandLineParameter *)__newParameter;
- (BOOL)__noConflictsFound;
- (BOOL)__noWrongArgumentsFound;
- (void)__performActions;
@end

@implementation MiscCommandLineParameterParser

/*************************************************************************
 *** Initializing the class
 *************************************************************************/
+ (void)initialize
{
	if (self == [MiscCommandLineParameterParser class])  {
		[self setVersion:MISC_CLASS_VERSION]; // Set the class version
	}
	else
		[self doesNotRecognizeSelector:_cmd]; // Prints an error and aborts the app
	return;
}

/*************************************************************************
 *** Class & object Name and Description
 *************************************************************************/
+ (NSString *)className
{
	return [NSString stringWithFormat:@"%@",MISC_CLASS_NAME];
}

+ (NSString *)description
{
	return [NSString stringWithFormat:MISC_CLASS_NAME];
}

- (NSString *)description
{
	NSMutableDictionary* descDict = 
		[NSMutableDictionary dictionaryWithCapacity:numberOfIvars];
	NSMutableString* argList = [NSMutableString stringWithCapacity:0];
	int i;
	
	[descDict setObject:[NSString stringWithFormat:@"%d", argCount] 
		forKey:[NSString stringWithCString:"argCount"]];
		
	for (i = 0; i < argCount; i++)
		[argList appendFormat:@"%s\n", argVector[i]];
	[descDict setObject:argList
		forKey:[NSString stringWithCString:"argVector"]];
	
	if (parameterDictionary)
		[descDict setObject:parameterDictionary 
			forKey:[NSString stringWithCString:"parameterDictionary"]];
	else
		[descDict setObject:MISC_EMPTY
			forKey:[NSString stringWithCString:"parameterDictionary"]];
		
	if (parameterSeparators)
		[descDict setObject:parameterSeparators
			forKey:[NSString stringWithCString:"parameterSeparators"]];
	else
		[descDict setObject:MISC_EMPTY
			forKey:[NSString stringWithCString:"parameterSeparators"]];
	
	if (parseErrors)
		[descDict setObject:parseErrors
			forKey:[NSString stringWithCString:"parseErrors"]];
	else
		[descDict setObject:MISC_EMPTY
			forKey:[NSString stringWithCString:"parseErrors"]];
	
	if (parsedParameters)
		[descDict setObject:parsedParameters
			forKey:[NSString stringWithCString:"parsedParameters"]];
	else
		[descDict setObject:MISC_EMPTY
			forKey:[NSString stringWithCString:"parsedParameters"]];
	
	if (unixStyle)
		[descDict setObject:MISC_YES_STR
			forKey:[NSString stringWithCString:"unixStyle"]];
	else
		[descDict setObject:MISC_NO_STR
			forKey:[NSString stringWithCString:"unixStyle"]];
	if (gnuStyle)
		[descDict setObject:MISC_YES_STR
			forKey:[NSString stringWithCString:"gnuStyle"]];
	else
		[descDict setObject:MISC_NO_STR
			forKey:[NSString stringWithCString:"gnuStyle"]];
	if (multipleCharacterParameters)
		[descDict setObject:MISC_YES_STR
			forKey:[NSString stringWithCString:"multipleCharacterParameters"]];
	else
		[descDict setObject:MISC_NO_STR
			forKey:[NSString stringWithCString:"multipleCharacterParameters"]];
	if (multipleParameterString)
		[descDict setObject:MISC_YES_STR
			forKey:[NSString stringWithCString:"multipleParameterString"]];
	else
		[descDict setObject:MISC_NO_STR
			forKey:[NSString stringWithCString:"multipleParameterString"]];
	if (parameterChecking)
		[descDict setObject:MISC_YES_STR
			forKey:[NSString stringWithCString:"parameterChecking"]];
	else
		[descDict setObject:MISC_NO_STR
			forKey:[NSString stringWithCString:"parameterChecking"]];
	if (helpParsing)
		[descDict setObject:MISC_YES_STR
			forKey:[NSString stringWithCString:"helpParsing"]];
	else
		[descDict setObject:MISC_NO_STR
			forKey:[NSString stringWithCString:"helpParsing"]];
	if (parsed)
		[descDict setObject:MISC_YES_STR
			forKey:[NSString stringWithCString:"parsed"]];
	else
		[descDict setObject:MISC_NO_STR
			forKey:[NSString stringWithCString:"parsed"]];
	if (applicationName)
		[descDict setObject:applicationName
			forKey:[NSString stringWithCString:"applicationName"]];
	else
		[descDict setObject:MISC_EMPTY
			forKey:[NSString stringWithCString:"applicationName"]];
	
	if (delegate)
		[descDict setObject:delegate
			forKey:[NSString stringWithCString:"delegate"]];
	else
		[descDict setObject:MISC_EMPTY
			forKey:[NSString stringWithCString:"delegate"]];
	if (helpHandler)
		[descDict setObject:MISC_UNKNOWN_POINTER
			forKey:[NSString stringWithCString:"helpHandler"]];
	else
		[descDict setObject:MISC_EMPTY
			forKey:[NSString stringWithCString:"helpHandler"]];
	if (helpMethod)
		[descDict setObject:[NSString stringWithCString:sel_getName(helpMethod)]
			forKey:[NSString stringWithCString:"helpMethod"]];
	else
		[descDict setObject:MISC_EMPTY
			forKey:[NSString stringWithCString:"helpMethod"]];
	return [descDict description];
}

- (NSString *)className
{
	return [NSString stringWithFormat:@"%@",MISC_CLASS_NAME];
}

/*************************************************************************
 *** Creating and destroying instances
 *************************************************************************/
- (id)init
{
	return [self initWithArgumentCount:NO_PARAMETERS argumentVector:NULL];
}

- (id)initWithArgumentCount:(const int)argc argumentVector:(const char **)argv
{
	[super init];
	
	argCount = NO_PARAMETERS;
	argVector = NULL;
	
	// Check and Set argv & argc
	if ( (argc > 0) && ![self setArgumentCount:argc argumentVector:argv]) { 
		[self __freeIvars];                                     // Init error
		return nil;
	}

	parameterDictionary = [NSMutableDictionary dictionaryWithCapacity:1];
	[parameterDictionary retain];
	
	// Default settings. NOTE: NEVER CHANGE THE LINES BELOW!
	unixStyle = YES;
	gnuStyle = YES;
	multipleCharacterParameters = YES;
	multipleParameterString = NO;
	parameterChecking = NO;
	helpParsing = YES;
	
	delegate = nil;
	
	return self;
}

- (void)__freeArgVector
{
	int i;
	
	for(i = 0; i < argCount; i++)
		NSZoneFree([self zone],argVector[i]);
	NSZoneFree([self zone],argVector);
}

- (void)__freeIvars
{
	[self __freeArgVector];
	
	// Empty the parameter dictionary
	[parameterDictionary release];

	// Empty all arrays
	[parameterSeparators release];
	[parseErrors release];
	[parsedParameters release];
	
	// release the delegate
	[delegate release];
	
	return;
}

- (void)dealloc
{
	[self __freeIvars];
	return [super dealloc];
}

- (id)initWithCoder:(NSCoder *)aDecoder
{
	int i;
	  
	[aDecoder decodeValuesOfObjCTypes:"S", &argCount];
	argVector = (char **)NSZoneMalloc([self zone],sizeof(char**) * (argCount+1));
	for (i = 0; i < argCount; i++)
		[aDecoder decodeValuesOfObjCTypes:"*", argVector[i]];
	argVector[argCount] = NULL;
	parameterDictionary = [[aDecoder decodeObject] retain];
	parameterSeparators = [[aDecoder decodeObject] retain];
	parseErrors = [[aDecoder decodeObject] retain];
	parsedParameters = [[aDecoder decodeObject] retain];
	[aDecoder decodeValuesOfObjCTypes:"ccccccc", &unixStyle, &gnuStyle, 
		&multipleCharacterParameters, &multipleParameterString, 
		&parameterChecking, &helpParsing, &parsed];
	applicationName = [[aDecoder decodeObject] retain];
	delegate = nil;
	helpHandler = NULL;
	helpMethod = NULL;
	return self;
}

- (void)encodeWithCoder:(NSCoder *)aCoder
{
	int i;
	
	[aCoder encodeValuesOfObjCTypes:"S", &argCount];
	for (i = 0; i < argCount; i++)
		[aCoder encodeValuesOfObjCTypes:"*", argVector[i]];
	[aCoder encodeObject:parameterDictionary];
	[aCoder encodeObject:parameterSeparators];
	[aCoder encodeObject:parseErrors];
	[aCoder encodeObject:parsedParameters];
	[aCoder encodeValuesOfObjCTypes:"ccccccc", &unixStyle, &gnuStyle, 
		&multipleCharacterParameters, &multipleParameterString, 
		&parameterChecking, &helpParsing, &parsed];
	[aCoder encodeObject:applicationName];

	return;
}

/*************************************************************************
 *** Argument vector & count
 *************************************************************************/
- (id)setArgumentCount:(const int)argc argumentVector:(const char **)argv
{
	int i;
	char *cp;
	
	NS_DURING
		if ( (argc <= 0 && argv) || (argc > 0 && !argv) ) {
			[NSException raise:NSInvalidArgumentException
				format:@"Invalid arguments"];
		}
	NS_HANDLER
		NSLog(
			@">>>MiscCommandLineParameterParser$setArgumentCount:argumentVector:\n%@\n%@",
			[localException name], [localException reason]);
		[localException raise];
		return nil;
	NS_ENDHANDLER

	if (argVector)
		[self __freeArgVector];
	argVector = (char **)NSZoneMalloc([self zone],sizeof(char**)*argc);
	for (i = 0; i < argc; i++) {
		cp = (char *)NSZoneMalloc([self zone],strlen(argv[i])+1);
		argVector[i] = cp;
		strcpy(*(argVector+i),argv[i]);
	}
	argVector[argc] = NULL;
	argCount = argc;
	parsed = NO;
	
	return self;
}

- (int)argumentCount
{
	return argCount;
}

- (char **)argumentVector
{
	return argVector;
}

/*************************************************************************
 *** Parameter options
 *************************************************************************/
- (void)setUnixStyleParametersEnabled:(BOOL)flag
{
	unixStyle = flag;
	return;
}

- (BOOL)areUnixStyleParametersEnabled
{
	return unixStyle;
}

- (void)setGNUStyleParametersEnabled:(BOOL)flag
{
	gnuStyle = flag;
	return;
}

- (BOOL)areGNUStyleParametersEnabled
{
	return gnuStyle;
}

- (void)addMiscParameterSeparator:(char *)separator
{
	NSString *sep = nil;
	
	if (separator == NULL)
		return;
		
	if (!parameterSeparators) {
		parameterSeparators = [NSMutableArray arrayWithCapacity:1];
		[parameterSeparators retain];
	}
	sep = [NSString stringWithCString:separator];
	[parameterSeparators addObject:sep];
	
	return;
}

- (void)removeMiscParameterSeparator:(char *)separator
{
	NSEnumerator *enumerator;
	id aSep;
	
	if (parameterSeparators)
		enumerator = [parameterSeparators objectEnumerator];
	else
		return;
		
	while (aSep = [enumerator nextObject]) {
		if (strcmp(separator,[aSep cString]) == 0 ) {
			[parameterSeparators removeObject:aSep];
			return;
		}
	}
	return;
}

- (void)removeAllMiscParameterSeparators
{
	[parameterSeparators removeAllObjects];
	return;
}

- (void)setMulipleCharacterParametersEnabled:(BOOL)flag
{
	multipleCharacterParameters = flag;
	if (multipleCharacterParameters == YES)
		multipleParameterString = NO;
	return;
}

- (BOOL)areMultipleCharacterParametersEnabled
{
	return multipleCharacterParameters;
}

- (void)setMultipleParameterStringEnabled:(BOOL)flag
{
	multipleParameterString = flag;
	if (multipleParameterString == YES)
		multipleCharacterParameters = NO;
	return;
}

- (BOOL)isMultipleParameterStringEnabled
{
	return multipleParameterString;
}

- (void)setParameterCheckingEnabled:(BOOL)flag
{
	parameterChecking = flag;
	return;
}

- (BOOL)isParameterCheckingEnabled
{
	return parameterChecking;
}

- (void)setAutomaticHelpParsingEnabled:(BOOL)flag
{
	helpParsing = flag;
	return;
}

- (BOOL)isAutomaticHelpParsingEnabled
{
	return helpParsing;
}

/*************************************************************************
 *** Maintainig the list of allowed parameters
 *************************************************************************/
- (void)setDelegate:(id)anObject
{
	if (anObject == nil)
		return;
	
	if (delegate)
		[delegate release];
	delegate = [anObject retain];
	
	return;
}

- (id)delegate
{
	return delegate;
}

- (void)addAllowedParameter:(char *)param hasValue:(BOOL)flag
{
	return [self __addAllowedParameter:param hasValue:flag handler:NULL selector:NULL];
}

- (void)addAllowedParameter:(char *)param hasValue:(BOOL)flag
	handler:(Misc_ArgProc)proc
{
	return [self __addAllowedParameter:param hasValue:flag handler:proc selector:NULL];
}

- (void)addAllowedParameter:(char *)param hasValue:(BOOL)flag
	selector:(SEL)method
{
	return [self __addAllowedParameter:param hasValue:flag handler:NULL 
		selector:method];
}

- (void)__addAllowedParameter:(char *)param hasValue:(BOOL)flag
	handler:(Misc_ArgProc)proc selector:(SEL)method
{
	id newParam;
	NSString *key = [NSString stringWithCString:param];
	
	if ((newParam = [parameterDictionary objectForKey:key]) == nil ) {
		newParam = [self __newParameter];
		[parameterDictionary setObject:newParam forKey:key];
	}
	[newParam setAcceptsArgumentsEnabled:flag];
	[newParam setHandler:proc];
	[newParam setSelector:method];
	return;
}

- (void)setHelpHandler:(Misc_ArgProc)proc
{
	helpHandler = proc;
	return;
}

- (void)setHelpSelector:(SEL)method
{
	helpMethod = method;
	return;
}

- (NSArray *)allowedParameters
{
	return [parameterDictionary allKeys];
}

- (void)removeAllowedParameters
{
	[parameterDictionary removeAllObjects];
	return;
}

- (void)parameter:(char *)param conflictsWith:(char *)anotherParam,...
{
	va_list ap;
	char *cp;
	__MiscCommandLineParameter *p;
	
	p = [parameterDictionary objectForKey:[NSString stringWithCString:param]];
	if (!p)
		return;
		
	va_start(ap,anotherParam);
	
	cp = anotherParam;
	do {
		[p setConflictingParameter:cp];
	} while (cp = va_arg(ap,char *));
	
	va_end(ap);

	return;
}

/*************************************************************************
 *** Parsing end error detection
 *************************************************************************/
- (void)__parseAppName
{
	char* buf;
	char* cp;
	
	buf = NSZoneMalloc([self zone],strlen(argVector[0])+1);
	strcpy(buf,argVector[0]);
	cp = strrchr(buf,'/');
	if (!cp) {
		cp = argVector[0];
	}
	else {
		cp++;
	}
	[applicationName autorelease];
	applicationName = [NSString stringWithCString:cp];
	[applicationName retain];
	NSZoneFree([self zone],buf);
	return;
}

- (void)parse
{
	int i = 1;
	__MiscCommandLineParameter *lastParameter;
	
	// Clear all errors
	if (parseErrors)
		[parseErrors removeAllObjects];
	
	[self __prepareSeparators];         // Push unix & gnu separators in the list
	[self __prepareHelpParsing];        // .. for -h or --help
	[self __parseAppName];              // Get the app name
	
	while (i < argCount) {
		// Check if it's a parameter 
		if ([self __isParameter:i]) {
			lastParameter = [self __pushParameter:argVector[i]];
			i++;
			while ((i < argCount) && ![self __isParameter:i]) {
				[lastParameter addValue:[NSString stringWithCString:argVector[i]]];
				i++;
			}
		}
		else {
			[self __addError:ERR_WrongParameter withString:
				[NSString stringWithFormat:@"[%s]",argVector[i]]];
		}
	}
	
	if ( ([parseErrors count] == 0) && [self __noConflictsFound] && 
		[self __noWrongArgumentsFound]) {
		[self __performActions];
	}
	
	parsed = YES;
	
	return;
}

- (NSArray *)parseErrors
{
	return parseErrors;
}

/*************************************************************************
 *** Maintaining & querying the dictionary of parameters
 *************************************************************************/
- (NSString *)applicationName
{
	return applicationName;
}

- (BOOL)isHelpRequired
{
	if (unixStyle && [[parameterDictionary objectForKey:@"h"] isUsed])
		return YES;
	if (gnuStyle && [[parameterDictionary objectForKey:@"help"] isUsed])
		return YES;
	return NO;
}

- (NSEnumerator *)parameterEnumerator
{
	NSEnumerator *enumerator = [parameterDictionary keyEnumerator];
	__MiscCommandLineParameter *value;
	NSString *key;
	
	if (parsedParameters)
		[parsedParameters removeAllObjects];
	else {
		parsedParameters = [NSMutableArray arrayWithCapacity:0];
		[parsedParameters retain];
	}
	
	while (key = [enumerator nextObject]) {
		value = [parameterDictionary objectForKey:key];
		if ([value isUsed]) {
			[parsedParameters addObject:key];
		}
	}	
	
	return [parsedParameters objectEnumerator];
}

- (NSArray *)argumentsForParameter:(NSString *)key
{
	if ([[parameterDictionary objectForKey:key] isUsed]) 
		return [[parameterDictionary objectForKey:key] values];
	else
		return nil;
}

/*************************************************************************
 *** Misc Methods
 *************************************************************************/
- (void)__addError:(ErrorCodes)errNumb withString:(NSString *)comment;

{
	NSMutableString *nS = [NSMutableString 
		stringWithFormat:@"%@",__errorMessages[errNumb]];
	
	if (!parseErrors) {
		parseErrors = [NSMutableArray arrayWithCapacity:0];
		[parseErrors retain];
	}
	
	if (comment)
		[nS appendString:comment];
	
	[parseErrors addObject:nS];
	return;
}

- (void)__prepareSeparators
{
	// Note: The order of the both if statements is IMPORTANT.
	if (gnuStyle)
		[self addMiscParameterSeparator:"--"];
		
	if (unixStyle)
		[self addMiscParameterSeparator:"-"];
	
	return;
}

- (void)__prepareHelpParsing
{
	__MiscCommandLineParameter *param;
	if (helpParsing) {
		param = [self __newParameter];
		[param setHandler:helpHandler];
		[param setSelector:helpMethod];
		if (unixStyle)
			[parameterDictionary setObject:param forKey:
				[NSString stringWithCString:"h"]];
		if (gnuStyle)
			[parameterDictionary setObject:param forKey:
				[NSString stringWithCString:"help"]];
	}
	return;
}

- (BOOL)__isParameter:(int)paramNumb
{
	NSEnumerator *enumerator = [parameterSeparators objectEnumerator];
	id aSep;
	char* cp;
	char arg[100];
	
	// Check for obvious case ;-)
	if ([parameterSeparators count] == 0)
		return YES;                    // Everything is a parameter
	
	strcpy(arg,argVector[paramNumb]);
		
	while (aSep = [enumerator nextObject]) {
		cp = strstr(arg,[aSep cString]);
		if (cp)
			return YES;
	}
	return NO;
}

- (__MiscCommandLineParameter *)__pushParameter:(char *)param
{
	__MiscCommandLineParameter *newParameter = nil;
	NSEnumerator *enumerator = [parameterSeparators objectEnumerator];
	id aSep;
	char* cp = NULL;
	BOOL done = NO;
	char scp[2] = {'\000','\000'};
	
	while (!done && (aSep = [enumerator nextObject])) {
		cp = strstr(param,[aSep cString]);
		if (cp) {
			cp += strlen([aSep cString]);
			done = YES;
		}
	}

	if (parameterChecking) {
		if (multipleCharacterParameters) {
			if (newParameter = [parameterDictionary objectForKey:
				[NSString stringWithCString:cp]]) {
				[newParameter useIt];
			}
			else {
				[self __addError:ERR_UnknownParameter withString:
					[NSString stringWithFormat:@"[%s]",cp]];
			}
		}
		else if (multipleParameterString) {
			do {
				scp[0] = *cp;
				if (newParameter = [parameterDictionary objectForKey:
					[NSString stringWithCString:cp]]) {
					[newParameter useIt];
				}
				else {
					[self __addError:ERR_UnknownParameter withString:
						[NSString stringWithFormat:@"[%s]",cp]];
				}
				[newParameter useIt];
				cp++;
			} while (*cp);
		}
		else {
			[self __addError:ERR_MultipleParameters withString:
				[NSString stringWithFormat:@"[%s]",cp]];
		}
	}
	else {
		if (multipleCharacterParameters) {
			newParameter = [self __newParameter];
			[parameterDictionary setObject:newParameter forKey:
				[NSString stringWithCString:cp]];
			[newParameter useIt];
		}
		else if (multipleParameterString) {
			do {
				scp[0] = *cp;
				newParameter = [self __newParameter];
				[parameterDictionary setObject:newParameter forKey:
					[NSString stringWithCString:scp]];
				[newParameter useIt];
				cp++;
			} while (*cp);
		}
		else {
			[self __addError:ERR_MultipleParameters withString:
				[NSString stringWithFormat:@"[%s]",param]];
		}
	}
	
	return newParameter;
}

- (__MiscCommandLineParameter *)__newParameter
{
	__MiscCommandLineParameter *newParameter;

	newParameter = [[[__MiscCommandLineParameter allocWithZone:[self zone]] init]
		autorelease];

	return newParameter;
}

- (BOOL)__noConflictsFound
{
	NSEnumerator *enumerator = [parameterDictionary keyEnumerator];
	NSEnumerator *confEnumerator;
	__MiscCommandLineParameter *value;
	NSString *key;
	NSArray *conflicts;
	NSString *confParam;
	
	while (key = [enumerator nextObject]) {
		value = [parameterDictionary objectForKey:key];
		conflicts = [value conflictingParameters];
		if ( [value isUsed] && ([conflicts count] > 0) ) {
			confEnumerator = [conflicts objectEnumerator];
			while (confParam = [confEnumerator nextObject]) {
				if ([[parameterDictionary objectForKey:confParam] isUsed])
					[self __addError:ERR_ArgumentNotAllowed withString:
						[NSString stringWithFormat:@"[%@ and %@]",key,confParam]];
					return NO;
			}
		}
	}	
	return YES;
}

- (BOOL)__noWrongArgumentsFound
{
	NSEnumerator *enumerator = [parameterDictionary keyEnumerator];
	__MiscCommandLineParameter *value;
	NSString *key;
	
	while (key = [enumerator nextObject]) {
		value = [parameterDictionary objectForKey:key];
		if ( [value isUsed] && (![value isAcceptsArgumentsEnabled]) && 
			([[value values] count] > 0) ) {
			[self __addError:ERR_ArgumentNotAllowed withString:key];
			return NO;
		}
	}	
	
	return YES;
}

- (void)__performActions
{
	NSEnumerator *enumerator = [parameterDictionary keyEnumerator];
	__MiscCommandLineParameter *value;
	NSString *key;
	
	while (key = [enumerator nextObject]) {
		value = [parameterDictionary objectForKey:key];
		if ([value isUsed]) {
			[value setDelegate:delegate];
			[value performAction];
		}
	}	
	return;
}

@end