Thalassa CMS logo

Thalassa CMS

Common macros

This page describes macros available both for thalassa (the static content generator) and thalcgi.cgi (the CGI program).

Please note that the colon char “:” is used hereinafter as the delimiter, but in fact any ASCII punctuation char (except for “_” and “*”) can be used in this role.

Conditionals

This section describes macros that allow to choose one of the two (or more) alternatives depending on certain conditions.

WARNING: Please make sure you read and understood the section devoted to eager computational model and its consequences. It is important to understand that all arguments of any nesting macro call are always computed (unconditionally), and conditional macros only choose one of the results of these computations, simply dropping the results that aren't choosen.

The if macro

Synopsis: %[if:condition:then_text:else_text]

The first argument (condition) is trimmed off any leading and trailing whitespace; in case the result of this trimming is not empty, the condition is assumed true, and then_text is returned; otherwise, else_text is returned.

The third argument may be omitted; in this case, if the condition is empty (false), the macro call expands to an empty string.

The ifeq macro

Synopsis: %[ifeq:str1:str2:then_text:else_text]

The first two arguments (str1 and str2) are trimmed off any leading and trailing whitespace, and the results are compared; in case they are equal, then_text is returned; otherwise, else_text is returned.

The fourth argument may be omitted; in this case, if the strings are not equal, the macro call expands to an empty string.

The ifbelongs macro

Synopsis: %[ifbelongs:str1:list:then_text:else_text]

The first argument (str) is trimmed off any leading and trailing whitespace. The second argument (list) is broken down to words (that is, using whitespace as separators); then, check is performed whether str equals any of the words. In case the word is found, then_text is returned; otherwise, else_text is returned.

The fourth argument may be omitted; in this case, if the given word isn't found in the list, the macro call expands to an empty string.

The or macro

Synopsis: %[or:str1:str2: ... ]

The arguments (str1, str2 etc.) are checked one by one, whether they turn into an empty string after trimming off leading and trailing whitespace. The first argument that doesn't become empty (which effectively means it contains any non-whitespace characters) is returned as it was in the call, before trimming.

If no suitable argument is found, an empty string is returned.

The switch macro

Synopsis: %[switch:expr:v1:r1:v2:r2 ... ]

The first argument, expr, after stripping off all leading and trainling whitespace, is compared, one by one, with values v1, v2 etc., also with spaces stripped off; in case one of them equals to the expr, the work is immediately finished and the corresponding result (r1, r2...) is returned. In case none of the values match, an empty string is returned.

The mapper

In the present version of Thalassa CMS, there's only one macro resembling a loop construct. In reality it is not a loop, it is a mapper. It might look somewhat bad practice to provide anything loop-like in such an environment; unfortunately, it seems hard to invent anything else to build, e.g., a list of <option> items for the <select> HTML form input — and, generally speaking, having an arbitrary list of items, to build an HTML representation for it.

Anyway, the mapper is not intended to be used as a general purpose loop construct, and it definitely should not be abused this way. Remember, all these Thalassa CMS ini files are not programs. If you feel you need a general purpose loop, it means you're doing something wrong.

The foreach macro

Synopsis: %[foreach:list:name:a1:a2: ... ]

The first argument (list) is broken down to words (using whitespace for delimiting). Then for each word, the macro named name is invoked with arguments a1, a2, ..., and with the word as the last argument.

The results of all invocations are concatenated (with no chars inserted in between the items — that is, a real concatenation is done), and the concatenated string is returned.

For example, consider the following call:

  %[foreach:alpha beta gamma:m1:pp:qq:rr]

The result will be precisely the same as if we did

  %[m1:pp:qq:rr:alpha]%[m1:pp:qq:rr:beta]%[m1:pp:qq:rr:gamma]

How to use it

In most cases, the html macro is used as the second argument to foreach, and the HTML snippet name is passed as the third argument.

Consider, for example, you've got a list of certain words and you want to create an HTML enumerated list out of them. The following two snippets will do the thing:

  [html]
  words2ol   = <ol>%[foreach:%0%:html:li_enclose]</ol>
  li_enclose = <li>%0%</li>

Having these two snippets, it is sufficient to write %[html:words2ol:put your list here] to get the HTML list.

String manipulation

In the present version, the set of string manipulation functions doesn't look very powerful. However, once again, this macro system is not a programming language, so we shouldn't ever want things like indexing a string, getting substrings and the like.

The trim macro

Synopsis: %[trim:string]

The argument is stripped off any leading and trailing whitespace; the result is returned.

The collapsews macro: collapse whitespace

Synopsis: %[collapsews:string]

The argument is stripped off any leading and trailing whitespace; within the rest, any non-empty sequence of whitespace chars (space, tab, carriage return and newline) is replaced with exactly one space. The result is returned.

The rmlf macro: remove linefeeds

Synopsis: %[rmlf:string]

All carriage return and newline chars are removed from the argument, the result is returned.

Please note the argument is not stripped nor modified in any other way, only the CR and LF chars are removed.

The lhead and lhead macros: list splitting

