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: 22d1a0d267SMarcel Moolenaar * - xo_do_emit() -- the central function of the library 23d1a0d267SMarcel Moolenaar * - xo_do_format_field() -- handles formatting a single field 24d1a0d267SMarcel Moolenaar * - xo_transiton() -- the state machine that keeps things sane 25d1a0d267SMarcel Moolenaar * and of course the "xo_handle_t" data structure, which carries all 26d1a0d267SMarcel Moolenaar * configuration and state. 2731337658SMarcel Moolenaar */ 2831337658SMarcel Moolenaar 2931337658SMarcel Moolenaar #include <stdio.h> 3031337658SMarcel Moolenaar #include <stdlib.h> 3131337658SMarcel Moolenaar #include <stdint.h> 32545ddfbeSMarcel Moolenaar #include <unistd.h> 3331337658SMarcel Moolenaar #include <stddef.h> 3431337658SMarcel Moolenaar #include <wchar.h> 3531337658SMarcel Moolenaar #include <locale.h> 3631337658SMarcel Moolenaar #include <sys/types.h> 3731337658SMarcel Moolenaar #include <stdarg.h> 3831337658SMarcel Moolenaar #include <string.h> 3931337658SMarcel Moolenaar #include <errno.h> 4031337658SMarcel Moolenaar #include <limits.h> 4131337658SMarcel Moolenaar #include <ctype.h> 4231337658SMarcel Moolenaar #include <wctype.h> 4331337658SMarcel Moolenaar #include <getopt.h> 4431337658SMarcel Moolenaar 45d1a0d267SMarcel Moolenaar #include "xo_config.h" 4631337658SMarcel Moolenaar #include "xo.h" 47d1a0d267SMarcel Moolenaar #include "xo_encoder.h" 48d1a0d267SMarcel Moolenaar #include "xo_buf.h" 49d1a0d267SMarcel Moolenaar 50d1a0d267SMarcel Moolenaar /* 51d1a0d267SMarcel Moolenaar * We ask wcwidth() to do an impossible job, really. It's supposed to 52d1a0d267SMarcel Moolenaar * need to tell us the number of columns consumed to display a unicode 53d1a0d267SMarcel Moolenaar * character. It returns that number without any sort of context, but 54d1a0d267SMarcel Moolenaar * we know they are characters whose glyph differs based on placement 55d1a0d267SMarcel Moolenaar * (end of word, middle of word, etc) and many that affect characters 56d1a0d267SMarcel Moolenaar * previously emitted. Without content, it can't hope to tell us. 57d1a0d267SMarcel Moolenaar * But it's the only standard tool we've got, so we use it. We would 58d1a0d267SMarcel Moolenaar * use wcswidth() but it typically just loops thru adding the results 59d1a0d267SMarcel Moolenaar * of wcwidth() calls in an entirely unhelpful way. 60d1a0d267SMarcel Moolenaar * 61d1a0d267SMarcel Moolenaar * Even then, there are many poor implementations (macosx), so we have 62d1a0d267SMarcel Moolenaar * to carry our own. We could have configure.ac test this (with 63d1a0d267SMarcel Moolenaar * something like 'assert(wcwidth(0x200d) == 0)'), but it would have 64d1a0d267SMarcel Moolenaar * to run a binary, which breaks cross-compilation. Hmm... I could 65d1a0d267SMarcel Moolenaar * run this test at init time and make a warning for our dear user. 66d1a0d267SMarcel Moolenaar * 67d1a0d267SMarcel Moolenaar * Anyhow, it remains a best-effort sort of thing. And it's all made 68d1a0d267SMarcel Moolenaar * more hopeless because we assume the display code doing the rendering is 69d1a0d267SMarcel Moolenaar * playing by the same rules we are. If it display 0x200d as a square 70d1a0d267SMarcel Moolenaar * box or a funky question mark, the output will be hosed. 71d1a0d267SMarcel Moolenaar */ 72d1a0d267SMarcel Moolenaar #ifdef LIBXO_WCWIDTH 73d1a0d267SMarcel Moolenaar #include "xo_wcwidth.h" 74d1a0d267SMarcel Moolenaar #else /* LIBXO_WCWIDTH */ 75d1a0d267SMarcel Moolenaar #define xo_wcwidth(_x) wcwidth(_x) 76d1a0d267SMarcel Moolenaar #endif /* LIBXO_WCWIDTH */ 7731337658SMarcel Moolenaar 78545ddfbeSMarcel Moolenaar #ifdef HAVE_STDIO_EXT_H 79545ddfbeSMarcel Moolenaar #include <stdio_ext.h> 80545ddfbeSMarcel Moolenaar #endif /* HAVE_STDIO_EXT_H */ 81545ddfbeSMarcel Moolenaar 82d1a0d267SMarcel Moolenaar /* 83d1a0d267SMarcel Moolenaar * humanize_number is a great function, unless you don't have it. So 84d1a0d267SMarcel Moolenaar * we carry one in our pocket. 85d1a0d267SMarcel Moolenaar */ 86d1a0d267SMarcel Moolenaar #ifdef HAVE_HUMANIZE_NUMBER 87d1a0d267SMarcel Moolenaar #include <libutil.h> 88d1a0d267SMarcel Moolenaar #define xo_humanize_number humanize_number 89d1a0d267SMarcel Moolenaar #else /* HAVE_HUMANIZE_NUMBER */ 90d1a0d267SMarcel Moolenaar #include "xo_humanize.h" 91d1a0d267SMarcel Moolenaar #endif /* HAVE_HUMANIZE_NUMBER */ 92d1a0d267SMarcel Moolenaar 93d1a0d267SMarcel Moolenaar #ifdef HAVE_GETTEXT 94d1a0d267SMarcel Moolenaar #include <libintl.h> 95d1a0d267SMarcel Moolenaar #endif /* HAVE_GETTEXT */ 96d1a0d267SMarcel Moolenaar 97d1a0d267SMarcel Moolenaar /* 98d1a0d267SMarcel Moolenaar * Three styles of specifying thread-local variables are supported. 99d1a0d267SMarcel Moolenaar * configure.ac has the brains to run each possibility thru the 100d1a0d267SMarcel Moolenaar * compiler and see what works; we are left to define the THREAD_LOCAL 101d1a0d267SMarcel Moolenaar * macro to the right value. Most toolchains (clang, gcc) use 102d1a0d267SMarcel Moolenaar * "before", but some (borland) use "after" and I've heard of some 103d1a0d267SMarcel Moolenaar * (ms) that use __declspec. Any others out there? 104d1a0d267SMarcel Moolenaar */ 105d1a0d267SMarcel Moolenaar #define THREAD_LOCAL_before 1 106d1a0d267SMarcel Moolenaar #define THREAD_LOCAL_after 2 107d1a0d267SMarcel Moolenaar #define THREAD_LOCAL_declspec 3 108d1a0d267SMarcel Moolenaar 109d1a0d267SMarcel Moolenaar #ifndef HAVE_THREAD_LOCAL 110d1a0d267SMarcel Moolenaar #define THREAD_LOCAL(_x) _x 111d1a0d267SMarcel Moolenaar #elif HAVE_THREAD_LOCAL == THREAD_LOCAL_before 112d1a0d267SMarcel Moolenaar #define THREAD_LOCAL(_x) __thread _x 113d1a0d267SMarcel Moolenaar #elif HAVE_THREAD_LOCAL == THREAD_LOCAL_after 114d1a0d267SMarcel Moolenaar #define THREAD_LOCAL(_x) _x __thread 115d1a0d267SMarcel Moolenaar #elif HAVE_THREAD_LOCAL == THREAD_LOCAL_declspec 116d1a0d267SMarcel Moolenaar #define THREAD_LOCAL(_x) __declspec(_x) 117d1a0d267SMarcel Moolenaar #else 118d1a0d267SMarcel Moolenaar #error unknown thread-local setting 119d1a0d267SMarcel Moolenaar #endif /* HAVE_THREADS_H */ 120d1a0d267SMarcel Moolenaar 12131337658SMarcel Moolenaar const char xo_version[] = LIBXO_VERSION; 12231337658SMarcel Moolenaar const char xo_version_extra[] = LIBXO_VERSION_EXTRA; 12331337658SMarcel Moolenaar 12431337658SMarcel Moolenaar #ifndef UNUSED 12531337658SMarcel Moolenaar #define UNUSED __attribute__ ((__unused__)) 12631337658SMarcel Moolenaar #endif /* UNUSED */ 12731337658SMarcel Moolenaar 12831337658SMarcel Moolenaar #define XO_INDENT_BY 2 /* Amount to indent when pretty printing */ 129d1a0d267SMarcel Moolenaar #define XO_DEPTH 128 /* Default stack depth */ 13031337658SMarcel Moolenaar #define XO_MAX_ANCHOR_WIDTH (8*1024) /* Anything wider is just sillyb */ 13131337658SMarcel Moolenaar 13231337658SMarcel Moolenaar #define XO_FAILURE_NAME "failure" 13331337658SMarcel Moolenaar 13431337658SMarcel Moolenaar /* Flags for the stack frame */ 13531337658SMarcel Moolenaar typedef unsigned xo_xsf_flags_t; /* XSF_* flags */ 13631337658SMarcel Moolenaar #define XSF_NOT_FIRST (1<<0) /* Not the first element */ 13731337658SMarcel Moolenaar #define XSF_LIST (1<<1) /* Frame is a list */ 13831337658SMarcel Moolenaar #define XSF_INSTANCE (1<<2) /* Frame is an instance */ 13931337658SMarcel Moolenaar #define XSF_DTRT (1<<3) /* Save the name for DTRT mode */ 14031337658SMarcel Moolenaar 141545ddfbeSMarcel Moolenaar #define XSF_CONTENT (1<<4) /* Some content has been emitted */ 142545ddfbeSMarcel Moolenaar #define XSF_EMIT (1<<5) /* Some field has been emitted */ 143545ddfbeSMarcel Moolenaar #define XSF_EMIT_KEY (1<<6) /* A key has been emitted */ 144545ddfbeSMarcel Moolenaar #define XSF_EMIT_LEAF_LIST (1<<7) /* A leaf-list field has been emitted */ 145545ddfbeSMarcel Moolenaar 146545ddfbeSMarcel Moolenaar /* These are the flags we propagate between markers and their parents */ 147545ddfbeSMarcel Moolenaar #define XSF_MARKER_FLAGS \ 148545ddfbeSMarcel Moolenaar (XSF_NOT_FIRST | XSF_CONTENT | XSF_EMIT | XSF_EMIT_KEY | XSF_EMIT_LEAF_LIST ) 149545ddfbeSMarcel Moolenaar 150545ddfbeSMarcel Moolenaar /* 151d1a0d267SMarcel Moolenaar * A word about states: We use a finite state machine (FMS) approach 152d1a0d267SMarcel Moolenaar * to help remove fragility from the caller's code. Instead of 153d1a0d267SMarcel Moolenaar * requiring a specific order of calls, we'll allow the caller more 154545ddfbeSMarcel Moolenaar * flexibility and make the library responsible for recovering from 155d1a0d267SMarcel Moolenaar * missed steps. The goal is that the library should not be capable 156d1a0d267SMarcel Moolenaar * of emitting invalid xml or json, but the developer shouldn't need 157545ddfbeSMarcel Moolenaar * to know or understand all the details about these encodings. 158545ddfbeSMarcel Moolenaar * 159d1a0d267SMarcel Moolenaar * You can think of states as either states or events, since they 160545ddfbeSMarcel Moolenaar * function rather like both. None of the XO_CLOSE_* events will 161d1a0d267SMarcel Moolenaar * persist as states, since the matching stack frame will be popped. 162545ddfbeSMarcel Moolenaar * Same is true of XSS_EMIT, which is an event that asks us to 163545ddfbeSMarcel Moolenaar * prep for emitting output fields. 164545ddfbeSMarcel Moolenaar */ 165545ddfbeSMarcel Moolenaar 166545ddfbeSMarcel Moolenaar /* Stack frame states */ 167545ddfbeSMarcel Moolenaar typedef unsigned xo_state_t; 168545ddfbeSMarcel Moolenaar #define XSS_INIT 0 /* Initial stack state */ 169545ddfbeSMarcel Moolenaar #define XSS_OPEN_CONTAINER 1 170545ddfbeSMarcel Moolenaar #define XSS_CLOSE_CONTAINER 2 171545ddfbeSMarcel Moolenaar #define XSS_OPEN_LIST 3 172545ddfbeSMarcel Moolenaar #define XSS_CLOSE_LIST 4 173545ddfbeSMarcel Moolenaar #define XSS_OPEN_INSTANCE 5 174545ddfbeSMarcel Moolenaar #define XSS_CLOSE_INSTANCE 6 175545ddfbeSMarcel Moolenaar #define XSS_OPEN_LEAF_LIST 7 176545ddfbeSMarcel Moolenaar #define XSS_CLOSE_LEAF_LIST 8 177545ddfbeSMarcel Moolenaar #define XSS_DISCARDING 9 /* Discarding data until recovered */ 178545ddfbeSMarcel Moolenaar #define XSS_MARKER 10 /* xo_open_marker's marker */ 179545ddfbeSMarcel Moolenaar #define XSS_EMIT 11 /* xo_emit has a leaf field */ 180545ddfbeSMarcel Moolenaar #define XSS_EMIT_LEAF_LIST 12 /* xo_emit has a leaf-list ({l:}) */ 181545ddfbeSMarcel Moolenaar #define XSS_FINISH 13 /* xo_finish was called */ 182545ddfbeSMarcel Moolenaar 183545ddfbeSMarcel Moolenaar #define XSS_MAX 13 184545ddfbeSMarcel Moolenaar 185545ddfbeSMarcel Moolenaar #define XSS_TRANSITION(_old, _new) ((_old) << 8 | (_new)) 186545ddfbeSMarcel Moolenaar 18731337658SMarcel Moolenaar /* 18831337658SMarcel Moolenaar * xo_stack_t: As we open and close containers and levels, we 18931337658SMarcel Moolenaar * create a stack of frames to track them. This is needed for 19031337658SMarcel Moolenaar * XOF_WARN and XOF_XPATH. 19131337658SMarcel Moolenaar */ 19231337658SMarcel Moolenaar typedef struct xo_stack_s { 19331337658SMarcel Moolenaar xo_xsf_flags_t xs_flags; /* Flags for this frame */ 194545ddfbeSMarcel Moolenaar xo_state_t xs_state; /* State for this stack frame */ 19531337658SMarcel Moolenaar char *xs_name; /* Name (for XPath value) */ 19631337658SMarcel Moolenaar char *xs_keys; /* XPath predicate for any key fields */ 19731337658SMarcel Moolenaar } xo_stack_t; 19831337658SMarcel Moolenaar 199d1a0d267SMarcel Moolenaar /* 200d1a0d267SMarcel Moolenaar * libxo supports colors and effects, for those who like them. 201d1a0d267SMarcel Moolenaar * XO_COL_* ("colors") refers to fancy ansi codes, while X__EFF_* 202d1a0d267SMarcel Moolenaar * ("effects") are bits since we need to maintain state. 203d1a0d267SMarcel Moolenaar */ 204788ca347SMarcel Moolenaar #define XO_COL_DEFAULT 0 205788ca347SMarcel Moolenaar #define XO_COL_BLACK 1 206788ca347SMarcel Moolenaar #define XO_COL_RED 2 207788ca347SMarcel Moolenaar #define XO_COL_GREEN 3 208788ca347SMarcel Moolenaar #define XO_COL_YELLOW 4 209788ca347SMarcel Moolenaar #define XO_COL_BLUE 5 210788ca347SMarcel Moolenaar #define XO_COL_MAGENTA 6 211788ca347SMarcel Moolenaar #define XO_COL_CYAN 7 212788ca347SMarcel Moolenaar #define XO_COL_WHITE 8 213788ca347SMarcel Moolenaar 214788ca347SMarcel Moolenaar #define XO_NUM_COLORS 9 215788ca347SMarcel Moolenaar 216788ca347SMarcel Moolenaar /* 217788ca347SMarcel Moolenaar * Yes, there's no blink. We're civilized. We like users. Blink 218788ca347SMarcel Moolenaar * isn't something one does to someone you like. Friends don't let 219788ca347SMarcel Moolenaar * friends use blink. On friends. You know what I mean. Blink is 220788ca347SMarcel Moolenaar * like, well, it's like bursting into show tunes at a funeral. It's 221788ca347SMarcel Moolenaar * just not done. Not something anyone wants. And on those rare 222d1a0d267SMarcel Moolenaar * instances where it might actually be appropriate, it's still wrong, 223d1a0d267SMarcel Moolenaar * since it's likely done by the wrong person for the wrong reason. 224d1a0d267SMarcel Moolenaar * Just like blink. And if I implemented blink, I'd be like a funeral 225788ca347SMarcel Moolenaar * director who adds "Would you like us to burst into show tunes?" on 226d1a0d267SMarcel Moolenaar * the list of questions asked while making funeral arrangements. 227788ca347SMarcel Moolenaar * It's formalizing wrongness in the wrong way. And we're just too 228788ca347SMarcel Moolenaar * civilized to do that. Hhhmph! 229788ca347SMarcel Moolenaar */ 230788ca347SMarcel Moolenaar #define XO_EFF_RESET (1<<0) 231788ca347SMarcel Moolenaar #define XO_EFF_NORMAL (1<<1) 232788ca347SMarcel Moolenaar #define XO_EFF_BOLD (1<<2) 233788ca347SMarcel Moolenaar #define XO_EFF_UNDERLINE (1<<3) 234788ca347SMarcel Moolenaar #define XO_EFF_INVERSE (1<<4) 235788ca347SMarcel Moolenaar 236d1a0d267SMarcel Moolenaar #define XO_EFF_CLEAR_BITS XO_EFF_RESET /* Reset gets reset, surprisingly */ 237788ca347SMarcel Moolenaar 238788ca347SMarcel Moolenaar typedef uint8_t xo_effect_t; 239788ca347SMarcel Moolenaar typedef uint8_t xo_color_t; 240788ca347SMarcel Moolenaar typedef struct xo_colors_s { 241788ca347SMarcel Moolenaar xo_effect_t xoc_effects; /* Current effect set */ 242788ca347SMarcel Moolenaar xo_color_t xoc_col_fg; /* Foreground color */ 243788ca347SMarcel Moolenaar xo_color_t xoc_col_bg; /* Background color */ 244788ca347SMarcel Moolenaar } xo_colors_t; 245788ca347SMarcel Moolenaar 24631337658SMarcel Moolenaar /* 24731337658SMarcel Moolenaar * xo_handle_t: this is the principle data structure for libxo. 248d1a0d267SMarcel Moolenaar * It's used as a store for state, options, content, and all manor 249d1a0d267SMarcel Moolenaar * of other information. 25031337658SMarcel Moolenaar */ 25131337658SMarcel Moolenaar struct xo_handle_s { 252d1a0d267SMarcel Moolenaar xo_xof_flags_t xo_flags; /* Flags (XOF_*) from the user*/ 253d1a0d267SMarcel Moolenaar xo_xof_flags_t xo_iflags; /* Internal flags (XOIF_*) */ 254d1a0d267SMarcel Moolenaar xo_style_t xo_style; /* XO_STYLE_* value */ 25531337658SMarcel Moolenaar unsigned short xo_indent; /* Indent level (if pretty) */ 25631337658SMarcel Moolenaar unsigned short xo_indent_by; /* Indent amount (tab stop) */ 25731337658SMarcel Moolenaar xo_write_func_t xo_write; /* Write callback */ 258a0f704ffSMarcel Moolenaar xo_close_func_t xo_close; /* Close callback */ 259545ddfbeSMarcel Moolenaar xo_flush_func_t xo_flush; /* Flush callback */ 26031337658SMarcel Moolenaar xo_formatter_t xo_formatter; /* Custom formating function */ 26131337658SMarcel Moolenaar xo_checkpointer_t xo_checkpointer; /* Custom formating support function */ 26231337658SMarcel Moolenaar void *xo_opaque; /* Opaque data for write function */ 26331337658SMarcel Moolenaar xo_buffer_t xo_data; /* Output data */ 26431337658SMarcel Moolenaar xo_buffer_t xo_fmt; /* Work area for building format strings */ 26531337658SMarcel Moolenaar xo_buffer_t xo_attrs; /* Work area for building XML attributes */ 26631337658SMarcel Moolenaar xo_buffer_t xo_predicate; /* Work area for building XPath predicates */ 26731337658SMarcel Moolenaar xo_stack_t *xo_stack; /* Stack pointer */ 26831337658SMarcel Moolenaar int xo_depth; /* Depth of stack */ 26931337658SMarcel Moolenaar int xo_stack_size; /* Size of the stack */ 27031337658SMarcel Moolenaar xo_info_t *xo_info; /* Info fields for all elements */ 27131337658SMarcel Moolenaar int xo_info_count; /* Number of info entries */ 27231337658SMarcel Moolenaar va_list xo_vap; /* Variable arguments (stdargs) */ 27331337658SMarcel Moolenaar char *xo_leading_xpath; /* A leading XPath expression */ 27431337658SMarcel Moolenaar mbstate_t xo_mbstate; /* Multi-byte character conversion state */ 27531337658SMarcel Moolenaar unsigned xo_anchor_offset; /* Start of anchored text */ 27631337658SMarcel Moolenaar unsigned xo_anchor_columns; /* Number of columns since the start anchor */ 27731337658SMarcel Moolenaar int xo_anchor_min_width; /* Desired width of anchored text */ 27831337658SMarcel Moolenaar unsigned xo_units_offset; /* Start of units insertion point */ 27931337658SMarcel Moolenaar unsigned xo_columns; /* Columns emitted during this xo_emit call */ 280788ca347SMarcel Moolenaar uint8_t xo_color_map_fg[XO_NUM_COLORS]; /* Foreground color mappings */ 281788ca347SMarcel Moolenaar uint8_t xo_color_map_bg[XO_NUM_COLORS]; /* Background color mappings */ 282788ca347SMarcel Moolenaar xo_colors_t xo_colors; /* Current color and effect values */ 283788ca347SMarcel Moolenaar xo_buffer_t xo_color_buf; /* HTML: buffer of colors and effects */ 284788ca347SMarcel Moolenaar char *xo_version; /* Version string */ 285d1a0d267SMarcel Moolenaar int xo_errno; /* Saved errno for "%m" */ 286d1a0d267SMarcel Moolenaar char *xo_gt_domain; /* Gettext domain, suitable for dgettext(3) */ 287d1a0d267SMarcel Moolenaar xo_encoder_func_t xo_encoder; /* Encoding function */ 288d1a0d267SMarcel Moolenaar void *xo_private; /* Private data for external encoders */ 28931337658SMarcel Moolenaar }; 29031337658SMarcel Moolenaar 291d1a0d267SMarcel Moolenaar /* Flag operations */ 292d1a0d267SMarcel Moolenaar #define XOF_BIT_ISSET(_flag, _bit) (((_flag) & (_bit)) ? 1 : 0) 293d1a0d267SMarcel Moolenaar #define XOF_BIT_SET(_flag, _bit) do { (_flag) |= (_bit); } while (0) 294d1a0d267SMarcel Moolenaar #define XOF_BIT_CLEAR(_flag, _bit) do { (_flag) &= ~(_bit); } while (0) 295d1a0d267SMarcel Moolenaar 296d1a0d267SMarcel Moolenaar #define XOF_ISSET(_xop, _bit) XOF_BIT_ISSET(_xop->xo_flags, _bit) 297d1a0d267SMarcel Moolenaar #define XOF_SET(_xop, _bit) XOF_BIT_SET(_xop->xo_flags, _bit) 298d1a0d267SMarcel Moolenaar #define XOF_CLEAR(_xop, _bit) XOF_BIT_CLEAR(_xop->xo_flags, _bit) 299d1a0d267SMarcel Moolenaar 300d1a0d267SMarcel Moolenaar #define XOIF_ISSET(_xop, _bit) XOF_BIT_ISSET(_xop->xo_iflags, _bit) 301d1a0d267SMarcel Moolenaar #define XOIF_SET(_xop, _bit) XOF_BIT_SET(_xop->xo_iflags, _bit) 302d1a0d267SMarcel Moolenaar #define XOIF_CLEAR(_xop, _bit) XOF_BIT_CLEAR(_xop->xo_iflags, _bit) 303d1a0d267SMarcel Moolenaar 304d1a0d267SMarcel Moolenaar /* Internal flags */ 305d1a0d267SMarcel Moolenaar #define XOIF_REORDER XOF_BIT(0) /* Reordering fields; record field info */ 306d1a0d267SMarcel Moolenaar #define XOIF_DIV_OPEN XOF_BIT(1) /* A <div> is open */ 307d1a0d267SMarcel Moolenaar #define XOIF_TOP_EMITTED XOF_BIT(2) /* The top JSON braces have been emitted */ 308d1a0d267SMarcel Moolenaar #define XOIF_ANCHOR XOF_BIT(3) /* An anchor is in place */ 309d1a0d267SMarcel Moolenaar 310d1a0d267SMarcel Moolenaar #define XOIF_UNITS_PENDING XOF_BIT(4) /* We have a units-insertion pending */ 311d1a0d267SMarcel Moolenaar #define XOIF_INIT_IN_PROGRESS XOF_BIT(5) /* Init of handle is in progress */ 312d1a0d267SMarcel Moolenaar 31331337658SMarcel Moolenaar /* Flags for formatting functions */ 31431337658SMarcel Moolenaar typedef unsigned long xo_xff_flags_t; 31531337658SMarcel Moolenaar #define XFF_COLON (1<<0) /* Append a ":" */ 31631337658SMarcel Moolenaar #define XFF_COMMA (1<<1) /* Append a "," iff there's more output */ 31731337658SMarcel Moolenaar #define XFF_WS (1<<2) /* Append a blank */ 318d1a0d267SMarcel Moolenaar #define XFF_ENCODE_ONLY (1<<3) /* Only emit for encoding styles (XML, JSON) */ 31931337658SMarcel Moolenaar 32031337658SMarcel Moolenaar #define XFF_QUOTE (1<<4) /* Force quotes */ 32131337658SMarcel Moolenaar #define XFF_NOQUOTE (1<<5) /* Force no quotes */ 322d1a0d267SMarcel Moolenaar #define XFF_DISPLAY_ONLY (1<<6) /* Only emit for display styles (text, html) */ 32331337658SMarcel Moolenaar #define XFF_KEY (1<<7) /* Field is a key (for XPath) */ 32431337658SMarcel Moolenaar 32531337658SMarcel Moolenaar #define XFF_XML (1<<8) /* Force XML encoding style (for XPath) */ 32631337658SMarcel Moolenaar #define XFF_ATTR (1<<9) /* Escape value using attribute rules (XML) */ 32731337658SMarcel Moolenaar #define XFF_BLANK_LINE (1<<10) /* Emit a blank line */ 32831337658SMarcel Moolenaar #define XFF_NO_OUTPUT (1<<11) /* Do not make any output */ 32931337658SMarcel Moolenaar 33031337658SMarcel Moolenaar #define XFF_TRIM_WS (1<<12) /* Trim whitespace off encoded values */ 33131337658SMarcel Moolenaar #define XFF_LEAF_LIST (1<<13) /* A leaf-list (list of values) */ 33231337658SMarcel Moolenaar #define XFF_UNESCAPE (1<<14) /* Need to printf-style unescape the value */ 333d1a0d267SMarcel Moolenaar #define XFF_HUMANIZE (1<<15) /* Humanize the value (for display styles) */ 334d1a0d267SMarcel Moolenaar 335d1a0d267SMarcel Moolenaar #define XFF_HN_SPACE (1<<16) /* Humanize: put space before suffix */ 336d1a0d267SMarcel Moolenaar #define XFF_HN_DECIMAL (1<<17) /* Humanize: add one decimal place if <10 */ 337d1a0d267SMarcel Moolenaar #define XFF_HN_1000 (1<<18) /* Humanize: use 1000, not 1024 */ 338d1a0d267SMarcel Moolenaar #define XFF_GT_FIELD (1<<19) /* Call gettext() on a field */ 339d1a0d267SMarcel Moolenaar 340d1a0d267SMarcel Moolenaar #define XFF_GT_PLURAL (1<<20) /* Call dngettext to find plural form */ 341d1a0d267SMarcel Moolenaar 342d1a0d267SMarcel Moolenaar /* Flags to turn off when we don't want i18n processing */ 343d1a0d267SMarcel Moolenaar #define XFF_GT_FLAGS (XFF_GT_FIELD | XFF_GT_PLURAL) 34431337658SMarcel Moolenaar 34531337658SMarcel Moolenaar /* 34631337658SMarcel Moolenaar * Normal printf has width and precision, which for strings operate as 34731337658SMarcel Moolenaar * min and max number of columns. But this depends on the idea that 34831337658SMarcel Moolenaar * one byte means one column, which UTF-8 and multi-byte characters 34931337658SMarcel Moolenaar * pitches on its ear. It may take 40 bytes of data to populate 14 35031337658SMarcel Moolenaar * columns, but we can't go off looking at 40 bytes of data without the 35131337658SMarcel Moolenaar * caller's permission for fear/knowledge that we'll generate core files. 35231337658SMarcel Moolenaar * 35331337658SMarcel Moolenaar * So we make three values, distinguishing between "max column" and 35431337658SMarcel Moolenaar * "number of bytes that we will inspect inspect safely" We call the 35531337658SMarcel Moolenaar * later "size", and make the format "%[[<min>].[[<size>].<max>]]s". 35631337658SMarcel Moolenaar * 35731337658SMarcel Moolenaar * Under the "first do no harm" theory, we default "max" to "size". 35831337658SMarcel Moolenaar * This is a reasonable assumption for folks that don't grok the 35931337658SMarcel Moolenaar * MBS/WCS/UTF-8 world, and while it will be annoying, it will never 36031337658SMarcel Moolenaar * be evil. 36131337658SMarcel Moolenaar * 36231337658SMarcel Moolenaar * For example, xo_emit("{:tag/%-14.14s}", buf) will make 14 36331337658SMarcel Moolenaar * columns of output, but will never look at more than 14 bytes of the 36431337658SMarcel Moolenaar * input buffer. This is mostly compatible with printf and caller's 36531337658SMarcel Moolenaar * expectations. 36631337658SMarcel Moolenaar * 36731337658SMarcel Moolenaar * In contrast xo_emit("{:tag/%-14..14s}", buf) will look at however 36831337658SMarcel Moolenaar * many bytes (or until a NUL is seen) are needed to fill 14 columns 36931337658SMarcel Moolenaar * of output. xo_emit("{:tag/%-14.*.14s}", xx, buf) will look at up 37031337658SMarcel Moolenaar * to xx bytes (or until a NUL is seen) in order to fill 14 columns 37131337658SMarcel Moolenaar * of output. 37231337658SMarcel Moolenaar * 37331337658SMarcel Moolenaar * It's fairly amazing how a good idea (handle all languages of the 37431337658SMarcel Moolenaar * world) blows such a big hole in the bottom of the fairly weak boat 37531337658SMarcel Moolenaar * that is C string handling. The simplicity and completenesss are 37631337658SMarcel Moolenaar * sunk in ways we haven't even begun to understand. 37731337658SMarcel Moolenaar */ 37831337658SMarcel Moolenaar #define XF_WIDTH_MIN 0 /* Minimal width */ 37931337658SMarcel Moolenaar #define XF_WIDTH_SIZE 1 /* Maximum number of bytes to examine */ 38031337658SMarcel Moolenaar #define XF_WIDTH_MAX 2 /* Maximum width */ 38131337658SMarcel Moolenaar #define XF_WIDTH_NUM 3 /* Numeric fields in printf (min.size.max) */ 38231337658SMarcel Moolenaar 38331337658SMarcel Moolenaar /* Input and output string encodings */ 38431337658SMarcel Moolenaar #define XF_ENC_WIDE 1 /* Wide characters (wchar_t) */ 38531337658SMarcel Moolenaar #define XF_ENC_UTF8 2 /* UTF-8 */ 38631337658SMarcel Moolenaar #define XF_ENC_LOCALE 3 /* Current locale */ 38731337658SMarcel Moolenaar 38831337658SMarcel Moolenaar /* 38931337658SMarcel Moolenaar * A place to parse printf-style format flags for each field 39031337658SMarcel Moolenaar */ 39131337658SMarcel Moolenaar typedef struct xo_format_s { 39231337658SMarcel Moolenaar unsigned char xf_fc; /* Format character */ 39331337658SMarcel Moolenaar unsigned char xf_enc; /* Encoding of the string (XF_ENC_*) */ 39431337658SMarcel Moolenaar unsigned char xf_skip; /* Skip this field */ 39531337658SMarcel Moolenaar unsigned char xf_lflag; /* 'l' (long) */ 39631337658SMarcel Moolenaar unsigned char xf_hflag;; /* 'h' (half) */ 39731337658SMarcel Moolenaar unsigned char xf_jflag; /* 'j' (intmax_t) */ 39831337658SMarcel Moolenaar unsigned char xf_tflag; /* 't' (ptrdiff_t) */ 39931337658SMarcel Moolenaar unsigned char xf_zflag; /* 'z' (size_t) */ 40031337658SMarcel Moolenaar unsigned char xf_qflag; /* 'q' (quad_t) */ 40131337658SMarcel Moolenaar unsigned char xf_seen_minus; /* Seen a minus */ 40231337658SMarcel Moolenaar int xf_leading_zero; /* Seen a leading zero (zero fill) */ 40331337658SMarcel Moolenaar unsigned xf_dots; /* Seen one or more '.'s */ 40431337658SMarcel Moolenaar int xf_width[XF_WIDTH_NUM]; /* Width/precision/size numeric fields */ 40531337658SMarcel Moolenaar unsigned xf_stars; /* Seen one or more '*'s */ 40631337658SMarcel Moolenaar unsigned char xf_star[XF_WIDTH_NUM]; /* Seen one or more '*'s */ 40731337658SMarcel Moolenaar } xo_format_t; 40831337658SMarcel Moolenaar 40931337658SMarcel Moolenaar /* 410d1a0d267SMarcel Moolenaar * This structure represents the parsed field information, suitable for 411d1a0d267SMarcel Moolenaar * processing by xo_do_emit and anything else that needs to parse fields. 412d1a0d267SMarcel Moolenaar * Note that all pointers point to the main format string. 413d1a0d267SMarcel Moolenaar * 414d1a0d267SMarcel Moolenaar * XXX This is a first step toward compilable or cachable format 415d1a0d267SMarcel Moolenaar * strings. We can also cache the results of dgettext when no format 416d1a0d267SMarcel Moolenaar * is used, assuming the 'p' modifier has _not_ been set. 41731337658SMarcel Moolenaar */ 418d1a0d267SMarcel Moolenaar typedef struct xo_field_info_s { 419d1a0d267SMarcel Moolenaar xo_xff_flags_t xfi_flags; /* Flags for this field */ 420d1a0d267SMarcel Moolenaar unsigned xfi_ftype; /* Field type, as character (e.g. 'V') */ 421d1a0d267SMarcel Moolenaar const char *xfi_start; /* Start of field in the format string */ 422d1a0d267SMarcel Moolenaar const char *xfi_content; /* Field's content */ 423d1a0d267SMarcel Moolenaar const char *xfi_format; /* Field's Format */ 424d1a0d267SMarcel Moolenaar const char *xfi_encoding; /* Field's encoding format */ 425d1a0d267SMarcel Moolenaar const char *xfi_next; /* Next character in format string */ 426d1a0d267SMarcel Moolenaar unsigned xfi_len; /* Length of field */ 427d1a0d267SMarcel Moolenaar unsigned xfi_clen; /* Content length */ 428d1a0d267SMarcel Moolenaar unsigned xfi_flen; /* Format length */ 429d1a0d267SMarcel Moolenaar unsigned xfi_elen; /* Encoding length */ 430d1a0d267SMarcel Moolenaar unsigned xfi_fnum; /* Field number (if used; 0 otherwise) */ 431d1a0d267SMarcel Moolenaar unsigned xfi_renum; /* Reordered number (0 == no renumbering) */ 432d1a0d267SMarcel Moolenaar } xo_field_info_t; 433d1a0d267SMarcel Moolenaar 434d1a0d267SMarcel Moolenaar /* 435d1a0d267SMarcel Moolenaar * We keep a 'default' handle to allow callers to avoid having to 436d1a0d267SMarcel Moolenaar * allocate one. Passing NULL to any of our functions will use 437d1a0d267SMarcel Moolenaar * this default handle. Most functions have a variant that doesn't 438d1a0d267SMarcel Moolenaar * require a handle at all, since most output is to stdout, which 439d1a0d267SMarcel Moolenaar * the default handle handles handily. 440d1a0d267SMarcel Moolenaar */ 441d1a0d267SMarcel Moolenaar static THREAD_LOCAL(xo_handle_t) xo_default_handle; 442d1a0d267SMarcel Moolenaar static THREAD_LOCAL(int) xo_default_inited; 44331337658SMarcel Moolenaar static int xo_locale_inited; 444545ddfbeSMarcel Moolenaar static const char *xo_program; 44531337658SMarcel Moolenaar 44631337658SMarcel Moolenaar /* 44731337658SMarcel Moolenaar * To allow libxo to be used in diverse environment, we allow the 44831337658SMarcel Moolenaar * caller to give callbacks for memory allocation. 44931337658SMarcel Moolenaar */ 450d1a0d267SMarcel Moolenaar xo_realloc_func_t xo_realloc = realloc; 451d1a0d267SMarcel Moolenaar xo_free_func_t xo_free = free; 45231337658SMarcel Moolenaar 45331337658SMarcel Moolenaar /* Forward declarations */ 45431337658SMarcel Moolenaar static void 45531337658SMarcel Moolenaar xo_failure (xo_handle_t *xop, const char *fmt, ...); 45631337658SMarcel Moolenaar 457545ddfbeSMarcel Moolenaar static int 458545ddfbeSMarcel Moolenaar xo_transition (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name, 459545ddfbeSMarcel Moolenaar xo_state_t new_state); 460545ddfbeSMarcel Moolenaar 46131337658SMarcel Moolenaar static void 46231337658SMarcel Moolenaar xo_buf_append_div (xo_handle_t *xop, const char *class, xo_xff_flags_t flags, 46331337658SMarcel Moolenaar const char *name, int nlen, 46431337658SMarcel Moolenaar const char *value, int vlen, 46531337658SMarcel Moolenaar const char *encoding, int elen); 46631337658SMarcel Moolenaar 46731337658SMarcel Moolenaar static void 46831337658SMarcel Moolenaar xo_anchor_clear (xo_handle_t *xop); 46931337658SMarcel Moolenaar 47031337658SMarcel Moolenaar /* 471788ca347SMarcel Moolenaar * xo_style is used to retrieve the current style. When we're built 472788ca347SMarcel Moolenaar * for "text only" mode, we use this function to drive the removal 473788ca347SMarcel Moolenaar * of most of the code in libxo. We return a constant and the compiler 474788ca347SMarcel Moolenaar * happily removes the non-text code that is not longer executed. This 475788ca347SMarcel Moolenaar * trims our code nicely without needing to trampel perfectly readable 476788ca347SMarcel Moolenaar * code with ifdefs. 477788ca347SMarcel Moolenaar */ 478d1a0d267SMarcel Moolenaar static inline xo_style_t 479788ca347SMarcel Moolenaar xo_style (xo_handle_t *xop UNUSED) 480788ca347SMarcel Moolenaar { 481788ca347SMarcel Moolenaar #ifdef LIBXO_TEXT_ONLY 482788ca347SMarcel Moolenaar return XO_STYLE_TEXT; 483788ca347SMarcel Moolenaar #else /* LIBXO_TEXT_ONLY */ 484788ca347SMarcel Moolenaar return xop->xo_style; 485788ca347SMarcel Moolenaar #endif /* LIBXO_TEXT_ONLY */ 486788ca347SMarcel Moolenaar } 487788ca347SMarcel Moolenaar 488788ca347SMarcel Moolenaar /* 48931337658SMarcel Moolenaar * Callback to write data to a FILE pointer 49031337658SMarcel Moolenaar */ 49131337658SMarcel Moolenaar static int 49231337658SMarcel Moolenaar xo_write_to_file (void *opaque, const char *data) 49331337658SMarcel Moolenaar { 49431337658SMarcel Moolenaar FILE *fp = (FILE *) opaque; 495545ddfbeSMarcel Moolenaar 49631337658SMarcel Moolenaar return fprintf(fp, "%s", data); 49731337658SMarcel Moolenaar } 49831337658SMarcel Moolenaar 49931337658SMarcel Moolenaar /* 50031337658SMarcel Moolenaar * Callback to close a file 50131337658SMarcel Moolenaar */ 50231337658SMarcel Moolenaar static void 50331337658SMarcel Moolenaar xo_close_file (void *opaque) 50431337658SMarcel Moolenaar { 50531337658SMarcel Moolenaar FILE *fp = (FILE *) opaque; 506545ddfbeSMarcel Moolenaar 50731337658SMarcel Moolenaar fclose(fp); 50831337658SMarcel Moolenaar } 50931337658SMarcel Moolenaar 51031337658SMarcel Moolenaar /* 511545ddfbeSMarcel Moolenaar * Callback to flush a FILE pointer 512545ddfbeSMarcel Moolenaar */ 513545ddfbeSMarcel Moolenaar static int 514545ddfbeSMarcel Moolenaar xo_flush_file (void *opaque) 515545ddfbeSMarcel Moolenaar { 516545ddfbeSMarcel Moolenaar FILE *fp = (FILE *) opaque; 517545ddfbeSMarcel Moolenaar 518545ddfbeSMarcel Moolenaar return fflush(fp); 519545ddfbeSMarcel Moolenaar } 520545ddfbeSMarcel Moolenaar 521545ddfbeSMarcel Moolenaar /* 522d1a0d267SMarcel Moolenaar * Use a rotating stock of buffers to make a printable string 52331337658SMarcel Moolenaar */ 524d1a0d267SMarcel Moolenaar #define XO_NUMBUFS 8 525d1a0d267SMarcel Moolenaar #define XO_SMBUFSZ 128 526d1a0d267SMarcel Moolenaar 527d1a0d267SMarcel Moolenaar static const char * 528d1a0d267SMarcel Moolenaar xo_printable (const char *str) 52931337658SMarcel Moolenaar { 530d1a0d267SMarcel Moolenaar static THREAD_LOCAL(char) bufset[XO_NUMBUFS][XO_SMBUFSZ]; 531d1a0d267SMarcel Moolenaar static THREAD_LOCAL(int) bufnum = 0; 532d1a0d267SMarcel Moolenaar 533d1a0d267SMarcel Moolenaar if (str == NULL) 534d1a0d267SMarcel Moolenaar return ""; 535d1a0d267SMarcel Moolenaar 536d1a0d267SMarcel Moolenaar if (++bufnum == XO_NUMBUFS) 537d1a0d267SMarcel Moolenaar bufnum = 0; 538d1a0d267SMarcel Moolenaar 539d1a0d267SMarcel Moolenaar char *res = bufset[bufnum], *cp, *ep; 540d1a0d267SMarcel Moolenaar 541d1a0d267SMarcel Moolenaar for (cp = res, ep = res + XO_SMBUFSZ - 1; *str && cp < ep; cp++, str++) { 542d1a0d267SMarcel Moolenaar if (*str == '\n') { 543d1a0d267SMarcel Moolenaar *cp++ = '\\'; 544d1a0d267SMarcel Moolenaar *cp = 'n'; 545d1a0d267SMarcel Moolenaar } else if (*str == '\r') { 546d1a0d267SMarcel Moolenaar *cp++ = '\\'; 547d1a0d267SMarcel Moolenaar *cp = 'r'; 548d1a0d267SMarcel Moolenaar } else if (*str == '\"') { 549d1a0d267SMarcel Moolenaar *cp++ = '\\'; 550d1a0d267SMarcel Moolenaar *cp = '"'; 551d1a0d267SMarcel Moolenaar } else 552d1a0d267SMarcel Moolenaar *cp = *str; 55331337658SMarcel Moolenaar } 55431337658SMarcel Moolenaar 555d1a0d267SMarcel Moolenaar *cp = '\0'; 556d1a0d267SMarcel Moolenaar return res; 55731337658SMarcel Moolenaar } 55831337658SMarcel Moolenaar 55931337658SMarcel Moolenaar static int 56031337658SMarcel Moolenaar xo_depth_check (xo_handle_t *xop, int depth) 56131337658SMarcel Moolenaar { 56231337658SMarcel Moolenaar xo_stack_t *xsp; 56331337658SMarcel Moolenaar 56431337658SMarcel Moolenaar if (depth >= xop->xo_stack_size) { 565d1a0d267SMarcel Moolenaar depth += XO_DEPTH; /* Extra room */ 566d1a0d267SMarcel Moolenaar 56731337658SMarcel Moolenaar xsp = xo_realloc(xop->xo_stack, sizeof(xop->xo_stack[0]) * depth); 56831337658SMarcel Moolenaar if (xsp == NULL) { 56931337658SMarcel Moolenaar xo_failure(xop, "xo_depth_check: out of memory (%d)", depth); 570d1a0d267SMarcel Moolenaar return -1; 57131337658SMarcel Moolenaar } 57231337658SMarcel Moolenaar 57331337658SMarcel Moolenaar int count = depth - xop->xo_stack_size; 57431337658SMarcel Moolenaar 57531337658SMarcel Moolenaar bzero(xsp + xop->xo_stack_size, count * sizeof(*xsp)); 57631337658SMarcel Moolenaar xop->xo_stack_size = depth; 57731337658SMarcel Moolenaar xop->xo_stack = xsp; 57831337658SMarcel Moolenaar } 57931337658SMarcel Moolenaar 58031337658SMarcel Moolenaar return 0; 58131337658SMarcel Moolenaar } 58231337658SMarcel Moolenaar 58331337658SMarcel Moolenaar void 58431337658SMarcel Moolenaar xo_no_setlocale (void) 58531337658SMarcel Moolenaar { 58631337658SMarcel Moolenaar xo_locale_inited = 1; /* Skip initialization */ 58731337658SMarcel Moolenaar } 58831337658SMarcel Moolenaar 58931337658SMarcel Moolenaar /* 590545ddfbeSMarcel Moolenaar * We need to decide if stdout is line buffered (_IOLBF). Lacking a 591545ddfbeSMarcel Moolenaar * standard way to decide this (e.g. getlinebuf()), we have configure 592788ca347SMarcel Moolenaar * look to find __flbf, which glibc supported. If not, we'll rely on 593788ca347SMarcel Moolenaar * isatty, with the assumption that terminals are the only thing 594545ddfbeSMarcel Moolenaar * that's line buffered. We _could_ test for "steam._flags & _IOLBF", 595545ddfbeSMarcel Moolenaar * which is all __flbf does, but that's even tackier. Like a 596545ddfbeSMarcel Moolenaar * bedazzled Elvis outfit on an ugly lap dog sort of tacky. Not 597545ddfbeSMarcel Moolenaar * something we're willing to do. 598545ddfbeSMarcel Moolenaar */ 599545ddfbeSMarcel Moolenaar static int 600545ddfbeSMarcel Moolenaar xo_is_line_buffered (FILE *stream) 601545ddfbeSMarcel Moolenaar { 602545ddfbeSMarcel Moolenaar #if HAVE___FLBF 603545ddfbeSMarcel Moolenaar if (__flbf(stream)) 604545ddfbeSMarcel Moolenaar return 1; 605545ddfbeSMarcel Moolenaar #else /* HAVE___FLBF */ 606545ddfbeSMarcel Moolenaar if (isatty(fileno(stream))) 607545ddfbeSMarcel Moolenaar return 1; 608545ddfbeSMarcel Moolenaar #endif /* HAVE___FLBF */ 609545ddfbeSMarcel Moolenaar return 0; 610545ddfbeSMarcel Moolenaar } 611545ddfbeSMarcel Moolenaar 612545ddfbeSMarcel Moolenaar /* 61331337658SMarcel Moolenaar * Initialize an xo_handle_t, using both static defaults and 61431337658SMarcel Moolenaar * the global settings from the LIBXO_OPTIONS environment 61531337658SMarcel Moolenaar * variable. 61631337658SMarcel Moolenaar */ 61731337658SMarcel Moolenaar static void 61831337658SMarcel Moolenaar xo_init_handle (xo_handle_t *xop) 61931337658SMarcel Moolenaar { 62031337658SMarcel Moolenaar xop->xo_opaque = stdout; 62131337658SMarcel Moolenaar xop->xo_write = xo_write_to_file; 622545ddfbeSMarcel Moolenaar xop->xo_flush = xo_flush_file; 623545ddfbeSMarcel Moolenaar 624545ddfbeSMarcel Moolenaar if (xo_is_line_buffered(stdout)) 625d1a0d267SMarcel Moolenaar XOF_SET(xop, XOF_FLUSH_LINE); 62631337658SMarcel Moolenaar 62731337658SMarcel Moolenaar /* 628788ca347SMarcel Moolenaar * We only want to do color output on terminals, but we only want 629788ca347SMarcel Moolenaar * to do this if the user has asked for color. 630788ca347SMarcel Moolenaar */ 631d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_COLOR_ALLOWED) && isatty(1)) 632d1a0d267SMarcel Moolenaar XOF_SET(xop, XOF_COLOR); 633788ca347SMarcel Moolenaar 634788ca347SMarcel Moolenaar /* 63531337658SMarcel Moolenaar * We need to initialize the locale, which isn't really pretty. 63631337658SMarcel Moolenaar * Libraries should depend on their caller to set up the 63731337658SMarcel Moolenaar * environment. But we really can't count on the caller to do 63831337658SMarcel Moolenaar * this, because well, they won't. Trust me. 63931337658SMarcel Moolenaar */ 64031337658SMarcel Moolenaar if (!xo_locale_inited) { 64131337658SMarcel Moolenaar xo_locale_inited = 1; /* Only do this once */ 64231337658SMarcel Moolenaar 64331337658SMarcel Moolenaar const char *cp = getenv("LC_CTYPE"); 64431337658SMarcel Moolenaar if (cp == NULL) 64531337658SMarcel Moolenaar cp = getenv("LANG"); 64631337658SMarcel Moolenaar if (cp == NULL) 64731337658SMarcel Moolenaar cp = getenv("LC_ALL"); 64831337658SMarcel Moolenaar if (cp == NULL) 649d1a0d267SMarcel Moolenaar cp = "C"; /* Default for C programs */ 650c600d307SMarcel Moolenaar (void) setlocale(LC_CTYPE, cp); 65131337658SMarcel Moolenaar } 65231337658SMarcel Moolenaar 65331337658SMarcel Moolenaar /* 65431337658SMarcel Moolenaar * Initialize only the xo_buffers we know we'll need; the others 65531337658SMarcel Moolenaar * can be allocated as needed. 65631337658SMarcel Moolenaar */ 65731337658SMarcel Moolenaar xo_buf_init(&xop->xo_data); 65831337658SMarcel Moolenaar xo_buf_init(&xop->xo_fmt); 65931337658SMarcel Moolenaar 660d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_INIT_IN_PROGRESS)) 661d1a0d267SMarcel Moolenaar return; 662d1a0d267SMarcel Moolenaar XOIF_SET(xop, XOIF_INIT_IN_PROGRESS); 663d1a0d267SMarcel Moolenaar 66431337658SMarcel Moolenaar xop->xo_indent_by = XO_INDENT_BY; 66531337658SMarcel Moolenaar xo_depth_check(xop, XO_DEPTH); 66631337658SMarcel Moolenaar 66731337658SMarcel Moolenaar #if !defined(NO_LIBXO_OPTIONS) 668d1a0d267SMarcel Moolenaar if (!XOF_ISSET(xop, XOF_NO_ENV)) { 66931337658SMarcel Moolenaar char *env = getenv("LIBXO_OPTIONS"); 67031337658SMarcel Moolenaar if (env) 67131337658SMarcel Moolenaar xo_set_options(xop, env); 672d1a0d267SMarcel Moolenaar 67331337658SMarcel Moolenaar } 67431337658SMarcel Moolenaar #endif /* NO_GETENV */ 675d1a0d267SMarcel Moolenaar 676d1a0d267SMarcel Moolenaar XOIF_CLEAR(xop, XOIF_INIT_IN_PROGRESS); 67731337658SMarcel Moolenaar } 67831337658SMarcel Moolenaar 67931337658SMarcel Moolenaar /* 68031337658SMarcel Moolenaar * Initialize the default handle. 68131337658SMarcel Moolenaar */ 68231337658SMarcel Moolenaar static void 68331337658SMarcel Moolenaar xo_default_init (void) 68431337658SMarcel Moolenaar { 68531337658SMarcel Moolenaar xo_handle_t *xop = &xo_default_handle; 68631337658SMarcel Moolenaar 68731337658SMarcel Moolenaar xo_init_handle(xop); 68831337658SMarcel Moolenaar 68931337658SMarcel Moolenaar xo_default_inited = 1; 69031337658SMarcel Moolenaar } 69131337658SMarcel Moolenaar 69231337658SMarcel Moolenaar /* 69331337658SMarcel Moolenaar * Cheap convenience function to return either the argument, or 69431337658SMarcel Moolenaar * the internal handle, after it has been initialized. The usage 69531337658SMarcel Moolenaar * is: 69631337658SMarcel Moolenaar * xop = xo_default(xop); 69731337658SMarcel Moolenaar */ 69831337658SMarcel Moolenaar static xo_handle_t * 69931337658SMarcel Moolenaar xo_default (xo_handle_t *xop) 70031337658SMarcel Moolenaar { 70131337658SMarcel Moolenaar if (xop == NULL) { 70231337658SMarcel Moolenaar if (xo_default_inited == 0) 70331337658SMarcel Moolenaar xo_default_init(); 70431337658SMarcel Moolenaar xop = &xo_default_handle; 70531337658SMarcel Moolenaar } 70631337658SMarcel Moolenaar 70731337658SMarcel Moolenaar return xop; 70831337658SMarcel Moolenaar } 70931337658SMarcel Moolenaar 71031337658SMarcel Moolenaar /* 71131337658SMarcel Moolenaar * Return the number of spaces we should be indenting. If 712788ca347SMarcel Moolenaar * we are pretty-printing, this is indent * indent_by. 71331337658SMarcel Moolenaar */ 71431337658SMarcel Moolenaar static int 71531337658SMarcel Moolenaar xo_indent (xo_handle_t *xop) 71631337658SMarcel Moolenaar { 71731337658SMarcel Moolenaar int rc = 0; 71831337658SMarcel Moolenaar 71931337658SMarcel Moolenaar xop = xo_default(xop); 72031337658SMarcel Moolenaar 721d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_PRETTY)) { 72231337658SMarcel Moolenaar rc = xop->xo_indent * xop->xo_indent_by; 723d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_TOP_EMITTED)) 72431337658SMarcel Moolenaar rc += xop->xo_indent_by; 72531337658SMarcel Moolenaar } 72631337658SMarcel Moolenaar 727545ddfbeSMarcel Moolenaar return (rc > 0) ? rc : 0; 72831337658SMarcel Moolenaar } 72931337658SMarcel Moolenaar 73031337658SMarcel Moolenaar static void 73131337658SMarcel Moolenaar xo_buf_indent (xo_handle_t *xop, int indent) 73231337658SMarcel Moolenaar { 73331337658SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_data; 73431337658SMarcel Moolenaar 73531337658SMarcel Moolenaar if (indent <= 0) 73631337658SMarcel Moolenaar indent = xo_indent(xop); 73731337658SMarcel Moolenaar 73831337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, indent)) 73931337658SMarcel Moolenaar return; 74031337658SMarcel Moolenaar 74131337658SMarcel Moolenaar memset(xbp->xb_curp, ' ', indent); 74231337658SMarcel Moolenaar xbp->xb_curp += indent; 74331337658SMarcel Moolenaar } 74431337658SMarcel Moolenaar 74531337658SMarcel Moolenaar static char xo_xml_amp[] = "&"; 74631337658SMarcel Moolenaar static char xo_xml_lt[] = "<"; 74731337658SMarcel Moolenaar static char xo_xml_gt[] = ">"; 74831337658SMarcel Moolenaar static char xo_xml_quot[] = """; 74931337658SMarcel Moolenaar 75031337658SMarcel Moolenaar static int 751d1a0d267SMarcel Moolenaar xo_escape_xml (xo_buffer_t *xbp, int len, xo_xff_flags_t flags) 75231337658SMarcel Moolenaar { 75331337658SMarcel Moolenaar int slen; 75431337658SMarcel Moolenaar unsigned delta = 0; 75531337658SMarcel Moolenaar char *cp, *ep, *ip; 75631337658SMarcel Moolenaar const char *sp; 757d1a0d267SMarcel Moolenaar int attr = (flags & XFF_ATTR); 75831337658SMarcel Moolenaar 75931337658SMarcel Moolenaar for (cp = xbp->xb_curp, ep = cp + len; cp < ep; cp++) { 76031337658SMarcel Moolenaar /* We're subtracting 2: 1 for the NUL, 1 for the char we replace */ 76131337658SMarcel Moolenaar if (*cp == '<') 76231337658SMarcel Moolenaar delta += sizeof(xo_xml_lt) - 2; 76331337658SMarcel Moolenaar else if (*cp == '>') 76431337658SMarcel Moolenaar delta += sizeof(xo_xml_gt) - 2; 76531337658SMarcel Moolenaar else if (*cp == '&') 76631337658SMarcel Moolenaar delta += sizeof(xo_xml_amp) - 2; 76731337658SMarcel Moolenaar else if (attr && *cp == '"') 76831337658SMarcel Moolenaar delta += sizeof(xo_xml_quot) - 2; 76931337658SMarcel Moolenaar } 77031337658SMarcel Moolenaar 77131337658SMarcel Moolenaar if (delta == 0) /* Nothing to escape; bail */ 77231337658SMarcel Moolenaar return len; 77331337658SMarcel Moolenaar 77431337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, delta)) /* No room; bail, but don't append */ 77531337658SMarcel Moolenaar return 0; 77631337658SMarcel Moolenaar 77731337658SMarcel Moolenaar ep = xbp->xb_curp; 77831337658SMarcel Moolenaar cp = ep + len; 77931337658SMarcel Moolenaar ip = cp + delta; 78031337658SMarcel Moolenaar do { 78131337658SMarcel Moolenaar cp -= 1; 78231337658SMarcel Moolenaar ip -= 1; 78331337658SMarcel Moolenaar 78431337658SMarcel Moolenaar if (*cp == '<') 78531337658SMarcel Moolenaar sp = xo_xml_lt; 78631337658SMarcel Moolenaar else if (*cp == '>') 78731337658SMarcel Moolenaar sp = xo_xml_gt; 78831337658SMarcel Moolenaar else if (*cp == '&') 78931337658SMarcel Moolenaar sp = xo_xml_amp; 79031337658SMarcel Moolenaar else if (attr && *cp == '"') 79131337658SMarcel Moolenaar sp = xo_xml_quot; 79231337658SMarcel Moolenaar else { 79331337658SMarcel Moolenaar *ip = *cp; 79431337658SMarcel Moolenaar continue; 79531337658SMarcel Moolenaar } 79631337658SMarcel Moolenaar 79731337658SMarcel Moolenaar slen = strlen(sp); 79831337658SMarcel Moolenaar ip -= slen - 1; 79931337658SMarcel Moolenaar memcpy(ip, sp, slen); 80031337658SMarcel Moolenaar 80131337658SMarcel Moolenaar } while (cp > ep && cp != ip); 80231337658SMarcel Moolenaar 80331337658SMarcel Moolenaar return len + delta; 80431337658SMarcel Moolenaar } 80531337658SMarcel Moolenaar 80631337658SMarcel Moolenaar static int 807d1a0d267SMarcel Moolenaar xo_escape_json (xo_buffer_t *xbp, int len, xo_xff_flags_t flags UNUSED) 80831337658SMarcel Moolenaar { 80931337658SMarcel Moolenaar unsigned delta = 0; 81031337658SMarcel Moolenaar char *cp, *ep, *ip; 81131337658SMarcel Moolenaar 81231337658SMarcel Moolenaar for (cp = xbp->xb_curp, ep = cp + len; cp < ep; cp++) { 813545ddfbeSMarcel Moolenaar if (*cp == '\\' || *cp == '"') 81431337658SMarcel Moolenaar delta += 1; 815545ddfbeSMarcel Moolenaar else if (*cp == '\n' || *cp == '\r') 81631337658SMarcel Moolenaar delta += 1; 81731337658SMarcel Moolenaar } 81831337658SMarcel Moolenaar 81931337658SMarcel Moolenaar if (delta == 0) /* Nothing to escape; bail */ 82031337658SMarcel Moolenaar return len; 82131337658SMarcel Moolenaar 82231337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, delta)) /* No room; bail, but don't append */ 82331337658SMarcel Moolenaar return 0; 82431337658SMarcel Moolenaar 82531337658SMarcel Moolenaar ep = xbp->xb_curp; 82631337658SMarcel Moolenaar cp = ep + len; 82731337658SMarcel Moolenaar ip = cp + delta; 82831337658SMarcel Moolenaar do { 82931337658SMarcel Moolenaar cp -= 1; 83031337658SMarcel Moolenaar ip -= 1; 83131337658SMarcel Moolenaar 832545ddfbeSMarcel Moolenaar if (*cp == '\\' || *cp == '"') { 83331337658SMarcel Moolenaar *ip-- = *cp; 83431337658SMarcel Moolenaar *ip = '\\'; 835545ddfbeSMarcel Moolenaar } else if (*cp == '\n') { 836545ddfbeSMarcel Moolenaar *ip-- = 'n'; 837545ddfbeSMarcel Moolenaar *ip = '\\'; 838545ddfbeSMarcel Moolenaar } else if (*cp == '\r') { 839545ddfbeSMarcel Moolenaar *ip-- = 'r'; 840545ddfbeSMarcel Moolenaar *ip = '\\'; 841545ddfbeSMarcel Moolenaar } else { 842545ddfbeSMarcel Moolenaar *ip = *cp; 843545ddfbeSMarcel Moolenaar } 84431337658SMarcel Moolenaar 84531337658SMarcel Moolenaar } while (cp > ep && cp != ip); 84631337658SMarcel Moolenaar 84731337658SMarcel Moolenaar return len + delta; 84831337658SMarcel Moolenaar } 84931337658SMarcel Moolenaar 85031337658SMarcel Moolenaar /* 851d1a0d267SMarcel Moolenaar * PARAM-VALUE = UTF-8-STRING ; characters '"', '\' and 852d1a0d267SMarcel Moolenaar * ; ']' MUST be escaped. 85331337658SMarcel Moolenaar */ 854d1a0d267SMarcel Moolenaar static int 855d1a0d267SMarcel Moolenaar xo_escape_sdparams (xo_buffer_t *xbp, int len, xo_xff_flags_t flags UNUSED) 85631337658SMarcel Moolenaar { 857d1a0d267SMarcel Moolenaar unsigned delta = 0; 858d1a0d267SMarcel Moolenaar char *cp, *ep, *ip; 85931337658SMarcel Moolenaar 860d1a0d267SMarcel Moolenaar for (cp = xbp->xb_curp, ep = cp + len; cp < ep; cp++) { 861d1a0d267SMarcel Moolenaar if (*cp == '\\' || *cp == '"' || *cp == ']') 862d1a0d267SMarcel Moolenaar delta += 1; 86331337658SMarcel Moolenaar } 86431337658SMarcel Moolenaar 865d1a0d267SMarcel Moolenaar if (delta == 0) /* Nothing to escape; bail */ 866d1a0d267SMarcel Moolenaar return len; 867788ca347SMarcel Moolenaar 868d1a0d267SMarcel Moolenaar if (!xo_buf_has_room(xbp, delta)) /* No room; bail, but don't append */ 869d1a0d267SMarcel Moolenaar return 0; 870788ca347SMarcel Moolenaar 871d1a0d267SMarcel Moolenaar ep = xbp->xb_curp; 872d1a0d267SMarcel Moolenaar cp = ep + len; 873d1a0d267SMarcel Moolenaar ip = cp + delta; 874d1a0d267SMarcel Moolenaar do { 875d1a0d267SMarcel Moolenaar cp -= 1; 876d1a0d267SMarcel Moolenaar ip -= 1; 877d1a0d267SMarcel Moolenaar 878d1a0d267SMarcel Moolenaar if (*cp == '\\' || *cp == '"' || *cp == ']') { 879d1a0d267SMarcel Moolenaar *ip-- = *cp; 880d1a0d267SMarcel Moolenaar *ip = '\\'; 881d1a0d267SMarcel Moolenaar } else { 882d1a0d267SMarcel Moolenaar *ip = *cp; 883d1a0d267SMarcel Moolenaar } 884d1a0d267SMarcel Moolenaar 885d1a0d267SMarcel Moolenaar } while (cp > ep && cp != ip); 886d1a0d267SMarcel Moolenaar 887d1a0d267SMarcel Moolenaar return len + delta; 888788ca347SMarcel Moolenaar } 889788ca347SMarcel Moolenaar 89031337658SMarcel Moolenaar static void 89131337658SMarcel Moolenaar xo_buf_escape (xo_handle_t *xop, xo_buffer_t *xbp, 89231337658SMarcel Moolenaar const char *str, int len, xo_xff_flags_t flags) 89331337658SMarcel Moolenaar { 89431337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, len)) 89531337658SMarcel Moolenaar return; 89631337658SMarcel Moolenaar 89731337658SMarcel Moolenaar memcpy(xbp->xb_curp, str, len); 89831337658SMarcel Moolenaar 899788ca347SMarcel Moolenaar switch (xo_style(xop)) { 90031337658SMarcel Moolenaar case XO_STYLE_XML: 90131337658SMarcel Moolenaar case XO_STYLE_HTML: 902d1a0d267SMarcel Moolenaar len = xo_escape_xml(xbp, len, flags); 90331337658SMarcel Moolenaar break; 90431337658SMarcel Moolenaar 90531337658SMarcel Moolenaar case XO_STYLE_JSON: 906d1a0d267SMarcel Moolenaar len = xo_escape_json(xbp, len, flags); 907d1a0d267SMarcel Moolenaar break; 908d1a0d267SMarcel Moolenaar 909d1a0d267SMarcel Moolenaar case XO_STYLE_SDPARAMS: 910d1a0d267SMarcel Moolenaar len = xo_escape_sdparams(xbp, len, flags); 91131337658SMarcel Moolenaar break; 91231337658SMarcel Moolenaar } 91331337658SMarcel Moolenaar 91431337658SMarcel Moolenaar xbp->xb_curp += len; 91531337658SMarcel Moolenaar } 91631337658SMarcel Moolenaar 91731337658SMarcel Moolenaar /* 91831337658SMarcel Moolenaar * Write the current contents of the data buffer using the handle's 91931337658SMarcel Moolenaar * xo_write function. 92031337658SMarcel Moolenaar */ 921545ddfbeSMarcel Moolenaar static int 92231337658SMarcel Moolenaar xo_write (xo_handle_t *xop) 92331337658SMarcel Moolenaar { 924545ddfbeSMarcel Moolenaar int rc = 0; 92531337658SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_data; 92631337658SMarcel Moolenaar 92731337658SMarcel Moolenaar if (xbp->xb_curp != xbp->xb_bufp) { 92831337658SMarcel Moolenaar xo_buf_append(xbp, "", 1); /* Append ending NUL */ 92931337658SMarcel Moolenaar xo_anchor_clear(xop); 930d1a0d267SMarcel Moolenaar if (xop->xo_write) 931545ddfbeSMarcel Moolenaar rc = xop->xo_write(xop->xo_opaque, xbp->xb_bufp); 93231337658SMarcel Moolenaar xbp->xb_curp = xbp->xb_bufp; 93331337658SMarcel Moolenaar } 93431337658SMarcel Moolenaar 93531337658SMarcel Moolenaar /* Turn off the flags that don't survive across writes */ 936d1a0d267SMarcel Moolenaar XOIF_CLEAR(xop, XOIF_UNITS_PENDING); 937545ddfbeSMarcel Moolenaar 938545ddfbeSMarcel Moolenaar return rc; 93931337658SMarcel Moolenaar } 94031337658SMarcel Moolenaar 94131337658SMarcel Moolenaar /* 94231337658SMarcel Moolenaar * Format arguments into our buffer. If a custom formatter has been set, 94331337658SMarcel Moolenaar * we use that to do the work; otherwise we vsnprintf(). 94431337658SMarcel Moolenaar */ 94531337658SMarcel Moolenaar static int 94631337658SMarcel Moolenaar xo_vsnprintf (xo_handle_t *xop, xo_buffer_t *xbp, const char *fmt, va_list vap) 94731337658SMarcel Moolenaar { 94831337658SMarcel Moolenaar va_list va_local; 94931337658SMarcel Moolenaar int rc; 95031337658SMarcel Moolenaar int left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp); 95131337658SMarcel Moolenaar 95231337658SMarcel Moolenaar va_copy(va_local, vap); 95331337658SMarcel Moolenaar 95431337658SMarcel Moolenaar if (xop->xo_formatter) 95531337658SMarcel Moolenaar rc = xop->xo_formatter(xop, xbp->xb_curp, left, fmt, va_local); 95631337658SMarcel Moolenaar else 95731337658SMarcel Moolenaar rc = vsnprintf(xbp->xb_curp, left, fmt, va_local); 95831337658SMarcel Moolenaar 959788ca347SMarcel Moolenaar if (rc >= left) { 960c600d307SMarcel Moolenaar if (!xo_buf_has_room(xbp, rc)) { 961c600d307SMarcel Moolenaar va_end(va_local); 96231337658SMarcel Moolenaar return -1; 963c600d307SMarcel Moolenaar } 96431337658SMarcel Moolenaar 96531337658SMarcel Moolenaar /* 96631337658SMarcel Moolenaar * After we call vsnprintf(), the stage of vap is not defined. 96731337658SMarcel Moolenaar * We need to copy it before we pass. Then we have to do our 96831337658SMarcel Moolenaar * own logic below to move it along. This is because the 969788ca347SMarcel Moolenaar * implementation can have va_list be a pointer (bsd) or a 97031337658SMarcel Moolenaar * structure (macosx) or anything in between. 97131337658SMarcel Moolenaar */ 97231337658SMarcel Moolenaar 97331337658SMarcel Moolenaar va_end(va_local); /* Reset vap to the start */ 97431337658SMarcel Moolenaar va_copy(va_local, vap); 97531337658SMarcel Moolenaar 97631337658SMarcel Moolenaar left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp); 97731337658SMarcel Moolenaar if (xop->xo_formatter) 978788ca347SMarcel Moolenaar rc = xop->xo_formatter(xop, xbp->xb_curp, left, fmt, va_local); 97931337658SMarcel Moolenaar else 98031337658SMarcel Moolenaar rc = vsnprintf(xbp->xb_curp, left, fmt, va_local); 98131337658SMarcel Moolenaar } 98231337658SMarcel Moolenaar va_end(va_local); 98331337658SMarcel Moolenaar 98431337658SMarcel Moolenaar return rc; 98531337658SMarcel Moolenaar } 98631337658SMarcel Moolenaar 98731337658SMarcel Moolenaar /* 98831337658SMarcel Moolenaar * Print some data thru the handle. 98931337658SMarcel Moolenaar */ 99031337658SMarcel Moolenaar static int 99131337658SMarcel Moolenaar xo_printf_v (xo_handle_t *xop, const char *fmt, va_list vap) 99231337658SMarcel Moolenaar { 99331337658SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_data; 99431337658SMarcel Moolenaar int left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp); 99531337658SMarcel Moolenaar int rc; 99631337658SMarcel Moolenaar va_list va_local; 99731337658SMarcel Moolenaar 99831337658SMarcel Moolenaar va_copy(va_local, vap); 99931337658SMarcel Moolenaar 100031337658SMarcel Moolenaar rc = vsnprintf(xbp->xb_curp, left, fmt, va_local); 100131337658SMarcel Moolenaar 1002d1a0d267SMarcel Moolenaar if (rc >= left) { 1003c600d307SMarcel Moolenaar if (!xo_buf_has_room(xbp, rc)) { 1004c600d307SMarcel Moolenaar va_end(va_local); 100531337658SMarcel Moolenaar return -1; 1006c600d307SMarcel Moolenaar } 100731337658SMarcel Moolenaar 100831337658SMarcel Moolenaar va_end(va_local); /* Reset vap to the start */ 100931337658SMarcel Moolenaar va_copy(va_local, vap); 101031337658SMarcel Moolenaar 101131337658SMarcel Moolenaar left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp); 101231337658SMarcel Moolenaar rc = vsnprintf(xbp->xb_curp, left, fmt, va_local); 101331337658SMarcel Moolenaar } 101431337658SMarcel Moolenaar 101531337658SMarcel Moolenaar va_end(va_local); 101631337658SMarcel Moolenaar 101731337658SMarcel Moolenaar if (rc > 0) 101831337658SMarcel Moolenaar xbp->xb_curp += rc; 101931337658SMarcel Moolenaar 102031337658SMarcel Moolenaar return rc; 102131337658SMarcel Moolenaar } 102231337658SMarcel Moolenaar 102331337658SMarcel Moolenaar static int 102431337658SMarcel Moolenaar xo_printf (xo_handle_t *xop, const char *fmt, ...) 102531337658SMarcel Moolenaar { 102631337658SMarcel Moolenaar int rc; 102731337658SMarcel Moolenaar va_list vap; 102831337658SMarcel Moolenaar 102931337658SMarcel Moolenaar va_start(vap, fmt); 103031337658SMarcel Moolenaar 103131337658SMarcel Moolenaar rc = xo_printf_v(xop, fmt, vap); 103231337658SMarcel Moolenaar 103331337658SMarcel Moolenaar va_end(vap); 103431337658SMarcel Moolenaar return rc; 103531337658SMarcel Moolenaar } 103631337658SMarcel Moolenaar 103731337658SMarcel Moolenaar /* 103831337658SMarcel Moolenaar * These next few function are make The Essential UTF-8 Ginsu Knife. 103931337658SMarcel Moolenaar * Identify an input and output character, and convert it. 104031337658SMarcel Moolenaar */ 104131337658SMarcel Moolenaar static int xo_utf8_bits[7] = { 0, 0x7f, 0x1f, 0x0f, 0x07, 0x03, 0x01 }; 104231337658SMarcel Moolenaar 104331337658SMarcel Moolenaar static int 104431337658SMarcel Moolenaar xo_is_utf8 (char ch) 104531337658SMarcel Moolenaar { 104631337658SMarcel Moolenaar return (ch & 0x80); 104731337658SMarcel Moolenaar } 104831337658SMarcel Moolenaar 104931337658SMarcel Moolenaar static int 105031337658SMarcel Moolenaar xo_utf8_to_wc_len (const char *buf) 105131337658SMarcel Moolenaar { 105231337658SMarcel Moolenaar unsigned b = (unsigned char) *buf; 105331337658SMarcel Moolenaar int len; 105431337658SMarcel Moolenaar 105531337658SMarcel Moolenaar if ((b & 0x80) == 0x0) 105631337658SMarcel Moolenaar len = 1; 105731337658SMarcel Moolenaar else if ((b & 0xe0) == 0xc0) 105831337658SMarcel Moolenaar len = 2; 105931337658SMarcel Moolenaar else if ((b & 0xf0) == 0xe0) 106031337658SMarcel Moolenaar len = 3; 106131337658SMarcel Moolenaar else if ((b & 0xf8) == 0xf0) 106231337658SMarcel Moolenaar len = 4; 106331337658SMarcel Moolenaar else if ((b & 0xfc) == 0xf8) 106431337658SMarcel Moolenaar len = 5; 106531337658SMarcel Moolenaar else if ((b & 0xfe) == 0xfc) 106631337658SMarcel Moolenaar len = 6; 106731337658SMarcel Moolenaar else 106831337658SMarcel Moolenaar len = -1; 106931337658SMarcel Moolenaar 107031337658SMarcel Moolenaar return len; 107131337658SMarcel Moolenaar } 107231337658SMarcel Moolenaar 107331337658SMarcel Moolenaar static int 107431337658SMarcel Moolenaar xo_buf_utf8_len (xo_handle_t *xop, const char *buf, int bufsiz) 107531337658SMarcel Moolenaar { 107631337658SMarcel Moolenaar 107731337658SMarcel Moolenaar unsigned b = (unsigned char) *buf; 107831337658SMarcel Moolenaar int len, i; 107931337658SMarcel Moolenaar 108031337658SMarcel Moolenaar len = xo_utf8_to_wc_len(buf); 108131337658SMarcel Moolenaar if (len == -1) { 108231337658SMarcel Moolenaar xo_failure(xop, "invalid UTF-8 data: %02hhx", b); 108331337658SMarcel Moolenaar return -1; 108431337658SMarcel Moolenaar } 108531337658SMarcel Moolenaar 108631337658SMarcel Moolenaar if (len > bufsiz) { 108731337658SMarcel Moolenaar xo_failure(xop, "invalid UTF-8 data (short): %02hhx (%d/%d)", 108831337658SMarcel Moolenaar b, len, bufsiz); 108931337658SMarcel Moolenaar return -1; 109031337658SMarcel Moolenaar } 109131337658SMarcel Moolenaar 109231337658SMarcel Moolenaar for (i = 2; i < len; i++) { 109331337658SMarcel Moolenaar b = (unsigned char ) buf[i]; 109431337658SMarcel Moolenaar if ((b & 0xc0) != 0x80) { 109531337658SMarcel Moolenaar xo_failure(xop, "invalid UTF-8 data (byte %d): %x", i, b); 109631337658SMarcel Moolenaar return -1; 109731337658SMarcel Moolenaar } 109831337658SMarcel Moolenaar } 109931337658SMarcel Moolenaar 110031337658SMarcel Moolenaar return len; 110131337658SMarcel Moolenaar } 110231337658SMarcel Moolenaar 110331337658SMarcel Moolenaar /* 110431337658SMarcel Moolenaar * Build a wide character from the input buffer; the number of 110531337658SMarcel Moolenaar * bits we pull off the first character is dependent on the length, 110631337658SMarcel Moolenaar * but we put 6 bits off all other bytes. 110731337658SMarcel Moolenaar */ 110831337658SMarcel Moolenaar static wchar_t 110931337658SMarcel Moolenaar xo_utf8_char (const char *buf, int len) 111031337658SMarcel Moolenaar { 111131337658SMarcel Moolenaar int i; 111231337658SMarcel Moolenaar wchar_t wc; 111331337658SMarcel Moolenaar const unsigned char *cp = (const unsigned char *) buf; 111431337658SMarcel Moolenaar 111531337658SMarcel Moolenaar wc = *cp & xo_utf8_bits[len]; 111631337658SMarcel Moolenaar for (i = 1; i < len; i++) { 111731337658SMarcel Moolenaar wc <<= 6; 111831337658SMarcel Moolenaar wc |= cp[i] & 0x3f; 111931337658SMarcel Moolenaar if ((cp[i] & 0xc0) != 0x80) 112031337658SMarcel Moolenaar return (wchar_t) -1; 112131337658SMarcel Moolenaar } 112231337658SMarcel Moolenaar 112331337658SMarcel Moolenaar return wc; 112431337658SMarcel Moolenaar } 112531337658SMarcel Moolenaar 112631337658SMarcel Moolenaar /* 112731337658SMarcel Moolenaar * Determine the number of bytes needed to encode a wide character. 112831337658SMarcel Moolenaar */ 112931337658SMarcel Moolenaar static int 113031337658SMarcel Moolenaar xo_utf8_emit_len (wchar_t wc) 113131337658SMarcel Moolenaar { 113231337658SMarcel Moolenaar int len; 113331337658SMarcel Moolenaar 113431337658SMarcel Moolenaar if ((wc & ((1<<7) - 1)) == wc) /* Simple case */ 113531337658SMarcel Moolenaar len = 1; 113631337658SMarcel Moolenaar else if ((wc & ((1<<11) - 1)) == wc) 113731337658SMarcel Moolenaar len = 2; 113831337658SMarcel Moolenaar else if ((wc & ((1<<16) - 1)) == wc) 113931337658SMarcel Moolenaar len = 3; 114031337658SMarcel Moolenaar else if ((wc & ((1<<21) - 1)) == wc) 114131337658SMarcel Moolenaar len = 4; 114231337658SMarcel Moolenaar else if ((wc & ((1<<26) - 1)) == wc) 114331337658SMarcel Moolenaar len = 5; 114431337658SMarcel Moolenaar else 114531337658SMarcel Moolenaar len = 6; 114631337658SMarcel Moolenaar 114731337658SMarcel Moolenaar return len; 114831337658SMarcel Moolenaar } 114931337658SMarcel Moolenaar 115031337658SMarcel Moolenaar static void 115131337658SMarcel Moolenaar xo_utf8_emit_char (char *buf, int len, wchar_t wc) 115231337658SMarcel Moolenaar { 115331337658SMarcel Moolenaar int i; 115431337658SMarcel Moolenaar 115531337658SMarcel Moolenaar if (len == 1) { /* Simple case */ 115631337658SMarcel Moolenaar buf[0] = wc & 0x7f; 115731337658SMarcel Moolenaar return; 115831337658SMarcel Moolenaar } 115931337658SMarcel Moolenaar 116031337658SMarcel Moolenaar for (i = len - 1; i >= 0; i--) { 116131337658SMarcel Moolenaar buf[i] = 0x80 | (wc & 0x3f); 116231337658SMarcel Moolenaar wc >>= 6; 116331337658SMarcel Moolenaar } 116431337658SMarcel Moolenaar 116531337658SMarcel Moolenaar buf[0] &= xo_utf8_bits[len]; 116631337658SMarcel Moolenaar buf[0] |= ~xo_utf8_bits[len] << 1; 116731337658SMarcel Moolenaar } 116831337658SMarcel Moolenaar 116931337658SMarcel Moolenaar static int 117031337658SMarcel Moolenaar xo_buf_append_locale_from_utf8 (xo_handle_t *xop, xo_buffer_t *xbp, 117131337658SMarcel Moolenaar const char *ibuf, int ilen) 117231337658SMarcel Moolenaar { 117331337658SMarcel Moolenaar wchar_t wc; 117431337658SMarcel Moolenaar int len; 117531337658SMarcel Moolenaar 117631337658SMarcel Moolenaar /* 117731337658SMarcel Moolenaar * Build our wide character from the input buffer; the number of 117831337658SMarcel Moolenaar * bits we pull off the first character is dependent on the length, 117931337658SMarcel Moolenaar * but we put 6 bits off all other bytes. 118031337658SMarcel Moolenaar */ 118131337658SMarcel Moolenaar wc = xo_utf8_char(ibuf, ilen); 118231337658SMarcel Moolenaar if (wc == (wchar_t) -1) { 118331337658SMarcel Moolenaar xo_failure(xop, "invalid utf-8 byte sequence"); 118431337658SMarcel Moolenaar return 0; 118531337658SMarcel Moolenaar } 118631337658SMarcel Moolenaar 1187d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_NO_LOCALE)) { 118831337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, ilen)) 118931337658SMarcel Moolenaar return 0; 119031337658SMarcel Moolenaar 119131337658SMarcel Moolenaar memcpy(xbp->xb_curp, ibuf, ilen); 119231337658SMarcel Moolenaar xbp->xb_curp += ilen; 119331337658SMarcel Moolenaar 119431337658SMarcel Moolenaar } else { 119531337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, MB_LEN_MAX + 1)) 119631337658SMarcel Moolenaar return 0; 119731337658SMarcel Moolenaar 119831337658SMarcel Moolenaar bzero(&xop->xo_mbstate, sizeof(xop->xo_mbstate)); 119931337658SMarcel Moolenaar len = wcrtomb(xbp->xb_curp, wc, &xop->xo_mbstate); 120031337658SMarcel Moolenaar 120131337658SMarcel Moolenaar if (len <= 0) { 120231337658SMarcel Moolenaar xo_failure(xop, "could not convert wide char: %lx", 120331337658SMarcel Moolenaar (unsigned long) wc); 120431337658SMarcel Moolenaar return 0; 120531337658SMarcel Moolenaar } 120631337658SMarcel Moolenaar xbp->xb_curp += len; 120731337658SMarcel Moolenaar } 120831337658SMarcel Moolenaar 1209d1a0d267SMarcel Moolenaar return xo_wcwidth(wc); 121031337658SMarcel Moolenaar } 121131337658SMarcel Moolenaar 121231337658SMarcel Moolenaar static void 121331337658SMarcel Moolenaar xo_buf_append_locale (xo_handle_t *xop, xo_buffer_t *xbp, 121431337658SMarcel Moolenaar const char *cp, int len) 121531337658SMarcel Moolenaar { 121631337658SMarcel Moolenaar const char *sp = cp, *ep = cp + len; 121731337658SMarcel Moolenaar unsigned save_off = xbp->xb_bufp - xbp->xb_curp; 121831337658SMarcel Moolenaar int slen; 121931337658SMarcel Moolenaar int cols = 0; 122031337658SMarcel Moolenaar 122131337658SMarcel Moolenaar for ( ; cp < ep; cp++) { 122231337658SMarcel Moolenaar if (!xo_is_utf8(*cp)) { 122331337658SMarcel Moolenaar cols += 1; 122431337658SMarcel Moolenaar continue; 122531337658SMarcel Moolenaar } 122631337658SMarcel Moolenaar 122731337658SMarcel Moolenaar /* 122831337658SMarcel Moolenaar * We're looking at a non-ascii UTF-8 character. 122931337658SMarcel Moolenaar * First we copy the previous data. 123031337658SMarcel Moolenaar * Then we need find the length and validate it. 123131337658SMarcel Moolenaar * Then we turn it into a wide string. 123231337658SMarcel Moolenaar * Then we turn it into a localized string. 123331337658SMarcel Moolenaar * Then we repeat. Isn't i18n fun? 123431337658SMarcel Moolenaar */ 123531337658SMarcel Moolenaar if (sp != cp) 123631337658SMarcel Moolenaar xo_buf_append(xbp, sp, cp - sp); /* Append previous data */ 123731337658SMarcel Moolenaar 123831337658SMarcel Moolenaar slen = xo_buf_utf8_len(xop, cp, ep - cp); 123931337658SMarcel Moolenaar if (slen <= 0) { 124031337658SMarcel Moolenaar /* Bad data; back it all out */ 124131337658SMarcel Moolenaar xbp->xb_curp = xbp->xb_bufp + save_off; 124231337658SMarcel Moolenaar return; 124331337658SMarcel Moolenaar } 124431337658SMarcel Moolenaar 124531337658SMarcel Moolenaar cols += xo_buf_append_locale_from_utf8(xop, xbp, cp, slen); 124631337658SMarcel Moolenaar 124731337658SMarcel Moolenaar /* Next time thru, we'll start at the next character */ 124831337658SMarcel Moolenaar cp += slen - 1; 124931337658SMarcel Moolenaar sp = cp + 1; 125031337658SMarcel Moolenaar } 125131337658SMarcel Moolenaar 125231337658SMarcel Moolenaar /* Update column values */ 1253d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_COLUMNS)) 125431337658SMarcel Moolenaar xop->xo_columns += cols; 1255d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_ANCHOR)) 125631337658SMarcel Moolenaar xop->xo_anchor_columns += cols; 125731337658SMarcel Moolenaar 125831337658SMarcel Moolenaar /* Before we fall into the basic logic below, we need reset len */ 125931337658SMarcel Moolenaar len = ep - sp; 126031337658SMarcel Moolenaar if (len != 0) /* Append trailing data */ 126131337658SMarcel Moolenaar xo_buf_append(xbp, sp, len); 126231337658SMarcel Moolenaar } 126331337658SMarcel Moolenaar 126431337658SMarcel Moolenaar /* 1265d1a0d267SMarcel Moolenaar * Append the given string to the given buffer, without escaping or 1266d1a0d267SMarcel Moolenaar * character set conversion. This is the straight copy to the data 1267d1a0d267SMarcel Moolenaar * buffer with no fanciness. 126831337658SMarcel Moolenaar */ 126931337658SMarcel Moolenaar static void 127031337658SMarcel Moolenaar xo_data_append (xo_handle_t *xop, const char *str, int len) 127131337658SMarcel Moolenaar { 127231337658SMarcel Moolenaar xo_buf_append(&xop->xo_data, str, len); 127331337658SMarcel Moolenaar } 127431337658SMarcel Moolenaar 127531337658SMarcel Moolenaar /* 127631337658SMarcel Moolenaar * Append the given string to the given buffer 127731337658SMarcel Moolenaar */ 127831337658SMarcel Moolenaar static void 127931337658SMarcel Moolenaar xo_data_escape (xo_handle_t *xop, const char *str, int len) 128031337658SMarcel Moolenaar { 128131337658SMarcel Moolenaar xo_buf_escape(xop, &xop->xo_data, str, len, 0); 128231337658SMarcel Moolenaar } 128331337658SMarcel Moolenaar 128431337658SMarcel Moolenaar /* 128531337658SMarcel Moolenaar * Generate a warning. Normally, this is a text message written to 128631337658SMarcel Moolenaar * standard error. If the XOF_WARN_XML flag is set, then we generate 128731337658SMarcel Moolenaar * XMLified content on standard output. 128831337658SMarcel Moolenaar */ 128931337658SMarcel Moolenaar static void 129031337658SMarcel Moolenaar xo_warn_hcv (xo_handle_t *xop, int code, int check_warn, 129131337658SMarcel Moolenaar const char *fmt, va_list vap) 129231337658SMarcel Moolenaar { 129331337658SMarcel Moolenaar xop = xo_default(xop); 1294d1a0d267SMarcel Moolenaar if (check_warn && !XOF_ISSET(xop, XOF_WARN)) 129531337658SMarcel Moolenaar return; 129631337658SMarcel Moolenaar 129731337658SMarcel Moolenaar if (fmt == NULL) 129831337658SMarcel Moolenaar return; 129931337658SMarcel Moolenaar 130031337658SMarcel Moolenaar int len = strlen(fmt); 130131337658SMarcel Moolenaar int plen = xo_program ? strlen(xo_program) : 0; 1302545ddfbeSMarcel Moolenaar char *newfmt = alloca(len + 1 + plen + 2); /* NUL, and ": " */ 130331337658SMarcel Moolenaar 130431337658SMarcel Moolenaar if (plen) { 130531337658SMarcel Moolenaar memcpy(newfmt, xo_program, plen); 130631337658SMarcel Moolenaar newfmt[plen++] = ':'; 130731337658SMarcel Moolenaar newfmt[plen++] = ' '; 130831337658SMarcel Moolenaar } 130931337658SMarcel Moolenaar memcpy(newfmt + plen, fmt, len); 131031337658SMarcel Moolenaar newfmt[len + plen] = '\0'; 131131337658SMarcel Moolenaar 1312d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_WARN_XML)) { 131331337658SMarcel Moolenaar static char err_open[] = "<error>"; 131431337658SMarcel Moolenaar static char err_close[] = "</error>"; 131531337658SMarcel Moolenaar static char msg_open[] = "<message>"; 131631337658SMarcel Moolenaar static char msg_close[] = "</message>"; 131731337658SMarcel Moolenaar 131831337658SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_data; 131931337658SMarcel Moolenaar 132031337658SMarcel Moolenaar xo_buf_append(xbp, err_open, sizeof(err_open) - 1); 132131337658SMarcel Moolenaar xo_buf_append(xbp, msg_open, sizeof(msg_open) - 1); 132231337658SMarcel Moolenaar 132331337658SMarcel Moolenaar va_list va_local; 132431337658SMarcel Moolenaar va_copy(va_local, vap); 132531337658SMarcel Moolenaar 132631337658SMarcel Moolenaar int left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp); 132731337658SMarcel Moolenaar int rc = vsnprintf(xbp->xb_curp, left, newfmt, vap); 1328d1a0d267SMarcel Moolenaar if (rc >= left) { 1329c600d307SMarcel Moolenaar if (!xo_buf_has_room(xbp, rc)) { 1330c600d307SMarcel Moolenaar va_end(va_local); 133131337658SMarcel Moolenaar return; 1332c600d307SMarcel Moolenaar } 133331337658SMarcel Moolenaar 133431337658SMarcel Moolenaar va_end(vap); /* Reset vap to the start */ 133531337658SMarcel Moolenaar va_copy(vap, va_local); 133631337658SMarcel Moolenaar 133731337658SMarcel Moolenaar left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp); 133831337658SMarcel Moolenaar rc = vsnprintf(xbp->xb_curp, left, fmt, vap); 133931337658SMarcel Moolenaar } 134031337658SMarcel Moolenaar va_end(va_local); 134131337658SMarcel Moolenaar 134231337658SMarcel Moolenaar rc = xo_escape_xml(xbp, rc, 1); 134331337658SMarcel Moolenaar xbp->xb_curp += rc; 134431337658SMarcel Moolenaar 134531337658SMarcel Moolenaar xo_buf_append(xbp, msg_close, sizeof(msg_close) - 1); 134631337658SMarcel Moolenaar xo_buf_append(xbp, err_close, sizeof(err_close) - 1); 134731337658SMarcel Moolenaar 1348545ddfbeSMarcel Moolenaar if (code >= 0) { 134931337658SMarcel Moolenaar const char *msg = strerror(code); 135031337658SMarcel Moolenaar if (msg) { 135131337658SMarcel Moolenaar xo_buf_append(xbp, ": ", 2); 135231337658SMarcel Moolenaar xo_buf_append(xbp, msg, strlen(msg)); 135331337658SMarcel Moolenaar } 135431337658SMarcel Moolenaar } 135531337658SMarcel Moolenaar 1356d1a0d267SMarcel Moolenaar xo_buf_append(xbp, "\n", 1); /* Append newline and NUL to string */ 1357545ddfbeSMarcel Moolenaar (void) xo_write(xop); 135831337658SMarcel Moolenaar 135931337658SMarcel Moolenaar } else { 136031337658SMarcel Moolenaar vfprintf(stderr, newfmt, vap); 1361545ddfbeSMarcel Moolenaar if (code >= 0) { 1362545ddfbeSMarcel Moolenaar const char *msg = strerror(code); 1363545ddfbeSMarcel Moolenaar if (msg) 1364545ddfbeSMarcel Moolenaar fprintf(stderr, ": %s", msg); 1365545ddfbeSMarcel Moolenaar } 1366545ddfbeSMarcel Moolenaar fprintf(stderr, "\n"); 136731337658SMarcel Moolenaar } 136831337658SMarcel Moolenaar } 136931337658SMarcel Moolenaar 137031337658SMarcel Moolenaar void 137131337658SMarcel Moolenaar xo_warn_hc (xo_handle_t *xop, int code, const char *fmt, ...) 137231337658SMarcel Moolenaar { 137331337658SMarcel Moolenaar va_list vap; 137431337658SMarcel Moolenaar 137531337658SMarcel Moolenaar va_start(vap, fmt); 137631337658SMarcel Moolenaar xo_warn_hcv(xop, code, 0, fmt, vap); 137731337658SMarcel Moolenaar va_end(vap); 137831337658SMarcel Moolenaar } 137931337658SMarcel Moolenaar 138031337658SMarcel Moolenaar void 138131337658SMarcel Moolenaar xo_warn_c (int code, const char *fmt, ...) 138231337658SMarcel Moolenaar { 138331337658SMarcel Moolenaar va_list vap; 138431337658SMarcel Moolenaar 138531337658SMarcel Moolenaar va_start(vap, fmt); 1386545ddfbeSMarcel Moolenaar xo_warn_hcv(NULL, code, 0, fmt, vap); 138731337658SMarcel Moolenaar va_end(vap); 138831337658SMarcel Moolenaar } 138931337658SMarcel Moolenaar 139031337658SMarcel Moolenaar void 139131337658SMarcel Moolenaar xo_warn (const char *fmt, ...) 139231337658SMarcel Moolenaar { 139331337658SMarcel Moolenaar int code = errno; 139431337658SMarcel Moolenaar va_list vap; 139531337658SMarcel Moolenaar 139631337658SMarcel Moolenaar va_start(vap, fmt); 139731337658SMarcel Moolenaar xo_warn_hcv(NULL, code, 0, fmt, vap); 139831337658SMarcel Moolenaar va_end(vap); 139931337658SMarcel Moolenaar } 140031337658SMarcel Moolenaar 140131337658SMarcel Moolenaar void 140231337658SMarcel Moolenaar xo_warnx (const char *fmt, ...) 140331337658SMarcel Moolenaar { 140431337658SMarcel Moolenaar va_list vap; 140531337658SMarcel Moolenaar 140631337658SMarcel Moolenaar va_start(vap, fmt); 140731337658SMarcel Moolenaar xo_warn_hcv(NULL, -1, 0, fmt, vap); 140831337658SMarcel Moolenaar va_end(vap); 140931337658SMarcel Moolenaar } 141031337658SMarcel Moolenaar 141131337658SMarcel Moolenaar void 141231337658SMarcel Moolenaar xo_err (int eval, const char *fmt, ...) 141331337658SMarcel Moolenaar { 141431337658SMarcel Moolenaar int code = errno; 141531337658SMarcel Moolenaar va_list vap; 141631337658SMarcel Moolenaar 141731337658SMarcel Moolenaar va_start(vap, fmt); 141831337658SMarcel Moolenaar xo_warn_hcv(NULL, code, 0, fmt, vap); 141931337658SMarcel Moolenaar va_end(vap); 142031337658SMarcel Moolenaar xo_finish(); 142131337658SMarcel Moolenaar exit(eval); 142231337658SMarcel Moolenaar } 142331337658SMarcel Moolenaar 142431337658SMarcel Moolenaar void 142531337658SMarcel Moolenaar xo_errx (int eval, const char *fmt, ...) 142631337658SMarcel Moolenaar { 142731337658SMarcel Moolenaar va_list vap; 142831337658SMarcel Moolenaar 142931337658SMarcel Moolenaar va_start(vap, fmt); 143031337658SMarcel Moolenaar xo_warn_hcv(NULL, -1, 0, fmt, vap); 143131337658SMarcel Moolenaar va_end(vap); 143231337658SMarcel Moolenaar xo_finish(); 143331337658SMarcel Moolenaar exit(eval); 143431337658SMarcel Moolenaar } 143531337658SMarcel Moolenaar 143631337658SMarcel Moolenaar void 143731337658SMarcel Moolenaar xo_errc (int eval, int code, const char *fmt, ...) 143831337658SMarcel Moolenaar { 143931337658SMarcel Moolenaar va_list vap; 144031337658SMarcel Moolenaar 144131337658SMarcel Moolenaar va_start(vap, fmt); 144231337658SMarcel Moolenaar xo_warn_hcv(NULL, code, 0, fmt, vap); 144331337658SMarcel Moolenaar va_end(vap); 144431337658SMarcel Moolenaar xo_finish(); 144531337658SMarcel Moolenaar exit(eval); 144631337658SMarcel Moolenaar } 144731337658SMarcel Moolenaar 144831337658SMarcel Moolenaar /* 144931337658SMarcel Moolenaar * Generate a warning. Normally, this is a text message written to 145031337658SMarcel Moolenaar * standard error. If the XOF_WARN_XML flag is set, then we generate 145131337658SMarcel Moolenaar * XMLified content on standard output. 145231337658SMarcel Moolenaar */ 145331337658SMarcel Moolenaar void 145431337658SMarcel Moolenaar xo_message_hcv (xo_handle_t *xop, int code, const char *fmt, va_list vap) 145531337658SMarcel Moolenaar { 145631337658SMarcel Moolenaar static char msg_open[] = "<message>"; 145731337658SMarcel Moolenaar static char msg_close[] = "</message>"; 145831337658SMarcel Moolenaar xo_buffer_t *xbp; 145931337658SMarcel Moolenaar int rc; 146031337658SMarcel Moolenaar va_list va_local; 146131337658SMarcel Moolenaar 146231337658SMarcel Moolenaar xop = xo_default(xop); 146331337658SMarcel Moolenaar 146431337658SMarcel Moolenaar if (fmt == NULL || *fmt == '\0') 146531337658SMarcel Moolenaar return; 146631337658SMarcel Moolenaar 146731337658SMarcel Moolenaar int need_nl = (fmt[strlen(fmt) - 1] != '\n'); 146831337658SMarcel Moolenaar 1469788ca347SMarcel Moolenaar switch (xo_style(xop)) { 147031337658SMarcel Moolenaar case XO_STYLE_XML: 147131337658SMarcel Moolenaar xbp = &xop->xo_data; 1472d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_PRETTY)) 147331337658SMarcel Moolenaar xo_buf_indent(xop, xop->xo_indent_by); 147431337658SMarcel Moolenaar xo_buf_append(xbp, msg_open, sizeof(msg_open) - 1); 147531337658SMarcel Moolenaar 147631337658SMarcel Moolenaar va_copy(va_local, vap); 147731337658SMarcel Moolenaar 147831337658SMarcel Moolenaar int left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp); 147931337658SMarcel Moolenaar rc = vsnprintf(xbp->xb_curp, left, fmt, vap); 1480d1a0d267SMarcel Moolenaar if (rc >= left) { 1481c600d307SMarcel Moolenaar if (!xo_buf_has_room(xbp, rc)) { 1482c600d307SMarcel Moolenaar va_end(va_local); 148331337658SMarcel Moolenaar return; 1484c600d307SMarcel Moolenaar } 148531337658SMarcel Moolenaar 148631337658SMarcel Moolenaar va_end(vap); /* Reset vap to the start */ 148731337658SMarcel Moolenaar va_copy(vap, va_local); 148831337658SMarcel Moolenaar 148931337658SMarcel Moolenaar left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp); 149031337658SMarcel Moolenaar rc = vsnprintf(xbp->xb_curp, left, fmt, vap); 149131337658SMarcel Moolenaar } 149231337658SMarcel Moolenaar va_end(va_local); 149331337658SMarcel Moolenaar 1494d1a0d267SMarcel Moolenaar rc = xo_escape_xml(xbp, rc, 0); 149531337658SMarcel Moolenaar xbp->xb_curp += rc; 149631337658SMarcel Moolenaar 149731337658SMarcel Moolenaar if (need_nl && code > 0) { 149831337658SMarcel Moolenaar const char *msg = strerror(code); 149931337658SMarcel Moolenaar if (msg) { 150031337658SMarcel Moolenaar xo_buf_append(xbp, ": ", 2); 150131337658SMarcel Moolenaar xo_buf_append(xbp, msg, strlen(msg)); 150231337658SMarcel Moolenaar } 150331337658SMarcel Moolenaar } 150431337658SMarcel Moolenaar 150531337658SMarcel Moolenaar if (need_nl) 1506d1a0d267SMarcel Moolenaar xo_buf_append(xbp, "\n", 1); /* Append newline and NUL to string */ 1507d1a0d267SMarcel Moolenaar 1508d1a0d267SMarcel Moolenaar xo_buf_append(xbp, msg_close, sizeof(msg_close) - 1); 1509d1a0d267SMarcel Moolenaar 1510d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_PRETTY)) 1511d1a0d267SMarcel Moolenaar xo_buf_append(xbp, "\n", 1); /* Append newline and NUL to string */ 1512d1a0d267SMarcel Moolenaar 1513545ddfbeSMarcel Moolenaar (void) xo_write(xop); 151431337658SMarcel Moolenaar break; 151531337658SMarcel Moolenaar 151631337658SMarcel Moolenaar case XO_STYLE_HTML: 151731337658SMarcel Moolenaar { 151831337658SMarcel Moolenaar char buf[BUFSIZ], *bp = buf, *cp; 151931337658SMarcel Moolenaar int bufsiz = sizeof(buf); 152031337658SMarcel Moolenaar int rc2; 152131337658SMarcel Moolenaar 152231337658SMarcel Moolenaar va_copy(va_local, vap); 152331337658SMarcel Moolenaar 1524c600d307SMarcel Moolenaar rc = vsnprintf(bp, bufsiz, fmt, va_local); 152531337658SMarcel Moolenaar if (rc > bufsiz) { 152631337658SMarcel Moolenaar bufsiz = rc + BUFSIZ; 152731337658SMarcel Moolenaar bp = alloca(bufsiz); 152831337658SMarcel Moolenaar va_end(va_local); 152931337658SMarcel Moolenaar va_copy(va_local, vap); 1530c600d307SMarcel Moolenaar rc = vsnprintf(bp, bufsiz, fmt, va_local); 153131337658SMarcel Moolenaar } 1532c600d307SMarcel Moolenaar va_end(va_local); 153331337658SMarcel Moolenaar cp = bp + rc; 153431337658SMarcel Moolenaar 153531337658SMarcel Moolenaar if (need_nl) { 153631337658SMarcel Moolenaar rc2 = snprintf(cp, bufsiz - rc, "%s%s\n", 153731337658SMarcel Moolenaar (code > 0) ? ": " : "", 153831337658SMarcel Moolenaar (code > 0) ? strerror(code) : ""); 153931337658SMarcel Moolenaar if (rc2 > 0) 154031337658SMarcel Moolenaar rc += rc2; 154131337658SMarcel Moolenaar } 154231337658SMarcel Moolenaar 154331337658SMarcel Moolenaar xo_buf_append_div(xop, "message", 0, NULL, 0, bp, rc, NULL, 0); 154431337658SMarcel Moolenaar } 154531337658SMarcel Moolenaar break; 154631337658SMarcel Moolenaar 154731337658SMarcel Moolenaar case XO_STYLE_JSON: 1548d1a0d267SMarcel Moolenaar case XO_STYLE_SDPARAMS: 1549d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER: 1550d1a0d267SMarcel Moolenaar /* No means of representing messages */ 1551d1a0d267SMarcel Moolenaar return; 155231337658SMarcel Moolenaar 155331337658SMarcel Moolenaar case XO_STYLE_TEXT: 155431337658SMarcel Moolenaar rc = xo_printf_v(xop, fmt, vap); 155531337658SMarcel Moolenaar /* 155631337658SMarcel Moolenaar * XXX need to handle UTF-8 widths 155731337658SMarcel Moolenaar */ 155831337658SMarcel Moolenaar if (rc > 0) { 1559d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_COLUMNS)) 156031337658SMarcel Moolenaar xop->xo_columns += rc; 1561d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_ANCHOR)) 156231337658SMarcel Moolenaar xop->xo_anchor_columns += rc; 156331337658SMarcel Moolenaar } 156431337658SMarcel Moolenaar 156531337658SMarcel Moolenaar if (need_nl && code > 0) { 156631337658SMarcel Moolenaar const char *msg = strerror(code); 156731337658SMarcel Moolenaar if (msg) { 156831337658SMarcel Moolenaar xo_printf(xop, ": %s", msg); 156931337658SMarcel Moolenaar } 157031337658SMarcel Moolenaar } 157131337658SMarcel Moolenaar if (need_nl) 157231337658SMarcel Moolenaar xo_printf(xop, "\n"); 157331337658SMarcel Moolenaar 157431337658SMarcel Moolenaar break; 157531337658SMarcel Moolenaar } 157631337658SMarcel Moolenaar 1577545ddfbeSMarcel Moolenaar (void) xo_flush_h(xop); 157831337658SMarcel Moolenaar } 157931337658SMarcel Moolenaar 158031337658SMarcel Moolenaar void 158131337658SMarcel Moolenaar xo_message_hc (xo_handle_t *xop, int code, const char *fmt, ...) 158231337658SMarcel Moolenaar { 158331337658SMarcel Moolenaar va_list vap; 158431337658SMarcel Moolenaar 158531337658SMarcel Moolenaar va_start(vap, fmt); 158631337658SMarcel Moolenaar xo_message_hcv(xop, code, fmt, vap); 158731337658SMarcel Moolenaar va_end(vap); 158831337658SMarcel Moolenaar } 158931337658SMarcel Moolenaar 159031337658SMarcel Moolenaar void 159131337658SMarcel Moolenaar xo_message_c (int code, const char *fmt, ...) 159231337658SMarcel Moolenaar { 159331337658SMarcel Moolenaar va_list vap; 159431337658SMarcel Moolenaar 159531337658SMarcel Moolenaar va_start(vap, fmt); 159631337658SMarcel Moolenaar xo_message_hcv(NULL, code, fmt, vap); 159731337658SMarcel Moolenaar va_end(vap); 159831337658SMarcel Moolenaar } 159931337658SMarcel Moolenaar 160031337658SMarcel Moolenaar void 1601d1a0d267SMarcel Moolenaar xo_message_e (const char *fmt, ...) 160231337658SMarcel Moolenaar { 160331337658SMarcel Moolenaar int code = errno; 160431337658SMarcel Moolenaar va_list vap; 160531337658SMarcel Moolenaar 160631337658SMarcel Moolenaar va_start(vap, fmt); 160731337658SMarcel Moolenaar xo_message_hcv(NULL, code, fmt, vap); 160831337658SMarcel Moolenaar va_end(vap); 160931337658SMarcel Moolenaar } 161031337658SMarcel Moolenaar 1611d1a0d267SMarcel Moolenaar void 1612d1a0d267SMarcel Moolenaar xo_message (const char *fmt, ...) 1613d1a0d267SMarcel Moolenaar { 1614d1a0d267SMarcel Moolenaar va_list vap; 1615d1a0d267SMarcel Moolenaar 1616d1a0d267SMarcel Moolenaar va_start(vap, fmt); 1617d1a0d267SMarcel Moolenaar xo_message_hcv(NULL, 0, fmt, vap); 1618d1a0d267SMarcel Moolenaar va_end(vap); 1619d1a0d267SMarcel Moolenaar } 1620d1a0d267SMarcel Moolenaar 162131337658SMarcel Moolenaar static void 162231337658SMarcel Moolenaar xo_failure (xo_handle_t *xop, const char *fmt, ...) 162331337658SMarcel Moolenaar { 1624d1a0d267SMarcel Moolenaar if (!XOF_ISSET(xop, XOF_WARN)) 162531337658SMarcel Moolenaar return; 162631337658SMarcel Moolenaar 162731337658SMarcel Moolenaar va_list vap; 162831337658SMarcel Moolenaar 162931337658SMarcel Moolenaar va_start(vap, fmt); 163031337658SMarcel Moolenaar xo_warn_hcv(xop, -1, 1, fmt, vap); 163131337658SMarcel Moolenaar va_end(vap); 163231337658SMarcel Moolenaar } 163331337658SMarcel Moolenaar 163431337658SMarcel Moolenaar /** 163531337658SMarcel Moolenaar * Create a handle for use by later libxo functions. 163631337658SMarcel Moolenaar * 163731337658SMarcel Moolenaar * Note: normal use of libxo does not require a distinct handle, since 163831337658SMarcel Moolenaar * the default handle (used when NULL is passed) generates text on stdout. 163931337658SMarcel Moolenaar * 164031337658SMarcel Moolenaar * @style Style of output desired (XO_STYLE_* value) 164131337658SMarcel Moolenaar * @flags Set of XOF_* flags in use with this handle 164231337658SMarcel Moolenaar */ 164331337658SMarcel Moolenaar xo_handle_t * 164431337658SMarcel Moolenaar xo_create (xo_style_t style, xo_xof_flags_t flags) 164531337658SMarcel Moolenaar { 164631337658SMarcel Moolenaar xo_handle_t *xop = xo_realloc(NULL, sizeof(*xop)); 164731337658SMarcel Moolenaar 164831337658SMarcel Moolenaar if (xop) { 164931337658SMarcel Moolenaar bzero(xop, sizeof(*xop)); 165031337658SMarcel Moolenaar 165131337658SMarcel Moolenaar xop->xo_style = style; 1652d1a0d267SMarcel Moolenaar XOF_SET(xop, flags); 165331337658SMarcel Moolenaar xo_init_handle(xop); 1654d1a0d267SMarcel Moolenaar xop->xo_style = style; /* Reset style (see LIBXO_OPTIONS) */ 165531337658SMarcel Moolenaar } 165631337658SMarcel Moolenaar 165731337658SMarcel Moolenaar return xop; 165831337658SMarcel Moolenaar } 165931337658SMarcel Moolenaar 166031337658SMarcel Moolenaar /** 166131337658SMarcel Moolenaar * Create a handle that will write to the given file. Use 166231337658SMarcel Moolenaar * the XOF_CLOSE_FP flag to have the file closed on xo_destroy(). 166331337658SMarcel Moolenaar * @fp FILE pointer to use 166431337658SMarcel Moolenaar * @style Style of output desired (XO_STYLE_* value) 166531337658SMarcel Moolenaar * @flags Set of XOF_* flags to use with this handle 166631337658SMarcel Moolenaar */ 166731337658SMarcel Moolenaar xo_handle_t * 166831337658SMarcel Moolenaar xo_create_to_file (FILE *fp, xo_style_t style, xo_xof_flags_t flags) 166931337658SMarcel Moolenaar { 167031337658SMarcel Moolenaar xo_handle_t *xop = xo_create(style, flags); 167131337658SMarcel Moolenaar 167231337658SMarcel Moolenaar if (xop) { 167331337658SMarcel Moolenaar xop->xo_opaque = fp; 167431337658SMarcel Moolenaar xop->xo_write = xo_write_to_file; 167531337658SMarcel Moolenaar xop->xo_close = xo_close_file; 1676545ddfbeSMarcel Moolenaar xop->xo_flush = xo_flush_file; 167731337658SMarcel Moolenaar } 167831337658SMarcel Moolenaar 167931337658SMarcel Moolenaar return xop; 168031337658SMarcel Moolenaar } 168131337658SMarcel Moolenaar 168231337658SMarcel Moolenaar /** 168331337658SMarcel Moolenaar * Release any resources held by the handle. 168431337658SMarcel Moolenaar * @xop XO handle to alter (or NULL for default handle) 168531337658SMarcel Moolenaar */ 168631337658SMarcel Moolenaar void 1687c600d307SMarcel Moolenaar xo_destroy (xo_handle_t *xop_arg) 168831337658SMarcel Moolenaar { 1689c600d307SMarcel Moolenaar xo_handle_t *xop = xo_default(xop_arg); 169031337658SMarcel Moolenaar 1691d1a0d267SMarcel Moolenaar xo_flush_h(xop); 1692d1a0d267SMarcel Moolenaar 1693d1a0d267SMarcel Moolenaar if (xop->xo_close && XOF_ISSET(xop, XOF_CLOSE_FP)) 169431337658SMarcel Moolenaar xop->xo_close(xop->xo_opaque); 169531337658SMarcel Moolenaar 169631337658SMarcel Moolenaar xo_free(xop->xo_stack); 169731337658SMarcel Moolenaar xo_buf_cleanup(&xop->xo_data); 169831337658SMarcel Moolenaar xo_buf_cleanup(&xop->xo_fmt); 169931337658SMarcel Moolenaar xo_buf_cleanup(&xop->xo_predicate); 170031337658SMarcel Moolenaar xo_buf_cleanup(&xop->xo_attrs); 1701788ca347SMarcel Moolenaar xo_buf_cleanup(&xop->xo_color_buf); 1702788ca347SMarcel Moolenaar 1703788ca347SMarcel Moolenaar if (xop->xo_version) 1704788ca347SMarcel Moolenaar xo_free(xop->xo_version); 170531337658SMarcel Moolenaar 1706c600d307SMarcel Moolenaar if (xop_arg == NULL) { 1707545ddfbeSMarcel Moolenaar bzero(&xo_default_handle, sizeof(xo_default_handle)); 170831337658SMarcel Moolenaar xo_default_inited = 0; 170931337658SMarcel Moolenaar } else 171031337658SMarcel Moolenaar xo_free(xop); 171131337658SMarcel Moolenaar } 171231337658SMarcel Moolenaar 171331337658SMarcel Moolenaar /** 171431337658SMarcel Moolenaar * Record a new output style to use for the given handle (or default if 171531337658SMarcel Moolenaar * handle is NULL). This output style will be used for any future output. 171631337658SMarcel Moolenaar * 171731337658SMarcel Moolenaar * @xop XO handle to alter (or NULL for default handle) 171831337658SMarcel Moolenaar * @style new output style (XO_STYLE_*) 171931337658SMarcel Moolenaar */ 172031337658SMarcel Moolenaar void 172131337658SMarcel Moolenaar xo_set_style (xo_handle_t *xop, xo_style_t style) 172231337658SMarcel Moolenaar { 172331337658SMarcel Moolenaar xop = xo_default(xop); 172431337658SMarcel Moolenaar xop->xo_style = style; 172531337658SMarcel Moolenaar } 172631337658SMarcel Moolenaar 172731337658SMarcel Moolenaar xo_style_t 172831337658SMarcel Moolenaar xo_get_style (xo_handle_t *xop) 172931337658SMarcel Moolenaar { 173031337658SMarcel Moolenaar xop = xo_default(xop); 1731788ca347SMarcel Moolenaar return xo_style(xop); 173231337658SMarcel Moolenaar } 173331337658SMarcel Moolenaar 173431337658SMarcel Moolenaar static int 173531337658SMarcel Moolenaar xo_name_to_style (const char *name) 173631337658SMarcel Moolenaar { 173731337658SMarcel Moolenaar if (strcmp(name, "xml") == 0) 173831337658SMarcel Moolenaar return XO_STYLE_XML; 173931337658SMarcel Moolenaar else if (strcmp(name, "json") == 0) 174031337658SMarcel Moolenaar return XO_STYLE_JSON; 1741d1a0d267SMarcel Moolenaar else if (strcmp(name, "encoder") == 0) 1742d1a0d267SMarcel Moolenaar return XO_STYLE_ENCODER; 174331337658SMarcel Moolenaar else if (strcmp(name, "text") == 0) 174431337658SMarcel Moolenaar return XO_STYLE_TEXT; 174531337658SMarcel Moolenaar else if (strcmp(name, "html") == 0) 174631337658SMarcel Moolenaar return XO_STYLE_HTML; 1747d1a0d267SMarcel Moolenaar else if (strcmp(name, "sdparams") == 0) 1748d1a0d267SMarcel Moolenaar return XO_STYLE_SDPARAMS; 174931337658SMarcel Moolenaar 175031337658SMarcel Moolenaar return -1; 175131337658SMarcel Moolenaar } 175231337658SMarcel Moolenaar 175331337658SMarcel Moolenaar /* 1754d1a0d267SMarcel Moolenaar * Indicate if the style is an "encoding" one as opposed to a "display" one. 1755d1a0d267SMarcel Moolenaar */ 1756d1a0d267SMarcel Moolenaar static int 1757d1a0d267SMarcel Moolenaar xo_style_is_encoding (xo_handle_t *xop) 1758d1a0d267SMarcel Moolenaar { 1759d1a0d267SMarcel Moolenaar if (xo_style(xop) == XO_STYLE_JSON 1760d1a0d267SMarcel Moolenaar || xo_style(xop) == XO_STYLE_XML 1761d1a0d267SMarcel Moolenaar || xo_style(xop) == XO_STYLE_SDPARAMS 1762d1a0d267SMarcel Moolenaar || xo_style(xop) == XO_STYLE_ENCODER) 1763d1a0d267SMarcel Moolenaar return 1; 1764d1a0d267SMarcel Moolenaar return 0; 1765d1a0d267SMarcel Moolenaar } 1766d1a0d267SMarcel Moolenaar 1767d1a0d267SMarcel Moolenaar /* Simple name-value mapping */ 1768d1a0d267SMarcel Moolenaar typedef struct xo_mapping_s { 1769d1a0d267SMarcel Moolenaar xo_xff_flags_t xm_value; 1770d1a0d267SMarcel Moolenaar const char *xm_name; 1771d1a0d267SMarcel Moolenaar } xo_mapping_t; 1772d1a0d267SMarcel Moolenaar 1773d1a0d267SMarcel Moolenaar static xo_xff_flags_t 1774d1a0d267SMarcel Moolenaar xo_name_lookup (xo_mapping_t *map, const char *value, int len) 1775d1a0d267SMarcel Moolenaar { 1776d1a0d267SMarcel Moolenaar if (len == 0) 1777d1a0d267SMarcel Moolenaar return 0; 1778d1a0d267SMarcel Moolenaar 1779d1a0d267SMarcel Moolenaar if (len < 0) 1780d1a0d267SMarcel Moolenaar len = strlen(value); 1781d1a0d267SMarcel Moolenaar 1782d1a0d267SMarcel Moolenaar while (isspace((int) *value)) { 1783d1a0d267SMarcel Moolenaar value += 1; 1784d1a0d267SMarcel Moolenaar len -= 1; 1785d1a0d267SMarcel Moolenaar } 1786d1a0d267SMarcel Moolenaar 1787d1a0d267SMarcel Moolenaar while (isspace((int) value[len])) 1788d1a0d267SMarcel Moolenaar len -= 1; 1789d1a0d267SMarcel Moolenaar 1790d1a0d267SMarcel Moolenaar if (*value == '\0') 1791d1a0d267SMarcel Moolenaar return 0; 1792d1a0d267SMarcel Moolenaar 1793d1a0d267SMarcel Moolenaar for ( ; map->xm_name; map++) 1794d1a0d267SMarcel Moolenaar if (strncmp(map->xm_name, value, len) == 0) 1795d1a0d267SMarcel Moolenaar return map->xm_value; 1796d1a0d267SMarcel Moolenaar 1797d1a0d267SMarcel Moolenaar return 0; 1798d1a0d267SMarcel Moolenaar } 1799d1a0d267SMarcel Moolenaar 1800d1a0d267SMarcel Moolenaar #ifdef NOT_NEEDED_YET 1801d1a0d267SMarcel Moolenaar static const char * 1802d1a0d267SMarcel Moolenaar xo_value_lookup (xo_mapping_t *map, xo_xff_flags_t value) 1803d1a0d267SMarcel Moolenaar { 1804d1a0d267SMarcel Moolenaar if (value == 0) 1805d1a0d267SMarcel Moolenaar return NULL; 1806d1a0d267SMarcel Moolenaar 1807d1a0d267SMarcel Moolenaar for ( ; map->xm_name; map++) 1808d1a0d267SMarcel Moolenaar if (map->xm_value == value) 1809d1a0d267SMarcel Moolenaar return map->xm_name; 1810d1a0d267SMarcel Moolenaar 1811d1a0d267SMarcel Moolenaar return NULL; 1812d1a0d267SMarcel Moolenaar } 1813d1a0d267SMarcel Moolenaar #endif /* NOT_NEEDED_YET */ 1814d1a0d267SMarcel Moolenaar 1815d1a0d267SMarcel Moolenaar static xo_mapping_t xo_xof_names[] = { 1816d1a0d267SMarcel Moolenaar { XOF_COLOR_ALLOWED, "color" }, 1817d1a0d267SMarcel Moolenaar { XOF_COLUMNS, "columns" }, 1818d1a0d267SMarcel Moolenaar { XOF_DTRT, "dtrt" }, 1819d1a0d267SMarcel Moolenaar { XOF_FLUSH, "flush" }, 1820d1a0d267SMarcel Moolenaar { XOF_IGNORE_CLOSE, "ignore-close" }, 1821d1a0d267SMarcel Moolenaar { XOF_INFO, "info" }, 1822d1a0d267SMarcel Moolenaar { XOF_KEYS, "keys" }, 1823d1a0d267SMarcel Moolenaar { XOF_LOG_GETTEXT, "log-gettext" }, 1824d1a0d267SMarcel Moolenaar { XOF_LOG_SYSLOG, "log-syslog" }, 1825d1a0d267SMarcel Moolenaar { XOF_NO_HUMANIZE, "no-humanize" }, 1826d1a0d267SMarcel Moolenaar { XOF_NO_LOCALE, "no-locale" }, 1827d1a0d267SMarcel Moolenaar { XOF_NO_TOP, "no-top" }, 1828d1a0d267SMarcel Moolenaar { XOF_NOT_FIRST, "not-first" }, 1829d1a0d267SMarcel Moolenaar { XOF_PRETTY, "pretty" }, 1830d1a0d267SMarcel Moolenaar { XOF_UNDERSCORES, "underscores" }, 1831d1a0d267SMarcel Moolenaar { XOF_UNITS, "units" }, 1832d1a0d267SMarcel Moolenaar { XOF_WARN, "warn" }, 1833d1a0d267SMarcel Moolenaar { XOF_WARN_XML, "warn-xml" }, 1834d1a0d267SMarcel Moolenaar { XOF_XPATH, "xpath" }, 1835d1a0d267SMarcel Moolenaar { 0, NULL } 1836d1a0d267SMarcel Moolenaar }; 1837d1a0d267SMarcel Moolenaar 1838d1a0d267SMarcel Moolenaar /* 183931337658SMarcel Moolenaar * Convert string name to XOF_* flag value. 184031337658SMarcel Moolenaar * Not all are useful. Or safe. Or sane. 184131337658SMarcel Moolenaar */ 184231337658SMarcel Moolenaar static unsigned 184331337658SMarcel Moolenaar xo_name_to_flag (const char *name) 184431337658SMarcel Moolenaar { 1845d1a0d267SMarcel Moolenaar return (unsigned) xo_name_lookup(xo_xof_names, name, -1); 184631337658SMarcel Moolenaar } 184731337658SMarcel Moolenaar 184831337658SMarcel Moolenaar int 184931337658SMarcel Moolenaar xo_set_style_name (xo_handle_t *xop, const char *name) 185031337658SMarcel Moolenaar { 185131337658SMarcel Moolenaar if (name == NULL) 185231337658SMarcel Moolenaar return -1; 185331337658SMarcel Moolenaar 185431337658SMarcel Moolenaar int style = xo_name_to_style(name); 185531337658SMarcel Moolenaar if (style < 0) 185631337658SMarcel Moolenaar return -1; 185731337658SMarcel Moolenaar 185831337658SMarcel Moolenaar xo_set_style(xop, style); 185931337658SMarcel Moolenaar return 0; 186031337658SMarcel Moolenaar } 186131337658SMarcel Moolenaar 186231337658SMarcel Moolenaar /* 186331337658SMarcel Moolenaar * Set the options for a handle using a string of options 186431337658SMarcel Moolenaar * passed in. The input is a comma-separated set of names 186531337658SMarcel Moolenaar * and optional values: "xml,pretty,indent=4" 186631337658SMarcel Moolenaar */ 186731337658SMarcel Moolenaar int 186831337658SMarcel Moolenaar xo_set_options (xo_handle_t *xop, const char *input) 186931337658SMarcel Moolenaar { 187031337658SMarcel Moolenaar char *cp, *ep, *vp, *np, *bp; 187131337658SMarcel Moolenaar int style = -1, new_style, len, rc = 0; 187231337658SMarcel Moolenaar xo_xof_flags_t new_flag; 187331337658SMarcel Moolenaar 187431337658SMarcel Moolenaar if (input == NULL) 187531337658SMarcel Moolenaar return 0; 187631337658SMarcel Moolenaar 187731337658SMarcel Moolenaar xop = xo_default(xop); 187831337658SMarcel Moolenaar 1879788ca347SMarcel Moolenaar #ifdef LIBXO_COLOR_ON_BY_DEFAULT 1880788ca347SMarcel Moolenaar /* If the installer used --enable-color-on-by-default, then we allow it */ 1881d1a0d267SMarcel Moolenaar XOF_SET(xop, XOF_COLOR_ALLOWED); 1882788ca347SMarcel Moolenaar #endif /* LIBXO_COLOR_ON_BY_DEFAULT */ 1883788ca347SMarcel Moolenaar 188431337658SMarcel Moolenaar /* 188531337658SMarcel Moolenaar * We support a simpler, old-school style of giving option 188631337658SMarcel Moolenaar * also, using a single character for each option. It's 188731337658SMarcel Moolenaar * ideal for lazy people, such as myself. 188831337658SMarcel Moolenaar */ 188931337658SMarcel Moolenaar if (*input == ':') { 189031337658SMarcel Moolenaar int sz; 189131337658SMarcel Moolenaar 189231337658SMarcel Moolenaar for (input++ ; *input; input++) { 189331337658SMarcel Moolenaar switch (*input) { 1894788ca347SMarcel Moolenaar case 'c': 1895d1a0d267SMarcel Moolenaar XOF_SET(xop, XOF_COLOR_ALLOWED); 1896788ca347SMarcel Moolenaar break; 1897788ca347SMarcel Moolenaar 189831337658SMarcel Moolenaar case 'f': 1899d1a0d267SMarcel Moolenaar XOF_SET(xop, XOF_FLUSH); 190031337658SMarcel Moolenaar break; 190131337658SMarcel Moolenaar 1902545ddfbeSMarcel Moolenaar case 'F': 1903d1a0d267SMarcel Moolenaar XOF_SET(xop, XOF_FLUSH_LINE); 1904d1a0d267SMarcel Moolenaar break; 1905d1a0d267SMarcel Moolenaar 1906d1a0d267SMarcel Moolenaar case 'g': 1907d1a0d267SMarcel Moolenaar XOF_SET(xop, XOF_LOG_GETTEXT); 1908545ddfbeSMarcel Moolenaar break; 1909545ddfbeSMarcel Moolenaar 191031337658SMarcel Moolenaar case 'H': 191131337658SMarcel Moolenaar xop->xo_style = XO_STYLE_HTML; 191231337658SMarcel Moolenaar break; 191331337658SMarcel Moolenaar 191431337658SMarcel Moolenaar case 'I': 1915d1a0d267SMarcel Moolenaar XOF_SET(xop, XOF_INFO); 191631337658SMarcel Moolenaar break; 191731337658SMarcel Moolenaar 191831337658SMarcel Moolenaar case 'i': 191931337658SMarcel Moolenaar sz = strspn(input + 1, "0123456789"); 192031337658SMarcel Moolenaar if (sz > 0) { 192131337658SMarcel Moolenaar xop->xo_indent_by = atoi(input + 1); 192231337658SMarcel Moolenaar input += sz - 1; /* Skip value */ 192331337658SMarcel Moolenaar } 192431337658SMarcel Moolenaar break; 192531337658SMarcel Moolenaar 192631337658SMarcel Moolenaar case 'J': 192731337658SMarcel Moolenaar xop->xo_style = XO_STYLE_JSON; 192831337658SMarcel Moolenaar break; 192931337658SMarcel Moolenaar 1930d1a0d267SMarcel Moolenaar case 'k': 1931d1a0d267SMarcel Moolenaar XOF_SET(xop, XOF_KEYS); 1932d1a0d267SMarcel Moolenaar break; 1933d1a0d267SMarcel Moolenaar 1934d1a0d267SMarcel Moolenaar case 'n': 1935d1a0d267SMarcel Moolenaar XOF_SET(xop, XOF_NO_HUMANIZE); 1936d1a0d267SMarcel Moolenaar break; 1937d1a0d267SMarcel Moolenaar 193831337658SMarcel Moolenaar case 'P': 1939d1a0d267SMarcel Moolenaar XOF_SET(xop, XOF_PRETTY); 194031337658SMarcel Moolenaar break; 194131337658SMarcel Moolenaar 194231337658SMarcel Moolenaar case 'T': 194331337658SMarcel Moolenaar xop->xo_style = XO_STYLE_TEXT; 194431337658SMarcel Moolenaar break; 194531337658SMarcel Moolenaar 194631337658SMarcel Moolenaar case 'U': 1947d1a0d267SMarcel Moolenaar XOF_SET(xop, XOF_UNITS); 194831337658SMarcel Moolenaar break; 194931337658SMarcel Moolenaar 195031337658SMarcel Moolenaar case 'u': 1951d1a0d267SMarcel Moolenaar XOF_SET(xop, XOF_UNDERSCORES); 195231337658SMarcel Moolenaar break; 195331337658SMarcel Moolenaar 195431337658SMarcel Moolenaar case 'W': 1955d1a0d267SMarcel Moolenaar XOF_SET(xop, XOF_WARN); 195631337658SMarcel Moolenaar break; 195731337658SMarcel Moolenaar 195831337658SMarcel Moolenaar case 'X': 195931337658SMarcel Moolenaar xop->xo_style = XO_STYLE_XML; 196031337658SMarcel Moolenaar break; 196131337658SMarcel Moolenaar 196231337658SMarcel Moolenaar case 'x': 1963d1a0d267SMarcel Moolenaar XOF_SET(xop, XOF_XPATH); 196431337658SMarcel Moolenaar break; 196531337658SMarcel Moolenaar } 196631337658SMarcel Moolenaar } 196731337658SMarcel Moolenaar return 0; 196831337658SMarcel Moolenaar } 196931337658SMarcel Moolenaar 197031337658SMarcel Moolenaar len = strlen(input) + 1; 197131337658SMarcel Moolenaar bp = alloca(len); 197231337658SMarcel Moolenaar memcpy(bp, input, len); 197331337658SMarcel Moolenaar 197431337658SMarcel Moolenaar for (cp = bp, ep = cp + len - 1; cp && cp < ep; cp = np) { 197531337658SMarcel Moolenaar np = strchr(cp, ','); 197631337658SMarcel Moolenaar if (np) 197731337658SMarcel Moolenaar *np++ = '\0'; 197831337658SMarcel Moolenaar 197931337658SMarcel Moolenaar vp = strchr(cp, '='); 198031337658SMarcel Moolenaar if (vp) 198131337658SMarcel Moolenaar *vp++ = '\0'; 198231337658SMarcel Moolenaar 1983788ca347SMarcel Moolenaar if (strcmp("colors", cp) == 0) { 1984788ca347SMarcel Moolenaar /* XXX Look for colors=red-blue+green-yellow */ 1985788ca347SMarcel Moolenaar continue; 1986788ca347SMarcel Moolenaar } 1987788ca347SMarcel Moolenaar 1988d1a0d267SMarcel Moolenaar /* 1989d1a0d267SMarcel Moolenaar * For options, we don't allow "encoder" since we want to 1990d1a0d267SMarcel Moolenaar * handle it explicitly below as "encoder=xxx". 1991d1a0d267SMarcel Moolenaar */ 199231337658SMarcel Moolenaar new_style = xo_name_to_style(cp); 1993d1a0d267SMarcel Moolenaar if (new_style >= 0 && new_style != XO_STYLE_ENCODER) { 199431337658SMarcel Moolenaar if (style >= 0) 199531337658SMarcel Moolenaar xo_warnx("ignoring multiple styles: '%s'", cp); 199631337658SMarcel Moolenaar else 199731337658SMarcel Moolenaar style = new_style; 199831337658SMarcel Moolenaar } else { 199931337658SMarcel Moolenaar new_flag = xo_name_to_flag(cp); 200031337658SMarcel Moolenaar if (new_flag != 0) 2001d1a0d267SMarcel Moolenaar XOF_SET(xop, new_flag); 200231337658SMarcel Moolenaar else { 2003788ca347SMarcel Moolenaar if (strcmp(cp, "no-color") == 0) { 2004d1a0d267SMarcel Moolenaar XOF_CLEAR(xop, XOF_COLOR_ALLOWED); 2005788ca347SMarcel Moolenaar } else if (strcmp(cp, "indent") == 0) { 2006d1a0d267SMarcel Moolenaar if (vp) 200731337658SMarcel Moolenaar xop->xo_indent_by = atoi(vp); 2008d1a0d267SMarcel Moolenaar else 2009d1a0d267SMarcel Moolenaar xo_failure(xop, "missing value for indent option"); 2010d1a0d267SMarcel Moolenaar } else if (strcmp(cp, "encoder") == 0) { 2011d1a0d267SMarcel Moolenaar if (vp == NULL) 2012d1a0d267SMarcel Moolenaar xo_failure(xop, "missing value for encoder option"); 2013d1a0d267SMarcel Moolenaar else { 2014d1a0d267SMarcel Moolenaar if (xo_encoder_init(xop, vp)) { 2015d1a0d267SMarcel Moolenaar xo_failure(xop, "encoder not found: %s", vp); 2016d1a0d267SMarcel Moolenaar rc = -1; 2017d1a0d267SMarcel Moolenaar } 2018d1a0d267SMarcel Moolenaar } 2019d1a0d267SMarcel Moolenaar 202031337658SMarcel Moolenaar } else { 2021d1a0d267SMarcel Moolenaar xo_warnx("unknown libxo option value: '%s'", cp); 202231337658SMarcel Moolenaar rc = -1; 202331337658SMarcel Moolenaar } 202431337658SMarcel Moolenaar } 202531337658SMarcel Moolenaar } 202631337658SMarcel Moolenaar } 202731337658SMarcel Moolenaar 202831337658SMarcel Moolenaar if (style > 0) 202931337658SMarcel Moolenaar xop->xo_style= style; 203031337658SMarcel Moolenaar 203131337658SMarcel Moolenaar return rc; 203231337658SMarcel Moolenaar } 203331337658SMarcel Moolenaar 203431337658SMarcel Moolenaar /** 203531337658SMarcel Moolenaar * Set one or more flags for a given handle (or default if handle is NULL). 203631337658SMarcel Moolenaar * These flags will affect future output. 203731337658SMarcel Moolenaar * 203831337658SMarcel Moolenaar * @xop XO handle to alter (or NULL for default handle) 203931337658SMarcel Moolenaar * @flags Flags to be set (XOF_*) 204031337658SMarcel Moolenaar */ 204131337658SMarcel Moolenaar void 204231337658SMarcel Moolenaar xo_set_flags (xo_handle_t *xop, xo_xof_flags_t flags) 204331337658SMarcel Moolenaar { 204431337658SMarcel Moolenaar xop = xo_default(xop); 204531337658SMarcel Moolenaar 2046d1a0d267SMarcel Moolenaar XOF_SET(xop, flags); 204731337658SMarcel Moolenaar } 204831337658SMarcel Moolenaar 204931337658SMarcel Moolenaar xo_xof_flags_t 205031337658SMarcel Moolenaar xo_get_flags (xo_handle_t *xop) 205131337658SMarcel Moolenaar { 205231337658SMarcel Moolenaar xop = xo_default(xop); 205331337658SMarcel Moolenaar 205431337658SMarcel Moolenaar return xop->xo_flags; 205531337658SMarcel Moolenaar } 205631337658SMarcel Moolenaar 2057d1a0d267SMarcel Moolenaar /* 2058d1a0d267SMarcel Moolenaar * strndup with a twist: len < 0 means strlen 2059d1a0d267SMarcel Moolenaar */ 2060d1a0d267SMarcel Moolenaar static char * 2061d1a0d267SMarcel Moolenaar xo_strndup (const char *str, int len) 2062d1a0d267SMarcel Moolenaar { 2063d1a0d267SMarcel Moolenaar if (len < 0) 2064d1a0d267SMarcel Moolenaar len = strlen(str); 2065d1a0d267SMarcel Moolenaar 2066d1a0d267SMarcel Moolenaar char *cp = xo_realloc(NULL, len + 1); 2067d1a0d267SMarcel Moolenaar if (cp) { 2068d1a0d267SMarcel Moolenaar memcpy(cp, str, len); 2069d1a0d267SMarcel Moolenaar cp[len] = '\0'; 2070d1a0d267SMarcel Moolenaar } 2071d1a0d267SMarcel Moolenaar 2072d1a0d267SMarcel Moolenaar return cp; 2073d1a0d267SMarcel Moolenaar } 2074d1a0d267SMarcel Moolenaar 207531337658SMarcel Moolenaar /** 207631337658SMarcel Moolenaar * Record a leading prefix for the XPath we generate. This allows the 207731337658SMarcel Moolenaar * generated data to be placed within an XML hierarchy but still have 207831337658SMarcel Moolenaar * accurate XPath expressions. 207931337658SMarcel Moolenaar * 208031337658SMarcel Moolenaar * @xop XO handle to alter (or NULL for default handle) 208131337658SMarcel Moolenaar * @path The XPath expression 208231337658SMarcel Moolenaar */ 208331337658SMarcel Moolenaar void 208431337658SMarcel Moolenaar xo_set_leading_xpath (xo_handle_t *xop, const char *path) 208531337658SMarcel Moolenaar { 208631337658SMarcel Moolenaar xop = xo_default(xop); 208731337658SMarcel Moolenaar 208831337658SMarcel Moolenaar if (xop->xo_leading_xpath) { 208931337658SMarcel Moolenaar xo_free(xop->xo_leading_xpath); 209031337658SMarcel Moolenaar xop->xo_leading_xpath = NULL; 209131337658SMarcel Moolenaar } 209231337658SMarcel Moolenaar 209331337658SMarcel Moolenaar if (path == NULL) 209431337658SMarcel Moolenaar return; 209531337658SMarcel Moolenaar 2096d1a0d267SMarcel Moolenaar xop->xo_leading_xpath = xo_strndup(path, -1); 209731337658SMarcel Moolenaar } 209831337658SMarcel Moolenaar 209931337658SMarcel Moolenaar /** 210031337658SMarcel Moolenaar * Record the info data for a set of tags 210131337658SMarcel Moolenaar * 210231337658SMarcel Moolenaar * @xop XO handle to alter (or NULL for default handle) 210331337658SMarcel Moolenaar * @info Info data (xo_info_t) to be recorded (or NULL) (MUST BE SORTED) 210431337658SMarcel Moolenaar * @count Number of entries in info (or -1 to count them ourselves) 210531337658SMarcel Moolenaar */ 210631337658SMarcel Moolenaar void 210731337658SMarcel Moolenaar xo_set_info (xo_handle_t *xop, xo_info_t *infop, int count) 210831337658SMarcel Moolenaar { 210931337658SMarcel Moolenaar xop = xo_default(xop); 211031337658SMarcel Moolenaar 211131337658SMarcel Moolenaar if (count < 0 && infop) { 211231337658SMarcel Moolenaar xo_info_t *xip; 211331337658SMarcel Moolenaar 211431337658SMarcel Moolenaar for (xip = infop, count = 0; xip->xi_name; xip++, count++) 211531337658SMarcel Moolenaar continue; 211631337658SMarcel Moolenaar } 211731337658SMarcel Moolenaar 211831337658SMarcel Moolenaar xop->xo_info = infop; 211931337658SMarcel Moolenaar xop->xo_info_count = count; 212031337658SMarcel Moolenaar } 212131337658SMarcel Moolenaar 212231337658SMarcel Moolenaar /** 212331337658SMarcel Moolenaar * Set the formatter callback for a handle. The callback should 212431337658SMarcel Moolenaar * return a newly formatting contents of a formatting instruction, 212531337658SMarcel Moolenaar * meaning the bits inside the braces. 212631337658SMarcel Moolenaar */ 212731337658SMarcel Moolenaar void 212831337658SMarcel Moolenaar xo_set_formatter (xo_handle_t *xop, xo_formatter_t func, 212931337658SMarcel Moolenaar xo_checkpointer_t cfunc) 213031337658SMarcel Moolenaar { 213131337658SMarcel Moolenaar xop = xo_default(xop); 213231337658SMarcel Moolenaar 213331337658SMarcel Moolenaar xop->xo_formatter = func; 213431337658SMarcel Moolenaar xop->xo_checkpointer = cfunc; 213531337658SMarcel Moolenaar } 213631337658SMarcel Moolenaar 213731337658SMarcel Moolenaar /** 213831337658SMarcel Moolenaar * Clear one or more flags for a given handle (or default if handle is NULL). 213931337658SMarcel Moolenaar * These flags will affect future output. 214031337658SMarcel Moolenaar * 214131337658SMarcel Moolenaar * @xop XO handle to alter (or NULL for default handle) 214231337658SMarcel Moolenaar * @flags Flags to be cleared (XOF_*) 214331337658SMarcel Moolenaar */ 214431337658SMarcel Moolenaar void 214531337658SMarcel Moolenaar xo_clear_flags (xo_handle_t *xop, xo_xof_flags_t flags) 214631337658SMarcel Moolenaar { 214731337658SMarcel Moolenaar xop = xo_default(xop); 214831337658SMarcel Moolenaar 2149d1a0d267SMarcel Moolenaar XOF_CLEAR(xop, flags); 215031337658SMarcel Moolenaar } 215131337658SMarcel Moolenaar 2152545ddfbeSMarcel Moolenaar static const char * 2153545ddfbeSMarcel Moolenaar xo_state_name (xo_state_t state) 2154545ddfbeSMarcel Moolenaar { 2155545ddfbeSMarcel Moolenaar static const char *names[] = { 2156545ddfbeSMarcel Moolenaar "init", 2157545ddfbeSMarcel Moolenaar "open_container", 2158545ddfbeSMarcel Moolenaar "close_container", 2159545ddfbeSMarcel Moolenaar "open_list", 2160545ddfbeSMarcel Moolenaar "close_list", 2161545ddfbeSMarcel Moolenaar "open_instance", 2162545ddfbeSMarcel Moolenaar "close_instance", 2163545ddfbeSMarcel Moolenaar "open_leaf_list", 2164545ddfbeSMarcel Moolenaar "close_leaf_list", 2165545ddfbeSMarcel Moolenaar "discarding", 2166545ddfbeSMarcel Moolenaar "marker", 2167545ddfbeSMarcel Moolenaar "emit", 2168545ddfbeSMarcel Moolenaar "emit_leaf_list", 2169545ddfbeSMarcel Moolenaar "finish", 2170545ddfbeSMarcel Moolenaar NULL 2171545ddfbeSMarcel Moolenaar }; 2172545ddfbeSMarcel Moolenaar 2173545ddfbeSMarcel Moolenaar if (state < (sizeof(names) / sizeof(names[0]))) 2174545ddfbeSMarcel Moolenaar return names[state]; 2175545ddfbeSMarcel Moolenaar 2176545ddfbeSMarcel Moolenaar return "unknown"; 2177545ddfbeSMarcel Moolenaar } 2178545ddfbeSMarcel Moolenaar 217931337658SMarcel Moolenaar static void 218031337658SMarcel Moolenaar xo_line_ensure_open (xo_handle_t *xop, xo_xff_flags_t flags UNUSED) 218131337658SMarcel Moolenaar { 218231337658SMarcel Moolenaar static char div_open[] = "<div class=\"line\">"; 218331337658SMarcel Moolenaar static char div_open_blank[] = "<div class=\"blank-line\">"; 218431337658SMarcel Moolenaar 2185d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_DIV_OPEN)) 218631337658SMarcel Moolenaar return; 218731337658SMarcel Moolenaar 2188788ca347SMarcel Moolenaar if (xo_style(xop) != XO_STYLE_HTML) 218931337658SMarcel Moolenaar return; 219031337658SMarcel Moolenaar 2191d1a0d267SMarcel Moolenaar XOIF_SET(xop, XOIF_DIV_OPEN); 219231337658SMarcel Moolenaar if (flags & XFF_BLANK_LINE) 219331337658SMarcel Moolenaar xo_data_append(xop, div_open_blank, sizeof(div_open_blank) - 1); 219431337658SMarcel Moolenaar else 219531337658SMarcel Moolenaar xo_data_append(xop, div_open, sizeof(div_open) - 1); 219631337658SMarcel Moolenaar 2197d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_PRETTY)) 219831337658SMarcel Moolenaar xo_data_append(xop, "\n", 1); 219931337658SMarcel Moolenaar } 220031337658SMarcel Moolenaar 220131337658SMarcel Moolenaar static void 220231337658SMarcel Moolenaar xo_line_close (xo_handle_t *xop) 220331337658SMarcel Moolenaar { 220431337658SMarcel Moolenaar static char div_close[] = "</div>"; 220531337658SMarcel Moolenaar 2206788ca347SMarcel Moolenaar switch (xo_style(xop)) { 220731337658SMarcel Moolenaar case XO_STYLE_HTML: 2208d1a0d267SMarcel Moolenaar if (!XOIF_ISSET(xop, XOIF_DIV_OPEN)) 220931337658SMarcel Moolenaar xo_line_ensure_open(xop, 0); 221031337658SMarcel Moolenaar 2211d1a0d267SMarcel Moolenaar XOIF_CLEAR(xop, XOIF_DIV_OPEN); 221231337658SMarcel Moolenaar xo_data_append(xop, div_close, sizeof(div_close) - 1); 221331337658SMarcel Moolenaar 2214d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_PRETTY)) 221531337658SMarcel Moolenaar xo_data_append(xop, "\n", 1); 221631337658SMarcel Moolenaar break; 221731337658SMarcel Moolenaar 221831337658SMarcel Moolenaar case XO_STYLE_TEXT: 221931337658SMarcel Moolenaar xo_data_append(xop, "\n", 1); 222031337658SMarcel Moolenaar break; 222131337658SMarcel Moolenaar } 222231337658SMarcel Moolenaar } 222331337658SMarcel Moolenaar 222431337658SMarcel Moolenaar static int 222531337658SMarcel Moolenaar xo_info_compare (const void *key, const void *data) 222631337658SMarcel Moolenaar { 222731337658SMarcel Moolenaar const char *name = key; 222831337658SMarcel Moolenaar const xo_info_t *xip = data; 222931337658SMarcel Moolenaar 223031337658SMarcel Moolenaar return strcmp(name, xip->xi_name); 223131337658SMarcel Moolenaar } 223231337658SMarcel Moolenaar 223331337658SMarcel Moolenaar 223431337658SMarcel Moolenaar static xo_info_t * 223531337658SMarcel Moolenaar xo_info_find (xo_handle_t *xop, const char *name, int nlen) 223631337658SMarcel Moolenaar { 223731337658SMarcel Moolenaar xo_info_t *xip; 223831337658SMarcel Moolenaar char *cp = alloca(nlen + 1); /* Need local copy for NUL termination */ 223931337658SMarcel Moolenaar 224031337658SMarcel Moolenaar memcpy(cp, name, nlen); 224131337658SMarcel Moolenaar cp[nlen] = '\0'; 224231337658SMarcel Moolenaar 224331337658SMarcel Moolenaar xip = bsearch(cp, xop->xo_info, xop->xo_info_count, 224431337658SMarcel Moolenaar sizeof(xop->xo_info[0]), xo_info_compare); 224531337658SMarcel Moolenaar return xip; 224631337658SMarcel Moolenaar } 224731337658SMarcel Moolenaar 224831337658SMarcel Moolenaar #define CONVERT(_have, _need) (((_have) << 8) | (_need)) 224931337658SMarcel Moolenaar 225031337658SMarcel Moolenaar /* 225131337658SMarcel Moolenaar * Check to see that the conversion is safe and sane. 225231337658SMarcel Moolenaar */ 225331337658SMarcel Moolenaar static int 225431337658SMarcel Moolenaar xo_check_conversion (xo_handle_t *xop, int have_enc, int need_enc) 225531337658SMarcel Moolenaar { 225631337658SMarcel Moolenaar switch (CONVERT(have_enc, need_enc)) { 225731337658SMarcel Moolenaar case CONVERT(XF_ENC_UTF8, XF_ENC_UTF8): 225831337658SMarcel Moolenaar case CONVERT(XF_ENC_UTF8, XF_ENC_LOCALE): 225931337658SMarcel Moolenaar case CONVERT(XF_ENC_WIDE, XF_ENC_UTF8): 226031337658SMarcel Moolenaar case CONVERT(XF_ENC_WIDE, XF_ENC_LOCALE): 226131337658SMarcel Moolenaar case CONVERT(XF_ENC_LOCALE, XF_ENC_LOCALE): 226231337658SMarcel Moolenaar case CONVERT(XF_ENC_LOCALE, XF_ENC_UTF8): 226331337658SMarcel Moolenaar return 0; 226431337658SMarcel Moolenaar 226531337658SMarcel Moolenaar default: 226631337658SMarcel Moolenaar xo_failure(xop, "invalid conversion (%c:%c)", have_enc, need_enc); 226731337658SMarcel Moolenaar return 1; 226831337658SMarcel Moolenaar } 226931337658SMarcel Moolenaar } 227031337658SMarcel Moolenaar 227131337658SMarcel Moolenaar static int 227231337658SMarcel Moolenaar xo_format_string_direct (xo_handle_t *xop, xo_buffer_t *xbp, 227331337658SMarcel Moolenaar xo_xff_flags_t flags, 227431337658SMarcel Moolenaar const wchar_t *wcp, const char *cp, int len, int max, 227531337658SMarcel Moolenaar int need_enc, int have_enc) 227631337658SMarcel Moolenaar { 227731337658SMarcel Moolenaar int cols = 0; 2278c600d307SMarcel Moolenaar wchar_t wc = 0; 227931337658SMarcel Moolenaar int ilen, olen, width; 228031337658SMarcel Moolenaar int attr = (flags & XFF_ATTR); 228131337658SMarcel Moolenaar const char *sp; 228231337658SMarcel Moolenaar 228331337658SMarcel Moolenaar if (len > 0 && !xo_buf_has_room(xbp, len)) 228431337658SMarcel Moolenaar return 0; 228531337658SMarcel Moolenaar 228631337658SMarcel Moolenaar for (;;) { 228731337658SMarcel Moolenaar if (len == 0) 228831337658SMarcel Moolenaar break; 228931337658SMarcel Moolenaar 229031337658SMarcel Moolenaar if (cp) { 229131337658SMarcel Moolenaar if (*cp == '\0') 229231337658SMarcel Moolenaar break; 229331337658SMarcel Moolenaar if ((flags & XFF_UNESCAPE) && (*cp == '\\' || *cp == '%')) { 229431337658SMarcel Moolenaar cp += 1; 229531337658SMarcel Moolenaar len -= 1; 229631337658SMarcel Moolenaar } 229731337658SMarcel Moolenaar } 229831337658SMarcel Moolenaar 229931337658SMarcel Moolenaar if (wcp && *wcp == L'\0') 230031337658SMarcel Moolenaar break; 230131337658SMarcel Moolenaar 230231337658SMarcel Moolenaar ilen = 0; 230331337658SMarcel Moolenaar 230431337658SMarcel Moolenaar switch (have_enc) { 230531337658SMarcel Moolenaar case XF_ENC_WIDE: /* Wide character */ 230631337658SMarcel Moolenaar wc = *wcp++; 230731337658SMarcel Moolenaar ilen = 1; 230831337658SMarcel Moolenaar break; 230931337658SMarcel Moolenaar 231031337658SMarcel Moolenaar case XF_ENC_UTF8: /* UTF-8 */ 231131337658SMarcel Moolenaar ilen = xo_utf8_to_wc_len(cp); 231231337658SMarcel Moolenaar if (ilen < 0) { 231331337658SMarcel Moolenaar xo_failure(xop, "invalid UTF-8 character: %02hhx", *cp); 2314d1a0d267SMarcel Moolenaar return -1; /* Can't continue; we can't find the end */ 231531337658SMarcel Moolenaar } 231631337658SMarcel Moolenaar 231731337658SMarcel Moolenaar if (len > 0 && len < ilen) { 231831337658SMarcel Moolenaar len = 0; /* Break out of the loop */ 231931337658SMarcel Moolenaar continue; 232031337658SMarcel Moolenaar } 232131337658SMarcel Moolenaar 232231337658SMarcel Moolenaar wc = xo_utf8_char(cp, ilen); 232331337658SMarcel Moolenaar if (wc == (wchar_t) -1) { 232431337658SMarcel Moolenaar xo_failure(xop, "invalid UTF-8 character: %02hhx/%d", 232531337658SMarcel Moolenaar *cp, ilen); 2326d1a0d267SMarcel Moolenaar return -1; /* Can't continue; we can't find the end */ 232731337658SMarcel Moolenaar } 232831337658SMarcel Moolenaar cp += ilen; 232931337658SMarcel Moolenaar break; 233031337658SMarcel Moolenaar 233131337658SMarcel Moolenaar case XF_ENC_LOCALE: /* Native locale */ 233231337658SMarcel Moolenaar ilen = (len > 0) ? len : MB_LEN_MAX; 233331337658SMarcel Moolenaar ilen = mbrtowc(&wc, cp, ilen, &xop->xo_mbstate); 233431337658SMarcel Moolenaar if (ilen < 0) { /* Invalid data; skip */ 233531337658SMarcel Moolenaar xo_failure(xop, "invalid mbs char: %02hhx", *cp); 2336dbf26257SAlexander Kabaev wc = L'?'; 2337dbf26257SAlexander Kabaev ilen = 1; 233831337658SMarcel Moolenaar } 2339d1a0d267SMarcel Moolenaar 234031337658SMarcel Moolenaar if (ilen == 0) { /* Hit a wide NUL character */ 234131337658SMarcel Moolenaar len = 0; 234231337658SMarcel Moolenaar continue; 234331337658SMarcel Moolenaar } 234431337658SMarcel Moolenaar 234531337658SMarcel Moolenaar cp += ilen; 234631337658SMarcel Moolenaar break; 234731337658SMarcel Moolenaar } 234831337658SMarcel Moolenaar 234931337658SMarcel Moolenaar /* Reduce len, but not below zero */ 235031337658SMarcel Moolenaar if (len > 0) { 235131337658SMarcel Moolenaar len -= ilen; 235231337658SMarcel Moolenaar if (len < 0) 235331337658SMarcel Moolenaar len = 0; 235431337658SMarcel Moolenaar } 235531337658SMarcel Moolenaar 235631337658SMarcel Moolenaar /* 235731337658SMarcel Moolenaar * Find the width-in-columns of this character, which must be done 235831337658SMarcel Moolenaar * in wide characters, since we lack a mbswidth() function. If 235931337658SMarcel Moolenaar * it doesn't fit 236031337658SMarcel Moolenaar */ 2361d1a0d267SMarcel Moolenaar width = xo_wcwidth(wc); 236231337658SMarcel Moolenaar if (width < 0) 236331337658SMarcel Moolenaar width = iswcntrl(wc) ? 0 : 1; 236431337658SMarcel Moolenaar 2365788ca347SMarcel Moolenaar if (xo_style(xop) == XO_STYLE_TEXT || xo_style(xop) == XO_STYLE_HTML) { 236631337658SMarcel Moolenaar if (max > 0 && cols + width > max) 236731337658SMarcel Moolenaar break; 236831337658SMarcel Moolenaar } 236931337658SMarcel Moolenaar 237031337658SMarcel Moolenaar switch (need_enc) { 237131337658SMarcel Moolenaar case XF_ENC_UTF8: 237231337658SMarcel Moolenaar 237331337658SMarcel Moolenaar /* Output in UTF-8 needs to be escaped, based on the style */ 2374788ca347SMarcel Moolenaar switch (xo_style(xop)) { 237531337658SMarcel Moolenaar case XO_STYLE_XML: 237631337658SMarcel Moolenaar case XO_STYLE_HTML: 237731337658SMarcel Moolenaar if (wc == '<') 237831337658SMarcel Moolenaar sp = xo_xml_lt; 237931337658SMarcel Moolenaar else if (wc == '>') 238031337658SMarcel Moolenaar sp = xo_xml_gt; 238131337658SMarcel Moolenaar else if (wc == '&') 238231337658SMarcel Moolenaar sp = xo_xml_amp; 238331337658SMarcel Moolenaar else if (attr && wc == '"') 238431337658SMarcel Moolenaar sp = xo_xml_quot; 238531337658SMarcel Moolenaar else 238631337658SMarcel Moolenaar break; 238731337658SMarcel Moolenaar 238831337658SMarcel Moolenaar int slen = strlen(sp); 238931337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, slen - 1)) 239031337658SMarcel Moolenaar return -1; 239131337658SMarcel Moolenaar 239231337658SMarcel Moolenaar memcpy(xbp->xb_curp, sp, slen); 239331337658SMarcel Moolenaar xbp->xb_curp += slen; 239431337658SMarcel Moolenaar goto done_with_encoding; /* Need multi-level 'break' */ 239531337658SMarcel Moolenaar 239631337658SMarcel Moolenaar case XO_STYLE_JSON: 2397545ddfbeSMarcel Moolenaar if (wc != '\\' && wc != '"' && wc != '\n' && wc != '\r') 239831337658SMarcel Moolenaar break; 239931337658SMarcel Moolenaar 240031337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, 2)) 240131337658SMarcel Moolenaar return -1; 240231337658SMarcel Moolenaar 240331337658SMarcel Moolenaar *xbp->xb_curp++ = '\\'; 2404545ddfbeSMarcel Moolenaar if (wc == '\n') 2405545ddfbeSMarcel Moolenaar wc = 'n'; 2406545ddfbeSMarcel Moolenaar else if (wc == '\r') 2407545ddfbeSMarcel Moolenaar wc = 'r'; 2408545ddfbeSMarcel Moolenaar else wc = wc & 0x7f; 2409545ddfbeSMarcel Moolenaar 2410545ddfbeSMarcel Moolenaar *xbp->xb_curp++ = wc; 241131337658SMarcel Moolenaar goto done_with_encoding; 2412d1a0d267SMarcel Moolenaar 2413d1a0d267SMarcel Moolenaar case XO_STYLE_SDPARAMS: 2414d1a0d267SMarcel Moolenaar if (wc != '\\' && wc != '"' && wc != ']') 2415d1a0d267SMarcel Moolenaar break; 2416d1a0d267SMarcel Moolenaar 2417d1a0d267SMarcel Moolenaar if (!xo_buf_has_room(xbp, 2)) 2418d1a0d267SMarcel Moolenaar return -1; 2419d1a0d267SMarcel Moolenaar 2420d1a0d267SMarcel Moolenaar *xbp->xb_curp++ = '\\'; 2421d1a0d267SMarcel Moolenaar wc = wc & 0x7f; 2422d1a0d267SMarcel Moolenaar *xbp->xb_curp++ = wc; 2423d1a0d267SMarcel Moolenaar goto done_with_encoding; 242431337658SMarcel Moolenaar } 242531337658SMarcel Moolenaar 242631337658SMarcel Moolenaar olen = xo_utf8_emit_len(wc); 242731337658SMarcel Moolenaar if (olen < 0) { 242831337658SMarcel Moolenaar xo_failure(xop, "ignoring bad length"); 242931337658SMarcel Moolenaar continue; 243031337658SMarcel Moolenaar } 243131337658SMarcel Moolenaar 243231337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, olen)) 243331337658SMarcel Moolenaar return -1; 243431337658SMarcel Moolenaar 243531337658SMarcel Moolenaar xo_utf8_emit_char(xbp->xb_curp, olen, wc); 243631337658SMarcel Moolenaar xbp->xb_curp += olen; 243731337658SMarcel Moolenaar break; 243831337658SMarcel Moolenaar 243931337658SMarcel Moolenaar case XF_ENC_LOCALE: 244031337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, MB_LEN_MAX + 1)) 244131337658SMarcel Moolenaar return -1; 244231337658SMarcel Moolenaar 244331337658SMarcel Moolenaar olen = wcrtomb(xbp->xb_curp, wc, &xop->xo_mbstate); 244431337658SMarcel Moolenaar if (olen <= 0) { 244531337658SMarcel Moolenaar xo_failure(xop, "could not convert wide char: %lx", 244631337658SMarcel Moolenaar (unsigned long) wc); 244731337658SMarcel Moolenaar width = 1; 244831337658SMarcel Moolenaar *xbp->xb_curp++ = '?'; 244931337658SMarcel Moolenaar } else 245031337658SMarcel Moolenaar xbp->xb_curp += olen; 245131337658SMarcel Moolenaar break; 245231337658SMarcel Moolenaar } 245331337658SMarcel Moolenaar 245431337658SMarcel Moolenaar done_with_encoding: 245531337658SMarcel Moolenaar cols += width; 245631337658SMarcel Moolenaar } 245731337658SMarcel Moolenaar 245831337658SMarcel Moolenaar return cols; 245931337658SMarcel Moolenaar } 246031337658SMarcel Moolenaar 246131337658SMarcel Moolenaar static int 2462d1a0d267SMarcel Moolenaar xo_needed_encoding (xo_handle_t *xop) 2463d1a0d267SMarcel Moolenaar { 2464d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_UTF8)) /* Check the override flag */ 2465d1a0d267SMarcel Moolenaar return XF_ENC_UTF8; 2466d1a0d267SMarcel Moolenaar 2467d1a0d267SMarcel Moolenaar if (xo_style(xop) == XO_STYLE_TEXT) /* Text means locale */ 2468d1a0d267SMarcel Moolenaar return XF_ENC_LOCALE; 2469d1a0d267SMarcel Moolenaar 2470d1a0d267SMarcel Moolenaar return XF_ENC_UTF8; /* Otherwise, we love UTF-8 */ 2471d1a0d267SMarcel Moolenaar } 2472d1a0d267SMarcel Moolenaar 2473d1a0d267SMarcel Moolenaar static int 247431337658SMarcel Moolenaar xo_format_string (xo_handle_t *xop, xo_buffer_t *xbp, xo_xff_flags_t flags, 247531337658SMarcel Moolenaar xo_format_t *xfp) 247631337658SMarcel Moolenaar { 247731337658SMarcel Moolenaar static char null[] = "(null)"; 2478d1a0d267SMarcel Moolenaar static char null_no_quotes[] = "null"; 2479a0f704ffSMarcel Moolenaar 248031337658SMarcel Moolenaar char *cp = NULL; 248131337658SMarcel Moolenaar wchar_t *wcp = NULL; 248231337658SMarcel Moolenaar int len, cols = 0, rc = 0; 248331337658SMarcel Moolenaar int off = xbp->xb_curp - xbp->xb_bufp, off2; 2484d1a0d267SMarcel Moolenaar int need_enc = xo_needed_encoding(xop); 248531337658SMarcel Moolenaar 248631337658SMarcel Moolenaar if (xo_check_conversion(xop, xfp->xf_enc, need_enc)) 248731337658SMarcel Moolenaar return 0; 248831337658SMarcel Moolenaar 2489a0f704ffSMarcel Moolenaar len = xfp->xf_width[XF_WIDTH_SIZE]; 2490a0f704ffSMarcel Moolenaar 2491d1a0d267SMarcel Moolenaar if (xfp->xf_fc == 'm') { 2492d1a0d267SMarcel Moolenaar cp = strerror(xop->xo_errno); 2493d1a0d267SMarcel Moolenaar if (len < 0) 2494d1a0d267SMarcel Moolenaar len = cp ? strlen(cp) : 0; 2495d1a0d267SMarcel Moolenaar goto normal_string; 2496d1a0d267SMarcel Moolenaar 2497d1a0d267SMarcel Moolenaar } else if (xfp->xf_enc == XF_ENC_WIDE) { 249831337658SMarcel Moolenaar wcp = va_arg(xop->xo_vap, wchar_t *); 249931337658SMarcel Moolenaar if (xfp->xf_skip) 250031337658SMarcel Moolenaar return 0; 250131337658SMarcel Moolenaar 2502a0f704ffSMarcel Moolenaar /* 2503a0f704ffSMarcel Moolenaar * Dont' deref NULL; use the traditional "(null)" instead 2504a0f704ffSMarcel Moolenaar * of the more accurate "who's been a naughty boy, then?". 2505a0f704ffSMarcel Moolenaar */ 2506a0f704ffSMarcel Moolenaar if (wcp == NULL) { 2507a0f704ffSMarcel Moolenaar cp = null; 2508a0f704ffSMarcel Moolenaar len = sizeof(null) - 1; 2509a0f704ffSMarcel Moolenaar } 2510a0f704ffSMarcel Moolenaar 251131337658SMarcel Moolenaar } else { 251231337658SMarcel Moolenaar cp = va_arg(xop->xo_vap, char *); /* UTF-8 or native */ 2513d1a0d267SMarcel Moolenaar 2514d1a0d267SMarcel Moolenaar normal_string: 251531337658SMarcel Moolenaar if (xfp->xf_skip) 251631337658SMarcel Moolenaar return 0; 251731337658SMarcel Moolenaar 2518a0f704ffSMarcel Moolenaar /* Echo "Dont' deref NULL" logic */ 2519a0f704ffSMarcel Moolenaar if (cp == NULL) { 2520d1a0d267SMarcel Moolenaar if ((flags & XFF_NOQUOTE) && xo_style_is_encoding(xop)) { 2521d1a0d267SMarcel Moolenaar cp = null_no_quotes; 2522d1a0d267SMarcel Moolenaar len = sizeof(null_no_quotes) - 1; 2523d1a0d267SMarcel Moolenaar } else { 2524a0f704ffSMarcel Moolenaar cp = null; 2525a0f704ffSMarcel Moolenaar len = sizeof(null) - 1; 2526a0f704ffSMarcel Moolenaar } 2527d1a0d267SMarcel Moolenaar } 2528a0f704ffSMarcel Moolenaar 252931337658SMarcel Moolenaar /* 253031337658SMarcel Moolenaar * Optimize the most common case, which is "%s". We just 253131337658SMarcel Moolenaar * need to copy the complete string to the output buffer. 253231337658SMarcel Moolenaar */ 253331337658SMarcel Moolenaar if (xfp->xf_enc == need_enc 253431337658SMarcel Moolenaar && xfp->xf_width[XF_WIDTH_MIN] < 0 253531337658SMarcel Moolenaar && xfp->xf_width[XF_WIDTH_SIZE] < 0 253631337658SMarcel Moolenaar && xfp->xf_width[XF_WIDTH_MAX] < 0 2537d1a0d267SMarcel Moolenaar && !(XOIF_ISSET(xop, XOIF_ANCHOR) 2538d1a0d267SMarcel Moolenaar || XOF_ISSET(xop, XOF_COLUMNS))) { 253931337658SMarcel Moolenaar len = strlen(cp); 254031337658SMarcel Moolenaar xo_buf_escape(xop, xbp, cp, len, flags); 254131337658SMarcel Moolenaar 254231337658SMarcel Moolenaar /* 254331337658SMarcel Moolenaar * Our caller expects xb_curp left untouched, so we have 254431337658SMarcel Moolenaar * to reset it and return the number of bytes written to 254531337658SMarcel Moolenaar * the buffer. 254631337658SMarcel Moolenaar */ 254731337658SMarcel Moolenaar off2 = xbp->xb_curp - xbp->xb_bufp; 254831337658SMarcel Moolenaar rc = off2 - off; 254931337658SMarcel Moolenaar xbp->xb_curp = xbp->xb_bufp + off; 255031337658SMarcel Moolenaar 255131337658SMarcel Moolenaar return rc; 255231337658SMarcel Moolenaar } 255331337658SMarcel Moolenaar } 255431337658SMarcel Moolenaar 255531337658SMarcel Moolenaar cols = xo_format_string_direct(xop, xbp, flags, wcp, cp, len, 255631337658SMarcel Moolenaar xfp->xf_width[XF_WIDTH_MAX], 255731337658SMarcel Moolenaar need_enc, xfp->xf_enc); 255831337658SMarcel Moolenaar if (cols < 0) 255931337658SMarcel Moolenaar goto bail; 256031337658SMarcel Moolenaar 256131337658SMarcel Moolenaar /* 256231337658SMarcel Moolenaar * xo_buf_append* will move xb_curp, so we save/restore it. 256331337658SMarcel Moolenaar */ 256431337658SMarcel Moolenaar off2 = xbp->xb_curp - xbp->xb_bufp; 256531337658SMarcel Moolenaar rc = off2 - off; 256631337658SMarcel Moolenaar xbp->xb_curp = xbp->xb_bufp + off; 256731337658SMarcel Moolenaar 256831337658SMarcel Moolenaar if (cols < xfp->xf_width[XF_WIDTH_MIN]) { 256931337658SMarcel Moolenaar /* 257031337658SMarcel Moolenaar * Find the number of columns needed to display the string. 257131337658SMarcel Moolenaar * If we have the original wide string, we just call wcswidth, 257231337658SMarcel Moolenaar * but if we did the work ourselves, then we need to do it. 257331337658SMarcel Moolenaar */ 257431337658SMarcel Moolenaar int delta = xfp->xf_width[XF_WIDTH_MIN] - cols; 257531337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, delta)) 257631337658SMarcel Moolenaar goto bail; 257731337658SMarcel Moolenaar 257831337658SMarcel Moolenaar /* 257931337658SMarcel Moolenaar * If seen_minus, then pad on the right; otherwise move it so 258031337658SMarcel Moolenaar * we can pad on the left. 258131337658SMarcel Moolenaar */ 258231337658SMarcel Moolenaar if (xfp->xf_seen_minus) { 258331337658SMarcel Moolenaar cp = xbp->xb_curp + rc; 258431337658SMarcel Moolenaar } else { 258531337658SMarcel Moolenaar cp = xbp->xb_curp; 258631337658SMarcel Moolenaar memmove(xbp->xb_curp + delta, xbp->xb_curp, rc); 258731337658SMarcel Moolenaar } 258831337658SMarcel Moolenaar 258931337658SMarcel Moolenaar /* Set the padding */ 259031337658SMarcel Moolenaar memset(cp, (xfp->xf_leading_zero > 0) ? '0' : ' ', delta); 259131337658SMarcel Moolenaar rc += delta; 259231337658SMarcel Moolenaar cols += delta; 259331337658SMarcel Moolenaar } 259431337658SMarcel Moolenaar 2595d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_COLUMNS)) 259631337658SMarcel Moolenaar xop->xo_columns += cols; 2597d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_ANCHOR)) 259831337658SMarcel Moolenaar xop->xo_anchor_columns += cols; 259931337658SMarcel Moolenaar 260031337658SMarcel Moolenaar return rc; 260131337658SMarcel Moolenaar 260231337658SMarcel Moolenaar bail: 260331337658SMarcel Moolenaar xbp->xb_curp = xbp->xb_bufp + off; 260431337658SMarcel Moolenaar return 0; 260531337658SMarcel Moolenaar } 260631337658SMarcel Moolenaar 2607d1a0d267SMarcel Moolenaar /* 2608d1a0d267SMarcel Moolenaar * Look backwards in a buffer to find a numeric value 2609d1a0d267SMarcel Moolenaar */ 2610d1a0d267SMarcel Moolenaar static int 2611d1a0d267SMarcel Moolenaar xo_buf_find_last_number (xo_buffer_t *xbp, int start_offset) 2612d1a0d267SMarcel Moolenaar { 2613d1a0d267SMarcel Moolenaar int rc = 0; /* Fail with zero */ 2614d1a0d267SMarcel Moolenaar int digit = 1; 2615d1a0d267SMarcel Moolenaar char *sp = xbp->xb_bufp; 2616d1a0d267SMarcel Moolenaar char *cp = sp + start_offset; 2617d1a0d267SMarcel Moolenaar 2618d1a0d267SMarcel Moolenaar while (--cp >= sp) 2619d1a0d267SMarcel Moolenaar if (isdigit((int) *cp)) 2620d1a0d267SMarcel Moolenaar break; 2621d1a0d267SMarcel Moolenaar 2622d1a0d267SMarcel Moolenaar for ( ; cp >= sp; cp--) { 2623d1a0d267SMarcel Moolenaar if (!isdigit((int) *cp)) 2624d1a0d267SMarcel Moolenaar break; 2625d1a0d267SMarcel Moolenaar rc += (*cp - '0') * digit; 2626d1a0d267SMarcel Moolenaar digit *= 10; 2627d1a0d267SMarcel Moolenaar } 2628d1a0d267SMarcel Moolenaar 2629d1a0d267SMarcel Moolenaar return rc; 2630d1a0d267SMarcel Moolenaar } 2631d1a0d267SMarcel Moolenaar 2632d1a0d267SMarcel Moolenaar static int 2633d1a0d267SMarcel Moolenaar xo_count_utf8_cols (const char *str, int len) 2634d1a0d267SMarcel Moolenaar { 2635d1a0d267SMarcel Moolenaar int tlen; 2636d1a0d267SMarcel Moolenaar wchar_t wc; 2637d1a0d267SMarcel Moolenaar int cols = 0; 2638d1a0d267SMarcel Moolenaar const char *ep = str + len; 2639d1a0d267SMarcel Moolenaar 2640d1a0d267SMarcel Moolenaar while (str < ep) { 2641d1a0d267SMarcel Moolenaar tlen = xo_utf8_to_wc_len(str); 2642d1a0d267SMarcel Moolenaar if (tlen < 0) /* Broken input is very bad */ 2643d1a0d267SMarcel Moolenaar return cols; 2644d1a0d267SMarcel Moolenaar 2645d1a0d267SMarcel Moolenaar wc = xo_utf8_char(str, tlen); 2646d1a0d267SMarcel Moolenaar if (wc == (wchar_t) -1) 2647d1a0d267SMarcel Moolenaar return cols; 2648d1a0d267SMarcel Moolenaar 2649d1a0d267SMarcel Moolenaar /* We only print printable characters */ 2650d1a0d267SMarcel Moolenaar if (iswprint((wint_t) wc)) { 2651d1a0d267SMarcel Moolenaar /* 2652d1a0d267SMarcel Moolenaar * Find the width-in-columns of this character, which must be done 2653d1a0d267SMarcel Moolenaar * in wide characters, since we lack a mbswidth() function. 2654d1a0d267SMarcel Moolenaar */ 2655d1a0d267SMarcel Moolenaar int width = xo_wcwidth(wc); 2656d1a0d267SMarcel Moolenaar if (width < 0) 2657d1a0d267SMarcel Moolenaar width = iswcntrl(wc) ? 0 : 1; 2658d1a0d267SMarcel Moolenaar 2659d1a0d267SMarcel Moolenaar cols += width; 2660d1a0d267SMarcel Moolenaar } 2661d1a0d267SMarcel Moolenaar 2662d1a0d267SMarcel Moolenaar str += tlen; 2663d1a0d267SMarcel Moolenaar } 2664d1a0d267SMarcel Moolenaar 2665d1a0d267SMarcel Moolenaar return cols; 2666d1a0d267SMarcel Moolenaar } 2667d1a0d267SMarcel Moolenaar 2668d1a0d267SMarcel Moolenaar #ifdef HAVE_GETTEXT 2669d1a0d267SMarcel Moolenaar static inline const char * 2670d1a0d267SMarcel Moolenaar xo_dgettext (xo_handle_t *xop, const char *str) 2671d1a0d267SMarcel Moolenaar { 2672d1a0d267SMarcel Moolenaar const char *domainname = xop->xo_gt_domain; 2673d1a0d267SMarcel Moolenaar const char *res; 2674d1a0d267SMarcel Moolenaar 2675d1a0d267SMarcel Moolenaar res = dgettext(domainname, str); 2676d1a0d267SMarcel Moolenaar 2677d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_LOG_GETTEXT)) 2678d1a0d267SMarcel Moolenaar fprintf(stderr, "xo: gettext: %s%s%smsgid \"%s\" returns \"%s\"\n", 2679d1a0d267SMarcel Moolenaar domainname ? "domain \"" : "", xo_printable(domainname), 2680d1a0d267SMarcel Moolenaar domainname ? "\", " : "", xo_printable(str), xo_printable(res)); 2681d1a0d267SMarcel Moolenaar 2682d1a0d267SMarcel Moolenaar return res; 2683d1a0d267SMarcel Moolenaar } 2684d1a0d267SMarcel Moolenaar 2685d1a0d267SMarcel Moolenaar static inline const char * 2686d1a0d267SMarcel Moolenaar xo_dngettext (xo_handle_t *xop, const char *sing, const char *plural, 2687d1a0d267SMarcel Moolenaar unsigned long int n) 2688d1a0d267SMarcel Moolenaar { 2689d1a0d267SMarcel Moolenaar const char *domainname = xop->xo_gt_domain; 2690d1a0d267SMarcel Moolenaar const char *res; 2691d1a0d267SMarcel Moolenaar 2692d1a0d267SMarcel Moolenaar res = dngettext(domainname, sing, plural, n); 2693d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_LOG_GETTEXT)) 2694d1a0d267SMarcel Moolenaar fprintf(stderr, "xo: gettext: %s%s%s" 2695d1a0d267SMarcel Moolenaar "msgid \"%s\", msgid_plural \"%s\" (%lu) returns \"%s\"\n", 2696d1a0d267SMarcel Moolenaar domainname ? "domain \"" : "", 2697d1a0d267SMarcel Moolenaar xo_printable(domainname), domainname ? "\", " : "", 2698d1a0d267SMarcel Moolenaar xo_printable(sing), 2699d1a0d267SMarcel Moolenaar xo_printable(plural), n, xo_printable(res)); 2700d1a0d267SMarcel Moolenaar 2701d1a0d267SMarcel Moolenaar return res; 2702d1a0d267SMarcel Moolenaar } 2703d1a0d267SMarcel Moolenaar #else /* HAVE_GETTEXT */ 2704d1a0d267SMarcel Moolenaar static inline const char * 2705d1a0d267SMarcel Moolenaar xo_dgettext (xo_handle_t *xop UNUSED, const char *str) 2706d1a0d267SMarcel Moolenaar { 2707d1a0d267SMarcel Moolenaar return str; 2708d1a0d267SMarcel Moolenaar } 2709d1a0d267SMarcel Moolenaar 2710d1a0d267SMarcel Moolenaar static inline const char * 2711d1a0d267SMarcel Moolenaar xo_dngettext (xo_handle_t *xop UNUSED, const char *singular, 2712d1a0d267SMarcel Moolenaar const char *plural, unsigned long int n) 2713d1a0d267SMarcel Moolenaar { 2714d1a0d267SMarcel Moolenaar return (n == 1) ? singular : plural; 2715d1a0d267SMarcel Moolenaar } 2716d1a0d267SMarcel Moolenaar #endif /* HAVE_GETTEXT */ 2717d1a0d267SMarcel Moolenaar 2718d1a0d267SMarcel Moolenaar /* 2719d1a0d267SMarcel Moolenaar * This is really _re_formatting, since the normal format code has 2720d1a0d267SMarcel Moolenaar * generated a beautiful string into xo_data, starting at 2721d1a0d267SMarcel Moolenaar * start_offset. We need to see if it's plural, which means 2722d1a0d267SMarcel Moolenaar * comma-separated options, or singular. Then we make the appropriate 2723d1a0d267SMarcel Moolenaar * call to d[n]gettext() to get the locale-based version. Note that 2724d1a0d267SMarcel Moolenaar * both input and output of gettext() this should be UTF-8. 2725d1a0d267SMarcel Moolenaar */ 2726d1a0d267SMarcel Moolenaar static int 2727d1a0d267SMarcel Moolenaar xo_format_gettext (xo_handle_t *xop, xo_xff_flags_t flags, 2728d1a0d267SMarcel Moolenaar int start_offset, int cols, int need_enc) 2729d1a0d267SMarcel Moolenaar { 2730d1a0d267SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_data; 2731d1a0d267SMarcel Moolenaar 2732d1a0d267SMarcel Moolenaar if (!xo_buf_has_room(xbp, 1)) 2733d1a0d267SMarcel Moolenaar return cols; 2734d1a0d267SMarcel Moolenaar 2735d1a0d267SMarcel Moolenaar xbp->xb_curp[0] = '\0'; /* NUL-terminate the input string */ 2736d1a0d267SMarcel Moolenaar 2737d1a0d267SMarcel Moolenaar char *cp = xbp->xb_bufp + start_offset; 2738d1a0d267SMarcel Moolenaar int len = xbp->xb_curp - cp; 2739d1a0d267SMarcel Moolenaar const char *newstr = NULL; 2740d1a0d267SMarcel Moolenaar 2741d1a0d267SMarcel Moolenaar /* 2742d1a0d267SMarcel Moolenaar * The plural flag asks us to look backwards at the last numeric 2743d1a0d267SMarcel Moolenaar * value rendered and disect the string into two pieces. 2744d1a0d267SMarcel Moolenaar */ 2745d1a0d267SMarcel Moolenaar if (flags & XFF_GT_PLURAL) { 2746d1a0d267SMarcel Moolenaar int n = xo_buf_find_last_number(xbp, start_offset); 2747d1a0d267SMarcel Moolenaar char *two = memchr(cp, (int) ',', len); 2748d1a0d267SMarcel Moolenaar if (two == NULL) { 2749d1a0d267SMarcel Moolenaar xo_failure(xop, "no comma in plural gettext field: '%s'", cp); 2750d1a0d267SMarcel Moolenaar return cols; 2751d1a0d267SMarcel Moolenaar } 2752d1a0d267SMarcel Moolenaar 2753d1a0d267SMarcel Moolenaar if (two == cp) { 2754d1a0d267SMarcel Moolenaar xo_failure(xop, "nothing before comma in plural gettext " 2755d1a0d267SMarcel Moolenaar "field: '%s'", cp); 2756d1a0d267SMarcel Moolenaar return cols; 2757d1a0d267SMarcel Moolenaar } 2758d1a0d267SMarcel Moolenaar 2759d1a0d267SMarcel Moolenaar if (two == xbp->xb_curp) { 2760d1a0d267SMarcel Moolenaar xo_failure(xop, "nothing after comma in plural gettext " 2761d1a0d267SMarcel Moolenaar "field: '%s'", cp); 2762d1a0d267SMarcel Moolenaar return cols; 2763d1a0d267SMarcel Moolenaar } 2764d1a0d267SMarcel Moolenaar 2765d1a0d267SMarcel Moolenaar *two++ = '\0'; 2766d1a0d267SMarcel Moolenaar if (flags & XFF_GT_FIELD) { 2767d1a0d267SMarcel Moolenaar newstr = xo_dngettext(xop, cp, two, n); 2768d1a0d267SMarcel Moolenaar } else { 2769d1a0d267SMarcel Moolenaar /* Don't do a gettext() look up, just get the plural form */ 2770d1a0d267SMarcel Moolenaar newstr = (n == 1) ? cp : two; 2771d1a0d267SMarcel Moolenaar } 2772d1a0d267SMarcel Moolenaar 2773d1a0d267SMarcel Moolenaar /* 2774d1a0d267SMarcel Moolenaar * If we returned the first string, optimize a bit by 2775d1a0d267SMarcel Moolenaar * backing up over comma 2776d1a0d267SMarcel Moolenaar */ 2777d1a0d267SMarcel Moolenaar if (newstr == cp) { 2778d1a0d267SMarcel Moolenaar xbp->xb_curp = two - 1; /* One for comma */ 2779d1a0d267SMarcel Moolenaar /* 2780d1a0d267SMarcel Moolenaar * If the caller wanted UTF8, we're done; nothing changed, 2781d1a0d267SMarcel Moolenaar * but we need to count the columns used. 2782d1a0d267SMarcel Moolenaar */ 2783d1a0d267SMarcel Moolenaar if (need_enc == XF_ENC_UTF8) 2784d1a0d267SMarcel Moolenaar return xo_count_utf8_cols(cp, xbp->xb_curp - cp); 2785d1a0d267SMarcel Moolenaar } 2786d1a0d267SMarcel Moolenaar 2787d1a0d267SMarcel Moolenaar } else { 2788d1a0d267SMarcel Moolenaar /* The simple case (singular) */ 2789d1a0d267SMarcel Moolenaar newstr = xo_dgettext(xop, cp); 2790d1a0d267SMarcel Moolenaar 2791d1a0d267SMarcel Moolenaar if (newstr == cp) { 2792d1a0d267SMarcel Moolenaar /* If the caller wanted UTF8, we're done; nothing changed */ 2793d1a0d267SMarcel Moolenaar if (need_enc == XF_ENC_UTF8) 2794d1a0d267SMarcel Moolenaar return cols; 2795d1a0d267SMarcel Moolenaar } 2796d1a0d267SMarcel Moolenaar } 2797d1a0d267SMarcel Moolenaar 2798d1a0d267SMarcel Moolenaar /* 2799d1a0d267SMarcel Moolenaar * Since the new string string might be in gettext's buffer or 2800d1a0d267SMarcel Moolenaar * in the buffer (as the plural form), we make a copy. 2801d1a0d267SMarcel Moolenaar */ 2802d1a0d267SMarcel Moolenaar int nlen = strlen(newstr); 2803d1a0d267SMarcel Moolenaar char *newcopy = alloca(nlen + 1); 2804d1a0d267SMarcel Moolenaar memcpy(newcopy, newstr, nlen + 1); 2805d1a0d267SMarcel Moolenaar 2806d1a0d267SMarcel Moolenaar xbp->xb_curp = xbp->xb_bufp + start_offset; /* Reset the buffer */ 2807d1a0d267SMarcel Moolenaar return xo_format_string_direct(xop, xbp, flags, NULL, newcopy, nlen, 0, 2808d1a0d267SMarcel Moolenaar need_enc, XF_ENC_UTF8); 2809d1a0d267SMarcel Moolenaar } 2810d1a0d267SMarcel Moolenaar 281131337658SMarcel Moolenaar static void 2812d1a0d267SMarcel Moolenaar xo_data_append_content (xo_handle_t *xop, const char *str, int len, 2813d1a0d267SMarcel Moolenaar xo_xff_flags_t flags) 281431337658SMarcel Moolenaar { 281531337658SMarcel Moolenaar int cols; 2816d1a0d267SMarcel Moolenaar int need_enc = xo_needed_encoding(xop); 2817d1a0d267SMarcel Moolenaar int start_offset = xo_buf_offset(&xop->xo_data); 281831337658SMarcel Moolenaar 2819d1a0d267SMarcel Moolenaar cols = xo_format_string_direct(xop, &xop->xo_data, XFF_UNESCAPE | flags, 282031337658SMarcel Moolenaar NULL, str, len, -1, 282131337658SMarcel Moolenaar need_enc, XF_ENC_UTF8); 2822d1a0d267SMarcel Moolenaar if (flags & XFF_GT_FLAGS) 2823d1a0d267SMarcel Moolenaar cols = xo_format_gettext(xop, flags, start_offset, cols, need_enc); 282431337658SMarcel Moolenaar 2825d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_COLUMNS)) 282631337658SMarcel Moolenaar xop->xo_columns += cols; 2827d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_ANCHOR)) 282831337658SMarcel Moolenaar xop->xo_anchor_columns += cols; 282931337658SMarcel Moolenaar } 283031337658SMarcel Moolenaar 283131337658SMarcel Moolenaar static void 283231337658SMarcel Moolenaar xo_bump_width (xo_format_t *xfp, int digit) 283331337658SMarcel Moolenaar { 283431337658SMarcel Moolenaar int *ip = &xfp->xf_width[xfp->xf_dots]; 283531337658SMarcel Moolenaar 283631337658SMarcel Moolenaar *ip = ((*ip > 0) ? *ip : 0) * 10 + digit; 283731337658SMarcel Moolenaar } 283831337658SMarcel Moolenaar 283931337658SMarcel Moolenaar static int 284031337658SMarcel Moolenaar xo_trim_ws (xo_buffer_t *xbp, int len) 284131337658SMarcel Moolenaar { 284231337658SMarcel Moolenaar char *cp, *sp, *ep; 284331337658SMarcel Moolenaar int delta; 284431337658SMarcel Moolenaar 284531337658SMarcel Moolenaar /* First trim leading space */ 284631337658SMarcel Moolenaar for (cp = sp = xbp->xb_curp, ep = cp + len; cp < ep; cp++) { 284731337658SMarcel Moolenaar if (*cp != ' ') 284831337658SMarcel Moolenaar break; 284931337658SMarcel Moolenaar } 285031337658SMarcel Moolenaar 285131337658SMarcel Moolenaar delta = cp - sp; 285231337658SMarcel Moolenaar if (delta) { 285331337658SMarcel Moolenaar len -= delta; 285431337658SMarcel Moolenaar memmove(sp, cp, len); 285531337658SMarcel Moolenaar } 285631337658SMarcel Moolenaar 285731337658SMarcel Moolenaar /* Then trim off the end */ 285831337658SMarcel Moolenaar for (cp = xbp->xb_curp, sp = ep = cp + len; cp < ep; ep--) { 285931337658SMarcel Moolenaar if (ep[-1] != ' ') 286031337658SMarcel Moolenaar break; 286131337658SMarcel Moolenaar } 286231337658SMarcel Moolenaar 286331337658SMarcel Moolenaar delta = sp - ep; 286431337658SMarcel Moolenaar if (delta) { 286531337658SMarcel Moolenaar len -= delta; 286631337658SMarcel Moolenaar cp[len] = '\0'; 286731337658SMarcel Moolenaar } 286831337658SMarcel Moolenaar 286931337658SMarcel Moolenaar return len; 287031337658SMarcel Moolenaar } 287131337658SMarcel Moolenaar 2872d1a0d267SMarcel Moolenaar /* 2873d1a0d267SMarcel Moolenaar * Interface to format a single field. The arguments are in xo_vap, 2874d1a0d267SMarcel Moolenaar * and the format is in 'fmt'. If 'xbp' is null, we use xop->xo_data; 2875d1a0d267SMarcel Moolenaar * this is the most common case. 2876d1a0d267SMarcel Moolenaar */ 287731337658SMarcel Moolenaar static int 2878d1a0d267SMarcel Moolenaar xo_do_format_field (xo_handle_t *xop, xo_buffer_t *xbp, 287931337658SMarcel Moolenaar const char *fmt, int flen, xo_xff_flags_t flags) 288031337658SMarcel Moolenaar { 288131337658SMarcel Moolenaar xo_format_t xf; 288231337658SMarcel Moolenaar const char *cp, *ep, *sp, *xp = NULL; 288331337658SMarcel Moolenaar int rc, cols; 2884788ca347SMarcel Moolenaar int style = (flags & XFF_XML) ? XO_STYLE_XML : xo_style(xop); 288531337658SMarcel Moolenaar unsigned make_output = !(flags & XFF_NO_OUTPUT); 2886d1a0d267SMarcel Moolenaar int need_enc = xo_needed_encoding(xop); 2887d1a0d267SMarcel Moolenaar int real_need_enc = need_enc; 2888d1a0d267SMarcel Moolenaar int old_cols = xop->xo_columns; 2889d1a0d267SMarcel Moolenaar 2890d1a0d267SMarcel Moolenaar /* The gettext interface is UTF-8, so we'll need that for now */ 2891d1a0d267SMarcel Moolenaar if (flags & XFF_GT_FIELD) 2892d1a0d267SMarcel Moolenaar need_enc = XF_ENC_UTF8; 289331337658SMarcel Moolenaar 289431337658SMarcel Moolenaar if (xbp == NULL) 289531337658SMarcel Moolenaar xbp = &xop->xo_data; 289631337658SMarcel Moolenaar 2897d1a0d267SMarcel Moolenaar unsigned start_offset = xo_buf_offset(xbp); 2898d1a0d267SMarcel Moolenaar 289931337658SMarcel Moolenaar for (cp = fmt, ep = fmt + flen; cp < ep; cp++) { 2900d1a0d267SMarcel Moolenaar /* 2901d1a0d267SMarcel Moolenaar * Since we're starting a new field, save the starting offset. 2902d1a0d267SMarcel Moolenaar * We'll need this later for field-related operations. 2903d1a0d267SMarcel Moolenaar */ 2904d1a0d267SMarcel Moolenaar 290531337658SMarcel Moolenaar if (*cp != '%') { 290631337658SMarcel Moolenaar add_one: 290731337658SMarcel Moolenaar if (xp == NULL) 290831337658SMarcel Moolenaar xp = cp; 290931337658SMarcel Moolenaar 291031337658SMarcel Moolenaar if (*cp == '\\' && cp[1] != '\0') 291131337658SMarcel Moolenaar cp += 1; 291231337658SMarcel Moolenaar continue; 291331337658SMarcel Moolenaar 291431337658SMarcel Moolenaar } if (cp + 1 < ep && cp[1] == '%') { 291531337658SMarcel Moolenaar cp += 1; 291631337658SMarcel Moolenaar goto add_one; 291731337658SMarcel Moolenaar } 291831337658SMarcel Moolenaar 291931337658SMarcel Moolenaar if (xp) { 292031337658SMarcel Moolenaar if (make_output) { 292131337658SMarcel Moolenaar cols = xo_format_string_direct(xop, xbp, flags | XFF_UNESCAPE, 292231337658SMarcel Moolenaar NULL, xp, cp - xp, -1, 292331337658SMarcel Moolenaar need_enc, XF_ENC_UTF8); 2924d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_COLUMNS)) 292531337658SMarcel Moolenaar xop->xo_columns += cols; 2926d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_ANCHOR)) 292731337658SMarcel Moolenaar xop->xo_anchor_columns += cols; 292831337658SMarcel Moolenaar } 292931337658SMarcel Moolenaar 293031337658SMarcel Moolenaar xp = NULL; 293131337658SMarcel Moolenaar } 293231337658SMarcel Moolenaar 293331337658SMarcel Moolenaar bzero(&xf, sizeof(xf)); 293431337658SMarcel Moolenaar xf.xf_leading_zero = -1; 293531337658SMarcel Moolenaar xf.xf_width[0] = xf.xf_width[1] = xf.xf_width[2] = -1; 293631337658SMarcel Moolenaar 293731337658SMarcel Moolenaar /* 293831337658SMarcel Moolenaar * "%@" starts an XO-specific set of flags: 293931337658SMarcel Moolenaar * @X@ - XML-only field; ignored if style isn't XML 294031337658SMarcel Moolenaar */ 294131337658SMarcel Moolenaar if (cp[1] == '@') { 294231337658SMarcel Moolenaar for (cp += 2; cp < ep; cp++) { 294331337658SMarcel Moolenaar if (*cp == '@') { 294431337658SMarcel Moolenaar break; 294531337658SMarcel Moolenaar } 294631337658SMarcel Moolenaar if (*cp == '*') { 294731337658SMarcel Moolenaar /* 294831337658SMarcel Moolenaar * '*' means there's a "%*.*s" value in vap that 294931337658SMarcel Moolenaar * we want to ignore 295031337658SMarcel Moolenaar */ 2951d1a0d267SMarcel Moolenaar if (!XOF_ISSET(xop, XOF_NO_VA_ARG)) 295231337658SMarcel Moolenaar va_arg(xop->xo_vap, int); 295331337658SMarcel Moolenaar } 295431337658SMarcel Moolenaar } 295531337658SMarcel Moolenaar } 295631337658SMarcel Moolenaar 295731337658SMarcel Moolenaar /* Hidden fields are only visible to JSON and XML */ 2958d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XFF_ENCODE_ONLY)) { 295931337658SMarcel Moolenaar if (style != XO_STYLE_XML 2960d1a0d267SMarcel Moolenaar && !xo_style_is_encoding(xop)) 296131337658SMarcel Moolenaar xf.xf_skip = 1; 2962d1a0d267SMarcel Moolenaar } else if (XOF_ISSET(xop, XFF_DISPLAY_ONLY)) { 296331337658SMarcel Moolenaar if (style != XO_STYLE_TEXT 2964788ca347SMarcel Moolenaar && xo_style(xop) != XO_STYLE_HTML) 296531337658SMarcel Moolenaar xf.xf_skip = 1; 296631337658SMarcel Moolenaar } 296731337658SMarcel Moolenaar 296831337658SMarcel Moolenaar if (!make_output) 296931337658SMarcel Moolenaar xf.xf_skip = 1; 297031337658SMarcel Moolenaar 297131337658SMarcel Moolenaar /* 297231337658SMarcel Moolenaar * Looking at one piece of a format; find the end and 297331337658SMarcel Moolenaar * call snprintf. Then advance xo_vap on our own. 297431337658SMarcel Moolenaar * 297531337658SMarcel Moolenaar * Note that 'n', 'v', and '$' are not supported. 297631337658SMarcel Moolenaar */ 297731337658SMarcel Moolenaar sp = cp; /* Save start pointer */ 297831337658SMarcel Moolenaar for (cp += 1; cp < ep; cp++) { 297931337658SMarcel Moolenaar if (*cp == 'l') 298031337658SMarcel Moolenaar xf.xf_lflag += 1; 298131337658SMarcel Moolenaar else if (*cp == 'h') 298231337658SMarcel Moolenaar xf.xf_hflag += 1; 298331337658SMarcel Moolenaar else if (*cp == 'j') 298431337658SMarcel Moolenaar xf.xf_jflag += 1; 298531337658SMarcel Moolenaar else if (*cp == 't') 298631337658SMarcel Moolenaar xf.xf_tflag += 1; 298731337658SMarcel Moolenaar else if (*cp == 'z') 298831337658SMarcel Moolenaar xf.xf_zflag += 1; 298931337658SMarcel Moolenaar else if (*cp == 'q') 299031337658SMarcel Moolenaar xf.xf_qflag += 1; 299131337658SMarcel Moolenaar else if (*cp == '.') { 299231337658SMarcel Moolenaar if (++xf.xf_dots >= XF_WIDTH_NUM) { 299331337658SMarcel Moolenaar xo_failure(xop, "Too many dots in format: '%s'", fmt); 299431337658SMarcel Moolenaar return -1; 299531337658SMarcel Moolenaar } 299631337658SMarcel Moolenaar } else if (*cp == '-') 299731337658SMarcel Moolenaar xf.xf_seen_minus = 1; 299831337658SMarcel Moolenaar else if (isdigit((int) *cp)) { 299931337658SMarcel Moolenaar if (xf.xf_leading_zero < 0) 300031337658SMarcel Moolenaar xf.xf_leading_zero = (*cp == '0'); 300131337658SMarcel Moolenaar xo_bump_width(&xf, *cp - '0'); 300231337658SMarcel Moolenaar } else if (*cp == '*') { 300331337658SMarcel Moolenaar xf.xf_stars += 1; 300431337658SMarcel Moolenaar xf.xf_star[xf.xf_dots] = 1; 3005d1a0d267SMarcel Moolenaar } else if (strchr("diouxXDOUeEfFgGaAcCsSpm", *cp) != NULL) 300631337658SMarcel Moolenaar break; 300731337658SMarcel Moolenaar else if (*cp == 'n' || *cp == 'v') { 300831337658SMarcel Moolenaar xo_failure(xop, "unsupported format: '%s'", fmt); 300931337658SMarcel Moolenaar return -1; 301031337658SMarcel Moolenaar } 301131337658SMarcel Moolenaar } 301231337658SMarcel Moolenaar 301331337658SMarcel Moolenaar if (cp == ep) 301431337658SMarcel Moolenaar xo_failure(xop, "field format missing format character: %s", 301531337658SMarcel Moolenaar fmt); 301631337658SMarcel Moolenaar 301731337658SMarcel Moolenaar xf.xf_fc = *cp; 301831337658SMarcel Moolenaar 3019d1a0d267SMarcel Moolenaar if (!XOF_ISSET(xop, XOF_NO_VA_ARG)) { 302031337658SMarcel Moolenaar if (*cp == 's' || *cp == 'S') { 302131337658SMarcel Moolenaar /* Handle "%*.*.*s" */ 302231337658SMarcel Moolenaar int s; 302331337658SMarcel Moolenaar for (s = 0; s < XF_WIDTH_NUM; s++) { 302431337658SMarcel Moolenaar if (xf.xf_star[s]) { 302531337658SMarcel Moolenaar xf.xf_width[s] = va_arg(xop->xo_vap, int); 302631337658SMarcel Moolenaar 302731337658SMarcel Moolenaar /* Normalize a negative width value */ 302831337658SMarcel Moolenaar if (xf.xf_width[s] < 0) { 302931337658SMarcel Moolenaar if (s == 0) { 303031337658SMarcel Moolenaar xf.xf_width[0] = -xf.xf_width[0]; 303131337658SMarcel Moolenaar xf.xf_seen_minus = 1; 303231337658SMarcel Moolenaar } else 303331337658SMarcel Moolenaar xf.xf_width[s] = -1; /* Ignore negative values */ 303431337658SMarcel Moolenaar } 303531337658SMarcel Moolenaar } 303631337658SMarcel Moolenaar } 303731337658SMarcel Moolenaar } 303831337658SMarcel Moolenaar } 303931337658SMarcel Moolenaar 304031337658SMarcel Moolenaar /* If no max is given, it defaults to size */ 304131337658SMarcel Moolenaar if (xf.xf_width[XF_WIDTH_MAX] < 0 && xf.xf_width[XF_WIDTH_SIZE] >= 0) 304231337658SMarcel Moolenaar xf.xf_width[XF_WIDTH_MAX] = xf.xf_width[XF_WIDTH_SIZE]; 304331337658SMarcel Moolenaar 304431337658SMarcel Moolenaar if (xf.xf_fc == 'D' || xf.xf_fc == 'O' || xf.xf_fc == 'U') 304531337658SMarcel Moolenaar xf.xf_lflag = 1; 304631337658SMarcel Moolenaar 304731337658SMarcel Moolenaar if (!xf.xf_skip) { 304831337658SMarcel Moolenaar xo_buffer_t *fbp = &xop->xo_fmt; 304931337658SMarcel Moolenaar int len = cp - sp + 1; 305031337658SMarcel Moolenaar if (!xo_buf_has_room(fbp, len + 1)) 305131337658SMarcel Moolenaar return -1; 305231337658SMarcel Moolenaar 305331337658SMarcel Moolenaar char *newfmt = fbp->xb_curp; 305431337658SMarcel Moolenaar memcpy(newfmt, sp, len); 305531337658SMarcel Moolenaar newfmt[0] = '%'; /* If we skipped over a "%@...@s" format */ 305631337658SMarcel Moolenaar newfmt[len] = '\0'; 305731337658SMarcel Moolenaar 305831337658SMarcel Moolenaar /* 305931337658SMarcel Moolenaar * Bad news: our strings are UTF-8, but the stock printf 306031337658SMarcel Moolenaar * functions won't handle field widths for wide characters 306131337658SMarcel Moolenaar * correctly. So we have to handle this ourselves. 306231337658SMarcel Moolenaar */ 306331337658SMarcel Moolenaar if (xop->xo_formatter == NULL 3064d1a0d267SMarcel Moolenaar && (xf.xf_fc == 's' || xf.xf_fc == 'S' 3065d1a0d267SMarcel Moolenaar || xf.xf_fc == 'm')) { 3066d1a0d267SMarcel Moolenaar 3067d1a0d267SMarcel Moolenaar xf.xf_enc = (xf.xf_fc == 'm') ? XF_ENC_UTF8 3068d1a0d267SMarcel Moolenaar : (xf.xf_lflag || (xf.xf_fc == 'S')) ? XF_ENC_WIDE 3069d1a0d267SMarcel Moolenaar : xf.xf_hflag ? XF_ENC_LOCALE : XF_ENC_UTF8; 3070d1a0d267SMarcel Moolenaar 307131337658SMarcel Moolenaar rc = xo_format_string(xop, xbp, flags, &xf); 307231337658SMarcel Moolenaar 3073d1a0d267SMarcel Moolenaar if ((flags & XFF_TRIM_WS) && xo_style_is_encoding(xop)) 307431337658SMarcel Moolenaar rc = xo_trim_ws(xbp, rc); 307531337658SMarcel Moolenaar 307631337658SMarcel Moolenaar } else { 307731337658SMarcel Moolenaar int columns = rc = xo_vsnprintf(xop, xbp, newfmt, xop->xo_vap); 307831337658SMarcel Moolenaar 307931337658SMarcel Moolenaar /* 308031337658SMarcel Moolenaar * For XML and HTML, we need "&<>" processing; for JSON, 308131337658SMarcel Moolenaar * it's quotes. Text gets nothing. 308231337658SMarcel Moolenaar */ 308331337658SMarcel Moolenaar switch (style) { 308431337658SMarcel Moolenaar case XO_STYLE_XML: 308531337658SMarcel Moolenaar if (flags & XFF_TRIM_WS) 308631337658SMarcel Moolenaar columns = rc = xo_trim_ws(xbp, rc); 308731337658SMarcel Moolenaar /* fall thru */ 308831337658SMarcel Moolenaar case XO_STYLE_HTML: 308931337658SMarcel Moolenaar rc = xo_escape_xml(xbp, rc, (flags & XFF_ATTR)); 309031337658SMarcel Moolenaar break; 309131337658SMarcel Moolenaar 309231337658SMarcel Moolenaar case XO_STYLE_JSON: 309331337658SMarcel Moolenaar if (flags & XFF_TRIM_WS) 309431337658SMarcel Moolenaar columns = rc = xo_trim_ws(xbp, rc); 3095d1a0d267SMarcel Moolenaar rc = xo_escape_json(xbp, rc, 0); 3096d1a0d267SMarcel Moolenaar break; 3097d1a0d267SMarcel Moolenaar 3098d1a0d267SMarcel Moolenaar case XO_STYLE_SDPARAMS: 3099d1a0d267SMarcel Moolenaar if (flags & XFF_TRIM_WS) 3100d1a0d267SMarcel Moolenaar columns = rc = xo_trim_ws(xbp, rc); 3101d1a0d267SMarcel Moolenaar rc = xo_escape_sdparams(xbp, rc, 0); 3102d1a0d267SMarcel Moolenaar break; 3103d1a0d267SMarcel Moolenaar 3104d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER: 3105d1a0d267SMarcel Moolenaar if (flags & XFF_TRIM_WS) 3106d1a0d267SMarcel Moolenaar columns = rc = xo_trim_ws(xbp, rc); 310731337658SMarcel Moolenaar break; 310831337658SMarcel Moolenaar } 310931337658SMarcel Moolenaar 311031337658SMarcel Moolenaar /* 3111d1a0d267SMarcel Moolenaar * We can assume all the non-%s data we've 3112d1a0d267SMarcel Moolenaar * added is ASCII, so the columns and bytes are the 3113d1a0d267SMarcel Moolenaar * same. xo_format_string handles all the fancy 3114d1a0d267SMarcel Moolenaar * string conversions and updates xo_anchor_columns 3115d1a0d267SMarcel Moolenaar * accordingly. 311631337658SMarcel Moolenaar */ 3117d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_COLUMNS)) 311831337658SMarcel Moolenaar xop->xo_columns += columns; 3119d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_ANCHOR)) 312031337658SMarcel Moolenaar xop->xo_anchor_columns += columns; 312131337658SMarcel Moolenaar } 312231337658SMarcel Moolenaar 312331337658SMarcel Moolenaar xbp->xb_curp += rc; 312431337658SMarcel Moolenaar } 312531337658SMarcel Moolenaar 312631337658SMarcel Moolenaar /* 312731337658SMarcel Moolenaar * Now for the tricky part: we need to move the argument pointer 312831337658SMarcel Moolenaar * along by the amount needed. 312931337658SMarcel Moolenaar */ 3130d1a0d267SMarcel Moolenaar if (!XOF_ISSET(xop, XOF_NO_VA_ARG)) { 313131337658SMarcel Moolenaar 313231337658SMarcel Moolenaar if (xf.xf_fc == 's' ||xf.xf_fc == 'S') { 313331337658SMarcel Moolenaar /* 313431337658SMarcel Moolenaar * The 'S' and 's' formats are normally handled in 313531337658SMarcel Moolenaar * xo_format_string, but if we skipped it, then we 313631337658SMarcel Moolenaar * need to pop it. 313731337658SMarcel Moolenaar */ 313831337658SMarcel Moolenaar if (xf.xf_skip) 313931337658SMarcel Moolenaar va_arg(xop->xo_vap, char *); 314031337658SMarcel Moolenaar 3141d1a0d267SMarcel Moolenaar } else if (xf.xf_fc == 'm') { 3142d1a0d267SMarcel Moolenaar /* Nothing on the stack for "%m" */ 3143d1a0d267SMarcel Moolenaar 314431337658SMarcel Moolenaar } else { 314531337658SMarcel Moolenaar int s; 314631337658SMarcel Moolenaar for (s = 0; s < XF_WIDTH_NUM; s++) { 314731337658SMarcel Moolenaar if (xf.xf_star[s]) 314831337658SMarcel Moolenaar va_arg(xop->xo_vap, int); 314931337658SMarcel Moolenaar } 315031337658SMarcel Moolenaar 315131337658SMarcel Moolenaar if (strchr("diouxXDOU", xf.xf_fc) != NULL) { 315231337658SMarcel Moolenaar if (xf.xf_hflag > 1) { 315331337658SMarcel Moolenaar va_arg(xop->xo_vap, int); 315431337658SMarcel Moolenaar 315531337658SMarcel Moolenaar } else if (xf.xf_hflag > 0) { 315631337658SMarcel Moolenaar va_arg(xop->xo_vap, int); 315731337658SMarcel Moolenaar 315831337658SMarcel Moolenaar } else if (xf.xf_lflag > 1) { 315931337658SMarcel Moolenaar va_arg(xop->xo_vap, unsigned long long); 316031337658SMarcel Moolenaar 316131337658SMarcel Moolenaar } else if (xf.xf_lflag > 0) { 316231337658SMarcel Moolenaar va_arg(xop->xo_vap, unsigned long); 316331337658SMarcel Moolenaar 316431337658SMarcel Moolenaar } else if (xf.xf_jflag > 0) { 316531337658SMarcel Moolenaar va_arg(xop->xo_vap, intmax_t); 316631337658SMarcel Moolenaar 316731337658SMarcel Moolenaar } else if (xf.xf_tflag > 0) { 316831337658SMarcel Moolenaar va_arg(xop->xo_vap, ptrdiff_t); 316931337658SMarcel Moolenaar 317031337658SMarcel Moolenaar } else if (xf.xf_zflag > 0) { 317131337658SMarcel Moolenaar va_arg(xop->xo_vap, size_t); 317231337658SMarcel Moolenaar 317331337658SMarcel Moolenaar } else if (xf.xf_qflag > 0) { 317431337658SMarcel Moolenaar va_arg(xop->xo_vap, quad_t); 317531337658SMarcel Moolenaar 317631337658SMarcel Moolenaar } else { 317731337658SMarcel Moolenaar va_arg(xop->xo_vap, int); 317831337658SMarcel Moolenaar } 317931337658SMarcel Moolenaar } else if (strchr("eEfFgGaA", xf.xf_fc) != NULL) 318031337658SMarcel Moolenaar if (xf.xf_lflag) 318131337658SMarcel Moolenaar va_arg(xop->xo_vap, long double); 318231337658SMarcel Moolenaar else 318331337658SMarcel Moolenaar va_arg(xop->xo_vap, double); 318431337658SMarcel Moolenaar 318531337658SMarcel Moolenaar else if (xf.xf_fc == 'C' || (xf.xf_fc == 'c' && xf.xf_lflag)) 318631337658SMarcel Moolenaar va_arg(xop->xo_vap, wint_t); 318731337658SMarcel Moolenaar 318831337658SMarcel Moolenaar else if (xf.xf_fc == 'c') 318931337658SMarcel Moolenaar va_arg(xop->xo_vap, int); 319031337658SMarcel Moolenaar 319131337658SMarcel Moolenaar else if (xf.xf_fc == 'p') 319231337658SMarcel Moolenaar va_arg(xop->xo_vap, void *); 319331337658SMarcel Moolenaar } 319431337658SMarcel Moolenaar } 319531337658SMarcel Moolenaar } 319631337658SMarcel Moolenaar 319731337658SMarcel Moolenaar if (xp) { 319831337658SMarcel Moolenaar if (make_output) { 319931337658SMarcel Moolenaar cols = xo_format_string_direct(xop, xbp, flags | XFF_UNESCAPE, 320031337658SMarcel Moolenaar NULL, xp, cp - xp, -1, 320131337658SMarcel Moolenaar need_enc, XF_ENC_UTF8); 3202d1a0d267SMarcel Moolenaar 3203d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_COLUMNS)) 320431337658SMarcel Moolenaar xop->xo_columns += cols; 3205d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_ANCHOR)) 320631337658SMarcel Moolenaar xop->xo_anchor_columns += cols; 320731337658SMarcel Moolenaar } 320831337658SMarcel Moolenaar 320931337658SMarcel Moolenaar xp = NULL; 321031337658SMarcel Moolenaar } 321131337658SMarcel Moolenaar 3212d1a0d267SMarcel Moolenaar if (flags & XFF_GT_FLAGS) { 3213d1a0d267SMarcel Moolenaar /* 3214d1a0d267SMarcel Moolenaar * Handle gettext()ing the field by looking up the value 3215d1a0d267SMarcel Moolenaar * and then copying it in, while converting to locale, if 3216d1a0d267SMarcel Moolenaar * needed. 3217d1a0d267SMarcel Moolenaar */ 3218d1a0d267SMarcel Moolenaar int new_cols = xo_format_gettext(xop, flags, start_offset, 3219d1a0d267SMarcel Moolenaar old_cols, real_need_enc); 3220d1a0d267SMarcel Moolenaar 3221d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_COLUMNS)) 3222d1a0d267SMarcel Moolenaar xop->xo_columns += new_cols - old_cols; 3223d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_ANCHOR)) 3224d1a0d267SMarcel Moolenaar xop->xo_anchor_columns += new_cols - old_cols; 3225d1a0d267SMarcel Moolenaar } 3226d1a0d267SMarcel Moolenaar 322731337658SMarcel Moolenaar return 0; 322831337658SMarcel Moolenaar } 322931337658SMarcel Moolenaar 323031337658SMarcel Moolenaar static char * 323131337658SMarcel Moolenaar xo_fix_encoding (xo_handle_t *xop UNUSED, char *encoding) 323231337658SMarcel Moolenaar { 323331337658SMarcel Moolenaar char *cp = encoding; 323431337658SMarcel Moolenaar 323531337658SMarcel Moolenaar if (cp[0] != '%' || !isdigit((int) cp[1])) 323631337658SMarcel Moolenaar return encoding; 323731337658SMarcel Moolenaar 323831337658SMarcel Moolenaar for (cp += 2; *cp; cp++) { 323931337658SMarcel Moolenaar if (!isdigit((int) *cp)) 324031337658SMarcel Moolenaar break; 324131337658SMarcel Moolenaar } 324231337658SMarcel Moolenaar 324331337658SMarcel Moolenaar cp -= 1; 324431337658SMarcel Moolenaar *cp = '%'; 324531337658SMarcel Moolenaar 324631337658SMarcel Moolenaar return cp; 324731337658SMarcel Moolenaar } 324831337658SMarcel Moolenaar 324931337658SMarcel Moolenaar static void 3250788ca347SMarcel Moolenaar xo_color_append_html (xo_handle_t *xop) 3251788ca347SMarcel Moolenaar { 3252788ca347SMarcel Moolenaar /* 3253788ca347SMarcel Moolenaar * If the color buffer has content, we add it now. It's already 3254788ca347SMarcel Moolenaar * prebuilt and ready, since we want to add it to every <div>. 3255788ca347SMarcel Moolenaar */ 3256788ca347SMarcel Moolenaar if (!xo_buf_is_empty(&xop->xo_color_buf)) { 3257788ca347SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_color_buf; 3258788ca347SMarcel Moolenaar 3259788ca347SMarcel Moolenaar xo_data_append(xop, xbp->xb_bufp, xbp->xb_curp - xbp->xb_bufp); 3260788ca347SMarcel Moolenaar } 3261788ca347SMarcel Moolenaar } 3262788ca347SMarcel Moolenaar 3263d1a0d267SMarcel Moolenaar /* 3264d1a0d267SMarcel Moolenaar * A wrapper for humanize_number that autoscales, since the 3265d1a0d267SMarcel Moolenaar * HN_AUTOSCALE flag scales as needed based on the size of 3266d1a0d267SMarcel Moolenaar * the output buffer, not the size of the value. I also 3267d1a0d267SMarcel Moolenaar * wish HN_DECIMAL was more imperative, without the <10 3268d1a0d267SMarcel Moolenaar * test. But the boat only goes where we want when we hold 3269d1a0d267SMarcel Moolenaar * the rudder, so xo_humanize fixes part of the problem. 3270d1a0d267SMarcel Moolenaar */ 3271d1a0d267SMarcel Moolenaar static int 3272d1a0d267SMarcel Moolenaar xo_humanize (char *buf, int len, uint64_t value, int flags) 3273d1a0d267SMarcel Moolenaar { 3274d1a0d267SMarcel Moolenaar int scale = 0; 3275d1a0d267SMarcel Moolenaar 3276d1a0d267SMarcel Moolenaar if (value) { 3277d1a0d267SMarcel Moolenaar uint64_t left = value; 3278d1a0d267SMarcel Moolenaar 3279d1a0d267SMarcel Moolenaar if (flags & HN_DIVISOR_1000) { 3280d1a0d267SMarcel Moolenaar for ( ; left; scale++) 3281d1a0d267SMarcel Moolenaar left /= 1000; 3282d1a0d267SMarcel Moolenaar } else { 3283d1a0d267SMarcel Moolenaar for ( ; left; scale++) 3284d1a0d267SMarcel Moolenaar left /= 1024; 3285d1a0d267SMarcel Moolenaar } 3286d1a0d267SMarcel Moolenaar scale -= 1; 3287d1a0d267SMarcel Moolenaar } 3288d1a0d267SMarcel Moolenaar 3289d1a0d267SMarcel Moolenaar return xo_humanize_number(buf, len, value, "", scale, flags); 3290d1a0d267SMarcel Moolenaar } 3291d1a0d267SMarcel Moolenaar 3292d1a0d267SMarcel Moolenaar /* 3293d1a0d267SMarcel Moolenaar * This is an area where we can save information from the handle for 3294d1a0d267SMarcel Moolenaar * later restoration. We need to know what data was rendered to know 3295d1a0d267SMarcel Moolenaar * what needs cleaned up. 3296d1a0d267SMarcel Moolenaar */ 3297d1a0d267SMarcel Moolenaar typedef struct xo_humanize_save_s { 3298d1a0d267SMarcel Moolenaar unsigned xhs_offset; /* Saved xo_offset */ 3299d1a0d267SMarcel Moolenaar unsigned xhs_columns; /* Saved xo_columns */ 3300d1a0d267SMarcel Moolenaar unsigned xhs_anchor_columns; /* Saved xo_anchor_columns */ 3301d1a0d267SMarcel Moolenaar } xo_humanize_save_t; 3302d1a0d267SMarcel Moolenaar 3303d1a0d267SMarcel Moolenaar /* 3304d1a0d267SMarcel Moolenaar * Format a "humanized" value for a numeric, meaning something nice 3305d1a0d267SMarcel Moolenaar * like "44M" instead of "44470272". We autoscale, choosing the 3306d1a0d267SMarcel Moolenaar * most appropriate value for K/M/G/T/P/E based on the value given. 3307d1a0d267SMarcel Moolenaar */ 3308d1a0d267SMarcel Moolenaar static void 3309d1a0d267SMarcel Moolenaar xo_format_humanize (xo_handle_t *xop, xo_buffer_t *xbp, 3310d1a0d267SMarcel Moolenaar xo_humanize_save_t *savep, xo_xff_flags_t flags) 3311d1a0d267SMarcel Moolenaar { 3312d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_NO_HUMANIZE)) 3313d1a0d267SMarcel Moolenaar return; 3314d1a0d267SMarcel Moolenaar 3315d1a0d267SMarcel Moolenaar unsigned end_offset = xbp->xb_curp - xbp->xb_bufp; 3316d1a0d267SMarcel Moolenaar if (end_offset == savep->xhs_offset) /* Huh? Nothing to render */ 3317d1a0d267SMarcel Moolenaar return; 3318d1a0d267SMarcel Moolenaar 3319d1a0d267SMarcel Moolenaar /* 3320d1a0d267SMarcel Moolenaar * We have a string that's allegedly a number. We want to 3321d1a0d267SMarcel Moolenaar * humanize it, which means turning it back into a number 3322d1a0d267SMarcel Moolenaar * and calling xo_humanize_number on it. 3323d1a0d267SMarcel Moolenaar */ 3324d1a0d267SMarcel Moolenaar uint64_t value; 3325d1a0d267SMarcel Moolenaar char *ep; 3326d1a0d267SMarcel Moolenaar 3327d1a0d267SMarcel Moolenaar xo_buf_append(xbp, "", 1); /* NUL-terminate it */ 3328d1a0d267SMarcel Moolenaar 3329d1a0d267SMarcel Moolenaar value = strtoull(xbp->xb_bufp + savep->xhs_offset, &ep, 0); 3330d1a0d267SMarcel Moolenaar if (!(value == ULLONG_MAX && errno == ERANGE) 3331d1a0d267SMarcel Moolenaar && (ep != xbp->xb_bufp + savep->xhs_offset)) { 3332d1a0d267SMarcel Moolenaar /* 3333d1a0d267SMarcel Moolenaar * There are few values where humanize_number needs 3334d1a0d267SMarcel Moolenaar * more bytes than the original value. I've used 3335d1a0d267SMarcel Moolenaar * 10 as a rectal number to cover those scenarios. 3336d1a0d267SMarcel Moolenaar */ 3337d1a0d267SMarcel Moolenaar if (xo_buf_has_room(xbp, 10)) { 3338d1a0d267SMarcel Moolenaar xbp->xb_curp = xbp->xb_bufp + savep->xhs_offset; 3339d1a0d267SMarcel Moolenaar 3340d1a0d267SMarcel Moolenaar int rc; 3341d1a0d267SMarcel Moolenaar int left = (xbp->xb_bufp + xbp->xb_size) - xbp->xb_curp; 3342d1a0d267SMarcel Moolenaar int hn_flags = HN_NOSPACE; /* On by default */ 3343d1a0d267SMarcel Moolenaar 3344d1a0d267SMarcel Moolenaar if (flags & XFF_HN_SPACE) 3345d1a0d267SMarcel Moolenaar hn_flags &= ~HN_NOSPACE; 3346d1a0d267SMarcel Moolenaar 3347d1a0d267SMarcel Moolenaar if (flags & XFF_HN_DECIMAL) 3348d1a0d267SMarcel Moolenaar hn_flags |= HN_DECIMAL; 3349d1a0d267SMarcel Moolenaar 3350d1a0d267SMarcel Moolenaar if (flags & XFF_HN_1000) 3351d1a0d267SMarcel Moolenaar hn_flags |= HN_DIVISOR_1000; 3352d1a0d267SMarcel Moolenaar 3353d1a0d267SMarcel Moolenaar rc = xo_humanize(xbp->xb_curp, 3354d1a0d267SMarcel Moolenaar left, value, hn_flags); 3355d1a0d267SMarcel Moolenaar if (rc > 0) { 3356d1a0d267SMarcel Moolenaar xbp->xb_curp += rc; 3357d1a0d267SMarcel Moolenaar xop->xo_columns = savep->xhs_columns + rc; 3358d1a0d267SMarcel Moolenaar xop->xo_anchor_columns = savep->xhs_anchor_columns + rc; 3359d1a0d267SMarcel Moolenaar } 3360d1a0d267SMarcel Moolenaar } 3361d1a0d267SMarcel Moolenaar } 3362d1a0d267SMarcel Moolenaar } 3363d1a0d267SMarcel Moolenaar 3364788ca347SMarcel Moolenaar static void 336531337658SMarcel Moolenaar xo_buf_append_div (xo_handle_t *xop, const char *class, xo_xff_flags_t flags, 336631337658SMarcel Moolenaar const char *name, int nlen, 336731337658SMarcel Moolenaar const char *value, int vlen, 336831337658SMarcel Moolenaar const char *encoding, int elen) 336931337658SMarcel Moolenaar { 337031337658SMarcel Moolenaar static char div_start[] = "<div class=\""; 337131337658SMarcel Moolenaar static char div_tag[] = "\" data-tag=\""; 337231337658SMarcel Moolenaar static char div_xpath[] = "\" data-xpath=\""; 337331337658SMarcel Moolenaar static char div_key[] = "\" data-key=\"key"; 337431337658SMarcel Moolenaar static char div_end[] = "\">"; 337531337658SMarcel Moolenaar static char div_close[] = "</div>"; 337631337658SMarcel Moolenaar 337731337658SMarcel Moolenaar /* 337831337658SMarcel Moolenaar * To build our XPath predicate, we need to save the va_list before 337931337658SMarcel Moolenaar * we format our data, and then restore it before we format the 338031337658SMarcel Moolenaar * xpath expression. 338131337658SMarcel Moolenaar * Display-only keys implies that we've got an encode-only key 338231337658SMarcel Moolenaar * elsewhere, so we don't use them from making predicates. 338331337658SMarcel Moolenaar */ 338431337658SMarcel Moolenaar int need_predidate = 338531337658SMarcel Moolenaar (name && (flags & XFF_KEY) && !(flags & XFF_DISPLAY_ONLY) 3386d1a0d267SMarcel Moolenaar && XOF_ISSET(xop, XOF_XPATH)); 338731337658SMarcel Moolenaar 338831337658SMarcel Moolenaar if (need_predidate) { 338931337658SMarcel Moolenaar va_list va_local; 339031337658SMarcel Moolenaar 339131337658SMarcel Moolenaar va_copy(va_local, xop->xo_vap); 339231337658SMarcel Moolenaar if (xop->xo_checkpointer) 339331337658SMarcel Moolenaar xop->xo_checkpointer(xop, xop->xo_vap, 0); 339431337658SMarcel Moolenaar 339531337658SMarcel Moolenaar /* 339631337658SMarcel Moolenaar * Build an XPath predicate expression to match this key. 339731337658SMarcel Moolenaar * We use the format buffer. 339831337658SMarcel Moolenaar */ 339931337658SMarcel Moolenaar xo_buffer_t *pbp = &xop->xo_predicate; 340031337658SMarcel Moolenaar pbp->xb_curp = pbp->xb_bufp; /* Restart buffer */ 340131337658SMarcel Moolenaar 340231337658SMarcel Moolenaar xo_buf_append(pbp, "[", 1); 340331337658SMarcel Moolenaar xo_buf_escape(xop, pbp, name, nlen, 0); 3404d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_PRETTY)) 340531337658SMarcel Moolenaar xo_buf_append(pbp, " = '", 4); 340631337658SMarcel Moolenaar else 340731337658SMarcel Moolenaar xo_buf_append(pbp, "='", 2); 340831337658SMarcel Moolenaar 340931337658SMarcel Moolenaar /* The encoding format defaults to the normal format */ 341031337658SMarcel Moolenaar if (encoding == NULL) { 341131337658SMarcel Moolenaar char *enc = alloca(vlen + 1); 341231337658SMarcel Moolenaar memcpy(enc, value, vlen); 341331337658SMarcel Moolenaar enc[vlen] = '\0'; 341431337658SMarcel Moolenaar encoding = xo_fix_encoding(xop, enc); 341531337658SMarcel Moolenaar elen = strlen(encoding); 341631337658SMarcel Moolenaar } 341731337658SMarcel Moolenaar 3418d1a0d267SMarcel Moolenaar xo_xff_flags_t pflags = flags | XFF_XML | XFF_ATTR; 3419d1a0d267SMarcel Moolenaar pflags &= ~(XFF_NO_OUTPUT | XFF_ENCODE_ONLY); 3420d1a0d267SMarcel Moolenaar xo_do_format_field(xop, pbp, encoding, elen, pflags); 342131337658SMarcel Moolenaar 342231337658SMarcel Moolenaar xo_buf_append(pbp, "']", 2); 342331337658SMarcel Moolenaar 342431337658SMarcel Moolenaar /* Now we record this predicate expression in the stack */ 342531337658SMarcel Moolenaar xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth]; 342631337658SMarcel Moolenaar int olen = xsp->xs_keys ? strlen(xsp->xs_keys) : 0; 342731337658SMarcel Moolenaar int dlen = pbp->xb_curp - pbp->xb_bufp; 342831337658SMarcel Moolenaar 342931337658SMarcel Moolenaar char *cp = xo_realloc(xsp->xs_keys, olen + dlen + 1); 343031337658SMarcel Moolenaar if (cp) { 343131337658SMarcel Moolenaar memcpy(cp + olen, pbp->xb_bufp, dlen); 343231337658SMarcel Moolenaar cp[olen + dlen] = '\0'; 343331337658SMarcel Moolenaar xsp->xs_keys = cp; 343431337658SMarcel Moolenaar } 343531337658SMarcel Moolenaar 343631337658SMarcel Moolenaar /* Now we reset the xo_vap as if we were never here */ 343731337658SMarcel Moolenaar va_end(xop->xo_vap); 343831337658SMarcel Moolenaar va_copy(xop->xo_vap, va_local); 343931337658SMarcel Moolenaar va_end(va_local); 344031337658SMarcel Moolenaar if (xop->xo_checkpointer) 344131337658SMarcel Moolenaar xop->xo_checkpointer(xop, xop->xo_vap, 1); 344231337658SMarcel Moolenaar } 344331337658SMarcel Moolenaar 344431337658SMarcel Moolenaar if (flags & XFF_ENCODE_ONLY) { 344531337658SMarcel Moolenaar /* 344631337658SMarcel Moolenaar * Even if this is encode-only, we need to go thru the 344731337658SMarcel Moolenaar * work of formatting it to make sure the args are cleared 344831337658SMarcel Moolenaar * from xo_vap. 344931337658SMarcel Moolenaar */ 3450d1a0d267SMarcel Moolenaar xo_do_format_field(xop, NULL, encoding, elen, 345131337658SMarcel Moolenaar flags | XFF_NO_OUTPUT); 345231337658SMarcel Moolenaar return; 345331337658SMarcel Moolenaar } 345431337658SMarcel Moolenaar 345531337658SMarcel Moolenaar xo_line_ensure_open(xop, 0); 345631337658SMarcel Moolenaar 3457d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_PRETTY)) 345831337658SMarcel Moolenaar xo_buf_indent(xop, xop->xo_indent_by); 345931337658SMarcel Moolenaar 346031337658SMarcel Moolenaar xo_data_append(xop, div_start, sizeof(div_start) - 1); 346131337658SMarcel Moolenaar xo_data_append(xop, class, strlen(class)); 346231337658SMarcel Moolenaar 3463788ca347SMarcel Moolenaar /* 3464788ca347SMarcel Moolenaar * If the color buffer has content, we add it now. It's already 3465788ca347SMarcel Moolenaar * prebuilt and ready, since we want to add it to every <div>. 3466788ca347SMarcel Moolenaar */ 3467788ca347SMarcel Moolenaar if (!xo_buf_is_empty(&xop->xo_color_buf)) { 3468788ca347SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_color_buf; 3469788ca347SMarcel Moolenaar 3470788ca347SMarcel Moolenaar xo_data_append(xop, xbp->xb_bufp, xbp->xb_curp - xbp->xb_bufp); 3471788ca347SMarcel Moolenaar } 3472788ca347SMarcel Moolenaar 347331337658SMarcel Moolenaar if (name) { 347431337658SMarcel Moolenaar xo_data_append(xop, div_tag, sizeof(div_tag) - 1); 347531337658SMarcel Moolenaar xo_data_escape(xop, name, nlen); 347631337658SMarcel Moolenaar 347731337658SMarcel Moolenaar /* 347831337658SMarcel Moolenaar * Save the offset at which we'd place units. See xo_format_units. 347931337658SMarcel Moolenaar */ 3480d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_UNITS)) { 3481d1a0d267SMarcel Moolenaar XOIF_SET(xop, XOIF_UNITS_PENDING); 348231337658SMarcel Moolenaar /* 348331337658SMarcel Moolenaar * Note: We need the '+1' here because we know we've not 348431337658SMarcel Moolenaar * added the closing quote. We add one, knowing the quote 348531337658SMarcel Moolenaar * will be added shortly. 348631337658SMarcel Moolenaar */ 348731337658SMarcel Moolenaar xop->xo_units_offset = 348831337658SMarcel Moolenaar xop->xo_data.xb_curp -xop->xo_data.xb_bufp + 1; 348931337658SMarcel Moolenaar } 349031337658SMarcel Moolenaar 3491d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_XPATH)) { 349231337658SMarcel Moolenaar int i; 349331337658SMarcel Moolenaar xo_stack_t *xsp; 349431337658SMarcel Moolenaar 349531337658SMarcel Moolenaar xo_data_append(xop, div_xpath, sizeof(div_xpath) - 1); 349631337658SMarcel Moolenaar if (xop->xo_leading_xpath) 349731337658SMarcel Moolenaar xo_data_append(xop, xop->xo_leading_xpath, 349831337658SMarcel Moolenaar strlen(xop->xo_leading_xpath)); 349931337658SMarcel Moolenaar 350031337658SMarcel Moolenaar for (i = 0; i <= xop->xo_depth; i++) { 350131337658SMarcel Moolenaar xsp = &xop->xo_stack[i]; 350231337658SMarcel Moolenaar if (xsp->xs_name == NULL) 350331337658SMarcel Moolenaar continue; 350431337658SMarcel Moolenaar 3505545ddfbeSMarcel Moolenaar /* 3506545ddfbeSMarcel Moolenaar * XSS_OPEN_LIST and XSS_OPEN_LEAF_LIST stack frames 3507545ddfbeSMarcel Moolenaar * are directly under XSS_OPEN_INSTANCE frames so we 3508545ddfbeSMarcel Moolenaar * don't need to put these in our XPath expressions. 3509545ddfbeSMarcel Moolenaar */ 3510545ddfbeSMarcel Moolenaar if (xsp->xs_state == XSS_OPEN_LIST 3511545ddfbeSMarcel Moolenaar || xsp->xs_state == XSS_OPEN_LEAF_LIST) 3512545ddfbeSMarcel Moolenaar continue; 3513545ddfbeSMarcel Moolenaar 351431337658SMarcel Moolenaar xo_data_append(xop, "/", 1); 351531337658SMarcel Moolenaar xo_data_escape(xop, xsp->xs_name, strlen(xsp->xs_name)); 351631337658SMarcel Moolenaar if (xsp->xs_keys) { 351731337658SMarcel Moolenaar /* Don't show keys for the key field */ 351831337658SMarcel Moolenaar if (i != xop->xo_depth || !(flags & XFF_KEY)) 351931337658SMarcel Moolenaar xo_data_append(xop, xsp->xs_keys, strlen(xsp->xs_keys)); 352031337658SMarcel Moolenaar } 352131337658SMarcel Moolenaar } 352231337658SMarcel Moolenaar 352331337658SMarcel Moolenaar xo_data_append(xop, "/", 1); 352431337658SMarcel Moolenaar xo_data_escape(xop, name, nlen); 352531337658SMarcel Moolenaar } 352631337658SMarcel Moolenaar 3527d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_INFO) && xop->xo_info) { 352831337658SMarcel Moolenaar static char in_type[] = "\" data-type=\""; 352931337658SMarcel Moolenaar static char in_help[] = "\" data-help=\""; 353031337658SMarcel Moolenaar 353131337658SMarcel Moolenaar xo_info_t *xip = xo_info_find(xop, name, nlen); 353231337658SMarcel Moolenaar if (xip) { 353331337658SMarcel Moolenaar if (xip->xi_type) { 353431337658SMarcel Moolenaar xo_data_append(xop, in_type, sizeof(in_type) - 1); 353531337658SMarcel Moolenaar xo_data_escape(xop, xip->xi_type, strlen(xip->xi_type)); 353631337658SMarcel Moolenaar } 353731337658SMarcel Moolenaar if (xip->xi_help) { 353831337658SMarcel Moolenaar xo_data_append(xop, in_help, sizeof(in_help) - 1); 353931337658SMarcel Moolenaar xo_data_escape(xop, xip->xi_help, strlen(xip->xi_help)); 354031337658SMarcel Moolenaar } 354131337658SMarcel Moolenaar } 354231337658SMarcel Moolenaar } 354331337658SMarcel Moolenaar 3544d1a0d267SMarcel Moolenaar if ((flags & XFF_KEY) && XOF_ISSET(xop, XOF_KEYS)) 354531337658SMarcel Moolenaar xo_data_append(xop, div_key, sizeof(div_key) - 1); 354631337658SMarcel Moolenaar } 354731337658SMarcel Moolenaar 3548d1a0d267SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_data; 3549d1a0d267SMarcel Moolenaar unsigned base_offset = xbp->xb_curp - xbp->xb_bufp; 3550d1a0d267SMarcel Moolenaar 355131337658SMarcel Moolenaar xo_data_append(xop, div_end, sizeof(div_end) - 1); 355231337658SMarcel Moolenaar 3553d1a0d267SMarcel Moolenaar xo_humanize_save_t save; /* Save values for humanizing logic */ 3554d1a0d267SMarcel Moolenaar 3555d1a0d267SMarcel Moolenaar save.xhs_offset = xbp->xb_curp - xbp->xb_bufp; 3556d1a0d267SMarcel Moolenaar save.xhs_columns = xop->xo_columns; 3557d1a0d267SMarcel Moolenaar save.xhs_anchor_columns = xop->xo_anchor_columns; 3558d1a0d267SMarcel Moolenaar 3559d1a0d267SMarcel Moolenaar xo_do_format_field(xop, NULL, value, vlen, flags); 3560d1a0d267SMarcel Moolenaar 3561d1a0d267SMarcel Moolenaar if (flags & XFF_HUMANIZE) { 3562d1a0d267SMarcel Moolenaar /* 3563d1a0d267SMarcel Moolenaar * Unlike text style, we want to retain the original value and 3564d1a0d267SMarcel Moolenaar * stuff it into the "data-number" attribute. 3565d1a0d267SMarcel Moolenaar */ 3566d1a0d267SMarcel Moolenaar static const char div_number[] = "\" data-number=\""; 3567d1a0d267SMarcel Moolenaar int div_len = sizeof(div_number) - 1; 3568d1a0d267SMarcel Moolenaar 3569d1a0d267SMarcel Moolenaar unsigned end_offset = xbp->xb_curp - xbp->xb_bufp; 3570d1a0d267SMarcel Moolenaar int olen = end_offset - save.xhs_offset; 3571d1a0d267SMarcel Moolenaar 3572d1a0d267SMarcel Moolenaar char *cp = alloca(olen + 1); 3573d1a0d267SMarcel Moolenaar memcpy(cp, xbp->xb_bufp + save.xhs_offset, olen); 3574d1a0d267SMarcel Moolenaar cp[olen] = '\0'; 3575d1a0d267SMarcel Moolenaar 3576d1a0d267SMarcel Moolenaar xo_format_humanize(xop, xbp, &save, flags); 3577d1a0d267SMarcel Moolenaar 3578d1a0d267SMarcel Moolenaar if (xo_buf_has_room(xbp, div_len + olen)) { 3579d1a0d267SMarcel Moolenaar unsigned new_offset = xbp->xb_curp - xbp->xb_bufp; 3580d1a0d267SMarcel Moolenaar 3581d1a0d267SMarcel Moolenaar 3582d1a0d267SMarcel Moolenaar /* Move the humanized string off to the left */ 3583d1a0d267SMarcel Moolenaar memmove(xbp->xb_bufp + base_offset + div_len + olen, 3584d1a0d267SMarcel Moolenaar xbp->xb_bufp + base_offset, new_offset - base_offset); 3585d1a0d267SMarcel Moolenaar 3586d1a0d267SMarcel Moolenaar /* Copy the data_number attribute name */ 3587d1a0d267SMarcel Moolenaar memcpy(xbp->xb_bufp + base_offset, div_number, div_len); 3588d1a0d267SMarcel Moolenaar 3589d1a0d267SMarcel Moolenaar /* Copy the original long value */ 3590d1a0d267SMarcel Moolenaar memcpy(xbp->xb_bufp + base_offset + div_len, cp, olen); 3591d1a0d267SMarcel Moolenaar xbp->xb_curp += div_len + olen; 3592d1a0d267SMarcel Moolenaar } 3593d1a0d267SMarcel Moolenaar } 359431337658SMarcel Moolenaar 359531337658SMarcel Moolenaar xo_data_append(xop, div_close, sizeof(div_close) - 1); 359631337658SMarcel Moolenaar 3597d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_PRETTY)) 359831337658SMarcel Moolenaar xo_data_append(xop, "\n", 1); 359931337658SMarcel Moolenaar } 360031337658SMarcel Moolenaar 360131337658SMarcel Moolenaar static void 360231337658SMarcel Moolenaar xo_format_text (xo_handle_t *xop, const char *str, int len) 360331337658SMarcel Moolenaar { 3604788ca347SMarcel Moolenaar switch (xo_style(xop)) { 360531337658SMarcel Moolenaar case XO_STYLE_TEXT: 360631337658SMarcel Moolenaar xo_buf_append_locale(xop, &xop->xo_data, str, len); 360731337658SMarcel Moolenaar break; 360831337658SMarcel Moolenaar 360931337658SMarcel Moolenaar case XO_STYLE_HTML: 361031337658SMarcel Moolenaar xo_buf_append_div(xop, "text", 0, NULL, 0, str, len, NULL, 0); 361131337658SMarcel Moolenaar break; 361231337658SMarcel Moolenaar } 361331337658SMarcel Moolenaar } 361431337658SMarcel Moolenaar 361531337658SMarcel Moolenaar static void 3616d1a0d267SMarcel Moolenaar xo_format_title (xo_handle_t *xop, xo_field_info_t *xfip) 361731337658SMarcel Moolenaar { 3618d1a0d267SMarcel Moolenaar const char *str = xfip->xfi_content; 3619d1a0d267SMarcel Moolenaar unsigned len = xfip->xfi_clen; 3620d1a0d267SMarcel Moolenaar const char *fmt = xfip->xfi_format; 3621d1a0d267SMarcel Moolenaar unsigned flen = xfip->xfi_flen; 3622d1a0d267SMarcel Moolenaar xo_xff_flags_t flags = xfip->xfi_flags; 3623d1a0d267SMarcel Moolenaar 3624788ca347SMarcel Moolenaar static char div_open[] = "<div class=\"title"; 3625788ca347SMarcel Moolenaar static char div_middle[] = "\">"; 362631337658SMarcel Moolenaar static char div_close[] = "</div>"; 362731337658SMarcel Moolenaar 3628545ddfbeSMarcel Moolenaar if (flen == 0) { 3629545ddfbeSMarcel Moolenaar fmt = "%s"; 3630545ddfbeSMarcel Moolenaar flen = 2; 3631545ddfbeSMarcel Moolenaar } 3632545ddfbeSMarcel Moolenaar 3633788ca347SMarcel Moolenaar switch (xo_style(xop)) { 363431337658SMarcel Moolenaar case XO_STYLE_XML: 363531337658SMarcel Moolenaar case XO_STYLE_JSON: 3636d1a0d267SMarcel Moolenaar case XO_STYLE_SDPARAMS: 3637d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER: 363831337658SMarcel Moolenaar /* 363931337658SMarcel Moolenaar * Even though we don't care about text, we need to do 364031337658SMarcel Moolenaar * enough parsing work to skip over the right bits of xo_vap. 364131337658SMarcel Moolenaar */ 364231337658SMarcel Moolenaar if (len == 0) 3643d1a0d267SMarcel Moolenaar xo_do_format_field(xop, NULL, fmt, flen, flags | XFF_NO_OUTPUT); 364431337658SMarcel Moolenaar return; 364531337658SMarcel Moolenaar } 364631337658SMarcel Moolenaar 364731337658SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_data; 364831337658SMarcel Moolenaar int start = xbp->xb_curp - xbp->xb_bufp; 364931337658SMarcel Moolenaar int left = xbp->xb_size - start; 365031337658SMarcel Moolenaar int rc; 365131337658SMarcel Moolenaar 3652788ca347SMarcel Moolenaar if (xo_style(xop) == XO_STYLE_HTML) { 365331337658SMarcel Moolenaar xo_line_ensure_open(xop, 0); 3654d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_PRETTY)) 365531337658SMarcel Moolenaar xo_buf_indent(xop, xop->xo_indent_by); 365631337658SMarcel Moolenaar xo_buf_append(&xop->xo_data, div_open, sizeof(div_open) - 1); 3657788ca347SMarcel Moolenaar xo_color_append_html(xop); 3658788ca347SMarcel Moolenaar xo_buf_append(&xop->xo_data, div_middle, sizeof(div_middle) - 1); 365931337658SMarcel Moolenaar } 366031337658SMarcel Moolenaar 366131337658SMarcel Moolenaar start = xbp->xb_curp - xbp->xb_bufp; /* Reset start */ 366231337658SMarcel Moolenaar if (len) { 366331337658SMarcel Moolenaar char *newfmt = alloca(flen + 1); 366431337658SMarcel Moolenaar memcpy(newfmt, fmt, flen); 366531337658SMarcel Moolenaar newfmt[flen] = '\0'; 366631337658SMarcel Moolenaar 366731337658SMarcel Moolenaar /* If len is non-zero, the format string apply to the name */ 366831337658SMarcel Moolenaar char *newstr = alloca(len + 1); 366931337658SMarcel Moolenaar memcpy(newstr, str, len); 367031337658SMarcel Moolenaar newstr[len] = '\0'; 367131337658SMarcel Moolenaar 367231337658SMarcel Moolenaar if (newstr[len - 1] == 's') { 367331337658SMarcel Moolenaar char *bp; 367431337658SMarcel Moolenaar 367531337658SMarcel Moolenaar rc = snprintf(NULL, 0, newfmt, newstr); 367631337658SMarcel Moolenaar if (rc > 0) { 367731337658SMarcel Moolenaar /* 367831337658SMarcel Moolenaar * We have to do this the hard way, since we might need 367931337658SMarcel Moolenaar * the columns. 368031337658SMarcel Moolenaar */ 368131337658SMarcel Moolenaar bp = alloca(rc + 1); 368231337658SMarcel Moolenaar rc = snprintf(bp, rc + 1, newfmt, newstr); 3683d1a0d267SMarcel Moolenaar 3684d1a0d267SMarcel Moolenaar xo_data_append_content(xop, bp, rc, flags); 368531337658SMarcel Moolenaar } 368631337658SMarcel Moolenaar goto move_along; 368731337658SMarcel Moolenaar 368831337658SMarcel Moolenaar } else { 368931337658SMarcel Moolenaar rc = snprintf(xbp->xb_curp, left, newfmt, newstr); 3690d1a0d267SMarcel Moolenaar if (rc >= left) { 369131337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, rc)) 369231337658SMarcel Moolenaar return; 369331337658SMarcel Moolenaar left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp); 369431337658SMarcel Moolenaar rc = snprintf(xbp->xb_curp, left, newfmt, newstr); 369531337658SMarcel Moolenaar } 369631337658SMarcel Moolenaar 369731337658SMarcel Moolenaar if (rc > 0) { 3698d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_COLUMNS)) 369931337658SMarcel Moolenaar xop->xo_columns += rc; 3700d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_ANCHOR)) 370131337658SMarcel Moolenaar xop->xo_anchor_columns += rc; 370231337658SMarcel Moolenaar } 370331337658SMarcel Moolenaar } 370431337658SMarcel Moolenaar 370531337658SMarcel Moolenaar } else { 3706d1a0d267SMarcel Moolenaar xo_do_format_field(xop, NULL, fmt, flen, flags); 370731337658SMarcel Moolenaar 3708d1a0d267SMarcel Moolenaar /* xo_do_format_field moved curp, so we need to reset it */ 370931337658SMarcel Moolenaar rc = xbp->xb_curp - (xbp->xb_bufp + start); 371031337658SMarcel Moolenaar xbp->xb_curp = xbp->xb_bufp + start; 371131337658SMarcel Moolenaar } 371231337658SMarcel Moolenaar 371331337658SMarcel Moolenaar /* If we're styling HTML, then we need to escape it */ 3714788ca347SMarcel Moolenaar if (xo_style(xop) == XO_STYLE_HTML) { 371531337658SMarcel Moolenaar rc = xo_escape_xml(xbp, rc, 0); 371631337658SMarcel Moolenaar } 371731337658SMarcel Moolenaar 371831337658SMarcel Moolenaar if (rc > 0) 371931337658SMarcel Moolenaar xbp->xb_curp += rc; 372031337658SMarcel Moolenaar 372131337658SMarcel Moolenaar move_along: 3722788ca347SMarcel Moolenaar if (xo_style(xop) == XO_STYLE_HTML) { 372331337658SMarcel Moolenaar xo_data_append(xop, div_close, sizeof(div_close) - 1); 3724d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_PRETTY)) 372531337658SMarcel Moolenaar xo_data_append(xop, "\n", 1); 372631337658SMarcel Moolenaar } 372731337658SMarcel Moolenaar } 372831337658SMarcel Moolenaar 372931337658SMarcel Moolenaar static void 373031337658SMarcel Moolenaar xo_format_prep (xo_handle_t *xop, xo_xff_flags_t flags) 373131337658SMarcel Moolenaar { 373231337658SMarcel Moolenaar if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST) { 373331337658SMarcel Moolenaar xo_data_append(xop, ",", 1); 3734d1a0d267SMarcel Moolenaar if (!(flags & XFF_LEAF_LIST) && XOF_ISSET(xop, XOF_PRETTY)) 373531337658SMarcel Moolenaar xo_data_append(xop, "\n", 1); 373631337658SMarcel Moolenaar } else 373731337658SMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 373831337658SMarcel Moolenaar } 373931337658SMarcel Moolenaar 374031337658SMarcel Moolenaar #if 0 374131337658SMarcel Moolenaar /* Useful debugging function */ 374231337658SMarcel Moolenaar void 374331337658SMarcel Moolenaar xo_arg (xo_handle_t *xop); 374431337658SMarcel Moolenaar void 374531337658SMarcel Moolenaar xo_arg (xo_handle_t *xop) 374631337658SMarcel Moolenaar { 374731337658SMarcel Moolenaar xop = xo_default(xop); 374831337658SMarcel Moolenaar fprintf(stderr, "0x%x", va_arg(xop->xo_vap, unsigned)); 374931337658SMarcel Moolenaar } 375031337658SMarcel Moolenaar #endif /* 0 */ 375131337658SMarcel Moolenaar 375231337658SMarcel Moolenaar static void 375331337658SMarcel Moolenaar xo_format_value (xo_handle_t *xop, const char *name, int nlen, 375431337658SMarcel Moolenaar const char *format, int flen, 375531337658SMarcel Moolenaar const char *encoding, int elen, xo_xff_flags_t flags) 375631337658SMarcel Moolenaar { 3757d1a0d267SMarcel Moolenaar int pretty = XOF_ISSET(xop, XOF_PRETTY); 375831337658SMarcel Moolenaar int quote; 375931337658SMarcel Moolenaar 3760545ddfbeSMarcel Moolenaar /* 3761545ddfbeSMarcel Moolenaar * Before we emit a value, we need to know that the frame is ready. 3762545ddfbeSMarcel Moolenaar */ 3763545ddfbeSMarcel Moolenaar xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth]; 3764545ddfbeSMarcel Moolenaar 3765545ddfbeSMarcel Moolenaar if (flags & XFF_LEAF_LIST) { 3766545ddfbeSMarcel Moolenaar /* 3767545ddfbeSMarcel Moolenaar * Check if we've already started to emit normal leafs 3768545ddfbeSMarcel Moolenaar * or if we're not in a leaf list. 3769545ddfbeSMarcel Moolenaar */ 3770545ddfbeSMarcel Moolenaar if ((xsp->xs_flags & (XSF_EMIT | XSF_EMIT_KEY)) 3771545ddfbeSMarcel Moolenaar || !(xsp->xs_flags & XSF_EMIT_LEAF_LIST)) { 3772545ddfbeSMarcel Moolenaar char nbuf[nlen + 1]; 3773545ddfbeSMarcel Moolenaar memcpy(nbuf, name, nlen); 3774545ddfbeSMarcel Moolenaar nbuf[nlen] = '\0'; 3775545ddfbeSMarcel Moolenaar 3776545ddfbeSMarcel Moolenaar int rc = xo_transition(xop, 0, nbuf, XSS_EMIT_LEAF_LIST); 3777545ddfbeSMarcel Moolenaar if (rc < 0) 3778545ddfbeSMarcel Moolenaar flags |= XFF_DISPLAY_ONLY | XFF_ENCODE_ONLY; 3779545ddfbeSMarcel Moolenaar else 3780545ddfbeSMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_EMIT_LEAF_LIST; 3781545ddfbeSMarcel Moolenaar } 3782545ddfbeSMarcel Moolenaar 3783545ddfbeSMarcel Moolenaar xsp = &xop->xo_stack[xop->xo_depth]; 3784545ddfbeSMarcel Moolenaar if (xsp->xs_name) { 3785545ddfbeSMarcel Moolenaar name = xsp->xs_name; 3786545ddfbeSMarcel Moolenaar nlen = strlen(name); 3787545ddfbeSMarcel Moolenaar } 3788545ddfbeSMarcel Moolenaar 3789545ddfbeSMarcel Moolenaar } else if (flags & XFF_KEY) { 3790545ddfbeSMarcel Moolenaar /* Emitting a 'k' (key) field */ 3791545ddfbeSMarcel Moolenaar if ((xsp->xs_flags & XSF_EMIT) && !(flags & XFF_DISPLAY_ONLY)) { 3792545ddfbeSMarcel Moolenaar xo_failure(xop, "key field emitted after normal value field: '%.*s'", 3793545ddfbeSMarcel Moolenaar nlen, name); 3794545ddfbeSMarcel Moolenaar 3795545ddfbeSMarcel Moolenaar } else if (!(xsp->xs_flags & XSF_EMIT_KEY)) { 3796545ddfbeSMarcel Moolenaar char nbuf[nlen + 1]; 3797545ddfbeSMarcel Moolenaar memcpy(nbuf, name, nlen); 3798545ddfbeSMarcel Moolenaar nbuf[nlen] = '\0'; 3799545ddfbeSMarcel Moolenaar 3800545ddfbeSMarcel Moolenaar int rc = xo_transition(xop, 0, nbuf, XSS_EMIT); 3801545ddfbeSMarcel Moolenaar if (rc < 0) 3802545ddfbeSMarcel Moolenaar flags |= XFF_DISPLAY_ONLY | XFF_ENCODE_ONLY; 3803545ddfbeSMarcel Moolenaar else 3804545ddfbeSMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_EMIT_KEY; 3805545ddfbeSMarcel Moolenaar 3806545ddfbeSMarcel Moolenaar xsp = &xop->xo_stack[xop->xo_depth]; 3807545ddfbeSMarcel Moolenaar xsp->xs_flags |= XSF_EMIT_KEY; 3808545ddfbeSMarcel Moolenaar } 3809545ddfbeSMarcel Moolenaar 3810545ddfbeSMarcel Moolenaar } else { 3811545ddfbeSMarcel Moolenaar /* Emitting a normal value field */ 3812545ddfbeSMarcel Moolenaar if ((xsp->xs_flags & XSF_EMIT_LEAF_LIST) 3813545ddfbeSMarcel Moolenaar || !(xsp->xs_flags & XSF_EMIT)) { 3814545ddfbeSMarcel Moolenaar char nbuf[nlen + 1]; 3815545ddfbeSMarcel Moolenaar memcpy(nbuf, name, nlen); 3816545ddfbeSMarcel Moolenaar nbuf[nlen] = '\0'; 3817545ddfbeSMarcel Moolenaar 3818545ddfbeSMarcel Moolenaar int rc = xo_transition(xop, 0, nbuf, XSS_EMIT); 3819545ddfbeSMarcel Moolenaar if (rc < 0) 3820545ddfbeSMarcel Moolenaar flags |= XFF_DISPLAY_ONLY | XFF_ENCODE_ONLY; 3821545ddfbeSMarcel Moolenaar else 3822545ddfbeSMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_EMIT; 3823545ddfbeSMarcel Moolenaar 3824545ddfbeSMarcel Moolenaar xsp = &xop->xo_stack[xop->xo_depth]; 3825545ddfbeSMarcel Moolenaar xsp->xs_flags |= XSF_EMIT; 3826545ddfbeSMarcel Moolenaar } 3827545ddfbeSMarcel Moolenaar } 3828545ddfbeSMarcel Moolenaar 3829d1a0d267SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_data; 3830d1a0d267SMarcel Moolenaar xo_humanize_save_t save; /* Save values for humanizing logic */ 3831d1a0d267SMarcel Moolenaar 3832788ca347SMarcel Moolenaar switch (xo_style(xop)) { 383331337658SMarcel Moolenaar case XO_STYLE_TEXT: 383431337658SMarcel Moolenaar if (flags & XFF_ENCODE_ONLY) 383531337658SMarcel Moolenaar flags |= XFF_NO_OUTPUT; 3836d1a0d267SMarcel Moolenaar 3837d1a0d267SMarcel Moolenaar save.xhs_offset = xbp->xb_curp - xbp->xb_bufp; 3838d1a0d267SMarcel Moolenaar save.xhs_columns = xop->xo_columns; 3839d1a0d267SMarcel Moolenaar save.xhs_anchor_columns = xop->xo_anchor_columns; 3840d1a0d267SMarcel Moolenaar 3841d1a0d267SMarcel Moolenaar xo_do_format_field(xop, NULL, format, flen, flags); 3842d1a0d267SMarcel Moolenaar 3843d1a0d267SMarcel Moolenaar if (flags & XFF_HUMANIZE) 3844d1a0d267SMarcel Moolenaar xo_format_humanize(xop, xbp, &save, flags); 384531337658SMarcel Moolenaar break; 384631337658SMarcel Moolenaar 384731337658SMarcel Moolenaar case XO_STYLE_HTML: 384831337658SMarcel Moolenaar if (flags & XFF_ENCODE_ONLY) 384931337658SMarcel Moolenaar flags |= XFF_NO_OUTPUT; 3850d1a0d267SMarcel Moolenaar 385131337658SMarcel Moolenaar xo_buf_append_div(xop, "data", flags, name, nlen, 385231337658SMarcel Moolenaar format, flen, encoding, elen); 385331337658SMarcel Moolenaar break; 385431337658SMarcel Moolenaar 385531337658SMarcel Moolenaar case XO_STYLE_XML: 385631337658SMarcel Moolenaar /* 385731337658SMarcel Moolenaar * Even though we're not making output, we still need to 385831337658SMarcel Moolenaar * let the formatting code handle the va_arg popping. 385931337658SMarcel Moolenaar */ 386031337658SMarcel Moolenaar if (flags & XFF_DISPLAY_ONLY) { 386131337658SMarcel Moolenaar flags |= XFF_NO_OUTPUT; 3862d1a0d267SMarcel Moolenaar xo_do_format_field(xop, NULL, format, flen, flags); 386331337658SMarcel Moolenaar break; 386431337658SMarcel Moolenaar } 386531337658SMarcel Moolenaar 386631337658SMarcel Moolenaar if (encoding) { 386731337658SMarcel Moolenaar format = encoding; 386831337658SMarcel Moolenaar flen = elen; 386931337658SMarcel Moolenaar } else { 387031337658SMarcel Moolenaar char *enc = alloca(flen + 1); 387131337658SMarcel Moolenaar memcpy(enc, format, flen); 387231337658SMarcel Moolenaar enc[flen] = '\0'; 387331337658SMarcel Moolenaar format = xo_fix_encoding(xop, enc); 387431337658SMarcel Moolenaar flen = strlen(format); 387531337658SMarcel Moolenaar } 387631337658SMarcel Moolenaar 387731337658SMarcel Moolenaar if (nlen == 0) { 387831337658SMarcel Moolenaar static char missing[] = "missing-field-name"; 387931337658SMarcel Moolenaar xo_failure(xop, "missing field name: %s", format); 388031337658SMarcel Moolenaar name = missing; 388131337658SMarcel Moolenaar nlen = sizeof(missing) - 1; 388231337658SMarcel Moolenaar } 388331337658SMarcel Moolenaar 388431337658SMarcel Moolenaar if (pretty) 388531337658SMarcel Moolenaar xo_buf_indent(xop, -1); 388631337658SMarcel Moolenaar xo_data_append(xop, "<", 1); 388731337658SMarcel Moolenaar xo_data_escape(xop, name, nlen); 388831337658SMarcel Moolenaar 388931337658SMarcel Moolenaar if (xop->xo_attrs.xb_curp != xop->xo_attrs.xb_bufp) { 389031337658SMarcel Moolenaar xo_data_append(xop, xop->xo_attrs.xb_bufp, 389131337658SMarcel Moolenaar xop->xo_attrs.xb_curp - xop->xo_attrs.xb_bufp); 389231337658SMarcel Moolenaar xop->xo_attrs.xb_curp = xop->xo_attrs.xb_bufp; 389331337658SMarcel Moolenaar } 389431337658SMarcel Moolenaar 389531337658SMarcel Moolenaar /* 389631337658SMarcel Moolenaar * We indicate 'key' fields using the 'key' attribute. While 389731337658SMarcel Moolenaar * this is really committing the crime of mixing meta-data with 389831337658SMarcel Moolenaar * data, it's often useful. Especially when format meta-data is 389931337658SMarcel Moolenaar * difficult to come by. 390031337658SMarcel Moolenaar */ 3901d1a0d267SMarcel Moolenaar if ((flags & XFF_KEY) && XOF_ISSET(xop, XOF_KEYS)) { 390231337658SMarcel Moolenaar static char attr[] = " key=\"key\""; 390331337658SMarcel Moolenaar xo_data_append(xop, attr, sizeof(attr) - 1); 390431337658SMarcel Moolenaar } 390531337658SMarcel Moolenaar 390631337658SMarcel Moolenaar /* 390731337658SMarcel Moolenaar * Save the offset at which we'd place units. See xo_format_units. 390831337658SMarcel Moolenaar */ 3909d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_UNITS)) { 3910d1a0d267SMarcel Moolenaar XOIF_SET(xop, XOIF_UNITS_PENDING); 391131337658SMarcel Moolenaar xop->xo_units_offset = xop->xo_data.xb_curp -xop->xo_data.xb_bufp; 391231337658SMarcel Moolenaar } 391331337658SMarcel Moolenaar 391431337658SMarcel Moolenaar xo_data_append(xop, ">", 1); 3915d1a0d267SMarcel Moolenaar xo_do_format_field(xop, NULL, format, flen, flags); 391631337658SMarcel Moolenaar xo_data_append(xop, "</", 2); 391731337658SMarcel Moolenaar xo_data_escape(xop, name, nlen); 391831337658SMarcel Moolenaar xo_data_append(xop, ">", 1); 391931337658SMarcel Moolenaar if (pretty) 392031337658SMarcel Moolenaar xo_data_append(xop, "\n", 1); 392131337658SMarcel Moolenaar break; 392231337658SMarcel Moolenaar 392331337658SMarcel Moolenaar case XO_STYLE_JSON: 392431337658SMarcel Moolenaar if (flags & XFF_DISPLAY_ONLY) { 392531337658SMarcel Moolenaar flags |= XFF_NO_OUTPUT; 3926d1a0d267SMarcel Moolenaar xo_do_format_field(xop, NULL, format, flen, flags); 392731337658SMarcel Moolenaar break; 392831337658SMarcel Moolenaar } 392931337658SMarcel Moolenaar 393031337658SMarcel Moolenaar if (encoding) { 393131337658SMarcel Moolenaar format = encoding; 393231337658SMarcel Moolenaar flen = elen; 393331337658SMarcel Moolenaar } else { 393431337658SMarcel Moolenaar char *enc = alloca(flen + 1); 393531337658SMarcel Moolenaar memcpy(enc, format, flen); 393631337658SMarcel Moolenaar enc[flen] = '\0'; 393731337658SMarcel Moolenaar format = xo_fix_encoding(xop, enc); 393831337658SMarcel Moolenaar flen = strlen(format); 393931337658SMarcel Moolenaar } 394031337658SMarcel Moolenaar 394131337658SMarcel Moolenaar int first = !(xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST); 394231337658SMarcel Moolenaar 394331337658SMarcel Moolenaar xo_format_prep(xop, flags); 394431337658SMarcel Moolenaar 394531337658SMarcel Moolenaar if (flags & XFF_QUOTE) 394631337658SMarcel Moolenaar quote = 1; 394731337658SMarcel Moolenaar else if (flags & XFF_NOQUOTE) 394831337658SMarcel Moolenaar quote = 0; 394931337658SMarcel Moolenaar else if (flen == 0) { 395031337658SMarcel Moolenaar quote = 0; 395131337658SMarcel Moolenaar format = "true"; /* JSON encodes empty tags as a boolean true */ 395231337658SMarcel Moolenaar flen = 4; 395331337658SMarcel Moolenaar } else if (strchr("diouxXDOUeEfFgGaAcCp", format[flen - 1]) == NULL) 395431337658SMarcel Moolenaar quote = 1; 395531337658SMarcel Moolenaar else 395631337658SMarcel Moolenaar quote = 0; 395731337658SMarcel Moolenaar 395831337658SMarcel Moolenaar if (nlen == 0) { 395931337658SMarcel Moolenaar static char missing[] = "missing-field-name"; 396031337658SMarcel Moolenaar xo_failure(xop, "missing field name: %s", format); 396131337658SMarcel Moolenaar name = missing; 396231337658SMarcel Moolenaar nlen = sizeof(missing) - 1; 396331337658SMarcel Moolenaar } 396431337658SMarcel Moolenaar 396531337658SMarcel Moolenaar if (flags & XFF_LEAF_LIST) { 3966788ca347SMarcel Moolenaar if (!first && pretty) 3967788ca347SMarcel Moolenaar xo_data_append(xop, "\n", 1); 3968788ca347SMarcel Moolenaar if (pretty) 396931337658SMarcel Moolenaar xo_buf_indent(xop, -1); 397031337658SMarcel Moolenaar } else { 397131337658SMarcel Moolenaar if (pretty) 397231337658SMarcel Moolenaar xo_buf_indent(xop, -1); 397331337658SMarcel Moolenaar xo_data_append(xop, "\"", 1); 397431337658SMarcel Moolenaar 397531337658SMarcel Moolenaar xbp = &xop->xo_data; 397631337658SMarcel Moolenaar int off = xbp->xb_curp - xbp->xb_bufp; 397731337658SMarcel Moolenaar 397831337658SMarcel Moolenaar xo_data_escape(xop, name, nlen); 397931337658SMarcel Moolenaar 3980d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_UNDERSCORES)) { 398131337658SMarcel Moolenaar int now = xbp->xb_curp - xbp->xb_bufp; 398231337658SMarcel Moolenaar for ( ; off < now; off++) 398331337658SMarcel Moolenaar if (xbp->xb_bufp[off] == '-') 398431337658SMarcel Moolenaar xbp->xb_bufp[off] = '_'; 398531337658SMarcel Moolenaar } 398631337658SMarcel Moolenaar xo_data_append(xop, "\":", 2); 398731337658SMarcel Moolenaar if (pretty) 398831337658SMarcel Moolenaar xo_data_append(xop, " ", 1); 3989788ca347SMarcel Moolenaar } 3990788ca347SMarcel Moolenaar 399131337658SMarcel Moolenaar if (quote) 399231337658SMarcel Moolenaar xo_data_append(xop, "\"", 1); 399331337658SMarcel Moolenaar 3994d1a0d267SMarcel Moolenaar xo_do_format_field(xop, NULL, format, flen, flags); 399531337658SMarcel Moolenaar 399631337658SMarcel Moolenaar if (quote) 399731337658SMarcel Moolenaar xo_data_append(xop, "\"", 1); 399831337658SMarcel Moolenaar break; 3999d1a0d267SMarcel Moolenaar 4000d1a0d267SMarcel Moolenaar case XO_STYLE_SDPARAMS: 4001d1a0d267SMarcel Moolenaar if (flags & XFF_DISPLAY_ONLY) { 4002d1a0d267SMarcel Moolenaar flags |= XFF_NO_OUTPUT; 4003d1a0d267SMarcel Moolenaar xo_do_format_field(xop, NULL, format, flen, flags); 4004d1a0d267SMarcel Moolenaar break; 4005d1a0d267SMarcel Moolenaar } 4006d1a0d267SMarcel Moolenaar 4007d1a0d267SMarcel Moolenaar if (encoding) { 4008d1a0d267SMarcel Moolenaar format = encoding; 4009d1a0d267SMarcel Moolenaar flen = elen; 4010d1a0d267SMarcel Moolenaar } else { 4011d1a0d267SMarcel Moolenaar char *enc = alloca(flen + 1); 4012d1a0d267SMarcel Moolenaar memcpy(enc, format, flen); 4013d1a0d267SMarcel Moolenaar enc[flen] = '\0'; 4014d1a0d267SMarcel Moolenaar format = xo_fix_encoding(xop, enc); 4015d1a0d267SMarcel Moolenaar flen = strlen(format); 4016d1a0d267SMarcel Moolenaar } 4017d1a0d267SMarcel Moolenaar 4018d1a0d267SMarcel Moolenaar if (nlen == 0) { 4019d1a0d267SMarcel Moolenaar static char missing[] = "missing-field-name"; 4020d1a0d267SMarcel Moolenaar xo_failure(xop, "missing field name: %s", format); 4021d1a0d267SMarcel Moolenaar name = missing; 4022d1a0d267SMarcel Moolenaar nlen = sizeof(missing) - 1; 4023d1a0d267SMarcel Moolenaar } 4024d1a0d267SMarcel Moolenaar 4025d1a0d267SMarcel Moolenaar xo_data_escape(xop, name, nlen); 4026d1a0d267SMarcel Moolenaar xo_data_append(xop, "=\"", 2); 4027d1a0d267SMarcel Moolenaar xo_do_format_field(xop, NULL, format, flen, flags); 4028d1a0d267SMarcel Moolenaar xo_data_append(xop, "\" ", 2); 4029d1a0d267SMarcel Moolenaar break; 4030d1a0d267SMarcel Moolenaar 4031d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER: 4032d1a0d267SMarcel Moolenaar if (flags & XFF_DISPLAY_ONLY) { 4033d1a0d267SMarcel Moolenaar flags |= XFF_NO_OUTPUT; 4034d1a0d267SMarcel Moolenaar xo_do_format_field(xop, NULL, format, flen, flags); 4035d1a0d267SMarcel Moolenaar break; 4036d1a0d267SMarcel Moolenaar } 4037d1a0d267SMarcel Moolenaar 4038d1a0d267SMarcel Moolenaar if (flags & XFF_QUOTE) 4039d1a0d267SMarcel Moolenaar quote = 1; 4040d1a0d267SMarcel Moolenaar else if (flags & XFF_NOQUOTE) 4041d1a0d267SMarcel Moolenaar quote = 0; 4042d1a0d267SMarcel Moolenaar else if (flen == 0) { 4043d1a0d267SMarcel Moolenaar quote = 0; 4044d1a0d267SMarcel Moolenaar format = "true"; /* JSON encodes empty tags as a boolean true */ 4045d1a0d267SMarcel Moolenaar flen = 4; 4046d1a0d267SMarcel Moolenaar } else if (strchr("diouxXDOUeEfFgGaAcCp", format[flen - 1]) == NULL) 4047d1a0d267SMarcel Moolenaar quote = 1; 4048d1a0d267SMarcel Moolenaar else 4049d1a0d267SMarcel Moolenaar quote = 0; 4050d1a0d267SMarcel Moolenaar 4051d1a0d267SMarcel Moolenaar if (encoding) { 4052d1a0d267SMarcel Moolenaar format = encoding; 4053d1a0d267SMarcel Moolenaar flen = elen; 4054d1a0d267SMarcel Moolenaar } else { 4055d1a0d267SMarcel Moolenaar char *enc = alloca(flen + 1); 4056d1a0d267SMarcel Moolenaar memcpy(enc, format, flen); 4057d1a0d267SMarcel Moolenaar enc[flen] = '\0'; 4058d1a0d267SMarcel Moolenaar format = xo_fix_encoding(xop, enc); 4059d1a0d267SMarcel Moolenaar flen = strlen(format); 4060d1a0d267SMarcel Moolenaar } 4061d1a0d267SMarcel Moolenaar 4062d1a0d267SMarcel Moolenaar if (nlen == 0) { 4063d1a0d267SMarcel Moolenaar static char missing[] = "missing-field-name"; 4064d1a0d267SMarcel Moolenaar xo_failure(xop, "missing field name: %s", format); 4065d1a0d267SMarcel Moolenaar name = missing; 4066d1a0d267SMarcel Moolenaar nlen = sizeof(missing) - 1; 4067d1a0d267SMarcel Moolenaar } 4068d1a0d267SMarcel Moolenaar 4069d1a0d267SMarcel Moolenaar unsigned name_offset = xo_buf_offset(&xop->xo_data); 4070d1a0d267SMarcel Moolenaar xo_data_append(xop, name, nlen); 4071d1a0d267SMarcel Moolenaar xo_data_append(xop, "", 1); 4072d1a0d267SMarcel Moolenaar 4073d1a0d267SMarcel Moolenaar unsigned value_offset = xo_buf_offset(&xop->xo_data); 4074d1a0d267SMarcel Moolenaar xo_do_format_field(xop, NULL, format, flen, flags); 4075d1a0d267SMarcel Moolenaar xo_data_append(xop, "", 1); 4076d1a0d267SMarcel Moolenaar 4077d1a0d267SMarcel Moolenaar xo_encoder_handle(xop, quote ? XO_OP_STRING : XO_OP_CONTENT, 4078d1a0d267SMarcel Moolenaar xo_buf_data(&xop->xo_data, name_offset), 4079d1a0d267SMarcel Moolenaar xo_buf_data(&xop->xo_data, value_offset)); 4080d1a0d267SMarcel Moolenaar xo_buf_reset(&xop->xo_data); 4081d1a0d267SMarcel Moolenaar break; 408231337658SMarcel Moolenaar } 408331337658SMarcel Moolenaar } 408431337658SMarcel Moolenaar 408531337658SMarcel Moolenaar static void 4086d1a0d267SMarcel Moolenaar xo_set_gettext_domain (xo_handle_t *xop, xo_field_info_t *xfip) 4087d1a0d267SMarcel Moolenaar { 4088d1a0d267SMarcel Moolenaar const char *str = xfip->xfi_content; 4089d1a0d267SMarcel Moolenaar unsigned len = xfip->xfi_clen; 4090d1a0d267SMarcel Moolenaar const char *fmt = xfip->xfi_format; 4091d1a0d267SMarcel Moolenaar unsigned flen = xfip->xfi_flen; 4092d1a0d267SMarcel Moolenaar 4093d1a0d267SMarcel Moolenaar /* Start by discarding previous domain */ 4094d1a0d267SMarcel Moolenaar if (xop->xo_gt_domain) { 4095d1a0d267SMarcel Moolenaar xo_free(xop->xo_gt_domain); 4096d1a0d267SMarcel Moolenaar xop->xo_gt_domain = NULL; 4097d1a0d267SMarcel Moolenaar } 4098d1a0d267SMarcel Moolenaar 4099d1a0d267SMarcel Moolenaar /* An empty {G:} means no domainname */ 4100d1a0d267SMarcel Moolenaar if (len == 0 && flen == 0) 4101d1a0d267SMarcel Moolenaar return; 4102d1a0d267SMarcel Moolenaar 4103d1a0d267SMarcel Moolenaar int start_offset = -1; 4104d1a0d267SMarcel Moolenaar if (len == 0 && flen != 0) { 4105d1a0d267SMarcel Moolenaar /* Need to do format the data to get the domainname from args */ 4106d1a0d267SMarcel Moolenaar start_offset = xop->xo_data.xb_curp - xop->xo_data.xb_bufp; 4107d1a0d267SMarcel Moolenaar xo_do_format_field(xop, NULL, fmt, flen, 0); 4108d1a0d267SMarcel Moolenaar 4109d1a0d267SMarcel Moolenaar int end_offset = xop->xo_data.xb_curp - xop->xo_data.xb_bufp; 4110d1a0d267SMarcel Moolenaar len = end_offset - start_offset; 4111d1a0d267SMarcel Moolenaar str = xop->xo_data.xb_bufp + start_offset; 4112d1a0d267SMarcel Moolenaar } 4113d1a0d267SMarcel Moolenaar 4114d1a0d267SMarcel Moolenaar xop->xo_gt_domain = xo_strndup(str, len); 4115d1a0d267SMarcel Moolenaar 4116d1a0d267SMarcel Moolenaar /* Reset the current buffer point to avoid emitting the name as output */ 4117d1a0d267SMarcel Moolenaar if (start_offset >= 0) 4118d1a0d267SMarcel Moolenaar xop->xo_data.xb_curp = xop->xo_data.xb_bufp + start_offset; 4119d1a0d267SMarcel Moolenaar } 4120d1a0d267SMarcel Moolenaar 4121d1a0d267SMarcel Moolenaar static void 412231337658SMarcel Moolenaar xo_format_content (xo_handle_t *xop, const char *class_name, 4123d1a0d267SMarcel Moolenaar const char *tag_name, 4124d1a0d267SMarcel Moolenaar const char *str, int len, const char *fmt, int flen, 4125d1a0d267SMarcel Moolenaar xo_xff_flags_t flags) 412631337658SMarcel Moolenaar { 4127788ca347SMarcel Moolenaar switch (xo_style(xop)) { 412831337658SMarcel Moolenaar case XO_STYLE_TEXT: 4129d1a0d267SMarcel Moolenaar if (len) 4130d1a0d267SMarcel Moolenaar xo_data_append_content(xop, str, len, flags); 4131d1a0d267SMarcel Moolenaar else 4132d1a0d267SMarcel Moolenaar xo_do_format_field(xop, NULL, fmt, flen, flags); 413331337658SMarcel Moolenaar break; 413431337658SMarcel Moolenaar 413531337658SMarcel Moolenaar case XO_STYLE_HTML: 413631337658SMarcel Moolenaar if (len == 0) { 413731337658SMarcel Moolenaar str = fmt; 413831337658SMarcel Moolenaar len = flen; 413931337658SMarcel Moolenaar } 414031337658SMarcel Moolenaar 4141d1a0d267SMarcel Moolenaar xo_buf_append_div(xop, class_name, flags, NULL, 0, str, len, NULL, 0); 414231337658SMarcel Moolenaar break; 414331337658SMarcel Moolenaar 414431337658SMarcel Moolenaar case XO_STYLE_XML: 4145d1a0d267SMarcel Moolenaar case XO_STYLE_JSON: 4146d1a0d267SMarcel Moolenaar case XO_STYLE_SDPARAMS: 4147d1a0d267SMarcel Moolenaar if (tag_name) { 414831337658SMarcel Moolenaar if (len == 0) { 414931337658SMarcel Moolenaar str = fmt; 415031337658SMarcel Moolenaar len = flen; 415131337658SMarcel Moolenaar } 415231337658SMarcel Moolenaar 4153d1a0d267SMarcel Moolenaar xo_open_container_h(xop, tag_name); 4154d1a0d267SMarcel Moolenaar xo_format_value(xop, "message", 7, str, len, NULL, 0, flags); 4155d1a0d267SMarcel Moolenaar xo_close_container_h(xop, tag_name); 415631337658SMarcel Moolenaar 415731337658SMarcel Moolenaar } else { 415831337658SMarcel Moolenaar /* 415931337658SMarcel Moolenaar * Even though we don't care about labels, we need to do 416031337658SMarcel Moolenaar * enough parsing work to skip over the right bits of xo_vap. 416131337658SMarcel Moolenaar */ 416231337658SMarcel Moolenaar if (len == 0) 4163d1a0d267SMarcel Moolenaar xo_do_format_field(xop, NULL, fmt, flen, 4164d1a0d267SMarcel Moolenaar flags | XFF_NO_OUTPUT); 416531337658SMarcel Moolenaar } 416631337658SMarcel Moolenaar break; 416731337658SMarcel Moolenaar 4168d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER: 416931337658SMarcel Moolenaar if (len == 0) 4170d1a0d267SMarcel Moolenaar xo_do_format_field(xop, NULL, fmt, flen, 4171d1a0d267SMarcel Moolenaar flags | XFF_NO_OUTPUT); 417231337658SMarcel Moolenaar break; 417331337658SMarcel Moolenaar } 417431337658SMarcel Moolenaar } 417531337658SMarcel Moolenaar 4176788ca347SMarcel Moolenaar static const char *xo_color_names[] = { 4177788ca347SMarcel Moolenaar "default", /* XO_COL_DEFAULT */ 4178788ca347SMarcel Moolenaar "black", /* XO_COL_BLACK */ 4179788ca347SMarcel Moolenaar "red", /* XO_CLOR_RED */ 4180788ca347SMarcel Moolenaar "green", /* XO_COL_GREEN */ 4181788ca347SMarcel Moolenaar "yellow", /* XO_COL_YELLOW */ 4182788ca347SMarcel Moolenaar "blue", /* XO_COL_BLUE */ 4183788ca347SMarcel Moolenaar "magenta", /* XO_COL_MAGENTA */ 4184788ca347SMarcel Moolenaar "cyan", /* XO_COL_CYAN */ 4185788ca347SMarcel Moolenaar "white", /* XO_COL_WHITE */ 4186788ca347SMarcel Moolenaar NULL 4187788ca347SMarcel Moolenaar }; 4188788ca347SMarcel Moolenaar 4189788ca347SMarcel Moolenaar static int 4190788ca347SMarcel Moolenaar xo_color_find (const char *str) 4191788ca347SMarcel Moolenaar { 4192788ca347SMarcel Moolenaar int i; 4193788ca347SMarcel Moolenaar 4194788ca347SMarcel Moolenaar for (i = 0; xo_color_names[i]; i++) { 4195788ca347SMarcel Moolenaar if (strcmp(xo_color_names[i], str) == 0) 4196788ca347SMarcel Moolenaar return i; 4197788ca347SMarcel Moolenaar } 4198788ca347SMarcel Moolenaar 4199788ca347SMarcel Moolenaar return -1; 4200788ca347SMarcel Moolenaar } 4201788ca347SMarcel Moolenaar 4202788ca347SMarcel Moolenaar static const char *xo_effect_names[] = { 4203788ca347SMarcel Moolenaar "reset", /* XO_EFF_RESET */ 4204788ca347SMarcel Moolenaar "normal", /* XO_EFF_NORMAL */ 4205788ca347SMarcel Moolenaar "bold", /* XO_EFF_BOLD */ 4206788ca347SMarcel Moolenaar "underline", /* XO_EFF_UNDERLINE */ 4207788ca347SMarcel Moolenaar "inverse", /* XO_EFF_INVERSE */ 4208788ca347SMarcel Moolenaar NULL 4209788ca347SMarcel Moolenaar }; 4210788ca347SMarcel Moolenaar 4211788ca347SMarcel Moolenaar static const char *xo_effect_on_codes[] = { 4212788ca347SMarcel Moolenaar "0", /* XO_EFF_RESET */ 4213788ca347SMarcel Moolenaar "0", /* XO_EFF_NORMAL */ 4214788ca347SMarcel Moolenaar "1", /* XO_EFF_BOLD */ 4215788ca347SMarcel Moolenaar "4", /* XO_EFF_UNDERLINE */ 4216788ca347SMarcel Moolenaar "7", /* XO_EFF_INVERSE */ 4217788ca347SMarcel Moolenaar NULL 4218788ca347SMarcel Moolenaar }; 4219788ca347SMarcel Moolenaar 4220788ca347SMarcel Moolenaar #if 0 4221788ca347SMarcel Moolenaar /* 4222788ca347SMarcel Moolenaar * See comment below re: joy of terminal standards. These can 4223788ca347SMarcel Moolenaar * be use by just adding: 4224d1a0d267SMarcel Moolenaar * + if (newp->xoc_effects & bit) 4225788ca347SMarcel Moolenaar * code = xo_effect_on_codes[i]; 4226788ca347SMarcel Moolenaar * + else 4227788ca347SMarcel Moolenaar * + code = xo_effect_off_codes[i]; 4228788ca347SMarcel Moolenaar * in xo_color_handle_text. 4229788ca347SMarcel Moolenaar */ 4230788ca347SMarcel Moolenaar static const char *xo_effect_off_codes[] = { 4231788ca347SMarcel Moolenaar "0", /* XO_EFF_RESET */ 4232788ca347SMarcel Moolenaar "0", /* XO_EFF_NORMAL */ 4233788ca347SMarcel Moolenaar "21", /* XO_EFF_BOLD */ 4234788ca347SMarcel Moolenaar "24", /* XO_EFF_UNDERLINE */ 4235788ca347SMarcel Moolenaar "27", /* XO_EFF_INVERSE */ 4236788ca347SMarcel Moolenaar NULL 4237788ca347SMarcel Moolenaar }; 4238788ca347SMarcel Moolenaar #endif /* 0 */ 4239788ca347SMarcel Moolenaar 4240788ca347SMarcel Moolenaar static int 4241788ca347SMarcel Moolenaar xo_effect_find (const char *str) 4242788ca347SMarcel Moolenaar { 4243788ca347SMarcel Moolenaar int i; 4244788ca347SMarcel Moolenaar 4245788ca347SMarcel Moolenaar for (i = 0; xo_effect_names[i]; i++) { 4246788ca347SMarcel Moolenaar if (strcmp(xo_effect_names[i], str) == 0) 4247788ca347SMarcel Moolenaar return i; 4248788ca347SMarcel Moolenaar } 4249788ca347SMarcel Moolenaar 4250788ca347SMarcel Moolenaar return -1; 4251788ca347SMarcel Moolenaar } 4252788ca347SMarcel Moolenaar 4253788ca347SMarcel Moolenaar static void 4254788ca347SMarcel Moolenaar xo_colors_parse (xo_handle_t *xop, xo_colors_t *xocp, char *str) 4255788ca347SMarcel Moolenaar { 4256788ca347SMarcel Moolenaar #ifdef LIBXO_TEXT_ONLY 4257788ca347SMarcel Moolenaar return; 4258788ca347SMarcel Moolenaar #endif /* LIBXO_TEXT_ONLY */ 4259788ca347SMarcel Moolenaar 4260788ca347SMarcel Moolenaar char *cp, *ep, *np, *xp; 4261788ca347SMarcel Moolenaar int len = strlen(str); 4262788ca347SMarcel Moolenaar int rc; 4263788ca347SMarcel Moolenaar 4264788ca347SMarcel Moolenaar /* 4265788ca347SMarcel Moolenaar * Possible tokens: colors, bg-colors, effects, no-effects, "reset". 4266788ca347SMarcel Moolenaar */ 4267788ca347SMarcel Moolenaar for (cp = str, ep = cp + len - 1; cp && cp < ep; cp = np) { 4268788ca347SMarcel Moolenaar /* Trim leading whitespace */ 4269788ca347SMarcel Moolenaar while (isspace((int) *cp)) 4270788ca347SMarcel Moolenaar cp += 1; 4271788ca347SMarcel Moolenaar 4272788ca347SMarcel Moolenaar np = strchr(cp, ','); 4273788ca347SMarcel Moolenaar if (np) 4274788ca347SMarcel Moolenaar *np++ = '\0'; 4275788ca347SMarcel Moolenaar 4276788ca347SMarcel Moolenaar /* Trim trailing whitespace */ 4277788ca347SMarcel Moolenaar xp = cp + strlen(cp) - 1; 4278788ca347SMarcel Moolenaar while (isspace(*xp) && xp > cp) 4279788ca347SMarcel Moolenaar *xp-- = '\0'; 4280788ca347SMarcel Moolenaar 4281788ca347SMarcel Moolenaar if (cp[0] == 'f' && cp[1] == 'g' && cp[2] == '-') { 4282788ca347SMarcel Moolenaar rc = xo_color_find(cp + 3); 4283788ca347SMarcel Moolenaar if (rc < 0) 4284788ca347SMarcel Moolenaar goto unknown; 4285788ca347SMarcel Moolenaar 4286788ca347SMarcel Moolenaar xocp->xoc_col_fg = rc; 4287788ca347SMarcel Moolenaar 4288788ca347SMarcel Moolenaar } else if (cp[0] == 'b' && cp[1] == 'g' && cp[2] == '-') { 4289788ca347SMarcel Moolenaar rc = xo_color_find(cp + 3); 4290788ca347SMarcel Moolenaar if (rc < 0) 4291788ca347SMarcel Moolenaar goto unknown; 4292788ca347SMarcel Moolenaar xocp->xoc_col_bg = rc; 4293788ca347SMarcel Moolenaar 4294788ca347SMarcel Moolenaar } else if (cp[0] == 'n' && cp[1] == 'o' && cp[2] == '-') { 4295788ca347SMarcel Moolenaar rc = xo_effect_find(cp + 3); 4296788ca347SMarcel Moolenaar if (rc < 0) 4297788ca347SMarcel Moolenaar goto unknown; 4298788ca347SMarcel Moolenaar xocp->xoc_effects &= ~(1 << rc); 4299788ca347SMarcel Moolenaar 4300788ca347SMarcel Moolenaar } else { 4301788ca347SMarcel Moolenaar rc = xo_effect_find(cp); 4302788ca347SMarcel Moolenaar if (rc < 0) 4303788ca347SMarcel Moolenaar goto unknown; 4304788ca347SMarcel Moolenaar xocp->xoc_effects |= 1 << rc; 4305788ca347SMarcel Moolenaar 4306788ca347SMarcel Moolenaar switch (1 << rc) { 4307788ca347SMarcel Moolenaar case XO_EFF_RESET: 4308788ca347SMarcel Moolenaar xocp->xoc_col_fg = xocp->xoc_col_bg = 0; 4309788ca347SMarcel Moolenaar /* Note: not "|=" since we want to wipe out the old value */ 4310788ca347SMarcel Moolenaar xocp->xoc_effects = XO_EFF_RESET; 4311788ca347SMarcel Moolenaar break; 4312788ca347SMarcel Moolenaar 4313788ca347SMarcel Moolenaar case XO_EFF_NORMAL: 4314788ca347SMarcel Moolenaar xocp->xoc_effects &= ~(XO_EFF_BOLD | XO_EFF_UNDERLINE 4315788ca347SMarcel Moolenaar | XO_EFF_INVERSE | XO_EFF_NORMAL); 4316788ca347SMarcel Moolenaar break; 4317788ca347SMarcel Moolenaar } 4318788ca347SMarcel Moolenaar } 4319788ca347SMarcel Moolenaar continue; 4320788ca347SMarcel Moolenaar 4321788ca347SMarcel Moolenaar unknown: 4322d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_WARN)) 4323788ca347SMarcel Moolenaar xo_failure(xop, "unknown color/effect string detected: '%s'", cp); 4324788ca347SMarcel Moolenaar } 4325788ca347SMarcel Moolenaar } 4326788ca347SMarcel Moolenaar 4327788ca347SMarcel Moolenaar static inline int 4328788ca347SMarcel Moolenaar xo_colors_enabled (xo_handle_t *xop UNUSED) 4329788ca347SMarcel Moolenaar { 4330788ca347SMarcel Moolenaar #ifdef LIBXO_TEXT_ONLY 4331788ca347SMarcel Moolenaar return 0; 4332788ca347SMarcel Moolenaar #else /* LIBXO_TEXT_ONLY */ 4333d1a0d267SMarcel Moolenaar return XOF_ISSET(xop, XOF_COLOR); 4334788ca347SMarcel Moolenaar #endif /* LIBXO_TEXT_ONLY */ 4335788ca347SMarcel Moolenaar } 4336788ca347SMarcel Moolenaar 4337788ca347SMarcel Moolenaar static void 4338788ca347SMarcel Moolenaar xo_colors_handle_text (xo_handle_t *xop UNUSED, xo_colors_t *newp) 4339788ca347SMarcel Moolenaar { 4340788ca347SMarcel Moolenaar char buf[BUFSIZ]; 4341788ca347SMarcel Moolenaar char *cp = buf, *ep = buf + sizeof(buf); 4342788ca347SMarcel Moolenaar unsigned i, bit; 4343788ca347SMarcel Moolenaar xo_colors_t *oldp = &xop->xo_colors; 43443865ad3fSMarcel Moolenaar const char *code = NULL; 4345788ca347SMarcel Moolenaar 4346788ca347SMarcel Moolenaar /* 4347788ca347SMarcel Moolenaar * Start the buffer with an escape. We don't want to add the '[' 4348788ca347SMarcel Moolenaar * now, since we let xo_effect_text_add unconditionally add the ';'. 4349788ca347SMarcel Moolenaar * We'll replace the first ';' with a '[' when we're done. 4350788ca347SMarcel Moolenaar */ 4351788ca347SMarcel Moolenaar *cp++ = 0x1b; /* Escape */ 4352788ca347SMarcel Moolenaar 4353788ca347SMarcel Moolenaar /* 4354788ca347SMarcel Moolenaar * Terminals were designed back in the age before "certainty" was 4355788ca347SMarcel Moolenaar * invented, when standards were more what you'd call "guidelines" 4356788ca347SMarcel Moolenaar * than actual rules. Anyway we can't depend on them to operate 4357788ca347SMarcel Moolenaar * correctly. So when display attributes are changed, we punt, 4358788ca347SMarcel Moolenaar * reseting them all and turning back on the ones we want to keep. 4359788ca347SMarcel Moolenaar * Longer, but should be completely reliable. Savvy? 4360788ca347SMarcel Moolenaar */ 4361788ca347SMarcel Moolenaar if (oldp->xoc_effects != (newp->xoc_effects & oldp->xoc_effects)) { 4362788ca347SMarcel Moolenaar newp->xoc_effects |= XO_EFF_RESET; 4363788ca347SMarcel Moolenaar oldp->xoc_effects = 0; 4364788ca347SMarcel Moolenaar } 4365788ca347SMarcel Moolenaar 4366788ca347SMarcel Moolenaar for (i = 0, bit = 1; xo_effect_names[i]; i++, bit <<= 1) { 4367788ca347SMarcel Moolenaar if ((newp->xoc_effects & bit) == (oldp->xoc_effects & bit)) 4368788ca347SMarcel Moolenaar continue; 4369788ca347SMarcel Moolenaar 4370788ca347SMarcel Moolenaar code = xo_effect_on_codes[i]; 4371788ca347SMarcel Moolenaar 4372788ca347SMarcel Moolenaar cp += snprintf(cp, ep - cp, ";%s", code); 4373788ca347SMarcel Moolenaar if (cp >= ep) 4374788ca347SMarcel Moolenaar return; /* Should not occur */ 4375788ca347SMarcel Moolenaar 4376788ca347SMarcel Moolenaar if (bit == XO_EFF_RESET) { 4377788ca347SMarcel Moolenaar /* Mark up the old value so we can detect current values as new */ 4378788ca347SMarcel Moolenaar oldp->xoc_effects = 0; 4379788ca347SMarcel Moolenaar oldp->xoc_col_fg = oldp->xoc_col_bg = XO_COL_DEFAULT; 4380788ca347SMarcel Moolenaar } 4381788ca347SMarcel Moolenaar } 4382788ca347SMarcel Moolenaar 4383788ca347SMarcel Moolenaar if (newp->xoc_col_fg != oldp->xoc_col_fg) { 4384788ca347SMarcel Moolenaar cp += snprintf(cp, ep - cp, ";3%u", 4385788ca347SMarcel Moolenaar (newp->xoc_col_fg != XO_COL_DEFAULT) 4386788ca347SMarcel Moolenaar ? newp->xoc_col_fg - 1 : 9); 4387788ca347SMarcel Moolenaar } 4388788ca347SMarcel Moolenaar 4389788ca347SMarcel Moolenaar if (newp->xoc_col_bg != oldp->xoc_col_bg) { 4390788ca347SMarcel Moolenaar cp += snprintf(cp, ep - cp, ";4%u", 4391788ca347SMarcel Moolenaar (newp->xoc_col_bg != XO_COL_DEFAULT) 4392788ca347SMarcel Moolenaar ? newp->xoc_col_bg - 1 : 9); 4393788ca347SMarcel Moolenaar } 4394788ca347SMarcel Moolenaar 4395788ca347SMarcel Moolenaar if (cp - buf != 1 && cp < ep - 3) { 4396788ca347SMarcel Moolenaar buf[1] = '['; /* Overwrite leading ';' */ 4397788ca347SMarcel Moolenaar *cp++ = 'm'; 4398788ca347SMarcel Moolenaar *cp = '\0'; 4399788ca347SMarcel Moolenaar xo_buf_append(&xop->xo_data, buf, cp - buf); 4400788ca347SMarcel Moolenaar } 4401788ca347SMarcel Moolenaar } 4402788ca347SMarcel Moolenaar 4403788ca347SMarcel Moolenaar static void 4404788ca347SMarcel Moolenaar xo_colors_handle_html (xo_handle_t *xop, xo_colors_t *newp) 4405788ca347SMarcel Moolenaar { 4406788ca347SMarcel Moolenaar xo_colors_t *oldp = &xop->xo_colors; 4407788ca347SMarcel Moolenaar 4408788ca347SMarcel Moolenaar /* 4409788ca347SMarcel Moolenaar * HTML colors are mostly trivial: fill in xo_color_buf with 4410788ca347SMarcel Moolenaar * a set of class tags representing the colors and effects. 4411788ca347SMarcel Moolenaar */ 4412788ca347SMarcel Moolenaar 4413788ca347SMarcel Moolenaar /* If nothing changed, then do nothing */ 4414788ca347SMarcel Moolenaar if (oldp->xoc_effects == newp->xoc_effects 4415788ca347SMarcel Moolenaar && oldp->xoc_col_fg == newp->xoc_col_fg 4416788ca347SMarcel Moolenaar && oldp->xoc_col_bg == newp->xoc_col_bg) 4417788ca347SMarcel Moolenaar return; 4418788ca347SMarcel Moolenaar 4419788ca347SMarcel Moolenaar unsigned i, bit; 4420788ca347SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_color_buf; 4421788ca347SMarcel Moolenaar 4422788ca347SMarcel Moolenaar xo_buf_reset(xbp); /* We rebuild content after each change */ 4423788ca347SMarcel Moolenaar 4424788ca347SMarcel Moolenaar for (i = 0, bit = 1; xo_effect_names[i]; i++, bit <<= 1) { 4425788ca347SMarcel Moolenaar if (!(newp->xoc_effects & bit)) 4426788ca347SMarcel Moolenaar continue; 4427788ca347SMarcel Moolenaar 4428788ca347SMarcel Moolenaar xo_buf_append_str(xbp, " effect-"); 4429788ca347SMarcel Moolenaar xo_buf_append_str(xbp, xo_effect_names[i]); 4430788ca347SMarcel Moolenaar } 4431788ca347SMarcel Moolenaar 4432788ca347SMarcel Moolenaar const char *fg = NULL; 4433788ca347SMarcel Moolenaar const char *bg = NULL; 4434788ca347SMarcel Moolenaar 4435788ca347SMarcel Moolenaar if (newp->xoc_col_fg != XO_COL_DEFAULT) 4436788ca347SMarcel Moolenaar fg = xo_color_names[newp->xoc_col_fg]; 4437788ca347SMarcel Moolenaar if (newp->xoc_col_bg != XO_COL_DEFAULT) 4438788ca347SMarcel Moolenaar bg = xo_color_names[newp->xoc_col_bg]; 4439788ca347SMarcel Moolenaar 4440788ca347SMarcel Moolenaar if (newp->xoc_effects & XO_EFF_INVERSE) { 4441788ca347SMarcel Moolenaar const char *tmp = fg; 4442788ca347SMarcel Moolenaar fg = bg; 4443788ca347SMarcel Moolenaar bg = tmp; 4444788ca347SMarcel Moolenaar if (fg == NULL) 4445788ca347SMarcel Moolenaar fg = "inverse"; 4446788ca347SMarcel Moolenaar if (bg == NULL) 4447788ca347SMarcel Moolenaar bg = "inverse"; 4448788ca347SMarcel Moolenaar 4449788ca347SMarcel Moolenaar } 4450788ca347SMarcel Moolenaar 4451788ca347SMarcel Moolenaar if (fg) { 4452788ca347SMarcel Moolenaar xo_buf_append_str(xbp, " color-fg-"); 4453788ca347SMarcel Moolenaar xo_buf_append_str(xbp, fg); 4454788ca347SMarcel Moolenaar } 4455788ca347SMarcel Moolenaar 4456788ca347SMarcel Moolenaar if (bg) { 4457788ca347SMarcel Moolenaar xo_buf_append_str(xbp, " color-bg-"); 4458788ca347SMarcel Moolenaar xo_buf_append_str(xbp, bg); 4459788ca347SMarcel Moolenaar } 4460788ca347SMarcel Moolenaar } 4461788ca347SMarcel Moolenaar 4462788ca347SMarcel Moolenaar static void 4463d1a0d267SMarcel Moolenaar xo_format_colors (xo_handle_t *xop, xo_field_info_t *xfip) 4464788ca347SMarcel Moolenaar { 4465d1a0d267SMarcel Moolenaar const char *str = xfip->xfi_content; 4466d1a0d267SMarcel Moolenaar unsigned len = xfip->xfi_clen; 4467d1a0d267SMarcel Moolenaar const char *fmt = xfip->xfi_format; 4468d1a0d267SMarcel Moolenaar unsigned flen = xfip->xfi_flen; 4469d1a0d267SMarcel Moolenaar 4470788ca347SMarcel Moolenaar xo_buffer_t xb; 4471788ca347SMarcel Moolenaar 4472788ca347SMarcel Moolenaar /* If the string is static and we've in an encoding style, bail */ 4473d1a0d267SMarcel Moolenaar if (len != 0 && xo_style_is_encoding(xop)) 4474788ca347SMarcel Moolenaar return; 4475788ca347SMarcel Moolenaar 4476788ca347SMarcel Moolenaar xo_buf_init(&xb); 4477788ca347SMarcel Moolenaar 4478788ca347SMarcel Moolenaar if (len) 4479788ca347SMarcel Moolenaar xo_buf_append(&xb, str, len); 4480788ca347SMarcel Moolenaar else if (flen) 4481d1a0d267SMarcel Moolenaar xo_do_format_field(xop, &xb, fmt, flen, 0); 4482788ca347SMarcel Moolenaar else 4483788ca347SMarcel Moolenaar xo_buf_append(&xb, "reset", 6); /* Default if empty */ 4484788ca347SMarcel Moolenaar 4485788ca347SMarcel Moolenaar if (xo_colors_enabled(xop)) { 4486788ca347SMarcel Moolenaar switch (xo_style(xop)) { 4487788ca347SMarcel Moolenaar case XO_STYLE_TEXT: 4488788ca347SMarcel Moolenaar case XO_STYLE_HTML: 4489788ca347SMarcel Moolenaar xo_buf_append(&xb, "", 1); 4490788ca347SMarcel Moolenaar 4491788ca347SMarcel Moolenaar xo_colors_t xoc = xop->xo_colors; 4492788ca347SMarcel Moolenaar xo_colors_parse(xop, &xoc, xb.xb_bufp); 4493788ca347SMarcel Moolenaar 4494788ca347SMarcel Moolenaar if (xo_style(xop) == XO_STYLE_TEXT) { 4495788ca347SMarcel Moolenaar /* 4496788ca347SMarcel Moolenaar * Text mode means emitting the colors as ANSI character 4497788ca347SMarcel Moolenaar * codes. This will allow people who like colors to have 4498788ca347SMarcel Moolenaar * colors. The issue is, of course conflicting with the 4499788ca347SMarcel Moolenaar * user's perfectly reasonable color scheme. Which leads 4500788ca347SMarcel Moolenaar * to the hell of LSCOLORS, where even app need to have 4501788ca347SMarcel Moolenaar * customization hooks for adjusting colors. Instead we 4502788ca347SMarcel Moolenaar * provide a simpler-but-still-annoying answer where one 4503788ca347SMarcel Moolenaar * can map colors to other colors. 4504788ca347SMarcel Moolenaar */ 4505788ca347SMarcel Moolenaar xo_colors_handle_text(xop, &xoc); 4506788ca347SMarcel Moolenaar xoc.xoc_effects &= ~XO_EFF_RESET; /* After handling it */ 4507788ca347SMarcel Moolenaar 4508788ca347SMarcel Moolenaar } else { 4509788ca347SMarcel Moolenaar /* 4510788ca347SMarcel Moolenaar * HTML output is wrapped in divs, so the color information 4511788ca347SMarcel Moolenaar * must appear in every div until cleared. Most pathetic. 4512788ca347SMarcel Moolenaar * Most unavoidable. 4513788ca347SMarcel Moolenaar */ 4514788ca347SMarcel Moolenaar xoc.xoc_effects &= ~XO_EFF_RESET; /* Before handling effects */ 4515788ca347SMarcel Moolenaar xo_colors_handle_html(xop, &xoc); 4516788ca347SMarcel Moolenaar } 4517788ca347SMarcel Moolenaar 4518788ca347SMarcel Moolenaar xop->xo_colors = xoc; 4519788ca347SMarcel Moolenaar break; 4520788ca347SMarcel Moolenaar 4521788ca347SMarcel Moolenaar case XO_STYLE_XML: 4522788ca347SMarcel Moolenaar case XO_STYLE_JSON: 4523d1a0d267SMarcel Moolenaar case XO_STYLE_SDPARAMS: 4524d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER: 4525788ca347SMarcel Moolenaar /* 4526788ca347SMarcel Moolenaar * Nothing to do; we did all that work just to clear the stack of 4527788ca347SMarcel Moolenaar * formatting arguments. 4528788ca347SMarcel Moolenaar */ 4529788ca347SMarcel Moolenaar break; 4530788ca347SMarcel Moolenaar } 4531788ca347SMarcel Moolenaar } 4532788ca347SMarcel Moolenaar 4533788ca347SMarcel Moolenaar xo_buf_cleanup(&xb); 4534788ca347SMarcel Moolenaar } 4535788ca347SMarcel Moolenaar 453631337658SMarcel Moolenaar static void 4537d1a0d267SMarcel Moolenaar xo_format_units (xo_handle_t *xop, xo_field_info_t *xfip) 453831337658SMarcel Moolenaar { 4539d1a0d267SMarcel Moolenaar const char *str = xfip->xfi_content; 4540d1a0d267SMarcel Moolenaar unsigned len = xfip->xfi_clen; 4541d1a0d267SMarcel Moolenaar const char *fmt = xfip->xfi_format; 4542d1a0d267SMarcel Moolenaar unsigned flen = xfip->xfi_flen; 4543d1a0d267SMarcel Moolenaar xo_xff_flags_t flags = xfip->xfi_flags; 4544d1a0d267SMarcel Moolenaar 454531337658SMarcel Moolenaar static char units_start_xml[] = " units=\""; 454631337658SMarcel Moolenaar static char units_start_html[] = " data-units=\""; 454731337658SMarcel Moolenaar 4548d1a0d267SMarcel Moolenaar if (!XOIF_ISSET(xop, XOIF_UNITS_PENDING)) { 4549d1a0d267SMarcel Moolenaar xo_format_content(xop, "units", NULL, str, len, fmt, flen, flags); 455031337658SMarcel Moolenaar return; 455131337658SMarcel Moolenaar } 455231337658SMarcel Moolenaar 455331337658SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_data; 455431337658SMarcel Moolenaar int start = xop->xo_units_offset; 455531337658SMarcel Moolenaar int stop = xbp->xb_curp - xbp->xb_bufp; 455631337658SMarcel Moolenaar 4557788ca347SMarcel Moolenaar if (xo_style(xop) == XO_STYLE_XML) 455831337658SMarcel Moolenaar xo_buf_append(xbp, units_start_xml, sizeof(units_start_xml) - 1); 4559788ca347SMarcel Moolenaar else if (xo_style(xop) == XO_STYLE_HTML) 456031337658SMarcel Moolenaar xo_buf_append(xbp, units_start_html, sizeof(units_start_html) - 1); 456131337658SMarcel Moolenaar else 456231337658SMarcel Moolenaar return; 456331337658SMarcel Moolenaar 456431337658SMarcel Moolenaar if (len) 4565d1a0d267SMarcel Moolenaar xo_data_escape(xop, str, len); 456631337658SMarcel Moolenaar else 4567d1a0d267SMarcel Moolenaar xo_do_format_field(xop, NULL, fmt, flen, flags); 456831337658SMarcel Moolenaar 456931337658SMarcel Moolenaar xo_buf_append(xbp, "\"", 1); 457031337658SMarcel Moolenaar 457131337658SMarcel Moolenaar int now = xbp->xb_curp - xbp->xb_bufp; 457231337658SMarcel Moolenaar int delta = now - stop; 4573d1a0d267SMarcel Moolenaar if (delta <= 0) { /* Strange; no output to move */ 457431337658SMarcel Moolenaar xbp->xb_curp = xbp->xb_bufp + stop; /* Reset buffer to prior state */ 457531337658SMarcel Moolenaar return; 457631337658SMarcel Moolenaar } 457731337658SMarcel Moolenaar 457831337658SMarcel Moolenaar /* 457931337658SMarcel Moolenaar * Now we're in it alright. We've need to insert the unit value 458031337658SMarcel Moolenaar * we just created into the right spot. We make a local copy, 458131337658SMarcel Moolenaar * move it and then insert our copy. We know there's room in the 458231337658SMarcel Moolenaar * buffer, since we're just moving this around. 458331337658SMarcel Moolenaar */ 458431337658SMarcel Moolenaar char *buf = alloca(delta); 458531337658SMarcel Moolenaar 458631337658SMarcel Moolenaar memcpy(buf, xbp->xb_bufp + stop, delta); 458731337658SMarcel Moolenaar memmove(xbp->xb_bufp + start + delta, xbp->xb_bufp + start, stop - start); 458831337658SMarcel Moolenaar memmove(xbp->xb_bufp + start, buf, delta); 458931337658SMarcel Moolenaar } 459031337658SMarcel Moolenaar 459131337658SMarcel Moolenaar static int 4592d1a0d267SMarcel Moolenaar xo_find_width (xo_handle_t *xop, xo_field_info_t *xfip) 459331337658SMarcel Moolenaar { 4594d1a0d267SMarcel Moolenaar const char *str = xfip->xfi_content; 4595d1a0d267SMarcel Moolenaar unsigned len = xfip->xfi_clen; 4596d1a0d267SMarcel Moolenaar const char *fmt = xfip->xfi_format; 4597d1a0d267SMarcel Moolenaar unsigned flen = xfip->xfi_flen; 4598d1a0d267SMarcel Moolenaar 459931337658SMarcel Moolenaar long width = 0; 460031337658SMarcel Moolenaar char *bp; 460131337658SMarcel Moolenaar char *cp; 460231337658SMarcel Moolenaar 460331337658SMarcel Moolenaar if (len) { 460431337658SMarcel Moolenaar bp = alloca(len + 1); /* Make local NUL-terminated copy of str */ 460531337658SMarcel Moolenaar memcpy(bp, str, len); 460631337658SMarcel Moolenaar bp[len] = '\0'; 460731337658SMarcel Moolenaar 460831337658SMarcel Moolenaar width = strtol(bp, &cp, 0); 460931337658SMarcel Moolenaar if (width == LONG_MIN || width == LONG_MAX 461031337658SMarcel Moolenaar || bp == cp || *cp != '\0' ) { 461131337658SMarcel Moolenaar width = 0; 461231337658SMarcel Moolenaar xo_failure(xop, "invalid width for anchor: '%s'", bp); 461331337658SMarcel Moolenaar } 461431337658SMarcel Moolenaar } else if (flen) { 461531337658SMarcel Moolenaar if (flen != 2 || strncmp("%d", fmt, flen) != 0) 461631337658SMarcel Moolenaar xo_failure(xop, "invalid width format: '%*.*s'", flen, flen, fmt); 4617d1a0d267SMarcel Moolenaar if (!XOF_ISSET(xop, XOF_NO_VA_ARG)) 461831337658SMarcel Moolenaar width = va_arg(xop->xo_vap, int); 461931337658SMarcel Moolenaar } 462031337658SMarcel Moolenaar 462131337658SMarcel Moolenaar return width; 462231337658SMarcel Moolenaar } 462331337658SMarcel Moolenaar 462431337658SMarcel Moolenaar static void 462531337658SMarcel Moolenaar xo_anchor_clear (xo_handle_t *xop) 462631337658SMarcel Moolenaar { 4627d1a0d267SMarcel Moolenaar XOIF_CLEAR(xop, XOIF_ANCHOR); 462831337658SMarcel Moolenaar xop->xo_anchor_offset = 0; 462931337658SMarcel Moolenaar xop->xo_anchor_columns = 0; 463031337658SMarcel Moolenaar xop->xo_anchor_min_width = 0; 463131337658SMarcel Moolenaar } 463231337658SMarcel Moolenaar 463331337658SMarcel Moolenaar /* 463431337658SMarcel Moolenaar * An anchor is a marker used to delay field width implications. 463531337658SMarcel Moolenaar * Imagine the format string "{[:10}{min:%d}/{cur:%d}/{max:%d}{:]}". 463631337658SMarcel Moolenaar * We are looking for output like " 1/4/5" 463731337658SMarcel Moolenaar * 463831337658SMarcel Moolenaar * To make this work, we record the anchor and then return to 463931337658SMarcel Moolenaar * format it when the end anchor tag is seen. 464031337658SMarcel Moolenaar */ 464131337658SMarcel Moolenaar static void 4642d1a0d267SMarcel Moolenaar xo_anchor_start (xo_handle_t *xop, xo_field_info_t *xfip) 464331337658SMarcel Moolenaar { 4644788ca347SMarcel Moolenaar if (xo_style(xop) != XO_STYLE_TEXT && xo_style(xop) != XO_STYLE_HTML) 464531337658SMarcel Moolenaar return; 464631337658SMarcel Moolenaar 4647d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_ANCHOR)) 464831337658SMarcel Moolenaar xo_failure(xop, "the anchor already recording is discarded"); 464931337658SMarcel Moolenaar 4650d1a0d267SMarcel Moolenaar XOIF_SET(xop, XOIF_ANCHOR); 465131337658SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_data; 465231337658SMarcel Moolenaar xop->xo_anchor_offset = xbp->xb_curp - xbp->xb_bufp; 465331337658SMarcel Moolenaar xop->xo_anchor_columns = 0; 465431337658SMarcel Moolenaar 465531337658SMarcel Moolenaar /* 465631337658SMarcel Moolenaar * Now we find the width, if possible. If it's not there, 465731337658SMarcel Moolenaar * we'll get it on the end anchor. 465831337658SMarcel Moolenaar */ 4659d1a0d267SMarcel Moolenaar xop->xo_anchor_min_width = xo_find_width(xop, xfip); 466031337658SMarcel Moolenaar } 466131337658SMarcel Moolenaar 466231337658SMarcel Moolenaar static void 4663d1a0d267SMarcel Moolenaar xo_anchor_stop (xo_handle_t *xop, xo_field_info_t *xfip) 466431337658SMarcel Moolenaar { 4665788ca347SMarcel Moolenaar if (xo_style(xop) != XO_STYLE_TEXT && xo_style(xop) != XO_STYLE_HTML) 466631337658SMarcel Moolenaar return; 466731337658SMarcel Moolenaar 4668d1a0d267SMarcel Moolenaar if (!XOIF_ISSET(xop, XOIF_ANCHOR)) { 466931337658SMarcel Moolenaar xo_failure(xop, "no start anchor"); 467031337658SMarcel Moolenaar return; 467131337658SMarcel Moolenaar } 467231337658SMarcel Moolenaar 4673d1a0d267SMarcel Moolenaar XOIF_CLEAR(xop, XOIF_UNITS_PENDING); 467431337658SMarcel Moolenaar 4675d1a0d267SMarcel Moolenaar int width = xo_find_width(xop, xfip); 467631337658SMarcel Moolenaar if (width == 0) 467731337658SMarcel Moolenaar width = xop->xo_anchor_min_width; 467831337658SMarcel Moolenaar 467931337658SMarcel Moolenaar if (width == 0) /* No width given; nothing to do */ 468031337658SMarcel Moolenaar goto done; 468131337658SMarcel Moolenaar 468231337658SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_data; 468331337658SMarcel Moolenaar int start = xop->xo_anchor_offset; 468431337658SMarcel Moolenaar int stop = xbp->xb_curp - xbp->xb_bufp; 468531337658SMarcel Moolenaar int abswidth = (width > 0) ? width : -width; 468631337658SMarcel Moolenaar int blen = abswidth - xop->xo_anchor_columns; 468731337658SMarcel Moolenaar 468831337658SMarcel Moolenaar if (blen <= 0) /* Already over width */ 468931337658SMarcel Moolenaar goto done; 469031337658SMarcel Moolenaar 469131337658SMarcel Moolenaar if (abswidth > XO_MAX_ANCHOR_WIDTH) { 469231337658SMarcel Moolenaar xo_failure(xop, "width over %u are not supported", 469331337658SMarcel Moolenaar XO_MAX_ANCHOR_WIDTH); 469431337658SMarcel Moolenaar goto done; 469531337658SMarcel Moolenaar } 469631337658SMarcel Moolenaar 469731337658SMarcel Moolenaar /* Make a suitable padding field and emit it */ 469831337658SMarcel Moolenaar char *buf = alloca(blen); 469931337658SMarcel Moolenaar memset(buf, ' ', blen); 4700d1a0d267SMarcel Moolenaar xo_format_content(xop, "padding", NULL, buf, blen, NULL, 0, 0); 470131337658SMarcel Moolenaar 470231337658SMarcel Moolenaar if (width < 0) /* Already left justified */ 470331337658SMarcel Moolenaar goto done; 470431337658SMarcel Moolenaar 470531337658SMarcel Moolenaar int now = xbp->xb_curp - xbp->xb_bufp; 470631337658SMarcel Moolenaar int delta = now - stop; 4707d1a0d267SMarcel Moolenaar if (delta <= 0) /* Strange; no output to move */ 470831337658SMarcel Moolenaar goto done; 470931337658SMarcel Moolenaar 471031337658SMarcel Moolenaar /* 471131337658SMarcel Moolenaar * Now we're in it alright. We've need to insert the padding data 471231337658SMarcel Moolenaar * we just created (which might be an HTML <div> or text) before 471331337658SMarcel Moolenaar * the formatted data. We make a local copy, move it and then 471431337658SMarcel Moolenaar * insert our copy. We know there's room in the buffer, since 471531337658SMarcel Moolenaar * we're just moving this around. 471631337658SMarcel Moolenaar */ 471731337658SMarcel Moolenaar if (delta > blen) 471831337658SMarcel Moolenaar buf = alloca(delta); /* Expand buffer if needed */ 471931337658SMarcel Moolenaar 472031337658SMarcel Moolenaar memcpy(buf, xbp->xb_bufp + stop, delta); 472131337658SMarcel Moolenaar memmove(xbp->xb_bufp + start + delta, xbp->xb_bufp + start, stop - start); 472231337658SMarcel Moolenaar memmove(xbp->xb_bufp + start, buf, delta); 472331337658SMarcel Moolenaar 472431337658SMarcel Moolenaar done: 472531337658SMarcel Moolenaar xo_anchor_clear(xop); 472631337658SMarcel Moolenaar } 472731337658SMarcel Moolenaar 4728d1a0d267SMarcel Moolenaar static const char * 4729d1a0d267SMarcel Moolenaar xo_class_name (int ftype) 473031337658SMarcel Moolenaar { 4731d1a0d267SMarcel Moolenaar switch (ftype) { 4732d1a0d267SMarcel Moolenaar case 'D': return "decoration"; 4733d1a0d267SMarcel Moolenaar case 'E': return "error"; 4734d1a0d267SMarcel Moolenaar case 'L': return "label"; 4735d1a0d267SMarcel Moolenaar case 'N': return "note"; 4736d1a0d267SMarcel Moolenaar case 'P': return "padding"; 4737d1a0d267SMarcel Moolenaar case 'W': return "warning"; 473831337658SMarcel Moolenaar } 473931337658SMarcel Moolenaar 4740d1a0d267SMarcel Moolenaar return NULL; 474131337658SMarcel Moolenaar } 474231337658SMarcel Moolenaar 4743d1a0d267SMarcel Moolenaar static const char * 4744d1a0d267SMarcel Moolenaar xo_tag_name (int ftype) 4745d1a0d267SMarcel Moolenaar { 4746d1a0d267SMarcel Moolenaar switch (ftype) { 4747d1a0d267SMarcel Moolenaar case 'E': return "__error"; 4748d1a0d267SMarcel Moolenaar case 'W': return "__warning"; 4749d1a0d267SMarcel Moolenaar } 4750d1a0d267SMarcel Moolenaar 4751d1a0d267SMarcel Moolenaar return NULL; 4752d1a0d267SMarcel Moolenaar } 4753d1a0d267SMarcel Moolenaar 4754d1a0d267SMarcel Moolenaar static int 4755d1a0d267SMarcel Moolenaar xo_role_wants_default_format (int ftype) 4756d1a0d267SMarcel Moolenaar { 4757d1a0d267SMarcel Moolenaar switch (ftype) { 4758d1a0d267SMarcel Moolenaar /* These roles can be completely empty and/or without formatting */ 4759d1a0d267SMarcel Moolenaar case 'C': 4760d1a0d267SMarcel Moolenaar case 'G': 4761d1a0d267SMarcel Moolenaar case '[': 4762d1a0d267SMarcel Moolenaar case ']': 4763d1a0d267SMarcel Moolenaar return 0; 4764d1a0d267SMarcel Moolenaar } 4765d1a0d267SMarcel Moolenaar 4766d1a0d267SMarcel Moolenaar return 1; 4767d1a0d267SMarcel Moolenaar } 4768d1a0d267SMarcel Moolenaar 4769d1a0d267SMarcel Moolenaar static xo_mapping_t xo_role_names[] = { 4770d1a0d267SMarcel Moolenaar { 'C', "color" }, 4771d1a0d267SMarcel Moolenaar { 'D', "decoration" }, 4772d1a0d267SMarcel Moolenaar { 'E', "error" }, 4773d1a0d267SMarcel Moolenaar { 'L', "label" }, 4774d1a0d267SMarcel Moolenaar { 'N', "note" }, 4775d1a0d267SMarcel Moolenaar { 'P', "padding" }, 4776d1a0d267SMarcel Moolenaar { 'T', "title" }, 4777d1a0d267SMarcel Moolenaar { 'U', "units" }, 4778d1a0d267SMarcel Moolenaar { 'V', "value" }, 4779d1a0d267SMarcel Moolenaar { 'W', "warning" }, 4780d1a0d267SMarcel Moolenaar { '[', "start-anchor" }, 4781d1a0d267SMarcel Moolenaar { ']', "stop-anchor" }, 4782d1a0d267SMarcel Moolenaar { 0, NULL } 4783d1a0d267SMarcel Moolenaar }; 4784d1a0d267SMarcel Moolenaar 4785d1a0d267SMarcel Moolenaar #define XO_ROLE_EBRACE '{' /* Escaped braces */ 4786d1a0d267SMarcel Moolenaar #define XO_ROLE_TEXT '+' 4787d1a0d267SMarcel Moolenaar #define XO_ROLE_NEWLINE '\n' 4788d1a0d267SMarcel Moolenaar 4789d1a0d267SMarcel Moolenaar static xo_mapping_t xo_modifier_names[] = { 4790d1a0d267SMarcel Moolenaar { XFF_COLON, "colon" }, 4791d1a0d267SMarcel Moolenaar { XFF_COMMA, "comma" }, 4792d1a0d267SMarcel Moolenaar { XFF_DISPLAY_ONLY, "display" }, 4793d1a0d267SMarcel Moolenaar { XFF_ENCODE_ONLY, "encoding" }, 4794d1a0d267SMarcel Moolenaar { XFF_GT_FIELD, "gettext" }, 4795d1a0d267SMarcel Moolenaar { XFF_HUMANIZE, "humanize" }, 4796d1a0d267SMarcel Moolenaar { XFF_HUMANIZE, "hn" }, 4797d1a0d267SMarcel Moolenaar { XFF_HN_SPACE, "hn-space" }, 4798d1a0d267SMarcel Moolenaar { XFF_HN_DECIMAL, "hn-decimal" }, 4799d1a0d267SMarcel Moolenaar { XFF_HN_1000, "hn-1000" }, 4800d1a0d267SMarcel Moolenaar { XFF_KEY, "key" }, 4801d1a0d267SMarcel Moolenaar { XFF_LEAF_LIST, "leaf-list" }, 4802d1a0d267SMarcel Moolenaar { XFF_LEAF_LIST, "list" }, 4803d1a0d267SMarcel Moolenaar { XFF_NOQUOTE, "no-quotes" }, 4804d1a0d267SMarcel Moolenaar { XFF_NOQUOTE, "no-quote" }, 4805d1a0d267SMarcel Moolenaar { XFF_GT_PLURAL, "plural" }, 4806d1a0d267SMarcel Moolenaar { XFF_QUOTE, "quotes" }, 4807d1a0d267SMarcel Moolenaar { XFF_QUOTE, "quote" }, 4808d1a0d267SMarcel Moolenaar { XFF_TRIM_WS, "trim" }, 4809d1a0d267SMarcel Moolenaar { XFF_WS, "white" }, 4810d1a0d267SMarcel Moolenaar { 0, NULL } 4811d1a0d267SMarcel Moolenaar }; 4812d1a0d267SMarcel Moolenaar 4813d1a0d267SMarcel Moolenaar #ifdef NOT_NEEDED_YET 4814d1a0d267SMarcel Moolenaar static xo_mapping_t xo_modifier_short_names[] = { 4815d1a0d267SMarcel Moolenaar { XFF_COLON, "c" }, 4816d1a0d267SMarcel Moolenaar { XFF_DISPLAY_ONLY, "d" }, 4817d1a0d267SMarcel Moolenaar { XFF_ENCODE_ONLY, "e" }, 4818d1a0d267SMarcel Moolenaar { XFF_GT_FIELD, "g" }, 4819d1a0d267SMarcel Moolenaar { XFF_HUMANIZE, "h" }, 4820d1a0d267SMarcel Moolenaar { XFF_KEY, "k" }, 4821d1a0d267SMarcel Moolenaar { XFF_LEAF_LIST, "l" }, 4822d1a0d267SMarcel Moolenaar { XFF_NOQUOTE, "n" }, 4823d1a0d267SMarcel Moolenaar { XFF_GT_PLURAL, "p" }, 4824d1a0d267SMarcel Moolenaar { XFF_QUOTE, "q" }, 4825d1a0d267SMarcel Moolenaar { XFF_TRIM_WS, "t" }, 4826d1a0d267SMarcel Moolenaar { XFF_WS, "w" }, 4827d1a0d267SMarcel Moolenaar { 0, NULL } 4828d1a0d267SMarcel Moolenaar }; 4829d1a0d267SMarcel Moolenaar #endif /* NOT_NEEDED_YET */ 4830d1a0d267SMarcel Moolenaar 4831d1a0d267SMarcel Moolenaar static int 4832d1a0d267SMarcel Moolenaar xo_count_fields (xo_handle_t *xop UNUSED, const char *fmt) 4833d1a0d267SMarcel Moolenaar { 4834d1a0d267SMarcel Moolenaar int rc = 1; 4835d1a0d267SMarcel Moolenaar const char *cp; 4836d1a0d267SMarcel Moolenaar 4837d1a0d267SMarcel Moolenaar for (cp = fmt; *cp; cp++) 4838d1a0d267SMarcel Moolenaar if (*cp == '{' || *cp == '\n') 4839d1a0d267SMarcel Moolenaar rc += 1; 4840d1a0d267SMarcel Moolenaar 4841d1a0d267SMarcel Moolenaar return rc * 2 + 1; 4842d1a0d267SMarcel Moolenaar } 484331337658SMarcel Moolenaar 484431337658SMarcel Moolenaar /* 4845d1a0d267SMarcel Moolenaar * The field format is: 484631337658SMarcel Moolenaar * '{' modifiers ':' content [ '/' print-fmt [ '/' encode-fmt ]] '}' 4847d1a0d267SMarcel Moolenaar * Roles are optional and include the following field types: 484831337658SMarcel Moolenaar * 'D': decoration; something non-text and non-data (colons, commmas) 484931337658SMarcel Moolenaar * 'E': error message 4850d1a0d267SMarcel Moolenaar * 'G': gettext() the entire string; optional domainname as content 485131337658SMarcel Moolenaar * 'L': label; text preceding data 485231337658SMarcel Moolenaar * 'N': note; text following data 485331337658SMarcel Moolenaar * 'P': padding; whitespace 485431337658SMarcel Moolenaar * 'T': Title, where 'content' is a column title 485531337658SMarcel Moolenaar * 'U': Units, where 'content' is the unit label 485631337658SMarcel Moolenaar * 'V': value, where 'content' is the name of the field (the default) 485731337658SMarcel Moolenaar * 'W': warning message 485831337658SMarcel Moolenaar * '[': start a section of anchored text 485931337658SMarcel Moolenaar * ']': end a section of anchored text 4860d1a0d267SMarcel Moolenaar * The following modifiers are also supported: 486131337658SMarcel Moolenaar * 'c': flag: emit a colon after the label 4862d1a0d267SMarcel Moolenaar * 'd': field is only emitted for display styles (text and html) 4863d1a0d267SMarcel Moolenaar * 'e': field is only emitted for encoding styles (xml and json) 4864d1a0d267SMarcel Moolenaar * 'g': gettext() the field 4865d1a0d267SMarcel Moolenaar * 'h': humanize a numeric value (only for display styles) 486631337658SMarcel Moolenaar * 'k': this field is a key, suitable for XPath predicates 486731337658SMarcel Moolenaar * 'l': a leaf-list, a simple list of values 486831337658SMarcel Moolenaar * 'n': no quotes around this field 4869d1a0d267SMarcel Moolenaar * 'p': the field has plural gettext semantics (ngettext) 487031337658SMarcel Moolenaar * 'q': add quotes around this field 487131337658SMarcel Moolenaar * 't': trim whitespace around the value 487231337658SMarcel Moolenaar * 'w': emit a blank after the label 487331337658SMarcel Moolenaar * The print-fmt and encode-fmt strings is the printf-style formating 487431337658SMarcel Moolenaar * for this data. JSON and XML will use the encoding-fmt, if present. 487531337658SMarcel Moolenaar * If the encode-fmt is not provided, it defaults to the print-fmt. 487631337658SMarcel Moolenaar * If the print-fmt is not provided, it defaults to 's'. 487731337658SMarcel Moolenaar */ 4878d1a0d267SMarcel Moolenaar static const char * 4879d1a0d267SMarcel Moolenaar xo_parse_roles (xo_handle_t *xop, const char *fmt, 4880d1a0d267SMarcel Moolenaar const char *basep, xo_field_info_t *xfip) 4881d1a0d267SMarcel Moolenaar { 4882d1a0d267SMarcel Moolenaar const char *sp; 4883d1a0d267SMarcel Moolenaar unsigned ftype = 0; 4884d1a0d267SMarcel Moolenaar xo_xff_flags_t flags = 0; 4885d1a0d267SMarcel Moolenaar uint8_t fnum = 0; 488631337658SMarcel Moolenaar 488731337658SMarcel Moolenaar for (sp = basep; sp; sp++) { 488831337658SMarcel Moolenaar if (*sp == ':' || *sp == '/' || *sp == '}') 488931337658SMarcel Moolenaar break; 489031337658SMarcel Moolenaar 489131337658SMarcel Moolenaar if (*sp == '\\') { 489231337658SMarcel Moolenaar if (sp[1] == '\0') { 489331337658SMarcel Moolenaar xo_failure(xop, "backslash at the end of string"); 4894d1a0d267SMarcel Moolenaar return NULL; 489531337658SMarcel Moolenaar } 4896d1a0d267SMarcel Moolenaar 4897d1a0d267SMarcel Moolenaar /* Anything backslashed is ignored */ 489831337658SMarcel Moolenaar sp += 1; 489931337658SMarcel Moolenaar continue; 490031337658SMarcel Moolenaar } 490131337658SMarcel Moolenaar 4902d1a0d267SMarcel Moolenaar if (*sp == ',') { 4903d1a0d267SMarcel Moolenaar const char *np; 4904d1a0d267SMarcel Moolenaar for (np = ++sp; *np; np++) 4905d1a0d267SMarcel Moolenaar if (*np == ':' || *np == '/' || *np == '}' || *np == ',') 4906d1a0d267SMarcel Moolenaar break; 4907d1a0d267SMarcel Moolenaar 4908d1a0d267SMarcel Moolenaar int slen = np - sp; 4909d1a0d267SMarcel Moolenaar if (slen > 0) { 4910d1a0d267SMarcel Moolenaar xo_xff_flags_t value; 4911d1a0d267SMarcel Moolenaar 4912d1a0d267SMarcel Moolenaar value = xo_name_lookup(xo_role_names, sp, slen); 4913d1a0d267SMarcel Moolenaar if (value) 4914d1a0d267SMarcel Moolenaar ftype = value; 4915d1a0d267SMarcel Moolenaar else { 4916d1a0d267SMarcel Moolenaar value = xo_name_lookup(xo_modifier_names, sp, slen); 4917d1a0d267SMarcel Moolenaar if (value) 4918d1a0d267SMarcel Moolenaar flags |= value; 4919d1a0d267SMarcel Moolenaar else 4920d1a0d267SMarcel Moolenaar xo_failure(xop, "unknown keyword ignored: '%.*s'", 4921d1a0d267SMarcel Moolenaar slen, sp); 4922d1a0d267SMarcel Moolenaar } 4923d1a0d267SMarcel Moolenaar } 4924d1a0d267SMarcel Moolenaar 4925d1a0d267SMarcel Moolenaar sp = np - 1; 4926d1a0d267SMarcel Moolenaar continue; 4927d1a0d267SMarcel Moolenaar } 4928d1a0d267SMarcel Moolenaar 492931337658SMarcel Moolenaar switch (*sp) { 4930788ca347SMarcel Moolenaar case 'C': 493131337658SMarcel Moolenaar case 'D': 493231337658SMarcel Moolenaar case 'E': 4933d1a0d267SMarcel Moolenaar case 'G': 493431337658SMarcel Moolenaar case 'L': 493531337658SMarcel Moolenaar case 'N': 493631337658SMarcel Moolenaar case 'P': 493731337658SMarcel Moolenaar case 'T': 493831337658SMarcel Moolenaar case 'U': 493931337658SMarcel Moolenaar case 'V': 494031337658SMarcel Moolenaar case 'W': 494131337658SMarcel Moolenaar case '[': 494231337658SMarcel Moolenaar case ']': 494331337658SMarcel Moolenaar if (ftype != 0) { 4944d1a0d267SMarcel Moolenaar xo_failure(xop, "field descriptor uses multiple types: '%s'", 4945d1a0d267SMarcel Moolenaar xo_printable(fmt)); 4946d1a0d267SMarcel Moolenaar return NULL; 494731337658SMarcel Moolenaar } 494831337658SMarcel Moolenaar ftype = *sp; 494931337658SMarcel Moolenaar break; 495031337658SMarcel Moolenaar 4951d1a0d267SMarcel Moolenaar case '0': 4952d1a0d267SMarcel Moolenaar case '1': 4953d1a0d267SMarcel Moolenaar case '2': 4954d1a0d267SMarcel Moolenaar case '3': 4955d1a0d267SMarcel Moolenaar case '4': 4956d1a0d267SMarcel Moolenaar case '5': 4957d1a0d267SMarcel Moolenaar case '6': 4958d1a0d267SMarcel Moolenaar case '7': 4959d1a0d267SMarcel Moolenaar case '8': 4960d1a0d267SMarcel Moolenaar case '9': 4961d1a0d267SMarcel Moolenaar fnum = (fnum * 10) + (*sp - '0'); 4962d1a0d267SMarcel Moolenaar break; 4963d1a0d267SMarcel Moolenaar 496431337658SMarcel Moolenaar case 'c': 496531337658SMarcel Moolenaar flags |= XFF_COLON; 496631337658SMarcel Moolenaar break; 496731337658SMarcel Moolenaar 496831337658SMarcel Moolenaar case 'd': 496931337658SMarcel Moolenaar flags |= XFF_DISPLAY_ONLY; 497031337658SMarcel Moolenaar break; 497131337658SMarcel Moolenaar 497231337658SMarcel Moolenaar case 'e': 497331337658SMarcel Moolenaar flags |= XFF_ENCODE_ONLY; 497431337658SMarcel Moolenaar break; 497531337658SMarcel Moolenaar 4976d1a0d267SMarcel Moolenaar case 'g': 4977d1a0d267SMarcel Moolenaar flags |= XFF_GT_FIELD; 4978d1a0d267SMarcel Moolenaar break; 4979d1a0d267SMarcel Moolenaar 4980d1a0d267SMarcel Moolenaar case 'h': 4981d1a0d267SMarcel Moolenaar flags |= XFF_HUMANIZE; 4982d1a0d267SMarcel Moolenaar break; 4983d1a0d267SMarcel Moolenaar 498431337658SMarcel Moolenaar case 'k': 498531337658SMarcel Moolenaar flags |= XFF_KEY; 498631337658SMarcel Moolenaar break; 498731337658SMarcel Moolenaar 498831337658SMarcel Moolenaar case 'l': 498931337658SMarcel Moolenaar flags |= XFF_LEAF_LIST; 499031337658SMarcel Moolenaar break; 499131337658SMarcel Moolenaar 499231337658SMarcel Moolenaar case 'n': 499331337658SMarcel Moolenaar flags |= XFF_NOQUOTE; 499431337658SMarcel Moolenaar break; 499531337658SMarcel Moolenaar 4996d1a0d267SMarcel Moolenaar case 'p': 4997d1a0d267SMarcel Moolenaar flags |= XFF_GT_PLURAL; 4998d1a0d267SMarcel Moolenaar break; 4999d1a0d267SMarcel Moolenaar 500031337658SMarcel Moolenaar case 'q': 500131337658SMarcel Moolenaar flags |= XFF_QUOTE; 500231337658SMarcel Moolenaar break; 500331337658SMarcel Moolenaar 500431337658SMarcel Moolenaar case 't': 500531337658SMarcel Moolenaar flags |= XFF_TRIM_WS; 500631337658SMarcel Moolenaar break; 500731337658SMarcel Moolenaar 500831337658SMarcel Moolenaar case 'w': 500931337658SMarcel Moolenaar flags |= XFF_WS; 501031337658SMarcel Moolenaar break; 501131337658SMarcel Moolenaar 501231337658SMarcel Moolenaar default: 5013d1a0d267SMarcel Moolenaar xo_failure(xop, "field descriptor uses unknown modifier: '%s'", 5014d1a0d267SMarcel Moolenaar xo_printable(fmt)); 501531337658SMarcel Moolenaar /* 501631337658SMarcel Moolenaar * No good answer here; a bad format will likely 501731337658SMarcel Moolenaar * mean a core file. We just return and hope 501831337658SMarcel Moolenaar * the caller notices there's no output, and while 5019d1a0d267SMarcel Moolenaar * that seems, well, bad, there's nothing better. 502031337658SMarcel Moolenaar */ 5021d1a0d267SMarcel Moolenaar return NULL; 5022d1a0d267SMarcel Moolenaar } 5023d1a0d267SMarcel Moolenaar 5024d1a0d267SMarcel Moolenaar if (ftype == 'N' || ftype == 'U') { 5025d1a0d267SMarcel Moolenaar if (flags & XFF_COLON) { 5026d1a0d267SMarcel Moolenaar xo_failure(xop, "colon modifier on 'N' or 'U' field ignored: " 5027d1a0d267SMarcel Moolenaar "'%s'", xo_printable(fmt)); 5028d1a0d267SMarcel Moolenaar flags &= ~XFF_COLON; 5029d1a0d267SMarcel Moolenaar } 503031337658SMarcel Moolenaar } 503131337658SMarcel Moolenaar } 503231337658SMarcel Moolenaar 5033d1a0d267SMarcel Moolenaar xfip->xfi_flags = flags; 5034d1a0d267SMarcel Moolenaar xfip->xfi_ftype = ftype ?: 'V'; 5035d1a0d267SMarcel Moolenaar xfip->xfi_fnum = fnum; 5036d1a0d267SMarcel Moolenaar 5037d1a0d267SMarcel Moolenaar return sp; 5038d1a0d267SMarcel Moolenaar } 5039d1a0d267SMarcel Moolenaar 5040d1a0d267SMarcel Moolenaar /* 5041d1a0d267SMarcel Moolenaar * Number any remaining fields that need numbers. Note that some 5042d1a0d267SMarcel Moolenaar * field types (text, newline, escaped braces) never get numbers. 5043d1a0d267SMarcel Moolenaar */ 5044d1a0d267SMarcel Moolenaar static void 5045d1a0d267SMarcel Moolenaar xo_gettext_finish_numbering_fields (xo_handle_t *xop UNUSED, 5046d1a0d267SMarcel Moolenaar const char *fmt UNUSED, 5047d1a0d267SMarcel Moolenaar xo_field_info_t *fields) 5048d1a0d267SMarcel Moolenaar { 5049d1a0d267SMarcel Moolenaar xo_field_info_t *xfip; 5050d1a0d267SMarcel Moolenaar unsigned fnum, max_fields; 5051d1a0d267SMarcel Moolenaar uint64_t bits = 0; 5052d1a0d267SMarcel Moolenaar 5053d1a0d267SMarcel Moolenaar /* First make a list of add the explicitly used bits */ 5054d1a0d267SMarcel Moolenaar for (xfip = fields, fnum = 0; xfip->xfi_ftype; xfip++) { 5055d1a0d267SMarcel Moolenaar switch (xfip->xfi_ftype) { 5056d1a0d267SMarcel Moolenaar case XO_ROLE_NEWLINE: /* Don't get numbered */ 5057d1a0d267SMarcel Moolenaar case XO_ROLE_TEXT: 5058d1a0d267SMarcel Moolenaar case XO_ROLE_EBRACE: 5059d1a0d267SMarcel Moolenaar case 'G': 5060d1a0d267SMarcel Moolenaar continue; 5061d1a0d267SMarcel Moolenaar } 5062d1a0d267SMarcel Moolenaar 5063d1a0d267SMarcel Moolenaar fnum += 1; 5064d1a0d267SMarcel Moolenaar if (fnum >= 63) 5065d1a0d267SMarcel Moolenaar break; 5066d1a0d267SMarcel Moolenaar 5067d1a0d267SMarcel Moolenaar if (xfip->xfi_fnum) 5068d1a0d267SMarcel Moolenaar bits |= 1 << xfip->xfi_fnum; 5069d1a0d267SMarcel Moolenaar } 5070d1a0d267SMarcel Moolenaar 5071d1a0d267SMarcel Moolenaar max_fields = fnum; 5072d1a0d267SMarcel Moolenaar 5073d1a0d267SMarcel Moolenaar for (xfip = fields, fnum = 0; xfip->xfi_ftype; xfip++) { 5074d1a0d267SMarcel Moolenaar switch (xfip->xfi_ftype) { 5075d1a0d267SMarcel Moolenaar case XO_ROLE_NEWLINE: /* Don't get numbered */ 5076d1a0d267SMarcel Moolenaar case XO_ROLE_TEXT: 5077d1a0d267SMarcel Moolenaar case XO_ROLE_EBRACE: 5078d1a0d267SMarcel Moolenaar case 'G': 5079d1a0d267SMarcel Moolenaar continue; 5080d1a0d267SMarcel Moolenaar } 5081d1a0d267SMarcel Moolenaar 5082d1a0d267SMarcel Moolenaar if (xfip->xfi_fnum != 0) 5083d1a0d267SMarcel Moolenaar continue; 5084d1a0d267SMarcel Moolenaar 5085d1a0d267SMarcel Moolenaar /* Find the next unassigned field */ 5086d1a0d267SMarcel Moolenaar for (fnum++; bits & (1 << fnum); fnum++) 5087d1a0d267SMarcel Moolenaar continue; 5088d1a0d267SMarcel Moolenaar 5089d1a0d267SMarcel Moolenaar if (fnum > max_fields) 5090d1a0d267SMarcel Moolenaar break; 5091d1a0d267SMarcel Moolenaar 5092d1a0d267SMarcel Moolenaar xfip->xfi_fnum = fnum; /* Mark the field number */ 5093d1a0d267SMarcel Moolenaar bits |= 1 << fnum; /* Mark it used */ 5094d1a0d267SMarcel Moolenaar } 5095d1a0d267SMarcel Moolenaar } 5096d1a0d267SMarcel Moolenaar 5097d1a0d267SMarcel Moolenaar /* 5098d1a0d267SMarcel Moolenaar * The format string uses field numbers, so we need to whiffle thru it 5099d1a0d267SMarcel Moolenaar * and make sure everything's sane and lovely. 5100d1a0d267SMarcel Moolenaar */ 5101d1a0d267SMarcel Moolenaar static int 5102d1a0d267SMarcel Moolenaar xo_parse_field_numbers (xo_handle_t *xop, const char *fmt, 5103d1a0d267SMarcel Moolenaar xo_field_info_t *fields, unsigned num_fields) 5104d1a0d267SMarcel Moolenaar { 5105d1a0d267SMarcel Moolenaar xo_field_info_t *xfip; 5106d1a0d267SMarcel Moolenaar unsigned field, fnum; 5107d1a0d267SMarcel Moolenaar uint64_t bits = 0; 5108d1a0d267SMarcel Moolenaar 5109d1a0d267SMarcel Moolenaar for (xfip = fields, field = 0; field < num_fields; xfip++, field++) { 5110d1a0d267SMarcel Moolenaar /* Fields default to 1:1 with natural position */ 5111d1a0d267SMarcel Moolenaar if (xfip->xfi_fnum == 0) 5112d1a0d267SMarcel Moolenaar xfip->xfi_fnum = field + 1; 5113d1a0d267SMarcel Moolenaar else if (xfip->xfi_fnum > num_fields) { 5114d1a0d267SMarcel Moolenaar xo_failure(xop, "field number exceeds number of fields: '%s'", fmt); 5115d1a0d267SMarcel Moolenaar return -1; 5116d1a0d267SMarcel Moolenaar } 5117d1a0d267SMarcel Moolenaar 5118d1a0d267SMarcel Moolenaar fnum = xfip->xfi_fnum - 1; /* Move to zero origin */ 5119d1a0d267SMarcel Moolenaar if (fnum < 64) { /* Only test what fits */ 5120d1a0d267SMarcel Moolenaar if (bits & (1 << fnum)) { 5121d1a0d267SMarcel Moolenaar xo_failure(xop, "field number %u reused: '%s'", 5122d1a0d267SMarcel Moolenaar xfip->xfi_fnum, fmt); 5123d1a0d267SMarcel Moolenaar return -1; 5124d1a0d267SMarcel Moolenaar } 5125d1a0d267SMarcel Moolenaar bits |= 1 << fnum; 5126d1a0d267SMarcel Moolenaar } 5127d1a0d267SMarcel Moolenaar } 5128d1a0d267SMarcel Moolenaar 5129d1a0d267SMarcel Moolenaar return 0; 5130d1a0d267SMarcel Moolenaar } 5131d1a0d267SMarcel Moolenaar 5132d1a0d267SMarcel Moolenaar static int 5133d1a0d267SMarcel Moolenaar xo_parse_fields (xo_handle_t *xop, xo_field_info_t *fields, 5134d1a0d267SMarcel Moolenaar unsigned num_fields, const char *fmt) 5135d1a0d267SMarcel Moolenaar { 5136d1a0d267SMarcel Moolenaar static const char default_format[] = "%s"; 5137d1a0d267SMarcel Moolenaar const char *cp, *sp, *ep, *basep; 5138d1a0d267SMarcel Moolenaar unsigned field = 0; 5139d1a0d267SMarcel Moolenaar xo_field_info_t *xfip = fields; 5140d1a0d267SMarcel Moolenaar unsigned seen_fnum = 0; 5141d1a0d267SMarcel Moolenaar 5142d1a0d267SMarcel Moolenaar for (cp = fmt; *cp && field < num_fields; field++, xfip++) { 5143d1a0d267SMarcel Moolenaar xfip->xfi_start = cp; 5144d1a0d267SMarcel Moolenaar 5145d1a0d267SMarcel Moolenaar if (*cp == '\n') { 5146d1a0d267SMarcel Moolenaar xfip->xfi_ftype = XO_ROLE_NEWLINE; 5147d1a0d267SMarcel Moolenaar xfip->xfi_len = 1; 5148d1a0d267SMarcel Moolenaar cp += 1; 5149d1a0d267SMarcel Moolenaar continue; 5150d1a0d267SMarcel Moolenaar } 5151d1a0d267SMarcel Moolenaar 5152d1a0d267SMarcel Moolenaar if (*cp != '{') { 5153d1a0d267SMarcel Moolenaar /* Normal text */ 5154d1a0d267SMarcel Moolenaar for (sp = cp; *sp; sp++) { 5155d1a0d267SMarcel Moolenaar if (*sp == '{' || *sp == '\n') 5156d1a0d267SMarcel Moolenaar break; 5157d1a0d267SMarcel Moolenaar } 5158d1a0d267SMarcel Moolenaar 5159d1a0d267SMarcel Moolenaar xfip->xfi_ftype = XO_ROLE_TEXT; 5160d1a0d267SMarcel Moolenaar xfip->xfi_content = cp; 5161d1a0d267SMarcel Moolenaar xfip->xfi_clen = sp - cp; 5162d1a0d267SMarcel Moolenaar xfip->xfi_next = sp; 5163d1a0d267SMarcel Moolenaar 5164d1a0d267SMarcel Moolenaar cp = sp; 5165d1a0d267SMarcel Moolenaar continue; 5166d1a0d267SMarcel Moolenaar } 5167d1a0d267SMarcel Moolenaar 5168d1a0d267SMarcel Moolenaar if (cp[1] == '{') { /* Start of {{escaped braces}} */ 5169d1a0d267SMarcel Moolenaar xfip->xfi_start = cp + 1; /* Start at second brace */ 5170d1a0d267SMarcel Moolenaar xfip->xfi_ftype = XO_ROLE_EBRACE; 5171d1a0d267SMarcel Moolenaar 5172d1a0d267SMarcel Moolenaar cp += 2; /* Skip over _both_ characters */ 5173d1a0d267SMarcel Moolenaar for (sp = cp; *sp; sp++) { 5174d1a0d267SMarcel Moolenaar if (*sp == '}' && sp[1] == '}') 5175d1a0d267SMarcel Moolenaar break; 5176d1a0d267SMarcel Moolenaar } 5177d1a0d267SMarcel Moolenaar if (*sp == '\0') { 5178d1a0d267SMarcel Moolenaar xo_failure(xop, "missing closing '}}': '%s'", 5179d1a0d267SMarcel Moolenaar xo_printable(fmt)); 5180d1a0d267SMarcel Moolenaar return -1; 5181d1a0d267SMarcel Moolenaar } 5182d1a0d267SMarcel Moolenaar 5183d1a0d267SMarcel Moolenaar xfip->xfi_len = sp - xfip->xfi_start + 1; 5184d1a0d267SMarcel Moolenaar 5185d1a0d267SMarcel Moolenaar /* Move along the string, but don't run off the end */ 5186d1a0d267SMarcel Moolenaar if (*sp == '}' && sp[1] == '}') 5187d1a0d267SMarcel Moolenaar sp += 2; 5188d1a0d267SMarcel Moolenaar cp = *sp ? sp : sp; 5189d1a0d267SMarcel Moolenaar xfip->xfi_next = cp; 5190d1a0d267SMarcel Moolenaar continue; 5191d1a0d267SMarcel Moolenaar } 5192d1a0d267SMarcel Moolenaar 5193d1a0d267SMarcel Moolenaar /* We are looking at the start of a field definition */ 5194d1a0d267SMarcel Moolenaar xfip->xfi_start = basep = cp + 1; 5195d1a0d267SMarcel Moolenaar 5196d1a0d267SMarcel Moolenaar const char *format = NULL; 5197d1a0d267SMarcel Moolenaar int flen = 0; 5198d1a0d267SMarcel Moolenaar 5199d1a0d267SMarcel Moolenaar /* Looking at roles and modifiers */ 5200d1a0d267SMarcel Moolenaar sp = xo_parse_roles(xop, fmt, basep, xfip); 5201d1a0d267SMarcel Moolenaar if (sp == NULL) { 5202d1a0d267SMarcel Moolenaar /* xo_failure has already been called */ 5203d1a0d267SMarcel Moolenaar return -1; 5204d1a0d267SMarcel Moolenaar } 5205d1a0d267SMarcel Moolenaar 5206d1a0d267SMarcel Moolenaar if (xfip->xfi_fnum) 5207d1a0d267SMarcel Moolenaar seen_fnum = 1; 5208d1a0d267SMarcel Moolenaar 5209d1a0d267SMarcel Moolenaar /* Looking at content */ 521031337658SMarcel Moolenaar if (*sp == ':') { 521131337658SMarcel Moolenaar for (ep = ++sp; *sp; sp++) { 521231337658SMarcel Moolenaar if (*sp == '}' || *sp == '/') 521331337658SMarcel Moolenaar break; 521431337658SMarcel Moolenaar if (*sp == '\\') { 521531337658SMarcel Moolenaar if (sp[1] == '\0') { 521631337658SMarcel Moolenaar xo_failure(xop, "backslash at the end of string"); 521731337658SMarcel Moolenaar return -1; 521831337658SMarcel Moolenaar } 521931337658SMarcel Moolenaar sp += 1; 522031337658SMarcel Moolenaar continue; 522131337658SMarcel Moolenaar } 522231337658SMarcel Moolenaar } 522331337658SMarcel Moolenaar if (ep != sp) { 5224d1a0d267SMarcel Moolenaar xfip->xfi_clen = sp - ep; 5225d1a0d267SMarcel Moolenaar xfip->xfi_content = ep; 522631337658SMarcel Moolenaar } 522731337658SMarcel Moolenaar } else { 5228d1a0d267SMarcel Moolenaar xo_failure(xop, "missing content (':'): '%s'", xo_printable(fmt)); 522931337658SMarcel Moolenaar return -1; 523031337658SMarcel Moolenaar } 523131337658SMarcel Moolenaar 5232d1a0d267SMarcel Moolenaar /* Looking at main (display) format */ 523331337658SMarcel Moolenaar if (*sp == '/') { 523431337658SMarcel Moolenaar for (ep = ++sp; *sp; sp++) { 523531337658SMarcel Moolenaar if (*sp == '}' || *sp == '/') 523631337658SMarcel Moolenaar break; 523731337658SMarcel Moolenaar if (*sp == '\\') { 523831337658SMarcel Moolenaar if (sp[1] == '\0') { 523931337658SMarcel Moolenaar xo_failure(xop, "backslash at the end of string"); 524031337658SMarcel Moolenaar return -1; 524131337658SMarcel Moolenaar } 524231337658SMarcel Moolenaar sp += 1; 524331337658SMarcel Moolenaar continue; 524431337658SMarcel Moolenaar } 524531337658SMarcel Moolenaar } 524631337658SMarcel Moolenaar flen = sp - ep; 524731337658SMarcel Moolenaar format = ep; 524831337658SMarcel Moolenaar } 524931337658SMarcel Moolenaar 5250d1a0d267SMarcel Moolenaar /* Looking at encoding format */ 525131337658SMarcel Moolenaar if (*sp == '/') { 525231337658SMarcel Moolenaar for (ep = ++sp; *sp; sp++) { 525331337658SMarcel Moolenaar if (*sp == '}') 525431337658SMarcel Moolenaar break; 525531337658SMarcel Moolenaar } 5256d1a0d267SMarcel Moolenaar 5257d1a0d267SMarcel Moolenaar xfip->xfi_encoding = ep; 5258d1a0d267SMarcel Moolenaar xfip->xfi_elen = sp - ep; 525931337658SMarcel Moolenaar } 526031337658SMarcel Moolenaar 5261d1a0d267SMarcel Moolenaar if (*sp != '}') { 5262d1a0d267SMarcel Moolenaar xo_failure(xop, "missing closing '}': %s", xo_printable(fmt)); 526331337658SMarcel Moolenaar return -1; 526431337658SMarcel Moolenaar } 526531337658SMarcel Moolenaar 5266d1a0d267SMarcel Moolenaar xfip->xfi_len = sp - xfip->xfi_start; 5267d1a0d267SMarcel Moolenaar xfip->xfi_next = ++sp; 5268d1a0d267SMarcel Moolenaar 5269d1a0d267SMarcel Moolenaar /* If we have content, then we have a default format */ 5270d1a0d267SMarcel Moolenaar if (xfip->xfi_clen || format) { 5271d1a0d267SMarcel Moolenaar if (format) { 5272d1a0d267SMarcel Moolenaar xfip->xfi_format = format; 5273d1a0d267SMarcel Moolenaar xfip->xfi_flen = flen; 5274d1a0d267SMarcel Moolenaar } else if (xo_role_wants_default_format(xfip->xfi_ftype)) { 5275d1a0d267SMarcel Moolenaar xfip->xfi_format = default_format; 5276d1a0d267SMarcel Moolenaar xfip->xfi_flen = 2; 5277d1a0d267SMarcel Moolenaar } 527831337658SMarcel Moolenaar } 527931337658SMarcel Moolenaar 5280d1a0d267SMarcel Moolenaar cp = sp; 5281d1a0d267SMarcel Moolenaar } 5282545ddfbeSMarcel Moolenaar 5283d1a0d267SMarcel Moolenaar int rc = 0; 5284d1a0d267SMarcel Moolenaar 5285d1a0d267SMarcel Moolenaar /* 5286d1a0d267SMarcel Moolenaar * If we saw a field number on at least one field, then we need 5287d1a0d267SMarcel Moolenaar * to enforce some rules and/or guidelines. 5288d1a0d267SMarcel Moolenaar */ 5289d1a0d267SMarcel Moolenaar if (seen_fnum) 5290d1a0d267SMarcel Moolenaar rc = xo_parse_field_numbers(xop, fmt, fields, field); 5291d1a0d267SMarcel Moolenaar 5292d1a0d267SMarcel Moolenaar return rc; 5293d1a0d267SMarcel Moolenaar } 5294d1a0d267SMarcel Moolenaar 5295d1a0d267SMarcel Moolenaar /* 5296d1a0d267SMarcel Moolenaar * We are passed a pointer to a format string just past the "{G:}" 5297d1a0d267SMarcel Moolenaar * field. We build a simplified version of the format string. 5298d1a0d267SMarcel Moolenaar */ 5299d1a0d267SMarcel Moolenaar static int 5300d1a0d267SMarcel Moolenaar xo_gettext_simplify_format (xo_handle_t *xop UNUSED, 5301d1a0d267SMarcel Moolenaar xo_buffer_t *xbp, 5302d1a0d267SMarcel Moolenaar xo_field_info_t *fields, 5303d1a0d267SMarcel Moolenaar int this_field, 5304d1a0d267SMarcel Moolenaar const char *fmt UNUSED, 5305d1a0d267SMarcel Moolenaar xo_simplify_field_func_t field_cb) 5306d1a0d267SMarcel Moolenaar { 5307d1a0d267SMarcel Moolenaar unsigned ftype; 5308d1a0d267SMarcel Moolenaar xo_xff_flags_t flags; 5309d1a0d267SMarcel Moolenaar int field = this_field + 1; 5310d1a0d267SMarcel Moolenaar xo_field_info_t *xfip; 5311d1a0d267SMarcel Moolenaar char ch; 5312d1a0d267SMarcel Moolenaar 5313d1a0d267SMarcel Moolenaar for (xfip = &fields[field]; xfip->xfi_ftype; xfip++, field++) { 5314d1a0d267SMarcel Moolenaar ftype = xfip->xfi_ftype; 5315d1a0d267SMarcel Moolenaar flags = xfip->xfi_flags; 5316d1a0d267SMarcel Moolenaar 5317d1a0d267SMarcel Moolenaar if ((flags & XFF_GT_FIELD) && xfip->xfi_content && ftype != 'V') { 5318d1a0d267SMarcel Moolenaar if (field_cb) 5319d1a0d267SMarcel Moolenaar field_cb(xfip->xfi_content, xfip->xfi_clen, 5320d1a0d267SMarcel Moolenaar (flags & XFF_GT_PLURAL) ? 1 : 0); 5321d1a0d267SMarcel Moolenaar } 5322d1a0d267SMarcel Moolenaar 5323d1a0d267SMarcel Moolenaar switch (ftype) { 5324d1a0d267SMarcel Moolenaar case 'G': 5325d1a0d267SMarcel Moolenaar /* Ignore gettext roles */ 5326d1a0d267SMarcel Moolenaar break; 5327d1a0d267SMarcel Moolenaar 5328d1a0d267SMarcel Moolenaar case XO_ROLE_NEWLINE: 5329d1a0d267SMarcel Moolenaar xo_buf_append(xbp, "\n", 1); 5330d1a0d267SMarcel Moolenaar break; 5331d1a0d267SMarcel Moolenaar 5332d1a0d267SMarcel Moolenaar case XO_ROLE_EBRACE: 5333d1a0d267SMarcel Moolenaar xo_buf_append(xbp, "{", 1); 5334d1a0d267SMarcel Moolenaar xo_buf_append(xbp, xfip->xfi_content, xfip->xfi_clen); 5335d1a0d267SMarcel Moolenaar xo_buf_append(xbp, "}", 1); 5336d1a0d267SMarcel Moolenaar break; 5337d1a0d267SMarcel Moolenaar 5338d1a0d267SMarcel Moolenaar case XO_ROLE_TEXT: 5339d1a0d267SMarcel Moolenaar xo_buf_append(xbp, xfip->xfi_content, xfip->xfi_clen); 5340d1a0d267SMarcel Moolenaar break; 5341d1a0d267SMarcel Moolenaar 5342d1a0d267SMarcel Moolenaar default: 5343d1a0d267SMarcel Moolenaar xo_buf_append(xbp, "{", 1); 5344d1a0d267SMarcel Moolenaar if (ftype != 'V') { 5345d1a0d267SMarcel Moolenaar ch = ftype; 5346d1a0d267SMarcel Moolenaar xo_buf_append(xbp, &ch, 1); 5347d1a0d267SMarcel Moolenaar } 5348d1a0d267SMarcel Moolenaar 5349d1a0d267SMarcel Moolenaar unsigned fnum = xfip->xfi_fnum ?: 0; 5350d1a0d267SMarcel Moolenaar if (fnum) { 5351d1a0d267SMarcel Moolenaar char num[12]; 5352d1a0d267SMarcel Moolenaar /* Field numbers are origin 1, not 0, following printf(3) */ 5353d1a0d267SMarcel Moolenaar snprintf(num, sizeof(num), "%u", fnum); 5354d1a0d267SMarcel Moolenaar xo_buf_append(xbp, num, strlen(num)); 5355d1a0d267SMarcel Moolenaar } 5356d1a0d267SMarcel Moolenaar 5357d1a0d267SMarcel Moolenaar xo_buf_append(xbp, ":", 1); 5358d1a0d267SMarcel Moolenaar xo_buf_append(xbp, xfip->xfi_content, xfip->xfi_clen); 5359d1a0d267SMarcel Moolenaar xo_buf_append(xbp, "}", 1); 5360d1a0d267SMarcel Moolenaar } 5361d1a0d267SMarcel Moolenaar } 5362d1a0d267SMarcel Moolenaar 5363d1a0d267SMarcel Moolenaar xo_buf_append(xbp, "", 1); 5364d1a0d267SMarcel Moolenaar return 0; 5365d1a0d267SMarcel Moolenaar } 5366d1a0d267SMarcel Moolenaar 5367d1a0d267SMarcel Moolenaar void 5368d1a0d267SMarcel Moolenaar xo_dump_fields (xo_field_info_t *); /* Fake prototype for debug function */ 5369d1a0d267SMarcel Moolenaar void 5370d1a0d267SMarcel Moolenaar xo_dump_fields (xo_field_info_t *fields) 5371d1a0d267SMarcel Moolenaar { 5372d1a0d267SMarcel Moolenaar xo_field_info_t *xfip; 5373d1a0d267SMarcel Moolenaar 5374d1a0d267SMarcel Moolenaar for (xfip = fields; xfip->xfi_ftype; xfip++) { 5375d1a0d267SMarcel Moolenaar printf("%lu(%u): %lx [%c/%u] [%.*s] [%.*s] [%.*s]\n", 5376d1a0d267SMarcel Moolenaar (unsigned long) (xfip - fields), xfip->xfi_fnum, 5377d1a0d267SMarcel Moolenaar (unsigned long) xfip->xfi_flags, 5378d1a0d267SMarcel Moolenaar isprint((int) xfip->xfi_ftype) ? xfip->xfi_ftype : ' ', 5379d1a0d267SMarcel Moolenaar xfip->xfi_ftype, 5380d1a0d267SMarcel Moolenaar xfip->xfi_clen, xfip->xfi_content ?: "", 5381d1a0d267SMarcel Moolenaar xfip->xfi_flen, xfip->xfi_format ?: "", 5382d1a0d267SMarcel Moolenaar xfip->xfi_elen, xfip->xfi_encoding ?: ""); 5383d1a0d267SMarcel Moolenaar } 5384d1a0d267SMarcel Moolenaar } 5385d1a0d267SMarcel Moolenaar 5386d1a0d267SMarcel Moolenaar #ifdef HAVE_GETTEXT 5387d1a0d267SMarcel Moolenaar /* 5388d1a0d267SMarcel Moolenaar * Find the field that matches the given field number 5389d1a0d267SMarcel Moolenaar */ 5390d1a0d267SMarcel Moolenaar static xo_field_info_t * 5391d1a0d267SMarcel Moolenaar xo_gettext_find_field (xo_field_info_t *fields, unsigned fnum) 5392d1a0d267SMarcel Moolenaar { 5393d1a0d267SMarcel Moolenaar xo_field_info_t *xfip; 5394d1a0d267SMarcel Moolenaar 5395d1a0d267SMarcel Moolenaar for (xfip = fields; xfip->xfi_ftype; xfip++) 5396d1a0d267SMarcel Moolenaar if (xfip->xfi_fnum == fnum) 5397d1a0d267SMarcel Moolenaar return xfip; 5398d1a0d267SMarcel Moolenaar 5399d1a0d267SMarcel Moolenaar return NULL; 5400d1a0d267SMarcel Moolenaar } 5401d1a0d267SMarcel Moolenaar 5402d1a0d267SMarcel Moolenaar /* 5403d1a0d267SMarcel Moolenaar * At this point, we need to consider if the fields have been reordered, 5404d1a0d267SMarcel Moolenaar * such as "The {:adjective} {:noun}" to "La {:noun} {:adjective}". 5405d1a0d267SMarcel Moolenaar * 5406d1a0d267SMarcel Moolenaar * We need to rewrite the new_fields using the old fields order, 5407d1a0d267SMarcel Moolenaar * so that we can render the message using the arguments as they 5408d1a0d267SMarcel Moolenaar * appear on the stack. It's a lot of work, but we don't really 5409d1a0d267SMarcel Moolenaar * want to (eventually) fall into the standard printf code which 5410d1a0d267SMarcel Moolenaar * means using the arguments straight (and in order) from the 5411d1a0d267SMarcel Moolenaar * varargs we were originally passed. 5412d1a0d267SMarcel Moolenaar */ 5413d1a0d267SMarcel Moolenaar static void 5414d1a0d267SMarcel Moolenaar xo_gettext_rewrite_fields (xo_handle_t *xop UNUSED, 5415d1a0d267SMarcel Moolenaar xo_field_info_t *fields, unsigned max_fields) 5416d1a0d267SMarcel Moolenaar { 5417d1a0d267SMarcel Moolenaar xo_field_info_t tmp[max_fields]; 5418d1a0d267SMarcel Moolenaar bzero(tmp, max_fields * sizeof(tmp[0])); 5419d1a0d267SMarcel Moolenaar 5420d1a0d267SMarcel Moolenaar unsigned fnum = 0; 5421d1a0d267SMarcel Moolenaar xo_field_info_t *newp, *outp, *zp; 5422d1a0d267SMarcel Moolenaar for (newp = fields, outp = tmp; newp->xfi_ftype; newp++, outp++) { 5423d1a0d267SMarcel Moolenaar switch (newp->xfi_ftype) { 5424d1a0d267SMarcel Moolenaar case XO_ROLE_NEWLINE: /* Don't get numbered */ 5425d1a0d267SMarcel Moolenaar case XO_ROLE_TEXT: 5426d1a0d267SMarcel Moolenaar case XO_ROLE_EBRACE: 5427d1a0d267SMarcel Moolenaar case 'G': 5428d1a0d267SMarcel Moolenaar *outp = *newp; 5429d1a0d267SMarcel Moolenaar outp->xfi_renum = 0; 5430d1a0d267SMarcel Moolenaar continue; 5431d1a0d267SMarcel Moolenaar } 5432d1a0d267SMarcel Moolenaar 5433d1a0d267SMarcel Moolenaar zp = xo_gettext_find_field(fields, ++fnum); 5434d1a0d267SMarcel Moolenaar if (zp == NULL) { /* Should not occur */ 5435d1a0d267SMarcel Moolenaar *outp = *newp; 5436d1a0d267SMarcel Moolenaar outp->xfi_renum = 0; 5437d1a0d267SMarcel Moolenaar continue; 5438d1a0d267SMarcel Moolenaar } 5439d1a0d267SMarcel Moolenaar 5440d1a0d267SMarcel Moolenaar *outp = *zp; 5441d1a0d267SMarcel Moolenaar outp->xfi_renum = newp->xfi_fnum; 5442d1a0d267SMarcel Moolenaar } 5443d1a0d267SMarcel Moolenaar 5444d1a0d267SMarcel Moolenaar memcpy(fields, tmp, max_fields * sizeof(tmp[0])); 5445d1a0d267SMarcel Moolenaar } 5446d1a0d267SMarcel Moolenaar 5447d1a0d267SMarcel Moolenaar /* 5448d1a0d267SMarcel Moolenaar * We've got two lists of fields, the old list from the original 5449d1a0d267SMarcel Moolenaar * format string and the new one from the parsed gettext reply. The 5450d1a0d267SMarcel Moolenaar * new list has the localized words, where the old list has the 5451d1a0d267SMarcel Moolenaar * formatting information. We need to combine them into a single list 5452d1a0d267SMarcel Moolenaar * (the new list). 5453d1a0d267SMarcel Moolenaar * 5454d1a0d267SMarcel Moolenaar * If the list needs to be reordered, then we've got more serious work 5455d1a0d267SMarcel Moolenaar * to do. 5456d1a0d267SMarcel Moolenaar */ 5457d1a0d267SMarcel Moolenaar static int 5458d1a0d267SMarcel Moolenaar xo_gettext_combine_formats (xo_handle_t *xop, const char *fmt UNUSED, 5459d1a0d267SMarcel Moolenaar const char *gtfmt, xo_field_info_t *old_fields, 5460d1a0d267SMarcel Moolenaar xo_field_info_t *new_fields, unsigned new_max_fields, 5461d1a0d267SMarcel Moolenaar int *reorderedp) 5462d1a0d267SMarcel Moolenaar { 5463d1a0d267SMarcel Moolenaar int reordered = 0; 5464d1a0d267SMarcel Moolenaar xo_field_info_t *newp, *oldp, *startp = old_fields; 5465d1a0d267SMarcel Moolenaar 5466d1a0d267SMarcel Moolenaar xo_gettext_finish_numbering_fields(xop, fmt, old_fields); 5467d1a0d267SMarcel Moolenaar 5468d1a0d267SMarcel Moolenaar for (newp = new_fields; newp->xfi_ftype; newp++) { 5469d1a0d267SMarcel Moolenaar switch (newp->xfi_ftype) { 5470d1a0d267SMarcel Moolenaar case XO_ROLE_NEWLINE: 5471d1a0d267SMarcel Moolenaar case XO_ROLE_TEXT: 5472d1a0d267SMarcel Moolenaar case XO_ROLE_EBRACE: 5473d1a0d267SMarcel Moolenaar continue; 5474d1a0d267SMarcel Moolenaar 5475d1a0d267SMarcel Moolenaar case 'V': 5476d1a0d267SMarcel Moolenaar for (oldp = startp; oldp->xfi_ftype; oldp++) { 5477d1a0d267SMarcel Moolenaar if (oldp->xfi_ftype != 'V') 5478d1a0d267SMarcel Moolenaar continue; 5479d1a0d267SMarcel Moolenaar if (newp->xfi_clen != oldp->xfi_clen 5480d1a0d267SMarcel Moolenaar || strncmp(newp->xfi_content, oldp->xfi_content, 5481d1a0d267SMarcel Moolenaar oldp->xfi_clen) != 0) { 5482d1a0d267SMarcel Moolenaar reordered = 1; 5483d1a0d267SMarcel Moolenaar continue; 5484d1a0d267SMarcel Moolenaar } 5485d1a0d267SMarcel Moolenaar startp = oldp + 1; 5486d1a0d267SMarcel Moolenaar break; 5487d1a0d267SMarcel Moolenaar } 5488d1a0d267SMarcel Moolenaar 5489d1a0d267SMarcel Moolenaar /* Didn't find it on the first pass (starting from start) */ 5490d1a0d267SMarcel Moolenaar if (oldp->xfi_ftype == 0) { 5491d1a0d267SMarcel Moolenaar for (oldp = old_fields; oldp < startp; oldp++) { 5492d1a0d267SMarcel Moolenaar if (oldp->xfi_ftype != 'V') 5493d1a0d267SMarcel Moolenaar continue; 5494d1a0d267SMarcel Moolenaar if (newp->xfi_clen != oldp->xfi_clen) 5495d1a0d267SMarcel Moolenaar continue; 5496d1a0d267SMarcel Moolenaar if (strncmp(newp->xfi_content, oldp->xfi_content, 5497d1a0d267SMarcel Moolenaar oldp->xfi_clen) != 0) 5498d1a0d267SMarcel Moolenaar continue; 5499d1a0d267SMarcel Moolenaar reordered = 1; 5500d1a0d267SMarcel Moolenaar break; 5501d1a0d267SMarcel Moolenaar } 5502d1a0d267SMarcel Moolenaar if (oldp == startp) { 5503d1a0d267SMarcel Moolenaar /* Field not found */ 5504d1a0d267SMarcel Moolenaar xo_failure(xop, "post-gettext format can't find field " 5505d1a0d267SMarcel Moolenaar "'%.*s' in format '%s'", 5506d1a0d267SMarcel Moolenaar newp->xfi_clen, newp->xfi_content, 5507d1a0d267SMarcel Moolenaar xo_printable(gtfmt)); 5508d1a0d267SMarcel Moolenaar return -1; 5509d1a0d267SMarcel Moolenaar } 5510d1a0d267SMarcel Moolenaar } 5511d1a0d267SMarcel Moolenaar break; 5512d1a0d267SMarcel Moolenaar 5513d1a0d267SMarcel Moolenaar default: 5514d1a0d267SMarcel Moolenaar /* 5515d1a0d267SMarcel Moolenaar * Other fields don't have names for us to use, so if 5516d1a0d267SMarcel Moolenaar * the types aren't the same, then we'll have to assume 5517d1a0d267SMarcel Moolenaar * the original field is a match. 5518d1a0d267SMarcel Moolenaar */ 5519d1a0d267SMarcel Moolenaar for (oldp = startp; oldp->xfi_ftype; oldp++) { 5520d1a0d267SMarcel Moolenaar if (oldp->xfi_ftype == 'V') /* Can't go past these */ 5521d1a0d267SMarcel Moolenaar break; 5522d1a0d267SMarcel Moolenaar if (oldp->xfi_ftype == newp->xfi_ftype) 5523d1a0d267SMarcel Moolenaar goto copy_it; /* Assumably we have a match */ 5524d1a0d267SMarcel Moolenaar } 5525d1a0d267SMarcel Moolenaar continue; 5526d1a0d267SMarcel Moolenaar } 5527d1a0d267SMarcel Moolenaar 5528d1a0d267SMarcel Moolenaar /* 5529d1a0d267SMarcel Moolenaar * Found a match; copy over appropriate fields 5530d1a0d267SMarcel Moolenaar */ 5531d1a0d267SMarcel Moolenaar copy_it: 5532d1a0d267SMarcel Moolenaar newp->xfi_flags = oldp->xfi_flags; 5533d1a0d267SMarcel Moolenaar newp->xfi_fnum = oldp->xfi_fnum; 5534d1a0d267SMarcel Moolenaar newp->xfi_format = oldp->xfi_format; 5535d1a0d267SMarcel Moolenaar newp->xfi_flen = oldp->xfi_flen; 5536d1a0d267SMarcel Moolenaar newp->xfi_encoding = oldp->xfi_encoding; 5537d1a0d267SMarcel Moolenaar newp->xfi_elen = oldp->xfi_elen; 5538d1a0d267SMarcel Moolenaar } 5539d1a0d267SMarcel Moolenaar 5540d1a0d267SMarcel Moolenaar *reorderedp = reordered; 5541d1a0d267SMarcel Moolenaar if (reordered) { 5542d1a0d267SMarcel Moolenaar xo_gettext_finish_numbering_fields(xop, fmt, new_fields); 5543d1a0d267SMarcel Moolenaar xo_gettext_rewrite_fields(xop, new_fields, new_max_fields); 5544d1a0d267SMarcel Moolenaar } 5545d1a0d267SMarcel Moolenaar 5546d1a0d267SMarcel Moolenaar return 0; 5547d1a0d267SMarcel Moolenaar } 5548d1a0d267SMarcel Moolenaar 5549d1a0d267SMarcel Moolenaar /* 5550d1a0d267SMarcel Moolenaar * We don't want to make gettext() calls here with a complete format 5551d1a0d267SMarcel Moolenaar * string, since that means changing a flag would mean a 5552d1a0d267SMarcel Moolenaar * labor-intensive re-translation expense. Instead we build a 5553d1a0d267SMarcel Moolenaar * simplified form with a reduced level of detail, perform a lookup on 5554d1a0d267SMarcel Moolenaar * that string and then re-insert the formating info. 5555d1a0d267SMarcel Moolenaar * 5556d1a0d267SMarcel Moolenaar * So something like: 5557d1a0d267SMarcel Moolenaar * xo_emit("{G:}close {:fd/%ld} returned {g:error/%m} {:test/%6.6s}\n", ...) 5558d1a0d267SMarcel Moolenaar * would have a lookup string of: 5559d1a0d267SMarcel Moolenaar * "close {:fd} returned {:error} {:test}\n" 5560d1a0d267SMarcel Moolenaar * 5561d1a0d267SMarcel Moolenaar * We also need to handling reordering of fields, where the gettext() 5562d1a0d267SMarcel Moolenaar * reply string uses fields in a different order than the original 5563d1a0d267SMarcel Moolenaar * format string: 5564d1a0d267SMarcel Moolenaar * "cluse-a {:fd} retoorned {:test}. Bork {:error} Bork. Bork.\n" 5565d1a0d267SMarcel Moolenaar * If we have to reorder fields within the message, then things get 5566d1a0d267SMarcel Moolenaar * complicated. See xo_gettext_rewrite_fields. 5567d1a0d267SMarcel Moolenaar * 5568d1a0d267SMarcel Moolenaar * Summary: i18n aighn't cheap. 5569d1a0d267SMarcel Moolenaar */ 5570d1a0d267SMarcel Moolenaar static const char * 5571d1a0d267SMarcel Moolenaar xo_gettext_build_format (xo_handle_t *xop UNUSED, 5572d1a0d267SMarcel Moolenaar xo_field_info_t *fields UNUSED, 5573d1a0d267SMarcel Moolenaar int this_field UNUSED, 5574d1a0d267SMarcel Moolenaar const char *fmt, char **new_fmtp) 5575d1a0d267SMarcel Moolenaar { 5576d1a0d267SMarcel Moolenaar if (xo_style_is_encoding(xop)) 5577d1a0d267SMarcel Moolenaar goto bail; 5578d1a0d267SMarcel Moolenaar 5579d1a0d267SMarcel Moolenaar xo_buffer_t xb; 5580d1a0d267SMarcel Moolenaar xo_buf_init(&xb); 5581d1a0d267SMarcel Moolenaar 5582d1a0d267SMarcel Moolenaar if (xo_gettext_simplify_format(xop, &xb, fields, 5583d1a0d267SMarcel Moolenaar this_field, fmt, NULL)) 5584d1a0d267SMarcel Moolenaar goto bail2; 5585d1a0d267SMarcel Moolenaar 5586d1a0d267SMarcel Moolenaar const char *gtfmt = xo_dgettext(xop, xb.xb_bufp); 5587d1a0d267SMarcel Moolenaar if (gtfmt == NULL || gtfmt == fmt || strcmp(gtfmt, fmt) == 0) 5588d1a0d267SMarcel Moolenaar goto bail2; 5589d1a0d267SMarcel Moolenaar 5590d1a0d267SMarcel Moolenaar xo_buf_cleanup(&xb); 5591d1a0d267SMarcel Moolenaar 5592d1a0d267SMarcel Moolenaar char *new_fmt = xo_strndup(gtfmt, -1); 5593d1a0d267SMarcel Moolenaar if (new_fmt == NULL) 5594d1a0d267SMarcel Moolenaar goto bail2; 5595d1a0d267SMarcel Moolenaar 5596d1a0d267SMarcel Moolenaar *new_fmtp = new_fmt; 5597d1a0d267SMarcel Moolenaar return new_fmt; 5598d1a0d267SMarcel Moolenaar 5599d1a0d267SMarcel Moolenaar bail2: 5600d1a0d267SMarcel Moolenaar xo_buf_cleanup(&xb); 5601d1a0d267SMarcel Moolenaar bail: 5602d1a0d267SMarcel Moolenaar *new_fmtp = NULL; 5603d1a0d267SMarcel Moolenaar return fmt; 5604d1a0d267SMarcel Moolenaar } 5605d1a0d267SMarcel Moolenaar 5606d1a0d267SMarcel Moolenaar static void 5607d1a0d267SMarcel Moolenaar xo_gettext_rebuild_content (xo_handle_t *xop, xo_field_info_t *fields, 5608d1a0d267SMarcel Moolenaar unsigned *fstart, unsigned min_fstart, 5609d1a0d267SMarcel Moolenaar unsigned *fend, unsigned max_fend) 5610d1a0d267SMarcel Moolenaar { 5611d1a0d267SMarcel Moolenaar xo_field_info_t *xfip; 5612d1a0d267SMarcel Moolenaar char *buf; 5613d1a0d267SMarcel Moolenaar unsigned base = fstart[min_fstart]; 5614d1a0d267SMarcel Moolenaar unsigned blen = fend[max_fend] - base; 5615d1a0d267SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_data; 5616d1a0d267SMarcel Moolenaar 5617d1a0d267SMarcel Moolenaar if (blen == 0) 5618d1a0d267SMarcel Moolenaar return; 5619d1a0d267SMarcel Moolenaar 5620d1a0d267SMarcel Moolenaar buf = xo_realloc(NULL, blen); 5621d1a0d267SMarcel Moolenaar if (buf == NULL) 5622d1a0d267SMarcel Moolenaar return; 5623d1a0d267SMarcel Moolenaar 5624d1a0d267SMarcel Moolenaar memcpy(buf, xbp->xb_bufp + fstart[min_fstart], blen); /* Copy our data */ 5625d1a0d267SMarcel Moolenaar 5626d1a0d267SMarcel Moolenaar unsigned field = min_fstart, soff, doff = base, len, fnum; 5627d1a0d267SMarcel Moolenaar xo_field_info_t *zp; 5628d1a0d267SMarcel Moolenaar 5629d1a0d267SMarcel Moolenaar /* 5630d1a0d267SMarcel Moolenaar * Be aware there are two competing views of "field number": we 5631d1a0d267SMarcel Moolenaar * want the user to thing in terms of "The {1:size}" where {G:}, 5632d1a0d267SMarcel Moolenaar * newlines, escaped braces, and text don't have numbers. But is 5633d1a0d267SMarcel Moolenaar * also the internal view, where we have an array of 5634d1a0d267SMarcel Moolenaar * xo_field_info_t and every field have an index. fnum, fstart[] 5635d1a0d267SMarcel Moolenaar * and fend[] are the latter, but xfi_renum is the former. 5636d1a0d267SMarcel Moolenaar */ 5637d1a0d267SMarcel Moolenaar for (xfip = fields + field; xfip->xfi_ftype; xfip++, field++) { 5638d1a0d267SMarcel Moolenaar fnum = field; 5639d1a0d267SMarcel Moolenaar if (xfip->xfi_renum) { 5640d1a0d267SMarcel Moolenaar zp = xo_gettext_find_field(fields, xfip->xfi_renum); 5641d1a0d267SMarcel Moolenaar fnum = zp ? zp - fields : field; 5642d1a0d267SMarcel Moolenaar } 5643d1a0d267SMarcel Moolenaar 5644d1a0d267SMarcel Moolenaar soff = fstart[fnum]; 5645d1a0d267SMarcel Moolenaar len = fend[fnum] - soff; 5646d1a0d267SMarcel Moolenaar 5647d1a0d267SMarcel Moolenaar if (len > 0) { 5648d1a0d267SMarcel Moolenaar soff -= base; 5649d1a0d267SMarcel Moolenaar memcpy(xbp->xb_bufp + doff, buf + soff, len); 5650d1a0d267SMarcel Moolenaar doff += len; 5651d1a0d267SMarcel Moolenaar } 5652d1a0d267SMarcel Moolenaar } 5653d1a0d267SMarcel Moolenaar 5654d1a0d267SMarcel Moolenaar xo_free(buf); 5655d1a0d267SMarcel Moolenaar } 5656d1a0d267SMarcel Moolenaar #else /* HAVE_GETTEXT */ 5657d1a0d267SMarcel Moolenaar static const char * 5658d1a0d267SMarcel Moolenaar xo_gettext_build_format (xo_handle_t *xop UNUSED, 5659d1a0d267SMarcel Moolenaar xo_field_info_t *fields UNUSED, 5660d1a0d267SMarcel Moolenaar int this_field UNUSED, 5661d1a0d267SMarcel Moolenaar const char *fmt UNUSED, char **new_fmtp) 5662d1a0d267SMarcel Moolenaar { 5663d1a0d267SMarcel Moolenaar *new_fmtp = NULL; 5664d1a0d267SMarcel Moolenaar return fmt; 5665d1a0d267SMarcel Moolenaar } 5666d1a0d267SMarcel Moolenaar 5667d1a0d267SMarcel Moolenaar static int 5668d1a0d267SMarcel Moolenaar xo_gettext_combine_formats (xo_handle_t *xop UNUSED, const char *fmt UNUSED, 5669d1a0d267SMarcel Moolenaar const char *gtfmt UNUSED, 5670d1a0d267SMarcel Moolenaar xo_field_info_t *old_fields UNUSED, 5671d1a0d267SMarcel Moolenaar xo_field_info_t *new_fields UNUSED, 5672d1a0d267SMarcel Moolenaar unsigned new_max_fields UNUSED, 5673d1a0d267SMarcel Moolenaar int *reorderedp UNUSED) 5674d1a0d267SMarcel Moolenaar { 5675d1a0d267SMarcel Moolenaar return -1; 5676d1a0d267SMarcel Moolenaar } 5677d1a0d267SMarcel Moolenaar 5678d1a0d267SMarcel Moolenaar static void 5679d1a0d267SMarcel Moolenaar xo_gettext_rebuild_content (xo_handle_t *xop UNUSED, 5680d1a0d267SMarcel Moolenaar xo_field_info_t *fields UNUSED, 5681d1a0d267SMarcel Moolenaar unsigned *fstart UNUSED, unsigned min_fstart UNUSED, 5682d1a0d267SMarcel Moolenaar unsigned *fend UNUSED, unsigned max_fend UNUSED) 5683d1a0d267SMarcel Moolenaar { 5684d1a0d267SMarcel Moolenaar return; 5685d1a0d267SMarcel Moolenaar } 5686d1a0d267SMarcel Moolenaar #endif /* HAVE_GETTEXT */ 5687d1a0d267SMarcel Moolenaar 5688d1a0d267SMarcel Moolenaar /* 5689d1a0d267SMarcel Moolenaar * The central function for emitting libxo output. 5690d1a0d267SMarcel Moolenaar */ 5691d1a0d267SMarcel Moolenaar static int 5692d1a0d267SMarcel Moolenaar xo_do_emit (xo_handle_t *xop, const char *fmt) 5693d1a0d267SMarcel Moolenaar { 5694d1a0d267SMarcel Moolenaar int gettext_inuse = 0; 5695d1a0d267SMarcel Moolenaar int gettext_changed = 0; 5696d1a0d267SMarcel Moolenaar int gettext_reordered = 0; 5697d1a0d267SMarcel Moolenaar xo_field_info_t *new_fields = NULL; 5698d1a0d267SMarcel Moolenaar 5699d1a0d267SMarcel Moolenaar int rc = 0; 5700d1a0d267SMarcel Moolenaar int flush = XOF_ISSET(xop, XOF_FLUSH); 5701d1a0d267SMarcel Moolenaar int flush_line = XOF_ISSET(xop, XOF_FLUSH_LINE); 5702d1a0d267SMarcel Moolenaar char *new_fmt = NULL; 5703d1a0d267SMarcel Moolenaar 5704d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_REORDER) || xo_style(xop) == XO_STYLE_ENCODER) 5705d1a0d267SMarcel Moolenaar flush_line = 0; 5706d1a0d267SMarcel Moolenaar 5707d1a0d267SMarcel Moolenaar xop->xo_columns = 0; /* Always reset it */ 5708d1a0d267SMarcel Moolenaar xop->xo_errno = errno; /* Save for "%m" */ 5709d1a0d267SMarcel Moolenaar 5710d1a0d267SMarcel Moolenaar unsigned max_fields = xo_count_fields(xop, fmt), field; 5711d1a0d267SMarcel Moolenaar xo_field_info_t fields[max_fields], *xfip; 5712d1a0d267SMarcel Moolenaar 5713d1a0d267SMarcel Moolenaar bzero(fields, max_fields * sizeof(fields[0])); 5714d1a0d267SMarcel Moolenaar 5715d1a0d267SMarcel Moolenaar if (xo_parse_fields(xop, fields, max_fields, fmt)) 5716d1a0d267SMarcel Moolenaar return -1; /* Warning already displayed */ 5717d1a0d267SMarcel Moolenaar 5718d1a0d267SMarcel Moolenaar unsigned ftype; 5719d1a0d267SMarcel Moolenaar xo_xff_flags_t flags; 5720d1a0d267SMarcel Moolenaar 5721d1a0d267SMarcel Moolenaar /* 5722d1a0d267SMarcel Moolenaar * Some overhead for gettext; if the fields in the msgstr returned 5723d1a0d267SMarcel Moolenaar * by gettext are reordered, then we need to record start and end 5724d1a0d267SMarcel Moolenaar * for each field. We'll go ahead and render the fields in the 5725d1a0d267SMarcel Moolenaar * normal order, but later we can then reconstruct the reordered 5726d1a0d267SMarcel Moolenaar * fields using these fstart/fend values. 5727d1a0d267SMarcel Moolenaar */ 5728d1a0d267SMarcel Moolenaar unsigned flimit = max_fields * 2; /* Pessimistic limit */ 5729d1a0d267SMarcel Moolenaar unsigned min_fstart = flimit - 1; 5730d1a0d267SMarcel Moolenaar unsigned max_fend = 0; /* Highest recorded fend[] entry */ 5731d1a0d267SMarcel Moolenaar unsigned fstart[flimit]; 5732d1a0d267SMarcel Moolenaar bzero(fstart, flimit * sizeof(fstart[0])); 5733d1a0d267SMarcel Moolenaar unsigned fend[flimit]; 5734d1a0d267SMarcel Moolenaar bzero(fend, flimit * sizeof(fend[0])); 5735d1a0d267SMarcel Moolenaar 5736d1a0d267SMarcel Moolenaar for (xfip = fields, field = 0; xfip->xfi_ftype && field < max_fields; 5737d1a0d267SMarcel Moolenaar xfip++, field++) { 5738d1a0d267SMarcel Moolenaar ftype = xfip->xfi_ftype; 5739d1a0d267SMarcel Moolenaar flags = xfip->xfi_flags; 5740d1a0d267SMarcel Moolenaar 5741d1a0d267SMarcel Moolenaar /* Record field start offset */ 5742d1a0d267SMarcel Moolenaar if (gettext_reordered) { 5743d1a0d267SMarcel Moolenaar fstart[field] = xo_buf_offset(&xop->xo_data); 5744d1a0d267SMarcel Moolenaar if (min_fstart > field) 5745d1a0d267SMarcel Moolenaar min_fstart = field; 5746d1a0d267SMarcel Moolenaar } 5747d1a0d267SMarcel Moolenaar 5748d1a0d267SMarcel Moolenaar if (ftype == XO_ROLE_NEWLINE) { 5749d1a0d267SMarcel Moolenaar xo_line_close(xop); 5750d1a0d267SMarcel Moolenaar if (flush_line && xo_flush_h(xop) < 0) 5751d1a0d267SMarcel Moolenaar return -1; 5752d1a0d267SMarcel Moolenaar goto bottom; 5753d1a0d267SMarcel Moolenaar 5754d1a0d267SMarcel Moolenaar } else if (ftype == XO_ROLE_EBRACE) { 5755d1a0d267SMarcel Moolenaar xo_format_text(xop, xfip->xfi_start, xfip->xfi_len); 5756d1a0d267SMarcel Moolenaar goto bottom; 5757d1a0d267SMarcel Moolenaar 5758d1a0d267SMarcel Moolenaar } else if (ftype == XO_ROLE_TEXT) { 5759d1a0d267SMarcel Moolenaar /* Normal text */ 5760d1a0d267SMarcel Moolenaar xo_format_text(xop, xfip->xfi_content, xfip->xfi_clen); 5761d1a0d267SMarcel Moolenaar goto bottom; 5762d1a0d267SMarcel Moolenaar } 5763d1a0d267SMarcel Moolenaar 5764d1a0d267SMarcel Moolenaar /* 5765d1a0d267SMarcel Moolenaar * Notes and units need the 'w' flag handled before the content. 5766d1a0d267SMarcel Moolenaar */ 5767d1a0d267SMarcel Moolenaar if (ftype == 'N' || ftype == 'U') { 5768d1a0d267SMarcel Moolenaar if (flags & XFF_WS) { 5769d1a0d267SMarcel Moolenaar xo_format_content(xop, "padding", NULL, " ", 1, 5770d1a0d267SMarcel Moolenaar NULL, 0, flags); 5771d1a0d267SMarcel Moolenaar flags &= ~XFF_WS; /* Block later handling of this */ 5772d1a0d267SMarcel Moolenaar } 5773d1a0d267SMarcel Moolenaar } 5774d1a0d267SMarcel Moolenaar 5775d1a0d267SMarcel Moolenaar if (ftype == 'V') 5776d1a0d267SMarcel Moolenaar xo_format_value(xop, xfip->xfi_content, xfip->xfi_clen, 5777d1a0d267SMarcel Moolenaar xfip->xfi_format, xfip->xfi_flen, 5778d1a0d267SMarcel Moolenaar xfip->xfi_encoding, xfip->xfi_elen, flags); 5779d1a0d267SMarcel Moolenaar else if (ftype == '[') 5780d1a0d267SMarcel Moolenaar xo_anchor_start(xop, xfip); 5781545ddfbeSMarcel Moolenaar else if (ftype == ']') 5782d1a0d267SMarcel Moolenaar xo_anchor_stop(xop, xfip); 5783788ca347SMarcel Moolenaar else if (ftype == 'C') 5784d1a0d267SMarcel Moolenaar xo_format_colors(xop, xfip); 5785545ddfbeSMarcel Moolenaar 5786d1a0d267SMarcel Moolenaar else if (ftype == 'G') { 5787d1a0d267SMarcel Moolenaar /* 5788d1a0d267SMarcel Moolenaar * A {G:domain} field; disect the domain name and translate 5789d1a0d267SMarcel Moolenaar * the remaining portion of the input string. If the user 5790d1a0d267SMarcel Moolenaar * didn't put the {G:} at the start of the format string, then 5791d1a0d267SMarcel Moolenaar * assumably they just want us to translate the rest of it. 5792d1a0d267SMarcel Moolenaar * Since gettext returns strings in a static buffer, we make 5793d1a0d267SMarcel Moolenaar * a copy in new_fmt. 5794d1a0d267SMarcel Moolenaar */ 5795d1a0d267SMarcel Moolenaar xo_set_gettext_domain(xop, xfip); 5796d1a0d267SMarcel Moolenaar 5797d1a0d267SMarcel Moolenaar if (!gettext_inuse) { /* Only translate once */ 5798d1a0d267SMarcel Moolenaar gettext_inuse = 1; 5799d1a0d267SMarcel Moolenaar if (new_fmt) { 5800d1a0d267SMarcel Moolenaar xo_free(new_fmt); 5801d1a0d267SMarcel Moolenaar new_fmt = NULL; 5802545ddfbeSMarcel Moolenaar } 5803545ddfbeSMarcel Moolenaar 5804d1a0d267SMarcel Moolenaar xo_gettext_build_format(xop, fields, field, 5805d1a0d267SMarcel Moolenaar xfip->xfi_next, &new_fmt); 5806d1a0d267SMarcel Moolenaar if (new_fmt) { 5807d1a0d267SMarcel Moolenaar gettext_changed = 1; 5808d1a0d267SMarcel Moolenaar 5809d1a0d267SMarcel Moolenaar unsigned new_max_fields = xo_count_fields(xop, new_fmt); 5810d1a0d267SMarcel Moolenaar 5811d1a0d267SMarcel Moolenaar if (++new_max_fields < max_fields) 5812d1a0d267SMarcel Moolenaar new_max_fields = max_fields; 5813d1a0d267SMarcel Moolenaar 5814d1a0d267SMarcel Moolenaar /* Leave a blank slot at the beginning */ 5815d1a0d267SMarcel Moolenaar int sz = (new_max_fields + 1) * sizeof(xo_field_info_t); 5816d1a0d267SMarcel Moolenaar new_fields = alloca(sz); 5817d1a0d267SMarcel Moolenaar bzero(new_fields, sz); 5818d1a0d267SMarcel Moolenaar 5819d1a0d267SMarcel Moolenaar if (!xo_parse_fields(xop, new_fields + 1, 5820d1a0d267SMarcel Moolenaar new_max_fields, new_fmt)) { 5821d1a0d267SMarcel Moolenaar gettext_reordered = 0; 5822d1a0d267SMarcel Moolenaar 5823d1a0d267SMarcel Moolenaar if (!xo_gettext_combine_formats(xop, fmt, new_fmt, 5824d1a0d267SMarcel Moolenaar fields, new_fields + 1, 5825d1a0d267SMarcel Moolenaar new_max_fields, &gettext_reordered)) { 5826d1a0d267SMarcel Moolenaar 5827d1a0d267SMarcel Moolenaar if (gettext_reordered) { 5828d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_LOG_GETTEXT)) 5829d1a0d267SMarcel Moolenaar xo_failure(xop, "gettext finds reordered " 5830d1a0d267SMarcel Moolenaar "fields in '%s' and '%s'", 5831d1a0d267SMarcel Moolenaar xo_printable(fmt), 5832d1a0d267SMarcel Moolenaar xo_printable(new_fmt)); 5833d1a0d267SMarcel Moolenaar flush_line = 0; /* Must keep at content */ 5834d1a0d267SMarcel Moolenaar XOIF_SET(xop, XOIF_REORDER); 5835d1a0d267SMarcel Moolenaar } 5836d1a0d267SMarcel Moolenaar 5837d1a0d267SMarcel Moolenaar field = -1; /* Will be incremented at top of loop */ 5838d1a0d267SMarcel Moolenaar xfip = new_fields; 5839d1a0d267SMarcel Moolenaar max_fields = new_max_fields; 5840d1a0d267SMarcel Moolenaar } 5841d1a0d267SMarcel Moolenaar } 5842d1a0d267SMarcel Moolenaar } 5843d1a0d267SMarcel Moolenaar } 5844d1a0d267SMarcel Moolenaar continue; 5845d1a0d267SMarcel Moolenaar 5846d1a0d267SMarcel Moolenaar } else if (xfip->xfi_clen || xfip->xfi_format) { 5847d1a0d267SMarcel Moolenaar 5848d1a0d267SMarcel Moolenaar const char *class_name = xo_class_name(ftype); 5849d1a0d267SMarcel Moolenaar if (class_name) 5850d1a0d267SMarcel Moolenaar xo_format_content(xop, class_name, xo_tag_name(ftype), 5851d1a0d267SMarcel Moolenaar xfip->xfi_content, xfip->xfi_clen, 5852d1a0d267SMarcel Moolenaar xfip->xfi_format, xfip->xfi_flen, flags); 585331337658SMarcel Moolenaar else if (ftype == 'T') 5854d1a0d267SMarcel Moolenaar xo_format_title(xop, xfip); 5855d1a0d267SMarcel Moolenaar else if (ftype == 'U') 5856d1a0d267SMarcel Moolenaar xo_format_units(xop, xfip); 5857d1a0d267SMarcel Moolenaar else 5858d1a0d267SMarcel Moolenaar xo_failure(xop, "unknown field type: '%c'", ftype); 5859545ddfbeSMarcel Moolenaar } 586031337658SMarcel Moolenaar 586131337658SMarcel Moolenaar if (flags & XFF_COLON) 5862d1a0d267SMarcel Moolenaar xo_format_content(xop, "decoration", NULL, ":", 1, NULL, 0, 0); 586331337658SMarcel Moolenaar 5864d1a0d267SMarcel Moolenaar if (flags & XFF_WS) 5865d1a0d267SMarcel Moolenaar xo_format_content(xop, "padding", NULL, " ", 1, NULL, 0, 0); 5866d1a0d267SMarcel Moolenaar 5867d1a0d267SMarcel Moolenaar bottom: 5868d1a0d267SMarcel Moolenaar /* Record the end-of-field offset */ 5869d1a0d267SMarcel Moolenaar if (gettext_reordered) { 5870d1a0d267SMarcel Moolenaar fend[field] = xo_buf_offset(&xop->xo_data); 5871d1a0d267SMarcel Moolenaar max_fend = field; 587231337658SMarcel Moolenaar } 587331337658SMarcel Moolenaar } 587431337658SMarcel Moolenaar 5875d1a0d267SMarcel Moolenaar if (gettext_changed && gettext_reordered) { 5876d1a0d267SMarcel Moolenaar /* Final step: rebuild the content using the rendered fields */ 5877d1a0d267SMarcel Moolenaar xo_gettext_rebuild_content(xop, new_fields + 1, fstart, min_fstart, 5878d1a0d267SMarcel Moolenaar fend, max_fend); 5879d1a0d267SMarcel Moolenaar } 5880d1a0d267SMarcel Moolenaar 5881d1a0d267SMarcel Moolenaar XOIF_CLEAR(xop, XOIF_REORDER); 5882d1a0d267SMarcel Moolenaar 588331337658SMarcel Moolenaar /* If we don't have an anchor, write the text out */ 5884d1a0d267SMarcel Moolenaar if (flush && !XOIF_ISSET(xop, XOIF_ANCHOR)) { 5885545ddfbeSMarcel Moolenaar if (xo_write(xop) < 0) 5886545ddfbeSMarcel Moolenaar rc = -1; /* Report failure */ 5887545ddfbeSMarcel Moolenaar else if (xop->xo_flush && xop->xo_flush(xop->xo_opaque) < 0) 5888545ddfbeSMarcel Moolenaar rc = -1; 5889545ddfbeSMarcel Moolenaar } 589031337658SMarcel Moolenaar 5891d1a0d267SMarcel Moolenaar if (new_fmt) 5892d1a0d267SMarcel Moolenaar xo_free(new_fmt); 5893d1a0d267SMarcel Moolenaar 5894d1a0d267SMarcel Moolenaar /* 5895d1a0d267SMarcel Moolenaar * We've carried the gettext domainname inside our handle just for 5896d1a0d267SMarcel Moolenaar * convenience, but we need to ensure it doesn't survive across 5897d1a0d267SMarcel Moolenaar * xo_emit calls. 5898d1a0d267SMarcel Moolenaar */ 5899d1a0d267SMarcel Moolenaar if (xop->xo_gt_domain) { 5900d1a0d267SMarcel Moolenaar xo_free(xop->xo_gt_domain); 5901d1a0d267SMarcel Moolenaar xop->xo_gt_domain = NULL; 5902d1a0d267SMarcel Moolenaar } 5903d1a0d267SMarcel Moolenaar 590431337658SMarcel Moolenaar return (rc < 0) ? rc : (int) xop->xo_columns; 590531337658SMarcel Moolenaar } 590631337658SMarcel Moolenaar 5907d1a0d267SMarcel Moolenaar /* 5908d1a0d267SMarcel Moolenaar * Rebuild a format string in a gettext-friendly format. This function 5909d1a0d267SMarcel Moolenaar * is exposed to tools can perform this function. See xo(1). 5910d1a0d267SMarcel Moolenaar */ 5911d1a0d267SMarcel Moolenaar char * 5912d1a0d267SMarcel Moolenaar xo_simplify_format (xo_handle_t *xop, const char *fmt, int with_numbers, 5913d1a0d267SMarcel Moolenaar xo_simplify_field_func_t field_cb) 5914d1a0d267SMarcel Moolenaar { 5915d1a0d267SMarcel Moolenaar xop = xo_default(xop); 5916d1a0d267SMarcel Moolenaar 5917d1a0d267SMarcel Moolenaar xop->xo_columns = 0; /* Always reset it */ 5918d1a0d267SMarcel Moolenaar xop->xo_errno = errno; /* Save for "%m" */ 5919d1a0d267SMarcel Moolenaar 5920d1a0d267SMarcel Moolenaar unsigned max_fields = xo_count_fields(xop, fmt); 5921d1a0d267SMarcel Moolenaar xo_field_info_t fields[max_fields]; 5922d1a0d267SMarcel Moolenaar 5923d1a0d267SMarcel Moolenaar bzero(fields, max_fields * sizeof(fields[0])); 5924d1a0d267SMarcel Moolenaar 5925d1a0d267SMarcel Moolenaar if (xo_parse_fields(xop, fields, max_fields, fmt)) 5926d1a0d267SMarcel Moolenaar return NULL; /* Warning already displayed */ 5927d1a0d267SMarcel Moolenaar 5928d1a0d267SMarcel Moolenaar xo_buffer_t xb; 5929d1a0d267SMarcel Moolenaar xo_buf_init(&xb); 5930d1a0d267SMarcel Moolenaar 5931d1a0d267SMarcel Moolenaar if (with_numbers) 5932d1a0d267SMarcel Moolenaar xo_gettext_finish_numbering_fields(xop, fmt, fields); 5933d1a0d267SMarcel Moolenaar 5934d1a0d267SMarcel Moolenaar if (xo_gettext_simplify_format(xop, &xb, fields, -1, fmt, field_cb)) 5935d1a0d267SMarcel Moolenaar return NULL; 5936d1a0d267SMarcel Moolenaar 5937d1a0d267SMarcel Moolenaar return xb.xb_bufp; 5938d1a0d267SMarcel Moolenaar } 5939d1a0d267SMarcel Moolenaar 594031337658SMarcel Moolenaar int 594131337658SMarcel Moolenaar xo_emit_hv (xo_handle_t *xop, const char *fmt, va_list vap) 594231337658SMarcel Moolenaar { 594331337658SMarcel Moolenaar int rc; 594431337658SMarcel Moolenaar 594531337658SMarcel Moolenaar xop = xo_default(xop); 594631337658SMarcel Moolenaar va_copy(xop->xo_vap, vap); 594731337658SMarcel Moolenaar rc = xo_do_emit(xop, fmt); 594831337658SMarcel Moolenaar va_end(xop->xo_vap); 594931337658SMarcel Moolenaar bzero(&xop->xo_vap, sizeof(xop->xo_vap)); 595031337658SMarcel Moolenaar 595131337658SMarcel Moolenaar return rc; 595231337658SMarcel Moolenaar } 595331337658SMarcel Moolenaar 595431337658SMarcel Moolenaar int 595531337658SMarcel Moolenaar xo_emit_h (xo_handle_t *xop, const char *fmt, ...) 595631337658SMarcel Moolenaar { 595731337658SMarcel Moolenaar int rc; 595831337658SMarcel Moolenaar 595931337658SMarcel Moolenaar xop = xo_default(xop); 596031337658SMarcel Moolenaar va_start(xop->xo_vap, fmt); 596131337658SMarcel Moolenaar rc = xo_do_emit(xop, fmt); 596231337658SMarcel Moolenaar va_end(xop->xo_vap); 596331337658SMarcel Moolenaar bzero(&xop->xo_vap, sizeof(xop->xo_vap)); 596431337658SMarcel Moolenaar 596531337658SMarcel Moolenaar return rc; 596631337658SMarcel Moolenaar } 596731337658SMarcel Moolenaar 596831337658SMarcel Moolenaar int 596931337658SMarcel Moolenaar xo_emit (const char *fmt, ...) 597031337658SMarcel Moolenaar { 597131337658SMarcel Moolenaar xo_handle_t *xop = xo_default(NULL); 597231337658SMarcel Moolenaar int rc; 597331337658SMarcel Moolenaar 597431337658SMarcel Moolenaar va_start(xop->xo_vap, fmt); 597531337658SMarcel Moolenaar rc = xo_do_emit(xop, fmt); 597631337658SMarcel Moolenaar va_end(xop->xo_vap); 597731337658SMarcel Moolenaar bzero(&xop->xo_vap, sizeof(xop->xo_vap)); 597831337658SMarcel Moolenaar 597931337658SMarcel Moolenaar return rc; 598031337658SMarcel Moolenaar } 598131337658SMarcel Moolenaar 598231337658SMarcel Moolenaar int 598331337658SMarcel Moolenaar xo_attr_hv (xo_handle_t *xop, const char *name, const char *fmt, va_list vap) 598431337658SMarcel Moolenaar { 598531337658SMarcel Moolenaar const int extra = 5; /* space, equals, quote, quote, and nul */ 598631337658SMarcel Moolenaar xop = xo_default(xop); 598731337658SMarcel Moolenaar 5988d1a0d267SMarcel Moolenaar int rc = 0; 598931337658SMarcel Moolenaar int nlen = strlen(name); 599031337658SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_attrs; 5991d1a0d267SMarcel Moolenaar unsigned name_offset, value_offset; 599231337658SMarcel Moolenaar 5993d1a0d267SMarcel Moolenaar switch (xo_style(xop)) { 5994d1a0d267SMarcel Moolenaar case XO_STYLE_XML: 599531337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, nlen + extra)) 599631337658SMarcel Moolenaar return -1; 599731337658SMarcel Moolenaar 599831337658SMarcel Moolenaar *xbp->xb_curp++ = ' '; 599931337658SMarcel Moolenaar memcpy(xbp->xb_curp, name, nlen); 600031337658SMarcel Moolenaar xbp->xb_curp += nlen; 600131337658SMarcel Moolenaar *xbp->xb_curp++ = '='; 600231337658SMarcel Moolenaar *xbp->xb_curp++ = '"'; 600331337658SMarcel Moolenaar 6004d1a0d267SMarcel Moolenaar rc = xo_vsnprintf(xop, xbp, fmt, vap); 600531337658SMarcel Moolenaar 6006d1a0d267SMarcel Moolenaar if (rc >= 0) { 600731337658SMarcel Moolenaar rc = xo_escape_xml(xbp, rc, 1); 600831337658SMarcel Moolenaar xbp->xb_curp += rc; 600931337658SMarcel Moolenaar } 601031337658SMarcel Moolenaar 601131337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, 2)) 601231337658SMarcel Moolenaar return -1; 601331337658SMarcel Moolenaar 601431337658SMarcel Moolenaar *xbp->xb_curp++ = '"'; 601531337658SMarcel Moolenaar *xbp->xb_curp = '\0'; 601631337658SMarcel Moolenaar 6017d1a0d267SMarcel Moolenaar rc += nlen + extra; 6018d1a0d267SMarcel Moolenaar break; 6019d1a0d267SMarcel Moolenaar 6020d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER: 6021d1a0d267SMarcel Moolenaar name_offset = xo_buf_offset(xbp); 6022d1a0d267SMarcel Moolenaar xo_buf_append(xbp, name, nlen); 6023d1a0d267SMarcel Moolenaar xo_buf_append(xbp, "", 1); 6024d1a0d267SMarcel Moolenaar 6025d1a0d267SMarcel Moolenaar value_offset = xo_buf_offset(xbp); 6026d1a0d267SMarcel Moolenaar rc = xo_vsnprintf(xop, xbp, fmt, vap); 6027d1a0d267SMarcel Moolenaar if (rc >= 0) { 6028d1a0d267SMarcel Moolenaar xbp->xb_curp += rc; 6029d1a0d267SMarcel Moolenaar *xbp->xb_curp = '\0'; 6030d1a0d267SMarcel Moolenaar rc = xo_encoder_handle(xop, XO_OP_ATTRIBUTE, 6031d1a0d267SMarcel Moolenaar xo_buf_data(xbp, name_offset), 6032d1a0d267SMarcel Moolenaar xo_buf_data(xbp, value_offset)); 6033d1a0d267SMarcel Moolenaar } 6034d1a0d267SMarcel Moolenaar } 6035d1a0d267SMarcel Moolenaar 6036d1a0d267SMarcel Moolenaar return rc; 603731337658SMarcel Moolenaar } 603831337658SMarcel Moolenaar 603931337658SMarcel Moolenaar int 604031337658SMarcel Moolenaar xo_attr_h (xo_handle_t *xop, const char *name, const char *fmt, ...) 604131337658SMarcel Moolenaar { 604231337658SMarcel Moolenaar int rc; 604331337658SMarcel Moolenaar va_list vap; 604431337658SMarcel Moolenaar 604531337658SMarcel Moolenaar va_start(vap, fmt); 604631337658SMarcel Moolenaar rc = xo_attr_hv(xop, name, fmt, vap); 604731337658SMarcel Moolenaar va_end(vap); 604831337658SMarcel Moolenaar 604931337658SMarcel Moolenaar return rc; 605031337658SMarcel Moolenaar } 605131337658SMarcel Moolenaar 605231337658SMarcel Moolenaar int 605331337658SMarcel Moolenaar xo_attr (const char *name, const char *fmt, ...) 605431337658SMarcel Moolenaar { 605531337658SMarcel Moolenaar int rc; 605631337658SMarcel Moolenaar va_list vap; 605731337658SMarcel Moolenaar 605831337658SMarcel Moolenaar va_start(vap, fmt); 605931337658SMarcel Moolenaar rc = xo_attr_hv(NULL, name, fmt, vap); 606031337658SMarcel Moolenaar va_end(vap); 606131337658SMarcel Moolenaar 606231337658SMarcel Moolenaar return rc; 606331337658SMarcel Moolenaar } 606431337658SMarcel Moolenaar 606531337658SMarcel Moolenaar static void 606631337658SMarcel Moolenaar xo_stack_set_flags (xo_handle_t *xop) 606731337658SMarcel Moolenaar { 6068d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_NOT_FIRST)) { 606931337658SMarcel Moolenaar xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth]; 607031337658SMarcel Moolenaar 607131337658SMarcel Moolenaar xsp->xs_flags |= XSF_NOT_FIRST; 6072d1a0d267SMarcel Moolenaar XOF_CLEAR(xop, XOF_NOT_FIRST); 607331337658SMarcel Moolenaar } 607431337658SMarcel Moolenaar } 607531337658SMarcel Moolenaar 607631337658SMarcel Moolenaar static void 607731337658SMarcel Moolenaar xo_depth_change (xo_handle_t *xop, const char *name, 6078545ddfbeSMarcel Moolenaar int delta, int indent, xo_state_t state, xo_xsf_flags_t flags) 607931337658SMarcel Moolenaar { 6080788ca347SMarcel Moolenaar if (xo_style(xop) == XO_STYLE_HTML || xo_style(xop) == XO_STYLE_TEXT) 6081545ddfbeSMarcel Moolenaar indent = 0; 6082545ddfbeSMarcel Moolenaar 6083d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_DTRT)) 608431337658SMarcel Moolenaar flags |= XSF_DTRT; 608531337658SMarcel Moolenaar 608631337658SMarcel Moolenaar if (delta >= 0) { /* Push operation */ 608731337658SMarcel Moolenaar if (xo_depth_check(xop, xop->xo_depth + delta)) 608831337658SMarcel Moolenaar return; 608931337658SMarcel Moolenaar 609031337658SMarcel Moolenaar xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth + delta]; 609131337658SMarcel Moolenaar xsp->xs_flags = flags; 6092545ddfbeSMarcel Moolenaar xsp->xs_state = state; 609331337658SMarcel Moolenaar xo_stack_set_flags(xop); 609431337658SMarcel Moolenaar 6095545ddfbeSMarcel Moolenaar if (name == NULL) 6096545ddfbeSMarcel Moolenaar name = XO_FAILURE_NAME; 609731337658SMarcel Moolenaar 6098d1a0d267SMarcel Moolenaar xsp->xs_name = xo_strndup(name, -1); 609931337658SMarcel Moolenaar 610031337658SMarcel Moolenaar } else { /* Pop operation */ 610131337658SMarcel Moolenaar if (xop->xo_depth == 0) { 6102d1a0d267SMarcel Moolenaar if (!XOF_ISSET(xop, XOF_IGNORE_CLOSE)) 610331337658SMarcel Moolenaar xo_failure(xop, "close with empty stack: '%s'", name); 610431337658SMarcel Moolenaar return; 610531337658SMarcel Moolenaar } 610631337658SMarcel Moolenaar 610731337658SMarcel Moolenaar xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth]; 6108d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_WARN)) { 610931337658SMarcel Moolenaar const char *top = xsp->xs_name; 611031337658SMarcel Moolenaar if (top && strcmp(name, top) != 0) { 611131337658SMarcel Moolenaar xo_failure(xop, "incorrect close: '%s' .vs. '%s'", 611231337658SMarcel Moolenaar name, top); 611331337658SMarcel Moolenaar return; 611431337658SMarcel Moolenaar } 611531337658SMarcel Moolenaar if ((xsp->xs_flags & XSF_LIST) != (flags & XSF_LIST)) { 611631337658SMarcel Moolenaar xo_failure(xop, "list close on list confict: '%s'", 611731337658SMarcel Moolenaar name); 611831337658SMarcel Moolenaar return; 611931337658SMarcel Moolenaar } 612031337658SMarcel Moolenaar if ((xsp->xs_flags & XSF_INSTANCE) != (flags & XSF_INSTANCE)) { 612131337658SMarcel Moolenaar xo_failure(xop, "list close on instance confict: '%s'", 612231337658SMarcel Moolenaar name); 612331337658SMarcel Moolenaar return; 612431337658SMarcel Moolenaar } 612531337658SMarcel Moolenaar } 612631337658SMarcel Moolenaar 612731337658SMarcel Moolenaar if (xsp->xs_name) { 612831337658SMarcel Moolenaar xo_free(xsp->xs_name); 612931337658SMarcel Moolenaar xsp->xs_name = NULL; 613031337658SMarcel Moolenaar } 613131337658SMarcel Moolenaar if (xsp->xs_keys) { 613231337658SMarcel Moolenaar xo_free(xsp->xs_keys); 613331337658SMarcel Moolenaar xsp->xs_keys = NULL; 613431337658SMarcel Moolenaar } 613531337658SMarcel Moolenaar } 613631337658SMarcel Moolenaar 613731337658SMarcel Moolenaar xop->xo_depth += delta; /* Record new depth */ 613831337658SMarcel Moolenaar xop->xo_indent += indent; 613931337658SMarcel Moolenaar } 614031337658SMarcel Moolenaar 614131337658SMarcel Moolenaar void 614231337658SMarcel Moolenaar xo_set_depth (xo_handle_t *xop, int depth) 614331337658SMarcel Moolenaar { 614431337658SMarcel Moolenaar xop = xo_default(xop); 614531337658SMarcel Moolenaar 614631337658SMarcel Moolenaar if (xo_depth_check(xop, depth)) 614731337658SMarcel Moolenaar return; 614831337658SMarcel Moolenaar 614931337658SMarcel Moolenaar xop->xo_depth += depth; 615031337658SMarcel Moolenaar xop->xo_indent += depth; 615131337658SMarcel Moolenaar } 615231337658SMarcel Moolenaar 615331337658SMarcel Moolenaar static xo_xsf_flags_t 615431337658SMarcel Moolenaar xo_stack_flags (unsigned xflags) 615531337658SMarcel Moolenaar { 615631337658SMarcel Moolenaar if (xflags & XOF_DTRT) 615731337658SMarcel Moolenaar return XSF_DTRT; 615831337658SMarcel Moolenaar return 0; 615931337658SMarcel Moolenaar } 616031337658SMarcel Moolenaar 6161788ca347SMarcel Moolenaar static void 6162788ca347SMarcel Moolenaar xo_emit_top (xo_handle_t *xop, const char *ppn) 6163788ca347SMarcel Moolenaar { 6164788ca347SMarcel Moolenaar xo_printf(xop, "%*s{%s", xo_indent(xop), "", ppn); 6165d1a0d267SMarcel Moolenaar XOIF_SET(xop, XOIF_TOP_EMITTED); 6166788ca347SMarcel Moolenaar 6167788ca347SMarcel Moolenaar if (xop->xo_version) { 6168788ca347SMarcel Moolenaar xo_printf(xop, "%*s\"__version\": \"%s\", %s", 6169788ca347SMarcel Moolenaar xo_indent(xop), "", xop->xo_version, ppn); 6170788ca347SMarcel Moolenaar xo_free(xop->xo_version); 6171788ca347SMarcel Moolenaar xop->xo_version = NULL; 6172788ca347SMarcel Moolenaar } 6173788ca347SMarcel Moolenaar } 6174788ca347SMarcel Moolenaar 617531337658SMarcel Moolenaar static int 6176545ddfbeSMarcel Moolenaar xo_do_open_container (xo_handle_t *xop, xo_xof_flags_t flags, const char *name) 617731337658SMarcel Moolenaar { 617831337658SMarcel Moolenaar int rc = 0; 6179d1a0d267SMarcel Moolenaar const char *ppn = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : ""; 618031337658SMarcel Moolenaar const char *pre_nl = ""; 618131337658SMarcel Moolenaar 618231337658SMarcel Moolenaar if (name == NULL) { 618331337658SMarcel Moolenaar xo_failure(xop, "NULL passed for container name"); 618431337658SMarcel Moolenaar name = XO_FAILURE_NAME; 618531337658SMarcel Moolenaar } 618631337658SMarcel Moolenaar 618731337658SMarcel Moolenaar flags |= xop->xo_flags; /* Pick up handle flags */ 618831337658SMarcel Moolenaar 6189788ca347SMarcel Moolenaar switch (xo_style(xop)) { 619031337658SMarcel Moolenaar case XO_STYLE_XML: 6191545ddfbeSMarcel Moolenaar rc = xo_printf(xop, "%*s<%s", xo_indent(xop), "", name); 6192545ddfbeSMarcel Moolenaar 6193545ddfbeSMarcel Moolenaar if (xop->xo_attrs.xb_curp != xop->xo_attrs.xb_bufp) { 6194545ddfbeSMarcel Moolenaar rc += xop->xo_attrs.xb_curp - xop->xo_attrs.xb_bufp; 6195545ddfbeSMarcel Moolenaar xo_data_append(xop, xop->xo_attrs.xb_bufp, 6196545ddfbeSMarcel Moolenaar xop->xo_attrs.xb_curp - xop->xo_attrs.xb_bufp); 6197545ddfbeSMarcel Moolenaar xop->xo_attrs.xb_curp = xop->xo_attrs.xb_bufp; 6198545ddfbeSMarcel Moolenaar } 6199545ddfbeSMarcel Moolenaar 6200545ddfbeSMarcel Moolenaar rc += xo_printf(xop, ">%s", ppn); 620131337658SMarcel Moolenaar break; 620231337658SMarcel Moolenaar 620331337658SMarcel Moolenaar case XO_STYLE_JSON: 620431337658SMarcel Moolenaar xo_stack_set_flags(xop); 620531337658SMarcel Moolenaar 6206d1a0d267SMarcel Moolenaar if (!XOF_ISSET(xop, XOF_NO_TOP) 6207d1a0d267SMarcel Moolenaar && !XOIF_ISSET(xop, XOIF_TOP_EMITTED)) 6208788ca347SMarcel Moolenaar xo_emit_top(xop, ppn); 620931337658SMarcel Moolenaar 621031337658SMarcel Moolenaar if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST) 6211d1a0d267SMarcel Moolenaar pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? ",\n" : ", "; 621231337658SMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 621331337658SMarcel Moolenaar 621431337658SMarcel Moolenaar rc = xo_printf(xop, "%s%*s\"%s\": {%s", 621531337658SMarcel Moolenaar pre_nl, xo_indent(xop), "", name, ppn); 621631337658SMarcel Moolenaar break; 6217d1a0d267SMarcel Moolenaar 6218d1a0d267SMarcel Moolenaar case XO_STYLE_SDPARAMS: 6219d1a0d267SMarcel Moolenaar break; 6220d1a0d267SMarcel Moolenaar 6221d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER: 6222d1a0d267SMarcel Moolenaar rc = xo_encoder_handle(xop, XO_OP_OPEN_CONTAINER, name, NULL); 6223d1a0d267SMarcel Moolenaar break; 622431337658SMarcel Moolenaar } 622531337658SMarcel Moolenaar 6226545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, 1, 1, XSS_OPEN_CONTAINER, 6227545ddfbeSMarcel Moolenaar xo_stack_flags(flags)); 6228545ddfbeSMarcel Moolenaar 622931337658SMarcel Moolenaar return rc; 623031337658SMarcel Moolenaar } 623131337658SMarcel Moolenaar 6232545ddfbeSMarcel Moolenaar static int 6233545ddfbeSMarcel Moolenaar xo_open_container_hf (xo_handle_t *xop, xo_xof_flags_t flags, const char *name) 6234545ddfbeSMarcel Moolenaar { 6235545ddfbeSMarcel Moolenaar return xo_transition(xop, flags, name, XSS_OPEN_CONTAINER); 6236545ddfbeSMarcel Moolenaar } 6237545ddfbeSMarcel Moolenaar 623831337658SMarcel Moolenaar int 623931337658SMarcel Moolenaar xo_open_container_h (xo_handle_t *xop, const char *name) 624031337658SMarcel Moolenaar { 624131337658SMarcel Moolenaar return xo_open_container_hf(xop, 0, name); 624231337658SMarcel Moolenaar } 624331337658SMarcel Moolenaar 624431337658SMarcel Moolenaar int 624531337658SMarcel Moolenaar xo_open_container (const char *name) 624631337658SMarcel Moolenaar { 624731337658SMarcel Moolenaar return xo_open_container_hf(NULL, 0, name); 624831337658SMarcel Moolenaar } 624931337658SMarcel Moolenaar 625031337658SMarcel Moolenaar int 625131337658SMarcel Moolenaar xo_open_container_hd (xo_handle_t *xop, const char *name) 625231337658SMarcel Moolenaar { 625331337658SMarcel Moolenaar return xo_open_container_hf(xop, XOF_DTRT, name); 625431337658SMarcel Moolenaar } 625531337658SMarcel Moolenaar 625631337658SMarcel Moolenaar int 625731337658SMarcel Moolenaar xo_open_container_d (const char *name) 625831337658SMarcel Moolenaar { 625931337658SMarcel Moolenaar return xo_open_container_hf(NULL, XOF_DTRT, name); 626031337658SMarcel Moolenaar } 626131337658SMarcel Moolenaar 6262545ddfbeSMarcel Moolenaar static int 6263545ddfbeSMarcel Moolenaar xo_do_close_container (xo_handle_t *xop, const char *name) 626431337658SMarcel Moolenaar { 626531337658SMarcel Moolenaar xop = xo_default(xop); 626631337658SMarcel Moolenaar 626731337658SMarcel Moolenaar int rc = 0; 6268d1a0d267SMarcel Moolenaar const char *ppn = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : ""; 626931337658SMarcel Moolenaar const char *pre_nl = ""; 627031337658SMarcel Moolenaar 627131337658SMarcel Moolenaar if (name == NULL) { 627231337658SMarcel Moolenaar xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth]; 627331337658SMarcel Moolenaar 627431337658SMarcel Moolenaar name = xsp->xs_name; 627531337658SMarcel Moolenaar if (name) { 627631337658SMarcel Moolenaar int len = strlen(name) + 1; 627731337658SMarcel Moolenaar /* We need to make a local copy; xo_depth_change will free it */ 627831337658SMarcel Moolenaar char *cp = alloca(len); 627931337658SMarcel Moolenaar memcpy(cp, name, len); 628031337658SMarcel Moolenaar name = cp; 6281545ddfbeSMarcel Moolenaar } else if (!(xsp->xs_flags & XSF_DTRT)) { 6282545ddfbeSMarcel Moolenaar xo_failure(xop, "missing name without 'dtrt' mode"); 628331337658SMarcel Moolenaar name = XO_FAILURE_NAME; 628431337658SMarcel Moolenaar } 6285545ddfbeSMarcel Moolenaar } 628631337658SMarcel Moolenaar 6287788ca347SMarcel Moolenaar switch (xo_style(xop)) { 628831337658SMarcel Moolenaar case XO_STYLE_XML: 6289545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, -1, -1, XSS_CLOSE_CONTAINER, 0); 629031337658SMarcel Moolenaar rc = xo_printf(xop, "%*s</%s>%s", xo_indent(xop), "", name, ppn); 629131337658SMarcel Moolenaar break; 629231337658SMarcel Moolenaar 629331337658SMarcel Moolenaar case XO_STYLE_JSON: 6294d1a0d267SMarcel Moolenaar pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : ""; 629531337658SMarcel Moolenaar ppn = (xop->xo_depth <= 1) ? "\n" : ""; 629631337658SMarcel Moolenaar 6297545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, -1, -1, XSS_CLOSE_CONTAINER, 0); 629831337658SMarcel Moolenaar rc = xo_printf(xop, "%s%*s}%s", pre_nl, xo_indent(xop), "", ppn); 629931337658SMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 630031337658SMarcel Moolenaar break; 630131337658SMarcel Moolenaar 630231337658SMarcel Moolenaar case XO_STYLE_HTML: 630331337658SMarcel Moolenaar case XO_STYLE_TEXT: 6304545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, -1, 0, XSS_CLOSE_CONTAINER, 0); 630531337658SMarcel Moolenaar break; 6306d1a0d267SMarcel Moolenaar 6307d1a0d267SMarcel Moolenaar case XO_STYLE_SDPARAMS: 6308d1a0d267SMarcel Moolenaar break; 6309d1a0d267SMarcel Moolenaar 6310d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER: 6311d1a0d267SMarcel Moolenaar xo_depth_change(xop, name, -1, 0, XSS_CLOSE_CONTAINER, 0); 6312d1a0d267SMarcel Moolenaar rc = xo_encoder_handle(xop, XO_OP_CLOSE_CONTAINER, name, NULL); 6313d1a0d267SMarcel Moolenaar break; 631431337658SMarcel Moolenaar } 631531337658SMarcel Moolenaar 631631337658SMarcel Moolenaar return rc; 631731337658SMarcel Moolenaar } 631831337658SMarcel Moolenaar 631931337658SMarcel Moolenaar int 6320545ddfbeSMarcel Moolenaar xo_close_container_h (xo_handle_t *xop, const char *name) 6321545ddfbeSMarcel Moolenaar { 6322545ddfbeSMarcel Moolenaar return xo_transition(xop, 0, name, XSS_CLOSE_CONTAINER); 6323545ddfbeSMarcel Moolenaar } 6324545ddfbeSMarcel Moolenaar 6325545ddfbeSMarcel Moolenaar int 632631337658SMarcel Moolenaar xo_close_container (const char *name) 632731337658SMarcel Moolenaar { 632831337658SMarcel Moolenaar return xo_close_container_h(NULL, name); 632931337658SMarcel Moolenaar } 633031337658SMarcel Moolenaar 633131337658SMarcel Moolenaar int 633231337658SMarcel Moolenaar xo_close_container_hd (xo_handle_t *xop) 633331337658SMarcel Moolenaar { 633431337658SMarcel Moolenaar return xo_close_container_h(xop, NULL); 633531337658SMarcel Moolenaar } 633631337658SMarcel Moolenaar 633731337658SMarcel Moolenaar int 633831337658SMarcel Moolenaar xo_close_container_d (void) 633931337658SMarcel Moolenaar { 634031337658SMarcel Moolenaar return xo_close_container_h(NULL, NULL); 634131337658SMarcel Moolenaar } 634231337658SMarcel Moolenaar 634331337658SMarcel Moolenaar static int 6344545ddfbeSMarcel Moolenaar xo_do_open_list (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name) 634531337658SMarcel Moolenaar { 6346545ddfbeSMarcel Moolenaar int rc = 0; 6347545ddfbeSMarcel Moolenaar int indent = 0; 6348545ddfbeSMarcel Moolenaar 634931337658SMarcel Moolenaar xop = xo_default(xop); 635031337658SMarcel Moolenaar 6351d1a0d267SMarcel Moolenaar const char *ppn = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : ""; 635231337658SMarcel Moolenaar const char *pre_nl = ""; 635331337658SMarcel Moolenaar 6354d1a0d267SMarcel Moolenaar switch (xo_style(xop)) { 6355d1a0d267SMarcel Moolenaar case XO_STYLE_JSON: 6356d1a0d267SMarcel Moolenaar 6357545ddfbeSMarcel Moolenaar indent = 1; 6358d1a0d267SMarcel Moolenaar if (!XOF_ISSET(xop, XOF_NO_TOP) 6359d1a0d267SMarcel Moolenaar && !XOIF_ISSET(xop, XOIF_TOP_EMITTED)) 6360788ca347SMarcel Moolenaar xo_emit_top(xop, ppn); 636131337658SMarcel Moolenaar 636231337658SMarcel Moolenaar if (name == NULL) { 636331337658SMarcel Moolenaar xo_failure(xop, "NULL passed for list name"); 636431337658SMarcel Moolenaar name = XO_FAILURE_NAME; 636531337658SMarcel Moolenaar } 636631337658SMarcel Moolenaar 636731337658SMarcel Moolenaar xo_stack_set_flags(xop); 636831337658SMarcel Moolenaar 636931337658SMarcel Moolenaar if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST) 6370d1a0d267SMarcel Moolenaar pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? ",\n" : ", "; 637131337658SMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 637231337658SMarcel Moolenaar 637331337658SMarcel Moolenaar rc = xo_printf(xop, "%s%*s\"%s\": [%s", 637431337658SMarcel Moolenaar pre_nl, xo_indent(xop), "", name, ppn); 6375d1a0d267SMarcel Moolenaar break; 6376d1a0d267SMarcel Moolenaar 6377d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER: 6378d1a0d267SMarcel Moolenaar rc = xo_encoder_handle(xop, XO_OP_OPEN_LIST, name, NULL); 6379d1a0d267SMarcel Moolenaar break; 6380545ddfbeSMarcel Moolenaar } 6381545ddfbeSMarcel Moolenaar 6382545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, 1, indent, XSS_OPEN_LIST, 6383545ddfbeSMarcel Moolenaar XSF_LIST | xo_stack_flags(flags)); 638431337658SMarcel Moolenaar 638531337658SMarcel Moolenaar return rc; 638631337658SMarcel Moolenaar } 638731337658SMarcel Moolenaar 6388545ddfbeSMarcel Moolenaar static int 6389545ddfbeSMarcel Moolenaar xo_open_list_hf (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name) 6390545ddfbeSMarcel Moolenaar { 6391545ddfbeSMarcel Moolenaar return xo_transition(xop, flags, name, XSS_OPEN_LIST); 6392545ddfbeSMarcel Moolenaar } 6393545ddfbeSMarcel Moolenaar 639431337658SMarcel Moolenaar int 639531337658SMarcel Moolenaar xo_open_list_h (xo_handle_t *xop, const char *name UNUSED) 639631337658SMarcel Moolenaar { 639731337658SMarcel Moolenaar return xo_open_list_hf(xop, 0, name); 639831337658SMarcel Moolenaar } 639931337658SMarcel Moolenaar 640031337658SMarcel Moolenaar int 640131337658SMarcel Moolenaar xo_open_list (const char *name) 640231337658SMarcel Moolenaar { 640331337658SMarcel Moolenaar return xo_open_list_hf(NULL, 0, name); 640431337658SMarcel Moolenaar } 640531337658SMarcel Moolenaar 640631337658SMarcel Moolenaar int 640731337658SMarcel Moolenaar xo_open_list_hd (xo_handle_t *xop, const char *name UNUSED) 640831337658SMarcel Moolenaar { 640931337658SMarcel Moolenaar return xo_open_list_hf(xop, XOF_DTRT, name); 641031337658SMarcel Moolenaar } 641131337658SMarcel Moolenaar 641231337658SMarcel Moolenaar int 641331337658SMarcel Moolenaar xo_open_list_d (const char *name) 641431337658SMarcel Moolenaar { 641531337658SMarcel Moolenaar return xo_open_list_hf(NULL, XOF_DTRT, name); 641631337658SMarcel Moolenaar } 641731337658SMarcel Moolenaar 6418545ddfbeSMarcel Moolenaar static int 6419545ddfbeSMarcel Moolenaar xo_do_close_list (xo_handle_t *xop, const char *name) 642031337658SMarcel Moolenaar { 642131337658SMarcel Moolenaar int rc = 0; 642231337658SMarcel Moolenaar const char *pre_nl = ""; 642331337658SMarcel Moolenaar 642431337658SMarcel Moolenaar if (name == NULL) { 642531337658SMarcel Moolenaar xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth]; 642631337658SMarcel Moolenaar 642731337658SMarcel Moolenaar name = xsp->xs_name; 642831337658SMarcel Moolenaar if (name) { 642931337658SMarcel Moolenaar int len = strlen(name) + 1; 643031337658SMarcel Moolenaar /* We need to make a local copy; xo_depth_change will free it */ 643131337658SMarcel Moolenaar char *cp = alloca(len); 643231337658SMarcel Moolenaar memcpy(cp, name, len); 643331337658SMarcel Moolenaar name = cp; 6434545ddfbeSMarcel Moolenaar } else if (!(xsp->xs_flags & XSF_DTRT)) { 6435545ddfbeSMarcel Moolenaar xo_failure(xop, "missing name without 'dtrt' mode"); 643631337658SMarcel Moolenaar name = XO_FAILURE_NAME; 643731337658SMarcel Moolenaar } 6438545ddfbeSMarcel Moolenaar } 643931337658SMarcel Moolenaar 6440d1a0d267SMarcel Moolenaar switch (xo_style(xop)) { 6441d1a0d267SMarcel Moolenaar case XO_STYLE_JSON: 644231337658SMarcel Moolenaar if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST) 6443d1a0d267SMarcel Moolenaar pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : ""; 644431337658SMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 644531337658SMarcel Moolenaar 6446545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, -1, -1, XSS_CLOSE_LIST, XSF_LIST); 644731337658SMarcel Moolenaar rc = xo_printf(xop, "%s%*s]", pre_nl, xo_indent(xop), ""); 644831337658SMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 6449d1a0d267SMarcel Moolenaar break; 645031337658SMarcel Moolenaar 6451d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER: 6452d1a0d267SMarcel Moolenaar xo_depth_change(xop, name, -1, 0, XSS_CLOSE_LIST, XSF_LIST); 6453d1a0d267SMarcel Moolenaar rc = xo_encoder_handle(xop, XO_OP_CLOSE_LIST, name, NULL); 6454d1a0d267SMarcel Moolenaar break; 6455d1a0d267SMarcel Moolenaar 6456d1a0d267SMarcel Moolenaar default: 6457545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, -1, 0, XSS_CLOSE_LIST, XSF_LIST); 6458545ddfbeSMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 6459d1a0d267SMarcel Moolenaar break; 6460545ddfbeSMarcel Moolenaar } 6461545ddfbeSMarcel Moolenaar 6462a0f704ffSMarcel Moolenaar return rc; 646331337658SMarcel Moolenaar } 646431337658SMarcel Moolenaar 646531337658SMarcel Moolenaar int 6466545ddfbeSMarcel Moolenaar xo_close_list_h (xo_handle_t *xop, const char *name) 6467545ddfbeSMarcel Moolenaar { 6468545ddfbeSMarcel Moolenaar return xo_transition(xop, 0, name, XSS_CLOSE_LIST); 6469545ddfbeSMarcel Moolenaar } 6470545ddfbeSMarcel Moolenaar 6471545ddfbeSMarcel Moolenaar int 647231337658SMarcel Moolenaar xo_close_list (const char *name) 647331337658SMarcel Moolenaar { 647431337658SMarcel Moolenaar return xo_close_list_h(NULL, name); 647531337658SMarcel Moolenaar } 647631337658SMarcel Moolenaar 647731337658SMarcel Moolenaar int 647831337658SMarcel Moolenaar xo_close_list_hd (xo_handle_t *xop) 647931337658SMarcel Moolenaar { 648031337658SMarcel Moolenaar return xo_close_list_h(xop, NULL); 648131337658SMarcel Moolenaar } 648231337658SMarcel Moolenaar 648331337658SMarcel Moolenaar int 648431337658SMarcel Moolenaar xo_close_list_d (void) 648531337658SMarcel Moolenaar { 648631337658SMarcel Moolenaar return xo_close_list_h(NULL, NULL); 648731337658SMarcel Moolenaar } 648831337658SMarcel Moolenaar 648931337658SMarcel Moolenaar static int 6490545ddfbeSMarcel Moolenaar xo_do_open_leaf_list (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name) 6491545ddfbeSMarcel Moolenaar { 6492545ddfbeSMarcel Moolenaar int rc = 0; 6493545ddfbeSMarcel Moolenaar int indent = 0; 6494545ddfbeSMarcel Moolenaar 6495545ddfbeSMarcel Moolenaar xop = xo_default(xop); 6496545ddfbeSMarcel Moolenaar 6497d1a0d267SMarcel Moolenaar const char *ppn = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : ""; 6498545ddfbeSMarcel Moolenaar const char *pre_nl = ""; 6499545ddfbeSMarcel Moolenaar 6500d1a0d267SMarcel Moolenaar switch (xo_style(xop)) { 6501d1a0d267SMarcel Moolenaar case XO_STYLE_JSON: 6502545ddfbeSMarcel Moolenaar indent = 1; 6503545ddfbeSMarcel Moolenaar 6504d1a0d267SMarcel Moolenaar if (!XOF_ISSET(xop, XOF_NO_TOP)) { 6505d1a0d267SMarcel Moolenaar if (!XOIF_ISSET(xop, XOIF_TOP_EMITTED)) { 6506545ddfbeSMarcel Moolenaar xo_printf(xop, "%*s{%s", xo_indent(xop), "", ppn); 6507d1a0d267SMarcel Moolenaar XOIF_SET(xop, XOIF_TOP_EMITTED); 6508545ddfbeSMarcel Moolenaar } 6509545ddfbeSMarcel Moolenaar } 6510545ddfbeSMarcel Moolenaar 6511545ddfbeSMarcel Moolenaar if (name == NULL) { 6512545ddfbeSMarcel Moolenaar xo_failure(xop, "NULL passed for list name"); 6513545ddfbeSMarcel Moolenaar name = XO_FAILURE_NAME; 6514545ddfbeSMarcel Moolenaar } 6515545ddfbeSMarcel Moolenaar 6516545ddfbeSMarcel Moolenaar xo_stack_set_flags(xop); 6517545ddfbeSMarcel Moolenaar 6518545ddfbeSMarcel Moolenaar if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST) 6519d1a0d267SMarcel Moolenaar pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? ",\n" : ", "; 6520545ddfbeSMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 6521545ddfbeSMarcel Moolenaar 6522545ddfbeSMarcel Moolenaar rc = xo_printf(xop, "%s%*s\"%s\": [%s", 6523545ddfbeSMarcel Moolenaar pre_nl, xo_indent(xop), "", name, ppn); 6524d1a0d267SMarcel Moolenaar break; 6525d1a0d267SMarcel Moolenaar 6526d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER: 6527d1a0d267SMarcel Moolenaar rc = xo_encoder_handle(xop, XO_OP_OPEN_LEAF_LIST, name, NULL); 6528d1a0d267SMarcel Moolenaar break; 6529545ddfbeSMarcel Moolenaar } 6530545ddfbeSMarcel Moolenaar 6531545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, 1, indent, XSS_OPEN_LEAF_LIST, 6532545ddfbeSMarcel Moolenaar XSF_LIST | xo_stack_flags(flags)); 6533545ddfbeSMarcel Moolenaar 6534545ddfbeSMarcel Moolenaar return rc; 6535545ddfbeSMarcel Moolenaar } 6536545ddfbeSMarcel Moolenaar 6537545ddfbeSMarcel Moolenaar static int 6538545ddfbeSMarcel Moolenaar xo_do_close_leaf_list (xo_handle_t *xop, const char *name) 6539545ddfbeSMarcel Moolenaar { 6540545ddfbeSMarcel Moolenaar int rc = 0; 6541545ddfbeSMarcel Moolenaar const char *pre_nl = ""; 6542545ddfbeSMarcel Moolenaar 6543545ddfbeSMarcel Moolenaar if (name == NULL) { 6544545ddfbeSMarcel Moolenaar xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth]; 6545545ddfbeSMarcel Moolenaar 6546545ddfbeSMarcel Moolenaar name = xsp->xs_name; 6547545ddfbeSMarcel Moolenaar if (name) { 6548545ddfbeSMarcel Moolenaar int len = strlen(name) + 1; 6549545ddfbeSMarcel Moolenaar /* We need to make a local copy; xo_depth_change will free it */ 6550545ddfbeSMarcel Moolenaar char *cp = alloca(len); 6551545ddfbeSMarcel Moolenaar memcpy(cp, name, len); 6552545ddfbeSMarcel Moolenaar name = cp; 6553545ddfbeSMarcel Moolenaar } else if (!(xsp->xs_flags & XSF_DTRT)) { 6554545ddfbeSMarcel Moolenaar xo_failure(xop, "missing name without 'dtrt' mode"); 6555545ddfbeSMarcel Moolenaar name = XO_FAILURE_NAME; 6556545ddfbeSMarcel Moolenaar } 6557545ddfbeSMarcel Moolenaar } 6558545ddfbeSMarcel Moolenaar 6559d1a0d267SMarcel Moolenaar switch (xo_style(xop)) { 6560d1a0d267SMarcel Moolenaar case XO_STYLE_JSON: 6561545ddfbeSMarcel Moolenaar if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST) 6562d1a0d267SMarcel Moolenaar pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : ""; 6563545ddfbeSMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 6564545ddfbeSMarcel Moolenaar 6565545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, -1, -1, XSS_CLOSE_LEAF_LIST, XSF_LIST); 6566545ddfbeSMarcel Moolenaar rc = xo_printf(xop, "%s%*s]", pre_nl, xo_indent(xop), ""); 6567545ddfbeSMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 6568d1a0d267SMarcel Moolenaar break; 6569545ddfbeSMarcel Moolenaar 6570d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER: 6571d1a0d267SMarcel Moolenaar rc = xo_encoder_handle(xop, XO_OP_CLOSE_LEAF_LIST, name, NULL); 6572d1a0d267SMarcel Moolenaar /*fallthru*/ 6573d1a0d267SMarcel Moolenaar 6574d1a0d267SMarcel Moolenaar default: 6575545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, -1, 0, XSS_CLOSE_LEAF_LIST, XSF_LIST); 6576545ddfbeSMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 6577d1a0d267SMarcel Moolenaar break; 6578545ddfbeSMarcel Moolenaar } 6579545ddfbeSMarcel Moolenaar 6580545ddfbeSMarcel Moolenaar return rc; 6581545ddfbeSMarcel Moolenaar } 6582545ddfbeSMarcel Moolenaar 6583545ddfbeSMarcel Moolenaar static int 6584545ddfbeSMarcel Moolenaar xo_do_open_instance (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name) 658531337658SMarcel Moolenaar { 658631337658SMarcel Moolenaar xop = xo_default(xop); 658731337658SMarcel Moolenaar 658831337658SMarcel Moolenaar int rc = 0; 6589d1a0d267SMarcel Moolenaar const char *ppn = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : ""; 659031337658SMarcel Moolenaar const char *pre_nl = ""; 659131337658SMarcel Moolenaar 659231337658SMarcel Moolenaar flags |= xop->xo_flags; 659331337658SMarcel Moolenaar 659431337658SMarcel Moolenaar if (name == NULL) { 659531337658SMarcel Moolenaar xo_failure(xop, "NULL passed for instance name"); 659631337658SMarcel Moolenaar name = XO_FAILURE_NAME; 659731337658SMarcel Moolenaar } 659831337658SMarcel Moolenaar 6599788ca347SMarcel Moolenaar switch (xo_style(xop)) { 660031337658SMarcel Moolenaar case XO_STYLE_XML: 6601545ddfbeSMarcel Moolenaar rc = xo_printf(xop, "%*s<%s", xo_indent(xop), "", name); 6602545ddfbeSMarcel Moolenaar 6603545ddfbeSMarcel Moolenaar if (xop->xo_attrs.xb_curp != xop->xo_attrs.xb_bufp) { 6604545ddfbeSMarcel Moolenaar rc += xop->xo_attrs.xb_curp - xop->xo_attrs.xb_bufp; 6605545ddfbeSMarcel Moolenaar xo_data_append(xop, xop->xo_attrs.xb_bufp, 6606545ddfbeSMarcel Moolenaar xop->xo_attrs.xb_curp - xop->xo_attrs.xb_bufp); 6607545ddfbeSMarcel Moolenaar xop->xo_attrs.xb_curp = xop->xo_attrs.xb_bufp; 6608545ddfbeSMarcel Moolenaar } 6609545ddfbeSMarcel Moolenaar 6610545ddfbeSMarcel Moolenaar rc += xo_printf(xop, ">%s", ppn); 661131337658SMarcel Moolenaar break; 661231337658SMarcel Moolenaar 661331337658SMarcel Moolenaar case XO_STYLE_JSON: 661431337658SMarcel Moolenaar xo_stack_set_flags(xop); 661531337658SMarcel Moolenaar 661631337658SMarcel Moolenaar if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST) 6617d1a0d267SMarcel Moolenaar pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? ",\n" : ", "; 661831337658SMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 661931337658SMarcel Moolenaar 662031337658SMarcel Moolenaar rc = xo_printf(xop, "%s%*s{%s", 662131337658SMarcel Moolenaar pre_nl, xo_indent(xop), "", ppn); 662231337658SMarcel Moolenaar break; 6623d1a0d267SMarcel Moolenaar 6624d1a0d267SMarcel Moolenaar case XO_STYLE_SDPARAMS: 6625d1a0d267SMarcel Moolenaar break; 6626d1a0d267SMarcel Moolenaar 6627d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER: 6628d1a0d267SMarcel Moolenaar rc = xo_encoder_handle(xop, XO_OP_OPEN_INSTANCE, name, NULL); 6629d1a0d267SMarcel Moolenaar break; 663031337658SMarcel Moolenaar } 663131337658SMarcel Moolenaar 6632545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, 1, 1, XSS_OPEN_INSTANCE, xo_stack_flags(flags)); 6633545ddfbeSMarcel Moolenaar 663431337658SMarcel Moolenaar return rc; 663531337658SMarcel Moolenaar } 663631337658SMarcel Moolenaar 6637545ddfbeSMarcel Moolenaar static int 6638545ddfbeSMarcel Moolenaar xo_open_instance_hf (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name) 6639545ddfbeSMarcel Moolenaar { 6640545ddfbeSMarcel Moolenaar return xo_transition(xop, flags, name, XSS_OPEN_INSTANCE); 6641545ddfbeSMarcel Moolenaar } 6642545ddfbeSMarcel Moolenaar 664331337658SMarcel Moolenaar int 664431337658SMarcel Moolenaar xo_open_instance_h (xo_handle_t *xop, const char *name) 664531337658SMarcel Moolenaar { 664631337658SMarcel Moolenaar return xo_open_instance_hf(xop, 0, name); 664731337658SMarcel Moolenaar } 664831337658SMarcel Moolenaar 664931337658SMarcel Moolenaar int 665031337658SMarcel Moolenaar xo_open_instance (const char *name) 665131337658SMarcel Moolenaar { 665231337658SMarcel Moolenaar return xo_open_instance_hf(NULL, 0, name); 665331337658SMarcel Moolenaar } 665431337658SMarcel Moolenaar 665531337658SMarcel Moolenaar int 665631337658SMarcel Moolenaar xo_open_instance_hd (xo_handle_t *xop, const char *name) 665731337658SMarcel Moolenaar { 665831337658SMarcel Moolenaar return xo_open_instance_hf(xop, XOF_DTRT, name); 665931337658SMarcel Moolenaar } 666031337658SMarcel Moolenaar 666131337658SMarcel Moolenaar int 666231337658SMarcel Moolenaar xo_open_instance_d (const char *name) 666331337658SMarcel Moolenaar { 666431337658SMarcel Moolenaar return xo_open_instance_hf(NULL, XOF_DTRT, name); 666531337658SMarcel Moolenaar } 666631337658SMarcel Moolenaar 6667545ddfbeSMarcel Moolenaar static int 6668545ddfbeSMarcel Moolenaar xo_do_close_instance (xo_handle_t *xop, const char *name) 666931337658SMarcel Moolenaar { 667031337658SMarcel Moolenaar xop = xo_default(xop); 667131337658SMarcel Moolenaar 667231337658SMarcel Moolenaar int rc = 0; 6673d1a0d267SMarcel Moolenaar const char *ppn = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : ""; 667431337658SMarcel Moolenaar const char *pre_nl = ""; 667531337658SMarcel Moolenaar 667631337658SMarcel Moolenaar if (name == NULL) { 667731337658SMarcel Moolenaar xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth]; 667831337658SMarcel Moolenaar 667931337658SMarcel Moolenaar name = xsp->xs_name; 668031337658SMarcel Moolenaar if (name) { 668131337658SMarcel Moolenaar int len = strlen(name) + 1; 668231337658SMarcel Moolenaar /* We need to make a local copy; xo_depth_change will free it */ 668331337658SMarcel Moolenaar char *cp = alloca(len); 668431337658SMarcel Moolenaar memcpy(cp, name, len); 668531337658SMarcel Moolenaar name = cp; 6686545ddfbeSMarcel Moolenaar } else if (!(xsp->xs_flags & XSF_DTRT)) { 6687545ddfbeSMarcel Moolenaar xo_failure(xop, "missing name without 'dtrt' mode"); 668831337658SMarcel Moolenaar name = XO_FAILURE_NAME; 668931337658SMarcel Moolenaar } 6690545ddfbeSMarcel Moolenaar } 669131337658SMarcel Moolenaar 6692788ca347SMarcel Moolenaar switch (xo_style(xop)) { 669331337658SMarcel Moolenaar case XO_STYLE_XML: 6694545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, -1, -1, XSS_CLOSE_INSTANCE, 0); 669531337658SMarcel Moolenaar rc = xo_printf(xop, "%*s</%s>%s", xo_indent(xop), "", name, ppn); 669631337658SMarcel Moolenaar break; 669731337658SMarcel Moolenaar 669831337658SMarcel Moolenaar case XO_STYLE_JSON: 6699d1a0d267SMarcel Moolenaar pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : ""; 670031337658SMarcel Moolenaar 6701545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, -1, -1, XSS_CLOSE_INSTANCE, 0); 670231337658SMarcel Moolenaar rc = xo_printf(xop, "%s%*s}", pre_nl, xo_indent(xop), ""); 670331337658SMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 670431337658SMarcel Moolenaar break; 670531337658SMarcel Moolenaar 670631337658SMarcel Moolenaar case XO_STYLE_HTML: 670731337658SMarcel Moolenaar case XO_STYLE_TEXT: 6708545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, -1, 0, XSS_CLOSE_INSTANCE, 0); 670931337658SMarcel Moolenaar break; 6710d1a0d267SMarcel Moolenaar 6711d1a0d267SMarcel Moolenaar case XO_STYLE_SDPARAMS: 6712d1a0d267SMarcel Moolenaar break; 6713d1a0d267SMarcel Moolenaar 6714d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER: 6715d1a0d267SMarcel Moolenaar xo_depth_change(xop, name, -1, 0, XSS_CLOSE_INSTANCE, 0); 6716d1a0d267SMarcel Moolenaar rc = xo_encoder_handle(xop, XO_OP_CLOSE_INSTANCE, name, NULL); 6717d1a0d267SMarcel Moolenaar break; 671831337658SMarcel Moolenaar } 671931337658SMarcel Moolenaar 672031337658SMarcel Moolenaar return rc; 672131337658SMarcel Moolenaar } 672231337658SMarcel Moolenaar 672331337658SMarcel Moolenaar int 6724545ddfbeSMarcel Moolenaar xo_close_instance_h (xo_handle_t *xop, const char *name) 6725545ddfbeSMarcel Moolenaar { 6726545ddfbeSMarcel Moolenaar return xo_transition(xop, 0, name, XSS_CLOSE_INSTANCE); 6727545ddfbeSMarcel Moolenaar } 6728545ddfbeSMarcel Moolenaar 6729545ddfbeSMarcel Moolenaar int 673031337658SMarcel Moolenaar xo_close_instance (const char *name) 673131337658SMarcel Moolenaar { 673231337658SMarcel Moolenaar return xo_close_instance_h(NULL, name); 673331337658SMarcel Moolenaar } 673431337658SMarcel Moolenaar 673531337658SMarcel Moolenaar int 673631337658SMarcel Moolenaar xo_close_instance_hd (xo_handle_t *xop) 673731337658SMarcel Moolenaar { 673831337658SMarcel Moolenaar return xo_close_instance_h(xop, NULL); 673931337658SMarcel Moolenaar } 674031337658SMarcel Moolenaar 674131337658SMarcel Moolenaar int 674231337658SMarcel Moolenaar xo_close_instance_d (void) 674331337658SMarcel Moolenaar { 674431337658SMarcel Moolenaar return xo_close_instance_h(NULL, NULL); 674531337658SMarcel Moolenaar } 674631337658SMarcel Moolenaar 6747545ddfbeSMarcel Moolenaar static int 6748545ddfbeSMarcel Moolenaar xo_do_close_all (xo_handle_t *xop, xo_stack_t *limit) 6749545ddfbeSMarcel Moolenaar { 6750545ddfbeSMarcel Moolenaar xo_stack_t *xsp; 6751545ddfbeSMarcel Moolenaar int rc = 0; 6752545ddfbeSMarcel Moolenaar xo_xsf_flags_t flags; 6753545ddfbeSMarcel Moolenaar 6754545ddfbeSMarcel Moolenaar for (xsp = &xop->xo_stack[xop->xo_depth]; xsp >= limit; xsp--) { 6755545ddfbeSMarcel Moolenaar switch (xsp->xs_state) { 6756545ddfbeSMarcel Moolenaar case XSS_INIT: 6757545ddfbeSMarcel Moolenaar /* Nothing */ 6758545ddfbeSMarcel Moolenaar rc = 0; 6759545ddfbeSMarcel Moolenaar break; 6760545ddfbeSMarcel Moolenaar 6761545ddfbeSMarcel Moolenaar case XSS_OPEN_CONTAINER: 6762545ddfbeSMarcel Moolenaar rc = xo_do_close_container(xop, NULL); 6763545ddfbeSMarcel Moolenaar break; 6764545ddfbeSMarcel Moolenaar 6765545ddfbeSMarcel Moolenaar case XSS_OPEN_LIST: 6766545ddfbeSMarcel Moolenaar rc = xo_do_close_list(xop, NULL); 6767545ddfbeSMarcel Moolenaar break; 6768545ddfbeSMarcel Moolenaar 6769545ddfbeSMarcel Moolenaar case XSS_OPEN_INSTANCE: 6770545ddfbeSMarcel Moolenaar rc = xo_do_close_instance(xop, NULL); 6771545ddfbeSMarcel Moolenaar break; 6772545ddfbeSMarcel Moolenaar 6773545ddfbeSMarcel Moolenaar case XSS_OPEN_LEAF_LIST: 6774545ddfbeSMarcel Moolenaar rc = xo_do_close_leaf_list(xop, NULL); 6775545ddfbeSMarcel Moolenaar break; 6776545ddfbeSMarcel Moolenaar 6777545ddfbeSMarcel Moolenaar case XSS_MARKER: 6778545ddfbeSMarcel Moolenaar flags = xsp->xs_flags & XSF_MARKER_FLAGS; 6779545ddfbeSMarcel Moolenaar xo_depth_change(xop, xsp->xs_name, -1, 0, XSS_MARKER, 0); 6780545ddfbeSMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= flags; 6781545ddfbeSMarcel Moolenaar rc = 0; 6782545ddfbeSMarcel Moolenaar break; 6783545ddfbeSMarcel Moolenaar } 6784545ddfbeSMarcel Moolenaar 6785545ddfbeSMarcel Moolenaar if (rc < 0) 6786545ddfbeSMarcel Moolenaar xo_failure(xop, "close %d failed: %d", xsp->xs_state, rc); 6787545ddfbeSMarcel Moolenaar } 6788545ddfbeSMarcel Moolenaar 6789545ddfbeSMarcel Moolenaar return 0; 6790545ddfbeSMarcel Moolenaar } 6791545ddfbeSMarcel Moolenaar 6792545ddfbeSMarcel Moolenaar /* 6793545ddfbeSMarcel Moolenaar * This function is responsible for clearing out whatever is needed 6794545ddfbeSMarcel Moolenaar * to get to the desired state, if possible. 6795545ddfbeSMarcel Moolenaar */ 6796545ddfbeSMarcel Moolenaar static int 6797545ddfbeSMarcel Moolenaar xo_do_close (xo_handle_t *xop, const char *name, xo_state_t new_state) 6798545ddfbeSMarcel Moolenaar { 6799545ddfbeSMarcel Moolenaar xo_stack_t *xsp, *limit = NULL; 6800545ddfbeSMarcel Moolenaar int rc; 6801545ddfbeSMarcel Moolenaar xo_state_t need_state = new_state; 6802545ddfbeSMarcel Moolenaar 6803545ddfbeSMarcel Moolenaar if (new_state == XSS_CLOSE_CONTAINER) 6804545ddfbeSMarcel Moolenaar need_state = XSS_OPEN_CONTAINER; 6805545ddfbeSMarcel Moolenaar else if (new_state == XSS_CLOSE_LIST) 6806545ddfbeSMarcel Moolenaar need_state = XSS_OPEN_LIST; 6807545ddfbeSMarcel Moolenaar else if (new_state == XSS_CLOSE_INSTANCE) 6808545ddfbeSMarcel Moolenaar need_state = XSS_OPEN_INSTANCE; 6809545ddfbeSMarcel Moolenaar else if (new_state == XSS_CLOSE_LEAF_LIST) 6810545ddfbeSMarcel Moolenaar need_state = XSS_OPEN_LEAF_LIST; 6811545ddfbeSMarcel Moolenaar else if (new_state == XSS_MARKER) 6812545ddfbeSMarcel Moolenaar need_state = XSS_MARKER; 6813545ddfbeSMarcel Moolenaar else 6814545ddfbeSMarcel Moolenaar return 0; /* Unknown or useless new states are ignored */ 6815545ddfbeSMarcel Moolenaar 6816545ddfbeSMarcel Moolenaar for (xsp = &xop->xo_stack[xop->xo_depth]; xsp > xop->xo_stack; xsp--) { 6817545ddfbeSMarcel Moolenaar /* 6818545ddfbeSMarcel Moolenaar * Marker's normally stop us from going any further, unless 6819545ddfbeSMarcel Moolenaar * we are popping a marker (new_state == XSS_MARKER). 6820545ddfbeSMarcel Moolenaar */ 6821545ddfbeSMarcel Moolenaar if (xsp->xs_state == XSS_MARKER && need_state != XSS_MARKER) { 6822545ddfbeSMarcel Moolenaar if (name) { 6823545ddfbeSMarcel Moolenaar xo_failure(xop, "close (xo_%s) fails at marker '%s'; " 6824545ddfbeSMarcel Moolenaar "not found '%s'", 6825545ddfbeSMarcel Moolenaar xo_state_name(new_state), 6826545ddfbeSMarcel Moolenaar xsp->xs_name, name); 6827545ddfbeSMarcel Moolenaar return 0; 6828545ddfbeSMarcel Moolenaar 6829545ddfbeSMarcel Moolenaar } else { 6830545ddfbeSMarcel Moolenaar limit = xsp; 6831545ddfbeSMarcel Moolenaar xo_failure(xop, "close stops at marker '%s'", xsp->xs_name); 6832545ddfbeSMarcel Moolenaar } 6833545ddfbeSMarcel Moolenaar break; 6834545ddfbeSMarcel Moolenaar } 6835545ddfbeSMarcel Moolenaar 6836545ddfbeSMarcel Moolenaar if (xsp->xs_state != need_state) 6837545ddfbeSMarcel Moolenaar continue; 6838545ddfbeSMarcel Moolenaar 6839545ddfbeSMarcel Moolenaar if (name && xsp->xs_name && strcmp(name, xsp->xs_name) != 0) 6840545ddfbeSMarcel Moolenaar continue; 6841545ddfbeSMarcel Moolenaar 6842545ddfbeSMarcel Moolenaar limit = xsp; 6843545ddfbeSMarcel Moolenaar break; 6844545ddfbeSMarcel Moolenaar } 6845545ddfbeSMarcel Moolenaar 6846545ddfbeSMarcel Moolenaar if (limit == NULL) { 6847545ddfbeSMarcel Moolenaar xo_failure(xop, "xo_%s can't find match for '%s'", 6848545ddfbeSMarcel Moolenaar xo_state_name(new_state), name); 6849545ddfbeSMarcel Moolenaar return 0; 6850545ddfbeSMarcel Moolenaar } 6851545ddfbeSMarcel Moolenaar 6852545ddfbeSMarcel Moolenaar rc = xo_do_close_all(xop, limit); 6853545ddfbeSMarcel Moolenaar 6854545ddfbeSMarcel Moolenaar return rc; 6855545ddfbeSMarcel Moolenaar } 6856545ddfbeSMarcel Moolenaar 6857545ddfbeSMarcel Moolenaar /* 6858545ddfbeSMarcel Moolenaar * We are in a given state and need to transition to the new state. 6859545ddfbeSMarcel Moolenaar */ 6860545ddfbeSMarcel Moolenaar static int 6861545ddfbeSMarcel Moolenaar xo_transition (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name, 6862545ddfbeSMarcel Moolenaar xo_state_t new_state) 6863545ddfbeSMarcel Moolenaar { 6864545ddfbeSMarcel Moolenaar xo_stack_t *xsp; 6865545ddfbeSMarcel Moolenaar int rc; 6866545ddfbeSMarcel Moolenaar int old_state, on_marker; 6867545ddfbeSMarcel Moolenaar 6868545ddfbeSMarcel Moolenaar xop = xo_default(xop); 6869545ddfbeSMarcel Moolenaar 6870545ddfbeSMarcel Moolenaar rc = 0; 6871545ddfbeSMarcel Moolenaar xsp = &xop->xo_stack[xop->xo_depth]; 6872545ddfbeSMarcel Moolenaar old_state = xsp->xs_state; 6873545ddfbeSMarcel Moolenaar on_marker = (old_state == XSS_MARKER); 6874545ddfbeSMarcel Moolenaar 6875545ddfbeSMarcel Moolenaar /* If there's a marker on top of the stack, we need to find a real state */ 6876545ddfbeSMarcel Moolenaar while (old_state == XSS_MARKER) { 6877545ddfbeSMarcel Moolenaar if (xsp == xop->xo_stack) 6878545ddfbeSMarcel Moolenaar break; 6879545ddfbeSMarcel Moolenaar xsp -= 1; 6880545ddfbeSMarcel Moolenaar old_state = xsp->xs_state; 6881545ddfbeSMarcel Moolenaar } 6882545ddfbeSMarcel Moolenaar 6883545ddfbeSMarcel Moolenaar /* 6884545ddfbeSMarcel Moolenaar * At this point, the list of possible states are: 6885545ddfbeSMarcel Moolenaar * XSS_INIT, XSS_OPEN_CONTAINER, XSS_OPEN_LIST, 6886545ddfbeSMarcel Moolenaar * XSS_OPEN_INSTANCE, XSS_OPEN_LEAF_LIST, XSS_DISCARDING 6887545ddfbeSMarcel Moolenaar */ 6888545ddfbeSMarcel Moolenaar switch (XSS_TRANSITION(old_state, new_state)) { 6889545ddfbeSMarcel Moolenaar 6890545ddfbeSMarcel Moolenaar open_container: 6891545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_INIT, XSS_OPEN_CONTAINER): 6892545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_OPEN_CONTAINER): 6893545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_OPEN_CONTAINER): 6894545ddfbeSMarcel Moolenaar rc = xo_do_open_container(xop, flags, name); 6895545ddfbeSMarcel Moolenaar break; 6896545ddfbeSMarcel Moolenaar 6897545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LIST, XSS_OPEN_CONTAINER): 6898545ddfbeSMarcel Moolenaar if (on_marker) 6899545ddfbeSMarcel Moolenaar goto marker_prevents_close; 6900545ddfbeSMarcel Moolenaar rc = xo_do_close_list(xop, NULL); 6901545ddfbeSMarcel Moolenaar if (rc >= 0) 6902545ddfbeSMarcel Moolenaar goto open_container; 6903545ddfbeSMarcel Moolenaar break; 6904545ddfbeSMarcel Moolenaar 6905545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_OPEN_CONTAINER): 6906545ddfbeSMarcel Moolenaar if (on_marker) 6907545ddfbeSMarcel Moolenaar goto marker_prevents_close; 6908545ddfbeSMarcel Moolenaar rc = xo_do_close_leaf_list(xop, NULL); 6909545ddfbeSMarcel Moolenaar if (rc >= 0) 6910545ddfbeSMarcel Moolenaar goto open_container; 6911545ddfbeSMarcel Moolenaar break; 6912545ddfbeSMarcel Moolenaar 6913545ddfbeSMarcel Moolenaar /*close_container:*/ 6914545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_CLOSE_CONTAINER): 6915545ddfbeSMarcel Moolenaar if (on_marker) 6916545ddfbeSMarcel Moolenaar goto marker_prevents_close; 6917545ddfbeSMarcel Moolenaar rc = xo_do_close(xop, name, new_state); 6918545ddfbeSMarcel Moolenaar break; 6919545ddfbeSMarcel Moolenaar 6920545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_INIT, XSS_CLOSE_CONTAINER): 6921545ddfbeSMarcel Moolenaar /* This is an exception for "xo --close" */ 6922545ddfbeSMarcel Moolenaar rc = xo_do_close_container(xop, name); 6923545ddfbeSMarcel Moolenaar break; 6924545ddfbeSMarcel Moolenaar 6925545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LIST, XSS_CLOSE_CONTAINER): 6926545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_CLOSE_CONTAINER): 6927545ddfbeSMarcel Moolenaar if (on_marker) 6928545ddfbeSMarcel Moolenaar goto marker_prevents_close; 6929545ddfbeSMarcel Moolenaar rc = xo_do_close(xop, name, new_state); 6930545ddfbeSMarcel Moolenaar break; 6931545ddfbeSMarcel Moolenaar 6932545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_CLOSE_CONTAINER): 6933545ddfbeSMarcel Moolenaar if (on_marker) 6934545ddfbeSMarcel Moolenaar goto marker_prevents_close; 6935545ddfbeSMarcel Moolenaar rc = xo_do_close_leaf_list(xop, NULL); 6936545ddfbeSMarcel Moolenaar if (rc >= 0) 6937545ddfbeSMarcel Moolenaar rc = xo_do_close(xop, name, new_state); 6938545ddfbeSMarcel Moolenaar break; 6939545ddfbeSMarcel Moolenaar 6940545ddfbeSMarcel Moolenaar open_list: 6941545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_INIT, XSS_OPEN_LIST): 6942545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_OPEN_LIST): 6943545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_OPEN_LIST): 6944545ddfbeSMarcel Moolenaar rc = xo_do_open_list(xop, flags, name); 6945545ddfbeSMarcel Moolenaar break; 6946545ddfbeSMarcel Moolenaar 6947545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LIST, XSS_OPEN_LIST): 6948545ddfbeSMarcel Moolenaar if (on_marker) 6949545ddfbeSMarcel Moolenaar goto marker_prevents_close; 6950545ddfbeSMarcel Moolenaar rc = xo_do_close_list(xop, NULL); 6951545ddfbeSMarcel Moolenaar if (rc >= 0) 6952545ddfbeSMarcel Moolenaar goto open_list; 6953545ddfbeSMarcel Moolenaar break; 6954545ddfbeSMarcel Moolenaar 6955545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_OPEN_LIST): 6956545ddfbeSMarcel Moolenaar if (on_marker) 6957545ddfbeSMarcel Moolenaar goto marker_prevents_close; 6958545ddfbeSMarcel Moolenaar rc = xo_do_close_leaf_list(xop, NULL); 6959545ddfbeSMarcel Moolenaar if (rc >= 0) 6960545ddfbeSMarcel Moolenaar goto open_list; 6961545ddfbeSMarcel Moolenaar break; 6962545ddfbeSMarcel Moolenaar 6963545ddfbeSMarcel Moolenaar /*close_list:*/ 6964545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LIST, XSS_CLOSE_LIST): 6965545ddfbeSMarcel Moolenaar if (on_marker) 6966545ddfbeSMarcel Moolenaar goto marker_prevents_close; 6967545ddfbeSMarcel Moolenaar rc = xo_do_close(xop, name, new_state); 6968545ddfbeSMarcel Moolenaar break; 6969545ddfbeSMarcel Moolenaar 6970545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_INIT, XSS_CLOSE_LIST): 6971545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_CLOSE_LIST): 6972545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_CLOSE_LIST): 6973545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_CLOSE_LIST): 6974545ddfbeSMarcel Moolenaar rc = xo_do_close(xop, name, new_state); 6975545ddfbeSMarcel Moolenaar break; 6976545ddfbeSMarcel Moolenaar 6977545ddfbeSMarcel Moolenaar open_instance: 6978545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LIST, XSS_OPEN_INSTANCE): 6979545ddfbeSMarcel Moolenaar rc = xo_do_open_instance(xop, flags, name); 6980545ddfbeSMarcel Moolenaar break; 6981545ddfbeSMarcel Moolenaar 6982545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_INIT, XSS_OPEN_INSTANCE): 6983788ca347SMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_OPEN_INSTANCE): 6984545ddfbeSMarcel Moolenaar rc = xo_do_open_list(xop, flags, name); 6985545ddfbeSMarcel Moolenaar if (rc >= 0) 6986545ddfbeSMarcel Moolenaar goto open_instance; 6987545ddfbeSMarcel Moolenaar break; 6988545ddfbeSMarcel Moolenaar 6989545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_OPEN_INSTANCE): 6990545ddfbeSMarcel Moolenaar if (on_marker) { 6991545ddfbeSMarcel Moolenaar rc = xo_do_open_list(xop, flags, name); 6992545ddfbeSMarcel Moolenaar } else { 6993545ddfbeSMarcel Moolenaar rc = xo_do_close_instance(xop, NULL); 6994545ddfbeSMarcel Moolenaar } 6995545ddfbeSMarcel Moolenaar if (rc >= 0) 6996545ddfbeSMarcel Moolenaar goto open_instance; 6997545ddfbeSMarcel Moolenaar break; 6998545ddfbeSMarcel Moolenaar 6999545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_OPEN_INSTANCE): 7000545ddfbeSMarcel Moolenaar if (on_marker) 7001545ddfbeSMarcel Moolenaar goto marker_prevents_close; 7002545ddfbeSMarcel Moolenaar rc = xo_do_close_leaf_list(xop, NULL); 7003545ddfbeSMarcel Moolenaar if (rc >= 0) 7004545ddfbeSMarcel Moolenaar goto open_instance; 7005545ddfbeSMarcel Moolenaar break; 7006545ddfbeSMarcel Moolenaar 7007545ddfbeSMarcel Moolenaar /*close_instance:*/ 7008545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_CLOSE_INSTANCE): 7009545ddfbeSMarcel Moolenaar if (on_marker) 7010545ddfbeSMarcel Moolenaar goto marker_prevents_close; 7011545ddfbeSMarcel Moolenaar rc = xo_do_close_instance(xop, name); 7012545ddfbeSMarcel Moolenaar break; 7013545ddfbeSMarcel Moolenaar 7014545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_INIT, XSS_CLOSE_INSTANCE): 7015545ddfbeSMarcel Moolenaar /* This one makes no sense; ignore it */ 7016788ca347SMarcel Moolenaar xo_failure(xop, "xo_close_instance ignored when called from " 7017788ca347SMarcel Moolenaar "initial state ('%s')", name ?: "(unknown)"); 7018545ddfbeSMarcel Moolenaar break; 7019545ddfbeSMarcel Moolenaar 7020545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_CLOSE_INSTANCE): 7021545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LIST, XSS_CLOSE_INSTANCE): 7022545ddfbeSMarcel Moolenaar if (on_marker) 7023545ddfbeSMarcel Moolenaar goto marker_prevents_close; 7024545ddfbeSMarcel Moolenaar rc = xo_do_close(xop, name, new_state); 7025545ddfbeSMarcel Moolenaar break; 7026545ddfbeSMarcel Moolenaar 7027545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_CLOSE_INSTANCE): 7028545ddfbeSMarcel Moolenaar if (on_marker) 7029545ddfbeSMarcel Moolenaar goto marker_prevents_close; 7030545ddfbeSMarcel Moolenaar rc = xo_do_close_leaf_list(xop, NULL); 7031545ddfbeSMarcel Moolenaar if (rc >= 0) 7032545ddfbeSMarcel Moolenaar rc = xo_do_close(xop, name, new_state); 7033545ddfbeSMarcel Moolenaar break; 7034545ddfbeSMarcel Moolenaar 7035545ddfbeSMarcel Moolenaar open_leaf_list: 7036545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_OPEN_LEAF_LIST): 7037545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_OPEN_LEAF_LIST): 7038545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_INIT, XSS_OPEN_LEAF_LIST): 7039545ddfbeSMarcel Moolenaar rc = xo_do_open_leaf_list(xop, flags, name); 7040545ddfbeSMarcel Moolenaar break; 7041545ddfbeSMarcel Moolenaar 7042545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LIST, XSS_OPEN_LEAF_LIST): 7043545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_OPEN_LEAF_LIST): 7044545ddfbeSMarcel Moolenaar if (on_marker) 7045545ddfbeSMarcel Moolenaar goto marker_prevents_close; 7046545ddfbeSMarcel Moolenaar rc = xo_do_close_list(xop, NULL); 7047545ddfbeSMarcel Moolenaar if (rc >= 0) 7048545ddfbeSMarcel Moolenaar goto open_leaf_list; 7049545ddfbeSMarcel Moolenaar break; 7050545ddfbeSMarcel Moolenaar 7051545ddfbeSMarcel Moolenaar /*close_leaf_list:*/ 7052545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_CLOSE_LEAF_LIST): 7053545ddfbeSMarcel Moolenaar if (on_marker) 7054545ddfbeSMarcel Moolenaar goto marker_prevents_close; 7055545ddfbeSMarcel Moolenaar rc = xo_do_close_leaf_list(xop, name); 7056545ddfbeSMarcel Moolenaar break; 7057545ddfbeSMarcel Moolenaar 7058545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_INIT, XSS_CLOSE_LEAF_LIST): 7059545ddfbeSMarcel Moolenaar /* Makes no sense; ignore */ 7060788ca347SMarcel Moolenaar xo_failure(xop, "xo_close_leaf_list ignored when called from " 7061788ca347SMarcel Moolenaar "initial state ('%s')", name ?: "(unknown)"); 7062545ddfbeSMarcel Moolenaar break; 7063545ddfbeSMarcel Moolenaar 7064545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_CLOSE_LEAF_LIST): 7065545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LIST, XSS_CLOSE_LEAF_LIST): 7066545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_CLOSE_LEAF_LIST): 7067545ddfbeSMarcel Moolenaar if (on_marker) 7068545ddfbeSMarcel Moolenaar goto marker_prevents_close; 7069545ddfbeSMarcel Moolenaar rc = xo_do_close(xop, name, new_state); 7070545ddfbeSMarcel Moolenaar break; 7071545ddfbeSMarcel Moolenaar 7072545ddfbeSMarcel Moolenaar /*emit:*/ 7073545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_EMIT): 7074545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_EMIT): 7075545ddfbeSMarcel Moolenaar break; 7076545ddfbeSMarcel Moolenaar 7077545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LIST, XSS_EMIT): 7078545ddfbeSMarcel Moolenaar if (on_marker) 7079545ddfbeSMarcel Moolenaar goto marker_prevents_close; 7080545ddfbeSMarcel Moolenaar rc = xo_do_close(xop, NULL, XSS_CLOSE_LIST); 7081545ddfbeSMarcel Moolenaar break; 7082545ddfbeSMarcel Moolenaar 7083545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_INIT, XSS_EMIT): 7084545ddfbeSMarcel Moolenaar break; 7085545ddfbeSMarcel Moolenaar 7086545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_EMIT): 7087545ddfbeSMarcel Moolenaar if (on_marker) 7088545ddfbeSMarcel Moolenaar goto marker_prevents_close; 7089545ddfbeSMarcel Moolenaar rc = xo_do_close_leaf_list(xop, NULL); 7090545ddfbeSMarcel Moolenaar break; 7091545ddfbeSMarcel Moolenaar 7092545ddfbeSMarcel Moolenaar /*emit_leaf_list:*/ 7093545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_INIT, XSS_EMIT_LEAF_LIST): 7094545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_EMIT_LEAF_LIST): 7095545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_EMIT_LEAF_LIST): 7096545ddfbeSMarcel Moolenaar rc = xo_do_open_leaf_list(xop, flags, name); 7097545ddfbeSMarcel Moolenaar break; 7098545ddfbeSMarcel Moolenaar 7099545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_EMIT_LEAF_LIST): 7100545ddfbeSMarcel Moolenaar break; 7101545ddfbeSMarcel Moolenaar 7102545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LIST, XSS_EMIT_LEAF_LIST): 7103545ddfbeSMarcel Moolenaar /* 7104545ddfbeSMarcel Moolenaar * We need to be backward compatible with the pre-xo_open_leaf_list 7105545ddfbeSMarcel Moolenaar * API, where both lists and leaf-lists were opened as lists. So 7106545ddfbeSMarcel Moolenaar * if we find an open list that hasn't had anything written to it, 7107545ddfbeSMarcel Moolenaar * we'll accept it. 7108545ddfbeSMarcel Moolenaar */ 7109545ddfbeSMarcel Moolenaar break; 7110545ddfbeSMarcel Moolenaar 7111545ddfbeSMarcel Moolenaar default: 7112545ddfbeSMarcel Moolenaar xo_failure(xop, "unknown transition: (%u -> %u)", 7113545ddfbeSMarcel Moolenaar xsp->xs_state, new_state); 7114545ddfbeSMarcel Moolenaar } 7115545ddfbeSMarcel Moolenaar 7116545ddfbeSMarcel Moolenaar return rc; 7117545ddfbeSMarcel Moolenaar 7118545ddfbeSMarcel Moolenaar marker_prevents_close: 7119545ddfbeSMarcel Moolenaar xo_failure(xop, "marker '%s' prevents transition from %s to %s", 7120545ddfbeSMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_name, 7121545ddfbeSMarcel Moolenaar xo_state_name(old_state), xo_state_name(new_state)); 7122545ddfbeSMarcel Moolenaar return -1; 7123545ddfbeSMarcel Moolenaar } 7124545ddfbeSMarcel Moolenaar 7125545ddfbeSMarcel Moolenaar int 7126545ddfbeSMarcel Moolenaar xo_open_marker_h (xo_handle_t *xop, const char *name) 7127545ddfbeSMarcel Moolenaar { 7128545ddfbeSMarcel Moolenaar xop = xo_default(xop); 7129545ddfbeSMarcel Moolenaar 7130545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, 1, 0, XSS_MARKER, 7131545ddfbeSMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags & XSF_MARKER_FLAGS); 7132545ddfbeSMarcel Moolenaar 7133545ddfbeSMarcel Moolenaar return 0; 7134545ddfbeSMarcel Moolenaar } 7135545ddfbeSMarcel Moolenaar 7136545ddfbeSMarcel Moolenaar int 7137545ddfbeSMarcel Moolenaar xo_open_marker (const char *name) 7138545ddfbeSMarcel Moolenaar { 7139545ddfbeSMarcel Moolenaar return xo_open_marker_h(NULL, name); 7140545ddfbeSMarcel Moolenaar } 7141545ddfbeSMarcel Moolenaar 7142545ddfbeSMarcel Moolenaar int 7143545ddfbeSMarcel Moolenaar xo_close_marker_h (xo_handle_t *xop, const char *name) 7144545ddfbeSMarcel Moolenaar { 7145545ddfbeSMarcel Moolenaar xop = xo_default(xop); 7146545ddfbeSMarcel Moolenaar 7147545ddfbeSMarcel Moolenaar return xo_do_close(xop, name, XSS_MARKER); 7148545ddfbeSMarcel Moolenaar } 7149545ddfbeSMarcel Moolenaar 7150545ddfbeSMarcel Moolenaar int 7151545ddfbeSMarcel Moolenaar xo_close_marker (const char *name) 7152545ddfbeSMarcel Moolenaar { 7153545ddfbeSMarcel Moolenaar return xo_close_marker_h(NULL, name); 7154545ddfbeSMarcel Moolenaar } 7155545ddfbeSMarcel Moolenaar 7156d1a0d267SMarcel Moolenaar /* 7157d1a0d267SMarcel Moolenaar * Record custom output functions into the xo handle, allowing 7158d1a0d267SMarcel Moolenaar * integration with a variety of output frameworks. 7159d1a0d267SMarcel Moolenaar */ 716031337658SMarcel Moolenaar void 716131337658SMarcel Moolenaar xo_set_writer (xo_handle_t *xop, void *opaque, xo_write_func_t write_func, 7162545ddfbeSMarcel Moolenaar xo_close_func_t close_func, xo_flush_func_t flush_func) 716331337658SMarcel Moolenaar { 716431337658SMarcel Moolenaar xop = xo_default(xop); 716531337658SMarcel Moolenaar 716631337658SMarcel Moolenaar xop->xo_opaque = opaque; 716731337658SMarcel Moolenaar xop->xo_write = write_func; 716831337658SMarcel Moolenaar xop->xo_close = close_func; 7169545ddfbeSMarcel Moolenaar xop->xo_flush = flush_func; 717031337658SMarcel Moolenaar } 717131337658SMarcel Moolenaar 717231337658SMarcel Moolenaar void 717331337658SMarcel Moolenaar xo_set_allocator (xo_realloc_func_t realloc_func, xo_free_func_t free_func) 717431337658SMarcel Moolenaar { 717531337658SMarcel Moolenaar xo_realloc = realloc_func; 717631337658SMarcel Moolenaar xo_free = free_func; 717731337658SMarcel Moolenaar } 717831337658SMarcel Moolenaar 7179545ddfbeSMarcel Moolenaar int 718031337658SMarcel Moolenaar xo_flush_h (xo_handle_t *xop) 718131337658SMarcel Moolenaar { 718231337658SMarcel Moolenaar static char div_close[] = "</div>"; 7183545ddfbeSMarcel Moolenaar int rc; 718431337658SMarcel Moolenaar 718531337658SMarcel Moolenaar xop = xo_default(xop); 718631337658SMarcel Moolenaar 7187788ca347SMarcel Moolenaar switch (xo_style(xop)) { 718831337658SMarcel Moolenaar case XO_STYLE_HTML: 7189d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_DIV_OPEN)) { 7190d1a0d267SMarcel Moolenaar XOIF_CLEAR(xop, XOIF_DIV_OPEN); 719131337658SMarcel Moolenaar xo_data_append(xop, div_close, sizeof(div_close) - 1); 719231337658SMarcel Moolenaar 7193d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_PRETTY)) 719431337658SMarcel Moolenaar xo_data_append(xop, "\n", 1); 719531337658SMarcel Moolenaar } 719631337658SMarcel Moolenaar break; 7197d1a0d267SMarcel Moolenaar 7198d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER: 7199d1a0d267SMarcel Moolenaar xo_encoder_handle(xop, XO_OP_FLUSH, NULL, NULL); 720031337658SMarcel Moolenaar } 720131337658SMarcel Moolenaar 7202545ddfbeSMarcel Moolenaar rc = xo_write(xop); 7203545ddfbeSMarcel Moolenaar if (rc >= 0 && xop->xo_flush) 7204545ddfbeSMarcel Moolenaar if (xop->xo_flush(xop->xo_opaque) < 0) 7205545ddfbeSMarcel Moolenaar return -1; 7206545ddfbeSMarcel Moolenaar 7207545ddfbeSMarcel Moolenaar return rc; 720831337658SMarcel Moolenaar } 720931337658SMarcel Moolenaar 7210545ddfbeSMarcel Moolenaar int 721131337658SMarcel Moolenaar xo_flush (void) 721231337658SMarcel Moolenaar { 7213545ddfbeSMarcel Moolenaar return xo_flush_h(NULL); 721431337658SMarcel Moolenaar } 721531337658SMarcel Moolenaar 7216545ddfbeSMarcel Moolenaar int 721731337658SMarcel Moolenaar xo_finish_h (xo_handle_t *xop) 721831337658SMarcel Moolenaar { 721931337658SMarcel Moolenaar const char *cp = ""; 722031337658SMarcel Moolenaar xop = xo_default(xop); 722131337658SMarcel Moolenaar 7222d1a0d267SMarcel Moolenaar if (!XOF_ISSET(xop, XOF_NO_CLOSE)) 7223545ddfbeSMarcel Moolenaar xo_do_close_all(xop, xop->xo_stack); 7224545ddfbeSMarcel Moolenaar 7225788ca347SMarcel Moolenaar switch (xo_style(xop)) { 722631337658SMarcel Moolenaar case XO_STYLE_JSON: 7227d1a0d267SMarcel Moolenaar if (!XOF_ISSET(xop, XOF_NO_TOP)) { 7228d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_TOP_EMITTED)) 7229d1a0d267SMarcel Moolenaar XOIF_CLEAR(xop, XOIF_TOP_EMITTED); /* Turn off before output */ 723031337658SMarcel Moolenaar else 723131337658SMarcel Moolenaar cp = "{ "; 723231337658SMarcel Moolenaar xo_printf(xop, "%*s%s}\n",xo_indent(xop), "", cp); 723331337658SMarcel Moolenaar } 723431337658SMarcel Moolenaar break; 7235d1a0d267SMarcel Moolenaar 7236d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER: 7237d1a0d267SMarcel Moolenaar xo_encoder_handle(xop, XO_OP_FINISH, NULL, NULL); 7238d1a0d267SMarcel Moolenaar break; 723931337658SMarcel Moolenaar } 724031337658SMarcel Moolenaar 7241545ddfbeSMarcel Moolenaar return xo_flush_h(xop); 724231337658SMarcel Moolenaar } 724331337658SMarcel Moolenaar 7244545ddfbeSMarcel Moolenaar int 724531337658SMarcel Moolenaar xo_finish (void) 724631337658SMarcel Moolenaar { 7247545ddfbeSMarcel Moolenaar return xo_finish_h(NULL); 724831337658SMarcel Moolenaar } 724931337658SMarcel Moolenaar 725031337658SMarcel Moolenaar /* 7251d1a0d267SMarcel Moolenaar * xo_finish_atexit is suitable for atexit() calls, to force clear up 7252d1a0d267SMarcel Moolenaar * and finalizing output. 7253d1a0d267SMarcel Moolenaar */ 7254d1a0d267SMarcel Moolenaar void 7255d1a0d267SMarcel Moolenaar xo_finish_atexit (void) 7256d1a0d267SMarcel Moolenaar { 7257d1a0d267SMarcel Moolenaar (void) xo_finish_h(NULL); 7258d1a0d267SMarcel Moolenaar } 7259d1a0d267SMarcel Moolenaar 7260d1a0d267SMarcel Moolenaar /* 726131337658SMarcel Moolenaar * Generate an error message, such as would be displayed on stderr 726231337658SMarcel Moolenaar */ 726331337658SMarcel Moolenaar void 726431337658SMarcel Moolenaar xo_error_hv (xo_handle_t *xop, const char *fmt, va_list vap) 726531337658SMarcel Moolenaar { 726631337658SMarcel Moolenaar xop = xo_default(xop); 726731337658SMarcel Moolenaar 726831337658SMarcel Moolenaar /* 726931337658SMarcel Moolenaar * If the format string doesn't end with a newline, we pop 727031337658SMarcel Moolenaar * one on ourselves. 727131337658SMarcel Moolenaar */ 727231337658SMarcel Moolenaar int len = strlen(fmt); 727331337658SMarcel Moolenaar if (len > 0 && fmt[len - 1] != '\n') { 727431337658SMarcel Moolenaar char *newfmt = alloca(len + 2); 727531337658SMarcel Moolenaar memcpy(newfmt, fmt, len); 727631337658SMarcel Moolenaar newfmt[len] = '\n'; 727731337658SMarcel Moolenaar newfmt[len] = '\0'; 727831337658SMarcel Moolenaar fmt = newfmt; 727931337658SMarcel Moolenaar } 728031337658SMarcel Moolenaar 7281788ca347SMarcel Moolenaar switch (xo_style(xop)) { 728231337658SMarcel Moolenaar case XO_STYLE_TEXT: 728331337658SMarcel Moolenaar vfprintf(stderr, fmt, vap); 728431337658SMarcel Moolenaar break; 728531337658SMarcel Moolenaar 728631337658SMarcel Moolenaar case XO_STYLE_HTML: 728731337658SMarcel Moolenaar va_copy(xop->xo_vap, vap); 728831337658SMarcel Moolenaar 728931337658SMarcel Moolenaar xo_buf_append_div(xop, "error", 0, NULL, 0, fmt, strlen(fmt), NULL, 0); 729031337658SMarcel Moolenaar 7291d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_DIV_OPEN)) 729231337658SMarcel Moolenaar xo_line_close(xop); 729331337658SMarcel Moolenaar 729431337658SMarcel Moolenaar xo_write(xop); 729531337658SMarcel Moolenaar 729631337658SMarcel Moolenaar va_end(xop->xo_vap); 729731337658SMarcel Moolenaar bzero(&xop->xo_vap, sizeof(xop->xo_vap)); 729831337658SMarcel Moolenaar break; 729931337658SMarcel Moolenaar 730031337658SMarcel Moolenaar case XO_STYLE_XML: 7301545ddfbeSMarcel Moolenaar case XO_STYLE_JSON: 730231337658SMarcel Moolenaar va_copy(xop->xo_vap, vap); 730331337658SMarcel Moolenaar 730431337658SMarcel Moolenaar xo_open_container_h(xop, "error"); 730531337658SMarcel Moolenaar xo_format_value(xop, "message", 7, fmt, strlen(fmt), NULL, 0, 0); 730631337658SMarcel Moolenaar xo_close_container_h(xop, "error"); 730731337658SMarcel Moolenaar 730831337658SMarcel Moolenaar va_end(xop->xo_vap); 730931337658SMarcel Moolenaar bzero(&xop->xo_vap, sizeof(xop->xo_vap)); 731031337658SMarcel Moolenaar break; 7311d1a0d267SMarcel Moolenaar 7312d1a0d267SMarcel Moolenaar case XO_STYLE_SDPARAMS: 7313d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER: 7314d1a0d267SMarcel Moolenaar break; 731531337658SMarcel Moolenaar } 731631337658SMarcel Moolenaar } 731731337658SMarcel Moolenaar 731831337658SMarcel Moolenaar void 731931337658SMarcel Moolenaar xo_error_h (xo_handle_t *xop, const char *fmt, ...) 732031337658SMarcel Moolenaar { 732131337658SMarcel Moolenaar va_list vap; 732231337658SMarcel Moolenaar 732331337658SMarcel Moolenaar va_start(vap, fmt); 732431337658SMarcel Moolenaar xo_error_hv(xop, fmt, vap); 732531337658SMarcel Moolenaar va_end(vap); 732631337658SMarcel Moolenaar } 732731337658SMarcel Moolenaar 732831337658SMarcel Moolenaar /* 732931337658SMarcel Moolenaar * Generate an error message, such as would be displayed on stderr 733031337658SMarcel Moolenaar */ 733131337658SMarcel Moolenaar void 733231337658SMarcel Moolenaar xo_error (const char *fmt, ...) 733331337658SMarcel Moolenaar { 733431337658SMarcel Moolenaar va_list vap; 733531337658SMarcel Moolenaar 733631337658SMarcel Moolenaar va_start(vap, fmt); 733731337658SMarcel Moolenaar xo_error_hv(NULL, fmt, vap); 733831337658SMarcel Moolenaar va_end(vap); 733931337658SMarcel Moolenaar } 734031337658SMarcel Moolenaar 7341d1a0d267SMarcel Moolenaar /* 7342d1a0d267SMarcel Moolenaar * Parse any libxo-specific options from the command line, removing them 7343d1a0d267SMarcel Moolenaar * so the main() argument parsing won't see them. We return the new value 7344d1a0d267SMarcel Moolenaar * for argc or -1 for error. If an error occurred, the program should 7345d1a0d267SMarcel Moolenaar * exit. A suitable error message has already been displayed. 7346d1a0d267SMarcel Moolenaar */ 734731337658SMarcel Moolenaar int 734831337658SMarcel Moolenaar xo_parse_args (int argc, char **argv) 734931337658SMarcel Moolenaar { 735031337658SMarcel Moolenaar static char libxo_opt[] = "--libxo"; 735131337658SMarcel Moolenaar char *cp; 735231337658SMarcel Moolenaar int i, save; 735331337658SMarcel Moolenaar 735431337658SMarcel Moolenaar /* Save our program name for xo_err and friends */ 735531337658SMarcel Moolenaar xo_program = argv[0]; 735631337658SMarcel Moolenaar cp = strrchr(xo_program, '/'); 735731337658SMarcel Moolenaar if (cp) 735831337658SMarcel Moolenaar xo_program = cp + 1; 735931337658SMarcel Moolenaar 736031337658SMarcel Moolenaar for (save = i = 1; i < argc; i++) { 736131337658SMarcel Moolenaar if (argv[i] == NULL 736231337658SMarcel Moolenaar || strncmp(argv[i], libxo_opt, sizeof(libxo_opt) - 1) != 0) { 736331337658SMarcel Moolenaar if (save != i) 736431337658SMarcel Moolenaar argv[save] = argv[i]; 736531337658SMarcel Moolenaar save += 1; 736631337658SMarcel Moolenaar continue; 736731337658SMarcel Moolenaar } 736831337658SMarcel Moolenaar 736931337658SMarcel Moolenaar cp = argv[i] + sizeof(libxo_opt) - 1; 737031337658SMarcel Moolenaar if (*cp == 0) { 737131337658SMarcel Moolenaar cp = argv[++i]; 737231337658SMarcel Moolenaar if (cp == 0) { 737331337658SMarcel Moolenaar xo_warnx("missing libxo option"); 737431337658SMarcel Moolenaar return -1; 737531337658SMarcel Moolenaar } 737631337658SMarcel Moolenaar 737731337658SMarcel Moolenaar if (xo_set_options(NULL, cp) < 0) 737831337658SMarcel Moolenaar return -1; 737931337658SMarcel Moolenaar } else if (*cp == ':') { 738031337658SMarcel Moolenaar if (xo_set_options(NULL, cp) < 0) 738131337658SMarcel Moolenaar return -1; 738231337658SMarcel Moolenaar 738331337658SMarcel Moolenaar } else if (*cp == '=') { 738431337658SMarcel Moolenaar if (xo_set_options(NULL, ++cp) < 0) 738531337658SMarcel Moolenaar return -1; 738631337658SMarcel Moolenaar 738731337658SMarcel Moolenaar } else if (*cp == '-') { 738831337658SMarcel Moolenaar cp += 1; 738931337658SMarcel Moolenaar if (strcmp(cp, "check") == 0) { 739031337658SMarcel Moolenaar exit(XO_HAS_LIBXO); 739131337658SMarcel Moolenaar 739231337658SMarcel Moolenaar } else { 739331337658SMarcel Moolenaar xo_warnx("unknown libxo option: '%s'", argv[i]); 739431337658SMarcel Moolenaar return -1; 739531337658SMarcel Moolenaar } 739631337658SMarcel Moolenaar } else { 739731337658SMarcel Moolenaar xo_warnx("unknown libxo option: '%s'", argv[i]); 739831337658SMarcel Moolenaar return -1; 739931337658SMarcel Moolenaar } 740031337658SMarcel Moolenaar } 740131337658SMarcel Moolenaar 740231337658SMarcel Moolenaar argv[save] = NULL; 740331337658SMarcel Moolenaar return save; 740431337658SMarcel Moolenaar } 740531337658SMarcel Moolenaar 7406d1a0d267SMarcel Moolenaar /* 7407d1a0d267SMarcel Moolenaar * Debugging function that dumps the current stack of open libxo constructs, 7408d1a0d267SMarcel Moolenaar * suitable for calling from the debugger. 7409d1a0d267SMarcel Moolenaar */ 7410545ddfbeSMarcel Moolenaar void 7411545ddfbeSMarcel Moolenaar xo_dump_stack (xo_handle_t *xop) 7412545ddfbeSMarcel Moolenaar { 7413545ddfbeSMarcel Moolenaar int i; 7414545ddfbeSMarcel Moolenaar xo_stack_t *xsp; 7415545ddfbeSMarcel Moolenaar 7416545ddfbeSMarcel Moolenaar xop = xo_default(xop); 7417545ddfbeSMarcel Moolenaar 7418545ddfbeSMarcel Moolenaar fprintf(stderr, "Stack dump:\n"); 7419545ddfbeSMarcel Moolenaar 7420545ddfbeSMarcel Moolenaar xsp = xop->xo_stack; 7421545ddfbeSMarcel Moolenaar for (i = 1, xsp++; i <= xop->xo_depth; i++, xsp++) { 7422545ddfbeSMarcel Moolenaar fprintf(stderr, " [%d] %s '%s' [%x]\n", 7423545ddfbeSMarcel Moolenaar i, xo_state_name(xsp->xs_state), 7424545ddfbeSMarcel Moolenaar xsp->xs_name ?: "--", xsp->xs_flags); 7425545ddfbeSMarcel Moolenaar } 7426545ddfbeSMarcel Moolenaar } 7427545ddfbeSMarcel Moolenaar 7428d1a0d267SMarcel Moolenaar /* 7429d1a0d267SMarcel Moolenaar * Record the program name used for error messages 7430d1a0d267SMarcel Moolenaar */ 7431545ddfbeSMarcel Moolenaar void 7432545ddfbeSMarcel Moolenaar xo_set_program (const char *name) 7433545ddfbeSMarcel Moolenaar { 7434545ddfbeSMarcel Moolenaar xo_program = name; 7435545ddfbeSMarcel Moolenaar } 7436545ddfbeSMarcel Moolenaar 7437788ca347SMarcel Moolenaar void 7438788ca347SMarcel Moolenaar xo_set_version_h (xo_handle_t *xop, const char *version UNUSED) 7439788ca347SMarcel Moolenaar { 7440788ca347SMarcel Moolenaar xop = xo_default(xop); 7441788ca347SMarcel Moolenaar 7442788ca347SMarcel Moolenaar if (version == NULL || strchr(version, '"') != NULL) 7443788ca347SMarcel Moolenaar return; 7444788ca347SMarcel Moolenaar 7445d1a0d267SMarcel Moolenaar if (!xo_style_is_encoding(xop)) 7446d1a0d267SMarcel Moolenaar return; 7447d1a0d267SMarcel Moolenaar 7448788ca347SMarcel Moolenaar switch (xo_style(xop)) { 7449788ca347SMarcel Moolenaar case XO_STYLE_XML: 7450788ca347SMarcel Moolenaar /* For XML, we record this as an attribute for the first tag */ 7451788ca347SMarcel Moolenaar xo_attr_h(xop, "__version", "%s", version); 7452788ca347SMarcel Moolenaar break; 7453788ca347SMarcel Moolenaar 7454788ca347SMarcel Moolenaar case XO_STYLE_JSON: 7455788ca347SMarcel Moolenaar /* 7456d1a0d267SMarcel Moolenaar * For JSON, we record the version string in our handle, and emit 7457788ca347SMarcel Moolenaar * it in xo_emit_top. 7458788ca347SMarcel Moolenaar */ 7459d1a0d267SMarcel Moolenaar xop->xo_version = xo_strndup(version, -1); 7460d1a0d267SMarcel Moolenaar break; 7461d1a0d267SMarcel Moolenaar 7462d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER: 7463d1a0d267SMarcel Moolenaar xo_encoder_handle(xop, XO_OP_VERSION, NULL, version); 7464788ca347SMarcel Moolenaar break; 7465788ca347SMarcel Moolenaar } 7466788ca347SMarcel Moolenaar } 7467788ca347SMarcel Moolenaar 7468d1a0d267SMarcel Moolenaar /* 7469d1a0d267SMarcel Moolenaar * Set the version number for the API content being carried thru 7470d1a0d267SMarcel Moolenaar * the xo handle. 7471d1a0d267SMarcel Moolenaar */ 7472788ca347SMarcel Moolenaar void 7473788ca347SMarcel Moolenaar xo_set_version (const char *version) 7474788ca347SMarcel Moolenaar { 7475788ca347SMarcel Moolenaar xo_set_version_h(NULL, version); 7476788ca347SMarcel Moolenaar } 7477788ca347SMarcel Moolenaar 7478d1a0d267SMarcel Moolenaar /* 7479d1a0d267SMarcel Moolenaar * Generate a warning. Normally, this is a text message written to 7480d1a0d267SMarcel Moolenaar * standard error. If the XOF_WARN_XML flag is set, then we generate 7481d1a0d267SMarcel Moolenaar * XMLified content on standard output. 7482d1a0d267SMarcel Moolenaar */ 7483d1a0d267SMarcel Moolenaar void 7484d1a0d267SMarcel Moolenaar xo_emit_warn_hcv (xo_handle_t *xop, int as_warning, int code, 7485d1a0d267SMarcel Moolenaar const char *fmt, va_list vap) 748631337658SMarcel Moolenaar { 7487d1a0d267SMarcel Moolenaar xop = xo_default(xop); 748831337658SMarcel Moolenaar 7489d1a0d267SMarcel Moolenaar if (fmt == NULL) 7490d1a0d267SMarcel Moolenaar return; 749131337658SMarcel Moolenaar 7492d1a0d267SMarcel Moolenaar xo_open_marker_h(xop, "xo_emit_warn_hcv"); 7493d1a0d267SMarcel Moolenaar xo_open_container_h(xop, as_warning ? "__warning" : "__error"); 749431337658SMarcel Moolenaar 7495d1a0d267SMarcel Moolenaar if (xo_program) 7496d1a0d267SMarcel Moolenaar xo_emit("{wc:program}", xo_program); 749731337658SMarcel Moolenaar 7498d1a0d267SMarcel Moolenaar if (xo_style(xop) == XO_STYLE_XML || xo_style(xop) == XO_STYLE_JSON) { 7499d1a0d267SMarcel Moolenaar va_list ap; 7500d1a0d267SMarcel Moolenaar xo_handle_t temp; 750131337658SMarcel Moolenaar 7502d1a0d267SMarcel Moolenaar bzero(&temp, sizeof(temp)); 7503d1a0d267SMarcel Moolenaar temp.xo_style = XO_STYLE_TEXT; 7504d1a0d267SMarcel Moolenaar xo_buf_init(&temp.xo_data); 7505d1a0d267SMarcel Moolenaar xo_depth_check(&temp, XO_DEPTH); 750631337658SMarcel Moolenaar 7507d1a0d267SMarcel Moolenaar va_copy(ap, vap); 7508d1a0d267SMarcel Moolenaar (void) xo_emit_hv(&temp, fmt, ap); 7509d1a0d267SMarcel Moolenaar va_end(ap); 751031337658SMarcel Moolenaar 7511d1a0d267SMarcel Moolenaar xo_buffer_t *src = &temp.xo_data; 7512d1a0d267SMarcel Moolenaar xo_format_value(xop, "message", 7, src->xb_bufp, 7513d1a0d267SMarcel Moolenaar src->xb_curp - src->xb_bufp, NULL, 0, 0); 751431337658SMarcel Moolenaar 7515d1a0d267SMarcel Moolenaar xo_free(temp.xo_stack); 7516d1a0d267SMarcel Moolenaar xo_buf_cleanup(src); 751731337658SMarcel Moolenaar } 751831337658SMarcel Moolenaar 7519d1a0d267SMarcel Moolenaar (void) xo_emit_hv(xop, fmt, vap); 752031337658SMarcel Moolenaar 7521d1a0d267SMarcel Moolenaar int len = strlen(fmt); 7522d1a0d267SMarcel Moolenaar if (len > 0 && fmt[len - 1] != '\n') { 7523d1a0d267SMarcel Moolenaar if (code > 0) { 7524d1a0d267SMarcel Moolenaar const char *msg = strerror(code); 7525d1a0d267SMarcel Moolenaar if (msg) 7526d1a0d267SMarcel Moolenaar xo_emit_h(xop, ": {G:strerror}{g:error/%s}", msg); 7527d1a0d267SMarcel Moolenaar } 7528d1a0d267SMarcel Moolenaar xo_emit("\n"); 752931337658SMarcel Moolenaar } 753031337658SMarcel Moolenaar 7531d1a0d267SMarcel Moolenaar xo_close_marker_h(xop, "xo_emit_warn_hcv"); 7532d1a0d267SMarcel Moolenaar xo_flush_h(xop); 753331337658SMarcel Moolenaar } 753431337658SMarcel Moolenaar 7535d1a0d267SMarcel Moolenaar void 7536d1a0d267SMarcel Moolenaar xo_emit_warn_hc (xo_handle_t *xop, int code, const char *fmt, ...) 7537d1a0d267SMarcel Moolenaar { 7538d1a0d267SMarcel Moolenaar va_list vap; 753931337658SMarcel Moolenaar 7540d1a0d267SMarcel Moolenaar va_start(vap, fmt); 7541d1a0d267SMarcel Moolenaar xo_emit_warn_hcv(xop, 1, code, fmt, vap); 7542d1a0d267SMarcel Moolenaar va_end(vap); 754331337658SMarcel Moolenaar } 754431337658SMarcel Moolenaar 7545d1a0d267SMarcel Moolenaar void 7546d1a0d267SMarcel Moolenaar xo_emit_warn_c (int code, const char *fmt, ...) 7547d1a0d267SMarcel Moolenaar { 7548d1a0d267SMarcel Moolenaar va_list vap; 754931337658SMarcel Moolenaar 7550d1a0d267SMarcel Moolenaar va_start(vap, fmt); 7551d1a0d267SMarcel Moolenaar xo_emit_warn_hcv(NULL, 1, code, fmt, vap); 7552d1a0d267SMarcel Moolenaar va_end(vap); 7553d1a0d267SMarcel Moolenaar } 755431337658SMarcel Moolenaar 7555d1a0d267SMarcel Moolenaar void 7556d1a0d267SMarcel Moolenaar xo_emit_warn (const char *fmt, ...) 7557d1a0d267SMarcel Moolenaar { 7558d1a0d267SMarcel Moolenaar int code = errno; 7559d1a0d267SMarcel Moolenaar va_list vap; 7560d1a0d267SMarcel Moolenaar 7561d1a0d267SMarcel Moolenaar va_start(vap, fmt); 7562d1a0d267SMarcel Moolenaar xo_emit_warn_hcv(NULL, 1, code, fmt, vap); 7563d1a0d267SMarcel Moolenaar va_end(vap); 7564d1a0d267SMarcel Moolenaar } 7565d1a0d267SMarcel Moolenaar 7566d1a0d267SMarcel Moolenaar void 7567d1a0d267SMarcel Moolenaar xo_emit_warnx (const char *fmt, ...) 7568d1a0d267SMarcel Moolenaar { 7569d1a0d267SMarcel Moolenaar va_list vap; 7570d1a0d267SMarcel Moolenaar 7571d1a0d267SMarcel Moolenaar va_start(vap, fmt); 7572d1a0d267SMarcel Moolenaar xo_emit_warn_hcv(NULL, 1, -1, fmt, vap); 7573d1a0d267SMarcel Moolenaar va_end(vap); 7574d1a0d267SMarcel Moolenaar } 7575d1a0d267SMarcel Moolenaar 7576d1a0d267SMarcel Moolenaar void 7577d1a0d267SMarcel Moolenaar xo_emit_err_v (int eval, int code, const char *fmt, va_list vap) 7578d1a0d267SMarcel Moolenaar { 7579d1a0d267SMarcel Moolenaar xo_emit_warn_hcv(NULL, 0, code, fmt, vap); 758031337658SMarcel Moolenaar xo_finish(); 7581d1a0d267SMarcel Moolenaar exit(eval); 758231337658SMarcel Moolenaar } 7583d1a0d267SMarcel Moolenaar 7584d1a0d267SMarcel Moolenaar void 7585d1a0d267SMarcel Moolenaar xo_emit_err (int eval, const char *fmt, ...) 7586d1a0d267SMarcel Moolenaar { 7587d1a0d267SMarcel Moolenaar int code = errno; 7588d1a0d267SMarcel Moolenaar va_list vap; 7589d1a0d267SMarcel Moolenaar va_start(vap, fmt); 7590d1a0d267SMarcel Moolenaar xo_emit_err_v(0, code, fmt, vap); 7591d1a0d267SMarcel Moolenaar va_end(vap); 7592d1a0d267SMarcel Moolenaar exit(eval); 7593d1a0d267SMarcel Moolenaar } 7594d1a0d267SMarcel Moolenaar 7595d1a0d267SMarcel Moolenaar void 7596d1a0d267SMarcel Moolenaar xo_emit_errx (int eval, const char *fmt, ...) 7597d1a0d267SMarcel Moolenaar { 7598d1a0d267SMarcel Moolenaar va_list vap; 7599d1a0d267SMarcel Moolenaar 7600d1a0d267SMarcel Moolenaar va_start(vap, fmt); 7601d1a0d267SMarcel Moolenaar xo_emit_err_v(0, -1, fmt, vap); 7602d1a0d267SMarcel Moolenaar va_end(vap); 7603d1a0d267SMarcel Moolenaar xo_finish(); 7604d1a0d267SMarcel Moolenaar exit(eval); 7605d1a0d267SMarcel Moolenaar } 7606d1a0d267SMarcel Moolenaar 7607d1a0d267SMarcel Moolenaar void 7608d1a0d267SMarcel Moolenaar xo_emit_errc (int eval, int code, const char *fmt, ...) 7609d1a0d267SMarcel Moolenaar { 7610d1a0d267SMarcel Moolenaar va_list vap; 7611d1a0d267SMarcel Moolenaar 7612d1a0d267SMarcel Moolenaar va_start(vap, fmt); 7613d1a0d267SMarcel Moolenaar xo_emit_warn_hcv(NULL, 0, code, fmt, vap); 7614d1a0d267SMarcel Moolenaar va_end(vap); 7615d1a0d267SMarcel Moolenaar xo_finish(); 7616d1a0d267SMarcel Moolenaar exit(eval); 7617d1a0d267SMarcel Moolenaar } 7618d1a0d267SMarcel Moolenaar 7619d1a0d267SMarcel Moolenaar /* 7620d1a0d267SMarcel Moolenaar * Get the opaque private pointer for an xo handle 7621d1a0d267SMarcel Moolenaar */ 7622d1a0d267SMarcel Moolenaar void * 7623d1a0d267SMarcel Moolenaar xo_get_private (xo_handle_t *xop) 7624d1a0d267SMarcel Moolenaar { 7625d1a0d267SMarcel Moolenaar xop = xo_default(xop); 7626d1a0d267SMarcel Moolenaar return xop->xo_private; 7627d1a0d267SMarcel Moolenaar } 7628d1a0d267SMarcel Moolenaar 7629d1a0d267SMarcel Moolenaar /* 7630d1a0d267SMarcel Moolenaar * Set the opaque private pointer for an xo handle. 7631d1a0d267SMarcel Moolenaar */ 7632d1a0d267SMarcel Moolenaar void 7633d1a0d267SMarcel Moolenaar xo_set_private (xo_handle_t *xop, void *opaque) 7634d1a0d267SMarcel Moolenaar { 7635d1a0d267SMarcel Moolenaar xop = xo_default(xop); 7636d1a0d267SMarcel Moolenaar xop->xo_private = opaque; 7637d1a0d267SMarcel Moolenaar } 7638d1a0d267SMarcel Moolenaar 7639d1a0d267SMarcel Moolenaar /* 7640d1a0d267SMarcel Moolenaar * Get the encoder function 7641d1a0d267SMarcel Moolenaar */ 7642d1a0d267SMarcel Moolenaar xo_encoder_func_t 7643d1a0d267SMarcel Moolenaar xo_get_encoder (xo_handle_t *xop) 7644d1a0d267SMarcel Moolenaar { 7645d1a0d267SMarcel Moolenaar xop = xo_default(xop); 7646d1a0d267SMarcel Moolenaar return xop->xo_encoder; 7647d1a0d267SMarcel Moolenaar } 7648d1a0d267SMarcel Moolenaar 7649d1a0d267SMarcel Moolenaar /* 7650d1a0d267SMarcel Moolenaar * Record an encoder callback function in an xo handle. 7651d1a0d267SMarcel Moolenaar */ 7652d1a0d267SMarcel Moolenaar void 7653d1a0d267SMarcel Moolenaar xo_set_encoder (xo_handle_t *xop, xo_encoder_func_t encoder) 7654d1a0d267SMarcel Moolenaar { 7655d1a0d267SMarcel Moolenaar xop = xo_default(xop); 7656d1a0d267SMarcel Moolenaar 7657d1a0d267SMarcel Moolenaar xop->xo_style = XO_STYLE_ENCODER; 7658d1a0d267SMarcel Moolenaar xop->xo_encoder = encoder; 7659d1a0d267SMarcel Moolenaar } 7660