1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 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)");
|