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 9d1a0d267SMarcel Moolenaar * 10d1a0d267SMarcel Moolenaar * This is the implementation of libxo, the formatting library that 11d1a0d267SMarcel Moolenaar * generates multiple styles of output from a single code path. 12d1a0d267SMarcel Moolenaar * Command line utilities can have their normal text output while 13d1a0d267SMarcel Moolenaar * automation tools can see XML or JSON output, and web tools can use 14d1a0d267SMarcel Moolenaar * HTML output that encodes the text output annotated with additional 15d1a0d267SMarcel Moolenaar * information. Specialized encoders can be built that allow custom 16d1a0d267SMarcel Moolenaar * encoding including binary ones like CBOR, thrift, protobufs, etc. 17d1a0d267SMarcel Moolenaar * 18d1a0d267SMarcel Moolenaar * Full documentation is available in ./doc/libxo.txt or online at: 19d1a0d267SMarcel Moolenaar * http://juniper.github.io/libxo/libxo-manual.html 20d1a0d267SMarcel Moolenaar * 21d1a0d267SMarcel Moolenaar * For first time readers, the core bits of code to start looking at are: 2242ff34c3SPhil Shafer * - xo_do_emit() -- parse and emit a set of fields 2342ff34c3SPhil Shafer * - xo_do_emit_fields -- the central function of the library 24d1a0d267SMarcel Moolenaar * - xo_do_format_field() -- handles formatting a single field 25d1a0d267SMarcel Moolenaar * - xo_transiton() -- the state machine that keeps things sane 26d1a0d267SMarcel Moolenaar * and of course the "xo_handle_t" data structure, which carries all 27d1a0d267SMarcel Moolenaar * configuration and state. 2831337658SMarcel Moolenaar */ 2931337658SMarcel Moolenaar 3031337658SMarcel Moolenaar #include <stdio.h> 3131337658SMarcel Moolenaar #include <stdlib.h> 3231337658SMarcel Moolenaar #include <stdint.h> 33545ddfbeSMarcel Moolenaar #include <unistd.h> 3431337658SMarcel Moolenaar #include <stddef.h> 3531337658SMarcel Moolenaar #include <wchar.h> 3631337658SMarcel Moolenaar #include <locale.h> 3731337658SMarcel Moolenaar #include <sys/types.h> 3831337658SMarcel Moolenaar #include <stdarg.h> 3931337658SMarcel Moolenaar #include <string.h> 4031337658SMarcel Moolenaar #include <errno.h> 4131337658SMarcel Moolenaar #include <limits.h> 4231337658SMarcel Moolenaar #include <ctype.h> 4331337658SMarcel Moolenaar #include <wctype.h> 4431337658SMarcel Moolenaar #include <getopt.h> 4531337658SMarcel Moolenaar 46d1a0d267SMarcel Moolenaar #include "xo_config.h" 4731337658SMarcel Moolenaar #include "xo.h" 48d1a0d267SMarcel Moolenaar #include "xo_encoder.h" 49d1a0d267SMarcel Moolenaar #include "xo_buf.h" 50d1a0d267SMarcel Moolenaar 51d1a0d267SMarcel Moolenaar /* 52d1a0d267SMarcel Moolenaar * We ask wcwidth() to do an impossible job, really. It's supposed to 53d1a0d267SMarcel Moolenaar * need to tell us the number of columns consumed to display a unicode 54d1a0d267SMarcel Moolenaar * character. It returns that number without any sort of context, but 55d1a0d267SMarcel Moolenaar * we know they are characters whose glyph differs based on placement 56d1a0d267SMarcel Moolenaar * (end of word, middle of word, etc) and many that affect characters 57d1a0d267SMarcel Moolenaar * previously emitted. Without content, it can't hope to tell us. 58d1a0d267SMarcel Moolenaar * But it's the only standard tool we've got, so we use it. We would 59ee5cf116SPhil Shafer * use wcswidth() but it typically just loops through adding the results 60d1a0d267SMarcel Moolenaar * of wcwidth() calls in an entirely unhelpful way. 61d1a0d267SMarcel Moolenaar * 62d1a0d267SMarcel Moolenaar * Even then, there are many poor implementations (macosx), so we have 63d1a0d267SMarcel Moolenaar * to carry our own. We could have configure.ac test this (with 64d1a0d267SMarcel Moolenaar * something like 'assert(wcwidth(0x200d) == 0)'), but it would have 65d1a0d267SMarcel Moolenaar * to run a binary, which breaks cross-compilation. Hmm... I could 66d1a0d267SMarcel Moolenaar * run this test at init time and make a warning for our dear user. 67d1a0d267SMarcel Moolenaar * 68d1a0d267SMarcel Moolenaar * Anyhow, it remains a best-effort sort of thing. And it's all made 69d1a0d267SMarcel Moolenaar * more hopeless because we assume the display code doing the rendering is 70d1a0d267SMarcel Moolenaar * playing by the same rules we are. If it display 0x200d as a square 71d1a0d267SMarcel Moolenaar * box or a funky question mark, the output will be hosed. 72d1a0d267SMarcel Moolenaar */ 73d1a0d267SMarcel Moolenaar #ifdef LIBXO_WCWIDTH 74d1a0d267SMarcel Moolenaar #include "xo_wcwidth.h" 75d1a0d267SMarcel Moolenaar #else /* LIBXO_WCWIDTH */ 76d1a0d267SMarcel Moolenaar #define xo_wcwidth(_x) wcwidth(_x) 77d1a0d267SMarcel Moolenaar #endif /* LIBXO_WCWIDTH */ 7831337658SMarcel Moolenaar 79545ddfbeSMarcel Moolenaar #ifdef HAVE_STDIO_EXT_H 80545ddfbeSMarcel Moolenaar #include <stdio_ext.h> 81545ddfbeSMarcel Moolenaar #endif /* HAVE_STDIO_EXT_H */ 82545ddfbeSMarcel Moolenaar 83d1a0d267SMarcel Moolenaar /* 84d1a0d267SMarcel Moolenaar * humanize_number is a great function, unless you don't have it. So 85d1a0d267SMarcel Moolenaar * we carry one in our pocket. 86d1a0d267SMarcel Moolenaar */ 87d1a0d267SMarcel Moolenaar #ifdef HAVE_HUMANIZE_NUMBER 88d1a0d267SMarcel Moolenaar #include <libutil.h> 89d1a0d267SMarcel Moolenaar #define xo_humanize_number humanize_number 90d1a0d267SMarcel Moolenaar #else /* HAVE_HUMANIZE_NUMBER */ 91d1a0d267SMarcel Moolenaar #include "xo_humanize.h" 92d1a0d267SMarcel Moolenaar #endif /* HAVE_HUMANIZE_NUMBER */ 93d1a0d267SMarcel Moolenaar 94d1a0d267SMarcel Moolenaar #ifdef HAVE_GETTEXT 95d1a0d267SMarcel Moolenaar #include <libintl.h> 96d1a0d267SMarcel Moolenaar #endif /* HAVE_GETTEXT */ 97d1a0d267SMarcel Moolenaar 98d1a0d267SMarcel Moolenaar /* 99d1a0d267SMarcel Moolenaar * Three styles of specifying thread-local variables are supported. 100ee5cf116SPhil Shafer * configure.ac has the brains to run each possibility through the 101d1a0d267SMarcel Moolenaar * compiler and see what works; we are left to define the THREAD_LOCAL 102d1a0d267SMarcel Moolenaar * macro to the right value. Most toolchains (clang, gcc) use 103d1a0d267SMarcel Moolenaar * "before", but some (borland) use "after" and I've heard of some 104d1a0d267SMarcel Moolenaar * (ms) that use __declspec. Any others out there? 105d1a0d267SMarcel Moolenaar */ 106d1a0d267SMarcel Moolenaar #define THREAD_LOCAL_before 1 107d1a0d267SMarcel Moolenaar #define THREAD_LOCAL_after 2 108d1a0d267SMarcel Moolenaar #define THREAD_LOCAL_declspec 3 109d1a0d267SMarcel Moolenaar 110d1a0d267SMarcel Moolenaar #ifndef HAVE_THREAD_LOCAL 111d1a0d267SMarcel Moolenaar #define THREAD_LOCAL(_x) _x 112d1a0d267SMarcel Moolenaar #elif HAVE_THREAD_LOCAL == THREAD_LOCAL_before 113d1a0d267SMarcel Moolenaar #define THREAD_LOCAL(_x) __thread _x 114d1a0d267SMarcel Moolenaar #elif HAVE_THREAD_LOCAL == THREAD_LOCAL_after 115d1a0d267SMarcel Moolenaar #define THREAD_LOCAL(_x) _x __thread 116d1a0d267SMarcel Moolenaar #elif HAVE_THREAD_LOCAL == THREAD_LOCAL_declspec 117d1a0d267SMarcel Moolenaar #define THREAD_LOCAL(_x) __declspec(_x) 118d1a0d267SMarcel Moolenaar #else 119d1a0d267SMarcel Moolenaar #error unknown thread-local setting 120d1a0d267SMarcel Moolenaar #endif /* HAVE_THREADS_H */ 121d1a0d267SMarcel Moolenaar 12231337658SMarcel Moolenaar const char xo_version[] = LIBXO_VERSION; 12331337658SMarcel Moolenaar const char xo_version_extra[] = LIBXO_VERSION_EXTRA; 12442ff34c3SPhil Shafer static const char xo_default_format[] = "%s"; 12531337658SMarcel Moolenaar 12631337658SMarcel Moolenaar #ifndef UNUSED 12731337658SMarcel Moolenaar #define UNUSED __attribute__ ((__unused__)) 12831337658SMarcel Moolenaar #endif /* UNUSED */ 12931337658SMarcel Moolenaar 13031337658SMarcel Moolenaar #define XO_INDENT_BY 2 /* Amount to indent when pretty printing */ 131d1a0d267SMarcel Moolenaar #define XO_DEPTH 128 /* Default stack depth */ 13231337658SMarcel Moolenaar #define XO_MAX_ANCHOR_WIDTH (8*1024) /* Anything wider is just sillyb */ 13331337658SMarcel Moolenaar 13431337658SMarcel Moolenaar #define XO_FAILURE_NAME "failure" 13531337658SMarcel Moolenaar 13631337658SMarcel Moolenaar /* Flags for the stack frame */ 13731337658SMarcel Moolenaar typedef unsigned xo_xsf_flags_t; /* XSF_* flags */ 13831337658SMarcel Moolenaar #define XSF_NOT_FIRST (1<<0) /* Not the first element */ 13931337658SMarcel Moolenaar #define XSF_LIST (1<<1) /* Frame is a list */ 14031337658SMarcel Moolenaar #define XSF_INSTANCE (1<<2) /* Frame is an instance */ 14131337658SMarcel Moolenaar #define XSF_DTRT (1<<3) /* Save the name for DTRT mode */ 14231337658SMarcel Moolenaar 143545ddfbeSMarcel Moolenaar #define XSF_CONTENT (1<<4) /* Some content has been emitted */ 144545ddfbeSMarcel Moolenaar #define XSF_EMIT (1<<5) /* Some field has been emitted */ 145545ddfbeSMarcel Moolenaar #define XSF_EMIT_KEY (1<<6) /* A key has been emitted */ 146545ddfbeSMarcel Moolenaar #define XSF_EMIT_LEAF_LIST (1<<7) /* A leaf-list field has been emitted */ 147545ddfbeSMarcel Moolenaar 148545ddfbeSMarcel Moolenaar /* These are the flags we propagate between markers and their parents */ 149545ddfbeSMarcel Moolenaar #define XSF_MARKER_FLAGS \ 150545ddfbeSMarcel Moolenaar (XSF_NOT_FIRST | XSF_CONTENT | XSF_EMIT | XSF_EMIT_KEY | XSF_EMIT_LEAF_LIST ) 151545ddfbeSMarcel Moolenaar 152545ddfbeSMarcel Moolenaar /* 153d1a0d267SMarcel Moolenaar * A word about states: We use a finite state machine (FMS) approach 154d1a0d267SMarcel Moolenaar * to help remove fragility from the caller's code. Instead of 155d1a0d267SMarcel Moolenaar * requiring a specific order of calls, we'll allow the caller more 156545ddfbeSMarcel Moolenaar * flexibility and make the library responsible for recovering from 157d1a0d267SMarcel Moolenaar * missed steps. The goal is that the library should not be capable 158d1a0d267SMarcel Moolenaar * of emitting invalid xml or json, but the developer shouldn't need 159545ddfbeSMarcel Moolenaar * to know or understand all the details about these encodings. 160545ddfbeSMarcel Moolenaar * 161d1a0d267SMarcel Moolenaar * You can think of states as either states or events, since they 162545ddfbeSMarcel Moolenaar * function rather like both. None of the XO_CLOSE_* events will 163d1a0d267SMarcel Moolenaar * persist as states, since the matching stack frame will be popped. 164545ddfbeSMarcel Moolenaar * Same is true of XSS_EMIT, which is an event that asks us to 165545ddfbeSMarcel Moolenaar * prep for emitting output fields. 166545ddfbeSMarcel Moolenaar */ 167545ddfbeSMarcel Moolenaar 168545ddfbeSMarcel Moolenaar /* Stack frame states */ 169545ddfbeSMarcel Moolenaar typedef unsigned xo_state_t; 170545ddfbeSMarcel Moolenaar #define XSS_INIT 0 /* Initial stack state */ 171545ddfbeSMarcel Moolenaar #define XSS_OPEN_CONTAINER 1 172545ddfbeSMarcel Moolenaar #define XSS_CLOSE_CONTAINER 2 173545ddfbeSMarcel Moolenaar #define XSS_OPEN_LIST 3 174545ddfbeSMarcel Moolenaar #define XSS_CLOSE_LIST 4 175545ddfbeSMarcel Moolenaar #define XSS_OPEN_INSTANCE 5 176545ddfbeSMarcel Moolenaar #define XSS_CLOSE_INSTANCE 6 177545ddfbeSMarcel Moolenaar #define XSS_OPEN_LEAF_LIST 7 178545ddfbeSMarcel Moolenaar #define XSS_CLOSE_LEAF_LIST 8 179545ddfbeSMarcel Moolenaar #define XSS_DISCARDING 9 /* Discarding data until recovered */ 180545ddfbeSMarcel Moolenaar #define XSS_MARKER 10 /* xo_open_marker's marker */ 181545ddfbeSMarcel Moolenaar #define XSS_EMIT 11 /* xo_emit has a leaf field */ 182545ddfbeSMarcel Moolenaar #define XSS_EMIT_LEAF_LIST 12 /* xo_emit has a leaf-list ({l:}) */ 183545ddfbeSMarcel Moolenaar #define XSS_FINISH 13 /* xo_finish was called */ 184545ddfbeSMarcel Moolenaar 185545ddfbeSMarcel Moolenaar #define XSS_MAX 13 186545ddfbeSMarcel Moolenaar 187545ddfbeSMarcel Moolenaar #define XSS_TRANSITION(_old, _new) ((_old) << 8 | (_new)) 188545ddfbeSMarcel Moolenaar 18931337658SMarcel Moolenaar /* 19031337658SMarcel Moolenaar * xo_stack_t: As we open and close containers and levels, we 19131337658SMarcel Moolenaar * create a stack of frames to track them. This is needed for 19231337658SMarcel Moolenaar * XOF_WARN and XOF_XPATH. 19331337658SMarcel Moolenaar */ 19431337658SMarcel Moolenaar typedef struct xo_stack_s { 19531337658SMarcel Moolenaar xo_xsf_flags_t xs_flags; /* Flags for this frame */ 196545ddfbeSMarcel Moolenaar xo_state_t xs_state; /* State for this stack frame */ 19731337658SMarcel Moolenaar char *xs_name; /* Name (for XPath value) */ 19831337658SMarcel Moolenaar char *xs_keys; /* XPath predicate for any key fields */ 19931337658SMarcel Moolenaar } xo_stack_t; 20031337658SMarcel Moolenaar 201d1a0d267SMarcel Moolenaar /* 202d1a0d267SMarcel Moolenaar * libxo supports colors and effects, for those who like them. 203d1a0d267SMarcel Moolenaar * XO_COL_* ("colors") refers to fancy ansi codes, while X__EFF_* 204d1a0d267SMarcel Moolenaar * ("effects") are bits since we need to maintain state. 205d1a0d267SMarcel Moolenaar */ 206788ca347SMarcel Moolenaar #define XO_COL_DEFAULT 0 207788ca347SMarcel Moolenaar #define XO_COL_BLACK 1 208788ca347SMarcel Moolenaar #define XO_COL_RED 2 209788ca347SMarcel Moolenaar #define XO_COL_GREEN 3 210788ca347SMarcel Moolenaar #define XO_COL_YELLOW 4 211788ca347SMarcel Moolenaar #define XO_COL_BLUE 5 212788ca347SMarcel Moolenaar #define XO_COL_MAGENTA 6 213788ca347SMarcel Moolenaar #define XO_COL_CYAN 7 214788ca347SMarcel Moolenaar #define XO_COL_WHITE 8 215788ca347SMarcel Moolenaar 216788ca347SMarcel Moolenaar #define XO_NUM_COLORS 9 217788ca347SMarcel Moolenaar 218788ca347SMarcel Moolenaar /* 219788ca347SMarcel Moolenaar * Yes, there's no blink. We're civilized. We like users. Blink 220788ca347SMarcel Moolenaar * isn't something one does to someone you like. Friends don't let 221788ca347SMarcel Moolenaar * friends use blink. On friends. You know what I mean. Blink is 222788ca347SMarcel Moolenaar * like, well, it's like bursting into show tunes at a funeral. It's 223788ca347SMarcel Moolenaar * just not done. Not something anyone wants. And on those rare 224d1a0d267SMarcel Moolenaar * instances where it might actually be appropriate, it's still wrong, 225d1a0d267SMarcel Moolenaar * since it's likely done by the wrong person for the wrong reason. 226d1a0d267SMarcel Moolenaar * Just like blink. And if I implemented blink, I'd be like a funeral 227788ca347SMarcel Moolenaar * director who adds "Would you like us to burst into show tunes?" on 228d1a0d267SMarcel Moolenaar * the list of questions asked while making funeral arrangements. 229788ca347SMarcel Moolenaar * It's formalizing wrongness in the wrong way. And we're just too 230788ca347SMarcel Moolenaar * civilized to do that. Hhhmph! 231788ca347SMarcel Moolenaar */ 232788ca347SMarcel Moolenaar #define XO_EFF_RESET (1<<0) 233788ca347SMarcel Moolenaar #define XO_EFF_NORMAL (1<<1) 234788ca347SMarcel Moolenaar #define XO_EFF_BOLD (1<<2) 235788ca347SMarcel Moolenaar #define XO_EFF_UNDERLINE (1<<3) 236788ca347SMarcel Moolenaar #define XO_EFF_INVERSE (1<<4) 237788ca347SMarcel Moolenaar 238d1a0d267SMarcel Moolenaar #define XO_EFF_CLEAR_BITS XO_EFF_RESET /* Reset gets reset, surprisingly */ 239788ca347SMarcel Moolenaar 240788ca347SMarcel Moolenaar typedef uint8_t xo_effect_t; 241788ca347SMarcel Moolenaar typedef uint8_t xo_color_t; 242788ca347SMarcel Moolenaar typedef struct xo_colors_s { 243788ca347SMarcel Moolenaar xo_effect_t xoc_effects; /* Current effect set */ 244788ca347SMarcel Moolenaar xo_color_t xoc_col_fg; /* Foreground color */ 245788ca347SMarcel Moolenaar xo_color_t xoc_col_bg; /* Background color */ 246788ca347SMarcel Moolenaar } xo_colors_t; 247788ca347SMarcel Moolenaar 24831337658SMarcel Moolenaar /* 24931337658SMarcel Moolenaar * xo_handle_t: this is the principle data structure for libxo. 250d1a0d267SMarcel Moolenaar * It's used as a store for state, options, content, and all manor 251d1a0d267SMarcel Moolenaar * of other information. 25231337658SMarcel Moolenaar */ 25331337658SMarcel Moolenaar struct xo_handle_s { 254d1a0d267SMarcel Moolenaar xo_xof_flags_t xo_flags; /* Flags (XOF_*) from the user*/ 255d1a0d267SMarcel Moolenaar xo_xof_flags_t xo_iflags; /* Internal flags (XOIF_*) */ 256d1a0d267SMarcel Moolenaar xo_style_t xo_style; /* XO_STYLE_* value */ 25731337658SMarcel Moolenaar unsigned short xo_indent; /* Indent level (if pretty) */ 25831337658SMarcel Moolenaar unsigned short xo_indent_by; /* Indent amount (tab stop) */ 25931337658SMarcel Moolenaar xo_write_func_t xo_write; /* Write callback */ 260a0f704ffSMarcel Moolenaar xo_close_func_t xo_close; /* Close callback */ 261545ddfbeSMarcel Moolenaar xo_flush_func_t xo_flush; /* Flush callback */ 26231337658SMarcel Moolenaar xo_formatter_t xo_formatter; /* Custom formating function */ 26331337658SMarcel Moolenaar xo_checkpointer_t xo_checkpointer; /* Custom formating support function */ 26431337658SMarcel Moolenaar void *xo_opaque; /* Opaque data for write function */ 26531337658SMarcel Moolenaar xo_buffer_t xo_data; /* Output data */ 26631337658SMarcel Moolenaar xo_buffer_t xo_fmt; /* Work area for building format strings */ 26731337658SMarcel Moolenaar xo_buffer_t xo_attrs; /* Work area for building XML attributes */ 26831337658SMarcel Moolenaar xo_buffer_t xo_predicate; /* Work area for building XPath predicates */ 26931337658SMarcel Moolenaar xo_stack_t *xo_stack; /* Stack pointer */ 27031337658SMarcel Moolenaar int xo_depth; /* Depth of stack */ 27131337658SMarcel Moolenaar int xo_stack_size; /* Size of the stack */ 27231337658SMarcel Moolenaar xo_info_t *xo_info; /* Info fields for all elements */ 27331337658SMarcel Moolenaar int xo_info_count; /* Number of info entries */ 27431337658SMarcel Moolenaar va_list xo_vap; /* Variable arguments (stdargs) */ 27531337658SMarcel Moolenaar char *xo_leading_xpath; /* A leading XPath expression */ 27631337658SMarcel Moolenaar mbstate_t xo_mbstate; /* Multi-byte character conversion state */ 2778a6eceffSPhil Shafer ssize_t xo_anchor_offset; /* Start of anchored text */ 2788a6eceffSPhil Shafer ssize_t xo_anchor_columns; /* Number of columns since the start anchor */ 2798a6eceffSPhil Shafer ssize_t xo_anchor_min_width; /* Desired width of anchored text */ 2808a6eceffSPhil Shafer ssize_t xo_units_offset; /* Start of units insertion point */ 2818a6eceffSPhil Shafer ssize_t xo_columns; /* Columns emitted during this xo_emit call */ 282788ca347SMarcel Moolenaar uint8_t xo_color_map_fg[XO_NUM_COLORS]; /* Foreground color mappings */ 283788ca347SMarcel Moolenaar uint8_t xo_color_map_bg[XO_NUM_COLORS]; /* Background color mappings */ 284788ca347SMarcel Moolenaar xo_colors_t xo_colors; /* Current color and effect values */ 285788ca347SMarcel Moolenaar xo_buffer_t xo_color_buf; /* HTML: buffer of colors and effects */ 286788ca347SMarcel Moolenaar char *xo_version; /* Version string */ 287d1a0d267SMarcel Moolenaar int xo_errno; /* Saved errno for "%m" */ 288d1a0d267SMarcel Moolenaar char *xo_gt_domain; /* Gettext domain, suitable for dgettext(3) */ 289d1a0d267SMarcel Moolenaar xo_encoder_func_t xo_encoder; /* Encoding function */ 290d1a0d267SMarcel Moolenaar void *xo_private; /* Private data for external encoders */ 29131337658SMarcel Moolenaar }; 29231337658SMarcel Moolenaar 293d1a0d267SMarcel Moolenaar /* Flag operations */ 294d1a0d267SMarcel Moolenaar #define XOF_BIT_ISSET(_flag, _bit) (((_flag) & (_bit)) ? 1 : 0) 295d1a0d267SMarcel Moolenaar #define XOF_BIT_SET(_flag, _bit) do { (_flag) |= (_bit); } while (0) 296d1a0d267SMarcel Moolenaar #define XOF_BIT_CLEAR(_flag, _bit) do { (_flag) &= ~(_bit); } while (0) 297d1a0d267SMarcel Moolenaar 298d1a0d267SMarcel Moolenaar #define XOF_ISSET(_xop, _bit) XOF_BIT_ISSET(_xop->xo_flags, _bit) 299d1a0d267SMarcel Moolenaar #define XOF_SET(_xop, _bit) XOF_BIT_SET(_xop->xo_flags, _bit) 300d1a0d267SMarcel Moolenaar #define XOF_CLEAR(_xop, _bit) XOF_BIT_CLEAR(_xop->xo_flags, _bit) 301d1a0d267SMarcel Moolenaar 302d1a0d267SMarcel Moolenaar #define XOIF_ISSET(_xop, _bit) XOF_BIT_ISSET(_xop->xo_iflags, _bit) 303d1a0d267SMarcel Moolenaar #define XOIF_SET(_xop, _bit) XOF_BIT_SET(_xop->xo_iflags, _bit) 304d1a0d267SMarcel Moolenaar #define XOIF_CLEAR(_xop, _bit) XOF_BIT_CLEAR(_xop->xo_iflags, _bit) 305d1a0d267SMarcel Moolenaar 306d1a0d267SMarcel Moolenaar /* Internal flags */ 307d1a0d267SMarcel Moolenaar #define XOIF_REORDER XOF_BIT(0) /* Reordering fields; record field info */ 308d1a0d267SMarcel Moolenaar #define XOIF_DIV_OPEN XOF_BIT(1) /* A <div> is open */ 309d1a0d267SMarcel Moolenaar #define XOIF_TOP_EMITTED XOF_BIT(2) /* The top JSON braces have been emitted */ 310d1a0d267SMarcel Moolenaar #define XOIF_ANCHOR XOF_BIT(3) /* An anchor is in place */ 311d1a0d267SMarcel Moolenaar 312d1a0d267SMarcel Moolenaar #define XOIF_UNITS_PENDING XOF_BIT(4) /* We have a units-insertion pending */ 313d1a0d267SMarcel Moolenaar #define XOIF_INIT_IN_PROGRESS XOF_BIT(5) /* Init of handle is in progress */ 314d1a0d267SMarcel Moolenaar 31531337658SMarcel Moolenaar /* Flags for formatting functions */ 31631337658SMarcel Moolenaar typedef unsigned long xo_xff_flags_t; 31731337658SMarcel Moolenaar #define XFF_COLON (1<<0) /* Append a ":" */ 31831337658SMarcel Moolenaar #define XFF_COMMA (1<<1) /* Append a "," iff there's more output */ 31931337658SMarcel Moolenaar #define XFF_WS (1<<2) /* Append a blank */ 320d1a0d267SMarcel Moolenaar #define XFF_ENCODE_ONLY (1<<3) /* Only emit for encoding styles (XML, JSON) */ 32131337658SMarcel Moolenaar 32231337658SMarcel Moolenaar #define XFF_QUOTE (1<<4) /* Force quotes */ 32331337658SMarcel Moolenaar #define XFF_NOQUOTE (1<<5) /* Force no quotes */ 324d1a0d267SMarcel Moolenaar #define XFF_DISPLAY_ONLY (1<<6) /* Only emit for display styles (text, html) */ 32531337658SMarcel Moolenaar #define XFF_KEY (1<<7) /* Field is a key (for XPath) */ 32631337658SMarcel Moolenaar 32731337658SMarcel Moolenaar #define XFF_XML (1<<8) /* Force XML encoding style (for XPath) */ 32831337658SMarcel Moolenaar #define XFF_ATTR (1<<9) /* Escape value using attribute rules (XML) */ 32931337658SMarcel Moolenaar #define XFF_BLANK_LINE (1<<10) /* Emit a blank line */ 33031337658SMarcel Moolenaar #define XFF_NO_OUTPUT (1<<11) /* Do not make any output */ 33131337658SMarcel Moolenaar 33231337658SMarcel Moolenaar #define XFF_TRIM_WS (1<<12) /* Trim whitespace off encoded values */ 33331337658SMarcel Moolenaar #define XFF_LEAF_LIST (1<<13) /* A leaf-list (list of values) */ 33431337658SMarcel Moolenaar #define XFF_UNESCAPE (1<<14) /* Need to printf-style unescape the value */ 335d1a0d267SMarcel Moolenaar #define XFF_HUMANIZE (1<<15) /* Humanize the value (for display styles) */ 336d1a0d267SMarcel Moolenaar 337d1a0d267SMarcel Moolenaar #define XFF_HN_SPACE (1<<16) /* Humanize: put space before suffix */ 338d1a0d267SMarcel Moolenaar #define XFF_HN_DECIMAL (1<<17) /* Humanize: add one decimal place if <10 */ 339d1a0d267SMarcel Moolenaar #define XFF_HN_1000 (1<<18) /* Humanize: use 1000, not 1024 */ 340d1a0d267SMarcel Moolenaar #define XFF_GT_FIELD (1<<19) /* Call gettext() on a field */ 341d1a0d267SMarcel Moolenaar 342d1a0d267SMarcel Moolenaar #define XFF_GT_PLURAL (1<<20) /* Call dngettext to find plural form */ 34342ff34c3SPhil Shafer #define XFF_ARGUMENT (1<<21) /* Content provided via argument */ 344d1a0d267SMarcel Moolenaar 345d1a0d267SMarcel Moolenaar /* Flags to turn off when we don't want i18n processing */ 346d1a0d267SMarcel Moolenaar #define XFF_GT_FLAGS (XFF_GT_FIELD | XFF_GT_PLURAL) 34731337658SMarcel Moolenaar 34831337658SMarcel Moolenaar /* 34931337658SMarcel Moolenaar * Normal printf has width and precision, which for strings operate as 35031337658SMarcel Moolenaar * min and max number of columns. But this depends on the idea that 35131337658SMarcel Moolenaar * one byte means one column, which UTF-8 and multi-byte characters 35231337658SMarcel Moolenaar * pitches on its ear. It may take 40 bytes of data to populate 14 35331337658SMarcel Moolenaar * columns, but we can't go off looking at 40 bytes of data without the 35431337658SMarcel Moolenaar * caller's permission for fear/knowledge that we'll generate core files. 35531337658SMarcel Moolenaar * 35631337658SMarcel Moolenaar * So we make three values, distinguishing between "max column" and 35731337658SMarcel Moolenaar * "number of bytes that we will inspect inspect safely" We call the 35831337658SMarcel Moolenaar * later "size", and make the format "%[[<min>].[[<size>].<max>]]s". 35931337658SMarcel Moolenaar * 36031337658SMarcel Moolenaar * Under the "first do no harm" theory, we default "max" to "size". 36131337658SMarcel Moolenaar * This is a reasonable assumption for folks that don't grok the 36231337658SMarcel Moolenaar * MBS/WCS/UTF-8 world, and while it will be annoying, it will never 36331337658SMarcel Moolenaar * be evil. 36431337658SMarcel Moolenaar * 36531337658SMarcel Moolenaar * For example, xo_emit("{:tag/%-14.14s}", buf) will make 14 36631337658SMarcel Moolenaar * columns of output, but will never look at more than 14 bytes of the 36731337658SMarcel Moolenaar * input buffer. This is mostly compatible with printf and caller's 36831337658SMarcel Moolenaar * expectations. 36931337658SMarcel Moolenaar * 37031337658SMarcel Moolenaar * In contrast xo_emit("{:tag/%-14..14s}", buf) will look at however 37131337658SMarcel Moolenaar * many bytes (or until a NUL is seen) are needed to fill 14 columns 37231337658SMarcel Moolenaar * of output. xo_emit("{:tag/%-14.*.14s}", xx, buf) will look at up 37331337658SMarcel Moolenaar * to xx bytes (or until a NUL is seen) in order to fill 14 columns 37431337658SMarcel Moolenaar * of output. 37531337658SMarcel Moolenaar * 37631337658SMarcel Moolenaar * It's fairly amazing how a good idea (handle all languages of the 37731337658SMarcel Moolenaar * world) blows such a big hole in the bottom of the fairly weak boat 37831337658SMarcel Moolenaar * that is C string handling. The simplicity and completenesss are 37931337658SMarcel Moolenaar * sunk in ways we haven't even begun to understand. 38031337658SMarcel Moolenaar */ 38131337658SMarcel Moolenaar #define XF_WIDTH_MIN 0 /* Minimal width */ 38231337658SMarcel Moolenaar #define XF_WIDTH_SIZE 1 /* Maximum number of bytes to examine */ 38331337658SMarcel Moolenaar #define XF_WIDTH_MAX 2 /* Maximum width */ 38431337658SMarcel Moolenaar #define XF_WIDTH_NUM 3 /* Numeric fields in printf (min.size.max) */ 38531337658SMarcel Moolenaar 38631337658SMarcel Moolenaar /* Input and output string encodings */ 38731337658SMarcel Moolenaar #define XF_ENC_WIDE 1 /* Wide characters (wchar_t) */ 38831337658SMarcel Moolenaar #define XF_ENC_UTF8 2 /* UTF-8 */ 38931337658SMarcel Moolenaar #define XF_ENC_LOCALE 3 /* Current locale */ 39031337658SMarcel Moolenaar 39131337658SMarcel Moolenaar /* 39231337658SMarcel Moolenaar * A place to parse printf-style format flags for each field 39331337658SMarcel Moolenaar */ 39431337658SMarcel Moolenaar typedef struct xo_format_s { 39531337658SMarcel Moolenaar unsigned char xf_fc; /* Format character */ 39631337658SMarcel Moolenaar unsigned char xf_enc; /* Encoding of the string (XF_ENC_*) */ 39731337658SMarcel Moolenaar unsigned char xf_skip; /* Skip this field */ 39831337658SMarcel Moolenaar unsigned char xf_lflag; /* 'l' (long) */ 39931337658SMarcel Moolenaar unsigned char xf_hflag;; /* 'h' (half) */ 40031337658SMarcel Moolenaar unsigned char xf_jflag; /* 'j' (intmax_t) */ 40131337658SMarcel Moolenaar unsigned char xf_tflag; /* 't' (ptrdiff_t) */ 40231337658SMarcel Moolenaar unsigned char xf_zflag; /* 'z' (size_t) */ 40331337658SMarcel Moolenaar unsigned char xf_qflag; /* 'q' (quad_t) */ 40431337658SMarcel Moolenaar unsigned char xf_seen_minus; /* Seen a minus */ 40531337658SMarcel Moolenaar int xf_leading_zero; /* Seen a leading zero (zero fill) */ 40631337658SMarcel Moolenaar unsigned xf_dots; /* Seen one or more '.'s */ 40731337658SMarcel Moolenaar int xf_width[XF_WIDTH_NUM]; /* Width/precision/size numeric fields */ 40831337658SMarcel Moolenaar unsigned xf_stars; /* Seen one or more '*'s */ 40931337658SMarcel Moolenaar unsigned char xf_star[XF_WIDTH_NUM]; /* Seen one or more '*'s */ 41031337658SMarcel Moolenaar } xo_format_t; 41131337658SMarcel Moolenaar 41231337658SMarcel Moolenaar /* 413d1a0d267SMarcel Moolenaar * This structure represents the parsed field information, suitable for 414d1a0d267SMarcel Moolenaar * processing by xo_do_emit and anything else that needs to parse fields. 415d1a0d267SMarcel Moolenaar * Note that all pointers point to the main format string. 416d1a0d267SMarcel Moolenaar * 417d1a0d267SMarcel Moolenaar * XXX This is a first step toward compilable or cachable format 418d1a0d267SMarcel Moolenaar * strings. We can also cache the results of dgettext when no format 419d1a0d267SMarcel Moolenaar * is used, assuming the 'p' modifier has _not_ been set. 42031337658SMarcel Moolenaar */ 421d1a0d267SMarcel Moolenaar typedef struct xo_field_info_s { 422d1a0d267SMarcel Moolenaar xo_xff_flags_t xfi_flags; /* Flags for this field */ 423d1a0d267SMarcel Moolenaar unsigned xfi_ftype; /* Field type, as character (e.g. 'V') */ 424d1a0d267SMarcel Moolenaar const char *xfi_start; /* Start of field in the format string */ 425d1a0d267SMarcel Moolenaar const char *xfi_content; /* Field's content */ 426d1a0d267SMarcel Moolenaar const char *xfi_format; /* Field's Format */ 427d1a0d267SMarcel Moolenaar const char *xfi_encoding; /* Field's encoding format */ 428d1a0d267SMarcel Moolenaar const char *xfi_next; /* Next character in format string */ 4298a6eceffSPhil Shafer ssize_t xfi_len; /* Length of field */ 4308a6eceffSPhil Shafer ssize_t xfi_clen; /* Content length */ 4318a6eceffSPhil Shafer ssize_t xfi_flen; /* Format length */ 4328a6eceffSPhil Shafer ssize_t xfi_elen; /* Encoding length */ 433d1a0d267SMarcel Moolenaar unsigned xfi_fnum; /* Field number (if used; 0 otherwise) */ 434d1a0d267SMarcel Moolenaar unsigned xfi_renum; /* Reordered number (0 == no renumbering) */ 435d1a0d267SMarcel Moolenaar } xo_field_info_t; 436d1a0d267SMarcel Moolenaar 437d1a0d267SMarcel Moolenaar /* 438d1a0d267SMarcel Moolenaar * We keep a 'default' handle to allow callers to avoid having to 439d1a0d267SMarcel Moolenaar * allocate one. Passing NULL to any of our functions will use 440d1a0d267SMarcel Moolenaar * this default handle. Most functions have a variant that doesn't 441d1a0d267SMarcel Moolenaar * require a handle at all, since most output is to stdout, which 442d1a0d267SMarcel Moolenaar * the default handle handles handily. 443d1a0d267SMarcel Moolenaar */ 444d1a0d267SMarcel Moolenaar static THREAD_LOCAL(xo_handle_t) xo_default_handle; 445d1a0d267SMarcel Moolenaar static THREAD_LOCAL(int) xo_default_inited; 44631337658SMarcel Moolenaar static int xo_locale_inited; 447545ddfbeSMarcel Moolenaar static const char *xo_program; 44831337658SMarcel Moolenaar 44931337658SMarcel Moolenaar /* 45031337658SMarcel Moolenaar * To allow libxo to be used in diverse environment, we allow the 45131337658SMarcel Moolenaar * caller to give callbacks for memory allocation. 45231337658SMarcel Moolenaar */ 453d1a0d267SMarcel Moolenaar xo_realloc_func_t xo_realloc = realloc; 454d1a0d267SMarcel Moolenaar xo_free_func_t xo_free = free; 45531337658SMarcel Moolenaar 45631337658SMarcel Moolenaar /* Forward declarations */ 45731337658SMarcel Moolenaar static void 45831337658SMarcel Moolenaar xo_failure (xo_handle_t *xop, const char *fmt, ...); 45931337658SMarcel Moolenaar 4608a6eceffSPhil Shafer static ssize_t 461545ddfbeSMarcel Moolenaar xo_transition (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name, 462545ddfbeSMarcel Moolenaar xo_state_t new_state); 463545ddfbeSMarcel Moolenaar 46431337658SMarcel Moolenaar static void 46531337658SMarcel Moolenaar xo_buf_append_div (xo_handle_t *xop, const char *class, xo_xff_flags_t flags, 4668a6eceffSPhil Shafer const char *name, ssize_t nlen, 4678a6eceffSPhil Shafer const char *value, ssize_t vlen, 4688a6eceffSPhil Shafer const char *encoding, ssize_t elen); 46931337658SMarcel Moolenaar 47031337658SMarcel Moolenaar static void 47131337658SMarcel Moolenaar xo_anchor_clear (xo_handle_t *xop); 47231337658SMarcel Moolenaar 47331337658SMarcel Moolenaar /* 474788ca347SMarcel Moolenaar * xo_style is used to retrieve the current style. When we're built 475788ca347SMarcel Moolenaar * for "text only" mode, we use this function to drive the removal 476788ca347SMarcel Moolenaar * of most of the code in libxo. We return a constant and the compiler 477788ca347SMarcel Moolenaar * happily removes the non-text code that is not longer executed. This 478788ca347SMarcel Moolenaar * trims our code nicely without needing to trampel perfectly readable 479788ca347SMarcel Moolenaar * code with ifdefs. 480788ca347SMarcel Moolenaar */ 481d1a0d267SMarcel Moolenaar static inline xo_style_t 482788ca347SMarcel Moolenaar xo_style (xo_handle_t *xop UNUSED) 483788ca347SMarcel Moolenaar { 484788ca347SMarcel Moolenaar #ifdef LIBXO_TEXT_ONLY 485788ca347SMarcel Moolenaar return XO_STYLE_TEXT; 486788ca347SMarcel Moolenaar #else /* LIBXO_TEXT_ONLY */ 487788ca347SMarcel Moolenaar return xop->xo_style; 488788ca347SMarcel Moolenaar #endif /* LIBXO_TEXT_ONLY */ 489788ca347SMarcel Moolenaar } 490788ca347SMarcel Moolenaar 491788ca347SMarcel Moolenaar /* 49231337658SMarcel Moolenaar * Callback to write data to a FILE pointer 49331337658SMarcel Moolenaar */ 4948a6eceffSPhil Shafer static xo_ssize_t 49531337658SMarcel Moolenaar xo_write_to_file (void *opaque, const char *data) 49631337658SMarcel Moolenaar { 49731337658SMarcel Moolenaar FILE *fp = (FILE *) opaque; 498545ddfbeSMarcel Moolenaar 49931337658SMarcel Moolenaar return fprintf(fp, "%s", data); 50031337658SMarcel Moolenaar } 50131337658SMarcel Moolenaar 50231337658SMarcel Moolenaar /* 50331337658SMarcel Moolenaar * Callback to close a file 50431337658SMarcel Moolenaar */ 50531337658SMarcel Moolenaar static void 50631337658SMarcel Moolenaar xo_close_file (void *opaque) 50731337658SMarcel Moolenaar { 50831337658SMarcel Moolenaar FILE *fp = (FILE *) opaque; 509545ddfbeSMarcel Moolenaar 51031337658SMarcel Moolenaar fclose(fp); 51131337658SMarcel Moolenaar } 51231337658SMarcel Moolenaar 51331337658SMarcel Moolenaar /* 514545ddfbeSMarcel Moolenaar * Callback to flush a FILE pointer 515545ddfbeSMarcel Moolenaar */ 516545ddfbeSMarcel Moolenaar static int 517545ddfbeSMarcel Moolenaar xo_flush_file (void *opaque) 518545ddfbeSMarcel Moolenaar { 519545ddfbeSMarcel Moolenaar FILE *fp = (FILE *) opaque; 520545ddfbeSMarcel Moolenaar 521545ddfbeSMarcel Moolenaar return fflush(fp); 522545ddfbeSMarcel Moolenaar } 523545ddfbeSMarcel Moolenaar 524545ddfbeSMarcel Moolenaar /* 525d1a0d267SMarcel Moolenaar * Use a rotating stock of buffers to make a printable string 52631337658SMarcel Moolenaar */ 527d1a0d267SMarcel Moolenaar #define XO_NUMBUFS 8 528d1a0d267SMarcel Moolenaar #define XO_SMBUFSZ 128 529d1a0d267SMarcel Moolenaar 530d1a0d267SMarcel Moolenaar static const char * 531d1a0d267SMarcel Moolenaar xo_printable (const char *str) 53231337658SMarcel Moolenaar { 533d1a0d267SMarcel Moolenaar static THREAD_LOCAL(char) bufset[XO_NUMBUFS][XO_SMBUFSZ]; 534d1a0d267SMarcel Moolenaar static THREAD_LOCAL(int) bufnum = 0; 535d1a0d267SMarcel Moolenaar 536d1a0d267SMarcel Moolenaar if (str == NULL) 537d1a0d267SMarcel Moolenaar return ""; 538d1a0d267SMarcel Moolenaar 539d1a0d267SMarcel Moolenaar if (++bufnum == XO_NUMBUFS) 540d1a0d267SMarcel Moolenaar bufnum = 0; 541d1a0d267SMarcel Moolenaar 542d1a0d267SMarcel Moolenaar char *res = bufset[bufnum], *cp, *ep; 543d1a0d267SMarcel Moolenaar 544d1a0d267SMarcel Moolenaar for (cp = res, ep = res + XO_SMBUFSZ - 1; *str && cp < ep; cp++, str++) { 545d1a0d267SMarcel Moolenaar if (*str == '\n') { 546d1a0d267SMarcel Moolenaar *cp++ = '\\'; 547d1a0d267SMarcel Moolenaar *cp = 'n'; 548d1a0d267SMarcel Moolenaar } else if (*str == '\r') { 549d1a0d267SMarcel Moolenaar *cp++ = '\\'; 550d1a0d267SMarcel Moolenaar *cp = 'r'; 551d1a0d267SMarcel Moolenaar } else if (*str == '\"') { 552d1a0d267SMarcel Moolenaar *cp++ = '\\'; 553d1a0d267SMarcel Moolenaar *cp = '"'; 554d1a0d267SMarcel Moolenaar } else 555d1a0d267SMarcel Moolenaar *cp = *str; 55631337658SMarcel Moolenaar } 55731337658SMarcel Moolenaar 558d1a0d267SMarcel Moolenaar *cp = '\0'; 559d1a0d267SMarcel Moolenaar return res; 56031337658SMarcel Moolenaar } 56131337658SMarcel Moolenaar 56231337658SMarcel Moolenaar static int 56331337658SMarcel Moolenaar xo_depth_check (xo_handle_t *xop, int depth) 56431337658SMarcel Moolenaar { 56531337658SMarcel Moolenaar xo_stack_t *xsp; 56631337658SMarcel Moolenaar 56731337658SMarcel Moolenaar if (depth >= xop->xo_stack_size) { 568d1a0d267SMarcel Moolenaar depth += XO_DEPTH; /* Extra room */ 569d1a0d267SMarcel Moolenaar 57031337658SMarcel Moolenaar xsp = xo_realloc(xop->xo_stack, sizeof(xop->xo_stack[0]) * depth); 57131337658SMarcel Moolenaar if (xsp == NULL) { 57231337658SMarcel Moolenaar xo_failure(xop, "xo_depth_check: out of memory (%d)", depth); 573d1a0d267SMarcel Moolenaar return -1; 57431337658SMarcel Moolenaar } 57531337658SMarcel Moolenaar 57631337658SMarcel Moolenaar int count = depth - xop->xo_stack_size; 57731337658SMarcel Moolenaar 57831337658SMarcel Moolenaar bzero(xsp + xop->xo_stack_size, count * sizeof(*xsp)); 57931337658SMarcel Moolenaar xop->xo_stack_size = depth; 58031337658SMarcel Moolenaar xop->xo_stack = xsp; 58131337658SMarcel Moolenaar } 58231337658SMarcel Moolenaar 58331337658SMarcel Moolenaar return 0; 58431337658SMarcel Moolenaar } 58531337658SMarcel Moolenaar 58631337658SMarcel Moolenaar void 58731337658SMarcel Moolenaar xo_no_setlocale (void) 58831337658SMarcel Moolenaar { 58931337658SMarcel Moolenaar xo_locale_inited = 1; /* Skip initialization */ 59031337658SMarcel Moolenaar } 59131337658SMarcel Moolenaar 59231337658SMarcel Moolenaar /* 593545ddfbeSMarcel Moolenaar * We need to decide if stdout is line buffered (_IOLBF). Lacking a 594545ddfbeSMarcel Moolenaar * standard way to decide this (e.g. getlinebuf()), we have configure 595788ca347SMarcel Moolenaar * look to find __flbf, which glibc supported. If not, we'll rely on 596788ca347SMarcel Moolenaar * isatty, with the assumption that terminals are the only thing 597545ddfbeSMarcel Moolenaar * that's line buffered. We _could_ test for "steam._flags & _IOLBF", 598545ddfbeSMarcel Moolenaar * which is all __flbf does, but that's even tackier. Like a 599545ddfbeSMarcel Moolenaar * bedazzled Elvis outfit on an ugly lap dog sort of tacky. Not 600545ddfbeSMarcel Moolenaar * something we're willing to do. 601545ddfbeSMarcel Moolenaar */ 602545ddfbeSMarcel Moolenaar static int 603545ddfbeSMarcel Moolenaar xo_is_line_buffered (FILE *stream) 604545ddfbeSMarcel Moolenaar { 605545ddfbeSMarcel Moolenaar #if HAVE___FLBF 606545ddfbeSMarcel Moolenaar if (__flbf(stream)) 607545ddfbeSMarcel Moolenaar return 1; 608545ddfbeSMarcel Moolenaar #else /* HAVE___FLBF */ 609545ddfbeSMarcel Moolenaar if (isatty(fileno(stream))) 610545ddfbeSMarcel Moolenaar return 1; 611545ddfbeSMarcel Moolenaar #endif /* HAVE___FLBF */ 612545ddfbeSMarcel Moolenaar return 0; 613545ddfbeSMarcel Moolenaar } 614545ddfbeSMarcel Moolenaar 615545ddfbeSMarcel Moolenaar /* 61631337658SMarcel Moolenaar * Initialize an xo_handle_t, using both static defaults and 61731337658SMarcel Moolenaar * the global settings from the LIBXO_OPTIONS environment 61831337658SMarcel Moolenaar * variable. 61931337658SMarcel Moolenaar */ 62031337658SMarcel Moolenaar static void 62131337658SMarcel Moolenaar xo_init_handle (xo_handle_t *xop) 62231337658SMarcel Moolenaar { 62331337658SMarcel Moolenaar xop->xo_opaque = stdout; 62431337658SMarcel Moolenaar xop->xo_write = xo_write_to_file; 625545ddfbeSMarcel Moolenaar xop->xo_flush = xo_flush_file; 626545ddfbeSMarcel Moolenaar 627545ddfbeSMarcel Moolenaar if (xo_is_line_buffered(stdout)) 628d1a0d267SMarcel Moolenaar XOF_SET(xop, XOF_FLUSH_LINE); 62931337658SMarcel Moolenaar 63031337658SMarcel Moolenaar /* 631788ca347SMarcel Moolenaar * We only want to do color output on terminals, but we only want 632788ca347SMarcel Moolenaar * to do this if the user has asked for color. 633788ca347SMarcel Moolenaar */ 634d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_COLOR_ALLOWED) && isatty(1)) 635d1a0d267SMarcel Moolenaar XOF_SET(xop, XOF_COLOR); 636788ca347SMarcel Moolenaar 637788ca347SMarcel Moolenaar /* 63831337658SMarcel Moolenaar * We need to initialize the locale, which isn't really pretty. 63931337658SMarcel Moolenaar * Libraries should depend on their caller to set up the 64031337658SMarcel Moolenaar * environment. But we really can't count on the caller to do 64131337658SMarcel Moolenaar * this, because well, they won't. Trust me. 64231337658SMarcel Moolenaar */ 64331337658SMarcel Moolenaar if (!xo_locale_inited) { 64431337658SMarcel Moolenaar xo_locale_inited = 1; /* Only do this once */ 64531337658SMarcel Moolenaar 64631337658SMarcel Moolenaar const char *cp = getenv("LC_CTYPE"); 64731337658SMarcel Moolenaar if (cp == NULL) 64831337658SMarcel Moolenaar cp = getenv("LANG"); 64931337658SMarcel Moolenaar if (cp == NULL) 65031337658SMarcel Moolenaar cp = getenv("LC_ALL"); 65131337658SMarcel Moolenaar if (cp == NULL) 652d1a0d267SMarcel Moolenaar cp = "C"; /* Default for C programs */ 653c600d307SMarcel Moolenaar (void) setlocale(LC_CTYPE, cp); 65431337658SMarcel Moolenaar } 65531337658SMarcel Moolenaar 65631337658SMarcel Moolenaar /* 65731337658SMarcel Moolenaar * Initialize only the xo_buffers we know we'll need; the others 65831337658SMarcel Moolenaar * can be allocated as needed. 65931337658SMarcel Moolenaar */ 66031337658SMarcel Moolenaar xo_buf_init(&xop->xo_data); 66131337658SMarcel Moolenaar xo_buf_init(&xop->xo_fmt); 66231337658SMarcel Moolenaar 663d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_INIT_IN_PROGRESS)) 664d1a0d267SMarcel Moolenaar return; 665d1a0d267SMarcel Moolenaar XOIF_SET(xop, XOIF_INIT_IN_PROGRESS); 666d1a0d267SMarcel Moolenaar 66731337658SMarcel Moolenaar xop->xo_indent_by = XO_INDENT_BY; 66831337658SMarcel Moolenaar xo_depth_check(xop, XO_DEPTH); 66931337658SMarcel Moolenaar 67031337658SMarcel Moolenaar #if !defined(NO_LIBXO_OPTIONS) 671d1a0d267SMarcel Moolenaar if (!XOF_ISSET(xop, XOF_NO_ENV)) { 67231337658SMarcel Moolenaar char *env = getenv("LIBXO_OPTIONS"); 67331337658SMarcel Moolenaar if (env) 67431337658SMarcel Moolenaar xo_set_options(xop, env); 675d1a0d267SMarcel Moolenaar 67631337658SMarcel Moolenaar } 67731337658SMarcel Moolenaar #endif /* NO_GETENV */ 678d1a0d267SMarcel Moolenaar 679d1a0d267SMarcel Moolenaar XOIF_CLEAR(xop, XOIF_INIT_IN_PROGRESS); 68031337658SMarcel Moolenaar } 68131337658SMarcel Moolenaar 68231337658SMarcel Moolenaar /* 68331337658SMarcel Moolenaar * Initialize the default handle. 68431337658SMarcel Moolenaar */ 68531337658SMarcel Moolenaar static void 68631337658SMarcel Moolenaar xo_default_init (void) 68731337658SMarcel Moolenaar { 68831337658SMarcel Moolenaar xo_handle_t *xop = &xo_default_handle; 68931337658SMarcel Moolenaar 69031337658SMarcel Moolenaar xo_init_handle(xop); 69131337658SMarcel Moolenaar 69231337658SMarcel Moolenaar xo_default_inited = 1; 69331337658SMarcel Moolenaar } 69431337658SMarcel Moolenaar 69531337658SMarcel Moolenaar /* 69631337658SMarcel Moolenaar * Cheap convenience function to return either the argument, or 69731337658SMarcel Moolenaar * the internal handle, after it has been initialized. The usage 69831337658SMarcel Moolenaar * is: 69931337658SMarcel Moolenaar * xop = xo_default(xop); 70031337658SMarcel Moolenaar */ 70131337658SMarcel Moolenaar static xo_handle_t * 70231337658SMarcel Moolenaar xo_default (xo_handle_t *xop) 70331337658SMarcel Moolenaar { 70431337658SMarcel Moolenaar if (xop == NULL) { 70531337658SMarcel Moolenaar if (xo_default_inited == 0) 70631337658SMarcel Moolenaar xo_default_init(); 70731337658SMarcel Moolenaar xop = &xo_default_handle; 70831337658SMarcel Moolenaar } 70931337658SMarcel Moolenaar 71031337658SMarcel Moolenaar return xop; 71131337658SMarcel Moolenaar } 71231337658SMarcel Moolenaar 71331337658SMarcel Moolenaar /* 71431337658SMarcel Moolenaar * Return the number of spaces we should be indenting. If 715788ca347SMarcel Moolenaar * we are pretty-printing, this is indent * indent_by. 71631337658SMarcel Moolenaar */ 71731337658SMarcel Moolenaar static int 71831337658SMarcel Moolenaar xo_indent (xo_handle_t *xop) 71931337658SMarcel Moolenaar { 72031337658SMarcel Moolenaar int rc = 0; 72131337658SMarcel Moolenaar 72231337658SMarcel Moolenaar xop = xo_default(xop); 72331337658SMarcel Moolenaar 724d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_PRETTY)) { 72531337658SMarcel Moolenaar rc = xop->xo_indent * xop->xo_indent_by; 726d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_TOP_EMITTED)) 72731337658SMarcel Moolenaar rc += xop->xo_indent_by; 72831337658SMarcel Moolenaar } 72931337658SMarcel Moolenaar 730545ddfbeSMarcel Moolenaar return (rc > 0) ? rc : 0; 73131337658SMarcel Moolenaar } 73231337658SMarcel Moolenaar 73331337658SMarcel Moolenaar static void 73431337658SMarcel Moolenaar xo_buf_indent (xo_handle_t *xop, int indent) 73531337658SMarcel Moolenaar { 73631337658SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_data; 73731337658SMarcel Moolenaar 73831337658SMarcel Moolenaar if (indent <= 0) 73931337658SMarcel Moolenaar indent = xo_indent(xop); 74031337658SMarcel Moolenaar 74131337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, indent)) 74231337658SMarcel Moolenaar return; 74331337658SMarcel Moolenaar 74431337658SMarcel Moolenaar memset(xbp->xb_curp, ' ', indent); 74531337658SMarcel Moolenaar xbp->xb_curp += indent; 74631337658SMarcel Moolenaar } 74731337658SMarcel Moolenaar 74831337658SMarcel Moolenaar static char xo_xml_amp[] = "&"; 74931337658SMarcel Moolenaar static char xo_xml_lt[] = "<"; 75031337658SMarcel Moolenaar static char xo_xml_gt[] = ">"; 75131337658SMarcel Moolenaar static char xo_xml_quot[] = """; 75231337658SMarcel Moolenaar 7538a6eceffSPhil Shafer static ssize_t 7548a6eceffSPhil Shafer xo_escape_xml (xo_buffer_t *xbp, ssize_t len, xo_xff_flags_t flags) 75531337658SMarcel Moolenaar { 7568a6eceffSPhil Shafer ssize_t slen; 7578a6eceffSPhil Shafer ssize_t delta = 0; 75831337658SMarcel Moolenaar char *cp, *ep, *ip; 75931337658SMarcel Moolenaar const char *sp; 7608a6eceffSPhil Shafer int attr = XOF_BIT_ISSET(flags, XFF_ATTR); 76131337658SMarcel Moolenaar 76231337658SMarcel Moolenaar for (cp = xbp->xb_curp, ep = cp + len; cp < ep; cp++) { 76331337658SMarcel Moolenaar /* We're subtracting 2: 1 for the NUL, 1 for the char we replace */ 76431337658SMarcel Moolenaar if (*cp == '<') 76531337658SMarcel Moolenaar delta += sizeof(xo_xml_lt) - 2; 76631337658SMarcel Moolenaar else if (*cp == '>') 76731337658SMarcel Moolenaar delta += sizeof(xo_xml_gt) - 2; 76831337658SMarcel Moolenaar else if (*cp == '&') 76931337658SMarcel Moolenaar delta += sizeof(xo_xml_amp) - 2; 77031337658SMarcel Moolenaar else if (attr && *cp == '"') 77131337658SMarcel Moolenaar delta += sizeof(xo_xml_quot) - 2; 77231337658SMarcel Moolenaar } 77331337658SMarcel Moolenaar 77431337658SMarcel Moolenaar if (delta == 0) /* Nothing to escape; bail */ 77531337658SMarcel Moolenaar return len; 77631337658SMarcel Moolenaar 77731337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, delta)) /* No room; bail, but don't append */ 77831337658SMarcel Moolenaar return 0; 77931337658SMarcel Moolenaar 78031337658SMarcel Moolenaar ep = xbp->xb_curp; 78131337658SMarcel Moolenaar cp = ep + len; 78231337658SMarcel Moolenaar ip = cp + delta; 78331337658SMarcel Moolenaar do { 78431337658SMarcel Moolenaar cp -= 1; 78531337658SMarcel Moolenaar ip -= 1; 78631337658SMarcel Moolenaar 78731337658SMarcel Moolenaar if (*cp == '<') 78831337658SMarcel Moolenaar sp = xo_xml_lt; 78931337658SMarcel Moolenaar else if (*cp == '>') 79031337658SMarcel Moolenaar sp = xo_xml_gt; 79131337658SMarcel Moolenaar else if (*cp == '&') 79231337658SMarcel Moolenaar sp = xo_xml_amp; 79331337658SMarcel Moolenaar else if (attr && *cp == '"') 79431337658SMarcel Moolenaar sp = xo_xml_quot; 79531337658SMarcel Moolenaar else { 79631337658SMarcel Moolenaar *ip = *cp; 79731337658SMarcel Moolenaar continue; 79831337658SMarcel Moolenaar } 79931337658SMarcel Moolenaar 80031337658SMarcel Moolenaar slen = strlen(sp); 80131337658SMarcel Moolenaar ip -= slen - 1; 80231337658SMarcel Moolenaar memcpy(ip, sp, slen); 80331337658SMarcel Moolenaar 80431337658SMarcel Moolenaar } while (cp > ep && cp != ip); 80531337658SMarcel Moolenaar 80631337658SMarcel Moolenaar return len + delta; 80731337658SMarcel Moolenaar } 80831337658SMarcel Moolenaar 8098a6eceffSPhil Shafer static ssize_t 8108a6eceffSPhil Shafer xo_escape_json (xo_buffer_t *xbp, ssize_t len, xo_xff_flags_t flags UNUSED) 81131337658SMarcel Moolenaar { 8128a6eceffSPhil Shafer ssize_t delta = 0; 81331337658SMarcel Moolenaar char *cp, *ep, *ip; 81431337658SMarcel Moolenaar 81531337658SMarcel Moolenaar for (cp = xbp->xb_curp, ep = cp + len; cp < ep; cp++) { 816545ddfbeSMarcel Moolenaar if (*cp == '\\' || *cp == '"') 81731337658SMarcel Moolenaar delta += 1; 818545ddfbeSMarcel Moolenaar else if (*cp == '\n' || *cp == '\r') 81931337658SMarcel Moolenaar delta += 1; 82031337658SMarcel Moolenaar } 82131337658SMarcel Moolenaar 82231337658SMarcel Moolenaar if (delta == 0) /* Nothing to escape; bail */ 82331337658SMarcel Moolenaar return len; 82431337658SMarcel Moolenaar 82531337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, delta)) /* No room; bail, but don't append */ 82631337658SMarcel Moolenaar return 0; 82731337658SMarcel Moolenaar 82831337658SMarcel Moolenaar ep = xbp->xb_curp; 82931337658SMarcel Moolenaar cp = ep + len; 83031337658SMarcel Moolenaar ip = cp + delta; 83131337658SMarcel Moolenaar do { 83231337658SMarcel Moolenaar cp -= 1; 83331337658SMarcel Moolenaar ip -= 1; 83431337658SMarcel Moolenaar 835545ddfbeSMarcel Moolenaar if (*cp == '\\' || *cp == '"') { 83631337658SMarcel Moolenaar *ip-- = *cp; 83731337658SMarcel Moolenaar *ip = '\\'; 838545ddfbeSMarcel Moolenaar } else if (*cp == '\n') { 839545ddfbeSMarcel Moolenaar *ip-- = 'n'; 840545ddfbeSMarcel Moolenaar *ip = '\\'; 841545ddfbeSMarcel Moolenaar } else if (*cp == '\r') { 842545ddfbeSMarcel Moolenaar *ip-- = 'r'; 843545ddfbeSMarcel Moolenaar *ip = '\\'; 844545ddfbeSMarcel Moolenaar } else { 845545ddfbeSMarcel Moolenaar *ip = *cp; 846545ddfbeSMarcel Moolenaar } 84731337658SMarcel Moolenaar 84831337658SMarcel Moolenaar } while (cp > ep && cp != ip); 84931337658SMarcel Moolenaar 85031337658SMarcel Moolenaar return len + delta; 85131337658SMarcel Moolenaar } 85231337658SMarcel Moolenaar 85331337658SMarcel Moolenaar /* 854d1a0d267SMarcel Moolenaar * PARAM-VALUE = UTF-8-STRING ; characters '"', '\' and 855d1a0d267SMarcel Moolenaar * ; ']' MUST be escaped. 85631337658SMarcel Moolenaar */ 8578a6eceffSPhil Shafer static ssize_t 8588a6eceffSPhil Shafer xo_escape_sdparams (xo_buffer_t *xbp, ssize_t len, xo_xff_flags_t flags UNUSED) 85931337658SMarcel Moolenaar { 8608a6eceffSPhil Shafer ssize_t delta = 0; 861d1a0d267SMarcel Moolenaar char *cp, *ep, *ip; 86231337658SMarcel Moolenaar 863d1a0d267SMarcel Moolenaar for (cp = xbp->xb_curp, ep = cp + len; cp < ep; cp++) { 864d1a0d267SMarcel Moolenaar if (*cp == '\\' || *cp == '"' || *cp == ']') 865d1a0d267SMarcel Moolenaar delta += 1; 86631337658SMarcel Moolenaar } 86731337658SMarcel Moolenaar 868d1a0d267SMarcel Moolenaar if (delta == 0) /* Nothing to escape; bail */ 869d1a0d267SMarcel Moolenaar return len; 870788ca347SMarcel Moolenaar 871d1a0d267SMarcel Moolenaar if (!xo_buf_has_room(xbp, delta)) /* No room; bail, but don't append */ 872d1a0d267SMarcel Moolenaar return 0; 873788ca347SMarcel Moolenaar 874d1a0d267SMarcel Moolenaar ep = xbp->xb_curp; 875d1a0d267SMarcel Moolenaar cp = ep + len; 876d1a0d267SMarcel Moolenaar ip = cp + delta; 877d1a0d267SMarcel Moolenaar do { 878d1a0d267SMarcel Moolenaar cp -= 1; 879d1a0d267SMarcel Moolenaar ip -= 1; 880d1a0d267SMarcel Moolenaar 881d1a0d267SMarcel Moolenaar if (*cp == '\\' || *cp == '"' || *cp == ']') { 882d1a0d267SMarcel Moolenaar *ip-- = *cp; 883d1a0d267SMarcel Moolenaar *ip = '\\'; 884d1a0d267SMarcel Moolenaar } else { 885d1a0d267SMarcel Moolenaar *ip = *cp; 886d1a0d267SMarcel Moolenaar } 887d1a0d267SMarcel Moolenaar 888d1a0d267SMarcel Moolenaar } while (cp > ep && cp != ip); 889d1a0d267SMarcel Moolenaar 890d1a0d267SMarcel Moolenaar return len + delta; 891788ca347SMarcel Moolenaar } 892788ca347SMarcel Moolenaar 89331337658SMarcel Moolenaar static void 89431337658SMarcel Moolenaar xo_buf_escape (xo_handle_t *xop, xo_buffer_t *xbp, 8958a6eceffSPhil Shafer const char *str, ssize_t len, xo_xff_flags_t flags) 89631337658SMarcel Moolenaar { 89731337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, len)) 89831337658SMarcel Moolenaar return; 89931337658SMarcel Moolenaar 90031337658SMarcel Moolenaar memcpy(xbp->xb_curp, str, len); 90131337658SMarcel Moolenaar 902788ca347SMarcel Moolenaar switch (xo_style(xop)) { 90331337658SMarcel Moolenaar case XO_STYLE_XML: 90431337658SMarcel Moolenaar case XO_STYLE_HTML: 905d1a0d267SMarcel Moolenaar len = xo_escape_xml(xbp, len, flags); 90631337658SMarcel Moolenaar break; 90731337658SMarcel Moolenaar 90831337658SMarcel Moolenaar case XO_STYLE_JSON: 909d1a0d267SMarcel Moolenaar len = xo_escape_json(xbp, len, flags); 910d1a0d267SMarcel Moolenaar break; 911d1a0d267SMarcel Moolenaar 912d1a0d267SMarcel Moolenaar case XO_STYLE_SDPARAMS: 913d1a0d267SMarcel Moolenaar len = xo_escape_sdparams(xbp, len, flags); 91431337658SMarcel Moolenaar break; 91531337658SMarcel Moolenaar } 91631337658SMarcel Moolenaar 91731337658SMarcel Moolenaar xbp->xb_curp += len; 91831337658SMarcel Moolenaar } 91931337658SMarcel Moolenaar 92031337658SMarcel Moolenaar /* 92131337658SMarcel Moolenaar * Write the current contents of the data buffer using the handle's 92231337658SMarcel Moolenaar * xo_write function. 92331337658SMarcel Moolenaar */ 9248a6eceffSPhil Shafer static ssize_t 92531337658SMarcel Moolenaar xo_write (xo_handle_t *xop) 92631337658SMarcel Moolenaar { 9278a6eceffSPhil Shafer ssize_t rc = 0; 92831337658SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_data; 92931337658SMarcel Moolenaar 93031337658SMarcel Moolenaar if (xbp->xb_curp != xbp->xb_bufp) { 93131337658SMarcel Moolenaar xo_buf_append(xbp, "", 1); /* Append ending NUL */ 93231337658SMarcel Moolenaar xo_anchor_clear(xop); 933d1a0d267SMarcel Moolenaar if (xop->xo_write) 934545ddfbeSMarcel Moolenaar rc = xop->xo_write(xop->xo_opaque, xbp->xb_bufp); 93531337658SMarcel Moolenaar xbp->xb_curp = xbp->xb_bufp; 93631337658SMarcel Moolenaar } 93731337658SMarcel Moolenaar 93831337658SMarcel Moolenaar /* Turn off the flags that don't survive across writes */ 939d1a0d267SMarcel Moolenaar XOIF_CLEAR(xop, XOIF_UNITS_PENDING); 940545ddfbeSMarcel Moolenaar 941545ddfbeSMarcel Moolenaar return rc; 94231337658SMarcel Moolenaar } 94331337658SMarcel Moolenaar 94431337658SMarcel Moolenaar /* 94531337658SMarcel Moolenaar * Format arguments into our buffer. If a custom formatter has been set, 94631337658SMarcel Moolenaar * we use that to do the work; otherwise we vsnprintf(). 94731337658SMarcel Moolenaar */ 9488a6eceffSPhil Shafer static ssize_t 94931337658SMarcel Moolenaar xo_vsnprintf (xo_handle_t *xop, xo_buffer_t *xbp, const char *fmt, va_list vap) 95031337658SMarcel Moolenaar { 95131337658SMarcel Moolenaar va_list va_local; 9528a6eceffSPhil Shafer ssize_t rc; 9538a6eceffSPhil Shafer ssize_t left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp); 95431337658SMarcel Moolenaar 95531337658SMarcel Moolenaar va_copy(va_local, vap); 95631337658SMarcel Moolenaar 95731337658SMarcel Moolenaar if (xop->xo_formatter) 95831337658SMarcel Moolenaar rc = xop->xo_formatter(xop, xbp->xb_curp, left, fmt, va_local); 95931337658SMarcel Moolenaar else 96031337658SMarcel Moolenaar rc = vsnprintf(xbp->xb_curp, left, fmt, va_local); 96131337658SMarcel Moolenaar 962788ca347SMarcel Moolenaar if (rc >= left) { 963c600d307SMarcel Moolenaar if (!xo_buf_has_room(xbp, rc)) { 964c600d307SMarcel Moolenaar va_end(va_local); 96531337658SMarcel Moolenaar return -1; 966c600d307SMarcel Moolenaar } 96731337658SMarcel Moolenaar 96831337658SMarcel Moolenaar /* 96931337658SMarcel Moolenaar * After we call vsnprintf(), the stage of vap is not defined. 97031337658SMarcel Moolenaar * We need to copy it before we pass. Then we have to do our 97131337658SMarcel Moolenaar * own logic below to move it along. This is because the 972788ca347SMarcel Moolenaar * implementation can have va_list be a pointer (bsd) or a 97331337658SMarcel Moolenaar * structure (macosx) or anything in between. 97431337658SMarcel Moolenaar */ 97531337658SMarcel Moolenaar 97631337658SMarcel Moolenaar va_end(va_local); /* Reset vap to the start */ 97731337658SMarcel Moolenaar va_copy(va_local, vap); 97831337658SMarcel Moolenaar 97931337658SMarcel Moolenaar left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp); 98031337658SMarcel Moolenaar if (xop->xo_formatter) 981788ca347SMarcel Moolenaar rc = xop->xo_formatter(xop, xbp->xb_curp, left, fmt, va_local); 98231337658SMarcel Moolenaar else 98331337658SMarcel Moolenaar rc = vsnprintf(xbp->xb_curp, left, fmt, va_local); 98431337658SMarcel Moolenaar } 98531337658SMarcel Moolenaar va_end(va_local); 98631337658SMarcel Moolenaar 98731337658SMarcel Moolenaar return rc; 98831337658SMarcel Moolenaar } 98931337658SMarcel Moolenaar 99031337658SMarcel Moolenaar /* 991ee5cf116SPhil Shafer * Print some data through the handle. 99231337658SMarcel Moolenaar */ 9938a6eceffSPhil Shafer static ssize_t 99431337658SMarcel Moolenaar xo_printf_v (xo_handle_t *xop, const char *fmt, va_list vap) 99531337658SMarcel Moolenaar { 99631337658SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_data; 9978a6eceffSPhil Shafer ssize_t left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp); 9988a6eceffSPhil Shafer ssize_t rc; 99931337658SMarcel Moolenaar va_list va_local; 100031337658SMarcel Moolenaar 100131337658SMarcel Moolenaar va_copy(va_local, vap); 100231337658SMarcel Moolenaar 100331337658SMarcel Moolenaar rc = vsnprintf(xbp->xb_curp, left, fmt, va_local); 100431337658SMarcel Moolenaar 1005d1a0d267SMarcel Moolenaar if (rc >= left) { 1006c600d307SMarcel Moolenaar if (!xo_buf_has_room(xbp, rc)) { 1007c600d307SMarcel Moolenaar va_end(va_local); 100831337658SMarcel Moolenaar return -1; 1009c600d307SMarcel Moolenaar } 101031337658SMarcel Moolenaar 101131337658SMarcel Moolenaar va_end(va_local); /* Reset vap to the start */ 101231337658SMarcel Moolenaar va_copy(va_local, vap); 101331337658SMarcel Moolenaar 101431337658SMarcel Moolenaar left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp); 101531337658SMarcel Moolenaar rc = vsnprintf(xbp->xb_curp, left, fmt, va_local); 101631337658SMarcel Moolenaar } 101731337658SMarcel Moolenaar 101831337658SMarcel Moolenaar va_end(va_local); 101931337658SMarcel Moolenaar 102031337658SMarcel Moolenaar if (rc > 0) 102131337658SMarcel Moolenaar xbp->xb_curp += rc; 102231337658SMarcel Moolenaar 102331337658SMarcel Moolenaar return rc; 102431337658SMarcel Moolenaar } 102531337658SMarcel Moolenaar 10268a6eceffSPhil Shafer static ssize_t 102731337658SMarcel Moolenaar xo_printf (xo_handle_t *xop, const char *fmt, ...) 102831337658SMarcel Moolenaar { 10298a6eceffSPhil Shafer ssize_t rc; 103031337658SMarcel Moolenaar va_list vap; 103131337658SMarcel Moolenaar 103231337658SMarcel Moolenaar va_start(vap, fmt); 103331337658SMarcel Moolenaar 103431337658SMarcel Moolenaar rc = xo_printf_v(xop, fmt, vap); 103531337658SMarcel Moolenaar 103631337658SMarcel Moolenaar va_end(vap); 103731337658SMarcel Moolenaar return rc; 103831337658SMarcel Moolenaar } 103931337658SMarcel Moolenaar 104031337658SMarcel Moolenaar /* 104131337658SMarcel Moolenaar * These next few function are make The Essential UTF-8 Ginsu Knife. 104231337658SMarcel Moolenaar * Identify an input and output character, and convert it. 104331337658SMarcel Moolenaar */ 10448a6eceffSPhil Shafer static uint8_t xo_utf8_bits[7] = { 0, 0x7f, 0x1f, 0x0f, 0x07, 0x03, 0x01 }; 104531337658SMarcel Moolenaar 104631337658SMarcel Moolenaar static int 104731337658SMarcel Moolenaar xo_is_utf8 (char ch) 104831337658SMarcel Moolenaar { 104931337658SMarcel Moolenaar return (ch & 0x80); 105031337658SMarcel Moolenaar } 105131337658SMarcel Moolenaar 10528a6eceffSPhil Shafer static inline ssize_t 105331337658SMarcel Moolenaar xo_utf8_to_wc_len (const char *buf) 105431337658SMarcel Moolenaar { 105531337658SMarcel Moolenaar unsigned b = (unsigned char) *buf; 10568a6eceffSPhil Shafer ssize_t len; 105731337658SMarcel Moolenaar 105831337658SMarcel Moolenaar if ((b & 0x80) == 0x0) 105931337658SMarcel Moolenaar len = 1; 106031337658SMarcel Moolenaar else if ((b & 0xe0) == 0xc0) 106131337658SMarcel Moolenaar len = 2; 106231337658SMarcel Moolenaar else if ((b & 0xf0) == 0xe0) 106331337658SMarcel Moolenaar len = 3; 106431337658SMarcel Moolenaar else if ((b & 0xf8) == 0xf0) 106531337658SMarcel Moolenaar len = 4; 106631337658SMarcel Moolenaar else if ((b & 0xfc) == 0xf8) 106731337658SMarcel Moolenaar len = 5; 106831337658SMarcel Moolenaar else if ((b & 0xfe) == 0xfc) 106931337658SMarcel Moolenaar len = 6; 107031337658SMarcel Moolenaar else 107131337658SMarcel Moolenaar len = -1; 107231337658SMarcel Moolenaar 107331337658SMarcel Moolenaar return len; 107431337658SMarcel Moolenaar } 107531337658SMarcel Moolenaar 10768a6eceffSPhil Shafer static ssize_t 10778a6eceffSPhil Shafer xo_buf_utf8_len (xo_handle_t *xop, const char *buf, ssize_t bufsiz) 107831337658SMarcel Moolenaar { 107931337658SMarcel Moolenaar 108031337658SMarcel Moolenaar unsigned b = (unsigned char) *buf; 10818a6eceffSPhil Shafer ssize_t len, i; 108231337658SMarcel Moolenaar 108331337658SMarcel Moolenaar len = xo_utf8_to_wc_len(buf); 108431337658SMarcel Moolenaar if (len == -1) { 108531337658SMarcel Moolenaar xo_failure(xop, "invalid UTF-8 data: %02hhx", b); 108631337658SMarcel Moolenaar return -1; 108731337658SMarcel Moolenaar } 108831337658SMarcel Moolenaar 108931337658SMarcel Moolenaar if (len > bufsiz) { 109031337658SMarcel Moolenaar xo_failure(xop, "invalid UTF-8 data (short): %02hhx (%d/%d)", 109131337658SMarcel Moolenaar b, len, bufsiz); 109231337658SMarcel Moolenaar return -1; 109331337658SMarcel Moolenaar } 109431337658SMarcel Moolenaar 109531337658SMarcel Moolenaar for (i = 2; i < len; i++) { 109631337658SMarcel Moolenaar b = (unsigned char ) buf[i]; 109731337658SMarcel Moolenaar if ((b & 0xc0) != 0x80) { 109831337658SMarcel Moolenaar xo_failure(xop, "invalid UTF-8 data (byte %d): %x", i, b); 109931337658SMarcel Moolenaar return -1; 110031337658SMarcel Moolenaar } 110131337658SMarcel Moolenaar } 110231337658SMarcel Moolenaar 110331337658SMarcel Moolenaar return len; 110431337658SMarcel Moolenaar } 110531337658SMarcel Moolenaar 110631337658SMarcel Moolenaar /* 110731337658SMarcel Moolenaar * Build a wide character from the input buffer; the number of 110831337658SMarcel Moolenaar * bits we pull off the first character is dependent on the length, 110931337658SMarcel Moolenaar * but we put 6 bits off all other bytes. 111031337658SMarcel Moolenaar */ 111142ff34c3SPhil Shafer static inline wchar_t 11128a6eceffSPhil Shafer xo_utf8_char (const char *buf, ssize_t len) 111331337658SMarcel Moolenaar { 111442ff34c3SPhil Shafer /* Most common case: singleton byte */ 111542ff34c3SPhil Shafer if (len == 1) 111642ff34c3SPhil Shafer return (unsigned char) buf[0]; 111742ff34c3SPhil Shafer 11188a6eceffSPhil Shafer ssize_t i; 111931337658SMarcel Moolenaar wchar_t wc; 112031337658SMarcel Moolenaar const unsigned char *cp = (const unsigned char *) buf; 112131337658SMarcel Moolenaar 112231337658SMarcel Moolenaar wc = *cp & xo_utf8_bits[len]; 112331337658SMarcel Moolenaar for (i = 1; i < len; i++) { 112431337658SMarcel Moolenaar wc <<= 6; 112531337658SMarcel Moolenaar wc |= cp[i] & 0x3f; 112631337658SMarcel Moolenaar if ((cp[i] & 0xc0) != 0x80) 112731337658SMarcel Moolenaar return (wchar_t) -1; 112831337658SMarcel Moolenaar } 112931337658SMarcel Moolenaar 113031337658SMarcel Moolenaar return wc; 113131337658SMarcel Moolenaar } 113231337658SMarcel Moolenaar 113331337658SMarcel Moolenaar /* 113431337658SMarcel Moolenaar * Determine the number of bytes needed to encode a wide character. 113531337658SMarcel Moolenaar */ 11368a6eceffSPhil Shafer static ssize_t 113731337658SMarcel Moolenaar xo_utf8_emit_len (wchar_t wc) 113831337658SMarcel Moolenaar { 11398a6eceffSPhil Shafer ssize_t len; 114031337658SMarcel Moolenaar 114131337658SMarcel Moolenaar if ((wc & ((1<<7) - 1)) == wc) /* Simple case */ 114231337658SMarcel Moolenaar len = 1; 114331337658SMarcel Moolenaar else if ((wc & ((1<<11) - 1)) == wc) 114431337658SMarcel Moolenaar len = 2; 114531337658SMarcel Moolenaar else if ((wc & ((1<<16) - 1)) == wc) 114631337658SMarcel Moolenaar len = 3; 114731337658SMarcel Moolenaar else if ((wc & ((1<<21) - 1)) == wc) 114831337658SMarcel Moolenaar len = 4; 114931337658SMarcel Moolenaar else if ((wc & ((1<<26) - 1)) == wc) 115031337658SMarcel Moolenaar len = 5; 115131337658SMarcel Moolenaar else 115231337658SMarcel Moolenaar len = 6; 115331337658SMarcel Moolenaar 115431337658SMarcel Moolenaar return len; 115531337658SMarcel Moolenaar } 115631337658SMarcel Moolenaar 115731337658SMarcel Moolenaar static void 11588a6eceffSPhil Shafer xo_utf8_emit_char (char *buf, ssize_t len, wchar_t wc) 115931337658SMarcel Moolenaar { 11608a6eceffSPhil Shafer ssize_t i; 116131337658SMarcel Moolenaar 116231337658SMarcel Moolenaar if (len == 1) { /* Simple case */ 116331337658SMarcel Moolenaar buf[0] = wc & 0x7f; 116431337658SMarcel Moolenaar return; 116531337658SMarcel Moolenaar } 116631337658SMarcel Moolenaar 116731337658SMarcel Moolenaar for (i = len - 1; i >= 0; i--) { 116831337658SMarcel Moolenaar buf[i] = 0x80 | (wc & 0x3f); 116931337658SMarcel Moolenaar wc >>= 6; 117031337658SMarcel Moolenaar } 117131337658SMarcel Moolenaar 117231337658SMarcel Moolenaar buf[0] &= xo_utf8_bits[len]; 117331337658SMarcel Moolenaar buf[0] |= ~xo_utf8_bits[len] << 1; 117431337658SMarcel Moolenaar } 117531337658SMarcel Moolenaar 11768a6eceffSPhil Shafer static ssize_t 117731337658SMarcel Moolenaar xo_buf_append_locale_from_utf8 (xo_handle_t *xop, xo_buffer_t *xbp, 11788a6eceffSPhil Shafer const char *ibuf, ssize_t ilen) 117931337658SMarcel Moolenaar { 118031337658SMarcel Moolenaar wchar_t wc; 11818a6eceffSPhil Shafer ssize_t len; 118231337658SMarcel Moolenaar 118331337658SMarcel Moolenaar /* 118431337658SMarcel Moolenaar * Build our wide character from the input buffer; the number of 118531337658SMarcel Moolenaar * bits we pull off the first character is dependent on the length, 118631337658SMarcel Moolenaar * but we put 6 bits off all other bytes. 118731337658SMarcel Moolenaar */ 118831337658SMarcel Moolenaar wc = xo_utf8_char(ibuf, ilen); 118931337658SMarcel Moolenaar if (wc == (wchar_t) -1) { 119031337658SMarcel Moolenaar xo_failure(xop, "invalid utf-8 byte sequence"); 119131337658SMarcel Moolenaar return 0; 119231337658SMarcel Moolenaar } 119331337658SMarcel Moolenaar 1194d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_NO_LOCALE)) { 119531337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, ilen)) 119631337658SMarcel Moolenaar return 0; 119731337658SMarcel Moolenaar 119831337658SMarcel Moolenaar memcpy(xbp->xb_curp, ibuf, ilen); 119931337658SMarcel Moolenaar xbp->xb_curp += ilen; 120031337658SMarcel Moolenaar 120131337658SMarcel Moolenaar } else { 120231337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, MB_LEN_MAX + 1)) 120331337658SMarcel Moolenaar return 0; 120431337658SMarcel Moolenaar 120531337658SMarcel Moolenaar bzero(&xop->xo_mbstate, sizeof(xop->xo_mbstate)); 120631337658SMarcel Moolenaar len = wcrtomb(xbp->xb_curp, wc, &xop->xo_mbstate); 120731337658SMarcel Moolenaar 120831337658SMarcel Moolenaar if (len <= 0) { 120931337658SMarcel Moolenaar xo_failure(xop, "could not convert wide char: %lx", 121031337658SMarcel Moolenaar (unsigned long) wc); 121131337658SMarcel Moolenaar return 0; 121231337658SMarcel Moolenaar } 121331337658SMarcel Moolenaar xbp->xb_curp += len; 121431337658SMarcel Moolenaar } 121531337658SMarcel Moolenaar 1216d1a0d267SMarcel Moolenaar return xo_wcwidth(wc); 121731337658SMarcel Moolenaar } 121831337658SMarcel Moolenaar 121931337658SMarcel Moolenaar static void 122031337658SMarcel Moolenaar xo_buf_append_locale (xo_handle_t *xop, xo_buffer_t *xbp, 12218a6eceffSPhil Shafer const char *cp, ssize_t len) 122231337658SMarcel Moolenaar { 122331337658SMarcel Moolenaar const char *sp = cp, *ep = cp + len; 12248a6eceffSPhil Shafer ssize_t save_off = xbp->xb_bufp - xbp->xb_curp; 12258a6eceffSPhil Shafer ssize_t slen; 122631337658SMarcel Moolenaar int cols = 0; 122731337658SMarcel Moolenaar 122831337658SMarcel Moolenaar for ( ; cp < ep; cp++) { 122931337658SMarcel Moolenaar if (!xo_is_utf8(*cp)) { 123031337658SMarcel Moolenaar cols += 1; 123131337658SMarcel Moolenaar continue; 123231337658SMarcel Moolenaar } 123331337658SMarcel Moolenaar 123431337658SMarcel Moolenaar /* 123531337658SMarcel Moolenaar * We're looking at a non-ascii UTF-8 character. 123631337658SMarcel Moolenaar * First we copy the previous data. 123731337658SMarcel Moolenaar * Then we need find the length and validate it. 123831337658SMarcel Moolenaar * Then we turn it into a wide string. 123931337658SMarcel Moolenaar * Then we turn it into a localized string. 124031337658SMarcel Moolenaar * Then we repeat. Isn't i18n fun? 124131337658SMarcel Moolenaar */ 124231337658SMarcel Moolenaar if (sp != cp) 124331337658SMarcel Moolenaar xo_buf_append(xbp, sp, cp - sp); /* Append previous data */ 124431337658SMarcel Moolenaar 124531337658SMarcel Moolenaar slen = xo_buf_utf8_len(xop, cp, ep - cp); 124631337658SMarcel Moolenaar if (slen <= 0) { 124731337658SMarcel Moolenaar /* Bad data; back it all out */ 124831337658SMarcel Moolenaar xbp->xb_curp = xbp->xb_bufp + save_off; 124931337658SMarcel Moolenaar return; 125031337658SMarcel Moolenaar } 125131337658SMarcel Moolenaar 125231337658SMarcel Moolenaar cols += xo_buf_append_locale_from_utf8(xop, xbp, cp, slen); 125331337658SMarcel Moolenaar 1254ee5cf116SPhil Shafer /* Next time through, we'll start at the next character */ 125531337658SMarcel Moolenaar cp += slen - 1; 125631337658SMarcel Moolenaar sp = cp + 1; 125731337658SMarcel Moolenaar } 125831337658SMarcel Moolenaar 125931337658SMarcel Moolenaar /* Update column values */ 1260d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_COLUMNS)) 126131337658SMarcel Moolenaar xop->xo_columns += cols; 1262d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_ANCHOR)) 126331337658SMarcel Moolenaar xop->xo_anchor_columns += cols; 126431337658SMarcel Moolenaar 126531337658SMarcel Moolenaar /* Before we fall into the basic logic below, we need reset len */ 126631337658SMarcel Moolenaar len = ep - sp; 126731337658SMarcel Moolenaar if (len != 0) /* Append trailing data */ 126831337658SMarcel Moolenaar xo_buf_append(xbp, sp, len); 126931337658SMarcel Moolenaar } 127031337658SMarcel Moolenaar 127131337658SMarcel Moolenaar /* 1272d1a0d267SMarcel Moolenaar * Append the given string to the given buffer, without escaping or 1273d1a0d267SMarcel Moolenaar * character set conversion. This is the straight copy to the data 1274d1a0d267SMarcel Moolenaar * buffer with no fanciness. 127531337658SMarcel Moolenaar */ 127631337658SMarcel Moolenaar static void 12778a6eceffSPhil Shafer xo_data_append (xo_handle_t *xop, const char *str, ssize_t len) 127831337658SMarcel Moolenaar { 127931337658SMarcel Moolenaar xo_buf_append(&xop->xo_data, str, len); 128031337658SMarcel Moolenaar } 128131337658SMarcel Moolenaar 128231337658SMarcel Moolenaar /* 128331337658SMarcel Moolenaar * Append the given string to the given buffer 128431337658SMarcel Moolenaar */ 128531337658SMarcel Moolenaar static void 12868a6eceffSPhil Shafer xo_data_escape (xo_handle_t *xop, const char *str, ssize_t len) 128731337658SMarcel Moolenaar { 128831337658SMarcel Moolenaar xo_buf_escape(xop, &xop->xo_data, str, len, 0); 128931337658SMarcel Moolenaar } 129031337658SMarcel Moolenaar 129142ff34c3SPhil Shafer #ifdef LIBXO_NO_RETAIN 129242ff34c3SPhil Shafer /* 129342ff34c3SPhil Shafer * Empty implementations of the retain logic 129442ff34c3SPhil Shafer */ 129542ff34c3SPhil Shafer 129642ff34c3SPhil Shafer void 129742ff34c3SPhil Shafer xo_retain_clear_all (void) 129842ff34c3SPhil Shafer { 129942ff34c3SPhil Shafer return; 130042ff34c3SPhil Shafer } 130142ff34c3SPhil Shafer 130242ff34c3SPhil Shafer void 130342ff34c3SPhil Shafer xo_retain_clear (const char *fmt UNUSED) 130442ff34c3SPhil Shafer { 130542ff34c3SPhil Shafer return; 130642ff34c3SPhil Shafer } 130742ff34c3SPhil Shafer static void 130842ff34c3SPhil Shafer xo_retain_add (const char *fmt UNUSED, xo_field_info_t *fields UNUSED, 130942ff34c3SPhil Shafer unsigned num_fields UNUSED) 131042ff34c3SPhil Shafer { 131142ff34c3SPhil Shafer return; 131242ff34c3SPhil Shafer } 131342ff34c3SPhil Shafer 131442ff34c3SPhil Shafer static int 131542ff34c3SPhil Shafer xo_retain_find (const char *fmt UNUSED, xo_field_info_t **valp UNUSED, 131642ff34c3SPhil Shafer unsigned *nump UNUSED) 131742ff34c3SPhil Shafer { 131842ff34c3SPhil Shafer return -1; 131942ff34c3SPhil Shafer } 132042ff34c3SPhil Shafer 132142ff34c3SPhil Shafer #else /* !LIBXO_NO_RETAIN */ 132242ff34c3SPhil Shafer /* 132342ff34c3SPhil Shafer * Retain: We retain parsed field definitions to enhance performance, 132442ff34c3SPhil Shafer * especially inside loops. We depend on the caller treating the format 132542ff34c3SPhil Shafer * strings as immutable, so that we can retain pointers into them. We 132642ff34c3SPhil Shafer * hold the pointers in a hash table, so allow quick access. Retained 132742ff34c3SPhil Shafer * information is retained until xo_retain_clear is called. 132842ff34c3SPhil Shafer */ 132942ff34c3SPhil Shafer 133042ff34c3SPhil Shafer /* 133142ff34c3SPhil Shafer * xo_retain_entry_t holds information about one retained set of 133242ff34c3SPhil Shafer * parsed fields. 133342ff34c3SPhil Shafer */ 133442ff34c3SPhil Shafer typedef struct xo_retain_entry_s { 133542ff34c3SPhil Shafer struct xo_retain_entry_s *xre_next; /* Pointer to next (older) entry */ 133642ff34c3SPhil Shafer unsigned long xre_hits; /* Number of times we've hit */ 133742ff34c3SPhil Shafer const char *xre_format; /* Pointer to format string */ 133842ff34c3SPhil Shafer unsigned xre_num_fields; /* Number of fields saved */ 133942ff34c3SPhil Shafer xo_field_info_t *xre_fields; /* Pointer to fields */ 134042ff34c3SPhil Shafer } xo_retain_entry_t; 134142ff34c3SPhil Shafer 134242ff34c3SPhil Shafer /* 134342ff34c3SPhil Shafer * xo_retain_t holds a complete set of parsed fields as a hash table. 134442ff34c3SPhil Shafer */ 134542ff34c3SPhil Shafer #ifndef XO_RETAIN_SIZE 134642ff34c3SPhil Shafer #define XO_RETAIN_SIZE 6 134742ff34c3SPhil Shafer #endif /* XO_RETAIN_SIZE */ 134842ff34c3SPhil Shafer #define RETAIN_HASH_SIZE (1<<XO_RETAIN_SIZE) 134942ff34c3SPhil Shafer 135042ff34c3SPhil Shafer typedef struct xo_retain_s { 135142ff34c3SPhil Shafer xo_retain_entry_t *xr_bucket[RETAIN_HASH_SIZE]; 135242ff34c3SPhil Shafer } xo_retain_t; 135342ff34c3SPhil Shafer 135442ff34c3SPhil Shafer static THREAD_LOCAL(xo_retain_t) xo_retain; 135542ff34c3SPhil Shafer static THREAD_LOCAL(unsigned) xo_retain_count; 135642ff34c3SPhil Shafer 135742ff34c3SPhil Shafer /* 135842ff34c3SPhil Shafer * Simple hash function based on Thomas Wang's paper. The original is 135942ff34c3SPhil Shafer * gone, but an archive is available on the Way Back Machine: 136042ff34c3SPhil Shafer * 136142ff34c3SPhil Shafer * http://web.archive.org/web/20071223173210/\ 136242ff34c3SPhil Shafer * http://www.concentric.net/~Ttwang/tech/inthash.htm 136342ff34c3SPhil Shafer * 136442ff34c3SPhil Shafer * For our purposes, we can assume the low four bits are uninteresting 136542ff34c3SPhil Shafer * since any string less that 16 bytes wouldn't be worthy of 136642ff34c3SPhil Shafer * retaining. We toss the high bits also, since these bits are likely 136742ff34c3SPhil Shafer * to be common among constant format strings. We then run Wang's 136842ff34c3SPhil Shafer * algorithm, and cap the result at RETAIN_HASH_SIZE. 136942ff34c3SPhil Shafer */ 137042ff34c3SPhil Shafer static unsigned 137142ff34c3SPhil Shafer xo_retain_hash (const char *fmt) 137242ff34c3SPhil Shafer { 137342ff34c3SPhil Shafer volatile uintptr_t iptr = (uintptr_t) (const void *) fmt; 137442ff34c3SPhil Shafer 137542ff34c3SPhil Shafer /* Discard low four bits and high bits; they aren't interesting */ 137642ff34c3SPhil Shafer uint32_t val = (uint32_t) ((iptr >> 4) & (((1 << 24) - 1))); 137742ff34c3SPhil Shafer 137842ff34c3SPhil Shafer val = (val ^ 61) ^ (val >> 16); 137942ff34c3SPhil Shafer val = val + (val << 3); 138042ff34c3SPhil Shafer val = val ^ (val >> 4); 138142ff34c3SPhil Shafer val = val * 0x3a8f05c5; /* My large prime number */ 138242ff34c3SPhil Shafer val = val ^ (val >> 15); 138342ff34c3SPhil Shafer val &= RETAIN_HASH_SIZE - 1; 138442ff34c3SPhil Shafer 138542ff34c3SPhil Shafer return val; 138642ff34c3SPhil Shafer } 138742ff34c3SPhil Shafer 138842ff34c3SPhil Shafer /* 138942ff34c3SPhil Shafer * Walk all buckets, clearing all retained entries 139042ff34c3SPhil Shafer */ 139142ff34c3SPhil Shafer void 139242ff34c3SPhil Shafer xo_retain_clear_all (void) 139342ff34c3SPhil Shafer { 139442ff34c3SPhil Shafer int i; 139542ff34c3SPhil Shafer xo_retain_entry_t *xrep, *next; 139642ff34c3SPhil Shafer 139742ff34c3SPhil Shafer for (i = 0; i < RETAIN_HASH_SIZE; i++) { 139842ff34c3SPhil Shafer for (xrep = xo_retain.xr_bucket[i]; xrep; xrep = next) { 139942ff34c3SPhil Shafer next = xrep->xre_next; 140042ff34c3SPhil Shafer xo_free(xrep); 140142ff34c3SPhil Shafer } 140242ff34c3SPhil Shafer xo_retain.xr_bucket[i] = NULL; 140342ff34c3SPhil Shafer } 140442ff34c3SPhil Shafer xo_retain_count = 0; 140542ff34c3SPhil Shafer } 140642ff34c3SPhil Shafer 140742ff34c3SPhil Shafer /* 140842ff34c3SPhil Shafer * Walk all buckets, clearing all retained entries 140942ff34c3SPhil Shafer */ 141042ff34c3SPhil Shafer void 141142ff34c3SPhil Shafer xo_retain_clear (const char *fmt) 141242ff34c3SPhil Shafer { 141342ff34c3SPhil Shafer xo_retain_entry_t **xrepp; 141442ff34c3SPhil Shafer unsigned hash = xo_retain_hash(fmt); 141542ff34c3SPhil Shafer 141642ff34c3SPhil Shafer for (xrepp = &xo_retain.xr_bucket[hash]; *xrepp; 141742ff34c3SPhil Shafer xrepp = &(*xrepp)->xre_next) { 141842ff34c3SPhil Shafer if ((*xrepp)->xre_format == fmt) { 141942ff34c3SPhil Shafer *xrepp = (*xrepp)->xre_next; 142042ff34c3SPhil Shafer xo_retain_count -= 1; 142142ff34c3SPhil Shafer return; 142242ff34c3SPhil Shafer } 142342ff34c3SPhil Shafer } 142442ff34c3SPhil Shafer } 142542ff34c3SPhil Shafer 142642ff34c3SPhil Shafer /* 142742ff34c3SPhil Shafer * Search the hash for an entry matching 'fmt'; return it's fields. 142842ff34c3SPhil Shafer */ 142942ff34c3SPhil Shafer static int 143042ff34c3SPhil Shafer xo_retain_find (const char *fmt, xo_field_info_t **valp, unsigned *nump) 143142ff34c3SPhil Shafer { 143242ff34c3SPhil Shafer if (xo_retain_count == 0) 143342ff34c3SPhil Shafer return -1; 143442ff34c3SPhil Shafer 143542ff34c3SPhil Shafer unsigned hash = xo_retain_hash(fmt); 143642ff34c3SPhil Shafer xo_retain_entry_t *xrep; 143742ff34c3SPhil Shafer 143842ff34c3SPhil Shafer for (xrep = xo_retain.xr_bucket[hash]; xrep != NULL; 143942ff34c3SPhil Shafer xrep = xrep->xre_next) { 144042ff34c3SPhil Shafer if (xrep->xre_format == fmt) { 144142ff34c3SPhil Shafer *valp = xrep->xre_fields; 144242ff34c3SPhil Shafer *nump = xrep->xre_num_fields; 144342ff34c3SPhil Shafer xrep->xre_hits += 1; 144442ff34c3SPhil Shafer return 0; 144542ff34c3SPhil Shafer } 144642ff34c3SPhil Shafer } 144742ff34c3SPhil Shafer 144842ff34c3SPhil Shafer return -1; 144942ff34c3SPhil Shafer } 145042ff34c3SPhil Shafer 145142ff34c3SPhil Shafer static void 145242ff34c3SPhil Shafer xo_retain_add (const char *fmt, xo_field_info_t *fields, unsigned num_fields) 145342ff34c3SPhil Shafer { 145442ff34c3SPhil Shafer unsigned hash = xo_retain_hash(fmt); 145542ff34c3SPhil Shafer xo_retain_entry_t *xrep; 14568a6eceffSPhil Shafer ssize_t sz = sizeof(*xrep) + (num_fields + 1) * sizeof(*fields); 145742ff34c3SPhil Shafer xo_field_info_t *xfip; 145842ff34c3SPhil Shafer 145942ff34c3SPhil Shafer xrep = xo_realloc(NULL, sz); 146042ff34c3SPhil Shafer if (xrep == NULL) 146142ff34c3SPhil Shafer return; 146242ff34c3SPhil Shafer 146342ff34c3SPhil Shafer xfip = (xo_field_info_t *) &xrep[1]; 146442ff34c3SPhil Shafer memcpy(xfip, fields, num_fields * sizeof(*fields)); 146542ff34c3SPhil Shafer 146642ff34c3SPhil Shafer bzero(xrep, sizeof(*xrep)); 146742ff34c3SPhil Shafer 146842ff34c3SPhil Shafer xrep->xre_format = fmt; 146942ff34c3SPhil Shafer xrep->xre_fields = xfip; 147042ff34c3SPhil Shafer xrep->xre_num_fields = num_fields; 147142ff34c3SPhil Shafer 147242ff34c3SPhil Shafer /* Record the field info in the retain bucket */ 147342ff34c3SPhil Shafer xrep->xre_next = xo_retain.xr_bucket[hash]; 147442ff34c3SPhil Shafer xo_retain.xr_bucket[hash] = xrep; 147542ff34c3SPhil Shafer xo_retain_count += 1; 147642ff34c3SPhil Shafer } 147742ff34c3SPhil Shafer 147842ff34c3SPhil Shafer #endif /* !LIBXO_NO_RETAIN */ 147942ff34c3SPhil Shafer 148031337658SMarcel Moolenaar /* 148131337658SMarcel Moolenaar * Generate a warning. Normally, this is a text message written to 148231337658SMarcel Moolenaar * standard error. If the XOF_WARN_XML flag is set, then we generate 148331337658SMarcel Moolenaar * XMLified content on standard output. 148431337658SMarcel Moolenaar */ 148531337658SMarcel Moolenaar static void 148631337658SMarcel Moolenaar xo_warn_hcv (xo_handle_t *xop, int code, int check_warn, 148731337658SMarcel Moolenaar const char *fmt, va_list vap) 148831337658SMarcel Moolenaar { 148931337658SMarcel Moolenaar xop = xo_default(xop); 1490d1a0d267SMarcel Moolenaar if (check_warn && !XOF_ISSET(xop, XOF_WARN)) 149131337658SMarcel Moolenaar return; 149231337658SMarcel Moolenaar 149331337658SMarcel Moolenaar if (fmt == NULL) 149431337658SMarcel Moolenaar return; 149531337658SMarcel Moolenaar 14968a6eceffSPhil Shafer ssize_t len = strlen(fmt); 14978a6eceffSPhil Shafer ssize_t plen = xo_program ? strlen(xo_program) : 0; 1498545ddfbeSMarcel Moolenaar char *newfmt = alloca(len + 1 + plen + 2); /* NUL, and ": " */ 149931337658SMarcel Moolenaar 150031337658SMarcel Moolenaar if (plen) { 150131337658SMarcel Moolenaar memcpy(newfmt, xo_program, plen); 150231337658SMarcel Moolenaar newfmt[plen++] = ':'; 150331337658SMarcel Moolenaar newfmt[plen++] = ' '; 150431337658SMarcel Moolenaar } 150531337658SMarcel Moolenaar memcpy(newfmt + plen, fmt, len); 150631337658SMarcel Moolenaar newfmt[len + plen] = '\0'; 150731337658SMarcel Moolenaar 1508d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_WARN_XML)) { 150931337658SMarcel Moolenaar static char err_open[] = "<error>"; 151031337658SMarcel Moolenaar static char err_close[] = "</error>"; 151131337658SMarcel Moolenaar static char msg_open[] = "<message>"; 151231337658SMarcel Moolenaar static char msg_close[] = "</message>"; 151331337658SMarcel Moolenaar 151431337658SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_data; 151531337658SMarcel Moolenaar 151631337658SMarcel Moolenaar xo_buf_append(xbp, err_open, sizeof(err_open) - 1); 151731337658SMarcel Moolenaar xo_buf_append(xbp, msg_open, sizeof(msg_open) - 1); 151831337658SMarcel Moolenaar 151931337658SMarcel Moolenaar va_list va_local; 152031337658SMarcel Moolenaar va_copy(va_local, vap); 152131337658SMarcel Moolenaar 15228a6eceffSPhil Shafer ssize_t left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp); 15238a6eceffSPhil Shafer ssize_t rc = vsnprintf(xbp->xb_curp, left, newfmt, vap); 1524d1a0d267SMarcel Moolenaar if (rc >= left) { 1525c600d307SMarcel Moolenaar if (!xo_buf_has_room(xbp, rc)) { 1526c600d307SMarcel Moolenaar va_end(va_local); 152731337658SMarcel Moolenaar return; 1528c600d307SMarcel Moolenaar } 152931337658SMarcel Moolenaar 153031337658SMarcel Moolenaar va_end(vap); /* Reset vap to the start */ 153131337658SMarcel Moolenaar va_copy(vap, va_local); 153231337658SMarcel Moolenaar 153331337658SMarcel Moolenaar left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp); 153431337658SMarcel Moolenaar rc = vsnprintf(xbp->xb_curp, left, fmt, vap); 153531337658SMarcel Moolenaar } 153631337658SMarcel Moolenaar va_end(va_local); 153731337658SMarcel Moolenaar 153831337658SMarcel Moolenaar rc = xo_escape_xml(xbp, rc, 1); 153931337658SMarcel Moolenaar xbp->xb_curp += rc; 154031337658SMarcel Moolenaar 154131337658SMarcel Moolenaar xo_buf_append(xbp, msg_close, sizeof(msg_close) - 1); 154231337658SMarcel Moolenaar xo_buf_append(xbp, err_close, sizeof(err_close) - 1); 154331337658SMarcel Moolenaar 1544545ddfbeSMarcel Moolenaar if (code >= 0) { 154531337658SMarcel Moolenaar const char *msg = strerror(code); 154631337658SMarcel Moolenaar if (msg) { 154731337658SMarcel Moolenaar xo_buf_append(xbp, ": ", 2); 154831337658SMarcel Moolenaar xo_buf_append(xbp, msg, strlen(msg)); 154931337658SMarcel Moolenaar } 155031337658SMarcel Moolenaar } 155131337658SMarcel Moolenaar 1552d1a0d267SMarcel Moolenaar xo_buf_append(xbp, "\n", 1); /* Append newline and NUL to string */ 1553545ddfbeSMarcel Moolenaar (void) xo_write(xop); 155431337658SMarcel Moolenaar 155531337658SMarcel Moolenaar } else { 155631337658SMarcel Moolenaar vfprintf(stderr, newfmt, vap); 1557545ddfbeSMarcel Moolenaar if (code >= 0) { 1558545ddfbeSMarcel Moolenaar const char *msg = strerror(code); 1559545ddfbeSMarcel Moolenaar if (msg) 1560545ddfbeSMarcel Moolenaar fprintf(stderr, ": %s", msg); 1561545ddfbeSMarcel Moolenaar } 1562545ddfbeSMarcel Moolenaar fprintf(stderr, "\n"); 156331337658SMarcel Moolenaar } 156431337658SMarcel Moolenaar } 156531337658SMarcel Moolenaar 156631337658SMarcel Moolenaar void 156731337658SMarcel Moolenaar xo_warn_hc (xo_handle_t *xop, int code, const char *fmt, ...) 156831337658SMarcel Moolenaar { 156931337658SMarcel Moolenaar va_list vap; 157031337658SMarcel Moolenaar 157131337658SMarcel Moolenaar va_start(vap, fmt); 157231337658SMarcel Moolenaar xo_warn_hcv(xop, code, 0, fmt, vap); 157331337658SMarcel Moolenaar va_end(vap); 157431337658SMarcel Moolenaar } 157531337658SMarcel Moolenaar 157631337658SMarcel Moolenaar void 157731337658SMarcel Moolenaar xo_warn_c (int code, const char *fmt, ...) 157831337658SMarcel Moolenaar { 157931337658SMarcel Moolenaar va_list vap; 158031337658SMarcel Moolenaar 158131337658SMarcel Moolenaar va_start(vap, fmt); 1582545ddfbeSMarcel Moolenaar xo_warn_hcv(NULL, code, 0, fmt, vap); 158331337658SMarcel Moolenaar va_end(vap); 158431337658SMarcel Moolenaar } 158531337658SMarcel Moolenaar 158631337658SMarcel Moolenaar void 158731337658SMarcel Moolenaar xo_warn (const char *fmt, ...) 158831337658SMarcel Moolenaar { 158931337658SMarcel Moolenaar int code = errno; 159031337658SMarcel Moolenaar va_list vap; 159131337658SMarcel Moolenaar 159231337658SMarcel Moolenaar va_start(vap, fmt); 159331337658SMarcel Moolenaar xo_warn_hcv(NULL, code, 0, fmt, vap); 159431337658SMarcel Moolenaar va_end(vap); 159531337658SMarcel Moolenaar } 159631337658SMarcel Moolenaar 159731337658SMarcel Moolenaar void 159831337658SMarcel Moolenaar xo_warnx (const char *fmt, ...) 159931337658SMarcel Moolenaar { 160031337658SMarcel Moolenaar va_list vap; 160131337658SMarcel Moolenaar 160231337658SMarcel Moolenaar va_start(vap, fmt); 160331337658SMarcel Moolenaar xo_warn_hcv(NULL, -1, 0, fmt, vap); 160431337658SMarcel Moolenaar va_end(vap); 160531337658SMarcel Moolenaar } 160631337658SMarcel Moolenaar 160731337658SMarcel Moolenaar void 160831337658SMarcel Moolenaar xo_err (int eval, const char *fmt, ...) 160931337658SMarcel Moolenaar { 161031337658SMarcel Moolenaar int code = errno; 161131337658SMarcel Moolenaar va_list vap; 161231337658SMarcel Moolenaar 161331337658SMarcel Moolenaar va_start(vap, fmt); 161431337658SMarcel Moolenaar xo_warn_hcv(NULL, code, 0, fmt, vap); 161531337658SMarcel Moolenaar va_end(vap); 161631337658SMarcel Moolenaar xo_finish(); 161731337658SMarcel Moolenaar exit(eval); 161831337658SMarcel Moolenaar } 161931337658SMarcel Moolenaar 162031337658SMarcel Moolenaar void 162131337658SMarcel Moolenaar xo_errx (int eval, const char *fmt, ...) 162231337658SMarcel Moolenaar { 162331337658SMarcel Moolenaar va_list vap; 162431337658SMarcel Moolenaar 162531337658SMarcel Moolenaar va_start(vap, fmt); 162631337658SMarcel Moolenaar xo_warn_hcv(NULL, -1, 0, fmt, vap); 162731337658SMarcel Moolenaar va_end(vap); 162831337658SMarcel Moolenaar xo_finish(); 162931337658SMarcel Moolenaar exit(eval); 163031337658SMarcel Moolenaar } 163131337658SMarcel Moolenaar 163231337658SMarcel Moolenaar void 163331337658SMarcel Moolenaar xo_errc (int eval, int code, const char *fmt, ...) 163431337658SMarcel Moolenaar { 163531337658SMarcel Moolenaar va_list vap; 163631337658SMarcel Moolenaar 163731337658SMarcel Moolenaar va_start(vap, fmt); 163831337658SMarcel Moolenaar xo_warn_hcv(NULL, code, 0, fmt, vap); 163931337658SMarcel Moolenaar va_end(vap); 164031337658SMarcel Moolenaar xo_finish(); 164131337658SMarcel Moolenaar exit(eval); 164231337658SMarcel Moolenaar } 164331337658SMarcel Moolenaar 164431337658SMarcel Moolenaar /* 164531337658SMarcel Moolenaar * Generate a warning. Normally, this is a text message written to 164631337658SMarcel Moolenaar * standard error. If the XOF_WARN_XML flag is set, then we generate 164731337658SMarcel Moolenaar * XMLified content on standard output. 164831337658SMarcel Moolenaar */ 164931337658SMarcel Moolenaar void 165031337658SMarcel Moolenaar xo_message_hcv (xo_handle_t *xop, int code, const char *fmt, va_list vap) 165131337658SMarcel Moolenaar { 165231337658SMarcel Moolenaar static char msg_open[] = "<message>"; 165331337658SMarcel Moolenaar static char msg_close[] = "</message>"; 165431337658SMarcel Moolenaar xo_buffer_t *xbp; 16558a6eceffSPhil Shafer ssize_t rc; 165631337658SMarcel Moolenaar va_list va_local; 165731337658SMarcel Moolenaar 165831337658SMarcel Moolenaar xop = xo_default(xop); 165931337658SMarcel Moolenaar 166031337658SMarcel Moolenaar if (fmt == NULL || *fmt == '\0') 166131337658SMarcel Moolenaar return; 166231337658SMarcel Moolenaar 166331337658SMarcel Moolenaar int need_nl = (fmt[strlen(fmt) - 1] != '\n'); 166431337658SMarcel Moolenaar 1665788ca347SMarcel Moolenaar switch (xo_style(xop)) { 166631337658SMarcel Moolenaar case XO_STYLE_XML: 166731337658SMarcel Moolenaar xbp = &xop->xo_data; 1668d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_PRETTY)) 166931337658SMarcel Moolenaar xo_buf_indent(xop, xop->xo_indent_by); 167031337658SMarcel Moolenaar xo_buf_append(xbp, msg_open, sizeof(msg_open) - 1); 167131337658SMarcel Moolenaar 167231337658SMarcel Moolenaar va_copy(va_local, vap); 167331337658SMarcel Moolenaar 16748a6eceffSPhil Shafer ssize_t left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp); 167531337658SMarcel Moolenaar rc = vsnprintf(xbp->xb_curp, left, fmt, vap); 1676d1a0d267SMarcel Moolenaar if (rc >= left) { 1677c600d307SMarcel Moolenaar if (!xo_buf_has_room(xbp, rc)) { 1678c600d307SMarcel Moolenaar va_end(va_local); 167931337658SMarcel Moolenaar return; 1680c600d307SMarcel Moolenaar } 168131337658SMarcel Moolenaar 168231337658SMarcel Moolenaar va_end(vap); /* Reset vap to the start */ 168331337658SMarcel Moolenaar va_copy(vap, va_local); 168431337658SMarcel Moolenaar 168531337658SMarcel Moolenaar left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp); 168631337658SMarcel Moolenaar rc = vsnprintf(xbp->xb_curp, left, fmt, vap); 168731337658SMarcel Moolenaar } 168831337658SMarcel Moolenaar va_end(va_local); 168931337658SMarcel Moolenaar 1690d1a0d267SMarcel Moolenaar rc = xo_escape_xml(xbp, rc, 0); 169131337658SMarcel Moolenaar xbp->xb_curp += rc; 169231337658SMarcel Moolenaar 169331337658SMarcel Moolenaar if (need_nl && code > 0) { 169431337658SMarcel Moolenaar const char *msg = strerror(code); 169531337658SMarcel Moolenaar if (msg) { 169631337658SMarcel Moolenaar xo_buf_append(xbp, ": ", 2); 169731337658SMarcel Moolenaar xo_buf_append(xbp, msg, strlen(msg)); 169831337658SMarcel Moolenaar } 169931337658SMarcel Moolenaar } 170031337658SMarcel Moolenaar 170131337658SMarcel Moolenaar if (need_nl) 1702d1a0d267SMarcel Moolenaar xo_buf_append(xbp, "\n", 1); /* Append newline and NUL to string */ 1703d1a0d267SMarcel Moolenaar 1704d1a0d267SMarcel Moolenaar xo_buf_append(xbp, msg_close, sizeof(msg_close) - 1); 1705d1a0d267SMarcel Moolenaar 1706d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_PRETTY)) 1707d1a0d267SMarcel Moolenaar xo_buf_append(xbp, "\n", 1); /* Append newline and NUL to string */ 1708d1a0d267SMarcel Moolenaar 1709545ddfbeSMarcel Moolenaar (void) xo_write(xop); 171031337658SMarcel Moolenaar break; 171131337658SMarcel Moolenaar 171231337658SMarcel Moolenaar case XO_STYLE_HTML: 171331337658SMarcel Moolenaar { 171431337658SMarcel Moolenaar char buf[BUFSIZ], *bp = buf, *cp; 17158a6eceffSPhil Shafer ssize_t bufsiz = sizeof(buf); 17168a6eceffSPhil Shafer ssize_t rc2; 171731337658SMarcel Moolenaar 171831337658SMarcel Moolenaar va_copy(va_local, vap); 171931337658SMarcel Moolenaar 1720c600d307SMarcel Moolenaar rc = vsnprintf(bp, bufsiz, fmt, va_local); 172131337658SMarcel Moolenaar if (rc > bufsiz) { 172231337658SMarcel Moolenaar bufsiz = rc + BUFSIZ; 172331337658SMarcel Moolenaar bp = alloca(bufsiz); 172431337658SMarcel Moolenaar va_end(va_local); 172531337658SMarcel Moolenaar va_copy(va_local, vap); 1726c600d307SMarcel Moolenaar rc = vsnprintf(bp, bufsiz, fmt, va_local); 172731337658SMarcel Moolenaar } 1728c600d307SMarcel Moolenaar va_end(va_local); 172931337658SMarcel Moolenaar cp = bp + rc; 173031337658SMarcel Moolenaar 173131337658SMarcel Moolenaar if (need_nl) { 173231337658SMarcel Moolenaar rc2 = snprintf(cp, bufsiz - rc, "%s%s\n", 173331337658SMarcel Moolenaar (code > 0) ? ": " : "", 173431337658SMarcel Moolenaar (code > 0) ? strerror(code) : ""); 173531337658SMarcel Moolenaar if (rc2 > 0) 173631337658SMarcel Moolenaar rc += rc2; 173731337658SMarcel Moolenaar } 173831337658SMarcel Moolenaar 173931337658SMarcel Moolenaar xo_buf_append_div(xop, "message", 0, NULL, 0, bp, rc, NULL, 0); 174031337658SMarcel Moolenaar } 174131337658SMarcel Moolenaar break; 174231337658SMarcel Moolenaar 174331337658SMarcel Moolenaar case XO_STYLE_JSON: 1744d1a0d267SMarcel Moolenaar case XO_STYLE_SDPARAMS: 1745d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER: 1746d1a0d267SMarcel Moolenaar /* No means of representing messages */ 1747d1a0d267SMarcel Moolenaar return; 174831337658SMarcel Moolenaar 174931337658SMarcel Moolenaar case XO_STYLE_TEXT: 175031337658SMarcel Moolenaar rc = xo_printf_v(xop, fmt, vap); 175131337658SMarcel Moolenaar /* 175231337658SMarcel Moolenaar * XXX need to handle UTF-8 widths 175331337658SMarcel Moolenaar */ 175431337658SMarcel Moolenaar if (rc > 0) { 1755d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_COLUMNS)) 175631337658SMarcel Moolenaar xop->xo_columns += rc; 1757d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_ANCHOR)) 175831337658SMarcel Moolenaar xop->xo_anchor_columns += rc; 175931337658SMarcel Moolenaar } 176031337658SMarcel Moolenaar 176131337658SMarcel Moolenaar if (need_nl && code > 0) { 176231337658SMarcel Moolenaar const char *msg = strerror(code); 176331337658SMarcel Moolenaar if (msg) { 176431337658SMarcel Moolenaar xo_printf(xop, ": %s", msg); 176531337658SMarcel Moolenaar } 176631337658SMarcel Moolenaar } 176731337658SMarcel Moolenaar if (need_nl) 176831337658SMarcel Moolenaar xo_printf(xop, "\n"); 176931337658SMarcel Moolenaar 177031337658SMarcel Moolenaar break; 177131337658SMarcel Moolenaar } 177231337658SMarcel Moolenaar 177342ff34c3SPhil Shafer switch (xo_style(xop)) { 177442ff34c3SPhil Shafer case XO_STYLE_HTML: 177542ff34c3SPhil Shafer if (XOIF_ISSET(xop, XOIF_DIV_OPEN)) { 177642ff34c3SPhil Shafer static char div_close[] = "</div>"; 177742ff34c3SPhil Shafer XOIF_CLEAR(xop, XOIF_DIV_OPEN); 177842ff34c3SPhil Shafer xo_data_append(xop, div_close, sizeof(div_close) - 1); 177942ff34c3SPhil Shafer 178042ff34c3SPhil Shafer if (XOF_ISSET(xop, XOF_PRETTY)) 178142ff34c3SPhil Shafer xo_data_append(xop, "\n", 1); 178242ff34c3SPhil Shafer } 178342ff34c3SPhil Shafer break; 178442ff34c3SPhil Shafer } 178542ff34c3SPhil Shafer 1786545ddfbeSMarcel Moolenaar (void) xo_flush_h(xop); 178731337658SMarcel Moolenaar } 178831337658SMarcel Moolenaar 178931337658SMarcel Moolenaar void 179031337658SMarcel Moolenaar xo_message_hc (xo_handle_t *xop, int code, const char *fmt, ...) 179131337658SMarcel Moolenaar { 179231337658SMarcel Moolenaar va_list vap; 179331337658SMarcel Moolenaar 179431337658SMarcel Moolenaar va_start(vap, fmt); 179531337658SMarcel Moolenaar xo_message_hcv(xop, code, fmt, vap); 179631337658SMarcel Moolenaar va_end(vap); 179731337658SMarcel Moolenaar } 179831337658SMarcel Moolenaar 179931337658SMarcel Moolenaar void 180031337658SMarcel Moolenaar xo_message_c (int code, const char *fmt, ...) 180131337658SMarcel Moolenaar { 180231337658SMarcel Moolenaar va_list vap; 180331337658SMarcel Moolenaar 180431337658SMarcel Moolenaar va_start(vap, fmt); 180531337658SMarcel Moolenaar xo_message_hcv(NULL, code, fmt, vap); 180631337658SMarcel Moolenaar va_end(vap); 180731337658SMarcel Moolenaar } 180831337658SMarcel Moolenaar 180931337658SMarcel Moolenaar void 1810d1a0d267SMarcel Moolenaar xo_message_e (const char *fmt, ...) 181131337658SMarcel Moolenaar { 181231337658SMarcel Moolenaar int code = errno; 181331337658SMarcel Moolenaar va_list vap; 181431337658SMarcel Moolenaar 181531337658SMarcel Moolenaar va_start(vap, fmt); 181631337658SMarcel Moolenaar xo_message_hcv(NULL, code, fmt, vap); 181731337658SMarcel Moolenaar va_end(vap); 181831337658SMarcel Moolenaar } 181931337658SMarcel Moolenaar 1820d1a0d267SMarcel Moolenaar void 1821d1a0d267SMarcel Moolenaar xo_message (const char *fmt, ...) 1822d1a0d267SMarcel Moolenaar { 1823d1a0d267SMarcel Moolenaar va_list vap; 1824d1a0d267SMarcel Moolenaar 1825d1a0d267SMarcel Moolenaar va_start(vap, fmt); 1826d1a0d267SMarcel Moolenaar xo_message_hcv(NULL, 0, fmt, vap); 1827d1a0d267SMarcel Moolenaar va_end(vap); 1828d1a0d267SMarcel Moolenaar } 1829d1a0d267SMarcel Moolenaar 183031337658SMarcel Moolenaar static void 183131337658SMarcel Moolenaar xo_failure (xo_handle_t *xop, const char *fmt, ...) 183231337658SMarcel Moolenaar { 1833d1a0d267SMarcel Moolenaar if (!XOF_ISSET(xop, XOF_WARN)) 183431337658SMarcel Moolenaar return; 183531337658SMarcel Moolenaar 183631337658SMarcel Moolenaar va_list vap; 183731337658SMarcel Moolenaar 183831337658SMarcel Moolenaar va_start(vap, fmt); 183931337658SMarcel Moolenaar xo_warn_hcv(xop, -1, 1, fmt, vap); 184031337658SMarcel Moolenaar va_end(vap); 184131337658SMarcel Moolenaar } 184231337658SMarcel Moolenaar 184331337658SMarcel Moolenaar /** 184431337658SMarcel Moolenaar * Create a handle for use by later libxo functions. 184531337658SMarcel Moolenaar * 184631337658SMarcel Moolenaar * Note: normal use of libxo does not require a distinct handle, since 184731337658SMarcel Moolenaar * the default handle (used when NULL is passed) generates text on stdout. 184831337658SMarcel Moolenaar * 184931337658SMarcel Moolenaar * @style Style of output desired (XO_STYLE_* value) 185031337658SMarcel Moolenaar * @flags Set of XOF_* flags in use with this handle 185131337658SMarcel Moolenaar */ 185231337658SMarcel Moolenaar xo_handle_t * 185331337658SMarcel Moolenaar xo_create (xo_style_t style, xo_xof_flags_t flags) 185431337658SMarcel Moolenaar { 185531337658SMarcel Moolenaar xo_handle_t *xop = xo_realloc(NULL, sizeof(*xop)); 185631337658SMarcel Moolenaar 185731337658SMarcel Moolenaar if (xop) { 185831337658SMarcel Moolenaar bzero(xop, sizeof(*xop)); 185931337658SMarcel Moolenaar 186031337658SMarcel Moolenaar xop->xo_style = style; 1861d1a0d267SMarcel Moolenaar XOF_SET(xop, flags); 186231337658SMarcel Moolenaar xo_init_handle(xop); 1863d1a0d267SMarcel Moolenaar xop->xo_style = style; /* Reset style (see LIBXO_OPTIONS) */ 186431337658SMarcel Moolenaar } 186531337658SMarcel Moolenaar 186631337658SMarcel Moolenaar return xop; 186731337658SMarcel Moolenaar } 186831337658SMarcel Moolenaar 186931337658SMarcel Moolenaar /** 187031337658SMarcel Moolenaar * Create a handle that will write to the given file. Use 187131337658SMarcel Moolenaar * the XOF_CLOSE_FP flag to have the file closed on xo_destroy(). 187231337658SMarcel Moolenaar * @fp FILE pointer to use 187331337658SMarcel Moolenaar * @style Style of output desired (XO_STYLE_* value) 187431337658SMarcel Moolenaar * @flags Set of XOF_* flags to use with this handle 187531337658SMarcel Moolenaar */ 187631337658SMarcel Moolenaar xo_handle_t * 187731337658SMarcel Moolenaar xo_create_to_file (FILE *fp, xo_style_t style, xo_xof_flags_t flags) 187831337658SMarcel Moolenaar { 187931337658SMarcel Moolenaar xo_handle_t *xop = xo_create(style, flags); 188031337658SMarcel Moolenaar 188131337658SMarcel Moolenaar if (xop) { 188231337658SMarcel Moolenaar xop->xo_opaque = fp; 188331337658SMarcel Moolenaar xop->xo_write = xo_write_to_file; 188431337658SMarcel Moolenaar xop->xo_close = xo_close_file; 1885545ddfbeSMarcel Moolenaar xop->xo_flush = xo_flush_file; 188631337658SMarcel Moolenaar } 188731337658SMarcel Moolenaar 188831337658SMarcel Moolenaar return xop; 188931337658SMarcel Moolenaar } 189031337658SMarcel Moolenaar 189131337658SMarcel Moolenaar /** 189242ff34c3SPhil Shafer * Set the default handler to output to a file. 189342ff34c3SPhil Shafer * @xop libxo handle 189442ff34c3SPhil Shafer * @fp FILE pointer to use 189542ff34c3SPhil Shafer */ 189642ff34c3SPhil Shafer int 189742ff34c3SPhil Shafer xo_set_file_h (xo_handle_t *xop, FILE *fp) 189842ff34c3SPhil Shafer { 189942ff34c3SPhil Shafer xop = xo_default(xop); 190042ff34c3SPhil Shafer 190142ff34c3SPhil Shafer if (fp == NULL) { 190242ff34c3SPhil Shafer xo_failure(xop, "xo_set_file: NULL fp"); 190342ff34c3SPhil Shafer return -1; 190442ff34c3SPhil Shafer } 190542ff34c3SPhil Shafer 190642ff34c3SPhil Shafer xop->xo_opaque = fp; 190742ff34c3SPhil Shafer xop->xo_write = xo_write_to_file; 190842ff34c3SPhil Shafer xop->xo_close = xo_close_file; 190942ff34c3SPhil Shafer xop->xo_flush = xo_flush_file; 191042ff34c3SPhil Shafer 191142ff34c3SPhil Shafer return 0; 191242ff34c3SPhil Shafer } 191342ff34c3SPhil Shafer 191442ff34c3SPhil Shafer /** 191542ff34c3SPhil Shafer * Set the default handler to output to a file. 191642ff34c3SPhil Shafer * @fp FILE pointer to use 191742ff34c3SPhil Shafer */ 191842ff34c3SPhil Shafer int 191942ff34c3SPhil Shafer xo_set_file (FILE *fp) 192042ff34c3SPhil Shafer { 192142ff34c3SPhil Shafer return xo_set_file_h(NULL, fp); 192242ff34c3SPhil Shafer } 192342ff34c3SPhil Shafer 192442ff34c3SPhil Shafer /** 192531337658SMarcel Moolenaar * Release any resources held by the handle. 192631337658SMarcel Moolenaar * @xop XO handle to alter (or NULL for default handle) 192731337658SMarcel Moolenaar */ 192831337658SMarcel Moolenaar void 1929c600d307SMarcel Moolenaar xo_destroy (xo_handle_t *xop_arg) 193031337658SMarcel Moolenaar { 1931c600d307SMarcel Moolenaar xo_handle_t *xop = xo_default(xop_arg); 193231337658SMarcel Moolenaar 1933d1a0d267SMarcel Moolenaar xo_flush_h(xop); 1934d1a0d267SMarcel Moolenaar 1935d1a0d267SMarcel Moolenaar if (xop->xo_close && XOF_ISSET(xop, XOF_CLOSE_FP)) 193631337658SMarcel Moolenaar xop->xo_close(xop->xo_opaque); 193731337658SMarcel Moolenaar 193831337658SMarcel Moolenaar xo_free(xop->xo_stack); 193931337658SMarcel Moolenaar xo_buf_cleanup(&xop->xo_data); 194031337658SMarcel Moolenaar xo_buf_cleanup(&xop->xo_fmt); 194131337658SMarcel Moolenaar xo_buf_cleanup(&xop->xo_predicate); 194231337658SMarcel Moolenaar xo_buf_cleanup(&xop->xo_attrs); 1943788ca347SMarcel Moolenaar xo_buf_cleanup(&xop->xo_color_buf); 1944788ca347SMarcel Moolenaar 1945788ca347SMarcel Moolenaar if (xop->xo_version) 1946788ca347SMarcel Moolenaar xo_free(xop->xo_version); 194731337658SMarcel Moolenaar 1948c600d307SMarcel Moolenaar if (xop_arg == NULL) { 1949545ddfbeSMarcel Moolenaar bzero(&xo_default_handle, sizeof(xo_default_handle)); 195031337658SMarcel Moolenaar xo_default_inited = 0; 195131337658SMarcel Moolenaar } else 195231337658SMarcel Moolenaar xo_free(xop); 195331337658SMarcel Moolenaar } 195431337658SMarcel Moolenaar 195531337658SMarcel Moolenaar /** 195631337658SMarcel Moolenaar * Record a new output style to use for the given handle (or default if 195731337658SMarcel Moolenaar * handle is NULL). This output style will be used for any future output. 195831337658SMarcel Moolenaar * 195931337658SMarcel Moolenaar * @xop XO handle to alter (or NULL for default handle) 196031337658SMarcel Moolenaar * @style new output style (XO_STYLE_*) 196131337658SMarcel Moolenaar */ 196231337658SMarcel Moolenaar void 196331337658SMarcel Moolenaar xo_set_style (xo_handle_t *xop, xo_style_t style) 196431337658SMarcel Moolenaar { 196531337658SMarcel Moolenaar xop = xo_default(xop); 196631337658SMarcel Moolenaar xop->xo_style = style; 196731337658SMarcel Moolenaar } 196831337658SMarcel Moolenaar 196931337658SMarcel Moolenaar xo_style_t 197031337658SMarcel Moolenaar xo_get_style (xo_handle_t *xop) 197131337658SMarcel Moolenaar { 197231337658SMarcel Moolenaar xop = xo_default(xop); 1973788ca347SMarcel Moolenaar return xo_style(xop); 197431337658SMarcel Moolenaar } 197531337658SMarcel Moolenaar 197631337658SMarcel Moolenaar static int 197731337658SMarcel Moolenaar xo_name_to_style (const char *name) 197831337658SMarcel Moolenaar { 197931337658SMarcel Moolenaar if (strcmp(name, "xml") == 0) 198031337658SMarcel Moolenaar return XO_STYLE_XML; 198131337658SMarcel Moolenaar else if (strcmp(name, "json") == 0) 198231337658SMarcel Moolenaar return XO_STYLE_JSON; 1983d1a0d267SMarcel Moolenaar else if (strcmp(name, "encoder") == 0) 1984d1a0d267SMarcel Moolenaar return XO_STYLE_ENCODER; 198531337658SMarcel Moolenaar else if (strcmp(name, "text") == 0) 198631337658SMarcel Moolenaar return XO_STYLE_TEXT; 198731337658SMarcel Moolenaar else if (strcmp(name, "html") == 0) 198831337658SMarcel Moolenaar return XO_STYLE_HTML; 1989d1a0d267SMarcel Moolenaar else if (strcmp(name, "sdparams") == 0) 1990d1a0d267SMarcel Moolenaar return XO_STYLE_SDPARAMS; 199131337658SMarcel Moolenaar 199231337658SMarcel Moolenaar return -1; 199331337658SMarcel Moolenaar } 199431337658SMarcel Moolenaar 199531337658SMarcel Moolenaar /* 1996d1a0d267SMarcel Moolenaar * Indicate if the style is an "encoding" one as opposed to a "display" one. 1997d1a0d267SMarcel Moolenaar */ 1998d1a0d267SMarcel Moolenaar static int 1999d1a0d267SMarcel Moolenaar xo_style_is_encoding (xo_handle_t *xop) 2000d1a0d267SMarcel Moolenaar { 2001d1a0d267SMarcel Moolenaar if (xo_style(xop) == XO_STYLE_JSON 2002d1a0d267SMarcel Moolenaar || xo_style(xop) == XO_STYLE_XML 2003d1a0d267SMarcel Moolenaar || xo_style(xop) == XO_STYLE_SDPARAMS 2004d1a0d267SMarcel Moolenaar || xo_style(xop) == XO_STYLE_ENCODER) 2005d1a0d267SMarcel Moolenaar return 1; 2006d1a0d267SMarcel Moolenaar return 0; 2007d1a0d267SMarcel Moolenaar } 2008d1a0d267SMarcel Moolenaar 2009d1a0d267SMarcel Moolenaar /* Simple name-value mapping */ 2010d1a0d267SMarcel Moolenaar typedef struct xo_mapping_s { 2011d1a0d267SMarcel Moolenaar xo_xff_flags_t xm_value; 2012d1a0d267SMarcel Moolenaar const char *xm_name; 2013d1a0d267SMarcel Moolenaar } xo_mapping_t; 2014d1a0d267SMarcel Moolenaar 2015d1a0d267SMarcel Moolenaar static xo_xff_flags_t 20168a6eceffSPhil Shafer xo_name_lookup (xo_mapping_t *map, const char *value, ssize_t len) 2017d1a0d267SMarcel Moolenaar { 2018d1a0d267SMarcel Moolenaar if (len == 0) 2019d1a0d267SMarcel Moolenaar return 0; 2020d1a0d267SMarcel Moolenaar 2021d1a0d267SMarcel Moolenaar if (len < 0) 2022d1a0d267SMarcel Moolenaar len = strlen(value); 2023d1a0d267SMarcel Moolenaar 2024d1a0d267SMarcel Moolenaar while (isspace((int) *value)) { 2025d1a0d267SMarcel Moolenaar value += 1; 2026d1a0d267SMarcel Moolenaar len -= 1; 2027d1a0d267SMarcel Moolenaar } 2028d1a0d267SMarcel Moolenaar 2029d1a0d267SMarcel Moolenaar while (isspace((int) value[len])) 2030d1a0d267SMarcel Moolenaar len -= 1; 2031d1a0d267SMarcel Moolenaar 2032d1a0d267SMarcel Moolenaar if (*value == '\0') 2033d1a0d267SMarcel Moolenaar return 0; 2034d1a0d267SMarcel Moolenaar 2035d1a0d267SMarcel Moolenaar for ( ; map->xm_name; map++) 2036d1a0d267SMarcel Moolenaar if (strncmp(map->xm_name, value, len) == 0) 2037d1a0d267SMarcel Moolenaar return map->xm_value; 2038d1a0d267SMarcel Moolenaar 2039d1a0d267SMarcel Moolenaar return 0; 2040d1a0d267SMarcel Moolenaar } 2041d1a0d267SMarcel Moolenaar 2042d1a0d267SMarcel Moolenaar #ifdef NOT_NEEDED_YET 2043d1a0d267SMarcel Moolenaar static const char * 2044d1a0d267SMarcel Moolenaar xo_value_lookup (xo_mapping_t *map, xo_xff_flags_t value) 2045d1a0d267SMarcel Moolenaar { 2046d1a0d267SMarcel Moolenaar if (value == 0) 2047d1a0d267SMarcel Moolenaar return NULL; 2048d1a0d267SMarcel Moolenaar 2049d1a0d267SMarcel Moolenaar for ( ; map->xm_name; map++) 2050d1a0d267SMarcel Moolenaar if (map->xm_value == value) 2051d1a0d267SMarcel Moolenaar return map->xm_name; 2052d1a0d267SMarcel Moolenaar 2053d1a0d267SMarcel Moolenaar return NULL; 2054d1a0d267SMarcel Moolenaar } 2055d1a0d267SMarcel Moolenaar #endif /* NOT_NEEDED_YET */ 2056d1a0d267SMarcel Moolenaar 2057d1a0d267SMarcel Moolenaar static xo_mapping_t xo_xof_names[] = { 2058d1a0d267SMarcel Moolenaar { XOF_COLOR_ALLOWED, "color" }, 2059d1a0d267SMarcel Moolenaar { XOF_COLUMNS, "columns" }, 2060d1a0d267SMarcel Moolenaar { XOF_DTRT, "dtrt" }, 2061d1a0d267SMarcel Moolenaar { XOF_FLUSH, "flush" }, 20628a6eceffSPhil Shafer { XOF_FLUSH_LINE, "flush-line" }, 2063d1a0d267SMarcel Moolenaar { XOF_IGNORE_CLOSE, "ignore-close" }, 2064d1a0d267SMarcel Moolenaar { XOF_INFO, "info" }, 2065d1a0d267SMarcel Moolenaar { XOF_KEYS, "keys" }, 2066d1a0d267SMarcel Moolenaar { XOF_LOG_GETTEXT, "log-gettext" }, 2067d1a0d267SMarcel Moolenaar { XOF_LOG_SYSLOG, "log-syslog" }, 2068d1a0d267SMarcel Moolenaar { XOF_NO_HUMANIZE, "no-humanize" }, 2069d1a0d267SMarcel Moolenaar { XOF_NO_LOCALE, "no-locale" }, 207042ff34c3SPhil Shafer { XOF_RETAIN_NONE, "no-retain" }, 2071d1a0d267SMarcel Moolenaar { XOF_NO_TOP, "no-top" }, 2072d1a0d267SMarcel Moolenaar { XOF_NOT_FIRST, "not-first" }, 2073d1a0d267SMarcel Moolenaar { XOF_PRETTY, "pretty" }, 207442ff34c3SPhil Shafer { XOF_RETAIN_ALL, "retain" }, 2075d1a0d267SMarcel Moolenaar { XOF_UNDERSCORES, "underscores" }, 2076d1a0d267SMarcel Moolenaar { XOF_UNITS, "units" }, 2077d1a0d267SMarcel Moolenaar { XOF_WARN, "warn" }, 2078d1a0d267SMarcel Moolenaar { XOF_WARN_XML, "warn-xml" }, 2079d1a0d267SMarcel Moolenaar { XOF_XPATH, "xpath" }, 2080d1a0d267SMarcel Moolenaar { 0, NULL } 2081d1a0d267SMarcel Moolenaar }; 2082d1a0d267SMarcel Moolenaar 2083d1a0d267SMarcel Moolenaar /* 208431337658SMarcel Moolenaar * Convert string name to XOF_* flag value. 208531337658SMarcel Moolenaar * Not all are useful. Or safe. Or sane. 208631337658SMarcel Moolenaar */ 208731337658SMarcel Moolenaar static unsigned 208831337658SMarcel Moolenaar xo_name_to_flag (const char *name) 208931337658SMarcel Moolenaar { 2090d1a0d267SMarcel Moolenaar return (unsigned) xo_name_lookup(xo_xof_names, name, -1); 209131337658SMarcel Moolenaar } 209231337658SMarcel Moolenaar 209331337658SMarcel Moolenaar int 209431337658SMarcel Moolenaar xo_set_style_name (xo_handle_t *xop, const char *name) 209531337658SMarcel Moolenaar { 209631337658SMarcel Moolenaar if (name == NULL) 209731337658SMarcel Moolenaar return -1; 209831337658SMarcel Moolenaar 209931337658SMarcel Moolenaar int style = xo_name_to_style(name); 210031337658SMarcel Moolenaar if (style < 0) 210131337658SMarcel Moolenaar return -1; 210231337658SMarcel Moolenaar 210331337658SMarcel Moolenaar xo_set_style(xop, style); 210431337658SMarcel Moolenaar return 0; 210531337658SMarcel Moolenaar } 210631337658SMarcel Moolenaar 210731337658SMarcel Moolenaar /* 210831337658SMarcel Moolenaar * Set the options for a handle using a string of options 210931337658SMarcel Moolenaar * passed in. The input is a comma-separated set of names 211031337658SMarcel Moolenaar * and optional values: "xml,pretty,indent=4" 211131337658SMarcel Moolenaar */ 211231337658SMarcel Moolenaar int 211331337658SMarcel Moolenaar xo_set_options (xo_handle_t *xop, const char *input) 211431337658SMarcel Moolenaar { 211531337658SMarcel Moolenaar char *cp, *ep, *vp, *np, *bp; 21168a6eceffSPhil Shafer int style = -1, new_style, rc = 0; 21178a6eceffSPhil Shafer ssize_t len; 211831337658SMarcel Moolenaar xo_xof_flags_t new_flag; 211931337658SMarcel Moolenaar 212031337658SMarcel Moolenaar if (input == NULL) 212131337658SMarcel Moolenaar return 0; 212231337658SMarcel Moolenaar 212331337658SMarcel Moolenaar xop = xo_default(xop); 212431337658SMarcel Moolenaar 2125788ca347SMarcel Moolenaar #ifdef LIBXO_COLOR_ON_BY_DEFAULT 2126788ca347SMarcel Moolenaar /* If the installer used --enable-color-on-by-default, then we allow it */ 2127d1a0d267SMarcel Moolenaar XOF_SET(xop, XOF_COLOR_ALLOWED); 2128788ca347SMarcel Moolenaar #endif /* LIBXO_COLOR_ON_BY_DEFAULT */ 2129788ca347SMarcel Moolenaar 213031337658SMarcel Moolenaar /* 213131337658SMarcel Moolenaar * We support a simpler, old-school style of giving option 213231337658SMarcel Moolenaar * also, using a single character for each option. It's 213331337658SMarcel Moolenaar * ideal for lazy people, such as myself. 213431337658SMarcel Moolenaar */ 213531337658SMarcel Moolenaar if (*input == ':') { 21368a6eceffSPhil Shafer ssize_t sz; 213731337658SMarcel Moolenaar 213831337658SMarcel Moolenaar for (input++ ; *input; input++) { 213931337658SMarcel Moolenaar switch (*input) { 2140788ca347SMarcel Moolenaar case 'c': 2141d1a0d267SMarcel Moolenaar XOF_SET(xop, XOF_COLOR_ALLOWED); 2142788ca347SMarcel Moolenaar break; 2143788ca347SMarcel Moolenaar 214431337658SMarcel Moolenaar case 'f': 2145d1a0d267SMarcel Moolenaar XOF_SET(xop, XOF_FLUSH); 214631337658SMarcel Moolenaar break; 214731337658SMarcel Moolenaar 2148545ddfbeSMarcel Moolenaar case 'F': 2149d1a0d267SMarcel Moolenaar XOF_SET(xop, XOF_FLUSH_LINE); 2150d1a0d267SMarcel Moolenaar break; 2151d1a0d267SMarcel Moolenaar 2152d1a0d267SMarcel Moolenaar case 'g': 2153d1a0d267SMarcel Moolenaar XOF_SET(xop, XOF_LOG_GETTEXT); 2154545ddfbeSMarcel Moolenaar break; 2155545ddfbeSMarcel Moolenaar 215631337658SMarcel Moolenaar case 'H': 215731337658SMarcel Moolenaar xop->xo_style = XO_STYLE_HTML; 215831337658SMarcel Moolenaar break; 215931337658SMarcel Moolenaar 216031337658SMarcel Moolenaar case 'I': 2161d1a0d267SMarcel Moolenaar XOF_SET(xop, XOF_INFO); 216231337658SMarcel Moolenaar break; 216331337658SMarcel Moolenaar 216431337658SMarcel Moolenaar case 'i': 216531337658SMarcel Moolenaar sz = strspn(input + 1, "0123456789"); 216631337658SMarcel Moolenaar if (sz > 0) { 216731337658SMarcel Moolenaar xop->xo_indent_by = atoi(input + 1); 216831337658SMarcel Moolenaar input += sz - 1; /* Skip value */ 216931337658SMarcel Moolenaar } 217031337658SMarcel Moolenaar break; 217131337658SMarcel Moolenaar 217231337658SMarcel Moolenaar case 'J': 217331337658SMarcel Moolenaar xop->xo_style = XO_STYLE_JSON; 217431337658SMarcel Moolenaar break; 217531337658SMarcel Moolenaar 2176d1a0d267SMarcel Moolenaar case 'k': 2177d1a0d267SMarcel Moolenaar XOF_SET(xop, XOF_KEYS); 2178d1a0d267SMarcel Moolenaar break; 2179d1a0d267SMarcel Moolenaar 2180d1a0d267SMarcel Moolenaar case 'n': 2181d1a0d267SMarcel Moolenaar XOF_SET(xop, XOF_NO_HUMANIZE); 2182d1a0d267SMarcel Moolenaar break; 2183d1a0d267SMarcel Moolenaar 218431337658SMarcel Moolenaar case 'P': 2185d1a0d267SMarcel Moolenaar XOF_SET(xop, XOF_PRETTY); 218631337658SMarcel Moolenaar break; 218731337658SMarcel Moolenaar 218831337658SMarcel Moolenaar case 'T': 218931337658SMarcel Moolenaar xop->xo_style = XO_STYLE_TEXT; 219031337658SMarcel Moolenaar break; 219131337658SMarcel Moolenaar 219231337658SMarcel Moolenaar case 'U': 2193d1a0d267SMarcel Moolenaar XOF_SET(xop, XOF_UNITS); 219431337658SMarcel Moolenaar break; 219531337658SMarcel Moolenaar 219631337658SMarcel Moolenaar case 'u': 2197d1a0d267SMarcel Moolenaar XOF_SET(xop, XOF_UNDERSCORES); 219831337658SMarcel Moolenaar break; 219931337658SMarcel Moolenaar 220031337658SMarcel Moolenaar case 'W': 2201d1a0d267SMarcel Moolenaar XOF_SET(xop, XOF_WARN); 220231337658SMarcel Moolenaar break; 220331337658SMarcel Moolenaar 220431337658SMarcel Moolenaar case 'X': 220531337658SMarcel Moolenaar xop->xo_style = XO_STYLE_XML; 220631337658SMarcel Moolenaar break; 220731337658SMarcel Moolenaar 220831337658SMarcel Moolenaar case 'x': 2209d1a0d267SMarcel Moolenaar XOF_SET(xop, XOF_XPATH); 221031337658SMarcel Moolenaar break; 221131337658SMarcel Moolenaar } 221231337658SMarcel Moolenaar } 221331337658SMarcel Moolenaar return 0; 221431337658SMarcel Moolenaar } 221531337658SMarcel Moolenaar 221631337658SMarcel Moolenaar len = strlen(input) + 1; 221731337658SMarcel Moolenaar bp = alloca(len); 221831337658SMarcel Moolenaar memcpy(bp, input, len); 221931337658SMarcel Moolenaar 222031337658SMarcel Moolenaar for (cp = bp, ep = cp + len - 1; cp && cp < ep; cp = np) { 222131337658SMarcel Moolenaar np = strchr(cp, ','); 222231337658SMarcel Moolenaar if (np) 222331337658SMarcel Moolenaar *np++ = '\0'; 222431337658SMarcel Moolenaar 222531337658SMarcel Moolenaar vp = strchr(cp, '='); 222631337658SMarcel Moolenaar if (vp) 222731337658SMarcel Moolenaar *vp++ = '\0'; 222831337658SMarcel Moolenaar 2229788ca347SMarcel Moolenaar if (strcmp("colors", cp) == 0) { 2230788ca347SMarcel Moolenaar /* XXX Look for colors=red-blue+green-yellow */ 2231788ca347SMarcel Moolenaar continue; 2232788ca347SMarcel Moolenaar } 2233788ca347SMarcel Moolenaar 2234d1a0d267SMarcel Moolenaar /* 2235d1a0d267SMarcel Moolenaar * For options, we don't allow "encoder" since we want to 2236d1a0d267SMarcel Moolenaar * handle it explicitly below as "encoder=xxx". 2237d1a0d267SMarcel Moolenaar */ 223831337658SMarcel Moolenaar new_style = xo_name_to_style(cp); 2239d1a0d267SMarcel Moolenaar if (new_style >= 0 && new_style != XO_STYLE_ENCODER) { 224031337658SMarcel Moolenaar if (style >= 0) 224131337658SMarcel Moolenaar xo_warnx("ignoring multiple styles: '%s'", cp); 224231337658SMarcel Moolenaar else 224331337658SMarcel Moolenaar style = new_style; 224431337658SMarcel Moolenaar } else { 224531337658SMarcel Moolenaar new_flag = xo_name_to_flag(cp); 224631337658SMarcel Moolenaar if (new_flag != 0) 2247d1a0d267SMarcel Moolenaar XOF_SET(xop, new_flag); 224831337658SMarcel Moolenaar else { 2249788ca347SMarcel Moolenaar if (strcmp(cp, "no-color") == 0) { 2250d1a0d267SMarcel Moolenaar XOF_CLEAR(xop, XOF_COLOR_ALLOWED); 2251788ca347SMarcel Moolenaar } else if (strcmp(cp, "indent") == 0) { 2252d1a0d267SMarcel Moolenaar if (vp) 225331337658SMarcel Moolenaar xop->xo_indent_by = atoi(vp); 2254d1a0d267SMarcel Moolenaar else 2255d1a0d267SMarcel Moolenaar xo_failure(xop, "missing value for indent option"); 2256d1a0d267SMarcel Moolenaar } else if (strcmp(cp, "encoder") == 0) { 2257d1a0d267SMarcel Moolenaar if (vp == NULL) 2258d1a0d267SMarcel Moolenaar xo_failure(xop, "missing value for encoder option"); 2259d1a0d267SMarcel Moolenaar else { 2260d1a0d267SMarcel Moolenaar if (xo_encoder_init(xop, vp)) { 2261d1a0d267SMarcel Moolenaar xo_failure(xop, "encoder not found: %s", vp); 2262d1a0d267SMarcel Moolenaar rc = -1; 2263d1a0d267SMarcel Moolenaar } 2264d1a0d267SMarcel Moolenaar } 2265d1a0d267SMarcel Moolenaar 226631337658SMarcel Moolenaar } else { 2267d1a0d267SMarcel Moolenaar xo_warnx("unknown libxo option value: '%s'", cp); 226831337658SMarcel Moolenaar rc = -1; 226931337658SMarcel Moolenaar } 227031337658SMarcel Moolenaar } 227131337658SMarcel Moolenaar } 227231337658SMarcel Moolenaar } 227331337658SMarcel Moolenaar 227431337658SMarcel Moolenaar if (style > 0) 227531337658SMarcel Moolenaar xop->xo_style= style; 227631337658SMarcel Moolenaar 227731337658SMarcel Moolenaar return rc; 227831337658SMarcel Moolenaar } 227931337658SMarcel Moolenaar 228031337658SMarcel Moolenaar /** 228131337658SMarcel Moolenaar * Set one or more flags for a given handle (or default if handle is NULL). 228231337658SMarcel Moolenaar * These flags will affect future output. 228331337658SMarcel Moolenaar * 228431337658SMarcel Moolenaar * @xop XO handle to alter (or NULL for default handle) 228531337658SMarcel Moolenaar * @flags Flags to be set (XOF_*) 228631337658SMarcel Moolenaar */ 228731337658SMarcel Moolenaar void 228831337658SMarcel Moolenaar xo_set_flags (xo_handle_t *xop, xo_xof_flags_t flags) 228931337658SMarcel Moolenaar { 229031337658SMarcel Moolenaar xop = xo_default(xop); 229131337658SMarcel Moolenaar 2292d1a0d267SMarcel Moolenaar XOF_SET(xop, flags); 229331337658SMarcel Moolenaar } 229431337658SMarcel Moolenaar 229531337658SMarcel Moolenaar xo_xof_flags_t 229631337658SMarcel Moolenaar xo_get_flags (xo_handle_t *xop) 229731337658SMarcel Moolenaar { 229831337658SMarcel Moolenaar xop = xo_default(xop); 229931337658SMarcel Moolenaar 230031337658SMarcel Moolenaar return xop->xo_flags; 230131337658SMarcel Moolenaar } 230231337658SMarcel Moolenaar 2303d1a0d267SMarcel Moolenaar /* 2304d1a0d267SMarcel Moolenaar * strndup with a twist: len < 0 means strlen 2305d1a0d267SMarcel Moolenaar */ 2306d1a0d267SMarcel Moolenaar static char * 23078a6eceffSPhil Shafer xo_strndup (const char *str, ssize_t len) 2308d1a0d267SMarcel Moolenaar { 2309d1a0d267SMarcel Moolenaar if (len < 0) 2310d1a0d267SMarcel Moolenaar len = strlen(str); 2311d1a0d267SMarcel Moolenaar 2312d1a0d267SMarcel Moolenaar char *cp = xo_realloc(NULL, len + 1); 2313d1a0d267SMarcel Moolenaar if (cp) { 2314d1a0d267SMarcel Moolenaar memcpy(cp, str, len); 2315d1a0d267SMarcel Moolenaar cp[len] = '\0'; 2316d1a0d267SMarcel Moolenaar } 2317d1a0d267SMarcel Moolenaar 2318d1a0d267SMarcel Moolenaar return cp; 2319d1a0d267SMarcel Moolenaar } 2320d1a0d267SMarcel Moolenaar 232131337658SMarcel Moolenaar /** 232231337658SMarcel Moolenaar * Record a leading prefix for the XPath we generate. This allows the 232331337658SMarcel Moolenaar * generated data to be placed within an XML hierarchy but still have 232431337658SMarcel Moolenaar * accurate XPath expressions. 232531337658SMarcel Moolenaar * 232631337658SMarcel Moolenaar * @xop XO handle to alter (or NULL for default handle) 232731337658SMarcel Moolenaar * @path The XPath expression 232831337658SMarcel Moolenaar */ 232931337658SMarcel Moolenaar void 233031337658SMarcel Moolenaar xo_set_leading_xpath (xo_handle_t *xop, const char *path) 233131337658SMarcel Moolenaar { 233231337658SMarcel Moolenaar xop = xo_default(xop); 233331337658SMarcel Moolenaar 233431337658SMarcel Moolenaar if (xop->xo_leading_xpath) { 233531337658SMarcel Moolenaar xo_free(xop->xo_leading_xpath); 233631337658SMarcel Moolenaar xop->xo_leading_xpath = NULL; 233731337658SMarcel Moolenaar } 233831337658SMarcel Moolenaar 233931337658SMarcel Moolenaar if (path == NULL) 234031337658SMarcel Moolenaar return; 234131337658SMarcel Moolenaar 2342d1a0d267SMarcel Moolenaar xop->xo_leading_xpath = xo_strndup(path, -1); 234331337658SMarcel Moolenaar } 234431337658SMarcel Moolenaar 234531337658SMarcel Moolenaar /** 234631337658SMarcel Moolenaar * Record the info data for a set of tags 234731337658SMarcel Moolenaar * 234831337658SMarcel Moolenaar * @xop XO handle to alter (or NULL for default handle) 234931337658SMarcel Moolenaar * @info Info data (xo_info_t) to be recorded (or NULL) (MUST BE SORTED) 235031337658SMarcel Moolenaar * @count Number of entries in info (or -1 to count them ourselves) 235131337658SMarcel Moolenaar */ 235231337658SMarcel Moolenaar void 235331337658SMarcel Moolenaar xo_set_info (xo_handle_t *xop, xo_info_t *infop, int count) 235431337658SMarcel Moolenaar { 235531337658SMarcel Moolenaar xop = xo_default(xop); 235631337658SMarcel Moolenaar 235731337658SMarcel Moolenaar if (count < 0 && infop) { 235831337658SMarcel Moolenaar xo_info_t *xip; 235931337658SMarcel Moolenaar 236031337658SMarcel Moolenaar for (xip = infop, count = 0; xip->xi_name; xip++, count++) 236131337658SMarcel Moolenaar continue; 236231337658SMarcel Moolenaar } 236331337658SMarcel Moolenaar 236431337658SMarcel Moolenaar xop->xo_info = infop; 236531337658SMarcel Moolenaar xop->xo_info_count = count; 236631337658SMarcel Moolenaar } 236731337658SMarcel Moolenaar 236831337658SMarcel Moolenaar /** 236931337658SMarcel Moolenaar * Set the formatter callback for a handle. The callback should 237031337658SMarcel Moolenaar * return a newly formatting contents of a formatting instruction, 237131337658SMarcel Moolenaar * meaning the bits inside the braces. 237231337658SMarcel Moolenaar */ 237331337658SMarcel Moolenaar void 237431337658SMarcel Moolenaar xo_set_formatter (xo_handle_t *xop, xo_formatter_t func, 237531337658SMarcel Moolenaar xo_checkpointer_t cfunc) 237631337658SMarcel Moolenaar { 237731337658SMarcel Moolenaar xop = xo_default(xop); 237831337658SMarcel Moolenaar 237931337658SMarcel Moolenaar xop->xo_formatter = func; 238031337658SMarcel Moolenaar xop->xo_checkpointer = cfunc; 238131337658SMarcel Moolenaar } 238231337658SMarcel Moolenaar 238331337658SMarcel Moolenaar /** 238431337658SMarcel Moolenaar * Clear one or more flags for a given handle (or default if handle is NULL). 238531337658SMarcel Moolenaar * These flags will affect future output. 238631337658SMarcel Moolenaar * 238731337658SMarcel Moolenaar * @xop XO handle to alter (or NULL for default handle) 238831337658SMarcel Moolenaar * @flags Flags to be cleared (XOF_*) 238931337658SMarcel Moolenaar */ 239031337658SMarcel Moolenaar void 239131337658SMarcel Moolenaar xo_clear_flags (xo_handle_t *xop, xo_xof_flags_t flags) 239231337658SMarcel Moolenaar { 239331337658SMarcel Moolenaar xop = xo_default(xop); 239431337658SMarcel Moolenaar 2395d1a0d267SMarcel Moolenaar XOF_CLEAR(xop, flags); 239631337658SMarcel Moolenaar } 239731337658SMarcel Moolenaar 2398545ddfbeSMarcel Moolenaar static const char * 2399545ddfbeSMarcel Moolenaar xo_state_name (xo_state_t state) 2400545ddfbeSMarcel Moolenaar { 2401545ddfbeSMarcel Moolenaar static const char *names[] = { 2402545ddfbeSMarcel Moolenaar "init", 2403545ddfbeSMarcel Moolenaar "open_container", 2404545ddfbeSMarcel Moolenaar "close_container", 2405545ddfbeSMarcel Moolenaar "open_list", 2406545ddfbeSMarcel Moolenaar "close_list", 2407545ddfbeSMarcel Moolenaar "open_instance", 2408545ddfbeSMarcel Moolenaar "close_instance", 2409545ddfbeSMarcel Moolenaar "open_leaf_list", 2410545ddfbeSMarcel Moolenaar "close_leaf_list", 2411545ddfbeSMarcel Moolenaar "discarding", 2412545ddfbeSMarcel Moolenaar "marker", 2413545ddfbeSMarcel Moolenaar "emit", 2414545ddfbeSMarcel Moolenaar "emit_leaf_list", 2415545ddfbeSMarcel Moolenaar "finish", 2416545ddfbeSMarcel Moolenaar NULL 2417545ddfbeSMarcel Moolenaar }; 2418545ddfbeSMarcel Moolenaar 2419545ddfbeSMarcel Moolenaar if (state < (sizeof(names) / sizeof(names[0]))) 2420545ddfbeSMarcel Moolenaar return names[state]; 2421545ddfbeSMarcel Moolenaar 2422545ddfbeSMarcel Moolenaar return "unknown"; 2423545ddfbeSMarcel Moolenaar } 2424545ddfbeSMarcel Moolenaar 242531337658SMarcel Moolenaar static void 242631337658SMarcel Moolenaar xo_line_ensure_open (xo_handle_t *xop, xo_xff_flags_t flags UNUSED) 242731337658SMarcel Moolenaar { 242831337658SMarcel Moolenaar static char div_open[] = "<div class=\"line\">"; 242931337658SMarcel Moolenaar static char div_open_blank[] = "<div class=\"blank-line\">"; 243031337658SMarcel Moolenaar 2431d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_DIV_OPEN)) 243231337658SMarcel Moolenaar return; 243331337658SMarcel Moolenaar 2434788ca347SMarcel Moolenaar if (xo_style(xop) != XO_STYLE_HTML) 243531337658SMarcel Moolenaar return; 243631337658SMarcel Moolenaar 2437d1a0d267SMarcel Moolenaar XOIF_SET(xop, XOIF_DIV_OPEN); 243831337658SMarcel Moolenaar if (flags & XFF_BLANK_LINE) 243931337658SMarcel Moolenaar xo_data_append(xop, div_open_blank, sizeof(div_open_blank) - 1); 244031337658SMarcel Moolenaar else 244131337658SMarcel Moolenaar xo_data_append(xop, div_open, sizeof(div_open) - 1); 244231337658SMarcel Moolenaar 2443d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_PRETTY)) 244431337658SMarcel Moolenaar xo_data_append(xop, "\n", 1); 244531337658SMarcel Moolenaar } 244631337658SMarcel Moolenaar 244731337658SMarcel Moolenaar static void 244831337658SMarcel Moolenaar xo_line_close (xo_handle_t *xop) 244931337658SMarcel Moolenaar { 245031337658SMarcel Moolenaar static char div_close[] = "</div>"; 245131337658SMarcel Moolenaar 2452788ca347SMarcel Moolenaar switch (xo_style(xop)) { 245331337658SMarcel Moolenaar case XO_STYLE_HTML: 2454d1a0d267SMarcel Moolenaar if (!XOIF_ISSET(xop, XOIF_DIV_OPEN)) 245531337658SMarcel Moolenaar xo_line_ensure_open(xop, 0); 245631337658SMarcel Moolenaar 2457d1a0d267SMarcel Moolenaar XOIF_CLEAR(xop, XOIF_DIV_OPEN); 245831337658SMarcel Moolenaar xo_data_append(xop, div_close, sizeof(div_close) - 1); 245931337658SMarcel Moolenaar 2460d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_PRETTY)) 246131337658SMarcel Moolenaar xo_data_append(xop, "\n", 1); 246231337658SMarcel Moolenaar break; 246331337658SMarcel Moolenaar 246431337658SMarcel Moolenaar case XO_STYLE_TEXT: 246531337658SMarcel Moolenaar xo_data_append(xop, "\n", 1); 246631337658SMarcel Moolenaar break; 246731337658SMarcel Moolenaar } 246831337658SMarcel Moolenaar } 246931337658SMarcel Moolenaar 247031337658SMarcel Moolenaar static int 247131337658SMarcel Moolenaar xo_info_compare (const void *key, const void *data) 247231337658SMarcel Moolenaar { 247331337658SMarcel Moolenaar const char *name = key; 247431337658SMarcel Moolenaar const xo_info_t *xip = data; 247531337658SMarcel Moolenaar 247631337658SMarcel Moolenaar return strcmp(name, xip->xi_name); 247731337658SMarcel Moolenaar } 247831337658SMarcel Moolenaar 247931337658SMarcel Moolenaar 248031337658SMarcel Moolenaar static xo_info_t * 24818a6eceffSPhil Shafer xo_info_find (xo_handle_t *xop, const char *name, ssize_t nlen) 248231337658SMarcel Moolenaar { 248331337658SMarcel Moolenaar xo_info_t *xip; 248431337658SMarcel Moolenaar char *cp = alloca(nlen + 1); /* Need local copy for NUL termination */ 248531337658SMarcel Moolenaar 248631337658SMarcel Moolenaar memcpy(cp, name, nlen); 248731337658SMarcel Moolenaar cp[nlen] = '\0'; 248831337658SMarcel Moolenaar 248931337658SMarcel Moolenaar xip = bsearch(cp, xop->xo_info, xop->xo_info_count, 249031337658SMarcel Moolenaar sizeof(xop->xo_info[0]), xo_info_compare); 249131337658SMarcel Moolenaar return xip; 249231337658SMarcel Moolenaar } 249331337658SMarcel Moolenaar 249431337658SMarcel Moolenaar #define CONVERT(_have, _need) (((_have) << 8) | (_need)) 249531337658SMarcel Moolenaar 249631337658SMarcel Moolenaar /* 249731337658SMarcel Moolenaar * Check to see that the conversion is safe and sane. 249831337658SMarcel Moolenaar */ 249931337658SMarcel Moolenaar static int 250031337658SMarcel Moolenaar xo_check_conversion (xo_handle_t *xop, int have_enc, int need_enc) 250131337658SMarcel Moolenaar { 250231337658SMarcel Moolenaar switch (CONVERT(have_enc, need_enc)) { 250331337658SMarcel Moolenaar case CONVERT(XF_ENC_UTF8, XF_ENC_UTF8): 250431337658SMarcel Moolenaar case CONVERT(XF_ENC_UTF8, XF_ENC_LOCALE): 250531337658SMarcel Moolenaar case CONVERT(XF_ENC_WIDE, XF_ENC_UTF8): 250631337658SMarcel Moolenaar case CONVERT(XF_ENC_WIDE, XF_ENC_LOCALE): 250731337658SMarcel Moolenaar case CONVERT(XF_ENC_LOCALE, XF_ENC_LOCALE): 250831337658SMarcel Moolenaar case CONVERT(XF_ENC_LOCALE, XF_ENC_UTF8): 250931337658SMarcel Moolenaar return 0; 251031337658SMarcel Moolenaar 251131337658SMarcel Moolenaar default: 251231337658SMarcel Moolenaar xo_failure(xop, "invalid conversion (%c:%c)", have_enc, need_enc); 251331337658SMarcel Moolenaar return 1; 251431337658SMarcel Moolenaar } 251531337658SMarcel Moolenaar } 251631337658SMarcel Moolenaar 251731337658SMarcel Moolenaar static int 251831337658SMarcel Moolenaar xo_format_string_direct (xo_handle_t *xop, xo_buffer_t *xbp, 251931337658SMarcel Moolenaar xo_xff_flags_t flags, 25208a6eceffSPhil Shafer const wchar_t *wcp, const char *cp, 25218a6eceffSPhil Shafer ssize_t len, int max, 252231337658SMarcel Moolenaar int need_enc, int have_enc) 252331337658SMarcel Moolenaar { 252431337658SMarcel Moolenaar int cols = 0; 2525c600d307SMarcel Moolenaar wchar_t wc = 0; 25268a6eceffSPhil Shafer ssize_t ilen, olen; 25278a6eceffSPhil Shafer ssize_t width; 25288a6eceffSPhil Shafer int attr = XOF_BIT_ISSET(flags, XFF_ATTR); 252931337658SMarcel Moolenaar const char *sp; 253031337658SMarcel Moolenaar 253131337658SMarcel Moolenaar if (len > 0 && !xo_buf_has_room(xbp, len)) 253231337658SMarcel Moolenaar return 0; 253331337658SMarcel Moolenaar 253431337658SMarcel Moolenaar for (;;) { 253531337658SMarcel Moolenaar if (len == 0) 253631337658SMarcel Moolenaar break; 253731337658SMarcel Moolenaar 253831337658SMarcel Moolenaar if (cp) { 253931337658SMarcel Moolenaar if (*cp == '\0') 254031337658SMarcel Moolenaar break; 254131337658SMarcel Moolenaar if ((flags & XFF_UNESCAPE) && (*cp == '\\' || *cp == '%')) { 254231337658SMarcel Moolenaar cp += 1; 254331337658SMarcel Moolenaar len -= 1; 254431337658SMarcel Moolenaar } 254531337658SMarcel Moolenaar } 254631337658SMarcel Moolenaar 254731337658SMarcel Moolenaar if (wcp && *wcp == L'\0') 254831337658SMarcel Moolenaar break; 254931337658SMarcel Moolenaar 255031337658SMarcel Moolenaar ilen = 0; 255131337658SMarcel Moolenaar 255231337658SMarcel Moolenaar switch (have_enc) { 255331337658SMarcel Moolenaar case XF_ENC_WIDE: /* Wide character */ 255431337658SMarcel Moolenaar wc = *wcp++; 255531337658SMarcel Moolenaar ilen = 1; 255631337658SMarcel Moolenaar break; 255731337658SMarcel Moolenaar 255831337658SMarcel Moolenaar case XF_ENC_UTF8: /* UTF-8 */ 255931337658SMarcel Moolenaar ilen = xo_utf8_to_wc_len(cp); 256031337658SMarcel Moolenaar if (ilen < 0) { 256131337658SMarcel Moolenaar xo_failure(xop, "invalid UTF-8 character: %02hhx", *cp); 2562d1a0d267SMarcel Moolenaar return -1; /* Can't continue; we can't find the end */ 256331337658SMarcel Moolenaar } 256431337658SMarcel Moolenaar 256531337658SMarcel Moolenaar if (len > 0 && len < ilen) { 256631337658SMarcel Moolenaar len = 0; /* Break out of the loop */ 256731337658SMarcel Moolenaar continue; 256831337658SMarcel Moolenaar } 256931337658SMarcel Moolenaar 257031337658SMarcel Moolenaar wc = xo_utf8_char(cp, ilen); 257131337658SMarcel Moolenaar if (wc == (wchar_t) -1) { 257231337658SMarcel Moolenaar xo_failure(xop, "invalid UTF-8 character: %02hhx/%d", 257331337658SMarcel Moolenaar *cp, ilen); 2574d1a0d267SMarcel Moolenaar return -1; /* Can't continue; we can't find the end */ 257531337658SMarcel Moolenaar } 257631337658SMarcel Moolenaar cp += ilen; 257731337658SMarcel Moolenaar break; 257831337658SMarcel Moolenaar 257931337658SMarcel Moolenaar case XF_ENC_LOCALE: /* Native locale */ 258031337658SMarcel Moolenaar ilen = (len > 0) ? len : MB_LEN_MAX; 258131337658SMarcel Moolenaar ilen = mbrtowc(&wc, cp, ilen, &xop->xo_mbstate); 258231337658SMarcel Moolenaar if (ilen < 0) { /* Invalid data; skip */ 258331337658SMarcel Moolenaar xo_failure(xop, "invalid mbs char: %02hhx", *cp); 2584dbf26257SAlexander Kabaev wc = L'?'; 2585dbf26257SAlexander Kabaev ilen = 1; 258631337658SMarcel Moolenaar } 2587d1a0d267SMarcel Moolenaar 258831337658SMarcel Moolenaar if (ilen == 0) { /* Hit a wide NUL character */ 258931337658SMarcel Moolenaar len = 0; 259031337658SMarcel Moolenaar continue; 259131337658SMarcel Moolenaar } 259231337658SMarcel Moolenaar 259331337658SMarcel Moolenaar cp += ilen; 259431337658SMarcel Moolenaar break; 259531337658SMarcel Moolenaar } 259631337658SMarcel Moolenaar 259731337658SMarcel Moolenaar /* Reduce len, but not below zero */ 259831337658SMarcel Moolenaar if (len > 0) { 259931337658SMarcel Moolenaar len -= ilen; 260031337658SMarcel Moolenaar if (len < 0) 260131337658SMarcel Moolenaar len = 0; 260231337658SMarcel Moolenaar } 260331337658SMarcel Moolenaar 260431337658SMarcel Moolenaar /* 260531337658SMarcel Moolenaar * Find the width-in-columns of this character, which must be done 260631337658SMarcel Moolenaar * in wide characters, since we lack a mbswidth() function. If 260731337658SMarcel Moolenaar * it doesn't fit 260831337658SMarcel Moolenaar */ 2609d1a0d267SMarcel Moolenaar width = xo_wcwidth(wc); 261031337658SMarcel Moolenaar if (width < 0) 261131337658SMarcel Moolenaar width = iswcntrl(wc) ? 0 : 1; 261231337658SMarcel Moolenaar 2613788ca347SMarcel Moolenaar if (xo_style(xop) == XO_STYLE_TEXT || xo_style(xop) == XO_STYLE_HTML) { 261431337658SMarcel Moolenaar if (max > 0 && cols + width > max) 261531337658SMarcel Moolenaar break; 261631337658SMarcel Moolenaar } 261731337658SMarcel Moolenaar 261831337658SMarcel Moolenaar switch (need_enc) { 261931337658SMarcel Moolenaar case XF_ENC_UTF8: 262031337658SMarcel Moolenaar 262131337658SMarcel Moolenaar /* Output in UTF-8 needs to be escaped, based on the style */ 2622788ca347SMarcel Moolenaar switch (xo_style(xop)) { 262331337658SMarcel Moolenaar case XO_STYLE_XML: 262431337658SMarcel Moolenaar case XO_STYLE_HTML: 262531337658SMarcel Moolenaar if (wc == '<') 262631337658SMarcel Moolenaar sp = xo_xml_lt; 262731337658SMarcel Moolenaar else if (wc == '>') 262831337658SMarcel Moolenaar sp = xo_xml_gt; 262931337658SMarcel Moolenaar else if (wc == '&') 263031337658SMarcel Moolenaar sp = xo_xml_amp; 263131337658SMarcel Moolenaar else if (attr && wc == '"') 263231337658SMarcel Moolenaar sp = xo_xml_quot; 263331337658SMarcel Moolenaar else 263431337658SMarcel Moolenaar break; 263531337658SMarcel Moolenaar 26368a6eceffSPhil Shafer ssize_t slen = strlen(sp); 263731337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, slen - 1)) 263831337658SMarcel Moolenaar return -1; 263931337658SMarcel Moolenaar 264031337658SMarcel Moolenaar memcpy(xbp->xb_curp, sp, slen); 264131337658SMarcel Moolenaar xbp->xb_curp += slen; 264231337658SMarcel Moolenaar goto done_with_encoding; /* Need multi-level 'break' */ 264331337658SMarcel Moolenaar 264431337658SMarcel Moolenaar case XO_STYLE_JSON: 2645545ddfbeSMarcel Moolenaar if (wc != '\\' && wc != '"' && wc != '\n' && wc != '\r') 264631337658SMarcel Moolenaar break; 264731337658SMarcel Moolenaar 264831337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, 2)) 264931337658SMarcel Moolenaar return -1; 265031337658SMarcel Moolenaar 265131337658SMarcel Moolenaar *xbp->xb_curp++ = '\\'; 2652545ddfbeSMarcel Moolenaar if (wc == '\n') 2653545ddfbeSMarcel Moolenaar wc = 'n'; 2654545ddfbeSMarcel Moolenaar else if (wc == '\r') 2655545ddfbeSMarcel Moolenaar wc = 'r'; 2656545ddfbeSMarcel Moolenaar else wc = wc & 0x7f; 2657545ddfbeSMarcel Moolenaar 2658545ddfbeSMarcel Moolenaar *xbp->xb_curp++ = wc; 265931337658SMarcel Moolenaar goto done_with_encoding; 2660d1a0d267SMarcel Moolenaar 2661d1a0d267SMarcel Moolenaar case XO_STYLE_SDPARAMS: 2662d1a0d267SMarcel Moolenaar if (wc != '\\' && wc != '"' && wc != ']') 2663d1a0d267SMarcel Moolenaar break; 2664d1a0d267SMarcel Moolenaar 2665d1a0d267SMarcel Moolenaar if (!xo_buf_has_room(xbp, 2)) 2666d1a0d267SMarcel Moolenaar return -1; 2667d1a0d267SMarcel Moolenaar 2668d1a0d267SMarcel Moolenaar *xbp->xb_curp++ = '\\'; 2669d1a0d267SMarcel Moolenaar wc = wc & 0x7f; 2670d1a0d267SMarcel Moolenaar *xbp->xb_curp++ = wc; 2671d1a0d267SMarcel Moolenaar goto done_with_encoding; 267231337658SMarcel Moolenaar } 267331337658SMarcel Moolenaar 267431337658SMarcel Moolenaar olen = xo_utf8_emit_len(wc); 267531337658SMarcel Moolenaar if (olen < 0) { 267631337658SMarcel Moolenaar xo_failure(xop, "ignoring bad length"); 267731337658SMarcel Moolenaar continue; 267831337658SMarcel Moolenaar } 267931337658SMarcel Moolenaar 268031337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, olen)) 268131337658SMarcel Moolenaar return -1; 268231337658SMarcel Moolenaar 268331337658SMarcel Moolenaar xo_utf8_emit_char(xbp->xb_curp, olen, wc); 268431337658SMarcel Moolenaar xbp->xb_curp += olen; 268531337658SMarcel Moolenaar break; 268631337658SMarcel Moolenaar 268731337658SMarcel Moolenaar case XF_ENC_LOCALE: 268831337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, MB_LEN_MAX + 1)) 268931337658SMarcel Moolenaar return -1; 269031337658SMarcel Moolenaar 269131337658SMarcel Moolenaar olen = wcrtomb(xbp->xb_curp, wc, &xop->xo_mbstate); 269231337658SMarcel Moolenaar if (olen <= 0) { 269331337658SMarcel Moolenaar xo_failure(xop, "could not convert wide char: %lx", 269431337658SMarcel Moolenaar (unsigned long) wc); 269531337658SMarcel Moolenaar width = 1; 269631337658SMarcel Moolenaar *xbp->xb_curp++ = '?'; 269731337658SMarcel Moolenaar } else 269831337658SMarcel Moolenaar xbp->xb_curp += olen; 269931337658SMarcel Moolenaar break; 270031337658SMarcel Moolenaar } 270131337658SMarcel Moolenaar 270231337658SMarcel Moolenaar done_with_encoding: 270331337658SMarcel Moolenaar cols += width; 270431337658SMarcel Moolenaar } 270531337658SMarcel Moolenaar 270631337658SMarcel Moolenaar return cols; 270731337658SMarcel Moolenaar } 270831337658SMarcel Moolenaar 270931337658SMarcel Moolenaar static int 2710d1a0d267SMarcel Moolenaar xo_needed_encoding (xo_handle_t *xop) 2711d1a0d267SMarcel Moolenaar { 2712d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_UTF8)) /* Check the override flag */ 2713d1a0d267SMarcel Moolenaar return XF_ENC_UTF8; 2714d1a0d267SMarcel Moolenaar 2715d1a0d267SMarcel Moolenaar if (xo_style(xop) == XO_STYLE_TEXT) /* Text means locale */ 2716d1a0d267SMarcel Moolenaar return XF_ENC_LOCALE; 2717d1a0d267SMarcel Moolenaar 2718d1a0d267SMarcel Moolenaar return XF_ENC_UTF8; /* Otherwise, we love UTF-8 */ 2719d1a0d267SMarcel Moolenaar } 2720d1a0d267SMarcel Moolenaar 27218a6eceffSPhil Shafer static ssize_t 272231337658SMarcel Moolenaar xo_format_string (xo_handle_t *xop, xo_buffer_t *xbp, xo_xff_flags_t flags, 272331337658SMarcel Moolenaar xo_format_t *xfp) 272431337658SMarcel Moolenaar { 272531337658SMarcel Moolenaar static char null[] = "(null)"; 2726d1a0d267SMarcel Moolenaar static char null_no_quotes[] = "null"; 2727a0f704ffSMarcel Moolenaar 272831337658SMarcel Moolenaar char *cp = NULL; 272931337658SMarcel Moolenaar wchar_t *wcp = NULL; 27308a6eceffSPhil Shafer ssize_t len; 27318a6eceffSPhil Shafer ssize_t cols = 0, rc = 0; 27328a6eceffSPhil Shafer ssize_t off = xbp->xb_curp - xbp->xb_bufp, off2; 2733d1a0d267SMarcel Moolenaar int need_enc = xo_needed_encoding(xop); 273431337658SMarcel Moolenaar 273531337658SMarcel Moolenaar if (xo_check_conversion(xop, xfp->xf_enc, need_enc)) 273631337658SMarcel Moolenaar return 0; 273731337658SMarcel Moolenaar 2738a0f704ffSMarcel Moolenaar len = xfp->xf_width[XF_WIDTH_SIZE]; 2739a0f704ffSMarcel Moolenaar 2740d1a0d267SMarcel Moolenaar if (xfp->xf_fc == 'm') { 2741d1a0d267SMarcel Moolenaar cp = strerror(xop->xo_errno); 2742d1a0d267SMarcel Moolenaar if (len < 0) 2743d1a0d267SMarcel Moolenaar len = cp ? strlen(cp) : 0; 2744d1a0d267SMarcel Moolenaar goto normal_string; 2745d1a0d267SMarcel Moolenaar 2746d1a0d267SMarcel Moolenaar } else if (xfp->xf_enc == XF_ENC_WIDE) { 274731337658SMarcel Moolenaar wcp = va_arg(xop->xo_vap, wchar_t *); 274831337658SMarcel Moolenaar if (xfp->xf_skip) 274931337658SMarcel Moolenaar return 0; 275031337658SMarcel Moolenaar 2751a0f704ffSMarcel Moolenaar /* 2752a0f704ffSMarcel Moolenaar * Dont' deref NULL; use the traditional "(null)" instead 2753a0f704ffSMarcel Moolenaar * of the more accurate "who's been a naughty boy, then?". 2754a0f704ffSMarcel Moolenaar */ 2755a0f704ffSMarcel Moolenaar if (wcp == NULL) { 2756a0f704ffSMarcel Moolenaar cp = null; 2757a0f704ffSMarcel Moolenaar len = sizeof(null) - 1; 2758a0f704ffSMarcel Moolenaar } 2759a0f704ffSMarcel Moolenaar 276031337658SMarcel Moolenaar } else { 276131337658SMarcel Moolenaar cp = va_arg(xop->xo_vap, char *); /* UTF-8 or native */ 2762d1a0d267SMarcel Moolenaar 2763d1a0d267SMarcel Moolenaar normal_string: 276431337658SMarcel Moolenaar if (xfp->xf_skip) 276531337658SMarcel Moolenaar return 0; 276631337658SMarcel Moolenaar 2767a0f704ffSMarcel Moolenaar /* Echo "Dont' deref NULL" logic */ 2768a0f704ffSMarcel Moolenaar if (cp == NULL) { 2769d1a0d267SMarcel Moolenaar if ((flags & XFF_NOQUOTE) && xo_style_is_encoding(xop)) { 2770d1a0d267SMarcel Moolenaar cp = null_no_quotes; 2771d1a0d267SMarcel Moolenaar len = sizeof(null_no_quotes) - 1; 2772d1a0d267SMarcel Moolenaar } else { 2773a0f704ffSMarcel Moolenaar cp = null; 2774a0f704ffSMarcel Moolenaar len = sizeof(null) - 1; 2775a0f704ffSMarcel Moolenaar } 2776d1a0d267SMarcel Moolenaar } 2777a0f704ffSMarcel Moolenaar 277831337658SMarcel Moolenaar /* 277931337658SMarcel Moolenaar * Optimize the most common case, which is "%s". We just 278031337658SMarcel Moolenaar * need to copy the complete string to the output buffer. 278131337658SMarcel Moolenaar */ 278231337658SMarcel Moolenaar if (xfp->xf_enc == need_enc 278331337658SMarcel Moolenaar && xfp->xf_width[XF_WIDTH_MIN] < 0 278431337658SMarcel Moolenaar && xfp->xf_width[XF_WIDTH_SIZE] < 0 278531337658SMarcel Moolenaar && xfp->xf_width[XF_WIDTH_MAX] < 0 2786d1a0d267SMarcel Moolenaar && !(XOIF_ISSET(xop, XOIF_ANCHOR) 2787d1a0d267SMarcel Moolenaar || XOF_ISSET(xop, XOF_COLUMNS))) { 278831337658SMarcel Moolenaar len = strlen(cp); 278931337658SMarcel Moolenaar xo_buf_escape(xop, xbp, cp, len, flags); 279031337658SMarcel Moolenaar 279131337658SMarcel Moolenaar /* 279231337658SMarcel Moolenaar * Our caller expects xb_curp left untouched, so we have 279331337658SMarcel Moolenaar * to reset it and return the number of bytes written to 279431337658SMarcel Moolenaar * the buffer. 279531337658SMarcel Moolenaar */ 279631337658SMarcel Moolenaar off2 = xbp->xb_curp - xbp->xb_bufp; 279731337658SMarcel Moolenaar rc = off2 - off; 279831337658SMarcel Moolenaar xbp->xb_curp = xbp->xb_bufp + off; 279931337658SMarcel Moolenaar 280031337658SMarcel Moolenaar return rc; 280131337658SMarcel Moolenaar } 280231337658SMarcel Moolenaar } 280331337658SMarcel Moolenaar 280431337658SMarcel Moolenaar cols = xo_format_string_direct(xop, xbp, flags, wcp, cp, len, 280531337658SMarcel Moolenaar xfp->xf_width[XF_WIDTH_MAX], 280631337658SMarcel Moolenaar need_enc, xfp->xf_enc); 280731337658SMarcel Moolenaar if (cols < 0) 280831337658SMarcel Moolenaar goto bail; 280931337658SMarcel Moolenaar 281031337658SMarcel Moolenaar /* 281131337658SMarcel Moolenaar * xo_buf_append* will move xb_curp, so we save/restore it. 281231337658SMarcel Moolenaar */ 281331337658SMarcel Moolenaar off2 = xbp->xb_curp - xbp->xb_bufp; 281431337658SMarcel Moolenaar rc = off2 - off; 281531337658SMarcel Moolenaar xbp->xb_curp = xbp->xb_bufp + off; 281631337658SMarcel Moolenaar 281731337658SMarcel Moolenaar if (cols < xfp->xf_width[XF_WIDTH_MIN]) { 281831337658SMarcel Moolenaar /* 281931337658SMarcel Moolenaar * Find the number of columns needed to display the string. 282031337658SMarcel Moolenaar * If we have the original wide string, we just call wcswidth, 282131337658SMarcel Moolenaar * but if we did the work ourselves, then we need to do it. 282231337658SMarcel Moolenaar */ 282331337658SMarcel Moolenaar int delta = xfp->xf_width[XF_WIDTH_MIN] - cols; 2824ee5cf116SPhil Shafer if (!xo_buf_has_room(xbp, xfp->xf_width[XF_WIDTH_MIN])) 282531337658SMarcel Moolenaar goto bail; 282631337658SMarcel Moolenaar 282731337658SMarcel Moolenaar /* 282831337658SMarcel Moolenaar * If seen_minus, then pad on the right; otherwise move it so 282931337658SMarcel Moolenaar * we can pad on the left. 283031337658SMarcel Moolenaar */ 283131337658SMarcel Moolenaar if (xfp->xf_seen_minus) { 283231337658SMarcel Moolenaar cp = xbp->xb_curp + rc; 283331337658SMarcel Moolenaar } else { 283431337658SMarcel Moolenaar cp = xbp->xb_curp; 283531337658SMarcel Moolenaar memmove(xbp->xb_curp + delta, xbp->xb_curp, rc); 283631337658SMarcel Moolenaar } 283731337658SMarcel Moolenaar 283831337658SMarcel Moolenaar /* Set the padding */ 283931337658SMarcel Moolenaar memset(cp, (xfp->xf_leading_zero > 0) ? '0' : ' ', delta); 284031337658SMarcel Moolenaar rc += delta; 284131337658SMarcel Moolenaar cols += delta; 284231337658SMarcel Moolenaar } 284331337658SMarcel Moolenaar 2844d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_COLUMNS)) 284531337658SMarcel Moolenaar xop->xo_columns += cols; 2846d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_ANCHOR)) 284731337658SMarcel Moolenaar xop->xo_anchor_columns += cols; 284831337658SMarcel Moolenaar 284931337658SMarcel Moolenaar return rc; 285031337658SMarcel Moolenaar 285131337658SMarcel Moolenaar bail: 285231337658SMarcel Moolenaar xbp->xb_curp = xbp->xb_bufp + off; 285331337658SMarcel Moolenaar return 0; 285431337658SMarcel Moolenaar } 285531337658SMarcel Moolenaar 2856d1a0d267SMarcel Moolenaar /* 2857d1a0d267SMarcel Moolenaar * Look backwards in a buffer to find a numeric value 2858d1a0d267SMarcel Moolenaar */ 2859d1a0d267SMarcel Moolenaar static int 28608a6eceffSPhil Shafer xo_buf_find_last_number (xo_buffer_t *xbp, ssize_t start_offset) 2861d1a0d267SMarcel Moolenaar { 2862d1a0d267SMarcel Moolenaar int rc = 0; /* Fail with zero */ 2863d1a0d267SMarcel Moolenaar int digit = 1; 2864d1a0d267SMarcel Moolenaar char *sp = xbp->xb_bufp; 2865d1a0d267SMarcel Moolenaar char *cp = sp + start_offset; 2866d1a0d267SMarcel Moolenaar 2867d1a0d267SMarcel Moolenaar while (--cp >= sp) 2868d1a0d267SMarcel Moolenaar if (isdigit((int) *cp)) 2869d1a0d267SMarcel Moolenaar break; 2870d1a0d267SMarcel Moolenaar 2871d1a0d267SMarcel Moolenaar for ( ; cp >= sp; cp--) { 2872d1a0d267SMarcel Moolenaar if (!isdigit((int) *cp)) 2873d1a0d267SMarcel Moolenaar break; 2874d1a0d267SMarcel Moolenaar rc += (*cp - '0') * digit; 2875d1a0d267SMarcel Moolenaar digit *= 10; 2876d1a0d267SMarcel Moolenaar } 2877d1a0d267SMarcel Moolenaar 2878d1a0d267SMarcel Moolenaar return rc; 2879d1a0d267SMarcel Moolenaar } 2880d1a0d267SMarcel Moolenaar 28818a6eceffSPhil Shafer static ssize_t 28828a6eceffSPhil Shafer xo_count_utf8_cols (const char *str, ssize_t len) 2883d1a0d267SMarcel Moolenaar { 28848a6eceffSPhil Shafer ssize_t tlen; 2885d1a0d267SMarcel Moolenaar wchar_t wc; 28868a6eceffSPhil Shafer ssize_t cols = 0; 2887d1a0d267SMarcel Moolenaar const char *ep = str + len; 2888d1a0d267SMarcel Moolenaar 2889d1a0d267SMarcel Moolenaar while (str < ep) { 2890d1a0d267SMarcel Moolenaar tlen = xo_utf8_to_wc_len(str); 2891d1a0d267SMarcel Moolenaar if (tlen < 0) /* Broken input is very bad */ 2892d1a0d267SMarcel Moolenaar return cols; 2893d1a0d267SMarcel Moolenaar 2894d1a0d267SMarcel Moolenaar wc = xo_utf8_char(str, tlen); 2895d1a0d267SMarcel Moolenaar if (wc == (wchar_t) -1) 2896d1a0d267SMarcel Moolenaar return cols; 2897d1a0d267SMarcel Moolenaar 2898d1a0d267SMarcel Moolenaar /* We only print printable characters */ 2899d1a0d267SMarcel Moolenaar if (iswprint((wint_t) wc)) { 2900d1a0d267SMarcel Moolenaar /* 2901d1a0d267SMarcel Moolenaar * Find the width-in-columns of this character, which must be done 2902d1a0d267SMarcel Moolenaar * in wide characters, since we lack a mbswidth() function. 2903d1a0d267SMarcel Moolenaar */ 29048a6eceffSPhil Shafer ssize_t width = xo_wcwidth(wc); 2905d1a0d267SMarcel Moolenaar if (width < 0) 2906d1a0d267SMarcel Moolenaar width = iswcntrl(wc) ? 0 : 1; 2907d1a0d267SMarcel Moolenaar 2908d1a0d267SMarcel Moolenaar cols += width; 2909d1a0d267SMarcel Moolenaar } 2910d1a0d267SMarcel Moolenaar 2911d1a0d267SMarcel Moolenaar str += tlen; 2912d1a0d267SMarcel Moolenaar } 2913d1a0d267SMarcel Moolenaar 2914d1a0d267SMarcel Moolenaar return cols; 2915d1a0d267SMarcel Moolenaar } 2916d1a0d267SMarcel Moolenaar 2917d1a0d267SMarcel Moolenaar #ifdef HAVE_GETTEXT 2918d1a0d267SMarcel Moolenaar static inline const char * 2919d1a0d267SMarcel Moolenaar xo_dgettext (xo_handle_t *xop, const char *str) 2920d1a0d267SMarcel Moolenaar { 2921d1a0d267SMarcel Moolenaar const char *domainname = xop->xo_gt_domain; 2922d1a0d267SMarcel Moolenaar const char *res; 2923d1a0d267SMarcel Moolenaar 2924d1a0d267SMarcel Moolenaar res = dgettext(domainname, str); 2925d1a0d267SMarcel Moolenaar 2926d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_LOG_GETTEXT)) 2927d1a0d267SMarcel Moolenaar fprintf(stderr, "xo: gettext: %s%s%smsgid \"%s\" returns \"%s\"\n", 2928d1a0d267SMarcel Moolenaar domainname ? "domain \"" : "", xo_printable(domainname), 2929d1a0d267SMarcel Moolenaar domainname ? "\", " : "", xo_printable(str), xo_printable(res)); 2930d1a0d267SMarcel Moolenaar 2931d1a0d267SMarcel Moolenaar return res; 2932d1a0d267SMarcel Moolenaar } 2933d1a0d267SMarcel Moolenaar 2934d1a0d267SMarcel Moolenaar static inline const char * 2935d1a0d267SMarcel Moolenaar xo_dngettext (xo_handle_t *xop, const char *sing, const char *plural, 2936d1a0d267SMarcel Moolenaar unsigned long int n) 2937d1a0d267SMarcel Moolenaar { 2938d1a0d267SMarcel Moolenaar const char *domainname = xop->xo_gt_domain; 2939d1a0d267SMarcel Moolenaar const char *res; 2940d1a0d267SMarcel Moolenaar 2941d1a0d267SMarcel Moolenaar res = dngettext(domainname, sing, plural, n); 2942d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_LOG_GETTEXT)) 2943d1a0d267SMarcel Moolenaar fprintf(stderr, "xo: gettext: %s%s%s" 2944d1a0d267SMarcel Moolenaar "msgid \"%s\", msgid_plural \"%s\" (%lu) returns \"%s\"\n", 2945d1a0d267SMarcel Moolenaar domainname ? "domain \"" : "", 2946d1a0d267SMarcel Moolenaar xo_printable(domainname), domainname ? "\", " : "", 2947d1a0d267SMarcel Moolenaar xo_printable(sing), 2948d1a0d267SMarcel Moolenaar xo_printable(plural), n, xo_printable(res)); 2949d1a0d267SMarcel Moolenaar 2950d1a0d267SMarcel Moolenaar return res; 2951d1a0d267SMarcel Moolenaar } 2952d1a0d267SMarcel Moolenaar #else /* HAVE_GETTEXT */ 2953d1a0d267SMarcel Moolenaar static inline const char * 2954d1a0d267SMarcel Moolenaar xo_dgettext (xo_handle_t *xop UNUSED, const char *str) 2955d1a0d267SMarcel Moolenaar { 2956d1a0d267SMarcel Moolenaar return str; 2957d1a0d267SMarcel Moolenaar } 2958d1a0d267SMarcel Moolenaar 2959d1a0d267SMarcel Moolenaar static inline const char * 2960d1a0d267SMarcel Moolenaar xo_dngettext (xo_handle_t *xop UNUSED, const char *singular, 2961d1a0d267SMarcel Moolenaar const char *plural, unsigned long int n) 2962d1a0d267SMarcel Moolenaar { 2963d1a0d267SMarcel Moolenaar return (n == 1) ? singular : plural; 2964d1a0d267SMarcel Moolenaar } 2965d1a0d267SMarcel Moolenaar #endif /* HAVE_GETTEXT */ 2966d1a0d267SMarcel Moolenaar 2967d1a0d267SMarcel Moolenaar /* 2968d1a0d267SMarcel Moolenaar * This is really _re_formatting, since the normal format code has 2969d1a0d267SMarcel Moolenaar * generated a beautiful string into xo_data, starting at 2970d1a0d267SMarcel Moolenaar * start_offset. We need to see if it's plural, which means 2971d1a0d267SMarcel Moolenaar * comma-separated options, or singular. Then we make the appropriate 2972d1a0d267SMarcel Moolenaar * call to d[n]gettext() to get the locale-based version. Note that 2973d1a0d267SMarcel Moolenaar * both input and output of gettext() this should be UTF-8. 2974d1a0d267SMarcel Moolenaar */ 29758a6eceffSPhil Shafer static ssize_t 2976d1a0d267SMarcel Moolenaar xo_format_gettext (xo_handle_t *xop, xo_xff_flags_t flags, 29778a6eceffSPhil Shafer ssize_t start_offset, ssize_t cols, int need_enc) 2978d1a0d267SMarcel Moolenaar { 2979d1a0d267SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_data; 2980d1a0d267SMarcel Moolenaar 2981d1a0d267SMarcel Moolenaar if (!xo_buf_has_room(xbp, 1)) 2982d1a0d267SMarcel Moolenaar return cols; 2983d1a0d267SMarcel Moolenaar 2984d1a0d267SMarcel Moolenaar xbp->xb_curp[0] = '\0'; /* NUL-terminate the input string */ 2985d1a0d267SMarcel Moolenaar 2986d1a0d267SMarcel Moolenaar char *cp = xbp->xb_bufp + start_offset; 29878a6eceffSPhil Shafer ssize_t len = xbp->xb_curp - cp; 2988d1a0d267SMarcel Moolenaar const char *newstr = NULL; 2989d1a0d267SMarcel Moolenaar 2990d1a0d267SMarcel Moolenaar /* 2991d1a0d267SMarcel Moolenaar * The plural flag asks us to look backwards at the last numeric 2992d1a0d267SMarcel Moolenaar * value rendered and disect the string into two pieces. 2993d1a0d267SMarcel Moolenaar */ 2994d1a0d267SMarcel Moolenaar if (flags & XFF_GT_PLURAL) { 2995d1a0d267SMarcel Moolenaar int n = xo_buf_find_last_number(xbp, start_offset); 2996d1a0d267SMarcel Moolenaar char *two = memchr(cp, (int) ',', len); 2997d1a0d267SMarcel Moolenaar if (two == NULL) { 2998d1a0d267SMarcel Moolenaar xo_failure(xop, "no comma in plural gettext field: '%s'", cp); 2999d1a0d267SMarcel Moolenaar return cols; 3000d1a0d267SMarcel Moolenaar } 3001d1a0d267SMarcel Moolenaar 3002d1a0d267SMarcel Moolenaar if (two == cp) { 3003d1a0d267SMarcel Moolenaar xo_failure(xop, "nothing before comma in plural gettext " 3004d1a0d267SMarcel Moolenaar "field: '%s'", cp); 3005d1a0d267SMarcel Moolenaar return cols; 3006d1a0d267SMarcel Moolenaar } 3007d1a0d267SMarcel Moolenaar 3008d1a0d267SMarcel Moolenaar if (two == xbp->xb_curp) { 3009d1a0d267SMarcel Moolenaar xo_failure(xop, "nothing after comma in plural gettext " 3010d1a0d267SMarcel Moolenaar "field: '%s'", cp); 3011d1a0d267SMarcel Moolenaar return cols; 3012d1a0d267SMarcel Moolenaar } 3013d1a0d267SMarcel Moolenaar 3014d1a0d267SMarcel Moolenaar *two++ = '\0'; 3015d1a0d267SMarcel Moolenaar if (flags & XFF_GT_FIELD) { 3016d1a0d267SMarcel Moolenaar newstr = xo_dngettext(xop, cp, two, n); 3017d1a0d267SMarcel Moolenaar } else { 3018d1a0d267SMarcel Moolenaar /* Don't do a gettext() look up, just get the plural form */ 3019d1a0d267SMarcel Moolenaar newstr = (n == 1) ? cp : two; 3020d1a0d267SMarcel Moolenaar } 3021d1a0d267SMarcel Moolenaar 3022d1a0d267SMarcel Moolenaar /* 3023d1a0d267SMarcel Moolenaar * If we returned the first string, optimize a bit by 3024d1a0d267SMarcel Moolenaar * backing up over comma 3025d1a0d267SMarcel Moolenaar */ 3026d1a0d267SMarcel Moolenaar if (newstr == cp) { 3027d1a0d267SMarcel Moolenaar xbp->xb_curp = two - 1; /* One for comma */ 3028d1a0d267SMarcel Moolenaar /* 3029d1a0d267SMarcel Moolenaar * If the caller wanted UTF8, we're done; nothing changed, 3030d1a0d267SMarcel Moolenaar * but we need to count the columns used. 3031d1a0d267SMarcel Moolenaar */ 3032d1a0d267SMarcel Moolenaar if (need_enc == XF_ENC_UTF8) 3033d1a0d267SMarcel Moolenaar return xo_count_utf8_cols(cp, xbp->xb_curp - cp); 3034d1a0d267SMarcel Moolenaar } 3035d1a0d267SMarcel Moolenaar 3036d1a0d267SMarcel Moolenaar } else { 3037d1a0d267SMarcel Moolenaar /* The simple case (singular) */ 3038d1a0d267SMarcel Moolenaar newstr = xo_dgettext(xop, cp); 3039d1a0d267SMarcel Moolenaar 3040d1a0d267SMarcel Moolenaar if (newstr == cp) { 3041d1a0d267SMarcel Moolenaar /* If the caller wanted UTF8, we're done; nothing changed */ 3042d1a0d267SMarcel Moolenaar if (need_enc == XF_ENC_UTF8) 3043d1a0d267SMarcel Moolenaar return cols; 3044d1a0d267SMarcel Moolenaar } 3045d1a0d267SMarcel Moolenaar } 3046d1a0d267SMarcel Moolenaar 3047d1a0d267SMarcel Moolenaar /* 3048d1a0d267SMarcel Moolenaar * Since the new string string might be in gettext's buffer or 3049d1a0d267SMarcel Moolenaar * in the buffer (as the plural form), we make a copy. 3050d1a0d267SMarcel Moolenaar */ 30518a6eceffSPhil Shafer ssize_t nlen = strlen(newstr); 3052d1a0d267SMarcel Moolenaar char *newcopy = alloca(nlen + 1); 3053d1a0d267SMarcel Moolenaar memcpy(newcopy, newstr, nlen + 1); 3054d1a0d267SMarcel Moolenaar 3055d1a0d267SMarcel Moolenaar xbp->xb_curp = xbp->xb_bufp + start_offset; /* Reset the buffer */ 3056d1a0d267SMarcel Moolenaar return xo_format_string_direct(xop, xbp, flags, NULL, newcopy, nlen, 0, 3057d1a0d267SMarcel Moolenaar need_enc, XF_ENC_UTF8); 3058d1a0d267SMarcel Moolenaar } 3059d1a0d267SMarcel Moolenaar 306031337658SMarcel Moolenaar static void 30618a6eceffSPhil Shafer xo_data_append_content (xo_handle_t *xop, const char *str, ssize_t len, 3062d1a0d267SMarcel Moolenaar xo_xff_flags_t flags) 306331337658SMarcel Moolenaar { 306431337658SMarcel Moolenaar int cols; 3065d1a0d267SMarcel Moolenaar int need_enc = xo_needed_encoding(xop); 30668a6eceffSPhil Shafer ssize_t start_offset = xo_buf_offset(&xop->xo_data); 306731337658SMarcel Moolenaar 3068d1a0d267SMarcel Moolenaar cols = xo_format_string_direct(xop, &xop->xo_data, XFF_UNESCAPE | flags, 306931337658SMarcel Moolenaar NULL, str, len, -1, 307031337658SMarcel Moolenaar need_enc, XF_ENC_UTF8); 3071d1a0d267SMarcel Moolenaar if (flags & XFF_GT_FLAGS) 3072d1a0d267SMarcel Moolenaar cols = xo_format_gettext(xop, flags, start_offset, cols, need_enc); 307331337658SMarcel Moolenaar 3074d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_COLUMNS)) 307531337658SMarcel Moolenaar xop->xo_columns += cols; 3076d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_ANCHOR)) 307731337658SMarcel Moolenaar xop->xo_anchor_columns += cols; 307831337658SMarcel Moolenaar } 307931337658SMarcel Moolenaar 308031337658SMarcel Moolenaar static void 308131337658SMarcel Moolenaar xo_bump_width (xo_format_t *xfp, int digit) 308231337658SMarcel Moolenaar { 308331337658SMarcel Moolenaar int *ip = &xfp->xf_width[xfp->xf_dots]; 308431337658SMarcel Moolenaar 308531337658SMarcel Moolenaar *ip = ((*ip > 0) ? *ip : 0) * 10 + digit; 308631337658SMarcel Moolenaar } 308731337658SMarcel Moolenaar 30888a6eceffSPhil Shafer static ssize_t 30898a6eceffSPhil Shafer xo_trim_ws (xo_buffer_t *xbp, ssize_t len) 309031337658SMarcel Moolenaar { 309131337658SMarcel Moolenaar char *cp, *sp, *ep; 30928a6eceffSPhil Shafer ssize_t delta; 309331337658SMarcel Moolenaar 309431337658SMarcel Moolenaar /* First trim leading space */ 309531337658SMarcel Moolenaar for (cp = sp = xbp->xb_curp, ep = cp + len; cp < ep; cp++) { 309631337658SMarcel Moolenaar if (*cp != ' ') 309731337658SMarcel Moolenaar break; 309831337658SMarcel Moolenaar } 309931337658SMarcel Moolenaar 310031337658SMarcel Moolenaar delta = cp - sp; 310131337658SMarcel Moolenaar if (delta) { 310231337658SMarcel Moolenaar len -= delta; 310331337658SMarcel Moolenaar memmove(sp, cp, len); 310431337658SMarcel Moolenaar } 310531337658SMarcel Moolenaar 310631337658SMarcel Moolenaar /* Then trim off the end */ 310731337658SMarcel Moolenaar for (cp = xbp->xb_curp, sp = ep = cp + len; cp < ep; ep--) { 310831337658SMarcel Moolenaar if (ep[-1] != ' ') 310931337658SMarcel Moolenaar break; 311031337658SMarcel Moolenaar } 311131337658SMarcel Moolenaar 311231337658SMarcel Moolenaar delta = sp - ep; 311331337658SMarcel Moolenaar if (delta) { 311431337658SMarcel Moolenaar len -= delta; 311531337658SMarcel Moolenaar cp[len] = '\0'; 311631337658SMarcel Moolenaar } 311731337658SMarcel Moolenaar 311831337658SMarcel Moolenaar return len; 311931337658SMarcel Moolenaar } 312031337658SMarcel Moolenaar 3121d1a0d267SMarcel Moolenaar /* 3122d1a0d267SMarcel Moolenaar * Interface to format a single field. The arguments are in xo_vap, 3123d1a0d267SMarcel Moolenaar * and the format is in 'fmt'. If 'xbp' is null, we use xop->xo_data; 3124d1a0d267SMarcel Moolenaar * this is the most common case. 3125d1a0d267SMarcel Moolenaar */ 31268a6eceffSPhil Shafer static ssize_t 3127d1a0d267SMarcel Moolenaar xo_do_format_field (xo_handle_t *xop, xo_buffer_t *xbp, 31288a6eceffSPhil Shafer const char *fmt, ssize_t flen, xo_xff_flags_t flags) 312931337658SMarcel Moolenaar { 313031337658SMarcel Moolenaar xo_format_t xf; 313131337658SMarcel Moolenaar const char *cp, *ep, *sp, *xp = NULL; 31328a6eceffSPhil Shafer ssize_t rc, cols; 3133788ca347SMarcel Moolenaar int style = (flags & XFF_XML) ? XO_STYLE_XML : xo_style(xop); 31348a6eceffSPhil Shafer unsigned make_output = !(flags & XFF_NO_OUTPUT) ? 1 : 0; 3135d1a0d267SMarcel Moolenaar int need_enc = xo_needed_encoding(xop); 3136d1a0d267SMarcel Moolenaar int real_need_enc = need_enc; 31378a6eceffSPhil Shafer ssize_t old_cols = xop->xo_columns; 3138d1a0d267SMarcel Moolenaar 3139d1a0d267SMarcel Moolenaar /* The gettext interface is UTF-8, so we'll need that for now */ 3140d1a0d267SMarcel Moolenaar if (flags & XFF_GT_FIELD) 3141d1a0d267SMarcel Moolenaar need_enc = XF_ENC_UTF8; 314231337658SMarcel Moolenaar 314331337658SMarcel Moolenaar if (xbp == NULL) 314431337658SMarcel Moolenaar xbp = &xop->xo_data; 314531337658SMarcel Moolenaar 31468a6eceffSPhil Shafer ssize_t start_offset = xo_buf_offset(xbp); 3147d1a0d267SMarcel Moolenaar 314831337658SMarcel Moolenaar for (cp = fmt, ep = fmt + flen; cp < ep; cp++) { 3149d1a0d267SMarcel Moolenaar /* 3150d1a0d267SMarcel Moolenaar * Since we're starting a new field, save the starting offset. 3151d1a0d267SMarcel Moolenaar * We'll need this later for field-related operations. 3152d1a0d267SMarcel Moolenaar */ 3153d1a0d267SMarcel Moolenaar 315431337658SMarcel Moolenaar if (*cp != '%') { 315531337658SMarcel Moolenaar add_one: 315631337658SMarcel Moolenaar if (xp == NULL) 315731337658SMarcel Moolenaar xp = cp; 315831337658SMarcel Moolenaar 315931337658SMarcel Moolenaar if (*cp == '\\' && cp[1] != '\0') 316031337658SMarcel Moolenaar cp += 1; 316131337658SMarcel Moolenaar continue; 316231337658SMarcel Moolenaar 316331337658SMarcel Moolenaar } if (cp + 1 < ep && cp[1] == '%') { 316431337658SMarcel Moolenaar cp += 1; 316531337658SMarcel Moolenaar goto add_one; 316631337658SMarcel Moolenaar } 316731337658SMarcel Moolenaar 316831337658SMarcel Moolenaar if (xp) { 316931337658SMarcel Moolenaar if (make_output) { 317031337658SMarcel Moolenaar cols = xo_format_string_direct(xop, xbp, flags | XFF_UNESCAPE, 317131337658SMarcel Moolenaar NULL, xp, cp - xp, -1, 317231337658SMarcel Moolenaar need_enc, XF_ENC_UTF8); 3173d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_COLUMNS)) 317431337658SMarcel Moolenaar xop->xo_columns += cols; 3175d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_ANCHOR)) 317631337658SMarcel Moolenaar xop->xo_anchor_columns += cols; 317731337658SMarcel Moolenaar } 317831337658SMarcel Moolenaar 317931337658SMarcel Moolenaar xp = NULL; 318031337658SMarcel Moolenaar } 318131337658SMarcel Moolenaar 318231337658SMarcel Moolenaar bzero(&xf, sizeof(xf)); 318331337658SMarcel Moolenaar xf.xf_leading_zero = -1; 318431337658SMarcel Moolenaar xf.xf_width[0] = xf.xf_width[1] = xf.xf_width[2] = -1; 318531337658SMarcel Moolenaar 318631337658SMarcel Moolenaar /* 318731337658SMarcel Moolenaar * "%@" starts an XO-specific set of flags: 318831337658SMarcel Moolenaar * @X@ - XML-only field; ignored if style isn't XML 318931337658SMarcel Moolenaar */ 319031337658SMarcel Moolenaar if (cp[1] == '@') { 319131337658SMarcel Moolenaar for (cp += 2; cp < ep; cp++) { 319231337658SMarcel Moolenaar if (*cp == '@') { 319331337658SMarcel Moolenaar break; 319431337658SMarcel Moolenaar } 319531337658SMarcel Moolenaar if (*cp == '*') { 319631337658SMarcel Moolenaar /* 319731337658SMarcel Moolenaar * '*' means there's a "%*.*s" value in vap that 319831337658SMarcel Moolenaar * we want to ignore 319931337658SMarcel Moolenaar */ 3200d1a0d267SMarcel Moolenaar if (!XOF_ISSET(xop, XOF_NO_VA_ARG)) 320131337658SMarcel Moolenaar va_arg(xop->xo_vap, int); 320231337658SMarcel Moolenaar } 320331337658SMarcel Moolenaar } 320431337658SMarcel Moolenaar } 320531337658SMarcel Moolenaar 320631337658SMarcel Moolenaar /* Hidden fields are only visible to JSON and XML */ 3207d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XFF_ENCODE_ONLY)) { 320831337658SMarcel Moolenaar if (style != XO_STYLE_XML 3209d1a0d267SMarcel Moolenaar && !xo_style_is_encoding(xop)) 321031337658SMarcel Moolenaar xf.xf_skip = 1; 3211d1a0d267SMarcel Moolenaar } else if (XOF_ISSET(xop, XFF_DISPLAY_ONLY)) { 321231337658SMarcel Moolenaar if (style != XO_STYLE_TEXT 3213788ca347SMarcel Moolenaar && xo_style(xop) != XO_STYLE_HTML) 321431337658SMarcel Moolenaar xf.xf_skip = 1; 321531337658SMarcel Moolenaar } 321631337658SMarcel Moolenaar 321731337658SMarcel Moolenaar if (!make_output) 321831337658SMarcel Moolenaar xf.xf_skip = 1; 321931337658SMarcel Moolenaar 322031337658SMarcel Moolenaar /* 322131337658SMarcel Moolenaar * Looking at one piece of a format; find the end and 322231337658SMarcel Moolenaar * call snprintf. Then advance xo_vap on our own. 322331337658SMarcel Moolenaar * 322431337658SMarcel Moolenaar * Note that 'n', 'v', and '$' are not supported. 322531337658SMarcel Moolenaar */ 322631337658SMarcel Moolenaar sp = cp; /* Save start pointer */ 322731337658SMarcel Moolenaar for (cp += 1; cp < ep; cp++) { 322831337658SMarcel Moolenaar if (*cp == 'l') 322931337658SMarcel Moolenaar xf.xf_lflag += 1; 323031337658SMarcel Moolenaar else if (*cp == 'h') 323131337658SMarcel Moolenaar xf.xf_hflag += 1; 323231337658SMarcel Moolenaar else if (*cp == 'j') 323331337658SMarcel Moolenaar xf.xf_jflag += 1; 323431337658SMarcel Moolenaar else if (*cp == 't') 323531337658SMarcel Moolenaar xf.xf_tflag += 1; 323631337658SMarcel Moolenaar else if (*cp == 'z') 323731337658SMarcel Moolenaar xf.xf_zflag += 1; 323831337658SMarcel Moolenaar else if (*cp == 'q') 323931337658SMarcel Moolenaar xf.xf_qflag += 1; 324031337658SMarcel Moolenaar else if (*cp == '.') { 324131337658SMarcel Moolenaar if (++xf.xf_dots >= XF_WIDTH_NUM) { 324231337658SMarcel Moolenaar xo_failure(xop, "Too many dots in format: '%s'", fmt); 324331337658SMarcel Moolenaar return -1; 324431337658SMarcel Moolenaar } 324531337658SMarcel Moolenaar } else if (*cp == '-') 324631337658SMarcel Moolenaar xf.xf_seen_minus = 1; 324731337658SMarcel Moolenaar else if (isdigit((int) *cp)) { 324831337658SMarcel Moolenaar if (xf.xf_leading_zero < 0) 324931337658SMarcel Moolenaar xf.xf_leading_zero = (*cp == '0'); 325031337658SMarcel Moolenaar xo_bump_width(&xf, *cp - '0'); 325131337658SMarcel Moolenaar } else if (*cp == '*') { 325231337658SMarcel Moolenaar xf.xf_stars += 1; 325331337658SMarcel Moolenaar xf.xf_star[xf.xf_dots] = 1; 3254d1a0d267SMarcel Moolenaar } else if (strchr("diouxXDOUeEfFgGaAcCsSpm", *cp) != NULL) 325531337658SMarcel Moolenaar break; 325631337658SMarcel Moolenaar else if (*cp == 'n' || *cp == 'v') { 325731337658SMarcel Moolenaar xo_failure(xop, "unsupported format: '%s'", fmt); 325831337658SMarcel Moolenaar return -1; 325931337658SMarcel Moolenaar } 326031337658SMarcel Moolenaar } 326131337658SMarcel Moolenaar 326231337658SMarcel Moolenaar if (cp == ep) 326331337658SMarcel Moolenaar xo_failure(xop, "field format missing format character: %s", 326431337658SMarcel Moolenaar fmt); 326531337658SMarcel Moolenaar 326631337658SMarcel Moolenaar xf.xf_fc = *cp; 326731337658SMarcel Moolenaar 3268d1a0d267SMarcel Moolenaar if (!XOF_ISSET(xop, XOF_NO_VA_ARG)) { 326931337658SMarcel Moolenaar if (*cp == 's' || *cp == 'S') { 327031337658SMarcel Moolenaar /* Handle "%*.*.*s" */ 327131337658SMarcel Moolenaar int s; 327231337658SMarcel Moolenaar for (s = 0; s < XF_WIDTH_NUM; s++) { 327331337658SMarcel Moolenaar if (xf.xf_star[s]) { 327431337658SMarcel Moolenaar xf.xf_width[s] = va_arg(xop->xo_vap, int); 327531337658SMarcel Moolenaar 327631337658SMarcel Moolenaar /* Normalize a negative width value */ 327731337658SMarcel Moolenaar if (xf.xf_width[s] < 0) { 327831337658SMarcel Moolenaar if (s == 0) { 327931337658SMarcel Moolenaar xf.xf_width[0] = -xf.xf_width[0]; 328031337658SMarcel Moolenaar xf.xf_seen_minus = 1; 328131337658SMarcel Moolenaar } else 328231337658SMarcel Moolenaar xf.xf_width[s] = -1; /* Ignore negative values */ 328331337658SMarcel Moolenaar } 328431337658SMarcel Moolenaar } 328531337658SMarcel Moolenaar } 328631337658SMarcel Moolenaar } 328731337658SMarcel Moolenaar } 328831337658SMarcel Moolenaar 328931337658SMarcel Moolenaar /* If no max is given, it defaults to size */ 329031337658SMarcel Moolenaar if (xf.xf_width[XF_WIDTH_MAX] < 0 && xf.xf_width[XF_WIDTH_SIZE] >= 0) 329131337658SMarcel Moolenaar xf.xf_width[XF_WIDTH_MAX] = xf.xf_width[XF_WIDTH_SIZE]; 329231337658SMarcel Moolenaar 329331337658SMarcel Moolenaar if (xf.xf_fc == 'D' || xf.xf_fc == 'O' || xf.xf_fc == 'U') 329431337658SMarcel Moolenaar xf.xf_lflag = 1; 329531337658SMarcel Moolenaar 329631337658SMarcel Moolenaar if (!xf.xf_skip) { 329731337658SMarcel Moolenaar xo_buffer_t *fbp = &xop->xo_fmt; 32988a6eceffSPhil Shafer ssize_t len = cp - sp + 1; 329931337658SMarcel Moolenaar if (!xo_buf_has_room(fbp, len + 1)) 330031337658SMarcel Moolenaar return -1; 330131337658SMarcel Moolenaar 330231337658SMarcel Moolenaar char *newfmt = fbp->xb_curp; 330331337658SMarcel Moolenaar memcpy(newfmt, sp, len); 330431337658SMarcel Moolenaar newfmt[0] = '%'; /* If we skipped over a "%@...@s" format */ 330531337658SMarcel Moolenaar newfmt[len] = '\0'; 330631337658SMarcel Moolenaar 330731337658SMarcel Moolenaar /* 330831337658SMarcel Moolenaar * Bad news: our strings are UTF-8, but the stock printf 330931337658SMarcel Moolenaar * functions won't handle field widths for wide characters 331031337658SMarcel Moolenaar * correctly. So we have to handle this ourselves. 331131337658SMarcel Moolenaar */ 331231337658SMarcel Moolenaar if (xop->xo_formatter == NULL 3313d1a0d267SMarcel Moolenaar && (xf.xf_fc == 's' || xf.xf_fc == 'S' 3314d1a0d267SMarcel Moolenaar || xf.xf_fc == 'm')) { 3315d1a0d267SMarcel Moolenaar 3316d1a0d267SMarcel Moolenaar xf.xf_enc = (xf.xf_fc == 'm') ? XF_ENC_UTF8 3317d1a0d267SMarcel Moolenaar : (xf.xf_lflag || (xf.xf_fc == 'S')) ? XF_ENC_WIDE 3318d1a0d267SMarcel Moolenaar : xf.xf_hflag ? XF_ENC_LOCALE : XF_ENC_UTF8; 3319d1a0d267SMarcel Moolenaar 332031337658SMarcel Moolenaar rc = xo_format_string(xop, xbp, flags, &xf); 332131337658SMarcel Moolenaar 3322d1a0d267SMarcel Moolenaar if ((flags & XFF_TRIM_WS) && xo_style_is_encoding(xop)) 332331337658SMarcel Moolenaar rc = xo_trim_ws(xbp, rc); 332431337658SMarcel Moolenaar 332531337658SMarcel Moolenaar } else { 33268a6eceffSPhil Shafer ssize_t columns = rc = xo_vsnprintf(xop, xbp, newfmt, xop->xo_vap); 332731337658SMarcel Moolenaar 332831337658SMarcel Moolenaar /* 332931337658SMarcel Moolenaar * For XML and HTML, we need "&<>" processing; for JSON, 333031337658SMarcel Moolenaar * it's quotes. Text gets nothing. 333131337658SMarcel Moolenaar */ 333231337658SMarcel Moolenaar switch (style) { 333331337658SMarcel Moolenaar case XO_STYLE_XML: 333431337658SMarcel Moolenaar if (flags & XFF_TRIM_WS) 333531337658SMarcel Moolenaar columns = rc = xo_trim_ws(xbp, rc); 3336ee5cf116SPhil Shafer /* FALLTHRU */ 333731337658SMarcel Moolenaar case XO_STYLE_HTML: 333831337658SMarcel Moolenaar rc = xo_escape_xml(xbp, rc, (flags & XFF_ATTR)); 333931337658SMarcel Moolenaar break; 334031337658SMarcel Moolenaar 334131337658SMarcel Moolenaar case XO_STYLE_JSON: 334231337658SMarcel Moolenaar if (flags & XFF_TRIM_WS) 334331337658SMarcel Moolenaar columns = rc = xo_trim_ws(xbp, rc); 3344d1a0d267SMarcel Moolenaar rc = xo_escape_json(xbp, rc, 0); 3345d1a0d267SMarcel Moolenaar break; 3346d1a0d267SMarcel Moolenaar 3347d1a0d267SMarcel Moolenaar case XO_STYLE_SDPARAMS: 3348d1a0d267SMarcel Moolenaar if (flags & XFF_TRIM_WS) 3349d1a0d267SMarcel Moolenaar columns = rc = xo_trim_ws(xbp, rc); 3350d1a0d267SMarcel Moolenaar rc = xo_escape_sdparams(xbp, rc, 0); 3351d1a0d267SMarcel Moolenaar break; 3352d1a0d267SMarcel Moolenaar 3353d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER: 3354d1a0d267SMarcel Moolenaar if (flags & XFF_TRIM_WS) 3355d1a0d267SMarcel Moolenaar columns = rc = xo_trim_ws(xbp, rc); 335631337658SMarcel Moolenaar break; 335731337658SMarcel Moolenaar } 335831337658SMarcel Moolenaar 335931337658SMarcel Moolenaar /* 3360d1a0d267SMarcel Moolenaar * We can assume all the non-%s data we've 3361d1a0d267SMarcel Moolenaar * added is ASCII, so the columns and bytes are the 3362d1a0d267SMarcel Moolenaar * same. xo_format_string handles all the fancy 3363d1a0d267SMarcel Moolenaar * string conversions and updates xo_anchor_columns 3364d1a0d267SMarcel Moolenaar * accordingly. 336531337658SMarcel Moolenaar */ 3366d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_COLUMNS)) 336731337658SMarcel Moolenaar xop->xo_columns += columns; 3368d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_ANCHOR)) 336931337658SMarcel Moolenaar xop->xo_anchor_columns += columns; 337031337658SMarcel Moolenaar } 337131337658SMarcel Moolenaar 337231337658SMarcel Moolenaar xbp->xb_curp += rc; 337331337658SMarcel Moolenaar } 337431337658SMarcel Moolenaar 337531337658SMarcel Moolenaar /* 337631337658SMarcel Moolenaar * Now for the tricky part: we need to move the argument pointer 337731337658SMarcel Moolenaar * along by the amount needed. 337831337658SMarcel Moolenaar */ 3379d1a0d267SMarcel Moolenaar if (!XOF_ISSET(xop, XOF_NO_VA_ARG)) { 338031337658SMarcel Moolenaar 338131337658SMarcel Moolenaar if (xf.xf_fc == 's' ||xf.xf_fc == 'S') { 338231337658SMarcel Moolenaar /* 338331337658SMarcel Moolenaar * The 'S' and 's' formats are normally handled in 338431337658SMarcel Moolenaar * xo_format_string, but if we skipped it, then we 338531337658SMarcel Moolenaar * need to pop it. 338631337658SMarcel Moolenaar */ 338731337658SMarcel Moolenaar if (xf.xf_skip) 338831337658SMarcel Moolenaar va_arg(xop->xo_vap, char *); 338931337658SMarcel Moolenaar 3390d1a0d267SMarcel Moolenaar } else if (xf.xf_fc == 'm') { 3391d1a0d267SMarcel Moolenaar /* Nothing on the stack for "%m" */ 3392d1a0d267SMarcel Moolenaar 339331337658SMarcel Moolenaar } else { 339431337658SMarcel Moolenaar int s; 339531337658SMarcel Moolenaar for (s = 0; s < XF_WIDTH_NUM; s++) { 339631337658SMarcel Moolenaar if (xf.xf_star[s]) 339731337658SMarcel Moolenaar va_arg(xop->xo_vap, int); 339831337658SMarcel Moolenaar } 339931337658SMarcel Moolenaar 340031337658SMarcel Moolenaar if (strchr("diouxXDOU", xf.xf_fc) != NULL) { 340131337658SMarcel Moolenaar if (xf.xf_hflag > 1) { 340231337658SMarcel Moolenaar va_arg(xop->xo_vap, int); 340331337658SMarcel Moolenaar 340431337658SMarcel Moolenaar } else if (xf.xf_hflag > 0) { 340531337658SMarcel Moolenaar va_arg(xop->xo_vap, int); 340631337658SMarcel Moolenaar 340731337658SMarcel Moolenaar } else if (xf.xf_lflag > 1) { 340831337658SMarcel Moolenaar va_arg(xop->xo_vap, unsigned long long); 340931337658SMarcel Moolenaar 341031337658SMarcel Moolenaar } else if (xf.xf_lflag > 0) { 341131337658SMarcel Moolenaar va_arg(xop->xo_vap, unsigned long); 341231337658SMarcel Moolenaar 341331337658SMarcel Moolenaar } else if (xf.xf_jflag > 0) { 341431337658SMarcel Moolenaar va_arg(xop->xo_vap, intmax_t); 341531337658SMarcel Moolenaar 341631337658SMarcel Moolenaar } else if (xf.xf_tflag > 0) { 341731337658SMarcel Moolenaar va_arg(xop->xo_vap, ptrdiff_t); 341831337658SMarcel Moolenaar 341931337658SMarcel Moolenaar } else if (xf.xf_zflag > 0) { 342031337658SMarcel Moolenaar va_arg(xop->xo_vap, size_t); 342131337658SMarcel Moolenaar 342231337658SMarcel Moolenaar } else if (xf.xf_qflag > 0) { 342331337658SMarcel Moolenaar va_arg(xop->xo_vap, quad_t); 342431337658SMarcel Moolenaar 342531337658SMarcel Moolenaar } else { 342631337658SMarcel Moolenaar va_arg(xop->xo_vap, int); 342731337658SMarcel Moolenaar } 342831337658SMarcel Moolenaar } else if (strchr("eEfFgGaA", xf.xf_fc) != NULL) 342931337658SMarcel Moolenaar if (xf.xf_lflag) 343031337658SMarcel Moolenaar va_arg(xop->xo_vap, long double); 343131337658SMarcel Moolenaar else 343231337658SMarcel Moolenaar va_arg(xop->xo_vap, double); 343331337658SMarcel Moolenaar 343431337658SMarcel Moolenaar else if (xf.xf_fc == 'C' || (xf.xf_fc == 'c' && xf.xf_lflag)) 343531337658SMarcel Moolenaar va_arg(xop->xo_vap, wint_t); 343631337658SMarcel Moolenaar 343731337658SMarcel Moolenaar else if (xf.xf_fc == 'c') 343831337658SMarcel Moolenaar va_arg(xop->xo_vap, int); 343931337658SMarcel Moolenaar 344031337658SMarcel Moolenaar else if (xf.xf_fc == 'p') 344131337658SMarcel Moolenaar va_arg(xop->xo_vap, void *); 344231337658SMarcel Moolenaar } 344331337658SMarcel Moolenaar } 344431337658SMarcel Moolenaar } 344531337658SMarcel Moolenaar 344631337658SMarcel Moolenaar if (xp) { 344731337658SMarcel Moolenaar if (make_output) { 344831337658SMarcel Moolenaar cols = xo_format_string_direct(xop, xbp, flags | XFF_UNESCAPE, 344931337658SMarcel Moolenaar NULL, xp, cp - xp, -1, 345031337658SMarcel Moolenaar need_enc, XF_ENC_UTF8); 3451d1a0d267SMarcel Moolenaar 3452d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_COLUMNS)) 345331337658SMarcel Moolenaar xop->xo_columns += cols; 3454d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_ANCHOR)) 345531337658SMarcel Moolenaar xop->xo_anchor_columns += cols; 345631337658SMarcel Moolenaar } 345731337658SMarcel Moolenaar 345831337658SMarcel Moolenaar xp = NULL; 345931337658SMarcel Moolenaar } 346031337658SMarcel Moolenaar 3461d1a0d267SMarcel Moolenaar if (flags & XFF_GT_FLAGS) { 3462d1a0d267SMarcel Moolenaar /* 3463d1a0d267SMarcel Moolenaar * Handle gettext()ing the field by looking up the value 3464d1a0d267SMarcel Moolenaar * and then copying it in, while converting to locale, if 3465d1a0d267SMarcel Moolenaar * needed. 3466d1a0d267SMarcel Moolenaar */ 34678a6eceffSPhil Shafer ssize_t new_cols = xo_format_gettext(xop, flags, start_offset, 3468d1a0d267SMarcel Moolenaar old_cols, real_need_enc); 3469d1a0d267SMarcel Moolenaar 3470d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_COLUMNS)) 3471d1a0d267SMarcel Moolenaar xop->xo_columns += new_cols - old_cols; 3472d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_ANCHOR)) 3473d1a0d267SMarcel Moolenaar xop->xo_anchor_columns += new_cols - old_cols; 3474d1a0d267SMarcel Moolenaar } 3475d1a0d267SMarcel Moolenaar 347631337658SMarcel Moolenaar return 0; 347731337658SMarcel Moolenaar } 347831337658SMarcel Moolenaar 347931337658SMarcel Moolenaar static char * 348031337658SMarcel Moolenaar xo_fix_encoding (xo_handle_t *xop UNUSED, char *encoding) 348131337658SMarcel Moolenaar { 348231337658SMarcel Moolenaar char *cp = encoding; 348331337658SMarcel Moolenaar 348431337658SMarcel Moolenaar if (cp[0] != '%' || !isdigit((int) cp[1])) 348531337658SMarcel Moolenaar return encoding; 348631337658SMarcel Moolenaar 348731337658SMarcel Moolenaar for (cp += 2; *cp; cp++) { 348831337658SMarcel Moolenaar if (!isdigit((int) *cp)) 348931337658SMarcel Moolenaar break; 349031337658SMarcel Moolenaar } 349131337658SMarcel Moolenaar 349231337658SMarcel Moolenaar cp -= 1; 349331337658SMarcel Moolenaar *cp = '%'; 349431337658SMarcel Moolenaar 349531337658SMarcel Moolenaar return cp; 349631337658SMarcel Moolenaar } 349731337658SMarcel Moolenaar 349831337658SMarcel Moolenaar static void 3499788ca347SMarcel Moolenaar xo_color_append_html (xo_handle_t *xop) 3500788ca347SMarcel Moolenaar { 3501788ca347SMarcel Moolenaar /* 3502788ca347SMarcel Moolenaar * If the color buffer has content, we add it now. It's already 3503788ca347SMarcel Moolenaar * prebuilt and ready, since we want to add it to every <div>. 3504788ca347SMarcel Moolenaar */ 3505788ca347SMarcel Moolenaar if (!xo_buf_is_empty(&xop->xo_color_buf)) { 3506788ca347SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_color_buf; 3507788ca347SMarcel Moolenaar 3508788ca347SMarcel Moolenaar xo_data_append(xop, xbp->xb_bufp, xbp->xb_curp - xbp->xb_bufp); 3509788ca347SMarcel Moolenaar } 3510788ca347SMarcel Moolenaar } 3511788ca347SMarcel Moolenaar 3512d1a0d267SMarcel Moolenaar /* 3513d1a0d267SMarcel Moolenaar * A wrapper for humanize_number that autoscales, since the 3514d1a0d267SMarcel Moolenaar * HN_AUTOSCALE flag scales as needed based on the size of 3515d1a0d267SMarcel Moolenaar * the output buffer, not the size of the value. I also 3516d1a0d267SMarcel Moolenaar * wish HN_DECIMAL was more imperative, without the <10 3517d1a0d267SMarcel Moolenaar * test. But the boat only goes where we want when we hold 3518d1a0d267SMarcel Moolenaar * the rudder, so xo_humanize fixes part of the problem. 3519d1a0d267SMarcel Moolenaar */ 35208a6eceffSPhil Shafer static ssize_t 35218a6eceffSPhil Shafer xo_humanize (char *buf, ssize_t len, uint64_t value, int flags) 3522d1a0d267SMarcel Moolenaar { 3523d1a0d267SMarcel Moolenaar int scale = 0; 3524d1a0d267SMarcel Moolenaar 3525d1a0d267SMarcel Moolenaar if (value) { 3526d1a0d267SMarcel Moolenaar uint64_t left = value; 3527d1a0d267SMarcel Moolenaar 3528d1a0d267SMarcel Moolenaar if (flags & HN_DIVISOR_1000) { 3529d1a0d267SMarcel Moolenaar for ( ; left; scale++) 3530d1a0d267SMarcel Moolenaar left /= 1000; 3531d1a0d267SMarcel Moolenaar } else { 3532d1a0d267SMarcel Moolenaar for ( ; left; scale++) 3533d1a0d267SMarcel Moolenaar left /= 1024; 3534d1a0d267SMarcel Moolenaar } 3535d1a0d267SMarcel Moolenaar scale -= 1; 3536d1a0d267SMarcel Moolenaar } 3537d1a0d267SMarcel Moolenaar 3538d1a0d267SMarcel Moolenaar return xo_humanize_number(buf, len, value, "", scale, flags); 3539d1a0d267SMarcel Moolenaar } 3540d1a0d267SMarcel Moolenaar 3541d1a0d267SMarcel Moolenaar /* 3542d1a0d267SMarcel Moolenaar * This is an area where we can save information from the handle for 3543d1a0d267SMarcel Moolenaar * later restoration. We need to know what data was rendered to know 3544d1a0d267SMarcel Moolenaar * what needs cleaned up. 3545d1a0d267SMarcel Moolenaar */ 3546d1a0d267SMarcel Moolenaar typedef struct xo_humanize_save_s { 35478a6eceffSPhil Shafer ssize_t xhs_offset; /* Saved xo_offset */ 35488a6eceffSPhil Shafer ssize_t xhs_columns; /* Saved xo_columns */ 35498a6eceffSPhil Shafer ssize_t xhs_anchor_columns; /* Saved xo_anchor_columns */ 3550d1a0d267SMarcel Moolenaar } xo_humanize_save_t; 3551d1a0d267SMarcel Moolenaar 3552d1a0d267SMarcel Moolenaar /* 3553d1a0d267SMarcel Moolenaar * Format a "humanized" value for a numeric, meaning something nice 3554d1a0d267SMarcel Moolenaar * like "44M" instead of "44470272". We autoscale, choosing the 3555d1a0d267SMarcel Moolenaar * most appropriate value for K/M/G/T/P/E based on the value given. 3556d1a0d267SMarcel Moolenaar */ 3557d1a0d267SMarcel Moolenaar static void 3558d1a0d267SMarcel Moolenaar xo_format_humanize (xo_handle_t *xop, xo_buffer_t *xbp, 3559d1a0d267SMarcel Moolenaar xo_humanize_save_t *savep, xo_xff_flags_t flags) 3560d1a0d267SMarcel Moolenaar { 3561d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_NO_HUMANIZE)) 3562d1a0d267SMarcel Moolenaar return; 3563d1a0d267SMarcel Moolenaar 35648a6eceffSPhil Shafer ssize_t end_offset = xbp->xb_curp - xbp->xb_bufp; 3565d1a0d267SMarcel Moolenaar if (end_offset == savep->xhs_offset) /* Huh? Nothing to render */ 3566d1a0d267SMarcel Moolenaar return; 3567d1a0d267SMarcel Moolenaar 3568d1a0d267SMarcel Moolenaar /* 3569d1a0d267SMarcel Moolenaar * We have a string that's allegedly a number. We want to 3570d1a0d267SMarcel Moolenaar * humanize it, which means turning it back into a number 3571d1a0d267SMarcel Moolenaar * and calling xo_humanize_number on it. 3572d1a0d267SMarcel Moolenaar */ 3573d1a0d267SMarcel Moolenaar uint64_t value; 3574d1a0d267SMarcel Moolenaar char *ep; 3575d1a0d267SMarcel Moolenaar 3576d1a0d267SMarcel Moolenaar xo_buf_append(xbp, "", 1); /* NUL-terminate it */ 3577d1a0d267SMarcel Moolenaar 3578d1a0d267SMarcel Moolenaar value = strtoull(xbp->xb_bufp + savep->xhs_offset, &ep, 0); 3579d1a0d267SMarcel Moolenaar if (!(value == ULLONG_MAX && errno == ERANGE) 3580d1a0d267SMarcel Moolenaar && (ep != xbp->xb_bufp + savep->xhs_offset)) { 3581d1a0d267SMarcel Moolenaar /* 3582d1a0d267SMarcel Moolenaar * There are few values where humanize_number needs 3583d1a0d267SMarcel Moolenaar * more bytes than the original value. I've used 3584d1a0d267SMarcel Moolenaar * 10 as a rectal number to cover those scenarios. 3585d1a0d267SMarcel Moolenaar */ 3586d1a0d267SMarcel Moolenaar if (xo_buf_has_room(xbp, 10)) { 3587d1a0d267SMarcel Moolenaar xbp->xb_curp = xbp->xb_bufp + savep->xhs_offset; 3588d1a0d267SMarcel Moolenaar 35898a6eceffSPhil Shafer ssize_t rc; 35908a6eceffSPhil Shafer ssize_t left = (xbp->xb_bufp + xbp->xb_size) - xbp->xb_curp; 3591d1a0d267SMarcel Moolenaar int hn_flags = HN_NOSPACE; /* On by default */ 3592d1a0d267SMarcel Moolenaar 3593d1a0d267SMarcel Moolenaar if (flags & XFF_HN_SPACE) 3594d1a0d267SMarcel Moolenaar hn_flags &= ~HN_NOSPACE; 3595d1a0d267SMarcel Moolenaar 3596d1a0d267SMarcel Moolenaar if (flags & XFF_HN_DECIMAL) 3597d1a0d267SMarcel Moolenaar hn_flags |= HN_DECIMAL; 3598d1a0d267SMarcel Moolenaar 3599d1a0d267SMarcel Moolenaar if (flags & XFF_HN_1000) 3600d1a0d267SMarcel Moolenaar hn_flags |= HN_DIVISOR_1000; 3601d1a0d267SMarcel Moolenaar 36028a6eceffSPhil Shafer rc = xo_humanize(xbp->xb_curp, left, value, hn_flags); 3603d1a0d267SMarcel Moolenaar if (rc > 0) { 3604d1a0d267SMarcel Moolenaar xbp->xb_curp += rc; 3605d1a0d267SMarcel Moolenaar xop->xo_columns = savep->xhs_columns + rc; 3606d1a0d267SMarcel Moolenaar xop->xo_anchor_columns = savep->xhs_anchor_columns + rc; 3607d1a0d267SMarcel Moolenaar } 3608d1a0d267SMarcel Moolenaar } 3609d1a0d267SMarcel Moolenaar } 3610d1a0d267SMarcel Moolenaar } 3611d1a0d267SMarcel Moolenaar 3612788ca347SMarcel Moolenaar static void 361331337658SMarcel Moolenaar xo_buf_append_div (xo_handle_t *xop, const char *class, xo_xff_flags_t flags, 36148a6eceffSPhil Shafer const char *name, ssize_t nlen, 36158a6eceffSPhil Shafer const char *value, ssize_t vlen, 36168a6eceffSPhil Shafer const char *encoding, ssize_t elen) 361731337658SMarcel Moolenaar { 361831337658SMarcel Moolenaar static char div_start[] = "<div class=\""; 361931337658SMarcel Moolenaar static char div_tag[] = "\" data-tag=\""; 362031337658SMarcel Moolenaar static char div_xpath[] = "\" data-xpath=\""; 362131337658SMarcel Moolenaar static char div_key[] = "\" data-key=\"key"; 362231337658SMarcel Moolenaar static char div_end[] = "\">"; 362331337658SMarcel Moolenaar static char div_close[] = "</div>"; 362431337658SMarcel Moolenaar 3625a321cc5dSPhil Shafer /* The encoding format defaults to the normal format */ 3626a321cc5dSPhil Shafer if (encoding == NULL) { 3627a321cc5dSPhil Shafer char *enc = alloca(vlen + 1); 3628a321cc5dSPhil Shafer memcpy(enc, value, vlen); 3629a321cc5dSPhil Shafer enc[vlen] = '\0'; 3630a321cc5dSPhil Shafer encoding = xo_fix_encoding(xop, enc); 3631a321cc5dSPhil Shafer elen = strlen(encoding); 3632a321cc5dSPhil Shafer } 3633a321cc5dSPhil Shafer 363431337658SMarcel Moolenaar /* 363531337658SMarcel Moolenaar * To build our XPath predicate, we need to save the va_list before 363631337658SMarcel Moolenaar * we format our data, and then restore it before we format the 363731337658SMarcel Moolenaar * xpath expression. 363831337658SMarcel Moolenaar * Display-only keys implies that we've got an encode-only key 363931337658SMarcel Moolenaar * elsewhere, so we don't use them from making predicates. 364031337658SMarcel Moolenaar */ 364131337658SMarcel Moolenaar int need_predidate = 364231337658SMarcel Moolenaar (name && (flags & XFF_KEY) && !(flags & XFF_DISPLAY_ONLY) 36438a6eceffSPhil Shafer && XOF_ISSET(xop, XOF_XPATH)) ? 1 : 0; 364431337658SMarcel Moolenaar 364531337658SMarcel Moolenaar if (need_predidate) { 364631337658SMarcel Moolenaar va_list va_local; 364731337658SMarcel Moolenaar 364831337658SMarcel Moolenaar va_copy(va_local, xop->xo_vap); 364931337658SMarcel Moolenaar if (xop->xo_checkpointer) 365031337658SMarcel Moolenaar xop->xo_checkpointer(xop, xop->xo_vap, 0); 365131337658SMarcel Moolenaar 365231337658SMarcel Moolenaar /* 365331337658SMarcel Moolenaar * Build an XPath predicate expression to match this key. 365431337658SMarcel Moolenaar * We use the format buffer. 365531337658SMarcel Moolenaar */ 365631337658SMarcel Moolenaar xo_buffer_t *pbp = &xop->xo_predicate; 365731337658SMarcel Moolenaar pbp->xb_curp = pbp->xb_bufp; /* Restart buffer */ 365831337658SMarcel Moolenaar 365931337658SMarcel Moolenaar xo_buf_append(pbp, "[", 1); 366031337658SMarcel Moolenaar xo_buf_escape(xop, pbp, name, nlen, 0); 3661d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_PRETTY)) 366231337658SMarcel Moolenaar xo_buf_append(pbp, " = '", 4); 366331337658SMarcel Moolenaar else 366431337658SMarcel Moolenaar xo_buf_append(pbp, "='", 2); 366531337658SMarcel Moolenaar 3666d1a0d267SMarcel Moolenaar xo_xff_flags_t pflags = flags | XFF_XML | XFF_ATTR; 3667d1a0d267SMarcel Moolenaar pflags &= ~(XFF_NO_OUTPUT | XFF_ENCODE_ONLY); 3668d1a0d267SMarcel Moolenaar xo_do_format_field(xop, pbp, encoding, elen, pflags); 366931337658SMarcel Moolenaar 367031337658SMarcel Moolenaar xo_buf_append(pbp, "']", 2); 367131337658SMarcel Moolenaar 367231337658SMarcel Moolenaar /* Now we record this predicate expression in the stack */ 367331337658SMarcel Moolenaar xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth]; 36748a6eceffSPhil Shafer ssize_t olen = xsp->xs_keys ? strlen(xsp->xs_keys) : 0; 36758a6eceffSPhil Shafer ssize_t dlen = pbp->xb_curp - pbp->xb_bufp; 367631337658SMarcel Moolenaar 367731337658SMarcel Moolenaar char *cp = xo_realloc(xsp->xs_keys, olen + dlen + 1); 367831337658SMarcel Moolenaar if (cp) { 367931337658SMarcel Moolenaar memcpy(cp + olen, pbp->xb_bufp, dlen); 368031337658SMarcel Moolenaar cp[olen + dlen] = '\0'; 368131337658SMarcel Moolenaar xsp->xs_keys = cp; 368231337658SMarcel Moolenaar } 368331337658SMarcel Moolenaar 368431337658SMarcel Moolenaar /* Now we reset the xo_vap as if we were never here */ 368531337658SMarcel Moolenaar va_end(xop->xo_vap); 368631337658SMarcel Moolenaar va_copy(xop->xo_vap, va_local); 368731337658SMarcel Moolenaar va_end(va_local); 368831337658SMarcel Moolenaar if (xop->xo_checkpointer) 368931337658SMarcel Moolenaar xop->xo_checkpointer(xop, xop->xo_vap, 1); 369031337658SMarcel Moolenaar } 369131337658SMarcel Moolenaar 369231337658SMarcel Moolenaar if (flags & XFF_ENCODE_ONLY) { 369331337658SMarcel Moolenaar /* 3694ee5cf116SPhil Shafer * Even if this is encode-only, we need to go through the 369531337658SMarcel Moolenaar * work of formatting it to make sure the args are cleared 369631337658SMarcel Moolenaar * from xo_vap. 369731337658SMarcel Moolenaar */ 3698d1a0d267SMarcel Moolenaar xo_do_format_field(xop, NULL, encoding, elen, 369931337658SMarcel Moolenaar flags | XFF_NO_OUTPUT); 370031337658SMarcel Moolenaar return; 370131337658SMarcel Moolenaar } 370231337658SMarcel Moolenaar 370331337658SMarcel Moolenaar xo_line_ensure_open(xop, 0); 370431337658SMarcel Moolenaar 3705d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_PRETTY)) 370631337658SMarcel Moolenaar xo_buf_indent(xop, xop->xo_indent_by); 370731337658SMarcel Moolenaar 370831337658SMarcel Moolenaar xo_data_append(xop, div_start, sizeof(div_start) - 1); 370931337658SMarcel Moolenaar xo_data_append(xop, class, strlen(class)); 371031337658SMarcel Moolenaar 3711788ca347SMarcel Moolenaar /* 3712788ca347SMarcel Moolenaar * If the color buffer has content, we add it now. It's already 3713788ca347SMarcel Moolenaar * prebuilt and ready, since we want to add it to every <div>. 3714788ca347SMarcel Moolenaar */ 3715788ca347SMarcel Moolenaar if (!xo_buf_is_empty(&xop->xo_color_buf)) { 3716788ca347SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_color_buf; 3717788ca347SMarcel Moolenaar 3718788ca347SMarcel Moolenaar xo_data_append(xop, xbp->xb_bufp, xbp->xb_curp - xbp->xb_bufp); 3719788ca347SMarcel Moolenaar } 3720788ca347SMarcel Moolenaar 372131337658SMarcel Moolenaar if (name) { 372231337658SMarcel Moolenaar xo_data_append(xop, div_tag, sizeof(div_tag) - 1); 372331337658SMarcel Moolenaar xo_data_escape(xop, name, nlen); 372431337658SMarcel Moolenaar 372531337658SMarcel Moolenaar /* 372631337658SMarcel Moolenaar * Save the offset at which we'd place units. See xo_format_units. 372731337658SMarcel Moolenaar */ 3728d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_UNITS)) { 3729d1a0d267SMarcel Moolenaar XOIF_SET(xop, XOIF_UNITS_PENDING); 373031337658SMarcel Moolenaar /* 373131337658SMarcel Moolenaar * Note: We need the '+1' here because we know we've not 373231337658SMarcel Moolenaar * added the closing quote. We add one, knowing the quote 373331337658SMarcel Moolenaar * will be added shortly. 373431337658SMarcel Moolenaar */ 373531337658SMarcel Moolenaar xop->xo_units_offset = 373631337658SMarcel Moolenaar xop->xo_data.xb_curp -xop->xo_data.xb_bufp + 1; 373731337658SMarcel Moolenaar } 373831337658SMarcel Moolenaar 3739d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_XPATH)) { 374031337658SMarcel Moolenaar int i; 374131337658SMarcel Moolenaar xo_stack_t *xsp; 374231337658SMarcel Moolenaar 374331337658SMarcel Moolenaar xo_data_append(xop, div_xpath, sizeof(div_xpath) - 1); 374431337658SMarcel Moolenaar if (xop->xo_leading_xpath) 374531337658SMarcel Moolenaar xo_data_append(xop, xop->xo_leading_xpath, 374631337658SMarcel Moolenaar strlen(xop->xo_leading_xpath)); 374731337658SMarcel Moolenaar 374831337658SMarcel Moolenaar for (i = 0; i <= xop->xo_depth; i++) { 374931337658SMarcel Moolenaar xsp = &xop->xo_stack[i]; 375031337658SMarcel Moolenaar if (xsp->xs_name == NULL) 375131337658SMarcel Moolenaar continue; 375231337658SMarcel Moolenaar 3753545ddfbeSMarcel Moolenaar /* 3754545ddfbeSMarcel Moolenaar * XSS_OPEN_LIST and XSS_OPEN_LEAF_LIST stack frames 3755545ddfbeSMarcel Moolenaar * are directly under XSS_OPEN_INSTANCE frames so we 3756545ddfbeSMarcel Moolenaar * don't need to put these in our XPath expressions. 3757545ddfbeSMarcel Moolenaar */ 3758545ddfbeSMarcel Moolenaar if (xsp->xs_state == XSS_OPEN_LIST 3759545ddfbeSMarcel Moolenaar || xsp->xs_state == XSS_OPEN_LEAF_LIST) 3760545ddfbeSMarcel Moolenaar continue; 3761545ddfbeSMarcel Moolenaar 376231337658SMarcel Moolenaar xo_data_append(xop, "/", 1); 376331337658SMarcel Moolenaar xo_data_escape(xop, xsp->xs_name, strlen(xsp->xs_name)); 376431337658SMarcel Moolenaar if (xsp->xs_keys) { 376531337658SMarcel Moolenaar /* Don't show keys for the key field */ 376631337658SMarcel Moolenaar if (i != xop->xo_depth || !(flags & XFF_KEY)) 376731337658SMarcel Moolenaar xo_data_append(xop, xsp->xs_keys, strlen(xsp->xs_keys)); 376831337658SMarcel Moolenaar } 376931337658SMarcel Moolenaar } 377031337658SMarcel Moolenaar 377131337658SMarcel Moolenaar xo_data_append(xop, "/", 1); 377231337658SMarcel Moolenaar xo_data_escape(xop, name, nlen); 377331337658SMarcel Moolenaar } 377431337658SMarcel Moolenaar 3775d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_INFO) && xop->xo_info) { 377631337658SMarcel Moolenaar static char in_type[] = "\" data-type=\""; 377731337658SMarcel Moolenaar static char in_help[] = "\" data-help=\""; 377831337658SMarcel Moolenaar 377931337658SMarcel Moolenaar xo_info_t *xip = xo_info_find(xop, name, nlen); 378031337658SMarcel Moolenaar if (xip) { 378131337658SMarcel Moolenaar if (xip->xi_type) { 378231337658SMarcel Moolenaar xo_data_append(xop, in_type, sizeof(in_type) - 1); 378331337658SMarcel Moolenaar xo_data_escape(xop, xip->xi_type, strlen(xip->xi_type)); 378431337658SMarcel Moolenaar } 378531337658SMarcel Moolenaar if (xip->xi_help) { 378631337658SMarcel Moolenaar xo_data_append(xop, in_help, sizeof(in_help) - 1); 378731337658SMarcel Moolenaar xo_data_escape(xop, xip->xi_help, strlen(xip->xi_help)); 378831337658SMarcel Moolenaar } 378931337658SMarcel Moolenaar } 379031337658SMarcel Moolenaar } 379131337658SMarcel Moolenaar 3792d1a0d267SMarcel Moolenaar if ((flags & XFF_KEY) && XOF_ISSET(xop, XOF_KEYS)) 379331337658SMarcel Moolenaar xo_data_append(xop, div_key, sizeof(div_key) - 1); 379431337658SMarcel Moolenaar } 379531337658SMarcel Moolenaar 3796d1a0d267SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_data; 37978a6eceffSPhil Shafer ssize_t base_offset = xbp->xb_curp - xbp->xb_bufp; 3798d1a0d267SMarcel Moolenaar 379931337658SMarcel Moolenaar xo_data_append(xop, div_end, sizeof(div_end) - 1); 380031337658SMarcel Moolenaar 3801d1a0d267SMarcel Moolenaar xo_humanize_save_t save; /* Save values for humanizing logic */ 3802d1a0d267SMarcel Moolenaar 3803d1a0d267SMarcel Moolenaar save.xhs_offset = xbp->xb_curp - xbp->xb_bufp; 3804d1a0d267SMarcel Moolenaar save.xhs_columns = xop->xo_columns; 3805d1a0d267SMarcel Moolenaar save.xhs_anchor_columns = xop->xo_anchor_columns; 3806d1a0d267SMarcel Moolenaar 3807d1a0d267SMarcel Moolenaar xo_do_format_field(xop, NULL, value, vlen, flags); 3808d1a0d267SMarcel Moolenaar 3809d1a0d267SMarcel Moolenaar if (flags & XFF_HUMANIZE) { 3810d1a0d267SMarcel Moolenaar /* 3811d1a0d267SMarcel Moolenaar * Unlike text style, we want to retain the original value and 3812d1a0d267SMarcel Moolenaar * stuff it into the "data-number" attribute. 3813d1a0d267SMarcel Moolenaar */ 3814d1a0d267SMarcel Moolenaar static const char div_number[] = "\" data-number=\""; 38158a6eceffSPhil Shafer ssize_t div_len = sizeof(div_number) - 1; 3816d1a0d267SMarcel Moolenaar 38178a6eceffSPhil Shafer ssize_t end_offset = xbp->xb_curp - xbp->xb_bufp; 38188a6eceffSPhil Shafer ssize_t olen = end_offset - save.xhs_offset; 3819d1a0d267SMarcel Moolenaar 3820d1a0d267SMarcel Moolenaar char *cp = alloca(olen + 1); 3821d1a0d267SMarcel Moolenaar memcpy(cp, xbp->xb_bufp + save.xhs_offset, olen); 3822d1a0d267SMarcel Moolenaar cp[olen] = '\0'; 3823d1a0d267SMarcel Moolenaar 3824d1a0d267SMarcel Moolenaar xo_format_humanize(xop, xbp, &save, flags); 3825d1a0d267SMarcel Moolenaar 3826d1a0d267SMarcel Moolenaar if (xo_buf_has_room(xbp, div_len + olen)) { 38278a6eceffSPhil Shafer ssize_t new_offset = xbp->xb_curp - xbp->xb_bufp; 3828d1a0d267SMarcel Moolenaar 3829d1a0d267SMarcel Moolenaar 3830d1a0d267SMarcel Moolenaar /* Move the humanized string off to the left */ 3831d1a0d267SMarcel Moolenaar memmove(xbp->xb_bufp + base_offset + div_len + olen, 3832d1a0d267SMarcel Moolenaar xbp->xb_bufp + base_offset, new_offset - base_offset); 3833d1a0d267SMarcel Moolenaar 3834d1a0d267SMarcel Moolenaar /* Copy the data_number attribute name */ 3835d1a0d267SMarcel Moolenaar memcpy(xbp->xb_bufp + base_offset, div_number, div_len); 3836d1a0d267SMarcel Moolenaar 3837d1a0d267SMarcel Moolenaar /* Copy the original long value */ 3838d1a0d267SMarcel Moolenaar memcpy(xbp->xb_bufp + base_offset + div_len, cp, olen); 3839d1a0d267SMarcel Moolenaar xbp->xb_curp += div_len + olen; 3840d1a0d267SMarcel Moolenaar } 3841d1a0d267SMarcel Moolenaar } 384231337658SMarcel Moolenaar 384331337658SMarcel Moolenaar xo_data_append(xop, div_close, sizeof(div_close) - 1); 384431337658SMarcel Moolenaar 3845d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_PRETTY)) 384631337658SMarcel Moolenaar xo_data_append(xop, "\n", 1); 384731337658SMarcel Moolenaar } 384831337658SMarcel Moolenaar 384931337658SMarcel Moolenaar static void 38508a6eceffSPhil Shafer xo_format_text (xo_handle_t *xop, const char *str, ssize_t len) 385131337658SMarcel Moolenaar { 3852788ca347SMarcel Moolenaar switch (xo_style(xop)) { 385331337658SMarcel Moolenaar case XO_STYLE_TEXT: 385431337658SMarcel Moolenaar xo_buf_append_locale(xop, &xop->xo_data, str, len); 385531337658SMarcel Moolenaar break; 385631337658SMarcel Moolenaar 385731337658SMarcel Moolenaar case XO_STYLE_HTML: 385831337658SMarcel Moolenaar xo_buf_append_div(xop, "text", 0, NULL, 0, str, len, NULL, 0); 385931337658SMarcel Moolenaar break; 386031337658SMarcel Moolenaar } 386131337658SMarcel Moolenaar } 386231337658SMarcel Moolenaar 386331337658SMarcel Moolenaar static void 386442ff34c3SPhil Shafer xo_format_title (xo_handle_t *xop, xo_field_info_t *xfip, 38658a6eceffSPhil Shafer const char *str, ssize_t len) 386631337658SMarcel Moolenaar { 3867d1a0d267SMarcel Moolenaar const char *fmt = xfip->xfi_format; 38688a6eceffSPhil Shafer ssize_t flen = xfip->xfi_flen; 3869d1a0d267SMarcel Moolenaar xo_xff_flags_t flags = xfip->xfi_flags; 3870d1a0d267SMarcel Moolenaar 3871788ca347SMarcel Moolenaar static char div_open[] = "<div class=\"title"; 3872788ca347SMarcel Moolenaar static char div_middle[] = "\">"; 387331337658SMarcel Moolenaar static char div_close[] = "</div>"; 387431337658SMarcel Moolenaar 3875545ddfbeSMarcel Moolenaar if (flen == 0) { 3876545ddfbeSMarcel Moolenaar fmt = "%s"; 3877545ddfbeSMarcel Moolenaar flen = 2; 3878545ddfbeSMarcel Moolenaar } 3879545ddfbeSMarcel Moolenaar 3880788ca347SMarcel Moolenaar switch (xo_style(xop)) { 388131337658SMarcel Moolenaar case XO_STYLE_XML: 388231337658SMarcel Moolenaar case XO_STYLE_JSON: 3883d1a0d267SMarcel Moolenaar case XO_STYLE_SDPARAMS: 3884d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER: 388531337658SMarcel Moolenaar /* 388631337658SMarcel Moolenaar * Even though we don't care about text, we need to do 388731337658SMarcel Moolenaar * enough parsing work to skip over the right bits of xo_vap. 388831337658SMarcel Moolenaar */ 388931337658SMarcel Moolenaar if (len == 0) 3890d1a0d267SMarcel Moolenaar xo_do_format_field(xop, NULL, fmt, flen, flags | XFF_NO_OUTPUT); 389131337658SMarcel Moolenaar return; 389231337658SMarcel Moolenaar } 389331337658SMarcel Moolenaar 389431337658SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_data; 38958a6eceffSPhil Shafer ssize_t start = xbp->xb_curp - xbp->xb_bufp; 38968a6eceffSPhil Shafer ssize_t left = xbp->xb_size - start; 38978a6eceffSPhil Shafer ssize_t rc; 389831337658SMarcel Moolenaar 3899788ca347SMarcel Moolenaar if (xo_style(xop) == XO_STYLE_HTML) { 390031337658SMarcel Moolenaar xo_line_ensure_open(xop, 0); 3901d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_PRETTY)) 390231337658SMarcel Moolenaar xo_buf_indent(xop, xop->xo_indent_by); 390331337658SMarcel Moolenaar xo_buf_append(&xop->xo_data, div_open, sizeof(div_open) - 1); 3904788ca347SMarcel Moolenaar xo_color_append_html(xop); 3905788ca347SMarcel Moolenaar xo_buf_append(&xop->xo_data, div_middle, sizeof(div_middle) - 1); 390631337658SMarcel Moolenaar } 390731337658SMarcel Moolenaar 390831337658SMarcel Moolenaar start = xbp->xb_curp - xbp->xb_bufp; /* Reset start */ 390931337658SMarcel Moolenaar if (len) { 391031337658SMarcel Moolenaar char *newfmt = alloca(flen + 1); 391131337658SMarcel Moolenaar memcpy(newfmt, fmt, flen); 391231337658SMarcel Moolenaar newfmt[flen] = '\0'; 391331337658SMarcel Moolenaar 391431337658SMarcel Moolenaar /* If len is non-zero, the format string apply to the name */ 391531337658SMarcel Moolenaar char *newstr = alloca(len + 1); 391631337658SMarcel Moolenaar memcpy(newstr, str, len); 391731337658SMarcel Moolenaar newstr[len] = '\0'; 391831337658SMarcel Moolenaar 391931337658SMarcel Moolenaar if (newstr[len - 1] == 's') { 392031337658SMarcel Moolenaar char *bp; 392131337658SMarcel Moolenaar 392231337658SMarcel Moolenaar rc = snprintf(NULL, 0, newfmt, newstr); 392331337658SMarcel Moolenaar if (rc > 0) { 392431337658SMarcel Moolenaar /* 392531337658SMarcel Moolenaar * We have to do this the hard way, since we might need 392631337658SMarcel Moolenaar * the columns. 392731337658SMarcel Moolenaar */ 392831337658SMarcel Moolenaar bp = alloca(rc + 1); 392931337658SMarcel Moolenaar rc = snprintf(bp, rc + 1, newfmt, newstr); 3930d1a0d267SMarcel Moolenaar 3931d1a0d267SMarcel Moolenaar xo_data_append_content(xop, bp, rc, flags); 393231337658SMarcel Moolenaar } 393331337658SMarcel Moolenaar goto move_along; 393431337658SMarcel Moolenaar 393531337658SMarcel Moolenaar } else { 393631337658SMarcel Moolenaar rc = snprintf(xbp->xb_curp, left, newfmt, newstr); 3937d1a0d267SMarcel Moolenaar if (rc >= left) { 393831337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, rc)) 393931337658SMarcel Moolenaar return; 394031337658SMarcel Moolenaar left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp); 394131337658SMarcel Moolenaar rc = snprintf(xbp->xb_curp, left, newfmt, newstr); 394231337658SMarcel Moolenaar } 394331337658SMarcel Moolenaar 394431337658SMarcel Moolenaar if (rc > 0) { 3945d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_COLUMNS)) 394631337658SMarcel Moolenaar xop->xo_columns += rc; 3947d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_ANCHOR)) 394831337658SMarcel Moolenaar xop->xo_anchor_columns += rc; 394931337658SMarcel Moolenaar } 395031337658SMarcel Moolenaar } 395131337658SMarcel Moolenaar 395231337658SMarcel Moolenaar } else { 3953d1a0d267SMarcel Moolenaar xo_do_format_field(xop, NULL, fmt, flen, flags); 395431337658SMarcel Moolenaar 3955d1a0d267SMarcel Moolenaar /* xo_do_format_field moved curp, so we need to reset it */ 395631337658SMarcel Moolenaar rc = xbp->xb_curp - (xbp->xb_bufp + start); 395731337658SMarcel Moolenaar xbp->xb_curp = xbp->xb_bufp + start; 395831337658SMarcel Moolenaar } 395931337658SMarcel Moolenaar 396031337658SMarcel Moolenaar /* If we're styling HTML, then we need to escape it */ 3961788ca347SMarcel Moolenaar if (xo_style(xop) == XO_STYLE_HTML) { 396231337658SMarcel Moolenaar rc = xo_escape_xml(xbp, rc, 0); 396331337658SMarcel Moolenaar } 396431337658SMarcel Moolenaar 396531337658SMarcel Moolenaar if (rc > 0) 396631337658SMarcel Moolenaar xbp->xb_curp += rc; 396731337658SMarcel Moolenaar 396831337658SMarcel Moolenaar move_along: 3969788ca347SMarcel Moolenaar if (xo_style(xop) == XO_STYLE_HTML) { 397031337658SMarcel Moolenaar xo_data_append(xop, div_close, sizeof(div_close) - 1); 3971d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_PRETTY)) 397231337658SMarcel Moolenaar xo_data_append(xop, "\n", 1); 397331337658SMarcel Moolenaar } 397431337658SMarcel Moolenaar } 397531337658SMarcel Moolenaar 397631337658SMarcel Moolenaar static void 397731337658SMarcel Moolenaar xo_format_prep (xo_handle_t *xop, xo_xff_flags_t flags) 397831337658SMarcel Moolenaar { 397931337658SMarcel Moolenaar if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST) { 398031337658SMarcel Moolenaar xo_data_append(xop, ",", 1); 3981d1a0d267SMarcel Moolenaar if (!(flags & XFF_LEAF_LIST) && XOF_ISSET(xop, XOF_PRETTY)) 398231337658SMarcel Moolenaar xo_data_append(xop, "\n", 1); 398331337658SMarcel Moolenaar } else 398431337658SMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 398531337658SMarcel Moolenaar } 398631337658SMarcel Moolenaar 398731337658SMarcel Moolenaar #if 0 398831337658SMarcel Moolenaar /* Useful debugging function */ 398931337658SMarcel Moolenaar void 399031337658SMarcel Moolenaar xo_arg (xo_handle_t *xop); 399131337658SMarcel Moolenaar void 399231337658SMarcel Moolenaar xo_arg (xo_handle_t *xop) 399331337658SMarcel Moolenaar { 399431337658SMarcel Moolenaar xop = xo_default(xop); 399531337658SMarcel Moolenaar fprintf(stderr, "0x%x", va_arg(xop->xo_vap, unsigned)); 399631337658SMarcel Moolenaar } 399731337658SMarcel Moolenaar #endif /* 0 */ 399831337658SMarcel Moolenaar 399931337658SMarcel Moolenaar static void 40008a6eceffSPhil Shafer xo_format_value (xo_handle_t *xop, const char *name, ssize_t nlen, 40018a6eceffSPhil Shafer const char *format, ssize_t flen, 40028a6eceffSPhil Shafer const char *encoding, ssize_t elen, xo_xff_flags_t flags) 400331337658SMarcel Moolenaar { 4004d1a0d267SMarcel Moolenaar int pretty = XOF_ISSET(xop, XOF_PRETTY); 400531337658SMarcel Moolenaar int quote; 400631337658SMarcel Moolenaar 4007545ddfbeSMarcel Moolenaar /* 4008545ddfbeSMarcel Moolenaar * Before we emit a value, we need to know that the frame is ready. 4009545ddfbeSMarcel Moolenaar */ 4010545ddfbeSMarcel Moolenaar xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth]; 4011545ddfbeSMarcel Moolenaar 4012545ddfbeSMarcel Moolenaar if (flags & XFF_LEAF_LIST) { 4013545ddfbeSMarcel Moolenaar /* 4014545ddfbeSMarcel Moolenaar * Check if we've already started to emit normal leafs 4015545ddfbeSMarcel Moolenaar * or if we're not in a leaf list. 4016545ddfbeSMarcel Moolenaar */ 4017545ddfbeSMarcel Moolenaar if ((xsp->xs_flags & (XSF_EMIT | XSF_EMIT_KEY)) 4018545ddfbeSMarcel Moolenaar || !(xsp->xs_flags & XSF_EMIT_LEAF_LIST)) { 4019545ddfbeSMarcel Moolenaar char nbuf[nlen + 1]; 4020545ddfbeSMarcel Moolenaar memcpy(nbuf, name, nlen); 4021545ddfbeSMarcel Moolenaar nbuf[nlen] = '\0'; 4022545ddfbeSMarcel Moolenaar 40238a6eceffSPhil Shafer ssize_t rc = xo_transition(xop, 0, nbuf, XSS_EMIT_LEAF_LIST); 4024545ddfbeSMarcel Moolenaar if (rc < 0) 4025545ddfbeSMarcel Moolenaar flags |= XFF_DISPLAY_ONLY | XFF_ENCODE_ONLY; 4026545ddfbeSMarcel Moolenaar else 4027545ddfbeSMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_EMIT_LEAF_LIST; 4028545ddfbeSMarcel Moolenaar } 4029545ddfbeSMarcel Moolenaar 4030545ddfbeSMarcel Moolenaar xsp = &xop->xo_stack[xop->xo_depth]; 4031545ddfbeSMarcel Moolenaar if (xsp->xs_name) { 4032545ddfbeSMarcel Moolenaar name = xsp->xs_name; 4033545ddfbeSMarcel Moolenaar nlen = strlen(name); 4034545ddfbeSMarcel Moolenaar } 4035545ddfbeSMarcel Moolenaar 4036545ddfbeSMarcel Moolenaar } else if (flags & XFF_KEY) { 4037545ddfbeSMarcel Moolenaar /* Emitting a 'k' (key) field */ 4038545ddfbeSMarcel Moolenaar if ((xsp->xs_flags & XSF_EMIT) && !(flags & XFF_DISPLAY_ONLY)) { 4039545ddfbeSMarcel Moolenaar xo_failure(xop, "key field emitted after normal value field: '%.*s'", 4040545ddfbeSMarcel Moolenaar nlen, name); 4041545ddfbeSMarcel Moolenaar 4042545ddfbeSMarcel Moolenaar } else if (!(xsp->xs_flags & XSF_EMIT_KEY)) { 4043545ddfbeSMarcel Moolenaar char nbuf[nlen + 1]; 4044545ddfbeSMarcel Moolenaar memcpy(nbuf, name, nlen); 4045545ddfbeSMarcel Moolenaar nbuf[nlen] = '\0'; 4046545ddfbeSMarcel Moolenaar 40478a6eceffSPhil Shafer ssize_t rc = xo_transition(xop, 0, nbuf, XSS_EMIT); 4048545ddfbeSMarcel Moolenaar if (rc < 0) 4049545ddfbeSMarcel Moolenaar flags |= XFF_DISPLAY_ONLY | XFF_ENCODE_ONLY; 4050545ddfbeSMarcel Moolenaar else 4051545ddfbeSMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_EMIT_KEY; 4052545ddfbeSMarcel Moolenaar 4053545ddfbeSMarcel Moolenaar xsp = &xop->xo_stack[xop->xo_depth]; 4054545ddfbeSMarcel Moolenaar xsp->xs_flags |= XSF_EMIT_KEY; 4055545ddfbeSMarcel Moolenaar } 4056545ddfbeSMarcel Moolenaar 4057545ddfbeSMarcel Moolenaar } else { 4058545ddfbeSMarcel Moolenaar /* Emitting a normal value field */ 4059545ddfbeSMarcel Moolenaar if ((xsp->xs_flags & XSF_EMIT_LEAF_LIST) 4060545ddfbeSMarcel Moolenaar || !(xsp->xs_flags & XSF_EMIT)) { 4061545ddfbeSMarcel Moolenaar char nbuf[nlen + 1]; 4062545ddfbeSMarcel Moolenaar memcpy(nbuf, name, nlen); 4063545ddfbeSMarcel Moolenaar nbuf[nlen] = '\0'; 4064545ddfbeSMarcel Moolenaar 40658a6eceffSPhil Shafer ssize_t rc = xo_transition(xop, 0, nbuf, XSS_EMIT); 4066545ddfbeSMarcel Moolenaar if (rc < 0) 4067545ddfbeSMarcel Moolenaar flags |= XFF_DISPLAY_ONLY | XFF_ENCODE_ONLY; 4068545ddfbeSMarcel Moolenaar else 4069545ddfbeSMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_EMIT; 4070545ddfbeSMarcel Moolenaar 4071545ddfbeSMarcel Moolenaar xsp = &xop->xo_stack[xop->xo_depth]; 4072545ddfbeSMarcel Moolenaar xsp->xs_flags |= XSF_EMIT; 4073545ddfbeSMarcel Moolenaar } 4074545ddfbeSMarcel Moolenaar } 4075545ddfbeSMarcel Moolenaar 4076d1a0d267SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_data; 4077d1a0d267SMarcel Moolenaar xo_humanize_save_t save; /* Save values for humanizing logic */ 4078d1a0d267SMarcel Moolenaar 4079788ca347SMarcel Moolenaar switch (xo_style(xop)) { 408031337658SMarcel Moolenaar case XO_STYLE_TEXT: 408131337658SMarcel Moolenaar if (flags & XFF_ENCODE_ONLY) 408231337658SMarcel Moolenaar flags |= XFF_NO_OUTPUT; 4083d1a0d267SMarcel Moolenaar 4084d1a0d267SMarcel Moolenaar save.xhs_offset = xbp->xb_curp - xbp->xb_bufp; 4085d1a0d267SMarcel Moolenaar save.xhs_columns = xop->xo_columns; 4086d1a0d267SMarcel Moolenaar save.xhs_anchor_columns = xop->xo_anchor_columns; 4087d1a0d267SMarcel Moolenaar 4088d1a0d267SMarcel Moolenaar xo_do_format_field(xop, NULL, format, flen, flags); 4089d1a0d267SMarcel Moolenaar 4090d1a0d267SMarcel Moolenaar if (flags & XFF_HUMANIZE) 4091d1a0d267SMarcel Moolenaar xo_format_humanize(xop, xbp, &save, flags); 409231337658SMarcel Moolenaar break; 409331337658SMarcel Moolenaar 409431337658SMarcel Moolenaar case XO_STYLE_HTML: 409531337658SMarcel Moolenaar if (flags & XFF_ENCODE_ONLY) 409631337658SMarcel Moolenaar flags |= XFF_NO_OUTPUT; 4097d1a0d267SMarcel Moolenaar 409831337658SMarcel Moolenaar xo_buf_append_div(xop, "data", flags, name, nlen, 409931337658SMarcel Moolenaar format, flen, encoding, elen); 410031337658SMarcel Moolenaar break; 410131337658SMarcel Moolenaar 410231337658SMarcel Moolenaar case XO_STYLE_XML: 410331337658SMarcel Moolenaar /* 410431337658SMarcel Moolenaar * Even though we're not making output, we still need to 410531337658SMarcel Moolenaar * let the formatting code handle the va_arg popping. 410631337658SMarcel Moolenaar */ 410731337658SMarcel Moolenaar if (flags & XFF_DISPLAY_ONLY) { 410831337658SMarcel Moolenaar flags |= XFF_NO_OUTPUT; 4109d1a0d267SMarcel Moolenaar xo_do_format_field(xop, NULL, format, flen, flags); 411031337658SMarcel Moolenaar break; 411131337658SMarcel Moolenaar } 411231337658SMarcel Moolenaar 411331337658SMarcel Moolenaar if (encoding) { 411431337658SMarcel Moolenaar format = encoding; 411531337658SMarcel Moolenaar flen = elen; 411631337658SMarcel Moolenaar } else { 411731337658SMarcel Moolenaar char *enc = alloca(flen + 1); 411831337658SMarcel Moolenaar memcpy(enc, format, flen); 411931337658SMarcel Moolenaar enc[flen] = '\0'; 412031337658SMarcel Moolenaar format = xo_fix_encoding(xop, enc); 412131337658SMarcel Moolenaar flen = strlen(format); 412231337658SMarcel Moolenaar } 412331337658SMarcel Moolenaar 412431337658SMarcel Moolenaar if (nlen == 0) { 412531337658SMarcel Moolenaar static char missing[] = "missing-field-name"; 412631337658SMarcel Moolenaar xo_failure(xop, "missing field name: %s", format); 412731337658SMarcel Moolenaar name = missing; 412831337658SMarcel Moolenaar nlen = sizeof(missing) - 1; 412931337658SMarcel Moolenaar } 413031337658SMarcel Moolenaar 413131337658SMarcel Moolenaar if (pretty) 413231337658SMarcel Moolenaar xo_buf_indent(xop, -1); 413331337658SMarcel Moolenaar xo_data_append(xop, "<", 1); 413431337658SMarcel Moolenaar xo_data_escape(xop, name, nlen); 413531337658SMarcel Moolenaar 413631337658SMarcel Moolenaar if (xop->xo_attrs.xb_curp != xop->xo_attrs.xb_bufp) { 413731337658SMarcel Moolenaar xo_data_append(xop, xop->xo_attrs.xb_bufp, 413831337658SMarcel Moolenaar xop->xo_attrs.xb_curp - xop->xo_attrs.xb_bufp); 413931337658SMarcel Moolenaar xop->xo_attrs.xb_curp = xop->xo_attrs.xb_bufp; 414031337658SMarcel Moolenaar } 414131337658SMarcel Moolenaar 414231337658SMarcel Moolenaar /* 414331337658SMarcel Moolenaar * We indicate 'key' fields using the 'key' attribute. While 414431337658SMarcel Moolenaar * this is really committing the crime of mixing meta-data with 414531337658SMarcel Moolenaar * data, it's often useful. Especially when format meta-data is 414631337658SMarcel Moolenaar * difficult to come by. 414731337658SMarcel Moolenaar */ 4148d1a0d267SMarcel Moolenaar if ((flags & XFF_KEY) && XOF_ISSET(xop, XOF_KEYS)) { 414931337658SMarcel Moolenaar static char attr[] = " key=\"key\""; 415031337658SMarcel Moolenaar xo_data_append(xop, attr, sizeof(attr) - 1); 415131337658SMarcel Moolenaar } 415231337658SMarcel Moolenaar 415331337658SMarcel Moolenaar /* 415431337658SMarcel Moolenaar * Save the offset at which we'd place units. See xo_format_units. 415531337658SMarcel Moolenaar */ 4156d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_UNITS)) { 4157d1a0d267SMarcel Moolenaar XOIF_SET(xop, XOIF_UNITS_PENDING); 415831337658SMarcel Moolenaar xop->xo_units_offset = xop->xo_data.xb_curp -xop->xo_data.xb_bufp; 415931337658SMarcel Moolenaar } 416031337658SMarcel Moolenaar 416131337658SMarcel Moolenaar xo_data_append(xop, ">", 1); 4162d1a0d267SMarcel Moolenaar xo_do_format_field(xop, NULL, format, flen, flags); 416331337658SMarcel Moolenaar xo_data_append(xop, "</", 2); 416431337658SMarcel Moolenaar xo_data_escape(xop, name, nlen); 416531337658SMarcel Moolenaar xo_data_append(xop, ">", 1); 416631337658SMarcel Moolenaar if (pretty) 416731337658SMarcel Moolenaar xo_data_append(xop, "\n", 1); 416831337658SMarcel Moolenaar break; 416931337658SMarcel Moolenaar 417031337658SMarcel Moolenaar case XO_STYLE_JSON: 417131337658SMarcel Moolenaar if (flags & XFF_DISPLAY_ONLY) { 417231337658SMarcel Moolenaar flags |= XFF_NO_OUTPUT; 4173d1a0d267SMarcel Moolenaar xo_do_format_field(xop, NULL, format, flen, flags); 417431337658SMarcel Moolenaar break; 417531337658SMarcel Moolenaar } 417631337658SMarcel Moolenaar 417731337658SMarcel Moolenaar if (encoding) { 417831337658SMarcel Moolenaar format = encoding; 417931337658SMarcel Moolenaar flen = elen; 418031337658SMarcel Moolenaar } else { 418131337658SMarcel Moolenaar char *enc = alloca(flen + 1); 418231337658SMarcel Moolenaar memcpy(enc, format, flen); 418331337658SMarcel Moolenaar enc[flen] = '\0'; 418431337658SMarcel Moolenaar format = xo_fix_encoding(xop, enc); 418531337658SMarcel Moolenaar flen = strlen(format); 418631337658SMarcel Moolenaar } 418731337658SMarcel Moolenaar 41888a6eceffSPhil Shafer int first = (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST) 41898a6eceffSPhil Shafer ? 0 : 1; 419031337658SMarcel Moolenaar 419131337658SMarcel Moolenaar xo_format_prep(xop, flags); 419231337658SMarcel Moolenaar 419331337658SMarcel Moolenaar if (flags & XFF_QUOTE) 419431337658SMarcel Moolenaar quote = 1; 419531337658SMarcel Moolenaar else if (flags & XFF_NOQUOTE) 419631337658SMarcel Moolenaar quote = 0; 419731337658SMarcel Moolenaar else if (flen == 0) { 419831337658SMarcel Moolenaar quote = 0; 419931337658SMarcel Moolenaar format = "true"; /* JSON encodes empty tags as a boolean true */ 420031337658SMarcel Moolenaar flen = 4; 42018a6eceffSPhil Shafer } else if (strchr("diouDOUeEfFgG", format[flen - 1]) == NULL) 420231337658SMarcel Moolenaar quote = 1; 420331337658SMarcel Moolenaar else 420431337658SMarcel Moolenaar quote = 0; 420531337658SMarcel Moolenaar 420631337658SMarcel Moolenaar if (nlen == 0) { 420731337658SMarcel Moolenaar static char missing[] = "missing-field-name"; 420831337658SMarcel Moolenaar xo_failure(xop, "missing field name: %s", format); 420931337658SMarcel Moolenaar name = missing; 421031337658SMarcel Moolenaar nlen = sizeof(missing) - 1; 421131337658SMarcel Moolenaar } 421231337658SMarcel Moolenaar 421331337658SMarcel Moolenaar if (flags & XFF_LEAF_LIST) { 4214788ca347SMarcel Moolenaar if (!first && pretty) 4215788ca347SMarcel Moolenaar xo_data_append(xop, "\n", 1); 4216788ca347SMarcel Moolenaar if (pretty) 421731337658SMarcel Moolenaar xo_buf_indent(xop, -1); 421831337658SMarcel Moolenaar } else { 421931337658SMarcel Moolenaar if (pretty) 422031337658SMarcel Moolenaar xo_buf_indent(xop, -1); 422131337658SMarcel Moolenaar xo_data_append(xop, "\"", 1); 422231337658SMarcel Moolenaar 422331337658SMarcel Moolenaar xbp = &xop->xo_data; 42248a6eceffSPhil Shafer ssize_t off = xbp->xb_curp - xbp->xb_bufp; 422531337658SMarcel Moolenaar 422631337658SMarcel Moolenaar xo_data_escape(xop, name, nlen); 422731337658SMarcel Moolenaar 4228d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_UNDERSCORES)) { 42298a6eceffSPhil Shafer ssize_t coff = xbp->xb_curp - xbp->xb_bufp; 42308a6eceffSPhil Shafer for ( ; off < coff; off++) 423131337658SMarcel Moolenaar if (xbp->xb_bufp[off] == '-') 423231337658SMarcel Moolenaar xbp->xb_bufp[off] = '_'; 423331337658SMarcel Moolenaar } 423431337658SMarcel Moolenaar xo_data_append(xop, "\":", 2); 423531337658SMarcel Moolenaar if (pretty) 423631337658SMarcel Moolenaar xo_data_append(xop, " ", 1); 4237788ca347SMarcel Moolenaar } 4238788ca347SMarcel Moolenaar 423931337658SMarcel Moolenaar if (quote) 424031337658SMarcel Moolenaar xo_data_append(xop, "\"", 1); 424131337658SMarcel Moolenaar 4242d1a0d267SMarcel Moolenaar xo_do_format_field(xop, NULL, format, flen, flags); 424331337658SMarcel Moolenaar 424431337658SMarcel Moolenaar if (quote) 424531337658SMarcel Moolenaar xo_data_append(xop, "\"", 1); 424631337658SMarcel Moolenaar break; 4247d1a0d267SMarcel Moolenaar 4248d1a0d267SMarcel Moolenaar case XO_STYLE_SDPARAMS: 4249d1a0d267SMarcel Moolenaar if (flags & XFF_DISPLAY_ONLY) { 4250d1a0d267SMarcel Moolenaar flags |= XFF_NO_OUTPUT; 4251d1a0d267SMarcel Moolenaar xo_do_format_field(xop, NULL, format, flen, flags); 4252d1a0d267SMarcel Moolenaar break; 4253d1a0d267SMarcel Moolenaar } 4254d1a0d267SMarcel Moolenaar 4255d1a0d267SMarcel Moolenaar if (encoding) { 4256d1a0d267SMarcel Moolenaar format = encoding; 4257d1a0d267SMarcel Moolenaar flen = elen; 4258d1a0d267SMarcel Moolenaar } else { 4259d1a0d267SMarcel Moolenaar char *enc = alloca(flen + 1); 4260d1a0d267SMarcel Moolenaar memcpy(enc, format, flen); 4261d1a0d267SMarcel Moolenaar enc[flen] = '\0'; 4262d1a0d267SMarcel Moolenaar format = xo_fix_encoding(xop, enc); 4263d1a0d267SMarcel Moolenaar flen = strlen(format); 4264d1a0d267SMarcel Moolenaar } 4265d1a0d267SMarcel Moolenaar 4266d1a0d267SMarcel Moolenaar if (nlen == 0) { 4267d1a0d267SMarcel Moolenaar static char missing[] = "missing-field-name"; 4268d1a0d267SMarcel Moolenaar xo_failure(xop, "missing field name: %s", format); 4269d1a0d267SMarcel Moolenaar name = missing; 4270d1a0d267SMarcel Moolenaar nlen = sizeof(missing) - 1; 4271d1a0d267SMarcel Moolenaar } 4272d1a0d267SMarcel Moolenaar 4273d1a0d267SMarcel Moolenaar xo_data_escape(xop, name, nlen); 4274d1a0d267SMarcel Moolenaar xo_data_append(xop, "=\"", 2); 4275d1a0d267SMarcel Moolenaar xo_do_format_field(xop, NULL, format, flen, flags); 4276d1a0d267SMarcel Moolenaar xo_data_append(xop, "\" ", 2); 4277d1a0d267SMarcel Moolenaar break; 4278d1a0d267SMarcel Moolenaar 4279d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER: 4280d1a0d267SMarcel Moolenaar if (flags & XFF_DISPLAY_ONLY) { 4281d1a0d267SMarcel Moolenaar flags |= XFF_NO_OUTPUT; 4282d1a0d267SMarcel Moolenaar xo_do_format_field(xop, NULL, format, flen, flags); 4283d1a0d267SMarcel Moolenaar break; 4284d1a0d267SMarcel Moolenaar } 4285d1a0d267SMarcel Moolenaar 4286d1a0d267SMarcel Moolenaar if (flags & XFF_QUOTE) 4287d1a0d267SMarcel Moolenaar quote = 1; 4288d1a0d267SMarcel Moolenaar else if (flags & XFF_NOQUOTE) 4289d1a0d267SMarcel Moolenaar quote = 0; 4290d1a0d267SMarcel Moolenaar else if (flen == 0) { 4291d1a0d267SMarcel Moolenaar quote = 0; 4292d1a0d267SMarcel Moolenaar format = "true"; /* JSON encodes empty tags as a boolean true */ 4293d1a0d267SMarcel Moolenaar flen = 4; 4294d1a0d267SMarcel Moolenaar } else if (strchr("diouxXDOUeEfFgGaAcCp", format[flen - 1]) == NULL) 4295d1a0d267SMarcel Moolenaar quote = 1; 4296d1a0d267SMarcel Moolenaar else 4297d1a0d267SMarcel Moolenaar quote = 0; 4298d1a0d267SMarcel Moolenaar 4299d1a0d267SMarcel Moolenaar if (encoding) { 4300d1a0d267SMarcel Moolenaar format = encoding; 4301d1a0d267SMarcel Moolenaar flen = elen; 4302d1a0d267SMarcel Moolenaar } else { 4303d1a0d267SMarcel Moolenaar char *enc = alloca(flen + 1); 4304d1a0d267SMarcel Moolenaar memcpy(enc, format, flen); 4305d1a0d267SMarcel Moolenaar enc[flen] = '\0'; 4306d1a0d267SMarcel Moolenaar format = xo_fix_encoding(xop, enc); 4307d1a0d267SMarcel Moolenaar flen = strlen(format); 4308d1a0d267SMarcel Moolenaar } 4309d1a0d267SMarcel Moolenaar 4310d1a0d267SMarcel Moolenaar if (nlen == 0) { 4311d1a0d267SMarcel Moolenaar static char missing[] = "missing-field-name"; 4312d1a0d267SMarcel Moolenaar xo_failure(xop, "missing field name: %s", format); 4313d1a0d267SMarcel Moolenaar name = missing; 4314d1a0d267SMarcel Moolenaar nlen = sizeof(missing) - 1; 4315d1a0d267SMarcel Moolenaar } 4316d1a0d267SMarcel Moolenaar 43178a6eceffSPhil Shafer ssize_t name_offset = xo_buf_offset(&xop->xo_data); 4318d1a0d267SMarcel Moolenaar xo_data_append(xop, name, nlen); 4319d1a0d267SMarcel Moolenaar xo_data_append(xop, "", 1); 4320d1a0d267SMarcel Moolenaar 43218a6eceffSPhil Shafer ssize_t value_offset = xo_buf_offset(&xop->xo_data); 4322d1a0d267SMarcel Moolenaar xo_do_format_field(xop, NULL, format, flen, flags); 4323d1a0d267SMarcel Moolenaar xo_data_append(xop, "", 1); 4324d1a0d267SMarcel Moolenaar 4325d1a0d267SMarcel Moolenaar xo_encoder_handle(xop, quote ? XO_OP_STRING : XO_OP_CONTENT, 4326d1a0d267SMarcel Moolenaar xo_buf_data(&xop->xo_data, name_offset), 4327d1a0d267SMarcel Moolenaar xo_buf_data(&xop->xo_data, value_offset)); 4328d1a0d267SMarcel Moolenaar xo_buf_reset(&xop->xo_data); 4329d1a0d267SMarcel Moolenaar break; 433031337658SMarcel Moolenaar } 433131337658SMarcel Moolenaar } 433231337658SMarcel Moolenaar 433331337658SMarcel Moolenaar static void 433442ff34c3SPhil Shafer xo_set_gettext_domain (xo_handle_t *xop, xo_field_info_t *xfip, 43358a6eceffSPhil Shafer const char *str, ssize_t len) 4336d1a0d267SMarcel Moolenaar { 4337d1a0d267SMarcel Moolenaar const char *fmt = xfip->xfi_format; 43388a6eceffSPhil Shafer ssize_t flen = xfip->xfi_flen; 4339d1a0d267SMarcel Moolenaar 4340d1a0d267SMarcel Moolenaar /* Start by discarding previous domain */ 4341d1a0d267SMarcel Moolenaar if (xop->xo_gt_domain) { 4342d1a0d267SMarcel Moolenaar xo_free(xop->xo_gt_domain); 4343d1a0d267SMarcel Moolenaar xop->xo_gt_domain = NULL; 4344d1a0d267SMarcel Moolenaar } 4345d1a0d267SMarcel Moolenaar 4346d1a0d267SMarcel Moolenaar /* An empty {G:} means no domainname */ 4347d1a0d267SMarcel Moolenaar if (len == 0 && flen == 0) 4348d1a0d267SMarcel Moolenaar return; 4349d1a0d267SMarcel Moolenaar 43508a6eceffSPhil Shafer ssize_t start_offset = -1; 4351d1a0d267SMarcel Moolenaar if (len == 0 && flen != 0) { 4352d1a0d267SMarcel Moolenaar /* Need to do format the data to get the domainname from args */ 4353d1a0d267SMarcel Moolenaar start_offset = xop->xo_data.xb_curp - xop->xo_data.xb_bufp; 4354d1a0d267SMarcel Moolenaar xo_do_format_field(xop, NULL, fmt, flen, 0); 4355d1a0d267SMarcel Moolenaar 43568a6eceffSPhil Shafer ssize_t end_offset = xop->xo_data.xb_curp - xop->xo_data.xb_bufp; 4357d1a0d267SMarcel Moolenaar len = end_offset - start_offset; 4358d1a0d267SMarcel Moolenaar str = xop->xo_data.xb_bufp + start_offset; 4359d1a0d267SMarcel Moolenaar } 4360d1a0d267SMarcel Moolenaar 4361d1a0d267SMarcel Moolenaar xop->xo_gt_domain = xo_strndup(str, len); 4362d1a0d267SMarcel Moolenaar 4363d1a0d267SMarcel Moolenaar /* Reset the current buffer point to avoid emitting the name as output */ 4364d1a0d267SMarcel Moolenaar if (start_offset >= 0) 4365d1a0d267SMarcel Moolenaar xop->xo_data.xb_curp = xop->xo_data.xb_bufp + start_offset; 4366d1a0d267SMarcel Moolenaar } 4367d1a0d267SMarcel Moolenaar 4368d1a0d267SMarcel Moolenaar static void 436931337658SMarcel Moolenaar xo_format_content (xo_handle_t *xop, const char *class_name, 4370d1a0d267SMarcel Moolenaar const char *tag_name, 43718a6eceffSPhil Shafer const char *str, ssize_t len, const char *fmt, ssize_t flen, 4372d1a0d267SMarcel Moolenaar xo_xff_flags_t flags) 437331337658SMarcel Moolenaar { 4374788ca347SMarcel Moolenaar switch (xo_style(xop)) { 437531337658SMarcel Moolenaar case XO_STYLE_TEXT: 4376d1a0d267SMarcel Moolenaar if (len) 4377d1a0d267SMarcel Moolenaar xo_data_append_content(xop, str, len, flags); 4378d1a0d267SMarcel Moolenaar else 4379d1a0d267SMarcel Moolenaar xo_do_format_field(xop, NULL, fmt, flen, flags); 438031337658SMarcel Moolenaar break; 438131337658SMarcel Moolenaar 438231337658SMarcel Moolenaar case XO_STYLE_HTML: 438331337658SMarcel Moolenaar if (len == 0) { 438431337658SMarcel Moolenaar str = fmt; 438531337658SMarcel Moolenaar len = flen; 438631337658SMarcel Moolenaar } 438731337658SMarcel Moolenaar 4388d1a0d267SMarcel Moolenaar xo_buf_append_div(xop, class_name, flags, NULL, 0, str, len, NULL, 0); 438931337658SMarcel Moolenaar break; 439031337658SMarcel Moolenaar 439131337658SMarcel Moolenaar case XO_STYLE_XML: 4392d1a0d267SMarcel Moolenaar case XO_STYLE_JSON: 4393d1a0d267SMarcel Moolenaar case XO_STYLE_SDPARAMS: 4394d1a0d267SMarcel Moolenaar if (tag_name) { 439531337658SMarcel Moolenaar if (len == 0) { 439631337658SMarcel Moolenaar str = fmt; 439731337658SMarcel Moolenaar len = flen; 439831337658SMarcel Moolenaar } 439931337658SMarcel Moolenaar 4400d1a0d267SMarcel Moolenaar xo_open_container_h(xop, tag_name); 4401d1a0d267SMarcel Moolenaar xo_format_value(xop, "message", 7, str, len, NULL, 0, flags); 4402d1a0d267SMarcel Moolenaar xo_close_container_h(xop, tag_name); 440331337658SMarcel Moolenaar 440431337658SMarcel Moolenaar } else { 440531337658SMarcel Moolenaar /* 440631337658SMarcel Moolenaar * Even though we don't care about labels, we need to do 440731337658SMarcel Moolenaar * enough parsing work to skip over the right bits of xo_vap. 440831337658SMarcel Moolenaar */ 440931337658SMarcel Moolenaar if (len == 0) 4410d1a0d267SMarcel Moolenaar xo_do_format_field(xop, NULL, fmt, flen, 4411d1a0d267SMarcel Moolenaar flags | XFF_NO_OUTPUT); 441231337658SMarcel Moolenaar } 441331337658SMarcel Moolenaar break; 441431337658SMarcel Moolenaar 4415d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER: 441631337658SMarcel Moolenaar if (len == 0) 4417d1a0d267SMarcel Moolenaar xo_do_format_field(xop, NULL, fmt, flen, 4418d1a0d267SMarcel Moolenaar flags | XFF_NO_OUTPUT); 441931337658SMarcel Moolenaar break; 442031337658SMarcel Moolenaar } 442131337658SMarcel Moolenaar } 442231337658SMarcel Moolenaar 4423788ca347SMarcel Moolenaar static const char *xo_color_names[] = { 4424788ca347SMarcel Moolenaar "default", /* XO_COL_DEFAULT */ 4425788ca347SMarcel Moolenaar "black", /* XO_COL_BLACK */ 4426788ca347SMarcel Moolenaar "red", /* XO_CLOR_RED */ 4427788ca347SMarcel Moolenaar "green", /* XO_COL_GREEN */ 4428788ca347SMarcel Moolenaar "yellow", /* XO_COL_YELLOW */ 4429788ca347SMarcel Moolenaar "blue", /* XO_COL_BLUE */ 4430788ca347SMarcel Moolenaar "magenta", /* XO_COL_MAGENTA */ 4431788ca347SMarcel Moolenaar "cyan", /* XO_COL_CYAN */ 4432788ca347SMarcel Moolenaar "white", /* XO_COL_WHITE */ 4433788ca347SMarcel Moolenaar NULL 4434788ca347SMarcel Moolenaar }; 4435788ca347SMarcel Moolenaar 4436788ca347SMarcel Moolenaar static int 4437788ca347SMarcel Moolenaar xo_color_find (const char *str) 4438788ca347SMarcel Moolenaar { 4439788ca347SMarcel Moolenaar int i; 4440788ca347SMarcel Moolenaar 4441788ca347SMarcel Moolenaar for (i = 0; xo_color_names[i]; i++) { 4442788ca347SMarcel Moolenaar if (strcmp(xo_color_names[i], str) == 0) 4443788ca347SMarcel Moolenaar return i; 4444788ca347SMarcel Moolenaar } 4445788ca347SMarcel Moolenaar 4446788ca347SMarcel Moolenaar return -1; 4447788ca347SMarcel Moolenaar } 4448788ca347SMarcel Moolenaar 4449788ca347SMarcel Moolenaar static const char *xo_effect_names[] = { 4450788ca347SMarcel Moolenaar "reset", /* XO_EFF_RESET */ 4451788ca347SMarcel Moolenaar "normal", /* XO_EFF_NORMAL */ 4452788ca347SMarcel Moolenaar "bold", /* XO_EFF_BOLD */ 4453788ca347SMarcel Moolenaar "underline", /* XO_EFF_UNDERLINE */ 4454788ca347SMarcel Moolenaar "inverse", /* XO_EFF_INVERSE */ 4455788ca347SMarcel Moolenaar NULL 4456788ca347SMarcel Moolenaar }; 4457788ca347SMarcel Moolenaar 4458788ca347SMarcel Moolenaar static const char *xo_effect_on_codes[] = { 4459788ca347SMarcel Moolenaar "0", /* XO_EFF_RESET */ 4460788ca347SMarcel Moolenaar "0", /* XO_EFF_NORMAL */ 4461788ca347SMarcel Moolenaar "1", /* XO_EFF_BOLD */ 4462788ca347SMarcel Moolenaar "4", /* XO_EFF_UNDERLINE */ 4463788ca347SMarcel Moolenaar "7", /* XO_EFF_INVERSE */ 4464788ca347SMarcel Moolenaar NULL 4465788ca347SMarcel Moolenaar }; 4466788ca347SMarcel Moolenaar 4467788ca347SMarcel Moolenaar #if 0 4468788ca347SMarcel Moolenaar /* 4469788ca347SMarcel Moolenaar * See comment below re: joy of terminal standards. These can 4470788ca347SMarcel Moolenaar * be use by just adding: 4471d1a0d267SMarcel Moolenaar * + if (newp->xoc_effects & bit) 4472788ca347SMarcel Moolenaar * code = xo_effect_on_codes[i]; 4473788ca347SMarcel Moolenaar * + else 4474788ca347SMarcel Moolenaar * + code = xo_effect_off_codes[i]; 4475788ca347SMarcel Moolenaar * in xo_color_handle_text. 4476788ca347SMarcel Moolenaar */ 4477788ca347SMarcel Moolenaar static const char *xo_effect_off_codes[] = { 4478788ca347SMarcel Moolenaar "0", /* XO_EFF_RESET */ 4479788ca347SMarcel Moolenaar "0", /* XO_EFF_NORMAL */ 4480788ca347SMarcel Moolenaar "21", /* XO_EFF_BOLD */ 4481788ca347SMarcel Moolenaar "24", /* XO_EFF_UNDERLINE */ 4482788ca347SMarcel Moolenaar "27", /* XO_EFF_INVERSE */ 4483788ca347SMarcel Moolenaar NULL 4484788ca347SMarcel Moolenaar }; 4485788ca347SMarcel Moolenaar #endif /* 0 */ 4486788ca347SMarcel Moolenaar 4487788ca347SMarcel Moolenaar static int 4488788ca347SMarcel Moolenaar xo_effect_find (const char *str) 4489788ca347SMarcel Moolenaar { 4490788ca347SMarcel Moolenaar int i; 4491788ca347SMarcel Moolenaar 4492788ca347SMarcel Moolenaar for (i = 0; xo_effect_names[i]; i++) { 4493788ca347SMarcel Moolenaar if (strcmp(xo_effect_names[i], str) == 0) 4494788ca347SMarcel Moolenaar return i; 4495788ca347SMarcel Moolenaar } 4496788ca347SMarcel Moolenaar 4497788ca347SMarcel Moolenaar return -1; 4498788ca347SMarcel Moolenaar } 4499788ca347SMarcel Moolenaar 4500788ca347SMarcel Moolenaar static void 4501788ca347SMarcel Moolenaar xo_colors_parse (xo_handle_t *xop, xo_colors_t *xocp, char *str) 4502788ca347SMarcel Moolenaar { 4503788ca347SMarcel Moolenaar #ifdef LIBXO_TEXT_ONLY 4504788ca347SMarcel Moolenaar return; 4505788ca347SMarcel Moolenaar #endif /* LIBXO_TEXT_ONLY */ 4506788ca347SMarcel Moolenaar 4507788ca347SMarcel Moolenaar char *cp, *ep, *np, *xp; 45088a6eceffSPhil Shafer ssize_t len = strlen(str); 4509788ca347SMarcel Moolenaar int rc; 4510788ca347SMarcel Moolenaar 4511788ca347SMarcel Moolenaar /* 4512788ca347SMarcel Moolenaar * Possible tokens: colors, bg-colors, effects, no-effects, "reset". 4513788ca347SMarcel Moolenaar */ 4514788ca347SMarcel Moolenaar for (cp = str, ep = cp + len - 1; cp && cp < ep; cp = np) { 4515788ca347SMarcel Moolenaar /* Trim leading whitespace */ 4516788ca347SMarcel Moolenaar while (isspace((int) *cp)) 4517788ca347SMarcel Moolenaar cp += 1; 4518788ca347SMarcel Moolenaar 4519788ca347SMarcel Moolenaar np = strchr(cp, ','); 4520788ca347SMarcel Moolenaar if (np) 4521788ca347SMarcel Moolenaar *np++ = '\0'; 4522788ca347SMarcel Moolenaar 4523788ca347SMarcel Moolenaar /* Trim trailing whitespace */ 4524788ca347SMarcel Moolenaar xp = cp + strlen(cp) - 1; 4525788ca347SMarcel Moolenaar while (isspace(*xp) && xp > cp) 4526788ca347SMarcel Moolenaar *xp-- = '\0'; 4527788ca347SMarcel Moolenaar 4528788ca347SMarcel Moolenaar if (cp[0] == 'f' && cp[1] == 'g' && cp[2] == '-') { 4529788ca347SMarcel Moolenaar rc = xo_color_find(cp + 3); 4530788ca347SMarcel Moolenaar if (rc < 0) 4531788ca347SMarcel Moolenaar goto unknown; 4532788ca347SMarcel Moolenaar 4533788ca347SMarcel Moolenaar xocp->xoc_col_fg = rc; 4534788ca347SMarcel Moolenaar 4535788ca347SMarcel Moolenaar } else if (cp[0] == 'b' && cp[1] == 'g' && cp[2] == '-') { 4536788ca347SMarcel Moolenaar rc = xo_color_find(cp + 3); 4537788ca347SMarcel Moolenaar if (rc < 0) 4538788ca347SMarcel Moolenaar goto unknown; 4539788ca347SMarcel Moolenaar xocp->xoc_col_bg = rc; 4540788ca347SMarcel Moolenaar 4541788ca347SMarcel Moolenaar } else if (cp[0] == 'n' && cp[1] == 'o' && cp[2] == '-') { 4542788ca347SMarcel Moolenaar rc = xo_effect_find(cp + 3); 4543788ca347SMarcel Moolenaar if (rc < 0) 4544788ca347SMarcel Moolenaar goto unknown; 4545788ca347SMarcel Moolenaar xocp->xoc_effects &= ~(1 << rc); 4546788ca347SMarcel Moolenaar 4547788ca347SMarcel Moolenaar } else { 4548788ca347SMarcel Moolenaar rc = xo_effect_find(cp); 4549788ca347SMarcel Moolenaar if (rc < 0) 4550788ca347SMarcel Moolenaar goto unknown; 4551788ca347SMarcel Moolenaar xocp->xoc_effects |= 1 << rc; 4552788ca347SMarcel Moolenaar 4553788ca347SMarcel Moolenaar switch (1 << rc) { 4554788ca347SMarcel Moolenaar case XO_EFF_RESET: 4555788ca347SMarcel Moolenaar xocp->xoc_col_fg = xocp->xoc_col_bg = 0; 4556788ca347SMarcel Moolenaar /* Note: not "|=" since we want to wipe out the old value */ 4557788ca347SMarcel Moolenaar xocp->xoc_effects = XO_EFF_RESET; 4558788ca347SMarcel Moolenaar break; 4559788ca347SMarcel Moolenaar 4560788ca347SMarcel Moolenaar case XO_EFF_NORMAL: 4561788ca347SMarcel Moolenaar xocp->xoc_effects &= ~(XO_EFF_BOLD | XO_EFF_UNDERLINE 4562788ca347SMarcel Moolenaar | XO_EFF_INVERSE | XO_EFF_NORMAL); 4563788ca347SMarcel Moolenaar break; 4564788ca347SMarcel Moolenaar } 4565788ca347SMarcel Moolenaar } 4566788ca347SMarcel Moolenaar continue; 4567788ca347SMarcel Moolenaar 4568788ca347SMarcel Moolenaar unknown: 4569d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_WARN)) 4570788ca347SMarcel Moolenaar xo_failure(xop, "unknown color/effect string detected: '%s'", cp); 4571788ca347SMarcel Moolenaar } 4572788ca347SMarcel Moolenaar } 4573788ca347SMarcel Moolenaar 4574788ca347SMarcel Moolenaar static inline int 4575788ca347SMarcel Moolenaar xo_colors_enabled (xo_handle_t *xop UNUSED) 4576788ca347SMarcel Moolenaar { 4577788ca347SMarcel Moolenaar #ifdef LIBXO_TEXT_ONLY 4578788ca347SMarcel Moolenaar return 0; 4579788ca347SMarcel Moolenaar #else /* LIBXO_TEXT_ONLY */ 4580d1a0d267SMarcel Moolenaar return XOF_ISSET(xop, XOF_COLOR); 4581788ca347SMarcel Moolenaar #endif /* LIBXO_TEXT_ONLY */ 4582788ca347SMarcel Moolenaar } 4583788ca347SMarcel Moolenaar 4584788ca347SMarcel Moolenaar static void 458542ff34c3SPhil Shafer xo_colors_handle_text (xo_handle_t *xop, xo_colors_t *newp) 4586788ca347SMarcel Moolenaar { 4587788ca347SMarcel Moolenaar char buf[BUFSIZ]; 4588788ca347SMarcel Moolenaar char *cp = buf, *ep = buf + sizeof(buf); 4589788ca347SMarcel Moolenaar unsigned i, bit; 4590788ca347SMarcel Moolenaar xo_colors_t *oldp = &xop->xo_colors; 459142ff34c3SPhil Shafer const char *code = NULL; 4592788ca347SMarcel Moolenaar 4593788ca347SMarcel Moolenaar /* 4594788ca347SMarcel Moolenaar * Start the buffer with an escape. We don't want to add the '[' 4595788ca347SMarcel Moolenaar * now, since we let xo_effect_text_add unconditionally add the ';'. 4596788ca347SMarcel Moolenaar * We'll replace the first ';' with a '[' when we're done. 4597788ca347SMarcel Moolenaar */ 4598788ca347SMarcel Moolenaar *cp++ = 0x1b; /* Escape */ 4599788ca347SMarcel Moolenaar 4600788ca347SMarcel Moolenaar /* 4601788ca347SMarcel Moolenaar * Terminals were designed back in the age before "certainty" was 4602788ca347SMarcel Moolenaar * invented, when standards were more what you'd call "guidelines" 4603788ca347SMarcel Moolenaar * than actual rules. Anyway we can't depend on them to operate 4604788ca347SMarcel Moolenaar * correctly. So when display attributes are changed, we punt, 4605788ca347SMarcel Moolenaar * reseting them all and turning back on the ones we want to keep. 4606788ca347SMarcel Moolenaar * Longer, but should be completely reliable. Savvy? 4607788ca347SMarcel Moolenaar */ 4608788ca347SMarcel Moolenaar if (oldp->xoc_effects != (newp->xoc_effects & oldp->xoc_effects)) { 4609788ca347SMarcel Moolenaar newp->xoc_effects |= XO_EFF_RESET; 4610788ca347SMarcel Moolenaar oldp->xoc_effects = 0; 4611788ca347SMarcel Moolenaar } 4612788ca347SMarcel Moolenaar 4613788ca347SMarcel Moolenaar for (i = 0, bit = 1; xo_effect_names[i]; i++, bit <<= 1) { 4614788ca347SMarcel Moolenaar if ((newp->xoc_effects & bit) == (oldp->xoc_effects & bit)) 4615788ca347SMarcel Moolenaar continue; 4616788ca347SMarcel Moolenaar 4617788ca347SMarcel Moolenaar code = xo_effect_on_codes[i]; 4618788ca347SMarcel Moolenaar 4619788ca347SMarcel Moolenaar cp += snprintf(cp, ep - cp, ";%s", code); 4620788ca347SMarcel Moolenaar if (cp >= ep) 4621788ca347SMarcel Moolenaar return; /* Should not occur */ 4622788ca347SMarcel Moolenaar 4623788ca347SMarcel Moolenaar if (bit == XO_EFF_RESET) { 4624788ca347SMarcel Moolenaar /* Mark up the old value so we can detect current values as new */ 4625788ca347SMarcel Moolenaar oldp->xoc_effects = 0; 4626788ca347SMarcel Moolenaar oldp->xoc_col_fg = oldp->xoc_col_bg = XO_COL_DEFAULT; 4627788ca347SMarcel Moolenaar } 4628788ca347SMarcel Moolenaar } 4629788ca347SMarcel Moolenaar 4630788ca347SMarcel Moolenaar if (newp->xoc_col_fg != oldp->xoc_col_fg) { 4631788ca347SMarcel Moolenaar cp += snprintf(cp, ep - cp, ";3%u", 4632788ca347SMarcel Moolenaar (newp->xoc_col_fg != XO_COL_DEFAULT) 4633788ca347SMarcel Moolenaar ? newp->xoc_col_fg - 1 : 9); 4634788ca347SMarcel Moolenaar } 4635788ca347SMarcel Moolenaar 4636788ca347SMarcel Moolenaar if (newp->xoc_col_bg != oldp->xoc_col_bg) { 4637788ca347SMarcel Moolenaar cp += snprintf(cp, ep - cp, ";4%u", 4638788ca347SMarcel Moolenaar (newp->xoc_col_bg != XO_COL_DEFAULT) 4639788ca347SMarcel Moolenaar ? newp->xoc_col_bg - 1 : 9); 4640788ca347SMarcel Moolenaar } 4641788ca347SMarcel Moolenaar 4642788ca347SMarcel Moolenaar if (cp - buf != 1 && cp < ep - 3) { 4643788ca347SMarcel Moolenaar buf[1] = '['; /* Overwrite leading ';' */ 4644788ca347SMarcel Moolenaar *cp++ = 'm'; 4645788ca347SMarcel Moolenaar *cp = '\0'; 4646788ca347SMarcel Moolenaar xo_buf_append(&xop->xo_data, buf, cp - buf); 4647788ca347SMarcel Moolenaar } 4648788ca347SMarcel Moolenaar } 4649788ca347SMarcel Moolenaar 4650788ca347SMarcel Moolenaar static void 4651788ca347SMarcel Moolenaar xo_colors_handle_html (xo_handle_t *xop, xo_colors_t *newp) 4652788ca347SMarcel Moolenaar { 4653788ca347SMarcel Moolenaar xo_colors_t *oldp = &xop->xo_colors; 4654788ca347SMarcel Moolenaar 4655788ca347SMarcel Moolenaar /* 4656788ca347SMarcel Moolenaar * HTML colors are mostly trivial: fill in xo_color_buf with 4657788ca347SMarcel Moolenaar * a set of class tags representing the colors and effects. 4658788ca347SMarcel Moolenaar */ 4659788ca347SMarcel Moolenaar 4660788ca347SMarcel Moolenaar /* If nothing changed, then do nothing */ 4661788ca347SMarcel Moolenaar if (oldp->xoc_effects == newp->xoc_effects 4662788ca347SMarcel Moolenaar && oldp->xoc_col_fg == newp->xoc_col_fg 4663788ca347SMarcel Moolenaar && oldp->xoc_col_bg == newp->xoc_col_bg) 4664788ca347SMarcel Moolenaar return; 4665788ca347SMarcel Moolenaar 4666788ca347SMarcel Moolenaar unsigned i, bit; 4667788ca347SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_color_buf; 4668788ca347SMarcel Moolenaar 4669788ca347SMarcel Moolenaar xo_buf_reset(xbp); /* We rebuild content after each change */ 4670788ca347SMarcel Moolenaar 4671788ca347SMarcel Moolenaar for (i = 0, bit = 1; xo_effect_names[i]; i++, bit <<= 1) { 4672788ca347SMarcel Moolenaar if (!(newp->xoc_effects & bit)) 4673788ca347SMarcel Moolenaar continue; 4674788ca347SMarcel Moolenaar 4675788ca347SMarcel Moolenaar xo_buf_append_str(xbp, " effect-"); 4676788ca347SMarcel Moolenaar xo_buf_append_str(xbp, xo_effect_names[i]); 4677788ca347SMarcel Moolenaar } 4678788ca347SMarcel Moolenaar 4679788ca347SMarcel Moolenaar const char *fg = NULL; 4680788ca347SMarcel Moolenaar const char *bg = NULL; 4681788ca347SMarcel Moolenaar 4682788ca347SMarcel Moolenaar if (newp->xoc_col_fg != XO_COL_DEFAULT) 4683788ca347SMarcel Moolenaar fg = xo_color_names[newp->xoc_col_fg]; 4684788ca347SMarcel Moolenaar if (newp->xoc_col_bg != XO_COL_DEFAULT) 4685788ca347SMarcel Moolenaar bg = xo_color_names[newp->xoc_col_bg]; 4686788ca347SMarcel Moolenaar 4687788ca347SMarcel Moolenaar if (newp->xoc_effects & XO_EFF_INVERSE) { 4688788ca347SMarcel Moolenaar const char *tmp = fg; 4689788ca347SMarcel Moolenaar fg = bg; 4690788ca347SMarcel Moolenaar bg = tmp; 4691788ca347SMarcel Moolenaar if (fg == NULL) 4692788ca347SMarcel Moolenaar fg = "inverse"; 4693788ca347SMarcel Moolenaar if (bg == NULL) 4694788ca347SMarcel Moolenaar bg = "inverse"; 4695788ca347SMarcel Moolenaar 4696788ca347SMarcel Moolenaar } 4697788ca347SMarcel Moolenaar 4698788ca347SMarcel Moolenaar if (fg) { 4699788ca347SMarcel Moolenaar xo_buf_append_str(xbp, " color-fg-"); 4700788ca347SMarcel Moolenaar xo_buf_append_str(xbp, fg); 4701788ca347SMarcel Moolenaar } 4702788ca347SMarcel Moolenaar 4703788ca347SMarcel Moolenaar if (bg) { 4704788ca347SMarcel Moolenaar xo_buf_append_str(xbp, " color-bg-"); 4705788ca347SMarcel Moolenaar xo_buf_append_str(xbp, bg); 4706788ca347SMarcel Moolenaar } 4707788ca347SMarcel Moolenaar } 4708788ca347SMarcel Moolenaar 4709788ca347SMarcel Moolenaar static void 471042ff34c3SPhil Shafer xo_format_colors (xo_handle_t *xop, xo_field_info_t *xfip, 47118a6eceffSPhil Shafer const char *str, ssize_t len) 4712788ca347SMarcel Moolenaar { 4713d1a0d267SMarcel Moolenaar const char *fmt = xfip->xfi_format; 47148a6eceffSPhil Shafer ssize_t flen = xfip->xfi_flen; 4715d1a0d267SMarcel Moolenaar 4716788ca347SMarcel Moolenaar xo_buffer_t xb; 4717788ca347SMarcel Moolenaar 4718788ca347SMarcel Moolenaar /* If the string is static and we've in an encoding style, bail */ 4719d1a0d267SMarcel Moolenaar if (len != 0 && xo_style_is_encoding(xop)) 4720788ca347SMarcel Moolenaar return; 4721788ca347SMarcel Moolenaar 4722788ca347SMarcel Moolenaar xo_buf_init(&xb); 4723788ca347SMarcel Moolenaar 4724788ca347SMarcel Moolenaar if (len) 4725788ca347SMarcel Moolenaar xo_buf_append(&xb, str, len); 4726788ca347SMarcel Moolenaar else if (flen) 4727d1a0d267SMarcel Moolenaar xo_do_format_field(xop, &xb, fmt, flen, 0); 4728788ca347SMarcel Moolenaar else 4729788ca347SMarcel Moolenaar xo_buf_append(&xb, "reset", 6); /* Default if empty */ 4730788ca347SMarcel Moolenaar 4731788ca347SMarcel Moolenaar if (xo_colors_enabled(xop)) { 4732788ca347SMarcel Moolenaar switch (xo_style(xop)) { 4733788ca347SMarcel Moolenaar case XO_STYLE_TEXT: 4734788ca347SMarcel Moolenaar case XO_STYLE_HTML: 4735788ca347SMarcel Moolenaar xo_buf_append(&xb, "", 1); 4736788ca347SMarcel Moolenaar 4737788ca347SMarcel Moolenaar xo_colors_t xoc = xop->xo_colors; 4738788ca347SMarcel Moolenaar xo_colors_parse(xop, &xoc, xb.xb_bufp); 4739788ca347SMarcel Moolenaar 4740788ca347SMarcel Moolenaar if (xo_style(xop) == XO_STYLE_TEXT) { 4741788ca347SMarcel Moolenaar /* 4742788ca347SMarcel Moolenaar * Text mode means emitting the colors as ANSI character 4743788ca347SMarcel Moolenaar * codes. This will allow people who like colors to have 4744788ca347SMarcel Moolenaar * colors. The issue is, of course conflicting with the 4745788ca347SMarcel Moolenaar * user's perfectly reasonable color scheme. Which leads 4746788ca347SMarcel Moolenaar * to the hell of LSCOLORS, where even app need to have 4747788ca347SMarcel Moolenaar * customization hooks for adjusting colors. Instead we 4748788ca347SMarcel Moolenaar * provide a simpler-but-still-annoying answer where one 4749788ca347SMarcel Moolenaar * can map colors to other colors. 4750788ca347SMarcel Moolenaar */ 4751788ca347SMarcel Moolenaar xo_colors_handle_text(xop, &xoc); 4752788ca347SMarcel Moolenaar xoc.xoc_effects &= ~XO_EFF_RESET; /* After handling it */ 4753788ca347SMarcel Moolenaar 4754788ca347SMarcel Moolenaar } else { 4755788ca347SMarcel Moolenaar /* 4756788ca347SMarcel Moolenaar * HTML output is wrapped in divs, so the color information 4757788ca347SMarcel Moolenaar * must appear in every div until cleared. Most pathetic. 4758788ca347SMarcel Moolenaar * Most unavoidable. 4759788ca347SMarcel Moolenaar */ 4760788ca347SMarcel Moolenaar xoc.xoc_effects &= ~XO_EFF_RESET; /* Before handling effects */ 4761788ca347SMarcel Moolenaar xo_colors_handle_html(xop, &xoc); 4762788ca347SMarcel Moolenaar } 4763788ca347SMarcel Moolenaar 4764788ca347SMarcel Moolenaar xop->xo_colors = xoc; 4765788ca347SMarcel Moolenaar break; 4766788ca347SMarcel Moolenaar 4767788ca347SMarcel Moolenaar case XO_STYLE_XML: 4768788ca347SMarcel Moolenaar case XO_STYLE_JSON: 4769d1a0d267SMarcel Moolenaar case XO_STYLE_SDPARAMS: 4770d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER: 4771788ca347SMarcel Moolenaar /* 4772788ca347SMarcel Moolenaar * Nothing to do; we did all that work just to clear the stack of 4773788ca347SMarcel Moolenaar * formatting arguments. 4774788ca347SMarcel Moolenaar */ 4775788ca347SMarcel Moolenaar break; 4776788ca347SMarcel Moolenaar } 4777788ca347SMarcel Moolenaar } 4778788ca347SMarcel Moolenaar 4779788ca347SMarcel Moolenaar xo_buf_cleanup(&xb); 4780788ca347SMarcel Moolenaar } 4781788ca347SMarcel Moolenaar 478231337658SMarcel Moolenaar static void 478342ff34c3SPhil Shafer xo_format_units (xo_handle_t *xop, xo_field_info_t *xfip, 47848a6eceffSPhil Shafer const char *str, ssize_t len) 478531337658SMarcel Moolenaar { 4786d1a0d267SMarcel Moolenaar const char *fmt = xfip->xfi_format; 47878a6eceffSPhil Shafer ssize_t flen = xfip->xfi_flen; 4788d1a0d267SMarcel Moolenaar xo_xff_flags_t flags = xfip->xfi_flags; 4789d1a0d267SMarcel Moolenaar 479031337658SMarcel Moolenaar static char units_start_xml[] = " units=\""; 479131337658SMarcel Moolenaar static char units_start_html[] = " data-units=\""; 479231337658SMarcel Moolenaar 4793d1a0d267SMarcel Moolenaar if (!XOIF_ISSET(xop, XOIF_UNITS_PENDING)) { 4794d1a0d267SMarcel Moolenaar xo_format_content(xop, "units", NULL, str, len, fmt, flen, flags); 479531337658SMarcel Moolenaar return; 479631337658SMarcel Moolenaar } 479731337658SMarcel Moolenaar 479831337658SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_data; 47998a6eceffSPhil Shafer ssize_t start = xop->xo_units_offset; 48008a6eceffSPhil Shafer ssize_t stop = xbp->xb_curp - xbp->xb_bufp; 480131337658SMarcel Moolenaar 4802788ca347SMarcel Moolenaar if (xo_style(xop) == XO_STYLE_XML) 480331337658SMarcel Moolenaar xo_buf_append(xbp, units_start_xml, sizeof(units_start_xml) - 1); 4804788ca347SMarcel Moolenaar else if (xo_style(xop) == XO_STYLE_HTML) 480531337658SMarcel Moolenaar xo_buf_append(xbp, units_start_html, sizeof(units_start_html) - 1); 480631337658SMarcel Moolenaar else 480731337658SMarcel Moolenaar return; 480831337658SMarcel Moolenaar 480931337658SMarcel Moolenaar if (len) 4810d1a0d267SMarcel Moolenaar xo_data_escape(xop, str, len); 481131337658SMarcel Moolenaar else 4812d1a0d267SMarcel Moolenaar xo_do_format_field(xop, NULL, fmt, flen, flags); 481331337658SMarcel Moolenaar 481431337658SMarcel Moolenaar xo_buf_append(xbp, "\"", 1); 481531337658SMarcel Moolenaar 48168a6eceffSPhil Shafer ssize_t now = xbp->xb_curp - xbp->xb_bufp; 48178a6eceffSPhil Shafer ssize_t delta = now - stop; 4818d1a0d267SMarcel Moolenaar if (delta <= 0) { /* Strange; no output to move */ 481931337658SMarcel Moolenaar xbp->xb_curp = xbp->xb_bufp + stop; /* Reset buffer to prior state */ 482031337658SMarcel Moolenaar return; 482131337658SMarcel Moolenaar } 482231337658SMarcel Moolenaar 482331337658SMarcel Moolenaar /* 482431337658SMarcel Moolenaar * Now we're in it alright. We've need to insert the unit value 482531337658SMarcel Moolenaar * we just created into the right spot. We make a local copy, 482631337658SMarcel Moolenaar * move it and then insert our copy. We know there's room in the 482731337658SMarcel Moolenaar * buffer, since we're just moving this around. 482831337658SMarcel Moolenaar */ 482931337658SMarcel Moolenaar char *buf = alloca(delta); 483031337658SMarcel Moolenaar 483131337658SMarcel Moolenaar memcpy(buf, xbp->xb_bufp + stop, delta); 483231337658SMarcel Moolenaar memmove(xbp->xb_bufp + start + delta, xbp->xb_bufp + start, stop - start); 483331337658SMarcel Moolenaar memmove(xbp->xb_bufp + start, buf, delta); 483431337658SMarcel Moolenaar } 483531337658SMarcel Moolenaar 48368a6eceffSPhil Shafer static ssize_t 483742ff34c3SPhil Shafer xo_find_width (xo_handle_t *xop, xo_field_info_t *xfip, 48388a6eceffSPhil Shafer const char *str, ssize_t len) 483931337658SMarcel Moolenaar { 4840d1a0d267SMarcel Moolenaar const char *fmt = xfip->xfi_format; 48418a6eceffSPhil Shafer ssize_t flen = xfip->xfi_flen; 4842d1a0d267SMarcel Moolenaar 484331337658SMarcel Moolenaar long width = 0; 484431337658SMarcel Moolenaar char *bp; 484531337658SMarcel Moolenaar char *cp; 484631337658SMarcel Moolenaar 484731337658SMarcel Moolenaar if (len) { 484831337658SMarcel Moolenaar bp = alloca(len + 1); /* Make local NUL-terminated copy of str */ 484931337658SMarcel Moolenaar memcpy(bp, str, len); 485031337658SMarcel Moolenaar bp[len] = '\0'; 485131337658SMarcel Moolenaar 485231337658SMarcel Moolenaar width = strtol(bp, &cp, 0); 485331337658SMarcel Moolenaar if (width == LONG_MIN || width == LONG_MAX 485431337658SMarcel Moolenaar || bp == cp || *cp != '\0' ) { 485531337658SMarcel Moolenaar width = 0; 485631337658SMarcel Moolenaar xo_failure(xop, "invalid width for anchor: '%s'", bp); 485731337658SMarcel Moolenaar } 485831337658SMarcel Moolenaar } else if (flen) { 485931337658SMarcel Moolenaar if (flen != 2 || strncmp("%d", fmt, flen) != 0) 486031337658SMarcel Moolenaar xo_failure(xop, "invalid width format: '%*.*s'", flen, flen, fmt); 4861d1a0d267SMarcel Moolenaar if (!XOF_ISSET(xop, XOF_NO_VA_ARG)) 486231337658SMarcel Moolenaar width = va_arg(xop->xo_vap, int); 486331337658SMarcel Moolenaar } 486431337658SMarcel Moolenaar 486531337658SMarcel Moolenaar return width; 486631337658SMarcel Moolenaar } 486731337658SMarcel Moolenaar 486831337658SMarcel Moolenaar static void 486931337658SMarcel Moolenaar xo_anchor_clear (xo_handle_t *xop) 487031337658SMarcel Moolenaar { 4871d1a0d267SMarcel Moolenaar XOIF_CLEAR(xop, XOIF_ANCHOR); 487231337658SMarcel Moolenaar xop->xo_anchor_offset = 0; 487331337658SMarcel Moolenaar xop->xo_anchor_columns = 0; 487431337658SMarcel Moolenaar xop->xo_anchor_min_width = 0; 487531337658SMarcel Moolenaar } 487631337658SMarcel Moolenaar 487731337658SMarcel Moolenaar /* 487831337658SMarcel Moolenaar * An anchor is a marker used to delay field width implications. 487931337658SMarcel Moolenaar * Imagine the format string "{[:10}{min:%d}/{cur:%d}/{max:%d}{:]}". 488031337658SMarcel Moolenaar * We are looking for output like " 1/4/5" 488131337658SMarcel Moolenaar * 488231337658SMarcel Moolenaar * To make this work, we record the anchor and then return to 488331337658SMarcel Moolenaar * format it when the end anchor tag is seen. 488431337658SMarcel Moolenaar */ 488531337658SMarcel Moolenaar static void 488642ff34c3SPhil Shafer xo_anchor_start (xo_handle_t *xop, xo_field_info_t *xfip, 48878a6eceffSPhil Shafer const char *str, ssize_t len) 488831337658SMarcel Moolenaar { 4889788ca347SMarcel Moolenaar if (xo_style(xop) != XO_STYLE_TEXT && xo_style(xop) != XO_STYLE_HTML) 489031337658SMarcel Moolenaar return; 489131337658SMarcel Moolenaar 4892d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_ANCHOR)) 489331337658SMarcel Moolenaar xo_failure(xop, "the anchor already recording is discarded"); 489431337658SMarcel Moolenaar 4895d1a0d267SMarcel Moolenaar XOIF_SET(xop, XOIF_ANCHOR); 489631337658SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_data; 489731337658SMarcel Moolenaar xop->xo_anchor_offset = xbp->xb_curp - xbp->xb_bufp; 489831337658SMarcel Moolenaar xop->xo_anchor_columns = 0; 489931337658SMarcel Moolenaar 490031337658SMarcel Moolenaar /* 490131337658SMarcel Moolenaar * Now we find the width, if possible. If it's not there, 490231337658SMarcel Moolenaar * we'll get it on the end anchor. 490331337658SMarcel Moolenaar */ 490442ff34c3SPhil Shafer xop->xo_anchor_min_width = xo_find_width(xop, xfip, str, len); 490531337658SMarcel Moolenaar } 490631337658SMarcel Moolenaar 490731337658SMarcel Moolenaar static void 490842ff34c3SPhil Shafer xo_anchor_stop (xo_handle_t *xop, xo_field_info_t *xfip, 49098a6eceffSPhil Shafer const char *str, ssize_t len) 491031337658SMarcel Moolenaar { 4911788ca347SMarcel Moolenaar if (xo_style(xop) != XO_STYLE_TEXT && xo_style(xop) != XO_STYLE_HTML) 491231337658SMarcel Moolenaar return; 491331337658SMarcel Moolenaar 4914d1a0d267SMarcel Moolenaar if (!XOIF_ISSET(xop, XOIF_ANCHOR)) { 491531337658SMarcel Moolenaar xo_failure(xop, "no start anchor"); 491631337658SMarcel Moolenaar return; 491731337658SMarcel Moolenaar } 491831337658SMarcel Moolenaar 4919d1a0d267SMarcel Moolenaar XOIF_CLEAR(xop, XOIF_UNITS_PENDING); 492031337658SMarcel Moolenaar 49218a6eceffSPhil Shafer ssize_t width = xo_find_width(xop, xfip, str, len); 492231337658SMarcel Moolenaar if (width == 0) 492331337658SMarcel Moolenaar width = xop->xo_anchor_min_width; 492431337658SMarcel Moolenaar 492531337658SMarcel Moolenaar if (width == 0) /* No width given; nothing to do */ 492631337658SMarcel Moolenaar goto done; 492731337658SMarcel Moolenaar 492831337658SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_data; 49298a6eceffSPhil Shafer ssize_t start = xop->xo_anchor_offset; 49308a6eceffSPhil Shafer ssize_t stop = xbp->xb_curp - xbp->xb_bufp; 49318a6eceffSPhil Shafer ssize_t abswidth = (width > 0) ? width : -width; 49328a6eceffSPhil Shafer ssize_t blen = abswidth - xop->xo_anchor_columns; 493331337658SMarcel Moolenaar 493431337658SMarcel Moolenaar if (blen <= 0) /* Already over width */ 493531337658SMarcel Moolenaar goto done; 493631337658SMarcel Moolenaar 493731337658SMarcel Moolenaar if (abswidth > XO_MAX_ANCHOR_WIDTH) { 493831337658SMarcel Moolenaar xo_failure(xop, "width over %u are not supported", 493931337658SMarcel Moolenaar XO_MAX_ANCHOR_WIDTH); 494031337658SMarcel Moolenaar goto done; 494131337658SMarcel Moolenaar } 494231337658SMarcel Moolenaar 494331337658SMarcel Moolenaar /* Make a suitable padding field and emit it */ 494431337658SMarcel Moolenaar char *buf = alloca(blen); 494531337658SMarcel Moolenaar memset(buf, ' ', blen); 4946d1a0d267SMarcel Moolenaar xo_format_content(xop, "padding", NULL, buf, blen, NULL, 0, 0); 494731337658SMarcel Moolenaar 494831337658SMarcel Moolenaar if (width < 0) /* Already left justified */ 494931337658SMarcel Moolenaar goto done; 495031337658SMarcel Moolenaar 49518a6eceffSPhil Shafer ssize_t now = xbp->xb_curp - xbp->xb_bufp; 49528a6eceffSPhil Shafer ssize_t delta = now - stop; 4953d1a0d267SMarcel Moolenaar if (delta <= 0) /* Strange; no output to move */ 495431337658SMarcel Moolenaar goto done; 495531337658SMarcel Moolenaar 495631337658SMarcel Moolenaar /* 495731337658SMarcel Moolenaar * Now we're in it alright. We've need to insert the padding data 495831337658SMarcel Moolenaar * we just created (which might be an HTML <div> or text) before 495931337658SMarcel Moolenaar * the formatted data. We make a local copy, move it and then 496031337658SMarcel Moolenaar * insert our copy. We know there's room in the buffer, since 496131337658SMarcel Moolenaar * we're just moving this around. 496231337658SMarcel Moolenaar */ 496331337658SMarcel Moolenaar if (delta > blen) 496431337658SMarcel Moolenaar buf = alloca(delta); /* Expand buffer if needed */ 496531337658SMarcel Moolenaar 496631337658SMarcel Moolenaar memcpy(buf, xbp->xb_bufp + stop, delta); 496731337658SMarcel Moolenaar memmove(xbp->xb_bufp + start + delta, xbp->xb_bufp + start, stop - start); 496831337658SMarcel Moolenaar memmove(xbp->xb_bufp + start, buf, delta); 496931337658SMarcel Moolenaar 497031337658SMarcel Moolenaar done: 497131337658SMarcel Moolenaar xo_anchor_clear(xop); 497231337658SMarcel Moolenaar } 497331337658SMarcel Moolenaar 4974d1a0d267SMarcel Moolenaar static const char * 4975d1a0d267SMarcel Moolenaar xo_class_name (int ftype) 497631337658SMarcel Moolenaar { 4977d1a0d267SMarcel Moolenaar switch (ftype) { 4978d1a0d267SMarcel Moolenaar case 'D': return "decoration"; 4979d1a0d267SMarcel Moolenaar case 'E': return "error"; 4980d1a0d267SMarcel Moolenaar case 'L': return "label"; 4981d1a0d267SMarcel Moolenaar case 'N': return "note"; 4982d1a0d267SMarcel Moolenaar case 'P': return "padding"; 4983d1a0d267SMarcel Moolenaar case 'W': return "warning"; 498431337658SMarcel Moolenaar } 498531337658SMarcel Moolenaar 4986d1a0d267SMarcel Moolenaar return NULL; 498731337658SMarcel Moolenaar } 498831337658SMarcel Moolenaar 4989d1a0d267SMarcel Moolenaar static const char * 4990d1a0d267SMarcel Moolenaar xo_tag_name (int ftype) 4991d1a0d267SMarcel Moolenaar { 4992d1a0d267SMarcel Moolenaar switch (ftype) { 4993d1a0d267SMarcel Moolenaar case 'E': return "__error"; 4994d1a0d267SMarcel Moolenaar case 'W': return "__warning"; 4995d1a0d267SMarcel Moolenaar } 4996d1a0d267SMarcel Moolenaar 4997d1a0d267SMarcel Moolenaar return NULL; 4998d1a0d267SMarcel Moolenaar } 4999d1a0d267SMarcel Moolenaar 5000d1a0d267SMarcel Moolenaar static int 5001d1a0d267SMarcel Moolenaar xo_role_wants_default_format (int ftype) 5002d1a0d267SMarcel Moolenaar { 5003d1a0d267SMarcel Moolenaar switch (ftype) { 5004d1a0d267SMarcel Moolenaar /* These roles can be completely empty and/or without formatting */ 5005d1a0d267SMarcel Moolenaar case 'C': 5006d1a0d267SMarcel Moolenaar case 'G': 5007d1a0d267SMarcel Moolenaar case '[': 5008d1a0d267SMarcel Moolenaar case ']': 5009d1a0d267SMarcel Moolenaar return 0; 5010d1a0d267SMarcel Moolenaar } 5011d1a0d267SMarcel Moolenaar 5012d1a0d267SMarcel Moolenaar return 1; 5013d1a0d267SMarcel Moolenaar } 5014d1a0d267SMarcel Moolenaar 5015d1a0d267SMarcel Moolenaar static xo_mapping_t xo_role_names[] = { 5016d1a0d267SMarcel Moolenaar { 'C', "color" }, 5017d1a0d267SMarcel Moolenaar { 'D', "decoration" }, 5018d1a0d267SMarcel Moolenaar { 'E', "error" }, 5019d1a0d267SMarcel Moolenaar { 'L', "label" }, 5020d1a0d267SMarcel Moolenaar { 'N', "note" }, 5021d1a0d267SMarcel Moolenaar { 'P', "padding" }, 5022d1a0d267SMarcel Moolenaar { 'T', "title" }, 5023d1a0d267SMarcel Moolenaar { 'U', "units" }, 5024d1a0d267SMarcel Moolenaar { 'V', "value" }, 5025d1a0d267SMarcel Moolenaar { 'W', "warning" }, 5026d1a0d267SMarcel Moolenaar { '[', "start-anchor" }, 5027d1a0d267SMarcel Moolenaar { ']', "stop-anchor" }, 5028d1a0d267SMarcel Moolenaar { 0, NULL } 5029d1a0d267SMarcel Moolenaar }; 5030d1a0d267SMarcel Moolenaar 5031d1a0d267SMarcel Moolenaar #define XO_ROLE_EBRACE '{' /* Escaped braces */ 5032d1a0d267SMarcel Moolenaar #define XO_ROLE_TEXT '+' 5033d1a0d267SMarcel Moolenaar #define XO_ROLE_NEWLINE '\n' 5034d1a0d267SMarcel Moolenaar 5035d1a0d267SMarcel Moolenaar static xo_mapping_t xo_modifier_names[] = { 503642ff34c3SPhil Shafer { XFF_ARGUMENT, "argument" }, 5037d1a0d267SMarcel Moolenaar { XFF_COLON, "colon" }, 5038d1a0d267SMarcel Moolenaar { XFF_COMMA, "comma" }, 5039d1a0d267SMarcel Moolenaar { XFF_DISPLAY_ONLY, "display" }, 5040d1a0d267SMarcel Moolenaar { XFF_ENCODE_ONLY, "encoding" }, 5041d1a0d267SMarcel Moolenaar { XFF_GT_FIELD, "gettext" }, 5042d1a0d267SMarcel Moolenaar { XFF_HUMANIZE, "humanize" }, 5043d1a0d267SMarcel Moolenaar { XFF_HUMANIZE, "hn" }, 5044d1a0d267SMarcel Moolenaar { XFF_HN_SPACE, "hn-space" }, 5045d1a0d267SMarcel Moolenaar { XFF_HN_DECIMAL, "hn-decimal" }, 5046d1a0d267SMarcel Moolenaar { XFF_HN_1000, "hn-1000" }, 5047d1a0d267SMarcel Moolenaar { XFF_KEY, "key" }, 5048d1a0d267SMarcel Moolenaar { XFF_LEAF_LIST, "leaf-list" }, 5049d1a0d267SMarcel Moolenaar { XFF_LEAF_LIST, "list" }, 5050d1a0d267SMarcel Moolenaar { XFF_NOQUOTE, "no-quotes" }, 5051d1a0d267SMarcel Moolenaar { XFF_NOQUOTE, "no-quote" }, 5052d1a0d267SMarcel Moolenaar { XFF_GT_PLURAL, "plural" }, 5053d1a0d267SMarcel Moolenaar { XFF_QUOTE, "quotes" }, 5054d1a0d267SMarcel Moolenaar { XFF_QUOTE, "quote" }, 5055d1a0d267SMarcel Moolenaar { XFF_TRIM_WS, "trim" }, 5056d1a0d267SMarcel Moolenaar { XFF_WS, "white" }, 5057d1a0d267SMarcel Moolenaar { 0, NULL } 5058d1a0d267SMarcel Moolenaar }; 5059d1a0d267SMarcel Moolenaar 5060d1a0d267SMarcel Moolenaar #ifdef NOT_NEEDED_YET 5061d1a0d267SMarcel Moolenaar static xo_mapping_t xo_modifier_short_names[] = { 5062d1a0d267SMarcel Moolenaar { XFF_COLON, "c" }, 5063d1a0d267SMarcel Moolenaar { XFF_DISPLAY_ONLY, "d" }, 5064d1a0d267SMarcel Moolenaar { XFF_ENCODE_ONLY, "e" }, 5065d1a0d267SMarcel Moolenaar { XFF_GT_FIELD, "g" }, 5066d1a0d267SMarcel Moolenaar { XFF_HUMANIZE, "h" }, 5067d1a0d267SMarcel Moolenaar { XFF_KEY, "k" }, 5068d1a0d267SMarcel Moolenaar { XFF_LEAF_LIST, "l" }, 5069d1a0d267SMarcel Moolenaar { XFF_NOQUOTE, "n" }, 5070d1a0d267SMarcel Moolenaar { XFF_GT_PLURAL, "p" }, 5071d1a0d267SMarcel Moolenaar { XFF_QUOTE, "q" }, 5072d1a0d267SMarcel Moolenaar { XFF_TRIM_WS, "t" }, 5073d1a0d267SMarcel Moolenaar { XFF_WS, "w" }, 5074d1a0d267SMarcel Moolenaar { 0, NULL } 5075d1a0d267SMarcel Moolenaar }; 5076d1a0d267SMarcel Moolenaar #endif /* NOT_NEEDED_YET */ 5077d1a0d267SMarcel Moolenaar 5078d1a0d267SMarcel Moolenaar static int 5079d1a0d267SMarcel Moolenaar xo_count_fields (xo_handle_t *xop UNUSED, const char *fmt) 5080d1a0d267SMarcel Moolenaar { 5081d1a0d267SMarcel Moolenaar int rc = 1; 5082d1a0d267SMarcel Moolenaar const char *cp; 5083d1a0d267SMarcel Moolenaar 5084d1a0d267SMarcel Moolenaar for (cp = fmt; *cp; cp++) 5085d1a0d267SMarcel Moolenaar if (*cp == '{' || *cp == '\n') 5086d1a0d267SMarcel Moolenaar rc += 1; 5087d1a0d267SMarcel Moolenaar 5088d1a0d267SMarcel Moolenaar return rc * 2 + 1; 5089d1a0d267SMarcel Moolenaar } 509031337658SMarcel Moolenaar 509131337658SMarcel Moolenaar /* 5092d1a0d267SMarcel Moolenaar * The field format is: 509331337658SMarcel Moolenaar * '{' modifiers ':' content [ '/' print-fmt [ '/' encode-fmt ]] '}' 5094d1a0d267SMarcel Moolenaar * Roles are optional and include the following field types: 509531337658SMarcel Moolenaar * 'D': decoration; something non-text and non-data (colons, commmas) 509631337658SMarcel Moolenaar * 'E': error message 5097d1a0d267SMarcel Moolenaar * 'G': gettext() the entire string; optional domainname as content 509831337658SMarcel Moolenaar * 'L': label; text preceding data 509931337658SMarcel Moolenaar * 'N': note; text following data 510031337658SMarcel Moolenaar * 'P': padding; whitespace 510131337658SMarcel Moolenaar * 'T': Title, where 'content' is a column title 510231337658SMarcel Moolenaar * 'U': Units, where 'content' is the unit label 510331337658SMarcel Moolenaar * 'V': value, where 'content' is the name of the field (the default) 510431337658SMarcel Moolenaar * 'W': warning message 510531337658SMarcel Moolenaar * '[': start a section of anchored text 510631337658SMarcel Moolenaar * ']': end a section of anchored text 5107d1a0d267SMarcel Moolenaar * The following modifiers are also supported: 510842ff34c3SPhil Shafer * 'a': content is provided via argument (const char *), not descriptor 510931337658SMarcel Moolenaar * 'c': flag: emit a colon after the label 5110d1a0d267SMarcel Moolenaar * 'd': field is only emitted for display styles (text and html) 5111d1a0d267SMarcel Moolenaar * 'e': field is only emitted for encoding styles (xml and json) 5112d1a0d267SMarcel Moolenaar * 'g': gettext() the field 5113d1a0d267SMarcel Moolenaar * 'h': humanize a numeric value (only for display styles) 511431337658SMarcel Moolenaar * 'k': this field is a key, suitable for XPath predicates 511531337658SMarcel Moolenaar * 'l': a leaf-list, a simple list of values 511631337658SMarcel Moolenaar * 'n': no quotes around this field 5117d1a0d267SMarcel Moolenaar * 'p': the field has plural gettext semantics (ngettext) 511831337658SMarcel Moolenaar * 'q': add quotes around this field 511931337658SMarcel Moolenaar * 't': trim whitespace around the value 512031337658SMarcel Moolenaar * 'w': emit a blank after the label 512131337658SMarcel Moolenaar * The print-fmt and encode-fmt strings is the printf-style formating 512231337658SMarcel Moolenaar * for this data. JSON and XML will use the encoding-fmt, if present. 512331337658SMarcel Moolenaar * If the encode-fmt is not provided, it defaults to the print-fmt. 512431337658SMarcel Moolenaar * If the print-fmt is not provided, it defaults to 's'. 512531337658SMarcel Moolenaar */ 5126d1a0d267SMarcel Moolenaar static const char * 5127d1a0d267SMarcel Moolenaar xo_parse_roles (xo_handle_t *xop, const char *fmt, 5128d1a0d267SMarcel Moolenaar const char *basep, xo_field_info_t *xfip) 5129d1a0d267SMarcel Moolenaar { 5130d1a0d267SMarcel Moolenaar const char *sp; 5131d1a0d267SMarcel Moolenaar unsigned ftype = 0; 5132d1a0d267SMarcel Moolenaar xo_xff_flags_t flags = 0; 5133d1a0d267SMarcel Moolenaar uint8_t fnum = 0; 513431337658SMarcel Moolenaar 513542ff34c3SPhil Shafer for (sp = basep; sp && *sp; sp++) { 513631337658SMarcel Moolenaar if (*sp == ':' || *sp == '/' || *sp == '}') 513731337658SMarcel Moolenaar break; 513831337658SMarcel Moolenaar 513931337658SMarcel Moolenaar if (*sp == '\\') { 514031337658SMarcel Moolenaar if (sp[1] == '\0') { 514131337658SMarcel Moolenaar xo_failure(xop, "backslash at the end of string"); 5142d1a0d267SMarcel Moolenaar return NULL; 514331337658SMarcel Moolenaar } 5144d1a0d267SMarcel Moolenaar 5145d1a0d267SMarcel Moolenaar /* Anything backslashed is ignored */ 514631337658SMarcel Moolenaar sp += 1; 514731337658SMarcel Moolenaar continue; 514831337658SMarcel Moolenaar } 514931337658SMarcel Moolenaar 5150d1a0d267SMarcel Moolenaar if (*sp == ',') { 5151d1a0d267SMarcel Moolenaar const char *np; 5152d1a0d267SMarcel Moolenaar for (np = ++sp; *np; np++) 5153d1a0d267SMarcel Moolenaar if (*np == ':' || *np == '/' || *np == '}' || *np == ',') 5154d1a0d267SMarcel Moolenaar break; 5155d1a0d267SMarcel Moolenaar 51568a6eceffSPhil Shafer ssize_t slen = np - sp; 5157d1a0d267SMarcel Moolenaar if (slen > 0) { 5158d1a0d267SMarcel Moolenaar xo_xff_flags_t value; 5159d1a0d267SMarcel Moolenaar 5160d1a0d267SMarcel Moolenaar value = xo_name_lookup(xo_role_names, sp, slen); 5161d1a0d267SMarcel Moolenaar if (value) 5162d1a0d267SMarcel Moolenaar ftype = value; 5163d1a0d267SMarcel Moolenaar else { 5164d1a0d267SMarcel Moolenaar value = xo_name_lookup(xo_modifier_names, sp, slen); 5165d1a0d267SMarcel Moolenaar if (value) 5166d1a0d267SMarcel Moolenaar flags |= value; 5167d1a0d267SMarcel Moolenaar else 5168d1a0d267SMarcel Moolenaar xo_failure(xop, "unknown keyword ignored: '%.*s'", 5169d1a0d267SMarcel Moolenaar slen, sp); 5170d1a0d267SMarcel Moolenaar } 5171d1a0d267SMarcel Moolenaar } 5172d1a0d267SMarcel Moolenaar 5173d1a0d267SMarcel Moolenaar sp = np - 1; 5174d1a0d267SMarcel Moolenaar continue; 5175d1a0d267SMarcel Moolenaar } 5176d1a0d267SMarcel Moolenaar 517731337658SMarcel Moolenaar switch (*sp) { 5178788ca347SMarcel Moolenaar case 'C': 517931337658SMarcel Moolenaar case 'D': 518031337658SMarcel Moolenaar case 'E': 5181d1a0d267SMarcel Moolenaar case 'G': 518231337658SMarcel Moolenaar case 'L': 518331337658SMarcel Moolenaar case 'N': 518431337658SMarcel Moolenaar case 'P': 518531337658SMarcel Moolenaar case 'T': 518631337658SMarcel Moolenaar case 'U': 518731337658SMarcel Moolenaar case 'V': 518831337658SMarcel Moolenaar case 'W': 518931337658SMarcel Moolenaar case '[': 519031337658SMarcel Moolenaar case ']': 519131337658SMarcel Moolenaar if (ftype != 0) { 5192d1a0d267SMarcel Moolenaar xo_failure(xop, "field descriptor uses multiple types: '%s'", 5193d1a0d267SMarcel Moolenaar xo_printable(fmt)); 5194d1a0d267SMarcel Moolenaar return NULL; 519531337658SMarcel Moolenaar } 519631337658SMarcel Moolenaar ftype = *sp; 519731337658SMarcel Moolenaar break; 519831337658SMarcel Moolenaar 5199d1a0d267SMarcel Moolenaar case '0': 5200d1a0d267SMarcel Moolenaar case '1': 5201d1a0d267SMarcel Moolenaar case '2': 5202d1a0d267SMarcel Moolenaar case '3': 5203d1a0d267SMarcel Moolenaar case '4': 5204d1a0d267SMarcel Moolenaar case '5': 5205d1a0d267SMarcel Moolenaar case '6': 5206d1a0d267SMarcel Moolenaar case '7': 5207d1a0d267SMarcel Moolenaar case '8': 5208d1a0d267SMarcel Moolenaar case '9': 5209d1a0d267SMarcel Moolenaar fnum = (fnum * 10) + (*sp - '0'); 5210d1a0d267SMarcel Moolenaar break; 5211d1a0d267SMarcel Moolenaar 521242ff34c3SPhil Shafer case 'a': 521342ff34c3SPhil Shafer flags |= XFF_ARGUMENT; 521442ff34c3SPhil Shafer break; 521542ff34c3SPhil Shafer 521631337658SMarcel Moolenaar case 'c': 521731337658SMarcel Moolenaar flags |= XFF_COLON; 521831337658SMarcel Moolenaar break; 521931337658SMarcel Moolenaar 522031337658SMarcel Moolenaar case 'd': 522131337658SMarcel Moolenaar flags |= XFF_DISPLAY_ONLY; 522231337658SMarcel Moolenaar break; 522331337658SMarcel Moolenaar 522431337658SMarcel Moolenaar case 'e': 522531337658SMarcel Moolenaar flags |= XFF_ENCODE_ONLY; 522631337658SMarcel Moolenaar break; 522731337658SMarcel Moolenaar 5228d1a0d267SMarcel Moolenaar case 'g': 5229d1a0d267SMarcel Moolenaar flags |= XFF_GT_FIELD; 5230d1a0d267SMarcel Moolenaar break; 5231d1a0d267SMarcel Moolenaar 5232d1a0d267SMarcel Moolenaar case 'h': 5233d1a0d267SMarcel Moolenaar flags |= XFF_HUMANIZE; 5234d1a0d267SMarcel Moolenaar break; 5235d1a0d267SMarcel Moolenaar 523631337658SMarcel Moolenaar case 'k': 523731337658SMarcel Moolenaar flags |= XFF_KEY; 523831337658SMarcel Moolenaar break; 523931337658SMarcel Moolenaar 524031337658SMarcel Moolenaar case 'l': 524131337658SMarcel Moolenaar flags |= XFF_LEAF_LIST; 524231337658SMarcel Moolenaar break; 524331337658SMarcel Moolenaar 524431337658SMarcel Moolenaar case 'n': 524531337658SMarcel Moolenaar flags |= XFF_NOQUOTE; 524631337658SMarcel Moolenaar break; 524731337658SMarcel Moolenaar 5248d1a0d267SMarcel Moolenaar case 'p': 5249d1a0d267SMarcel Moolenaar flags |= XFF_GT_PLURAL; 5250d1a0d267SMarcel Moolenaar break; 5251d1a0d267SMarcel Moolenaar 525231337658SMarcel Moolenaar case 'q': 525331337658SMarcel Moolenaar flags |= XFF_QUOTE; 525431337658SMarcel Moolenaar break; 525531337658SMarcel Moolenaar 525631337658SMarcel Moolenaar case 't': 525731337658SMarcel Moolenaar flags |= XFF_TRIM_WS; 525831337658SMarcel Moolenaar break; 525931337658SMarcel Moolenaar 526031337658SMarcel Moolenaar case 'w': 526131337658SMarcel Moolenaar flags |= XFF_WS; 526231337658SMarcel Moolenaar break; 526331337658SMarcel Moolenaar 526431337658SMarcel Moolenaar default: 5265d1a0d267SMarcel Moolenaar xo_failure(xop, "field descriptor uses unknown modifier: '%s'", 5266d1a0d267SMarcel Moolenaar xo_printable(fmt)); 526731337658SMarcel Moolenaar /* 526831337658SMarcel Moolenaar * No good answer here; a bad format will likely 526931337658SMarcel Moolenaar * mean a core file. We just return and hope 527031337658SMarcel Moolenaar * the caller notices there's no output, and while 5271d1a0d267SMarcel Moolenaar * that seems, well, bad, there's nothing better. 527231337658SMarcel Moolenaar */ 5273d1a0d267SMarcel Moolenaar return NULL; 5274d1a0d267SMarcel Moolenaar } 5275d1a0d267SMarcel Moolenaar 5276d1a0d267SMarcel Moolenaar if (ftype == 'N' || ftype == 'U') { 5277d1a0d267SMarcel Moolenaar if (flags & XFF_COLON) { 5278d1a0d267SMarcel Moolenaar xo_failure(xop, "colon modifier on 'N' or 'U' field ignored: " 5279d1a0d267SMarcel Moolenaar "'%s'", xo_printable(fmt)); 5280d1a0d267SMarcel Moolenaar flags &= ~XFF_COLON; 5281d1a0d267SMarcel Moolenaar } 528231337658SMarcel Moolenaar } 528331337658SMarcel Moolenaar } 528431337658SMarcel Moolenaar 5285d1a0d267SMarcel Moolenaar xfip->xfi_flags = flags; 5286d1a0d267SMarcel Moolenaar xfip->xfi_ftype = ftype ?: 'V'; 5287d1a0d267SMarcel Moolenaar xfip->xfi_fnum = fnum; 5288d1a0d267SMarcel Moolenaar 5289d1a0d267SMarcel Moolenaar return sp; 5290d1a0d267SMarcel Moolenaar } 5291d1a0d267SMarcel Moolenaar 5292d1a0d267SMarcel Moolenaar /* 5293d1a0d267SMarcel Moolenaar * Number any remaining fields that need numbers. Note that some 5294d1a0d267SMarcel Moolenaar * field types (text, newline, escaped braces) never get numbers. 5295d1a0d267SMarcel Moolenaar */ 5296d1a0d267SMarcel Moolenaar static void 5297d1a0d267SMarcel Moolenaar xo_gettext_finish_numbering_fields (xo_handle_t *xop UNUSED, 5298d1a0d267SMarcel Moolenaar const char *fmt UNUSED, 5299d1a0d267SMarcel Moolenaar xo_field_info_t *fields) 5300d1a0d267SMarcel Moolenaar { 5301d1a0d267SMarcel Moolenaar xo_field_info_t *xfip; 5302d1a0d267SMarcel Moolenaar unsigned fnum, max_fields; 5303d1a0d267SMarcel Moolenaar uint64_t bits = 0; 5304d1a0d267SMarcel Moolenaar 5305d1a0d267SMarcel Moolenaar /* First make a list of add the explicitly used bits */ 5306d1a0d267SMarcel Moolenaar for (xfip = fields, fnum = 0; xfip->xfi_ftype; xfip++) { 5307d1a0d267SMarcel Moolenaar switch (xfip->xfi_ftype) { 5308d1a0d267SMarcel Moolenaar case XO_ROLE_NEWLINE: /* Don't get numbered */ 5309d1a0d267SMarcel Moolenaar case XO_ROLE_TEXT: 5310d1a0d267SMarcel Moolenaar case XO_ROLE_EBRACE: 5311d1a0d267SMarcel Moolenaar case 'G': 5312d1a0d267SMarcel Moolenaar continue; 5313d1a0d267SMarcel Moolenaar } 5314d1a0d267SMarcel Moolenaar 5315d1a0d267SMarcel Moolenaar fnum += 1; 5316d1a0d267SMarcel Moolenaar if (fnum >= 63) 5317d1a0d267SMarcel Moolenaar break; 5318d1a0d267SMarcel Moolenaar 5319d1a0d267SMarcel Moolenaar if (xfip->xfi_fnum) 5320d1a0d267SMarcel Moolenaar bits |= 1 << xfip->xfi_fnum; 5321d1a0d267SMarcel Moolenaar } 5322d1a0d267SMarcel Moolenaar 5323d1a0d267SMarcel Moolenaar max_fields = fnum; 5324d1a0d267SMarcel Moolenaar 5325d1a0d267SMarcel Moolenaar for (xfip = fields, fnum = 0; xfip->xfi_ftype; xfip++) { 5326d1a0d267SMarcel Moolenaar switch (xfip->xfi_ftype) { 5327d1a0d267SMarcel Moolenaar case XO_ROLE_NEWLINE: /* Don't get numbered */ 5328d1a0d267SMarcel Moolenaar case XO_ROLE_TEXT: 5329d1a0d267SMarcel Moolenaar case XO_ROLE_EBRACE: 5330d1a0d267SMarcel Moolenaar case 'G': 5331d1a0d267SMarcel Moolenaar continue; 5332d1a0d267SMarcel Moolenaar } 5333d1a0d267SMarcel Moolenaar 5334d1a0d267SMarcel Moolenaar if (xfip->xfi_fnum != 0) 5335d1a0d267SMarcel Moolenaar continue; 5336d1a0d267SMarcel Moolenaar 5337d1a0d267SMarcel Moolenaar /* Find the next unassigned field */ 5338d1a0d267SMarcel Moolenaar for (fnum++; bits & (1 << fnum); fnum++) 5339d1a0d267SMarcel Moolenaar continue; 5340d1a0d267SMarcel Moolenaar 5341d1a0d267SMarcel Moolenaar if (fnum > max_fields) 5342d1a0d267SMarcel Moolenaar break; 5343d1a0d267SMarcel Moolenaar 5344d1a0d267SMarcel Moolenaar xfip->xfi_fnum = fnum; /* Mark the field number */ 5345d1a0d267SMarcel Moolenaar bits |= 1 << fnum; /* Mark it used */ 5346d1a0d267SMarcel Moolenaar } 5347d1a0d267SMarcel Moolenaar } 5348d1a0d267SMarcel Moolenaar 5349d1a0d267SMarcel Moolenaar /* 5350ee5cf116SPhil Shafer * The format string uses field numbers, so we need to whiffle through it 5351d1a0d267SMarcel Moolenaar * and make sure everything's sane and lovely. 5352d1a0d267SMarcel Moolenaar */ 5353d1a0d267SMarcel Moolenaar static int 5354d1a0d267SMarcel Moolenaar xo_parse_field_numbers (xo_handle_t *xop, const char *fmt, 5355d1a0d267SMarcel Moolenaar xo_field_info_t *fields, unsigned num_fields) 5356d1a0d267SMarcel Moolenaar { 5357d1a0d267SMarcel Moolenaar xo_field_info_t *xfip; 5358d1a0d267SMarcel Moolenaar unsigned field, fnum; 5359d1a0d267SMarcel Moolenaar uint64_t bits = 0; 5360d1a0d267SMarcel Moolenaar 5361d1a0d267SMarcel Moolenaar for (xfip = fields, field = 0; field < num_fields; xfip++, field++) { 5362d1a0d267SMarcel Moolenaar /* Fields default to 1:1 with natural position */ 5363d1a0d267SMarcel Moolenaar if (xfip->xfi_fnum == 0) 5364d1a0d267SMarcel Moolenaar xfip->xfi_fnum = field + 1; 5365d1a0d267SMarcel Moolenaar else if (xfip->xfi_fnum > num_fields) { 5366d1a0d267SMarcel Moolenaar xo_failure(xop, "field number exceeds number of fields: '%s'", fmt); 5367d1a0d267SMarcel Moolenaar return -1; 5368d1a0d267SMarcel Moolenaar } 5369d1a0d267SMarcel Moolenaar 5370d1a0d267SMarcel Moolenaar fnum = xfip->xfi_fnum - 1; /* Move to zero origin */ 5371d1a0d267SMarcel Moolenaar if (fnum < 64) { /* Only test what fits */ 5372d1a0d267SMarcel Moolenaar if (bits & (1 << fnum)) { 5373d1a0d267SMarcel Moolenaar xo_failure(xop, "field number %u reused: '%s'", 5374d1a0d267SMarcel Moolenaar xfip->xfi_fnum, fmt); 5375d1a0d267SMarcel Moolenaar return -1; 5376d1a0d267SMarcel Moolenaar } 5377d1a0d267SMarcel Moolenaar bits |= 1 << fnum; 5378d1a0d267SMarcel Moolenaar } 5379d1a0d267SMarcel Moolenaar } 5380d1a0d267SMarcel Moolenaar 5381d1a0d267SMarcel Moolenaar return 0; 5382d1a0d267SMarcel Moolenaar } 5383d1a0d267SMarcel Moolenaar 5384d1a0d267SMarcel Moolenaar static int 5385d1a0d267SMarcel Moolenaar xo_parse_fields (xo_handle_t *xop, xo_field_info_t *fields, 5386d1a0d267SMarcel Moolenaar unsigned num_fields, const char *fmt) 5387d1a0d267SMarcel Moolenaar { 5388d1a0d267SMarcel Moolenaar const char *cp, *sp, *ep, *basep; 5389d1a0d267SMarcel Moolenaar unsigned field = 0; 5390d1a0d267SMarcel Moolenaar xo_field_info_t *xfip = fields; 5391d1a0d267SMarcel Moolenaar unsigned seen_fnum = 0; 5392d1a0d267SMarcel Moolenaar 5393d1a0d267SMarcel Moolenaar for (cp = fmt; *cp && field < num_fields; field++, xfip++) { 5394d1a0d267SMarcel Moolenaar xfip->xfi_start = cp; 5395d1a0d267SMarcel Moolenaar 5396d1a0d267SMarcel Moolenaar if (*cp == '\n') { 5397d1a0d267SMarcel Moolenaar xfip->xfi_ftype = XO_ROLE_NEWLINE; 5398d1a0d267SMarcel Moolenaar xfip->xfi_len = 1; 5399d1a0d267SMarcel Moolenaar cp += 1; 5400d1a0d267SMarcel Moolenaar continue; 5401d1a0d267SMarcel Moolenaar } 5402d1a0d267SMarcel Moolenaar 5403d1a0d267SMarcel Moolenaar if (*cp != '{') { 5404d1a0d267SMarcel Moolenaar /* Normal text */ 5405d1a0d267SMarcel Moolenaar for (sp = cp; *sp; sp++) { 5406d1a0d267SMarcel Moolenaar if (*sp == '{' || *sp == '\n') 5407d1a0d267SMarcel Moolenaar break; 5408d1a0d267SMarcel Moolenaar } 5409d1a0d267SMarcel Moolenaar 5410d1a0d267SMarcel Moolenaar xfip->xfi_ftype = XO_ROLE_TEXT; 5411d1a0d267SMarcel Moolenaar xfip->xfi_content = cp; 5412d1a0d267SMarcel Moolenaar xfip->xfi_clen = sp - cp; 5413d1a0d267SMarcel Moolenaar xfip->xfi_next = sp; 5414d1a0d267SMarcel Moolenaar 5415d1a0d267SMarcel Moolenaar cp = sp; 5416d1a0d267SMarcel Moolenaar continue; 5417d1a0d267SMarcel Moolenaar } 5418d1a0d267SMarcel Moolenaar 5419d1a0d267SMarcel Moolenaar if (cp[1] == '{') { /* Start of {{escaped braces}} */ 5420d1a0d267SMarcel Moolenaar xfip->xfi_start = cp + 1; /* Start at second brace */ 5421d1a0d267SMarcel Moolenaar xfip->xfi_ftype = XO_ROLE_EBRACE; 5422d1a0d267SMarcel Moolenaar 5423d1a0d267SMarcel Moolenaar cp += 2; /* Skip over _both_ characters */ 5424d1a0d267SMarcel Moolenaar for (sp = cp; *sp; sp++) { 5425d1a0d267SMarcel Moolenaar if (*sp == '}' && sp[1] == '}') 5426d1a0d267SMarcel Moolenaar break; 5427d1a0d267SMarcel Moolenaar } 5428d1a0d267SMarcel Moolenaar if (*sp == '\0') { 5429d1a0d267SMarcel Moolenaar xo_failure(xop, "missing closing '}}': '%s'", 5430d1a0d267SMarcel Moolenaar xo_printable(fmt)); 5431d1a0d267SMarcel Moolenaar return -1; 5432d1a0d267SMarcel Moolenaar } 5433d1a0d267SMarcel Moolenaar 5434d1a0d267SMarcel Moolenaar xfip->xfi_len = sp - xfip->xfi_start + 1; 5435d1a0d267SMarcel Moolenaar 5436d1a0d267SMarcel Moolenaar /* Move along the string, but don't run off the end */ 5437d1a0d267SMarcel Moolenaar if (*sp == '}' && sp[1] == '}') 5438d1a0d267SMarcel Moolenaar sp += 2; 5439d1a0d267SMarcel Moolenaar cp = *sp ? sp : sp; 5440d1a0d267SMarcel Moolenaar xfip->xfi_next = cp; 5441d1a0d267SMarcel Moolenaar continue; 5442d1a0d267SMarcel Moolenaar } 5443d1a0d267SMarcel Moolenaar 5444d1a0d267SMarcel Moolenaar /* We are looking at the start of a field definition */ 5445d1a0d267SMarcel Moolenaar xfip->xfi_start = basep = cp + 1; 5446d1a0d267SMarcel Moolenaar 5447d1a0d267SMarcel Moolenaar const char *format = NULL; 54488a6eceffSPhil Shafer ssize_t flen = 0; 5449d1a0d267SMarcel Moolenaar 5450d1a0d267SMarcel Moolenaar /* Looking at roles and modifiers */ 5451d1a0d267SMarcel Moolenaar sp = xo_parse_roles(xop, fmt, basep, xfip); 5452d1a0d267SMarcel Moolenaar if (sp == NULL) { 5453d1a0d267SMarcel Moolenaar /* xo_failure has already been called */ 5454d1a0d267SMarcel Moolenaar return -1; 5455d1a0d267SMarcel Moolenaar } 5456d1a0d267SMarcel Moolenaar 5457d1a0d267SMarcel Moolenaar if (xfip->xfi_fnum) 5458d1a0d267SMarcel Moolenaar seen_fnum = 1; 5459d1a0d267SMarcel Moolenaar 5460d1a0d267SMarcel Moolenaar /* Looking at content */ 546131337658SMarcel Moolenaar if (*sp == ':') { 546231337658SMarcel Moolenaar for (ep = ++sp; *sp; sp++) { 546331337658SMarcel Moolenaar if (*sp == '}' || *sp == '/') 546431337658SMarcel Moolenaar break; 546531337658SMarcel Moolenaar if (*sp == '\\') { 546631337658SMarcel Moolenaar if (sp[1] == '\0') { 546731337658SMarcel Moolenaar xo_failure(xop, "backslash at the end of string"); 546831337658SMarcel Moolenaar return -1; 546931337658SMarcel Moolenaar } 547031337658SMarcel Moolenaar sp += 1; 547131337658SMarcel Moolenaar continue; 547231337658SMarcel Moolenaar } 547331337658SMarcel Moolenaar } 547431337658SMarcel Moolenaar if (ep != sp) { 5475d1a0d267SMarcel Moolenaar xfip->xfi_clen = sp - ep; 5476d1a0d267SMarcel Moolenaar xfip->xfi_content = ep; 547731337658SMarcel Moolenaar } 547831337658SMarcel Moolenaar } else { 5479d1a0d267SMarcel Moolenaar xo_failure(xop, "missing content (':'): '%s'", xo_printable(fmt)); 548031337658SMarcel Moolenaar return -1; 548131337658SMarcel Moolenaar } 548231337658SMarcel Moolenaar 5483d1a0d267SMarcel Moolenaar /* Looking at main (display) format */ 548431337658SMarcel Moolenaar if (*sp == '/') { 548531337658SMarcel Moolenaar for (ep = ++sp; *sp; sp++) { 548631337658SMarcel Moolenaar if (*sp == '}' || *sp == '/') 548731337658SMarcel Moolenaar break; 548831337658SMarcel Moolenaar if (*sp == '\\') { 548931337658SMarcel Moolenaar if (sp[1] == '\0') { 549031337658SMarcel Moolenaar xo_failure(xop, "backslash at the end of string"); 549131337658SMarcel Moolenaar return -1; 549231337658SMarcel Moolenaar } 549331337658SMarcel Moolenaar sp += 1; 549431337658SMarcel Moolenaar continue; 549531337658SMarcel Moolenaar } 549631337658SMarcel Moolenaar } 549731337658SMarcel Moolenaar flen = sp - ep; 549831337658SMarcel Moolenaar format = ep; 549931337658SMarcel Moolenaar } 550031337658SMarcel Moolenaar 5501d1a0d267SMarcel Moolenaar /* Looking at encoding format */ 550231337658SMarcel Moolenaar if (*sp == '/') { 550331337658SMarcel Moolenaar for (ep = ++sp; *sp; sp++) { 550431337658SMarcel Moolenaar if (*sp == '}') 550531337658SMarcel Moolenaar break; 550631337658SMarcel Moolenaar } 5507d1a0d267SMarcel Moolenaar 5508d1a0d267SMarcel Moolenaar xfip->xfi_encoding = ep; 5509d1a0d267SMarcel Moolenaar xfip->xfi_elen = sp - ep; 551031337658SMarcel Moolenaar } 551131337658SMarcel Moolenaar 5512d1a0d267SMarcel Moolenaar if (*sp != '}') { 5513d1a0d267SMarcel Moolenaar xo_failure(xop, "missing closing '}': %s", xo_printable(fmt)); 551431337658SMarcel Moolenaar return -1; 551531337658SMarcel Moolenaar } 551631337658SMarcel Moolenaar 5517d1a0d267SMarcel Moolenaar xfip->xfi_len = sp - xfip->xfi_start; 5518d1a0d267SMarcel Moolenaar xfip->xfi_next = ++sp; 5519d1a0d267SMarcel Moolenaar 5520d1a0d267SMarcel Moolenaar /* If we have content, then we have a default format */ 552142ff34c3SPhil Shafer if (xfip->xfi_clen || format || (xfip->xfi_flags & XFF_ARGUMENT)) { 5522d1a0d267SMarcel Moolenaar if (format) { 5523d1a0d267SMarcel Moolenaar xfip->xfi_format = format; 5524d1a0d267SMarcel Moolenaar xfip->xfi_flen = flen; 5525d1a0d267SMarcel Moolenaar } else if (xo_role_wants_default_format(xfip->xfi_ftype)) { 552642ff34c3SPhil Shafer xfip->xfi_format = xo_default_format; 5527d1a0d267SMarcel Moolenaar xfip->xfi_flen = 2; 5528d1a0d267SMarcel Moolenaar } 552931337658SMarcel Moolenaar } 553031337658SMarcel Moolenaar 5531d1a0d267SMarcel Moolenaar cp = sp; 5532d1a0d267SMarcel Moolenaar } 5533545ddfbeSMarcel Moolenaar 5534d1a0d267SMarcel Moolenaar int rc = 0; 5535d1a0d267SMarcel Moolenaar 5536d1a0d267SMarcel Moolenaar /* 5537d1a0d267SMarcel Moolenaar * If we saw a field number on at least one field, then we need 5538d1a0d267SMarcel Moolenaar * to enforce some rules and/or guidelines. 5539d1a0d267SMarcel Moolenaar */ 5540d1a0d267SMarcel Moolenaar if (seen_fnum) 5541d1a0d267SMarcel Moolenaar rc = xo_parse_field_numbers(xop, fmt, fields, field); 5542d1a0d267SMarcel Moolenaar 5543d1a0d267SMarcel Moolenaar return rc; 5544d1a0d267SMarcel Moolenaar } 5545d1a0d267SMarcel Moolenaar 5546d1a0d267SMarcel Moolenaar /* 5547d1a0d267SMarcel Moolenaar * We are passed a pointer to a format string just past the "{G:}" 5548d1a0d267SMarcel Moolenaar * field. We build a simplified version of the format string. 5549d1a0d267SMarcel Moolenaar */ 5550d1a0d267SMarcel Moolenaar static int 5551d1a0d267SMarcel Moolenaar xo_gettext_simplify_format (xo_handle_t *xop UNUSED, 5552d1a0d267SMarcel Moolenaar xo_buffer_t *xbp, 5553d1a0d267SMarcel Moolenaar xo_field_info_t *fields, 5554d1a0d267SMarcel Moolenaar int this_field, 5555d1a0d267SMarcel Moolenaar const char *fmt UNUSED, 5556d1a0d267SMarcel Moolenaar xo_simplify_field_func_t field_cb) 5557d1a0d267SMarcel Moolenaar { 5558d1a0d267SMarcel Moolenaar unsigned ftype; 5559d1a0d267SMarcel Moolenaar xo_xff_flags_t flags; 5560d1a0d267SMarcel Moolenaar int field = this_field + 1; 5561d1a0d267SMarcel Moolenaar xo_field_info_t *xfip; 5562d1a0d267SMarcel Moolenaar char ch; 5563d1a0d267SMarcel Moolenaar 5564d1a0d267SMarcel Moolenaar for (xfip = &fields[field]; xfip->xfi_ftype; xfip++, field++) { 5565d1a0d267SMarcel Moolenaar ftype = xfip->xfi_ftype; 5566d1a0d267SMarcel Moolenaar flags = xfip->xfi_flags; 5567d1a0d267SMarcel Moolenaar 5568d1a0d267SMarcel Moolenaar if ((flags & XFF_GT_FIELD) && xfip->xfi_content && ftype != 'V') { 5569d1a0d267SMarcel Moolenaar if (field_cb) 5570d1a0d267SMarcel Moolenaar field_cb(xfip->xfi_content, xfip->xfi_clen, 5571d1a0d267SMarcel Moolenaar (flags & XFF_GT_PLURAL) ? 1 : 0); 5572d1a0d267SMarcel Moolenaar } 5573d1a0d267SMarcel Moolenaar 5574d1a0d267SMarcel Moolenaar switch (ftype) { 5575d1a0d267SMarcel Moolenaar case 'G': 5576d1a0d267SMarcel Moolenaar /* Ignore gettext roles */ 5577d1a0d267SMarcel Moolenaar break; 5578d1a0d267SMarcel Moolenaar 5579d1a0d267SMarcel Moolenaar case XO_ROLE_NEWLINE: 5580d1a0d267SMarcel Moolenaar xo_buf_append(xbp, "\n", 1); 5581d1a0d267SMarcel Moolenaar break; 5582d1a0d267SMarcel Moolenaar 5583d1a0d267SMarcel Moolenaar case XO_ROLE_EBRACE: 5584d1a0d267SMarcel Moolenaar xo_buf_append(xbp, "{", 1); 5585d1a0d267SMarcel Moolenaar xo_buf_append(xbp, xfip->xfi_content, xfip->xfi_clen); 5586d1a0d267SMarcel Moolenaar xo_buf_append(xbp, "}", 1); 5587d1a0d267SMarcel Moolenaar break; 5588d1a0d267SMarcel Moolenaar 5589d1a0d267SMarcel Moolenaar case XO_ROLE_TEXT: 5590d1a0d267SMarcel Moolenaar xo_buf_append(xbp, xfip->xfi_content, xfip->xfi_clen); 5591d1a0d267SMarcel Moolenaar break; 5592d1a0d267SMarcel Moolenaar 5593d1a0d267SMarcel Moolenaar default: 5594d1a0d267SMarcel Moolenaar xo_buf_append(xbp, "{", 1); 5595d1a0d267SMarcel Moolenaar if (ftype != 'V') { 5596d1a0d267SMarcel Moolenaar ch = ftype; 5597d1a0d267SMarcel Moolenaar xo_buf_append(xbp, &ch, 1); 5598d1a0d267SMarcel Moolenaar } 5599d1a0d267SMarcel Moolenaar 5600d1a0d267SMarcel Moolenaar unsigned fnum = xfip->xfi_fnum ?: 0; 5601d1a0d267SMarcel Moolenaar if (fnum) { 5602d1a0d267SMarcel Moolenaar char num[12]; 5603d1a0d267SMarcel Moolenaar /* Field numbers are origin 1, not 0, following printf(3) */ 5604d1a0d267SMarcel Moolenaar snprintf(num, sizeof(num), "%u", fnum); 5605d1a0d267SMarcel Moolenaar xo_buf_append(xbp, num, strlen(num)); 5606d1a0d267SMarcel Moolenaar } 5607d1a0d267SMarcel Moolenaar 5608d1a0d267SMarcel Moolenaar xo_buf_append(xbp, ":", 1); 5609d1a0d267SMarcel Moolenaar xo_buf_append(xbp, xfip->xfi_content, xfip->xfi_clen); 5610d1a0d267SMarcel Moolenaar xo_buf_append(xbp, "}", 1); 5611d1a0d267SMarcel Moolenaar } 5612d1a0d267SMarcel Moolenaar } 5613d1a0d267SMarcel Moolenaar 5614d1a0d267SMarcel Moolenaar xo_buf_append(xbp, "", 1); 5615d1a0d267SMarcel Moolenaar return 0; 5616d1a0d267SMarcel Moolenaar } 5617d1a0d267SMarcel Moolenaar 5618d1a0d267SMarcel Moolenaar void 5619d1a0d267SMarcel Moolenaar xo_dump_fields (xo_field_info_t *); /* Fake prototype for debug function */ 5620d1a0d267SMarcel Moolenaar void 5621d1a0d267SMarcel Moolenaar xo_dump_fields (xo_field_info_t *fields) 5622d1a0d267SMarcel Moolenaar { 5623d1a0d267SMarcel Moolenaar xo_field_info_t *xfip; 5624d1a0d267SMarcel Moolenaar 5625d1a0d267SMarcel Moolenaar for (xfip = fields; xfip->xfi_ftype; xfip++) { 5626d1a0d267SMarcel Moolenaar printf("%lu(%u): %lx [%c/%u] [%.*s] [%.*s] [%.*s]\n", 5627d1a0d267SMarcel Moolenaar (unsigned long) (xfip - fields), xfip->xfi_fnum, 5628d1a0d267SMarcel Moolenaar (unsigned long) xfip->xfi_flags, 5629d1a0d267SMarcel Moolenaar isprint((int) xfip->xfi_ftype) ? xfip->xfi_ftype : ' ', 5630d1a0d267SMarcel Moolenaar xfip->xfi_ftype, 56318a6eceffSPhil Shafer (int) xfip->xfi_clen, xfip->xfi_content ?: "", 56328a6eceffSPhil Shafer (int) xfip->xfi_flen, xfip->xfi_format ?: "", 56338a6eceffSPhil Shafer (int) xfip->xfi_elen, xfip->xfi_encoding ?: ""); 5634d1a0d267SMarcel Moolenaar } 5635d1a0d267SMarcel Moolenaar } 5636d1a0d267SMarcel Moolenaar 5637d1a0d267SMarcel Moolenaar #ifdef HAVE_GETTEXT 5638d1a0d267SMarcel Moolenaar /* 5639d1a0d267SMarcel Moolenaar * Find the field that matches the given field number 5640d1a0d267SMarcel Moolenaar */ 5641d1a0d267SMarcel Moolenaar static xo_field_info_t * 5642d1a0d267SMarcel Moolenaar xo_gettext_find_field (xo_field_info_t *fields, unsigned fnum) 5643d1a0d267SMarcel Moolenaar { 5644d1a0d267SMarcel Moolenaar xo_field_info_t *xfip; 5645d1a0d267SMarcel Moolenaar 5646d1a0d267SMarcel Moolenaar for (xfip = fields; xfip->xfi_ftype; xfip++) 5647d1a0d267SMarcel Moolenaar if (xfip->xfi_fnum == fnum) 5648d1a0d267SMarcel Moolenaar return xfip; 5649d1a0d267SMarcel Moolenaar 5650d1a0d267SMarcel Moolenaar return NULL; 5651d1a0d267SMarcel Moolenaar } 5652d1a0d267SMarcel Moolenaar 5653d1a0d267SMarcel Moolenaar /* 5654d1a0d267SMarcel Moolenaar * At this point, we need to consider if the fields have been reordered, 5655d1a0d267SMarcel Moolenaar * such as "The {:adjective} {:noun}" to "La {:noun} {:adjective}". 5656d1a0d267SMarcel Moolenaar * 5657d1a0d267SMarcel Moolenaar * We need to rewrite the new_fields using the old fields order, 5658d1a0d267SMarcel Moolenaar * so that we can render the message using the arguments as they 5659d1a0d267SMarcel Moolenaar * appear on the stack. It's a lot of work, but we don't really 5660d1a0d267SMarcel Moolenaar * want to (eventually) fall into the standard printf code which 5661d1a0d267SMarcel Moolenaar * means using the arguments straight (and in order) from the 5662d1a0d267SMarcel Moolenaar * varargs we were originally passed. 5663d1a0d267SMarcel Moolenaar */ 5664d1a0d267SMarcel Moolenaar static void 5665d1a0d267SMarcel Moolenaar xo_gettext_rewrite_fields (xo_handle_t *xop UNUSED, 5666d1a0d267SMarcel Moolenaar xo_field_info_t *fields, unsigned max_fields) 5667d1a0d267SMarcel Moolenaar { 5668d1a0d267SMarcel Moolenaar xo_field_info_t tmp[max_fields]; 5669d1a0d267SMarcel Moolenaar bzero(tmp, max_fields * sizeof(tmp[0])); 5670d1a0d267SMarcel Moolenaar 5671d1a0d267SMarcel Moolenaar unsigned fnum = 0; 5672d1a0d267SMarcel Moolenaar xo_field_info_t *newp, *outp, *zp; 5673d1a0d267SMarcel Moolenaar for (newp = fields, outp = tmp; newp->xfi_ftype; newp++, outp++) { 5674d1a0d267SMarcel Moolenaar switch (newp->xfi_ftype) { 5675d1a0d267SMarcel Moolenaar case XO_ROLE_NEWLINE: /* Don't get numbered */ 5676d1a0d267SMarcel Moolenaar case XO_ROLE_TEXT: 5677d1a0d267SMarcel Moolenaar case XO_ROLE_EBRACE: 5678d1a0d267SMarcel Moolenaar case 'G': 5679d1a0d267SMarcel Moolenaar *outp = *newp; 5680d1a0d267SMarcel Moolenaar outp->xfi_renum = 0; 5681d1a0d267SMarcel Moolenaar continue; 5682d1a0d267SMarcel Moolenaar } 5683d1a0d267SMarcel Moolenaar 5684d1a0d267SMarcel Moolenaar zp = xo_gettext_find_field(fields, ++fnum); 5685d1a0d267SMarcel Moolenaar if (zp == NULL) { /* Should not occur */ 5686d1a0d267SMarcel Moolenaar *outp = *newp; 5687d1a0d267SMarcel Moolenaar outp->xfi_renum = 0; 5688d1a0d267SMarcel Moolenaar continue; 5689d1a0d267SMarcel Moolenaar } 5690d1a0d267SMarcel Moolenaar 5691d1a0d267SMarcel Moolenaar *outp = *zp; 5692d1a0d267SMarcel Moolenaar outp->xfi_renum = newp->xfi_fnum; 5693d1a0d267SMarcel Moolenaar } 5694d1a0d267SMarcel Moolenaar 5695d1a0d267SMarcel Moolenaar memcpy(fields, tmp, max_fields * sizeof(tmp[0])); 5696d1a0d267SMarcel Moolenaar } 5697d1a0d267SMarcel Moolenaar 5698d1a0d267SMarcel Moolenaar /* 5699d1a0d267SMarcel Moolenaar * We've got two lists of fields, the old list from the original 5700d1a0d267SMarcel Moolenaar * format string and the new one from the parsed gettext reply. The 5701d1a0d267SMarcel Moolenaar * new list has the localized words, where the old list has the 5702d1a0d267SMarcel Moolenaar * formatting information. We need to combine them into a single list 5703d1a0d267SMarcel Moolenaar * (the new list). 5704d1a0d267SMarcel Moolenaar * 5705d1a0d267SMarcel Moolenaar * If the list needs to be reordered, then we've got more serious work 5706d1a0d267SMarcel Moolenaar * to do. 5707d1a0d267SMarcel Moolenaar */ 5708d1a0d267SMarcel Moolenaar static int 5709d1a0d267SMarcel Moolenaar xo_gettext_combine_formats (xo_handle_t *xop, const char *fmt UNUSED, 5710d1a0d267SMarcel Moolenaar const char *gtfmt, xo_field_info_t *old_fields, 5711d1a0d267SMarcel Moolenaar xo_field_info_t *new_fields, unsigned new_max_fields, 5712d1a0d267SMarcel Moolenaar int *reorderedp) 5713d1a0d267SMarcel Moolenaar { 5714d1a0d267SMarcel Moolenaar int reordered = 0; 5715d1a0d267SMarcel Moolenaar xo_field_info_t *newp, *oldp, *startp = old_fields; 5716d1a0d267SMarcel Moolenaar 5717d1a0d267SMarcel Moolenaar xo_gettext_finish_numbering_fields(xop, fmt, old_fields); 5718d1a0d267SMarcel Moolenaar 5719d1a0d267SMarcel Moolenaar for (newp = new_fields; newp->xfi_ftype; newp++) { 5720d1a0d267SMarcel Moolenaar switch (newp->xfi_ftype) { 5721d1a0d267SMarcel Moolenaar case XO_ROLE_NEWLINE: 5722d1a0d267SMarcel Moolenaar case XO_ROLE_TEXT: 5723d1a0d267SMarcel Moolenaar case XO_ROLE_EBRACE: 5724d1a0d267SMarcel Moolenaar continue; 5725d1a0d267SMarcel Moolenaar 5726d1a0d267SMarcel Moolenaar case 'V': 5727d1a0d267SMarcel Moolenaar for (oldp = startp; oldp->xfi_ftype; oldp++) { 5728d1a0d267SMarcel Moolenaar if (oldp->xfi_ftype != 'V') 5729d1a0d267SMarcel Moolenaar continue; 5730d1a0d267SMarcel Moolenaar if (newp->xfi_clen != oldp->xfi_clen 5731d1a0d267SMarcel Moolenaar || strncmp(newp->xfi_content, oldp->xfi_content, 5732d1a0d267SMarcel Moolenaar oldp->xfi_clen) != 0) { 5733d1a0d267SMarcel Moolenaar reordered = 1; 5734d1a0d267SMarcel Moolenaar continue; 5735d1a0d267SMarcel Moolenaar } 5736d1a0d267SMarcel Moolenaar startp = oldp + 1; 5737d1a0d267SMarcel Moolenaar break; 5738d1a0d267SMarcel Moolenaar } 5739d1a0d267SMarcel Moolenaar 5740d1a0d267SMarcel Moolenaar /* Didn't find it on the first pass (starting from start) */ 5741d1a0d267SMarcel Moolenaar if (oldp->xfi_ftype == 0) { 5742d1a0d267SMarcel Moolenaar for (oldp = old_fields; oldp < startp; oldp++) { 5743d1a0d267SMarcel Moolenaar if (oldp->xfi_ftype != 'V') 5744d1a0d267SMarcel Moolenaar continue; 5745d1a0d267SMarcel Moolenaar if (newp->xfi_clen != oldp->xfi_clen) 5746d1a0d267SMarcel Moolenaar continue; 5747d1a0d267SMarcel Moolenaar if (strncmp(newp->xfi_content, oldp->xfi_content, 5748d1a0d267SMarcel Moolenaar oldp->xfi_clen) != 0) 5749d1a0d267SMarcel Moolenaar continue; 5750d1a0d267SMarcel Moolenaar reordered = 1; 5751d1a0d267SMarcel Moolenaar break; 5752d1a0d267SMarcel Moolenaar } 5753d1a0d267SMarcel Moolenaar if (oldp == startp) { 5754d1a0d267SMarcel Moolenaar /* Field not found */ 5755d1a0d267SMarcel Moolenaar xo_failure(xop, "post-gettext format can't find field " 5756d1a0d267SMarcel Moolenaar "'%.*s' in format '%s'", 5757d1a0d267SMarcel Moolenaar newp->xfi_clen, newp->xfi_content, 5758d1a0d267SMarcel Moolenaar xo_printable(gtfmt)); 5759d1a0d267SMarcel Moolenaar return -1; 5760d1a0d267SMarcel Moolenaar } 5761d1a0d267SMarcel Moolenaar } 5762d1a0d267SMarcel Moolenaar break; 5763d1a0d267SMarcel Moolenaar 5764d1a0d267SMarcel Moolenaar default: 5765d1a0d267SMarcel Moolenaar /* 5766d1a0d267SMarcel Moolenaar * Other fields don't have names for us to use, so if 5767d1a0d267SMarcel Moolenaar * the types aren't the same, then we'll have to assume 5768d1a0d267SMarcel Moolenaar * the original field is a match. 5769d1a0d267SMarcel Moolenaar */ 5770d1a0d267SMarcel Moolenaar for (oldp = startp; oldp->xfi_ftype; oldp++) { 5771d1a0d267SMarcel Moolenaar if (oldp->xfi_ftype == 'V') /* Can't go past these */ 5772d1a0d267SMarcel Moolenaar break; 5773d1a0d267SMarcel Moolenaar if (oldp->xfi_ftype == newp->xfi_ftype) 5774d1a0d267SMarcel Moolenaar goto copy_it; /* Assumably we have a match */ 5775d1a0d267SMarcel Moolenaar } 5776d1a0d267SMarcel Moolenaar continue; 5777d1a0d267SMarcel Moolenaar } 5778d1a0d267SMarcel Moolenaar 5779d1a0d267SMarcel Moolenaar /* 5780d1a0d267SMarcel Moolenaar * Found a match; copy over appropriate fields 5781d1a0d267SMarcel Moolenaar */ 5782d1a0d267SMarcel Moolenaar copy_it: 5783d1a0d267SMarcel Moolenaar newp->xfi_flags = oldp->xfi_flags; 5784d1a0d267SMarcel Moolenaar newp->xfi_fnum = oldp->xfi_fnum; 5785d1a0d267SMarcel Moolenaar newp->xfi_format = oldp->xfi_format; 5786d1a0d267SMarcel Moolenaar newp->xfi_flen = oldp->xfi_flen; 5787d1a0d267SMarcel Moolenaar newp->xfi_encoding = oldp->xfi_encoding; 5788d1a0d267SMarcel Moolenaar newp->xfi_elen = oldp->xfi_elen; 5789d1a0d267SMarcel Moolenaar } 5790d1a0d267SMarcel Moolenaar 5791d1a0d267SMarcel Moolenaar *reorderedp = reordered; 5792d1a0d267SMarcel Moolenaar if (reordered) { 5793d1a0d267SMarcel Moolenaar xo_gettext_finish_numbering_fields(xop, fmt, new_fields); 5794d1a0d267SMarcel Moolenaar xo_gettext_rewrite_fields(xop, new_fields, new_max_fields); 5795d1a0d267SMarcel Moolenaar } 5796d1a0d267SMarcel Moolenaar 5797d1a0d267SMarcel Moolenaar return 0; 5798d1a0d267SMarcel Moolenaar } 5799d1a0d267SMarcel Moolenaar 5800d1a0d267SMarcel Moolenaar /* 5801d1a0d267SMarcel Moolenaar * We don't want to make gettext() calls here with a complete format 5802d1a0d267SMarcel Moolenaar * string, since that means changing a flag would mean a 5803d1a0d267SMarcel Moolenaar * labor-intensive re-translation expense. Instead we build a 5804d1a0d267SMarcel Moolenaar * simplified form with a reduced level of detail, perform a lookup on 5805d1a0d267SMarcel Moolenaar * that string and then re-insert the formating info. 5806d1a0d267SMarcel Moolenaar * 5807d1a0d267SMarcel Moolenaar * So something like: 5808d1a0d267SMarcel Moolenaar * xo_emit("{G:}close {:fd/%ld} returned {g:error/%m} {:test/%6.6s}\n", ...) 5809d1a0d267SMarcel Moolenaar * would have a lookup string of: 5810d1a0d267SMarcel Moolenaar * "close {:fd} returned {:error} {:test}\n" 5811d1a0d267SMarcel Moolenaar * 5812d1a0d267SMarcel Moolenaar * We also need to handling reordering of fields, where the gettext() 5813d1a0d267SMarcel Moolenaar * reply string uses fields in a different order than the original 5814d1a0d267SMarcel Moolenaar * format string: 5815d1a0d267SMarcel Moolenaar * "cluse-a {:fd} retoorned {:test}. Bork {:error} Bork. Bork.\n" 5816d1a0d267SMarcel Moolenaar * If we have to reorder fields within the message, then things get 5817d1a0d267SMarcel Moolenaar * complicated. See xo_gettext_rewrite_fields. 5818d1a0d267SMarcel Moolenaar * 5819d1a0d267SMarcel Moolenaar * Summary: i18n aighn't cheap. 5820d1a0d267SMarcel Moolenaar */ 5821d1a0d267SMarcel Moolenaar static const char * 582242ff34c3SPhil Shafer xo_gettext_build_format (xo_handle_t *xop, 582342ff34c3SPhil Shafer xo_field_info_t *fields, int this_field, 5824d1a0d267SMarcel Moolenaar const char *fmt, char **new_fmtp) 5825d1a0d267SMarcel Moolenaar { 5826d1a0d267SMarcel Moolenaar if (xo_style_is_encoding(xop)) 5827d1a0d267SMarcel Moolenaar goto bail; 5828d1a0d267SMarcel Moolenaar 5829d1a0d267SMarcel Moolenaar xo_buffer_t xb; 5830d1a0d267SMarcel Moolenaar xo_buf_init(&xb); 5831d1a0d267SMarcel Moolenaar 5832d1a0d267SMarcel Moolenaar if (xo_gettext_simplify_format(xop, &xb, fields, 5833d1a0d267SMarcel Moolenaar this_field, fmt, NULL)) 5834d1a0d267SMarcel Moolenaar goto bail2; 5835d1a0d267SMarcel Moolenaar 5836d1a0d267SMarcel Moolenaar const char *gtfmt = xo_dgettext(xop, xb.xb_bufp); 5837d1a0d267SMarcel Moolenaar if (gtfmt == NULL || gtfmt == fmt || strcmp(gtfmt, fmt) == 0) 5838d1a0d267SMarcel Moolenaar goto bail2; 5839d1a0d267SMarcel Moolenaar 5840d1a0d267SMarcel Moolenaar xo_buf_cleanup(&xb); 5841d1a0d267SMarcel Moolenaar 5842d1a0d267SMarcel Moolenaar char *new_fmt = xo_strndup(gtfmt, -1); 5843d1a0d267SMarcel Moolenaar if (new_fmt == NULL) 5844d1a0d267SMarcel Moolenaar goto bail2; 5845d1a0d267SMarcel Moolenaar 5846d1a0d267SMarcel Moolenaar *new_fmtp = new_fmt; 5847d1a0d267SMarcel Moolenaar return new_fmt; 5848d1a0d267SMarcel Moolenaar 5849d1a0d267SMarcel Moolenaar bail2: 5850d1a0d267SMarcel Moolenaar xo_buf_cleanup(&xb); 5851d1a0d267SMarcel Moolenaar bail: 5852d1a0d267SMarcel Moolenaar *new_fmtp = NULL; 5853d1a0d267SMarcel Moolenaar return fmt; 5854d1a0d267SMarcel Moolenaar } 5855d1a0d267SMarcel Moolenaar 5856d1a0d267SMarcel Moolenaar static void 5857d1a0d267SMarcel Moolenaar xo_gettext_rebuild_content (xo_handle_t *xop, xo_field_info_t *fields, 58588a6eceffSPhil Shafer ssize_t *fstart, unsigned min_fstart, 58598a6eceffSPhil Shafer ssize_t *fend, unsigned max_fend) 5860d1a0d267SMarcel Moolenaar { 5861d1a0d267SMarcel Moolenaar xo_field_info_t *xfip; 5862d1a0d267SMarcel Moolenaar char *buf; 58638a6eceffSPhil Shafer ssize_t base = fstart[min_fstart]; 58648a6eceffSPhil Shafer ssize_t blen = fend[max_fend] - base; 5865d1a0d267SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_data; 5866d1a0d267SMarcel Moolenaar 5867d1a0d267SMarcel Moolenaar if (blen == 0) 5868d1a0d267SMarcel Moolenaar return; 5869d1a0d267SMarcel Moolenaar 5870d1a0d267SMarcel Moolenaar buf = xo_realloc(NULL, blen); 5871d1a0d267SMarcel Moolenaar if (buf == NULL) 5872d1a0d267SMarcel Moolenaar return; 5873d1a0d267SMarcel Moolenaar 5874d1a0d267SMarcel Moolenaar memcpy(buf, xbp->xb_bufp + fstart[min_fstart], blen); /* Copy our data */ 5875d1a0d267SMarcel Moolenaar 58768a6eceffSPhil Shafer unsigned field = min_fstart, len, fnum; 58778a6eceffSPhil Shafer ssize_t soff, doff = base; 5878d1a0d267SMarcel Moolenaar xo_field_info_t *zp; 5879d1a0d267SMarcel Moolenaar 5880d1a0d267SMarcel Moolenaar /* 5881d1a0d267SMarcel Moolenaar * Be aware there are two competing views of "field number": we 5882d1a0d267SMarcel Moolenaar * want the user to thing in terms of "The {1:size}" where {G:}, 5883d1a0d267SMarcel Moolenaar * newlines, escaped braces, and text don't have numbers. But is 5884d1a0d267SMarcel Moolenaar * also the internal view, where we have an array of 5885d1a0d267SMarcel Moolenaar * xo_field_info_t and every field have an index. fnum, fstart[] 5886d1a0d267SMarcel Moolenaar * and fend[] are the latter, but xfi_renum is the former. 5887d1a0d267SMarcel Moolenaar */ 5888d1a0d267SMarcel Moolenaar for (xfip = fields + field; xfip->xfi_ftype; xfip++, field++) { 5889d1a0d267SMarcel Moolenaar fnum = field; 5890d1a0d267SMarcel Moolenaar if (xfip->xfi_renum) { 5891d1a0d267SMarcel Moolenaar zp = xo_gettext_find_field(fields, xfip->xfi_renum); 5892d1a0d267SMarcel Moolenaar fnum = zp ? zp - fields : field; 5893d1a0d267SMarcel Moolenaar } 5894d1a0d267SMarcel Moolenaar 5895d1a0d267SMarcel Moolenaar soff = fstart[fnum]; 5896d1a0d267SMarcel Moolenaar len = fend[fnum] - soff; 5897d1a0d267SMarcel Moolenaar 5898d1a0d267SMarcel Moolenaar if (len > 0) { 5899d1a0d267SMarcel Moolenaar soff -= base; 5900d1a0d267SMarcel Moolenaar memcpy(xbp->xb_bufp + doff, buf + soff, len); 5901d1a0d267SMarcel Moolenaar doff += len; 5902d1a0d267SMarcel Moolenaar } 5903d1a0d267SMarcel Moolenaar } 5904d1a0d267SMarcel Moolenaar 5905d1a0d267SMarcel Moolenaar xo_free(buf); 5906d1a0d267SMarcel Moolenaar } 5907d1a0d267SMarcel Moolenaar #else /* HAVE_GETTEXT */ 5908d1a0d267SMarcel Moolenaar static const char * 5909d1a0d267SMarcel Moolenaar xo_gettext_build_format (xo_handle_t *xop UNUSED, 5910d1a0d267SMarcel Moolenaar xo_field_info_t *fields UNUSED, 5911d1a0d267SMarcel Moolenaar int this_field UNUSED, 5912d1a0d267SMarcel Moolenaar const char *fmt UNUSED, char **new_fmtp) 5913d1a0d267SMarcel Moolenaar { 5914d1a0d267SMarcel Moolenaar *new_fmtp = NULL; 5915d1a0d267SMarcel Moolenaar return fmt; 5916d1a0d267SMarcel Moolenaar } 5917d1a0d267SMarcel Moolenaar 5918d1a0d267SMarcel Moolenaar static int 5919d1a0d267SMarcel Moolenaar xo_gettext_combine_formats (xo_handle_t *xop UNUSED, const char *fmt UNUSED, 5920d1a0d267SMarcel Moolenaar const char *gtfmt UNUSED, 5921d1a0d267SMarcel Moolenaar xo_field_info_t *old_fields UNUSED, 5922d1a0d267SMarcel Moolenaar xo_field_info_t *new_fields UNUSED, 5923d1a0d267SMarcel Moolenaar unsigned new_max_fields UNUSED, 5924d1a0d267SMarcel Moolenaar int *reorderedp UNUSED) 5925d1a0d267SMarcel Moolenaar { 5926d1a0d267SMarcel Moolenaar return -1; 5927d1a0d267SMarcel Moolenaar } 5928d1a0d267SMarcel Moolenaar 5929d1a0d267SMarcel Moolenaar static void 5930d1a0d267SMarcel Moolenaar xo_gettext_rebuild_content (xo_handle_t *xop UNUSED, 5931d1a0d267SMarcel Moolenaar xo_field_info_t *fields UNUSED, 59328a6eceffSPhil Shafer ssize_t *fstart UNUSED, unsigned min_fstart UNUSED, 59338a6eceffSPhil Shafer ssize_t *fend UNUSED, unsigned max_fend UNUSED) 5934d1a0d267SMarcel Moolenaar { 5935d1a0d267SMarcel Moolenaar return; 5936d1a0d267SMarcel Moolenaar } 5937d1a0d267SMarcel Moolenaar #endif /* HAVE_GETTEXT */ 5938d1a0d267SMarcel Moolenaar 5939d1a0d267SMarcel Moolenaar /* 594042ff34c3SPhil Shafer * Emit a set of fields. This is really the core of libxo. 5941d1a0d267SMarcel Moolenaar */ 59428a6eceffSPhil Shafer static ssize_t 594342ff34c3SPhil Shafer xo_do_emit_fields (xo_handle_t *xop, xo_field_info_t *fields, 594442ff34c3SPhil Shafer unsigned max_fields, const char *fmt) 5945d1a0d267SMarcel Moolenaar { 5946d1a0d267SMarcel Moolenaar int gettext_inuse = 0; 5947d1a0d267SMarcel Moolenaar int gettext_changed = 0; 5948d1a0d267SMarcel Moolenaar int gettext_reordered = 0; 594942ff34c3SPhil Shafer unsigned ftype; 595042ff34c3SPhil Shafer xo_xff_flags_t flags; 5951d1a0d267SMarcel Moolenaar xo_field_info_t *new_fields = NULL; 595242ff34c3SPhil Shafer xo_field_info_t *xfip; 595342ff34c3SPhil Shafer unsigned field; 59548a6eceffSPhil Shafer ssize_t rc = 0; 595542ff34c3SPhil Shafer 5956d1a0d267SMarcel Moolenaar int flush = XOF_ISSET(xop, XOF_FLUSH); 5957d1a0d267SMarcel Moolenaar int flush_line = XOF_ISSET(xop, XOF_FLUSH_LINE); 5958d1a0d267SMarcel Moolenaar char *new_fmt = NULL; 5959d1a0d267SMarcel Moolenaar 5960d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_REORDER) || xo_style(xop) == XO_STYLE_ENCODER) 5961d1a0d267SMarcel Moolenaar flush_line = 0; 5962d1a0d267SMarcel Moolenaar 5963d1a0d267SMarcel Moolenaar /* 5964d1a0d267SMarcel Moolenaar * Some overhead for gettext; if the fields in the msgstr returned 5965d1a0d267SMarcel Moolenaar * by gettext are reordered, then we need to record start and end 5966d1a0d267SMarcel Moolenaar * for each field. We'll go ahead and render the fields in the 5967d1a0d267SMarcel Moolenaar * normal order, but later we can then reconstruct the reordered 5968d1a0d267SMarcel Moolenaar * fields using these fstart/fend values. 5969d1a0d267SMarcel Moolenaar */ 5970d1a0d267SMarcel Moolenaar unsigned flimit = max_fields * 2; /* Pessimistic limit */ 5971d1a0d267SMarcel Moolenaar unsigned min_fstart = flimit - 1; 5972d1a0d267SMarcel Moolenaar unsigned max_fend = 0; /* Highest recorded fend[] entry */ 59738a6eceffSPhil Shafer ssize_t fstart[flimit]; 5974d1a0d267SMarcel Moolenaar bzero(fstart, flimit * sizeof(fstart[0])); 59758a6eceffSPhil Shafer ssize_t fend[flimit]; 5976d1a0d267SMarcel Moolenaar bzero(fend, flimit * sizeof(fend[0])); 5977d1a0d267SMarcel Moolenaar 5978d1a0d267SMarcel Moolenaar for (xfip = fields, field = 0; xfip->xfi_ftype && field < max_fields; 5979d1a0d267SMarcel Moolenaar xfip++, field++) { 5980d1a0d267SMarcel Moolenaar ftype = xfip->xfi_ftype; 5981d1a0d267SMarcel Moolenaar flags = xfip->xfi_flags; 5982d1a0d267SMarcel Moolenaar 5983d1a0d267SMarcel Moolenaar /* Record field start offset */ 5984d1a0d267SMarcel Moolenaar if (gettext_reordered) { 5985d1a0d267SMarcel Moolenaar fstart[field] = xo_buf_offset(&xop->xo_data); 5986d1a0d267SMarcel Moolenaar if (min_fstart > field) 5987d1a0d267SMarcel Moolenaar min_fstart = field; 5988d1a0d267SMarcel Moolenaar } 5989d1a0d267SMarcel Moolenaar 599042ff34c3SPhil Shafer const char *content = xfip->xfi_content; 59918a6eceffSPhil Shafer ssize_t clen = xfip->xfi_clen; 599242ff34c3SPhil Shafer 599342ff34c3SPhil Shafer if (flags & XFF_ARGUMENT) { 599442ff34c3SPhil Shafer /* 599542ff34c3SPhil Shafer * Argument flag means the content isn't given in the descriptor, 599642ff34c3SPhil Shafer * but as a UTF-8 string ('const char *') argument in xo_vap. 599742ff34c3SPhil Shafer */ 599842ff34c3SPhil Shafer content = va_arg(xop->xo_vap, char *); 599942ff34c3SPhil Shafer clen = content ? strlen(content) : 0; 600042ff34c3SPhil Shafer } 600142ff34c3SPhil Shafer 6002d1a0d267SMarcel Moolenaar if (ftype == XO_ROLE_NEWLINE) { 6003d1a0d267SMarcel Moolenaar xo_line_close(xop); 6004d1a0d267SMarcel Moolenaar if (flush_line && xo_flush_h(xop) < 0) 6005d1a0d267SMarcel Moolenaar return -1; 6006d1a0d267SMarcel Moolenaar goto bottom; 6007d1a0d267SMarcel Moolenaar 6008d1a0d267SMarcel Moolenaar } else if (ftype == XO_ROLE_EBRACE) { 6009d1a0d267SMarcel Moolenaar xo_format_text(xop, xfip->xfi_start, xfip->xfi_len); 6010d1a0d267SMarcel Moolenaar goto bottom; 6011d1a0d267SMarcel Moolenaar 6012d1a0d267SMarcel Moolenaar } else if (ftype == XO_ROLE_TEXT) { 6013d1a0d267SMarcel Moolenaar /* Normal text */ 6014d1a0d267SMarcel Moolenaar xo_format_text(xop, xfip->xfi_content, xfip->xfi_clen); 6015d1a0d267SMarcel Moolenaar goto bottom; 6016d1a0d267SMarcel Moolenaar } 6017d1a0d267SMarcel Moolenaar 6018d1a0d267SMarcel Moolenaar /* 6019d1a0d267SMarcel Moolenaar * Notes and units need the 'w' flag handled before the content. 6020d1a0d267SMarcel Moolenaar */ 6021d1a0d267SMarcel Moolenaar if (ftype == 'N' || ftype == 'U') { 6022d1a0d267SMarcel Moolenaar if (flags & XFF_WS) { 6023d1a0d267SMarcel Moolenaar xo_format_content(xop, "padding", NULL, " ", 1, 6024d1a0d267SMarcel Moolenaar NULL, 0, flags); 6025d1a0d267SMarcel Moolenaar flags &= ~XFF_WS; /* Block later handling of this */ 6026d1a0d267SMarcel Moolenaar } 6027d1a0d267SMarcel Moolenaar } 6028d1a0d267SMarcel Moolenaar 6029d1a0d267SMarcel Moolenaar if (ftype == 'V') 603042ff34c3SPhil Shafer xo_format_value(xop, content, clen, 6031d1a0d267SMarcel Moolenaar xfip->xfi_format, xfip->xfi_flen, 6032d1a0d267SMarcel Moolenaar xfip->xfi_encoding, xfip->xfi_elen, flags); 6033d1a0d267SMarcel Moolenaar else if (ftype == '[') 603442ff34c3SPhil Shafer xo_anchor_start(xop, xfip, content, clen); 6035545ddfbeSMarcel Moolenaar else if (ftype == ']') 603642ff34c3SPhil Shafer xo_anchor_stop(xop, xfip, content, clen); 6037788ca347SMarcel Moolenaar else if (ftype == 'C') 603842ff34c3SPhil Shafer xo_format_colors(xop, xfip, content, clen); 6039545ddfbeSMarcel Moolenaar 6040d1a0d267SMarcel Moolenaar else if (ftype == 'G') { 6041d1a0d267SMarcel Moolenaar /* 6042d1a0d267SMarcel Moolenaar * A {G:domain} field; disect the domain name and translate 6043d1a0d267SMarcel Moolenaar * the remaining portion of the input string. If the user 6044d1a0d267SMarcel Moolenaar * didn't put the {G:} at the start of the format string, then 6045d1a0d267SMarcel Moolenaar * assumably they just want us to translate the rest of it. 6046d1a0d267SMarcel Moolenaar * Since gettext returns strings in a static buffer, we make 6047d1a0d267SMarcel Moolenaar * a copy in new_fmt. 6048d1a0d267SMarcel Moolenaar */ 604942ff34c3SPhil Shafer xo_set_gettext_domain(xop, xfip, content, clen); 6050d1a0d267SMarcel Moolenaar 6051d1a0d267SMarcel Moolenaar if (!gettext_inuse) { /* Only translate once */ 6052d1a0d267SMarcel Moolenaar gettext_inuse = 1; 6053d1a0d267SMarcel Moolenaar if (new_fmt) { 6054d1a0d267SMarcel Moolenaar xo_free(new_fmt); 6055d1a0d267SMarcel Moolenaar new_fmt = NULL; 6056545ddfbeSMarcel Moolenaar } 6057545ddfbeSMarcel Moolenaar 6058d1a0d267SMarcel Moolenaar xo_gettext_build_format(xop, fields, field, 6059d1a0d267SMarcel Moolenaar xfip->xfi_next, &new_fmt); 6060d1a0d267SMarcel Moolenaar if (new_fmt) { 6061d1a0d267SMarcel Moolenaar gettext_changed = 1; 6062d1a0d267SMarcel Moolenaar 6063d1a0d267SMarcel Moolenaar unsigned new_max_fields = xo_count_fields(xop, new_fmt); 6064d1a0d267SMarcel Moolenaar 6065d1a0d267SMarcel Moolenaar if (++new_max_fields < max_fields) 6066d1a0d267SMarcel Moolenaar new_max_fields = max_fields; 6067d1a0d267SMarcel Moolenaar 6068d1a0d267SMarcel Moolenaar /* Leave a blank slot at the beginning */ 60698a6eceffSPhil Shafer ssize_t sz = (new_max_fields + 1) * sizeof(xo_field_info_t); 6070d1a0d267SMarcel Moolenaar new_fields = alloca(sz); 6071d1a0d267SMarcel Moolenaar bzero(new_fields, sz); 6072d1a0d267SMarcel Moolenaar 6073d1a0d267SMarcel Moolenaar if (!xo_parse_fields(xop, new_fields + 1, 6074d1a0d267SMarcel Moolenaar new_max_fields, new_fmt)) { 6075d1a0d267SMarcel Moolenaar gettext_reordered = 0; 6076d1a0d267SMarcel Moolenaar 6077d1a0d267SMarcel Moolenaar if (!xo_gettext_combine_formats(xop, fmt, new_fmt, 6078d1a0d267SMarcel Moolenaar fields, new_fields + 1, 6079d1a0d267SMarcel Moolenaar new_max_fields, &gettext_reordered)) { 6080d1a0d267SMarcel Moolenaar 6081d1a0d267SMarcel Moolenaar if (gettext_reordered) { 6082d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_LOG_GETTEXT)) 6083d1a0d267SMarcel Moolenaar xo_failure(xop, "gettext finds reordered " 6084d1a0d267SMarcel Moolenaar "fields in '%s' and '%s'", 6085d1a0d267SMarcel Moolenaar xo_printable(fmt), 6086d1a0d267SMarcel Moolenaar xo_printable(new_fmt)); 6087d1a0d267SMarcel Moolenaar flush_line = 0; /* Must keep at content */ 6088d1a0d267SMarcel Moolenaar XOIF_SET(xop, XOIF_REORDER); 6089d1a0d267SMarcel Moolenaar } 6090d1a0d267SMarcel Moolenaar 6091d1a0d267SMarcel Moolenaar field = -1; /* Will be incremented at top of loop */ 6092d1a0d267SMarcel Moolenaar xfip = new_fields; 6093d1a0d267SMarcel Moolenaar max_fields = new_max_fields; 6094d1a0d267SMarcel Moolenaar } 6095d1a0d267SMarcel Moolenaar } 6096d1a0d267SMarcel Moolenaar } 6097d1a0d267SMarcel Moolenaar } 6098d1a0d267SMarcel Moolenaar continue; 6099d1a0d267SMarcel Moolenaar 610042ff34c3SPhil Shafer } else if (clen || xfip->xfi_format) { 6101d1a0d267SMarcel Moolenaar 6102d1a0d267SMarcel Moolenaar const char *class_name = xo_class_name(ftype); 6103d1a0d267SMarcel Moolenaar if (class_name) 6104d1a0d267SMarcel Moolenaar xo_format_content(xop, class_name, xo_tag_name(ftype), 610542ff34c3SPhil Shafer content, clen, 6106d1a0d267SMarcel Moolenaar xfip->xfi_format, xfip->xfi_flen, flags); 610731337658SMarcel Moolenaar else if (ftype == 'T') 610842ff34c3SPhil Shafer xo_format_title(xop, xfip, content, clen); 6109d1a0d267SMarcel Moolenaar else if (ftype == 'U') 611042ff34c3SPhil Shafer xo_format_units(xop, xfip, content, clen); 6111d1a0d267SMarcel Moolenaar else 6112d1a0d267SMarcel Moolenaar xo_failure(xop, "unknown field type: '%c'", ftype); 6113545ddfbeSMarcel Moolenaar } 611431337658SMarcel Moolenaar 611531337658SMarcel Moolenaar if (flags & XFF_COLON) 6116d1a0d267SMarcel Moolenaar xo_format_content(xop, "decoration", NULL, ":", 1, NULL, 0, 0); 611731337658SMarcel Moolenaar 6118d1a0d267SMarcel Moolenaar if (flags & XFF_WS) 6119d1a0d267SMarcel Moolenaar xo_format_content(xop, "padding", NULL, " ", 1, NULL, 0, 0); 6120d1a0d267SMarcel Moolenaar 6121d1a0d267SMarcel Moolenaar bottom: 6122d1a0d267SMarcel Moolenaar /* Record the end-of-field offset */ 6123d1a0d267SMarcel Moolenaar if (gettext_reordered) { 6124d1a0d267SMarcel Moolenaar fend[field] = xo_buf_offset(&xop->xo_data); 6125d1a0d267SMarcel Moolenaar max_fend = field; 612631337658SMarcel Moolenaar } 612731337658SMarcel Moolenaar } 612831337658SMarcel Moolenaar 6129d1a0d267SMarcel Moolenaar if (gettext_changed && gettext_reordered) { 6130d1a0d267SMarcel Moolenaar /* Final step: rebuild the content using the rendered fields */ 6131d1a0d267SMarcel Moolenaar xo_gettext_rebuild_content(xop, new_fields + 1, fstart, min_fstart, 6132d1a0d267SMarcel Moolenaar fend, max_fend); 6133d1a0d267SMarcel Moolenaar } 6134d1a0d267SMarcel Moolenaar 6135d1a0d267SMarcel Moolenaar XOIF_CLEAR(xop, XOIF_REORDER); 6136d1a0d267SMarcel Moolenaar 6137ee5cf116SPhil Shafer /* 6138ee5cf116SPhil Shafer * If we've got enough data, flush it. 6139ee5cf116SPhil Shafer */ 6140ee5cf116SPhil Shafer if (xo_buf_offset(&xop->xo_data) > XO_BUF_HIGH_WATER) 6141ee5cf116SPhil Shafer flush = 1; 6142ee5cf116SPhil Shafer 614331337658SMarcel Moolenaar /* If we don't have an anchor, write the text out */ 6144d1a0d267SMarcel Moolenaar if (flush && !XOIF_ISSET(xop, XOIF_ANCHOR)) { 6145545ddfbeSMarcel Moolenaar if (xo_write(xop) < 0) 6146545ddfbeSMarcel Moolenaar rc = -1; /* Report failure */ 614742ff34c3SPhil Shafer else if (xo_flush_h(xop) < 0) 6148545ddfbeSMarcel Moolenaar rc = -1; 6149545ddfbeSMarcel Moolenaar } 615031337658SMarcel Moolenaar 6151d1a0d267SMarcel Moolenaar if (new_fmt) 6152d1a0d267SMarcel Moolenaar xo_free(new_fmt); 6153d1a0d267SMarcel Moolenaar 6154d1a0d267SMarcel Moolenaar /* 6155d1a0d267SMarcel Moolenaar * We've carried the gettext domainname inside our handle just for 6156d1a0d267SMarcel Moolenaar * convenience, but we need to ensure it doesn't survive across 6157d1a0d267SMarcel Moolenaar * xo_emit calls. 6158d1a0d267SMarcel Moolenaar */ 6159d1a0d267SMarcel Moolenaar if (xop->xo_gt_domain) { 6160d1a0d267SMarcel Moolenaar xo_free(xop->xo_gt_domain); 6161d1a0d267SMarcel Moolenaar xop->xo_gt_domain = NULL; 6162d1a0d267SMarcel Moolenaar } 6163d1a0d267SMarcel Moolenaar 61648a6eceffSPhil Shafer return (rc < 0) ? rc : xop->xo_columns; 616531337658SMarcel Moolenaar } 616631337658SMarcel Moolenaar 6167d1a0d267SMarcel Moolenaar /* 616842ff34c3SPhil Shafer * Parse and emit a set of fields 616942ff34c3SPhil Shafer */ 617042ff34c3SPhil Shafer static int 617142ff34c3SPhil Shafer xo_do_emit (xo_handle_t *xop, xo_emit_flags_t flags, const char *fmt) 617242ff34c3SPhil Shafer { 617342ff34c3SPhil Shafer xop->xo_columns = 0; /* Always reset it */ 617442ff34c3SPhil Shafer xop->xo_errno = errno; /* Save for "%m" */ 617542ff34c3SPhil Shafer 617642ff34c3SPhil Shafer if (fmt == NULL) 617742ff34c3SPhil Shafer return 0; 617842ff34c3SPhil Shafer 617942ff34c3SPhil Shafer unsigned max_fields; 618042ff34c3SPhil Shafer xo_field_info_t *fields = NULL; 618142ff34c3SPhil Shafer 618242ff34c3SPhil Shafer /* Adjust XOEF_RETAIN based on global flags */ 618342ff34c3SPhil Shafer if (XOF_ISSET(xop, XOF_RETAIN_ALL)) 618442ff34c3SPhil Shafer flags |= XOEF_RETAIN; 618542ff34c3SPhil Shafer if (XOF_ISSET(xop, XOF_RETAIN_NONE)) 618642ff34c3SPhil Shafer flags &= ~XOEF_RETAIN; 618742ff34c3SPhil Shafer 618842ff34c3SPhil Shafer /* 618942ff34c3SPhil Shafer * Check for 'retain' flag, telling us to retain the field 619042ff34c3SPhil Shafer * information. If we've already saved it, then we can avoid 619142ff34c3SPhil Shafer * re-parsing the format string. 619242ff34c3SPhil Shafer */ 619342ff34c3SPhil Shafer if (!(flags & XOEF_RETAIN) 619442ff34c3SPhil Shafer || xo_retain_find(fmt, &fields, &max_fields) != 0 619542ff34c3SPhil Shafer || fields == NULL) { 619642ff34c3SPhil Shafer 619742ff34c3SPhil Shafer /* Nothing retained; parse the format string */ 619842ff34c3SPhil Shafer max_fields = xo_count_fields(xop, fmt); 619942ff34c3SPhil Shafer fields = alloca(max_fields * sizeof(fields[0])); 620042ff34c3SPhil Shafer bzero(fields, max_fields * sizeof(fields[0])); 620142ff34c3SPhil Shafer 620242ff34c3SPhil Shafer if (xo_parse_fields(xop, fields, max_fields, fmt)) 620342ff34c3SPhil Shafer return -1; /* Warning already displayed */ 620442ff34c3SPhil Shafer 620542ff34c3SPhil Shafer if (flags & XOEF_RETAIN) { 620642ff34c3SPhil Shafer /* Retain the info */ 620742ff34c3SPhil Shafer xo_retain_add(fmt, fields, max_fields); 620842ff34c3SPhil Shafer } 620942ff34c3SPhil Shafer } 621042ff34c3SPhil Shafer 621142ff34c3SPhil Shafer return xo_do_emit_fields(xop, fields, max_fields, fmt); 621242ff34c3SPhil Shafer } 621342ff34c3SPhil Shafer 621442ff34c3SPhil Shafer /* 6215d1a0d267SMarcel Moolenaar * Rebuild a format string in a gettext-friendly format. This function 6216d1a0d267SMarcel Moolenaar * is exposed to tools can perform this function. See xo(1). 6217d1a0d267SMarcel Moolenaar */ 6218d1a0d267SMarcel Moolenaar char * 6219d1a0d267SMarcel Moolenaar xo_simplify_format (xo_handle_t *xop, const char *fmt, int with_numbers, 6220d1a0d267SMarcel Moolenaar xo_simplify_field_func_t field_cb) 6221d1a0d267SMarcel Moolenaar { 6222d1a0d267SMarcel Moolenaar xop = xo_default(xop); 6223d1a0d267SMarcel Moolenaar 6224d1a0d267SMarcel Moolenaar xop->xo_columns = 0; /* Always reset it */ 6225d1a0d267SMarcel Moolenaar xop->xo_errno = errno; /* Save for "%m" */ 6226d1a0d267SMarcel Moolenaar 6227d1a0d267SMarcel Moolenaar unsigned max_fields = xo_count_fields(xop, fmt); 6228d1a0d267SMarcel Moolenaar xo_field_info_t fields[max_fields]; 6229d1a0d267SMarcel Moolenaar 6230d1a0d267SMarcel Moolenaar bzero(fields, max_fields * sizeof(fields[0])); 6231d1a0d267SMarcel Moolenaar 6232d1a0d267SMarcel Moolenaar if (xo_parse_fields(xop, fields, max_fields, fmt)) 6233d1a0d267SMarcel Moolenaar return NULL; /* Warning already displayed */ 6234d1a0d267SMarcel Moolenaar 6235d1a0d267SMarcel Moolenaar xo_buffer_t xb; 6236d1a0d267SMarcel Moolenaar xo_buf_init(&xb); 6237d1a0d267SMarcel Moolenaar 6238d1a0d267SMarcel Moolenaar if (with_numbers) 6239d1a0d267SMarcel Moolenaar xo_gettext_finish_numbering_fields(xop, fmt, fields); 6240d1a0d267SMarcel Moolenaar 6241d1a0d267SMarcel Moolenaar if (xo_gettext_simplify_format(xop, &xb, fields, -1, fmt, field_cb)) 6242d1a0d267SMarcel Moolenaar return NULL; 6243d1a0d267SMarcel Moolenaar 6244d1a0d267SMarcel Moolenaar return xb.xb_bufp; 6245d1a0d267SMarcel Moolenaar } 6246d1a0d267SMarcel Moolenaar 62478a6eceffSPhil Shafer xo_ssize_t 624831337658SMarcel Moolenaar xo_emit_hv (xo_handle_t *xop, const char *fmt, va_list vap) 624931337658SMarcel Moolenaar { 62508a6eceffSPhil Shafer ssize_t rc; 625131337658SMarcel Moolenaar 625231337658SMarcel Moolenaar xop = xo_default(xop); 625331337658SMarcel Moolenaar va_copy(xop->xo_vap, vap); 625442ff34c3SPhil Shafer rc = xo_do_emit(xop, 0, fmt); 625531337658SMarcel Moolenaar va_end(xop->xo_vap); 625631337658SMarcel Moolenaar bzero(&xop->xo_vap, sizeof(xop->xo_vap)); 625731337658SMarcel Moolenaar 625831337658SMarcel Moolenaar return rc; 625931337658SMarcel Moolenaar } 626031337658SMarcel Moolenaar 62618a6eceffSPhil Shafer xo_ssize_t 626231337658SMarcel Moolenaar xo_emit_h (xo_handle_t *xop, const char *fmt, ...) 626331337658SMarcel Moolenaar { 62648a6eceffSPhil Shafer ssize_t rc; 626531337658SMarcel Moolenaar 626631337658SMarcel Moolenaar xop = xo_default(xop); 626731337658SMarcel Moolenaar va_start(xop->xo_vap, fmt); 626842ff34c3SPhil Shafer rc = xo_do_emit(xop, 0, fmt); 626931337658SMarcel Moolenaar va_end(xop->xo_vap); 627031337658SMarcel Moolenaar bzero(&xop->xo_vap, sizeof(xop->xo_vap)); 627131337658SMarcel Moolenaar 627231337658SMarcel Moolenaar return rc; 627331337658SMarcel Moolenaar } 627431337658SMarcel Moolenaar 62758a6eceffSPhil Shafer xo_ssize_t 627631337658SMarcel Moolenaar xo_emit (const char *fmt, ...) 627731337658SMarcel Moolenaar { 627831337658SMarcel Moolenaar xo_handle_t *xop = xo_default(NULL); 62798a6eceffSPhil Shafer ssize_t rc; 628031337658SMarcel Moolenaar 628131337658SMarcel Moolenaar va_start(xop->xo_vap, fmt); 628242ff34c3SPhil Shafer rc = xo_do_emit(xop, 0, fmt); 628331337658SMarcel Moolenaar va_end(xop->xo_vap); 628431337658SMarcel Moolenaar bzero(&xop->xo_vap, sizeof(xop->xo_vap)); 628531337658SMarcel Moolenaar 628631337658SMarcel Moolenaar return rc; 628731337658SMarcel Moolenaar } 628831337658SMarcel Moolenaar 62898a6eceffSPhil Shafer xo_ssize_t 629042ff34c3SPhil Shafer xo_emit_hvf (xo_handle_t *xop, xo_emit_flags_t flags, 629142ff34c3SPhil Shafer const char *fmt, va_list vap) 629242ff34c3SPhil Shafer { 62938a6eceffSPhil Shafer ssize_t rc; 629442ff34c3SPhil Shafer 629542ff34c3SPhil Shafer xop = xo_default(xop); 629642ff34c3SPhil Shafer va_copy(xop->xo_vap, vap); 629742ff34c3SPhil Shafer rc = xo_do_emit(xop, flags, fmt); 629842ff34c3SPhil Shafer va_end(xop->xo_vap); 629942ff34c3SPhil Shafer bzero(&xop->xo_vap, sizeof(xop->xo_vap)); 630042ff34c3SPhil Shafer 630142ff34c3SPhil Shafer return rc; 630242ff34c3SPhil Shafer } 630342ff34c3SPhil Shafer 63048a6eceffSPhil Shafer xo_ssize_t 630542ff34c3SPhil Shafer xo_emit_hf (xo_handle_t *xop, xo_emit_flags_t flags, const char *fmt, ...) 630642ff34c3SPhil Shafer { 63078a6eceffSPhil Shafer ssize_t rc; 630842ff34c3SPhil Shafer 630942ff34c3SPhil Shafer xop = xo_default(xop); 631042ff34c3SPhil Shafer va_start(xop->xo_vap, fmt); 631142ff34c3SPhil Shafer rc = xo_do_emit(xop, flags, fmt); 631242ff34c3SPhil Shafer va_end(xop->xo_vap); 631342ff34c3SPhil Shafer bzero(&xop->xo_vap, sizeof(xop->xo_vap)); 631442ff34c3SPhil Shafer 631542ff34c3SPhil Shafer return rc; 631642ff34c3SPhil Shafer } 631742ff34c3SPhil Shafer 63188a6eceffSPhil Shafer xo_ssize_t 631942ff34c3SPhil Shafer xo_emit_f (xo_emit_flags_t flags, const char *fmt, ...) 632042ff34c3SPhil Shafer { 632142ff34c3SPhil Shafer xo_handle_t *xop = xo_default(NULL); 63228a6eceffSPhil Shafer ssize_t rc; 632342ff34c3SPhil Shafer 632442ff34c3SPhil Shafer va_start(xop->xo_vap, fmt); 632542ff34c3SPhil Shafer rc = xo_do_emit(xop, flags, fmt); 632642ff34c3SPhil Shafer va_end(xop->xo_vap); 632742ff34c3SPhil Shafer bzero(&xop->xo_vap, sizeof(xop->xo_vap)); 632842ff34c3SPhil Shafer 632942ff34c3SPhil Shafer return rc; 633042ff34c3SPhil Shafer } 633142ff34c3SPhil Shafer 633242ff34c3SPhil Shafer /* 633342ff34c3SPhil Shafer * Emit a single field by providing the info information typically provided 633442ff34c3SPhil Shafer * inside the field description (role, modifiers, and formats). This is 633542ff34c3SPhil Shafer * a convenience function to avoid callers using snprintf to build field 633642ff34c3SPhil Shafer * descriptions. 633742ff34c3SPhil Shafer */ 63388a6eceffSPhil Shafer xo_ssize_t 633942ff34c3SPhil Shafer xo_emit_field_hv (xo_handle_t *xop, const char *rolmod, const char *contents, 634042ff34c3SPhil Shafer const char *fmt, const char *efmt, 634142ff34c3SPhil Shafer va_list vap) 634242ff34c3SPhil Shafer { 63438a6eceffSPhil Shafer ssize_t rc; 634442ff34c3SPhil Shafer 634542ff34c3SPhil Shafer xop = xo_default(xop); 634642ff34c3SPhil Shafer 634742ff34c3SPhil Shafer if (rolmod == NULL) 634842ff34c3SPhil Shafer rolmod = "V"; 634942ff34c3SPhil Shafer 635042ff34c3SPhil Shafer xo_field_info_t xfi; 635142ff34c3SPhil Shafer 635242ff34c3SPhil Shafer bzero(&xfi, sizeof(xfi)); 635342ff34c3SPhil Shafer 635442ff34c3SPhil Shafer const char *cp; 635542ff34c3SPhil Shafer cp = xo_parse_roles(xop, rolmod, rolmod, &xfi); 635642ff34c3SPhil Shafer if (cp == NULL) 635742ff34c3SPhil Shafer return -1; 635842ff34c3SPhil Shafer 635942ff34c3SPhil Shafer xfi.xfi_start = fmt; 636042ff34c3SPhil Shafer xfi.xfi_content = contents; 636142ff34c3SPhil Shafer xfi.xfi_format = fmt; 636242ff34c3SPhil Shafer xfi.xfi_encoding = efmt; 636342ff34c3SPhil Shafer xfi.xfi_clen = contents ? strlen(contents) : 0; 636442ff34c3SPhil Shafer xfi.xfi_flen = fmt ? strlen(fmt) : 0; 636542ff34c3SPhil Shafer xfi.xfi_elen = efmt ? strlen(efmt) : 0; 636642ff34c3SPhil Shafer 636742ff34c3SPhil Shafer /* If we have content, then we have a default format */ 636842ff34c3SPhil Shafer if (contents && fmt == NULL 636942ff34c3SPhil Shafer && xo_role_wants_default_format(xfi.xfi_ftype)) { 637042ff34c3SPhil Shafer xfi.xfi_format = xo_default_format; 637142ff34c3SPhil Shafer xfi.xfi_flen = 2; 637242ff34c3SPhil Shafer } 637342ff34c3SPhil Shafer 637442ff34c3SPhil Shafer va_copy(xop->xo_vap, vap); 637542ff34c3SPhil Shafer 637642ff34c3SPhil Shafer rc = xo_do_emit_fields(xop, &xfi, 1, fmt ?: contents ?: "field"); 637742ff34c3SPhil Shafer 637842ff34c3SPhil Shafer va_end(xop->xo_vap); 637942ff34c3SPhil Shafer 638042ff34c3SPhil Shafer return rc; 638142ff34c3SPhil Shafer } 638242ff34c3SPhil Shafer 63838a6eceffSPhil Shafer xo_ssize_t 638442ff34c3SPhil Shafer xo_emit_field_h (xo_handle_t *xop, const char *rolmod, const char *contents, 638542ff34c3SPhil Shafer const char *fmt, const char *efmt, ...) 638642ff34c3SPhil Shafer { 63878a6eceffSPhil Shafer ssize_t rc; 638842ff34c3SPhil Shafer va_list vap; 638942ff34c3SPhil Shafer 639042ff34c3SPhil Shafer va_start(vap, efmt); 639142ff34c3SPhil Shafer rc = xo_emit_field_hv(xop, rolmod, contents, fmt, efmt, vap); 639242ff34c3SPhil Shafer va_end(vap); 639342ff34c3SPhil Shafer 639442ff34c3SPhil Shafer return rc; 639542ff34c3SPhil Shafer } 639642ff34c3SPhil Shafer 63978a6eceffSPhil Shafer xo_ssize_t 639842ff34c3SPhil Shafer xo_emit_field (const char *rolmod, const char *contents, 639942ff34c3SPhil Shafer const char *fmt, const char *efmt, ...) 640042ff34c3SPhil Shafer { 64018a6eceffSPhil Shafer ssize_t rc; 640242ff34c3SPhil Shafer va_list vap; 640342ff34c3SPhil Shafer 640442ff34c3SPhil Shafer va_start(vap, efmt); 640542ff34c3SPhil Shafer rc = xo_emit_field_hv(NULL, rolmod, contents, fmt, efmt, vap); 640642ff34c3SPhil Shafer va_end(vap); 640742ff34c3SPhil Shafer 640842ff34c3SPhil Shafer return rc; 640942ff34c3SPhil Shafer } 641042ff34c3SPhil Shafer 64118a6eceffSPhil Shafer xo_ssize_t 641231337658SMarcel Moolenaar xo_attr_hv (xo_handle_t *xop, const char *name, const char *fmt, va_list vap) 641331337658SMarcel Moolenaar { 64148a6eceffSPhil Shafer const ssize_t extra = 5; /* space, equals, quote, quote, and nul */ 641531337658SMarcel Moolenaar xop = xo_default(xop); 641631337658SMarcel Moolenaar 64178a6eceffSPhil Shafer ssize_t rc = 0; 64188a6eceffSPhil Shafer ssize_t nlen = strlen(name); 641931337658SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_attrs; 64208a6eceffSPhil Shafer ssize_t name_offset, value_offset; 642131337658SMarcel Moolenaar 6422d1a0d267SMarcel Moolenaar switch (xo_style(xop)) { 6423d1a0d267SMarcel Moolenaar case XO_STYLE_XML: 642431337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, nlen + extra)) 642531337658SMarcel Moolenaar return -1; 642631337658SMarcel Moolenaar 642731337658SMarcel Moolenaar *xbp->xb_curp++ = ' '; 642831337658SMarcel Moolenaar memcpy(xbp->xb_curp, name, nlen); 642931337658SMarcel Moolenaar xbp->xb_curp += nlen; 643031337658SMarcel Moolenaar *xbp->xb_curp++ = '='; 643131337658SMarcel Moolenaar *xbp->xb_curp++ = '"'; 643231337658SMarcel Moolenaar 6433d1a0d267SMarcel Moolenaar rc = xo_vsnprintf(xop, xbp, fmt, vap); 643431337658SMarcel Moolenaar 6435d1a0d267SMarcel Moolenaar if (rc >= 0) { 643631337658SMarcel Moolenaar rc = xo_escape_xml(xbp, rc, 1); 643731337658SMarcel Moolenaar xbp->xb_curp += rc; 643831337658SMarcel Moolenaar } 643931337658SMarcel Moolenaar 644031337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, 2)) 644131337658SMarcel Moolenaar return -1; 644231337658SMarcel Moolenaar 644331337658SMarcel Moolenaar *xbp->xb_curp++ = '"'; 644431337658SMarcel Moolenaar *xbp->xb_curp = '\0'; 644531337658SMarcel Moolenaar 6446d1a0d267SMarcel Moolenaar rc += nlen + extra; 6447d1a0d267SMarcel Moolenaar break; 6448d1a0d267SMarcel Moolenaar 6449d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER: 6450d1a0d267SMarcel Moolenaar name_offset = xo_buf_offset(xbp); 6451d1a0d267SMarcel Moolenaar xo_buf_append(xbp, name, nlen); 6452d1a0d267SMarcel Moolenaar xo_buf_append(xbp, "", 1); 6453d1a0d267SMarcel Moolenaar 6454d1a0d267SMarcel Moolenaar value_offset = xo_buf_offset(xbp); 6455d1a0d267SMarcel Moolenaar rc = xo_vsnprintf(xop, xbp, fmt, vap); 6456d1a0d267SMarcel Moolenaar if (rc >= 0) { 6457d1a0d267SMarcel Moolenaar xbp->xb_curp += rc; 6458d1a0d267SMarcel Moolenaar *xbp->xb_curp = '\0'; 6459d1a0d267SMarcel Moolenaar rc = xo_encoder_handle(xop, XO_OP_ATTRIBUTE, 6460d1a0d267SMarcel Moolenaar xo_buf_data(xbp, name_offset), 6461d1a0d267SMarcel Moolenaar xo_buf_data(xbp, value_offset)); 6462d1a0d267SMarcel Moolenaar } 6463d1a0d267SMarcel Moolenaar } 6464d1a0d267SMarcel Moolenaar 6465d1a0d267SMarcel Moolenaar return rc; 646631337658SMarcel Moolenaar } 646731337658SMarcel Moolenaar 64688a6eceffSPhil Shafer xo_ssize_t 646931337658SMarcel Moolenaar xo_attr_h (xo_handle_t *xop, const char *name, const char *fmt, ...) 647031337658SMarcel Moolenaar { 64718a6eceffSPhil Shafer ssize_t rc; 647231337658SMarcel Moolenaar va_list vap; 647331337658SMarcel Moolenaar 647431337658SMarcel Moolenaar va_start(vap, fmt); 647531337658SMarcel Moolenaar rc = xo_attr_hv(xop, name, fmt, vap); 647631337658SMarcel Moolenaar va_end(vap); 647731337658SMarcel Moolenaar 647831337658SMarcel Moolenaar return rc; 647931337658SMarcel Moolenaar } 648031337658SMarcel Moolenaar 64818a6eceffSPhil Shafer xo_ssize_t 648231337658SMarcel Moolenaar xo_attr (const char *name, const char *fmt, ...) 648331337658SMarcel Moolenaar { 64848a6eceffSPhil Shafer ssize_t rc; 648531337658SMarcel Moolenaar va_list vap; 648631337658SMarcel Moolenaar 648731337658SMarcel Moolenaar va_start(vap, fmt); 648831337658SMarcel Moolenaar rc = xo_attr_hv(NULL, name, fmt, vap); 648931337658SMarcel Moolenaar va_end(vap); 649031337658SMarcel Moolenaar 649131337658SMarcel Moolenaar return rc; 649231337658SMarcel Moolenaar } 649331337658SMarcel Moolenaar 649431337658SMarcel Moolenaar static void 649531337658SMarcel Moolenaar xo_stack_set_flags (xo_handle_t *xop) 649631337658SMarcel Moolenaar { 6497d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_NOT_FIRST)) { 649831337658SMarcel Moolenaar xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth]; 649931337658SMarcel Moolenaar 650031337658SMarcel Moolenaar xsp->xs_flags |= XSF_NOT_FIRST; 6501d1a0d267SMarcel Moolenaar XOF_CLEAR(xop, XOF_NOT_FIRST); 650231337658SMarcel Moolenaar } 650331337658SMarcel Moolenaar } 650431337658SMarcel Moolenaar 650531337658SMarcel Moolenaar static void 650631337658SMarcel Moolenaar xo_depth_change (xo_handle_t *xop, const char *name, 6507545ddfbeSMarcel Moolenaar int delta, int indent, xo_state_t state, xo_xsf_flags_t flags) 650831337658SMarcel Moolenaar { 6509788ca347SMarcel Moolenaar if (xo_style(xop) == XO_STYLE_HTML || xo_style(xop) == XO_STYLE_TEXT) 6510545ddfbeSMarcel Moolenaar indent = 0; 6511545ddfbeSMarcel Moolenaar 6512d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_DTRT)) 651331337658SMarcel Moolenaar flags |= XSF_DTRT; 651431337658SMarcel Moolenaar 651531337658SMarcel Moolenaar if (delta >= 0) { /* Push operation */ 651631337658SMarcel Moolenaar if (xo_depth_check(xop, xop->xo_depth + delta)) 651731337658SMarcel Moolenaar return; 651831337658SMarcel Moolenaar 651931337658SMarcel Moolenaar xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth + delta]; 652031337658SMarcel Moolenaar xsp->xs_flags = flags; 6521545ddfbeSMarcel Moolenaar xsp->xs_state = state; 652231337658SMarcel Moolenaar xo_stack_set_flags(xop); 652331337658SMarcel Moolenaar 6524545ddfbeSMarcel Moolenaar if (name == NULL) 6525545ddfbeSMarcel Moolenaar name = XO_FAILURE_NAME; 652631337658SMarcel Moolenaar 6527d1a0d267SMarcel Moolenaar xsp->xs_name = xo_strndup(name, -1); 652831337658SMarcel Moolenaar 652931337658SMarcel Moolenaar } else { /* Pop operation */ 653031337658SMarcel Moolenaar if (xop->xo_depth == 0) { 6531d1a0d267SMarcel Moolenaar if (!XOF_ISSET(xop, XOF_IGNORE_CLOSE)) 653231337658SMarcel Moolenaar xo_failure(xop, "close with empty stack: '%s'", name); 653331337658SMarcel Moolenaar return; 653431337658SMarcel Moolenaar } 653531337658SMarcel Moolenaar 653631337658SMarcel Moolenaar xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth]; 6537d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_WARN)) { 653831337658SMarcel Moolenaar const char *top = xsp->xs_name; 653931337658SMarcel Moolenaar if (top && strcmp(name, top) != 0) { 654031337658SMarcel Moolenaar xo_failure(xop, "incorrect close: '%s' .vs. '%s'", 654131337658SMarcel Moolenaar name, top); 654231337658SMarcel Moolenaar return; 654331337658SMarcel Moolenaar } 654431337658SMarcel Moolenaar if ((xsp->xs_flags & XSF_LIST) != (flags & XSF_LIST)) { 654531337658SMarcel Moolenaar xo_failure(xop, "list close on list confict: '%s'", 654631337658SMarcel Moolenaar name); 654731337658SMarcel Moolenaar return; 654831337658SMarcel Moolenaar } 654931337658SMarcel Moolenaar if ((xsp->xs_flags & XSF_INSTANCE) != (flags & XSF_INSTANCE)) { 655031337658SMarcel Moolenaar xo_failure(xop, "list close on instance confict: '%s'", 655131337658SMarcel Moolenaar name); 655231337658SMarcel Moolenaar return; 655331337658SMarcel Moolenaar } 655431337658SMarcel Moolenaar } 655531337658SMarcel Moolenaar 655631337658SMarcel Moolenaar if (xsp->xs_name) { 655731337658SMarcel Moolenaar xo_free(xsp->xs_name); 655831337658SMarcel Moolenaar xsp->xs_name = NULL; 655931337658SMarcel Moolenaar } 656031337658SMarcel Moolenaar if (xsp->xs_keys) { 656131337658SMarcel Moolenaar xo_free(xsp->xs_keys); 656231337658SMarcel Moolenaar xsp->xs_keys = NULL; 656331337658SMarcel Moolenaar } 656431337658SMarcel Moolenaar } 656531337658SMarcel Moolenaar 656631337658SMarcel Moolenaar xop->xo_depth += delta; /* Record new depth */ 656731337658SMarcel Moolenaar xop->xo_indent += indent; 656831337658SMarcel Moolenaar } 656931337658SMarcel Moolenaar 657031337658SMarcel Moolenaar void 657131337658SMarcel Moolenaar xo_set_depth (xo_handle_t *xop, int depth) 657231337658SMarcel Moolenaar { 657331337658SMarcel Moolenaar xop = xo_default(xop); 657431337658SMarcel Moolenaar 657531337658SMarcel Moolenaar if (xo_depth_check(xop, depth)) 657631337658SMarcel Moolenaar return; 657731337658SMarcel Moolenaar 657831337658SMarcel Moolenaar xop->xo_depth += depth; 657931337658SMarcel Moolenaar xop->xo_indent += depth; 658031337658SMarcel Moolenaar } 658131337658SMarcel Moolenaar 658231337658SMarcel Moolenaar static xo_xsf_flags_t 65838a6eceffSPhil Shafer xo_stack_flags (xo_xof_flags_t xflags) 658431337658SMarcel Moolenaar { 658531337658SMarcel Moolenaar if (xflags & XOF_DTRT) 658631337658SMarcel Moolenaar return XSF_DTRT; 658731337658SMarcel Moolenaar return 0; 658831337658SMarcel Moolenaar } 658931337658SMarcel Moolenaar 6590788ca347SMarcel Moolenaar static void 6591788ca347SMarcel Moolenaar xo_emit_top (xo_handle_t *xop, const char *ppn) 6592788ca347SMarcel Moolenaar { 6593788ca347SMarcel Moolenaar xo_printf(xop, "%*s{%s", xo_indent(xop), "", ppn); 6594d1a0d267SMarcel Moolenaar XOIF_SET(xop, XOIF_TOP_EMITTED); 6595788ca347SMarcel Moolenaar 6596788ca347SMarcel Moolenaar if (xop->xo_version) { 6597788ca347SMarcel Moolenaar xo_printf(xop, "%*s\"__version\": \"%s\", %s", 6598788ca347SMarcel Moolenaar xo_indent(xop), "", xop->xo_version, ppn); 6599788ca347SMarcel Moolenaar xo_free(xop->xo_version); 6600788ca347SMarcel Moolenaar xop->xo_version = NULL; 6601788ca347SMarcel Moolenaar } 6602788ca347SMarcel Moolenaar } 6603788ca347SMarcel Moolenaar 66048a6eceffSPhil Shafer static ssize_t 6605545ddfbeSMarcel Moolenaar xo_do_open_container (xo_handle_t *xop, xo_xof_flags_t flags, const char *name) 660631337658SMarcel Moolenaar { 66078a6eceffSPhil Shafer ssize_t rc = 0; 6608d1a0d267SMarcel Moolenaar const char *ppn = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : ""; 660931337658SMarcel Moolenaar const char *pre_nl = ""; 661031337658SMarcel Moolenaar 661131337658SMarcel Moolenaar if (name == NULL) { 661231337658SMarcel Moolenaar xo_failure(xop, "NULL passed for container name"); 661331337658SMarcel Moolenaar name = XO_FAILURE_NAME; 661431337658SMarcel Moolenaar } 661531337658SMarcel Moolenaar 661631337658SMarcel Moolenaar flags |= xop->xo_flags; /* Pick up handle flags */ 661731337658SMarcel Moolenaar 6618788ca347SMarcel Moolenaar switch (xo_style(xop)) { 661931337658SMarcel Moolenaar case XO_STYLE_XML: 6620545ddfbeSMarcel Moolenaar rc = xo_printf(xop, "%*s<%s", xo_indent(xop), "", name); 6621545ddfbeSMarcel Moolenaar 6622545ddfbeSMarcel Moolenaar if (xop->xo_attrs.xb_curp != xop->xo_attrs.xb_bufp) { 6623545ddfbeSMarcel Moolenaar rc += xop->xo_attrs.xb_curp - xop->xo_attrs.xb_bufp; 6624545ddfbeSMarcel Moolenaar xo_data_append(xop, xop->xo_attrs.xb_bufp, 6625545ddfbeSMarcel Moolenaar xop->xo_attrs.xb_curp - xop->xo_attrs.xb_bufp); 6626545ddfbeSMarcel Moolenaar xop->xo_attrs.xb_curp = xop->xo_attrs.xb_bufp; 6627545ddfbeSMarcel Moolenaar } 6628545ddfbeSMarcel Moolenaar 6629545ddfbeSMarcel Moolenaar rc += xo_printf(xop, ">%s", ppn); 663031337658SMarcel Moolenaar break; 663131337658SMarcel Moolenaar 663231337658SMarcel Moolenaar case XO_STYLE_JSON: 663331337658SMarcel Moolenaar xo_stack_set_flags(xop); 663431337658SMarcel Moolenaar 6635d1a0d267SMarcel Moolenaar if (!XOF_ISSET(xop, XOF_NO_TOP) 6636d1a0d267SMarcel Moolenaar && !XOIF_ISSET(xop, XOIF_TOP_EMITTED)) 6637788ca347SMarcel Moolenaar xo_emit_top(xop, ppn); 663831337658SMarcel Moolenaar 663931337658SMarcel Moolenaar if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST) 6640d1a0d267SMarcel Moolenaar pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? ",\n" : ", "; 664131337658SMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 664231337658SMarcel Moolenaar 664331337658SMarcel Moolenaar rc = xo_printf(xop, "%s%*s\"%s\": {%s", 664431337658SMarcel Moolenaar pre_nl, xo_indent(xop), "", name, ppn); 664531337658SMarcel Moolenaar break; 6646d1a0d267SMarcel Moolenaar 6647d1a0d267SMarcel Moolenaar case XO_STYLE_SDPARAMS: 6648d1a0d267SMarcel Moolenaar break; 6649d1a0d267SMarcel Moolenaar 6650d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER: 6651d1a0d267SMarcel Moolenaar rc = xo_encoder_handle(xop, XO_OP_OPEN_CONTAINER, name, NULL); 6652d1a0d267SMarcel Moolenaar break; 665331337658SMarcel Moolenaar } 665431337658SMarcel Moolenaar 6655545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, 1, 1, XSS_OPEN_CONTAINER, 6656545ddfbeSMarcel Moolenaar xo_stack_flags(flags)); 6657545ddfbeSMarcel Moolenaar 665831337658SMarcel Moolenaar return rc; 665931337658SMarcel Moolenaar } 666031337658SMarcel Moolenaar 6661545ddfbeSMarcel Moolenaar static int 6662545ddfbeSMarcel Moolenaar xo_open_container_hf (xo_handle_t *xop, xo_xof_flags_t flags, const char *name) 6663545ddfbeSMarcel Moolenaar { 6664545ddfbeSMarcel Moolenaar return xo_transition(xop, flags, name, XSS_OPEN_CONTAINER); 6665545ddfbeSMarcel Moolenaar } 6666545ddfbeSMarcel Moolenaar 66678a6eceffSPhil Shafer xo_ssize_t 666831337658SMarcel Moolenaar xo_open_container_h (xo_handle_t *xop, const char *name) 666931337658SMarcel Moolenaar { 667031337658SMarcel Moolenaar return xo_open_container_hf(xop, 0, name); 667131337658SMarcel Moolenaar } 667231337658SMarcel Moolenaar 66738a6eceffSPhil Shafer xo_ssize_t 667431337658SMarcel Moolenaar xo_open_container (const char *name) 667531337658SMarcel Moolenaar { 667631337658SMarcel Moolenaar return xo_open_container_hf(NULL, 0, name); 667731337658SMarcel Moolenaar } 667831337658SMarcel Moolenaar 66798a6eceffSPhil Shafer xo_ssize_t 668031337658SMarcel Moolenaar xo_open_container_hd (xo_handle_t *xop, const char *name) 668131337658SMarcel Moolenaar { 668231337658SMarcel Moolenaar return xo_open_container_hf(xop, XOF_DTRT, name); 668331337658SMarcel Moolenaar } 668431337658SMarcel Moolenaar 66858a6eceffSPhil Shafer xo_ssize_t 668631337658SMarcel Moolenaar xo_open_container_d (const char *name) 668731337658SMarcel Moolenaar { 668831337658SMarcel Moolenaar return xo_open_container_hf(NULL, XOF_DTRT, name); 668931337658SMarcel Moolenaar } 669031337658SMarcel Moolenaar 6691545ddfbeSMarcel Moolenaar static int 6692545ddfbeSMarcel Moolenaar xo_do_close_container (xo_handle_t *xop, const char *name) 669331337658SMarcel Moolenaar { 669431337658SMarcel Moolenaar xop = xo_default(xop); 669531337658SMarcel Moolenaar 66968a6eceffSPhil Shafer ssize_t rc = 0; 6697d1a0d267SMarcel Moolenaar const char *ppn = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : ""; 669831337658SMarcel Moolenaar const char *pre_nl = ""; 669931337658SMarcel Moolenaar 670031337658SMarcel Moolenaar if (name == NULL) { 670131337658SMarcel Moolenaar xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth]; 670231337658SMarcel Moolenaar 670331337658SMarcel Moolenaar name = xsp->xs_name; 670431337658SMarcel Moolenaar if (name) { 67058a6eceffSPhil Shafer ssize_t len = strlen(name) + 1; 670631337658SMarcel Moolenaar /* We need to make a local copy; xo_depth_change will free it */ 670731337658SMarcel Moolenaar char *cp = alloca(len); 670831337658SMarcel Moolenaar memcpy(cp, name, len); 670931337658SMarcel Moolenaar name = cp; 6710545ddfbeSMarcel Moolenaar } else if (!(xsp->xs_flags & XSF_DTRT)) { 6711545ddfbeSMarcel Moolenaar xo_failure(xop, "missing name without 'dtrt' mode"); 671231337658SMarcel Moolenaar name = XO_FAILURE_NAME; 671331337658SMarcel Moolenaar } 6714545ddfbeSMarcel Moolenaar } 671531337658SMarcel Moolenaar 6716788ca347SMarcel Moolenaar switch (xo_style(xop)) { 671731337658SMarcel Moolenaar case XO_STYLE_XML: 6718545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, -1, -1, XSS_CLOSE_CONTAINER, 0); 671931337658SMarcel Moolenaar rc = xo_printf(xop, "%*s</%s>%s", xo_indent(xop), "", name, ppn); 672031337658SMarcel Moolenaar break; 672131337658SMarcel Moolenaar 672231337658SMarcel Moolenaar case XO_STYLE_JSON: 6723d1a0d267SMarcel Moolenaar pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : ""; 672431337658SMarcel Moolenaar ppn = (xop->xo_depth <= 1) ? "\n" : ""; 672531337658SMarcel Moolenaar 6726545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, -1, -1, XSS_CLOSE_CONTAINER, 0); 672731337658SMarcel Moolenaar rc = xo_printf(xop, "%s%*s}%s", pre_nl, xo_indent(xop), "", ppn); 672831337658SMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 672931337658SMarcel Moolenaar break; 673031337658SMarcel Moolenaar 673131337658SMarcel Moolenaar case XO_STYLE_HTML: 673231337658SMarcel Moolenaar case XO_STYLE_TEXT: 6733545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, -1, 0, XSS_CLOSE_CONTAINER, 0); 673431337658SMarcel Moolenaar break; 6735d1a0d267SMarcel Moolenaar 6736d1a0d267SMarcel Moolenaar case XO_STYLE_SDPARAMS: 6737d1a0d267SMarcel Moolenaar break; 6738d1a0d267SMarcel Moolenaar 6739d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER: 6740d1a0d267SMarcel Moolenaar xo_depth_change(xop, name, -1, 0, XSS_CLOSE_CONTAINER, 0); 6741d1a0d267SMarcel Moolenaar rc = xo_encoder_handle(xop, XO_OP_CLOSE_CONTAINER, name, NULL); 6742d1a0d267SMarcel Moolenaar break; 674331337658SMarcel Moolenaar } 674431337658SMarcel Moolenaar 674531337658SMarcel Moolenaar return rc; 674631337658SMarcel Moolenaar } 674731337658SMarcel Moolenaar 67488a6eceffSPhil Shafer xo_ssize_t 6749545ddfbeSMarcel Moolenaar xo_close_container_h (xo_handle_t *xop, const char *name) 6750545ddfbeSMarcel Moolenaar { 6751545ddfbeSMarcel Moolenaar return xo_transition(xop, 0, name, XSS_CLOSE_CONTAINER); 6752545ddfbeSMarcel Moolenaar } 6753545ddfbeSMarcel Moolenaar 67548a6eceffSPhil Shafer xo_ssize_t 675531337658SMarcel Moolenaar xo_close_container (const char *name) 675631337658SMarcel Moolenaar { 675731337658SMarcel Moolenaar return xo_close_container_h(NULL, name); 675831337658SMarcel Moolenaar } 675931337658SMarcel Moolenaar 67608a6eceffSPhil Shafer xo_ssize_t 676131337658SMarcel Moolenaar xo_close_container_hd (xo_handle_t *xop) 676231337658SMarcel Moolenaar { 676331337658SMarcel Moolenaar return xo_close_container_h(xop, NULL); 676431337658SMarcel Moolenaar } 676531337658SMarcel Moolenaar 67668a6eceffSPhil Shafer xo_ssize_t 676731337658SMarcel Moolenaar xo_close_container_d (void) 676831337658SMarcel Moolenaar { 676931337658SMarcel Moolenaar return xo_close_container_h(NULL, NULL); 677031337658SMarcel Moolenaar } 677131337658SMarcel Moolenaar 677231337658SMarcel Moolenaar static int 6773545ddfbeSMarcel Moolenaar xo_do_open_list (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name) 677431337658SMarcel Moolenaar { 67758a6eceffSPhil Shafer ssize_t rc = 0; 6776545ddfbeSMarcel Moolenaar int indent = 0; 6777545ddfbeSMarcel Moolenaar 677831337658SMarcel Moolenaar xop = xo_default(xop); 677931337658SMarcel Moolenaar 6780d1a0d267SMarcel Moolenaar const char *ppn = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : ""; 678131337658SMarcel Moolenaar const char *pre_nl = ""; 678231337658SMarcel Moolenaar 6783d1a0d267SMarcel Moolenaar switch (xo_style(xop)) { 6784d1a0d267SMarcel Moolenaar case XO_STYLE_JSON: 6785d1a0d267SMarcel Moolenaar 6786545ddfbeSMarcel Moolenaar indent = 1; 6787d1a0d267SMarcel Moolenaar if (!XOF_ISSET(xop, XOF_NO_TOP) 6788d1a0d267SMarcel Moolenaar && !XOIF_ISSET(xop, XOIF_TOP_EMITTED)) 6789788ca347SMarcel Moolenaar xo_emit_top(xop, ppn); 679031337658SMarcel Moolenaar 679131337658SMarcel Moolenaar if (name == NULL) { 679231337658SMarcel Moolenaar xo_failure(xop, "NULL passed for list name"); 679331337658SMarcel Moolenaar name = XO_FAILURE_NAME; 679431337658SMarcel Moolenaar } 679531337658SMarcel Moolenaar 679631337658SMarcel Moolenaar xo_stack_set_flags(xop); 679731337658SMarcel Moolenaar 679831337658SMarcel Moolenaar if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST) 6799d1a0d267SMarcel Moolenaar pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? ",\n" : ", "; 680031337658SMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 680131337658SMarcel Moolenaar 680231337658SMarcel Moolenaar rc = xo_printf(xop, "%s%*s\"%s\": [%s", 680331337658SMarcel Moolenaar pre_nl, xo_indent(xop), "", name, ppn); 6804d1a0d267SMarcel Moolenaar break; 6805d1a0d267SMarcel Moolenaar 6806d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER: 6807d1a0d267SMarcel Moolenaar rc = xo_encoder_handle(xop, XO_OP_OPEN_LIST, name, NULL); 6808d1a0d267SMarcel Moolenaar break; 6809545ddfbeSMarcel Moolenaar } 6810545ddfbeSMarcel Moolenaar 6811545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, 1, indent, XSS_OPEN_LIST, 6812545ddfbeSMarcel Moolenaar XSF_LIST | xo_stack_flags(flags)); 681331337658SMarcel Moolenaar 681431337658SMarcel Moolenaar return rc; 681531337658SMarcel Moolenaar } 681631337658SMarcel Moolenaar 6817545ddfbeSMarcel Moolenaar static int 6818545ddfbeSMarcel Moolenaar xo_open_list_hf (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name) 6819545ddfbeSMarcel Moolenaar { 6820545ddfbeSMarcel Moolenaar return xo_transition(xop, flags, name, XSS_OPEN_LIST); 6821545ddfbeSMarcel Moolenaar } 6822545ddfbeSMarcel Moolenaar 68238a6eceffSPhil Shafer xo_ssize_t 682442ff34c3SPhil Shafer xo_open_list_h (xo_handle_t *xop, const char *name) 682531337658SMarcel Moolenaar { 682631337658SMarcel Moolenaar return xo_open_list_hf(xop, 0, name); 682731337658SMarcel Moolenaar } 682831337658SMarcel Moolenaar 68298a6eceffSPhil Shafer xo_ssize_t 683031337658SMarcel Moolenaar xo_open_list (const char *name) 683131337658SMarcel Moolenaar { 683231337658SMarcel Moolenaar return xo_open_list_hf(NULL, 0, name); 683331337658SMarcel Moolenaar } 683431337658SMarcel Moolenaar 68358a6eceffSPhil Shafer xo_ssize_t 683642ff34c3SPhil Shafer xo_open_list_hd (xo_handle_t *xop, const char *name) 683731337658SMarcel Moolenaar { 683831337658SMarcel Moolenaar return xo_open_list_hf(xop, XOF_DTRT, name); 683931337658SMarcel Moolenaar } 684031337658SMarcel Moolenaar 68418a6eceffSPhil Shafer xo_ssize_t 684231337658SMarcel Moolenaar xo_open_list_d (const char *name) 684331337658SMarcel Moolenaar { 684431337658SMarcel Moolenaar return xo_open_list_hf(NULL, XOF_DTRT, name); 684531337658SMarcel Moolenaar } 684631337658SMarcel Moolenaar 6847545ddfbeSMarcel Moolenaar static int 6848545ddfbeSMarcel Moolenaar xo_do_close_list (xo_handle_t *xop, const char *name) 684931337658SMarcel Moolenaar { 68508a6eceffSPhil Shafer ssize_t rc = 0; 685131337658SMarcel Moolenaar const char *pre_nl = ""; 685231337658SMarcel Moolenaar 685331337658SMarcel Moolenaar if (name == NULL) { 685431337658SMarcel Moolenaar xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth]; 685531337658SMarcel Moolenaar 685631337658SMarcel Moolenaar name = xsp->xs_name; 685731337658SMarcel Moolenaar if (name) { 68588a6eceffSPhil Shafer ssize_t len = strlen(name) + 1; 685931337658SMarcel Moolenaar /* We need to make a local copy; xo_depth_change will free it */ 686031337658SMarcel Moolenaar char *cp = alloca(len); 686131337658SMarcel Moolenaar memcpy(cp, name, len); 686231337658SMarcel Moolenaar name = cp; 6863545ddfbeSMarcel Moolenaar } else if (!(xsp->xs_flags & XSF_DTRT)) { 6864545ddfbeSMarcel Moolenaar xo_failure(xop, "missing name without 'dtrt' mode"); 686531337658SMarcel Moolenaar name = XO_FAILURE_NAME; 686631337658SMarcel Moolenaar } 6867545ddfbeSMarcel Moolenaar } 686831337658SMarcel Moolenaar 6869d1a0d267SMarcel Moolenaar switch (xo_style(xop)) { 6870d1a0d267SMarcel Moolenaar case XO_STYLE_JSON: 687131337658SMarcel Moolenaar if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST) 6872d1a0d267SMarcel Moolenaar pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : ""; 687331337658SMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 687431337658SMarcel Moolenaar 6875545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, -1, -1, XSS_CLOSE_LIST, XSF_LIST); 687631337658SMarcel Moolenaar rc = xo_printf(xop, "%s%*s]", pre_nl, xo_indent(xop), ""); 687731337658SMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 6878d1a0d267SMarcel Moolenaar break; 687931337658SMarcel Moolenaar 6880d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER: 6881d1a0d267SMarcel Moolenaar xo_depth_change(xop, name, -1, 0, XSS_CLOSE_LIST, XSF_LIST); 6882d1a0d267SMarcel Moolenaar rc = xo_encoder_handle(xop, XO_OP_CLOSE_LIST, name, NULL); 6883d1a0d267SMarcel Moolenaar break; 6884d1a0d267SMarcel Moolenaar 6885d1a0d267SMarcel Moolenaar default: 6886545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, -1, 0, XSS_CLOSE_LIST, XSF_LIST); 6887545ddfbeSMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 6888d1a0d267SMarcel Moolenaar break; 6889545ddfbeSMarcel Moolenaar } 6890545ddfbeSMarcel Moolenaar 6891a0f704ffSMarcel Moolenaar return rc; 689231337658SMarcel Moolenaar } 689331337658SMarcel Moolenaar 68948a6eceffSPhil Shafer xo_ssize_t 6895545ddfbeSMarcel Moolenaar xo_close_list_h (xo_handle_t *xop, const char *name) 6896545ddfbeSMarcel Moolenaar { 6897545ddfbeSMarcel Moolenaar return xo_transition(xop, 0, name, XSS_CLOSE_LIST); 6898545ddfbeSMarcel Moolenaar } 6899545ddfbeSMarcel Moolenaar 69008a6eceffSPhil Shafer xo_ssize_t 690131337658SMarcel Moolenaar xo_close_list (const char *name) 690231337658SMarcel Moolenaar { 690331337658SMarcel Moolenaar return xo_close_list_h(NULL, name); 690431337658SMarcel Moolenaar } 690531337658SMarcel Moolenaar 69068a6eceffSPhil Shafer xo_ssize_t 690731337658SMarcel Moolenaar xo_close_list_hd (xo_handle_t *xop) 690831337658SMarcel Moolenaar { 690931337658SMarcel Moolenaar return xo_close_list_h(xop, NULL); 691031337658SMarcel Moolenaar } 691131337658SMarcel Moolenaar 69128a6eceffSPhil Shafer xo_ssize_t 691331337658SMarcel Moolenaar xo_close_list_d (void) 691431337658SMarcel Moolenaar { 691531337658SMarcel Moolenaar return xo_close_list_h(NULL, NULL); 691631337658SMarcel Moolenaar } 691731337658SMarcel Moolenaar 691831337658SMarcel Moolenaar static int 6919545ddfbeSMarcel Moolenaar xo_do_open_leaf_list (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name) 6920545ddfbeSMarcel Moolenaar { 69218a6eceffSPhil Shafer ssize_t rc = 0; 6922545ddfbeSMarcel Moolenaar int indent = 0; 6923545ddfbeSMarcel Moolenaar 6924545ddfbeSMarcel Moolenaar xop = xo_default(xop); 6925545ddfbeSMarcel Moolenaar 6926d1a0d267SMarcel Moolenaar const char *ppn = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : ""; 6927545ddfbeSMarcel Moolenaar const char *pre_nl = ""; 6928545ddfbeSMarcel Moolenaar 6929d1a0d267SMarcel Moolenaar switch (xo_style(xop)) { 6930d1a0d267SMarcel Moolenaar case XO_STYLE_JSON: 6931545ddfbeSMarcel Moolenaar indent = 1; 6932545ddfbeSMarcel Moolenaar 6933d1a0d267SMarcel Moolenaar if (!XOF_ISSET(xop, XOF_NO_TOP)) { 6934d1a0d267SMarcel Moolenaar if (!XOIF_ISSET(xop, XOIF_TOP_EMITTED)) { 6935545ddfbeSMarcel Moolenaar xo_printf(xop, "%*s{%s", xo_indent(xop), "", ppn); 6936d1a0d267SMarcel Moolenaar XOIF_SET(xop, XOIF_TOP_EMITTED); 6937545ddfbeSMarcel Moolenaar } 6938545ddfbeSMarcel Moolenaar } 6939545ddfbeSMarcel Moolenaar 6940545ddfbeSMarcel Moolenaar if (name == NULL) { 6941545ddfbeSMarcel Moolenaar xo_failure(xop, "NULL passed for list name"); 6942545ddfbeSMarcel Moolenaar name = XO_FAILURE_NAME; 6943545ddfbeSMarcel Moolenaar } 6944545ddfbeSMarcel Moolenaar 6945545ddfbeSMarcel Moolenaar xo_stack_set_flags(xop); 6946545ddfbeSMarcel Moolenaar 6947545ddfbeSMarcel Moolenaar if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST) 6948d1a0d267SMarcel Moolenaar pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? ",\n" : ", "; 6949545ddfbeSMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 6950545ddfbeSMarcel Moolenaar 6951545ddfbeSMarcel Moolenaar rc = xo_printf(xop, "%s%*s\"%s\": [%s", 6952545ddfbeSMarcel Moolenaar pre_nl, xo_indent(xop), "", name, ppn); 6953d1a0d267SMarcel Moolenaar break; 6954d1a0d267SMarcel Moolenaar 6955d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER: 6956d1a0d267SMarcel Moolenaar rc = xo_encoder_handle(xop, XO_OP_OPEN_LEAF_LIST, name, NULL); 6957d1a0d267SMarcel Moolenaar break; 6958545ddfbeSMarcel Moolenaar } 6959545ddfbeSMarcel Moolenaar 6960545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, 1, indent, XSS_OPEN_LEAF_LIST, 6961545ddfbeSMarcel Moolenaar XSF_LIST | xo_stack_flags(flags)); 6962545ddfbeSMarcel Moolenaar 6963545ddfbeSMarcel Moolenaar return rc; 6964545ddfbeSMarcel Moolenaar } 6965545ddfbeSMarcel Moolenaar 6966545ddfbeSMarcel Moolenaar static int 6967545ddfbeSMarcel Moolenaar xo_do_close_leaf_list (xo_handle_t *xop, const char *name) 6968545ddfbeSMarcel Moolenaar { 69698a6eceffSPhil Shafer ssize_t rc = 0; 6970545ddfbeSMarcel Moolenaar const char *pre_nl = ""; 6971545ddfbeSMarcel Moolenaar 6972545ddfbeSMarcel Moolenaar if (name == NULL) { 6973545ddfbeSMarcel Moolenaar xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth]; 6974545ddfbeSMarcel Moolenaar 6975545ddfbeSMarcel Moolenaar name = xsp->xs_name; 6976545ddfbeSMarcel Moolenaar if (name) { 69778a6eceffSPhil Shafer ssize_t len = strlen(name) + 1; 6978545ddfbeSMarcel Moolenaar /* We need to make a local copy; xo_depth_change will free it */ 6979545ddfbeSMarcel Moolenaar char *cp = alloca(len); 6980545ddfbeSMarcel Moolenaar memcpy(cp, name, len); 6981545ddfbeSMarcel Moolenaar name = cp; 6982545ddfbeSMarcel Moolenaar } else if (!(xsp->xs_flags & XSF_DTRT)) { 6983545ddfbeSMarcel Moolenaar xo_failure(xop, "missing name without 'dtrt' mode"); 6984545ddfbeSMarcel Moolenaar name = XO_FAILURE_NAME; 6985545ddfbeSMarcel Moolenaar } 6986545ddfbeSMarcel Moolenaar } 6987545ddfbeSMarcel Moolenaar 6988d1a0d267SMarcel Moolenaar switch (xo_style(xop)) { 6989d1a0d267SMarcel Moolenaar case XO_STYLE_JSON: 6990545ddfbeSMarcel Moolenaar if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST) 6991d1a0d267SMarcel Moolenaar pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : ""; 6992545ddfbeSMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 6993545ddfbeSMarcel Moolenaar 6994545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, -1, -1, XSS_CLOSE_LEAF_LIST, XSF_LIST); 6995545ddfbeSMarcel Moolenaar rc = xo_printf(xop, "%s%*s]", pre_nl, xo_indent(xop), ""); 6996545ddfbeSMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 6997d1a0d267SMarcel Moolenaar break; 6998545ddfbeSMarcel Moolenaar 6999d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER: 7000d1a0d267SMarcel Moolenaar rc = xo_encoder_handle(xop, XO_OP_CLOSE_LEAF_LIST, name, NULL); 7001ee5cf116SPhil Shafer /* FALLTHRU */ 7002d1a0d267SMarcel Moolenaar 7003d1a0d267SMarcel Moolenaar default: 7004545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, -1, 0, XSS_CLOSE_LEAF_LIST, XSF_LIST); 7005545ddfbeSMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 7006d1a0d267SMarcel Moolenaar break; 7007545ddfbeSMarcel Moolenaar } 7008545ddfbeSMarcel Moolenaar 7009545ddfbeSMarcel Moolenaar return rc; 7010545ddfbeSMarcel Moolenaar } 7011545ddfbeSMarcel Moolenaar 7012545ddfbeSMarcel Moolenaar static int 7013545ddfbeSMarcel Moolenaar xo_do_open_instance (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name) 701431337658SMarcel Moolenaar { 701531337658SMarcel Moolenaar xop = xo_default(xop); 701631337658SMarcel Moolenaar 70178a6eceffSPhil Shafer ssize_t rc = 0; 7018d1a0d267SMarcel Moolenaar const char *ppn = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : ""; 701931337658SMarcel Moolenaar const char *pre_nl = ""; 702031337658SMarcel Moolenaar 702131337658SMarcel Moolenaar flags |= xop->xo_flags; 702231337658SMarcel Moolenaar 702331337658SMarcel Moolenaar if (name == NULL) { 702431337658SMarcel Moolenaar xo_failure(xop, "NULL passed for instance name"); 702531337658SMarcel Moolenaar name = XO_FAILURE_NAME; 702631337658SMarcel Moolenaar } 702731337658SMarcel Moolenaar 7028788ca347SMarcel Moolenaar switch (xo_style(xop)) { 702931337658SMarcel Moolenaar case XO_STYLE_XML: 7030545ddfbeSMarcel Moolenaar rc = xo_printf(xop, "%*s<%s", xo_indent(xop), "", name); 7031545ddfbeSMarcel Moolenaar 7032545ddfbeSMarcel Moolenaar if (xop->xo_attrs.xb_curp != xop->xo_attrs.xb_bufp) { 7033545ddfbeSMarcel Moolenaar rc += xop->xo_attrs.xb_curp - xop->xo_attrs.xb_bufp; 7034545ddfbeSMarcel Moolenaar xo_data_append(xop, xop->xo_attrs.xb_bufp, 7035545ddfbeSMarcel Moolenaar xop->xo_attrs.xb_curp - xop->xo_attrs.xb_bufp); 7036545ddfbeSMarcel Moolenaar xop->xo_attrs.xb_curp = xop->xo_attrs.xb_bufp; 7037545ddfbeSMarcel Moolenaar } 7038545ddfbeSMarcel Moolenaar 7039545ddfbeSMarcel Moolenaar rc += xo_printf(xop, ">%s", ppn); 704031337658SMarcel Moolenaar break; 704131337658SMarcel Moolenaar 704231337658SMarcel Moolenaar case XO_STYLE_JSON: 704331337658SMarcel Moolenaar xo_stack_set_flags(xop); 704431337658SMarcel Moolenaar 704531337658SMarcel Moolenaar if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST) 7046d1a0d267SMarcel Moolenaar pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? ",\n" : ", "; 704731337658SMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 704831337658SMarcel Moolenaar 704931337658SMarcel Moolenaar rc = xo_printf(xop, "%s%*s{%s", 705031337658SMarcel Moolenaar pre_nl, xo_indent(xop), "", ppn); 705131337658SMarcel Moolenaar break; 7052d1a0d267SMarcel Moolenaar 7053d1a0d267SMarcel Moolenaar case XO_STYLE_SDPARAMS: 7054d1a0d267SMarcel Moolenaar break; 7055d1a0d267SMarcel Moolenaar 7056d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER: 7057d1a0d267SMarcel Moolenaar rc = xo_encoder_handle(xop, XO_OP_OPEN_INSTANCE, name, NULL); 7058d1a0d267SMarcel Moolenaar break; 705931337658SMarcel Moolenaar } 706031337658SMarcel Moolenaar 7061545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, 1, 1, XSS_OPEN_INSTANCE, xo_stack_flags(flags)); 7062545ddfbeSMarcel Moolenaar 706331337658SMarcel Moolenaar return rc; 706431337658SMarcel Moolenaar } 706531337658SMarcel Moolenaar 7066545ddfbeSMarcel Moolenaar static int 7067545ddfbeSMarcel Moolenaar xo_open_instance_hf (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name) 7068545ddfbeSMarcel Moolenaar { 7069545ddfbeSMarcel Moolenaar return xo_transition(xop, flags, name, XSS_OPEN_INSTANCE); 7070545ddfbeSMarcel Moolenaar } 7071545ddfbeSMarcel Moolenaar 70728a6eceffSPhil Shafer xo_ssize_t 707331337658SMarcel Moolenaar xo_open_instance_h (xo_handle_t *xop, const char *name) 707431337658SMarcel Moolenaar { 707531337658SMarcel Moolenaar return xo_open_instance_hf(xop, 0, name); 707631337658SMarcel Moolenaar } 707731337658SMarcel Moolenaar 70788a6eceffSPhil Shafer xo_ssize_t 707931337658SMarcel Moolenaar xo_open_instance (const char *name) 708031337658SMarcel Moolenaar { 708131337658SMarcel Moolenaar return xo_open_instance_hf(NULL, 0, name); 708231337658SMarcel Moolenaar } 708331337658SMarcel Moolenaar 70848a6eceffSPhil Shafer xo_ssize_t 708531337658SMarcel Moolenaar xo_open_instance_hd (xo_handle_t *xop, const char *name) 708631337658SMarcel Moolenaar { 708731337658SMarcel Moolenaar return xo_open_instance_hf(xop, XOF_DTRT, name); 708831337658SMarcel Moolenaar } 708931337658SMarcel Moolenaar 70908a6eceffSPhil Shafer xo_ssize_t 709131337658SMarcel Moolenaar xo_open_instance_d (const char *name) 709231337658SMarcel Moolenaar { 709331337658SMarcel Moolenaar return xo_open_instance_hf(NULL, XOF_DTRT, name); 709431337658SMarcel Moolenaar } 709531337658SMarcel Moolenaar 7096545ddfbeSMarcel Moolenaar static int 7097545ddfbeSMarcel Moolenaar xo_do_close_instance (xo_handle_t *xop, const char *name) 709831337658SMarcel Moolenaar { 709931337658SMarcel Moolenaar xop = xo_default(xop); 710031337658SMarcel Moolenaar 71018a6eceffSPhil Shafer ssize_t rc = 0; 7102d1a0d267SMarcel Moolenaar const char *ppn = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : ""; 710331337658SMarcel Moolenaar const char *pre_nl = ""; 710431337658SMarcel Moolenaar 710531337658SMarcel Moolenaar if (name == NULL) { 710631337658SMarcel Moolenaar xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth]; 710731337658SMarcel Moolenaar 710831337658SMarcel Moolenaar name = xsp->xs_name; 710931337658SMarcel Moolenaar if (name) { 71108a6eceffSPhil Shafer ssize_t len = strlen(name) + 1; 711131337658SMarcel Moolenaar /* We need to make a local copy; xo_depth_change will free it */ 711231337658SMarcel Moolenaar char *cp = alloca(len); 711331337658SMarcel Moolenaar memcpy(cp, name, len); 711431337658SMarcel Moolenaar name = cp; 7115545ddfbeSMarcel Moolenaar } else if (!(xsp->xs_flags & XSF_DTRT)) { 7116545ddfbeSMarcel Moolenaar xo_failure(xop, "missing name without 'dtrt' mode"); 711731337658SMarcel Moolenaar name = XO_FAILURE_NAME; 711831337658SMarcel Moolenaar } 7119545ddfbeSMarcel Moolenaar } 712031337658SMarcel Moolenaar 7121788ca347SMarcel Moolenaar switch (xo_style(xop)) { 712231337658SMarcel Moolenaar case XO_STYLE_XML: 7123545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, -1, -1, XSS_CLOSE_INSTANCE, 0); 712431337658SMarcel Moolenaar rc = xo_printf(xop, "%*s</%s>%s", xo_indent(xop), "", name, ppn); 712531337658SMarcel Moolenaar break; 712631337658SMarcel Moolenaar 712731337658SMarcel Moolenaar case XO_STYLE_JSON: 7128d1a0d267SMarcel Moolenaar pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : ""; 712931337658SMarcel Moolenaar 7130545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, -1, -1, XSS_CLOSE_INSTANCE, 0); 713131337658SMarcel Moolenaar rc = xo_printf(xop, "%s%*s}", pre_nl, xo_indent(xop), ""); 713231337658SMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 713331337658SMarcel Moolenaar break; 713431337658SMarcel Moolenaar 713531337658SMarcel Moolenaar case XO_STYLE_HTML: 713631337658SMarcel Moolenaar case XO_STYLE_TEXT: 7137545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, -1, 0, XSS_CLOSE_INSTANCE, 0); 713831337658SMarcel Moolenaar break; 7139d1a0d267SMarcel Moolenaar 7140d1a0d267SMarcel Moolenaar case XO_STYLE_SDPARAMS: 7141d1a0d267SMarcel Moolenaar break; 7142d1a0d267SMarcel Moolenaar 7143d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER: 7144d1a0d267SMarcel Moolenaar xo_depth_change(xop, name, -1, 0, XSS_CLOSE_INSTANCE, 0); 7145d1a0d267SMarcel Moolenaar rc = xo_encoder_handle(xop, XO_OP_CLOSE_INSTANCE, name, NULL); 7146d1a0d267SMarcel Moolenaar break; 714731337658SMarcel Moolenaar } 714831337658SMarcel Moolenaar 714931337658SMarcel Moolenaar return rc; 715031337658SMarcel Moolenaar } 715131337658SMarcel Moolenaar 71528a6eceffSPhil Shafer xo_ssize_t 7153545ddfbeSMarcel Moolenaar xo_close_instance_h (xo_handle_t *xop, const char *name) 7154545ddfbeSMarcel Moolenaar { 7155545ddfbeSMarcel Moolenaar return xo_transition(xop, 0, name, XSS_CLOSE_INSTANCE); 7156545ddfbeSMarcel Moolenaar } 7157545ddfbeSMarcel Moolenaar 71588a6eceffSPhil Shafer xo_ssize_t 715931337658SMarcel Moolenaar xo_close_instance (const char *name) 716031337658SMarcel Moolenaar { 716131337658SMarcel Moolenaar return xo_close_instance_h(NULL, name); 716231337658SMarcel Moolenaar } 716331337658SMarcel Moolenaar 71648a6eceffSPhil Shafer xo_ssize_t 716531337658SMarcel Moolenaar xo_close_instance_hd (xo_handle_t *xop) 716631337658SMarcel Moolenaar { 716731337658SMarcel Moolenaar return xo_close_instance_h(xop, NULL); 716831337658SMarcel Moolenaar } 716931337658SMarcel Moolenaar 71708a6eceffSPhil Shafer xo_ssize_t 717131337658SMarcel Moolenaar xo_close_instance_d (void) 717231337658SMarcel Moolenaar { 717331337658SMarcel Moolenaar return xo_close_instance_h(NULL, NULL); 717431337658SMarcel Moolenaar } 717531337658SMarcel Moolenaar 7176545ddfbeSMarcel Moolenaar static int 7177545ddfbeSMarcel Moolenaar xo_do_close_all (xo_handle_t *xop, xo_stack_t *limit) 7178545ddfbeSMarcel Moolenaar { 7179545ddfbeSMarcel Moolenaar xo_stack_t *xsp; 71808a6eceffSPhil Shafer ssize_t rc = 0; 7181545ddfbeSMarcel Moolenaar xo_xsf_flags_t flags; 7182545ddfbeSMarcel Moolenaar 7183545ddfbeSMarcel Moolenaar for (xsp = &xop->xo_stack[xop->xo_depth]; xsp >= limit; xsp--) { 7184545ddfbeSMarcel Moolenaar switch (xsp->xs_state) { 7185545ddfbeSMarcel Moolenaar case XSS_INIT: 7186545ddfbeSMarcel Moolenaar /* Nothing */ 7187545ddfbeSMarcel Moolenaar rc = 0; 7188545ddfbeSMarcel Moolenaar break; 7189545ddfbeSMarcel Moolenaar 7190545ddfbeSMarcel Moolenaar case XSS_OPEN_CONTAINER: 7191545ddfbeSMarcel Moolenaar rc = xo_do_close_container(xop, NULL); 7192545ddfbeSMarcel Moolenaar break; 7193545ddfbeSMarcel Moolenaar 7194545ddfbeSMarcel Moolenaar case XSS_OPEN_LIST: 7195545ddfbeSMarcel Moolenaar rc = xo_do_close_list(xop, NULL); 7196545ddfbeSMarcel Moolenaar break; 7197545ddfbeSMarcel Moolenaar 7198545ddfbeSMarcel Moolenaar case XSS_OPEN_INSTANCE: 7199545ddfbeSMarcel Moolenaar rc = xo_do_close_instance(xop, NULL); 7200545ddfbeSMarcel Moolenaar break; 7201545ddfbeSMarcel Moolenaar 7202545ddfbeSMarcel Moolenaar case XSS_OPEN_LEAF_LIST: 7203545ddfbeSMarcel Moolenaar rc = xo_do_close_leaf_list(xop, NULL); 7204545ddfbeSMarcel Moolenaar break; 7205545ddfbeSMarcel Moolenaar 7206545ddfbeSMarcel Moolenaar case XSS_MARKER: 7207545ddfbeSMarcel Moolenaar flags = xsp->xs_flags & XSF_MARKER_FLAGS; 7208545ddfbeSMarcel Moolenaar xo_depth_change(xop, xsp->xs_name, -1, 0, XSS_MARKER, 0); 7209545ddfbeSMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= flags; 7210545ddfbeSMarcel Moolenaar rc = 0; 7211545ddfbeSMarcel Moolenaar break; 7212545ddfbeSMarcel Moolenaar } 7213545ddfbeSMarcel Moolenaar 7214545ddfbeSMarcel Moolenaar if (rc < 0) 7215545ddfbeSMarcel Moolenaar xo_failure(xop, "close %d failed: %d", xsp->xs_state, rc); 7216545ddfbeSMarcel Moolenaar } 7217545ddfbeSMarcel Moolenaar 7218545ddfbeSMarcel Moolenaar return 0; 7219545ddfbeSMarcel Moolenaar } 7220545ddfbeSMarcel Moolenaar 7221545ddfbeSMarcel Moolenaar /* 7222545ddfbeSMarcel Moolenaar * This function is responsible for clearing out whatever is needed 7223545ddfbeSMarcel Moolenaar * to get to the desired state, if possible. 7224545ddfbeSMarcel Moolenaar */ 7225545ddfbeSMarcel Moolenaar static int 7226545ddfbeSMarcel Moolenaar xo_do_close (xo_handle_t *xop, const char *name, xo_state_t new_state) 7227545ddfbeSMarcel Moolenaar { 7228545ddfbeSMarcel Moolenaar xo_stack_t *xsp, *limit = NULL; 72298a6eceffSPhil Shafer ssize_t rc; 7230545ddfbeSMarcel Moolenaar xo_state_t need_state = new_state; 7231545ddfbeSMarcel Moolenaar 7232545ddfbeSMarcel Moolenaar if (new_state == XSS_CLOSE_CONTAINER) 7233545ddfbeSMarcel Moolenaar need_state = XSS_OPEN_CONTAINER; 7234545ddfbeSMarcel Moolenaar else if (new_state == XSS_CLOSE_LIST) 7235545ddfbeSMarcel Moolenaar need_state = XSS_OPEN_LIST; 7236545ddfbeSMarcel Moolenaar else if (new_state == XSS_CLOSE_INSTANCE) 7237545ddfbeSMarcel Moolenaar need_state = XSS_OPEN_INSTANCE; 7238545ddfbeSMarcel Moolenaar else if (new_state == XSS_CLOSE_LEAF_LIST) 7239545ddfbeSMarcel Moolenaar need_state = XSS_OPEN_LEAF_LIST; 7240545ddfbeSMarcel Moolenaar else if (new_state == XSS_MARKER) 7241545ddfbeSMarcel Moolenaar need_state = XSS_MARKER; 7242545ddfbeSMarcel Moolenaar else 7243545ddfbeSMarcel Moolenaar return 0; /* Unknown or useless new states are ignored */ 7244545ddfbeSMarcel Moolenaar 7245545ddfbeSMarcel Moolenaar for (xsp = &xop->xo_stack[xop->xo_depth]; xsp > xop->xo_stack; xsp--) { 7246545ddfbeSMarcel Moolenaar /* 7247545ddfbeSMarcel Moolenaar * Marker's normally stop us from going any further, unless 7248545ddfbeSMarcel Moolenaar * we are popping a marker (new_state == XSS_MARKER). 7249545ddfbeSMarcel Moolenaar */ 7250545ddfbeSMarcel Moolenaar if (xsp->xs_state == XSS_MARKER && need_state != XSS_MARKER) { 7251545ddfbeSMarcel Moolenaar if (name) { 7252545ddfbeSMarcel Moolenaar xo_failure(xop, "close (xo_%s) fails at marker '%s'; " 7253545ddfbeSMarcel Moolenaar "not found '%s'", 7254545ddfbeSMarcel Moolenaar xo_state_name(new_state), 7255545ddfbeSMarcel Moolenaar xsp->xs_name, name); 7256545ddfbeSMarcel Moolenaar return 0; 7257545ddfbeSMarcel Moolenaar 7258545ddfbeSMarcel Moolenaar } else { 7259545ddfbeSMarcel Moolenaar limit = xsp; 7260545ddfbeSMarcel Moolenaar xo_failure(xop, "close stops at marker '%s'", xsp->xs_name); 7261545ddfbeSMarcel Moolenaar } 7262545ddfbeSMarcel Moolenaar break; 7263545ddfbeSMarcel Moolenaar } 7264545ddfbeSMarcel Moolenaar 7265545ddfbeSMarcel Moolenaar if (xsp->xs_state != need_state) 7266545ddfbeSMarcel Moolenaar continue; 7267545ddfbeSMarcel Moolenaar 7268545ddfbeSMarcel Moolenaar if (name && xsp->xs_name && strcmp(name, xsp->xs_name) != 0) 7269545ddfbeSMarcel Moolenaar continue; 7270545ddfbeSMarcel Moolenaar 7271545ddfbeSMarcel Moolenaar limit = xsp; 7272545ddfbeSMarcel Moolenaar break; 7273545ddfbeSMarcel Moolenaar } 7274545ddfbeSMarcel Moolenaar 7275545ddfbeSMarcel Moolenaar if (limit == NULL) { 7276545ddfbeSMarcel Moolenaar xo_failure(xop, "xo_%s can't find match for '%s'", 7277545ddfbeSMarcel Moolenaar xo_state_name(new_state), name); 7278545ddfbeSMarcel Moolenaar return 0; 7279545ddfbeSMarcel Moolenaar } 7280545ddfbeSMarcel Moolenaar 7281545ddfbeSMarcel Moolenaar rc = xo_do_close_all(xop, limit); 7282545ddfbeSMarcel Moolenaar 7283545ddfbeSMarcel Moolenaar return rc; 7284545ddfbeSMarcel Moolenaar } 7285545ddfbeSMarcel Moolenaar 7286545ddfbeSMarcel Moolenaar /* 7287545ddfbeSMarcel Moolenaar * We are in a given state and need to transition to the new state. 7288545ddfbeSMarcel Moolenaar */ 72898a6eceffSPhil Shafer static ssize_t 7290545ddfbeSMarcel Moolenaar xo_transition (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name, 7291545ddfbeSMarcel Moolenaar xo_state_t new_state) 7292545ddfbeSMarcel Moolenaar { 7293545ddfbeSMarcel Moolenaar xo_stack_t *xsp; 72948a6eceffSPhil Shafer ssize_t rc = 0; 7295545ddfbeSMarcel Moolenaar int old_state, on_marker; 7296545ddfbeSMarcel Moolenaar 7297545ddfbeSMarcel Moolenaar xop = xo_default(xop); 7298545ddfbeSMarcel Moolenaar 7299545ddfbeSMarcel Moolenaar xsp = &xop->xo_stack[xop->xo_depth]; 7300545ddfbeSMarcel Moolenaar old_state = xsp->xs_state; 7301545ddfbeSMarcel Moolenaar on_marker = (old_state == XSS_MARKER); 7302545ddfbeSMarcel Moolenaar 7303545ddfbeSMarcel Moolenaar /* If there's a marker on top of the stack, we need to find a real state */ 7304545ddfbeSMarcel Moolenaar while (old_state == XSS_MARKER) { 7305545ddfbeSMarcel Moolenaar if (xsp == xop->xo_stack) 7306545ddfbeSMarcel Moolenaar break; 7307545ddfbeSMarcel Moolenaar xsp -= 1; 7308545ddfbeSMarcel Moolenaar old_state = xsp->xs_state; 7309545ddfbeSMarcel Moolenaar } 7310545ddfbeSMarcel Moolenaar 7311545ddfbeSMarcel Moolenaar /* 7312545ddfbeSMarcel Moolenaar * At this point, the list of possible states are: 7313545ddfbeSMarcel Moolenaar * XSS_INIT, XSS_OPEN_CONTAINER, XSS_OPEN_LIST, 7314545ddfbeSMarcel Moolenaar * XSS_OPEN_INSTANCE, XSS_OPEN_LEAF_LIST, XSS_DISCARDING 7315545ddfbeSMarcel Moolenaar */ 7316545ddfbeSMarcel Moolenaar switch (XSS_TRANSITION(old_state, new_state)) { 7317545ddfbeSMarcel Moolenaar 7318545ddfbeSMarcel Moolenaar open_container: 7319545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_INIT, XSS_OPEN_CONTAINER): 7320545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_OPEN_CONTAINER): 7321545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_OPEN_CONTAINER): 7322545ddfbeSMarcel Moolenaar rc = xo_do_open_container(xop, flags, name); 7323545ddfbeSMarcel Moolenaar break; 7324545ddfbeSMarcel Moolenaar 7325545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LIST, XSS_OPEN_CONTAINER): 7326545ddfbeSMarcel Moolenaar if (on_marker) 7327545ddfbeSMarcel Moolenaar goto marker_prevents_close; 7328545ddfbeSMarcel Moolenaar rc = xo_do_close_list(xop, NULL); 7329545ddfbeSMarcel Moolenaar if (rc >= 0) 7330545ddfbeSMarcel Moolenaar goto open_container; 7331545ddfbeSMarcel Moolenaar break; 7332545ddfbeSMarcel Moolenaar 7333545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_OPEN_CONTAINER): 7334545ddfbeSMarcel Moolenaar if (on_marker) 7335545ddfbeSMarcel Moolenaar goto marker_prevents_close; 7336545ddfbeSMarcel Moolenaar rc = xo_do_close_leaf_list(xop, NULL); 7337545ddfbeSMarcel Moolenaar if (rc >= 0) 7338545ddfbeSMarcel Moolenaar goto open_container; 7339545ddfbeSMarcel Moolenaar break; 7340545ddfbeSMarcel Moolenaar 7341545ddfbeSMarcel Moolenaar /*close_container:*/ 7342545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_CLOSE_CONTAINER): 7343545ddfbeSMarcel Moolenaar if (on_marker) 7344545ddfbeSMarcel Moolenaar goto marker_prevents_close; 7345545ddfbeSMarcel Moolenaar rc = xo_do_close(xop, name, new_state); 7346545ddfbeSMarcel Moolenaar break; 7347545ddfbeSMarcel Moolenaar 7348545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_INIT, XSS_CLOSE_CONTAINER): 7349545ddfbeSMarcel Moolenaar /* This is an exception for "xo --close" */ 7350545ddfbeSMarcel Moolenaar rc = xo_do_close_container(xop, name); 7351545ddfbeSMarcel Moolenaar break; 7352545ddfbeSMarcel Moolenaar 7353545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LIST, XSS_CLOSE_CONTAINER): 7354545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_CLOSE_CONTAINER): 7355545ddfbeSMarcel Moolenaar if (on_marker) 7356545ddfbeSMarcel Moolenaar goto marker_prevents_close; 7357545ddfbeSMarcel Moolenaar rc = xo_do_close(xop, name, new_state); 7358545ddfbeSMarcel Moolenaar break; 7359545ddfbeSMarcel Moolenaar 7360545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_CLOSE_CONTAINER): 7361545ddfbeSMarcel Moolenaar if (on_marker) 7362545ddfbeSMarcel Moolenaar goto marker_prevents_close; 7363545ddfbeSMarcel Moolenaar rc = xo_do_close_leaf_list(xop, NULL); 7364545ddfbeSMarcel Moolenaar if (rc >= 0) 7365545ddfbeSMarcel Moolenaar rc = xo_do_close(xop, name, new_state); 7366545ddfbeSMarcel Moolenaar break; 7367545ddfbeSMarcel Moolenaar 7368545ddfbeSMarcel Moolenaar open_list: 7369545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_INIT, XSS_OPEN_LIST): 7370545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_OPEN_LIST): 7371545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_OPEN_LIST): 7372545ddfbeSMarcel Moolenaar rc = xo_do_open_list(xop, flags, name); 7373545ddfbeSMarcel Moolenaar break; 7374545ddfbeSMarcel Moolenaar 7375545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LIST, XSS_OPEN_LIST): 7376545ddfbeSMarcel Moolenaar if (on_marker) 7377545ddfbeSMarcel Moolenaar goto marker_prevents_close; 7378545ddfbeSMarcel Moolenaar rc = xo_do_close_list(xop, NULL); 7379545ddfbeSMarcel Moolenaar if (rc >= 0) 7380545ddfbeSMarcel Moolenaar goto open_list; 7381545ddfbeSMarcel Moolenaar break; 7382545ddfbeSMarcel Moolenaar 7383545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_OPEN_LIST): 7384545ddfbeSMarcel Moolenaar if (on_marker) 7385545ddfbeSMarcel Moolenaar goto marker_prevents_close; 7386545ddfbeSMarcel Moolenaar rc = xo_do_close_leaf_list(xop, NULL); 7387545ddfbeSMarcel Moolenaar if (rc >= 0) 7388545ddfbeSMarcel Moolenaar goto open_list; 7389545ddfbeSMarcel Moolenaar break; 7390545ddfbeSMarcel Moolenaar 7391545ddfbeSMarcel Moolenaar /*close_list:*/ 7392545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LIST, XSS_CLOSE_LIST): 7393545ddfbeSMarcel Moolenaar if (on_marker) 7394545ddfbeSMarcel Moolenaar goto marker_prevents_close; 7395545ddfbeSMarcel Moolenaar rc = xo_do_close(xop, name, new_state); 7396545ddfbeSMarcel Moolenaar break; 7397545ddfbeSMarcel Moolenaar 7398545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_INIT, XSS_CLOSE_LIST): 7399545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_CLOSE_LIST): 7400545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_CLOSE_LIST): 7401545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_CLOSE_LIST): 7402545ddfbeSMarcel Moolenaar rc = xo_do_close(xop, name, new_state); 7403545ddfbeSMarcel Moolenaar break; 7404545ddfbeSMarcel Moolenaar 7405545ddfbeSMarcel Moolenaar open_instance: 7406545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LIST, XSS_OPEN_INSTANCE): 7407545ddfbeSMarcel Moolenaar rc = xo_do_open_instance(xop, flags, name); 7408545ddfbeSMarcel Moolenaar break; 7409545ddfbeSMarcel Moolenaar 7410545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_INIT, XSS_OPEN_INSTANCE): 7411788ca347SMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_OPEN_INSTANCE): 7412545ddfbeSMarcel Moolenaar rc = xo_do_open_list(xop, flags, name); 7413545ddfbeSMarcel Moolenaar if (rc >= 0) 7414545ddfbeSMarcel Moolenaar goto open_instance; 7415545ddfbeSMarcel Moolenaar break; 7416545ddfbeSMarcel Moolenaar 7417545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_OPEN_INSTANCE): 7418545ddfbeSMarcel Moolenaar if (on_marker) { 7419545ddfbeSMarcel Moolenaar rc = xo_do_open_list(xop, flags, name); 7420545ddfbeSMarcel Moolenaar } else { 7421545ddfbeSMarcel Moolenaar rc = xo_do_close_instance(xop, NULL); 7422545ddfbeSMarcel Moolenaar } 7423545ddfbeSMarcel Moolenaar if (rc >= 0) 7424545ddfbeSMarcel Moolenaar goto open_instance; 7425545ddfbeSMarcel Moolenaar break; 7426545ddfbeSMarcel Moolenaar 7427545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_OPEN_INSTANCE): 7428545ddfbeSMarcel Moolenaar if (on_marker) 7429545ddfbeSMarcel Moolenaar goto marker_prevents_close; 7430545ddfbeSMarcel Moolenaar rc = xo_do_close_leaf_list(xop, NULL); 7431545ddfbeSMarcel Moolenaar if (rc >= 0) 7432545ddfbeSMarcel Moolenaar goto open_instance; 7433545ddfbeSMarcel Moolenaar break; 7434545ddfbeSMarcel Moolenaar 7435545ddfbeSMarcel Moolenaar /*close_instance:*/ 7436545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_CLOSE_INSTANCE): 7437545ddfbeSMarcel Moolenaar if (on_marker) 7438545ddfbeSMarcel Moolenaar goto marker_prevents_close; 7439545ddfbeSMarcel Moolenaar rc = xo_do_close_instance(xop, name); 7440545ddfbeSMarcel Moolenaar break; 7441545ddfbeSMarcel Moolenaar 7442545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_INIT, XSS_CLOSE_INSTANCE): 7443545ddfbeSMarcel Moolenaar /* This one makes no sense; ignore it */ 7444788ca347SMarcel Moolenaar xo_failure(xop, "xo_close_instance ignored when called from " 7445788ca347SMarcel Moolenaar "initial state ('%s')", name ?: "(unknown)"); 7446545ddfbeSMarcel Moolenaar break; 7447545ddfbeSMarcel Moolenaar 7448545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_CLOSE_INSTANCE): 7449545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LIST, XSS_CLOSE_INSTANCE): 7450545ddfbeSMarcel Moolenaar if (on_marker) 7451545ddfbeSMarcel Moolenaar goto marker_prevents_close; 7452545ddfbeSMarcel Moolenaar rc = xo_do_close(xop, name, new_state); 7453545ddfbeSMarcel Moolenaar break; 7454545ddfbeSMarcel Moolenaar 7455545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_CLOSE_INSTANCE): 7456545ddfbeSMarcel Moolenaar if (on_marker) 7457545ddfbeSMarcel Moolenaar goto marker_prevents_close; 7458545ddfbeSMarcel Moolenaar rc = xo_do_close_leaf_list(xop, NULL); 7459545ddfbeSMarcel Moolenaar if (rc >= 0) 7460545ddfbeSMarcel Moolenaar rc = xo_do_close(xop, name, new_state); 7461545ddfbeSMarcel Moolenaar break; 7462545ddfbeSMarcel Moolenaar 7463545ddfbeSMarcel Moolenaar open_leaf_list: 7464545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_OPEN_LEAF_LIST): 7465545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_OPEN_LEAF_LIST): 7466545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_INIT, XSS_OPEN_LEAF_LIST): 7467545ddfbeSMarcel Moolenaar rc = xo_do_open_leaf_list(xop, flags, name); 7468545ddfbeSMarcel Moolenaar break; 7469545ddfbeSMarcel Moolenaar 7470545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LIST, XSS_OPEN_LEAF_LIST): 7471545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_OPEN_LEAF_LIST): 7472545ddfbeSMarcel Moolenaar if (on_marker) 7473545ddfbeSMarcel Moolenaar goto marker_prevents_close; 7474545ddfbeSMarcel Moolenaar rc = xo_do_close_list(xop, NULL); 7475545ddfbeSMarcel Moolenaar if (rc >= 0) 7476545ddfbeSMarcel Moolenaar goto open_leaf_list; 7477545ddfbeSMarcel Moolenaar break; 7478545ddfbeSMarcel Moolenaar 7479545ddfbeSMarcel Moolenaar /*close_leaf_list:*/ 7480545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_CLOSE_LEAF_LIST): 7481545ddfbeSMarcel Moolenaar if (on_marker) 7482545ddfbeSMarcel Moolenaar goto marker_prevents_close; 7483545ddfbeSMarcel Moolenaar rc = xo_do_close_leaf_list(xop, name); 7484545ddfbeSMarcel Moolenaar break; 7485545ddfbeSMarcel Moolenaar 7486545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_INIT, XSS_CLOSE_LEAF_LIST): 7487545ddfbeSMarcel Moolenaar /* Makes no sense; ignore */ 7488788ca347SMarcel Moolenaar xo_failure(xop, "xo_close_leaf_list ignored when called from " 7489788ca347SMarcel Moolenaar "initial state ('%s')", name ?: "(unknown)"); 7490545ddfbeSMarcel Moolenaar break; 7491545ddfbeSMarcel Moolenaar 7492545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_CLOSE_LEAF_LIST): 7493545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LIST, XSS_CLOSE_LEAF_LIST): 7494545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_CLOSE_LEAF_LIST): 7495545ddfbeSMarcel Moolenaar if (on_marker) 7496545ddfbeSMarcel Moolenaar goto marker_prevents_close; 7497545ddfbeSMarcel Moolenaar rc = xo_do_close(xop, name, new_state); 7498545ddfbeSMarcel Moolenaar break; 7499545ddfbeSMarcel Moolenaar 7500545ddfbeSMarcel Moolenaar /*emit:*/ 7501545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_EMIT): 7502545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_EMIT): 7503545ddfbeSMarcel Moolenaar break; 7504545ddfbeSMarcel Moolenaar 7505545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LIST, XSS_EMIT): 7506545ddfbeSMarcel Moolenaar if (on_marker) 7507545ddfbeSMarcel Moolenaar goto marker_prevents_close; 7508545ddfbeSMarcel Moolenaar rc = xo_do_close(xop, NULL, XSS_CLOSE_LIST); 7509545ddfbeSMarcel Moolenaar break; 7510545ddfbeSMarcel Moolenaar 7511545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_INIT, XSS_EMIT): 7512545ddfbeSMarcel Moolenaar break; 7513545ddfbeSMarcel Moolenaar 7514545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_EMIT): 7515545ddfbeSMarcel Moolenaar if (on_marker) 7516545ddfbeSMarcel Moolenaar goto marker_prevents_close; 7517545ddfbeSMarcel Moolenaar rc = xo_do_close_leaf_list(xop, NULL); 7518545ddfbeSMarcel Moolenaar break; 7519545ddfbeSMarcel Moolenaar 7520545ddfbeSMarcel Moolenaar /*emit_leaf_list:*/ 7521545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_INIT, XSS_EMIT_LEAF_LIST): 7522545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_EMIT_LEAF_LIST): 7523545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_EMIT_LEAF_LIST): 7524545ddfbeSMarcel Moolenaar rc = xo_do_open_leaf_list(xop, flags, name); 7525545ddfbeSMarcel Moolenaar break; 7526545ddfbeSMarcel Moolenaar 7527545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_EMIT_LEAF_LIST): 7528545ddfbeSMarcel Moolenaar break; 7529545ddfbeSMarcel Moolenaar 7530545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LIST, XSS_EMIT_LEAF_LIST): 7531545ddfbeSMarcel Moolenaar /* 7532545ddfbeSMarcel Moolenaar * We need to be backward compatible with the pre-xo_open_leaf_list 7533545ddfbeSMarcel Moolenaar * API, where both lists and leaf-lists were opened as lists. So 7534545ddfbeSMarcel Moolenaar * if we find an open list that hasn't had anything written to it, 7535545ddfbeSMarcel Moolenaar * we'll accept it. 7536545ddfbeSMarcel Moolenaar */ 7537545ddfbeSMarcel Moolenaar break; 7538545ddfbeSMarcel Moolenaar 7539545ddfbeSMarcel Moolenaar default: 7540545ddfbeSMarcel Moolenaar xo_failure(xop, "unknown transition: (%u -> %u)", 7541545ddfbeSMarcel Moolenaar xsp->xs_state, new_state); 7542545ddfbeSMarcel Moolenaar } 7543545ddfbeSMarcel Moolenaar 754442ff34c3SPhil Shafer /* Handle the flush flag */ 754542ff34c3SPhil Shafer if (rc >= 0 && XOF_ISSET(xop, XOF_FLUSH)) 754642ff34c3SPhil Shafer if (xo_flush_h(xop)) 754742ff34c3SPhil Shafer rc = -1; 754842ff34c3SPhil Shafer 7549545ddfbeSMarcel Moolenaar return rc; 7550545ddfbeSMarcel Moolenaar 7551545ddfbeSMarcel Moolenaar marker_prevents_close: 7552545ddfbeSMarcel Moolenaar xo_failure(xop, "marker '%s' prevents transition from %s to %s", 7553545ddfbeSMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_name, 7554545ddfbeSMarcel Moolenaar xo_state_name(old_state), xo_state_name(new_state)); 7555545ddfbeSMarcel Moolenaar return -1; 7556545ddfbeSMarcel Moolenaar } 7557545ddfbeSMarcel Moolenaar 75588a6eceffSPhil Shafer xo_ssize_t 7559545ddfbeSMarcel Moolenaar xo_open_marker_h (xo_handle_t *xop, const char *name) 7560545ddfbeSMarcel Moolenaar { 7561545ddfbeSMarcel Moolenaar xop = xo_default(xop); 7562545ddfbeSMarcel Moolenaar 7563545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, 1, 0, XSS_MARKER, 7564545ddfbeSMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags & XSF_MARKER_FLAGS); 7565545ddfbeSMarcel Moolenaar 7566545ddfbeSMarcel Moolenaar return 0; 7567545ddfbeSMarcel Moolenaar } 7568545ddfbeSMarcel Moolenaar 75698a6eceffSPhil Shafer xo_ssize_t 7570545ddfbeSMarcel Moolenaar xo_open_marker (const char *name) 7571545ddfbeSMarcel Moolenaar { 7572545ddfbeSMarcel Moolenaar return xo_open_marker_h(NULL, name); 7573545ddfbeSMarcel Moolenaar } 7574545ddfbeSMarcel Moolenaar 75758a6eceffSPhil Shafer xo_ssize_t 7576545ddfbeSMarcel Moolenaar xo_close_marker_h (xo_handle_t *xop, const char *name) 7577545ddfbeSMarcel Moolenaar { 7578545ddfbeSMarcel Moolenaar xop = xo_default(xop); 7579545ddfbeSMarcel Moolenaar 7580545ddfbeSMarcel Moolenaar return xo_do_close(xop, name, XSS_MARKER); 7581545ddfbeSMarcel Moolenaar } 7582545ddfbeSMarcel Moolenaar 75838a6eceffSPhil Shafer xo_ssize_t 7584545ddfbeSMarcel Moolenaar xo_close_marker (const char *name) 7585545ddfbeSMarcel Moolenaar { 7586545ddfbeSMarcel Moolenaar return xo_close_marker_h(NULL, name); 7587545ddfbeSMarcel Moolenaar } 7588545ddfbeSMarcel Moolenaar 7589d1a0d267SMarcel Moolenaar /* 7590d1a0d267SMarcel Moolenaar * Record custom output functions into the xo handle, allowing 7591d1a0d267SMarcel Moolenaar * integration with a variety of output frameworks. 7592d1a0d267SMarcel Moolenaar */ 759331337658SMarcel Moolenaar void 759431337658SMarcel Moolenaar xo_set_writer (xo_handle_t *xop, void *opaque, xo_write_func_t write_func, 7595545ddfbeSMarcel Moolenaar xo_close_func_t close_func, xo_flush_func_t flush_func) 759631337658SMarcel Moolenaar { 759731337658SMarcel Moolenaar xop = xo_default(xop); 759831337658SMarcel Moolenaar 759931337658SMarcel Moolenaar xop->xo_opaque = opaque; 760031337658SMarcel Moolenaar xop->xo_write = write_func; 760131337658SMarcel Moolenaar xop->xo_close = close_func; 7602545ddfbeSMarcel Moolenaar xop->xo_flush = flush_func; 760331337658SMarcel Moolenaar } 760431337658SMarcel Moolenaar 760531337658SMarcel Moolenaar void 760631337658SMarcel Moolenaar xo_set_allocator (xo_realloc_func_t realloc_func, xo_free_func_t free_func) 760731337658SMarcel Moolenaar { 760831337658SMarcel Moolenaar xo_realloc = realloc_func; 760931337658SMarcel Moolenaar xo_free = free_func; 761031337658SMarcel Moolenaar } 761131337658SMarcel Moolenaar 76128a6eceffSPhil Shafer xo_ssize_t 761331337658SMarcel Moolenaar xo_flush_h (xo_handle_t *xop) 761431337658SMarcel Moolenaar { 76158a6eceffSPhil Shafer ssize_t rc; 761631337658SMarcel Moolenaar 761731337658SMarcel Moolenaar xop = xo_default(xop); 761831337658SMarcel Moolenaar 7619788ca347SMarcel Moolenaar switch (xo_style(xop)) { 7620d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER: 7621d1a0d267SMarcel Moolenaar xo_encoder_handle(xop, XO_OP_FLUSH, NULL, NULL); 762231337658SMarcel Moolenaar } 762331337658SMarcel Moolenaar 7624545ddfbeSMarcel Moolenaar rc = xo_write(xop); 7625545ddfbeSMarcel Moolenaar if (rc >= 0 && xop->xo_flush) 7626545ddfbeSMarcel Moolenaar if (xop->xo_flush(xop->xo_opaque) < 0) 7627545ddfbeSMarcel Moolenaar return -1; 7628545ddfbeSMarcel Moolenaar 7629545ddfbeSMarcel Moolenaar return rc; 763031337658SMarcel Moolenaar } 763131337658SMarcel Moolenaar 76328a6eceffSPhil Shafer xo_ssize_t 763331337658SMarcel Moolenaar xo_flush (void) 763431337658SMarcel Moolenaar { 7635545ddfbeSMarcel Moolenaar return xo_flush_h(NULL); 763631337658SMarcel Moolenaar } 763731337658SMarcel Moolenaar 76388a6eceffSPhil Shafer xo_ssize_t 763931337658SMarcel Moolenaar xo_finish_h (xo_handle_t *xop) 764031337658SMarcel Moolenaar { 764131337658SMarcel Moolenaar const char *cp = ""; 764231337658SMarcel Moolenaar xop = xo_default(xop); 764331337658SMarcel Moolenaar 7644d1a0d267SMarcel Moolenaar if (!XOF_ISSET(xop, XOF_NO_CLOSE)) 7645545ddfbeSMarcel Moolenaar xo_do_close_all(xop, xop->xo_stack); 7646545ddfbeSMarcel Moolenaar 7647788ca347SMarcel Moolenaar switch (xo_style(xop)) { 764831337658SMarcel Moolenaar case XO_STYLE_JSON: 7649d1a0d267SMarcel Moolenaar if (!XOF_ISSET(xop, XOF_NO_TOP)) { 7650d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_TOP_EMITTED)) 7651d1a0d267SMarcel Moolenaar XOIF_CLEAR(xop, XOIF_TOP_EMITTED); /* Turn off before output */ 765231337658SMarcel Moolenaar else 765331337658SMarcel Moolenaar cp = "{ "; 765431337658SMarcel Moolenaar xo_printf(xop, "%*s%s}\n",xo_indent(xop), "", cp); 765531337658SMarcel Moolenaar } 765631337658SMarcel Moolenaar break; 7657d1a0d267SMarcel Moolenaar 7658d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER: 7659d1a0d267SMarcel Moolenaar xo_encoder_handle(xop, XO_OP_FINISH, NULL, NULL); 7660d1a0d267SMarcel Moolenaar break; 766131337658SMarcel Moolenaar } 766231337658SMarcel Moolenaar 7663545ddfbeSMarcel Moolenaar return xo_flush_h(xop); 766431337658SMarcel Moolenaar } 766531337658SMarcel Moolenaar 76668a6eceffSPhil Shafer xo_ssize_t 766731337658SMarcel Moolenaar xo_finish (void) 766831337658SMarcel Moolenaar { 7669545ddfbeSMarcel Moolenaar return xo_finish_h(NULL); 767031337658SMarcel Moolenaar } 767131337658SMarcel Moolenaar 767231337658SMarcel Moolenaar /* 7673d1a0d267SMarcel Moolenaar * xo_finish_atexit is suitable for atexit() calls, to force clear up 7674d1a0d267SMarcel Moolenaar * and finalizing output. 7675d1a0d267SMarcel Moolenaar */ 7676d1a0d267SMarcel Moolenaar void 7677d1a0d267SMarcel Moolenaar xo_finish_atexit (void) 7678d1a0d267SMarcel Moolenaar { 7679d1a0d267SMarcel Moolenaar (void) xo_finish_h(NULL); 7680d1a0d267SMarcel Moolenaar } 7681d1a0d267SMarcel Moolenaar 7682d1a0d267SMarcel Moolenaar /* 768331337658SMarcel Moolenaar * Generate an error message, such as would be displayed on stderr 768431337658SMarcel Moolenaar */ 768531337658SMarcel Moolenaar void 768631337658SMarcel Moolenaar xo_error_hv (xo_handle_t *xop, const char *fmt, va_list vap) 768731337658SMarcel Moolenaar { 768831337658SMarcel Moolenaar xop = xo_default(xop); 768931337658SMarcel Moolenaar 769031337658SMarcel Moolenaar /* 769131337658SMarcel Moolenaar * If the format string doesn't end with a newline, we pop 769231337658SMarcel Moolenaar * one on ourselves. 769331337658SMarcel Moolenaar */ 76948a6eceffSPhil Shafer ssize_t len = strlen(fmt); 769531337658SMarcel Moolenaar if (len > 0 && fmt[len - 1] != '\n') { 769631337658SMarcel Moolenaar char *newfmt = alloca(len + 2); 769731337658SMarcel Moolenaar memcpy(newfmt, fmt, len); 769831337658SMarcel Moolenaar newfmt[len] = '\n'; 769931337658SMarcel Moolenaar newfmt[len] = '\0'; 770031337658SMarcel Moolenaar fmt = newfmt; 770131337658SMarcel Moolenaar } 770231337658SMarcel Moolenaar 7703788ca347SMarcel Moolenaar switch (xo_style(xop)) { 770431337658SMarcel Moolenaar case XO_STYLE_TEXT: 770531337658SMarcel Moolenaar vfprintf(stderr, fmt, vap); 770631337658SMarcel Moolenaar break; 770731337658SMarcel Moolenaar 770831337658SMarcel Moolenaar case XO_STYLE_HTML: 770931337658SMarcel Moolenaar va_copy(xop->xo_vap, vap); 771031337658SMarcel Moolenaar 771131337658SMarcel Moolenaar xo_buf_append_div(xop, "error", 0, NULL, 0, fmt, strlen(fmt), NULL, 0); 771231337658SMarcel Moolenaar 7713d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_DIV_OPEN)) 771431337658SMarcel Moolenaar xo_line_close(xop); 771531337658SMarcel Moolenaar 771631337658SMarcel Moolenaar xo_write(xop); 771731337658SMarcel Moolenaar 771831337658SMarcel Moolenaar va_end(xop->xo_vap); 771931337658SMarcel Moolenaar bzero(&xop->xo_vap, sizeof(xop->xo_vap)); 772031337658SMarcel Moolenaar break; 772131337658SMarcel Moolenaar 772231337658SMarcel Moolenaar case XO_STYLE_XML: 7723545ddfbeSMarcel Moolenaar case XO_STYLE_JSON: 772431337658SMarcel Moolenaar va_copy(xop->xo_vap, vap); 772531337658SMarcel Moolenaar 772631337658SMarcel Moolenaar xo_open_container_h(xop, "error"); 772731337658SMarcel Moolenaar xo_format_value(xop, "message", 7, fmt, strlen(fmt), NULL, 0, 0); 772831337658SMarcel Moolenaar xo_close_container_h(xop, "error"); 772931337658SMarcel Moolenaar 773031337658SMarcel Moolenaar va_end(xop->xo_vap); 773131337658SMarcel Moolenaar bzero(&xop->xo_vap, sizeof(xop->xo_vap)); 773231337658SMarcel Moolenaar break; 7733d1a0d267SMarcel Moolenaar 7734d1a0d267SMarcel Moolenaar case XO_STYLE_SDPARAMS: 7735d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER: 7736d1a0d267SMarcel Moolenaar break; 773731337658SMarcel Moolenaar } 773831337658SMarcel Moolenaar } 773931337658SMarcel Moolenaar 774031337658SMarcel Moolenaar void 774131337658SMarcel Moolenaar xo_error_h (xo_handle_t *xop, const char *fmt, ...) 774231337658SMarcel Moolenaar { 774331337658SMarcel Moolenaar va_list vap; 774431337658SMarcel Moolenaar 774531337658SMarcel Moolenaar va_start(vap, fmt); 774631337658SMarcel Moolenaar xo_error_hv(xop, fmt, vap); 774731337658SMarcel Moolenaar va_end(vap); 774831337658SMarcel Moolenaar } 774931337658SMarcel Moolenaar 775031337658SMarcel Moolenaar /* 775131337658SMarcel Moolenaar * Generate an error message, such as would be displayed on stderr 775231337658SMarcel Moolenaar */ 775331337658SMarcel Moolenaar void 775431337658SMarcel Moolenaar xo_error (const char *fmt, ...) 775531337658SMarcel Moolenaar { 775631337658SMarcel Moolenaar va_list vap; 775731337658SMarcel Moolenaar 775831337658SMarcel Moolenaar va_start(vap, fmt); 775931337658SMarcel Moolenaar xo_error_hv(NULL, fmt, vap); 776031337658SMarcel Moolenaar va_end(vap); 776131337658SMarcel Moolenaar } 776231337658SMarcel Moolenaar 7763d1a0d267SMarcel Moolenaar /* 7764d1a0d267SMarcel Moolenaar * Parse any libxo-specific options from the command line, removing them 7765d1a0d267SMarcel Moolenaar * so the main() argument parsing won't see them. We return the new value 7766d1a0d267SMarcel Moolenaar * for argc or -1 for error. If an error occurred, the program should 7767d1a0d267SMarcel Moolenaar * exit. A suitable error message has already been displayed. 7768d1a0d267SMarcel Moolenaar */ 776931337658SMarcel Moolenaar int 777031337658SMarcel Moolenaar xo_parse_args (int argc, char **argv) 777131337658SMarcel Moolenaar { 777231337658SMarcel Moolenaar static char libxo_opt[] = "--libxo"; 777331337658SMarcel Moolenaar char *cp; 777431337658SMarcel Moolenaar int i, save; 777531337658SMarcel Moolenaar 777631337658SMarcel Moolenaar /* Save our program name for xo_err and friends */ 777731337658SMarcel Moolenaar xo_program = argv[0]; 777831337658SMarcel Moolenaar cp = strrchr(xo_program, '/'); 777931337658SMarcel Moolenaar if (cp) 778031337658SMarcel Moolenaar xo_program = cp + 1; 778131337658SMarcel Moolenaar 778231337658SMarcel Moolenaar for (save = i = 1; i < argc; i++) { 778331337658SMarcel Moolenaar if (argv[i] == NULL 778431337658SMarcel Moolenaar || strncmp(argv[i], libxo_opt, sizeof(libxo_opt) - 1) != 0) { 778531337658SMarcel Moolenaar if (save != i) 778631337658SMarcel Moolenaar argv[save] = argv[i]; 778731337658SMarcel Moolenaar save += 1; 778831337658SMarcel Moolenaar continue; 778931337658SMarcel Moolenaar } 779031337658SMarcel Moolenaar 779131337658SMarcel Moolenaar cp = argv[i] + sizeof(libxo_opt) - 1; 7792ee5cf116SPhil Shafer if (*cp == '\0') { 779331337658SMarcel Moolenaar cp = argv[++i]; 7794ee5cf116SPhil Shafer if (cp == NULL) { 779531337658SMarcel Moolenaar xo_warnx("missing libxo option"); 779631337658SMarcel Moolenaar return -1; 779731337658SMarcel Moolenaar } 779831337658SMarcel Moolenaar 779931337658SMarcel Moolenaar if (xo_set_options(NULL, cp) < 0) 780031337658SMarcel Moolenaar return -1; 780131337658SMarcel Moolenaar } else if (*cp == ':') { 780231337658SMarcel Moolenaar if (xo_set_options(NULL, cp) < 0) 780331337658SMarcel Moolenaar return -1; 780431337658SMarcel Moolenaar 780531337658SMarcel Moolenaar } else if (*cp == '=') { 780631337658SMarcel Moolenaar if (xo_set_options(NULL, ++cp) < 0) 780731337658SMarcel Moolenaar return -1; 780831337658SMarcel Moolenaar 780931337658SMarcel Moolenaar } else if (*cp == '-') { 781031337658SMarcel Moolenaar cp += 1; 781131337658SMarcel Moolenaar if (strcmp(cp, "check") == 0) { 781231337658SMarcel Moolenaar exit(XO_HAS_LIBXO); 781331337658SMarcel Moolenaar 781431337658SMarcel Moolenaar } else { 781531337658SMarcel Moolenaar xo_warnx("unknown libxo option: '%s'", argv[i]); 781631337658SMarcel Moolenaar return -1; 781731337658SMarcel Moolenaar } 781831337658SMarcel Moolenaar } else { 781931337658SMarcel Moolenaar xo_warnx("unknown libxo option: '%s'", argv[i]); 782031337658SMarcel Moolenaar return -1; 782131337658SMarcel Moolenaar } 782231337658SMarcel Moolenaar } 782331337658SMarcel Moolenaar 782431337658SMarcel Moolenaar argv[save] = NULL; 782531337658SMarcel Moolenaar return save; 782631337658SMarcel Moolenaar } 782731337658SMarcel Moolenaar 7828d1a0d267SMarcel Moolenaar /* 7829d1a0d267SMarcel Moolenaar * Debugging function that dumps the current stack of open libxo constructs, 7830d1a0d267SMarcel Moolenaar * suitable for calling from the debugger. 7831d1a0d267SMarcel Moolenaar */ 7832545ddfbeSMarcel Moolenaar void 7833545ddfbeSMarcel Moolenaar xo_dump_stack (xo_handle_t *xop) 7834545ddfbeSMarcel Moolenaar { 7835545ddfbeSMarcel Moolenaar int i; 7836545ddfbeSMarcel Moolenaar xo_stack_t *xsp; 7837545ddfbeSMarcel Moolenaar 7838545ddfbeSMarcel Moolenaar xop = xo_default(xop); 7839545ddfbeSMarcel Moolenaar 7840545ddfbeSMarcel Moolenaar fprintf(stderr, "Stack dump:\n"); 7841545ddfbeSMarcel Moolenaar 7842545ddfbeSMarcel Moolenaar xsp = xop->xo_stack; 7843545ddfbeSMarcel Moolenaar for (i = 1, xsp++; i <= xop->xo_depth; i++, xsp++) { 7844545ddfbeSMarcel Moolenaar fprintf(stderr, " [%d] %s '%s' [%x]\n", 7845545ddfbeSMarcel Moolenaar i, xo_state_name(xsp->xs_state), 7846545ddfbeSMarcel Moolenaar xsp->xs_name ?: "--", xsp->xs_flags); 7847545ddfbeSMarcel Moolenaar } 7848545ddfbeSMarcel Moolenaar } 7849545ddfbeSMarcel Moolenaar 7850d1a0d267SMarcel Moolenaar /* 7851d1a0d267SMarcel Moolenaar * Record the program name used for error messages 7852d1a0d267SMarcel Moolenaar */ 7853545ddfbeSMarcel Moolenaar void 7854545ddfbeSMarcel Moolenaar xo_set_program (const char *name) 7855545ddfbeSMarcel Moolenaar { 7856545ddfbeSMarcel Moolenaar xo_program = name; 7857545ddfbeSMarcel Moolenaar } 7858545ddfbeSMarcel Moolenaar 7859788ca347SMarcel Moolenaar void 786042ff34c3SPhil Shafer xo_set_version_h (xo_handle_t *xop, const char *version) 7861788ca347SMarcel Moolenaar { 7862788ca347SMarcel Moolenaar xop = xo_default(xop); 7863788ca347SMarcel Moolenaar 7864788ca347SMarcel Moolenaar if (version == NULL || strchr(version, '"') != NULL) 7865788ca347SMarcel Moolenaar return; 7866788ca347SMarcel Moolenaar 7867d1a0d267SMarcel Moolenaar if (!xo_style_is_encoding(xop)) 7868d1a0d267SMarcel Moolenaar return; 7869d1a0d267SMarcel Moolenaar 7870788ca347SMarcel Moolenaar switch (xo_style(xop)) { 7871788ca347SMarcel Moolenaar case XO_STYLE_XML: 7872788ca347SMarcel Moolenaar /* For XML, we record this as an attribute for the first tag */ 78738a6eceffSPhil Shafer xo_attr_h(xop, "version", "%s", version); 7874788ca347SMarcel Moolenaar break; 7875788ca347SMarcel Moolenaar 7876788ca347SMarcel Moolenaar case XO_STYLE_JSON: 7877788ca347SMarcel Moolenaar /* 7878d1a0d267SMarcel Moolenaar * For JSON, we record the version string in our handle, and emit 7879788ca347SMarcel Moolenaar * it in xo_emit_top. 7880788ca347SMarcel Moolenaar */ 7881d1a0d267SMarcel Moolenaar xop->xo_version = xo_strndup(version, -1); 7882d1a0d267SMarcel Moolenaar break; 7883d1a0d267SMarcel Moolenaar 7884d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER: 7885d1a0d267SMarcel Moolenaar xo_encoder_handle(xop, XO_OP_VERSION, NULL, version); 7886788ca347SMarcel Moolenaar break; 7887788ca347SMarcel Moolenaar } 7888788ca347SMarcel Moolenaar } 7889788ca347SMarcel Moolenaar 7890d1a0d267SMarcel Moolenaar /* 7891ee5cf116SPhil Shafer * Set the version number for the API content being carried through 7892d1a0d267SMarcel Moolenaar * the xo handle. 7893d1a0d267SMarcel Moolenaar */ 7894788ca347SMarcel Moolenaar void 7895788ca347SMarcel Moolenaar xo_set_version (const char *version) 7896788ca347SMarcel Moolenaar { 7897788ca347SMarcel Moolenaar xo_set_version_h(NULL, version); 7898788ca347SMarcel Moolenaar } 7899788ca347SMarcel Moolenaar 7900d1a0d267SMarcel Moolenaar /* 7901d1a0d267SMarcel Moolenaar * Generate a warning. Normally, this is a text message written to 7902d1a0d267SMarcel Moolenaar * standard error. If the XOF_WARN_XML flag is set, then we generate 7903d1a0d267SMarcel Moolenaar * XMLified content on standard output. 7904d1a0d267SMarcel Moolenaar */ 7905d1a0d267SMarcel Moolenaar void 7906d1a0d267SMarcel Moolenaar xo_emit_warn_hcv (xo_handle_t *xop, int as_warning, int code, 7907d1a0d267SMarcel Moolenaar const char *fmt, va_list vap) 790831337658SMarcel Moolenaar { 7909d1a0d267SMarcel Moolenaar xop = xo_default(xop); 791031337658SMarcel Moolenaar 7911d1a0d267SMarcel Moolenaar if (fmt == NULL) 7912d1a0d267SMarcel Moolenaar return; 791331337658SMarcel Moolenaar 7914d1a0d267SMarcel Moolenaar xo_open_marker_h(xop, "xo_emit_warn_hcv"); 7915d1a0d267SMarcel Moolenaar xo_open_container_h(xop, as_warning ? "__warning" : "__error"); 791631337658SMarcel Moolenaar 7917d1a0d267SMarcel Moolenaar if (xo_program) 7918d1a0d267SMarcel Moolenaar xo_emit("{wc:program}", xo_program); 791931337658SMarcel Moolenaar 7920d1a0d267SMarcel Moolenaar if (xo_style(xop) == XO_STYLE_XML || xo_style(xop) == XO_STYLE_JSON) { 7921d1a0d267SMarcel Moolenaar va_list ap; 7922d1a0d267SMarcel Moolenaar xo_handle_t temp; 792331337658SMarcel Moolenaar 7924d1a0d267SMarcel Moolenaar bzero(&temp, sizeof(temp)); 7925d1a0d267SMarcel Moolenaar temp.xo_style = XO_STYLE_TEXT; 7926d1a0d267SMarcel Moolenaar xo_buf_init(&temp.xo_data); 7927d1a0d267SMarcel Moolenaar xo_depth_check(&temp, XO_DEPTH); 792831337658SMarcel Moolenaar 7929d1a0d267SMarcel Moolenaar va_copy(ap, vap); 7930d1a0d267SMarcel Moolenaar (void) xo_emit_hv(&temp, fmt, ap); 7931d1a0d267SMarcel Moolenaar va_end(ap); 793231337658SMarcel Moolenaar 7933d1a0d267SMarcel Moolenaar xo_buffer_t *src = &temp.xo_data; 7934d1a0d267SMarcel Moolenaar xo_format_value(xop, "message", 7, src->xb_bufp, 7935d1a0d267SMarcel Moolenaar src->xb_curp - src->xb_bufp, NULL, 0, 0); 793631337658SMarcel Moolenaar 7937d1a0d267SMarcel Moolenaar xo_free(temp.xo_stack); 7938d1a0d267SMarcel Moolenaar xo_buf_cleanup(src); 793931337658SMarcel Moolenaar } 794031337658SMarcel Moolenaar 7941d1a0d267SMarcel Moolenaar (void) xo_emit_hv(xop, fmt, vap); 794231337658SMarcel Moolenaar 79438a6eceffSPhil Shafer ssize_t len = strlen(fmt); 7944d1a0d267SMarcel Moolenaar if (len > 0 && fmt[len - 1] != '\n') { 7945d1a0d267SMarcel Moolenaar if (code > 0) { 7946d1a0d267SMarcel Moolenaar const char *msg = strerror(code); 7947d1a0d267SMarcel Moolenaar if (msg) 7948d1a0d267SMarcel Moolenaar xo_emit_h(xop, ": {G:strerror}{g:error/%s}", msg); 7949d1a0d267SMarcel Moolenaar } 7950d1a0d267SMarcel Moolenaar xo_emit("\n"); 795131337658SMarcel Moolenaar } 795231337658SMarcel Moolenaar 7953d1a0d267SMarcel Moolenaar xo_close_marker_h(xop, "xo_emit_warn_hcv"); 7954d1a0d267SMarcel Moolenaar xo_flush_h(xop); 795531337658SMarcel Moolenaar } 795631337658SMarcel Moolenaar 7957d1a0d267SMarcel Moolenaar void 7958d1a0d267SMarcel Moolenaar xo_emit_warn_hc (xo_handle_t *xop, int code, const char *fmt, ...) 7959d1a0d267SMarcel Moolenaar { 7960d1a0d267SMarcel Moolenaar va_list vap; 796131337658SMarcel Moolenaar 7962d1a0d267SMarcel Moolenaar va_start(vap, fmt); 7963d1a0d267SMarcel Moolenaar xo_emit_warn_hcv(xop, 1, code, fmt, vap); 7964d1a0d267SMarcel Moolenaar va_end(vap); 796531337658SMarcel Moolenaar } 796631337658SMarcel Moolenaar 7967d1a0d267SMarcel Moolenaar void 7968d1a0d267SMarcel Moolenaar xo_emit_warn_c (int code, const char *fmt, ...) 7969d1a0d267SMarcel Moolenaar { 7970d1a0d267SMarcel Moolenaar va_list vap; 797131337658SMarcel Moolenaar 7972d1a0d267SMarcel Moolenaar va_start(vap, fmt); 7973d1a0d267SMarcel Moolenaar xo_emit_warn_hcv(NULL, 1, code, fmt, vap); 7974d1a0d267SMarcel Moolenaar va_end(vap); 7975d1a0d267SMarcel Moolenaar } 797631337658SMarcel Moolenaar 7977d1a0d267SMarcel Moolenaar void 7978d1a0d267SMarcel Moolenaar xo_emit_warn (const char *fmt, ...) 7979d1a0d267SMarcel Moolenaar { 7980d1a0d267SMarcel Moolenaar int code = errno; 7981d1a0d267SMarcel Moolenaar va_list vap; 7982d1a0d267SMarcel Moolenaar 7983d1a0d267SMarcel Moolenaar va_start(vap, fmt); 7984d1a0d267SMarcel Moolenaar xo_emit_warn_hcv(NULL, 1, code, fmt, vap); 7985d1a0d267SMarcel Moolenaar va_end(vap); 7986d1a0d267SMarcel Moolenaar } 7987d1a0d267SMarcel Moolenaar 7988d1a0d267SMarcel Moolenaar void 7989d1a0d267SMarcel Moolenaar xo_emit_warnx (const char *fmt, ...) 7990d1a0d267SMarcel Moolenaar { 7991d1a0d267SMarcel Moolenaar va_list vap; 7992d1a0d267SMarcel Moolenaar 7993d1a0d267SMarcel Moolenaar va_start(vap, fmt); 7994d1a0d267SMarcel Moolenaar xo_emit_warn_hcv(NULL, 1, -1, fmt, vap); 7995d1a0d267SMarcel Moolenaar va_end(vap); 7996d1a0d267SMarcel Moolenaar } 7997d1a0d267SMarcel Moolenaar 7998d1a0d267SMarcel Moolenaar void 7999d1a0d267SMarcel Moolenaar xo_emit_err_v (int eval, int code, const char *fmt, va_list vap) 8000d1a0d267SMarcel Moolenaar { 8001d1a0d267SMarcel Moolenaar xo_emit_warn_hcv(NULL, 0, code, fmt, vap); 800231337658SMarcel Moolenaar xo_finish(); 8003d1a0d267SMarcel Moolenaar exit(eval); 800431337658SMarcel Moolenaar } 8005d1a0d267SMarcel Moolenaar 8006d1a0d267SMarcel Moolenaar void 8007d1a0d267SMarcel Moolenaar xo_emit_err (int eval, const char *fmt, ...) 8008d1a0d267SMarcel Moolenaar { 8009d1a0d267SMarcel Moolenaar int code = errno; 8010d1a0d267SMarcel Moolenaar va_list vap; 8011d1a0d267SMarcel Moolenaar va_start(vap, fmt); 8012d1a0d267SMarcel Moolenaar xo_emit_err_v(0, code, fmt, vap); 8013d1a0d267SMarcel Moolenaar va_end(vap); 8014d1a0d267SMarcel Moolenaar exit(eval); 8015d1a0d267SMarcel Moolenaar } 8016d1a0d267SMarcel Moolenaar 8017d1a0d267SMarcel Moolenaar void 8018d1a0d267SMarcel Moolenaar xo_emit_errx (int eval, const char *fmt, ...) 8019d1a0d267SMarcel Moolenaar { 8020d1a0d267SMarcel Moolenaar va_list vap; 8021d1a0d267SMarcel Moolenaar 8022d1a0d267SMarcel Moolenaar va_start(vap, fmt); 8023d1a0d267SMarcel Moolenaar xo_emit_err_v(0, -1, fmt, vap); 8024d1a0d267SMarcel Moolenaar va_end(vap); 8025d1a0d267SMarcel Moolenaar xo_finish(); 8026d1a0d267SMarcel Moolenaar exit(eval); 8027d1a0d267SMarcel Moolenaar } 8028d1a0d267SMarcel Moolenaar 8029d1a0d267SMarcel Moolenaar void 8030d1a0d267SMarcel Moolenaar xo_emit_errc (int eval, int code, const char *fmt, ...) 8031d1a0d267SMarcel Moolenaar { 8032d1a0d267SMarcel Moolenaar va_list vap; 8033d1a0d267SMarcel Moolenaar 8034d1a0d267SMarcel Moolenaar va_start(vap, fmt); 8035d1a0d267SMarcel Moolenaar xo_emit_warn_hcv(NULL, 0, code, fmt, vap); 8036d1a0d267SMarcel Moolenaar va_end(vap); 8037d1a0d267SMarcel Moolenaar xo_finish(); 8038d1a0d267SMarcel Moolenaar exit(eval); 8039d1a0d267SMarcel Moolenaar } 8040d1a0d267SMarcel Moolenaar 8041d1a0d267SMarcel Moolenaar /* 8042d1a0d267SMarcel Moolenaar * Get the opaque private pointer for an xo handle 8043d1a0d267SMarcel Moolenaar */ 8044d1a0d267SMarcel Moolenaar void * 8045d1a0d267SMarcel Moolenaar xo_get_private (xo_handle_t *xop) 8046d1a0d267SMarcel Moolenaar { 8047d1a0d267SMarcel Moolenaar xop = xo_default(xop); 8048d1a0d267SMarcel Moolenaar return xop->xo_private; 8049d1a0d267SMarcel Moolenaar } 8050d1a0d267SMarcel Moolenaar 8051d1a0d267SMarcel Moolenaar /* 8052d1a0d267SMarcel Moolenaar * Set the opaque private pointer for an xo handle. 8053d1a0d267SMarcel Moolenaar */ 8054d1a0d267SMarcel Moolenaar void 8055d1a0d267SMarcel Moolenaar xo_set_private (xo_handle_t *xop, void *opaque) 8056d1a0d267SMarcel Moolenaar { 8057d1a0d267SMarcel Moolenaar xop = xo_default(xop); 8058d1a0d267SMarcel Moolenaar xop->xo_private = opaque; 8059d1a0d267SMarcel Moolenaar } 8060d1a0d267SMarcel Moolenaar 8061d1a0d267SMarcel Moolenaar /* 8062d1a0d267SMarcel Moolenaar * Get the encoder function 8063d1a0d267SMarcel Moolenaar */ 8064d1a0d267SMarcel Moolenaar xo_encoder_func_t 8065d1a0d267SMarcel Moolenaar xo_get_encoder (xo_handle_t *xop) 8066d1a0d267SMarcel Moolenaar { 8067d1a0d267SMarcel Moolenaar xop = xo_default(xop); 8068d1a0d267SMarcel Moolenaar return xop->xo_encoder; 8069d1a0d267SMarcel Moolenaar } 8070d1a0d267SMarcel Moolenaar 8071d1a0d267SMarcel Moolenaar /* 8072d1a0d267SMarcel Moolenaar * Record an encoder callback function in an xo handle. 8073d1a0d267SMarcel Moolenaar */ 8074d1a0d267SMarcel Moolenaar void 8075d1a0d267SMarcel Moolenaar xo_set_encoder (xo_handle_t *xop, xo_encoder_func_t encoder) 8076d1a0d267SMarcel Moolenaar { 8077d1a0d267SMarcel Moolenaar xop = xo_default(xop); 8078d1a0d267SMarcel Moolenaar 8079d1a0d267SMarcel Moolenaar xop->xo_style = XO_STYLE_ENCODER; 8080d1a0d267SMarcel Moolenaar xop->xo_encoder = encoder; 8081d1a0d267SMarcel Moolenaar } 8082