sf_fmt(3)

NAME
format_init, format_free, format_metarule, formatf, format_lastresult, format_lastsize, format_detach - template formatting functions.

SYNOPSIS
#include <strfunc.h>

fmt_base *
format_init();
void
format_free(fmt_base *base);
int
format_metarule(fmt_base *base, char leftBrace, char rightBrace, char **(*function)(char *found, void *optKeyPassed));
char *
formatf(fmt_base *base, char *template, void *optKeyToPass);
char *
format_lastresult(fmt_base *base, size_t *optSize);
size_t
format_lastsize(fmt_base *base);
char *
format_detach(fmt_base *base, size_t *optReturnSize);

DESCRIPTION
It is widely used for programs to read a template and display the data according to it.

Those functions forms a powerful solution to work with a kind of "active" templates. The term "active" means that some operations, like a test of existence of a keyword, or equality tests, can be placed directly to the template and no special handling needed in an application to support it. strfunc(3) library will automagically do the dirty work for you.

The internal language of defining the template is powerful yet simple. Before we run into detailed explanation, let's talk about programming.

First, programmer needed to initialize the formatting engine by defining the formatting rules. Formatting rules are stored inside the special structure fmt_base. format_init() creates an empty structure and returns the pointer to it. One may define a numerous formatting bases and randomly use them. Formatting base may be freed using format_free(fmt_base *). Please note that format_init() will never return the NULL pointer.

Second, there is a need to fill this empty structure with some formatting rules using the format_metarule() function. Formatting rules are defined by specifiyng a braces and the function which will handle the data inside the braces.

Say, we have a template "abc${def}ghi". Programmer should call the format_metarule(fb, '{', '}', handler), where fb is the pointer to the formatting base returned by format_init(). If two braces are equal, the result is undefined. handler is the function which should be defined as

char ** handler(char *found, void *optKeyPassed).

It is possible to define a number of such a rules for the single fmt_base. The handler function should return the pointer to the internal char ** data when it is possible, and NULL pointer otherwise. Please NOTE that this pointer is never modified or freed and probably it should be the semi-static structure inside the handler function.

When you're finished to fill the fmt_base with the rules, the function formatf() can be used multiple times to convert the specified template to the destination text. The optional optKeyToPass argument may be specified to pass some additional data, if necessary.

formatf() will place the output to the buffer located in the fmt_base and return a pointer to it. The pointer will never be NULL, and is not to be freed. If you need this buffer to be completely yours and do not want to strdup(3) it, there is a function called format_detach(fmt_base *, size_t *optReturnSize) to achive this.

Two other functions, char * format_lastresult(fmt_base *, size_t *optSize) and size_t format_lastsize(fmt_base *), are to be used to obtain the pointer to the buffer and its size without invoking formatf() once more.

ACTIVE TEMPLATE DEFINITION
If a template is not containing the special tokens it will considered as plain text and returned unmodified.

Special token and the whole template are defined in the following BNF:

    <template>  :=  *(*<string> *<token> *<string>)
    <token>     :=  <simple> | <join> | <index> | <choise> | <equality>
    <simple>    :=  '$' <LB> <param> <RB>
    <join>      :=  '$' <LB> <param> '+' <delimiter> <RB>
    <index>     :=  '$' <LB> <param> '[' <number> ']' <RB>
    <choise>    :=  '$' <LB> <param> '?' <iftrue> ':' <iffalse> <RB>
    <equality>  :=  '$' <LB> <param> { "==" | "!=" } *<string> '?' <iftrue> ':' <iffalse> <RB>
    <LB>        :=  <the second argument of format_metarule()>
    <RB>        :=  <the third argument of format_metarule()>
    <param>     := <string>
    <delimiter> := <string>
    <iftrue>    := <template>
    <iffalse>   := <template>
    <string>    :=  *<CHAR>
    <number>    :=  1*<character from '0' to '9'>

The word param defined above, will be passed as the first argument to the handler function.

ACTIVE TEMPLATE EXAMPLE
The following is an example of the typical template. It may be placed to a file, then read and passed to formatf(). It is also can be defined as the argument's value within the configuration file read by cfgread(3).

    Login: ${login[0]}
    Password: ${password}
    Username: ${name?${name}:Unknown name}
    Comments: ${comment+, }
    $<status==Busy?User is busy>

You can see the index token "${login[0]}", the simple token "${password}", the join token right after the "Comments: ", and equality token is the whole last string. Please note that the last token is formed using the angle braces: you should specify an additional handler function to handle this case. Refer to the PROGRAMMING EXAMPLE section.

Simple tokens are used to display the data returned by the handler() function almost without the modification. One exception from this rule exists: if handler return multiple values they are joined together with the string ", " as the separator.

Join tokens are used to join the multiple values. As mentioned above, the handler function return the string array. In this case all values will be joined together separated by the specified delimiter. For this primer the delimiter is ", " (comma followed by single space).

Index token used to get the specified value from the string array returned by handler. Values are counted from zero, so zero index will represent the first available string.

Choise token used to give the dynamic behavior to the templates. The <iftrue> section will be placed to the output buffer if the handler return the valid non-NULL pointer to the non-empty array. This form can be used to rule the output if the expected parameter is not present or empty.

Equality token used to test the array returned by the handler against the string value. The <iftrue> section will be placed to the output buffer if this string match at least one of the array's elements. All comparisons are canse-insensitive.

PROGRAMMING EXAMPLE
It is interesting how to implement the things to parse the above template example.

    #include <strfunc.h>
    char **handler1(char *found, void *optKeyPassed);
    char **handler2(char *found, void *optKeyPassed);
    int
    main() {
    	fmt_base *fb;
    	char *template = "Login: ${login[0]}\nPassword: ${password}\nUsername: ${name?${name}:Unknown name}\nComments: ${comment+, }\n$<status==Busy?User is busy>\n";
    	char *s;
    	/* Create empty structure */
    	fb = format_init();
    	/* Add one formatting rule */
    	format_metarule(fb, '{', '}', handler1);
    	/* Add another formatting rule to parse angle braces */
    	format_metarule(fb, '<', '>', handler1);
    	/* Format the template */
    	s = formatf(fb, template, NULL);
    	/* Print out the result */
    	printf("%s", s);
    	/* Free the formatting structure */
    	format_free(fb);
    	return 0;
    };
    char **
    handler1(char *found, void *optKeyPassed) {
    	static char *arr[3] = { NULL, NULL, NULL };
    	(void)optKeyPassed;
    	arr[1] = NULL;
    	if(strcasecmp(found, "login") == 0) {
    		arr[0] = "john";
    		return NULL;
    	};
    	if(strcasecmp(found, "name") == 0) {
    		arr[0] = "John Smith";
    		return NULL;
    	};
    	if(strcasecmp(found, "password") == 0) {
    		arr[0] = "123";
    		return NULL;
    	};
    	if(strcasecmp(found, "comment") == 0) {
    		arr[0] = "Comment value #1";
    		arr[1] = "Comment value #2";
    		return NULL;
    	};
    	return NULL;
    };
    char **
    handler2(char *found, void *optKeyPassed) {
    	static char *arr[] = { NULL, NULL };
    	(void)optKeyPassed;
    	if(strcasecmp(found, status) == 0) {
    		arr[0] = "busy";
    		return arr;
    	};
    	return NULL;
    };

SEE ALSO
strfunc(3), cfgread(3).

AUTHORS
Lev Walkin <vlm@spelio.net.ru>