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 newfmt[len + plen] = '\0';
961 if (xop->xo_flags & XOF_WARN_XML) {
962 static char err_open[] = "<error>";
963 static char err_close[] = "</error>";
964 static char msg_open[] = "<message>";
965 static char msg_close[] = "</message>";
967 xo_buffer_t *xbp = &xop->xo_data;
969 xo_buf_append(xbp, err_open, sizeof(err_open) - 1);
970 xo_buf_append(xbp, msg_open, sizeof(msg_open) - 1);
973 va_copy(va_local, vap);
975 int left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp);
976 int rc = vsnprintf(xbp->xb_curp, left, newfmt, vap);
977 if (rc > xbp->xb_size) {
978 if (!xo_buf_has_room(xbp, rc)) {
983 va_end(vap); /* Reset vap to the start */
984 va_copy(vap, va_local);
986 left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp);
987 rc = vsnprintf(xbp->xb_curp, left, fmt, vap);
991 rc = xo_escape_xml(xbp, rc, 1);
994 xo_buf_append(xbp, msg_close, sizeof(msg_close) - 1);
995 xo_buf_append(xbp, err_close, sizeof(err_close) - 1);
998 const char *msg = strerror(code);
1000 xo_buf_append(xbp, ": ", 2);
1001 xo_buf_append(xbp, msg, strlen(msg));
1005 xo_buf_append(xbp, "\n", 2); /* Append newline and NUL to string */
1009 vfprintf(stderr, newfmt, vap);
1010 fprintf(stderr, ": %s\n", strerror(code));
1015 xo_warn_hc (xo_handle_t *xop, int code, const char *fmt, ...)
1020 xo_warn_hcv(xop, code, 0, fmt, vap);
1025 xo_warn_c (int code, const char *fmt, ...)
1030 xo_warn_hcv(NULL, 0, code, fmt, vap);
1035 xo_warn (const char *fmt, ...)
1041 xo_warn_hcv(NULL, code, 0, fmt, vap);
1046 xo_warnx (const char *fmt, ...)
1051 xo_warn_hcv(NULL, -1, 0, fmt, vap);
1056 xo_err (int eval, const char *fmt, ...)
1062 xo_warn_hcv(NULL, code, 0, fmt, vap);
1069 xo_errx (int eval, const char *fmt, ...)
1074 xo_warn_hcv(NULL, -1, 0, fmt, vap);
1081 xo_errc (int eval, int code, const char *fmt, ...)
1086 xo_warn_hcv(NULL, code, 0, fmt, vap);
1093 * Generate a warning. Normally, this is a text message written to
1094 * standard error. If the XOF_WARN_XML flag is set, then we generate
1095 * XMLified content on standard output.
1098 xo_message_hcv (xo_handle_t *xop, int code, const char *fmt, va_list vap)
1100 static char msg_open[] = "<message>";
1101 static char msg_close[] = "</message>";
1106 xop = xo_default(xop);
1108 if (fmt == NULL || *fmt == '\0')
1111 int need_nl = (fmt[strlen(fmt) - 1] != '\n');
1113 switch (xop->xo_style) {
1115 xbp = &xop->xo_data;
1116 if (xop->xo_flags & XOF_PRETTY)
1117 xo_buf_indent(xop, xop->xo_indent_by);
1118 xo_buf_append(xbp, msg_open, sizeof(msg_open) - 1);
1120 va_copy(va_local, vap);
1122 int left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp);
1123 rc = vsnprintf(xbp->xb_curp, left, fmt, vap);
1124 if (rc > xbp->xb_size) {
1125 if (!xo_buf_has_room(xbp, rc)) {
1130 va_end(vap); /* Reset vap to the start */
1131 va_copy(vap, va_local);
1133 left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp);
1134 rc = vsnprintf(xbp->xb_curp, left, fmt, vap);
1138 rc = xo_escape_xml(xbp, rc, 1);
1141 if (need_nl && code > 0) {
1142 const char *msg = strerror(code);
1144 xo_buf_append(xbp, ": ", 2);
1145 xo_buf_append(xbp, msg, strlen(msg));
1149 xo_buf_append(xbp, msg_close, sizeof(msg_close) - 1);
1151 xo_buf_append(xbp, "\n", 2); /* Append newline and NUL to string */
1157 char buf[BUFSIZ], *bp = buf, *cp;
1158 int bufsiz = sizeof(buf);
1161 va_copy(va_local, vap);
1163 rc = vsnprintf(bp, bufsiz, fmt, va_local);
1165 bufsiz = rc + BUFSIZ;
1166 bp = alloca(bufsiz);
1168 va_copy(va_local, vap);
1169 rc = vsnprintf(bp, bufsiz, fmt, va_local);
1175 rc2 = snprintf(cp, bufsiz - rc, "%s%s\n",
1176 (code > 0) ? ": " : "",
1177 (code > 0) ? strerror(code) : "");
1182 xo_buf_append_div(xop, "message", 0, NULL, 0, bp, rc, NULL, 0);
1187 /* No meanings of representing messages in JSON */
1191 rc = xo_printf_v(xop, fmt, vap);
1193 * XXX need to handle UTF-8 widths
1196 if (xop->xo_flags & XOF_COLUMNS)
1197 xop->xo_columns += rc;
1198 if (xop->xo_flags & XOF_ANCHOR)
1199 xop->xo_anchor_columns += rc;
1202 if (need_nl && code > 0) {
1203 const char *msg = strerror(code);
1205 xo_printf(xop, ": %s", msg);
1209 xo_printf(xop, "\n");
1218 xo_message_hc (xo_handle_t *xop, int code, const char *fmt, ...)
1223 xo_message_hcv(xop, code, fmt, vap);
1228 xo_message_c (int code, const char *fmt, ...)
1233 xo_message_hcv(NULL, code, fmt, vap);
1238 xo_message (const char *fmt, ...)
1244 xo_message_hcv(NULL, code, fmt, vap);
1249 xo_failure (xo_handle_t *xop, const char *fmt, ...)
1251 if (!(xop->xo_flags & XOF_WARN))
1257 xo_warn_hcv(xop, -1, 1, fmt, vap);
1262 * Create a handle for use by later libxo functions.
1264 * Note: normal use of libxo does not require a distinct handle, since
1265 * the default handle (used when NULL is passed) generates text on stdout.
1267 * @style Style of output desired (XO_STYLE_* value)
1268 * @flags Set of XOF_* flags in use with this handle
1271 xo_create (xo_style_t style, xo_xof_flags_t flags)
1273 xo_handle_t *xop = xo_realloc(NULL, sizeof(*xop));
1276 bzero(xop, sizeof(*xop));
1278 xop->xo_style = style;
1279 xop->xo_flags = flags;
1280 xo_init_handle(xop);
1287 * Create a handle that will write to the given file. Use
1288 * the XOF_CLOSE_FP flag to have the file closed on xo_destroy().
1289 * @fp FILE pointer to use
1290 * @style Style of output desired (XO_STYLE_* value)
1291 * @flags Set of XOF_* flags to use with this handle
1294 xo_create_to_file (FILE *fp, xo_style_t style, xo_xof_flags_t flags)
1296 xo_handle_t *xop = xo_create(style, flags);
1299 xop->xo_opaque = fp;
1300 xop->xo_write = xo_write_to_file;
1301 xop->xo_close = xo_close_file;
1308 * Release any resources held by the handle.
1309 * @xop XO handle to alter (or NULL for default handle)
1312 xo_destroy (xo_handle_t *xop_arg)
1314 xo_handle_t *xop = xo_default(xop_arg);
1316 if (xop->xo_close && (xop->xo_flags & XOF_CLOSE_FP))
1317 xop->xo_close(xop->xo_opaque);
1319 xo_free(xop->xo_stack);
1320 xo_buf_cleanup(&xop->xo_data);
1321 xo_buf_cleanup(&xop->xo_fmt);
1322 xo_buf_cleanup(&xop->xo_predicate);
1323 xo_buf_cleanup(&xop->xo_attrs);
1325 if (xop_arg == NULL) {
1326 bzero(&xo_default_handle, sizeof(&xo_default_handle));
1327 xo_default_inited = 0;
1333 * Record a new output style to use for the given handle (or default if
1334 * handle is NULL). This output style will be used for any future output.
1336 * @xop XO handle to alter (or NULL for default handle)
1337 * @style new output style (XO_STYLE_*)
1340 xo_set_style (xo_handle_t *xop, xo_style_t style)
1342 xop = xo_default(xop);
1343 xop->xo_style = style;
1347 xo_get_style (xo_handle_t *xop)
1349 xop = xo_default(xop);
1350 return xop->xo_style;
1354 xo_name_to_style (const char *name)
1356 if (strcmp(name, "xml") == 0)
1357 return XO_STYLE_XML;
1358 else if (strcmp(name, "json") == 0)
1359 return XO_STYLE_JSON;
1360 else if (strcmp(name, "text") == 0)
1361 return XO_STYLE_TEXT;
1362 else if (strcmp(name, "html") == 0)
1363 return XO_STYLE_HTML;
1369 * Convert string name to XOF_* flag value.
1370 * Not all are useful. Or safe. Or sane.
1373 xo_name_to_flag (const char *name)
1375 if (strcmp(name, "pretty") == 0)
1377 if (strcmp(name, "warn") == 0)
1379 if (strcmp(name, "xpath") == 0)
1381 if (strcmp(name, "info") == 0)
1383 if (strcmp(name, "warn-xml") == 0)
1384 return XOF_WARN_XML;
1385 if (strcmp(name, "columns") == 0)
1387 if (strcmp(name, "dtrt") == 0)
1389 if (strcmp(name, "flush") == 0)
1391 if (strcmp(name, "keys") == 0)
1393 if (strcmp(name, "ignore-close") == 0)
1394 return XOF_IGNORE_CLOSE;
1395 if (strcmp(name, "not-first") == 0)
1396 return XOF_NOT_FIRST;
1397 if (strcmp(name, "no-locale") == 0)
1398 return XOF_NO_LOCALE;
1399 if (strcmp(name, "no-top") == 0)
1401 if (strcmp(name, "units") == 0)
1403 if (strcmp(name, "underscores") == 0)
1404 return XOF_UNDERSCORES;
1410 xo_set_style_name (xo_handle_t *xop, const char *name)
1415 int style = xo_name_to_style(name);
1419 xo_set_style(xop, style);
1424 * Set the options for a handle using a string of options
1425 * passed in. The input is a comma-separated set of names
1426 * and optional values: "xml,pretty,indent=4"
1429 xo_set_options (xo_handle_t *xop, const char *input)
1431 char *cp, *ep, *vp, *np, *bp;
1432 int style = -1, new_style, len, rc = 0;
1433 xo_xof_flags_t new_flag;
1438 xop = xo_default(xop);
1441 * We support a simpler, old-school style of giving option
1442 * also, using a single character for each option. It's
1443 * ideal for lazy people, such as myself.
1445 if (*input == ':') {
1448 for (input++ ; *input; input++) {
1451 xop->xo_flags |= XOF_FLUSH;
1455 xop->xo_style = XO_STYLE_HTML;
1459 xop->xo_flags |= XOF_INFO;
1463 sz = strspn(input + 1, "0123456789");
1465 xop->xo_indent_by = atoi(input + 1);
1466 input += sz - 1; /* Skip value */
1471 xop->xo_flags |= XOF_KEYS;
1475 xop->xo_style = XO_STYLE_JSON;
1479 xop->xo_flags |= XOF_PRETTY;
1483 xop->xo_style = XO_STYLE_TEXT;
1487 xop->xo_flags |= XOF_UNITS;
1491 xop->xo_flags |= XOF_UNDERSCORES;
1495 xop->xo_flags |= XOF_WARN;
1499 xop->xo_style = XO_STYLE_XML;
1503 xop->xo_flags |= XOF_XPATH;
1510 len = strlen(input) + 1;
1512 memcpy(bp, input, len);
1514 for (cp = bp, ep = cp + len - 1; cp && cp < ep; cp = np) {
1515 np = strchr(cp, ',');
1519 vp = strchr(cp, '=');
1523 new_style = xo_name_to_style(cp);
1524 if (new_style >= 0) {
1526 xo_warnx("ignoring multiple styles: '%s'", cp);
1530 new_flag = xo_name_to_flag(cp);
1532 xop->xo_flags |= new_flag;
1534 if (strcmp(cp, "indent") == 0) {
1535 xop->xo_indent_by = atoi(vp);
1537 xo_warnx("unknown option: '%s'", cp);
1545 xop->xo_style= style;
1551 * Set one or more flags for a given handle (or default if handle is NULL).
1552 * These flags will affect future output.
1554 * @xop XO handle to alter (or NULL for default handle)
1555 * @flags Flags to be set (XOF_*)
1558 xo_set_flags (xo_handle_t *xop, xo_xof_flags_t flags)
1560 xop = xo_default(xop);
1562 xop->xo_flags |= flags;
1566 xo_get_flags (xo_handle_t *xop)
1568 xop = xo_default(xop);
1570 return xop->xo_flags;
1574 * Record a leading prefix for the XPath we generate. This allows the
1575 * generated data to be placed within an XML hierarchy but still have
1576 * accurate XPath expressions.
1578 * @xop XO handle to alter (or NULL for default handle)
1579 * @path The XPath expression
1582 xo_set_leading_xpath (xo_handle_t *xop, const char *path)
1584 xop = xo_default(xop);
1586 if (xop->xo_leading_xpath) {
1587 xo_free(xop->xo_leading_xpath);
1588 xop->xo_leading_xpath = NULL;
1594 int len = strlen(path);
1595 xop->xo_leading_xpath = xo_realloc(NULL, len + 1);
1596 if (xop->xo_leading_xpath) {
1597 memcpy(xop->xo_leading_xpath, path, len + 1);
1602 * Record the info data for a set of tags
1604 * @xop XO handle to alter (or NULL for default handle)
1605 * @info Info data (xo_info_t) to be recorded (or NULL) (MUST BE SORTED)
1606 * @count Number of entries in info (or -1 to count them ourselves)
1609 xo_set_info (xo_handle_t *xop, xo_info_t *infop, int count)
1611 xop = xo_default(xop);
1613 if (count < 0 && infop) {
1616 for (xip = infop, count = 0; xip->xi_name; xip++, count++)
1620 xop->xo_info = infop;
1621 xop->xo_info_count = count;
1625 * Set the formatter callback for a handle. The callback should
1626 * return a newly formatting contents of a formatting instruction,
1627 * meaning the bits inside the braces.
1630 xo_set_formatter (xo_handle_t *xop, xo_formatter_t func,
1631 xo_checkpointer_t cfunc)
1633 xop = xo_default(xop);
1635 xop->xo_formatter = func;
1636 xop->xo_checkpointer = cfunc;
1640 * Clear one or more flags for a given handle (or default if handle is NULL).
1641 * These flags will affect future output.
1643 * @xop XO handle to alter (or NULL for default handle)
1644 * @flags Flags to be cleared (XOF_*)
1647 xo_clear_flags (xo_handle_t *xop, xo_xof_flags_t flags)
1649 xop = xo_default(xop);
1651 xop->xo_flags &= ~flags;
1655 xo_line_ensure_open (xo_handle_t *xop, xo_xff_flags_t flags UNUSED)
1657 static char div_open[] = "<div class=\"line\">";
1658 static char div_open_blank[] = "<div class=\"blank-line\">";
1660 if (xop->xo_flags & XOF_DIV_OPEN)
1663 if (xop->xo_style != XO_STYLE_HTML)
1666 xop->xo_flags |= XOF_DIV_OPEN;
1667 if (flags & XFF_BLANK_LINE)
1668 xo_data_append(xop, div_open_blank, sizeof(div_open_blank) - 1);
1670 xo_data_append(xop, div_open, sizeof(div_open) - 1);
1672 if (xop->xo_flags & XOF_PRETTY)
1673 xo_data_append(xop, "\n", 1);
1677 xo_line_close (xo_handle_t *xop)
1679 static char div_close[] = "</div>";
1681 switch (xop->xo_style) {
1683 if (!(xop->xo_flags & XOF_DIV_OPEN))
1684 xo_line_ensure_open(xop, 0);
1686 xop->xo_flags &= ~XOF_DIV_OPEN;
1687 xo_data_append(xop, div_close, sizeof(div_close) - 1);
1689 if (xop->xo_flags & XOF_PRETTY)
1690 xo_data_append(xop, "\n", 1);
1694 xo_data_append(xop, "\n", 1);
1700 xo_info_compare (const void *key, const void *data)
1702 const char *name = key;
1703 const xo_info_t *xip = data;
1705 return strcmp(name, xip->xi_name);
1710 xo_info_find (xo_handle_t *xop, const char *name, int nlen)
1713 char *cp = alloca(nlen + 1); /* Need local copy for NUL termination */
1715 memcpy(cp, name, nlen);
1718 xip = bsearch(cp, xop->xo_info, xop->xo_info_count,
1719 sizeof(xop->xo_info[0]), xo_info_compare);
1723 #define CONVERT(_have, _need) (((_have) << 8) | (_need))
1726 * Check to see that the conversion is safe and sane.
1729 xo_check_conversion (xo_handle_t *xop, int have_enc, int need_enc)
1731 switch (CONVERT(have_enc, need_enc)) {
1732 case CONVERT(XF_ENC_UTF8, XF_ENC_UTF8):
1733 case CONVERT(XF_ENC_UTF8, XF_ENC_LOCALE):
1734 case CONVERT(XF_ENC_WIDE, XF_ENC_UTF8):
1735 case CONVERT(XF_ENC_WIDE, XF_ENC_LOCALE):
1736 case CONVERT(XF_ENC_LOCALE, XF_ENC_LOCALE):
1737 case CONVERT(XF_ENC_LOCALE, XF_ENC_UTF8):
1741 xo_failure(xop, "invalid conversion (%c:%c)", have_enc, need_enc);
1747 xo_format_string_direct (xo_handle_t *xop, xo_buffer_t *xbp,
1748 xo_xff_flags_t flags,
1749 const wchar_t *wcp, const char *cp, int len, int max,
1750 int need_enc, int have_enc)
1754 int ilen, olen, width;
1755 int attr = (flags & XFF_ATTR);
1758 if (len > 0 && !xo_buf_has_room(xbp, len))
1768 if ((flags & XFF_UNESCAPE) && (*cp == '\\' || *cp == '%')) {
1774 if (wcp && *wcp == L'\0')
1780 case XF_ENC_WIDE: /* Wide character */
1785 case XF_ENC_UTF8: /* UTF-8 */
1786 ilen = xo_utf8_to_wc_len(cp);
1788 xo_failure(xop, "invalid UTF-8 character: %02hhx", *cp);
1792 if (len > 0 && len < ilen) {
1793 len = 0; /* Break out of the loop */
1797 wc = xo_utf8_char(cp, ilen);
1798 if (wc == (wchar_t) -1) {
1799 xo_failure(xop, "invalid UTF-8 character: %02hhx/%d",
1806 case XF_ENC_LOCALE: /* Native locale */
1807 ilen = (len > 0) ? len : MB_LEN_MAX;
1808 ilen = mbrtowc(&wc, cp, ilen, &xop->xo_mbstate);
1809 if (ilen < 0) { /* Invalid data; skip */
1810 xo_failure(xop, "invalid mbs char: %02hhx", *cp);
1813 if (ilen == 0) { /* Hit a wide NUL character */
1822 /* Reduce len, but not below zero */
1830 * Find the width-in-columns of this character, which must be done
1831 * in wide characters, since we lack a mbswidth() function. If
1834 width = wcwidth(wc);
1836 width = iswcntrl(wc) ? 0 : 1;
1838 if (xop->xo_style == XO_STYLE_TEXT || xop->xo_style == XO_STYLE_HTML) {
1839 if (max > 0 && cols + width > max)
1846 /* Output in UTF-8 needs to be escaped, based on the style */
1847 switch (xop->xo_style) {
1856 else if (attr && wc == '"')
1861 int slen = strlen(sp);
1862 if (!xo_buf_has_room(xbp, slen - 1))
1865 memcpy(xbp->xb_curp, sp, slen);
1866 xbp->xb_curp += slen;
1867 goto done_with_encoding; /* Need multi-level 'break' */
1870 if (wc != '\\' && wc != '"')
1873 if (!xo_buf_has_room(xbp, 2))
1876 *xbp->xb_curp++ = '\\';
1877 *xbp->xb_curp++ = wc & 0x7f;
1878 goto done_with_encoding;
1881 olen = xo_utf8_emit_len(wc);
1883 xo_failure(xop, "ignoring bad length");
1887 if (!xo_buf_has_room(xbp, olen))
1890 xo_utf8_emit_char(xbp->xb_curp, olen, wc);
1891 xbp->xb_curp += olen;
1895 if (!xo_buf_has_room(xbp, MB_LEN_MAX + 1))
1898 olen = wcrtomb(xbp->xb_curp, wc, &xop->xo_mbstate);
1900 xo_failure(xop, "could not convert wide char: %lx",
1901 (unsigned long) wc);
1904 *xbp->xb_curp++ = '?';
1906 xbp->xb_curp += olen;
1918 xo_format_string (xo_handle_t *xop, xo_buffer_t *xbp, xo_xff_flags_t flags,
1921 static char null[] = "(null)";
1924 wchar_t *wcp = NULL;
1925 int len, cols = 0, rc = 0;
1926 int off = xbp->xb_curp - xbp->xb_bufp, off2;
1927 int need_enc = (xop->xo_style == XO_STYLE_TEXT)
1928 ? XF_ENC_LOCALE : XF_ENC_UTF8;
1930 if (xo_check_conversion(xop, xfp->xf_enc, need_enc))
1933 len = xfp->xf_width[XF_WIDTH_SIZE];
1935 if (xfp->xf_enc == XF_ENC_WIDE) {
1936 wcp = va_arg(xop->xo_vap, wchar_t *);
1941 * Dont' deref NULL; use the traditional "(null)" instead
1942 * of the more accurate "who's been a naughty boy, then?".
1946 len = sizeof(null) - 1;
1950 cp = va_arg(xop->xo_vap, char *); /* UTF-8 or native */
1954 /* Echo "Dont' deref NULL" logic */
1957 len = sizeof(null) - 1;
1961 * Optimize the most common case, which is "%s". We just
1962 * need to copy the complete string to the output buffer.
1964 if (xfp->xf_enc == need_enc
1965 && xfp->xf_width[XF_WIDTH_MIN] < 0
1966 && xfp->xf_width[XF_WIDTH_SIZE] < 0
1967 && xfp->xf_width[XF_WIDTH_MAX] < 0
1968 && !(xop->xo_flags & (XOF_ANCHOR | XOF_COLUMNS))) {
1970 xo_buf_escape(xop, xbp, cp, len, flags);
1973 * Our caller expects xb_curp left untouched, so we have
1974 * to reset it and return the number of bytes written to
1977 off2 = xbp->xb_curp - xbp->xb_bufp;
1979 xbp->xb_curp = xbp->xb_bufp + off;
1985 cols = xo_format_string_direct(xop, xbp, flags, wcp, cp, len,
1986 xfp->xf_width[XF_WIDTH_MAX],
1987 need_enc, xfp->xf_enc);
1992 * xo_buf_append* will move xb_curp, so we save/restore it.
1994 off2 = xbp->xb_curp - xbp->xb_bufp;
1996 xbp->xb_curp = xbp->xb_bufp + off;
1998 if (cols < xfp->xf_width[XF_WIDTH_MIN]) {
2000 * Find the number of columns needed to display the string.
2001 * If we have the original wide string, we just call wcswidth,
2002 * but if we did the work ourselves, then we need to do it.
2004 int delta = xfp->xf_width[XF_WIDTH_MIN] - cols;
2005 if (!xo_buf_has_room(xbp, delta))
2009 * If seen_minus, then pad on the right; otherwise move it so
2010 * we can pad on the left.
2012 if (xfp->xf_seen_minus) {
2013 cp = xbp->xb_curp + rc;
2016 memmove(xbp->xb_curp + delta, xbp->xb_curp, rc);
2019 /* Set the padding */
2020 memset(cp, (xfp->xf_leading_zero > 0) ? '0' : ' ', delta);
2025 if (xop->xo_flags & XOF_COLUMNS)
2026 xop->xo_columns += cols;
2027 if (xop->xo_flags & XOF_ANCHOR)
2028 xop->xo_anchor_columns += cols;
2033 xbp->xb_curp = xbp->xb_bufp + off;
2038 xo_data_append_content (xo_handle_t *xop, const char *str, int len)
2041 int need_enc = (xop->xo_style == XO_STYLE_TEXT)
2042 ? XF_ENC_LOCALE : XF_ENC_UTF8;
2044 cols = xo_format_string_direct(xop, &xop->xo_data, XFF_UNESCAPE,
2046 need_enc, XF_ENC_UTF8);
2048 if (xop->xo_flags & XOF_COLUMNS)
2049 xop->xo_columns += cols;
2050 if (xop->xo_flags & XOF_ANCHOR)
2051 xop->xo_anchor_columns += cols;
2055 xo_bump_width (xo_format_t *xfp, int digit)
2057 int *ip = &xfp->xf_width[xfp->xf_dots];
2059 *ip = ((*ip > 0) ? *ip : 0) * 10 + digit;
2063 xo_trim_ws (xo_buffer_t *xbp, int len)
2068 /* First trim leading space */
2069 for (cp = sp = xbp->xb_curp, ep = cp + len; cp < ep; cp++) {
2077 memmove(sp, cp, len);
2080 /* Then trim off the end */
2081 for (cp = xbp->xb_curp, sp = ep = cp + len; cp < ep; ep--) {
2096 xo_format_data (xo_handle_t *xop, xo_buffer_t *xbp,
2097 const char *fmt, int flen, xo_xff_flags_t flags)
2100 const char *cp, *ep, *sp, *xp = NULL;
2102 int style = (flags & XFF_XML) ? XO_STYLE_XML : xop->xo_style;
2103 unsigned make_output = !(flags & XFF_NO_OUTPUT);
2104 int need_enc = (xop->xo_style == XO_STYLE_TEXT)
2105 ? XF_ENC_LOCALE : XF_ENC_UTF8;
2108 xbp = &xop->xo_data;
2110 for (cp = fmt, ep = fmt + flen; cp < ep; cp++) {
2116 if (*cp == '\\' && cp[1] != '\0')
2120 } if (cp + 1 < ep && cp[1] == '%') {
2127 cols = xo_format_string_direct(xop, xbp, flags | XFF_UNESCAPE,
2128 NULL, xp, cp - xp, -1,
2129 need_enc, XF_ENC_UTF8);
2130 if (xop->xo_flags & XOF_COLUMNS)
2131 xop->xo_columns += cols;
2132 if (xop->xo_flags & XOF_ANCHOR)
2133 xop->xo_anchor_columns += cols;
2139 bzero(&xf, sizeof(xf));
2140 xf.xf_leading_zero = -1;
2141 xf.xf_width[0] = xf.xf_width[1] = xf.xf_width[2] = -1;
2144 * "%@" starts an XO-specific set of flags:
2145 * @X@ - XML-only field; ignored if style isn't XML
2148 for (cp += 2; cp < ep; cp++) {
2154 * '*' means there's a "%*.*s" value in vap that
2157 if (!(xop->xo_flags & XOF_NO_VA_ARG))
2158 va_arg(xop->xo_vap, int);
2163 /* Hidden fields are only visible to JSON and XML */
2164 if (xop->xo_flags & XFF_ENCODE_ONLY) {
2165 if (style != XO_STYLE_XML
2166 && xop->xo_style != XO_STYLE_JSON)
2168 } else if (xop->xo_flags & XFF_DISPLAY_ONLY) {
2169 if (style != XO_STYLE_TEXT
2170 && xop->xo_style != XO_STYLE_HTML)
2178 * Looking at one piece of a format; find the end and
2179 * call snprintf. Then advance xo_vap on our own.
2181 * Note that 'n', 'v', and '$' are not supported.
2183 sp = cp; /* Save start pointer */
2184 for (cp += 1; cp < ep; cp++) {
2187 else if (*cp == 'h')
2189 else if (*cp == 'j')
2191 else if (*cp == 't')
2193 else if (*cp == 'z')
2195 else if (*cp == 'q')
2197 else if (*cp == '.') {
2198 if (++xf.xf_dots >= XF_WIDTH_NUM) {
2199 xo_failure(xop, "Too many dots in format: '%s'", fmt);
2202 } else if (*cp == '-')
2203 xf.xf_seen_minus = 1;
2204 else if (isdigit((int) *cp)) {
2205 if (xf.xf_leading_zero < 0)
2206 xf.xf_leading_zero = (*cp == '0');
2207 xo_bump_width(&xf, *cp - '0');
2208 } else if (*cp == '*') {
2210 xf.xf_star[xf.xf_dots] = 1;
2211 } else if (strchr("diouxXDOUeEfFgGaAcCsSp", *cp) != NULL)
2213 else if (*cp == 'n' || *cp == 'v') {
2214 xo_failure(xop, "unsupported format: '%s'", fmt);
2220 xo_failure(xop, "field format missing format character: %s",
2225 if (!(xop->xo_flags & XOF_NO_VA_ARG)) {
2226 if (*cp == 's' || *cp == 'S') {
2227 /* Handle "%*.*.*s" */
2229 for (s = 0; s < XF_WIDTH_NUM; s++) {
2230 if (xf.xf_star[s]) {
2231 xf.xf_width[s] = va_arg(xop->xo_vap, int);
2233 /* Normalize a negative width value */
2234 if (xf.xf_width[s] < 0) {
2236 xf.xf_width[0] = -xf.xf_width[0];
2237 xf.xf_seen_minus = 1;
2239 xf.xf_width[s] = -1; /* Ignore negative values */
2246 /* If no max is given, it defaults to size */
2247 if (xf.xf_width[XF_WIDTH_MAX] < 0 && xf.xf_width[XF_WIDTH_SIZE] >= 0)
2248 xf.xf_width[XF_WIDTH_MAX] = xf.xf_width[XF_WIDTH_SIZE];
2250 if (xf.xf_fc == 'D' || xf.xf_fc == 'O' || xf.xf_fc == 'U')
2254 xo_buffer_t *fbp = &xop->xo_fmt;
2255 int len = cp - sp + 1;
2256 if (!xo_buf_has_room(fbp, len + 1))
2259 char *newfmt = fbp->xb_curp;
2260 memcpy(newfmt, sp, len);
2261 newfmt[0] = '%'; /* If we skipped over a "%@...@s" format */
2265 * Bad news: our strings are UTF-8, but the stock printf
2266 * functions won't handle field widths for wide characters
2267 * correctly. So we have to handle this ourselves.
2269 if (xop->xo_formatter == NULL
2270 && (xf.xf_fc == 's' || xf.xf_fc == 'S')) {
2271 xf.xf_enc = (xf.xf_lflag || (xf.xf_fc == 'S'))
2272 ? XF_ENC_WIDE : xf.xf_hflag ? XF_ENC_LOCALE : XF_ENC_UTF8;
2273 rc = xo_format_string(xop, xbp, flags, &xf);
2275 if ((flags & XFF_TRIM_WS)
2276 && (xop->xo_style == XO_STYLE_XML
2277 || xop->xo_style == XO_STYLE_JSON))
2278 rc = xo_trim_ws(xbp, rc);
2281 int columns = rc = xo_vsnprintf(xop, xbp, newfmt, xop->xo_vap);
2284 * For XML and HTML, we need "&<>" processing; for JSON,
2285 * it's quotes. Text gets nothing.
2289 if (flags & XFF_TRIM_WS)
2290 columns = rc = xo_trim_ws(xbp, rc);
2293 rc = xo_escape_xml(xbp, rc, (flags & XFF_ATTR));
2297 if (flags & XFF_TRIM_WS)
2298 columns = rc = xo_trim_ws(xbp, rc);
2299 rc = xo_escape_json(xbp, rc);
2304 * We can assume all the data we've added is ASCII, so
2305 * the columns and bytes are the same. xo_format_string
2306 * handles all the fancy string conversions and updates
2307 * xo_anchor_columns accordingly.
2309 if (xop->xo_flags & XOF_COLUMNS)
2310 xop->xo_columns += columns;
2311 if (xop->xo_flags & XOF_ANCHOR)
2312 xop->xo_anchor_columns += columns;
2319 * Now for the tricky part: we need to move the argument pointer
2320 * along by the amount needed.
2322 if (!(xop->xo_flags & XOF_NO_VA_ARG)) {
2324 if (xf.xf_fc == 's' ||xf.xf_fc == 'S') {
2326 * The 'S' and 's' formats are normally handled in
2327 * xo_format_string, but if we skipped it, then we
2331 va_arg(xop->xo_vap, char *);
2335 for (s = 0; s < XF_WIDTH_NUM; s++) {
2337 va_arg(xop->xo_vap, int);
2340 if (strchr("diouxXDOU", xf.xf_fc) != NULL) {
2341 if (xf.xf_hflag > 1) {
2342 va_arg(xop->xo_vap, int);
2344 } else if (xf.xf_hflag > 0) {
2345 va_arg(xop->xo_vap, int);
2347 } else if (xf.xf_lflag > 1) {
2348 va_arg(xop->xo_vap, unsigned long long);
2350 } else if (xf.xf_lflag > 0) {
2351 va_arg(xop->xo_vap, unsigned long);
2353 } else if (xf.xf_jflag > 0) {
2354 va_arg(xop->xo_vap, intmax_t);
2356 } else if (xf.xf_tflag > 0) {
2357 va_arg(xop->xo_vap, ptrdiff_t);
2359 } else if (xf.xf_zflag > 0) {
2360 va_arg(xop->xo_vap, size_t);
2362 } else if (xf.xf_qflag > 0) {
2363 va_arg(xop->xo_vap, quad_t);
2366 va_arg(xop->xo_vap, int);
2368 } else if (strchr("eEfFgGaA", xf.xf_fc) != NULL)
2370 va_arg(xop->xo_vap, long double);
2372 va_arg(xop->xo_vap, double);
2374 else if (xf.xf_fc == 'C' || (xf.xf_fc == 'c' && xf.xf_lflag))
2375 va_arg(xop->xo_vap, wint_t);
2377 else if (xf.xf_fc == 'c')
2378 va_arg(xop->xo_vap, int);
2380 else if (xf.xf_fc == 'p')
2381 va_arg(xop->xo_vap, void *);
2388 cols = xo_format_string_direct(xop, xbp, flags | XFF_UNESCAPE,
2389 NULL, xp, cp - xp, -1,
2390 need_enc, XF_ENC_UTF8);
2391 if (xop->xo_flags & XOF_COLUMNS)
2392 xop->xo_columns += cols;
2393 if (xop->xo_flags & XOF_ANCHOR)
2394 xop->xo_anchor_columns += cols;
2404 xo_fix_encoding (xo_handle_t *xop UNUSED, char *encoding)
2406 char *cp = encoding;
2408 if (cp[0] != '%' || !isdigit((int) cp[1]))
2411 for (cp += 2; *cp; cp++) {
2412 if (!isdigit((int) *cp))
2423 xo_buf_append_div (xo_handle_t *xop, const char *class, xo_xff_flags_t flags,
2424 const char *name, int nlen,
2425 const char *value, int vlen,
2426 const char *encoding, int elen)
2428 static char div_start[] = "<div class=\"";
2429 static char div_tag[] = "\" data-tag=\"";
2430 static char div_xpath[] = "\" data-xpath=\"";
2431 static char div_key[] = "\" data-key=\"key";
2432 static char div_end[] = "\">";
2433 static char div_close[] = "</div>";
2436 * To build our XPath predicate, we need to save the va_list before
2437 * we format our data, and then restore it before we format the
2439 * Display-only keys implies that we've got an encode-only key
2440 * elsewhere, so we don't use them from making predicates.
2442 int need_predidate =
2443 (name && (flags & XFF_KEY) && !(flags & XFF_DISPLAY_ONLY)
2444 && (xop->xo_flags & XOF_XPATH));
2446 if (need_predidate) {
2449 va_copy(va_local, xop->xo_vap);
2450 if (xop->xo_checkpointer)
2451 xop->xo_checkpointer(xop, xop->xo_vap, 0);
2454 * Build an XPath predicate expression to match this key.
2455 * We use the format buffer.
2457 xo_buffer_t *pbp = &xop->xo_predicate;
2458 pbp->xb_curp = pbp->xb_bufp; /* Restart buffer */
2460 xo_buf_append(pbp, "[", 1);
2461 xo_buf_escape(xop, pbp, name, nlen, 0);
2462 if (xop->xo_flags & XOF_PRETTY)
2463 xo_buf_append(pbp, " = '", 4);
2465 xo_buf_append(pbp, "='", 2);
2467 /* The encoding format defaults to the normal format */
2468 if (encoding == NULL) {
2469 char *enc = alloca(vlen + 1);
2470 memcpy(enc, value, vlen);
2472 encoding = xo_fix_encoding(xop, enc);
2473 elen = strlen(encoding);
2476 xo_format_data(xop, pbp, encoding, elen, XFF_XML | XFF_ATTR);
2478 xo_buf_append(pbp, "']", 2);
2480 /* Now we record this predicate expression in the stack */
2481 xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth];
2482 int olen = xsp->xs_keys ? strlen(xsp->xs_keys) : 0;
2483 int dlen = pbp->xb_curp - pbp->xb_bufp;
2485 char *cp = xo_realloc(xsp->xs_keys, olen + dlen + 1);
2487 memcpy(cp + olen, pbp->xb_bufp, dlen);
2488 cp[olen + dlen] = '\0';
2492 /* Now we reset the xo_vap as if we were never here */
2493 va_end(xop->xo_vap);
2494 va_copy(xop->xo_vap, va_local);
2496 if (xop->xo_checkpointer)
2497 xop->xo_checkpointer(xop, xop->xo_vap, 1);
2500 if (flags & XFF_ENCODE_ONLY) {
2502 * Even if this is encode-only, we need to go thru the
2503 * work of formatting it to make sure the args are cleared
2506 xo_format_data(xop, &xop->xo_data, encoding, elen,
2507 flags | XFF_NO_OUTPUT);
2511 xo_line_ensure_open(xop, 0);
2513 if (xop->xo_flags & XOF_PRETTY)
2514 xo_buf_indent(xop, xop->xo_indent_by);
2516 xo_data_append(xop, div_start, sizeof(div_start) - 1);
2517 xo_data_append(xop, class, strlen(class));
2520 xo_data_append(xop, div_tag, sizeof(div_tag) - 1);
2521 xo_data_escape(xop, name, nlen);
2524 * Save the offset at which we'd place units. See xo_format_units.
2526 if (xop->xo_flags & XOF_UNITS) {
2527 xop->xo_flags |= XOF_UNITS_PENDING;
2529 * Note: We need the '+1' here because we know we've not
2530 * added the closing quote. We add one, knowing the quote
2531 * will be added shortly.
2533 xop->xo_units_offset =
2534 xop->xo_data.xb_curp -xop->xo_data.xb_bufp + 1;
2539 if (xop->xo_flags & XOF_XPATH) {
2543 xo_data_append(xop, div_xpath, sizeof(div_xpath) - 1);
2544 if (xop->xo_leading_xpath)
2545 xo_data_append(xop, xop->xo_leading_xpath,
2546 strlen(xop->xo_leading_xpath));
2548 for (i = 0; i <= xop->xo_depth; i++) {
2549 xsp = &xop->xo_stack[i];
2550 if (xsp->xs_name == NULL)
2553 xo_data_append(xop, "/", 1);
2554 xo_data_escape(xop, xsp->xs_name, strlen(xsp->xs_name));
2556 /* Don't show keys for the key field */
2557 if (i != xop->xo_depth || !(flags & XFF_KEY))
2558 xo_data_append(xop, xsp->xs_keys, strlen(xsp->xs_keys));
2562 xo_data_append(xop, "/", 1);
2563 xo_data_escape(xop, name, nlen);
2566 if ((xop->xo_flags & XOF_INFO) && xop->xo_info) {
2567 static char in_type[] = "\" data-type=\"";
2568 static char in_help[] = "\" data-help=\"";
2570 xo_info_t *xip = xo_info_find(xop, name, nlen);
2573 xo_data_append(xop, in_type, sizeof(in_type) - 1);
2574 xo_data_escape(xop, xip->xi_type, strlen(xip->xi_type));
2577 xo_data_append(xop, in_help, sizeof(in_help) - 1);
2578 xo_data_escape(xop, xip->xi_help, strlen(xip->xi_help));
2583 if ((flags & XFF_KEY) && (xop->xo_flags & XOF_KEYS))
2584 xo_data_append(xop, div_key, sizeof(div_key) - 1);
2587 xo_data_append(xop, div_end, sizeof(div_end) - 1);
2589 xo_format_data(xop, NULL, value, vlen, 0);
2591 xo_data_append(xop, div_close, sizeof(div_close) - 1);
2593 if (xop->xo_flags & XOF_PRETTY)
2594 xo_data_append(xop, "\n", 1);
2598 xo_format_text (xo_handle_t *xop, const char *str, int len)
2600 switch (xop->xo_style) {
2602 xo_buf_append_locale(xop, &xop->xo_data, str, len);
2606 xo_buf_append_div(xop, "text", 0, NULL, 0, str, len, NULL, 0);
2612 xo_format_title (xo_handle_t *xop, const char *str, int len,
2613 const char *fmt, int flen)
2615 static char div_open[] = "<div class=\"title\">";
2616 static char div_close[] = "</div>";
2618 switch (xop->xo_style) {
2622 * Even though we don't care about text, we need to do
2623 * enough parsing work to skip over the right bits of xo_vap.
2626 xo_format_data(xop, NULL, fmt, flen, XFF_NO_OUTPUT);
2630 xo_buffer_t *xbp = &xop->xo_data;
2631 int start = xbp->xb_curp - xbp->xb_bufp;
2632 int left = xbp->xb_size - start;
2634 int need_enc = XF_ENC_LOCALE;
2636 if (xop->xo_style == XO_STYLE_HTML) {
2637 need_enc = XF_ENC_UTF8;
2638 xo_line_ensure_open(xop, 0);
2639 if (xop->xo_flags & XOF_PRETTY)
2640 xo_buf_indent(xop, xop->xo_indent_by);
2641 xo_buf_append(&xop->xo_data, div_open, sizeof(div_open) - 1);
2644 start = xbp->xb_curp - xbp->xb_bufp; /* Reset start */
2646 char *newfmt = alloca(flen + 1);
2647 memcpy(newfmt, fmt, flen);
2648 newfmt[flen] = '\0';
2650 /* If len is non-zero, the format string apply to the name */
2651 char *newstr = alloca(len + 1);
2652 memcpy(newstr, str, len);
2655 if (newstr[len - 1] == 's') {
2659 rc = snprintf(NULL, 0, newfmt, newstr);
2662 * We have to do this the hard way, since we might need
2665 bp = alloca(rc + 1);
2666 rc = snprintf(bp, rc + 1, newfmt, newstr);
2667 cols = xo_format_string_direct(xop, xbp, 0, NULL, bp, rc, -1,
2668 need_enc, XF_ENC_UTF8);
2670 if (xop->xo_flags & XOF_COLUMNS)
2671 xop->xo_columns += cols;
2672 if (xop->xo_flags & XOF_ANCHOR)
2673 xop->xo_anchor_columns += cols;
2679 rc = snprintf(xbp->xb_curp, left, newfmt, newstr);
2681 if (!xo_buf_has_room(xbp, rc))
2683 left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp);
2684 rc = snprintf(xbp->xb_curp, left, newfmt, newstr);
2688 if (xop->xo_flags & XOF_COLUMNS)
2689 xop->xo_columns += rc;
2690 if (xop->xo_flags & XOF_ANCHOR)
2691 xop->xo_anchor_columns += rc;
2696 xo_format_data(xop, NULL, fmt, flen, 0);
2698 /* xo_format_data moved curp, so we need to reset it */
2699 rc = xbp->xb_curp - (xbp->xb_bufp + start);
2700 xbp->xb_curp = xbp->xb_bufp + start;
2703 /* If we're styling HTML, then we need to escape it */
2704 if (xop->xo_style == XO_STYLE_HTML) {
2705 rc = xo_escape_xml(xbp, rc, 0);
2712 if (xop->xo_style == XO_STYLE_HTML) {
2713 xo_data_append(xop, div_close, sizeof(div_close) - 1);
2714 if (xop->xo_flags & XOF_PRETTY)
2715 xo_data_append(xop, "\n", 1);
2720 xo_format_prep (xo_handle_t *xop, xo_xff_flags_t flags)
2722 if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST) {
2723 xo_data_append(xop, ",", 1);
2724 if (!(flags & XFF_LEAF_LIST) && (xop->xo_flags & XOF_PRETTY))
2725 xo_data_append(xop, "\n", 1);
2727 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST;
2731 /* Useful debugging function */
2733 xo_arg (xo_handle_t *xop);
2735 xo_arg (xo_handle_t *xop)
2737 xop = xo_default(xop);
2738 fprintf(stderr, "0x%x", va_arg(xop->xo_vap, unsigned));
2743 xo_format_value (xo_handle_t *xop, const char *name, int nlen,
2744 const char *format, int flen,
2745 const char *encoding, int elen, xo_xff_flags_t flags)
2747 int pretty = (xop->xo_flags & XOF_PRETTY);
2751 switch (xop->xo_style) {
2753 if (flags & XFF_ENCODE_ONLY)
2754 flags |= XFF_NO_OUTPUT;
2755 xo_format_data(xop, NULL, format, flen, flags);
2759 if (flags & XFF_ENCODE_ONLY)
2760 flags |= XFF_NO_OUTPUT;
2761 xo_buf_append_div(xop, "data", flags, name, nlen,
2762 format, flen, encoding, elen);
2767 * Even though we're not making output, we still need to
2768 * let the formatting code handle the va_arg popping.
2770 if (flags & XFF_DISPLAY_ONLY) {
2771 flags |= XFF_NO_OUTPUT;
2772 xo_format_data(xop, NULL, format, flen, flags);
2780 char *enc = alloca(flen + 1);
2781 memcpy(enc, format, flen);
2783 format = xo_fix_encoding(xop, enc);
2784 flen = strlen(format);
2788 static char missing[] = "missing-field-name";
2789 xo_failure(xop, "missing field name: %s", format);
2791 nlen = sizeof(missing) - 1;
2795 xo_buf_indent(xop, -1);
2796 xo_data_append(xop, "<", 1);
2797 xo_data_escape(xop, name, nlen);
2799 if (xop->xo_attrs.xb_curp != xop->xo_attrs.xb_bufp) {
2800 xo_data_append(xop, xop->xo_attrs.xb_bufp,
2801 xop->xo_attrs.xb_curp - xop->xo_attrs.xb_bufp);
2802 xop->xo_attrs.xb_curp = xop->xo_attrs.xb_bufp;
2806 * We indicate 'key' fields using the 'key' attribute. While
2807 * this is really committing the crime of mixing meta-data with
2808 * data, it's often useful. Especially when format meta-data is
2809 * difficult to come by.
2811 if ((flags & XFF_KEY) && (xop->xo_flags & XOF_KEYS)) {
2812 static char attr[] = " key=\"key\"";
2813 xo_data_append(xop, attr, sizeof(attr) - 1);
2817 * Save the offset at which we'd place units. See xo_format_units.
2819 if (xop->xo_flags & XOF_UNITS) {
2820 xop->xo_flags |= XOF_UNITS_PENDING;
2821 xop->xo_units_offset = xop->xo_data.xb_curp -xop->xo_data.xb_bufp;
2824 xo_data_append(xop, ">", 1);
2825 xo_format_data(xop, NULL, format, flen, flags);
2826 xo_data_append(xop, "</", 2);
2827 xo_data_escape(xop, name, nlen);
2828 xo_data_append(xop, ">", 1);
2830 xo_data_append(xop, "\n", 1);
2834 if (flags & XFF_DISPLAY_ONLY) {
2835 flags |= XFF_NO_OUTPUT;
2836 xo_format_data(xop, NULL, format, flen, flags);
2844 char *enc = alloca(flen + 1);
2845 memcpy(enc, format, flen);
2847 format = xo_fix_encoding(xop, enc);
2848 flen = strlen(format);
2851 int first = !(xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST);
2853 xo_format_prep(xop, flags);
2855 if (flags & XFF_QUOTE)
2857 else if (flags & XFF_NOQUOTE)
2859 else if (flen == 0) {
2861 format = "true"; /* JSON encodes empty tags as a boolean true */
2863 } else if (strchr("diouxXDOUeEfFgGaAcCp", format[flen - 1]) == NULL)
2869 static char missing[] = "missing-field-name";
2870 xo_failure(xop, "missing field name: %s", format);
2872 nlen = sizeof(missing) - 1;
2875 if (flags & XFF_LEAF_LIST) {
2876 if (first && pretty)
2877 xo_buf_indent(xop, -1);
2880 xo_buf_indent(xop, -1);
2881 xo_data_append(xop, "\"", 1);
2883 xbp = &xop->xo_data;
2884 int off = xbp->xb_curp - xbp->xb_bufp;
2886 xo_data_escape(xop, name, nlen);
2888 if (xop->xo_flags & XOF_UNDERSCORES) {
2889 int now = xbp->xb_curp - xbp->xb_bufp;
2890 for ( ; off < now; off++)
2891 if (xbp->xb_bufp[off] == '-')
2892 xbp->xb_bufp[off] = '_';
2894 xo_data_append(xop, "\":", 2);
2898 xo_data_append(xop, " ", 1);
2900 xo_data_append(xop, "\"", 1);
2902 xo_format_data(xop, NULL, format, flen, flags);
2905 xo_data_append(xop, "\"", 1);
2911 xo_format_content (xo_handle_t *xop, const char *class_name,
2912 const char *xml_tag, int display_only,
2913 const char *str, int len, const char *fmt, int flen)
2915 switch (xop->xo_style) {
2918 xo_data_append_content(xop, str, len);
2920 xo_format_data(xop, NULL, fmt, flen, 0);
2929 xo_buf_append_div(xop, class_name, 0, NULL, 0, str, len, NULL, 0);
2939 xo_open_container_h(xop, xml_tag);
2940 xo_format_value(xop, "message", 7, str, len, NULL, 0, 0);
2941 xo_close_container_h(xop, xml_tag);
2945 * Even though we don't care about labels, we need to do
2946 * enough parsing work to skip over the right bits of xo_vap.
2949 xo_format_data(xop, NULL, fmt, flen, XFF_NO_OUTPUT);
2955 * Even though we don't care about labels, we need to do
2956 * enough parsing work to skip over the right bits of xo_vap.
2960 xo_format_data(xop, NULL, fmt, flen, XFF_NO_OUTPUT);
2963 /* XXX need schem for representing errors in JSON */
2969 xo_format_units (xo_handle_t *xop, const char *str, int len,
2970 const char *fmt, int flen)
2972 static char units_start_xml[] = " units=\"";
2973 static char units_start_html[] = " data-units=\"";
2975 if (!(xop->xo_flags & XOF_UNITS_PENDING)) {
2976 xo_format_content(xop, "units", NULL, 1, str, len, fmt, flen);
2980 xo_buffer_t *xbp = &xop->xo_data;
2981 int start = xop->xo_units_offset;
2982 int stop = xbp->xb_curp - xbp->xb_bufp;
2984 if (xop->xo_style == XO_STYLE_XML)
2985 xo_buf_append(xbp, units_start_xml, sizeof(units_start_xml) - 1);
2986 else if (xop->xo_style == XO_STYLE_HTML)
2987 xo_buf_append(xbp, units_start_html, sizeof(units_start_html) - 1);
2992 xo_data_append(xop, str, len);
2994 xo_format_data(xop, NULL, fmt, flen, 0);
2996 xo_buf_append(xbp, "\"", 1);
2998 int now = xbp->xb_curp - xbp->xb_bufp;
2999 int delta = now - stop;
3000 if (delta < 0) { /* Strange; no output to move */
3001 xbp->xb_curp = xbp->xb_bufp + stop; /* Reset buffer to prior state */
3006 * Now we're in it alright. We've need to insert the unit value
3007 * we just created into the right spot. We make a local copy,
3008 * move it and then insert our copy. We know there's room in the
3009 * buffer, since we're just moving this around.
3011 char *buf = alloca(delta);
3013 memcpy(buf, xbp->xb_bufp + stop, delta);
3014 memmove(xbp->xb_bufp + start + delta, xbp->xb_bufp + start, stop - start);
3015 memmove(xbp->xb_bufp + start, buf, delta);
3019 xo_find_width (xo_handle_t *xop, const char *str, int len,
3020 const char *fmt, int flen)
3027 bp = alloca(len + 1); /* Make local NUL-terminated copy of str */
3028 memcpy(bp, str, len);
3031 width = strtol(bp, &cp, 0);
3032 if (width == LONG_MIN || width == LONG_MAX
3033 || bp == cp || *cp != '\0' ) {
3035 xo_failure(xop, "invalid width for anchor: '%s'", bp);
3038 if (flen != 2 || strncmp("%d", fmt, flen) != 0)
3039 xo_failure(xop, "invalid width format: '%*.*s'", flen, flen, fmt);
3040 if (!(xop->xo_flags & XOF_NO_VA_ARG))
3041 width = va_arg(xop->xo_vap, int);
3048 xo_anchor_clear (xo_handle_t *xop)
3050 xop->xo_flags &= ~XOF_ANCHOR;
3051 xop->xo_anchor_offset = 0;
3052 xop->xo_anchor_columns = 0;
3053 xop->xo_anchor_min_width = 0;
3057 * An anchor is a marker used to delay field width implications.
3058 * Imagine the format string "{[:10}{min:%d}/{cur:%d}/{max:%d}{:]}".
3059 * We are looking for output like " 1/4/5"
3061 * To make this work, we record the anchor and then return to
3062 * format it when the end anchor tag is seen.
3065 xo_anchor_start (xo_handle_t *xop, const char *str, int len,
3066 const char *fmt, int flen)
3068 if (xop->xo_style != XO_STYLE_TEXT && xop->xo_style != XO_STYLE_HTML)
3071 if (xop->xo_flags & XOF_ANCHOR)
3072 xo_failure(xop, "the anchor already recording is discarded");
3074 xop->xo_flags |= XOF_ANCHOR;
3075 xo_buffer_t *xbp = &xop->xo_data;
3076 xop->xo_anchor_offset = xbp->xb_curp - xbp->xb_bufp;
3077 xop->xo_anchor_columns = 0;
3080 * Now we find the width, if possible. If it's not there,
3081 * we'll get it on the end anchor.
3083 xop->xo_anchor_min_width = xo_find_width(xop, str, len, fmt, flen);
3087 xo_anchor_stop (xo_handle_t *xop, const char *str, int len,
3088 const char *fmt, int flen)
3090 if (xop->xo_style != XO_STYLE_TEXT && xop->xo_style != XO_STYLE_HTML)
3093 if (!(xop->xo_flags & XOF_ANCHOR)) {
3094 xo_failure(xop, "no start anchor");
3098 xop->xo_flags &= ~XOF_UNITS_PENDING;
3100 int width = xo_find_width(xop, str, len, fmt, flen);
3102 width = xop->xo_anchor_min_width;
3104 if (width == 0) /* No width given; nothing to do */
3107 xo_buffer_t *xbp = &xop->xo_data;
3108 int start = xop->xo_anchor_offset;
3109 int stop = xbp->xb_curp - xbp->xb_bufp;
3110 int abswidth = (width > 0) ? width : -width;
3111 int blen = abswidth - xop->xo_anchor_columns;
3113 if (blen <= 0) /* Already over width */
3116 if (abswidth > XO_MAX_ANCHOR_WIDTH) {
3117 xo_failure(xop, "width over %u are not supported",
3118 XO_MAX_ANCHOR_WIDTH);
3122 /* Make a suitable padding field and emit it */
3123 char *buf = alloca(blen);
3124 memset(buf, ' ', blen);
3125 xo_format_content(xop, "padding", NULL, 1, buf, blen, NULL, 0);
3127 if (width < 0) /* Already left justified */
3130 int now = xbp->xb_curp - xbp->xb_bufp;
3131 int delta = now - stop;
3132 if (delta < 0) /* Strange; no output to move */
3136 * Now we're in it alright. We've need to insert the padding data
3137 * we just created (which might be an HTML <div> or text) before
3138 * the formatted data. We make a local copy, move it and then
3139 * insert our copy. We know there's room in the buffer, since
3140 * we're just moving this around.
3143 buf = alloca(delta); /* Expand buffer if needed */
3145 memcpy(buf, xbp->xb_bufp + stop, delta);
3146 memmove(xbp->xb_bufp + start + delta, xbp->xb_bufp + start, stop - start);
3147 memmove(xbp->xb_bufp + start, buf, delta);
3150 xo_anchor_clear(xop);
3154 xo_do_emit (xo_handle_t *xop, const char *fmt)
3157 const char *cp, *sp, *ep, *basep;
3159 int flush = (xop->xo_flags & XOF_FLUSH) ? 1 : 0;
3161 xop->xo_columns = 0; /* Always reset it */
3163 for (cp = fmt; *cp; ) {
3170 } else if (*cp == '{') {
3171 if (cp[1] == '{') { /* Start of {{escaped braces}} */
3173 cp += 2; /* Skip over _both_ characters */
3174 for (sp = cp; *sp; sp++) {
3175 if (*sp == '}' && sp[1] == '}')
3179 xo_failure(xop, "missing closing '}}': %s", fmt);
3183 xo_format_text(xop, cp, sp - cp);
3185 /* Move along the string, but don't run off the end */
3186 if (*sp == '}' && sp[1] == '}')
3188 cp = *sp ? sp + 1 : sp;
3191 /* Else fall thru to the code below */
3195 for (sp = cp; *sp; sp++) {
3196 if (*sp == '{' || *sp == '\n')
3199 xo_format_text(xop, cp, sp - cp);
3208 * We are looking at the start of a field definition. The format is:
3209 * '{' modifiers ':' content [ '/' print-fmt [ '/' encode-fmt ]] '}'
3210 * Modifiers are optional and include the following field types:
3211 * 'D': decoration; something non-text and non-data (colons, commmas)
3212 * 'E': error message
3213 * 'L': label; text preceding data
3214 * 'N': note; text following data
3215 * 'P': padding; whitespace
3216 * 'T': Title, where 'content' is a column title
3217 * 'U': Units, where 'content' is the unit label
3218 * 'V': value, where 'content' is the name of the field (the default)
3219 * 'W': warning message
3220 * '[': start a section of anchored text
3221 * ']': end a section of anchored text
3222 * The following flags are also supported:
3223 * 'c': flag: emit a colon after the label
3224 * 'd': field is only emitted for display formats (text and html)
3225 * 'e': field is only emitted for encoding formats (xml and json)
3226 * 'k': this field is a key, suitable for XPath predicates
3227 * 'l': a leaf-list, a simple list of values
3228 * 'n': no quotes around this field
3229 * 'q': add quotes around this field
3230 * 't': trim whitespace around the value
3231 * 'w': emit a blank after the label
3232 * The print-fmt and encode-fmt strings is the printf-style formating
3233 * for this data. JSON and XML will use the encoding-fmt, if present.
3234 * If the encode-fmt is not provided, it defaults to the print-fmt.
3235 * If the print-fmt is not provided, it defaults to 's'.
3237 unsigned ftype = 0, flags = 0;
3238 const char *content = NULL, *format = NULL, *encoding = NULL;
3239 int clen = 0, flen = 0, elen = 0;
3241 for (sp = basep; sp; sp++) {
3242 if (*sp == ':' || *sp == '/' || *sp == '}')
3246 if (sp[1] == '\0') {
3247 xo_failure(xop, "backslash at the end of string");
3267 xo_failure(xop, "field descriptor uses multiple types: %s",
3279 flags |= XFF_DISPLAY_ONLY;
3283 flags |= XFF_ENCODE_ONLY;
3291 flags |= XFF_LEAF_LIST;
3295 flags |= XFF_NOQUOTE;
3303 flags |= XFF_TRIM_WS;
3311 xo_failure(xop, "field descriptor uses unknown modifier: %s",
3314 * No good answer here; a bad format will likely
3315 * mean a core file. We just return and hope
3316 * the caller notices there's no output, and while
3317 * that seems, well, bad. There's nothing better.
3324 for (ep = ++sp; *sp; sp++) {
3325 if (*sp == '}' || *sp == '/')
3328 if (sp[1] == '\0') {
3329 xo_failure(xop, "backslash at the end of string");
3341 xo_failure(xop, "missing content (':'): %s", fmt);
3346 for (ep = ++sp; *sp; sp++) {
3347 if (*sp == '}' || *sp == '/')
3350 if (sp[1] == '\0') {
3351 xo_failure(xop, "backslash at the end of string");
3363 for (ep = ++sp; *sp; sp++) {
3374 xo_failure(xop, "missing closing '}': %s", fmt);
3378 if (format == NULL && ftype != '[' && ftype != ']' ) {
3383 if (ftype == 0 || ftype == 'V')
3384 xo_format_value(xop, content, clen, format, flen,
3385 encoding, elen, flags);
3386 else if (ftype == 'D')
3387 xo_format_content(xop, "decoration", NULL, 1,
3388 content, clen, format, flen);
3389 else if (ftype == 'E')
3390 xo_format_content(xop, "error", "error", 0,
3391 content, clen, format, flen);
3392 else if (ftype == 'L')
3393 xo_format_content(xop, "label", NULL, 1,
3394 content, clen, format, flen);
3395 else if (ftype == 'N')
3396 xo_format_content(xop, "note", NULL, 1,
3397 content, clen, format, flen);
3398 else if (ftype == 'P')
3399 xo_format_content(xop, "padding", NULL, 1,
3400 content, clen, format, flen);
3401 else if (ftype == 'T')
3402 xo_format_title(xop, content, clen, format, flen);
3403 else if (ftype == 'U') {
3405 xo_format_content(xop, "padding", NULL, 1, " ", 1, NULL, 0);
3406 xo_format_units(xop, content, clen, format, flen);
3407 } else if (ftype == 'W')
3408 xo_format_content(xop, "warning", "warning", 0,
3409 content, clen, format, flen);
3410 else if (ftype == '[')
3411 xo_anchor_start(xop, content, clen, format, flen);
3412 else if (ftype == ']')
3413 xo_anchor_stop(xop, content, clen, format, flen);
3415 if (flags & XFF_COLON)
3416 xo_format_content(xop, "decoration", NULL, 1, ":", 1, NULL, 0);
3417 if (ftype != 'U' && (flags & XFF_WS))
3418 xo_format_content(xop, "padding", NULL, 1, " ", 1, NULL, 0);
3420 cp += sp - basep + 1;
3427 /* If we don't have an anchor, write the text out */
3428 if (flush && !(xop->xo_flags & XOF_ANCHOR))
3431 return (rc < 0) ? rc : (int) xop->xo_columns;
3435 xo_emit_hv (xo_handle_t *xop, const char *fmt, va_list vap)
3439 xop = xo_default(xop);
3440 va_copy(xop->xo_vap, vap);
3441 rc = xo_do_emit(xop, fmt);
3442 va_end(xop->xo_vap);
3443 bzero(&xop->xo_vap, sizeof(xop->xo_vap));
3449 xo_emit_h (xo_handle_t *xop, const char *fmt, ...)
3453 xop = xo_default(xop);
3454 va_start(xop->xo_vap, fmt);
3455 rc = xo_do_emit(xop, fmt);
3456 va_end(xop->xo_vap);
3457 bzero(&xop->xo_vap, sizeof(xop->xo_vap));
3463 xo_emit (const char *fmt, ...)
3465 xo_handle_t *xop = xo_default(NULL);
3468 va_start(xop->xo_vap, fmt);
3469 rc = xo_do_emit(xop, fmt);
3470 va_end(xop->xo_vap);
3471 bzero(&xop->xo_vap, sizeof(xop->xo_vap));
3477 xo_attr_hv (xo_handle_t *xop, const char *name, const char *fmt, va_list vap)
3479 const int extra = 5; /* space, equals, quote, quote, and nul */
3480 xop = xo_default(xop);
3482 if (xop->xo_style != XO_STYLE_XML)
3485 int nlen = strlen(name);
3486 xo_buffer_t *xbp = &xop->xo_attrs;
3488 if (!xo_buf_has_room(xbp, nlen + extra))
3491 *xbp->xb_curp++ = ' ';
3492 memcpy(xbp->xb_curp, name, nlen);
3493 xbp->xb_curp += nlen;
3494 *xbp->xb_curp++ = '=';
3495 *xbp->xb_curp++ = '"';
3497 int rc = xo_vsnprintf(xop, xbp, fmt, vap);
3500 rc = xo_escape_xml(xbp, rc, 1);
3504 if (!xo_buf_has_room(xbp, 2))
3507 *xbp->xb_curp++ = '"';
3508 *xbp->xb_curp = '\0';
3510 return rc + nlen + extra;
3514 xo_attr_h (xo_handle_t *xop, const char *name, const char *fmt, ...)
3520 rc = xo_attr_hv(xop, name, fmt, vap);
3527 xo_attr (const char *name, const char *fmt, ...)
3533 rc = xo_attr_hv(NULL, name, fmt, vap);
3540 xo_stack_set_flags (xo_handle_t *xop)
3542 if (xop->xo_flags & XOF_NOT_FIRST) {
3543 xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth];
3545 xsp->xs_flags |= XSF_NOT_FIRST;
3546 xop->xo_flags &= ~XOF_NOT_FIRST;
3551 xo_depth_change (xo_handle_t *xop, const char *name,
3552 int delta, int indent, xo_xsf_flags_t flags)
3554 if (xop->xo_flags & XOF_DTRT)
3557 if (delta >= 0) { /* Push operation */
3558 if (xo_depth_check(xop, xop->xo_depth + delta))
3561 xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth + delta];
3562 xsp->xs_flags = flags;
3563 xo_stack_set_flags(xop);
3565 unsigned save = (xop->xo_flags & (XOF_XPATH | XOF_WARN | XOF_DTRT));
3566 save |= (flags & XSF_DTRT);
3569 int len = strlen(name) + 1;
3570 char *cp = xo_realloc(NULL, len);
3572 memcpy(cp, name, len);
3577 } else { /* Pop operation */
3578 if (xop->xo_depth == 0) {
3579 if (!(xop->xo_flags & XOF_IGNORE_CLOSE))
3580 xo_failure(xop, "close with empty stack: '%s'", name);
3584 xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth];
3585 if (xop->xo_flags & XOF_WARN) {
3586 const char *top = xsp->xs_name;
3587 if (top && strcmp(name, top) != 0) {
3588 xo_failure(xop, "incorrect close: '%s' .vs. '%s'",
3592 if ((xsp->xs_flags & XSF_LIST) != (flags & XSF_LIST)) {
3593 xo_failure(xop, "list close on list confict: '%s'",
3597 if ((xsp->xs_flags & XSF_INSTANCE) != (flags & XSF_INSTANCE)) {
3598 xo_failure(xop, "list close on instance confict: '%s'",
3605 xo_free(xsp->xs_name);
3606 xsp->xs_name = NULL;
3609 xo_free(xsp->xs_keys);
3610 xsp->xs_keys = NULL;
3614 xop->xo_depth += delta; /* Record new depth */
3615 xop->xo_indent += indent;
3619 xo_set_depth (xo_handle_t *xop, int depth)
3621 xop = xo_default(xop);
3623 if (xo_depth_check(xop, depth))
3626 xop->xo_depth += depth;
3627 xop->xo_indent += depth;
3630 static xo_xsf_flags_t
3631 xo_stack_flags (unsigned xflags)
3633 if (xflags & XOF_DTRT)
3639 xo_open_container_hf (xo_handle_t *xop, xo_xof_flags_t flags, const char *name)
3641 xop = xo_default(xop);
3644 const char *ppn = (xop->xo_flags & XOF_PRETTY) ? "\n" : "";
3645 const char *pre_nl = "";
3648 xo_failure(xop, "NULL passed for container name");
3649 name = XO_FAILURE_NAME;
3652 flags |= xop->xo_flags; /* Pick up handle flags */
3654 switch (xop->xo_style) {
3656 rc = xo_printf(xop, "%*s<%s>%s", xo_indent(xop), "",
3658 xo_depth_change(xop, name, 1, 1, xo_stack_flags(flags));
3662 xo_stack_set_flags(xop);
3664 if (!(xop->xo_flags & XOF_NO_TOP)) {
3665 if (!(xop->xo_flags & XOF_TOP_EMITTED)) {
3666 xo_printf(xop, "%*s{%s", xo_indent(xop), "", ppn);
3667 xop->xo_flags |= XOF_TOP_EMITTED;
3671 if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST)
3672 pre_nl = (xop->xo_flags & XOF_PRETTY) ? ",\n" : ", ";
3673 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST;
3675 rc = xo_printf(xop, "%s%*s\"%s\": {%s",
3676 pre_nl, xo_indent(xop), "", name, ppn);
3677 xo_depth_change(xop, name, 1, 1, xo_stack_flags(flags));
3682 xo_depth_change(xop, name, 1, 0, xo_stack_flags(flags));
3690 xo_open_container_h (xo_handle_t *xop, const char *name)
3692 return xo_open_container_hf(xop, 0, name);
3696 xo_open_container (const char *name)
3698 return xo_open_container_hf(NULL, 0, name);
3702 xo_open_container_hd (xo_handle_t *xop, const char *name)
3704 return xo_open_container_hf(xop, XOF_DTRT, name);
3708 xo_open_container_d (const char *name)
3710 return xo_open_container_hf(NULL, XOF_DTRT, name);
3714 xo_close_container_h (xo_handle_t *xop, const char *name)
3716 xop = xo_default(xop);
3719 const char *ppn = (xop->xo_flags & XOF_PRETTY) ? "\n" : "";
3720 const char *pre_nl = "";
3723 xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth];
3724 if (!(xsp->xs_flags & XSF_DTRT))
3725 xo_failure(xop, "missing name without 'dtrt' mode");
3727 name = xsp->xs_name;
3729 int len = strlen(name) + 1;
3730 /* We need to make a local copy; xo_depth_change will free it */
3731 char *cp = alloca(len);
3732 memcpy(cp, name, len);
3735 name = XO_FAILURE_NAME;
3738 switch (xop->xo_style) {
3740 xo_depth_change(xop, name, -1, -1, 0);
3741 rc = xo_printf(xop, "%*s</%s>%s", xo_indent(xop), "", name, ppn);
3745 pre_nl = (xop->xo_flags & XOF_PRETTY) ? "\n" : "";
3746 ppn = (xop->xo_depth <= 1) ? "\n" : "";
3748 xo_depth_change(xop, name, -1, -1, 0);
3749 rc = xo_printf(xop, "%s%*s}%s", pre_nl, xo_indent(xop), "", ppn);
3750 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST;
3755 xo_depth_change(xop, name, -1, 0, 0);
3763 xo_close_container (const char *name)
3765 return xo_close_container_h(NULL, name);
3769 xo_close_container_hd (xo_handle_t *xop)
3771 return xo_close_container_h(xop, NULL);
3775 xo_close_container_d (void)
3777 return xo_close_container_h(NULL, NULL);
3781 xo_open_list_hf (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name)
3783 xop = xo_default(xop);
3785 if (xop->xo_style != XO_STYLE_JSON)
3789 const char *ppn = (xop->xo_flags & XOF_PRETTY) ? "\n" : "";
3790 const char *pre_nl = "";
3792 if (!(xop->xo_flags & XOF_NO_TOP)) {
3793 if (!(xop->xo_flags & XOF_TOP_EMITTED)) {
3794 xo_printf(xop, "%*s{%s", xo_indent(xop), "", ppn);
3795 xop->xo_flags |= XOF_TOP_EMITTED;
3800 xo_failure(xop, "NULL passed for list name");
3801 name = XO_FAILURE_NAME;
3804 xo_stack_set_flags(xop);
3806 if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST)
3807 pre_nl = (xop->xo_flags & XOF_PRETTY) ? ",\n" : ", ";
3808 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST;
3810 rc = xo_printf(xop, "%s%*s\"%s\": [%s",
3811 pre_nl, xo_indent(xop), "", name, ppn);
3812 xo_depth_change(xop, name, 1, 1, XSF_LIST | xo_stack_flags(flags));
3818 xo_open_list_h (xo_handle_t *xop, const char *name UNUSED)
3820 return xo_open_list_hf(xop, 0, name);
3824 xo_open_list (const char *name)
3826 return xo_open_list_hf(NULL, 0, name);
3830 xo_open_list_hd (xo_handle_t *xop, const char *name UNUSED)
3832 return xo_open_list_hf(xop, XOF_DTRT, name);
3836 xo_open_list_d (const char *name)
3838 return xo_open_list_hf(NULL, XOF_DTRT, name);
3842 xo_close_list_h (xo_handle_t *xop, const char *name)
3845 const char *pre_nl = "";
3847 xop = xo_default(xop);
3849 if (xop->xo_style != XO_STYLE_JSON)
3853 xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth];
3854 if (!(xsp->xs_flags & XSF_DTRT))
3855 xo_failure(xop, "missing name without 'dtrt' mode");
3857 name = xsp->xs_name;
3859 int len = strlen(name) + 1;
3860 /* We need to make a local copy; xo_depth_change will free it */
3861 char *cp = alloca(len);
3862 memcpy(cp, name, len);
3865 name = XO_FAILURE_NAME;
3868 if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST)
3869 pre_nl = (xop->xo_flags & XOF_PRETTY) ? "\n" : "";
3870 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST;
3872 xo_depth_change(xop, name, -1, -1, XSF_LIST);
3873 rc = xo_printf(xop, "%s%*s]", pre_nl, xo_indent(xop), "");
3874 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST;
3880 xo_close_list (const char *name)
3882 return xo_close_list_h(NULL, name);
3886 xo_close_list_hd (xo_handle_t *xop)
3888 return xo_close_list_h(xop, NULL);
3892 xo_close_list_d (void)
3894 return xo_close_list_h(NULL, NULL);
3898 xo_open_instance_hf (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name)
3900 xop = xo_default(xop);
3903 const char *ppn = (xop->xo_flags & XOF_PRETTY) ? "\n" : "";
3904 const char *pre_nl = "";
3906 flags |= xop->xo_flags;
3909 xo_failure(xop, "NULL passed for instance name");
3910 name = XO_FAILURE_NAME;
3913 switch (xop->xo_style) {
3915 rc = xo_printf(xop, "%*s<%s>%s", xo_indent(xop), "", name, ppn);
3916 xo_depth_change(xop, name, 1, 1, xo_stack_flags(flags));
3920 xo_stack_set_flags(xop);
3922 if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST)
3923 pre_nl = (xop->xo_flags & XOF_PRETTY) ? ",\n" : ", ";
3924 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST;
3926 rc = xo_printf(xop, "%s%*s{%s",
3927 pre_nl, xo_indent(xop), "", ppn);
3928 xo_depth_change(xop, name, 1, 1, xo_stack_flags(flags));
3933 xo_depth_change(xop, name, 1, 0, xo_stack_flags(flags));
3941 xo_open_instance_h (xo_handle_t *xop, const char *name)
3943 return xo_open_instance_hf(xop, 0, name);
3947 xo_open_instance (const char *name)
3949 return xo_open_instance_hf(NULL, 0, name);
3953 xo_open_instance_hd (xo_handle_t *xop, const char *name)
3955 return xo_open_instance_hf(xop, XOF_DTRT, name);
3959 xo_open_instance_d (const char *name)
3961 return xo_open_instance_hf(NULL, XOF_DTRT, name);
3965 xo_close_instance_h (xo_handle_t *xop, const char *name)
3967 xop = xo_default(xop);
3970 const char *ppn = (xop->xo_flags & XOF_PRETTY) ? "\n" : "";
3971 const char *pre_nl = "";
3974 xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth];
3975 if (!(xsp->xs_flags & XSF_DTRT))
3976 xo_failure(xop, "missing name without 'dtrt' mode");
3978 name = xsp->xs_name;
3980 int len = strlen(name) + 1;
3981 /* We need to make a local copy; xo_depth_change will free it */
3982 char *cp = alloca(len);
3983 memcpy(cp, name, len);
3986 name = XO_FAILURE_NAME;
3989 switch (xop->xo_style) {
3991 xo_depth_change(xop, name, -1, -1, 0);
3992 rc = xo_printf(xop, "%*s</%s>%s", xo_indent(xop), "", name, ppn);
3996 pre_nl = (xop->xo_flags & XOF_PRETTY) ? "\n" : "";
3998 xo_depth_change(xop, name, -1, -1, 0);
3999 rc = xo_printf(xop, "%s%*s}", pre_nl, xo_indent(xop), "");
4000 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST;
4005 xo_depth_change(xop, name, -1, 0, 0);
4013 xo_close_instance (const char *name)
4015 return xo_close_instance_h(NULL, name);
4019 xo_close_instance_hd (xo_handle_t *xop)
4021 return xo_close_instance_h(xop, NULL);
4025 xo_close_instance_d (void)
4027 return xo_close_instance_h(NULL, NULL);
4031 xo_set_writer (xo_handle_t *xop, void *opaque, xo_write_func_t write_func,
4032 xo_close_func_t close_func)
4034 xop = xo_default(xop);
4036 xop->xo_opaque = opaque;
4037 xop->xo_write = write_func;
4038 xop->xo_close = close_func;
4042 xo_set_allocator (xo_realloc_func_t realloc_func, xo_free_func_t free_func)
4044 xo_realloc = realloc_func;
4045 xo_free = free_func;
4049 xo_flush_h (xo_handle_t *xop)
4051 static char div_close[] = "</div>";
4053 xop = xo_default(xop);
4055 switch (xop->xo_style) {
4057 if (xop->xo_flags & XOF_DIV_OPEN) {
4058 xop->xo_flags &= ~XOF_DIV_OPEN;
4059 xo_data_append(xop, div_close, sizeof(div_close) - 1);
4061 if (xop->xo_flags & XOF_PRETTY)
4062 xo_data_append(xop, "\n", 1);
4077 xo_finish_h (xo_handle_t *xop)
4079 const char *cp = "";
4080 xop = xo_default(xop);
4082 switch (xop->xo_style) {
4084 if (!(xop->xo_flags & XOF_NO_TOP)) {
4085 if (xop->xo_flags & XOF_TOP_EMITTED)
4086 xop->xo_flags &= ~XOF_TOP_EMITTED; /* Turn off before output */
4089 xo_printf(xop, "%*s%s}\n",xo_indent(xop), "", cp);
4104 * Generate an error message, such as would be displayed on stderr
4107 xo_error_hv (xo_handle_t *xop, const char *fmt, va_list vap)
4109 xop = xo_default(xop);
4112 * If the format string doesn't end with a newline, we pop
4115 int len = strlen(fmt);
4116 if (len > 0 && fmt[len - 1] != '\n') {
4117 char *newfmt = alloca(len + 2);
4118 memcpy(newfmt, fmt, len);
4124 switch (xop->xo_style) {
4126 vfprintf(stderr, fmt, vap);
4130 va_copy(xop->xo_vap, vap);
4132 xo_buf_append_div(xop, "error", 0, NULL, 0, fmt, strlen(fmt), NULL, 0);
4134 if (xop->xo_flags & XOF_DIV_OPEN)
4139 va_end(xop->xo_vap);
4140 bzero(&xop->xo_vap, sizeof(xop->xo_vap));
4144 va_copy(xop->xo_vap, vap);
4146 xo_open_container_h(xop, "error");
4147 xo_format_value(xop, "message", 7, fmt, strlen(fmt), NULL, 0, 0);
4148 xo_close_container_h(xop, "error");
4150 va_end(xop->xo_vap);
4151 bzero(&xop->xo_vap, sizeof(xop->xo_vap));
4157 xo_error_h (xo_handle_t *xop, const char *fmt, ...)
4162 xo_error_hv(xop, fmt, vap);
4167 * Generate an error message, such as would be displayed on stderr
4170 xo_error (const char *fmt, ...)
4175 xo_error_hv(NULL, fmt, vap);
4180 xo_parse_args (int argc, char **argv)
4182 static char libxo_opt[] = "--libxo";
4186 /* Save our program name for xo_err and friends */
4187 xo_program = argv[0];
4188 cp = strrchr(xo_program, '/');
4190 xo_program = cp + 1;
4192 for (save = i = 1; i < argc; i++) {
4194 || strncmp(argv[i], libxo_opt, sizeof(libxo_opt) - 1) != 0) {
4196 argv[save] = argv[i];
4201 cp = argv[i] + sizeof(libxo_opt) - 1;
4205 xo_warnx("missing libxo option");
4209 if (xo_set_options(NULL, cp) < 0)
4211 } else if (*cp == ':') {
4212 if (xo_set_options(NULL, cp) < 0)
4215 } else if (*cp == '=') {
4216 if (xo_set_options(NULL, ++cp) < 0)
4219 } else if (*cp == '-') {
4221 if (strcmp(cp, "check") == 0) {
4225 xo_warnx("unknown libxo option: '%s'", argv[i]);
4229 xo_warnx("unknown libxo option: '%s'", argv[i]);
4240 main (int argc, char **argv)
4242 static char base_grocery[] = "GRO";
4243 static char base_hardware[] = "HRD";
4245 const char *i_title;
4249 const char *i_sku_base;
4252 struct item list[] = {
4253 { "gum&this&that", 1412, 54, 10, base_grocery, 415 },
4254 { "<rope>", 85, 4, 2, base_hardware, 212 },
4255 { "ladder", 0, 2, 1, base_hardware, 517 },
4256 { "\"bolt\"", 4123, 144, 42, base_hardware, 632 },
4257 { "water\\blue", 17, 14, 2, base_grocery, 2331 },
4258 { NULL, 0, 0, 0, NULL, 0 }
4260 struct item list2[] = {
4261 { "fish", 1321, 45, 1, base_grocery, 533 },
4262 { NULL, 0, 0, 0, NULL, 0 }
4265 xo_info_t info[] = {
4266 { "in-stock", "number", "Number of items in stock" },
4267 { "name", "string", "Name of the item" },
4268 { "on-order", "number", "Number of items on order" },
4269 { "sku", "string", "Stock Keeping Unit" },
4270 { "sold", "number", "Number of items sold" },
4271 { NULL, NULL, NULL },
4273 int info_count = (sizeof(info) / sizeof(info[0])) - 1;
4275 argc = xo_parse_args(argc, argv);
4279 xo_set_info(NULL, info, info_count);
4281 xo_open_container_h(NULL, "top");
4283 xo_open_container("data");
4284 xo_open_list("item");
4286 xo_emit("{T:Item/%-15s}{T:Total Sold/%12s}{T:In Stock/%12s}"
4287 "{T:On Order/%12s}{T:SKU/%5s}\n");
4289 for (ip = list; ip->i_title; ip++) {
4290 xo_open_instance("item");
4292 xo_emit("{k:name/%-15s/%s}{n:sold/%12u/%u}{:in-stock/%12u/%u}"
4293 "{:on-order/%12u/%u} {q:sku/%5s-000-%u/%s-000-%u}\n",
4294 ip->i_title, ip->i_sold, ip->i_instock, ip->i_onorder,
4295 ip->i_sku_base, ip->i_sku_num);
4297 xo_close_instance("item");
4300 xo_close_list("item");
4301 xo_close_container("data");
4305 xo_open_container("data");
4306 xo_open_list("item");
4308 for (ip = list; ip->i_title; ip++) {
4309 xo_open_instance("item");
4311 xo_attr("fancy", "%s%d", "item", ip - list);
4312 xo_emit("{L:Item} '{k:name/%s}':\n", ip->i_title);
4313 xo_emit("{P: }{L:Total sold}: {n:sold/%u%s}{e:percent/%u}\n",
4314 ip->i_sold, ip->i_sold ? ".0" : "", 44);
4315 xo_emit("{P: }{Lcw:In stock}{:in-stock/%u}\n", ip->i_instock);
4316 xo_emit("{P: }{Lcw:On order}{:on-order/%u}\n", ip->i_onorder);
4317 xo_emit("{P: }{L:SKU}: {q:sku/%s-000-%u}\n",
4318 ip->i_sku_base, ip->i_sku_num);
4320 xo_close_instance("item");
4323 xo_close_list("item");
4324 xo_close_container("data");
4326 xo_open_container("data");
4327 xo_open_list("item");
4329 for (ip = list2; ip->i_title; ip++) {
4330 xo_open_instance("item");
4332 xo_emit("{L:Item} '{k:name/%s}':\n", ip->i_title);
4333 xo_emit("{P: }{L:Total sold}: {n:sold/%u%s}\n",
4334 ip->i_sold, ip->i_sold ? ".0" : "");
4335 xo_emit("{P: }{Lcw:In stock}{:in-stock/%u}\n", ip->i_instock);
4336 xo_emit("{P: }{Lcw:On order}{:on-order/%u}\n", ip->i_onorder);
4337 xo_emit("{P: }{L:SKU}: {q:sku/%s-000-%u}\n",
4338 ip->i_sku_base, ip->i_sku_num);
4340 xo_open_list("month");
4342 const char *months[] = { "Jan", "Feb", "Mar", NULL };
4343 int discounts[] = { 10, 20, 25, 0 };
4345 for (i = 0; months[i]; i++) {
4346 xo_open_instance("month");
4348 "{Lwc:Month}{k:month}, {Lwc:Special}{:discount/%d}\n",
4349 months[i], discounts[i]);
4350 xo_close_instance("month");
4353 xo_close_list("month");
4355 xo_close_instance("item");
4358 xo_close_list("item");
4359 xo_close_container("data");
4361 xo_close_container_h(NULL, "top");
4367 #endif /* UNIT_TEST */