2 * Copyright (c) 2014, Juniper Networks, Inc.
4 * This SOFTWARE is licensed under the LICENSE provided in the
5 * ../Copyright file. By downloading, installing, copying, or otherwise
6 * using the SOFTWARE, you agree to be bound by the terms of that
8 * Phil Shafer, July 2014
17 #include <sys/types.h>
28 #include "xoversion.h"
30 const char xo_version[] = LIBXO_VERSION;
31 const char xo_version_extra[] = LIBXO_VERSION_EXTRA;
34 #define UNUSED __attribute__ ((__unused__))
37 #define XO_INDENT_BY 2 /* Amount to indent when pretty printing */
38 #define XO_BUFSIZ (8*1024) /* Initial buffer size */
39 #define XO_DEPTH 512 /* Default stack depth */
40 #define XO_MAX_ANCHOR_WIDTH (8*1024) /* Anything wider is just sillyb */
42 #define XO_FAILURE_NAME "failure"
45 * xo_buffer_t: a memory buffer that can be grown as needed. We
46 * use them for building format strings and output data.
48 typedef struct xo_buffer_s {
49 char *xb_bufp; /* Buffer memory */
50 char *xb_curp; /* Current insertion point */
51 int xb_size; /* Size of buffer */
54 /* Flags for the stack frame */
55 typedef unsigned xo_xsf_flags_t; /* XSF_* flags */
56 #define XSF_NOT_FIRST (1<<0) /* Not the first element */
57 #define XSF_LIST (1<<1) /* Frame is a list */
58 #define XSF_INSTANCE (1<<2) /* Frame is an instance */
59 #define XSF_DTRT (1<<3) /* Save the name for DTRT mode */
62 * xo_stack_t: As we open and close containers and levels, we
63 * create a stack of frames to track them. This is needed for
64 * XOF_WARN and XOF_XPATH.
66 typedef struct xo_stack_s {
67 xo_xsf_flags_t xs_flags; /* Flags for this frame */
68 char *xs_name; /* Name (for XPath value) */
69 char *xs_keys; /* XPath predicate for any key fields */
73 * xo_handle_t: this is the principle data structure for libxo.
74 * It's used as a store for state, options, and content.
77 unsigned long xo_flags; /* Flags */
78 unsigned short xo_style; /* XO_STYLE_* value */
79 unsigned short xo_indent; /* Indent level (if pretty) */
80 unsigned short xo_indent_by; /* Indent amount (tab stop) */
81 xo_write_func_t xo_write; /* Write callback */
82 xo_close_func_t xo_close; /* Close callback */
83 xo_formatter_t xo_formatter; /* Custom formating function */
84 xo_checkpointer_t xo_checkpointer; /* Custom formating support function */
85 void *xo_opaque; /* Opaque data for write function */
86 FILE *xo_fp; /* XXX File pointer */
87 xo_buffer_t xo_data; /* Output data */
88 xo_buffer_t xo_fmt; /* Work area for building format strings */
89 xo_buffer_t xo_attrs; /* Work area for building XML attributes */
90 xo_buffer_t xo_predicate; /* Work area for building XPath predicates */
91 xo_stack_t *xo_stack; /* Stack pointer */
92 int xo_depth; /* Depth of stack */
93 int xo_stack_size; /* Size of the stack */
94 xo_info_t *xo_info; /* Info fields for all elements */
95 int xo_info_count; /* Number of info entries */
96 va_list xo_vap; /* Variable arguments (stdargs) */
97 char *xo_leading_xpath; /* A leading XPath expression */
98 mbstate_t xo_mbstate; /* Multi-byte character conversion state */
99 unsigned xo_anchor_offset; /* Start of anchored text */
100 unsigned xo_anchor_columns; /* Number of columns since the start anchor */
101 int xo_anchor_min_width; /* Desired width of anchored text */
102 unsigned xo_units_offset; /* Start of units insertion point */
103 unsigned xo_columns; /* Columns emitted during this xo_emit call */
106 /* Flags for formatting functions */
107 typedef unsigned long xo_xff_flags_t;
108 #define XFF_COLON (1<<0) /* Append a ":" */
109 #define XFF_COMMA (1<<1) /* Append a "," iff there's more output */
110 #define XFF_WS (1<<2) /* Append a blank */
111 #define XFF_ENCODE_ONLY (1<<3) /* Only emit for encoding formats (xml and json) */
113 #define XFF_QUOTE (1<<4) /* Force quotes */
114 #define XFF_NOQUOTE (1<<5) /* Force no quotes */
115 #define XFF_DISPLAY_ONLY (1<<6) /* Only emit for display formats (text and html) */
116 #define XFF_KEY (1<<7) /* Field is a key (for XPath) */
118 #define XFF_XML (1<<8) /* Force XML encoding style (for XPath) */
119 #define XFF_ATTR (1<<9) /* Escape value using attribute rules (XML) */
120 #define XFF_BLANK_LINE (1<<10) /* Emit a blank line */
121 #define XFF_NO_OUTPUT (1<<11) /* Do not make any output */
123 #define XFF_TRIM_WS (1<<12) /* Trim whitespace off encoded values */
124 #define XFF_LEAF_LIST (1<<13) /* A leaf-list (list of values) */
125 #define XFF_UNESCAPE (1<<14) /* Need to printf-style unescape the value */
128 * Normal printf has width and precision, which for strings operate as
129 * min and max number of columns. But this depends on the idea that
130 * one byte means one column, which UTF-8 and multi-byte characters
131 * pitches on its ear. It may take 40 bytes of data to populate 14
132 * columns, but we can't go off looking at 40 bytes of data without the
133 * caller's permission for fear/knowledge that we'll generate core files.
135 * So we make three values, distinguishing between "max column" and
136 * "number of bytes that we will inspect inspect safely" We call the
137 * later "size", and make the format "%[[<min>].[[<size>].<max>]]s".
139 * Under the "first do no harm" theory, we default "max" to "size".
140 * This is a reasonable assumption for folks that don't grok the
141 * MBS/WCS/UTF-8 world, and while it will be annoying, it will never
144 * For example, xo_emit("{:tag/%-14.14s}", buf) will make 14
145 * columns of output, but will never look at more than 14 bytes of the
146 * input buffer. This is mostly compatible with printf and caller's
149 * In contrast xo_emit("{:tag/%-14..14s}", buf) will look at however
150 * many bytes (or until a NUL is seen) are needed to fill 14 columns
151 * of output. xo_emit("{:tag/%-14.*.14s}", xx, buf) will look at up
152 * to xx bytes (or until a NUL is seen) in order to fill 14 columns
155 * It's fairly amazing how a good idea (handle all languages of the
156 * world) blows such a big hole in the bottom of the fairly weak boat
157 * that is C string handling. The simplicity and completenesss are
158 * sunk in ways we haven't even begun to understand.
161 #define XF_WIDTH_MIN 0 /* Minimal width */
162 #define XF_WIDTH_SIZE 1 /* Maximum number of bytes to examine */
163 #define XF_WIDTH_MAX 2 /* Maximum width */
164 #define XF_WIDTH_NUM 3 /* Numeric fields in printf (min.size.max) */
166 /* Input and output string encodings */
167 #define XF_ENC_WIDE 1 /* Wide characters (wchar_t) */
168 #define XF_ENC_UTF8 2 /* UTF-8 */
169 #define XF_ENC_LOCALE 3 /* Current locale */
172 * A place to parse printf-style format flags for each field
174 typedef struct xo_format_s {
175 unsigned char xf_fc; /* Format character */
176 unsigned char xf_enc; /* Encoding of the string (XF_ENC_*) */
177 unsigned char xf_skip; /* Skip this field */
178 unsigned char xf_lflag; /* 'l' (long) */
179 unsigned char xf_hflag;; /* 'h' (half) */
180 unsigned char xf_jflag; /* 'j' (intmax_t) */
181 unsigned char xf_tflag; /* 't' (ptrdiff_t) */
182 unsigned char xf_zflag; /* 'z' (size_t) */
183 unsigned char xf_qflag; /* 'q' (quad_t) */
184 unsigned char xf_seen_minus; /* Seen a minus */
185 int xf_leading_zero; /* Seen a leading zero (zero fill) */
186 unsigned xf_dots; /* Seen one or more '.'s */
187 int xf_width[XF_WIDTH_NUM]; /* Width/precision/size numeric fields */
188 unsigned xf_stars; /* Seen one or more '*'s */
189 unsigned char xf_star[XF_WIDTH_NUM]; /* Seen one or more '*'s */
193 * We keep a default handle to allow callers to avoid having to
194 * allocate one. Passing NULL to any of our functions will use
195 * this default handle.
197 static xo_handle_t xo_default_handle;
198 static int xo_default_inited;
199 static int xo_locale_inited;
200 static char *xo_program;
203 * To allow libxo to be used in diverse environment, we allow the
204 * caller to give callbacks for memory allocation.
206 static xo_realloc_func_t xo_realloc = realloc;
207 static xo_free_func_t xo_free = free;
209 /* Forward declarations */
211 xo_failure (xo_handle_t *xop, const char *fmt, ...);
214 xo_buf_append_div (xo_handle_t *xop, const char *class, xo_xff_flags_t flags,
215 const char *name, int nlen,
216 const char *value, int vlen,
217 const char *encoding, int elen);
220 xo_anchor_clear (xo_handle_t *xop);
223 * Callback to write data to a FILE pointer
226 xo_write_to_file (void *opaque, const char *data)
228 FILE *fp = (FILE *) opaque;
229 return fprintf(fp, "%s", data);
233 * Callback to close a file
236 xo_close_file (void *opaque)
238 FILE *fp = (FILE *) opaque;
243 * Initialize the contents of an xo_buffer_t.
246 xo_buf_init (xo_buffer_t *xbp)
248 xbp->xb_size = XO_BUFSIZ;
249 xbp->xb_bufp = xo_realloc(NULL, xbp->xb_size);
250 xbp->xb_curp = xbp->xb_bufp;
254 * Initialize the contents of an xo_buffer_t.
257 xo_buf_cleanup (xo_buffer_t *xbp)
260 xo_free(xbp->xb_bufp);
261 bzero(xbp, sizeof(*xbp));
265 xo_depth_check (xo_handle_t *xop, int depth)
269 if (depth >= xop->xo_stack_size) {
271 xsp = xo_realloc(xop->xo_stack, sizeof(xop->xo_stack[0]) * depth);
273 xo_failure(xop, "xo_depth_check: out of memory (%d)", depth);
277 int count = depth - xop->xo_stack_size;
279 bzero(xsp + xop->xo_stack_size, count * sizeof(*xsp));
280 xop->xo_stack_size = depth;
288 xo_no_setlocale (void)
290 xo_locale_inited = 1; /* Skip initialization */
294 * Initialize an xo_handle_t, using both static defaults and
295 * the global settings from the LIBXO_OPTIONS environment
299 xo_init_handle (xo_handle_t *xop)
301 xop->xo_opaque = stdout;
302 xop->xo_write = xo_write_to_file;
305 * We need to initialize the locale, which isn't really pretty.
306 * Libraries should depend on their caller to set up the
307 * environment. But we really can't count on the caller to do
308 * this, because well, they won't. Trust me.
310 if (!xo_locale_inited) {
311 xo_locale_inited = 1; /* Only do this once */
313 const char *cp = getenv("LC_CTYPE");
317 cp = getenv("LC_ALL");
319 cp = "UTF-8"; /* Optimistic? */
320 (void) setlocale(LC_CTYPE, cp);
324 * Initialize only the xo_buffers we know we'll need; the others
325 * can be allocated as needed.
327 xo_buf_init(&xop->xo_data);
328 xo_buf_init(&xop->xo_fmt);
330 xop->xo_indent_by = XO_INDENT_BY;
331 xo_depth_check(xop, XO_DEPTH);
333 #if !defined(NO_LIBXO_OPTIONS)
334 if (!(xop->xo_flags & XOF_NO_ENV)) {
335 char *env = getenv("LIBXO_OPTIONS");
337 xo_set_options(xop, env);
339 #endif /* NO_GETENV */
343 * Initialize the default handle.
346 xo_default_init (void)
348 xo_handle_t *xop = &xo_default_handle;
352 xo_default_inited = 1;
356 * Does the buffer have room for the given number of bytes of data?
357 * If not, realloc the buffer to make room. If that fails, we
358 * return 0 to tell the caller they are in trouble.
361 xo_buf_has_room (xo_buffer_t *xbp, int len)
363 if (xbp->xb_curp + len >= xbp->xb_bufp + xbp->xb_size) {
364 int sz = xbp->xb_size + XO_BUFSIZ;
365 char *bp = xo_realloc(xbp->xb_bufp, sz);
368 * XXX If we wanted to put a stick XOF_ENOMEM on xop,
369 * this would be the place to do it. But we'd need
370 * to churn the code to pass xop in here....
375 xbp->xb_curp = bp + (xbp->xb_curp - xbp->xb_bufp);
384 * Cheap convenience function to return either the argument, or
385 * the internal handle, after it has been initialized. The usage
387 * xop = xo_default(xop);
390 xo_default (xo_handle_t *xop)
393 if (xo_default_inited == 0)
395 xop = &xo_default_handle;
402 * Return the number of spaces we should be indenting. If
403 * we are pretty-printing, theis is indent * indent_by.
406 xo_indent (xo_handle_t *xop)
410 xop = xo_default(xop);
412 if (xop->xo_flags & XOF_PRETTY) {
413 rc = xop->xo_indent * xop->xo_indent_by;
414 if (xop->xo_flags & XOF_TOP_EMITTED)
415 rc += xop->xo_indent_by;
422 xo_buf_indent (xo_handle_t *xop, int indent)
424 xo_buffer_t *xbp = &xop->xo_data;
427 indent = xo_indent(xop);
429 if (!xo_buf_has_room(xbp, indent))
432 memset(xbp->xb_curp, ' ', indent);
433 xbp->xb_curp += indent;
436 static char xo_xml_amp[] = "&";
437 static char xo_xml_lt[] = "<";
438 static char xo_xml_gt[] = ">";
439 static char xo_xml_quot[] = """;
442 xo_escape_xml (xo_buffer_t *xbp, int len, int attr)
449 for (cp = xbp->xb_curp, ep = cp + len; cp < ep; cp++) {
450 /* We're subtracting 2: 1 for the NUL, 1 for the char we replace */
452 delta += sizeof(xo_xml_lt) - 2;
454 delta += sizeof(xo_xml_gt) - 2;
456 delta += sizeof(xo_xml_amp) - 2;
457 else if (attr && *cp == '"')
458 delta += sizeof(xo_xml_quot) - 2;
461 if (delta == 0) /* Nothing to escape; bail */
464 if (!xo_buf_has_room(xbp, delta)) /* No room; bail, but don't append */
480 else if (attr && *cp == '"')
489 memcpy(ip, sp, slen);
491 } while (cp > ep && cp != ip);
497 xo_escape_json (xo_buffer_t *xbp, int len)
502 for (cp = xbp->xb_curp, ep = cp + len; cp < ep; cp++) {
509 if (delta == 0) /* Nothing to escape; bail */
512 if (!xo_buf_has_room(xbp, delta)) /* No room; bail, but don't append */
522 if (*cp != '\\' && *cp != '"') {
530 } while (cp > ep && cp != ip);
536 * Append the given string to the given buffer
539 xo_buf_append (xo_buffer_t *xbp, const char *str, int len)
541 if (!xo_buf_has_room(xbp, len))
544 memcpy(xbp->xb_curp, str, len);
549 xo_buf_escape (xo_handle_t *xop, xo_buffer_t *xbp,
550 const char *str, int len, xo_xff_flags_t flags)
552 if (!xo_buf_has_room(xbp, len))
555 memcpy(xbp->xb_curp, str, len);
557 switch (xop->xo_style) {
560 len = xo_escape_xml(xbp, len, (flags & XFF_ATTR));
564 len = xo_escape_json(xbp, len);
572 * Write the current contents of the data buffer using the handle's
576 xo_write (xo_handle_t *xop)
578 xo_buffer_t *xbp = &xop->xo_data;
580 if (xbp->xb_curp != xbp->xb_bufp) {
581 xo_buf_append(xbp, "", 1); /* Append ending NUL */
582 xo_anchor_clear(xop);
583 xop->xo_write(xop->xo_opaque, xbp->xb_bufp);
584 xbp->xb_curp = xbp->xb_bufp;
587 /* Turn off the flags that don't survive across writes */
588 xop->xo_flags &= ~(XOF_UNITS_PENDING);
592 * Format arguments into our buffer. If a custom formatter has been set,
593 * we use that to do the work; otherwise we vsnprintf().
596 xo_vsnprintf (xo_handle_t *xop, xo_buffer_t *xbp, const char *fmt, va_list vap)
600 int left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp);
602 va_copy(va_local, vap);
604 if (xop->xo_formatter)
605 rc = xop->xo_formatter(xop, xbp->xb_curp, left, fmt, va_local);
607 rc = vsnprintf(xbp->xb_curp, left, fmt, va_local);
609 if (rc > xbp->xb_size) {
610 if (!xo_buf_has_room(xbp, rc)) {
616 * After we call vsnprintf(), the stage of vap is not defined.
617 * We need to copy it before we pass. Then we have to do our
618 * own logic below to move it along. This is because the
619 * implementation can have va_list be a point (bsd) or a
620 * structure (macosx) or anything in between.
623 va_end(va_local); /* Reset vap to the start */
624 va_copy(va_local, vap);
626 left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp);
627 if (xop->xo_formatter)
628 xop->xo_formatter(xop, xbp->xb_curp, left, fmt, va_local);
630 rc = vsnprintf(xbp->xb_curp, left, fmt, va_local);
638 * Print some data thru the handle.
641 xo_printf_v (xo_handle_t *xop, const char *fmt, va_list vap)
643 xo_buffer_t *xbp = &xop->xo_data;
644 int left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp);
648 va_copy(va_local, vap);
650 rc = vsnprintf(xbp->xb_curp, left, fmt, va_local);
652 if (rc > xbp->xb_size) {
653 if (!xo_buf_has_room(xbp, rc)) {
658 va_end(va_local); /* Reset vap to the start */
659 va_copy(va_local, vap);
661 left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp);
662 rc = vsnprintf(xbp->xb_curp, left, fmt, va_local);
674 xo_printf (xo_handle_t *xop, const char *fmt, ...)
681 rc = xo_printf_v(xop, fmt, vap);
688 * These next few function are make The Essential UTF-8 Ginsu Knife.
689 * Identify an input and output character, and convert it.
691 static int xo_utf8_bits[7] = { 0, 0x7f, 0x1f, 0x0f, 0x07, 0x03, 0x01 };
700 xo_utf8_to_wc_len (const char *buf)
702 unsigned b = (unsigned char) *buf;
705 if ((b & 0x80) == 0x0)
707 else if ((b & 0xe0) == 0xc0)
709 else if ((b & 0xf0) == 0xe0)
711 else if ((b & 0xf8) == 0xf0)
713 else if ((b & 0xfc) == 0xf8)
715 else if ((b & 0xfe) == 0xfc)
724 xo_buf_utf8_len (xo_handle_t *xop, const char *buf, int bufsiz)
727 unsigned b = (unsigned char) *buf;
730 len = xo_utf8_to_wc_len(buf);
732 xo_failure(xop, "invalid UTF-8 data: %02hhx", b);
737 xo_failure(xop, "invalid UTF-8 data (short): %02hhx (%d/%d)",
742 for (i = 2; i < len; i++) {
743 b = (unsigned char ) buf[i];
744 if ((b & 0xc0) != 0x80) {
745 xo_failure(xop, "invalid UTF-8 data (byte %d): %x", i, b);
754 * Build a wide character from the input buffer; the number of
755 * bits we pull off the first character is dependent on the length,
756 * but we put 6 bits off all other bytes.
759 xo_utf8_char (const char *buf, int len)
763 const unsigned char *cp = (const unsigned char *) buf;
765 wc = *cp & xo_utf8_bits[len];
766 for (i = 1; i < len; i++) {
769 if ((cp[i] & 0xc0) != 0x80)
777 * Determine the number of bytes needed to encode a wide character.
780 xo_utf8_emit_len (wchar_t wc)
784 if ((wc & ((1<<7) - 1)) == wc) /* Simple case */
786 else if ((wc & ((1<<11) - 1)) == wc)
788 else if ((wc & ((1<<16) - 1)) == wc)
790 else if ((wc & ((1<<21) - 1)) == wc)
792 else if ((wc & ((1<<26) - 1)) == wc)
801 xo_utf8_emit_char (char *buf, int len, wchar_t wc)
805 if (len == 1) { /* Simple case */
810 for (i = len - 1; i >= 0; i--) {
811 buf[i] = 0x80 | (wc & 0x3f);
815 buf[0] &= xo_utf8_bits[len];
816 buf[0] |= ~xo_utf8_bits[len] << 1;
820 xo_buf_append_locale_from_utf8 (xo_handle_t *xop, xo_buffer_t *xbp,
821 const char *ibuf, int ilen)
827 * Build our wide character from the input buffer; the number of
828 * bits we pull off the first character is dependent on the length,
829 * but we put 6 bits off all other bytes.
831 wc = xo_utf8_char(ibuf, ilen);
832 if (wc == (wchar_t) -1) {
833 xo_failure(xop, "invalid utf-8 byte sequence");
837 if (xop->xo_flags & XOF_NO_LOCALE) {
838 if (!xo_buf_has_room(xbp, ilen))
841 memcpy(xbp->xb_curp, ibuf, ilen);
842 xbp->xb_curp += ilen;
845 if (!xo_buf_has_room(xbp, MB_LEN_MAX + 1))
848 bzero(&xop->xo_mbstate, sizeof(xop->xo_mbstate));
849 len = wcrtomb(xbp->xb_curp, wc, &xop->xo_mbstate);
852 xo_failure(xop, "could not convert wide char: %lx",
863 xo_buf_append_locale (xo_handle_t *xop, xo_buffer_t *xbp,
864 const char *cp, int len)
866 const char *sp = cp, *ep = cp + len;
867 unsigned save_off = xbp->xb_bufp - xbp->xb_curp;
871 for ( ; cp < ep; cp++) {
872 if (!xo_is_utf8(*cp)) {
878 * We're looking at a non-ascii UTF-8 character.
879 * First we copy the previous data.
880 * Then we need find the length and validate it.
881 * Then we turn it into a wide string.
882 * Then we turn it into a localized string.
883 * Then we repeat. Isn't i18n fun?
886 xo_buf_append(xbp, sp, cp - sp); /* Append previous data */
888 slen = xo_buf_utf8_len(xop, cp, ep - cp);
890 /* Bad data; back it all out */
891 xbp->xb_curp = xbp->xb_bufp + save_off;
895 cols += xo_buf_append_locale_from_utf8(xop, xbp, cp, slen);
897 /* Next time thru, we'll start at the next character */
902 /* Update column values */
903 if (xop->xo_flags & XOF_COLUMNS)
904 xop->xo_columns += cols;
905 if (xop->xo_flags & XOF_ANCHOR)
906 xop->xo_anchor_columns += cols;
908 /* Before we fall into the basic logic below, we need reset len */
910 if (len != 0) /* Append trailing data */
911 xo_buf_append(xbp, sp, len);
915 * Append the given string to the given buffer
918 xo_data_append (xo_handle_t *xop, const char *str, int len)
920 xo_buf_append(&xop->xo_data, str, len);
924 * Append the given string to the given buffer
927 xo_data_escape (xo_handle_t *xop, const char *str, int len)
929 xo_buf_escape(xop, &xop->xo_data, str, len, 0);
933 * Generate a warning. Normally, this is a text message written to
934 * standard error. If the XOF_WARN_XML flag is set, then we generate
935 * XMLified content on standard output.
938 xo_warn_hcv (xo_handle_t *xop, int code, int check_warn,
939 const char *fmt, va_list vap)
941 xop = xo_default(xop);
942 if (check_warn && !(xop->xo_flags & XOF_WARN))
948 int len = strlen(fmt);
949 int plen = xo_program ? strlen(xo_program) : 0;
950 char *newfmt = alloca(len + 2 + plen + 2); /* newline, NUL, and ": " */
953 memcpy(newfmt, xo_program, plen);
954 newfmt[plen++] = ':';
955 newfmt[plen++] = ' ';
957 memcpy(newfmt + plen, fmt, len);
959 /* Add a newline to the fmt string */
960 if (!(xop->xo_flags & XOF_WARN_XML))
961 newfmt[len++ + plen] = '\n';
962 newfmt[len + plen] = '\0';
964 if (xop->xo_flags & XOF_WARN_XML) {
965 static char err_open[] = "<error>";
966 static char err_close[] = "</error>";
967 static char msg_open[] = "<message>";
968 static char msg_close[] = "</message>";
970 xo_buffer_t *xbp = &xop->xo_data;
972 xo_buf_append(xbp, err_open, sizeof(err_open) - 1);
973 xo_buf_append(xbp, msg_open, sizeof(msg_open) - 1);
976 va_copy(va_local, vap);
978 int left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp);
979 int rc = vsnprintf(xbp->xb_curp, left, newfmt, vap);
980 if (rc > xbp->xb_size) {
981 if (!xo_buf_has_room(xbp, rc)) {
986 va_end(vap); /* Reset vap to the start */
987 va_copy(vap, va_local);
989 left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp);
990 rc = vsnprintf(xbp->xb_curp, left, fmt, vap);
994 rc = xo_escape_xml(xbp, rc, 1);
997 xo_buf_append(xbp, msg_close, sizeof(msg_close) - 1);
998 xo_buf_append(xbp, err_close, sizeof(err_close) - 1);
1001 const char *msg = strerror(code);
1003 xo_buf_append(xbp, ": ", 2);
1004 xo_buf_append(xbp, msg, strlen(msg));
1008 xo_buf_append(xbp, "\n", 2); /* Append newline and NUL to string */
1012 vfprintf(stderr, newfmt, vap);
1017 xo_warn_hc (xo_handle_t *xop, int code, const char *fmt, ...)
1022 xo_warn_hcv(xop, code, 0, fmt, vap);
1027 xo_warn_c (int code, const char *fmt, ...)
1032 xo_warn_hcv(NULL, 0, code, fmt, vap);
1037 xo_warn (const char *fmt, ...)
1043 xo_warn_hcv(NULL, code, 0, fmt, vap);
1048 xo_warnx (const char *fmt, ...)
1053 xo_warn_hcv(NULL, -1, 0, fmt, vap);
1058 xo_err (int eval, const char *fmt, ...)
1064 xo_warn_hcv(NULL, code, 0, fmt, vap);
1071 xo_errx (int eval, const char *fmt, ...)
1076 xo_warn_hcv(NULL, -1, 0, fmt, vap);
1083 xo_errc (int eval, int code, const char *fmt, ...)
1088 xo_warn_hcv(NULL, code, 0, fmt, vap);
1095 * Generate a warning. Normally, this is a text message written to
1096 * standard error. If the XOF_WARN_XML flag is set, then we generate
1097 * XMLified content on standard output.
1100 xo_message_hcv (xo_handle_t *xop, int code, const char *fmt, va_list vap)
1102 static char msg_open[] = "<message>";
1103 static char msg_close[] = "</message>";
1108 xop = xo_default(xop);
1110 if (fmt == NULL || *fmt == '\0')
1113 int need_nl = (fmt[strlen(fmt) - 1] != '\n');
1115 switch (xop->xo_style) {
1117 xbp = &xop->xo_data;
1118 if (xop->xo_flags & XOF_PRETTY)
1119 xo_buf_indent(xop, xop->xo_indent_by);
1120 xo_buf_append(xbp, msg_open, sizeof(msg_open) - 1);
1122 va_copy(va_local, vap);
1124 int left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp);
1125 rc = vsnprintf(xbp->xb_curp, left, fmt, vap);
1126 if (rc > xbp->xb_size) {
1127 if (!xo_buf_has_room(xbp, rc)) {
1132 va_end(vap); /* Reset vap to the start */
1133 va_copy(vap, va_local);
1135 left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp);
1136 rc = vsnprintf(xbp->xb_curp, left, fmt, vap);
1140 rc = xo_escape_xml(xbp, rc, 1);
1143 if (need_nl && code > 0) {
1144 const char *msg = strerror(code);
1146 xo_buf_append(xbp, ": ", 2);
1147 xo_buf_append(xbp, msg, strlen(msg));
1151 xo_buf_append(xbp, msg_close, sizeof(msg_close) - 1);
1153 xo_buf_append(xbp, "\n", 2); /* Append newline and NUL to string */
1159 char buf[BUFSIZ], *bp = buf, *cp;
1160 int bufsiz = sizeof(buf);
1163 va_copy(va_local, vap);
1165 rc = vsnprintf(bp, bufsiz, fmt, va_local);
1167 bufsiz = rc + BUFSIZ;
1168 bp = alloca(bufsiz);
1170 va_copy(va_local, vap);
1171 rc = vsnprintf(bp, bufsiz, fmt, va_local);
1177 rc2 = snprintf(cp, bufsiz - rc, "%s%s\n",
1178 (code > 0) ? ": " : "",
1179 (code > 0) ? strerror(code) : "");
1184 xo_buf_append_div(xop, "message", 0, NULL, 0, bp, rc, NULL, 0);
1189 /* No meanings of representing messages in JSON */
1193 rc = xo_printf_v(xop, fmt, vap);
1195 * XXX need to handle UTF-8 widths
1198 if (xop->xo_flags & XOF_COLUMNS)
1199 xop->xo_columns += rc;
1200 if (xop->xo_flags & XOF_ANCHOR)
1201 xop->xo_anchor_columns += rc;
1204 if (need_nl && code > 0) {
1205 const char *msg = strerror(code);
1207 xo_printf(xop, ": %s", msg);
1211 xo_printf(xop, "\n");
1220 xo_message_hc (xo_handle_t *xop, int code, const char *fmt, ...)
1225 xo_message_hcv(xop, code, fmt, vap);
1230 xo_message_c (int code, const char *fmt, ...)
1235 xo_message_hcv(NULL, code, fmt, vap);
1240 xo_message (const char *fmt, ...)
1246 xo_message_hcv(NULL, code, fmt, vap);
1251 xo_failure (xo_handle_t *xop, const char *fmt, ...)
1253 if (!(xop->xo_flags & XOF_WARN))
1259 xo_warn_hcv(xop, -1, 1, fmt, vap);
1264 * Create a handle for use by later libxo functions.
1266 * Note: normal use of libxo does not require a distinct handle, since
1267 * the default handle (used when NULL is passed) generates text on stdout.
1269 * @style Style of output desired (XO_STYLE_* value)
1270 * @flags Set of XOF_* flags in use with this handle
1273 xo_create (xo_style_t style, xo_xof_flags_t flags)
1275 xo_handle_t *xop = xo_realloc(NULL, sizeof(*xop));
1278 bzero(xop, sizeof(*xop));
1280 xop->xo_style = style;
1281 xop->xo_flags = flags;
1282 xo_init_handle(xop);
1289 * Create a handle that will write to the given file. Use
1290 * the XOF_CLOSE_FP flag to have the file closed on xo_destroy().
1291 * @fp FILE pointer to use
1292 * @style Style of output desired (XO_STYLE_* value)
1293 * @flags Set of XOF_* flags to use with this handle
1296 xo_create_to_file (FILE *fp, xo_style_t style, xo_xof_flags_t flags)
1298 xo_handle_t *xop = xo_create(style, flags);
1301 xop->xo_opaque = fp;
1302 xop->xo_write = xo_write_to_file;
1303 xop->xo_close = xo_close_file;
1310 * Release any resources held by the handle.
1311 * @xop XO handle to alter (or NULL for default handle)
1314 xo_destroy (xo_handle_t *xop_arg)
1316 xo_handle_t *xop = xo_default(xop_arg);
1318 if (xop->xo_close && (xop->xo_flags & XOF_CLOSE_FP))
1319 xop->xo_close(xop->xo_opaque);
1321 xo_free(xop->xo_stack);
1322 xo_buf_cleanup(&xop->xo_data);
1323 xo_buf_cleanup(&xop->xo_fmt);
1324 xo_buf_cleanup(&xop->xo_predicate);
1325 xo_buf_cleanup(&xop->xo_attrs);
1327 if (xop_arg == NULL) {
1328 bzero(&xo_default_handle, sizeof(&xo_default_handle));
1329 xo_default_inited = 0;
1335 * Record a new output style to use for the given handle (or default if
1336 * handle is NULL). This output style will be used for any future output.
1338 * @xop XO handle to alter (or NULL for default handle)
1339 * @style new output style (XO_STYLE_*)
1342 xo_set_style (xo_handle_t *xop, xo_style_t style)
1344 xop = xo_default(xop);
1345 xop->xo_style = style;
1349 xo_get_style (xo_handle_t *xop)
1351 xop = xo_default(xop);
1352 return xop->xo_style;
1356 xo_name_to_style (const char *name)
1358 if (strcmp(name, "xml") == 0)
1359 return XO_STYLE_XML;
1360 else if (strcmp(name, "json") == 0)
1361 return XO_STYLE_JSON;
1362 else if (strcmp(name, "text") == 0)
1363 return XO_STYLE_TEXT;
1364 else if (strcmp(name, "html") == 0)
1365 return XO_STYLE_HTML;
1371 * Convert string name to XOF_* flag value.
1372 * Not all are useful. Or safe. Or sane.
1375 xo_name_to_flag (const char *name)
1377 if (strcmp(name, "pretty") == 0)
1379 if (strcmp(name, "warn") == 0)
1381 if (strcmp(name, "xpath") == 0)
1383 if (strcmp(name, "info") == 0)
1385 if (strcmp(name, "warn-xml") == 0)
1386 return XOF_WARN_XML;
1387 if (strcmp(name, "columns") == 0)
1389 if (strcmp(name, "dtrt") == 0)
1391 if (strcmp(name, "flush") == 0)
1393 if (strcmp(name, "keys") == 0)
1395 if (strcmp(name, "ignore-close") == 0)
1396 return XOF_IGNORE_CLOSE;
1397 if (strcmp(name, "not-first") == 0)
1398 return XOF_NOT_FIRST;
1399 if (strcmp(name, "no-locale") == 0)
1400 return XOF_NO_LOCALE;
1401 if (strcmp(name, "no-top") == 0)
1403 if (strcmp(name, "units") == 0)
1405 if (strcmp(name, "underscores") == 0)
1406 return XOF_UNDERSCORES;
1412 xo_set_style_name (xo_handle_t *xop, const char *name)
1417 int style = xo_name_to_style(name);
1421 xo_set_style(xop, style);
1426 * Set the options for a handle using a string of options
1427 * passed in. The input is a comma-separated set of names
1428 * and optional values: "xml,pretty,indent=4"
1431 xo_set_options (xo_handle_t *xop, const char *input)
1433 char *cp, *ep, *vp, *np, *bp;
1434 int style = -1, new_style, len, rc = 0;
1435 xo_xof_flags_t new_flag;
1440 xop = xo_default(xop);
1443 * We support a simpler, old-school style of giving option
1444 * also, using a single character for each option. It's
1445 * ideal for lazy people, such as myself.
1447 if (*input == ':') {
1450 for (input++ ; *input; input++) {
1453 xop->xo_flags |= XOF_FLUSH;
1457 xop->xo_style = XO_STYLE_HTML;
1461 xop->xo_flags |= XOF_INFO;
1465 sz = strspn(input + 1, "0123456789");
1467 xop->xo_indent_by = atoi(input + 1);
1468 input += sz - 1; /* Skip value */
1473 xop->xo_flags |= XOF_KEYS;
1477 xop->xo_style = XO_STYLE_JSON;
1481 xop->xo_flags |= XOF_PRETTY;
1485 xop->xo_style = XO_STYLE_TEXT;
1489 xop->xo_flags |= XOF_UNITS;
1493 xop->xo_flags |= XOF_UNDERSCORES;
1497 xop->xo_flags |= XOF_WARN;
1501 xop->xo_style = XO_STYLE_XML;
1505 xop->xo_flags |= XOF_XPATH;
1512 len = strlen(input) + 1;
1514 memcpy(bp, input, len);
1516 for (cp = bp, ep = cp + len - 1; cp && cp < ep; cp = np) {
1517 np = strchr(cp, ',');
1521 vp = strchr(cp, '=');
1525 new_style = xo_name_to_style(cp);
1526 if (new_style >= 0) {
1528 xo_warnx("ignoring multiple styles: '%s'", cp);
1532 new_flag = xo_name_to_flag(cp);
1534 xop->xo_flags |= new_flag;
1536 if (strcmp(cp, "indent") == 0) {
1537 xop->xo_indent_by = atoi(vp);
1539 xo_warnx("unknown option: '%s'", cp);
1547 xop->xo_style= style;
1553 * Set one or more flags for a given handle (or default if handle is NULL).
1554 * These flags will affect future output.
1556 * @xop XO handle to alter (or NULL for default handle)
1557 * @flags Flags to be set (XOF_*)
1560 xo_set_flags (xo_handle_t *xop, xo_xof_flags_t flags)
1562 xop = xo_default(xop);
1564 xop->xo_flags |= flags;
1568 xo_get_flags (xo_handle_t *xop)
1570 xop = xo_default(xop);
1572 return xop->xo_flags;
1576 * Record a leading prefix for the XPath we generate. This allows the
1577 * generated data to be placed within an XML hierarchy but still have
1578 * accurate XPath expressions.
1580 * @xop XO handle to alter (or NULL for default handle)
1581 * @path The XPath expression
1584 xo_set_leading_xpath (xo_handle_t *xop, const char *path)
1586 xop = xo_default(xop);
1588 if (xop->xo_leading_xpath) {
1589 xo_free(xop->xo_leading_xpath);
1590 xop->xo_leading_xpath = NULL;
1596 int len = strlen(path);
1597 xop->xo_leading_xpath = xo_realloc(NULL, len + 1);
1598 if (xop->xo_leading_xpath) {
1599 memcpy(xop->xo_leading_xpath, path, len + 1);
1604 * Record the info data for a set of tags
1606 * @xop XO handle to alter (or NULL for default handle)
1607 * @info Info data (xo_info_t) to be recorded (or NULL) (MUST BE SORTED)
1608 * @count Number of entries in info (or -1 to count them ourselves)
1611 xo_set_info (xo_handle_t *xop, xo_info_t *infop, int count)
1613 xop = xo_default(xop);
1615 if (count < 0 && infop) {
1618 for (xip = infop, count = 0; xip->xi_name; xip++, count++)
1622 xop->xo_info = infop;
1623 xop->xo_info_count = count;
1627 * Set the formatter callback for a handle. The callback should
1628 * return a newly formatting contents of a formatting instruction,
1629 * meaning the bits inside the braces.
1632 xo_set_formatter (xo_handle_t *xop, xo_formatter_t func,
1633 xo_checkpointer_t cfunc)
1635 xop = xo_default(xop);
1637 xop->xo_formatter = func;
1638 xop->xo_checkpointer = cfunc;
1642 * Clear one or more flags for a given handle (or default if handle is NULL).
1643 * These flags will affect future output.
1645 * @xop XO handle to alter (or NULL for default handle)
1646 * @flags Flags to be cleared (XOF_*)
1649 xo_clear_flags (xo_handle_t *xop, xo_xof_flags_t flags)
1651 xop = xo_default(xop);
1653 xop->xo_flags &= ~flags;
1657 xo_line_ensure_open (xo_handle_t *xop, xo_xff_flags_t flags UNUSED)
1659 static char div_open[] = "<div class=\"line\">";
1660 static char div_open_blank[] = "<div class=\"blank-line\">";
1662 if (xop->xo_flags & XOF_DIV_OPEN)
1665 if (xop->xo_style != XO_STYLE_HTML)
1668 xop->xo_flags |= XOF_DIV_OPEN;
1669 if (flags & XFF_BLANK_LINE)
1670 xo_data_append(xop, div_open_blank, sizeof(div_open_blank) - 1);
1672 xo_data_append(xop, div_open, sizeof(div_open) - 1);
1674 if (xop->xo_flags & XOF_PRETTY)
1675 xo_data_append(xop, "\n", 1);
1679 xo_line_close (xo_handle_t *xop)
1681 static char div_close[] = "</div>";
1683 switch (xop->xo_style) {
1685 if (!(xop->xo_flags & XOF_DIV_OPEN))
1686 xo_line_ensure_open(xop, 0);
1688 xop->xo_flags &= ~XOF_DIV_OPEN;
1689 xo_data_append(xop, div_close, sizeof(div_close) - 1);
1691 if (xop->xo_flags & XOF_PRETTY)
1692 xo_data_append(xop, "\n", 1);
1696 xo_data_append(xop, "\n", 1);
1702 xo_info_compare (const void *key, const void *data)
1704 const char *name = key;
1705 const xo_info_t *xip = data;
1707 return strcmp(name, xip->xi_name);
1712 xo_info_find (xo_handle_t *xop, const char *name, int nlen)
1715 char *cp = alloca(nlen + 1); /* Need local copy for NUL termination */
1717 memcpy(cp, name, nlen);
1720 xip = bsearch(cp, xop->xo_info, xop->xo_info_count,
1721 sizeof(xop->xo_info[0]), xo_info_compare);
1725 #define CONVERT(_have, _need) (((_have) << 8) | (_need))
1728 * Check to see that the conversion is safe and sane.
1731 xo_check_conversion (xo_handle_t *xop, int have_enc, int need_enc)
1733 switch (CONVERT(have_enc, need_enc)) {
1734 case CONVERT(XF_ENC_UTF8, XF_ENC_UTF8):
1735 case CONVERT(XF_ENC_UTF8, XF_ENC_LOCALE):
1736 case CONVERT(XF_ENC_WIDE, XF_ENC_UTF8):
1737 case CONVERT(XF_ENC_WIDE, XF_ENC_LOCALE):
1738 case CONVERT(XF_ENC_LOCALE, XF_ENC_LOCALE):
1739 case CONVERT(XF_ENC_LOCALE, XF_ENC_UTF8):
1743 xo_failure(xop, "invalid conversion (%c:%c)", have_enc, need_enc);
1749 xo_format_string_direct (xo_handle_t *xop, xo_buffer_t *xbp,
1750 xo_xff_flags_t flags,
1751 const wchar_t *wcp, const char *cp, int len, int max,
1752 int need_enc, int have_enc)
1756 int ilen, olen, width;
1757 int attr = (flags & XFF_ATTR);
1760 if (len > 0 && !xo_buf_has_room(xbp, len))
1770 if ((flags & XFF_UNESCAPE) && (*cp == '\\' || *cp == '%')) {
1776 if (wcp && *wcp == L'\0')
1782 case XF_ENC_WIDE: /* Wide character */
1787 case XF_ENC_UTF8: /* UTF-8 */
1788 ilen = xo_utf8_to_wc_len(cp);
1790 xo_failure(xop, "invalid UTF-8 character: %02hhx", *cp);
1794 if (len > 0 && len < ilen) {
1795 len = 0; /* Break out of the loop */
1799 wc = xo_utf8_char(cp, ilen);
1800 if (wc == (wchar_t) -1) {
1801 xo_failure(xop, "invalid UTF-8 character: %02hhx/%d",
1808 case XF_ENC_LOCALE: /* Native locale */
1809 ilen = (len > 0) ? len : MB_LEN_MAX;
1810 ilen = mbrtowc(&wc, cp, ilen, &xop->xo_mbstate);
1811 if (ilen < 0) { /* Invalid data; skip */
1812 xo_failure(xop, "invalid mbs char: %02hhx", *cp);
1815 if (ilen == 0) { /* Hit a wide NUL character */
1824 /* Reduce len, but not below zero */
1832 * Find the width-in-columns of this character, which must be done
1833 * in wide characters, since we lack a mbswidth() function. If
1836 width = wcwidth(wc);
1838 width = iswcntrl(wc) ? 0 : 1;
1840 if (xop->xo_style == XO_STYLE_TEXT || xop->xo_style == XO_STYLE_HTML) {
1841 if (max > 0 && cols + width > max)
1848 /* Output in UTF-8 needs to be escaped, based on the style */
1849 switch (xop->xo_style) {
1858 else if (attr && wc == '"')
1863 int slen = strlen(sp);
1864 if (!xo_buf_has_room(xbp, slen - 1))
1867 memcpy(xbp->xb_curp, sp, slen);
1868 xbp->xb_curp += slen;
1869 goto done_with_encoding; /* Need multi-level 'break' */
1872 if (wc != '\\' && wc != '"')
1875 if (!xo_buf_has_room(xbp, 2))
1878 *xbp->xb_curp++ = '\\';
1879 *xbp->xb_curp++ = wc & 0x7f;
1880 goto done_with_encoding;
1883 olen = xo_utf8_emit_len(wc);
1885 xo_failure(xop, "ignoring bad length");
1889 if (!xo_buf_has_room(xbp, olen))
1892 xo_utf8_emit_char(xbp->xb_curp, olen, wc);
1893 xbp->xb_curp += olen;
1897 if (!xo_buf_has_room(xbp, MB_LEN_MAX + 1))
1900 olen = wcrtomb(xbp->xb_curp, wc, &xop->xo_mbstate);
1902 xo_failure(xop, "could not convert wide char: %lx",
1903 (unsigned long) wc);
1906 *xbp->xb_curp++ = '?';
1908 xbp->xb_curp += olen;
1920 xo_format_string (xo_handle_t *xop, xo_buffer_t *xbp, xo_xff_flags_t flags,
1923 static char null[] = "(null)";
1926 wchar_t *wcp = NULL;
1927 int len, cols = 0, rc = 0;
1928 int off = xbp->xb_curp - xbp->xb_bufp, off2;
1929 int need_enc = (xop->xo_style == XO_STYLE_TEXT)
1930 ? XF_ENC_LOCALE : XF_ENC_UTF8;
1932 if (xo_check_conversion(xop, xfp->xf_enc, need_enc))
1935 len = xfp->xf_width[XF_WIDTH_SIZE];
1937 if (xfp->xf_enc == XF_ENC_WIDE) {
1938 wcp = va_arg(xop->xo_vap, wchar_t *);
1943 * Dont' deref NULL; use the traditional "(null)" instead
1944 * of the more accurate "who's been a naughty boy, then?".
1948 len = sizeof(null) - 1;
1952 cp = va_arg(xop->xo_vap, char *); /* UTF-8 or native */
1956 /* Echo "Dont' deref NULL" logic */
1959 len = sizeof(null) - 1;
1963 * Optimize the most common case, which is "%s". We just
1964 * need to copy the complete string to the output buffer.
1966 if (xfp->xf_enc == need_enc
1967 && xfp->xf_width[XF_WIDTH_MIN] < 0
1968 && xfp->xf_width[XF_WIDTH_SIZE] < 0
1969 && xfp->xf_width[XF_WIDTH_MAX] < 0
1970 && !(xop->xo_flags & (XOF_ANCHOR | XOF_COLUMNS))) {
1972 xo_buf_escape(xop, xbp, cp, len, flags);
1975 * Our caller expects xb_curp left untouched, so we have
1976 * to reset it and return the number of bytes written to
1979 off2 = xbp->xb_curp - xbp->xb_bufp;
1981 xbp->xb_curp = xbp->xb_bufp + off;
1987 cols = xo_format_string_direct(xop, xbp, flags, wcp, cp, len,
1988 xfp->xf_width[XF_WIDTH_MAX],
1989 need_enc, xfp->xf_enc);
1994 * xo_buf_append* will move xb_curp, so we save/restore it.
1996 off2 = xbp->xb_curp - xbp->xb_bufp;
1998 xbp->xb_curp = xbp->xb_bufp + off;
2000 if (cols < xfp->xf_width[XF_WIDTH_MIN]) {
2002 * Find the number of columns needed to display the string.
2003 * If we have the original wide string, we just call wcswidth,
2004 * but if we did the work ourselves, then we need to do it.
2006 int delta = xfp->xf_width[XF_WIDTH_MIN] - cols;
2007 if (!xo_buf_has_room(xbp, delta))
2011 * If seen_minus, then pad on the right; otherwise move it so
2012 * we can pad on the left.
2014 if (xfp->xf_seen_minus) {
2015 cp = xbp->xb_curp + rc;
2018 memmove(xbp->xb_curp + delta, xbp->xb_curp, rc);
2021 /* Set the padding */
2022 memset(cp, (xfp->xf_leading_zero > 0) ? '0' : ' ', delta);
2027 if (xop->xo_flags & XOF_COLUMNS)
2028 xop->xo_columns += cols;
2029 if (xop->xo_flags & XOF_ANCHOR)
2030 xop->xo_anchor_columns += cols;
2035 xbp->xb_curp = xbp->xb_bufp + off;
2040 xo_data_append_content (xo_handle_t *xop, const char *str, int len)
2043 int need_enc = (xop->xo_style == XO_STYLE_TEXT)
2044 ? XF_ENC_LOCALE : XF_ENC_UTF8;
2046 cols = xo_format_string_direct(xop, &xop->xo_data, XFF_UNESCAPE,
2048 need_enc, XF_ENC_UTF8);
2050 if (xop->xo_flags & XOF_COLUMNS)
2051 xop->xo_columns += cols;
2052 if (xop->xo_flags & XOF_ANCHOR)
2053 xop->xo_anchor_columns += cols;
2057 xo_bump_width (xo_format_t *xfp, int digit)
2059 int *ip = &xfp->xf_width[xfp->xf_dots];
2061 *ip = ((*ip > 0) ? *ip : 0) * 10 + digit;
2065 xo_trim_ws (xo_buffer_t *xbp, int len)
2070 /* First trim leading space */
2071 for (cp = sp = xbp->xb_curp, ep = cp + len; cp < ep; cp++) {
2079 memmove(sp, cp, len);
2082 /* Then trim off the end */
2083 for (cp = xbp->xb_curp, sp = ep = cp + len; cp < ep; ep--) {
2098 xo_format_data (xo_handle_t *xop, xo_buffer_t *xbp,
2099 const char *fmt, int flen, xo_xff_flags_t flags)
2102 const char *cp, *ep, *sp, *xp = NULL;
2104 int style = (flags & XFF_XML) ? XO_STYLE_XML : xop->xo_style;
2105 unsigned make_output = !(flags & XFF_NO_OUTPUT);
2106 int need_enc = (xop->xo_style == XO_STYLE_TEXT)
2107 ? XF_ENC_LOCALE : XF_ENC_UTF8;
2110 xbp = &xop->xo_data;
2112 for (cp = fmt, ep = fmt + flen; cp < ep; cp++) {
2118 if (*cp == '\\' && cp[1] != '\0')
2122 } if (cp + 1 < ep && cp[1] == '%') {
2129 cols = xo_format_string_direct(xop, xbp, flags | XFF_UNESCAPE,
2130 NULL, xp, cp - xp, -1,
2131 need_enc, XF_ENC_UTF8);
2132 if (xop->xo_flags & XOF_COLUMNS)
2133 xop->xo_columns += cols;
2134 if (xop->xo_flags & XOF_ANCHOR)
2135 xop->xo_anchor_columns += cols;
2141 bzero(&xf, sizeof(xf));
2142 xf.xf_leading_zero = -1;
2143 xf.xf_width[0] = xf.xf_width[1] = xf.xf_width[2] = -1;
2146 * "%@" starts an XO-specific set of flags:
2147 * @X@ - XML-only field; ignored if style isn't XML
2150 for (cp += 2; cp < ep; cp++) {
2156 * '*' means there's a "%*.*s" value in vap that
2159 if (!(xop->xo_flags & XOF_NO_VA_ARG))
2160 va_arg(xop->xo_vap, int);
2165 /* Hidden fields are only visible to JSON and XML */
2166 if (xop->xo_flags & XFF_ENCODE_ONLY) {
2167 if (style != XO_STYLE_XML
2168 && xop->xo_style != XO_STYLE_JSON)
2170 } else if (xop->xo_flags & XFF_DISPLAY_ONLY) {
2171 if (style != XO_STYLE_TEXT
2172 && xop->xo_style != XO_STYLE_HTML)
2180 * Looking at one piece of a format; find the end and
2181 * call snprintf. Then advance xo_vap on our own.
2183 * Note that 'n', 'v', and '$' are not supported.
2185 sp = cp; /* Save start pointer */
2186 for (cp += 1; cp < ep; cp++) {
2189 else if (*cp == 'h')
2191 else if (*cp == 'j')
2193 else if (*cp == 't')
2195 else if (*cp == 'z')
2197 else if (*cp == 'q')
2199 else if (*cp == '.') {
2200 if (++xf.xf_dots >= XF_WIDTH_NUM) {
2201 xo_failure(xop, "Too many dots in format: '%s'", fmt);
2204 } else if (*cp == '-')
2205 xf.xf_seen_minus = 1;
2206 else if (isdigit((int) *cp)) {
2207 if (xf.xf_leading_zero < 0)
2208 xf.xf_leading_zero = (*cp == '0');
2209 xo_bump_width(&xf, *cp - '0');
2210 } else if (*cp == '*') {
2212 xf.xf_star[xf.xf_dots] = 1;
2213 } else if (strchr("diouxXDOUeEfFgGaAcCsSp", *cp) != NULL)
2215 else if (*cp == 'n' || *cp == 'v') {
2216 xo_failure(xop, "unsupported format: '%s'", fmt);
2222 xo_failure(xop, "field format missing format character: %s",
2227 if (!(xop->xo_flags & XOF_NO_VA_ARG)) {
2228 if (*cp == 's' || *cp == 'S') {
2229 /* Handle "%*.*.*s" */
2231 for (s = 0; s < XF_WIDTH_NUM; s++) {
2232 if (xf.xf_star[s]) {
2233 xf.xf_width[s] = va_arg(xop->xo_vap, int);
2235 /* Normalize a negative width value */
2236 if (xf.xf_width[s] < 0) {
2238 xf.xf_width[0] = -xf.xf_width[0];
2239 xf.xf_seen_minus = 1;
2241 xf.xf_width[s] = -1; /* Ignore negative values */
2248 /* If no max is given, it defaults to size */
2249 if (xf.xf_width[XF_WIDTH_MAX] < 0 && xf.xf_width[XF_WIDTH_SIZE] >= 0)
2250 xf.xf_width[XF_WIDTH_MAX] = xf.xf_width[XF_WIDTH_SIZE];
2252 if (xf.xf_fc == 'D' || xf.xf_fc == 'O' || xf.xf_fc == 'U')
2256 xo_buffer_t *fbp = &xop->xo_fmt;
2257 int len = cp - sp + 1;
2258 if (!xo_buf_has_room(fbp, len + 1))
2261 char *newfmt = fbp->xb_curp;
2262 memcpy(newfmt, sp, len);
2263 newfmt[0] = '%'; /* If we skipped over a "%@...@s" format */
2267 * Bad news: our strings are UTF-8, but the stock printf
2268 * functions won't handle field widths for wide characters
2269 * correctly. So we have to handle this ourselves.
2271 if (xop->xo_formatter == NULL
2272 && (xf.xf_fc == 's' || xf.xf_fc == 'S')) {
2273 xf.xf_enc = (xf.xf_lflag || (xf.xf_fc == 'S'))
2274 ? XF_ENC_WIDE : xf.xf_hflag ? XF_ENC_LOCALE : XF_ENC_UTF8;
2275 rc = xo_format_string(xop, xbp, flags, &xf);
2277 if ((flags & XFF_TRIM_WS)
2278 && (xop->xo_style == XO_STYLE_XML
2279 || xop->xo_style == XO_STYLE_JSON))
2280 rc = xo_trim_ws(xbp, rc);
2283 int columns = rc = xo_vsnprintf(xop, xbp, newfmt, xop->xo_vap);
2286 * For XML and HTML, we need "&<>" processing; for JSON,
2287 * it's quotes. Text gets nothing.
2291 if (flags & XFF_TRIM_WS)
2292 columns = rc = xo_trim_ws(xbp, rc);
2295 rc = xo_escape_xml(xbp, rc, (flags & XFF_ATTR));
2299 if (flags & XFF_TRIM_WS)
2300 columns = rc = xo_trim_ws(xbp, rc);
2301 rc = xo_escape_json(xbp, rc);
2306 * We can assume all the data we've added is ASCII, so
2307 * the columns and bytes are the same. xo_format_string
2308 * handles all the fancy string conversions and updates
2309 * xo_anchor_columns accordingly.
2311 if (xop->xo_flags & XOF_COLUMNS)
2312 xop->xo_columns += columns;
2313 if (xop->xo_flags & XOF_ANCHOR)
2314 xop->xo_anchor_columns += columns;
2321 * Now for the tricky part: we need to move the argument pointer
2322 * along by the amount needed.
2324 if (!(xop->xo_flags & XOF_NO_VA_ARG)) {
2326 if (xf.xf_fc == 's' ||xf.xf_fc == 'S') {
2328 * The 'S' and 's' formats are normally handled in
2329 * xo_format_string, but if we skipped it, then we
2333 va_arg(xop->xo_vap, char *);
2337 for (s = 0; s < XF_WIDTH_NUM; s++) {
2339 va_arg(xop->xo_vap, int);
2342 if (strchr("diouxXDOU", xf.xf_fc) != NULL) {
2343 if (xf.xf_hflag > 1) {
2344 va_arg(xop->xo_vap, int);
2346 } else if (xf.xf_hflag > 0) {
2347 va_arg(xop->xo_vap, int);
2349 } else if (xf.xf_lflag > 1) {
2350 va_arg(xop->xo_vap, unsigned long long);
2352 } else if (xf.xf_lflag > 0) {
2353 va_arg(xop->xo_vap, unsigned long);
2355 } else if (xf.xf_jflag > 0) {
2356 va_arg(xop->xo_vap, intmax_t);
2358 } else if (xf.xf_tflag > 0) {
2359 va_arg(xop->xo_vap, ptrdiff_t);
2361 } else if (xf.xf_zflag > 0) {
2362 va_arg(xop->xo_vap, size_t);
2364 } else if (xf.xf_qflag > 0) {
2365 va_arg(xop->xo_vap, quad_t);
2368 va_arg(xop->xo_vap, int);
2370 } else if (strchr("eEfFgGaA", xf.xf_fc) != NULL)
2372 va_arg(xop->xo_vap, long double);
2374 va_arg(xop->xo_vap, double);
2376 else if (xf.xf_fc == 'C' || (xf.xf_fc == 'c' && xf.xf_lflag))
2377 va_arg(xop->xo_vap, wint_t);
2379 else if (xf.xf_fc == 'c')
2380 va_arg(xop->xo_vap, int);
2382 else if (xf.xf_fc == 'p')
2383 va_arg(xop->xo_vap, void *);
2390 cols = xo_format_string_direct(xop, xbp, flags | XFF_UNESCAPE,
2391 NULL, xp, cp - xp, -1,
2392 need_enc, XF_ENC_UTF8);
2393 if (xop->xo_flags & XOF_COLUMNS)
2394 xop->xo_columns += cols;
2395 if (xop->xo_flags & XOF_ANCHOR)
2396 xop->xo_anchor_columns += cols;
2406 xo_fix_encoding (xo_handle_t *xop UNUSED, char *encoding)
2408 char *cp = encoding;
2410 if (cp[0] != '%' || !isdigit((int) cp[1]))
2413 for (cp += 2; *cp; cp++) {
2414 if (!isdigit((int) *cp))
2425 xo_buf_append_div (xo_handle_t *xop, const char *class, xo_xff_flags_t flags,
2426 const char *name, int nlen,
2427 const char *value, int vlen,
2428 const char *encoding, int elen)
2430 static char div_start[] = "<div class=\"";
2431 static char div_tag[] = "\" data-tag=\"";
2432 static char div_xpath[] = "\" data-xpath=\"";
2433 static char div_key[] = "\" data-key=\"key";
2434 static char div_end[] = "\">";
2435 static char div_close[] = "</div>";
2438 * To build our XPath predicate, we need to save the va_list before
2439 * we format our data, and then restore it before we format the
2441 * Display-only keys implies that we've got an encode-only key
2442 * elsewhere, so we don't use them from making predicates.
2444 int need_predidate =
2445 (name && (flags & XFF_KEY) && !(flags & XFF_DISPLAY_ONLY)
2446 && (xop->xo_flags & XOF_XPATH));
2448 if (need_predidate) {
2451 va_copy(va_local, xop->xo_vap);
2452 if (xop->xo_checkpointer)
2453 xop->xo_checkpointer(xop, xop->xo_vap, 0);
2456 * Build an XPath predicate expression to match this key.
2457 * We use the format buffer.
2459 xo_buffer_t *pbp = &xop->xo_predicate;
2460 pbp->xb_curp = pbp->xb_bufp; /* Restart buffer */
2462 xo_buf_append(pbp, "[", 1);
2463 xo_buf_escape(xop, pbp, name, nlen, 0);
2464 if (xop->xo_flags & XOF_PRETTY)
2465 xo_buf_append(pbp, " = '", 4);
2467 xo_buf_append(pbp, "='", 2);
2469 /* The encoding format defaults to the normal format */
2470 if (encoding == NULL) {
2471 char *enc = alloca(vlen + 1);
2472 memcpy(enc, value, vlen);
2474 encoding = xo_fix_encoding(xop, enc);
2475 elen = strlen(encoding);
2478 xo_format_data(xop, pbp, encoding, elen, XFF_XML | XFF_ATTR);
2480 xo_buf_append(pbp, "']", 2);
2482 /* Now we record this predicate expression in the stack */
2483 xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth];
2484 int olen = xsp->xs_keys ? strlen(xsp->xs_keys) : 0;
2485 int dlen = pbp->xb_curp - pbp->xb_bufp;
2487 char *cp = xo_realloc(xsp->xs_keys, olen + dlen + 1);
2489 memcpy(cp + olen, pbp->xb_bufp, dlen);
2490 cp[olen + dlen] = '\0';
2494 /* Now we reset the xo_vap as if we were never here */
2495 va_end(xop->xo_vap);
2496 va_copy(xop->xo_vap, va_local);
2498 if (xop->xo_checkpointer)
2499 xop->xo_checkpointer(xop, xop->xo_vap, 1);
2502 if (flags & XFF_ENCODE_ONLY) {
2504 * Even if this is encode-only, we need to go thru the
2505 * work of formatting it to make sure the args are cleared
2508 xo_format_data(xop, &xop->xo_data, encoding, elen,
2509 flags | XFF_NO_OUTPUT);
2513 xo_line_ensure_open(xop, 0);
2515 if (xop->xo_flags & XOF_PRETTY)
2516 xo_buf_indent(xop, xop->xo_indent_by);
2518 xo_data_append(xop, div_start, sizeof(div_start) - 1);
2519 xo_data_append(xop, class, strlen(class));
2522 xo_data_append(xop, div_tag, sizeof(div_tag) - 1);
2523 xo_data_escape(xop, name, nlen);
2526 * Save the offset at which we'd place units. See xo_format_units.
2528 if (xop->xo_flags & XOF_UNITS) {
2529 xop->xo_flags |= XOF_UNITS_PENDING;
2531 * Note: We need the '+1' here because we know we've not
2532 * added the closing quote. We add one, knowing the quote
2533 * will be added shortly.
2535 xop->xo_units_offset =
2536 xop->xo_data.xb_curp -xop->xo_data.xb_bufp + 1;
2541 if (xop->xo_flags & XOF_XPATH) {
2545 xo_data_append(xop, div_xpath, sizeof(div_xpath) - 1);
2546 if (xop->xo_leading_xpath)
2547 xo_data_append(xop, xop->xo_leading_xpath,
2548 strlen(xop->xo_leading_xpath));
2550 for (i = 0; i <= xop->xo_depth; i++) {
2551 xsp = &xop->xo_stack[i];
2552 if (xsp->xs_name == NULL)
2555 xo_data_append(xop, "/", 1);
2556 xo_data_escape(xop, xsp->xs_name, strlen(xsp->xs_name));
2558 /* Don't show keys for the key field */
2559 if (i != xop->xo_depth || !(flags & XFF_KEY))
2560 xo_data_append(xop, xsp->xs_keys, strlen(xsp->xs_keys));
2564 xo_data_append(xop, "/", 1);
2565 xo_data_escape(xop, name, nlen);
2568 if ((xop->xo_flags & XOF_INFO) && xop->xo_info) {
2569 static char in_type[] = "\" data-type=\"";
2570 static char in_help[] = "\" data-help=\"";
2572 xo_info_t *xip = xo_info_find(xop, name, nlen);
2575 xo_data_append(xop, in_type, sizeof(in_type) - 1);
2576 xo_data_escape(xop, xip->xi_type, strlen(xip->xi_type));
2579 xo_data_append(xop, in_help, sizeof(in_help) - 1);
2580 xo_data_escape(xop, xip->xi_help, strlen(xip->xi_help));
2585 if ((flags & XFF_KEY) && (xop->xo_flags & XOF_KEYS))
2586 xo_data_append(xop, div_key, sizeof(div_key) - 1);
2589 xo_data_append(xop, div_end, sizeof(div_end) - 1);
2591 xo_format_data(xop, NULL, value, vlen, 0);
2593 xo_data_append(xop, div_close, sizeof(div_close) - 1);
2595 if (xop->xo_flags & XOF_PRETTY)
2596 xo_data_append(xop, "\n", 1);
2600 xo_format_text (xo_handle_t *xop, const char *str, int len)
2602 switch (xop->xo_style) {
2604 xo_buf_append_locale(xop, &xop->xo_data, str, len);
2608 xo_buf_append_div(xop, "text", 0, NULL, 0, str, len, NULL, 0);
2614 xo_format_title (xo_handle_t *xop, const char *str, int len,
2615 const char *fmt, int flen)
2617 static char div_open[] = "<div class=\"title\">";
2618 static char div_close[] = "</div>";
2620 switch (xop->xo_style) {
2624 * Even though we don't care about text, we need to do
2625 * enough parsing work to skip over the right bits of xo_vap.
2628 xo_format_data(xop, NULL, fmt, flen, XFF_NO_OUTPUT);
2632 xo_buffer_t *xbp = &xop->xo_data;
2633 int start = xbp->xb_curp - xbp->xb_bufp;
2634 int left = xbp->xb_size - start;
2636 int need_enc = XF_ENC_LOCALE;
2638 if (xop->xo_style == XO_STYLE_HTML) {
2639 need_enc = XF_ENC_UTF8;
2640 xo_line_ensure_open(xop, 0);
2641 if (xop->xo_flags & XOF_PRETTY)
2642 xo_buf_indent(xop, xop->xo_indent_by);
2643 xo_buf_append(&xop->xo_data, div_open, sizeof(div_open) - 1);
2646 start = xbp->xb_curp - xbp->xb_bufp; /* Reset start */
2648 char *newfmt = alloca(flen + 1);
2649 memcpy(newfmt, fmt, flen);
2650 newfmt[flen] = '\0';
2652 /* If len is non-zero, the format string apply to the name */
2653 char *newstr = alloca(len + 1);
2654 memcpy(newstr, str, len);
2657 if (newstr[len - 1] == 's') {
2661 rc = snprintf(NULL, 0, newfmt, newstr);
2664 * We have to do this the hard way, since we might need
2667 bp = alloca(rc + 1);
2668 rc = snprintf(bp, rc + 1, newfmt, newstr);
2669 cols = xo_format_string_direct(xop, xbp, 0, NULL, bp, rc, -1,
2670 need_enc, XF_ENC_UTF8);
2672 if (xop->xo_flags & XOF_COLUMNS)
2673 xop->xo_columns += cols;
2674 if (xop->xo_flags & XOF_ANCHOR)
2675 xop->xo_anchor_columns += cols;
2681 rc = snprintf(xbp->xb_curp, left, newfmt, newstr);
2683 if (!xo_buf_has_room(xbp, rc))
2685 left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp);
2686 rc = snprintf(xbp->xb_curp, left, newfmt, newstr);
2690 if (xop->xo_flags & XOF_COLUMNS)
2691 xop->xo_columns += rc;
2692 if (xop->xo_flags & XOF_ANCHOR)
2693 xop->xo_anchor_columns += rc;
2698 xo_format_data(xop, NULL, fmt, flen, 0);
2700 /* xo_format_data moved curp, so we need to reset it */
2701 rc = xbp->xb_curp - (xbp->xb_bufp + start);
2702 xbp->xb_curp = xbp->xb_bufp + start;
2705 /* If we're styling HTML, then we need to escape it */
2706 if (xop->xo_style == XO_STYLE_HTML) {
2707 rc = xo_escape_xml(xbp, rc, 0);
2714 if (xop->xo_style == XO_STYLE_HTML) {
2715 xo_data_append(xop, div_close, sizeof(div_close) - 1);
2716 if (xop->xo_flags & XOF_PRETTY)
2717 xo_data_append(xop, "\n", 1);
2722 xo_format_prep (xo_handle_t *xop, xo_xff_flags_t flags)
2724 if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST) {
2725 xo_data_append(xop, ",", 1);
2726 if (!(flags & XFF_LEAF_LIST) && (xop->xo_flags & XOF_PRETTY))
2727 xo_data_append(xop, "\n", 1);
2729 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST;
2733 /* Useful debugging function */
2735 xo_arg (xo_handle_t *xop);
2737 xo_arg (xo_handle_t *xop)
2739 xop = xo_default(xop);
2740 fprintf(stderr, "0x%x", va_arg(xop->xo_vap, unsigned));
2745 xo_format_value (xo_handle_t *xop, const char *name, int nlen,
2746 const char *format, int flen,
2747 const char *encoding, int elen, xo_xff_flags_t flags)
2749 int pretty = (xop->xo_flags & XOF_PRETTY);
2753 switch (xop->xo_style) {
2755 if (flags & XFF_ENCODE_ONLY)
2756 flags |= XFF_NO_OUTPUT;
2757 xo_format_data(xop, NULL, format, flen, flags);
2761 if (flags & XFF_ENCODE_ONLY)
2762 flags |= XFF_NO_OUTPUT;
2763 xo_buf_append_div(xop, "data", flags, name, nlen,
2764 format, flen, encoding, elen);
2769 * Even though we're not making output, we still need to
2770 * let the formatting code handle the va_arg popping.
2772 if (flags & XFF_DISPLAY_ONLY) {
2773 flags |= XFF_NO_OUTPUT;
2774 xo_format_data(xop, NULL, format, flen, flags);
2782 char *enc = alloca(flen + 1);
2783 memcpy(enc, format, flen);
2785 format = xo_fix_encoding(xop, enc);
2786 flen = strlen(format);
2790 static char missing[] = "missing-field-name";
2791 xo_failure(xop, "missing field name: %s", format);
2793 nlen = sizeof(missing) - 1;
2797 xo_buf_indent(xop, -1);
2798 xo_data_append(xop, "<", 1);
2799 xo_data_escape(xop, name, nlen);
2801 if (xop->xo_attrs.xb_curp != xop->xo_attrs.xb_bufp) {
2802 xo_data_append(xop, xop->xo_attrs.xb_bufp,
2803 xop->xo_attrs.xb_curp - xop->xo_attrs.xb_bufp);
2804 xop->xo_attrs.xb_curp = xop->xo_attrs.xb_bufp;
2808 * We indicate 'key' fields using the 'key' attribute. While
2809 * this is really committing the crime of mixing meta-data with
2810 * data, it's often useful. Especially when format meta-data is
2811 * difficult to come by.
2813 if ((flags & XFF_KEY) && (xop->xo_flags & XOF_KEYS)) {
2814 static char attr[] = " key=\"key\"";
2815 xo_data_append(xop, attr, sizeof(attr) - 1);
2819 * Save the offset at which we'd place units. See xo_format_units.
2821 if (xop->xo_flags & XOF_UNITS) {
2822 xop->xo_flags |= XOF_UNITS_PENDING;
2823 xop->xo_units_offset = xop->xo_data.xb_curp -xop->xo_data.xb_bufp;
2826 xo_data_append(xop, ">", 1);
2827 xo_format_data(xop, NULL, format, flen, flags);
2828 xo_data_append(xop, "</", 2);
2829 xo_data_escape(xop, name, nlen);
2830 xo_data_append(xop, ">", 1);
2832 xo_data_append(xop, "\n", 1);
2836 if (flags & XFF_DISPLAY_ONLY) {
2837 flags |= XFF_NO_OUTPUT;
2838 xo_format_data(xop, NULL, format, flen, flags);
2846 char *enc = alloca(flen + 1);
2847 memcpy(enc, format, flen);
2849 format = xo_fix_encoding(xop, enc);
2850 flen = strlen(format);
2853 int first = !(xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST);
2855 xo_format_prep(xop, flags);
2857 if (flags & XFF_QUOTE)
2859 else if (flags & XFF_NOQUOTE)
2861 else if (flen == 0) {
2863 format = "true"; /* JSON encodes empty tags as a boolean true */
2865 } else if (strchr("diouxXDOUeEfFgGaAcCp", format[flen - 1]) == NULL)
2871 static char missing[] = "missing-field-name";
2872 xo_failure(xop, "missing field name: %s", format);
2874 nlen = sizeof(missing) - 1;
2877 if (flags & XFF_LEAF_LIST) {
2878 if (first && pretty)
2879 xo_buf_indent(xop, -1);
2882 xo_buf_indent(xop, -1);
2883 xo_data_append(xop, "\"", 1);
2885 xbp = &xop->xo_data;
2886 int off = xbp->xb_curp - xbp->xb_bufp;
2888 xo_data_escape(xop, name, nlen);
2890 if (xop->xo_flags & XOF_UNDERSCORES) {
2891 int now = xbp->xb_curp - xbp->xb_bufp;
2892 for ( ; off < now; off++)
2893 if (xbp->xb_bufp[off] == '-')
2894 xbp->xb_bufp[off] = '_';
2896 xo_data_append(xop, "\":", 2);
2900 xo_data_append(xop, " ", 1);
2902 xo_data_append(xop, "\"", 1);
2904 xo_format_data(xop, NULL, format, flen, flags);
2907 xo_data_append(xop, "\"", 1);
2913 xo_format_content (xo_handle_t *xop, const char *class_name,
2914 const char *xml_tag, int display_only,
2915 const char *str, int len, const char *fmt, int flen)
2917 switch (xop->xo_style) {
2920 xo_data_append_content(xop, str, len);
2922 xo_format_data(xop, NULL, fmt, flen, 0);
2931 xo_buf_append_div(xop, class_name, 0, NULL, 0, str, len, NULL, 0);
2941 xo_open_container_h(xop, xml_tag);
2942 xo_format_value(xop, "message", 7, str, len, NULL, 0, 0);
2943 xo_close_container_h(xop, xml_tag);
2947 * Even though we don't care about labels, we need to do
2948 * enough parsing work to skip over the right bits of xo_vap.
2951 xo_format_data(xop, NULL, fmt, flen, XFF_NO_OUTPUT);
2957 * Even though we don't care about labels, we need to do
2958 * enough parsing work to skip over the right bits of xo_vap.
2962 xo_format_data(xop, NULL, fmt, flen, XFF_NO_OUTPUT);
2965 /* XXX need schem for representing errors in JSON */
2971 xo_format_units (xo_handle_t *xop, const char *str, int len,
2972 const char *fmt, int flen)
2974 static char units_start_xml[] = " units=\"";
2975 static char units_start_html[] = " data-units=\"";
2977 if (!(xop->xo_flags & XOF_UNITS_PENDING)) {
2978 xo_format_content(xop, "units", NULL, 1, str, len, fmt, flen);
2982 xo_buffer_t *xbp = &xop->xo_data;
2983 int start = xop->xo_units_offset;
2984 int stop = xbp->xb_curp - xbp->xb_bufp;
2986 if (xop->xo_style == XO_STYLE_XML)
2987 xo_buf_append(xbp, units_start_xml, sizeof(units_start_xml) - 1);
2988 else if (xop->xo_style == XO_STYLE_HTML)
2989 xo_buf_append(xbp, units_start_html, sizeof(units_start_html) - 1);
2994 xo_data_append(xop, str, len);
2996 xo_format_data(xop, NULL, fmt, flen, 0);
2998 xo_buf_append(xbp, "\"", 1);
3000 int now = xbp->xb_curp - xbp->xb_bufp;
3001 int delta = now - stop;
3002 if (delta < 0) { /* Strange; no output to move */
3003 xbp->xb_curp = xbp->xb_bufp + stop; /* Reset buffer to prior state */
3008 * Now we're in it alright. We've need to insert the unit value
3009 * we just created into the right spot. We make a local copy,
3010 * move it and then insert our copy. We know there's room in the
3011 * buffer, since we're just moving this around.
3013 char *buf = alloca(delta);
3015 memcpy(buf, xbp->xb_bufp + stop, delta);
3016 memmove(xbp->xb_bufp + start + delta, xbp->xb_bufp + start, stop - start);
3017 memmove(xbp->xb_bufp + start, buf, delta);
3021 xo_find_width (xo_handle_t *xop, const char *str, int len,
3022 const char *fmt, int flen)
3029 bp = alloca(len + 1); /* Make local NUL-terminated copy of str */
3030 memcpy(bp, str, len);
3033 width = strtol(bp, &cp, 0);
3034 if (width == LONG_MIN || width == LONG_MAX
3035 || bp == cp || *cp != '\0' ) {
3037 xo_failure(xop, "invalid width for anchor: '%s'", bp);
3040 if (flen != 2 || strncmp("%d", fmt, flen) != 0)
3041 xo_failure(xop, "invalid width format: '%*.*s'", flen, flen, fmt);
3042 if (!(xop->xo_flags & XOF_NO_VA_ARG))
3043 width = va_arg(xop->xo_vap, int);
3050 xo_anchor_clear (xo_handle_t *xop)
3052 xop->xo_flags &= ~XOF_ANCHOR;
3053 xop->xo_anchor_offset = 0;
3054 xop->xo_anchor_columns = 0;
3055 xop->xo_anchor_min_width = 0;
3059 * An anchor is a marker used to delay field width implications.
3060 * Imagine the format string "{[:10}{min:%d}/{cur:%d}/{max:%d}{:]}".
3061 * We are looking for output like " 1/4/5"
3063 * To make this work, we record the anchor and then return to
3064 * format it when the end anchor tag is seen.
3067 xo_anchor_start (xo_handle_t *xop, const char *str, int len,
3068 const char *fmt, int flen)
3070 if (xop->xo_style != XO_STYLE_TEXT && xop->xo_style != XO_STYLE_HTML)
3073 if (xop->xo_flags & XOF_ANCHOR)
3074 xo_failure(xop, "the anchor already recording is discarded");
3076 xop->xo_flags |= XOF_ANCHOR;
3077 xo_buffer_t *xbp = &xop->xo_data;
3078 xop->xo_anchor_offset = xbp->xb_curp - xbp->xb_bufp;
3079 xop->xo_anchor_columns = 0;
3082 * Now we find the width, if possible. If it's not there,
3083 * we'll get it on the end anchor.
3085 xop->xo_anchor_min_width = xo_find_width(xop, str, len, fmt, flen);
3089 xo_anchor_stop (xo_handle_t *xop, const char *str, int len,
3090 const char *fmt, int flen)
3092 if (xop->xo_style != XO_STYLE_TEXT && xop->xo_style != XO_STYLE_HTML)
3095 if (!(xop->xo_flags & XOF_ANCHOR)) {
3096 xo_failure(xop, "no start anchor");
3100 xop->xo_flags &= ~XOF_UNITS_PENDING;
3102 int width = xo_find_width(xop, str, len, fmt, flen);
3104 width = xop->xo_anchor_min_width;
3106 if (width == 0) /* No width given; nothing to do */
3109 xo_buffer_t *xbp = &xop->xo_data;
3110 int start = xop->xo_anchor_offset;
3111 int stop = xbp->xb_curp - xbp->xb_bufp;
3112 int abswidth = (width > 0) ? width : -width;
3113 int blen = abswidth - xop->xo_anchor_columns;
3115 if (blen <= 0) /* Already over width */
3118 if (abswidth > XO_MAX_ANCHOR_WIDTH) {
3119 xo_failure(xop, "width over %u are not supported",
3120 XO_MAX_ANCHOR_WIDTH);
3124 /* Make a suitable padding field and emit it */
3125 char *buf = alloca(blen);
3126 memset(buf, ' ', blen);
3127 xo_format_content(xop, "padding", NULL, 1, buf, blen, NULL, 0);
3129 if (width < 0) /* Already left justified */
3132 int now = xbp->xb_curp - xbp->xb_bufp;
3133 int delta = now - stop;
3134 if (delta < 0) /* Strange; no output to move */
3138 * Now we're in it alright. We've need to insert the padding data
3139 * we just created (which might be an HTML <div> or text) before
3140 * the formatted data. We make a local copy, move it and then
3141 * insert our copy. We know there's room in the buffer, since
3142 * we're just moving this around.
3145 buf = alloca(delta); /* Expand buffer if needed */
3147 memcpy(buf, xbp->xb_bufp + stop, delta);
3148 memmove(xbp->xb_bufp + start + delta, xbp->xb_bufp + start, stop - start);
3149 memmove(xbp->xb_bufp + start, buf, delta);
3152 xo_anchor_clear(xop);
3156 xo_do_emit (xo_handle_t *xop, const char *fmt)
3159 const char *cp, *sp, *ep, *basep;
3161 int flush = (xop->xo_flags & XOF_FLUSH) ? 1 : 0;
3163 xop->xo_columns = 0; /* Always reset it */
3165 for (cp = fmt; *cp; ) {
3172 } else if (*cp == '{') {
3173 if (cp[1] == '{') { /* Start of {{escaped braces}} */
3175 cp += 2; /* Skip over _both_ characters */
3176 for (sp = cp; *sp; sp++) {
3177 if (*sp == '}' && sp[1] == '}')
3181 xo_failure(xop, "missing closing '}}': %s", fmt);
3185 xo_format_text(xop, cp, sp - cp);
3187 /* Move along the string, but don't run off the end */
3188 if (*sp == '}' && sp[1] == '}')
3190 cp = *sp ? sp + 1 : sp;
3193 /* Else fall thru to the code below */
3197 for (sp = cp; *sp; sp++) {
3198 if (*sp == '{' || *sp == '\n')
3201 xo_format_text(xop, cp, sp - cp);
3210 * We are looking at the start of a field definition. The format is:
3211 * '{' modifiers ':' content [ '/' print-fmt [ '/' encode-fmt ]] '}'
3212 * Modifiers are optional and include the following field types:
3213 * 'D': decoration; something non-text and non-data (colons, commmas)
3214 * 'E': error message
3215 * 'L': label; text preceding data
3216 * 'N': note; text following data
3217 * 'P': padding; whitespace
3218 * 'T': Title, where 'content' is a column title
3219 * 'U': Units, where 'content' is the unit label
3220 * 'V': value, where 'content' is the name of the field (the default)
3221 * 'W': warning message
3222 * '[': start a section of anchored text
3223 * ']': end a section of anchored text
3224 * The following flags are also supported:
3225 * 'c': flag: emit a colon after the label
3226 * 'd': field is only emitted for display formats (text and html)
3227 * 'e': field is only emitted for encoding formats (xml and json)
3228 * 'k': this field is a key, suitable for XPath predicates
3229 * 'l': a leaf-list, a simple list of values
3230 * 'n': no quotes around this field
3231 * 'q': add quotes around this field
3232 * 't': trim whitespace around the value
3233 * 'w': emit a blank after the label
3234 * The print-fmt and encode-fmt strings is the printf-style formating
3235 * for this data. JSON and XML will use the encoding-fmt, if present.
3236 * If the encode-fmt is not provided, it defaults to the print-fmt.
3237 * If the print-fmt is not provided, it defaults to 's'.
3239 unsigned ftype = 0, flags = 0;
3240 const char *content = NULL, *format = NULL, *encoding = NULL;
3241 int clen = 0, flen = 0, elen = 0;
3243 for (sp = basep; sp; sp++) {
3244 if (*sp == ':' || *sp == '/' || *sp == '}')
3248 if (sp[1] == '\0') {
3249 xo_failure(xop, "backslash at the end of string");
3269 xo_failure(xop, "field descriptor uses multiple types: %s",
3281 flags |= XFF_DISPLAY_ONLY;
3285 flags |= XFF_ENCODE_ONLY;
3293 flags |= XFF_LEAF_LIST;
3297 flags |= XFF_NOQUOTE;
3305 flags |= XFF_TRIM_WS;
3313 xo_failure(xop, "field descriptor uses unknown modifier: %s",
3316 * No good answer here; a bad format will likely
3317 * mean a core file. We just return and hope
3318 * the caller notices there's no output, and while
3319 * that seems, well, bad. There's nothing better.
3326 for (ep = ++sp; *sp; sp++) {
3327 if (*sp == '}' || *sp == '/')
3330 if (sp[1] == '\0') {
3331 xo_failure(xop, "backslash at the end of string");
3343 xo_failure(xop, "missing content (':'): %s", fmt);
3348 for (ep = ++sp; *sp; sp++) {
3349 if (*sp == '}' || *sp == '/')
3352 if (sp[1] == '\0') {
3353 xo_failure(xop, "backslash at the end of string");
3365 for (ep = ++sp; *sp; sp++) {
3376 xo_failure(xop, "missing closing '}': %s", fmt);
3380 if (format == NULL && ftype != '[' && ftype != ']' ) {
3385 if (ftype == 0 || ftype == 'V')
3386 xo_format_value(xop, content, clen, format, flen,
3387 encoding, elen, flags);
3388 else if (ftype == 'D')
3389 xo_format_content(xop, "decoration", NULL, 1,
3390 content, clen, format, flen);
3391 else if (ftype == 'E')
3392 xo_format_content(xop, "error", "error", 0,
3393 content, clen, format, flen);
3394 else if (ftype == 'L')
3395 xo_format_content(xop, "label", NULL, 1,
3396 content, clen, format, flen);
3397 else if (ftype == 'N')
3398 xo_format_content(xop, "note", NULL, 1,
3399 content, clen, format, flen);
3400 else if (ftype == 'P')
3401 xo_format_content(xop, "padding", NULL, 1,
3402 content, clen, format, flen);
3403 else if (ftype == 'T')
3404 xo_format_title(xop, content, clen, format, flen);
3405 else if (ftype == 'U') {
3407 xo_format_content(xop, "padding", NULL, 1, " ", 1, NULL, 0);
3408 xo_format_units(xop, content, clen, format, flen);
3409 } else if (ftype == 'W')
3410 xo_format_content(xop, "warning", "warning", 0,
3411 content, clen, format, flen);
3412 else if (ftype == '[')
3413 xo_anchor_start(xop, content, clen, format, flen);
3414 else if (ftype == ']')
3415 xo_anchor_stop(xop, content, clen, format, flen);
3417 if (flags & XFF_COLON)
3418 xo_format_content(xop, "decoration", NULL, 1, ":", 1, NULL, 0);
3419 if (ftype != 'U' && (flags & XFF_WS))
3420 xo_format_content(xop, "padding", NULL, 1, " ", 1, NULL, 0);
3422 cp += sp - basep + 1;
3429 /* If we don't have an anchor, write the text out */
3430 if (flush && !(xop->xo_flags & XOF_ANCHOR))
3433 return (rc < 0) ? rc : (int) xop->xo_columns;
3437 xo_emit_hv (xo_handle_t *xop, const char *fmt, va_list vap)
3441 xop = xo_default(xop);
3442 va_copy(xop->xo_vap, vap);
3443 rc = xo_do_emit(xop, fmt);
3444 va_end(xop->xo_vap);
3445 bzero(&xop->xo_vap, sizeof(xop->xo_vap));
3451 xo_emit_h (xo_handle_t *xop, const char *fmt, ...)
3455 xop = xo_default(xop);
3456 va_start(xop->xo_vap, fmt);
3457 rc = xo_do_emit(xop, fmt);
3458 va_end(xop->xo_vap);
3459 bzero(&xop->xo_vap, sizeof(xop->xo_vap));
3465 xo_emit (const char *fmt, ...)
3467 xo_handle_t *xop = xo_default(NULL);
3470 va_start(xop->xo_vap, fmt);
3471 rc = xo_do_emit(xop, fmt);
3472 va_end(xop->xo_vap);
3473 bzero(&xop->xo_vap, sizeof(xop->xo_vap));
3479 xo_attr_hv (xo_handle_t *xop, const char *name, const char *fmt, va_list vap)
3481 const int extra = 5; /* space, equals, quote, quote, and nul */
3482 xop = xo_default(xop);
3484 if (xop->xo_style != XO_STYLE_XML)
3487 int nlen = strlen(name);
3488 xo_buffer_t *xbp = &xop->xo_attrs;
3490 if (!xo_buf_has_room(xbp, nlen + extra))
3493 *xbp->xb_curp++ = ' ';
3494 memcpy(xbp->xb_curp, name, nlen);
3495 xbp->xb_curp += nlen;
3496 *xbp->xb_curp++ = '=';
3497 *xbp->xb_curp++ = '"';
3499 int rc = xo_vsnprintf(xop, xbp, fmt, vap);
3502 rc = xo_escape_xml(xbp, rc, 1);
3506 if (!xo_buf_has_room(xbp, 2))
3509 *xbp->xb_curp++ = '"';
3510 *xbp->xb_curp = '\0';
3512 return rc + nlen + extra;
3516 xo_attr_h (xo_handle_t *xop, const char *name, const char *fmt, ...)
3522 rc = xo_attr_hv(xop, name, fmt, vap);
3529 xo_attr (const char *name, const char *fmt, ...)
3535 rc = xo_attr_hv(NULL, name, fmt, vap);
3542 xo_stack_set_flags (xo_handle_t *xop)
3544 if (xop->xo_flags & XOF_NOT_FIRST) {
3545 xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth];
3547 xsp->xs_flags |= XSF_NOT_FIRST;
3548 xop->xo_flags &= ~XOF_NOT_FIRST;
3553 xo_depth_change (xo_handle_t *xop, const char *name,
3554 int delta, int indent, xo_xsf_flags_t flags)
3556 if (xop->xo_flags & XOF_DTRT)
3559 if (delta >= 0) { /* Push operation */
3560 if (xo_depth_check(xop, xop->xo_depth + delta))
3563 xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth + delta];
3564 xsp->xs_flags = flags;
3565 xo_stack_set_flags(xop);
3567 unsigned save = (xop->xo_flags & (XOF_XPATH | XOF_WARN | XOF_DTRT));
3568 save |= (flags & XSF_DTRT);
3571 int len = strlen(name) + 1;
3572 char *cp = xo_realloc(NULL, len);
3574 memcpy(cp, name, len);
3579 } else { /* Pop operation */
3580 if (xop->xo_depth == 0) {
3581 if (!(xop->xo_flags & XOF_IGNORE_CLOSE))
3582 xo_failure(xop, "close with empty stack: '%s'", name);
3586 xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth];
3587 if (xop->xo_flags & XOF_WARN) {
3588 const char *top = xsp->xs_name;
3589 if (top && strcmp(name, top) != 0) {
3590 xo_failure(xop, "incorrect close: '%s' .vs. '%s'",
3594 if ((xsp->xs_flags & XSF_LIST) != (flags & XSF_LIST)) {
3595 xo_failure(xop, "list close on list confict: '%s'",
3599 if ((xsp->xs_flags & XSF_INSTANCE) != (flags & XSF_INSTANCE)) {
3600 xo_failure(xop, "list close on instance confict: '%s'",
3607 xo_free(xsp->xs_name);
3608 xsp->xs_name = NULL;
3611 xo_free(xsp->xs_keys);
3612 xsp->xs_keys = NULL;
3616 xop->xo_depth += delta; /* Record new depth */
3617 xop->xo_indent += indent;
3621 xo_set_depth (xo_handle_t *xop, int depth)
3623 xop = xo_default(xop);
3625 if (xo_depth_check(xop, depth))
3628 xop->xo_depth += depth;
3629 xop->xo_indent += depth;
3632 static xo_xsf_flags_t
3633 xo_stack_flags (unsigned xflags)
3635 if (xflags & XOF_DTRT)
3641 xo_open_container_hf (xo_handle_t *xop, xo_xof_flags_t flags, const char *name)
3643 xop = xo_default(xop);
3646 const char *ppn = (xop->xo_flags & XOF_PRETTY) ? "\n" : "";
3647 const char *pre_nl = "";
3650 xo_failure(xop, "NULL passed for container name");
3651 name = XO_FAILURE_NAME;
3654 flags |= xop->xo_flags; /* Pick up handle flags */
3656 switch (xop->xo_style) {
3658 rc = xo_printf(xop, "%*s<%s>%s", xo_indent(xop), "",
3660 xo_depth_change(xop, name, 1, 1, xo_stack_flags(flags));
3664 xo_stack_set_flags(xop);
3666 if (!(xop->xo_flags & XOF_NO_TOP)) {
3667 if (!(xop->xo_flags & XOF_TOP_EMITTED)) {
3668 xo_printf(xop, "%*s{%s", xo_indent(xop), "", ppn);
3669 xop->xo_flags |= XOF_TOP_EMITTED;
3673 if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST)
3674 pre_nl = (xop->xo_flags & XOF_PRETTY) ? ",\n" : ", ";
3675 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST;
3677 rc = xo_printf(xop, "%s%*s\"%s\": {%s",
3678 pre_nl, xo_indent(xop), "", name, ppn);
3679 xo_depth_change(xop, name, 1, 1, xo_stack_flags(flags));
3684 xo_depth_change(xop, name, 1, 0, xo_stack_flags(flags));
3692 xo_open_container_h (xo_handle_t *xop, const char *name)
3694 return xo_open_container_hf(xop, 0, name);
3698 xo_open_container (const char *name)
3700 return xo_open_container_hf(NULL, 0, name);
3704 xo_open_container_hd (xo_handle_t *xop, const char *name)
3706 return xo_open_container_hf(xop, XOF_DTRT, name);
3710 xo_open_container_d (const char *name)
3712 return xo_open_container_hf(NULL, XOF_DTRT, name);
3716 xo_close_container_h (xo_handle_t *xop, const char *name)
3718 xop = xo_default(xop);
3721 const char *ppn = (xop->xo_flags & XOF_PRETTY) ? "\n" : "";
3722 const char *pre_nl = "";
3725 xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth];
3726 if (!(xsp->xs_flags & XSF_DTRT))
3727 xo_failure(xop, "missing name without 'dtrt' mode");
3729 name = xsp->xs_name;
3731 int len = strlen(name) + 1;
3732 /* We need to make a local copy; xo_depth_change will free it */
3733 char *cp = alloca(len);
3734 memcpy(cp, name, len);
3737 name = XO_FAILURE_NAME;
3740 switch (xop->xo_style) {
3742 xo_depth_change(xop, name, -1, -1, 0);
3743 rc = xo_printf(xop, "%*s</%s>%s", xo_indent(xop), "", name, ppn);
3747 pre_nl = (xop->xo_flags & XOF_PRETTY) ? "\n" : "";
3748 ppn = (xop->xo_depth <= 1) ? "\n" : "";
3750 xo_depth_change(xop, name, -1, -1, 0);
3751 rc = xo_printf(xop, "%s%*s}%s", pre_nl, xo_indent(xop), "", ppn);
3752 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST;
3757 xo_depth_change(xop, name, -1, 0, 0);
3765 xo_close_container (const char *name)
3767 return xo_close_container_h(NULL, name);
3771 xo_close_container_hd (xo_handle_t *xop)
3773 return xo_close_container_h(xop, NULL);
3777 xo_close_container_d (void)
3779 return xo_close_container_h(NULL, NULL);
3783 xo_open_list_hf (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name)
3785 xop = xo_default(xop);
3787 if (xop->xo_style != XO_STYLE_JSON)
3791 const char *ppn = (xop->xo_flags & XOF_PRETTY) ? "\n" : "";
3792 const char *pre_nl = "";
3794 if (!(xop->xo_flags & XOF_NO_TOP)) {
3795 if (!(xop->xo_flags & XOF_TOP_EMITTED)) {
3796 xo_printf(xop, "%*s{%s", xo_indent(xop), "", ppn);
3797 xop->xo_flags |= XOF_TOP_EMITTED;
3802 xo_failure(xop, "NULL passed for list name");
3803 name = XO_FAILURE_NAME;
3806 xo_stack_set_flags(xop);
3808 if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST)
3809 pre_nl = (xop->xo_flags & XOF_PRETTY) ? ",\n" : ", ";
3810 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST;
3812 rc = xo_printf(xop, "%s%*s\"%s\": [%s",
3813 pre_nl, xo_indent(xop), "", name, ppn);
3814 xo_depth_change(xop, name, 1, 1, XSF_LIST | xo_stack_flags(flags));
3820 xo_open_list_h (xo_handle_t *xop, const char *name UNUSED)
3822 return xo_open_list_hf(xop, 0, name);
3826 xo_open_list (const char *name)
3828 return xo_open_list_hf(NULL, 0, name);
3832 xo_open_list_hd (xo_handle_t *xop, const char *name UNUSED)
3834 return xo_open_list_hf(xop, XOF_DTRT, name);
3838 xo_open_list_d (const char *name)
3840 return xo_open_list_hf(NULL, XOF_DTRT, name);
3844 xo_close_list_h (xo_handle_t *xop, const char *name)
3847 const char *pre_nl = "";
3849 xop = xo_default(xop);
3851 if (xop->xo_style != XO_STYLE_JSON)
3855 xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth];
3856 if (!(xsp->xs_flags & XSF_DTRT))
3857 xo_failure(xop, "missing name without 'dtrt' mode");
3859 name = xsp->xs_name;
3861 int len = strlen(name) + 1;
3862 /* We need to make a local copy; xo_depth_change will free it */
3863 char *cp = alloca(len);
3864 memcpy(cp, name, len);
3867 name = XO_FAILURE_NAME;
3870 if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST)
3871 pre_nl = (xop->xo_flags & XOF_PRETTY) ? "\n" : "";
3872 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST;
3874 xo_depth_change(xop, name, -1, -1, XSF_LIST);
3875 rc = xo_printf(xop, "%s%*s]", pre_nl, xo_indent(xop), "");
3876 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST;
3882 xo_close_list (const char *name)
3884 return xo_close_list_h(NULL, name);
3888 xo_close_list_hd (xo_handle_t *xop)
3890 return xo_close_list_h(xop, NULL);
3894 xo_close_list_d (void)
3896 return xo_close_list_h(NULL, NULL);
3900 xo_open_instance_hf (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name)
3902 xop = xo_default(xop);
3905 const char *ppn = (xop->xo_flags & XOF_PRETTY) ? "\n" : "";
3906 const char *pre_nl = "";
3908 flags |= xop->xo_flags;
3911 xo_failure(xop, "NULL passed for instance name");
3912 name = XO_FAILURE_NAME;
3915 switch (xop->xo_style) {
3917 rc = xo_printf(xop, "%*s<%s>%s", xo_indent(xop), "", name, ppn);
3918 xo_depth_change(xop, name, 1, 1, xo_stack_flags(flags));
3922 xo_stack_set_flags(xop);
3924 if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST)
3925 pre_nl = (xop->xo_flags & XOF_PRETTY) ? ",\n" : ", ";
3926 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST;
3928 rc = xo_printf(xop, "%s%*s{%s",
3929 pre_nl, xo_indent(xop), "", ppn);
3930 xo_depth_change(xop, name, 1, 1, xo_stack_flags(flags));
3935 xo_depth_change(xop, name, 1, 0, xo_stack_flags(flags));
3943 xo_open_instance_h (xo_handle_t *xop, const char *name)
3945 return xo_open_instance_hf(xop, 0, name);
3949 xo_open_instance (const char *name)
3951 return xo_open_instance_hf(NULL, 0, name);
3955 xo_open_instance_hd (xo_handle_t *xop, const char *name)
3957 return xo_open_instance_hf(xop, XOF_DTRT, name);
3961 xo_open_instance_d (const char *name)
3963 return xo_open_instance_hf(NULL, XOF_DTRT, name);
3967 xo_close_instance_h (xo_handle_t *xop, const char *name)
3969 xop = xo_default(xop);
3972 const char *ppn = (xop->xo_flags & XOF_PRETTY) ? "\n" : "";
3973 const char *pre_nl = "";
3976 xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth];
3977 if (!(xsp->xs_flags & XSF_DTRT))
3978 xo_failure(xop, "missing name without 'dtrt' mode");
3980 name = xsp->xs_name;
3982 int len = strlen(name) + 1;
3983 /* We need to make a local copy; xo_depth_change will free it */
3984 char *cp = alloca(len);
3985 memcpy(cp, name, len);
3988 name = XO_FAILURE_NAME;
3991 switch (xop->xo_style) {
3993 xo_depth_change(xop, name, -1, -1, 0);
3994 rc = xo_printf(xop, "%*s</%s>%s", xo_indent(xop), "", name, ppn);
3998 pre_nl = (xop->xo_flags & XOF_PRETTY) ? "\n" : "";
4000 xo_depth_change(xop, name, -1, -1, 0);
4001 rc = xo_printf(xop, "%s%*s}", pre_nl, xo_indent(xop), "");
4002 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST;
4007 xo_depth_change(xop, name, -1, 0, 0);
4015 xo_close_instance (const char *name)
4017 return xo_close_instance_h(NULL, name);
4021 xo_close_instance_hd (xo_handle_t *xop)
4023 return xo_close_instance_h(xop, NULL);
4027 xo_close_instance_d (void)
4029 return xo_close_instance_h(NULL, NULL);
4033 xo_set_writer (xo_handle_t *xop, void *opaque, xo_write_func_t write_func,
4034 xo_close_func_t close_func)
4036 xop = xo_default(xop);
4038 xop->xo_opaque = opaque;
4039 xop->xo_write = write_func;
4040 xop->xo_close = close_func;
4044 xo_set_allocator (xo_realloc_func_t realloc_func, xo_free_func_t free_func)
4046 xo_realloc = realloc_func;
4047 xo_free = free_func;
4051 xo_flush_h (xo_handle_t *xop)
4053 static char div_close[] = "</div>";
4055 xop = xo_default(xop);
4057 switch (xop->xo_style) {
4059 if (xop->xo_flags & XOF_DIV_OPEN) {
4060 xop->xo_flags &= ~XOF_DIV_OPEN;
4061 xo_data_append(xop, div_close, sizeof(div_close) - 1);
4063 if (xop->xo_flags & XOF_PRETTY)
4064 xo_data_append(xop, "\n", 1);
4079 xo_finish_h (xo_handle_t *xop)
4081 const char *cp = "";
4082 xop = xo_default(xop);
4084 switch (xop->xo_style) {
4086 if (!(xop->xo_flags & XOF_NO_TOP)) {
4087 if (xop->xo_flags & XOF_TOP_EMITTED)
4088 xop->xo_flags &= ~XOF_TOP_EMITTED; /* Turn off before output */
4091 xo_printf(xop, "%*s%s}\n",xo_indent(xop), "", cp);
4106 * Generate an error message, such as would be displayed on stderr
4109 xo_error_hv (xo_handle_t *xop, const char *fmt, va_list vap)
4111 xop = xo_default(xop);
4114 * If the format string doesn't end with a newline, we pop
4117 int len = strlen(fmt);
4118 if (len > 0 && fmt[len - 1] != '\n') {
4119 char *newfmt = alloca(len + 2);
4120 memcpy(newfmt, fmt, len);
4126 switch (xop->xo_style) {
4128 vfprintf(stderr, fmt, vap);
4132 va_copy(xop->xo_vap, vap);
4134 xo_buf_append_div(xop, "error", 0, NULL, 0, fmt, strlen(fmt), NULL, 0);
4136 if (xop->xo_flags & XOF_DIV_OPEN)
4141 va_end(xop->xo_vap);
4142 bzero(&xop->xo_vap, sizeof(xop->xo_vap));
4146 va_copy(xop->xo_vap, vap);
4148 xo_open_container_h(xop, "error");
4149 xo_format_value(xop, "message", 7, fmt, strlen(fmt), NULL, 0, 0);
4150 xo_close_container_h(xop, "error");
4152 va_end(xop->xo_vap);
4153 bzero(&xop->xo_vap, sizeof(xop->xo_vap));
4159 xo_error_h (xo_handle_t *xop, const char *fmt, ...)
4164 xo_error_hv(xop, fmt, vap);
4169 * Generate an error message, such as would be displayed on stderr
4172 xo_error (const char *fmt, ...)
4177 xo_error_hv(NULL, fmt, vap);
4182 xo_parse_args (int argc, char **argv)
4184 static char libxo_opt[] = "--libxo";
4188 /* Save our program name for xo_err and friends */
4189 xo_program = argv[0];
4190 cp = strrchr(xo_program, '/');
4192 xo_program = cp + 1;
4194 for (save = i = 1; i < argc; i++) {
4196 || strncmp(argv[i], libxo_opt, sizeof(libxo_opt) - 1) != 0) {
4198 argv[save] = argv[i];
4203 cp = argv[i] + sizeof(libxo_opt) - 1;
4207 xo_warnx("missing libxo option");
4211 if (xo_set_options(NULL, cp) < 0)
4213 } else if (*cp == ':') {
4214 if (xo_set_options(NULL, cp) < 0)
4217 } else if (*cp == '=') {
4218 if (xo_set_options(NULL, ++cp) < 0)
4221 } else if (*cp == '-') {
4223 if (strcmp(cp, "check") == 0) {
4227 xo_warnx("unknown libxo option: '%s'", argv[i]);
4231 xo_warnx("unknown libxo option: '%s'", argv[i]);
4242 main (int argc, char **argv)
4244 static char base_grocery[] = "GRO";
4245 static char base_hardware[] = "HRD";
4247 const char *i_title;
4251 const char *i_sku_base;
4254 struct item list[] = {
4255 { "gum&this&that", 1412, 54, 10, base_grocery, 415 },
4256 { "<rope>", 85, 4, 2, base_hardware, 212 },
4257 { "ladder", 0, 2, 1, base_hardware, 517 },
4258 { "\"bolt\"", 4123, 144, 42, base_hardware, 632 },
4259 { "water\\blue", 17, 14, 2, base_grocery, 2331 },
4260 { NULL, 0, 0, 0, NULL, 0 }
4262 struct item list2[] = {
4263 { "fish", 1321, 45, 1, base_grocery, 533 },
4264 { NULL, 0, 0, 0, NULL, 0 }
4267 xo_info_t info[] = {
4268 { "in-stock", "number", "Number of items in stock" },
4269 { "name", "string", "Name of the item" },
4270 { "on-order", "number", "Number of items on order" },
4271 { "sku", "string", "Stock Keeping Unit" },
4272 { "sold", "number", "Number of items sold" },
4273 { NULL, NULL, NULL },
4275 int info_count = (sizeof(info) / sizeof(info[0])) - 1;
4277 argc = xo_parse_args(argc, argv);
4281 xo_set_info(NULL, info, info_count);
4283 xo_open_container_h(NULL, "top");
4285 xo_open_container("data");
4286 xo_open_list("item");
4288 xo_emit("{T:Item/%-15s}{T:Total Sold/%12s}{T:In Stock/%12s}"
4289 "{T:On Order/%12s}{T:SKU/%5s}\n");
4291 for (ip = list; ip->i_title; ip++) {
4292 xo_open_instance("item");
4294 xo_emit("{k:name/%-15s/%s}{n:sold/%12u/%u}{:in-stock/%12u/%u}"
4295 "{:on-order/%12u/%u} {q:sku/%5s-000-%u/%s-000-%u}\n",
4296 ip->i_title, ip->i_sold, ip->i_instock, ip->i_onorder,
4297 ip->i_sku_base, ip->i_sku_num);
4299 xo_close_instance("item");
4302 xo_close_list("item");
4303 xo_close_container("data");
4307 xo_open_container("data");
4308 xo_open_list("item");
4310 for (ip = list; ip->i_title; ip++) {
4311 xo_open_instance("item");
4313 xo_attr("fancy", "%s%d", "item", ip - list);
4314 xo_emit("{L:Item} '{k:name/%s}':\n", ip->i_title);
4315 xo_emit("{P: }{L:Total sold}: {n:sold/%u%s}{e:percent/%u}\n",
4316 ip->i_sold, ip->i_sold ? ".0" : "", 44);
4317 xo_emit("{P: }{Lcw:In stock}{:in-stock/%u}\n", ip->i_instock);
4318 xo_emit("{P: }{Lcw:On order}{:on-order/%u}\n", ip->i_onorder);
4319 xo_emit("{P: }{L:SKU}: {q:sku/%s-000-%u}\n",
4320 ip->i_sku_base, ip->i_sku_num);
4322 xo_close_instance("item");
4325 xo_close_list("item");
4326 xo_close_container("data");
4328 xo_open_container("data");
4329 xo_open_list("item");
4331 for (ip = list2; ip->i_title; ip++) {
4332 xo_open_instance("item");
4334 xo_emit("{L:Item} '{k:name/%s}':\n", ip->i_title);
4335 xo_emit("{P: }{L:Total sold}: {n:sold/%u%s}\n",
4336 ip->i_sold, ip->i_sold ? ".0" : "");
4337 xo_emit("{P: }{Lcw:In stock}{:in-stock/%u}\n", ip->i_instock);
4338 xo_emit("{P: }{Lcw:On order}{:on-order/%u}\n", ip->i_onorder);
4339 xo_emit("{P: }{L:SKU}: {q:sku/%s-000-%u}\n",
4340 ip->i_sku_base, ip->i_sku_num);
4342 xo_open_list("month");
4344 const char *months[] = { "Jan", "Feb", "Mar", NULL };
4345 int discounts[] = { 10, 20, 25, 0 };
4347 for (i = 0; months[i]; i++) {
4348 xo_open_instance("month");
4350 "{Lwc:Month}{k:month}, {Lwc:Special}{:discount/%d}\n",
4351 months[i], discounts[i]);
4352 xo_close_instance("month");
4355 xo_close_list("month");
4357 xo_close_instance("item");
4360 xo_close_list("item");
4361 xo_close_container("data");
4363 xo_close_container_h(NULL, "top");
4369 #endif /* UNIT_TEST */