summaryrefslogtreecommitdiff
path: root/doc/string_formatter.rst
diff options
context:
space:
mode:
Diffstat (limited to 'doc/string_formatter.rst')
-rw-r--r--doc/string_formatter.rst211
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)");