1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-12-22 11:17:19 +00:00

Import to 0.6.1

0.5.0:
    document "trim" modifier
    add xo_emit_field functions
    Add xo_set_file{,_h} functions
    Fix LIBXO_* variables; add -L and -I as needed
    add --disable-silent-rules and an explicit make; s/PACKAGE-NAME/PACKAGE_NAME/; add /download/ to 'url'
    fix silliness where xo_flush_h emitted closing tag (html); make the caller (xo_message) do it
    flush after transitions; fix flush call in xo_do_emit
    mkdir the version-specific packaging dir
    use "XO_" instead of LIBXO_

  0.6.0:
    Add --with-retain-size to set the size (in bits) of the retain hash buckets
    Add The Argument Modifier ({a:})
    Add retain and no-retain to --libxo
    autoconf: Add test for monitor.h
    Document quote heuristic
    go deep with nroff backslashes
    Use "ULL" for 32 bit check
    add xo_retain_clear and xo_retain_clear_all
    docs: combine two 'handles' section; move command line argument section
    handle GETTEXT when msgfmt isn't where it's supposed to be (FreeBSD)
    make 'retain' a flag (XOEF_RETAIN) instead of a role; it's simpler, and doesn't feel as tacky. "{R:}" was painful to document, which means it's painful to use.
    new xo_emit_f functions
    nuke some unused UNUSEDs
    test code: path must be static
    update test cases

  0.6.1:
    fix version number (missed a commit during new-release)

Reviewed by:	sjg
Approved by:	sjg (mentor)
This commit is contained in:
Phil Shafer 2016-04-15 18:46:15 +00:00
commit 42ff34c310
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=298083
47 changed files with 2512 additions and 833 deletions

View File

@ -77,6 +77,7 @@ GH_PAGES_PACKAGE_DIR = ${GH_PAGES_DIR}/${GH_PACKAGING_DIR}
packages:
@-[ -d ${GH_PAGES_DIR} ] && set -x \
&& echo "Updating packages on gh-pages ..." \
&& mkdir -p ${GH_PAGES_DIR}/${GH_PACKAGING_DIR} \
&& SHA1="`openssl sha1 ${PACKAGE_FILE} | awk '{print $$2}'`" \
&& SHA256="`openssl sha256 ${PACKAGE_FILE} | awk '{print $$2}'`" \
&& SIZE="`ls -l ${PACKAGE_FILE} | awk '{print $$5}'`" \

View File

@ -12,7 +12,7 @@
#
AC_PREREQ(2.2)
AC_INIT([libxo], [0.4.7], [phil@juniper.net])
AC_INIT([libxo], [0.6.1], [phil@juniper.net])
AM_INIT_AUTOMAKE([-Wall -Werror foreign -Wno-portability])
# Support silent build rules. Requires at least automake-1.11.
@ -74,6 +74,7 @@ AC_CHECK_HEADERS([ctype.h errno.h stdio.h stdlib.h])
AC_CHECK_HEADERS([string.h sys/param.h unistd.h ])
AC_CHECK_HEADERS([sys/sysctl.h])
AC_CHECK_HEADERS([threads.h])
AC_CHECK_HEADERS([monitor.h])
dnl humanize_number(3) is a great function, but it's not standard.
dnl Note Macosx has the function in libutil.a but doesn't ship the
@ -148,10 +149,18 @@ fi
AC_SUBST(GETTEXT_CFLAGS)
AC_SUBST(GETTEXT_LIBS)
GETTEXT_BINDIR=${GETTEXT_PREFIX}/bin
AC_SUBST(GETTEXT_BINDIR)
GETTEXT_LIBDIR=${GETTEXT_PREFIX}/lib
AC_SUBST(GETTEXT_LIBDIR)
if test -x ${GETTEXT_PREFIX}/bin/msgfmt ; then
GETTEXT_BINDIR=${GETTEXT_PREFIX}/bin
elif test -x ${GETTEXT_PREFIX}/local/bin/msgfmt ; then
GETTEXT_BINDIR=${GETTEXT_PREFIX}/local/bin
else
AC_MSG_NOTICE("could not find msgfmt tool")
# Use a (bad) fall back value
GETTEXT_BINDIR=${GETTEXT_PREFIX}/bin
fi
AC_SUBST(GETTEXT_BINDIR)
AM_CONDITIONAL([HAVE_GETTEXT], [test "$HAVE_GETTEXT" = "yes"])
@ -287,6 +296,18 @@ if test "${LIBXO_WCWIDTH}" != "no"; then
AC_DEFINE([LIBXO_WCWIDTH], [1], [Enable local wcwidth implementation])
fi
AC_MSG_CHECKING([retain hash bucket size])
AC_ARG_WITH(retain-size,
[ --with-retain-size=[DIR] Specify retain hash bucket size (in bits)],
[XO_RETAIN_SIZE=$withval],
[XO_RETAIN_SIZE=default]
)
AC_MSG_RESULT([$XO_RETAIN_SIZE])
if test "${XO_RETAIN_SIZE}" != "default"; then
AC_DEFINE_UNQUOTED([XO_RETAIN_SIZE], ${XO_RETAIN_SIZE}, [Retain hash bucket size])
fi
AC_CHECK_LIB([m], [lrint])
AM_CONDITIONAL([HAVE_LIBM], [test "$HAVE_LIBM" != "no"])
@ -347,12 +368,15 @@ XO_SRCDIR=${srcdir}
XO_LIBDIR=${libdir}
XO_BINDIR=${bindir}
XO_INCLUDEDIR=${includedir}
XO_CFLAGS="${CFLAGS}"
AC_SUBST(XO_LIBS)
AC_SUBST(XO_SRCDIR)
AC_SUBST(XO_LIBDIR)
AC_SUBST(XO_BINDIR)
AC_SUBST(XO_INCLUDEDIR)
AC_SUBST(XO_LIBEXT)
AC_SUBST(XO_CFLAGS)
AC_ARG_WITH(encoder-dir,
[ --with-encoder-dir=[DIR] Specify location of encoder libraries],
@ -449,4 +473,5 @@ AC_MSG_NOTICE([summary of build options:
isthreaded: ${HAVE_ISTHREADED:-no}
thread-local: ${THREAD_LOCAL:-no}
local wcwidth: ${LIBXO_WCWIDTH:-no}
retain size: ${XO_RETAIN_SIZE:-no}
])

File diff suppressed because it is too large Load Diff

View File

@ -699,25 +699,26 @@ XOF_WARN is set, a warning will be generated.
Field modifiers are flags which modify the way content emitted for
particular 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) |
| g | gettext | Call gettext on field's render content |
| h | humanize (hn) | Format large numbers in human-readable style |
| | hn-space | Humanize: Place space between numeric and unit |
| | hn-decimal | Humanize: Add a decimal digit, if number < 10 |
| | hn-1000 | Humanize: Use 1000 as divisor instead of 1024 |
| k | key | Field is a key, suitable for XPath predicates |
| l | leaf-list | Field is a leaf-list |
| n | no-quotes | Do not quote the field when using JSON style |
| p | plural | Gettext: Use comma-separated plural form |
| q | quotes | Quote the field when using JSON style |
| t | trim | Trim leading and trailing whitespace |
| w | white | A blank (" ") is appended after the label |
|---+---------------+-------------------------------------------------|
|---+---------------+--------------------------------------------------|
| M | Name | Description |
|---+---------------+--------------------------------------------------|
| a | argument | The content appears as a 'const char *' argument |
| 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) |
| g | gettext | Call gettext on field's render content |
| h | humanize (hn) | Format large numbers in human-readable style |
| | hn-space | Humanize: Place space between numeric and unit |
| | hn-decimal | Humanize: Add a decimal digit, if number < 10 |
| | hn-1000 | Humanize: Use 1000 as divisor instead of 1024 |
| k | key | Field is a key, suitable for XPath predicates |
| l | leaf-list | Field is a leaf-list |
| n | no-quotes | Do not quote the field when using JSON style |
| p | plural | Gettext: Use comma-separated plural form |
| q | quotes | Quote the field when using JSON style |
| t | trim | Trim leading and trailing whitespace |
| w | white | A blank (" ") is appended after the label |
|---+---------------+--------------------------------------------------|
Roles and modifiers can also use more verbose names, when preceeded by
a comma. For example, the modifier string "Lwc" (or "L,white,colon")
@ -727,6 +728,27 @@ modifier string "Vkq" (or ":key,quote") means the field has a value
role (the default role), that it is a key for the current instance,
and that the value should be quoted when encoded for JSON.
**** The Argument Modifier ({a:})
The argument modifier indicates that the content of the field
descriptor will be placed as a UTF-8 string (const char *) argument
within the xo_emit parameters.
EXAMPLE:
xo_emit("{La:} {a:}\n", "Label text", "label", "value");
TEXT:
Label text value
JSON:
"label": "value"
XML:
<label>value</label>
The argument modifier allows field names for value fields to be passed
on the stack, avoiding the need to build a field descriptor using
snprintf. For many field roles, the argument modifier is not needed,
since those roles have specific mechanisms for arguments, such as
"{C:fg-%s}".
**** The Colon Modifier ({c:})
The colon modifier appends a single colon to the data value:
@ -907,6 +929,21 @@ needed, but often this needs to be controlled by the caller.
JSON:
"year": "2014"
The heuristic is based on the format; if the format uses any of the
following conversion specifiers, then no quotes are used:
d i o u x X D O U e E f F g G a A c C p
**** The Trim Modifier ({t:})
The trim modifier removes any leading or trailing whitespace from
the value.
EXAMPLE:
xo_emit("{t:description}", " some input ");
JSON:
"description": "some input"
**** The White Space Modifier ({w:})
The white space modifier appends a single space to the data value:
@ -1029,6 +1066,24 @@ LANG, or LC_ALL environment varibles. The first of this list of
variables is used and if none of the variables are set, the locale
defaults to "UTF-8".
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".
|--------+-----------------+-------------------------------|
| Format | Argument Type | Argument Contents |
|--------+-----------------+-------------------------------|
| %s | const char * | UTF-8 string |
| %S | const char * | UTF-8 string (alias for '%s') |
| %ls | const wchar_t * | Wide character UNICODE string |
| %hs | const char * | locale-based string |
|--------+-----------------+-------------------------------|
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.
@ -1163,6 +1218,53 @@ variants might be wise.
| xo_emit_errc | xo_emit_errc_p |
|------------------+------------------------|
*** Retaining Parsed Format Information @retain@
libxo can retain the parsed internal information related to the given
format string, allowing subsequent xo_emit calls, the retained
information is used, avoiding repetitive parsing of the format string.
SYNTAX:
int xo_emit_f(xo_emit_flags_t flags, const char fmt, ...);
EXAMPLE:
xo_emit_f(XOEF_RETAIN, "{:some/%02d}{:thing/%-6s}{:fancy}\n",
some, thing, fancy);
To retain parsed format information, use the XOEF_RETAIN flag to the
xo_emit_f() function. A complete set of xo_emit_f functions exist to
match all the xo_emit function signatures (with handles, varadic
argument, and printf-like flags):
|------------------+------------------------|
| Function | Flags Equivalent |
|------------------+------------------------|
| xo_emit_hv | xo_emit_hvf |
| xo_emit_h | xo_emit_hf |
| xo_emit | xo_emit_f |
| xo_emit_hvp | xo_emit_hvfp |
| xo_emit_hp | xo_emit_hfp |
| xo_emit_p | xo_emit_fp |
|------------------+------------------------|
The format string must be immutable across multiple calls to xo_emit_f(),
since the library retains the string. Typically this is done by using
static constant strings, such as string literals. If the string is not
immutable, the XOEF_RETAIN flag must not be used.
The functions xo_retain_clear() and xo_retain_clear_all() release
internal information on either a single format string or all format
strings, respectively. Neither is required, but the library will
retain this information until it is cleared or the process exits.
const char *fmt = "{:name} {:count/%d}\n";
for (i = 0; i < 1000; i++) {
xo_open_instance("item");
xo_emit_f(XOEF_RETAIN, fmt, name[i], count[i]);
}
xo_retain_clear(fmt);
The retained information is kept as thread-specific data.
*** Example
In this example, the value for the number of items in stock is emitted:
@ -1196,46 +1298,6 @@ penultimate line to:
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 |
|-------------+-------------------------------------------------------|
| color | Enable colors/effects for display styles (TEXT, HTML) |
| 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) |
| log-gettext | Log (via stderr) each gettext(3) string lookup |
| log-syslog | Log (via stderr) each syslog message (via xo_syslog) |
| no-humanize | Ignore the {h:} modifier (TEXT, HTML) |
| 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 |
| underscores | Replace XML-friendly "-"s with JSON friendly "_"s e |
| 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
@ -1382,20 +1444,81 @@ properly.
xo_close_marker("fish-guts");
}
** Command-line Arguments
libxo uses command line options to trigger rendering behavior. The
following options are recognised:
- --libxo <options>
- --libxo=<options>
- --libxo:<brief-options>
Programs using libxo are expecting to call the xo_parse_args function
to parse these arguments. See ^xo_parse_args^ for details.
Options is a comma-separated list of tokens that correspond to output
styles, flags, or features:
|-------------+-------------------------------------------------------|
| Token | Action |
|-------------+-------------------------------------------------------|
| color | Enable colors/effects for display styles (TEXT, HTML) |
| 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) |
| log-gettext | Log (via stderr) each gettext(3) string lookup |
| log-syslog | Log (via stderr) each syslog message (via xo_syslog) |
| no-humanize | Ignore the {h:} modifier (TEXT, HTML) |
| no-locale | Do not initialize the locale setting |
| no-retain | Prevent retaining formatting information |
| 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 |
| retain | Force retaining formatting information |
| text | Emit TEXT output |
| underscores | Replace XML-friendly "-"s with JSON friendly "_"s e |
| 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^.
* The libxo API
This section gives details about the functions in libxo, how to call
them, and the actions they perform.
** Handles @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).
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.
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.
The library has a default handle that is automatically initialized.
By default, this handle will send text style output (XO_STYLE_TEXT) to
standard output. The xo_set_style and xo_set_flags functions can be
used to change this behavior.
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.
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 convenience of
callers, the libxo library includes handle-less functions that
implicitly use the default handle.
For example, the following are equivalent:
@ -1404,46 +1527,6 @@ For example, the following are equivalent:
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:
@ -1653,11 +1736,34 @@ 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().
*** Single Field Emitting Functions (xo_emit_field) @xo_emit_field@
The following functions can also make output, but only make a single
field at a time:
int xo_emit_field_hv (xo_handle_t *xop, const char *rolmod,
const char *contents, const char *fmt,
const char *efmt, va_list vap);
int xo_emit_field_h (xo_handle_t *xop, const char *rolmod,
const char *contents, const char *fmt,
const char *efmt, ...);
int xo_emit_field (const char *rolmod, const char *contents,
const char *fmt, const char *efmt, ...);
These functions are intended to avoid the scenario where one
would otherwise need to compose a format descriptors using
snprintf(). The individual parts of the format descriptor are
passed in distinctly.
xo_emit("T", "Host name is ", NULL, NULL);
xo_emit("V", "host-name", NULL, NULL, host-name);
*** 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, ...);
@ -2555,23 +2661,23 @@ 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
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.
delay in tracking HEAD. 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.
output calls with a single code path.
*** Did the complex semantics of format strings evolve over time?

View File

