CFG2 1.2.042 - Reference


Contents


Command-Line Options

These are the command-line options that you can use. Please note that while these options are "applied" at program start-up, the user-specified switches and configs are applied at a later time. Note that options and settings can be freely mixed - the program can distinguish between them and makes sure that the correct item is applied at the right time.

In fact, a couple of things can force the command-line settings to be applied. Usually, they will be applied when the program has to do its first expression evaluation. However, use of a #SET word will also cause the settings to be applied.

Thus, if there is an error in the set of items cited on the command-line, it may not be reported as an error by the system until is has reached a good way into the script's source files.

-D

This will enable full debugging diagnostic output. You will get lots of drivel from CFG2 if you use this option. Although mainly used for debugging of CFG2 itself, the output can be useful in debugging complicated script files. Note that this option will automatically enable the verbose and source trace options.

-F

This allows you to specify the name of the "root" script file. By default, this name is made up of the root program name plus the default source file extension.

-H

Use this to get a large help "screen" that will give you the main limits and defaults of the program. That help page, rather than this document, should be taken as the "gospel" as far as default and limits are concerned since the page is generated by the program's internal data and constants - it is not just a hard-coded help page.

-P<path_name>

With some CFG2 scripts, especially if they are being used to generate, for example, your system's AUTOEXEC.BAT and CONFIG.SYS files, it can be pretty inconvenient if the program over-writes your current configuration files before you've perfected the script. (Overwriting the previously-mentioned files can, for example, stop your system from re-booting properly.) Therefore, it'd be nice if you could test your scripts in a temporary directory before unleashing it on the real world. That is the purpose of this option - the given path name is added to the start of every file opened or created by the program.

-T

If enabled, this option will make CFG2 display each non-empty line of source as it is processed. If the use of macros causes the expanded line of text to be different from the original, then the expanded version will also be displayed.

-V

The "verbose" option causes the program to emit rather more "comfort" messages than it usually does.

Pre-Defined Items

The following are the items, of whatever sort, that have "special" names. You should consider these names as "reserved words". Therefore, for example, you should not declare a config called "ALWAYS" unless you really want it to be automatically executed. In fact, these pre-defined names will take priority over any item declared with the same name.

ALWAYS

This is the name of the config that is run before any user-specified configurations are applied. Please note that the "always" config is run before the "default" config. (The latter is only run if no command-line items have been specified, of course.)

DEFAULT

This configuration is run if the user has specified no command-line switches or configs at all. Note that this config runs after the "always" config.

CFG2PROGNAME

When cited, this macro will expand as the name and version number of the currently running CFG2 program.

CFG2PARAMS

This macro will expand to contain the switches and configs that the user specified on the command-line. Note that this is not necessarily the same set as those that are currently on.

CFG2SWITCHES

This macro will produce a list of the switches that are currently enabled.

CFG2SOURCE

This macro holds the name of the current source file.

CFG2DATE

Use this when you want to get hold of the current date. The format used is a numeric YYYYMMDD format.

CFG2TIME

Use this when you want to get hold of the current time. The format used is a numeric HHMMSS format.

CFG2DATIM

