libxo.c revision 322172
1/* 2 * Copyright (c) 2014-2015, Juniper Networks, Inc. 3 * All rights reserved. 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 7 * LICENSE. 8 * Phil Shafer, July 2014 9 * 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. 17 * 18 * Full documentation is available in ./doc/libxo.txt or online at: 19 * http://juniper.github.io/libxo/libxo-manual.html 20 * 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. 28 */ 29 30#include <stdio.h> 31#include <stdlib.h> 32#include <stdint.h> 33#include <unistd.h> 34#include <stddef.h> 35#include <wchar.h> 36#include <locale.h> 37#include <sys/types.h> 38#include <stdarg.h> 39#include <string.h> 40#include <errno.h> 41#include <limits.h> 42#include <ctype.h> 43#include <wctype.h> 44#include <getopt.h> 45 46#include "xo_config.h" 47#include "xo.h" 48#include "xo_encoder.h" 49#include "xo_buf.h" 50 51/* 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. 61 * 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. 67 * 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. 72 */ 73#ifdef LIBXO_WCWIDTH 74#include "xo_wcwidth.h" 75#else /* LIBXO_WCWIDTH */ 76#define xo_wcwidth(_x) wcwidth(_x) 77#endif /* LIBXO_WCWIDTH */ 78 79#ifdef HAVE_STDIO_EXT_H 80#include <stdio_ext.h> 81#endif /* HAVE_STDIO_EXT_H */ 82 83/* 84 * humanize_number is a great function, unless you don't have it. So 85 * we carry one in our pocket. 86 */ 87#ifdef HAVE_HUMANIZE_NUMBER 88#include <libutil.h> 89#define xo_humanize_number humanize_number 90#else /* HAVE_HUMANIZE_NUMBER */ 91#include "xo_humanize.h" 92#endif /* HAVE_HUMANIZE_NUMBER */ 93 94#ifdef HAVE_GETTEXT 95#include <libintl.h> 96#endif /* HAVE_GETTEXT */ 97 98/* Rather lame that we can't count on these... */ 99#ifndef FALSE 100#define FALSE 0 101#endif 102#ifndef TRUE 103#define TRUE 1 104#endif 105 106/* 107 * Three styles of specifying thread-local variables are supported. 108 * configure.ac has the brains to run each possibility through the 109 * compiler and see what works; we are left to define the THREAD_LOCAL 110 * macro to the right value. Most toolchains (clang, gcc) use 111 * "before", but some (borland) use "after" and I've heard of some 112 * (ms) that use __declspec. Any others out there? 113 */ 114#define THREAD_LOCAL_before 1 115#define THREAD_LOCAL_after 2 116#define THREAD_LOCAL_declspec 3 117 118#ifndef HAVE_THREAD_LOCAL 119#define THREAD_LOCAL(_x) _x 120#elif HAVE_THREAD_LOCAL == THREAD_LOCAL_before 121#define THREAD_LOCAL(_x) __thread _x 122#elif HAVE_THREAD_LOCAL == THREAD_LOCAL_after 123#define THREAD_LOCAL(_x) _x __thread 124#elif HAVE_THREAD_LOCAL == THREAD_LOCAL_declspec 125#define THREAD_LOCAL(_x) __declspec(_x) 126#else 127#error unknown thread-local setting 128#endif /* HAVE_THREADS_H */ 129 130const char xo_version[] = LIBXO_VERSION; 131const char xo_version_extra[] = LIBXO_VERSION_EXTRA; 132static const char xo_default_format[] = "%s"; 133 134#ifndef UNUSED 135#define UNUSED __attribute__ ((__unused__)) 136#endif /* UNUSED */ 137 138#define XO_INDENT_BY 2 /* Amount to indent when pretty printing */ 139#define XO_DEPTH 128 /* Default stack depth */ 140#define XO_MAX_ANCHOR_WIDTH (8*1024) /* Anything wider is just silly */ 141 142#define XO_FAILURE_NAME "failure" 143 144/* Flags for the stack frame */ 145typedef unsigned xo_xsf_flags_t; /* XSF_* flags */ 146#define XSF_NOT_FIRST (1<<0) /* Not the first element */ 147#define XSF_LIST (1<<1) /* Frame is a list */ 148#define XSF_INSTANCE (1<<2) /* Frame is an instance */ 149#define XSF_DTRT (1<<3) /* Save the name for DTRT mode */ 150 151#define XSF_CONTENT (1<<4) /* Some content has been emitted */ 152#define XSF_EMIT (1<<5) /* Some field has been emitted */ 153#define XSF_EMIT_KEY (1<<6) /* A key has been emitted */ 154#define XSF_EMIT_LEAF_LIST (1<<7) /* A leaf-list field has been emitted */ 155 156/* These are the flags we propagate between markers and their parents */ 157#define XSF_MARKER_FLAGS \ 158 (XSF_NOT_FIRST | XSF_CONTENT | XSF_EMIT | XSF_EMIT_KEY | XSF_EMIT_LEAF_LIST ) 159 160/* 161 * A word about states: We use a finite state machine (FMS) approach 162 * to help remove fragility from the caller's code. Instead of 163 * requiring a specific order of calls, we'll allow the caller more 164 * flexibility and make the library responsible for recovering from 165 * missed steps. The goal is that the library should not be capable 166 * of emitting invalid xml or json, but the developer shouldn't need 167 * to know or understand all the details about these encodings. 168 * 169 * You can think of states as either states or events, since they 170 * function rather like both. None of the XO_CLOSE_* events will 171 * persist as states, since the matching stack frame will be popped. 172 * Same is true of XSS_EMIT, which is an event that asks us to 173 * prep for emitting output fields. 174 */ 175 176/* Stack frame states */ 177typedef unsigned xo_state_t; 178#define XSS_INIT 0 /* Initial stack state */ 179#define XSS_OPEN_CONTAINER 1 180#define XSS_CLOSE_CONTAINER 2 181#define XSS_OPEN_LIST 3 182#define XSS_CLOSE_LIST 4 183#define XSS_OPEN_INSTANCE 5 184#define XSS_CLOSE_INSTANCE 6 185#define XSS_OPEN_LEAF_LIST 7 186#define XSS_CLOSE_LEAF_LIST 8 187#define XSS_DISCARDING 9 /* Discarding data until recovered */ 188#define XSS_MARKER 10 /* xo_open_marker's marker */ 189#define XSS_EMIT 11 /* xo_emit has a leaf field */ 190#define XSS_EMIT_LEAF_LIST 12 /* xo_emit has a leaf-list ({l:}) */ 191#define XSS_FINISH 13 /* xo_finish was called */ 192 193#define XSS_MAX 13 194 195#define XSS_TRANSITION(_old, _new) ((_old) << 8 | (_new)) 196 197/* 198 * xo_stack_t: As we open and close containers and levels, we 199 * create a stack of frames to track them. This is needed for 200 * XOF_WARN and XOF_XPATH. 201 */ 202typedef struct xo_stack_s { 203 xo_xsf_flags_t xs_flags; /* Flags for this frame */ 204 xo_state_t xs_state; /* State for this stack frame */ 205 char *xs_name; /* Name (for XPath value) */ 206 char *xs_keys; /* XPath predicate for any key fields */ 207} xo_stack_t; 208 209/* 210 * libxo supports colors and effects, for those who like them. 211 * XO_COL_* ("colors") refers to fancy ansi codes, while X__EFF_* 212 * ("effects") are bits since we need to maintain state. 213 */ 214typedef uint8_t xo_color_t; 215#define XO_COL_DEFAULT 0 216#define XO_COL_BLACK 1 217#define XO_COL_RED 2 218#define XO_COL_GREEN 3 219#define XO_COL_YELLOW 4 220#define XO_COL_BLUE 5 221#define XO_COL_MAGENTA 6 222#define XO_COL_CYAN 7 223#define XO_COL_WHITE 8 224 225#define XO_NUM_COLORS 9 226 227/* 228 * Yes, there's no blink. We're civilized. We like users. Blink 229 * isn't something one does to someone you like. Friends don't let 230 * friends use blink. On friends. You know what I mean. Blink is 231 * like, well, it's like bursting into show tunes at a funeral. It's 232 * just not done. Not something anyone wants. And on those rare 233 * instances where it might actually be appropriate, it's still wrong, 234 * since it's likely done by the wrong person for the wrong reason. 235 * Just like blink. And if I implemented blink, I'd be like a funeral 236 * director who adds "Would you like us to burst into show tunes?" on 237 * the list of questions asked while making funeral arrangements. 238 * It's formalizing wrongness in the wrong way. And we're just too 239 * civilized to do that. Hhhmph! 240 */ 241#define XO_EFF_RESET (1<<0) 242#define XO_EFF_NORMAL (1<<1) 243#define XO_EFF_BOLD (1<<2) 244#define XO_EFF_UNDERLINE (1<<3) 245#define XO_EFF_INVERSE (1<<4) 246 247#define XO_EFF_CLEAR_BITS XO_EFF_RESET /* Reset gets reset, surprisingly */ 248 249typedef uint8_t xo_effect_t; 250typedef struct xo_colors_s { 251 xo_effect_t xoc_effects; /* Current effect set */ 252 xo_color_t xoc_col_fg; /* Foreground color */ 253 xo_color_t xoc_col_bg; /* Background color */ 254} xo_colors_t; 255 256/* 257 * xo_handle_t: this is the principle data structure for libxo. 258 * It's used as a store for state, options, content, and all manor 259 * of other information. 260 */ 261struct xo_handle_s { 262 xo_xof_flags_t xo_flags; /* Flags (XOF_*) from the user*/ 263 xo_xof_flags_t xo_iflags; /* Internal flags (XOIF_*) */ 264 xo_style_t xo_style; /* XO_STYLE_* value */ 265 unsigned short xo_indent; /* Indent level (if pretty) */ 266 unsigned short xo_indent_by; /* Indent amount (tab stop) */ 267 xo_write_func_t xo_write; /* Write callback */ 268 xo_close_func_t xo_close; /* Close callback */ 269 xo_flush_func_t xo_flush; /* Flush callback */ 270 xo_formatter_t xo_formatter; /* Custom formating function */ 271 xo_checkpointer_t xo_checkpointer; /* Custom formating support function */ 272 void *xo_opaque; /* Opaque data for write function */ 273 xo_buffer_t xo_data; /* Output data */ 274 xo_buffer_t xo_fmt; /* Work area for building format strings */ 275 xo_buffer_t xo_attrs; /* Work area for building XML attributes */ 276 xo_buffer_t xo_predicate; /* Work area for building XPath predicates */ 277 xo_stack_t *xo_stack; /* Stack pointer */ 278 int xo_depth; /* Depth of stack */ 279 int xo_stack_size; /* Size of the stack */ 280 xo_info_t *xo_info; /* Info fields for all elements */ 281 int xo_info_count; /* Number of info entries */ 282 va_list xo_vap; /* Variable arguments (stdargs) */ 283 char *xo_leading_xpath; /* A leading XPath expression */ 284 mbstate_t xo_mbstate; /* Multi-byte character conversion state */ 285 ssize_t xo_anchor_offset; /* Start of anchored text */ 286 ssize_t xo_anchor_columns; /* Number of columns since the start anchor */ 287 ssize_t xo_anchor_min_width; /* Desired width of anchored text */ 288 ssize_t xo_units_offset; /* Start of units insertion point */ 289 ssize_t xo_columns; /* Columns emitted during this xo_emit call */ 290#ifndef LIBXO_TEXT_ONLY 291 uint8_t xo_color_map_fg[XO_NUM_COLORS]; /* Foreground color mappings */ 292 uint8_t xo_color_map_bg[XO_NUM_COLORS]; /* Background color mappings */ 293#endif /* LIBXO_TEXT_ONLY */ 294 xo_colors_t xo_colors; /* Current color and effect values */ 295 xo_buffer_t xo_color_buf; /* HTML: buffer of colors and effects */ 296 char *xo_version; /* Version string */ 297 int xo_errno; /* Saved errno for "%m" */ 298 char *xo_gt_domain; /* Gettext domain, suitable for dgettext(3) */ 299 xo_encoder_func_t xo_encoder; /* Encoding function */ 300 void *xo_private; /* Private data for external encoders */ 301}; 302 303/* Flag operations */ 304#define XOF_BIT_ISSET(_flag, _bit) (((_flag) & (_bit)) ? 1 : 0) 305#define XOF_BIT_SET(_flag, _bit) do { (_flag) |= (_bit); } while (0) 306#define XOF_BIT_CLEAR(_flag, _bit) do { (_flag) &= ~(_bit); } while (0) 307 308#define XOF_ISSET(_xop, _bit) XOF_BIT_ISSET(_xop->xo_flags, _bit) 309#define XOF_SET(_xop, _bit) XOF_BIT_SET(_xop->xo_flags, _bit) 310#define XOF_CLEAR(_xop, _bit) XOF_BIT_CLEAR(_xop->xo_flags, _bit) 311 312#define XOIF_ISSET(_xop, _bit) XOF_BIT_ISSET(_xop->xo_iflags, _bit) 313#define XOIF_SET(_xop, _bit) XOF_BIT_SET(_xop->xo_iflags, _bit) 314#define XOIF_CLEAR(_xop, _bit) XOF_BIT_CLEAR(_xop->xo_iflags, _bit) 315 316/* Internal flags */ 317#define XOIF_REORDER XOF_BIT(0) /* Reordering fields; record field info */ 318#define XOIF_DIV_OPEN XOF_BIT(1) /* A <div> is open */ 319#define XOIF_TOP_EMITTED XOF_BIT(2) /* The top JSON braces have been emitted */ 320#define XOIF_ANCHOR XOF_BIT(3) /* An anchor is in place */ 321 322#define XOIF_UNITS_PENDING XOF_BIT(4) /* We have a units-insertion pending */ 323#define XOIF_INIT_IN_PROGRESS XOF_BIT(5) /* Init of handle is in progress */ 324 325/* Flags for formatting functions */ 326typedef unsigned long xo_xff_flags_t; 327#define XFF_COLON (1<<0) /* Append a ":" */ 328#define XFF_COMMA (1<<1) /* Append a "," iff there's more output */ 329#define XFF_WS (1<<2) /* Append a blank */ 330#define XFF_ENCODE_ONLY (1<<3) /* Only emit for encoding styles (XML, JSON) */ 331 332#define XFF_QUOTE (1<<4) /* Force quotes */ 333#define XFF_NOQUOTE (1<<5) /* Force no quotes */ 334#define XFF_DISPLAY_ONLY (1<<6) /* Only emit for display styles (text, html) */ 335#define XFF_KEY (1<<7) /* Field is a key (for XPath) */ 336 337#define XFF_XML (1<<8) /* Force XML encoding style (for XPath) */ 338#define XFF_ATTR (1<<9) /* Escape value using attribute rules (XML) */ 339#define XFF_BLANK_LINE (1<<10) /* Emit a blank line */ 340#define XFF_NO_OUTPUT (1<<11) /* Do not make any output */ 341 342#define XFF_TRIM_WS (1<<12) /* Trim whitespace off encoded values */ 343#define XFF_LEAF_LIST (1<<13) /* A leaf-list (list of values) */ 344#define XFF_UNESCAPE (1<<14) /* Need to printf-style unescape the value */ 345#define XFF_HUMANIZE (1<<15) /* Humanize the value (for display styles) */ 346 347#define XFF_HN_SPACE (1<<16) /* Humanize: put space before suffix */ 348#define XFF_HN_DECIMAL (1<<17) /* Humanize: add one decimal place if <10 */ 349#define XFF_HN_1000 (1<<18) /* Humanize: use 1000, not 1024 */ 350#define XFF_GT_FIELD (1<<19) /* Call gettext() on a field */ 351 352#define XFF_GT_PLURAL (1<<20) /* Call dngettext to find plural form */ 353#define XFF_ARGUMENT (1<<21) /* Content provided via argument */ 354 355/* Flags to turn off when we don't want i18n processing */ 356#define XFF_GT_FLAGS (XFF_GT_FIELD | XFF_GT_PLURAL) 357 358/* 359 * Normal printf has width and precision, which for strings operate as 360 * min and max number of columns. But this depends on the idea that 361 * one byte means one column, which UTF-8 and multi-byte characters 362 * pitches on its ear. It may take 40 bytes of data to populate 14 363 * columns, but we can't go off looking at 40 bytes of data without the 364 * caller's permission for fear/knowledge that we'll generate core files. 365 * 366 * So we make three values, distinguishing between "max column" and 367 * "number of bytes that we will inspect inspect safely" We call the 368 * later "size", and make the format "%[[<min>].[[<size>].<max>]]s". 369 * 370 * Under the "first do no harm" theory, we default "max" to "size". 371 * This is a reasonable assumption for folks that don't grok the 372 * MBS/WCS/UTF-8 world, and while it will be annoying, it will never 373 * be evil. 374 * 375 * For example, xo_emit("{:tag/%-14.14s}", buf) will make 14 376 * columns of output, but will never look at more than 14 bytes of the 377 * input buffer. This is mostly compatible with printf and caller's 378 * expectations. 379 * 380 * In contrast xo_emit("{:tag/%-14..14s}", buf) will look at however 381 * many bytes (or until a NUL is seen) are needed to fill 14 columns 382 * of output. xo_emit("{:tag/%-14.*.14s}", xx, buf) will look at up 383 * to xx bytes (or until a NUL is seen) in order to fill 14 columns 384 * of output. 385 * 386 * It's fairly amazing how a good idea (handle all languages of the 387 * world) blows such a big hole in the bottom of the fairly weak boat 388 * that is C string handling. The simplicity and completenesss are 389 * sunk in ways we haven't even begun to understand. 390 */ 391#define XF_WIDTH_MIN 0 /* Minimal width */ 392#define XF_WIDTH_SIZE 1 /* Maximum number of bytes to examine */ 393#define XF_WIDTH_MAX 2 /* Maximum width */ 394#define XF_WIDTH_NUM 3 /* Numeric fields in printf (min.size.max) */ 395 396/* Input and output string encodings */ 397#define XF_ENC_WIDE 1 /* Wide characters (wchar_t) */ 398#define XF_ENC_UTF8 2 /* UTF-8 */ 399#define XF_ENC_LOCALE 3 /* Current locale */ 400 401/* 402 * A place to parse printf-style format flags for each field 403 */ 404typedef struct xo_format_s { 405 unsigned char xf_fc; /* Format character */ 406 unsigned char xf_enc; /* Encoding of the string (XF_ENC_*) */ 407 unsigned char xf_skip; /* Skip this field */ 408 unsigned char xf_lflag; /* 'l' (long) */ 409 unsigned char xf_hflag;; /* 'h' (half) */ 410 unsigned char xf_jflag; /* 'j' (intmax_t) */ 411 unsigned char xf_tflag; /* 't' (ptrdiff_t) */ 412 unsigned char xf_zflag; /* 'z' (size_t) */ 413 unsigned char xf_qflag; /* 'q' (quad_t) */ 414 unsigned char xf_seen_minus; /* Seen a minus */ 415 int xf_leading_zero; /* Seen a leading zero (zero fill) */ 416 unsigned xf_dots; /* Seen one or more '.'s */ 417 int xf_width[XF_WIDTH_NUM]; /* Width/precision/size numeric fields */ 418 unsigned xf_stars; /* Seen one or more '*'s */ 419 unsigned char xf_star[XF_WIDTH_NUM]; /* Seen one or more '*'s */ 420} xo_format_t; 421 422/* 423 * This structure represents the parsed field information, suitable for 424 * processing by xo_do_emit and anything else that needs to parse fields. 425 * Note that all pointers point to the main format string. 426 * 427 * XXX This is a first step toward compilable or cachable format 428 * strings. We can also cache the results of dgettext when no format 429 * is used, assuming the 'p' modifier has _not_ been set. 430 */ 431typedef struct xo_field_info_s { 432 xo_xff_flags_t xfi_flags; /* Flags for this field */ 433 unsigned xfi_ftype; /* Field type, as character (e.g. 'V') */ 434 const char *xfi_start; /* Start of field in the format string */ 435 const char *xfi_content; /* Field's content */ 436 const char *xfi_format; /* Field's Format */ 437 const char *xfi_encoding; /* Field's encoding format */ 438 const char *xfi_next; /* Next character in format string */ 439 ssize_t xfi_len; /* Length of field */ 440 ssize_t xfi_clen; /* Content length */ 441 ssize_t xfi_flen; /* Format length */ 442 ssize_t xfi_elen; /* Encoding length */ 443 unsigned xfi_fnum; /* Field number (if used; 0 otherwise) */ 444 unsigned xfi_renum; /* Reordered number (0 == no renumbering) */ 445} xo_field_info_t; 446 447/* 448 * We keep a 'default' handle to allow callers to avoid having to 449 * allocate one. Passing NULL to any of our functions will use 450 * this default handle. Most functions have a variant that doesn't 451 * require a handle at all, since most output is to stdout, which 452 * the default handle handles handily. 453 */ 454static THREAD_LOCAL(xo_handle_t) xo_default_handle; 455static THREAD_LOCAL(int) xo_default_inited; 456static int xo_locale_inited; 457static const char *xo_program; 458 459/* 460 * To allow libxo to be used in diverse environment, we allow the 461 * caller to give callbacks for memory allocation. 462 */ 463xo_realloc_func_t xo_realloc = realloc; 464xo_free_func_t xo_free = free; 465 466/* Forward declarations */ 467static void 468xo_failure (xo_handle_t *xop, const char *fmt, ...); 469 470static ssize_t 471xo_transition (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name, 472 xo_state_t new_state); 473 474static int 475xo_set_options_simple (xo_handle_t *xop, const char *input); 476 477static int 478xo_color_find (const char *str); 479 480static void 481xo_buf_append_div (xo_handle_t *xop, const char *class, xo_xff_flags_t flags, 482 const char *name, ssize_t nlen, 483 const char *value, ssize_t vlen, 484 const char *fmt, ssize_t flen, 485 const char *encoding, ssize_t elen); 486 487static void 488xo_anchor_clear (xo_handle_t *xop); 489 490/* 491 * xo_style is used to retrieve the current style. When we're built 492 * for "text only" mode, we use this function to drive the removal 493 * of most of the code in libxo. We return a constant and the compiler 494 * happily removes the non-text code that is not longer executed. This 495 * trims our code nicely without needing to trampel perfectly readable 496 * code with ifdefs. 497 */ 498static inline xo_style_t 499xo_style (xo_handle_t *xop UNUSED) 500{ 501#ifdef LIBXO_TEXT_ONLY 502 return XO_STYLE_TEXT; 503#else /* LIBXO_TEXT_ONLY */ 504 return xop->xo_style; 505#endif /* LIBXO_TEXT_ONLY */ 506} 507 508/* 509 * Callback to write data to a FILE pointer 510 */ 511static xo_ssize_t 512xo_write_to_file (void *opaque, const char *data) 513{ 514 FILE *fp = (FILE *) opaque; 515 516 return fprintf(fp, "%s", data); 517} 518 519/* 520 * Callback to close a file 521 */ 522static void 523xo_close_file (void *opaque) 524{ 525 FILE *fp = (FILE *) opaque; 526 527 fclose(fp); 528} 529 530/* 531 * Callback to flush a FILE pointer 532 */ 533static int 534xo_flush_file (void *opaque) 535{ 536 FILE *fp = (FILE *) opaque; 537 538 return fflush(fp); 539} 540 541/* 542 * Use a rotating stock of buffers to make a printable string 543 */ 544#define XO_NUMBUFS 8 545#define XO_SMBUFSZ 128 546 547static const char * 548xo_printable (const char *str) 549{ 550 static THREAD_LOCAL(char) bufset[XO_NUMBUFS][XO_SMBUFSZ]; 551 static THREAD_LOCAL(int) bufnum = 0; 552 553 if (str == NULL) 554 return ""; 555 556 if (++bufnum == XO_NUMBUFS) 557 bufnum = 0; 558 559 char *res = bufset[bufnum], *cp, *ep; 560 561 for (cp = res, ep = res + XO_SMBUFSZ - 1; *str && cp < ep; cp++, str++) { 562 if (*str == '\n') { 563 *cp++ = '\\'; 564 *cp = 'n'; 565 } else if (*str == '\r') { 566 *cp++ = '\\'; 567 *cp = 'r'; 568 } else if (*str == '\"') { 569 *cp++ = '\\'; 570 *cp = '"'; 571 } else 572 *cp = *str; 573 } 574 575 *cp = '\0'; 576 return res; 577} 578 579static int 580xo_depth_check (xo_handle_t *xop, int depth) 581{ 582 xo_stack_t *xsp; 583 584 if (depth >= xop->xo_stack_size) { 585 depth += XO_DEPTH; /* Extra room */ 586 587 xsp = xo_realloc(xop->xo_stack, sizeof(xop->xo_stack[0]) * depth); 588 if (xsp == NULL) { 589 xo_failure(xop, "xo_depth_check: out of memory (%d)", depth); 590 return -1; 591 } 592 593 int count = depth - xop->xo_stack_size; 594 595 bzero(xsp + xop->xo_stack_size, count * sizeof(*xsp)); 596 xop->xo_stack_size = depth; 597 xop->xo_stack = xsp; 598 } 599 600 return 0; 601} 602 603void 604xo_no_setlocale (void) 605{ 606 xo_locale_inited = 1; /* Skip initialization */ 607} 608 609/* 610 * We need to decide if stdout is line buffered (_IOLBF). Lacking a 611 * standard way to decide this (e.g. getlinebuf()), we have configure 612 * look to find __flbf, which glibc supported. If not, we'll rely on 613 * isatty, with the assumption that terminals are the only thing 614 * that's line buffered. We _could_ test for "steam._flags & _IOLBF", 615 * which is all __flbf does, but that's even tackier. Like a 616 * bedazzled Elvis outfit on an ugly lap dog sort of tacky. Not 617 * something we're willing to do. 618 */ 619static int 620xo_is_line_buffered (FILE *stream) 621{ 622#if HAVE___FLBF 623 if (__flbf(stream)) 624 return 1; 625#else /* HAVE___FLBF */ 626 if (isatty(fileno(stream))) 627 return 1; 628#endif /* HAVE___FLBF */ 629 return 0; 630} 631 632/* 633 * Initialize an xo_handle_t, using both static defaults and 634 * the global settings from the LIBXO_OPTIONS environment 635 * variable. 636 */ 637static void 638xo_init_handle (xo_handle_t *xop) 639{ 640 xop->xo_opaque = stdout; 641 xop->xo_write = xo_write_to_file; 642 xop->xo_flush = xo_flush_file; 643 644 if (xo_is_line_buffered(stdout)) 645 XOF_SET(xop, XOF_FLUSH_LINE); 646 647 /* 648 * We need to initialize the locale, which isn't really pretty. 649 * Libraries should depend on their caller to set up the 650 * environment. But we really can't count on the caller to do 651 * this, because well, they won't. Trust me. 652 */ 653 if (!xo_locale_inited) { 654 xo_locale_inited = 1; /* Only do this once */ 655 656 const char *cp = getenv("LC_CTYPE"); 657 if (cp == NULL) 658 cp = getenv("LANG"); 659 if (cp == NULL) 660 cp = getenv("LC_ALL"); 661 if (cp == NULL) 662 cp = "C"; /* Default for C programs */ 663 (void) setlocale(LC_CTYPE, cp); 664 } 665 666 /* 667 * Initialize only the xo_buffers we know we'll need; the others 668 * can be allocated as needed. 669 */ 670 xo_buf_init(&xop->xo_data); 671 xo_buf_init(&xop->xo_fmt); 672 673 if (XOIF_ISSET(xop, XOIF_INIT_IN_PROGRESS)) 674 return; 675 XOIF_SET(xop, XOIF_INIT_IN_PROGRESS); 676 677 xop->xo_indent_by = XO_INDENT_BY; 678 xo_depth_check(xop, XO_DEPTH); 679 680 XOIF_CLEAR(xop, XOIF_INIT_IN_PROGRESS); 681} 682 683/* 684 * Initialize the default handle. 685 */ 686static void 687xo_default_init (void) 688{ 689 xo_handle_t *xop = &xo_default_handle; 690 691 xo_init_handle(xop); 692 693#if !defined(NO_LIBXO_OPTIONS) 694 if (!XOF_ISSET(xop, XOF_NO_ENV)) { 695 char *env = getenv("LIBXO_OPTIONS"); 696 697 if (env) 698 xo_set_options_simple(xop, env); 699 700 } 701#endif /* NO_LIBXO_OPTIONS */ 702 703 xo_default_inited = 1; 704} 705 706/* 707 * Cheap convenience function to return either the argument, or 708 * the internal handle, after it has been initialized. The usage 709 * is: 710 * xop = xo_default(xop); 711 */ 712static xo_handle_t * 713xo_default (xo_handle_t *xop) 714{ 715 if (xop == NULL) { 716 if (xo_default_inited == 0) 717 xo_default_init(); 718 xop = &xo_default_handle; 719 } 720 721 return xop; 722} 723 724/* 725 * Return the number of spaces we should be indenting. If 726 * we are pretty-printing, this is indent * indent_by. 727 */ 728static int 729xo_indent (xo_handle_t *xop) 730{ 731 int rc = 0; 732 733 xop = xo_default(xop); 734 735 if (XOF_ISSET(xop, XOF_PRETTY)) { 736 rc = xop->xo_indent * xop->xo_indent_by; 737 if (XOIF_ISSET(xop, XOIF_TOP_EMITTED)) 738 rc += xop->xo_indent_by; 739 } 740 741 return (rc > 0) ? rc : 0; 742} 743 744static void 745xo_buf_indent (xo_handle_t *xop, int indent) 746{ 747 xo_buffer_t *xbp = &xop->xo_data; 748 749 if (indent <= 0) 750 indent = xo_indent(xop); 751 752 if (!xo_buf_has_room(xbp, indent)) 753 return; 754 755 memset(xbp->xb_curp, ' ', indent); 756 xbp->xb_curp += indent; 757} 758 759static char xo_xml_amp[] = "&"; 760static char xo_xml_lt[] = "<"; 761static char xo_xml_gt[] = ">"; 762static char xo_xml_quot[] = """; 763 764static ssize_t 765xo_escape_xml (xo_buffer_t *xbp, ssize_t len, xo_xff_flags_t flags) 766{ 767 ssize_t slen; 768 ssize_t delta = 0; 769 char *cp, *ep, *ip; 770 const char *sp; 771 int attr = XOF_BIT_ISSET(flags, XFF_ATTR); 772 773 for (cp = xbp->xb_curp, ep = cp + len; cp < ep; cp++) { 774 /* We're subtracting 2: 1 for the NUL, 1 for the char we replace */ 775 if (*cp == '<') 776 delta += sizeof(xo_xml_lt) - 2; 777 else if (*cp == '>') 778 delta += sizeof(xo_xml_gt) - 2; 779 else if (*cp == '&') 780 delta += sizeof(xo_xml_amp) - 2; 781 else if (attr && *cp == '"') 782 delta += sizeof(xo_xml_quot) - 2; 783 } 784 785 if (delta == 0) /* Nothing to escape; bail */ 786 return len; 787 788 if (!xo_buf_has_room(xbp, delta)) /* No room; bail, but don't append */ 789 return 0; 790 791 ep = xbp->xb_curp; 792 cp = ep + len; 793 ip = cp + delta; 794 do { 795 cp -= 1; 796 ip -= 1; 797 798 if (*cp == '<') 799 sp = xo_xml_lt; 800 else if (*cp == '>') 801 sp = xo_xml_gt; 802 else if (*cp == '&') 803 sp = xo_xml_amp; 804 else if (attr && *cp == '"') 805 sp = xo_xml_quot; 806 else { 807 *ip = *cp; 808 continue; 809 } 810 811 slen = strlen(sp); 812 ip -= slen - 1; 813 memcpy(ip, sp, slen); 814 815 } while (cp > ep && cp != ip); 816 817 return len + delta; 818} 819 820static ssize_t 821xo_escape_json (xo_buffer_t *xbp, ssize_t len, xo_xff_flags_t flags UNUSED) 822{ 823 ssize_t delta = 0; 824 char *cp, *ep, *ip; 825 826 for (cp = xbp->xb_curp, ep = cp + len; cp < ep; cp++) { 827 if (*cp == '\\' || *cp == '"') 828 delta += 1; 829 else if (*cp == '\n' || *cp == '\r') 830 delta += 1; 831 } 832 833 if (delta == 0) /* Nothing to escape; bail */ 834 return len; 835 836 if (!xo_buf_has_room(xbp, delta)) /* No room; bail, but don't append */ 837 return 0; 838 839 ep = xbp->xb_curp; 840 cp = ep + len; 841 ip = cp + delta; 842 do { 843 cp -= 1; 844 ip -= 1; 845 846 if (*cp == '\\' || *cp == '"') { 847 *ip-- = *cp; 848 *ip = '\\'; 849 } else if (*cp == '\n') { 850 *ip-- = 'n'; 851 *ip = '\\'; 852 } else if (*cp == '\r') { 853 *ip-- = 'r'; 854 *ip = '\\'; 855 } else { 856 *ip = *cp; 857 } 858 859 } while (cp > ep && cp != ip); 860 861 return len + delta; 862} 863 864/* 865 * PARAM-VALUE = UTF-8-STRING ; characters '"', '\' and 866 * ; ']' MUST be escaped. 867 */ 868static ssize_t 869xo_escape_sdparams (xo_buffer_t *xbp, ssize_t len, xo_xff_flags_t flags UNUSED) 870{ 871 ssize_t delta = 0; 872 char *cp, *ep, *ip; 873 874 for (cp = xbp->xb_curp, ep = cp + len; cp < ep; cp++) { 875 if (*cp == '\\' || *cp == '"' || *cp == ']') 876 delta += 1; 877 } 878 879 if (delta == 0) /* Nothing to escape; bail */ 880 return len; 881 882 if (!xo_buf_has_room(xbp, delta)) /* No room; bail, but don't append */ 883 return 0; 884 885 ep = xbp->xb_curp; 886 cp = ep + len; 887 ip = cp + delta; 888 do { 889 cp -= 1; 890 ip -= 1; 891 892 if (*cp == '\\' || *cp == '"' || *cp == ']') { 893 *ip-- = *cp; 894 *ip = '\\'; 895 } else { 896 *ip = *cp; 897 } 898 899 } while (cp > ep && cp != ip); 900 901 return len + delta; 902} 903 904static void 905xo_buf_escape (xo_handle_t *xop, xo_buffer_t *xbp, 906 const char *str, ssize_t len, xo_xff_flags_t flags) 907{ 908 if (!xo_buf_has_room(xbp, len)) 909 return; 910 911 memcpy(xbp->xb_curp, str, len); 912 913 switch (xo_style(xop)) { 914 case XO_STYLE_XML: 915 case XO_STYLE_HTML: 916 len = xo_escape_xml(xbp, len, flags); 917 break; 918 919 case XO_STYLE_JSON: 920 len = xo_escape_json(xbp, len, flags); 921 break; 922 923 case XO_STYLE_SDPARAMS: 924 len = xo_escape_sdparams(xbp, len, flags); 925 break; 926 } 927 928 xbp->xb_curp += len; 929} 930 931/* 932 * Write the current contents of the data buffer using the handle's 933 * xo_write function. 934 */ 935static ssize_t 936xo_write (xo_handle_t *xop) 937{ 938 ssize_t rc = 0; 939 xo_buffer_t *xbp = &xop->xo_data; 940 941 if (xbp->xb_curp != xbp->xb_bufp) { 942 xo_buf_append(xbp, "", 1); /* Append ending NUL */ 943 xo_anchor_clear(xop); 944 if (xop->xo_write) 945 rc = xop->xo_write(xop->xo_opaque, xbp->xb_bufp); 946 xbp->xb_curp = xbp->xb_bufp; 947 } 948 949 /* Turn off the flags that don't survive across writes */ 950 XOIF_CLEAR(xop, XOIF_UNITS_PENDING); 951 952 return rc; 953} 954 955/* 956 * Format arguments into our buffer. If a custom formatter has been set, 957 * we use that to do the work; otherwise we vsnprintf(). 958 */ 959static ssize_t 960xo_vsnprintf (xo_handle_t *xop, xo_buffer_t *xbp, const char *fmt, va_list vap) 961{ 962 va_list va_local; 963 ssize_t rc; 964 ssize_t left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp); 965 966 va_copy(va_local, vap); 967 968 if (xop->xo_formatter) 969 rc = xop->xo_formatter(xop, xbp->xb_curp, left, fmt, va_local); 970 else 971 rc = vsnprintf(xbp->xb_curp, left, fmt, va_local); 972 973 if (rc >= left) { 974 if (!xo_buf_has_room(xbp, rc)) { 975 va_end(va_local); 976 return -1; 977 } 978 979 /* 980 * After we call vsnprintf(), the stage of vap is not defined. 981 * We need to copy it before we pass. Then we have to do our 982 * own logic below to move it along. This is because the 983 * implementation can have va_list be a pointer (bsd) or a 984 * structure (macosx) or anything in between. 985 */ 986 987 va_end(va_local); /* Reset vap to the start */ 988 va_copy(va_local, vap); 989 990 left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp); 991 if (xop->xo_formatter) 992 rc = xop->xo_formatter(xop, xbp->xb_curp, left, fmt, va_local); 993 else 994 rc = vsnprintf(xbp->xb_curp, left, fmt, va_local); 995 } 996 va_end(va_local); 997 998 return rc; 999} 1000 1001/* 1002 * Print some data through the handle. 1003 */ 1004static ssize_t 1005xo_printf_v (xo_handle_t *xop, const char *fmt, va_list vap) 1006{ 1007 xo_buffer_t *xbp = &xop->xo_data; 1008 ssize_t left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp); 1009 ssize_t rc; 1010 va_list va_local; 1011 1012 va_copy(va_local, vap); 1013 1014 rc = vsnprintf(xbp->xb_curp, left, fmt, va_local); 1015 1016 if (rc >= left) { 1017 if (!xo_buf_has_room(xbp, rc)) { 1018 va_end(va_local); 1019 return -1; 1020 } 1021 1022 va_end(va_local); /* Reset vap to the start */ 1023 va_copy(va_local, vap); 1024 1025 left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp); 1026 rc = vsnprintf(xbp->xb_curp, left, fmt, va_local); 1027 } 1028 1029 va_end(va_local); 1030 1031 if (rc > 0) 1032 xbp->xb_curp += rc; 1033 1034 return rc; 1035} 1036 1037static ssize_t 1038xo_printf (xo_handle_t *xop, const char *fmt, ...) 1039{ 1040 ssize_t rc; 1041 va_list vap; 1042 1043 va_start(vap, fmt); 1044 1045 rc = xo_printf_v(xop, fmt, vap); 1046 1047 va_end(vap); 1048 return rc; 1049} 1050 1051/* 1052 * These next few function are make The Essential UTF-8 Ginsu Knife. 1053 * Identify an input and output character, and convert it. 1054 */ 1055static uint8_t xo_utf8_data_bits[5] = { 0, 0x7f, 0x1f, 0x0f, 0x07 }; 1056static uint8_t xo_utf8_len_bits[5] = { 0, 0x00, 0xc0, 0xe0, 0xf0 }; 1057 1058/* 1059 * If the byte has a high-bit set, it's UTF-8, not ASCII. 1060 */ 1061static int 1062xo_is_utf8 (char ch) 1063{ 1064 return (ch & 0x80); 1065} 1066 1067/* 1068 * Look at the high bits of the first byte to determine the length 1069 * of the UTF-8 character. 1070 */ 1071static inline ssize_t 1072xo_utf8_to_wc_len (const char *buf) 1073{ 1074 uint8_t bval = (uint8_t) *buf; 1075 ssize_t len; 1076 1077 if ((bval & 0x80) == 0x0) 1078 len = 1; 1079 else if ((bval & 0xe0) == 0xc0) 1080 len = 2; 1081 else if ((bval & 0xf0) == 0xe0) 1082 len = 3; 1083 else if ((bval & 0xf8) == 0xf0) 1084 len = 4; 1085 else 1086 len = -1; 1087 1088 return len; 1089} 1090 1091static ssize_t 1092xo_buf_utf8_len (xo_handle_t *xop, const char *buf, ssize_t bufsiz) 1093{ 1094 unsigned b = (unsigned char) *buf; 1095 ssize_t len, i; 1096 1097 len = xo_utf8_to_wc_len(buf); 1098 if (len < 0) { 1099 xo_failure(xop, "invalid UTF-8 data: %02hhx", b); 1100 return -1; 1101 } 1102 1103 if (len > bufsiz) { 1104 xo_failure(xop, "invalid UTF-8 data (short): %02hhx (%d/%d)", 1105 b, len, bufsiz); 1106 return -1; 1107 } 1108 1109 for (i = 2; i < len; i++) { 1110 b = (unsigned char ) buf[i]; 1111 if ((b & 0xc0) != 0x80) { 1112 xo_failure(xop, "invalid UTF-8 data (byte %d): %x", i, b); 1113 return -1; 1114 } 1115 } 1116 1117 return len; 1118} 1119 1120/* 1121 * Build a wide character from the input buffer; the number of 1122 * bits we pull off the first character is dependent on the length, 1123 * but we put 6 bits off all other bytes. 1124 */ 1125static inline wchar_t 1126xo_utf8_char (const char *buf, ssize_t len) 1127{ 1128 /* Most common case: singleton byte */ 1129 if (len == 1) 1130 return (unsigned char) buf[0]; 1131 1132 ssize_t i; 1133 wchar_t wc; 1134 const unsigned char *cp = (const unsigned char *) buf; 1135 1136 wc = *cp & xo_utf8_data_bits[len]; 1137 for (i = 1; i < len; i++) { 1138 wc <<= 6; /* Low six bits have data */ 1139 wc |= cp[i] & 0x3f; 1140 if ((cp[i] & 0xc0) != 0x80) 1141 return (wchar_t) -1; 1142 } 1143 1144 return wc; 1145} 1146 1147/* 1148 * Determine the number of bytes needed to encode a wide character. 1149 */ 1150static ssize_t 1151xo_utf8_emit_len (wchar_t wc) 1152{ 1153 ssize_t len; 1154 1155 if ((wc & ((1 << 7) - 1)) == wc) /* Simple case */ 1156 len = 1; 1157 else if ((wc & ((1 << 11) - 1)) == wc) 1158 len = 2; 1159 else if ((wc & ((1 << 16) - 1)) == wc) 1160 len = 3; 1161 else if ((wc & ((1 << 21) - 1)) == wc) 1162 len = 4; 1163 else 1164 len = -1; /* Invalid */ 1165 1166 return len; 1167} 1168 1169/* 1170 * Emit one wide character into the given buffer 1171 */ 1172static void 1173xo_utf8_emit_char (char *buf, ssize_t len, wchar_t wc) 1174{ 1175 ssize_t i; 1176 1177 if (len == 1) { /* Simple case */ 1178 buf[0] = wc & 0x7f; 1179 return; 1180 } 1181 1182 /* Start with the low bits and insert them, six bits at a time */ 1183 for (i = len - 1; i >= 0; i--) { 1184 buf[i] = 0x80 | (wc & 0x3f); 1185 wc >>= 6; /* Drop the low six bits */ 1186 } 1187 1188 /* Finish off the first byte with the length bits */ 1189 buf[0] &= xo_utf8_data_bits[len]; /* Clear out the length bits */ 1190 buf[0] |= xo_utf8_len_bits[len]; /* Drop in new length bits */ 1191} 1192 1193/* 1194 * Append a single UTF-8 character to a buffer, converting it to locale 1195 * encoding. Returns the number of columns consumed by that character, 1196 * as best we can determine it. 1197 */ 1198static ssize_t 1199xo_buf_append_locale_from_utf8 (xo_handle_t *xop, xo_buffer_t *xbp, 1200 const char *ibuf, ssize_t ilen) 1201{ 1202 wchar_t wc; 1203 ssize_t len; 1204 1205 /* 1206 * Build our wide character from the input buffer; the number of 1207 * bits we pull off the first character is dependent on the length, 1208 * but we put 6 bits off all other bytes. 1209 */ 1210 wc = xo_utf8_char(ibuf, ilen); 1211 if (wc == (wchar_t) -1) { 1212 xo_failure(xop, "invalid UTF-8 byte sequence"); 1213 return 0; 1214 } 1215 1216 if (XOF_ISSET(xop, XOF_NO_LOCALE)) { 1217 if (!xo_buf_has_room(xbp, ilen)) 1218 return 0; 1219 1220 memcpy(xbp->xb_curp, ibuf, ilen); 1221 xbp->xb_curp += ilen; 1222 1223 } else { 1224 if (!xo_buf_has_room(xbp, MB_LEN_MAX + 1)) 1225 return 0; 1226 1227 bzero(&xop->xo_mbstate, sizeof(xop->xo_mbstate)); 1228 len = wcrtomb(xbp->xb_curp, wc, &xop->xo_mbstate); 1229 1230 if (len <= 0) { 1231 xo_failure(xop, "could not convert wide char: %lx", 1232 (unsigned long) wc); 1233 return 0; 1234 } 1235 xbp->xb_curp += len; 1236 } 1237 1238 return xo_wcwidth(wc); 1239} 1240 1241/* 1242 * Append a UTF-8 string to a buffer, converting it into locale encoding 1243 */ 1244static void 1245xo_buf_append_locale (xo_handle_t *xop, xo_buffer_t *xbp, 1246 const char *cp, ssize_t len) 1247{ 1248 const char *sp = cp, *ep = cp + len; 1249 ssize_t save_off = xbp->xb_bufp - xbp->xb_curp; 1250 ssize_t slen; 1251 int cols = 0; 1252 1253 for ( ; cp < ep; cp++) { 1254 if (!xo_is_utf8(*cp)) { 1255 cols += 1; 1256 continue; 1257 } 1258 1259 /* 1260 * We're looking at a non-ascii UTF-8 character. 1261 * First we copy the previous data. 1262 * Then we need find the length and validate it. 1263 * Then we turn it into a wide string. 1264 * Then we turn it into a localized string. 1265 * Then we repeat. Isn't i18n fun? 1266 */ 1267 if (sp != cp) 1268 xo_buf_append(xbp, sp, cp - sp); /* Append previous data */ 1269 1270 slen = xo_buf_utf8_len(xop, cp, ep - cp); 1271 if (slen <= 0) { 1272 /* Bad data; back it all out */ 1273 xbp->xb_curp = xbp->xb_bufp + save_off; 1274 return; 1275 } 1276 1277 cols += xo_buf_append_locale_from_utf8(xop, xbp, cp, slen); 1278 1279 /* Next time through, we'll start at the next character */ 1280 cp += slen - 1; 1281 sp = cp + 1; 1282 } 1283 1284 /* Update column values */ 1285 if (XOF_ISSET(xop, XOF_COLUMNS)) 1286 xop->xo_columns += cols; 1287 if (XOIF_ISSET(xop, XOIF_ANCHOR)) 1288 xop->xo_anchor_columns += cols; 1289 1290 /* Before we fall into the basic logic below, we need reset len */ 1291 len = ep - sp; 1292 if (len != 0) /* Append trailing data */ 1293 xo_buf_append(xbp, sp, len); 1294} 1295 1296/* 1297 * Append the given string to the given buffer, without escaping or 1298 * character set conversion. This is the straight copy to the data 1299 * buffer with no fanciness. 1300 */ 1301static void 1302xo_data_append (xo_handle_t *xop, const char *str, ssize_t len) 1303{ 1304 xo_buf_append(&xop->xo_data, str, len); 1305} 1306 1307/* 1308 * Append the given string to the given buffer 1309 */ 1310static void 1311xo_data_escape (xo_handle_t *xop, const char *str, ssize_t len) 1312{ 1313 xo_buf_escape(xop, &xop->xo_data, str, len, 0); 1314} 1315 1316#ifdef LIBXO_NO_RETAIN 1317/* 1318 * Empty implementations of the retain logic 1319 */ 1320 1321void 1322xo_retain_clear_all (void) 1323{ 1324 return; 1325} 1326 1327void 1328xo_retain_clear (const char *fmt UNUSED) 1329{ 1330 return; 1331} 1332static void 1333xo_retain_add (const char *fmt UNUSED, xo_field_info_t *fields UNUSED, 1334 unsigned num_fields UNUSED) 1335{ 1336 return; 1337} 1338 1339static int 1340xo_retain_find (const char *fmt UNUSED, xo_field_info_t **valp UNUSED, 1341 unsigned *nump UNUSED) 1342{ 1343 return -1; 1344} 1345 1346#else /* !LIBXO_NO_RETAIN */ 1347/* 1348 * Retain: We retain parsed field definitions to enhance performance, 1349 * especially inside loops. We depend on the caller treating the format 1350 * strings as immutable, so that we can retain pointers into them. We 1351 * hold the pointers in a hash table, so allow quick access. Retained 1352 * information is retained until xo_retain_clear is called. 1353 */ 1354 1355/* 1356 * xo_retain_entry_t holds information about one retained set of 1357 * parsed fields. 1358 */ 1359typedef struct xo_retain_entry_s { 1360 struct xo_retain_entry_s *xre_next; /* Pointer to next (older) entry */ 1361 unsigned long xre_hits; /* Number of times we've hit */ 1362 const char *xre_format; /* Pointer to format string */ 1363 unsigned xre_num_fields; /* Number of fields saved */ 1364 xo_field_info_t *xre_fields; /* Pointer to fields */ 1365} xo_retain_entry_t; 1366 1367/* 1368 * xo_retain_t holds a complete set of parsed fields as a hash table. 1369 */ 1370#ifndef XO_RETAIN_SIZE 1371#define XO_RETAIN_SIZE 6 1372#endif /* XO_RETAIN_SIZE */ 1373#define RETAIN_HASH_SIZE (1<<XO_RETAIN_SIZE) 1374 1375typedef struct xo_retain_s { 1376 xo_retain_entry_t *xr_bucket[RETAIN_HASH_SIZE]; 1377} xo_retain_t; 1378 1379static THREAD_LOCAL(xo_retain_t) xo_retain; 1380static THREAD_LOCAL(unsigned) xo_retain_count; 1381 1382/* 1383 * Simple hash function based on Thomas Wang's paper. The original is 1384 * gone, but an archive is available on the Way Back Machine: 1385 * 1386 * http://web.archive.org/web/20071223173210/\ 1387 * http://www.concentric.net/~Ttwang/tech/inthash.htm 1388 * 1389 * For our purposes, we can assume the low four bits are uninteresting 1390 * since any string less that 16 bytes wouldn't be worthy of 1391 * retaining. We toss the high bits also, since these bits are likely 1392 * to be common among constant format strings. We then run Wang's 1393 * algorithm, and cap the result at RETAIN_HASH_SIZE. 1394 */ 1395static unsigned 1396xo_retain_hash (const char *fmt) 1397{ 1398 volatile uintptr_t iptr = (uintptr_t) (const void *) fmt; 1399 1400 /* Discard low four bits and high bits; they aren't interesting */ 1401 uint32_t val = (uint32_t) ((iptr >> 4) & (((1 << 24) - 1))); 1402 1403 val = (val ^ 61) ^ (val >> 16); 1404 val = val + (val << 3); 1405 val = val ^ (val >> 4); 1406 val = val * 0x3a8f05c5; /* My large prime number */ 1407 val = val ^ (val >> 15); 1408 val &= RETAIN_HASH_SIZE - 1; 1409 1410 return val; 1411} 1412 1413/* 1414 * Walk all buckets, clearing all retained entries 1415 */ 1416void 1417xo_retain_clear_all (void) 1418{ 1419 int i; 1420 xo_retain_entry_t *xrep, *next; 1421 1422 for (i = 0; i < RETAIN_HASH_SIZE; i++) { 1423 for (xrep = xo_retain.xr_bucket[i]; xrep; xrep = next) { 1424 next = xrep->xre_next; 1425 xo_free(xrep); 1426 } 1427 xo_retain.xr_bucket[i] = NULL; 1428 } 1429 xo_retain_count = 0; 1430} 1431 1432/* 1433 * Walk all buckets, clearing all retained entries 1434 */ 1435void 1436xo_retain_clear (const char *fmt) 1437{ 1438 xo_retain_entry_t **xrepp; 1439 unsigned hash = xo_retain_hash(fmt); 1440 1441 for (xrepp = &xo_retain.xr_bucket[hash]; *xrepp; 1442 xrepp = &(*xrepp)->xre_next) { 1443 if ((*xrepp)->xre_format == fmt) { 1444 *xrepp = (*xrepp)->xre_next; 1445 xo_retain_count -= 1; 1446 return; 1447 } 1448 } 1449} 1450 1451/* 1452 * Search the hash for an entry matching 'fmt'; return it's fields. 1453 */ 1454static int 1455xo_retain_find (const char *fmt, xo_field_info_t **valp, unsigned *nump) 1456{ 1457 if (xo_retain_count == 0) 1458 return -1; 1459 1460 unsigned hash = xo_retain_hash(fmt); 1461 xo_retain_entry_t *xrep; 1462 1463 for (xrep = xo_retain.xr_bucket[hash]; xrep != NULL; 1464 xrep = xrep->xre_next) { 1465 if (xrep->xre_format == fmt) { 1466 *valp = xrep->xre_fields; 1467 *nump = xrep->xre_num_fields; 1468 xrep->xre_hits += 1; 1469 return 0; 1470 } 1471 } 1472 1473 return -1; 1474} 1475 1476static void 1477xo_retain_add (const char *fmt, xo_field_info_t *fields, unsigned num_fields) 1478{ 1479 unsigned hash = xo_retain_hash(fmt); 1480 xo_retain_entry_t *xrep; 1481 ssize_t sz = sizeof(*xrep) + (num_fields + 1) * sizeof(*fields); 1482 xo_field_info_t *xfip; 1483 1484 xrep = xo_realloc(NULL, sz); 1485 if (xrep == NULL) 1486 return; 1487 1488 xfip = (xo_field_info_t *) &xrep[1]; 1489 memcpy(xfip, fields, num_fields * sizeof(*fields)); 1490 1491 bzero(xrep, sizeof(*xrep)); 1492 1493 xrep->xre_format = fmt; 1494 xrep->xre_fields = xfip; 1495 xrep->xre_num_fields = num_fields; 1496 1497 /* Record the field info in the retain bucket */ 1498 xrep->xre_next = xo_retain.xr_bucket[hash]; 1499 xo_retain.xr_bucket[hash] = xrep; 1500 xo_retain_count += 1; 1501} 1502 1503#endif /* !LIBXO_NO_RETAIN */ 1504 1505/* 1506 * Generate a warning. Normally, this is a text message written to 1507 * standard error. If the XOF_WARN_XML flag is set, then we generate 1508 * XMLified content on standard output. 1509 */ 1510static void 1511xo_warn_hcv (xo_handle_t *xop, int code, int check_warn, 1512 const char *fmt, va_list vap) 1513{ 1514 xop = xo_default(xop); 1515 if (check_warn && !XOF_ISSET(xop, XOF_WARN)) 1516 return; 1517 1518 if (fmt == NULL) 1519 return; 1520 1521 ssize_t len = strlen(fmt); 1522 ssize_t plen = xo_program ? strlen(xo_program) : 0; 1523 char *newfmt = alloca(len + 1 + plen + 2); /* NUL, and ": " */ 1524 1525 if (plen) { 1526 memcpy(newfmt, xo_program, plen); 1527 newfmt[plen++] = ':'; 1528 newfmt[plen++] = ' '; 1529 } 1530 1531 memcpy(newfmt + plen, fmt, len); 1532 newfmt[len + plen] = '\0'; 1533 1534 if (XOF_ISSET(xop, XOF_WARN_XML)) { 1535 static char err_open[] = "<error>"; 1536 static char err_close[] = "</error>"; 1537 static char msg_open[] = "<message>"; 1538 static char msg_close[] = "</message>"; 1539 1540 xo_buffer_t *xbp = &xop->xo_data; 1541 1542 xo_buf_append(xbp, err_open, sizeof(err_open) - 1); 1543 xo_buf_append(xbp, msg_open, sizeof(msg_open) - 1); 1544 1545 va_list va_local; 1546 va_copy(va_local, vap); 1547 1548 ssize_t left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp); 1549 ssize_t rc = vsnprintf(xbp->xb_curp, left, newfmt, vap); 1550 1551 if (rc >= left) { 1552 if (!xo_buf_has_room(xbp, rc)) { 1553 va_end(va_local); 1554 return; 1555 } 1556 1557 va_end(vap); /* Reset vap to the start */ 1558 va_copy(vap, va_local); 1559 1560 left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp); 1561 rc = vsnprintf(xbp->xb_curp, left, fmt, vap); 1562 } 1563 1564 va_end(va_local); 1565 1566 rc = xo_escape_xml(xbp, rc, 1); 1567 xbp->xb_curp += rc; 1568 1569 xo_buf_append(xbp, msg_close, sizeof(msg_close) - 1); 1570 xo_buf_append(xbp, err_close, sizeof(err_close) - 1); 1571 1572 if (code >= 0) { 1573 const char *msg = strerror(code); 1574 1575 if (msg) { 1576 xo_buf_append(xbp, ": ", 2); 1577 xo_buf_append(xbp, msg, strlen(msg)); 1578 } 1579 } 1580 1581 xo_buf_append(xbp, "\n", 1); /* Append newline and NUL to string */ 1582 (void) xo_write(xop); 1583 1584 } else { 1585 vfprintf(stderr, newfmt, vap); 1586 if (code >= 0) { 1587 const char *msg = strerror(code); 1588 1589 if (msg) 1590 fprintf(stderr, ": %s", msg); 1591 } 1592 fprintf(stderr, "\n"); 1593 } 1594} 1595 1596void 1597xo_warn_hc (xo_handle_t *xop, int code, const char *fmt, ...) 1598{ 1599 va_list vap; 1600 1601 va_start(vap, fmt); 1602 xo_warn_hcv(xop, code, 0, fmt, vap); 1603 va_end(vap); 1604} 1605 1606void 1607xo_warn_c (int code, const char *fmt, ...) 1608{ 1609 va_list vap; 1610 1611 va_start(vap, fmt); 1612 xo_warn_hcv(NULL, code, 0, fmt, vap); 1613 va_end(vap); 1614} 1615 1616void 1617xo_warn (const char *fmt, ...) 1618{ 1619 int code = errno; 1620 va_list vap; 1621 1622 va_start(vap, fmt); 1623 xo_warn_hcv(NULL, code, 0, fmt, vap); 1624 va_end(vap); 1625} 1626 1627void 1628xo_warnx (const char *fmt, ...) 1629{ 1630 va_list vap; 1631 1632 va_start(vap, fmt); 1633 xo_warn_hcv(NULL, -1, 0, fmt, vap); 1634 va_end(vap); 1635} 1636 1637void 1638xo_err (int eval, const char *fmt, ...) 1639{ 1640 int code = errno; 1641 va_list vap; 1642 1643 va_start(vap, fmt); 1644 xo_warn_hcv(NULL, code, 0, fmt, vap); 1645 va_end(vap); 1646 xo_finish(); 1647 exit(eval); 1648} 1649 1650void 1651xo_errx (int eval, const char *fmt, ...) 1652{ 1653 va_list vap; 1654 1655 va_start(vap, fmt); 1656 xo_warn_hcv(NULL, -1, 0, fmt, vap); 1657 va_end(vap); 1658 xo_finish(); 1659 exit(eval); 1660} 1661 1662void 1663xo_errc (int eval, int code, const char *fmt, ...) 1664{ 1665 va_list vap; 1666 1667 va_start(vap, fmt); 1668 xo_warn_hcv(NULL, code, 0, fmt, vap); 1669 va_end(vap); 1670 xo_finish(); 1671 exit(eval); 1672} 1673 1674/* 1675 * Generate a warning. Normally, this is a text message written to 1676 * standard error. If the XOF_WARN_XML flag is set, then we generate 1677 * XMLified content on standard output. 1678 */ 1679void 1680xo_message_hcv (xo_handle_t *xop, int code, const char *fmt, va_list vap) 1681{ 1682 static char msg_open[] = "<message>"; 1683 static char msg_close[] = "</message>"; 1684 xo_buffer_t *xbp; 1685 ssize_t rc; 1686 va_list va_local; 1687 1688 xop = xo_default(xop); 1689 1690 if (fmt == NULL || *fmt == '\0') 1691 return; 1692 1693 int need_nl = (fmt[strlen(fmt) - 1] != '\n'); 1694 1695 switch (xo_style(xop)) { 1696 case XO_STYLE_XML: 1697 xbp = &xop->xo_data; 1698 if (XOF_ISSET(xop, XOF_PRETTY)) 1699 xo_buf_indent(xop, xop->xo_indent_by); 1700 xo_buf_append(xbp, msg_open, sizeof(msg_open) - 1); 1701 1702 va_copy(va_local, vap); 1703 1704 ssize_t left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp); 1705 1706 rc = vsnprintf(xbp->xb_curp, left, fmt, vap); 1707 if (rc >= left) { 1708 if (!xo_buf_has_room(xbp, rc)) { 1709 va_end(va_local); 1710 return; 1711 } 1712 1713 va_end(vap); /* Reset vap to the start */ 1714 va_copy(vap, va_local); 1715 1716 left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp); 1717 rc = vsnprintf(xbp->xb_curp, left, fmt, vap); 1718 } 1719 1720 va_end(va_local); 1721 1722 rc = xo_escape_xml(xbp, rc, 0); 1723 xbp->xb_curp += rc; 1724 1725 if (need_nl && code > 0) { 1726 const char *msg = strerror(code); 1727 1728 if (msg) { 1729 xo_buf_append(xbp, ": ", 2); 1730 xo_buf_append(xbp, msg, strlen(msg)); 1731 } 1732 } 1733 1734 if (need_nl) 1735 xo_buf_append(xbp, "\n", 1); /* Append newline and NUL to string */ 1736 1737 xo_buf_append(xbp, msg_close, sizeof(msg_close) - 1); 1738 1739 if (XOF_ISSET(xop, XOF_PRETTY)) 1740 xo_buf_append(xbp, "\n", 1); /* Append newline and NUL to string */ 1741 1742 (void) xo_write(xop); 1743 break; 1744 1745 case XO_STYLE_HTML: 1746 { 1747 char buf[BUFSIZ], *bp = buf, *cp; 1748 ssize_t bufsiz = sizeof(buf); 1749 ssize_t rc2; 1750 1751 va_copy(va_local, vap); 1752 1753 rc = vsnprintf(bp, bufsiz, fmt, va_local); 1754 if (rc > bufsiz) { 1755 bufsiz = rc + BUFSIZ; 1756 bp = alloca(bufsiz); 1757 va_end(va_local); 1758 va_copy(va_local, vap); 1759 rc = vsnprintf(bp, bufsiz, fmt, va_local); 1760 } 1761 1762 va_end(va_local); 1763 cp = bp + rc; 1764 1765 if (need_nl) { 1766 rc2 = snprintf(cp, bufsiz - rc, "%s%s\n", 1767 (code > 0) ? ": " : "", 1768 (code > 0) ? strerror(code) : ""); 1769 if (rc2 > 0) 1770 rc += rc2; 1771 } 1772 1773 xo_buf_append_div(xop, "message", 0, NULL, 0, bp, rc, 1774 NULL, 0, NULL, 0); 1775 } 1776 break; 1777 1778 case XO_STYLE_JSON: 1779 case XO_STYLE_SDPARAMS: 1780 case XO_STYLE_ENCODER: 1781 /* No means of representing messages */ 1782 return; 1783 1784 case XO_STYLE_TEXT: 1785 rc = xo_printf_v(xop, fmt, vap); 1786 /* 1787 * XXX need to handle UTF-8 widths 1788 */ 1789 if (rc > 0) { 1790 if (XOF_ISSET(xop, XOF_COLUMNS)) 1791 xop->xo_columns += rc; 1792 if (XOIF_ISSET(xop, XOIF_ANCHOR)) 1793 xop->xo_anchor_columns += rc; 1794 } 1795 1796 if (need_nl && code > 0) { 1797 const char *msg = strerror(code); 1798 1799 if (msg) { 1800 xo_printf(xop, ": %s", msg); 1801 } 1802 } 1803 if (need_nl) 1804 xo_printf(xop, "\n"); 1805 1806 break; 1807 } 1808 1809 switch (xo_style(xop)) { 1810 case XO_STYLE_HTML: 1811 if (XOIF_ISSET(xop, XOIF_DIV_OPEN)) { 1812 static char div_close[] = "</div>"; 1813 1814 XOIF_CLEAR(xop, XOIF_DIV_OPEN); 1815 xo_data_append(xop, div_close, sizeof(div_close) - 1); 1816 1817 if (XOF_ISSET(xop, XOF_PRETTY)) 1818 xo_data_append(xop, "\n", 1); 1819 } 1820 break; 1821 } 1822 1823 (void) xo_flush_h(xop); 1824} 1825 1826void 1827xo_message_hc (xo_handle_t *xop, int code, const char *fmt, ...) 1828{ 1829 va_list vap; 1830 1831 va_start(vap, fmt); 1832 xo_message_hcv(xop, code, fmt, vap); 1833 va_end(vap); 1834} 1835 1836void 1837xo_message_c (int code, const char *fmt, ...) 1838{ 1839 va_list vap; 1840 1841 va_start(vap, fmt); 1842 xo_message_hcv(NULL, code, fmt, vap); 1843 va_end(vap); 1844} 1845 1846void 1847xo_message_e (const char *fmt, ...) 1848{ 1849 int code = errno; 1850 va_list vap; 1851 1852 va_start(vap, fmt); 1853 xo_message_hcv(NULL, code, fmt, vap); 1854 va_end(vap); 1855} 1856 1857void 1858xo_message (const char *fmt, ...) 1859{ 1860 va_list vap; 1861 1862 va_start(vap, fmt); 1863 xo_message_hcv(NULL, 0, fmt, vap); 1864 va_end(vap); 1865} 1866 1867static void 1868xo_failure (xo_handle_t *xop, const char *fmt, ...) 1869{ 1870 if (!XOF_ISSET(xop, XOF_WARN)) 1871 return; 1872 1873 va_list vap; 1874 1875 va_start(vap, fmt); 1876 xo_warn_hcv(xop, -1, 1, fmt, vap); 1877 va_end(vap); 1878} 1879 1880/** 1881 * Create a handle for use by later libxo functions. 1882 * 1883 * Note: normal use of libxo does not require a distinct handle, since 1884 * the default handle (used when NULL is passed) generates text on stdout. 1885 * 1886 * @param style Style of output desired (XO_STYLE_* value) 1887 * @param flags Set of XOF_* flags in use with this handle 1888 * @return Newly allocated handle 1889 * @see xo_destroy 1890 */ 1891xo_handle_t * 1892xo_create (xo_style_t style, xo_xof_flags_t flags) 1893{ 1894 xo_handle_t *xop = xo_realloc(NULL, sizeof(*xop)); 1895 1896 if (xop) { 1897 bzero(xop, sizeof(*xop)); 1898 1899 xop->xo_style = style; 1900 XOF_SET(xop, flags); 1901 xo_init_handle(xop); 1902 xop->xo_style = style; /* Reset style (see LIBXO_OPTIONS) */ 1903 } 1904 1905 return xop; 1906} 1907 1908/** 1909 * Create a handle that will write to the given file. Use 1910 * the XOF_CLOSE_FP flag to have the file closed on xo_destroy(). 1911 * 1912 * @param fp FILE pointer to use 1913 * @param style Style of output desired (XO_STYLE_* value) 1914 * @param flags Set of XOF_* flags to use with this handle 1915 * @return Newly allocated handle 1916 * @see xo_destroy 1917 */ 1918xo_handle_t * 1919xo_create_to_file (FILE *fp, xo_style_t style, xo_xof_flags_t flags) 1920{ 1921 xo_handle_t *xop = xo_create(style, flags); 1922 1923 if (xop) { 1924 xop->xo_opaque = fp; 1925 xop->xo_write = xo_write_to_file; 1926 xop->xo_close = xo_close_file; 1927 xop->xo_flush = xo_flush_file; 1928 } 1929 1930 return xop; 1931} 1932 1933/** 1934 * Set the default handler to output to a file. 1935 * 1936 * @param xop libxo handle 1937 * @param fp FILE pointer to use 1938 * @return 0 on success, non-zero on failure 1939 */ 1940int 1941xo_set_file_h (xo_handle_t *xop, FILE *fp) 1942{ 1943 xop = xo_default(xop); 1944 1945 if (fp == NULL) { 1946 xo_failure(xop, "xo_set_file: NULL fp"); 1947 return -1; 1948 } 1949 1950 xop->xo_opaque = fp; 1951 xop->xo_write = xo_write_to_file; 1952 xop->xo_close = xo_close_file; 1953 xop->xo_flush = xo_flush_file; 1954 1955 return 0; 1956} 1957 1958/** 1959 * Set the default handler to output to a file. 1960 * 1961 * @param fp FILE pointer to use 1962 * @return 0 on success, non-zero on failure 1963 */ 1964int 1965xo_set_file (FILE *fp) 1966{ 1967 return xo_set_file_h(NULL, fp); 1968} 1969 1970/** 1971 * Release any resources held by the handle. 1972 * 1973 * @param xop XO handle to alter (or NULL for default handle) 1974 */ 1975void 1976xo_destroy (xo_handle_t *xop_arg) 1977{ 1978 xo_handle_t *xop = xo_default(xop_arg); 1979 1980 xo_flush_h(xop); 1981 1982 if (xop->xo_close && XOF_ISSET(xop, XOF_CLOSE_FP)) 1983 xop->xo_close(xop->xo_opaque); 1984 1985 xo_free(xop->xo_stack); 1986 xo_buf_cleanup(&xop->xo_data); 1987 xo_buf_cleanup(&xop->xo_fmt); 1988 xo_buf_cleanup(&xop->xo_predicate); 1989 xo_buf_cleanup(&xop->xo_attrs); 1990 xo_buf_cleanup(&xop->xo_color_buf); 1991 1992 if (xop->xo_version) 1993 xo_free(xop->xo_version); 1994 1995 if (xop_arg == NULL) { 1996 bzero(&xo_default_handle, sizeof(xo_default_handle)); 1997 xo_default_inited = 0; 1998 } else 1999 xo_free(xop); 2000} 2001 2002/** 2003 * Record a new output style to use for the given handle (or default if 2004 * handle is NULL). This output style will be used for any future output. 2005 * 2006 * @param xop XO handle to alter (or NULL for default handle) 2007 * @param style new output style (XO_STYLE_*) 2008 */ 2009void 2010xo_set_style (xo_handle_t *xop, xo_style_t style) 2011{ 2012 xop = xo_default(xop); 2013 xop->xo_style = style; 2014} 2015 2016/** 2017 * Return the current style of a handle 2018 * 2019 * @param xop XO handle to access 2020 * @return The handle's current style 2021 */ 2022xo_style_t 2023xo_get_style (xo_handle_t *xop) 2024{ 2025 xop = xo_default(xop); 2026 return xo_style(xop); 2027} 2028 2029/** 2030 * Return the XO_STYLE_* value matching a given name 2031 * 2032 * @param name String name of a style 2033 * @return XO_STYLE_* value 2034 */ 2035static int 2036xo_name_to_style (const char *name) 2037{ 2038 if (strcmp(name, "xml") == 0) 2039 return XO_STYLE_XML; 2040 else if (strcmp(name, "json") == 0) 2041 return XO_STYLE_JSON; 2042 else if (strcmp(name, "encoder") == 0) 2043 return XO_STYLE_ENCODER; 2044 else if (strcmp(name, "text") == 0) 2045 return XO_STYLE_TEXT; 2046 else if (strcmp(name, "html") == 0) 2047 return XO_STYLE_HTML; 2048 else if (strcmp(name, "sdparams") == 0) 2049 return XO_STYLE_SDPARAMS; 2050 2051 return -1; 2052} 2053 2054/* 2055 * Indicate if the style is an "encoding" one as opposed to a "display" one. 2056 */ 2057static int 2058xo_style_is_encoding (xo_handle_t *xop) 2059{ 2060 if (xo_style(xop) == XO_STYLE_JSON 2061 || xo_style(xop) == XO_STYLE_XML 2062 || xo_style(xop) == XO_STYLE_SDPARAMS 2063 || xo_style(xop) == XO_STYLE_ENCODER) 2064 return 1; 2065 return 0; 2066} 2067 2068/* Simple name-value mapping */ 2069typedef struct xo_mapping_s { 2070 xo_xff_flags_t xm_value; /* Flag value */ 2071 const char *xm_name; /* String name */ 2072} xo_mapping_t; 2073 2074static xo_xff_flags_t 2075xo_name_lookup (xo_mapping_t *map, const char *value, ssize_t len) 2076{ 2077 if (len == 0) 2078 return 0; 2079 2080 if (len < 0) 2081 len = strlen(value); 2082 2083 while (isspace((int) *value)) { 2084 value += 1; 2085 len -= 1; 2086 } 2087 2088 while (isspace((int) value[len])) 2089 len -= 1; 2090 2091 if (*value == '\0') 2092 return 0; 2093 2094 for ( ; map->xm_name; map++) 2095 if (strncmp(map->xm_name, value, len) == 0) 2096 return map->xm_value; 2097 2098 return 0; 2099} 2100 2101#ifdef NOT_NEEDED_YET 2102static const char * 2103xo_value_lookup (xo_mapping_t *map, xo_xff_flags_t value) 2104{ 2105 if (value == 0) 2106 return NULL; 2107 2108 for ( ; map->xm_name; map++) 2109 if (map->xm_value == value) 2110 return map->xm_name; 2111 2112 return NULL; 2113} 2114#endif /* NOT_NEEDED_YET */ 2115 2116static xo_mapping_t xo_xof_names[] = { 2117 { XOF_COLOR_ALLOWED, "color" }, 2118 { XOF_COLOR, "color-force" }, 2119 { XOF_COLUMNS, "columns" }, 2120 { XOF_DTRT, "dtrt" }, 2121 { XOF_FLUSH, "flush" }, 2122 { XOF_FLUSH_LINE, "flush-line" }, 2123 { XOF_IGNORE_CLOSE, "ignore-close" }, 2124 { XOF_INFO, "info" }, 2125 { XOF_KEYS, "keys" }, 2126 { XOF_LOG_GETTEXT, "log-gettext" }, 2127 { XOF_LOG_SYSLOG, "log-syslog" }, 2128 { XOF_NO_HUMANIZE, "no-humanize" }, 2129 { XOF_NO_LOCALE, "no-locale" }, 2130 { XOF_RETAIN_NONE, "no-retain" }, 2131 { XOF_NO_TOP, "no-top" }, 2132 { XOF_NOT_FIRST, "not-first" }, 2133 { XOF_PRETTY, "pretty" }, 2134 { XOF_RETAIN_ALL, "retain" }, 2135 { XOF_UNDERSCORES, "underscores" }, 2136 { XOF_UNITS, "units" }, 2137 { XOF_WARN, "warn" }, 2138 { XOF_WARN_XML, "warn-xml" }, 2139 { XOF_XPATH, "xpath" }, 2140 { 0, NULL } 2141}; 2142 2143/* Options available via the environment variable ($LIBXO_OPTIONS) */ 2144static xo_mapping_t xo_xof_simple_names[] = { 2145 { XOF_COLOR_ALLOWED, "color" }, 2146 { XOF_FLUSH, "flush" }, 2147 { XOF_FLUSH_LINE, "flush-line" }, 2148 { XOF_NO_HUMANIZE, "no-humanize" }, 2149 { XOF_NO_LOCALE, "no-locale" }, 2150 { XOF_RETAIN_NONE, "no-retain" }, 2151 { XOF_PRETTY, "pretty" }, 2152 { XOF_RETAIN_ALL, "retain" }, 2153 { XOF_UNDERSCORES, "underscores" }, 2154 { XOF_WARN, "warn" }, 2155 { 0, NULL } 2156}; 2157 2158/* 2159 * Convert string name to XOF_* flag value. 2160 * Not all are useful. Or safe. Or sane. 2161 */ 2162static unsigned 2163xo_name_to_flag (const char *name) 2164{ 2165 return (unsigned) xo_name_lookup(xo_xof_names, name, -1); 2166} 2167 2168/** 2169 * Set the style of an libxo handle based on a string name 2170 * 2171 * @param xop XO handle 2172 * @param name String value of name 2173 * @return 0 on success, non-zero on failure 2174 */ 2175int 2176xo_set_style_name (xo_handle_t *xop, const char *name) 2177{ 2178 if (name == NULL) 2179 return -1; 2180 2181 int style = xo_name_to_style(name); 2182 2183 if (style < 0) 2184 return -1; 2185 2186 xo_set_style(xop, style); 2187 return 0; 2188} 2189 2190/* 2191 * Fill in the color map, based on the input string; currently unimplemented 2192 * Look for something like "colors=red/blue+green/yellow" as fg/bg pairs. 2193 */ 2194static void 2195xo_set_color_map (xo_handle_t *xop, char *value) 2196{ 2197#ifdef LIBXO_TEXT_ONLY 2198 return; 2199#endif /* LIBXO_TEXT_ONLY */ 2200 2201 char *cp, *ep, *vp, *np; 2202 ssize_t len = value ? strlen(value) + 1 : 0; 2203 int num = 1, fg, bg; 2204 2205 for (cp = value, ep = cp + len - 1; cp && *cp && cp < ep; cp = np) { 2206 np = strchr(cp, '+'); 2207 if (np) 2208 *np++ = '\0'; 2209 2210 vp = strchr(cp, '/'); 2211 if (vp) 2212 *vp++ = '\0'; 2213 2214 fg = *cp ? xo_color_find(cp) : -1; 2215 bg = (vp && *vp) ? xo_color_find(vp) : -1; 2216 2217 xop->xo_color_map_fg[num] = (fg < 0) ? num : fg; 2218 xop->xo_color_map_bg[num] = (bg < 0) ? num : bg; 2219 if (++num > XO_NUM_COLORS) 2220 break; 2221 } 2222 2223 /* If no color initialization happened, then we don't need the map */ 2224 if (num > 0) 2225 XOF_SET(xop, XOF_COLOR_MAP); 2226 else 2227 XOF_CLEAR(xop, XOF_COLOR_MAP); 2228 2229 /* Fill in the rest of the colors with the defaults */ 2230 for ( ; num < XO_NUM_COLORS; num++) 2231 xop->xo_color_map_fg[num] = xop->xo_color_map_bg[num] = num; 2232} 2233 2234static int 2235xo_set_options_simple (xo_handle_t *xop, const char *input) 2236{ 2237 xo_xof_flags_t new_flag; 2238 char *cp, *ep, *vp, *np, *bp; 2239 ssize_t len = strlen(input) + 1; 2240 2241 bp = alloca(len); 2242 memcpy(bp, input, len); 2243 2244 for (cp = bp, ep = cp + len - 1; cp && cp < ep; cp = np) { 2245 np = strchr(cp, ','); 2246 if (np) 2247 *np++ = '\0'; 2248 2249 vp = strchr(cp, '='); 2250 if (vp) 2251 *vp++ = '\0'; 2252 2253 if (strcmp("colors", cp) == 0) { 2254 xo_set_color_map(xop, vp); 2255 continue; 2256 } 2257 2258 new_flag = xo_name_lookup(xo_xof_simple_names, cp, -1); 2259 if (new_flag != 0) { 2260 XOF_SET(xop, new_flag); 2261 } else if (strcmp(cp, "no-color") == 0) { 2262 XOF_CLEAR(xop, XOF_COLOR_ALLOWED); 2263 } else { 2264 xo_failure(xop, "unknown simple option: %s", cp); 2265 return -1; 2266 } 2267 } 2268 2269 return 0; 2270} 2271 2272/** 2273 * Set the options for a handle using a string of options 2274 * passed in. The input is a comma-separated set of names 2275 * and optional values: "xml,pretty,indent=4" 2276 * 2277 * @param xop XO handle 2278 * @param input Comma-separated set of option values 2279 * @return 0 on success, non-zero on failure 2280 */ 2281int 2282xo_set_options (xo_handle_t *xop, const char *input) 2283{ 2284 char *cp, *ep, *vp, *np, *bp; 2285 int style = -1, new_style, rc = 0; 2286 ssize_t len; 2287 xo_xof_flags_t new_flag; 2288 2289 if (input == NULL) 2290 return 0; 2291 2292 xop = xo_default(xop); 2293 2294#ifdef LIBXO_COLOR_ON_BY_DEFAULT 2295 /* If the installer used --enable-color-on-by-default, then we allow it */ 2296 XOF_SET(xop, XOF_COLOR_ALLOWED); 2297#endif /* LIBXO_COLOR_ON_BY_DEFAULT */ 2298 2299 /* 2300 * We support a simpler, old-school style of giving option 2301 * also, using a single character for each option. It's 2302 * ideal for lazy people, such as myself. 2303 */ 2304 if (*input == ':') { 2305 ssize_t sz; 2306 2307 for (input++ ; *input; input++) { 2308 switch (*input) { 2309 case 'c': 2310 XOF_SET(xop, XOF_COLOR_ALLOWED); 2311 break; 2312 2313 case 'f': 2314 XOF_SET(xop, XOF_FLUSH); 2315 break; 2316 2317 case 'F': 2318 XOF_SET(xop, XOF_FLUSH_LINE); 2319 break; 2320 2321 case 'g': 2322 XOF_SET(xop, XOF_LOG_GETTEXT); 2323 break; 2324 2325 case 'H': 2326 xop->xo_style = XO_STYLE_HTML; 2327 break; 2328 2329 case 'I': 2330 XOF_SET(xop, XOF_INFO); 2331 break; 2332 2333 case 'i': 2334 sz = strspn(input + 1, "0123456789"); 2335 if (sz > 0) { 2336 xop->xo_indent_by = atoi(input + 1); 2337 input += sz - 1; /* Skip value */ 2338 } 2339 break; 2340 2341 case 'J': 2342 xop->xo_style = XO_STYLE_JSON; 2343 break; 2344 2345 case 'k': 2346 XOF_SET(xop, XOF_KEYS); 2347 break; 2348 2349 case 'n': 2350 XOF_SET(xop, XOF_NO_HUMANIZE); 2351 break; 2352 2353 case 'P': 2354 XOF_SET(xop, XOF_PRETTY); 2355 break; 2356 2357 case 'T': 2358 xop->xo_style = XO_STYLE_TEXT; 2359 break; 2360 2361 case 'U': 2362 XOF_SET(xop, XOF_UNITS); 2363 break; 2364 2365 case 'u': 2366 XOF_SET(xop, XOF_UNDERSCORES); 2367 break; 2368 2369 case 'W': 2370 XOF_SET(xop, XOF_WARN); 2371 break; 2372 2373 case 'X': 2374 xop->xo_style = XO_STYLE_XML; 2375 break; 2376 2377 case 'x': 2378 XOF_SET(xop, XOF_XPATH); 2379 break; 2380 } 2381 } 2382 return 0; 2383 } 2384 2385 len = strlen(input) + 1; 2386 bp = alloca(len); 2387 memcpy(bp, input, len); 2388 2389 for (cp = bp, ep = cp + len - 1; cp && cp < ep; cp = np) { 2390 np = strchr(cp, ','); 2391 if (np) 2392 *np++ = '\0'; 2393 2394 vp = strchr(cp, '='); 2395 if (vp) 2396 *vp++ = '\0'; 2397 2398 if (strcmp("colors", cp) == 0) { 2399 xo_set_color_map(xop, vp); 2400 continue; 2401 } 2402 2403 /* 2404 * For options, we don't allow "encoder" since we want to 2405 * handle it explicitly below as "encoder=xxx". 2406 */ 2407 new_style = xo_name_to_style(cp); 2408 if (new_style >= 0 && new_style != XO_STYLE_ENCODER) { 2409 if (style >= 0) 2410 xo_warnx("ignoring multiple styles: '%s'", cp); 2411 else 2412 style = new_style; 2413 } else { 2414 new_flag = xo_name_to_flag(cp); 2415 if (new_flag != 0) 2416 XOF_SET(xop, new_flag); 2417 else if (strcmp(cp, "no-color") == 0) 2418 XOF_CLEAR(xop, XOF_COLOR_ALLOWED); 2419 else if (strcmp(cp, "indent") == 0) { 2420 if (vp) 2421 xop->xo_indent_by = atoi(vp); 2422 else 2423 xo_failure(xop, "missing value for indent option"); 2424 } else if (strcmp(cp, "encoder") == 0) { 2425 if (vp == NULL) 2426 xo_failure(xop, "missing value for encoder option"); 2427 else { 2428 if (xo_encoder_init(xop, vp)) { 2429 xo_failure(xop, "encoder not found: %s", vp); 2430 rc = -1; 2431 } 2432 } 2433 2434 } else { 2435 xo_warnx("unknown libxo option value: '%s'", cp); 2436 rc = -1; 2437 } 2438 } 2439 } 2440 2441 if (style > 0) 2442 xop->xo_style= style; 2443 2444 return rc; 2445} 2446 2447/** 2448 * Set one or more flags for a given handle (or default if handle is NULL). 2449 * These flags will affect future output. 2450 * 2451 * @param xop XO handle to alter (or NULL for default handle) 2452 * @param flags Flags to be set (XOF_*) 2453 */ 2454void 2455xo_set_flags (xo_handle_t *xop, xo_xof_flags_t flags) 2456{ 2457 xop = xo_default(xop); 2458 2459 XOF_SET(xop, flags); 2460} 2461 2462/** 2463 * Accessor to return the current set of flags for a handle 2464 * @param xop XO handle 2465 * @return Current set of flags 2466 */ 2467xo_xof_flags_t 2468xo_get_flags (xo_handle_t *xop) 2469{ 2470 xop = xo_default(xop); 2471 2472 return xop->xo_flags; 2473} 2474 2475/** 2476 * strndup with a twist: len < 0 means len = strlen(str) 2477 */ 2478static char * 2479xo_strndup (const char *str, ssize_t len) 2480{ 2481 if (len < 0) 2482 len = strlen(str); 2483 2484 char *cp = xo_realloc(NULL, len + 1); 2485 if (cp) { 2486 memcpy(cp, str, len); 2487 cp[len] = '\0'; 2488 } 2489 2490 return cp; 2491} 2492 2493/** 2494 * Record a leading prefix for the XPath we generate. This allows the 2495 * generated data to be placed within an XML hierarchy but still have 2496 * accurate XPath expressions. 2497 * 2498 * @param xop XO handle to alter (or NULL for default handle) 2499 * @param path The XPath expression 2500 */ 2501void 2502xo_set_leading_xpath (xo_handle_t *xop, const char *path) 2503{ 2504 xop = xo_default(xop); 2505 2506 if (xop->xo_leading_xpath) { 2507 xo_free(xop->xo_leading_xpath); 2508 xop->xo_leading_xpath = NULL; 2509 } 2510 2511 if (path == NULL) 2512 return; 2513 2514 xop->xo_leading_xpath = xo_strndup(path, -1); 2515} 2516 2517/** 2518 * Record the info data for a set of tags 2519 * 2520 * @param xop XO handle to alter (or NULL for default handle) 2521 * @param info Info data (xo_info_t) to be recorded (or NULL) (MUST BE SORTED) 2522 * @pararm count Number of entries in info (or -1 to count them ourselves) 2523 */ 2524void 2525xo_set_info (xo_handle_t *xop, xo_info_t *infop, int count) 2526{ 2527 xop = xo_default(xop); 2528 2529 if (count < 0 && infop) { 2530 xo_info_t *xip; 2531 2532 for (xip = infop, count = 0; xip->xi_name; xip++, count++) 2533 continue; 2534 } 2535 2536 xop->xo_info = infop; 2537 xop->xo_info_count = count; 2538} 2539 2540/** 2541 * Set the formatter callback for a handle. The callback should 2542 * return a newly formatting contents of a formatting instruction, 2543 * meaning the bits inside the braces. 2544 */ 2545void 2546xo_set_formatter (xo_handle_t *xop, xo_formatter_t func, 2547 xo_checkpointer_t cfunc) 2548{ 2549 xop = xo_default(xop); 2550 2551 xop->xo_formatter = func; 2552 xop->xo_checkpointer = cfunc; 2553} 2554 2555/** 2556 * Clear one or more flags for a given handle (or default if handle is NULL). 2557 * These flags will affect future output. 2558 * 2559 * @param xop XO handle to alter (or NULL for default handle) 2560 * @param flags Flags to be cleared (XOF_*) 2561 */ 2562void 2563xo_clear_flags (xo_handle_t *xop, xo_xof_flags_t flags) 2564{ 2565 xop = xo_default(xop); 2566 2567 XOF_CLEAR(xop, flags); 2568} 2569 2570static const char * 2571xo_state_name (xo_state_t state) 2572{ 2573 static const char *names[] = { 2574 "init", 2575 "open_container", 2576 "close_container", 2577 "open_list", 2578 "close_list", 2579 "open_instance", 2580 "close_instance", 2581 "open_leaf_list", 2582 "close_leaf_list", 2583 "discarding", 2584 "marker", 2585 "emit", 2586 "emit_leaf_list", 2587 "finish", 2588 NULL 2589 }; 2590 2591 if (state < (sizeof(names) / sizeof(names[0]))) 2592 return names[state]; 2593 2594 return "unknown"; 2595} 2596 2597static void 2598xo_line_ensure_open (xo_handle_t *xop, xo_xff_flags_t flags UNUSED) 2599{ 2600 static char div_open[] = "<div class=\"line\">"; 2601 static char div_open_blank[] = "<div class=\"blank-line\">"; 2602 2603 if (XOIF_ISSET(xop, XOIF_DIV_OPEN)) 2604 return; 2605 2606 if (xo_style(xop) != XO_STYLE_HTML) 2607 return; 2608 2609 XOIF_SET(xop, XOIF_DIV_OPEN); 2610 if (flags & XFF_BLANK_LINE) 2611 xo_data_append(xop, div_open_blank, sizeof(div_open_blank) - 1); 2612 else 2613 xo_data_append(xop, div_open, sizeof(div_open) - 1); 2614 2615 if (XOF_ISSET(xop, XOF_PRETTY)) 2616 xo_data_append(xop, "\n", 1); 2617} 2618 2619static void 2620xo_line_close (xo_handle_t *xop) 2621{ 2622 static char div_close[] = "</div>"; 2623 2624 switch (xo_style(xop)) { 2625 case XO_STYLE_HTML: 2626 if (!XOIF_ISSET(xop, XOIF_DIV_OPEN)) 2627 xo_line_ensure_open(xop, 0); 2628 2629 XOIF_CLEAR(xop, XOIF_DIV_OPEN); 2630 xo_data_append(xop, div_close, sizeof(div_close) - 1); 2631 2632 if (XOF_ISSET(xop, XOF_PRETTY)) 2633 xo_data_append(xop, "\n", 1); 2634 break; 2635 2636 case XO_STYLE_TEXT: 2637 xo_data_append(xop, "\n", 1); 2638 break; 2639 } 2640} 2641 2642static int 2643xo_info_compare (const void *key, const void *data) 2644{ 2645 const char *name = key; 2646 const xo_info_t *xip = data; 2647 2648 return strcmp(name, xip->xi_name); 2649} 2650 2651 2652static xo_info_t * 2653xo_info_find (xo_handle_t *xop, const char *name, ssize_t nlen) 2654{ 2655 xo_info_t *xip; 2656 char *cp = alloca(nlen + 1); /* Need local copy for NUL termination */ 2657 2658 memcpy(cp, name, nlen); 2659 cp[nlen] = '\0'; 2660 2661 xip = bsearch(cp, xop->xo_info, xop->xo_info_count, 2662 sizeof(xop->xo_info[0]), xo_info_compare); 2663 return xip; 2664} 2665 2666#define CONVERT(_have, _need) (((_have) << 8) | (_need)) 2667 2668/* 2669 * Check to see that the conversion is safe and sane. 2670 */ 2671static int 2672xo_check_conversion (xo_handle_t *xop, int have_enc, int need_enc) 2673{ 2674 switch (CONVERT(have_enc, need_enc)) { 2675 case CONVERT(XF_ENC_UTF8, XF_ENC_UTF8): 2676 case CONVERT(XF_ENC_UTF8, XF_ENC_LOCALE): 2677 case CONVERT(XF_ENC_WIDE, XF_ENC_UTF8): 2678 case CONVERT(XF_ENC_WIDE, XF_ENC_LOCALE): 2679 case CONVERT(XF_ENC_LOCALE, XF_ENC_LOCALE): 2680 case CONVERT(XF_ENC_LOCALE, XF_ENC_UTF8): 2681 return 0; 2682 2683 default: 2684 xo_failure(xop, "invalid conversion (%c:%c)", have_enc, need_enc); 2685 return 1; 2686 } 2687} 2688 2689static int 2690xo_format_string_direct (xo_handle_t *xop, xo_buffer_t *xbp, 2691 xo_xff_flags_t flags, 2692 const wchar_t *wcp, const char *cp, 2693 ssize_t len, int max, 2694 int need_enc, int have_enc) 2695{ 2696 int cols = 0; 2697 wchar_t wc = 0; 2698 ssize_t ilen, olen; 2699 ssize_t width; 2700 int attr = XOF_BIT_ISSET(flags, XFF_ATTR); 2701 const char *sp; 2702 2703 if (len > 0 && !xo_buf_has_room(xbp, len)) 2704 return 0; 2705 2706 for (;;) { 2707 if (len == 0) 2708 break; 2709 2710 if (cp) { 2711 if (*cp == '\0') 2712 break; 2713 if ((flags & XFF_UNESCAPE) && (*cp == '\\' || *cp == '%')) { 2714 cp += 1; 2715 len -= 1; 2716 if (len == 0 || *cp == '\0') 2717 break; 2718 } 2719 } 2720 2721 if (wcp && *wcp == L'\0') 2722 break; 2723 2724 ilen = 0; 2725 2726 switch (have_enc) { 2727 case XF_ENC_WIDE: /* Wide character */ 2728 wc = *wcp++; 2729 ilen = 1; 2730 break; 2731 2732 case XF_ENC_UTF8: /* UTF-8 */ 2733 ilen = xo_utf8_to_wc_len(cp); 2734 if (ilen < 0) { 2735 xo_failure(xop, "invalid UTF-8 character: %02hhx", *cp); 2736 return -1; /* Can't continue; we can't find the end */ 2737 } 2738 2739 if (len > 0 && len < ilen) { 2740 len = 0; /* Break out of the loop */ 2741 continue; 2742 } 2743 2744 wc = xo_utf8_char(cp, ilen); 2745 if (wc == (wchar_t) -1) { 2746 xo_failure(xop, "invalid UTF-8 character: %02hhx/%d", 2747 *cp, ilen); 2748 return -1; /* Can't continue; we can't find the end */ 2749 } 2750 cp += ilen; 2751 break; 2752 2753 case XF_ENC_LOCALE: /* Native locale */ 2754 ilen = (len > 0) ? len : MB_LEN_MAX; 2755 ilen = mbrtowc(&wc, cp, ilen, &xop->xo_mbstate); 2756 if (ilen < 0) { /* Invalid data; skip */ 2757 xo_failure(xop, "invalid mbs char: %02hhx", *cp); 2758 wc = L'?'; 2759 ilen = 1; 2760 } 2761 2762 if (ilen == 0) { /* Hit a wide NUL character */ 2763 len = 0; 2764 continue; 2765 } 2766 2767 cp += ilen; 2768 break; 2769 } 2770 2771 /* Reduce len, but not below zero */ 2772 if (len > 0) { 2773 len -= ilen; 2774 if (len < 0) 2775 len = 0; 2776 } 2777 2778 /* 2779 * Find the width-in-columns of this character, which must be done 2780 * in wide characters, since we lack a mbswidth() function. If 2781 * it doesn't fit 2782 */ 2783 width = xo_wcwidth(wc); 2784 if (width < 0) 2785 width = iswcntrl(wc) ? 0 : 1; 2786 2787 if (xo_style(xop) == XO_STYLE_TEXT || xo_style(xop) == XO_STYLE_HTML) { 2788 if (max > 0 && cols + width > max) 2789 break; 2790 } 2791 2792 switch (need_enc) { 2793 case XF_ENC_UTF8: 2794 2795 /* Output in UTF-8 needs to be escaped, based on the style */ 2796 switch (xo_style(xop)) { 2797 case XO_STYLE_XML: 2798 case XO_STYLE_HTML: 2799 if (wc == '<') 2800 sp = xo_xml_lt; 2801 else if (wc == '>') 2802 sp = xo_xml_gt; 2803 else if (wc == '&') 2804 sp = xo_xml_amp; 2805 else if (attr && wc == '"') 2806 sp = xo_xml_quot; 2807 else 2808 break; 2809 2810 ssize_t slen = strlen(sp); 2811 if (!xo_buf_has_room(xbp, slen - 1)) 2812 return -1; 2813 2814 memcpy(xbp->xb_curp, sp, slen); 2815 xbp->xb_curp += slen; 2816 goto done_with_encoding; /* Need multi-level 'break' */ 2817 2818 case XO_STYLE_JSON: 2819 if (wc != '\\' && wc != '"' && wc != '\n' && wc != '\r') 2820 break; 2821 2822 if (!xo_buf_has_room(xbp, 2)) 2823 return -1; 2824 2825 *xbp->xb_curp++ = '\\'; 2826 if (wc == '\n') 2827 wc = 'n'; 2828 else if (wc == '\r') 2829 wc = 'r'; 2830 else wc = wc & 0x7f; 2831 2832 *xbp->xb_curp++ = wc; 2833 goto done_with_encoding; 2834 2835 case XO_STYLE_SDPARAMS: 2836 if (wc != '\\' && wc != '"' && wc != ']') 2837 break; 2838 2839 if (!xo_buf_has_room(xbp, 2)) 2840 return -1; 2841 2842 *xbp->xb_curp++ = '\\'; 2843 wc = wc & 0x7f; 2844 *xbp->xb_curp++ = wc; 2845 goto done_with_encoding; 2846 } 2847 2848 olen = xo_utf8_emit_len(wc); 2849 if (olen < 0) { 2850 xo_failure(xop, "ignoring bad length"); 2851 continue; 2852 } 2853 2854 if (!xo_buf_has_room(xbp, olen)) 2855 return -1; 2856 2857 xo_utf8_emit_char(xbp->xb_curp, olen, wc); 2858 xbp->xb_curp += olen; 2859 break; 2860 2861 case XF_ENC_LOCALE: 2862 if (!xo_buf_has_room(xbp, MB_LEN_MAX + 1)) 2863 return -1; 2864 2865 olen = wcrtomb(xbp->xb_curp, wc, &xop->xo_mbstate); 2866 if (olen <= 0) { 2867 xo_failure(xop, "could not convert wide char: %lx", 2868 (unsigned long) wc); 2869 width = 1; 2870 *xbp->xb_curp++ = '?'; 2871 } else 2872 xbp->xb_curp += olen; 2873 break; 2874 } 2875 2876 done_with_encoding: 2877 cols += width; 2878 } 2879 2880 return cols; 2881} 2882 2883static int 2884xo_needed_encoding (xo_handle_t *xop) 2885{ 2886 if (XOF_ISSET(xop, XOF_UTF8)) /* Check the override flag */ 2887 return XF_ENC_UTF8; 2888 2889 if (xo_style(xop) == XO_STYLE_TEXT) /* Text means locale */ 2890 return XF_ENC_LOCALE; 2891 2892 return XF_ENC_UTF8; /* Otherwise, we love UTF-8 */ 2893} 2894 2895static ssize_t 2896xo_format_string (xo_handle_t *xop, xo_buffer_t *xbp, xo_xff_flags_t flags, 2897 xo_format_t *xfp) 2898{ 2899 static char null[] = "(null)"; 2900 static char null_no_quotes[] = "null"; 2901 2902 char *cp = NULL; 2903 wchar_t *wcp = NULL; 2904 ssize_t len; 2905 ssize_t cols = 0, rc = 0; 2906 ssize_t off = xbp->xb_curp - xbp->xb_bufp, off2; 2907 int need_enc = xo_needed_encoding(xop); 2908 2909 if (xo_check_conversion(xop, xfp->xf_enc, need_enc)) 2910 return 0; 2911 2912 len = xfp->xf_width[XF_WIDTH_SIZE]; 2913 2914 if (xfp->xf_fc == 'm') { 2915 cp = strerror(xop->xo_errno); 2916 if (len < 0) 2917 len = cp ? strlen(cp) : 0; 2918 goto normal_string; 2919 2920 } else if (xfp->xf_enc == XF_ENC_WIDE) { 2921 wcp = va_arg(xop->xo_vap, wchar_t *); 2922 if (xfp->xf_skip) 2923 return 0; 2924 2925 /* 2926 * Dont' deref NULL; use the traditional "(null)" instead 2927 * of the more accurate "who's been a naughty boy, then?". 2928 */ 2929 if (wcp == NULL) { 2930 cp = null; 2931 len = sizeof(null) - 1; 2932 } 2933 2934 } else { 2935 cp = va_arg(xop->xo_vap, char *); /* UTF-8 or native */ 2936 2937 normal_string: 2938 if (xfp->xf_skip) 2939 return 0; 2940 2941 /* Echo "Dont' deref NULL" logic */ 2942 if (cp == NULL) { 2943 if ((flags & XFF_NOQUOTE) && xo_style_is_encoding(xop)) { 2944 cp = null_no_quotes; 2945 len = sizeof(null_no_quotes) - 1; 2946 } else { 2947 cp = null; 2948 len = sizeof(null) - 1; 2949 } 2950 } 2951 2952 /* 2953 * Optimize the most common case, which is "%s". We just 2954 * need to copy the complete string to the output buffer. 2955 */ 2956 if (xfp->xf_enc == need_enc 2957 && xfp->xf_width[XF_WIDTH_MIN] < 0 2958 && xfp->xf_width[XF_WIDTH_SIZE] < 0 2959 && xfp->xf_width[XF_WIDTH_MAX] < 0 2960 && !(XOIF_ISSET(xop, XOIF_ANCHOR) 2961 || XOF_ISSET(xop, XOF_COLUMNS))) { 2962 len = strlen(cp); 2963 xo_buf_escape(xop, xbp, cp, len, flags); 2964 2965 /* 2966 * Our caller expects xb_curp left untouched, so we have 2967 * to reset it and return the number of bytes written to 2968 * the buffer. 2969 */ 2970 off2 = xbp->xb_curp - xbp->xb_bufp; 2971 rc = off2 - off; 2972 xbp->xb_curp = xbp->xb_bufp + off; 2973 2974 return rc; 2975 } 2976 } 2977 2978 cols = xo_format_string_direct(xop, xbp, flags, wcp, cp, len, 2979 xfp->xf_width[XF_WIDTH_MAX], 2980 need_enc, xfp->xf_enc); 2981 if (cols < 0) 2982 goto bail; 2983 2984 /* 2985 * xo_buf_append* will move xb_curp, so we save/restore it. 2986 */ 2987 off2 = xbp->xb_curp - xbp->xb_bufp; 2988 rc = off2 - off; 2989 xbp->xb_curp = xbp->xb_bufp + off; 2990 2991 if (cols < xfp->xf_width[XF_WIDTH_MIN]) { 2992 /* 2993 * Find the number of columns needed to display the string. 2994 * If we have the original wide string, we just call wcswidth, 2995 * but if we did the work ourselves, then we need to do it. 2996 */ 2997 int delta = xfp->xf_width[XF_WIDTH_MIN] - cols; 2998 if (!xo_buf_has_room(xbp, xfp->xf_width[XF_WIDTH_MIN])) 2999 goto bail; 3000 3001 /* 3002 * If seen_minus, then pad on the right; otherwise move it so 3003 * we can pad on the left. 3004 */ 3005 if (xfp->xf_seen_minus) { 3006 cp = xbp->xb_curp + rc; 3007 } else { 3008 cp = xbp->xb_curp; 3009 memmove(xbp->xb_curp + delta, xbp->xb_curp, rc); 3010 } 3011 3012 /* Set the padding */ 3013 memset(cp, (xfp->xf_leading_zero > 0) ? '0' : ' ', delta); 3014 rc += delta; 3015 cols += delta; 3016 } 3017 3018 if (XOF_ISSET(xop, XOF_COLUMNS)) 3019 xop->xo_columns += cols; 3020 if (XOIF_ISSET(xop, XOIF_ANCHOR)) 3021 xop->xo_anchor_columns += cols; 3022 3023 return rc; 3024 3025 bail: 3026 xbp->xb_curp = xbp->xb_bufp + off; 3027 return 0; 3028} 3029 3030/* 3031 * Look backwards in a buffer to find a numeric value 3032 */ 3033static int 3034xo_buf_find_last_number (xo_buffer_t *xbp, ssize_t start_offset) 3035{ 3036 int rc = 0; /* Fail with zero */ 3037 int digit = 1; 3038 char *sp = xbp->xb_bufp; 3039 char *cp = sp + start_offset; 3040 3041 while (--cp >= sp) 3042 if (isdigit((int) *cp)) 3043 break; 3044 3045 for ( ; cp >= sp; cp--) { 3046 if (!isdigit((int) *cp)) 3047 break; 3048 rc += (*cp - '0') * digit; 3049 digit *= 10; 3050 } 3051 3052 return rc; 3053} 3054 3055static ssize_t 3056xo_count_utf8_cols (const char *str, ssize_t len) 3057{ 3058 ssize_t tlen; 3059 wchar_t wc; 3060 ssize_t cols = 0; 3061 const char *ep = str + len; 3062 3063 while (str < ep) { 3064 tlen = xo_utf8_to_wc_len(str); 3065 if (tlen < 0) /* Broken input is very bad */ 3066 return cols; 3067 3068 wc = xo_utf8_char(str, tlen); 3069 if (wc == (wchar_t) -1) 3070 return cols; 3071 3072 /* We only print printable characters */ 3073 if (iswprint((wint_t) wc)) { 3074 /* 3075 * Find the width-in-columns of this character, which must be done 3076 * in wide characters, since we lack a mbswidth() function. 3077 */ 3078 ssize_t width = xo_wcwidth(wc); 3079 if (width < 0) 3080 width = iswcntrl(wc) ? 0 : 1; 3081 3082 cols += width; 3083 } 3084 3085 str += tlen; 3086 } 3087 3088 return cols; 3089} 3090 3091#ifdef HAVE_GETTEXT 3092static inline const char * 3093xo_dgettext (xo_handle_t *xop, const char *str) 3094{ 3095 const char *domainname = xop->xo_gt_domain; 3096 const char *res; 3097 3098 res = dgettext(domainname, str); 3099 3100 if (XOF_ISSET(xop, XOF_LOG_GETTEXT)) 3101 fprintf(stderr, "xo: gettext: %s%s%smsgid \"%s\" returns \"%s\"\n", 3102 domainname ? "domain \"" : "", xo_printable(domainname), 3103 domainname ? "\", " : "", xo_printable(str), xo_printable(res)); 3104 3105 return res; 3106} 3107 3108static inline const char * 3109xo_dngettext (xo_handle_t *xop, const char *sing, const char *plural, 3110 unsigned long int n) 3111{ 3112 const char *domainname = xop->xo_gt_domain; 3113 const char *res; 3114 3115 res = dngettext(domainname, sing, plural, n); 3116 if (XOF_ISSET(xop, XOF_LOG_GETTEXT)) 3117 fprintf(stderr, "xo: gettext: %s%s%s" 3118 "msgid \"%s\", msgid_plural \"%s\" (%lu) returns \"%s\"\n", 3119 domainname ? "domain \"" : "", 3120 xo_printable(domainname), domainname ? "\", " : "", 3121 xo_printable(sing), 3122 xo_printable(plural), n, xo_printable(res)); 3123 3124 return res; 3125} 3126#else /* HAVE_GETTEXT */ 3127static inline const char * 3128xo_dgettext (xo_handle_t *xop UNUSED, const char *str) 3129{ 3130 return str; 3131} 3132 3133static inline const char * 3134xo_dngettext (xo_handle_t *xop UNUSED, const char *singular, 3135 const char *plural, unsigned long int n) 3136{ 3137 return (n == 1) ? singular : plural; 3138} 3139#endif /* HAVE_GETTEXT */ 3140 3141/* 3142 * This is really _re_formatting, since the normal format code has 3143 * generated a beautiful string into xo_data, starting at 3144 * start_offset. We need to see if it's plural, which means 3145 * comma-separated options, or singular. Then we make the appropriate 3146 * call to d[n]gettext() to get the locale-based version. Note that 3147 * both input and output of gettext() this should be UTF-8. 3148 */ 3149static ssize_t 3150xo_format_gettext (xo_handle_t *xop, xo_xff_flags_t flags, 3151 ssize_t start_offset, ssize_t cols, int need_enc) 3152{ 3153 xo_buffer_t *xbp = &xop->xo_data; 3154 3155 if (!xo_buf_has_room(xbp, 1)) 3156 return cols; 3157 3158 xbp->xb_curp[0] = '\0'; /* NUL-terminate the input string */ 3159 3160 char *cp = xbp->xb_bufp + start_offset; 3161 ssize_t len = xbp->xb_curp - cp; 3162 const char *newstr = NULL; 3163 3164 /* 3165 * The plural flag asks us to look backwards at the last numeric 3166 * value rendered and disect the string into two pieces. 3167 */ 3168 if (flags & XFF_GT_PLURAL) { 3169 int n = xo_buf_find_last_number(xbp, start_offset); 3170 char *two = memchr(cp, (int) ',', len); 3171 if (two == NULL) { 3172 xo_failure(xop, "no comma in plural gettext field: '%s'", cp); 3173 return cols; 3174 } 3175 3176 if (two == cp) { 3177 xo_failure(xop, "nothing before comma in plural gettext " 3178 "field: '%s'", cp); 3179 return cols; 3180 } 3181 3182 if (two == xbp->xb_curp) { 3183 xo_failure(xop, "nothing after comma in plural gettext " 3184 "field: '%s'", cp); 3185 return cols; 3186 } 3187 3188 *two++ = '\0'; 3189 if (flags & XFF_GT_FIELD) { 3190 newstr = xo_dngettext(xop, cp, two, n); 3191 } else { 3192 /* Don't do a gettext() look up, just get the plural form */ 3193 newstr = (n == 1) ? cp : two; 3194 } 3195 3196 /* 3197 * If we returned the first string, optimize a bit by 3198 * backing up over comma 3199 */ 3200 if (newstr == cp) { 3201 xbp->xb_curp = two - 1; /* One for comma */ 3202 /* 3203 * If the caller wanted UTF8, we're done; nothing changed, 3204 * but we need to count the columns used. 3205 */ 3206 if (need_enc == XF_ENC_UTF8) 3207 return xo_count_utf8_cols(cp, xbp->xb_curp - cp); 3208 } 3209 3210 } else { 3211 /* The simple case (singular) */ 3212 newstr = xo_dgettext(xop, cp); 3213 3214 if (newstr == cp) { 3215 /* If the caller wanted UTF8, we're done; nothing changed */ 3216 if (need_enc == XF_ENC_UTF8) 3217 return cols; 3218 } 3219 } 3220 3221 /* 3222 * Since the new string string might be in gettext's buffer or 3223 * in the buffer (as the plural form), we make a copy. 3224 */ 3225 ssize_t nlen = strlen(newstr); 3226 char *newcopy = alloca(nlen + 1); 3227 memcpy(newcopy, newstr, nlen + 1); 3228 3229 xbp->xb_curp = xbp->xb_bufp + start_offset; /* Reset the buffer */ 3230 return xo_format_string_direct(xop, xbp, flags, NULL, newcopy, nlen, 0, 3231 need_enc, XF_ENC_UTF8); 3232} 3233 3234static void 3235xo_data_append_content (xo_handle_t *xop, const char *str, ssize_t len, 3236 xo_xff_flags_t flags) 3237{ 3238 int cols; 3239 int need_enc = xo_needed_encoding(xop); 3240 ssize_t start_offset = xo_buf_offset(&xop->xo_data); 3241 3242 cols = xo_format_string_direct(xop, &xop->xo_data, XFF_UNESCAPE | flags, 3243 NULL, str, len, -1, 3244 need_enc, XF_ENC_UTF8); 3245 if (flags & XFF_GT_FLAGS) 3246 cols = xo_format_gettext(xop, flags, start_offset, cols, need_enc); 3247 3248 if (XOF_ISSET(xop, XOF_COLUMNS)) 3249 xop->xo_columns += cols; 3250 if (XOIF_ISSET(xop, XOIF_ANCHOR)) 3251 xop->xo_anchor_columns += cols; 3252} 3253 3254/** 3255 * Bump one of the 'width' values in a format strings (e.g. "%40.50.60s"). 3256 * @param xfp Formatting instructions 3257 * @param digit Single digit (0-9) of input 3258 */ 3259static void 3260xo_bump_width (xo_format_t *xfp, int digit) 3261{ 3262 int *ip = &xfp->xf_width[xfp->xf_dots]; 3263 3264 *ip = ((*ip > 0) ? *ip : 0) * 10 + digit; 3265} 3266 3267static ssize_t 3268xo_trim_ws (xo_buffer_t *xbp, ssize_t len) 3269{ 3270 char *cp, *sp, *ep; 3271 ssize_t delta; 3272 3273 /* First trim leading space */ 3274 for (cp = sp = xbp->xb_curp, ep = cp + len; cp < ep; cp++) { 3275 if (*cp != ' ') 3276 break; 3277 } 3278 3279 delta = cp - sp; 3280 if (delta) { 3281 len -= delta; 3282 memmove(sp, cp, len); 3283 } 3284 3285 /* Then trim off the end */ 3286 for (cp = xbp->xb_curp, sp = ep = cp + len; cp < ep; ep--) { 3287 if (ep[-1] != ' ') 3288 break; 3289 } 3290 3291 delta = sp - ep; 3292 if (delta) { 3293 len -= delta; 3294 cp[len] = '\0'; 3295 } 3296 3297 return len; 3298} 3299 3300/* 3301 * Interface to format a single field. The arguments are in xo_vap, 3302 * and the format is in 'fmt'. If 'xbp' is null, we use xop->xo_data; 3303 * this is the most common case. 3304 */ 3305static ssize_t 3306xo_do_format_field (xo_handle_t *xop, xo_buffer_t *xbp, 3307 const char *fmt, ssize_t flen, xo_xff_flags_t flags) 3308{ 3309 xo_format_t xf; 3310 const char *cp, *ep, *sp, *xp = NULL; 3311 ssize_t rc, cols; 3312 int style = (flags & XFF_XML) ? XO_STYLE_XML : xo_style(xop); 3313 unsigned make_output = !(flags & XFF_NO_OUTPUT) ? 1 : 0; 3314 int need_enc = xo_needed_encoding(xop); 3315 int real_need_enc = need_enc; 3316 ssize_t old_cols = xop->xo_columns; 3317 3318 /* The gettext interface is UTF-8, so we'll need that for now */ 3319 if (flags & XFF_GT_FIELD) 3320 need_enc = XF_ENC_UTF8; 3321 3322 if (xbp == NULL) 3323 xbp = &xop->xo_data; 3324 3325 ssize_t start_offset = xo_buf_offset(xbp); 3326 3327 for (cp = fmt, ep = fmt + flen; cp < ep; cp++) { 3328 /* 3329 * Since we're starting a new field, save the starting offset. 3330 * We'll need this later for field-related operations. 3331 */ 3332 3333 if (*cp != '%') { 3334 add_one: 3335 if (xp == NULL) 3336 xp = cp; 3337 3338 if (*cp == '\\' && cp[1] != '\0') 3339 cp += 1; 3340 continue; 3341 3342 } if (cp + 1 < ep && cp[1] == '%') { 3343 cp += 1; 3344 goto add_one; 3345 } 3346 3347 if (xp) { 3348 if (make_output) { 3349 cols = xo_format_string_direct(xop, xbp, flags | XFF_UNESCAPE, 3350 NULL, xp, cp - xp, -1, 3351 need_enc, XF_ENC_UTF8); 3352 if (XOF_ISSET(xop, XOF_COLUMNS)) 3353 xop->xo_columns += cols; 3354 if (XOIF_ISSET(xop, XOIF_ANCHOR)) 3355 xop->xo_anchor_columns += cols; 3356 } 3357 3358 xp = NULL; 3359 } 3360 3361 bzero(&xf, sizeof(xf)); 3362 xf.xf_leading_zero = -1; 3363 xf.xf_width[0] = xf.xf_width[1] = xf.xf_width[2] = -1; 3364 3365 /* 3366 * "%@" starts an XO-specific set of flags: 3367 * @X@ - XML-only field; ignored if style isn't XML 3368 */ 3369 if (cp[1] == '@') { 3370 for (cp += 2; cp < ep; cp++) { 3371 if (*cp == '@') { 3372 break; 3373 } 3374 if (*cp == '*') { 3375 /* 3376 * '*' means there's a "%*.*s" value in vap that 3377 * we want to ignore 3378 */ 3379 if (!XOF_ISSET(xop, XOF_NO_VA_ARG)) 3380 va_arg(xop->xo_vap, int); 3381 } 3382 } 3383 } 3384 3385 /* Hidden fields are only visible to JSON and XML */ 3386 if (XOF_ISSET(xop, XFF_ENCODE_ONLY)) { 3387 if (style != XO_STYLE_XML 3388 && !xo_style_is_encoding(xop)) 3389 xf.xf_skip = 1; 3390 } else if (XOF_ISSET(xop, XFF_DISPLAY_ONLY)) { 3391 if (style != XO_STYLE_TEXT 3392 && xo_style(xop) != XO_STYLE_HTML) 3393 xf.xf_skip = 1; 3394 } 3395 3396 if (!make_output) 3397 xf.xf_skip = 1; 3398 3399 /* 3400 * Looking at one piece of a format; find the end and 3401 * call snprintf. Then advance xo_vap on our own. 3402 * 3403 * Note that 'n', 'v', and '$' are not supported. 3404 */ 3405 sp = cp; /* Save start pointer */ 3406 for (cp += 1; cp < ep; cp++) { 3407 if (*cp == 'l') 3408 xf.xf_lflag += 1; 3409 else if (*cp == 'h') 3410 xf.xf_hflag += 1; 3411 else if (*cp == 'j') 3412 xf.xf_jflag += 1; 3413 else if (*cp == 't') 3414 xf.xf_tflag += 1; 3415 else if (*cp == 'z') 3416 xf.xf_zflag += 1; 3417 else if (*cp == 'q') 3418 xf.xf_qflag += 1; 3419 else if (*cp == '.') { 3420 if (++xf.xf_dots >= XF_WIDTH_NUM) { 3421 xo_failure(xop, "Too many dots in format: '%s'", fmt); 3422 return -1; 3423 } 3424 } else if (*cp == '-') 3425 xf.xf_seen_minus = 1; 3426 else if (isdigit((int) *cp)) { 3427 if (xf.xf_leading_zero < 0) 3428 xf.xf_leading_zero = (*cp == '0'); 3429 xo_bump_width(&xf, *cp - '0'); 3430 } else if (*cp == '*') { 3431 xf.xf_stars += 1; 3432 xf.xf_star[xf.xf_dots] = 1; 3433 } else if (strchr("diouxXDOUeEfFgGaAcCsSpm", *cp) != NULL) 3434 break; 3435 else if (*cp == 'n' || *cp == 'v') { 3436 xo_failure(xop, "unsupported format: '%s'", fmt); 3437 return -1; 3438 } 3439 } 3440 3441 if (cp == ep) 3442 xo_failure(xop, "field format missing format character: %s", 3443 fmt); 3444 3445 xf.xf_fc = *cp; 3446 3447 if (!XOF_ISSET(xop, XOF_NO_VA_ARG)) { 3448 if (*cp == 's' || *cp == 'S') { 3449 /* Handle "%*.*.*s" */ 3450 int s; 3451 for (s = 0; s < XF_WIDTH_NUM; s++) { 3452 if (xf.xf_star[s]) { 3453 xf.xf_width[s] = va_arg(xop->xo_vap, int); 3454 3455 /* Normalize a negative width value */ 3456 if (xf.xf_width[s] < 0) { 3457 if (s == 0) { 3458 xf.xf_width[0] = -xf.xf_width[0]; 3459 xf.xf_seen_minus = 1; 3460 } else 3461 xf.xf_width[s] = -1; /* Ignore negative values */ 3462 } 3463 } 3464 } 3465 } 3466 } 3467 3468 /* If no max is given, it defaults to size */ 3469 if (xf.xf_width[XF_WIDTH_MAX] < 0 && xf.xf_width[XF_WIDTH_SIZE] >= 0) 3470 xf.xf_width[XF_WIDTH_MAX] = xf.xf_width[XF_WIDTH_SIZE]; 3471 3472 if (xf.xf_fc == 'D' || xf.xf_fc == 'O' || xf.xf_fc == 'U') 3473 xf.xf_lflag = 1; 3474 3475 if (!xf.xf_skip) { 3476 xo_buffer_t *fbp = &xop->xo_fmt; 3477 ssize_t len = cp - sp + 1; 3478 if (!xo_buf_has_room(fbp, len + 1)) 3479 return -1; 3480 3481 char *newfmt = fbp->xb_curp; 3482 memcpy(newfmt, sp, len); 3483 newfmt[0] = '%'; /* If we skipped over a "%@...@s" format */ 3484 newfmt[len] = '\0'; 3485 3486 /* 3487 * Bad news: our strings are UTF-8, but the stock printf 3488 * functions won't handle field widths for wide characters 3489 * correctly. So we have to handle this ourselves. 3490 */ 3491 if (xop->xo_formatter == NULL 3492 && (xf.xf_fc == 's' || xf.xf_fc == 'S' 3493 || xf.xf_fc == 'm')) { 3494 3495 xf.xf_enc = (xf.xf_fc == 'm') ? XF_ENC_UTF8 3496 : (xf.xf_lflag || (xf.xf_fc == 'S')) ? XF_ENC_WIDE 3497 : xf.xf_hflag ? XF_ENC_LOCALE : XF_ENC_UTF8; 3498 3499 rc = xo_format_string(xop, xbp, flags, &xf); 3500 3501 if ((flags & XFF_TRIM_WS) && xo_style_is_encoding(xop)) 3502 rc = xo_trim_ws(xbp, rc); 3503 3504 } else { 3505 ssize_t columns = rc = xo_vsnprintf(xop, xbp, newfmt, 3506 xop->xo_vap); 3507 3508 /* 3509 * For XML and HTML, we need "&<>" processing; for JSON, 3510 * it's quotes. Text gets nothing. 3511 */ 3512 switch (style) { 3513 case XO_STYLE_XML: 3514 if (flags & XFF_TRIM_WS) 3515 columns = rc = xo_trim_ws(xbp, rc); 3516 /* FALLTHRU */ 3517 case XO_STYLE_HTML: 3518 rc = xo_escape_xml(xbp, rc, (flags & XFF_ATTR)); 3519 break; 3520 3521 case XO_STYLE_JSON: 3522 if (flags & XFF_TRIM_WS) 3523 columns = rc = xo_trim_ws(xbp, rc); 3524 rc = xo_escape_json(xbp, rc, 0); 3525 break; 3526 3527 case XO_STYLE_SDPARAMS: 3528 if (flags & XFF_TRIM_WS) 3529 columns = rc = xo_trim_ws(xbp, rc); 3530 rc = xo_escape_sdparams(xbp, rc, 0); 3531 break; 3532 3533 case XO_STYLE_ENCODER: 3534 if (flags & XFF_TRIM_WS) 3535 columns = rc = xo_trim_ws(xbp, rc); 3536 break; 3537 } 3538 3539 /* 3540 * We can assume all the non-%s data we've 3541 * added is ASCII, so the columns and bytes are the 3542 * same. xo_format_string handles all the fancy 3543 * string conversions and updates xo_anchor_columns 3544 * accordingly. 3545 */ 3546 if (XOF_ISSET(xop, XOF_COLUMNS)) 3547 xop->xo_columns += columns; 3548 if (XOIF_ISSET(xop, XOIF_ANCHOR)) 3549 xop->xo_anchor_columns += columns; 3550 } 3551 3552 xbp->xb_curp += rc; 3553 } 3554 3555 /* 3556 * Now for the tricky part: we need to move the argument pointer 3557 * along by the amount needed. 3558 */ 3559 if (!XOF_ISSET(xop, XOF_NO_VA_ARG)) { 3560 3561 if (xf.xf_fc == 's' ||xf.xf_fc == 'S') { 3562 /* 3563 * The 'S' and 's' formats are normally handled in 3564 * xo_format_string, but if we skipped it, then we 3565 * need to pop it. 3566 */ 3567 if (xf.xf_skip) 3568 va_arg(xop->xo_vap, char *); 3569 3570 } else if (xf.xf_fc == 'm') { 3571 /* Nothing on the stack for "%m" */ 3572 3573 } else { 3574 int s; 3575 for (s = 0; s < XF_WIDTH_NUM; s++) { 3576 if (xf.xf_star[s]) 3577 va_arg(xop->xo_vap, int); 3578 } 3579 3580 if (strchr("diouxXDOU", xf.xf_fc) != NULL) { 3581 if (xf.xf_hflag > 1) { 3582 va_arg(xop->xo_vap, int); 3583 3584 } else if (xf.xf_hflag > 0) { 3585 va_arg(xop->xo_vap, int); 3586 3587 } else if (xf.xf_lflag > 1) { 3588 va_arg(xop->xo_vap, unsigned long long); 3589 3590 } else if (xf.xf_lflag > 0) { 3591 va_arg(xop->xo_vap, unsigned long); 3592 3593 } else if (xf.xf_jflag > 0) { 3594 va_arg(xop->xo_vap, intmax_t); 3595 3596 } else if (xf.xf_tflag > 0) { 3597 va_arg(xop->xo_vap, ptrdiff_t); 3598 3599 } else if (xf.xf_zflag > 0) { 3600 va_arg(xop->xo_vap, size_t); 3601 3602 } else if (xf.xf_qflag > 0) { 3603 va_arg(xop->xo_vap, quad_t); 3604 3605 } else { 3606 va_arg(xop->xo_vap, int); 3607 } 3608 } else if (strchr("eEfFgGaA", xf.xf_fc) != NULL) 3609 if (xf.xf_lflag) 3610 va_arg(xop->xo_vap, long double); 3611 else 3612 va_arg(xop->xo_vap, double); 3613 3614 else if (xf.xf_fc == 'C' || (xf.xf_fc == 'c' && xf.xf_lflag)) 3615 va_arg(xop->xo_vap, wint_t); 3616 3617 else if (xf.xf_fc == 'c') 3618 va_arg(xop->xo_vap, int); 3619 3620 else if (xf.xf_fc == 'p') 3621 va_arg(xop->xo_vap, void *); 3622 } 3623 } 3624 } 3625 3626 if (xp) { 3627 if (make_output) { 3628 cols = xo_format_string_direct(xop, xbp, flags | XFF_UNESCAPE, 3629 NULL, xp, cp - xp, -1, 3630 need_enc, XF_ENC_UTF8); 3631 3632 if (XOF_ISSET(xop, XOF_COLUMNS)) 3633 xop->xo_columns += cols; 3634 if (XOIF_ISSET(xop, XOIF_ANCHOR)) 3635 xop->xo_anchor_columns += cols; 3636 } 3637 3638 xp = NULL; 3639 } 3640 3641 if (flags & XFF_GT_FLAGS) { 3642 /* 3643 * Handle gettext()ing the field by looking up the value 3644 * and then copying it in, while converting to locale, if 3645 * needed. 3646 */ 3647 ssize_t new_cols = xo_format_gettext(xop, flags, start_offset, 3648 old_cols, real_need_enc); 3649 3650 if (XOF_ISSET(xop, XOF_COLUMNS)) 3651 xop->xo_columns += new_cols - old_cols; 3652 if (XOIF_ISSET(xop, XOIF_ANCHOR)) 3653 xop->xo_anchor_columns += new_cols - old_cols; 3654 } 3655 3656 return 0; 3657} 3658 3659/* 3660 * Remove any numeric precision/width format from the format string by 3661 * inserting the "%" after the [0-9]+, returning the substring. 3662 */ 3663static char * 3664xo_fix_encoding (xo_handle_t *xop UNUSED, char *encoding) 3665{ 3666 char *cp = encoding; 3667 3668 if (cp[0] != '%' || !isdigit((int) cp[1])) 3669 return encoding; 3670 3671 for (cp += 2; *cp; cp++) { 3672 if (!isdigit((int) *cp)) 3673 break; 3674 } 3675 3676 *--cp = '%'; /* Back off and insert the '%' */ 3677 3678 return cp; 3679} 3680 3681static void 3682xo_color_append_html (xo_handle_t *xop) 3683{ 3684 /* 3685 * If the color buffer has content, we add it now. It's already 3686 * prebuilt and ready, since we want to add it to every <div>. 3687 */ 3688 if (!xo_buf_is_empty(&xop->xo_color_buf)) { 3689 xo_buffer_t *xbp = &xop->xo_color_buf; 3690 3691 xo_data_append(xop, xbp->xb_bufp, xbp->xb_curp - xbp->xb_bufp); 3692 } 3693} 3694 3695/* 3696 * A wrapper for humanize_number that autoscales, since the 3697 * HN_AUTOSCALE flag scales as needed based on the size of 3698 * the output buffer, not the size of the value. I also 3699 * wish HN_DECIMAL was more imperative, without the <10 3700 * test. But the boat only goes where we want when we hold 3701 * the rudder, so xo_humanize fixes part of the problem. 3702 */ 3703static ssize_t 3704xo_humanize (char *buf, ssize_t len, uint64_t value, int flags) 3705{ 3706 int scale = 0; 3707 3708 if (value) { 3709 uint64_t left = value; 3710 3711 if (flags & HN_DIVISOR_1000) { 3712 for ( ; left; scale++) 3713 left /= 1000; 3714 } else { 3715 for ( ; left; scale++) 3716 left /= 1024; 3717 } 3718 scale -= 1; 3719 } 3720 3721 return xo_humanize_number(buf, len, value, "", scale, flags); 3722} 3723 3724/* 3725 * This is an area where we can save information from the handle for 3726 * later restoration. We need to know what data was rendered to know 3727 * what needs cleaned up. 3728 */ 3729typedef struct xo_humanize_save_s { 3730 ssize_t xhs_offset; /* Saved xo_offset */ 3731 ssize_t xhs_columns; /* Saved xo_columns */ 3732 ssize_t xhs_anchor_columns; /* Saved xo_anchor_columns */ 3733} xo_humanize_save_t; 3734 3735/* 3736 * Format a "humanized" value for a numeric, meaning something nice 3737 * like "44M" instead of "44470272". We autoscale, choosing the 3738 * most appropriate value for K/M/G/T/P/E based on the value given. 3739 */ 3740static void 3741xo_format_humanize (xo_handle_t *xop, xo_buffer_t *xbp, 3742 xo_humanize_save_t *savep, xo_xff_flags_t flags) 3743{ 3744 if (XOF_ISSET(xop, XOF_NO_HUMANIZE)) 3745 return; 3746 3747 ssize_t end_offset = xbp->xb_curp - xbp->xb_bufp; 3748 if (end_offset == savep->xhs_offset) /* Huh? Nothing to render */ 3749 return; 3750 3751 /* 3752 * We have a string that's allegedly a number. We want to 3753 * humanize it, which means turning it back into a number 3754 * and calling xo_humanize_number on it. 3755 */ 3756 uint64_t value; 3757 char *ep; 3758 3759 xo_buf_append(xbp, "", 1); /* NUL-terminate it */ 3760 3761 value = strtoull(xbp->xb_bufp + savep->xhs_offset, &ep, 0); 3762 if (!(value == ULLONG_MAX && errno == ERANGE) 3763 && (ep != xbp->xb_bufp + savep->xhs_offset)) { 3764 /* 3765 * There are few values where humanize_number needs 3766 * more bytes than the original value. I've used 3767 * 10 as a rectal number to cover those scenarios. 3768 */ 3769 if (xo_buf_has_room(xbp, 10)) { 3770 xbp->xb_curp = xbp->xb_bufp + savep->xhs_offset; 3771 3772 ssize_t rc; 3773 ssize_t left = (xbp->xb_bufp + xbp->xb_size) - xbp->xb_curp; 3774 int hn_flags = HN_NOSPACE; /* On by default */ 3775 3776 if (flags & XFF_HN_SPACE) 3777 hn_flags &= ~HN_NOSPACE; 3778 3779 if (flags & XFF_HN_DECIMAL) 3780 hn_flags |= HN_DECIMAL; 3781 3782 if (flags & XFF_HN_1000) 3783 hn_flags |= HN_DIVISOR_1000; 3784 3785 rc = xo_humanize(xbp->xb_curp, left, value, hn_flags); 3786 if (rc > 0) { 3787 xbp->xb_curp += rc; 3788 xop->xo_columns = savep->xhs_columns + rc; 3789 xop->xo_anchor_columns = savep->xhs_anchor_columns + rc; 3790 } 3791 } 3792 } 3793} 3794 3795/* 3796 * Convenience function that either append a fixed value (if one is 3797 * given) or formats a field using a format string. If it's 3798 * encode_only, then we can't skip formatting the field, since it may 3799 * be pulling arguments off the stack. 3800 */ 3801static inline void 3802xo_simple_field (xo_handle_t *xop, unsigned encode_only, 3803 const char *value, ssize_t vlen, 3804 const char *fmt, ssize_t flen, xo_xff_flags_t flags) 3805{ 3806 if (encode_only) 3807 flags |= XFF_NO_OUTPUT; 3808 3809 if (vlen == 0) 3810 xo_do_format_field(xop, NULL, fmt, flen, flags); 3811 else if (!encode_only) 3812 xo_data_append_content(xop, value, vlen, flags); 3813} 3814 3815/* 3816 * Html mode: append a <div> to the output buffer contain a field 3817 * along with all the supporting information indicated by the flags. 3818 */ 3819static void 3820xo_buf_append_div (xo_handle_t *xop, const char *class, xo_xff_flags_t flags, 3821 const char *name, ssize_t nlen, 3822 const char *value, ssize_t vlen, 3823 const char *fmt, ssize_t flen, 3824 const char *encoding, ssize_t elen) 3825{ 3826 static char div_start[] = "<div class=\""; 3827 static char div_tag[] = "\" data-tag=\""; 3828 static char div_xpath[] = "\" data-xpath=\""; 3829 static char div_key[] = "\" data-key=\"key"; 3830 static char div_end[] = "\">"; 3831 static char div_close[] = "</div>"; 3832 3833 /* The encoding format defaults to the normal format */ 3834 if (encoding == NULL && fmt != NULL) { 3835 char *enc = alloca(flen + 1); 3836 memcpy(enc, fmt, flen); 3837 enc[flen] = '\0'; 3838 encoding = xo_fix_encoding(xop, enc); 3839 elen = strlen(encoding); 3840 } 3841 3842 /* 3843 * To build our XPath predicate, we need to save the va_list before 3844 * we format our data, and then restore it before we format the 3845 * xpath expression. 3846 * Display-only keys implies that we've got an encode-only key 3847 * elsewhere, so we don't use them from making predicates. 3848 */ 3849 int need_predidate = 3850 (name && (flags & XFF_KEY) && !(flags & XFF_DISPLAY_ONLY) 3851 && XOF_ISSET(xop, XOF_XPATH)) ? 1 : 0; 3852 3853 if (need_predidate) { 3854 va_list va_local; 3855 3856 va_copy(va_local, xop->xo_vap); 3857 if (xop->xo_checkpointer) 3858 xop->xo_checkpointer(xop, xop->xo_vap, 0); 3859 3860 /* 3861 * Build an XPath predicate expression to match this key. 3862 * We use the format buffer. 3863 */ 3864 xo_buffer_t *pbp = &xop->xo_predicate; 3865 pbp->xb_curp = pbp->xb_bufp; /* Restart buffer */ 3866 3867 xo_buf_append(pbp, "[", 1); 3868 xo_buf_escape(xop, pbp, name, nlen, 0); 3869 if (XOF_ISSET(xop, XOF_PRETTY)) 3870 xo_buf_append(pbp, " = '", 4); 3871 else 3872 xo_buf_append(pbp, "='", 2); 3873 3874 xo_xff_flags_t pflags = flags | XFF_XML | XFF_ATTR; 3875 pflags &= ~(XFF_NO_OUTPUT | XFF_ENCODE_ONLY); 3876 xo_do_format_field(xop, pbp, encoding, elen, pflags); 3877 3878 xo_buf_append(pbp, "']", 2); 3879 3880 /* Now we record this predicate expression in the stack */ 3881 xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth]; 3882 ssize_t olen = xsp->xs_keys ? strlen(xsp->xs_keys) : 0; 3883 ssize_t dlen = pbp->xb_curp - pbp->xb_bufp; 3884 3885 char *cp = xo_realloc(xsp->xs_keys, olen + dlen + 1); 3886 if (cp) { 3887 memcpy(cp + olen, pbp->xb_bufp, dlen); 3888 cp[olen + dlen] = '\0'; 3889 xsp->xs_keys = cp; 3890 } 3891 3892 /* Now we reset the xo_vap as if we were never here */ 3893 va_end(xop->xo_vap); 3894 va_copy(xop->xo_vap, va_local); 3895 va_end(va_local); 3896 if (xop->xo_checkpointer) 3897 xop->xo_checkpointer(xop, xop->xo_vap, 1); 3898 } 3899 3900 if (flags & XFF_ENCODE_ONLY) { 3901 /* 3902 * Even if this is encode-only, we need to go through the 3903 * work of formatting it to make sure the args are cleared 3904 * from xo_vap. This is not true when vlen is zero, since 3905 * that means our "value" isn't on the stack. 3906 */ 3907 xo_simple_field(xop, TRUE, NULL, 0, encoding, elen, flags); 3908 return; 3909 } 3910 3911 xo_line_ensure_open(xop, 0); 3912 3913 if (XOF_ISSET(xop, XOF_PRETTY)) 3914 xo_buf_indent(xop, xop->xo_indent_by); 3915 3916 xo_data_append(xop, div_start, sizeof(div_start) - 1); 3917 xo_data_append(xop, class, strlen(class)); 3918 3919 /* 3920 * If the color buffer has content, we add it now. It's already 3921 * prebuilt and ready, since we want to add it to every <div>. 3922 */ 3923 if (!xo_buf_is_empty(&xop->xo_color_buf)) { 3924 xo_buffer_t *xbp = &xop->xo_color_buf; 3925 3926 xo_data_append(xop, xbp->xb_bufp, xbp->xb_curp - xbp->xb_bufp); 3927 } 3928 3929 if (name) { 3930 xo_data_append(xop, div_tag, sizeof(div_tag) - 1); 3931 xo_data_escape(xop, name, nlen); 3932 3933 /* 3934 * Save the offset at which we'd place units. See xo_format_units. 3935 */ 3936 if (XOF_ISSET(xop, XOF_UNITS)) { 3937 XOIF_SET(xop, XOIF_UNITS_PENDING); 3938 /* 3939 * Note: We need the '+1' here because we know we've not 3940 * added the closing quote. We add one, knowing the quote 3941 * will be added shortly. 3942 */ 3943 xop->xo_units_offset = 3944 xop->xo_data.xb_curp -xop->xo_data.xb_bufp + 1; 3945 } 3946 3947 if (XOF_ISSET(xop, XOF_XPATH)) { 3948 int i; 3949 xo_stack_t *xsp; 3950 3951 xo_data_append(xop, div_xpath, sizeof(div_xpath) - 1); 3952 if (xop->xo_leading_xpath) 3953 xo_data_append(xop, xop->xo_leading_xpath, 3954 strlen(xop->xo_leading_xpath)); 3955 3956 for (i = 0; i <= xop->xo_depth; i++) { 3957 xsp = &xop->xo_stack[i]; 3958 if (xsp->xs_name == NULL) 3959 continue; 3960 3961 /* 3962 * XSS_OPEN_LIST and XSS_OPEN_LEAF_LIST stack frames 3963 * are directly under XSS_OPEN_INSTANCE frames so we 3964 * don't need to put these in our XPath expressions. 3965 */ 3966 if (xsp->xs_state == XSS_OPEN_LIST 3967 || xsp->xs_state == XSS_OPEN_LEAF_LIST) 3968 continue; 3969 3970 xo_data_append(xop, "/", 1); 3971 xo_data_escape(xop, xsp->xs_name, strlen(xsp->xs_name)); 3972 if (xsp->xs_keys) { 3973 /* Don't show keys for the key field */ 3974 if (i != xop->xo_depth || !(flags & XFF_KEY)) 3975 xo_data_append(xop, xsp->xs_keys, strlen(xsp->xs_keys)); 3976 } 3977 } 3978 3979 xo_data_append(xop, "/", 1); 3980 xo_data_escape(xop, name, nlen); 3981 } 3982 3983 if (XOF_ISSET(xop, XOF_INFO) && xop->xo_info) { 3984 static char in_type[] = "\" data-type=\""; 3985 static char in_help[] = "\" data-help=\""; 3986 3987 xo_info_t *xip = xo_info_find(xop, name, nlen); 3988 if (xip) { 3989 if (xip->xi_type) { 3990 xo_data_append(xop, in_type, sizeof(in_type) - 1); 3991 xo_data_escape(xop, xip->xi_type, strlen(xip->xi_type)); 3992 } 3993 if (xip->xi_help) { 3994 xo_data_append(xop, in_help, sizeof(in_help) - 1); 3995 xo_data_escape(xop, xip->xi_help, strlen(xip->xi_help)); 3996 } 3997 } 3998 } 3999 4000 if ((flags & XFF_KEY) && XOF_ISSET(xop, XOF_KEYS)) 4001 xo_data_append(xop, div_key, sizeof(div_key) - 1); 4002 } 4003 4004 xo_buffer_t *xbp = &xop->xo_data; 4005 ssize_t base_offset = xbp->xb_curp - xbp->xb_bufp; 4006 4007 xo_data_append(xop, div_end, sizeof(div_end) - 1); 4008 4009 xo_humanize_save_t save; /* Save values for humanizing logic */ 4010 4011 save.xhs_offset = xbp->xb_curp - xbp->xb_bufp; 4012 save.xhs_columns = xop->xo_columns; 4013 save.xhs_anchor_columns = xop->xo_anchor_columns; 4014 4015 xo_simple_field(xop, FALSE, value, vlen, fmt, flen, flags); 4016 4017 if (flags & XFF_HUMANIZE) { 4018 /* 4019 * Unlike text style, we want to retain the original value and 4020 * stuff it into the "data-number" attribute. 4021 */ 4022 static const char div_number[] = "\" data-number=\""; 4023 ssize_t div_len = sizeof(div_number) - 1; 4024 4025 ssize_t end_offset = xbp->xb_curp - xbp->xb_bufp; 4026 ssize_t olen = end_offset - save.xhs_offset; 4027 4028 char *cp = alloca(olen + 1); 4029 memcpy(cp, xbp->xb_bufp + save.xhs_offset, olen); 4030 cp[olen] = '\0'; 4031 4032 xo_format_humanize(xop, xbp, &save, flags); 4033 4034 if (xo_buf_has_room(xbp, div_len + olen)) { 4035 ssize_t new_offset = xbp->xb_curp - xbp->xb_bufp; 4036 4037 4038 /* Move the humanized string off to the left */ 4039 memmove(xbp->xb_bufp + base_offset + div_len + olen, 4040 xbp->xb_bufp + base_offset, new_offset - base_offset); 4041 4042 /* Copy the data_number attribute name */ 4043 memcpy(xbp->xb_bufp + base_offset, div_number, div_len); 4044 4045 /* Copy the original long value */ 4046 memcpy(xbp->xb_bufp + base_offset + div_len, cp, olen); 4047 xbp->xb_curp += div_len + olen; 4048 } 4049 } 4050 4051 xo_data_append(xop, div_close, sizeof(div_close) - 1); 4052 4053 if (XOF_ISSET(xop, XOF_PRETTY)) 4054 xo_data_append(xop, "\n", 1); 4055} 4056 4057static void 4058xo_format_text (xo_handle_t *xop, const char *str, ssize_t len) 4059{ 4060 switch (xo_style(xop)) { 4061 case XO_STYLE_TEXT: 4062 xo_buf_append_locale(xop, &xop->xo_data, str, len); 4063 break; 4064 4065 case XO_STYLE_HTML: 4066 xo_buf_append_div(xop, "text", 0, NULL, 0, str, len, NULL, 0, NULL, 0); 4067 break; 4068 } 4069} 4070 4071static void 4072xo_format_title (xo_handle_t *xop, xo_field_info_t *xfip, 4073 const char *value, ssize_t vlen) 4074{ 4075 const char *fmt = xfip->xfi_format; 4076 ssize_t flen = xfip->xfi_flen; 4077 xo_xff_flags_t flags = xfip->xfi_flags; 4078 4079 static char div_open[] = "<div class=\"title"; 4080 static char div_middle[] = "\">"; 4081 static char div_close[] = "</div>"; 4082 4083 if (flen == 0) { 4084 fmt = "%s"; 4085 flen = 2; 4086 } 4087 4088 switch (xo_style(xop)) { 4089 case XO_STYLE_XML: 4090 case XO_STYLE_JSON: 4091 case XO_STYLE_SDPARAMS: 4092 case XO_STYLE_ENCODER: 4093 /* 4094 * Even though we don't care about text, we need to do 4095 * enough parsing work to skip over the right bits of xo_vap. 4096 */ 4097 xo_simple_field(xop, TRUE, value, vlen, fmt, flen, flags); 4098 return; 4099 } 4100 4101 xo_buffer_t *xbp = &xop->xo_data; 4102 ssize_t start = xbp->xb_curp - xbp->xb_bufp; 4103 ssize_t left = xbp->xb_size - start; 4104 ssize_t rc; 4105 4106 if (xo_style(xop) == XO_STYLE_HTML) { 4107 xo_line_ensure_open(xop, 0); 4108 if (XOF_ISSET(xop, XOF_PRETTY)) 4109 xo_buf_indent(xop, xop->xo_indent_by); 4110 xo_buf_append(&xop->xo_data, div_open, sizeof(div_open) - 1); 4111 xo_color_append_html(xop); 4112 xo_buf_append(&xop->xo_data, div_middle, sizeof(div_middle) - 1); 4113 } 4114 4115 start = xbp->xb_curp - xbp->xb_bufp; /* Reset start */ 4116 if (vlen) { 4117 char *newfmt = alloca(flen + 1); 4118 memcpy(newfmt, fmt, flen); 4119 newfmt[flen] = '\0'; 4120 4121 /* If len is non-zero, the format string apply to the name */ 4122 char *newstr = alloca(vlen + 1); 4123 memcpy(newstr, value, vlen); 4124 newstr[vlen] = '\0'; 4125 4126 if (newstr[vlen - 1] == 's') { 4127 char *bp; 4128 4129 rc = snprintf(NULL, 0, newfmt, newstr); 4130 if (rc > 0) { 4131 /* 4132 * We have to do this the hard way, since we might need 4133 * the columns. 4134 */ 4135 bp = alloca(rc + 1); 4136 rc = snprintf(bp, rc + 1, newfmt, newstr); 4137 4138 xo_data_append_content(xop, bp, rc, flags); 4139 } 4140 goto move_along; 4141 4142 } else { 4143 rc = snprintf(xbp->xb_curp, left, newfmt, newstr); 4144 if (rc >= left) { 4145 if (!xo_buf_has_room(xbp, rc)) 4146 return; 4147 left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp); 4148 rc = snprintf(xbp->xb_curp, left, newfmt, newstr); 4149 } 4150 4151 if (rc > 0) { 4152 if (XOF_ISSET(xop, XOF_COLUMNS)) 4153 xop->xo_columns += rc; 4154 if (XOIF_ISSET(xop, XOIF_ANCHOR)) 4155 xop->xo_anchor_columns += rc; 4156 } 4157 } 4158 4159 } else { 4160 xo_do_format_field(xop, NULL, fmt, flen, flags); 4161 4162 /* xo_do_format_field moved curp, so we need to reset it */ 4163 rc = xbp->xb_curp - (xbp->xb_bufp + start); 4164 xbp->xb_curp = xbp->xb_bufp + start; 4165 } 4166 4167 /* If we're styling HTML, then we need to escape it */ 4168 if (xo_style(xop) == XO_STYLE_HTML) { 4169 rc = xo_escape_xml(xbp, rc, 0); 4170 } 4171 4172 if (rc > 0) 4173 xbp->xb_curp += rc; 4174 4175 move_along: 4176 if (xo_style(xop) == XO_STYLE_HTML) { 4177 xo_data_append(xop, div_close, sizeof(div_close) - 1); 4178 if (XOF_ISSET(xop, XOF_PRETTY)) 4179 xo_data_append(xop, "\n", 1); 4180 } 4181} 4182 4183static void 4184xo_format_prep (xo_handle_t *xop, xo_xff_flags_t flags) 4185{ 4186 if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST) { 4187 xo_data_append(xop, ",", 1); 4188 if (!(flags & XFF_LEAF_LIST) && XOF_ISSET(xop, XOF_PRETTY)) 4189 xo_data_append(xop, "\n", 1); 4190 } else 4191 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 4192} 4193 4194#if 0 4195/* Useful debugging function */ 4196void 4197xo_arg (xo_handle_t *xop); 4198void 4199xo_arg (xo_handle_t *xop) 4200{ 4201 xop = xo_default(xop); 4202 fprintf(stderr, "0x%x", va_arg(xop->xo_vap, unsigned)); 4203} 4204#endif /* 0 */ 4205 4206static void 4207xo_format_value (xo_handle_t *xop, const char *name, ssize_t nlen, 4208 const char *value, ssize_t vlen, 4209 const char *fmt, ssize_t flen, 4210 const char *encoding, ssize_t elen, xo_xff_flags_t flags) 4211{ 4212 int pretty = XOF_ISSET(xop, XOF_PRETTY); 4213 int quote; 4214 4215 /* 4216 * Before we emit a value, we need to know that the frame is ready. 4217 */ 4218 xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth]; 4219 4220 if (flags & XFF_LEAF_LIST) { 4221 /* 4222 * Check if we've already started to emit normal leafs 4223 * or if we're not in a leaf list. 4224 */ 4225 if ((xsp->xs_flags & (XSF_EMIT | XSF_EMIT_KEY)) 4226 || !(xsp->xs_flags & XSF_EMIT_LEAF_LIST)) { 4227 char nbuf[nlen + 1]; 4228 memcpy(nbuf, name, nlen); 4229 nbuf[nlen] = '\0'; 4230 4231 ssize_t rc = xo_transition(xop, 0, nbuf, XSS_EMIT_LEAF_LIST); 4232 if (rc < 0) 4233 flags |= XFF_DISPLAY_ONLY | XFF_ENCODE_ONLY; 4234 else 4235 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_EMIT_LEAF_LIST; 4236 } 4237 4238 xsp = &xop->xo_stack[xop->xo_depth]; 4239 if (xsp->xs_name) { 4240 name = xsp->xs_name; 4241 nlen = strlen(name); 4242 } 4243 4244 } else if (flags & XFF_KEY) { 4245 /* Emitting a 'k' (key) field */ 4246 if ((xsp->xs_flags & XSF_EMIT) && !(flags & XFF_DISPLAY_ONLY)) { 4247 xo_failure(xop, "key field emitted after normal value field: '%.*s'", 4248 nlen, name); 4249 4250 } else if (!(xsp->xs_flags & XSF_EMIT_KEY)) { 4251 char nbuf[nlen + 1]; 4252 memcpy(nbuf, name, nlen); 4253 nbuf[nlen] = '\0'; 4254 4255 ssize_t rc = xo_transition(xop, 0, nbuf, XSS_EMIT); 4256 if (rc < 0) 4257 flags |= XFF_DISPLAY_ONLY | XFF_ENCODE_ONLY; 4258 else 4259 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_EMIT_KEY; 4260 4261 xsp = &xop->xo_stack[xop->xo_depth]; 4262 xsp->xs_flags |= XSF_EMIT_KEY; 4263 } 4264 4265 } else { 4266 /* Emitting a normal value field */ 4267 if ((xsp->xs_flags & XSF_EMIT_LEAF_LIST) 4268 || !(xsp->xs_flags & XSF_EMIT)) { 4269 char nbuf[nlen + 1]; 4270 memcpy(nbuf, name, nlen); 4271 nbuf[nlen] = '\0'; 4272 4273 ssize_t rc = xo_transition(xop, 0, nbuf, XSS_EMIT); 4274 if (rc < 0) 4275 flags |= XFF_DISPLAY_ONLY | XFF_ENCODE_ONLY; 4276 else 4277 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_EMIT; 4278 4279 xsp = &xop->xo_stack[xop->xo_depth]; 4280 xsp->xs_flags |= XSF_EMIT; 4281 } 4282 } 4283 4284 xo_buffer_t *xbp = &xop->xo_data; 4285 xo_humanize_save_t save; /* Save values for humanizing logic */ 4286 4287 switch (xo_style(xop)) { 4288 case XO_STYLE_TEXT: 4289 if (flags & XFF_ENCODE_ONLY) 4290 flags |= XFF_NO_OUTPUT; 4291 4292 save.xhs_offset = xbp->xb_curp - xbp->xb_bufp; 4293 save.xhs_columns = xop->xo_columns; 4294 save.xhs_anchor_columns = xop->xo_anchor_columns; 4295 4296 xo_simple_field(xop, FALSE, value, vlen, fmt, flen, flags); 4297 4298 if (flags & XFF_HUMANIZE) 4299 xo_format_humanize(xop, xbp, &save, flags); 4300 break; 4301 4302 case XO_STYLE_HTML: 4303 if (flags & XFF_ENCODE_ONLY) 4304 flags |= XFF_NO_OUTPUT; 4305 4306 xo_buf_append_div(xop, "data", flags, name, nlen, value, vlen, 4307 fmt, flen, encoding, elen); 4308 break; 4309 4310 case XO_STYLE_XML: 4311 /* 4312 * Even though we're not making output, we still need to 4313 * let the formatting code handle the va_arg popping. 4314 */ 4315 if (flags & XFF_DISPLAY_ONLY) { 4316 xo_simple_field(xop, TRUE, value, vlen, fmt, flen, flags); 4317 break; 4318 } 4319 4320 if (encoding) { 4321 fmt = encoding; 4322 flen = elen; 4323 } else { 4324 char *enc = alloca(flen + 1); 4325 memcpy(enc, fmt, flen); 4326 enc[flen] = '\0'; 4327 fmt = xo_fix_encoding(xop, enc); 4328 flen = strlen(fmt); 4329 } 4330 4331 if (nlen == 0) { 4332 static char missing[] = "missing-field-name"; 4333 xo_failure(xop, "missing field name: %s", fmt); 4334 name = missing; 4335 nlen = sizeof(missing) - 1; 4336 } 4337 4338 if (pretty) 4339 xo_buf_indent(xop, -1); 4340 xo_data_append(xop, "<", 1); 4341 xo_data_escape(xop, name, nlen); 4342 4343 if (xop->xo_attrs.xb_curp != xop->xo_attrs.xb_bufp) { 4344 xo_data_append(xop, xop->xo_attrs.xb_bufp, 4345 xop->xo_attrs.xb_curp - xop->xo_attrs.xb_bufp); 4346 xop->xo_attrs.xb_curp = xop->xo_attrs.xb_bufp; 4347 } 4348 4349 /* 4350 * We indicate 'key' fields using the 'key' attribute. While 4351 * this is really committing the crime of mixing meta-data with 4352 * data, it's often useful. Especially when format meta-data is 4353 * difficult to come by. 4354 */ 4355 if ((flags & XFF_KEY) && XOF_ISSET(xop, XOF_KEYS)) { 4356 static char attr[] = " key=\"key\""; 4357 xo_data_append(xop, attr, sizeof(attr) - 1); 4358 } 4359 4360 /* 4361 * Save the offset at which we'd place units. See xo_format_units. 4362 */ 4363 if (XOF_ISSET(xop, XOF_UNITS)) { 4364 XOIF_SET(xop, XOIF_UNITS_PENDING); 4365 xop->xo_units_offset = xop->xo_data.xb_curp -xop->xo_data.xb_bufp; 4366 } 4367 4368 xo_data_append(xop, ">", 1); 4369 4370 xo_simple_field(xop, FALSE, value, vlen, fmt, flen, flags); 4371 4372 xo_data_append(xop, "</", 2); 4373 xo_data_escape(xop, name, nlen); 4374 xo_data_append(xop, ">", 1); 4375 if (pretty) 4376 xo_data_append(xop, "\n", 1); 4377 break; 4378 4379 case XO_STYLE_JSON: 4380 if (flags & XFF_DISPLAY_ONLY) { 4381 xo_simple_field(xop, TRUE, value, vlen, fmt, flen, flags); 4382 break; 4383 } 4384 4385 if (encoding) { 4386 fmt = encoding; 4387 flen = elen; 4388 } else { 4389 char *enc = alloca(flen + 1); 4390 memcpy(enc, fmt, flen); 4391 enc[flen] = '\0'; 4392 fmt = xo_fix_encoding(xop, enc); 4393 flen = strlen(fmt); 4394 } 4395 4396 int first = (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST) 4397 ? 0 : 1; 4398 4399 xo_format_prep(xop, flags); 4400 4401 if (flags & XFF_QUOTE) 4402 quote = 1; 4403 else if (flags & XFF_NOQUOTE) 4404 quote = 0; 4405 else if (vlen != 0) 4406 quote = 1; 4407 else if (flen == 0) { 4408 quote = 0; 4409 fmt = "true"; /* JSON encodes empty tags as a boolean true */ 4410 flen = 4; 4411 } else if (strchr("diouDOUeEfFgG", fmt[flen - 1]) == NULL) 4412 quote = 1; 4413 else 4414 quote = 0; 4415 4416 if (nlen == 0) { 4417 static char missing[] = "missing-field-name"; 4418 xo_failure(xop, "missing field name: %s", fmt); 4419 name = missing; 4420 nlen = sizeof(missing) - 1; 4421 } 4422 4423 if (flags & XFF_LEAF_LIST) { 4424 if (!first && pretty) 4425 xo_data_append(xop, "\n", 1); 4426 if (pretty) 4427 xo_buf_indent(xop, -1); 4428 } else { 4429 if (pretty) 4430 xo_buf_indent(xop, -1); 4431 xo_data_append(xop, "\"", 1); 4432 4433 xbp = &xop->xo_data; 4434 ssize_t off = xbp->xb_curp - xbp->xb_bufp; 4435 4436 xo_data_escape(xop, name, nlen); 4437 4438 if (XOF_ISSET(xop, XOF_UNDERSCORES)) { 4439 ssize_t coff = xbp->xb_curp - xbp->xb_bufp; 4440 for ( ; off < coff; off++) 4441 if (xbp->xb_bufp[off] == '-') 4442 xbp->xb_bufp[off] = '_'; 4443 } 4444 xo_data_append(xop, "\":", 2); 4445 if (pretty) 4446 xo_data_append(xop, " ", 1); 4447 } 4448 4449 if (quote) 4450 xo_data_append(xop, "\"", 1); 4451 4452 xo_simple_field(xop, FALSE, value, vlen, fmt, flen, flags); 4453 4454 if (quote) 4455 xo_data_append(xop, "\"", 1); 4456 break; 4457 4458 case XO_STYLE_SDPARAMS: 4459 if (flags & XFF_DISPLAY_ONLY) { 4460 xo_simple_field(xop, TRUE, value, vlen, fmt, flen, flags); 4461 break; 4462 } 4463 4464 if (encoding) { 4465 fmt = encoding; 4466 flen = elen; 4467 } else { 4468 char *enc = alloca(flen + 1); 4469 memcpy(enc, fmt, flen); 4470 enc[flen] = '\0'; 4471 fmt = xo_fix_encoding(xop, enc); 4472 flen = strlen(fmt); 4473 } 4474 4475 if (nlen == 0) { 4476 static char missing[] = "missing-field-name"; 4477 xo_failure(xop, "missing field name: %s", fmt); 4478 name = missing; 4479 nlen = sizeof(missing) - 1; 4480 } 4481 4482 xo_data_escape(xop, name, nlen); 4483 xo_data_append(xop, "=\"", 2); 4484 4485 xo_simple_field(xop, FALSE, value, vlen, fmt, flen, flags); 4486 4487 xo_data_append(xop, "\" ", 2); 4488 break; 4489 4490 case XO_STYLE_ENCODER: 4491 if (flags & XFF_DISPLAY_ONLY) { 4492 xo_simple_field(xop, TRUE, value, vlen, fmt, flen, flags); 4493 break; 4494 } 4495 4496 if (flags & XFF_QUOTE) 4497 quote = 1; 4498 else if (flags & XFF_NOQUOTE) 4499 quote = 0; 4500 else if (flen == 0) { 4501 quote = 0; 4502 fmt = "true"; /* JSON encodes empty tags as a boolean true */ 4503 flen = 4; 4504 } else if (strchr("diouxXDOUeEfFgGaAcCp", fmt[flen - 1]) == NULL) 4505 quote = 1; 4506 else 4507 quote = 0; 4508 4509 if (encoding) { 4510 fmt = encoding; 4511 flen = elen; 4512 } else { 4513 char *enc = alloca(flen + 1); 4514 memcpy(enc, fmt, flen); 4515 enc[flen] = '\0'; 4516 fmt = xo_fix_encoding(xop, enc); 4517 flen = strlen(fmt); 4518 } 4519 4520 if (nlen == 0) { 4521 static char missing[] = "missing-field-name"; 4522 xo_failure(xop, "missing field name: %s", fmt); 4523 name = missing; 4524 nlen = sizeof(missing) - 1; 4525 } 4526 4527 ssize_t name_offset = xo_buf_offset(&xop->xo_data); 4528 xo_data_append(xop, name, nlen); 4529 xo_data_append(xop, "", 1); 4530 4531 ssize_t value_offset = xo_buf_offset(&xop->xo_data); 4532 4533 xo_simple_field(xop, FALSE, value, vlen, fmt, flen, flags); 4534 4535 xo_data_append(xop, "", 1); 4536 4537 xo_encoder_handle(xop, quote ? XO_OP_STRING : XO_OP_CONTENT, 4538 xo_buf_data(&xop->xo_data, name_offset), 4539 xo_buf_data(&xop->xo_data, value_offset), flags); 4540 xo_buf_reset(&xop->xo_data); 4541 break; 4542 } 4543} 4544 4545static void 4546xo_set_gettext_domain (xo_handle_t *xop, xo_field_info_t *xfip, 4547 const char *str, ssize_t len) 4548{ 4549 const char *fmt = xfip->xfi_format; 4550 ssize_t flen = xfip->xfi_flen; 4551 4552 /* Start by discarding previous domain */ 4553 if (xop->xo_gt_domain) { 4554 xo_free(xop->xo_gt_domain); 4555 xop->xo_gt_domain = NULL; 4556 } 4557 4558 /* An empty {G:} means no domainname */ 4559 if (len == 0 && flen == 0) 4560 return; 4561 4562 ssize_t start_offset = -1; 4563 if (len == 0 && flen != 0) { 4564 /* Need to do format the data to get the domainname from args */ 4565 start_offset = xop->xo_data.xb_curp - xop->xo_data.xb_bufp; 4566 xo_do_format_field(xop, NULL, fmt, flen, 0); 4567 4568 ssize_t end_offset = xop->xo_data.xb_curp - xop->xo_data.xb_bufp; 4569 len = end_offset - start_offset; 4570 str = xop->xo_data.xb_bufp + start_offset; 4571 } 4572 4573 xop->xo_gt_domain = xo_strndup(str, len); 4574 4575 /* Reset the current buffer point to avoid emitting the name as output */ 4576 if (start_offset >= 0) 4577 xop->xo_data.xb_curp = xop->xo_data.xb_bufp + start_offset; 4578} 4579 4580static void 4581xo_format_content (xo_handle_t *xop, const char *class_name, 4582 const char *tag_name, 4583 const char *value, ssize_t vlen, 4584 const char *fmt, ssize_t flen, 4585 xo_xff_flags_t flags) 4586{ 4587 switch (xo_style(xop)) { 4588 case XO_STYLE_TEXT: 4589 xo_simple_field(xop, FALSE, value, vlen, fmt, flen, flags); 4590 break; 4591 4592 case XO_STYLE_HTML: 4593 xo_buf_append_div(xop, class_name, flags, NULL, 0, 4594 value, vlen, fmt, flen, NULL, 0); 4595 break; 4596 4597 case XO_STYLE_XML: 4598 case XO_STYLE_JSON: 4599 case XO_STYLE_SDPARAMS: 4600 if (tag_name) { 4601 xo_open_container_h(xop, tag_name); 4602 xo_format_value(xop, "message", 7, value, vlen, 4603 fmt, flen, NULL, 0, flags); 4604 xo_close_container_h(xop, tag_name); 4605 4606 } else { 4607 /* 4608 * Even though we don't care about labels, we need to do 4609 * enough parsing work to skip over the right bits of xo_vap. 4610 */ 4611 xo_simple_field(xop, TRUE, value, vlen, fmt, flen, flags); 4612 } 4613 break; 4614 4615 case XO_STYLE_ENCODER: 4616 xo_simple_field(xop, TRUE, value, vlen, fmt, flen, flags); 4617 break; 4618 } 4619} 4620 4621static const char *xo_color_names[] = { 4622 "default", /* XO_COL_DEFAULT */ 4623 "black", /* XO_COL_BLACK */ 4624 "red", /* XO_CLOR_RED */ 4625 "green", /* XO_COL_GREEN */ 4626 "yellow", /* XO_COL_YELLOW */ 4627 "blue", /* XO_COL_BLUE */ 4628 "magenta", /* XO_COL_MAGENTA */ 4629 "cyan", /* XO_COL_CYAN */ 4630 "white", /* XO_COL_WHITE */ 4631 NULL 4632}; 4633 4634static int 4635xo_color_find (const char *str) 4636{ 4637 int i; 4638 4639 for (i = 0; xo_color_names[i]; i++) { 4640 if (strcmp(xo_color_names[i], str) == 0) 4641 return i; 4642 } 4643 4644 return -1; 4645} 4646 4647static const char *xo_effect_names[] = { 4648 "reset", /* XO_EFF_RESET */ 4649 "normal", /* XO_EFF_NORMAL */ 4650 "bold", /* XO_EFF_BOLD */ 4651 "underline", /* XO_EFF_UNDERLINE */ 4652 "inverse", /* XO_EFF_INVERSE */ 4653 NULL 4654}; 4655 4656static const char *xo_effect_on_codes[] = { 4657 "0", /* XO_EFF_RESET */ 4658 "0", /* XO_EFF_NORMAL */ 4659 "1", /* XO_EFF_BOLD */ 4660 "4", /* XO_EFF_UNDERLINE */ 4661 "7", /* XO_EFF_INVERSE */ 4662 NULL 4663}; 4664 4665#if 0 4666/* 4667 * See comment below re: joy of terminal standards. These can 4668 * be use by just adding: 4669 * + if (newp->xoc_effects & bit) 4670 * code = xo_effect_on_codes[i]; 4671 * + else 4672 * + code = xo_effect_off_codes[i]; 4673 * in xo_color_handle_text. 4674 */ 4675static const char *xo_effect_off_codes[] = { 4676 "0", /* XO_EFF_RESET */ 4677 "0", /* XO_EFF_NORMAL */ 4678 "21", /* XO_EFF_BOLD */ 4679 "24", /* XO_EFF_UNDERLINE */ 4680 "27", /* XO_EFF_INVERSE */ 4681 NULL 4682}; 4683#endif /* 0 */ 4684 4685static int 4686xo_effect_find (const char *str) 4687{ 4688 int i; 4689 4690 for (i = 0; xo_effect_names[i]; i++) { 4691 if (strcmp(xo_effect_names[i], str) == 0) 4692 return i; 4693 } 4694 4695 return -1; 4696} 4697 4698static void 4699xo_colors_parse (xo_handle_t *xop, xo_colors_t *xocp, char *str) 4700{ 4701#ifdef LIBXO_TEXT_ONLY 4702 return; 4703#endif /* LIBXO_TEXT_ONLY */ 4704 4705 char *cp, *ep, *np, *xp; 4706 ssize_t len = strlen(str); 4707 int rc; 4708 4709 /* 4710 * Possible tokens: colors, bg-colors, effects, no-effects, "reset". 4711 */ 4712 for (cp = str, ep = cp + len - 1; cp && cp < ep; cp = np) { 4713 /* Trim leading whitespace */ 4714 while (isspace((int) *cp)) 4715 cp += 1; 4716 4717 np = strchr(cp, ','); 4718 if (np) 4719 *np++ = '\0'; 4720 4721 /* Trim trailing whitespace */ 4722 xp = cp + strlen(cp) - 1; 4723 while (isspace(*xp) && xp > cp) 4724 *xp-- = '\0'; 4725 4726 if (cp[0] == 'f' && cp[1] == 'g' && cp[2] == '-') { 4727 rc = xo_color_find(cp + 3); 4728 if (rc < 0) 4729 goto unknown; 4730 4731 xocp->xoc_col_fg = rc; 4732 4733 } else if (cp[0] == 'b' && cp[1] == 'g' && cp[2] == '-') { 4734 rc = xo_color_find(cp + 3); 4735 if (rc < 0) 4736 goto unknown; 4737 xocp->xoc_col_bg = rc; 4738 4739 } else if (cp[0] == 'n' && cp[1] == 'o' && cp[2] == '-') { 4740 rc = xo_effect_find(cp + 3); 4741 if (rc < 0) 4742 goto unknown; 4743 xocp->xoc_effects &= ~(1 << rc); 4744 4745 } else { 4746 rc = xo_effect_find(cp); 4747 if (rc < 0) 4748 goto unknown; 4749 xocp->xoc_effects |= 1 << rc; 4750 4751 switch (1 << rc) { 4752 case XO_EFF_RESET: 4753 xocp->xoc_col_fg = xocp->xoc_col_bg = 0; 4754 /* Note: not "|=" since we want to wipe out the old value */ 4755 xocp->xoc_effects = XO_EFF_RESET; 4756 break; 4757 4758 case XO_EFF_NORMAL: 4759 xocp->xoc_effects &= ~(XO_EFF_BOLD | XO_EFF_UNDERLINE 4760 | XO_EFF_INVERSE | XO_EFF_NORMAL); 4761 break; 4762 } 4763 } 4764 continue; 4765 4766 unknown: 4767 if (XOF_ISSET(xop, XOF_WARN)) 4768 xo_failure(xop, "unknown color/effect string detected: '%s'", cp); 4769 } 4770} 4771 4772static inline int 4773xo_colors_enabled (xo_handle_t *xop UNUSED) 4774{ 4775#ifdef LIBXO_TEXT_ONLY 4776 return 0; 4777#else /* LIBXO_TEXT_ONLY */ 4778 return XOF_ISSET(xop, XOF_COLOR); 4779#endif /* LIBXO_TEXT_ONLY */ 4780} 4781 4782/* 4783 * If the color map is in use (--libxo colors=xxxx), then update 4784 * the incoming foreground and background colors from the map. 4785 */ 4786static void 4787xo_colors_update (xo_handle_t *xop, xo_colors_t *newp) 4788{ 4789#ifdef LIBXO_TEXT_ONLY 4790 return; 4791#endif /* LIBXO_TEXT_ONLY */ 4792 4793 xo_color_t fg = newp->xoc_col_fg; 4794 if (XOF_ISSET(xop, XOF_COLOR_MAP) && fg < XO_NUM_COLORS) 4795 fg = xop->xo_color_map_fg[fg]; /* Fetch from color map */ 4796 newp->xoc_col_fg = fg; 4797 4798 xo_color_t bg = newp->xoc_col_bg; 4799 if (XOF_ISSET(xop, XOF_COLOR_MAP) && bg < XO_NUM_COLORS) 4800 bg = xop->xo_color_map_bg[bg]; /* Fetch from color map */ 4801 newp->xoc_col_bg = bg; 4802} 4803 4804static void 4805xo_colors_handle_text (xo_handle_t *xop, xo_colors_t *newp) 4806{ 4807 char buf[BUFSIZ]; 4808 char *cp = buf, *ep = buf + sizeof(buf); 4809 unsigned i, bit; 4810 xo_colors_t *oldp = &xop->xo_colors; 4811 const char *code = NULL; 4812 4813 /* 4814 * Start the buffer with an escape. We don't want to add the '[' 4815 * now, since we let xo_effect_text_add unconditionally add the ';'. 4816 * We'll replace the first ';' with a '[' when we're done. 4817 */ 4818 *cp++ = 0x1b; /* Escape */ 4819 4820 /* 4821 * Terminals were designed back in the age before "certainty" was 4822 * invented, when standards were more what you'd call "guidelines" 4823 * than actual rules. Anyway we can't depend on them to operate 4824 * correctly. So when display attributes are changed, we punt, 4825 * reseting them all and turning back on the ones we want to keep. 4826 * Longer, but should be completely reliable. Savvy? 4827 */ 4828 if (oldp->xoc_effects != (newp->xoc_effects & oldp->xoc_effects)) { 4829 newp->xoc_effects |= XO_EFF_RESET; 4830 oldp->xoc_effects = 0; 4831 } 4832 4833 for (i = 0, bit = 1; xo_effect_names[i]; i++, bit <<= 1) { 4834 if ((newp->xoc_effects & bit) == (oldp->xoc_effects & bit)) 4835 continue; 4836 4837 code = xo_effect_on_codes[i]; 4838 4839 cp += snprintf(cp, ep - cp, ";%s", code); 4840 if (cp >= ep) 4841 return; /* Should not occur */ 4842 4843 if (bit == XO_EFF_RESET) { 4844 /* Mark up the old value so we can detect current values as new */ 4845 oldp->xoc_effects = 0; 4846 oldp->xoc_col_fg = oldp->xoc_col_bg = XO_COL_DEFAULT; 4847 } 4848 } 4849 4850 xo_color_t fg = newp->xoc_col_fg; 4851 if (fg != oldp->xoc_col_fg) { 4852 cp += snprintf(cp, ep - cp, ";3%u", 4853 (fg != XO_COL_DEFAULT) ? fg - 1 : 9); 4854 } 4855 4856 xo_color_t bg = newp->xoc_col_bg; 4857 if (bg != oldp->xoc_col_bg) { 4858 cp += snprintf(cp, ep - cp, ";4%u", 4859 (bg != XO_COL_DEFAULT) ? bg - 1 : 9); 4860 } 4861 4862 if (cp - buf != 1 && cp < ep - 3) { 4863 buf[1] = '['; /* Overwrite leading ';' */ 4864 *cp++ = 'm'; 4865 *cp = '\0'; 4866 xo_buf_append(&xop->xo_data, buf, cp - buf); 4867 } 4868} 4869 4870static void 4871xo_colors_handle_html (xo_handle_t *xop, xo_colors_t *newp) 4872{ 4873 xo_colors_t *oldp = &xop->xo_colors; 4874 4875 /* 4876 * HTML colors are mostly trivial: fill in xo_color_buf with 4877 * a set of class tags representing the colors and effects. 4878 */ 4879 4880 /* If nothing changed, then do nothing */ 4881 if (oldp->xoc_effects == newp->xoc_effects 4882 && oldp->xoc_col_fg == newp->xoc_col_fg 4883 && oldp->xoc_col_bg == newp->xoc_col_bg) 4884 return; 4885 4886 unsigned i, bit; 4887 xo_buffer_t *xbp = &xop->xo_color_buf; 4888 4889 xo_buf_reset(xbp); /* We rebuild content after each change */ 4890 4891 for (i = 0, bit = 1; xo_effect_names[i]; i++, bit <<= 1) { 4892 if (!(newp->xoc_effects & bit)) 4893 continue; 4894 4895 xo_buf_append_str(xbp, " effect-"); 4896 xo_buf_append_str(xbp, xo_effect_names[i]); 4897 } 4898 4899 const char *fg = NULL; 4900 const char *bg = NULL; 4901 4902 if (newp->xoc_col_fg != XO_COL_DEFAULT) 4903 fg = xo_color_names[newp->xoc_col_fg]; 4904 if (newp->xoc_col_bg != XO_COL_DEFAULT) 4905 bg = xo_color_names[newp->xoc_col_bg]; 4906 4907 if (newp->xoc_effects & XO_EFF_INVERSE) { 4908 const char *tmp = fg; 4909 fg = bg; 4910 bg = tmp; 4911 if (fg == NULL) 4912 fg = "inverse"; 4913 if (bg == NULL) 4914 bg = "inverse"; 4915 4916 } 4917 4918 if (fg) { 4919 xo_buf_append_str(xbp, " color-fg-"); 4920 xo_buf_append_str(xbp, fg); 4921 } 4922 4923 if (bg) { 4924 xo_buf_append_str(xbp, " color-bg-"); 4925 xo_buf_append_str(xbp, bg); 4926 } 4927} 4928 4929static void 4930xo_format_colors (xo_handle_t *xop, xo_field_info_t *xfip, 4931 const char *value, ssize_t vlen) 4932{ 4933 const char *fmt = xfip->xfi_format; 4934 ssize_t flen = xfip->xfi_flen; 4935 4936 xo_buffer_t xb; 4937 4938 /* If the string is static and we've in an encoding style, bail */ 4939 if (vlen != 0 && xo_style_is_encoding(xop)) 4940 return; 4941 4942 xo_buf_init(&xb); 4943 4944 if (vlen) 4945 xo_buf_append(&xb, value, vlen); 4946 else if (flen) 4947 xo_do_format_field(xop, &xb, fmt, flen, 0); 4948 else 4949 xo_buf_append(&xb, "reset", 6); /* Default if empty */ 4950 4951 if (xo_colors_enabled(xop)) { 4952 switch (xo_style(xop)) { 4953 case XO_STYLE_TEXT: 4954 case XO_STYLE_HTML: 4955 xo_buf_append(&xb, "", 1); 4956 4957 xo_colors_t xoc = xop->xo_colors; 4958 xo_colors_parse(xop, &xoc, xb.xb_bufp); 4959 xo_colors_update(xop, &xoc); 4960 4961 if (xo_style(xop) == XO_STYLE_TEXT) { 4962 /* 4963 * Text mode means emitting the colors as ANSI character 4964 * codes. This will allow people who like colors to have 4965 * colors. The issue is, of course conflicting with the 4966 * user's perfectly reasonable color scheme. Which leads 4967 * to the hell of LSCOLORS, where even app need to have 4968 * customization hooks for adjusting colors. Instead we 4969 * provide a simpler-but-still-annoying answer where one 4970 * can map colors to other colors. 4971 */ 4972 xo_colors_handle_text(xop, &xoc); 4973 xoc.xoc_effects &= ~XO_EFF_RESET; /* After handling it */ 4974 4975 } else { 4976 /* 4977 * HTML output is wrapped in divs, so the color information 4978 * must appear in every div until cleared. Most pathetic. 4979 * Most unavoidable. 4980 */ 4981 xoc.xoc_effects &= ~XO_EFF_RESET; /* Before handling effects */ 4982 xo_colors_handle_html(xop, &xoc); 4983 } 4984 4985 xop->xo_colors = xoc; 4986 break; 4987 4988 case XO_STYLE_XML: 4989 case XO_STYLE_JSON: 4990 case XO_STYLE_SDPARAMS: 4991 case XO_STYLE_ENCODER: 4992 /* 4993 * Nothing to do; we did all that work just to clear the stack of 4994 * formatting arguments. 4995 */ 4996 break; 4997 } 4998 } 4999 5000 xo_buf_cleanup(&xb); 5001} 5002 5003static void 5004xo_format_units (xo_handle_t *xop, xo_field_info_t *xfip, 5005 const char *value, ssize_t vlen) 5006{ 5007 const char *fmt = xfip->xfi_format; 5008 ssize_t flen = xfip->xfi_flen; 5009 xo_xff_flags_t flags = xfip->xfi_flags; 5010 5011 static char units_start_xml[] = " units=\""; 5012 static char units_start_html[] = " data-units=\""; 5013 5014 if (!XOIF_ISSET(xop, XOIF_UNITS_PENDING)) { 5015 xo_format_content(xop, "units", NULL, value, vlen, fmt, flen, flags); 5016 return; 5017 } 5018 5019 xo_buffer_t *xbp = &xop->xo_data; 5020 ssize_t start = xop->xo_units_offset; 5021 ssize_t stop = xbp->xb_curp - xbp->xb_bufp; 5022 5023 if (xo_style(xop) == XO_STYLE_XML) 5024 xo_buf_append(xbp, units_start_xml, sizeof(units_start_xml) - 1); 5025 else if (xo_style(xop) == XO_STYLE_HTML) 5026 xo_buf_append(xbp, units_start_html, sizeof(units_start_html) - 1); 5027 else 5028 return; 5029 5030 if (vlen) 5031 xo_data_escape(xop, value, vlen); 5032 else 5033 xo_do_format_field(xop, NULL, fmt, flen, flags); 5034 5035 xo_buf_append(xbp, "\"", 1); 5036 5037 ssize_t now = xbp->xb_curp - xbp->xb_bufp; 5038 ssize_t delta = now - stop; 5039 if (delta <= 0) { /* Strange; no output to move */ 5040 xbp->xb_curp = xbp->xb_bufp + stop; /* Reset buffer to prior state */ 5041 return; 5042 } 5043 5044 /* 5045 * Now we're in it alright. We've need to insert the unit value 5046 * we just created into the right spot. We make a local copy, 5047 * move it and then insert our copy. We know there's room in the 5048 * buffer, since we're just moving this around. 5049 */ 5050 char *buf = alloca(delta); 5051 5052 memcpy(buf, xbp->xb_bufp + stop, delta); 5053 memmove(xbp->xb_bufp + start + delta, xbp->xb_bufp + start, stop - start); 5054 memmove(xbp->xb_bufp + start, buf, delta); 5055} 5056 5057static ssize_t 5058xo_find_width (xo_handle_t *xop, xo_field_info_t *xfip, 5059 const char *value, ssize_t vlen) 5060{ 5061 const char *fmt = xfip->xfi_format; 5062 ssize_t flen = xfip->xfi_flen; 5063 5064 long width = 0; 5065 char *bp; 5066 char *cp; 5067 5068 if (vlen) { 5069 bp = alloca(vlen + 1); /* Make local NUL-terminated copy of value */ 5070 memcpy(bp, value, vlen); 5071 bp[vlen] = '\0'; 5072 5073 width = strtol(bp, &cp, 0); 5074 if (width == LONG_MIN || width == LONG_MAX || bp == cp || *cp != '\0') { 5075 width = 0; 5076 xo_failure(xop, "invalid width for anchor: '%s'", bp); 5077 } 5078 } else if (flen) { 5079 /* 5080 * We really expect the format for width to be "{:/%d}" or 5081 * "{:/%u}", so if that's the case, we just grab our width off 5082 * the argument list. But we need to avoid optimized logic if 5083 * there's a custom formatter. 5084 */ 5085 if (xop->xo_formatter == NULL && flen == 2 5086 && strncmp("%d", fmt, flen) == 0) { 5087 if (!XOF_ISSET(xop, XOF_NO_VA_ARG)) 5088 width = va_arg(xop->xo_vap, int); 5089 } else if (xop->xo_formatter == NULL && flen == 2 5090 && strncmp("%u", fmt, flen) == 0) { 5091 if (!XOF_ISSET(xop, XOF_NO_VA_ARG)) 5092 width = va_arg(xop->xo_vap, unsigned); 5093 } else { 5094 /* 5095 * So we have a format and it's not a simple one like 5096 * "{:/%d}". That means we need to format the field, 5097 * extract the value from the formatted output, and then 5098 * discard that output. 5099 */ 5100 int anchor_was_set = FALSE; 5101 xo_buffer_t *xbp = &xop->xo_data; 5102 ssize_t start_offset = xo_buf_offset(xbp); 5103 bp = xo_buf_cur(xbp); /* Save start of the string */ 5104 cp = NULL; 5105 5106 if (XOIF_ISSET(xop, XOIF_ANCHOR)) { 5107 XOIF_CLEAR(xop, XOIF_ANCHOR); 5108 anchor_was_set = TRUE; 5109 } 5110 5111 ssize_t rc = xo_do_format_field(xop, xbp, fmt, flen, 0); 5112 if (rc >= 0) { 5113 xo_buf_append(xbp, "", 1); /* Append a NUL */ 5114 5115 width = strtol(bp, &cp, 0); 5116 if (width == LONG_MIN || width == LONG_MAX 5117 || bp == cp || *cp != '\0') { 5118 width = 0; 5119 xo_failure(xop, "invalid width for anchor: '%s'", bp); 5120 } 5121 } 5122 5123 /* Reset the cur pointer to where we found it */ 5124 xbp->xb_curp = xbp->xb_bufp + start_offset; 5125 if (anchor_was_set) 5126 XOIF_SET(xop, XOIF_ANCHOR); 5127 } 5128 } 5129 5130 return width; 5131} 5132 5133static void 5134xo_anchor_clear (xo_handle_t *xop) 5135{ 5136 XOIF_CLEAR(xop, XOIF_ANCHOR); 5137 xop->xo_anchor_offset = 0; 5138 xop->xo_anchor_columns = 0; 5139 xop->xo_anchor_min_width = 0; 5140} 5141 5142/* 5143 * An anchor is a marker used to delay field width implications. 5144 * Imagine the format string "{[:10}{min:%d}/{cur:%d}/{max:%d}{:]}". 5145 * We are looking for output like " 1/4/5" 5146 * 5147 * To make this work, we record the anchor and then return to 5148 * format it when the end anchor tag is seen. 5149 */ 5150static void 5151xo_anchor_start (xo_handle_t *xop, xo_field_info_t *xfip, 5152 const char *value, ssize_t vlen) 5153{ 5154 if (XOIF_ISSET(xop, XOIF_ANCHOR)) 5155 xo_failure(xop, "the anchor already recording is discarded"); 5156 5157 XOIF_SET(xop, XOIF_ANCHOR); 5158 xo_buffer_t *xbp = &xop->xo_data; 5159 xop->xo_anchor_offset = xbp->xb_curp - xbp->xb_bufp; 5160 xop->xo_anchor_columns = 0; 5161 5162 /* 5163 * Now we find the width, if possible. If it's not there, 5164 * we'll get it on the end anchor. 5165 */ 5166 xop->xo_anchor_min_width = xo_find_width(xop, xfip, value, vlen); 5167} 5168 5169static void 5170xo_anchor_stop (xo_handle_t *xop, xo_field_info_t *xfip, 5171 const char *value, ssize_t vlen) 5172{ 5173 if (!XOIF_ISSET(xop, XOIF_ANCHOR)) { 5174 xo_failure(xop, "no start anchor"); 5175 return; 5176 } 5177 5178 XOIF_CLEAR(xop, XOIF_UNITS_PENDING); 5179 5180 ssize_t width = xo_find_width(xop, xfip, value, vlen); 5181 if (width == 0) 5182 width = xop->xo_anchor_min_width; 5183 5184 if (width == 0) /* No width given; nothing to do */ 5185 goto done; 5186 5187 xo_buffer_t *xbp = &xop->xo_data; 5188 ssize_t start = xop->xo_anchor_offset; 5189 ssize_t stop = xbp->xb_curp - xbp->xb_bufp; 5190 ssize_t abswidth = (width > 0) ? width : -width; 5191 ssize_t blen = abswidth - xop->xo_anchor_columns; 5192 5193 if (blen <= 0) /* Already over width */ 5194 goto done; 5195 5196 if (abswidth > XO_MAX_ANCHOR_WIDTH) { 5197 xo_failure(xop, "width over %u are not supported", 5198 XO_MAX_ANCHOR_WIDTH); 5199 goto done; 5200 } 5201 5202 /* Make a suitable padding field and emit it */ 5203 char *buf = alloca(blen); 5204 memset(buf, ' ', blen); 5205 xo_format_content(xop, "padding", NULL, buf, blen, NULL, 0, 0); 5206 5207 if (width < 0) /* Already left justified */ 5208 goto done; 5209 5210 ssize_t now = xbp->xb_curp - xbp->xb_bufp; 5211 ssize_t delta = now - stop; 5212 if (delta <= 0) /* Strange; no output to move */ 5213 goto done; 5214 5215 /* 5216 * Now we're in it alright. We've need to insert the padding data 5217 * we just created (which might be an HTML <div> or text) before 5218 * the formatted data. We make a local copy, move it and then 5219 * insert our copy. We know there's room in the buffer, since 5220 * we're just moving this around. 5221 */ 5222 if (delta > blen) 5223 buf = alloca(delta); /* Expand buffer if needed */ 5224 5225 memcpy(buf, xbp->xb_bufp + stop, delta); 5226 memmove(xbp->xb_bufp + start + delta, xbp->xb_bufp + start, stop - start); 5227 memmove(xbp->xb_bufp + start, buf, delta); 5228 5229 done: 5230 xo_anchor_clear(xop); 5231} 5232 5233static const char * 5234xo_class_name (int ftype) 5235{ 5236 switch (ftype) { 5237 case 'D': return "decoration"; 5238 case 'E': return "error"; 5239 case 'L': return "label"; 5240 case 'N': return "note"; 5241 case 'P': return "padding"; 5242 case 'W': return "warning"; 5243 } 5244 5245 return NULL; 5246} 5247 5248static const char * 5249xo_tag_name (int ftype) 5250{ 5251 switch (ftype) { 5252 case 'E': return "__error"; 5253 case 'W': return "__warning"; 5254 } 5255 5256 return NULL; 5257} 5258 5259static int 5260xo_role_wants_default_format (int ftype) 5261{ 5262 switch (ftype) { 5263 /* These roles can be completely empty and/or without formatting */ 5264 case 'C': 5265 case 'G': 5266 case '[': 5267 case ']': 5268 return 0; 5269 } 5270 5271 return 1; 5272} 5273 5274static xo_mapping_t xo_role_names[] = { 5275 { 'C', "color" }, 5276 { 'D', "decoration" }, 5277 { 'E', "error" }, 5278 { 'L', "label" }, 5279 { 'N', "note" }, 5280 { 'P', "padding" }, 5281 { 'T', "title" }, 5282 { 'U', "units" }, 5283 { 'V', "value" }, 5284 { 'W', "warning" }, 5285 { '[', "start-anchor" }, 5286 { ']', "stop-anchor" }, 5287 { 0, NULL } 5288}; 5289 5290#define XO_ROLE_EBRACE '{' /* Escaped braces */ 5291#define XO_ROLE_TEXT '+' 5292#define XO_ROLE_NEWLINE '\n' 5293 5294static xo_mapping_t xo_modifier_names[] = { 5295 { XFF_ARGUMENT, "argument" }, 5296 { XFF_COLON, "colon" }, 5297 { XFF_COMMA, "comma" }, 5298 { XFF_DISPLAY_ONLY, "display" }, 5299 { XFF_ENCODE_ONLY, "encoding" }, 5300 { XFF_GT_FIELD, "gettext" }, 5301 { XFF_HUMANIZE, "humanize" }, 5302 { XFF_HUMANIZE, "hn" }, 5303 { XFF_HN_SPACE, "hn-space" }, 5304 { XFF_HN_DECIMAL, "hn-decimal" }, 5305 { XFF_HN_1000, "hn-1000" }, 5306 { XFF_KEY, "key" }, 5307 { XFF_LEAF_LIST, "leaf-list" }, 5308 { XFF_LEAF_LIST, "list" }, 5309 { XFF_NOQUOTE, "no-quotes" }, 5310 { XFF_NOQUOTE, "no-quote" }, 5311 { XFF_GT_PLURAL, "plural" }, 5312 { XFF_QUOTE, "quotes" }, 5313 { XFF_QUOTE, "quote" }, 5314 { XFF_TRIM_WS, "trim" }, 5315 { XFF_WS, "white" }, 5316 { 0, NULL } 5317}; 5318 5319#ifdef NOT_NEEDED_YET 5320static xo_mapping_t xo_modifier_short_names[] = { 5321 { XFF_COLON, "c" }, 5322 { XFF_DISPLAY_ONLY, "d" }, 5323 { XFF_ENCODE_ONLY, "e" }, 5324 { XFF_GT_FIELD, "g" }, 5325 { XFF_HUMANIZE, "h" }, 5326 { XFF_KEY, "k" }, 5327 { XFF_LEAF_LIST, "l" }, 5328 { XFF_NOQUOTE, "n" }, 5329 { XFF_GT_PLURAL, "p" }, 5330 { XFF_QUOTE, "q" }, 5331 { XFF_TRIM_WS, "t" }, 5332 { XFF_WS, "w" }, 5333 { 0, NULL } 5334}; 5335#endif /* NOT_NEEDED_YET */ 5336 5337static int 5338xo_count_fields (xo_handle_t *xop UNUSED, const char *fmt) 5339{ 5340 int rc = 1; 5341 const char *cp; 5342 5343 for (cp = fmt; *cp; cp++) 5344 if (*cp == '{' || *cp == '\n') 5345 rc += 1; 5346 5347 return rc * 2 + 1; 5348} 5349 5350/* 5351 * The field format is: 5352 * '{' modifiers ':' content [ '/' print-fmt [ '/' encode-fmt ]] '}' 5353 * Roles are optional and include the following field types: 5354 * 'D': decoration; something non-text and non-data (colons, commmas) 5355 * 'E': error message 5356 * 'G': gettext() the entire string; optional domainname as content 5357 * 'L': label; text preceding data 5358 * 'N': note; text following data 5359 * 'P': padding; whitespace 5360 * 'T': Title, where 'content' is a column title 5361 * 'U': Units, where 'content' is the unit label 5362 * 'V': value, where 'content' is the name of the field (the default) 5363 * 'W': warning message 5364 * '[': start a section of anchored text 5365 * ']': end a section of anchored text 5366 * The following modifiers are also supported: 5367 * 'a': content is provided via argument (const char *), not descriptor 5368 * 'c': flag: emit a colon after the label 5369 * 'd': field is only emitted for display styles (text and html) 5370 * 'e': field is only emitted for encoding styles (xml and json) 5371 * 'g': gettext() the field 5372 * 'h': humanize a numeric value (only for display styles) 5373 * 'k': this field is a key, suitable for XPath predicates 5374 * 'l': a leaf-list, a simple list of values 5375 * 'n': no quotes around this field 5376 * 'p': the field has plural gettext semantics (ngettext) 5377 * 'q': add quotes around this field 5378 * 't': trim whitespace around the value 5379 * 'w': emit a blank after the label 5380 * The print-fmt and encode-fmt strings is the printf-style formating 5381 * for this data. JSON and XML will use the encoding-fmt, if present. 5382 * If the encode-fmt is not provided, it defaults to the print-fmt. 5383 * If the print-fmt is not provided, it defaults to 's'. 5384 */ 5385static const char * 5386xo_parse_roles (xo_handle_t *xop, const char *fmt, 5387 const char *basep, xo_field_info_t *xfip) 5388{ 5389 const char *sp; 5390 unsigned ftype = 0; 5391 xo_xff_flags_t flags = 0; 5392 uint8_t fnum = 0; 5393 5394 for (sp = basep; sp && *sp; sp++) { 5395 if (*sp == ':' || *sp == '/' || *sp == '}') 5396 break; 5397 5398 if (*sp == '\\') { 5399 if (sp[1] == '\0') { 5400 xo_failure(xop, "backslash at the end of string"); 5401 return NULL; 5402 } 5403 5404 /* Anything backslashed is ignored */ 5405 sp += 1; 5406 continue; 5407 } 5408 5409 if (*sp == ',') { 5410 const char *np; 5411 for (np = ++sp; *np; np++) 5412 if (*np == ':' || *np == '/' || *np == '}' || *np == ',') 5413 break; 5414 5415 ssize_t slen = np - sp; 5416 if (slen > 0) { 5417 xo_xff_flags_t value; 5418 5419 value = xo_name_lookup(xo_role_names, sp, slen); 5420 if (value) 5421 ftype = value; 5422 else { 5423 value = xo_name_lookup(xo_modifier_names, sp, slen); 5424 if (value) 5425 flags |= value; 5426 else 5427 xo_failure(xop, "unknown keyword ignored: '%.*s'", 5428 slen, sp); 5429 } 5430 } 5431 5432 sp = np - 1; 5433 continue; 5434 } 5435 5436 switch (*sp) { 5437 case 'C': 5438 case 'D': 5439 case 'E': 5440 case 'G': 5441 case 'L': 5442 case 'N': 5443 case 'P': 5444 case 'T': 5445 case 'U': 5446 case 'V': 5447 case 'W': 5448 case '[': 5449 case ']': 5450 if (ftype != 0) { 5451 xo_failure(xop, "field descriptor uses multiple types: '%s'", 5452 xo_printable(fmt)); 5453 return NULL; 5454 } 5455 ftype = *sp; 5456 break; 5457 5458 case '0': 5459 case '1': 5460 case '2': 5461 case '3': 5462 case '4': 5463 case '5': 5464 case '6': 5465 case '7': 5466 case '8': 5467 case '9': 5468 fnum = (fnum * 10) + (*sp - '0'); 5469 break; 5470 5471 case 'a': 5472 flags |= XFF_ARGUMENT; 5473 break; 5474 5475 case 'c': 5476 flags |= XFF_COLON; 5477 break; 5478 5479 case 'd': 5480 flags |= XFF_DISPLAY_ONLY; 5481 break; 5482 5483 case 'e': 5484 flags |= XFF_ENCODE_ONLY; 5485 break; 5486 5487 case 'g': 5488 flags |= XFF_GT_FIELD; 5489 break; 5490 5491 case 'h': 5492 flags |= XFF_HUMANIZE; 5493 break; 5494 5495 case 'k': 5496 flags |= XFF_KEY; 5497 break; 5498 5499 case 'l': 5500 flags |= XFF_LEAF_LIST; 5501 break; 5502 5503 case 'n': 5504 flags |= XFF_NOQUOTE; 5505 break; 5506 5507 case 'p': 5508 flags |= XFF_GT_PLURAL; 5509 break; 5510 5511 case 'q': 5512 flags |= XFF_QUOTE; 5513 break; 5514 5515 case 't': 5516 flags |= XFF_TRIM_WS; 5517 break; 5518 5519 case 'w': 5520 flags |= XFF_WS; 5521 break; 5522 5523 default: 5524 xo_failure(xop, "field descriptor uses unknown modifier: '%s'", 5525 xo_printable(fmt)); 5526 /* 5527 * No good answer here; a bad format will likely 5528 * mean a core file. We just return and hope 5529 * the caller notices there's no output, and while 5530 * that seems, well, bad, there's nothing better. 5531 */ 5532 return NULL; 5533 } 5534 5535 if (ftype == 'N' || ftype == 'U') { 5536 if (flags & XFF_COLON) { 5537 xo_failure(xop, "colon modifier on 'N' or 'U' field ignored: " 5538 "'%s'", xo_printable(fmt)); 5539 flags &= ~XFF_COLON; 5540 } 5541 } 5542 } 5543 5544 xfip->xfi_flags = flags; 5545 xfip->xfi_ftype = ftype ?: 'V'; 5546 xfip->xfi_fnum = fnum; 5547 5548 return sp; 5549} 5550 5551/* 5552 * Number any remaining fields that need numbers. Note that some 5553 * field types (text, newline, escaped braces) never get numbers. 5554 */ 5555static void 5556xo_gettext_finish_numbering_fields (xo_handle_t *xop UNUSED, 5557 const char *fmt UNUSED, 5558 xo_field_info_t *fields) 5559{ 5560 xo_field_info_t *xfip; 5561 unsigned fnum, max_fields; 5562 uint64_t bits = 0; 5563 5564 /* First make a list of add the explicitly used bits */ 5565 for (xfip = fields, fnum = 0; xfip->xfi_ftype; xfip++) { 5566 switch (xfip->xfi_ftype) { 5567 case XO_ROLE_NEWLINE: /* Don't get numbered */ 5568 case XO_ROLE_TEXT: 5569 case XO_ROLE_EBRACE: 5570 case 'G': 5571 continue; 5572 } 5573 5574 fnum += 1; 5575 if (fnum >= 63) 5576 break; 5577 5578 if (xfip->xfi_fnum) 5579 bits |= 1 << xfip->xfi_fnum; 5580 } 5581 5582 max_fields = fnum; 5583 5584 for (xfip = fields, fnum = 0; xfip->xfi_ftype; xfip++) { 5585 switch (xfip->xfi_ftype) { 5586 case XO_ROLE_NEWLINE: /* Don't get numbered */ 5587 case XO_ROLE_TEXT: 5588 case XO_ROLE_EBRACE: 5589 case 'G': 5590 continue; 5591 } 5592 5593 if (xfip->xfi_fnum != 0) 5594 continue; 5595 5596 /* Find the next unassigned field */ 5597 for (fnum++; bits & (1 << fnum); fnum++) 5598 continue; 5599 5600 if (fnum > max_fields) 5601 break; 5602 5603 xfip->xfi_fnum = fnum; /* Mark the field number */ 5604 bits |= 1 << fnum; /* Mark it used */ 5605 } 5606} 5607 5608/* 5609 * The format string uses field numbers, so we need to whiffle through it 5610 * and make sure everything's sane and lovely. 5611 */ 5612static int 5613xo_parse_field_numbers (xo_handle_t *xop, const char *fmt, 5614 xo_field_info_t *fields, unsigned num_fields) 5615{ 5616 xo_field_info_t *xfip; 5617 unsigned field, fnum; 5618 uint64_t bits = 0; 5619 5620 for (xfip = fields, field = 0; field < num_fields; xfip++, field++) { 5621 /* Fields default to 1:1 with natural position */ 5622 if (xfip->xfi_fnum == 0) 5623 xfip->xfi_fnum = field + 1; 5624 else if (xfip->xfi_fnum > num_fields) { 5625 xo_failure(xop, "field number exceeds number of fields: '%s'", fmt); 5626 return -1; 5627 } 5628 5629 fnum = xfip->xfi_fnum - 1; /* Move to zero origin */ 5630 if (fnum < 64) { /* Only test what fits */ 5631 if (bits & (1 << fnum)) { 5632 xo_failure(xop, "field number %u reused: '%s'", 5633 xfip->xfi_fnum, fmt); 5634 return -1; 5635 } 5636 bits |= 1 << fnum; 5637 } 5638 } 5639 5640 return 0; 5641} 5642 5643static int 5644xo_parse_fields (xo_handle_t *xop, xo_field_info_t *fields, 5645 unsigned num_fields, const char *fmt) 5646{ 5647 const char *cp, *sp, *ep, *basep; 5648 unsigned field = 0; 5649 xo_field_info_t *xfip = fields; 5650 unsigned seen_fnum = 0; 5651 5652 for (cp = fmt; *cp && field < num_fields; field++, xfip++) { 5653 xfip->xfi_start = cp; 5654 5655 if (*cp == '\n') { 5656 xfip->xfi_ftype = XO_ROLE_NEWLINE; 5657 xfip->xfi_len = 1; 5658 cp += 1; 5659 continue; 5660 } 5661 5662 if (*cp != '{') { 5663 /* Normal text */ 5664 for (sp = cp; *sp; sp++) { 5665 if (*sp == '{' || *sp == '\n') 5666 break; 5667 } 5668 5669 xfip->xfi_ftype = XO_ROLE_TEXT; 5670 xfip->xfi_content = cp; 5671 xfip->xfi_clen = sp - cp; 5672 xfip->xfi_next = sp; 5673 5674 cp = sp; 5675 continue; 5676 } 5677 5678 if (cp[1] == '{') { /* Start of {{escaped braces}} */ 5679 xfip->xfi_start = cp + 1; /* Start at second brace */ 5680 xfip->xfi_ftype = XO_ROLE_EBRACE; 5681 5682 cp += 2; /* Skip over _both_ characters */ 5683 for (sp = cp; *sp; sp++) { 5684 if (*sp == '}' && sp[1] == '}') 5685 break; 5686 } 5687 if (*sp == '\0') { 5688 xo_failure(xop, "missing closing '}}': '%s'", 5689 xo_printable(fmt)); 5690 return -1; 5691 } 5692 5693 xfip->xfi_len = sp - xfip->xfi_start + 1; 5694 5695 /* Move along the string, but don't run off the end */ 5696 if (*sp == '}' && sp[1] == '}') 5697 sp += 2; 5698 cp = *sp ? sp : sp; 5699 xfip->xfi_next = cp; 5700 continue; 5701 } 5702 5703 /* We are looking at the start of a field definition */ 5704 xfip->xfi_start = basep = cp + 1; 5705 5706 const char *format = NULL; 5707 ssize_t flen = 0; 5708 5709 /* Looking at roles and modifiers */ 5710 sp = xo_parse_roles(xop, fmt, basep, xfip); 5711 if (sp == NULL) { 5712 /* xo_failure has already been called */ 5713 return -1; 5714 } 5715 5716 if (xfip->xfi_fnum) 5717 seen_fnum = 1; 5718 5719 /* Looking at content */ 5720 if (*sp == ':') { 5721 for (ep = ++sp; *sp; sp++) { 5722 if (*sp == '}' || *sp == '/') 5723 break; 5724 if (*sp == '\\') { 5725 if (sp[1] == '\0') { 5726 xo_failure(xop, "backslash at the end of string"); 5727 return -1; 5728 } 5729 sp += 1; 5730 continue; 5731 } 5732 } 5733 if (ep != sp) { 5734 xfip->xfi_clen = sp - ep; 5735 xfip->xfi_content = ep; 5736 } 5737 } else { 5738 xo_failure(xop, "missing content (':'): '%s'", xo_printable(fmt)); 5739 return -1; 5740 } 5741 5742 /* Looking at main (display) format */ 5743 if (*sp == '/') { 5744 for (ep = ++sp; *sp; sp++) { 5745 if (*sp == '}' || *sp == '/') 5746 break; 5747 if (*sp == '\\') { 5748 if (sp[1] == '\0') { 5749 xo_failure(xop, "backslash at the end of string"); 5750 return -1; 5751 } 5752 sp += 1; 5753 continue; 5754 } 5755 } 5756 flen = sp - ep; 5757 format = ep; 5758 } 5759 5760 /* Looking at encoding format */ 5761 if (*sp == '/') { 5762 for (ep = ++sp; *sp; sp++) { 5763 if (*sp == '}') 5764 break; 5765 } 5766 5767 xfip->xfi_encoding = ep; 5768 xfip->xfi_elen = sp - ep; 5769 } 5770 5771 if (*sp != '}') { 5772 xo_failure(xop, "missing closing '}': %s", xo_printable(fmt)); 5773 return -1; 5774 } 5775 5776 xfip->xfi_len = sp - xfip->xfi_start; 5777 xfip->xfi_next = ++sp; 5778 5779 /* If we have content, then we have a default format */ 5780 if (xfip->xfi_clen || format || (xfip->xfi_flags & XFF_ARGUMENT)) { 5781 if (format) { 5782 xfip->xfi_format = format; 5783 xfip->xfi_flen = flen; 5784 } else if (xo_role_wants_default_format(xfip->xfi_ftype)) { 5785 xfip->xfi_format = xo_default_format; 5786 xfip->xfi_flen = 2; 5787 } 5788 } 5789 5790 cp = sp; 5791 } 5792 5793 int rc = 0; 5794 5795 /* 5796 * If we saw a field number on at least one field, then we need 5797 * to enforce some rules and/or guidelines. 5798 */ 5799 if (seen_fnum) 5800 rc = xo_parse_field_numbers(xop, fmt, fields, field); 5801 5802 return rc; 5803} 5804 5805/* 5806 * We are passed a pointer to a format string just past the "{G:}" 5807 * field. We build a simplified version of the format string. 5808 */ 5809static int 5810xo_gettext_simplify_format (xo_handle_t *xop UNUSED, 5811 xo_buffer_t *xbp, 5812 xo_field_info_t *fields, 5813 int this_field, 5814 const char *fmt UNUSED, 5815 xo_simplify_field_func_t field_cb) 5816{ 5817 unsigned ftype; 5818 xo_xff_flags_t flags; 5819 int field = this_field + 1; 5820 xo_field_info_t *xfip; 5821 char ch; 5822 5823 for (xfip = &fields[field]; xfip->xfi_ftype; xfip++, field++) { 5824 ftype = xfip->xfi_ftype; 5825 flags = xfip->xfi_flags; 5826 5827 if ((flags & XFF_GT_FIELD) && xfip->xfi_content && ftype != 'V') { 5828 if (field_cb) 5829 field_cb(xfip->xfi_content, xfip->xfi_clen, 5830 (flags & XFF_GT_PLURAL) ? 1 : 0); 5831 } 5832 5833 switch (ftype) { 5834 case 'G': 5835 /* Ignore gettext roles */ 5836 break; 5837 5838 case XO_ROLE_NEWLINE: 5839 xo_buf_append(xbp, "\n", 1); 5840 break; 5841 5842 case XO_ROLE_EBRACE: 5843 xo_buf_append(xbp, "{", 1); 5844 xo_buf_append(xbp, xfip->xfi_content, xfip->xfi_clen); 5845 xo_buf_append(xbp, "}", 1); 5846 break; 5847 5848 case XO_ROLE_TEXT: 5849 xo_buf_append(xbp, xfip->xfi_content, xfip->xfi_clen); 5850 break; 5851 5852 default: 5853 xo_buf_append(xbp, "{", 1); 5854 if (ftype != 'V') { 5855 ch = ftype; 5856 xo_buf_append(xbp, &ch, 1); 5857 } 5858 5859 unsigned fnum = xfip->xfi_fnum ?: 0; 5860 if (fnum) { 5861 char num[12]; 5862 /* Field numbers are origin 1, not 0, following printf(3) */ 5863 snprintf(num, sizeof(num), "%u", fnum); 5864 xo_buf_append(xbp, num, strlen(num)); 5865 } 5866 5867 xo_buf_append(xbp, ":", 1); 5868 xo_buf_append(xbp, xfip->xfi_content, xfip->xfi_clen); 5869 xo_buf_append(xbp, "}", 1); 5870 } 5871 } 5872 5873 xo_buf_append(xbp, "", 1); 5874 return 0; 5875} 5876 5877void 5878xo_dump_fields (xo_field_info_t *); /* Fake prototype for debug function */ 5879void 5880xo_dump_fields (xo_field_info_t *fields) 5881{ 5882 xo_field_info_t *xfip; 5883 5884 for (xfip = fields; xfip->xfi_ftype; xfip++) { 5885 printf("%lu(%u): %lx [%c/%u] [%.*s] [%.*s] [%.*s]\n", 5886 (unsigned long) (xfip - fields), xfip->xfi_fnum, 5887 (unsigned long) xfip->xfi_flags, 5888 isprint((int) xfip->xfi_ftype) ? xfip->xfi_ftype : ' ', 5889 xfip->xfi_ftype, 5890 (int) xfip->xfi_clen, xfip->xfi_content ?: "", 5891 (int) xfip->xfi_flen, xfip->xfi_format ?: "", 5892 (int) xfip->xfi_elen, xfip->xfi_encoding ?: ""); 5893 } 5894} 5895 5896#ifdef HAVE_GETTEXT 5897/* 5898 * Find the field that matches the given field number 5899 */ 5900static xo_field_info_t * 5901xo_gettext_find_field (xo_field_info_t *fields, unsigned fnum) 5902{ 5903 xo_field_info_t *xfip; 5904 5905 for (xfip = fields; xfip->xfi_ftype; xfip++) 5906 if (xfip->xfi_fnum == fnum) 5907 return xfip; 5908 5909 return NULL; 5910} 5911 5912/* 5913 * At this point, we need to consider if the fields have been reordered, 5914 * such as "The {:adjective} {:noun}" to "La {:noun} {:adjective}". 5915 * 5916 * We need to rewrite the new_fields using the old fields order, 5917 * so that we can render the message using the arguments as they 5918 * appear on the stack. It's a lot of work, but we don't really 5919 * want to (eventually) fall into the standard printf code which 5920 * means using the arguments straight (and in order) from the 5921 * varargs we were originally passed. 5922 */ 5923static void 5924xo_gettext_rewrite_fields (xo_handle_t *xop UNUSED, 5925 xo_field_info_t *fields, unsigned max_fields) 5926{ 5927 xo_field_info_t tmp[max_fields]; 5928 bzero(tmp, max_fields * sizeof(tmp[0])); 5929 5930 unsigned fnum = 0; 5931 xo_field_info_t *newp, *outp, *zp; 5932 for (newp = fields, outp = tmp; newp->xfi_ftype; newp++, outp++) { 5933 switch (newp->xfi_ftype) { 5934 case XO_ROLE_NEWLINE: /* Don't get numbered */ 5935 case XO_ROLE_TEXT: 5936 case XO_ROLE_EBRACE: 5937 case 'G': 5938 *outp = *newp; 5939 outp->xfi_renum = 0; 5940 continue; 5941 } 5942 5943 zp = xo_gettext_find_field(fields, ++fnum); 5944 if (zp == NULL) { /* Should not occur */ 5945 *outp = *newp; 5946 outp->xfi_renum = 0; 5947 continue; 5948 } 5949 5950 *outp = *zp; 5951 outp->xfi_renum = newp->xfi_fnum; 5952 } 5953 5954 memcpy(fields, tmp, max_fields * sizeof(tmp[0])); 5955} 5956 5957/* 5958 * We've got two lists of fields, the old list from the original 5959 * format string and the new one from the parsed gettext reply. The 5960 * new list has the localized words, where the old list has the 5961 * formatting information. We need to combine them into a single list 5962 * (the new list). 5963 * 5964 * If the list needs to be reordered, then we've got more serious work 5965 * to do. 5966 */ 5967static int 5968xo_gettext_combine_formats (xo_handle_t *xop, const char *fmt UNUSED, 5969 const char *gtfmt, xo_field_info_t *old_fields, 5970 xo_field_info_t *new_fields, unsigned new_max_fields, 5971 int *reorderedp) 5972{ 5973 int reordered = 0; 5974 xo_field_info_t *newp, *oldp, *startp = old_fields; 5975 5976 xo_gettext_finish_numbering_fields(xop, fmt, old_fields); 5977 5978 for (newp = new_fields; newp->xfi_ftype; newp++) { 5979 switch (newp->xfi_ftype) { 5980 case XO_ROLE_NEWLINE: 5981 case XO_ROLE_TEXT: 5982 case XO_ROLE_EBRACE: 5983 continue; 5984 5985 case 'V': 5986 for (oldp = startp; oldp->xfi_ftype; oldp++) { 5987 if (oldp->xfi_ftype != 'V') 5988 continue; 5989 if (newp->xfi_clen != oldp->xfi_clen 5990 || strncmp(newp->xfi_content, oldp->xfi_content, 5991 oldp->xfi_clen) != 0) { 5992 reordered = 1; 5993 continue; 5994 } 5995 startp = oldp + 1; 5996 break; 5997 } 5998 5999 /* Didn't find it on the first pass (starting from start) */ 6000 if (oldp->xfi_ftype == 0) { 6001 for (oldp = old_fields; oldp < startp; oldp++) { 6002 if (oldp->xfi_ftype != 'V') 6003 continue; 6004 if (newp->xfi_clen != oldp->xfi_clen) 6005 continue; 6006 if (strncmp(newp->xfi_content, oldp->xfi_content, 6007 oldp->xfi_clen) != 0) 6008 continue; 6009 reordered = 1; 6010 break; 6011 } 6012 if (oldp == startp) { 6013 /* Field not found */ 6014 xo_failure(xop, "post-gettext format can't find field " 6015 "'%.*s' in format '%s'", 6016 newp->xfi_clen, newp->xfi_content, 6017 xo_printable(gtfmt)); 6018 return -1; 6019 } 6020 } 6021 break; 6022 6023 default: 6024 /* 6025 * Other fields don't have names for us to use, so if 6026 * the types aren't the same, then we'll have to assume 6027 * the original field is a match. 6028 */ 6029 for (oldp = startp; oldp->xfi_ftype; oldp++) { 6030 if (oldp->xfi_ftype == 'V') /* Can't go past these */ 6031 break; 6032 if (oldp->xfi_ftype == newp->xfi_ftype) 6033 goto copy_it; /* Assumably we have a match */ 6034 } 6035 continue; 6036 } 6037 6038 /* 6039 * Found a match; copy over appropriate fields 6040 */ 6041 copy_it: 6042 newp->xfi_flags = oldp->xfi_flags; 6043 newp->xfi_fnum = oldp->xfi_fnum; 6044 newp->xfi_format = oldp->xfi_format; 6045 newp->xfi_flen = oldp->xfi_flen; 6046 newp->xfi_encoding = oldp->xfi_encoding; 6047 newp->xfi_elen = oldp->xfi_elen; 6048 } 6049 6050 *reorderedp = reordered; 6051 if (reordered) { 6052 xo_gettext_finish_numbering_fields(xop, fmt, new_fields); 6053 xo_gettext_rewrite_fields(xop, new_fields, new_max_fields); 6054 } 6055 6056 return 0; 6057} 6058 6059/* 6060 * We don't want to make gettext() calls here with a complete format 6061 * string, since that means changing a flag would mean a 6062 * labor-intensive re-translation expense. Instead we build a 6063 * simplified form with a reduced level of detail, perform a lookup on 6064 * that string and then re-insert the formating info. 6065 * 6066 * So something like: 6067 * xo_emit("{G:}close {:fd/%ld} returned {g:error/%m} {:test/%6.6s}\n", ...) 6068 * would have a lookup string of: 6069 * "close {:fd} returned {:error} {:test}\n" 6070 * 6071 * We also need to handling reordering of fields, where the gettext() 6072 * reply string uses fields in a different order than the original 6073 * format string: 6074 * "cluse-a {:fd} retoorned {:test}. Bork {:error} Bork. Bork.\n" 6075 * If we have to reorder fields within the message, then things get 6076 * complicated. See xo_gettext_rewrite_fields. 6077 * 6078 * Summary: i18n aighn't cheap. 6079 */ 6080static const char * 6081xo_gettext_build_format (xo_handle_t *xop, 6082 xo_field_info_t *fields, int this_field, 6083 const char *fmt, char **new_fmtp) 6084{ 6085 if (xo_style_is_encoding(xop)) 6086 goto bail; 6087 6088 xo_buffer_t xb; 6089 xo_buf_init(&xb); 6090 6091 if (xo_gettext_simplify_format(xop, &xb, fields, 6092 this_field, fmt, NULL)) 6093 goto bail2; 6094 6095 const char *gtfmt = xo_dgettext(xop, xb.xb_bufp); 6096 if (gtfmt == NULL || gtfmt == fmt || strcmp(gtfmt, fmt) == 0) 6097 goto bail2; 6098 6099 char *new_fmt = xo_strndup(gtfmt, -1); 6100 if (new_fmt == NULL) 6101 goto bail2; 6102 6103 xo_buf_cleanup(&xb); 6104 6105 *new_fmtp = new_fmt; 6106 return new_fmt; 6107 6108 bail2: 6109 xo_buf_cleanup(&xb); 6110 bail: 6111 *new_fmtp = NULL; 6112 return fmt; 6113} 6114 6115static void 6116xo_gettext_rebuild_content (xo_handle_t *xop, xo_field_info_t *fields, 6117 ssize_t *fstart, unsigned min_fstart, 6118 ssize_t *fend, unsigned max_fend) 6119{ 6120 xo_field_info_t *xfip; 6121 char *buf; 6122 ssize_t base = fstart[min_fstart]; 6123 ssize_t blen = fend[max_fend] - base; 6124 xo_buffer_t *xbp = &xop->xo_data; 6125 6126 if (blen == 0) 6127 return; 6128 6129 buf = xo_realloc(NULL, blen); 6130 if (buf == NULL) 6131 return; 6132 6133 memcpy(buf, xbp->xb_bufp + fstart[min_fstart], blen); /* Copy our data */ 6134 6135 unsigned field = min_fstart, len, fnum; 6136 ssize_t soff, doff = base; 6137 xo_field_info_t *zp; 6138 6139 /* 6140 * Be aware there are two competing views of "field number": we 6141 * want the user to thing in terms of "The {1:size}" where {G:}, 6142 * newlines, escaped braces, and text don't have numbers. But is 6143 * also the internal view, where we have an array of 6144 * xo_field_info_t and every field have an index. fnum, fstart[] 6145 * and fend[] are the latter, but xfi_renum is the former. 6146 */ 6147 for (xfip = fields + field; xfip->xfi_ftype; xfip++, field++) { 6148 fnum = field; 6149 if (xfip->xfi_renum) { 6150 zp = xo_gettext_find_field(fields, xfip->xfi_renum); 6151 fnum = zp ? zp - fields : field; 6152 } 6153 6154 soff = fstart[fnum]; 6155 len = fend[fnum] - soff; 6156 6157 if (len > 0) { 6158 soff -= base; 6159 memcpy(xbp->xb_bufp + doff, buf + soff, len); 6160 doff += len; 6161 } 6162 } 6163 6164 xo_free(buf); 6165} 6166#else /* HAVE_GETTEXT */ 6167static const char * 6168xo_gettext_build_format (xo_handle_t *xop UNUSED, 6169 xo_field_info_t *fields UNUSED, 6170 int this_field UNUSED, 6171 const char *fmt UNUSED, char **new_fmtp) 6172{ 6173 *new_fmtp = NULL; 6174 return fmt; 6175} 6176 6177static int 6178xo_gettext_combine_formats (xo_handle_t *xop UNUSED, const char *fmt UNUSED, 6179 const char *gtfmt UNUSED, 6180 xo_field_info_t *old_fields UNUSED, 6181 xo_field_info_t *new_fields UNUSED, 6182 unsigned new_max_fields UNUSED, 6183 int *reorderedp UNUSED) 6184{ 6185 return -1; 6186} 6187 6188static void 6189xo_gettext_rebuild_content (xo_handle_t *xop UNUSED, 6190 xo_field_info_t *fields UNUSED, 6191 ssize_t *fstart UNUSED, unsigned min_fstart UNUSED, 6192 ssize_t *fend UNUSED, unsigned max_fend UNUSED) 6193{ 6194 return; 6195} 6196#endif /* HAVE_GETTEXT */ 6197 6198/* 6199 * Emit a set of fields. This is really the core of libxo. 6200 */ 6201static ssize_t 6202xo_do_emit_fields (xo_handle_t *xop, xo_field_info_t *fields, 6203 unsigned max_fields, const char *fmt) 6204{ 6205 int gettext_inuse = 0; 6206 int gettext_changed = 0; 6207 int gettext_reordered = 0; 6208 unsigned ftype; 6209 xo_xff_flags_t flags; 6210 xo_field_info_t *new_fields = NULL; 6211 xo_field_info_t *xfip; 6212 unsigned field; 6213 ssize_t rc = 0; 6214 6215 int flush = XOF_ISSET(xop, XOF_FLUSH); 6216 int flush_line = XOF_ISSET(xop, XOF_FLUSH_LINE); 6217 char *new_fmt = NULL; 6218 6219 if (XOIF_ISSET(xop, XOIF_REORDER) || xo_style(xop) == XO_STYLE_ENCODER) 6220 flush_line = 0; 6221 6222 /* 6223 * Some overhead for gettext; if the fields in the msgstr returned 6224 * by gettext are reordered, then we need to record start and end 6225 * for each field. We'll go ahead and render the fields in the 6226 * normal order, but later we can then reconstruct the reordered 6227 * fields using these fstart/fend values. 6228 */ 6229 unsigned flimit = max_fields * 2; /* Pessimistic limit */ 6230 unsigned min_fstart = flimit - 1; 6231 unsigned max_fend = 0; /* Highest recorded fend[] entry */ 6232 ssize_t fstart[flimit]; 6233 bzero(fstart, flimit * sizeof(fstart[0])); 6234 ssize_t fend[flimit]; 6235 bzero(fend, flimit * sizeof(fend[0])); 6236 6237 for (xfip = fields, field = 0; field < max_fields && xfip->xfi_ftype; 6238 xfip++, field++) { 6239 ftype = xfip->xfi_ftype; 6240 flags = xfip->xfi_flags; 6241 6242 /* Record field start offset */ 6243 if (gettext_reordered) { 6244 fstart[field] = xo_buf_offset(&xop->xo_data); 6245 if (min_fstart > field) 6246 min_fstart = field; 6247 } 6248 6249 const char *content = xfip->xfi_content; 6250 ssize_t clen = xfip->xfi_clen; 6251 6252 if (flags & XFF_ARGUMENT) { 6253 /* 6254 * Argument flag means the content isn't given in the descriptor, 6255 * but as a UTF-8 string ('const char *') argument in xo_vap. 6256 */ 6257 content = va_arg(xop->xo_vap, char *); 6258 clen = content ? strlen(content) : 0; 6259 } 6260 6261 if (ftype == XO_ROLE_NEWLINE) { 6262 xo_line_close(xop); 6263 if (flush_line && xo_flush_h(xop) < 0) 6264 return -1; 6265 goto bottom; 6266 6267 } else if (ftype == XO_ROLE_EBRACE) { 6268 xo_format_text(xop, xfip->xfi_start, xfip->xfi_len); 6269 goto bottom; 6270 6271 } else if (ftype == XO_ROLE_TEXT) { 6272 /* Normal text */ 6273 xo_format_text(xop, xfip->xfi_content, xfip->xfi_clen); 6274 goto bottom; 6275 } 6276 6277 /* 6278 * Notes and units need the 'w' flag handled before the content. 6279 */ 6280 if (ftype == 'N' || ftype == 'U') { 6281 if (flags & XFF_WS) { 6282 xo_format_content(xop, "padding", NULL, " ", 1, 6283 NULL, 0, flags); 6284 flags &= ~XFF_WS; /* Prevent later handling of this flag */ 6285 } 6286 } 6287 6288 if (ftype == 'V') 6289 xo_format_value(xop, content, clen, NULL, 0, 6290 xfip->xfi_format, xfip->xfi_flen, 6291 xfip->xfi_encoding, xfip->xfi_elen, flags); 6292 else if (ftype == '[') 6293 xo_anchor_start(xop, xfip, content, clen); 6294 else if (ftype == ']') 6295 xo_anchor_stop(xop, xfip, content, clen); 6296 else if (ftype == 'C') 6297 xo_format_colors(xop, xfip, content, clen); 6298 6299 else if (ftype == 'G') { 6300 /* 6301 * A {G:domain} field; disect the domain name and translate 6302 * the remaining portion of the input string. If the user 6303 * didn't put the {G:} at the start of the format string, then 6304 * assumably they just want us to translate the rest of it. 6305 * Since gettext returns strings in a static buffer, we make 6306 * a copy in new_fmt. 6307 */ 6308 xo_set_gettext_domain(xop, xfip, content, clen); 6309 6310 if (!gettext_inuse) { /* Only translate once */ 6311 gettext_inuse = 1; 6312 if (new_fmt) { 6313 xo_free(new_fmt); 6314 new_fmt = NULL; 6315 } 6316 6317 xo_gettext_build_format(xop, fields, field, 6318 xfip->xfi_next, &new_fmt); 6319 if (new_fmt) { 6320 gettext_changed = 1; 6321 6322 unsigned new_max_fields = xo_count_fields(xop, new_fmt); 6323 6324 if (++new_max_fields < max_fields) 6325 new_max_fields = max_fields; 6326 6327 /* Leave a blank slot at the beginning */ 6328 ssize_t sz = (new_max_fields + 1) * sizeof(xo_field_info_t); 6329 new_fields = alloca(sz); 6330 bzero(new_fields, sz); 6331 6332 if (!xo_parse_fields(xop, new_fields + 1, 6333 new_max_fields, new_fmt)) { 6334 gettext_reordered = 0; 6335 6336 if (!xo_gettext_combine_formats(xop, fmt, new_fmt, 6337 fields, new_fields + 1, 6338 new_max_fields, &gettext_reordered)) { 6339 6340 if (gettext_reordered) { 6341 if (XOF_ISSET(xop, XOF_LOG_GETTEXT)) 6342 xo_failure(xop, "gettext finds reordered " 6343 "fields in '%s' and '%s'", 6344 xo_printable(fmt), 6345 xo_printable(new_fmt)); 6346 flush_line = 0; /* Must keep at content */ 6347 XOIF_SET(xop, XOIF_REORDER); 6348 } 6349 6350 field = -1; /* Will be incremented at top of loop */ 6351 xfip = new_fields; 6352 max_fields = new_max_fields; 6353 } 6354 } 6355 } 6356 } 6357 continue; 6358 6359 } else if (clen || xfip->xfi_format) { 6360 6361 const char *class_name = xo_class_name(ftype); 6362 if (class_name) 6363 xo_format_content(xop, class_name, xo_tag_name(ftype), 6364 content, clen, 6365 xfip->xfi_format, xfip->xfi_flen, flags); 6366 else if (ftype == 'T') 6367 xo_format_title(xop, xfip, content, clen); 6368 else if (ftype == 'U') 6369 xo_format_units(xop, xfip, content, clen); 6370 else 6371 xo_failure(xop, "unknown field type: '%c'", ftype); 6372 } 6373 6374 if (flags & XFF_COLON) 6375 xo_format_content(xop, "decoration", NULL, ":", 1, NULL, 0, 0); 6376 6377 if (flags & XFF_WS) 6378 xo_format_content(xop, "padding", NULL, " ", 1, NULL, 0, 0); 6379 6380 bottom: 6381 /* Record the end-of-field offset */ 6382 if (gettext_reordered) { 6383 fend[field] = xo_buf_offset(&xop->xo_data); 6384 max_fend = field; 6385 } 6386 } 6387 6388 if (gettext_changed && gettext_reordered) { 6389 /* Final step: rebuild the content using the rendered fields */ 6390 xo_gettext_rebuild_content(xop, new_fields + 1, fstart, min_fstart, 6391 fend, max_fend); 6392 } 6393 6394 XOIF_CLEAR(xop, XOIF_REORDER); 6395 6396 /* 6397 * If we've got enough data, flush it. 6398 */ 6399 if (xo_buf_offset(&xop->xo_data) > XO_BUF_HIGH_WATER) 6400 flush = 1; 6401 6402 /* If we don't have an anchor, write the text out */ 6403 if (flush && !XOIF_ISSET(xop, XOIF_ANCHOR)) { 6404 if (xo_write(xop) < 0) 6405 rc = -1; /* Report failure */ 6406 else if (xo_flush_h(xop) < 0) 6407 rc = -1; 6408 } 6409 6410 if (new_fmt) 6411 xo_free(new_fmt); 6412 6413 /* 6414 * We've carried the gettext domainname inside our handle just for 6415 * convenience, but we need to ensure it doesn't survive across 6416 * xo_emit calls. 6417 */ 6418 if (xop->xo_gt_domain) { 6419 xo_free(xop->xo_gt_domain); 6420 xop->xo_gt_domain = NULL; 6421 } 6422 6423 return (rc < 0) ? rc : xop->xo_columns; 6424} 6425 6426/* 6427 * Parse and emit a set of fields 6428 */ 6429static int 6430xo_do_emit (xo_handle_t *xop, xo_emit_flags_t flags, const char *fmt) 6431{ 6432 xop->xo_columns = 0; /* Always reset it */ 6433 xop->xo_errno = errno; /* Save for "%m" */ 6434 6435 if (fmt == NULL) 6436 return 0; 6437 6438 unsigned max_fields; 6439 xo_field_info_t *fields = NULL; 6440 6441 /* Adjust XOEF_RETAIN based on global flags */ 6442 if (XOF_ISSET(xop, XOF_RETAIN_ALL)) 6443 flags |= XOEF_RETAIN; 6444 if (XOF_ISSET(xop, XOF_RETAIN_NONE)) 6445 flags &= ~XOEF_RETAIN; 6446 6447 /* 6448 * Check for 'retain' flag, telling us to retain the field 6449 * information. If we've already saved it, then we can avoid 6450 * re-parsing the format string. 6451 */ 6452 if (!(flags & XOEF_RETAIN) 6453 || xo_retain_find(fmt, &fields, &max_fields) != 0 6454 || fields == NULL) { 6455 6456 /* Nothing retained; parse the format string */ 6457 max_fields = xo_count_fields(xop, fmt); 6458 fields = alloca(max_fields * sizeof(fields[0])); 6459 bzero(fields, max_fields * sizeof(fields[0])); 6460 6461 if (xo_parse_fields(xop, fields, max_fields, fmt)) 6462 return -1; /* Warning already displayed */ 6463 6464 if (flags & XOEF_RETAIN) { 6465 /* Retain the info */ 6466 xo_retain_add(fmt, fields, max_fields); 6467 } 6468 } 6469 6470 return xo_do_emit_fields(xop, fields, max_fields, fmt); 6471} 6472 6473/* 6474 * Rebuild a format string in a gettext-friendly format. This function 6475 * is exposed to tools can perform this function. See xo(1). 6476 */ 6477char * 6478xo_simplify_format (xo_handle_t *xop, const char *fmt, int with_numbers, 6479 xo_simplify_field_func_t field_cb) 6480{ 6481 xop = xo_default(xop); 6482 6483 xop->xo_columns = 0; /* Always reset it */ 6484 xop->xo_errno = errno; /* Save for "%m" */ 6485 6486 unsigned max_fields = xo_count_fields(xop, fmt); 6487 xo_field_info_t fields[max_fields]; 6488 6489 bzero(fields, max_fields * sizeof(fields[0])); 6490 6491 if (xo_parse_fields(xop, fields, max_fields, fmt)) 6492 return NULL; /* Warning already displayed */ 6493 6494 xo_buffer_t xb; 6495 xo_buf_init(&xb); 6496 6497 if (with_numbers) 6498 xo_gettext_finish_numbering_fields(xop, fmt, fields); 6499 6500 if (xo_gettext_simplify_format(xop, &xb, fields, -1, fmt, field_cb)) 6501 return NULL; 6502 6503 return xb.xb_bufp; 6504} 6505 6506xo_ssize_t 6507xo_emit_hv (xo_handle_t *xop, const char *fmt, va_list vap) 6508{ 6509 ssize_t rc; 6510 6511 xop = xo_default(xop); 6512 va_copy(xop->xo_vap, vap); 6513 rc = xo_do_emit(xop, 0, fmt); 6514 va_end(xop->xo_vap); 6515 bzero(&xop->xo_vap, sizeof(xop->xo_vap)); 6516 6517 return rc; 6518} 6519 6520xo_ssize_t 6521xo_emit_h (xo_handle_t *xop, const char *fmt, ...) 6522{ 6523 ssize_t rc; 6524 6525 xop = xo_default(xop); 6526 va_start(xop->xo_vap, fmt); 6527 rc = xo_do_emit(xop, 0, fmt); 6528 va_end(xop->xo_vap); 6529 bzero(&xop->xo_vap, sizeof(xop->xo_vap)); 6530 6531 return rc; 6532} 6533 6534xo_ssize_t 6535xo_emit (const char *fmt, ...) 6536{ 6537 xo_handle_t *xop = xo_default(NULL); 6538 ssize_t rc; 6539 6540 va_start(xop->xo_vap, fmt); 6541 rc = xo_do_emit(xop, 0, fmt); 6542 va_end(xop->xo_vap); 6543 bzero(&xop->xo_vap, sizeof(xop->xo_vap)); 6544 6545 return rc; 6546} 6547 6548xo_ssize_t 6549xo_emit_hvf (xo_handle_t *xop, xo_emit_flags_t flags, 6550 const char *fmt, va_list vap) 6551{ 6552 ssize_t rc; 6553 6554 xop = xo_default(xop); 6555 va_copy(xop->xo_vap, vap); 6556 rc = xo_do_emit(xop, flags, fmt); 6557 va_end(xop->xo_vap); 6558 bzero(&xop->xo_vap, sizeof(xop->xo_vap)); 6559 6560 return rc; 6561} 6562 6563xo_ssize_t 6564xo_emit_hf (xo_handle_t *xop, xo_emit_flags_t flags, const char *fmt, ...) 6565{ 6566 ssize_t rc; 6567 6568 xop = xo_default(xop); 6569 va_start(xop->xo_vap, fmt); 6570 rc = xo_do_emit(xop, flags, fmt); 6571 va_end(xop->xo_vap); 6572 bzero(&xop->xo_vap, sizeof(xop->xo_vap)); 6573 6574 return rc; 6575} 6576 6577xo_ssize_t 6578xo_emit_f (xo_emit_flags_t flags, const char *fmt, ...) 6579{ 6580 xo_handle_t *xop = xo_default(NULL); 6581 ssize_t rc; 6582 6583 va_start(xop->xo_vap, fmt); 6584 rc = xo_do_emit(xop, flags, fmt); 6585 va_end(xop->xo_vap); 6586 bzero(&xop->xo_vap, sizeof(xop->xo_vap)); 6587 6588 return rc; 6589} 6590 6591/* 6592 * Emit a single field by providing the info information typically provided 6593 * inside the field description (role, modifiers, and formats). This is 6594 * a convenience function to avoid callers using snprintf to build field 6595 * descriptions. 6596 */ 6597xo_ssize_t 6598xo_emit_field_hv (xo_handle_t *xop, const char *rolmod, const char *contents, 6599 const char *fmt, const char *efmt, 6600 va_list vap) 6601{ 6602 ssize_t rc; 6603 6604 xop = xo_default(xop); 6605 6606 if (rolmod == NULL) 6607 rolmod = "V"; 6608 6609 xo_field_info_t xfi; 6610 6611 bzero(&xfi, sizeof(xfi)); 6612 6613 const char *cp; 6614 cp = xo_parse_roles(xop, rolmod, rolmod, &xfi); 6615 if (cp == NULL) 6616 return -1; 6617 6618 xfi.xfi_start = fmt; 6619 xfi.xfi_content = contents; 6620 xfi.xfi_format = fmt; 6621 xfi.xfi_encoding = efmt; 6622 xfi.xfi_clen = contents ? strlen(contents) : 0; 6623 xfi.xfi_flen = fmt ? strlen(fmt) : 0; 6624 xfi.xfi_elen = efmt ? strlen(efmt) : 0; 6625 6626 /* If we have content, then we have a default format */ 6627 if (contents && fmt == NULL 6628 && xo_role_wants_default_format(xfi.xfi_ftype)) { 6629 xfi.xfi_format = xo_default_format; 6630 xfi.xfi_flen = 2; 6631 } 6632 6633 va_copy(xop->xo_vap, vap); 6634 6635 rc = xo_do_emit_fields(xop, &xfi, 1, fmt ?: contents ?: "field"); 6636 6637 va_end(xop->xo_vap); 6638 6639 return rc; 6640} 6641 6642xo_ssize_t 6643xo_emit_field_h (xo_handle_t *xop, const char *rolmod, const char *contents, 6644 const char *fmt, const char *efmt, ...) 6645{ 6646 ssize_t rc; 6647 va_list vap; 6648 6649 va_start(vap, efmt); 6650 rc = xo_emit_field_hv(xop, rolmod, contents, fmt, efmt, vap); 6651 va_end(vap); 6652 6653 return rc; 6654} 6655 6656xo_ssize_t 6657xo_emit_field (const char *rolmod, const char *contents, 6658 const char *fmt, const char *efmt, ...) 6659{ 6660 ssize_t rc; 6661 va_list vap; 6662 6663 va_start(vap, efmt); 6664 rc = xo_emit_field_hv(NULL, rolmod, contents, fmt, efmt, vap); 6665 va_end(vap); 6666 6667 return rc; 6668} 6669 6670xo_ssize_t 6671xo_attr_hv (xo_handle_t *xop, const char *name, const char *fmt, va_list vap) 6672{ 6673 const ssize_t extra = 5; /* space, equals, quote, quote, and nul */ 6674 xop = xo_default(xop); 6675 6676 ssize_t rc = 0; 6677 ssize_t nlen = strlen(name); 6678 xo_buffer_t *xbp = &xop->xo_attrs; 6679 ssize_t name_offset, value_offset; 6680 6681 switch (xo_style(xop)) { 6682 case XO_STYLE_XML: 6683 if (!xo_buf_has_room(xbp, nlen + extra)) 6684 return -1; 6685 6686 *xbp->xb_curp++ = ' '; 6687 memcpy(xbp->xb_curp, name, nlen); 6688 xbp->xb_curp += nlen; 6689 *xbp->xb_curp++ = '='; 6690 *xbp->xb_curp++ = '"'; 6691 6692 rc = xo_vsnprintf(xop, xbp, fmt, vap); 6693 6694 if (rc >= 0) { 6695 rc = xo_escape_xml(xbp, rc, 1); 6696 xbp->xb_curp += rc; 6697 } 6698 6699 if (!xo_buf_has_room(xbp, 2)) 6700 return -1; 6701 6702 *xbp->xb_curp++ = '"'; 6703 *xbp->xb_curp = '\0'; 6704 6705 rc += nlen + extra; 6706 break; 6707 6708 case XO_STYLE_ENCODER: 6709 name_offset = xo_buf_offset(xbp); 6710 xo_buf_append(xbp, name, nlen); 6711 xo_buf_append(xbp, "", 1); 6712 6713 value_offset = xo_buf_offset(xbp); 6714 rc = xo_vsnprintf(xop, xbp, fmt, vap); 6715 if (rc >= 0) { 6716 xbp->xb_curp += rc; 6717 *xbp->xb_curp = '\0'; 6718 rc = xo_encoder_handle(xop, XO_OP_ATTRIBUTE, 6719 xo_buf_data(xbp, name_offset), 6720 xo_buf_data(xbp, value_offset), 0); 6721 } 6722 } 6723 6724 return rc; 6725} 6726 6727xo_ssize_t 6728xo_attr_h (xo_handle_t *xop, const char *name, const char *fmt, ...) 6729{ 6730 ssize_t rc; 6731 va_list vap; 6732 6733 va_start(vap, fmt); 6734 rc = xo_attr_hv(xop, name, fmt, vap); 6735 va_end(vap); 6736 6737 return rc; 6738} 6739 6740xo_ssize_t 6741xo_attr (const char *name, const char *fmt, ...) 6742{ 6743 ssize_t rc; 6744 va_list vap; 6745 6746 va_start(vap, fmt); 6747 rc = xo_attr_hv(NULL, name, fmt, vap); 6748 va_end(vap); 6749 6750 return rc; 6751} 6752 6753static void 6754xo_stack_set_flags (xo_handle_t *xop) 6755{ 6756 if (XOF_ISSET(xop, XOF_NOT_FIRST)) { 6757 xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth]; 6758 6759 xsp->xs_flags |= XSF_NOT_FIRST; 6760 XOF_CLEAR(xop, XOF_NOT_FIRST); 6761 } 6762} 6763 6764static void 6765xo_depth_change (xo_handle_t *xop, const char *name, 6766 int delta, int indent, xo_state_t state, xo_xsf_flags_t flags) 6767{ 6768 if (xo_style(xop) == XO_STYLE_HTML || xo_style(xop) == XO_STYLE_TEXT) 6769 indent = 0; 6770 6771 if (XOF_ISSET(xop, XOF_DTRT)) 6772 flags |= XSF_DTRT; 6773 6774 if (delta >= 0) { /* Push operation */ 6775 if (xo_depth_check(xop, xop->xo_depth + delta)) 6776 return; 6777 6778 xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth + delta]; 6779 xsp->xs_flags = flags; 6780 xsp->xs_state = state; 6781 xo_stack_set_flags(xop); 6782 6783 if (name == NULL) 6784 name = XO_FAILURE_NAME; 6785 6786 xsp->xs_name = xo_strndup(name, -1); 6787 6788 } else { /* Pop operation */ 6789 if (xop->xo_depth == 0) { 6790 if (!XOF_ISSET(xop, XOF_IGNORE_CLOSE)) 6791 xo_failure(xop, "close with empty stack: '%s'", name); 6792 return; 6793 } 6794 6795 xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth]; 6796 if (XOF_ISSET(xop, XOF_WARN)) { 6797 const char *top = xsp->xs_name; 6798 if (top != NULL && name != NULL && strcmp(name, top) != 0) { 6799 xo_failure(xop, "incorrect close: '%s' .vs. '%s'", 6800 name, top); 6801 return; 6802 } 6803 if ((xsp->xs_flags & XSF_LIST) != (flags & XSF_LIST)) { 6804 xo_failure(xop, "list close on list confict: '%s'", 6805 name); 6806 return; 6807 } 6808 if ((xsp->xs_flags & XSF_INSTANCE) != (flags & XSF_INSTANCE)) { 6809 xo_failure(xop, "list close on instance confict: '%s'", 6810 name); 6811 return; 6812 } 6813 } 6814 6815 if (xsp->xs_name) { 6816 xo_free(xsp->xs_name); 6817 xsp->xs_name = NULL; 6818 } 6819 if (xsp->xs_keys) { 6820 xo_free(xsp->xs_keys); 6821 xsp->xs_keys = NULL; 6822 } 6823 } 6824 6825 xop->xo_depth += delta; /* Record new depth */ 6826 xop->xo_indent += indent; 6827} 6828 6829void 6830xo_set_depth (xo_handle_t *xop, int depth) 6831{ 6832 xop = xo_default(xop); 6833 6834 if (xo_depth_check(xop, depth)) 6835 return; 6836 6837 xop->xo_depth += depth; 6838 xop->xo_indent += depth; 6839} 6840 6841static xo_xsf_flags_t 6842xo_stack_flags (xo_xof_flags_t xflags) 6843{ 6844 if (xflags & XOF_DTRT) 6845 return XSF_DTRT; 6846 return 0; 6847} 6848 6849static void 6850xo_emit_top (xo_handle_t *xop, const char *ppn) 6851{ 6852 xo_printf(xop, "%*s{%s", xo_indent(xop), "", ppn); 6853 XOIF_SET(xop, XOIF_TOP_EMITTED); 6854 6855 if (xop->xo_version) { 6856 xo_printf(xop, "%*s\"__version\": \"%s\", %s", 6857 xo_indent(xop), "", xop->xo_version, ppn); 6858 xo_free(xop->xo_version); 6859 xop->xo_version = NULL; 6860 } 6861} 6862 6863static ssize_t 6864xo_do_open_container (xo_handle_t *xop, xo_xof_flags_t flags, const char *name) 6865{ 6866 ssize_t rc = 0; 6867 const char *ppn = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : ""; 6868 const char *pre_nl = ""; 6869 6870 if (name == NULL) { 6871 xo_failure(xop, "NULL passed for container name"); 6872 name = XO_FAILURE_NAME; 6873 } 6874 6875 flags |= xop->xo_flags; /* Pick up handle flags */ 6876 6877 switch (xo_style(xop)) { 6878 case XO_STYLE_XML: 6879 rc = xo_printf(xop, "%*s<%s", xo_indent(xop), "", name); 6880 6881 if (xop->xo_attrs.xb_curp != xop->xo_attrs.xb_bufp) { 6882 rc += xop->xo_attrs.xb_curp - xop->xo_attrs.xb_bufp; 6883 xo_data_append(xop, xop->xo_attrs.xb_bufp, 6884 xop->xo_attrs.xb_curp - xop->xo_attrs.xb_bufp); 6885 xop->xo_attrs.xb_curp = xop->xo_attrs.xb_bufp; 6886 } 6887 6888 rc += xo_printf(xop, ">%s", ppn); 6889 break; 6890 6891 case XO_STYLE_JSON: 6892 xo_stack_set_flags(xop); 6893 6894 if (!XOF_ISSET(xop, XOF_NO_TOP) 6895 && !XOIF_ISSET(xop, XOIF_TOP_EMITTED)) 6896 xo_emit_top(xop, ppn); 6897 6898 if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST) 6899 pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? ",\n" : ", "; 6900 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 6901 6902 rc = xo_printf(xop, "%s%*s\"%s\": {%s", 6903 pre_nl, xo_indent(xop), "", name, ppn); 6904 break; 6905 6906 case XO_STYLE_SDPARAMS: 6907 break; 6908 6909 case XO_STYLE_ENCODER: 6910 rc = xo_encoder_handle(xop, XO_OP_OPEN_CONTAINER, name, NULL, flags); 6911 break; 6912 } 6913 6914 xo_depth_change(xop, name, 1, 1, XSS_OPEN_CONTAINER, 6915 xo_stack_flags(flags)); 6916 6917 return rc; 6918} 6919 6920static int 6921xo_open_container_hf (xo_handle_t *xop, xo_xof_flags_t flags, const char *name) 6922{ 6923 return xo_transition(xop, flags, name, XSS_OPEN_CONTAINER); 6924} 6925 6926xo_ssize_t 6927xo_open_container_h (xo_handle_t *xop, const char *name) 6928{ 6929 return xo_open_container_hf(xop, 0, name); 6930} 6931 6932xo_ssize_t 6933xo_open_container (const char *name) 6934{ 6935 return xo_open_container_hf(NULL, 0, name); 6936} 6937 6938xo_ssize_t 6939xo_open_container_hd (xo_handle_t *xop, const char *name) 6940{ 6941 return xo_open_container_hf(xop, XOF_DTRT, name); 6942} 6943 6944xo_ssize_t 6945xo_open_container_d (const char *name) 6946{ 6947 return xo_open_container_hf(NULL, XOF_DTRT, name); 6948} 6949 6950static int 6951xo_do_close_container (xo_handle_t *xop, const char *name) 6952{ 6953 xop = xo_default(xop); 6954 6955 ssize_t rc = 0; 6956 const char *ppn = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : ""; 6957 const char *pre_nl = ""; 6958 6959 if (name == NULL) { 6960 xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth]; 6961 6962 name = xsp->xs_name; 6963 if (name) { 6964 ssize_t len = strlen(name) + 1; 6965 /* We need to make a local copy; xo_depth_change will free it */ 6966 char *cp = alloca(len); 6967 memcpy(cp, name, len); 6968 name = cp; 6969 } else if (!(xsp->xs_flags & XSF_DTRT)) { 6970 xo_failure(xop, "missing name without 'dtrt' mode"); 6971 name = XO_FAILURE_NAME; 6972 } 6973 } 6974 6975 switch (xo_style(xop)) { 6976 case XO_STYLE_XML: 6977 xo_depth_change(xop, name, -1, -1, XSS_CLOSE_CONTAINER, 0); 6978 rc = xo_printf(xop, "%*s</%s>%s", xo_indent(xop), "", name, ppn); 6979 break; 6980 6981 case XO_STYLE_JSON: 6982 pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : ""; 6983 ppn = (xop->xo_depth <= 1) ? "\n" : ""; 6984 6985 xo_depth_change(xop, name, -1, -1, XSS_CLOSE_CONTAINER, 0); 6986 rc = xo_printf(xop, "%s%*s}%s", pre_nl, xo_indent(xop), "", ppn); 6987 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 6988 break; 6989 6990 case XO_STYLE_HTML: 6991 case XO_STYLE_TEXT: 6992 xo_depth_change(xop, name, -1, 0, XSS_CLOSE_CONTAINER, 0); 6993 break; 6994 6995 case XO_STYLE_SDPARAMS: 6996 break; 6997 6998 case XO_STYLE_ENCODER: 6999 xo_depth_change(xop, name, -1, 0, XSS_CLOSE_CONTAINER, 0); 7000 rc = xo_encoder_handle(xop, XO_OP_CLOSE_CONTAINER, name, NULL, 0); 7001 break; 7002 } 7003 7004 return rc; 7005} 7006 7007xo_ssize_t 7008xo_close_container_h (xo_handle_t *xop, const char *name) 7009{ 7010 return xo_transition(xop, 0, name, XSS_CLOSE_CONTAINER); 7011} 7012 7013xo_ssize_t 7014xo_close_container (const char *name) 7015{ 7016 return xo_close_container_h(NULL, name); 7017} 7018 7019xo_ssize_t 7020xo_close_container_hd (xo_handle_t *xop) 7021{ 7022 return xo_close_container_h(xop, NULL); 7023} 7024 7025xo_ssize_t 7026xo_close_container_d (void) 7027{ 7028 return xo_close_container_h(NULL, NULL); 7029} 7030 7031static int 7032xo_do_open_list (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name) 7033{ 7034 ssize_t rc = 0; 7035 int indent = 0; 7036 7037 xop = xo_default(xop); 7038 7039 const char *ppn = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : ""; 7040 const char *pre_nl = ""; 7041 7042 switch (xo_style(xop)) { 7043 case XO_STYLE_JSON: 7044 7045 indent = 1; 7046 if (!XOF_ISSET(xop, XOF_NO_TOP) 7047 && !XOIF_ISSET(xop, XOIF_TOP_EMITTED)) 7048 xo_emit_top(xop, ppn); 7049 7050 if (name == NULL) { 7051 xo_failure(xop, "NULL passed for list name"); 7052 name = XO_FAILURE_NAME; 7053 } 7054 7055 xo_stack_set_flags(xop); 7056 7057 if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST) 7058 pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? ",\n" : ", "; 7059 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 7060 7061 rc = xo_printf(xop, "%s%*s\"%s\": [%s", 7062 pre_nl, xo_indent(xop), "", name, ppn); 7063 break; 7064 7065 case XO_STYLE_ENCODER: 7066 rc = xo_encoder_handle(xop, XO_OP_OPEN_LIST, name, NULL, flags); 7067 break; 7068 } 7069 7070 xo_depth_change(xop, name, 1, indent, XSS_OPEN_LIST, 7071 XSF_LIST | xo_stack_flags(flags)); 7072 7073 return rc; 7074} 7075 7076static int 7077xo_open_list_hf (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name) 7078{ 7079 return xo_transition(xop, flags, name, XSS_OPEN_LIST); 7080} 7081 7082xo_ssize_t 7083xo_open_list_h (xo_handle_t *xop, const char *name) 7084{ 7085 return xo_open_list_hf(xop, 0, name); 7086} 7087 7088xo_ssize_t 7089xo_open_list (const char *name) 7090{ 7091 return xo_open_list_hf(NULL, 0, name); 7092} 7093 7094xo_ssize_t 7095xo_open_list_hd (xo_handle_t *xop, const char *name) 7096{ 7097 return xo_open_list_hf(xop, XOF_DTRT, name); 7098} 7099 7100xo_ssize_t 7101xo_open_list_d (const char *name) 7102{ 7103 return xo_open_list_hf(NULL, XOF_DTRT, name); 7104} 7105 7106static int 7107xo_do_close_list (xo_handle_t *xop, const char *name) 7108{ 7109 ssize_t rc = 0; 7110 const char *pre_nl = ""; 7111 7112 if (name == NULL) { 7113 xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth]; 7114 7115 name = xsp->xs_name; 7116 if (name) { 7117 ssize_t len = strlen(name) + 1; 7118 /* We need to make a local copy; xo_depth_change will free it */ 7119 char *cp = alloca(len); 7120 memcpy(cp, name, len); 7121 name = cp; 7122 } else if (!(xsp->xs_flags & XSF_DTRT)) { 7123 xo_failure(xop, "missing name without 'dtrt' mode"); 7124 name = XO_FAILURE_NAME; 7125 } 7126 } 7127 7128 switch (xo_style(xop)) { 7129 case XO_STYLE_JSON: 7130 if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST) 7131 pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : ""; 7132 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 7133 7134 xo_depth_change(xop, name, -1, -1, XSS_CLOSE_LIST, XSF_LIST); 7135 rc = xo_printf(xop, "%s%*s]", pre_nl, xo_indent(xop), ""); 7136 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 7137 break; 7138 7139 case XO_STYLE_ENCODER: 7140 xo_depth_change(xop, name, -1, 0, XSS_CLOSE_LIST, XSF_LIST); 7141 rc = xo_encoder_handle(xop, XO_OP_CLOSE_LIST, name, NULL, 0); 7142 break; 7143 7144 default: 7145 xo_depth_change(xop, name, -1, 0, XSS_CLOSE_LIST, XSF_LIST); 7146 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 7147 break; 7148 } 7149 7150 return rc; 7151} 7152 7153xo_ssize_t 7154xo_close_list_h (xo_handle_t *xop, const char *name) 7155{ 7156 return xo_transition(xop, 0, name, XSS_CLOSE_LIST); 7157} 7158 7159xo_ssize_t 7160xo_close_list (const char *name) 7161{ 7162 return xo_close_list_h(NULL, name); 7163} 7164 7165xo_ssize_t 7166xo_close_list_hd (xo_handle_t *xop) 7167{ 7168 return xo_close_list_h(xop, NULL); 7169} 7170 7171xo_ssize_t 7172xo_close_list_d (void) 7173{ 7174 return xo_close_list_h(NULL, NULL); 7175} 7176 7177static int 7178xo_do_open_leaf_list (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name) 7179{ 7180 ssize_t rc = 0; 7181 int indent = 0; 7182 7183 xop = xo_default(xop); 7184 7185 const char *ppn = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : ""; 7186 const char *pre_nl = ""; 7187 7188 switch (xo_style(xop)) { 7189 case XO_STYLE_JSON: 7190 indent = 1; 7191 7192 if (!XOF_ISSET(xop, XOF_NO_TOP)) { 7193 if (!XOIF_ISSET(xop, XOIF_TOP_EMITTED)) { 7194 xo_printf(xop, "%*s{%s", xo_indent(xop), "", ppn); 7195 XOIF_SET(xop, XOIF_TOP_EMITTED); 7196 } 7197 } 7198 7199 if (name == NULL) { 7200 xo_failure(xop, "NULL passed for list name"); 7201 name = XO_FAILURE_NAME; 7202 } 7203 7204 xo_stack_set_flags(xop); 7205 7206 if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST) 7207 pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? ",\n" : ", "; 7208 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 7209 7210 rc = xo_printf(xop, "%s%*s\"%s\": [%s", 7211 pre_nl, xo_indent(xop), "", name, ppn); 7212 break; 7213 7214 case XO_STYLE_ENCODER: 7215 rc = xo_encoder_handle(xop, XO_OP_OPEN_LEAF_LIST, name, NULL, flags); 7216 break; 7217 } 7218 7219 xo_depth_change(xop, name, 1, indent, XSS_OPEN_LEAF_LIST, 7220 XSF_LIST | xo_stack_flags(flags)); 7221 7222 return rc; 7223} 7224 7225static int 7226xo_do_close_leaf_list (xo_handle_t *xop, const char *name) 7227{ 7228 ssize_t rc = 0; 7229 const char *pre_nl = ""; 7230 7231 if (name == NULL) { 7232 xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth]; 7233 7234 name = xsp->xs_name; 7235 if (name) { 7236 ssize_t len = strlen(name) + 1; 7237 /* We need to make a local copy; xo_depth_change will free it */ 7238 char *cp = alloca(len); 7239 memcpy(cp, name, len); 7240 name = cp; 7241 } else if (!(xsp->xs_flags & XSF_DTRT)) { 7242 xo_failure(xop, "missing name without 'dtrt' mode"); 7243 name = XO_FAILURE_NAME; 7244 } 7245 } 7246 7247 switch (xo_style(xop)) { 7248 case XO_STYLE_JSON: 7249 if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST) 7250 pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : ""; 7251 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 7252 7253 xo_depth_change(xop, name, -1, -1, XSS_CLOSE_LEAF_LIST, XSF_LIST); 7254 rc = xo_printf(xop, "%s%*s]", pre_nl, xo_indent(xop), ""); 7255 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 7256 break; 7257 7258 case XO_STYLE_ENCODER: 7259 rc = xo_encoder_handle(xop, XO_OP_CLOSE_LEAF_LIST, name, NULL, 0); 7260 /* FALLTHRU */ 7261 7262 default: 7263 xo_depth_change(xop, name, -1, 0, XSS_CLOSE_LEAF_LIST, XSF_LIST); 7264 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 7265 break; 7266 } 7267 7268 return rc; 7269} 7270 7271static int 7272xo_do_open_instance (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name) 7273{ 7274 xop = xo_default(xop); 7275 7276 ssize_t rc = 0; 7277 const char *ppn = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : ""; 7278 const char *pre_nl = ""; 7279 7280 flags |= xop->xo_flags; 7281 7282 if (name == NULL) { 7283 xo_failure(xop, "NULL passed for instance name"); 7284 name = XO_FAILURE_NAME; 7285 } 7286 7287 switch (xo_style(xop)) { 7288 case XO_STYLE_XML: 7289 rc = xo_printf(xop, "%*s<%s", xo_indent(xop), "", name); 7290 7291 if (xop->xo_attrs.xb_curp != xop->xo_attrs.xb_bufp) { 7292 rc += xop->xo_attrs.xb_curp - xop->xo_attrs.xb_bufp; 7293 xo_data_append(xop, xop->xo_attrs.xb_bufp, 7294 xop->xo_attrs.xb_curp - xop->xo_attrs.xb_bufp); 7295 xop->xo_attrs.xb_curp = xop->xo_attrs.xb_bufp; 7296 } 7297 7298 rc += xo_printf(xop, ">%s", ppn); 7299 break; 7300 7301 case XO_STYLE_JSON: 7302 xo_stack_set_flags(xop); 7303 7304 if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST) 7305 pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? ",\n" : ", "; 7306 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 7307 7308 rc = xo_printf(xop, "%s%*s{%s", 7309 pre_nl, xo_indent(xop), "", ppn); 7310 break; 7311 7312 case XO_STYLE_SDPARAMS: 7313 break; 7314 7315 case XO_STYLE_ENCODER: 7316 rc = xo_encoder_handle(xop, XO_OP_OPEN_INSTANCE, name, NULL, flags); 7317 break; 7318 } 7319 7320 xo_depth_change(xop, name, 1, 1, XSS_OPEN_INSTANCE, xo_stack_flags(flags)); 7321 7322 return rc; 7323} 7324 7325static int 7326xo_open_instance_hf (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name) 7327{ 7328 return xo_transition(xop, flags, name, XSS_OPEN_INSTANCE); 7329} 7330 7331xo_ssize_t 7332xo_open_instance_h (xo_handle_t *xop, const char *name) 7333{ 7334 return xo_open_instance_hf(xop, 0, name); 7335} 7336 7337xo_ssize_t 7338xo_open_instance (const char *name) 7339{ 7340 return xo_open_instance_hf(NULL, 0, name); 7341} 7342 7343xo_ssize_t 7344xo_open_instance_hd (xo_handle_t *xop, const char *name) 7345{ 7346 return xo_open_instance_hf(xop, XOF_DTRT, name); 7347} 7348 7349xo_ssize_t 7350xo_open_instance_d (const char *name) 7351{ 7352 return xo_open_instance_hf(NULL, XOF_DTRT, name); 7353} 7354 7355static int 7356xo_do_close_instance (xo_handle_t *xop, const char *name) 7357{ 7358 xop = xo_default(xop); 7359 7360 ssize_t rc = 0; 7361 const char *ppn = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : ""; 7362 const char *pre_nl = ""; 7363 7364 if (name == NULL) { 7365 xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth]; 7366 7367 name = xsp->xs_name; 7368 if (name) { 7369 ssize_t len = strlen(name) + 1; 7370 /* We need to make a local copy; xo_depth_change will free it */ 7371 char *cp = alloca(len); 7372 memcpy(cp, name, len); 7373 name = cp; 7374 } else if (!(xsp->xs_flags & XSF_DTRT)) { 7375 xo_failure(xop, "missing name without 'dtrt' mode"); 7376 name = XO_FAILURE_NAME; 7377 } 7378 } 7379 7380 switch (xo_style(xop)) { 7381 case XO_STYLE_XML: 7382 xo_depth_change(xop, name, -1, -1, XSS_CLOSE_INSTANCE, 0); 7383 rc = xo_printf(xop, "%*s</%s>%s", xo_indent(xop), "", name, ppn); 7384 break; 7385 7386 case XO_STYLE_JSON: 7387 pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : ""; 7388 7389 xo_depth_change(xop, name, -1, -1, XSS_CLOSE_INSTANCE, 0); 7390 rc = xo_printf(xop, "%s%*s}", pre_nl, xo_indent(xop), ""); 7391 xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 7392 break; 7393 7394 case XO_STYLE_HTML: 7395 case XO_STYLE_TEXT: 7396 xo_depth_change(xop, name, -1, 0, XSS_CLOSE_INSTANCE, 0); 7397 break; 7398 7399 case XO_STYLE_SDPARAMS: 7400 break; 7401 7402 case XO_STYLE_ENCODER: 7403 xo_depth_change(xop, name, -1, 0, XSS_CLOSE_INSTANCE, 0); 7404 rc = xo_encoder_handle(xop, XO_OP_CLOSE_INSTANCE, name, NULL, 0); 7405 break; 7406 } 7407 7408 return rc; 7409} 7410 7411xo_ssize_t 7412xo_close_instance_h (xo_handle_t *xop, const char *name) 7413{ 7414 return xo_transition(xop, 0, name, XSS_CLOSE_INSTANCE); 7415} 7416 7417xo_ssize_t 7418xo_close_instance (const char *name) 7419{ 7420 return xo_close_instance_h(NULL, name); 7421} 7422 7423xo_ssize_t 7424xo_close_instance_hd (xo_handle_t *xop) 7425{ 7426 return xo_close_instance_h(xop, NULL); 7427} 7428 7429xo_ssize_t 7430xo_close_instance_d (void) 7431{ 7432 return xo_close_instance_h(NULL, NULL); 7433} 7434 7435static int 7436xo_do_close_all (xo_handle_t *xop, xo_stack_t *limit) 7437{ 7438 xo_stack_t *xsp; 7439 ssize_t rc = 0; 7440 xo_xsf_flags_t flags; 7441 7442 for (xsp = &xop->xo_stack[xop->xo_depth]; xsp >= limit; xsp--) { 7443 switch (xsp->xs_state) { 7444 case XSS_INIT: 7445 /* Nothing */ 7446 rc = 0; 7447 break; 7448 7449 case XSS_OPEN_CONTAINER: 7450 rc = xo_do_close_container(xop, NULL); 7451 break; 7452 7453 case XSS_OPEN_LIST: 7454 rc = xo_do_close_list(xop, NULL); 7455 break; 7456 7457 case XSS_OPEN_INSTANCE: 7458 rc = xo_do_close_instance(xop, NULL); 7459 break; 7460 7461 case XSS_OPEN_LEAF_LIST: 7462 rc = xo_do_close_leaf_list(xop, NULL); 7463 break; 7464 7465 case XSS_MARKER: 7466 flags = xsp->xs_flags & XSF_MARKER_FLAGS; 7467 xo_depth_change(xop, xsp->xs_name, -1, 0, XSS_MARKER, 0); 7468 xop->xo_stack[xop->xo_depth].xs_flags |= flags; 7469 rc = 0; 7470 break; 7471 } 7472 7473 if (rc < 0) 7474 xo_failure(xop, "close %d failed: %d", xsp->xs_state, rc); 7475 } 7476 7477 return 0; 7478} 7479 7480/* 7481 * This function is responsible for clearing out whatever is needed 7482 * to get to the desired state, if possible. 7483 */ 7484static int 7485xo_do_close (xo_handle_t *xop, const char *name, xo_state_t new_state) 7486{ 7487 xo_stack_t *xsp, *limit = NULL; 7488 ssize_t rc; 7489 xo_state_t need_state = new_state; 7490 7491 if (new_state == XSS_CLOSE_CONTAINER) 7492 need_state = XSS_OPEN_CONTAINER; 7493 else if (new_state == XSS_CLOSE_LIST) 7494 need_state = XSS_OPEN_LIST; 7495 else if (new_state == XSS_CLOSE_INSTANCE) 7496 need_state = XSS_OPEN_INSTANCE; 7497 else if (new_state == XSS_CLOSE_LEAF_LIST) 7498 need_state = XSS_OPEN_LEAF_LIST; 7499 else if (new_state == XSS_MARKER) 7500 need_state = XSS_MARKER; 7501 else 7502 return 0; /* Unknown or useless new states are ignored */ 7503 7504 for (xsp = &xop->xo_stack[xop->xo_depth]; xsp > xop->xo_stack; xsp--) { 7505 /* 7506 * Marker's normally stop us from going any further, unless 7507 * we are popping a marker (new_state == XSS_MARKER). 7508 */ 7509 if (xsp->xs_state == XSS_MARKER && need_state != XSS_MARKER) { 7510 if (name) { 7511 xo_failure(xop, "close (xo_%s) fails at marker '%s'; " 7512 "not found '%s'", 7513 xo_state_name(new_state), 7514 xsp->xs_name, name); 7515 return 0; 7516 7517 } else { 7518 limit = xsp; 7519 xo_failure(xop, "close stops at marker '%s'", xsp->xs_name); 7520 } 7521 break; 7522 } 7523 7524 if (xsp->xs_state != need_state) 7525 continue; 7526 7527 if (name && xsp->xs_name && strcmp(name, xsp->xs_name) != 0) 7528 continue; 7529 7530 limit = xsp; 7531 break; 7532 } 7533 7534 if (limit == NULL) { 7535 xo_failure(xop, "xo_%s can't find match for '%s'", 7536 xo_state_name(new_state), name); 7537 return 0; 7538 } 7539 7540 rc = xo_do_close_all(xop, limit); 7541 7542 return rc; 7543} 7544 7545/* 7546 * We are in a given state and need to transition to the new state. 7547 */ 7548static ssize_t 7549xo_transition (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name, 7550 xo_state_t new_state) 7551{ 7552 xo_stack_t *xsp; 7553 ssize_t rc = 0; 7554 int old_state, on_marker; 7555 7556 xop = xo_default(xop); 7557 7558 xsp = &xop->xo_stack[xop->xo_depth]; 7559 old_state = xsp->xs_state; 7560 on_marker = (old_state == XSS_MARKER); 7561 7562 /* If there's a marker on top of the stack, we need to find a real state */ 7563 while (old_state == XSS_MARKER) { 7564 if (xsp == xop->xo_stack) 7565 break; 7566 xsp -= 1; 7567 old_state = xsp->xs_state; 7568 } 7569 7570 /* 7571 * At this point, the list of possible states are: 7572 * XSS_INIT, XSS_OPEN_CONTAINER, XSS_OPEN_LIST, 7573 * XSS_OPEN_INSTANCE, XSS_OPEN_LEAF_LIST, XSS_DISCARDING 7574 */ 7575 switch (XSS_TRANSITION(old_state, new_state)) { 7576 7577 open_container: 7578 case XSS_TRANSITION(XSS_INIT, XSS_OPEN_CONTAINER): 7579 case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_OPEN_CONTAINER): 7580 case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_OPEN_CONTAINER): 7581 rc = xo_do_open_container(xop, flags, name); 7582 break; 7583 7584 case XSS_TRANSITION(XSS_OPEN_LIST, XSS_OPEN_CONTAINER): 7585 if (on_marker) 7586 goto marker_prevents_close; 7587 rc = xo_do_close_list(xop, NULL); 7588 if (rc >= 0) 7589 goto open_container; 7590 break; 7591 7592 case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_OPEN_CONTAINER): 7593 if (on_marker) 7594 goto marker_prevents_close; 7595 rc = xo_do_close_leaf_list(xop, NULL); 7596 if (rc >= 0) 7597 goto open_container; 7598 break; 7599 7600 /*close_container:*/ 7601 case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_CLOSE_CONTAINER): 7602 if (on_marker) 7603 goto marker_prevents_close; 7604 rc = xo_do_close(xop, name, new_state); 7605 break; 7606 7607 case XSS_TRANSITION(XSS_INIT, XSS_CLOSE_CONTAINER): 7608 /* This is an exception for "xo --close" */ 7609 rc = xo_do_close_container(xop, name); 7610 break; 7611 7612 case XSS_TRANSITION(XSS_OPEN_LIST, XSS_CLOSE_CONTAINER): 7613 case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_CLOSE_CONTAINER): 7614 if (on_marker) 7615 goto marker_prevents_close; 7616 rc = xo_do_close(xop, name, new_state); 7617 break; 7618 7619 case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_CLOSE_CONTAINER): 7620 if (on_marker) 7621 goto marker_prevents_close; 7622 rc = xo_do_close_leaf_list(xop, NULL); 7623 if (rc >= 0) 7624 rc = xo_do_close(xop, name, new_state); 7625 break; 7626 7627 open_list: 7628 case XSS_TRANSITION(XSS_INIT, XSS_OPEN_LIST): 7629 case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_OPEN_LIST): 7630 case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_OPEN_LIST): 7631 rc = xo_do_open_list(xop, flags, name); 7632 break; 7633 7634 case XSS_TRANSITION(XSS_OPEN_LIST, XSS_OPEN_LIST): 7635 if (on_marker) 7636 goto marker_prevents_close; 7637 rc = xo_do_close_list(xop, NULL); 7638 if (rc >= 0) 7639 goto open_list; 7640 break; 7641 7642 case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_OPEN_LIST): 7643 if (on_marker) 7644 goto marker_prevents_close; 7645 rc = xo_do_close_leaf_list(xop, NULL); 7646 if (rc >= 0) 7647 goto open_list; 7648 break; 7649 7650 /*close_list:*/ 7651 case XSS_TRANSITION(XSS_OPEN_LIST, XSS_CLOSE_LIST): 7652 if (on_marker) 7653 goto marker_prevents_close; 7654 rc = xo_do_close(xop, name, new_state); 7655 break; 7656 7657 case XSS_TRANSITION(XSS_INIT, XSS_CLOSE_LIST): 7658 case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_CLOSE_LIST): 7659 case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_CLOSE_LIST): 7660 case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_CLOSE_LIST): 7661 rc = xo_do_close(xop, name, new_state); 7662 break; 7663 7664 open_instance: 7665 case XSS_TRANSITION(XSS_OPEN_LIST, XSS_OPEN_INSTANCE): 7666 rc = xo_do_open_instance(xop, flags, name); 7667 break; 7668 7669 case XSS_TRANSITION(XSS_INIT, XSS_OPEN_INSTANCE): 7670 case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_OPEN_INSTANCE): 7671 rc = xo_do_open_list(xop, flags, name); 7672 if (rc >= 0) 7673 goto open_instance; 7674 break; 7675 7676 case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_OPEN_INSTANCE): 7677 if (on_marker) { 7678 rc = xo_do_open_list(xop, flags, name); 7679 } else { 7680 rc = xo_do_close_instance(xop, NULL); 7681 } 7682 if (rc >= 0) 7683 goto open_instance; 7684 break; 7685 7686 case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_OPEN_INSTANCE): 7687 if (on_marker) 7688 goto marker_prevents_close; 7689 rc = xo_do_close_leaf_list(xop, NULL); 7690 if (rc >= 0) 7691 goto open_instance; 7692 break; 7693 7694 /*close_instance:*/ 7695 case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_CLOSE_INSTANCE): 7696 if (on_marker) 7697 goto marker_prevents_close; 7698 rc = xo_do_close_instance(xop, name); 7699 break; 7700 7701 case XSS_TRANSITION(XSS_INIT, XSS_CLOSE_INSTANCE): 7702 /* This one makes no sense; ignore it */ 7703 xo_failure(xop, "xo_close_instance ignored when called from " 7704 "initial state ('%s')", name ?: "(unknown)"); 7705 break; 7706 7707 case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_CLOSE_INSTANCE): 7708 case XSS_TRANSITION(XSS_OPEN_LIST, XSS_CLOSE_INSTANCE): 7709 if (on_marker) 7710 goto marker_prevents_close; 7711 rc = xo_do_close(xop, name, new_state); 7712 break; 7713 7714 case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_CLOSE_INSTANCE): 7715 if (on_marker) 7716 goto marker_prevents_close; 7717 rc = xo_do_close_leaf_list(xop, NULL); 7718 if (rc >= 0) 7719 rc = xo_do_close(xop, name, new_state); 7720 break; 7721 7722 open_leaf_list: 7723 case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_OPEN_LEAF_LIST): 7724 case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_OPEN_LEAF_LIST): 7725 case XSS_TRANSITION(XSS_INIT, XSS_OPEN_LEAF_LIST): 7726 rc = xo_do_open_leaf_list(xop, flags, name); 7727 break; 7728 7729 case XSS_TRANSITION(XSS_OPEN_LIST, XSS_OPEN_LEAF_LIST): 7730 case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_OPEN_LEAF_LIST): 7731 if (on_marker) 7732 goto marker_prevents_close; 7733 rc = xo_do_close_list(xop, NULL); 7734 if (rc >= 0) 7735 goto open_leaf_list; 7736 break; 7737 7738 /*close_leaf_list:*/ 7739 case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_CLOSE_LEAF_LIST): 7740 if (on_marker) 7741 goto marker_prevents_close; 7742 rc = xo_do_close_leaf_list(xop, name); 7743 break; 7744 7745 case XSS_TRANSITION(XSS_INIT, XSS_CLOSE_LEAF_LIST): 7746 /* Makes no sense; ignore */ 7747 xo_failure(xop, "xo_close_leaf_list ignored when called from " 7748 "initial state ('%s')", name ?: "(unknown)"); 7749 break; 7750 7751 case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_CLOSE_LEAF_LIST): 7752 case XSS_TRANSITION(XSS_OPEN_LIST, XSS_CLOSE_LEAF_LIST): 7753 case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_CLOSE_LEAF_LIST): 7754 if (on_marker) 7755 goto marker_prevents_close; 7756 rc = xo_do_close(xop, name, new_state); 7757 break; 7758 7759 /*emit:*/ 7760 case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_EMIT): 7761 case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_EMIT): 7762 break; 7763 7764 case XSS_TRANSITION(XSS_OPEN_LIST, XSS_EMIT): 7765 if (on_marker) 7766 goto marker_prevents_close; 7767 rc = xo_do_close(xop, NULL, XSS_CLOSE_LIST); 7768 break; 7769 7770 case XSS_TRANSITION(XSS_INIT, XSS_EMIT): 7771 break; 7772 7773 case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_EMIT): 7774 if (on_marker) 7775 goto marker_prevents_close; 7776 rc = xo_do_close_leaf_list(xop, NULL); 7777 break; 7778 7779 /*emit_leaf_list:*/ 7780 case XSS_TRANSITION(XSS_INIT, XSS_EMIT_LEAF_LIST): 7781 case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_EMIT_LEAF_LIST): 7782 case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_EMIT_LEAF_LIST): 7783 rc = xo_do_open_leaf_list(xop, flags, name); 7784 break; 7785 7786 case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_EMIT_LEAF_LIST): 7787 break; 7788 7789 case XSS_TRANSITION(XSS_OPEN_LIST, XSS_EMIT_LEAF_LIST): 7790 /* 7791 * We need to be backward compatible with the pre-xo_open_leaf_list 7792 * API, where both lists and leaf-lists were opened as lists. So 7793 * if we find an open list that hasn't had anything written to it, 7794 * we'll accept it. 7795 */ 7796 break; 7797 7798 default: 7799 xo_failure(xop, "unknown transition: (%u -> %u)", 7800 xsp->xs_state, new_state); 7801 } 7802 7803 /* Handle the flush flag */ 7804 if (rc >= 0 && XOF_ISSET(xop, XOF_FLUSH)) 7805 if (xo_flush_h(xop)) 7806 rc = -1; 7807 7808 return rc; 7809 7810 marker_prevents_close: 7811 xo_failure(xop, "marker '%s' prevents transition from %s to %s", 7812 xop->xo_stack[xop->xo_depth].xs_name, 7813 xo_state_name(old_state), xo_state_name(new_state)); 7814 return -1; 7815} 7816 7817xo_ssize_t 7818xo_open_marker_h (xo_handle_t *xop, const char *name) 7819{ 7820 xop = xo_default(xop); 7821 7822 xo_depth_change(xop, name, 1, 0, XSS_MARKER, 7823 xop->xo_stack[xop->xo_depth].xs_flags & XSF_MARKER_FLAGS); 7824 7825 return 0; 7826} 7827 7828xo_ssize_t 7829xo_open_marker (const char *name) 7830{ 7831 return xo_open_marker_h(NULL, name); 7832} 7833 7834xo_ssize_t 7835xo_close_marker_h (xo_handle_t *xop, const char *name) 7836{ 7837 xop = xo_default(xop); 7838 7839 return xo_do_close(xop, name, XSS_MARKER); 7840} 7841 7842xo_ssize_t 7843xo_close_marker (const char *name) 7844{ 7845 return xo_close_marker_h(NULL, name); 7846} 7847 7848/* 7849 * Record custom output functions into the xo handle, allowing 7850 * integration with a variety of output frameworks. 7851 */ 7852void 7853xo_set_writer (xo_handle_t *xop, void *opaque, xo_write_func_t write_func, 7854 xo_close_func_t close_func, xo_flush_func_t flush_func) 7855{ 7856 xop = xo_default(xop); 7857 7858 xop->xo_opaque = opaque; 7859 xop->xo_write = write_func; 7860 xop->xo_close = close_func; 7861 xop->xo_flush = flush_func; 7862} 7863 7864void 7865xo_set_allocator (xo_realloc_func_t realloc_func, xo_free_func_t free_func) 7866{ 7867 xo_realloc = realloc_func; 7868 xo_free = free_func; 7869} 7870 7871xo_ssize_t 7872xo_flush_h (xo_handle_t *xop) 7873{ 7874 ssize_t rc; 7875 7876 xop = xo_default(xop); 7877 7878 switch (xo_style(xop)) { 7879 case XO_STYLE_ENCODER: 7880 xo_encoder_handle(xop, XO_OP_FLUSH, NULL, NULL, 0); 7881 } 7882 7883 rc = xo_write(xop); 7884 if (rc >= 0 && xop->xo_flush) 7885 if (xop->xo_flush(xop->xo_opaque) < 0) 7886 return -1; 7887 7888 return rc; 7889} 7890 7891xo_ssize_t 7892xo_flush (void) 7893{ 7894 return xo_flush_h(NULL); 7895} 7896 7897xo_ssize_t 7898xo_finish_h (xo_handle_t *xop) 7899{ 7900 const char *cp = ""; 7901 xop = xo_default(xop); 7902 7903 if (!XOF_ISSET(xop, XOF_NO_CLOSE)) 7904 xo_do_close_all(xop, xop->xo_stack); 7905 7906 switch (xo_style(xop)) { 7907 case XO_STYLE_JSON: 7908 if (!XOF_ISSET(xop, XOF_NO_TOP)) { 7909 if (XOIF_ISSET(xop, XOIF_TOP_EMITTED)) 7910 XOIF_CLEAR(xop, XOIF_TOP_EMITTED); /* Turn off before output */ 7911 else 7912 cp = "{ "; 7913 xo_printf(xop, "%*s%s}\n",xo_indent(xop), "", cp); 7914 } 7915 break; 7916 7917 case XO_STYLE_ENCODER: 7918 xo_encoder_handle(xop, XO_OP_FINISH, NULL, NULL, 0); 7919 break; 7920 } 7921 7922 return xo_flush_h(xop); 7923} 7924 7925xo_ssize_t 7926xo_finish (void) 7927{ 7928 return xo_finish_h(NULL); 7929} 7930 7931/* 7932 * xo_finish_atexit is suitable for atexit() calls, to force clear up 7933 * and finalizing output. 7934 */ 7935void 7936xo_finish_atexit (void) 7937{ 7938 (void) xo_finish_h(NULL); 7939} 7940 7941/* 7942 * Generate an error message, such as would be displayed on stderr 7943 */ 7944void 7945xo_error_hv (xo_handle_t *xop, const char *fmt, va_list vap) 7946{ 7947 xop = xo_default(xop); 7948 7949 /* 7950 * If the format string doesn't end with a newline, we pop 7951 * one on ourselves. 7952 */ 7953 ssize_t len = strlen(fmt); 7954 if (len > 0 && fmt[len - 1] != '\n') { 7955 char *newfmt = alloca(len + 2); 7956 memcpy(newfmt, fmt, len); 7957 newfmt[len] = '\n'; 7958 newfmt[len] = '\0'; 7959 fmt = newfmt; 7960 } 7961 7962 switch (xo_style(xop)) { 7963 case XO_STYLE_TEXT: 7964 vfprintf(stderr, fmt, vap); 7965 break; 7966 7967 case XO_STYLE_HTML: 7968 va_copy(xop->xo_vap, vap); 7969 7970 xo_buf_append_div(xop, "error", 0, NULL, 0, NULL, 0, 7971 fmt, strlen(fmt), NULL, 0); 7972 7973 if (XOIF_ISSET(xop, XOIF_DIV_OPEN)) 7974 xo_line_close(xop); 7975 7976 xo_write(xop); 7977 7978 va_end(xop->xo_vap); 7979 bzero(&xop->xo_vap, sizeof(xop->xo_vap)); 7980 break; 7981 7982 case XO_STYLE_XML: 7983 case XO_STYLE_JSON: 7984 va_copy(xop->xo_vap, vap); 7985 7986 xo_open_container_h(xop, "error"); 7987 xo_format_value(xop, "message", 7, NULL, 0, 7988 fmt, strlen(fmt), NULL, 0, 0); 7989 xo_close_container_h(xop, "error"); 7990 7991 va_end(xop->xo_vap); 7992 bzero(&xop->xo_vap, sizeof(xop->xo_vap)); 7993 break; 7994 7995 case XO_STYLE_SDPARAMS: 7996 case XO_STYLE_ENCODER: 7997 break; 7998 } 7999} 8000 8001void 8002xo_error_h (xo_handle_t *xop, const char *fmt, ...) 8003{ 8004 va_list vap; 8005 8006 va_start(vap, fmt); 8007 xo_error_hv(xop, fmt, vap); 8008 va_end(vap); 8009} 8010 8011/* 8012 * Generate an error message, such as would be displayed on stderr 8013 */ 8014void 8015xo_error (const char *fmt, ...) 8016{ 8017 va_list vap; 8018 8019 va_start(vap, fmt); 8020 xo_error_hv(NULL, fmt, vap); 8021 va_end(vap); 8022} 8023 8024/* 8025 * Parse any libxo-specific options from the command line, removing them 8026 * so the main() argument parsing won't see them. We return the new value 8027 * for argc or -1 for error. If an error occurred, the program should 8028 * exit. A suitable error message has already been displayed. 8029 */ 8030int 8031xo_parse_args (int argc, char **argv) 8032{ 8033 static char libxo_opt[] = "--libxo"; 8034 char *cp; 8035 int i, save; 8036 8037 /* Save our program name for xo_err and friends */ 8038 xo_program = argv[0]; 8039 cp = strrchr(xo_program, '/'); 8040 if (cp) 8041 xo_program = cp + 1; 8042 8043 xo_handle_t *xop = xo_default(NULL); 8044 8045 for (save = i = 1; i < argc; i++) { 8046 if (argv[i] == NULL 8047 || strncmp(argv[i], libxo_opt, sizeof(libxo_opt) - 1) != 0) { 8048 if (save != i) 8049 argv[save] = argv[i]; 8050 save += 1; 8051 continue; 8052 } 8053 8054 cp = argv[i] + sizeof(libxo_opt) - 1; 8055 if (*cp == '\0') { 8056 cp = argv[++i]; 8057 if (cp == NULL) { 8058 xo_warnx("missing libxo option"); 8059 return -1; 8060 } 8061 8062 if (xo_set_options(xop, cp) < 0) 8063 return -1; 8064 } else if (*cp == ':') { 8065 if (xo_set_options(xop, cp) < 0) 8066 return -1; 8067 8068 } else if (*cp == '=') { 8069 if (xo_set_options(xop, ++cp) < 0) 8070 return -1; 8071 8072 } else if (*cp == '-') { 8073 cp += 1; 8074 if (strcmp(cp, "check") == 0) { 8075 exit(XO_HAS_LIBXO); 8076 8077 } else { 8078 xo_warnx("unknown libxo option: '%s'", argv[i]); 8079 return -1; 8080 } 8081 } else { 8082 xo_warnx("unknown libxo option: '%s'", argv[i]); 8083 return -1; 8084 } 8085 } 8086 8087 /* 8088 * We only want to do color output on terminals, but we only want 8089 * to do this if the user has asked for color. 8090 */ 8091 if (XOF_ISSET(xop, XOF_COLOR_ALLOWED) && isatty(1)) 8092 XOF_SET(xop, XOF_COLOR); 8093 8094 argv[save] = NULL; 8095 return save; 8096} 8097 8098/* 8099 * Debugging function that dumps the current stack of open libxo constructs, 8100 * suitable for calling from the debugger. 8101 */ 8102void 8103xo_dump_stack (xo_handle_t *xop) 8104{ 8105 int i; 8106 xo_stack_t *xsp; 8107 8108 xop = xo_default(xop); 8109 8110 fprintf(stderr, "Stack dump:\n"); 8111 8112 xsp = xop->xo_stack; 8113 for (i = 1, xsp++; i <= xop->xo_depth; i++, xsp++) { 8114 fprintf(stderr, " [%d] %s '%s' [%x]\n", 8115 i, xo_state_name(xsp->xs_state), 8116 xsp->xs_name ?: "--", xsp->xs_flags); 8117 } 8118} 8119 8120/* 8121 * Record the program name used for error messages 8122 */ 8123void 8124xo_set_program (const char *name) 8125{ 8126 xo_program = name; 8127} 8128 8129void 8130xo_set_version_h (xo_handle_t *xop, const char *version) 8131{ 8132 xop = xo_default(xop); 8133 8134 if (version == NULL || strchr(version, '"') != NULL) 8135 return; 8136 8137 if (!xo_style_is_encoding(xop)) 8138 return; 8139 8140 switch (xo_style(xop)) { 8141 case XO_STYLE_XML: 8142 /* For XML, we record this as an attribute for the first tag */ 8143 xo_attr_h(xop, "version", "%s", version); 8144 break; 8145 8146 case XO_STYLE_JSON: 8147 /* 8148 * For JSON, we record the version string in our handle, and emit 8149 * it in xo_emit_top. 8150 */ 8151 xop->xo_version = xo_strndup(version, -1); 8152 break; 8153 8154 case XO_STYLE_ENCODER: 8155 xo_encoder_handle(xop, XO_OP_VERSION, NULL, version, 0); 8156 break; 8157 } 8158} 8159 8160/* 8161 * Set the version number for the API content being carried through 8162 * the xo handle. 8163 */ 8164void 8165xo_set_version (const char *version) 8166{ 8167 xo_set_version_h(NULL, version); 8168} 8169 8170/* 8171 * Generate a warning. Normally, this is a text message written to 8172 * standard error. If the XOF_WARN_XML flag is set, then we generate 8173 * XMLified content on standard output. 8174 */ 8175void 8176xo_emit_warn_hcv (xo_handle_t *xop, int as_warning, int code, 8177 const char *fmt, va_list vap) 8178{ 8179 xop = xo_default(xop); 8180 8181 if (fmt == NULL) 8182 return; 8183 8184 xo_open_marker_h(xop, "xo_emit_warn_hcv"); 8185 xo_open_container_h(xop, as_warning ? "__warning" : "__error"); 8186 8187 if (xo_program) 8188 xo_emit("{wc:program}", xo_program); 8189 8190 if (xo_style(xop) == XO_STYLE_XML || xo_style(xop) == XO_STYLE_JSON) { 8191 va_list ap; 8192 xo_handle_t temp; 8193 8194 bzero(&temp, sizeof(temp)); 8195 temp.xo_style = XO_STYLE_TEXT; 8196 xo_buf_init(&temp.xo_data); 8197 xo_depth_check(&temp, XO_DEPTH); 8198 8199 va_copy(ap, vap); 8200 (void) xo_emit_hv(&temp, fmt, ap); 8201 va_end(ap); 8202 8203 xo_buffer_t *src = &temp.xo_data; 8204 xo_format_value(xop, "message", 7, src->xb_bufp, 8205 src->xb_curp - src->xb_bufp, NULL, 0, NULL, 0, 0); 8206 8207 xo_free(temp.xo_stack); 8208 xo_buf_cleanup(src); 8209 } 8210 8211 (void) xo_emit_hv(xop, fmt, vap); 8212 8213 ssize_t len = strlen(fmt); 8214 if (len > 0 && fmt[len - 1] != '\n') { 8215 if (code > 0) { 8216 const char *msg = strerror(code); 8217 if (msg) 8218 xo_emit_h(xop, ": {G:strerror}{g:error/%s}", msg); 8219 } 8220 xo_emit("\n"); 8221 } 8222 8223 xo_close_marker_h(xop, "xo_emit_warn_hcv"); 8224 xo_flush_h(xop); 8225} 8226 8227void 8228xo_emit_warn_hc (xo_handle_t *xop, int code, const char *fmt, ...) 8229{ 8230 va_list vap; 8231 8232 va_start(vap, fmt); 8233 xo_emit_warn_hcv(xop, 1, code, fmt, vap); 8234 va_end(vap); 8235} 8236 8237void 8238xo_emit_warn_c (int code, const char *fmt, ...) 8239{ 8240 va_list vap; 8241 8242 va_start(vap, fmt); 8243 xo_emit_warn_hcv(NULL, 1, code, fmt, vap); 8244 va_end(vap); 8245} 8246 8247void 8248xo_emit_warn (const char *fmt, ...) 8249{ 8250 int code = errno; 8251 va_list vap; 8252 8253 va_start(vap, fmt); 8254 xo_emit_warn_hcv(NULL, 1, code, fmt, vap); 8255 va_end(vap); 8256} 8257 8258void 8259xo_emit_warnx (const char *fmt, ...) 8260{ 8261 va_list vap; 8262 8263 va_start(vap, fmt); 8264 xo_emit_warn_hcv(NULL, 1, -1, fmt, vap); 8265 va_end(vap); 8266} 8267 8268void 8269xo_emit_err_v (int eval, int code, const char *fmt, va_list vap) 8270{ 8271 xo_emit_warn_hcv(NULL, 0, code, fmt, vap); 8272 xo_finish(); 8273 exit(eval); 8274} 8275 8276void 8277xo_emit_err (int eval, const char *fmt, ...) 8278{ 8279 int code = errno; 8280 va_list vap; 8281 va_start(vap, fmt); 8282 xo_emit_err_v(0, code, fmt, vap); 8283 va_end(vap); 8284 exit(eval); 8285} 8286 8287void 8288xo_emit_errx (int eval, const char *fmt, ...) 8289{ 8290 va_list vap; 8291 8292 va_start(vap, fmt); 8293 xo_emit_err_v(0, -1, fmt, vap); 8294 va_end(vap); 8295 xo_finish(); 8296 exit(eval); 8297} 8298 8299void 8300xo_emit_errc (int eval, int code, const char *fmt, ...) 8301{ 8302 va_list vap; 8303 8304 va_start(vap, fmt); 8305 xo_emit_warn_hcv(NULL, 0, code, fmt, vap); 8306 va_end(vap); 8307 xo_finish(); 8308 exit(eval); 8309} 8310 8311/* 8312 * Get the opaque private pointer for an xo handle 8313 */ 8314void * 8315xo_get_private (xo_handle_t *xop) 8316{ 8317 xop = xo_default(xop); 8318 return xop->xo_private; 8319} 8320 8321/* 8322 * Set the opaque private pointer for an xo handle. 8323 */ 8324void 8325xo_set_private (xo_handle_t *xop, void *opaque) 8326{ 8327 xop = xo_default(xop); 8328 xop->xo_private = opaque; 8329} 8330 8331/* 8332 * Get the encoder function 8333 */ 8334xo_encoder_func_t 8335xo_get_encoder (xo_handle_t *xop) 8336{ 8337 xop = xo_default(xop); 8338 return xop->xo_encoder; 8339} 8340 8341/* 8342 * Record an encoder callback function in an xo handle. 8343 */ 8344void 8345xo_set_encoder (xo_handle_t *xop, xo_encoder_func_t encoder) 8346{ 8347 xop = xo_default(xop); 8348 8349 xop->xo_style = XO_STYLE_ENCODER; 8350 xop->xo_encoder = encoder; 8351} 8352