CASM DOCUMENTATION ================== by Laszlo Gerencser casm@mailbox.hu http://casm.portologic.hu Updated on 2003.11.24 CASM is a free, commandline content assembler for UNIX written entirely in Perl5. CASM is primarily designed to help you when dealing with complex content structures. It is extremely cool when you have to create and maintain large web/wap sites, but it can be used to other purposes as well. It has a powerful wildcard matching feature that helps you to intuitively compile your *.cal files to *.html or *.cal* files to *.* and so on. Features: - recursive filehandling - target directory structure building - powerful and intuitive file name wildcarding (not the UN*X way) - a great content description language called CAL - content compiling with embedded CAL compiler - embedded PERL scripting - HTML/CSS/XML optimizing - HTML syntax checking with HTML Tidy integration - great multilingual site support - in-depth documentation and more... CASM is designed with the following rules in mind: - you know what you want - you know what you are doing - you hate doing repetitive tasks manually (you are lazy) - you know extremely well the language you use, so you don't want a utility that modifies your code or 'corrects' your 'errors' - you can write primitive shell scripts - you use a POSIX-compatible operating system If these rules don't suit you, then forget CASM and try a WYSIWYG HTML editor in Windows. ################# # INSTALLATION # ################# 1. Just put casm in your /bin or /usr/bin or /usr/local/bin directory, or somewhere else where you can run it. 2. Check the first line of the casm file and modify #!/usr/bin/perl -w if perl is not in /usr/bin/ on your system. 3. Enjoy! ########### # USAGE # ########### casm [] s: t:[] -C Compiles CAL commands. If set, CASM will compile all CAL commands found in source files. Example: casm -C s:mysourcefile t:mytargetfile will open mysourcefile, compile all CAL commands in it and write result to mytargetfile. -H Optimizes output as HTML/XML. If set, CASM will optimize source files as HTML/XML files. The optimization method is safe for SSI, JavaScript, PDDDL, CAL and other HTML-embedded stuff. Optimization does not affect browser rendering in any way. Example: casm -H s:*.html t:./optimized/*.* will read all *.html files in the current directory, optimize them, and write the optimized files at the ./optimized directory with the same name as source files have. -T Validates target files with HTML Tidy. In case of errors/warnings CASM will leave a Tidy log file named to .tidylog This file contains all Tidy warning, error and other messages that tidy sends when called with -qe. Tidy runs last in the processing order so all other manipulations (as -H or -C) will precede it. You need an installed HTML Tidy to use this feature. You can download it from W3C at the following URL: http://www.w3.org/People/Raggett/tidy/ Example 1: casm -T s:*.html t:./checked/*.* will copy all *.html files from current directory to ./checked and will check all copied files with HTML Tidy. Example 2: casm -HT s:*.html t:./checked/*.* will copy and optimize all *.html files from current directory to ./checked and will check all optimized files with HTML Tidy. -R Recurses directories (default is no recurse). If set, CASM will find source files recursively in all subdirectories of the source directory, and will create target files into the appropriate subdirectories. Example: casm -CHR s:./readable/*.html t:./optimized/*.* will read all *.html files in ./readable and its subdirectories, compile CAL commands, optimize files as HTML/XML and write result to ./optimized with the same name and subdirectory path as source files have. To be clear: ./readable/mypics/family/daddy.html will be written to ./optimized/mypics/family/daddy.html in our example. -D Deletes old target files from target directory first of all. Deletes all files matching to target mask in the target dir. Deletes recursively if -R is specified. (Default is do not delete anything.) Use with caution! This feature can delete all of your files at once!!! Example 1: casm -DH s:*.html t:./optimized/* will delete all files in ./optimized directory, read all *.html files in the current directory, optimize them, and write the optimized files at the ./optimized directory with the same name as source files have. Example 2: casm -DCHR s:./readable/*.html t:./optimized/*.html will delete *.html files in ./optimized directory and its subdirectories, read all *.html files in ./readable and its subdirectories, compile CAL commands, optimize files as HTML/CSS and write result to ./optimized with the same name and subdirectory path as source files have. -V Verbose mode: all messages will be displayed (default is quiet). Recommended for general usage!!! -t Test mode: displays what CASM should do (but doesn't do it). Recommended with -V to test the effects of parameter settings!!! Example: casm -DCHRVt s:./readable/*.html t:./optimized/*.html will display all messages but don't deletes, reads or writes anything. -d Displays this DOCUMENTATION. -v Displays version info and exit. -c Displays copyright info and exit. -h, -? Displays help message and exit. Directory path and name (mask) of source file(s) to process. If -R switch is used, CASM will search the source directory recursively for files matching the source file name (mask). The file mask is interpreted in the following way: - * means all files - *.* means all files with extension (there is a dot in the filename somewhere after the first character) - * in the mask means any number of any character - ? in the mask means one character Extension: substring of the file name after the last dot. If there is no dot in the filename, or the only dot is the first character, then the file has no extension. Example: s:* means all files in the current directory s:develop/html/mysite/*.html means all *.html files in the develop/html/mysite directory -R s:develop/html/mysite/*.html means all *.html files in the develop/html/mysite directory and all of its subdirectories Directory path and name (mask) of target file(s) to create. CASM will put target files into the target directory. If a file name (mask) is specified, CASM will name target file(s) according to the name (mask). If not specified, the default '*' will be applied. When -R switch is used, CASM will analyze the subdirectory structure of source files relative to the given source directory path (in the parameter) and creates the required subdirectory structure into the given target directory (in this parameter). Then it puts target file(s) to the appropriate subdirectory. The file mask is interpreted in the following way: - * or *.* leaves filename as is (target filename = source filename) - *. cuts extension from source filename - all * and ? in the mask will be substitued according the wildcards of source mask (* and ? are equivalent here). Example: s:t?_*.t* t:?_*.* converts ta_menu.thtml to a_menu.html tb_page.tcss to b_page.css and so on. s:*.* t:*. converts blabla.txt to blabla, index.html.cal to index.html and so on. Just try it! It's easy! ##################################### # THE CONTENT ASSEMBLING LANGUAGE # ##################################### CASM implements the Content Assembling Language (CAL). CAL is a markup language to define and assemble contents of files. The main purpose of this language is to enable authors to separate content from display properties and to eliminate redundancy. With CAL you can define content and presentation in separate files. If you define something once, you can use it any time you want. CAL is extremely useful when you create HTML/WML/XML content, but can be used to create monolithic Perl scripts (as CASM) or anything you want. CAL has variables, loops, if-then-else, templates, include commands, embedded PERL scripting, and so on. A CAL command is a sequence of tokens starting with the CAL prefix token and ending with CAL postfix token. Middle tokens are delimited by CAL delimiter. The default syntax element values are the followings: - prefix token: "<~" and zero or more whitespace (one whitespace is recommended) - postfix token: zero or more whitespace and "~>" (one whitespace is recommended) - delimiter: one or more whitespace. So a CAL command may be the following: <~ INCLUDE FILE myfile ~> Why do casm uses the <~ ~> pre/postfix tokens? Because these don't interfere with commonly used markup and scripting language syntaxes as XML/HTML/SGML/ Javascript/Perl/PHP/ASP/SSI/CSS and so on. So no constructs in these languages will be regarded as CASM commands. A CAL command can contain any number of whitespaces (space, tab and newline characters) where its definition contains a space. CAL is not line-oriented so there are no restrictions using or not using new lines within it. You can write <~ INCLUDE FILE myfile ~> or <~ INCLUDE FILE myfile ~> or <~ INCLUDE FILE myfile ~> or you can write the whole file in one line, it's up to you. All legal whitespaces inside of a CAL command will be omitted. But don't write whitespaces at unallowed positions. Examples: <~ INC LUDE FILE myfile ~> is an invalid command. <~ INCLUDE FILE my file ~> means that the file name is "my", CAL commands are often written indented in new lines to enhance readability. CAL commands can (and will) cover a line break and/or an indent (with tabs and spaces) before and after the command itself. This means that the following commands are identical: blabla<~ INCLUDE FILE myfile ~>bla blabla <~ INCLUDE FILE myfile ~> bla blabla <~ INCLUDE FILE myfile ~> bla When you want to add a line break before or after a CAL command, you should use two line breaks. For example: ---------------------------------------------------------------------------- blabla <~ INCLUDE FILE myfile ~> bla ---------------------------------------------------------------------------- will be compiled to ---------------------------------------------------------------------------- blabla myfile_content bla ---------------------------------------------------------------------------- Note: If you use binary content with CASM, don't forget to add a newline before and after each and every CAL command. CAL is strictly case sensitive. CAL has no reserved words but you cannot use whitespaces or CAL postfix token in the identifiers of a CAL command (e.g:variable names, field names, file names, menu names, and so on). You cannot use CAL prefix token as a part of your content. If you have to do that, you must exclude that part of the content from compilation, using the EXCLUDE or the INCLUDE RAWFILE command. If the compiler finds some syntactic or semantic errors during compilation, it will send you an error message and continues compilation. The buggy part of your CAL code will be copied into the target file untouched. This compiler never gives up (as I hope), so check compiler messages every time. Since CAL is not a line-oriented language, the compiler will not display line numbers in error messages. It will display the buggy part of your code within square brackets. The compiled code containing unsuccesfull CAL commands will help you debugging your CAL source. Well, let's see those CAL commands now... Note: The following section uses HTML examples for comprehensibility. But don't limit your imagination to HTML. You can use CAL in any other environments (eg.: Perl, PHP, RTF, XML, plain text, binary data and so on). SIMPLE FILE INCLUDE <~ INCLUDE FILE myfile ~> Implements a standard file include function: CASM will replace this command with the content of the file named "myfile" ("myfile" can contain path information in an absolute or relative form or a simple filename). CASM will process the included files recursively so it is possible to include a file into an include file - there is no depth limit to do that. Warning!!! This feature (as CASM itself) is not foolsafe: if you define an infinite recursive loop, CASM will process it infinitely while it has enough resources to do it. Remember: CASM will not think on behalf of you. This is not a bug, this is a feature. Tip: Using a newline character at the beginning and the end of include files in HTML/XML environment is a good idea. The result will be more readable. RAW FILE INCLUDE <~ INCLUDE RAWFILE myfile ~> Implements a raw file include function: CASM will replace this command with the content of the file named "myfile" ("myfile" can contain path information in an absolute or relative form or a simple filename). The difference between the file include and the raw file include is that the included raw file will not processed by the compiler, it will be included as it is. Therefore it is impossible to recursively include raw files. MENU INCLUDE <~ INCLUDE MENU menufile ~> <~ INCLUDE MENU menufile menuitem ~> Includes a menu from a file. "menufile" - the name of the file containing the menu. "menuitem" - the name of the current menu item (optional) Current menu item will be included in OFF state, all others will be included in ON state. If no "menuitem" given, all menu item will be included in ON state. MENU FILE - THE MENU DEFINITION The menu file contains menu item definitions. A menu item definition consist of the ON state and OFF state of the menu item with the following syntax: <~ MENU menuitem ON ~> menuitem_ON_definition <~ MENU menuitem OFF ~> menuitem_OFF_definition <~ MENU menuitem END ~> (A line break before and after menuitem definitions is permitted but not needed. If you use line break there, the line break and the preceeding/following spaces and tabulators will be considered as a part of the MENU command, not of the definition, therefore will not be compiled. If you want to add a newline to the end of your menuitem definition, use double line breaks.) Only menu files can contain menu definitions. If you try to define menu in other files, the compiler will consider it as invalid CAL commands. Example HTML menu file: menu.html ---------------------------------------------------------------------------- <~ MENU search ON ~> Search <~ MENU search OFF ~> Search <~ MENU search END ~> | <~ MENU help ON ~> Help <~ MENU help OFF ~> Help <~ MENU help END ~> | <~ MENU faq ON ~> F.A.Q. <~ MENU faq OFF ~> F.A.Q. <~ MENU faq END ~> | <~ MENU feedback ON ~> Feedback <~ MENU feedback OFF ~> Feedback <~ MENU feedback END ~> ---------------------------------------------------------------------------- Using this example, the command <~ INCLUDE MENU menu.html faq ~> will be replaced with the following: ---------------------------------------------------------------------------- Search | Help | F.A.Q. | Feedback ---------------------------------------------------------------------------- Of course, menu files can contain any type of other data, even other CAL commands, the compiler can handle all of them. Possibilities are endless. For example you can define a part of a menu using INCLUDE FILE command, or you can define multilevel menus by defining a menuitem as a menu with the following structure: <~ MENU menuitem ON ~>menuitem_ON_definition <~ MENU menuitem OFF ~> <~ INCLUDE MENU submenufile ~> <~ MENU menuitem END ~> Tip: Using a newline character at the beginning and the end of and menu files in HTML/XML environment is a good idea. The result will be more readable. CHARACTER SUBSTITUTION <~ \mycode ~> You can use handy escape codes to insert characters into your content. The following codes can be used: \s - space \t - tab \n - newline (as Perl interprets it on the given platform) \r - return \f - form feed \b - backspace \a - alarm (bell) \e - escape \dnn - character which has decimal code nn \xnn - character which has hexadecimal code nn \onn - character which has octal code nn For example the following sequence will be compiled to four spaces: <~\s~><~\d32~><~\x20~><~\o40~> VARIABLE DEFINITION <~ DEF VAR myvar myvalue ~> <~ DEF VAR myvar ~>myvalue<~ DEF VAR myvar END ~> <~ DEF VAR myvar ~> myvalue <~ DEF VAR myvar END ~> These commands define a variable named "myvar" and assigns the "myvalue" value to it. Both syntax can be used. You cannot use CAL delimiter (whitespace by default) in the variable name and in the value when first form of variable definition is used. There is no such restriction for the value using the second form. You can use even CAL commands as a part of a variable value in this case - the compiler will handle it right. A later variable definition with the same variable name alters the value of the variable. Variable scopes: The defined variable is accessible: - in the file or DEF block, where it is defined, - in the file or DEF block, where the file defining the variable is included by INCLUDE FILE or INCLUDE VARS, - in included files or menus at all levels up from the place of the definition, - in used templates at all levels up from the definition place, The defined variable can be overdefined: - in the file or DEF block, where it is defined, - in the file or DEF block, where the file defining the variable is included by INCLUDE FILE or INCLUDE VARS. - in included files at all levels up from the definition place, The defined variable can NOT be overdefined: - in included menus and used templates, - in all other files used in or included to these menus or templates There can be two different variables with the same name if they have different, non-overlapping scope. Scope can be temporarily overridden by overdefinition. Examples: <~ DEF VAR var1 Value ~> var1 = "Value" <~ DEF VAR var1 Value 1 ~> var1 = "Value 1" <~ DEF VAR var2 ~> var2 will be equal to the <~ INCLUDE FILE inc.ihtml ~> content of the file inc.ihtml <~ DEF VAR var2 END ~> <~ DEF VAR var2 ~> var2 will be equal to the <~ INCLUDE FILE inc.ihtml ~> content of the file inc.ihtml <~ VAR incvar1 ~> and the value of incvar1 (a <~ DEF VAR var2 END ~> variable defined in inc.ihtml) <~ VAR incvar1 ~> this is undefined here due to its scope in the DEF block of var2 <~ INCLUDE FILE inc.ihtml ~> Including file and it's vars <~ VAR incvar1 ~> Now it's ok. because of the include, if inc.ihtml contains the definition of incvar1. <~ DEF VAR var3 ~> <~ VAR var1 ~> "Value 1" (the global var1) <~ DEF VAR var1 WOW! ~> var1 = "WOW!" (it's local now) <~ VAR var1 ~> "WOW!" (the local var1) <~ DEF VAR var3 END ~> <~ VAR var1 ~> var1 = "Value 1" (We are out of the DEF block of var3 so it's the global var1) VARIABLE INCLUDE <~ INCLUDE VARS myfile ~> This command will include all variables from the file called "myfile". The included variables can be used as if they were defined in the current source files. All other content of the included file will be omitted. VARIABLE SUBSTITUTION <~ VAR myvar ~> This command will be substitued by the value of the variable called "myvar". If variable is undefined, an error message will be shown. There are predefined CAL variables that you can use without defining them: CASMVERSION - the version number and release name of CASM NOW - current local date and time in YYYY.MM.DD HH:MM:SS format DATE - current local date in YYYY.MM.DD format TIME - current local time in HH:MM:SS format NOWGMT - current GMT date and time in YYYY.MM.DD HH:MM:SS format DATEGMT - current GMT date in YYYY.MM.DD format TIMEGMT - current GMT time in HH:MM:SS format NOWUNIX - current UNIX time in seconds (e.g. 1069701651) For example Last updated at <~\s~> <~ NOW ~> will be compiled to something like this: Last updated at 2003.11.23 22:46:35 and will be compiled to something like this: It's charming. Isn't it? DEFINING RECORDSETS Recordset is a special variable structure. A recordset contains records. A record contains named fields with their values. <~ DEF RECORDSET myrecordsetname ~> myrecorddefinitions <~ DEF RECORDSET myrecordsetname END ~> This command defines a recordset called "myrecordsetname". If you define a recordset with the name of an already defined recordset, it will overdefine the old one. The recordset definition HAS THE SAME SCOPING as variable definitions have. You should think a recordset as a CAL variable which can handled by special CAL commands, but is controlled by the standard CAL variable scoping mechanism. A recordset definititon cannot contain another recordset definition. If such thing occures, the result will not be that you expect. A recordset contains one or more record definitions ("myrecorddefinitions") with the following syntax: <~ DEF RECORD ~> myfielddefinitions <~ DEF RECORD END ~> You can define as many records in a recordset as you want. Each defined record is appended to the recordset. A record contains one or more field definitions ("myfielddefinitions") with the following syntax: <~ DEF FIELD myfieldname ~> valueofmyfield <~ DEF FIELD myfieldname END ~> If you define a field with the name of an already defined field in the same record, it will overdefine the old one. Of course you can define fields in different records with the same name. They won't hurt each-other. A full recordset definition might look like this: <~ DEF RECORDSET articles ~> <~ DEF RECORD ~> <~ DEF FIELD title ~> CASM Is Released! <~ DEF FIELD title END ~> <~ DEF FIELD subtitle ~> After a long beta test, the great utility is here. <~ DEF FIELD subtitle END ~> <~ DEF FIELD text ~> CASM beta testing is finished. A lot of great features and the excellent CAL markup language are waiting for you. This commandline utility makes website creation and maintenance a breeze! <~ DEF FIELD text END ~> <~ DEF RECORD END ~> <~ DEF RECORD ~> <~ DEF FIELD title ~> CASM Is Still In Beta <~ DEF FIELD title END ~> <~ DEF FIELD text ~> CASM has a long beta testing period started at 3th May 2001. The final release is under heavy development. The new language called CAL is under implementation. Stay tuned! <~ DEF FIELD text END ~> <~ DEF RECORD END ~> <~ DEF RECORDSET articles END ~> Any characters between DEF RECORDSET and DEF RECORDSET END commands are omitted if they are not part of: - DEF RECORD commands, - DEF FIELDS commands or - defined field values. This means that you can freely write notes and comments or use whitespaces between any commands of the recordset definition except DEF FIELD and DEF FIELD END commands. WARNING: If you mistype any DEF RECORD or DEF FIELD command, it will silently ignored by the compiler!!! GETTING NUMBER OF RECORDS IN A RECORDSET <~ RECORDSET COUNT myrecordset ~> This command will be replaced with the number of records in the recordset called "myrecordset". LOOP RECORDSET <~ LOOP RECORDSET myrecordset ~> mydefinedcontent <~ LOOP RECORDSET myrecordset END ~> <~ LOOP RECORDSET myrecordset ~> mydefinedcontent <~ LOOP RECORDSET myrecordset UNDEFINED ~> myundefinedcontent <~ LOOP RECORDSET myrecordset END ~> The recordset loop command will loop trough the records of the recordset called "myrecordset". This command will substitued with the result of the loop. The recordset loop contains a definition of what to do if the recordset is defined ("mydefinedcontent"), and an other definition of what to do if the recordset is not defined ("myundefinedcontent"). The second one can contain simple content data and CAL commands. The first one has a special syntax. It contains one or more definition block to describe what to do with the records of the recordset: <~ RECORD ~> whattodowitharecord <~ RECORD END ~> or <~ RECORD ~> whattodowitharecord <~ RECORD UNDEFINED ~> whattodoifnomorerecords <~ RECORD END ~> Any byte between a LOOP RECORDSET and a LOOP RECORDSET UNDEFINED command will be ignored if it is not a part of a "whattodowitharecord" or a "whattodoifnomorerecords" definition. "whattodowitharecord" can contain simple content data, CAL commands and references to the fields of the current record with the following syntax: <~ FIELD myfieldname ~> If the field "myfieldname" is defined in the current record of the used recordset, this command will be replaced with the field value. Otherwise it will be replaced with an empty string. "whattodoifnomorerecords" can contain simple content data and CAL commands. A loop cannot contain another loop. If such thing occures, the result will not be that you expect. A loop recordset structure might look like the following: <~ LOOP RECORDSET articles ~> <~ RECORD ~> <~ RECORD END ~> <~ RECORD ~> <~ RECORD UNDEFINED ~> <~ RECORD END ~> <~ LOOP RECORDSET articles UNDEFINED ~> !!! ERROR !!! The articles recordset is not defined here !!! <~ LOOP RECORDSET articles END ~>
This is the first loop cycle.
Field title: <~ FIELD title ~> br> Field subtitle: <~ FIELD subtitle ~>
Field text: <~ FIELD text ~>
This is the second loop cycle.
Field title: [<~ FIELD title ~> ]
Field subtitle: [<~ FIELD subtitle ~> ]
Field text: [<~ FIELD text ~> ]
-
This will draw a table with two colums and will place each article in a separate cell. The looping algorythm of the compiler is the following: - If recordset is not defined, it will find the LOOP RECORDSET UNDEFINED block and compiles it. All other parts of the LOOP structure will be ignored. - If the recordset is defined it will do the following: - Sets the current record to the first record of the recordset. - Sets the current RECORD - RECORD UNDEFINED - RECORD END block to the first one. [1] - Gets the current record. - Gets the current RECORD - RECORD UNDEFINED - RECORD END block. - If the current record is defined then compiles the content between the RECORD and RECORD UNDEFINED command. Else compiles the content between the RECORD UNDEFINED and RECORD END command. - Sets the current record to the next record of the recordset. - If there is another RECORD - RECORD UNDEFINED - RECORD END block then sets the current RECORD - RECORD UNDEFINED - RECORD END block to the next one and jumps to [1]. Else does the following: - if the current record is defined, it sets the current RECORD - RECORD UNDEFINED - RECORD END block to the first one and jumps to [1]. - if the current record is not defined (there are no more records) it finishes the loop. CONDITIONAL SUBSTITUTION (IF ELSE ENDIF) <~ IF mycondition ~> mytruecontent <~ ENDIF mycondition ~> <~ IF mycondition ~> mytruecontent <~ ELSE mycondition ~> myfalsecontent <~ ENDIF mycondition ~> This command will evaluate "mycondition". - If condition is true then the whole command will be substitued with "mytruecontent". - If it is false, and an ELSE command exists, the whole command will be substitued with "myfalsecontent". - If it is false, and an ELSE command not exists, the whole command will be deleted during compilation. The IF THEN ELSE structure can contain another IF THEN ELSE structures recursively if (and only if) the conditions of the structures are different. Valid conditions are the followings: DEFINED VAR myvar - true when the variable is defined UNDEFINED VAR myvar - true when the variable is not defined DEFINED RECORDSET myrecordset - true when the recordset is defined UNDEFINED RECORDSET myrecordset - true when the recordset is not defined EQ VAL myvar myvalue - true when the variable "myvar" has a value equal to "myvalue" NE VAL myvar myvalue - true when the variable "myvar" has a value not equal to "myvalue" EQ VAR myvar1 myvar2 - true when the variable "myvar1" has a value equal to the value of "myvar2" NE VAR myvar1 myvar2 - true when the variable "myvar1" has a value not equal to the value of "myvar2" Valid conditions only in recordset loops are the followings: DEFINED FIELD myfield - true when the field is defined UNDEFINED FIELD myfield - true when the field is not defined Example: <~ IF DEFINED title ~> Title: <~ VAR title ~> <~ ELSE DEFINED title ~> No title <~ ENDIF DEFINED title ~> USING TEMPLATES <~ USE TEMPLATE mytemplatefile ~> This command will use a template file called "mytemplatefile" to assemble the result content. The template file is a simple CAL file. It can use the variables and recordsets of the source file, but cannot alter them. This command is recommended to be the first command of a source file due to performance considerations. However it works correctly when the command is at an other position. A template file can use an other template recursively. Only one tamplate file can be used in a file at the same time. Multiple CAL USE TEMPLATE commands are silently ignored. If you want to compile the same data with two or more different templates, you should define the data in a file and include it to as many files as you want. Those files can use different templates. The compilation process is the following: - compiler compiles the current source file, - reads and compiles the template file using the variables and recordsets defined in the source file, - the result is the compiled template file. Variables and recordsets defined in the current source file are "stronger" of those defined in the template. It means that source file variables and recordsets can override template variables and recordsets, but cannot be overridden in the template. EXCLUDING CONTENT FROM CAL COMPILATION <~ EXCLUDE START ~> <~ EXCLUDE END ~> Excludes content between these commands from CAL compilation. Content between these commands will be simply copied, compiler will not do anything else with it. These commands cannot be nested in itselfs. Nesting will result a silently wrong operation. An EXCLUDE END command must be preceeded with an EXCLUDE START command, otherwise the compiler will consider it as an invalid CAL command. COMMENTS <~ COMMENT mycomment ~> "mycomment" is your comment text. You can write anything here except other CAL commands or CAL command terminator tokens (" ~> " by default). The COMMENT command will be simply deleted during compilation. You can use it to make your CAL source more comprehensible. Examples: <~ COMMENT This is a single-line comment ~> <~ COMMENT This is a multi-line comment ~> EMBEDDED PERL SCRIPTING <~ PERLSCRIPT ~> <~ PERLSCRIPT END ~> Executes content between these commands as a PERL script. Result will be the return value of the script. So as you see CAL enables you to use embedded PERL scripts in CAL source files. It's a powerful tool, but needs precaution. Please read the followings carefully!!! First of all, declare each and every PERL variable you use as local (with "my" statement). Otherwise you can unintentionally overdefine or overwrite internally used variables of CASM. Be careful with variables, function definitions and others, because your script runs as a part of the CAL compiler. Your script can modify the compiler itself! (This behavior not because of my lazy programming style. This is by concept. If you want, you definitely can modify the internal stuff of CASM. You have the possibility of unlimited hacking.) The return value of your script is the value of the last expression of it. Be elegant, use the "return" PERL function. Let's see a simple example: <~ PERLSCRIPT ~> my $datetime; $datetime = localtime; return "\n Last modified: $datetime\n";; <~ PERLSCRIPT END ~> It will result something like this: Last modified: Fri Oct 31 23:30:54 2003 Another useful example: <~ PERLSCRIPT ~> use Sys::Hostname; return hostname(); <~ PERLSCRIPT END ~> It will result the hostname of your computer. You can put CAL commands in the return value, of course. CASM will compile them after finishing your script's interpretation. If there is an error in your perl script, the CAL compiler warns you and includes the PERL interpreter error message into the target file just before the <~ PERLSCRIPT ~> command. The PERL interpreter error message looks like this: -------- PERL runtime error message -------- Can't use an undefined value as a SCALAR reference at (eval 74) line 1. -------------------------------------------- The line number in the error message refers to the line number of your embedded script. Line 1. is the first line of the script (not the first line of your CAL source!). Note: Do you want to reuse your scripts multiple times? Define a CAL variable containing the script, and use this variable within PERLSCRIPT and PERLSCRIPT END commands. Ok, now you are able to write simple embedded PERL scripts. Let's take a look at the internal variables of the CAL compiler. These are the most interesting internal variables that you can use in your scripts: $source_orig - The original content of the source file. $source - The uncompiled part of thes ource content. When compiling, CASM deletes already compiled parts from this variable. $result - The compiled target content. When compiling, CASM appends newly compiled parts to this variable. The value of this variable will be the content of the target file. $sourcefile - The full path of the source file under compilation. %cal - Hash containing CAL syntax. (Yes, you can alter it.) %calvar - Hash containing CAL variables and their values. For example: $calvar{'MYVAR'} is the value of MYVAR %calrecset - Hash containing CAL recordsets with records, fields and their values. For example: $calrecset{'MYRECORDSET'][2]{'MYFIELD'} is the value of MYFIELD in the third(!) record of MYRECORDSET. $templatefile, $template, - The filenamename and the content of the CAL template in use. The hacking power of CASM is unlimited. Isn't it? If you want to know more, get the CASM source package, open the casm_cal.pli file and read it. This is the CAL compiler source with a lot of comments. Reading other CASM source files is a good idea, since there are many useful and interesting internal functions and variables out there. ################ # BUG REPORT # ################ Send an e-mail to casm@mailbox.hu with CASM BUG in the subject line. Please include the followings: - OS name/version you use - Perl version you use - CASM version you use - Error messages you have got (if any) - Detailed description of the bug encountered - the minimal CAL source content needed to reproduce the buggy behaviour, and the compiled content of it. (Use tar.gz format to make my life easier) - the copy of the command that you issued in commandline to invoke CASM Please, please DO NOT send me extremely complex and large CAL sources! Delete all unnecessaire contents and files before sending me! --