mirror of
https://github.com/gnustep/libs-ec.git
synced 2025-05-30 17:00:49 +00:00
make command line argument handling and documentation easier
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/ec/trunk@38148 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
parent
27911d0938
commit
6db4067e56
3 changed files with 369 additions and 40 deletions
|
@ -1,3 +1,11 @@
|
|||
2014-11-02 Richard Frith-Macdonald <rfm@gnu.org>
|
||||
|
||||
* EcProcess.h:
|
||||
* EcProcess.m:
|
||||
Add method to register a user default / configuration key to have
|
||||
updates for a default automatically trigger a method to handle it,
|
||||
and to provide 'help' documentation for command line arguments.
|
||||
|
||||
2014-11-01 Richard Frith-Macdonald <rfm@gnu.org>
|
||||
|
||||
* EcProcess.m: Check for descriptor leaks at 1 minute intervals,
|
||||
|
|
85
EcProcess.h
85
EcProcess.h
|
@ -430,6 +430,31 @@ extern NSString* cmdVersion(NSString *ver);
|
|||
*/
|
||||
+ (NSMutableDictionary*) ecInitialDefaults;
|
||||
|
||||
/** Registers an NSUserDefaults key that the receiver understands.<br />
|
||||
* This is primarily intended for user defaults which can reasonably
|
||||
* be supplied at the command line when a process is started (and for
|
||||
* which the process should therefore supply help information)<br />
|
||||
* The type text must be a a short string saying what kind of value
|
||||
* must be provided (eg 'YES/NO') for the default, or nil if no help
|
||||
* is to be provided for the default.<br />
|
||||
* The help text should be a description of what the default does,
|
||||
* or nil if no help is to be provided for the default.<br />
|
||||
* The action may either be NULL or a selector for a message to be sent
|
||||
* to the EcProc instance with a single argument (the new default value)
|
||||
* when the value of the user default changes.<br />
|
||||
* If the same default name is registered more than once, the values
|
||||
* from the last registration are used, except for the case where the
|
||||
* cmd argument is NULL, in that case the previous selector is kept
|
||||
* in the new rfegistration.<br />
|
||||
* This method should be called in your +initialize method, so that all
|
||||
* supported defaults are already registered by the time your process
|
||||
* tries to respond to being started with a --help command line argument.
|
||||
*/
|
||||
+ (void) ecRegisterDefault: (NSString*)name
|
||||
withTypeText: (NSString*)type
|
||||
andHelpText: (NSString*)help
|
||||
action: (SEL)cmd;
|
||||
|
||||
/** Convenience method to create the singleton EcProcess instance
|
||||
* using the initial configuration provided by the +ecInitialDefaults
|
||||
* method.<br />
|
||||
|
@ -592,18 +617,17 @@ extern NSString* cmdVersion(NSString *ver);
|
|||
- (void) cmdDebug: (NSString*)fmt, ... NS_FORMAT_FUNCTION(1,2);
|
||||
|
||||
/** Called whenever the user defaults are updated (which may be due to a
|
||||
* central configuration in additions to other defaults system changes).<br />
|
||||
* central configuration in additions to local defaults system changes).<br />
|
||||
* This is automatically called by -cmdUpdate: (even if the user defaults
|
||||
* database has not actually changed), in this case the notification
|
||||
* argument is nil.<br />
|
||||
* If you override this to handle configuration changes, don't forget
|
||||
* database has not actually changed, in this case the notification
|
||||
* argument is nil).<br />
|
||||
* This method deals with the updates for any defaults registered using
|
||||
* the +ecRegisterDefault:withTypeText:andHelpText:action: method, so
|
||||
* if you override this to handle configuration changes, don't forget
|
||||
* to call the superclass implementation.<br />
|
||||
* This method is provided to allow subclasses to control the order
|
||||
* in which defaults changes are handled by them and their superclasses.<br />
|
||||
* Generally, this method is for use handling changes in the local
|
||||
* NSUserDefaults database; to handle explict configuration changes from
|
||||
* the central configuration in the Control server, you should usually
|
||||
* override the -cmdUpdated method instead.
|
||||
* If you wish to manage updates from the central database in a specific
|
||||
* order, you may wish to override the -cmdUpdate: and -cmdUpdated methods
|
||||
* directly.
|
||||
*/
|
||||
- (void) cmdDefaultsChanged: (NSNotification*)n;
|
||||
|
||||
|
@ -700,7 +724,7 @@ extern NSString* cmdVersion(NSString *ver);
|
|||
*/
|
||||
- (int) cmdSignalled;
|
||||
|
||||
/** Used to tell your application about configuration changes.<br />
|
||||
/** Used to tell your application about central configuration changes.<br />
|
||||
* This is called before the NSUserDefaults system is updated with the
|
||||
* changes, so you may use it to update internal state in the knowledge
|
||||
* that code watching for user defaults change notifications will not
|
||||
|
@ -708,30 +732,38 @@ extern NSString* cmdVersion(NSString *ver);
|
|||
* The base class implementation is responsible for updating the user
|
||||
* defaults system ... so be sure that your implementation calls the
|
||||
* superclass implementation (unless you wish to suppress the configuration
|
||||
* update).<br />
|
||||
* update) after performing any pre-update operations.<br />
|
||||
* You may alter the info dictionary prior to passing it to the superclass
|
||||
* implementation if you wish to adjust the new configuration before it
|
||||
* takes effect.<br />
|
||||
* The order of execution of a configuration update is therefore as follows:
|
||||
* <list>
|
||||
* <item>Any subclass implementation of -cmdUpdate: is entered.
|
||||
* </item>
|
||||
* <item>The base implementation of -cmdUpdate: is entered, the stored
|
||||
* configuration is changed as necessary, the user defaults database is
|
||||
* updated.
|
||||
* </item>
|
||||
* <item>The -cmdDefaultsChanged: method is called (either as a result of
|
||||
* a user defaults update, or directly by the base -cmdUpdate: method.
|
||||
* <item>Any subclass implementation of -cmdUpdate: is entered.
|
||||
* </item>
|
||||
* <item>The base implementation of the -cmdDefaults: method ends.
|
||||
* <item>The base implementation of -cmdUpdate: is entered, the stored
|
||||
* configuration is changed as necessary, the user defaults database is
|
||||
* updated.
|
||||
* </item>
|
||||
* <item>Any subclass implementation of the -cmdDefaults: method ends.
|
||||
* <item>Any subclass of the -cmdDefaultsChanged: method is entere
|
||||
* (either as a result of the user defaults update,
|
||||
* or directly by the base -cmdUpdate: method).
|
||||
* </item>
|
||||
* <item>The base implementation of the -cmdDefaultsChanged: method is
|
||||
* entered, and any messages registered using the
|
||||
* +ecRegisterDefault:withTypeText:andHelpText:action: method are
|
||||
* sent if the corresponding default value has changed.
|
||||
* </item>
|
||||
* <item>The base implementation of the -cmdDefaultsChanged: method ends.
|
||||
* </item>
|
||||
* <item>Any subclass implementation of the -cmdDefaultsChanged: method ends.
|
||||
* </item>
|
||||
* <item>The -cmdUpdated method is called.
|
||||
* </item>
|
||||
* </list>
|
||||
* You should usually override the -cmdUpdated method to handle configuration
|
||||
* changes, using this method only when you want to check/override changes
|
||||
* You should usually either register your own methods to handle changes
|
||||
* to particular defaults values, or override the -cmdDefaultsChanged:
|
||||
* method to handle general configuration changes.<br />
|
||||
* Use this method only when you want to check/override changes
|
||||
* before they take effect.
|
||||
*/
|
||||
- (void) cmdUpdate: (NSMutableDictionary*)info;
|
||||
|
@ -755,6 +787,9 @@ extern NSString* cmdVersion(NSString *ver);
|
|||
* calls the superclass implementation, and if that returns a non-nil
|
||||
* result, you should pass that on as the return value from your own
|
||||
* implementation.
|
||||
* Use this method only for handling config changes which must be done
|
||||
* after any code which is watching NSUserDefaultsDidChangNotification
|
||||
* has run, or for situations where a config error may be fatal.
|
||||
*/
|
||||
- (NSString*) cmdUpdated;
|
||||
|
||||
|
@ -867,7 +902,7 @@ extern NSString* cmdVersion(NSString *ver);
|
|||
* If 'EcHomeDirectory' is not present in the defaults system (or is
|
||||
* an empty string) then no directory change is done.<br />
|
||||
* Please note, that the base implementation of this method may
|
||||
* cause other methods (eg -cmdUpdated and -cmdDefaultsChaned) to be called,
|
||||
* cause other methods (eg -cmdUpdated and -cmdDefaultsChanged:) to be called,
|
||||
* so you must take care that when you override those methods, your own
|
||||
* implementations do not depend on initialisation having completed.
|
||||
* It's therefore recommended that you use 'lazy' initialisation of subclass
|
||||
|
|
316
EcProcess.m
316
EcProcess.m
|
@ -98,6 +98,23 @@
|
|||
#define EC_EFFECTIVE_USER nil
|
||||
#endif
|
||||
|
||||
|
||||
@interface EcDefaultRegistration : NSObject
|
||||
{
|
||||
NSString *name; // The name/key of the default (without prefix)
|
||||
NSString *type; // The type text for the default
|
||||
NSString *help; // The help text for the default
|
||||
SEL cmd; // method to update when default values change
|
||||
id obj; // The latest value of the default
|
||||
}
|
||||
+ (void) defaultsChanged: (NSUserDefaults*)defs;
|
||||
+ (void) registerDefault: (NSString*)name
|
||||
withTypeText: (NSString*)type
|
||||
andHelpText: (NSString*)help
|
||||
action: (SEL)cmd;
|
||||
+ (void) showHelp;
|
||||
@end
|
||||
|
||||
/* Lock for controlling access to per-process singleton instance.
|
||||
*/
|
||||
static NSRecursiveLock *ecLock = nil;
|
||||
|
@ -958,6 +975,12 @@ findMode(NSDictionary* d, NSString* s)
|
|||
|
||||
@end
|
||||
|
||||
@interface EcProcess (Defaults)
|
||||
- (void) _defMemory: (id)val;
|
||||
- (void) _defRelease: (id)val;
|
||||
- (void) _defTesting: (id)val;
|
||||
@end
|
||||
|
||||
@interface EcProcess (Private)
|
||||
- (void) cmdMesgrelease: (NSArray*)msg;
|
||||
- (void) cmdMesgtesting: (NSArray*)msg;
|
||||
|
@ -1030,6 +1053,17 @@ findMode(NSDictionary* d, NSString* s)
|
|||
count: 2];
|
||||
}
|
||||
|
||||
+ (void) ecRegisterDefault: (NSString*)name
|
||||
withTypeText: (NSString*)type
|
||||
andHelpText: (NSString*)help
|
||||
action: (SEL)cmd
|
||||
{
|
||||
[EcDefaultRegistration registerDefault: name
|
||||
withTypeText: type
|
||||
andHelpText: help
|
||||
action: cmd];
|
||||
}
|
||||
|
||||
+ (void) ecSetup
|
||||
{
|
||||
if (nil != EcProc)
|
||||
|
@ -1171,6 +1205,8 @@ static NSString *noFiles = @"No log files to archive";
|
|||
NSString *str;
|
||||
int i;
|
||||
|
||||
[EcDefaultRegistration defaultsChanged: cmdDefs];
|
||||
|
||||
enumerator = [cmdDebugKnown keyEnumerator];
|
||||
while (nil != (mode = [enumerator nextObject]))
|
||||
{
|
||||
|
@ -1195,10 +1231,6 @@ static NSString *noFiles = @"No log files to archive";
|
|||
[ecLock unlock];
|
||||
}
|
||||
|
||||
GSDebugAllocationActive([cmdDefs boolForKey: @"Memory"]);
|
||||
[NSObject enableDoubleReleaseCheck: [cmdDefs boolForKey: @"Release"]];
|
||||
cmdFlagTesting = [cmdDefs boolForKey: @"Testing"];
|
||||
|
||||
if ((str = [cmdDefs stringForKey: @"CmdInterval"]) != nil)
|
||||
{
|
||||
[self setCmdInterval: [str floatValue]];
|
||||
|
@ -1641,6 +1673,18 @@ NSLog(@"Ignored attempt to set timer interval to %g ... using 10.0", interval);
|
|||
|
||||
[cmdDebugModes addObject: cmdDefaultDbg];
|
||||
|
||||
[self ecRegisterDefault: @"Memory"
|
||||
withTypeText: @"YES/NO"
|
||||
andHelpText: @"Enable memory allocation checks"
|
||||
action: @selector(_defMemory:)];
|
||||
[self ecRegisterDefault: @"Release"
|
||||
withTypeText: @"YES/NO"
|
||||
andHelpText: @"Turn on double release checks (debug)"
|
||||
action: @selector(_defRelease:)];
|
||||
[self ecRegisterDefault: @"Testing"
|
||||
withTypeText: @"YES/NO"
|
||||
andHelpText: @"Run in test mode (if supported)"
|
||||
action: @selector(_defTesting:)];
|
||||
/*
|
||||
* Set the timeouts for the default connection so that
|
||||
* they will be inherited by other connections.
|
||||
|
@ -3479,6 +3523,7 @@ NSLog(@"Ignored attempt to set timer interval to %g ... using 10.0", interval);
|
|||
else
|
||||
{
|
||||
NSProcessInfo *pinfo;
|
||||
NSArray *args;
|
||||
NSFileManager *mgr;
|
||||
NSEnumerator *enumerator;
|
||||
NSString *str;
|
||||
|
@ -3492,6 +3537,7 @@ NSLog(@"Ignored attempt to set timer interval to %g ... using 10.0", interval);
|
|||
started = RETAIN([dateClass date]);
|
||||
|
||||
pinfo = [NSProcessInfo processInfo];
|
||||
args = [pinfo arguments];
|
||||
mgr = [NSFileManager defaultManager];
|
||||
prf = EC_DEFAULTS_PREFIX;
|
||||
if (nil == prf)
|
||||
|
@ -3507,28 +3553,69 @@ NSLog(@"Ignored attempt to set timer interval to %g ... using 10.0", interval);
|
|||
[cmdDefs registerDefaults: defs];
|
||||
}
|
||||
|
||||
if ([[pinfo arguments] containsObject: @"--help"])
|
||||
if ([args containsObject: @"--help"] || [args containsObject: @"-H"])
|
||||
{
|
||||
NSLog(@"Standard command-line arguments ...\n\n"
|
||||
@"-%@CommandHost [aHost] Host of command server to use.\n"
|
||||
@"-%@CommandName [aName] Name of command server to use.\n"
|
||||
@"-%@Daemon [YES/NO] Fork process to run in background?\n"
|
||||
@"-%@EffectiveUser [aName] User to run as\n"
|
||||
GSPrintf(stderr, @"Standard command-line arguments ...\n\n");
|
||||
|
||||
if ([self isKindOfClass: NSClassFromString(@"EcControl")])
|
||||
{
|
||||
GSPrintf(stderr,
|
||||
@"-%@Daemon NO Run process in the foreground.\n",
|
||||
prf);
|
||||
}
|
||||
else if ([self isKindOfClass: NSClassFromString(@"EcConsole")])
|
||||
{
|
||||
GSPrintf(stderr,
|
||||
@"-%@ControlHost [aHost] Host of the Control server to use.\n"
|
||||
@"-%@ControlName [aName] Name of the Control server to use.\n"
|
||||
@"-%@Daemon [YES/NO] Fork process to run in background?\n",
|
||||
prf, prf, prf);
|
||||
}
|
||||
else if ([self isKindOfClass: NSClassFromString(@"EcCommand")])
|
||||
{
|
||||
GSPrintf(stderr,
|
||||
@"-%@ControlHost [aHost] Host of the Control server to use.\n"
|
||||
@"-%@ControlName [aName] Name of the Control server to use.\n"
|
||||
@"-%@Daemon NO Run process in in the foreground.\n",
|
||||
prf, prf, prf);
|
||||
}
|
||||
else
|
||||
{
|
||||
GSPrintf(stderr,
|
||||
@"-%@CommandHost [aHost] Host of the Command server to use.\n"
|
||||
@"-%@CommandName [aName] Name of the Command server to use.\n"
|
||||
@"-%@Daemon [YES/NO] Fork process to run in background?\n",
|
||||
@"-%@Transient [YES/NO] Expect this process be short-lived?\n",
|
||||
prf, prf, prf, prf);
|
||||
}
|
||||
|
||||
GSPrintf(stderr, @"\n");
|
||||
GSPrintf(stderr,
|
||||
@"-%@CoreSize [MB] Maximum core dump size\n"
|
||||
@" 0 = no dumps, -1 = unlimited\n"
|
||||
@"-%@DescriptorsMaximum [N]\n"
|
||||
@" Set maximum file descriptors to use\n"
|
||||
@"-%@Debug-name [YES/NO] Turn on/off the named type of debug\n"
|
||||
@"-%@EffectiveUser [aName] User to run this process as\n"
|
||||
@"-%@HomeDirectory [relDir] Relative home within user directory\n"
|
||||
@"-%@UserDirectory [dir] Override home directory for user\n"
|
||||
@"-%@Instance [aNumber] Instance number for multiple copies\n"
|
||||
@"-%@Memory [YES/NO] Enable memory allocation checks?\n"
|
||||
@"-%@MemoryAllowed [MB] Expected memory usage (before alerts)\n"
|
||||
@"-%@MemoryIncrement [KB] Absolute increase in alert threshold\n"
|
||||
@"-%@MemoryMaximum [MB] Maximum memory usage (before restart)\n"
|
||||
@"-%@MemoryPercentage [N] Percent increase in alert threshold\n"
|
||||
@"-%@ProgramName [aName] Name to use for this program\n"
|
||||
@"-%@Testing [YES/NO] Run in test mode (if supported)\n"
|
||||
@"\n--version to get version information and quit\n\n",
|
||||
prf, prf, prf, prf, prf, prf, prf, prf, prf, prf
|
||||
);
|
||||
prf, prf, prf, prf, prf, prf, prf, prf, prf, prf, prf, prf);
|
||||
|
||||
[EcDefaultRegistration showHelp];
|
||||
|
||||
RELEASE(self);
|
||||
[ecLock unlock];
|
||||
return nil;
|
||||
}
|
||||
|
||||
if ([[pinfo arguments] containsObject: @"--version"])
|
||||
if ([args containsObject: @"--version"])
|
||||
{
|
||||
NSLog(@"%@ %@", [self ecCopyright], cmdVersion(nil));
|
||||
RELEASE(self);
|
||||
|
@ -4519,3 +4606,202 @@ NSLog(@"Ignored attempt to set timer interval to %g ... using 10.0", interval);
|
|||
|
||||
@end
|
||||
|
||||
@implementation EcProcess (Defaults)
|
||||
- (void) _defMemory: (id)val
|
||||
{
|
||||
GSDebugAllocationActive([val boolValue]);
|
||||
}
|
||||
- (void) _defRelease: (id)val
|
||||
{
|
||||
[NSObject enableDoubleReleaseCheck: [val boolValue]];
|
||||
}
|
||||
- (void) _defTesting: (id)val
|
||||
{
|
||||
cmdFlagTesting = [val boolValue];
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation EcDefaultRegistration
|
||||
|
||||
static NSMutableDictionary *regDefs = nil;
|
||||
|
||||
+ (void) defaultsChanged: (NSUserDefaults*)defs
|
||||
{
|
||||
NSEnumerator *e;
|
||||
NSString *n;
|
||||
|
||||
[ecLock lock];
|
||||
e = [[regDefs allKeys] objectEnumerator];
|
||||
[ecLock unlock];
|
||||
while (nil != (n = [e nextObject]))
|
||||
{
|
||||
EcDefaultRegistration *d;
|
||||
id o = nil;
|
||||
SEL c = NULL;
|
||||
|
||||
[ecLock lock];
|
||||
d = [regDefs objectForKey: n];
|
||||
if (nil != d)
|
||||
{
|
||||
o = [defs objectForKey: n];
|
||||
if (o != d->obj && NO == [o isEqual: d->obj])
|
||||
{
|
||||
ASSIGNCOPY(d->obj, o);
|
||||
o = d->obj;
|
||||
c = d->cmd;
|
||||
}
|
||||
}
|
||||
[ecLock unlock];
|
||||
if (NULL != c && [EcProc respondsToSelector: c])
|
||||
{
|
||||
[EcProc performSelector: c withObject: o];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+ (void) initialize
|
||||
{
|
||||
regDefs = [NSMutableDictionary new];
|
||||
}
|
||||
|
||||
+ (void) registerDefault: (NSString*)name
|
||||
withTypeText: (NSString*)type
|
||||
andHelpText: (NSString*)help
|
||||
action: (SEL)cmd
|
||||
{
|
||||
static NSCharacterSet *w = nil;
|
||||
EcDefaultRegistration *d;
|
||||
|
||||
if (nil == w)
|
||||
{
|
||||
w = RETAIN([NSCharacterSet whitespaceAndNewlineCharacterSet]);
|
||||
}
|
||||
if ([type length] > 0)
|
||||
{
|
||||
type = [type stringByTrimmingSpaces];
|
||||
if ([type length] == 0)
|
||||
{
|
||||
type = nil;
|
||||
}
|
||||
else
|
||||
{
|
||||
NSUInteger length = [type length];
|
||||
NSMutableString *m = nil;
|
||||
|
||||
while (length-- > 0)
|
||||
{
|
||||
unichar u = [type characterAtIndex: length];
|
||||
|
||||
if (u != ' ' && [w characterIsMember: u])
|
||||
{
|
||||
if (nil == m)
|
||||
{
|
||||
m = AUTORELEASE([type mutableCopy]);
|
||||
type = m;
|
||||
}
|
||||
[m replaceCharactersInRange: NSMakeRange(length, 1)
|
||||
withString: @" "];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ([help length] > 0)
|
||||
{
|
||||
help = [help stringByTrimmingSpaces];
|
||||
if ([help length] == 0)
|
||||
{
|
||||
help = nil;
|
||||
}
|
||||
}
|
||||
|
||||
[ecLock lock];
|
||||
d = [regDefs objectForKey: name];
|
||||
if (nil == d)
|
||||
{
|
||||
d = [EcDefaultRegistration new];
|
||||
ASSIGNCOPY(d->name, name);
|
||||
[regDefs setObject: d forKey: d->name];
|
||||
RELEASE(d);
|
||||
}
|
||||
ASSIGNCOPY(d->type, type);
|
||||
ASSIGNCOPY(d->help, help);
|
||||
if (0 != cmd)
|
||||
{
|
||||
d->cmd = cmd;
|
||||
}
|
||||
[ecLock unlock];
|
||||
}
|
||||
|
||||
+ (void) showHelp
|
||||
{
|
||||
NSArray *keys;
|
||||
NSString *prf;
|
||||
NSEnumerator *e;
|
||||
NSString *k;
|
||||
NSUInteger max = 0;
|
||||
|
||||
prf = EC_DEFAULTS_PREFIX;
|
||||
if (nil == prf)
|
||||
{
|
||||
prf = @"";
|
||||
}
|
||||
|
||||
keys = [regDefs allKeys];
|
||||
e = [keys objectEnumerator];
|
||||
while (nil != (k = [e nextObject]))
|
||||
{
|
||||
EcDefaultRegistration *d = [regDefs objectForKey: k];
|
||||
|
||||
if (nil != d->type && nil != d->help)
|
||||
{
|
||||
NSUInteger length = [prf length] + 5;
|
||||
|
||||
length += [k length] + [d->type length];
|
||||
if (length > max)
|
||||
{
|
||||
max = length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
keys = [keys sortedArrayUsingSelector: @selector(compare:)];
|
||||
e = [keys objectEnumerator];
|
||||
while (nil != (k = [e nextObject]))
|
||||
{
|
||||
EcDefaultRegistration *d = [regDefs objectForKey: k];
|
||||
|
||||
if (nil != d->type && nil != d->help)
|
||||
{
|
||||
/* If the help text is short enough, put it all on one line.
|
||||
*/
|
||||
if ([d->help length] + max < 80)
|
||||
{
|
||||
NSMutableString *m;
|
||||
|
||||
m = [NSMutableString stringWithFormat: @"-%@%@ [%@] ",
|
||||
prf, k, d->type];
|
||||
while ([m length] < max)
|
||||
{
|
||||
[m appendString: @" "];
|
||||
}
|
||||
GSPrintf(stderr, @"%@%@\n", m, d->help);
|
||||
}
|
||||
else
|
||||
{
|
||||
GSPrintf(stderr, @"-%@%@ [%@]\n %@\n", prf, k, d->type, d->help);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void) dealloc
|
||||
{
|
||||
RELEASE(name);
|
||||
RELEASE(type);
|
||||
RELEASE(help);
|
||||
RELEASE(obj);
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue