xref: /freebsd/contrib/libxo/doc/howto.rst (revision 1c4ee7dfb8affed302171232b0f612e6bcba3c10)
1
2Howtos: Focused Directions
3==========================
4
5This section provides task-oriented instructions for selected tasks.
6If you have a task that needs instructions, please open a request as
7an enhancement issue on github.
8
9Howto: Report bugs
10------------------
11
12libxo uses github to track bugs or request enhancements.  Please use
13the following URL:
14
15  https://github.com/Juniper/libxo/issues
16
17Howto: Install libxo
18--------------------
19
20libxo is open source, under a new BSD license.  Source code is
21available on github, as are recent releases.  To get the most
22current release, please visit:
23
24  https://github.com/Juniper/libxo/releases
25
26After downloading and untarring the source code, building involves the
27following steps::
28
29    sh bin/setup.sh
30    cd build
31    ../configure
32    make
33    make test
34    sudo make install
35
36libxo uses a distinct "*build*" directory to keep generated files
37separated from source files.
38
39.. index:: configure
40
41Use "`../configure --help`" to display available configuration
42options, which include the following::
43
44  --enable-warnings      Turn on compiler warnings
45  --enable-debug         Turn on debugging
46  --enable-text-only     Turn on text-only rendering
47  --enable-printflike    Enable use of GCC __printflike attribute
48  --disable-libxo-options  Turn off support for LIBXO_OPTIONS
49  --with-gettext=PFX     Specify location of gettext installation
50  --with-libslax-prefix=PFX  Specify location of libslax config
51
52Compiler warnings are a very good thing, but recent compiler version
53have added some very pedantic checks.  While every attempt is made to
54keep libxo code warning-free, warnings are now optional.  If you are
55doing development work on libxo, it is required that you
56use --enable-warnings to keep the code warning free, but most users
57need not use this option.
58
59.. index:: --enable-text-only
60
61libxo provides the `--enable-text-only` option to reduce the
62footprint of the library for smaller installations.  XML, JSON, and
63HTML rendering logic is removed.
64
65.. index:: --with-gettext
66
67The gettext library does not provide a simple means of learning its
68location, but libxo will look for it in /usr and /opt/local.  If
69installed elsewhere, the installer will need to provide this
70information using the "`--with-gettext=/dir/path`" option.
71
72.. index:: libslax
73
74libslax is not required by libxo; it contains the "oxtradoc" program
75used to format documentation.
76
77For additional information, see :ref:`building`.
78
79Howto: Convert command line applications
80----------------------------------------
81
82Common question: How do I convert an existing command line application?
83
84There are four basic steps for converting command line application to
85use libxo::
86
87- Setting up the context
88- Converting printf calls
89- Creating hierarchy
90- Converting error functions
91
92Setting up the context
93~~~~~~~~~~~~~~~~~~~~~~
94
95To use libxo, you'll need to include the "xo.h" header file in your
96source code files::
97
98    #include <libxo/xo.h>
99
100In your main() function, you'll need to call xo_parse_args to handling
101argument parsing (:ref:`xo_parse_args`).  This function removes
102libxo-specific arguments the program's argv and returns either the
103number of remaining arguments or -1 to indicate an error::
104
105    int
106    main (int argc, char **argv)
107    {
108        argc = xo_parse_args(argc, argv);
109        if (argc < 0)
110            return argc;
111        ....
112    }
113
114.. index:: atexit
115.. index:: xo_finish_atexit
116
117At the bottom of your main(), you'll need to call xo_finish() to
118complete output processing for the default handle (:ref:`handles`).  This
119is required to flush internal information buffers.  libxo provides the
120xo_finish_atexit function that is suitable for use with the
121:manpage:`atexit(3)` function::
122
123    atexit(xo_finish_atexit);
124
125Converting printf Calls
126~~~~~~~~~~~~~~~~~~~~~~~
127
128The second task is inspecting code for :manpage:`printf(3)` calls and
129replacing them with xo_emit() calls.  The format strings are similar
130in task, but libxo format strings wrap output fields in braces.  The
131following two calls produce identical text output::
132
133  OLD::
134    printf("There are %d %s events\n", count, etype);
135
136  NEW::
137    xo_emit("There are {:count/%d} {:event} events\n", count, etype);
138
139"count" and "event" are used as names for JSON and XML output.  The
140"count" field uses the format "%d" and "event" uses the default "%s"
141format.  Both are "value" roles, which is the default role.
142
143Since text outside of output fields is passed verbatim, other roles
144are less important, but their proper use can help make output more
145useful.  The "note" and "label" roles allow HTML output to recognize
146the relationship between text and the associated values, allowing
147appropriate "hover" and "onclick" behavior.  Using the "units" role
148allows the presentation layer to perform conversions when needed.  The
149"warning" and "error" roles allows use of color and font to draw
150attention to warnings.  The "padding" role makes the use of vital
151whitespace more clear (:ref:`padding-role`).
152
153The "*title*" role indicates the headings of table and sections.  This
154allows HTML output to use CSS to make this relationship more obvious::
155
156  OLD::
157    printf("Statistics:\n");
158
159  NEW::
160    xo_emit("{T:Statistics}:\n");
161
162The "*color*" roles controls foreground and background colors, as well
163as effects like bold and underline (see :ref:`color-role`)::
164
165  NEW::
166    xo_emit("{C:bold}required{C:}\n");
167
168Finally, the start- and stop-anchor roles allow justification and
169padding over multiple fields (see :ref:`anchor-role`)::
170
171  OLD::
172    snprintf(buf, sizeof(buf), "(%u/%u/%u)", min, ave, max);
173    printf("%30s", buf);
174
175  NEW::
176    xo_emit("{[:30}({:minimum/%u}/{:average/%u}/{:maximum/%u}{]:}",
177            min, ave, max);
178
179Creating Hierarchy
180~~~~~~~~~~~~~~~~~~
181
182Text output doesn't have any sort of hierarchy, but XML and JSON
183require this.  Typically applications use indentation to represent
184these relationship::
185
186  OLD::
187    printf("table %d\n", tnum);
188    for (i = 0; i < tmax; i++) {
189        printf("    %s %d\n", table[i].name, table[i].size);
190    }
191
192  NEW::
193    xo_emit("{T:/table %d}\n", tnum);
194    xo_open_list("table");
195    for (i = 0; i < tmax; i++) {
196        xo_open_instance("table");
197        xo_emit("{P:    }{k:name} {:size/%d}\n",
198                table[i].name, table[i].size);
199        xo_close_instance("table");
200    }
201    xo_close_list("table");
202
203The open and close list functions are used before and after the list,
204and the open and close instance functions are used before and after
205each instance with in the list.
206
207Typically these developer looks for a "for" loop as an indication of
208where to put these calls.
209
210In addition, the open and close container functions allow for
211organization levels of hierarchy::
212
213  OLD::
214    printf("Paging information:\n");
215    printf("    Free:      %lu\n", free);
216    printf("    Active:    %lu\n", active);
217    printf("    Inactive:  %lu\n", inactive);
218
219  NEW::
220    xo_open_container("paging-information");
221    xo_emit("{P:    }{L:Free:      }{:free/%lu}\n", free);
222    xo_emit("{P:    }{L:Active:    }{:active/%lu}\n", active);
223    xo_emit("{P:    }{L:Inactive:  }{:inactive/%lu}\n", inactive);
224    xo_close_container("paging-information");
225
226Converting Error Functions
227~~~~~~~~~~~~~~~~~~~~~~~~~~
228
229libxo provides variants of the standard error and warning functions,
230:manpage:`err(3)` and :manpage:`warn(3)`.  There are two variants, one
231for putting the errors on standard error, and the other writes the
232errors and warnings to the handle using the appropriate encoding
233style::
234
235  OLD::
236    err(1, "cannot open output file: %s", file);
237
238  NEW::
239    xo_err(1, "cannot open output file: %s", file);
240    xo_emit_err(1, "cannot open output file: {:filename}", file);
241
242.. index:: xo_finish
243
244Call xo_finish
245~~~~~~~~~~~~~~
246
247One important item: call `xo_finish` at the end of your program so
248ensure that all buffered data is written out.  You can call it
249explicitly call it, or use :manpage:`atexit(3)` to have
250`xo_finish_atexit` called implicitly on exit::
251
252  OLD::
253    exit(0);
254
255  NEW::
256    xo_finish();
257    exit(0);
258
259Howto: Use "xo" in Shell Scripts
260--------------------------------
261
262.. admonition:: Needed
263
264  Documentation is needed for this area.
265
266.. index:: Internationalization (i18n)
267.. index:: gettext
268.. index:: xopo
269
270.. _i18n:
271
272Howto: Internationalization (i18n)
273-----------------------------------------------
274
275    How do I use libxo to support internationalization?
276
277libxo allows format and field strings to be used a keys into message
278catalogs to enable translation into a user's native language by
279invoking the standard :manpage:`gettext(3)` functions.
280
281gettext setup is a bit complicated: text strings are extracted from
282source files into "*portable object template*" (.pot) files using the
283`xgettext` command.  For each language, this template file is used as
284the source for a message catalog in the "*portable object*" (.po)
285format, which are translated by hand and compiled into "*machine
286object*" (.mo) files using the `msgfmt` command.  The .mo files are
287then typically installed in the /usr/share/locale or
288/opt/local/share/locale directories.  At run time, the user's language
289settings are used to select a .mo file which is searched for matching
290messages.  Text strings in the source code are used as keys to look up
291the native language strings in the .mo file.
292
293Since the xo_emit format string is used as the key into the message
294catalog, libxo removes unimportant field formatting and modifiers from
295the format string before use so that minor formatting changes will not
296impact the expensive translation process.  We don't want a developer
297change such as changing "/%06d" to "/%08d" to force hand inspection of
298all .po files.  The simplified version can be generated for a single
299message using the `xopo -s $text` command, or an entire .pot can be
300translated using the `xopo -f $input -o $output` command::
301
302    EXAMPLE:
303        % xopo -s "There are {:count/%u} {:event/%.6s} events\n"
304        There are {:count} {:event} events\n
305
306    Recommended workflow:
307        # Extract text messages
308	xgettext --default-domain=foo --no-wrap \
309	    --add-comments --keyword=xo_emit --keyword=xo_emit_h \
310	    --keyword=xo_emit_warn -C -E -n --foreign-user \
311	    -o foo.pot.raw foo.c
312
313        # Simplify format strings for libxo
314        xopo -f foo.pot.raw -o foo.pot
315
316        # For a new language, just copy the file
317        cp foo.pot po/LC/my_lang/foo.po
318
319        # For an existing language:
320        msgmerge --no-wrap po/LC/my_lang/foo.po \
321                foo.pot -o po/LC/my_lang/foo.po.new
322
323        # Now the hard part: translate foo.po using tools
324        # like poedit or emacs' po-mode
325
326        # Compile the finished file; Use of msgfmt's "-v" option is
327        # strongly encouraged, so that "fuzzy" entries are reported.
328        msgfmt -v -o po/my_lang/LC_MESSAGES/foo.mo po/my_lang/foo.po
329
330        # Install the .mo file
331        sudo cp po/my_lang/LC_MESSAGES/foo.mo \
332                /opt/local/share/locale/my_lang/LC_MESSAGE/
333
334Once these steps are complete, you can use the `gettext` command to
335test the message catalog::
336
337    gettext -d foo -e "some text"
338
339i18n and xo_emit
340~~~~~~~~~~~~~~~~
341
342There are three features used in libxo used to support i18n:
343
344- The "{G:}" role looks for a translation of the format string.
345- The "{g:}" modifier looks for a translation of the field.
346- The "{p:}" modifier looks for a pluralized version of the field.
347
348Together these three flags allows a single function call to give
349native language support, as well as libxo's normal XML, JSON, and HTML
350support::
351
352    printf(gettext("Received %zu %s from {g:server} server\n"),
353           counter, ngettext("byte", "bytes", counter),
354           gettext("web"));
355
356    xo_emit("{G:}Received {:received/%zu} {Ngp:byte,bytes} "
357            "from {g:server} server\n", counter, "web");
358
359libxo will see the "{G:}" role and will first simplify the format
360string, removing field formats and modifiers::
361
362    "Received {:received} {N:byte,bytes} from {:server} server\n"
363
364libxo calls :manpage:`gettext(3)` with that string to get a localized
365version.  If your language were *Pig Latin*, the result might look
366like::
367
368    "Eceivedray {:received} {N:byte,bytes} omfray "
369               "{:server} erversay\n"
370
371Note the field names do not change and they should not be translated.
372The contents of the note ("byte,bytes") should also not be translated,
373since the "g" modifier will need the untranslated value as the key for
374the message catalog.
375
376The field "{g:server}" requests the rendered value of the field be
377translated using :manpage:`gettext(3)`.  In this example, "web" would
378be used.
379
380The field "{Ngp:byte,bytes}" shows an example of plural form using the
381"{p:}" modifier with the "{g:}" modifier.  The base singular and plural
382forms appear inside the field, separated by a comma.  At run time,
383libxo uses the previous field's numeric value to decide which form to
384use by calling :manpage:`ngettext(3)`.
385
386If a domain name is needed, it can be supplied as the content of the
387{G:} role.  Domain names remain in use throughout the format string
388until cleared with another domain name::
389
390    printf(dgettext("dns", "Host %s not found: %d(%s)\n"),
391        name, errno, dgettext("strerror", strerror(errno)));
392
393    xo_emit("{G:dns}Host {:hostname} not found: "
394            "%d({G:strerror}{g:%m})\n", name, errno);
395