.\" 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 .\" .\" $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.