131337658SMarcel Moolenaar /* 2545ddfbeSMarcel Moolenaar * Copyright (c) 2014-2015, Juniper Networks, Inc. 331337658SMarcel Moolenaar * All rights reserved. 431337658SMarcel Moolenaar * This SOFTWARE is licensed under the LICENSE provided in the 531337658SMarcel Moolenaar * ../Copyright file. By downloading, installing, copying, or otherwise 631337658SMarcel Moolenaar * using the SOFTWARE, you agree to be bound by the terms of that 731337658SMarcel Moolenaar * LICENSE. 831337658SMarcel Moolenaar * Phil Shafer, July 2014 931337658SMarcel Moolenaar */ 1031337658SMarcel Moolenaar 1131337658SMarcel Moolenaar #include <stdio.h> 1231337658SMarcel Moolenaar #include <stdlib.h> 1331337658SMarcel Moolenaar #include <stdint.h> 14545ddfbeSMarcel Moolenaar #include <unistd.h> 1531337658SMarcel Moolenaar #include <stddef.h> 1631337658SMarcel Moolenaar #include <wchar.h> 1731337658SMarcel Moolenaar #include <locale.h> 1831337658SMarcel Moolenaar #include <sys/types.h> 1931337658SMarcel Moolenaar #include <stdarg.h> 2031337658SMarcel Moolenaar #include <string.h> 2131337658SMarcel Moolenaar #include <errno.h> 2231337658SMarcel Moolenaar #include <limits.h> 2331337658SMarcel Moolenaar #include <ctype.h> 2431337658SMarcel Moolenaar #include <wctype.h> 2531337658SMarcel Moolenaar #include <getopt.h> 2631337658SMarcel Moolenaar 2731337658SMarcel Moolenaar #include "xoconfig.h" 2831337658SMarcel Moolenaar #include "xo.h" 2931337658SMarcel Moolenaar #include "xoversion.h" 3031337658SMarcel Moolenaar 31545ddfbeSMarcel Moolenaar #ifdef HAVE_STDIO_EXT_H 32545ddfbeSMarcel Moolenaar #include <stdio_ext.h> 33545ddfbeSMarcel Moolenaar #endif /* HAVE_STDIO_EXT_H */ 34545ddfbeSMarcel Moolenaar 3531337658SMarcel Moolenaar const char xo_version[] = LIBXO_VERSION; 3631337658SMarcel Moolenaar const char xo_version_extra[] = LIBXO_VERSION_EXTRA; 3731337658SMarcel Moolenaar 3831337658SMarcel Moolenaar #ifndef UNUSED 3931337658SMarcel Moolenaar #define UNUSED __attribute__ ((__unused__)) 4031337658SMarcel Moolenaar #endif /* UNUSED */ 4131337658SMarcel Moolenaar 4231337658SMarcel Moolenaar #define XO_INDENT_BY 2 /* Amount to indent when pretty printing */ 4331337658SMarcel Moolenaar #define XO_BUFSIZ (8*1024) /* Initial buffer size */ 4431337658SMarcel Moolenaar #define XO_DEPTH 512 /* Default stack depth */ 4531337658SMarcel Moolenaar #define XO_MAX_ANCHOR_WIDTH (8*1024) /* Anything wider is just sillyb */ 4631337658SMarcel Moolenaar 4731337658SMarcel Moolenaar #define XO_FAILURE_NAME "failure" 4831337658SMarcel Moolenaar 4931337658SMarcel Moolenaar /* 5031337658SMarcel Moolenaar * xo_buffer_t: a memory buffer that can be grown as needed. We 5131337658SMarcel Moolenaar * use them for building format strings and output data. 5231337658SMarcel Moolenaar */ 5331337658SMarcel Moolenaar typedef struct xo_buffer_s { 5431337658SMarcel Moolenaar char *xb_bufp; /* Buffer memory */ 5531337658SMarcel Moolenaar char *xb_curp; /* Current insertion point */ 5631337658SMarcel Moolenaar int xb_size; /* Size of buffer */ 5731337658SMarcel Moolenaar } xo_buffer_t; 5831337658SMarcel Moolenaar 5931337658SMarcel Moolenaar /* Flags for the stack frame */ 6031337658SMarcel Moolenaar typedef unsigned xo_xsf_flags_t; /* XSF_* flags */ 6131337658SMarcel Moolenaar #define XSF_NOT_FIRST (1<<0) /* Not the first element */ 6231337658SMarcel Moolenaar #define XSF_LIST (1<<1) /* Frame is a list */ 6331337658SMarcel Moolenaar #define XSF_INSTANCE (1<<2) /* Frame is an instance */ 6431337658SMarcel Moolenaar #define XSF_DTRT (1<<3) /* Save the name for DTRT mode */ 6531337658SMarcel Moolenaar 66545ddfbeSMarcel Moolenaar #define XSF_CONTENT (1<<4) /* Some content has been emitted */ 67545ddfbeSMarcel Moolenaar #define XSF_EMIT (1<<5) /* Some field has been emitted */ 68545ddfbeSMarcel Moolenaar #define XSF_EMIT_KEY (1<<6) /* A key has been emitted */ 69545ddfbeSMarcel Moolenaar #define XSF_EMIT_LEAF_LIST (1<<7) /* A leaf-list field has been emitted */ 70545ddfbeSMarcel Moolenaar 71545ddfbeSMarcel Moolenaar /* These are the flags we propagate between markers and their parents */ 72545ddfbeSMarcel Moolenaar #define XSF_MARKER_FLAGS \ 73545ddfbeSMarcel Moolenaar (XSF_NOT_FIRST | XSF_CONTENT | XSF_EMIT | XSF_EMIT_KEY | XSF_EMIT_LEAF_LIST ) 74545ddfbeSMarcel Moolenaar 75545ddfbeSMarcel Moolenaar /* 76545ddfbeSMarcel Moolenaar * A word about states: We're moving to a finite state machine (FMS) 77545ddfbeSMarcel Moolenaar * approach to help remove fragility from the caller's code. Instead 78545ddfbeSMarcel Moolenaar * of requiring a specific order of calls, we'll allow the caller more 79545ddfbeSMarcel Moolenaar * flexibility and make the library responsible for recovering from 80545ddfbeSMarcel Moolenaar * missed steps. The goal is that the library should not be capable of 81545ddfbeSMarcel Moolenaar * emitting invalid xml or json, but the developer shouldn't need 82545ddfbeSMarcel Moolenaar * to know or understand all the details about these encodings. 83545ddfbeSMarcel Moolenaar * 84545ddfbeSMarcel Moolenaar * You can think of states as either states or event, since they 85545ddfbeSMarcel Moolenaar * function rather like both. None of the XO_CLOSE_* events will 86545ddfbeSMarcel Moolenaar * persist as states, since their stack frame will be popped. 87545ddfbeSMarcel Moolenaar * Same is true of XSS_EMIT, which is an event that asks us to 88545ddfbeSMarcel Moolenaar * prep for emitting output fields. 89545ddfbeSMarcel Moolenaar */ 90545ddfbeSMarcel Moolenaar 91545ddfbeSMarcel Moolenaar /* Stack frame states */ 92545ddfbeSMarcel Moolenaar typedef unsigned xo_state_t; 93545ddfbeSMarcel Moolenaar #define XSS_INIT 0 /* Initial stack state */ 94545ddfbeSMarcel Moolenaar #define XSS_OPEN_CONTAINER 1 95545ddfbeSMarcel Moolenaar #define XSS_CLOSE_CONTAINER 2 96545ddfbeSMarcel Moolenaar #define XSS_OPEN_LIST 3 97545ddfbeSMarcel Moolenaar #define XSS_CLOSE_LIST 4 98545ddfbeSMarcel Moolenaar #define XSS_OPEN_INSTANCE 5 99545ddfbeSMarcel Moolenaar #define XSS_CLOSE_INSTANCE 6 100545ddfbeSMarcel Moolenaar #define XSS_OPEN_LEAF_LIST 7 101545ddfbeSMarcel Moolenaar #define XSS_CLOSE_LEAF_LIST 8 102545ddfbeSMarcel Moolenaar #define XSS_DISCARDING 9 /* Discarding data until recovered */ 103545ddfbeSMarcel Moolenaar #define XSS_MARKER 10 /* xo_open_marker's marker */ 104545ddfbeSMarcel Moolenaar #define XSS_EMIT 11 /* xo_emit has a leaf field */ 105545ddfbeSMarcel Moolenaar #define XSS_EMIT_LEAF_LIST 12 /* xo_emit has a leaf-list ({l:}) */ 106545ddfbeSMarcel Moolenaar #define XSS_FINISH 13 /* xo_finish was called */ 107545ddfbeSMarcel Moolenaar 108545ddfbeSMarcel Moolenaar #define XSS_MAX 13 109545ddfbeSMarcel Moolenaar 110545ddfbeSMarcel Moolenaar #define XSS_TRANSITION(_old, _new) ((_old) << 8 | (_new)) 111545ddfbeSMarcel Moolenaar 11231337658SMarcel Moolenaar /* 11331337658SMarcel Moolenaar * xo_stack_t: As we open and close containers and levels, we 11431337658SMarcel Moolenaar * create a stack of frames to track them. This is needed for 11531337658SMarcel Moolenaar * XOF_WARN and XOF_XPATH. 11631337658SMarcel Moolenaar */ 11731337658SMarcel Moolenaar typedef struct xo_stack_s { 11831337658SMarcel Moolenaar xo_xsf_flags_t xs_flags; /* Flags for this frame */ 119545ddfbeSMarcel Moolenaar xo_state_t xs_state; /* State for this stack frame */ 12031337658SMarcel Moolenaar char *xs_name; /* Name (for XPath value) */ 12131337658SMarcel Moolenaar char *xs_keys; /* XPath predicate for any key fields */ 12231337658SMarcel Moolenaar } xo_stack_t; 12331337658SMarcel Moolenaar 124788ca347SMarcel Moolenaar /* "colors" refers to fancy ansi codes */ 125788ca347SMarcel Moolenaar #define XO_COL_DEFAULT 0 126788ca347SMarcel Moolenaar #define XO_COL_BLACK 1 127788ca347SMarcel Moolenaar #define XO_COL_RED 2 128788ca347SMarcel Moolenaar #define XO_COL_GREEN 3 129788ca347SMarcel Moolenaar #define XO_COL_YELLOW 4 130788ca347SMarcel Moolenaar #define XO_COL_BLUE 5 131788ca347SMarcel Moolenaar #define XO_COL_MAGENTA 6 132788ca347SMarcel Moolenaar #define XO_COL_CYAN 7 133788ca347SMarcel Moolenaar #define XO_COL_WHITE 8 134788ca347SMarcel Moolenaar 135788ca347SMarcel Moolenaar #define XO_NUM_COLORS 9 136788ca347SMarcel Moolenaar 137788ca347SMarcel Moolenaar /* "effects" refers to fancy ansi codes */ 138788ca347SMarcel Moolenaar /* 139788ca347SMarcel Moolenaar * Yes, there's no blink. We're civilized. We like users. Blink 140788ca347SMarcel Moolenaar * isn't something one does to someone you like. Friends don't let 141788ca347SMarcel Moolenaar * friends use blink. On friends. You know what I mean. Blink is 142788ca347SMarcel Moolenaar * like, well, it's like bursting into show tunes at a funeral. It's 143788ca347SMarcel Moolenaar * just not done. Not something anyone wants. And on those rare 144788ca347SMarcel Moolenaar * instances where it might actually be appropriate, it's still wrong. 145788ca347SMarcel Moolenaar * It's likely done my the wrong person for the wrong reason. Just 146788ca347SMarcel Moolenaar * like blink. And if I implemented blink, I'd be like a funeral 147788ca347SMarcel Moolenaar * director who adds "Would you like us to burst into show tunes?" on 148788ca347SMarcel Moolenaar * the list of questions asking while making funeral arrangements. 149788ca347SMarcel Moolenaar * It's formalizing wrongness in the wrong way. And we're just too 150788ca347SMarcel Moolenaar * civilized to do that. Hhhmph! 151788ca347SMarcel Moolenaar */ 152788ca347SMarcel Moolenaar #define XO_EFF_RESET (1<<0) 153788ca347SMarcel Moolenaar #define XO_EFF_NORMAL (1<<1) 154788ca347SMarcel Moolenaar #define XO_EFF_BOLD (1<<2) 155788ca347SMarcel Moolenaar #define XO_EFF_UNDERLINE (1<<3) 156788ca347SMarcel Moolenaar #define XO_EFF_INVERSE (1<<4) 157788ca347SMarcel Moolenaar 158788ca347SMarcel Moolenaar #define XO_EFF_CLEAR_BITS XO_EFF_RESET 159788ca347SMarcel Moolenaar 160788ca347SMarcel Moolenaar typedef uint8_t xo_effect_t; 161788ca347SMarcel Moolenaar typedef uint8_t xo_color_t; 162788ca347SMarcel Moolenaar typedef struct xo_colors_s { 163788ca347SMarcel Moolenaar xo_effect_t xoc_effects; /* Current effect set */ 164788ca347SMarcel Moolenaar xo_color_t xoc_col_fg; /* Foreground color */ 165788ca347SMarcel Moolenaar xo_color_t xoc_col_bg; /* Background color */ 166788ca347SMarcel Moolenaar } xo_colors_t; 167788ca347SMarcel Moolenaar 16831337658SMarcel Moolenaar /* 16931337658SMarcel Moolenaar * xo_handle_t: this is the principle data structure for libxo. 17031337658SMarcel Moolenaar * It's used as a store for state, options, and content. 17131337658SMarcel Moolenaar */ 17231337658SMarcel Moolenaar struct xo_handle_s { 173545ddfbeSMarcel Moolenaar xo_xof_flags_t xo_flags; /* Flags */ 17431337658SMarcel Moolenaar unsigned short xo_style; /* XO_STYLE_* value */ 17531337658SMarcel Moolenaar unsigned short xo_indent; /* Indent level (if pretty) */ 17631337658SMarcel Moolenaar unsigned short xo_indent_by; /* Indent amount (tab stop) */ 17731337658SMarcel Moolenaar xo_write_func_t xo_write; /* Write callback */ 178a0f704ffSMarcel Moolenaar xo_close_func_t xo_close; /* Close callback */ 179545ddfbeSMarcel Moolenaar xo_flush_func_t xo_flush; /* Flush callback */ 18031337658SMarcel Moolenaar xo_formatter_t xo_formatter; /* Custom formating function */ 18131337658SMarcel Moolenaar xo_checkpointer_t xo_checkpointer; /* Custom formating support function */ 18231337658SMarcel Moolenaar void *xo_opaque; /* Opaque data for write function */ 18331337658SMarcel Moolenaar xo_buffer_t xo_data; /* Output data */ 18431337658SMarcel Moolenaar xo_buffer_t xo_fmt; /* Work area for building format strings */ 18531337658SMarcel Moolenaar xo_buffer_t xo_attrs; /* Work area for building XML attributes */ 18631337658SMarcel Moolenaar xo_buffer_t xo_predicate; /* Work area for building XPath predicates */ 18731337658SMarcel Moolenaar xo_stack_t *xo_stack; /* Stack pointer */ 18831337658SMarcel Moolenaar int xo_depth; /* Depth of stack */ 18931337658SMarcel Moolenaar int xo_stack_size; /* Size of the stack */ 19031337658SMarcel Moolenaar xo_info_t *xo_info; /* Info fields for all elements */ 19131337658SMarcel Moolenaar int xo_info_count; /* Number of info entries */ 19231337658SMarcel Moolenaar va_list xo_vap; /* Variable arguments (stdargs) */ 19331337658SMarcel Moolenaar char *xo_leading_xpath; /* A leading XPath expression */ 19431337658SMarcel Moolenaar mbstate_t xo_mbstate; /* Multi-byte character conversion state */ 19531337658SMarcel Moolenaar unsigned xo_anchor_offset; /* Start of anchored text */ 19631337658SMarcel Moolenaar unsigned xo_anchor_columns; /* Number of columns since the start anchor */ 19731337658SMarcel Moolenaar int xo_anchor_min_width; /* Desired width of anchored text */ 19831337658SMarcel Moolenaar unsigned xo_units_offset; /* Start of units insertion point */ 19931337658SMarcel Moolenaar unsigned xo_columns; /* Columns emitted during this xo_emit call */ 200788ca347SMarcel Moolenaar uint8_t xo_color_map_fg[XO_NUM_COLORS]; /* Foreground color mappings */ 201788ca347SMarcel Moolenaar uint8_t xo_color_map_bg[XO_NUM_COLORS]; /* Background color mappings */ 202788ca347SMarcel Moolenaar xo_colors_t xo_colors; /* Current color and effect values */ 203788ca347SMarcel Moolenaar xo_buffer_t xo_color_buf; /* HTML: buffer of colors and effects */ 204788ca347SMarcel Moolenaar char *xo_version; /* Version string */ 20531337658SMarcel Moolenaar }; 20631337658SMarcel Moolenaar 20731337658SMarcel Moolenaar /* Flags for formatting functions */ 20831337658SMarcel Moolenaar typedef unsigned long xo_xff_flags_t; 20931337658SMarcel Moolenaar #define XFF_COLON (1<<0) /* Append a ":" */ 21031337658SMarcel Moolenaar #define XFF_COMMA (1<<1) /* Append a "," iff there's more output */ 21131337658SMarcel Moolenaar #define XFF_WS (1<<2) /* Append a blank */ 212788ca347SMarcel Moolenaar #define XFF_ENCODE_ONLY (1<<3) /* Only emit for encoding formats (xml, json) */ 21331337658SMarcel Moolenaar 21431337658SMarcel Moolenaar #define XFF_QUOTE (1<<4) /* Force quotes */ 21531337658SMarcel Moolenaar #define XFF_NOQUOTE (1<<5) /* Force no quotes */ 21631337658SMarcel Moolenaar #define XFF_DISPLAY_ONLY (1<<6) /* Only emit for display formats (text and html) */ 21731337658SMarcel Moolenaar #define XFF_KEY (1<<7) /* Field is a key (for XPath) */ 21831337658SMarcel Moolenaar 21931337658SMarcel Moolenaar #define XFF_XML (1<<8) /* Force XML encoding style (for XPath) */ 22031337658SMarcel Moolenaar #define XFF_ATTR (1<<9) /* Escape value using attribute rules (XML) */ 22131337658SMarcel Moolenaar #define XFF_BLANK_LINE (1<<10) /* Emit a blank line */ 22231337658SMarcel Moolenaar #define XFF_NO_OUTPUT (1<<11) /* Do not make any output */ 22331337658SMarcel Moolenaar 22431337658SMarcel Moolenaar #define XFF_TRIM_WS (1<<12) /* Trim whitespace off encoded values */ 22531337658SMarcel Moolenaar #define XFF_LEAF_LIST (1<<13) /* A leaf-list (list of values) */ 22631337658SMarcel Moolenaar #define XFF_UNESCAPE (1<<14) /* Need to printf-style unescape the value */ 22731337658SMarcel Moolenaar 22831337658SMarcel Moolenaar /* 22931337658SMarcel Moolenaar * Normal printf has width and precision, which for strings operate as 23031337658SMarcel Moolenaar * min and max number of columns. But this depends on the idea that 23131337658SMarcel Moolenaar * one byte means one column, which UTF-8 and multi-byte characters 23231337658SMarcel Moolenaar * pitches on its ear. It may take 40 bytes of data to populate 14 23331337658SMarcel Moolenaar * columns, but we can't go off looking at 40 bytes of data without the 23431337658SMarcel Moolenaar * caller's permission for fear/knowledge that we'll generate core files. 23531337658SMarcel Moolenaar * 23631337658SMarcel Moolenaar * So we make three values, distinguishing between "max column" and 23731337658SMarcel Moolenaar * "number of bytes that we will inspect inspect safely" We call the 23831337658SMarcel Moolenaar * later "size", and make the format "%[[<min>].[[<size>].<max>]]s". 23931337658SMarcel Moolenaar * 24031337658SMarcel Moolenaar * Under the "first do no harm" theory, we default "max" to "size". 24131337658SMarcel Moolenaar * This is a reasonable assumption for folks that don't grok the 24231337658SMarcel Moolenaar * MBS/WCS/UTF-8 world, and while it will be annoying, it will never 24331337658SMarcel Moolenaar * be evil. 24431337658SMarcel Moolenaar * 24531337658SMarcel Moolenaar * For example, xo_emit("{:tag/%-14.14s}", buf) will make 14 24631337658SMarcel Moolenaar * columns of output, but will never look at more than 14 bytes of the 24731337658SMarcel Moolenaar * input buffer. This is mostly compatible with printf and caller's 24831337658SMarcel Moolenaar * expectations. 24931337658SMarcel Moolenaar * 25031337658SMarcel Moolenaar * In contrast xo_emit("{:tag/%-14..14s}", buf) will look at however 25131337658SMarcel Moolenaar * many bytes (or until a NUL is seen) are needed to fill 14 columns 25231337658SMarcel Moolenaar * of output. xo_emit("{:tag/%-14.*.14s}", xx, buf) will look at up 25331337658SMarcel Moolenaar * to xx bytes (or until a NUL is seen) in order to fill 14 columns 25431337658SMarcel Moolenaar * of output. 25531337658SMarcel Moolenaar * 25631337658SMarcel Moolenaar * It's fairly amazing how a good idea (handle all languages of the 25731337658SMarcel Moolenaar * world) blows such a big hole in the bottom of the fairly weak boat 25831337658SMarcel Moolenaar * that is C string handling. The simplicity and completenesss are 25931337658SMarcel Moolenaar * sunk in ways we haven't even begun to understand. 26031337658SMarcel Moolenaar */ 26131337658SMarcel Moolenaar 26231337658SMarcel Moolenaar #define XF_WIDTH_MIN 0 /* Minimal width */ 26331337658SMarcel Moolenaar #define XF_WIDTH_SIZE 1 /* Maximum number of bytes to examine */ 26431337658SMarcel Moolenaar #define XF_WIDTH_MAX 2 /* Maximum width */ 26531337658SMarcel Moolenaar #define XF_WIDTH_NUM 3 /* Numeric fields in printf (min.size.max) */ 26631337658SMarcel Moolenaar 26731337658SMarcel Moolenaar /* Input and output string encodings */ 26831337658SMarcel Moolenaar #define XF_ENC_WIDE 1 /* Wide characters (wchar_t) */ 26931337658SMarcel Moolenaar #define XF_ENC_UTF8 2 /* UTF-8 */ 27031337658SMarcel Moolenaar #define XF_ENC_LOCALE 3 /* Current locale */ 27131337658SMarcel Moolenaar 27231337658SMarcel Moolenaar /* 27331337658SMarcel Moolenaar * A place to parse printf-style format flags for each field 27431337658SMarcel Moolenaar */ 27531337658SMarcel Moolenaar typedef struct xo_format_s { 27631337658SMarcel Moolenaar unsigned char xf_fc; /* Format character */ 27731337658SMarcel Moolenaar unsigned char xf_enc; /* Encoding of the string (XF_ENC_*) */ 27831337658SMarcel Moolenaar unsigned char xf_skip; /* Skip this field */ 27931337658SMarcel Moolenaar unsigned char xf_lflag; /* 'l' (long) */ 28031337658SMarcel Moolenaar unsigned char xf_hflag;; /* 'h' (half) */ 28131337658SMarcel Moolenaar unsigned char xf_jflag; /* 'j' (intmax_t) */ 28231337658SMarcel Moolenaar unsigned char xf_tflag; /* 't' (ptrdiff_t) */ 28331337658SMarcel Moolenaar unsigned char xf_zflag; /* 'z' (size_t) */ 28431337658SMarcel Moolenaar unsigned char xf_qflag; /* 'q' (quad_t) */ 28531337658SMarcel Moolenaar unsigned char xf_seen_minus; /* Seen a minus */ 28631337658SMarcel Moolenaar int xf_leading_zero; /* Seen a leading zero (zero fill) */ 28731337658SMarcel Moolenaar unsigned xf_dots; /* Seen one or more '.'s */ 28831337658SMarcel Moolenaar int xf_width[XF_WIDTH_NUM]; /* Width/precision/size numeric fields */ 28931337658SMarcel Moolenaar unsigned xf_stars; /* Seen one or more '*'s */ 29031337658SMarcel Moolenaar unsigned char xf_star[XF_WIDTH_NUM]; /* Seen one or more '*'s */ 29131337658SMarcel Moolenaar } xo_format_t; 29231337658SMarcel Moolenaar 29331337658SMarcel Moolenaar /* 29431337658SMarcel Moolenaar * We keep a default handle to allow callers to avoid having to 29531337658SMarcel Moolenaar * allocate one. Passing NULL to any of our functions will use 29631337658SMarcel Moolenaar * this default handle. 29731337658SMarcel Moolenaar */ 29831337658SMarcel Moolenaar static xo_handle_t xo_default_handle; 29931337658SMarcel Moolenaar static int xo_default_inited; 30031337658SMarcel Moolenaar static int xo_locale_inited; 301545ddfbeSMarcel Moolenaar static const char *xo_program; 30231337658SMarcel Moolenaar 30331337658SMarcel Moolenaar /* 30431337658SMarcel Moolenaar * To allow libxo to be used in diverse environment, we allow the 30531337658SMarcel Moolenaar * caller to give callbacks for memory allocation. 30631337658SMarcel Moolenaar */ 30731337658SMarcel Moolenaar static xo_realloc_func_t xo_realloc = realloc; 30831337658SMarcel Moolenaar static xo_free_func_t xo_free = free; 30931337658SMarcel Moolenaar 31031337658SMarcel Moolenaar /* Forward declarations */ 31131337658SMarcel Moolenaar static void 31231337658SMarcel Moolenaar xo_failure (xo_handle_t *xop, const char *fmt, ...); 31331337658SMarcel Moolenaar 314545ddfbeSMarcel Moolenaar static int 315545ddfbeSMarcel Moolenaar xo_transition (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name, 316545ddfbeSMarcel Moolenaar xo_state_t new_state); 317545ddfbeSMarcel Moolenaar 31831337658SMarcel Moolenaar static void 31931337658SMarcel Moolenaar xo_buf_append_div (xo_handle_t *xop, const char *class, xo_xff_flags_t flags, 32031337658SMarcel Moolenaar const char *name, int nlen, 32131337658SMarcel Moolenaar const char *value, int vlen, 32231337658SMarcel Moolenaar const char *encoding, int elen); 32331337658SMarcel Moolenaar 32431337658SMarcel Moolenaar static void 32531337658SMarcel Moolenaar xo_anchor_clear (xo_handle_t *xop); 32631337658SMarcel Moolenaar 32731337658SMarcel Moolenaar /* 328788ca347SMarcel Moolenaar * xo_style is used to retrieve the current style. When we're built 329788ca347SMarcel Moolenaar * for "text only" mode, we use this function to drive the removal 330788ca347SMarcel Moolenaar * of most of the code in libxo. We return a constant and the compiler 331788ca347SMarcel Moolenaar * happily removes the non-text code that is not longer executed. This 332788ca347SMarcel Moolenaar * trims our code nicely without needing to trampel perfectly readable 333788ca347SMarcel Moolenaar * code with ifdefs. 334788ca347SMarcel Moolenaar */ 335788ca347SMarcel Moolenaar static inline unsigned short 336788ca347SMarcel Moolenaar xo_style (xo_handle_t *xop UNUSED) 337788ca347SMarcel Moolenaar { 338788ca347SMarcel Moolenaar #ifdef LIBXO_TEXT_ONLY 339788ca347SMarcel Moolenaar return XO_STYLE_TEXT; 340788ca347SMarcel Moolenaar #else /* LIBXO_TEXT_ONLY */ 341788ca347SMarcel Moolenaar return xop->xo_style; 342788ca347SMarcel Moolenaar #endif /* LIBXO_TEXT_ONLY */ 343788ca347SMarcel Moolenaar } 344788ca347SMarcel Moolenaar 345788ca347SMarcel Moolenaar /* 34631337658SMarcel Moolenaar * Callback to write data to a FILE pointer 34731337658SMarcel Moolenaar */ 34831337658SMarcel Moolenaar static int 34931337658SMarcel Moolenaar xo_write_to_file (void *opaque, const char *data) 35031337658SMarcel Moolenaar { 35131337658SMarcel Moolenaar FILE *fp = (FILE *) opaque; 352545ddfbeSMarcel Moolenaar 35331337658SMarcel Moolenaar return fprintf(fp, "%s", data); 35431337658SMarcel Moolenaar } 35531337658SMarcel Moolenaar 35631337658SMarcel Moolenaar /* 35731337658SMarcel Moolenaar * Callback to close a file 35831337658SMarcel Moolenaar */ 35931337658SMarcel Moolenaar static void 36031337658SMarcel Moolenaar xo_close_file (void *opaque) 36131337658SMarcel Moolenaar { 36231337658SMarcel Moolenaar FILE *fp = (FILE *) opaque; 363545ddfbeSMarcel Moolenaar 36431337658SMarcel Moolenaar fclose(fp); 36531337658SMarcel Moolenaar } 36631337658SMarcel Moolenaar 36731337658SMarcel Moolenaar /* 368545ddfbeSMarcel Moolenaar * Callback to flush a FILE pointer 369545ddfbeSMarcel Moolenaar */ 370545ddfbeSMarcel Moolenaar static int 371545ddfbeSMarcel Moolenaar xo_flush_file (void *opaque) 372545ddfbeSMarcel Moolenaar { 373545ddfbeSMarcel Moolenaar FILE *fp = (FILE *) opaque; 374545ddfbeSMarcel Moolenaar 375545ddfbeSMarcel Moolenaar return fflush(fp); 376545ddfbeSMarcel Moolenaar } 377545ddfbeSMarcel Moolenaar 378545ddfbeSMarcel Moolenaar /* 37931337658SMarcel Moolenaar * Initialize the contents of an xo_buffer_t. 38031337658SMarcel Moolenaar */ 38131337658SMarcel Moolenaar static void 38231337658SMarcel Moolenaar xo_buf_init (xo_buffer_t *xbp) 38331337658SMarcel Moolenaar { 38431337658SMarcel Moolenaar xbp->xb_size = XO_BUFSIZ; 38531337658SMarcel Moolenaar xbp->xb_bufp = xo_realloc(NULL, xbp->xb_size); 38631337658SMarcel Moolenaar xbp->xb_curp = xbp->xb_bufp; 38731337658SMarcel Moolenaar } 38831337658SMarcel Moolenaar 38931337658SMarcel Moolenaar /* 390788ca347SMarcel Moolenaar * Reset the buffer to empty 391788ca347SMarcel Moolenaar */ 392788ca347SMarcel Moolenaar static void 393788ca347SMarcel Moolenaar xo_buf_reset (xo_buffer_t *xbp) 394788ca347SMarcel Moolenaar { 395788ca347SMarcel Moolenaar xbp->xb_curp = xbp->xb_bufp; 396788ca347SMarcel Moolenaar } 397788ca347SMarcel Moolenaar 398788ca347SMarcel Moolenaar /* 399788ca347SMarcel Moolenaar * Reset the buffer to empty 400788ca347SMarcel Moolenaar */ 401788ca347SMarcel Moolenaar static int 402788ca347SMarcel Moolenaar xo_buf_is_empty (xo_buffer_t *xbp) 403788ca347SMarcel Moolenaar { 404788ca347SMarcel Moolenaar return (xbp->xb_curp == xbp->xb_bufp); 405788ca347SMarcel Moolenaar } 406788ca347SMarcel Moolenaar 407788ca347SMarcel Moolenaar /* 40831337658SMarcel Moolenaar * Initialize the contents of an xo_buffer_t. 40931337658SMarcel Moolenaar */ 41031337658SMarcel Moolenaar static void 41131337658SMarcel Moolenaar xo_buf_cleanup (xo_buffer_t *xbp) 41231337658SMarcel Moolenaar { 41331337658SMarcel Moolenaar if (xbp->xb_bufp) 41431337658SMarcel Moolenaar xo_free(xbp->xb_bufp); 41531337658SMarcel Moolenaar bzero(xbp, sizeof(*xbp)); 41631337658SMarcel Moolenaar } 41731337658SMarcel Moolenaar 41831337658SMarcel Moolenaar static int 41931337658SMarcel Moolenaar xo_depth_check (xo_handle_t *xop, int depth) 42031337658SMarcel Moolenaar { 42131337658SMarcel Moolenaar xo_stack_t *xsp; 42231337658SMarcel Moolenaar 42331337658SMarcel Moolenaar if (depth >= xop->xo_stack_size) { 42431337658SMarcel Moolenaar depth += 16; 42531337658SMarcel Moolenaar xsp = xo_realloc(xop->xo_stack, sizeof(xop->xo_stack[0]) * depth); 42631337658SMarcel Moolenaar if (xsp == NULL) { 42731337658SMarcel Moolenaar xo_failure(xop, "xo_depth_check: out of memory (%d)", depth); 42831337658SMarcel Moolenaar return 0; 42931337658SMarcel Moolenaar } 43031337658SMarcel Moolenaar 43131337658SMarcel Moolenaar int count = depth - xop->xo_stack_size; 43231337658SMarcel Moolenaar 43331337658SMarcel Moolenaar bzero(xsp + xop->xo_stack_size, count * sizeof(*xsp)); 43431337658SMarcel Moolenaar xop->xo_stack_size = depth; 43531337658SMarcel Moolenaar xop->xo_stack = xsp; 43631337658SMarcel Moolenaar } 43731337658SMarcel Moolenaar 43831337658SMarcel Moolenaar return 0; 43931337658SMarcel Moolenaar } 44031337658SMarcel Moolenaar 44131337658SMarcel Moolenaar void 44231337658SMarcel Moolenaar xo_no_setlocale (void) 44331337658SMarcel Moolenaar { 44431337658SMarcel Moolenaar xo_locale_inited = 1; /* Skip initialization */ 44531337658SMarcel Moolenaar } 44631337658SMarcel Moolenaar 44731337658SMarcel Moolenaar /* 448545ddfbeSMarcel Moolenaar * We need to decide if stdout is line buffered (_IOLBF). Lacking a 449545ddfbeSMarcel Moolenaar * standard way to decide this (e.g. getlinebuf()), we have configure 450788ca347SMarcel Moolenaar * look to find __flbf, which glibc supported. If not, we'll rely on 451788ca347SMarcel Moolenaar * isatty, with the assumption that terminals are the only thing 452545ddfbeSMarcel Moolenaar * that's line buffered. We _could_ test for "steam._flags & _IOLBF", 453545ddfbeSMarcel Moolenaar * which is all __flbf does, but that's even tackier. Like a 454545ddfbeSMarcel Moolenaar * bedazzled Elvis outfit on an ugly lap dog sort of tacky. Not 455545ddfbeSMarcel Moolenaar * something we're willing to do. 456545ddfbeSMarcel Moolenaar */ 457545ddfbeSMarcel Moolenaar static int 458545ddfbeSMarcel Moolenaar xo_is_line_buffered (FILE *stream) 459545ddfbeSMarcel Moolenaar { 460545ddfbeSMarcel Moolenaar #if HAVE___FLBF 461545ddfbeSMarcel Moolenaar if (__flbf(stream)) 462545ddfbeSMarcel Moolenaar return 1; 463545ddfbeSMarcel Moolenaar #else /* HAVE___FLBF */ 464545ddfbeSMarcel Moolenaar if (isatty(fileno(stream))) 465545ddfbeSMarcel Moolenaar return 1; 466545ddfbeSMarcel Moolenaar #endif /* HAVE___FLBF */ 467545ddfbeSMarcel Moolenaar return 0; 468545ddfbeSMarcel Moolenaar } 469545ddfbeSMarcel Moolenaar 470545ddfbeSMarcel Moolenaar /* 47131337658SMarcel Moolenaar * Initialize an xo_handle_t, using both static defaults and 47231337658SMarcel Moolenaar * the global settings from the LIBXO_OPTIONS environment 47331337658SMarcel Moolenaar * variable. 47431337658SMarcel Moolenaar */ 47531337658SMarcel Moolenaar static void 47631337658SMarcel Moolenaar xo_init_handle (xo_handle_t *xop) 47731337658SMarcel Moolenaar { 47831337658SMarcel Moolenaar xop->xo_opaque = stdout; 47931337658SMarcel Moolenaar xop->xo_write = xo_write_to_file; 480545ddfbeSMarcel Moolenaar xop->xo_flush = xo_flush_file; 481545ddfbeSMarcel Moolenaar 482545ddfbeSMarcel Moolenaar if (xo_is_line_buffered(stdout)) 483545ddfbeSMarcel Moolenaar xop->xo_flags |= XOF_FLUSH_LINE; 48431337658SMarcel Moolenaar 48531337658SMarcel Moolenaar /* 486788ca347SMarcel Moolenaar * We only want to do color output on terminals, but we only want 487788ca347SMarcel Moolenaar * to do this if the user has asked for color. 488788ca347SMarcel Moolenaar */ 489788ca347SMarcel Moolenaar if ((xop->xo_flags & XOF_COLOR_ALLOWED) && isatty(1)) 490788ca347SMarcel Moolenaar xop->xo_flags |= XOF_COLOR; 491788ca347SMarcel Moolenaar 492788ca347SMarcel Moolenaar /* 49331337658SMarcel Moolenaar * We need to initialize the locale, which isn't really pretty. 49431337658SMarcel Moolenaar * Libraries should depend on their caller to set up the 49531337658SMarcel Moolenaar * environment. But we really can't count on the caller to do 49631337658SMarcel Moolenaar * this, because well, they won't. Trust me. 49731337658SMarcel Moolenaar */ 49831337658SMarcel Moolenaar if (!xo_locale_inited) { 49931337658SMarcel Moolenaar xo_locale_inited = 1; /* Only do this once */ 50031337658SMarcel Moolenaar 50131337658SMarcel Moolenaar const char *cp = getenv("LC_CTYPE"); 50231337658SMarcel Moolenaar if (cp == NULL) 50331337658SMarcel Moolenaar cp = getenv("LANG"); 50431337658SMarcel Moolenaar if (cp == NULL) 50531337658SMarcel Moolenaar cp = getenv("LC_ALL"); 50631337658SMarcel Moolenaar if (cp == NULL) 50731337658SMarcel Moolenaar cp = "UTF-8"; /* Optimistic? */ 508c600d307SMarcel Moolenaar (void) setlocale(LC_CTYPE, cp); 50931337658SMarcel Moolenaar } 51031337658SMarcel Moolenaar 51131337658SMarcel Moolenaar /* 51231337658SMarcel Moolenaar * Initialize only the xo_buffers we know we'll need; the others 51331337658SMarcel Moolenaar * can be allocated as needed. 51431337658SMarcel Moolenaar */ 51531337658SMarcel Moolenaar xo_buf_init(&xop->xo_data); 51631337658SMarcel Moolenaar xo_buf_init(&xop->xo_fmt); 51731337658SMarcel Moolenaar 51831337658SMarcel Moolenaar xop->xo_indent_by = XO_INDENT_BY; 51931337658SMarcel Moolenaar xo_depth_check(xop, XO_DEPTH); 52031337658SMarcel Moolenaar 52131337658SMarcel Moolenaar #if !defined(NO_LIBXO_OPTIONS) 52231337658SMarcel Moolenaar if (!(xop->xo_flags & XOF_NO_ENV)) { 52331337658SMarcel Moolenaar char *env = getenv("LIBXO_OPTIONS"); 52431337658SMarcel Moolenaar if (env) 52531337658SMarcel Moolenaar xo_set_options(xop, env); 52631337658SMarcel Moolenaar } 52731337658SMarcel Moolenaar #endif /* NO_GETENV */ 52831337658SMarcel Moolenaar } 52931337658SMarcel Moolenaar 53031337658SMarcel Moolenaar /* 53131337658SMarcel Moolenaar * Initialize the default handle. 53231337658SMarcel Moolenaar */ 53331337658SMarcel Moolenaar static void 53431337658SMarcel Moolenaar xo_default_init (void) 53531337658SMarcel Moolenaar { 53631337658SMarcel Moolenaar xo_handle_t *xop = &xo_default_handle; 53731337658SMarcel Moolenaar 53831337658SMarcel Moolenaar xo_init_handle(xop); 53931337658SMarcel Moolenaar 54031337658SMarcel Moolenaar xo_default_inited = 1; 54131337658SMarcel Moolenaar } 54231337658SMarcel Moolenaar 54331337658SMarcel Moolenaar /* 54431337658SMarcel Moolenaar * Does the buffer have room for the given number of bytes of data? 54531337658SMarcel Moolenaar * If not, realloc the buffer to make room. If that fails, we 54631337658SMarcel Moolenaar * return 0 to tell the caller they are in trouble. 54731337658SMarcel Moolenaar */ 54831337658SMarcel Moolenaar static int 54931337658SMarcel Moolenaar xo_buf_has_room (xo_buffer_t *xbp, int len) 55031337658SMarcel Moolenaar { 55131337658SMarcel Moolenaar if (xbp->xb_curp + len >= xbp->xb_bufp + xbp->xb_size) { 55231337658SMarcel Moolenaar int sz = xbp->xb_size + XO_BUFSIZ; 55331337658SMarcel Moolenaar char *bp = xo_realloc(xbp->xb_bufp, sz); 55431337658SMarcel Moolenaar if (bp == NULL) { 55531337658SMarcel Moolenaar /* 55631337658SMarcel Moolenaar * XXX If we wanted to put a stick XOF_ENOMEM on xop, 55731337658SMarcel Moolenaar * this would be the place to do it. But we'd need 55831337658SMarcel Moolenaar * to churn the code to pass xop in here.... 55931337658SMarcel Moolenaar */ 56031337658SMarcel Moolenaar return 0; 56131337658SMarcel Moolenaar } 56231337658SMarcel Moolenaar 56331337658SMarcel Moolenaar xbp->xb_curp = bp + (xbp->xb_curp - xbp->xb_bufp); 56431337658SMarcel Moolenaar xbp->xb_bufp = bp; 56531337658SMarcel Moolenaar xbp->xb_size = sz; 56631337658SMarcel Moolenaar } 56731337658SMarcel Moolenaar 56831337658SMarcel Moolenaar return 1; 56931337658SMarcel Moolenaar } 57031337658SMarcel Moolenaar 57131337658SMarcel Moolenaar /* 57231337658SMarcel Moolenaar * Cheap convenience function to return either the argument, or 57331337658SMarcel Moolenaar * the internal handle, after it has been initialized. The usage 57431337658SMarcel Moolenaar * is: 57531337658SMarcel Moolenaar * xop = xo_default(xop); 57631337658SMarcel Moolenaar */ 57731337658SMarcel Moolenaar static xo_handle_t * 57831337658SMarcel Moolenaar xo_default (xo_handle_t *xop) 57931337658SMarcel Moolenaar { 58031337658SMarcel Moolenaar if (xop == NULL) { 58131337658SMarcel Moolenaar if (xo_default_inited == 0) 58231337658SMarcel Moolenaar xo_default_init(); 58331337658SMarcel Moolenaar xop = &xo_default_handle; 58431337658SMarcel Moolenaar } 58531337658SMarcel Moolenaar 58631337658SMarcel Moolenaar return xop; 58731337658SMarcel Moolenaar } 58831337658SMarcel Moolenaar 58931337658SMarcel Moolenaar /* 59031337658SMarcel Moolenaar * Return the number of spaces we should be indenting. If 591788ca347SMarcel Moolenaar * we are pretty-printing, this is indent * indent_by. 59231337658SMarcel Moolenaar */ 59331337658SMarcel Moolenaar static int 59431337658SMarcel Moolenaar xo_indent (xo_handle_t *xop) 59531337658SMarcel Moolenaar { 59631337658SMarcel Moolenaar int rc = 0; 59731337658SMarcel Moolenaar 59831337658SMarcel Moolenaar xop = xo_default(xop); 59931337658SMarcel Moolenaar 60031337658SMarcel Moolenaar if (xop->xo_flags & XOF_PRETTY) { 60131337658SMarcel Moolenaar rc = xop->xo_indent * xop->xo_indent_by; 60231337658SMarcel Moolenaar if (xop->xo_flags & XOF_TOP_EMITTED) 60331337658SMarcel Moolenaar rc += xop->xo_indent_by; 60431337658SMarcel Moolenaar } 60531337658SMarcel Moolenaar 606545ddfbeSMarcel Moolenaar return (rc > 0) ? rc : 0; 60731337658SMarcel Moolenaar } 60831337658SMarcel Moolenaar 60931337658SMarcel Moolenaar static void 61031337658SMarcel Moolenaar xo_buf_indent (xo_handle_t *xop, int indent) 61131337658SMarcel Moolenaar { 61231337658SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_data; 61331337658SMarcel Moolenaar 61431337658SMarcel Moolenaar if (indent <= 0) 61531337658SMarcel Moolenaar indent = xo_indent(xop); 61631337658SMarcel Moolenaar 61731337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, indent)) 61831337658SMarcel Moolenaar return; 61931337658SMarcel Moolenaar 62031337658SMarcel Moolenaar memset(xbp->xb_curp, ' ', indent); 62131337658SMarcel Moolenaar xbp->xb_curp += indent; 62231337658SMarcel Moolenaar } 62331337658SMarcel Moolenaar 62431337658SMarcel Moolenaar static char xo_xml_amp[] = "&"; 62531337658SMarcel Moolenaar static char xo_xml_lt[] = "<"; 62631337658SMarcel Moolenaar static char xo_xml_gt[] = ">"; 62731337658SMarcel Moolenaar static char xo_xml_quot[] = """; 62831337658SMarcel Moolenaar 62931337658SMarcel Moolenaar static int 63031337658SMarcel Moolenaar xo_escape_xml (xo_buffer_t *xbp, int len, int attr) 63131337658SMarcel Moolenaar { 63231337658SMarcel Moolenaar int slen; 63331337658SMarcel Moolenaar unsigned delta = 0; 63431337658SMarcel Moolenaar char *cp, *ep, *ip; 63531337658SMarcel Moolenaar const char *sp; 63631337658SMarcel Moolenaar 63731337658SMarcel Moolenaar for (cp = xbp->xb_curp, ep = cp + len; cp < ep; cp++) { 63831337658SMarcel Moolenaar /* We're subtracting 2: 1 for the NUL, 1 for the char we replace */ 63931337658SMarcel Moolenaar if (*cp == '<') 64031337658SMarcel Moolenaar delta += sizeof(xo_xml_lt) - 2; 64131337658SMarcel Moolenaar else if (*cp == '>') 64231337658SMarcel Moolenaar delta += sizeof(xo_xml_gt) - 2; 64331337658SMarcel Moolenaar else if (*cp == '&') 64431337658SMarcel Moolenaar delta += sizeof(xo_xml_amp) - 2; 64531337658SMarcel Moolenaar else if (attr && *cp == '"') 64631337658SMarcel Moolenaar delta += sizeof(xo_xml_quot) - 2; 64731337658SMarcel Moolenaar } 64831337658SMarcel Moolenaar 64931337658SMarcel Moolenaar if (delta == 0) /* Nothing to escape; bail */ 65031337658SMarcel Moolenaar return len; 65131337658SMarcel Moolenaar 65231337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, delta)) /* No room; bail, but don't append */ 65331337658SMarcel Moolenaar return 0; 65431337658SMarcel Moolenaar 65531337658SMarcel Moolenaar ep = xbp->xb_curp; 65631337658SMarcel Moolenaar cp = ep + len; 65731337658SMarcel Moolenaar ip = cp + delta; 65831337658SMarcel Moolenaar do { 65931337658SMarcel Moolenaar cp -= 1; 66031337658SMarcel Moolenaar ip -= 1; 66131337658SMarcel Moolenaar 66231337658SMarcel Moolenaar if (*cp == '<') 66331337658SMarcel Moolenaar sp = xo_xml_lt; 66431337658SMarcel Moolenaar else if (*cp == '>') 66531337658SMarcel Moolenaar sp = xo_xml_gt; 66631337658SMarcel Moolenaar else if (*cp == '&') 66731337658SMarcel Moolenaar sp = xo_xml_amp; 66831337658SMarcel Moolenaar else if (attr && *cp == '"') 66931337658SMarcel Moolenaar sp = xo_xml_quot; 67031337658SMarcel Moolenaar else { 67131337658SMarcel Moolenaar *ip = *cp; 67231337658SMarcel Moolenaar continue; 67331337658SMarcel Moolenaar } 67431337658SMarcel Moolenaar 67531337658SMarcel Moolenaar slen = strlen(sp); 67631337658SMarcel Moolenaar ip -= slen - 1; 67731337658SMarcel Moolenaar memcpy(ip, sp, slen); 67831337658SMarcel Moolenaar 67931337658SMarcel Moolenaar } while (cp > ep && cp != ip); 68031337658SMarcel Moolenaar 68131337658SMarcel Moolenaar return len + delta; 68231337658SMarcel Moolenaar } 68331337658SMarcel Moolenaar 68431337658SMarcel Moolenaar static int 68531337658SMarcel Moolenaar xo_escape_json (xo_buffer_t *xbp, int len) 68631337658SMarcel Moolenaar { 68731337658SMarcel Moolenaar unsigned delta = 0; 68831337658SMarcel Moolenaar char *cp, *ep, *ip; 68931337658SMarcel Moolenaar 69031337658SMarcel Moolenaar for (cp = xbp->xb_curp, ep = cp + len; cp < ep; cp++) { 691545ddfbeSMarcel Moolenaar if (*cp == '\\' || *cp == '"') 69231337658SMarcel Moolenaar delta += 1; 693545ddfbeSMarcel Moolenaar else if (*cp == '\n' || *cp == '\r') 69431337658SMarcel Moolenaar delta += 1; 69531337658SMarcel Moolenaar } 69631337658SMarcel Moolenaar 69731337658SMarcel Moolenaar if (delta == 0) /* Nothing to escape; bail */ 69831337658SMarcel Moolenaar return len; 69931337658SMarcel Moolenaar 70031337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, delta)) /* No room; bail, but don't append */ 70131337658SMarcel Moolenaar return 0; 70231337658SMarcel Moolenaar 70331337658SMarcel Moolenaar ep = xbp->xb_curp; 70431337658SMarcel Moolenaar cp = ep + len; 70531337658SMarcel Moolenaar ip = cp + delta; 70631337658SMarcel Moolenaar do { 70731337658SMarcel Moolenaar cp -= 1; 70831337658SMarcel Moolenaar ip -= 1; 70931337658SMarcel Moolenaar 710545ddfbeSMarcel Moolenaar if (*cp == '\\' || *cp == '"') { 71131337658SMarcel Moolenaar *ip-- = *cp; 71231337658SMarcel Moolenaar *ip = '\\'; 713545ddfbeSMarcel Moolenaar } else if (*cp == '\n') { 714545ddfbeSMarcel Moolenaar *ip-- = 'n'; 715545ddfbeSMarcel Moolenaar *ip = '\\'; 716545ddfbeSMarcel Moolenaar } else if (*cp == '\r') { 717545ddfbeSMarcel Moolenaar *ip-- = 'r'; 718545ddfbeSMarcel Moolenaar *ip = '\\'; 719545ddfbeSMarcel Moolenaar } else { 720545ddfbeSMarcel Moolenaar *ip = *cp; 721545ddfbeSMarcel Moolenaar } 72231337658SMarcel Moolenaar 72331337658SMarcel Moolenaar } while (cp > ep && cp != ip); 72431337658SMarcel Moolenaar 72531337658SMarcel Moolenaar return len + delta; 72631337658SMarcel Moolenaar } 72731337658SMarcel Moolenaar 72831337658SMarcel Moolenaar /* 72931337658SMarcel Moolenaar * Append the given string to the given buffer 73031337658SMarcel Moolenaar */ 73131337658SMarcel Moolenaar static void 73231337658SMarcel Moolenaar xo_buf_append (xo_buffer_t *xbp, const char *str, int len) 73331337658SMarcel Moolenaar { 73431337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, len)) 73531337658SMarcel Moolenaar return; 73631337658SMarcel Moolenaar 73731337658SMarcel Moolenaar memcpy(xbp->xb_curp, str, len); 73831337658SMarcel Moolenaar xbp->xb_curp += len; 73931337658SMarcel Moolenaar } 74031337658SMarcel Moolenaar 741788ca347SMarcel Moolenaar /* 742788ca347SMarcel Moolenaar * Append the given NUL-terminated string to the given buffer 743788ca347SMarcel Moolenaar */ 744788ca347SMarcel Moolenaar static void 745788ca347SMarcel Moolenaar xo_buf_append_str (xo_buffer_t *xbp, const char *str) 746788ca347SMarcel Moolenaar { 747788ca347SMarcel Moolenaar int len = strlen(str); 748788ca347SMarcel Moolenaar 749788ca347SMarcel Moolenaar if (!xo_buf_has_room(xbp, len)) 750788ca347SMarcel Moolenaar return; 751788ca347SMarcel Moolenaar 752788ca347SMarcel Moolenaar memcpy(xbp->xb_curp, str, len); 753788ca347SMarcel Moolenaar xbp->xb_curp += len; 754788ca347SMarcel Moolenaar } 755788ca347SMarcel Moolenaar 75631337658SMarcel Moolenaar static void 75731337658SMarcel Moolenaar xo_buf_escape (xo_handle_t *xop, xo_buffer_t *xbp, 75831337658SMarcel Moolenaar const char *str, int len, xo_xff_flags_t flags) 75931337658SMarcel Moolenaar { 76031337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, len)) 76131337658SMarcel Moolenaar return; 76231337658SMarcel Moolenaar 76331337658SMarcel Moolenaar memcpy(xbp->xb_curp, str, len); 76431337658SMarcel Moolenaar 765788ca347SMarcel Moolenaar switch (xo_style(xop)) { 76631337658SMarcel Moolenaar case XO_STYLE_XML: 76731337658SMarcel Moolenaar case XO_STYLE_HTML: 76831337658SMarcel Moolenaar len = xo_escape_xml(xbp, len, (flags & XFF_ATTR)); 76931337658SMarcel Moolenaar break; 77031337658SMarcel Moolenaar 77131337658SMarcel Moolenaar case XO_STYLE_JSON: 77231337658SMarcel Moolenaar len = xo_escape_json(xbp, len); 77331337658SMarcel Moolenaar break; 77431337658SMarcel Moolenaar } 77531337658SMarcel Moolenaar 77631337658SMarcel Moolenaar xbp->xb_curp += len; 77731337658SMarcel Moolenaar } 77831337658SMarcel Moolenaar 77931337658SMarcel Moolenaar /* 78031337658SMarcel Moolenaar * Write the current contents of the data buffer using the handle's 78131337658SMarcel Moolenaar * xo_write function. 78231337658SMarcel Moolenaar */ 783545ddfbeSMarcel Moolenaar static int 78431337658SMarcel Moolenaar xo_write (xo_handle_t *xop) 78531337658SMarcel Moolenaar { 786545ddfbeSMarcel Moolenaar int rc = 0; 78731337658SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_data; 78831337658SMarcel Moolenaar 78931337658SMarcel Moolenaar if (xbp->xb_curp != xbp->xb_bufp) { 79031337658SMarcel Moolenaar xo_buf_append(xbp, "", 1); /* Append ending NUL */ 79131337658SMarcel Moolenaar xo_anchor_clear(xop); 792545ddfbeSMarcel Moolenaar rc = xop->xo_write(xop->xo_opaque, xbp->xb_bufp); 79331337658SMarcel Moolenaar xbp->xb_curp = xbp->xb_bufp; 79431337658SMarcel Moolenaar } 79531337658SMarcel Moolenaar 79631337658SMarcel Moolenaar /* Turn off the flags that don't survive across writes */ 79731337658SMarcel Moolenaar xop->xo_flags &= ~(XOF_UNITS_PENDING); 798545ddfbeSMarcel Moolenaar 799545ddfbeSMarcel Moolenaar return rc; 80031337658SMarcel Moolenaar } 80131337658SMarcel Moolenaar 80231337658SMarcel Moolenaar /* 80331337658SMarcel Moolenaar * Format arguments into our buffer. If a custom formatter has been set, 80431337658SMarcel Moolenaar * we use that to do the work; otherwise we vsnprintf(). 80531337658SMarcel Moolenaar */ 80631337658SMarcel Moolenaar static int 80731337658SMarcel Moolenaar xo_vsnprintf (xo_handle_t *xop, xo_buffer_t *xbp, const char *fmt, va_list vap) 80831337658SMarcel Moolenaar { 80931337658SMarcel Moolenaar va_list va_local; 81031337658SMarcel Moolenaar int rc; 81131337658SMarcel Moolenaar int left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp); 81231337658SMarcel Moolenaar 81331337658SMarcel Moolenaar va_copy(va_local, vap); 81431337658SMarcel Moolenaar 81531337658SMarcel Moolenaar if (xop->xo_formatter) 81631337658SMarcel Moolenaar rc = xop->xo_formatter(xop, xbp->xb_curp, left, fmt, va_local); 81731337658SMarcel Moolenaar else 81831337658SMarcel Moolenaar rc = vsnprintf(xbp->xb_curp, left, fmt, va_local); 81931337658SMarcel Moolenaar 820788ca347SMarcel Moolenaar if (rc >= left) { 821c600d307SMarcel Moolenaar if (!xo_buf_has_room(xbp, rc)) { 822c600d307SMarcel Moolenaar va_end(va_local); 82331337658SMarcel Moolenaar return -1; 824c600d307SMarcel Moolenaar } 82531337658SMarcel Moolenaar 82631337658SMarcel Moolenaar /* 82731337658SMarcel Moolenaar * After we call vsnprintf(), the stage of vap is not defined. 82831337658SMarcel Moolenaar * We need to copy it before we pass. Then we have to do our 82931337658SMarcel Moolenaar * own logic below to move it along. This is because the 830788ca347SMarcel Moolenaar * implementation can have va_list be a pointer (bsd) or a 83131337658SMarcel Moolenaar * structure (macosx) or anything in between. 83231337658SMarcel Moolenaar */ 83331337658SMarcel Moolenaar 83431337658SMarcel Moolenaar va_end(va_local); /* Reset vap to the start */ 83531337658SMarcel Moolenaar va_copy(va_local, vap); 83631337658SMarcel Moolenaar 83731337658SMarcel Moolenaar left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp); 83831337658SMarcel Moolenaar if (xop->xo_formatter) 839788ca347SMarcel Moolenaar rc = xop->xo_formatter(xop, xbp->xb_curp, left, fmt, va_local); 84031337658SMarcel Moolenaar else 84131337658SMarcel Moolenaar rc = vsnprintf(xbp->xb_curp, left, fmt, va_local); 84231337658SMarcel Moolenaar } 84331337658SMarcel Moolenaar va_end(va_local); 84431337658SMarcel Moolenaar 84531337658SMarcel Moolenaar return rc; 84631337658SMarcel Moolenaar } 84731337658SMarcel Moolenaar 84831337658SMarcel Moolenaar /* 84931337658SMarcel Moolenaar * Print some data thru the handle. 85031337658SMarcel Moolenaar */ 85131337658SMarcel Moolenaar static int 85231337658SMarcel Moolenaar xo_printf_v (xo_handle_t *xop, const char *fmt, va_list vap) 85331337658SMarcel Moolenaar { 85431337658SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_data; 85531337658SMarcel Moolenaar int left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp); 85631337658SMarcel Moolenaar int rc; 85731337658SMarcel Moolenaar va_list va_local; 85831337658SMarcel Moolenaar 85931337658SMarcel Moolenaar va_copy(va_local, vap); 86031337658SMarcel Moolenaar 86131337658SMarcel Moolenaar rc = vsnprintf(xbp->xb_curp, left, fmt, va_local); 86231337658SMarcel Moolenaar 86331337658SMarcel Moolenaar if (rc > xbp->xb_size) { 864c600d307SMarcel Moolenaar if (!xo_buf_has_room(xbp, rc)) { 865c600d307SMarcel Moolenaar va_end(va_local); 86631337658SMarcel Moolenaar return -1; 867c600d307SMarcel Moolenaar } 86831337658SMarcel Moolenaar 86931337658SMarcel Moolenaar va_end(va_local); /* Reset vap to the start */ 87031337658SMarcel Moolenaar va_copy(va_local, vap); 87131337658SMarcel Moolenaar 87231337658SMarcel Moolenaar left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp); 87331337658SMarcel Moolenaar rc = vsnprintf(xbp->xb_curp, left, fmt, va_local); 87431337658SMarcel Moolenaar } 87531337658SMarcel Moolenaar 87631337658SMarcel Moolenaar va_end(va_local); 87731337658SMarcel Moolenaar 87831337658SMarcel Moolenaar if (rc > 0) 87931337658SMarcel Moolenaar xbp->xb_curp += rc; 88031337658SMarcel Moolenaar 88131337658SMarcel Moolenaar return rc; 88231337658SMarcel Moolenaar } 88331337658SMarcel Moolenaar 88431337658SMarcel Moolenaar static int 88531337658SMarcel Moolenaar xo_printf (xo_handle_t *xop, const char *fmt, ...) 88631337658SMarcel Moolenaar { 88731337658SMarcel Moolenaar int rc; 88831337658SMarcel Moolenaar va_list vap; 88931337658SMarcel Moolenaar 89031337658SMarcel Moolenaar va_start(vap, fmt); 89131337658SMarcel Moolenaar 89231337658SMarcel Moolenaar rc = xo_printf_v(xop, fmt, vap); 89331337658SMarcel Moolenaar 89431337658SMarcel Moolenaar va_end(vap); 89531337658SMarcel Moolenaar return rc; 89631337658SMarcel Moolenaar } 89731337658SMarcel Moolenaar 89831337658SMarcel Moolenaar /* 89931337658SMarcel Moolenaar * These next few function are make The Essential UTF-8 Ginsu Knife. 90031337658SMarcel Moolenaar * Identify an input and output character, and convert it. 90131337658SMarcel Moolenaar */ 90231337658SMarcel Moolenaar static int xo_utf8_bits[7] = { 0, 0x7f, 0x1f, 0x0f, 0x07, 0x03, 0x01 }; 90331337658SMarcel Moolenaar 90431337658SMarcel Moolenaar static int 90531337658SMarcel Moolenaar xo_is_utf8 (char ch) 90631337658SMarcel Moolenaar { 90731337658SMarcel Moolenaar return (ch & 0x80); 90831337658SMarcel Moolenaar } 90931337658SMarcel Moolenaar 91031337658SMarcel Moolenaar static int 91131337658SMarcel Moolenaar xo_utf8_to_wc_len (const char *buf) 91231337658SMarcel Moolenaar { 91331337658SMarcel Moolenaar unsigned b = (unsigned char) *buf; 91431337658SMarcel Moolenaar int len; 91531337658SMarcel Moolenaar 91631337658SMarcel Moolenaar if ((b & 0x80) == 0x0) 91731337658SMarcel Moolenaar len = 1; 91831337658SMarcel Moolenaar else if ((b & 0xe0) == 0xc0) 91931337658SMarcel Moolenaar len = 2; 92031337658SMarcel Moolenaar else if ((b & 0xf0) == 0xe0) 92131337658SMarcel Moolenaar len = 3; 92231337658SMarcel Moolenaar else if ((b & 0xf8) == 0xf0) 92331337658SMarcel Moolenaar len = 4; 92431337658SMarcel Moolenaar else if ((b & 0xfc) == 0xf8) 92531337658SMarcel Moolenaar len = 5; 92631337658SMarcel Moolenaar else if ((b & 0xfe) == 0xfc) 92731337658SMarcel Moolenaar len = 6; 92831337658SMarcel Moolenaar else 92931337658SMarcel Moolenaar len = -1; 93031337658SMarcel Moolenaar 93131337658SMarcel Moolenaar return len; 93231337658SMarcel Moolenaar } 93331337658SMarcel Moolenaar 93431337658SMarcel Moolenaar static int 93531337658SMarcel Moolenaar xo_buf_utf8_len (xo_handle_t *xop, const char *buf, int bufsiz) 93631337658SMarcel Moolenaar { 93731337658SMarcel Moolenaar 93831337658SMarcel Moolenaar unsigned b = (unsigned char) *buf; 93931337658SMarcel Moolenaar int len, i; 94031337658SMarcel Moolenaar 94131337658SMarcel Moolenaar len = xo_utf8_to_wc_len(buf); 94231337658SMarcel Moolenaar if (len == -1) { 94331337658SMarcel Moolenaar xo_failure(xop, "invalid UTF-8 data: %02hhx", b); 94431337658SMarcel Moolenaar return -1; 94531337658SMarcel Moolenaar } 94631337658SMarcel Moolenaar 94731337658SMarcel Moolenaar if (len > bufsiz) { 94831337658SMarcel Moolenaar xo_failure(xop, "invalid UTF-8 data (short): %02hhx (%d/%d)", 94931337658SMarcel Moolenaar b, len, bufsiz); 95031337658SMarcel Moolenaar return -1; 95131337658SMarcel Moolenaar } 95231337658SMarcel Moolenaar 95331337658SMarcel Moolenaar for (i = 2; i < len; i++) { 95431337658SMarcel Moolenaar b = (unsigned char ) buf[i]; 95531337658SMarcel Moolenaar if ((b & 0xc0) != 0x80) { 95631337658SMarcel Moolenaar xo_failure(xop, "invalid UTF-8 data (byte %d): %x", i, b); 95731337658SMarcel Moolenaar return -1; 95831337658SMarcel Moolenaar } 95931337658SMarcel Moolenaar } 96031337658SMarcel Moolenaar 96131337658SMarcel Moolenaar return len; 96231337658SMarcel Moolenaar } 96331337658SMarcel Moolenaar 96431337658SMarcel Moolenaar /* 96531337658SMarcel Moolenaar * Build a wide character from the input buffer; the number of 96631337658SMarcel Moolenaar * bits we pull off the first character is dependent on the length, 96731337658SMarcel Moolenaar * but we put 6 bits off all other bytes. 96831337658SMarcel Moolenaar */ 96931337658SMarcel Moolenaar static wchar_t 97031337658SMarcel Moolenaar xo_utf8_char (const char *buf, int len) 97131337658SMarcel Moolenaar { 97231337658SMarcel Moolenaar int i; 97331337658SMarcel Moolenaar wchar_t wc; 97431337658SMarcel Moolenaar const unsigned char *cp = (const unsigned char *) buf; 97531337658SMarcel Moolenaar 97631337658SMarcel Moolenaar wc = *cp & xo_utf8_bits[len]; 97731337658SMarcel Moolenaar for (i = 1; i < len; i++) { 97831337658SMarcel Moolenaar wc <<= 6; 97931337658SMarcel Moolenaar wc |= cp[i] & 0x3f; 98031337658SMarcel Moolenaar if ((cp[i] & 0xc0) != 0x80) 98131337658SMarcel Moolenaar return (wchar_t) -1; 98231337658SMarcel Moolenaar } 98331337658SMarcel Moolenaar 98431337658SMarcel Moolenaar return wc; 98531337658SMarcel Moolenaar } 98631337658SMarcel Moolenaar 98731337658SMarcel Moolenaar /* 98831337658SMarcel Moolenaar * Determine the number of bytes needed to encode a wide character. 98931337658SMarcel Moolenaar */ 99031337658SMarcel Moolenaar static int 99131337658SMarcel Moolenaar xo_utf8_emit_len (wchar_t wc) 99231337658SMarcel Moolenaar { 99331337658SMarcel Moolenaar int len; 99431337658SMarcel Moolenaar 99531337658SMarcel Moolenaar if ((wc & ((1<<7) - 1)) == wc) /* Simple case */ 99631337658SMarcel Moolenaar len = 1; 99731337658SMarcel Moolenaar else if ((wc & ((1<<11) - 1)) == wc) 99831337658SMarcel Moolenaar len = 2; 99931337658SMarcel Moolenaar else if ((wc & ((1<<16) - 1)) == wc) 100031337658SMarcel Moolenaar len = 3; 100131337658SMarcel Moolenaar else if ((wc & ((1<<21) - 1)) == wc) 100231337658SMarcel Moolenaar len = 4; 100331337658SMarcel Moolenaar else if ((wc & ((1<<26) - 1)) == wc) 100431337658SMarcel Moolenaar len = 5; 100531337658SMarcel Moolenaar else 100631337658SMarcel Moolenaar len = 6; 100731337658SMarcel Moolenaar 100831337658SMarcel Moolenaar return len; 100931337658SMarcel Moolenaar } 101031337658SMarcel Moolenaar 101131337658SMarcel Moolenaar static void 101231337658SMarcel Moolenaar xo_utf8_emit_char (char *buf, int len, wchar_t wc) 101331337658SMarcel Moolenaar { 101431337658SMarcel Moolenaar int i; 101531337658SMarcel Moolenaar 101631337658SMarcel Moolenaar if (len == 1) { /* Simple case */ 101731337658SMarcel Moolenaar buf[0] = wc & 0x7f; 101831337658SMarcel Moolenaar return; 101931337658SMarcel Moolenaar } 102031337658SMarcel Moolenaar 102131337658SMarcel Moolenaar for (i = len - 1; i >= 0; i--) { 102231337658SMarcel Moolenaar buf[i] = 0x80 | (wc & 0x3f); 102331337658SMarcel Moolenaar wc >>= 6; 102431337658SMarcel Moolenaar } 102531337658SMarcel Moolenaar 102631337658SMarcel Moolenaar buf[0] &= xo_utf8_bits[len]; 102731337658SMarcel Moolenaar buf[0] |= ~xo_utf8_bits[len] << 1; 102831337658SMarcel Moolenaar } 102931337658SMarcel Moolenaar 103031337658SMarcel Moolenaar static int 103131337658SMarcel Moolenaar xo_buf_append_locale_from_utf8 (xo_handle_t *xop, xo_buffer_t *xbp, 103231337658SMarcel Moolenaar const char *ibuf, int ilen) 103331337658SMarcel Moolenaar { 103431337658SMarcel Moolenaar wchar_t wc; 103531337658SMarcel Moolenaar int len; 103631337658SMarcel Moolenaar 103731337658SMarcel Moolenaar /* 103831337658SMarcel Moolenaar * Build our wide character from the input buffer; the number of 103931337658SMarcel Moolenaar * bits we pull off the first character is dependent on the length, 104031337658SMarcel Moolenaar * but we put 6 bits off all other bytes. 104131337658SMarcel Moolenaar */ 104231337658SMarcel Moolenaar wc = xo_utf8_char(ibuf, ilen); 104331337658SMarcel Moolenaar if (wc == (wchar_t) -1) { 104431337658SMarcel Moolenaar xo_failure(xop, "invalid utf-8 byte sequence"); 104531337658SMarcel Moolenaar return 0; 104631337658SMarcel Moolenaar } 104731337658SMarcel Moolenaar 104831337658SMarcel Moolenaar if (xop->xo_flags & XOF_NO_LOCALE) { 104931337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, ilen)) 105031337658SMarcel Moolenaar return 0; 105131337658SMarcel Moolenaar 105231337658SMarcel Moolenaar memcpy(xbp->xb_curp, ibuf, ilen); 105331337658SMarcel Moolenaar xbp->xb_curp += ilen; 105431337658SMarcel Moolenaar 105531337658SMarcel Moolenaar } else { 105631337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, MB_LEN_MAX + 1)) 105731337658SMarcel Moolenaar return 0; 105831337658SMarcel Moolenaar 105931337658SMarcel Moolenaar bzero(&xop->xo_mbstate, sizeof(xop->xo_mbstate)); 106031337658SMarcel Moolenaar len = wcrtomb(xbp->xb_curp, wc, &xop->xo_mbstate); 106131337658SMarcel Moolenaar 106231337658SMarcel Moolenaar if (len <= 0) { 106331337658SMarcel Moolenaar xo_failure(xop, "could not convert wide char: %lx", 106431337658SMarcel Moolenaar (unsigned long) wc); 106531337658SMarcel Moolenaar return 0; 106631337658SMarcel Moolenaar } 106731337658SMarcel Moolenaar xbp->xb_curp += len; 106831337658SMarcel Moolenaar } 106931337658SMarcel Moolenaar 107031337658SMarcel Moolenaar return wcwidth(wc); 107131337658SMarcel Moolenaar } 107231337658SMarcel Moolenaar 107331337658SMarcel Moolenaar static void 107431337658SMarcel Moolenaar xo_buf_append_locale (xo_handle_t *xop, xo_buffer_t *xbp, 107531337658SMarcel Moolenaar const char *cp, int len) 107631337658SMarcel Moolenaar { 107731337658SMarcel Moolenaar const char *sp = cp, *ep = cp + len; 107831337658SMarcel Moolenaar unsigned save_off = xbp->xb_bufp - xbp->xb_curp; 107931337658SMarcel Moolenaar int slen; 108031337658SMarcel Moolenaar int cols = 0; 108131337658SMarcel Moolenaar 108231337658SMarcel Moolenaar for ( ; cp < ep; cp++) { 108331337658SMarcel Moolenaar if (!xo_is_utf8(*cp)) { 108431337658SMarcel Moolenaar cols += 1; 108531337658SMarcel Moolenaar continue; 108631337658SMarcel Moolenaar } 108731337658SMarcel Moolenaar 108831337658SMarcel Moolenaar /* 108931337658SMarcel Moolenaar * We're looking at a non-ascii UTF-8 character. 109031337658SMarcel Moolenaar * First we copy the previous data. 109131337658SMarcel Moolenaar * Then we need find the length and validate it. 109231337658SMarcel Moolenaar * Then we turn it into a wide string. 109331337658SMarcel Moolenaar * Then we turn it into a localized string. 109431337658SMarcel Moolenaar * Then we repeat. Isn't i18n fun? 109531337658SMarcel Moolenaar */ 109631337658SMarcel Moolenaar if (sp != cp) 109731337658SMarcel Moolenaar xo_buf_append(xbp, sp, cp - sp); /* Append previous data */ 109831337658SMarcel Moolenaar 109931337658SMarcel Moolenaar slen = xo_buf_utf8_len(xop, cp, ep - cp); 110031337658SMarcel Moolenaar if (slen <= 0) { 110131337658SMarcel Moolenaar /* Bad data; back it all out */ 110231337658SMarcel Moolenaar xbp->xb_curp = xbp->xb_bufp + save_off; 110331337658SMarcel Moolenaar return; 110431337658SMarcel Moolenaar } 110531337658SMarcel Moolenaar 110631337658SMarcel Moolenaar cols += xo_buf_append_locale_from_utf8(xop, xbp, cp, slen); 110731337658SMarcel Moolenaar 110831337658SMarcel Moolenaar /* Next time thru, we'll start at the next character */ 110931337658SMarcel Moolenaar cp += slen - 1; 111031337658SMarcel Moolenaar sp = cp + 1; 111131337658SMarcel Moolenaar } 111231337658SMarcel Moolenaar 111331337658SMarcel Moolenaar /* Update column values */ 111431337658SMarcel Moolenaar if (xop->xo_flags & XOF_COLUMNS) 111531337658SMarcel Moolenaar xop->xo_columns += cols; 111631337658SMarcel Moolenaar if (xop->xo_flags & XOF_ANCHOR) 111731337658SMarcel Moolenaar xop->xo_anchor_columns += cols; 111831337658SMarcel Moolenaar 111931337658SMarcel Moolenaar /* Before we fall into the basic logic below, we need reset len */ 112031337658SMarcel Moolenaar len = ep - sp; 112131337658SMarcel Moolenaar if (len != 0) /* Append trailing data */ 112231337658SMarcel Moolenaar xo_buf_append(xbp, sp, len); 112331337658SMarcel Moolenaar } 112431337658SMarcel Moolenaar 112531337658SMarcel Moolenaar /* 112631337658SMarcel Moolenaar * Append the given string to the given buffer 112731337658SMarcel Moolenaar */ 112831337658SMarcel Moolenaar static void 112931337658SMarcel Moolenaar xo_data_append (xo_handle_t *xop, const char *str, int len) 113031337658SMarcel Moolenaar { 113131337658SMarcel Moolenaar xo_buf_append(&xop->xo_data, str, len); 113231337658SMarcel Moolenaar } 113331337658SMarcel Moolenaar 113431337658SMarcel Moolenaar /* 113531337658SMarcel Moolenaar * Append the given string to the given buffer 113631337658SMarcel Moolenaar */ 113731337658SMarcel Moolenaar static void 113831337658SMarcel Moolenaar xo_data_escape (xo_handle_t *xop, const char *str, int len) 113931337658SMarcel Moolenaar { 114031337658SMarcel Moolenaar xo_buf_escape(xop, &xop->xo_data, str, len, 0); 114131337658SMarcel Moolenaar } 114231337658SMarcel Moolenaar 114331337658SMarcel Moolenaar /* 114431337658SMarcel Moolenaar * Generate a warning. Normally, this is a text message written to 114531337658SMarcel Moolenaar * standard error. If the XOF_WARN_XML flag is set, then we generate 114631337658SMarcel Moolenaar * XMLified content on standard output. 114731337658SMarcel Moolenaar */ 114831337658SMarcel Moolenaar static void 114931337658SMarcel Moolenaar xo_warn_hcv (xo_handle_t *xop, int code, int check_warn, 115031337658SMarcel Moolenaar const char *fmt, va_list vap) 115131337658SMarcel Moolenaar { 115231337658SMarcel Moolenaar xop = xo_default(xop); 115331337658SMarcel Moolenaar if (check_warn && !(xop->xo_flags & XOF_WARN)) 115431337658SMarcel Moolenaar return; 115531337658SMarcel Moolenaar 115631337658SMarcel Moolenaar if (fmt == NULL) 115731337658SMarcel Moolenaar return; 115831337658SMarcel Moolenaar 115931337658SMarcel Moolenaar int len = strlen(fmt); 116031337658SMarcel Moolenaar int plen = xo_program ? strlen(xo_program) : 0; 1161545ddfbeSMarcel Moolenaar char *newfmt = alloca(len + 1 + plen + 2); /* NUL, and ": " */ 116231337658SMarcel Moolenaar 116331337658SMarcel Moolenaar if (plen) { 116431337658SMarcel Moolenaar memcpy(newfmt, xo_program, plen); 116531337658SMarcel Moolenaar newfmt[plen++] = ':'; 116631337658SMarcel Moolenaar newfmt[plen++] = ' '; 116731337658SMarcel Moolenaar } 116831337658SMarcel Moolenaar memcpy(newfmt + plen, fmt, len); 116931337658SMarcel Moolenaar newfmt[len + plen] = '\0'; 117031337658SMarcel Moolenaar 117131337658SMarcel Moolenaar if (xop->xo_flags & XOF_WARN_XML) { 117231337658SMarcel Moolenaar static char err_open[] = "<error>"; 117331337658SMarcel Moolenaar static char err_close[] = "</error>"; 117431337658SMarcel Moolenaar static char msg_open[] = "<message>"; 117531337658SMarcel Moolenaar static char msg_close[] = "</message>"; 117631337658SMarcel Moolenaar 117731337658SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_data; 117831337658SMarcel Moolenaar 117931337658SMarcel Moolenaar xo_buf_append(xbp, err_open, sizeof(err_open) - 1); 118031337658SMarcel Moolenaar xo_buf_append(xbp, msg_open, sizeof(msg_open) - 1); 118131337658SMarcel Moolenaar 118231337658SMarcel Moolenaar va_list va_local; 118331337658SMarcel Moolenaar va_copy(va_local, vap); 118431337658SMarcel Moolenaar 118531337658SMarcel Moolenaar int left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp); 118631337658SMarcel Moolenaar int rc = vsnprintf(xbp->xb_curp, left, newfmt, vap); 118731337658SMarcel Moolenaar if (rc > xbp->xb_size) { 1188c600d307SMarcel Moolenaar if (!xo_buf_has_room(xbp, rc)) { 1189c600d307SMarcel Moolenaar va_end(va_local); 119031337658SMarcel Moolenaar return; 1191c600d307SMarcel Moolenaar } 119231337658SMarcel Moolenaar 119331337658SMarcel Moolenaar va_end(vap); /* Reset vap to the start */ 119431337658SMarcel Moolenaar va_copy(vap, va_local); 119531337658SMarcel Moolenaar 119631337658SMarcel Moolenaar left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp); 119731337658SMarcel Moolenaar rc = vsnprintf(xbp->xb_curp, left, fmt, vap); 119831337658SMarcel Moolenaar } 119931337658SMarcel Moolenaar va_end(va_local); 120031337658SMarcel Moolenaar 120131337658SMarcel Moolenaar rc = xo_escape_xml(xbp, rc, 1); 120231337658SMarcel Moolenaar xbp->xb_curp += rc; 120331337658SMarcel Moolenaar 120431337658SMarcel Moolenaar xo_buf_append(xbp, msg_close, sizeof(msg_close) - 1); 120531337658SMarcel Moolenaar xo_buf_append(xbp, err_close, sizeof(err_close) - 1); 120631337658SMarcel Moolenaar 1207545ddfbeSMarcel Moolenaar if (code >= 0) { 120831337658SMarcel Moolenaar const char *msg = strerror(code); 120931337658SMarcel Moolenaar if (msg) { 121031337658SMarcel Moolenaar xo_buf_append(xbp, ": ", 2); 121131337658SMarcel Moolenaar xo_buf_append(xbp, msg, strlen(msg)); 121231337658SMarcel Moolenaar } 121331337658SMarcel Moolenaar } 121431337658SMarcel Moolenaar 121531337658SMarcel Moolenaar xo_buf_append(xbp, "\n", 2); /* Append newline and NUL to string */ 1216545ddfbeSMarcel Moolenaar (void) xo_write(xop); 121731337658SMarcel Moolenaar 121831337658SMarcel Moolenaar } else { 121931337658SMarcel Moolenaar vfprintf(stderr, newfmt, vap); 1220545ddfbeSMarcel Moolenaar if (code >= 0) { 1221545ddfbeSMarcel Moolenaar const char *msg = strerror(code); 1222545ddfbeSMarcel Moolenaar if (msg) 1223545ddfbeSMarcel Moolenaar fprintf(stderr, ": %s", msg); 1224545ddfbeSMarcel Moolenaar } 1225545ddfbeSMarcel Moolenaar fprintf(stderr, "\n"); 122631337658SMarcel Moolenaar } 122731337658SMarcel Moolenaar } 122831337658SMarcel Moolenaar 122931337658SMarcel Moolenaar void 123031337658SMarcel Moolenaar xo_warn_hc (xo_handle_t *xop, int code, const char *fmt, ...) 123131337658SMarcel Moolenaar { 123231337658SMarcel Moolenaar va_list vap; 123331337658SMarcel Moolenaar 123431337658SMarcel Moolenaar va_start(vap, fmt); 123531337658SMarcel Moolenaar xo_warn_hcv(xop, code, 0, fmt, vap); 123631337658SMarcel Moolenaar va_end(vap); 123731337658SMarcel Moolenaar } 123831337658SMarcel Moolenaar 123931337658SMarcel Moolenaar void 124031337658SMarcel Moolenaar xo_warn_c (int code, const char *fmt, ...) 124131337658SMarcel Moolenaar { 124231337658SMarcel Moolenaar va_list vap; 124331337658SMarcel Moolenaar 124431337658SMarcel Moolenaar va_start(vap, fmt); 1245545ddfbeSMarcel Moolenaar xo_warn_hcv(NULL, code, 0, fmt, vap); 124631337658SMarcel Moolenaar va_end(vap); 124731337658SMarcel Moolenaar } 124831337658SMarcel Moolenaar 124931337658SMarcel Moolenaar void 125031337658SMarcel Moolenaar xo_warn (const char *fmt, ...) 125131337658SMarcel Moolenaar { 125231337658SMarcel Moolenaar int code = errno; 125331337658SMarcel Moolenaar va_list vap; 125431337658SMarcel Moolenaar 125531337658SMarcel Moolenaar va_start(vap, fmt); 125631337658SMarcel Moolenaar xo_warn_hcv(NULL, code, 0, fmt, vap); 125731337658SMarcel Moolenaar va_end(vap); 125831337658SMarcel Moolenaar } 125931337658SMarcel Moolenaar 126031337658SMarcel Moolenaar void 126131337658SMarcel Moolenaar xo_warnx (const char *fmt, ...) 126231337658SMarcel Moolenaar { 126331337658SMarcel Moolenaar va_list vap; 126431337658SMarcel Moolenaar 126531337658SMarcel Moolenaar va_start(vap, fmt); 126631337658SMarcel Moolenaar xo_warn_hcv(NULL, -1, 0, fmt, vap); 126731337658SMarcel Moolenaar va_end(vap); 126831337658SMarcel Moolenaar } 126931337658SMarcel Moolenaar 127031337658SMarcel Moolenaar void 127131337658SMarcel Moolenaar xo_err (int eval, const char *fmt, ...) 127231337658SMarcel Moolenaar { 127331337658SMarcel Moolenaar int code = errno; 127431337658SMarcel Moolenaar va_list vap; 127531337658SMarcel Moolenaar 127631337658SMarcel Moolenaar va_start(vap, fmt); 127731337658SMarcel Moolenaar xo_warn_hcv(NULL, code, 0, fmt, vap); 127831337658SMarcel Moolenaar va_end(vap); 127931337658SMarcel Moolenaar xo_finish(); 128031337658SMarcel Moolenaar exit(eval); 128131337658SMarcel Moolenaar } 128231337658SMarcel Moolenaar 128331337658SMarcel Moolenaar void 128431337658SMarcel Moolenaar xo_errx (int eval, const char *fmt, ...) 128531337658SMarcel Moolenaar { 128631337658SMarcel Moolenaar va_list vap; 128731337658SMarcel Moolenaar 128831337658SMarcel Moolenaar va_start(vap, fmt); 128931337658SMarcel Moolenaar xo_warn_hcv(NULL, -1, 0, fmt, vap); 129031337658SMarcel Moolenaar va_end(vap); 129131337658SMarcel Moolenaar xo_finish(); 129231337658SMarcel Moolenaar exit(eval); 129331337658SMarcel Moolenaar } 129431337658SMarcel Moolenaar 129531337658SMarcel Moolenaar void 129631337658SMarcel Moolenaar xo_errc (int eval, int code, const char *fmt, ...) 129731337658SMarcel Moolenaar { 129831337658SMarcel Moolenaar va_list vap; 129931337658SMarcel Moolenaar 130031337658SMarcel Moolenaar va_start(vap, fmt); 130131337658SMarcel Moolenaar xo_warn_hcv(NULL, code, 0, fmt, vap); 130231337658SMarcel Moolenaar va_end(vap); 130331337658SMarcel Moolenaar xo_finish(); 130431337658SMarcel Moolenaar exit(eval); 130531337658SMarcel Moolenaar } 130631337658SMarcel Moolenaar 130731337658SMarcel Moolenaar /* 130831337658SMarcel Moolenaar * Generate a warning. Normally, this is a text message written to 130931337658SMarcel Moolenaar * standard error. If the XOF_WARN_XML flag is set, then we generate 131031337658SMarcel Moolenaar * XMLified content on standard output. 131131337658SMarcel Moolenaar */ 131231337658SMarcel Moolenaar void 131331337658SMarcel Moolenaar xo_message_hcv (xo_handle_t *xop, int code, const char *fmt, va_list vap) 131431337658SMarcel Moolenaar { 131531337658SMarcel Moolenaar static char msg_open[] = "<message>"; 131631337658SMarcel Moolenaar static char msg_close[] = "</message>"; 131731337658SMarcel Moolenaar xo_buffer_t *xbp; 131831337658SMarcel Moolenaar int rc; 131931337658SMarcel Moolenaar va_list va_local; 132031337658SMarcel Moolenaar 132131337658SMarcel Moolenaar xop = xo_default(xop); 132231337658SMarcel Moolenaar 132331337658SMarcel Moolenaar if (fmt == NULL || *fmt == '\0') 132431337658SMarcel Moolenaar return; 132531337658SMarcel Moolenaar 132631337658SMarcel Moolenaar int need_nl = (fmt[strlen(fmt) - 1] != '\n'); 132731337658SMarcel Moolenaar 1328788ca347SMarcel Moolenaar switch (xo_style(xop)) { 132931337658SMarcel Moolenaar case XO_STYLE_XML: 133031337658SMarcel Moolenaar xbp = &xop->xo_data; 133131337658SMarcel Moolenaar if (xop->xo_flags & XOF_PRETTY) 133231337658SMarcel Moolenaar xo_buf_indent(xop, xop->xo_indent_by); 133331337658SMarcel Moolenaar xo_buf_append(xbp, msg_open, sizeof(msg_open) - 1); 133431337658SMarcel Moolenaar 133531337658SMarcel Moolenaar va_copy(va_local, vap); 133631337658SMarcel Moolenaar 133731337658SMarcel Moolenaar int left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp); 133831337658SMarcel Moolenaar rc = vsnprintf(xbp->xb_curp, left, fmt, vap); 133931337658SMarcel Moolenaar if (rc > xbp->xb_size) { 1340c600d307SMarcel Moolenaar if (!xo_buf_has_room(xbp, rc)) { 1341c600d307SMarcel Moolenaar va_end(va_local); 134231337658SMarcel Moolenaar return; 1343c600d307SMarcel Moolenaar } 134431337658SMarcel Moolenaar 134531337658SMarcel Moolenaar va_end(vap); /* Reset vap to the start */ 134631337658SMarcel Moolenaar va_copy(vap, va_local); 134731337658SMarcel Moolenaar 134831337658SMarcel Moolenaar left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp); 134931337658SMarcel Moolenaar rc = vsnprintf(xbp->xb_curp, left, fmt, vap); 135031337658SMarcel Moolenaar } 135131337658SMarcel Moolenaar va_end(va_local); 135231337658SMarcel Moolenaar 135331337658SMarcel Moolenaar rc = xo_escape_xml(xbp, rc, 1); 135431337658SMarcel Moolenaar xbp->xb_curp += rc; 135531337658SMarcel Moolenaar 135631337658SMarcel Moolenaar if (need_nl && code > 0) { 135731337658SMarcel Moolenaar const char *msg = strerror(code); 135831337658SMarcel Moolenaar if (msg) { 135931337658SMarcel Moolenaar xo_buf_append(xbp, ": ", 2); 136031337658SMarcel Moolenaar xo_buf_append(xbp, msg, strlen(msg)); 136131337658SMarcel Moolenaar } 136231337658SMarcel Moolenaar } 136331337658SMarcel Moolenaar 136431337658SMarcel Moolenaar xo_buf_append(xbp, msg_close, sizeof(msg_close) - 1); 136531337658SMarcel Moolenaar if (need_nl) 136631337658SMarcel Moolenaar xo_buf_append(xbp, "\n", 2); /* Append newline and NUL to string */ 1367545ddfbeSMarcel Moolenaar (void) xo_write(xop); 136831337658SMarcel Moolenaar break; 136931337658SMarcel Moolenaar 137031337658SMarcel Moolenaar case XO_STYLE_HTML: 137131337658SMarcel Moolenaar { 137231337658SMarcel Moolenaar char buf[BUFSIZ], *bp = buf, *cp; 137331337658SMarcel Moolenaar int bufsiz = sizeof(buf); 137431337658SMarcel Moolenaar int rc2; 137531337658SMarcel Moolenaar 137631337658SMarcel Moolenaar va_copy(va_local, vap); 137731337658SMarcel Moolenaar 1378c600d307SMarcel Moolenaar rc = vsnprintf(bp, bufsiz, fmt, va_local); 137931337658SMarcel Moolenaar if (rc > bufsiz) { 138031337658SMarcel Moolenaar bufsiz = rc + BUFSIZ; 138131337658SMarcel Moolenaar bp = alloca(bufsiz); 138231337658SMarcel Moolenaar va_end(va_local); 138331337658SMarcel Moolenaar va_copy(va_local, vap); 1384c600d307SMarcel Moolenaar rc = vsnprintf(bp, bufsiz, fmt, va_local); 138531337658SMarcel Moolenaar } 1386c600d307SMarcel Moolenaar va_end(va_local); 138731337658SMarcel Moolenaar cp = bp + rc; 138831337658SMarcel Moolenaar 138931337658SMarcel Moolenaar if (need_nl) { 139031337658SMarcel Moolenaar rc2 = snprintf(cp, bufsiz - rc, "%s%s\n", 139131337658SMarcel Moolenaar (code > 0) ? ": " : "", 139231337658SMarcel Moolenaar (code > 0) ? strerror(code) : ""); 139331337658SMarcel Moolenaar if (rc2 > 0) 139431337658SMarcel Moolenaar rc += rc2; 139531337658SMarcel Moolenaar } 139631337658SMarcel Moolenaar 139731337658SMarcel Moolenaar xo_buf_append_div(xop, "message", 0, NULL, 0, bp, rc, NULL, 0); 139831337658SMarcel Moolenaar } 139931337658SMarcel Moolenaar break; 140031337658SMarcel Moolenaar 140131337658SMarcel Moolenaar case XO_STYLE_JSON: 140231337658SMarcel Moolenaar /* No meanings of representing messages in JSON */ 140331337658SMarcel Moolenaar break; 140431337658SMarcel Moolenaar 140531337658SMarcel Moolenaar case XO_STYLE_TEXT: 140631337658SMarcel Moolenaar rc = xo_printf_v(xop, fmt, vap); 140731337658SMarcel Moolenaar /* 140831337658SMarcel Moolenaar * XXX need to handle UTF-8 widths 140931337658SMarcel Moolenaar */ 141031337658SMarcel Moolenaar if (rc > 0) { 141131337658SMarcel Moolenaar if (xop->xo_flags & XOF_COLUMNS) 141231337658SMarcel Moolenaar xop->xo_columns += rc; 141331337658SMarcel Moolenaar if (xop->xo_flags & XOF_ANCHOR) 141431337658SMarcel Moolenaar xop->xo_anchor_columns += rc; 141531337658SMarcel Moolenaar } 141631337658SMarcel Moolenaar 141731337658SMarcel Moolenaar if (need_nl && code > 0) { 141831337658SMarcel Moolenaar const char *msg = strerror(code); 141931337658SMarcel Moolenaar if (msg) { 142031337658SMarcel Moolenaar xo_printf(xop, ": %s", msg); 142131337658SMarcel Moolenaar } 142231337658SMarcel Moolenaar } 142331337658SMarcel Moolenaar if (need_nl) 142431337658SMarcel Moolenaar xo_printf(xop, "\n"); 142531337658SMarcel Moolenaar 142631337658SMarcel Moolenaar break; 142731337658SMarcel Moolenaar } 142831337658SMarcel Moolenaar 1429545ddfbeSMarcel Moolenaar (void) xo_flush_h(xop); 143031337658SMarcel Moolenaar } 143131337658SMarcel Moolenaar 143231337658SMarcel Moolenaar void 143331337658SMarcel Moolenaar xo_message_hc (xo_handle_t *xop, int code, const char *fmt, ...) 143431337658SMarcel Moolenaar { 143531337658SMarcel Moolenaar va_list vap; 143631337658SMarcel Moolenaar 143731337658SMarcel Moolenaar va_start(vap, fmt); 143831337658SMarcel Moolenaar xo_message_hcv(xop, code, fmt, vap); 143931337658SMarcel Moolenaar va_end(vap); 144031337658SMarcel Moolenaar } 144131337658SMarcel Moolenaar 144231337658SMarcel Moolenaar void 144331337658SMarcel Moolenaar xo_message_c (int code, const char *fmt, ...) 144431337658SMarcel Moolenaar { 144531337658SMarcel Moolenaar va_list vap; 144631337658SMarcel Moolenaar 144731337658SMarcel Moolenaar va_start(vap, fmt); 144831337658SMarcel Moolenaar xo_message_hcv(NULL, code, fmt, vap); 144931337658SMarcel Moolenaar va_end(vap); 145031337658SMarcel Moolenaar } 145131337658SMarcel Moolenaar 145231337658SMarcel Moolenaar void 145331337658SMarcel Moolenaar xo_message (const char *fmt, ...) 145431337658SMarcel Moolenaar { 145531337658SMarcel Moolenaar int code = errno; 145631337658SMarcel Moolenaar va_list vap; 145731337658SMarcel Moolenaar 145831337658SMarcel Moolenaar va_start(vap, fmt); 145931337658SMarcel Moolenaar xo_message_hcv(NULL, code, fmt, vap); 146031337658SMarcel Moolenaar va_end(vap); 146131337658SMarcel Moolenaar } 146231337658SMarcel Moolenaar 146331337658SMarcel Moolenaar static void 146431337658SMarcel Moolenaar xo_failure (xo_handle_t *xop, const char *fmt, ...) 146531337658SMarcel Moolenaar { 146631337658SMarcel Moolenaar if (!(xop->xo_flags & XOF_WARN)) 146731337658SMarcel Moolenaar return; 146831337658SMarcel Moolenaar 146931337658SMarcel Moolenaar va_list vap; 147031337658SMarcel Moolenaar 147131337658SMarcel Moolenaar va_start(vap, fmt); 147231337658SMarcel Moolenaar xo_warn_hcv(xop, -1, 1, fmt, vap); 147331337658SMarcel Moolenaar va_end(vap); 147431337658SMarcel Moolenaar } 147531337658SMarcel Moolenaar 147631337658SMarcel Moolenaar /** 147731337658SMarcel Moolenaar * Create a handle for use by later libxo functions. 147831337658SMarcel Moolenaar * 147931337658SMarcel Moolenaar * Note: normal use of libxo does not require a distinct handle, since 148031337658SMarcel Moolenaar * the default handle (used when NULL is passed) generates text on stdout. 148131337658SMarcel Moolenaar * 148231337658SMarcel Moolenaar * @style Style of output desired (XO_STYLE_* value) 148331337658SMarcel Moolenaar * @flags Set of XOF_* flags in use with this handle 148431337658SMarcel Moolenaar */ 148531337658SMarcel Moolenaar xo_handle_t * 148631337658SMarcel Moolenaar xo_create (xo_style_t style, xo_xof_flags_t flags) 148731337658SMarcel Moolenaar { 148831337658SMarcel Moolenaar xo_handle_t *xop = xo_realloc(NULL, sizeof(*xop)); 148931337658SMarcel Moolenaar 149031337658SMarcel Moolenaar if (xop) { 149131337658SMarcel Moolenaar bzero(xop, sizeof(*xop)); 149231337658SMarcel Moolenaar 149331337658SMarcel Moolenaar xop->xo_style = style; 149431337658SMarcel Moolenaar xop->xo_flags = flags; 149531337658SMarcel Moolenaar xo_init_handle(xop); 149631337658SMarcel Moolenaar } 149731337658SMarcel Moolenaar 149831337658SMarcel Moolenaar return xop; 149931337658SMarcel Moolenaar } 150031337658SMarcel Moolenaar 150131337658SMarcel Moolenaar /** 150231337658SMarcel Moolenaar * Create a handle that will write to the given file. Use 150331337658SMarcel Moolenaar * the XOF_CLOSE_FP flag to have the file closed on xo_destroy(). 150431337658SMarcel Moolenaar * @fp FILE pointer to use 150531337658SMarcel Moolenaar * @style Style of output desired (XO_STYLE_* value) 150631337658SMarcel Moolenaar * @flags Set of XOF_* flags to use with this handle 150731337658SMarcel Moolenaar */ 150831337658SMarcel Moolenaar xo_handle_t * 150931337658SMarcel Moolenaar xo_create_to_file (FILE *fp, xo_style_t style, xo_xof_flags_t flags) 151031337658SMarcel Moolenaar { 151131337658SMarcel Moolenaar xo_handle_t *xop = xo_create(style, flags); 151231337658SMarcel Moolenaar 151331337658SMarcel Moolenaar if (xop) { 151431337658SMarcel Moolenaar xop->xo_opaque = fp; 151531337658SMarcel Moolenaar xop->xo_write = xo_write_to_file; 151631337658SMarcel Moolenaar xop->xo_close = xo_close_file; 1517545ddfbeSMarcel Moolenaar xop->xo_flush = xo_flush_file; 151831337658SMarcel Moolenaar } 151931337658SMarcel Moolenaar 152031337658SMarcel Moolenaar return xop; 152131337658SMarcel Moolenaar } 152231337658SMarcel Moolenaar 152331337658SMarcel Moolenaar /** 152431337658SMarcel Moolenaar * Release any resources held by the handle. 152531337658SMarcel Moolenaar * @xop XO handle to alter (or NULL for default handle) 152631337658SMarcel Moolenaar */ 152731337658SMarcel Moolenaar void 1528c600d307SMarcel Moolenaar xo_destroy (xo_handle_t *xop_arg) 152931337658SMarcel Moolenaar { 1530c600d307SMarcel Moolenaar xo_handle_t *xop = xo_default(xop_arg); 153131337658SMarcel Moolenaar 153231337658SMarcel Moolenaar if (xop->xo_close && (xop->xo_flags & XOF_CLOSE_FP)) 153331337658SMarcel Moolenaar xop->xo_close(xop->xo_opaque); 153431337658SMarcel Moolenaar 153531337658SMarcel Moolenaar xo_free(xop->xo_stack); 153631337658SMarcel Moolenaar xo_buf_cleanup(&xop->xo_data); 153731337658SMarcel Moolenaar xo_buf_cleanup(&xop->xo_fmt); 153831337658SMarcel Moolenaar xo_buf_cleanup(&xop->xo_predicate); 153931337658SMarcel Moolenaar xo_buf_cleanup(&xop->xo_attrs); 1540788ca347SMarcel Moolenaar xo_buf_cleanup(&xop->xo_color_buf); 1541788ca347SMarcel Moolenaar 1542788ca347SMarcel Moolenaar if (xop->xo_version) 1543788ca347SMarcel Moolenaar xo_free(xop->xo_version); 154431337658SMarcel Moolenaar 1545c600d307SMarcel Moolenaar if (xop_arg == NULL) { 1546545ddfbeSMarcel Moolenaar bzero(&xo_default_handle, sizeof(xo_default_handle)); 154731337658SMarcel Moolenaar xo_default_inited = 0; 154831337658SMarcel Moolenaar } else 154931337658SMarcel Moolenaar xo_free(xop); 155031337658SMarcel Moolenaar } 155131337658SMarcel Moolenaar 155231337658SMarcel Moolenaar /** 155331337658SMarcel Moolenaar * Record a new output style to use for the given handle (or default if 155431337658SMarcel Moolenaar * handle is NULL). This output style will be used for any future output. 155531337658SMarcel Moolenaar * 155631337658SMarcel Moolenaar * @xop XO handle to alter (or NULL for default handle) 155731337658SMarcel Moolenaar * @style new output style (XO_STYLE_*) 155831337658SMarcel Moolenaar */ 155931337658SMarcel Moolenaar void 156031337658SMarcel Moolenaar xo_set_style (xo_handle_t *xop, xo_style_t style) 156131337658SMarcel Moolenaar { 156231337658SMarcel Moolenaar xop = xo_default(xop); 156331337658SMarcel Moolenaar xop->xo_style = style; 156431337658SMarcel Moolenaar } 156531337658SMarcel Moolenaar 156631337658SMarcel Moolenaar xo_style_t 156731337658SMarcel Moolenaar xo_get_style (xo_handle_t *xop) 156831337658SMarcel Moolenaar { 156931337658SMarcel Moolenaar xop = xo_default(xop); 1570788ca347SMarcel Moolenaar return xo_style(xop); 157131337658SMarcel Moolenaar } 157231337658SMarcel Moolenaar 157331337658SMarcel Moolenaar static int 157431337658SMarcel Moolenaar xo_name_to_style (const char *name) 157531337658SMarcel Moolenaar { 157631337658SMarcel Moolenaar if (strcmp(name, "xml") == 0) 157731337658SMarcel Moolenaar return XO_STYLE_XML; 157831337658SMarcel Moolenaar else if (strcmp(name, "json") == 0) 157931337658SMarcel Moolenaar return XO_STYLE_JSON; 158031337658SMarcel Moolenaar else if (strcmp(name, "text") == 0) 158131337658SMarcel Moolenaar return XO_STYLE_TEXT; 158231337658SMarcel Moolenaar else if (strcmp(name, "html") == 0) 158331337658SMarcel Moolenaar return XO_STYLE_HTML; 158431337658SMarcel Moolenaar 158531337658SMarcel Moolenaar return -1; 158631337658SMarcel Moolenaar } 158731337658SMarcel Moolenaar 158831337658SMarcel Moolenaar /* 158931337658SMarcel Moolenaar * Convert string name to XOF_* flag value. 159031337658SMarcel Moolenaar * Not all are useful. Or safe. Or sane. 159131337658SMarcel Moolenaar */ 159231337658SMarcel Moolenaar static unsigned 159331337658SMarcel Moolenaar xo_name_to_flag (const char *name) 159431337658SMarcel Moolenaar { 159531337658SMarcel Moolenaar if (strcmp(name, "pretty") == 0) 159631337658SMarcel Moolenaar return XOF_PRETTY; 159731337658SMarcel Moolenaar if (strcmp(name, "warn") == 0) 159831337658SMarcel Moolenaar return XOF_WARN; 159931337658SMarcel Moolenaar if (strcmp(name, "xpath") == 0) 160031337658SMarcel Moolenaar return XOF_XPATH; 160131337658SMarcel Moolenaar if (strcmp(name, "info") == 0) 160231337658SMarcel Moolenaar return XOF_INFO; 160331337658SMarcel Moolenaar if (strcmp(name, "warn-xml") == 0) 160431337658SMarcel Moolenaar return XOF_WARN_XML; 1605788ca347SMarcel Moolenaar if (strcmp(name, "color") == 0) 1606788ca347SMarcel Moolenaar return XOF_COLOR_ALLOWED; 160731337658SMarcel Moolenaar if (strcmp(name, "columns") == 0) 160831337658SMarcel Moolenaar return XOF_COLUMNS; 160931337658SMarcel Moolenaar if (strcmp(name, "dtrt") == 0) 161031337658SMarcel Moolenaar return XOF_DTRT; 161131337658SMarcel Moolenaar if (strcmp(name, "flush") == 0) 161231337658SMarcel Moolenaar return XOF_FLUSH; 161331337658SMarcel Moolenaar if (strcmp(name, "keys") == 0) 161431337658SMarcel Moolenaar return XOF_KEYS; 161531337658SMarcel Moolenaar if (strcmp(name, "ignore-close") == 0) 161631337658SMarcel Moolenaar return XOF_IGNORE_CLOSE; 161731337658SMarcel Moolenaar if (strcmp(name, "not-first") == 0) 161831337658SMarcel Moolenaar return XOF_NOT_FIRST; 161931337658SMarcel Moolenaar if (strcmp(name, "no-locale") == 0) 162031337658SMarcel Moolenaar return XOF_NO_LOCALE; 162131337658SMarcel Moolenaar if (strcmp(name, "no-top") == 0) 162231337658SMarcel Moolenaar return XOF_NO_TOP; 162331337658SMarcel Moolenaar if (strcmp(name, "units") == 0) 162431337658SMarcel Moolenaar return XOF_UNITS; 162531337658SMarcel Moolenaar if (strcmp(name, "underscores") == 0) 162631337658SMarcel Moolenaar return XOF_UNDERSCORES; 162731337658SMarcel Moolenaar 162831337658SMarcel Moolenaar return 0; 162931337658SMarcel Moolenaar } 163031337658SMarcel Moolenaar 163131337658SMarcel Moolenaar int 163231337658SMarcel Moolenaar xo_set_style_name (xo_handle_t *xop, const char *name) 163331337658SMarcel Moolenaar { 163431337658SMarcel Moolenaar if (name == NULL) 163531337658SMarcel Moolenaar return -1; 163631337658SMarcel Moolenaar 163731337658SMarcel Moolenaar int style = xo_name_to_style(name); 163831337658SMarcel Moolenaar if (style < 0) 163931337658SMarcel Moolenaar return -1; 164031337658SMarcel Moolenaar 164131337658SMarcel Moolenaar xo_set_style(xop, style); 164231337658SMarcel Moolenaar return 0; 164331337658SMarcel Moolenaar } 164431337658SMarcel Moolenaar 164531337658SMarcel Moolenaar /* 164631337658SMarcel Moolenaar * Set the options for a handle using a string of options 164731337658SMarcel Moolenaar * passed in. The input is a comma-separated set of names 164831337658SMarcel Moolenaar * and optional values: "xml,pretty,indent=4" 164931337658SMarcel Moolenaar */ 165031337658SMarcel Moolenaar int 165131337658SMarcel Moolenaar xo_set_options (xo_handle_t *xop, const char *input) 165231337658SMarcel Moolenaar { 165331337658SMarcel Moolenaar char *cp, *ep, *vp, *np, *bp; 165431337658SMarcel Moolenaar int style = -1, new_style, len, rc = 0; 165531337658SMarcel Moolenaar xo_xof_flags_t new_flag; 165631337658SMarcel Moolenaar 165731337658SMarcel Moolenaar if (input == NULL) 165831337658SMarcel Moolenaar return 0; 165931337658SMarcel Moolenaar 166031337658SMarcel Moolenaar xop = xo_default(xop); 166131337658SMarcel Moolenaar 1662788ca347SMarcel Moolenaar #ifdef LIBXO_COLOR_ON_BY_DEFAULT 1663788ca347SMarcel Moolenaar /* If the installer used --enable-color-on-by-default, then we allow it */ 1664788ca347SMarcel Moolenaar xop->xo_flags |= XOF_COLOR_ALLOWED; 1665788ca347SMarcel Moolenaar #endif /* LIBXO_COLOR_ON_BY_DEFAULT */ 1666788ca347SMarcel Moolenaar 166731337658SMarcel Moolenaar /* 166831337658SMarcel Moolenaar * We support a simpler, old-school style of giving option 166931337658SMarcel Moolenaar * also, using a single character for each option. It's 167031337658SMarcel Moolenaar * ideal for lazy people, such as myself. 167131337658SMarcel Moolenaar */ 167231337658SMarcel Moolenaar if (*input == ':') { 167331337658SMarcel Moolenaar int sz; 167431337658SMarcel Moolenaar 167531337658SMarcel Moolenaar for (input++ ; *input; input++) { 167631337658SMarcel Moolenaar switch (*input) { 1677788ca347SMarcel Moolenaar case 'c': 1678788ca347SMarcel Moolenaar xop->xo_flags |= XOF_COLOR_ALLOWED; 1679788ca347SMarcel Moolenaar break; 1680788ca347SMarcel Moolenaar 168131337658SMarcel Moolenaar case 'f': 168231337658SMarcel Moolenaar xop->xo_flags |= XOF_FLUSH; 168331337658SMarcel Moolenaar break; 168431337658SMarcel Moolenaar 1685545ddfbeSMarcel Moolenaar case 'F': 1686545ddfbeSMarcel Moolenaar xop->xo_flags |= XOF_FLUSH_LINE; 1687545ddfbeSMarcel Moolenaar break; 1688545ddfbeSMarcel Moolenaar 168931337658SMarcel Moolenaar case 'H': 169031337658SMarcel Moolenaar xop->xo_style = XO_STYLE_HTML; 169131337658SMarcel Moolenaar break; 169231337658SMarcel Moolenaar 169331337658SMarcel Moolenaar case 'I': 169431337658SMarcel Moolenaar xop->xo_flags |= XOF_INFO; 169531337658SMarcel Moolenaar break; 169631337658SMarcel Moolenaar 169731337658SMarcel Moolenaar case 'i': 169831337658SMarcel Moolenaar sz = strspn(input + 1, "0123456789"); 169931337658SMarcel Moolenaar if (sz > 0) { 170031337658SMarcel Moolenaar xop->xo_indent_by = atoi(input + 1); 170131337658SMarcel Moolenaar input += sz - 1; /* Skip value */ 170231337658SMarcel Moolenaar } 170331337658SMarcel Moolenaar break; 170431337658SMarcel Moolenaar 170531337658SMarcel Moolenaar case 'k': 170631337658SMarcel Moolenaar xop->xo_flags |= XOF_KEYS; 170731337658SMarcel Moolenaar break; 170831337658SMarcel Moolenaar 170931337658SMarcel Moolenaar case 'J': 171031337658SMarcel Moolenaar xop->xo_style = XO_STYLE_JSON; 171131337658SMarcel Moolenaar break; 171231337658SMarcel Moolenaar 171331337658SMarcel Moolenaar case 'P': 171431337658SMarcel Moolenaar xop->xo_flags |= XOF_PRETTY; 171531337658SMarcel Moolenaar break; 171631337658SMarcel Moolenaar 171731337658SMarcel Moolenaar case 'T': 171831337658SMarcel Moolenaar xop->xo_style = XO_STYLE_TEXT; 171931337658SMarcel Moolenaar break; 172031337658SMarcel Moolenaar 172131337658SMarcel Moolenaar case 'U': 172231337658SMarcel Moolenaar xop->xo_flags |= XOF_UNITS; 172331337658SMarcel Moolenaar break; 172431337658SMarcel Moolenaar 172531337658SMarcel Moolenaar case 'u': 172631337658SMarcel Moolenaar xop->xo_flags |= XOF_UNDERSCORES; 172731337658SMarcel Moolenaar break; 172831337658SMarcel Moolenaar 172931337658SMarcel Moolenaar case 'W': 173031337658SMarcel Moolenaar xop->xo_flags |= XOF_WARN; 173131337658SMarcel Moolenaar break; 173231337658SMarcel Moolenaar 173331337658SMarcel Moolenaar case 'X': 173431337658SMarcel Moolenaar xop->xo_style = XO_STYLE_XML; 173531337658SMarcel Moolenaar break; 173631337658SMarcel Moolenaar 173731337658SMarcel Moolenaar case 'x': 173831337658SMarcel Moolenaar xop->xo_flags |= XOF_XPATH; 173931337658SMarcel Moolenaar break; 174031337658SMarcel Moolenaar } 174131337658SMarcel Moolenaar } 174231337658SMarcel Moolenaar return 0; 174331337658SMarcel Moolenaar } 174431337658SMarcel Moolenaar 174531337658SMarcel Moolenaar len = strlen(input) + 1; 174631337658SMarcel Moolenaar bp = alloca(len); 174731337658SMarcel Moolenaar memcpy(bp, input, len); 174831337658SMarcel Moolenaar 174931337658SMarcel Moolenaar for (cp = bp, ep = cp + len - 1; cp && cp < ep; cp = np) { 175031337658SMarcel Moolenaar np = strchr(cp, ','); 175131337658SMarcel Moolenaar if (np) 175231337658SMarcel Moolenaar *np++ = '\0'; 175331337658SMarcel Moolenaar 175431337658SMarcel Moolenaar vp = strchr(cp, '='); 175531337658SMarcel Moolenaar if (vp) 175631337658SMarcel Moolenaar *vp++ = '\0'; 175731337658SMarcel Moolenaar 1758788ca347SMarcel Moolenaar if (strcmp("colors", cp) == 0) { 1759788ca347SMarcel Moolenaar /* XXX Look for colors=red-blue+green-yellow */ 1760788ca347SMarcel Moolenaar continue; 1761788ca347SMarcel Moolenaar } 1762788ca347SMarcel Moolenaar 176331337658SMarcel Moolenaar new_style = xo_name_to_style(cp); 176431337658SMarcel Moolenaar if (new_style >= 0) { 176531337658SMarcel Moolenaar if (style >= 0) 176631337658SMarcel Moolenaar xo_warnx("ignoring multiple styles: '%s'", cp); 176731337658SMarcel Moolenaar else 176831337658SMarcel Moolenaar style = new_style; 176931337658SMarcel Moolenaar } else { 177031337658SMarcel Moolenaar new_flag = xo_name_to_flag(cp); 177131337658SMarcel Moolenaar if (new_flag != 0) 177231337658SMarcel Moolenaar xop->xo_flags |= new_flag; 177331337658SMarcel Moolenaar else { 1774788ca347SMarcel Moolenaar if (strcmp(cp, "no-color") == 0) { 1775788ca347SMarcel Moolenaar xop->xo_flags &= ~XOF_COLOR_ALLOWED; 1776788ca347SMarcel Moolenaar } else if (strcmp(cp, "indent") == 0) { 177731337658SMarcel Moolenaar xop->xo_indent_by = atoi(vp); 177831337658SMarcel Moolenaar } else { 177931337658SMarcel Moolenaar xo_warnx("unknown option: '%s'", cp); 178031337658SMarcel Moolenaar rc = -1; 178131337658SMarcel Moolenaar } 178231337658SMarcel Moolenaar } 178331337658SMarcel Moolenaar } 178431337658SMarcel Moolenaar } 178531337658SMarcel Moolenaar 178631337658SMarcel Moolenaar if (style > 0) 178731337658SMarcel Moolenaar xop->xo_style= style; 178831337658SMarcel Moolenaar 178931337658SMarcel Moolenaar return rc; 179031337658SMarcel Moolenaar } 179131337658SMarcel Moolenaar 179231337658SMarcel Moolenaar /** 179331337658SMarcel Moolenaar * Set one or more flags for a given handle (or default if handle is NULL). 179431337658SMarcel Moolenaar * These flags will affect future output. 179531337658SMarcel Moolenaar * 179631337658SMarcel Moolenaar * @xop XO handle to alter (or NULL for default handle) 179731337658SMarcel Moolenaar * @flags Flags to be set (XOF_*) 179831337658SMarcel Moolenaar */ 179931337658SMarcel Moolenaar void 180031337658SMarcel Moolenaar xo_set_flags (xo_handle_t *xop, xo_xof_flags_t flags) 180131337658SMarcel Moolenaar { 180231337658SMarcel Moolenaar xop = xo_default(xop); 180331337658SMarcel Moolenaar 180431337658SMarcel Moolenaar xop->xo_flags |= flags; 180531337658SMarcel Moolenaar } 180631337658SMarcel Moolenaar 180731337658SMarcel Moolenaar xo_xof_flags_t 180831337658SMarcel Moolenaar xo_get_flags (xo_handle_t *xop) 180931337658SMarcel Moolenaar { 181031337658SMarcel Moolenaar xop = xo_default(xop); 181131337658SMarcel Moolenaar 181231337658SMarcel Moolenaar return xop->xo_flags; 181331337658SMarcel Moolenaar } 181431337658SMarcel Moolenaar 181531337658SMarcel Moolenaar /** 181631337658SMarcel Moolenaar * Record a leading prefix for the XPath we generate. This allows the 181731337658SMarcel Moolenaar * generated data to be placed within an XML hierarchy but still have 181831337658SMarcel Moolenaar * accurate XPath expressions. 181931337658SMarcel Moolenaar * 182031337658SMarcel Moolenaar * @xop XO handle to alter (or NULL for default handle) 182131337658SMarcel Moolenaar * @path The XPath expression 182231337658SMarcel Moolenaar */ 182331337658SMarcel Moolenaar void 182431337658SMarcel Moolenaar xo_set_leading_xpath (xo_handle_t *xop, const char *path) 182531337658SMarcel Moolenaar { 182631337658SMarcel Moolenaar xop = xo_default(xop); 182731337658SMarcel Moolenaar 182831337658SMarcel Moolenaar if (xop->xo_leading_xpath) { 182931337658SMarcel Moolenaar xo_free(xop->xo_leading_xpath); 183031337658SMarcel Moolenaar xop->xo_leading_xpath = NULL; 183131337658SMarcel Moolenaar } 183231337658SMarcel Moolenaar 183331337658SMarcel Moolenaar if (path == NULL) 183431337658SMarcel Moolenaar return; 183531337658SMarcel Moolenaar 183631337658SMarcel Moolenaar int len = strlen(path); 183731337658SMarcel Moolenaar xop->xo_leading_xpath = xo_realloc(NULL, len + 1); 183831337658SMarcel Moolenaar if (xop->xo_leading_xpath) { 183931337658SMarcel Moolenaar memcpy(xop->xo_leading_xpath, path, len + 1); 184031337658SMarcel Moolenaar } 184131337658SMarcel Moolenaar } 184231337658SMarcel Moolenaar 184331337658SMarcel Moolenaar /** 184431337658SMarcel Moolenaar * Record the info data for a set of tags 184531337658SMarcel Moolenaar * 184631337658SMarcel Moolenaar * @xop XO handle to alter (or NULL for default handle) 184731337658SMarcel Moolenaar * @info Info data (xo_info_t) to be recorded (or NULL) (MUST BE SORTED) 184831337658SMarcel Moolenaar * @count Number of entries in info (or -1 to count them ourselves) 184931337658SMarcel Moolenaar */ 185031337658SMarcel Moolenaar void 185131337658SMarcel Moolenaar xo_set_info (xo_handle_t *xop, xo_info_t *infop, int count) 185231337658SMarcel Moolenaar { 185331337658SMarcel Moolenaar xop = xo_default(xop); 185431337658SMarcel Moolenaar 185531337658SMarcel Moolenaar if (count < 0 && infop) { 185631337658SMarcel Moolenaar xo_info_t *xip; 185731337658SMarcel Moolenaar 185831337658SMarcel Moolenaar for (xip = infop, count = 0; xip->xi_name; xip++, count++) 185931337658SMarcel Moolenaar continue; 186031337658SMarcel Moolenaar } 186131337658SMarcel Moolenaar 186231337658SMarcel Moolenaar xop->xo_info = infop; 186331337658SMarcel Moolenaar xop->xo_info_count = count; 186431337658SMarcel Moolenaar } 186531337658SMarcel Moolenaar 186631337658SMarcel Moolenaar /** 186731337658SMarcel Moolenaar * Set the formatter callback for a handle. The callback should 186831337658SMarcel Moolenaar * return a newly formatting contents of a formatting instruction, 186931337658SMarcel Moolenaar * meaning the bits inside the braces. 187031337658SMarcel Moolenaar */ 187131337658SMarcel Moolenaar void 187231337658SMarcel Moolenaar xo_set_formatter (xo_handle_t *xop, xo_formatter_t func, 187331337658SMarcel Moolenaar xo_checkpointer_t cfunc) 187431337658SMarcel Moolenaar { 187531337658SMarcel Moolenaar xop = xo_default(xop); 187631337658SMarcel Moolenaar 187731337658SMarcel Moolenaar xop->xo_formatter = func; 187831337658SMarcel Moolenaar xop->xo_checkpointer = cfunc; 187931337658SMarcel Moolenaar } 188031337658SMarcel Moolenaar 188131337658SMarcel Moolenaar /** 188231337658SMarcel Moolenaar * Clear one or more flags for a given handle (or default if handle is NULL). 188331337658SMarcel Moolenaar * These flags will affect future output. 188431337658SMarcel Moolenaar * 188531337658SMarcel Moolenaar * @xop XO handle to alter (or NULL for default handle) 188631337658SMarcel Moolenaar * @flags Flags to be cleared (XOF_*) 188731337658SMarcel Moolenaar */ 188831337658SMarcel Moolenaar void 188931337658SMarcel Moolenaar xo_clear_flags (xo_handle_t *xop, xo_xof_flags_t flags) 189031337658SMarcel Moolenaar { 189131337658SMarcel Moolenaar xop = xo_default(xop); 189231337658SMarcel Moolenaar 189331337658SMarcel Moolenaar xop->xo_flags &= ~flags; 189431337658SMarcel Moolenaar } 189531337658SMarcel Moolenaar 1896545ddfbeSMarcel Moolenaar static const char * 1897545ddfbeSMarcel Moolenaar xo_state_name (xo_state_t state) 1898545ddfbeSMarcel Moolenaar { 1899545ddfbeSMarcel Moolenaar static const char *names[] = { 1900545ddfbeSMarcel Moolenaar "init", 1901545ddfbeSMarcel Moolenaar "open_container", 1902545ddfbeSMarcel Moolenaar "close_container", 1903545ddfbeSMarcel Moolenaar "open_list", 1904545ddfbeSMarcel Moolenaar "close_list", 1905545ddfbeSMarcel Moolenaar "open_instance", 1906545ddfbeSMarcel Moolenaar "close_instance", 1907545ddfbeSMarcel Moolenaar "open_leaf_list", 1908545ddfbeSMarcel Moolenaar "close_leaf_list", 1909545ddfbeSMarcel Moolenaar "discarding", 1910545ddfbeSMarcel Moolenaar "marker", 1911545ddfbeSMarcel Moolenaar "emit", 1912545ddfbeSMarcel Moolenaar "emit_leaf_list", 1913545ddfbeSMarcel Moolenaar "finish", 1914545ddfbeSMarcel Moolenaar NULL 1915545ddfbeSMarcel Moolenaar }; 1916545ddfbeSMarcel Moolenaar 1917545ddfbeSMarcel Moolenaar if (state < (sizeof(names) / sizeof(names[0]))) 1918545ddfbeSMarcel Moolenaar return names[state]; 1919545ddfbeSMarcel Moolenaar 1920545ddfbeSMarcel Moolenaar return "unknown"; 1921545ddfbeSMarcel Moolenaar } 1922545ddfbeSMarcel Moolenaar 192331337658SMarcel Moolenaar static void 192431337658SMarcel Moolenaar xo_line_ensure_open (xo_handle_t *xop, xo_xff_flags_t flags UNUSED) 192531337658SMarcel Moolenaar { 192631337658SMarcel Moolenaar static char div_open[] = "<div class=\"line\">"; 192731337658SMarcel Moolenaar static char div_open_blank[] = "<div class=\"blank-line\">"; 192831337658SMarcel Moolenaar 192931337658SMarcel Moolenaar if (xop->xo_flags & XOF_DIV_OPEN) 193031337658SMarcel Moolenaar return; 193131337658SMarcel Moolenaar 1932788ca347SMarcel Moolenaar if (xo_style(xop) != XO_STYLE_HTML) 193331337658SMarcel Moolenaar return; 193431337658SMarcel Moolenaar 193531337658SMarcel Moolenaar xop->xo_flags |= XOF_DIV_OPEN; 193631337658SMarcel Moolenaar if (flags & XFF_BLANK_LINE) 193731337658SMarcel Moolenaar xo_data_append(xop, div_open_blank, sizeof(div_open_blank) - 1); 193831337658SMarcel Moolenaar else 193931337658SMarcel Moolenaar xo_data_append(xop, div_open, sizeof(div_open) - 1); 194031337658SMarcel Moolenaar 194131337658SMarcel Moolenaar if (xop->xo_flags & XOF_PRETTY) 194231337658SMarcel Moolenaar xo_data_append(xop, "\n", 1); 194331337658SMarcel Moolenaar } 194431337658SMarcel Moolenaar 194531337658SMarcel Moolenaar static void 194631337658SMarcel Moolenaar xo_line_close (xo_handle_t *xop) 194731337658SMarcel Moolenaar { 194831337658SMarcel Moolenaar static char div_close[] = "</div>"; 194931337658SMarcel Moolenaar 1950788ca347SMarcel Moolenaar switch (xo_style(xop)) { 195131337658SMarcel Moolenaar case XO_STYLE_HTML: 195231337658SMarcel Moolenaar if (!(xop->xo_flags & XOF_DIV_OPEN)) 195331337658SMarcel Moolenaar xo_line_ensure_open(xop, 0); 195431337658SMarcel Moolenaar 195531337658SMarcel Moolenaar xop->xo_flags &= ~XOF_DIV_OPEN; 195631337658SMarcel Moolenaar xo_data_append(xop, div_close, sizeof(div_close) - 1); 195731337658SMarcel Moolenaar 195831337658SMarcel Moolenaar if (xop->xo_flags & XOF_PRETTY) 195931337658SMarcel Moolenaar xo_data_append(xop, "\n", 1); 196031337658SMarcel Moolenaar break; 196131337658SMarcel Moolenaar 196231337658SMarcel Moolenaar case XO_STYLE_TEXT: 196331337658SMarcel Moolenaar xo_data_append(xop, "\n", 1); 196431337658SMarcel Moolenaar break; 196531337658SMarcel Moolenaar } 196631337658SMarcel Moolenaar } 196731337658SMarcel Moolenaar 196831337658SMarcel Moolenaar static int 196931337658SMarcel Moolenaar xo_info_compare (const void *key, const void *data) 197031337658SMarcel Moolenaar { 197131337658SMarcel Moolenaar const char *name = key; 197231337658SMarcel Moolenaar const xo_info_t *xip = data; 197331337658SMarcel Moolenaar 197431337658SMarcel Moolenaar return strcmp(name, xip->xi_name); 197531337658SMarcel Moolenaar } 197631337658SMarcel Moolenaar 197731337658SMarcel Moolenaar 197831337658SMarcel Moolenaar static xo_info_t * 197931337658SMarcel Moolenaar xo_info_find (xo_handle_t *xop, const char *name, int nlen) 198031337658SMarcel Moolenaar { 198131337658SMarcel Moolenaar xo_info_t *xip; 198231337658SMarcel Moolenaar char *cp = alloca(nlen + 1); /* Need local copy for NUL termination */ 198331337658SMarcel Moolenaar 198431337658SMarcel Moolenaar memcpy(cp, name, nlen); 198531337658SMarcel Moolenaar cp[nlen] = '\0'; 198631337658SMarcel Moolenaar 198731337658SMarcel Moolenaar xip = bsearch(cp, xop->xo_info, xop->xo_info_count, 198831337658SMarcel Moolenaar sizeof(xop->xo_info[0]), xo_info_compare); 198931337658SMarcel Moolenaar return xip; 199031337658SMarcel Moolenaar } 199131337658SMarcel Moolenaar 199231337658SMarcel Moolenaar #define CONVERT(_have, _need) (((_have) << 8) | (_need)) 199331337658SMarcel Moolenaar 199431337658SMarcel Moolenaar /* 199531337658SMarcel Moolenaar * Check to see that the conversion is safe and sane. 199631337658SMarcel Moolenaar */ 199731337658SMarcel Moolenaar static int 199831337658SMarcel Moolenaar xo_check_conversion (xo_handle_t *xop, int have_enc, int need_enc) 199931337658SMarcel Moolenaar { 200031337658SMarcel Moolenaar switch (CONVERT(have_enc, need_enc)) { 200131337658SMarcel Moolenaar case CONVERT(XF_ENC_UTF8, XF_ENC_UTF8): 200231337658SMarcel Moolenaar case CONVERT(XF_ENC_UTF8, XF_ENC_LOCALE): 200331337658SMarcel Moolenaar case CONVERT(XF_ENC_WIDE, XF_ENC_UTF8): 200431337658SMarcel Moolenaar case CONVERT(XF_ENC_WIDE, XF_ENC_LOCALE): 200531337658SMarcel Moolenaar case CONVERT(XF_ENC_LOCALE, XF_ENC_LOCALE): 200631337658SMarcel Moolenaar case CONVERT(XF_ENC_LOCALE, XF_ENC_UTF8): 200731337658SMarcel Moolenaar return 0; 200831337658SMarcel Moolenaar 200931337658SMarcel Moolenaar default: 201031337658SMarcel Moolenaar xo_failure(xop, "invalid conversion (%c:%c)", have_enc, need_enc); 201131337658SMarcel Moolenaar return 1; 201231337658SMarcel Moolenaar } 201331337658SMarcel Moolenaar } 201431337658SMarcel Moolenaar 201531337658SMarcel Moolenaar static int 201631337658SMarcel Moolenaar xo_format_string_direct (xo_handle_t *xop, xo_buffer_t *xbp, 201731337658SMarcel Moolenaar xo_xff_flags_t flags, 201831337658SMarcel Moolenaar const wchar_t *wcp, const char *cp, int len, int max, 201931337658SMarcel Moolenaar int need_enc, int have_enc) 202031337658SMarcel Moolenaar { 202131337658SMarcel Moolenaar int cols = 0; 2022c600d307SMarcel Moolenaar wchar_t wc = 0; 202331337658SMarcel Moolenaar int ilen, olen, width; 202431337658SMarcel Moolenaar int attr = (flags & XFF_ATTR); 202531337658SMarcel Moolenaar const char *sp; 202631337658SMarcel Moolenaar 202731337658SMarcel Moolenaar if (len > 0 && !xo_buf_has_room(xbp, len)) 202831337658SMarcel Moolenaar return 0; 202931337658SMarcel Moolenaar 203031337658SMarcel Moolenaar for (;;) { 203131337658SMarcel Moolenaar if (len == 0) 203231337658SMarcel Moolenaar break; 203331337658SMarcel Moolenaar 203431337658SMarcel Moolenaar if (cp) { 203531337658SMarcel Moolenaar if (*cp == '\0') 203631337658SMarcel Moolenaar break; 203731337658SMarcel Moolenaar if ((flags & XFF_UNESCAPE) && (*cp == '\\' || *cp == '%')) { 203831337658SMarcel Moolenaar cp += 1; 203931337658SMarcel Moolenaar len -= 1; 204031337658SMarcel Moolenaar } 204131337658SMarcel Moolenaar } 204231337658SMarcel Moolenaar 204331337658SMarcel Moolenaar if (wcp && *wcp == L'\0') 204431337658SMarcel Moolenaar break; 204531337658SMarcel Moolenaar 204631337658SMarcel Moolenaar ilen = 0; 204731337658SMarcel Moolenaar 204831337658SMarcel Moolenaar switch (have_enc) { 204931337658SMarcel Moolenaar case XF_ENC_WIDE: /* Wide character */ 205031337658SMarcel Moolenaar wc = *wcp++; 205131337658SMarcel Moolenaar ilen = 1; 205231337658SMarcel Moolenaar break; 205331337658SMarcel Moolenaar 205431337658SMarcel Moolenaar case XF_ENC_UTF8: /* UTF-8 */ 205531337658SMarcel Moolenaar ilen = xo_utf8_to_wc_len(cp); 205631337658SMarcel Moolenaar if (ilen < 0) { 205731337658SMarcel Moolenaar xo_failure(xop, "invalid UTF-8 character: %02hhx", *cp); 205831337658SMarcel Moolenaar return -1; 205931337658SMarcel Moolenaar } 206031337658SMarcel Moolenaar 206131337658SMarcel Moolenaar if (len > 0 && len < ilen) { 206231337658SMarcel Moolenaar len = 0; /* Break out of the loop */ 206331337658SMarcel Moolenaar continue; 206431337658SMarcel Moolenaar } 206531337658SMarcel Moolenaar 206631337658SMarcel Moolenaar wc = xo_utf8_char(cp, ilen); 206731337658SMarcel Moolenaar if (wc == (wchar_t) -1) { 206831337658SMarcel Moolenaar xo_failure(xop, "invalid UTF-8 character: %02hhx/%d", 206931337658SMarcel Moolenaar *cp, ilen); 207031337658SMarcel Moolenaar return -1; 207131337658SMarcel Moolenaar } 207231337658SMarcel Moolenaar cp += ilen; 207331337658SMarcel Moolenaar break; 207431337658SMarcel Moolenaar 207531337658SMarcel Moolenaar case XF_ENC_LOCALE: /* Native locale */ 207631337658SMarcel Moolenaar ilen = (len > 0) ? len : MB_LEN_MAX; 207731337658SMarcel Moolenaar ilen = mbrtowc(&wc, cp, ilen, &xop->xo_mbstate); 207831337658SMarcel Moolenaar if (ilen < 0) { /* Invalid data; skip */ 207931337658SMarcel Moolenaar xo_failure(xop, "invalid mbs char: %02hhx", *cp); 2080dbf26257SAlexander Kabaev wc = L'?'; 2081dbf26257SAlexander Kabaev ilen = 1; 208231337658SMarcel Moolenaar } 208331337658SMarcel Moolenaar if (ilen == 0) { /* Hit a wide NUL character */ 208431337658SMarcel Moolenaar len = 0; 208531337658SMarcel Moolenaar continue; 208631337658SMarcel Moolenaar } 208731337658SMarcel Moolenaar 208831337658SMarcel Moolenaar cp += ilen; 208931337658SMarcel Moolenaar break; 209031337658SMarcel Moolenaar } 209131337658SMarcel Moolenaar 209231337658SMarcel Moolenaar /* Reduce len, but not below zero */ 209331337658SMarcel Moolenaar if (len > 0) { 209431337658SMarcel Moolenaar len -= ilen; 209531337658SMarcel Moolenaar if (len < 0) 209631337658SMarcel Moolenaar len = 0; 209731337658SMarcel Moolenaar } 209831337658SMarcel Moolenaar 209931337658SMarcel Moolenaar /* 210031337658SMarcel Moolenaar * Find the width-in-columns of this character, which must be done 210131337658SMarcel Moolenaar * in wide characters, since we lack a mbswidth() function. If 210231337658SMarcel Moolenaar * it doesn't fit 210331337658SMarcel Moolenaar */ 210431337658SMarcel Moolenaar width = wcwidth(wc); 210531337658SMarcel Moolenaar if (width < 0) 210631337658SMarcel Moolenaar width = iswcntrl(wc) ? 0 : 1; 210731337658SMarcel Moolenaar 2108788ca347SMarcel Moolenaar if (xo_style(xop) == XO_STYLE_TEXT || xo_style(xop) == XO_STYLE_HTML) { 210931337658SMarcel Moolenaar if (max > 0 && cols + width > max) 211031337658SMarcel Moolenaar break; 211131337658SMarcel Moolenaar } 211231337658SMarcel Moolenaar 211331337658SMarcel Moolenaar switch (need_enc) { 211431337658SMarcel Moolenaar case XF_ENC_UTF8: 211531337658SMarcel Moolenaar 211631337658SMarcel Moolenaar /* Output in UTF-8 needs to be escaped, based on the style */ 2117788ca347SMarcel Moolenaar switch (xo_style(xop)) { 211831337658SMarcel Moolenaar case XO_STYLE_XML: 211931337658SMarcel Moolenaar case XO_STYLE_HTML: 212031337658SMarcel Moolenaar if (wc == '<') 212131337658SMarcel Moolenaar sp = xo_xml_lt; 212231337658SMarcel Moolenaar else if (wc == '>') 212331337658SMarcel Moolenaar sp = xo_xml_gt; 212431337658SMarcel Moolenaar else if (wc == '&') 212531337658SMarcel Moolenaar sp = xo_xml_amp; 212631337658SMarcel Moolenaar else if (attr && wc == '"') 212731337658SMarcel Moolenaar sp = xo_xml_quot; 212831337658SMarcel Moolenaar else 212931337658SMarcel Moolenaar break; 213031337658SMarcel Moolenaar 213131337658SMarcel Moolenaar int slen = strlen(sp); 213231337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, slen - 1)) 213331337658SMarcel Moolenaar return -1; 213431337658SMarcel Moolenaar 213531337658SMarcel Moolenaar memcpy(xbp->xb_curp, sp, slen); 213631337658SMarcel Moolenaar xbp->xb_curp += slen; 213731337658SMarcel Moolenaar goto done_with_encoding; /* Need multi-level 'break' */ 213831337658SMarcel Moolenaar 213931337658SMarcel Moolenaar case XO_STYLE_JSON: 2140545ddfbeSMarcel Moolenaar if (wc != '\\' && wc != '"' && wc != '\n' && wc != '\r') 214131337658SMarcel Moolenaar break; 214231337658SMarcel Moolenaar 214331337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, 2)) 214431337658SMarcel Moolenaar return -1; 214531337658SMarcel Moolenaar 214631337658SMarcel Moolenaar *xbp->xb_curp++ = '\\'; 2147545ddfbeSMarcel Moolenaar if (wc == '\n') 2148545ddfbeSMarcel Moolenaar wc = 'n'; 2149545ddfbeSMarcel Moolenaar else if (wc == '\r') 2150545ddfbeSMarcel Moolenaar wc = 'r'; 2151545ddfbeSMarcel Moolenaar else wc = wc & 0x7f; 2152545ddfbeSMarcel Moolenaar 2153545ddfbeSMarcel Moolenaar *xbp->xb_curp++ = wc; 215431337658SMarcel Moolenaar goto done_with_encoding; 215531337658SMarcel Moolenaar } 215631337658SMarcel Moolenaar 215731337658SMarcel Moolenaar olen = xo_utf8_emit_len(wc); 215831337658SMarcel Moolenaar if (olen < 0) { 215931337658SMarcel Moolenaar xo_failure(xop, "ignoring bad length"); 216031337658SMarcel Moolenaar continue; 216131337658SMarcel Moolenaar } 216231337658SMarcel Moolenaar 216331337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, olen)) 216431337658SMarcel Moolenaar return -1; 216531337658SMarcel Moolenaar 216631337658SMarcel Moolenaar xo_utf8_emit_char(xbp->xb_curp, olen, wc); 216731337658SMarcel Moolenaar xbp->xb_curp += olen; 216831337658SMarcel Moolenaar break; 216931337658SMarcel Moolenaar 217031337658SMarcel Moolenaar case XF_ENC_LOCALE: 217131337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, MB_LEN_MAX + 1)) 217231337658SMarcel Moolenaar return -1; 217331337658SMarcel Moolenaar 217431337658SMarcel Moolenaar olen = wcrtomb(xbp->xb_curp, wc, &xop->xo_mbstate); 217531337658SMarcel Moolenaar if (olen <= 0) { 217631337658SMarcel Moolenaar xo_failure(xop, "could not convert wide char: %lx", 217731337658SMarcel Moolenaar (unsigned long) wc); 217831337658SMarcel Moolenaar olen = 1; 217931337658SMarcel Moolenaar width = 1; 218031337658SMarcel Moolenaar *xbp->xb_curp++ = '?'; 218131337658SMarcel Moolenaar } else 218231337658SMarcel Moolenaar xbp->xb_curp += olen; 218331337658SMarcel Moolenaar break; 218431337658SMarcel Moolenaar } 218531337658SMarcel Moolenaar 218631337658SMarcel Moolenaar done_with_encoding: 218731337658SMarcel Moolenaar cols += width; 218831337658SMarcel Moolenaar } 218931337658SMarcel Moolenaar 219031337658SMarcel Moolenaar return cols; 219131337658SMarcel Moolenaar } 219231337658SMarcel Moolenaar 219331337658SMarcel Moolenaar static int 219431337658SMarcel Moolenaar xo_format_string (xo_handle_t *xop, xo_buffer_t *xbp, xo_xff_flags_t flags, 219531337658SMarcel Moolenaar xo_format_t *xfp) 219631337658SMarcel Moolenaar { 219731337658SMarcel Moolenaar static char null[] = "(null)"; 2198a0f704ffSMarcel Moolenaar 219931337658SMarcel Moolenaar char *cp = NULL; 220031337658SMarcel Moolenaar wchar_t *wcp = NULL; 220131337658SMarcel Moolenaar int len, cols = 0, rc = 0; 220231337658SMarcel Moolenaar int off = xbp->xb_curp - xbp->xb_bufp, off2; 2203788ca347SMarcel Moolenaar int need_enc = (xo_style(xop) == XO_STYLE_TEXT) 220431337658SMarcel Moolenaar ? XF_ENC_LOCALE : XF_ENC_UTF8; 220531337658SMarcel Moolenaar 220631337658SMarcel Moolenaar if (xo_check_conversion(xop, xfp->xf_enc, need_enc)) 220731337658SMarcel Moolenaar return 0; 220831337658SMarcel Moolenaar 2209a0f704ffSMarcel Moolenaar len = xfp->xf_width[XF_WIDTH_SIZE]; 2210a0f704ffSMarcel Moolenaar 221131337658SMarcel Moolenaar if (xfp->xf_enc == XF_ENC_WIDE) { 221231337658SMarcel Moolenaar wcp = va_arg(xop->xo_vap, wchar_t *); 221331337658SMarcel Moolenaar if (xfp->xf_skip) 221431337658SMarcel Moolenaar return 0; 221531337658SMarcel Moolenaar 2216a0f704ffSMarcel Moolenaar /* 2217a0f704ffSMarcel Moolenaar * Dont' deref NULL; use the traditional "(null)" instead 2218a0f704ffSMarcel Moolenaar * of the more accurate "who's been a naughty boy, then?". 2219a0f704ffSMarcel Moolenaar */ 2220a0f704ffSMarcel Moolenaar if (wcp == NULL) { 2221a0f704ffSMarcel Moolenaar cp = null; 2222a0f704ffSMarcel Moolenaar len = sizeof(null) - 1; 2223a0f704ffSMarcel Moolenaar } 2224a0f704ffSMarcel Moolenaar 222531337658SMarcel Moolenaar } else { 222631337658SMarcel Moolenaar cp = va_arg(xop->xo_vap, char *); /* UTF-8 or native */ 222731337658SMarcel Moolenaar if (xfp->xf_skip) 222831337658SMarcel Moolenaar return 0; 222931337658SMarcel Moolenaar 2230a0f704ffSMarcel Moolenaar /* Echo "Dont' deref NULL" logic */ 2231a0f704ffSMarcel Moolenaar if (cp == NULL) { 2232a0f704ffSMarcel Moolenaar cp = null; 2233a0f704ffSMarcel Moolenaar len = sizeof(null) - 1; 2234a0f704ffSMarcel Moolenaar } 2235a0f704ffSMarcel Moolenaar 223631337658SMarcel Moolenaar /* 223731337658SMarcel Moolenaar * Optimize the most common case, which is "%s". We just 223831337658SMarcel Moolenaar * need to copy the complete string to the output buffer. 223931337658SMarcel Moolenaar */ 224031337658SMarcel Moolenaar if (xfp->xf_enc == need_enc 224131337658SMarcel Moolenaar && xfp->xf_width[XF_WIDTH_MIN] < 0 224231337658SMarcel Moolenaar && xfp->xf_width[XF_WIDTH_SIZE] < 0 224331337658SMarcel Moolenaar && xfp->xf_width[XF_WIDTH_MAX] < 0 224431337658SMarcel Moolenaar && !(xop->xo_flags & (XOF_ANCHOR | XOF_COLUMNS))) { 224531337658SMarcel Moolenaar len = strlen(cp); 224631337658SMarcel Moolenaar xo_buf_escape(xop, xbp, cp, len, flags); 224731337658SMarcel Moolenaar 224831337658SMarcel Moolenaar /* 224931337658SMarcel Moolenaar * Our caller expects xb_curp left untouched, so we have 225031337658SMarcel Moolenaar * to reset it and return the number of bytes written to 225131337658SMarcel Moolenaar * the buffer. 225231337658SMarcel Moolenaar */ 225331337658SMarcel Moolenaar off2 = xbp->xb_curp - xbp->xb_bufp; 225431337658SMarcel Moolenaar rc = off2 - off; 225531337658SMarcel Moolenaar xbp->xb_curp = xbp->xb_bufp + off; 225631337658SMarcel Moolenaar 225731337658SMarcel Moolenaar return rc; 225831337658SMarcel Moolenaar } 225931337658SMarcel Moolenaar } 226031337658SMarcel Moolenaar 226131337658SMarcel Moolenaar cols = xo_format_string_direct(xop, xbp, flags, wcp, cp, len, 226231337658SMarcel Moolenaar xfp->xf_width[XF_WIDTH_MAX], 226331337658SMarcel Moolenaar need_enc, xfp->xf_enc); 226431337658SMarcel Moolenaar if (cols < 0) 226531337658SMarcel Moolenaar goto bail; 226631337658SMarcel Moolenaar 226731337658SMarcel Moolenaar /* 226831337658SMarcel Moolenaar * xo_buf_append* will move xb_curp, so we save/restore it. 226931337658SMarcel Moolenaar */ 227031337658SMarcel Moolenaar off2 = xbp->xb_curp - xbp->xb_bufp; 227131337658SMarcel Moolenaar rc = off2 - off; 227231337658SMarcel Moolenaar xbp->xb_curp = xbp->xb_bufp + off; 227331337658SMarcel Moolenaar 227431337658SMarcel Moolenaar if (cols < xfp->xf_width[XF_WIDTH_MIN]) { 227531337658SMarcel Moolenaar /* 227631337658SMarcel Moolenaar * Find the number of columns needed to display the string. 227731337658SMarcel Moolenaar * If we have the original wide string, we just call wcswidth, 227831337658SMarcel Moolenaar * but if we did the work ourselves, then we need to do it. 227931337658SMarcel Moolenaar */ 228031337658SMarcel Moolenaar int delta = xfp->xf_width[XF_WIDTH_MIN] - cols; 228131337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, delta)) 228231337658SMarcel Moolenaar goto bail; 228331337658SMarcel Moolenaar 228431337658SMarcel Moolenaar /* 228531337658SMarcel Moolenaar * If seen_minus, then pad on the right; otherwise move it so 228631337658SMarcel Moolenaar * we can pad on the left. 228731337658SMarcel Moolenaar */ 228831337658SMarcel Moolenaar if (xfp->xf_seen_minus) { 228931337658SMarcel Moolenaar cp = xbp->xb_curp + rc; 229031337658SMarcel Moolenaar } else { 229131337658SMarcel Moolenaar cp = xbp->xb_curp; 229231337658SMarcel Moolenaar memmove(xbp->xb_curp + delta, xbp->xb_curp, rc); 229331337658SMarcel Moolenaar } 229431337658SMarcel Moolenaar 229531337658SMarcel Moolenaar /* Set the padding */ 229631337658SMarcel Moolenaar memset(cp, (xfp->xf_leading_zero > 0) ? '0' : ' ', delta); 229731337658SMarcel Moolenaar rc += delta; 229831337658SMarcel Moolenaar cols += delta; 229931337658SMarcel Moolenaar } 230031337658SMarcel Moolenaar 230131337658SMarcel Moolenaar if (xop->xo_flags & XOF_COLUMNS) 230231337658SMarcel Moolenaar xop->xo_columns += cols; 230331337658SMarcel Moolenaar if (xop->xo_flags & XOF_ANCHOR) 230431337658SMarcel Moolenaar xop->xo_anchor_columns += cols; 230531337658SMarcel Moolenaar 230631337658SMarcel Moolenaar return rc; 230731337658SMarcel Moolenaar 230831337658SMarcel Moolenaar bail: 230931337658SMarcel Moolenaar xbp->xb_curp = xbp->xb_bufp + off; 231031337658SMarcel Moolenaar return 0; 231131337658SMarcel Moolenaar } 231231337658SMarcel Moolenaar 231331337658SMarcel Moolenaar static void 231431337658SMarcel Moolenaar xo_data_append_content (xo_handle_t *xop, const char *str, int len) 231531337658SMarcel Moolenaar { 231631337658SMarcel Moolenaar int cols; 2317788ca347SMarcel Moolenaar int need_enc = (xo_style(xop) == XO_STYLE_TEXT) 231831337658SMarcel Moolenaar ? XF_ENC_LOCALE : XF_ENC_UTF8; 231931337658SMarcel Moolenaar 232031337658SMarcel Moolenaar cols = xo_format_string_direct(xop, &xop->xo_data, XFF_UNESCAPE, 232131337658SMarcel Moolenaar NULL, str, len, -1, 232231337658SMarcel Moolenaar need_enc, XF_ENC_UTF8); 232331337658SMarcel Moolenaar 232431337658SMarcel Moolenaar if (xop->xo_flags & XOF_COLUMNS) 232531337658SMarcel Moolenaar xop->xo_columns += cols; 232631337658SMarcel Moolenaar if (xop->xo_flags & XOF_ANCHOR) 232731337658SMarcel Moolenaar xop->xo_anchor_columns += cols; 232831337658SMarcel Moolenaar } 232931337658SMarcel Moolenaar 233031337658SMarcel Moolenaar static void 233131337658SMarcel Moolenaar xo_bump_width (xo_format_t *xfp, int digit) 233231337658SMarcel Moolenaar { 233331337658SMarcel Moolenaar int *ip = &xfp->xf_width[xfp->xf_dots]; 233431337658SMarcel Moolenaar 233531337658SMarcel Moolenaar *ip = ((*ip > 0) ? *ip : 0) * 10 + digit; 233631337658SMarcel Moolenaar } 233731337658SMarcel Moolenaar 233831337658SMarcel Moolenaar static int 233931337658SMarcel Moolenaar xo_trim_ws (xo_buffer_t *xbp, int len) 234031337658SMarcel Moolenaar { 234131337658SMarcel Moolenaar char *cp, *sp, *ep; 234231337658SMarcel Moolenaar int delta; 234331337658SMarcel Moolenaar 234431337658SMarcel Moolenaar /* First trim leading space */ 234531337658SMarcel Moolenaar for (cp = sp = xbp->xb_curp, ep = cp + len; cp < ep; cp++) { 234631337658SMarcel Moolenaar if (*cp != ' ') 234731337658SMarcel Moolenaar break; 234831337658SMarcel Moolenaar } 234931337658SMarcel Moolenaar 235031337658SMarcel Moolenaar delta = cp - sp; 235131337658SMarcel Moolenaar if (delta) { 235231337658SMarcel Moolenaar len -= delta; 235331337658SMarcel Moolenaar memmove(sp, cp, len); 235431337658SMarcel Moolenaar } 235531337658SMarcel Moolenaar 235631337658SMarcel Moolenaar /* Then trim off the end */ 235731337658SMarcel Moolenaar for (cp = xbp->xb_curp, sp = ep = cp + len; cp < ep; ep--) { 235831337658SMarcel Moolenaar if (ep[-1] != ' ') 235931337658SMarcel Moolenaar break; 236031337658SMarcel Moolenaar } 236131337658SMarcel Moolenaar 236231337658SMarcel Moolenaar delta = sp - ep; 236331337658SMarcel Moolenaar if (delta) { 236431337658SMarcel Moolenaar len -= delta; 236531337658SMarcel Moolenaar cp[len] = '\0'; 236631337658SMarcel Moolenaar } 236731337658SMarcel Moolenaar 236831337658SMarcel Moolenaar return len; 236931337658SMarcel Moolenaar } 237031337658SMarcel Moolenaar 237131337658SMarcel Moolenaar static int 237231337658SMarcel Moolenaar xo_format_data (xo_handle_t *xop, xo_buffer_t *xbp, 237331337658SMarcel Moolenaar const char *fmt, int flen, xo_xff_flags_t flags) 237431337658SMarcel Moolenaar { 237531337658SMarcel Moolenaar xo_format_t xf; 237631337658SMarcel Moolenaar const char *cp, *ep, *sp, *xp = NULL; 237731337658SMarcel Moolenaar int rc, cols; 2378788ca347SMarcel Moolenaar int style = (flags & XFF_XML) ? XO_STYLE_XML : xo_style(xop); 237931337658SMarcel Moolenaar unsigned make_output = !(flags & XFF_NO_OUTPUT); 2380788ca347SMarcel Moolenaar int need_enc = (xo_style(xop) == XO_STYLE_TEXT) 238131337658SMarcel Moolenaar ? XF_ENC_LOCALE : XF_ENC_UTF8; 238231337658SMarcel Moolenaar 238331337658SMarcel Moolenaar if (xbp == NULL) 238431337658SMarcel Moolenaar xbp = &xop->xo_data; 238531337658SMarcel Moolenaar 238631337658SMarcel Moolenaar for (cp = fmt, ep = fmt + flen; cp < ep; cp++) { 238731337658SMarcel Moolenaar if (*cp != '%') { 238831337658SMarcel Moolenaar add_one: 238931337658SMarcel Moolenaar if (xp == NULL) 239031337658SMarcel Moolenaar xp = cp; 239131337658SMarcel Moolenaar 239231337658SMarcel Moolenaar if (*cp == '\\' && cp[1] != '\0') 239331337658SMarcel Moolenaar cp += 1; 239431337658SMarcel Moolenaar continue; 239531337658SMarcel Moolenaar 239631337658SMarcel Moolenaar } if (cp + 1 < ep && cp[1] == '%') { 239731337658SMarcel Moolenaar cp += 1; 239831337658SMarcel Moolenaar goto add_one; 239931337658SMarcel Moolenaar } 240031337658SMarcel Moolenaar 240131337658SMarcel Moolenaar if (xp) { 240231337658SMarcel Moolenaar if (make_output) { 240331337658SMarcel Moolenaar cols = xo_format_string_direct(xop, xbp, flags | XFF_UNESCAPE, 240431337658SMarcel Moolenaar NULL, xp, cp - xp, -1, 240531337658SMarcel Moolenaar need_enc, XF_ENC_UTF8); 240631337658SMarcel Moolenaar if (xop->xo_flags & XOF_COLUMNS) 240731337658SMarcel Moolenaar xop->xo_columns += cols; 240831337658SMarcel Moolenaar if (xop->xo_flags & XOF_ANCHOR) 240931337658SMarcel Moolenaar xop->xo_anchor_columns += cols; 241031337658SMarcel Moolenaar } 241131337658SMarcel Moolenaar 241231337658SMarcel Moolenaar xp = NULL; 241331337658SMarcel Moolenaar } 241431337658SMarcel Moolenaar 241531337658SMarcel Moolenaar bzero(&xf, sizeof(xf)); 241631337658SMarcel Moolenaar xf.xf_leading_zero = -1; 241731337658SMarcel Moolenaar xf.xf_width[0] = xf.xf_width[1] = xf.xf_width[2] = -1; 241831337658SMarcel Moolenaar 241931337658SMarcel Moolenaar /* 242031337658SMarcel Moolenaar * "%@" starts an XO-specific set of flags: 242131337658SMarcel Moolenaar * @X@ - XML-only field; ignored if style isn't XML 242231337658SMarcel Moolenaar */ 242331337658SMarcel Moolenaar if (cp[1] == '@') { 242431337658SMarcel Moolenaar for (cp += 2; cp < ep; cp++) { 242531337658SMarcel Moolenaar if (*cp == '@') { 242631337658SMarcel Moolenaar break; 242731337658SMarcel Moolenaar } 242831337658SMarcel Moolenaar if (*cp == '*') { 242931337658SMarcel Moolenaar /* 243031337658SMarcel Moolenaar * '*' means there's a "%*.*s" value in vap that 243131337658SMarcel Moolenaar * we want to ignore 243231337658SMarcel Moolenaar */ 243331337658SMarcel Moolenaar if (!(xop->xo_flags & XOF_NO_VA_ARG)) 243431337658SMarcel Moolenaar va_arg(xop->xo_vap, int); 243531337658SMarcel Moolenaar } 243631337658SMarcel Moolenaar } 243731337658SMarcel Moolenaar } 243831337658SMarcel Moolenaar 243931337658SMarcel Moolenaar /* Hidden fields are only visible to JSON and XML */ 244031337658SMarcel Moolenaar if (xop->xo_flags & XFF_ENCODE_ONLY) { 244131337658SMarcel Moolenaar if (style != XO_STYLE_XML 2442788ca347SMarcel Moolenaar && xo_style(xop) != XO_STYLE_JSON) 244331337658SMarcel Moolenaar xf.xf_skip = 1; 244431337658SMarcel Moolenaar } else if (xop->xo_flags & XFF_DISPLAY_ONLY) { 244531337658SMarcel Moolenaar if (style != XO_STYLE_TEXT 2446788ca347SMarcel Moolenaar && xo_style(xop) != XO_STYLE_HTML) 244731337658SMarcel Moolenaar xf.xf_skip = 1; 244831337658SMarcel Moolenaar } 244931337658SMarcel Moolenaar 245031337658SMarcel Moolenaar if (!make_output) 245131337658SMarcel Moolenaar xf.xf_skip = 1; 245231337658SMarcel Moolenaar 245331337658SMarcel Moolenaar /* 245431337658SMarcel Moolenaar * Looking at one piece of a format; find the end and 245531337658SMarcel Moolenaar * call snprintf. Then advance xo_vap on our own. 245631337658SMarcel Moolenaar * 245731337658SMarcel Moolenaar * Note that 'n', 'v', and '$' are not supported. 245831337658SMarcel Moolenaar */ 245931337658SMarcel Moolenaar sp = cp; /* Save start pointer */ 246031337658SMarcel Moolenaar for (cp += 1; cp < ep; cp++) { 246131337658SMarcel Moolenaar if (*cp == 'l') 246231337658SMarcel Moolenaar xf.xf_lflag += 1; 246331337658SMarcel Moolenaar else if (*cp == 'h') 246431337658SMarcel Moolenaar xf.xf_hflag += 1; 246531337658SMarcel Moolenaar else if (*cp == 'j') 246631337658SMarcel Moolenaar xf.xf_jflag += 1; 246731337658SMarcel Moolenaar else if (*cp == 't') 246831337658SMarcel Moolenaar xf.xf_tflag += 1; 246931337658SMarcel Moolenaar else if (*cp == 'z') 247031337658SMarcel Moolenaar xf.xf_zflag += 1; 247131337658SMarcel Moolenaar else if (*cp == 'q') 247231337658SMarcel Moolenaar xf.xf_qflag += 1; 247331337658SMarcel Moolenaar else if (*cp == '.') { 247431337658SMarcel Moolenaar if (++xf.xf_dots >= XF_WIDTH_NUM) { 247531337658SMarcel Moolenaar xo_failure(xop, "Too many dots in format: '%s'", fmt); 247631337658SMarcel Moolenaar return -1; 247731337658SMarcel Moolenaar } 247831337658SMarcel Moolenaar } else if (*cp == '-') 247931337658SMarcel Moolenaar xf.xf_seen_minus = 1; 248031337658SMarcel Moolenaar else if (isdigit((int) *cp)) { 248131337658SMarcel Moolenaar if (xf.xf_leading_zero < 0) 248231337658SMarcel Moolenaar xf.xf_leading_zero = (*cp == '0'); 248331337658SMarcel Moolenaar xo_bump_width(&xf, *cp - '0'); 248431337658SMarcel Moolenaar } else if (*cp == '*') { 248531337658SMarcel Moolenaar xf.xf_stars += 1; 248631337658SMarcel Moolenaar xf.xf_star[xf.xf_dots] = 1; 248731337658SMarcel Moolenaar } else if (strchr("diouxXDOUeEfFgGaAcCsSp", *cp) != NULL) 248831337658SMarcel Moolenaar break; 248931337658SMarcel Moolenaar else if (*cp == 'n' || *cp == 'v') { 249031337658SMarcel Moolenaar xo_failure(xop, "unsupported format: '%s'", fmt); 249131337658SMarcel Moolenaar return -1; 249231337658SMarcel Moolenaar } 249331337658SMarcel Moolenaar } 249431337658SMarcel Moolenaar 249531337658SMarcel Moolenaar if (cp == ep) 249631337658SMarcel Moolenaar xo_failure(xop, "field format missing format character: %s", 249731337658SMarcel Moolenaar fmt); 249831337658SMarcel Moolenaar 249931337658SMarcel Moolenaar xf.xf_fc = *cp; 250031337658SMarcel Moolenaar 250131337658SMarcel Moolenaar if (!(xop->xo_flags & XOF_NO_VA_ARG)) { 250231337658SMarcel Moolenaar if (*cp == 's' || *cp == 'S') { 250331337658SMarcel Moolenaar /* Handle "%*.*.*s" */ 250431337658SMarcel Moolenaar int s; 250531337658SMarcel Moolenaar for (s = 0; s < XF_WIDTH_NUM; s++) { 250631337658SMarcel Moolenaar if (xf.xf_star[s]) { 250731337658SMarcel Moolenaar xf.xf_width[s] = va_arg(xop->xo_vap, int); 250831337658SMarcel Moolenaar 250931337658SMarcel Moolenaar /* Normalize a negative width value */ 251031337658SMarcel Moolenaar if (xf.xf_width[s] < 0) { 251131337658SMarcel Moolenaar if (s == 0) { 251231337658SMarcel Moolenaar xf.xf_width[0] = -xf.xf_width[0]; 251331337658SMarcel Moolenaar xf.xf_seen_minus = 1; 251431337658SMarcel Moolenaar } else 251531337658SMarcel Moolenaar xf.xf_width[s] = -1; /* Ignore negative values */ 251631337658SMarcel Moolenaar } 251731337658SMarcel Moolenaar } 251831337658SMarcel Moolenaar } 251931337658SMarcel Moolenaar } 252031337658SMarcel Moolenaar } 252131337658SMarcel Moolenaar 252231337658SMarcel Moolenaar /* If no max is given, it defaults to size */ 252331337658SMarcel Moolenaar if (xf.xf_width[XF_WIDTH_MAX] < 0 && xf.xf_width[XF_WIDTH_SIZE] >= 0) 252431337658SMarcel Moolenaar xf.xf_width[XF_WIDTH_MAX] = xf.xf_width[XF_WIDTH_SIZE]; 252531337658SMarcel Moolenaar 252631337658SMarcel Moolenaar if (xf.xf_fc == 'D' || xf.xf_fc == 'O' || xf.xf_fc == 'U') 252731337658SMarcel Moolenaar xf.xf_lflag = 1; 252831337658SMarcel Moolenaar 252931337658SMarcel Moolenaar if (!xf.xf_skip) { 253031337658SMarcel Moolenaar xo_buffer_t *fbp = &xop->xo_fmt; 253131337658SMarcel Moolenaar int len = cp - sp + 1; 253231337658SMarcel Moolenaar if (!xo_buf_has_room(fbp, len + 1)) 253331337658SMarcel Moolenaar return -1; 253431337658SMarcel Moolenaar 253531337658SMarcel Moolenaar char *newfmt = fbp->xb_curp; 253631337658SMarcel Moolenaar memcpy(newfmt, sp, len); 253731337658SMarcel Moolenaar newfmt[0] = '%'; /* If we skipped over a "%@...@s" format */ 253831337658SMarcel Moolenaar newfmt[len] = '\0'; 253931337658SMarcel Moolenaar 254031337658SMarcel Moolenaar /* 254131337658SMarcel Moolenaar * Bad news: our strings are UTF-8, but the stock printf 254231337658SMarcel Moolenaar * functions won't handle field widths for wide characters 254331337658SMarcel Moolenaar * correctly. So we have to handle this ourselves. 254431337658SMarcel Moolenaar */ 254531337658SMarcel Moolenaar if (xop->xo_formatter == NULL 254631337658SMarcel Moolenaar && (xf.xf_fc == 's' || xf.xf_fc == 'S')) { 254731337658SMarcel Moolenaar xf.xf_enc = (xf.xf_lflag || (xf.xf_fc == 'S')) 254831337658SMarcel Moolenaar ? XF_ENC_WIDE : xf.xf_hflag ? XF_ENC_LOCALE : XF_ENC_UTF8; 254931337658SMarcel Moolenaar rc = xo_format_string(xop, xbp, flags, &xf); 255031337658SMarcel Moolenaar 255131337658SMarcel Moolenaar if ((flags & XFF_TRIM_WS) 2552788ca347SMarcel Moolenaar && (xo_style(xop) == XO_STYLE_XML 2553788ca347SMarcel Moolenaar || xo_style(xop) == XO_STYLE_JSON)) 255431337658SMarcel Moolenaar rc = xo_trim_ws(xbp, rc); 255531337658SMarcel Moolenaar 255631337658SMarcel Moolenaar } else { 255731337658SMarcel Moolenaar int columns = rc = xo_vsnprintf(xop, xbp, newfmt, xop->xo_vap); 255831337658SMarcel Moolenaar 255931337658SMarcel Moolenaar /* 256031337658SMarcel Moolenaar * For XML and HTML, we need "&<>" processing; for JSON, 256131337658SMarcel Moolenaar * it's quotes. Text gets nothing. 256231337658SMarcel Moolenaar */ 256331337658SMarcel Moolenaar switch (style) { 256431337658SMarcel Moolenaar case XO_STYLE_XML: 256531337658SMarcel Moolenaar if (flags & XFF_TRIM_WS) 256631337658SMarcel Moolenaar columns = rc = xo_trim_ws(xbp, rc); 256731337658SMarcel Moolenaar /* fall thru */ 256831337658SMarcel Moolenaar case XO_STYLE_HTML: 256931337658SMarcel Moolenaar rc = xo_escape_xml(xbp, rc, (flags & XFF_ATTR)); 257031337658SMarcel Moolenaar break; 257131337658SMarcel Moolenaar 257231337658SMarcel Moolenaar case XO_STYLE_JSON: 257331337658SMarcel Moolenaar if (flags & XFF_TRIM_WS) 257431337658SMarcel Moolenaar columns = rc = xo_trim_ws(xbp, rc); 257531337658SMarcel Moolenaar rc = xo_escape_json(xbp, rc); 257631337658SMarcel Moolenaar break; 257731337658SMarcel Moolenaar } 257831337658SMarcel Moolenaar 257931337658SMarcel Moolenaar /* 258031337658SMarcel Moolenaar * We can assume all the data we've added is ASCII, so 258131337658SMarcel Moolenaar * the columns and bytes are the same. xo_format_string 258231337658SMarcel Moolenaar * handles all the fancy string conversions and updates 258331337658SMarcel Moolenaar * xo_anchor_columns accordingly. 258431337658SMarcel Moolenaar */ 258531337658SMarcel Moolenaar if (xop->xo_flags & XOF_COLUMNS) 258631337658SMarcel Moolenaar xop->xo_columns += columns; 258731337658SMarcel Moolenaar if (xop->xo_flags & XOF_ANCHOR) 258831337658SMarcel Moolenaar xop->xo_anchor_columns += columns; 258931337658SMarcel Moolenaar } 259031337658SMarcel Moolenaar 259131337658SMarcel Moolenaar xbp->xb_curp += rc; 259231337658SMarcel Moolenaar } 259331337658SMarcel Moolenaar 259431337658SMarcel Moolenaar /* 259531337658SMarcel Moolenaar * Now for the tricky part: we need to move the argument pointer 259631337658SMarcel Moolenaar * along by the amount needed. 259731337658SMarcel Moolenaar */ 259831337658SMarcel Moolenaar if (!(xop->xo_flags & XOF_NO_VA_ARG)) { 259931337658SMarcel Moolenaar 260031337658SMarcel Moolenaar if (xf.xf_fc == 's' ||xf.xf_fc == 'S') { 260131337658SMarcel Moolenaar /* 260231337658SMarcel Moolenaar * The 'S' and 's' formats are normally handled in 260331337658SMarcel Moolenaar * xo_format_string, but if we skipped it, then we 260431337658SMarcel Moolenaar * need to pop it. 260531337658SMarcel Moolenaar */ 260631337658SMarcel Moolenaar if (xf.xf_skip) 260731337658SMarcel Moolenaar va_arg(xop->xo_vap, char *); 260831337658SMarcel Moolenaar 260931337658SMarcel Moolenaar } else { 261031337658SMarcel Moolenaar int s; 261131337658SMarcel Moolenaar for (s = 0; s < XF_WIDTH_NUM; s++) { 261231337658SMarcel Moolenaar if (xf.xf_star[s]) 261331337658SMarcel Moolenaar va_arg(xop->xo_vap, int); 261431337658SMarcel Moolenaar } 261531337658SMarcel Moolenaar 261631337658SMarcel Moolenaar if (strchr("diouxXDOU", xf.xf_fc) != NULL) { 261731337658SMarcel Moolenaar if (xf.xf_hflag > 1) { 261831337658SMarcel Moolenaar va_arg(xop->xo_vap, int); 261931337658SMarcel Moolenaar 262031337658SMarcel Moolenaar } else if (xf.xf_hflag > 0) { 262131337658SMarcel Moolenaar va_arg(xop->xo_vap, int); 262231337658SMarcel Moolenaar 262331337658SMarcel Moolenaar } else if (xf.xf_lflag > 1) { 262431337658SMarcel Moolenaar va_arg(xop->xo_vap, unsigned long long); 262531337658SMarcel Moolenaar 262631337658SMarcel Moolenaar } else if (xf.xf_lflag > 0) { 262731337658SMarcel Moolenaar va_arg(xop->xo_vap, unsigned long); 262831337658SMarcel Moolenaar 262931337658SMarcel Moolenaar } else if (xf.xf_jflag > 0) { 263031337658SMarcel Moolenaar va_arg(xop->xo_vap, intmax_t); 263131337658SMarcel Moolenaar 263231337658SMarcel Moolenaar } else if (xf.xf_tflag > 0) { 263331337658SMarcel Moolenaar va_arg(xop->xo_vap, ptrdiff_t); 263431337658SMarcel Moolenaar 263531337658SMarcel Moolenaar } else if (xf.xf_zflag > 0) { 263631337658SMarcel Moolenaar va_arg(xop->xo_vap, size_t); 263731337658SMarcel Moolenaar 263831337658SMarcel Moolenaar } else if (xf.xf_qflag > 0) { 263931337658SMarcel Moolenaar va_arg(xop->xo_vap, quad_t); 264031337658SMarcel Moolenaar 264131337658SMarcel Moolenaar } else { 264231337658SMarcel Moolenaar va_arg(xop->xo_vap, int); 264331337658SMarcel Moolenaar } 264431337658SMarcel Moolenaar } else if (strchr("eEfFgGaA", xf.xf_fc) != NULL) 264531337658SMarcel Moolenaar if (xf.xf_lflag) 264631337658SMarcel Moolenaar va_arg(xop->xo_vap, long double); 264731337658SMarcel Moolenaar else 264831337658SMarcel Moolenaar va_arg(xop->xo_vap, double); 264931337658SMarcel Moolenaar 265031337658SMarcel Moolenaar else if (xf.xf_fc == 'C' || (xf.xf_fc == 'c' && xf.xf_lflag)) 265131337658SMarcel Moolenaar va_arg(xop->xo_vap, wint_t); 265231337658SMarcel Moolenaar 265331337658SMarcel Moolenaar else if (xf.xf_fc == 'c') 265431337658SMarcel Moolenaar va_arg(xop->xo_vap, int); 265531337658SMarcel Moolenaar 265631337658SMarcel Moolenaar else if (xf.xf_fc == 'p') 265731337658SMarcel Moolenaar va_arg(xop->xo_vap, void *); 265831337658SMarcel Moolenaar } 265931337658SMarcel Moolenaar } 266031337658SMarcel Moolenaar } 266131337658SMarcel Moolenaar 266231337658SMarcel Moolenaar if (xp) { 266331337658SMarcel Moolenaar if (make_output) { 266431337658SMarcel Moolenaar cols = xo_format_string_direct(xop, xbp, flags | XFF_UNESCAPE, 266531337658SMarcel Moolenaar NULL, xp, cp - xp, -1, 266631337658SMarcel Moolenaar need_enc, XF_ENC_UTF8); 266731337658SMarcel Moolenaar if (xop->xo_flags & XOF_COLUMNS) 266831337658SMarcel Moolenaar xop->xo_columns += cols; 266931337658SMarcel Moolenaar if (xop->xo_flags & XOF_ANCHOR) 267031337658SMarcel Moolenaar xop->xo_anchor_columns += cols; 267131337658SMarcel Moolenaar } 267231337658SMarcel Moolenaar 267331337658SMarcel Moolenaar xp = NULL; 267431337658SMarcel Moolenaar } 267531337658SMarcel Moolenaar 267631337658SMarcel Moolenaar return 0; 267731337658SMarcel Moolenaar } 267831337658SMarcel Moolenaar 267931337658SMarcel Moolenaar static char * 268031337658SMarcel Moolenaar xo_fix_encoding (xo_handle_t *xop UNUSED, char *encoding) 268131337658SMarcel Moolenaar { 268231337658SMarcel Moolenaar char *cp = encoding; 268331337658SMarcel Moolenaar 268431337658SMarcel Moolenaar if (cp[0] != '%' || !isdigit((int) cp[1])) 268531337658SMarcel Moolenaar return encoding; 268631337658SMarcel Moolenaar 268731337658SMarcel Moolenaar for (cp += 2; *cp; cp++) { 268831337658SMarcel Moolenaar if (!isdigit((int) *cp)) 268931337658SMarcel Moolenaar break; 269031337658SMarcel Moolenaar } 269131337658SMarcel Moolenaar 269231337658SMarcel Moolenaar cp -= 1; 269331337658SMarcel Moolenaar *cp = '%'; 269431337658SMarcel Moolenaar 269531337658SMarcel Moolenaar return cp; 269631337658SMarcel Moolenaar } 269731337658SMarcel Moolenaar 269831337658SMarcel Moolenaar static void 2699788ca347SMarcel Moolenaar xo_color_append_html (xo_handle_t *xop) 2700788ca347SMarcel Moolenaar { 2701788ca347SMarcel Moolenaar /* 2702788ca347SMarcel Moolenaar * If the color buffer has content, we add it now. It's already 2703788ca347SMarcel Moolenaar * prebuilt and ready, since we want to add it to every <div>. 2704788ca347SMarcel Moolenaar */ 2705788ca347SMarcel Moolenaar if (!xo_buf_is_empty(&xop->xo_color_buf)) { 2706788ca347SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_color_buf; 2707788ca347SMarcel Moolenaar 2708788ca347SMarcel Moolenaar xo_data_append(xop, xbp->xb_bufp, xbp->xb_curp - xbp->xb_bufp); 2709788ca347SMarcel Moolenaar } 2710788ca347SMarcel Moolenaar } 2711788ca347SMarcel Moolenaar 2712788ca347SMarcel Moolenaar static void 271331337658SMarcel Moolenaar xo_buf_append_div (xo_handle_t *xop, const char *class, xo_xff_flags_t flags, 271431337658SMarcel Moolenaar const char *name, int nlen, 271531337658SMarcel Moolenaar const char *value, int vlen, 271631337658SMarcel Moolenaar const char *encoding, int elen) 271731337658SMarcel Moolenaar { 271831337658SMarcel Moolenaar static char div_start[] = "<div class=\""; 271931337658SMarcel Moolenaar static char div_tag[] = "\" data-tag=\""; 272031337658SMarcel Moolenaar static char div_xpath[] = "\" data-xpath=\""; 272131337658SMarcel Moolenaar static char div_key[] = "\" data-key=\"key"; 272231337658SMarcel Moolenaar static char div_end[] = "\">"; 272331337658SMarcel Moolenaar static char div_close[] = "</div>"; 272431337658SMarcel Moolenaar 272531337658SMarcel Moolenaar /* 272631337658SMarcel Moolenaar * To build our XPath predicate, we need to save the va_list before 272731337658SMarcel Moolenaar * we format our data, and then restore it before we format the 272831337658SMarcel Moolenaar * xpath expression. 272931337658SMarcel Moolenaar * Display-only keys implies that we've got an encode-only key 273031337658SMarcel Moolenaar * elsewhere, so we don't use them from making predicates. 273131337658SMarcel Moolenaar */ 273231337658SMarcel Moolenaar int need_predidate = 273331337658SMarcel Moolenaar (name && (flags & XFF_KEY) && !(flags & XFF_DISPLAY_ONLY) 273431337658SMarcel Moolenaar && (xop->xo_flags & XOF_XPATH)); 273531337658SMarcel Moolenaar 273631337658SMarcel Moolenaar if (need_predidate) { 273731337658SMarcel Moolenaar va_list va_local; 273831337658SMarcel Moolenaar 273931337658SMarcel Moolenaar va_copy(va_local, xop->xo_vap); 274031337658SMarcel Moolenaar if (xop->xo_checkpointer) 274131337658SMarcel Moolenaar xop->xo_checkpointer(xop, xop->xo_vap, 0); 274231337658SMarcel Moolenaar 274331337658SMarcel Moolenaar /* 274431337658SMarcel Moolenaar * Build an XPath predicate expression to match this key. 274531337658SMarcel Moolenaar * We use the format buffer. 274631337658SMarcel Moolenaar */ 274731337658SMarcel Moolenaar xo_buffer_t *pbp = &xop->xo_predicate; 274831337658SMarcel Moolenaar pbp->xb_curp = pbp->xb_bufp; /* Restart buffer */ 274931337658SMarcel Moolenaar 275031337658SMarcel Moolenaar xo_buf_append(pbp, "[", 1); 275131337658SMarcel Moolenaar xo_buf_escape(xop, pbp, name, nlen, 0); 275231337658SMarcel Moolenaar if (xop->xo_flags & XOF_PRETTY) 275331337658SMarcel Moolenaar xo_buf_append(pbp, " = '", 4); 275431337658SMarcel Moolenaar else 275531337658SMarcel Moolenaar xo_buf_append(pbp, "='", 2); 275631337658SMarcel Moolenaar 275731337658SMarcel Moolenaar /* The encoding format defaults to the normal format */ 275831337658SMarcel Moolenaar if (encoding == NULL) { 275931337658SMarcel Moolenaar char *enc = alloca(vlen + 1); 276031337658SMarcel Moolenaar memcpy(enc, value, vlen); 276131337658SMarcel Moolenaar enc[vlen] = '\0'; 276231337658SMarcel Moolenaar encoding = xo_fix_encoding(xop, enc); 276331337658SMarcel Moolenaar elen = strlen(encoding); 276431337658SMarcel Moolenaar } 276531337658SMarcel Moolenaar 276631337658SMarcel Moolenaar xo_format_data(xop, pbp, encoding, elen, XFF_XML | XFF_ATTR); 276731337658SMarcel Moolenaar 276831337658SMarcel Moolenaar xo_buf_append(pbp, "']", 2); 276931337658SMarcel Moolenaar 277031337658SMarcel Moolenaar /* Now we record this predicate expression in the stack */ 277131337658SMarcel Moolenaar xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth]; 277231337658SMarcel Moolenaar int olen = xsp->xs_keys ? strlen(xsp->xs_keys) : 0; 277331337658SMarcel Moolenaar int dlen = pbp->xb_curp - pbp->xb_bufp; 277431337658SMarcel Moolenaar 277531337658SMarcel Moolenaar char *cp = xo_realloc(xsp->xs_keys, olen + dlen + 1); 277631337658SMarcel Moolenaar if (cp) { 277731337658SMarcel Moolenaar memcpy(cp + olen, pbp->xb_bufp, dlen); 277831337658SMarcel Moolenaar cp[olen + dlen] = '\0'; 277931337658SMarcel Moolenaar xsp->xs_keys = cp; 278031337658SMarcel Moolenaar } 278131337658SMarcel Moolenaar 278231337658SMarcel Moolenaar /* Now we reset the xo_vap as if we were never here */ 278331337658SMarcel Moolenaar va_end(xop->xo_vap); 278431337658SMarcel Moolenaar va_copy(xop->xo_vap, va_local); 278531337658SMarcel Moolenaar va_end(va_local); 278631337658SMarcel Moolenaar if (xop->xo_checkpointer) 278731337658SMarcel Moolenaar xop->xo_checkpointer(xop, xop->xo_vap, 1); 278831337658SMarcel Moolenaar } 278931337658SMarcel Moolenaar 279031337658SMarcel Moolenaar if (flags & XFF_ENCODE_ONLY) { 279131337658SMarcel Moolenaar /* 279231337658SMarcel Moolenaar * Even if this is encode-only, we need to go thru the 279331337658SMarcel Moolenaar * work of formatting it to make sure the args are cleared 279431337658SMarcel Moolenaar * from xo_vap. 279531337658SMarcel Moolenaar */ 279631337658SMarcel Moolenaar xo_format_data(xop, &xop->xo_data, encoding, elen, 279731337658SMarcel Moolenaar flags | XFF_NO_OUTPUT); 279831337658SMarcel Moolenaar return; 279931337658SMarcel Moolenaar } 280031337658SMarcel Moolenaar 280131337658SMarcel Moolenaar xo_line_ensure_open(xop, 0); 280231337658SMarcel Moolenaar 280331337658SMarcel Moolenaar if (xop->xo_flags & XOF_PRETTY) 280431337658SMarcel Moolenaar xo_buf_indent(xop, xop->xo_indent_by); 280531337658SMarcel Moolenaar 280631337658SMarcel Moolenaar xo_data_append(xop, div_start, sizeof(div_start) - 1); 280731337658SMarcel Moolenaar xo_data_append(xop, class, strlen(class)); 280831337658SMarcel Moolenaar 2809788ca347SMarcel Moolenaar /* 2810788ca347SMarcel Moolenaar * If the color buffer has content, we add it now. It's already 2811788ca347SMarcel Moolenaar * prebuilt and ready, since we want to add it to every <div>. 2812788ca347SMarcel Moolenaar */ 2813788ca347SMarcel Moolenaar if (!xo_buf_is_empty(&xop->xo_color_buf)) { 2814788ca347SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_color_buf; 2815788ca347SMarcel Moolenaar 2816788ca347SMarcel Moolenaar xo_data_append(xop, xbp->xb_bufp, xbp->xb_curp - xbp->xb_bufp); 2817788ca347SMarcel Moolenaar } 2818788ca347SMarcel Moolenaar 281931337658SMarcel Moolenaar if (name) { 282031337658SMarcel Moolenaar xo_data_append(xop, div_tag, sizeof(div_tag) - 1); 282131337658SMarcel Moolenaar xo_data_escape(xop, name, nlen); 282231337658SMarcel Moolenaar 282331337658SMarcel Moolenaar /* 282431337658SMarcel Moolenaar * Save the offset at which we'd place units. See xo_format_units. 282531337658SMarcel Moolenaar */ 282631337658SMarcel Moolenaar if (xop->xo_flags & XOF_UNITS) { 282731337658SMarcel Moolenaar xop->xo_flags |= XOF_UNITS_PENDING; 282831337658SMarcel Moolenaar /* 282931337658SMarcel Moolenaar * Note: We need the '+1' here because we know we've not 283031337658SMarcel Moolenaar * added the closing quote. We add one, knowing the quote 283131337658SMarcel Moolenaar * will be added shortly. 283231337658SMarcel Moolenaar */ 283331337658SMarcel Moolenaar xop->xo_units_offset = 283431337658SMarcel Moolenaar xop->xo_data.xb_curp -xop->xo_data.xb_bufp + 1; 283531337658SMarcel Moolenaar } 283631337658SMarcel Moolenaar } 283731337658SMarcel Moolenaar 283831337658SMarcel Moolenaar if (name) { 283931337658SMarcel Moolenaar if (xop->xo_flags & XOF_XPATH) { 284031337658SMarcel Moolenaar int i; 284131337658SMarcel Moolenaar xo_stack_t *xsp; 284231337658SMarcel Moolenaar 284331337658SMarcel Moolenaar xo_data_append(xop, div_xpath, sizeof(div_xpath) - 1); 284431337658SMarcel Moolenaar if (xop->xo_leading_xpath) 284531337658SMarcel Moolenaar xo_data_append(xop, xop->xo_leading_xpath, 284631337658SMarcel Moolenaar strlen(xop->xo_leading_xpath)); 284731337658SMarcel Moolenaar 284831337658SMarcel Moolenaar for (i = 0; i <= xop->xo_depth; i++) { 284931337658SMarcel Moolenaar xsp = &xop->xo_stack[i]; 285031337658SMarcel Moolenaar if (xsp->xs_name == NULL) 285131337658SMarcel Moolenaar continue; 285231337658SMarcel Moolenaar 2853545ddfbeSMarcel Moolenaar /* 2854545ddfbeSMarcel Moolenaar * XSS_OPEN_LIST and XSS_OPEN_LEAF_LIST stack frames 2855545ddfbeSMarcel Moolenaar * are directly under XSS_OPEN_INSTANCE frames so we 2856545ddfbeSMarcel Moolenaar * don't need to put these in our XPath expressions. 2857545ddfbeSMarcel Moolenaar */ 2858545ddfbeSMarcel Moolenaar if (xsp->xs_state == XSS_OPEN_LIST 2859545ddfbeSMarcel Moolenaar || xsp->xs_state == XSS_OPEN_LEAF_LIST) 2860545ddfbeSMarcel Moolenaar continue; 2861545ddfbeSMarcel Moolenaar 286231337658SMarcel Moolenaar xo_data_append(xop, "/", 1); 286331337658SMarcel Moolenaar xo_data_escape(xop, xsp->xs_name, strlen(xsp->xs_name)); 286431337658SMarcel Moolenaar if (xsp->xs_keys) { 286531337658SMarcel Moolenaar /* Don't show keys for the key field */ 286631337658SMarcel Moolenaar if (i != xop->xo_depth || !(flags & XFF_KEY)) 286731337658SMarcel Moolenaar xo_data_append(xop, xsp->xs_keys, strlen(xsp->xs_keys)); 286831337658SMarcel Moolenaar } 286931337658SMarcel Moolenaar } 287031337658SMarcel Moolenaar 287131337658SMarcel Moolenaar xo_data_append(xop, "/", 1); 287231337658SMarcel Moolenaar xo_data_escape(xop, name, nlen); 287331337658SMarcel Moolenaar } 287431337658SMarcel Moolenaar 287531337658SMarcel Moolenaar if ((xop->xo_flags & XOF_INFO) && xop->xo_info) { 287631337658SMarcel Moolenaar static char in_type[] = "\" data-type=\""; 287731337658SMarcel Moolenaar static char in_help[] = "\" data-help=\""; 287831337658SMarcel Moolenaar 287931337658SMarcel Moolenaar xo_info_t *xip = xo_info_find(xop, name, nlen); 288031337658SMarcel Moolenaar if (xip) { 288131337658SMarcel Moolenaar if (xip->xi_type) { 288231337658SMarcel Moolenaar xo_data_append(xop, in_type, sizeof(in_type) - 1); 288331337658SMarcel Moolenaar xo_data_escape(xop, xip->xi_type, strlen(xip->xi_type)); 288431337658SMarcel Moolenaar } 288531337658SMarcel Moolenaar if (xip->xi_help) { 288631337658SMarcel Moolenaar xo_data_append(xop, in_help, sizeof(in_help) - 1); 288731337658SMarcel Moolenaar xo_data_escape(xop, xip->xi_help, strlen(xip->xi_help)); 288831337658SMarcel Moolenaar } 288931337658SMarcel Moolenaar } 289031337658SMarcel Moolenaar } 289131337658SMarcel Moolenaar 289231337658SMarcel Moolenaar if ((flags & XFF_KEY) && (xop->xo_flags & XOF_KEYS)) 289331337658SMarcel Moolenaar xo_data_append(xop, div_key, sizeof(div_key) - 1); 289431337658SMarcel Moolenaar } 289531337658SMarcel Moolenaar 289631337658SMarcel Moolenaar xo_data_append(xop, div_end, sizeof(div_end) - 1); 289731337658SMarcel Moolenaar 289831337658SMarcel Moolenaar xo_format_data(xop, NULL, value, vlen, 0); 289931337658SMarcel Moolenaar 290031337658SMarcel Moolenaar xo_data_append(xop, div_close, sizeof(div_close) - 1); 290131337658SMarcel Moolenaar 290231337658SMarcel Moolenaar if (xop->xo_flags & XOF_PRETTY) 290331337658SMarcel Moolenaar xo_data_append(xop, "\n", 1); 290431337658SMarcel Moolenaar } 290531337658SMarcel Moolenaar 290631337658SMarcel Moolenaar static void 290731337658SMarcel Moolenaar xo_format_text (xo_handle_t *xop, const char *str, int len) 290831337658SMarcel Moolenaar { 2909788ca347SMarcel Moolenaar switch (xo_style(xop)) { 291031337658SMarcel Moolenaar case XO_STYLE_TEXT: 291131337658SMarcel Moolenaar xo_buf_append_locale(xop, &xop->xo_data, str, len); 291231337658SMarcel Moolenaar break; 291331337658SMarcel Moolenaar 291431337658SMarcel Moolenaar case XO_STYLE_HTML: 291531337658SMarcel Moolenaar xo_buf_append_div(xop, "text", 0, NULL, 0, str, len, NULL, 0); 291631337658SMarcel Moolenaar break; 291731337658SMarcel Moolenaar } 291831337658SMarcel Moolenaar } 291931337658SMarcel Moolenaar 292031337658SMarcel Moolenaar static void 292131337658SMarcel Moolenaar xo_format_title (xo_handle_t *xop, const char *str, int len, 292231337658SMarcel Moolenaar const char *fmt, int flen) 292331337658SMarcel Moolenaar { 2924788ca347SMarcel Moolenaar static char div_open[] = "<div class=\"title"; 2925788ca347SMarcel Moolenaar static char div_middle[] = "\">"; 292631337658SMarcel Moolenaar static char div_close[] = "</div>"; 292731337658SMarcel Moolenaar 2928545ddfbeSMarcel Moolenaar if (flen == 0) { 2929545ddfbeSMarcel Moolenaar fmt = "%s"; 2930545ddfbeSMarcel Moolenaar flen = 2; 2931545ddfbeSMarcel Moolenaar } 2932545ddfbeSMarcel Moolenaar 2933788ca347SMarcel Moolenaar switch (xo_style(xop)) { 293431337658SMarcel Moolenaar case XO_STYLE_XML: 293531337658SMarcel Moolenaar case XO_STYLE_JSON: 293631337658SMarcel Moolenaar /* 293731337658SMarcel Moolenaar * Even though we don't care about text, we need to do 293831337658SMarcel Moolenaar * enough parsing work to skip over the right bits of xo_vap. 293931337658SMarcel Moolenaar */ 294031337658SMarcel Moolenaar if (len == 0) 294131337658SMarcel Moolenaar xo_format_data(xop, NULL, fmt, flen, XFF_NO_OUTPUT); 294231337658SMarcel Moolenaar return; 294331337658SMarcel Moolenaar } 294431337658SMarcel Moolenaar 294531337658SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_data; 294631337658SMarcel Moolenaar int start = xbp->xb_curp - xbp->xb_bufp; 294731337658SMarcel Moolenaar int left = xbp->xb_size - start; 294831337658SMarcel Moolenaar int rc; 294931337658SMarcel Moolenaar int need_enc = XF_ENC_LOCALE; 295031337658SMarcel Moolenaar 2951788ca347SMarcel Moolenaar if (xo_style(xop) == XO_STYLE_HTML) { 295231337658SMarcel Moolenaar need_enc = XF_ENC_UTF8; 295331337658SMarcel Moolenaar xo_line_ensure_open(xop, 0); 295431337658SMarcel Moolenaar if (xop->xo_flags & XOF_PRETTY) 295531337658SMarcel Moolenaar xo_buf_indent(xop, xop->xo_indent_by); 295631337658SMarcel Moolenaar xo_buf_append(&xop->xo_data, div_open, sizeof(div_open) - 1); 2957788ca347SMarcel Moolenaar xo_color_append_html(xop); 2958788ca347SMarcel Moolenaar xo_buf_append(&xop->xo_data, div_middle, sizeof(div_middle) - 1); 295931337658SMarcel Moolenaar } 296031337658SMarcel Moolenaar 296131337658SMarcel Moolenaar start = xbp->xb_curp - xbp->xb_bufp; /* Reset start */ 296231337658SMarcel Moolenaar if (len) { 296331337658SMarcel Moolenaar char *newfmt = alloca(flen + 1); 296431337658SMarcel Moolenaar memcpy(newfmt, fmt, flen); 296531337658SMarcel Moolenaar newfmt[flen] = '\0'; 296631337658SMarcel Moolenaar 296731337658SMarcel Moolenaar /* If len is non-zero, the format string apply to the name */ 296831337658SMarcel Moolenaar char *newstr = alloca(len + 1); 296931337658SMarcel Moolenaar memcpy(newstr, str, len); 297031337658SMarcel Moolenaar newstr[len] = '\0'; 297131337658SMarcel Moolenaar 297231337658SMarcel Moolenaar if (newstr[len - 1] == 's') { 297331337658SMarcel Moolenaar int cols; 297431337658SMarcel Moolenaar char *bp; 297531337658SMarcel Moolenaar 297631337658SMarcel Moolenaar rc = snprintf(NULL, 0, newfmt, newstr); 297731337658SMarcel Moolenaar if (rc > 0) { 297831337658SMarcel Moolenaar /* 297931337658SMarcel Moolenaar * We have to do this the hard way, since we might need 298031337658SMarcel Moolenaar * the columns. 298131337658SMarcel Moolenaar */ 298231337658SMarcel Moolenaar bp = alloca(rc + 1); 298331337658SMarcel Moolenaar rc = snprintf(bp, rc + 1, newfmt, newstr); 298431337658SMarcel Moolenaar cols = xo_format_string_direct(xop, xbp, 0, NULL, bp, rc, -1, 298531337658SMarcel Moolenaar need_enc, XF_ENC_UTF8); 298631337658SMarcel Moolenaar if (cols > 0) { 298731337658SMarcel Moolenaar if (xop->xo_flags & XOF_COLUMNS) 298831337658SMarcel Moolenaar xop->xo_columns += cols; 298931337658SMarcel Moolenaar if (xop->xo_flags & XOF_ANCHOR) 299031337658SMarcel Moolenaar xop->xo_anchor_columns += cols; 299131337658SMarcel Moolenaar } 299231337658SMarcel Moolenaar } 299331337658SMarcel Moolenaar goto move_along; 299431337658SMarcel Moolenaar 299531337658SMarcel Moolenaar } else { 299631337658SMarcel Moolenaar rc = snprintf(xbp->xb_curp, left, newfmt, newstr); 299731337658SMarcel Moolenaar if (rc > left) { 299831337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, rc)) 299931337658SMarcel Moolenaar return; 300031337658SMarcel Moolenaar left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp); 300131337658SMarcel Moolenaar rc = snprintf(xbp->xb_curp, left, newfmt, newstr); 300231337658SMarcel Moolenaar } 300331337658SMarcel Moolenaar 300431337658SMarcel Moolenaar if (rc > 0) { 300531337658SMarcel Moolenaar if (xop->xo_flags & XOF_COLUMNS) 300631337658SMarcel Moolenaar xop->xo_columns += rc; 300731337658SMarcel Moolenaar if (xop->xo_flags & XOF_ANCHOR) 300831337658SMarcel Moolenaar xop->xo_anchor_columns += rc; 300931337658SMarcel Moolenaar } 301031337658SMarcel Moolenaar } 301131337658SMarcel Moolenaar 301231337658SMarcel Moolenaar } else { 301331337658SMarcel Moolenaar xo_format_data(xop, NULL, fmt, flen, 0); 301431337658SMarcel Moolenaar 301531337658SMarcel Moolenaar /* xo_format_data moved curp, so we need to reset it */ 301631337658SMarcel Moolenaar rc = xbp->xb_curp - (xbp->xb_bufp + start); 301731337658SMarcel Moolenaar xbp->xb_curp = xbp->xb_bufp + start; 301831337658SMarcel Moolenaar } 301931337658SMarcel Moolenaar 302031337658SMarcel Moolenaar /* If we're styling HTML, then we need to escape it */ 3021788ca347SMarcel Moolenaar if (xo_style(xop) == XO_STYLE_HTML) { 302231337658SMarcel Moolenaar rc = xo_escape_xml(xbp, rc, 0); 302331337658SMarcel Moolenaar } 302431337658SMarcel Moolenaar 302531337658SMarcel Moolenaar if (rc > 0) 302631337658SMarcel Moolenaar xbp->xb_curp += rc; 302731337658SMarcel Moolenaar 302831337658SMarcel Moolenaar move_along: 3029788ca347SMarcel Moolenaar if (xo_style(xop) == XO_STYLE_HTML) { 303031337658SMarcel Moolenaar xo_data_append(xop, div_close, sizeof(div_close) - 1); 303131337658SMarcel Moolenaar if (xop->xo_flags & XOF_PRETTY) 303231337658SMarcel Moolenaar xo_data_append(xop, "\n", 1); 303331337658SMarcel Moolenaar } 303431337658SMarcel Moolenaar } 303531337658SMarcel Moolenaar 303631337658SMarcel Moolenaar static void 303731337658SMarcel Moolenaar xo_format_prep (xo_handle_t *xop, xo_xff_flags_t flags) 303831337658SMarcel Moolenaar { 303931337658SMarcel Moolenaar if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST) { 304031337658SMarcel Moolenaar xo_data_append(xop, ",", 1); 304131337658SMarcel Moolenaar if (!(flags & XFF_LEAF_LIST) && (xop->xo_flags & XOF_PRETTY)) 304231337658SMarcel Moolenaar xo_data_append(xop, "\n", 1); 304331337658SMarcel Moolenaar } else 304431337658SMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 304531337658SMarcel Moolenaar } 304631337658SMarcel Moolenaar 304731337658SMarcel Moolenaar #if 0 304831337658SMarcel Moolenaar /* Useful debugging function */ 304931337658SMarcel Moolenaar void 305031337658SMarcel Moolenaar xo_arg (xo_handle_t *xop); 305131337658SMarcel Moolenaar void 305231337658SMarcel Moolenaar xo_arg (xo_handle_t *xop) 305331337658SMarcel Moolenaar { 305431337658SMarcel Moolenaar xop = xo_default(xop); 305531337658SMarcel Moolenaar fprintf(stderr, "0x%x", va_arg(xop->xo_vap, unsigned)); 305631337658SMarcel Moolenaar } 305731337658SMarcel Moolenaar #endif /* 0 */ 305831337658SMarcel Moolenaar 305931337658SMarcel Moolenaar static void 306031337658SMarcel Moolenaar xo_format_value (xo_handle_t *xop, const char *name, int nlen, 306131337658SMarcel Moolenaar const char *format, int flen, 306231337658SMarcel Moolenaar const char *encoding, int elen, xo_xff_flags_t flags) 306331337658SMarcel Moolenaar { 306431337658SMarcel Moolenaar int pretty = (xop->xo_flags & XOF_PRETTY); 306531337658SMarcel Moolenaar int quote; 306631337658SMarcel Moolenaar xo_buffer_t *xbp; 306731337658SMarcel Moolenaar 3068545ddfbeSMarcel Moolenaar /* 3069545ddfbeSMarcel Moolenaar * Before we emit a value, we need to know that the frame is ready. 3070545ddfbeSMarcel Moolenaar */ 3071545ddfbeSMarcel Moolenaar xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth]; 3072545ddfbeSMarcel Moolenaar 3073545ddfbeSMarcel Moolenaar if (flags & XFF_LEAF_LIST) { 3074545ddfbeSMarcel Moolenaar /* 3075545ddfbeSMarcel Moolenaar * Check if we've already started to emit normal leafs 3076545ddfbeSMarcel Moolenaar * or if we're not in a leaf list. 3077545ddfbeSMarcel Moolenaar */ 3078545ddfbeSMarcel Moolenaar if ((xsp->xs_flags & (XSF_EMIT | XSF_EMIT_KEY)) 3079545ddfbeSMarcel Moolenaar || !(xsp->xs_flags & XSF_EMIT_LEAF_LIST)) { 3080545ddfbeSMarcel Moolenaar char nbuf[nlen + 1]; 3081545ddfbeSMarcel Moolenaar memcpy(nbuf, name, nlen); 3082545ddfbeSMarcel Moolenaar nbuf[nlen] = '\0'; 3083545ddfbeSMarcel Moolenaar 3084545ddfbeSMarcel Moolenaar int rc = xo_transition(xop, 0, nbuf, XSS_EMIT_LEAF_LIST); 3085545ddfbeSMarcel Moolenaar if (rc < 0) 3086545ddfbeSMarcel Moolenaar flags |= XFF_DISPLAY_ONLY | XFF_ENCODE_ONLY; 3087545ddfbeSMarcel Moolenaar else 3088545ddfbeSMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_EMIT_LEAF_LIST; 3089545ddfbeSMarcel Moolenaar } 3090545ddfbeSMarcel Moolenaar 3091545ddfbeSMarcel Moolenaar xsp = &xop->xo_stack[xop->xo_depth]; 3092545ddfbeSMarcel Moolenaar if (xsp->xs_name) { 3093545ddfbeSMarcel Moolenaar name = xsp->xs_name; 3094545ddfbeSMarcel Moolenaar nlen = strlen(name); 3095545ddfbeSMarcel Moolenaar } 3096545ddfbeSMarcel Moolenaar 3097545ddfbeSMarcel Moolenaar } else if (flags & XFF_KEY) { 3098545ddfbeSMarcel Moolenaar /* Emitting a 'k' (key) field */ 3099545ddfbeSMarcel Moolenaar if ((xsp->xs_flags & XSF_EMIT) && !(flags & XFF_DISPLAY_ONLY)) { 3100545ddfbeSMarcel Moolenaar xo_failure(xop, "key field emitted after normal value field: '%.*s'", 3101545ddfbeSMarcel Moolenaar nlen, name); 3102545ddfbeSMarcel Moolenaar 3103545ddfbeSMarcel Moolenaar } else if (!(xsp->xs_flags & XSF_EMIT_KEY)) { 3104545ddfbeSMarcel Moolenaar char nbuf[nlen + 1]; 3105545ddfbeSMarcel Moolenaar memcpy(nbuf, name, nlen); 3106545ddfbeSMarcel Moolenaar nbuf[nlen] = '\0'; 3107545ddfbeSMarcel Moolenaar 3108545ddfbeSMarcel Moolenaar int rc = xo_transition(xop, 0, nbuf, XSS_EMIT); 3109545ddfbeSMarcel Moolenaar if (rc < 0) 3110545ddfbeSMarcel Moolenaar flags |= XFF_DISPLAY_ONLY | XFF_ENCODE_ONLY; 3111545ddfbeSMarcel Moolenaar else 3112545ddfbeSMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_EMIT_KEY; 3113545ddfbeSMarcel Moolenaar 3114545ddfbeSMarcel Moolenaar xsp = &xop->xo_stack[xop->xo_depth]; 3115545ddfbeSMarcel Moolenaar xsp->xs_flags |= XSF_EMIT_KEY; 3116545ddfbeSMarcel Moolenaar } 3117545ddfbeSMarcel Moolenaar 3118545ddfbeSMarcel Moolenaar } else { 3119545ddfbeSMarcel Moolenaar /* Emitting a normal value field */ 3120545ddfbeSMarcel Moolenaar if ((xsp->xs_flags & XSF_EMIT_LEAF_LIST) 3121545ddfbeSMarcel Moolenaar || !(xsp->xs_flags & XSF_EMIT)) { 3122545ddfbeSMarcel Moolenaar char nbuf[nlen + 1]; 3123545ddfbeSMarcel Moolenaar memcpy(nbuf, name, nlen); 3124545ddfbeSMarcel Moolenaar nbuf[nlen] = '\0'; 3125545ddfbeSMarcel Moolenaar 3126545ddfbeSMarcel Moolenaar int rc = xo_transition(xop, 0, nbuf, XSS_EMIT); 3127545ddfbeSMarcel Moolenaar if (rc < 0) 3128545ddfbeSMarcel Moolenaar flags |= XFF_DISPLAY_ONLY | XFF_ENCODE_ONLY; 3129545ddfbeSMarcel Moolenaar else 3130545ddfbeSMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_EMIT; 3131545ddfbeSMarcel Moolenaar 3132545ddfbeSMarcel Moolenaar xsp = &xop->xo_stack[xop->xo_depth]; 3133545ddfbeSMarcel Moolenaar xsp->xs_flags |= XSF_EMIT; 3134545ddfbeSMarcel Moolenaar } 3135545ddfbeSMarcel Moolenaar } 3136545ddfbeSMarcel Moolenaar 3137788ca347SMarcel Moolenaar switch (xo_style(xop)) { 313831337658SMarcel Moolenaar case XO_STYLE_TEXT: 313931337658SMarcel Moolenaar if (flags & XFF_ENCODE_ONLY) 314031337658SMarcel Moolenaar flags |= XFF_NO_OUTPUT; 314131337658SMarcel Moolenaar xo_format_data(xop, NULL, format, flen, flags); 314231337658SMarcel Moolenaar break; 314331337658SMarcel Moolenaar 314431337658SMarcel Moolenaar case XO_STYLE_HTML: 314531337658SMarcel Moolenaar if (flags & XFF_ENCODE_ONLY) 314631337658SMarcel Moolenaar flags |= XFF_NO_OUTPUT; 314731337658SMarcel Moolenaar xo_buf_append_div(xop, "data", flags, name, nlen, 314831337658SMarcel Moolenaar format, flen, encoding, elen); 314931337658SMarcel Moolenaar break; 315031337658SMarcel Moolenaar 315131337658SMarcel Moolenaar case XO_STYLE_XML: 315231337658SMarcel Moolenaar /* 315331337658SMarcel Moolenaar * Even though we're not making output, we still need to 315431337658SMarcel Moolenaar * let the formatting code handle the va_arg popping. 315531337658SMarcel Moolenaar */ 315631337658SMarcel Moolenaar if (flags & XFF_DISPLAY_ONLY) { 315731337658SMarcel Moolenaar flags |= XFF_NO_OUTPUT; 315831337658SMarcel Moolenaar xo_format_data(xop, NULL, format, flen, flags); 315931337658SMarcel Moolenaar break; 316031337658SMarcel Moolenaar } 316131337658SMarcel Moolenaar 316231337658SMarcel Moolenaar if (encoding) { 316331337658SMarcel Moolenaar format = encoding; 316431337658SMarcel Moolenaar flen = elen; 316531337658SMarcel Moolenaar } else { 316631337658SMarcel Moolenaar char *enc = alloca(flen + 1); 316731337658SMarcel Moolenaar memcpy(enc, format, flen); 316831337658SMarcel Moolenaar enc[flen] = '\0'; 316931337658SMarcel Moolenaar format = xo_fix_encoding(xop, enc); 317031337658SMarcel Moolenaar flen = strlen(format); 317131337658SMarcel Moolenaar } 317231337658SMarcel Moolenaar 317331337658SMarcel Moolenaar if (nlen == 0) { 317431337658SMarcel Moolenaar static char missing[] = "missing-field-name"; 317531337658SMarcel Moolenaar xo_failure(xop, "missing field name: %s", format); 317631337658SMarcel Moolenaar name = missing; 317731337658SMarcel Moolenaar nlen = sizeof(missing) - 1; 317831337658SMarcel Moolenaar } 317931337658SMarcel Moolenaar 318031337658SMarcel Moolenaar if (pretty) 318131337658SMarcel Moolenaar xo_buf_indent(xop, -1); 318231337658SMarcel Moolenaar xo_data_append(xop, "<", 1); 318331337658SMarcel Moolenaar xo_data_escape(xop, name, nlen); 318431337658SMarcel Moolenaar 318531337658SMarcel Moolenaar if (xop->xo_attrs.xb_curp != xop->xo_attrs.xb_bufp) { 318631337658SMarcel Moolenaar xo_data_append(xop, xop->xo_attrs.xb_bufp, 318731337658SMarcel Moolenaar xop->xo_attrs.xb_curp - xop->xo_attrs.xb_bufp); 318831337658SMarcel Moolenaar xop->xo_attrs.xb_curp = xop->xo_attrs.xb_bufp; 318931337658SMarcel Moolenaar } 319031337658SMarcel Moolenaar 319131337658SMarcel Moolenaar /* 319231337658SMarcel Moolenaar * We indicate 'key' fields using the 'key' attribute. While 319331337658SMarcel Moolenaar * this is really committing the crime of mixing meta-data with 319431337658SMarcel Moolenaar * data, it's often useful. Especially when format meta-data is 319531337658SMarcel Moolenaar * difficult to come by. 319631337658SMarcel Moolenaar */ 319731337658SMarcel Moolenaar if ((flags & XFF_KEY) && (xop->xo_flags & XOF_KEYS)) { 319831337658SMarcel Moolenaar static char attr[] = " key=\"key\""; 319931337658SMarcel Moolenaar xo_data_append(xop, attr, sizeof(attr) - 1); 320031337658SMarcel Moolenaar } 320131337658SMarcel Moolenaar 320231337658SMarcel Moolenaar /* 320331337658SMarcel Moolenaar * Save the offset at which we'd place units. See xo_format_units. 320431337658SMarcel Moolenaar */ 320531337658SMarcel Moolenaar if (xop->xo_flags & XOF_UNITS) { 320631337658SMarcel Moolenaar xop->xo_flags |= XOF_UNITS_PENDING; 320731337658SMarcel Moolenaar xop->xo_units_offset = xop->xo_data.xb_curp -xop->xo_data.xb_bufp; 320831337658SMarcel Moolenaar } 320931337658SMarcel Moolenaar 321031337658SMarcel Moolenaar xo_data_append(xop, ">", 1); 321131337658SMarcel Moolenaar xo_format_data(xop, NULL, format, flen, flags); 321231337658SMarcel Moolenaar xo_data_append(xop, "</", 2); 321331337658SMarcel Moolenaar xo_data_escape(xop, name, nlen); 321431337658SMarcel Moolenaar xo_data_append(xop, ">", 1); 321531337658SMarcel Moolenaar if (pretty) 321631337658SMarcel Moolenaar xo_data_append(xop, "\n", 1); 321731337658SMarcel Moolenaar break; 321831337658SMarcel Moolenaar 321931337658SMarcel Moolenaar case XO_STYLE_JSON: 322031337658SMarcel Moolenaar if (flags & XFF_DISPLAY_ONLY) { 322131337658SMarcel Moolenaar flags |= XFF_NO_OUTPUT; 322231337658SMarcel Moolenaar xo_format_data(xop, NULL, format, flen, flags); 322331337658SMarcel Moolenaar break; 322431337658SMarcel Moolenaar } 322531337658SMarcel Moolenaar 322631337658SMarcel Moolenaar if (encoding) { 322731337658SMarcel Moolenaar format = encoding; 322831337658SMarcel Moolenaar flen = elen; 322931337658SMarcel Moolenaar } else { 323031337658SMarcel Moolenaar char *enc = alloca(flen + 1); 323131337658SMarcel Moolenaar memcpy(enc, format, flen); 323231337658SMarcel Moolenaar enc[flen] = '\0'; 323331337658SMarcel Moolenaar format = xo_fix_encoding(xop, enc); 323431337658SMarcel Moolenaar flen = strlen(format); 323531337658SMarcel Moolenaar } 323631337658SMarcel Moolenaar 323731337658SMarcel Moolenaar int first = !(xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST); 323831337658SMarcel Moolenaar 323931337658SMarcel Moolenaar xo_format_prep(xop, flags); 324031337658SMarcel Moolenaar 324131337658SMarcel Moolenaar if (flags & XFF_QUOTE) 324231337658SMarcel Moolenaar quote = 1; 324331337658SMarcel Moolenaar else if (flags & XFF_NOQUOTE) 324431337658SMarcel Moolenaar quote = 0; 324531337658SMarcel Moolenaar else if (flen == 0) { 324631337658SMarcel Moolenaar quote = 0; 324731337658SMarcel Moolenaar format = "true"; /* JSON encodes empty tags as a boolean true */ 324831337658SMarcel Moolenaar flen = 4; 324931337658SMarcel Moolenaar } else if (strchr("diouxXDOUeEfFgGaAcCp", format[flen - 1]) == NULL) 325031337658SMarcel Moolenaar quote = 1; 325131337658SMarcel Moolenaar else 325231337658SMarcel Moolenaar quote = 0; 325331337658SMarcel Moolenaar 325431337658SMarcel Moolenaar if (nlen == 0) { 325531337658SMarcel Moolenaar static char missing[] = "missing-field-name"; 325631337658SMarcel Moolenaar xo_failure(xop, "missing field name: %s", format); 325731337658SMarcel Moolenaar name = missing; 325831337658SMarcel Moolenaar nlen = sizeof(missing) - 1; 325931337658SMarcel Moolenaar } 326031337658SMarcel Moolenaar 326131337658SMarcel Moolenaar if (flags & XFF_LEAF_LIST) { 3262788ca347SMarcel Moolenaar if (!first && pretty) 3263788ca347SMarcel Moolenaar xo_data_append(xop, "\n", 1); 3264788ca347SMarcel Moolenaar if (pretty) 326531337658SMarcel Moolenaar xo_buf_indent(xop, -1); 326631337658SMarcel Moolenaar } else { 326731337658SMarcel Moolenaar if (pretty) 326831337658SMarcel Moolenaar xo_buf_indent(xop, -1); 326931337658SMarcel Moolenaar xo_data_append(xop, "\"", 1); 327031337658SMarcel Moolenaar 327131337658SMarcel Moolenaar xbp = &xop->xo_data; 327231337658SMarcel Moolenaar int off = xbp->xb_curp - xbp->xb_bufp; 327331337658SMarcel Moolenaar 327431337658SMarcel Moolenaar xo_data_escape(xop, name, nlen); 327531337658SMarcel Moolenaar 327631337658SMarcel Moolenaar if (xop->xo_flags & XOF_UNDERSCORES) { 327731337658SMarcel Moolenaar int now = xbp->xb_curp - xbp->xb_bufp; 327831337658SMarcel Moolenaar for ( ; off < now; off++) 327931337658SMarcel Moolenaar if (xbp->xb_bufp[off] == '-') 328031337658SMarcel Moolenaar xbp->xb_bufp[off] = '_'; 328131337658SMarcel Moolenaar } 328231337658SMarcel Moolenaar xo_data_append(xop, "\":", 2); 328331337658SMarcel Moolenaar if (pretty) 328431337658SMarcel Moolenaar xo_data_append(xop, " ", 1); 3285788ca347SMarcel Moolenaar } 3286788ca347SMarcel Moolenaar 328731337658SMarcel Moolenaar if (quote) 328831337658SMarcel Moolenaar xo_data_append(xop, "\"", 1); 328931337658SMarcel Moolenaar 329031337658SMarcel Moolenaar xo_format_data(xop, NULL, format, flen, flags); 329131337658SMarcel Moolenaar 329231337658SMarcel Moolenaar if (quote) 329331337658SMarcel Moolenaar xo_data_append(xop, "\"", 1); 329431337658SMarcel Moolenaar break; 329531337658SMarcel Moolenaar } 329631337658SMarcel Moolenaar } 329731337658SMarcel Moolenaar 329831337658SMarcel Moolenaar static void 329931337658SMarcel Moolenaar xo_format_content (xo_handle_t *xop, const char *class_name, 330031337658SMarcel Moolenaar const char *xml_tag, int display_only, 330131337658SMarcel Moolenaar const char *str, int len, const char *fmt, int flen) 330231337658SMarcel Moolenaar { 3303788ca347SMarcel Moolenaar switch (xo_style(xop)) { 330431337658SMarcel Moolenaar case XO_STYLE_TEXT: 330531337658SMarcel Moolenaar if (len) { 330631337658SMarcel Moolenaar xo_data_append_content(xop, str, len); 330731337658SMarcel Moolenaar } else 330831337658SMarcel Moolenaar xo_format_data(xop, NULL, fmt, flen, 0); 330931337658SMarcel Moolenaar break; 331031337658SMarcel Moolenaar 331131337658SMarcel Moolenaar case XO_STYLE_HTML: 331231337658SMarcel Moolenaar if (len == 0) { 331331337658SMarcel Moolenaar str = fmt; 331431337658SMarcel Moolenaar len = flen; 331531337658SMarcel Moolenaar } 331631337658SMarcel Moolenaar 331731337658SMarcel Moolenaar xo_buf_append_div(xop, class_name, 0, NULL, 0, str, len, NULL, 0); 331831337658SMarcel Moolenaar break; 331931337658SMarcel Moolenaar 332031337658SMarcel Moolenaar case XO_STYLE_XML: 332131337658SMarcel Moolenaar if (xml_tag) { 332231337658SMarcel Moolenaar if (len == 0) { 332331337658SMarcel Moolenaar str = fmt; 332431337658SMarcel Moolenaar len = flen; 332531337658SMarcel Moolenaar } 332631337658SMarcel Moolenaar 332731337658SMarcel Moolenaar xo_open_container_h(xop, xml_tag); 332831337658SMarcel Moolenaar xo_format_value(xop, "message", 7, str, len, NULL, 0, 0); 332931337658SMarcel Moolenaar xo_close_container_h(xop, xml_tag); 333031337658SMarcel Moolenaar 333131337658SMarcel Moolenaar } else { 333231337658SMarcel Moolenaar /* 333331337658SMarcel Moolenaar * Even though we don't care about labels, we need to do 333431337658SMarcel Moolenaar * enough parsing work to skip over the right bits of xo_vap. 333531337658SMarcel Moolenaar */ 333631337658SMarcel Moolenaar if (len == 0) 333731337658SMarcel Moolenaar xo_format_data(xop, NULL, fmt, flen, XFF_NO_OUTPUT); 333831337658SMarcel Moolenaar } 333931337658SMarcel Moolenaar break; 334031337658SMarcel Moolenaar 334131337658SMarcel Moolenaar case XO_STYLE_JSON: 334231337658SMarcel Moolenaar /* 334331337658SMarcel Moolenaar * Even though we don't care about labels, we need to do 334431337658SMarcel Moolenaar * enough parsing work to skip over the right bits of xo_vap. 334531337658SMarcel Moolenaar */ 334631337658SMarcel Moolenaar if (display_only) { 334731337658SMarcel Moolenaar if (len == 0) 334831337658SMarcel Moolenaar xo_format_data(xop, NULL, fmt, flen, XFF_NO_OUTPUT); 334931337658SMarcel Moolenaar break; 335031337658SMarcel Moolenaar } 335131337658SMarcel Moolenaar /* XXX need schem for representing errors in JSON */ 335231337658SMarcel Moolenaar break; 335331337658SMarcel Moolenaar } 335431337658SMarcel Moolenaar } 335531337658SMarcel Moolenaar 3356788ca347SMarcel Moolenaar static const char *xo_color_names[] = { 3357788ca347SMarcel Moolenaar "default", /* XO_COL_DEFAULT */ 3358788ca347SMarcel Moolenaar "black", /* XO_COL_BLACK */ 3359788ca347SMarcel Moolenaar "red", /* XO_CLOR_RED */ 3360788ca347SMarcel Moolenaar "green", /* XO_COL_GREEN */ 3361788ca347SMarcel Moolenaar "yellow", /* XO_COL_YELLOW */ 3362788ca347SMarcel Moolenaar "blue", /* XO_COL_BLUE */ 3363788ca347SMarcel Moolenaar "magenta", /* XO_COL_MAGENTA */ 3364788ca347SMarcel Moolenaar "cyan", /* XO_COL_CYAN */ 3365788ca347SMarcel Moolenaar "white", /* XO_COL_WHITE */ 3366788ca347SMarcel Moolenaar NULL 3367788ca347SMarcel Moolenaar }; 3368788ca347SMarcel Moolenaar 3369788ca347SMarcel Moolenaar static int 3370788ca347SMarcel Moolenaar xo_color_find (const char *str) 3371788ca347SMarcel Moolenaar { 3372788ca347SMarcel Moolenaar int i; 3373788ca347SMarcel Moolenaar 3374788ca347SMarcel Moolenaar for (i = 0; xo_color_names[i]; i++) { 3375788ca347SMarcel Moolenaar if (strcmp(xo_color_names[i], str) == 0) 3376788ca347SMarcel Moolenaar return i; 3377788ca347SMarcel Moolenaar } 3378788ca347SMarcel Moolenaar 3379788ca347SMarcel Moolenaar return -1; 3380788ca347SMarcel Moolenaar } 3381788ca347SMarcel Moolenaar 3382788ca347SMarcel Moolenaar static const char *xo_effect_names[] = { 3383788ca347SMarcel Moolenaar "reset", /* XO_EFF_RESET */ 3384788ca347SMarcel Moolenaar "normal", /* XO_EFF_NORMAL */ 3385788ca347SMarcel Moolenaar "bold", /* XO_EFF_BOLD */ 3386788ca347SMarcel Moolenaar "underline", /* XO_EFF_UNDERLINE */ 3387788ca347SMarcel Moolenaar "inverse", /* XO_EFF_INVERSE */ 3388788ca347SMarcel Moolenaar NULL 3389788ca347SMarcel Moolenaar }; 3390788ca347SMarcel Moolenaar 3391788ca347SMarcel Moolenaar static const char *xo_effect_on_codes[] = { 3392788ca347SMarcel Moolenaar "0", /* XO_EFF_RESET */ 3393788ca347SMarcel Moolenaar "0", /* XO_EFF_NORMAL */ 3394788ca347SMarcel Moolenaar "1", /* XO_EFF_BOLD */ 3395788ca347SMarcel Moolenaar "4", /* XO_EFF_UNDERLINE */ 3396788ca347SMarcel Moolenaar "7", /* XO_EFF_INVERSE */ 3397788ca347SMarcel Moolenaar NULL 3398788ca347SMarcel Moolenaar }; 3399788ca347SMarcel Moolenaar 3400788ca347SMarcel Moolenaar #if 0 3401788ca347SMarcel Moolenaar /* 3402788ca347SMarcel Moolenaar * See comment below re: joy of terminal standards. These can 3403788ca347SMarcel Moolenaar * be use by just adding: 3404788ca347SMarcel Moolenaar * if (newp->xoc_effects & bit) 3405788ca347SMarcel Moolenaar * code = xo_effect_on_codes[i]; 3406788ca347SMarcel Moolenaar * + else 3407788ca347SMarcel Moolenaar * + code = xo_effect_off_codes[i]; 3408788ca347SMarcel Moolenaar * in xo_color_handle_text. 3409788ca347SMarcel Moolenaar */ 3410788ca347SMarcel Moolenaar static const char *xo_effect_off_codes[] = { 3411788ca347SMarcel Moolenaar "0", /* XO_EFF_RESET */ 3412788ca347SMarcel Moolenaar "0", /* XO_EFF_NORMAL */ 3413788ca347SMarcel Moolenaar "21", /* XO_EFF_BOLD */ 3414788ca347SMarcel Moolenaar "24", /* XO_EFF_UNDERLINE */ 3415788ca347SMarcel Moolenaar "27", /* XO_EFF_INVERSE */ 3416788ca347SMarcel Moolenaar NULL 3417788ca347SMarcel Moolenaar }; 3418788ca347SMarcel Moolenaar #endif /* 0 */ 3419788ca347SMarcel Moolenaar 3420788ca347SMarcel Moolenaar static int 3421788ca347SMarcel Moolenaar xo_effect_find (const char *str) 3422788ca347SMarcel Moolenaar { 3423788ca347SMarcel Moolenaar int i; 3424788ca347SMarcel Moolenaar 3425788ca347SMarcel Moolenaar for (i = 0; xo_effect_names[i]; i++) { 3426788ca347SMarcel Moolenaar if (strcmp(xo_effect_names[i], str) == 0) 3427788ca347SMarcel Moolenaar return i; 3428788ca347SMarcel Moolenaar } 3429788ca347SMarcel Moolenaar 3430788ca347SMarcel Moolenaar return -1; 3431788ca347SMarcel Moolenaar } 3432788ca347SMarcel Moolenaar 3433788ca347SMarcel Moolenaar static void 3434788ca347SMarcel Moolenaar xo_colors_parse (xo_handle_t *xop, xo_colors_t *xocp, char *str) 3435788ca347SMarcel Moolenaar { 3436788ca347SMarcel Moolenaar #ifdef LIBXO_TEXT_ONLY 3437788ca347SMarcel Moolenaar return; 3438788ca347SMarcel Moolenaar #endif /* LIBXO_TEXT_ONLY */ 3439788ca347SMarcel Moolenaar 3440788ca347SMarcel Moolenaar char *cp, *ep, *np, *xp; 3441788ca347SMarcel Moolenaar int len = strlen(str); 3442788ca347SMarcel Moolenaar int rc; 3443788ca347SMarcel Moolenaar 3444788ca347SMarcel Moolenaar /* 3445788ca347SMarcel Moolenaar * Possible tokens: colors, bg-colors, effects, no-effects, "reset". 3446788ca347SMarcel Moolenaar */ 3447788ca347SMarcel Moolenaar for (cp = str, ep = cp + len - 1; cp && cp < ep; cp = np) { 3448788ca347SMarcel Moolenaar /* Trim leading whitespace */ 3449788ca347SMarcel Moolenaar while (isspace((int) *cp)) 3450788ca347SMarcel Moolenaar cp += 1; 3451788ca347SMarcel Moolenaar 3452788ca347SMarcel Moolenaar np = strchr(cp, ','); 3453788ca347SMarcel Moolenaar if (np) 3454788ca347SMarcel Moolenaar *np++ = '\0'; 3455788ca347SMarcel Moolenaar 3456788ca347SMarcel Moolenaar /* Trim trailing whitespace */ 3457788ca347SMarcel Moolenaar xp = cp + strlen(cp) - 1; 3458788ca347SMarcel Moolenaar while (isspace(*xp) && xp > cp) 3459788ca347SMarcel Moolenaar *xp-- = '\0'; 3460788ca347SMarcel Moolenaar 3461788ca347SMarcel Moolenaar if (cp[0] == 'f' && cp[1] == 'g' && cp[2] == '-') { 3462788ca347SMarcel Moolenaar rc = xo_color_find(cp + 3); 3463788ca347SMarcel Moolenaar if (rc < 0) 3464788ca347SMarcel Moolenaar goto unknown; 3465788ca347SMarcel Moolenaar 3466788ca347SMarcel Moolenaar xocp->xoc_col_fg = rc; 3467788ca347SMarcel Moolenaar 3468788ca347SMarcel Moolenaar } else if (cp[0] == 'b' && cp[1] == 'g' && cp[2] == '-') { 3469788ca347SMarcel Moolenaar rc = xo_color_find(cp + 3); 3470788ca347SMarcel Moolenaar if (rc < 0) 3471788ca347SMarcel Moolenaar goto unknown; 3472788ca347SMarcel Moolenaar xocp->xoc_col_bg = rc; 3473788ca347SMarcel Moolenaar 3474788ca347SMarcel Moolenaar } else if (cp[0] == 'n' && cp[1] == 'o' && cp[2] == '-') { 3475788ca347SMarcel Moolenaar rc = xo_effect_find(cp + 3); 3476788ca347SMarcel Moolenaar if (rc < 0) 3477788ca347SMarcel Moolenaar goto unknown; 3478788ca347SMarcel Moolenaar xocp->xoc_effects &= ~(1 << rc); 3479788ca347SMarcel Moolenaar 3480788ca347SMarcel Moolenaar } else { 3481788ca347SMarcel Moolenaar rc = xo_effect_find(cp); 3482788ca347SMarcel Moolenaar if (rc < 0) 3483788ca347SMarcel Moolenaar goto unknown; 3484788ca347SMarcel Moolenaar xocp->xoc_effects |= 1 << rc; 3485788ca347SMarcel Moolenaar 3486788ca347SMarcel Moolenaar switch (1 << rc) { 3487788ca347SMarcel Moolenaar case XO_EFF_RESET: 3488788ca347SMarcel Moolenaar xocp->xoc_col_fg = xocp->xoc_col_bg = 0; 3489788ca347SMarcel Moolenaar /* Note: not "|=" since we want to wipe out the old value */ 3490788ca347SMarcel Moolenaar xocp->xoc_effects = XO_EFF_RESET; 3491788ca347SMarcel Moolenaar break; 3492788ca347SMarcel Moolenaar 3493788ca347SMarcel Moolenaar case XO_EFF_NORMAL: 3494788ca347SMarcel Moolenaar xocp->xoc_effects &= ~(XO_EFF_BOLD | XO_EFF_UNDERLINE 3495788ca347SMarcel Moolenaar | XO_EFF_INVERSE | XO_EFF_NORMAL); 3496788ca347SMarcel Moolenaar break; 3497788ca347SMarcel Moolenaar } 3498788ca347SMarcel Moolenaar } 3499788ca347SMarcel Moolenaar continue; 3500788ca347SMarcel Moolenaar 3501788ca347SMarcel Moolenaar unknown: 3502788ca347SMarcel Moolenaar if (xop->xo_flags & XOF_WARN) 3503788ca347SMarcel Moolenaar xo_failure(xop, "unknown color/effect string detected: '%s'", cp); 3504788ca347SMarcel Moolenaar } 3505788ca347SMarcel Moolenaar } 3506788ca347SMarcel Moolenaar 3507788ca347SMarcel Moolenaar static inline int 3508788ca347SMarcel Moolenaar xo_colors_enabled (xo_handle_t *xop UNUSED) 3509788ca347SMarcel Moolenaar { 3510788ca347SMarcel Moolenaar #ifdef LIBXO_TEXT_ONLY 3511788ca347SMarcel Moolenaar return 0; 3512788ca347SMarcel Moolenaar #else /* LIBXO_TEXT_ONLY */ 3513788ca347SMarcel Moolenaar return ((xop->xo_flags & XOF_COLOR) ? 1 : 0); 3514788ca347SMarcel Moolenaar #endif /* LIBXO_TEXT_ONLY */ 3515788ca347SMarcel Moolenaar } 3516788ca347SMarcel Moolenaar 3517788ca347SMarcel Moolenaar static void 3518788ca347SMarcel Moolenaar xo_colors_handle_text (xo_handle_t *xop UNUSED, xo_colors_t *newp) 3519788ca347SMarcel Moolenaar { 3520788ca347SMarcel Moolenaar char buf[BUFSIZ]; 3521788ca347SMarcel Moolenaar char *cp = buf, *ep = buf + sizeof(buf); 3522788ca347SMarcel Moolenaar unsigned i, bit; 3523788ca347SMarcel Moolenaar xo_colors_t *oldp = &xop->xo_colors; 35243865ad3fSMarcel Moolenaar const char *code = NULL; 3525788ca347SMarcel Moolenaar 3526788ca347SMarcel Moolenaar /* 3527788ca347SMarcel Moolenaar * Start the buffer with an escape. We don't want to add the '[' 3528788ca347SMarcel Moolenaar * now, since we let xo_effect_text_add unconditionally add the ';'. 3529788ca347SMarcel Moolenaar * We'll replace the first ';' with a '[' when we're done. 3530788ca347SMarcel Moolenaar */ 3531788ca347SMarcel Moolenaar *cp++ = 0x1b; /* Escape */ 3532788ca347SMarcel Moolenaar 3533788ca347SMarcel Moolenaar /* 3534788ca347SMarcel Moolenaar * Terminals were designed back in the age before "certainty" was 3535788ca347SMarcel Moolenaar * invented, when standards were more what you'd call "guidelines" 3536788ca347SMarcel Moolenaar * than actual rules. Anyway we can't depend on them to operate 3537788ca347SMarcel Moolenaar * correctly. So when display attributes are changed, we punt, 3538788ca347SMarcel Moolenaar * reseting them all and turning back on the ones we want to keep. 3539788ca347SMarcel Moolenaar * Longer, but should be completely reliable. Savvy? 3540788ca347SMarcel Moolenaar */ 3541788ca347SMarcel Moolenaar if (oldp->xoc_effects != (newp->xoc_effects & oldp->xoc_effects)) { 3542788ca347SMarcel Moolenaar newp->xoc_effects |= XO_EFF_RESET; 3543788ca347SMarcel Moolenaar oldp->xoc_effects = 0; 3544788ca347SMarcel Moolenaar } 3545788ca347SMarcel Moolenaar 3546788ca347SMarcel Moolenaar for (i = 0, bit = 1; xo_effect_names[i]; i++, bit <<= 1) { 3547788ca347SMarcel Moolenaar if ((newp->xoc_effects & bit) == (oldp->xoc_effects & bit)) 3548788ca347SMarcel Moolenaar continue; 3549788ca347SMarcel Moolenaar 3550788ca347SMarcel Moolenaar if (newp->xoc_effects & bit) 3551788ca347SMarcel Moolenaar code = xo_effect_on_codes[i]; 3552788ca347SMarcel Moolenaar 3553788ca347SMarcel Moolenaar cp += snprintf(cp, ep - cp, ";%s", code); 3554788ca347SMarcel Moolenaar if (cp >= ep) 3555788ca347SMarcel Moolenaar return; /* Should not occur */ 3556788ca347SMarcel Moolenaar 3557788ca347SMarcel Moolenaar if (bit == XO_EFF_RESET) { 3558788ca347SMarcel Moolenaar /* Mark up the old value so we can detect current values as new */ 3559788ca347SMarcel Moolenaar oldp->xoc_effects = 0; 3560788ca347SMarcel Moolenaar oldp->xoc_col_fg = oldp->xoc_col_bg = XO_COL_DEFAULT; 3561788ca347SMarcel Moolenaar } 3562788ca347SMarcel Moolenaar } 3563788ca347SMarcel Moolenaar 3564788ca347SMarcel Moolenaar if (newp->xoc_col_fg != oldp->xoc_col_fg) { 3565788ca347SMarcel Moolenaar cp += snprintf(cp, ep - cp, ";3%u", 3566788ca347SMarcel Moolenaar (newp->xoc_col_fg != XO_COL_DEFAULT) 3567788ca347SMarcel Moolenaar ? newp->xoc_col_fg - 1 : 9); 3568788ca347SMarcel Moolenaar } 3569788ca347SMarcel Moolenaar 3570788ca347SMarcel Moolenaar if (newp->xoc_col_bg != oldp->xoc_col_bg) { 3571788ca347SMarcel Moolenaar cp += snprintf(cp, ep - cp, ";4%u", 3572788ca347SMarcel Moolenaar (newp->xoc_col_bg != XO_COL_DEFAULT) 3573788ca347SMarcel Moolenaar ? newp->xoc_col_bg - 1 : 9); 3574788ca347SMarcel Moolenaar } 3575788ca347SMarcel Moolenaar 3576788ca347SMarcel Moolenaar if (cp - buf != 1 && cp < ep - 3) { 3577788ca347SMarcel Moolenaar buf[1] = '['; /* Overwrite leading ';' */ 3578788ca347SMarcel Moolenaar *cp++ = 'm'; 3579788ca347SMarcel Moolenaar *cp = '\0'; 3580788ca347SMarcel Moolenaar xo_buf_append(&xop->xo_data, buf, cp - buf); 3581788ca347SMarcel Moolenaar } 3582788ca347SMarcel Moolenaar } 3583788ca347SMarcel Moolenaar 3584788ca347SMarcel Moolenaar static void 3585788ca347SMarcel Moolenaar xo_colors_handle_html (xo_handle_t *xop, xo_colors_t *newp) 3586788ca347SMarcel Moolenaar { 3587788ca347SMarcel Moolenaar xo_colors_t *oldp = &xop->xo_colors; 3588788ca347SMarcel Moolenaar 3589788ca347SMarcel Moolenaar /* 3590788ca347SMarcel Moolenaar * HTML colors are mostly trivial: fill in xo_color_buf with 3591788ca347SMarcel Moolenaar * a set of class tags representing the colors and effects. 3592788ca347SMarcel Moolenaar */ 3593788ca347SMarcel Moolenaar 3594788ca347SMarcel Moolenaar /* If nothing changed, then do nothing */ 3595788ca347SMarcel Moolenaar if (oldp->xoc_effects == newp->xoc_effects 3596788ca347SMarcel Moolenaar && oldp->xoc_col_fg == newp->xoc_col_fg 3597788ca347SMarcel Moolenaar && oldp->xoc_col_bg == newp->xoc_col_bg) 3598788ca347SMarcel Moolenaar return; 3599788ca347SMarcel Moolenaar 3600788ca347SMarcel Moolenaar unsigned i, bit; 3601788ca347SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_color_buf; 3602788ca347SMarcel Moolenaar 3603788ca347SMarcel Moolenaar xo_buf_reset(xbp); /* We rebuild content after each change */ 3604788ca347SMarcel Moolenaar 3605788ca347SMarcel Moolenaar for (i = 0, bit = 1; xo_effect_names[i]; i++, bit <<= 1) { 3606788ca347SMarcel Moolenaar if (!(newp->xoc_effects & bit)) 3607788ca347SMarcel Moolenaar continue; 3608788ca347SMarcel Moolenaar 3609788ca347SMarcel Moolenaar xo_buf_append_str(xbp, " effect-"); 3610788ca347SMarcel Moolenaar xo_buf_append_str(xbp, xo_effect_names[i]); 3611788ca347SMarcel Moolenaar } 3612788ca347SMarcel Moolenaar 3613788ca347SMarcel Moolenaar const char *fg = NULL; 3614788ca347SMarcel Moolenaar const char *bg = NULL; 3615788ca347SMarcel Moolenaar 3616788ca347SMarcel Moolenaar if (newp->xoc_col_fg != XO_COL_DEFAULT) 3617788ca347SMarcel Moolenaar fg = xo_color_names[newp->xoc_col_fg]; 3618788ca347SMarcel Moolenaar if (newp->xoc_col_bg != XO_COL_DEFAULT) 3619788ca347SMarcel Moolenaar bg = xo_color_names[newp->xoc_col_bg]; 3620788ca347SMarcel Moolenaar 3621788ca347SMarcel Moolenaar if (newp->xoc_effects & XO_EFF_INVERSE) { 3622788ca347SMarcel Moolenaar const char *tmp = fg; 3623788ca347SMarcel Moolenaar fg = bg; 3624788ca347SMarcel Moolenaar bg = tmp; 3625788ca347SMarcel Moolenaar if (fg == NULL) 3626788ca347SMarcel Moolenaar fg = "inverse"; 3627788ca347SMarcel Moolenaar if (bg == NULL) 3628788ca347SMarcel Moolenaar bg = "inverse"; 3629788ca347SMarcel Moolenaar 3630788ca347SMarcel Moolenaar } 3631788ca347SMarcel Moolenaar 3632788ca347SMarcel Moolenaar if (fg) { 3633788ca347SMarcel Moolenaar xo_buf_append_str(xbp, " color-fg-"); 3634788ca347SMarcel Moolenaar xo_buf_append_str(xbp, fg); 3635788ca347SMarcel Moolenaar } 3636788ca347SMarcel Moolenaar 3637788ca347SMarcel Moolenaar if (bg) { 3638788ca347SMarcel Moolenaar xo_buf_append_str(xbp, " color-bg-"); 3639788ca347SMarcel Moolenaar xo_buf_append_str(xbp, bg); 3640788ca347SMarcel Moolenaar } 3641788ca347SMarcel Moolenaar } 3642788ca347SMarcel Moolenaar 3643788ca347SMarcel Moolenaar static void 3644788ca347SMarcel Moolenaar xo_format_colors (xo_handle_t *xop, const char *str, int len, 3645788ca347SMarcel Moolenaar const char *fmt, int flen) 3646788ca347SMarcel Moolenaar { 3647788ca347SMarcel Moolenaar xo_buffer_t xb; 3648788ca347SMarcel Moolenaar 3649788ca347SMarcel Moolenaar /* If the string is static and we've in an encoding style, bail */ 3650788ca347SMarcel Moolenaar if (len != 0 3651788ca347SMarcel Moolenaar && (xo_style(xop) == XO_STYLE_XML || xo_style(xop) == XO_STYLE_JSON)) 3652788ca347SMarcel Moolenaar return; 3653788ca347SMarcel Moolenaar 3654788ca347SMarcel Moolenaar xo_buf_init(&xb); 3655788ca347SMarcel Moolenaar 3656788ca347SMarcel Moolenaar if (len) 3657788ca347SMarcel Moolenaar xo_buf_append(&xb, str, len); 3658788ca347SMarcel Moolenaar else if (flen) 3659788ca347SMarcel Moolenaar xo_format_data(xop, &xb, fmt, flen, 0); 3660788ca347SMarcel Moolenaar else 3661788ca347SMarcel Moolenaar xo_buf_append(&xb, "reset", 6); /* Default if empty */ 3662788ca347SMarcel Moolenaar 3663788ca347SMarcel Moolenaar if (xo_colors_enabled(xop)) { 3664788ca347SMarcel Moolenaar switch (xo_style(xop)) { 3665788ca347SMarcel Moolenaar case XO_STYLE_TEXT: 3666788ca347SMarcel Moolenaar case XO_STYLE_HTML: 3667788ca347SMarcel Moolenaar xo_buf_append(&xb, "", 1); 3668788ca347SMarcel Moolenaar 3669788ca347SMarcel Moolenaar xo_colors_t xoc = xop->xo_colors; 3670788ca347SMarcel Moolenaar xo_colors_parse(xop, &xoc, xb.xb_bufp); 3671788ca347SMarcel Moolenaar 3672788ca347SMarcel Moolenaar if (xo_style(xop) == XO_STYLE_TEXT) { 3673788ca347SMarcel Moolenaar /* 3674788ca347SMarcel Moolenaar * Text mode means emitting the colors as ANSI character 3675788ca347SMarcel Moolenaar * codes. This will allow people who like colors to have 3676788ca347SMarcel Moolenaar * colors. The issue is, of course conflicting with the 3677788ca347SMarcel Moolenaar * user's perfectly reasonable color scheme. Which leads 3678788ca347SMarcel Moolenaar * to the hell of LSCOLORS, where even app need to have 3679788ca347SMarcel Moolenaar * customization hooks for adjusting colors. Instead we 3680788ca347SMarcel Moolenaar * provide a simpler-but-still-annoying answer where one 3681788ca347SMarcel Moolenaar * can map colors to other colors. 3682788ca347SMarcel Moolenaar */ 3683788ca347SMarcel Moolenaar xo_colors_handle_text(xop, &xoc); 3684788ca347SMarcel Moolenaar xoc.xoc_effects &= ~XO_EFF_RESET; /* After handling it */ 3685788ca347SMarcel Moolenaar 3686788ca347SMarcel Moolenaar } else { 3687788ca347SMarcel Moolenaar /* 3688788ca347SMarcel Moolenaar * HTML output is wrapped in divs, so the color information 3689788ca347SMarcel Moolenaar * must appear in every div until cleared. Most pathetic. 3690788ca347SMarcel Moolenaar * Most unavoidable. 3691788ca347SMarcel Moolenaar */ 3692788ca347SMarcel Moolenaar xoc.xoc_effects &= ~XO_EFF_RESET; /* Before handling effects */ 3693788ca347SMarcel Moolenaar xo_colors_handle_html(xop, &xoc); 3694788ca347SMarcel Moolenaar } 3695788ca347SMarcel Moolenaar 3696788ca347SMarcel Moolenaar xop->xo_colors = xoc; 3697788ca347SMarcel Moolenaar break; 3698788ca347SMarcel Moolenaar 3699788ca347SMarcel Moolenaar case XO_STYLE_XML: 3700788ca347SMarcel Moolenaar case XO_STYLE_JSON: 3701788ca347SMarcel Moolenaar /* 3702788ca347SMarcel Moolenaar * Nothing to do; we did all that work just to clear the stack of 3703788ca347SMarcel Moolenaar * formatting arguments. 3704788ca347SMarcel Moolenaar */ 3705788ca347SMarcel Moolenaar break; 3706788ca347SMarcel Moolenaar } 3707788ca347SMarcel Moolenaar } 3708788ca347SMarcel Moolenaar 3709788ca347SMarcel Moolenaar xo_buf_cleanup(&xb); 3710788ca347SMarcel Moolenaar } 3711788ca347SMarcel Moolenaar 371231337658SMarcel Moolenaar static void 371331337658SMarcel Moolenaar xo_format_units (xo_handle_t *xop, const char *str, int len, 371431337658SMarcel Moolenaar const char *fmt, int flen) 371531337658SMarcel Moolenaar { 371631337658SMarcel Moolenaar static char units_start_xml[] = " units=\""; 371731337658SMarcel Moolenaar static char units_start_html[] = " data-units=\""; 371831337658SMarcel Moolenaar 371931337658SMarcel Moolenaar if (!(xop->xo_flags & XOF_UNITS_PENDING)) { 372031337658SMarcel Moolenaar xo_format_content(xop, "units", NULL, 1, str, len, fmt, flen); 372131337658SMarcel Moolenaar return; 372231337658SMarcel Moolenaar } 372331337658SMarcel Moolenaar 372431337658SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_data; 372531337658SMarcel Moolenaar int start = xop->xo_units_offset; 372631337658SMarcel Moolenaar int stop = xbp->xb_curp - xbp->xb_bufp; 372731337658SMarcel Moolenaar 3728788ca347SMarcel Moolenaar if (xo_style(xop) == XO_STYLE_XML) 372931337658SMarcel Moolenaar xo_buf_append(xbp, units_start_xml, sizeof(units_start_xml) - 1); 3730788ca347SMarcel Moolenaar else if (xo_style(xop) == XO_STYLE_HTML) 373131337658SMarcel Moolenaar xo_buf_append(xbp, units_start_html, sizeof(units_start_html) - 1); 373231337658SMarcel Moolenaar else 373331337658SMarcel Moolenaar return; 373431337658SMarcel Moolenaar 373531337658SMarcel Moolenaar if (len) 373631337658SMarcel Moolenaar xo_data_append(xop, str, len); 373731337658SMarcel Moolenaar else 373831337658SMarcel Moolenaar xo_format_data(xop, NULL, fmt, flen, 0); 373931337658SMarcel Moolenaar 374031337658SMarcel Moolenaar xo_buf_append(xbp, "\"", 1); 374131337658SMarcel Moolenaar 374231337658SMarcel Moolenaar int now = xbp->xb_curp - xbp->xb_bufp; 374331337658SMarcel Moolenaar int delta = now - stop; 374431337658SMarcel Moolenaar if (delta < 0) { /* Strange; no output to move */ 374531337658SMarcel Moolenaar xbp->xb_curp = xbp->xb_bufp + stop; /* Reset buffer to prior state */ 374631337658SMarcel Moolenaar return; 374731337658SMarcel Moolenaar } 374831337658SMarcel Moolenaar 374931337658SMarcel Moolenaar /* 375031337658SMarcel Moolenaar * Now we're in it alright. We've need to insert the unit value 375131337658SMarcel Moolenaar * we just created into the right spot. We make a local copy, 375231337658SMarcel Moolenaar * move it and then insert our copy. We know there's room in the 375331337658SMarcel Moolenaar * buffer, since we're just moving this around. 375431337658SMarcel Moolenaar */ 375531337658SMarcel Moolenaar char *buf = alloca(delta); 375631337658SMarcel Moolenaar 375731337658SMarcel Moolenaar memcpy(buf, xbp->xb_bufp + stop, delta); 375831337658SMarcel Moolenaar memmove(xbp->xb_bufp + start + delta, xbp->xb_bufp + start, stop - start); 375931337658SMarcel Moolenaar memmove(xbp->xb_bufp + start, buf, delta); 376031337658SMarcel Moolenaar } 376131337658SMarcel Moolenaar 376231337658SMarcel Moolenaar static int 376331337658SMarcel Moolenaar xo_find_width (xo_handle_t *xop, const char *str, int len, 376431337658SMarcel Moolenaar const char *fmt, int flen) 376531337658SMarcel Moolenaar { 376631337658SMarcel Moolenaar long width = 0; 376731337658SMarcel Moolenaar char *bp; 376831337658SMarcel Moolenaar char *cp; 376931337658SMarcel Moolenaar 377031337658SMarcel Moolenaar if (len) { 377131337658SMarcel Moolenaar bp = alloca(len + 1); /* Make local NUL-terminated copy of str */ 377231337658SMarcel Moolenaar memcpy(bp, str, len); 377331337658SMarcel Moolenaar bp[len] = '\0'; 377431337658SMarcel Moolenaar 377531337658SMarcel Moolenaar width = strtol(bp, &cp, 0); 377631337658SMarcel Moolenaar if (width == LONG_MIN || width == LONG_MAX 377731337658SMarcel Moolenaar || bp == cp || *cp != '\0' ) { 377831337658SMarcel Moolenaar width = 0; 377931337658SMarcel Moolenaar xo_failure(xop, "invalid width for anchor: '%s'", bp); 378031337658SMarcel Moolenaar } 378131337658SMarcel Moolenaar } else if (flen) { 378231337658SMarcel Moolenaar if (flen != 2 || strncmp("%d", fmt, flen) != 0) 378331337658SMarcel Moolenaar xo_failure(xop, "invalid width format: '%*.*s'", flen, flen, fmt); 378431337658SMarcel Moolenaar if (!(xop->xo_flags & XOF_NO_VA_ARG)) 378531337658SMarcel Moolenaar width = va_arg(xop->xo_vap, int); 378631337658SMarcel Moolenaar } 378731337658SMarcel Moolenaar 378831337658SMarcel Moolenaar return width; 378931337658SMarcel Moolenaar } 379031337658SMarcel Moolenaar 379131337658SMarcel Moolenaar static void 379231337658SMarcel Moolenaar xo_anchor_clear (xo_handle_t *xop) 379331337658SMarcel Moolenaar { 379431337658SMarcel Moolenaar xop->xo_flags &= ~XOF_ANCHOR; 379531337658SMarcel Moolenaar xop->xo_anchor_offset = 0; 379631337658SMarcel Moolenaar xop->xo_anchor_columns = 0; 379731337658SMarcel Moolenaar xop->xo_anchor_min_width = 0; 379831337658SMarcel Moolenaar } 379931337658SMarcel Moolenaar 380031337658SMarcel Moolenaar /* 380131337658SMarcel Moolenaar * An anchor is a marker used to delay field width implications. 380231337658SMarcel Moolenaar * Imagine the format string "{[:10}{min:%d}/{cur:%d}/{max:%d}{:]}". 380331337658SMarcel Moolenaar * We are looking for output like " 1/4/5" 380431337658SMarcel Moolenaar * 380531337658SMarcel Moolenaar * To make this work, we record the anchor and then return to 380631337658SMarcel Moolenaar * format it when the end anchor tag is seen. 380731337658SMarcel Moolenaar */ 380831337658SMarcel Moolenaar static void 380931337658SMarcel Moolenaar xo_anchor_start (xo_handle_t *xop, const char *str, int len, 381031337658SMarcel Moolenaar const char *fmt, int flen) 381131337658SMarcel Moolenaar { 3812788ca347SMarcel Moolenaar if (xo_style(xop) != XO_STYLE_TEXT && xo_style(xop) != XO_STYLE_HTML) 381331337658SMarcel Moolenaar return; 381431337658SMarcel Moolenaar 381531337658SMarcel Moolenaar if (xop->xo_flags & XOF_ANCHOR) 381631337658SMarcel Moolenaar xo_failure(xop, "the anchor already recording is discarded"); 381731337658SMarcel Moolenaar 381831337658SMarcel Moolenaar xop->xo_flags |= XOF_ANCHOR; 381931337658SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_data; 382031337658SMarcel Moolenaar xop->xo_anchor_offset = xbp->xb_curp - xbp->xb_bufp; 382131337658SMarcel Moolenaar xop->xo_anchor_columns = 0; 382231337658SMarcel Moolenaar 382331337658SMarcel Moolenaar /* 382431337658SMarcel Moolenaar * Now we find the width, if possible. If it's not there, 382531337658SMarcel Moolenaar * we'll get it on the end anchor. 382631337658SMarcel Moolenaar */ 382731337658SMarcel Moolenaar xop->xo_anchor_min_width = xo_find_width(xop, str, len, fmt, flen); 382831337658SMarcel Moolenaar } 382931337658SMarcel Moolenaar 383031337658SMarcel Moolenaar static void 383131337658SMarcel Moolenaar xo_anchor_stop (xo_handle_t *xop, const char *str, int len, 383231337658SMarcel Moolenaar const char *fmt, int flen) 383331337658SMarcel Moolenaar { 3834788ca347SMarcel Moolenaar if (xo_style(xop) != XO_STYLE_TEXT && xo_style(xop) != XO_STYLE_HTML) 383531337658SMarcel Moolenaar return; 383631337658SMarcel Moolenaar 383731337658SMarcel Moolenaar if (!(xop->xo_flags & XOF_ANCHOR)) { 383831337658SMarcel Moolenaar xo_failure(xop, "no start anchor"); 383931337658SMarcel Moolenaar return; 384031337658SMarcel Moolenaar } 384131337658SMarcel Moolenaar 384231337658SMarcel Moolenaar xop->xo_flags &= ~XOF_UNITS_PENDING; 384331337658SMarcel Moolenaar 384431337658SMarcel Moolenaar int width = xo_find_width(xop, str, len, fmt, flen); 384531337658SMarcel Moolenaar if (width == 0) 384631337658SMarcel Moolenaar width = xop->xo_anchor_min_width; 384731337658SMarcel Moolenaar 384831337658SMarcel Moolenaar if (width == 0) /* No width given; nothing to do */ 384931337658SMarcel Moolenaar goto done; 385031337658SMarcel Moolenaar 385131337658SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_data; 385231337658SMarcel Moolenaar int start = xop->xo_anchor_offset; 385331337658SMarcel Moolenaar int stop = xbp->xb_curp - xbp->xb_bufp; 385431337658SMarcel Moolenaar int abswidth = (width > 0) ? width : -width; 385531337658SMarcel Moolenaar int blen = abswidth - xop->xo_anchor_columns; 385631337658SMarcel Moolenaar 385731337658SMarcel Moolenaar if (blen <= 0) /* Already over width */ 385831337658SMarcel Moolenaar goto done; 385931337658SMarcel Moolenaar 386031337658SMarcel Moolenaar if (abswidth > XO_MAX_ANCHOR_WIDTH) { 386131337658SMarcel Moolenaar xo_failure(xop, "width over %u are not supported", 386231337658SMarcel Moolenaar XO_MAX_ANCHOR_WIDTH); 386331337658SMarcel Moolenaar goto done; 386431337658SMarcel Moolenaar } 386531337658SMarcel Moolenaar 386631337658SMarcel Moolenaar /* Make a suitable padding field and emit it */ 386731337658SMarcel Moolenaar char *buf = alloca(blen); 386831337658SMarcel Moolenaar memset(buf, ' ', blen); 386931337658SMarcel Moolenaar xo_format_content(xop, "padding", NULL, 1, buf, blen, NULL, 0); 387031337658SMarcel Moolenaar 387131337658SMarcel Moolenaar if (width < 0) /* Already left justified */ 387231337658SMarcel Moolenaar goto done; 387331337658SMarcel Moolenaar 387431337658SMarcel Moolenaar int now = xbp->xb_curp - xbp->xb_bufp; 387531337658SMarcel Moolenaar int delta = now - stop; 387631337658SMarcel Moolenaar if (delta < 0) /* Strange; no output to move */ 387731337658SMarcel Moolenaar goto done; 387831337658SMarcel Moolenaar 387931337658SMarcel Moolenaar /* 388031337658SMarcel Moolenaar * Now we're in it alright. We've need to insert the padding data 388131337658SMarcel Moolenaar * we just created (which might be an HTML <div> or text) before 388231337658SMarcel Moolenaar * the formatted data. We make a local copy, move it and then 388331337658SMarcel Moolenaar * insert our copy. We know there's room in the buffer, since 388431337658SMarcel Moolenaar * we're just moving this around. 388531337658SMarcel Moolenaar */ 388631337658SMarcel Moolenaar if (delta > blen) 388731337658SMarcel Moolenaar buf = alloca(delta); /* Expand buffer if needed */ 388831337658SMarcel Moolenaar 388931337658SMarcel Moolenaar memcpy(buf, xbp->xb_bufp + stop, delta); 389031337658SMarcel Moolenaar memmove(xbp->xb_bufp + start + delta, xbp->xb_bufp + start, stop - start); 389131337658SMarcel Moolenaar memmove(xbp->xb_bufp + start, buf, delta); 389231337658SMarcel Moolenaar 389331337658SMarcel Moolenaar done: 389431337658SMarcel Moolenaar xo_anchor_clear(xop); 389531337658SMarcel Moolenaar } 389631337658SMarcel Moolenaar 389731337658SMarcel Moolenaar static int 389831337658SMarcel Moolenaar xo_do_emit (xo_handle_t *xop, const char *fmt) 389931337658SMarcel Moolenaar { 390031337658SMarcel Moolenaar int rc = 0; 390131337658SMarcel Moolenaar const char *cp, *sp, *ep, *basep; 390231337658SMarcel Moolenaar char *newp = NULL; 390331337658SMarcel Moolenaar int flush = (xop->xo_flags & XOF_FLUSH) ? 1 : 0; 3904545ddfbeSMarcel Moolenaar int flush_line = (xop->xo_flags & XOF_FLUSH_LINE) ? 1 : 0; 390531337658SMarcel Moolenaar 390631337658SMarcel Moolenaar xop->xo_columns = 0; /* Always reset it */ 390731337658SMarcel Moolenaar 390831337658SMarcel Moolenaar for (cp = fmt; *cp; ) { 390931337658SMarcel Moolenaar if (*cp == '\n') { 391031337658SMarcel Moolenaar xo_line_close(xop); 3911545ddfbeSMarcel Moolenaar if (flush_line && xo_flush_h(xop) < 0) 3912545ddfbeSMarcel Moolenaar return -1; 391331337658SMarcel Moolenaar cp += 1; 391431337658SMarcel Moolenaar continue; 391531337658SMarcel Moolenaar 391631337658SMarcel Moolenaar } else if (*cp == '{') { 391731337658SMarcel Moolenaar if (cp[1] == '{') { /* Start of {{escaped braces}} */ 391831337658SMarcel Moolenaar 391931337658SMarcel Moolenaar cp += 2; /* Skip over _both_ characters */ 392031337658SMarcel Moolenaar for (sp = cp; *sp; sp++) { 392131337658SMarcel Moolenaar if (*sp == '}' && sp[1] == '}') 392231337658SMarcel Moolenaar break; 392331337658SMarcel Moolenaar } 392431337658SMarcel Moolenaar if (*sp == '\0') { 392531337658SMarcel Moolenaar xo_failure(xop, "missing closing '}}': %s", fmt); 392631337658SMarcel Moolenaar return -1; 392731337658SMarcel Moolenaar } 392831337658SMarcel Moolenaar 392931337658SMarcel Moolenaar xo_format_text(xop, cp, sp - cp); 393031337658SMarcel Moolenaar 393131337658SMarcel Moolenaar /* Move along the string, but don't run off the end */ 393231337658SMarcel Moolenaar if (*sp == '}' && sp[1] == '}') 393331337658SMarcel Moolenaar sp += 2; 393431337658SMarcel Moolenaar cp = *sp ? sp + 1 : sp; 393531337658SMarcel Moolenaar continue; 393631337658SMarcel Moolenaar } 393731337658SMarcel Moolenaar /* Else fall thru to the code below */ 393831337658SMarcel Moolenaar 393931337658SMarcel Moolenaar } else { 394031337658SMarcel Moolenaar /* Normal text */ 394131337658SMarcel Moolenaar for (sp = cp; *sp; sp++) { 394231337658SMarcel Moolenaar if (*sp == '{' || *sp == '\n') 394331337658SMarcel Moolenaar break; 394431337658SMarcel Moolenaar } 394531337658SMarcel Moolenaar xo_format_text(xop, cp, sp - cp); 394631337658SMarcel Moolenaar 394731337658SMarcel Moolenaar cp = sp; 394831337658SMarcel Moolenaar continue; 394931337658SMarcel Moolenaar } 395031337658SMarcel Moolenaar 395131337658SMarcel Moolenaar basep = cp + 1; 395231337658SMarcel Moolenaar 395331337658SMarcel Moolenaar /* 395431337658SMarcel Moolenaar * We are looking at the start of a field definition. The format is: 395531337658SMarcel Moolenaar * '{' modifiers ':' content [ '/' print-fmt [ '/' encode-fmt ]] '}' 395631337658SMarcel Moolenaar * Modifiers are optional and include the following field types: 395731337658SMarcel Moolenaar * 'D': decoration; something non-text and non-data (colons, commmas) 395831337658SMarcel Moolenaar * 'E': error message 395931337658SMarcel Moolenaar * 'L': label; text preceding data 396031337658SMarcel Moolenaar * 'N': note; text following data 396131337658SMarcel Moolenaar * 'P': padding; whitespace 396231337658SMarcel Moolenaar * 'T': Title, where 'content' is a column title 396331337658SMarcel Moolenaar * 'U': Units, where 'content' is the unit label 396431337658SMarcel Moolenaar * 'V': value, where 'content' is the name of the field (the default) 396531337658SMarcel Moolenaar * 'W': warning message 396631337658SMarcel Moolenaar * '[': start a section of anchored text 396731337658SMarcel Moolenaar * ']': end a section of anchored text 396831337658SMarcel Moolenaar * The following flags are also supported: 396931337658SMarcel Moolenaar * 'c': flag: emit a colon after the label 397031337658SMarcel Moolenaar * 'd': field is only emitted for display formats (text and html) 397131337658SMarcel Moolenaar * 'e': field is only emitted for encoding formats (xml and json) 397231337658SMarcel Moolenaar * 'k': this field is a key, suitable for XPath predicates 397331337658SMarcel Moolenaar * 'l': a leaf-list, a simple list of values 397431337658SMarcel Moolenaar * 'n': no quotes around this field 397531337658SMarcel Moolenaar * 'q': add quotes around this field 397631337658SMarcel Moolenaar * 't': trim whitespace around the value 397731337658SMarcel Moolenaar * 'w': emit a blank after the label 397831337658SMarcel Moolenaar * The print-fmt and encode-fmt strings is the printf-style formating 397931337658SMarcel Moolenaar * for this data. JSON and XML will use the encoding-fmt, if present. 398031337658SMarcel Moolenaar * If the encode-fmt is not provided, it defaults to the print-fmt. 398131337658SMarcel Moolenaar * If the print-fmt is not provided, it defaults to 's'. 398231337658SMarcel Moolenaar */ 398331337658SMarcel Moolenaar unsigned ftype = 0, flags = 0; 398431337658SMarcel Moolenaar const char *content = NULL, *format = NULL, *encoding = NULL; 398531337658SMarcel Moolenaar int clen = 0, flen = 0, elen = 0; 398631337658SMarcel Moolenaar 398731337658SMarcel Moolenaar for (sp = basep; sp; sp++) { 398831337658SMarcel Moolenaar if (*sp == ':' || *sp == '/' || *sp == '}') 398931337658SMarcel Moolenaar break; 399031337658SMarcel Moolenaar 399131337658SMarcel Moolenaar if (*sp == '\\') { 399231337658SMarcel Moolenaar if (sp[1] == '\0') { 399331337658SMarcel Moolenaar xo_failure(xop, "backslash at the end of string"); 399431337658SMarcel Moolenaar return -1; 399531337658SMarcel Moolenaar } 399631337658SMarcel Moolenaar sp += 1; 399731337658SMarcel Moolenaar continue; 399831337658SMarcel Moolenaar } 399931337658SMarcel Moolenaar 400031337658SMarcel Moolenaar switch (*sp) { 4001788ca347SMarcel Moolenaar case 'C': 400231337658SMarcel Moolenaar case 'D': 400331337658SMarcel Moolenaar case 'E': 400431337658SMarcel Moolenaar case 'L': 400531337658SMarcel Moolenaar case 'N': 400631337658SMarcel Moolenaar case 'P': 400731337658SMarcel Moolenaar case 'T': 400831337658SMarcel Moolenaar case 'U': 400931337658SMarcel Moolenaar case 'V': 401031337658SMarcel Moolenaar case 'W': 401131337658SMarcel Moolenaar case '[': 401231337658SMarcel Moolenaar case ']': 401331337658SMarcel Moolenaar if (ftype != 0) { 401431337658SMarcel Moolenaar xo_failure(xop, "field descriptor uses multiple types: %s", 401531337658SMarcel Moolenaar fmt); 401631337658SMarcel Moolenaar return -1; 401731337658SMarcel Moolenaar } 401831337658SMarcel Moolenaar ftype = *sp; 401931337658SMarcel Moolenaar break; 402031337658SMarcel Moolenaar 402131337658SMarcel Moolenaar case 'c': 402231337658SMarcel Moolenaar flags |= XFF_COLON; 402331337658SMarcel Moolenaar break; 402431337658SMarcel Moolenaar 402531337658SMarcel Moolenaar case 'd': 402631337658SMarcel Moolenaar flags |= XFF_DISPLAY_ONLY; 402731337658SMarcel Moolenaar break; 402831337658SMarcel Moolenaar 402931337658SMarcel Moolenaar case 'e': 403031337658SMarcel Moolenaar flags |= XFF_ENCODE_ONLY; 403131337658SMarcel Moolenaar break; 403231337658SMarcel Moolenaar 403331337658SMarcel Moolenaar case 'k': 403431337658SMarcel Moolenaar flags |= XFF_KEY; 403531337658SMarcel Moolenaar break; 403631337658SMarcel Moolenaar 403731337658SMarcel Moolenaar case 'l': 403831337658SMarcel Moolenaar flags |= XFF_LEAF_LIST; 403931337658SMarcel Moolenaar break; 404031337658SMarcel Moolenaar 404131337658SMarcel Moolenaar case 'n': 404231337658SMarcel Moolenaar flags |= XFF_NOQUOTE; 404331337658SMarcel Moolenaar break; 404431337658SMarcel Moolenaar 404531337658SMarcel Moolenaar case 'q': 404631337658SMarcel Moolenaar flags |= XFF_QUOTE; 404731337658SMarcel Moolenaar break; 404831337658SMarcel Moolenaar 404931337658SMarcel Moolenaar case 't': 405031337658SMarcel Moolenaar flags |= XFF_TRIM_WS; 405131337658SMarcel Moolenaar break; 405231337658SMarcel Moolenaar 405331337658SMarcel Moolenaar case 'w': 405431337658SMarcel Moolenaar flags |= XFF_WS; 405531337658SMarcel Moolenaar break; 405631337658SMarcel Moolenaar 405731337658SMarcel Moolenaar default: 405831337658SMarcel Moolenaar xo_failure(xop, "field descriptor uses unknown modifier: %s", 405931337658SMarcel Moolenaar fmt); 406031337658SMarcel Moolenaar /* 406131337658SMarcel Moolenaar * No good answer here; a bad format will likely 406231337658SMarcel Moolenaar * mean a core file. We just return and hope 406331337658SMarcel Moolenaar * the caller notices there's no output, and while 406431337658SMarcel Moolenaar * that seems, well, bad. There's nothing better. 406531337658SMarcel Moolenaar */ 406631337658SMarcel Moolenaar return -1; 406731337658SMarcel Moolenaar } 406831337658SMarcel Moolenaar } 406931337658SMarcel Moolenaar 407031337658SMarcel Moolenaar if (*sp == ':') { 407131337658SMarcel Moolenaar for (ep = ++sp; *sp; sp++) { 407231337658SMarcel Moolenaar if (*sp == '}' || *sp == '/') 407331337658SMarcel Moolenaar break; 407431337658SMarcel Moolenaar if (*sp == '\\') { 407531337658SMarcel Moolenaar if (sp[1] == '\0') { 407631337658SMarcel Moolenaar xo_failure(xop, "backslash at the end of string"); 407731337658SMarcel Moolenaar return -1; 407831337658SMarcel Moolenaar } 407931337658SMarcel Moolenaar sp += 1; 408031337658SMarcel Moolenaar continue; 408131337658SMarcel Moolenaar } 408231337658SMarcel Moolenaar } 408331337658SMarcel Moolenaar if (ep != sp) { 408431337658SMarcel Moolenaar clen = sp - ep; 408531337658SMarcel Moolenaar content = ep; 408631337658SMarcel Moolenaar } 408731337658SMarcel Moolenaar } else { 408831337658SMarcel Moolenaar xo_failure(xop, "missing content (':'): %s", fmt); 408931337658SMarcel Moolenaar return -1; 409031337658SMarcel Moolenaar } 409131337658SMarcel Moolenaar 409231337658SMarcel Moolenaar if (*sp == '/') { 409331337658SMarcel Moolenaar for (ep = ++sp; *sp; sp++) { 409431337658SMarcel Moolenaar if (*sp == '}' || *sp == '/') 409531337658SMarcel Moolenaar break; 409631337658SMarcel Moolenaar if (*sp == '\\') { 409731337658SMarcel Moolenaar if (sp[1] == '\0') { 409831337658SMarcel Moolenaar xo_failure(xop, "backslash at the end of string"); 409931337658SMarcel Moolenaar return -1; 410031337658SMarcel Moolenaar } 410131337658SMarcel Moolenaar sp += 1; 410231337658SMarcel Moolenaar continue; 410331337658SMarcel Moolenaar } 410431337658SMarcel Moolenaar } 410531337658SMarcel Moolenaar flen = sp - ep; 410631337658SMarcel Moolenaar format = ep; 410731337658SMarcel Moolenaar } 410831337658SMarcel Moolenaar 410931337658SMarcel Moolenaar if (*sp == '/') { 411031337658SMarcel Moolenaar for (ep = ++sp; *sp; sp++) { 411131337658SMarcel Moolenaar if (*sp == '}') 411231337658SMarcel Moolenaar break; 411331337658SMarcel Moolenaar } 411431337658SMarcel Moolenaar elen = sp - ep; 411531337658SMarcel Moolenaar encoding = ep; 411631337658SMarcel Moolenaar } 411731337658SMarcel Moolenaar 411831337658SMarcel Moolenaar if (*sp == '}') { 411931337658SMarcel Moolenaar sp += 1; 412031337658SMarcel Moolenaar } else { 412131337658SMarcel Moolenaar xo_failure(xop, "missing closing '}': %s", fmt); 412231337658SMarcel Moolenaar return -1; 412331337658SMarcel Moolenaar } 412431337658SMarcel Moolenaar 4125545ddfbeSMarcel Moolenaar if (ftype == 0 || ftype == 'V') { 4126545ddfbeSMarcel Moolenaar if (format == NULL) { 4127545ddfbeSMarcel Moolenaar /* Default format for value fields is '%s' */ 412831337658SMarcel Moolenaar format = "%s"; 412931337658SMarcel Moolenaar flen = 2; 413031337658SMarcel Moolenaar } 413131337658SMarcel Moolenaar 413231337658SMarcel Moolenaar xo_format_value(xop, content, clen, format, flen, 413331337658SMarcel Moolenaar encoding, elen, flags); 4134545ddfbeSMarcel Moolenaar 4135545ddfbeSMarcel Moolenaar } else if (ftype == '[') 4136545ddfbeSMarcel Moolenaar xo_anchor_start(xop, content, clen, format, flen); 4137545ddfbeSMarcel Moolenaar else if (ftype == ']') 4138545ddfbeSMarcel Moolenaar xo_anchor_stop(xop, content, clen, format, flen); 4139788ca347SMarcel Moolenaar else if (ftype == 'C') 4140788ca347SMarcel Moolenaar xo_format_colors(xop, content, clen, format, flen); 4141545ddfbeSMarcel Moolenaar 4142545ddfbeSMarcel Moolenaar else if (clen || format) { /* Need either content or format */ 4143545ddfbeSMarcel Moolenaar if (format == NULL) { 4144545ddfbeSMarcel Moolenaar /* Default format for value fields is '%s' */ 4145545ddfbeSMarcel Moolenaar format = "%s"; 4146545ddfbeSMarcel Moolenaar flen = 2; 4147545ddfbeSMarcel Moolenaar } 4148545ddfbeSMarcel Moolenaar 4149545ddfbeSMarcel Moolenaar if (ftype == 'D') 415031337658SMarcel Moolenaar xo_format_content(xop, "decoration", NULL, 1, 415131337658SMarcel Moolenaar content, clen, format, flen); 415231337658SMarcel Moolenaar else if (ftype == 'E') 415331337658SMarcel Moolenaar xo_format_content(xop, "error", "error", 0, 415431337658SMarcel Moolenaar content, clen, format, flen); 415531337658SMarcel Moolenaar else if (ftype == 'L') 415631337658SMarcel Moolenaar xo_format_content(xop, "label", NULL, 1, 415731337658SMarcel Moolenaar content, clen, format, flen); 415831337658SMarcel Moolenaar else if (ftype == 'N') 415931337658SMarcel Moolenaar xo_format_content(xop, "note", NULL, 1, 416031337658SMarcel Moolenaar content, clen, format, flen); 416131337658SMarcel Moolenaar else if (ftype == 'P') 416231337658SMarcel Moolenaar xo_format_content(xop, "padding", NULL, 1, 416331337658SMarcel Moolenaar content, clen, format, flen); 416431337658SMarcel Moolenaar else if (ftype == 'T') 416531337658SMarcel Moolenaar xo_format_title(xop, content, clen, format, flen); 416631337658SMarcel Moolenaar else if (ftype == 'U') { 416731337658SMarcel Moolenaar if (flags & XFF_WS) 416831337658SMarcel Moolenaar xo_format_content(xop, "padding", NULL, 1, " ", 1, NULL, 0); 416931337658SMarcel Moolenaar xo_format_units(xop, content, clen, format, flen); 417031337658SMarcel Moolenaar } else if (ftype == 'W') 417131337658SMarcel Moolenaar xo_format_content(xop, "warning", "warning", 0, 417231337658SMarcel Moolenaar content, clen, format, flen); 4173545ddfbeSMarcel Moolenaar } 417431337658SMarcel Moolenaar 417531337658SMarcel Moolenaar if (flags & XFF_COLON) 417631337658SMarcel Moolenaar xo_format_content(xop, "decoration", NULL, 1, ":", 1, NULL, 0); 417731337658SMarcel Moolenaar if (ftype != 'U' && (flags & XFF_WS)) 417831337658SMarcel Moolenaar xo_format_content(xop, "padding", NULL, 1, " ", 1, NULL, 0); 417931337658SMarcel Moolenaar 418031337658SMarcel Moolenaar cp += sp - basep + 1; 418131337658SMarcel Moolenaar if (newp) { 418231337658SMarcel Moolenaar xo_free(newp); 418331337658SMarcel Moolenaar newp = NULL; 418431337658SMarcel Moolenaar } 418531337658SMarcel Moolenaar } 418631337658SMarcel Moolenaar 418731337658SMarcel Moolenaar /* If we don't have an anchor, write the text out */ 4188545ddfbeSMarcel Moolenaar if (flush && !(xop->xo_flags & XOF_ANCHOR)) { 4189545ddfbeSMarcel Moolenaar if (xo_write(xop) < 0) 4190545ddfbeSMarcel Moolenaar rc = -1; /* Report failure */ 4191545ddfbeSMarcel Moolenaar else if (xop->xo_flush && xop->xo_flush(xop->xo_opaque) < 0) 4192545ddfbeSMarcel Moolenaar rc = -1; 4193545ddfbeSMarcel Moolenaar } 419431337658SMarcel Moolenaar 419531337658SMarcel Moolenaar return (rc < 0) ? rc : (int) xop->xo_columns; 419631337658SMarcel Moolenaar } 419731337658SMarcel Moolenaar 419831337658SMarcel Moolenaar int 419931337658SMarcel Moolenaar xo_emit_hv (xo_handle_t *xop, const char *fmt, va_list vap) 420031337658SMarcel Moolenaar { 420131337658SMarcel Moolenaar int rc; 420231337658SMarcel Moolenaar 420331337658SMarcel Moolenaar xop = xo_default(xop); 420431337658SMarcel Moolenaar va_copy(xop->xo_vap, vap); 420531337658SMarcel Moolenaar rc = xo_do_emit(xop, fmt); 420631337658SMarcel Moolenaar va_end(xop->xo_vap); 420731337658SMarcel Moolenaar bzero(&xop->xo_vap, sizeof(xop->xo_vap)); 420831337658SMarcel Moolenaar 420931337658SMarcel Moolenaar return rc; 421031337658SMarcel Moolenaar } 421131337658SMarcel Moolenaar 421231337658SMarcel Moolenaar int 421331337658SMarcel Moolenaar xo_emit_h (xo_handle_t *xop, const char *fmt, ...) 421431337658SMarcel Moolenaar { 421531337658SMarcel Moolenaar int rc; 421631337658SMarcel Moolenaar 421731337658SMarcel Moolenaar xop = xo_default(xop); 421831337658SMarcel Moolenaar va_start(xop->xo_vap, fmt); 421931337658SMarcel Moolenaar rc = xo_do_emit(xop, fmt); 422031337658SMarcel Moolenaar va_end(xop->xo_vap); 422131337658SMarcel Moolenaar bzero(&xop->xo_vap, sizeof(xop->xo_vap)); 422231337658SMarcel Moolenaar 422331337658SMarcel Moolenaar return rc; 422431337658SMarcel Moolenaar } 422531337658SMarcel Moolenaar 422631337658SMarcel Moolenaar int 422731337658SMarcel Moolenaar xo_emit (const char *fmt, ...) 422831337658SMarcel Moolenaar { 422931337658SMarcel Moolenaar xo_handle_t *xop = xo_default(NULL); 423031337658SMarcel Moolenaar int rc; 423131337658SMarcel Moolenaar 423231337658SMarcel Moolenaar va_start(xop->xo_vap, fmt); 423331337658SMarcel Moolenaar rc = xo_do_emit(xop, fmt); 423431337658SMarcel Moolenaar va_end(xop->xo_vap); 423531337658SMarcel Moolenaar bzero(&xop->xo_vap, sizeof(xop->xo_vap)); 423631337658SMarcel Moolenaar 423731337658SMarcel Moolenaar return rc; 423831337658SMarcel Moolenaar } 423931337658SMarcel Moolenaar 424031337658SMarcel Moolenaar int 424131337658SMarcel Moolenaar xo_attr_hv (xo_handle_t *xop, const char *name, const char *fmt, va_list vap) 424231337658SMarcel Moolenaar { 424331337658SMarcel Moolenaar const int extra = 5; /* space, equals, quote, quote, and nul */ 424431337658SMarcel Moolenaar xop = xo_default(xop); 424531337658SMarcel Moolenaar 4246788ca347SMarcel Moolenaar if (xo_style(xop) != XO_STYLE_XML) 424731337658SMarcel Moolenaar return 0; 424831337658SMarcel Moolenaar 424931337658SMarcel Moolenaar int nlen = strlen(name); 425031337658SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_attrs; 425131337658SMarcel Moolenaar 425231337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, nlen + extra)) 425331337658SMarcel Moolenaar return -1; 425431337658SMarcel Moolenaar 425531337658SMarcel Moolenaar *xbp->xb_curp++ = ' '; 425631337658SMarcel Moolenaar memcpy(xbp->xb_curp, name, nlen); 425731337658SMarcel Moolenaar xbp->xb_curp += nlen; 425831337658SMarcel Moolenaar *xbp->xb_curp++ = '='; 425931337658SMarcel Moolenaar *xbp->xb_curp++ = '"'; 426031337658SMarcel Moolenaar 426131337658SMarcel Moolenaar int rc = xo_vsnprintf(xop, xbp, fmt, vap); 426231337658SMarcel Moolenaar 426331337658SMarcel Moolenaar if (rc > 0) { 426431337658SMarcel Moolenaar rc = xo_escape_xml(xbp, rc, 1); 426531337658SMarcel Moolenaar xbp->xb_curp += rc; 426631337658SMarcel Moolenaar } 426731337658SMarcel Moolenaar 426831337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, 2)) 426931337658SMarcel Moolenaar return -1; 427031337658SMarcel Moolenaar 427131337658SMarcel Moolenaar *xbp->xb_curp++ = '"'; 427231337658SMarcel Moolenaar *xbp->xb_curp = '\0'; 427331337658SMarcel Moolenaar 427431337658SMarcel Moolenaar return rc + nlen + extra; 427531337658SMarcel Moolenaar } 427631337658SMarcel Moolenaar 427731337658SMarcel Moolenaar int 427831337658SMarcel Moolenaar xo_attr_h (xo_handle_t *xop, const char *name, const char *fmt, ...) 427931337658SMarcel Moolenaar { 428031337658SMarcel Moolenaar int rc; 428131337658SMarcel Moolenaar va_list vap; 428231337658SMarcel Moolenaar 428331337658SMarcel Moolenaar va_start(vap, fmt); 428431337658SMarcel Moolenaar rc = xo_attr_hv(xop, name, fmt, vap); 428531337658SMarcel Moolenaar va_end(vap); 428631337658SMarcel Moolenaar 428731337658SMarcel Moolenaar return rc; 428831337658SMarcel Moolenaar } 428931337658SMarcel Moolenaar 429031337658SMarcel Moolenaar int 429131337658SMarcel Moolenaar xo_attr (const char *name, const char *fmt, ...) 429231337658SMarcel Moolenaar { 429331337658SMarcel Moolenaar int rc; 429431337658SMarcel Moolenaar va_list vap; 429531337658SMarcel Moolenaar 429631337658SMarcel Moolenaar va_start(vap, fmt); 429731337658SMarcel Moolenaar rc = xo_attr_hv(NULL, name, fmt, vap); 429831337658SMarcel Moolenaar va_end(vap); 429931337658SMarcel Moolenaar 430031337658SMarcel Moolenaar return rc; 430131337658SMarcel Moolenaar } 430231337658SMarcel Moolenaar 430331337658SMarcel Moolenaar static void 430431337658SMarcel Moolenaar xo_stack_set_flags (xo_handle_t *xop) 430531337658SMarcel Moolenaar { 430631337658SMarcel Moolenaar if (xop->xo_flags & XOF_NOT_FIRST) { 430731337658SMarcel Moolenaar xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth]; 430831337658SMarcel Moolenaar 430931337658SMarcel Moolenaar xsp->xs_flags |= XSF_NOT_FIRST; 431031337658SMarcel Moolenaar xop->xo_flags &= ~XOF_NOT_FIRST; 431131337658SMarcel Moolenaar } 431231337658SMarcel Moolenaar } 431331337658SMarcel Moolenaar 431431337658SMarcel Moolenaar static void 431531337658SMarcel Moolenaar xo_depth_change (xo_handle_t *xop, const char *name, 4316545ddfbeSMarcel Moolenaar int delta, int indent, xo_state_t state, xo_xsf_flags_t flags) 431731337658SMarcel Moolenaar { 4318788ca347SMarcel Moolenaar if (xo_style(xop) == XO_STYLE_HTML || xo_style(xop) == XO_STYLE_TEXT) 4319545ddfbeSMarcel Moolenaar indent = 0; 4320545ddfbeSMarcel Moolenaar 432131337658SMarcel Moolenaar if (xop->xo_flags & XOF_DTRT) 432231337658SMarcel Moolenaar flags |= XSF_DTRT; 432331337658SMarcel Moolenaar 432431337658SMarcel Moolenaar if (delta >= 0) { /* Push operation */ 432531337658SMarcel Moolenaar if (xo_depth_check(xop, xop->xo_depth + delta)) 432631337658SMarcel Moolenaar return; 432731337658SMarcel Moolenaar 432831337658SMarcel Moolenaar xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth + delta]; 432931337658SMarcel Moolenaar xsp->xs_flags = flags; 4330545ddfbeSMarcel Moolenaar xsp->xs_state = state; 433131337658SMarcel Moolenaar xo_stack_set_flags(xop); 433231337658SMarcel Moolenaar 4333545ddfbeSMarcel Moolenaar if (name == NULL) 4334545ddfbeSMarcel Moolenaar name = XO_FAILURE_NAME; 433531337658SMarcel Moolenaar 433631337658SMarcel Moolenaar int len = strlen(name) + 1; 433731337658SMarcel Moolenaar char *cp = xo_realloc(NULL, len); 433831337658SMarcel Moolenaar if (cp) { 433931337658SMarcel Moolenaar memcpy(cp, name, len); 434031337658SMarcel Moolenaar xsp->xs_name = cp; 434131337658SMarcel Moolenaar } 434231337658SMarcel Moolenaar 434331337658SMarcel Moolenaar } else { /* Pop operation */ 434431337658SMarcel Moolenaar if (xop->xo_depth == 0) { 434531337658SMarcel Moolenaar if (!(xop->xo_flags & XOF_IGNORE_CLOSE)) 434631337658SMarcel Moolenaar xo_failure(xop, "close with empty stack: '%s'", name); 434731337658SMarcel Moolenaar return; 434831337658SMarcel Moolenaar } 434931337658SMarcel Moolenaar 435031337658SMarcel Moolenaar xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth]; 435131337658SMarcel Moolenaar if (xop->xo_flags & XOF_WARN) { 435231337658SMarcel Moolenaar const char *top = xsp->xs_name; 435331337658SMarcel Moolenaar if (top && strcmp(name, top) != 0) { 435431337658SMarcel Moolenaar xo_failure(xop, "incorrect close: '%s' .vs. '%s'", 435531337658SMarcel Moolenaar name, top); 435631337658SMarcel Moolenaar return; 435731337658SMarcel Moolenaar } 435831337658SMarcel Moolenaar if ((xsp->xs_flags & XSF_LIST) != (flags & XSF_LIST)) { 435931337658SMarcel Moolenaar xo_failure(xop, "list close on list confict: '%s'", 436031337658SMarcel Moolenaar name); 436131337658SMarcel Moolenaar return; 436231337658SMarcel Moolenaar } 436331337658SMarcel Moolenaar if ((xsp->xs_flags & XSF_INSTANCE) != (flags & XSF_INSTANCE)) { 436431337658SMarcel Moolenaar xo_failure(xop, "list close on instance confict: '%s'", 436531337658SMarcel Moolenaar name); 436631337658SMarcel Moolenaar return; 436731337658SMarcel Moolenaar } 436831337658SMarcel Moolenaar } 436931337658SMarcel Moolenaar 437031337658SMarcel Moolenaar if (xsp->xs_name) { 437131337658SMarcel Moolenaar xo_free(xsp->xs_name); 437231337658SMarcel Moolenaar xsp->xs_name = NULL; 437331337658SMarcel Moolenaar } 437431337658SMarcel Moolenaar if (xsp->xs_keys) { 437531337658SMarcel Moolenaar xo_free(xsp->xs_keys); 437631337658SMarcel Moolenaar xsp->xs_keys = NULL; 437731337658SMarcel Moolenaar } 437831337658SMarcel Moolenaar } 437931337658SMarcel Moolenaar 438031337658SMarcel Moolenaar xop->xo_depth += delta; /* Record new depth */ 438131337658SMarcel Moolenaar xop->xo_indent += indent; 438231337658SMarcel Moolenaar } 438331337658SMarcel Moolenaar 438431337658SMarcel Moolenaar void 438531337658SMarcel Moolenaar xo_set_depth (xo_handle_t *xop, int depth) 438631337658SMarcel Moolenaar { 438731337658SMarcel Moolenaar xop = xo_default(xop); 438831337658SMarcel Moolenaar 438931337658SMarcel Moolenaar if (xo_depth_check(xop, depth)) 439031337658SMarcel Moolenaar return; 439131337658SMarcel Moolenaar 439231337658SMarcel Moolenaar xop->xo_depth += depth; 439331337658SMarcel Moolenaar xop->xo_indent += depth; 439431337658SMarcel Moolenaar } 439531337658SMarcel Moolenaar 439631337658SMarcel Moolenaar static xo_xsf_flags_t 439731337658SMarcel Moolenaar xo_stack_flags (unsigned xflags) 439831337658SMarcel Moolenaar { 439931337658SMarcel Moolenaar if (xflags & XOF_DTRT) 440031337658SMarcel Moolenaar return XSF_DTRT; 440131337658SMarcel Moolenaar return 0; 440231337658SMarcel Moolenaar } 440331337658SMarcel Moolenaar 4404788ca347SMarcel Moolenaar static void 4405788ca347SMarcel Moolenaar xo_emit_top (xo_handle_t *xop, const char *ppn) 4406788ca347SMarcel Moolenaar { 4407788ca347SMarcel Moolenaar xo_printf(xop, "%*s{%s", xo_indent(xop), "", ppn); 4408788ca347SMarcel Moolenaar xop->xo_flags |= XOF_TOP_EMITTED; 4409788ca347SMarcel Moolenaar 4410788ca347SMarcel Moolenaar if (xop->xo_version) { 4411788ca347SMarcel Moolenaar xo_printf(xop, "%*s\"__version\": \"%s\", %s", 4412788ca347SMarcel Moolenaar xo_indent(xop), "", xop->xo_version, ppn); 4413788ca347SMarcel Moolenaar xo_free(xop->xo_version); 4414788ca347SMarcel Moolenaar xop->xo_version = NULL; 4415788ca347SMarcel Moolenaar } 4416788ca347SMarcel Moolenaar } 4417788ca347SMarcel Moolenaar 441831337658SMarcel Moolenaar static int 4419545ddfbeSMarcel Moolenaar xo_do_open_container (xo_handle_t *xop, xo_xof_flags_t flags, const char *name) 442031337658SMarcel Moolenaar { 442131337658SMarcel Moolenaar int rc = 0; 442231337658SMarcel Moolenaar const char *ppn = (xop->xo_flags & XOF_PRETTY) ? "\n" : ""; 442331337658SMarcel Moolenaar const char *pre_nl = ""; 442431337658SMarcel Moolenaar 442531337658SMarcel Moolenaar if (name == NULL) { 442631337658SMarcel Moolenaar xo_failure(xop, "NULL passed for container name"); 442731337658SMarcel Moolenaar name = XO_FAILURE_NAME; 442831337658SMarcel Moolenaar } 442931337658SMarcel Moolenaar 443031337658SMarcel Moolenaar flags |= xop->xo_flags; /* Pick up handle flags */ 443131337658SMarcel Moolenaar 4432788ca347SMarcel Moolenaar switch (xo_style(xop)) { 443331337658SMarcel Moolenaar case XO_STYLE_XML: 4434545ddfbeSMarcel Moolenaar rc = xo_printf(xop, "%*s<%s", xo_indent(xop), "", name); 4435545ddfbeSMarcel Moolenaar 4436545ddfbeSMarcel Moolenaar if (xop->xo_attrs.xb_curp != xop->xo_attrs.xb_bufp) { 4437545ddfbeSMarcel Moolenaar rc += xop->xo_attrs.xb_curp - xop->xo_attrs.xb_bufp; 4438545ddfbeSMarcel Moolenaar xo_data_append(xop, xop->xo_attrs.xb_bufp, 4439545ddfbeSMarcel Moolenaar xop->xo_attrs.xb_curp - xop->xo_attrs.xb_bufp); 4440545ddfbeSMarcel Moolenaar xop->xo_attrs.xb_curp = xop->xo_attrs.xb_bufp; 4441545ddfbeSMarcel Moolenaar } 4442545ddfbeSMarcel Moolenaar 4443545ddfbeSMarcel Moolenaar rc += xo_printf(xop, ">%s", ppn); 444431337658SMarcel Moolenaar break; 444531337658SMarcel Moolenaar 444631337658SMarcel Moolenaar case XO_STYLE_JSON: 444731337658SMarcel Moolenaar xo_stack_set_flags(xop); 444831337658SMarcel Moolenaar 4449788ca347SMarcel Moolenaar if (!(xop->xo_flags & XOF_NO_TOP) 4450788ca347SMarcel Moolenaar && !(xop->xo_flags & XOF_TOP_EMITTED)) 4451788ca347SMarcel Moolenaar xo_emit_top(xop, ppn); 445231337658SMarcel Moolenaar 445331337658SMarcel Moolenaar if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST) 445431337658SMarcel Moolenaar pre_nl = (xop->xo_flags & XOF_PRETTY) ? ",\n" : ", "; 445531337658SMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 445631337658SMarcel Moolenaar 445731337658SMarcel Moolenaar rc = xo_printf(xop, "%s%*s\"%s\": {%s", 445831337658SMarcel Moolenaar pre_nl, xo_indent(xop), "", name, ppn); 445931337658SMarcel Moolenaar break; 446031337658SMarcel Moolenaar } 446131337658SMarcel Moolenaar 4462545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, 1, 1, XSS_OPEN_CONTAINER, 4463545ddfbeSMarcel Moolenaar xo_stack_flags(flags)); 4464545ddfbeSMarcel Moolenaar 446531337658SMarcel Moolenaar return rc; 446631337658SMarcel Moolenaar } 446731337658SMarcel Moolenaar 4468545ddfbeSMarcel Moolenaar static int 4469545ddfbeSMarcel Moolenaar xo_open_container_hf (xo_handle_t *xop, xo_xof_flags_t flags, const char *name) 4470545ddfbeSMarcel Moolenaar { 4471545ddfbeSMarcel Moolenaar return xo_transition(xop, flags, name, XSS_OPEN_CONTAINER); 4472545ddfbeSMarcel Moolenaar } 4473545ddfbeSMarcel Moolenaar 447431337658SMarcel Moolenaar int 447531337658SMarcel Moolenaar xo_open_container_h (xo_handle_t *xop, const char *name) 447631337658SMarcel Moolenaar { 447731337658SMarcel Moolenaar return xo_open_container_hf(xop, 0, name); 447831337658SMarcel Moolenaar } 447931337658SMarcel Moolenaar 448031337658SMarcel Moolenaar int 448131337658SMarcel Moolenaar xo_open_container (const char *name) 448231337658SMarcel Moolenaar { 448331337658SMarcel Moolenaar return xo_open_container_hf(NULL, 0, name); 448431337658SMarcel Moolenaar } 448531337658SMarcel Moolenaar 448631337658SMarcel Moolenaar int 448731337658SMarcel Moolenaar xo_open_container_hd (xo_handle_t *xop, const char *name) 448831337658SMarcel Moolenaar { 448931337658SMarcel Moolenaar return xo_open_container_hf(xop, XOF_DTRT, name); 449031337658SMarcel Moolenaar } 449131337658SMarcel Moolenaar 449231337658SMarcel Moolenaar int 449331337658SMarcel Moolenaar xo_open_container_d (const char *name) 449431337658SMarcel Moolenaar { 449531337658SMarcel Moolenaar return xo_open_container_hf(NULL, XOF_DTRT, name); 449631337658SMarcel Moolenaar } 449731337658SMarcel Moolenaar 4498545ddfbeSMarcel Moolenaar static int 4499545ddfbeSMarcel Moolenaar xo_do_close_container (xo_handle_t *xop, const char *name) 450031337658SMarcel Moolenaar { 450131337658SMarcel Moolenaar xop = xo_default(xop); 450231337658SMarcel Moolenaar 450331337658SMarcel Moolenaar int rc = 0; 450431337658SMarcel Moolenaar const char *ppn = (xop->xo_flags & XOF_PRETTY) ? "\n" : ""; 450531337658SMarcel Moolenaar const char *pre_nl = ""; 450631337658SMarcel Moolenaar 450731337658SMarcel Moolenaar if (name == NULL) { 450831337658SMarcel Moolenaar xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth]; 450931337658SMarcel Moolenaar 451031337658SMarcel Moolenaar name = xsp->xs_name; 451131337658SMarcel Moolenaar if (name) { 451231337658SMarcel Moolenaar int len = strlen(name) + 1; 451331337658SMarcel Moolenaar /* We need to make a local copy; xo_depth_change will free it */ 451431337658SMarcel Moolenaar char *cp = alloca(len); 451531337658SMarcel Moolenaar memcpy(cp, name, len); 451631337658SMarcel Moolenaar name = cp; 4517545ddfbeSMarcel Moolenaar } else if (!(xsp->xs_flags & XSF_DTRT)) { 4518545ddfbeSMarcel Moolenaar xo_failure(xop, "missing name without 'dtrt' mode"); 451931337658SMarcel Moolenaar name = XO_FAILURE_NAME; 452031337658SMarcel Moolenaar } 4521545ddfbeSMarcel Moolenaar } 452231337658SMarcel Moolenaar 4523788ca347SMarcel Moolenaar switch (xo_style(xop)) { 452431337658SMarcel Moolenaar case XO_STYLE_XML: 4525545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, -1, -1, XSS_CLOSE_CONTAINER, 0); 452631337658SMarcel Moolenaar rc = xo_printf(xop, "%*s</%s>%s", xo_indent(xop), "", name, ppn); 452731337658SMarcel Moolenaar break; 452831337658SMarcel Moolenaar 452931337658SMarcel Moolenaar case XO_STYLE_JSON: 453031337658SMarcel Moolenaar pre_nl = (xop->xo_flags & XOF_PRETTY) ? "\n" : ""; 453131337658SMarcel Moolenaar ppn = (xop->xo_depth <= 1) ? "\n" : ""; 453231337658SMarcel Moolenaar 4533545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, -1, -1, XSS_CLOSE_CONTAINER, 0); 453431337658SMarcel Moolenaar rc = xo_printf(xop, "%s%*s}%s", pre_nl, xo_indent(xop), "", ppn); 453531337658SMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 453631337658SMarcel Moolenaar break; 453731337658SMarcel Moolenaar 453831337658SMarcel Moolenaar case XO_STYLE_HTML: 453931337658SMarcel Moolenaar case XO_STYLE_TEXT: 4540545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, -1, 0, XSS_CLOSE_CONTAINER, 0); 454131337658SMarcel Moolenaar break; 454231337658SMarcel Moolenaar } 454331337658SMarcel Moolenaar 454431337658SMarcel Moolenaar return rc; 454531337658SMarcel Moolenaar } 454631337658SMarcel Moolenaar 454731337658SMarcel Moolenaar int 4548545ddfbeSMarcel Moolenaar xo_close_container_h (xo_handle_t *xop, const char *name) 4549545ddfbeSMarcel Moolenaar { 4550545ddfbeSMarcel Moolenaar return xo_transition(xop, 0, name, XSS_CLOSE_CONTAINER); 4551545ddfbeSMarcel Moolenaar } 4552545ddfbeSMarcel Moolenaar 4553545ddfbeSMarcel Moolenaar int 455431337658SMarcel Moolenaar xo_close_container (const char *name) 455531337658SMarcel Moolenaar { 455631337658SMarcel Moolenaar return xo_close_container_h(NULL, name); 455731337658SMarcel Moolenaar } 455831337658SMarcel Moolenaar 455931337658SMarcel Moolenaar int 456031337658SMarcel Moolenaar xo_close_container_hd (xo_handle_t *xop) 456131337658SMarcel Moolenaar { 456231337658SMarcel Moolenaar return xo_close_container_h(xop, NULL); 456331337658SMarcel Moolenaar } 456431337658SMarcel Moolenaar 456531337658SMarcel Moolenaar int 456631337658SMarcel Moolenaar xo_close_container_d (void) 456731337658SMarcel Moolenaar { 456831337658SMarcel Moolenaar return xo_close_container_h(NULL, NULL); 456931337658SMarcel Moolenaar } 457031337658SMarcel Moolenaar 457131337658SMarcel Moolenaar static int 4572545ddfbeSMarcel Moolenaar xo_do_open_list (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name) 457331337658SMarcel Moolenaar { 4574545ddfbeSMarcel Moolenaar int rc = 0; 4575545ddfbeSMarcel Moolenaar int indent = 0; 4576545ddfbeSMarcel Moolenaar 457731337658SMarcel Moolenaar xop = xo_default(xop); 457831337658SMarcel Moolenaar 4579788ca347SMarcel Moolenaar if (xo_style(xop) == XO_STYLE_JSON) { 458031337658SMarcel Moolenaar const char *ppn = (xop->xo_flags & XOF_PRETTY) ? "\n" : ""; 458131337658SMarcel Moolenaar const char *pre_nl = ""; 458231337658SMarcel Moolenaar 4583545ddfbeSMarcel Moolenaar indent = 1; 4584788ca347SMarcel Moolenaar if (!(xop->xo_flags & XOF_NO_TOP) 4585788ca347SMarcel Moolenaar && !(xop->xo_flags & XOF_TOP_EMITTED)) 4586788ca347SMarcel Moolenaar xo_emit_top(xop, ppn); 458731337658SMarcel Moolenaar 458831337658SMarcel Moolenaar if (name == NULL) { 458931337658SMarcel Moolenaar xo_failure(xop, "NULL passed for list name"); 459031337658SMarcel Moolenaar name = XO_FAILURE_NAME; 459131337658SMarcel Moolenaar } 459231337658SMarcel Moolenaar 459331337658SMarcel Moolenaar xo_stack_set_flags(xop); 459431337658SMarcel Moolenaar 459531337658SMarcel Moolenaar if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST) 459631337658SMarcel Moolenaar pre_nl = (xop->xo_flags & XOF_PRETTY) ? ",\n" : ", "; 459731337658SMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 459831337658SMarcel Moolenaar 459931337658SMarcel Moolenaar rc = xo_printf(xop, "%s%*s\"%s\": [%s", 460031337658SMarcel Moolenaar pre_nl, xo_indent(xop), "", name, ppn); 4601545ddfbeSMarcel Moolenaar } 4602545ddfbeSMarcel Moolenaar 4603545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, 1, indent, XSS_OPEN_LIST, 4604545ddfbeSMarcel Moolenaar XSF_LIST | xo_stack_flags(flags)); 460531337658SMarcel Moolenaar 460631337658SMarcel Moolenaar return rc; 460731337658SMarcel Moolenaar } 460831337658SMarcel Moolenaar 4609545ddfbeSMarcel Moolenaar static int 4610545ddfbeSMarcel Moolenaar xo_open_list_hf (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name) 4611545ddfbeSMarcel Moolenaar { 4612545ddfbeSMarcel Moolenaar return xo_transition(xop, flags, name, XSS_OPEN_LIST); 4613545ddfbeSMarcel Moolenaar } 4614545ddfbeSMarcel Moolenaar 461531337658SMarcel Moolenaar int 461631337658SMarcel Moolenaar xo_open_list_h (xo_handle_t *xop, const char *name UNUSED) 461731337658SMarcel Moolenaar { 461831337658SMarcel Moolenaar return xo_open_list_hf(xop, 0, name); 461931337658SMarcel Moolenaar } 462031337658SMarcel Moolenaar 462131337658SMarcel Moolenaar int 462231337658SMarcel Moolenaar xo_open_list (const char *name) 462331337658SMarcel Moolenaar { 462431337658SMarcel Moolenaar return xo_open_list_hf(NULL, 0, name); 462531337658SMarcel Moolenaar } 462631337658SMarcel Moolenaar 462731337658SMarcel Moolenaar int 462831337658SMarcel Moolenaar xo_open_list_hd (xo_handle_t *xop, const char *name UNUSED) 462931337658SMarcel Moolenaar { 463031337658SMarcel Moolenaar return xo_open_list_hf(xop, XOF_DTRT, name); 463131337658SMarcel Moolenaar } 463231337658SMarcel Moolenaar 463331337658SMarcel Moolenaar int 463431337658SMarcel Moolenaar xo_open_list_d (const char *name) 463531337658SMarcel Moolenaar { 463631337658SMarcel Moolenaar return xo_open_list_hf(NULL, XOF_DTRT, name); 463731337658SMarcel Moolenaar } 463831337658SMarcel Moolenaar 4639545ddfbeSMarcel Moolenaar static int 4640545ddfbeSMarcel Moolenaar xo_do_close_list (xo_handle_t *xop, const char *name) 464131337658SMarcel Moolenaar { 464231337658SMarcel Moolenaar int rc = 0; 464331337658SMarcel Moolenaar const char *pre_nl = ""; 464431337658SMarcel Moolenaar 464531337658SMarcel Moolenaar if (name == NULL) { 464631337658SMarcel Moolenaar xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth]; 464731337658SMarcel Moolenaar 464831337658SMarcel Moolenaar name = xsp->xs_name; 464931337658SMarcel Moolenaar if (name) { 465031337658SMarcel Moolenaar int len = strlen(name) + 1; 465131337658SMarcel Moolenaar /* We need to make a local copy; xo_depth_change will free it */ 465231337658SMarcel Moolenaar char *cp = alloca(len); 465331337658SMarcel Moolenaar memcpy(cp, name, len); 465431337658SMarcel Moolenaar name = cp; 4655545ddfbeSMarcel Moolenaar } else if (!(xsp->xs_flags & XSF_DTRT)) { 4656545ddfbeSMarcel Moolenaar xo_failure(xop, "missing name without 'dtrt' mode"); 465731337658SMarcel Moolenaar name = XO_FAILURE_NAME; 465831337658SMarcel Moolenaar } 4659545ddfbeSMarcel Moolenaar } 466031337658SMarcel Moolenaar 4661788ca347SMarcel Moolenaar if (xo_style(xop) == XO_STYLE_JSON) { 466231337658SMarcel Moolenaar if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST) 466331337658SMarcel Moolenaar pre_nl = (xop->xo_flags & XOF_PRETTY) ? "\n" : ""; 466431337658SMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 466531337658SMarcel Moolenaar 4666545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, -1, -1, XSS_CLOSE_LIST, XSF_LIST); 466731337658SMarcel Moolenaar rc = xo_printf(xop, "%s%*s]", pre_nl, xo_indent(xop), ""); 466831337658SMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 466931337658SMarcel Moolenaar 4670545ddfbeSMarcel Moolenaar } else { 4671545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, -1, 0, XSS_CLOSE_LIST, XSF_LIST); 4672545ddfbeSMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 4673545ddfbeSMarcel Moolenaar } 4674545ddfbeSMarcel Moolenaar 4675a0f704ffSMarcel Moolenaar return rc; 467631337658SMarcel Moolenaar } 467731337658SMarcel Moolenaar 467831337658SMarcel Moolenaar int 4679545ddfbeSMarcel Moolenaar xo_close_list_h (xo_handle_t *xop, const char *name) 4680545ddfbeSMarcel Moolenaar { 4681545ddfbeSMarcel Moolenaar return xo_transition(xop, 0, name, XSS_CLOSE_LIST); 4682545ddfbeSMarcel Moolenaar } 4683545ddfbeSMarcel Moolenaar 4684545ddfbeSMarcel Moolenaar int 468531337658SMarcel Moolenaar xo_close_list (const char *name) 468631337658SMarcel Moolenaar { 468731337658SMarcel Moolenaar return xo_close_list_h(NULL, name); 468831337658SMarcel Moolenaar } 468931337658SMarcel Moolenaar 469031337658SMarcel Moolenaar int 469131337658SMarcel Moolenaar xo_close_list_hd (xo_handle_t *xop) 469231337658SMarcel Moolenaar { 469331337658SMarcel Moolenaar return xo_close_list_h(xop, NULL); 469431337658SMarcel Moolenaar } 469531337658SMarcel Moolenaar 469631337658SMarcel Moolenaar int 469731337658SMarcel Moolenaar xo_close_list_d (void) 469831337658SMarcel Moolenaar { 469931337658SMarcel Moolenaar return xo_close_list_h(NULL, NULL); 470031337658SMarcel Moolenaar } 470131337658SMarcel Moolenaar 470231337658SMarcel Moolenaar static int 4703545ddfbeSMarcel Moolenaar xo_do_open_leaf_list (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name) 4704545ddfbeSMarcel Moolenaar { 4705545ddfbeSMarcel Moolenaar int rc = 0; 4706545ddfbeSMarcel Moolenaar int indent = 0; 4707545ddfbeSMarcel Moolenaar 4708545ddfbeSMarcel Moolenaar xop = xo_default(xop); 4709545ddfbeSMarcel Moolenaar 4710788ca347SMarcel Moolenaar if (xo_style(xop) == XO_STYLE_JSON) { 4711545ddfbeSMarcel Moolenaar const char *ppn = (xop->xo_flags & XOF_PRETTY) ? "\n" : ""; 4712545ddfbeSMarcel Moolenaar const char *pre_nl = ""; 4713545ddfbeSMarcel Moolenaar 4714545ddfbeSMarcel Moolenaar indent = 1; 4715545ddfbeSMarcel Moolenaar 4716545ddfbeSMarcel Moolenaar if (!(xop->xo_flags & XOF_NO_TOP)) { 4717545ddfbeSMarcel Moolenaar if (!(xop->xo_flags & XOF_TOP_EMITTED)) { 4718545ddfbeSMarcel Moolenaar xo_printf(xop, "%*s{%s", xo_indent(xop), "", ppn); 4719545ddfbeSMarcel Moolenaar xop->xo_flags |= XOF_TOP_EMITTED; 4720545ddfbeSMarcel Moolenaar } 4721545ddfbeSMarcel Moolenaar } 4722545ddfbeSMarcel Moolenaar 4723545ddfbeSMarcel Moolenaar if (name == NULL) { 4724545ddfbeSMarcel Moolenaar xo_failure(xop, "NULL passed for list name"); 4725545ddfbeSMarcel Moolenaar name = XO_FAILURE_NAME; 4726545ddfbeSMarcel Moolenaar } 4727545ddfbeSMarcel Moolenaar 4728545ddfbeSMarcel Moolenaar xo_stack_set_flags(xop); 4729545ddfbeSMarcel Moolenaar 4730545ddfbeSMarcel Moolenaar if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST) 4731545ddfbeSMarcel Moolenaar pre_nl = (xop->xo_flags & XOF_PRETTY) ? ",\n" : ", "; 4732545ddfbeSMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 4733545ddfbeSMarcel Moolenaar 4734545ddfbeSMarcel Moolenaar rc = xo_printf(xop, "%s%*s\"%s\": [%s", 4735545ddfbeSMarcel Moolenaar pre_nl, xo_indent(xop), "", name, ppn); 4736545ddfbeSMarcel Moolenaar } 4737545ddfbeSMarcel Moolenaar 4738545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, 1, indent, XSS_OPEN_LEAF_LIST, 4739545ddfbeSMarcel Moolenaar XSF_LIST | xo_stack_flags(flags)); 4740545ddfbeSMarcel Moolenaar 4741545ddfbeSMarcel Moolenaar return rc; 4742545ddfbeSMarcel Moolenaar } 4743545ddfbeSMarcel Moolenaar 4744545ddfbeSMarcel Moolenaar static int 4745545ddfbeSMarcel Moolenaar xo_do_close_leaf_list (xo_handle_t *xop, const char *name) 4746545ddfbeSMarcel Moolenaar { 4747545ddfbeSMarcel Moolenaar int rc = 0; 4748545ddfbeSMarcel Moolenaar const char *pre_nl = ""; 4749545ddfbeSMarcel Moolenaar 4750545ddfbeSMarcel Moolenaar if (name == NULL) { 4751545ddfbeSMarcel Moolenaar xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth]; 4752545ddfbeSMarcel Moolenaar 4753545ddfbeSMarcel Moolenaar name = xsp->xs_name; 4754545ddfbeSMarcel Moolenaar if (name) { 4755545ddfbeSMarcel Moolenaar int len = strlen(name) + 1; 4756545ddfbeSMarcel Moolenaar /* We need to make a local copy; xo_depth_change will free it */ 4757545ddfbeSMarcel Moolenaar char *cp = alloca(len); 4758545ddfbeSMarcel Moolenaar memcpy(cp, name, len); 4759545ddfbeSMarcel Moolenaar name = cp; 4760545ddfbeSMarcel Moolenaar } else if (!(xsp->xs_flags & XSF_DTRT)) { 4761545ddfbeSMarcel Moolenaar xo_failure(xop, "missing name without 'dtrt' mode"); 4762545ddfbeSMarcel Moolenaar name = XO_FAILURE_NAME; 4763545ddfbeSMarcel Moolenaar } 4764545ddfbeSMarcel Moolenaar } 4765545ddfbeSMarcel Moolenaar 4766788ca347SMarcel Moolenaar if (xo_style(xop) == XO_STYLE_JSON) { 4767545ddfbeSMarcel Moolenaar if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST) 4768545ddfbeSMarcel Moolenaar pre_nl = (xop->xo_flags & XOF_PRETTY) ? "\n" : ""; 4769545ddfbeSMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 4770545ddfbeSMarcel Moolenaar 4771545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, -1, -1, XSS_CLOSE_LEAF_LIST, XSF_LIST); 4772545ddfbeSMarcel Moolenaar rc = xo_printf(xop, "%s%*s]", pre_nl, xo_indent(xop), ""); 4773545ddfbeSMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 4774545ddfbeSMarcel Moolenaar 4775545ddfbeSMarcel Moolenaar } else { 4776545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, -1, 0, XSS_CLOSE_LEAF_LIST, XSF_LIST); 4777545ddfbeSMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 4778545ddfbeSMarcel Moolenaar } 4779545ddfbeSMarcel Moolenaar 4780545ddfbeSMarcel Moolenaar return rc; 4781545ddfbeSMarcel Moolenaar } 4782545ddfbeSMarcel Moolenaar 4783545ddfbeSMarcel Moolenaar static int 4784545ddfbeSMarcel Moolenaar xo_do_open_instance (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name) 478531337658SMarcel Moolenaar { 478631337658SMarcel Moolenaar xop = xo_default(xop); 478731337658SMarcel Moolenaar 478831337658SMarcel Moolenaar int rc = 0; 478931337658SMarcel Moolenaar const char *ppn = (xop->xo_flags & XOF_PRETTY) ? "\n" : ""; 479031337658SMarcel Moolenaar const char *pre_nl = ""; 479131337658SMarcel Moolenaar 479231337658SMarcel Moolenaar flags |= xop->xo_flags; 479331337658SMarcel Moolenaar 479431337658SMarcel Moolenaar if (name == NULL) { 479531337658SMarcel Moolenaar xo_failure(xop, "NULL passed for instance name"); 479631337658SMarcel Moolenaar name = XO_FAILURE_NAME; 479731337658SMarcel Moolenaar } 479831337658SMarcel Moolenaar 4799788ca347SMarcel Moolenaar switch (xo_style(xop)) { 480031337658SMarcel Moolenaar case XO_STYLE_XML: 4801545ddfbeSMarcel Moolenaar rc = xo_printf(xop, "%*s<%s", xo_indent(xop), "", name); 4802545ddfbeSMarcel Moolenaar 4803545ddfbeSMarcel Moolenaar if (xop->xo_attrs.xb_curp != xop->xo_attrs.xb_bufp) { 4804545ddfbeSMarcel Moolenaar rc += xop->xo_attrs.xb_curp - xop->xo_attrs.xb_bufp; 4805545ddfbeSMarcel Moolenaar xo_data_append(xop, xop->xo_attrs.xb_bufp, 4806545ddfbeSMarcel Moolenaar xop->xo_attrs.xb_curp - xop->xo_attrs.xb_bufp); 4807545ddfbeSMarcel Moolenaar xop->xo_attrs.xb_curp = xop->xo_attrs.xb_bufp; 4808545ddfbeSMarcel Moolenaar } 4809545ddfbeSMarcel Moolenaar 4810545ddfbeSMarcel Moolenaar rc += xo_printf(xop, ">%s", ppn); 481131337658SMarcel Moolenaar break; 481231337658SMarcel Moolenaar 481331337658SMarcel Moolenaar case XO_STYLE_JSON: 481431337658SMarcel Moolenaar xo_stack_set_flags(xop); 481531337658SMarcel Moolenaar 481631337658SMarcel Moolenaar if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST) 481731337658SMarcel Moolenaar pre_nl = (xop->xo_flags & XOF_PRETTY) ? ",\n" : ", "; 481831337658SMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 481931337658SMarcel Moolenaar 482031337658SMarcel Moolenaar rc = xo_printf(xop, "%s%*s{%s", 482131337658SMarcel Moolenaar pre_nl, xo_indent(xop), "", ppn); 482231337658SMarcel Moolenaar break; 482331337658SMarcel Moolenaar } 482431337658SMarcel Moolenaar 4825545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, 1, 1, XSS_OPEN_INSTANCE, xo_stack_flags(flags)); 4826545ddfbeSMarcel Moolenaar 482731337658SMarcel Moolenaar return rc; 482831337658SMarcel Moolenaar } 482931337658SMarcel Moolenaar 4830545ddfbeSMarcel Moolenaar static int 4831545ddfbeSMarcel Moolenaar xo_open_instance_hf (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name) 4832545ddfbeSMarcel Moolenaar { 4833545ddfbeSMarcel Moolenaar return xo_transition(xop, flags, name, XSS_OPEN_INSTANCE); 4834545ddfbeSMarcel Moolenaar } 4835545ddfbeSMarcel Moolenaar 483631337658SMarcel Moolenaar int 483731337658SMarcel Moolenaar xo_open_instance_h (xo_handle_t *xop, const char *name) 483831337658SMarcel Moolenaar { 483931337658SMarcel Moolenaar return xo_open_instance_hf(xop, 0, name); 484031337658SMarcel Moolenaar } 484131337658SMarcel Moolenaar 484231337658SMarcel Moolenaar int 484331337658SMarcel Moolenaar xo_open_instance (const char *name) 484431337658SMarcel Moolenaar { 484531337658SMarcel Moolenaar return xo_open_instance_hf(NULL, 0, name); 484631337658SMarcel Moolenaar } 484731337658SMarcel Moolenaar 484831337658SMarcel Moolenaar int 484931337658SMarcel Moolenaar xo_open_instance_hd (xo_handle_t *xop, const char *name) 485031337658SMarcel Moolenaar { 485131337658SMarcel Moolenaar return xo_open_instance_hf(xop, XOF_DTRT, name); 485231337658SMarcel Moolenaar } 485331337658SMarcel Moolenaar 485431337658SMarcel Moolenaar int 485531337658SMarcel Moolenaar xo_open_instance_d (const char *name) 485631337658SMarcel Moolenaar { 485731337658SMarcel Moolenaar return xo_open_instance_hf(NULL, XOF_DTRT, name); 485831337658SMarcel Moolenaar } 485931337658SMarcel Moolenaar 4860545ddfbeSMarcel Moolenaar static int 4861545ddfbeSMarcel Moolenaar xo_do_close_instance (xo_handle_t *xop, const char *name) 486231337658SMarcel Moolenaar { 486331337658SMarcel Moolenaar xop = xo_default(xop); 486431337658SMarcel Moolenaar 486531337658SMarcel Moolenaar int rc = 0; 486631337658SMarcel Moolenaar const char *ppn = (xop->xo_flags & XOF_PRETTY) ? "\n" : ""; 486731337658SMarcel Moolenaar const char *pre_nl = ""; 486831337658SMarcel Moolenaar 486931337658SMarcel Moolenaar if (name == NULL) { 487031337658SMarcel Moolenaar xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth]; 487131337658SMarcel Moolenaar 487231337658SMarcel Moolenaar name = xsp->xs_name; 487331337658SMarcel Moolenaar if (name) { 487431337658SMarcel Moolenaar int len = strlen(name) + 1; 487531337658SMarcel Moolenaar /* We need to make a local copy; xo_depth_change will free it */ 487631337658SMarcel Moolenaar char *cp = alloca(len); 487731337658SMarcel Moolenaar memcpy(cp, name, len); 487831337658SMarcel Moolenaar name = cp; 4879545ddfbeSMarcel Moolenaar } else if (!(xsp->xs_flags & XSF_DTRT)) { 4880545ddfbeSMarcel Moolenaar xo_failure(xop, "missing name without 'dtrt' mode"); 488131337658SMarcel Moolenaar name = XO_FAILURE_NAME; 488231337658SMarcel Moolenaar } 4883545ddfbeSMarcel Moolenaar } 488431337658SMarcel Moolenaar 4885788ca347SMarcel Moolenaar switch (xo_style(xop)) { 488631337658SMarcel Moolenaar case XO_STYLE_XML: 4887545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, -1, -1, XSS_CLOSE_INSTANCE, 0); 488831337658SMarcel Moolenaar rc = xo_printf(xop, "%*s</%s>%s", xo_indent(xop), "", name, ppn); 488931337658SMarcel Moolenaar break; 489031337658SMarcel Moolenaar 489131337658SMarcel Moolenaar case XO_STYLE_JSON: 489231337658SMarcel Moolenaar pre_nl = (xop->xo_flags & XOF_PRETTY) ? "\n" : ""; 489331337658SMarcel Moolenaar 4894545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, -1, -1, XSS_CLOSE_INSTANCE, 0); 489531337658SMarcel Moolenaar rc = xo_printf(xop, "%s%*s}", pre_nl, xo_indent(xop), ""); 489631337658SMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 489731337658SMarcel Moolenaar break; 489831337658SMarcel Moolenaar 489931337658SMarcel Moolenaar case XO_STYLE_HTML: 490031337658SMarcel Moolenaar case XO_STYLE_TEXT: 4901545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, -1, 0, XSS_CLOSE_INSTANCE, 0); 490231337658SMarcel Moolenaar break; 490331337658SMarcel Moolenaar } 490431337658SMarcel Moolenaar 490531337658SMarcel Moolenaar return rc; 490631337658SMarcel Moolenaar } 490731337658SMarcel Moolenaar 490831337658SMarcel Moolenaar int 4909545ddfbeSMarcel Moolenaar xo_close_instance_h (xo_handle_t *xop, const char *name) 4910545ddfbeSMarcel Moolenaar { 4911545ddfbeSMarcel Moolenaar return xo_transition(xop, 0, name, XSS_CLOSE_INSTANCE); 4912545ddfbeSMarcel Moolenaar } 4913545ddfbeSMarcel Moolenaar 4914545ddfbeSMarcel Moolenaar int 491531337658SMarcel Moolenaar xo_close_instance (const char *name) 491631337658SMarcel Moolenaar { 491731337658SMarcel Moolenaar return xo_close_instance_h(NULL, name); 491831337658SMarcel Moolenaar } 491931337658SMarcel Moolenaar 492031337658SMarcel Moolenaar int 492131337658SMarcel Moolenaar xo_close_instance_hd (xo_handle_t *xop) 492231337658SMarcel Moolenaar { 492331337658SMarcel Moolenaar return xo_close_instance_h(xop, NULL); 492431337658SMarcel Moolenaar } 492531337658SMarcel Moolenaar 492631337658SMarcel Moolenaar int 492731337658SMarcel Moolenaar xo_close_instance_d (void) 492831337658SMarcel Moolenaar { 492931337658SMarcel Moolenaar return xo_close_instance_h(NULL, NULL); 493031337658SMarcel Moolenaar } 493131337658SMarcel Moolenaar 4932545ddfbeSMarcel Moolenaar static int 4933545ddfbeSMarcel Moolenaar xo_do_close_all (xo_handle_t *xop, xo_stack_t *limit) 4934545ddfbeSMarcel Moolenaar { 4935545ddfbeSMarcel Moolenaar xo_stack_t *xsp; 4936545ddfbeSMarcel Moolenaar int rc = 0; 4937545ddfbeSMarcel Moolenaar xo_xsf_flags_t flags; 4938545ddfbeSMarcel Moolenaar 4939545ddfbeSMarcel Moolenaar for (xsp = &xop->xo_stack[xop->xo_depth]; xsp >= limit; xsp--) { 4940545ddfbeSMarcel Moolenaar switch (xsp->xs_state) { 4941545ddfbeSMarcel Moolenaar case XSS_INIT: 4942545ddfbeSMarcel Moolenaar /* Nothing */ 4943545ddfbeSMarcel Moolenaar rc = 0; 4944545ddfbeSMarcel Moolenaar break; 4945545ddfbeSMarcel Moolenaar 4946545ddfbeSMarcel Moolenaar case XSS_OPEN_CONTAINER: 4947545ddfbeSMarcel Moolenaar rc = xo_do_close_container(xop, NULL); 4948545ddfbeSMarcel Moolenaar break; 4949545ddfbeSMarcel Moolenaar 4950545ddfbeSMarcel Moolenaar case XSS_OPEN_LIST: 4951545ddfbeSMarcel Moolenaar rc = xo_do_close_list(xop, NULL); 4952545ddfbeSMarcel Moolenaar break; 4953545ddfbeSMarcel Moolenaar 4954545ddfbeSMarcel Moolenaar case XSS_OPEN_INSTANCE: 4955545ddfbeSMarcel Moolenaar rc = xo_do_close_instance(xop, NULL); 4956545ddfbeSMarcel Moolenaar break; 4957545ddfbeSMarcel Moolenaar 4958545ddfbeSMarcel Moolenaar case XSS_OPEN_LEAF_LIST: 4959545ddfbeSMarcel Moolenaar rc = xo_do_close_leaf_list(xop, NULL); 4960545ddfbeSMarcel Moolenaar break; 4961545ddfbeSMarcel Moolenaar 4962545ddfbeSMarcel Moolenaar case XSS_MARKER: 4963545ddfbeSMarcel Moolenaar flags = xsp->xs_flags & XSF_MARKER_FLAGS; 4964545ddfbeSMarcel Moolenaar xo_depth_change(xop, xsp->xs_name, -1, 0, XSS_MARKER, 0); 4965545ddfbeSMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= flags; 4966545ddfbeSMarcel Moolenaar rc = 0; 4967545ddfbeSMarcel Moolenaar break; 4968545ddfbeSMarcel Moolenaar } 4969545ddfbeSMarcel Moolenaar 4970545ddfbeSMarcel Moolenaar if (rc < 0) 4971545ddfbeSMarcel Moolenaar xo_failure(xop, "close %d failed: %d", xsp->xs_state, rc); 4972545ddfbeSMarcel Moolenaar } 4973545ddfbeSMarcel Moolenaar 4974545ddfbeSMarcel Moolenaar return 0; 4975545ddfbeSMarcel Moolenaar } 4976545ddfbeSMarcel Moolenaar 4977545ddfbeSMarcel Moolenaar /* 4978545ddfbeSMarcel Moolenaar * This function is responsible for clearing out whatever is needed 4979545ddfbeSMarcel Moolenaar * to get to the desired state, if possible. 4980545ddfbeSMarcel Moolenaar */ 4981545ddfbeSMarcel Moolenaar static int 4982545ddfbeSMarcel Moolenaar xo_do_close (xo_handle_t *xop, const char *name, xo_state_t new_state) 4983545ddfbeSMarcel Moolenaar { 4984545ddfbeSMarcel Moolenaar xo_stack_t *xsp, *limit = NULL; 4985545ddfbeSMarcel Moolenaar int rc; 4986545ddfbeSMarcel Moolenaar xo_state_t need_state = new_state; 4987545ddfbeSMarcel Moolenaar 4988545ddfbeSMarcel Moolenaar if (new_state == XSS_CLOSE_CONTAINER) 4989545ddfbeSMarcel Moolenaar need_state = XSS_OPEN_CONTAINER; 4990545ddfbeSMarcel Moolenaar else if (new_state == XSS_CLOSE_LIST) 4991545ddfbeSMarcel Moolenaar need_state = XSS_OPEN_LIST; 4992545ddfbeSMarcel Moolenaar else if (new_state == XSS_CLOSE_INSTANCE) 4993545ddfbeSMarcel Moolenaar need_state = XSS_OPEN_INSTANCE; 4994545ddfbeSMarcel Moolenaar else if (new_state == XSS_CLOSE_LEAF_LIST) 4995545ddfbeSMarcel Moolenaar need_state = XSS_OPEN_LEAF_LIST; 4996545ddfbeSMarcel Moolenaar else if (new_state == XSS_MARKER) 4997545ddfbeSMarcel Moolenaar need_state = XSS_MARKER; 4998545ddfbeSMarcel Moolenaar else 4999545ddfbeSMarcel Moolenaar return 0; /* Unknown or useless new states are ignored */ 5000545ddfbeSMarcel Moolenaar 5001545ddfbeSMarcel Moolenaar for (xsp = &xop->xo_stack[xop->xo_depth]; xsp > xop->xo_stack; xsp--) { 5002545ddfbeSMarcel Moolenaar /* 5003545ddfbeSMarcel Moolenaar * Marker's normally stop us from going any further, unless 5004545ddfbeSMarcel Moolenaar * we are popping a marker (new_state == XSS_MARKER). 5005545ddfbeSMarcel Moolenaar */ 5006545ddfbeSMarcel Moolenaar if (xsp->xs_state == XSS_MARKER && need_state != XSS_MARKER) { 5007545ddfbeSMarcel Moolenaar if (name) { 5008545ddfbeSMarcel Moolenaar xo_failure(xop, "close (xo_%s) fails at marker '%s'; " 5009545ddfbeSMarcel Moolenaar "not found '%s'", 5010545ddfbeSMarcel Moolenaar xo_state_name(new_state), 5011545ddfbeSMarcel Moolenaar xsp->xs_name, name); 5012545ddfbeSMarcel Moolenaar return 0; 5013545ddfbeSMarcel Moolenaar 5014545ddfbeSMarcel Moolenaar } else { 5015545ddfbeSMarcel Moolenaar limit = xsp; 5016545ddfbeSMarcel Moolenaar xo_failure(xop, "close stops at marker '%s'", xsp->xs_name); 5017545ddfbeSMarcel Moolenaar } 5018545ddfbeSMarcel Moolenaar break; 5019545ddfbeSMarcel Moolenaar } 5020545ddfbeSMarcel Moolenaar 5021545ddfbeSMarcel Moolenaar if (xsp->xs_state != need_state) 5022545ddfbeSMarcel Moolenaar continue; 5023545ddfbeSMarcel Moolenaar 5024545ddfbeSMarcel Moolenaar if (name && xsp->xs_name && strcmp(name, xsp->xs_name) != 0) 5025545ddfbeSMarcel Moolenaar continue; 5026545ddfbeSMarcel Moolenaar 5027545ddfbeSMarcel Moolenaar limit = xsp; 5028545ddfbeSMarcel Moolenaar break; 5029545ddfbeSMarcel Moolenaar } 5030545ddfbeSMarcel Moolenaar 5031545ddfbeSMarcel Moolenaar if (limit == NULL) { 5032545ddfbeSMarcel Moolenaar xo_failure(xop, "xo_%s can't find match for '%s'", 5033545ddfbeSMarcel Moolenaar xo_state_name(new_state), name); 5034545ddfbeSMarcel Moolenaar return 0; 5035545ddfbeSMarcel Moolenaar } 5036545ddfbeSMarcel Moolenaar 5037545ddfbeSMarcel Moolenaar rc = xo_do_close_all(xop, limit); 5038545ddfbeSMarcel Moolenaar 5039545ddfbeSMarcel Moolenaar return rc; 5040545ddfbeSMarcel Moolenaar } 5041545ddfbeSMarcel Moolenaar 5042545ddfbeSMarcel Moolenaar /* 5043545ddfbeSMarcel Moolenaar * We are in a given state and need to transition to the new state. 5044545ddfbeSMarcel Moolenaar */ 5045545ddfbeSMarcel Moolenaar static int 5046545ddfbeSMarcel Moolenaar xo_transition (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name, 5047545ddfbeSMarcel Moolenaar xo_state_t new_state) 5048545ddfbeSMarcel Moolenaar { 5049545ddfbeSMarcel Moolenaar xo_stack_t *xsp; 5050545ddfbeSMarcel Moolenaar int rc; 5051545ddfbeSMarcel Moolenaar int old_state, on_marker; 5052545ddfbeSMarcel Moolenaar 5053545ddfbeSMarcel Moolenaar xop = xo_default(xop); 5054545ddfbeSMarcel Moolenaar 5055545ddfbeSMarcel Moolenaar rc = 0; 5056545ddfbeSMarcel Moolenaar xsp = &xop->xo_stack[xop->xo_depth]; 5057545ddfbeSMarcel Moolenaar old_state = xsp->xs_state; 5058545ddfbeSMarcel Moolenaar on_marker = (old_state == XSS_MARKER); 5059545ddfbeSMarcel Moolenaar 5060545ddfbeSMarcel Moolenaar /* If there's a marker on top of the stack, we need to find a real state */ 5061545ddfbeSMarcel Moolenaar while (old_state == XSS_MARKER) { 5062545ddfbeSMarcel Moolenaar if (xsp == xop->xo_stack) 5063545ddfbeSMarcel Moolenaar break; 5064545ddfbeSMarcel Moolenaar xsp -= 1; 5065545ddfbeSMarcel Moolenaar old_state = xsp->xs_state; 5066545ddfbeSMarcel Moolenaar } 5067545ddfbeSMarcel Moolenaar 5068545ddfbeSMarcel Moolenaar /* 5069545ddfbeSMarcel Moolenaar * At this point, the list of possible states are: 5070545ddfbeSMarcel Moolenaar * XSS_INIT, XSS_OPEN_CONTAINER, XSS_OPEN_LIST, 5071545ddfbeSMarcel Moolenaar * XSS_OPEN_INSTANCE, XSS_OPEN_LEAF_LIST, XSS_DISCARDING 5072545ddfbeSMarcel Moolenaar */ 5073545ddfbeSMarcel Moolenaar switch (XSS_TRANSITION(old_state, new_state)) { 5074545ddfbeSMarcel Moolenaar 5075545ddfbeSMarcel Moolenaar open_container: 5076545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_INIT, XSS_OPEN_CONTAINER): 5077545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_OPEN_CONTAINER): 5078545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_OPEN_CONTAINER): 5079545ddfbeSMarcel Moolenaar rc = xo_do_open_container(xop, flags, name); 5080545ddfbeSMarcel Moolenaar break; 5081545ddfbeSMarcel Moolenaar 5082545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LIST, XSS_OPEN_CONTAINER): 5083545ddfbeSMarcel Moolenaar if (on_marker) 5084545ddfbeSMarcel Moolenaar goto marker_prevents_close; 5085545ddfbeSMarcel Moolenaar rc = xo_do_close_list(xop, NULL); 5086545ddfbeSMarcel Moolenaar if (rc >= 0) 5087545ddfbeSMarcel Moolenaar goto open_container; 5088545ddfbeSMarcel Moolenaar break; 5089545ddfbeSMarcel Moolenaar 5090545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_OPEN_CONTAINER): 5091545ddfbeSMarcel Moolenaar if (on_marker) 5092545ddfbeSMarcel Moolenaar goto marker_prevents_close; 5093545ddfbeSMarcel Moolenaar rc = xo_do_close_leaf_list(xop, NULL); 5094545ddfbeSMarcel Moolenaar if (rc >= 0) 5095545ddfbeSMarcel Moolenaar goto open_container; 5096545ddfbeSMarcel Moolenaar break; 5097545ddfbeSMarcel Moolenaar 5098545ddfbeSMarcel Moolenaar /*close_container:*/ 5099545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_CLOSE_CONTAINER): 5100545ddfbeSMarcel Moolenaar if (on_marker) 5101545ddfbeSMarcel Moolenaar goto marker_prevents_close; 5102545ddfbeSMarcel Moolenaar rc = xo_do_close(xop, name, new_state); 5103545ddfbeSMarcel Moolenaar break; 5104545ddfbeSMarcel Moolenaar 5105545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_INIT, XSS_CLOSE_CONTAINER): 5106545ddfbeSMarcel Moolenaar /* This is an exception for "xo --close" */ 5107545ddfbeSMarcel Moolenaar rc = xo_do_close_container(xop, name); 5108545ddfbeSMarcel Moolenaar break; 5109545ddfbeSMarcel Moolenaar 5110545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LIST, XSS_CLOSE_CONTAINER): 5111545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_CLOSE_CONTAINER): 5112545ddfbeSMarcel Moolenaar if (on_marker) 5113545ddfbeSMarcel Moolenaar goto marker_prevents_close; 5114545ddfbeSMarcel Moolenaar rc = xo_do_close(xop, name, new_state); 5115545ddfbeSMarcel Moolenaar break; 5116545ddfbeSMarcel Moolenaar 5117545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_CLOSE_CONTAINER): 5118545ddfbeSMarcel Moolenaar if (on_marker) 5119545ddfbeSMarcel Moolenaar goto marker_prevents_close; 5120545ddfbeSMarcel Moolenaar rc = xo_do_close_leaf_list(xop, NULL); 5121545ddfbeSMarcel Moolenaar if (rc >= 0) 5122545ddfbeSMarcel Moolenaar rc = xo_do_close(xop, name, new_state); 5123545ddfbeSMarcel Moolenaar break; 5124545ddfbeSMarcel Moolenaar 5125545ddfbeSMarcel Moolenaar open_list: 5126545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_INIT, XSS_OPEN_LIST): 5127545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_OPEN_LIST): 5128545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_OPEN_LIST): 5129545ddfbeSMarcel Moolenaar rc = xo_do_open_list(xop, flags, name); 5130545ddfbeSMarcel Moolenaar break; 5131545ddfbeSMarcel Moolenaar 5132545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LIST, XSS_OPEN_LIST): 5133545ddfbeSMarcel Moolenaar if (on_marker) 5134545ddfbeSMarcel Moolenaar goto marker_prevents_close; 5135545ddfbeSMarcel Moolenaar rc = xo_do_close_list(xop, NULL); 5136545ddfbeSMarcel Moolenaar if (rc >= 0) 5137545ddfbeSMarcel Moolenaar goto open_list; 5138545ddfbeSMarcel Moolenaar break; 5139545ddfbeSMarcel Moolenaar 5140545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_OPEN_LIST): 5141545ddfbeSMarcel Moolenaar if (on_marker) 5142545ddfbeSMarcel Moolenaar goto marker_prevents_close; 5143545ddfbeSMarcel Moolenaar rc = xo_do_close_leaf_list(xop, NULL); 5144545ddfbeSMarcel Moolenaar if (rc >= 0) 5145545ddfbeSMarcel Moolenaar goto open_list; 5146545ddfbeSMarcel Moolenaar break; 5147545ddfbeSMarcel Moolenaar 5148545ddfbeSMarcel Moolenaar /*close_list:*/ 5149545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LIST, XSS_CLOSE_LIST): 5150545ddfbeSMarcel Moolenaar if (on_marker) 5151545ddfbeSMarcel Moolenaar goto marker_prevents_close; 5152545ddfbeSMarcel Moolenaar rc = xo_do_close(xop, name, new_state); 5153545ddfbeSMarcel Moolenaar break; 5154545ddfbeSMarcel Moolenaar 5155545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_INIT, XSS_CLOSE_LIST): 5156545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_CLOSE_LIST): 5157545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_CLOSE_LIST): 5158545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_CLOSE_LIST): 5159545ddfbeSMarcel Moolenaar rc = xo_do_close(xop, name, new_state); 5160545ddfbeSMarcel Moolenaar break; 5161545ddfbeSMarcel Moolenaar 5162545ddfbeSMarcel Moolenaar open_instance: 5163545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LIST, XSS_OPEN_INSTANCE): 5164545ddfbeSMarcel Moolenaar rc = xo_do_open_instance(xop, flags, name); 5165545ddfbeSMarcel Moolenaar break; 5166545ddfbeSMarcel Moolenaar 5167545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_INIT, XSS_OPEN_INSTANCE): 5168788ca347SMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_OPEN_INSTANCE): 5169545ddfbeSMarcel Moolenaar rc = xo_do_open_list(xop, flags, name); 5170545ddfbeSMarcel Moolenaar if (rc >= 0) 5171545ddfbeSMarcel Moolenaar goto open_instance; 5172545ddfbeSMarcel Moolenaar break; 5173545ddfbeSMarcel Moolenaar 5174545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_OPEN_INSTANCE): 5175545ddfbeSMarcel Moolenaar if (on_marker) { 5176545ddfbeSMarcel Moolenaar rc = xo_do_open_list(xop, flags, name); 5177545ddfbeSMarcel Moolenaar } else { 5178545ddfbeSMarcel Moolenaar rc = xo_do_close_instance(xop, NULL); 5179545ddfbeSMarcel Moolenaar } 5180545ddfbeSMarcel Moolenaar if (rc >= 0) 5181545ddfbeSMarcel Moolenaar goto open_instance; 5182545ddfbeSMarcel Moolenaar break; 5183545ddfbeSMarcel Moolenaar 5184545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_OPEN_INSTANCE): 5185545ddfbeSMarcel Moolenaar if (on_marker) 5186545ddfbeSMarcel Moolenaar goto marker_prevents_close; 5187545ddfbeSMarcel Moolenaar rc = xo_do_close_leaf_list(xop, NULL); 5188545ddfbeSMarcel Moolenaar if (rc >= 0) 5189545ddfbeSMarcel Moolenaar goto open_instance; 5190545ddfbeSMarcel Moolenaar break; 5191545ddfbeSMarcel Moolenaar 5192545ddfbeSMarcel Moolenaar /*close_instance:*/ 5193545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_CLOSE_INSTANCE): 5194545ddfbeSMarcel Moolenaar if (on_marker) 5195545ddfbeSMarcel Moolenaar goto marker_prevents_close; 5196545ddfbeSMarcel Moolenaar rc = xo_do_close_instance(xop, name); 5197545ddfbeSMarcel Moolenaar break; 5198545ddfbeSMarcel Moolenaar 5199545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_INIT, XSS_CLOSE_INSTANCE): 5200545ddfbeSMarcel Moolenaar /* This one makes no sense; ignore it */ 5201788ca347SMarcel Moolenaar xo_failure(xop, "xo_close_instance ignored when called from " 5202788ca347SMarcel Moolenaar "initial state ('%s')", name ?: "(unknown)"); 5203545ddfbeSMarcel Moolenaar break; 5204545ddfbeSMarcel Moolenaar 5205545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_CLOSE_INSTANCE): 5206545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LIST, XSS_CLOSE_INSTANCE): 5207545ddfbeSMarcel Moolenaar if (on_marker) 5208545ddfbeSMarcel Moolenaar goto marker_prevents_close; 5209545ddfbeSMarcel Moolenaar rc = xo_do_close(xop, name, new_state); 5210545ddfbeSMarcel Moolenaar break; 5211545ddfbeSMarcel Moolenaar 5212545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_CLOSE_INSTANCE): 5213545ddfbeSMarcel Moolenaar if (on_marker) 5214545ddfbeSMarcel Moolenaar goto marker_prevents_close; 5215545ddfbeSMarcel Moolenaar rc = xo_do_close_leaf_list(xop, NULL); 5216545ddfbeSMarcel Moolenaar if (rc >= 0) 5217545ddfbeSMarcel Moolenaar rc = xo_do_close(xop, name, new_state); 5218545ddfbeSMarcel Moolenaar break; 5219545ddfbeSMarcel Moolenaar 5220545ddfbeSMarcel Moolenaar open_leaf_list: 5221545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_OPEN_LEAF_LIST): 5222545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_OPEN_LEAF_LIST): 5223545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_INIT, XSS_OPEN_LEAF_LIST): 5224545ddfbeSMarcel Moolenaar rc = xo_do_open_leaf_list(xop, flags, name); 5225545ddfbeSMarcel Moolenaar break; 5226545ddfbeSMarcel Moolenaar 5227545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LIST, XSS_OPEN_LEAF_LIST): 5228545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_OPEN_LEAF_LIST): 5229545ddfbeSMarcel Moolenaar if (on_marker) 5230545ddfbeSMarcel Moolenaar goto marker_prevents_close; 5231545ddfbeSMarcel Moolenaar rc = xo_do_close_list(xop, NULL); 5232545ddfbeSMarcel Moolenaar if (rc >= 0) 5233545ddfbeSMarcel Moolenaar goto open_leaf_list; 5234545ddfbeSMarcel Moolenaar break; 5235545ddfbeSMarcel Moolenaar 5236545ddfbeSMarcel Moolenaar /*close_leaf_list:*/ 5237545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_CLOSE_LEAF_LIST): 5238545ddfbeSMarcel Moolenaar if (on_marker) 5239545ddfbeSMarcel Moolenaar goto marker_prevents_close; 5240545ddfbeSMarcel Moolenaar rc = xo_do_close_leaf_list(xop, name); 5241545ddfbeSMarcel Moolenaar break; 5242545ddfbeSMarcel Moolenaar 5243545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_INIT, XSS_CLOSE_LEAF_LIST): 5244545ddfbeSMarcel Moolenaar /* Makes no sense; ignore */ 5245788ca347SMarcel Moolenaar xo_failure(xop, "xo_close_leaf_list ignored when called from " 5246788ca347SMarcel Moolenaar "initial state ('%s')", name ?: "(unknown)"); 5247545ddfbeSMarcel Moolenaar break; 5248545ddfbeSMarcel Moolenaar 5249545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_CLOSE_LEAF_LIST): 5250545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LIST, XSS_CLOSE_LEAF_LIST): 5251545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_CLOSE_LEAF_LIST): 5252545ddfbeSMarcel Moolenaar if (on_marker) 5253545ddfbeSMarcel Moolenaar goto marker_prevents_close; 5254545ddfbeSMarcel Moolenaar rc = xo_do_close(xop, name, new_state); 5255545ddfbeSMarcel Moolenaar break; 5256545ddfbeSMarcel Moolenaar 5257545ddfbeSMarcel Moolenaar /*emit:*/ 5258545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_EMIT): 5259545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_EMIT): 5260545ddfbeSMarcel Moolenaar break; 5261545ddfbeSMarcel Moolenaar 5262545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LIST, XSS_EMIT): 5263545ddfbeSMarcel Moolenaar if (on_marker) 5264545ddfbeSMarcel Moolenaar goto marker_prevents_close; 5265545ddfbeSMarcel Moolenaar rc = xo_do_close(xop, NULL, XSS_CLOSE_LIST); 5266545ddfbeSMarcel Moolenaar break; 5267545ddfbeSMarcel Moolenaar 5268545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_INIT, XSS_EMIT): 5269545ddfbeSMarcel Moolenaar break; 5270545ddfbeSMarcel Moolenaar 5271545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_EMIT): 5272545ddfbeSMarcel Moolenaar if (on_marker) 5273545ddfbeSMarcel Moolenaar goto marker_prevents_close; 5274545ddfbeSMarcel Moolenaar rc = xo_do_close_leaf_list(xop, NULL); 5275545ddfbeSMarcel Moolenaar break; 5276545ddfbeSMarcel Moolenaar 5277545ddfbeSMarcel Moolenaar /*emit_leaf_list:*/ 5278545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_INIT, XSS_EMIT_LEAF_LIST): 5279545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_EMIT_LEAF_LIST): 5280545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_EMIT_LEAF_LIST): 5281545ddfbeSMarcel Moolenaar rc = xo_do_open_leaf_list(xop, flags, name); 5282545ddfbeSMarcel Moolenaar break; 5283545ddfbeSMarcel Moolenaar 5284545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_EMIT_LEAF_LIST): 5285545ddfbeSMarcel Moolenaar break; 5286545ddfbeSMarcel Moolenaar 5287545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LIST, XSS_EMIT_LEAF_LIST): 5288545ddfbeSMarcel Moolenaar /* 5289545ddfbeSMarcel Moolenaar * We need to be backward compatible with the pre-xo_open_leaf_list 5290545ddfbeSMarcel Moolenaar * API, where both lists and leaf-lists were opened as lists. So 5291545ddfbeSMarcel Moolenaar * if we find an open list that hasn't had anything written to it, 5292545ddfbeSMarcel Moolenaar * we'll accept it. 5293545ddfbeSMarcel Moolenaar */ 5294545ddfbeSMarcel Moolenaar break; 5295545ddfbeSMarcel Moolenaar 5296545ddfbeSMarcel Moolenaar default: 5297545ddfbeSMarcel Moolenaar xo_failure(xop, "unknown transition: (%u -> %u)", 5298545ddfbeSMarcel Moolenaar xsp->xs_state, new_state); 5299545ddfbeSMarcel Moolenaar } 5300545ddfbeSMarcel Moolenaar 5301545ddfbeSMarcel Moolenaar return rc; 5302545ddfbeSMarcel Moolenaar 5303545ddfbeSMarcel Moolenaar marker_prevents_close: 5304545ddfbeSMarcel Moolenaar xo_failure(xop, "marker '%s' prevents transition from %s to %s", 5305545ddfbeSMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_name, 5306545ddfbeSMarcel Moolenaar xo_state_name(old_state), xo_state_name(new_state)); 5307545ddfbeSMarcel Moolenaar return -1; 5308545ddfbeSMarcel Moolenaar } 5309545ddfbeSMarcel Moolenaar 5310545ddfbeSMarcel Moolenaar int 5311545ddfbeSMarcel Moolenaar xo_open_marker_h (xo_handle_t *xop, const char *name) 5312545ddfbeSMarcel Moolenaar { 5313545ddfbeSMarcel Moolenaar xop = xo_default(xop); 5314545ddfbeSMarcel Moolenaar 5315545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, 1, 0, XSS_MARKER, 5316545ddfbeSMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags & XSF_MARKER_FLAGS); 5317545ddfbeSMarcel Moolenaar 5318545ddfbeSMarcel Moolenaar return 0; 5319545ddfbeSMarcel Moolenaar } 5320545ddfbeSMarcel Moolenaar 5321545ddfbeSMarcel Moolenaar int 5322545ddfbeSMarcel Moolenaar xo_open_marker (const char *name) 5323545ddfbeSMarcel Moolenaar { 5324545ddfbeSMarcel Moolenaar return xo_open_marker_h(NULL, name); 5325545ddfbeSMarcel Moolenaar } 5326545ddfbeSMarcel Moolenaar 5327545ddfbeSMarcel Moolenaar int 5328545ddfbeSMarcel Moolenaar xo_close_marker_h (xo_handle_t *xop, const char *name) 5329545ddfbeSMarcel Moolenaar { 5330545ddfbeSMarcel Moolenaar xop = xo_default(xop); 5331545ddfbeSMarcel Moolenaar 5332545ddfbeSMarcel Moolenaar return xo_do_close(xop, name, XSS_MARKER); 5333545ddfbeSMarcel Moolenaar } 5334545ddfbeSMarcel Moolenaar 5335545ddfbeSMarcel Moolenaar int 5336545ddfbeSMarcel Moolenaar xo_close_marker (const char *name) 5337545ddfbeSMarcel Moolenaar { 5338545ddfbeSMarcel Moolenaar return xo_close_marker_h(NULL, name); 5339545ddfbeSMarcel Moolenaar } 5340545ddfbeSMarcel Moolenaar 534131337658SMarcel Moolenaar void 534231337658SMarcel Moolenaar xo_set_writer (xo_handle_t *xop, void *opaque, xo_write_func_t write_func, 5343545ddfbeSMarcel Moolenaar xo_close_func_t close_func, xo_flush_func_t flush_func) 534431337658SMarcel Moolenaar { 534531337658SMarcel Moolenaar xop = xo_default(xop); 534631337658SMarcel Moolenaar 534731337658SMarcel Moolenaar xop->xo_opaque = opaque; 534831337658SMarcel Moolenaar xop->xo_write = write_func; 534931337658SMarcel Moolenaar xop->xo_close = close_func; 5350545ddfbeSMarcel Moolenaar xop->xo_flush = flush_func; 535131337658SMarcel Moolenaar } 535231337658SMarcel Moolenaar 535331337658SMarcel Moolenaar void 535431337658SMarcel Moolenaar xo_set_allocator (xo_realloc_func_t realloc_func, xo_free_func_t free_func) 535531337658SMarcel Moolenaar { 535631337658SMarcel Moolenaar xo_realloc = realloc_func; 535731337658SMarcel Moolenaar xo_free = free_func; 535831337658SMarcel Moolenaar } 535931337658SMarcel Moolenaar 5360545ddfbeSMarcel Moolenaar int 536131337658SMarcel Moolenaar xo_flush_h (xo_handle_t *xop) 536231337658SMarcel Moolenaar { 536331337658SMarcel Moolenaar static char div_close[] = "</div>"; 5364545ddfbeSMarcel Moolenaar int rc; 536531337658SMarcel Moolenaar 536631337658SMarcel Moolenaar xop = xo_default(xop); 536731337658SMarcel Moolenaar 5368788ca347SMarcel Moolenaar switch (xo_style(xop)) { 536931337658SMarcel Moolenaar case XO_STYLE_HTML: 537031337658SMarcel Moolenaar if (xop->xo_flags & XOF_DIV_OPEN) { 537131337658SMarcel Moolenaar xop->xo_flags &= ~XOF_DIV_OPEN; 537231337658SMarcel Moolenaar xo_data_append(xop, div_close, sizeof(div_close) - 1); 537331337658SMarcel Moolenaar 537431337658SMarcel Moolenaar if (xop->xo_flags & XOF_PRETTY) 537531337658SMarcel Moolenaar xo_data_append(xop, "\n", 1); 537631337658SMarcel Moolenaar } 537731337658SMarcel Moolenaar break; 537831337658SMarcel Moolenaar } 537931337658SMarcel Moolenaar 5380545ddfbeSMarcel Moolenaar rc = xo_write(xop); 5381545ddfbeSMarcel Moolenaar if (rc >= 0 && xop->xo_flush) 5382545ddfbeSMarcel Moolenaar if (xop->xo_flush(xop->xo_opaque) < 0) 5383545ddfbeSMarcel Moolenaar return -1; 5384545ddfbeSMarcel Moolenaar 5385545ddfbeSMarcel Moolenaar return rc; 538631337658SMarcel Moolenaar } 538731337658SMarcel Moolenaar 5388545ddfbeSMarcel Moolenaar int 538931337658SMarcel Moolenaar xo_flush (void) 539031337658SMarcel Moolenaar { 5391545ddfbeSMarcel Moolenaar return xo_flush_h(NULL); 539231337658SMarcel Moolenaar } 539331337658SMarcel Moolenaar 5394545ddfbeSMarcel Moolenaar int 539531337658SMarcel Moolenaar xo_finish_h (xo_handle_t *xop) 539631337658SMarcel Moolenaar { 539731337658SMarcel Moolenaar const char *cp = ""; 539831337658SMarcel Moolenaar xop = xo_default(xop); 539931337658SMarcel Moolenaar 5400545ddfbeSMarcel Moolenaar if (!(xop->xo_flags & XOF_NO_CLOSE)) 5401545ddfbeSMarcel Moolenaar xo_do_close_all(xop, xop->xo_stack); 5402545ddfbeSMarcel Moolenaar 5403788ca347SMarcel Moolenaar switch (xo_style(xop)) { 540431337658SMarcel Moolenaar case XO_STYLE_JSON: 540531337658SMarcel Moolenaar if (!(xop->xo_flags & XOF_NO_TOP)) { 540631337658SMarcel Moolenaar if (xop->xo_flags & XOF_TOP_EMITTED) 540731337658SMarcel Moolenaar xop->xo_flags &= ~XOF_TOP_EMITTED; /* Turn off before output */ 540831337658SMarcel Moolenaar else 540931337658SMarcel Moolenaar cp = "{ "; 541031337658SMarcel Moolenaar xo_printf(xop, "%*s%s}\n",xo_indent(xop), "", cp); 541131337658SMarcel Moolenaar } 541231337658SMarcel Moolenaar break; 541331337658SMarcel Moolenaar } 541431337658SMarcel Moolenaar 5415545ddfbeSMarcel Moolenaar return xo_flush_h(xop); 541631337658SMarcel Moolenaar } 541731337658SMarcel Moolenaar 5418545ddfbeSMarcel Moolenaar int 541931337658SMarcel Moolenaar xo_finish (void) 542031337658SMarcel Moolenaar { 5421545ddfbeSMarcel Moolenaar return xo_finish_h(NULL); 542231337658SMarcel Moolenaar } 542331337658SMarcel Moolenaar 542431337658SMarcel Moolenaar /* 542531337658SMarcel Moolenaar * Generate an error message, such as would be displayed on stderr 542631337658SMarcel Moolenaar */ 542731337658SMarcel Moolenaar void 542831337658SMarcel Moolenaar xo_error_hv (xo_handle_t *xop, const char *fmt, va_list vap) 542931337658SMarcel Moolenaar { 543031337658SMarcel Moolenaar xop = xo_default(xop); 543131337658SMarcel Moolenaar 543231337658SMarcel Moolenaar /* 543331337658SMarcel Moolenaar * If the format string doesn't end with a newline, we pop 543431337658SMarcel Moolenaar * one on ourselves. 543531337658SMarcel Moolenaar */ 543631337658SMarcel Moolenaar int len = strlen(fmt); 543731337658SMarcel Moolenaar if (len > 0 && fmt[len - 1] != '\n') { 543831337658SMarcel Moolenaar char *newfmt = alloca(len + 2); 543931337658SMarcel Moolenaar memcpy(newfmt, fmt, len); 544031337658SMarcel Moolenaar newfmt[len] = '\n'; 544131337658SMarcel Moolenaar newfmt[len] = '\0'; 544231337658SMarcel Moolenaar fmt = newfmt; 544331337658SMarcel Moolenaar } 544431337658SMarcel Moolenaar 5445788ca347SMarcel Moolenaar switch (xo_style(xop)) { 544631337658SMarcel Moolenaar case XO_STYLE_TEXT: 544731337658SMarcel Moolenaar vfprintf(stderr, fmt, vap); 544831337658SMarcel Moolenaar break; 544931337658SMarcel Moolenaar 545031337658SMarcel Moolenaar case XO_STYLE_HTML: 545131337658SMarcel Moolenaar va_copy(xop->xo_vap, vap); 545231337658SMarcel Moolenaar 545331337658SMarcel Moolenaar xo_buf_append_div(xop, "error", 0, NULL, 0, fmt, strlen(fmt), NULL, 0); 545431337658SMarcel Moolenaar 545531337658SMarcel Moolenaar if (xop->xo_flags & XOF_DIV_OPEN) 545631337658SMarcel Moolenaar xo_line_close(xop); 545731337658SMarcel Moolenaar 545831337658SMarcel Moolenaar xo_write(xop); 545931337658SMarcel Moolenaar 546031337658SMarcel Moolenaar va_end(xop->xo_vap); 546131337658SMarcel Moolenaar bzero(&xop->xo_vap, sizeof(xop->xo_vap)); 546231337658SMarcel Moolenaar break; 546331337658SMarcel Moolenaar 546431337658SMarcel Moolenaar case XO_STYLE_XML: 5465545ddfbeSMarcel Moolenaar case XO_STYLE_JSON: 546631337658SMarcel Moolenaar va_copy(xop->xo_vap, vap); 546731337658SMarcel Moolenaar 546831337658SMarcel Moolenaar xo_open_container_h(xop, "error"); 546931337658SMarcel Moolenaar xo_format_value(xop, "message", 7, fmt, strlen(fmt), NULL, 0, 0); 547031337658SMarcel Moolenaar xo_close_container_h(xop, "error"); 547131337658SMarcel Moolenaar 547231337658SMarcel Moolenaar va_end(xop->xo_vap); 547331337658SMarcel Moolenaar bzero(&xop->xo_vap, sizeof(xop->xo_vap)); 547431337658SMarcel Moolenaar break; 547531337658SMarcel Moolenaar } 547631337658SMarcel Moolenaar } 547731337658SMarcel Moolenaar 547831337658SMarcel Moolenaar void 547931337658SMarcel Moolenaar xo_error_h (xo_handle_t *xop, const char *fmt, ...) 548031337658SMarcel Moolenaar { 548131337658SMarcel Moolenaar va_list vap; 548231337658SMarcel Moolenaar 548331337658SMarcel Moolenaar va_start(vap, fmt); 548431337658SMarcel Moolenaar xo_error_hv(xop, fmt, vap); 548531337658SMarcel Moolenaar va_end(vap); 548631337658SMarcel Moolenaar } 548731337658SMarcel Moolenaar 548831337658SMarcel Moolenaar /* 548931337658SMarcel Moolenaar * Generate an error message, such as would be displayed on stderr 549031337658SMarcel Moolenaar */ 549131337658SMarcel Moolenaar void 549231337658SMarcel Moolenaar xo_error (const char *fmt, ...) 549331337658SMarcel Moolenaar { 549431337658SMarcel Moolenaar va_list vap; 549531337658SMarcel Moolenaar 549631337658SMarcel Moolenaar va_start(vap, fmt); 549731337658SMarcel Moolenaar xo_error_hv(NULL, fmt, vap); 549831337658SMarcel Moolenaar va_end(vap); 549931337658SMarcel Moolenaar } 550031337658SMarcel Moolenaar 550131337658SMarcel Moolenaar int 550231337658SMarcel Moolenaar xo_parse_args (int argc, char **argv) 550331337658SMarcel Moolenaar { 550431337658SMarcel Moolenaar static char libxo_opt[] = "--libxo"; 550531337658SMarcel Moolenaar char *cp; 550631337658SMarcel Moolenaar int i, save; 550731337658SMarcel Moolenaar 550831337658SMarcel Moolenaar /* Save our program name for xo_err and friends */ 550931337658SMarcel Moolenaar xo_program = argv[0]; 551031337658SMarcel Moolenaar cp = strrchr(xo_program, '/'); 551131337658SMarcel Moolenaar if (cp) 551231337658SMarcel Moolenaar xo_program = cp + 1; 551331337658SMarcel Moolenaar 551431337658SMarcel Moolenaar for (save = i = 1; i < argc; i++) { 551531337658SMarcel Moolenaar if (argv[i] == NULL 551631337658SMarcel Moolenaar || strncmp(argv[i], libxo_opt, sizeof(libxo_opt) - 1) != 0) { 551731337658SMarcel Moolenaar if (save != i) 551831337658SMarcel Moolenaar argv[save] = argv[i]; 551931337658SMarcel Moolenaar save += 1; 552031337658SMarcel Moolenaar continue; 552131337658SMarcel Moolenaar } 552231337658SMarcel Moolenaar 552331337658SMarcel Moolenaar cp = argv[i] + sizeof(libxo_opt) - 1; 552431337658SMarcel Moolenaar if (*cp == 0) { 552531337658SMarcel Moolenaar cp = argv[++i]; 552631337658SMarcel Moolenaar if (cp == 0) { 552731337658SMarcel Moolenaar xo_warnx("missing libxo option"); 552831337658SMarcel Moolenaar return -1; 552931337658SMarcel Moolenaar } 553031337658SMarcel Moolenaar 553131337658SMarcel Moolenaar if (xo_set_options(NULL, cp) < 0) 553231337658SMarcel Moolenaar return -1; 553331337658SMarcel Moolenaar } else if (*cp == ':') { 553431337658SMarcel Moolenaar if (xo_set_options(NULL, cp) < 0) 553531337658SMarcel Moolenaar return -1; 553631337658SMarcel Moolenaar 553731337658SMarcel Moolenaar } else if (*cp == '=') { 553831337658SMarcel Moolenaar if (xo_set_options(NULL, ++cp) < 0) 553931337658SMarcel Moolenaar return -1; 554031337658SMarcel Moolenaar 554131337658SMarcel Moolenaar } else if (*cp == '-') { 554231337658SMarcel Moolenaar cp += 1; 554331337658SMarcel Moolenaar if (strcmp(cp, "check") == 0) { 554431337658SMarcel Moolenaar exit(XO_HAS_LIBXO); 554531337658SMarcel Moolenaar 554631337658SMarcel Moolenaar } else { 554731337658SMarcel Moolenaar xo_warnx("unknown libxo option: '%s'", argv[i]); 554831337658SMarcel Moolenaar return -1; 554931337658SMarcel Moolenaar } 555031337658SMarcel Moolenaar } else { 555131337658SMarcel Moolenaar xo_warnx("unknown libxo option: '%s'", argv[i]); 555231337658SMarcel Moolenaar return -1; 555331337658SMarcel Moolenaar } 555431337658SMarcel Moolenaar } 555531337658SMarcel Moolenaar 555631337658SMarcel Moolenaar argv[save] = NULL; 555731337658SMarcel Moolenaar return save; 555831337658SMarcel Moolenaar } 555931337658SMarcel Moolenaar 5560545ddfbeSMarcel Moolenaar void 5561545ddfbeSMarcel Moolenaar xo_dump_stack (xo_handle_t *xop) 5562545ddfbeSMarcel Moolenaar { 5563545ddfbeSMarcel Moolenaar int i; 5564545ddfbeSMarcel Moolenaar xo_stack_t *xsp; 5565545ddfbeSMarcel Moolenaar 5566545ddfbeSMarcel Moolenaar xop = xo_default(xop); 5567545ddfbeSMarcel Moolenaar 5568545ddfbeSMarcel Moolenaar fprintf(stderr, "Stack dump:\n"); 5569545ddfbeSMarcel Moolenaar 5570545ddfbeSMarcel Moolenaar xsp = xop->xo_stack; 5571545ddfbeSMarcel Moolenaar for (i = 1, xsp++; i <= xop->xo_depth; i++, xsp++) { 5572545ddfbeSMarcel Moolenaar fprintf(stderr, " [%d] %s '%s' [%x]\n", 5573545ddfbeSMarcel Moolenaar i, xo_state_name(xsp->xs_state), 5574545ddfbeSMarcel Moolenaar xsp->xs_name ?: "--", xsp->xs_flags); 5575545ddfbeSMarcel Moolenaar } 5576545ddfbeSMarcel Moolenaar } 5577545ddfbeSMarcel Moolenaar 5578545ddfbeSMarcel Moolenaar void 5579545ddfbeSMarcel Moolenaar xo_set_program (const char *name) 5580545ddfbeSMarcel Moolenaar { 5581545ddfbeSMarcel Moolenaar xo_program = name; 5582545ddfbeSMarcel Moolenaar } 5583545ddfbeSMarcel Moolenaar 5584788ca347SMarcel Moolenaar void 5585788ca347SMarcel Moolenaar xo_set_version_h (xo_handle_t *xop, const char *version UNUSED) 5586788ca347SMarcel Moolenaar { 5587788ca347SMarcel Moolenaar xop = xo_default(xop); 5588788ca347SMarcel Moolenaar 5589788ca347SMarcel Moolenaar if (version == NULL || strchr(version, '"') != NULL) 5590788ca347SMarcel Moolenaar return; 5591788ca347SMarcel Moolenaar 5592788ca347SMarcel Moolenaar switch (xo_style(xop)) { 5593788ca347SMarcel Moolenaar case XO_STYLE_XML: 5594788ca347SMarcel Moolenaar /* For XML, we record this as an attribute for the first tag */ 5595788ca347SMarcel Moolenaar xo_attr_h(xop, "__version", "%s", version); 5596788ca347SMarcel Moolenaar break; 5597788ca347SMarcel Moolenaar 5598788ca347SMarcel Moolenaar case XO_STYLE_JSON: 5599788ca347SMarcel Moolenaar { 5600788ca347SMarcel Moolenaar /* 5601788ca347SMarcel Moolenaar * For XML, we record the version string in our handle, and emit 5602788ca347SMarcel Moolenaar * it in xo_emit_top. 5603788ca347SMarcel Moolenaar */ 5604788ca347SMarcel Moolenaar int len = strlen(version) + 1; 5605788ca347SMarcel Moolenaar xop->xo_version = xo_realloc(NULL, len); 5606788ca347SMarcel Moolenaar if (xop->xo_version) 5607788ca347SMarcel Moolenaar memcpy(xop->xo_version, version, len); 5608788ca347SMarcel Moolenaar } 5609788ca347SMarcel Moolenaar break; 5610788ca347SMarcel Moolenaar } 5611788ca347SMarcel Moolenaar } 5612788ca347SMarcel Moolenaar 5613788ca347SMarcel Moolenaar void 5614788ca347SMarcel Moolenaar xo_set_version (const char *version) 5615788ca347SMarcel Moolenaar { 5616788ca347SMarcel Moolenaar xo_set_version_h(NULL, version); 5617788ca347SMarcel Moolenaar } 5618788ca347SMarcel Moolenaar 561931337658SMarcel Moolenaar #ifdef UNIT_TEST 562031337658SMarcel Moolenaar int 562131337658SMarcel Moolenaar main (int argc, char **argv) 562231337658SMarcel Moolenaar { 562331337658SMarcel Moolenaar static char base_grocery[] = "GRO"; 562431337658SMarcel Moolenaar static char base_hardware[] = "HRD"; 562531337658SMarcel Moolenaar struct item { 562631337658SMarcel Moolenaar const char *i_title; 562731337658SMarcel Moolenaar int i_sold; 562831337658SMarcel Moolenaar int i_instock; 562931337658SMarcel Moolenaar int i_onorder; 563031337658SMarcel Moolenaar const char *i_sku_base; 563131337658SMarcel Moolenaar int i_sku_num; 563231337658SMarcel Moolenaar }; 563331337658SMarcel Moolenaar struct item list[] = { 563431337658SMarcel Moolenaar { "gum&this&that", 1412, 54, 10, base_grocery, 415 }, 563531337658SMarcel Moolenaar { "<rope>", 85, 4, 2, base_hardware, 212 }, 563631337658SMarcel Moolenaar { "ladder", 0, 2, 1, base_hardware, 517 }, 563731337658SMarcel Moolenaar { "\"bolt\"", 4123, 144, 42, base_hardware, 632 }, 563831337658SMarcel Moolenaar { "water\\blue", 17, 14, 2, base_grocery, 2331 }, 563931337658SMarcel Moolenaar { NULL, 0, 0, 0, NULL, 0 } 564031337658SMarcel Moolenaar }; 564131337658SMarcel Moolenaar struct item list2[] = { 564231337658SMarcel Moolenaar { "fish", 1321, 45, 1, base_grocery, 533 }, 564331337658SMarcel Moolenaar { NULL, 0, 0, 0, NULL, 0 } 564431337658SMarcel Moolenaar }; 564531337658SMarcel Moolenaar struct item *ip; 564631337658SMarcel Moolenaar xo_info_t info[] = { 564731337658SMarcel Moolenaar { "in-stock", "number", "Number of items in stock" }, 564831337658SMarcel Moolenaar { "name", "string", "Name of the item" }, 564931337658SMarcel Moolenaar { "on-order", "number", "Number of items on order" }, 565031337658SMarcel Moolenaar { "sku", "string", "Stock Keeping Unit" }, 565131337658SMarcel Moolenaar { "sold", "number", "Number of items sold" }, 565231337658SMarcel Moolenaar { NULL, NULL, NULL }, 565331337658SMarcel Moolenaar }; 565431337658SMarcel Moolenaar int info_count = (sizeof(info) / sizeof(info[0])) - 1; 565531337658SMarcel Moolenaar 565631337658SMarcel Moolenaar argc = xo_parse_args(argc, argv); 565731337658SMarcel Moolenaar if (argc < 0) 565831337658SMarcel Moolenaar exit(1); 565931337658SMarcel Moolenaar 566031337658SMarcel Moolenaar xo_set_info(NULL, info, info_count); 566131337658SMarcel Moolenaar 566231337658SMarcel Moolenaar xo_open_container_h(NULL, "top"); 566331337658SMarcel Moolenaar 566431337658SMarcel Moolenaar xo_open_container("data"); 566531337658SMarcel Moolenaar xo_open_list("item"); 566631337658SMarcel Moolenaar 566731337658SMarcel Moolenaar xo_emit("{T:Item/%-15s}{T:Total Sold/%12s}{T:In Stock/%12s}" 566831337658SMarcel Moolenaar "{T:On Order/%12s}{T:SKU/%5s}\n"); 566931337658SMarcel Moolenaar 567031337658SMarcel Moolenaar for (ip = list; ip->i_title; ip++) { 567131337658SMarcel Moolenaar xo_open_instance("item"); 567231337658SMarcel Moolenaar 567331337658SMarcel Moolenaar xo_emit("{k:name/%-15s/%s}{n:sold/%12u/%u}{:in-stock/%12u/%u}" 567431337658SMarcel Moolenaar "{:on-order/%12u/%u} {q:sku/%5s-000-%u/%s-000-%u}\n", 567531337658SMarcel Moolenaar ip->i_title, ip->i_sold, ip->i_instock, ip->i_onorder, 567631337658SMarcel Moolenaar ip->i_sku_base, ip->i_sku_num); 567731337658SMarcel Moolenaar 567831337658SMarcel Moolenaar xo_close_instance("item"); 567931337658SMarcel Moolenaar } 568031337658SMarcel Moolenaar 568131337658SMarcel Moolenaar xo_close_list("item"); 568231337658SMarcel Moolenaar xo_close_container("data"); 568331337658SMarcel Moolenaar 568431337658SMarcel Moolenaar xo_emit("\n\n"); 568531337658SMarcel Moolenaar 568631337658SMarcel Moolenaar xo_open_container("data"); 568731337658SMarcel Moolenaar xo_open_list("item"); 568831337658SMarcel Moolenaar 568931337658SMarcel Moolenaar for (ip = list; ip->i_title; ip++) { 569031337658SMarcel Moolenaar xo_open_instance("item"); 569131337658SMarcel Moolenaar 569231337658SMarcel Moolenaar xo_attr("fancy", "%s%d", "item", ip - list); 569331337658SMarcel Moolenaar xo_emit("{L:Item} '{k:name/%s}':\n", ip->i_title); 569431337658SMarcel Moolenaar xo_emit("{P: }{L:Total sold}: {n:sold/%u%s}{e:percent/%u}\n", 569531337658SMarcel Moolenaar ip->i_sold, ip->i_sold ? ".0" : "", 44); 569631337658SMarcel Moolenaar xo_emit("{P: }{Lcw:In stock}{:in-stock/%u}\n", ip->i_instock); 569731337658SMarcel Moolenaar xo_emit("{P: }{Lcw:On order}{:on-order/%u}\n", ip->i_onorder); 569831337658SMarcel Moolenaar xo_emit("{P: }{L:SKU}: {q:sku/%s-000-%u}\n", 569931337658SMarcel Moolenaar ip->i_sku_base, ip->i_sku_num); 570031337658SMarcel Moolenaar 570131337658SMarcel Moolenaar xo_close_instance("item"); 570231337658SMarcel Moolenaar } 570331337658SMarcel Moolenaar 570431337658SMarcel Moolenaar xo_close_list("item"); 570531337658SMarcel Moolenaar xo_close_container("data"); 570631337658SMarcel Moolenaar 570731337658SMarcel Moolenaar xo_open_container("data"); 570831337658SMarcel Moolenaar xo_open_list("item"); 570931337658SMarcel Moolenaar 571031337658SMarcel Moolenaar for (ip = list2; ip->i_title; ip++) { 571131337658SMarcel Moolenaar xo_open_instance("item"); 571231337658SMarcel Moolenaar 571331337658SMarcel Moolenaar xo_emit("{L:Item} '{k:name/%s}':\n", ip->i_title); 571431337658SMarcel Moolenaar xo_emit("{P: }{L:Total sold}: {n:sold/%u%s}\n", 571531337658SMarcel Moolenaar ip->i_sold, ip->i_sold ? ".0" : ""); 571631337658SMarcel Moolenaar xo_emit("{P: }{Lcw:In stock}{:in-stock/%u}\n", ip->i_instock); 571731337658SMarcel Moolenaar xo_emit("{P: }{Lcw:On order}{:on-order/%u}\n", ip->i_onorder); 571831337658SMarcel Moolenaar xo_emit("{P: }{L:SKU}: {q:sku/%s-000-%u}\n", 571931337658SMarcel Moolenaar ip->i_sku_base, ip->i_sku_num); 572031337658SMarcel Moolenaar 572131337658SMarcel Moolenaar xo_open_list("month"); 572231337658SMarcel Moolenaar 572331337658SMarcel Moolenaar const char *months[] = { "Jan", "Feb", "Mar", NULL }; 572431337658SMarcel Moolenaar int discounts[] = { 10, 20, 25, 0 }; 572531337658SMarcel Moolenaar int i; 572631337658SMarcel Moolenaar for (i = 0; months[i]; i++) { 572731337658SMarcel Moolenaar xo_open_instance("month"); 572831337658SMarcel Moolenaar xo_emit("{P: }" 572931337658SMarcel Moolenaar "{Lwc:Month}{k:month}, {Lwc:Special}{:discount/%d}\n", 573031337658SMarcel Moolenaar months[i], discounts[i]); 573131337658SMarcel Moolenaar xo_close_instance("month"); 573231337658SMarcel Moolenaar } 573331337658SMarcel Moolenaar 573431337658SMarcel Moolenaar xo_close_list("month"); 573531337658SMarcel Moolenaar 573631337658SMarcel Moolenaar xo_close_instance("item"); 573731337658SMarcel Moolenaar } 573831337658SMarcel Moolenaar 573931337658SMarcel Moolenaar xo_close_list("item"); 574031337658SMarcel Moolenaar xo_close_container("data"); 574131337658SMarcel Moolenaar 574231337658SMarcel Moolenaar xo_close_container_h(NULL, "top"); 574331337658SMarcel Moolenaar 574431337658SMarcel Moolenaar xo_finish(); 574531337658SMarcel Moolenaar 574631337658SMarcel Moolenaar return 0; 574731337658SMarcel Moolenaar } 574831337658SMarcel Moolenaar #endif /* UNIT_TEST */ 5749