@ -135,7 +135,7 @@ cbor_encode_uint (xo_buffer_t *xbp, uint64_t minor, unsigned limit)
char *bp = xbp->xb_curp;
int i, m;
if (minor > (1UL<<32)) {
if (minor > (1ULL << 32)) {
*bp++ |= CBOR_LEN64;
m = 64;

View File

@ -77,34 +77,34 @@ while test $# -gt 0; do
;;
--cflags)
echo -I@LIBXO_INCLUDEDIR@ @LIBXO_CFLAGS@
echo -I@XO_INCLUDEDIR@ @XO_CFLAGS@
;;
--share)
echo @LIBXO_SHAREDIR@
echo @XO_SHAREDIR@
;;
--bindir)
echo @LIBXO_BINDIR@
echo @XO_BINDIR@
;;
--libdir)
echo @LIBXO_LIBDIR@
echo @XO_LIBDIR@
;;
--libs)
if [ "`uname`" = "Linux" ]
then
if [ "@LIBXO_LIBDIR@" = "-L/usr/lib" -o "@LIBXO_LIBDIR@" = "-L/usr/lib64" ]
if [ "@XO_LIBDIR@" = "-L/usr/lib" -o "@XO_LIBDIR@" = "-L/usr/lib64" ]
then
echo @LIBXO_LIBS@
echo @XO_LIBS@
else
echo -L@LIBXO_LIBDIR@ @LIBXO_LIBS@
echo -L@XO_LIBDIR@ @XO_LIBS@
fi
else
echo -L@LIBXO_LIBDIR@ @LIBXO_LIBS@ @WIN32_EXTRA_LIBADD@
echo -L@XO_LIBDIR@ @XO_LIBS@
fi
;;

View File

