summaryrefslogtreecommitdiff
path: root/doc/string_formatter.rst
blob: 77badf8a2ae55be9dbf4d5fc2fc654e8c50ae807 (plain)
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)");