home *** CD-ROM | disk | FTP | other *** search
- // -------------------------------------------------------------------------------------
- // ReadConfig.m
- // Martin D. Flynn, NeXT Computer, Inc.
- // Read config file and send methods to target
- // Note: See the header file for usage information and examples.
- // -------------------------------------------------------------------------------------
- // Permission is granted to freely redistribute this source code, and to use fragments
- // of this code in your own applications if you find them to be useful. This class,
- // along with the source code, come with no warranty of any kind, and the user assumes
- // all responsibility for its use.
- // -------------------------------------------------------------------------------------
-
- #import <stdlib.h>
- #import <stdio.h>
- #import <string.h>
- #import <libc.h>
- #import <sys/time.h>
- #import <objc/objc-runtime.h>
- #import <objc/Storage.h>
- #import <defaults/defaults.h>
- #import <appkit/Panel.h>
- #import <appkit/Application.h>
- #import "ReadConfig.h"
-
- // -------------------------------------------------------------------------------------
- extern char *getwd(char *path);
-
- // -------------------------------------------------------------------------------------
- // misc defines
- #define maxLEN 512 // max allowable record length
- #define prefixMethodNAME "*cfg" // internal method prefix "cfg_<methName>"
- #define isCOMMENT(T) ((*T == ' ') || (*T == '\t') || (*T == '\n') || (*T == '/'))
- #define parseFORMAT "%s%[^\n]"
-
- // -------------------------------------------------------------------------------------
- // internal config private method definition
- @interface ReadConfig(_Private)
- - profileElapsedTime:(float)elapsedTime;
- - configErrorTrap:(int)errCode inFile:(char*)fileName atLine:(int)lineNbr;
- - configWillPerform:(SEL)selector with:anArg;
- - setTarget:targetObject;
- - (int)readConfigFile:(char*)file;
- @end
-
- // -------------------------------------------------------------------------------------
- // internal config private method definition
- @interface ReadConfig(_InternalMethod)
- - (int)cfg_globalMethodTrace:(BOOL)yesNo;
- - (int)cfg_globalPrintError:(BOOL)yesNo;
- - (int)cfg_globalCheckReturn:(BOOL)yesNo;
- - (int)cfg_methodTrace:(BOOL)yesNo;
- - (int)cfg_printError:(BOOL)yesNo;
- - (int)cfg_checkReturn:(BOOL)yesNo;
- - (int)cfg_terminateOnError:(BOOL)yesNo;
- - (int)cfg_print:(char*)text;
- - (int)cfg_goto:(char*)label;
- - (int)cfg_ifTrue:(char*)label;
- - (int)cfg_ifFalse:(char*)label;
- - (int)cfg_terminate;
- - (int)cfg_alertPanel:(char*)message;
- - (int)cfg_abortPanel:(char*)message;
- - (int)cfg_optionPanel:(char*)message;
- - (int)cfg_ifOptionA:(char*)label;
- - (int)cfg_ifOptionB:(char*)label;
- - (int)cfg_endModalPanel;
- - (int)cfg_infoPanel:(char*)message;
- - (int)cfg_endInfoPanel;
- - (int)cfg_stopPanel:(char*)message;
- - (int)cfg_endStopPanel;
- - (int)cfg_ifStop:(char*)label;
- @end
-
- // -------------------------------------------------------------------------------------
- @implementation ReadConfig
-
- // -------------------------------------------------------------------------------------
- // global defaults
- static BOOL dftExitOnError = YES;
- static BOOL dftPrintError = YES;
- static BOOL dftMethodTrace = NO;
- static BOOL dftCheckAll = YES;
- static BOOL dftBackupOnWrite = NO;
-
- // -------------------------------------------------------------------------------------
- // line/level data on last call to ReadConfig
- static int globalLevel = 0;
- static int globalLine = 0;
-
- // -------------------------------------------------------------------------------------
- // global error trap
- static id errorTrap = (id)nil; // error trap target
-
- // -------------------------------------------------------------------------------------
- // time lapse profile variables
- static double profileStart = -1.0; // profile start time
- static int profileCount = 0; // profile 'ping' count
- static float profileLapse = 0.0; // latched profile lapse time
- static BOOL profileActive = NO;
- static id profileTarget = (id)nil;
- #define profilePING @selector(profileElapsedTime:)
-
- // -------------------------------------------------------------------------------------
- // constant data
- static char *errorDesc[] = { // error descriptions
- "Successful",
- "Unable to open config file",
- "Invalid number of configuration parameters",
- "Selector not found for method",
- "Method returned TRUE error condition",
- "Goto label not found",
- "Invalid error code specified",
- "Target does not respond to method",
- 0
- };
-
- // -------------------------------------------------------------------------------------
- // class initialization
- + initialize
- {
- errorTrap = self;
- return self;
- }
-
- // -------------------------------------------------------------------------------------
- // Return the full path to the current application
- // Note: the application name is removed from the path
- + (char*)appPath
- {
- int i;
- static char *appPathName = (char*)nil;
- if (!appPathName) {
- for (i = strlen(NXArgv[0]) - 1; (i >= 0) && (NXArgv[0][i] != '/'); i--);
- appPathName = (char*)malloc(i + 2);
- strncpy(appPathName, NXArgv[0], i + 1);
- appPathName[i + 1] = 0;
- if (NXArgv[0][0] != '/') {
- char path[1024], *aPtr;
- aPtr = (char*)malloc(strlen(getwd(path)) + strlen(appPathName) + 2);
- sprintf(aPtr, "%s/%s", path, appPathName);
- free(appPathName);
- appPathName = aPtr;
- }
- }
- return appPathName;
- }
-
- // -------------------------------------------------------------------------------------
- // Build a fully qualified path to the specified file name
-
- /* build full path to specified file name and extension */
- + (char*)configPath:(const char*)fileName
- {
- char *fullPath = (char*)malloc(strlen([self appPath]) + strlen(fileName) + 1);
- sprintf(fullPath, "%s%s", [self appPath], fileName);
- return fullPath;
- }
-
- // -------------------------------------------------------------------------------------
- // Return status info
- // Note:
- // - These functions may be used to get information about configuration file reads
- // when an error condition has occurred.
-
- /* return current line number (usually used in error conditions) */
- + (int)lineNumber
- {
- return globalLine;
- }
-
- /* return current level number (usually used in error conditions) */
- + (int)levelNumber
- {
- return globalLevel;
- }
-
- /* return error description text for specified error code */
- + (char*)errorDescription:(int)errorCode
- {
- if ((errorCode < 0) || (errorCode > cfgLAST_ERROR)) errorCode = cfgINVALID_CODE;
- return errorDesc[errorCode];
- }
-
- // -------------------------------------------------------------------------------------
- // Set global error trap target object
- // Note:
- // - The 'setErrorTrap:' may be used to set the target object that will be notified
- // when error conditions occur during a readConfigFile: call.
- // - The method that will be called in the target object will be the following:
- // configErrorTrap:(int) inFile:(char*) atLine:(int)
- // See definition below for more information.
-
- /* set trap to specified target */
- + setErrorTrap:trapId
- {
- if (![trapId respondsTo:@selector(configErrorTrap:inFile:atLine:)])
- return (id)nil;
- errorTrap = trapId;
- return self;
- }
-
- /* example error trap method */
- // errCode - the error condition that occurred
- // fileName - the current configuration file name that had the error
- // lineNbr - the line number that caused the error
- + configErrorTrap:(int)errCode inFile:(char*)fileName atLine:(int)lineNbr
- {
-
- char *temp = fileName? rindex(fileName, '/') : (char*)nil, *fName = temp? temp : fileName;
- char errMsg[256], *title = "ReadConfig Error", *fmt = "File %s (line %d)\n%s";
-
- /* build error message */
- sprintf(errMsg, fmt, fName, lineNbr, [ReadConfig errorDescription:errCode]);
-
- /* defautl alert panel */
- NXRunAlertPanel(title, errMsg, "Quit", (char*)nil, (char*)nil);
- exit(-1);
-
- return (id)nil;
- }
-
- /* default error trap method */
- - configErrorTrap:(int)errCode inFile:(char*)fileName atLine:(int)lineNbr
- {
- return [ReadConfig configErrorTrap:errCode inFile:fileName atLine:lineNbr];
- }
-
- // -------------------------------------------------------------------------------------
- // Set global defaults
- // Notes:
- // - These methods may be used to set global defaults that will be in effect for all
- // configuration files read.
-
- /* set global default Terminate-On-Error flag */
- + setTerminateOnError:(BOOL)yesNo
- {
- dftExitOnError = yesNo;
- return NO;
- }
-
- /* set global default error print mode */
- + setPrintError:(BOOL)yesNo
- {
- dftPrintError = yesNo;
- return NO;
- }
-
- /* set global default return status checking mode */
- + setCheckReturn:(BOOL)yesNo
- {
- dftCheckAll = yesNo;
- return NO;
- }
-
- /* set global default method trace mode */
- + (int)setMethodTrace:(BOOL)yesNo
- {
- dftMethodTrace = yesNo;
- return NO;
- }
-
- /* set global default backup-on-write flag */
- + setBackupOnWrite:(BOOL)yesNo
- {
- dftBackupOnWrite = yesNo;
- return NO;
- }
-
- // -------------------------------------------------------------------------------------
- // read specified config file
- // -------------------------------------------------------------------------------------
- #define breakERROR(err) { error = err; break; }
-
- /* open/read config file */
- + (int)readConfigFile:(char*)file target:cfgTarget
- {
- int rtnError;
- id mySelf = [[self alloc] init];
- [mySelf setTarget:cfgTarget];
- rtnError = [mySelf readConfigFile:file];
- [mySelf free];
- return rtnError;
- }
-
- /* open/read config file relative to application directory */
- + (int)readAppConfig:(char*)file target:cfgTarget
- {
- char *fileName = [self configPath:file];
- int error = [self readConfigFile:fileName target:cfgTarget];
- free(fileName);
- return error;
- }
-
- // -------------------------------------------------------------------------------------
- // Update config file methods with new parameters
- // Notes:
- // - These methods allow for updating existing config-files method with new parameters.
- // - If duplicate method name occur in the config file, they may be made unique for
- // purposes of updating by placing a '$' followed by a character after the method name.
- // ie. setParmValue:$0 parms
- // setParmValue:$1 parms
- // The '$' and character will be stripped prior to sending the method to the target.
- // - Search for the desired method by including the '$' and character if necessary.
- // -------------------------------------------------------------------------------------
-
- /* return Storage object containing entire specified config-file */
- + newConfigStorage:(char*)fileName
- {
- FILE *fNum;
- char txt[maxLEN + 1];
- char *cPtr;
- id storage;
-
- /* open file */
- fNum = fopen(fileName, "r");
- if (!fNum) return (id)nil;
-
- /* create storage */
- storage = [[Storage alloc] initCount:10 elementSize:sizeof(char*) description:(char*)nil];
- [storage empty];
-
- /* fill storage */
- for (*txt = 0; fgets(txt, maxLEN, fNum);) {
- cPtr = NXCopyStringBuffer(txt);
- [storage addElement:&cPtr];
- }
-
- /* close file */
- fclose(fNum);
- return storage;
-
- }
-
- /* read config file (relative to app) into storage class */
- + newAppConfigStorage:(char*)file
- {
- char *fileName = [self configPath:file];
- id storage = [self newConfigStorage:fileName];
- free(fileName);
- return storage;
- }
-
- /* return index for matching string in Config-Storage object (return -1 if not found) */
- + (int)findIndexForMethodName:(char*)methName inCfgStorage:storage
- {
- char *cPtr;
- int i, methLen = strlen(methName);
- for (i = 0; i < [storage count]; i++) {
- cPtr = *((char**)[storage elementAt:i]);
- if (!strncmp(cPtr, methName, methLen)) return i;
- }
- return -1;
- }
-
- /* replace specified record with corresponding record in Config-Storage object */
- + replaceRecord:(char*)rcd inCfgStorage:storage
- {
- int ndx, len;
- char *cPtr, *nPtr, *fmt, name[maxLEN];
-
- /* find occurrance of record */
- sscanf(rcd, "%s", name);
- ndx = [self findIndexForMethodName:name inCfgStorage:storage];
- if (ndx < 0) return (id)nil;
-
- /* make a static copy of the record */
- len = strlen(rcd);
- if (rcd[len - 1] != '\n') { len++; fmt = "%s\n"; } else fmt = "%s";
- nPtr = (char*)malloc(len + 1);
- sprintf(nPtr, fmt, rcd);
-
- /* index found, replace record */
- cPtr = *((char**)[storage elementAt:ndx]);
- [storage replaceElementAt:ndx with:&nPtr];
- free(cPtr);
-
- return self;
- }
-
- /* write Config-Storage object to specified file (return nil if error) */
- + writeConfigStorage:storage toFile:(char*)fileName
- {
- int i;
- char *cPtr;
- FILE *fNum;
-
- /* check for backup (bkuFile will first be removed if it exists) */
- if (dftBackupOnWrite) {
- char bkuFile[MAXPATHLEN + 1];
- sprintf(bkuFile, "%s~", fileName);
- if (rename(fileName, bkuFile))
- fprintf(stderr, "ReadConfig: Unable to rename to %s\n", bkuFile);
- }
-
- /* open output file */
- fNum = fopen(fileName, "w");
- if (!fNum) return (id)nil;
-
- /* write data to file */
- for (i = 0; i < [storage count]; i++) {
- cPtr = *((char**)[storage elementAt:i]);
- fprintf(fNum, "%s", cPtr);
- }
-
- /* close file and return */
- fclose(fNum);
- return self;
-
- }
-
- /* write config file to file (relative to app) */
- + writeAppConfigStorage:storage toFile:(char*)file
- {
- char *fileName = [self configPath:file];
- id obj = [self writeConfigStorage:storage toFile:fileName];
- free(fileName);
- return obj;
- }
-
- /* free Config-Storage object */
- + freeConfigStorage:storage
- {
- int i;
- for (i = 0; i < [storage count]; i++) free(*((char**)[storage elementAt:i]));
- [storage free];
- return self;
- }
-
- /* print contents of Config-Storage object */
- + printConfigStorage:storage
- {
- int i;
- for (i = 0; i < [storage count]; i++)
- printf("%3d: %s", i, *((char**)[storage elementAt:i]));
- return self;
- }
-
- // -------------------------------------------------------------------------------------
- // parse and execute single config record
- // -------------------------------------------------------------------------------------
-
- + (int)sendConfigMethod:(char*)txt toTarget:theTarget
- {
- int rtn; // method call record value
- int parms; // number of parsed parms
- char methName[maxLEN + 1]; // max method name length
- char parmString[maxLEN + 1]; // max parm length
- char *pPtr, *dPtr; // char pointers
- SEL selector; // method selector
-
- /* skip record if comment */
- if (isCOMMENT(txt) || (*txt==':') || (*txt=='*') || (*txt=='!')) return cfgSINGLE_MODE;
-
- /* parse record */
- parms = sscanf(txt, parseFORMAT, methName, parmString);
- if (parms < 1) return cfgINVALID_PARMS;
-
- /* convert name to selector */
- if (dPtr = index(methName, '$')) *dPtr = 0;
- selector = sel_getUid(methName);
- if (!sel_isMapped(selector)) return cfgNO_METHOD;
-
- /* parse additional parameter */
- if (parms < 2) dPtr = pPtr = (char*)nil;
- else {
- for (pPtr = parmString; *pPtr && ((*pPtr == ' ') || (*pPtr == '\t')); pPtr++);
- dPtr = pPtr;
- if (*pPtr == '*') {
- if (!strcmp(pPtr, "*NIL") || !strcmp(pPtr, "*nil")) pPtr = (char*)nil; else
- if (!strcmp(pPtr, "*NO" ) || !strcmp(pPtr, "*no" )) pPtr = (char*)((int)NO ); else
- if (!strcmp(pPtr, "*YES") || !strcmp(pPtr, "*yes")) pPtr = (char*)((int)YES); else
- if (!strcmp(pPtr, "*ID" ) || !strcmp(pPtr, "*id") ) pPtr = (char*)nil;
- }
- }
-
- /* perform method */
- if (parms == 2) rtn = (int)[theTarget perform:selector with:(id)pPtr];
- else rtn = (int)[theTarget perform:selector];
-
- /* check return status */
- return (rtn)? cfgMETHOD_ERROR : cfgOK;
-
- }
-
- // -------------------------------------------------------------------------------------
- // profile / time-lapse methods
- // -------------------------------------------------------------------------------------
-
- static double currentTimeStamp()
- {
- struct timeval tp;
- struct timezone tzp;
- gettimeofday(&tp, &tzp);
- return (double)tp.tv_sec + (double)tp.tv_usec / 1000000.00;
- }
-
- /* return time lapse from last startProfile */
- + (float)profileElapsedTime
- {
- return (profileActive)? (float)(currentTimeStamp() - profileStart) : profileLapse;
- }
-
- /* return number of executed ReadConfig methods */
- + (int)profileCount
- {
- return profileCount;
- }
-
- /* set profiler target */
- + setProfileTarget:profile
- {
- if ([profile respondsTo:profilePING]) profileTarget = profile;
- return self;
- }
-
- /* start profiler */
- + startProfile
- {
- if (!profileTarget) [self setProfileTarget:NXApp]; // default profile target
- profileStart = currentTimeStamp();
- profileCount = 0;
- profileActive = YES;
- return self;
- }
-
- /* stop profile */
- + stopProfile
- {
- profileLapse = (float)(currentTimeStamp() - profileStart);
- profileActive = NO;
- return self;
- }
-
- // -------------------------------------------------------------------------------------
- // read specified config file
- // Notes:
- // - These methods are for INTERNAL USE ONLY and should not be called directly.
- // -------------------------------------------------------------------------------------
-
- /* initialize the internal ReadConfig instance */
- - init
- {
- [super init];
- level = 0;
- modalPanel = (id)nil;
- methodRtn = 0;
- *findLabel = 0;
- [self cfg_methodTrace:dftMethodTrace];
- [self cfg_printError:dftPrintError];
- [self cfg_checkReturn:dftCheckAll];
- [self cfg_terminateOnError:dftExitOnError];
- return self;
- }
-
- /* free internal ReadConfig instance */
- - free
- {
- return [super free];
- }
-
- /* set method target */
- - setTarget:obj
- {
- mainTarget = obj;
- return self;
- }
-
- /* open/read config file */
- - (int)readConfigFile:(char*)file
- {
- int error = cfgOK; // returned error code
- char txt[2048]; // max record length
- char method[maxLEN + 1], *methName; // max method name length
- char parmString[2048]; // max parm length
- char *pPtr, *dPtr, *cPtr; // char pointers
- SEL selector; // method selector
- BOOL checkRtn; // check return for method
- int parms; // number of parsed parms
- id msgTarget = (id)nil; // method target (default target)
- int prefixLen; // length of internal method prefix
-
- /* open file for current recursion level */
- exc[level].lineNumber = 0;
- exc[level].fNum = fopen(file, "r");
- if (!exc[level].fNum) {
- error = cfgOPEN_ERROR;
- printf("ReadConfig ERROR: Unable to open config file %s\n", file);
- if (errorTrap) [errorTrap configErrorTrap:error inFile:file atLine:0];
- return error;
- }
-
- /* set up internal method name prefix */
- strcpy(method, prefixMethodNAME);
- prefixLen = strlen(method);
-
- /* read file records */
- for (*txt = 0; fgets(txt, maxLEN, exc[level].fNum);) {
-
- /* count line number */
- exc[level].lineNumber++;
-
- /* skip record if comment */
- if (isCOMMENT(txt)) continue;
-
- /* parse record */
- methName = &method[prefixLen];
- parmString[0] = 0;
- parms = sscanf(txt, parseFORMAT, methName, parmString);
- if (parms < 1) breakERROR(cfgINVALID_PARMS);
-
- /* internal method check */
- if (*methName == '*') { *methName = '_'; methName = method; }
-
- /* check for label match */
- if (*findLabel || (*methName == ':')) {
- if ((*methName == ':') && !strcmp(findLabel, &methName[1])) *findLabel = 0;
- continue;
- }
-
- /* parse additional parameter */
- if (parms < 2) dPtr = pPtr = (char*)nil;
- else {
- for (pPtr = parmString; *pPtr && ((*pPtr == ' ') || (*pPtr == '\t')); pPtr++);
- dPtr = pPtr;
- if (*pPtr == '*') {
- if (!strcmp(pPtr, "*NIL") || !strcmp(pPtr, "*nil")) pPtr = (char*)nil; else
- if (!strcmp(pPtr, "*NO" ) || !strcmp(pPtr, "*no" )) pPtr = (char*)((int)NO ); else
- if (!strcmp(pPtr, "*YES") || !strcmp(pPtr, "*yes")) pPtr = (char*)((int)YES);
- }
- }
-
- /* return status checking */
- checkRtn = (*methName == '!' || *methName == '*')? NO: YES;
-
- /* check for update keys in method name */
- if (cPtr = index(methName, '$')) *cPtr = 0;
-
- /* convert name to selector */
- selector = sel_getUid(&methName[(checkRtn)? 0: 1]);
- if (!sel_isMapped(selector)) breakERROR(cfgNO_METHOD);
-
- /* determine real target */
- if (*methName == '*') msgTarget = self;
- else msgTarget = mainTarget;
-
- /* debug: show method performed */
- if (methodTrace) {
- printf("[%s perform:\"%s\"", [msgTarget name], sel_getName(selector));
- if (parms == 2) printf(((pPtr==dPtr)? " with:\"%s\"": " with:(void*)%d"), (int)pPtr);
- printf("];\n");
- }
-
- /* precheck method performance */
- if ( [msgTarget respondsTo:@selector(configWillPerform:with:)] &&
- ![msgTarget configWillPerform:selector with:(id)pPtr]) continue;
-
- /* make sure target can respond to the selector */
- if (![msgTarget respondsTo:selector]) breakERROR(cfgBADTARGET);
-
- /* perform method */
- if (parms == 2) methodRtn = (int)[msgTarget perform:selector with:(id)pPtr];
- else methodRtn = (int)[msgTarget perform:selector];
-
- /* check return status */
- if (checkAll && checkRtn && methodRtn) breakERROR(cfgMETHOD_ERROR);
-
- /* check for active elapsed time profiling */
- if (profileActive && (msgTarget != self)) {
- profileCount++;
- if (profileTarget) [profileTarget profileElapsedTime:[[self class] profileElapsedTime]];
- }
-
- }
-
- /* terminate any status modal loop */
- if (modalPanel) [self cfg_endStopPanel];
-
- /* check for unmatched goto label */
- if (!error && *findLabel) error = cfgGOTO_LABEL;
-
- /* close file */
- fclose(exc[level].fNum);
-
- /* set global vars */
- globalLevel = level;
- globalLine = exc[level].lineNumber;
-
- /* print error */
- if (error) {
- int lineNbr = exc[level].lineNumber;
- if (printError) {
- char *fName = rindex(file, '/');
- printf("ReadConfig [%s] (%s): %s\n", [msgTarget name], ((fName)? (fName + 1): file),
- errorDesc[error]);
- printf(" line %d: %s\n", lineNbr, txt);
- }
- if (errorTrap) [errorTrap configErrorTrap:error inFile:file atLine:lineNbr];
- if (exitOnError) [self cfg_terminate];
- }
-
- /* return error */
- return error;
-
- }
-
- #undef breakERROR
-
- // -------------------------------------------------------------------------------------
- // Internal utility method routines
- // Notes:
- // - These methods are for use from within the config file. They are accessed by
- // placing an '*' in front of the desired method call. (ie. '*methodTrace: *YES')
- // - See the header file for more information on these methods.
- // -------------------------------------------------------------------------------------
-
- /* set method trace mode */
- - (int)cfg_methodTrace:(BOOL)yesNo
- {
- methodTrace = yesNo;
- return NO;
- }
-
- /* set global method trace mode */
- - (int)cfg_globalMethodTrace:(BOOL)yesNo
- {
- dftMethodTrace = yesNo;
- return [self cfg_methodTrace:dftMethodTrace];
- }
-
- /* set error print mode */
- - (int)cfg_printError:(BOOL)yesNo
- {
- printError = yesNo;
- return NO;
- }
-
- /* set global error print mode */
- - (int)cfg_globalPrintError:(BOOL)yesNo
- {
- dftPrintError = yesNo;
- return [self cfg_printError:dftPrintError];
- }
-
- /* set return status checking mode */
- - (int)cfg_checkReturn:(BOOL)yesNo
- {
- checkAll = yesNo;
- return NO;
- }
-
- /* set global return status checking mode */
- - (int)cfg_globalCheckReturn:(BOOL)yesNo
- {
- dftCheckAll = yesNo;
- return [self cfg_checkReturn:dftCheckAll];
- }
-
- /* set global return status checking mode */
- - (int)cfg_globalBackupOnWrite:(BOOL)yesNo
- {
- dftBackupOnWrite = yesNo;
- return NO;
- }
-
- /* set terminate application on error flag */
- - (int)cfg_terminateOnError:(BOOL)yesNo
- {
- exitOnError = yesNo;
- return NO;
- }
-
- /* set terminate application on error flag */
- - (int)cfg_globalTerminateOnError:(BOOL)yesNo
- {
- dftExitOnError = yesNo;
- return [self cfg_terminateOnError:dftExitOnError];
- }
-
- // -------------------------------------------------------------------------------------
- // branching
-
- /* goto label */
- - (int)cfg_goto:(char*)label
- {
- if (!label) return YES;
- strncpy(findLabel, label, sizeof(findLabel));
- rewind(exc[level].fNum); // rewind file
- return NO;
- }
-
- /* if True goto label */
- - (int)cfg_ifTrue:(char*)label
- {
- if (methodRtn) return [self cfg_goto:label];
- return NO;
- }
-
- /* if False goto label */
- - (int)cfg_ifFalse:(char*)label
- {
- if (!methodRtn) return [self cfg_goto:label];
- return NO;
- }
-
- // -------------------------------------------------------------------------------------
- // user panels
-
- /* alert panel */
- - (int)cfg_alertPanel:(char*)message
- {
- char *title = "Configuration File Alert";
- NXRunAlertPanel(title, message, "OK", (char*)nil, (char*)nil);
- return NO;
- }
-
- /* abort panel */
- - (int)cfg_abortPanel:(char*)message
- {
- int rtn;
- char *title = "Configuration File Abort";
- rtn = NXRunAlertPanel(title, message, "Continue", "Terminate", (char*)nil);
- if (!rtn) [self cfg_terminate];
- return NO;
- }
-
- /* option panel */
- - (int)cfg_optionPanel:(char*)message
- {
- int i;
- int alertRtn;
- char btnA[100], btnB[100];
- char *title = "Configuration File Option";
- if ((message[0]=='<') || (message[0]=='(') || (message[0]=='{')) {
- sscanf(message, "%*c%[^,/| ]%*c%[^>)}]%*c%n", btnA, btnB, &i);
- while (message[i] == ' ') i++;
- alertRtn = NXRunAlertPanel(title, &message[i], btnB, btnA, (char*)nil);
- } else
- alertRtn = NXRunAlertPanel(title, message, "B", "A", (char*)nil);
- return alertRtn;
- }
-
- /* if option-A (False) goto label */
- - (int)cfg_ifOptionA:(char*)label
- {
- return [self cfg_ifFalse:label];
- }
-
- /* if option-B (True) goto label */
- - (int)cfg_ifOptionB:(char*)label
- {
- return [self cfg_ifTrue:label];
- }
-
- /* end modal panel */
- - (int)cfg_endModalPanel
- {
- if (!modalPanel) return YES;
- [NXApp runModalSession:&modalSession]; // eat up any queued events
- [NXApp endModalSession:&modalSession];
- [modalPanel orderOut:nil];
- NXFreeAlertPanel(modalPanel);
- modalPanel = (id)nil;
- return NO;
- }
-
- /* begin info modal panel */
- - (int)cfg_infoPanel:(char*)message
- {
- char *title = "Configuration Information";
- if (modalPanel) [self cfg_endModalPanel];
- modalPanel = NXGetAlertPanel(title, message, "", 0, 0);
- [NXApp beginModalSession:&modalSession for:modalPanel];
- [NXApp runModalSession:&modalSession]; // show panel
- return NO;
- }
-
- /* end modal panel */
- - (int)cfg_endInfoPanel
- {
- return [self cfg_endModalPanel];
- }
-
- /* begin stop modal panel */
- - (int)cfg_stopPanel:(char*)message
- {
- char *title = "Configuration File Status";
- if (modalPanel) [self cfg_endModalPanel];
- modalPanel = NXGetAlertPanel(title, message, "Stop", 0, 0);
- [NXApp beginModalSession:&modalSession for:modalPanel];
- [NXApp runModalSession:&modalSession]; // show panel
- return NO;
- }
-
- /* end stop panel */
- - (int)cfg_endStopPanel
- {
- return [self cfg_endModalPanel];
- }
-
- /* check for modal stop button */
- - (int)ifStop:(char*)label
- {
- int modalRtn;
- if (!modalPanel) return YES;
- modalRtn = [NXApp runModalSession:&modalSession];
- if (modalRtn == NX_ALERTDEFAULT) {
- [self cfg_endStopPanel];
- return [self cfg_goto:label];
- }
- return NO;
- }
-
- // -------------------------------------------------------------------------------------
- // misc
-
- /* print string */
- - (int)cfg_print:(char*)text
- {
- printf("%s\n", text);
- return NO;
- }
-
- /* terminate application */
- - (int)cfg_terminate
- {
- [NXApp terminate:self];
- return YES;
- }
-
- /* include another config file */
- - (int)cfg_include:(char*)cfgName
- {
- int error;
- char *fileName;
- level++;
- fileName = [[self class] configPath:cfgName];
- error = [self readConfigFile:fileName];
- free(fileName);
- level--;
- return error;
- }
-
- // -------------------------------------------------------------------------------------
- // time lapse profiling
-
- /* start time profile */
- - (int)cfg_startProfile
- {
- [[self class] startProfile];
- return NO;
- }
-
- /* stop time profile */
- - (int)cfg_stopProfile
- {
- [[self class] stopProfile];
- return NO;
- }
-
- /* print current elapsed time */
- - (int)cfg_printElapsedTime
- {
- printf("ReadConfig: Profile elapsed time = %.2f seconds, executed methods = %d\n",
- [[self class] profileElapsedTime], [[self class] profileCount]);
- return NO;
- }
-
- @end
-