This macro produces the current date and time. The format is dependant upon the compiler used to compile CFG2. In general, though, it will be in a "plain text" format. (That is, it'll be in a non-numeric format convenient for people to read.)


Directives

CFG2 classes incoming lines of source text as one of two types - either it's "special" or it's "normal". After macro expansion has taken place, the program checks whether the word, starting in the first column of text, is a directive. If it is, then this line of text is special otherwise it is classed as "normal".

Assuming that the current line is special, the word is checked to see if it is a word that affects the IF Stack - viz, one of #IF, #ELSE or #ENDIF. The IF Stack holds the current state of the answer to the "do we want this line?" question. If the word is one of these words, then the state of the IF Stack is altered to reflect the new status.

If the word does not affect the IF Stack, then the state of the IF Stack is interrogated to see whether the line should be discarded. Assuming that we do want this line, then the directive will be de-coded and any necessary actions carried out.

If the current line just contains normal text, then different processing is applied. The first thing that is done is that the state of the IF Stack is, once again, interrogated. If the current state is FALSE, then the line of text is discarded.

Assuming that we do want to process the line, then the first few characters of the line of text are stripped off. (The size of this is set by the #MARGIN word.) If there is an expression in this margin and it evaluates to FALSE then, once again, the line would be discarded. Otherwise, the line of text (minus the text in the margin) is sent to all currently opened output files.

#CLOSE <wildcard_tag_name> ...

Having used the #SEND word to create some file output, one must, of course, have a way to close those files. This is what this word does. Note that, once again, this word accepts a wild-carded tag name and, thus, one can close all files at the same time by using a single asterisk.

Also, please bear in mind that a file can be opened and closed more than once throughout the time that the script runs. This can be used to limit the number of concurrently opened files if this is likely to be a problem.

All files are automatically closed at program termination.

#CONFIG [!]<switch_name> ...

Configs are merely a convenient way of setting a large number of switches without having to use a correspondingly large number of #SET words. When the config's name is cited by use of a #SET word or on the command-line, the corresponding setting (either on or off) is "rippled down" to all the cited switch or config names. Note that it is legal for a config to cite another config name. Hence, one can create a scheme whereby the setting of a single config can have wide-reaching effects upon the setting of all of the system's switches.

There are two special configs. The first is always run at system startup before any user-specified command-line settings are applied. The second is run only if there are no command-line user settings. (Thus, in the latter case, the default setting is applied after the "always run" settings.)

#DATA <tag_name> <input_file_name>

A file declared using the #DATA word operates in a similar manner to a file declared using the word #INCLUD except that, when the file is read, it is not scanned for special directive words - it is treated as a "raw data" file and the file's contents sent to the output stream in its entirety. Note that a file declared using this word must be read using the #READ word.

#DEFINE <macro_name> [<macro_text>]

CFG2's macro facility operate in a similar, though more limited, manner to those of the "C" language. One nominates a tag name and, optionally, a piece of text. When the tag is referenced in the script source, then the macro's text will be substituted instead. Note that after the substitution has been done, the new text is scanned for further macro substitutions.

In order to cite a macro, by the way, you have to enclose the macro tag name in two per-cent signs. If you want a per-cent sign in your output text, then use two together to represent each per-cent sign that you want.

One might wonder why the macro text is an optional parameter. The reason is that macros can be #REDEFined at a later time. Thus, one could #INCLUDe a script that takes parameters in the form of the currently defined set of macros.

#DELETE <wildcard_tag_name> ...

This word is used to delete files - it really will! Note that this word also takes a wild-card tag name and thus can delete multiple files in one command. However, you can only #DELETE files which have been declared using the #FILE word. In other words, you can only #DELETE output files.

If this is a problem, remember that one can declare the same file using different file tags.

#DUMP

This word will dump out all the currently defined macros, switches and configs.

#ELSE

The #ELSE word must follow a corresponding #IF word and be followed by an associated #ENDIF. This word "toggles" the state of the flag that indicates whether the script stream should be "seen" by CFG2. Hence, if the expression of it's #IF evaluates to TRUE, then the text following the #ELSE will be ignored and vice versa.

#EMPTY <integer_number>

By default, CFG2 ignores blank lines in the incoming data stream. In other words, blank lines in the script source are ignored unless a non-zero value is specified with this word. If you do this, then all blank lines encountered will be sent on though to the current set of open output files.

#ENDIF

This word terminates a corresponding #IF or #ELSE.

#ERROR <error_message>

If the program "executes" this word, it will displayed the specified error message text and then behave as if it had detected an error in the script source. It will tell the user which line and source file it is currently reading and then exit returning a "bad" status code to the operating system.

One would use this word to, for example, tell the user that they have selected an illegal combination of "set" switches.

All normal macro processing is done before the message is displayed so one can place macro names in the message text and have it expanded before being displayed on the screen.

#FILE <tag_name> <output_file_name>

The #FILE word associates a file tag with the "real" file name as required by the operating system. The real file name is only ever cited in a #FILE "definition" - elsewhere, the file is only ever referenced by its tag.

The same physical file can be referred to by different tags. For example, files come in three varieties: "output", "input" and "source". One uses the #FILE word to declare output files, the #DATA word to declare input files and the #SOURCE word to declare source files. The point is that CFG2 can generate a source file and read it in, usually with some sort of defined or redefined parameter, at a later stage in the script.

However, in order to to this, one must open it using the #SEND word when generating the file, but use the #INCLUD word when actually executing the file.

#IF <expression>

This word operates in a way akin to the similarly-named "C" construct. The #IF word takes an expression and, if it is TRUE, it will allow the following lines to executed by CFG2. If the expression evaluates to FALSE, then the following lines are ignored. The construct is terminated by a corresponding #ELSE or #ENDIF.

Note that #IF-#ELSE-#ENDIF groups can be nested several deep.

#INCLUD <wildcard_tag_name> ...

Use this word in exactly the same way as the #READ word except that the file read in is treated as if the user had physically included the file using a text editor. Unlike the #READ word, any file that is #INCLUDed will have directives obeyed.

As one might expect, the order of file inclusion depends upon the order in which the files were declared. In general, #INCLUD the files one at a time if the inclusion order is important.

#MARGIN <integer_number>

Use this word to set the width of the CFG2 "margin". Text within this margin is considered not to be part of the text to send to the current set of open files, but as a refuge for the line-by-line switch expression.

Note that a margin value of "one" is valid and will result in all non-special word text being sent to all currently open files.

#RDRUN <system_command_line>

This directive operates in a similar manner to the #RUN word except that it captures the output from the command and #SENDs it to all the currently open output files.

#READ <wildcard_tag_name> ...

Use this word when you want to include raw data into the output stream with no interpretation at all. Thus, any directives in a file that is #READ will be ignored. The file #READ must be an input file.

Just as with the #SEND word, one can #READ more than one file with a single command. Be careful, though as the order in which the files are #READ depends upon the order in which they are declared.

#REDEF <macro_name> [<macro_text>]

This word has the same effect as the #DEFINE keyword except that the cited macro tag name must have already been created. This word allows you to change the value of a macro's text whereas the #DEFINE word allows you to create a macro but will issue a warning if you use it to change the value.

#RUN <system_command_line>

This directive executes an operating system command. For example, one could create a set of files using the CFG2 script language and, at the end of the script, execute a system command that copies them all to some other directory.

A separate directive, #RDRUN, allows you to #RUN a command and capture the resulting output to all currently opened output files.

#SAY <user_message>

This word merely displays the user-supplied text on the screen. Note that as with the #ERROR word, macro names will be expanded before the text is displayed. (This latter fact can be used to your advantage since it allows #SAY to be useful in debugging CFG2 scripts.

#SEND <wildcard_tag_name> ...

This is how you get CFG2 to send data to a file. Following the #SEND directive must be one or more file tag names. Note, though, that they can be wild-card tag names. That is, they can contain question-marks ('?') and asterisks ('*') denoting, respectively, any single character and any sequence of zero or more characters.

Any number of files can have data sent to them subject, of course, to operating system constraints. Files can be opened and closed any number of times. The first time a file is opened, however, it is truncated if it already exists.

#SET [!]<switch_or_config_name> ...

The #SET word is one of three ways that you have of changing the value of a switch. (The other two are by creating a derived switch and by citing a switch or config name on the program command-line.)

If the switch name is cited with no preceding "not" sign, then the cited switch will be altered to the "on" state. If the name is preceded by a "not" sign, then the switch will be turned "off".

Note that one can cite a config name as one of the set of parameters to a #SET command. If you do this, then each item in the config's associated list of names is #SET in turn. Note that if you try to "turn off" a config name, then the "turn off" command will ripple down to all the names cited in the config's list.

#SHOW

Invoking this word will make the program display all the currently set switches. (If you want to see all currently defined switches, use the #DUMP word, instead.)

#SOURCE <tag_name> <source_file_name>

A file declared using this word operates in a similar manner to a file declared using the word #DATA except that, when the file is read, it is scanned for special directive words - in other words it is the CFG2 equivalent to the "C" #INCLUDE construct. Note that a file declared using this word must be read using the #INCLUD word.

#SWITCH [!]<switch_name> [<expression>]

This word is used to declare switches. Switches come in two "flavours": simple and derived.

In order to declare a simple switch, you use the #SWITCH keyword and follow it by a switch name. This will declare a simple switch that is on by default. If the switch name is immediately preceded by a "not" sign (an exclamation mark), then the switch will be off by default. Simple switches can only have their values altered by command-line parameters or by use of the #SET keyword.

Unlike simple switches, "derived" switches can be set neither by use of the #SET word nor by command-line parameters. As its description implies, the switch's value is derived from the supplied switch expression. Derived switch values are re-calculated each time that they are required.

#TAB <integer_number>

This word is used to set the width of the "tab stops" in the source script. CFG2 will expand tabs in the incoming text by replacing them with the correct number of spaces.


Expressions

Reverse Polish Notation (RPN)

There are three parts of the CFG2 syntax that require an "expression" to be entered: the #IF word, #SWITCH word and the margin area of "normal" text lines. In each case, the syntax of these expressions is the same.

The expressions syntax (almost) follows the RPN scheme. In this syntax, the operands come first and the operator comes last. In the case of CFG2, the operands are always non-derived switches. The operators are all Boolean ("logical") operators allowing the normal set of "and", "or" and "not" operations to be carried out upon the operands.

The RPN convention was adopted because it avoids the need for parentheses and operator precedence rules. Also, it's very easy to implement. As the expression is read, operands are placed upon a local stack and operators are applied to the top items of the stack. If at the end of the expression evaluation, you do not end up with a single item upon the stack, then the expression's syntax is at fault. (Similarly, an empty stack and full stack situation signals, respectively, a syntax error and an expression that is too complex to handle.)

There is one slight departure from true RPN convention. If you wish to invert the value of a switch, then one can preceded the tag name by a NOT operator and it will still be applied. The handling of this syntax has to be done by other parts of the program and employing this RPN extension makes simple expressions a little easier to follow.

The operators and associated syntaxes are as follows:

<expression> <expression> &

The AND operator takes the top two items off of the expression stack and does a logical "and" operation upon them. Note that, unlike "C", there is no "short circuiting" of the evaluation process if the first operand taken off of the stack is FALSE.

<expression> <expression> |

The OR operator takes the top two items off of the expression stack and does a logical "or" operation upon them. Note that, unlike "C", there is no "short circuiting" of the evaluation process if the first operand taken off of the stack is TRUE.

<expression> <expression> ^

The XOR operator takes the top two items off of the expression stack and does a logical "exclusive or" operation upon them.

<expression> !

The NOT operator takes the top item off of the expression stack and does a logical "not" operation upon it.

<expression>... JUSTONE

The JUSTONE operator takes all of the items off of the expression stack. It keeps track of how many of the removed items were TRUE. When the last item has been removed, it returns TRUE if the count of TRUE items is exactly one. (This operator is used to valid a set of switches which should all be mutually-exclusive.)

=

Strictly speaking, this is not part of the expression syntax. However, since the only legal place that it can appear is where an expression could, this seams as good a place as any to document it. The same-as-last-expression "operator" is only allowed a the normal text line margin. If it appears, it means "use the results of the last margin expression evaluated". This allows a single margin expression to control the insertion of several lines of output without the need to repeat the same expression.

Example Expression One

The following code will only insert the Sound Blaster stuff if the SB switch is enabled. In addition, if the Sound Blaster code is required and we are configuring the system for games playing (that is, switch GAMES is on), then, in addition, we want the system to set an extra environment variable (viz, DMXOPTIONS) and run the DIAGNOSE and SB16SET programs. Note the use of the "last" operator to remove the need for repeating the two expressions:

        sb              set sound=c:\bin\sb16
        =               set blaster=A220 I5 D1 H5 P330 T6
        =               set midi=synth:1 map:e
        sb games &      set dmxoptions=-phase
        =               c:\bin\sb16\diagnose /s
        =               c:\bin\sb16\sb16set /p /q

The above piece of code could equally well have been written using a couple of IF-ENDIF constructs:

        #if sb
                        set sound=c:\bin\sb16
                        set blaster=A220 I5 D1 H5 P330 T6
                        set midi=synth:1 map:e
        #if games
                        set dmxoptions=-phase
                        c:\bin\sb16\diagnose /s
                        c:\bin\sb16\sb16set /p /q
        #endif  ; GAMES
        #endif  ; SB

Please note the comments placed at the end of the #ENDIF words. When nesting IF-ELSE-ENDIF lines, it's sometimes useful to comment where each sub-construct ends. (Also, notice that, unlike in "C", you cannot indent the keywords.)

Example Expression Two

Let's say that we want to check to make sure that exactly one of two switches is set. We should disallow the situation whereby lines. In each case, the syntax of these expressions is the same.

neither or both are set. This should do the trick:

        #switch !SWA    ; The user *has* to select *one* of the switches
        #switch !SWB    ; to avoid provoking the following error message.
        #if     SWA SWB ^ !
        #error  Either SWA *or* SWB must be set, but not both.
        #endif

Note that the XOR of two operands is TRUE if exactly one of the operands is TRUE.

As usual, an alternative coding is possible. The #IF expression could equally well (although less lucidly) be coded as:

        #if     !SWA SWB &   SWA !SWB &   |   !

Note that when using the syntax of placing the not sign before switch tag names, no intervening space is allowed; if you do put a space after the not sign, it's interpreted as a "stand alone" not sign and will be applied to any preceding expression.


Special Characters

Comments

If you want to embed comments in CFG2 scripts, use the semi-colon character (';'). The text from the semi-colon until the end of the current line is ignored by the program. For example:

        #file   FRED fred.txt ; This is a comment.

If you want a semi-colon character to be treated as any other character, you can "escape" it by using two semi-colons next to each other. CFG2 will treated this pair of characters as a single semi-colon character:

        #file   FRED fred.txt ; This is a comment.
        #send   *
                ; This is a comment and will be ignored.
                ;; These lines are *not* comments and
                ;; will be sent to FRED complete with
                ;; *single* leading semi-colon.

Text Expansion

CFG2 has macro capabilities. Any text between a pair of macro characters - a per-cent sign ('%') - will be "expanded". After this expansion has been done, the process of checking for a macro character is resumed, starting at the first character of the expanded text.

In a similar manner to the comment character, the macro expansion character can be "escaped" by using two per-cent signs next to each other. For example:

        #define AMACRO Some macro text
        #say    The *expanded* macro text is "%AMACRO%".
        #say    The *escaped* macro name is "%%AMACRO%%".

This will produce output similar to:

        CFG2 Version 1.1.194
        The *expanded* macro text is "Some macro text".
        The *escaped* macro name is "%AMACRO%".

The text between the macro characters is checked for a matching name to one of several items. These items are checked in this order:

If a match is found, then the text substitution is done as detailed above.

Line Termination

Usually, the programmer requires that each line of output text be terminated by the standard operating system line termination characters. (For example, under MSdos-compatible systems, this sequence is ASCII CR-LF, 0x0D-0x0A.) Sometimes, one wants to inhibit this behaviour. The special character used for this purpose is the under-line character ('_').

CFG2 only applies special processing to under-lines characters if, after comment stripping has taken place, they are at the end of the CFG2 source line. For example, some of the following lines will provoke CFG2 to inhibit the normal end-of-line termination sequence - some will not:

        #file   BILL bill.txt
        #send   *
                Some text with normal end-of-line characters.
                Some text with the end-of-line inhibited._
                ; In the following line, the under-line isn't
                ; at the *end* of the source line and so the
                ; line will terminate normally:
                Some _ more _ *normal* _ text.

As with the other "special" characters, using two together will inhibit this behaviour. Alas, there is a small limitation of which you must be wary. You can generate normal lines. You can generate lines that have termination inhibited. You can generate lines that have normal termination that end with an under-line (by using two together). However, you can't (easily) generate lines that end with a physical under-line and that have termination inhibited.

Thankfully, so far at any rate, I've never wanted to do this... (I suppose that one could always arrange for the trailing under-line to be generated by the following piece of script instead.)