Synopsis:
%[lhead:list:delimiter]
%[ltail:list:delimiter]

The two macros allow to split a string, treated as a list, down to the head and the tail. The delimiter argument may be omitted or left empty; the string is then considered as a list of words, separated by arbitrary amounts of whitespace. If a non-empty delimiter is specified, it is trimmed off any leading and trailing whitespace, and the remaining string (as a whole) is used as the delimiter: the first occurence of it is found within the first argument, and lhead returns everything before the delimiter, while ltail returns everything after the delimiter.

If the specified delimiter is not found within the first agrument, lhead returns the whole agrument, and ltail returns an empty string. If delimiter is not specified, and there's only one word, lhead returns the word (trimmed off surrounding whitespace), and ltail returns an empty string. If the first argument is empty, both macros return an empty string.

The lindex macro: access list items by indices

Synopsis: %[lindex:list:template:delims]

The lindex macro treats its first argument (list) as a list and provides access to its first 10 elements by their indices; it is possible to access several elements in one call, building a new string according to the given template.

The third argument (delims) controls how the string is broken to list elements. The argument can be omitted or left empty; in this case the string is broken down to words using whitespace chars as delimiters, producing no empty words (that is, any amount of whitespace in a row it considered equal to a single space). If the argument consists of non-whitespace chars only, all the chars become delimiters; in this case, the string is broken down to elements using the delimiters, and empty elements become possible. Adding a trailing whitespace instructs the macro to trim off any leading and trailing whitespace from every element.

Please be warned that, as the macroprocessor is encoding-agnostic, using a non-ascii char as a delimiter will not work for UTF-8.

If the third argument starts with one or more whitespace chars and contains non-whitespace chars as well, it means one of the predefined special cases; the argument in this case is stripped off any leading and trailing whitespace, and the rest identifies how the list must be broken down to elements. In the present version, there are two such cases:

We leave it unspecified what happens if the delims argument has leading spaces and is not listed as a special case above; in future versions, some additional functionality may be implemented.

The template is first striped off any leading and trailing whitespace; what happens afterwards depends on whether its first non-space char is a digit or not, and may sound complicated. Before giving the full description, let's note that in the simplest case it is just a digit from 0 to 9, causing the macro to return the respective element of the list, numbered from zero. For example, %[lindex:foo bar bazz:1] will return bar.

In case the first character of the template is a digit, the template is traversed from left to right, and digits from 0 to 9 are replaced with the respective elements of the list, while all other characters are left intact. For example, a call

    %[lindex:foo bar bazz:2-0+1]

will expand to bazz-foo+bar.

If the first non-space char of the template is not a digit, it becomes the local escape char. The rest of the template, not including its first char, is traversed from left to right, and escape chars immediately followed by digits are replaced with the respective list elements. If the escape char is followed by another escape char, a single escape char is put into the resulting string; we intentinally leave unspecified what happens in case the escape char is followed by anything but a digit and the escape char. All the other stuff is left intact. For example,

    %[lindex:foo bar bazz:=AAA=2BBB=0==0CCC=1DDD]

will turn into AAAbazzBBBfoo=0CCCbarDDD. This mode allows including literal digits into the result.

The lsort macro

Synopsis: %[lsort:list:delims:glue]

The lsort macro sorts the given list of strings.

The first argument (list) is a string to be broken down to list items, and the second argument (delims) determines how the string is broken down; see the the lindex macro's delims argument description for details, it works exactly the same way.

The last argument (glue) is the string to put between each two elements after they are sorted; one might want to call this a separator, but we use the term glue to avoid confusion with how the initial separation is done. If this argument is omitted, a single space char is used. If the argument is empty, the elements within the resulting string are not separated from each other at all.

Hence, if both delims and glue are omitted (that is, the macro is called with only one argument), the list is assumed to consist of words (any amount of whitespace is considered a separator), and after the sorting is done, the list is reassembled into the string of words, separated with exactly one space.

The sorting is done lexicographically in respect to the ASCII codepage. Please note however that Thalassa is mostly encoding-agnostic, so with strings containing non-ascii chars, the result may be far from what you expect.

Furthermore, please be aware that, despite the quicksort algorithm is used internally, the whole thing can be way too slow for lenghty lists, just because performing the lsort involves, first, tokenizing a large string (which means a lot of memory allocation operations and copying), and, second, rejoining the sorted elements back to a single string. It is strongly recommended to avoid sorting as long as you can. For example, in the well-known Smoky template this macro is used only once (!), for the /thalcgi.cgi/cmtadmin page, because being left unsorted, that list of all existing discussions looks too messy to handle.

The ltgt macro: HTML protection

Synopsis: %[ltgt:string]

Within the argument, HTML active characters — namely “<”, “>” and “&” — are replaced with, respectively, “&lt;”, “&gt;” and “&amp;”, making the text safe to be included into an HTML document.

The urlenc macro: URL-encoding

Synopsis: %[urlenc:string]

The argument is URL-encoded and returned.

