diff options
author | marcel <marcel@FreeBSD.org> | 2014-10-23 22:30:14 +0000 |
---|---|---|
committer | marcel <marcel@FreeBSD.org> | 2014-10-23 22:30:14 +0000 |
commit | fefcd296e4716886912a74acd38abba4c94bc340 (patch) | |
tree | ce8b178967e5d33cbba89d0b2f3d2c2ea45616cb /contrib/libxo/doc | |
parent | bc4f095bf4554f6047f55bd8d28df41679cade6b (diff) | |
download | FreeBSD-src-fefcd296e4716886912a74acd38abba4c94bc340.zip FreeBSD-src-fefcd296e4716886912a74acd38abba4c94bc340.tar.gz |
Import libxo 0.1.4
Obtained from: https://github.com/Juniper/libxo
Sponsored by: Juniper Networks, Inc.
Diffstat (limited to 'contrib/libxo/doc')
-rw-r--r-- | contrib/libxo/doc/Makefile.am | 65 | ||||
-rw-r--r-- | contrib/libxo/doc/libxo.txt | 2400 |
2 files changed, 2465 insertions, 0 deletions
diff --git a/contrib/libxo/doc/Makefile.am b/contrib/libxo/doc/Makefile.am new file mode 100644 index 0000000..c0c3271 --- /dev/null +++ b/contrib/libxo/doc/Makefile.am @@ -0,0 +1,65 @@ +# +# $Id$ +# +# Copyright 2014, Juniper Networks, Inc. +# All rights reserved. +# This SOFTWARE is licensed under the LICENSE provided in the +# ../Copyright file. By downloading, installing, copying, or otherwise +# using the SOFTWARE, you agree to be bound by the terms of that +# LICENSE. + +OXTRADOC_DIR = ${SLAX_OXTRADOCDIR} +OXTRADOC_PREFIX = ${OXTRADOC_DIR} +OXTRADOC = ${OXTRADOC_DIR}/oxtradoc +SLAXPROC_BINDIR = ${SLAX_BINDIR} + +XML2RFC = ${OXTRADOC_DIR}/xml2rfc.tcl +XML2HTMLDIR = ${OXTRADOC_DIR} +XML2HTMLBIN = ${XML2HTMLDIR}/rfc2629-to-html.slax +SLAXPROC = ${SLAX_BINDIR}/slaxproc + +SLAXPROC_ARGS = \ + -a oxtradoc-dir ${OXTRADOC_DIR} \ + -a oxtradoc-install-dir ${OXTRADOC_DIR} \ + -a anchor-prefix docs + +SLAXPROC_ARGS_INLINE = \ + -a oxtradoc-inline yes + +SLAXPROC_ARGS += ${SLAXPROC_ARGS_INLINE} + +XML2HTML = \ + ${SLAXPROC} -g -e -I ${OXTRADOC_DIR} -I . \ + ${SLAXPROC_ARGS} \ + ${XML2HTMLBIN} + +OX_ARGS = -P ${OXTRADOC_PREFIX} -L ${OXTRADOC_PREFIX} +OX_ARGS += -S ${SLAXPROC} -p doc +OX_CMD = ${PERL} ${PERLOPTS} ${OXTRADOC} ${OX_ARGS} +OXTRADOC_CMD = ${OX_CMD} + + +OUTPUT = libxo-manual +INPUT = libxo.txt + +EXTRA_DIST = \ + ${INPUT} \ + ${OUTPUT}.html \ + ${OUTPUT}.txt + +doc docs: ${OUTPUT}.txt ${OUTPUT}.html + +${OUTPUT}.txt: ${INPUT} ${OXTRADOC} xolint.txt + ${OXTRADOC_CMD} -m text -o $@ $< + +${OUTPUT}.html: ${INPUT} ${OXTRADOC} ${XML2HTMLBIN} xolint.txt + ${OXTRADOC_CMD} -m html -o $@ $< + +xolint.txt: ${top_srcdir}/xolint/xolint.pl + perl ${top_srcdir}/xolint/xolint.pl -D > xolint.txt + +CLEANFILES = \ +${OUTPUT}.xml \ +${OUTPUT}.txt \ +${OUTPUT}.fxml \ +${OUTPUT}.html diff --git a/contrib/libxo/doc/libxo.txt b/contrib/libxo/doc/libxo.txt new file mode 100644 index 0000000..5148de0 --- /dev/null +++ b/contrib/libxo/doc/libxo.txt @@ -0,0 +1,2400 @@ +# +# Copyright (c) 2014, Juniper Networks, Inc. +# All rights reserved. +# This SOFTWARE is licensed under the LICENSE provided in the +# ../Copyright file. By downloading, installing, copying, or +# using the SOFTWARE, you agree to be bound by the terms of that +# LICENSE. +# Phil Shafer, July 2014 +# + +* libxo + +libxo - A Library for Generating Text, XML, JSON, and HTML Output + +You live in the present, but you want to live in the future. You'd +love a flying car, but need to get to work today. You want to support +features like XML, JSON, and HTML rendering to allow integration with +NETCONF, REST, and web browsers, but you need to make text output for +command line users. And you don't want multiple code paths that can't +help but get out of sync. None of this "if (xml) {... } else {...}" +logic. And ifdefs are right out. But you'd really, really like all +the fancy features that modern encoding formats can provide. + +The libxo library allows an application to generate text, XML, JSON, +and HTML output using a common set of function calls. The application +decides at run time which output style should be produced. The +application calls a function "xo_emit" to product output that is +described in a format string. A "field descriptor" tells libxo what +the field is and what it means. Each field descriptor is placed in +braces with a printf-like format string: + + xo_emit(" {:lines/%7ju} {:words/%7ju} " + "{:characters/%7ju}{d:filename/%s}\n", + linect, wordct, charct, file); + +Each field can have a role, with the 'value' role being the default, +and the role tells libxo how and when to render that field. Output +can then be generated in various style, using the "--libxo" option: + + % wc /etc/motd + 25 165 1140 /etc/motd + % wc --libxo xml,pretty,warn /etc/motd + <wc> + <file> + <filename>/etc/motd</filename> + <lines>25</lines> + <words>165</words> + <characters>1140</characters> + </file> + </wc> + % wc --libxo json,pretty,warn /etc/motd + { + "wc": { + "file": [ + { + "filename": "/etc/motd", + "lines": 25, + "words": 165, + "characters": 1140 + } + ] + } + } + % wc --libxo html,pretty,warn /etc/motd + <div class="line"> + <div class="text"> </div> + <div class="data" data-tag="lines"> 25</div> + <div class="text"> </div> + <div class="data" data-tag="words"> 165</div> + <div class="text"> </div> + <div class="data" data-tag="characters"> 1140</div> + <div class="text"> </div> + <div class="data" data-tag="filename">/etc/motd</div> + </div> + +** Getting libxo + +libxo lives on github as: + + https://github.com/Juniper/libxo + +The latest release of libxo is available at: + + https://github.com/Juniper/libxo/releases + +We are following the branching scheme from +^http://nvie.com/posts/a-successful-git-branching-model/^ +which means we will do development under the "develop" branch, and +release from the master. To clone a developer tree, run the following +command: + + git clone https://github.com/Juniper/libxo.git -b develop + +We're using semantic release numbering. + +* Overview + +Most unix commands emit text output aimed at humans. It is designed +to be parsed and understood by a user. Humans are gifted at extracted +details and pattern matching. Often programmers need to extract +information from this human-oriented output. Programmers use tools +like grep, awk, and regular expressions to ferret out the pieces of +information they need. Such solutions are fragile and require +updates when output contents change or evolve, requiring testing and +validation. + +Modern tool developers favors encoding schemes like XML and JSON, +which allow trivial parsing and extraction of data. Such formats are +simple, well understood, hierarchical, easily parsed, and often +integrate easier with common tools and environments. + +In addition, modern reality means that more output ends up in web +browsers than in terminals, making HTML output valuable. + +libxo allows a single set of function calls in source code to generate +traditional text output, as well as XML and JSON formatted data. HTML +can also be generated; "<div>" elements surround the traditional text +output, with attributes that detail how to render the data. + +A single libxo function call in source code is all that's required: + + xo_emit("Connecting to {:host}.{:domain}...\n", host, domain); + + Text: + Connection to my-box.example.com... + XML: + <host>my-box</host> + <domain>example.com</domain> + JSON: + "host": my-box", + "domain": "example.com" + +For brevity, the HTML output is emitted. + +** Encoding Styles + +There are four encoding styles supported by libxo: TEXT, HTML, JSON, +and XML. JSON and XML are suitable for encoding data, while TEXT and +HTML are suited for display to the user. TEXT output can be display +on a terminal session, allowing compatibility with traditional usage. +HTML can be matched with a small CSS file to permit rendering in any +HTML5 browser. XML output is suitable for tools like XPath and +protocols like NETCONF. JSON output can be used for RESTful APIs. + +*** Text Output + +Most traditional programs generate text output on standard output, +with contents like: + + 36 ./src + 40 ./bin + 90 . + +In this example (taken from du source code), the code to generate this +data might look like: + + printf("%d\t%s\n", num_blocks, path); + +Simple, direct, obvious. But it's only making text output. Imagine +using a single code path to make text, XML, JSON or HTML, deciding at +run time which to generate. + +libxo expands on the idea of printf format strings to make a single +format containing instructions for creating multiple output styles: + + xo_emit("{:blocks/%d}\t{:path/%s}\n", num_blocks, path); + +This line will generate the same text output as the earlier printf +call, but also has enough information to generate XML, JSON, and HTML. + +The following sections introduce the other formats. + +*** XML Output + +XML output consists of a hierarchical set of elements, each encoded +with a start tag and an end tag. The element should be named for data +value that it is encoding: + + <item> + <blocks>36</blocks> + <path>./src</path> + </item> + <item> + <blocks>40</blocks> + <path>./bin</path> + </item> + <item> + <blocks>90</blocks> + <path>.</path> + </item> + +XML is a W3C standard for encoding data. See w3c.org/TR/xml for +additional information. + +*** JSON Output + +JSON output consists of a hierarchical set of objects and lists, each +encoded with a quoted name, a colon, and a value. If the value is a +string, it must be quoted, but numbers are not quoted. Objects are +encoded using braces; lists are encoded using square brackets. +Data inside objects and lists is separated using commas: + + items: [ + { "blocks": 36, "path" : "./src" }, + { "blocks": 40, "path" : "./bin" }, + { "blocks": 90, "path" : "./" } + ] + +*** HTML Output + +HTML output is designed to allow the output to be rendered in a web +browser with minimal effort. Each piece of output data is rendered +inside a <div> element, with a class name related to the role of the +data. By using a small set of class attribute values, a CSS +stylesheet can render the HTML into rich text that mirrors the +traditional text content. + +Additional attributes can be enabled to provide more details about the +data, including data type, description, and an XPath location. + + <div class="line"> + <div class="data" data-tag="blocks">36</div> + <div class="padding"> </div> + <div class="data data-tag="path">./src</div> + </div> + <div class="line"> + <div class="data" data-tag="blocks">40</div> + <div class="padding"> </div> + <div class="data data-tag="path">./bin</div> + </div> + <div class="line"> + <div class="data" data-tag="blocks">90</div> + <div class="padding"> </div> + <div class="data data-tag="path">./</div> + </div> + +** Format Strings @format-strings@ + +libxo uses format strings to control the rendering of data into the +various output styles. Each format string contains a set of zero or +more field descriptions, which describe independent data fields. Each +field description contains a set of modifiers, a content string, and +zero, one, or two format descriptors. The modifiers tell libxo what +the field is and how to treat it, while the format descriptors are +formatting instructions using printf-style format strings, telling +libxo how to format the field. The field description is placed inside +a set of braces, with a colon (":") after the modifiers and a slash +("/") before each format descriptors. Text may be intermixed with +field descriptions within the format string. + +The field description is given as follows: + + '{' [ role | modifier ]* ':' [ content ] + [ '/' field-format [ '/' encoding-format ]] '}' + +The role describes the function of the field, while the modifiers +enable optional behaviors. The contents, field-format, and +encoding-format are used in varying ways, based on the role. These +are described in the following sections. + +In the following example, three field descriptors appear. The first +is a padding field containing three spaces of padding, the second is a +label ("In stock"), and the third is a value field ("in-stock"). The +in-stock field has a "%u" format that will parse the next argument +passed to the xo_emit function as an unsigned integer. + + xo_emit("{P: }{Lwc:In stock}{:in-stock/%u}\n", 65); + +This single line of code can generate text (" In stock: 65\n"), XML +("<in-stock>65</in-stock>"), JSON ('"in-stock": 6'), or HTML (too +lengthy to be listed here). + +*** Modifier Roles + +Modifiers are optional, and indicate the role and formatting of the +content. The roles are listed below; only one role is permitted: + +|---+--------------+-------------------------------------------------| +| M | Name | Description | +|---+--------------+-------------------------------------------------| +| D | decoration | Field is non-text (e.g. colon, comma) | +| E | error | Field is an error message | +| L | label | Field is text that prefixes a value | +| N | note | Field is text that follows a value | +| P | padding | Field is spaces needed for vertical alignment | +| T | title | Field is a title value for headings | +| U | units | Field is the units for the previous value field | +| V | value | Field is the name of field (the default) | +| W | warning | Field is a warning message | +| [ | start anchor | Begin a section of anchored variable-width text | +| ] | stop anchor | End a section of anchored variable-width text | +|---+--------------+-------------------------------------------------| + +**** The Decoration Role ({D:}) + +Decorations are typically punctuation marks such as colons, +semi-colons, and commas used to decorate the text and make it simpler +for human readers. By marking these distinctly, HTML usage scenarios +can use CSS to direct their display parameters. + + xo_emit("{D:((}{:name}{D:))}\n", name); + +**** The Label Role ({L:}) + +Labels are text that appears before a value. + + xo_emit("{Lwc:Cost}{:cost/%u}\n", cost); + +**** The Note Role ({N:}) + +Notes are text that appears after a value. + + xo_emit("{:cost/%u} {N:per year}\n", cost); + +**** The Padding Role ({P:}) + +Padding represents whitespace used before and between fields. + +The padding content can be either static, when placed directly within +the field descriptor, or a printf-style format descriptor can be used, +if preceded by a slash ("/"): + + xo_emit("{P: }{Lwc:Cost}{:cost/%u}\n", cost); + xo_emit("{P:/30s}{Lwc:Cost}{:cost/%u}\n", "", cost); + +**** The Title Role ({T:}) + +Title are heading or column headers that are meant to be displayed to +the user. The title can be either static, when placed directly within +the field descriptor, or a printf-style format descriptor can be used, +if preceded by a slash ("/"): + + xo_emit("{T:Interface Statistics}\n"); + xo_emit("{T:/%20.20s}{T:/%6.6s}\n", "Item Name", "Cost"); + +**** The Units Role ({U:}) + +Units are the dimension by which values are measured, such as degrees, +miles, bytes, and decibels. The units field carries this information +for the previous value field. + + xo_emit("{Lwc:Distance}{:distance/%u}{Uw:miles}\n", miles); + +Note that the sense of the 'w' modifier is reversed for units; +a blank is added before the contents, rather than after it. + +When the XOF_UNITS flag is set, units are rendered in XML as the +"units" attribute: + + <distance units="miles">50</distance> + +Units can also be rendered in HTML as the "data-units" attribute: + + <div class="data" data-tag="distance" data-units="miles" + data-xpath="/top/data/distance">50</div> + +**** The Value Role ({V:} and {:}) + +The value role is used to represent the a data value that is +interesting for the non-display output styles (XML and JSON). Value +is the default role; if no other role designation is given, the field +is a value. The field name must appear within the field descriptor, +followed by one or two format descriptors. The first format +descriptor is used for display styles (TEXT and HTML), while the +second one is used for encoding styles (XML and JSON). If no second +format is given, the encoding format defaults to the first format, +with any minimum width removed. If no first format is given, both +format descriptors default to "%s". + + xo_emit("{:length/%02u}x{:width/%02u}x{:height/%02u}\n", + length, width, height); + xo_emit("{:author} wrote \"{:poem}\" in {:year/%4d}\n, + author, poem, year); + +**** The Anchor Modifiers ({[:} and {]:}) + +The anchor roles allow a set of strings by be padded as a group, +but still be visible to xo_emit as distinct fields. Either the start +or stop anchor can give a field width and it can be either directly in +the descriptor or passed as an argument. Any fields between the start +and stop anchor are padded to meet the minimum width given. + +To give a width directly, encode it as the content of the anchor tag: + + xo_emit("({[:10}{:min/%d}/{:max/%d}{]:})\n", min, max); + +To pass a width as an argument, use "%d" as the format, which must +appear after the "/". Note that only "%d" is supported for widths. +Using any other value could ruin your day. + + xo_emit("({[:/%d}{:min/%d}/{:max/%d}{]:})\n", width, min, max); + +If the width is negative, padding will be added on the right, suitable +for left justification. Otherwise the padding will be added to the +left of the fields between the start and stop anchors, suitable for +right justification. If the width is zero, nothing happens. If the +number of columns of output between the start and stop anchors is less +than the absolute value of the given width, nothing happens. + +Widths over 8k are considered probable errors and not supported. If +XOF_WARN is set, a warning will be generated. + +*** Modifier Flags + +The modifiers can also include the following flags, which modify the +content emitted for some output styles: + +|---+--------------+-------------------------------------------------| +| M | Name | Description | +|---+--------------+-------------------------------------------------| +| c | colon | A colon (":") is appended after the label | +| d | display | Only emit field for display styles (text/HTML) | +| e | encoding | Only emit for encoding styles (XML/JSON) | +| k | key | Field is a key, suitable for XPath predicates | +| n | no-quotes | Do not quote the field when using JSON style | +| q | quotes | Quote the field when using JSON style | +| w | white space | A blank (" ") is appended after the label | +|---+--------------+-------------------------------------------------| + +For example, the modifier string "Lwc" means the field has a label +role (text that describes the next field) and should be followed by a +colon ('c') and a space ('w'). The modifier string "Vkq" means the +field has a value role, that it is a key for the current instance, and +that the value should be quoted when encoded for JSON. + +**** The Colon Modifier ({c:}) + +The colon modifier appends a single colon to the data value: + + EXAMPLE: + xo_emit("{Lc:Name}{:name}\n", "phil"); + TEXT: + Name:phil + +The colon modifier is only used for the TEXT and HTML output +styles. It is commonly combined with the space modifier ('{w:'). +It is purely a convenience feature. + +**** The Display Modifier ({d:}) + +The display modifier indicated the field should only be generated for +the display output styles, TEXT and HTML. + + EXAMPLE: + xo_emit("{Lcw:Name}{d:name} {:id/%d}\n", "phil", 1); + TEXT: + Name: phil 1 + XML: + <id>1</id> + +The display modifier is the opposite of the encoding modifier, and +they are often used to give to distinct views of the underlying data. + +**** The Encoding Modifier ({e:}) @e-modifier@ + +The display modifier indicated the field should only be generated for +the display output styles, TEXT and HTML. + + EXAMPLE: + xo_emit("{Lcw:Name}{:name} {e:id/%d}\n", "phil", 1); + TEXT: + Name: phil + XML: + <name>phil</name><id>1</id> + +The encoding modifier is the opposite of the display modifier, and +they are often used to give to distinct views of the underlying data. + +**** The Key Modifier ({k:}) + +The key modifier is used to indicate that a particular field helps +uniquely identify an instance of list data. + + EXAMPLE: + xo_open_list("user"); + for (i = 0; i < num_users; i++) { + xo_open_instance("user"); + xo_emit("User {k:name} has {:count} tickets\n", + user[i].u_name, user[i].u_tickets); + xo_close_instance("user"); + } + xo_close_list("user"); + +Currently the key modifier is only used when generating XPath value +for the HTML output style when XOF_XPATH is set, but other uses are +likely in the near future. + +**** The No-Quotes Modifier ({n:}) + +The no-quotes modifier (and its twin, the 'quotes' modifier) affect +the quoting of values in the JSON output style. JSON uses quotes for +string value, but no quotes for numeric, boolean, and null data. +xo_emit applies a simple heuristic to determine whether quotes are +needed, but often this needs to be controlled by the caller. + + EXAMPLE: + const char *bool = is_true ? "true" : "false"; + xo_emit("{n:fancy/%s}", bool); + JSON: + "fancy": true + +**** The Quotes Modifier ({q:}) + +The quotes modifier (and its twin, the 'no-quotes' modifier) affect +the quoting of values in the JSON output style. JSON uses quotes for +string value, but no quotes for numeric, boolean, and null data. +xo_emit applies a simple heuristic to determine whether quotes are +needed, but often this needs to be controlled by the caller. + + EXAMPLE: + xo_emit("{q:time/%d}", 2014); + JSON: + "year": "2014" + +**** The White Space Modifier ({w:}) + +The white space modifier appends a single space to the data value: + + EXAMPLE: + xo_emit("{Lw:Name}{:name}\n", "phil"); + TEXT: + Name phil + +The white space modifier is only used for the TEXT and HTML output +styles. It is commonly combined with the colon modifier ('{c:'). +It is purely a convenience feature. + +Note that the sense of the 'w' modifier is reversed for the units role +({Uw:}); a blank is added before the contents, rather than after it. + +*** Field Formatting + +The field format is similar to the format string for printf(3). It's +used varies based on the role of the field, but generally is used to +format the field's contents. + +If not provided, the format string defaults to "%s". + +Note a field definition can contain zero or more printf-style +'directives', which are sequences that start with a '%' and end with a +one of following characters: "diouxXDOUeEfFgGaAcCsSp". Each directive +is matched by one of more arguments to the xo_emit function. + +The format string has the form: + + '%' format-modifier * format-character + +The format- modifier can be: +- a '#' character, indicating the output value should be prefixed with +'0x', typically to indicate a base 16 (hex) value. +- a minus sign ('-'), indicating the output value should be padded on +the right instead of the left. +- a leading zero ('0') indicating the output value should be padded on the +left with zeroes instead of spaces (' '). +- one or more digits ('0' - '9') indicating the minimum width of the +argument. If the width in columns of the output value is less that +the minumum width, the value will be padded to reach the minimum. +- a period followed by one or more digits indicating the maximum +number of bytes which will be examined for a string argument, or the maximum +width for a non-string argument. When handling ASCII strings this is +functions as the field width but for multi-byte characters, a single +character may be composed of multiple bytes. +xo_emit will never dereference memory beyond the given number of bytes. +- a second period followed by one or more digits indicating the maximum +width for a string argument. This modifier cannot be given for non-string +arguments. +- one or more 'h' characters, indicating shorter input data. +- one or more 'l' characters, indicating longer input data. +- a 'z' character, indicating a 'size_t' argument. +- a 't' character, indicating a 'ptrdiff_t' argument. +- a ' ' character, indicating a space should be emitted before +positive numbers. +- a '+' character, indicating sign should emitted before any number. + +Note that 'q', 'D', 'O', and 'U' are considered deprecated and will be +removed eventually. + +The format character is described in the following table: + +|-----+-----------------+----------------------| +| Ltr | Argument Type | Format | +|-----+-----------------+----------------------| +| d | int | base 10 (decimal) | +| i | int | base 10 (decimal) | +| o | int | base 8 (octal) | +| u | unsigned | base 10 (decimal) | +| x | unsigned | base 16 (hex) | +| X | unsigned long | base 16 (hex) | +| D | long | base 10 (decimal) | +| O | unsigned long | base 8 (octal) | +| U | unsigned long | base 10 (decimal) | +| e | double | [-]d.ddde+-dd | +| E | double | [-]d.dddE+-dd | +| f | double | [-]ddd.ddd | +| F | double | [-]ddd.ddd | +| g | double | as 'e' or 'f' | +| G | double | as 'E' or 'F' | +| a | double | [-]0xh.hhhp[+-]d | +| A | double | [-]0Xh.hhhp[+-]d | +| c | unsigned char | a character | +| C | wint_t | a character | +| s | char * | a UTF-8 string | +| S | wchar_t * | a unicode/WCS string | +| p | void * | '%#lx' | +|-----+-----------------+----------------------| + +The 'h' and 'l' modifiers affect the size and treatment of the +argument: + +|-----+-------------+--------------------| +| Mod | d, i | o, u, x, X | +|-----+-------------+--------------------| +| hh | signed char | unsigned char | +| h | short | unsigned short | +| l | long | unsigned long | +| ll | long long | unsigned long long | +| j | intmax_t | uintmax_t | +| t | ptrdiff_t | ptrdiff_t | +| z | size_t | size_t | +| q | quad_t | u_quad_t | +|-----+-------------+--------------------| + +*** UTF-8 and Locale Strings + +For strings, the 'h' and 'l' modifiers affect the interpretation of +the bytes pointed to argument. The default '%s' string is a 'char *' +pointer to a string encoded as UTF-8. Since UTF-8 is compatible with +ASCII data, a normal 7-bit ASCII string can be used. '%ls' expects a +'wchar_t *' pointer to a wide-character string, encoded as a 32-bit +Unicode values. '%hs' expects a 'char *' pointer to a multi-byte +string encoded with the current locale, as given by the LC_CTYPE, +LANG, or LC_ALL environment varibles. The first of this list of +variables is used and if none of the variables, the locale defaults to +"UTF-8". + +For example, a function is passed a locale-base name, a hat size, +and a time value. The hat size is formatted in a UTF-8 (ASCII) +string, and the time value is formatted into a wchar_t string. + + void print_order (const char *name, int size, + struct tm *timep) { + char buf[32]; + const char *size_val = "unknown"; + + if (size > 0) + snprintf(buf, sizeof(buf), "%d", size); + size_val = buf; + } + + wchar_t when[32]; + wcsftime(when, sizeof(when), L"%d%b%y", timep); + + xo_emit("The hat for {:name/%hs} is {:size/%s}.\n", + name, size_val); + xo_emit("It was ordered on {:order-time/%ls}.\n", + when); + } + +It is important to note that xo_emit will perform the conversion +required to make appropriate output. Text style output uses the +current locale (as described above), while XML, JSON, and HTML use +UTF-8. + +UTF-8 and locale-encoded strings can use multiple bytes to encode one +column of data. The traditional "precision'" (aka "max-width") value +for "%s" printf formatting becomes overloaded since it specifies both +the number of bytes that can be safely referenced and the maximum +number of columns to emit. xo_emit uses the precision as the former, +and adds a third value for specifying the maximum number of columns. + +In this example, the name field is printed with a minimum of 3 columns +and a maximum of 6. Up to ten bytes are in used in filling those +columns. + + xo_emit("{:name/%3.10.6s}", name); + +*** Characters Outside of Field Definitions + +Characters in the format string are not part of a field definition are +copied to the output for the TEXT style, and are ignored for the JSON +and XML styles. For HTML, these characters are placed in a <div> with +class "text". + + EXAMPLE: + xo_emit("The hat is {:size/%s}.\n", size_val); + TEXT: + The hat is extra small. + XML: + <size>extra small</size> + JSON: + "size": "extra small" + HTML: + <div class="text">The hat is </div> + <div class="data" data-tag="size">extra small</div> + <div class="text">.</div> + +*** "%n" is Not Supported + +libxo does not support the '%n' directive. It's a bad idea and we +just don't do it. + +*** The Encoding Format (eformat) + +The "eformat" string is the format string used when encoding the field +for JSON and XML. If not provided, it defaults to the primary format +with any minimum width removed. If the primary is not given, both +default to "%s". + +*** Content Strings + +For padding and labels, the content string is considered the content, +unless a format is given. + +*** Example + +In this example, the value for the number of items in stock is emitted: + + xo_emit("{P: }{Lwc:In stock}{:in-stock/%u}\n", + instock); + +This call will generate the following output: + + TEXT: + In stock: 144 + XML: + <in-stock>144</in-stock> + JSON: + "in-stock": 144, + HTML: + <div class="line"> + <div class="padding"> </div> + <div class="label">In stock</div> + <div class="decoration">:</div> + <div class="padding"> </div> + <div class="data" data-tag="in-stock">144</div> + </div> + +Clearly HTML wins the verbosity award, and this output does +not include XOF_XPATH or XOF_INFO data, which would expand the +penultimate line to: + + <div class="data" data-tag="in-stock" + data-xpath="/top/data/item/in-stock" + data-type="number" + data-help="Number of items in stock">144</div> + +** Command-line Arguments + +libxo uses command line options to trigger rendering behavior. The +following options are recognised: + +- --libxo <options> +- --libxo=<options> +- --libxo:<brief-options> + +Options is a comma-separated list of tokens that correspond to output +styles, flags, or features: + +|-----------+-------------------------------------------------------| +| Token | Action | +|-----------+-------------------------------------------------------| +| dtrt | Enable "Do The Right Thing" mode | +| html | Emit HTML output | +| indent=xx | Set the indentation level | +| info | Add info attributes (HTML) | +| json | Emit JSON output | +| keys | Emit the key attribute for keys (XML) | +| no-locale | Do not initialize the locale setting | +| no-top | Do not emit a top set of braces (JSON) | +| not-first | Pretend the 1st output item was not 1st (JSON) | +| pretty | Emit pretty-printed output | +| text | Emit TEXT output | +| units | Add the 'units' (XML) or 'data-units (HTML) attribute | +| warn | Emit warnings when libxo detects bad calls | +| warn-xml | Emit warnings in XML | +| xml | Emit XML output | +| xpath | Add XPath expressions (HTML) | +|-----------+-------------------------------------------------------| + +The brief options are detailed in ^LIBXO_OPTIONS^. + +** Representing Hierarchy + +For XML and JSON, individual fields appear inside hierarchies which +provide context and meaning to the fields. Unfortunately, these +encoding have a basic disconnect between how lists is similar objects +are represented. + +XML encodes lists as set of sequential elements: + + <user>phil</user> + <user>pallavi</user> + <user>sjg</user> + +JSON encodes lists using a single name and square brackets: + + "user": [ "phil", "pallavi", "sjg" ] + +This means libxo needs three distinct indications of hierarchy: one +for containers of hierarchy appear only once for any specific parent, +one for lists, and one for each item in a list. + +*** Containers + +A "container" is an element of a hierarchy that appears only once +under any specific parent. The container has no value, but serves to +contain other nodes. + +To open a container, call xo_open_container() or +xo_open_container_h(). The former uses the default handle and +the latter accepts a specific handle. + + int xo_open_container_h (xo_handle_t *xop, const char *name); + int xo_open_container (const char *name); + +To close a level, use the xo_close_container() or +xo_close_container_h() functions: + + int xo_close_container_h (xo_handle_t *xop, const char *name); + int xo_close_container (const char *name); + +Each open call must have a matching close call. If the XOF_WARN flag +is set and the name given does not match the name of the currently open +container, a warning will be generated. + + Example: + + xo_open_container("top"); + xo_open_container("system"); + xo_emit("{:host-name/%s%s%s", hostname, + domainname ? "." : "", domainname ?: ""); + xo_close_container("system"); + xo_close_container("top"); + + Sample Output: + Text: + my-host.example.org + XML: + <top> + <system> + <host-name>my-host.example.org</host-name> + </system> + </top> + JSON: + "top" : { + "system" : { + "host-name": "my-host.example.org" + } + } + HTML: + <div class="data" + data-tag="host-name">my-host.example.org</div> + +*** Lists and Instances + +A list is set of one or more instances that appear under the same +parent. The instances contains details about a specific object. One +can think of instances as objects or records. A call is needed to +open and close the list, while a distinct call is needed to open and +close each instance of the list: + + xo_open_list("item"); + + for (ip = list; ip->i_title; ip++) { + xo_open_instance("item"); + xo_emit("{L:Item} '{:name/%s}':\n", ip->i_title); + xo_close_instance("item"); + } + + xo_close_list("item"); + +Getting the list and instance calls correct is critical to the proper +generation of XML and JSON data. + +*** DTRT Mode + +Some user may find tracking the names of open containers, lists, and +instances inconvenient. libxo offers "Do The Right Thing" mode, where +libxo will track the names of open containers, lists, and instances so +the close function can be called without a name. To enable DTRT mode, +turn on the XOF_DTRT flag prior to making any other libxo output. + + xo_set_flags(NULL, XOF_DTRT); + +Each open and close function has a version with the suffix "_d", which +will close the open container, list, or instance: + + xo_open_container("top"); + ... + xo_close_container_d(); + +Note that the XOF_WARN flag will also cause libxo to track open +containers, lists, and instances. A warning is generated with the +name given to the close function and the name recorded do not match. + +** Handles + +libxo uses "handles" to control its rendering functionality. The +handle contains state and buffered data, as well as callback functions +to process data. + +A default handle is used when a NULL is passed to functions accepting +a handle. This handle is initialized to write its data to stdout +using the default style of text (XO_STYLE_TEXT). + +For the convenience of callers, the libxo library includes handle-less +functions that implicitly use the default handle. Any function that +takes a handle will use the default handle is a value of NULL is +passed in place of a valid handle. + +For example, the following are equivalent: + + xo_emit("test"); + xo_emit_h(NULL, "test"); + +Handles are created using xo_create() and destroy using xo_destroy(). + +** UTF-8 + +All strings for libxo must be UTF-8. libxo will handle turning them +into locale-based strings for display to the user. + +The only exception is argument formatted using the "%ls" format, which +require a wide character string (wchar_t *) as input. libxo will +convert these arguments as needed to either UTF-8 (for XML, JSON, and +HTML styles) or locale-based strings for display in text style. + + xo_emit("Alll strings are utf-8 content {:tag/%ls}", + L"except for wide strings"); + +"%S" is equivalent to "%ls". + +* The libxo API + +This section gives details about the functions in libxo, how to call +them, and the actions they perform. + +** Handles + +Handles give an abstraction for libxo that encapsulates the state of a +stream of output. Handles have the data type "xo_handle_t" and are +opaque to the caller. + +The library has a default handle that is automatically initialized. +By default, this handle will send text style output to standard output. +The xo_set_style and xo_set_flags functions can be used to change this +behavior. + +Many libxo functions take a handle as their first parameter; most that +do not use the default handle. Any function taking a handle can +be passed NULL to access the default handle. + +For the typical command that is generating output on standard output, +there is no need to create an explicit handle, but they are available +when needed, e.g. for daemons that generate multiple streams of +output. + +*** xo_create + +A handle can be allocated using the xo_create() function: + + xo_handle_t *xo_create (unsigned style, unsigned flags); + + Example: + xo_handle_t *xop = xo_create(XO_STYLE_JSON, XOF_WARN); + .... + xo_emit_h(xop, "testing\n"); + +See also ^styles^ and ^flags^. + +*** xo_create_to_file + +By default, libxo writes output to standard output. A convenience +function is provided for situations when output should be written to +different file: + + xo_handle_t *xo_create_to_file (FILE *fp, unsigned style, + unsigned flags); + +Use the XOF_CLOSE_FP flag to trigger a call to fclose() for +the FILE pointer when the handle is destroyed. + +*** xo_set_writer + +The xo_set_writer function allows custom 'write' functions +which can tailor how libxo writes data. An opaque argument is +recorded and passed back to the write function, allowing the function +to acquire context information. The 'close' function can +release this opaque data and any other resources as needed. + + void xo_set_writer (xo_handle_t *xop, void *opaque, + xo_write_func_t write_func, + xo_close_func_t close_func); + +*** xo_set_style + +To set the style, use the xo_set_style() function: + + void xo_set_style(xo_handle_t *xop, unsigned style); + +To use the default handle, pass a NULL handle: + + xo_set_style(NULL, XO_STYLE_XML); + +**** Output Styles (XO_STYLE_*) @styles@ + +The libxo functions accept a set of output styles: + +|---------------+-------------------------| +| Flag | Description | +|---------------+-------------------------| +| XO_STYLE_TEXT | Traditional text output | +| XO_STYLE_XML | XML encoded data | +| XO_STYLE_JSON | JSON encoded data | +| XO_STYLE_HTML | HTML encoded data | +|---------------+-------------------------| + +**** xo_set_style_name + +The xo_set_style_name() can be used to set the style based on a name +encoded as a string: + + int xo_set_style_name (xo_handle_t *xop, const char *style); + +The name can be any of the styles: "text", "xml", "json", or "html". + + EXAMPLE: + xo_set_style_name(NULL, "html"); + +*** xo_set_flags + +To set the flags, use the xo_set_flags() function: + + void xo_set_flags(xo_handle_t *xop, unsigned flags); + +To use the default handle, pass a NULL handle: + + xo_set_style(NULL, XO_STYLE_XML); + +**** Flags (XOF_*) @flags@ + +The set of valid flags include: + +|-----------------+---------------------------------------| +| Flag | Description | +|-----------------+---------------------------------------| +| XOF_CLOSE_FP | Close file pointer on xo_destroy() | +| XOF_DTRT | Enable "do the right thing" mode | +| XOF_INFO | Display info data attributes (HTML) | +| XOF_KEYS | Emit the key attribute (XML) | +| XOF_NO_ENV | Do not use the LIBXO_OPTIONS env var | +| XOF_PRETTY | Make 'pretty printed' output | +| XOF_UNDERSCORES | Replaces hyphens with underscores | +| XOF_UNITS | Display units (XML and HMTL) | +| XOF_WARN | Generate warnings for broken calls | +| XOF_WARN_XML | Generate warnings in XML on stdout | +| XOF_XPATH | Emit XPath expressions (HTML) | +| XOF_COLUMNS | Force xo_emit to return columns used | +| XOF_FLUSH | Flush output after each xo_emit call | +|-----------------+---------------------------------------| + +The XOF_CLOSE_FP flag will trigger the call of the close_func +(provided via xo_set_writer()) when the handle is destroyed. + +The XOF_PRETTY flag requests 'pretty printing', which will trigger the +addition of indentation and newlines to enhance the readability of +XML, JSON, and HTML output. Text output is not affected. + +The XOF_WARN flag requests that warnings will trigger diagnostic +output (on standard error) when the library notices errors during +operations, or with arguments to functions. Without warning enabled, +such conditions are ignored. + +Warnings allow developers to debug their interaction with libxo. +The function "xo_failure" can used as a breakpoint for a debugger, +regardless of whether warnings are enabled. + +If the style is XO_STYLE_HTML, the following additional flags can be +used: + +|---------------+-----------------------------------------| +| Flag | Description | +|---------------+-----------------------------------------| +| XOF_XPATH | Emit "data-xpath" attributes | +| XOF_INFO | Emit additional info fields | +|---------------+-----------------------------------------| + +The XOF_XPATH flag enables the emission of XPath expressions detailing +the hierarchy of XML elements used to encode the data field, if the +XPATH style of output were requested. + +The XOF_INFO flag encodes additional informational fields for HTML +output. See ^info^ for details. + +If the style is XO_STYLE_XML, the following additional flags can be +used: + +|---------------+-----------------------------------------| +| Flag | Description | +|---------------+-----------------------------------------| +| XOF_KEYS | Flag 'key' fields for xml | +|---------------+-----------------------------------------| + +The XOF_KEYS flag adds 'key' attribute to the XML encoding for +field definitions that use the 'k' modifier. The key attribute has +the value "key": + + xo_emit("{k:name}", item); + + XML: + <name key="key">truck</name> + +**** xo_clear_flags + +The xo_clear_flags() function turns off the given flags in a specific +handle. + + void xo_clear_flags (xo_handle_t *xop, xo_xof_flags_t flags); + +**** xo_set_options + +The xo_set_options() function accepts a comma-separated list of styles +and flags and enables them for a specific handle. + + int xo_set_options (xo_handle_t *xop, const char *input); + +The options are identical to those listed in ^command-line-arguments^. + +*** xo_destroy + +The xo_destroy function releases a handle and any resources it is +using. Calling xo_destroy with a NULL handle will release any +resources associated with the default handle. + + void xo_destroy(xo_handle_t *xop); + +** Emitting Content (xo_emit) + +The following functions are used to emit output: + + int xo_emit (const char *fmt, ...); + int xo_emit_h (xo_handle_t *xop, const char *fmt, ...); + int xo_emit_hv (xo_handle_t *xop, const char *fmt, va_list vap); + +The "fmt" argument is a string containing field descriptors as +specified in ^format-strings^. The use of a handle is optional and +NULL can be passed to access the internal 'default' handle. See +^handles^. + +The remaining arguments to xo_emit() and xo_emit_h() are a set of +arguments corresponding to the fields in the format string. Care must +be taken to ensure the argument types match the fields in the format +string, since an inappropriate cast can ruin your day. The vap +argument to xo_emit_hv() points to a variable argument list that can +be used to retrieve arguments via va_arg(). + +*** Attributes (xo_attr) @xo_attr@ + +The xo_attr() function emits attributes for the XML output style. + + + int xo_attr (const char *name, const char *fmt, ...); + int xo_attr_h (xo_handle_t *xop, const char *name, + const char *fmt, ...); + int xo_attr_hv (xo_handle_t *xop, const char *name, + const char *fmt, va_list vap); + +The name parameter give the name of the attribute to be encoded. The +fmt parameter gives a printf-style format string used to format the +value of the attribute using any remaining arguments, or the vap +parameter passed to xo_attr_hv(). + + EXAMPLE: + xo_attr("seconds", "%ld", (unsigned long) login_time); + struct tm *tmp = localtime(login_time); + strftime(buf, sizeof(buf), "%R", tmp); + xo_emit("Logged in at {:login-time}\n", buf); + XML: + <login-time seconds="1408336270">00:14</login-time> + +*** Flushing Output (xo_flush) + +libxo buffers data, both for performance and consistency, but also to +allow some advanced features to work properly. At various times, the +caller may wish to flush any data buffered within the library. The +xo_flush() call is used for this: + + void xo_flush (void); + void xo_flush_h (xo_handle_t *xop); + +*** Finishing Output (xo_finish) + +When the program is ready to exit or close a handle, a call to +xo_finish() is required. This flushes any buffered data, closes +open libxo constructs, and completes any pending operations. + + void xo_finish (void); + void xo_finish_h (xo_handle_t *xop); + +Calling this function is vital to the proper operation of libxo, +especially for the non-TEXT output styles. + +** Emitting Hierarchy + +libxo represents to types of hierarchy: containers and lists. A +container appears once under a given parent where a list contains +instances that can appear multiple times. A container is used to hold +related fields and to give the data organization and scope. + +To create a container, use the xo_open_container and +xo_close_container functions: + + int xo_open_container (const char *name); + int xo_open_container_h (xo_handle_t *xop, const char *name); + int xo_open_container_hd (xo_handle_t *xop, const char *name); + int xo_open_container_d (const char *name); + + int xo_close_container (const char *name); + int xo_close_container_h (xo_handle_t *xop, const char *name); + int xo_close_container_hd (xo_handle_t *xop); + int xo_close_container_d (void); + +The name parameter gives the name of the container, encoded in UTF-8. +Since ASCII is a proper subset of UTF-8, traditional C strings can be +used directly. + +The close functions with the "_d" suffix are used in "Do The Right +Thing" mode, where the name of the open containers, lists, and +instances are maintained internally by libxo to allow the caller to +avoid keeping track of the open container name. + +Use the XOF_WARN flag to generate a warning if the name given on the +close does not match the current open container. + +For TEXT and HTML output, containers are not rendered into output +text, though for HTML they are used when the XOF_XPATH flag is set. + + EXAMPLE: + xo_open_container("system"); + xo_emit("The host name is {:host-name}\n", hn); + xo_close_container("system"); + XML: + <system><host-name>foo</host-name></system> + +*** Lists and Instances + +Lists are sequences of instances of homogeneous data objects. Two +distinct levels of calls are needed to represent them in our output +styles. Calls must be made to open and close a list, and for each +instance of data in that list, calls must be make to open and close +that instance. + +The name given to all calls must be identical, and it is strong +suggested that the name be singular, not plural, as a matter of +style and usage expectations. + + EXAMPLE: + xo_open_list("user"); + for (i = 0; i < num_users; i++) { + xo_open_instance("user"); + xo_emit("{k:name}:{:uid/%u}:{:gid/%u}:{:home}\n", + pw[i].pw_name, pw[i].pw_uid, + pw[i].pw_gid, pw[i].pw_dir); + xo_close_instance("user"); + } + xo_close_list("user"); + TEXT: + phil:1001:1001:/home/phil + pallavi:1002:1002:/home/pallavi + XML: + <user> + <name>phil</name> + <uid>1001</uid> + <gid>1001</gid> + <home>/home/phil</home> + </user> + <user> + <name>pallavi</name> + <uid>1002</uid> + <gid>1002</gid> + <home>/home/pallavi</home> + </user> + JSON: + user: [ + { + "name": "phil", + "uid": 1001, + "gid": 1001, + "home": "/home/phil", + }, + { + "name": "pallavi", + "uid": 1002, + "gid": 1002, + "home": "/home/pallavi", + } + ] + +** Additional Functionality + +*** Parsing Command-line Arguments (xo_parse_args) + +The xo_parse_args() function is used to process a program's +arguments. libxo-specific options are processed and removed +from the argument list so the calling application does not +need to process them. If successful, a new value for argc +is returned. On failure, a message it emitted and -1 is returned. + + argc = xo_parse_args(argc, argv); + if (argc < 0) + exit(1); + +Following the call to xo_parse_args, the application can process the +remaining arguments in a normal manner. See ^command-line-arguments^ +for a description of valid arguments. + +*** Field Information (xo_info_t) @info@ + +HTML data can include additional information in attributes that +begin with "data-". To enable this, three things must occur: + +First the application must build an array of xo_info_t structures, +one per tag. The array must be sorted by name, since libxo uses a +binary search to find the entry that matches names from format +instructions. + +Second, the application must inform libxo about this information using +the xo_set_info() call: + + typedef struct xo_info_s { + const char *xi_name; /* Name of the element */ + const char *xi_type; /* Type of field */ + const char *xi_help; /* Description of field */ + } xo_info_t; + + void xo_set_info (xo_handle_t *xop, xo_info_t *infop, int count); + +Like other libxo calls, passing NULL for the handle tells libxo to use +the default handle. + +If the count is -1, libxo will count the elements of infop, but there +must be an empty element at the end. More typically, the number is +known to the application: + + xo_info_t info[] = { + { "in-stock", "number", "Number of items in stock" }, + { "name", "string", "Name of the item" }, + { "on-order", "number", "Number of items on order" }, + { "sku", "string", "Stock Keeping Unit" }, + { "sold", "number", "Number of items sold" }, + }; + int info_count = (sizeof(info) / sizeof(info[0])); + ... + xo_set_info(NULL, info, info_count); + +Third, the emitting of info must be triggered with the XOF_INFO flag +using either the xo_set_flags() function or the "--libxo=info" command +line argument. + +The type and help values, if present, are emitted as the "data-type" +and "data-help" attributes: + + <div class="data" data-tag="sku" data-type="string" + data-help="Stock Keeping Unit">GRO-000-533</div> + +*** Memory Allocation + +The xo_set_allocator function allows libxo to be used in environments +where the standard realloc() and free() functions are not available. + + void xo_set_allocator (xo_realloc_func_t realloc_func, + xo_free_func_t free_func); + +realloc_func should expect the same arguments as realloc(3) and return +a pointer to memory following the same convention. free_func will +receive the same argument as free(3) and should release it, as +appropriate for the environment. + +By default, the standard realloc() and free() functions are used. + +*** LIBXO_OPTIONS @LIBXO_OPTIONS@ + +The environment variable "LIBXO_OPTIONS" can be set to a string of +options: + +|--------+-------------------------------------------| +| Option | Action | +|--------+-------------------------------------------| +| H | Enable HTML output (XO_STYLE_HTML) | +| I | Enable info output (XOF_INFO) | +| i<num> | Indent by <number> | +| J | Enable JSON output (XO_STYLE_JSON) | +| P | Enable pretty-printed output (XOF_PRETTY) | +| T | Enable text output (XO_STYLE_TEXT) | +| W | Enable warnings (XOF_WARN) | +| X | Enable XML output (XO_STYLE_XML) | +| x | Enable XPath data (XOF_XPATH) | +|--------+-------------------------------------------| + +For example, warnings can be enabled by: + + % env LIBXO_OPTIONS=W my-app + +Complete HTML output can be generated with: + + % env LIBXO_OPTIONS=HXI my-app + +*** Errors, Warnings, and Messages + +Many programs make use of the standard library functions err() and +warn() to generate errors and warnings for the user. libxo wants to +pass that information via the current output style, and provides +compatible functions to allow this: + + void xo_warn (const char *fmt, ...); + void xo_warnx (const char *fmt, ...); + void xo_warn_c (int code, const char *fmt, ...); + void xo_warn_hc (xo_handle_t *xop, int code, + const char *fmt, ...); + void xo_err (int eval, const char *fmt, ...); + void xo_errc (int eval, int code, const char *fmt, ...); + void xo_errx (int eval, const char *fmt, ...); + void xo_message (const char *fmt, ...); + void xo_message_c (int code, const char *fmt, ...); + void xo_message_hc (xo_handle_t *xop, int code, + const char *fmt, ...); + void xo_message_hcv (xo_handle_t *xop, int code, + const char *fmt, va_list vap); + +These functions display the program name, a colon, a formatted message +based on the arguments, and then optionally a colon and an error +message associated with either "errno" or the "code" parameter. + + EXAMPLE: + if (open(filename, O_RDONLY) < 0) + xo_err(1, "cannot open file '%s'", filename); + +*** xo_no_setlocale + +libxo automatically initializes the locale based on setting of the +environment variables LC_CTYPE, LANG, and LC_ALL. The first of this +list of variables is used and if none of the variables, the locale +defaults to "UTF-8". The caller may wish to avoid this behavior, and +can do so by calling the xo_no_setlocale() function. + + void xo_no_setlocale (void); + +* The "xo" Utility + +The "xo" utility allows command line access to the functionality of +the libxo library. Using "xo", shell scripts can emit XML, JSON, and +HTML using the same commands that emit text output. + +The style of output can be selected using a specific option: "-X" for +XML, "-J" for JSON, "-H" for HTML, or "-T" for TEXT, which is the +default. The "--style <style>" option can also be used. The +LIBXO_OPTIONS environment variable can also be used to set the style, +as well as other flags. + +The "xo" utility accepts a format string suitable for xo_emit() and a +set of zero or more arguments used to supply data for that string. + + xo "The {k:name} weighs {:weight/%d} pounds.\n" fish 6 + + TEXT: + The fish weighs 6 pounds. + XML: + <name>fish</name> + <weight>6</weight> + JSON: + "name": "fish", + "weight": 6 + HTML: + <div class="line"> + <div class="text">The </div> + <div class="data" data-tag="name">fish</div> + <div class="text"> weighs </div> + <div class="data" data-tag="weight">6</div> + <div class="text"> pounds.</div> + </div> + +The "--wrap <path>" option can be used to wrap emitted content in a +specific hierarchy. The path is a set of hierarchical names separated +by the '/' character. + + xo --wrap top/a/b/c '{:tag}' value + + XML: + <top> + <a> + <b> + <c> + <tag>value</tag> + </c> + </b> + </a> + </top> + JSON: + "top": { + "a": { + "b": { + "c": { + "tag": "value" + } + } + } + } + +The "--open <path>" and "--close <path>" can be used to emit +hierarchical information without the matching close and open +tag. This allows a shell script to emit open tags, data, and +then close tags. The "--depth" option may be used to set the +depth for indentation. The "--leading-xpath" may be used to +prepend data to the XPath values used for HTML output style. + + #!/bin/sh + xo --open top/data + xo --depth 2 '{tag}' value + xo --close top/data + XML: + <top> + <data> + <tag>value</tag> + </data> + </top> + JSON: + "top": { + "data": { + "tag": "value" + } + } + +** Command Line Options + +Usage: xo [options] format [fields] + --close <path> Close tags for the given path + --depth <num> Set the depth for pretty printing + --help Display this help text + --html OR -H Generate HTML output + --json OR -J Generate JSON output + --leading-xpath <path> Add a prefix to generated XPaths (HTML) + --open <path> Open tags for the given path + --pretty OR -p Make 'pretty' output (add indent, newlines) + --style <style> Generate given style (xml, json, text, html) + --text OR -T Generate text output (the default style) + --version Display version information + --warn OR -W Display warnings in text on stderr + --warn-xml Display warnings in xml on stdout + --wrap <path> Wrap output in a set of containers + --xml OR -X Generate XML output + --xpath Add XPath data to HTML output); + +** Example + + % xo 'The {:product} is {:status}\n' stereo "in route" + The stereo is in route + % ./xo/xo -p -X 'The {:product} is {:status}\n' stereo "in route" + <product>stereo</product> + <status>in route</status> + +* xolint + +xolint is a tool for reporting common mistakes in format strings +in source code that invokes xo_emit(). It allows these errors +to be diagnosed at build time, rather than waiting until runtime. + +xolint takes the one or more C files as arguments, and reports +and errors, warning, or informational messages as needed. + +|------------+---------------------------------------------------| +| Option | Meaning | +|------------+---------------------------------------------------| +| -c | Invoke 'cpp' against the input file | +| -C <flags> | Flags that are passed to 'cpp | +| -d | Enable debug output | +| -D | Generate documentation for all xolint messages | +| -I | Generate info table code | +| -p | Print the offending lines after the message | +| -V | Print vocabulary of all field names | +| -X | Extract samples from xolint, suitable for testing | +|------------+---------------------------------------------------| + +Output message contain the source filename and line number, the +class of the message, the message, and, if -p is given, the +line that contains the error: + + % xolint.pl -t xolint.c + xolint.c: 16: error: anchor format should be "%d" + 16 xo_emit("{[:/%s}"); + +The "-I" option will generate a table of xo_info_t structures , + +The "-V" option does not report errors, but prints a complete list of +all field names, sorted alphabetically. The output can help spot +inconsistencies and spelling errors. + +* FAQs + +This section contains the set of questions that users typically ask, +along with answers that might be helpful. + +!! list-sections + +** General + +*** Can you share the history of libxo? + +In 2001, we added an XML API to the JUNOS operating system, which is +built on top of FreeBSD. Eventually this API became standardized as +the NETCONF API (RFC 6241). As part of this effort, we modified many +FreeBSD utilities to emit XML, typically via a "-X" switch. The +results were mixed. The cost of maintaining this code, updating it +and carrying it were non-trivial, and contributed to our expense (and +the associated delay) with upgrading the version of FreeBSD on which +each release of JUNOS is based. + +A recent (2014) effort within JUNOS aims at removing our modifications +to the underlying FreeBSD code as a means of reducing the expense and +delay. JUNOS is structured to have system components generate XML +that is rendered by the CLI (think: login shell) into human-readable +text. This allows the API to use the same plumbing as the CLI, and +ensures that all components emit XML, and that it is emitted with +knowledge of the consumer of that XML, yielding an API that have no +incremental cost or feature delay. + +libxo is an effort to mix the best aspects of the JUNOS strategy into +FreeBSD in a seemless way, allowing commands to make printf-like +output calls without needing to care how the output is rendered. + +*** What makes a good field name? + +To make useful, consistent field names, follow these guidelines: + += Use lower case, even for TLAs +Lower case is more civilized. Even TLAs should be lower case +to avoid scenarios where the differences between "XPath" and +"Xpath" drive your users crazy. Using "xpath" is simpler and better. += Use hyphens, not underscores +Use of hyphens is traditional in XML, and the XOF_UNDERSCORES +flag can be used to generate underscores in JSON, if desired. +But the raw field name should use hyphens. += Use full words +Don't abbreviate especially when the abbreviation is not obvious or +not widely used. Use "data-size", not "dsz" or "dsize". Use +"interface" instead of "ifname", "if-name", "iface", "if", or "intf". += Use <verb>-<units> +Using the form <verb>-<units> or <verb>-<classifier>-<units> helps in +making consistent, useful names, avoiding the situation where one app +uses "sent-packet" and another "packets-sent" and another +"packets-we-have-sent". The <units> can be dropped when it is +obvious, as can obvious words in the classification. +Use "receive-after-window-packets" instead of +"received-packets-of-data-after-window". += Reuse existing field names +Nothing's worse than writing expressions like: + + if ($src1/process[pid == $pid]/name == + $src2/proc-table/proc/p[process-id == $pid]/proc-name) { + ... + } + +Find someone else who is expressing similar data and follow their +field's and hierarchy. Remember the quote is not "Consistency is the +hobgoblin of little minds", but "A foolish consistency is the +hobgoblin of little minds". += Think about your users +Have empathy for your users, choosing clear and useful fields that +contain clear and useful data. You may need to augment the display +content with xo_attr() calls (^xo_attr^) or "{e:}" fields +(^e-modifier^) to make the data useful. += Don't use an arbitrary number postfix +What does "errors2" mean? No one will know. "errors-after-restart" +would be a better choice. Think of you users, and think of the +future. If you make "errors2", the next guy will happily make +"errors3" and before you know it, someone will be asking what's the +difference between errors37 and errors63. += Be consistent, uniform, unsurprising, and predictable +Think of your field vocabulary as an API. You want it useful, +expressive, meaningful, direct, and obvious. You want the client +application's programmer to move between without the need to +understand a variety of opinions on how fields are named. They should +see the system as a single cohesive whole, not a sack of cats. + +Field names constitute the means by which client programmers interact +with our system. By choosing wise names now, you are making their +lives better. + +After using "xolint" to find errors in your field descriptors, use +"xolint -V" to spell check your field names and to detect different +names for the same data. "dropped-short" and "dropped-too-short" are +both reasonable names, but using them both will lead users to ask the +difference between the two fields. If there isn't a difference, +use only one of the field names. If there is a difference, change the +names to make that difference more obvious. + +** What does this message mean? + +!!include-file xolint.txt + +* Examples + +** Unit Test + +Here is the unit test example: + + int + main (int argc, char **argv) + { + static char base_grocery[] = "GRO"; + static char base_hardware[] = "HRD"; + struct item { + const char *i_title; + int i_sold; + int i_instock; + int i_onorder; + const char *i_sku_base; + int i_sku_num; + }; + struct item list[] = { + { "gum", 1412, 54, 10, base_grocery, 415 }, + { "rope", 85, 4, 2, base_hardware, 212 }, + { "ladder", 0, 2, 1, base_hardware, 517 }, + { "bolt", 4123, 144, 42, base_hardware, 632 }, + { "water", 17, 14, 2, base_grocery, 2331 }, + { NULL, 0, 0, 0, NULL, 0 } + }; + struct item list2[] = { + { "fish", 1321, 45, 1, base_grocery, 533 }, + }; + struct item *ip; + xo_info_t info[] = { + { "in-stock", "number", "Number of items in stock" }, + { "name", "string", "Name of the item" }, + { "on-order", "number", "Number of items on order" }, + { "sku", "string", "Stock Keeping Unit" }, + { "sold", "number", "Number of items sold" }, + { NULL, NULL, NULL }, + }; + int info_count = (sizeof(info) / sizeof(info[0])) - 1; + + argc = xo_parse_args(argc, argv); + if (argc < 0) + exit(1); + + xo_set_info(NULL, info, info_count); + + xo_open_container_h(NULL, "top"); + + xo_open_container("data"); + xo_open_list("item"); + + for (ip = list; ip->i_title; ip++) { + xo_open_instance("item"); + + xo_emit("{L:Item} '{k:name/%s}':\n", ip->i_title); + xo_emit("{P: }{L:Total sold}: {n:sold/%u%s}\n", + ip->i_sold, ip->i_sold ? ".0" : ""); + xo_emit("{P: }{Lwc:In stock}{:in-stock/%u}\n", + ip->i_instock); + xo_emit("{P: }{Lwc:On order}{:on-order/%u}\n", + ip->i_onorder); + xo_emit("{P: }{L:SKU}: {q:sku/%s-000-%u}\n", + ip->i_sku_base, ip->i_sku_num); + + xo_close_instance("item"); + } + + xo_close_list("item"); + xo_close_container("data"); + + xo_open_container("data"); + xo_open_list("item"); + + for (ip = list2; ip->i_title; ip++) { + xo_open_instance("item"); + + xo_emit("{L:Item} '{:name/%s}':\n", ip->i_title); + xo_emit("{P: }{L:Total sold}: {n:sold/%u%s}\n", + ip->i_sold, ip->i_sold ? ".0" : ""); + xo_emit("{P: }{Lwc:In stock}{:in-stock/%u}\n", + ip->i_instock); + xo_emit("{P: }{Lwc:On order}{:on-order/%u}\n", + ip->i_onorder); + xo_emit("{P: }{L:SKU}: {q:sku/%s-000-%u}\n", + ip->i_sku_base, ip->i_sku_num); + + xo_close_instance("item"); + } + + xo_close_list("item"); + xo_close_container("data"); + + xo_close_container_h(NULL, "top"); + + return 0; + } + +Text output: + + % ./testxo --libxo text + Item 'gum': + Total sold: 1412.0 + In stock: 54 + On order: 10 + SKU: GRO-000-415 + Item 'rope': + Total sold: 85.0 + In stock: 4 + On order: 2 + SKU: HRD-000-212 + Item 'ladder': + Total sold: 0 + In stock: 2 + On order: 1 + SKU: HRD-000-517 + Item 'bolt': + Total sold: 4123.0 + In stock: 144 + On order: 42 + SKU: HRD-000-632 + Item 'water': + Total sold: 17.0 + In stock: 14 + On order: 2 + SKU: GRO-000-2331 + Item 'fish': + Total sold: 1321.0 + In stock: 45 + On order: 1 + SKU: GRO-000-533 + +JSON output: + + % ./testxo --libxo json,pretty + "top": { + "data": { + "item": [ + { + "name": "gum", + "sold": 1412.0, + "in-stock": 54, + "on-order": 10, + "sku": "GRO-000-415" + }, + { + "name": "rope", + "sold": 85.0, + "in-stock": 4, + "on-order": 2, + "sku": "HRD-000-212" + }, + { + "name": "ladder", + "sold": 0, + "in-stock": 2, + "on-order": 1, + "sku": "HRD-000-517" + }, + { + "name": "bolt", + "sold": 4123.0, + "in-stock": 144, + "on-order": 42, + "sku": "HRD-000-632" + }, + { + "name": "water", + "sold": 17.0, + "in-stock": 14, + "on-order": 2, + "sku": "GRO-000-2331" + } + ] + }, + "data": { + "item": [ + { + "name": "fish", + "sold": 1321.0, + "in-stock": 45, + "on-order": 1, + "sku": "GRO-000-533" + } + ] + } + } + +XML output: + + % ./testxo --libxo pretty,xml + <top> + <data> + <item> + <name>gum</name> + <sold>1412.0</sold> + <in-stock>54</in-stock> + <on-order>10</on-order> + <sku>GRO-000-415</sku> + </item> + <item> + <name>rope</name> + <sold>85.0</sold> + <in-stock>4</in-stock> + <on-order>2</on-order> + <sku>HRD-000-212</sku> + </item> + <item> + <name>ladder</name> + <sold>0</sold> + <in-stock>2</in-stock> + <on-order>1</on-order> + <sku>HRD-000-517</sku> + </item> + <item> + <name>bolt</name> + <sold>4123.0</sold> + <in-stock>144</in-stock> + <on-order>42</on-order> + <sku>HRD-000-632</sku> + </item> + <item> + <name>water</name> + <sold>17.0</sold> + <in-stock>14</in-stock> + <on-order>2</on-order> + <sku>GRO-000-2331</sku> + </item> + </data> + <data> + <item> + <name>fish</name> + <sold>1321.0</sold> + <in-stock>45</in-stock> + <on-order>1</on-order> + <sku>GRO-000-533</sku> + </item> + </data> + </top> + +HMTL output: + + % ./testxo --libxo pretty,html + <div class="line"> + <div class="label">Item</div> + <div class="text"> '</div> + <div class="data" data-tag="name">gum</div> + <div class="text">':</div> + </div> + <div class="line"> + <div class="padding"> </div> + <div class="label">Total sold</div> + <div class="text">: </div> + <div class="data" data-tag="sold">1412.0</div> + </div> + <div class="line"> + <div class="padding"> </div> + <div class="label">In stock</div> + <div class="decoration">:</div> + <div class="padding"> </div> + <div class="data" data-tag="in-stock">54</div> + </div> + <div class="line"> + <div class="padding"> </div> + <div class="label">On order</div> + <div class="decoration">:</div> + <div class="padding"> </div> + <div class="data" data-tag="on-order">10</div> + </div> + <div class="line"> + <div class="padding"> </div> + <div class="label">SKU</div> + <div class="text">: </div> + <div class="data" data-tag="sku">GRO-000-415</div> + </div> + <div class="line"> + <div class="label">Item</div> + <div class="text"> '</div> + <div class="data" data-tag="name">rope</div> + <div class="text">':</div> + </div> + <div class="line"> + <div class="padding"> </div> + <div class="label">Total sold</div> + <div class="text">: </div> + <div class="data" data-tag="sold">85.0</div> + </div> + <div class="line"> + <div class="padding"> </div> + <div class="label">In stock</div> + <div class="decoration">:</div> + <div class="padding"> </div> + <div class="data" data-tag="in-stock">4</div> + </div> + <div class="line"> + <div class="padding"> </div> + <div class="label">On order</div> + <div class="decoration">:</div> + <div class="padding"> </div> + <div class="data" data-tag="on-order">2</div> + </div> + <div class="line"> + <div class="padding"> </div> + <div class="label">SKU</div> + <div class="text">: </div> + <div class="data" data-tag="sku">HRD-000-212</div> + </div> + <div class="line"> + <div class="label">Item</div> + <div class="text"> '</div> + <div class="data" data-tag="name">ladder</div> + <div class="text">':</div> + </div> + <div class="line"> + <div class="padding"> </div> + <div class="label">Total sold</div> + <div class="text">: </div> + <div class="data" data-tag="sold">0</div> + </div> + <div class="line"> + <div class="padding"> </div> + <div class="label">In stock</div> + <div class="decoration">:</div> + <div class="padding"> </div> + <div class="data" data-tag="in-stock">2</div> + </div> + <div class="line"> + <div class="padding"> </div> + <div class="label">On order</div> + <div class="decoration">:</div> + <div class="padding"> </div> + <div class="data" data-tag="on-order">1</div> + </div> + <div class="line"> + <div class="padding"> </div> + <div class="label">SKU</div> + <div class="text">: </div> + <div class="data" data-tag="sku">HRD-000-517</div> + </div> + <div class="line"> + <div class="label">Item</div> + <div class="text"> '</div> + <div class="data" data-tag="name">bolt</div> + <div class="text">':</div> + </div> + <div class="line"> + <div class="padding"> </div> + <div class="label">Total sold</div> + <div class="text">: </div> + <div class="data" data-tag="sold">4123.0</div> + </div> + <div class="line"> + <div class="padding"> </div> + <div class="label">In stock</div> + <div class="decoration">:</div> + <div class="padding"> </div> + <div class="data" data-tag="in-stock">144</div> + </div> + <div class="line"> + <div class="padding"> </div> + <div class="label">On order</div> + <div class="decoration">:</div> + <div class="padding"> </div> + <div class="data" data-tag="on-order">42</div> + </div> + <div class="line"> + <div class="padding"> </div> + <div class="label">SKU</div> + <div class="text">: </div> + <div class="data" data-tag="sku">HRD-000-632</div> + </div> + <div class="line"> + <div class="label">Item</div> + <div class="text"> '</div> + <div class="data" data-tag="name">water</div> + <div class="text">':</div> + </div> + <div class="line"> + <div class="padding"> </div> + <div class="label">Total sold</div> + <div class="text">: </div> + <div class="data" data-tag="sold">17.0</div> + </div> + <div class="line"> + <div class="padding"> </div> + <div class="label">In stock</div> + <div class="decoration">:</div> + <div class="padding"> </div> + <div class="data" data-tag="in-stock">14</div> + </div> + <div class="line"> + <div class="padding"> </div> + <div class="label">On order</div> + <div class="decoration">:</div> + <div class="padding"> </div> + <div class="data" data-tag="on-order">2</div> + </div> + <div class="line"> + <div class="padding"> </div> + <div class="label">SKU</div> + <div class="text">: </div> + <div class="data" data-tag="sku">GRO-000-2331</div> + </div> + <div class="line"> + <div class="label">Item</div> + <div class="text"> '</div> + <div class="data" data-tag="name">fish</div> + <div class="text">':</div> + </div> + <div class="line"> + <div class="padding"> </div> + <div class="label">Total sold</div> + <div class="text">: </div> + <div class="data" data-tag="sold">1321.0</div> + </div> + <div class="line"> + <div class="padding"> </div> + <div class="label">In stock</div> + <div class="decoration">:</div> + <div class="padding"> </div> + <div class="data" data-tag="in-stock">45</div> + </div> + <div class="line"> + <div class="padding"> </div> + <div class="label">On order</div> + <div class="decoration">:</div> + <div class="padding"> </div> + <div class="data" data-tag="on-order">1</div> + </div> + <div class="line"> + <div class="padding"> </div> + <div class="label">SKU</div> + <div class="text">: </div> + <div class="data" data-tag="sku">GRO-000-533</div> + </div> + +HTML output with xpath and info flags: + + % ./testxo --libxo pretty,html,xpath,info + <div class="line"> + <div class="label">Item</div> + <div class="text"> '</div> + <div class="data" data-tag="name" + data-xpath="/top/data/item/name" data-type="string" + data-help="Name of the item">gum</div> + <div class="text">':</div> + </div> + <div class="line"> + <div class="padding"> </div> + <div class="label">Total sold</div> + <div class="text">: </div> + <div class="data" data-tag="sold" + data-xpath="/top/data/item/sold" data-type="number" + data-help="Number of items sold">1412.0</div> + </div> + <div class="line"> + <div class="padding"> </div> + <div class="label">In stock</div> + <div class="decoration">:</div> + <div class="padding"> </div> + <div class="data" data-tag="in-stock" + data-xpath="/top/data/item/in-stock" data-type="number" + data-help="Number of items in stock">54</div> + </div> + <div class="line"> + <div class="padding"> </div> + <div class="label">On order</div> + <div class="decoration">:</div> + <div class="padding"> </div> + <div class="data" data-tag="on-order" + data-xpath="/top/data/item/on-order" data-type="number" + data-help="Number of items on order">10</div> + </div> + <div class="line"> + <div class="padding"> </div> + <div class="label">SKU</div> + <div class="text">: </div> + <div class="data" data-tag="sku" + data-xpath="/top/data/item/sku" data-type="string" + data-help="Stock Keeping Unit">GRO-000-415</div> + </div> + <div class="line"> + <div class="label">Item</div> + <div class="text"> '</div> + <div class="data" data-tag="name" + data-xpath="/top/data/item/name" data-type="string" + data-help="Name of the item">rope</div> + <div class="text">':</div> + </div> + <div class="line"> + <div class="padding"> </div> + <div class="label">Total sold</div> + <div class="text">: </div> + <div class="data" data-tag="sold" + data-xpath="/top/data/item/sold" data-type="number" + data-help="Number of items sold">85.0</div> + </div> + <div class="line"> + <div class="padding"> </div> + <div class="label">In stock</div> + <div class="decoration">:</div> + <div class="padding"> </div> + <div class="data" data-tag="in-stock" + data-xpath="/top/data/item/in-stock" data-type="number" + data-help="Number of items in stock">4</div> + </div> + <div class="line"> + <div class="padding"> </div> + <div class="label">On order</div> + <div class="decoration">:</div> + <div class="padding"> </div> + <div class="data" data-tag="on-order" + data-xpath="/top/data/item/on-order" data-type="number" + data-help="Number of items on order">2</div> + </div> + <div class="line"> + <div class="padding"> </div> + <div class="label">SKU</div> + <div class="text">: </div> + <div class="data" data-tag="sku" + data-xpath="/top/data/item/sku" data-type="string" + data-help="Stock Keeping Unit">HRD-000-212</div> + </div> + <div class="line"> + <div class="label">Item</div> + <div class="text"> '</div> + <div class="data" data-tag="name" + data-xpath="/top/data/item/name" data-type="string" + data-help="Name of the item">ladder</div> + <div class="text">':</div> + </div> + <div class="line"> + <div class="padding"> </div> + <div class="label">Total sold</div> + <div class="text">: </div> + <div class="data" data-tag="sold" + data-xpath="/top/data/item/sold" data-type="number" + data-help="Number of items sold">0</div> + </div> + <div class="line"> + <div class="padding"> </div> + <div class="label">In stock</div> + <div class="decoration">:</div> + <div class="padding"> </div> + <div class="data" data-tag="in-stock" + data-xpath="/top/data/item/in-stock" data-type="number" + data-help="Number of items in stock">2</div> + </div> + <div class="line"> + <div class="padding"> </div> + <div class="label">On order</div> + <div class="decoration">:</div> + <div class="padding"> </div> + <div class="data" data-tag="on-order" + data-xpath="/top/data/item/on-order" data-type="number" + data-help="Number of items on order">1</div> + </div> + <div class="line"> + <div class="padding"> </div> + <div class="label">SKU</div> + <div class="text">: </div> + <div class="data" data-tag="sku" + data-xpath="/top/data/item/sku" data-type="string" + data-help="Stock Keeping Unit">HRD-000-517</div> + </div> + <div class="line"> + <div class="label">Item</div> + <div class="text"> '</div> + <div class="data" data-tag="name" + data-xpath="/top/data/item/name" data-type="string" + data-help="Name of the item">bolt</div> + <div class="text">':</div> + </div> + <div class="line"> + <div class="padding"> </div> + <div class="label">Total sold</div> + <div class="text">: </div> + <div class="data" data-tag="sold" + data-xpath="/top/data/item/sold" data-type="number" + data-help="Number of items sold">4123.0</div> + </div> + <div class="line"> + <div class="padding"> </div> + <div class="label">In stock</div> + <div class="decoration">:</div> + <div class="padding"> </div> + <div class="data" data-tag="in-stock" + data-xpath="/top/data/item/in-stock" data-type="number" + data-help="Number of items in stock">144</div> + </div> + <div class="line"> + <div class="padding"> </div> + <div class="label">On order</div> + <div class="decoration">:</div> + <div class="padding"> </div> + <div class="data" data-tag="on-order" + data-xpath="/top/data/item/on-order" data-type="number" + data-help="Number of items on order">42</div> + </div> + <div class="line"> + <div class="padding"> </div> + <div class="label">SKU</div> + <div class="text">: </div> + <div class="data" data-tag="sku" + data-xpath="/top/data/item/sku" data-type="string" + data-help="Stock Keeping Unit">HRD-000-632</div> + </div> + <div class="line"> + <div class="label">Item</div> + <div class="text"> '</div> + <div class="data" data-tag="name" + data-xpath="/top/data/item/name" data-type="string" + data-help="Name of the item">water</div> + <div class="text">':</div> + </div> + <div class="line"> + <div class="padding"> </div> + <div class="label">Total sold</div> + <div class="text">: </div> + <div class="data" data-tag="sold" + data-xpath="/top/data/item/sold" data-type="number" + data-help="Number of items sold">17.0</div> + </div> + <div class="line"> + <div class="padding"> </div> + <div class="label">In stock</div> + <div class="decoration">:</div> + <div class="padding"> </div> + <div class="data" data-tag="in-stock" + data-xpath="/top/data/item/in-stock" data-type="number" + data-help="Number of items in stock">14</div> + </div> + <div class="line"> + <div class="padding"> </div> + <div class="label">On order</div> + <div class="decoration">:</div> + <div class="padding"> </div> + <div class="data" data-tag="on-order" + data-xpath="/top/data/item/on-order" data-type="number" + data-help="Number of items on order">2</div> + </div> + <div class="line"> + <div class="padding"> </div> + <div class="label">SKU</div> + <div class="text">: </div> + <div class="data" data-tag="sku" + data-xpath="/top/data/item/sku" data-type="string" + data-help="Stock Keeping Unit">GRO-000-2331</div> + </div> + <div class="line"> + <div class="label">Item</div> + <div class="text"> '</div> + <div class="data" data-tag="name" + data-xpath="/top/data/item/name" data-type="string" + data-help="Name of the item">fish</div> + <div class="text">':</div> + </div> + <div class="line"> + <div class="padding"> </div> + <div class="label">Total sold</div> + <div class="text">: </div> + <div class="data" data-tag="sold" + data-xpath="/top/data/item/sold" data-type="number" + data-help="Number of items sold">1321.0</div> + </div> + <div class="line"> + <div class="padding"> </div> + <div class="label">In stock</div> + <div class="decoration">:</div> + <div class="padding"> </div> + <div class="data" data-tag="in-stock" + data-xpath="/top/data/item/in-stock" data-type="number" + data-help="Number of items in stock">45</div> + </div> + <div class="line"> + <div class="padding"> </div> + <div class="label">On order</div> + <div class="decoration">:</div> + <div class="padding"> </div> + <div class="data" data-tag="on-order" + data-xpath="/top/data/item/on-order" data-type="number" + data-help="Number of items on order">1</div> + </div> + <div class="line"> + <div class="padding"> </div> + <div class="label">SKU</div> + <div class="text">: </div> + <div class="data" data-tag="sku" + data-xpath="/top/data/item/sku" data-type="string" + data-help="Stock Keeping Unit">GRO-000-533</div> + </div> + +{{document: + name libxo-manual; + private "The libxo Project"; + ipr none; + category exp; + abbreviation LIBXO-MANUAL; + title "libxo: The Easy Way to Generate text, XML, JSON, and HTML output"; + contributor "author:Phil Shafer:Juniper Networks:phil@juniper.net"; +}} |