| |
---|
| | brevity, the code examples here assume the following Python import |
---|
| | syntax: |
---|
| | |
---|
| | .nf |
---|
| | from tconfpy import * |
---|
| | from tconfpy import * |
---|
| | .fi |
---|
| | |
---|
| | If you prefer the more pedestrian: |
---|
| | |
---|
| | .nf |
---|
| | import tconfpy |
---|
| | import tconfpy |
---|
| | .fi |
---|
| | |
---|
| | you will have to prepend all references to a \*(TC object with |
---|
| | \'tconfpy.\'. So \'retval=ParseConfig(...\' becomes |
---|
| |
---|
| | to be processed is a required parameter, all the others are optional |
---|
| | and default as described below: |
---|
| | |
---|
| | .nf |
---|
| | from tconfpy import * |
---|
| | |
---|
| | retval = ParseConfig(cfgfile, |
---|
| | InitialSymTbl={}, |
---|
| | AllowNewVars=True, |
---|
| | AllowNewNamespaces=True, |
---|
| | Debug=False, |
---|
| | LiteralVars=False |
---|
| | ) |
---|
| | from tconfpy import * |
---|
| | |
---|
| | retval = ParseConfig(cfgfile, |
---|
| | InitialSymTable={}, |
---|
| | AllowNewVars=True, |
---|
| | AllowNewNamespaces=True, |
---|
| | Debug=False, |
---|
| | LiteralVars=False |
---|
| | ) |
---|
| | |
---|
| | where: |
---|
| | |
---|
| | .fi |
---|
| |
---|
| | |
---|
| | The the name of a file containing configuration information |
---|
| | |
---|
| | .TP |
---|
| | .B InitialSymTbl (Default: {}) |
---|
| | .B InitialSymTable (Default: {}) |
---|
| | |
---|
| | |
---|
| | A pre-populated symbol table (a Python dictionary). As described |
---|
| | below, this must contain valid \'VarDescriptor\' entries for each |
---|
| |
---|
| | |
---|
| | An object of type \'tconfpy.RetObj\' used to return parsing results. |
---|
| | |
---|
| | |
---|
| | .SS Reasons For Passing An Initial Symbol Table |
---|
| | .SS The Initial Symbol Table API Option |
---|
| | |
---|
| | The simplest way to parse a configuration file is just to call |
---|
| | the parser with the name of that file: |
---|
| | |
---|
| | |
---|
| | .nf |
---|
| | retval = ParseConfig("myconfigfile") |
---|
| | retval = ParseConfig("MyConfigFile") |
---|
| | .fi |
---|
| | |
---|
| | Assuming your configuration file is valid, \'ParseConfig()\' will |
---|
| | return a symbol table populated with all the variables defined in the |
---|
| |
---|
| | table to the parser that contains pre-defined symbols/values of |
---|
| | your own choosing. Why on earth would you want to do this? There |
---|
| | are a number of reasons: |
---|
| | |
---|
| | .nf |
---|
| | 1) You may wish to write a configuration file which somehow depends |
---|
| | on a pre-defined variable that only the calling program can know: |
---|
| | |
---|
| | .if [APPVERSION] == 1.0 |
---|
| | # Set configuration for older application releases |
---|
| | .else |
---|
| | # Set configuration for newer releases |
---|
| | .endif |
---|
| | |
---|
| | In this example, only the calling application can know its own |
---|
| | version, so it sets the variable APPVERSION in a symbol table |
---|
| | which is passed to \'ParseConfig()\'. |
---|
| | |
---|
| | 2) You may wish to "protect" certain variable names be creating |
---|
| | them ahead of time and marking them as "Read Only". This is |
---|
| | useful when you want a variable to be available for use |
---|
| | within a configuration file, but you do not want users to |
---|
| | be able to change its value. In this case, the variable can |
---|
| | be referenced in a string substitution or conditional test, |
---|
| | but cannot be changed. |
---|
| | |
---|
| | 3) You may want to place limits on what values can be assigned to |
---|
| | a particular variable. When a variable is newly defined in a |
---|
| | a configuration file, it just defaults to being a string |
---|
| | variable without any limits on its length or content. But |
---|
| | variables that are created by a program have access to the |
---|
| | variable's "descriptor". By setting various attribues |
---|
| | of the variable descriptor you can control variable type, |
---|
| | content, and range of values. In other words, you can have |
---|
| | \*(TC "validate" what values the user assigns to particular |
---|
| | variables. This substantially simplifies your application because |
---|
| | no invalid variable value will ever be returned from the parser. |
---|
| | |
---|
| | .fi |
---|
| | |
---|
| | .IP \(bu 4 |
---|
| | You may wish to write a configuration file which somehow depends |
---|
| | on a pre-defined variable that only the calling program can know: |
---|
| | |
---|
| | .nf |
---|
| | .if [APPVERSION] == 1.0 |
---|
| | # Set configuration for older application releases |
---|
| | .else |
---|
| | # Set configuration for newer releases |
---|
| | .endif |
---|
| | .fi |
---|
| | |
---|
| | In this example, only the calling application can know its own |
---|
| | version, so it sets the variable APPVERSION in a symbol table |
---|
| | which is passed to \'ParseConfig()\'. |
---|
| | |
---|
| | .IP \(bu 4 |
---|
| | You may wish to "protect" certain variable names be creating |
---|
| | them ahead of time and marking them as "Read Only". This is |
---|
| | useful when you want a variable to be available for use |
---|
| | within a configuration file, but you do not want users to |
---|
| | be able to change its value. In this case, the variable can |
---|
| | be referenced in a string substitution or conditional test, |
---|
| | but cannot be changed. |
---|
| | |
---|
| | .IP \(bu 4 |
---|
| | You may want to place limits on what values can be assigned to |
---|
| | a particular variable. When a variable is newly defined in a |
---|
| | a configuration file, it just defaults to being a string |
---|
| | variable without any limits on its length or content. But |
---|
| | variables that are created by a program have access to the |
---|
| | variable's "descriptor". By setting various attribues |
---|
| | of the variable descriptor you can control variable type, |
---|
| | content, and range of values. In other words, you can have |
---|
| | \*(TC "validate" what values the user assigns to particular |
---|
| | variables. This substantially simplifies your application because |
---|
| | no invalid variable value will ever be returned from the parser. |
---|
| | |
---|
| | |
---|
| | .SS How To Create An Initial Symbol Table |
---|
| | |
---|
| | A \*(TC "Symbol Table" is really nothing more than a Python |
---|
| |
---|
| | descriptor". Creating new variables in the symbol table involves |
---|
| | nothing more than this: |
---|
| | |
---|
| | .nf |
---|
| | |
---|
| | from tconfpy import * |
---|
| | |
---|
| | # Create an empty symbol table |
---|
| | MySymTable = {} |
---|
| | |
---|
| | # Create descriptor for new variable |
---|
| | MyVarDes = VarDescriptor() |
---|
| | |
---|
| | # Code to fiddle with descriptor contents goes here |
---|
| | MyVarDes.Value = "MyVal" |
---|
| | |
---|
| | # Now load the variable into the symbol table |
---|
| | MySymTable["MyVariableName"] = MyVarDes |
---|
| | |
---|
| | from tconfpy import * |
---|
| | |
---|
| | # Create an empty symbol table |
---|
| | MySymTable = {} |
---|
| | |
---|
| | # Create descriptor for new variable |
---|
| | MyVarDes = VarDescriptor() |
---|
| | |
---|
| | # Code to fiddle with descriptor contents goes here |
---|
| | MyVarDes.Value = "MyVal" |
---|
| | |
---|
| | # Now load the variable into the symbol table |
---|
| | MySymTable["MyVariableName"] = MyVarDes |
---|
| | |
---|
| | # Repeat this process for all variables, then call the parser |
---|
| | |
---|
| | retval = ParseConfig("MyConfigFile", InitialSymTable=MySymTable) |
---|
| | |
---|
| | .fi |
---|
| | |
---|
| | |
---|
| |
---|
| | it matches the type declared for that variable. For example, |
---|
| | suppose you did this when defining the variable, \'foo\': |
---|
| | |
---|
| | .nf |
---|
| | VarDescriptor.Type = TYPE_INT |
---|
| | VarDescriptor.Type = TYPE_INT |
---|
| | .fi |
---|
| | |
---|
| | Now suppose the user puts this in the configuration file: |
---|
| | |
---|
| | .nf |
---|
| | foo = bar |
---|
| | foo = bar |
---|
| | .fi |
---|
| | |
---|
| | This will cause a type mismatch error because \'bar\' cannot be coerced |
---|
| | into an integer type - it is a string. |
---|
| |
---|
| | That's what this attribute is for. \'LegalVals\' explictly lists |
---|
| | every legal value for the variable in question. If the list is |
---|
| | empty,then this validation check is skipped. |
---|
| | |
---|
| | .B IMPORTANT: |
---|
| | If you change the content of \'LegalVals\', make sure it is always a |
---|
| | Python list. \*(TC's validation logic presumes this attribute |
---|
| | to be a list and will blow up nicely if it is not. |
---|
| | |
---|
| | The exact semantics of LegalVals varies depending on |
---|
| | the type of the variable. |
---|
| | |
---|
| | .nf |
---|
| |
---|
| | observe case and test for either \'True\' or \'False\': |
---|
| | |
---|
| | .nf |
---|
| | |
---|
| | .if [boolvar] != False # This works fine |
---|
| | |
---|
| | .if [boolvar] != FALSE # This does not work - Case is not being observed |
---|
| | |
---|
| | .if [boolvar] != Off # Neither does this - Only True and False can be tested |
---|
| | |
---|
| | .fi |
---|
| | .if [boolvar] != False # This works fine |
---|
| | |
---|
| | .if [boolvar] != FALSE # This does not work - Case is not being observed |
---|
| | |
---|
| | .if [boolvar] != Off # Neither does this - Only True and False can be tested |
---|
| | |
---|
| | .fi |
---|
| | |
---|
| | |
---|
| | .SS The Initial Symbol Table And Lexical Namespaces |
---|
| | |
---|
| | The |
---|
| | .B CONFIGURATION LANGUAGE REFERENCE |
---|
| | section below discusses lexical namespaces in some detail from the |
---|
| | user's point-of-view. However, it is useful for the programmer |
---|
| | to understand how they are implemented. |
---|
| | |
---|
| | \*(TC is written to use a pre-defined variable named \'NAMESPACE\' as |
---|
| | the place where the current namespace is kept. If you do not |
---|
| | define this variable in the initial symbol table passed to the parser, |
---|
| | \*(TC will create it automatically with an initial value of "". |
---|
| | |
---|
| | From a programmer's perspective, there are are few important things |
---|
| | to know about namespaces and the \'NAMESPACE\' variable: |
---|
| | |
---|
| | |
---|
| | .IP \(bu 4 |
---|
| | You can manually set the initial namespace to something other than |
---|
| | "". You do this by creating the \'NAMESPACE\' variable in the initial |
---|
| | symbol table passed to the parser, and setting the \'Value\' attribute |
---|
| | of its descriptor to whatever you want as the initial namespace. |
---|
| | |
---|
| | .IP \(bu 4 |
---|
| | Each time the user enters a new namespace, it is added to the |
---|
| | \'LegalVals\' attribute of the \'NAMESPACE\' variable descriptor. |
---|
| | This is done to keep a "history" of all namespaces encountered during |
---|
| | parsing. |
---|
| | |
---|
| | .IP \(bu 4 |
---|
| | If you are inhibiting the creation of new namespaces (via the |
---|
| | \'AllowNewNamespaces = False\' option described below) you can |
---|
| | enumerate the namespaces the user |
---|
| | .B is |
---|
| | allowed to use by pre-defining them. You do this by adding each |
---|
| | permitted namespace to the \'LegalVals\' attribute of the |
---|
| | \'NAMESPACE\' variable descriptor in the initial symbol table. |
---|
| | |
---|
| | .IP \(bu 4 |
---|
| | When the call to \'ParseConfig()\' completes, the \'Value\' |
---|
| | attribute of the \'NAMESPACE\' variable descriptor will contain |
---|
| | the namespace that was in effect when the parse completed. i.e., |
---|
| | It will contain the last namespace used. |
---|
| | |
---|
| | .IP \(bu 4 |
---|
| | Similarly, on parser completion, the \'LegalVals\' attribute of the |
---|
| | \'NAMESPACE\' variable descriptor will contain the name of all the |
---|
| | namespaces encountered during parsing. This is |
---|
| | .B not |
---|
| | true if you suppressed new namespace creation with the |
---|
| | \'AllowNewNamespaces = False\' API option. In that case, the |
---|
| | \'LegalVals\' attribute will contain only those namespaces you |
---|
| | pre-defined. All of these may- or may not have been used in the |
---|
| | configuration file, there is no way to tell. |
---|
| | |
---|
| | |
---|
| | .SS How The \*(TC Parser Validates The Initial Symbol Table |
---|
| | |
---|
| |
---|
| | to be recorded. Similarly, if you put a value in \'LegalVals\' that |
---|
| | is outside the range of \'Min\' to \'Max\', \*(TC will accept |
---|
| | it quietly. |
---|
| | |
---|
| | .SS The \'AllowNewVars\' Option |
---|
| | .SS The \'AllowNewVars\' API Option |
---|
| | |
---|
| | By default, \*(TC lets the user define any new variables they |
---|
| | wish in a configuration file, merely by placing a line in the |
---|
| | file in this form: |
---|
| | |
---|
| | .nf |
---|
| | Varname = Value |
---|
| | Varname = Value |
---|
| | .fi |
---|
| | |
---|
| | However, you can disable this capability by calling the parser like |
---|
| | this: |
---|
| | |
---|
| | .nf |
---|
| | retval = ParseConfig("myconfigfile", AllowNewVars=False) |
---|
| | retval = ParseConfig("myconfigfile", AllowNewVars=False) |
---|
| | .fi |
---|
| | |
---|
| | This means that the configuration file can "reference" |
---|
| | any pre-defined variables, and even change their values |
---|
| |
---|
| | symbol table to the parser and you do not want any other variables |
---|
| | defined by the user. Why? There are several possible uses for |
---|
| | this option: |
---|
| | |
---|
| | .nf |
---|
| | |
---|
| | 1) You know every configuration variable name the calling program |
---|
| | will use ahead of time. Disabling new variable names keeps |
---|
| | the configuration file from getting cluttered with variables |
---|
| | that the calling program will ignore anyway, thereby |
---|
| | keeping the file more readable. |
---|
| | |
---|
| | 2) You want to insulate your user from silent errors caused |
---|
| | by misspellings. Say your program looks for a configuration |
---|
| | variable called \'MyEmail\' but the user enters something |
---|
| | like \'myemail = foo@bar.com\'. \'MyEmail\' and \'myemail\' |
---|
| | are entirely different variables and only the former is |
---|
| | recognized by your calling program. By turning off new |
---|
| | variable creation, the user's inadvertent misspelling of |
---|
| | the desired variable name will be flagged as an error. |
---|
| | |
---|
| | .fi |
---|
| | .IP \(bu 4 |
---|
| | You know every configuration variable name the calling program |
---|
| | will use ahead of time. Disabling new variable names keeps |
---|
| | the configuration file from getting cluttered with variables |
---|
| | that the calling program will ignore anyway, thereby |
---|
| | keeping the file more readable. |
---|
| | |
---|
| | .IP \(bu 4 |
---|
| | You want to insulate your user from silent errors caused |
---|
| | by misspellings. Say your program looks for a configuration |
---|
| | variable called \'MyEmail\' but the user enters something |
---|
| | like \'myemail = foo@bar.com\'. \'MyEmail\' and \'myemail\' |
---|
| | are entirely different variables and only the former is |
---|
| | recognized by your calling program. By turning off new |
---|
| | variable creation, the user's inadvertent misspelling of |
---|
| | the desired variable name will be flagged as an error. |
---|
| | |
---|
| | Note, however, that there is one big drawback to disabling new |
---|
| | variable creation. \*(TC processes the configuration file on a |
---|
| | line-by-line basis. No line "continuation" is supported. For really |
---|
| |
---|
| | example: |
---|
| | |
---|
| | .nf |
---|
| | |
---|
| | inter1 = Really, really, really, really, long argument #1 |
---|
| | inter2 = Really, really, really, really, long argument #2 |
---|
| | |
---|
| | realvar = command [inter1] [inter2] |
---|
| | inter1 = Really, really, really, really, long argument #1 |
---|
| | inter2 = Really, really, really, really, long argument #2 |
---|
| | |
---|
| | realvar = command [inter1] [inter2] |
---|
| | .fi |
---|
| | |
---|
| | If you disable new variable creation you can't do this |
---|
| | anymore unless all the variables \'inter1\', \'inter2\', |
---|
| | and \'realvar\' are predefined in the initial symbol |
---|
| | and \'realvar\' are pre-defined in the initial symbol |
---|
| | table passed to the parser. |
---|
| | |
---|
| | |
---|
| | .SS The \'AllowNewNamespaces\' Option |
---|
| | .SS The \'AllowNewNamespaces\' API Option |
---|
| | |
---|
| | By default, \*(TC supports the use of an arbitrary number of |
---|
| | lexical namespaces. They can be predefined in an initial |
---|
| | lexical namespaces. They can be pre-defined in an initial |
---|
| | symbol table passed to the parser and/or created in the configuration |
---|
| | file as desired. (The details are described in a later section |
---|
| | of this document.) |
---|
| | |
---|
| |
---|
| | these pre-defined namespaces is available for use throughout the |
---|
| | configuration file even if \'AllowNewNamespaces\' is set to False. |
---|
| | |
---|
| | |
---|
| | .SS The \'Debug\' Option |
---|
| | .SS The \'Debug\' API Option |
---|
| | |
---|
| | \*(TC has a fairly rich set of debugging features built into its |
---|
| | parser. It can provide some detail about each line parsed as well |
---|
| | as overall information about the parse. Be default, debugging is |
---|
| | turned off. To enable debugging, merely set \'Debug=True' in the |
---|
| | API call: |
---|
| | |
---|
| | .nf |
---|
| | retval = ParseConfig("myconfigfile", Debug=True) |
---|
| | .fi |
---|
| | |
---|
| | .SS The \'LiteralVars\' Option |
---|
| | retval = ParseConfig("myconfigfile", Debug=True) |
---|
| | .fi |
---|
| | |
---|
| | .SS The \'LiteralVars\' API Option |
---|
| | |
---|
| | \*(TC supports the inclusion of literal text anywhere in a |
---|
| | configuration file via the \'.literal\' directive. This |
---|
| | directive effectively tells the \*(TC parser to pass |
---|
| |
---|
| | |
---|
| | Here is an example: |
---|
| | |
---|
| | .nf |
---|
| | MyEmail = me@here.com # This defines variable MyEmail |
---|
| | MyEmail = me@here.com # This defines variable MyEmail |
---|
| | |
---|
| | .literal |
---|
| | printf("[MyEmail]"); /* A C Statement */ |
---|
| | .endliteral |
---|
| |
---|
| | literal blocks by setting \'LiteralVars=True\' in the |
---|
| | \'ParseConfig()\' call: |
---|
| | |
---|
| | .nf |
---|
| | retval = ParseConfig("myconfigfile", LiteralVars=True) |
---|
| | retval = ParseConfig("myconfigfile", LiteralVars=True) |
---|
| | .fi |
---|
| | |
---|
| | |
---|
| | In this example, \*(TC would return: |
---|
| |
---|
| | nothing more than a container to hold return data. In the simplest |
---|
| | case, we can parse and extract results like this: |
---|
| | |
---|
| | .nf |
---|
| | from tconfpy import * |
---|
| | |
---|
| | retval = ParseConfig("myconfigfile", Debug=True) |
---|
| | from tconfpy import * |
---|
| | |
---|
| | retval = ParseConfig("myconfigfile", Debug=True) |
---|
| | .fi |
---|
| | |
---|
| | \'retval\' now contains the results of the parse: |
---|
| | |
---|
| | .nf |
---|
| | retval.Errors - A Python list containing error messages. If this list |
---|
| | is empty, you can infer that there were no parsing |
---|
| | errors - i.e., The configuration file was OK. |
---|
| | |
---|
| | retval.Warnings - A Python list containing warning messages. These |
---|
| | describe minor problems not fatal to the parse |
---|
| | process, but that you really ought to clean up in |
---|
| | the configuration file. |
---|
| | |
---|
| | |
---|
| | retval.SymTable - A Python dictionary which lists all the defined |
---|
| | symbols and their associated values. A "value" |
---|
| | in this case is always an object of type |
---|
| | tconfpy.VarDescriptor (as described above). |
---|
| | |
---|
| | retval.Literals - As described below, the \*(TC configuration language |
---|
| | supports a \'.literal\' directive. This directive |
---|
| | allows the user to embed literal text anywhere in |
---|
| | the configuration file. This effectively makes |
---|
| | \*(TC useful as a preprocessor for any other |
---|
| | language or text. retval.Literals is a Python list |
---|
| | containing all literal text discovered during the parse. |
---|
| | |
---|
| | retval.Debug - A Python list containing detailed debug information for |
---|
| | each line parsed as well as some brief summary |
---|
| | information about the parse. retval.Debug defaults |
---|
| | to an empty list and is only populated if you set |
---|
| | \'Debug=True\' in the API call that initiated the |
---|
| | parse (as in the example above). |
---|
| | .fi |
---|
| | .TP |
---|
| | .B retval.Errors |
---|
| | A Python list containing error messages. If this list is empty, you |
---|
| | can infer that there were no parsing errors - i.e., The configuration |
---|
| | file was OK. |
---|
| | |
---|
| | .TP |
---|
| | .B retval.Warnings |
---|
| | A Python list containing warning messages. These describe minor |
---|
| | problems not fatal to the parse process, but that you really ought to |
---|
| | clean up in the configuration file. |
---|
| | |
---|
| | |
---|
| | .TP |
---|
| | .B retval.SymTable |
---|
| | |
---|
| | A Python dictionary which lists all the defined symbols and their |
---|
| | associated values. A "value" in this case is always an object of type |
---|
| | tconfpy.VarDescriptor (as described above). |
---|
| | |
---|
| | .TP |
---|
| | .B retval.Literals |
---|
| | |
---|
| | As described below, the \*(TC configuration language supports a |
---|
| | \'.literal\' directive. This directive allows the user to embed |
---|
| | literal text anywhere in the configuration file. This effectively |
---|
| | makes \*(TC useful as a preprocessor for any other language or text. |
---|
| | retval.Literals is a Python list containing all literal text |
---|
| | discovered during the parse. |
---|
| | |
---|
| | .TP |
---|
| | .B retval.Debug |
---|
| | |
---|
| | A Python list containing detailed debug information for each line |
---|
| | parsed as well as some brief summary information about the parse. |
---|
| | retval.Debug defaults to an empty list and is only populated if you |
---|
| | set \'Debug=True\' in the API call that initiated the parse (as in the |
---|
| | example above). |
---|
| | |
---|
| | |
---|
| | .SH CONFIGURATION LANGUAGE REFERENCE |
---|
| | |
---|
| |
---|
| | operators. However, there are some places where |
---|
| | whitespace matters: |
---|
| | |
---|
| | .nf |
---|
| | - Variable names may not contain whitespace |
---|
| | |
---|
| | - Directives must be followed by whitespace if |
---|
| | they take other arguments. |
---|
| | |
---|
| | - When assigning a value to a string variable, |
---|
| | whitespace within the value on the right-hand-side |
---|
| | is preserved. Leading- and trailing whitespace |
---|
| | around the right-hand-side of the assignment is |
---|
| | ignored. |
---|
| | - Variable names may not contain whitespace |
---|
| | |
---|
| | - Directives must be followed by whitespace if they |
---|
| | take other arguments. |
---|
| | |
---|
| | - When assigning a value to a string variable, |
---|
| | whitespace within the value on the right-hand-side |
---|
| | is preserved. Leading- and trailing whitespace around |
---|
| | the right-hand-side of the assignment is ignored. |
---|
| | |
---|
| | - Whitespace within both the left- and right-hand-side |
---|
| | arguments of a conditional comparison |
---|
| | (\'.if ... == / != ...\') is significant for purposes |
---|
| | of the comparison. |
---|
| | .fi |
---|
| | |
---|
| | .IP \(bu 4 |
---|
| | Case is always significant except when assigning a value to Booleans |
---|
| |
---|
| | .IP \(bu 4 |
---|
| | Regardless of a variable's type, all variable references |
---|
| | return |
---|
| | .B a string representation of the variable's value! |
---|
| | This is done so that the variable's value can be used for |
---|
| | comparison testing and string substitution/concatenation. |
---|
| | In other words, variables are stored in their native type |
---|
| | in the symbol table that is returned to the calling program, |
---|
| | but they are treated as strings throughout the configuration |
---|
| | file. |
---|
| | |
---|
| | |
---|
| | |
---|
| | .SS Variables And Variable References |
---|
| | This is done so that the variable's value can be used for comparison |
---|
| | testing and string substitution/concatenation. In other words, |
---|
| | variables are stored in their native type in the symbol table that is |
---|
| | returned to the calling program. However, they are treated as strings |
---|
| | during the parsing of the configuration file whenever they are used in |
---|
| | a comparison test or in a substitution. |
---|
| | |
---|
| | .IP \(bu 4 |
---|
| | Any line which does not conform to these rules and/or is not |
---|
| | in the proper format for one of the operations described below, |
---|
| | is considered an error. |
---|
| | |
---|
| | |
---|
| | .SS Creating Variables And Assigning A Value |
---|
| | |
---|
| | The heart of a configuration file is a "variable". Variables are |
---|
| | stored in a "Symbol Table" which is returned to the calling program |
---|
| | once the configuration file has been processed. The calling program |
---|
| |
---|
| | |
---|
| | Variables are assigned values like this: |
---|
| | |
---|
| | .nf |
---|
| | MyVariable = Some string of text |
---|
| | MyVariable = Some string of text |
---|
| | .fi |
---|
| | |
---|
| | If \'MyVariable\' is a new variable, \*(TC will create it on the spot. |
---|
| | If it already exists, \*(TC will first check and make sure that |
---|
| | \'Some string of text\' is a legal value for this variable. |
---|
| | If not, it will produce an error and refuse to change the current |
---|
| | value of \'MyVariable\' |
---|
| | |
---|
| | You can get the value of any currently defined variable by |
---|
| | "referencing" it like this: |
---|
| | |
---|
| | .nf |
---|
| | .... [MyVariable] ... |
---|
| | .fi |
---|
| | |
---|
| | The brackets surrounding any name are what indicate that you want that |
---|
| | variable's value. |
---|
| | |
---|
| | You can also get the value of any Environment Variable on your |
---|
| | system by naming the variable with a leading \'$\': |
---|
| | |
---|
| | .nf |
---|
| | ... [$USER] ... # Gets the value of the USER environment variable |
---|
| | .fi |
---|
| | |
---|
| | However you cannot set the value of an environment variable: |
---|
| | |
---|
| | .nf |
---|
| | $USER = me # This is not permitted |
---|
| | .fi |
---|
| | If it already exists, \*(TC will first check and make sure that \'Some |
---|
| | string of text\' is a legal value for this variable. If not, it will |
---|
| | produce an error and refuse to change the current value of |
---|
| | \'MyVariable\'. |
---|
| | |
---|
| | Anytime you create a new variable, the first value assigned to |
---|
| | it is also considered its "default" value. This may (or may |
---|
| | not) be meaningful to the application program. |
---|
| | |
---|
| | Variables which are newly-defined in a configuration file are |
---|
| | always understood to be |
---|
| | .B string |
---|
| | variables - i.e., They hold "strings" of text. However, it is |
---|
| | possible for the applications programmer to pre-define |
---|
| | variables with other types and place limitations on what values |
---|
| | the variable can take and/or how short or long a string variable |
---|
| | may be. (See the previous section, |
---|
| | .B PROGRAMMING USING THE \*(TC API |
---|
| | for all the gory details.) |
---|
| | |
---|
| | |
---|
| | .SS Variable Names |
---|
| | |
---|
| | Variables can be named pretty much anything you like, with certain |
---|
| | restrictions: |
---|
| | |
---|
| |
---|
| | Variable names cannot have the \'#\' character anywhere in them |
---|
| | because \*(TC sees that character as the beginning a comment. |
---|
| | |
---|
| | .IP \(bu 4 |
---|
| | You cannot have a variable with no name. This is illegal: |
---|
| | |
---|
| | .nf |
---|
| | = String |
---|
| | .fi |
---|
| | |
---|
| | |
---|
| | |
---|
| | .SS Predefined Variables |
---|
| | You cannot have a variable whose name is the empty string. This is |
---|
| | illegal: |
---|
| | |
---|
| | .nf |
---|
| | = String |
---|
| | .fi |
---|
| | |
---|
| | |
---|
| | .SS Getting And Using The Value Of A Variable |
---|
| | |
---|
| | You can get the value of any currently defined variable by |
---|
| | "referencing" it like this: |
---|
| | |
---|
| | .nf |
---|
| | .... [MyVariable] ... |
---|
| | .fi |
---|
| | |
---|
| | The brackets surrounding any name are what indicate that you want that |
---|
| | variable's value. |
---|
| | |
---|
| | You can also get the value of any Environment Variable on your |
---|
| | system by naming the variable with a leading \'$\': |
---|
| | |
---|
| | .nf |
---|
| | ... [$USER] ... # Gets the value of the USER environment variable |
---|
| | .fi |
---|
| | |
---|
| | However you cannot set the value of an environment variable: |
---|
| | |
---|
| | .nf |
---|
| | $USER = me # This is not permitted |
---|
| | .fi |
---|
| | |
---|
| | |
---|
| | This ability to both set and retrieve variable content makes it easy |
---|
| | to combine variables through "substitution": |
---|
| | |
---|
| | .nf |
---|
| | |
---|
| | MYNAME = Mr. Tconfpy |
---|
| | MYAGE = 101 |
---|
| | |
---|
| | Greeting = Hello [MYNAME], you look great for someone [MYAGE]! |
---|
| | |
---|
| | .fi |
---|
| | |
---|
| | |
---|
| | Several observations are worth noting here: |
---|
| | |
---|
| | .IP \(bu 4 |
---|
| | The substitution of variables takes place as soon as the parser |
---|
| | processes the line \'Greeting = ...\'. That is, variable substitution |
---|
| | happens as it is encountered in the configuration file. The only |
---|
| | exception to this is if an attempt is made to refer to an |
---|
| | undefined/non-existent variable. This generates an error. |
---|
| | |
---|
| | .IP \(bu 4 |
---|
| | Unless a variable as been marked as "Read Only" by the program, |
---|
| | you can continue to change its value as you go. Simply adding |
---|
| | another line at the end of our example above will change the value |
---|
| | of \'Greeting\' to something new: |
---|
| | |
---|
| | .nf |
---|
| | Greeting = Generic Greeting Message |
---|
| | .fi |
---|
| | |
---|
| | In other words, the last assignment statement for a given variable |
---|
| | "wins". This may seem sort of pointless, but it actually has great |
---|
| | utility. You can use the \'.include\' directive to get, say, a |
---|
| | "standard" configuration provided by the system administrator for a |
---|
| | particular application. You can then selectively override the |
---|
| | variables you want to change in your own configuration file. |
---|
| | |
---|
| | .IP \(bu 4 |
---|
| | The variable \'Greeting\' now contains the |
---|
| | .B string |
---|
| | "Hello Mr. Tconfpy, you look great for someone 101!" This is true even |
---|
| | if variable \'MYAGE\' has been defined by the calling program to be |
---|
| | an integer type. To repeat a previously-made point: All variable substitution |
---|
| | and comparison operations in a configuration file are done with |
---|
| | .B strings |
---|
| | regardless of the actual type of the variables involved. |
---|
| | |
---|
| | |
---|
| | .SS Lexical Namespaces |
---|
| | |
---|
| | The discussion of variable assignment and reference needs to be |
---|
| | expanded now to include the idea of "lexical namespaces". Think of a |
---|
| | namespace as just being the current context for variables - a way to |
---|
| | group variables together. The idea is that a variable name or |
---|
| | reference is always relative to the current namespace. That is, the |
---|
| | "real" name of the variable is the concatenation of the current |
---|
| | namespace with the variable's name. |
---|
| | |
---|
| | For instance, suppose the current namespace is "MYNS" and we see the |
---|
| | following in a configuration file: |
---|
| | |
---|
| | .nf |
---|
| | Foo = Bar |
---|
| | Baz = [Foo] |
---|
| | .fi |
---|
| | |
---|
| | What this |
---|
| | .B really |
---|
| | means to \*(TC is this: |
---|
| | |
---|
| | .nf |
---|
| | MYNS.Foo = MYNS.Bar |
---|
| | MYNS.Baz = [MYNS.Foo] |
---|
| | .fi |
---|
| | |
---|
| | In other words, \*(TC always prepends the current namespace (plus a |
---|
| | separating period) to any variable assignment or reference it finds in |
---|
| | a configuration. The one exception is when the namespace is "", in |
---|
| | which case \*(TC does not add anything to the variable names. |
---|
| | |
---|
| | By default, the initial namespace is always "", but you can change this |
---|
| | as desired. Note, however, that the programmer can change this default |
---|
| | initial namespace if they wish. Also there is a programming option |
---|
| | (\'AllowNewNamespaces\', described in the previous section) which can limit |
---|
| | you to using only the namespaces the programmer has pre-defined. i.e., You |
---|
| | will not be able to create |
---|
| | .B new |
---|
| | namespaces of your own invention. |
---|
| | |
---|
| | You can change to a new namespace anytime within the configuration file |
---|
| | a number of ways: |
---|
| | |
---|
| | |
---|
| | So how do you access variables in a namespace different than the one |
---|
| | you are in currently? |
---|
| | |
---|
| | |
---|
| | What's the point of all this namespace business anyway? |
---|
| | |
---|
| | |
---|
| | .SS Pre-Defined Variables |
---|
| | |
---|
| | .SS The \'.include\' Directive |
---|
| | |
---|
| | .SS Conditional Directives |
---|
| | |
---|
| | .SS \'.literal\. - Using \*(TC As A Preprocessor For Other Languages |
---|
| | |
---|
| | .SS Type And Value Enforcement |
---|
| | |
---|
| | .SS Lexical Namespaces |
---|
| | |
---|
| | .SH ADVANCED TOPICS |
---|
| | |
---|
| | .SS Guaranteeing A Correct Base Configuration |
---|
| |
---|
| | For the first two installation methods, you must first download the |
---|
| | latest release from: |
---|
| | |
---|
| | .nf |
---|
| | http://www.tundraware.com/Software/tconfpy/ |
---|
| | http://www.tundraware.com/Software/tconfpy/ |
---|
| | .fi |
---|
| | |
---|
| | Then unpack the contents by issuing the following command: |
---|
| | |
---|
| | .nf |
---|
| | tar -xzvf py-tconfpy-X.XXX.tar.gz (where X.XXX is the version number) |
---|
| | tar -xzvf py-tconfpy-X.XXX.tar.gz (where X.XXX is the version number) |
---|
| | .fi |
---|
| | |
---|
| | Win32 users who do not have tar installed on their system can find |
---|
| | a Windows version of the program at: |
---|
| |
---|
| | |
---|
| | Make sure you are logged in as root, then: |
---|
| | |
---|
| | .nf |
---|
| | cd /usr/ports/devel/py-tconfpy |
---|
| | make install |
---|
| | cd /usr/ports/devel/py-tconfpy |
---|
| | make install |
---|
| | .fi |
---|
| | |
---|
| | This is a fully-automated install that puts both code and |
---|
| | documentation where it belongs. After this command has completed |
---|
| | you'll find the license agreement and all the documentation (in the |
---|
| | various formats) in: |
---|
| | |
---|
| | .nf |
---|
| | /usr/local/share/doc/py-tconfpy |
---|
| | /usr/local/share/doc/py-tconfpy |
---|
| | .fi |
---|
| | |
---|
| | The 'man' pages will have been properly installed so either of these |
---|
| | commands will work: |
---|
| |
---|
| | |