.\" Copyright (c) 2001-2002 Packet Design, LLC.
.\" All rights reserved.
.\"
.\" Subject to the following obligations and disclaimer of warranty,
.\" use and redistribution of this software, in source or object code
.\" forms, with or without modifications are expressly permitted by
.\" Packet Design; provided, however, that:
.\"
.\" (i) Any and all reproductions of the source or object code
.\" must include the copyright notice above and the following
.\" disclaimer of warranties; and
.\" (ii) No rights are granted, in any manner or form, to use
.\" Packet Design trademarks, including the mark "PACKET DESIGN"
.\" on advertising, endorsements, or otherwise except as such
.\" appears in the above copyright notice or in the software.
.\"
.\" THIS SOFTWARE IS BEING PROVIDED BY PACKET DESIGN "AS IS", AND
.\" TO THE MAXIMUM EXTENT PERMITTED BY LAW, PACKET DESIGN MAKES NO
.\" REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING
.\" THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED
.\" WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE,
.\" OR NON-INFRINGEMENT. PACKET DESIGN DOES NOT WARRANT, GUARANTEE,
.\" OR MAKE ANY REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS
.\" OF THE USE OF THIS SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY,
.\" RELIABILITY OR OTHERWISE. IN NO EVENT SHALL PACKET DESIGN BE
.\" LIABLE FOR ANY DAMAGES RESULTING FROM OR ARISING OUT OF ANY USE
.\" OF THIS SOFTWARE, INCLUDING WITHOUT LIMITATION, ANY DIRECT,
.\" INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, PUNITIVE, OR CONSEQUENTIAL
.\" DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, LOSS OF
.\" USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY THEORY OF
.\" LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
.\" THE USE OF THIS SOFTWARE, EVEN IF PACKET DESIGN IS ADVISED OF
.\" THE POSSIBILITY OF SUCH DAMAGE.
.\"
.\" Author: Archie Cobbs <archie@freebsd.org>
.\"
.\" $Id: tmpl.3,v 1.1.1.1 2012/02/21 23:25:53 misho Exp $
.\"
.Dd April 22, 2002
.Dt TMPL 3
.Os
.Sh NAME
.Nm tmpl
.Nd templates
.Sh LIBRARY
PDEL Library (libpdel, \-lpdel)
.Sh SYNOPSIS
.Ft "struct tmpl *"
.Fn tmpl_create "FILE *input" "int *num_errors" "const char *mtype"
.Ft "struct tmpl *"
.Fn tmpl_create_mmap "const char *path" "int *num_errors" "const char *mtype"
.Ft void
.Fn tmpl_destroy "struct tmpl **tmplp"
.Ft "struct tmpl_ctx *"
.Fn tmpl_ctx_create "void *arg" "const char *mtype" "tmpl_handler_t *handler" "tmpl_errfmtr_t *errfmtr"
.Ft "void *"
.Fn tmpl_ctx_get_arg "struct tmpl_ctx *ctx"
.Ft "const char *"
.Fn tmpl_ctx_get_mtype "struct tmpl_ctx *ctx"
.Ft "const char *"
.Fn tmpl_ctx_get_var "struct tmpl_ctx *ctx" "const char *name"
.Ft "int"
.Fn tmpl_ctx_set_var "struct tmpl_ctx *ctx" "const char *name" "const char *value"
.Ft "char *"
.Fn tmpl_list_handler "struct tmpl_ctx *ctx" "const struct tmpl_func *userfuncs" "u_int uflen" "char **errmsgp" "int argc" "char **argv"
.Ft void
.Fn tmpl_ctx_destroy "struct tmpl_ctx **ctxp"
.Ft int
.Fn tmpl_execute "struct tmpl *tmpl" "struct tmpl_ctx *ctx" "FILE *output" "int flags"
.Ft int
.Fn tmpl_execute_func "struct tmpl_ctx *ctx" "FILE *output" "char **errmsgp" "int argc" "char **argv" "int flags"
.Ft void
.Fn tmpl_ctx_reset "struct tmpl_ctx *ctx"
.Sh DESCRIPTION
.\"
.Ss Overview
.\"
The
.Nm tmpl
library supports programmatic generation of output
based on input from
.Nm tmpl
template files.
Output is generated by parsing and then executing a tmpl file.
.Pp
The tmpl file simply contains the desired output, with invocations
of various tmpl functions, denoted by the
.Dq @
character, interspersed.
Tmpl functions take zero or more arguments, where each argument can
be either a doubly-quoted string or another, nested tmpl function.
.Pp
When executed, the output of a tmpl file is simply the contents of the
file, with each tmpl function replaced by the value returned by that function.
Several functions, including control flow constructs, are built-in,
and compile-time and run-time user defined functions are supported.
.Pp
Here is a simple example tmpl input file:
.Pp
.Bd -literal -compact -offset 3n
I'm going to count to three:
@set("i", "1")
@while(@le(@get("i"), "3"))
@get("i")
@set("i", @add(@get("i"), "1"))
@endwhile
Done.
.Ed
.Pp
If this template were executed, the output would be:
.Bd -literal -offset 3n
I'm going to count to three:
1
2
3
Done.
.Ed
.Pp
While the example above uses only built-in functions, user-defined functions
written in C may be invoked in the same way.
.\"
.Sh Parsing
.\"
This section describes the precise rules that govern how
.Nm tmpl
files are parsed.
.Pp
A tmpl file is parsed by scanning for special function calls.
A function call is an at sign ('@') followed by a contiguous sequence
of letters, digits, and underscores, followed by matching parentheses
containing zero or more function arguments.
The text between functions calls is ignored (it doesn't even have to
be text).
.Pp
Function arguments may be either other nested function calls (the argument
to the outer function is the result of the inner function invocation),
or constant literal strings in double quotes (the argument is the
value of the string).
Therefore, all function arguments begin with either an at sign or
a double quote character.
Function arguments are separated by commas and may have surrounding
whitespace, which is ignored.
.Pp
Constant literal strings are enclosed in double quotes and respect the
usual C backslash escapes.
.Pp
Built-in functions (see below) that take zero arguments do not require
parentheses, but parentheses may be included for separation purposes.
.Pp
A parsed tmpl file is represented by a
.Li "struct tmpl" .
.\"
.Sh Execution
.\"
.Pp
The parsing of a tmpl file and its execution are separate steps.
Once a tmpl file has been parsed, it may be executed several times.
Execution requires a
.Em context ,
represented by a
.Li "struct tmpl_ctx" ,
and generates output which is written to an output stream.
.Pp
When the template is executed, the text between function calls is
copied to the output stream without modification, while the function
calls are replaced with their values, which are strings.
Functions are evaluated as they are encountered during execution.
.Pp
The
.Nm tmpl
library includes several built-in functions, including
special control flow functions that control input processing.
The user code may also implement custom functions.
.Pp
User functions may return
.Dv NULL
and set
.Va errno
to indicate an error; they may also set an error string (if
no error string is set,
.Xr strerror 3
is used to generate one).
When such an error occurs, the function does not return at all.
Instead, an error message is propagated up to the outer-most function call.
The error message is reformatted by an optional user-supplied error
formatter function, and then the formatted message is written
to the output stream.
.\"
.Sh Built-in Functions
.\"
The built-in functions are listed below.
In these definitions, the numeric value of a string is the result
of parsing it with
.Xr strtol 3 ,
and a string is considered "true" if it has a numeric value other than zero.
.Pp
.Bl -hang -compact -width "xx"
.It Em "Control flow"
.Bl -hang -width "xx"
.It Li "@while(x) ... @endwhile"
.Pp
The text in between is repeated as long as the argument supplied to
.Li "@while()"
is true.
.It Li "@loop(x) ... @endloop"
.Pp
The text in between is repeated N times, where N is the numerical value
of the argument passed to
.Li "@loop()" .
.It Li "@loopindex(x)"
.Pp
Takes zero or one argument; returns the loop index (counting from zero)
of the loop that is N loops out from the innermost containing loop, where
N is the numerical value of the argument, or -1 if no such loop exists.
If the argument is omitted it is assumed to be zero.
.It Li "@if(x) ... [ @elif(y) ... ] [ @else ... ] @endif"
.Pp
Conditional execution depending on the truth value of the argument to
.Li "@if()" .
Zero or more
.Li "@elif()"
blocks may be followed by zero or one
.Li "@else"
block.
An
.Li "@endif"
is always required.
.It Li "@break()"
.Pp
Break out of the innermost enclosing
.Li "@loop"
or
.Li "@while" .
.It Li "@continue()"
.Pp
Continue with the next iteration of the nearest enclosing
.Li "@loop"
or
.Li "@while" .
.It Li "@return()"
.Pp
Return from within a run-time function.
.It Li "@eval(x)
.Pp
Parses the argument as a template, executes it, and returns the resulting
output.
.El
.Pp
.It Em "Run-time variables and functions"
.Bl -hang -width "xx"
.It Li "@set(name, value)"
.Pp
Sets the run-time variable named by the first argument to have the
value equal to the second argument.
All run-time variables are global and exist as long as the associated
execution context exists.
.It Li "@get(name)"
.Pp
Returns the value of the run-time variable named by the first argument,
or the empty string if the variable is not set.
.It Li "@define(name) ... @enddef"
.Pp
Defines a run-time function.
The text in between is executed whenever @name(...) is invoked.
During this execution, the variables
.Fa argc
and
.Fa arg0 ,
.Fa arg1 ,
\&... are set to the function argument count and arguments, respectively;
.Fa arg0
is always equal to the name of the function.
All run-time functions are global and exist as long as the associated
execution context exists.
.It Li "@invoke()
.Pp
Invokes a function.
The function to be invoked and its arguments are described by the
run-time variables
.Fa argc
and
.Fa arg0 ,
.Fa arg1 ,
\&...
as above.
So
.Fa arg0
is the function name and
.Fa arg0 ,
.Fa arg1 ,
\&...
are the function arguments.
@invoke() itself does not take any arguments.
.El
.Pp
.It Em "Evaluators"
.Bl -hang -width "xx"
.It Li "@equal(x, y)"
.Pp
Returns "1" if x and y are identical, otherwise "0".
.It Li "@not(x)"
.Pp
Returns "1" if x is false, otherwise "0".
.It Li "@and(...)"
.Pp
Returns "1" if all of the arguments are true, otherwise "0".
.It Li "@or(...)"
.Pp
Returns "1" if any of the arguments is true, otherwise "0".
.It Li "@add(...)"
.Pp
Returns the sum of the arguments.
.It Li "@sub(x, ...)"
.Pp
Returns the second and subsequent arguments subtracted from the first.
.It Li "@mul(...)"
.Pp
Returns the product of the arguments.
.It Li "@div(x, y)"
.Pp
Returns the first argument divided by the second.
.It Li "@mod(x, y)"
.Pp
Returns the first argument modulo the second.
.It Li "@lt(x, y)"
.Pp
Returns "1" if the first argument is less than the second, otherwise "0".
.It Li "@le(x, y)"
.Pp
Returns "1" if the first argument is less than or equal to the second,
otherwise "0".
.It Li "@gt(x, y)"
.Pp
Returns "1" if the first argument is greater than the second, otherwise "0".
.It Li "@ge(x, y)"
.Pp
Returns "1" if the first argument is greater than or equal to the second,
otherwise "0".
.El
.Pp
.It Em "String functions"
.Bl -hang -width "xx"
.It Li "@cat(...)"
.Pp
Returns the concatenation of all of the arguments.
.It Li "@@()"
.Pp
Returns "@".
.It Li "@error(arg)
.Pp
Returns the argument formatted using the caller-supplied error formatter.
.It Li "@htmlencode(arg)
.Pp
Encodes the argument with HTML escapes and returns the result.
.It Li "@urlencode(arg)
.Pp
Encodes the argument with URL escapes and returns the result.
.El
.Pp
.It Em "I/O functions"
.Bl -hang -width "xx"
.It Li "@flush()
.Pp
Flushes the output stream.
.It Li "@output(arg)
.Pp
Outputs the argument directly to the output stream. That is,
if this function is invoked from within a user-defined function,
the argument goes directly to the template output rather than
being concatenated to the return value of the function.
.El
.Pp
.El
.\"
.Sh API
.\"
.Fn tmpl_create
parses input from
.Fa input
and creates and returns a new template object,
which uses
.Xr typed_mem 3
type
.Fa mtype .
If
.Fa num_errors
is not
.Dv NULL ,
then the number of parse errors detected is stored in
.Fa "*num_errors" .
A parse error is an occurrence of the
.Dq @
character that is not the beginning of a well-formed
.Nm
function invocation.
.Pp
.Fn tmpl_create_mmap
parses the contents of the file named
.Fa path ,
using
.Xr mmap 2
internally to avoid having to store the entire file in memory.
This results in less memory being used; however, if the file's contents
are changed then subsequent invocations of
.Fn tmpl_execute
may give garbled output.
.Pp
.Fn tmpl_destroy
destroys a template object.
Upon return,
.Fa "*tmplp"
will be set to
.Dv NULL .
If
.Fa "*tmplp"
is already
.Dv NULL
when
.Fn tmpl_destroy
is invoked, nothing happens.
.Pp
.Fn tmpl_ctx_create
creates a new template execution context.
.Fa mtype
is the
.Xr typed_mem 3
type used not only for the execution context object itself, but also
for all strings generated during execution.
In particular, all strings returned by user functions must be stored
in buffers allocated with this type.
The
.Fa arg
is a user cookie ignored by the
.Nm tmpl
functions.
The parameters
.Fa arg
and
.Fa mtype
may be retrieved with
.Fn tmpl_ctx_get_arg
and
.Fn tmpl_ctx_get_mtype ,
respectively.
.Pp
.Fa handler
and
.Fa errfmtr
point to functions having these types:
.Pp
.Bd -literal -compact -offset 3n
typedef char *tmpl_handler_t(struct tmpl_ctx *ctx, char **errmsgp,
int argc, char **argv);
typedef char *tmpl_errfmtr_t(struct tmpl_ctx *ctx, const char *errmsg);
.Ed
.Pp
.Fn handler
returns the result of invoking the function described by
.Fa argc
and
.Fa argv
as a '\\0'-terminated string allocated with the
.Xr typed_mem 3
type
.Fa mtype .
The first argument is always the function name, and subsequent arguments
are the (evaluated) arguments passed to the function.
Therefore,
.Fa argc
is always at least one.
.Pp
.Fn handler
may indicate an error by returning
.Dv NULL ,
in which case it should either set
.Va errno
appropriately or else set
.Fa "*errmsgp"
to point to an error message (which should also be allocated with
.Xr typed_mem 3
type
.Fa mtype) ;
.Fa "*errmsgp"
will be
.Dv NULL
when
.Fn handler
is invoked.
.Pp
The error formatter function
.Fn errfmtr
is optional.
It should return an error string allocated with
.Xr typed_mem 3
type
.Fa mtype
and formatted appropriately for the template output
(e.g., in HTML).
The
.Fa errmsg
is the original, unformatted error string returned by the function handler;
if the handler did not return an explicit error message,
.Li "strerror(errno)"
is used for
.Fa errmsg .
.Pp
.Fn tmpl_list_handler
may be useful for implementing
.Fn handler
when there is a fixed list of user functions.
The
.Fa userfuncs
parameter points to a length
.Fa uflen
array of
.Li "struct tmpl_func" :
.Pp
.Bd -literal -compact -offset 3n
struct tmpl_func {
const char *name; /* function name, null to end list */
u_int min_args; /* min # args (not counting name) */
u_int max_args; /* max # args (not counting name) */
tmpl_handler_t *handler; /* handler for function */
};
.Ed
.Pp
Each entry in the array describes a user function.
The function called
.Fa name
accepts at least
.Fa min_args
and at most
.Fa max_args
parameters, and is implemented by the
.Fa handler .
The array must be sorted lexicographically by name.
.Fn tmpl_list_handler
finds the function named by
.Li "argv[0]"
using a binary search of the array and invokes its
.Fn handler
with the supplied arguments.
In the case of an error,
.Fn tmpl_list_handler
prepends the returned error string with the offending function call
and arguments.
.Pp
.Fn tmpl_ctx_set_var
and
.Fn tmpl_ctx_get_var
may be used to set and retrieve variables associated with
.Fa ctx .
.Pp
.Fn tmpl_ctx_destroy
destroys a template context.
Upon return,
.Fa "*ctxp"
will be set to
.Dv NULL .
If
.Fa "*ctxp"
is already
.Dv NULL
when
.Fn tmpl_ctx_destroy
is invoked, nothing happens.
.Pp
.Fn tmpl_execute
executes the parsed template
.Fa tmpl
using the execution context
.Fa ctx ,
and writes the output to
.Fa output .
.Fa flags
may contain any of the following values OR'd together:
.Pp
.Bd -literal -compact -offset 3n
TMPL_SKIP_NL_WHITE Skip newline plus whitespace
.Ed
.Pp
.Dv TMPL_SKIP_NL_WHITE
causes any inter-function occurrences of a newline followed by whitespace
to be ignored.
This often generates more intuitive output.
The example template given previously would generate this output if
.Dv TMPL_SKIP_NL_WHITE
were specified:
.Pp
.Bd -literal -compact -offset 3n
I'm going to count to three:
123
Done.
.Ed
.Pp
.Fn tmpl_execute_func
can be used to execute a single function defined in a template context.
This includes non-control flow built-in functions, user functions, and
run-time functions.
.Fa ctx ,
.Fa output ,
and
.Fa flags
are as with
.Fn tmpl_execute .
The function and arguments are described by
.Fa argc
and
.Fa argv ,
and
.Fa errmsgp
must point to a
.Li "char *"
error message pointer.
.Pp
.Fn tmpl_ctx_reset
resets an execution context to its initial state.
This causes any runtime variables and functions defined during a previous
execution using
.Fa ctx
to be forgotten.
.Sh RETURN VALUES
.Fn tmpl_create ,
.Fn tmpl_ctx_create ,
and
.Fn tmpl_execute
return
.Dv NULL
or -1 to indicate an error, with
.Va errno
set appropriately.
.Pp
.Fn tmpl_execute_func
returns -1 if there was an error.
In the case of a system error,
.Fa "*errmsgp"
will be set to
.Dv NULL
and
.Va errno
will be set appropriately; otherwise,
.Fa "*errmsgp"
will point to an appropriate error message allocated with
.Xr typed_mem 3
type
.Fa mtype ,
which the caller must eventually free.
.Sh SEE ALSO
.Xr http_servlet_tmpl 3 ,
.Xr libpdel 3 ,
.Xr strtol 3 ,
.Xr typed_mem 3
.Sh IMPLEMENTATION NOTES
Here are two common sources of bugs:
.Pp
.Bl -dash -compact -offset 3n
.It
User functions returning constant strings or strings allocated
with the wrong
.Xr typed_mem 3
type.
.It
Not sorting the user function array given to
.Fn tmpl_list_handler .
.El
.Sh HISTORY
The PDEL library was developed at Packet Design, LLC.
.Dv "http://www.packetdesign.com/"
.Sh AUTHORS
.An Archie Cobbs Aq archie@freebsd.org
.Sh BUGS
.Fn tmpl_create ,
.Fn tmpl_execute ,
and
.Fn tmpl_execute_func
may leak small amounts of memory if the thread is canceled.
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>