aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhil Shafer <phil@FreeBSD.org>2019-04-03 21:47:19 +0000
committerPhil Shafer <phil@FreeBSD.org>2019-04-03 21:47:19 +0000
commit8daa90e98c625e7920bce78cf8d29bc133b64f54 (patch)
treebec778aa9df8893259222c935c7744c51bf21a94
parentb7a4d12840ce37071dcb3d02a60d63721eae59ad (diff)
Import libxo 1.0.2vendor/Juniper/libxo/1.0.2
Notes
Notes: svn path=/vendor/Juniper/libxo/dist/; revision=345856 svn path=/vendor/Juniper/libxo/1.0.2/; revision=345857; tag=vendor/Juniper/libxo/1.0.2
-rw-r--r--configure.ac2
-rw-r--r--doc/api.rst32
-rw-r--r--doc/libxo-manual.html4
-rw-r--r--doc/xo.rst88
-rw-r--r--libxo/Makefile.am1
-rw-r--r--libxo/libxo.c345
-rw-r--r--libxo/xo.h38
-rw-r--r--libxo/xo_attr.36
-rw-r--r--libxo/xo_buf.h7
-rw-r--r--libxo/xo_emit.36
-rw-r--r--libxo/xo_emit_f.36
-rw-r--r--libxo/xo_explicit.h61
-rw-r--r--libxo/xo_finish.34
-rw-r--r--libxo/xo_flush.34
-rw-r--r--libxo/xo_open_container.316
-rw-r--r--libxo/xo_open_list.332
-rw-r--r--libxo/xo_open_marker.38
-rw-r--r--libxo/xo_set_writer.32
-rw-r--r--tests/core/saved/test_01.J.out3
-rw-r--r--tests/core/saved/test_02.J.out3
-rw-r--r--tests/core/saved/test_03.J.out3
-rw-r--r--tests/core/saved/test_04.J.out3
-rw-r--r--tests/core/saved/test_05.J.out4
-rw-r--r--tests/core/saved/test_05.JP.out3
-rw-r--r--tests/core/saved/test_06.J.out3
-rw-r--r--tests/core/saved/test_07.J.out3
-rw-r--r--tests/core/saved/test_08.J.out3
-rw-r--r--tests/core/saved/test_09.J.out3
-rw-r--r--tests/core/saved/test_10.J.out3
-rw-r--r--tests/core/saved/test_11.J.out3
-rw-r--r--tests/core/saved/test_12.E.err4
-rw-r--r--tests/core/saved/test_12.E.out4
-rw-r--r--tests/core/saved/test_12.H.err4
-rw-r--r--tests/core/saved/test_12.H.out2
-rw-r--r--tests/core/saved/test_12.HIPx.err4
-rw-r--r--tests/core/saved/test_12.HIPx.out6
-rw-r--r--tests/core/saved/test_12.HP.err4
-rw-r--r--tests/core/saved/test_12.HP.out6
-rw-r--r--tests/core/saved/test_12.J.err4
-rw-r--r--tests/core/saved/test_12.J.out3
-rw-r--r--tests/core/saved/test_12.JP.err4
-rw-r--r--tests/core/saved/test_12.JP.out6
-rw-r--r--tests/core/saved/test_12.T.err4
-rw-r--r--tests/core/saved/test_12.T.out1
-rw-r--r--tests/core/saved/test_12.X.err4
-rw-r--r--tests/core/saved/test_12.X.out2
-rw-r--r--tests/core/saved/test_12.XP.err4
-rw-r--r--tests/core/saved/test_12.XP.out4
-rw-r--r--tests/core/test_12.c5
-rw-r--r--tests/gettext/po/pig_latin/strerror.po4
-rw-r--r--tests/gettext/saved/gt_01.J.out3
-rw-r--r--tests/gettext/strerror.pot4
-rw-r--r--tests/xo/Makefile.am10
-rw-r--r--tests/xo/saved/xo_01.H.out2
-rw-r--r--tests/xo/saved/xo_01.HIPx.out4
-rw-r--r--tests/xo/saved/xo_01.HP.out4
-rw-r--r--tests/xo/saved/xo_01.J.out2
-rw-r--r--tests/xo/saved/xo_01.JP.out2
-rw-r--r--tests/xo/saved/xo_01.T.out1
-rw-r--r--tests/xo/saved/xo_02.H.err25
-rw-r--r--tests/xo/saved/xo_02.H.out1
-rw-r--r--tests/xo/saved/xo_02.HIPx.err25
-rw-r--r--tests/xo/saved/xo_02.HIPx.out83
-rw-r--r--tests/xo/saved/xo_02.HP.err25
-rw-r--r--tests/xo/saved/xo_02.HP.out83
-rw-r--r--tests/xo/saved/xo_02.J.err25
-rw-r--r--tests/xo/saved/xo_02.J.out7
-rw-r--r--tests/xo/saved/xo_02.JP.err25
-rw-r--r--tests/xo/saved/xo_02.JP.out84
-rw-r--r--tests/xo/saved/xo_02.T.err25
-rw-r--r--tests/xo/saved/xo_02.T.out14
-rw-r--r--tests/xo/saved/xo_02.X.err25
-rw-r--r--tests/xo/saved/xo_02.X.out1
-rw-r--r--tests/xo/saved/xo_02.XP.err25
-rw-r--r--tests/xo/saved/xo_02.XP.out74
-rwxr-xr-xtests/xo/xo_01.sh3
-rwxr-xr-xtests/xo/xo_02.sh57
-rw-r--r--xo/xo.111
-rw-r--r--xo/xo.c120
79 files changed, 1295 insertions, 253 deletions
diff --git a/configure.ac b/configure.ac
index 56b33c4c5e30..03affa949211 100644
--- a/configure.ac
+++ b/configure.ac
@@ -12,7 +12,7 @@
#
AC_PREREQ(2.2)
-AC_INIT([libxo], [0.9.0], [phil@juniper.net])
+AC_INIT([libxo], [1.0.2], [phil@juniper.net])
AM_INIT_AUTOMAKE([-Wall -Werror foreign -Wno-portability])
# Support silent build rules. Requires at least automake-1.11.
diff --git a/doc/api.rst b/doc/api.rst
index 98df01c99edb..ef5f985a8162 100644
--- a/doc/api.rst
+++ b/doc/api.rst
@@ -400,28 +400,28 @@ 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`.
-.. c:function:: int xo_emit (const char *fmt, ...)
+.. c:function:: xo_ssize_t xo_emit (const char *fmt, ...)
:param fmt: The format string, followed by zero or more arguments
:returns: If XOF_COLUMNS is set, the number of columns used; otherwise the number of bytes emitted
- :rtype: int
+ :rtype: xo_ssize_t
-.. c:function:: int xo_emit_h (xo_handle_t *xop, const char *fmt, ...)
+.. c:function:: xo_ssize_t xo_emit_h (xo_handle_t *xop, const char *fmt, ...)
:param xop: Handle for modify (or NULL for default handle)
:type xop: xo_handle_t \*
:param fmt: The format string, followed by zero or more arguments
:returns: If XOF_COLUMNS is set, the number of columns used; otherwise the number of bytes emitted
- :rtype: int
+ :rtype: xo_ssize_t
-.. c:function:: int xo_emit_hv (xo_handle_t *xop, const char *fmt, va_list vap)
+.. c:function:: xo_ssize_t xo_emit_hv (xo_handle_t *xop, const char *fmt, va_list vap)
:param xop: Handle for modify (or NULL for default handle)
:type xop: xo_handle_t \*
:param fmt: The format string
:param va_list vap: A set of variadic arguments
:returns: If XOF_COLUMNS is set, the number of columns used; otherwise the number of bytes emitted
- :rtype: int
+ :rtype: xo_ssize_t
.. index:: xo_emit_field
@@ -434,7 +434,7 @@ scenario where one would otherwise need to compose a format
descriptors using `snprintf`. The individual parts of the format
descriptor are passed in distinctly.
-.. c:function:: int xo_emit_field (const char *rolmod, const char *contents, const char *fmt, const char *efmt, ...)
+.. c:function:: xo_ssize_t xo_emit_field (const char *rolmod, const char *contents, const char *fmt, const char *efmt, ...)
:param rolmod: A comma-separated list of field roles and field modifiers
:type rolmod: const char *
@@ -445,7 +445,7 @@ descriptor are passed in distinctly.
:param efmt: Encoding format string, followed by additional arguments
:type efmt: const char *
:returns: If XOF_COLUMNS is set, the number of columns used; otherwise the number of bytes emitted
- :rtype: int
+ :rtype: xo_ssize_t
::
@@ -453,7 +453,7 @@ descriptor are passed in distinctly.
xo_emit_field("T", "Host name is ", NULL, NULL);
xo_emit_field("V", "host-name", NULL, NULL, host-name);
-.. c:function:: int xo_emit_field_h (xo_handle_t *xop, const char *rolmod, const char *contents, const char *fmt, const char *efmt, ...)
+.. c:function:: xo_ssize_t xo_emit_field_h (xo_handle_t *xop, const char *rolmod, const char *contents, const char *fmt, const char *efmt, ...)
:param xop: Handle for modify (or NULL for default handle)
:type xop: xo_handle_t \*
@@ -466,9 +466,9 @@ descriptor are passed in distinctly.
:param efmt: Encoding format string, followed by additional arguments
:type efmt: const char *
:returns: If XOF_COLUMNS is set, the number of columns used; otherwise the number of bytes emitted
- :rtype: int
+ :rtype: xo_ssize_t
-.. c:function:: int xo_emit_field_hv (xo_handle_t *xop, const char *rolmod, const char *contents, const char *fmt, const char *efmt, va_list vap)
+.. c:function:: xo_ssize_t xo_emit_field_hv (xo_handle_t *xop, const char *rolmod, const char *contents, const char *fmt, const char *efmt, va_list vap)
:param xop: Handle for modify (or NULL for default handle)
:type xop: xo_handle_t \*
@@ -482,7 +482,7 @@ descriptor are passed in distinctly.
:type efmt: const char *
:param va_list vap: A set of variadic arguments
:returns: If XOF_COLUMNS is set, the number of columns used; otherwise the number of bytes emitted
- :rtype: int
+ :rtype: xo_ssize_t
.. index:: xo_attr
.. _xo_attr:
@@ -505,14 +505,14 @@ Since attributes are only emitted in XML, their use should be limited
to meta-data and additional or redundant representations of data
already emitted in other form.
-.. c:function:: int xo_attr (const char *name, const char *fmt, ...)
+.. c:function:: xo_ssize_t xo_attr (const char *name, const char *fmt, ...)
:param name: Attribute name
:type name: const char *
:param fmt: Attribute value, as variadic arguments
:type fmt: const char *
:returns: -1 for error, or the number of bytes in the formatted attribute value
- :rtype: int
+ :rtype: xo_ssize_t
::
@@ -525,7 +525,7 @@ already emitted in other form.
<login-time seconds="1408336270">00:14</login-time>
-.. c:function:: int xo_attr_h (xo_handle_t *xop, const char *name, const char *fmt, ...)
+.. c:function:: xo_ssize_t xo_attr_h (xo_handle_t *xop, const char *name, const char *fmt, ...)
:param xop: Handle for modify (or NULL for default handle)
:type xop: xo_handle_t \*
@@ -533,7 +533,7 @@ already emitted in other form.
The `xo_attr_h` function follows the conventions of `xo_attr` but
adds an explicit libxo handle.
-.. c:function:: int xo_attr_hv (xo_handle_t *xop, const char *name, const char *fmt, va_list vap)
+.. c:function:: xo_ssize_t xo_attr_hv (xo_handle_t *xop, const char *name, const char *fmt, va_list vap)
The `xo_attr_h` function follows the conventions of `xo_attr_h`
but replaced the variadic list with a variadic pointer.
diff --git a/doc/libxo-manual.html b/doc/libxo-manual.html
index a10e056b4954..f65bdd18b4ac 100644
--- a/doc/libxo-manual.html
+++ b/doc/libxo-manual.html
@@ -515,7 +515,7 @@ li.indline1 {
}
@top-right {
- content: "May 2018";
+ content: "April 2019";
}
@top-center {
@@ -22011,7 +22011,7 @@ jQuery(function ($) {
</tr>
<tr>
<td class="header left"></td>
-<td class="header right">May 21, 2018</td>
+<td class="header right">April 2, 2019</td>
</tr>
</table></div>
<p id="title" class="title">libxo: The Easy Way to Generate text, XML, JSON, and HTML output<br><span class="filename">libxo-manual</span></p>
diff --git a/doc/xo.rst b/doc/xo.rst
index 9475c103b1ba..2556b01f2208 100644
--- a/doc/xo.rst
+++ b/doc/xo.rst
@@ -75,7 +75,7 @@ prepend data to the XPath values used for HTML output style::
EXAMPLE;
#!/bin/sh
xo --open top/data
- xo --depth 2 '{tag}' value
+ xo --depth 2 '{:tag}' value
xo --close top/data
XML:
<top>
@@ -90,6 +90,84 @@ prepend data to the XPath values used for HTML output style::
}
}
+When making partial lines of output (where the format string does not
+include a newline), use the `--continuation` option to let secondary
+invocations know they are adding data to an existing line.
+
+When emitting a series of objects, use the `--not-first` option to
+ensure that any details from the previous object (e.g. commas in JSON)
+are handled correctly.
+
+Use the `--top-wrap` option to ensure any top-level object details are
+handled correctly, e.g. wrap the entire output in a top-level set of
+braces for JSON output.
+
+ EXAMPLE;
+ #!/bin/sh
+ xo --top-wrap --open top/data
+ xo --depth 2 'First {:tag} ' value1
+ xo --depth 2 --continuation 'and then {:tag}\n' value2
+ xo --top-wrap --close top/data
+ TEXT:
+ First value1 and then value2
+ HTML:
+ <div class="line">
+ <div class="text">First </div>
+ <div class="data" data-tag="tag">value1</div>
+ <div class="text"> </div>
+ <div class="text">and then </div>
+ <div class="data" data-tag="tag">value2</div>
+ </div>
+ XML:
+ <top>
+ <data>
+ <tag>value1</tag>
+ <tag>value2</tag>
+ </data>
+ </top>
+ JSON:
+ {
+ "top": {
+ "data": {
+ "tag": "value1",
+ "tag": "value2"
+ }
+ }
+ }
+
+Lists and Instances
+-------------------
+
+A "*list*" is set of one or more instances that appear under the same
+parent. The instances contain details about a specific object. One
+can think of instances as objects or records. A call is needed to
+open and close the list, while a distinct call is needed to open and
+close each instance of the list.
+
+Use the `--open-list` and `--open-instances` to open lists and
+instances. Use the `--close-list` and `--close-instances` to close
+them. Each of these options take a `name` parameter, providing the
+name of the list and instance.
+
+In the following example, a list named "machine" is created with three
+instances:
+
+ opts="--json"
+ xo $opts --open-list machine
+ NF=
+ for name in red green blue; do
+ xo $opts --depth 1 $NF --open-instance machine
+ xo $opts --depth 2 "Machine {k:name} has {:memory}\n" $name 55
+ xo $opts --depth 1 --close-instance machine
+ NF=--not-first
+ done
+ xo $opts $NF --close-list machine
+
+The normal `libxo` functions use a state machine to help these
+transitions, but since each `xo` command is invoked independent of the
+previous calls, the state must be passed in explicitly via these
+command line options.
+
Command Line Options
--------------------
@@ -97,15 +175,23 @@ Command Line Options
Usage: xo [options] format [fields]
--close <path> Close tags for the given path
+ --close-instance <name> Close an open instance name
+ --close-list <name> Close an open list name
+ --continuation OR -C Output belongs on same line as previous output
--depth <num> Set the depth for pretty printing
--help Display this help text
--html OR -H Generate HTML output
--json OR -J Generate JSON output
--leading-xpath <path> Add a prefix to generated XPaths (HTML)
+ --not-first Indicate this object is not the first (JSON)
--open <path> Open tags for the given path
+ --open-instance <name> Open an instance given by name
+ --open-list <name> Open a list given by name
+ --option <opts> -or -O <opts> Give formatting options
--pretty OR -p Make 'pretty' output (add indent, newlines)
--style <style> Generate given style (xml, json, text, html)
--text OR -T Generate text output (the default style)
+ --top-wrap Generate a top-level object wrapper (JSON)
--version Display version information
--warn OR -W Display warnings in text on stderr
--warn-xml Display warnings in xml on stdout
diff --git a/libxo/Makefile.am b/libxo/Makefile.am
index a484a9b71dd9..a4d94acae19f 100644
--- a/libxo/Makefile.am
+++ b/libxo/Makefile.am
@@ -35,6 +35,7 @@ libxoinc_HEADERS = \
noinst_HEADERS = \
xo_buf.h \
+ xo_explicit.h \
xo_humanize.h \
xo_wcwidth.h
diff --git a/libxo/libxo.c b/libxo/libxo.c
index 51f2ea3c0eae..8666e3b4a269 100644
--- a/libxo/libxo.c
+++ b/libxo/libxo.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014-2015, Juniper Networks, Inc.
+ * Copyright (c) 2014-2018, Juniper Networks, Inc.
* All rights reserved.
* This SOFTWARE is licensed under the LICENSE provided in the
* ../Copyright file. By downloading, installing, copying, or otherwise
@@ -47,6 +47,7 @@
#include "xo.h"
#include "xo_encoder.h"
#include "xo_buf.h"
+#include "xo_explicit.h"
/*
* We ask wcwidth() to do an impossible job, really. It's supposed to
@@ -158,40 +159,9 @@ typedef unsigned xo_xsf_flags_t; /* XSF_* flags */
(XSF_NOT_FIRST | XSF_CONTENT | XSF_EMIT | XSF_EMIT_KEY | XSF_EMIT_LEAF_LIST )
/*
- * A word about states: We use a finite state machine (FMS) approach
- * to help remove fragility from the caller's code. Instead of
- * requiring a specific order of calls, we'll allow the caller more
- * flexibility and make the library responsible for recovering from
- * missed steps. The goal is that the library should not be capable
- * of emitting invalid xml or json, but the developer shouldn't need
- * to know or understand all the details about these encodings.
- *
- * You can think of states as either states or events, since they
- * function rather like both. None of the XO_CLOSE_* events will
- * persist as states, since the matching stack frame will be popped.
- * Same is true of XSS_EMIT, which is an event that asks us to
- * prep for emitting output fields.
+ * Turn the transition between two states into a number suitable for
+ * a "switch" statement.
*/
-
-/* Stack frame states */
-typedef unsigned xo_state_t;
-#define XSS_INIT 0 /* Initial stack state */
-#define XSS_OPEN_CONTAINER 1
-#define XSS_CLOSE_CONTAINER 2
-#define XSS_OPEN_LIST 3
-#define XSS_CLOSE_LIST 4
-#define XSS_OPEN_INSTANCE 5
-#define XSS_CLOSE_INSTANCE 6
-#define XSS_OPEN_LEAF_LIST 7
-#define XSS_CLOSE_LEAF_LIST 8
-#define XSS_DISCARDING 9 /* Discarding data until recovered */
-#define XSS_MARKER 10 /* xo_open_marker's marker */
-#define XSS_EMIT 11 /* xo_emit has a leaf field */
-#define XSS_EMIT_LEAF_LIST 12 /* xo_emit has a leaf-list ({l:}) */
-#define XSS_FINISH 13 /* xo_finish was called */
-
-#define XSS_MAX 13
-
#define XSS_TRANSITION(_old, _new) ((_old) << 8 | (_new))
/*
@@ -288,8 +258,8 @@ struct xo_handle_s {
ssize_t xo_units_offset; /* Start of units insertion point */
ssize_t xo_columns; /* Columns emitted during this xo_emit call */
#ifndef LIBXO_TEXT_ONLY
- uint8_t xo_color_map_fg[XO_NUM_COLORS]; /* Foreground color mappings */
- uint8_t xo_color_map_bg[XO_NUM_COLORS]; /* Background color mappings */
+ xo_color_t xo_color_map_fg[XO_NUM_COLORS]; /* Foreground color mappings */
+ xo_color_t xo_color_map_bg[XO_NUM_COLORS]; /* Background color mappings */
#endif /* LIBXO_TEXT_ONLY */
xo_colors_t xo_colors; /* Current color and effect values */
xo_buffer_t xo_color_buf; /* HTML: buffer of colors and effects */
@@ -321,6 +291,7 @@ struct xo_handle_s {
#define XOIF_UNITS_PENDING XOF_BIT(4) /* We have a units-insertion pending */
#define XOIF_INIT_IN_PROGRESS XOF_BIT(5) /* Init of handle is in progress */
+#define XOIF_MADE_OUTPUT XOF_BIT(6) /* Have already made output */
/* Flags for formatting functions */
typedef unsigned long xo_xff_flags_t;
@@ -468,7 +439,7 @@ static void
xo_failure (xo_handle_t *xop, const char *fmt, ...);
static ssize_t
-xo_transition (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name,
+xo_transition (xo_handle_t *xop, xo_xof_flags_t flags, const char *name,
xo_state_t new_state);
static int
@@ -506,6 +477,20 @@ xo_style (xo_handle_t *xop UNUSED)
}
/*
+ * Allow the compiler to optimize out non-text-only code while
+ * still compiling it.
+ */
+static inline int
+xo_text_only (void)
+{
+#ifdef LIBXO_TEXT_ONLY
+ return TRUE;
+#else /* LIBXO_TEXT_ONLY */
+ return FALSE;
+#endif /* LIBXO_TEXT_ONLY */
+}
+
+/*
* Callback to write data to a FILE pointer
*/
static xo_ssize_t
@@ -607,6 +592,28 @@ xo_no_setlocale (void)
}
/*
+ * For XML, the first character of a tag cannot be numeric, but people
+ * will likely not notice. So we people-proof them by forcing a leading
+ * underscore if they use invalid tags. Note that this doesn't cover
+ * all broken tags, just this fairly specific case.
+ */
+static const char *
+xo_xml_leader_len (xo_handle_t *xop, const char *name, xo_ssize_t nlen)
+{
+ if (isalpha(name[0]) || name[0] == '_')
+ return "";
+
+ xo_failure(xop, "invalid XML tag name: '%.*s'", nlen, name);
+ return "_";
+}
+
+static const char *
+xo_xml_leader (xo_handle_t *xop, const char *name)
+{
+ return xo_xml_leader_len(xop, name, strlen(name));
+}
+
+/*
* We need to decide if stdout is line buffered (_IOLBF). Lacking a
* standard way to decide this (e.g. getlinebuf()), we have configure
* look to find __flbf, which glibc supported. If not, we'll rely on
@@ -2194,9 +2201,8 @@ xo_set_style_name (xo_handle_t *xop, const char *name)
static void
xo_set_color_map (xo_handle_t *xop, char *value)
{
-#ifdef LIBXO_TEXT_ONLY
- return;
-#endif /* LIBXO_TEXT_ONLY */
+ if (xo_text_only())
+ return;
char *cp, *ep, *vp, *np;
ssize_t len = value ? strlen(value) + 1 : 0;
@@ -2214,8 +2220,11 @@ xo_set_color_map (xo_handle_t *xop, char *value)
fg = *cp ? xo_color_find(cp) : -1;
bg = (vp && *vp) ? xo_color_find(vp) : -1;
+#ifndef LIBXO_TEXT_ONLY
xop->xo_color_map_fg[num] = (fg < 0) ? num : fg;
xop->xo_color_map_bg[num] = (bg < 0) ? num : bg;
+#endif /* LIBXO_TEXT_ONLY */
+
if (++num > XO_NUM_COLORS)
break;
}
@@ -2226,9 +2235,11 @@ xo_set_color_map (xo_handle_t *xop, char *value)
else
XOF_CLEAR(xop, XOF_COLOR_MAP);
+#ifndef LIBXO_TEXT_ONLY
/* Fill in the rest of the colors with the defaults */
for ( ; num < XO_NUM_COLORS; num++)
xop->xo_color_map_fg[num] = xop->xo_color_map_bg[num] = num;
+#endif /* LIBXO_TEXT_ONLY */
}
static int
@@ -2600,6 +2611,12 @@ xo_line_ensure_open (xo_handle_t *xop, xo_xff_flags_t flags UNUSED)
static char div_open[] = "<div class=\"line\">";
static char div_open_blank[] = "<div class=\"blank-line\">";
+ if (XOF_ISSET(xop, XOF_CONTINUATION)) {
+ XOF_CLEAR(xop, XOF_CONTINUATION);
+ XOIF_SET(xop, XOIF_DIV_OPEN);
+ return;
+ }
+
if (XOIF_ISSET(xop, XOIF_DIV_OPEN))
return;
@@ -3505,51 +3522,54 @@ xo_do_format_field (xo_handle_t *xop, xo_buffer_t *xbp,
ssize_t columns = rc = xo_vsnprintf(xop, xbp, newfmt,
xop->xo_vap);
- /*
- * For XML and HTML, we need "&<>" processing; for JSON,
- * it's quotes. Text gets nothing.
- */
- switch (style) {
- case XO_STYLE_XML:
- if (flags & XFF_TRIM_WS)
- columns = rc = xo_trim_ws(xbp, rc);
- /* FALLTHRU */
- case XO_STYLE_HTML:
- rc = xo_escape_xml(xbp, rc, (flags & XFF_ATTR));
- break;
-
- case XO_STYLE_JSON:
- if (flags & XFF_TRIM_WS)
- columns = rc = xo_trim_ws(xbp, rc);
- rc = xo_escape_json(xbp, rc, 0);
- break;
-
- case XO_STYLE_SDPARAMS:
- if (flags & XFF_TRIM_WS)
- columns = rc = xo_trim_ws(xbp, rc);
- rc = xo_escape_sdparams(xbp, rc, 0);
- break;
+ if (rc > 0) {
+ /*
+ * For XML and HTML, we need "&<>" processing; for JSON,
+ * it's quotes. Text gets nothing.
+ */
+ switch (style) {
+ case XO_STYLE_XML:
+ if (flags & XFF_TRIM_WS)
+ columns = rc = xo_trim_ws(xbp, rc);
+ /* FALLTHRU */
+ case XO_STYLE_HTML:
+ rc = xo_escape_xml(xbp, rc, (flags & XFF_ATTR));
+ break;
+
+ case XO_STYLE_JSON:
+ if (flags & XFF_TRIM_WS)
+ columns = rc = xo_trim_ws(xbp, rc);
+ rc = xo_escape_json(xbp, rc, 0);
+ break;
+
+ case XO_STYLE_SDPARAMS:
+ if (flags & XFF_TRIM_WS)
+ columns = rc = xo_trim_ws(xbp, rc);
+ rc = xo_escape_sdparams(xbp, rc, 0);
+ break;
+
+ case XO_STYLE_ENCODER:
+ if (flags & XFF_TRIM_WS)
+ columns = rc = xo_trim_ws(xbp, rc);
+ break;
+ }
- case XO_STYLE_ENCODER:
- if (flags & XFF_TRIM_WS)
- columns = rc = xo_trim_ws(xbp, rc);
- break;
+ /*
+ * We can assume all the non-%s data we've
+ * added is ASCII, so the columns and bytes are the
+ * same. xo_format_string handles all the fancy
+ * string conversions and updates xo_anchor_columns
+ * accordingly.
+ */
+ if (XOF_ISSET(xop, XOF_COLUMNS))
+ xop->xo_columns += columns;
+ if (XOIF_ISSET(xop, XOIF_ANCHOR))
+ xop->xo_anchor_columns += columns;
}
-
- /*
- * We can assume all the non-%s data we've
- * added is ASCII, so the columns and bytes are the
- * same. xo_format_string handles all the fancy
- * string conversions and updates xo_anchor_columns
- * accordingly.
- */
- if (XOF_ISSET(xop, XOF_COLUMNS))
- xop->xo_columns += columns;
- if (XOIF_ISSET(xop, XOIF_ANCHOR))
- xop->xo_anchor_columns += columns;
}
- xbp->xb_curp += rc;
+ if (rc > 0)
+ xbp->xb_curp += rc;
}
/*
@@ -4233,6 +4253,21 @@ xo_format_is_numeric (const char *fmt, ssize_t flen)
return (strchr("diouDOUeEfFgG", *fmt) == NULL) ? FALSE : TRUE;
}
+/*
+ * Update the stack flags using the object flags, allowing callers
+ * to monkey with the stack flags without even knowing they exist.
+ */
+static void
+xo_stack_set_flags (xo_handle_t *xop)
+{
+ if (XOF_ISSET(xop, XOF_NOT_FIRST)) {
+ xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth];
+
+ xsp->xs_flags |= XSF_NOT_FIRST;
+ XOF_CLEAR(xop, XOF_NOT_FIRST);
+ }
+}
+
static void
xo_format_prep (xo_handle_t *xop, xo_xff_flags_t flags)
{
@@ -4337,6 +4372,8 @@ xo_format_value (xo_handle_t *xop, const char *name, ssize_t nlen,
xo_buffer_t *xbp = &xop->xo_data;
xo_humanize_save_t save; /* Save values for humanizing logic */
+ const char *leader = xo_xml_leader_len(xop, name, nlen);
+
switch (xo_style(xop)) {
case XO_STYLE_TEXT:
if (flags & XFF_ENCODE_ONLY)
@@ -4391,6 +4428,8 @@ xo_format_value (xo_handle_t *xop, const char *name, ssize_t nlen,
if (pretty)
xo_buf_indent(xop, -1);
xo_data_append(xop, "<", 1);
+ if (*leader)
+ xo_data_append(xop, leader, 1);
xo_data_escape(xop, name, nlen);
if (xop->xo_attrs.xb_curp != xop->xo_attrs.xb_bufp) {
@@ -4423,6 +4462,8 @@ xo_format_value (xo_handle_t *xop, const char *name, ssize_t nlen,
xo_simple_field(xop, FALSE, value, vlen, fmt, flen, flags);
xo_data_append(xop, "</", 2);
+ if (*leader)
+ xo_data_append(xop, leader, 1);
xo_data_escape(xop, name, nlen);
xo_data_append(xop, ">", 1);
if (pretty)
@@ -4446,6 +4487,8 @@ xo_format_value (xo_handle_t *xop, const char *name, ssize_t nlen,
flen = strlen(fmt);
}
+ xo_stack_set_flags(xop);
+
int first = (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST)
? 0 : 1;
@@ -4751,9 +4794,8 @@ xo_effect_find (const char *str)
static void
xo_colors_parse (xo_handle_t *xop, xo_colors_t *xocp, char *str)
{
-#ifdef LIBXO_TEXT_ONLY
- return;
-#endif /* LIBXO_TEXT_ONLY */
+ if (xo_text_only())
+ return;
char *cp, *ep, *np, *xp;
ssize_t len = strlen(str);
@@ -4837,12 +4879,9 @@ xo_colors_enabled (xo_handle_t *xop UNUSED)
* the incoming foreground and background colors from the map.
*/
static void
-xo_colors_update (xo_handle_t *xop, xo_colors_t *newp)
+xo_colors_update (xo_handle_t *xop UNUSED, xo_colors_t *newp UNUSED)
{
-#ifdef LIBXO_TEXT_ONLY
- return;
-#endif /* LIBXO_TEXT_ONLY */
-
+#ifndef LIBXO_TEXT_ONLY
xo_color_t fg = newp->xoc_col_fg;
if (XOF_ISSET(xop, XOF_COLOR_MAP) && fg < XO_NUM_COLORS)
fg = xop->xo_color_map_fg[fg]; /* Fetch from color map */
@@ -4852,6 +4891,7 @@ xo_colors_update (xo_handle_t *xop, xo_colors_t *newp)
if (XOF_ISSET(xop, XOF_COLOR_MAP) && bg < XO_NUM_COLORS)
bg = xop->xo_color_map_bg[bg]; /* Fetch from color map */
newp->xoc_col_bg = bg;
+#endif /* LIBXO_TEXT_ONLY */
}
static void
@@ -6454,9 +6494,7 @@ xo_do_emit_fields (xo_handle_t *xop, xo_field_info_t *fields,
/* If we don't have an anchor, write the text out */
if (flush && !XOIF_ISSET(xop, XOIF_ANCHOR)) {
- if (xo_write(xop) < 0)
- rc = -1; /* Report failure */
- else if (xo_flush_h(xop) < 0)
+ if (xo_flush_h(xop) < 0)
rc = -1;
}
@@ -6804,17 +6842,6 @@ xo_attr (const char *name, const char *fmt, ...)
}
static void
-xo_stack_set_flags (xo_handle_t *xop)
-{
- if (XOF_ISSET(xop, XOF_NOT_FIRST)) {
- xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth];
-
- xsp->xs_flags |= XSF_NOT_FIRST;
- XOF_CLEAR(xop, XOF_NOT_FIRST);
- }
-}
-
-static void
xo_depth_change (xo_handle_t *xop, const char *name,
int delta, int indent, xo_state_t state, xo_xsf_flags_t flags)
{
@@ -6889,6 +6916,15 @@ xo_set_depth (xo_handle_t *xop, int depth)
xop->xo_depth += depth;
xop->xo_indent += depth;
+
+ /*
+ * Handling the "top wrapper" for JSON is a bit of a pain. Here
+ * we need to detect that the depth has been changed to set the
+ * "XOIF_TOP_EMITTED" flag correctly.
+ */
+ if (xop->xo_style == XO_STYLE_JSON
+ && !XOF_ISSET(xop, XOF_NO_TOP) && xop->xo_depth > 0)
+ XOIF_SET(xop, XOIF_TOP_EMITTED);
}
static xo_xsf_flags_t
@@ -6925,11 +6961,12 @@ xo_do_open_container (xo_handle_t *xop, xo_xof_flags_t flags, const char *name)
name = XO_FAILURE_NAME;
}
+ const char *leader = xo_xml_leader(xop, name);
flags |= xop->xo_flags; /* Pick up handle flags */
switch (xo_style(xop)) {
case XO_STYLE_XML:
- rc = xo_printf(xop, "%*s<%s", xo_indent(xop), "", name);
+ rc = xo_printf(xop, "%*s<%s%s", xo_indent(xop), "", leader, name);
if (xop->xo_attrs.xb_curp != xop->xo_attrs.xb_bufp) {
rc += xop->xo_attrs.xb_curp - xop->xo_attrs.xb_bufp;
@@ -6970,7 +7007,7 @@ xo_do_open_container (xo_handle_t *xop, xo_xof_flags_t flags, const char *name)
return rc;
}
-static int
+xo_ssize_t
xo_open_container_hf (xo_handle_t *xop, xo_xof_flags_t flags, const char *name)
{
return xo_transition(xop, flags, name, XSS_OPEN_CONTAINER);
@@ -7025,15 +7062,20 @@ xo_do_close_container (xo_handle_t *xop, const char *name)
}
}
+ const char *leader = xo_xml_leader(xop, name);
+
switch (xo_style(xop)) {
case XO_STYLE_XML:
xo_depth_change(xop, name, -1, -1, XSS_CLOSE_CONTAINER, 0);
- rc = xo_printf(xop, "%*s</%s>%s", xo_indent(xop), "", name, ppn);
+ rc = xo_printf(xop, "%*s</%s%s>%s", xo_indent(xop), "", leader, name, ppn);
break;
case XO_STYLE_JSON:
+ xo_stack_set_flags(xop);
+
pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : "";
- ppn = (xop->xo_depth <= 1) ? "\n" : "";
+ ppn = (xop->xo_depth <= 1) ? pre_nl : "";
+ ppn = "";
xo_depth_change(xop, name, -1, -1, XSS_CLOSE_CONTAINER, 0);
rc = xo_printf(xop, "%s%*s}%s", pre_nl, xo_indent(xop), "", ppn);
@@ -7082,7 +7124,7 @@ xo_close_container_d (void)
}
static int
-xo_do_open_list (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name)
+xo_do_open_list (xo_handle_t *xop, xo_xof_flags_t flags, const char *name)
{
ssize_t rc = 0;
int indent = 0;
@@ -7126,8 +7168,8 @@ xo_do_open_list (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name)
return rc;
}
-static int
-xo_open_list_hf (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name)
+xo_ssize_t
+xo_open_list_hf (xo_handle_t *xop, xo_xof_flags_t flags, const char *name)
{
return xo_transition(xop, flags, name, XSS_OPEN_LIST);
}
@@ -7228,7 +7270,7 @@ xo_close_list_d (void)
}
static int
-xo_do_open_leaf_list (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name)
+xo_do_open_leaf_list (xo_handle_t *xop, xo_xof_flags_t flags, const char *name)
{
ssize_t rc = 0;
int indent = 0;
@@ -7322,7 +7364,7 @@ xo_do_close_leaf_list (xo_handle_t *xop, const char *name)
}
static int
-xo_do_open_instance (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name)
+xo_do_open_instance (xo_handle_t *xop, xo_xof_flags_t flags, const char *name)
{
xop = xo_default(xop);
@@ -7330,16 +7372,17 @@ xo_do_open_instance (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name)
const char *ppn = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : "";
const char *pre_nl = "";
- flags |= xop->xo_flags;
-
if (name == NULL) {
xo_failure(xop, "NULL passed for instance name");
name = XO_FAILURE_NAME;
}
+ const char *leader = xo_xml_leader(xop, name);
+ flags |= xop->xo_flags;
+
switch (xo_style(xop)) {
case XO_STYLE_XML:
- rc = xo_printf(xop, "%*s<%s", xo_indent(xop), "", name);
+ rc = xo_printf(xop, "%*s<%s%s", xo_indent(xop), "", leader, name);
if (xop->xo_attrs.xb_curp != xop->xo_attrs.xb_bufp) {
rc += xop->xo_attrs.xb_curp - xop->xo_attrs.xb_bufp;
@@ -7375,8 +7418,8 @@ xo_do_open_instance (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name)
return rc;
}
-static int
-xo_open_instance_hf (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name)
+xo_ssize_t
+xo_open_instance_hf (xo_handle_t *xop, xo_xof_flags_t flags, const char *name)
{
return xo_transition(xop, flags, name, XSS_OPEN_INSTANCE);
}
@@ -7430,10 +7473,12 @@ xo_do_close_instance (xo_handle_t *xop, const char *name)
}
}
+ const char *leader = xo_xml_leader(xop, name);
+
switch (xo_style(xop)) {
case XO_STYLE_XML:
xo_depth_change(xop, name, -1, -1, XSS_CLOSE_INSTANCE, 0);
- rc = xo_printf(xop, "%*s</%s>%s", xo_indent(xop), "", name, ppn);
+ rc = xo_printf(xop, "%*s</%s%s>%s", xo_indent(xop), "", leader, name, ppn);
break;
case XO_STYLE_JSON:
@@ -7599,7 +7644,7 @@ xo_do_close (xo_handle_t *xop, const char *name, xo_state_t new_state)
* We are in a given state and need to transition to the new state.
*/
static ssize_t
-xo_transition (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name,
+xo_transition (xo_handle_t *xop, xo_xof_flags_t flags, const char *name,
xo_state_t new_state)
{
xo_stack_t *xsp;
@@ -7855,9 +7900,12 @@ xo_transition (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name,
/* Handle the flush flag */
if (rc >= 0 && XOF_ISSET(xop, XOF_FLUSH))
- if (xo_flush_h(xop))
+ if (xo_flush_h(xop) < 0)
rc = -1;
+ /* We have now official made output */
+ XOIF_SET(xop, XOIF_MADE_OUTPUT);
+
return rc;
marker_prevents_close:
@@ -7950,7 +7998,7 @@ xo_flush (void)
xo_ssize_t
xo_finish_h (xo_handle_t *xop)
{
- const char *cp = "";
+ const char *open_if_empty = "";
xop = xo_default(xop);
if (!XOF_ISSET(xop, XOF_NO_CLOSE))
@@ -7959,11 +8007,17 @@ xo_finish_h (xo_handle_t *xop)
switch (xo_style(xop)) {
case XO_STYLE_JSON:
if (!XOF_ISSET(xop, XOF_NO_TOP)) {
+ const char *pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : "";
+
if (XOIF_ISSET(xop, XOIF_TOP_EMITTED))
XOIF_CLEAR(xop, XOIF_TOP_EMITTED); /* Turn off before output */
- else
- cp = "{ ";
- xo_printf(xop, "%*s%s}\n",xo_indent(xop), "", cp);
+ else if (!XOIF_ISSET(xop, XOIF_MADE_OUTPUT)) {
+ open_if_empty = "{ ";
+ pre_nl = "";
+ }
+
+ xo_printf(xop, "%s%*s%s}\n",
+ pre_nl, xo_indent(xop), "", open_if_empty);
}
break;
@@ -8402,3 +8456,46 @@ xo_set_encoder (xo_handle_t *xop, xo_encoder_func_t encoder)
xop->xo_style = XO_STYLE_ENCODER;
xop->xo_encoder = encoder;
}
+
+/*
+ * The xo(1) utility needs to be able to open and close lists and
+ * instances, but since it's called without "state", we cannot
+ * rely on the state transitions (in xo_transition) to DTRT, so
+ * we have a mechanism for external parties to "force" transitions
+ * that would otherwise be impossible. This is not a general
+ * mechanism, and is really tailored only for xo(1).
+ */
+void
+xo_explicit_transition (xo_handle_t *xop, xo_state_t new_state,
+ const char *name, xo_xof_flags_t flags)
+{
+ xo_xsf_flags_t xsf_flags;
+
+ xop = xo_default(xop);
+
+ switch (new_state) {
+
+ case XSS_OPEN_LIST:
+ xo_do_open_list(xop, flags, name);
+ break;
+
+ case XSS_OPEN_INSTANCE:
+ xo_do_open_instance(xop, flags, name);
+ break;
+
+ case XSS_CLOSE_INSTANCE:
+ xo_depth_change(xop, name, 1, 1, XSS_OPEN_INSTANCE,
+ xo_stack_flags(flags));
+ xo_stack_set_flags(xop);
+ xo_do_close_instance(xop, name);
+ break;
+
+ case XSS_CLOSE_LIST:
+ xsf_flags = XOF_ISSET(xop, XOF_NOT_FIRST) ? XSF_NOT_FIRST : 0;
+
+ xo_depth_change(xop, name, 1, 1, XSS_OPEN_LIST,
+ XSF_LIST | xsf_flags | xo_stack_flags(flags));
+ xo_do_close_list(xop, name);
+ break;
+ }
+}
diff --git a/libxo/xo.h b/libxo/xo.h
index c39fa47e0db5..8404c6cb93ae 100644
--- a/libxo/xo.h
+++ b/libxo/xo.h
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014-2015, Juniper Networks, Inc.
+ * Copyright (c) 2014-2018, Juniper Networks, Inc.
* All rights reserved.
* This SOFTWARE is licensed under the LICENSE provided in the
* ../Copyright file. By downloading, installing, copying, or otherwise
@@ -102,6 +102,7 @@ typedef unsigned long long xo_xof_flags_t;
#define XOF_RETAIN_NONE XOF_BIT(31) /** Prevent use of XOEF_RETAIN */
#define XOF_COLOR_MAP XOF_BIT(32) /** Color map has been initialized */
+#define XOF_CONTINUATION XOF_BIT(33) /** Continuation of previous line */
typedef unsigned xo_emit_flags_t; /* Flags to xo_emit() and friends */
#define XOEF_RETAIN (1<<0) /* Retain parsed formatting information */
@@ -126,11 +127,11 @@ typedef struct xo_handle_s xo_handle_t; /* Handle for XO output */
* sizes. We want to fix this but allow for backwards compatibility
* where needed.
*/
-#ifdef USE_INT_RETURN_CODES
+#ifdef XO_USE_INT_RETURN_CODES
typedef int xo_ssize_t; /* Buffer size */
-#else /* USE_INT_RETURN_CODES */
+#else /* XO_USE_INT_RETURN_CODES */
typedef ssize_t xo_ssize_t; /* Buffer size */
-#endif /* USE_INT_RETURN_CODES */
+#endif /* XO_USE_INT_RETURN_CODES */
typedef xo_ssize_t (*xo_write_func_t)(void *, const char *);
typedef void (*xo_close_func_t)(void *);
@@ -219,36 +220,36 @@ xo_ssize_t
xo_emit_f (xo_emit_flags_t flags, const char *fmt, ...);
PRINTFLIKE(2, 0)
-static inline int
+static inline xo_ssize_t
xo_emit_hvp (xo_handle_t *xop, const char *fmt, va_list vap)
{
return xo_emit_hv(xop, fmt, vap);
}
PRINTFLIKE(2, 3)
-static inline int
+static inline xo_ssize_t
xo_emit_hp (xo_handle_t *xop, const char *fmt, ...)
{
va_list vap;
va_start(vap, fmt);
- int rc = xo_emit_hv(xop, fmt, vap);
+ xo_ssize_t rc = xo_emit_hv(xop, fmt, vap);
va_end(vap);
return rc;
}
PRINTFLIKE(1, 2)
-static inline int
+static inline xo_ssize_t
xo_emit_p (const char *fmt, ...)
{
va_list vap;
va_start(vap, fmt);
- int rc = xo_emit_hv(NULL, fmt, vap);
+ xo_ssize_t rc = xo_emit_hv(NULL, fmt, vap);
va_end(vap);
return rc;
}
PRINTFLIKE(3, 0)
-static inline int
+static inline xo_ssize_t
xo_emit_hvfp (xo_handle_t *xop, xo_emit_flags_t flags,
const char *fmt, va_list vap)
{
@@ -256,28 +257,31 @@ xo_emit_hvfp (xo_handle_t *xop, xo_emit_flags_t flags,
}
PRINTFLIKE(3, 4)
-static inline int
+static inline xo_ssize_t
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);
+ xo_ssize_t rc = xo_emit_hvf(xop, flags, fmt, vap);
va_end(vap);
return rc;
}
PRINTFLIKE(2, 3)
-static inline int
+static inline xo_ssize_t
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);
+ xo_ssize_t rc = xo_emit_hvf(NULL, flags, fmt, vap);
va_end(vap);
return rc;
}
xo_ssize_t
+xo_open_container_hf (xo_handle_t *xop, xo_xof_flags_t flags, const char *name);
+
+xo_ssize_t
xo_open_container_h (xo_handle_t *xop, const char *name);
xo_ssize_t
@@ -302,6 +306,9 @@ xo_ssize_t
xo_close_container_d (void);
xo_ssize_t
+xo_open_list_hf (xo_handle_t *xop, xo_xof_flags_t flags, const char *name);
+
+xo_ssize_t
xo_open_list_h (xo_handle_t *xop, const char *name);
xo_ssize_t
@@ -326,6 +333,9 @@ xo_ssize_t
xo_close_list_d (void);
xo_ssize_t
+xo_open_instance_hf (xo_handle_t *xop, xo_xof_flags_t flags, const char *name);
+
+xo_ssize_t
xo_open_instance_h (xo_handle_t *xop, const char *name);
xo_ssize_t
diff --git a/libxo/xo_attr.3 b/libxo/xo_attr.3
index 9e61acd3656c..4f1465b1ffde 100644
--- a/libxo/xo_attr.3
+++ b/libxo/xo_attr.3
@@ -17,11 +17,11 @@
.Lb libxo
.Sh SYNOPSIS
.In libxo/xo.h
-.Ft int
+.Ft xo_ssize_t
.Fn xo_attr "const char *name" "const char *fmt" "..."
-.Ft int
+.Ft xo_ssize_t
.Fn xo_attr_h "xo_handle_t *handle" "const char *name, const char *fmt" "..."
-.Ft int
+.Ft xo_ssize_t
.Fn xo_attr_hv "xo_handle_t *handle" "const char *name" "const char *fmt" "va_list vap"
.Sh DESCRIPTION
The
diff --git a/libxo/xo_buf.h b/libxo/xo_buf.h
index d6a05005cd23..e9468901c077 100644
--- a/libxo/xo_buf.h
+++ b/libxo/xo_buf.h
@@ -114,7 +114,12 @@ static inline int
xo_buf_has_room (xo_buffer_t *xbp, ssize_t len)
{
if (xbp->xb_curp + len >= xbp->xb_bufp + xbp->xb_size) {
- ssize_t sz = xbp->xb_size + XO_BUFSIZ;
+ /*
+ * Find out how much new space we need, round it up to XO_BUFSIZ
+ */
+ ssize_t sz = (xbp->xb_curp + len) - xbp->xb_bufp;
+ sz = (sz + XO_BUFSIZ - 1) & ~(XO_BUFSIZ - 1);
+
char *bp = xo_realloc(xbp->xb_bufp, sz);
if (bp == NULL)
return 0;
diff --git a/libxo/xo_emit.3 b/libxo/xo_emit.3
index eceb38e0aa83..9e3ec85b6765 100644
--- a/libxo/xo_emit.3
+++ b/libxo/xo_emit.3
@@ -17,11 +17,11 @@
.Lb libxo
.Sh SYNOPSIS
.In libxo/xo.h
-.Ft int
+.Ft xo_ssize_t
.Fn xo_emit "const char *fmt" "..."
-.Ft int
+.Ft xo_ssize_t
.Fn xo_emit_h "xo_handle_t *xop" "const char *fmt" "..."
-.Ft int
+.Ft xo_ssize_t
.Fn xo_emit_hv "xo_handle_t *xop" "const char *fmt" "va_list vap"
.Sh DESCRIPTION
The
diff --git a/libxo/xo_emit_f.3 b/libxo/xo_emit_f.3
index 2edfa79ac9c0..7c340175aef6 100644
--- a/libxo/xo_emit_f.3
+++ b/libxo/xo_emit_f.3
@@ -17,11 +17,11 @@
.Lb libxo
.Sh SYNOPSIS
.In libxo/xo.h
-.Ft int
+.Ft xo_ssize_t
.Fn xo_emit_f "xo_emit_flags_t flags" "const char *fmt" "..."
-.Ft int
+.Ft xo_ssize_t
.Fn xo_emit_hf "xo_handle_t *xop" "xo_emit_flags_t flags" "const char *fmt" "..."
-.Ft int
+.Ft xo_ssize_t
.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"
diff --git a/libxo/xo_explicit.h b/libxo/xo_explicit.h
new file mode 100644
index 000000000000..e2133e75f70e
--- /dev/null
+++ b/libxo/xo_explicit.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2015, 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, March 2019
+ */
+
+#ifndef XO_EXPLICIT_H
+#define XO_EXPLICIT_H
+
+/*
+ * NOTE WELL: This file is needed to software that implements an
+ * explicit transition between libxo states on its internal stack.
+ * General libxo code should _never_ include this header file.
+ */
+
+
+/*
+ * A word about states: We use a finite state machine (FMS) approach
+ * to help remove fragility from the caller's code. Instead of
+ * requiring a specific order of calls, we'll allow the caller more
+ * flexibility and make the library responsible for recovering from
+ * missed steps. The goal is that the library should not be capable
+ * of emitting invalid xml or json, but the developer shouldn't need
+ * to know or understand all the details about these encodings.
+ *
+ * You can think of states as either states or events, since they
+ * function rather like both. None of the XO_CLOSE_* events will
+ * persist as states, since the matching stack frame will be popped.
+ * Same is true of XSS_EMIT, which is an event that asks us to
+ * prep for emitting output fields.
+ */
+
+/* Stack frame states */
+typedef unsigned xo_state_t; /* XSS_* values */
+#define XSS_INIT 0 /* Initial stack state */
+#define XSS_OPEN_CONTAINER 1
+#define XSS_CLOSE_CONTAINER 2
+#define XSS_OPEN_LIST 3
+#define XSS_CLOSE_LIST 4
+#define XSS_OPEN_INSTANCE 5
+#define XSS_CLOSE_INSTANCE 6
+#define XSS_OPEN_LEAF_LIST 7
+#define XSS_CLOSE_LEAF_LIST 8
+#define XSS_DISCARDING 9 /* Discarding data until recovered */
+#define XSS_MARKER 10 /* xo_open_marker's marker */
+#define XSS_EMIT 11 /* xo_emit has a leaf field */
+#define XSS_EMIT_LEAF_LIST 12 /* xo_emit has a leaf-list ({l:}) */
+#define XSS_FINISH 13 /* xo_finish was called */
+
+#define XSS_MAX 13
+
+void
+xo_explicit_transition (xo_handle_t *xop, xo_state_t new_state,
+ const char *tag, xo_xof_flags_t flags);
+
+#endif /* XO_EXPLICIT_H */
diff --git a/libxo/xo_finish.3 b/libxo/xo_finish.3
index f9c6b3de62e2..a61d5276b504 100644
--- a/libxo/xo_finish.3
+++ b/libxo/xo_finish.3
@@ -17,9 +17,9 @@
.Lb libxo
.Sh SYNOPSIS
.In libxo/xo.h
-.Ft void
+.Ft xo_ssize_t
.Fn xo_finish "void"
-.Ft void
+.Ft xo_ssize_t
.Fn xo_finish_h "xo_handle_t *xop"
.Sh DESCRIPTION
When the program is ready to exit or close a handle, a call to
diff --git a/libxo/xo_flush.3 b/libxo/xo_flush.3
index a785c68f12ec..d1375223f5e9 100644
--- a/libxo/xo_flush.3
+++ b/libxo/xo_flush.3
@@ -17,9 +17,9 @@
.Lb libxo
.Sh SYNOPSIS
.In libxo/xo.h
-.Ft void
+.Ft xo_ssize_t
.Fn xo_flush "void"
-.Ft void
+.Ft xo_ssize_t
.Fn xo_flush_h "xo_handle_t *handle"
.Sh DESCRIPTION
.Nm libxo
diff --git a/libxo/xo_open_container.3 b/libxo/xo_open_container.3
index 16cd9c999a17..7037974d2c4e 100644
--- a/libxo/xo_open_container.3
+++ b/libxo/xo_open_container.3
@@ -18,21 +18,21 @@
.Lb libxo
.Sh SYNOPSIS
.In libxo/xo.h
-.Ft int
+.Ft xo_ssize_t
.Fn xo_open_container "const char *name"
-.Ft int
+.Ft xo_ssize_t
.Fn xo_open_container_h "xo_handle_t *handle" "const char *name"
-.Ft int
+.Ft xo_ssize_t
.Fn xo_open_container_hd "xo_handle_t *handle" "const char *name"
-.Ft int
+.Ft xo_ssize_t
.Fn xo_open_container_d "const char *name"
-.Ft int
+.Ft xo_ssize_t
.Fn xo_close_container "const char *name"
-.Ft int
+.Ft xo_ssize_t
.Fn xo_close_container_h "xo_handle_t *handle" "const char *name"
-.Ft int
+.Ft xo_ssize_t
.Fn xo_close_container_hd "xo_handle_t *handle"
-.Ft int
+.Ft xo_ssize_t
.Fn xo_close_container_d "void"
.Sh DESCRIPTION
.Nm libxo
diff --git a/libxo/xo_open_list.3 b/libxo/xo_open_list.3
index e4d3080eeb63..f28c9b63ac35 100644
--- a/libxo/xo_open_list.3
+++ b/libxo/xo_open_list.3
@@ -20,37 +20,37 @@
.Lb libxo
.Sh SYNOPSIS
.In libxo/xo.h
-.Ft int
+.Ft xo_ssize_t
.Fn xo_open_list_h "xo_handle_t *xop" "const char *name"
-.Ft int
+.Ft xo_ssize_t
.Fn xo_open_list "const char *name"
-.Ft int
+.Ft xo_ssize_t
.Fn xo_open_list_hd "xo_handle_t *xop" "const char *name"
-.Ft int
+.Ft xo_ssize_t
.Fn xo_open_list_d "const char *name"
-.Ft int
+.Ft xo_ssize_t
.Fn xo_open_instance_h "xo_handle_t *xop" "const char *name"
-.Ft int
+.Ft xo_ssize_t
.Fn xo_open_instance "const char *name"
-.Ft int
+.Ft xo_ssize_t
.Fn xo_open_instance_hd "xo_handle_t *xop" "const char *name"
-.Ft int
+.Ft xo_ssize_t
.Fn xo_open_instance_d "const char *name"
-.Ft int
+.Ft xo_ssize_t
.Fn xo_close_instance_h "xo_handle_t *xop" "const char *name"
-.Ft int
+.Ft xo_ssize_t
.Fn xo_close_instance "const char *name"
-.Ft int
+.Ft xo_ssize_t
.Fn xo_close_instance_hd "xo_handle_t *xop"
-.Ft int
+.Ft xo_ssize_t
.Fn xo_close_instance_d "void"
-.Ft int
+.Ft xo_ssize_t
.Fn xo_close_list_h "xo_handle_t *xop" "const char *name"
-.Ft int
+.Ft xo_ssize_t
.Fn xo_close_list "const char *name"
-.Ft int
+.Ft xo_ssize_t
.Fn xo_close_list_hd "xo_handle_t *xop"
-.Ft int
+.Ft xo_ssize_t
.Fn xo_close_list_d "void"
.Sh DESCRIPTION
Lists are sequences of instances of homogeneous data objects.
diff --git a/libxo/xo_open_marker.3 b/libxo/xo_open_marker.3
index 2df3bff1535f..7c252f3d620f 100644
--- a/libxo/xo_open_marker.3
+++ b/libxo/xo_open_marker.3
@@ -17,13 +17,13 @@
.Lb libxo
.Sh SYNOPSIS
.In libxo/xo.h
-.Ft int
+.Ft xo_ssize_t
.Fn xo_open_marker "const char *name"
-.Ft int
+.Ft xo_ssize_t
.Fn xo_open_marker_h "xo_handle_t *handle" "const char *name"
-.Ft int
+.Ft xo_ssize_t
.Fn xo_close_marker "const char *name"
-.Ft int
+.Ft xo_ssize_t
.Fn xo_close_marker_h "xo_handle_t *handle" "const char *name"
.Sh DESCRIPTION
.Nm libxo
diff --git a/libxo/xo_set_writer.3 b/libxo/xo_set_writer.3
index 22c8ee91083b..f4f0a5dcb0d7 100644
--- a/libxo/xo_set_writer.3
+++ b/libxo/xo_set_writer.3
@@ -18,7 +18,7 @@
.Sh SYNOPSIS
.In libxo/xo.h
.Ft void
-.Sy typedef int (*xo_write_func_t)(void *, const char *);
+.Sy typedef xo_ssize_t (*xo_write_func_t)(void *, const char *);
.Pp
.Sy typedef void (*xo_close_func_t)(void *);
.Pp
diff --git a/tests/core/saved/test_01.J.out b/tests/core/saved/test_01.J.out
index 302ac2426f33..05cc739934ee 100644
--- a/tests/core/saved/test_01.J.out
+++ b/tests/core/saved/test_01.J.out
@@ -1,2 +1 @@
-{"top": {"type":"ethernet","type":"bridge","type":"18u","type":24,"address":"0x0","port":1,"address":"0x0","port":1,"address":"0x0","port":1,"used-percent":12,"kve_start":"0xdeadbeef","kve_end":"0xcabb1e","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","pre":"that","links":3,"post":"this","mode":"/some/file","mode_octal":640,"links":1,"user":"user","group":"group"}
-}
+{"top": {"type":"ethernet","type":"bridge","type":"18u","type":24,"address":"0x0","port":1,"address":"0x0","port":1,"address":"0x0","port":1,"used-percent":12,"kve_start":"0xdeadbeef","kve_end":"0xcabb1e","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","pre":"that","links":3,"post":"this","mode":"/some/file","mode_octal":640,"links":1,"user":"user","group":"group"}}
diff --git a/tests/core/saved/test_02.J.out b/tests/core/saved/test_02.J.out
index cafe7713c3ff..65732c5c28d7 100644
--- a/tests/core/saved/test_02.J.out
+++ b/tests/core/saved/test_02.J.out
@@ -1,2 +1 @@
-{"top": {"data": {"what":"braces","length":"abcdef","fd":-1,"error":"Bad file descriptor","test":"good","fd":-1,"error":"Bad fi","test":"good","lines":20,"words":30,"characters":40, "bytes": [0,1,2,3,4],"mbuf-current":10,"mbuf-cache":20,"mbuf-total":30,"distance":50,"location":"Boston","memory":64,"total":640,"memory":64,"total":640,"ten":10,"eleven":11,"unknown":1010,"unknown":1010,"min":15,"cur":20,"max":125,"min":15,"cur":20,"max":125,"min":15,"cur":20,"max":125,"min":15,"cur":20,"max":125,"val1":21,"val2":58368,"val3":100663296,"val4":44470272,"val5":1342172800, "flag": ["one","two","three"],"works":null,"empty-tag":true,"t1":"1000","t2":"test5000","t3":"ten-longx","t4":"xtest", "__error": {"message":"this is an error"}, "__error": {"message":"two more errors"}, "__warning": {"message":"this is an warning"}, "__warning": {"message":"two more warnings"},"count":10,"test":4, "error": {"message":"Shut 'er down, Clancey! She's a-pumpin' mud! <>!,\"!<>\n"}}}
-}
+{"top": {"data": {"what":"braces","length":"abcdef","fd":-1,"error":"Bad file descriptor","test":"good","fd":-1,"error":"Bad fi","test":"good","lines":20,"words":30,"characters":40, "bytes": [0,1,2,3,4],"mbuf-current":10,"mbuf-cache":20,"mbuf-total":30,"distance":50,"location":"Boston","memory":64,"total":640,"memory":64,"total":640,"ten":10,"eleven":11,"unknown":1010,"unknown":1010,"min":15,"cur":20,"max":125,"min":15,"cur":20,"max":125,"min":15,"cur":20,"max":125,"min":15,"cur":20,"max":125,"val1":21,"val2":58368,"val3":100663296,"val4":44470272,"val5":1342172800, "flag": ["one","two","three"],"works":null,"empty-tag":true,"t1":"1000","t2":"test5000","t3":"ten-longx","t4":"xtest", "__error": {"message":"this is an error"}, "__error": {"message":"two more errors"}, "__warning": {"message":"this is an warning"}, "__warning": {"message":"two more warnings"},"count":10,"test":4, "error": {"message":"Shut 'er down, Clancey! She's a-pumpin' mud! <>!,\"!<>\n"}}}}
diff --git a/tests/core/saved/test_03.J.out b/tests/core/saved/test_03.J.out
index 00a53016dc86..5902a79126be 100644
--- a/tests/core/saved/test_03.J.out
+++ b/tests/core/saved/test_03.J.out
@@ -1,2 +1 @@
-{"employees": {"employee": [],"extra":"", "memory": [{"type":"name","in-use":12345,"memory-use":54321,"high-use":"-","requests":32145}], "employee": [{"first-name":"Terry","last-name":"Jones","department":660}, {"first-name":"Leslie","last-name":"Patterson","department":341}, {"first-name":"Ashley","last-name":"Smith","department":1440}]}
-}
+{"employees": {"employee": [],"extra":"", "memory": [{"type":"name","in-use":12345,"memory-use":54321,"high-use":"-","requests":32145}], "employee": [{"first-name":"Terry","last-name":"Jones","department":660}, {"first-name":"Leslie","last-name":"Patterson","department":341}, {"first-name":"Ashley","last-name":"Smith","department":1440}]}}
diff --git a/tests/core/saved/test_04.J.out b/tests/core/saved/test_04.J.out
index 4ba1fb1772d8..94e01146e735 100644
--- a/tests/core/saved/test_04.J.out
+++ b/tests/core/saved/test_04.J.out
@@ -1,2 +1 @@
-{"employees": {"employee": [{"first-name":"Terry","last-name":"Jones","department":660}, {"first-name":"Leslie","last-name":"Patterson","department":341}, {"first-name":"Ashley","last-name":"Smith","department":1440}]}
-}
+{"employees": {"employee": [{"first-name":"Terry","last-name":"Jones","department":660}, {"first-name":"Leslie","last-name":"Patterson","department":341}, {"first-name":"Ashley","last-name":"Smith","department":1440}]}}
diff --git a/tests/core/saved/test_05.J.out b/tests/core/saved/test_05.J.out
index 3525c43223cf..0db0a65fb06b 100644
--- a/tests/core/saved/test_05.J.out
+++ b/tests/core/saved/test_05.J.out
@@ -1,3 +1 @@
-{"indian-languages": {"gurmukhi":"ਲਹੌਰ ਪਾਕਿਸਤਾਨੀ ਪੰਜਾਬ ਦੀ ਰਾਜਧਾਨੀ ਹੈ । ਲੋਕ ਗਿਣਤੀ ਦੇ ਨਾਲ ਕਰਾਚੀ ਤੋਂ ਬਾਅਦ ਲਹੌਰ ਦੂਜਾ ਸਭ ਤੋਂ ਵੱਡਾ ਸ਼ਹਿਰ ਹੈ । ਲਹੌਰ ਪਾਕਿਸਤਾਨ ਦਾ ਸਿਆਸੀ, ਰਹਤਲੀ ਤੇ ਪੜ੍ਹਾਈ ਦਾ ਗੜ੍ਹ ਹੈ ਅਤੇ ਇਸ ਲਈ ਇਹਨੂੰ ਪਾਕਿਸਤਾਨ ਦਾ ਦਿਲ ਵੀ ਕਿਹਾ ਜਾਂਦਾ ਹੈ । ਲਹੌਰ ਦਰਿਆ-ਏ-ਰਾਵੀ ਦੇ ਕੰਢੇ ਤੇ ਵਸਦਾ ਹੈ ਤੇ ਇਸਦੀ ਲੋਕ ਗਿਣਤੀ ਇੱਕ ਕਰੋੜ ਦੇ ਨੇੜੇ ਹੈ ।","shahmukhi":"لہور پاکستانی پنجاب دا دارالحکومت اے۔ لوک گنتی دے نال کراچی توں بعد لہور دوجا سبھ توں وڈا شہر اے۔ لہور پاکستان دا سیاسی، رہتلی تے پڑھائی دا گڑھ اے تے اس لئی ایھنوں پاکستان دا دل وی کیھا جاندا اے۔ لہور دریاۓ راوی دے کنڈھے تے وسدا اے اسدی لوک گنتی اک کروڑ دے نیڑے اے ۔","tranliteration":"lahor pākistān panjāb dā dārul hakūmat ē. lōk giṇtī dē nāḷ karācī tō᷈ bāad lahor dūjā sab tō᷈ vaḍḍā shahr ē. lahor pākistān dā siāsī, rahtalī tē paṛā̀ī dā gā́ṛ ē tē is laī ihnū᷈ pākistān dā dil vī kehā jāndā ē. lahor dariāē rāvī dē kanḍē tē vasdā ē. isdī lōk giṇtī ikk karōṛ dē nēṛē ē."}
-, "employees": {"wc": ["෴ - 0xdf4 - 1","ණ - 0xdab - 1","් - 0xdca - 0","ණ - 0xdab - 1","្ - 0x17d2 - 0","෴ - 0xdf4 - 1","1 - 0x31 - 1","͏ - 0x34f - 0","2 - 0x32 - 1","⃝ - 0x20dd - 0"],"fancy":"1͏2⃝","v1":"γιγνώσκειν","v2":"ὦ ἄνδρες ᾿Αθηναῖοι","v1":"ახლავე გაიაროთ რეგისტრაცია","v2":"Unicode-ის მეათე საერთაშორისო","width":55,"sinhala":"෴ණ්ණ෴","width":4,"sinhala":"෴","width":1,"sinhala":"෴ණ්ණ෴෴ණ්ණ෴","width":8,"not-sinhala":"123456","tag":"ර්‍ඝ","width":2, "employee": [{"first-name":"Jim","nic-name":"\"რეგტ\"","last-name":"გთხოვთ ახ","department":431,"percent-time":90,"benefits":"full"}, {"first-name":"Terry","nic-name":"\"<one\"","last-name":"Οὐχὶ ταὐτὰ παρίσταταί μοι Jones","department":660,"percent-time":90,"benefits":"full"}, {"first-name":"Leslie","nic-name":"\"Les\"","last-name":"Patterson","department":341,"percent-time":60,"benefits":"full"}, {"first-name":"Ashley","nic-name":"\"Ash\"","last-name":"Meter & Smith","department":1440,"percent-time":40}, {"first-name":"0123456789","nic-name":"\"0123456789\"","last-name":"012345678901234567890","department":1440,"percent-time":40}, {"first-name":"ახლა","nic-name":"\"გაიარო\"","last-name":"საერთაშორისო","department":123,"percent-time":90,"benefits":"full"}, {"first-name":"෴ණ්ණ෴෴ණ්ණ෴","nic-name":"\"Mick\"","last-name":"෴ණ්ණ෴෴ණ්ණ෴෴ණ්ණ෴෴෴","department":110,"percent-time":20}]}
-}
+{"indian-languages": {"gurmukhi":"ਲਹੌਰ ਪਾਕਿਸਤਾਨੀ ਪੰਜਾਬ ਦੀ ਰਾਜਧਾਨੀ ਹੈ । ਲੋਕ ਗਿਣਤੀ ਦੇ ਨਾਲ ਕਰਾਚੀ ਤੋਂ ਬਾਅਦ ਲਹੌਰ ਦੂਜਾ ਸਭ ਤੋਂ ਵੱਡਾ ਸ਼ਹਿਰ ਹੈ । ਲਹੌਰ ਪਾਕਿਸਤਾਨ ਦਾ ਸਿਆਸੀ, ਰਹਤਲੀ ਤੇ ਪੜ੍ਹਾਈ ਦਾ ਗੜ੍ਹ ਹੈ ਅਤੇ ਇਸ ਲਈ ਇਹਨੂੰ ਪਾਕਿਸਤਾਨ ਦਾ ਦਿਲ ਵੀ ਕਿਹਾ ਜਾਂਦਾ ਹੈ । ਲਹੌਰ ਦਰਿਆ-ਏ-ਰਾਵੀ ਦੇ ਕੰਢੇ ਤੇ ਵਸਦਾ ਹੈ ਤੇ ਇਸਦੀ ਲੋਕ ਗਿਣਤੀ ਇੱਕ ਕਰੋੜ ਦੇ ਨੇੜੇ ਹੈ ।","shahmukhi":"لہور پاکستانی پنجاب دا دارالحکومت اے۔ لوک گنتی دے نال کراچی توں بعد لہور دوجا سبھ توں وڈا شہر اے۔ لہور پاکستان دا سیاسی، رہتلی تے پڑھائی دا گڑھ اے تے اس لئی ایھنوں پاکستان دا دل وی کیھا جاندا اے۔ لہور دریاۓ راوی دے کنڈھے تے وسدا اے اسدی لوک گنتی اک کروڑ دے نیڑے اے ۔","tranliteration":"lahor pākistān panjāb dā dārul hakūmat ē. lōk giṇtī dē nāḷ karācī tō᷈ bāad lahor dūjā sab tō᷈ vaḍḍā shahr ē. lahor pākistān dā siāsī, rahtalī tē paṛā̀ī dā gā́ṛ ē tē is laī ihnū᷈ pākistān dā dil vī kehā jāndā ē. lahor dariāē rāvī dē kanḍē tē vasdā ē. isdī lōk giṇtī ikk karōṛ dē nēṛē ē."}, "employees": {"wc": ["෴ - 0xdf4 - 1","ණ - 0xdab - 1","් - 0xdca - 0","ණ - 0xdab - 1","្ - 0x17d2 - 0","෴ - 0xdf4 - 1","1 - 0x31 - 1","͏ - 0x34f - 0","2 - 0x32 - 1","⃝ - 0x20dd - 0"],"fancy":"1͏2⃝","v1":"γιγνώσκειν","v2":"ὦ ἄνδρες ᾿Αθηναῖοι","v1":"ახლავე გაიაროთ რეგისტრაცია","v2":"Unicode-ის მეათე საერთაშორისო","width":55,"sinhala":"෴ණ්ණ෴","width":4,"sinhala":"෴","width":1,"sinhala":"෴ණ්ණ෴෴ණ්ණ෴","width":8,"not-sinhala":"123456","tag":"ර්‍ඝ","width":2, "employee": [{"first-name":"Jim","nic-name":"\"რეგტ\"","last-name":"გთხოვთ ახ","department":431,"percent-time":90,"benefits":"full"}, {"first-name":"Terry","nic-name":"\"<one\"","last-name":"Οὐχὶ ταὐτὰ παρίσταταί μοι Jones","department":660,"percent-time":90,"benefits":"full"}, {"first-name":"Leslie","nic-name":"\"Les\"","last-name":"Patterson","department":341,"percent-time":60,"benefits":"full"}, {"first-name":"Ashley","nic-name":"\"Ash\"","last-name":"Meter & Smith","department":1440,"percent-time":40}, {"first-name":"0123456789","nic-name":"\"0123456789\"","last-name":"012345678901234567890","department":1440,"percent-time":40}, {"first-name":"ახლა","nic-name":"\"გაიარო\"","last-name":"საერთაშორისო","department":123,"percent-time":90,"benefits":"full"}, {"first-name":"෴ණ්ණ෴෴ණ්ණ෴","nic-name":"\"Mick\"","last-name":"෴ණ්ණ෴෴ණ්ණ෴෴ණ්ණ෴෴෴","department":110,"percent-time":20}]}}
diff --git a/tests/core/saved/test_05.JP.out b/tests/core/saved/test_05.JP.out
index e027034e37c0..5bf14118cbaa 100644
--- a/tests/core/saved/test_05.JP.out
+++ b/tests/core/saved/test_05.JP.out
@@ -3,8 +3,7 @@
"gurmukhi": "ਲਹੌਰ ਪਾਕਿਸਤਾਨੀ ਪੰਜਾਬ ਦੀ ਰਾਜਧਾਨੀ ਹੈ । ਲੋਕ ਗਿਣਤੀ ਦੇ ਨਾਲ ਕਰਾਚੀ ਤੋਂ ਬਾਅਦ ਲਹੌਰ ਦੂਜਾ ਸਭ ਤੋਂ ਵੱਡਾ ਸ਼ਹਿਰ ਹੈ । ਲਹੌਰ ਪਾਕਿਸਤਾਨ ਦਾ ਸਿਆਸੀ, ਰਹਤਲੀ ਤੇ ਪੜ੍ਹਾਈ ਦਾ ਗੜ੍ਹ ਹੈ ਅਤੇ ਇਸ ਲਈ ਇਹਨੂੰ ਪਾਕਿਸਤਾਨ ਦਾ ਦਿਲ ਵੀ ਕਿਹਾ ਜਾਂਦਾ ਹੈ । ਲਹੌਰ ਦਰਿਆ-ਏ-ਰਾਵੀ ਦੇ ਕੰਢੇ ਤੇ ਵਸਦਾ ਹੈ ਤੇ ਇਸਦੀ ਲੋਕ ਗਿਣਤੀ ਇੱਕ ਕਰੋੜ ਦੇ ਨੇੜੇ ਹੈ ।",
"shahmukhi": "لہور پاکستانی پنجاب دا دارالحکومت اے۔ لوک گنتی دے نال کراچی توں بعد لہور دوجا سبھ توں وڈا شہر اے۔ لہور پاکستان دا سیاسی، رہتلی تے پڑھائی دا گڑھ اے تے اس لئی ایھنوں پاکستان دا دل وی کیھا جاندا اے۔ لہور دریاۓ راوی دے کنڈھے تے وسدا اے اسدی لوک گنتی اک کروڑ دے نیڑے اے ۔",
"tranliteration": "lahor pākistān panjāb dā dārul hakūmat ē. lōk giṇtī dē nāḷ karācī tō᷈ bāad lahor dūjā sab tō᷈ vaḍḍā shahr ē. lahor pākistān dā siāsī, rahtalī tē paṛā̀ī dā gā́ṛ ē tē is laī ihnū᷈ pākistān dā dil vī kehā jāndā ē. lahor dariāē rāvī dē kanḍē tē vasdā ē. isdī lōk giṇtī ikk karōṛ dē nēṛē ē."
- }
-,
+ },
"employees": {
"wc": [
"෴ - 0xdf4 - 1",
diff --git a/tests/core/saved/test_06.J.out b/tests/core/saved/test_06.J.out
index 4ba1fb1772d8..94e01146e735 100644
--- a/tests/core/saved/test_06.J.out
+++ b/tests/core/saved/test_06.J.out
@@ -1,2 +1 @@
-{"employees": {"employee": [{"first-name":"Terry","last-name":"Jones","department":660}, {"first-name":"Leslie","last-name":"Patterson","department":341}, {"first-name":"Ashley","last-name":"Smith","department":1440}]}
-}
+{"employees": {"employee": [{"first-name":"Terry","last-name":"Jones","department":660}, {"first-name":"Leslie","last-name":"Patterson","department":341}, {"first-name":"Ashley","last-name":"Smith","department":1440}]}}
diff --git a/tests/core/saved/test_07.J.out b/tests/core/saved/test_07.J.out
index 8e9efaec8000..bef0fea986e9 100644
--- a/tests/core/saved/test_07.J.out
+++ b/tests/core/saved/test_07.J.out
@@ -1,2 +1 @@
-{"employees": {"test": [{"filename":"(null)"}],"v1":"γιγνώσκειν","v2":"ὦ ἄνδρες ᾿Αθηναῖοι","columns":28,"columns":2,"v1":"ახლავე გაიაროთ რეგისტრაცია","v2":"Unicode-ის მეათე საერთაშორისო","columns":55,"columns":0, "employee": [{"first-name":"Jim","nic-name":"\"რეგტ\"","last-name":"გთხოვთ ახ","department":431,"percent-time":90,"columns":23,"benefits":"full"}, {"first-name":"Terry","nic-name":"\"<one\"","last-name":"Οὐχὶ ταὐτὰ παρίσταταί μοι Jones","department":660,"percent-time":90,"columns":47,"benefits":"full"}, {"first-name":"Leslie","nic-name":"\"Les\"","last-name":"Patterson","department":341,"percent-time":60,"columns":25,"benefits":"full"}, {"first-name":"Ashley","nic-name":"\"Ash\"","last-name":"Meter & Smith","department":1440,"percent-time":40,"columns":30}, {"first-name":"0123456789","nic-name":"\"0123456789\"","last-name":"012345678901234567890","department":1440,"percent-time":40,"columns":49}, {"first-name":"ახლა","nic-name":"\"გაიარო\"","last-name":"საერთაშორისო","department":123,"percent-time":90,"columns":29,"benefits":"full"}]}
-}
+{"employees": {"test": [{"filename":"(null)"}],"v1":"γιγνώσκειν","v2":"ὦ ἄνδρες ᾿Αθηναῖοι","columns":28,"columns":2,"v1":"ახლავე გაიაროთ რეგისტრაცია","v2":"Unicode-ის მეათე საერთაშორისო","columns":55,"columns":0, "employee": [{"first-name":"Jim","nic-name":"\"რეგტ\"","last-name":"გთხოვთ ახ","department":431,"percent-time":90,"columns":23,"benefits":"full"}, {"first-name":"Terry","nic-name":"\"<one\"","last-name":"Οὐχὶ ταὐτὰ παρίσταταί μοι Jones","department":660,"percent-time":90,"columns":47,"benefits":"full"}, {"first-name":"Leslie","nic-name":"\"Les\"","last-name":"Patterson","department":341,"percent-time":60,"columns":25,"benefits":"full"}, {"first-name":"Ashley","nic-name":"\"Ash\"","last-name":"Meter & Smith","department":1440,"percent-time":40,"columns":30}, {"first-name":"0123456789","nic-name":"\"0123456789\"","last-name":"012345678901234567890","department":1440,"percent-time":40,"columns":49}, {"first-name":"ახლა","nic-name":"\"გაიარო\"","last-name":"საერთაშორისო","department":123,"percent-time":90,"columns":29,"benefits":"full"}]}}
diff --git a/tests/core/saved/test_08.J.out b/tests/core/saved/test_08.J.out
index 9d897552beed..7cb5eb895ab7 100644
--- a/tests/core/saved/test_08.J.out
+++ b/tests/core/saved/test_08.J.out
@@ -1,2 +1 @@
-{"top": {"data": {"contents": {"item": [{"name":"gum","count":1412}, {"name":"rope","count":85}, {"name":"ladder","count":0}, {"name":"bolt","count":4123}, {"name":"water","count":17}]}}, "data2": {"contents": {"item": [{"name":"gum","count":1412}, {"name":"rope","count":85}, {"name":"ladder","count":0}, {"name":"bolt","count":4123}, {"name":"water","count":17}]}}, "data3": {"contents": {"item": [{"name":"gum","count":1412}, {"name":"rope","count":85}, {"name":"ladder","count":0}, {"name":"bolt","count":4123}, {"name":"water","count":17,"test":"one"}]}}, "data4": {"contents": {"item": [{"name":"gum","count":1412, "sub": [{"name":0,"next":1}, {"name":1,"next":2}, {"name":2,"next":3}],"last":3}, {"name":"rope","count":85, "sub": [{"name":0,"next":1}, {"name":1,"next":2}, {"name":2,"next":3}],"last":3}, {"name":"ladder","count":0, "sub": [{"name":0,"next":1}, {"name":1,"next":2}, {"name":2,"next":3}],"last":3}, {"name":"bolt","count":4123, "sub": [{"name":0,"next":1}, {"name":1,"next":2}, {"name":2,"next":3}],"last":3}, {"name":"water","count":17, "sub": [{"name":0,"next":1}, {"name":1,"next":2}, {"name":2,"next":3}],"last":3,"test":"one"}]}}}
-}
+{"top": {"data": {"contents": {"item": [{"name":"gum","count":1412}, {"name":"rope","count":85}, {"name":"ladder","count":0}, {"name":"bolt","count":4123}, {"name":"water","count":17}]}}, "data2": {"contents": {"item": [{"name":"gum","count":1412}, {"name":"rope","count":85}, {"name":"ladder","count":0}, {"name":"bolt","count":4123}, {"name":"water","count":17}]}}, "data3": {"contents": {"item": [{"name":"gum","count":1412}, {"name":"rope","count":85}, {"name":"ladder","count":0}, {"name":"bolt","count":4123}, {"name":"water","count":17,"test":"one"}]}}, "data4": {"contents": {"item": [{"name":"gum","count":1412, "sub": [{"name":0,"next":1}, {"name":1,"next":2}, {"name":2,"next":3}],"last":3}, {"name":"rope","count":85, "sub": [{"name":0,"next":1}, {"name":1,"next":2}, {"name":2,"next":3}],"last":3}, {"name":"ladder","count":0, "sub": [{"name":0,"next":1}, {"name":1,"next":2}, {"name":2,"next":3}],"last":3}, {"name":"bolt","count":4123, "sub": [{"name":0,"next":1}, {"name":1,"next":2}, {"name":2,"next":3}],"last":3}, {"name":"water","count":17, "sub": [{"name":0,"next":1}, {"name":1,"next":2}, {"name":2,"next":3}],"last":3,"test":"one"}]}}}}
diff --git a/tests/core/saved/test_09.J.out b/tests/core/saved/test_09.J.out
index e43ad3150c30..acd572638049 100644
--- a/tests/core/saved/test_09.J.out
+++ b/tests/core/saved/test_09.J.out
@@ -1,2 +1 @@
-{"top": {"data": {"contents": {"name": ["gum","rope","ladder","bolt","water"]}, "contents": {"item": ["gum","rope","ladder","bolt","water"]}, "contents": {"item": ["gum","rope","ladder","bolt","water"],"total":"six","one":"one", "two": ["two"],"three":"three"}}}
-}
+{"top": {"data": {"contents": {"name": ["gum","rope","ladder","bolt","water"]}, "contents": {"item": ["gum","rope","ladder","bolt","water"]}, "contents": {"item": ["gum","rope","ladder","bolt","water"],"total":"six","one":"one", "two": ["two"],"three":"three"}}}}
diff --git a/tests/core/saved/test_10.J.out b/tests/core/saved/test_10.J.out
index 5091685fc745..9d9d729fff2d 100644
--- a/tests/core/saved/test_10.J.out
+++ b/tests/core/saved/test_10.J.out
@@ -1,2 +1 @@
-{"__version": "3.1.4", "top": {"data": {"item": [],"data":"bold","data":"bold-ul","data":"triple","data":"inv-ul","data":"underline","data":"plain", "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}]}, "data": {"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}]}, "data": {"item": [{"sku":"GRO-000-533","name":"fish","sold":1321.0,"in-stock":45,"on-order":1}]}, "data": {"item": ["gum","rope","ladder","bolt","water"]},"cost":425,"cost":455}
-}
+{"__version": "3.1.4", "top": {"data": {"item": [],"data":"bold","data":"bold-ul","data":"triple","data":"inv-ul","data":"underline","data":"plain", "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}]}, "data": {"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}]}, "data": {"item": [{"sku":"GRO-000-533","name":"fish","sold":1321.0,"in-stock":45,"on-order":1}]}, "data": {"item": ["gum","rope","ladder","bolt","water"]},"cost":425,"cost":455}}
diff --git a/tests/core/saved/test_11.J.out b/tests/core/saved/test_11.J.out
index 4eb710503dbb..a574bbba921e 100644
--- a/tests/core/saved/test_11.J.out
+++ b/tests/core/saved/test_11.J.out
@@ -14,5 +14,4 @@
{{test-program: }}
{{An application 1011 log entry}}
-{"__version": "3.1.4", "top": {}
-}
+{"__version": "3.1.4", "top": {}}
diff --git a/tests/core/saved/test_12.E.err b/tests/core/saved/test_12.E.err
index e69de29bb2d1..32a5e4d30db3 100644
--- a/tests/core/saved/test_12.E.err
+++ b/tests/core/saved/test_12.E.err
@@ -0,0 +1,4 @@
+test_12.test: invalid XML tag name: '2by4'
+test_12.test: invalid XML tag name: '4x4'
+test_12.test: invalid XML tag name: '2morrow'
+test_12.test: invalid XML tag name: '2by4'
diff --git a/tests/core/saved/test_12.E.out b/tests/core/saved/test_12.E.out
index a831f5c64b6b..2ce69f151960 100644
--- a/tests/core/saved/test_12.E.out
+++ b/tests/core/saved/test_12.E.out
@@ -83,6 +83,10 @@ op content: [time] [2:15] [0]
op string: [hand] [left] [0]
op string: [color] [blue] [0]
op content: [time] [3:45] [0]
+op open_container: [2by4] [] [0x4040010]
+op string: [4x4] [truck] [0]
+op string: [2morrow] [tomorrow] [0]
+op close_container: [2by4] [] [0]
op close_instance: [thing] [] [0]
op close_list: [thing] [] [0]
op close_container: [data] [] [0]
diff --git a/tests/core/saved/test_12.H.err b/tests/core/saved/test_12.H.err
index e69de29bb2d1..32a5e4d30db3 100644
--- a/tests/core/saved/test_12.H.err
+++ b/tests/core/saved/test_12.H.err
@@ -0,0 +1,4 @@
+test_12.test: invalid XML tag name: '2by4'
+test_12.test: invalid XML tag name: '4x4'
+test_12.test: invalid XML tag name: '2morrow'
+test_12.test: invalid XML tag name: '2by4'
diff --git a/tests/core/saved/test_12.H.out b/tests/core/saved/test_12.H.out
index 2bd9a44d16f1..86f0b3476dfa 100644
--- a/tests/core/saved/test_12.H.out
+++ b/tests/core/saved/test_12.H.out
@@ -1 +1 @@
-<div class="line"><div class="text color-fg-red color-bg-green">Merry XMas!!</div></div><div class="line"><div class="text">One </div><div class="data color-fg-yellow color-bg-blue" data-tag="animal">fish</div><div class="text">, Two </div><div class="data color-fg-green color-bg-yellow" data-tag="animal">fish</div></div><div class="line"><div class="text">The </div><div class="data color-fg-red" data-tag="name">thing</div><div class="text"> is </div><div class="data color-fg-green" 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 color-fg-red" data-tag="hand">left</div><div class="text"> hand is </div><div class="data color-fg-blue" 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 color-fg-red" data-tag="name">thing</div><div class="text"> is </div><div class="data color-fg-green" 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 color-fg-red" data-tag="hand">left</div><div class="text"> hand is </div><div class="data color-fg-blue" 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 color-fg-red" data-tag="name">thing</div><div class="text"> is </div><div class="data color-fg-green" 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 color-fg-red" data-tag="hand">left</div><div class="text"> hand is </div><div class="data color-fg-blue" 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 color-fg-red" data-tag="name">thing</div><div class="text"> is </div><div class="data color-fg-green" 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 color-fg-red" data-tag="hand">left</div><div class="text"> hand is </div><div class="data color-fg-blue" 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 color-fg-red" data-tag="name">thing</div><div class="text"> is </div><div class="data color-fg-green" 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 color-fg-red" data-tag="hand">left</div><div class="text"> hand is </div><div class="data color-fg-blue" 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 color-fg-red" data-tag="name">thing</div><div class="text"> is </div><div class="data color-fg-green" 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 color-fg-red" data-tag="hand">left</div><div class="text"> hand is </div><div class="data color-fg-blue" 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 color-fg-red" data-tag="name">thing</div><div class="text"> is </div><div class="data color-fg-green" 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 color-fg-red" data-tag="hand">left</div><div class="text"> hand is </div><div class="data color-fg-blue" 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 color-fg-red" data-tag="name">thing</div><div class="text"> is </div><div class="data color-fg-green" 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 color-fg-red" data-tag="hand">left</div><div class="text"> hand is </div><div class="data color-fg-blue" 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 color-fg-red" data-tag="name">thing</div><div class="text"> is </div><div class="data color-fg-green" 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 color-fg-red" data-tag="hand">left</div><div class="text"> hand is </div><div class="data color-fg-blue" 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 color-fg-red" data-tag="name">thing</div><div class="text"> is </div><div class="data color-fg-green" 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 color-fg-red" data-tag="hand">left</div><div class="text"> hand is </div><div class="data color-fg-blue" data-tag="color">blue</div><div class="text"> til </div><div class="data" data-tag="time">03:45</div></div> \ No newline at end of file
+<div class="line"><div class="text color-fg-red color-bg-green">Merry XMas!!</div></div><div class="line"><div class="text">One </div><div class="data color-fg-yellow color-bg-blue" data-tag="animal">fish</div><div class="text">, Two </div><div class="data color-fg-green color-bg-yellow" data-tag="animal">fish</div></div><div class="line"><div class="text">The </div><div class="data color-fg-red" data-tag="name">thing</div><div class="text"> is </div><div class="data color-fg-green" 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 color-fg-red" data-tag="hand">left</div><div class="text"> hand is </div><div class="data color-fg-blue" 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 color-fg-red" data-tag="name">thing</div><div class="text"> is </div><div class="data color-fg-green" 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 color-fg-red" data-tag="hand">left</div><div class="text"> hand is </div><div class="data color-fg-blue" 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 color-fg-red" data-tag="name">thing</div><div class="text"> is </div><div class="data color-fg-green" 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 color-fg-red" data-tag="hand">left</div><div class="text"> hand is </div><div class="data color-fg-blue" 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 color-fg-red" data-tag="name">thing</div><div class="text"> is </div><div class="data color-fg-green" 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 color-fg-red" data-tag="hand">left</div><div class="text"> hand is </div><div class="data color-fg-blue" 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 color-fg-red" data-tag="name">thing</div><div class="text"> is </div><div class="data color-fg-green" 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 color-fg-red" data-tag="hand">left</div><div class="text"> hand is </div><div class="data color-fg-blue" 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 color-fg-red" data-tag="name">thing</div><div class="text"> is </div><div class="data color-fg-green" 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 color-fg-red" data-tag="hand">left</div><div class="text"> hand is </div><div class="data color-fg-blue" 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 color-fg-red" data-tag="name">thing</div><div class="text"> is </div><div class="data color-fg-green" 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 color-fg-red" data-tag="hand">left</div><div class="text"> hand is </div><div class="data color-fg-blue" 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 color-fg-red" data-tag="name">thing</div><div class="text"> is </div><div class="data color-fg-green" 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 color-fg-red" data-tag="hand">left</div><div class="text"> hand is </div><div class="data color-fg-blue" 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 color-fg-red" data-tag="name">thing</div><div class="text"> is </div><div class="data color-fg-green" 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 color-fg-red" data-tag="hand">left</div><div class="text"> hand is </div><div class="data color-fg-blue" 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 color-fg-red" data-tag="name">thing</div><div class="text"> is </div><div class="data color-fg-green" 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 color-fg-red" data-tag="hand">left</div><div class="text"> hand is </div><div class="data color-fg-blue" 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">There is </div><div class="data" data-tag="4x4">truck</div><div class="text"> in </div><div class="data" data-tag="2morrow">tomorrow</div></div> \ No newline at end of file
diff --git a/tests/core/saved/test_12.HIPx.err b/tests/core/saved/test_12.HIPx.err
index e69de29bb2d1..32a5e4d30db3 100644
--- a/tests/core/saved/test_12.HIPx.err
+++ b/tests/core/saved/test_12.HIPx.err
@@ -0,0 +1,4 @@
+test_12.test: invalid XML tag name: '2by4'
+test_12.test: invalid XML tag name: '4x4'
+test_12.test: invalid XML tag name: '2morrow'
+test_12.test: invalid XML tag name: '2by4'
diff --git a/tests/core/saved/test_12.HIPx.out b/tests/core/saved/test_12.HIPx.out
index dab1975730d7..a5588c14493b 100644
--- a/tests/core/saved/test_12.HIPx.out
+++ b/tests/core/saved/test_12.HIPx.out
@@ -167,3 +167,9 @@
<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">There is </div>
+ <div class="data" data-tag="4x4" data-xpath="/top/data/thing[name = 'thing']/2by4/4x4">truck</div>
+ <div class="text"> in </div>
+ <div class="data" data-tag="2morrow" data-xpath="/top/data/thing[name = 'thing']/2by4/2morrow">tomorrow</div>
+</div>
diff --git a/tests/core/saved/test_12.HP.err b/tests/core/saved/test_12.HP.err
index e69de29bb2d1..32a5e4d30db3 100644
--- a/tests/core/saved/test_12.HP.err
+++ b/tests/core/saved/test_12.HP.err
@@ -0,0 +1,4 @@
+test_12.test: invalid XML tag name: '2by4'
+test_12.test: invalid XML tag name: '4x4'
+test_12.test: invalid XML tag name: '2morrow'
+test_12.test: invalid XML tag name: '2by4'
diff --git a/tests/core/saved/test_12.HP.out b/tests/core/saved/test_12.HP.out
index 54e3dfbda882..f0b04abf3397 100644
--- a/tests/core/saved/test_12.HP.out
+++ b/tests/core/saved/test_12.HP.out
@@ -167,3 +167,9 @@
<div class="text"> til </div>
<div class="data" data-tag="time">03:45</div>
</div>
+<div class="line">
+ <div class="text">There is </div>
+ <div class="data" data-tag="4x4">truck</div>
+ <div class="text"> in </div>
+ <div class="data" data-tag="2morrow">tomorrow</div>
+</div>
diff --git a/tests/core/saved/test_12.J.err b/tests/core/saved/test_12.J.err
index e69de29bb2d1..32a5e4d30db3 100644
--- a/tests/core/saved/test_12.J.err
+++ b/tests/core/saved/test_12.J.err
@@ -0,0 +1,4 @@
+test_12.test: invalid XML tag name: '2by4'
+test_12.test: invalid XML tag name: '4x4'
+test_12.test: invalid XML tag name: '2morrow'
+test_12.test: invalid XML tag name: '2by4'
diff --git a/tests/core/saved/test_12.J.out b/tests/core/saved/test_12.J.out
index fb85ce88c85d..25a2416195a9 100644
--- a/tests/core/saved/test_12.J.out
+++ b/tests/core/saved/test_12.J.out
@@ -1,2 +1 @@
-{"top": {"data": {"animal":"fish","animal":"fish", "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"}]}}
-}
+{"top": {"data": {"animal":"fish","animal":"fish", "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", "2by4": {"4x4":"truck","2morrow":"tomorrow"}}]}}}
diff --git a/tests/core/saved/test_12.JP.err b/tests/core/saved/test_12.JP.err
index e69de29bb2d1..32a5e4d30db3 100644
--- a/tests/core/saved/test_12.JP.err
+++ b/tests/core/saved/test_12.JP.err
@@ -0,0 +1,4 @@
+test_12.test: invalid XML tag name: '2by4'
+test_12.test: invalid XML tag name: '4x4'
+test_12.test: invalid XML tag name: '2morrow'
+test_12.test: invalid XML tag name: '2by4'
diff --git a/tests/core/saved/test_12.JP.out b/tests/core/saved/test_12.JP.out
index 85ab2837a83a..0095d8dcc59a 100644
--- a/tests/core/saved/test_12.JP.out
+++ b/tests/core/saved/test_12.JP.out
@@ -82,7 +82,11 @@
"time": "2:15",
"hand": "left",
"color": "blue",
- "time": "3:45"
+ "time": "3:45",
+ "2by4": {
+ "4x4": "truck",
+ "2morrow": "tomorrow"
+ }
}
]
}
diff --git a/tests/core/saved/test_12.T.err b/tests/core/saved/test_12.T.err
index e69de29bb2d1..32a5e4d30db3 100644
--- a/tests/core/saved/test_12.T.err
+++ b/tests/core/saved/test_12.T.err
@@ -0,0 +1,4 @@
+test_12.test: invalid XML tag name: '2by4'
+test_12.test: invalid XML tag name: '4x4'
+test_12.test: invalid XML tag name: '2morrow'
+test_12.test: invalid XML tag name: '2by4'
diff --git a/tests/core/saved/test_12.T.out b/tests/core/saved/test_12.T.out
index 83a96a119a40..42ee933808d7 100644
--- a/tests/core/saved/test_12.T.out
+++ b/tests/core/saved/test_12.T.out
@@ -20,3 +20,4 @@ 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
+There is truck in tomorrow
diff --git a/tests/core/saved/test_12.X.err b/tests/core/saved/test_12.X.err
index e69de29bb2d1..32a5e4d30db3 100644
--- a/tests/core/saved/test_12.X.err
+++ b/tests/core/saved/test_12.X.err
@@ -0,0 +1,4 @@
+test_12.test: invalid XML tag name: '2by4'
+test_12.test: invalid XML tag name: '4x4'
+test_12.test: invalid XML tag name: '2morrow'
+test_12.test: invalid XML tag name: '2by4'
diff --git a/tests/core/saved/test_12.X.out b/tests/core/saved/test_12.X.out
index 28a30f755e66..072444615c79 100644
--- a/tests/core/saved/test_12.X.out
+++ b/tests/core/saved/test_12.X.out
@@ -1 +1 @@
-<top><data><animal>fish</animal><animal>fish</animal><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> \ No newline at end of file
+<top><data><animal>fish</animal><animal>fish</animal><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><_2by4><_4x4>truck</_4x4><_2morrow>tomorrow</_2morrow></_2by4></thing></data></top> \ No newline at end of file
diff --git a/tests/core/saved/test_12.XP.err b/tests/core/saved/test_12.XP.err
index e69de29bb2d1..32a5e4d30db3 100644
--- a/tests/core/saved/test_12.XP.err
+++ b/tests/core/saved/test_12.XP.err
@@ -0,0 +1,4 @@
+test_12.test: invalid XML tag name: '2by4'
+test_12.test: invalid XML tag name: '4x4'
+test_12.test: invalid XML tag name: '2morrow'
+test_12.test: invalid XML tag name: '2by4'
diff --git a/tests/core/saved/test_12.XP.out b/tests/core/saved/test_12.XP.out
index 37ef1bde0659..b3dc5992fbe9 100644
--- a/tests/core/saved/test_12.XP.out
+++ b/tests/core/saved/test_12.XP.out
@@ -81,6 +81,10 @@
<hand>left</hand>
<color>blue</color>
<time>3:45</time>
+ <_2by4>
+ <_4x4>truck</_4x4>
+ <_2morrow>tomorrow</_2morrow>
+ </_2by4>
</thing>
</data>
</top>
diff --git a/tests/core/test_12.c b/tests/core/test_12.c
index f6021e749885..6a974b5268d5 100644
--- a/tests/core/test_12.c
+++ b/tests/core/test_12.c
@@ -78,6 +78,11 @@ main (int argc, char **argv)
xo_emit_f(flags, fmt2, "left", "blue", "blue", 3, 45);
}
+ xo_open_container("2by4");
+ xo_emit("There is {:4x4} in {:2morrow}\n", "truck", "tomorrow");
+ xo_close_container("2by4");
+
+
xo_close_container("data");
xo_close_container_h(NULL, "top");
diff --git a/tests/gettext/po/pig_latin/strerror.po b/tests/gettext/po/pig_latin/strerror.po
index 9b62dd71245d..cffae6512c22 100644
--- a/tests/gettext/po/pig_latin/strerror.po
+++ b/tests/gettext/po/pig_latin/strerror.po
@@ -457,3 +457,7 @@ msgstr "Atestay otnay ecoverableray"
# 96 - EOWNERDEAD
msgid "Previous owner died"
msgstr "Eviouspray ownerway iedday"
+
+# 97 - EINTEGRITY
+msgid "Integrity check failed"
+msgstr "Integrityway eckchey ailedfey"
diff --git a/tests/gettext/saved/gt_01.J.out b/tests/gettext/saved/gt_01.J.out
index 7380c76f578c..2d0e7cc63ba8 100644
--- a/tests/gettext/saved/gt_01.J.out
+++ b/tests/gettext/saved/gt_01.J.out
@@ -1,2 +1 @@
-{"top": {"adjective":"amingflay","noun":"ordsway","verb":"urningbay","owner":"ymay","target":"ouchcay","adjective":"amingflay","noun":"ordsway","verb":"urningbay","owner":"ymay","target":"ouchcay", "bytes": [0,1,2,3,4],"total":1234,"received":1234,"from":"foop","port":4321,"time":32,"received":1234,"from":"foop","port":4321,"time":32,"received":1234,"from":"foop","port":4321,"time":32,"marzlevanes":3,"version":"1.2.3","date":"Tue Jun 23 18:47:09 UTC 2015", "__warning": {"program":"gt_01.test","message":"Nableuay otay ectulatobjay orwardfay elocipingvay","verb":"ectulatobjay","error":"Ermissionpay eniedday"}, "__warning": {"program":"gt_01.test","message":"automaticyay ynchronizationsay ofyay ardinalyay ammetersgray ailedfay","style":"automaticyay","type":"ardinalyay","target":"ammetersgray","error":"Ermissionpay eniedday"},"marzlevanes":6,"windings":"otuslay-oyay-eltayay"}
-}
+{"top": {"adjective":"amingflay","noun":"ordsway","verb":"urningbay","owner":"ymay","target":"ouchcay","adjective":"amingflay","noun":"ordsway","verb":"urningbay","owner":"ymay","target":"ouchcay", "bytes": [0,1,2,3,4],"total":1234,"received":1234,"from":"foop","port":4321,"time":32,"received":1234,"from":"foop","port":4321,"time":32,"received":1234,"from":"foop","port":4321,"time":32,"marzlevanes":3,"version":"1.2.3","date":"Tue Jun 23 18:47:09 UTC 2015", "__warning": {"program":"gt_01.test","message":"Nableuay otay ectulatobjay orwardfay elocipingvay","verb":"ectulatobjay","error":"Ermissionpay eniedday"}, "__warning": {"program":"gt_01.test","message":"automaticyay ynchronizationsay ofyay ardinalyay ammetersgray ailedfay","style":"automaticyay","type":"ardinalyay","target":"ammetersgray","error":"Ermissionpay eniedday"},"marzlevanes":6,"windings":"otuslay-oyay-eltayay"}}
diff --git a/tests/gettext/strerror.pot b/tests/gettext/strerror.pot
index 475162d5413d..63da80d14325 100644
--- a/tests/gettext/strerror.pot
+++ b/tests/gettext/strerror.pot
@@ -466,3 +466,7 @@ msgstr ""
# 96 - EOWNERDEAD
msgid "Previous owner died"
msgstr ""
+
+# 97 - EINTEGRITY
+msgid "Integrity check failed"
+msgstr ""
diff --git a/tests/xo/Makefile.am b/tests/xo/Makefile.am
index 22698f87154f..1f3636d9345d 100644
--- a/tests/xo/Makefile.am
+++ b/tests/xo/Makefile.am
@@ -12,14 +12,8 @@ AM_CFLAGS = -I${top_srcdir} -I${top_srcdir}/libxo
# Ick: maintained by hand!
TEST_CASES = \
-xo_01.sh
-
-X=\
-xo_02.sh \
-xo_03.sh \
-xo_04.sh \
-xo_05.sh \
-xo_06.sh
+xo_01.sh \
+xo_02.sh
# TEST_CASES := $(shell cd ${srcdir} ; echo *.c )
diff --git a/tests/xo/saved/xo_01.H.out b/tests/xo/saved/xo_01.H.out
index 6d115c9abfae..a8b0e4783c8d 100644
--- a/tests/xo/saved/xo_01.H.out
+++ b/tests/xo/saved/xo_01.H.out
@@ -1 +1 @@
-<div class="line"><div class="text">Item </div><div class="data" data-tag="name">one</div><div class="text"> is </div><div class="label">number</div><div class="padding"> </div><div class="data" data-tag="value">001</div><div class="text">, </div><div class="label">color</div><div class="decoration">:</div><div class="padding"> </div><div class="data" data-tag="color">red</div></div><div class="line"><div class="text">Item </div><div class="data" data-tag="name">two</div><div class="text"> is </div><div class="label">number</div><div class="padding"> </div><div class="data" data-tag="value">002</div><div class="text">, </div><div class="label">color</div><div class="decoration">:</div><div class="padding"> </div><div class="data" data-tag="color">blue</div></div><div class="line"><div class="text">Item </div><div class="data" data-tag="name">three</div><div class="text"> is </div><div class="label">number</div><div class="padding"> </div><div class="data" data-tag="value">003</div><div class="text">, </div><div class="label">color</div><div class="decoration">:</div><div class="padding"> </div><div class="data" data-tag="color">green</div></div><div class="line"><div class="text">Item </div><div class="data" data-tag="name">four</div><div class="text"> is </div><div class="label">number</div><div class="padding"> </div><div class="data" data-tag="value">004</div><div class="text">, </div><div class="label">color</div><div class="decoration">:</div><div class="padding"> </div><div class="data" data-tag="color">yellow</div></div><div class="padding"> </div><div class="line"><div class="data" data-tag="address">0xdeadbeef</div><div class="text">..</div><div class="data" data-tag="foo">1</div></div><div class="padding"> </div><div class="line"><div class="data" data-tag="address">0xdeadbeef</div><div class="text">..</div><div class="data" data-tag="foo">1</div></div><div class="padding"> </div><div class="line"><div class="data" data-tag="address">0xdeadbeef</div><div class="text">..</div><div class="data" data-tag="foo">1</div></div><div class="padding"> </div><div class="line"><div class="data" data-tag="address">0xdeadbeef</div><div class="text">..</div><div class="data" data-tag="foo">1</div></div> \ No newline at end of file
+<div class="line"><div class="text">Item </div><div class="data" data-tag="name">one</div><div class="text"> is </div><div class="label">number</div><div class="padding"> </div><div class="data" data-tag="value">001</div><div class="text">, </div><div class="label">color</div><div class="decoration">:</div><div class="padding"> </div><div class="data" data-tag="color">red</div></div><div class="line"><div class="text">Item </div><div class="data" data-tag="name">two</div><div class="text"> is </div><div class="label">number</div><div class="padding"> </div><div class="data" data-tag="value">002</div><div class="text">, </div><div class="label">color</div><div class="decoration">:</div><div class="padding"> </div><div class="data" data-tag="color">blue</div></div><div class="line"><div class="text">Item </div><div class="data" data-tag="name">three</div><div class="text"> is </div><div class="label">number</div><div class="padding"> </div><div class="data" data-tag="value">003</div><div class="text">, </div><div class="label">color</div><div class="decoration">:</div><div class="padding"> </div><div class="data" data-tag="color">green</div></div><div class="line"><div class="text">Item </div><div class="data" data-tag="name">four</div><div class="text"> is </div><div class="label">number</div><div class="padding"> </div><div class="data" data-tag="value">004</div><div class="text">, </div><div class="label">color</div><div class="decoration">:</div><div class="padding"> </div><div class="data" data-tag="color">yellow</div></div><div class="padding"> </div><div class="line"><div class="data" data-tag="address">0xdeadbeef</div><div class="text">..</div><div class="data" data-tag="foo">1</div></div><div class="padding"> </div><div class="line"><div class="data" data-tag="address">0xdeadbeef</div><div class="text">..</div><div class="data" data-tag="foo">1</div></div><div class="padding"> </div><div class="line"><div class="data" data-tag="address">0xdeadbeef</div><div class="text">..</div><div class="data" data-tag="foo">1</div></div><div class="padding"> </div><div class="line"><div class="data" data-tag="address">0xdeadbeef</div><div class="text">..</div><div class="data" data-tag="foo">1</div></div><div class="line"><div class="text">partial line</div><div class="text"> -- rest of line</div></div> \ No newline at end of file
diff --git a/tests/xo/saved/xo_01.HIPx.out b/tests/xo/saved/xo_01.HIPx.out
index 66ba7b6a4d80..51d57cd731f0 100644
--- a/tests/xo/saved/xo_01.HIPx.out
+++ b/tests/xo/saved/xo_01.HIPx.out
@@ -74,3 +74,7 @@
<div class="text">..</div>
<div class="data" data-tag="foo" data-xpath="/anchor/foo">1</div>
</div>
+<div class="line">
+ <div class="text">partial line</div>
+ <div class="text"> -- rest of line</div>
+</div>
diff --git a/tests/xo/saved/xo_01.HP.out b/tests/xo/saved/xo_01.HP.out
index 2bda085da4f2..1f8bce7047c7 100644
--- a/tests/xo/saved/xo_01.HP.out
+++ b/tests/xo/saved/xo_01.HP.out
@@ -74,3 +74,7 @@
<div class="text">..</div>
<div class="data" data-tag="foo">1</div>
</div>
+<div class="line">
+ <div class="text">partial line</div>
+ <div class="text"> -- rest of line</div>
+</div>
diff --git a/tests/xo/saved/xo_01.J.out b/tests/xo/saved/xo_01.J.out
index 7bf42260ffad..a372383b2f90 100644
--- a/tests/xo/saved/xo_01.J.out
+++ b/tests/xo/saved/xo_01.J.out
@@ -1 +1 @@
-"top": {"item": {"name":"one","value":1,"color":"red"}, "item": {"name":"two","value":2,"color":"blue"}, "item": {"name":"three","value":3,"color":"green"}, "item": {"name":"four","value":4,"color":"yellow"}, "anchor": {"address":"0xdeadbeef","foo":1}, "anchor": {"address":"0xdeadbeef","foo":1}, "anchor": {"address":"0xdeadbeef","foo":1}, "anchor": {"address":"0xdeadbeef","foo":1}}
+"top": {"item": {"name":"one","value":1,"color":"red"}, "item": {"name":"two","value":2,"color":"blue"}, "item": {"name":"three","value":3,"color":"green"}, "item": {"name":"four","value":4,"color":"yellow"}, "anchor": {"address":"0xdeadbeef","foo":1}, "anchor": {"address":"0xdeadbeef","foo":1}, "anchor": {"address":"0xdeadbeef","foo":1}, "anchor": {"address":"0xdeadbeef","foo":1}} \ No newline at end of file
diff --git a/tests/xo/saved/xo_01.JP.out b/tests/xo/saved/xo_01.JP.out
index 95ac0e244e99..80c2b311b90e 100644
--- a/tests/xo/saved/xo_01.JP.out
+++ b/tests/xo/saved/xo_01.JP.out
@@ -35,4 +35,4 @@
"address": "0xdeadbeef",
"foo": 1
}
-}
+} \ No newline at end of file
diff --git a/tests/xo/saved/xo_01.T.out b/tests/xo/saved/xo_01.T.out
index 8b67427be4a0..731603a03d80 100644
--- a/tests/xo/saved/xo_01.T.out
+++ b/tests/xo/saved/xo_01.T.out
@@ -6,3 +6,4 @@ Item four is number 004, color: yellow
0xdeadbeef..1
0xdeadbeef..1
0xdeadbeef..1
+partial line -- rest of line
diff --git a/tests/xo/saved/xo_02.H.err b/tests/xo/saved/xo_02.H.err
new file mode 100644
index 000000000000..0c348c734898
--- /dev/null
+++ b/tests/xo/saved/xo_02.H.err
@@ -0,0 +1,25 @@
+Usage: xo [options] format [fields]
+ --close <path> Close tags for the given path
+ --close-instance <name> Close an open instance name
+ --close-list <name> Close an open list name
+ --continuation OR -C Output belongs on same line as previous output
+ --depth <num> Set the depth for pretty printing
+ --help Display this help text
+ --html OR -H Generate HTML output
+ --json OR -J Generate JSON output
+ --leading-xpath <path> OR -l <path> Add a prefix to generated XPaths (HTML)
+ --not-first Indicate this object is not the first (JSON)
+ --open <path> Open tags for the given path
+ --open-instance <name> Open an instance given by name
+ --open-list <name> Open a list given by name
+ --option <opts> -or -O <opts> Give formatting options
+ --pretty OR -p Make 'pretty' output (add indent, newlines)
+ --style <style> OR -s <style> Generate given style (xml, json, text, html)
+ --text OR -T Generate text output (the default style)
+ --top-wrap Generate a top-level object wrapper (JSON)
+ --version Display version information
+ --warn OR -W Display warnings in text on stderr
+ --warn-xml Display warnings in xml on stdout
+ --wrap <path> Wrap output in a set of containers
+ --xml OR -X Generate XML output
+ --xpath Add XPath data to HTML output
diff --git a/tests/xo/saved/xo_02.H.out b/tests/xo/saved/xo_02.H.out
new file mode 100644
index 000000000000..3eb4ec6a1b85
--- /dev/null
+++ b/tests/xo/saved/xo_02.H.out
@@ -0,0 +1 @@
+<div class="line"><div class="text">The capital of </div><div class="data" data-tag="state">North Carolina</div><div class="text"> is </div><div class="data" data-tag="city">Raleigh</div></div><div class="line"><div class="text">The capital of </div><div class="data" data-tag="state">North Carolina</div><div class="text"> is </div><div class="data" data-tag="city">Raleigh</div></div><div class="line"><div class="text">The capital of </div><div class="data" data-tag="state">North Carolina</div><div class="text"> is </div><div class="data" data-tag="city">Raleigh</div></div><div class="line"><div class="text">The capital of </div><div class="data" data-tag="state">North Carolina</div><div class="text"> is </div><div class="data" data-tag="city">Raleigh</div></div><div class="line"><div class="text">The capital of </div><div class="data" data-tag="state">North Carolina</div><div class="text"> is </div><div class="data" data-tag="city">Raleigh</div></div><div class="line"><div class="text">The capital of </div><div class="data" data-tag="state">North Carolina</div><div class="text"> is </div><div class="data" data-tag="city">Raleigh</div></div><div class="line"><div class="text">The capital of </div><div class="data" data-tag="state">North Carolina</div><div class="text"> is </div><div class="data" data-tag="city">Raleigh</div></div><div class="line"><div class="text">The capital of </div><div class="data" data-tag="state">North Carolina</div><div class="text"> is </div><div class="data" data-tag="city">Raleigh</div></div><div class="line"><div class="text">test</div></div><div class="line"><div class="text">Answer:</div><div class="text">The capital of </div><div class="data" data-tag="state">North Carolina</div><div class="text"> is </div><div class="data" data-tag="city">Raleigh</div></div><div class="line"><div class="text">First </div><div class="data" data-tag="tag">value1</div><div class="text"> </div><div class="text">and then </div><div class="data" data-tag="tag">value2</div></div><div class="line"><div class="text">Machine </div><div class="data" data-tag="name">red</div><div class="text"> has </div><div class="data" data-tag="memory">55</div></div><div class="line"><div class="text">Machine </div><div class="data" data-tag="name">green</div><div class="text"> has </div><div class="data" data-tag="memory">55</div></div><div class="line"><div class="text">Machine </div><div class="data" data-tag="name">blue</div><div class="text"> has </div><div class="data" data-tag="memory">55</div></div> \ No newline at end of file
diff --git a/tests/xo/saved/xo_02.HIPx.err b/tests/xo/saved/xo_02.HIPx.err
new file mode 100644
index 000000000000..0c348c734898
--- /dev/null
+++ b/tests/xo/saved/xo_02.HIPx.err
@@ -0,0 +1,25 @@
+Usage: xo [options] format [fields]
+ --close <path> Close tags for the given path
+ --close-instance <name> Close an open instance name
+ --close-list <name> Close an open list name
+ --continuation OR -C Output belongs on same line as previous output
+ --depth <num> Set the depth for pretty printing
+ --help Display this help text
+ --html OR -H Generate HTML output
+ --json OR -J Generate JSON output
+ --leading-xpath <path> OR -l <path> Add a prefix to generated XPaths (HTML)
+ --not-first Indicate this object is not the first (JSON)
+ --open <path> Open tags for the given path
+ --open-instance <name> Open an instance given by name
+ --open-list <name> Open a list given by name
+ --option <opts> -or -O <opts> Give formatting options
+ --pretty OR -p Make 'pretty' output (add indent, newlines)
+ --style <style> OR -s <style> Generate given style (xml, json, text, html)
+ --text OR -T Generate text output (the default style)
+ --top-wrap Generate a top-level object wrapper (JSON)
+ --version Display version information
+ --warn OR -W Display warnings in text on stderr
+ --warn-xml Display warnings in xml on stdout
+ --wrap <path> Wrap output in a set of containers
+ --xml OR -X Generate XML output
+ --xpath Add XPath data to HTML output
diff --git a/tests/xo/saved/xo_02.HIPx.out b/tests/xo/saved/xo_02.HIPx.out
new file mode 100644
index 000000000000..7a78a6b542cd
--- /dev/null
+++ b/tests/xo/saved/xo_02.HIPx.out
@@ -0,0 +1,83 @@
+<div class="line">
+ <div class="text">The capital of </div>
+ <div class="data" data-tag="state" data-xpath="/a/b/c/state">North Carolina</div>
+ <div class="text"> is </div>
+ <div class="data" data-tag="city" data-xpath="/a/b/c/city">Raleigh</div>
+</div>
+<div class="line">
+ <div class="text">The capital of </div>
+ <div class="data" data-tag="state" data-xpath="/state">North Carolina</div>
+ <div class="text"> is </div>
+ <div class="data" data-tag="city" data-xpath="/city">Raleigh</div>
+</div>
+<div class="line">
+ <div class="text">The capital of </div>
+ <div class="data" data-tag="state" data-xpath="/a/b/c/state">North Carolina</div>
+ <div class="text"> is </div>
+ <div class="data" data-tag="city" data-xpath="/a/b/c/city">Raleigh</div>
+</div>
+<div class="line">
+ <div class="text">The capital of </div>
+ <div class="data" data-tag="state" data-xpath="/a/b/c/state">North Carolina</div>
+ <div class="text"> is </div>
+ <div class="data" data-tag="city" data-xpath="/a/b/c/city">Raleigh</div>
+</div>
+<div class="line">
+ <div class="text">The capital of </div>
+ <div class="data" data-tag="state" data-xpath="/d/e/f/state">North Carolina</div>
+ <div class="text"> is </div>
+ <div class="data" data-tag="city" data-xpath="/d/e/f/city">Raleigh</div>
+</div>
+<div class="line">
+ <div class="text">The capital of </div>
+ <div class="data" data-tag="state" data-xpath="/state">North Carolina</div>
+ <div class="text"> is </div>
+ <div class="data" data-tag="city" data-xpath="/city">Raleigh</div>
+</div>
+<div class="line">
+ <div class="text">The capital of </div>
+ <div class="data" data-tag="state" data-xpath="/a/b/c/state">North Carolina</div>
+ <div class="text"> is </div>
+ <div class="data" data-tag="city" data-xpath="/a/b/c/city">Raleigh</div>
+</div>
+<div class="line">
+ <div class="text">The capital of </div>
+ <div class="data" data-tag="state" data-xpath="/a/b/c/state">North Carolina</div>
+ <div class="text"> is </div>
+ <div class="data" data-tag="city" data-xpath="/a/b/c/city">Raleigh</div>
+</div>
+<div class="line">
+ <div class="text">test</div>
+</div>
+<div class="line">
+ <div class="text">Answer:</div>
+ <div class="text">The capital of </div>
+ <div class="data" data-tag="state" data-xpath="/state">North Carolina</div>
+ <div class="text"> is </div>
+ <div class="data" data-tag="city" data-xpath="/city">Raleigh</div>
+</div>
+<div class="line">
+ <div class="text">First </div>
+ <div class="data" data-tag="tag" data-xpath="/tag">value1</div>
+ <div class="text"> </div>
+ <div class="text">and then </div>
+ <div class="data" data-tag="tag" data-xpath="/tag">value2</div>
+</div>
+<div class="line">
+ <div class="text">Machine </div>
+ <div class="data" data-tag="name" data-xpath="/name">red</div>
+ <div class="text"> has </div>
+ <div class="data" data-tag="memory" data-xpath="/memory">55</div>
+</div>
+<div class="line">
+ <div class="text">Machine </div>
+ <div class="data" data-tag="name" data-xpath="/name">green</div>
+ <div class="text"> has </div>
+ <div class="data" data-tag="memory" data-xpath="/memory">55</div>
+</div>
+<div class="line">
+ <div class="text">Machine </div>
+ <div class="data" data-tag="name" data-xpath="/name">blue</div>
+ <div class="text"> has </div>
+ <div class="data" data-tag="memory" data-xpath="/memory">55</div>
+</div>
diff --git a/tests/xo/saved/xo_02.HP.err b/tests/xo/saved/xo_02.HP.err
new file mode 100644
index 000000000000..0c348c734898
--- /dev/null
+++ b/tests/xo/saved/xo_02.HP.err
@@ -0,0 +1,25 @@
+Usage: xo [options] format [fields]
+ --close <path> Close tags for the given path
+ --close-instance <name> Close an open instance name
+ --close-list <name> Close an open list name
+ --continuation OR -C Output belongs on same line as previous output
+ --depth <num> Set the depth for pretty printing
+ --help Display this help text
+ --html OR -H Generate HTML output
+ --json OR -J Generate JSON output
+ --leading-xpath <path> OR -l <path> Add a prefix to generated XPaths (HTML)
+ --not-first Indicate this object is not the first (JSON)
+ --open <path> Open tags for the given path
+ --open-instance <name> Open an instance given by name
+ --open-list <name> Open a list given by name
+ --option <opts> -or -O <opts> Give formatting options
+ --pretty OR -p Make 'pretty' output (add indent, newlines)
+ --style <style> OR -s <style> Generate given style (xml, json, text, html)
+ --text OR -T Generate text output (the default style)
+ --top-wrap Generate a top-level object wrapper (JSON)
+ --version Display version information
+ --warn OR -W Display warnings in text on stderr
+ --warn-xml Display warnings in xml on stdout
+ --wrap <path> Wrap output in a set of containers
+ --xml OR -X Generate XML output
+ --xpath Add XPath data to HTML output
diff --git a/tests/xo/saved/xo_02.HP.out b/tests/xo/saved/xo_02.HP.out
new file mode 100644
index 000000000000..3e97e691540c
--- /dev/null
+++ b/tests/xo/saved/xo_02.HP.out
@@ -0,0 +1,83 @@
+<div class="line">
+ <div class="text">The capital of </div>
+ <div class="data" data-tag="state">North Carolina</div>
+ <div class="text"> is </div>
+ <div class="data" data-tag="city">Raleigh</div>
+</div>
+<div class="line">
+ <div class="text">The capital of </div>
+ <div class="data" data-tag="state">North Carolina</div>
+ <div class="text"> is </div>
+ <div class="data" data-tag="city">Raleigh</div>
+</div>
+<div class="line">
+ <div class="text">The capital of </div>
+ <div class="data" data-tag="state">North Carolina</div>
+ <div class="text"> is </div>
+ <div class="data" data-tag="city">Raleigh</div>
+</div>
+<div class="line">
+ <div class="text">The capital of </div>
+ <div class="data" data-tag="state">North Carolina</div>
+ <div class="text"> is </div>
+ <div class="data" data-tag="city">Raleigh</div>
+</div>
+<div class="line">
+ <div class="text">The capital of </div>
+ <div class="data" data-tag="state">North Carolina</div>
+ <div class="text"> is </div>
+ <div class="data" data-tag="city">Raleigh</div>
+</div>
+<div class="line">
+ <div class="text">The capital of </div>
+ <div class="data" data-tag="state">North Carolina</div>
+ <div class="text"> is </div>
+ <div class="data" data-tag="city">Raleigh</div>
+</div>
+<div class="line">
+ <div class="text">The capital of </div>
+ <div class="data" data-tag="state">North Carolina</div>
+ <div class="text"> is </div>
+ <div class="data" data-tag="city">Raleigh</div>
+</div>
+<div class="line">
+ <div class="text">The capital of </div>
+ <div class="data" data-tag="state">North Carolina</div>
+ <div class="text"> is </div>
+ <div class="data" data-tag="city">Raleigh</div>
+</div>
+<div class="line">
+ <div class="text">test</div>
+</div>
+<div class="line">
+ <div class="text">Answer:</div>
+ <div class="text">The capital of </div>
+ <div class="data" data-tag="state">North Carolina</div>
+ <div class="text"> is </div>
+ <div class="data" data-tag="city">Raleigh</div>
+</div>
+<div class="line">
+ <div class="text">First </div>
+ <div class="data" data-tag="tag">value1</div>
+ <div class="text"> </div>
+ <div class="text">and then </div>
+ <div class="data" data-tag="tag">value2</div>
+</div>
+<div class="line">
+ <div class="text">Machine </div>
+ <div class="data" data-tag="name">red</div>
+ <div class="text"> has </div>
+ <div class="data" data-tag="memory">55</div>
+</div>
+<div class="line">
+ <div class="text">Machine </div>
+ <div class="data" data-tag="name">green</div>
+ <div class="text"> has </div>
+ <div class="data" data-tag="memory">55</div>
+</div>
+<div class="line">
+ <div class="text">Machine </div>
+ <div class="data" data-tag="name">blue</div>
+ <div class="text"> has </div>
+ <div class="data" data-tag="memory">55</div>
+</div>
diff --git a/tests/xo/saved/xo_02.J.err b/tests/xo/saved/xo_02.J.err
new file mode 100644
index 000000000000..0c348c734898
--- /dev/null
+++ b/tests/xo/saved/xo_02.J.err
@@ -0,0 +1,25 @@
+Usage: xo [options] format [fields]
+ --close <path> Close tags for the given path
+ --close-instance <name> Close an open instance name
+ --close-list <name> Close an open list name
+ --continuation OR -C Output belongs on same line as previous output
+ --depth <num> Set the depth for pretty printing
+ --help Display this help text
+ --html OR -H Generate HTML output
+ --json OR -J Generate JSON output
+ --leading-xpath <path> OR -l <path> Add a prefix to generated XPaths (HTML)
+ --not-first Indicate this object is not the first (JSON)
+ --open <path> Open tags for the given path
+ --open-instance <name> Open an instance given by name
+ --open-list <name> Open a list given by name
+ --option <opts> -or -O <opts> Give formatting options
+ --pretty OR -p Make 'pretty' output (add indent, newlines)
+ --style <style> OR -s <style> Generate given style (xml, json, text, html)
+ --text OR -T Generate text output (the default style)
+ --top-wrap Generate a top-level object wrapper (JSON)
+ --version Display version information
+ --warn OR -W Display warnings in text on stderr
+ --warn-xml Display warnings in xml on stdout
+ --wrap <path> Wrap output in a set of containers
+ --xml OR -X Generate XML output
+ --xpath Add XPath data to HTML output
diff --git a/tests/xo/saved/xo_02.J.out b/tests/xo/saved/xo_02.J.out
new file mode 100644
index 000000000000..f11f784416b3
--- /dev/null
+++ b/tests/xo/saved/xo_02.J.out
@@ -0,0 +1,7 @@
+{"a": {"b": {"c": {"state":"North Carolina","city":"Raleigh","state":"North Carolina","city":"Raleigh"}}}}
+{"a": {"b": {"c": {"state":"North Carolina","city":"Raleigh"}}}}
+{"a": {"b": {"c": {"state":"North Carolina","city":"Raleigh", "d": {"e": {"f": {"state":"North Carolina","city":"Raleigh"}}},"state":"North Carolina","city":"Raleigh"}}}}
+"a": {"b": {"c": {"state":"North Carolina","city":"Raleigh"}}}{"a": {"b": {"c": {"state":"North Carolina","city":"Raleigh"}}}}
+{ }
+"answer": {"state":"North Carolina","city":"Raleigh"}{"top": {"data": {"tag":"value1""tag":"value2"}}}
+"machine": [{"name":"red","memory":"55"}, {"name":"green","memory":"55"}, {"name":"blue","memory":"55"}] \ No newline at end of file
diff --git a/tests/xo/saved/xo_02.JP.err b/tests/xo/saved/xo_02.JP.err
new file mode 100644
index 000000000000..0c348c734898
--- /dev/null
+++ b/tests/xo/saved/xo_02.JP.err
@@ -0,0 +1,25 @@
+Usage: xo [options] format [fields]
+ --close <path> Close tags for the given path
+ --close-instance <name> Close an open instance name
+ --close-list <name> Close an open list name
+ --continuation OR -C Output belongs on same line as previous output
+ --depth <num> Set the depth for pretty printing
+ --help Display this help text
+ --html OR -H Generate HTML output
+ --json OR -J Generate JSON output
+ --leading-xpath <path> OR -l <path> Add a prefix to generated XPaths (HTML)
+ --not-first Indicate this object is not the first (JSON)
+ --open <path> Open tags for the given path
+ --open-instance <name> Open an instance given by name
+ --open-list <name> Open a list given by name
+ --option <opts> -or -O <opts> Give formatting options
+ --pretty OR -p Make 'pretty' output (add indent, newlines)
+ --style <style> OR -s <style> Generate given style (xml, json, text, html)
+ --text OR -T Generate text output (the default style)
+ --top-wrap Generate a top-level object wrapper (JSON)
+ --version Display version information
+ --warn OR -W Display warnings in text on stderr
+ --warn-xml Display warnings in xml on stdout
+ --wrap <path> Wrap output in a set of containers
+ --xml OR -X Generate XML output
+ --xpath Add XPath data to HTML output
diff --git a/tests/xo/saved/xo_02.JP.out b/tests/xo/saved/xo_02.JP.out
new file mode 100644
index 000000000000..c540731430a4
--- /dev/null
+++ b/tests/xo/saved/xo_02.JP.out
@@ -0,0 +1,84 @@
+{
+ "a": {
+ "b": {
+ "c": {
+ "state": "North Carolina",
+ "city": "Raleigh",
+ "state": "North Carolina",
+ "city": "Raleigh"
+ }
+ }
+ }
+}
+{
+ "a": {
+ "b": {
+ "c": {
+ "state": "North Carolina",
+ "city": "Raleigh"
+ }
+ }
+ }
+}
+{
+ "a": {
+ "b": {
+ "c": {
+ "state": "North Carolina",
+ "city": "Raleigh",
+ "d": {
+ "e": {
+ "f": {
+ "state": "North Carolina",
+ "city": "Raleigh"
+ }
+ }
+ },
+ "state": "North Carolina",
+ "city": "Raleigh"
+ }
+ }
+ }
+}
+"a": {
+ "b": {
+ "c": {
+ "state": "North Carolina",
+ "city": "Raleigh"
+ }
+ }
+}{
+ "a": {
+ "b": {
+ "c": {
+ "state": "North Carolina",
+ "city": "Raleigh"
+ }
+ }
+ }
+}
+{ }
+"answer": {
+"state": "North Carolina",
+"city": "Raleigh"
+}{
+ "top": {
+ "data": {
+ "tag": "value1" "tag": "value2"
+ }
+ }
+}
+"machine": [
+ {
+ "name": "red",
+ "memory": "55"
+ },
+ {
+ "name": "green",
+ "memory": "55"
+ },
+ {
+ "name": "blue",
+ "memory": "55"
+ }
+] \ No newline at end of file
diff --git a/tests/xo/saved/xo_02.T.err b/tests/xo/saved/xo_02.T.err
new file mode 100644
index 000000000000..0c348c734898
--- /dev/null
+++ b/tests/xo/saved/xo_02.T.err
@@ -0,0 +1,25 @@
+Usage: xo [options] format [fields]
+ --close <path> Close tags for the given path
+ --close-instance <name> Close an open instance name
+ --close-list <name> Close an open list name
+ --continuation OR -C Output belongs on same line as previous output
+ --depth <num> Set the depth for pretty printing
+ --help Display this help text
+ --html OR -H Generate HTML output
+ --json OR -J Generate JSON output
+ --leading-xpath <path> OR -l <path> Add a prefix to generated XPaths (HTML)
+ --not-first Indicate this object is not the first (JSON)
+ --open <path> Open tags for the given path
+ --open-instance <name> Open an instance given by name
+ --open-list <name> Open a list given by name
+ --option <opts> -or -O <opts> Give formatting options
+ --pretty OR -p Make 'pretty' output (add indent, newlines)
+ --style <style> OR -s <style> Generate given style (xml, json, text, html)
+ --text OR -T Generate text output (the default style)
+ --top-wrap Generate a top-level object wrapper (JSON)
+ --version Display version information
+ --warn OR -W Display warnings in text on stderr
+ --warn-xml Display warnings in xml on stdout
+ --wrap <path> Wrap output in a set of containers
+ --xml OR -X Generate XML output
+ --xpath Add XPath data to HTML output
diff --git a/tests/xo/saved/xo_02.T.out b/tests/xo/saved/xo_02.T.out
new file mode 100644
index 000000000000..2eea241963fe
--- /dev/null
+++ b/tests/xo/saved/xo_02.T.out
@@ -0,0 +1,14 @@
+The capital of North Carolina is Raleigh
+The capital of North Carolina is Raleigh
+The capital of North Carolina is Raleigh
+The capital of North Carolina is Raleigh
+The capital of North Carolina is Raleigh
+The capital of North Carolina is Raleigh
+The capital of North Carolina is Raleigh
+The capital of North Carolina is Raleigh
+test
+Answer:The capital of North Carolina is Raleigh
+First value1 and then value2
+Machine red has 55
+Machine green has 55
+Machine blue has 55
diff --git a/tests/xo/saved/xo_02.X.err b/tests/xo/saved/xo_02.X.err
new file mode 100644
index 000000000000..0c348c734898
--- /dev/null
+++ b/tests/xo/saved/xo_02.X.err
@@ -0,0 +1,25 @@
+Usage: xo [options] format [fields]
+ --close <path> Close tags for the given path
+ --close-instance <name> Close an open instance name
+ --close-list <name> Close an open list name
+ --continuation OR -C Output belongs on same line as previous output
+ --depth <num> Set the depth for pretty printing
+ --help Display this help text
+ --html OR -H Generate HTML output
+ --json OR -J Generate JSON output
+ --leading-xpath <path> OR -l <path> Add a prefix to generated XPaths (HTML)
+ --not-first Indicate this object is not the first (JSON)
+ --open <path> Open tags for the given path
+ --open-instance <name> Open an instance given by name
+ --open-list <name> Open a list given by name
+ --option <opts> -or -O <opts> Give formatting options
+ --pretty OR -p Make 'pretty' output (add indent, newlines)
+ --style <style> OR -s <style> Generate given style (xml, json, text, html)
+ --text OR -T Generate text output (the default style)
+ --top-wrap Generate a top-level object wrapper (JSON)
+ --version Display version information
+ --warn OR -W Display warnings in text on stderr
+ --warn-xml Display warnings in xml on stdout
+ --wrap <path> Wrap output in a set of containers
+ --xml OR -X Generate XML output
+ --xpath Add XPath data to HTML output
diff --git a/tests/xo/saved/xo_02.X.out b/tests/xo/saved/xo_02.X.out
new file mode 100644
index 000000000000..40052cf59b8f
--- /dev/null
+++ b/tests/xo/saved/xo_02.X.out
@@ -0,0 +1 @@
+<a><b><c><state>North Carolina</state><city>Raleigh</city><state>North Carolina</state><city>Raleigh</city></c></b></a><a><b><c><state>North Carolina</state><city>Raleigh</city></c></b></a><a><b><c><state>North Carolina</state><city>Raleigh</city><d><e><f><state>North Carolina</state><city>Raleigh</city></f></e></d><state>North Carolina</state><city>Raleigh</city></c></b></a><a><b><c><state>North Carolina</state><city>Raleigh</city></c></b></a><a><b><c><state>North Carolina</state><city>Raleigh</city></c></b></a><answer><state>North Carolina</state><city>Raleigh</city></answer><top><data><tag>value1</tag><tag>value2</tag></data></top><machine><name>red</name><memory>55</memory></machine><machine><name>green</name><memory>55</memory></machine><machine><name>blue</name><memory>55</memory></machine> \ No newline at end of file
diff --git a/tests/xo/saved/xo_02.XP.err b/tests/xo/saved/xo_02.XP.err
new file mode 100644
index 000000000000..0c348c734898
--- /dev/null
+++ b/tests/xo/saved/xo_02.XP.err
@@ -0,0 +1,25 @@
+Usage: xo [options] format [fields]
+ --close <path> Close tags for the given path
+ --close-instance <name> Close an open instance name
+ --close-list <name> Close an open list name
+ --continuation OR -C Output belongs on same line as previous output
+ --depth <num> Set the depth for pretty printing
+ --help Display this help text
+ --html OR -H Generate HTML output
+ --json OR -J Generate JSON output
+ --leading-xpath <path> OR -l <path> Add a prefix to generated XPaths (HTML)
+ --not-first Indicate this object is not the first (JSON)
+ --open <path> Open tags for the given path
+ --open-instance <name> Open an instance given by name
+ --open-list <name> Open a list given by name
+ --option <opts> -or -O <opts> Give formatting options
+ --pretty OR -p Make 'pretty' output (add indent, newlines)
+ --style <style> OR -s <style> Generate given style (xml, json, text, html)
+ --text OR -T Generate text output (the default style)
+ --top-wrap Generate a top-level object wrapper (JSON)
+ --version Display version information
+ --warn OR -W Display warnings in text on stderr
+ --warn-xml Display warnings in xml on stdout
+ --wrap <path> Wrap output in a set of containers
+ --xml OR -X Generate XML output
+ --xpath Add XPath data to HTML output
diff --git a/tests/xo/saved/xo_02.XP.out b/tests/xo/saved/xo_02.XP.out
new file mode 100644
index 000000000000..6477af08ef1c
--- /dev/null
+++ b/tests/xo/saved/xo_02.XP.out
@@ -0,0 +1,74 @@
+<a>
+ <b>
+ <c>
+ <state>North Carolina</state>
+ <city>Raleigh</city>
+ <state>North Carolina</state>
+ <city>Raleigh</city>
+ </c>
+ </b>
+</a>
+<a>
+ <b>
+ <c>
+ <state>North Carolina</state>
+ <city>Raleigh</city>
+ </c>
+ </b>
+</a>
+<a>
+ <b>
+ <c>
+ <state>North Carolina</state>
+ <city>Raleigh</city>
+ <d>
+ <e>
+ <f>
+ <state>North Carolina</state>
+ <city>Raleigh</city>
+ </f>
+ </e>
+ </d>
+ <state>North Carolina</state>
+ <city>Raleigh</city>
+ </c>
+ </b>
+</a>
+<a>
+ <b>
+ <c>
+ <state>North Carolina</state>
+ <city>Raleigh</city>
+ </c>
+ </b>
+</a>
+<a>
+ <b>
+ <c>
+ <state>North Carolina</state>
+ <city>Raleigh</city>
+ </c>
+ </b>
+</a>
+<answer>
+<state>North Carolina</state>
+<city>Raleigh</city>
+</answer>
+<top>
+ <data>
+ <tag>value1</tag>
+ <tag>value2</tag>
+ </data>
+</top>
+ <machine>
+ <name>red</name>
+ <memory>55</memory>
+ </machine>
+ <machine>
+ <name>green</name>
+ <memory>55</memory>
+ </machine>
+ <machine>
+ <name>blue</name>
+ <memory>55</memory>
+ </machine>
diff --git a/tests/xo/xo_01.sh b/tests/xo/xo_01.sh
index 504f1755311c..472c58c48050 100755
--- a/tests/xo/xo_01.sh
+++ b/tests/xo/xo_01.sh
@@ -30,4 +30,7 @@ ${XOAN} "{[:/18}{:address/%p}..{:foo/%u}{]:}\n" 0xdeadbeef 1
${XOAN} "{[:/%d}{:address/%p}..{:foo/%u}{]:}\n" 18 0xdeadbeef 1
${XOAN} "{[:/%s}{:address/%p}..{:foo/%u}{]:}\n" 18 0xdeadbeef 1
+${XO} "partial line"
+${XO} -C " -- rest of line\n"
+
${XO} --close top
diff --git a/tests/xo/xo_02.sh b/tests/xo/xo_02.sh
new file mode 100755
index 000000000000..3f4917a62603
--- /dev/null
+++ b/tests/xo/xo_02.sh
@@ -0,0 +1,57 @@
+#
+# $Id$
+#
+# Copyright 2019, 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.
+
+XO=$1
+shift
+
+XOP="${XO} --warn"
+
+# This is testing --wrap, --open, --close, --top-wrap, etc, so
+# the output is not a single valid document
+
+set -- 'The capital of {:state} is {:city}\n' 'North Carolina' Raleigh
+
+${XOP} --top-wrap --open a/b/c "$@"
+${XOP} --top-wrap --close a/b/c --not-first "$@"
+
+${XOP} --top-wrap --wrap a/b/c "$@"
+
+${XOP} --top-wrap --open a/b/c "$@"
+${XOP} --depth 4 --not-first --wrap d/e/f "$@"
+${XOP} --top-wrap --close a/b/c --not-first "$@"
+
+${XOP} --wrap a/b/c "$@"
+
+${XOP} --top-wrap --wrap a/b/c "$@"
+
+${XOP} --top-wrap "test\n"
+
+${XOP} --open answer
+${XOP} "Answer:"
+${XOP} --continuation "$@"
+${XOP} --close answer
+
+${XOP} --top-wrap --open top/data
+${XOP} --depth 2 'First {:tag} ' value1
+${XOP} --depth 2 --continuation 'and then {:tag}\n' value2
+${XOP} --top-wrap --close top/data
+
+
+${XOP} --help
+
+${XOP} --open-list machine
+NF=
+for name in red green blue; do
+ ${XOP} --depth 1 $NF --open-instance machine
+ ${XOP} --depth 2 "Machine {k:name} has {:memory}\n" $name 55
+ ${XOP} --depth 1 --close-instance machine
+ NF=--not-first
+done
+${XOP} $NF --close-list machine
diff --git a/xo/xo.1 b/xo/xo.1
index 55a67176da90..cd885b3d193e 100644
--- a/xo/xo.1
+++ b/xo/xo.1
@@ -36,6 +36,10 @@ using the same commands that emit text output.
.Bl -tag -width indent
.It Ic --close Ar path
Close tags for the given path
+.It Ic -C | Ic --continuation
+Indicates this output is a continuation of the previous output data
+and should appear on the same line.
+This is allows HTML output to be constructed correctly.
.It Ic --depth Ar num
Set the depth for pretty printing
.It Ic --help
@@ -46,6 +50,10 @@ Generate HTML output
Generate JSON output
.It Ic --leading-xpath Ar path
Add a prefix to generated XPaths (HTML)
+.It Ic --not-first
+Indicate that this content is not the first in a series of sibling
+objects, which is vital information for "JSON" output, which requires
+a comma between such objects.
.It Ic --open Ar path
Open tags for the given path
.It Ic -p | Ic --pretty
@@ -54,6 +62,9 @@ Make 'pretty' output (add indent, newlines)
Generate given style (xml, json, text, html)
.It Ic -T | Ic --text
Generate text output (the default style)
+.It Ic --top-warp
+Indicates the entire object should be placed inside a top-level
+object wrapper, specifically when generating JSON output.
.It Ic --version
Display version information
.It Ic -W | Ic --warn
diff --git a/xo/xo.c b/xo/xo.c
index e48d2f8110c0..f74a1b25f56a 100644
--- a/xo/xo.c
+++ b/xo/xo.c
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2014, Juniper Networks, Inc.
+ * Copyright (c) 2014-2018, Juniper Networks, Inc.
* All rights reserved.
* This SOFTWARE is licensed under the LICENSE provided in the
* ../Copyright file. By downloading, installing, copying, or otherwise
@@ -15,6 +15,7 @@
#include "xo_config.h"
#include "xo.h"
+#include "xo_explicit.h"
#include <getopt.h> /* Include after xo.h for testing */
@@ -194,18 +195,25 @@ print_help (void)
fprintf(stderr,
"Usage: xo [options] format [fields]\n"
" --close <path> Close tags for the given path\n"
+" --close-instance <name> Close an open instance name\n"
+" --close-list <name> Close an open list name\n"
+" --continuation OR -C Output belongs on same line as previous output\n"
" --depth <num> Set the depth for pretty printing\n"
" --help Display this help text\n"
" --html OR -H Generate HTML output\n"
" --json OR -J Generate JSON output\n"
" --leading-xpath <path> OR -l <path> "
"Add a prefix to generated XPaths (HTML)\n"
+" --not-first Indicate this object is not the first (JSON)\n"
" --open <path> Open tags for the given path\n"
+" --open-instance <name> Open an instance given by name\n"
+" --open-list <name> Open a list given by name\n"
" --option <opts> -or -O <opts> Give formatting options\n"
" --pretty OR -p Make 'pretty' output (add indent, newlines)\n"
" --style <style> OR -s <style> "
"Generate given style (xml, json, text, html)\n"
" --text OR -T Generate text output (the default style)\n"
+" --top-wrap Generate a top-level object wrapper (JSON)\n"
" --version Display version information\n"
" --warn OR -W Display warnings in text on stderr\n"
" --warn-xml Display warnings in xml on stdout\n"
@@ -215,17 +223,25 @@ print_help (void)
}
static struct opts {
+ int o_close_instance;
+ int o_close_list;
int o_depth;
int o_help;
int o_not_first;
- int o_xpath;
+ int o_open_instance;
+ int o_open_list;
+ int o_top_wrap;
int o_version;
int o_warn_xml;
int o_wrap;
+ int o_xpath;
} opts;
static struct option long_opts[] = {
{ "close", required_argument, NULL, 'c' },
+ { "close-instance", required_argument, &opts.o_close_instance, 1 },
+ { "close-list", required_argument, &opts.o_close_list, 1 },
+ { "continuation", no_argument, NULL, 'C' },
{ "depth", required_argument, &opts.o_depth, 1 },
{ "help", no_argument, &opts.o_help, 1 },
{ "html", no_argument, NULL, 'H' },
@@ -233,10 +249,13 @@ static struct option long_opts[] = {
{ "leading-xpath", required_argument, NULL, 'l' },
{ "not-first", no_argument, &opts.o_not_first, 1 },
{ "open", required_argument, NULL, 'o' },
+ { "open-instance", required_argument, &opts.o_open_instance, 1 },
+ { "open-list", required_argument, &opts.o_open_list, 1 },
{ "option", required_argument, NULL, 'O' },
{ "pretty", no_argument, NULL, 'p' },
{ "style", required_argument, NULL, 's' },
{ "text", no_argument, NULL, 'T' },
+ { "top-wrap", no_argument, &opts.o_top_wrap, 1 },
{ "xml", no_argument, NULL, 'X' },
{ "xpath", no_argument, &opts.o_xpath, 1 },
{ "version", no_argument, &opts.o_version, 1 },
@@ -252,17 +271,24 @@ main (int argc UNUSED, char **argv)
char *fmt = NULL, *cp, *np;
char *opt_opener = NULL, *opt_closer = NULL, *opt_wrapper = NULL;
char *opt_options = NULL;
+ char *opt_name = NULL;
+ xo_state_t new_state = 0;
int opt_depth = 0;
int opt_not_first = 0;
+ int opt_top_wrap = 0;
int rc;
argc = xo_parse_args(argc, argv);
if (argc < 0)
return 1;
- while ((rc = getopt_long(argc, argv, "c:HJl:O:o:ps:TXW",
+ while ((rc = getopt_long(argc, argv, "Cc:HJl:O:o:ps:TXW",
long_opts, NULL)) != -1) {
switch (rc) {
+ case 'C':
+ xo_set_flags(NULL, XOF_CONTINUATION);
+ break;
+
case 'c':
opt_closer = optarg;
xo_set_flags(NULL, XOF_IGNORE_CLOSE);
@@ -339,6 +365,41 @@ main (int argc UNUSED, char **argv)
} else if (opts.o_wrap) {
opt_wrapper = optarg;
+ } else if (opts.o_top_wrap) {
+ opt_top_wrap = 1;
+
+ } else if (opts.o_open_list) {
+ if (opt_name)
+ xo_errx(1, "only one open/close list/instance allowed: %s",
+ optarg);
+
+ opt_name = optarg;
+ new_state = XSS_OPEN_LIST;
+
+ } else if (opts.o_open_instance) {
+ if (opt_name)
+ xo_errx(1, "only one open/close list/instance allowed: %s",
+ optarg);
+
+ opt_name = optarg;
+ new_state = XSS_OPEN_INSTANCE;
+
+ } else if (opts.o_close_list) {
+ if (opt_name)
+ xo_errx(1, "only one open/close list/instance allowed: %s",
+ optarg);
+
+ opt_name = optarg;
+ new_state = XSS_CLOSE_LIST;
+
+ } else if (opts.o_close_instance) {
+ if (opt_name)
+ xo_errx(1, "only one open/close list/instance allowed: %s",
+ optarg);
+
+ opt_name = optarg;
+ new_state = XSS_CLOSE_INSTANCE;
+
} else {
print_help();
return 1;
@@ -365,14 +426,34 @@ main (int argc UNUSED, char **argv)
xo_set_formatter(NULL, formatter, checkpoint);
xo_set_flags(NULL, XOF_NO_VA_ARG | XOF_NO_TOP | XOF_NO_CLOSE);
+ /*
+ * If we have some explicit state change, handle it
+ */
+ if (new_state) {
+ if (opt_depth > 0)
+ xo_set_depth(NULL, opt_depth);
+
+ if (opt_not_first)
+ xo_set_flags(NULL, XOF_NOT_FIRST);
+
+ xo_explicit_transition(NULL, new_state, opt_name, 0);
+ xo_finish();
+ exit(0);
+ }
+
fmt = *argv++;
if (opt_opener == NULL && opt_closer == NULL && fmt == NULL) {
print_help();
return 1;
}
- if (opt_not_first)
- xo_set_flags(NULL, XOF_NOT_FIRST);
+ if (opt_top_wrap) {
+ /* If we have a closing path, we'll be one extra level deeper */
+ if (opt_closer && xo_get_style(NULL) == XO_STYLE_JSON)
+ opt_depth += 1;
+ else
+ xo_clear_flags(NULL, XOF_NO_TOP);
+ }
if (opt_closer) {
opt_depth += 1;
@@ -388,6 +469,10 @@ main (int argc UNUSED, char **argv)
if (opt_depth > 0)
xo_set_depth(NULL, opt_depth);
+ if (opt_not_first)
+ xo_set_flags(NULL, XOF_NOT_FIRST);
+
+ /* If there's an opening hierarchy, open each element as a container */
if (opt_opener) {
for (cp = opt_opener; cp && *cp; cp = np) {
np = strchr(cp, '/');
@@ -395,10 +480,11 @@ main (int argc UNUSED, char **argv)
*np = '\0';
xo_open_container(cp);
if (np)
- *np++ = '/';
+ np += 1;
}
}
+ /* If there's an wrapper hierarchy, open each element as a container */
if (opt_wrapper) {
for (cp = opt_wrapper; cp && *cp; cp = np) {
np = strchr(cp, '/');
@@ -406,16 +492,18 @@ main (int argc UNUSED, char **argv)
*np = '\0';
xo_open_container(cp);
if (np)
- *np++ = '/';
+ *np++ = '/'; /* Put it back */
}
}
+ /* If there's a format string, call xo_emit to emit the contents */
if (fmt && *fmt) {
save_argv = argv;
prep_arg(fmt);
- xo_emit(fmt);
+ xo_emit(fmt); /* This call does the real formatting */
}
-
+
+ /* If there's an wrapper hierarchy, close each element's container */
while (opt_wrapper) {
np = strrchr(opt_wrapper, '/');
xo_close_container(np ? np + 1 : opt_wrapper);
@@ -425,6 +513,10 @@ main (int argc UNUSED, char **argv)
opt_wrapper = NULL;
}
+ /* Remember to undo the depth before calling xo_finish() */
+ opt_depth = (opt_closer && opt_top_wrap) ? -1 : 0;
+
+ /* If there's an closing hierarchy, close each element's container */
while (opt_closer) {
np = strrchr(opt_closer, '/');
xo_close_container(np ? np + 1 : opt_closer);
@@ -434,6 +526,16 @@ main (int argc UNUSED, char **argv)
opt_closer = NULL;
}
+ /* If there's a closer and a wrapper, we need to clean it up */
+ if (opt_depth) {
+ xo_set_depth(NULL, opt_depth);
+ xo_clear_flags(NULL, XOF_NO_TOP);
+ }
+
+ /* If we're wrapping the entire content, skip the closer */
+ if (opt_top_wrap && opt_opener)
+ xo_set_flags(NULL, XOF_NO_TOP);
+
xo_finish();
return 0;