home *** CD-ROM | disk | FTP | other *** search
- // -------------------------------------------------------------------------------------
- // ReadConfig.m - Read/Execute run-time configuration file.
- // Author: Martin D. Flynn, NeXT Computer, Inc.
- // Description:
- // This class provides a general solution to applications that require run-time
- // configuration. A configuration file name can be passed to the method name:
- // readConfigFile:target:
- // The file will be parsed and interpreted as method names and arguments to send to the
- // specified target. Methods requiring no-arguments, or one-argument, are allowed.
- // The class also contains some primitive branching controls to provide a simple contol
- // language.
- // -------------------------------------------------------------------------------------
- // 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.
- // -------------------------------------------------------------------------------------
- // Notes:
- // - Do NOT instantiate this object. It will instantiate a temporary copy of itself.
- // - Comment lines start with (' ', '\n', '\t', or '/') in the first column.
- // - A label is prefaced with a ':' (ie. ':Label').
- // - Method return status is 0 for success, error otherwise.
- // - Methods prefaced with '!' will skip return status checking for that method.
- // - A method with no parameter is allowed.
- // - If the target method responds to 'configWillPerform:with:', then this method will
- // will be sent to the target prior to each config method sent. If
- // 'configWillPerform:with:' returns true, then the config method will also be sent,
- // otherwise it will be skipped.
- // - Internal methods are prefaced with a '*' (internal methods listed below).
- // - The configuration read will be terminated when an error is encountered.
- // - [ReadConfig lineNumber] will return the last executed line.
- // - '*checkReturn: *NO' must be set prior to using '*ifTrue: lbl' or '*ifFalse: lbl'
- // - Method names may end with a '$' followed by any character. This will be stripped
- // prior to calling the method. (this is used for updating config files)
- // - If time lapse profiling has been activated, then after each config file method
- // executed, when ReadConfig regains control, the method 'profileTimeLapse:' will be
- // - sent to the global
- // - Reserved parameter options:
- // *nil = pass a nil pointer to method
- // *YES = pass boolean YES to method
- // *NO = pass boolean NO to method
- // *id = pass id created with *newId to method
- // -------------------------------------------------------------------------------------
- // Example config:
- //
- // // -----------------------------------------------------------------------------
- // // set ReadConfig modes
- // *methodTrace: *YES
- // *printError: *YES
- // // -----------------------------------------------------------------------------
- // // open and recalc data
- // *print: openning data file
- // myOpenData: data/myData.file
- // *print: recalculating info
- // *checkReturn: *NO
- // recalcInfo *nil
- // *ifTrue: returnTrue
- // *print: recalcInfo returned False
- // *goto: skip
- // :returnTrue
- // *print: recalcInfo returned True
- // showErrorPanel
- // :skip
- // // -----------------------------------------------------------------------------
- // // option selection panel
- // *optionPanel: select A or B
- // *ifFalse: selectA
- // *print: you selected B
- // *goto: endSelect
- // :selectA
- // *print: your selected A
- // :endSelect
- // // -----------------------------------------------------------------------------
- //
- // -------------------------------------------------------------------------------------
- // Example code:
- //
- // /* read configuration file */
- // if (error = [ReadConfig readAppConfig:"cfg/file.cfg" target:self])
- // printf("ReadConfig ERROR: %s (line %d)\n", [ReadConfig errorDescription:error],
- // [ReadConfig lineNumber]);
- //
- // -------------------------------------------------------------------------------------
- // Internal config methods:
- //
- // *globalMethodTrace: [ *YES | *NO ] (set global method trace mode)
- // *globalPrintError: [ *YES | *NO ] (set global print error mode)
- // *globalCheckReturn: [ *YES | *NO ] (set global check error return mode)
- //
- // *methodTrace: [ *YES | *NO ] (set debug method trace mode)
- // *printError: [ *YES | *NO ] (set error message print mode)
- // *checkReturn: [ *YES | *NO ] (set method return status check mode)
- // *terminateOnError: [ *YES | *NO ] (terminate application on error)
- //
- // *print: <text string> (print text string)
- // *goto: <label> (goto specified label)
- // *ifTrue: <label> (if method return==YES, then goto label)
- // *ifFalse: <label> (if method return==NO, then goto label)
- // *terminate (terminate application)
- // *alertPanel: <text string> (display alert panel with message)
- // *abortPanel: <text string> (display abort panel with message)
- // *optionPanel: <text string> (display A/B option select panel w message)
- // *ifOptionA: <label> (if option A selected {False}, goto label)
- // *ifOptionB: <label> (if option B selected {True}, goto label)
- //
- // *endModalPanel (end and remove modal info/stop panel)
- // *infoPanel: <text string> (display info panel, no button options)
- // *endInfoPanel (see 'endModalPanel')
- // *stopPanel: <text string> (display stop status message panel)
- // *endStopPanel (see 'endModalPanel')
- // *ifStop: <label> (if stop selected, then goto label)
- //
- // *startProfile (start time lapse profile counter)
- // *stopProfile (stop time lapse profile counter)
- // *printElapsedTime (print time lapse profile counter)
- //
- // -------------------------------------------------------------------------------------
- #import <appkit/Application.h>
-
- // -------------------------------------------------------------------------------------
- // config file macros
- #define readCONFIG(F,T) [ReadConfig readAppConfig:F target:T]
- #define readCfgSTORAGE(F) [ReadConfig newAppConfigStorage:F];
- #define writeCfgSTORAGE(S,F) [ReadConfig writeAppConfigStorage:S toFile:F];
-
- // -------------------------------------------------------------------------------------
- #define cfgOK 0 // successful
- #define cfgOPEN_ERROR 1 // file not open
- #define cfgINVALID_PARMS 2 // invalid cfg parameters
- #define cfgNO_METHOD 3 // no selector for method
- #define cfgMETHOD_ERROR 4 // method returned error
- #define cfgGOTO_LABEL 5 // goto label not found
- #define cfgINVALID_CODE 6 // invalid error code specified
- #define cfgSINGLE_MODE 7 // invalid format for single record mode
- #define cfgBADTARGET 8 // target does not respond to method
- #define cfgLAST_ERROR 8 // last error number
-
- // -------------------------------------------------------------------------------------
- #define maxLEVEL 10
-
- // -------------------------------------------------------------------------------------
- // multi-level vars
- typedef struct {
- FILE *fNum; // config file handle
- int lineNumber; // current line number
- } excLEVEL;
-
- // -------------------------------------------------------------------------------------
- @interface ReadConfig : Object
- {
- int level; // recursion level
- excLEVEL exc[maxLEVEL]; // recursion data
- id mainTarget; // original target object
- NXModalSession modalSession; // status message modal session
- id modalPanel; // status message modal panel
- BOOL methodTrace; // debug method trace mode
- BOOL printError; // print error message (if any)
- BOOL checkAll; // check method return values
- BOOL exitOnError; // terminate on error flag
- int methodRtn; // status returned from method
- char findLabel[16]; // label symbol
- }
-
- // -------------------------------------------------------------------------------------
- + (char*)appPath;
- // Returns the path to the current application. The application name and extension have
- // been removed.
- //
- // -------------------------------------------------------------------------------------
- + (char*)configPath:(const char*)fileName;
- // Returns a fully qualified file name by appending the specified fileName to the
- // current application path. This method uses malloc() to create a copy of the path,
- // so free() must be used to deallocate the storage used by the returned name.
- //
- // -------------------------------------------------------------------------------------
- + (int)readConfigFile:(char*)cfgFile target:cfgTarget;
- // Sends the configuration methods found in the fully qualified cfgFile to cfgTarget.
- // Control is passed to the error trap method configErrorTrap:inFile:atLine: if an
- // error occurs while reading cfgFile.
- //
- // -------------------------------------------------------------------------------------
- + (int)readAppConfig:(char*)cfgFile target:cfgTarget;
- // Same as readConfigFile:target: except that cfgFile is specified relative to the
- // current application directory.
- //
- // -------------------------------------------------------------------------------------
- + (int)sendConfigMethod:(char*)theBuffer toTarget:theTarget;
- // This method parses the config record specified in theBuffer and send the method to
- // theTarget. theBuffer cannot be prefaced with ':' (goto labels), '*' (internal
- // ReadConfig calls), '!' (overridden checkReturns), or '/' (comments). This method
- // returns cfgOK if it was successful, otherwise it return an error. Note: this method
- // is not called as part of the normal file ReadConfig loop.
- //
- // -------------------------------------------------------------------------------------
- + (int)lineNumber;
- // Returns the line number currently being processed in the config file. This may be
- // useful during an error return situation to determine the line number in error.
- //
- // -------------------------------------------------------------------------------------
- + (int)levelNumber;
- // Returns the current recursion level. This value starts out at 1, and is incremented
- // each time an *include: statement begins, and decremented each time an *include:
- // statement ends. The recursion level limit is 10 nested *include: statements.
- //
- // -------------------------------------------------------------------------------------
- + (char*)errorDescription:(int)errorCode;
- // Returns the text error description for the specified error code.
- //
- // -------------------------------------------------------------------------------------
- + setTerminateOnError:(BOOL)yesNo;
- // Set the global ReadConfig terminate-on-error flag. If set to YES, then any error
- // encountered will cause the application to terminate. The default is NO.
- //
- // -------------------------------------------------------------------------------------
- + setPrintError:(BOOL)yesNo;
- // Set the global ReadConfig print-error flag. If set to YES, then any error
- // encountered will be printed to stdout. The default is NO.
- //
- // -------------------------------------------------------------------------------------
- + setCheckReturn:(BOOL)yesNo;
- // Set the global ReadConfig check-return flag. If set to NO, then the return value
- // is checked for all methods executed. Methods returning a true condition indicate
- // an error condition. The default is YES.
- //
- // -------------------------------------------------------------------------------------
- + (int)setMethodTrace:(BOOL)yesNo;
- // Set the global ReadConfig method-trace flag. If set to YES, then all method names
- // which are about to be executed will be printed to stdout. The default is NO.
- //
- // -------------------------------------------------------------------------------------
- + setErrorTrap:target;
- // Set the target which is to receive the configErrorTrap:inFile:atLine: method when
- // an error is encountered. Default is self.
- //
- // -------------------------------------------------------------------------------------
- + configErrorTrap:(int)errCode inFile:(char*)fileName atLine:(int)lineNbr;
- // This is the default error handler if no '+setErrorTrap:' method is issued.
- //
- // -------------------------------------------------------------------------------------
- + newConfigStorage:(char*)fileName;
- // Reads the contents of the file fileName and place each record as an entry in a
- // storage object. This storage object is used by replaceRecord:inCfgStorage: to
- // allow replace configuration file records.
- //
- // -------------------------------------------------------------------------------------
- + newAppConfigStorage:(char*)fileName;
- // Same as newConfigStorage: except that the fileName is referenced relative to the
- // current application directory.
- //
- // -------------------------------------------------------------------------------------
- + (int)findIndexForMethodName:(char*)methName inCfgStorage:theStorage;
- // Returns the element index in theStorage for the record beginning with the method
- // name methName.
- //
- // -------------------------------------------------------------------------------------
- + replaceRecord:(char*)theRecord inCfgStorage:theStorage;
- // This method searches for the occurrance of the method name found at the beginning of
- // theRecord and replaces its occurrance in theStorage if found.
- //
- // -------------------------------------------------------------------------------------
- + writeConfigStorage:theStorage toFile:(char*)fileName;
- // This method writes the contents of theStorage to the fully qualified fileName.
- //
- // -------------------------------------------------------------------------------------
- + writeAppConfigStorage:theStorage toFile:(char*)fileName;
- // Same as writeConfigStorage:toFile: except that fileName is referenced relative to
- // the current application directory.
- //
- // -------------------------------------------------------------------------------------
- + freeConfigStorage:theStorage;
- // Frees the storage allocated by theStorage.
- //
- // -------------------------------------------------------------------------------------
- + printConfigStorage:theStorage;
- // Prints the text data records found in theStorage to stdout. May be used for
- // debugging purposes.
- //
- // -------------------------------------------------------------------------------------
- + setProfileTarget:theProfileTarget;
- // During active elapsed time profiling, the method 'profileElapsedTime:' will be sent
- // to the theProfileTarget whenever ReadConfig regains control after each config file
- // method executed. theProfileTarget must respond to the method 'profileElapsedTime:',
- // otherwise theProfileTarget is ignored. If no profile has been set by the time that
- // '*startProfile' is executed then 'setProfileTarget:' is implicitly called with NXApp
- // as the default target.
- //
- // -------------------------------------------------------------------------------------
- + (float)profileElapsedTime;
- // Returns the elapsed time between the commands '*startProfile' and '*stopProfile'. If
- // time profiling is still active then the elapsed time since the last '*startProfile'
- // is returned. The return time is in seconds.
- //
- // -------------------------------------------------------------------------------------
- + (int)profileCount;
- // Returns the number of methods executed by ReadConfig between the commands
- // '*startProfile' and '*stopProfile'. If time profiling is still active then the
- // number of executed methods since the last '*startProfile' is returned. This count
- // does not included '*' command methods sent directly to ReadConfig itself.
- //
- // -------------------------------------------------------------------------------------
- + startProfile;
- // Executes the ReadConfig command '*startProfile'.
- //
- // -------------------------------------------------------------------------------------
- + stopProfile;
- // Executes the ReadConfig command '*stopProfile'.
- //
- // -------------------------------------------------------------------------------------
-
- @end
-