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
10 * This is the implementation of libxo, the formatting library that
11 * generates multiple styles of output from a single code path.
12 * Command line utilities can have their normal text output while
13 * automation tools can see XML or JSON output, and web tools can use
14 * HTML output that encodes the text output annotated with additional
15 * information. Specialized encoders can be built that allow custom
16 * encoding including binary ones like CBOR, thrift, protobufs, etc.
18 * Full documentation is available in ./doc/libxo.txt or online at:
19 * http://juniper.github.io/libxo/libxo-manual.html
21 * For first time readers, the core bits of code to start looking at are:
22 * - xo_do_emit() -- parse and emit a set of fields
23 * - xo_do_emit_fields -- the central function of the library
24 * - xo_do_format_field() -- handles formatting a single field
25 * - xo_transiton() -- the state machine that keeps things sane
26 * and of course the "xo_handle_t" data structure, which carries all
27 * configuration and state.
37 #include <sys/types.h>
46 #include "xo_config.h"
48 #include "xo_encoder.h"
52 * We ask wcwidth() to do an impossible job, really. It's supposed to
53 * need to tell us the number of columns consumed to display a unicode
54 * character. It returns that number without any sort of context, but
55 * we know they are characters whose glyph differs based on placement
56 * (end of word, middle of word, etc) and many that affect characters
57 * previously emitted. Without content, it can't hope to tell us.
58 * But it's the only standard tool we've got, so we use it. We would
59 * use wcswidth() but it typically just loops through adding the results
60 * of wcwidth() calls in an entirely unhelpful way.
62 * Even then, there are many poor implementations (macosx), so we have
63 * to carry our own. We could have configure.ac test this (with
64 * something like 'assert(wcwidth(0x200d) == 0)'), but it would have
65 * to run a binary, which breaks cross-compilation. Hmm... I could
66 * run this test at init time and make a warning for our dear user.
68 * Anyhow, it remains a best-effort sort of thing. And it's all made
69 * more hopeless because we assume the display code doing the rendering is
70 * playing by the same rules we are. If it display 0x200d as a square
71 * box or a funky question mark, the output will be hosed.
74 #include "xo_wcwidth.h"
75 #else /* LIBXO_WCWIDTH */
76 #define xo_wcwidth(_x) wcwidth(_x)
77 #endif /* LIBXO_WCWIDTH */
79 #ifdef HAVE_STDIO_EXT_H
80 #include <stdio_ext.h>
81 #endif /* HAVE_STDIO_EXT_H */
84 * humanize_number is a great function, unless you don't have it. So
85 * we carry one in our pocket.
87 #ifdef HAVE_HUMANIZE_NUMBER
89 #define xo_humanize_number humanize_number
90 #else /* HAVE_HUMANIZE_NUMBER */
91 #include "xo_humanize.h"
92 #endif /* HAVE_HUMANIZE_NUMBER */
96 #endif /* HAVE_GETTEXT */
99 * Three styles of specifying thread-local variables are supported.
100 * configure.ac has the brains to run each possibility through the
101 * compiler and see what works; we are left to define the THREAD_LOCAL
102 * macro to the right value. Most toolchains (clang, gcc) use
103 * "before", but some (borland) use "after" and I've heard of some
104 * (ms) that use __declspec. Any others out there?
106 #define THREAD_LOCAL_before 1
107 #define THREAD_LOCAL_after 2
108 #define THREAD_LOCAL_declspec 3
110 #ifndef HAVE_THREAD_LOCAL
111 #define THREAD_LOCAL(_x) _x
112 #elif HAVE_THREAD_LOCAL == THREAD_LOCAL_before
113 #define THREAD_LOCAL(_x) __thread _x
114 #elif HAVE_THREAD_LOCAL == THREAD_LOCAL_after
115 #define THREAD_LOCAL(_x) _x __thread
116 #elif HAVE_THREAD_LOCAL == THREAD_LOCAL_declspec
117 #define THREAD_LOCAL(_x) __declspec(_x)
119 #error unknown thread-local setting
120 #endif /* HAVE_THREADS_H */
122 const char xo_version[] = LIBXO_VERSION;
123 const char xo_version_extra[] = LIBXO_VERSION_EXTRA;
124 static const char xo_default_format[] = "%s";
127 #define UNUSED __attribute__ ((__unused__))
130 #define XO_INDENT_BY 2 /* Amount to indent when pretty printing */
131 #define XO_DEPTH 128 /* Default stack depth */
132 #define XO_MAX_ANCHOR_WIDTH (8*1024) /* Anything wider is just sillyb */
134 #define XO_FAILURE_NAME "failure"
136 /* Flags for the stack frame */
137 typedef unsigned xo_xsf_flags_t; /* XSF_* flags */
138 #define XSF_NOT_FIRST (1<<0) /* Not the first element */
139 #define XSF_LIST (1<<1) /* Frame is a list */
140 #define XSF_INSTANCE (1<<2) /* Frame is an instance */
141 #define XSF_DTRT (1<<3) /* Save the name for DTRT mode */
143 #define XSF_CONTENT (1<<4) /* Some content has been emitted */
144 #define XSF_EMIT (1<<5) /* Some field has been emitted */
145 #define XSF_EMIT_KEY (1<<6) /* A key has been emitted */
146 #define XSF_EMIT_LEAF_LIST (1<<7) /* A leaf-list field has been emitted */
148 /* These are the flags we propagate between markers and their parents */
149 #define XSF_MARKER_FLAGS \
150 (XSF_NOT_FIRST | XSF_CONTENT | XSF_EMIT | XSF_EMIT_KEY | XSF_EMIT_LEAF_LIST )
153 * A word about states: We use a finite state machine (FMS) approach
154 * to help remove fragility from the caller's code. Instead of
155 * requiring a specific order of calls, we'll allow the caller more
156 * flexibility and make the library responsible for recovering from
157 * missed steps. The goal is that the library should not be capable
158 * of emitting invalid xml or json, but the developer shouldn't need
159 * to know or understand all the details about these encodings.
161 * You can think of states as either states or events, since they
162 * function rather like both. None of the XO_CLOSE_* events will
163 * persist as states, since the matching stack frame will be popped.
164 * Same is true of XSS_EMIT, which is an event that asks us to
165 * prep for emitting output fields.
168 /* Stack frame states */
169 typedef unsigned xo_state_t;
170 #define XSS_INIT 0 /* Initial stack state */
171 #define XSS_OPEN_CONTAINER 1
172 #define XSS_CLOSE_CONTAINER 2
173 #define XSS_OPEN_LIST 3
174 #define XSS_CLOSE_LIST 4
175 #define XSS_OPEN_INSTANCE 5
176 #define XSS_CLOSE_INSTANCE 6
177 #define XSS_OPEN_LEAF_LIST 7
178 #define XSS_CLOSE_LEAF_LIST 8
179 #define XSS_DISCARDING 9 /* Discarding data until recovered */
180 #define XSS_MARKER 10 /* xo_open_marker's marker */
181 #define XSS_EMIT 11 /* xo_emit has a leaf field */
182 #define XSS_EMIT_LEAF_LIST 12 /* xo_emit has a leaf-list ({l:}) */
183 #define XSS_FINISH 13 /* xo_finish was called */
187 #define XSS_TRANSITION(_old, _new) ((_old) << 8 | (_new))
190 * xo_stack_t: As we open and close containers and levels, we
191 * create a stack of frames to track them. This is needed for
192 * XOF_WARN and XOF_XPATH.
194 typedef struct xo_stack_s {
195 xo_xsf_flags_t xs_flags; /* Flags for this frame */
196 xo_state_t xs_state; /* State for this stack frame */
197 char *xs_name; /* Name (for XPath value) */
198 char *xs_keys; /* XPath predicate for any key fields */
202 * libxo supports colors and effects, for those who like them.
203 * XO_COL_* ("colors") refers to fancy ansi codes, while X__EFF_*
204 * ("effects") are bits since we need to maintain state.
206 #define XO_COL_DEFAULT 0
207 #define XO_COL_BLACK 1
209 #define XO_COL_GREEN 3
210 #define XO_COL_YELLOW 4
211 #define XO_COL_BLUE 5
212 #define XO_COL_MAGENTA 6
213 #define XO_COL_CYAN 7
214 #define XO_COL_WHITE 8
216 #define XO_NUM_COLORS 9
219 * Yes, there's no blink. We're civilized. We like users. Blink
220 * isn't something one does to someone you like. Friends don't let
221 * friends use blink. On friends. You know what I mean. Blink is
222 * like, well, it's like bursting into show tunes at a funeral. It's
223 * just not done. Not something anyone wants. And on those rare
224 * instances where it might actually be appropriate, it's still wrong,
225 * since it's likely done by the wrong person for the wrong reason.
226 * Just like blink. And if I implemented blink, I'd be like a funeral
227 * director who adds "Would you like us to burst into show tunes?" on
228 * the list of questions asked while making funeral arrangements.
229 * It's formalizing wrongness in the wrong way. And we're just too
230 * civilized to do that. Hhhmph!
232 #define XO_EFF_RESET (1<<0)
233 #define XO_EFF_NORMAL (1<<1)
234 #define XO_EFF_BOLD (1<<2)
235 #define XO_EFF_UNDERLINE (1<<3)
236 #define XO_EFF_INVERSE (1<<4)
238 #define XO_EFF_CLEAR_BITS XO_EFF_RESET /* Reset gets reset, surprisingly */
240 typedef uint8_t xo_effect_t;
241 typedef uint8_t xo_color_t;
242 typedef struct xo_colors_s {
243 xo_effect_t xoc_effects; /* Current effect set */
244 xo_color_t xoc_col_fg; /* Foreground color */
245 xo_color_t xoc_col_bg; /* Background color */
249 * xo_handle_t: this is the principle data structure for libxo.
250 * It's used as a store for state, options, content, and all manor
251 * of other information.
254 xo_xof_flags_t xo_flags; /* Flags (XOF_*) from the user*/
255 xo_xof_flags_t xo_iflags; /* Internal flags (XOIF_*) */
256 xo_style_t xo_style; /* XO_STYLE_* value */
257 unsigned short xo_indent; /* Indent level (if pretty) */
258 unsigned short xo_indent_by; /* Indent amount (tab stop) */
259 xo_write_func_t xo_write; /* Write callback */
260 xo_close_func_t xo_close; /* Close callback */
261 xo_flush_func_t xo_flush; /* Flush callback */
262 xo_formatter_t xo_formatter; /* Custom formating function */
263 xo_checkpointer_t xo_checkpointer; /* Custom formating support function */
264 void *xo_opaque; /* Opaque data for write function */
265 xo_buffer_t xo_data; /* Output data */
266 xo_buffer_t xo_fmt; /* Work area for building format strings */
267 xo_buffer_t xo_attrs; /* Work area for building XML attributes */
268 xo_buffer_t xo_predicate; /* Work area for building XPath predicates */
269 xo_stack_t *xo_stack; /* Stack pointer */
270 int xo_depth; /* Depth of stack */
271 int xo_stack_size; /* Size of the stack */
272 xo_info_t *xo_info; /* Info fields for all elements */
273 int xo_info_count; /* Number of info entries */
274 va_list xo_vap; /* Variable arguments (stdargs) */
275 char *xo_leading_xpath; /* A leading XPath expression */
276 mbstate_t xo_mbstate; /* Multi-byte character conversion state */
277 ssize_t xo_anchor_offset; /* Start of anchored text */
278 ssize_t xo_anchor_columns; /* Number of columns since the start anchor */
279 ssize_t xo_anchor_min_width; /* Desired width of anchored text */
280 ssize_t xo_units_offset; /* Start of units insertion point */
281 ssize_t xo_columns; /* Columns emitted during this xo_emit call */
282 uint8_t xo_color_map_fg[XO_NUM_COLORS]; /* Foreground color mappings */
283 uint8_t xo_color_map_bg[XO_NUM_COLORS]; /* Background color mappings */
284 xo_colors_t xo_colors; /* Current color and effect values */
285 xo_buffer_t xo_color_buf; /* HTML: buffer of colors and effects */
286 char *xo_version; /* Version string */
287 int xo_errno; /* Saved errno for "%m" */
288 char *xo_gt_domain; /* Gettext domain, suitable for dgettext(3) */
289 xo_encoder_func_t xo_encoder; /* Encoding function */
290 void *xo_private; /* Private data for external encoders */
293 /* Flag operations */
294 #define XOF_BIT_ISSET(_flag, _bit) (((_flag) & (_bit)) ? 1 : 0)
295 #define XOF_BIT_SET(_flag, _bit) do { (_flag) |= (_bit); } while (0)
296 #define XOF_BIT_CLEAR(_flag, _bit) do { (_flag) &= ~(_bit); } while (0)
298 #define XOF_ISSET(_xop, _bit) XOF_BIT_ISSET(_xop->xo_flags, _bit)
299 #define XOF_SET(_xop, _bit) XOF_BIT_SET(_xop->xo_flags, _bit)
300 #define XOF_CLEAR(_xop, _bit) XOF_BIT_CLEAR(_xop->xo_flags, _bit)
302 #define XOIF_ISSET(_xop, _bit) XOF_BIT_ISSET(_xop->xo_iflags, _bit)
303 #define XOIF_SET(_xop, _bit) XOF_BIT_SET(_xop->xo_iflags, _bit)
304 #define XOIF_CLEAR(_xop, _bit) XOF_BIT_CLEAR(_xop->xo_iflags, _bit)
307 #define XOIF_REORDER XOF_BIT(0) /* Reordering fields; record field info */
308 #define XOIF_DIV_OPEN XOF_BIT(1) /* A <div> is open */
309 #define XOIF_TOP_EMITTED XOF_BIT(2) /* The top JSON braces have been emitted */
310 #define XOIF_ANCHOR XOF_BIT(3) /* An anchor is in place */
312 #define XOIF_UNITS_PENDING XOF_BIT(4) /* We have a units-insertion pending */
313 #define XOIF_INIT_IN_PROGRESS XOF_BIT(5) /* Init of handle is in progress */
315 /* Flags for formatting functions */
316 typedef unsigned long xo_xff_flags_t;
317 #define XFF_COLON (1<<0) /* Append a ":" */
318 #define XFF_COMMA (1<<1) /* Append a "," iff there's more output */
319 #define XFF_WS (1<<2) /* Append a blank */
320 #define XFF_ENCODE_ONLY (1<<3) /* Only emit for encoding styles (XML, JSON) */
322 #define XFF_QUOTE (1<<4) /* Force quotes */
323 #define XFF_NOQUOTE (1<<5) /* Force no quotes */
324 #define XFF_DISPLAY_ONLY (1<<6) /* Only emit for display styles (text, html) */
325 #define XFF_KEY (1<<7) /* Field is a key (for XPath) */
327 #define XFF_XML (1<<8) /* Force XML encoding style (for XPath) */
328 #define XFF_ATTR (1<<9) /* Escape value using attribute rules (XML) */
329 #define XFF_BLANK_LINE (1<<10) /* Emit a blank line */
330 #define XFF_NO_OUTPUT (1<<11) /* Do not make any output */
332 #define XFF_TRIM_WS (1<<12) /* Trim whitespace off encoded values */
333 #define XFF_LEAF_LIST (1<<13) /* A leaf-list (list of values) */
334 #define XFF_UNESCAPE (1<<14) /* Need to printf-style unescape the value */
335 #define XFF_HUMANIZE (1<<15) /* Humanize the value (for display styles) */
337 #define XFF_HN_SPACE (1<<16) /* Humanize: put space before suffix */
338 #define XFF_HN_DECIMAL (1<<17) /* Humanize: add one decimal place if <10 */
339 #define XFF_HN_1000 (1<<18) /* Humanize: use 1000, not 1024 */
340 #define XFF_GT_FIELD (1<<19) /* Call gettext() on a field */
342 #define XFF_GT_PLURAL (1<<20) /* Call dngettext to find plural form */
343 #define XFF_ARGUMENT (1<<21) /* Content provided via argument */
345 /* Flags to turn off when we don't want i18n processing */
346 #define XFF_GT_FLAGS (XFF_GT_FIELD | XFF_GT_PLURAL)
349 * Normal printf has width and precision, which for strings operate as
350 * min and max number of columns. But this depends on the idea that
351 * one byte means one column, which UTF-8 and multi-byte characters
352 * pitches on its ear. It may take 40 bytes of data to populate 14
353 * columns, but we can't go off looking at 40 bytes of data without the
354 * caller's permission for fear/knowledge that we'll generate core files.
356 * So we make three values, distinguishing between "max column" and
357 * "number of bytes that we will inspect inspect safely" We call the
358 * later "size", and make the format "%[[<min>].[[<size>].<max>]]s".
360 * Under the "first do no harm" theory, we default "max" to "size".
361 * This is a reasonable assumption for folks that don't grok the
362 * MBS/WCS/UTF-8 world, and while it will be annoying, it will never
365 * For example, xo_emit("{:tag/%-14.14s}", buf) will make 14
366 * columns of output, but will never look at more than 14 bytes of the
367 * input buffer. This is mostly compatible with printf and caller's
370 * In contrast xo_emit("{:tag/%-14..14s}", buf) will look at however
371 * many bytes (or until a NUL is seen) are needed to fill 14 columns
372 * of output. xo_emit("{:tag/%-14.*.14s}", xx, buf) will look at up
373 * to xx bytes (or until a NUL is seen) in order to fill 14 columns
376 * It's fairly amazing how a good idea (handle all languages of the
377 * world) blows such a big hole in the bottom of the fairly weak boat
378 * that is C string handling. The simplicity and completenesss are
379 * sunk in ways we haven't even begun to understand.
381 #define XF_WIDTH_MIN 0 /* Minimal width */
382 #define XF_WIDTH_SIZE 1 /* Maximum number of bytes to examine */
383 #define XF_WIDTH_MAX 2 /* Maximum width */
384 #define XF_WIDTH_NUM 3 /* Numeric fields in printf (min.size.max) */
386 /* Input and output string encodings */
387 #define XF_ENC_WIDE 1 /* Wide characters (wchar_t) */
388 #define XF_ENC_UTF8 2 /* UTF-8 */
389 #define XF_ENC_LOCALE 3 /* Current locale */
392 * A place to parse printf-style format flags for each field
394 typedef struct xo_format_s {
395 unsigned char xf_fc; /* Format character */
396 unsigned char xf_enc; /* Encoding of the string (XF_ENC_*) */
397 unsigned char xf_skip; /* Skip this field */
398 unsigned char xf_lflag; /* 'l' (long) */
399 unsigned char xf_hflag;; /* 'h' (half) */
400 unsigned char xf_jflag; /* 'j' (intmax_t) */
401 unsigned char xf_tflag; /* 't' (ptrdiff_t) */
402 unsigned char xf_zflag; /* 'z' (size_t) */
403 unsigned char xf_qflag; /* 'q' (quad_t) */
404 unsigned char xf_seen_minus; /* Seen a minus */
405 int xf_leading_zero; /* Seen a leading zero (zero fill) */
406 unsigned xf_dots; /* Seen one or more '.'s */
407 int xf_width[XF_WIDTH_NUM]; /* Width/precision/size numeric fields */
408 unsigned xf_stars; /* Seen one or more '*'s */
409 unsigned char xf_star[XF_WIDTH_NUM]; /* Seen one or more '*'s */
413 * This structure represents the parsed field information, suitable for
414 * processing by xo_do_emit and anything else that needs to parse fields.
415 * Note that all pointers point to the main format string.
417 * XXX This is a first step toward compilable or cachable format
418 * strings. We can also cache the results of dgettext when no format
419 * is used, assuming the 'p' modifier has _not_ been set.
421 typedef struct xo_field_info_s {
422 xo_xff_flags_t xfi_flags; /* Flags for this field */
423 unsigned xfi_ftype; /* Field type, as character (e.g. 'V') */
424 const char *xfi_start; /* Start of field in the format string */
425 const char *xfi_content; /* Field's content */
426 const char *xfi_format; /* Field's Format */
427 const char *xfi_encoding; /* Field's encoding format */
428 const char *xfi_next; /* Next character in format string */
429 ssize_t xfi_len; /* Length of field */
430 ssize_t xfi_clen; /* Content length */
431 ssize_t xfi_flen; /* Format length */
432 ssize_t xfi_elen; /* Encoding length */
433 unsigned xfi_fnum; /* Field number (if used; 0 otherwise) */
434 unsigned xfi_renum; /* Reordered number (0 == no renumbering) */
438 * We keep a 'default' handle to allow callers to avoid having to
439 * allocate one. Passing NULL to any of our functions will use
440 * this default handle. Most functions have a variant that doesn't
441 * require a handle at all, since most output is to stdout, which
442 * the default handle handles handily.
444 static THREAD_LOCAL(xo_handle_t) xo_default_handle;
445 static THREAD_LOCAL(int) xo_default_inited;
446 static int xo_locale_inited;
447 static const char *xo_program;
450 * To allow libxo to be used in diverse environment, we allow the
451 * caller to give callbacks for memory allocation.
453 xo_realloc_func_t xo_realloc = realloc;
454 xo_free_func_t xo_free = free;
456 /* Forward declarations */
458 xo_failure (xo_handle_t *xop, const char *fmt, ...);
461 xo_transition (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name,
462 xo_state_t new_state);
465 xo_buf_append_div (xo_handle_t *xop, const char *class, xo_xff_flags_t flags,
466 const char *name, ssize_t nlen,
467 const char *value, ssize_t vlen,
468 const char *encoding, ssize_t elen);
471 xo_anchor_clear (xo_handle_t *xop);
474 * xo_style is used to retrieve the current style. When we're built
475 * for "text only" mode, we use this function to drive the removal
476 * of most of the code in libxo. We return a constant and the compiler
477 * happily removes the non-text code that is not longer executed. This
478 * trims our code nicely without needing to trampel perfectly readable
481 static inline xo_style_t
482 xo_style (xo_handle_t *xop UNUSED)
484 #ifdef LIBXO_TEXT_ONLY
485 return XO_STYLE_TEXT;
486 #else /* LIBXO_TEXT_ONLY */
487 return xop->xo_style;
488 #endif /* LIBXO_TEXT_ONLY */
492 * Callback to write data to a FILE pointer
495 xo_write_to_file (void *opaque, const char *data)
497 FILE *fp = (FILE *) opaque;
499 return fprintf(fp, "%s", data);
503 * Callback to close a file
506 xo_close_file (void *opaque)
508 FILE *fp = (FILE *) opaque;
514 * Callback to flush a FILE pointer
517 xo_flush_file (void *opaque)
519 FILE *fp = (FILE *) opaque;
525 * Use a rotating stock of buffers to make a printable string
528 #define XO_SMBUFSZ 128
531 xo_printable (const char *str)
533 static THREAD_LOCAL(char) bufset[XO_NUMBUFS][XO_SMBUFSZ];
534 static THREAD_LOCAL(int) bufnum = 0;
539 if (++bufnum == XO_NUMBUFS)
542 char *res = bufset[bufnum], *cp, *ep;
544 for (cp = res, ep = res + XO_SMBUFSZ - 1; *str && cp < ep; cp++, str++) {
548 } else if (*str == '\r') {
551 } else if (*str == '\"') {
563 xo_depth_check (xo_handle_t *xop, int depth)
567 if (depth >= xop->xo_stack_size) {
568 depth += XO_DEPTH; /* Extra room */
570 xsp = xo_realloc(xop->xo_stack, sizeof(xop->xo_stack[0]) * depth);
572 xo_failure(xop, "xo_depth_check: out of memory (%d)", depth);
576 int count = depth - xop->xo_stack_size;
578 bzero(xsp + xop->xo_stack_size, count * sizeof(*xsp));
579 xop->xo_stack_size = depth;
587 xo_no_setlocale (void)
589 xo_locale_inited = 1; /* Skip initialization */
593 * We need to decide if stdout is line buffered (_IOLBF). Lacking a
594 * standard way to decide this (e.g. getlinebuf()), we have configure
595 * look to find __flbf, which glibc supported. If not, we'll rely on
596 * isatty, with the assumption that terminals are the only thing
597 * that's line buffered. We _could_ test for "steam._flags & _IOLBF",
598 * which is all __flbf does, but that's even tackier. Like a
599 * bedazzled Elvis outfit on an ugly lap dog sort of tacky. Not
600 * something we're willing to do.
603 xo_is_line_buffered (FILE *stream)
608 #else /* HAVE___FLBF */
609 if (isatty(fileno(stream)))
611 #endif /* HAVE___FLBF */
616 * Initialize an xo_handle_t, using both static defaults and
617 * the global settings from the LIBXO_OPTIONS environment
621 xo_init_handle (xo_handle_t *xop)
623 xop->xo_opaque = stdout;
624 xop->xo_write = xo_write_to_file;
625 xop->xo_flush = xo_flush_file;
627 if (xo_is_line_buffered(stdout))
628 XOF_SET(xop, XOF_FLUSH_LINE);
631 * We only want to do color output on terminals, but we only want
632 * to do this if the user has asked for color.
634 if (XOF_ISSET(xop, XOF_COLOR_ALLOWED) && isatty(1))
635 XOF_SET(xop, XOF_COLOR);
638 * We need to initialize the locale, which isn't really pretty.
639 * Libraries should depend on their caller to set up the
640 * environment. But we really can't count on the caller to do
641 * this, because well, they won't. Trust me.
643 if (!xo_locale_inited) {
644 xo_locale_inited = 1; /* Only do this once */
646 const char *cp = getenv("LC_CTYPE");
650 cp = getenv("LC_ALL");
652 cp = "C"; /* Default for C programs */
653 (void) setlocale(LC_CTYPE, cp);
657 * Initialize only the xo_buffers we know we'll need; the others
658 * can be allocated as needed.
660 xo_buf_init(&xop->xo_data);
661 xo_buf_init(&xop->xo_fmt);
663 if (XOIF_ISSET(xop, XOIF_INIT_IN_PROGRESS))
665 XOIF_SET(xop, XOIF_INIT_IN_PROGRESS);
667 xop->xo_indent_by = XO_INDENT_BY;
668 xo_depth_check(xop, XO_DEPTH);
670 #if !defined(NO_LIBXO_OPTIONS)
671 if (!XOF_ISSET(xop, XOF_NO_ENV)) {
672 char *env = getenv("LIBXO_OPTIONS");
674 xo_set_options(xop, env);
677 #endif /* NO_GETENV */
679 XOIF_CLEAR(xop, XOIF_INIT_IN_PROGRESS);
683 * Initialize the default handle.
686 xo_default_init (void)
688 xo_handle_t *xop = &xo_default_handle;
692 xo_default_inited = 1;
696 * Cheap convenience function to return either the argument, or
697 * the internal handle, after it has been initialized. The usage
699 * xop = xo_default(xop);
702 xo_default (xo_handle_t *xop)
705 if (xo_default_inited == 0)
707 xop = &xo_default_handle;
714 * Return the number of spaces we should be indenting. If
715 * we are pretty-printing, this is indent * indent_by.
718 xo_indent (xo_handle_t *xop)
722 xop = xo_default(xop);
724 if (XOF_ISSET(xop, XOF_PRETTY)) {
725 rc = xop->xo_indent * xop->xo_indent_by;
726 if (XOIF_ISSET(xop, XOIF_TOP_EMITTED))
727 rc += xop->xo_indent_by;
730 return (rc > 0) ? rc : 0;
734 xo_buf_indent (xo_handle_t *xop, int indent)
736 xo_buffer_t *xbp = &xop->xo_data;
739 indent = xo_indent(xop);
741 if (!xo_buf_has_room(xbp, indent))
744 memset(xbp->xb_curp, ' ', indent);
745 xbp->xb_curp += indent;
748 static char xo_xml_amp[] = "&";
749 static char xo_xml_lt[] = "<";
750 static char xo_xml_gt[] = ">";
751 static char xo_xml_quot[] = """;
754 xo_escape_xml (xo_buffer_t *xbp, ssize_t len, xo_xff_flags_t flags)
760 int attr = XOF_BIT_ISSET(flags, XFF_ATTR);
762 for (cp = xbp->xb_curp, ep = cp + len; cp < ep; cp++) {
763 /* We're subtracting 2: 1 for the NUL, 1 for the char we replace */
765 delta += sizeof(xo_xml_lt) - 2;
767 delta += sizeof(xo_xml_gt) - 2;
769 delta += sizeof(xo_xml_amp) - 2;
770 else if (attr && *cp == '"')
771 delta += sizeof(xo_xml_quot) - 2;
774 if (delta == 0) /* Nothing to escape; bail */
777 if (!xo_buf_has_room(xbp, delta)) /* No room; bail, but don't append */
793 else if (attr && *cp == '"')
802 memcpy(ip, sp, slen);
804 } while (cp > ep && cp != ip);
810 xo_escape_json (xo_buffer_t *xbp, ssize_t len, xo_xff_flags_t flags UNUSED)
815 for (cp = xbp->xb_curp, ep = cp + len; cp < ep; cp++) {
816 if (*cp == '\\' || *cp == '"')
818 else if (*cp == '\n' || *cp == '\r')
822 if (delta == 0) /* Nothing to escape; bail */
825 if (!xo_buf_has_room(xbp, delta)) /* No room; bail, but don't append */
835 if (*cp == '\\' || *cp == '"') {
838 } else if (*cp == '\n') {
841 } else if (*cp == '\r') {
848 } while (cp > ep && cp != ip);
854 * PARAM-VALUE = UTF-8-STRING ; characters '"', '\' and
855 * ; ']' MUST be escaped.
858 xo_escape_sdparams (xo_buffer_t *xbp, ssize_t len, xo_xff_flags_t flags UNUSED)
863 for (cp = xbp->xb_curp, ep = cp + len; cp < ep; cp++) {
864 if (*cp == '\\' || *cp == '"' || *cp == ']')
868 if (delta == 0) /* Nothing to escape; bail */
871 if (!xo_buf_has_room(xbp, delta)) /* No room; bail, but don't append */
881 if (*cp == '\\' || *cp == '"' || *cp == ']') {
888 } while (cp > ep && cp != ip);
894 xo_buf_escape (xo_handle_t *xop, xo_buffer_t *xbp,
895 const char *str, ssize_t len, xo_xff_flags_t flags)
897 if (!xo_buf_has_room(xbp, len))
900 memcpy(xbp->xb_curp, str, len);
902 switch (xo_style(xop)) {
905 len = xo_escape_xml(xbp, len, flags);
909 len = xo_escape_json(xbp, len, flags);
912 case XO_STYLE_SDPARAMS:
913 len = xo_escape_sdparams(xbp, len, flags);
921 * Write the current contents of the data buffer using the handle's
925 xo_write (xo_handle_t *xop)
928 xo_buffer_t *xbp = &xop->xo_data;
930 if (xbp->xb_curp != xbp->xb_bufp) {
931 xo_buf_append(xbp, "", 1); /* Append ending NUL */
932 xo_anchor_clear(xop);
934 rc = xop->xo_write(xop->xo_opaque, xbp->xb_bufp);
935 xbp->xb_curp = xbp->xb_bufp;
938 /* Turn off the flags that don't survive across writes */
939 XOIF_CLEAR(xop, XOIF_UNITS_PENDING);
945 * Format arguments into our buffer. If a custom formatter has been set,
946 * we use that to do the work; otherwise we vsnprintf().
949 xo_vsnprintf (xo_handle_t *xop, xo_buffer_t *xbp, const char *fmt, va_list vap)
953 ssize_t left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp);
955 va_copy(va_local, vap);
957 if (xop->xo_formatter)
958 rc = xop->xo_formatter(xop, xbp->xb_curp, left, fmt, va_local);
960 rc = vsnprintf(xbp->xb_curp, left, fmt, va_local);
963 if (!xo_buf_has_room(xbp, rc)) {
969 * After we call vsnprintf(), the stage of vap is not defined.
970 * We need to copy it before we pass. Then we have to do our
971 * own logic below to move it along. This is because the
972 * implementation can have va_list be a pointer (bsd) or a
973 * structure (macosx) or anything in between.
976 va_end(va_local); /* Reset vap to the start */
977 va_copy(va_local, vap);
979 left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp);
980 if (xop->xo_formatter)
981 rc = xop->xo_formatter(xop, xbp->xb_curp, left, fmt, va_local);
983 rc = vsnprintf(xbp->xb_curp, left, fmt, va_local);
991 * Print some data through the handle.
994 xo_printf_v (xo_handle_t *xop, const char *fmt, va_list vap)
996 xo_buffer_t *xbp = &xop->xo_data;
997 ssize_t left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp);
1001 va_copy(va_local, vap);
1003 rc = vsnprintf(xbp->xb_curp, left, fmt, va_local);
1006 if (!xo_buf_has_room(xbp, rc)) {
1011 va_end(va_local); /* Reset vap to the start */
1012 va_copy(va_local, vap);
1014 left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp);
1015 rc = vsnprintf(xbp->xb_curp, left, fmt, va_local);
1027 xo_printf (xo_handle_t *xop, const char *fmt, ...)
1034 rc = xo_printf_v(xop, fmt, vap);
1041 * These next few function are make The Essential UTF-8 Ginsu Knife.
1042 * Identify an input and output character, and convert it.
1044 static uint8_t xo_utf8_bits[7] = { 0, 0x7f, 0x1f, 0x0f, 0x07, 0x03, 0x01 };
1047 xo_is_utf8 (char ch)
1052 static inline ssize_t
1053 xo_utf8_to_wc_len (const char *buf)
1055 unsigned b = (unsigned char) *buf;
1058 if ((b & 0x80) == 0x0)
1060 else if ((b & 0xe0) == 0xc0)
1062 else if ((b & 0xf0) == 0xe0)
1064 else if ((b & 0xf8) == 0xf0)
1066 else if ((b & 0xfc) == 0xf8)
1068 else if ((b & 0xfe) == 0xfc)
1077 xo_buf_utf8_len (xo_handle_t *xop, const char *buf, ssize_t bufsiz)
1080 unsigned b = (unsigned char) *buf;
1083 len = xo_utf8_to_wc_len(buf);
1085 xo_failure(xop, "invalid UTF-8 data: %02hhx", b);
1090 xo_failure(xop, "invalid UTF-8 data (short): %02hhx (%d/%d)",
1095 for (i = 2; i < len; i++) {
1096 b = (unsigned char ) buf[i];
1097 if ((b & 0xc0) != 0x80) {
1098 xo_failure(xop, "invalid UTF-8 data (byte %d): %x", i, b);
1107 * Build a wide character from the input buffer; the number of
1108 * bits we pull off the first character is dependent on the length,
1109 * but we put 6 bits off all other bytes.
1111 static inline wchar_t
1112 xo_utf8_char (const char *buf, ssize_t len)
1114 /* Most common case: singleton byte */
1116 return (unsigned char) buf[0];
1120 const unsigned char *cp = (const unsigned char *) buf;
1122 wc = *cp & xo_utf8_bits[len];
1123 for (i = 1; i < len; i++) {
1126 if ((cp[i] & 0xc0) != 0x80)
1127 return (wchar_t) -1;
1134 * Determine the number of bytes needed to encode a wide character.
1137 xo_utf8_emit_len (wchar_t wc)
1141 if ((wc & ((1<<7) - 1)) == wc) /* Simple case */
1143 else if ((wc & ((1<<11) - 1)) == wc)
1145 else if ((wc & ((1<<16) - 1)) == wc)
1147 else if ((wc & ((1<<21) - 1)) == wc)
1149 else if ((wc & ((1<<26) - 1)) == wc)
1158 xo_utf8_emit_char (char *buf, ssize_t len, wchar_t wc)
1162 if (len == 1) { /* Simple case */
1167 for (i = len - 1; i >= 0; i--) {
1168 buf[i] = 0x80 | (wc & 0x3f);
1172 buf[0] &= xo_utf8_bits[len];
1173 buf[0] |= ~xo_utf8_bits[len] << 1;
1177 xo_buf_append_locale_from_utf8 (xo_handle_t *xop, xo_buffer_t *xbp,
1178 const char *ibuf, ssize_t ilen)
1184 * Build our wide character from the input buffer; the number of
1185 * bits we pull off the first character is dependent on the length,
1186 * but we put 6 bits off all other bytes.
1188 wc = xo_utf8_char(ibuf, ilen);
1189 if (wc == (wchar_t) -1) {
1190 xo_failure(xop, "invalid utf-8 byte sequence");
1194 if (XOF_ISSET(xop, XOF_NO_LOCALE)) {
1195 if (!xo_buf_has_room(xbp, ilen))
1198 memcpy(xbp->xb_curp, ibuf, ilen);
1199 xbp->xb_curp += ilen;
1202 if (!xo_buf_has_room(xbp, MB_LEN_MAX + 1))
1205 bzero(&xop->xo_mbstate, sizeof(xop->xo_mbstate));
1206 len = wcrtomb(xbp->xb_curp, wc, &xop->xo_mbstate);
1209 xo_failure(xop, "could not convert wide char: %lx",
1210 (unsigned long) wc);
1213 xbp->xb_curp += len;
1216 return xo_wcwidth(wc);
1220 xo_buf_append_locale (xo_handle_t *xop, xo_buffer_t *xbp,
1221 const char *cp, ssize_t len)
1223 const char *sp = cp, *ep = cp + len;
1224 ssize_t save_off = xbp->xb_bufp - xbp->xb_curp;
1228 for ( ; cp < ep; cp++) {
1229 if (!xo_is_utf8(*cp)) {
1235 * We're looking at a non-ascii UTF-8 character.
1236 * First we copy the previous data.
1237 * Then we need find the length and validate it.
1238 * Then we turn it into a wide string.
1239 * Then we turn it into a localized string.
1240 * Then we repeat. Isn't i18n fun?
1243 xo_buf_append(xbp, sp, cp - sp); /* Append previous data */
1245 slen = xo_buf_utf8_len(xop, cp, ep - cp);
1247 /* Bad data; back it all out */
1248 xbp->xb_curp = xbp->xb_bufp + save_off;
1252 cols += xo_buf_append_locale_from_utf8(xop, xbp, cp, slen);
1254 /* Next time through, we'll start at the next character */
1259 /* Update column values */
1260 if (XOF_ISSET(xop, XOF_COLUMNS))
1261 xop->xo_columns += cols;
1262 if (XOIF_ISSET(xop, XOIF_ANCHOR))
1263 xop->xo_anchor_columns += cols;
1265 /* Before we fall into the basic logic below, we need reset len */
1267 if (len != 0) /* Append trailing data */
1268 xo_buf_append(xbp, sp, len);
1272 * Append the given string to the given buffer, without escaping or
1273 * character set conversion. This is the straight copy to the data
1274 * buffer with no fanciness.
1277 xo_data_append (xo_handle_t *xop, const char *str, ssize_t len)
1279 xo_buf_append(&xop->xo_data, str, len);
1283 * Append the given string to the given buffer
1286 xo_data_escape (xo_handle_t *xop, const char *str, ssize_t len)
1288 xo_buf_escape(xop, &xop->xo_data, str, len, 0);
1291 #ifdef LIBXO_NO_RETAIN
1293 * Empty implementations of the retain logic
1297 xo_retain_clear_all (void)
1303 xo_retain_clear (const char *fmt UNUSED)
1308 xo_retain_add (const char *fmt UNUSED, xo_field_info_t *fields UNUSED,
1309 unsigned num_fields UNUSED)
1315 xo_retain_find (const char *fmt UNUSED, xo_field_info_t **valp UNUSED,
1316 unsigned *nump UNUSED)
1321 #else /* !LIBXO_NO_RETAIN */
1323 * Retain: We retain parsed field definitions to enhance performance,
1324 * especially inside loops. We depend on the caller treating the format
1325 * strings as immutable, so that we can retain pointers into them. We
1326 * hold the pointers in a hash table, so allow quick access. Retained
1327 * information is retained until xo_retain_clear is called.
1331 * xo_retain_entry_t holds information about one retained set of
1334 typedef struct xo_retain_entry_s {
1335 struct xo_retain_entry_s *xre_next; /* Pointer to next (older) entry */
1336 unsigned long xre_hits; /* Number of times we've hit */
1337 const char *xre_format; /* Pointer to format string */
1338 unsigned xre_num_fields; /* Number of fields saved */
1339 xo_field_info_t *xre_fields; /* Pointer to fields */
1340 } xo_retain_entry_t;
1343 * xo_retain_t holds a complete set of parsed fields as a hash table.
1345 #ifndef XO_RETAIN_SIZE
1346 #define XO_RETAIN_SIZE 6
1347 #endif /* XO_RETAIN_SIZE */
1348 #define RETAIN_HASH_SIZE (1<<XO_RETAIN_SIZE)
1350 typedef struct xo_retain_s {
1351 xo_retain_entry_t *xr_bucket[RETAIN_HASH_SIZE];
1354 static THREAD_LOCAL(xo_retain_t) xo_retain;
1355 static THREAD_LOCAL(unsigned) xo_retain_count;
1358 * Simple hash function based on Thomas Wang's paper. The original is
1359 * gone, but an archive is available on the Way Back Machine:
1361 * http://web.archive.org/web/20071223173210/\
1362 * http://www.concentric.net/~Ttwang/tech/inthash.htm
1364 * For our purposes, we can assume the low four bits are uninteresting
1365 * since any string less that 16 bytes wouldn't be worthy of
1366 * retaining. We toss the high bits also, since these bits are likely
1367 * to be common among constant format strings. We then run Wang's
1368 * algorithm, and cap the result at RETAIN_HASH_SIZE.
1371 xo_retain_hash (const char *fmt)
1373 volatile uintptr_t iptr = (uintptr_t) (const void *) fmt;
1375 /* Discard low four bits and high bits; they aren't interesting */
1376 uint32_t val = (uint32_t) ((iptr >> 4) & (((1 << 24) - 1)));
1378 val = (val ^ 61) ^ (val >> 16);
1379 val = val + (val << 3);
1380 val = val ^ (val >> 4);
1381 val = val * 0x3a8f05c5; /* My large prime number */
1382 val = val ^ (val >> 15);
1383 val &= RETAIN_HASH_SIZE - 1;
1389 * Walk all buckets, clearing all retained entries
1392 xo_retain_clear_all (void)
1395 xo_retain_entry_t *xrep, *next;
1397 for (i = 0; i < RETAIN_HASH_SIZE; i++) {
1398 for (xrep = xo_retain.xr_bucket[i]; xrep; xrep = next) {
1399 next = xrep->xre_next;
1402 xo_retain.xr_bucket[i] = NULL;
1404 xo_retain_count = 0;
1408 * Walk all buckets, clearing all retained entries
1411 xo_retain_clear (const char *fmt)
1413 xo_retain_entry_t **xrepp;
1414 unsigned hash = xo_retain_hash(fmt);
1416 for (xrepp = &xo_retain.xr_bucket[hash]; *xrepp;
1417 xrepp = &(*xrepp)->xre_next) {
1418 if ((*xrepp)->xre_format == fmt) {
1419 *xrepp = (*xrepp)->xre_next;
1420 xo_retain_count -= 1;
1427 * Search the hash for an entry matching 'fmt'; return it's fields.
1430 xo_retain_find (const char *fmt, xo_field_info_t **valp, unsigned *nump)
1432 if (xo_retain_count == 0)
1435 unsigned hash = xo_retain_hash(fmt);
1436 xo_retain_entry_t *xrep;
1438 for (xrep = xo_retain.xr_bucket[hash]; xrep != NULL;
1439 xrep = xrep->xre_next) {
1440 if (xrep->xre_format == fmt) {
1441 *valp = xrep->xre_fields;
1442 *nump = xrep->xre_num_fields;
1443 xrep->xre_hits += 1;
1452 xo_retain_add (const char *fmt, xo_field_info_t *fields, unsigned num_fields)
1454 unsigned hash = xo_retain_hash(fmt);
1455 xo_retain_entry_t *xrep;
1456 ssize_t sz = sizeof(*xrep) + (num_fields + 1) * sizeof(*fields);
1457 xo_field_info_t *xfip;
1459 xrep = xo_realloc(NULL, sz);
1463 xfip = (xo_field_info_t *) &xrep[1];
1464 memcpy(xfip, fields, num_fields * sizeof(*fields));
1466 bzero(xrep, sizeof(*xrep));
1468 xrep->xre_format = fmt;
1469 xrep->xre_fields = xfip;
1470 xrep->xre_num_fields = num_fields;
1472 /* Record the field info in the retain bucket */
1473 xrep->xre_next = xo_retain.xr_bucket[hash];
1474 xo_retain.xr_bucket[hash] = xrep;
1475 xo_retain_count += 1;
1478 #endif /* !LIBXO_NO_RETAIN */
1481 * Generate a warning. Normally, this is a text message written to
1482 * standard error. If the XOF_WARN_XML flag is set, then we generate
1483 * XMLified content on standard output.
1486 xo_warn_hcv (xo_handle_t *xop, int code, int check_warn,
1487 const char *fmt, va_list vap)
1489 xop = xo_default(xop);
1490 if (check_warn && !XOF_ISSET(xop, XOF_WARN))
1496 ssize_t len = strlen(fmt);
1497 ssize_t plen = xo_program ? strlen(xo_program) : 0;
1498 char *newfmt = alloca(len + 1 + plen + 2); /* NUL, and ": " */
1501 memcpy(newfmt, xo_program, plen);
1502 newfmt[plen++] = ':';
1503 newfmt[plen++] = ' ';
1505 memcpy(newfmt + plen, fmt, len);
1506 newfmt[len + plen] = '\0';
1508 if (XOF_ISSET(xop, XOF_WARN_XML)) {
1509 static char err_open[] = "<error>";
1510 static char err_close[] = "</error>";
1511 static char msg_open[] = "<message>";
1512 static char msg_close[] = "</message>";
1514 xo_buffer_t *xbp = &xop->xo_data;
1516 xo_buf_append(xbp, err_open, sizeof(err_open) - 1);
1517 xo_buf_append(xbp, msg_open, sizeof(msg_open) - 1);
1520 va_copy(va_local, vap);
1522 ssize_t left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp);
1523 ssize_t rc = vsnprintf(xbp->xb_curp, left, newfmt, vap);
1525 if (!xo_buf_has_room(xbp, rc)) {
1530 va_end(vap); /* Reset vap to the start */
1531 va_copy(vap, va_local);
1533 left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp);
1534 rc = vsnprintf(xbp->xb_curp, left, fmt, vap);
1538 rc = xo_escape_xml(xbp, rc, 1);
1541 xo_buf_append(xbp, msg_close, sizeof(msg_close) - 1);
1542 xo_buf_append(xbp, err_close, sizeof(err_close) - 1);
1545 const char *msg = strerror(code);
1547 xo_buf_append(xbp, ": ", 2);
1548 xo_buf_append(xbp, msg, strlen(msg));
1552 xo_buf_append(xbp, "\n", 1); /* Append newline and NUL to string */
1553 (void) xo_write(xop);
1556 vfprintf(stderr, newfmt, vap);
1558 const char *msg = strerror(code);
1560 fprintf(stderr, ": %s", msg);
1562 fprintf(stderr, "\n");
1567 xo_warn_hc (xo_handle_t *xop, int code, const char *fmt, ...)
1572 xo_warn_hcv(xop, code, 0, fmt, vap);
1577 xo_warn_c (int code, const char *fmt, ...)
1582 xo_warn_hcv(NULL, code, 0, fmt, vap);
1587 xo_warn (const char *fmt, ...)
1593 xo_warn_hcv(NULL, code, 0, fmt, vap);
1598 xo_warnx (const char *fmt, ...)
1603 xo_warn_hcv(NULL, -1, 0, fmt, vap);
1608 xo_err (int eval, const char *fmt, ...)
1614 xo_warn_hcv(NULL, code, 0, fmt, vap);
1621 xo_errx (int eval, const char *fmt, ...)
1626 xo_warn_hcv(NULL, -1, 0, fmt, vap);
1633 xo_errc (int eval, int code, const char *fmt, ...)
1638 xo_warn_hcv(NULL, code, 0, fmt, vap);
1645 * Generate a warning. Normally, this is a text message written to
1646 * standard error. If the XOF_WARN_XML flag is set, then we generate
1647 * XMLified content on standard output.
1650 xo_message_hcv (xo_handle_t *xop, int code, const char *fmt, va_list vap)
1652 static char msg_open[] = "<message>";
1653 static char msg_close[] = "</message>";
1658 xop = xo_default(xop);
1660 if (fmt == NULL || *fmt == '\0')
1663 int need_nl = (fmt[strlen(fmt) - 1] != '\n');
1665 switch (xo_style(xop)) {
1667 xbp = &xop->xo_data;
1668 if (XOF_ISSET(xop, XOF_PRETTY))
1669 xo_buf_indent(xop, xop->xo_indent_by);
1670 xo_buf_append(xbp, msg_open, sizeof(msg_open) - 1);
1672 va_copy(va_local, vap);
1674 ssize_t left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp);
1675 rc = vsnprintf(xbp->xb_curp, left, fmt, vap);
1677 if (!xo_buf_has_room(xbp, rc)) {
1682 va_end(vap); /* Reset vap to the start */
1683 va_copy(vap, va_local);
1685 left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp);
1686 rc = vsnprintf(xbp->xb_curp, left, fmt, vap);
1690 rc = xo_escape_xml(xbp, rc, 0);
1693 if (need_nl && code > 0) {
1694 const char *msg = strerror(code);
1696 xo_buf_append(xbp, ": ", 2);
1697 xo_buf_append(xbp, msg, strlen(msg));
1702 xo_buf_append(xbp, "\n", 1); /* Append newline and NUL to string */
1704 xo_buf_append(xbp, msg_close, sizeof(msg_close) - 1);
1706 if (XOF_ISSET(xop, XOF_PRETTY))
1707 xo_buf_append(xbp, "\n", 1); /* Append newline and NUL to string */
1709 (void) xo_write(xop);
1714 char buf[BUFSIZ], *bp = buf, *cp;
1715 ssize_t bufsiz = sizeof(buf);
1718 va_copy(va_local, vap);
1720 rc = vsnprintf(bp, bufsiz, fmt, va_local);
1722 bufsiz = rc + BUFSIZ;
1723 bp = alloca(bufsiz);
1725 va_copy(va_local, vap);
1726 rc = vsnprintf(bp, bufsiz, fmt, va_local);
1732 rc2 = snprintf(cp, bufsiz - rc, "%s%s\n",
1733 (code > 0) ? ": " : "",
1734 (code > 0) ? strerror(code) : "");
1739 xo_buf_append_div(xop, "message", 0, NULL, 0, bp, rc, NULL, 0);
1744 case XO_STYLE_SDPARAMS:
1745 case XO_STYLE_ENCODER:
1746 /* No means of representing messages */
1750 rc = xo_printf_v(xop, fmt, vap);
1752 * XXX need to handle UTF-8 widths
1755 if (XOF_ISSET(xop, XOF_COLUMNS))
1756 xop->xo_columns += rc;
1757 if (XOIF_ISSET(xop, XOIF_ANCHOR))
1758 xop->xo_anchor_columns += rc;
1761 if (need_nl && code > 0) {
1762 const char *msg = strerror(code);
1764 xo_printf(xop, ": %s", msg);
1768 xo_printf(xop, "\n");
1773 switch (xo_style(xop)) {
1775 if (XOIF_ISSET(xop, XOIF_DIV_OPEN)) {
1776 static char div_close[] = "</div>";
1777 XOIF_CLEAR(xop, XOIF_DIV_OPEN);
1778 xo_data_append(xop, div_close, sizeof(div_close) - 1);
1780 if (XOF_ISSET(xop, XOF_PRETTY))
1781 xo_data_append(xop, "\n", 1);
1786 (void) xo_flush_h(xop);
1790 xo_message_hc (xo_handle_t *xop, int code, const char *fmt, ...)
1795 xo_message_hcv(xop, code, fmt, vap);
1800 xo_message_c (int code, const char *fmt, ...)
1805 xo_message_hcv(NULL, code, fmt, vap);
1810 xo_message_e (const char *fmt, ...)
1816 xo_message_hcv(NULL, code, fmt, vap);
1821 xo_message (const char *fmt, ...)
1826 xo_message_hcv(NULL, 0, fmt, vap);
1831 xo_failure (xo_handle_t *xop, const char *fmt, ...)
1833 if (!XOF_ISSET(xop, XOF_WARN))
1839 xo_warn_hcv(xop, -1, 1, fmt, vap);
1844 * Create a handle for use by later libxo functions.
1846 * Note: normal use of libxo does not require a distinct handle, since
1847 * the default handle (used when NULL is passed) generates text on stdout.
1849 * @style Style of output desired (XO_STYLE_* value)
1850 * @flags Set of XOF_* flags in use with this handle
1853 xo_create (xo_style_t style, xo_xof_flags_t flags)
1855 xo_handle_t *xop = xo_realloc(NULL, sizeof(*xop));
1858 bzero(xop, sizeof(*xop));
1860 xop->xo_style = style;
1861 XOF_SET(xop, flags);
1862 xo_init_handle(xop);
1863 xop->xo_style = style; /* Reset style (see LIBXO_OPTIONS) */
1870 * Create a handle that will write to the given file. Use
1871 * the XOF_CLOSE_FP flag to have the file closed on xo_destroy().
1872 * @fp FILE pointer to use
1873 * @style Style of output desired (XO_STYLE_* value)
1874 * @flags Set of XOF_* flags to use with this handle
1877 xo_create_to_file (FILE *fp, xo_style_t style, xo_xof_flags_t flags)
1879 xo_handle_t *xop = xo_create(style, flags);
1882 xop->xo_opaque = fp;
1883 xop->xo_write = xo_write_to_file;
1884 xop->xo_close = xo_close_file;
1885 xop->xo_flush = xo_flush_file;
1892 * Set the default handler to output to a file.
1894 * @fp FILE pointer to use
1897 xo_set_file_h (xo_handle_t *xop, FILE *fp)
1899 xop = xo_default(xop);
1902 xo_failure(xop, "xo_set_file: NULL fp");
1906 xop->xo_opaque = fp;
1907 xop->xo_write = xo_write_to_file;
1908 xop->xo_close = xo_close_file;
1909 xop->xo_flush = xo_flush_file;
1915 * Set the default handler to output to a file.
1916 * @fp FILE pointer to use
1919 xo_set_file (FILE *fp)
1921 return xo_set_file_h(NULL, fp);
1925 * Release any resources held by the handle.
1926 * @xop XO handle to alter (or NULL for default handle)
1929 xo_destroy (xo_handle_t *xop_arg)
1931 xo_handle_t *xop = xo_default(xop_arg);
1935 if (xop->xo_close && XOF_ISSET(xop, XOF_CLOSE_FP))
1936 xop->xo_close(xop->xo_opaque);
1938 xo_free(xop->xo_stack);
1939 xo_buf_cleanup(&xop->xo_data);
1940 xo_buf_cleanup(&xop->xo_fmt);
1941 xo_buf_cleanup(&xop->xo_predicate);
1942 xo_buf_cleanup(&xop->xo_attrs);
1943 xo_buf_cleanup(&xop->xo_color_buf);
1945 if (xop->xo_version)
1946 xo_free(xop->xo_version);
1948 if (xop_arg == NULL) {
1949 bzero(&xo_default_handle, sizeof(xo_default_handle));
1950 xo_default_inited = 0;
1956 * Record a new output style to use for the given handle (or default if
1957 * handle is NULL). This output style will be used for any future output.
1959 * @xop XO handle to alter (or NULL for default handle)
1960 * @style new output style (XO_STYLE_*)
1963 xo_set_style (xo_handle_t *xop, xo_style_t style)
1965 xop = xo_default(xop);
1966 xop->xo_style = style;
1970 xo_get_style (xo_handle_t *xop)
1972 xop = xo_default(xop);
1973 return xo_style(xop);
1977 xo_name_to_style (const char *name)
1979 if (strcmp(name, "xml") == 0)
1980 return XO_STYLE_XML;
1981 else if (strcmp(name, "json") == 0)
1982 return XO_STYLE_JSON;
1983 else if (strcmp(name, "encoder") == 0)
1984 return XO_STYLE_ENCODER;
1985 else if (strcmp(name, "text") == 0)
1986 return XO_STYLE_TEXT;
1987 else if (strcmp(name, "html") == 0)
1988 return XO_STYLE_HTML;
1989 else if (strcmp(name, "sdparams") == 0)
1990 return XO_STYLE_SDPARAMS;
1996 * Indicate if the style is an "encoding" one as opposed to a "display" one.
1999 xo_style_is_encoding (xo_handle_t *xop)
2001 if (xo_style(xop) == XO_STYLE_JSON
2002 || xo_style(xop) == XO_STYLE_XML
2003 || xo_style(xop) == XO_STYLE_SDPARAMS
2004 || xo_style(xop) == XO_STYLE_ENCODER)
2009 /* Simple name-value mapping */
2010 typedef struct xo_mapping_s {
2011 xo_xff_flags_t xm_value;
2012 const char *xm_name;
2015 static xo_xff_flags_t
2016 xo_name_lookup (xo_mapping_t *map, const char *value, ssize_t len)
2022 len = strlen(value);
2024 while (isspace((int) *value)) {
2029 while (isspace((int) value[len]))
2035 for ( ; map->xm_name; map++)
2036 if (strncmp(map->xm_name, value, len) == 0)
2037 return map->xm_value;
2042 #ifdef NOT_NEEDED_YET
2044 xo_value_lookup (xo_mapping_t *map, xo_xff_flags_t value)
2049 for ( ; map->xm_name; map++)
2050 if (map->xm_value == value)
2051 return map->xm_name;
2055 #endif /* NOT_NEEDED_YET */
2057 static xo_mapping_t xo_xof_names[] = {
2058 { XOF_COLOR_ALLOWED, "color" },
2059 { XOF_COLUMNS, "columns" },
2060 { XOF_DTRT, "dtrt" },
2061 { XOF_FLUSH, "flush" },
2062 { XOF_FLUSH_LINE, "flush-line" },
2063 { XOF_IGNORE_CLOSE, "ignore-close" },
2064 { XOF_INFO, "info" },
2065 { XOF_KEYS, "keys" },
2066 { XOF_LOG_GETTEXT, "log-gettext" },
2067 { XOF_LOG_SYSLOG, "log-syslog" },
2068 { XOF_NO_HUMANIZE, "no-humanize" },
2069 { XOF_NO_LOCALE, "no-locale" },
2070 { XOF_RETAIN_NONE, "no-retain" },
2071 { XOF_NO_TOP, "no-top" },
2072 { XOF_NOT_FIRST, "not-first" },
2073 { XOF_PRETTY, "pretty" },
2074 { XOF_RETAIN_ALL, "retain" },
2075 { XOF_UNDERSCORES, "underscores" },
2076 { XOF_UNITS, "units" },
2077 { XOF_WARN, "warn" },
2078 { XOF_WARN_XML, "warn-xml" },
2079 { XOF_XPATH, "xpath" },
2084 * Convert string name to XOF_* flag value.
2085 * Not all are useful. Or safe. Or sane.
2088 xo_name_to_flag (const char *name)
2090 return (unsigned) xo_name_lookup(xo_xof_names, name, -1);
2094 xo_set_style_name (xo_handle_t *xop, const char *name)
2099 int style = xo_name_to_style(name);
2103 xo_set_style(xop, style);
2108 * Set the options for a handle using a string of options
2109 * passed in. The input is a comma-separated set of names
2110 * and optional values: "xml,pretty,indent=4"
2113 xo_set_options (xo_handle_t *xop, const char *input)
2115 char *cp, *ep, *vp, *np, *bp;
2116 int style = -1, new_style, rc = 0;
2118 xo_xof_flags_t new_flag;
2123 xop = xo_default(xop);
2125 #ifdef LIBXO_COLOR_ON_BY_DEFAULT
2126 /* If the installer used --enable-color-on-by-default, then we allow it */
2127 XOF_SET(xop, XOF_COLOR_ALLOWED);
2128 #endif /* LIBXO_COLOR_ON_BY_DEFAULT */
2131 * We support a simpler, old-school style of giving option
2132 * also, using a single character for each option. It's
2133 * ideal for lazy people, such as myself.
2135 if (*input == ':') {
2138 for (input++ ; *input; input++) {
2141 XOF_SET(xop, XOF_COLOR_ALLOWED);
2145 XOF_SET(xop, XOF_FLUSH);
2149 XOF_SET(xop, XOF_FLUSH_LINE);
2153 XOF_SET(xop, XOF_LOG_GETTEXT);
2157 xop->xo_style = XO_STYLE_HTML;
2161 XOF_SET(xop, XOF_INFO);
2165 sz = strspn(input + 1, "0123456789");
2167 xop->xo_indent_by = atoi(input + 1);
2168 input += sz - 1; /* Skip value */
2173 xop->xo_style = XO_STYLE_JSON;
2177 XOF_SET(xop, XOF_KEYS);
2181 XOF_SET(xop, XOF_NO_HUMANIZE);
2185 XOF_SET(xop, XOF_PRETTY);
2189 xop->xo_style = XO_STYLE_TEXT;
2193 XOF_SET(xop, XOF_UNITS);
2197 XOF_SET(xop, XOF_UNDERSCORES);
2201 XOF_SET(xop, XOF_WARN);
2205 xop->xo_style = XO_STYLE_XML;
2209 XOF_SET(xop, XOF_XPATH);
2216 len = strlen(input) + 1;
2218 memcpy(bp, input, len);
2220 for (cp = bp, ep = cp + len - 1; cp && cp < ep; cp = np) {
2221 np = strchr(cp, ',');
2225 vp = strchr(cp, '=');
2229 if (strcmp("colors", cp) == 0) {
2230 /* XXX Look for colors=red-blue+green-yellow */
2235 * For options, we don't allow "encoder" since we want to
2236 * handle it explicitly below as "encoder=xxx".
2238 new_style = xo_name_to_style(cp);
2239 if (new_style >= 0 && new_style != XO_STYLE_ENCODER) {
2241 xo_warnx("ignoring multiple styles: '%s'", cp);
2245 new_flag = xo_name_to_flag(cp);
2247 XOF_SET(xop, new_flag);
2249 if (strcmp(cp, "no-color") == 0) {
2250 XOF_CLEAR(xop, XOF_COLOR_ALLOWED);
2251 } else if (strcmp(cp, "indent") == 0) {
2253 xop->xo_indent_by = atoi(vp);
2255 xo_failure(xop, "missing value for indent option");
2256 } else if (strcmp(cp, "encoder") == 0) {
2258 xo_failure(xop, "missing value for encoder option");
2260 if (xo_encoder_init(xop, vp)) {
2261 xo_failure(xop, "encoder not found: %s", vp);
2267 xo_warnx("unknown libxo option value: '%s'", cp);
2275 xop->xo_style= style;
2281 * Set one or more flags for a given handle (or default if handle is NULL).
2282 * These flags will affect future output.
2284 * @xop XO handle to alter (or NULL for default handle)
2285 * @flags Flags to be set (XOF_*)
2288 xo_set_flags (xo_handle_t *xop, xo_xof_flags_t flags)
2290 xop = xo_default(xop);
2292 XOF_SET(xop, flags);
2296 xo_get_flags (xo_handle_t *xop)
2298 xop = xo_default(xop);
2300 return xop->xo_flags;
2304 * strndup with a twist: len < 0 means strlen
2307 xo_strndup (const char *str, ssize_t len)
2312 char *cp = xo_realloc(NULL, len + 1);
2314 memcpy(cp, str, len);
2322 * Record a leading prefix for the XPath we generate. This allows the
2323 * generated data to be placed within an XML hierarchy but still have
2324 * accurate XPath expressions.
2326 * @xop XO handle to alter (or NULL for default handle)
2327 * @path The XPath expression
2330 xo_set_leading_xpath (xo_handle_t *xop, const char *path)
2332 xop = xo_default(xop);
2334 if (xop->xo_leading_xpath) {
2335 xo_free(xop->xo_leading_xpath);
2336 xop->xo_leading_xpath = NULL;
2342 xop->xo_leading_xpath = xo_strndup(path, -1);
2346 * Record the info data for a set of tags
2348 * @xop XO handle to alter (or NULL for default handle)
2349 * @info Info data (xo_info_t) to be recorded (or NULL) (MUST BE SORTED)
2350 * @count Number of entries in info (or -1 to count them ourselves)
2353 xo_set_info (xo_handle_t *xop, xo_info_t *infop, int count)
2355 xop = xo_default(xop);
2357 if (count < 0 && infop) {
2360 for (xip = infop, count = 0; xip->xi_name; xip++, count++)
2364 xop->xo_info = infop;
2365 xop->xo_info_count = count;
2369 * Set the formatter callback for a handle. The callback should
2370 * return a newly formatting contents of a formatting instruction,
2371 * meaning the bits inside the braces.
2374 xo_set_formatter (xo_handle_t *xop, xo_formatter_t func,
2375 xo_checkpointer_t cfunc)
2377 xop = xo_default(xop);
2379 xop->xo_formatter = func;
2380 xop->xo_checkpointer = cfunc;
2384 * Clear one or more flags for a given handle (or default if handle is NULL).
2385 * These flags will affect future output.
2387 * @xop XO handle to alter (or NULL for default handle)
2388 * @flags Flags to be cleared (XOF_*)
2391 xo_clear_flags (xo_handle_t *xop, xo_xof_flags_t flags)
2393 xop = xo_default(xop);
2395 XOF_CLEAR(xop, flags);
2399 xo_state_name (xo_state_t state)
2401 static const char *names[] = {
2419 if (state < (sizeof(names) / sizeof(names[0])))
2420 return names[state];
2426 xo_line_ensure_open (xo_handle_t *xop, xo_xff_flags_t flags UNUSED)
2428 static char div_open[] = "<div class=\"line\">";
2429 static char div_open_blank[] = "<div class=\"blank-line\">";
2431 if (XOIF_ISSET(xop, XOIF_DIV_OPEN))
2434 if (xo_style(xop) != XO_STYLE_HTML)
2437 XOIF_SET(xop, XOIF_DIV_OPEN);
2438 if (flags & XFF_BLANK_LINE)
2439 xo_data_append(xop, div_open_blank, sizeof(div_open_blank) - 1);
2441 xo_data_append(xop, div_open, sizeof(div_open) - 1);
2443 if (XOF_ISSET(xop, XOF_PRETTY))
2444 xo_data_append(xop, "\n", 1);
2448 xo_line_close (xo_handle_t *xop)
2450 static char div_close[] = "</div>";
2452 switch (xo_style(xop)) {
2454 if (!XOIF_ISSET(xop, XOIF_DIV_OPEN))
2455 xo_line_ensure_open(xop, 0);
2457 XOIF_CLEAR(xop, XOIF_DIV_OPEN);
2458 xo_data_append(xop, div_close, sizeof(div_close) - 1);
2460 if (XOF_ISSET(xop, XOF_PRETTY))
2461 xo_data_append(xop, "\n", 1);
2465 xo_data_append(xop, "\n", 1);
2471 xo_info_compare (const void *key, const void *data)
2473 const char *name = key;
2474 const xo_info_t *xip = data;
2476 return strcmp(name, xip->xi_name);
2481 xo_info_find (xo_handle_t *xop, const char *name, ssize_t nlen)
2484 char *cp = alloca(nlen + 1); /* Need local copy for NUL termination */
2486 memcpy(cp, name, nlen);
2489 xip = bsearch(cp, xop->xo_info, xop->xo_info_count,
2490 sizeof(xop->xo_info[0]), xo_info_compare);
2494 #define CONVERT(_have, _need) (((_have) << 8) | (_need))
2497 * Check to see that the conversion is safe and sane.
2500 xo_check_conversion (xo_handle_t *xop, int have_enc, int need_enc)
2502 switch (CONVERT(have_enc, need_enc)) {
2503 case CONVERT(XF_ENC_UTF8, XF_ENC_UTF8):
2504 case CONVERT(XF_ENC_UTF8, XF_ENC_LOCALE):
2505 case CONVERT(XF_ENC_WIDE, XF_ENC_UTF8):
2506 case CONVERT(XF_ENC_WIDE, XF_ENC_LOCALE):
2507 case CONVERT(XF_ENC_LOCALE, XF_ENC_LOCALE):
2508 case CONVERT(XF_ENC_LOCALE, XF_ENC_UTF8):
2512 xo_failure(xop, "invalid conversion (%c:%c)", have_enc, need_enc);
2518 xo_format_string_direct (xo_handle_t *xop, xo_buffer_t *xbp,
2519 xo_xff_flags_t flags,
2520 const wchar_t *wcp, const char *cp,
2521 ssize_t len, int max,
2522 int need_enc, int have_enc)
2528 int attr = XOF_BIT_ISSET(flags, XFF_ATTR);
2531 if (len > 0 && !xo_buf_has_room(xbp, len))
2541 if ((flags & XFF_UNESCAPE) && (*cp == '\\' || *cp == '%')) {
2547 if (wcp && *wcp == L'\0')
2553 case XF_ENC_WIDE: /* Wide character */
2558 case XF_ENC_UTF8: /* UTF-8 */
2559 ilen = xo_utf8_to_wc_len(cp);
2561 xo_failure(xop, "invalid UTF-8 character: %02hhx", *cp);
2562 return -1; /* Can't continue; we can't find the end */
2565 if (len > 0 && len < ilen) {
2566 len = 0; /* Break out of the loop */
2570 wc = xo_utf8_char(cp, ilen);
2571 if (wc == (wchar_t) -1) {
2572 xo_failure(xop, "invalid UTF-8 character: %02hhx/%d",
2574 return -1; /* Can't continue; we can't find the end */
2579 case XF_ENC_LOCALE: /* Native locale */
2580 ilen = (len > 0) ? len : MB_LEN_MAX;
2581 ilen = mbrtowc(&wc, cp, ilen, &xop->xo_mbstate);
2582 if (ilen < 0) { /* Invalid data; skip */
2583 xo_failure(xop, "invalid mbs char: %02hhx", *cp);
2588 if (ilen == 0) { /* Hit a wide NUL character */
2597 /* Reduce len, but not below zero */
2605 * Find the width-in-columns of this character, which must be done
2606 * in wide characters, since we lack a mbswidth() function. If
2609 width = xo_wcwidth(wc);
2611 width = iswcntrl(wc) ? 0 : 1;
2613 if (xo_style(xop) == XO_STYLE_TEXT || xo_style(xop) == XO_STYLE_HTML) {
2614 if (max > 0 && cols + width > max)
2621 /* Output in UTF-8 needs to be escaped, based on the style */
2622 switch (xo_style(xop)) {
2631 else if (attr && wc == '"')
2636 ssize_t slen = strlen(sp);
2637 if (!xo_buf_has_room(xbp, slen - 1))
2640 memcpy(xbp->xb_curp, sp, slen);
2641 xbp->xb_curp += slen;
2642 goto done_with_encoding; /* Need multi-level 'break' */
2645 if (wc != '\\' && wc != '"' && wc != '\n' && wc != '\r')
2648 if (!xo_buf_has_room(xbp, 2))
2651 *xbp->xb_curp++ = '\\';
2654 else if (wc == '\r')
2656 else wc = wc & 0x7f;
2658 *xbp->xb_curp++ = wc;
2659 goto done_with_encoding;
2661 case XO_STYLE_SDPARAMS:
2662 if (wc != '\\' && wc != '"' && wc != ']')
2665 if (!xo_buf_has_room(xbp, 2))
2668 *xbp->xb_curp++ = '\\';
2670 *xbp->xb_curp++ = wc;
2671 goto done_with_encoding;
2674 olen = xo_utf8_emit_len(wc);
2676 xo_failure(xop, "ignoring bad length");
2680 if (!xo_buf_has_room(xbp, olen))
2683 xo_utf8_emit_char(xbp->xb_curp, olen, wc);
2684 xbp->xb_curp += olen;
2688 if (!xo_buf_has_room(xbp, MB_LEN_MAX + 1))
2691 olen = wcrtomb(xbp->xb_curp, wc, &xop->xo_mbstate);
2693 xo_failure(xop, "could not convert wide char: %lx",
2694 (unsigned long) wc);
2696 *xbp->xb_curp++ = '?';
2698 xbp->xb_curp += olen;
2710 xo_needed_encoding (xo_handle_t *xop)
2712 if (XOF_ISSET(xop, XOF_UTF8)) /* Check the override flag */
2715 if (xo_style(xop) == XO_STYLE_TEXT) /* Text means locale */
2716 return XF_ENC_LOCALE;
2718 return XF_ENC_UTF8; /* Otherwise, we love UTF-8 */
2722 xo_format_string (xo_handle_t *xop, xo_buffer_t *xbp, xo_xff_flags_t flags,
2725 static char null[] = "(null)";
2726 static char null_no_quotes[] = "null";
2729 wchar_t *wcp = NULL;
2731 ssize_t cols = 0, rc = 0;
2732 ssize_t off = xbp->xb_curp - xbp->xb_bufp, off2;
2733 int need_enc = xo_needed_encoding(xop);
2735 if (xo_check_conversion(xop, xfp->xf_enc, need_enc))
2738 len = xfp->xf_width[XF_WIDTH_SIZE];
2740 if (xfp->xf_fc == 'm') {
2741 cp = strerror(xop->xo_errno);
2743 len = cp ? strlen(cp) : 0;
2746 } else if (xfp->xf_enc == XF_ENC_WIDE) {
2747 wcp = va_arg(xop->xo_vap, wchar_t *);
2752 * Dont' deref NULL; use the traditional "(null)" instead
2753 * of the more accurate "who's been a naughty boy, then?".
2757 len = sizeof(null) - 1;
2761 cp = va_arg(xop->xo_vap, char *); /* UTF-8 or native */
2767 /* Echo "Dont' deref NULL" logic */
2769 if ((flags & XFF_NOQUOTE) && xo_style_is_encoding(xop)) {
2770 cp = null_no_quotes;
2771 len = sizeof(null_no_quotes) - 1;
2774 len = sizeof(null) - 1;
2779 * Optimize the most common case, which is "%s". We just
2780 * need to copy the complete string to the output buffer.
2782 if (xfp->xf_enc == need_enc
2783 && xfp->xf_width[XF_WIDTH_MIN] < 0
2784 && xfp->xf_width[XF_WIDTH_SIZE] < 0
2785 && xfp->xf_width[XF_WIDTH_MAX] < 0
2786 && !(XOIF_ISSET(xop, XOIF_ANCHOR)
2787 || XOF_ISSET(xop, XOF_COLUMNS))) {
2789 xo_buf_escape(xop, xbp, cp, len, flags);
2792 * Our caller expects xb_curp left untouched, so we have
2793 * to reset it and return the number of bytes written to
2796 off2 = xbp->xb_curp - xbp->xb_bufp;
2798 xbp->xb_curp = xbp->xb_bufp + off;
2804 cols = xo_format_string_direct(xop, xbp, flags, wcp, cp, len,
2805 xfp->xf_width[XF_WIDTH_MAX],
2806 need_enc, xfp->xf_enc);
2811 * xo_buf_append* will move xb_curp, so we save/restore it.
2813 off2 = xbp->xb_curp - xbp->xb_bufp;
2815 xbp->xb_curp = xbp->xb_bufp + off;
2817 if (cols < xfp->xf_width[XF_WIDTH_MIN]) {
2819 * Find the number of columns needed to display the string.
2820 * If we have the original wide string, we just call wcswidth,
2821 * but if we did the work ourselves, then we need to do it.
2823 int delta = xfp->xf_width[XF_WIDTH_MIN] - cols;
2824 if (!xo_buf_has_room(xbp, xfp->xf_width[XF_WIDTH_MIN]))
2828 * If seen_minus, then pad on the right; otherwise move it so
2829 * we can pad on the left.
2831 if (xfp->xf_seen_minus) {
2832 cp = xbp->xb_curp + rc;
2835 memmove(xbp->xb_curp + delta, xbp->xb_curp, rc);
2838 /* Set the padding */
2839 memset(cp, (xfp->xf_leading_zero > 0) ? '0' : ' ', delta);
2844 if (XOF_ISSET(xop, XOF_COLUMNS))
2845 xop->xo_columns += cols;
2846 if (XOIF_ISSET(xop, XOIF_ANCHOR))
2847 xop->xo_anchor_columns += cols;
2852 xbp->xb_curp = xbp->xb_bufp + off;
2857 * Look backwards in a buffer to find a numeric value
2860 xo_buf_find_last_number (xo_buffer_t *xbp, ssize_t start_offset)
2862 int rc = 0; /* Fail with zero */
2864 char *sp = xbp->xb_bufp;
2865 char *cp = sp + start_offset;
2868 if (isdigit((int) *cp))
2871 for ( ; cp >= sp; cp--) {
2872 if (!isdigit((int) *cp))
2874 rc += (*cp - '0') * digit;
2882 xo_count_utf8_cols (const char *str, ssize_t len)
2887 const char *ep = str + len;
2890 tlen = xo_utf8_to_wc_len(str);
2891 if (tlen < 0) /* Broken input is very bad */
2894 wc = xo_utf8_char(str, tlen);
2895 if (wc == (wchar_t) -1)
2898 /* We only print printable characters */
2899 if (iswprint((wint_t) wc)) {
2901 * Find the width-in-columns of this character, which must be done
2902 * in wide characters, since we lack a mbswidth() function.
2904 ssize_t width = xo_wcwidth(wc);
2906 width = iswcntrl(wc) ? 0 : 1;
2918 static inline const char *
2919 xo_dgettext (xo_handle_t *xop, const char *str)
2921 const char *domainname = xop->xo_gt_domain;
2924 res = dgettext(domainname, str);
2926 if (XOF_ISSET(xop, XOF_LOG_GETTEXT))
2927 fprintf(stderr, "xo: gettext: %s%s%smsgid \"%s\" returns \"%s\"\n",
2928 domainname ? "domain \"" : "", xo_printable(domainname),
2929 domainname ? "\", " : "", xo_printable(str), xo_printable(res));
2934 static inline const char *
2935 xo_dngettext (xo_handle_t *xop, const char *sing, const char *plural,
2936 unsigned long int n)
2938 const char *domainname = xop->xo_gt_domain;
2941 res = dngettext(domainname, sing, plural, n);
2942 if (XOF_ISSET(xop, XOF_LOG_GETTEXT))
2943 fprintf(stderr, "xo: gettext: %s%s%s"
2944 "msgid \"%s\", msgid_plural \"%s\" (%lu) returns \"%s\"\n",
2945 domainname ? "domain \"" : "",
2946 xo_printable(domainname), domainname ? "\", " : "",
2948 xo_printable(plural), n, xo_printable(res));
2952 #else /* HAVE_GETTEXT */
2953 static inline const char *
2954 xo_dgettext (xo_handle_t *xop UNUSED, const char *str)
2959 static inline const char *
2960 xo_dngettext (xo_handle_t *xop UNUSED, const char *singular,
2961 const char *plural, unsigned long int n)
2963 return (n == 1) ? singular : plural;
2965 #endif /* HAVE_GETTEXT */
2968 * This is really _re_formatting, since the normal format code has
2969 * generated a beautiful string into xo_data, starting at
2970 * start_offset. We need to see if it's plural, which means
2971 * comma-separated options, or singular. Then we make the appropriate
2972 * call to d[n]gettext() to get the locale-based version. Note that
2973 * both input and output of gettext() this should be UTF-8.
2976 xo_format_gettext (xo_handle_t *xop, xo_xff_flags_t flags,
2977 ssize_t start_offset, ssize_t cols, int need_enc)
2979 xo_buffer_t *xbp = &xop->xo_data;
2981 if (!xo_buf_has_room(xbp, 1))
2984 xbp->xb_curp[0] = '\0'; /* NUL-terminate the input string */
2986 char *cp = xbp->xb_bufp + start_offset;
2987 ssize_t len = xbp->xb_curp - cp;
2988 const char *newstr = NULL;
2991 * The plural flag asks us to look backwards at the last numeric
2992 * value rendered and disect the string into two pieces.
2994 if (flags & XFF_GT_PLURAL) {
2995 int n = xo_buf_find_last_number(xbp, start_offset);
2996 char *two = memchr(cp, (int) ',', len);
2998 xo_failure(xop, "no comma in plural gettext field: '%s'", cp);
3003 xo_failure(xop, "nothing before comma in plural gettext "
3008 if (two == xbp->xb_curp) {
3009 xo_failure(xop, "nothing after comma in plural gettext "
3015 if (flags & XFF_GT_FIELD) {
3016 newstr = xo_dngettext(xop, cp, two, n);
3018 /* Don't do a gettext() look up, just get the plural form */
3019 newstr = (n == 1) ? cp : two;
3023 * If we returned the first string, optimize a bit by
3024 * backing up over comma
3027 xbp->xb_curp = two - 1; /* One for comma */
3029 * If the caller wanted UTF8, we're done; nothing changed,
3030 * but we need to count the columns used.
3032 if (need_enc == XF_ENC_UTF8)
3033 return xo_count_utf8_cols(cp, xbp->xb_curp - cp);
3037 /* The simple case (singular) */
3038 newstr = xo_dgettext(xop, cp);
3041 /* If the caller wanted UTF8, we're done; nothing changed */
3042 if (need_enc == XF_ENC_UTF8)
3048 * Since the new string string might be in gettext's buffer or
3049 * in the buffer (as the plural form), we make a copy.
3051 ssize_t nlen = strlen(newstr);
3052 char *newcopy = alloca(nlen + 1);
3053 memcpy(newcopy, newstr, nlen + 1);
3055 xbp->xb_curp = xbp->xb_bufp + start_offset; /* Reset the buffer */
3056 return xo_format_string_direct(xop, xbp, flags, NULL, newcopy, nlen, 0,
3057 need_enc, XF_ENC_UTF8);
3061 xo_data_append_content (xo_handle_t *xop, const char *str, ssize_t len,
3062 xo_xff_flags_t flags)
3065 int need_enc = xo_needed_encoding(xop);
3066 ssize_t start_offset = xo_buf_offset(&xop->xo_data);
3068 cols = xo_format_string_direct(xop, &xop->xo_data, XFF_UNESCAPE | flags,
3070 need_enc, XF_ENC_UTF8);
3071 if (flags & XFF_GT_FLAGS)
3072 cols = xo_format_gettext(xop, flags, start_offset, cols, need_enc);
3074 if (XOF_ISSET(xop, XOF_COLUMNS))
3075 xop->xo_columns += cols;
3076 if (XOIF_ISSET(xop, XOIF_ANCHOR))
3077 xop->xo_anchor_columns += cols;
3081 xo_bump_width (xo_format_t *xfp, int digit)
3083 int *ip = &xfp->xf_width[xfp->xf_dots];
3085 *ip = ((*ip > 0) ? *ip : 0) * 10 + digit;
3089 xo_trim_ws (xo_buffer_t *xbp, ssize_t len)
3094 /* First trim leading space */
3095 for (cp = sp = xbp->xb_curp, ep = cp + len; cp < ep; cp++) {
3103 memmove(sp, cp, len);
3106 /* Then trim off the end */
3107 for (cp = xbp->xb_curp, sp = ep = cp + len; cp < ep; ep--) {
3122 * Interface to format a single field. The arguments are in xo_vap,
3123 * and the format is in 'fmt'. If 'xbp' is null, we use xop->xo_data;
3124 * this is the most common case.
3127 xo_do_format_field (xo_handle_t *xop, xo_buffer_t *xbp,
3128 const char *fmt, ssize_t flen, xo_xff_flags_t flags)
3131 const char *cp, *ep, *sp, *xp = NULL;
3133 int style = (flags & XFF_XML) ? XO_STYLE_XML : xo_style(xop);
3134 unsigned make_output = !(flags & XFF_NO_OUTPUT) ? 1 : 0;
3135 int need_enc = xo_needed_encoding(xop);
3136 int real_need_enc = need_enc;
3137 ssize_t old_cols = xop->xo_columns;
3139 /* The gettext interface is UTF-8, so we'll need that for now */
3140 if (flags & XFF_GT_FIELD)
3141 need_enc = XF_ENC_UTF8;
3144 xbp = &xop->xo_data;
3146 ssize_t start_offset = xo_buf_offset(xbp);
3148 for (cp = fmt, ep = fmt + flen; cp < ep; cp++) {
3150 * Since we're starting a new field, save the starting offset.
3151 * We'll need this later for field-related operations.
3159 if (*cp == '\\' && cp[1] != '\0')
3163 } if (cp + 1 < ep && cp[1] == '%') {
3170 cols = xo_format_string_direct(xop, xbp, flags | XFF_UNESCAPE,
3171 NULL, xp, cp - xp, -1,
3172 need_enc, XF_ENC_UTF8);
3173 if (XOF_ISSET(xop, XOF_COLUMNS))
3174 xop->xo_columns += cols;
3175 if (XOIF_ISSET(xop, XOIF_ANCHOR))
3176 xop->xo_anchor_columns += cols;
3182 bzero(&xf, sizeof(xf));
3183 xf.xf_leading_zero = -1;
3184 xf.xf_width[0] = xf.xf_width[1] = xf.xf_width[2] = -1;
3187 * "%@" starts an XO-specific set of flags:
3188 * @X@ - XML-only field; ignored if style isn't XML
3191 for (cp += 2; cp < ep; cp++) {
3197 * '*' means there's a "%*.*s" value in vap that
3200 if (!XOF_ISSET(xop, XOF_NO_VA_ARG))
3201 va_arg(xop->xo_vap, int);
3206 /* Hidden fields are only visible to JSON and XML */
3207 if (XOF_ISSET(xop, XFF_ENCODE_ONLY)) {
3208 if (style != XO_STYLE_XML
3209 && !xo_style_is_encoding(xop))
3211 } else if (XOF_ISSET(xop, XFF_DISPLAY_ONLY)) {
3212 if (style != XO_STYLE_TEXT
3213 && xo_style(xop) != XO_STYLE_HTML)
3221 * Looking at one piece of a format; find the end and
3222 * call snprintf. Then advance xo_vap on our own.
3224 * Note that 'n', 'v', and '$' are not supported.
3226 sp = cp; /* Save start pointer */
3227 for (cp += 1; cp < ep; cp++) {
3230 else if (*cp == 'h')
3232 else if (*cp == 'j')
3234 else if (*cp == 't')
3236 else if (*cp == 'z')
3238 else if (*cp == 'q')
3240 else if (*cp == '.') {
3241 if (++xf.xf_dots >= XF_WIDTH_NUM) {
3242 xo_failure(xop, "Too many dots in format: '%s'", fmt);
3245 } else if (*cp == '-')
3246 xf.xf_seen_minus = 1;
3247 else if (isdigit((int) *cp)) {
3248 if (xf.xf_leading_zero < 0)
3249 xf.xf_leading_zero = (*cp == '0');
3250 xo_bump_width(&xf, *cp - '0');
3251 } else if (*cp == '*') {
3253 xf.xf_star[xf.xf_dots] = 1;
3254 } else if (strchr("diouxXDOUeEfFgGaAcCsSpm", *cp) != NULL)
3256 else if (*cp == 'n' || *cp == 'v') {
3257 xo_failure(xop, "unsupported format: '%s'", fmt);
3263 xo_failure(xop, "field format missing format character: %s",
3268 if (!XOF_ISSET(xop, XOF_NO_VA_ARG)) {
3269 if (*cp == 's' || *cp == 'S') {
3270 /* Handle "%*.*.*s" */
3272 for (s = 0; s < XF_WIDTH_NUM; s++) {
3273 if (xf.xf_star[s]) {
3274 xf.xf_width[s] = va_arg(xop->xo_vap, int);
3276 /* Normalize a negative width value */
3277 if (xf.xf_width[s] < 0) {
3279 xf.xf_width[0] = -xf.xf_width[0];
3280 xf.xf_seen_minus = 1;
3282 xf.xf_width[s] = -1; /* Ignore negative values */
3289 /* If no max is given, it defaults to size */
3290 if (xf.xf_width[XF_WIDTH_MAX] < 0 && xf.xf_width[XF_WIDTH_SIZE] >= 0)
3291 xf.xf_width[XF_WIDTH_MAX] = xf.xf_width[XF_WIDTH_SIZE];
3293 if (xf.xf_fc == 'D' || xf.xf_fc == 'O' || xf.xf_fc == 'U')
3297 xo_buffer_t *fbp = &xop->xo_fmt;
3298 ssize_t len = cp - sp + 1;
3299 if (!xo_buf_has_room(fbp, len + 1))
3302 char *newfmt = fbp->xb_curp;
3303 memcpy(newfmt, sp, len);
3304 newfmt[0] = '%'; /* If we skipped over a "%@...@s" format */
3308 * Bad news: our strings are UTF-8, but the stock printf
3309 * functions won't handle field widths for wide characters
3310 * correctly. So we have to handle this ourselves.
3312 if (xop->xo_formatter == NULL
3313 && (xf.xf_fc == 's' || xf.xf_fc == 'S'
3314 || xf.xf_fc == 'm')) {
3316 xf.xf_enc = (xf.xf_fc == 'm') ? XF_ENC_UTF8
3317 : (xf.xf_lflag || (xf.xf_fc == 'S')) ? XF_ENC_WIDE
3318 : xf.xf_hflag ? XF_ENC_LOCALE : XF_ENC_UTF8;
3320 rc = xo_format_string(xop, xbp, flags, &xf);
3322 if ((flags & XFF_TRIM_WS) && xo_style_is_encoding(xop))
3323 rc = xo_trim_ws(xbp, rc);
3326 ssize_t columns = rc = xo_vsnprintf(xop, xbp, newfmt, xop->xo_vap);
3329 * For XML and HTML, we need "&<>" processing; for JSON,
3330 * it's quotes. Text gets nothing.
3334 if (flags & XFF_TRIM_WS)
3335 columns = rc = xo_trim_ws(xbp, rc);
3338 rc = xo_escape_xml(xbp, rc, (flags & XFF_ATTR));
3342 if (flags & XFF_TRIM_WS)
3343 columns = rc = xo_trim_ws(xbp, rc);
3344 rc = xo_escape_json(xbp, rc, 0);
3347 case XO_STYLE_SDPARAMS:
3348 if (flags & XFF_TRIM_WS)
3349 columns = rc = xo_trim_ws(xbp, rc);
3350 rc = xo_escape_sdparams(xbp, rc, 0);
3353 case XO_STYLE_ENCODER:
3354 if (flags & XFF_TRIM_WS)
3355 columns = rc = xo_trim_ws(xbp, rc);
3360 * We can assume all the non-%s data we've
3361 * added is ASCII, so the columns and bytes are the
3362 * same. xo_format_string handles all the fancy
3363 * string conversions and updates xo_anchor_columns
3366 if (XOF_ISSET(xop, XOF_COLUMNS))
3367 xop->xo_columns += columns;
3368 if (XOIF_ISSET(xop, XOIF_ANCHOR))
3369 xop->xo_anchor_columns += columns;
3376 * Now for the tricky part: we need to move the argument pointer
3377 * along by the amount needed.
3379 if (!XOF_ISSET(xop, XOF_NO_VA_ARG)) {
3381 if (xf.xf_fc == 's' ||xf.xf_fc == 'S') {
3383 * The 'S' and 's' formats are normally handled in
3384 * xo_format_string, but if we skipped it, then we
3388 va_arg(xop->xo_vap, char *);
3390 } else if (xf.xf_fc == 'm') {
3391 /* Nothing on the stack for "%m" */
3395 for (s = 0; s < XF_WIDTH_NUM; s++) {
3397 va_arg(xop->xo_vap, int);
3400 if (strchr("diouxXDOU", xf.xf_fc) != NULL) {
3401 if (xf.xf_hflag > 1) {
3402 va_arg(xop->xo_vap, int);
3404 } else if (xf.xf_hflag > 0) {
3405 va_arg(xop->xo_vap, int);
3407 } else if (xf.xf_lflag > 1) {
3408 va_arg(xop->xo_vap, unsigned long long);
3410 } else if (xf.xf_lflag > 0) {
3411 va_arg(xop->xo_vap, unsigned long);
3413 } else if (xf.xf_jflag > 0) {
3414 va_arg(xop->xo_vap, intmax_t);
3416 } else if (xf.xf_tflag > 0) {
3417 va_arg(xop->xo_vap, ptrdiff_t);
3419 } else if (xf.xf_zflag > 0) {
3420 va_arg(xop->xo_vap, size_t);
3422 } else if (xf.xf_qflag > 0) {
3423 va_arg(xop->xo_vap, quad_t);
3426 va_arg(xop->xo_vap, int);
3428 } else if (strchr("eEfFgGaA", xf.xf_fc) != NULL)
3430 va_arg(xop->xo_vap, long double);
3432 va_arg(xop->xo_vap, double);
3434 else if (xf.xf_fc == 'C' || (xf.xf_fc == 'c' && xf.xf_lflag))
3435 va_arg(xop->xo_vap, wint_t);
3437 else if (xf.xf_fc == 'c')
3438 va_arg(xop->xo_vap, int);
3440 else if (xf.xf_fc == 'p')
3441 va_arg(xop->xo_vap, void *);
3448 cols = xo_format_string_direct(xop, xbp, flags | XFF_UNESCAPE,
3449 NULL, xp, cp - xp, -1,
3450 need_enc, XF_ENC_UTF8);
3452 if (XOF_ISSET(xop, XOF_COLUMNS))
3453 xop->xo_columns += cols;
3454 if (XOIF_ISSET(xop, XOIF_ANCHOR))
3455 xop->xo_anchor_columns += cols;
3461 if (flags & XFF_GT_FLAGS) {
3463 * Handle gettext()ing the field by looking up the value
3464 * and then copying it in, while converting to locale, if
3467 ssize_t new_cols = xo_format_gettext(xop, flags, start_offset,
3468 old_cols, real_need_enc);
3470 if (XOF_ISSET(xop, XOF_COLUMNS))
3471 xop->xo_columns += new_cols - old_cols;
3472 if (XOIF_ISSET(xop, XOIF_ANCHOR))
3473 xop->xo_anchor_columns += new_cols - old_cols;
3480 xo_fix_encoding (xo_handle_t *xop UNUSED, char *encoding)
3482 char *cp = encoding;
3484 if (cp[0] != '%' || !isdigit((int) cp[1]))
3487 for (cp += 2; *cp; cp++) {
3488 if (!isdigit((int) *cp))
3499 xo_color_append_html (xo_handle_t *xop)
3502 * If the color buffer has content, we add it now. It's already
3503 * prebuilt and ready, since we want to add it to every <div>.
3505 if (!xo_buf_is_empty(&xop->xo_color_buf)) {
3506 xo_buffer_t *xbp = &xop->xo_color_buf;
3508 xo_data_append(xop, xbp->xb_bufp, xbp->xb_curp - xbp->xb_bufp);
3513 * A wrapper for humanize_number that autoscales, since the
3514 * HN_AUTOSCALE flag scales as needed based on the size of
3515 * the output buffer, not the size of the value. I also
3516 * wish HN_DECIMAL was more imperative, without the <10
3517 * test. But the boat only goes where we want when we hold
3518 * the rudder, so xo_humanize fixes part of the problem.
3521 xo_humanize (char *buf, ssize_t len, uint64_t value, int flags)
3526 uint64_t left = value;
3528 if (flags & HN_DIVISOR_1000) {
3529 for ( ; left; scale++)
3532 for ( ; left; scale++)
3538 return xo_humanize_number(buf, len, value, "", scale, flags);
3542 * This is an area where we can save information from the handle for
3543 * later restoration. We need to know what data was rendered to know
3544 * what needs cleaned up.
3546 typedef struct xo_humanize_save_s {
3547 ssize_t xhs_offset; /* Saved xo_offset */
3548 ssize_t xhs_columns; /* Saved xo_columns */
3549 ssize_t xhs_anchor_columns; /* Saved xo_anchor_columns */
3550 } xo_humanize_save_t;
3553 * Format a "humanized" value for a numeric, meaning something nice
3554 * like "44M" instead of "44470272". We autoscale, choosing the
3555 * most appropriate value for K/M/G/T/P/E based on the value given.
3558 xo_format_humanize (xo_handle_t *xop, xo_buffer_t *xbp,
3559 xo_humanize_save_t *savep, xo_xff_flags_t flags)
3561 if (XOF_ISSET(xop, XOF_NO_HUMANIZE))
3564 ssize_t end_offset = xbp->xb_curp - xbp->xb_bufp;
3565 if (end_offset == savep->xhs_offset) /* Huh? Nothing to render */
3569 * We have a string that's allegedly a number. We want to
3570 * humanize it, which means turning it back into a number
3571 * and calling xo_humanize_number on it.
3576 xo_buf_append(xbp, "", 1); /* NUL-terminate it */
3578 value = strtoull(xbp->xb_bufp + savep->xhs_offset, &ep, 0);
3579 if (!(value == ULLONG_MAX && errno == ERANGE)
3580 && (ep != xbp->xb_bufp + savep->xhs_offset)) {
3582 * There are few values where humanize_number needs
3583 * more bytes than the original value. I've used
3584 * 10 as a rectal number to cover those scenarios.
3586 if (xo_buf_has_room(xbp, 10)) {
3587 xbp->xb_curp = xbp->xb_bufp + savep->xhs_offset;
3590 ssize_t left = (xbp->xb_bufp + xbp->xb_size) - xbp->xb_curp;
3591 int hn_flags = HN_NOSPACE; /* On by default */
3593 if (flags & XFF_HN_SPACE)
3594 hn_flags &= ~HN_NOSPACE;
3596 if (flags & XFF_HN_DECIMAL)
3597 hn_flags |= HN_DECIMAL;
3599 if (flags & XFF_HN_1000)
3600 hn_flags |= HN_DIVISOR_1000;
3602 rc = xo_humanize(xbp->xb_curp, left, value, hn_flags);
3605 xop->xo_columns = savep->xhs_columns + rc;
3606 xop->xo_anchor_columns = savep->xhs_anchor_columns + rc;
3613 xo_buf_append_div (xo_handle_t *xop, const char *class, xo_xff_flags_t flags,
3614 const char *name, ssize_t nlen,
3615 const char *value, ssize_t vlen,
3616 const char *encoding, ssize_t elen)
3618 static char div_start[] = "<div class=\"";
3619 static char div_tag[] = "\" data-tag=\"";
3620 static char div_xpath[] = "\" data-xpath=\"";
3621 static char div_key[] = "\" data-key=\"key";
3622 static char div_end[] = "\">";
3623 static char div_close[] = "</div>";
3625 /* The encoding format defaults to the normal format */
3626 if (encoding == NULL) {
3627 char *enc = alloca(vlen + 1);
3628 memcpy(enc, value, vlen);
3630 encoding = xo_fix_encoding(xop, enc);
3631 elen = strlen(encoding);
3635 * To build our XPath predicate, we need to save the va_list before
3636 * we format our data, and then restore it before we format the
3638 * Display-only keys implies that we've got an encode-only key
3639 * elsewhere, so we don't use them from making predicates.
3641 int need_predidate =
3642 (name && (flags & XFF_KEY) && !(flags & XFF_DISPLAY_ONLY)
3643 && XOF_ISSET(xop, XOF_XPATH)) ? 1 : 0;
3645 if (need_predidate) {
3648 va_copy(va_local, xop->xo_vap);
3649 if (xop->xo_checkpointer)
3650 xop->xo_checkpointer(xop, xop->xo_vap, 0);
3653 * Build an XPath predicate expression to match this key.
3654 * We use the format buffer.
3656 xo_buffer_t *pbp = &xop->xo_predicate;
3657 pbp->xb_curp = pbp->xb_bufp; /* Restart buffer */
3659 xo_buf_append(pbp, "[", 1);
3660 xo_buf_escape(xop, pbp, name, nlen, 0);
3661 if (XOF_ISSET(xop, XOF_PRETTY))
3662 xo_buf_append(pbp, " = '", 4);
3664 xo_buf_append(pbp, "='", 2);
3666 xo_xff_flags_t pflags = flags | XFF_XML | XFF_ATTR;
3667 pflags &= ~(XFF_NO_OUTPUT | XFF_ENCODE_ONLY);
3668 xo_do_format_field(xop, pbp, encoding, elen, pflags);
3670 xo_buf_append(pbp, "']", 2);
3672 /* Now we record this predicate expression in the stack */
3673 xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth];
3674 ssize_t olen = xsp->xs_keys ? strlen(xsp->xs_keys) : 0;
3675 ssize_t dlen = pbp->xb_curp - pbp->xb_bufp;
3677 char *cp = xo_realloc(xsp->xs_keys, olen + dlen + 1);
3679 memcpy(cp + olen, pbp->xb_bufp, dlen);
3680 cp[olen + dlen] = '\0';
3684 /* Now we reset the xo_vap as if we were never here */
3685 va_end(xop->xo_vap);
3686 va_copy(xop->xo_vap, va_local);
3688 if (xop->xo_checkpointer)
3689 xop->xo_checkpointer(xop, xop->xo_vap, 1);
3692 if (flags & XFF_ENCODE_ONLY) {
3694 * Even if this is encode-only, we need to go through the
3695 * work of formatting it to make sure the args are cleared
3698 xo_do_format_field(xop, NULL, encoding, elen,
3699 flags | XFF_NO_OUTPUT);
3703 xo_line_ensure_open(xop, 0);
3705 if (XOF_ISSET(xop, XOF_PRETTY))
3706 xo_buf_indent(xop, xop->xo_indent_by);
3708 xo_data_append(xop, div_start, sizeof(div_start) - 1);
3709 xo_data_append(xop, class, strlen(class));
3712 * If the color buffer has content, we add it now. It's already
3713 * prebuilt and ready, since we want to add it to every <div>.
3715 if (!xo_buf_is_empty(&xop->xo_color_buf)) {
3716 xo_buffer_t *xbp = &xop->xo_color_buf;
3718 xo_data_append(xop, xbp->xb_bufp, xbp->xb_curp - xbp->xb_bufp);
3722 xo_data_append(xop, div_tag, sizeof(div_tag) - 1);
3723 xo_data_escape(xop, name, nlen);
3726 * Save the offset at which we'd place units. See xo_format_units.
3728 if (XOF_ISSET(xop, XOF_UNITS)) {
3729 XOIF_SET(xop, XOIF_UNITS_PENDING);
3731 * Note: We need the '+1' here because we know we've not
3732 * added the closing quote. We add one, knowing the quote
3733 * will be added shortly.
3735 xop->xo_units_offset =
3736 xop->xo_data.xb_curp -xop->xo_data.xb_bufp + 1;
3739 if (XOF_ISSET(xop, XOF_XPATH)) {
3743 xo_data_append(xop, div_xpath, sizeof(div_xpath) - 1);
3744 if (xop->xo_leading_xpath)
3745 xo_data_append(xop, xop->xo_leading_xpath,
3746 strlen(xop->xo_leading_xpath));
3748 for (i = 0; i <= xop->xo_depth; i++) {
3749 xsp = &xop->xo_stack[i];
3750 if (xsp->xs_name == NULL)
3754 * XSS_OPEN_LIST and XSS_OPEN_LEAF_LIST stack frames
3755 * are directly under XSS_OPEN_INSTANCE frames so we
3756 * don't need to put these in our XPath expressions.
3758 if (xsp->xs_state == XSS_OPEN_LIST
3759 || xsp->xs_state == XSS_OPEN_LEAF_LIST)
3762 xo_data_append(xop, "/", 1);
3763 xo_data_escape(xop, xsp->xs_name, strlen(xsp->xs_name));
3765 /* Don't show keys for the key field */
3766 if (i != xop->xo_depth || !(flags & XFF_KEY))
3767 xo_data_append(xop, xsp->xs_keys, strlen(xsp->xs_keys));
3771 xo_data_append(xop, "/", 1);
3772 xo_data_escape(xop, name, nlen);
3775 if (XOF_ISSET(xop, XOF_INFO) && xop->xo_info) {
3776 static char in_type[] = "\" data-type=\"";
3777 static char in_help[] = "\" data-help=\"";
3779 xo_info_t *xip = xo_info_find(xop, name, nlen);
3782 xo_data_append(xop, in_type, sizeof(in_type) - 1);
3783 xo_data_escape(xop, xip->xi_type, strlen(xip->xi_type));
3786 xo_data_append(xop, in_help, sizeof(in_help) - 1);
3787 xo_data_escape(xop, xip->xi_help, strlen(xip->xi_help));
3792 if ((flags & XFF_KEY) && XOF_ISSET(xop, XOF_KEYS))
3793 xo_data_append(xop, div_key, sizeof(div_key) - 1);
3796 xo_buffer_t *xbp = &xop->xo_data;
3797 ssize_t base_offset = xbp->xb_curp - xbp->xb_bufp;
3799 xo_data_append(xop, div_end, sizeof(div_end) - 1);
3801 xo_humanize_save_t save; /* Save values for humanizing logic */
3803 save.xhs_offset = xbp->xb_curp - xbp->xb_bufp;
3804 save.xhs_columns = xop->xo_columns;
3805 save.xhs_anchor_columns = xop->xo_anchor_columns;
3807 xo_do_format_field(xop, NULL, value, vlen, flags);
3809 if (flags & XFF_HUMANIZE) {
3811 * Unlike text style, we want to retain the original value and
3812 * stuff it into the "data-number" attribute.
3814 static const char div_number[] = "\" data-number=\"";
3815 ssize_t div_len = sizeof(div_number) - 1;
3817 ssize_t end_offset = xbp->xb_curp - xbp->xb_bufp;
3818 ssize_t olen = end_offset - save.xhs_offset;
3820 char *cp = alloca(olen + 1);
3821 memcpy(cp, xbp->xb_bufp + save.xhs_offset, olen);
3824 xo_format_humanize(xop, xbp, &save, flags);
3826 if (xo_buf_has_room(xbp, div_len + olen)) {
3827 ssize_t new_offset = xbp->xb_curp - xbp->xb_bufp;
3830 /* Move the humanized string off to the left */
3831 memmove(xbp->xb_bufp + base_offset + div_len + olen,
3832 xbp->xb_bufp + base_offset, new_offset - base_offset);
3834 /* Copy the data_number attribute name */
3835 memcpy(xbp->xb_bufp + base_offset, div_number, div_len);
3837 /* Copy the original long value */
3838 memcpy(xbp->xb_bufp + base_offset + div_len, cp, olen);
3839 xbp->xb_curp += div_len + olen;
3843 xo_data_append(xop, div_close, sizeof(div_close) - 1);
3845 if (XOF_ISSET(xop, XOF_PRETTY))
3846 xo_data_append(xop, "\n", 1);
3850 xo_format_text (xo_handle_t *xop, const char *str, ssize_t len)
3852 switch (xo_style(xop)) {
3854 xo_buf_append_locale(xop, &xop->xo_data, str, len);
3858 xo_buf_append_div(xop, "text", 0, NULL, 0, str, len, NULL, 0);
3864 xo_format_title (xo_handle_t *xop, xo_field_info_t *xfip,
3865 const char *str, ssize_t len)
3867 const char *fmt = xfip->xfi_format;
3868 ssize_t flen = xfip->xfi_flen;
3869 xo_xff_flags_t flags = xfip->xfi_flags;
3871 static char div_open[] = "<div class=\"title";
3872 static char div_middle[] = "\">";
3873 static char div_close[] = "</div>";
3880 switch (xo_style(xop)) {
3883 case XO_STYLE_SDPARAMS:
3884 case XO_STYLE_ENCODER:
3886 * Even though we don't care about text, we need to do
3887 * enough parsing work to skip over the right bits of xo_vap.
3890 xo_do_format_field(xop, NULL, fmt, flen, flags | XFF_NO_OUTPUT);
3894 xo_buffer_t *xbp = &xop->xo_data;
3895 ssize_t start = xbp->xb_curp - xbp->xb_bufp;
3896 ssize_t left = xbp->xb_size - start;
3899 if (xo_style(xop) == XO_STYLE_HTML) {
3900 xo_line_ensure_open(xop, 0);
3901 if (XOF_ISSET(xop, XOF_PRETTY))
3902 xo_buf_indent(xop, xop->xo_indent_by);
3903 xo_buf_append(&xop->xo_data, div_open, sizeof(div_open) - 1);
3904 xo_color_append_html(xop);
3905 xo_buf_append(&xop->xo_data, div_middle, sizeof(div_middle) - 1);
3908 start = xbp->xb_curp - xbp->xb_bufp; /* Reset start */
3910 char *newfmt = alloca(flen + 1);
3911 memcpy(newfmt, fmt, flen);
3912 newfmt[flen] = '\0';
3914 /* If len is non-zero, the format string apply to the name */
3915 char *newstr = alloca(len + 1);
3916 memcpy(newstr, str, len);
3919 if (newstr[len - 1] == 's') {
3922 rc = snprintf(NULL, 0, newfmt, newstr);
3925 * We have to do this the hard way, since we might need
3928 bp = alloca(rc + 1);
3929 rc = snprintf(bp, rc + 1, newfmt, newstr);
3931 xo_data_append_content(xop, bp, rc, flags);
3936 rc = snprintf(xbp->xb_curp, left, newfmt, newstr);
3938 if (!xo_buf_has_room(xbp, rc))
3940 left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp);
3941 rc = snprintf(xbp->xb_curp, left, newfmt, newstr);
3945 if (XOF_ISSET(xop, XOF_COLUMNS))
3946 xop->xo_columns += rc;
3947 if (XOIF_ISSET(xop, XOIF_ANCHOR))
3948 xop->xo_anchor_columns += rc;
3953 xo_do_format_field(xop, NULL, fmt, flen, flags);
3955 /* xo_do_format_field moved curp, so we need to reset it */
3956 rc = xbp->xb_curp - (xbp->xb_bufp + start);
3957 xbp->xb_curp = xbp->xb_bufp + start;
3960 /* If we're styling HTML, then we need to escape it */
3961 if (xo_style(xop) == XO_STYLE_HTML) {
3962 rc = xo_escape_xml(xbp, rc, 0);
3969 if (xo_style(xop) == XO_STYLE_HTML) {
3970 xo_data_append(xop, div_close, sizeof(div_close) - 1);
3971 if (XOF_ISSET(xop, XOF_PRETTY))
3972 xo_data_append(xop, "\n", 1);
3977 xo_format_prep (xo_handle_t *xop, xo_xff_flags_t flags)
3979 if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST) {
3980 xo_data_append(xop, ",", 1);
3981 if (!(flags & XFF_LEAF_LIST) && XOF_ISSET(xop, XOF_PRETTY))
3982 xo_data_append(xop, "\n", 1);
3984 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST;
3988 /* Useful debugging function */
3990 xo_arg (xo_handle_t *xop);
3992 xo_arg (xo_handle_t *xop)
3994 xop = xo_default(xop);
3995 fprintf(stderr, "0x%x", va_arg(xop->xo_vap, unsigned));
4000 xo_format_value (xo_handle_t *xop, const char *name, ssize_t nlen,
4001 const char *format, ssize_t flen,
4002 const char *encoding, ssize_t elen, xo_xff_flags_t flags)
4004 int pretty = XOF_ISSET(xop, XOF_PRETTY);
4008 * Before we emit a value, we need to know that the frame is ready.
4010 xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth];
4012 if (flags & XFF_LEAF_LIST) {
4014 * Check if we've already started to emit normal leafs
4015 * or if we're not in a leaf list.
4017 if ((xsp->xs_flags & (XSF_EMIT | XSF_EMIT_KEY))
4018 || !(xsp->xs_flags & XSF_EMIT_LEAF_LIST)) {
4019 char nbuf[nlen + 1];
4020 memcpy(nbuf, name, nlen);
4023 ssize_t rc = xo_transition(xop, 0, nbuf, XSS_EMIT_LEAF_LIST);
4025 flags |= XFF_DISPLAY_ONLY | XFF_ENCODE_ONLY;
4027 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_EMIT_LEAF_LIST;
4030 xsp = &xop->xo_stack[xop->xo_depth];
4032 name = xsp->xs_name;
4033 nlen = strlen(name);
4036 } else if (flags & XFF_KEY) {
4037 /* Emitting a 'k' (key) field */
4038 if ((xsp->xs_flags & XSF_EMIT) && !(flags & XFF_DISPLAY_ONLY)) {
4039 xo_failure(xop, "key field emitted after normal value field: '%.*s'",
4042 } else if (!(xsp->xs_flags & XSF_EMIT_KEY)) {
4043 char nbuf[nlen + 1];
4044 memcpy(nbuf, name, nlen);
4047 ssize_t rc = xo_transition(xop, 0, nbuf, XSS_EMIT);
4049 flags |= XFF_DISPLAY_ONLY | XFF_ENCODE_ONLY;
4051 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_EMIT_KEY;
4053 xsp = &xop->xo_stack[xop->xo_depth];
4054 xsp->xs_flags |= XSF_EMIT_KEY;
4058 /* Emitting a normal value field */
4059 if ((xsp->xs_flags & XSF_EMIT_LEAF_LIST)
4060 || !(xsp->xs_flags & XSF_EMIT)) {
4061 char nbuf[nlen + 1];
4062 memcpy(nbuf, name, nlen);
4065 ssize_t rc = xo_transition(xop, 0, nbuf, XSS_EMIT);
4067 flags |= XFF_DISPLAY_ONLY | XFF_ENCODE_ONLY;
4069 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_EMIT;
4071 xsp = &xop->xo_stack[xop->xo_depth];
4072 xsp->xs_flags |= XSF_EMIT;
4076 xo_buffer_t *xbp = &xop->xo_data;
4077 xo_humanize_save_t save; /* Save values for humanizing logic */
4079 switch (xo_style(xop)) {
4081 if (flags & XFF_ENCODE_ONLY)
4082 flags |= XFF_NO_OUTPUT;
4084 save.xhs_offset = xbp->xb_curp - xbp->xb_bufp;
4085 save.xhs_columns = xop->xo_columns;
4086 save.xhs_anchor_columns = xop->xo_anchor_columns;
4088 xo_do_format_field(xop, NULL, format, flen, flags);
4090 if (flags & XFF_HUMANIZE)
4091 xo_format_humanize(xop, xbp, &save, flags);
4095 if (flags & XFF_ENCODE_ONLY)
4096 flags |= XFF_NO_OUTPUT;
4098 xo_buf_append_div(xop, "data", flags, name, nlen,
4099 format, flen, encoding, elen);
4104 * Even though we're not making output, we still need to
4105 * let the formatting code handle the va_arg popping.
4107 if (flags & XFF_DISPLAY_ONLY) {
4108 flags |= XFF_NO_OUTPUT;
4109 xo_do_format_field(xop, NULL, format, flen, flags);
4117 char *enc = alloca(flen + 1);
4118 memcpy(enc, format, flen);
4120 format = xo_fix_encoding(xop, enc);
4121 flen = strlen(format);
4125 static char missing[] = "missing-field-name";
4126 xo_failure(xop, "missing field name: %s", format);
4128 nlen = sizeof(missing) - 1;
4132 xo_buf_indent(xop, -1);
4133 xo_data_append(xop, "<", 1);
4134 xo_data_escape(xop, name, nlen);
4136 if (xop->xo_attrs.xb_curp != xop->xo_attrs.xb_bufp) {
4137 xo_data_append(xop, xop->xo_attrs.xb_bufp,
4138 xop->xo_attrs.xb_curp - xop->xo_attrs.xb_bufp);
4139 xop->xo_attrs.xb_curp = xop->xo_attrs.xb_bufp;
4143 * We indicate 'key' fields using the 'key' attribute. While
4144 * this is really committing the crime of mixing meta-data with
4145 * data, it's often useful. Especially when format meta-data is
4146 * difficult to come by.
4148 if ((flags & XFF_KEY) && XOF_ISSET(xop, XOF_KEYS)) {
4149 static char attr[] = " key=\"key\"";
4150 xo_data_append(xop, attr, sizeof(attr) - 1);
4154 * Save the offset at which we'd place units. See xo_format_units.
4156 if (XOF_ISSET(xop, XOF_UNITS)) {
4157 XOIF_SET(xop, XOIF_UNITS_PENDING);
4158 xop->xo_units_offset = xop->xo_data.xb_curp -xop->xo_data.xb_bufp;
4161 xo_data_append(xop, ">", 1);
4162 xo_do_format_field(xop, NULL, format, flen, flags);
4163 xo_data_append(xop, "</", 2);
4164 xo_data_escape(xop, name, nlen);
4165 xo_data_append(xop, ">", 1);
4167 xo_data_append(xop, "\n", 1);
4171 if (flags & XFF_DISPLAY_ONLY) {
4172 flags |= XFF_NO_OUTPUT;
4173 xo_do_format_field(xop, NULL, format, flen, flags);
4181 char *enc = alloca(flen + 1);
4182 memcpy(enc, format, flen);
4184 format = xo_fix_encoding(xop, enc);
4185 flen = strlen(format);
4188 int first = (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST)
4191 xo_format_prep(xop, flags);
4193 if (flags & XFF_QUOTE)
4195 else if (flags & XFF_NOQUOTE)
4197 else if (flen == 0) {
4199 format = "true"; /* JSON encodes empty tags as a boolean true */
4201 } else if (strchr("diouDOUeEfFgG", format[flen - 1]) == NULL)
4207 static char missing[] = "missing-field-name";
4208 xo_failure(xop, "missing field name: %s", format);
4210 nlen = sizeof(missing) - 1;
4213 if (flags & XFF_LEAF_LIST) {
4214 if (!first && pretty)
4215 xo_data_append(xop, "\n", 1);
4217 xo_buf_indent(xop, -1);
4220 xo_buf_indent(xop, -1);
4221 xo_data_append(xop, "\"", 1);
4223 xbp = &xop->xo_data;
4224 ssize_t off = xbp->xb_curp - xbp->xb_bufp;
4226 xo_data_escape(xop, name, nlen);
4228 if (XOF_ISSET(xop, XOF_UNDERSCORES)) {
4229 ssize_t coff = xbp->xb_curp - xbp->xb_bufp;
4230 for ( ; off < coff; off++)
4231 if (xbp->xb_bufp[off] == '-')
4232 xbp->xb_bufp[off] = '_';
4234 xo_data_append(xop, "\":", 2);
4236 xo_data_append(xop, " ", 1);
4240 xo_data_append(xop, "\"", 1);
4242 xo_do_format_field(xop, NULL, format, flen, flags);
4245 xo_data_append(xop, "\"", 1);
4248 case XO_STYLE_SDPARAMS:
4249 if (flags & XFF_DISPLAY_ONLY) {
4250 flags |= XFF_NO_OUTPUT;
4251 xo_do_format_field(xop, NULL, format, flen, flags);
4259 char *enc = alloca(flen + 1);
4260 memcpy(enc, format, flen);
4262 format = xo_fix_encoding(xop, enc);
4263 flen = strlen(format);
4267 static char missing[] = "missing-field-name";
4268 xo_failure(xop, "missing field name: %s", format);
4270 nlen = sizeof(missing) - 1;
4273 xo_data_escape(xop, name, nlen);
4274 xo_data_append(xop, "=\"", 2);
4275 xo_do_format_field(xop, NULL, format, flen, flags);
4276 xo_data_append(xop, "\" ", 2);
4279 case XO_STYLE_ENCODER:
4280 if (flags & XFF_DISPLAY_ONLY) {
4281 flags |= XFF_NO_OUTPUT;
4282 xo_do_format_field(xop, NULL, format, flen, flags);
4286 if (flags & XFF_QUOTE)
4288 else if (flags & XFF_NOQUOTE)
4290 else if (flen == 0) {
4292 format = "true"; /* JSON encodes empty tags as a boolean true */
4294 } else if (strchr("diouxXDOUeEfFgGaAcCp", format[flen - 1]) == NULL)
4303 char *enc = alloca(flen + 1);
4304 memcpy(enc, format, flen);
4306 format = xo_fix_encoding(xop, enc);
4307 flen = strlen(format);
4311 static char missing[] = "missing-field-name";
4312 xo_failure(xop, "missing field name: %s", format);
4314 nlen = sizeof(missing) - 1;
4317 ssize_t name_offset = xo_buf_offset(&xop->xo_data);
4318 xo_data_append(xop, name, nlen);
4319 xo_data_append(xop, "", 1);
4321 ssize_t value_offset = xo_buf_offset(&xop->xo_data);
4322 xo_do_format_field(xop, NULL, format, flen, flags);
4323 xo_data_append(xop, "", 1);
4325 xo_encoder_handle(xop, quote ? XO_OP_STRING : XO_OP_CONTENT,
4326 xo_buf_data(&xop->xo_data, name_offset),
4327 xo_buf_data(&xop->xo_data, value_offset));
4328 xo_buf_reset(&xop->xo_data);
4334 xo_set_gettext_domain (xo_handle_t *xop, xo_field_info_t *xfip,
4335 const char *str, ssize_t len)
4337 const char *fmt = xfip->xfi_format;
4338 ssize_t flen = xfip->xfi_flen;
4340 /* Start by discarding previous domain */
4341 if (xop->xo_gt_domain) {
4342 xo_free(xop->xo_gt_domain);
4343 xop->xo_gt_domain = NULL;
4346 /* An empty {G:} means no domainname */
4347 if (len == 0 && flen == 0)
4350 ssize_t start_offset = -1;
4351 if (len == 0 && flen != 0) {
4352 /* Need to do format the data to get the domainname from args */
4353 start_offset = xop->xo_data.xb_curp - xop->xo_data.xb_bufp;
4354 xo_do_format_field(xop, NULL, fmt, flen, 0);
4356 ssize_t end_offset = xop->xo_data.xb_curp - xop->xo_data.xb_bufp;
4357 len = end_offset - start_offset;
4358 str = xop->xo_data.xb_bufp + start_offset;
4361 xop->xo_gt_domain = xo_strndup(str, len);
4363 /* Reset the current buffer point to avoid emitting the name as output */
4364 if (start_offset >= 0)
4365 xop->xo_data.xb_curp = xop->xo_data.xb_bufp + start_offset;
4369 xo_format_content (xo_handle_t *xop, const char *class_name,
4370 const char *tag_name,
4371 const char *str, ssize_t len, const char *fmt, ssize_t flen,
4372 xo_xff_flags_t flags)
4374 switch (xo_style(xop)) {
4377 xo_data_append_content(xop, str, len, flags);
4379 xo_do_format_field(xop, NULL, fmt, flen, flags);
4388 xo_buf_append_div(xop, class_name, flags, NULL, 0, str, len, NULL, 0);
4393 case XO_STYLE_SDPARAMS:
4400 xo_open_container_h(xop, tag_name);
4401 xo_format_value(xop, "message", 7, str, len, NULL, 0, flags);
4402 xo_close_container_h(xop, tag_name);
4406 * Even though we don't care about labels, we need to do
4407 * enough parsing work to skip over the right bits of xo_vap.
4410 xo_do_format_field(xop, NULL, fmt, flen,
4411 flags | XFF_NO_OUTPUT);
4415 case XO_STYLE_ENCODER:
4417 xo_do_format_field(xop, NULL, fmt, flen,
4418 flags | XFF_NO_OUTPUT);
4423 static const char *xo_color_names[] = {
4424 "default", /* XO_COL_DEFAULT */
4425 "black", /* XO_COL_BLACK */
4426 "red", /* XO_CLOR_RED */
4427 "green", /* XO_COL_GREEN */
4428 "yellow", /* XO_COL_YELLOW */
4429 "blue", /* XO_COL_BLUE */
4430 "magenta", /* XO_COL_MAGENTA */
4431 "cyan", /* XO_COL_CYAN */
4432 "white", /* XO_COL_WHITE */
4437 xo_color_find (const char *str)
4441 for (i = 0; xo_color_names[i]; i++) {
4442 if (strcmp(xo_color_names[i], str) == 0)
4449 static const char *xo_effect_names[] = {
4450 "reset", /* XO_EFF_RESET */
4451 "normal", /* XO_EFF_NORMAL */
4452 "bold", /* XO_EFF_BOLD */
4453 "underline", /* XO_EFF_UNDERLINE */
4454 "inverse", /* XO_EFF_INVERSE */
4458 static const char *xo_effect_on_codes[] = {
4459 "0", /* XO_EFF_RESET */
4460 "0", /* XO_EFF_NORMAL */
4461 "1", /* XO_EFF_BOLD */
4462 "4", /* XO_EFF_UNDERLINE */
4463 "7", /* XO_EFF_INVERSE */
4469 * See comment below re: joy of terminal standards. These can
4470 * be use by just adding:
4471 * + if (newp->xoc_effects & bit)
4472 * code = xo_effect_on_codes[i];
4474 * + code = xo_effect_off_codes[i];
4475 * in xo_color_handle_text.
4477 static const char *xo_effect_off_codes[] = {
4478 "0", /* XO_EFF_RESET */
4479 "0", /* XO_EFF_NORMAL */
4480 "21", /* XO_EFF_BOLD */
4481 "24", /* XO_EFF_UNDERLINE */
4482 "27", /* XO_EFF_INVERSE */
4488 xo_effect_find (const char *str)
4492 for (i = 0; xo_effect_names[i]; i++) {
4493 if (strcmp(xo_effect_names[i], str) == 0)
4501 xo_colors_parse (xo_handle_t *xop, xo_colors_t *xocp, char *str)
4503 #ifdef LIBXO_TEXT_ONLY
4505 #endif /* LIBXO_TEXT_ONLY */
4507 char *cp, *ep, *np, *xp;
4508 ssize_t len = strlen(str);
4512 * Possible tokens: colors, bg-colors, effects, no-effects, "reset".
4514 for (cp = str, ep = cp + len - 1; cp && cp < ep; cp = np) {
4515 /* Trim leading whitespace */
4516 while (isspace((int) *cp))
4519 np = strchr(cp, ',');
4523 /* Trim trailing whitespace */
4524 xp = cp + strlen(cp) - 1;
4525 while (isspace(*xp) && xp > cp)
4528 if (cp[0] == 'f' && cp[1] == 'g' && cp[2] == '-') {
4529 rc = xo_color_find(cp + 3);
4533 xocp->xoc_col_fg = rc;
4535 } else if (cp[0] == 'b' && cp[1] == 'g' && cp[2] == '-') {
4536 rc = xo_color_find(cp + 3);
4539 xocp->xoc_col_bg = rc;
4541 } else if (cp[0] == 'n' && cp[1] == 'o' && cp[2] == '-') {
4542 rc = xo_effect_find(cp + 3);
4545 xocp->xoc_effects &= ~(1 << rc);
4548 rc = xo_effect_find(cp);
4551 xocp->xoc_effects |= 1 << rc;
4555 xocp->xoc_col_fg = xocp->xoc_col_bg = 0;
4556 /* Note: not "|=" since we want to wipe out the old value */
4557 xocp->xoc_effects = XO_EFF_RESET;
4561 xocp->xoc_effects &= ~(XO_EFF_BOLD | XO_EFF_UNDERLINE
4562 | XO_EFF_INVERSE | XO_EFF_NORMAL);
4569 if (XOF_ISSET(xop, XOF_WARN))
4570 xo_failure(xop, "unknown color/effect string detected: '%s'", cp);
4575 xo_colors_enabled (xo_handle_t *xop UNUSED)
4577 #ifdef LIBXO_TEXT_ONLY
4579 #else /* LIBXO_TEXT_ONLY */
4580 return XOF_ISSET(xop, XOF_COLOR);
4581 #endif /* LIBXO_TEXT_ONLY */
4585 xo_colors_handle_text (xo_handle_t *xop, xo_colors_t *newp)
4588 char *cp = buf, *ep = buf + sizeof(buf);
4590 xo_colors_t *oldp = &xop->xo_colors;
4591 const char *code = NULL;
4594 * Start the buffer with an escape. We don't want to add the '['
4595 * now, since we let xo_effect_text_add unconditionally add the ';'.
4596 * We'll replace the first ';' with a '[' when we're done.
4598 *cp++ = 0x1b; /* Escape */
4601 * Terminals were designed back in the age before "certainty" was
4602 * invented, when standards were more what you'd call "guidelines"
4603 * than actual rules. Anyway we can't depend on them to operate
4604 * correctly. So when display attributes are changed, we punt,
4605 * reseting them all and turning back on the ones we want to keep.
4606 * Longer, but should be completely reliable. Savvy?
4608 if (oldp->xoc_effects != (newp->xoc_effects & oldp->xoc_effects)) {
4609 newp->xoc_effects |= XO_EFF_RESET;
4610 oldp->xoc_effects = 0;
4613 for (i = 0, bit = 1; xo_effect_names[i]; i++, bit <<= 1) {
4614 if ((newp->xoc_effects & bit) == (oldp->xoc_effects & bit))
4617 code = xo_effect_on_codes[i];
4619 cp += snprintf(cp, ep - cp, ";%s", code);
4621 return; /* Should not occur */
4623 if (bit == XO_EFF_RESET) {
4624 /* Mark up the old value so we can detect current values as new */
4625 oldp->xoc_effects = 0;
4626 oldp->xoc_col_fg = oldp->xoc_col_bg = XO_COL_DEFAULT;
4630 if (newp->xoc_col_fg != oldp->xoc_col_fg) {
4631 cp += snprintf(cp, ep - cp, ";3%u",
4632 (newp->xoc_col_fg != XO_COL_DEFAULT)
4633 ? newp->xoc_col_fg - 1 : 9);
4636 if (newp->xoc_col_bg != oldp->xoc_col_bg) {
4637 cp += snprintf(cp, ep - cp, ";4%u",
4638 (newp->xoc_col_bg != XO_COL_DEFAULT)
4639 ? newp->xoc_col_bg - 1 : 9);
4642 if (cp - buf != 1 && cp < ep - 3) {
4643 buf[1] = '['; /* Overwrite leading ';' */
4646 xo_buf_append(&xop->xo_data, buf, cp - buf);
4651 xo_colors_handle_html (xo_handle_t *xop, xo_colors_t *newp)
4653 xo_colors_t *oldp = &xop->xo_colors;
4656 * HTML colors are mostly trivial: fill in xo_color_buf with
4657 * a set of class tags representing the colors and effects.
4660 /* If nothing changed, then do nothing */
4661 if (oldp->xoc_effects == newp->xoc_effects
4662 && oldp->xoc_col_fg == newp->xoc_col_fg
4663 && oldp->xoc_col_bg == newp->xoc_col_bg)
4667 xo_buffer_t *xbp = &xop->xo_color_buf;
4669 xo_buf_reset(xbp); /* We rebuild content after each change */
4671 for (i = 0, bit = 1; xo_effect_names[i]; i++, bit <<= 1) {
4672 if (!(newp->xoc_effects & bit))
4675 xo_buf_append_str(xbp, " effect-");
4676 xo_buf_append_str(xbp, xo_effect_names[i]);
4679 const char *fg = NULL;
4680 const char *bg = NULL;
4682 if (newp->xoc_col_fg != XO_COL_DEFAULT)
4683 fg = xo_color_names[newp->xoc_col_fg];
4684 if (newp->xoc_col_bg != XO_COL_DEFAULT)
4685 bg = xo_color_names[newp->xoc_col_bg];
4687 if (newp->xoc_effects & XO_EFF_INVERSE) {
4688 const char *tmp = fg;
4699 xo_buf_append_str(xbp, " color-fg-");
4700 xo_buf_append_str(xbp, fg);
4704 xo_buf_append_str(xbp, " color-bg-");
4705 xo_buf_append_str(xbp, bg);
4710 xo_format_colors (xo_handle_t *xop, xo_field_info_t *xfip,
4711 const char *str, ssize_t len)
4713 const char *fmt = xfip->xfi_format;
4714 ssize_t flen = xfip->xfi_flen;
4718 /* If the string is static and we've in an encoding style, bail */
4719 if (len != 0 && xo_style_is_encoding(xop))
4725 xo_buf_append(&xb, str, len);
4727 xo_do_format_field(xop, &xb, fmt, flen, 0);
4729 xo_buf_append(&xb, "reset", 6); /* Default if empty */
4731 if (xo_colors_enabled(xop)) {
4732 switch (xo_style(xop)) {
4735 xo_buf_append(&xb, "", 1);
4737 xo_colors_t xoc = xop->xo_colors;
4738 xo_colors_parse(xop, &xoc, xb.xb_bufp);
4740 if (xo_style(xop) == XO_STYLE_TEXT) {
4742 * Text mode means emitting the colors as ANSI character
4743 * codes. This will allow people who like colors to have
4744 * colors. The issue is, of course conflicting with the
4745 * user's perfectly reasonable color scheme. Which leads
4746 * to the hell of LSCOLORS, where even app need to have
4747 * customization hooks for adjusting colors. Instead we
4748 * provide a simpler-but-still-annoying answer where one
4749 * can map colors to other colors.
4751 xo_colors_handle_text(xop, &xoc);
4752 xoc.xoc_effects &= ~XO_EFF_RESET; /* After handling it */
4756 * HTML output is wrapped in divs, so the color information
4757 * must appear in every div until cleared. Most pathetic.
4760 xoc.xoc_effects &= ~XO_EFF_RESET; /* Before handling effects */
4761 xo_colors_handle_html(xop, &xoc);
4764 xop->xo_colors = xoc;
4769 case XO_STYLE_SDPARAMS:
4770 case XO_STYLE_ENCODER:
4772 * Nothing to do; we did all that work just to clear the stack of
4773 * formatting arguments.
4779 xo_buf_cleanup(&xb);
4783 xo_format_units (xo_handle_t *xop, xo_field_info_t *xfip,
4784 const char *str, ssize_t len)
4786 const char *fmt = xfip->xfi_format;
4787 ssize_t flen = xfip->xfi_flen;
4788 xo_xff_flags_t flags = xfip->xfi_flags;
4790 static char units_start_xml[] = " units=\"";
4791 static char units_start_html[] = " data-units=\"";
4793 if (!XOIF_ISSET(xop, XOIF_UNITS_PENDING)) {
4794 xo_format_content(xop, "units", NULL, str, len, fmt, flen, flags);
4798 xo_buffer_t *xbp = &xop->xo_data;
4799 ssize_t start = xop->xo_units_offset;
4800 ssize_t stop = xbp->xb_curp - xbp->xb_bufp;
4802 if (xo_style(xop) == XO_STYLE_XML)
4803 xo_buf_append(xbp, units_start_xml, sizeof(units_start_xml) - 1);
4804 else if (xo_style(xop) == XO_STYLE_HTML)
4805 xo_buf_append(xbp, units_start_html, sizeof(units_start_html) - 1);
4810 xo_data_escape(xop, str, len);
4812 xo_do_format_field(xop, NULL, fmt, flen, flags);
4814 xo_buf_append(xbp, "\"", 1);
4816 ssize_t now = xbp->xb_curp - xbp->xb_bufp;
4817 ssize_t delta = now - stop;
4818 if (delta <= 0) { /* Strange; no output to move */
4819 xbp->xb_curp = xbp->xb_bufp + stop; /* Reset buffer to prior state */
4824 * Now we're in it alright. We've need to insert the unit value
4825 * we just created into the right spot. We make a local copy,
4826 * move it and then insert our copy. We know there's room in the
4827 * buffer, since we're just moving this around.
4829 char *buf = alloca(delta);
4831 memcpy(buf, xbp->xb_bufp + stop, delta);
4832 memmove(xbp->xb_bufp + start + delta, xbp->xb_bufp + start, stop - start);
4833 memmove(xbp->xb_bufp + start, buf, delta);
4837 xo_find_width (xo_handle_t *xop, xo_field_info_t *xfip,
4838 const char *str, ssize_t len)
4840 const char *fmt = xfip->xfi_format;
4841 ssize_t flen = xfip->xfi_flen;
4848 bp = alloca(len + 1); /* Make local NUL-terminated copy of str */
4849 memcpy(bp, str, len);
4852 width = strtol(bp, &cp, 0);
4853 if (width == LONG_MIN || width == LONG_MAX
4854 || bp == cp || *cp != '\0' ) {
4856 xo_failure(xop, "invalid width for anchor: '%s'", bp);
4859 if (flen != 2 || strncmp("%d", fmt, flen) != 0)
4860 xo_failure(xop, "invalid width format: '%*.*s'", flen, flen, fmt);
4861 if (!XOF_ISSET(xop, XOF_NO_VA_ARG))
4862 width = va_arg(xop->xo_vap, int);
4869 xo_anchor_clear (xo_handle_t *xop)
4871 XOIF_CLEAR(xop, XOIF_ANCHOR);
4872 xop->xo_anchor_offset = 0;
4873 xop->xo_anchor_columns = 0;
4874 xop->xo_anchor_min_width = 0;
4878 * An anchor is a marker used to delay field width implications.
4879 * Imagine the format string "{[:10}{min:%d}/{cur:%d}/{max:%d}{:]}".
4880 * We are looking for output like " 1/4/5"
4882 * To make this work, we record the anchor and then return to
4883 * format it when the end anchor tag is seen.
4886 xo_anchor_start (xo_handle_t *xop, xo_field_info_t *xfip,
4887 const char *str, ssize_t len)
4889 if (xo_style(xop) != XO_STYLE_TEXT && xo_style(xop) != XO_STYLE_HTML)
4892 if (XOIF_ISSET(xop, XOIF_ANCHOR))
4893 xo_failure(xop, "the anchor already recording is discarded");
4895 XOIF_SET(xop, XOIF_ANCHOR);
4896 xo_buffer_t *xbp = &xop->xo_data;
4897 xop->xo_anchor_offset = xbp->xb_curp - xbp->xb_bufp;
4898 xop->xo_anchor_columns = 0;
4901 * Now we find the width, if possible. If it's not there,
4902 * we'll get it on the end anchor.
4904 xop->xo_anchor_min_width = xo_find_width(xop, xfip, str, len);
4908 xo_anchor_stop (xo_handle_t *xop, xo_field_info_t *xfip,
4909 const char *str, ssize_t len)
4911 if (xo_style(xop) != XO_STYLE_TEXT && xo_style(xop) != XO_STYLE_HTML)
4914 if (!XOIF_ISSET(xop, XOIF_ANCHOR)) {
4915 xo_failure(xop, "no start anchor");
4919 XOIF_CLEAR(xop, XOIF_UNITS_PENDING);
4921 ssize_t width = xo_find_width(xop, xfip, str, len);
4923 width = xop->xo_anchor_min_width;
4925 if (width == 0) /* No width given; nothing to do */
4928 xo_buffer_t *xbp = &xop->xo_data;
4929 ssize_t start = xop->xo_anchor_offset;
4930 ssize_t stop = xbp->xb_curp - xbp->xb_bufp;
4931 ssize_t abswidth = (width > 0) ? width : -width;
4932 ssize_t blen = abswidth - xop->xo_anchor_columns;
4934 if (blen <= 0) /* Already over width */
4937 if (abswidth > XO_MAX_ANCHOR_WIDTH) {
4938 xo_failure(xop, "width over %u are not supported",
4939 XO_MAX_ANCHOR_WIDTH);
4943 /* Make a suitable padding field and emit it */
4944 char *buf = alloca(blen);
4945 memset(buf, ' ', blen);
4946 xo_format_content(xop, "padding", NULL, buf, blen, NULL, 0, 0);
4948 if (width < 0) /* Already left justified */
4951 ssize_t now = xbp->xb_curp - xbp->xb_bufp;
4952 ssize_t delta = now - stop;
4953 if (delta <= 0) /* Strange; no output to move */
4957 * Now we're in it alright. We've need to insert the padding data
4958 * we just created (which might be an HTML <div> or text) before
4959 * the formatted data. We make a local copy, move it and then
4960 * insert our copy. We know there's room in the buffer, since
4961 * we're just moving this around.
4964 buf = alloca(delta); /* Expand buffer if needed */
4966 memcpy(buf, xbp->xb_bufp + stop, delta);
4967 memmove(xbp->xb_bufp + start + delta, xbp->xb_bufp + start, stop - start);
4968 memmove(xbp->xb_bufp + start, buf, delta);
4971 xo_anchor_clear(xop);
4975 xo_class_name (int ftype)
4978 case 'D': return "decoration";
4979 case 'E': return "error";
4980 case 'L': return "label";
4981 case 'N': return "note";
4982 case 'P': return "padding";
4983 case 'W': return "warning";
4990 xo_tag_name (int ftype)
4993 case 'E': return "__error";
4994 case 'W': return "__warning";
5001 xo_role_wants_default_format (int ftype)
5004 /* These roles can be completely empty and/or without formatting */
5015 static xo_mapping_t xo_role_names[] = {
5017 { 'D', "decoration" },
5026 { '[', "start-anchor" },
5027 { ']', "stop-anchor" },
5031 #define XO_ROLE_EBRACE '{' /* Escaped braces */
5032 #define XO_ROLE_TEXT '+'
5033 #define XO_ROLE_NEWLINE '\n'
5035 static xo_mapping_t xo_modifier_names[] = {
5036 { XFF_ARGUMENT, "argument" },
5037 { XFF_COLON, "colon" },
5038 { XFF_COMMA, "comma" },
5039 { XFF_DISPLAY_ONLY, "display" },
5040 { XFF_ENCODE_ONLY, "encoding" },
5041 { XFF_GT_FIELD, "gettext" },
5042 { XFF_HUMANIZE, "humanize" },
5043 { XFF_HUMANIZE, "hn" },
5044 { XFF_HN_SPACE, "hn-space" },
5045 { XFF_HN_DECIMAL, "hn-decimal" },
5046 { XFF_HN_1000, "hn-1000" },
5048 { XFF_LEAF_LIST, "leaf-list" },
5049 { XFF_LEAF_LIST, "list" },
5050 { XFF_NOQUOTE, "no-quotes" },
5051 { XFF_NOQUOTE, "no-quote" },
5052 { XFF_GT_PLURAL, "plural" },
5053 { XFF_QUOTE, "quotes" },
5054 { XFF_QUOTE, "quote" },
5055 { XFF_TRIM_WS, "trim" },
5056 { XFF_WS, "white" },
5060 #ifdef NOT_NEEDED_YET
5061 static xo_mapping_t xo_modifier_short_names[] = {
5063 { XFF_DISPLAY_ONLY, "d" },
5064 { XFF_ENCODE_ONLY, "e" },
5065 { XFF_GT_FIELD, "g" },
5066 { XFF_HUMANIZE, "h" },
5068 { XFF_LEAF_LIST, "l" },
5069 { XFF_NOQUOTE, "n" },
5070 { XFF_GT_PLURAL, "p" },
5072 { XFF_TRIM_WS, "t" },
5076 #endif /* NOT_NEEDED_YET */
5079 xo_count_fields (xo_handle_t *xop UNUSED, const char *fmt)
5084 for (cp = fmt; *cp; cp++)
5085 if (*cp == '{' || *cp == '\n')
5092 * The field format is:
5093 * '{' modifiers ':' content [ '/' print-fmt [ '/' encode-fmt ]] '}'
5094 * Roles are optional and include the following field types:
5095 * 'D': decoration; something non-text and non-data (colons, commmas)
5096 * 'E': error message
5097 * 'G': gettext() the entire string; optional domainname as content
5098 * 'L': label; text preceding data
5099 * 'N': note; text following data
5100 * 'P': padding; whitespace
5101 * 'T': Title, where 'content' is a column title
5102 * 'U': Units, where 'content' is the unit label
5103 * 'V': value, where 'content' is the name of the field (the default)
5104 * 'W': warning message
5105 * '[': start a section of anchored text
5106 * ']': end a section of anchored text
5107 * The following modifiers are also supported:
5108 * 'a': content is provided via argument (const char *), not descriptor
5109 * 'c': flag: emit a colon after the label
5110 * 'd': field is only emitted for display styles (text and html)
5111 * 'e': field is only emitted for encoding styles (xml and json)
5112 * 'g': gettext() the field
5113 * 'h': humanize a numeric value (only for display styles)
5114 * 'k': this field is a key, suitable for XPath predicates
5115 * 'l': a leaf-list, a simple list of values
5116 * 'n': no quotes around this field
5117 * 'p': the field has plural gettext semantics (ngettext)
5118 * 'q': add quotes around this field
5119 * 't': trim whitespace around the value
5120 * 'w': emit a blank after the label
5121 * The print-fmt and encode-fmt strings is the printf-style formating
5122 * for this data. JSON and XML will use the encoding-fmt, if present.
5123 * If the encode-fmt is not provided, it defaults to the print-fmt.
5124 * If the print-fmt is not provided, it defaults to 's'.
5127 xo_parse_roles (xo_handle_t *xop, const char *fmt,
5128 const char *basep, xo_field_info_t *xfip)
5132 xo_xff_flags_t flags = 0;
5135 for (sp = basep; sp && *sp; sp++) {
5136 if (*sp == ':' || *sp == '/' || *sp == '}')
5140 if (sp[1] == '\0') {
5141 xo_failure(xop, "backslash at the end of string");
5145 /* Anything backslashed is ignored */
5152 for (np = ++sp; *np; np++)
5153 if (*np == ':' || *np == '/' || *np == '}' || *np == ',')
5156 ssize_t slen = np - sp;
5158 xo_xff_flags_t value;
5160 value = xo_name_lookup(xo_role_names, sp, slen);
5164 value = xo_name_lookup(xo_modifier_names, sp, slen);
5168 xo_failure(xop, "unknown keyword ignored: '%.*s'",
5192 xo_failure(xop, "field descriptor uses multiple types: '%s'",
5209 fnum = (fnum * 10) + (*sp - '0');
5213 flags |= XFF_ARGUMENT;
5221 flags |= XFF_DISPLAY_ONLY;
5225 flags |= XFF_ENCODE_ONLY;
5229 flags |= XFF_GT_FIELD;
5233 flags |= XFF_HUMANIZE;
5241 flags |= XFF_LEAF_LIST;
5245 flags |= XFF_NOQUOTE;
5249 flags |= XFF_GT_PLURAL;
5257 flags |= XFF_TRIM_WS;
5265 xo_failure(xop, "field descriptor uses unknown modifier: '%s'",
5268 * No good answer here; a bad format will likely
5269 * mean a core file. We just return and hope
5270 * the caller notices there's no output, and while
5271 * that seems, well, bad, there's nothing better.
5276 if (ftype == 'N' || ftype == 'U') {
5277 if (flags & XFF_COLON) {
5278 xo_failure(xop, "colon modifier on 'N' or 'U' field ignored: "
5279 "'%s'", xo_printable(fmt));
5280 flags &= ~XFF_COLON;
5285 xfip->xfi_flags = flags;
5286 xfip->xfi_ftype = ftype ?: 'V';
5287 xfip->xfi_fnum = fnum;
5293 * Number any remaining fields that need numbers. Note that some
5294 * field types (text, newline, escaped braces) never get numbers.
5297 xo_gettext_finish_numbering_fields (xo_handle_t *xop UNUSED,
5298 const char *fmt UNUSED,
5299 xo_field_info_t *fields)
5301 xo_field_info_t *xfip;
5302 unsigned fnum, max_fields;
5305 /* First make a list of add the explicitly used bits */
5306 for (xfip = fields, fnum = 0; xfip->xfi_ftype; xfip++) {
5307 switch (xfip->xfi_ftype) {
5308 case XO_ROLE_NEWLINE: /* Don't get numbered */
5310 case XO_ROLE_EBRACE:
5320 bits |= 1 << xfip->xfi_fnum;
5325 for (xfip = fields, fnum = 0; xfip->xfi_ftype; xfip++) {
5326 switch (xfip->xfi_ftype) {
5327 case XO_ROLE_NEWLINE: /* Don't get numbered */
5329 case XO_ROLE_EBRACE:
5334 if (xfip->xfi_fnum != 0)
5337 /* Find the next unassigned field */
5338 for (fnum++; bits & (1 << fnum); fnum++)
5341 if (fnum > max_fields)
5344 xfip->xfi_fnum = fnum; /* Mark the field number */
5345 bits |= 1 << fnum; /* Mark it used */
5350 * The format string uses field numbers, so we need to whiffle through it
5351 * and make sure everything's sane and lovely.
5354 xo_parse_field_numbers (xo_handle_t *xop, const char *fmt,
5355 xo_field_info_t *fields, unsigned num_fields)
5357 xo_field_info_t *xfip;
5358 unsigned field, fnum;
5361 for (xfip = fields, field = 0; field < num_fields; xfip++, field++) {
5362 /* Fields default to 1:1 with natural position */
5363 if (xfip->xfi_fnum == 0)
5364 xfip->xfi_fnum = field + 1;
5365 else if (xfip->xfi_fnum > num_fields) {
5366 xo_failure(xop, "field number exceeds number of fields: '%s'", fmt);
5370 fnum = xfip->xfi_fnum - 1; /* Move to zero origin */
5371 if (fnum < 64) { /* Only test what fits */
5372 if (bits & (1 << fnum)) {
5373 xo_failure(xop, "field number %u reused: '%s'",
5374 xfip->xfi_fnum, fmt);
5385 xo_parse_fields (xo_handle_t *xop, xo_field_info_t *fields,
5386 unsigned num_fields, const char *fmt)
5388 const char *cp, *sp, *ep, *basep;
5390 xo_field_info_t *xfip = fields;
5391 unsigned seen_fnum = 0;
5393 for (cp = fmt; *cp && field < num_fields; field++, xfip++) {
5394 xfip->xfi_start = cp;
5397 xfip->xfi_ftype = XO_ROLE_NEWLINE;
5405 for (sp = cp; *sp; sp++) {
5406 if (*sp == '{' || *sp == '\n')
5410 xfip->xfi_ftype = XO_ROLE_TEXT;
5411 xfip->xfi_content = cp;
5412 xfip->xfi_clen = sp - cp;
5413 xfip->xfi_next = sp;
5419 if (cp[1] == '{') { /* Start of {{escaped braces}} */
5420 xfip->xfi_start = cp + 1; /* Start at second brace */
5421 xfip->xfi_ftype = XO_ROLE_EBRACE;
5423 cp += 2; /* Skip over _both_ characters */
5424 for (sp = cp; *sp; sp++) {
5425 if (*sp == '}' && sp[1] == '}')
5429 xo_failure(xop, "missing closing '}}': '%s'",
5434 xfip->xfi_len = sp - xfip->xfi_start + 1;
5436 /* Move along the string, but don't run off the end */
5437 if (*sp == '}' && sp[1] == '}')
5440 xfip->xfi_next = cp;
5444 /* We are looking at the start of a field definition */
5445 xfip->xfi_start = basep = cp + 1;
5447 const char *format = NULL;
5450 /* Looking at roles and modifiers */
5451 sp = xo_parse_roles(xop, fmt, basep, xfip);
5453 /* xo_failure has already been called */
5460 /* Looking at content */
5462 for (ep = ++sp; *sp; sp++) {
5463 if (*sp == '}' || *sp == '/')
5466 if (sp[1] == '\0') {
5467 xo_failure(xop, "backslash at the end of string");
5475 xfip->xfi_clen = sp - ep;
5476 xfip->xfi_content = ep;
5479 xo_failure(xop, "missing content (':'): '%s'", xo_printable(fmt));
5483 /* Looking at main (display) format */
5485 for (ep = ++sp; *sp; sp++) {
5486 if (*sp == '}' || *sp == '/')
5489 if (sp[1] == '\0') {
5490 xo_failure(xop, "backslash at the end of string");
5501 /* Looking at encoding format */
5503 for (ep = ++sp; *sp; sp++) {
5508 xfip->xfi_encoding = ep;
5509 xfip->xfi_elen = sp - ep;
5513 xo_failure(xop, "missing closing '}': %s", xo_printable(fmt));
5517 xfip->xfi_len = sp - xfip->xfi_start;
5518 xfip->xfi_next = ++sp;
5520 /* If we have content, then we have a default format */
5521 if (xfip->xfi_clen || format || (xfip->xfi_flags & XFF_ARGUMENT)) {
5523 xfip->xfi_format = format;
5524 xfip->xfi_flen = flen;
5525 } else if (xo_role_wants_default_format(xfip->xfi_ftype)) {
5526 xfip->xfi_format = xo_default_format;
5537 * If we saw a field number on at least one field, then we need
5538 * to enforce some rules and/or guidelines.
5541 rc = xo_parse_field_numbers(xop, fmt, fields, field);
5547 * We are passed a pointer to a format string just past the "{G:}"
5548 * field. We build a simplified version of the format string.
5551 xo_gettext_simplify_format (xo_handle_t *xop UNUSED,
5553 xo_field_info_t *fields,
5555 const char *fmt UNUSED,
5556 xo_simplify_field_func_t field_cb)
5559 xo_xff_flags_t flags;
5560 int field = this_field + 1;
5561 xo_field_info_t *xfip;
5564 for (xfip = &fields[field]; xfip->xfi_ftype; xfip++, field++) {
5565 ftype = xfip->xfi_ftype;
5566 flags = xfip->xfi_flags;
5568 if ((flags & XFF_GT_FIELD) && xfip->xfi_content && ftype != 'V') {
5570 field_cb(xfip->xfi_content, xfip->xfi_clen,
5571 (flags & XFF_GT_PLURAL) ? 1 : 0);
5576 /* Ignore gettext roles */
5579 case XO_ROLE_NEWLINE:
5580 xo_buf_append(xbp, "\n", 1);
5583 case XO_ROLE_EBRACE:
5584 xo_buf_append(xbp, "{", 1);
5585 xo_buf_append(xbp, xfip->xfi_content, xfip->xfi_clen);
5586 xo_buf_append(xbp, "}", 1);
5590 xo_buf_append(xbp, xfip->xfi_content, xfip->xfi_clen);
5594 xo_buf_append(xbp, "{", 1);
5597 xo_buf_append(xbp, &ch, 1);
5600 unsigned fnum = xfip->xfi_fnum ?: 0;
5603 /* Field numbers are origin 1, not 0, following printf(3) */
5604 snprintf(num, sizeof(num), "%u", fnum);
5605 xo_buf_append(xbp, num, strlen(num));
5608 xo_buf_append(xbp, ":", 1);
5609 xo_buf_append(xbp, xfip->xfi_content, xfip->xfi_clen);
5610 xo_buf_append(xbp, "}", 1);
5614 xo_buf_append(xbp, "", 1);
5619 xo_dump_fields (xo_field_info_t *); /* Fake prototype for debug function */
5621 xo_dump_fields (xo_field_info_t *fields)
5623 xo_field_info_t *xfip;
5625 for (xfip = fields; xfip->xfi_ftype; xfip++) {
5626 printf("%lu(%u): %lx [%c/%u] [%.*s] [%.*s] [%.*s]\n",
5627 (unsigned long) (xfip - fields), xfip->xfi_fnum,
5628 (unsigned long) xfip->xfi_flags,
5629 isprint((int) xfip->xfi_ftype) ? xfip->xfi_ftype : ' ',
5631 (int) xfip->xfi_clen, xfip->xfi_content ?: "",
5632 (int) xfip->xfi_flen, xfip->xfi_format ?: "",
5633 (int) xfip->xfi_elen, xfip->xfi_encoding ?: "");
5639 * Find the field that matches the given field number
5641 static xo_field_info_t *
5642 xo_gettext_find_field (xo_field_info_t *fields, unsigned fnum)
5644 xo_field_info_t *xfip;
5646 for (xfip = fields; xfip->xfi_ftype; xfip++)
5647 if (xfip->xfi_fnum == fnum)
5654 * At this point, we need to consider if the fields have been reordered,
5655 * such as "The {:adjective} {:noun}" to "La {:noun} {:adjective}".
5657 * We need to rewrite the new_fields using the old fields order,
5658 * so that we can render the message using the arguments as they
5659 * appear on the stack. It's a lot of work, but we don't really
5660 * want to (eventually) fall into the standard printf code which
5661 * means using the arguments straight (and in order) from the
5662 * varargs we were originally passed.
5665 xo_gettext_rewrite_fields (xo_handle_t *xop UNUSED,
5666 xo_field_info_t *fields, unsigned max_fields)
5668 xo_field_info_t tmp[max_fields];
5669 bzero(tmp, max_fields * sizeof(tmp[0]));
5672 xo_field_info_t *newp, *outp, *zp;
5673 for (newp = fields, outp = tmp; newp->xfi_ftype; newp++, outp++) {
5674 switch (newp->xfi_ftype) {
5675 case XO_ROLE_NEWLINE: /* Don't get numbered */
5677 case XO_ROLE_EBRACE:
5680 outp->xfi_renum = 0;
5684 zp = xo_gettext_find_field(fields, ++fnum);
5685 if (zp == NULL) { /* Should not occur */
5687 outp->xfi_renum = 0;
5692 outp->xfi_renum = newp->xfi_fnum;
5695 memcpy(fields, tmp, max_fields * sizeof(tmp[0]));
5699 * We've got two lists of fields, the old list from the original
5700 * format string and the new one from the parsed gettext reply. The
5701 * new list has the localized words, where the old list has the
5702 * formatting information. We need to combine them into a single list
5705 * If the list needs to be reordered, then we've got more serious work
5709 xo_gettext_combine_formats (xo_handle_t *xop, const char *fmt UNUSED,
5710 const char *gtfmt, xo_field_info_t *old_fields,
5711 xo_field_info_t *new_fields, unsigned new_max_fields,
5715 xo_field_info_t *newp, *oldp, *startp = old_fields;
5717 xo_gettext_finish_numbering_fields(xop, fmt, old_fields);
5719 for (newp = new_fields; newp->xfi_ftype; newp++) {
5720 switch (newp->xfi_ftype) {
5721 case XO_ROLE_NEWLINE:
5723 case XO_ROLE_EBRACE:
5727 for (oldp = startp; oldp->xfi_ftype; oldp++) {
5728 if (oldp->xfi_ftype != 'V')
5730 if (newp->xfi_clen != oldp->xfi_clen
5731 || strncmp(newp->xfi_content, oldp->xfi_content,
5732 oldp->xfi_clen) != 0) {
5740 /* Didn't find it on the first pass (starting from start) */
5741 if (oldp->xfi_ftype == 0) {
5742 for (oldp = old_fields; oldp < startp; oldp++) {
5743 if (oldp->xfi_ftype != 'V')
5745 if (newp->xfi_clen != oldp->xfi_clen)
5747 if (strncmp(newp->xfi_content, oldp->xfi_content,
5748 oldp->xfi_clen) != 0)
5753 if (oldp == startp) {
5754 /* Field not found */
5755 xo_failure(xop, "post-gettext format can't find field "
5756 "'%.*s' in format '%s'",
5757 newp->xfi_clen, newp->xfi_content,
5758 xo_printable(gtfmt));
5766 * Other fields don't have names for us to use, so if
5767 * the types aren't the same, then we'll have to assume
5768 * the original field is a match.
5770 for (oldp = startp; oldp->xfi_ftype; oldp++) {
5771 if (oldp->xfi_ftype == 'V') /* Can't go past these */
5773 if (oldp->xfi_ftype == newp->xfi_ftype)
5774 goto copy_it; /* Assumably we have a match */
5780 * Found a match; copy over appropriate fields
5783 newp->xfi_flags = oldp->xfi_flags;
5784 newp->xfi_fnum = oldp->xfi_fnum;
5785 newp->xfi_format = oldp->xfi_format;
5786 newp->xfi_flen = oldp->xfi_flen;
5787 newp->xfi_encoding = oldp->xfi_encoding;
5788 newp->xfi_elen = oldp->xfi_elen;
5791 *reorderedp = reordered;
5793 xo_gettext_finish_numbering_fields(xop, fmt, new_fields);
5794 xo_gettext_rewrite_fields(xop, new_fields, new_max_fields);
5801 * We don't want to make gettext() calls here with a complete format
5802 * string, since that means changing a flag would mean a
5803 * labor-intensive re-translation expense. Instead we build a
5804 * simplified form with a reduced level of detail, perform a lookup on
5805 * that string and then re-insert the formating info.
5807 * So something like:
5808 * xo_emit("{G:}close {:fd/%ld} returned {g:error/%m} {:test/%6.6s}\n", ...)
5809 * would have a lookup string of:
5810 * "close {:fd} returned {:error} {:test}\n"
5812 * We also need to handling reordering of fields, where the gettext()
5813 * reply string uses fields in a different order than the original
5815 * "cluse-a {:fd} retoorned {:test}. Bork {:error} Bork. Bork.\n"
5816 * If we have to reorder fields within the message, then things get
5817 * complicated. See xo_gettext_rewrite_fields.
5819 * Summary: i18n aighn't cheap.
5822 xo_gettext_build_format (xo_handle_t *xop,
5823 xo_field_info_t *fields, int this_field,
5824 const char *fmt, char **new_fmtp)
5826 if (xo_style_is_encoding(xop))
5832 if (xo_gettext_simplify_format(xop, &xb, fields,
5833 this_field, fmt, NULL))
5836 const char *gtfmt = xo_dgettext(xop, xb.xb_bufp);
5837 if (gtfmt == NULL || gtfmt == fmt || strcmp(gtfmt, fmt) == 0)
5840 xo_buf_cleanup(&xb);
5842 char *new_fmt = xo_strndup(gtfmt, -1);
5843 if (new_fmt == NULL)
5846 *new_fmtp = new_fmt;
5850 xo_buf_cleanup(&xb);
5857 xo_gettext_rebuild_content (xo_handle_t *xop, xo_field_info_t *fields,
5858 ssize_t *fstart, unsigned min_fstart,
5859 ssize_t *fend, unsigned max_fend)
5861 xo_field_info_t *xfip;
5863 ssize_t base = fstart[min_fstart];
5864 ssize_t blen = fend[max_fend] - base;
5865 xo_buffer_t *xbp = &xop->xo_data;
5870 buf = xo_realloc(NULL, blen);
5874 memcpy(buf, xbp->xb_bufp + fstart[min_fstart], blen); /* Copy our data */
5876 unsigned field = min_fstart, len, fnum;
5877 ssize_t soff, doff = base;
5878 xo_field_info_t *zp;
5881 * Be aware there are two competing views of "field number": we
5882 * want the user to thing in terms of "The {1:size}" where {G:},
5883 * newlines, escaped braces, and text don't have numbers. But is
5884 * also the internal view, where we have an array of
5885 * xo_field_info_t and every field have an index. fnum, fstart[]
5886 * and fend[] are the latter, but xfi_renum is the former.
5888 for (xfip = fields + field; xfip->xfi_ftype; xfip++, field++) {
5890 if (xfip->xfi_renum) {
5891 zp = xo_gettext_find_field(fields, xfip->xfi_renum);
5892 fnum = zp ? zp - fields : field;
5895 soff = fstart[fnum];
5896 len = fend[fnum] - soff;
5900 memcpy(xbp->xb_bufp + doff, buf + soff, len);
5907 #else /* HAVE_GETTEXT */
5909 xo_gettext_build_format (xo_handle_t *xop UNUSED,
5910 xo_field_info_t *fields UNUSED,
5911 int this_field UNUSED,
5912 const char *fmt UNUSED, char **new_fmtp)
5919 xo_gettext_combine_formats (xo_handle_t *xop UNUSED, const char *fmt UNUSED,
5920 const char *gtfmt UNUSED,
5921 xo_field_info_t *old_fields UNUSED,
5922 xo_field_info_t *new_fields UNUSED,
5923 unsigned new_max_fields UNUSED,
5924 int *reorderedp UNUSED)
5930 xo_gettext_rebuild_content (xo_handle_t *xop UNUSED,
5931 xo_field_info_t *fields UNUSED,
5932 ssize_t *fstart UNUSED, unsigned min_fstart UNUSED,
5933 ssize_t *fend UNUSED, unsigned max_fend UNUSED)
5937 #endif /* HAVE_GETTEXT */
5940 * Emit a set of fields. This is really the core of libxo.
5943 xo_do_emit_fields (xo_handle_t *xop, xo_field_info_t *fields,
5944 unsigned max_fields, const char *fmt)
5946 int gettext_inuse = 0;
5947 int gettext_changed = 0;
5948 int gettext_reordered = 0;
5950 xo_xff_flags_t flags;
5951 xo_field_info_t *new_fields = NULL;
5952 xo_field_info_t *xfip;
5956 int flush = XOF_ISSET(xop, XOF_FLUSH);
5957 int flush_line = XOF_ISSET(xop, XOF_FLUSH_LINE);
5958 char *new_fmt = NULL;
5960 if (XOIF_ISSET(xop, XOIF_REORDER) || xo_style(xop) == XO_STYLE_ENCODER)
5964 * Some overhead for gettext; if the fields in the msgstr returned
5965 * by gettext are reordered, then we need to record start and end
5966 * for each field. We'll go ahead and render the fields in the
5967 * normal order, but later we can then reconstruct the reordered
5968 * fields using these fstart/fend values.
5970 unsigned flimit = max_fields * 2; /* Pessimistic limit */
5971 unsigned min_fstart = flimit - 1;
5972 unsigned max_fend = 0; /* Highest recorded fend[] entry */
5973 ssize_t fstart[flimit];
5974 bzero(fstart, flimit * sizeof(fstart[0]));
5975 ssize_t fend[flimit];
5976 bzero(fend, flimit * sizeof(fend[0]));
5978 for (xfip = fields, field = 0; xfip->xfi_ftype && field < max_fields;
5980 ftype = xfip->xfi_ftype;
5981 flags = xfip->xfi_flags;
5983 /* Record field start offset */
5984 if (gettext_reordered) {
5985 fstart[field] = xo_buf_offset(&xop->xo_data);
5986 if (min_fstart > field)
5990 const char *content = xfip->xfi_content;
5991 ssize_t clen = xfip->xfi_clen;
5993 if (flags & XFF_ARGUMENT) {
5995 * Argument flag means the content isn't given in the descriptor,
5996 * but as a UTF-8 string ('const char *') argument in xo_vap.
5998 content = va_arg(xop->xo_vap, char *);
5999 clen = content ? strlen(content) : 0;
6002 if (ftype == XO_ROLE_NEWLINE) {
6004 if (flush_line && xo_flush_h(xop) < 0)
6008 } else if (ftype == XO_ROLE_EBRACE) {
6009 xo_format_text(xop, xfip->xfi_start, xfip->xfi_len);
6012 } else if (ftype == XO_ROLE_TEXT) {
6014 xo_format_text(xop, xfip->xfi_content, xfip->xfi_clen);
6019 * Notes and units need the 'w' flag handled before the content.
6021 if (ftype == 'N' || ftype == 'U') {
6022 if (flags & XFF_WS) {
6023 xo_format_content(xop, "padding", NULL, " ", 1,
6025 flags &= ~XFF_WS; /* Block later handling of this */
6030 xo_format_value(xop, content, clen,
6031 xfip->xfi_format, xfip->xfi_flen,
6032 xfip->xfi_encoding, xfip->xfi_elen, flags);
6033 else if (ftype == '[')
6034 xo_anchor_start(xop, xfip, content, clen);
6035 else if (ftype == ']')
6036 xo_anchor_stop(xop, xfip, content, clen);
6037 else if (ftype == 'C')
6038 xo_format_colors(xop, xfip, content, clen);
6040 else if (ftype == 'G') {
6042 * A {G:domain} field; disect the domain name and translate
6043 * the remaining portion of the input string. If the user
6044 * didn't put the {G:} at the start of the format string, then
6045 * assumably they just want us to translate the rest of it.
6046 * Since gettext returns strings in a static buffer, we make
6047 * a copy in new_fmt.
6049 xo_set_gettext_domain(xop, xfip, content, clen);
6051 if (!gettext_inuse) { /* Only translate once */
6058 xo_gettext_build_format(xop, fields, field,
6059 xfip->xfi_next, &new_fmt);
6061 gettext_changed = 1;
6063 unsigned new_max_fields = xo_count_fields(xop, new_fmt);
6065 if (++new_max_fields < max_fields)
6066 new_max_fields = max_fields;
6068 /* Leave a blank slot at the beginning */
6069 ssize_t sz = (new_max_fields + 1) * sizeof(xo_field_info_t);
6070 new_fields = alloca(sz);
6071 bzero(new_fields, sz);
6073 if (!xo_parse_fields(xop, new_fields + 1,
6074 new_max_fields, new_fmt)) {
6075 gettext_reordered = 0;
6077 if (!xo_gettext_combine_formats(xop, fmt, new_fmt,
6078 fields, new_fields + 1,
6079 new_max_fields, &gettext_reordered)) {
6081 if (gettext_reordered) {
6082 if (XOF_ISSET(xop, XOF_LOG_GETTEXT))
6083 xo_failure(xop, "gettext finds reordered "
6084 "fields in '%s' and '%s'",
6086 xo_printable(new_fmt));
6087 flush_line = 0; /* Must keep at content */
6088 XOIF_SET(xop, XOIF_REORDER);
6091 field = -1; /* Will be incremented at top of loop */
6093 max_fields = new_max_fields;
6100 } else if (clen || xfip->xfi_format) {
6102 const char *class_name = xo_class_name(ftype);
6104 xo_format_content(xop, class_name, xo_tag_name(ftype),
6106 xfip->xfi_format, xfip->xfi_flen, flags);
6107 else if (ftype == 'T')
6108 xo_format_title(xop, xfip, content, clen);
6109 else if (ftype == 'U')
6110 xo_format_units(xop, xfip, content, clen);
6112 xo_failure(xop, "unknown field type: '%c'", ftype);
6115 if (flags & XFF_COLON)
6116 xo_format_content(xop, "decoration", NULL, ":", 1, NULL, 0, 0);
6119 xo_format_content(xop, "padding", NULL, " ", 1, NULL, 0, 0);
6122 /* Record the end-of-field offset */
6123 if (gettext_reordered) {
6124 fend[field] = xo_buf_offset(&xop->xo_data);
6129 if (gettext_changed && gettext_reordered) {
6130 /* Final step: rebuild the content using the rendered fields */
6131 xo_gettext_rebuild_content(xop, new_fields + 1, fstart, min_fstart,
6135 XOIF_CLEAR(xop, XOIF_REORDER);
6138 * If we've got enough data, flush it.
6140 if (xo_buf_offset(&xop->xo_data) > XO_BUF_HIGH_WATER)
6143 /* If we don't have an anchor, write the text out */
6144 if (flush && !XOIF_ISSET(xop, XOIF_ANCHOR)) {
6145 if (xo_write(xop) < 0)
6146 rc = -1; /* Report failure */
6147 else if (xo_flush_h(xop) < 0)
6155 * We've carried the gettext domainname inside our handle just for
6156 * convenience, but we need to ensure it doesn't survive across
6159 if (xop->xo_gt_domain) {
6160 xo_free(xop->xo_gt_domain);
6161 xop->xo_gt_domain = NULL;
6164 return (rc < 0) ? rc : xop->xo_columns;
6168 * Parse and emit a set of fields
6171 xo_do_emit (xo_handle_t *xop, xo_emit_flags_t flags, const char *fmt)
6173 xop->xo_columns = 0; /* Always reset it */
6174 xop->xo_errno = errno; /* Save for "%m" */
6179 unsigned max_fields;
6180 xo_field_info_t *fields = NULL;
6182 /* Adjust XOEF_RETAIN based on global flags */
6183 if (XOF_ISSET(xop, XOF_RETAIN_ALL))
6184 flags |= XOEF_RETAIN;
6185 if (XOF_ISSET(xop, XOF_RETAIN_NONE))
6186 flags &= ~XOEF_RETAIN;
6189 * Check for 'retain' flag, telling us to retain the field
6190 * information. If we've already saved it, then we can avoid
6191 * re-parsing the format string.
6193 if (!(flags & XOEF_RETAIN)
6194 || xo_retain_find(fmt, &fields, &max_fields) != 0
6195 || fields == NULL) {
6197 /* Nothing retained; parse the format string */
6198 max_fields = xo_count_fields(xop, fmt);
6199 fields = alloca(max_fields * sizeof(fields[0]));
6200 bzero(fields, max_fields * sizeof(fields[0]));
6202 if (xo_parse_fields(xop, fields, max_fields, fmt))
6203 return -1; /* Warning already displayed */
6205 if (flags & XOEF_RETAIN) {
6206 /* Retain the info */
6207 xo_retain_add(fmt, fields, max_fields);
6211 return xo_do_emit_fields(xop, fields, max_fields, fmt);
6215 * Rebuild a format string in a gettext-friendly format. This function
6216 * is exposed to tools can perform this function. See xo(1).
6219 xo_simplify_format (xo_handle_t *xop, const char *fmt, int with_numbers,
6220 xo_simplify_field_func_t field_cb)
6222 xop = xo_default(xop);
6224 xop->xo_columns = 0; /* Always reset it */
6225 xop->xo_errno = errno; /* Save for "%m" */
6227 unsigned max_fields = xo_count_fields(xop, fmt);
6228 xo_field_info_t fields[max_fields];
6230 bzero(fields, max_fields * sizeof(fields[0]));
6232 if (xo_parse_fields(xop, fields, max_fields, fmt))
6233 return NULL; /* Warning already displayed */
6239 xo_gettext_finish_numbering_fields(xop, fmt, fields);
6241 if (xo_gettext_simplify_format(xop, &xb, fields, -1, fmt, field_cb))
6248 xo_emit_hv (xo_handle_t *xop, const char *fmt, va_list vap)
6252 xop = xo_default(xop);
6253 va_copy(xop->xo_vap, vap);
6254 rc = xo_do_emit(xop, 0, fmt);
6255 va_end(xop->xo_vap);
6256 bzero(&xop->xo_vap, sizeof(xop->xo_vap));
6262 xo_emit_h (xo_handle_t *xop, const char *fmt, ...)
6266 xop = xo_default(xop);
6267 va_start(xop->xo_vap, fmt);
6268 rc = xo_do_emit(xop, 0, fmt);
6269 va_end(xop->xo_vap);
6270 bzero(&xop->xo_vap, sizeof(xop->xo_vap));
6276 xo_emit (const char *fmt, ...)
6278 xo_handle_t *xop = xo_default(NULL);
6281 va_start(xop->xo_vap, fmt);
6282 rc = xo_do_emit(xop, 0, fmt);
6283 va_end(xop->xo_vap);
6284 bzero(&xop->xo_vap, sizeof(xop->xo_vap));
6290 xo_emit_hvf (xo_handle_t *xop, xo_emit_flags_t flags,
6291 const char *fmt, va_list vap)
6295 xop = xo_default(xop);
6296 va_copy(xop->xo_vap, vap);
6297 rc = xo_do_emit(xop, flags, fmt);
6298 va_end(xop->xo_vap);
6299 bzero(&xop->xo_vap, sizeof(xop->xo_vap));
6305 xo_emit_hf (xo_handle_t *xop, xo_emit_flags_t flags, const char *fmt, ...)
6309 xop = xo_default(xop);
6310 va_start(xop->xo_vap, fmt);
6311 rc = xo_do_emit(xop, flags, fmt);
6312 va_end(xop->xo_vap);
6313 bzero(&xop->xo_vap, sizeof(xop->xo_vap));
6319 xo_emit_f (xo_emit_flags_t flags, const char *fmt, ...)
6321 xo_handle_t *xop = xo_default(NULL);
6324 va_start(xop->xo_vap, fmt);
6325 rc = xo_do_emit(xop, flags, fmt);
6326 va_end(xop->xo_vap);
6327 bzero(&xop->xo_vap, sizeof(xop->xo_vap));
6333 * Emit a single field by providing the info information typically provided
6334 * inside the field description (role, modifiers, and formats). This is
6335 * a convenience function to avoid callers using snprintf to build field
6339 xo_emit_field_hv (xo_handle_t *xop, const char *rolmod, const char *contents,
6340 const char *fmt, const char *efmt,
6345 xop = xo_default(xop);
6350 xo_field_info_t xfi;
6352 bzero(&xfi, sizeof(xfi));
6355 cp = xo_parse_roles(xop, rolmod, rolmod, &xfi);
6359 xfi.xfi_start = fmt;
6360 xfi.xfi_content = contents;
6361 xfi.xfi_format = fmt;
6362 xfi.xfi_encoding = efmt;
6363 xfi.xfi_clen = contents ? strlen(contents) : 0;
6364 xfi.xfi_flen = fmt ? strlen(fmt) : 0;
6365 xfi.xfi_elen = efmt ? strlen(efmt) : 0;
6367 /* If we have content, then we have a default format */
6368 if (contents && fmt == NULL
6369 && xo_role_wants_default_format(xfi.xfi_ftype)) {
6370 xfi.xfi_format = xo_default_format;
6374 va_copy(xop->xo_vap, vap);
6376 rc = xo_do_emit_fields(xop, &xfi, 1, fmt ?: contents ?: "field");
6378 va_end(xop->xo_vap);
6384 xo_emit_field_h (xo_handle_t *xop, const char *rolmod, const char *contents,
6385 const char *fmt, const char *efmt, ...)
6390 va_start(vap, efmt);
6391 rc = xo_emit_field_hv(xop, rolmod, contents, fmt, efmt, vap);
6398 xo_emit_field (const char *rolmod, const char *contents,
6399 const char *fmt, const char *efmt, ...)
6404 va_start(vap, efmt);
6405 rc = xo_emit_field_hv(NULL, rolmod, contents, fmt, efmt, vap);
6412 xo_attr_hv (xo_handle_t *xop, const char *name, const char *fmt, va_list vap)
6414 const ssize_t extra = 5; /* space, equals, quote, quote, and nul */
6415 xop = xo_default(xop);
6418 ssize_t nlen = strlen(name);
6419 xo_buffer_t *xbp = &xop->xo_attrs;
6420 ssize_t name_offset, value_offset;
6422 switch (xo_style(xop)) {
6424 if (!xo_buf_has_room(xbp, nlen + extra))
6427 *xbp->xb_curp++ = ' ';
6428 memcpy(xbp->xb_curp, name, nlen);
6429 xbp->xb_curp += nlen;
6430 *xbp->xb_curp++ = '=';
6431 *xbp->xb_curp++ = '"';
6433 rc = xo_vsnprintf(xop, xbp, fmt, vap);
6436 rc = xo_escape_xml(xbp, rc, 1);
6440 if (!xo_buf_has_room(xbp, 2))
6443 *xbp->xb_curp++ = '"';
6444 *xbp->xb_curp = '\0';
6449 case XO_STYLE_ENCODER:
6450 name_offset = xo_buf_offset(xbp);
6451 xo_buf_append(xbp, name, nlen);
6452 xo_buf_append(xbp, "", 1);
6454 value_offset = xo_buf_offset(xbp);
6455 rc = xo_vsnprintf(xop, xbp, fmt, vap);
6458 *xbp->xb_curp = '\0';
6459 rc = xo_encoder_handle(xop, XO_OP_ATTRIBUTE,
6460 xo_buf_data(xbp, name_offset),
6461 xo_buf_data(xbp, value_offset));
6469 xo_attr_h (xo_handle_t *xop, const char *name, const char *fmt, ...)
6475 rc = xo_attr_hv(xop, name, fmt, vap);
6482 xo_attr (const char *name, const char *fmt, ...)
6488 rc = xo_attr_hv(NULL, name, fmt, vap);
6495 xo_stack_set_flags (xo_handle_t *xop)
6497 if (XOF_ISSET(xop, XOF_NOT_FIRST)) {
6498 xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth];
6500 xsp->xs_flags |= XSF_NOT_FIRST;
6501 XOF_CLEAR(xop, XOF_NOT_FIRST);
6506 xo_depth_change (xo_handle_t *xop, const char *name,
6507 int delta, int indent, xo_state_t state, xo_xsf_flags_t flags)
6509 if (xo_style(xop) == XO_STYLE_HTML || xo_style(xop) == XO_STYLE_TEXT)
6512 if (XOF_ISSET(xop, XOF_DTRT))
6515 if (delta >= 0) { /* Push operation */
6516 if (xo_depth_check(xop, xop->xo_depth + delta))
6519 xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth + delta];
6520 xsp->xs_flags = flags;
6521 xsp->xs_state = state;
6522 xo_stack_set_flags(xop);
6525 name = XO_FAILURE_NAME;
6527 xsp->xs_name = xo_strndup(name, -1);
6529 } else { /* Pop operation */
6530 if (xop->xo_depth == 0) {
6531 if (!XOF_ISSET(xop, XOF_IGNORE_CLOSE))
6532 xo_failure(xop, "close with empty stack: '%s'", name);
6536 xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth];
6537 if (XOF_ISSET(xop, XOF_WARN)) {
6538 const char *top = xsp->xs_name;
6539 if (top && strcmp(name, top) != 0) {
6540 xo_failure(xop, "incorrect close: '%s' .vs. '%s'",
6544 if ((xsp->xs_flags & XSF_LIST) != (flags & XSF_LIST)) {
6545 xo_failure(xop, "list close on list confict: '%s'",
6549 if ((xsp->xs_flags & XSF_INSTANCE) != (flags & XSF_INSTANCE)) {
6550 xo_failure(xop, "list close on instance confict: '%s'",
6557 xo_free(xsp->xs_name);
6558 xsp->xs_name = NULL;
6561 xo_free(xsp->xs_keys);
6562 xsp->xs_keys = NULL;
6566 xop->xo_depth += delta; /* Record new depth */
6567 xop->xo_indent += indent;
6571 xo_set_depth (xo_handle_t *xop, int depth)
6573 xop = xo_default(xop);
6575 if (xo_depth_check(xop, depth))
6578 xop->xo_depth += depth;
6579 xop->xo_indent += depth;
6582 static xo_xsf_flags_t
6583 xo_stack_flags (xo_xof_flags_t xflags)
6585 if (xflags & XOF_DTRT)
6591 xo_emit_top (xo_handle_t *xop, const char *ppn)
6593 xo_printf(xop, "%*s{%s", xo_indent(xop), "", ppn);
6594 XOIF_SET(xop, XOIF_TOP_EMITTED);
6596 if (xop->xo_version) {
6597 xo_printf(xop, "%*s\"__version\": \"%s\", %s",
6598 xo_indent(xop), "", xop->xo_version, ppn);
6599 xo_free(xop->xo_version);
6600 xop->xo_version = NULL;
6605 xo_do_open_container (xo_handle_t *xop, xo_xof_flags_t flags, const char *name)
6608 const char *ppn = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : "";
6609 const char *pre_nl = "";
6612 xo_failure(xop, "NULL passed for container name");
6613 name = XO_FAILURE_NAME;
6616 flags |= xop->xo_flags; /* Pick up handle flags */
6618 switch (xo_style(xop)) {
6620 rc = xo_printf(xop, "%*s<%s", xo_indent(xop), "", name);
6622 if (xop->xo_attrs.xb_curp != xop->xo_attrs.xb_bufp) {
6623 rc += xop->xo_attrs.xb_curp - xop->xo_attrs.xb_bufp;
6624 xo_data_append(xop, xop->xo_attrs.xb_bufp,
6625 xop->xo_attrs.xb_curp - xop->xo_attrs.xb_bufp);
6626 xop->xo_attrs.xb_curp = xop->xo_attrs.xb_bufp;
6629 rc += xo_printf(xop, ">%s", ppn);
6633 xo_stack_set_flags(xop);
6635 if (!XOF_ISSET(xop, XOF_NO_TOP)
6636 && !XOIF_ISSET(xop, XOIF_TOP_EMITTED))
6637 xo_emit_top(xop, ppn);
6639 if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST)
6640 pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? ",\n" : ", ";
6641 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST;
6643 rc = xo_printf(xop, "%s%*s\"%s\": {%s",
6644 pre_nl, xo_indent(xop), "", name, ppn);
6647 case XO_STYLE_SDPARAMS:
6650 case XO_STYLE_ENCODER:
6651 rc = xo_encoder_handle(xop, XO_OP_OPEN_CONTAINER, name, NULL);
6655 xo_depth_change(xop, name, 1, 1, XSS_OPEN_CONTAINER,
6656 xo_stack_flags(flags));
6662 xo_open_container_hf (xo_handle_t *xop, xo_xof_flags_t flags, const char *name)
6664 return xo_transition(xop, flags, name, XSS_OPEN_CONTAINER);
6668 xo_open_container_h (xo_handle_t *xop, const char *name)
6670 return xo_open_container_hf(xop, 0, name);
6674 xo_open_container (const char *name)
6676 return xo_open_container_hf(NULL, 0, name);
6680 xo_open_container_hd (xo_handle_t *xop, const char *name)
6682 return xo_open_container_hf(xop, XOF_DTRT, name);
6686 xo_open_container_d (const char *name)
6688 return xo_open_container_hf(NULL, XOF_DTRT, name);
6692 xo_do_close_container (xo_handle_t *xop, const char *name)
6694 xop = xo_default(xop);
6697 const char *ppn = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : "";
6698 const char *pre_nl = "";
6701 xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth];
6703 name = xsp->xs_name;
6705 ssize_t len = strlen(name) + 1;
6706 /* We need to make a local copy; xo_depth_change will free it */
6707 char *cp = alloca(len);
6708 memcpy(cp, name, len);
6710 } else if (!(xsp->xs_flags & XSF_DTRT)) {
6711 xo_failure(xop, "missing name without 'dtrt' mode");
6712 name = XO_FAILURE_NAME;
6716 switch (xo_style(xop)) {
6718 xo_depth_change(xop, name, -1, -1, XSS_CLOSE_CONTAINER, 0);
6719 rc = xo_printf(xop, "%*s</%s>%s", xo_indent(xop), "", name, ppn);
6723 pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : "";
6724 ppn = (xop->xo_depth <= 1) ? "\n" : "";
6726 xo_depth_change(xop, name, -1, -1, XSS_CLOSE_CONTAINER, 0);
6727 rc = xo_printf(xop, "%s%*s}%s", pre_nl, xo_indent(xop), "", ppn);
6728 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST;
6733 xo_depth_change(xop, name, -1, 0, XSS_CLOSE_CONTAINER, 0);
6736 case XO_STYLE_SDPARAMS:
6739 case XO_STYLE_ENCODER:
6740 xo_depth_change(xop, name, -1, 0, XSS_CLOSE_CONTAINER, 0);
6741 rc = xo_encoder_handle(xop, XO_OP_CLOSE_CONTAINER, name, NULL);
6749 xo_close_container_h (xo_handle_t *xop, const char *name)
6751 return xo_transition(xop, 0, name, XSS_CLOSE_CONTAINER);
6755 xo_close_container (const char *name)
6757 return xo_close_container_h(NULL, name);
6761 xo_close_container_hd (xo_handle_t *xop)
6763 return xo_close_container_h(xop, NULL);
6767 xo_close_container_d (void)
6769 return xo_close_container_h(NULL, NULL);
6773 xo_do_open_list (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name)
6778 xop = xo_default(xop);
6780 const char *ppn = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : "";
6781 const char *pre_nl = "";
6783 switch (xo_style(xop)) {
6787 if (!XOF_ISSET(xop, XOF_NO_TOP)
6788 && !XOIF_ISSET(xop, XOIF_TOP_EMITTED))
6789 xo_emit_top(xop, ppn);
6792 xo_failure(xop, "NULL passed for list name");
6793 name = XO_FAILURE_NAME;
6796 xo_stack_set_flags(xop);
6798 if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST)
6799 pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? ",\n" : ", ";
6800 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST;
6802 rc = xo_printf(xop, "%s%*s\"%s\": [%s",
6803 pre_nl, xo_indent(xop), "", name, ppn);
6806 case XO_STYLE_ENCODER:
6807 rc = xo_encoder_handle(xop, XO_OP_OPEN_LIST, name, NULL);
6811 xo_depth_change(xop, name, 1, indent, XSS_OPEN_LIST,
6812 XSF_LIST | xo_stack_flags(flags));
6818 xo_open_list_hf (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name)
6820 return xo_transition(xop, flags, name, XSS_OPEN_LIST);
6824 xo_open_list_h (xo_handle_t *xop, const char *name)
6826 return xo_open_list_hf(xop, 0, name);
6830 xo_open_list (const char *name)
6832 return xo_open_list_hf(NULL, 0, name);
6836 xo_open_list_hd (xo_handle_t *xop, const char *name)
6838 return xo_open_list_hf(xop, XOF_DTRT, name);
6842 xo_open_list_d (const char *name)
6844 return xo_open_list_hf(NULL, XOF_DTRT, name);
6848 xo_do_close_list (xo_handle_t *xop, const char *name)
6851 const char *pre_nl = "";
6854 xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth];
6856 name = xsp->xs_name;
6858 ssize_t len = strlen(name) + 1;
6859 /* We need to make a local copy; xo_depth_change will free it */
6860 char *cp = alloca(len);
6861 memcpy(cp, name, len);
6863 } else if (!(xsp->xs_flags & XSF_DTRT)) {
6864 xo_failure(xop, "missing name without 'dtrt' mode");
6865 name = XO_FAILURE_NAME;
6869 switch (xo_style(xop)) {
6871 if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST)
6872 pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : "";
6873 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST;
6875 xo_depth_change(xop, name, -1, -1, XSS_CLOSE_LIST, XSF_LIST);
6876 rc = xo_printf(xop, "%s%*s]", pre_nl, xo_indent(xop), "");
6877 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST;
6880 case XO_STYLE_ENCODER:
6881 xo_depth_change(xop, name, -1, 0, XSS_CLOSE_LIST, XSF_LIST);
6882 rc = xo_encoder_handle(xop, XO_OP_CLOSE_LIST, name, NULL);
6886 xo_depth_change(xop, name, -1, 0, XSS_CLOSE_LIST, XSF_LIST);
6887 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST;
6895 xo_close_list_h (xo_handle_t *xop, const char *name)
6897 return xo_transition(xop, 0, name, XSS_CLOSE_LIST);
6901 xo_close_list (const char *name)
6903 return xo_close_list_h(NULL, name);
6907 xo_close_list_hd (xo_handle_t *xop)
6909 return xo_close_list_h(xop, NULL);
6913 xo_close_list_d (void)
6915 return xo_close_list_h(NULL, NULL);
6919 xo_do_open_leaf_list (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name)
6924 xop = xo_default(xop);
6926 const char *ppn = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : "";
6927 const char *pre_nl = "";
6929 switch (xo_style(xop)) {
6933 if (!XOF_ISSET(xop, XOF_NO_TOP)) {
6934 if (!XOIF_ISSET(xop, XOIF_TOP_EMITTED)) {
6935 xo_printf(xop, "%*s{%s", xo_indent(xop), "", ppn);
6936 XOIF_SET(xop, XOIF_TOP_EMITTED);
6941 xo_failure(xop, "NULL passed for list name");
6942 name = XO_FAILURE_NAME;
6945 xo_stack_set_flags(xop);
6947 if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST)
6948 pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? ",\n" : ", ";
6949 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST;
6951 rc = xo_printf(xop, "%s%*s\"%s\": [%s",
6952 pre_nl, xo_indent(xop), "", name, ppn);
6955 case XO_STYLE_ENCODER:
6956 rc = xo_encoder_handle(xop, XO_OP_OPEN_LEAF_LIST, name, NULL);
6960 xo_depth_change(xop, name, 1, indent, XSS_OPEN_LEAF_LIST,
6961 XSF_LIST | xo_stack_flags(flags));
6967 xo_do_close_leaf_list (xo_handle_t *xop, const char *name)
6970 const char *pre_nl = "";
6973 xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth];
6975 name = xsp->xs_name;
6977 ssize_t len = strlen(name) + 1;
6978 /* We need to make a local copy; xo_depth_change will free it */
6979 char *cp = alloca(len);
6980 memcpy(cp, name, len);
6982 } else if (!(xsp->xs_flags & XSF_DTRT)) {
6983 xo_failure(xop, "missing name without 'dtrt' mode");
6984 name = XO_FAILURE_NAME;
6988 switch (xo_style(xop)) {
6990 if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST)
6991 pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : "";
6992 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST;
6994 xo_depth_change(xop, name, -1, -1, XSS_CLOSE_LEAF_LIST, XSF_LIST);
6995 rc = xo_printf(xop, "%s%*s]", pre_nl, xo_indent(xop), "");
6996 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST;
6999 case XO_STYLE_ENCODER:
7000 rc = xo_encoder_handle(xop, XO_OP_CLOSE_LEAF_LIST, name, NULL);
7004 xo_depth_change(xop, name, -1, 0, XSS_CLOSE_LEAF_LIST, XSF_LIST);
7005 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST;
7013 xo_do_open_instance (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name)
7015 xop = xo_default(xop);
7018 const char *ppn = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : "";
7019 const char *pre_nl = "";
7021 flags |= xop->xo_flags;
7024 xo_failure(xop, "NULL passed for instance name");
7025 name = XO_FAILURE_NAME;
7028 switch (xo_style(xop)) {
7030 rc = xo_printf(xop, "%*s<%s", xo_indent(xop), "", name);
7032 if (xop->xo_attrs.xb_curp != xop->xo_attrs.xb_bufp) {
7033 rc += xop->xo_attrs.xb_curp - xop->xo_attrs.xb_bufp;
7034 xo_data_append(xop, xop->xo_attrs.xb_bufp,
7035 xop->xo_attrs.xb_curp - xop->xo_attrs.xb_bufp);
7036 xop->xo_attrs.xb_curp = xop->xo_attrs.xb_bufp;
7039 rc += xo_printf(xop, ">%s", ppn);
7043 xo_stack_set_flags(xop);
7045 if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST)
7046 pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? ",\n" : ", ";
7047 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST;
7049 rc = xo_printf(xop, "%s%*s{%s",
7050 pre_nl, xo_indent(xop), "", ppn);
7053 case XO_STYLE_SDPARAMS:
7056 case XO_STYLE_ENCODER:
7057 rc = xo_encoder_handle(xop, XO_OP_OPEN_INSTANCE, name, NULL);
7061 xo_depth_change(xop, name, 1, 1, XSS_OPEN_INSTANCE, xo_stack_flags(flags));
7067 xo_open_instance_hf (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name)
7069 return xo_transition(xop, flags, name, XSS_OPEN_INSTANCE);
7073 xo_open_instance_h (xo_handle_t *xop, const char *name)
7075 return xo_open_instance_hf(xop, 0, name);
7079 xo_open_instance (const char *name)
7081 return xo_open_instance_hf(NULL, 0, name);
7085 xo_open_instance_hd (xo_handle_t *xop, const char *name)
7087 return xo_open_instance_hf(xop, XOF_DTRT, name);
7091 xo_open_instance_d (const char *name)
7093 return xo_open_instance_hf(NULL, XOF_DTRT, name);
7097 xo_do_close_instance (xo_handle_t *xop, const char *name)
7099 xop = xo_default(xop);
7102 const char *ppn = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : "";
7103 const char *pre_nl = "";
7106 xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth];
7108 name = xsp->xs_name;
7110 ssize_t len = strlen(name) + 1;
7111 /* We need to make a local copy; xo_depth_change will free it */
7112 char *cp = alloca(len);
7113 memcpy(cp, name, len);
7115 } else if (!(xsp->xs_flags & XSF_DTRT)) {
7116 xo_failure(xop, "missing name without 'dtrt' mode");
7117 name = XO_FAILURE_NAME;
7121 switch (xo_style(xop)) {
7123 xo_depth_change(xop, name, -1, -1, XSS_CLOSE_INSTANCE, 0);
7124 rc = xo_printf(xop, "%*s</%s>%s", xo_indent(xop), "", name, ppn);
7128 pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : "";
7130 xo_depth_change(xop, name, -1, -1, XSS_CLOSE_INSTANCE, 0);
7131 rc = xo_printf(xop, "%s%*s}", pre_nl, xo_indent(xop), "");
7132 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST;
7137 xo_depth_change(xop, name, -1, 0, XSS_CLOSE_INSTANCE, 0);
7140 case XO_STYLE_SDPARAMS:
7143 case XO_STYLE_ENCODER:
7144 xo_depth_change(xop, name, -1, 0, XSS_CLOSE_INSTANCE, 0);
7145 rc = xo_encoder_handle(xop, XO_OP_CLOSE_INSTANCE, name, NULL);
7153 xo_close_instance_h (xo_handle_t *xop, const char *name)
7155 return xo_transition(xop, 0, name, XSS_CLOSE_INSTANCE);
7159 xo_close_instance (const char *name)
7161 return xo_close_instance_h(NULL, name);
7165 xo_close_instance_hd (xo_handle_t *xop)
7167 return xo_close_instance_h(xop, NULL);
7171 xo_close_instance_d (void)
7173 return xo_close_instance_h(NULL, NULL);
7177 xo_do_close_all (xo_handle_t *xop, xo_stack_t *limit)
7181 xo_xsf_flags_t flags;
7183 for (xsp = &xop->xo_stack[xop->xo_depth]; xsp >= limit; xsp--) {
7184 switch (xsp->xs_state) {
7190 case XSS_OPEN_CONTAINER:
7191 rc = xo_do_close_container(xop, NULL);
7195 rc = xo_do_close_list(xop, NULL);
7198 case XSS_OPEN_INSTANCE:
7199 rc = xo_do_close_instance(xop, NULL);
7202 case XSS_OPEN_LEAF_LIST:
7203 rc = xo_do_close_leaf_list(xop, NULL);
7207 flags = xsp->xs_flags & XSF_MARKER_FLAGS;
7208 xo_depth_change(xop, xsp->xs_name, -1, 0, XSS_MARKER, 0);
7209 xop->xo_stack[xop->xo_depth].xs_flags |= flags;
7215 xo_failure(xop, "close %d failed: %d", xsp->xs_state, rc);
7222 * This function is responsible for clearing out whatever is needed
7223 * to get to the desired state, if possible.
7226 xo_do_close (xo_handle_t *xop, const char *name, xo_state_t new_state)
7228 xo_stack_t *xsp, *limit = NULL;
7230 xo_state_t need_state = new_state;
7232 if (new_state == XSS_CLOSE_CONTAINER)
7233 need_state = XSS_OPEN_CONTAINER;
7234 else if (new_state == XSS_CLOSE_LIST)
7235 need_state = XSS_OPEN_LIST;
7236 else if (new_state == XSS_CLOSE_INSTANCE)
7237 need_state = XSS_OPEN_INSTANCE;
7238 else if (new_state == XSS_CLOSE_LEAF_LIST)
7239 need_state = XSS_OPEN_LEAF_LIST;
7240 else if (new_state == XSS_MARKER)
7241 need_state = XSS_MARKER;
7243 return 0; /* Unknown or useless new states are ignored */
7245 for (xsp = &xop->xo_stack[xop->xo_depth]; xsp > xop->xo_stack; xsp--) {
7247 * Marker's normally stop us from going any further, unless
7248 * we are popping a marker (new_state == XSS_MARKER).
7250 if (xsp->xs_state == XSS_MARKER && need_state != XSS_MARKER) {
7252 xo_failure(xop, "close (xo_%s) fails at marker '%s'; "
7254 xo_state_name(new_state),
7255 xsp->xs_name, name);
7260 xo_failure(xop, "close stops at marker '%s'", xsp->xs_name);
7265 if (xsp->xs_state != need_state)
7268 if (name && xsp->xs_name && strcmp(name, xsp->xs_name) != 0)
7275 if (limit == NULL) {
7276 xo_failure(xop, "xo_%s can't find match for '%s'",
7277 xo_state_name(new_state), name);
7281 rc = xo_do_close_all(xop, limit);
7287 * We are in a given state and need to transition to the new state.
7290 xo_transition (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name,
7291 xo_state_t new_state)
7295 int old_state, on_marker;
7297 xop = xo_default(xop);
7299 xsp = &xop->xo_stack[xop->xo_depth];
7300 old_state = xsp->xs_state;
7301 on_marker = (old_state == XSS_MARKER);
7303 /* If there's a marker on top of the stack, we need to find a real state */
7304 while (old_state == XSS_MARKER) {
7305 if (xsp == xop->xo_stack)
7308 old_state = xsp->xs_state;
7312 * At this point, the list of possible states are:
7313 * XSS_INIT, XSS_OPEN_CONTAINER, XSS_OPEN_LIST,
7314 * XSS_OPEN_INSTANCE, XSS_OPEN_LEAF_LIST, XSS_DISCARDING
7316 switch (XSS_TRANSITION(old_state, new_state)) {
7319 case XSS_TRANSITION(XSS_INIT, XSS_OPEN_CONTAINER):
7320 case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_OPEN_CONTAINER):
7321 case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_OPEN_CONTAINER):
7322 rc = xo_do_open_container(xop, flags, name);
7325 case XSS_TRANSITION(XSS_OPEN_LIST, XSS_OPEN_CONTAINER):
7327 goto marker_prevents_close;
7328 rc = xo_do_close_list(xop, NULL);
7330 goto open_container;
7333 case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_OPEN_CONTAINER):
7335 goto marker_prevents_close;
7336 rc = xo_do_close_leaf_list(xop, NULL);
7338 goto open_container;
7341 /*close_container:*/
7342 case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_CLOSE_CONTAINER):
7344 goto marker_prevents_close;
7345 rc = xo_do_close(xop, name, new_state);
7348 case XSS_TRANSITION(XSS_INIT, XSS_CLOSE_CONTAINER):
7349 /* This is an exception for "xo --close" */
7350 rc = xo_do_close_container(xop, name);
7353 case XSS_TRANSITION(XSS_OPEN_LIST, XSS_CLOSE_CONTAINER):
7354 case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_CLOSE_CONTAINER):
7356 goto marker_prevents_close;
7357 rc = xo_do_close(xop, name, new_state);
7360 case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_CLOSE_CONTAINER):
7362 goto marker_prevents_close;
7363 rc = xo_do_close_leaf_list(xop, NULL);
7365 rc = xo_do_close(xop, name, new_state);
7369 case XSS_TRANSITION(XSS_INIT, XSS_OPEN_LIST):
7370 case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_OPEN_LIST):
7371 case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_OPEN_LIST):
7372 rc = xo_do_open_list(xop, flags, name);
7375 case XSS_TRANSITION(XSS_OPEN_LIST, XSS_OPEN_LIST):
7377 goto marker_prevents_close;
7378 rc = xo_do_close_list(xop, NULL);
7383 case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_OPEN_LIST):
7385 goto marker_prevents_close;
7386 rc = xo_do_close_leaf_list(xop, NULL);
7392 case XSS_TRANSITION(XSS_OPEN_LIST, XSS_CLOSE_LIST):
7394 goto marker_prevents_close;
7395 rc = xo_do_close(xop, name, new_state);
7398 case XSS_TRANSITION(XSS_INIT, XSS_CLOSE_LIST):
7399 case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_CLOSE_LIST):
7400 case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_CLOSE_LIST):
7401 case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_CLOSE_LIST):
7402 rc = xo_do_close(xop, name, new_state);
7406 case XSS_TRANSITION(XSS_OPEN_LIST, XSS_OPEN_INSTANCE):
7407 rc = xo_do_open_instance(xop, flags, name);
7410 case XSS_TRANSITION(XSS_INIT, XSS_OPEN_INSTANCE):
7411 case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_OPEN_INSTANCE):
7412 rc = xo_do_open_list(xop, flags, name);
7417 case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_OPEN_INSTANCE):
7419 rc = xo_do_open_list(xop, flags, name);
7421 rc = xo_do_close_instance(xop, NULL);
7427 case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_OPEN_INSTANCE):
7429 goto marker_prevents_close;
7430 rc = xo_do_close_leaf_list(xop, NULL);
7436 case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_CLOSE_INSTANCE):
7438 goto marker_prevents_close;
7439 rc = xo_do_close_instance(xop, name);
7442 case XSS_TRANSITION(XSS_INIT, XSS_CLOSE_INSTANCE):
7443 /* This one makes no sense; ignore it */
7444 xo_failure(xop, "xo_close_instance ignored when called from "
7445 "initial state ('%s')", name ?: "(unknown)");
7448 case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_CLOSE_INSTANCE):
7449 case XSS_TRANSITION(XSS_OPEN_LIST, XSS_CLOSE_INSTANCE):
7451 goto marker_prevents_close;
7452 rc = xo_do_close(xop, name, new_state);
7455 case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_CLOSE_INSTANCE):
7457 goto marker_prevents_close;
7458 rc = xo_do_close_leaf_list(xop, NULL);
7460 rc = xo_do_close(xop, name, new_state);
7464 case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_OPEN_LEAF_LIST):
7465 case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_OPEN_LEAF_LIST):
7466 case XSS_TRANSITION(XSS_INIT, XSS_OPEN_LEAF_LIST):
7467 rc = xo_do_open_leaf_list(xop, flags, name);
7470 case XSS_TRANSITION(XSS_OPEN_LIST, XSS_OPEN_LEAF_LIST):
7471 case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_OPEN_LEAF_LIST):
7473 goto marker_prevents_close;
7474 rc = xo_do_close_list(xop, NULL);
7476 goto open_leaf_list;
7479 /*close_leaf_list:*/
7480 case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_CLOSE_LEAF_LIST):
7482 goto marker_prevents_close;
7483 rc = xo_do_close_leaf_list(xop, name);
7486 case XSS_TRANSITION(XSS_INIT, XSS_CLOSE_LEAF_LIST):
7487 /* Makes no sense; ignore */
7488 xo_failure(xop, "xo_close_leaf_list ignored when called from "
7489 "initial state ('%s')", name ?: "(unknown)");
7492 case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_CLOSE_LEAF_LIST):
7493 case XSS_TRANSITION(XSS_OPEN_LIST, XSS_CLOSE_LEAF_LIST):
7494 case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_CLOSE_LEAF_LIST):
7496 goto marker_prevents_close;
7497 rc = xo_do_close(xop, name, new_state);
7501 case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_EMIT):
7502 case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_EMIT):
7505 case XSS_TRANSITION(XSS_OPEN_LIST, XSS_EMIT):
7507 goto marker_prevents_close;
7508 rc = xo_do_close(xop, NULL, XSS_CLOSE_LIST);
7511 case XSS_TRANSITION(XSS_INIT, XSS_EMIT):
7514 case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_EMIT):
7516 goto marker_prevents_close;
7517 rc = xo_do_close_leaf_list(xop, NULL);
7521 case XSS_TRANSITION(XSS_INIT, XSS_EMIT_LEAF_LIST):
7522 case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_EMIT_LEAF_LIST):
7523 case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_EMIT_LEAF_LIST):
7524 rc = xo_do_open_leaf_list(xop, flags, name);
7527 case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_EMIT_LEAF_LIST):
7530 case XSS_TRANSITION(XSS_OPEN_LIST, XSS_EMIT_LEAF_LIST):
7532 * We need to be backward compatible with the pre-xo_open_leaf_list
7533 * API, where both lists and leaf-lists were opened as lists. So
7534 * if we find an open list that hasn't had anything written to it,
7540 xo_failure(xop, "unknown transition: (%u -> %u)",
7541 xsp->xs_state, new_state);
7544 /* Handle the flush flag */
7545 if (rc >= 0 && XOF_ISSET(xop, XOF_FLUSH))
7546 if (xo_flush_h(xop))
7551 marker_prevents_close:
7552 xo_failure(xop, "marker '%s' prevents transition from %s to %s",
7553 xop->xo_stack[xop->xo_depth].xs_name,
7554 xo_state_name(old_state), xo_state_name(new_state));
7559 xo_open_marker_h (xo_handle_t *xop, const char *name)
7561 xop = xo_default(xop);
7563 xo_depth_change(xop, name, 1, 0, XSS_MARKER,
7564 xop->xo_stack[xop->xo_depth].xs_flags & XSF_MARKER_FLAGS);
7570 xo_open_marker (const char *name)
7572 return xo_open_marker_h(NULL, name);
7576 xo_close_marker_h (xo_handle_t *xop, const char *name)
7578 xop = xo_default(xop);
7580 return xo_do_close(xop, name, XSS_MARKER);
7584 xo_close_marker (const char *name)
7586 return xo_close_marker_h(NULL, name);
7590 * Record custom output functions into the xo handle, allowing
7591 * integration with a variety of output frameworks.
7594 xo_set_writer (xo_handle_t *xop, void *opaque, xo_write_func_t write_func,
7595 xo_close_func_t close_func, xo_flush_func_t flush_func)
7597 xop = xo_default(xop);
7599 xop->xo_opaque = opaque;
7600 xop->xo_write = write_func;
7601 xop->xo_close = close_func;
7602 xop->xo_flush = flush_func;
7606 xo_set_allocator (xo_realloc_func_t realloc_func, xo_free_func_t free_func)
7608 xo_realloc = realloc_func;
7609 xo_free = free_func;
7613 xo_flush_h (xo_handle_t *xop)
7617 xop = xo_default(xop);
7619 switch (xo_style(xop)) {
7620 case XO_STYLE_ENCODER:
7621 xo_encoder_handle(xop, XO_OP_FLUSH, NULL, NULL);
7625 if (rc >= 0 && xop->xo_flush)
7626 if (xop->xo_flush(xop->xo_opaque) < 0)
7635 return xo_flush_h(NULL);
7639 xo_finish_h (xo_handle_t *xop)
7641 const char *cp = "";
7642 xop = xo_default(xop);
7644 if (!XOF_ISSET(xop, XOF_NO_CLOSE))
7645 xo_do_close_all(xop, xop->xo_stack);
7647 switch (xo_style(xop)) {
7649 if (!XOF_ISSET(xop, XOF_NO_TOP)) {
7650 if (XOIF_ISSET(xop, XOIF_TOP_EMITTED))
7651 XOIF_CLEAR(xop, XOIF_TOP_EMITTED); /* Turn off before output */
7654 xo_printf(xop, "%*s%s}\n",xo_indent(xop), "", cp);
7658 case XO_STYLE_ENCODER:
7659 xo_encoder_handle(xop, XO_OP_FINISH, NULL, NULL);
7663 return xo_flush_h(xop);
7669 return xo_finish_h(NULL);
7673 * xo_finish_atexit is suitable for atexit() calls, to force clear up
7674 * and finalizing output.
7677 xo_finish_atexit (void)
7679 (void) xo_finish_h(NULL);
7683 * Generate an error message, such as would be displayed on stderr
7686 xo_error_hv (xo_handle_t *xop, const char *fmt, va_list vap)
7688 xop = xo_default(xop);
7691 * If the format string doesn't end with a newline, we pop
7694 ssize_t len = strlen(fmt);
7695 if (len > 0 && fmt[len - 1] != '\n') {
7696 char *newfmt = alloca(len + 2);
7697 memcpy(newfmt, fmt, len);
7703 switch (xo_style(xop)) {
7705 vfprintf(stderr, fmt, vap);
7709 va_copy(xop->xo_vap, vap);
7711 xo_buf_append_div(xop, "error", 0, NULL, 0, fmt, strlen(fmt), NULL, 0);
7713 if (XOIF_ISSET(xop, XOIF_DIV_OPEN))
7718 va_end(xop->xo_vap);
7719 bzero(&xop->xo_vap, sizeof(xop->xo_vap));
7724 va_copy(xop->xo_vap, vap);
7726 xo_open_container_h(xop, "error");
7727 xo_format_value(xop, "message", 7, fmt, strlen(fmt), NULL, 0, 0);
7728 xo_close_container_h(xop, "error");
7730 va_end(xop->xo_vap);
7731 bzero(&xop->xo_vap, sizeof(xop->xo_vap));
7734 case XO_STYLE_SDPARAMS:
7735 case XO_STYLE_ENCODER:
7741 xo_error_h (xo_handle_t *xop, const char *fmt, ...)
7746 xo_error_hv(xop, fmt, vap);
7751 * Generate an error message, such as would be displayed on stderr
7754 xo_error (const char *fmt, ...)
7759 xo_error_hv(NULL, fmt, vap);
7764 * Parse any libxo-specific options from the command line, removing them
7765 * so the main() argument parsing won't see them. We return the new value
7766 * for argc or -1 for error. If an error occurred, the program should
7767 * exit. A suitable error message has already been displayed.
7770 xo_parse_args (int argc, char **argv)
7772 static char libxo_opt[] = "--libxo";
7776 /* Save our program name for xo_err and friends */
7777 xo_program = argv[0];
7778 cp = strrchr(xo_program, '/');
7780 xo_program = cp + 1;
7782 for (save = i = 1; i < argc; i++) {
7784 || strncmp(argv[i], libxo_opt, sizeof(libxo_opt) - 1) != 0) {
7786 argv[save] = argv[i];
7791 cp = argv[i] + sizeof(libxo_opt) - 1;
7795 xo_warnx("missing libxo option");
7799 if (xo_set_options(NULL, cp) < 0)
7801 } else if (*cp == ':') {
7802 if (xo_set_options(NULL, cp) < 0)
7805 } else if (*cp == '=') {
7806 if (xo_set_options(NULL, ++cp) < 0)
7809 } else if (*cp == '-') {
7811 if (strcmp(cp, "check") == 0) {
7815 xo_warnx("unknown libxo option: '%s'", argv[i]);
7819 xo_warnx("unknown libxo option: '%s'", argv[i]);
7829 * Debugging function that dumps the current stack of open libxo constructs,
7830 * suitable for calling from the debugger.
7833 xo_dump_stack (xo_handle_t *xop)
7838 xop = xo_default(xop);
7840 fprintf(stderr, "Stack dump:\n");
7842 xsp = xop->xo_stack;
7843 for (i = 1, xsp++; i <= xop->xo_depth; i++, xsp++) {
7844 fprintf(stderr, " [%d] %s '%s' [%x]\n",
7845 i, xo_state_name(xsp->xs_state),
7846 xsp->xs_name ?: "--", xsp->xs_flags);
7851 * Record the program name used for error messages
7854 xo_set_program (const char *name)
7860 xo_set_version_h (xo_handle_t *xop, const char *version)
7862 xop = xo_default(xop);
7864 if (version == NULL || strchr(version, '"') != NULL)
7867 if (!xo_style_is_encoding(xop))
7870 switch (xo_style(xop)) {
7872 /* For XML, we record this as an attribute for the first tag */
7873 xo_attr_h(xop, "version", "%s", version);
7878 * For JSON, we record the version string in our handle, and emit
7879 * it in xo_emit_top.
7881 xop->xo_version = xo_strndup(version, -1);
7884 case XO_STYLE_ENCODER:
7885 xo_encoder_handle(xop, XO_OP_VERSION, NULL, version);
7891 * Set the version number for the API content being carried through
7895 xo_set_version (const char *version)
7897 xo_set_version_h(NULL, version);
7901 * Generate a warning. Normally, this is a text message written to
7902 * standard error. If the XOF_WARN_XML flag is set, then we generate
7903 * XMLified content on standard output.
7906 xo_emit_warn_hcv (xo_handle_t *xop, int as_warning, int code,
7907 const char *fmt, va_list vap)
7909 xop = xo_default(xop);
7914 xo_open_marker_h(xop, "xo_emit_warn_hcv");
7915 xo_open_container_h(xop, as_warning ? "__warning" : "__error");
7918 xo_emit("{wc:program}", xo_program);
7920 if (xo_style(xop) == XO_STYLE_XML || xo_style(xop) == XO_STYLE_JSON) {
7924 bzero(&temp, sizeof(temp));
7925 temp.xo_style = XO_STYLE_TEXT;
7926 xo_buf_init(&temp.xo_data);
7927 xo_depth_check(&temp, XO_DEPTH);
7930 (void) xo_emit_hv(&temp, fmt, ap);
7933 xo_buffer_t *src = &temp.xo_data;
7934 xo_format_value(xop, "message", 7, src->xb_bufp,
7935 src->xb_curp - src->xb_bufp, NULL, 0, 0);
7937 xo_free(temp.xo_stack);
7938 xo_buf_cleanup(src);
7941 (void) xo_emit_hv(xop, fmt, vap);
7943 ssize_t len = strlen(fmt);
7944 if (len > 0 && fmt[len - 1] != '\n') {
7946 const char *msg = strerror(code);
7948 xo_emit_h(xop, ": {G:strerror}{g:error/%s}", msg);
7953 xo_close_marker_h(xop, "xo_emit_warn_hcv");
7958 xo_emit_warn_hc (xo_handle_t *xop, int code, const char *fmt, ...)
7963 xo_emit_warn_hcv(xop, 1, code, fmt, vap);
7968 xo_emit_warn_c (int code, const char *fmt, ...)
7973 xo_emit_warn_hcv(NULL, 1, code, fmt, vap);
7978 xo_emit_warn (const char *fmt, ...)
7984 xo_emit_warn_hcv(NULL, 1, code, fmt, vap);
7989 xo_emit_warnx (const char *fmt, ...)
7994 xo_emit_warn_hcv(NULL, 1, -1, fmt, vap);
7999 xo_emit_err_v (int eval, int code, const char *fmt, va_list vap)
8001 xo_emit_warn_hcv(NULL, 0, code, fmt, vap);
8007 xo_emit_err (int eval, const char *fmt, ...)
8012 xo_emit_err_v(0, code, fmt, vap);
8018 xo_emit_errx (int eval, const char *fmt, ...)
8023 xo_emit_err_v(0, -1, fmt, vap);
8030 xo_emit_errc (int eval, int code, const char *fmt, ...)
8035 xo_emit_warn_hcv(NULL, 0, code, fmt, vap);
8042 * Get the opaque private pointer for an xo handle
8045 xo_get_private (xo_handle_t *xop)
8047 xop = xo_default(xop);
8048 return xop->xo_private;
8052 * Set the opaque private pointer for an xo handle.
8055 xo_set_private (xo_handle_t *xop, void *opaque)
8057 xop = xo_default(xop);
8058 xop->xo_private = opaque;
8062 * Get the encoder function
8065 xo_get_encoder (xo_handle_t *xop)
8067 xop = xo_default(xop);
8068 return xop->xo_encoder;
8072 * Record an encoder callback function in an xo handle.
8075 xo_set_encoder (xo_handle_t *xop, xo_encoder_func_t encoder)
8077 xop = xo_default(xop);
8079 xop->xo_style = XO_STYLE_ENCODER;
8080 xop->xo_encoder = encoder;