Precisely speaking, ASCII alphanumeric characters as well as punctuation chars -_~. are left as they are, spaces are replaced with +, and any other byte is percent-encoded, that is, replaced with the percent char and the 2-digit hexadecimal code. Upper-case chars are uses as hexadecimal digits.

The macro is codepage-agnostic, just like almost all Thalassa CMS.

The q macro: quote string as HTML attribute

Synopsis: %[q:string]

If the argument contains no doublequote char, it is returned being surrounded by doublequotes. If it contains doublequotes but doesn't contain apostrophes, it is returned surrounded by apostrophes. In case it contains both apostrophes and doublequotes, it is returned surrounded by doublequotes, with each doublequote char inside the argument replaced with “&quot;”.

The macro is useful when you need to give an attrubute to an HTML tag, and you're not sure it will not contain doublequote chars at the generation time. Actually, it seems to be a good practice to use this macro for all attribute values that contain any macro calls.

Accessing files

The iffile macro

Synopsis: %[iffile:filename:then_text:else_text]

The first argument (filename) is trimmed off any leading and trailing whitespace, and the result is used as a file name; if the file with given name exists, then_text is returned; otherwise, else_text is returned.

The third argument may be omitted; in this case, if the file doesn't exist, the call expands to an empty string.

The filesize macro

Synopsis: %[filesize:filename]

The first argument (filename) is trimmed off any leading and trailing whitespace, and the result is used as a file name; if the file with given name exists and is a regular file, its size (in bytes, as a decimal number) is returned; otherwise, the macro returns an empty string.

The readfile macro

Synopsis: %[readfile:filename]

The first argument (filename) is trimmed off any leading and trailing whitespace, and the result is used as a file name; if the file with given name exists and is readable, the macro returns the whole contents of the file (as a string, so you probably shouldn't apply this macro to binary files). In case of any error, an empty string is returned.

In case the argument is empty or becomes empty after trimming, no attempts will be made to open any files or to do anything else; the macro will immediately return the empty string. This property can be used for conditional reading of files: instead of placing the readfile call in one of branches of any conditional checkers (in which case the reading attempt will be performed regardless of the condition), better check for the condition within the readfile's argument and return an empty string in case the file is not to be read.

The dir macro

Synopsis: %[dir:dirname:flags]

The dir macro allows to extract file names from a given directory. By default, filenames starting with “.” and “_” are ignored; this behaviour can be modified with flags (see below). It is important to note that file names containing any whitespace are always ignored and there's no way to override this rule.

The first argument (dirname) is trimmed off any leading and trailing whitespace, and the result is used as the directory name.

The second argument may contain the following chars:

If the second argument is empty, it can be omitted altogether.

The macro returns a string containing the extracted file names, separated by a single space char.

The imgdim macro: dimensions for img tag

Synopsis: %[imgdim:filename]

The first argument (filename) is trimmed off any leading and trailing whitespace, and the result is used as a file name; the file should contain a valid image in PNG, JPEG or GIF format. In case the file doesn't exist, not readable or its format is not recognized, the macro returns an empty string.

In case of successful image parsing, a string with HTML attributes, suitable for use within the img HTML tag, is returned. The string looks like this:

  width="305" height="500"

The file format is detected by its content, not by its name.

The now macro: current time

Synopsis: %now%

No arguments are accepted. Macro call is replaced with the current time as a Unix datetime value (a decimal integer equal to the amount of seconds passed since Jan 01, 1970).

The rfcdate macro

Synopsis: %[rfcdate:unix_datetime]

The first argument (unix_datetime) is trimmed off any leading and trailing whitespace; the result, which must be a decimal number, is taken as a Unix date/time value. The macro returns the same date in a human-readable form as defined by rfc2822, something like “29 Mar 2023 19:15:00 +0000”.

Indices in file names

Sometimes it becomes necessary to provide a template for file names that form a kind of an array with a main page and some numbered additional pages. For such cases, Thalassa provides a family of index macros; being used within a file name template, they are replaced with the indicies as necessary, thus forming file name series containing numbers.

The “main” page is always meant to have zero as its index; however, there's often no page with index 1 (it is not present in lists that have a native order, while is present in reversed lists).

The following macros are used in “indexed” file name templates:

Please note these macros are not universally available, they only work for file name templates where numbered additional pages are involved. Parameters using these macros are explicitly declared as such.

Thalassa CMS version

Synopsis: %[thalassa_version:function]

If the argument is omitted, empty or contains whitespace only, the short version string is returned. It looks like 0.2.00.

Otherwise, the argument (function name) is trimmed off leading and trailing whitespace; as of the current version, %[thalassa_version:full] returns longer string, like

  Thalassa CMS v. 0.2.00 (built Feb 27 2024)

%[thalassa_version:id] returns short decimal number identifying the version; for 0.2.00, this number is 200, and for later versons the number is guaranteed to be greater.

%[thalassa_version:built] returns the build date, as given by the __DATE__ macro in gcc, like Feb 27 2024.

© Andrey V. Stolyarov, 2023, 2024