@ -19,7 +19,8 @@
* http://juniper.github.io/libxo/libxo-manual.html
*
* For first time readers, the core bits of code to start looking at are:
* - xo_do_emit() -- the central function of the library
* - xo_do_emit() -- parse and emit a set of fields
* - xo_do_emit_fields -- the central function of the library
* - xo_do_format_field() -- handles formatting a single field
* - xo_transiton() -- the state machine that keeps things sane
* and of course the "xo_handle_t" data structure, which carries all
@ -120,6 +121,7 @@
const char xo_version[] = LIBXO_VERSION;
const char xo_version_extra[] = LIBXO_VERSION_EXTRA;
static const char xo_default_format[] = "%s";
#ifndef UNUSED
#define UNUSED __attribute__ ((__unused__))
@ -338,6 +340,7 @@ typedef unsigned long xo_xff_flags_t;
#define XFF_GT_FIELD (1<<19) /* Call gettext() on a field */
#define XFF_GT_PLURAL (1<<20) /* Call dngettext to find plural form */
#define XFF_ARGUMENT (1<<21) /* Content provided via argument */
/* Flags to turn off when we don't want i18n processing */
#define XFF_GT_FLAGS (XFF_GT_FIELD | XFF_GT_PLURAL)
@ -1046,7 +1049,7 @@ xo_is_utf8 (char ch)
return (ch & 0x80);
}
static int
static inline int
xo_utf8_to_wc_len (const char *buf)
{
unsigned b = (unsigned char) *buf;
@ -1105,9 +1108,13 @@ xo_buf_utf8_len (xo_handle_t *xop, const char *buf, int bufsiz)
* bits we pull off the first character is dependent on the length,
* but we put 6 bits off all other bytes.
*/
static wchar_t
static inline wchar_t
xo_utf8_char (const char *buf, int len)
{
/* Most common case: singleton byte */
if (len == 1)
return (unsigned char) buf[0];
int i;
wchar_t wc;
const unsigned char *cp = (const unsigned char *) buf;
@ -1281,6 +1288,195 @@ xo_data_escape (xo_handle_t *xop, const char *str, int len)
xo_buf_escape(xop, &xop->xo_data, str, len, 0);
}
#ifdef LIBXO_NO_RETAIN
/*
* Empty implementations of the retain logic
*/
void
xo_retain_clear_all (void)
{
return;
}
void
xo_retain_clear (const char *fmt UNUSED)
{
return;
}
static void
xo_retain_add (const char *fmt UNUSED, xo_field_info_t *fields UNUSED,
unsigned num_fields UNUSED)
{
return;
}
static int
xo_retain_find (const char *fmt UNUSED, xo_field_info_t **valp UNUSED,
unsigned *nump UNUSED)
{
return -1;
}
#else /* !LIBXO_NO_RETAIN */
/*
* Retain: We retain parsed field definitions to enhance performance,
* especially inside loops. We depend on the caller treating the format
* strings as immutable, so that we can retain pointers into them. We
* hold the pointers in a hash table, so allow quick access. Retained
* information is retained until xo_retain_clear is called.
*/
/*
* xo_retain_entry_t holds information about one retained set of
* parsed fields.
*/
typedef struct xo_retain_entry_s {
struct xo_retain_entry_s *xre_next; /* Pointer to next (older) entry */
unsigned long xre_hits; /* Number of times we've hit */
const char *xre_format; /* Pointer to format string */
unsigned xre_num_fields; /* Number of fields saved */
xo_field_info_t *xre_fields; /* Pointer to fields */
} xo_retain_entry_t;
/*
* xo_retain_t holds a complete set of parsed fields as a hash table.
*/
#ifndef XO_RETAIN_SIZE
#define XO_RETAIN_SIZE 6
#endif /* XO_RETAIN_SIZE */
#define RETAIN_HASH_SIZE (1<<XO_RETAIN_SIZE)
typedef struct xo_retain_s {
xo_retain_entry_t *xr_bucket[RETAIN_HASH_SIZE];
} xo_retain_t;
static THREAD_LOCAL(xo_retain_t) xo_retain;
static THREAD_LOCAL(unsigned) xo_retain_count;
/*
* Simple hash function based on Thomas Wang's paper. The original is
* gone, but an archive is available on the Way Back Machine:
*
* http://web.archive.org/web/20071223173210/\
* http://www.concentric.net/~Ttwang/tech/inthash.htm
*
* For our purposes, we can assume the low four bits are uninteresting
* since any string less that 16 bytes wouldn't be worthy of
* retaining. We toss the high bits also, since these bits are likely
* to be common among constant format strings. We then run Wang's
* algorithm, and cap the result at RETAIN_HASH_SIZE.
*/
static unsigned
xo_retain_hash (const char *fmt)
{
volatile uintptr_t iptr = (uintptr_t) (const void *) fmt;
/* Discard low four bits and high bits; they aren't interesting */
uint32_t val = (uint32_t) ((iptr >> 4) & (((1 << 24) - 1)));
val = (val ^ 61) ^ (val >> 16);
val = val + (val << 3);
val = val ^ (val >> 4);
val = val * 0x3a8f05c5; /* My large prime number */
val = val ^ (val >> 15);
val &= RETAIN_HASH_SIZE - 1;
return val;
}
/*
* Walk all buckets, clearing all retained entries
*/
void
xo_retain_clear_all (void)
{
int i;
xo_retain_entry_t *xrep, *next;
for (i = 0; i < RETAIN_HASH_SIZE; i++) {
for (xrep = xo_retain.xr_bucket[i]; xrep; xrep = next) {
next = xrep->xre_next;
xo_free(xrep);
}
xo_retain.xr_bucket[i] = NULL;
}
xo_retain_count = 0;
}
/*
* Walk all buckets, clearing all retained entries
*/
void
xo_retain_clear (const char *fmt)
{
xo_retain_entry_t **xrepp;
unsigned hash = xo_retain_hash(fmt);
for (xrepp = &xo_retain.xr_bucket[hash]; *xrepp;
xrepp = &(*xrepp)->xre_next) {
if ((*xrepp)->xre_format == fmt) {
*xrepp = (*xrepp)->xre_next;
xo_retain_count -= 1;
return;
}
}
}
/*
* Search the hash for an entry matching 'fmt'; return it's fields.
*/
static int
xo_retain_find (const char *fmt, xo_field_info_t **valp, unsigned *nump)
{
if (xo_retain_count == 0)
return -1;
unsigned hash = xo_retain_hash(fmt);
xo_retain_entry_t *xrep;
for (xrep = xo_retain.xr_bucket[hash]; xrep != NULL;
xrep = xrep->xre_next) {
if (xrep->xre_format == fmt) {
*valp = xrep->xre_fields;
*nump = xrep->xre_num_fields;
xrep->xre_hits += 1;
return 0;
}
}
return -1;
}
static void
xo_retain_add (const char *fmt, xo_field_info_t *fields, unsigned num_fields)
{
unsigned hash = xo_retain_hash(fmt);
xo_retain_entry_t *xrep;
unsigned sz = sizeof(*xrep) + (num_fields + 1) * sizeof(*fields);
xo_field_info_t *xfip;
xrep = xo_realloc(NULL, sz);
if (xrep == NULL)
return;
xfip = (xo_field_info_t *) &xrep[1];
memcpy(xfip, fields, num_fields * sizeof(*fields));
bzero(xrep, sizeof(*xrep));
xrep->xre_format = fmt;
xrep->xre_fields = xfip;
xrep->xre_num_fields = num_fields;
/* Record the field info in the retain bucket */
xrep->xre_next = xo_retain.xr_bucket[hash];
xo_retain.xr_bucket[hash] = xrep;
xo_retain_count += 1;
}
#endif /* !LIBXO_NO_RETAIN */
/*
* Generate a warning. Normally, this is a text message written to
* standard error. If the XOF_WARN_XML flag is set, then we generate
@ -1574,6 +1770,19 @@ xo_message_hcv (xo_handle_t *xop, int code, const char *fmt, va_list vap)
break;
}
switch (xo_style(xop)) {
case XO_STYLE_HTML:
if (XOIF_ISSET(xop, XOIF_DIV_OPEN)) {
static char div_close[] = "</div>";
XOIF_CLEAR(xop, XOIF_DIV_OPEN);
xo_data_append(xop, div_close, sizeof(div_close) - 1);
if (XOF_ISSET(xop, XOF_PRETTY))
xo_data_append(xop, "\n", 1);
}
break;
}
(void) xo_flush_h(xop);
}
@ -1679,6 +1888,39 @@ xo_create_to_file (FILE *fp, xo_style_t style, xo_xof_flags_t flags)
return xop;
}
/**
* Set the default handler to output to a file.
* @xop libxo handle
* @fp FILE pointer to use
*/
int
xo_set_file_h (xo_handle_t *xop, FILE *fp)
{
xop = xo_default(xop);
if (fp == NULL) {
xo_failure(xop, "xo_set_file: NULL fp");
return -1;
}
xop->xo_opaque = fp;
xop->xo_write = xo_write_to_file;
xop->xo_close = xo_close_file;
xop->xo_flush = xo_flush_file;
return 0;
}
/**
* Set the default handler to output to a file.
* @fp FILE pointer to use
*/
int
xo_set_file (FILE *fp)
{
return xo_set_file_h(NULL, fp);
}
/**
* Release any resources held by the handle.
* @xop XO handle to alter (or NULL for default handle)
@ -1824,9 +2066,11 @@ static xo_mapping_t xo_xof_names[] = {
{ XOF_LOG_SYSLOG, "log-syslog" },
{ XOF_NO_HUMANIZE, "no-humanize" },
{ XOF_NO_LOCALE, "no-locale" },
{ XOF_RETAIN_NONE, "no-retain" },
{ XOF_NO_TOP, "no-top" },
{ XOF_NOT_FIRST, "not-first" },
{ XOF_PRETTY, "pretty" },
{ XOF_RETAIN_ALL, "retain" },
{ XOF_UNDERSCORES, "underscores" },
{ XOF_UNITS, "units" },
{ XOF_WARN, "warn" },
@ -3613,10 +3857,9 @@ xo_format_text (xo_handle_t *xop, const char *str, int len)
}
static void
xo_format_title (xo_handle_t *xop, xo_field_info_t *xfip)
xo_format_title (xo_handle_t *xop, xo_field_info_t *xfip,
const char *str, unsigned len)
{
const char *str = xfip->xfi_content;
unsigned len = xfip->xfi_clen;
const char *fmt = xfip->xfi_format;
unsigned flen = xfip->xfi_flen;
xo_xff_flags_t flags = xfip->xfi_flags;
@ -4083,10 +4326,9 @@ xo_format_value (xo_handle_t *xop, const char *name, int nlen,
}
static void
xo_set_gettext_domain (xo_handle_t *xop, xo_field_info_t *xfip)
xo_set_gettext_domain (xo_handle_t *xop, xo_field_info_t *xfip,
const char *str, unsigned len)
{
const char *str = xfip->xfi_content;
unsigned len = xfip->xfi_clen;
const char *fmt = xfip->xfi_format;
unsigned flen = xfip->xfi_flen;
@ -4335,13 +4577,13 @@ xo_colors_enabled (xo_handle_t *xop UNUSED)
}
static void
xo_colors_handle_text (xo_handle_t *xop UNUSED, xo_colors_t *newp)
xo_colors_handle_text (xo_handle_t *xop, xo_colors_t *newp)
{
char buf[BUFSIZ];
char *cp = buf, *ep = buf + sizeof(buf);
unsigned i, bit;
xo_colors_t *oldp = &xop->xo_colors;
const char *code;
const char *code = NULL;
/*
* Start the buffer with an escape. We don't want to add the '['
@ -4460,10 +4702,9 @@ xo_colors_handle_html (xo_handle_t *xop, xo_colors_t *newp)
}
static void
xo_format_colors (xo_handle_t *xop, xo_field_info_t *xfip)
xo_format_colors (xo_handle_t *xop, xo_field_info_t *xfip,
const char *str, unsigned len)
{
const char *str = xfip->xfi_content;
unsigned len = xfip->xfi_clen;
const char *fmt = xfip->xfi_format;
unsigned flen = xfip->xfi_flen;
@ -4534,10 +4775,9 @@ xo_format_colors (xo_handle_t *xop, xo_field_info_t *xfip)
}
static void
xo_format_units (xo_handle_t *xop, xo_field_info_t *xfip)
xo_format_units (xo_handle_t *xop, xo_field_info_t *xfip,
const char *str, unsigned len)
{
const char *str = xfip->xfi_content;
unsigned len = xfip->xfi_clen;
const char *fmt = xfip->xfi_format;
unsigned flen = xfip->xfi_flen;
xo_xff_flags_t flags = xfip->xfi_flags;
@ -4589,10 +4829,9 @@ xo_format_units (xo_handle_t *xop, xo_field_info_t *xfip)
}
static int
xo_find_width (xo_handle_t *xop, xo_field_info_t *xfip)
xo_find_width (xo_handle_t *xop, xo_field_info_t *xfip,
const char *str, unsigned len)
{
const char *str = xfip->xfi_content;
unsigned len = xfip->xfi_clen;
const char *fmt = xfip->xfi_format;
unsigned flen = xfip->xfi_flen;
@ -4639,7 +4878,8 @@ xo_anchor_clear (xo_handle_t *xop)
* format it when the end anchor tag is seen.
*/
static void
xo_anchor_start (xo_handle_t *xop, xo_field_info_t *xfip)
xo_anchor_start (xo_handle_t *xop, xo_field_info_t *xfip,
const char *str, unsigned len)
{
if (xo_style(xop) != XO_STYLE_TEXT && xo_style(xop) != XO_STYLE_HTML)
return;
@ -4656,11 +4896,12 @@ xo_anchor_start (xo_handle_t *xop, xo_field_info_t *xfip)
* Now we find the width, if possible. If it's not there,
* we'll get it on the end anchor.
*/
xop->xo_anchor_min_width = xo_find_width(xop, xfip);
xop->xo_anchor_min_width = xo_find_width(xop, xfip, str, len);
}
static void
xo_anchor_stop (xo_handle_t *xop, xo_field_info_t *xfip)
xo_anchor_stop (xo_handle_t *xop, xo_field_info_t *xfip,
const char *str, unsigned len)
{
if (xo_style(xop) != XO_STYLE_TEXT && xo_style(xop) != XO_STYLE_HTML)
return;
@ -4672,7 +4913,7 @@ xo_anchor_stop (xo_handle_t *xop, xo_field_info_t *xfip)
XOIF_CLEAR(xop, XOIF_UNITS_PENDING);
int width = xo_find_width(xop, xfip);
int width = xo_find_width(xop, xfip, str, len);
if (width == 0)
width = xop->xo_anchor_min_width;
@ -4787,6 +5028,7 @@ static xo_mapping_t xo_role_names[] = {
#define XO_ROLE_NEWLINE '\n'
static xo_mapping_t xo_modifier_names[] = {
{ XFF_ARGUMENT, "argument" },
{ XFF_COLON, "colon" },
{ XFF_COMMA, "comma" },
{ XFF_DISPLAY_ONLY, "display" },
@ -4858,6 +5100,7 @@ xo_count_fields (xo_handle_t *xop UNUSED, const char *fmt)
* '[': start a section of anchored text
* ']': end a section of anchored text
* The following modifiers are also supported:
* 'a': content is provided via argument (const char *), not descriptor
* 'c': flag: emit a colon after the label
* 'd': field is only emitted for display styles (text and html)
* 'e': field is only emitted for encoding styles (xml and json)
@ -4884,7 +5127,7 @@ xo_parse_roles (xo_handle_t *xop, const char *fmt,
xo_xff_flags_t flags = 0;
uint8_t fnum = 0;
for (sp = basep; sp; sp++) {
for (sp = basep; sp && *sp; sp++) {
if (*sp == ':' || *sp == '/' || *sp == '}')
break;
@ -4961,6 +5204,10 @@ xo_parse_roles (xo_handle_t *xop, const char *fmt,
fnum = (fnum * 10) + (*sp - '0');
break;
case 'a':
flags |= XFF_ARGUMENT;
break;
case 'c':
flags |= XFF_COLON;
break;
@ -5133,7 +5380,6 @@ static int
xo_parse_fields (xo_handle_t *xop, xo_field_info_t *fields,
unsigned num_fields, const char *fmt)
{
static const char default_format[] = "%s";
const char *cp, *sp, *ep, *basep;
unsigned field = 0;
xo_field_info_t *xfip = fields;
@ -5267,12 +5513,12 @@ xo_parse_fields (xo_handle_t *xop, xo_field_info_t *fields,
xfip->xfi_next = ++sp;
/* If we have content, then we have a default format */
if (xfip->xfi_clen || format) {
if (xfip->xfi_clen || format || (xfip->xfi_flags & XFF_ARGUMENT)) {
if (format) {
xfip->xfi_format = format;
xfip->xfi_flen = flen;
} else if (xo_role_wants_default_format(xfip->xfi_ftype)) {
xfip->xfi_format = default_format;
xfip->xfi_format = xo_default_format;
xfip->xfi_flen = 2;
}
}
@ -5568,9 +5814,8 @@ xo_gettext_combine_formats (xo_handle_t *xop, const char *fmt UNUSED,
* Summary: i18n aighn't cheap.
*/
static const char *
xo_gettext_build_format (xo_handle_t *xop UNUSED,
xo_field_info_t *fields UNUSED,
int this_field UNUSED,
xo_gettext_build_format (xo_handle_t *xop,
xo_field_info_t *fields, int this_field,
const char *fmt, char **new_fmtp)
{
if (xo_style_is_encoding(xop))
@ -5686,17 +5931,22 @@ xo_gettext_rebuild_content (xo_handle_t *xop UNUSED,
#endif /* HAVE_GETTEXT */
/*
* The central function for emitting libxo output.
* Emit a set of fields. This is really the core of libxo.
*/
static int
xo_do_emit (xo_handle_t *xop, const char *fmt)
xo_do_emit_fields (xo_handle_t *xop, xo_field_info_t *fields,
unsigned max_fields, const char *fmt)
{
int gettext_inuse = 0;
int gettext_changed = 0;
int gettext_reordered = 0;
unsigned ftype;
xo_xff_flags_t flags;
xo_field_info_t *new_fields = NULL;
xo_field_info_t *xfip;
unsigned field;
int rc = 0;
int flush = XOF_ISSET(xop, XOF_FLUSH);
int flush_line = XOF_ISSET(xop, XOF_FLUSH_LINE);
char *new_fmt = NULL;
@ -5704,20 +5954,6 @@ xo_do_emit (xo_handle_t *xop, const char *fmt)
if (XOIF_ISSET(xop, XOIF_REORDER) || xo_style(xop) == XO_STYLE_ENCODER)
flush_line = 0;
xop->xo_columns = 0; /* Always reset it */
xop->xo_errno = errno; /* Save for "%m" */
unsigned max_fields = xo_count_fields(xop, fmt), field;
xo_field_info_t fields[max_fields], *xfip;
bzero(fields, max_fields * sizeof(fields[0]));
if (xo_parse_fields(xop, fields, max_fields, fmt))
return -1; /* Warning already displayed */
unsigned ftype;
xo_xff_flags_t flags;
/*
* Some overhead for gettext; if the fields in the msgstr returned
* by gettext are reordered, then we need to record start and end
@ -5745,6 +5981,18 @@ xo_do_emit (xo_handle_t *xop, const char *fmt)
min_fstart = field;
}
const char *content = xfip->xfi_content;
int clen = xfip->xfi_clen;
if (flags & XFF_ARGUMENT) {
/*
* Argument flag means the content isn't given in the descriptor,
* but as a UTF-8 string ('const char *') argument in xo_vap.
*/
content = va_arg(xop->xo_vap, char *);
clen = content ? strlen(content) : 0;
}
if (ftype == XO_ROLE_NEWLINE) {
xo_line_close(xop);
if (flush_line && xo_flush_h(xop) < 0)
@ -5773,15 +6021,15 @@ xo_do_emit (xo_handle_t *xop, const char *fmt)
}
if (ftype == 'V')
xo_format_value(xop, xfip->xfi_content, xfip->xfi_clen,
xo_format_value(xop, content, clen,
xfip->xfi_format, xfip->xfi_flen,
xfip->xfi_encoding, xfip->xfi_elen, flags);
else if (ftype == '[')
xo_anchor_start(xop, xfip);
xo_anchor_start(xop, xfip, content, clen);
else if (ftype == ']')
xo_anchor_stop(xop, xfip);
xo_anchor_stop(xop, xfip, content, clen);
else if (ftype == 'C')
xo_format_colors(xop, xfip);
xo_format_colors(xop, xfip, content, clen);
else if (ftype == 'G') {
/*
@ -5792,7 +6040,7 @@ xo_do_emit (xo_handle_t *xop, const char *fmt)
* Since gettext returns strings in a static buffer, we make
* a copy in new_fmt.
*/
xo_set_gettext_domain(xop, xfip);
xo_set_gettext_domain(xop, xfip, content, clen);
if (!gettext_inuse) { /* Only translate once */
gettext_inuse = 1;
@ -5843,17 +6091,17 @@ xo_do_emit (xo_handle_t *xop, const char *fmt)
}
continue;
} else if (xfip->xfi_clen || xfip->xfi_format) {
} else if (clen || xfip->xfi_format) {
const char *class_name = xo_class_name(ftype);
if (class_name)
xo_format_content(xop, class_name, xo_tag_name(ftype),
xfip->xfi_content, xfip->xfi_clen,
content, clen,
xfip->xfi_format, xfip->xfi_flen, flags);
else if (ftype == 'T')
xo_format_title(xop, xfip);
xo_format_title(xop, xfip, content, clen);
else if (ftype == 'U')
xo_format_units(xop, xfip);
xo_format_units(xop, xfip, content, clen);
else
xo_failure(xop, "unknown field type: '%c'", ftype);
}
@ -5884,7 +6132,7 @@ xo_do_emit (xo_handle_t *xop, const char *fmt)
if (flush && !XOIF_ISSET(xop, XOIF_ANCHOR)) {
if (xo_write(xop) < 0)
rc = -1; /* Report failure */
else if (xop->xo_flush && xop->xo_flush(xop->xo_opaque) < 0)
else if (xo_flush_h(xop) < 0)
rc = -1;
}
@ -5904,6 +6152,53 @@ xo_do_emit (xo_handle_t *xop, const char *fmt)
return (rc < 0) ? rc : (int) xop->xo_columns;
}
/*
* Parse and emit a set of fields
*/
static int
xo_do_emit (xo_handle_t *xop, xo_emit_flags_t flags, const char *fmt)
{
xop->xo_columns = 0; /* Always reset it */
xop->xo_errno = errno; /* Save for "%m" */
if (fmt == NULL)
return 0;
unsigned max_fields;
xo_field_info_t *fields = NULL;
/* Adjust XOEF_RETAIN based on global flags */
if (XOF_ISSET(xop, XOF_RETAIN_ALL))
flags |= XOEF_RETAIN;
if (XOF_ISSET(xop, XOF_RETAIN_NONE))
flags &= ~XOEF_RETAIN;
/*
* Check for 'retain' flag, telling us to retain the field
* information. If we've already saved it, then we can avoid
* re-parsing the format string.
*/
if (!(flags & XOEF_RETAIN)
|| xo_retain_find(fmt, &fields, &max_fields) != 0
|| fields == NULL) {
/* Nothing retained; parse the format string */
max_fields = xo_count_fields(xop, fmt);
fields = alloca(max_fields * sizeof(fields[0]));
bzero(fields, max_fields * sizeof(fields[0]));
if (xo_parse_fields(xop, fields, max_fields, fmt))
return -1; /* Warning already displayed */
if (flags & XOEF_RETAIN) {
/* Retain the info */
xo_retain_add(fmt, fields, max_fields);
}
}
return xo_do_emit_fields(xop, fields, max_fields, fmt);
}
/*
* Rebuild a format string in a gettext-friendly format. This function
* is exposed to tools can perform this function. See xo(1).
@ -5944,7 +6239,7 @@ xo_emit_hv (xo_handle_t *xop, const char *fmt, va_list vap)
xop = xo_default(xop);
va_copy(xop->xo_vap, vap);
rc = xo_do_emit(xop, fmt);
rc = xo_do_emit(xop, 0, fmt);
va_end(xop->xo_vap);
bzero(&xop->xo_vap, sizeof(xop->xo_vap));
@ -5958,7 +6253,7 @@ xo_emit_h (xo_handle_t *xop, const char *fmt, ...)
xop = xo_default(xop);
va_start(xop->xo_vap, fmt);
rc = xo_do_emit(xop, fmt);
rc = xo_do_emit(xop, 0, fmt);
va_end(xop->xo_vap);
bzero(&xop->xo_vap, sizeof(xop->xo_vap));
@ -5972,13 +6267,137 @@ xo_emit (const char *fmt, ...)
int rc;
va_start(xop->xo_vap, fmt);
rc = xo_do_emit(xop, fmt);
rc = xo_do_emit(xop, 0, fmt);
va_end(xop->xo_vap);
bzero(&xop->xo_vap, sizeof(xop->xo_vap));
return rc;
}
int
xo_emit_hvf (xo_handle_t *xop, xo_emit_flags_t flags,
const char *fmt, va_list vap)
{
int rc;
xop = xo_default(xop);
va_copy(xop->xo_vap, vap);
rc = xo_do_emit(xop, flags, fmt);
va_end(xop->xo_vap);
bzero(&xop->xo_vap, sizeof(xop->xo_vap));
return rc;
}
int
xo_emit_hf (xo_handle_t *xop, xo_emit_flags_t flags, const char *fmt, ...)
{
int rc;
xop = xo_default(xop);
va_start(xop->xo_vap, fmt);
rc = xo_do_emit(xop, flags, fmt);
va_end(xop->xo_vap);
bzero(&xop->xo_vap, sizeof(xop->xo_vap));
return rc;
}
int
xo_emit_f (xo_emit_flags_t flags, const char *fmt, ...)
{
xo_handle_t *xop = xo_default(NULL);
int rc;
va_start(xop->xo_vap, fmt);
rc = xo_do_emit(xop, flags, fmt);
va_end(xop->xo_vap);
bzero(&xop->xo_vap, sizeof(xop->xo_vap));
return rc;
}
/*
* Emit a single field by providing the info information typically provided
* inside the field description (role, modifiers, and formats). This is
* a convenience function to avoid callers using snprintf to build field
* descriptions.
*/
int
xo_emit_field_hv (xo_handle_t *xop, const char *rolmod, const char *contents,
const char *fmt, const char *efmt,
va_list vap)
{
int rc;
xop = xo_default(xop);
if (rolmod == NULL)
rolmod = "V";
xo_field_info_t xfi;
bzero(&xfi, sizeof(xfi));
const char *cp;
cp = xo_parse_roles(xop, rolmod, rolmod, &xfi);
if (cp == NULL)
return -1;
xfi.xfi_start = fmt;
xfi.xfi_content = contents;
xfi.xfi_format = fmt;
xfi.xfi_encoding = efmt;
xfi.xfi_clen = contents ? strlen(contents) : 0;
xfi.xfi_flen = fmt ? strlen(fmt) : 0;
xfi.xfi_elen = efmt ? strlen(efmt) : 0;
/* If we have content, then we have a default format */
if (contents && fmt == NULL
&& xo_role_wants_default_format(xfi.xfi_ftype)) {
xfi.xfi_format = xo_default_format;
xfi.xfi_flen = 2;
}
va_copy(xop->xo_vap, vap);
rc = xo_do_emit_fields(xop, &xfi, 1, fmt ?: contents ?: "field");
va_end(xop->xo_vap);
return rc;
}
int
xo_emit_field_h (xo_handle_t *xop, const char *rolmod, const char *contents,
const char *fmt, const char *efmt, ...)
{
int rc;
va_list vap;
va_start(vap, efmt);
rc = xo_emit_field_hv(xop, rolmod, contents, fmt, efmt, vap);
va_end(vap);
return rc;
}
int
xo_emit_field (const char *rolmod, const char *contents,
const char *fmt, const char *efmt, ...)
{
int rc;
va_list vap;
va_start(vap, efmt);
rc = xo_emit_field_hv(NULL, rolmod, contents, fmt, efmt, vap);
va_end(vap);
return rc;
}
int
xo_attr_hv (xo_handle_t *xop, const char *name, const char *fmt, va_list vap)
{
@ -6392,7 +6811,7 @@ xo_open_list_hf (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name)
}
int
xo_open_list_h (xo_handle_t *xop, const char *name UNUSED)
xo_open_list_h (xo_handle_t *xop, const char *name)
{
return xo_open_list_hf(xop, 0, name);
}
@ -6404,7 +6823,7 @@ xo_open_list (const char *name)
}
int
xo_open_list_hd (xo_handle_t *xop, const char *name UNUSED)
xo_open_list_hd (xo_handle_t *xop, const char *name)
{
return xo_open_list_hf(xop, XOF_DTRT, name);
}
@ -7113,6 +7532,11 @@ xo_transition (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name,
xsp->xs_state, new_state);
}
/* Handle the flush flag */
if (rc >= 0 && XOF_ISSET(xop, XOF_FLUSH))
if (xo_flush_h(xop))
rc = -1;
return rc;
marker_prevents_close:
@ -7179,22 +7603,11 @@ xo_set_allocator (xo_realloc_func_t realloc_func, xo_free_func_t free_func)
int
xo_flush_h (xo_handle_t *xop)
{
static char div_close[] = "</div>";
int rc;
xop = xo_default(xop);
switch (xo_style(xop)) {
case XO_STYLE_HTML:
if (XOIF_ISSET(xop, XOIF_DIV_OPEN)) {
XOIF_CLEAR(xop, XOIF_DIV_OPEN);
xo_data_append(xop, div_close, sizeof(div_close) - 1);
if (XOF_ISSET(xop, XOF_PRETTY))
xo_data_append(xop, "\n", 1);
}
break;
case XO_STYLE_ENCODER:
xo_encoder_handle(xop, XO_OP_FLUSH, NULL, NULL);
}
@ -7435,7 +7848,7 @@ xo_set_program (const char *name)
}
void
xo_set_version_h (xo_handle_t *xop, const char *version UNUSED)
xo_set_version_h (xo_handle_t *xop, const char *version)
{
xop = xo_default(xop);

View File

@ -94,6 +94,11 @@ typedef unsigned long long xo_xof_flags_t;
#define XOF_LOG_GETTEXT XOF_BIT(28) /** Log (stderr) gettext lookup strings */
#define XOF_UTF8 XOF_BIT(29) /** Force text output to be UTF8 */
#define XOF_RETAIN_ALL XOF_BIT(30) /** Force use of XOEF_RETAIN */
#define XOF_RETAIN_NONE XOF_BIT(31) /** Prevent use of XOEF_RETAIN */
typedef unsigned xo_emit_flags_t; /* Flags to xo_emit() and friends */
#define XOEF_RETAIN (1<<0) /* Retain parsed formatting information */
/*
* The xo_info_t structure provides a mapping between names and
@ -162,6 +167,12 @@ xo_set_flags (xo_handle_t *xop, xo_xof_flags_t flags);
void
xo_clear_flags (xo_handle_t *xop, xo_xof_flags_t flags);
int
xo_set_file_h (xo_handle_t *xop, FILE *fp);
int
xo_set_file (FILE *fp);
void
xo_set_info (xo_handle_t *xop, xo_info_t *infop, int count);
@ -180,6 +191,16 @@ xo_emit_h (xo_handle_t *xop, const char *fmt, ...);
int
xo_emit (const char *fmt, ...);
int
xo_emit_hvf (xo_handle_t *xop, xo_emit_flags_t flags,
const char *fmt, va_list vap);
int
xo_emit_hf (xo_handle_t *xop, xo_emit_flags_t flags, const char *fmt, ...);
int
xo_emit_f (xo_emit_flags_t flags, const char *fmt, ...);
PRINTFLIKE(2, 0)
static inline int
xo_emit_hvp (xo_handle_t *xop, const char *fmt, va_list vap)
@ -209,6 +230,36 @@ xo_emit_p (const char *fmt, ...)
return rc;
}
PRINTFLIKE(3, 0)
static inline int
xo_emit_hvfp (xo_handle_t *xop, xo_emit_flags_t flags,
const char *fmt, va_list vap)
{
return xo_emit_hvf(xop, flags, fmt, vap);
}
PRINTFLIKE(3, 4)
static inline int
xo_emit_hfp (xo_handle_t *xop, xo_emit_flags_t flags, const char *fmt, ...)
{
va_list vap;
va_start(vap, fmt);
int rc = xo_emit_hvf(xop, flags, fmt, vap);
va_end(vap);
return rc;
}
PRINTFLIKE(2, 3)
static inline int
xo_emit_fp (xo_emit_flags_t flags, const char *fmt, ...)
{
va_list vap;
va_start(vap, fmt);
int rc = xo_emit_hvf(NULL, flags, fmt, vap);
va_end(vap);
return rc;
}
int
xo_open_container_h (xo_handle_t *xop, const char *name);
@ -593,4 +644,23 @@ char *
xo_simplify_format (xo_handle_t *xop, const char *fmt, int with_numbers,
xo_simplify_field_func_t field_cb);
int
xo_emit_field_hv (xo_handle_t *xop, const char *rolmod, const char *contents,
const char *fmt, const char *efmt,
va_list vap);
int
xo_emit_field_h (xo_handle_t *xop, const char *rolmod, const char *contents,
const char *fmt, const char *efmt, ...);
int
xo_emit_field (const char *rolmod, const char *contents,
const char *fmt, const char *efmt, ...);
void
xo_retain_clear_all (void);
void
xo_retain_clear (const char *fmt);
#endif /* INCLUDE_XO_H */

View File

@ -0,0 +1,111 @@
.\" #
.\" # Copyright (c) 2016, 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, April 2016
.\"
.Dd April 15, 2016
.Dt LIBXO 3
.Os
.Sh NAME
.Nm xo_emit_f , xo_emit_hf , xo_emit_hvf
.Nd emit formatted output based on format string and arguments
.Sh LIBRARY
.Lb libxo
.Sh SYNOPSIS
.In libxo/xo.h
.Ft int
.Fn xo_emit_f "xo_emit_flags_t flags" "const char *fmt" "..."
.Ft int
.Fn xo_emit_hf "xo_handle_t *xop" "xo_emit_flags_t flags" "const char *fmt" "..."
.Ft int
.Fn xo_emit_hvf "xo_handle_t *xop" "xo_emit_flags_t flags" "const char *fmt" "va_list vap"
.Ft void
.Fn xo_retain_clear_all "void"
.Ft void
.Fn xo_retain_clear "const char *fmt"
.Sh DESCRIPTION
These functions allow callers to pass a set of flags to
.Nm
emitting functions. These processing of arguments, except for
.Fa flags ,
is identical to the base functions.
See
.Xr xo_emit 3
for additional information.
.Pp
The only currently defined flag is
.Dv XOEF_RETAIN .
.Nm
can retain the parsed internal information related to the given
format string, allowing subsequent
.Xr xo_emit 3
calls, the retained
information is used, avoiding repetitive parsing of the format string.
To retain parsed format information, use the
.Dv XOEF_RETAIN
flag to the
.Fn xo_emit_f
function.
.Pp
The format string must be immutable across multiple calls to
.Xn xo_emit_f ,
since the library retains the string.
Typically this is done by using
static constant strings, such as string literals. If the string is not
immutable, the
.Dv XOEF_RETAIN
flag must not be used.
.Pp
The functions
.Fn xo_retain_clear
and
.Fn xo_retain_clear_all
release internal information on either a single format string or all
format strings, respectively.
Neither is required, but the library will
retain this information until it is cleared or the process exits.
.Pp
The retained information is kept as thread-specific data.
.Pp
Use
.Fn xo_retain_clear
and
.Fn xo_retain_clear_all
to clear the retained information, clearing the retained information
for either a specific format string or all format strings, respectively.
These functions are only needed when the calling application wants to
clear this information; they are not generally needed.
.Sh EXAMPLES
.Pp
.Bd -literal -offset indent
for (i = 0; i < 1000; i++) {
xo_open_instance("item");
xo_emit_f(XOEF_RETAIN, "{:name} {:count/%d}\n",
name[i], count[i]);
}
.Ed
.Pp
In this example, the caller desires to clear the retained information.
.Bd -literal -offset indent
const char *fmt = "{:name} {:count/%d}\n";
for (i = 0; i < 1000; i++) {
xo_open_instance("item");
xo_emit_f(XOEF_RETAIN, fmt, name[i], count[i]);
}
xo_retain_clear(fmt);
.Ed
.Sh RETURN CODE
The return values for these functions is identical to those of their
traditional counterparts. See
.Xr xo_emit 3
for details.
.Sh SEE ALSO
.Xr xo_emit 3 ,
.Xr xo_open_container 3 ,
.Xr xo_open_list 3 ,
.Xr xo_format 5 ,
.Xr libxo 3

View File

@ -51,14 +51,14 @@ field descriptions within the format string.
.Pp
The field description is given as follows:
.Bd -literal -offset indent
'{' [ role | modifier ]* [',' long-names ]* ':' [ content ]
[ '/' field-format [ '/' encoding-format ]] '}'
\(aq{\(aq [ role | modifier ]* [\(aq,\(aq long\-names ]* \(aq:\(aq [ content ]
[ \(aq/\(aq field\-format [ \(aq/\(aq encoding\-format ]] \(aq}\(aq
.Ed
.Pp
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.
The contents, field\-format, and
encoding\-format are used in varying ways, based on the role.
These are described in the following sections.
.Pp
Braces can be escaped by using double braces, similar to "%%" in
@ -68,26 +68,26 @@ The format string "{{braces}}" would emit "{braces}".
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
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
.Xr xo_emit 3 ,
function as an unsigned integer.
.Bd -literal -offset indent
xo_emit("{P: }{Lwc:In stock}{:in-stock/%u}\\n", 65);
xo_emit("{P: }{Lwc:In stock}{:in\-stock/%u}\\n", 65);
.Ed
.Pp
This single line of code can generate text ("In stock: 65\\n"), XML
("<in-stock>65</in-stock>"), JSON ('"in-stock": 65'), or HTML (too
("<in\-stock>65</in\-stock>"), JSON (\(aq"in\-stock": 65\(aq), or HTML (too
lengthy to be listed here).
.Pp
While roles and modifiers typically use single character for brevity,
there are alternative names for each which allow more verbose
formatting strings.
These names must be preceded by a comma, and may follow any
single-character values:
single\-character values:
.Bd -literal -offset indent
xo_emit("{L,white,colon:In stock}{,key:in-stock/%u}\n", 65);
xo_emit("{L,white,colon:In stock}{,key:in\-stock/%u}\\n", 65);
.Ed
.Ss "Field Roles"
Field roles are optional, and indicate the role and formatting of the
@ -96,7 +96,7 @@ The roles are listed below; only one role is permitted:
.Bl -column "M" "Name12341234"
.It Sy "M" "Name " "Description"
.It C "color " "Field is a color or effect"
.It D "decoration " "Field is non-text (e.g. colon, comma)"
.It D "decoration " "Field is non\-text (e.g. colon, comma)"
.It E "error " "Field is an error message"
.It L "label " "Field is text that prefixes a value"
.It N "note " "Field is text that follows a value"
@ -105,12 +105,12 @@ The roles are listed below; only one role is permitted:
.It U "units " "Field is the units for the previous value field"
.It V "value " "Field is the name of field (the default)"
.It W "warning " "Field is a warning message"
.It \&[ "start-anchor" "Begin a section of anchored variable-width text"
.It \&] "stop-anchor " "End a section of anchored variable-width text"
.It \&[ "start\-anchor" "Begin a section of anchored variable\-width text"
.It \&] "stop\-anchor " "End a section of anchored variable\-width text"
.El
.Bd -literal -offset indent
EXAMPLE:
xo_emit("{L:Free}{D::}{P: }{:free/%u} {U:Blocks}\n",
xo_emit("{L:Free}{D::}{P: }{:free/%u} {U:Blocks}\\n",
free_blocks);
.Ed
.Pp
@ -121,50 +121,50 @@ a comma:
.Bd -literal -offset indent
EXAMPLE:
xo_emit("{,label:Free}{,decoration::}{,padding: }"
"{,value:free/%u} {,units:Blocks}\n",
"{,value:free/%u} {,units:Blocks}\\n",
free_blocks);
.Ed
.Ss "The Color Role ({C:})"
Colors and effects control how text values are displayed; they are
used for display styles (TEXT and HTML).
.Bd -literal -offset indent
xo_emit("{C:bold}{:value}{C:no-bold}\n", value);
xo_emit("{C:bold}{:value}{C:no\-bold}\\n", value);
.Ed
.Pp
Colors and effects remain in effect until modified by other "C"-role
Colors and effects remain in effect until modified by other "C"\-role
fields.
.Bd -literal -offset indent
xo_emit("{C:bold}{C:inverse}both{C:no-bold}only inverse\n");
xo_emit("{C:bold}{C:inverse}both{C:no\-bold}only inverse\\n");
.Ed
.Pp
If the content is empty, the "reset" action is performed.
.Bd -literal -offset indent
xo_emit("{C:both,underline}{:value}{C:}\n", value);
xo_emit("{C:both,underline}{:value}{C:}\\n", value);
.Ed
.Pp
The content should be a comma-separated list of zero or more colors or
The content should be a comma\-separated list of zero or more colors or
display effects.
.Bd -literal -offset indent
xo_emit("{C:bold,underline,inverse}All three{C:no-bold,no-inverse}\n");
xo_emit("{C:bold,underline,inverse}All three{C:no\-bold,no\-inverse}\\n");
.Ed
.Pp
The color content can be either static, when placed directly within
the field descriptor, or a printf-style format descriptor can be used,
the field descriptor, or a printf\-style format descriptor can be used,
if preceded by a slash ("/"):
.Bd -literal -offset indent
xo_emit("{C:/%s%s}{:value}{C:}", need_bold ? "bold" : "",
need_underline ? "underline" : "", value);
.Ed
.Pp
Color names are prefixed with either "fg-" or "bg-" to change the
Color names are prefixed with either "fg\-" or "bg\-" to change the
foreground and background colors, respectively.
.Bd -literal -offset indent
xo_emit("{C:/fg-%s,bg-%s}{Lwc:Cost}{:cost/%u}{C:reset}\n",
xo_emit("{C:/fg\-%s,bg\-%s}{Lwc:Cost}{:cost/%u}{C:reset}\\n",
fg_color, bg_color, cost);
.Ed
.Pp
The following table lists the supported effects:
.Bl -column "no-underline"
.Bl -column "no\-underline"
.It Sy "Name " "Description"
.It "bg\-xxxxx " "Change background color"
.It "bold " "Start bold text effect"
@ -179,7 +179,7 @@ The following table lists the supported effects:
.El
.Pp
The following color names are supported:
.Bl -column "no-underline"
.Bl -column "no\-underline"
.It Sy "Name"
.It black
.It blue
@ -193,7 +193,7 @@ The following color names are supported:
.El
.Ss "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
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.
@ -219,22 +219,23 @@ change such as changing "/%06d" to "/%08d" should not force hand
inspection of all .po files.
.Pp
The simplified version can be generated for a single message using the
"xopo -s <text>" command, or an entire .pot can be translated using
the "xopo -f <input> -o <output>" command.
"xopo \-s <text>" command, or an entire .pot can be translated using
the "xopo \-f <input> \-o <output>" command.
.Bd -literal -offset indent
xo_emit("{G:}Invalid token\n");
xo_emit("{G:}Invalid token\\n");
.Ed
.Pp
The {G:} role allows a domain name to be set.
.Fn gettext
calls will
continue to use that domain name until the current format string
processing is complete, enabling a library function to emit strings
using it's own catalog.
using it\(aqs own catalog.
The domain name can be either static as the
content of the field, or a format can be used to get the domain name
from the arguments.
.Bd -literal -offset indent
xo_emit("{G:libc}Service unavailable in restricted mode\n");
xo_emit("{G:libc}Service unavailable in restricted mode\\n");
.Ed
.Ss "The Label Role ({L:})"
Labels are text that appears before a value.
@ -249,7 +250,7 @@ Notes are text that appears after a value.
.Ss "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,
the field descriptor, or a printf\-style format descriptor can be used,
if preceded by a slash ("/"):
.Bd -literal -offset indent
xo_emit("{P: }{Lwc:Cost}{:cost/%u}\\n", cost);
@ -259,7 +260,7 @@ if preceded by a slash ("/"):
Titles 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,
the field descriptor, or a printf\-style format descriptor can be used,
if preceded by a slash ("/"):
.Bd -literal -offset indent
xo_emit("{T:Interface Statistics}\\n");
@ -274,7 +275,7 @@ for the previous value field.
xo_emit("{Lwc:Distance}{:distance/%u}{Uw:miles}\\n", miles);
.Ed
.Pp
Note that the sense of the 'w' modifier is reversed for units;
Note that the sense of the \(aqw\(aq modifier is reversed for units;
a blank is added before the contents, rather than after it.
.Pp
When the
@ -286,14 +287,14 @@ attribute:
<distance units="miles">50</distance>
.Ed
.Pp
Units can also be rendered in HTML as the "data-units" attribute:
Units can also be rendered in HTML as the "data\-units" attribute:
.Bd -literal -offset indent
<div class="data" data-tag="distance" data-units="miles"
data-xpath="/top/data/distance">50</div>
<div class="data" data\-tag="distance" data\-units="miles"
data\-xpath="/top/data/distance">50</div>
.Ed
.Ss "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).
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.
@ -356,16 +357,17 @@ Field modifiers are flags which modify the way content emitted for
particular output styles:
.Bl -column M "Name123456789"
.It Sy M "Name " "Description"
.It a "argument " "The content appears as a ""const char *"" argument"
.It c "colon " "A colon ("":"") is appended after the label"
.It d "display " "Only emit field for display styles (text/HTML)"
.It e "encoding " "Only emit for encoding styles (XML/JSON)"
.It h "humanize (hn) " "Format large numbers in human-readable style"
.It " " "hn-space " "Humanize: Place space between numeric and unit"
.It " " "hn-decimal " "Humanize: Add a decimal digit, if number < 10"
.It " " "hn-1000 " "Humanize: Use 1000 as divisor instead of 1024"
.It h "humanize (hn) " "Format large numbers in human\-readable style"
.It " " "hn\-space " "Humanize: Place space between numeric and unit"
.It " " "hn\-decimal " "Humanize: Add a decimal digit, if number < 10"
.It " " "hn\-1000 " "Humanize: Use 1000 as divisor instead of 1024"
.It k "key " "Field is a key, suitable for XPath predicates"
.It l "leaf-list " "Field is a leaf-list, a list of leaf values"
.It n "no-quotes " "Do not quote the field when using JSON style"
.It l "leaf\-list " "Field is a leaf\-list, a list of leaf values"
.It n "no\-quotes " "Do not quote the field when using JSON style"
.It q "quotes " "Quote the field when using JSON style"
.It t "trim " "Trim leading and trailing whitespace"
.It w "white space " "A blank ("" "") is appended after the label"
@ -373,7 +375,7 @@ particular output styles:
.Pp
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').
colon (\(aqc\(aq) and a space (\(aqw\(aq).
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.
@ -382,10 +384,31 @@ Roles and modifiers can also use more verbose names, when preceeded by
a comma.
For example, the modifier string "Lwc" (or "L,white,colon")
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').
and should be followed by a colon (\(aqc\(aq) and a space (\(aqw\(aq).
The modifier string "Vkq" (or ":key,quote") means the field has a value
role (the default role), that it is a key for the current instance,
and that the value should be quoted when encoded for JSON.
.Ss "The Argument Modifier ({a:})"
The argument modifier indicates that the content of the field
descriptor will be placed as a UTF\-8 string (const char *) argument
within the xo_emit parameters.
.Bd -literal -offset indent
EXAMPLE:
xo_emit("{La:} {a:}\\n", "Label text", "label", "value");
TEXT:
Label text value
JSON:
"label": "value"
XML:
<label>value</label>
.Ed
.Pp
The argument modifier allows field names for value fields to be passed
on the stack, avoiding the need to build a field descriptor using
.Xr snprintf 1 .
For many field roles, the argument modifier is not needed,
since those roles have specific mechanisms for arguments,
such as "{C:fg\-%s}".
.Ss "The Colon Modifier ({c:})"
The colon modifier appends a single colon to the data value:
.Bd -literal -offset indent
@ -397,7 +420,7 @@ The colon modifier appends a single colon to the data value:
.Pp
The colon modifier is only used for the TEXT and HTML output
styles.
It is commonly combined with the space modifier ('{w:}').
It is commonly combined with the space modifier (\(aq{w:}\(aq).
It is purely a convenience feature.
.Ss "The Display Modifier ({d:})"
The display modifier indicated the field should only be generated for
@ -429,39 +452,39 @@ The encoding modifier is the opposite of the display modifier, and
they are often used to give to distinct views of the underlying data.
.Ss "The Humanize Modifier ({h:})"
The humanize modifier is used to render large numbers as in a
human-readable format.
human\-readable format.
While numbers like "44470272" are completely readable to computers and
savants, humans will generally find "44M" more meaningful.
.Pp
"hn" can be used as an alias for "humanize".
.Pp
The humanize modifier only affects display styles (TEXT and HMTL).
The "no-humanize" option will block the function of the humanize modifier.
The "no\-humanize" option will block the function of the humanize modifier.
.Pp
There are a number of modifiers that affect details of humanization.
These are only available in as full names, not single characters.
The "hn-space" modifier places a space between the number and any
The "hn\-space" modifier places a space between the number and any
multiplier symbol, such as "M" or "K" (ex: "44 K").
The "hn-decimal" modifier will add a decimal point and a single tenths digit
The "hn\-decimal" modifier will add a decimal point and a single tenths digit
when the number is less than 10 (ex: "4.4K").
The "hn-1000" modifier will use 1000 as divisor instead of 1024, following the
JEDEC-standard instead of the more natural binary powers-of-two
The "hn\-1000" modifier will use 1000 as divisor instead of 1024, following the
JEDEC\-standard instead of the more natural binary powers\-of\-two
tradition.
.Bd -literal -offset indent
EXAMPLE:
xo_emit("{h:input/%u}, {h,hn-space:output/%u}, "
"{h,hn-decimal:errors/%u}, {h,hn-1000:capacity/%u}, "
"{h,hn-decimal:remaining/%u}\n",
xo_emit("{h:input/%u}, {h,hn\-space:output/%u}, "
"{h,hn\-decimal:errors/%u}, {h,hn\-1000:capacity/%u}, "
"{h,hn\-decimal:remaining/%u}\\n",
input, output, errors, capacity, remaining);
TEXT:
21, 57 K, 96M, 44M, 1.2G
.Ed
.Pp
In the HTML style, the original numeric value is rendered in the
"data-number" attribute on the <div> element:
"data\-number" attribute on the <div> element:
.Bd -literal -offset indent
<div class="data" data-tag="errors"
data-number="100663296">96M</div>
<div class="data" data\-tag="errors"
data\-number="100663296">96M</div>
.Ed
.Ss "The Gettext Modifier ({g:})"
The gettext modifier is used to translate individual fields using the
@ -476,9 +499,9 @@ translation.
In the following example, the strings "State" and "full" are passed
to
.Fn gettext
to find locale-based translated strings.
to find locale\-based translated strings.
.Bd -literal -offset indent
xo_emit("{Lgwc:State}{g:state}\n", "full");
xo_emit("{Lgwc:State}{g:state}\\n", "full");
.Ed
.Ss "The Key Modifier ({k:})"
The key modifier is used to indicate that a particular field helps
@ -499,15 +522,15 @@ Currently the key modifier is only used when generating XPath values
for the HTML output style when
.Dv XOF_XPATH
is set, but other uses are likely in the near future.
.Ss "The Leaf-List Modifier ({l:})"
The leaf-list modifier is used to distinguish lists where each
.Ss "The Leaf\-List Modifier ({l:})"
The leaf\-list modifier is used to distinguish lists where each
instance consists of only a single value. In XML, these are
rendered as single elements, where JSON renders them as arrays.
.Bd -literal -offset indent
EXAMPLE:
xo_open_list("user");
for (i = 0; i < num_users; i++) {
xo_emit("Member {l:name}\n", user[i].u_name);
xo_emit("Member {l:name}\\n", user[i].u_name);
}
xo_close_list("user");
XML:
@ -516,8 +539,8 @@ rendered as single elements, where JSON renders them as arrays.
JSON:
"user": [ "phil", "pallavi" ]
.Ed
.Ss "The No-Quotes Modifier ({n:})"
The no-quotes modifier (and its twin, the 'quotes' modifier) affect
.Ss "The No\-Quotes Modifier ({n:})"
The no\-quotes modifier (and its twin, the \(aqquotes\(aq modifier) affect
the quoting of values in the JSON output style.
JSON uses quotes for
string values, but no quotes for numeric, boolean, and null data.
@ -538,8 +561,9 @@ language settings.
The contents of the field should be the singular
and plural English values, separated by a comma:
.Bd -literal -offset indent
xo_emit("{:bytes} {Ngp:byte,bytes}\n", bytes);
xo_emit("{:bytes} {Ngp:byte,bytes}\\n", bytes);
.Ed
.Pp
The plural modifier is meant to work with the gettext modifier ({g:})
but can work independently.
.Pp
@ -554,7 +578,7 @@ function is
called to handle the heavy lifting, using the message catalog to
convert the singular and plural forms into the native language.
.Ss "The Quotes Modifier ({q:})"
The quotes modifier (and its twin, the 'no-quotes' modifier) affect
The quotes modifier (and its twin, the \(aqno-quotes\(aq modifier) affect
the quoting of values in the JSON output style.
JSON uses quotes for
string values, but no quotes for numeric, boolean, and null data.
@ -578,23 +602,23 @@ The white space modifier appends a single space to the data value:
.Pp
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 commonly combined with the colon modifier (\(aq{c:}\(aq).
It is purely a convenience feature.
.Pp
Note that the sense of the 'w' modifier is reversed for the units role
Note that the sense of the \(aqw\(aq modifier is reversed for the units role
({Uw:}); a blank is added before the contents, rather than after it.
.Ss "Field Formatting"
The field format is similar to the format string for
.Xr printf 3 .
Its use varies based on the role of the field, but generally is used to
format the field's contents.
format the field\(aqs contents.
.Pp
If the format string is not provided for a value field, it defaults
to "%s".
.Pp
Note a field definition can contain zero or more printf-style
Note a field definition can contain zero or more printf\-style
.Dq directives ,
which are sequences that start with a '%' and end with
which are sequences that start with a \(aq%\(aq and end with
one of following characters: "diouxXDOUeEfFgGaAcCsSp".
Each directive
is matched by one of more arguments to the
@ -603,54 +627,54 @@ function.
.Pp
The format string has the form:
.Bd -literal -offset indent
'%' format-modifier * format-character
\(aq%\(aq format\-modifier * format\-character
.Ed
.Pp
The format- modifier can be:
The format\- modifier can be:
.Bl -bullet
.It
a '#' character, indicating the output value should be prefixed with
a \(aq#\(aq character, indicating the output value should be prefixed with
"0x", typically to indicate a base 16 (hex) value.
.It
a minus sign ('-'), indicating the output value should be padded on
a minus sign (\(aq\-\(aq), indicating the output value should be padded on
the right instead of the left.
.It
a leading zero ('0') indicating the output value should be padded on the
left with zeroes instead of spaces (' ').
a leading zero (\(aq0\(aq) indicating the output value should be padded on the
left with zeroes instead of spaces (\(aq \(aq).
.It
one or more digits ('0' - '9') indicating the minimum width of the
one or more digits (\(aq0\(aq \- \(aq9\(aq) indicating the minimum width of the
argument.
If the width in columns of the output value is less than
the minimum width, the value will be padded to reach the minimum.
.It
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.
width for a non\-string argument.
When handling ASCII strings this
functions as the field width but for multi-byte characters, a single
functions as the field width but for multi\-byte characters, a single
character may be composed of multiple bytes.
.Xr xo_emit 3
will never dereference memory beyond the given number of bytes.
.It
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.
This modifier cannot be given for non\-string arguments.
.It
one or more 'h' characters, indicating shorter input data.
one or more \(aqh\(aq characters, indicating shorter input data.
.It
one or more 'l' characters, indicating longer input data.
one or more \(aql\(aq characters, indicating longer input data.
.It
a 'z' character, indicating a 'size_t' argument.
a \(aqz\(aq character, indicating a \(aqsize_t\(aq argument.
.It
a 't' character, indicating a 'ptrdiff_t' argument.
a \(aqt\(aq character, indicating a \(aqptrdiff_t\(aq argument.
.It
a ' ' character, indicating a space should be emitted before
a \(aq \(aq character, indicating a space should be emitted before
positive numbers.
.It
a '+' character, indicating sign should emitted before any number.
a \(aq+\(aq character, indicating sign should emitted before any number.
.El
.Pp
Note that 'q', 'D', 'O', and 'U' are considered deprecated and will be
Note that \(aqq\(aq, \(aqD\(aq, \(aqO\(aq, and \(aqU\(aq are considered deprecated and will be
removed eventually.
.Pp
The format character is described in the following table:
@ -665,22 +689,22 @@ The format character is described in the following table:
.It D "long " "base 10 (decimal)"
.It O "unsigned long " "base 8 (octal)"
.It U "unsigned long " "base 10 (decimal)"
.It e "double " "[-]d.ddde+-dd"
.It E "double " "[-]d.dddE+-dd"
.It f "double " "[-]ddd.ddd"
.It F "double " "[-]ddd.ddd"
.It g "double " "as 'e' or 'f'"
.It G "double " "as 'E' or 'F'"
.It a "double " "[-]0xh.hhhp[+-]d"
.It A "double " "[-]0Xh.hhhp[+-]d"
.It e "double " "[\-]d.ddde+\-dd"
.It E "double " "[\-]d.dddE+\-dd"
.It f "double " "[\-]ddd.ddd"
.It F "double " "[\-]ddd.ddd"
.It g "double " "as \(aqe\(aq or \(aqf\(aq"
.It G "double " "as \(aqE\(aq or \(aqF\(aq"
.It a "double " "[\-]0xh.hhhp[+\-]d"
.It A "double " "[\-]0Xh.hhhp[+\-]d"
.It c "unsigned char " "a character"
.It C "wint_t " "a character"
.It s "char * " "a UTF-8 string"
.It s "char * " "a UTF\-8 string"
.It S "wchar_t * " "a unicode/WCS string"
.It p "void * " "'%#lx'"
.It p "void * " "\(aq%#lx\(aq"
.El
.Pp
The 'h' and 'l' modifiers affect the size and treatment of the
The \(aqh\(aq and \(aql\(aq modifiers affect the size and treatment of the
argument:
.Bl -column "Mod" "d, i " "o, u, x, X "
.It Sy "Mod" "d, i " "o, u, x, X"
@ -693,27 +717,27 @@ argument:
.It "z " "size_t " "size_t"
.It "q " "quad_t " "u_quad_t"
.El
.Ss "UTF-8 and Locale Strings"
.Ss "UTF\-8 and Locale Strings"
All strings for
.Nm libxo
must be UTF-8.
must be UTF\-8.
.Nm libxo
will handle turning them
into locale-based strings for display to the user.
into locale\-based strings for display to the user.
.Pp
For strings, the 'h' and 'l' modifiers affect the interpretation of
For strings, the \(aqh\(aq and \(aql\(aq 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
The default \(aq%s\(aq string is a \(aqchar *\(aq
pointer to a string encoded as UTF\-8.
Since UTF\-8 is compatible with
.Em ASCII
data, a normal 7-bit
data, a normal 7\-bit
.Em ASCII
string can be used.
"%ls" expects a
"wchar_t *" pointer to a wide-character string, encoded as 32-bit
"wchar_t *" pointer to a wide\-character string, encoded as 32\-bit
Unicode values.
"%hs" expects a "char *" pointer to a multi-byte
"%hs" expects a "char *" pointer to a multi\-byte
string encoded with the current locale, as given by the
.Ev LC_CTYPE ,
.Ev LANG ,
@ -722,22 +746,22 @@ or
environment variables.
The first of this list of
variables is used and if none of the variables are set, the locale defaults to
.Em UTF-8 .
.Em UTF\-8 .
.Pp
.Nm 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.
convert these arguments as needed to either UTF\-8 (for XML, JSON, and
HTML styles) or locale\-based strings for display in text style.
.Bd -literal -offset indent
xo_emit("All strings are utf-8 content {:tag/%ls}",
xo_emit("All strings are utf\-8 content {:tag/%ls}",
L"except for wide strings");
.Ed
.Pp
"%S" is equivalent to "%ls".
.Pp
For example, a function is passed a locale-base name, a hat size,
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)
The hat size is formatted in a UTF\-8 (ASCII)
string, and the time value is formatted into a wchar_t string.
.Bd -literal -offset indent
void print_order (const char *name, int size,
@ -755,7 +779,7 @@ string, and the time value is formatted into a wchar_t string.
xo_emit("The hat for {:name/%hs} is {:size/%s}.\\n",
name, size_val);
xo_emit("It was ordered on {:order-time/%ls}.\\n",
xo_emit("It was ordered on {:order\-time/%ls}.\\n",
when);
}
.Ed
@ -766,11 +790,11 @@ 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.
.Pp
UTF-8 and locale-encoded strings can use multiple bytes to encode one
UTF\-8 and locale\-encoded strings can use multiple bytes to encode one
column of data.
The traditional "precision'" (aka "max-width") value
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.
@ -800,12 +824,12 @@ For HTML, these characters are placed in a <div> with class "text".
"size": "extra small"
HTML:
<div class="text">The hat is </div>
<div class="data" data-tag="size">extra small</div>
<div class="data" data\-tag="size">extra small</div>
<div class="text">.</div>
.Ed
.Ss "'%n' is Not Supported"
.Ss "\(aq%n\(aq is Not Supported"
.Nm libxo
does not support the '%n' directive.
does not support the \(aq%n\(aq directive.
It is a bad idea and we
just do not do it.
.Ss "The Encoding Format (eformat)"
@ -817,7 +841,7 @@ If the primary is not given, both default to "%s".
.Sh EXAMPLE
In this example, the value for the number of items in stock is emitted:
.Bd -literal -offset indent
xo_emit("{P: }{Lwc:In stock}{:in-stock/%u}\\n",
xo_emit("{P: }{Lwc:In stock}{:in\-stock/%u}\\n",
instock);
.Ed
.Pp
@ -826,16 +850,16 @@ This call will generate the following output:
TEXT:
In stock: 144
XML:
<in-stock>144</in-stock>
<in\-stock>144</in\-stock>
JSON:
"in-stock": 144,
"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 class="data" data\-tag="in\-stock">144</div>
</div>
.Ed
.Pp
@ -846,10 +870,10 @@ or
.Dv XOF_INFO
data, which would expand the penultimate line to:
.Bd -literal -offset indent
<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 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>
.Ed
.Sh WHAT MAKES A GOOD FIELD NAME?
To make useful, consistent field names, follow these guidelines:
@ -867,23 +891,23 @@ But the raw field name should use hyphens.
.Ss "Use full words"
Do not abbreviate especially when the abbreviation is not obvious or
not widely used.
Use "data-size", not "dsz" or "dsize".
Use "data\-size", not "dsz" or "dsize".
Use
"interface" instead of "ifname", "if-name", "iface", "if", or "intf".
.Ss "Use <verb>-<units>"
Using the form <verb>-<units> or <verb>-<classifier>-<units> helps in
"interface" instead of "ifname", "if\-name", "iface", "if", or "intf".
.Ss "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".
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".
Use "receive\-after\-window\-packets" instead of
"received\-packets\-of\-data\-after\-window".
.Ss "Reuse existing field names"
Nothing is worse than writing expressions like:
.Bd -literal -offset indent
if ($src1/process[pid == $pid]/name ==
$src2/proc-table/proc/p[process-id == $pid]/proc-name) {
$src2/proc\-table/proc/p[process\-id == $pid]/proc\-name) {
...
}
.Ed
@ -903,7 +927,7 @@ calls or "{e:}" fields to make the data useful.
.Ss "Do not use an arbitrary number postfix"
What does "errors2" mean?
No one will know.
"errors-after-restart" would be a better choice.
"errors\-after\-restart" would be a better choice.
Think of your 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 is the
@ -913,7 +937,7 @@ 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
application\(aqs 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.
@ -925,12 +949,12 @@ By choosing wise names now, you are making their lives better.
After using
.Xr xolint 1
to find errors in your field descriptors, use
.Dq "xolint -V"
.Dq "xolint \-V"
to spell check your field names and to detect different
names for the same data.
.Dq dropped-short
.Dq dropped\-short
and
.Dq dropped-too-short
.Dq dropped\-too\-short
are both reasonable names, but using them both will lead users to ask the
difference between the two fields.
If there is no difference,

View File

@ -83,12 +83,16 @@ Log (via stderr) each syslog message (via
Ignore the {h:} modifier (TEXT, HTML)
.It Dv no-locale
Do not initialize the locale setting
.It Dv no-retain
Prevent retaining formatting information
.It Dv no-top
Do not emit a top set of braces (JSON)
.It Dv not-first
Pretend the 1st output item was not 1st (JSON)
.It Dv pretty
Emit pretty-printed output
.It Dv retain
Force retaining formatting information
.It Dv text
Emit TEXT output
.If Dv underscores

View File

@ -5,7 +5,7 @@ includedir=@includedir@
Name: libxo
Version: @VERSION@
Version: @LIBXO_VERSION@
Description: The XML Output Library
Libs: @LIBXO_LIBDIR@ @LIBXO_LIBS@
Cflags: @LIBXO_INCLUDEDIR@
Libs: -L@XO_LIBDIR@ @XO_LIBS@
Cflags: -I@XO_INCLUDEDIR@

View File

@ -6,15 +6,15 @@
require 'formula'
class Libxo < Formula
homepage 'https://github.com/Juniper/@PACKAGE-NAME@'
url 'https://github.com/Juniper/@PACKAGE_NAME@/releases/@PACKAGE_VERSION@/@PACKAGE_NAME@-@PACKAGE_VERSION@.tar.gz'
homepage 'https://github.com/Juniper/@PACKAGE_NAME@'
url 'https://github.com/Juniper/@PACKAGE_NAME@/releases/download/@PACKAGE_VERSION@/@PACKAGE_NAME@-@PACKAGE_VERSION@.tar.gz'
sha1 '__SHA1__'
depends_on 'libtool' => :build
def install
system "./configure", "--disable-dependency-tracking",
system "./configure", "--disable-dependency-tracking", "--disable-silent-rules",
"--prefix=#{prefix}"
system "make install"
system "make", "install"
end
end

View File

@ -22,7 +22,8 @@ test_07.c \
test_08.c \
test_09.c \
test_10.c \
test_11.c
test_11.c \
test_12.c
test_01_test_SOURCES = test_01.c
test_02_test_SOURCES = test_02.c
@ -35,6 +36,7 @@ test_08_test_SOURCES = test_08.c
test_09_test_SOURCES = test_09.c
test_10_test_SOURCES = test_10.c
test_11_test_SOURCES = test_11.c
test_12_test_SOURCES = test_12.c
# TEST_CASES := $(shell cd ${srcdir} ; echo *.c )

View File

@ -2,6 +2,14 @@ op create: [] []
op open_container: [top] []
op string: [host] [my-box]
op string: [domain] [example.com]
op string: [host] [my-box]
op string: [domain] [example.com]
op string: [label] [value]
op string: [max-chaos] [very]
op content: [min-chaos] [42]
op string: [some-chaos] [[42]]
op string: [host] [my-box]
op string: [domain] [example.com]
op attr: [test] [value]
op open_container: [data] []
op open_list: [item] []

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,26 @@
<div class="line">
<div class="text">testing argument modifier </div>
<div class="data" data-tag="host" data-xpath="/top/host">my-box</div>
<div class="text">.</div>
<div class="data" data-tag="domain" data-xpath="/top/domain">example.com</div>
<div class="text">...</div>
</div>
<div class="line">
<div class="text">testing argument modifier with encoding to </div>
<div class="text">.</div>
<div class="data" data-tag="domain" data-xpath="/top/domain">example.com</div>
<div class="text">...</div>
</div>
<div class="line">
<div class="label">Label text</div>
<div class="text"> </div>
<div class="data" data-tag="label" data-xpath="/top/label">value</div>
</div>
<div class="line">
<div class="data" data-tag="max-chaos" data-xpath="/top/max-chaos"> very </div>
<div class="data" data-tag="min-chaos" data-xpath="/top/min-chaos">42</div>
<div class="data" data-tag="some-chaos" data-xpath="/top/some-chaos">42
</div>
<div class="text">Connecting to </div>
<div class="data" data-tag="host" data-xpath="/top/host">my-box</div>
<div class="text">.</div>

View File

@ -1,4 +1,26 @@
<div class="line">
<div class="text">testing argument modifier </div>
<div class="data" data-tag="host">my-box</div>
<div class="text">.</div>
<div class="data" data-tag="domain">example.com</div>
<div class="text">...</div>
</div>
<div class="line">
<div class="text">testing argument modifier with encoding to </div>
<div class="text">.</div>
<div class="data" data-tag="domain">example.com</div>
<div class="text">...</div>
</div>
<div class="line">
<div class="label">Label text</div>
<div class="text"> </div>
<div class="data" data-tag="label">value</div>
</div>
<div class="line">
<div class="data" data-tag="max-chaos"> very </div>
<div class="data" data-tag="min-chaos">42</div>
<div class="data" data-tag="some-chaos">42
</div>
<div class="text">Connecting to </div>
<div class="data" data-tag="host">my-box</div>
<div class="text">.</div>

View File

@ -1,2 +1,2 @@
{"top": {"host":"my-box","domain":"example.com", "data": {"item": [{"sku":"GRO-000-415","name":"gum","sold":1412,"in-stock":54,"on-order":10}, {"sku":"HRD-000-212","name":"rope","sold":85,"in-stock":4,"on-order":2}, {"sku":"HRD-000-517","name":"ladder","sold":0,"in-stock":2,"on-order":1}, {"sku":"HRD-000-632","name":"bolt","sold":4123,"in-stock":144,"on-order":42}, {"sku":"GRO-000-2331","name":"water","sold":17,"in-stock":14,"on-order":2}]}, "data2": {"item": [{"sku":"GRO-000-415","name":"gum","sold":1412.0,"in-stock":54,"on-order":10}, {"sku":"HRD-000-212","name":"rope","sold":85.0,"in-stock":4,"on-order":2}, {"sku":"HRD-000-517","name":"ladder","sold":0,"in-stock":2,"on-order":1}, {"sku":"HRD-000-632","name":"bolt","sold":4123.0,"in-stock":144,"on-order":42}, {"sku":"GRO-000-2331","name":"water","sold":17.0,"in-stock":14,"on-order":2}]}, "data3": {"item": [{"sku":"GRO-000-533","name":"fish","sold":1321.0,"in-stock":45,"on-order":1}]}, "data4": {"item": ["gum","rope","ladder","bolt","water"]},"cost":425,"cost":455,"mode":"mode","mode_octal":"octal","links":"links","user":"user","group":"group","mode":"/some/file","mode_octal":640,"links":1,"user":"user","group":"group"}
{"top": {"host":"my-box","domain":"example.com","host":"my-box","domain":"example.com","label":"value","max-chaos":"very","min-chaos":42,"some-chaos":"[42]","host":"my-box","domain":"example.com", "data": {"item": [{"sku":"GRO-000-415","name":"gum","sold":1412,"in-stock":54,"on-order":10}, {"sku":"HRD-000-212","name":"rope","sold":85,"in-stock":4,"on-order":2}, {"sku":"HRD-000-517","name":"ladder","sold":0,"in-stock":2,"on-order":1}, {"sku":"HRD-000-632","name":"bolt","sold":4123,"in-stock":144,"on-order":42}, {"sku":"GRO-000-2331","name":"water","sold":17,"in-stock":14,"on-order":2}]}, "data2": {"item": [{"sku":"GRO-000-415","name":"gum","sold":1412.0,"in-stock":54,"on-order":10}, {"sku":"HRD-000-212","name":"rope","sold":85.0,"in-stock":4,"on-order":2}, {"sku":"HRD-000-517","name":"ladder","sold":0,"in-stock":2,"on-order":1}, {"sku":"HRD-000-632","name":"bolt","sold":4123.0,"in-stock":144,"on-order":42}, {"sku":"GRO-000-2331","name":"water","sold":17.0,"in-stock":14,"on-order":2}]}, "data3": {"item": [{"sku":"GRO-000-533","name":"fish","sold":1321.0,"in-stock":45,"on-order":1}]}, "data4": {"item": ["gum","rope","ladder","bolt","water"]},"cost":425,"cost":455,"mode":"mode","mode_octal":"octal","links":"links","user":"user","group":"group","mode":"/some/file","mode_octal":640,"links":1,"user":"user","group":"group"}
}

View File

@ -1,5 +1,13 @@
{
"top": {
"host": "my-box",
"domain": "example.com",
"host": "my-box",
"domain": "example.com",
"label": "value",
"max-chaos": "very",
"min-chaos": 42,
"some-chaos": "[42]",
"host": "my-box",
"domain": "example.com",
"data": {

View File

@ -1,3 +1,7 @@
testing argument modifier my-box.example.com...
testing argument modifier with encoding to .example.com...
Label text value
very 4242
Connecting to my-box.example.com...
Item Total Sold In Stock On Order SKU
gum 1412 54 10 GRO-000-415

View File

@ -1 +1 @@
<top><host>my-box</host><domain>example.com</domain><data test="value"><item test2="value2"><sku test3="value3" key="key">GRO-000-415</sku><name key="key">gum</name><sold>1412</sold><in-stock>54</in-stock><on-order>10</on-order></item><item><sku test3="value3" key="key">HRD-000-212</sku><name key="key">rope</name><sold>85</sold><in-stock>4</in-stock><on-order>2</on-order></item><item><sku test3="value3" key="key">HRD-000-517</sku><name key="key">ladder</name><sold>0</sold><in-stock>2</in-stock><on-order>1</on-order></item><item><sku test3="value3" key="key">HRD-000-632</sku><name key="key">bolt</name><sold>4123</sold><in-stock>144</in-stock><on-order>42</on-order></item><item><sku test3="value3" key="key">GRO-000-2331</sku><name key="key">water</name><sold>17</sold><in-stock>14</in-stock><on-order>2</on-order></item></data><data2><item><sku key="key">GRO-000-415</sku><name key="key">gum</name><sold>1412.0</sold><in-stock>54</in-stock><on-order>10</on-order></item><item><sku key="key">HRD-000-212</sku><name key="key">rope</name><sold>85.0</sold><in-stock>4</in-stock><on-order>2</on-order></item><item><sku key="key">HRD-000-517</sku><name key="key">ladder</name><sold>0</sold><in-stock>2</in-stock><on-order>1</on-order></item><item><sku key="key">HRD-000-632</sku><name key="key">bolt</name><sold>4123.0</sold><in-stock>144</in-stock><on-order>42</on-order></item><item><sku key="key">GRO-000-2331</sku><name key="key">water</name><sold>17.0</sold><in-stock>14</in-stock><on-order>2</on-order></item></data2><data3><item><sku key="key">GRO-000-533</sku><name key="key">fish</name><sold>1321.0</sold><in-stock>45</in-stock><on-order>1</on-order></item></data3><data4><item test4="value4">gum</item><item test4="value4">rope</item><item test4="value4">ladder</item><item test4="value4">bolt</item><item test4="value4">water</item></data4><cost>425</cost><cost>455</cost><mode>mode</mode><mode_octal>octal</mode_octal><links>links</links><user>user</user><group>group</group><mode>/some/file</mode><mode_octal>640</mode_octal><links>1</links><user>user</user><group>group</group></top>
<top><host>my-box</host><domain>example.com</domain><host>my-box</host><domain>example.com</domain><label>value</label><max-chaos>very</max-chaos><min-chaos>42</min-chaos><some-chaos>[42]</some-chaos><host>my-box</host><domain>example.com</domain><data test="value"><item test2="value2"><sku test3="value3" key="key">GRO-000-415</sku><name key="key">gum</name><sold>1412</sold><in-stock>54</in-stock><on-order>10</on-order></item><item><sku test3="value3" key="key">HRD-000-212</sku><name key="key">rope</name><sold>85</sold><in-stock>4</in-stock><on-order>2</on-order></item><item><sku test3="value3" key="key">HRD-000-517</sku><name key="key">ladder</name><sold>0</sold><in-stock>2</in-stock><on-order>1</on-order></item><item><sku test3="value3" key="key">HRD-000-632</sku><name key="key">bolt</name><sold>4123</sold><in-stock>144</in-stock><on-order>42</on-order></item><item><sku test3="value3" key="key">GRO-000-2331</sku><name key="key">water</name><sold>17</sold><in-stock>14</in-stock><on-order>2</on-order></item></data><data2><item><sku key="key">GRO-000-415</sku><name key="key">gum</name><sold>1412.0</sold><in-stock>54</in-stock><on-order>10</on-order></item><item><sku key="key">HRD-000-212</sku><name key="key">rope</name><sold>85.0</sold><in-stock>4</in-stock><on-order>2</on-order></item><item><sku key="key">HRD-000-517</sku><name key="key">ladder</name><sold>0</sold><in-stock>2</in-stock><on-order>1</on-order></item><item><sku key="key">HRD-000-632</sku><name key="key">bolt</name><sold>4123.0</sold><in-stock>144</in-stock><on-order>42</on-order></item><item><sku key="key">GRO-000-2331</sku><name key="key">water</name><sold>17.0</sold><in-stock>14</in-stock><on-order>2</on-order></item></data2><data3><item><sku key="key">GRO-000-533</sku><name key="key">fish</name><sold>1321.0</sold><in-stock>45</in-stock><on-order>1</on-order></item></data3><data4><item test4="value4">gum</item><item test4="value4">rope</item><item test4="value4">ladder</item><item test4="value4">bolt</item><item test4="value4">water</item></data4><cost>425</cost><cost>455</cost><mode>mode</mode><mode_octal>octal</mode_octal><links>links</links><user>user</user><group>group</group><mode>/some/file</mode><mode_octal>640</mode_octal><links>1</links><user>user</user><group>group</group></top>

View File

@ -1,4 +1,12 @@
<top>
<host>my-box</host>
<domain>example.com</domain>
<host>my-box</host>
<domain>example.com</domain>
<label>value</label>
<max-chaos>very</max-chaos>
<min-chaos>42</min-chaos>
<some-chaos>[42]</some-chaos>
<host>my-box</host>
<domain>example.com</domain>
<data test="value">

View File

@ -0,0 +1,89 @@
op create: [] []
op open_container: [top] []
op open_container: [data] []
op open_list: [thing] []
op open_instance: [thing] []
op string: [name] [thing]
op string: [color] [green]
op content: [time] [2:15]
op string: [hand] [left]
op string: [color] [blue]
op content: [time] [3:45]
op close_instance: [thing] []
op open_instance: [thing] []
op string: [name] [thing]
op string: [color] [green]
op content: [time] [2:15]
op string: [hand] [left]
op string: [color] [blue]
op content: [time] [3:45]
op close_instance: [thing] []
op open_instance: [thing] []
op string: [name] [thing]
op string: [color] [green]
op content: [time] [2:15]
op string: [hand] [left]
op string: [color] [blue]
op content: [time] [3:45]
op close_instance: [thing] []
op open_instance: [thing] []
op string: [name] [thing]
op string: [color] [green]
op content: [time] [2:15]
op string: [hand] [left]
op string: [color] [blue]
op content: [time] [3:45]
op close_instance: [thing] []
op open_instance: [thing] []
op string: [name] [thing]
op string: [color] [green]
op content: [time] [2:15]
op string: [hand] [left]
op string: [color] [blue]
op content: [time] [3:45]
op close_instance: [thing] []
op open_instance: [thing] []
op string: [name] [thing]
op string: [color] [green]
op content: [time] [2:15]
op string: [hand] [left]
op string: [color] [blue]
op content: [time] [3:45]
op close_instance: [thing] []
op open_instance: [thing] []
op string: [name] [thing]
op string: [color] [green]
op content: [time] [2:15]
op string: [hand] [left]
op string: [color] [blue]
op content: [time] [3:45]
op close_instance: [thing] []
op open_instance: [thing] []
op string: [name] [thing]
op string: [color] [green]
op content: [time] [2:15]
op string: [hand] [left]
op string: [color] [blue]
op content: [time] [3:45]
op close_instance: [thing] []
op open_instance: [thing] []
op string: [name] [thing]
op string: [color] [green]
op content: [time] [2:15]
op string: [hand] [left]
op string: [color] [blue]
op content: [time] [3:45]
op close_instance: [thing] []
op open_instance: [thing] []
op string: [name] [thing]
op string: [color] [green]
op content: [time] [2:15]
op string: [hand] [left]
op string: [color] [blue]
op content: [time] [3:45]
op close_instance: [thing] []
op close_list: [thing] []
op close_container: [data] []
op close_container: [top] []
op finish: [] []
op flush: [] []

View File

@ -0,0 +1 @@
<div class="line"><div class="text">The </div><div class="data" data-tag="name">thing</div><div class="text"> is </div><div class="data" data-tag="color">green</div><div class="text"> til </div><div class="data" data-tag="time">02:15</div></div><div class="line"><div class="text">My </div><div class="data" data-tag="hand">left</div><div class="text"> hand is </div><div class="data" data-tag="color">blue</div><div class="text"> til </div><div class="data" data-tag="time">03:45</div></div><div class="line"><div class="text">The </div><div class="data" data-tag="name">thing</div><div class="text"> is </div><div class="data" data-tag="color">green</div><div class="text"> til </div><div class="data" data-tag="time">02:15</div></div><div class="line"><div class="text">My </div><div class="data" data-tag="hand">left</div><div class="text"> hand is </div><div class="data" data-tag="color">blue</div><div class="text"> til </div><div class="data" data-tag="time">03:45</div></div><div class="line"><div class="text">The </div><div class="data" data-tag="name">thing</div><div class="text"> is </div><div class="data" data-tag="color">green</div><div class="text"> til </div><div class="data" data-tag="time">02:15</div></div><div class="line"><div class="text">My </div><div class="data" data-tag="hand">left</div><div class="text"> hand is </div><div class="data" data-tag="color">blue</div><div class="text"> til </div><div class="data" data-tag="time">03:45</div></div><div class="line"><div class="text">The </div><div class="data" data-tag="name">thing</div><div class="text"> is </div><div class="data" data-tag="color">green</div><div class="text"> til </div><div class="data" data-tag="time">02:15</div></div><div class="line"><div class="text">My </div><div class="data" data-tag="hand">left</div><div class="text"> hand is </div><div class="data" data-tag="color">blue</div><div class="text"> til </div><div class="data" data-tag="time">03:45</div></div><div class="line"><div class="text">The </div><div class="data" data-tag="name">thing</div><div class="text"> is </div><div class="data" data-tag="color">green</div><div class="text"> til </div><div class="data" data-tag="time">02:15</div></div><div class="line"><div class="text">My </div><div class="data" data-tag="hand">left</div><div class="text"> hand is </div><div class="data" data-tag="color">blue</div><div class="text"> til </div><div class="data" data-tag="time">03:45</div></div><div class="line"><div class="text">The </div><div class="data" data-tag="name">thing</div><div class="text"> is </div><div class="data" data-tag="color">green</div><div class="text"> til </div><div class="data" data-tag="time">02:15</div></div><div class="line"><div class="text">My </div><div class="data" data-tag="hand">left</div><div class="text"> hand is </div><div class="data" data-tag="color">blue</div><div class="text"> til </div><div class="data" data-tag="time">03:45</div></div><div class="line"><div class="text">The </div><div class="data" data-tag="name">thing</div><div class="text"> is </div><div class="data" data-tag="color">green</div><div class="text"> til </div><div class="data" data-tag="time">02:15</div></div><div class="line"><div class="text">My </div><div class="data" data-tag="hand">left</div><div class="text"> hand is </div><div class="data" data-tag="color">blue</div><div class="text"> til </div><div class="data" data-tag="time">03:45</div></div><div class="line"><div class="text">The </div><div class="data" data-tag="name">thing</div><div class="text"> is </div><div class="data" data-tag="color">green</div><div class="text"> til </div><div class="data" data-tag="time">02:15</div></div><div class="line"><div class="text">My </div><div class="data" data-tag="hand">left</div><div class="text"> hand is </div><div class="data" data-tag="color">blue</div><div class="text"> til </div><div class="data" data-tag="time">03:45</div></div><div class="line"><div class="text">The </div><div class="data" data-tag="name">thing</div><div class="text"> is </div><div class="data" data-tag="color">green</div><div class="text"> til </div><div class="data" data-tag="time">02:15</div></div><div class="line"><div class="text">My </div><div class="data" data-tag="hand">left</div><div class="text"> hand is </div><div class="data" data-tag="color">blue</div><div class="text"> til </div><div class="data" data-tag="time">03:45</div></div><div class="line"><div class="text">The </div><div class="data" data-tag="name">thing</div><div class="text"> is </div><div class="data" data-tag="color">green</div><div class="text"> til </div><div class="data" data-tag="time">02:15</div></div><div class="line"><div class="text">My </div><div class="data" data-tag="hand">left</div><div class="text"> hand is </div><div class="data" data-tag="color">blue</div><div class="text"> til </div><div class="data" data-tag="time">03:45</div></div>

View File

@ -0,0 +1,160 @@
<div class="line">
<div class="text">The </div>
<div class="data" data-tag="name" data-xpath="/top/data/thing/name">thing</div>
<div class="text"> is </div>
<div class="data" data-tag="color" data-xpath="/top/data/thing[name = 'thing']/color">green</div>
<div class="text"> til </div>
<div class="data" data-tag="time" data-xpath="/top/data/thing[name = 'thing']/time">02:15</div>
</div>
<div class="line">
<div class="text">My </div>
<div class="data" data-tag="hand" data-xpath="/top/data/thing[name = 'thing']/hand">left</div>
<div class="text"> hand is </div>
<div class="data" data-tag="color" data-xpath="/top/data/thing[name = 'thing']/color">blue</div>
<div class="text"> til </div>
<div class="data" data-tag="time" data-xpath="/top/data/thing[name = 'thing']/time">03:45</div>
</div>
<div class="line">
<div class="text">The </div>
<div class="data" data-tag="name" data-xpath="/top/data/thing/name">thing</div>
<div class="text"> is </div>
<div class="data" data-tag="color" data-xpath="/top/data/thing[name = 'thing']/color">green</div>
<div class="text"> til </div>
<div class="data" data-tag="time" data-xpath="/top/data/thing[name = 'thing']/time">02:15</div>
</div>
<div class="line">
<div class="text">My </div>
<div class="data" data-tag="hand" data-xpath="/top/data/thing[name = 'thing']/hand">left</div>
<div class="text"> hand is </div>
<div class="data" data-tag="color" data-xpath="/top/data/thing[name = 'thing']/color">blue</div>
<div class="text"> til </div>
<div class="data" data-tag="time" data-xpath="/top/data/thing[name = 'thing']/time">03:45</div>
</div>
<div class="line">
<div class="text">The </div>
<div class="data" data-tag="name" data-xpath="/top/data/thing/name">thing</div>
<div class="text"> is </div>
<div class="data" data-tag="color" data-xpath="/top/data/thing[name = 'thing']/color">green</div>
<div class="text"> til </div>
<div class="data" data-tag="time" data-xpath="/top/data/thing[name = 'thing']/time">02:15</div>
</div>
<div class="line">
<div class="text">My </div>
<div class="data" data-tag="hand" data-xpath="/top/data/thing[name = 'thing']/hand">left</div>
<div class="text"> hand is </div>
<div class="data" data-tag="color" data-xpath="/top/data/thing[name = 'thing']/color">blue</div>
<div class="text"> til </div>
<div class="data" data-tag="time" data-xpath="/top/data/thing[name = 'thing']/time">03:45</div>
</div>
<div class="line">
<div class="text">The </div>
<div class="data" data-tag="name" data-xpath="/top/data/thing/name">thing</div>
<div class="text"> is </div>
<div class="data" data-tag="color" data-xpath="/top/data/thing[name = 'thing']/color">green</div>
<div class="text"> til </div>
<div class="data" data-tag="time" data-xpath="/top/data/thing[name = 'thing']/time">02:15</div>
</div>
<div class="line">
<div class="text">My </div>
<div class="data" data-tag="hand" data-xpath="/top/data/thing[name = 'thing']/hand">left</div>
<div class="text"> hand is </div>
<div class="data" data-tag="color" data-xpath="/top/data/thing[name = 'thing']/color">blue</div>
<div class="text"> til </div>
<div class="data" data-tag="time" data-xpath="/top/data/thing[name = 'thing']/time">03:45</div>
</div>
<div class="line">
<div class="text">The </div>
<div class="data" data-tag="name" data-xpath="/top/data/thing/name">thing</div>
<div class="text"> is </div>
<div class="data" data-tag="color" data-xpath="/top/data/thing[name = 'thing']/color">green</div>
<div class="text"> til </div>
<div class="data" data-tag="time" data-xpath="/top/data/thing[name = 'thing']/time">02:15</div>
</div>
<div class="line">
<div class="text">My </div>
<div class="data" data-tag="hand" data-xpath="/top/data/thing[name = 'thing']/hand">left</div>
<div class="text"> hand is </div>
<div class="data" data-tag="color" data-xpath="/top/data/thing[name = 'thing']/color">blue</div>
<div class="text"> til </div>
<div class="data" data-tag="time" data-xpath="/top/data/thing[name = 'thing']/time">03:45</div>
</div>
<div class="line">
<div class="text">The </div>
<div class="data" data-tag="name" data-xpath="/top/data/thing/name">thing</div>
<div class="text"> is </div>
<div class="data" data-tag="color" data-xpath="/top/data/thing[name = 'thing']/color">green</div>
<div class="text"> til </div>
<div class="data" data-tag="time" data-xpath="/top/data/thing[name = 'thing']/time">02:15</div>
</div>
<div class="line">
<div class="text">My </div>
<div class="data" data-tag="hand" data-xpath="/top/data/thing[name = 'thing']/hand">left</div>
<div class="text"> hand is </div>
<div class="data" data-tag="color" data-xpath="/top/data/thing[name = 'thing']/color">blue</div>
<div class="text"> til </div>
<div class="data" data-tag="time" data-xpath="/top/data/thing[name = 'thing']/time">03:45</div>
</div>
<div class="line">
<div class="text">The </div>
<div class="data" data-tag="name" data-xpath="/top/data/thing/name">thing</div>
<div class="text"> is </div>
<div class="data" data-tag="color" data-xpath="/top/data/thing[name = 'thing']/color">green</div>
<div class="text"> til </div>
<div class="data" data-tag="time" data-xpath="/top/data/thing[name = 'thing']/time">02:15</div>
</div>
<div class="line">
<div class="text">My </div>
<div class="data" data-tag="hand" data-xpath="/top/data/thing[name = 'thing']/hand">left</div>
<div class="text"> hand is </div>
<div class="data" data-tag="color" data-xpath="/top/data/thing[name = 'thing']/color">blue</div>
<div class="text"> til </div>
<div class="data" data-tag="time" data-xpath="/top/data/thing[name = 'thing']/time">03:45</div>
</div>
<div class="line">
<div class="text">The </div>
<div class="data" data-tag="name" data-xpath="/top/data/thing/name">thing</div>
<div class="text"> is </div>
<div class="data" data-tag="color" data-xpath="/top/data/thing[name = 'thing']/color">green</div>
<div class="text"> til </div>
<div class="data" data-tag="time" data-xpath="/top/data/thing[name = 'thing']/time">02:15</div>
</div>
<div class="line">
<div class="text">My </div>
<div class="data" data-tag="hand" data-xpath="/top/data/thing[name = 'thing']/hand">left</div>
<div class="text"> hand is </div>
<div class="data" data-tag="color" data-xpath="/top/data/thing[name = 'thing']/color">blue</div>
<div class="text"> til </div>
<div class="data" data-tag="time" data-xpath="/top/data/thing[name = 'thing']/time">03:45</div>
</div>
<div class="line">
<div class="text">The </div>
<div class="data" data-tag="name" data-xpath="/top/data/thing/name">thing</div>
<div class="text"> is </div>
<div class="data" data-tag="color" data-xpath="/top/data/thing[name = 'thing']/color">green</div>
<div class="text"> til </div>
<div class="data" data-tag="time" data-xpath="/top/data/thing[name = 'thing']/time">02:15</div>
</div>
<div class="line">
<div class="text">My </div>
<div class="data" data-tag="hand" data-xpath="/top/data/thing[name = 'thing']/hand">left</div>
<div class="text"> hand is </div>
<div class="data" data-tag="color" data-xpath="/top/data/thing[name = 'thing']/color">blue</div>
<div class="text"> til </div>
<div class="data" data-tag="time" data-xpath="/top/data/thing[name = 'thing']/time">03:45</div>
</div>
<div class="line">
<div class="text">The </div>
<div class="data" data-tag="name" data-xpath="/top/data/thing/name">thing</div>
<div class="text"> is </div>
<div class="data" data-tag="color" data-xpath="/top/data/thing[name = 'thing']/color">green</div>
<div class="text"> til </div>
<div class="data" data-tag="time" data-xpath="/top/data/thing[name = 'thing']/time">02:15</div>
</div>
<div class="line">
<div class="text">My </div>
<div class="data" data-tag="hand" data-xpath="/top/data/thing[name = 'thing']/hand">left</div>
<div class="text"> hand is </div>
<div class="data" data-tag="color" data-xpath="/top/data/thing[name = 'thing']/color">blue</div>
<div class="text"> til </div>
<div class="data" data-tag="time" data-xpath="/top/data/thing[name = 'thing']/time">03:45</div>
</div>

View File

@ -0,0 +1,160 @@
<div class="line">
<div class="text">The </div>
<div class="data" data-tag="name">thing</div>
<div class="text"> is </div>
<div class="data" data-tag="color">green</div>
<div class="text"> til </div>
<div class="data" data-tag="time">02:15</div>
</div>
<div class="line">
<div class="text">My </div>
<div class="data" data-tag="hand">left</div>
<div class="text"> hand is </div>
<div class="data" data-tag="color">blue</div>
<div class="text"> til </div>
<div class="data" data-tag="time">03:45</div>
</div>
<div class="line">
<div class="text">The </div>
<div class="data" data-tag="name">thing</div>
<div class="text"> is </div>
<div class="data" data-tag="color">green</div>
<div class="text"> til </div>
<div class="data" data-tag="time">02:15</div>
</div>
<div class="line">
<div class="text">My </div>
<div class="data" data-tag="hand">left</div>
<div class="text"> hand is </div>
<div class="data" data-tag="color">blue</div>
<div class="text"> til </div>
<div class="data" data-tag="time">03:45</div>
</div>
<div class="line">
<div class="text">The </div>
<div class="data" data-tag="name">thing</div>
<div class="text"> is </div>
<div class="data" data-tag="color">green</div>
<div class="text"> til </div>
<div class="data" data-tag="time">02:15</div>
</div>
<div class="line">
<div class="text">My </div>
<div class="data" data-tag="hand">left</div>
<div class="text"> hand is </div>
<div class="data" data-tag="color">blue</div>
<div class="text"> til </div>
<div class="data" data-tag="time">03:45</div>
</div>
<div class="line">
<div class="text">The </div>
<div class="data" data-tag="name">thing</div>
<div class="text"> is </div>
<div class="data" data-tag="color">green</div>
<div class="text"> til </div>
<div class="data" data-tag="time">02:15</div>
</div>
<div class="line">
<div class="text">My </div>
<div class="data" data-tag="hand">left</div>
<div class="text"> hand is </div>
<div class="data" data-tag="color">blue</div>
<div class="text"> til </div>
<div class="data" data-tag="time">03:45</div>
</div>
<div class="line">
<div class="text">The </div>
<div class="data" data-tag="name">thing</div>
<div class="text"> is </div>
<div class="data" data-tag="color">green</div>
<div class="text"> til </div>
<div class="data" data-tag="time">02:15</div>
</div>
<div class="line">
<div class="text">My </div>
<div class="data" data-tag="hand">left</div>
<div class="text"> hand is </div>
<div class="data" data-tag="color">blue</div>
<div class="text"> til </div>
<div class="data" data-tag="time">03:45</div>
</div>
<div class="line">
<div class="text">The </div>
<div class="data" data-tag="name">thing</div>
<div class="text"> is </div>
<div class="data" data-tag="color">green</div>
<div class="text"> til </div>
<div class="data" data-tag="time">02:15</div>
</div>
<div class="line">
<div class="text">My </div>
<div class="data" data-tag="hand">left</div>
<div class="text"> hand is </div>
<div class="data" data-tag="color">blue</div>
<div class="text"> til </div>
<div class="data" data-tag="time">03:45</div>
</div>
<div class="line">
<div class="text">The </div>
<div class="data" data-tag="name">thing</div>
<div class="text"> is </div>
<div class="data" data-tag="color">green</div>
<div class="text"> til </div>
<div class="data" data-tag="time">02:15</div>
</div>
<div class="line">
<div class="text">My </div>
<div class="data" data-tag="hand">left</div>
<div class="text"> hand is </div>
<div class="data" data-tag="color">blue</div>
<div class="text"> til </div>
<div class="data" data-tag="time">03:45</div>
</div>
<div class="line">
<div class="text">The </div>
<div class="data" data-tag="name">thing</div>
<div class="text"> is </div>
<div class="data" data-tag="color">green</div>
<div class="text"> til </div>
<div class="data" data-tag="time">02:15</div>
</div>
<div class="line">
<div class="text">My </div>
<div class="data" data-tag="hand">left</div>
<div class="text"> hand is </div>
<div class="data" data-tag="color">blue</div>
<div class="text"> til </div>
<div class="data" data-tag="time">03:45</div>
</div>
<div class="line">
<div class="text">The </div>
<div class="data" data-tag="name">thing</div>
<div class="text"> is </div>
<div class="data" data-tag="color">green</div>
<div class="text"> til </div>
<div class="data" data-tag="time">02:15</div>
</div>
<div class="line">
<div class="text">My </div>
<div class="data" data-tag="hand">left</div>
<div class="text"> hand is </div>
<div class="data" data-tag="color">blue</div>
<div class="text"> til </div>
<div class="data" data-tag="time">03:45</div>
</div>
<div class="line">
<div class="text">The </div>
<div class="data" data-tag="name">thing</div>
<div class="text"> is </div>
<div class="data" data-tag="color">green</div>
<div class="text"> til </div>
<div class="data" data-tag="time">02:15</div>
</div>
<div class="line">
<div class="text">My </div>
<div class="data" data-tag="hand">left</div>
<div class="text"> hand is </div>
<div class="data" data-tag="color">blue</div>
<div class="text"> til </div>
<div class="data" data-tag="time">03:45</div>
</div>

View File

@ -0,0 +1,2 @@
{"top": {"data": {"thing": [{"name":"thing","color":"green","time":2:15,"hand":"left","color":"blue","time":3:45}, {"name":"thing","color":"green","time":2:15,"hand":"left","color":"blue","time":3:45}, {"name":"thing","color":"green","time":2:15,"hand":"left","color":"blue","time":3:45}, {"name":"thing","color":"green","time":2:15,"hand":"left","color":"blue","time":3:45}, {"name":"thing","color":"green","time":2:15,"hand":"left","color":"blue","time":3:45}, {"name":"thing","color":"green","time":2:15,"hand":"left","color":"blue","time":3:45}, {"name":"thing","color":"green","time":2:15,"hand":"left","color":"blue","time":3:45}, {"name":"thing","color":"green","time":2:15,"hand":"left","color":"blue","time":3:45}, {"name":"thing","color":"green","time":2:15,"hand":"left","color":"blue","time":3:45}, {"name":"thing","color":"green","time":2:15,"hand":"left","color":"blue","time":3:45}]}}
}

View File

@ -0,0 +1,88 @@
{
"top": {
"data": {
"thing": [
{
"name": "thing",
"color": "green",
"time": 2:15,
"hand": "left",
"color": "blue",
"time": 3:45
},
{
"name": "thing",
"color": "green",
"time": 2:15,
"hand": "left",
"color": "blue",
"time": 3:45
},
{
"name": "thing",
"color": "green",
"time": 2:15,
"hand": "left",
"color": "blue",
"time": 3:45
},
{
"name": "thing",
"color": "green",
"time": 2:15,
"hand": "left",
"color": "blue",
"time": 3:45
},
{
"name": "thing",
"color": "green",
"time": 2:15,
"hand": "left",
"color": "blue",
"time": 3:45
},
{
"name": "thing",
"color": "green",
"time": 2:15,
"hand": "left",
"color": "blue",
"time": 3:45
},
{
"name": "thing",
"color": "green",
"time": 2:15,
"hand": "left",
"color": "blue",
"time": 3:45
},
{
"name": "thing",
"color": "green",
"time": 2:15,
"hand": "left",
"color": "blue",
"time": 3:45
},
{
"name": "thing",
"color": "green",
"time": 2:15,
"hand": "left",
"color": "blue",
"time": 3:45
},
{
"name": "thing",
"color": "green",
"time": 2:15,
"hand": "left",
"color": "blue",
"time": 3:45
}
]
}
}
}

View File

@ -0,0 +1,20 @@
The thing is green til 02:15
My left hand is blue til 03:45
The thing is green til 02:15
My left hand is blue til 03:45
The thing is green til 02:15
My left hand is blue til 03:45
The thing is green til 02:15
My left hand is blue til 03:45
The thing is green til 02:15
My left hand is blue til 03:45
The thing is green til 02:15
My left hand is blue til 03:45
The thing is green til 02:15
My left hand is blue til 03:45
The thing is green til 02:15
My left hand is blue til 03:45
The thing is green til 02:15
My left hand is blue til 03:45
The thing is green til 02:15
My left hand is blue til 03:45

View File

@ -0,0 +1 @@
<top><data><thing><name>thing</name><color>green</color><time>2:15</time><hand>left</hand><color>blue</color><time>3:45</time></thing><thing><name>thing</name><color>green</color><time>2:15</time><hand>left</hand><color>blue</color><time>3:45</time></thing><thing><name>thing</name><color>green</color><time>2:15</time><hand>left</hand><color>blue</color><time>3:45</time></thing><thing><name>thing</name><color>green</color><time>2:15</time><hand>left</hand><color>blue</color><time>3:45</time></thing><thing><name>thing</name><color>green</color><time>2:15</time><hand>left</hand><color>blue</color><time>3:45</time></thing><thing><name>thing</name><color>green</color><time>2:15</time><hand>left</hand><color>blue</color><time>3:45</time></thing><thing><name>thing</name><color>green</color><time>2:15</time><hand>left</hand><color>blue</color><time>3:45</time></thing><thing><name>thing</name><color>green</color><time>2:15</time><hand>left</hand><color>blue</color><time>3:45</time></thing><thing><name>thing</name><color>green</color><time>2:15</time><hand>left</hand><color>blue</color><time>3:45</time></thing><thing><name>thing</name><color>green</color><time>2:15</time><hand>left</hand><color>blue</color><time>3:45</time></thing></data></top>

View File

@ -0,0 +1,84 @@
<top>
<data>
<thing>
<name>thing</name>
<color>green</color>
<time>2:15</time>
<hand>left</hand>
<color>blue</color>
<time>3:45</time>
</thing>
<thing>
<name>thing</name>
<color>green</color>
<time>2:15</time>
<hand>left</hand>
<color>blue</color>
<time>3:45</time>
</thing>
<thing>
<name>thing</name>
<color>green</color>
<time>2:15</time>
<hand>left</hand>
<color>blue</color>
<time>3:45</time>
</thing>
<thing>
<name>thing</name>
<color>green</color>
<time>2:15</time>
<hand>left</hand>
<color>blue</color>
<time>3:45</time>
</thing>
<thing>
<name>thing</name>
<color>green</color>
<time>2:15</time>
<hand>left</hand>
<color>blue</color>
<time>3:45</time>
</thing>
<thing>
<name>thing</name>
<color>green</color>
<time>2:15</time>
<hand>left</hand>
<color>blue</color>
<time>3:45</time>
</thing>
<thing>
<name>thing</name>
<color>green</color>
<time>2:15</time>
<hand>left</hand>
<color>blue</color>
<time>3:45</time>
</thing>
<thing>
<name>thing</name>
<color>green</color>
<time>2:15</time>
<hand>left</hand>
<color>blue</color>
<time>3:45</time>
</thing>
<thing>
<name>thing</name>
<color>green</color>
<time>2:15</time>
<hand>left</hand>
<color>blue</color>
<time>3:45</time>
</thing>
<thing>
<name>thing</name>
<color>green</color>
<time>2:15</time>
<hand>left</hand>
<color>blue</color>
<time>3:45</time>
</thing>
</data>
</top>

View File

@ -79,6 +79,18 @@ main (int argc, char **argv)
xo_open_container_h(NULL, "top");
xo_emit("testing argument modifier {a:}.{a:}...\n",
"host", "my-box", "domain", "example.com");
xo_emit("testing argument modifier with encoding to {ea:}.{a:}...\n",
"host", "my-box", "domain", "example.com");
xo_emit("{La:} {a:}\n", "Label text", "label", "value");
xo_emit_field("Vt", "max-chaos", NULL, NULL, " very ");
xo_emit_field("V", "min-chaos", "%d", NULL, 42);
xo_emit_field("V", "some-chaos", "%d\n", "[%d]", 42);
xo_emit("Connecting to {:host}.{:domain}...\n", "my-box", "example.com");
xo_attr("test", "value");

View File

@ -42,6 +42,7 @@ main (int argc, char **argv)
}
xo_set_flags(NULL, XOF_UNITS); /* Always test w/ this */
xo_set_file(stdout);
xo_open_container_h(NULL, "top");

View File

@ -0,0 +1,76 @@
/*
* 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 otherwise
* using the SOFTWARE, you agree to be bound by the terms of that
* LICENSE.
* Phil Shafer, July 2014
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include "xo_config.h"
#include "xo.h"
int
main (int argc, char **argv)
{
int i, count = 10;
int mon = 0;
xo_emit_flags_t flags = XOEF_RETAIN;
argc = xo_parse_args(argc, argv);
if (argc < 0)
return 1;
for (argc = 1; argv[argc]; argc++) {
if (strcmp(argv[argc], "xml") == 0)
xo_set_style(NULL, XO_STYLE_XML);
else if (strcmp(argv[argc], "json") == 0)
xo_set_style(NULL, XO_STYLE_JSON);
else if (strcmp(argv[argc], "text") == 0)
xo_set_style(NULL, XO_STYLE_TEXT);
else if (strcmp(argv[argc], "html") == 0)
xo_set_style(NULL, XO_STYLE_HTML);
else if (strcmp(argv[argc], "pretty") == 0)
xo_set_flags(NULL, XOF_PRETTY);
else if (strcmp(argv[argc], "xpath") == 0)
xo_set_flags(NULL, XOF_XPATH);
else if (strcmp(argv[argc], "info") == 0)
xo_set_flags(NULL, XOF_INFO);
else if (strcmp(argv[argc], "no-retain") == 0)
flags &= ~XOEF_RETAIN;
else if (strcmp(argv[argc], "big") == 0) {
if (argv[argc + 1])
count = atoi(argv[++argc]);
}
}
xo_set_flags(NULL, XOF_UNITS); /* Always test w/ this */
xo_set_file(stdout);
xo_open_container("top");
xo_open_container("data");
const char *fmt1 = "The {C:fg-red}{k:name}{C:reset} is "
"{C:/fg-%s}{:color}{C:reset} til {:time/%02d:%02d}\n";
const char *fmt2 = "My {C:fg-red}{:hand}{C:reset} hand is "
"{C:/fg-%s}{:color}{C:reset} til {:time/%02d:%02d}\n";
for (i = 0; i < count; i++) {
xo_open_instance("thing");
xo_emit_f(flags, fmt1, "thing", "green", "green", 2, 15);
xo_emit_f(flags, fmt2, "left", "blue", "blue", 3, 45);
}
xo_close_container("data");
xo_close_container_h(NULL, "top");
xo_finish();
return 0;
}

View File

@ -26,7 +26,7 @@ int
main (int argc, char **argv)
{
static char domainname[] = "gt_01";
char path[MAXPATHLEN];
static char path[MAXPATHLEN];
const char *tzone = "EST";
const char *lang = "pig_latin";

View File

@ -3,10 +3,10 @@
.Fx
uses
.Nm libxo
version 0.4.7.
version 0.6.1.
Complete documentation can be found on github:
.Bd -literal -offset indent
http://juniper.github.io/libxo/0.4.7/libxo\-manual.html
http://juniper.github.io/libxo/0.6.1/libxo\-manual.html
.Ed
.Pp
.Nm libxo

View File

@ -88,6 +88,9 @@
/* Define to 1 if you have the <memory.h> header file. */
#define HAVE_MEMORY_H 1
/* Define to 1 if you have the <monitor.h> header file. */
/* #undef HAVE_MONITOR_H */
/* Support printflike */
/* #undef HAVE_PRINTFLIKE */
@ -180,16 +183,16 @@
/* #undef LIBXO_TEXT_ONLY */
/* Version number as dotted value */
#define LIBXO_VERSION "0.4.7"
#define LIBXO_VERSION "0.6.1"
/* Version number extra information */
#define LIBXO_VERSION_EXTRA ""
/* Version number as a number */
#define LIBXO_VERSION_NUMBER 4007
#define LIBXO_VERSION_NUMBER 6001
/* Version number as string */
#define LIBXO_VERSION_STRING "4007"
#define LIBXO_VERSION_STRING "6001"
/* Enable local wcwidth implementation */
#define LIBXO_WCWIDTH 1
@ -207,7 +210,7 @@
#define PACKAGE_NAME "libxo"
/* Define to the full name and version of this package. */
#define PACKAGE_STRING "libxo 0.4.7"
#define PACKAGE_STRING "libxo 0.6.1"
/* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME "libxo"
@ -216,7 +219,7 @@
#define PACKAGE_URL ""
/* Define to the version of this package. */
#define PACKAGE_VERSION "0.4.7"
#define PACKAGE_VERSION "0.6.1"
/* If using the C implementation of alloca, define if you know the
direction of stack growth for your system; otherwise it will be
@ -230,7 +233,10 @@
#define STDC_HEADERS 1
/* Version number of package */
#define VERSION "0.4.7"
#define VERSION "0.6.1"
/* Retain hash bucket size */
/* #undef XO_RETAIN_SIZE */
/* Define to `__inline__' or `__inline' if that's what the C compiler
calls it, or to nothing if 'inline' is not supported under any name. */