tgen#
Simple templated text generator.
Introduction
The main function in this library is tgen(). It takes a template and a list of substitutions and produces a new document.
An example template may look like:
Group: {group_name}
Location: {group_location}
Members:
{list_members: - {first_name} {last_name} lives in {country}\n}
Group: skiers
Location: mountains
Members:
- Adam Smidth lives in Germany
- Jack Daniel lives in USA
- Fritjof Nansen lives in Norway
Variable tags
A pair of braces, “{” and “}”, that encloses a string is a tag. When the template is processed, the tags are replaced with new content according to the substitutions. The general form for a variable tag is:
{VAR%FMT:TEMPL}
VAR
identifies the tag in the substitutions.FMT
is an optional format specifier of the form:where the brackets stands for optional fields. The meaning of the fields are:[ALIGN][WIDTH][.PREC][CASE]
ALIGN is either “-” for left-aligned or empty for right-aligned.
WIDTH is a positive integer denoting the width, possible padded with spaces.
PREC is a positive integer denoting the maximum number of characters to write (not including padding).
CASE is a single character, with the following meaning:
’s’: no change in case
’c’: convert to lower case
’C’: convert to upper case
’u’: convert to underscore-separated lower case
’U’: convert to underscore-separated upper case
’m’: convert to lower mixedCase (aka camelCase)
’M’: convert to upper MixedCase (aka CamelCase)
’i’: convert to a valid C identifier (permissive)
’I’: convert to a valid C identifier (strict)
’T’: convert to title case (convert first character to upper case and the rest to lower case)
TEMPL
is an optional template that may be used in nested calls. It may contain embedded tags, as long as the opening and closing braces exactly match.
There are two types of substitutions, variable substitutions and function substitutions:
A variable substitution relates
VAR
to a string replacing the tag. If the tag contains aTEMPL
-part, it will be ignored.A function substitution relates
VAR
to a function. When the template is processed, the function is called replacing the tag with its output. The function usesTEMPL
as a (sub)template.
An alternative syntax is
{VAR?}
Assignment tags
Assignment tags has the form
{VAR=VALUE}
Conditional tags
Conditionals are a special form of tags with the following syntax:
{@if:COND}
<code...>
{@elif:COND}
<code...>
{@else}
<code...>
{@endif}
elif
and else
tags are optional and there may be multiple elif
tags.
If COND
takes one of the forms ‘string’ true if non-empty, false otherwise ‘string1’ = ‘string2’ true if the strings are equal ‘string1’ ! ‘string2’ true if the strings are not equal it is evaluated as a string expressions, where the strings must be quoted with either single (’) or double (”) quotes (quotes within the strings may be escaped with backslash).
Otherwise COND
is evaluated using infixcalc(), which supports integer arithmetic and the following binary operators: | logical or & logical and = logical equal ! logical not equal > logical greather than < logical smaller than
plus
minus times / division % modulus ^ power in addition to parenthesis.
Note that unary minus and plus are not supported. Hence, an expression like “-3” is invalid (if needed, it can be rewritten as “0-3”).
Variable expansion is performed before COND
is evaluated.
Expression tags
Expression tags has the form
{@eval:EXPR}
EXPR
with its evaluated value using integer arithmetics with infixcalc(). See the Conditional tags for limitations.
Alignment tags
Alignment are tags of the form
{@N}
N
may be any positive integer. It will be replaced with spaces such that the text following it will start on column N
(that is N
characters after the last newline). If the alignment tag it placed after column N
, no output will be produced.
Error tags
A template can signal errors like invalid variables or wrong use with the construct
{@error:message}
Comment tags
Comments that will not be visible in the output can be added by starting a tag with “: “, like
{: My comment... }
Literal braces and escapes
Literal braces may be included in the template and the TEMPL
section, if they are escaped according the following table:
escape sequence |
result |
comment |
---|---|---|
|
|
literal start brace |
|
|
literal end brace |
|
|
only use this if |
. |
` |
Furthermore are normal C escape sequences (\a
, \b
, \f
, \n
, \r
, \t
, \v
and \\
) supported as well as line-continuation by ending a line with a backslash. In addition includes tgen the special noop escape sequence \.
that expands to the empty string. It may be used as an alternative to {}
for separating end braces following each other, such that they are not interpreated as a literal end brace. These escapes can be turned off by setting the global variable tgen_convert_escape_sequences
to zero.
The strength of templating is that you can produce the same information in a completely different format just by changing the template, without changing the logics.
Utility functions
-
int tgen_escaped_copy(char *dest, const char *src, int n)#
Copies at most
n
bytes fromsrc
and writing them todest
. Ifn
is negative, all ofsrc
is copied.The following standard escape sequences are converted:
in addition to escaped newlines.\a, \b, \f, \n, \r, \t, \v \\
Returns the number of characters written to
dest
.
-
int tgen_setcase(char *s, int len, int casemode)#
Sets the case of the (sub)string
s
according tocasemode
.len
is the of length of the substring. Iflen
is negative, the case is applied to the whole string.Valid values for
casemode
are:”s”: no change in case
”l”: convert to lower case
”U”: convert to upper case
”T”: convert to title case (convert first character to upper case and the rest to lower case)
Returns non-zero on error.
-
char *tgen_convert_case(const char *s, int len, int casemode)#
Returns a new malloc’ed copy of the
len
first bytes ofs
with the case converted according tocasemod
. Iflen
is negative, all ofs
is copied.Valid values for
casemode
are:’s’: no change in case
’c’: convert to lower case
’C’: convert to upper case
’u’: convert to underscore-separated lower case
’U’: convert to underscore-separated upper case
’m’: convert to lower mixedCase (aka camelCase)
’M’: convert to upper MixedCase (aka CamelCase)
’i’: convert to a valid C identifier (permissive)
’I’: convert to a valid C identifier (strict)
’T’: convert to title case (convert first character to upper case and the rest to lower case)
Returns NULL on error.
Examples: s: “AVery mixed_Sentense” -> “AVery mixed_Sentense” c: “AVery mixed_Sentense” -> “avery mixed_sentense” C: “AVery mixed_Sentense” -> “AVERY MIXED_SENTENCE” u: “AVery mixed_Sentense” -> “a_very_mixed_sentense” U: “AVery mixed_Sentense” -> “A_VERY_MIXED_SENTENCE” m: “AVery mixed_Sentense” -> “aVeryMixedSentense” M: “AVery mixed_Sentense” -> “AVeryMixedSentense” T: “AVery mixed_Sentense” -> “Avery mixed_sentense” i: “ n-Atoms “ -> “n_Atoms” i: “ n+Atoms “ -> “n_Atoms” I: “ n-Atoms “ -> “n_Atoms” I: “ n+Atoms “ -> NULL
-
char *tgen_buf_steal(TGenBuf *s)#
Like tgen_buf_deinit(), but instead of free up the internal buffer, it is returned. The returned string is owned by the caller and should be free’ed with free().
-
int tgen_buf_append(TGenBuf *s, const char *src, int n)#
Appends
n
bytes from stringsrc
to end of output buffers
.If
n
is negative, all ofstr
(which must be NUL-terminated) is appended.Returns number of characters appended or -1 on error.
-
int tgen_buf_append_fmt(TGenBuf *s, const char *fmt, ...)#
Like tgen_buf_append() but allows printf() formatting of the input.
-
int tgen_buf_append_vfmt(TGenBuf *s, const char *fmt, va_list ap)#
Like tgen_buf_append_fmt(), but takes a
va_list
instead of a variable number of arguments.
-
int tgen_buf_append_case(TGenBuf *s, const char *src, int n, int casemode)#
Like tgen_buf_append(), but converts the first
n
bytes ofsrc
according tocasemode
before appending them tos
.Valid values for
casemode
are:’s’: no change in case
’c’: convert to lower case
’C’: convert to upper case
’u’: convert to underscore-separated lower case
’U’: convert to underscore-separated upper case
’m’: convert to lower mixedCase (aka camelCase)
’M’: convert to upper MixedCase (aka CamelCase)
’i’: convert to a valid C identifier (permissive)
’I’: convert to a valid C identifier (strict)
’T’: convert to title case (convert first character to upper case and the rest to lower case)
-
int tgen_buf_unappend(TGenBuf *s, size_t n)#
Removes the last
n
characters appended to buffers
. Ifn
is larger than the buffer length, it is truncated.Returns the number of characters removed.
-
int tgen_buf_calign(TGenBuf *s, int c, int n)#
Pad buffer with character
c
untiln
characters has been written since the last newline. If more thann
characters has already been written since the last newline, nothing is added.Returns number of padding added or -1 on error.
-
int tgen_buf_align(TGenBuf *s, int n)#
Like tgen_buf_calign() but pads with space.
-
int tgen_lineno(const char *template, const char *t)#
Returns the line number of position
t
intemplate
.
-
char *tgen_readfile(const char *filename)#
Reads a file and returns a newly allocated buffer with its content or NULL on error.
Functions for managing substitutions
-
const TGenSub *tgen_subs_get(const TGenSubs *subs, const char *var)#
Returns substitution corresponding to
var
or NULL if there are no matching substitution.
-
const TGenSub *tgen_subs_getn(const TGenSubs *subs, const char *var, int len)#
Like tgen_subs_get(), but allows
var
to not be NUL-terminated by specifying its length withlen
. Iflen
is negative, this is equivalent to calling tgen_subs_get().
-
int tgen_subs_set(TGenSubs *subs, const char *var, const char *repl, TGenFun func)#
Adds variable
var
to list of substitutionssubs
.repl
andfunc
are the corresponding replacement string and generator function, respectively.Returns non-zero on error.
-
int tgen_subs_setn(TGenSubs *subs, const char *var, int len, const char *repl, TGenFun func)#
Like tgen_subs_add(), but allows
var
to not be NUL-terminated by specifying its length withlen
. Iflen
is negative, this is equivalent to calling tgen_subs_get().Returns non-zero on error.
-
int tgen_subs_set_fmt(TGenSubs *subs, const char *var, TGenFun func, const char *repl_fmt, ...)#
Like tgen_subs_set(), but allows printf() formatting of the replacement string.
Returns non-zero on error.
-
int tgen_subs_setn_fmt(TGenSubs *subs, const char *var, int len, TGenFun func, const char *repl_fmt, ...)#
Like tgen_subs_setn(), but allows printf() formatting of the replacement string.
Returns non-zero on error.
-
int tgen_subs_setn_vfmt(TGenSubs *subs, const char *var, int len, TGenFun func, const char *repl_fmt, va_list ap)#
Like tgen_subs_setn(), but allows printf() formatting of the replacement string.
Returns non-zero on error.
Functions for text generations
-
char *tgen(const char *template, TGenSubs *subs, void *context)#
Returns a newly malloc’ed string based on
template
, where all occurences of{VAR}
are replaced according to substitutionVAR
in the arraysubstitutions
, which has lengthn
.The template may also refer to a substitution as
{VAR:TEMPL}
. If the substitution corresponding toVAR
provide a substitution function (via itssubs
member),TEMPL
will be passed as subtemplate to the substitution function. IfTEMPL
is not given, then the subtemplate will be taken from therepl
member of the corresponding substitution.context
is a pointer to user data passed on to the substitution function.Returns NULL, on error.
Typedefs
-
typedef struct _TGenBuf TGenBuf#
Buffer for generated output. Example use that prints “Hello world!” to stdout:
TGenBuf s; tgen_buf_init(&s); tgen_buf_append(&s, "Hello", -1); tgen_buf_append_fmt(&s, " %s!", "world"); printf("%s\n", tgen_buf_get(&s)); tgen_buf_deinit(&s);
-
typedef int (*TGenFun)(TGenBuf *s, const char *template, int len, TGenSubs *subs, void *context)#
Prototype for generator function that appends to the output buffer.
s
: output buffertemplate
: input templatelen
: length oftemplate
. A negative number indicates that the template is NUL-terminated.subs
: substitutionscontext
: pointer to user-defined context passed on to generator functions
Returns non-zero on error.
Enums
-
enum [anonymous]#
Error codes used by this library
Values:
-
enumerator TGenOk#
-
enumerator TGenAllocationError#
Allocation error
-
enumerator TGenSyntaxError#
Syntax error
-
enumerator TGenIOError#
Input/output error
-
enumerator TGenVariableError#
Invalid variable name
-
enumerator TGenSubtemplateError#
Missing subtemplate
-
enumerator TGenMapError#
Error from the map library
-
enumerator TGenFormatError#
Invalid format specifier
-
enumerator TGenUserError#
Triggered by the {@error:…} construct
-
enumerator TGenOk#
Variables
-
int tgen_convert_escape_sequences#
Whether to convert standard escape sequences.
-
struct _TGenBuf#
- #include <tgen.h>
Buffer for generated output. Example use that prints “Hello world!” to stdout:
TGenBuf s; tgen_buf_init(&s); tgen_buf_append(&s, "Hello", -1); tgen_buf_append_fmt(&s, " %s!", "world"); printf("%s\n", tgen_buf_get(&s)); tgen_buf_deinit(&s);
-
struct _TGenSubs#
- #include <tgen.h>
A structure managing a list of substitutions
-
struct _TGenSub#
- #include <tgen.h>
Struct defining a substitution.