diff --git a/tconfpy.3 b/tconfpy.3 index 9dfe11c..c6c5395 100644 --- a/tconfpy.3 +++ b/tconfpy.3 @@ -16,7 +16,7 @@ of string-substitution, variable name, conditional, and validation features. -By using \*(TC, you unburden your program from the major responsibility +By using \*(TC, you relieve your program of the major responsibility of configuration file parsing and validation, while providing your users a rich set of configuration features. @@ -24,10 +24,11 @@ information, as well as the value of the current predefined System Variables: +.ft C \" courier .nf python tconfpy.py .fi - +.ft \" revert .SH DOCUMENT ORGANIZATION @@ -68,15 +69,19 @@ brevity, the code examples here assume the following Python import syntax: +.ft C \" Courier .nf from tconfpy import * .fi +.ft \" revert If you prefer the more pedestrian: +.ft C \" Courier .nf import tconfpy .fi +.ft \" revert you will have to prepend all references to a \*(TC object with \'tconfpy.\'. So \'retval=ParseConfig(...\' becomes @@ -93,6 +98,7 @@ to be processed is a required parameter, all the others are optional and default as described below: +.ft C \" Courier .nf from tconfpy import * @@ -103,9 +109,10 @@ Debug=False ) -where: - .fi +.ft \" revert + +where: .TP .B cfgfile (Required Parameter - No Default) @@ -114,32 +121,32 @@ The the name of a file containing configuration information .TP -.B InitialSymTable (Default: {}) +.B InitialSymTable (Default: \fC{}\fP) -A pre-populated symbol table (a Python dictionary). As described +A prepopulated symbol table (a Python dictionary). As described below, this must contain valid \'VarDescriptor\' entries for each symbol in the table. .TP -.B AllowNewVars (Default: True) +.B AllowNewVars (Default: \fCTrue\fP) Allow the user to create new variables in the configuration file. .TP -.B LiteralVars (Default: False) +.B LiteralVars (Default: \fCFalse\fP) -If set to False this option enables variable substitutions within +If set to \fCTrue\fP this option enables variable substitutions within \'.literal\' blocks of a configuration file. See the section in the language reference below on \'.literal\' usage for details. .TP -.B Debug (Default: False) +.B Debug (Default: \fCFalse\fP) -If set to True, \*(TC will provide detailed debugging information +If set to \fCTrue\fP, \*(TC will provide detailed debugging information about each line processed when it returns. .TP @@ -155,27 +162,30 @@ the parser with the name of that file: +.ft C \" Courier .nf retval = ParseConfig("MyConfigFile") .fi +.ft \" revert Assuming your configuration file is valid, \'ParseConfig()\' will return a symbol table populated with all the variables defined in the file and their associated values. This symbol table will have .B only -the symbols defined in that file (plus a few built-in and pre-defined +the symbols defined in that file (plus a few built-in and predefined symbols needed internally by \*(TC). However, the API provides a way for you to pass a "primed" symbol -table to the parser that contains pre-defined symbols/values of +table to the parser that contains predefined symbols/values of your own choosing. Why on earth would you want to do this? There are a number of reasons: .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: +on a predefined variable that only the calling program can know: +.ft C \" Courier .nf .if [APPVERSION] == 1.0 # Set configuration for older application releases @@ -183,6 +193,7 @@ # Set configuration for newer releases .endif .fi +.ft \" revert In this example, only the calling application can know its own version, so it sets the variable APPVERSION in a symbol table @@ -219,6 +230,7 @@ descriptor". Creating new variables in the symbol table involves nothing more than this: +.ft C \" Courier .nf from tconfpy import * @@ -240,12 +252,14 @@ retval = ParseConfig("MyConfigFile", InitialSymTable=MySymTable) .fi +.ft \" revert The heart of this whole business the \'VarDescriptor\' object. It "describes" the value and properties of a variable. These descriptor objects have the following attributes and defaults: +.ft C \" Courier .nf VarDescriptor.Value = "" @@ -256,6 +270,7 @@ VarDescriptor.Min = None VarDescriptor.Max = None .fi +.ft \" revert When \*(TC encounters a new variable in a configuration file, it just instantiates one of these descriptor objects with these defaults for @@ -274,19 +289,19 @@ Each attribute has a specific role: .TP -.B VarDescriptor.Value (Default: Empty String) +.B VarDescriptor.Value (Default: \fCEmpty String\fP) Holds the current value for the variable. .TP -.B VarDescriptor.Writeable (Default: True) +.B VarDescriptor.Writeable (Default: \fCTrue\fP) Sets whether or not the user can change the variable's value. Setting -this attribute to False makes the variable +this attribute to \fCFalse\fP makes the variable .B Read Only. .TP -.B VarDescriptor.Type (Default: TYPE_STRING) +.B VarDescriptor.Type (Default: \fCTYPE_STRING\fP) One of TYPE_BOOL, TYPE_COMPLEX, TYPE_FLOAT, TYPE_INT, or TYPE_STRING. This defines the type of the variable. Each time \*(TC sees @@ -296,15 +311,19 @@ it matches the type declared for that variable. For example, suppose you did this when defining the variable, \'foo\': +.ft C \" Courier .nf VarDescriptor.Type = TYPE_INT .fi +.ft \" revert Now suppose the user puts this in the configuration file: +.ft C \" Courier .nf foo = bar .fi +.ft \" revert This will cause a type mismatch error because \'bar\' cannot be coerced into an integer type - it is a string. @@ -318,6 +337,7 @@ different type declarations: +.ft C \" Courier .nf VarDescriptor.Type VarDescriptor.Value @@ -329,6 +349,7 @@ TYPE_INT Type Error TYPE_STRING \'3+8j\' (A string) .fi +.ft \" revert This is why the default type for newly-defined variables in the configuration file is TYPE_STRING: they can accept @@ -337,7 +358,7 @@ value. .TP -.B VarDescriptor.Default (Default: Empty String) +.B VarDescriptor.Default (Default: \fCEmpty String\fP) This is a place to store the default value for a given variable. When a variable is newly-defined in a configuration file, \*(TC places the @@ -348,7 +369,7 @@ "reset" every variable to its default value if desired. .TP -.B VarDescriptor.LegalVals (Default: []) +.B VarDescriptor.LegalVals (Default: \fC[]\fP) Sometimes you want to limit a variable to a specific set of values. That's what this attribute is for. \'LegalVals\' explictly lists @@ -358,6 +379,7 @@ The exact semantics of LegalVals varies depending on the type of the variable. +.ft C \" Courier .nf Variable Type What LegalVals Does @@ -378,6 +400,7 @@ Example: [r'a+.*', r'^AnExactString$'] .fi +.ft \" revert The general semantic here is "If Legal Vals is not an empty list, the user must assign a value that matches one of the items in LegalVals." @@ -397,6 +420,7 @@ regular expression metacharacters that indicate "Start Of String" and "End Of String" do do this: +.ft C \" Courier .nf des = VarDescriptor() des.LegalVals = [r'^Red$', r'^White$', r'^Blue$'] @@ -404,6 +428,7 @@ SymTable['COLOR'] = des .fi +.ft \" revert .B NOTE: If you want this test to be skipped, then set \'LegalVals\' to an @@ -415,11 +440,12 @@ .TP -.B VarDescriptor.Min and VarDescriptor.Max (Default: None) +.B VarDescriptor.Min and VarDescriptor.Max (Default: \fCNone\fP) These set the minimum and maxium legal values for the variables, but the semantics vary by variable type: +.ft C \" Courier .nf Variable Type What Min/Max Do @@ -432,6 +458,7 @@ String Set Minimum/Maximum string length .fi +.ft \" revert In all cases, if you want either of these tests skipped, set \'Min\' or \'Max\' to the Python None. @@ -458,7 +485,7 @@ 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 +\*(TC is written to use a predefined 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 "". @@ -520,19 +547,23 @@ wish in a configuration file, merely by placing a line in the file in this form: +.ft C \" Courier .nf Varname = Value .fi +.ft \" revert However, you can disable this capability by calling the parser like this: +.ft C \" Courier .nf retval = ParseConfig("myconfigfile", AllowNewVars=False) .fi +.ft \" revert This means that the configuration file can "reference" -any pre-defined variables, and even change their values +any predefined variables, and even change their values (if they are not Read-Only), but it cannot create .B new variables. @@ -567,6 +598,7 @@ construct a variable actually needed by the calling program. For example: +.ft C \" Courier .nf inter1 = Really, really, really, really, long argument #1 @@ -574,10 +606,11 @@ realvar = command [inter1] [inter2] .fi +.ft \" revert If you disable new variable creation you can't do this anymore unless all the variables \'inter1\', \'inter2\', -and \'realvar\' are pre-defined in the initial symbol +and \'realvar\' are predefined in the initial symbol table passed to the parser. @@ -596,6 +629,7 @@ Here is an example: +.ft C \" Courier .nf MyEmail = me@here.com # This defines variable MyEmail @@ -603,14 +637,17 @@ printf("[MyEmail]"); /* A C Statement */ .endliteral .fi +.ft \" revert By default, \'ParseConfig()\' will leave everything within the \'.literal\'/\'.endliteral\' block unchanged. In our example, the string: +.ft C \" Courier .nf printf("[MyEmail]"); /* A C Statement */ .fi +.ft \" revert would be in the list of literals returned by \'ParseConfig()\'. @@ -619,16 +656,20 @@ literal blocks by setting \'LiteralVars=True\' in the \'ParseConfig()\' call: +.ft C \" Courier .nf retval = ParseConfig("myconfigfile", LiteralVars=True) .fi +.ft \" revert In this example, \*(TC would return: +.ft C \" Courier .nf printf("me@here.com"); /* A C Statement */ .fi +.ft \" revert At first glance this seems only mildly useful, but it is actually very handy. As described later in this document, @@ -649,9 +690,11 @@ turned off. To enable debugging, merely set \'Debug=True' in the API call: +.ft C \" Courier .nf retval = ParseConfig("myconfigfile", Debug=True) .fi +.ft \" revert .SS How \*(TC Processes Errors @@ -660,7 +703,7 @@ it adds a descriptive error message into the list of errors returned to the calling program (see the next section). Secondly, in many cases, noteably during conditional processing, it sets the parser -state so the block in which the error occurred is logically False. +state so the block in which the error occurred is logically \fCFalse\fP. This does not happen in every case, however. If you are having problems with errors, enable the Debugging features of the package and look at the debug output. It provides detailed information about what @@ -679,11 +722,13 @@ nothing more than a container to hold return data. In the simplest case, we can parse and extract results like this: +.ft C \" Courier .nf from tconfpy import * retval = ParseConfig("myconfigfile", Debug=True) .fi +.ft \" revert \'retval\' now contains the results of the parse: @@ -757,22 +802,21 @@ 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. + - 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. + - 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 + - Whitespace within both the left- and right-hand-side arguments of a conditional comparison - (\'.if ... == / != ...\') is significant for purposes + (\'.if ... == / != ...\') is significant for purposes of the comparison. -.fi + .IP \(bu 4 Case is always significant except when assigning a value to Booleans @@ -802,16 +846,18 @@ 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 -can pre-define any variables it wishes before processing a +can predefine any variables it wishes before processing a configuration file. You can normally also define your own new variables in the configuration file as desired (unless the programmer has inhibited new variable creation). Variables are assigned values like this: +.ft C \" Courier .nf MyVariable = Some string of text .fi +.ft \" revert 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 @@ -827,7 +873,7 @@ 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 +possible for the applications programmer to predefine 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, @@ -853,6 +899,7 @@ to this is when you are referencing the value of an environment variable. References to environment variables begin with \'$\': +.ft C \" Courier .nf # A reference to an environment variable is legal x = [$TERM] @@ -860,6 +907,7 @@ # Attempting to create a new variable starting with \'$\' is illegal $MYVAR = something .fi +.ft \" revert .IP \(bu 4 Variable names cannot have the \'#\' character anywhere in them @@ -879,9 +927,11 @@ You cannot have a variable whose name is the empty string. This is illegal: +.ft C \" Courier .nf = String .fi +.ft \" revert .IP \(bu 4 The variable named \'NAMESPACE\' is not available for your own @@ -896,9 +946,11 @@ You can get the value of any currently defined variable by "referencing" it like this: +.ft C \" Courier .nf .... [MyVariable] ... .fi +.ft \" revert The brackets surrounding any name are what indicate that you want that variable's value. @@ -906,20 +958,25 @@ You can also get the value of any Environment Variable on your system by naming the variable with a leading \'$\': +.ft C \" Courier .nf ... [$USER] ... # Gets the value of the USER environment variable .fi +.ft \" revert However you cannot set the value of an environment variable: +.ft C \" Courier .nf $USER = me # This is not permitted .fi +.ft \" revert This ability to both set and retrieve variable content makes it easy to combine variables through "substitution": +.ft C \" Courier .nf MYNAME = Mr. Tconfpy @@ -928,6 +985,7 @@ Greeting = Hello [MYNAME], you look great for someone [MYAGE]! .fi +.ft \" revert Several observations are worth noting here: @@ -956,9 +1014,11 @@ adding another line at the end of our example above will change the value of \'Greeting\' to something new: +.ft C \" Courier .nf Greeting = Generic Greeting Message .fi +.ft \" revert In other words, the last assignment statement for a given variable "wins". This may seem sort of pointless, but it actually has great @@ -975,10 +1035,12 @@ of an assignment statement. This means so-called "indirect" variable assignments are permitted: +.ft C \" Courier .nf CurrentTask = HouseCleaning [CurrentTask] = Dad .fi +.ft \" revert To understand what this does you need to realize that before \*(TC does anything with a statement in a configuration file, it @@ -986,9 +1048,11 @@ (or produces an error for references to non-existent variables). So the second statement above is first converted to: +.ft C \" Courier .nf HouseCleaning = Dad .fi +.ft \" revert i.e., The value \'Dad\' is assigned to a (new) variable called \'HouseCleaning\'. In other words, putting a variable reference @@ -998,17 +1062,21 @@ You have to be careful when doing this, though. Consider a similar, but slightly different example: +.ft C \" Courier .nf CurrentTask = House Cleaning # This is fine [CurrentTask] = Dad # Bad! .fi +.ft \" revert The reason this no longer works is that the indirect reference causes the second line to parse to: +.ft C \" Courier .nf House Cleaning = Dad .fi +.ft \" revert This is illegal because whitespace is not permitted in variable names. \*(TC will produce an error if it sees such a construct. As a general @@ -1024,20 +1092,24 @@ reference (relative to the root namespace). This can cause unexpected (though correct) behavior when doing indirect variable access: +.ft C \" Courier .nf NAMESPACE = NS1 foo = .bar # Creates variable NS1.foo with value \'.bar\' [foo] = baz # Means \'[NS1.foo] = baz\' .fi +.ft \" revert The second assignment statement in this example does not do what you might initially think. Remember, \*(TC always does variable dereferencing before anything else, so the second statement becomes: +.ft C \" Courier .nf .bar = baz .fi +.ft \" revert As you'll see in the section on Lexical Namespaces below, this actually means, "Set the variable \'bar\' in the root namespace to the value @@ -1051,12 +1123,14 @@ of \'something\'". See if you understand what the following does (if you don't, try it out with \'test-tc.py\'): +.ft C \" Courier .nf foo = 1 bar = 2 [foo] = bar [bar] = [foo] .fi +.ft \" revert You can get pretty creative with this since variable references can occur pretty much anywhere in an assignment statement. @@ -1064,6 +1138,7 @@ .B within another variable reference. That is, you cannot "nest" references: +.ft C \" Courier .nf # The Following Is Fine @@ -1075,6 +1150,7 @@ [FOO[BAR]] = Something Or Other .fi +.ft \" revert .SS Introducing Lexical Namespaces @@ -1085,6 +1161,7 @@ the options on your car in a configuration file. You might do this: +.ft C \" Courier .nf MyCar.Brand = Ferrari MyCar.Model = 250 GTO @@ -1092,11 +1169,13 @@ # And so on ... .fi +.ft \" revert You'll notice that every variable start with the "thing" that each item has in common - they are features of \'MyCar\'. We can simplify this considerably by introducing a lexical namespace: +.ft C \" Courier .nf [MyCar] @@ -1104,6 +1183,7 @@ Model = 250 GTO Color = Red .fi +.ft \" revert The first statement looks like a variable reference, but it is not. .B A string inside square brackets by itself on a line introduces a namespace. @@ -1159,7 +1239,7 @@ It helps enforce correct configuration files. By default, you can introduce new namespaces into the configuration file any time you like. However, as described in the previous section on the \*(TC API, -the application programmer can limit you to a pre-defined set of legal +the application programmer can limit you to a predefined set of legal namespaces (via the \'LegalVals\' attribute of the \'NAMESPACE\' variable descriptor). By doing this, the programmer is helping you avoid incorrect configuration file entries by limiting just which @@ -1187,20 +1267,24 @@ .IP \(bu 4 There two ways to change to a new namespace: +.ft C \" Courier .nf [NewNameSpace] # Must appear on a line by itself or with a comment only OR NAMESPACE = NewNamespace .fi +.ft \" revert If, at any point, you want to return to the root namespace, you can use one of these two methods: +.ft C \" Courier .nf [] OR NAMESPACE = .fi +.ft \" revert So, why are there two ways to do the same thing? The first way is the more common, and the more readable way to do it. It appears on a line by itself and @@ -1210,19 +1294,23 @@ Suppose you want to change the namespace in a way that depends on the value of another variable. For instance: +.ft C \" Courier .nf LOCATION = Timbuktu NAMESPACE = [LOCATION]-East .fi +.ft \" revert In other words, the second form of a namespace change allows you to employ the \*(TC string substitution and variable referencing features. Bear in mind that \*(TC is case-sensitive so this will not work as you expect: +.ft C \" Courier .nf Namespace = something .fi +.ft \" revert This just set the value of the variable \'Namespace\' to \'something\' and has nothing whatsoever to do with lexical @@ -1235,24 +1323,28 @@ For example, both of the following will cause an error: +.ft C \" Courier .nf [$FOO] OR x = $FOO NAMESPACE = [x] .fi +.ft \" revert .IP \(bu 4 By default, all variable assignments and references are .B relative to the currently active namespace: +.ft C \" Courier .nf [MyNameSpace] foo = 123 # Creates a variable called \'MyNameSpace.foo\' x = bar # Means: \'MyNameSpace.x = MyNameSpace.bar\' .fi +.ft \" revert .IP \(bu 4 If you want to set or reference a variable in a namespace different @@ -1263,6 +1355,7 @@ of that variable. (This is called the "fully qualified variable name".) For example: +.ft C \" Courier .nf [NS1] # Switch to the namespace \'NS1\' foo = 14 # Creates \'NS1.foo\' @@ -1270,6 +1363,7 @@ [NS2] # Switch to the \'NS2\' namespace foo = [.NS1.foo] # Sets \'NS2.foo = 14\' .fi +.ft \" revert There is another clever way to do this without using the escape character. \*(TC has no understanding whatsoever of what a @@ -1282,6 +1376,7 @@ without any escape character any time you are in the root namespace: +.ft C \" Courier .nf [NS1] # Switch to the namespace \'NS1\' foo = 14 # Creates \'NS1.foo\' @@ -1289,6 +1384,7 @@ [] # Switch to the root namespace foo = [NS1.foo] # Sets \'foo = 14\' - no escape needed .fi +.ft \" revert .IP \(bu 4 \*(TC keeps track of every new namespace you create and returns that @@ -1304,17 +1400,21 @@ repository of the current lexical namespace. This means you can use the value of NAMESPACE in your own string substitutions: +.ft C \" Courier .nf MyVar = [NAMESPACE]-Isn't This Cool? .fi +.ft \" revert You can even use the current value of NAMESPACE when setting a new namespace: +.ft C \" Courier .nf NAMESPACE = [NAMESPACE]-New .fi +.ft \" revert One final, but very important point is worth noting here. The \'NAMESPACE\' variable itself is always understood to be @@ -1326,13 +1426,15 @@ is understood to be relative to the root namespace. That's why things like this work: +.ft C \" Courier .nf [MyNewSpace] x = 100 # MyNewSpace.x = 100 y = [NAMESPACE]-1 # MyNewSpace.y = MyNewSpace-1 NAMESPACE = NewSpace # .NAMESPACE = NewSpace -fi +.fi +.ft \" revert .SS Predefined Variables @@ -1352,6 +1454,7 @@ one configuration file that works on both Unix and Windows operating systems. The System Variables are: +.ft C \" Courier .nf Variable Name Contains ------------- -------- @@ -1371,12 +1474,14 @@ .PYTHONVERSION - The version of Python in use. .fi +.ft \" revert By combining these System Variables as well as the content of selected Environment Variables, you can create complex conditional configurations that "adapt" to the system on which a Python application is running. For example: +.ft C \" Courier .nf .if [.MACHINENAME] == foo.bar.com @@ -1388,6 +1493,7 @@ .endif .fi +.ft \" revert The other kind of predefined variables are called "Reserved Variables". \*(TC understands a number of symbols as part of its own language. @@ -1399,6 +1505,7 @@ own purposes and have \*(TC ignore them. The Reserved Variables give you a way to do this. The Reserved Variables are: +.ft C \" Courier .nf Variable Name Contains ------------- -------- @@ -1421,32 +1528,38 @@ NOTEQUIV != PERIOD . -.nf +.fi For instance, suppose you wanted to include the \'#\' symbol in the value of one of your variables. This will not work, because \*(TC interprets it as the beginning of a comment, which is not what you want: +.ft C \" Courier .nf MyJersey = Is #23 .fi +.ft \" revert So, we use one of the Reserved Variables to get what we want: +.ft C \" Courier .nf MyJersey = Is [HASH]23 .fi +.ft \" revert One word of warning, though. At the end of the day, you still have to create variable names or namespace names that are legal. You can't "sneak" illegal characters into these names using Reserved Variables: +.ft C \" Courier .nf foo = [DOLLAR]MyNewNamespace # No problem NAMESPACE = [foo] # No way - namespace cannot start with $ .fi +.ft \" revert @@ -1474,11 +1587,13 @@ variable "Foo" to be a floating point number, and that it must have a value between -10.5 and 100.1. In that case: +.ft C \" Courier .nf Foo = 6.023E23 # Error - Value is out of range Foo = MyGoodness # Error - Value must be a FP number, not a string Foo = -2.387 # Good - Value is both FP an in range .fi +.ft \" revert .SS What Specific Validations Are Available? @@ -1560,20 +1675,23 @@ For instance, suppose the programmer defines variable \'Foo\' to be floating point. Then: +.ft C \" Courier .nf Foo = 1.23 Bar = Value is [Foo] # Creates a new *string* variable with the # value: "Value is 1.23" .fi +.ft \" revert In other words, variable values are "coerced" into strings for the purposes of substitution and conditional testing within a configuration file. This is primarily an issue with the conditional comparisons below. For example, the following -conditional is False because the string representations of the +conditional is \fCFalse\fP because the string representations of the two numbers are different. Assume \'f1\' and \'f2\' have been defined as floating point variables by the calling program: +.ft C \" Courier .nf f1 = 1.0 f2 = 1.00 @@ -1581,6 +1699,7 @@ .if [f1] == [f2] # False because "1.0" is not the same string as "1.00" ... .fi +.ft \" revert .IP \(bu 4 @@ -1608,13 +1727,12 @@ .SS Some Further Notes On Boolean Variables One last note here concerns Boolean variables. Booleans are actually -stored in the symbol table as the Python boolean values, -.B True -or -.B False. +stored in the symbol table as the Python boolean values, \fCTrue\fP or +\fCFalse\fP. However, \*(TC accepts user statements that set the value of the boolean in a number of formats: +.ft C \" Courier .nf Boolean True Boolean False @@ -1626,6 +1744,7 @@ foo = On foo = Off .fi +.ft \" revert This is the one case where \*(TC is insensitive to case - "tRUE", "TRUE", and "true" are all accepted, for example. @@ -1634,8 +1753,9 @@ If the user wants to do a conditional test on the value of a boolean they .B must -observe case and test for either \'True\' or \'False\': +observe case and test for either \fCTrue\fP or \fCFalse\fP: +.ft C \" Courier .nf boolvar = No @@ -1643,9 +1763,10 @@ .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 + .if [boolvar] == Off # Neither does this - Only True/False can be tested .fi +.ft \" revert .SS The \'.include\' Directive @@ -1653,19 +1774,23 @@ At any point in a configuration file, you can "include" another configuration file like this: +.ft C \" Courier .nf .include filename .fi +.ft \" revert In fact, you can use all the variable substitution and string concatenation features we've already discussed to do this symbolically: +.ft C \" Courier .nf Base = MyConfig Ver = 1.01 .include [Base]-[Ver].cfg .fi +.ft \" revert The whitespace after the \'.include\' directive is mandatory to separate it from the file name. You can have as many \'.include\' statements in your configuration file @@ -1691,6 +1816,7 @@ can make them available as a common configuration. You then \'.include\' that file and override any options you like: +.ft C \" Courier .nf # Get the standard options .include /usr/local/etc/MyAppStandardConfig.cfg @@ -1699,6 +1825,7 @@ ScreenColor = Blue Currency = Euros .fi +.ft \" revert This makes maintenance of complex configuration files .B much @@ -1744,6 +1871,7 @@ The general structure of any conditional looks like this: +.ft C \" Courier .nf ConditionalDirective Argument(s) @@ -1755,6 +1883,7 @@ .endif # Required .fi +.ft \" revert Except for the whitespace after the conditional directive itself, whitespace is not significant. You may indent as you wish. @@ -1762,6 +1891,7 @@ Conditionals may also be "nested". You can have a conditional within another conditional or \'.else\' block: +.ft C \" Courier .nf ConditionalDirective Argument(s) @@ -1782,6 +1912,7 @@ ending stuff .endif .fi +.ft \" revert There are no explicit limits to how deeply you can nest a configuration. However, you must have an \'.endif\' that terminates each conditional test. Bear in mind that @@ -1832,6 +1963,7 @@ There are three Existential Conditionals: \'.ifall\', \'.ifany\', and \'.ifnone\'. Each has the same syntax: +.ft C \" Courier .nf ExistentialDirective varname ... included if test was True @@ -1840,6 +1972,7 @@ included if test was False .fi +.ft \" revert .fi In other words, existential conditionals require one or more @@ -1853,6 +1986,7 @@ The three forms of existential conditional tests implement three different kinds of logic: +.ft C \" Courier .nf .ifall var1 var2 ... @@ -1869,9 +2003,11 @@ This is a logical "NOR" operation. It is True only if NONE of the variables, \'var1\', \'var2\' ... exist. .fi +.ft \" revert Here is an example: +.ft C \" Courier .nf F00 = 1 BAR = 2 @@ -1889,12 +2025,14 @@ z=3 .endif .fi +.ft \" revert When \*(TC finishes processing this, x=1, y=2, and z=0. You can also use references to environment variables in an existential conditional test: +.ft C \" Courier .nf .ifany $MYPROGOPTIONS options = $MYPROGOPTIONS @@ -1904,6 +2042,7 @@ .endif .fi +.ft \" revert You can also use variable references here to get the name of a varible to test by "indirection" (as we saw in the previous section on @@ -1911,6 +2050,7 @@ way of doing things because it can be kind of obscure to understand, but it is possible to do this: +.ft C \" Courier .nf foo = MyVarName @@ -1919,6 +2059,7 @@ ... .endif .fi +.ft \" revert This will test to see if either the variable \'MyVarName\' exists. You can also do indirection through an environment variable, but @@ -1928,11 +2069,13 @@ configuration file. Say the \'TERM\' environment variable is set to \'vt100\': +.ft C \" Courier .nf .ifany [$TERM] ... .endif .fi +.ft \" revert This will test to see if a variable called \'vt100\' exists in the symbol table. This is, perhaps, not the clearest possible construct! @@ -1942,10 +2085,12 @@ There are two Comparison Conditionals: +.ft C \" Courier .nf .if string1 == string2 # True if string1 and string2 are identical .if string1 != string2 # True if string1 and string2 are different .fi +.ft \" revert As a general matter, you can put literal strings on both sides of such a test, but the real value of these tests comes when you use variable @@ -1955,6 +2100,7 @@ It is the variable's value that is replaced in the string to test for equality or inequality: +.ft C \" Courier .nf MyName = Tconfpy @@ -1965,12 +2111,14 @@ MyAge = Unknown .fi +.ft \" revert .fi These are particularly useful when used in combination with the \*(TC Predefinded Variable or environment variables. You can build configurations that "sense" what system is currently running and "adapt" accordingly: +.ft C \" Courier .nf AppFiles = MyAppFiles @@ -1986,6 +2134,7 @@ ErrorMessage = I don't know what kind of system I am running! .endif .fi +.ft \" revert .SS The \'.literal\. Directive @@ -1997,11 +2146,13 @@ to the calling program without comment by using the \'.literal\' directive. It works like this: +.ft C \" Courier .nf .literal This is literal text that will be passed back. .endliteral .fi +.ft \" revert This tells \*(TC to ignore everything between \'.literal\' and \'.endliteral\' and just pass it back to the calling program (in \'retval.Literals\' - see @@ -2012,6 +2163,7 @@ pass them back to the calling program. This is especially handy when used in combination with \*(TC conditional features: +.ft C \" Courier .nf .if [.OSTYPE] == posix .literal @@ -2025,6 +2177,7 @@ .endif .fi +.ft \" revert In other words, we can use \*(TC as a "preprocessor" for other text or @@ -2040,6 +2193,7 @@ literal text that is returned to the calling program. Here is how it works: +.ft C \" Courier .nf .ifall $USER @@ -2060,6 +2214,7 @@ } .endliteral .fi +.ft \" revert If the calling program sets \'LiteralVars=True\', the literal block will return a C program that prints the greeting defined at the top of @@ -2100,6 +2255,7 @@ .IP \(bu 4 Probably the most common problem is attempting to do this: +.ft C \" Courier .nf foo = bar @@ -2107,6 +2263,7 @@ ... .endif .fi +.ft \" revert But this will not work. \*(TC is very strict about requiring you to explicity distinguish between @@ -2120,6 +2277,7 @@ What you probably want is to compare the value of variable \'foo\' with some string: +.ft C \" Courier .nf foo = bar @@ -2127,6 +2285,7 @@ ... .endif .fi +.ft \" revert Now you're comparing the .B value @@ -2137,18 +2296,23 @@ inferring it from context), you can mix both literal text and variable values on either side of a comparison or assignment: +.ft C \" Courier .nf foo = bar foo[foo]foo = bar # Means: foobarfoo = bar .if foo[foo] == foobar # Means: .if foobar == foobar +.fi +.ft \" revert + .IP \(bu 4 Namespaces are a handy way to keep configuration options organized, especially in large or complex configurations. However, you need to keep track of the current namespace when doing things: +.ft C \" Courier .nf foo = bar .... @@ -2158,11 +2322,13 @@ .if [foo] == something # Checks value of NS-NEW.foo - will cause error # since no such variable exists .fi +.ft \" revert .IP \(bu 4 Remember that "last assignment wins" when setting variable values: +.ft C \" Courier .nf myvar = 100 @@ -2170,6 +2336,7 @@ myvar = 200 .fi +.ft \" revert At the end of all this, \'myvar\' will be set to 200. This can be especially annoying if you \'.include\' a configuration file after @@ -2213,9 +2380,11 @@ options set to "sane" values. All the user has to do is create a configuration file with one line in it: +.ft C \" Courier .nf .include /wherever/the/standard/config/file/is .fi +.ft \" revert .IP \(bu 4 Predefine every option variable the program will support. Populate the @@ -2250,7 +2419,7 @@ parser. Next, have your program decide which options the current user is permitted to change. Finally, mark all the options they may not change as "Read Only", by setting the "Writeable" attribute for those -options to "False". Now call the parser. +options to \fCFalse\fP. Now call the parser. This general approach allows you to write programs that support a wide range of options which are enablde/disabled on a per-user, @@ -2270,6 +2439,7 @@ table it needs to, and passes it back to the parser for another "go". You can keep doing this as often as needed. For instance: +.ft C \" Courier .nf # Program calls the parser with PASS set to 1 @@ -2288,7 +2458,8 @@ .endif # And so on -.if +.fi +.ft \" revert In fact, you can even make this iterative parsing "goal driven". The program can keep calling the parser, modifing the results, @@ -2320,22 +2491,28 @@ For the first two installation methods, you must first download the latest release from: +.ft C \" Courier .nf http://www.tundraware.com/Software/tconfpy/ .fi +.ft \" revert Then unpack the contents by issuing the following command: +.ft C \" Courier .nf tar -xzvf py-tconfpy-X.XXX.tar.gz (where X.XXX is the version number) .fi +.ft \" revert Win32 users who do not have tar installed on their system can find a Windows version of the program at: +.ft C \" Courier .nf http://unxutils.sourceforge.net/ .fi +.ft \" revert .SS Install Method #1 - All Systems (Semi-Automated) @@ -2344,9 +2521,11 @@ Enter the directory created in the unpacking step above. Then issue the following command: +.ft C \" Courier .nf python setup.py install .fi +.ft \" revert This will install the \*(TC module and compile it. @@ -2362,25 +2541,31 @@ manually copy the tconfpy.py file to a directory somewhere in your PYTHONPATH. The recommended location for Unix-like systems is: +.ft C \" Courier .nf .../pythonX.Y/site-packages .fi +.ft \" revert For Win32 systems, the recommended location is: +.ft C \" Courier .nf ...\\PythonX.Y\\lib\\site-packages .fi +.ft \" revert Where X.Y is the Python release number. -You can pre-compile the \*(TC module by starting Python interactively +You can precompile the \*(TC module by starting Python interactively and then issuing the command: +.ft C \" Courier .nf import tconfpy .fi +.ft \" revert Manually copy the 'test-tc.py' program to a directory somewhere in your executable path. Copy the documentation @@ -2392,27 +2577,33 @@ Make sure you are logged in as root, then: +.ft C \" Courier .nf cd /usr/ports/devel/py-tconfpy make install .fi +.ft \" revert 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: +.ft C \" Courier .nf /usr/local/share/doc/py-tconfpy .fi +.ft \" revert The 'man' pages will have been properly installed so either of these commands will work: +.ft C \" Courier .nf man tconfpy man test-tc .fi +.ft \" revert .SS Bundling \*(TC With Your Own Programs @@ -2421,15 +2612,15 @@ that the end-users have it installed on their systems. There are two ways to do this: -1) Tell them to download and install the package as described above. - This is not recommended since you cannot rely on the technical - ability of end users to do this correctly. +.IP \(bu 4 +Tell them to download and install the package as described above. +This is not recommended since you cannot rely on the technical +ability of end users to do this correctly. -2) Just include 'tconfpy.py' in your program distribution directory. - This ensures that the module is available to your program - regardless of what the end-user system has installed. - - +.IP \(bu 4 +Just include 'tconfpy.py' in your program distribution directory. +This ensures that the module is available to your program +regardless of what the end-user system has installed. .SH THE \*(TC MAILING LIST @@ -2441,9 +2632,11 @@ with a single line of text in the body (not the Subject line) of the message: +.ft C \" Courier .nf subscribe tconfpy-users your-email-address-goes-here .fi +.ft \" revert You will be notified when your subscription has been approved. You will also receive detailed information about how to use the list,