diff options
Diffstat (limited to 'doc/string_formatter.rst')
-rw-r--r-- | doc/string_formatter.rst | 211 |
1 files changed, 211 insertions, 0 deletions
diff --git a/doc/string_formatter.rst b/doc/string_formatter.rst new file mode 100644 index 0000000..77badf8 --- /dev/null +++ b/doc/string_formatter.rst @@ -0,0 +1,211 @@ +======================= +String format templates +======================= + +HXfmt is a template system for by-name variable expansion. It can be used to +substitute placeholders in format strings supplied by the user by appropriate +expanded values defined by the program. Such can be used to allow for flexible +configuration files that define key-value mappings such as + +:: + + detect_peer = ping6 -c1 %(ADDR) + #detect_peer = nmap -sP %(ADDR) | grep -Eq "appears to be up" + +Consider, for example, a monitoring daemon that allows the administrator to +specify a program of his choice with which to detect whether a peer is alive or +not. The user can choose any program that is desired, but evidently needs to +pass the address to be tested to the program. This is where the daemon will do +a substitution of the string ``ping -c1 %(ADDR)`` it read from the config file, +and put the actual address in it before finally executing the command. + +.. code-block:: c + + printf("%s has %u files\n", user, num); + printf("%2$u files belong to %1$s\n", num, user); + +``%s`` (or ``%1$s`` here) specifies how large ``user`` is — ``sizeof(const char +*)`` in this case. If that is missing, there is no way to know the offset of +``num`` relative to ``user``, making varargs retrieval impossible. + +``printf``, at least from GNU libc, has something vaguely similar: positional +parameters. They have inherent drawbacks, though. One is of course the question +of portability, but there is a bigger issue. All parameters must be specified, +otherwise there is no way to determine the location of all following objects +following the missing one on the stack in a varargs-function like printf, which +makes it unsuitable to be used with templates where omitting some placeholders +is allowed. + +Initialization, use and deallocation +==================================== + +.. code-block:: c + + #include <libHX/option.h> + + struct HXformat_map *HXformat_init(void); + void HXformat_free(struct HXformat_map *table); + int HXformat_add(struct HXformat_map *table, const char *key, const void *ptr, unsigned int ptr_type); + +``HXformat_init`` will allocate and set up a string-to-string map that is used +for the underlying storage, and returns it. + +To release the substitution table and memory associated with it, call +``HXformat_free``. + +``HXformat_add`` is used to add substitution entries. One can also specify +other types such as numeric types. ``ptr_type`` describes the type behind +``ptr`` and the constants are the same from ``option.h`` (cf. section on +optionp arsing) — not all constants can be used, though, and their meaning also +differs from what ``HX_getopt`` or ``HX_shconfig`` use them for — the two could +be seen as “read” operations, while ``HXformat`` is a write operation. + +Immediate types +=============== + +“Immediate types” are resolved when ``HXformat_add`` is called, that is, they +are copied and inserted into the tree, and are subsequently independent from +any changes to variables in the program. Because the HXopt-originating type +name, i.e. ``HXTYPE_*``, is also used for deferred types, the constant +``HXFORMAT_IMMED`` needs to be specified on some types to denote an immediate +value. + +* ``HXTYPE_STRING`` — ptr is a ``const char *``. + +* ``HXTYPE_{U,}{CHAR,SHORT,INT,LONG,LLONG} | HXFORMAT_IMMED`` — + mapping to the standard typesk + +Deferred types +============== + +“Deferred types” are resolved on every invocation of a formatter function +(``HXformat_*printf``). The expansions may be changed by modifying the +underlying variable pointed to, but the pointer must remain valid and its +pointee not go out of scope. Code samples are provided below. + +* ``HXTYPE_STRP`` — ptr is a ``const char *const *``; the + pointer resolution is deferred until the formatter is called with one of the + ``HXformat_*printf`` functions. Deferred in the sense it is always resolved + anew. + +* ``HXTYPE_BOOL`` — ptr is a ``const int *``. + +* ``HXTYPE_{U,}..``, ``HXTYPE_FLOAT``, ``HXTYPE_DOUBLE`` — mapping to the + standard types with one indirection (e.g. ``int *``). + +Invoking the formatter +====================== + +.. code-block:: c + + int HXformat_aprintf(struct HXformat_map *table, hxmc_t **dest, const char *template); + int HXformat_sprintf(struct HXformat_map *table, char *dest, size_t size, const char *template); + int HXformat_fprintf(struct HXformat_map *table, FILE *filp, const char *template); + +``HXformat_aprintf`` + Substitutes placeholders in template using the given table. This will + produce a string in a HX memory container (``hxmc_t``), and the pointer + is placed into ``*dest``. The caller will be responsible for freeing it + later when it is done using the result. + +``HXformat_sprintf`` + Does substitution and stores the expanded result in the buffer ``dest`` + which is of size ``size``. + +``HXformat_fprintf`` + Does substituion and directly outputs the expansion to the given stdio + stream. + +On success, the length of the expanded string is returned (only up to a maximum +of SSIZE_MAX), excluding the trailing ``\0``. While ``HXformat_sprintf`` will +not write more than ``size`` bytes (including the ``\0``), the length it would +have taken is returned, similar to what sprintf does. On error, ``-errno`` is +returned. + +The HXformat function family recognizes make-style like functions and recursive +expansion, described below. + +Functions +========= + +To expand a variable, one uses a syntax like ``%(NAME)`` in the format string. +Recursive expansion like ``%(%(USER))`` is supported; assuming ``%(USER)`` +would expand to ``linux``, HXformat would try to resolve ``%(linux)`` next. +Besides these variable substitutions, HXformat also provides function calls +whose syntax isx ``%(nameOfFunction parameters[...])``. Parameters can be any +text, including variables. Paramters are separated from another by a delimiter +specific to each function. See this list for details: + +* ``%(env variable)`` + + The ``env`` function expands to the string that is stored in the + environmental variable by the given name. + +* ``%(exec command [args...])`` + + The ``exec`` function expands to the standard output of the command. The + command is directly run without shell invocation, so no special character + expansion (wildcards, etc.) takes place. stdin is set to ``/dev/null``. The + parameter delimiter is the space character. To be able to use this function — + as it is relevant to security — the fmt table needs to have a key + with the magic value ``/libhx/exec``. + +* ``%(if condition,[then][,[else]])`` + + If the condition parameter expands to a string of non-zero length, the + function expands to the ``then`` block, otherwise the ``else`` block. The + delimiter used is a comma. + +* ``%(lower text)``, ``%(upper text)`` + + Lowercases or uppercases the supplied argument. As these functions are meant + to take only one argument, there is no delimiter defined that would need + escaping if multiple arguments were supposed to be passed. ``%(lower a,b)`` + is equivalent to ``%(lower "a,b")``. + +* ``%(shell command [args...])`` + + Similar to ``%(exec)``, but invokes the shell inbetween (i.e. ``sh -c + 'command...'``) such that special characters, redirection, and so on can be + used. + +* ``%(substr text,offset[,length])`` + + Extracts a substring out of the given text, starting at offset and running + for the given length. If no length is given, will extract until the end of + the string. If ``offset`` is negative, it specifies the offset from the end + of the string. If ``length`` is negative, that many characters are left off + the end. + +* ``%(snl text)`` + + Strips trailing newlines from text and replaces any other newline by a space. + What happens implicity in Makefiles' ``$(shell ...)`` statements usually is + explicitly separate in libHX. + +Example: Immediate and deferred resolution +========================================== + +.. code-block:: c + + const char *b = "Hello World"; + char c[] = "Hello World"; + struct HXformat_map *table = HXformat_init(); + HXformat_add(table, "%(GREETING1)", b, HXTYPE_STRING); + HXformat_add(table, "%(GREETING2)", &c, HXTYPE_STRP); + b = NULL; + snprintf(c, sizeof(c), "Hello Home"); + HXformat_aprintf(...); + +Upon calling ``HXformat_*printf``, ``%(GREETING1)`` will expand to ``Hello +World`` whereas ``%(GREETING2)`` will expand to ``Hello Home``. + + +Example: Using the %(exec) function +=================================== + +.. code-block:: c + + struct HXformat_map *table = HXformat_init(); + HXformat_add(table, "/libhx/exec", NULL, HXTYPE_IMMED); + HXformat_aprintf(table, &result, "%(exec uname -s)"); |