2 * Copyright (c) 2014-2015, 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
18 #include <sys/types.h>
29 #include "xoversion.h"
31 #ifdef HAVE_STDIO_EXT_H
32 #include <stdio_ext.h>
33 #endif /* HAVE_STDIO_EXT_H */
35 const char xo_version[] = LIBXO_VERSION;
36 const char xo_version_extra[] = LIBXO_VERSION_EXTRA;
39 #define UNUSED __attribute__ ((__unused__))
42 #define XO_INDENT_BY 2 /* Amount to indent when pretty printing */
43 #define XO_BUFSIZ (8*1024) /* Initial buffer size */
44 #define XO_DEPTH 512 /* Default stack depth */
45 #define XO_MAX_ANCHOR_WIDTH (8*1024) /* Anything wider is just sillyb */
47 #define XO_FAILURE_NAME "failure"
50 * xo_buffer_t: a memory buffer that can be grown as needed. We
51 * use them for building format strings and output data.
53 typedef struct xo_buffer_s {
54 char *xb_bufp; /* Buffer memory */
55 char *xb_curp; /* Current insertion point */
56 int xb_size; /* Size of buffer */
59 /* Flags for the stack frame */
60 typedef unsigned xo_xsf_flags_t; /* XSF_* flags */
61 #define XSF_NOT_FIRST (1<<0) /* Not the first element */
62 #define XSF_LIST (1<<1) /* Frame is a list */
63 #define XSF_INSTANCE (1<<2) /* Frame is an instance */
64 #define XSF_DTRT (1<<3) /* Save the name for DTRT mode */
66 #define XSF_CONTENT (1<<4) /* Some content has been emitted */
67 #define XSF_EMIT (1<<5) /* Some field has been emitted */
68 #define XSF_EMIT_KEY (1<<6) /* A key has been emitted */
69 #define XSF_EMIT_LEAF_LIST (1<<7) /* A leaf-list field has been emitted */
71 /* These are the flags we propagate between markers and their parents */
72 #define XSF_MARKER_FLAGS \
73 (XSF_NOT_FIRST | XSF_CONTENT | XSF_EMIT | XSF_EMIT_KEY | XSF_EMIT_LEAF_LIST )
76 * A word about states: We're moving to a finite state machine (FMS)
77 * approach to help remove fragility from the caller's code. Instead
78 * of requiring a specific order of calls, we'll allow the caller more
79 * flexibility and make the library responsible for recovering from
80 * missed steps. The goal is that the library should not be capable of
81 * emitting invalid xml or json, but the developer shouldn't need
82 * to know or understand all the details about these encodings.
84 * You can think of states as either states or event, since they
85 * function rather like both. None of the XO_CLOSE_* events will
86 * persist as states, since their stack frame will be popped.
87 * Same is true of XSS_EMIT, which is an event that asks us to
88 * prep for emitting output fields.
91 /* Stack frame states */
92 typedef unsigned xo_state_t;
93 #define XSS_INIT 0 /* Initial stack state */
94 #define XSS_OPEN_CONTAINER 1
95 #define XSS_CLOSE_CONTAINER 2
96 #define XSS_OPEN_LIST 3
97 #define XSS_CLOSE_LIST 4
98 #define XSS_OPEN_INSTANCE 5
99 #define XSS_CLOSE_INSTANCE 6
100 #define XSS_OPEN_LEAF_LIST 7
101 #define XSS_CLOSE_LEAF_LIST 8
102 #define XSS_DISCARDING 9 /* Discarding data until recovered */
103 #define XSS_MARKER 10 /* xo_open_marker's marker */
104 #define XSS_EMIT 11 /* xo_emit has a leaf field */
105 #define XSS_EMIT_LEAF_LIST 12 /* xo_emit has a leaf-list ({l:}) */
106 #define XSS_FINISH 13 /* xo_finish was called */
110 #define XSS_TRANSITION(_old, _new) ((_old) << 8 | (_new))
113 * xo_stack_t: As we open and close containers and levels, we
114 * create a stack of frames to track them. This is needed for
115 * XOF_WARN and XOF_XPATH.
117 typedef struct xo_stack_s {
118 xo_xsf_flags_t xs_flags; /* Flags for this frame */
119 xo_state_t xs_state; /* State for this stack frame */
120 char *xs_name; /* Name (for XPath value) */
121 char *xs_keys; /* XPath predicate for any key fields */
124 /* "colors" refers to fancy ansi codes */
125 #define XO_COL_DEFAULT 0
126 #define XO_COL_BLACK 1
128 #define XO_COL_GREEN 3
129 #define XO_COL_YELLOW 4
130 #define XO_COL_BLUE 5
131 #define XO_COL_MAGENTA 6
132 #define XO_COL_CYAN 7
133 #define XO_COL_WHITE 8
135 #define XO_NUM_COLORS 9
137 /* "effects" refers to fancy ansi codes */
139 * Yes, there's no blink. We're civilized. We like users. Blink
140 * isn't something one does to someone you like. Friends don't let
141 * friends use blink. On friends. You know what I mean. Blink is
142 * like, well, it's like bursting into show tunes at a funeral. It's
143 * just not done. Not something anyone wants. And on those rare
144 * instances where it might actually be appropriate, it's still wrong.
145 * It's likely done my the wrong person for the wrong reason. Just
146 * like blink. And if I implemented blink, I'd be like a funeral
147 * director who adds "Would you like us to burst into show tunes?" on
148 * the list of questions asking while making funeral arrangements.
149 * It's formalizing wrongness in the wrong way. And we're just too
150 * civilized to do that. Hhhmph!
152 #define XO_EFF_RESET (1<<0)
153 #define XO_EFF_NORMAL (1<<1)
154 #define XO_EFF_BOLD (1<<2)
155 #define XO_EFF_UNDERLINE (1<<3)
156 #define XO_EFF_INVERSE (1<<4)
158 #define XO_EFF_CLEAR_BITS XO_EFF_RESET
160 typedef uint8_t xo_effect_t;
161 typedef uint8_t xo_color_t;
162 typedef struct xo_colors_s {
163 xo_effect_t xoc_effects; /* Current effect set */
164 xo_color_t xoc_col_fg; /* Foreground color */
165 xo_color_t xoc_col_bg; /* Background color */
169 * xo_handle_t: this is the principle data structure for libxo.
170 * It's used as a store for state, options, and content.
173 xo_xof_flags_t xo_flags; /* Flags */
174 unsigned short xo_style; /* XO_STYLE_* value */
175 unsigned short xo_indent; /* Indent level (if pretty) */
176 unsigned short xo_indent_by; /* Indent amount (tab stop) */
177 xo_write_func_t xo_write; /* Write callback */
178 xo_close_func_t xo_close; /* Close callback */
179 xo_flush_func_t xo_flush; /* Flush callback */
180 xo_formatter_t xo_formatter; /* Custom formating function */
181 xo_checkpointer_t xo_checkpointer; /* Custom formating support function */
182 void *xo_opaque; /* Opaque data for write function */
183 xo_buffer_t xo_data; /* Output data */
184 xo_buffer_t xo_fmt; /* Work area for building format strings */
185 xo_buffer_t xo_attrs; /* Work area for building XML attributes */
186 xo_buffer_t xo_predicate; /* Work area for building XPath predicates */
187 xo_stack_t *xo_stack; /* Stack pointer */
188 int xo_depth; /* Depth of stack */
189 int xo_stack_size; /* Size of the stack */
190 xo_info_t *xo_info; /* Info fields for all elements */
191 int xo_info_count; /* Number of info entries */
192 va_list xo_vap; /* Variable arguments (stdargs) */
193 char *xo_leading_xpath; /* A leading XPath expression */
194 mbstate_t xo_mbstate; /* Multi-byte character conversion state */
195 unsigned xo_anchor_offset; /* Start of anchored text */
196 unsigned xo_anchor_columns; /* Number of columns since the start anchor */
197 int xo_anchor_min_width; /* Desired width of anchored text */
198 unsigned xo_units_offset; /* Start of units insertion point */
199 unsigned xo_columns; /* Columns emitted during this xo_emit call */
200 uint8_t xo_color_map_fg[XO_NUM_COLORS]; /* Foreground color mappings */
201 uint8_t xo_color_map_bg[XO_NUM_COLORS]; /* Background color mappings */
202 xo_colors_t xo_colors; /* Current color and effect values */
203 xo_buffer_t xo_color_buf; /* HTML: buffer of colors and effects */
204 char *xo_version; /* Version string */
207 /* Flags for formatting functions */
208 typedef unsigned long xo_xff_flags_t;
209 #define XFF_COLON (1<<0) /* Append a ":" */
210 #define XFF_COMMA (1<<1) /* Append a "," iff there's more output */
211 #define XFF_WS (1<<2) /* Append a blank */
212 #define XFF_ENCODE_ONLY (1<<3) /* Only emit for encoding formats (xml, json) */
214 #define XFF_QUOTE (1<<4) /* Force quotes */
215 #define XFF_NOQUOTE (1<<5) /* Force no quotes */
216 #define XFF_DISPLAY_ONLY (1<<6) /* Only emit for display formats (text and html) */
217 #define XFF_KEY (1<<7) /* Field is a key (for XPath) */
219 #define XFF_XML (1<<8) /* Force XML encoding style (for XPath) */
220 #define XFF_ATTR (1<<9) /* Escape value using attribute rules (XML) */
221 #define XFF_BLANK_LINE (1<<10) /* Emit a blank line */
222 #define XFF_NO_OUTPUT (1<<11) /* Do not make any output */
224 #define XFF_TRIM_WS (1<<12) /* Trim whitespace off encoded values */
225 #define XFF_LEAF_LIST (1<<13) /* A leaf-list (list of values) */
226 #define XFF_UNESCAPE (1<<14) /* Need to printf-style unescape the value */
229 * Normal printf has width and precision, which for strings operate as
230 * min and max number of columns. But this depends on the idea that
231 * one byte means one column, which UTF-8 and multi-byte characters
232 * pitches on its ear. It may take 40 bytes of data to populate 14
233 * columns, but we can't go off looking at 40 bytes of data without the
234 * caller's permission for fear/knowledge that we'll generate core files.
236 * So we make three values, distinguishing between "max column" and
237 * "number of bytes that we will inspect inspect safely" We call the
238 * later "size", and make the format "%[[<min>].[[<size>].<max>]]s".
240 * Under the "first do no harm" theory, we default "max" to "size".
241 * This is a reasonable assumption for folks that don't grok the
242 * MBS/WCS/UTF-8 world, and while it will be annoying, it will never
245 * For example, xo_emit("{:tag/%-14.14s}", buf) will make 14
246 * columns of output, but will never look at more than 14 bytes of the
247 * input buffer. This is mostly compatible with printf and caller's
250 * In contrast xo_emit("{:tag/%-14..14s}", buf) will look at however
251 * many bytes (or until a NUL is seen) are needed to fill 14 columns
252 * of output. xo_emit("{:tag/%-14.*.14s}", xx, buf) will look at up
253 * to xx bytes (or until a NUL is seen) in order to fill 14 columns
256 * It's fairly amazing how a good idea (handle all languages of the
257 * world) blows such a big hole in the bottom of the fairly weak boat
258 * that is C string handling. The simplicity and completenesss are
259 * sunk in ways we haven't even begun to understand.
262 #define XF_WIDTH_MIN 0 /* Minimal width */
263 #define XF_WIDTH_SIZE 1 /* Maximum number of bytes to examine */
264 #define XF_WIDTH_MAX 2 /* Maximum width */
265 #define XF_WIDTH_NUM 3 /* Numeric fields in printf (min.size.max) */
267 /* Input and output string encodings */
268 #define XF_ENC_WIDE 1 /* Wide characters (wchar_t) */
269 #define XF_ENC_UTF8 2 /* UTF-8 */
270 #define XF_ENC_LOCALE 3 /* Current locale */
273 * A place to parse printf-style format flags for each field
275 typedef struct xo_format_s {
276 unsigned char xf_fc; /* Format character */
277 unsigned char xf_enc; /* Encoding of the string (XF_ENC_*) */
278 unsigned char xf_skip; /* Skip this field */
279 unsigned char xf_lflag; /* 'l' (long) */
280 unsigned char xf_hflag;; /* 'h' (half) */
281 unsigned char xf_jflag; /* 'j' (intmax_t) */
282 unsigned char xf_tflag; /* 't' (ptrdiff_t) */
283 unsigned char xf_zflag; /* 'z' (size_t) */
284 unsigned char xf_qflag; /* 'q' (quad_t) */
285 unsigned char xf_seen_minus; /* Seen a minus */
286 int xf_leading_zero; /* Seen a leading zero (zero fill) */
287 unsigned xf_dots; /* Seen one or more '.'s */
288 int xf_width[XF_WIDTH_NUM]; /* Width/precision/size numeric fields */
289 unsigned xf_stars; /* Seen one or more '*'s */
290 unsigned char xf_star[XF_WIDTH_NUM]; /* Seen one or more '*'s */
294 * We keep a default handle to allow callers to avoid having to
295 * allocate one. Passing NULL to any of our functions will use
296 * this default handle.
298 static xo_handle_t xo_default_handle;
299 static int xo_default_inited;
300 static int xo_locale_inited;
301 static const char *xo_program;
304 * To allow libxo to be used in diverse environment, we allow the
305 * caller to give callbacks for memory allocation.
307 static xo_realloc_func_t xo_realloc = realloc;
308 static xo_free_func_t xo_free = free;
310 /* Forward declarations */
312 xo_failure (xo_handle_t *xop, const char *fmt, ...);
315 xo_transition (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name,
316 xo_state_t new_state);
319 xo_buf_append_div (xo_handle_t *xop, const char *class, xo_xff_flags_t flags,
320 const char *name, int nlen,
321 const char *value, int vlen,
322 const char *encoding, int elen);
325 xo_anchor_clear (xo_handle_t *xop);
328 * xo_style is used to retrieve the current style. When we're built
329 * for "text only" mode, we use this function to drive the removal
330 * of most of the code in libxo. We return a constant and the compiler
331 * happily removes the non-text code that is not longer executed. This
332 * trims our code nicely without needing to trampel perfectly readable
335 static inline unsigned short
336 xo_style (xo_handle_t *xop UNUSED)
338 #ifdef LIBXO_TEXT_ONLY
339 return XO_STYLE_TEXT;
340 #else /* LIBXO_TEXT_ONLY */
341 return xop->xo_style;
342 #endif /* LIBXO_TEXT_ONLY */
346 * Callback to write data to a FILE pointer
349 xo_write_to_file (void *opaque, const char *data)
351 FILE *fp = (FILE *) opaque;
353 return fprintf(fp, "%s", data);
357 * Callback to close a file
360 xo_close_file (void *opaque)
362 FILE *fp = (FILE *) opaque;
368 * Callback to flush a FILE pointer
371 xo_flush_file (void *opaque)
373 FILE *fp = (FILE *) opaque;
379 * Initialize the contents of an xo_buffer_t.
382 xo_buf_init (xo_buffer_t *xbp)
384 xbp->xb_size = XO_BUFSIZ;
385 xbp->xb_bufp = xo_realloc(NULL, xbp->xb_size);
386 xbp->xb_curp = xbp->xb_bufp;
390 * Reset the buffer to empty
393 xo_buf_reset (xo_buffer_t *xbp)
395 xbp->xb_curp = xbp->xb_bufp;
399 * Reset the buffer to empty
402 xo_buf_is_empty (xo_buffer_t *xbp)
404 return (xbp->xb_curp == xbp->xb_bufp);
408 * Initialize the contents of an xo_buffer_t.
411 xo_buf_cleanup (xo_buffer_t *xbp)
414 xo_free(xbp->xb_bufp);
415 bzero(xbp, sizeof(*xbp));
419 xo_depth_check (xo_handle_t *xop, int depth)
423 if (depth >= xop->xo_stack_size) {
425 xsp = xo_realloc(xop->xo_stack, sizeof(xop->xo_stack[0]) * depth);
427 xo_failure(xop, "xo_depth_check: out of memory (%d)", depth);
431 int count = depth - xop->xo_stack_size;
433 bzero(xsp + xop->xo_stack_size, count * sizeof(*xsp));
434 xop->xo_stack_size = depth;
442 xo_no_setlocale (void)
444 xo_locale_inited = 1; /* Skip initialization */
448 * We need to decide if stdout is line buffered (_IOLBF). Lacking a
449 * standard way to decide this (e.g. getlinebuf()), we have configure
450 * look to find __flbf, which glibc supported. If not, we'll rely on
451 * isatty, with the assumption that terminals are the only thing
452 * that's line buffered. We _could_ test for "steam._flags & _IOLBF",
453 * which is all __flbf does, but that's even tackier. Like a
454 * bedazzled Elvis outfit on an ugly lap dog sort of tacky. Not
455 * something we're willing to do.
458 xo_is_line_buffered (FILE *stream)
463 #else /* HAVE___FLBF */
464 if (isatty(fileno(stream)))
466 #endif /* HAVE___FLBF */
471 * Initialize an xo_handle_t, using both static defaults and
472 * the global settings from the LIBXO_OPTIONS environment
476 xo_init_handle (xo_handle_t *xop)
478 xop->xo_opaque = stdout;
479 xop->xo_write = xo_write_to_file;
480 xop->xo_flush = xo_flush_file;
482 if (xo_is_line_buffered(stdout))
483 xop->xo_flags |= XOF_FLUSH_LINE;
486 * We only want to do color output on terminals, but we only want
487 * to do this if the user has asked for color.
489 if ((xop->xo_flags & XOF_COLOR_ALLOWED) && isatty(1))
490 xop->xo_flags |= XOF_COLOR;
493 * We need to initialize the locale, which isn't really pretty.
494 * Libraries should depend on their caller to set up the
495 * environment. But we really can't count on the caller to do
496 * this, because well, they won't. Trust me.
498 if (!xo_locale_inited) {
499 xo_locale_inited = 1; /* Only do this once */
501 const char *cp = getenv("LC_CTYPE");
505 cp = getenv("LC_ALL");
507 cp = "UTF-8"; /* Optimistic? */
508 (void) setlocale(LC_CTYPE, cp);
512 * Initialize only the xo_buffers we know we'll need; the others
513 * can be allocated as needed.
515 xo_buf_init(&xop->xo_data);
516 xo_buf_init(&xop->xo_fmt);
518 xop->xo_indent_by = XO_INDENT_BY;
519 xo_depth_check(xop, XO_DEPTH);
521 #if !defined(NO_LIBXO_OPTIONS)
522 if (!(xop->xo_flags & XOF_NO_ENV)) {
523 char *env = getenv("LIBXO_OPTIONS");
525 xo_set_options(xop, env);
527 #endif /* NO_GETENV */
531 * Initialize the default handle.
534 xo_default_init (void)
536 xo_handle_t *xop = &xo_default_handle;
540 xo_default_inited = 1;
544 * Does the buffer have room for the given number of bytes of data?
545 * If not, realloc the buffer to make room. If that fails, we
546 * return 0 to tell the caller they are in trouble.
549 xo_buf_has_room (xo_buffer_t *xbp, int len)
551 if (xbp->xb_curp + len >= xbp->xb_bufp + xbp->xb_size) {
552 int sz = xbp->xb_size + XO_BUFSIZ;
553 char *bp = xo_realloc(xbp->xb_bufp, sz);
556 * XXX If we wanted to put a stick XOF_ENOMEM on xop,
557 * this would be the place to do it. But we'd need
558 * to churn the code to pass xop in here....
563 xbp->xb_curp = bp + (xbp->xb_curp - xbp->xb_bufp);
572 * Cheap convenience function to return either the argument, or
573 * the internal handle, after it has been initialized. The usage
575 * xop = xo_default(xop);
578 xo_default (xo_handle_t *xop)
581 if (xo_default_inited == 0)
583 xop = &xo_default_handle;
590 * Return the number of spaces we should be indenting. If
591 * we are pretty-printing, this is indent * indent_by.
594 xo_indent (xo_handle_t *xop)
598 xop = xo_default(xop);
600 if (xop->xo_flags & XOF_PRETTY) {
601 rc = xop->xo_indent * xop->xo_indent_by;
602 if (xop->xo_flags & XOF_TOP_EMITTED)
603 rc += xop->xo_indent_by;
606 return (rc > 0) ? rc : 0;
610 xo_buf_indent (xo_handle_t *xop, int indent)
612 xo_buffer_t *xbp = &xop->xo_data;
615 indent = xo_indent(xop);
617 if (!xo_buf_has_room(xbp, indent))
620 memset(xbp->xb_curp, ' ', indent);
621 xbp->xb_curp += indent;
624 static char xo_xml_amp[] = "&";
625 static char xo_xml_lt[] = "<";
626 static char xo_xml_gt[] = ">";
627 static char xo_xml_quot[] = """;
630 xo_escape_xml (xo_buffer_t *xbp, int len, int attr)
637 for (cp = xbp->xb_curp, ep = cp + len; cp < ep; cp++) {
638 /* We're subtracting 2: 1 for the NUL, 1 for the char we replace */
640 delta += sizeof(xo_xml_lt) - 2;
642 delta += sizeof(xo_xml_gt) - 2;
644 delta += sizeof(xo_xml_amp) - 2;
645 else if (attr && *cp == '"')
646 delta += sizeof(xo_xml_quot) - 2;
649 if (delta == 0) /* Nothing to escape; bail */
652 if (!xo_buf_has_room(xbp, delta)) /* No room; bail, but don't append */
668 else if (attr && *cp == '"')
677 memcpy(ip, sp, slen);
679 } while (cp > ep && cp != ip);
685 xo_escape_json (xo_buffer_t *xbp, int len)
690 for (cp = xbp->xb_curp, ep = cp + len; cp < ep; cp++) {
691 if (*cp == '\\' || *cp == '"')
693 else if (*cp == '\n' || *cp == '\r')
697 if (delta == 0) /* Nothing to escape; bail */
700 if (!xo_buf_has_room(xbp, delta)) /* No room; bail, but don't append */
710 if (*cp == '\\' || *cp == '"') {
713 } else if (*cp == '\n') {
716 } else if (*cp == '\r') {
723 } while (cp > ep && cp != ip);
729 * Append the given string to the given buffer
732 xo_buf_append (xo_buffer_t *xbp, const char *str, int len)
734 if (!xo_buf_has_room(xbp, len))
737 memcpy(xbp->xb_curp, str, len);
742 * Append the given NUL-terminated string to the given buffer
745 xo_buf_append_str (xo_buffer_t *xbp, const char *str)
747 int len = strlen(str);
749 if (!xo_buf_has_room(xbp, len))
752 memcpy(xbp->xb_curp, str, len);
757 xo_buf_escape (xo_handle_t *xop, xo_buffer_t *xbp,
758 const char *str, int len, xo_xff_flags_t flags)
760 if (!xo_buf_has_room(xbp, len))
763 memcpy(xbp->xb_curp, str, len);
765 switch (xo_style(xop)) {
768 len = xo_escape_xml(xbp, len, (flags & XFF_ATTR));
772 len = xo_escape_json(xbp, len);
780 * Write the current contents of the data buffer using the handle's
784 xo_write (xo_handle_t *xop)
787 xo_buffer_t *xbp = &xop->xo_data;
789 if (xbp->xb_curp != xbp->xb_bufp) {
790 xo_buf_append(xbp, "", 1); /* Append ending NUL */
791 xo_anchor_clear(xop);
792 rc = xop->xo_write(xop->xo_opaque, xbp->xb_bufp);
793 xbp->xb_curp = xbp->xb_bufp;
796 /* Turn off the flags that don't survive across writes */
797 xop->xo_flags &= ~(XOF_UNITS_PENDING);
803 * Format arguments into our buffer. If a custom formatter has been set,
804 * we use that to do the work; otherwise we vsnprintf().
807 xo_vsnprintf (xo_handle_t *xop, xo_buffer_t *xbp, const char *fmt, va_list vap)
811 int left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp);
813 va_copy(va_local, vap);
815 if (xop->xo_formatter)
816 rc = xop->xo_formatter(xop, xbp->xb_curp, left, fmt, va_local);
818 rc = vsnprintf(xbp->xb_curp, left, fmt, va_local);
821 if (!xo_buf_has_room(xbp, rc)) {
827 * After we call vsnprintf(), the stage of vap is not defined.
828 * We need to copy it before we pass. Then we have to do our
829 * own logic below to move it along. This is because the
830 * implementation can have va_list be a pointer (bsd) or a
831 * structure (macosx) or anything in between.
834 va_end(va_local); /* Reset vap to the start */
835 va_copy(va_local, vap);
837 left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp);
838 if (xop->xo_formatter)
839 rc = xop->xo_formatter(xop, xbp->xb_curp, left, fmt, va_local);
841 rc = vsnprintf(xbp->xb_curp, left, fmt, va_local);
849 * Print some data thru the handle.
852 xo_printf_v (xo_handle_t *xop, const char *fmt, va_list vap)
854 xo_buffer_t *xbp = &xop->xo_data;
855 int left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp);
859 va_copy(va_local, vap);
861 rc = vsnprintf(xbp->xb_curp, left, fmt, va_local);
863 if (rc > xbp->xb_size) {
864 if (!xo_buf_has_room(xbp, rc)) {
869 va_end(va_local); /* Reset vap to the start */
870 va_copy(va_local, vap);
872 left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp);
873 rc = vsnprintf(xbp->xb_curp, left, fmt, va_local);
885 xo_printf (xo_handle_t *xop, const char *fmt, ...)
892 rc = xo_printf_v(xop, fmt, vap);
899 * These next few function are make The Essential UTF-8 Ginsu Knife.
900 * Identify an input and output character, and convert it.
902 static int xo_utf8_bits[7] = { 0, 0x7f, 0x1f, 0x0f, 0x07, 0x03, 0x01 };
911 xo_utf8_to_wc_len (const char *buf)
913 unsigned b = (unsigned char) *buf;
916 if ((b & 0x80) == 0x0)
918 else if ((b & 0xe0) == 0xc0)
920 else if ((b & 0xf0) == 0xe0)
922 else if ((b & 0xf8) == 0xf0)
924 else if ((b & 0xfc) == 0xf8)
926 else if ((b & 0xfe) == 0xfc)
935 xo_buf_utf8_len (xo_handle_t *xop, const char *buf, int bufsiz)
938 unsigned b = (unsigned char) *buf;
941 len = xo_utf8_to_wc_len(buf);
943 xo_failure(xop, "invalid UTF-8 data: %02hhx", b);
948 xo_failure(xop, "invalid UTF-8 data (short): %02hhx (%d/%d)",
953 for (i = 2; i < len; i++) {
954 b = (unsigned char ) buf[i];
955 if ((b & 0xc0) != 0x80) {
956 xo_failure(xop, "invalid UTF-8 data (byte %d): %x", i, b);
965 * Build a wide character from the input buffer; the number of
966 * bits we pull off the first character is dependent on the length,
967 * but we put 6 bits off all other bytes.
970 xo_utf8_char (const char *buf, int len)
974 const unsigned char *cp = (const unsigned char *) buf;
976 wc = *cp & xo_utf8_bits[len];
977 for (i = 1; i < len; i++) {
980 if ((cp[i] & 0xc0) != 0x80)
988 * Determine the number of bytes needed to encode a wide character.
991 xo_utf8_emit_len (wchar_t wc)
995 if ((wc & ((1<<7) - 1)) == wc) /* Simple case */
997 else if ((wc & ((1<<11) - 1)) == wc)
999 else if ((wc & ((1<<16) - 1)) == wc)
1001 else if ((wc & ((1<<21) - 1)) == wc)
1003 else if ((wc & ((1<<26) - 1)) == wc)
1012 xo_utf8_emit_char (char *buf, int len, wchar_t wc)
1016 if (len == 1) { /* Simple case */
1021 for (i = len - 1; i >= 0; i--) {
1022 buf[i] = 0x80 | (wc & 0x3f);
1026 buf[0] &= xo_utf8_bits[len];
1027 buf[0] |= ~xo_utf8_bits[len] << 1;
1031 xo_buf_append_locale_from_utf8 (xo_handle_t *xop, xo_buffer_t *xbp,
1032 const char *ibuf, int ilen)
1038 * Build our wide character from the input buffer; the number of
1039 * bits we pull off the first character is dependent on the length,
1040 * but we put 6 bits off all other bytes.
1042 wc = xo_utf8_char(ibuf, ilen);
1043 if (wc == (wchar_t) -1) {
1044 xo_failure(xop, "invalid utf-8 byte sequence");
1048 if (xop->xo_flags & XOF_NO_LOCALE) {
1049 if (!xo_buf_has_room(xbp, ilen))
1052 memcpy(xbp->xb_curp, ibuf, ilen);
1053 xbp->xb_curp += ilen;
1056 if (!xo_buf_has_room(xbp, MB_LEN_MAX + 1))
1059 bzero(&xop->xo_mbstate, sizeof(xop->xo_mbstate));
1060 len = wcrtomb(xbp->xb_curp, wc, &xop->xo_mbstate);
1063 xo_failure(xop, "could not convert wide char: %lx",
1064 (unsigned long) wc);
1067 xbp->xb_curp += len;
1074 xo_buf_append_locale (xo_handle_t *xop, xo_buffer_t *xbp,
1075 const char *cp, int len)
1077 const char *sp = cp, *ep = cp + len;
1078 unsigned save_off = xbp->xb_bufp - xbp->xb_curp;
1082 for ( ; cp < ep; cp++) {
1083 if (!xo_is_utf8(*cp)) {
1089 * We're looking at a non-ascii UTF-8 character.
1090 * First we copy the previous data.
1091 * Then we need find the length and validate it.
1092 * Then we turn it into a wide string.
1093 * Then we turn it into a localized string.
1094 * Then we repeat. Isn't i18n fun?
1097 xo_buf_append(xbp, sp, cp - sp); /* Append previous data */
1099 slen = xo_buf_utf8_len(xop, cp, ep - cp);
1101 /* Bad data; back it all out */
1102 xbp->xb_curp = xbp->xb_bufp + save_off;
1106 cols += xo_buf_append_locale_from_utf8(xop, xbp, cp, slen);
1108 /* Next time thru, we'll start at the next character */
1113 /* Update column values */
1114 if (xop->xo_flags & XOF_COLUMNS)
1115 xop->xo_columns += cols;
1116 if (xop->xo_flags & XOF_ANCHOR)
1117 xop->xo_anchor_columns += cols;
1119 /* Before we fall into the basic logic below, we need reset len */
1121 if (len != 0) /* Append trailing data */
1122 xo_buf_append(xbp, sp, len);
1126 * Append the given string to the given buffer
1129 xo_data_append (xo_handle_t *xop, const char *str, int len)
1131 xo_buf_append(&xop->xo_data, str, len);
1135 * Append the given string to the given buffer
1138 xo_data_escape (xo_handle_t *xop, const char *str, int len)
1140 xo_buf_escape(xop, &xop->xo_data, str, len, 0);
1144 * Generate a warning. Normally, this is a text message written to
1145 * standard error. If the XOF_WARN_XML flag is set, then we generate
1146 * XMLified content on standard output.
1149 xo_warn_hcv (xo_handle_t *xop, int code, int check_warn,
1150 const char *fmt, va_list vap)
1152 xop = xo_default(xop);
1153 if (check_warn && !(xop->xo_flags & XOF_WARN))
1159 int len = strlen(fmt);
1160 int plen = xo_program ? strlen(xo_program) : 0;
1161 char *newfmt = alloca(len + 1 + plen + 2); /* NUL, and ": " */
1164 memcpy(newfmt, xo_program, plen);
1165 newfmt[plen++] = ':';
1166 newfmt[plen++] = ' ';
1168 memcpy(newfmt + plen, fmt, len);
1169 newfmt[len + plen] = '\0';
1171 if (xop->xo_flags & XOF_WARN_XML) {
1172 static char err_open[] = "<error>";
1173 static char err_close[] = "</error>";
1174 static char msg_open[] = "<message>";
1175 static char msg_close[] = "</message>";
1177 xo_buffer_t *xbp = &xop->xo_data;
1179 xo_buf_append(xbp, err_open, sizeof(err_open) - 1);
1180 xo_buf_append(xbp, msg_open, sizeof(msg_open) - 1);
1183 va_copy(va_local, vap);
1185 int left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp);
1186 int rc = vsnprintf(xbp->xb_curp, left, newfmt, vap);
1187 if (rc > xbp->xb_size) {
1188 if (!xo_buf_has_room(xbp, rc)) {
1193 va_end(vap); /* Reset vap to the start */
1194 va_copy(vap, va_local);
1196 left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp);
1197 rc = vsnprintf(xbp->xb_curp, left, fmt, vap);
1201 rc = xo_escape_xml(xbp, rc, 1);
1204 xo_buf_append(xbp, msg_close, sizeof(msg_close) - 1);
1205 xo_buf_append(xbp, err_close, sizeof(err_close) - 1);
1208 const char *msg = strerror(code);
1210 xo_buf_append(xbp, ": ", 2);
1211 xo_buf_append(xbp, msg, strlen(msg));
1215 xo_buf_append(xbp, "\n", 2); /* Append newline and NUL to string */
1216 (void) xo_write(xop);
1219 vfprintf(stderr, newfmt, vap);
1221 const char *msg = strerror(code);
1223 fprintf(stderr, ": %s", msg);
1225 fprintf(stderr, "\n");
1230 xo_warn_hc (xo_handle_t *xop, int code, const char *fmt, ...)
1235 xo_warn_hcv(xop, code, 0, fmt, vap);
1240 xo_warn_c (int code, const char *fmt, ...)
1245 xo_warn_hcv(NULL, code, 0, fmt, vap);
1250 xo_warn (const char *fmt, ...)
1256 xo_warn_hcv(NULL, code, 0, fmt, vap);
1261 xo_warnx (const char *fmt, ...)
1266 xo_warn_hcv(NULL, -1, 0, fmt, vap);
1271 xo_err (int eval, const char *fmt, ...)
1277 xo_warn_hcv(NULL, code, 0, fmt, vap);
1284 xo_errx (int eval, const char *fmt, ...)
1289 xo_warn_hcv(NULL, -1, 0, fmt, vap);
1296 xo_errc (int eval, int code, const char *fmt, ...)
1301 xo_warn_hcv(NULL, code, 0, fmt, vap);
1308 * Generate a warning. Normally, this is a text message written to
1309 * standard error. If the XOF_WARN_XML flag is set, then we generate
1310 * XMLified content on standard output.
1313 xo_message_hcv (xo_handle_t *xop, int code, const char *fmt, va_list vap)
1315 static char msg_open[] = "<message>";
1316 static char msg_close[] = "</message>";
1321 xop = xo_default(xop);
1323 if (fmt == NULL || *fmt == '\0')
1326 int need_nl = (fmt[strlen(fmt) - 1] != '\n');
1328 switch (xo_style(xop)) {
1330 xbp = &xop->xo_data;
1331 if (xop->xo_flags & XOF_PRETTY)
1332 xo_buf_indent(xop, xop->xo_indent_by);
1333 xo_buf_append(xbp, msg_open, sizeof(msg_open) - 1);
1335 va_copy(va_local, vap);
1337 int left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp);
1338 rc = vsnprintf(xbp->xb_curp, left, fmt, vap);
1339 if (rc > xbp->xb_size) {
1340 if (!xo_buf_has_room(xbp, rc)) {
1345 va_end(vap); /* Reset vap to the start */
1346 va_copy(vap, va_local);
1348 left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp);
1349 rc = vsnprintf(xbp->xb_curp, left, fmt, vap);
1353 rc = xo_escape_xml(xbp, rc, 1);
1356 if (need_nl && code > 0) {
1357 const char *msg = strerror(code);
1359 xo_buf_append(xbp, ": ", 2);
1360 xo_buf_append(xbp, msg, strlen(msg));
1364 xo_buf_append(xbp, msg_close, sizeof(msg_close) - 1);
1366 xo_buf_append(xbp, "\n", 2); /* Append newline and NUL to string */
1367 (void) xo_write(xop);
1372 char buf[BUFSIZ], *bp = buf, *cp;
1373 int bufsiz = sizeof(buf);
1376 va_copy(va_local, vap);
1378 rc = vsnprintf(bp, bufsiz, fmt, va_local);
1380 bufsiz = rc + BUFSIZ;
1381 bp = alloca(bufsiz);
1383 va_copy(va_local, vap);
1384 rc = vsnprintf(bp, bufsiz, fmt, va_local);
1390 rc2 = snprintf(cp, bufsiz - rc, "%s%s\n",
1391 (code > 0) ? ": " : "",
1392 (code > 0) ? strerror(code) : "");
1397 xo_buf_append_div(xop, "message", 0, NULL, 0, bp, rc, NULL, 0);
1402 /* No meanings of representing messages in JSON */
1406 rc = xo_printf_v(xop, fmt, vap);
1408 * XXX need to handle UTF-8 widths
1411 if (xop->xo_flags & XOF_COLUMNS)
1412 xop->xo_columns += rc;
1413 if (xop->xo_flags & XOF_ANCHOR)
1414 xop->xo_anchor_columns += rc;
1417 if (need_nl && code > 0) {
1418 const char *msg = strerror(code);
1420 xo_printf(xop, ": %s", msg);
1424 xo_printf(xop, "\n");
1429 (void) xo_flush_h(xop);
1433 xo_message_hc (xo_handle_t *xop, int code, const char *fmt, ...)
1438 xo_message_hcv(xop, code, fmt, vap);
1443 xo_message_c (int code, const char *fmt, ...)
1448 xo_message_hcv(NULL, code, fmt, vap);
1453 xo_message (const char *fmt, ...)
1459 xo_message_hcv(NULL, code, fmt, vap);
1464 xo_failure (xo_handle_t *xop, const char *fmt, ...)
1466 if (!(xop->xo_flags & XOF_WARN))
1472 xo_warn_hcv(xop, -1, 1, fmt, vap);
1477 * Create a handle for use by later libxo functions.
1479 * Note: normal use of libxo does not require a distinct handle, since
1480 * the default handle (used when NULL is passed) generates text on stdout.
1482 * @style Style of output desired (XO_STYLE_* value)
1483 * @flags Set of XOF_* flags in use with this handle
1486 xo_create (xo_style_t style, xo_xof_flags_t flags)
1488 xo_handle_t *xop = xo_realloc(NULL, sizeof(*xop));
1491 bzero(xop, sizeof(*xop));
1493 xop->xo_style = style;
1494 xop->xo_flags = flags;
1495 xo_init_handle(xop);
1502 * Create a handle that will write to the given file. Use
1503 * the XOF_CLOSE_FP flag to have the file closed on xo_destroy().
1504 * @fp FILE pointer to use
1505 * @style Style of output desired (XO_STYLE_* value)
1506 * @flags Set of XOF_* flags to use with this handle
1509 xo_create_to_file (FILE *fp, xo_style_t style, xo_xof_flags_t flags)
1511 xo_handle_t *xop = xo_create(style, flags);
1514 xop->xo_opaque = fp;
1515 xop->xo_write = xo_write_to_file;
1516 xop->xo_close = xo_close_file;
1517 xop->xo_flush = xo_flush_file;
1524 * Release any resources held by the handle.
1525 * @xop XO handle to alter (or NULL for default handle)
1528 xo_destroy (xo_handle_t *xop_arg)
1530 xo_handle_t *xop = xo_default(xop_arg);
1532 if (xop->xo_close && (xop->xo_flags & XOF_CLOSE_FP))
1533 xop->xo_close(xop->xo_opaque);
1535 xo_free(xop->xo_stack);
1536 xo_buf_cleanup(&xop->xo_data);
1537 xo_buf_cleanup(&xop->xo_fmt);
1538 xo_buf_cleanup(&xop->xo_predicate);
1539 xo_buf_cleanup(&xop->xo_attrs);
1540 xo_buf_cleanup(&xop->xo_color_buf);
1542 if (xop->xo_version)
1543 xo_free(xop->xo_version);
1545 if (xop_arg == NULL) {
1546 bzero(&xo_default_handle, sizeof(xo_default_handle));
1547 xo_default_inited = 0;
1553 * Record a new output style to use for the given handle (or default if
1554 * handle is NULL). This output style will be used for any future output.
1556 * @xop XO handle to alter (or NULL for default handle)
1557 * @style new output style (XO_STYLE_*)
1560 xo_set_style (xo_handle_t *xop, xo_style_t style)
1562 xop = xo_default(xop);
1563 xop->xo_style = style;
1567 xo_get_style (xo_handle_t *xop)
1569 xop = xo_default(xop);
1570 return xo_style(xop);
1574 xo_name_to_style (const char *name)
1576 if (strcmp(name, "xml") == 0)
1577 return XO_STYLE_XML;
1578 else if (strcmp(name, "json") == 0)
1579 return XO_STYLE_JSON;
1580 else if (strcmp(name, "text") == 0)
1581 return XO_STYLE_TEXT;
1582 else if (strcmp(name, "html") == 0)
1583 return XO_STYLE_HTML;
1589 * Convert string name to XOF_* flag value.
1590 * Not all are useful. Or safe. Or sane.
1593 xo_name_to_flag (const char *name)
1595 if (strcmp(name, "pretty") == 0)
1597 if (strcmp(name, "warn") == 0)
1599 if (strcmp(name, "xpath") == 0)
1601 if (strcmp(name, "info") == 0)
1603 if (strcmp(name, "warn-xml") == 0)
1604 return XOF_WARN_XML;
1605 if (strcmp(name, "color") == 0)
1606 return XOF_COLOR_ALLOWED;
1607 if (strcmp(name, "columns") == 0)
1609 if (strcmp(name, "dtrt") == 0)
1611 if (strcmp(name, "flush") == 0)
1613 if (strcmp(name, "keys") == 0)
1615 if (strcmp(name, "ignore-close") == 0)
1616 return XOF_IGNORE_CLOSE;
1617 if (strcmp(name, "not-first") == 0)
1618 return XOF_NOT_FIRST;
1619 if (strcmp(name, "no-locale") == 0)
1620 return XOF_NO_LOCALE;
1621 if (strcmp(name, "no-top") == 0)
1623 if (strcmp(name, "units") == 0)
1625 if (strcmp(name, "underscores") == 0)
1626 return XOF_UNDERSCORES;
1632 xo_set_style_name (xo_handle_t *xop, const char *name)
1637 int style = xo_name_to_style(name);
1641 xo_set_style(xop, style);
1646 * Set the options for a handle using a string of options
1647 * passed in. The input is a comma-separated set of names
1648 * and optional values: "xml,pretty,indent=4"
1651 xo_set_options (xo_handle_t *xop, const char *input)
1653 char *cp, *ep, *vp, *np, *bp;
1654 int style = -1, new_style, len, rc = 0;
1655 xo_xof_flags_t new_flag;
1660 xop = xo_default(xop);
1662 #ifdef LIBXO_COLOR_ON_BY_DEFAULT
1663 /* If the installer used --enable-color-on-by-default, then we allow it */
1664 xop->xo_flags |= XOF_COLOR_ALLOWED;
1665 #endif /* LIBXO_COLOR_ON_BY_DEFAULT */
1668 * We support a simpler, old-school style of giving option
1669 * also, using a single character for each option. It's
1670 * ideal for lazy people, such as myself.
1672 if (*input == ':') {
1675 for (input++ ; *input; input++) {
1678 xop->xo_flags |= XOF_COLOR_ALLOWED;
1682 xop->xo_flags |= XOF_FLUSH;
1686 xop->xo_flags |= XOF_FLUSH_LINE;
1690 xop->xo_style = XO_STYLE_HTML;
1694 xop->xo_flags |= XOF_INFO;
1698 sz = strspn(input + 1, "0123456789");
1700 xop->xo_indent_by = atoi(input + 1);
1701 input += sz - 1; /* Skip value */
1706 xop->xo_flags |= XOF_KEYS;
1710 xop->xo_style = XO_STYLE_JSON;
1714 xop->xo_flags |= XOF_PRETTY;
1718 xop->xo_style = XO_STYLE_TEXT;
1722 xop->xo_flags |= XOF_UNITS;
1726 xop->xo_flags |= XOF_UNDERSCORES;
1730 xop->xo_flags |= XOF_WARN;
1734 xop->xo_style = XO_STYLE_XML;
1738 xop->xo_flags |= XOF_XPATH;
1745 len = strlen(input) + 1;
1747 memcpy(bp, input, len);
1749 for (cp = bp, ep = cp + len - 1; cp && cp < ep; cp = np) {
1750 np = strchr(cp, ',');
1754 vp = strchr(cp, '=');
1758 if (strcmp("colors", cp) == 0) {
1759 /* XXX Look for colors=red-blue+green-yellow */
1763 new_style = xo_name_to_style(cp);
1764 if (new_style >= 0) {
1766 xo_warnx("ignoring multiple styles: '%s'", cp);
1770 new_flag = xo_name_to_flag(cp);
1772 xop->xo_flags |= new_flag;
1774 if (strcmp(cp, "no-color") == 0) {
1775 xop->xo_flags &= ~XOF_COLOR_ALLOWED;
1776 } else if (strcmp(cp, "indent") == 0) {
1777 xop->xo_indent_by = atoi(vp);
1779 xo_warnx("unknown option: '%s'", cp);
1787 xop->xo_style= style;
1793 * Set one or more flags for a given handle (or default if handle is NULL).
1794 * These flags will affect future output.
1796 * @xop XO handle to alter (or NULL for default handle)
1797 * @flags Flags to be set (XOF_*)
1800 xo_set_flags (xo_handle_t *xop, xo_xof_flags_t flags)
1802 xop = xo_default(xop);
1804 xop->xo_flags |= flags;
1808 xo_get_flags (xo_handle_t *xop)
1810 xop = xo_default(xop);
1812 return xop->xo_flags;
1816 * Record a leading prefix for the XPath we generate. This allows the
1817 * generated data to be placed within an XML hierarchy but still have
1818 * accurate XPath expressions.
1820 * @xop XO handle to alter (or NULL for default handle)
1821 * @path The XPath expression
1824 xo_set_leading_xpath (xo_handle_t *xop, const char *path)
1826 xop = xo_default(xop);
1828 if (xop->xo_leading_xpath) {
1829 xo_free(xop->xo_leading_xpath);
1830 xop->xo_leading_xpath = NULL;
1836 int len = strlen(path);
1837 xop->xo_leading_xpath = xo_realloc(NULL, len + 1);
1838 if (xop->xo_leading_xpath) {
1839 memcpy(xop->xo_leading_xpath, path, len + 1);
1844 * Record the info data for a set of tags
1846 * @xop XO handle to alter (or NULL for default handle)
1847 * @info Info data (xo_info_t) to be recorded (or NULL) (MUST BE SORTED)
1848 * @count Number of entries in info (or -1 to count them ourselves)
1851 xo_set_info (xo_handle_t *xop, xo_info_t *infop, int count)
1853 xop = xo_default(xop);
1855 if (count < 0 && infop) {
1858 for (xip = infop, count = 0; xip->xi_name; xip++, count++)
1862 xop->xo_info = infop;
1863 xop->xo_info_count = count;
1867 * Set the formatter callback for a handle. The callback should
1868 * return a newly formatting contents of a formatting instruction,
1869 * meaning the bits inside the braces.
1872 xo_set_formatter (xo_handle_t *xop, xo_formatter_t func,
1873 xo_checkpointer_t cfunc)
1875 xop = xo_default(xop);
1877 xop->xo_formatter = func;
1878 xop->xo_checkpointer = cfunc;
1882 * Clear one or more flags for a given handle (or default if handle is NULL).
1883 * These flags will affect future output.
1885 * @xop XO handle to alter (or NULL for default handle)
1886 * @flags Flags to be cleared (XOF_*)
1889 xo_clear_flags (xo_handle_t *xop, xo_xof_flags_t flags)
1891 xop = xo_default(xop);
1893 xop->xo_flags &= ~flags;
1897 xo_state_name (xo_state_t state)
1899 static const char *names[] = {
1917 if (state < (sizeof(names) / sizeof(names[0])))
1918 return names[state];
1924 xo_line_ensure_open (xo_handle_t *xop, xo_xff_flags_t flags UNUSED)
1926 static char div_open[] = "<div class=\"line\">";
1927 static char div_open_blank[] = "<div class=\"blank-line\">";
1929 if (xop->xo_flags & XOF_DIV_OPEN)
1932 if (xo_style(xop) != XO_STYLE_HTML)
1935 xop->xo_flags |= XOF_DIV_OPEN;
1936 if (flags & XFF_BLANK_LINE)
1937 xo_data_append(xop, div_open_blank, sizeof(div_open_blank) - 1);
1939 xo_data_append(xop, div_open, sizeof(div_open) - 1);
1941 if (xop->xo_flags & XOF_PRETTY)
1942 xo_data_append(xop, "\n", 1);
1946 xo_line_close (xo_handle_t *xop)
1948 static char div_close[] = "</div>";
1950 switch (xo_style(xop)) {
1952 if (!(xop->xo_flags & XOF_DIV_OPEN))
1953 xo_line_ensure_open(xop, 0);
1955 xop->xo_flags &= ~XOF_DIV_OPEN;
1956 xo_data_append(xop, div_close, sizeof(div_close) - 1);
1958 if (xop->xo_flags & XOF_PRETTY)
1959 xo_data_append(xop, "\n", 1);
1963 xo_data_append(xop, "\n", 1);
1969 xo_info_compare (const void *key, const void *data)
1971 const char *name = key;
1972 const xo_info_t *xip = data;
1974 return strcmp(name, xip->xi_name);
1979 xo_info_find (xo_handle_t *xop, const char *name, int nlen)
1982 char *cp = alloca(nlen + 1); /* Need local copy for NUL termination */
1984 memcpy(cp, name, nlen);
1987 xip = bsearch(cp, xop->xo_info, xop->xo_info_count,
1988 sizeof(xop->xo_info[0]), xo_info_compare);
1992 #define CONVERT(_have, _need) (((_have) << 8) | (_need))
1995 * Check to see that the conversion is safe and sane.
1998 xo_check_conversion (xo_handle_t *xop, int have_enc, int need_enc)
2000 switch (CONVERT(have_enc, need_enc)) {
2001 case CONVERT(XF_ENC_UTF8, XF_ENC_UTF8):
2002 case CONVERT(XF_ENC_UTF8, XF_ENC_LOCALE):
2003 case CONVERT(XF_ENC_WIDE, XF_ENC_UTF8):
2004 case CONVERT(XF_ENC_WIDE, XF_ENC_LOCALE):
2005 case CONVERT(XF_ENC_LOCALE, XF_ENC_LOCALE):
2006 case CONVERT(XF_ENC_LOCALE, XF_ENC_UTF8):
2010 xo_failure(xop, "invalid conversion (%c:%c)", have_enc, need_enc);
2016 xo_format_string_direct (xo_handle_t *xop, xo_buffer_t *xbp,
2017 xo_xff_flags_t flags,
2018 const wchar_t *wcp, const char *cp, int len, int max,
2019 int need_enc, int have_enc)
2023 int ilen, olen, width;
2024 int attr = (flags & XFF_ATTR);
2027 if (len > 0 && !xo_buf_has_room(xbp, len))
2037 if ((flags & XFF_UNESCAPE) && (*cp == '\\' || *cp == '%')) {
2043 if (wcp && *wcp == L'\0')
2049 case XF_ENC_WIDE: /* Wide character */
2054 case XF_ENC_UTF8: /* UTF-8 */
2055 ilen = xo_utf8_to_wc_len(cp);
2057 xo_failure(xop, "invalid UTF-8 character: %02hhx", *cp);
2061 if (len > 0 && len < ilen) {
2062 len = 0; /* Break out of the loop */
2066 wc = xo_utf8_char(cp, ilen);
2067 if (wc == (wchar_t) -1) {
2068 xo_failure(xop, "invalid UTF-8 character: %02hhx/%d",
2075 case XF_ENC_LOCALE: /* Native locale */
2076 ilen = (len > 0) ? len : MB_LEN_MAX;
2077 ilen = mbrtowc(&wc, cp, ilen, &xop->xo_mbstate);
2078 if (ilen < 0) { /* Invalid data; skip */
2079 xo_failure(xop, "invalid mbs char: %02hhx", *cp);
2082 if (ilen == 0) { /* Hit a wide NUL character */
2091 /* Reduce len, but not below zero */
2099 * Find the width-in-columns of this character, which must be done
2100 * in wide characters, since we lack a mbswidth() function. If
2103 width = wcwidth(wc);
2105 width = iswcntrl(wc) ? 0 : 1;
2107 if (xo_style(xop) == XO_STYLE_TEXT || xo_style(xop) == XO_STYLE_HTML) {
2108 if (max > 0 && cols + width > max)
2115 /* Output in UTF-8 needs to be escaped, based on the style */
2116 switch (xo_style(xop)) {
2125 else if (attr && wc == '"')
2130 int slen = strlen(sp);
2131 if (!xo_buf_has_room(xbp, slen - 1))
2134 memcpy(xbp->xb_curp, sp, slen);
2135 xbp->xb_curp += slen;
2136 goto done_with_encoding; /* Need multi-level 'break' */
2139 if (wc != '\\' && wc != '"' && wc != '\n' && wc != '\r')
2142 if (!xo_buf_has_room(xbp, 2))
2145 *xbp->xb_curp++ = '\\';
2148 else if (wc == '\r')
2150 else wc = wc & 0x7f;
2152 *xbp->xb_curp++ = wc;
2153 goto done_with_encoding;
2156 olen = xo_utf8_emit_len(wc);
2158 xo_failure(xop, "ignoring bad length");
2162 if (!xo_buf_has_room(xbp, olen))
2165 xo_utf8_emit_char(xbp->xb_curp, olen, wc);
2166 xbp->xb_curp += olen;
2170 if (!xo_buf_has_room(xbp, MB_LEN_MAX + 1))
2173 olen = wcrtomb(xbp->xb_curp, wc, &xop->xo_mbstate);
2175 xo_failure(xop, "could not convert wide char: %lx",
2176 (unsigned long) wc);
2179 *xbp->xb_curp++ = '?';
2181 xbp->xb_curp += olen;
2193 xo_format_string (xo_handle_t *xop, xo_buffer_t *xbp, xo_xff_flags_t flags,
2196 static char null[] = "(null)";
2199 wchar_t *wcp = NULL;
2200 int len, cols = 0, rc = 0;
2201 int off = xbp->xb_curp - xbp->xb_bufp, off2;
2202 int need_enc = (xo_style(xop) == XO_STYLE_TEXT)
2203 ? XF_ENC_LOCALE : XF_ENC_UTF8;
2205 if (xo_check_conversion(xop, xfp->xf_enc, need_enc))
2208 len = xfp->xf_width[XF_WIDTH_SIZE];
2210 if (xfp->xf_enc == XF_ENC_WIDE) {
2211 wcp = va_arg(xop->xo_vap, wchar_t *);
2216 * Dont' deref NULL; use the traditional "(null)" instead
2217 * of the more accurate "who's been a naughty boy, then?".
2221 len = sizeof(null) - 1;
2225 cp = va_arg(xop->xo_vap, char *); /* UTF-8 or native */
2229 /* Echo "Dont' deref NULL" logic */
2232 len = sizeof(null) - 1;
2236 * Optimize the most common case, which is "%s". We just
2237 * need to copy the complete string to the output buffer.
2239 if (xfp->xf_enc == need_enc
2240 && xfp->xf_width[XF_WIDTH_MIN] < 0
2241 && xfp->xf_width[XF_WIDTH_SIZE] < 0
2242 && xfp->xf_width[XF_WIDTH_MAX] < 0
2243 && !(xop->xo_flags & (XOF_ANCHOR | XOF_COLUMNS))) {
2245 xo_buf_escape(xop, xbp, cp, len, flags);
2248 * Our caller expects xb_curp left untouched, so we have
2249 * to reset it and return the number of bytes written to
2252 off2 = xbp->xb_curp - xbp->xb_bufp;
2254 xbp->xb_curp = xbp->xb_bufp + off;
2260 cols = xo_format_string_direct(xop, xbp, flags, wcp, cp, len,
2261 xfp->xf_width[XF_WIDTH_MAX],
2262 need_enc, xfp->xf_enc);
2267 * xo_buf_append* will move xb_curp, so we save/restore it.
2269 off2 = xbp->xb_curp - xbp->xb_bufp;
2271 xbp->xb_curp = xbp->xb_bufp + off;
2273 if (cols < xfp->xf_width[XF_WIDTH_MIN]) {
2275 * Find the number of columns needed to display the string.
2276 * If we have the original wide string, we just call wcswidth,
2277 * but if we did the work ourselves, then we need to do it.
2279 int delta = xfp->xf_width[XF_WIDTH_MIN] - cols;
2280 if (!xo_buf_has_room(xbp, delta))
2284 * If seen_minus, then pad on the right; otherwise move it so
2285 * we can pad on the left.
2287 if (xfp->xf_seen_minus) {
2288 cp = xbp->xb_curp + rc;
2291 memmove(xbp->xb_curp + delta, xbp->xb_curp, rc);
2294 /* Set the padding */
2295 memset(cp, (xfp->xf_leading_zero > 0) ? '0' : ' ', delta);
2300 if (xop->xo_flags & XOF_COLUMNS)
2301 xop->xo_columns += cols;
2302 if (xop->xo_flags & XOF_ANCHOR)
2303 xop->xo_anchor_columns += cols;
2308 xbp->xb_curp = xbp->xb_bufp + off;
2313 xo_data_append_content (xo_handle_t *xop, const char *str, int len)
2316 int need_enc = (xo_style(xop) == XO_STYLE_TEXT)
2317 ? XF_ENC_LOCALE : XF_ENC_UTF8;
2319 cols = xo_format_string_direct(xop, &xop->xo_data, XFF_UNESCAPE,
2321 need_enc, XF_ENC_UTF8);
2323 if (xop->xo_flags & XOF_COLUMNS)
2324 xop->xo_columns += cols;
2325 if (xop->xo_flags & XOF_ANCHOR)
2326 xop->xo_anchor_columns += cols;
2330 xo_bump_width (xo_format_t *xfp, int digit)
2332 int *ip = &xfp->xf_width[xfp->xf_dots];
2334 *ip = ((*ip > 0) ? *ip : 0) * 10 + digit;
2338 xo_trim_ws (xo_buffer_t *xbp, int len)
2343 /* First trim leading space */
2344 for (cp = sp = xbp->xb_curp, ep = cp + len; cp < ep; cp++) {
2352 memmove(sp, cp, len);
2355 /* Then trim off the end */
2356 for (cp = xbp->xb_curp, sp = ep = cp + len; cp < ep; ep--) {
2371 xo_format_data (xo_handle_t *xop, xo_buffer_t *xbp,
2372 const char *fmt, int flen, xo_xff_flags_t flags)
2375 const char *cp, *ep, *sp, *xp = NULL;
2377 int style = (flags & XFF_XML) ? XO_STYLE_XML : xo_style(xop);
2378 unsigned make_output = !(flags & XFF_NO_OUTPUT);
2379 int need_enc = (xo_style(xop) == XO_STYLE_TEXT)
2380 ? XF_ENC_LOCALE : XF_ENC_UTF8;
2383 xbp = &xop->xo_data;
2385 for (cp = fmt, ep = fmt + flen; cp < ep; cp++) {
2391 if (*cp == '\\' && cp[1] != '\0')
2395 } if (cp + 1 < ep && cp[1] == '%') {
2402 cols = xo_format_string_direct(xop, xbp, flags | XFF_UNESCAPE,
2403 NULL, xp, cp - xp, -1,
2404 need_enc, XF_ENC_UTF8);
2405 if (xop->xo_flags & XOF_COLUMNS)
2406 xop->xo_columns += cols;
2407 if (xop->xo_flags & XOF_ANCHOR)
2408 xop->xo_anchor_columns += cols;
2414 bzero(&xf, sizeof(xf));
2415 xf.xf_leading_zero = -1;
2416 xf.xf_width[0] = xf.xf_width[1] = xf.xf_width[2] = -1;
2419 * "%@" starts an XO-specific set of flags:
2420 * @X@ - XML-only field; ignored if style isn't XML
2423 for (cp += 2; cp < ep; cp++) {
2429 * '*' means there's a "%*.*s" value in vap that
2432 if (!(xop->xo_flags & XOF_NO_VA_ARG))
2433 va_arg(xop->xo_vap, int);
2438 /* Hidden fields are only visible to JSON and XML */
2439 if (xop->xo_flags & XFF_ENCODE_ONLY) {
2440 if (style != XO_STYLE_XML
2441 && xo_style(xop) != XO_STYLE_JSON)
2443 } else if (xop->xo_flags & XFF_DISPLAY_ONLY) {
2444 if (style != XO_STYLE_TEXT
2445 && xo_style(xop) != XO_STYLE_HTML)
2453 * Looking at one piece of a format; find the end and
2454 * call snprintf. Then advance xo_vap on our own.
2456 * Note that 'n', 'v', and '$' are not supported.
2458 sp = cp; /* Save start pointer */
2459 for (cp += 1; cp < ep; cp++) {
2462 else if (*cp == 'h')
2464 else if (*cp == 'j')
2466 else if (*cp == 't')
2468 else if (*cp == 'z')
2470 else if (*cp == 'q')
2472 else if (*cp == '.') {
2473 if (++xf.xf_dots >= XF_WIDTH_NUM) {
2474 xo_failure(xop, "Too many dots in format: '%s'", fmt);
2477 } else if (*cp == '-')
2478 xf.xf_seen_minus = 1;
2479 else if (isdigit((int) *cp)) {
2480 if (xf.xf_leading_zero < 0)
2481 xf.xf_leading_zero = (*cp == '0');
2482 xo_bump_width(&xf, *cp - '0');
2483 } else if (*cp == '*') {
2485 xf.xf_star[xf.xf_dots] = 1;
2486 } else if (strchr("diouxXDOUeEfFgGaAcCsSp", *cp) != NULL)
2488 else if (*cp == 'n' || *cp == 'v') {
2489 xo_failure(xop, "unsupported format: '%s'", fmt);
2495 xo_failure(xop, "field format missing format character: %s",
2500 if (!(xop->xo_flags & XOF_NO_VA_ARG)) {
2501 if (*cp == 's' || *cp == 'S') {
2502 /* Handle "%*.*.*s" */
2504 for (s = 0; s < XF_WIDTH_NUM; s++) {
2505 if (xf.xf_star[s]) {
2506 xf.xf_width[s] = va_arg(xop->xo_vap, int);
2508 /* Normalize a negative width value */
2509 if (xf.xf_width[s] < 0) {
2511 xf.xf_width[0] = -xf.xf_width[0];
2512 xf.xf_seen_minus = 1;
2514 xf.xf_width[s] = -1; /* Ignore negative values */
2521 /* If no max is given, it defaults to size */
2522 if (xf.xf_width[XF_WIDTH_MAX] < 0 && xf.xf_width[XF_WIDTH_SIZE] >= 0)
2523 xf.xf_width[XF_WIDTH_MAX] = xf.xf_width[XF_WIDTH_SIZE];
2525 if (xf.xf_fc == 'D' || xf.xf_fc == 'O' || xf.xf_fc == 'U')
2529 xo_buffer_t *fbp = &xop->xo_fmt;
2530 int len = cp - sp + 1;
2531 if (!xo_buf_has_room(fbp, len + 1))
2534 char *newfmt = fbp->xb_curp;
2535 memcpy(newfmt, sp, len);
2536 newfmt[0] = '%'; /* If we skipped over a "%@...@s" format */
2540 * Bad news: our strings are UTF-8, but the stock printf
2541 * functions won't handle field widths for wide characters
2542 * correctly. So we have to handle this ourselves.
2544 if (xop->xo_formatter == NULL
2545 && (xf.xf_fc == 's' || xf.xf_fc == 'S')) {
2546 xf.xf_enc = (xf.xf_lflag || (xf.xf_fc == 'S'))
2547 ? XF_ENC_WIDE : xf.xf_hflag ? XF_ENC_LOCALE : XF_ENC_UTF8;
2548 rc = xo_format_string(xop, xbp, flags, &xf);
2550 if ((flags & XFF_TRIM_WS)
2551 && (xo_style(xop) == XO_STYLE_XML
2552 || xo_style(xop) == XO_STYLE_JSON))
2553 rc = xo_trim_ws(xbp, rc);
2556 int columns = rc = xo_vsnprintf(xop, xbp, newfmt, xop->xo_vap);
2559 * For XML and HTML, we need "&<>" processing; for JSON,
2560 * it's quotes. Text gets nothing.
2564 if (flags & XFF_TRIM_WS)
2565 columns = rc = xo_trim_ws(xbp, rc);
2568 rc = xo_escape_xml(xbp, rc, (flags & XFF_ATTR));
2572 if (flags & XFF_TRIM_WS)
2573 columns = rc = xo_trim_ws(xbp, rc);
2574 rc = xo_escape_json(xbp, rc);
2579 * We can assume all the data we've added is ASCII, so
2580 * the columns and bytes are the same. xo_format_string
2581 * handles all the fancy string conversions and updates
2582 * xo_anchor_columns accordingly.
2584 if (xop->xo_flags & XOF_COLUMNS)
2585 xop->xo_columns += columns;
2586 if (xop->xo_flags & XOF_ANCHOR)
2587 xop->xo_anchor_columns += columns;
2594 * Now for the tricky part: we need to move the argument pointer
2595 * along by the amount needed.
2597 if (!(xop->xo_flags & XOF_NO_VA_ARG)) {
2599 if (xf.xf_fc == 's' ||xf.xf_fc == 'S') {
2601 * The 'S' and 's' formats are normally handled in
2602 * xo_format_string, but if we skipped it, then we
2606 va_arg(xop->xo_vap, char *);
2610 for (s = 0; s < XF_WIDTH_NUM; s++) {
2612 va_arg(xop->xo_vap, int);
2615 if (strchr("diouxXDOU", xf.xf_fc) != NULL) {
2616 if (xf.xf_hflag > 1) {
2617 va_arg(xop->xo_vap, int);
2619 } else if (xf.xf_hflag > 0) {
2620 va_arg(xop->xo_vap, int);
2622 } else if (xf.xf_lflag > 1) {
2623 va_arg(xop->xo_vap, unsigned long long);
2625 } else if (xf.xf_lflag > 0) {
2626 va_arg(xop->xo_vap, unsigned long);
2628 } else if (xf.xf_jflag > 0) {
2629 va_arg(xop->xo_vap, intmax_t);
2631 } else if (xf.xf_tflag > 0) {
2632 va_arg(xop->xo_vap, ptrdiff_t);
2634 } else if (xf.xf_zflag > 0) {
2635 va_arg(xop->xo_vap, size_t);
2637 } else if (xf.xf_qflag > 0) {
2638 va_arg(xop->xo_vap, quad_t);
2641 va_arg(xop->xo_vap, int);
2643 } else if (strchr("eEfFgGaA", xf.xf_fc) != NULL)
2645 va_arg(xop->xo_vap, long double);
2647 va_arg(xop->xo_vap, double);
2649 else if (xf.xf_fc == 'C' || (xf.xf_fc == 'c' && xf.xf_lflag))
2650 va_arg(xop->xo_vap, wint_t);
2652 else if (xf.xf_fc == 'c')
2653 va_arg(xop->xo_vap, int);
2655 else if (xf.xf_fc == 'p')
2656 va_arg(xop->xo_vap, void *);
2663 cols = xo_format_string_direct(xop, xbp, flags | XFF_UNESCAPE,
2664 NULL, xp, cp - xp, -1,
2665 need_enc, XF_ENC_UTF8);
2666 if (xop->xo_flags & XOF_COLUMNS)
2667 xop->xo_columns += cols;
2668 if (xop->xo_flags & XOF_ANCHOR)
2669 xop->xo_anchor_columns += cols;
2679 xo_fix_encoding (xo_handle_t *xop UNUSED, char *encoding)
2681 char *cp = encoding;
2683 if (cp[0] != '%' || !isdigit((int) cp[1]))
2686 for (cp += 2; *cp; cp++) {
2687 if (!isdigit((int) *cp))
2698 xo_color_append_html (xo_handle_t *xop)
2701 * If the color buffer has content, we add it now. It's already
2702 * prebuilt and ready, since we want to add it to every <div>.
2704 if (!xo_buf_is_empty(&xop->xo_color_buf)) {
2705 xo_buffer_t *xbp = &xop->xo_color_buf;
2707 xo_data_append(xop, xbp->xb_bufp, xbp->xb_curp - xbp->xb_bufp);
2712 xo_buf_append_div (xo_handle_t *xop, const char *class, xo_xff_flags_t flags,
2713 const char *name, int nlen,
2714 const char *value, int vlen,
2715 const char *encoding, int elen)
2717 static char div_start[] = "<div class=\"";
2718 static char div_tag[] = "\" data-tag=\"";
2719 static char div_xpath[] = "\" data-xpath=\"";
2720 static char div_key[] = "\" data-key=\"key";
2721 static char div_end[] = "\">";
2722 static char div_close[] = "</div>";
2725 * To build our XPath predicate, we need to save the va_list before
2726 * we format our data, and then restore it before we format the
2728 * Display-only keys implies that we've got an encode-only key
2729 * elsewhere, so we don't use them from making predicates.
2731 int need_predidate =
2732 (name && (flags & XFF_KEY) && !(flags & XFF_DISPLAY_ONLY)
2733 && (xop->xo_flags & XOF_XPATH));
2735 if (need_predidate) {
2738 va_copy(va_local, xop->xo_vap);
2739 if (xop->xo_checkpointer)
2740 xop->xo_checkpointer(xop, xop->xo_vap, 0);
2743 * Build an XPath predicate expression to match this key.
2744 * We use the format buffer.
2746 xo_buffer_t *pbp = &xop->xo_predicate;
2747 pbp->xb_curp = pbp->xb_bufp; /* Restart buffer */
2749 xo_buf_append(pbp, "[", 1);
2750 xo_buf_escape(xop, pbp, name, nlen, 0);
2751 if (xop->xo_flags & XOF_PRETTY)
2752 xo_buf_append(pbp, " = '", 4);
2754 xo_buf_append(pbp, "='", 2);
2756 /* The encoding format defaults to the normal format */
2757 if (encoding == NULL) {
2758 char *enc = alloca(vlen + 1);
2759 memcpy(enc, value, vlen);
2761 encoding = xo_fix_encoding(xop, enc);
2762 elen = strlen(encoding);
2765 xo_format_data(xop, pbp, encoding, elen, XFF_XML | XFF_ATTR);
2767 xo_buf_append(pbp, "']", 2);
2769 /* Now we record this predicate expression in the stack */
2770 xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth];
2771 int olen = xsp->xs_keys ? strlen(xsp->xs_keys) : 0;
2772 int dlen = pbp->xb_curp - pbp->xb_bufp;
2774 char *cp = xo_realloc(xsp->xs_keys, olen + dlen + 1);
2776 memcpy(cp + olen, pbp->xb_bufp, dlen);
2777 cp[olen + dlen] = '\0';
2781 /* Now we reset the xo_vap as if we were never here */
2782 va_end(xop->xo_vap);
2783 va_copy(xop->xo_vap, va_local);
2785 if (xop->xo_checkpointer)
2786 xop->xo_checkpointer(xop, xop->xo_vap, 1);
2789 if (flags & XFF_ENCODE_ONLY) {
2791 * Even if this is encode-only, we need to go thru the
2792 * work of formatting it to make sure the args are cleared
2795 xo_format_data(xop, &xop->xo_data, encoding, elen,
2796 flags | XFF_NO_OUTPUT);
2800 xo_line_ensure_open(xop, 0);
2802 if (xop->xo_flags & XOF_PRETTY)
2803 xo_buf_indent(xop, xop->xo_indent_by);
2805 xo_data_append(xop, div_start, sizeof(div_start) - 1);
2806 xo_data_append(xop, class, strlen(class));
2809 * If the color buffer has content, we add it now. It's already
2810 * prebuilt and ready, since we want to add it to every <div>.
2812 if (!xo_buf_is_empty(&xop->xo_color_buf)) {
2813 xo_buffer_t *xbp = &xop->xo_color_buf;
2815 xo_data_append(xop, xbp->xb_bufp, xbp->xb_curp - xbp->xb_bufp);
2819 xo_data_append(xop, div_tag, sizeof(div_tag) - 1);
2820 xo_data_escape(xop, name, nlen);
2823 * Save the offset at which we'd place units. See xo_format_units.
2825 if (xop->xo_flags & XOF_UNITS) {
2826 xop->xo_flags |= XOF_UNITS_PENDING;
2828 * Note: We need the '+1' here because we know we've not
2829 * added the closing quote. We add one, knowing the quote
2830 * will be added shortly.
2832 xop->xo_units_offset =
2833 xop->xo_data.xb_curp -xop->xo_data.xb_bufp + 1;
2838 if (xop->xo_flags & XOF_XPATH) {
2842 xo_data_append(xop, div_xpath, sizeof(div_xpath) - 1);
2843 if (xop->xo_leading_xpath)
2844 xo_data_append(xop, xop->xo_leading_xpath,
2845 strlen(xop->xo_leading_xpath));
2847 for (i = 0; i <= xop->xo_depth; i++) {
2848 xsp = &xop->xo_stack[i];
2849 if (xsp->xs_name == NULL)
2853 * XSS_OPEN_LIST and XSS_OPEN_LEAF_LIST stack frames
2854 * are directly under XSS_OPEN_INSTANCE frames so we
2855 * don't need to put these in our XPath expressions.
2857 if (xsp->xs_state == XSS_OPEN_LIST
2858 || xsp->xs_state == XSS_OPEN_LEAF_LIST)
2861 xo_data_append(xop, "/", 1);
2862 xo_data_escape(xop, xsp->xs_name, strlen(xsp->xs_name));
2864 /* Don't show keys for the key field */
2865 if (i != xop->xo_depth || !(flags & XFF_KEY))
2866 xo_data_append(xop, xsp->xs_keys, strlen(xsp->xs_keys));
2870 xo_data_append(xop, "/", 1);
2871 xo_data_escape(xop, name, nlen);
2874 if ((xop->xo_flags & XOF_INFO) && xop->xo_info) {
2875 static char in_type[] = "\" data-type=\"";
2876 static char in_help[] = "\" data-help=\"";
2878 xo_info_t *xip = xo_info_find(xop, name, nlen);
2881 xo_data_append(xop, in_type, sizeof(in_type) - 1);
2882 xo_data_escape(xop, xip->xi_type, strlen(xip->xi_type));
2885 xo_data_append(xop, in_help, sizeof(in_help) - 1);
2886 xo_data_escape(xop, xip->xi_help, strlen(xip->xi_help));
2891 if ((flags & XFF_KEY) && (xop->xo_flags & XOF_KEYS))
2892 xo_data_append(xop, div_key, sizeof(div_key) - 1);
2895 xo_data_append(xop, div_end, sizeof(div_end) - 1);
2897 xo_format_data(xop, NULL, value, vlen, 0);
2899 xo_data_append(xop, div_close, sizeof(div_close) - 1);
2901 if (xop->xo_flags & XOF_PRETTY)
2902 xo_data_append(xop, "\n", 1);
2906 xo_format_text (xo_handle_t *xop, const char *str, int len)
2908 switch (xo_style(xop)) {
2910 xo_buf_append_locale(xop, &xop->xo_data, str, len);
2914 xo_buf_append_div(xop, "text", 0, NULL, 0, str, len, NULL, 0);
2920 xo_format_title (xo_handle_t *xop, const char *str, int len,
2921 const char *fmt, int flen)
2923 static char div_open[] = "<div class=\"title";
2924 static char div_middle[] = "\">";
2925 static char div_close[] = "</div>";
2932 switch (xo_style(xop)) {
2936 * Even though we don't care about text, we need to do
2937 * enough parsing work to skip over the right bits of xo_vap.
2940 xo_format_data(xop, NULL, fmt, flen, XFF_NO_OUTPUT);
2944 xo_buffer_t *xbp = &xop->xo_data;
2945 int start = xbp->xb_curp - xbp->xb_bufp;
2946 int left = xbp->xb_size - start;
2948 int need_enc = XF_ENC_LOCALE;
2950 if (xo_style(xop) == XO_STYLE_HTML) {
2951 need_enc = XF_ENC_UTF8;
2952 xo_line_ensure_open(xop, 0);
2953 if (xop->xo_flags & XOF_PRETTY)
2954 xo_buf_indent(xop, xop->xo_indent_by);
2955 xo_buf_append(&xop->xo_data, div_open, sizeof(div_open) - 1);
2956 xo_color_append_html(xop);
2957 xo_buf_append(&xop->xo_data, div_middle, sizeof(div_middle) - 1);
2960 start = xbp->xb_curp - xbp->xb_bufp; /* Reset start */
2962 char *newfmt = alloca(flen + 1);
2963 memcpy(newfmt, fmt, flen);
2964 newfmt[flen] = '\0';
2966 /* If len is non-zero, the format string apply to the name */
2967 char *newstr = alloca(len + 1);
2968 memcpy(newstr, str, len);
2971 if (newstr[len - 1] == 's') {
2975 rc = snprintf(NULL, 0, newfmt, newstr);
2978 * We have to do this the hard way, since we might need
2981 bp = alloca(rc + 1);
2982 rc = snprintf(bp, rc + 1, newfmt, newstr);
2983 cols = xo_format_string_direct(xop, xbp, 0, NULL, bp, rc, -1,
2984 need_enc, XF_ENC_UTF8);
2986 if (xop->xo_flags & XOF_COLUMNS)
2987 xop->xo_columns += cols;
2988 if (xop->xo_flags & XOF_ANCHOR)
2989 xop->xo_anchor_columns += cols;
2995 rc = snprintf(xbp->xb_curp, left, newfmt, newstr);
2997 if (!xo_buf_has_room(xbp, rc))
2999 left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp);
3000 rc = snprintf(xbp->xb_curp, left, newfmt, newstr);
3004 if (xop->xo_flags & XOF_COLUMNS)
3005 xop->xo_columns += rc;
3006 if (xop->xo_flags & XOF_ANCHOR)
3007 xop->xo_anchor_columns += rc;
3012 xo_format_data(xop, NULL, fmt, flen, 0);
3014 /* xo_format_data moved curp, so we need to reset it */
3015 rc = xbp->xb_curp - (xbp->xb_bufp + start);
3016 xbp->xb_curp = xbp->xb_bufp + start;
3019 /* If we're styling HTML, then we need to escape it */
3020 if (xo_style(xop) == XO_STYLE_HTML) {
3021 rc = xo_escape_xml(xbp, rc, 0);
3028 if (xo_style(xop) == XO_STYLE_HTML) {
3029 xo_data_append(xop, div_close, sizeof(div_close) - 1);
3030 if (xop->xo_flags & XOF_PRETTY)
3031 xo_data_append(xop, "\n", 1);
3036 xo_format_prep (xo_handle_t *xop, xo_xff_flags_t flags)
3038 if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST) {
3039 xo_data_append(xop, ",", 1);
3040 if (!(flags & XFF_LEAF_LIST) && (xop->xo_flags & XOF_PRETTY))
3041 xo_data_append(xop, "\n", 1);
3043 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST;
3047 /* Useful debugging function */
3049 xo_arg (xo_handle_t *xop);
3051 xo_arg (xo_handle_t *xop)
3053 xop = xo_default(xop);
3054 fprintf(stderr, "0x%x", va_arg(xop->xo_vap, unsigned));
3059 xo_format_value (xo_handle_t *xop, const char *name, int nlen,
3060 const char *format, int flen,
3061 const char *encoding, int elen, xo_xff_flags_t flags)
3063 int pretty = (xop->xo_flags & XOF_PRETTY);
3068 * Before we emit a value, we need to know that the frame is ready.
3070 xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth];
3072 if (flags & XFF_LEAF_LIST) {
3074 * Check if we've already started to emit normal leafs
3075 * or if we're not in a leaf list.
3077 if ((xsp->xs_flags & (XSF_EMIT | XSF_EMIT_KEY))
3078 || !(xsp->xs_flags & XSF_EMIT_LEAF_LIST)) {
3079 char nbuf[nlen + 1];
3080 memcpy(nbuf, name, nlen);
3083 int rc = xo_transition(xop, 0, nbuf, XSS_EMIT_LEAF_LIST);
3085 flags |= XFF_DISPLAY_ONLY | XFF_ENCODE_ONLY;
3087 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_EMIT_LEAF_LIST;
3090 xsp = &xop->xo_stack[xop->xo_depth];
3092 name = xsp->xs_name;
3093 nlen = strlen(name);
3096 } else if (flags & XFF_KEY) {
3097 /* Emitting a 'k' (key) field */
3098 if ((xsp->xs_flags & XSF_EMIT) && !(flags & XFF_DISPLAY_ONLY)) {
3099 xo_failure(xop, "key field emitted after normal value field: '%.*s'",
3102 } else if (!(xsp->xs_flags & XSF_EMIT_KEY)) {
3103 char nbuf[nlen + 1];
3104 memcpy(nbuf, name, nlen);
3107 int rc = xo_transition(xop, 0, nbuf, XSS_EMIT);
3109 flags |= XFF_DISPLAY_ONLY | XFF_ENCODE_ONLY;
3111 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_EMIT_KEY;
3113 xsp = &xop->xo_stack[xop->xo_depth];
3114 xsp->xs_flags |= XSF_EMIT_KEY;
3118 /* Emitting a normal value field */
3119 if ((xsp->xs_flags & XSF_EMIT_LEAF_LIST)
3120 || !(xsp->xs_flags & XSF_EMIT)) {
3121 char nbuf[nlen + 1];
3122 memcpy(nbuf, name, nlen);
3125 int rc = xo_transition(xop, 0, nbuf, XSS_EMIT);
3127 flags |= XFF_DISPLAY_ONLY | XFF_ENCODE_ONLY;
3129 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_EMIT;
3131 xsp = &xop->xo_stack[xop->xo_depth];
3132 xsp->xs_flags |= XSF_EMIT;
3136 switch (xo_style(xop)) {
3138 if (flags & XFF_ENCODE_ONLY)
3139 flags |= XFF_NO_OUTPUT;
3140 xo_format_data(xop, NULL, format, flen, flags);
3144 if (flags & XFF_ENCODE_ONLY)
3145 flags |= XFF_NO_OUTPUT;
3146 xo_buf_append_div(xop, "data", flags, name, nlen,
3147 format, flen, encoding, elen);
3152 * Even though we're not making output, we still need to
3153 * let the formatting code handle the va_arg popping.
3155 if (flags & XFF_DISPLAY_ONLY) {
3156 flags |= XFF_NO_OUTPUT;
3157 xo_format_data(xop, NULL, format, flen, flags);
3165 char *enc = alloca(flen + 1);
3166 memcpy(enc, format, flen);
3168 format = xo_fix_encoding(xop, enc);
3169 flen = strlen(format);
3173 static char missing[] = "missing-field-name";
3174 xo_failure(xop, "missing field name: %s", format);
3176 nlen = sizeof(missing) - 1;
3180 xo_buf_indent(xop, -1);
3181 xo_data_append(xop, "<", 1);
3182 xo_data_escape(xop, name, nlen);
3184 if (xop->xo_attrs.xb_curp != xop->xo_attrs.xb_bufp) {
3185 xo_data_append(xop, xop->xo_attrs.xb_bufp,
3186 xop->xo_attrs.xb_curp - xop->xo_attrs.xb_bufp);
3187 xop->xo_attrs.xb_curp = xop->xo_attrs.xb_bufp;
3191 * We indicate 'key' fields using the 'key' attribute. While
3192 * this is really committing the crime of mixing meta-data with
3193 * data, it's often useful. Especially when format meta-data is
3194 * difficult to come by.
3196 if ((flags & XFF_KEY) && (xop->xo_flags & XOF_KEYS)) {
3197 static char attr[] = " key=\"key\"";
3198 xo_data_append(xop, attr, sizeof(attr) - 1);
3202 * Save the offset at which we'd place units. See xo_format_units.
3204 if (xop->xo_flags & XOF_UNITS) {
3205 xop->xo_flags |= XOF_UNITS_PENDING;
3206 xop->xo_units_offset = xop->xo_data.xb_curp -xop->xo_data.xb_bufp;
3209 xo_data_append(xop, ">", 1);
3210 xo_format_data(xop, NULL, format, flen, flags);
3211 xo_data_append(xop, "</", 2);
3212 xo_data_escape(xop, name, nlen);
3213 xo_data_append(xop, ">", 1);
3215 xo_data_append(xop, "\n", 1);
3219 if (flags & XFF_DISPLAY_ONLY) {
3220 flags |= XFF_NO_OUTPUT;
3221 xo_format_data(xop, NULL, format, flen, flags);
3229 char *enc = alloca(flen + 1);
3230 memcpy(enc, format, flen);
3232 format = xo_fix_encoding(xop, enc);
3233 flen = strlen(format);
3236 int first = !(xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST);
3238 xo_format_prep(xop, flags);
3240 if (flags & XFF_QUOTE)
3242 else if (flags & XFF_NOQUOTE)
3244 else if (flen == 0) {
3246 format = "true"; /* JSON encodes empty tags as a boolean true */
3248 } else if (strchr("diouxXDOUeEfFgGaAcCp", format[flen - 1]) == NULL)
3254 static char missing[] = "missing-field-name";
3255 xo_failure(xop, "missing field name: %s", format);
3257 nlen = sizeof(missing) - 1;
3260 if (flags & XFF_LEAF_LIST) {
3261 if (!first && pretty)
3262 xo_data_append(xop, "\n", 1);
3264 xo_buf_indent(xop, -1);
3267 xo_buf_indent(xop, -1);
3268 xo_data_append(xop, "\"", 1);
3270 xbp = &xop->xo_data;
3271 int off = xbp->xb_curp - xbp->xb_bufp;
3273 xo_data_escape(xop, name, nlen);
3275 if (xop->xo_flags & XOF_UNDERSCORES) {
3276 int now = xbp->xb_curp - xbp->xb_bufp;
3277 for ( ; off < now; off++)
3278 if (xbp->xb_bufp[off] == '-')
3279 xbp->xb_bufp[off] = '_';
3281 xo_data_append(xop, "\":", 2);
3283 xo_data_append(xop, " ", 1);
3287 xo_data_append(xop, "\"", 1);
3289 xo_format_data(xop, NULL, format, flen, flags);
3292 xo_data_append(xop, "\"", 1);
3298 xo_format_content (xo_handle_t *xop, const char *class_name,
3299 const char *xml_tag, int display_only,
3300 const char *str, int len, const char *fmt, int flen)
3302 switch (xo_style(xop)) {
3305 xo_data_append_content(xop, str, len);
3307 xo_format_data(xop, NULL, fmt, flen, 0);
3316 xo_buf_append_div(xop, class_name, 0, NULL, 0, str, len, NULL, 0);
3326 xo_open_container_h(xop, xml_tag);
3327 xo_format_value(xop, "message", 7, str, len, NULL, 0, 0);
3328 xo_close_container_h(xop, xml_tag);
3332 * Even though we don't care about labels, we need to do
3333 * enough parsing work to skip over the right bits of xo_vap.
3336 xo_format_data(xop, NULL, fmt, flen, XFF_NO_OUTPUT);
3342 * Even though we don't care about labels, we need to do
3343 * enough parsing work to skip over the right bits of xo_vap.
3347 xo_format_data(xop, NULL, fmt, flen, XFF_NO_OUTPUT);
3350 /* XXX need schem for representing errors in JSON */
3355 static const char *xo_color_names[] = {
3356 "default", /* XO_COL_DEFAULT */
3357 "black", /* XO_COL_BLACK */
3358 "red", /* XO_CLOR_RED */
3359 "green", /* XO_COL_GREEN */
3360 "yellow", /* XO_COL_YELLOW */
3361 "blue", /* XO_COL_BLUE */
3362 "magenta", /* XO_COL_MAGENTA */
3363 "cyan", /* XO_COL_CYAN */
3364 "white", /* XO_COL_WHITE */
3369 xo_color_find (const char *str)
3373 for (i = 0; xo_color_names[i]; i++) {
3374 if (strcmp(xo_color_names[i], str) == 0)
3381 static const char *xo_effect_names[] = {
3382 "reset", /* XO_EFF_RESET */
3383 "normal", /* XO_EFF_NORMAL */
3384 "bold", /* XO_EFF_BOLD */
3385 "underline", /* XO_EFF_UNDERLINE */
3386 "inverse", /* XO_EFF_INVERSE */
3390 static const char *xo_effect_on_codes[] = {
3391 "0", /* XO_EFF_RESET */
3392 "0", /* XO_EFF_NORMAL */
3393 "1", /* XO_EFF_BOLD */
3394 "4", /* XO_EFF_UNDERLINE */
3395 "7", /* XO_EFF_INVERSE */
3401 * See comment below re: joy of terminal standards. These can
3402 * be use by just adding:
3403 * if (newp->xoc_effects & bit)
3404 * code = xo_effect_on_codes[i];
3406 * + code = xo_effect_off_codes[i];
3407 * in xo_color_handle_text.
3409 static const char *xo_effect_off_codes[] = {
3410 "0", /* XO_EFF_RESET */
3411 "0", /* XO_EFF_NORMAL */
3412 "21", /* XO_EFF_BOLD */
3413 "24", /* XO_EFF_UNDERLINE */
3414 "27", /* XO_EFF_INVERSE */
3420 xo_effect_find (const char *str)
3424 for (i = 0; xo_effect_names[i]; i++) {
3425 if (strcmp(xo_effect_names[i], str) == 0)
3433 xo_colors_parse (xo_handle_t *xop, xo_colors_t *xocp, char *str)
3435 #ifdef LIBXO_TEXT_ONLY
3437 #endif /* LIBXO_TEXT_ONLY */
3439 char *cp, *ep, *np, *xp;
3440 int len = strlen(str);
3444 * Possible tokens: colors, bg-colors, effects, no-effects, "reset".
3446 for (cp = str, ep = cp + len - 1; cp && cp < ep; cp = np) {
3447 /* Trim leading whitespace */
3448 while (isspace((int) *cp))
3451 np = strchr(cp, ',');
3455 /* Trim trailing whitespace */
3456 xp = cp + strlen(cp) - 1;
3457 while (isspace(*xp) && xp > cp)
3460 if (cp[0] == 'f' && cp[1] == 'g' && cp[2] == '-') {
3461 rc = xo_color_find(cp + 3);
3465 xocp->xoc_col_fg = rc;
3467 } else if (cp[0] == 'b' && cp[1] == 'g' && cp[2] == '-') {
3468 rc = xo_color_find(cp + 3);
3471 xocp->xoc_col_bg = rc;
3473 } else if (cp[0] == 'n' && cp[1] == 'o' && cp[2] == '-') {
3474 rc = xo_effect_find(cp + 3);
3477 xocp->xoc_effects &= ~(1 << rc);
3480 rc = xo_effect_find(cp);
3483 xocp->xoc_effects |= 1 << rc;
3487 xocp->xoc_col_fg = xocp->xoc_col_bg = 0;
3488 /* Note: not "|=" since we want to wipe out the old value */
3489 xocp->xoc_effects = XO_EFF_RESET;
3493 xocp->xoc_effects &= ~(XO_EFF_BOLD | XO_EFF_UNDERLINE
3494 | XO_EFF_INVERSE | XO_EFF_NORMAL);
3501 if (xop->xo_flags & XOF_WARN)
3502 xo_failure(xop, "unknown color/effect string detected: '%s'", cp);
3507 xo_colors_enabled (xo_handle_t *xop UNUSED)
3509 #ifdef LIBXO_TEXT_ONLY
3511 #else /* LIBXO_TEXT_ONLY */
3512 return ((xop->xo_flags & XOF_COLOR) ? 1 : 0);
3513 #endif /* LIBXO_TEXT_ONLY */
3517 xo_colors_handle_text (xo_handle_t *xop UNUSED, xo_colors_t *newp)
3520 char *cp = buf, *ep = buf + sizeof(buf);
3522 xo_colors_t *oldp = &xop->xo_colors;
3523 const char *code = NULL;
3526 * Start the buffer with an escape. We don't want to add the '['
3527 * now, since we let xo_effect_text_add unconditionally add the ';'.
3528 * We'll replace the first ';' with a '[' when we're done.
3530 *cp++ = 0x1b; /* Escape */
3533 * Terminals were designed back in the age before "certainty" was
3534 * invented, when standards were more what you'd call "guidelines"
3535 * than actual rules. Anyway we can't depend on them to operate
3536 * correctly. So when display attributes are changed, we punt,
3537 * reseting them all and turning back on the ones we want to keep.
3538 * Longer, but should be completely reliable. Savvy?
3540 if (oldp->xoc_effects != (newp->xoc_effects & oldp->xoc_effects)) {
3541 newp->xoc_effects |= XO_EFF_RESET;
3542 oldp->xoc_effects = 0;
3545 for (i = 0, bit = 1; xo_effect_names[i]; i++, bit <<= 1) {
3546 if ((newp->xoc_effects & bit) == (oldp->xoc_effects & bit))
3549 if (newp->xoc_effects & bit)
3550 code = xo_effect_on_codes[i];
3552 cp += snprintf(cp, ep - cp, ";%s", code);
3554 return; /* Should not occur */
3556 if (bit == XO_EFF_RESET) {
3557 /* Mark up the old value so we can detect current values as new */
3558 oldp->xoc_effects = 0;
3559 oldp->xoc_col_fg = oldp->xoc_col_bg = XO_COL_DEFAULT;
3563 if (newp->xoc_col_fg != oldp->xoc_col_fg) {
3564 cp += snprintf(cp, ep - cp, ";3%u",
3565 (newp->xoc_col_fg != XO_COL_DEFAULT)
3566 ? newp->xoc_col_fg - 1 : 9);
3569 if (newp->xoc_col_bg != oldp->xoc_col_bg) {
3570 cp += snprintf(cp, ep - cp, ";4%u",
3571 (newp->xoc_col_bg != XO_COL_DEFAULT)
3572 ? newp->xoc_col_bg - 1 : 9);
3575 if (cp - buf != 1 && cp < ep - 3) {
3576 buf[1] = '['; /* Overwrite leading ';' */
3579 xo_buf_append(&xop->xo_data, buf, cp - buf);
3584 xo_colors_handle_html (xo_handle_t *xop, xo_colors_t *newp)
3586 xo_colors_t *oldp = &xop->xo_colors;
3589 * HTML colors are mostly trivial: fill in xo_color_buf with
3590 * a set of class tags representing the colors and effects.
3593 /* If nothing changed, then do nothing */
3594 if (oldp->xoc_effects == newp->xoc_effects
3595 && oldp->xoc_col_fg == newp->xoc_col_fg
3596 && oldp->xoc_col_bg == newp->xoc_col_bg)
3600 xo_buffer_t *xbp = &xop->xo_color_buf;
3602 xo_buf_reset(xbp); /* We rebuild content after each change */
3604 for (i = 0, bit = 1; xo_effect_names[i]; i++, bit <<= 1) {
3605 if (!(newp->xoc_effects & bit))
3608 xo_buf_append_str(xbp, " effect-");
3609 xo_buf_append_str(xbp, xo_effect_names[i]);
3612 const char *fg = NULL;
3613 const char *bg = NULL;
3615 if (newp->xoc_col_fg != XO_COL_DEFAULT)
3616 fg = xo_color_names[newp->xoc_col_fg];
3617 if (newp->xoc_col_bg != XO_COL_DEFAULT)
3618 bg = xo_color_names[newp->xoc_col_bg];
3620 if (newp->xoc_effects & XO_EFF_INVERSE) {
3621 const char *tmp = fg;
3632 xo_buf_append_str(xbp, " color-fg-");
3633 xo_buf_append_str(xbp, fg);
3637 xo_buf_append_str(xbp, " color-bg-");
3638 xo_buf_append_str(xbp, bg);
3643 xo_format_colors (xo_handle_t *xop, const char *str, int len,
3644 const char *fmt, int flen)
3648 /* If the string is static and we've in an encoding style, bail */
3650 && (xo_style(xop) == XO_STYLE_XML || xo_style(xop) == XO_STYLE_JSON))
3656 xo_buf_append(&xb, str, len);
3658 xo_format_data(xop, &xb, fmt, flen, 0);
3660 xo_buf_append(&xb, "reset", 6); /* Default if empty */
3662 if (xo_colors_enabled(xop)) {
3663 switch (xo_style(xop)) {
3666 xo_buf_append(&xb, "", 1);
3668 xo_colors_t xoc = xop->xo_colors;
3669 xo_colors_parse(xop, &xoc, xb.xb_bufp);
3671 if (xo_style(xop) == XO_STYLE_TEXT) {
3673 * Text mode means emitting the colors as ANSI character
3674 * codes. This will allow people who like colors to have
3675 * colors. The issue is, of course conflicting with the
3676 * user's perfectly reasonable color scheme. Which leads
3677 * to the hell of LSCOLORS, where even app need to have
3678 * customization hooks for adjusting colors. Instead we
3679 * provide a simpler-but-still-annoying answer where one
3680 * can map colors to other colors.
3682 xo_colors_handle_text(xop, &xoc);
3683 xoc.xoc_effects &= ~XO_EFF_RESET; /* After handling it */
3687 * HTML output is wrapped in divs, so the color information
3688 * must appear in every div until cleared. Most pathetic.
3691 xoc.xoc_effects &= ~XO_EFF_RESET; /* Before handling effects */
3692 xo_colors_handle_html(xop, &xoc);
3695 xop->xo_colors = xoc;
3701 * Nothing to do; we did all that work just to clear the stack of
3702 * formatting arguments.
3708 xo_buf_cleanup(&xb);
3712 xo_format_units (xo_handle_t *xop, const char *str, int len,
3713 const char *fmt, int flen)
3715 static char units_start_xml[] = " units=\"";
3716 static char units_start_html[] = " data-units=\"";
3718 if (!(xop->xo_flags & XOF_UNITS_PENDING)) {
3719 xo_format_content(xop, "units", NULL, 1, str, len, fmt, flen);
3723 xo_buffer_t *xbp = &xop->xo_data;
3724 int start = xop->xo_units_offset;
3725 int stop = xbp->xb_curp - xbp->xb_bufp;
3727 if (xo_style(xop) == XO_STYLE_XML)
3728 xo_buf_append(xbp, units_start_xml, sizeof(units_start_xml) - 1);
3729 else if (xo_style(xop) == XO_STYLE_HTML)
3730 xo_buf_append(xbp, units_start_html, sizeof(units_start_html) - 1);
3735 xo_data_append(xop, str, len);
3737 xo_format_data(xop, NULL, fmt, flen, 0);
3739 xo_buf_append(xbp, "\"", 1);
3741 int now = xbp->xb_curp - xbp->xb_bufp;
3742 int delta = now - stop;
3743 if (delta < 0) { /* Strange; no output to move */
3744 xbp->xb_curp = xbp->xb_bufp + stop; /* Reset buffer to prior state */
3749 * Now we're in it alright. We've need to insert the unit value
3750 * we just created into the right spot. We make a local copy,
3751 * move it and then insert our copy. We know there's room in the
3752 * buffer, since we're just moving this around.
3754 char *buf = alloca(delta);
3756 memcpy(buf, xbp->xb_bufp + stop, delta);
3757 memmove(xbp->xb_bufp + start + delta, xbp->xb_bufp + start, stop - start);
3758 memmove(xbp->xb_bufp + start, buf, delta);
3762 xo_find_width (xo_handle_t *xop, const char *str, int len,
3763 const char *fmt, int flen)
3770 bp = alloca(len + 1); /* Make local NUL-terminated copy of str */
3771 memcpy(bp, str, len);
3774 width = strtol(bp, &cp, 0);
3775 if (width == LONG_MIN || width == LONG_MAX
3776 || bp == cp || *cp != '\0' ) {
3778 xo_failure(xop, "invalid width for anchor: '%s'", bp);
3781 if (flen != 2 || strncmp("%d", fmt, flen) != 0)
3782 xo_failure(xop, "invalid width format: '%*.*s'", flen, flen, fmt);
3783 if (!(xop->xo_flags & XOF_NO_VA_ARG))
3784 width = va_arg(xop->xo_vap, int);
3791 xo_anchor_clear (xo_handle_t *xop)
3793 xop->xo_flags &= ~XOF_ANCHOR;
3794 xop->xo_anchor_offset = 0;
3795 xop->xo_anchor_columns = 0;
3796 xop->xo_anchor_min_width = 0;
3800 * An anchor is a marker used to delay field width implications.
3801 * Imagine the format string "{[:10}{min:%d}/{cur:%d}/{max:%d}{:]}".
3802 * We are looking for output like " 1/4/5"
3804 * To make this work, we record the anchor and then return to
3805 * format it when the end anchor tag is seen.
3808 xo_anchor_start (xo_handle_t *xop, const char *str, int len,
3809 const char *fmt, int flen)
3811 if (xo_style(xop) != XO_STYLE_TEXT && xo_style(xop) != XO_STYLE_HTML)
3814 if (xop->xo_flags & XOF_ANCHOR)
3815 xo_failure(xop, "the anchor already recording is discarded");
3817 xop->xo_flags |= XOF_ANCHOR;
3818 xo_buffer_t *xbp = &xop->xo_data;
3819 xop->xo_anchor_offset = xbp->xb_curp - xbp->xb_bufp;
3820 xop->xo_anchor_columns = 0;
3823 * Now we find the width, if possible. If it's not there,
3824 * we'll get it on the end anchor.
3826 xop->xo_anchor_min_width = xo_find_width(xop, str, len, fmt, flen);
3830 xo_anchor_stop (xo_handle_t *xop, const char *str, int len,
3831 const char *fmt, int flen)
3833 if (xo_style(xop) != XO_STYLE_TEXT && xo_style(xop) != XO_STYLE_HTML)
3836 if (!(xop->xo_flags & XOF_ANCHOR)) {
3837 xo_failure(xop, "no start anchor");
3841 xop->xo_flags &= ~XOF_UNITS_PENDING;
3843 int width = xo_find_width(xop, str, len, fmt, flen);
3845 width = xop->xo_anchor_min_width;
3847 if (width == 0) /* No width given; nothing to do */
3850 xo_buffer_t *xbp = &xop->xo_data;
3851 int start = xop->xo_anchor_offset;
3852 int stop = xbp->xb_curp - xbp->xb_bufp;
3853 int abswidth = (width > 0) ? width : -width;
3854 int blen = abswidth - xop->xo_anchor_columns;
3856 if (blen <= 0) /* Already over width */
3859 if (abswidth > XO_MAX_ANCHOR_WIDTH) {
3860 xo_failure(xop, "width over %u are not supported",
3861 XO_MAX_ANCHOR_WIDTH);
3865 /* Make a suitable padding field and emit it */
3866 char *buf = alloca(blen);
3867 memset(buf, ' ', blen);
3868 xo_format_content(xop, "padding", NULL, 1, buf, blen, NULL, 0);
3870 if (width < 0) /* Already left justified */
3873 int now = xbp->xb_curp - xbp->xb_bufp;
3874 int delta = now - stop;
3875 if (delta < 0) /* Strange; no output to move */
3879 * Now we're in it alright. We've need to insert the padding data
3880 * we just created (which might be an HTML <div> or text) before
3881 * the formatted data. We make a local copy, move it and then
3882 * insert our copy. We know there's room in the buffer, since
3883 * we're just moving this around.
3886 buf = alloca(delta); /* Expand buffer if needed */
3888 memcpy(buf, xbp->xb_bufp + stop, delta);
3889 memmove(xbp->xb_bufp + start + delta, xbp->xb_bufp + start, stop - start);
3890 memmove(xbp->xb_bufp + start, buf, delta);
3893 xo_anchor_clear(xop);
3897 xo_do_emit (xo_handle_t *xop, const char *fmt)
3900 const char *cp, *sp, *ep, *basep;
3902 int flush = (xop->xo_flags & XOF_FLUSH) ? 1 : 0;
3903 int flush_line = (xop->xo_flags & XOF_FLUSH_LINE) ? 1 : 0;
3905 xop->xo_columns = 0; /* Always reset it */
3907 for (cp = fmt; *cp; ) {
3910 if (flush_line && xo_flush_h(xop) < 0)
3915 } else if (*cp == '{') {
3916 if (cp[1] == '{') { /* Start of {{escaped braces}} */
3918 cp += 2; /* Skip over _both_ characters */
3919 for (sp = cp; *sp; sp++) {
3920 if (*sp == '}' && sp[1] == '}')
3924 xo_failure(xop, "missing closing '}}': %s", fmt);
3928 xo_format_text(xop, cp, sp - cp);
3930 /* Move along the string, but don't run off the end */
3931 if (*sp == '}' && sp[1] == '}')
3933 cp = *sp ? sp + 1 : sp;
3936 /* Else fall thru to the code below */
3940 for (sp = cp; *sp; sp++) {
3941 if (*sp == '{' || *sp == '\n')
3944 xo_format_text(xop, cp, sp - cp);
3953 * We are looking at the start of a field definition. The format is:
3954 * '{' modifiers ':' content [ '/' print-fmt [ '/' encode-fmt ]] '}'
3955 * Modifiers are optional and include the following field types:
3956 * 'D': decoration; something non-text and non-data (colons, commmas)
3957 * 'E': error message
3958 * 'L': label; text preceding data
3959 * 'N': note; text following data
3960 * 'P': padding; whitespace
3961 * 'T': Title, where 'content' is a column title
3962 * 'U': Units, where 'content' is the unit label
3963 * 'V': value, where 'content' is the name of the field (the default)
3964 * 'W': warning message
3965 * '[': start a section of anchored text
3966 * ']': end a section of anchored text
3967 * The following flags are also supported:
3968 * 'c': flag: emit a colon after the label
3969 * 'd': field is only emitted for display formats (text and html)
3970 * 'e': field is only emitted for encoding formats (xml and json)
3971 * 'k': this field is a key, suitable for XPath predicates
3972 * 'l': a leaf-list, a simple list of values
3973 * 'n': no quotes around this field
3974 * 'q': add quotes around this field
3975 * 't': trim whitespace around the value
3976 * 'w': emit a blank after the label
3977 * The print-fmt and encode-fmt strings is the printf-style formating
3978 * for this data. JSON and XML will use the encoding-fmt, if present.
3979 * If the encode-fmt is not provided, it defaults to the print-fmt.
3980 * If the print-fmt is not provided, it defaults to 's'.
3982 unsigned ftype = 0, flags = 0;
3983 const char *content = NULL, *format = NULL, *encoding = NULL;
3984 int clen = 0, flen = 0, elen = 0;
3986 for (sp = basep; sp; sp++) {
3987 if (*sp == ':' || *sp == '/' || *sp == '}')
3991 if (sp[1] == '\0') {
3992 xo_failure(xop, "backslash at the end of string");
4013 xo_failure(xop, "field descriptor uses multiple types: %s",
4025 flags |= XFF_DISPLAY_ONLY;
4029 flags |= XFF_ENCODE_ONLY;
4037 flags |= XFF_LEAF_LIST;
4041 flags |= XFF_NOQUOTE;
4049 flags |= XFF_TRIM_WS;
4057 xo_failure(xop, "field descriptor uses unknown modifier: %s",
4060 * No good answer here; a bad format will likely
4061 * mean a core file. We just return and hope
4062 * the caller notices there's no output, and while
4063 * that seems, well, bad. There's nothing better.
4070 for (ep = ++sp; *sp; sp++) {
4071 if (*sp == '}' || *sp == '/')
4074 if (sp[1] == '\0') {
4075 xo_failure(xop, "backslash at the end of string");
4087 xo_failure(xop, "missing content (':'): %s", fmt);
4092 for (ep = ++sp; *sp; sp++) {
4093 if (*sp == '}' || *sp == '/')
4096 if (sp[1] == '\0') {
4097 xo_failure(xop, "backslash at the end of string");
4109 for (ep = ++sp; *sp; sp++) {
4120 xo_failure(xop, "missing closing '}': %s", fmt);
4124 if (ftype == 0 || ftype == 'V') {
4125 if (format == NULL) {
4126 /* Default format for value fields is '%s' */
4131 xo_format_value(xop, content, clen, format, flen,
4132 encoding, elen, flags);
4134 } else if (ftype == '[')
4135 xo_anchor_start(xop, content, clen, format, flen);
4136 else if (ftype == ']')
4137 xo_anchor_stop(xop, content, clen, format, flen);
4138 else if (ftype == 'C')
4139 xo_format_colors(xop, content, clen, format, flen);
4141 else if (clen || format) { /* Need either content or format */
4142 if (format == NULL) {
4143 /* Default format for value fields is '%s' */
4149 xo_format_content(xop, "decoration", NULL, 1,
4150 content, clen, format, flen);
4151 else if (ftype == 'E')
4152 xo_format_content(xop, "error", "error", 0,
4153 content, clen, format, flen);
4154 else if (ftype == 'L')
4155 xo_format_content(xop, "label", NULL, 1,
4156 content, clen, format, flen);
4157 else if (ftype == 'N')
4158 xo_format_content(xop, "note", NULL, 1,
4159 content, clen, format, flen);
4160 else if (ftype == 'P')
4161 xo_format_content(xop, "padding", NULL, 1,
4162 content, clen, format, flen);
4163 else if (ftype == 'T')
4164 xo_format_title(xop, content, clen, format, flen);
4165 else if (ftype == 'U') {
4167 xo_format_content(xop, "padding", NULL, 1, " ", 1, NULL, 0);
4168 xo_format_units(xop, content, clen, format, flen);
4169 } else if (ftype == 'W')
4170 xo_format_content(xop, "warning", "warning", 0,
4171 content, clen, format, flen);
4174 if (flags & XFF_COLON)
4175 xo_format_content(xop, "decoration", NULL, 1, ":", 1, NULL, 0);
4176 if (ftype != 'U' && (flags & XFF_WS))
4177 xo_format_content(xop, "padding", NULL, 1, " ", 1, NULL, 0);
4179 cp += sp - basep + 1;
4186 /* If we don't have an anchor, write the text out */
4187 if (flush && !(xop->xo_flags & XOF_ANCHOR)) {
4188 if (xo_write(xop) < 0)
4189 rc = -1; /* Report failure */
4190 else if (xop->xo_flush && xop->xo_flush(xop->xo_opaque) < 0)
4194 return (rc < 0) ? rc : (int) xop->xo_columns;
4198 xo_emit_hv (xo_handle_t *xop, const char *fmt, va_list vap)
4202 xop = xo_default(xop);
4203 va_copy(xop->xo_vap, vap);
4204 rc = xo_do_emit(xop, fmt);
4205 va_end(xop->xo_vap);
4206 bzero(&xop->xo_vap, sizeof(xop->xo_vap));
4212 xo_emit_h (xo_handle_t *xop, const char *fmt, ...)
4216 xop = xo_default(xop);
4217 va_start(xop->xo_vap, fmt);
4218 rc = xo_do_emit(xop, fmt);
4219 va_end(xop->xo_vap);
4220 bzero(&xop->xo_vap, sizeof(xop->xo_vap));
4226 xo_emit (const char *fmt, ...)
4228 xo_handle_t *xop = xo_default(NULL);
4231 va_start(xop->xo_vap, fmt);
4232 rc = xo_do_emit(xop, fmt);
4233 va_end(xop->xo_vap);
4234 bzero(&xop->xo_vap, sizeof(xop->xo_vap));
4240 xo_attr_hv (xo_handle_t *xop, const char *name, const char *fmt, va_list vap)
4242 const int extra = 5; /* space, equals, quote, quote, and nul */
4243 xop = xo_default(xop);
4245 if (xo_style(xop) != XO_STYLE_XML)
4248 int nlen = strlen(name);
4249 xo_buffer_t *xbp = &xop->xo_attrs;
4251 if (!xo_buf_has_room(xbp, nlen + extra))
4254 *xbp->xb_curp++ = ' ';
4255 memcpy(xbp->xb_curp, name, nlen);
4256 xbp->xb_curp += nlen;
4257 *xbp->xb_curp++ = '=';
4258 *xbp->xb_curp++ = '"';
4260 int rc = xo_vsnprintf(xop, xbp, fmt, vap);
4263 rc = xo_escape_xml(xbp, rc, 1);
4267 if (!xo_buf_has_room(xbp, 2))
4270 *xbp->xb_curp++ = '"';
4271 *xbp->xb_curp = '\0';
4273 return rc + nlen + extra;
4277 xo_attr_h (xo_handle_t *xop, const char *name, const char *fmt, ...)
4283 rc = xo_attr_hv(xop, name, fmt, vap);
4290 xo_attr (const char *name, const char *fmt, ...)
4296 rc = xo_attr_hv(NULL, name, fmt, vap);
4303 xo_stack_set_flags (xo_handle_t *xop)
4305 if (xop->xo_flags & XOF_NOT_FIRST) {
4306 xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth];
4308 xsp->xs_flags |= XSF_NOT_FIRST;
4309 xop->xo_flags &= ~XOF_NOT_FIRST;
4314 xo_depth_change (xo_handle_t *xop, const char *name,
4315 int delta, int indent, xo_state_t state, xo_xsf_flags_t flags)
4317 if (xo_style(xop) == XO_STYLE_HTML || xo_style(xop) == XO_STYLE_TEXT)
4320 if (xop->xo_flags & XOF_DTRT)
4323 if (delta >= 0) { /* Push operation */
4324 if (xo_depth_check(xop, xop->xo_depth + delta))
4327 xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth + delta];
4328 xsp->xs_flags = flags;
4329 xsp->xs_state = state;
4330 xo_stack_set_flags(xop);
4333 name = XO_FAILURE_NAME;
4335 int len = strlen(name) + 1;
4336 char *cp = xo_realloc(NULL, len);
4338 memcpy(cp, name, len);
4342 } else { /* Pop operation */
4343 if (xop->xo_depth == 0) {
4344 if (!(xop->xo_flags & XOF_IGNORE_CLOSE))
4345 xo_failure(xop, "close with empty stack: '%s'", name);
4349 xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth];
4350 if (xop->xo_flags & XOF_WARN) {
4351 const char *top = xsp->xs_name;
4352 if (top && strcmp(name, top) != 0) {
4353 xo_failure(xop, "incorrect close: '%s' .vs. '%s'",
4357 if ((xsp->xs_flags & XSF_LIST) != (flags & XSF_LIST)) {
4358 xo_failure(xop, "list close on list confict: '%s'",
4362 if ((xsp->xs_flags & XSF_INSTANCE) != (flags & XSF_INSTANCE)) {
4363 xo_failure(xop, "list close on instance confict: '%s'",
4370 xo_free(xsp->xs_name);
4371 xsp->xs_name = NULL;
4374 xo_free(xsp->xs_keys);
4375 xsp->xs_keys = NULL;
4379 xop->xo_depth += delta; /* Record new depth */
4380 xop->xo_indent += indent;
4384 xo_set_depth (xo_handle_t *xop, int depth)
4386 xop = xo_default(xop);
4388 if (xo_depth_check(xop, depth))
4391 xop->xo_depth += depth;
4392 xop->xo_indent += depth;
4395 static xo_xsf_flags_t
4396 xo_stack_flags (unsigned xflags)
4398 if (xflags & XOF_DTRT)
4404 xo_emit_top (xo_handle_t *xop, const char *ppn)
4406 xo_printf(xop, "%*s{%s", xo_indent(xop), "", ppn);
4407 xop->xo_flags |= XOF_TOP_EMITTED;
4409 if (xop->xo_version) {
4410 xo_printf(xop, "%*s\"__version\": \"%s\", %s",
4411 xo_indent(xop), "", xop->xo_version, ppn);
4412 xo_free(xop->xo_version);
4413 xop->xo_version = NULL;
4418 xo_do_open_container (xo_handle_t *xop, xo_xof_flags_t flags, const char *name)
4421 const char *ppn = (xop->xo_flags & XOF_PRETTY) ? "\n" : "";
4422 const char *pre_nl = "";
4425 xo_failure(xop, "NULL passed for container name");
4426 name = XO_FAILURE_NAME;
4429 flags |= xop->xo_flags; /* Pick up handle flags */
4431 switch (xo_style(xop)) {
4433 rc = xo_printf(xop, "%*s<%s", xo_indent(xop), "", name);
4435 if (xop->xo_attrs.xb_curp != xop->xo_attrs.xb_bufp) {
4436 rc += xop->xo_attrs.xb_curp - xop->xo_attrs.xb_bufp;
4437 xo_data_append(xop, xop->xo_attrs.xb_bufp,
4438 xop->xo_attrs.xb_curp - xop->xo_attrs.xb_bufp);
4439 xop->xo_attrs.xb_curp = xop->xo_attrs.xb_bufp;
4442 rc += xo_printf(xop, ">%s", ppn);
4446 xo_stack_set_flags(xop);
4448 if (!(xop->xo_flags & XOF_NO_TOP)
4449 && !(xop->xo_flags & XOF_TOP_EMITTED))
4450 xo_emit_top(xop, ppn);
4452 if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST)
4453 pre_nl = (xop->xo_flags & XOF_PRETTY) ? ",\n" : ", ";
4454 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST;
4456 rc = xo_printf(xop, "%s%*s\"%s\": {%s",
4457 pre_nl, xo_indent(xop), "", name, ppn);
4461 xo_depth_change(xop, name, 1, 1, XSS_OPEN_CONTAINER,
4462 xo_stack_flags(flags));
4468 xo_open_container_hf (xo_handle_t *xop, xo_xof_flags_t flags, const char *name)
4470 return xo_transition(xop, flags, name, XSS_OPEN_CONTAINER);
4474 xo_open_container_h (xo_handle_t *xop, const char *name)
4476 return xo_open_container_hf(xop, 0, name);
4480 xo_open_container (const char *name)
4482 return xo_open_container_hf(NULL, 0, name);
4486 xo_open_container_hd (xo_handle_t *xop, const char *name)
4488 return xo_open_container_hf(xop, XOF_DTRT, name);
4492 xo_open_container_d (const char *name)
4494 return xo_open_container_hf(NULL, XOF_DTRT, name);
4498 xo_do_close_container (xo_handle_t *xop, const char *name)
4500 xop = xo_default(xop);
4503 const char *ppn = (xop->xo_flags & XOF_PRETTY) ? "\n" : "";
4504 const char *pre_nl = "";
4507 xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth];
4509 name = xsp->xs_name;
4511 int len = strlen(name) + 1;
4512 /* We need to make a local copy; xo_depth_change will free it */
4513 char *cp = alloca(len);
4514 memcpy(cp, name, len);
4516 } else if (!(xsp->xs_flags & XSF_DTRT)) {
4517 xo_failure(xop, "missing name without 'dtrt' mode");
4518 name = XO_FAILURE_NAME;
4522 switch (xo_style(xop)) {
4524 xo_depth_change(xop, name, -1, -1, XSS_CLOSE_CONTAINER, 0);
4525 rc = xo_printf(xop, "%*s</%s>%s", xo_indent(xop), "", name, ppn);
4529 pre_nl = (xop->xo_flags & XOF_PRETTY) ? "\n" : "";
4530 ppn = (xop->xo_depth <= 1) ? "\n" : "";
4532 xo_depth_change(xop, name, -1, -1, XSS_CLOSE_CONTAINER, 0);
4533 rc = xo_printf(xop, "%s%*s}%s", pre_nl, xo_indent(xop), "", ppn);
4534 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST;
4539 xo_depth_change(xop, name, -1, 0, XSS_CLOSE_CONTAINER, 0);
4547 xo_close_container_h (xo_handle_t *xop, const char *name)
4549 return xo_transition(xop, 0, name, XSS_CLOSE_CONTAINER);
4553 xo_close_container (const char *name)
4555 return xo_close_container_h(NULL, name);
4559 xo_close_container_hd (xo_handle_t *xop)
4561 return xo_close_container_h(xop, NULL);
4565 xo_close_container_d (void)
4567 return xo_close_container_h(NULL, NULL);
4571 xo_do_open_list (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name)
4576 xop = xo_default(xop);
4578 if (xo_style(xop) == XO_STYLE_JSON) {
4579 const char *ppn = (xop->xo_flags & XOF_PRETTY) ? "\n" : "";
4580 const char *pre_nl = "";
4583 if (!(xop->xo_flags & XOF_NO_TOP)
4584 && !(xop->xo_flags & XOF_TOP_EMITTED))
4585 xo_emit_top(xop, ppn);
4588 xo_failure(xop, "NULL passed for list name");
4589 name = XO_FAILURE_NAME;
4592 xo_stack_set_flags(xop);
4594 if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST)
4595 pre_nl = (xop->xo_flags & XOF_PRETTY) ? ",\n" : ", ";
4596 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST;
4598 rc = xo_printf(xop, "%s%*s\"%s\": [%s",
4599 pre_nl, xo_indent(xop), "", name, ppn);
4602 xo_depth_change(xop, name, 1, indent, XSS_OPEN_LIST,
4603 XSF_LIST | xo_stack_flags(flags));
4609 xo_open_list_hf (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name)
4611 return xo_transition(xop, flags, name, XSS_OPEN_LIST);
4615 xo_open_list_h (xo_handle_t *xop, const char *name UNUSED)
4617 return xo_open_list_hf(xop, 0, name);
4621 xo_open_list (const char *name)
4623 return xo_open_list_hf(NULL, 0, name);
4627 xo_open_list_hd (xo_handle_t *xop, const char *name UNUSED)
4629 return xo_open_list_hf(xop, XOF_DTRT, name);
4633 xo_open_list_d (const char *name)
4635 return xo_open_list_hf(NULL, XOF_DTRT, name);
4639 xo_do_close_list (xo_handle_t *xop, const char *name)
4642 const char *pre_nl = "";
4645 xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth];
4647 name = xsp->xs_name;
4649 int len = strlen(name) + 1;
4650 /* We need to make a local copy; xo_depth_change will free it */
4651 char *cp = alloca(len);
4652 memcpy(cp, name, len);
4654 } else if (!(xsp->xs_flags & XSF_DTRT)) {
4655 xo_failure(xop, "missing name without 'dtrt' mode");
4656 name = XO_FAILURE_NAME;
4660 if (xo_style(xop) == XO_STYLE_JSON) {
4661 if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST)
4662 pre_nl = (xop->xo_flags & XOF_PRETTY) ? "\n" : "";
4663 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST;
4665 xo_depth_change(xop, name, -1, -1, XSS_CLOSE_LIST, XSF_LIST);
4666 rc = xo_printf(xop, "%s%*s]", pre_nl, xo_indent(xop), "");
4667 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST;
4670 xo_depth_change(xop, name, -1, 0, XSS_CLOSE_LIST, XSF_LIST);
4671 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST;
4678 xo_close_list_h (xo_handle_t *xop, const char *name)
4680 return xo_transition(xop, 0, name, XSS_CLOSE_LIST);
4684 xo_close_list (const char *name)
4686 return xo_close_list_h(NULL, name);
4690 xo_close_list_hd (xo_handle_t *xop)
4692 return xo_close_list_h(xop, NULL);
4696 xo_close_list_d (void)
4698 return xo_close_list_h(NULL, NULL);
4702 xo_do_open_leaf_list (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name)
4707 xop = xo_default(xop);
4709 if (xo_style(xop) == XO_STYLE_JSON) {
4710 const char *ppn = (xop->xo_flags & XOF_PRETTY) ? "\n" : "";
4711 const char *pre_nl = "";
4715 if (!(xop->xo_flags & XOF_NO_TOP)) {
4716 if (!(xop->xo_flags & XOF_TOP_EMITTED)) {
4717 xo_printf(xop, "%*s{%s", xo_indent(xop), "", ppn);
4718 xop->xo_flags |= XOF_TOP_EMITTED;
4723 xo_failure(xop, "NULL passed for list name");
4724 name = XO_FAILURE_NAME;
4727 xo_stack_set_flags(xop);
4729 if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST)
4730 pre_nl = (xop->xo_flags & XOF_PRETTY) ? ",\n" : ", ";
4731 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST;
4733 rc = xo_printf(xop, "%s%*s\"%s\": [%s",
4734 pre_nl, xo_indent(xop), "", name, ppn);
4737 xo_depth_change(xop, name, 1, indent, XSS_OPEN_LEAF_LIST,
4738 XSF_LIST | xo_stack_flags(flags));
4744 xo_do_close_leaf_list (xo_handle_t *xop, const char *name)
4747 const char *pre_nl = "";
4750 xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth];
4752 name = xsp->xs_name;
4754 int len = strlen(name) + 1;
4755 /* We need to make a local copy; xo_depth_change will free it */
4756 char *cp = alloca(len);
4757 memcpy(cp, name, len);
4759 } else if (!(xsp->xs_flags & XSF_DTRT)) {
4760 xo_failure(xop, "missing name without 'dtrt' mode");
4761 name = XO_FAILURE_NAME;
4765 if (xo_style(xop) == XO_STYLE_JSON) {
4766 if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST)
4767 pre_nl = (xop->xo_flags & XOF_PRETTY) ? "\n" : "";
4768 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST;
4770 xo_depth_change(xop, name, -1, -1, XSS_CLOSE_LEAF_LIST, XSF_LIST);
4771 rc = xo_printf(xop, "%s%*s]", pre_nl, xo_indent(xop), "");
4772 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST;
4775 xo_depth_change(xop, name, -1, 0, XSS_CLOSE_LEAF_LIST, XSF_LIST);
4776 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST;
4783 xo_do_open_instance (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name)
4785 xop = xo_default(xop);
4788 const char *ppn = (xop->xo_flags & XOF_PRETTY) ? "\n" : "";
4789 const char *pre_nl = "";
4791 flags |= xop->xo_flags;
4794 xo_failure(xop, "NULL passed for instance name");
4795 name = XO_FAILURE_NAME;
4798 switch (xo_style(xop)) {
4800 rc = xo_printf(xop, "%*s<%s", xo_indent(xop), "", name);
4802 if (xop->xo_attrs.xb_curp != xop->xo_attrs.xb_bufp) {
4803 rc += xop->xo_attrs.xb_curp - xop->xo_attrs.xb_bufp;
4804 xo_data_append(xop, xop->xo_attrs.xb_bufp,
4805 xop->xo_attrs.xb_curp - xop->xo_attrs.xb_bufp);
4806 xop->xo_attrs.xb_curp = xop->xo_attrs.xb_bufp;
4809 rc += xo_printf(xop, ">%s", ppn);
4813 xo_stack_set_flags(xop);
4815 if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST)
4816 pre_nl = (xop->xo_flags & XOF_PRETTY) ? ",\n" : ", ";
4817 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST;
4819 rc = xo_printf(xop, "%s%*s{%s",
4820 pre_nl, xo_indent(xop), "", ppn);
4824 xo_depth_change(xop, name, 1, 1, XSS_OPEN_INSTANCE, xo_stack_flags(flags));
4830 xo_open_instance_hf (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name)
4832 return xo_transition(xop, flags, name, XSS_OPEN_INSTANCE);
4836 xo_open_instance_h (xo_handle_t *xop, const char *name)
4838 return xo_open_instance_hf(xop, 0, name);
4842 xo_open_instance (const char *name)
4844 return xo_open_instance_hf(NULL, 0, name);
4848 xo_open_instance_hd (xo_handle_t *xop, const char *name)
4850 return xo_open_instance_hf(xop, XOF_DTRT, name);
4854 xo_open_instance_d (const char *name)
4856 return xo_open_instance_hf(NULL, XOF_DTRT, name);
4860 xo_do_close_instance (xo_handle_t *xop, const char *name)
4862 xop = xo_default(xop);
4865 const char *ppn = (xop->xo_flags & XOF_PRETTY) ? "\n" : "";
4866 const char *pre_nl = "";
4869 xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth];
4871 name = xsp->xs_name;
4873 int len = strlen(name) + 1;
4874 /* We need to make a local copy; xo_depth_change will free it */
4875 char *cp = alloca(len);
4876 memcpy(cp, name, len);
4878 } else if (!(xsp->xs_flags & XSF_DTRT)) {
4879 xo_failure(xop, "missing name without 'dtrt' mode");
4880 name = XO_FAILURE_NAME;
4884 switch (xo_style(xop)) {
4886 xo_depth_change(xop, name, -1, -1, XSS_CLOSE_INSTANCE, 0);
4887 rc = xo_printf(xop, "%*s</%s>%s", xo_indent(xop), "", name, ppn);
4891 pre_nl = (xop->xo_flags & XOF_PRETTY) ? "\n" : "";
4893 xo_depth_change(xop, name, -1, -1, XSS_CLOSE_INSTANCE, 0);
4894 rc = xo_printf(xop, "%s%*s}", pre_nl, xo_indent(xop), "");
4895 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST;
4900 xo_depth_change(xop, name, -1, 0, XSS_CLOSE_INSTANCE, 0);
4908 xo_close_instance_h (xo_handle_t *xop, const char *name)
4910 return xo_transition(xop, 0, name, XSS_CLOSE_INSTANCE);
4914 xo_close_instance (const char *name)
4916 return xo_close_instance_h(NULL, name);
4920 xo_close_instance_hd (xo_handle_t *xop)
4922 return xo_close_instance_h(xop, NULL);
4926 xo_close_instance_d (void)
4928 return xo_close_instance_h(NULL, NULL);
4932 xo_do_close_all (xo_handle_t *xop, xo_stack_t *limit)
4936 xo_xsf_flags_t flags;
4938 for (xsp = &xop->xo_stack[xop->xo_depth]; xsp >= limit; xsp--) {
4939 switch (xsp->xs_state) {
4945 case XSS_OPEN_CONTAINER:
4946 rc = xo_do_close_container(xop, NULL);
4950 rc = xo_do_close_list(xop, NULL);
4953 case XSS_OPEN_INSTANCE:
4954 rc = xo_do_close_instance(xop, NULL);
4957 case XSS_OPEN_LEAF_LIST:
4958 rc = xo_do_close_leaf_list(xop, NULL);
4962 flags = xsp->xs_flags & XSF_MARKER_FLAGS;
4963 xo_depth_change(xop, xsp->xs_name, -1, 0, XSS_MARKER, 0);
4964 xop->xo_stack[xop->xo_depth].xs_flags |= flags;
4970 xo_failure(xop, "close %d failed: %d", xsp->xs_state, rc);
4977 * This function is responsible for clearing out whatever is needed
4978 * to get to the desired state, if possible.
4981 xo_do_close (xo_handle_t *xop, const char *name, xo_state_t new_state)
4983 xo_stack_t *xsp, *limit = NULL;
4985 xo_state_t need_state = new_state;
4987 if (new_state == XSS_CLOSE_CONTAINER)
4988 need_state = XSS_OPEN_CONTAINER;
4989 else if (new_state == XSS_CLOSE_LIST)
4990 need_state = XSS_OPEN_LIST;
4991 else if (new_state == XSS_CLOSE_INSTANCE)
4992 need_state = XSS_OPEN_INSTANCE;
4993 else if (new_state == XSS_CLOSE_LEAF_LIST)
4994 need_state = XSS_OPEN_LEAF_LIST;
4995 else if (new_state == XSS_MARKER)
4996 need_state = XSS_MARKER;
4998 return 0; /* Unknown or useless new states are ignored */
5000 for (xsp = &xop->xo_stack[xop->xo_depth]; xsp > xop->xo_stack; xsp--) {
5002 * Marker's normally stop us from going any further, unless
5003 * we are popping a marker (new_state == XSS_MARKER).
5005 if (xsp->xs_state == XSS_MARKER && need_state != XSS_MARKER) {
5007 xo_failure(xop, "close (xo_%s) fails at marker '%s'; "
5009 xo_state_name(new_state),
5010 xsp->xs_name, name);
5015 xo_failure(xop, "close stops at marker '%s'", xsp->xs_name);
5020 if (xsp->xs_state != need_state)
5023 if (name && xsp->xs_name && strcmp(name, xsp->xs_name) != 0)
5030 if (limit == NULL) {
5031 xo_failure(xop, "xo_%s can't find match for '%s'",
5032 xo_state_name(new_state), name);
5036 rc = xo_do_close_all(xop, limit);
5042 * We are in a given state and need to transition to the new state.
5045 xo_transition (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name,
5046 xo_state_t new_state)
5050 int old_state, on_marker;
5052 xop = xo_default(xop);
5055 xsp = &xop->xo_stack[xop->xo_depth];
5056 old_state = xsp->xs_state;
5057 on_marker = (old_state == XSS_MARKER);
5059 /* If there's a marker on top of the stack, we need to find a real state */
5060 while (old_state == XSS_MARKER) {
5061 if (xsp == xop->xo_stack)
5064 old_state = xsp->xs_state;
5068 * At this point, the list of possible states are:
5069 * XSS_INIT, XSS_OPEN_CONTAINER, XSS_OPEN_LIST,
5070 * XSS_OPEN_INSTANCE, XSS_OPEN_LEAF_LIST, XSS_DISCARDING
5072 switch (XSS_TRANSITION(old_state, new_state)) {
5075 case XSS_TRANSITION(XSS_INIT, XSS_OPEN_CONTAINER):
5076 case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_OPEN_CONTAINER):
5077 case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_OPEN_CONTAINER):
5078 rc = xo_do_open_container(xop, flags, name);
5081 case XSS_TRANSITION(XSS_OPEN_LIST, XSS_OPEN_CONTAINER):
5083 goto marker_prevents_close;
5084 rc = xo_do_close_list(xop, NULL);
5086 goto open_container;
5089 case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_OPEN_CONTAINER):
5091 goto marker_prevents_close;
5092 rc = xo_do_close_leaf_list(xop, NULL);
5094 goto open_container;
5097 /*close_container:*/
5098 case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_CLOSE_CONTAINER):
5100 goto marker_prevents_close;
5101 rc = xo_do_close(xop, name, new_state);
5104 case XSS_TRANSITION(XSS_INIT, XSS_CLOSE_CONTAINER):
5105 /* This is an exception for "xo --close" */
5106 rc = xo_do_close_container(xop, name);
5109 case XSS_TRANSITION(XSS_OPEN_LIST, XSS_CLOSE_CONTAINER):
5110 case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_CLOSE_CONTAINER):
5112 goto marker_prevents_close;
5113 rc = xo_do_close(xop, name, new_state);
5116 case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_CLOSE_CONTAINER):
5118 goto marker_prevents_close;
5119 rc = xo_do_close_leaf_list(xop, NULL);
5121 rc = xo_do_close(xop, name, new_state);
5125 case XSS_TRANSITION(XSS_INIT, XSS_OPEN_LIST):
5126 case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_OPEN_LIST):
5127 case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_OPEN_LIST):
5128 rc = xo_do_open_list(xop, flags, name);
5131 case XSS_TRANSITION(XSS_OPEN_LIST, XSS_OPEN_LIST):
5133 goto marker_prevents_close;
5134 rc = xo_do_close_list(xop, NULL);
5139 case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_OPEN_LIST):
5141 goto marker_prevents_close;
5142 rc = xo_do_close_leaf_list(xop, NULL);
5148 case XSS_TRANSITION(XSS_OPEN_LIST, XSS_CLOSE_LIST):
5150 goto marker_prevents_close;
5151 rc = xo_do_close(xop, name, new_state);
5154 case XSS_TRANSITION(XSS_INIT, XSS_CLOSE_LIST):
5155 case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_CLOSE_LIST):
5156 case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_CLOSE_LIST):
5157 case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_CLOSE_LIST):
5158 rc = xo_do_close(xop, name, new_state);
5162 case XSS_TRANSITION(XSS_OPEN_LIST, XSS_OPEN_INSTANCE):
5163 rc = xo_do_open_instance(xop, flags, name);
5166 case XSS_TRANSITION(XSS_INIT, XSS_OPEN_INSTANCE):
5167 case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_OPEN_INSTANCE):
5168 rc = xo_do_open_list(xop, flags, name);
5173 case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_OPEN_INSTANCE):
5175 rc = xo_do_open_list(xop, flags, name);
5177 rc = xo_do_close_instance(xop, NULL);
5183 case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_OPEN_INSTANCE):
5185 goto marker_prevents_close;
5186 rc = xo_do_close_leaf_list(xop, NULL);
5192 case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_CLOSE_INSTANCE):
5194 goto marker_prevents_close;
5195 rc = xo_do_close_instance(xop, name);
5198 case XSS_TRANSITION(XSS_INIT, XSS_CLOSE_INSTANCE):
5199 /* This one makes no sense; ignore it */
5200 xo_failure(xop, "xo_close_instance ignored when called from "
5201 "initial state ('%s')", name ?: "(unknown)");
5204 case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_CLOSE_INSTANCE):
5205 case XSS_TRANSITION(XSS_OPEN_LIST, XSS_CLOSE_INSTANCE):
5207 goto marker_prevents_close;
5208 rc = xo_do_close(xop, name, new_state);
5211 case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_CLOSE_INSTANCE):
5213 goto marker_prevents_close;
5214 rc = xo_do_close_leaf_list(xop, NULL);
5216 rc = xo_do_close(xop, name, new_state);
5220 case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_OPEN_LEAF_LIST):
5221 case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_OPEN_LEAF_LIST):
5222 case XSS_TRANSITION(XSS_INIT, XSS_OPEN_LEAF_LIST):
5223 rc = xo_do_open_leaf_list(xop, flags, name);
5226 case XSS_TRANSITION(XSS_OPEN_LIST, XSS_OPEN_LEAF_LIST):
5227 case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_OPEN_LEAF_LIST):
5229 goto marker_prevents_close;
5230 rc = xo_do_close_list(xop, NULL);
5232 goto open_leaf_list;
5235 /*close_leaf_list:*/
5236 case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_CLOSE_LEAF_LIST):
5238 goto marker_prevents_close;
5239 rc = xo_do_close_leaf_list(xop, name);
5242 case XSS_TRANSITION(XSS_INIT, XSS_CLOSE_LEAF_LIST):
5243 /* Makes no sense; ignore */
5244 xo_failure(xop, "xo_close_leaf_list ignored when called from "
5245 "initial state ('%s')", name ?: "(unknown)");
5248 case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_CLOSE_LEAF_LIST):
5249 case XSS_TRANSITION(XSS_OPEN_LIST, XSS_CLOSE_LEAF_LIST):
5250 case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_CLOSE_LEAF_LIST):
5252 goto marker_prevents_close;
5253 rc = xo_do_close(xop, name, new_state);
5257 case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_EMIT):
5258 case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_EMIT):
5261 case XSS_TRANSITION(XSS_OPEN_LIST, XSS_EMIT):
5263 goto marker_prevents_close;
5264 rc = xo_do_close(xop, NULL, XSS_CLOSE_LIST);
5267 case XSS_TRANSITION(XSS_INIT, XSS_EMIT):
5270 case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_EMIT):
5272 goto marker_prevents_close;
5273 rc = xo_do_close_leaf_list(xop, NULL);
5277 case XSS_TRANSITION(XSS_INIT, XSS_EMIT_LEAF_LIST):
5278 case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_EMIT_LEAF_LIST):
5279 case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_EMIT_LEAF_LIST):
5280 rc = xo_do_open_leaf_list(xop, flags, name);
5283 case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_EMIT_LEAF_LIST):
5286 case XSS_TRANSITION(XSS_OPEN_LIST, XSS_EMIT_LEAF_LIST):
5288 * We need to be backward compatible with the pre-xo_open_leaf_list
5289 * API, where both lists and leaf-lists were opened as lists. So
5290 * if we find an open list that hasn't had anything written to it,
5296 xo_failure(xop, "unknown transition: (%u -> %u)",
5297 xsp->xs_state, new_state);
5302 marker_prevents_close:
5303 xo_failure(xop, "marker '%s' prevents transition from %s to %s",
5304 xop->xo_stack[xop->xo_depth].xs_name,
5305 xo_state_name(old_state), xo_state_name(new_state));
5310 xo_open_marker_h (xo_handle_t *xop, const char *name)
5312 xop = xo_default(xop);
5314 xo_depth_change(xop, name, 1, 0, XSS_MARKER,
5315 xop->xo_stack[xop->xo_depth].xs_flags & XSF_MARKER_FLAGS);
5321 xo_open_marker (const char *name)
5323 return xo_open_marker_h(NULL, name);
5327 xo_close_marker_h (xo_handle_t *xop, const char *name)
5329 xop = xo_default(xop);
5331 return xo_do_close(xop, name, XSS_MARKER);
5335 xo_close_marker (const char *name)
5337 return xo_close_marker_h(NULL, name);
5341 xo_set_writer (xo_handle_t *xop, void *opaque, xo_write_func_t write_func,
5342 xo_close_func_t close_func, xo_flush_func_t flush_func)
5344 xop = xo_default(xop);
5346 xop->xo_opaque = opaque;
5347 xop->xo_write = write_func;
5348 xop->xo_close = close_func;
5349 xop->xo_flush = flush_func;
5353 xo_set_allocator (xo_realloc_func_t realloc_func, xo_free_func_t free_func)
5355 xo_realloc = realloc_func;
5356 xo_free = free_func;
5360 xo_flush_h (xo_handle_t *xop)
5362 static char div_close[] = "</div>";
5365 xop = xo_default(xop);
5367 switch (xo_style(xop)) {
5369 if (xop->xo_flags & XOF_DIV_OPEN) {
5370 xop->xo_flags &= ~XOF_DIV_OPEN;
5371 xo_data_append(xop, div_close, sizeof(div_close) - 1);
5373 if (xop->xo_flags & XOF_PRETTY)
5374 xo_data_append(xop, "\n", 1);
5380 if (rc >= 0 && xop->xo_flush)
5381 if (xop->xo_flush(xop->xo_opaque) < 0)
5390 return xo_flush_h(NULL);
5394 xo_finish_h (xo_handle_t *xop)
5396 const char *cp = "";
5397 xop = xo_default(xop);
5399 if (!(xop->xo_flags & XOF_NO_CLOSE))
5400 xo_do_close_all(xop, xop->xo_stack);
5402 switch (xo_style(xop)) {
5404 if (!(xop->xo_flags & XOF_NO_TOP)) {
5405 if (xop->xo_flags & XOF_TOP_EMITTED)
5406 xop->xo_flags &= ~XOF_TOP_EMITTED; /* Turn off before output */
5409 xo_printf(xop, "%*s%s}\n",xo_indent(xop), "", cp);
5414 return xo_flush_h(xop);
5420 return xo_finish_h(NULL);
5424 * Generate an error message, such as would be displayed on stderr
5427 xo_error_hv (xo_handle_t *xop, const char *fmt, va_list vap)
5429 xop = xo_default(xop);
5432 * If the format string doesn't end with a newline, we pop
5435 int len = strlen(fmt);
5436 if (len > 0 && fmt[len - 1] != '\n') {
5437 char *newfmt = alloca(len + 2);
5438 memcpy(newfmt, fmt, len);
5444 switch (xo_style(xop)) {
5446 vfprintf(stderr, fmt, vap);
5450 va_copy(xop->xo_vap, vap);
5452 xo_buf_append_div(xop, "error", 0, NULL, 0, fmt, strlen(fmt), NULL, 0);
5454 if (xop->xo_flags & XOF_DIV_OPEN)
5459 va_end(xop->xo_vap);
5460 bzero(&xop->xo_vap, sizeof(xop->xo_vap));
5465 va_copy(xop->xo_vap, vap);
5467 xo_open_container_h(xop, "error");
5468 xo_format_value(xop, "message", 7, fmt, strlen(fmt), NULL, 0, 0);
5469 xo_close_container_h(xop, "error");
5471 va_end(xop->xo_vap);
5472 bzero(&xop->xo_vap, sizeof(xop->xo_vap));
5478 xo_error_h (xo_handle_t *xop, const char *fmt, ...)
5483 xo_error_hv(xop, fmt, vap);
5488 * Generate an error message, such as would be displayed on stderr
5491 xo_error (const char *fmt, ...)
5496 xo_error_hv(NULL, fmt, vap);
5501 xo_parse_args (int argc, char **argv)
5503 static char libxo_opt[] = "--libxo";
5507 /* Save our program name for xo_err and friends */
5508 xo_program = argv[0];
5509 cp = strrchr(xo_program, '/');
5511 xo_program = cp + 1;
5513 for (save = i = 1; i < argc; i++) {
5515 || strncmp(argv[i], libxo_opt, sizeof(libxo_opt) - 1) != 0) {
5517 argv[save] = argv[i];
5522 cp = argv[i] + sizeof(libxo_opt) - 1;
5526 xo_warnx("missing libxo option");
5530 if (xo_set_options(NULL, cp) < 0)
5532 } else if (*cp == ':') {
5533 if (xo_set_options(NULL, cp) < 0)
5536 } else if (*cp == '=') {
5537 if (xo_set_options(NULL, ++cp) < 0)
5540 } else if (*cp == '-') {
5542 if (strcmp(cp, "check") == 0) {
5546 xo_warnx("unknown libxo option: '%s'", argv[i]);
5550 xo_warnx("unknown libxo option: '%s'", argv[i]);
5560 xo_dump_stack (xo_handle_t *xop)
5565 xop = xo_default(xop);
5567 fprintf(stderr, "Stack dump:\n");
5569 xsp = xop->xo_stack;
5570 for (i = 1, xsp++; i <= xop->xo_depth; i++, xsp++) {
5571 fprintf(stderr, " [%d] %s '%s' [%x]\n",
5572 i, xo_state_name(xsp->xs_state),
5573 xsp->xs_name ?: "--", xsp->xs_flags);
5578 xo_set_program (const char *name)
5584 xo_set_version_h (xo_handle_t *xop, const char *version UNUSED)
5586 xop = xo_default(xop);
5588 if (version == NULL || strchr(version, '"') != NULL)
5591 switch (xo_style(xop)) {
5593 /* For XML, we record this as an attribute for the first tag */
5594 xo_attr_h(xop, "__version", "%s", version);
5600 * For XML, we record the version string in our handle, and emit
5601 * it in xo_emit_top.
5603 int len = strlen(version) + 1;
5604 xop->xo_version = xo_realloc(NULL, len);
5605 if (xop->xo_version)
5606 memcpy(xop->xo_version, version, len);
5613 xo_set_version (const char *version)
5615 xo_set_version_h(NULL, version);
5620 main (int argc, char **argv)
5622 static char base_grocery[] = "GRO";
5623 static char base_hardware[] = "HRD";
5625 const char *i_title;
5629 const char *i_sku_base;
5632 struct item list[] = {
5633 { "gum&this&that", 1412, 54, 10, base_grocery, 415 },
5634 { "<rope>", 85, 4, 2, base_hardware, 212 },
5635 { "ladder", 0, 2, 1, base_hardware, 517 },
5636 { "\"bolt\"", 4123, 144, 42, base_hardware, 632 },
5637 { "water\\blue", 17, 14, 2, base_grocery, 2331 },
5638 { NULL, 0, 0, 0, NULL, 0 }
5640 struct item list2[] = {
5641 { "fish", 1321, 45, 1, base_grocery, 533 },
5642 { NULL, 0, 0, 0, NULL, 0 }
5645 xo_info_t info[] = {
5646 { "in-stock", "number", "Number of items in stock" },
5647 { "name", "string", "Name of the item" },
5648 { "on-order", "number", "Number of items on order" },
5649 { "sku", "string", "Stock Keeping Unit" },
5650 { "sold", "number", "Number of items sold" },
5651 { NULL, NULL, NULL },
5653 int info_count = (sizeof(info) / sizeof(info[0])) - 1;
5655 argc = xo_parse_args(argc, argv);
5659 xo_set_info(NULL, info, info_count);
5661 xo_open_container_h(NULL, "top");
5663 xo_open_container("data");
5664 xo_open_list("item");
5666 xo_emit("{T:Item/%-15s}{T:Total Sold/%12s}{T:In Stock/%12s}"
5667 "{T:On Order/%12s}{T:SKU/%5s}\n");
5669 for (ip = list; ip->i_title; ip++) {
5670 xo_open_instance("item");
5672 xo_emit("{k:name/%-15s/%s}{n:sold/%12u/%u}{:in-stock/%12u/%u}"
5673 "{:on-order/%12u/%u} {q:sku/%5s-000-%u/%s-000-%u}\n",
5674 ip->i_title, ip->i_sold, ip->i_instock, ip->i_onorder,
5675 ip->i_sku_base, ip->i_sku_num);
5677 xo_close_instance("item");
5680 xo_close_list("item");
5681 xo_close_container("data");
5685 xo_open_container("data");
5686 xo_open_list("item");
5688 for (ip = list; ip->i_title; ip++) {
5689 xo_open_instance("item");
5691 xo_attr("fancy", "%s%d", "item", ip - list);
5692 xo_emit("{L:Item} '{k:name/%s}':\n", ip->i_title);
5693 xo_emit("{P: }{L:Total sold}: {n:sold/%u%s}{e:percent/%u}\n",
5694 ip->i_sold, ip->i_sold ? ".0" : "", 44);
5695 xo_emit("{P: }{Lcw:In stock}{:in-stock/%u}\n", ip->i_instock);
5696 xo_emit("{P: }{Lcw:On order}{:on-order/%u}\n", ip->i_onorder);
5697 xo_emit("{P: }{L:SKU}: {q:sku/%s-000-%u}\n",
5698 ip->i_sku_base, ip->i_sku_num);
5700 xo_close_instance("item");
5703 xo_close_list("item");
5704 xo_close_container("data");
5706 xo_open_container("data");
5707 xo_open_list("item");
5709 for (ip = list2; ip->i_title; ip++) {
5710 xo_open_instance("item");
5712 xo_emit("{L:Item} '{k:name/%s}':\n", ip->i_title);
5713 xo_emit("{P: }{L:Total sold}: {n:sold/%u%s}\n",
5714 ip->i_sold, ip->i_sold ? ".0" : "");
5715 xo_emit("{P: }{Lcw:In stock}{:in-stock/%u}\n", ip->i_instock);
5716 xo_emit("{P: }{Lcw:On order}{:on-order/%u}\n", ip->i_onorder);
5717 xo_emit("{P: }{L:SKU}: {q:sku/%s-000-%u}\n",
5718 ip->i_sku_base, ip->i_sku_num);
5720 xo_open_list("month");
5722 const char *months[] = { "Jan", "Feb", "Mar", NULL };
5723 int discounts[] = { 10, 20, 25, 0 };
5725 for (i = 0; months[i]; i++) {
5726 xo_open_instance("month");
5728 "{Lwc:Month}{k:month}, {Lwc:Special}{:discount/%d}\n",
5729 months[i], discounts[i]);
5730 xo_close_instance("month");
5733 xo_close_list("month");
5735 xo_close_instance("item");
5738 xo_close_list("item");
5739 xo_close_container("data");
5741 xo_close_container_h(NULL, "top");
5747 #endif /* UNIT_TEST */