131337658SMarcel Moolenaar /* 2545ddfbeSMarcel Moolenaar * Copyright (c) 2014-2015, Juniper Networks, Inc. 331337658SMarcel Moolenaar * All rights reserved. 431337658SMarcel Moolenaar * This SOFTWARE is licensed under the LICENSE provided in the 531337658SMarcel Moolenaar * ../Copyright file. By downloading, installing, copying, or otherwise 631337658SMarcel Moolenaar * using the SOFTWARE, you agree to be bound by the terms of that 731337658SMarcel Moolenaar * LICENSE. 831337658SMarcel Moolenaar * Phil Shafer, July 2014 9d1a0d267SMarcel Moolenaar * 10d1a0d267SMarcel Moolenaar * This is the implementation of libxo, the formatting library that 11d1a0d267SMarcel Moolenaar * generates multiple styles of output from a single code path. 12d1a0d267SMarcel Moolenaar * Command line utilities can have their normal text output while 13d1a0d267SMarcel Moolenaar * automation tools can see XML or JSON output, and web tools can use 14d1a0d267SMarcel Moolenaar * HTML output that encodes the text output annotated with additional 15d1a0d267SMarcel Moolenaar * information. Specialized encoders can be built that allow custom 16d1a0d267SMarcel Moolenaar * encoding including binary ones like CBOR, thrift, protobufs, etc. 17d1a0d267SMarcel Moolenaar * 18d1a0d267SMarcel Moolenaar * Full documentation is available in ./doc/libxo.txt or online at: 19d1a0d267SMarcel Moolenaar * http://juniper.github.io/libxo/libxo-manual.html 20d1a0d267SMarcel Moolenaar * 21d1a0d267SMarcel Moolenaar * For first time readers, the core bits of code to start looking at are: 2242ff34c3SPhil Shafer * - xo_do_emit() -- parse and emit a set of fields 2342ff34c3SPhil Shafer * - xo_do_emit_fields -- the central function of the library 24d1a0d267SMarcel Moolenaar * - xo_do_format_field() -- handles formatting a single field 25d1a0d267SMarcel Moolenaar * - xo_transiton() -- the state machine that keeps things sane 26d1a0d267SMarcel Moolenaar * and of course the "xo_handle_t" data structure, which carries all 27d1a0d267SMarcel Moolenaar * configuration and state. 2831337658SMarcel Moolenaar */ 2931337658SMarcel Moolenaar 3031337658SMarcel Moolenaar #include <stdio.h> 3131337658SMarcel Moolenaar #include <stdlib.h> 3231337658SMarcel Moolenaar #include <stdint.h> 33545ddfbeSMarcel Moolenaar #include <unistd.h> 3431337658SMarcel Moolenaar #include <stddef.h> 3531337658SMarcel Moolenaar #include <wchar.h> 3631337658SMarcel Moolenaar #include <locale.h> 3731337658SMarcel Moolenaar #include <sys/types.h> 3831337658SMarcel Moolenaar #include <stdarg.h> 3931337658SMarcel Moolenaar #include <string.h> 4031337658SMarcel Moolenaar #include <errno.h> 4131337658SMarcel Moolenaar #include <limits.h> 4231337658SMarcel Moolenaar #include <ctype.h> 4331337658SMarcel Moolenaar #include <wctype.h> 4431337658SMarcel Moolenaar #include <getopt.h> 4531337658SMarcel Moolenaar 46d1a0d267SMarcel Moolenaar #include "xo_config.h" 4731337658SMarcel Moolenaar #include "xo.h" 48d1a0d267SMarcel Moolenaar #include "xo_encoder.h" 49d1a0d267SMarcel Moolenaar #include "xo_buf.h" 50d1a0d267SMarcel Moolenaar 51d1a0d267SMarcel Moolenaar /* 52d1a0d267SMarcel Moolenaar * We ask wcwidth() to do an impossible job, really. It's supposed to 53d1a0d267SMarcel Moolenaar * need to tell us the number of columns consumed to display a unicode 54d1a0d267SMarcel Moolenaar * character. It returns that number without any sort of context, but 55d1a0d267SMarcel Moolenaar * we know they are characters whose glyph differs based on placement 56d1a0d267SMarcel Moolenaar * (end of word, middle of word, etc) and many that affect characters 57d1a0d267SMarcel Moolenaar * previously emitted. Without content, it can't hope to tell us. 58d1a0d267SMarcel Moolenaar * But it's the only standard tool we've got, so we use it. We would 59ee5cf116SPhil Shafer * use wcswidth() but it typically just loops through adding the results 60d1a0d267SMarcel Moolenaar * of wcwidth() calls in an entirely unhelpful way. 61d1a0d267SMarcel Moolenaar * 62d1a0d267SMarcel Moolenaar * Even then, there are many poor implementations (macosx), so we have 63d1a0d267SMarcel Moolenaar * to carry our own. We could have configure.ac test this (with 64d1a0d267SMarcel Moolenaar * something like 'assert(wcwidth(0x200d) == 0)'), but it would have 65d1a0d267SMarcel Moolenaar * to run a binary, which breaks cross-compilation. Hmm... I could 66d1a0d267SMarcel Moolenaar * run this test at init time and make a warning for our dear user. 67d1a0d267SMarcel Moolenaar * 68d1a0d267SMarcel Moolenaar * Anyhow, it remains a best-effort sort of thing. And it's all made 69d1a0d267SMarcel Moolenaar * more hopeless because we assume the display code doing the rendering is 70d1a0d267SMarcel Moolenaar * playing by the same rules we are. If it display 0x200d as a square 71d1a0d267SMarcel Moolenaar * box or a funky question mark, the output will be hosed. 72d1a0d267SMarcel Moolenaar */ 73d1a0d267SMarcel Moolenaar #ifdef LIBXO_WCWIDTH 74d1a0d267SMarcel Moolenaar #include "xo_wcwidth.h" 75d1a0d267SMarcel Moolenaar #else /* LIBXO_WCWIDTH */ 76d1a0d267SMarcel Moolenaar #define xo_wcwidth(_x) wcwidth(_x) 77d1a0d267SMarcel Moolenaar #endif /* LIBXO_WCWIDTH */ 7831337658SMarcel Moolenaar 79545ddfbeSMarcel Moolenaar #ifdef HAVE_STDIO_EXT_H 80545ddfbeSMarcel Moolenaar #include <stdio_ext.h> 81545ddfbeSMarcel Moolenaar #endif /* HAVE_STDIO_EXT_H */ 82545ddfbeSMarcel Moolenaar 83d1a0d267SMarcel Moolenaar /* 84d1a0d267SMarcel Moolenaar * humanize_number is a great function, unless you don't have it. So 85d1a0d267SMarcel Moolenaar * we carry one in our pocket. 86d1a0d267SMarcel Moolenaar */ 87d1a0d267SMarcel Moolenaar #ifdef HAVE_HUMANIZE_NUMBER 88d1a0d267SMarcel Moolenaar #include <libutil.h> 89d1a0d267SMarcel Moolenaar #define xo_humanize_number humanize_number 90d1a0d267SMarcel Moolenaar #else /* HAVE_HUMANIZE_NUMBER */ 91d1a0d267SMarcel Moolenaar #include "xo_humanize.h" 92d1a0d267SMarcel Moolenaar #endif /* HAVE_HUMANIZE_NUMBER */ 93d1a0d267SMarcel Moolenaar 94d1a0d267SMarcel Moolenaar #ifdef HAVE_GETTEXT 95d1a0d267SMarcel Moolenaar #include <libintl.h> 96d1a0d267SMarcel Moolenaar #endif /* HAVE_GETTEXT */ 97d1a0d267SMarcel Moolenaar 98264104f2SPhil Shafer /* Rather lame that we can't count on these... */ 99264104f2SPhil Shafer #ifndef FALSE 100264104f2SPhil Shafer #define FALSE 0 101264104f2SPhil Shafer #endif 102264104f2SPhil Shafer #ifndef TRUE 103264104f2SPhil Shafer #define TRUE 1 104264104f2SPhil Shafer #endif 105264104f2SPhil Shafer 106d1a0d267SMarcel Moolenaar /* 107d1a0d267SMarcel Moolenaar * Three styles of specifying thread-local variables are supported. 108ee5cf116SPhil Shafer * configure.ac has the brains to run each possibility through the 109d1a0d267SMarcel Moolenaar * compiler and see what works; we are left to define the THREAD_LOCAL 110d1a0d267SMarcel Moolenaar * macro to the right value. Most toolchains (clang, gcc) use 111d1a0d267SMarcel Moolenaar * "before", but some (borland) use "after" and I've heard of some 112d1a0d267SMarcel Moolenaar * (ms) that use __declspec. Any others out there? 113d1a0d267SMarcel Moolenaar */ 114d1a0d267SMarcel Moolenaar #define THREAD_LOCAL_before 1 115d1a0d267SMarcel Moolenaar #define THREAD_LOCAL_after 2 116d1a0d267SMarcel Moolenaar #define THREAD_LOCAL_declspec 3 117d1a0d267SMarcel Moolenaar 118d1a0d267SMarcel Moolenaar #ifndef HAVE_THREAD_LOCAL 119d1a0d267SMarcel Moolenaar #define THREAD_LOCAL(_x) _x 120d1a0d267SMarcel Moolenaar #elif HAVE_THREAD_LOCAL == THREAD_LOCAL_before 121d1a0d267SMarcel Moolenaar #define THREAD_LOCAL(_x) __thread _x 122d1a0d267SMarcel Moolenaar #elif HAVE_THREAD_LOCAL == THREAD_LOCAL_after 123d1a0d267SMarcel Moolenaar #define THREAD_LOCAL(_x) _x __thread 124d1a0d267SMarcel Moolenaar #elif HAVE_THREAD_LOCAL == THREAD_LOCAL_declspec 125d1a0d267SMarcel Moolenaar #define THREAD_LOCAL(_x) __declspec(_x) 126d1a0d267SMarcel Moolenaar #else 127d1a0d267SMarcel Moolenaar #error unknown thread-local setting 128d1a0d267SMarcel Moolenaar #endif /* HAVE_THREADS_H */ 129d1a0d267SMarcel Moolenaar 13031337658SMarcel Moolenaar const char xo_version[] = LIBXO_VERSION; 13131337658SMarcel Moolenaar const char xo_version_extra[] = LIBXO_VERSION_EXTRA; 13242ff34c3SPhil Shafer static const char xo_default_format[] = "%s"; 13331337658SMarcel Moolenaar 13431337658SMarcel Moolenaar #ifndef UNUSED 13531337658SMarcel Moolenaar #define UNUSED __attribute__ ((__unused__)) 13631337658SMarcel Moolenaar #endif /* UNUSED */ 13731337658SMarcel Moolenaar 13831337658SMarcel Moolenaar #define XO_INDENT_BY 2 /* Amount to indent when pretty printing */ 139d1a0d267SMarcel Moolenaar #define XO_DEPTH 128 /* Default stack depth */ 14031337658SMarcel Moolenaar #define XO_MAX_ANCHOR_WIDTH (8*1024) /* Anything wider is just sillyb */ 14131337658SMarcel Moolenaar 14231337658SMarcel Moolenaar #define XO_FAILURE_NAME "failure" 14331337658SMarcel Moolenaar 14431337658SMarcel Moolenaar /* Flags for the stack frame */ 14531337658SMarcel Moolenaar typedef unsigned xo_xsf_flags_t; /* XSF_* flags */ 14631337658SMarcel Moolenaar #define XSF_NOT_FIRST (1<<0) /* Not the first element */ 14731337658SMarcel Moolenaar #define XSF_LIST (1<<1) /* Frame is a list */ 14831337658SMarcel Moolenaar #define XSF_INSTANCE (1<<2) /* Frame is an instance */ 14931337658SMarcel Moolenaar #define XSF_DTRT (1<<3) /* Save the name for DTRT mode */ 15031337658SMarcel Moolenaar 151545ddfbeSMarcel Moolenaar #define XSF_CONTENT (1<<4) /* Some content has been emitted */ 152545ddfbeSMarcel Moolenaar #define XSF_EMIT (1<<5) /* Some field has been emitted */ 153545ddfbeSMarcel Moolenaar #define XSF_EMIT_KEY (1<<6) /* A key has been emitted */ 154545ddfbeSMarcel Moolenaar #define XSF_EMIT_LEAF_LIST (1<<7) /* A leaf-list field has been emitted */ 155545ddfbeSMarcel Moolenaar 156545ddfbeSMarcel Moolenaar /* These are the flags we propagate between markers and their parents */ 157545ddfbeSMarcel Moolenaar #define XSF_MARKER_FLAGS \ 158545ddfbeSMarcel Moolenaar (XSF_NOT_FIRST | XSF_CONTENT | XSF_EMIT | XSF_EMIT_KEY | XSF_EMIT_LEAF_LIST ) 159545ddfbeSMarcel Moolenaar 160545ddfbeSMarcel Moolenaar /* 161d1a0d267SMarcel Moolenaar * A word about states: We use a finite state machine (FMS) approach 162d1a0d267SMarcel Moolenaar * to help remove fragility from the caller's code. Instead of 163d1a0d267SMarcel Moolenaar * requiring a specific order of calls, we'll allow the caller more 164545ddfbeSMarcel Moolenaar * flexibility and make the library responsible for recovering from 165d1a0d267SMarcel Moolenaar * missed steps. The goal is that the library should not be capable 166d1a0d267SMarcel Moolenaar * of emitting invalid xml or json, but the developer shouldn't need 167545ddfbeSMarcel Moolenaar * to know or understand all the details about these encodings. 168545ddfbeSMarcel Moolenaar * 169d1a0d267SMarcel Moolenaar * You can think of states as either states or events, since they 170545ddfbeSMarcel Moolenaar * function rather like both. None of the XO_CLOSE_* events will 171d1a0d267SMarcel Moolenaar * persist as states, since the matching stack frame will be popped. 172545ddfbeSMarcel Moolenaar * Same is true of XSS_EMIT, which is an event that asks us to 173545ddfbeSMarcel Moolenaar * prep for emitting output fields. 174545ddfbeSMarcel Moolenaar */ 175545ddfbeSMarcel Moolenaar 176545ddfbeSMarcel Moolenaar /* Stack frame states */ 177545ddfbeSMarcel Moolenaar typedef unsigned xo_state_t; 178545ddfbeSMarcel Moolenaar #define XSS_INIT 0 /* Initial stack state */ 179545ddfbeSMarcel Moolenaar #define XSS_OPEN_CONTAINER 1 180545ddfbeSMarcel Moolenaar #define XSS_CLOSE_CONTAINER 2 181545ddfbeSMarcel Moolenaar #define XSS_OPEN_LIST 3 182545ddfbeSMarcel Moolenaar #define XSS_CLOSE_LIST 4 183545ddfbeSMarcel Moolenaar #define XSS_OPEN_INSTANCE 5 184545ddfbeSMarcel Moolenaar #define XSS_CLOSE_INSTANCE 6 185545ddfbeSMarcel Moolenaar #define XSS_OPEN_LEAF_LIST 7 186545ddfbeSMarcel Moolenaar #define XSS_CLOSE_LEAF_LIST 8 187545ddfbeSMarcel Moolenaar #define XSS_DISCARDING 9 /* Discarding data until recovered */ 188545ddfbeSMarcel Moolenaar #define XSS_MARKER 10 /* xo_open_marker's marker */ 189545ddfbeSMarcel Moolenaar #define XSS_EMIT 11 /* xo_emit has a leaf field */ 190545ddfbeSMarcel Moolenaar #define XSS_EMIT_LEAF_LIST 12 /* xo_emit has a leaf-list ({l:}) */ 191545ddfbeSMarcel Moolenaar #define XSS_FINISH 13 /* xo_finish was called */ 192545ddfbeSMarcel Moolenaar 193545ddfbeSMarcel Moolenaar #define XSS_MAX 13 194545ddfbeSMarcel Moolenaar 195545ddfbeSMarcel Moolenaar #define XSS_TRANSITION(_old, _new) ((_old) << 8 | (_new)) 196545ddfbeSMarcel Moolenaar 19731337658SMarcel Moolenaar /* 19831337658SMarcel Moolenaar * xo_stack_t: As we open and close containers and levels, we 19931337658SMarcel Moolenaar * create a stack of frames to track them. This is needed for 20031337658SMarcel Moolenaar * XOF_WARN and XOF_XPATH. 20131337658SMarcel Moolenaar */ 20231337658SMarcel Moolenaar typedef struct xo_stack_s { 20331337658SMarcel Moolenaar xo_xsf_flags_t xs_flags; /* Flags for this frame */ 204545ddfbeSMarcel Moolenaar xo_state_t xs_state; /* State for this stack frame */ 20531337658SMarcel Moolenaar char *xs_name; /* Name (for XPath value) */ 20631337658SMarcel Moolenaar char *xs_keys; /* XPath predicate for any key fields */ 20731337658SMarcel Moolenaar } xo_stack_t; 20831337658SMarcel Moolenaar 209d1a0d267SMarcel Moolenaar /* 210d1a0d267SMarcel Moolenaar * libxo supports colors and effects, for those who like them. 211d1a0d267SMarcel Moolenaar * XO_COL_* ("colors") refers to fancy ansi codes, while X__EFF_* 212d1a0d267SMarcel Moolenaar * ("effects") are bits since we need to maintain state. 213d1a0d267SMarcel Moolenaar */ 214f2b7bf8aSPhil Shafer typedef uint8_t xo_color_t; 215788ca347SMarcel Moolenaar #define XO_COL_DEFAULT 0 216788ca347SMarcel Moolenaar #define XO_COL_BLACK 1 217788ca347SMarcel Moolenaar #define XO_COL_RED 2 218788ca347SMarcel Moolenaar #define XO_COL_GREEN 3 219788ca347SMarcel Moolenaar #define XO_COL_YELLOW 4 220788ca347SMarcel Moolenaar #define XO_COL_BLUE 5 221788ca347SMarcel Moolenaar #define XO_COL_MAGENTA 6 222788ca347SMarcel Moolenaar #define XO_COL_CYAN 7 223788ca347SMarcel Moolenaar #define XO_COL_WHITE 8 224788ca347SMarcel Moolenaar 225788ca347SMarcel Moolenaar #define XO_NUM_COLORS 9 226788ca347SMarcel Moolenaar 227788ca347SMarcel Moolenaar /* 228788ca347SMarcel Moolenaar * Yes, there's no blink. We're civilized. We like users. Blink 229788ca347SMarcel Moolenaar * isn't something one does to someone you like. Friends don't let 230788ca347SMarcel Moolenaar * friends use blink. On friends. You know what I mean. Blink is 231788ca347SMarcel Moolenaar * like, well, it's like bursting into show tunes at a funeral. It's 232788ca347SMarcel Moolenaar * just not done. Not something anyone wants. And on those rare 233d1a0d267SMarcel Moolenaar * instances where it might actually be appropriate, it's still wrong, 234d1a0d267SMarcel Moolenaar * since it's likely done by the wrong person for the wrong reason. 235d1a0d267SMarcel Moolenaar * Just like blink. And if I implemented blink, I'd be like a funeral 236788ca347SMarcel Moolenaar * director who adds "Would you like us to burst into show tunes?" on 237d1a0d267SMarcel Moolenaar * the list of questions asked while making funeral arrangements. 238788ca347SMarcel Moolenaar * It's formalizing wrongness in the wrong way. And we're just too 239788ca347SMarcel Moolenaar * civilized to do that. Hhhmph! 240788ca347SMarcel Moolenaar */ 241788ca347SMarcel Moolenaar #define XO_EFF_RESET (1<<0) 242788ca347SMarcel Moolenaar #define XO_EFF_NORMAL (1<<1) 243788ca347SMarcel Moolenaar #define XO_EFF_BOLD (1<<2) 244788ca347SMarcel Moolenaar #define XO_EFF_UNDERLINE (1<<3) 245788ca347SMarcel Moolenaar #define XO_EFF_INVERSE (1<<4) 246788ca347SMarcel Moolenaar 247d1a0d267SMarcel Moolenaar #define XO_EFF_CLEAR_BITS XO_EFF_RESET /* Reset gets reset, surprisingly */ 248788ca347SMarcel Moolenaar 249788ca347SMarcel Moolenaar typedef uint8_t xo_effect_t; 250788ca347SMarcel Moolenaar typedef struct xo_colors_s { 251788ca347SMarcel Moolenaar xo_effect_t xoc_effects; /* Current effect set */ 252788ca347SMarcel Moolenaar xo_color_t xoc_col_fg; /* Foreground color */ 253788ca347SMarcel Moolenaar xo_color_t xoc_col_bg; /* Background color */ 254788ca347SMarcel Moolenaar } xo_colors_t; 255788ca347SMarcel Moolenaar 25631337658SMarcel Moolenaar /* 25731337658SMarcel Moolenaar * xo_handle_t: this is the principle data structure for libxo. 258d1a0d267SMarcel Moolenaar * It's used as a store for state, options, content, and all manor 259d1a0d267SMarcel Moolenaar * of other information. 26031337658SMarcel Moolenaar */ 26131337658SMarcel Moolenaar struct xo_handle_s { 262d1a0d267SMarcel Moolenaar xo_xof_flags_t xo_flags; /* Flags (XOF_*) from the user*/ 263d1a0d267SMarcel Moolenaar xo_xof_flags_t xo_iflags; /* Internal flags (XOIF_*) */ 264d1a0d267SMarcel Moolenaar xo_style_t xo_style; /* XO_STYLE_* value */ 26531337658SMarcel Moolenaar unsigned short xo_indent; /* Indent level (if pretty) */ 26631337658SMarcel Moolenaar unsigned short xo_indent_by; /* Indent amount (tab stop) */ 26731337658SMarcel Moolenaar xo_write_func_t xo_write; /* Write callback */ 268a0f704ffSMarcel Moolenaar xo_close_func_t xo_close; /* Close callback */ 269545ddfbeSMarcel Moolenaar xo_flush_func_t xo_flush; /* Flush callback */ 27031337658SMarcel Moolenaar xo_formatter_t xo_formatter; /* Custom formating function */ 27131337658SMarcel Moolenaar xo_checkpointer_t xo_checkpointer; /* Custom formating support function */ 27231337658SMarcel Moolenaar void *xo_opaque; /* Opaque data for write function */ 27331337658SMarcel Moolenaar xo_buffer_t xo_data; /* Output data */ 27431337658SMarcel Moolenaar xo_buffer_t xo_fmt; /* Work area for building format strings */ 27531337658SMarcel Moolenaar xo_buffer_t xo_attrs; /* Work area for building XML attributes */ 27631337658SMarcel Moolenaar xo_buffer_t xo_predicate; /* Work area for building XPath predicates */ 27731337658SMarcel Moolenaar xo_stack_t *xo_stack; /* Stack pointer */ 27831337658SMarcel Moolenaar int xo_depth; /* Depth of stack */ 27931337658SMarcel Moolenaar int xo_stack_size; /* Size of the stack */ 28031337658SMarcel Moolenaar xo_info_t *xo_info; /* Info fields for all elements */ 28131337658SMarcel Moolenaar int xo_info_count; /* Number of info entries */ 28231337658SMarcel Moolenaar va_list xo_vap; /* Variable arguments (stdargs) */ 28331337658SMarcel Moolenaar char *xo_leading_xpath; /* A leading XPath expression */ 28431337658SMarcel Moolenaar mbstate_t xo_mbstate; /* Multi-byte character conversion state */ 2858a6eceffSPhil Shafer ssize_t xo_anchor_offset; /* Start of anchored text */ 2868a6eceffSPhil Shafer ssize_t xo_anchor_columns; /* Number of columns since the start anchor */ 2878a6eceffSPhil Shafer ssize_t xo_anchor_min_width; /* Desired width of anchored text */ 2888a6eceffSPhil Shafer ssize_t xo_units_offset; /* Start of units insertion point */ 2898a6eceffSPhil Shafer ssize_t xo_columns; /* Columns emitted during this xo_emit call */ 290f2b7bf8aSPhil Shafer #ifndef LIBXO_TEXT_ONLY 291788ca347SMarcel Moolenaar uint8_t xo_color_map_fg[XO_NUM_COLORS]; /* Foreground color mappings */ 292788ca347SMarcel Moolenaar uint8_t xo_color_map_bg[XO_NUM_COLORS]; /* Background color mappings */ 293f2b7bf8aSPhil Shafer #endif /* LIBXO_TEXT_ONLY */ 294788ca347SMarcel Moolenaar xo_colors_t xo_colors; /* Current color and effect values */ 295788ca347SMarcel Moolenaar xo_buffer_t xo_color_buf; /* HTML: buffer of colors and effects */ 296788ca347SMarcel Moolenaar char *xo_version; /* Version string */ 297d1a0d267SMarcel Moolenaar int xo_errno; /* Saved errno for "%m" */ 298d1a0d267SMarcel Moolenaar char *xo_gt_domain; /* Gettext domain, suitable for dgettext(3) */ 299d1a0d267SMarcel Moolenaar xo_encoder_func_t xo_encoder; /* Encoding function */ 300d1a0d267SMarcel Moolenaar void *xo_private; /* Private data for external encoders */ 30131337658SMarcel Moolenaar }; 30231337658SMarcel Moolenaar 303d1a0d267SMarcel Moolenaar /* Flag operations */ 304d1a0d267SMarcel Moolenaar #define XOF_BIT_ISSET(_flag, _bit) (((_flag) & (_bit)) ? 1 : 0) 305d1a0d267SMarcel Moolenaar #define XOF_BIT_SET(_flag, _bit) do { (_flag) |= (_bit); } while (0) 306d1a0d267SMarcel Moolenaar #define XOF_BIT_CLEAR(_flag, _bit) do { (_flag) &= ~(_bit); } while (0) 307d1a0d267SMarcel Moolenaar 308d1a0d267SMarcel Moolenaar #define XOF_ISSET(_xop, _bit) XOF_BIT_ISSET(_xop->xo_flags, _bit) 309d1a0d267SMarcel Moolenaar #define XOF_SET(_xop, _bit) XOF_BIT_SET(_xop->xo_flags, _bit) 310d1a0d267SMarcel Moolenaar #define XOF_CLEAR(_xop, _bit) XOF_BIT_CLEAR(_xop->xo_flags, _bit) 311d1a0d267SMarcel Moolenaar 312d1a0d267SMarcel Moolenaar #define XOIF_ISSET(_xop, _bit) XOF_BIT_ISSET(_xop->xo_iflags, _bit) 313d1a0d267SMarcel Moolenaar #define XOIF_SET(_xop, _bit) XOF_BIT_SET(_xop->xo_iflags, _bit) 314d1a0d267SMarcel Moolenaar #define XOIF_CLEAR(_xop, _bit) XOF_BIT_CLEAR(_xop->xo_iflags, _bit) 315d1a0d267SMarcel Moolenaar 316d1a0d267SMarcel Moolenaar /* Internal flags */ 317d1a0d267SMarcel Moolenaar #define XOIF_REORDER XOF_BIT(0) /* Reordering fields; record field info */ 318d1a0d267SMarcel Moolenaar #define XOIF_DIV_OPEN XOF_BIT(1) /* A <div> is open */ 319d1a0d267SMarcel Moolenaar #define XOIF_TOP_EMITTED XOF_BIT(2) /* The top JSON braces have been emitted */ 320d1a0d267SMarcel Moolenaar #define XOIF_ANCHOR XOF_BIT(3) /* An anchor is in place */ 321d1a0d267SMarcel Moolenaar 322d1a0d267SMarcel Moolenaar #define XOIF_UNITS_PENDING XOF_BIT(4) /* We have a units-insertion pending */ 323d1a0d267SMarcel Moolenaar #define XOIF_INIT_IN_PROGRESS XOF_BIT(5) /* Init of handle is in progress */ 324d1a0d267SMarcel Moolenaar 32531337658SMarcel Moolenaar /* Flags for formatting functions */ 32631337658SMarcel Moolenaar typedef unsigned long xo_xff_flags_t; 32731337658SMarcel Moolenaar #define XFF_COLON (1<<0) /* Append a ":" */ 32831337658SMarcel Moolenaar #define XFF_COMMA (1<<1) /* Append a "," iff there's more output */ 32931337658SMarcel Moolenaar #define XFF_WS (1<<2) /* Append a blank */ 330d1a0d267SMarcel Moolenaar #define XFF_ENCODE_ONLY (1<<3) /* Only emit for encoding styles (XML, JSON) */ 33131337658SMarcel Moolenaar 33231337658SMarcel Moolenaar #define XFF_QUOTE (1<<4) /* Force quotes */ 33331337658SMarcel Moolenaar #define XFF_NOQUOTE (1<<5) /* Force no quotes */ 334d1a0d267SMarcel Moolenaar #define XFF_DISPLAY_ONLY (1<<6) /* Only emit for display styles (text, html) */ 33531337658SMarcel Moolenaar #define XFF_KEY (1<<7) /* Field is a key (for XPath) */ 33631337658SMarcel Moolenaar 33731337658SMarcel Moolenaar #define XFF_XML (1<<8) /* Force XML encoding style (for XPath) */ 33831337658SMarcel Moolenaar #define XFF_ATTR (1<<9) /* Escape value using attribute rules (XML) */ 33931337658SMarcel Moolenaar #define XFF_BLANK_LINE (1<<10) /* Emit a blank line */ 34031337658SMarcel Moolenaar #define XFF_NO_OUTPUT (1<<11) /* Do not make any output */ 34131337658SMarcel Moolenaar 34231337658SMarcel Moolenaar #define XFF_TRIM_WS (1<<12) /* Trim whitespace off encoded values */ 34331337658SMarcel Moolenaar #define XFF_LEAF_LIST (1<<13) /* A leaf-list (list of values) */ 34431337658SMarcel Moolenaar #define XFF_UNESCAPE (1<<14) /* Need to printf-style unescape the value */ 345d1a0d267SMarcel Moolenaar #define XFF_HUMANIZE (1<<15) /* Humanize the value (for display styles) */ 346d1a0d267SMarcel Moolenaar 347d1a0d267SMarcel Moolenaar #define XFF_HN_SPACE (1<<16) /* Humanize: put space before suffix */ 348d1a0d267SMarcel Moolenaar #define XFF_HN_DECIMAL (1<<17) /* Humanize: add one decimal place if <10 */ 349d1a0d267SMarcel Moolenaar #define XFF_HN_1000 (1<<18) /* Humanize: use 1000, not 1024 */ 350d1a0d267SMarcel Moolenaar #define XFF_GT_FIELD (1<<19) /* Call gettext() on a field */ 351d1a0d267SMarcel Moolenaar 352d1a0d267SMarcel Moolenaar #define XFF_GT_PLURAL (1<<20) /* Call dngettext to find plural form */ 35342ff34c3SPhil Shafer #define XFF_ARGUMENT (1<<21) /* Content provided via argument */ 354d1a0d267SMarcel Moolenaar 355d1a0d267SMarcel Moolenaar /* Flags to turn off when we don't want i18n processing */ 356d1a0d267SMarcel Moolenaar #define XFF_GT_FLAGS (XFF_GT_FIELD | XFF_GT_PLURAL) 35731337658SMarcel Moolenaar 35831337658SMarcel Moolenaar /* 35931337658SMarcel Moolenaar * Normal printf has width and precision, which for strings operate as 36031337658SMarcel Moolenaar * min and max number of columns. But this depends on the idea that 36131337658SMarcel Moolenaar * one byte means one column, which UTF-8 and multi-byte characters 36231337658SMarcel Moolenaar * pitches on its ear. It may take 40 bytes of data to populate 14 36331337658SMarcel Moolenaar * columns, but we can't go off looking at 40 bytes of data without the 36431337658SMarcel Moolenaar * caller's permission for fear/knowledge that we'll generate core files. 36531337658SMarcel Moolenaar * 36631337658SMarcel Moolenaar * So we make three values, distinguishing between "max column" and 36731337658SMarcel Moolenaar * "number of bytes that we will inspect inspect safely" We call the 36831337658SMarcel Moolenaar * later "size", and make the format "%[[<min>].[[<size>].<max>]]s". 36931337658SMarcel Moolenaar * 37031337658SMarcel Moolenaar * Under the "first do no harm" theory, we default "max" to "size". 37131337658SMarcel Moolenaar * This is a reasonable assumption for folks that don't grok the 37231337658SMarcel Moolenaar * MBS/WCS/UTF-8 world, and while it will be annoying, it will never 37331337658SMarcel Moolenaar * be evil. 37431337658SMarcel Moolenaar * 37531337658SMarcel Moolenaar * For example, xo_emit("{:tag/%-14.14s}", buf) will make 14 37631337658SMarcel Moolenaar * columns of output, but will never look at more than 14 bytes of the 37731337658SMarcel Moolenaar * input buffer. This is mostly compatible with printf and caller's 37831337658SMarcel Moolenaar * expectations. 37931337658SMarcel Moolenaar * 38031337658SMarcel Moolenaar * In contrast xo_emit("{:tag/%-14..14s}", buf) will look at however 38131337658SMarcel Moolenaar * many bytes (or until a NUL is seen) are needed to fill 14 columns 38231337658SMarcel Moolenaar * of output. xo_emit("{:tag/%-14.*.14s}", xx, buf) will look at up 38331337658SMarcel Moolenaar * to xx bytes (or until a NUL is seen) in order to fill 14 columns 38431337658SMarcel Moolenaar * of output. 38531337658SMarcel Moolenaar * 38631337658SMarcel Moolenaar * It's fairly amazing how a good idea (handle all languages of the 38731337658SMarcel Moolenaar * world) blows such a big hole in the bottom of the fairly weak boat 38831337658SMarcel Moolenaar * that is C string handling. The simplicity and completenesss are 38931337658SMarcel Moolenaar * sunk in ways we haven't even begun to understand. 39031337658SMarcel Moolenaar */ 39131337658SMarcel Moolenaar #define XF_WIDTH_MIN 0 /* Minimal width */ 39231337658SMarcel Moolenaar #define XF_WIDTH_SIZE 1 /* Maximum number of bytes to examine */ 39331337658SMarcel Moolenaar #define XF_WIDTH_MAX 2 /* Maximum width */ 39431337658SMarcel Moolenaar #define XF_WIDTH_NUM 3 /* Numeric fields in printf (min.size.max) */ 39531337658SMarcel Moolenaar 39631337658SMarcel Moolenaar /* Input and output string encodings */ 39731337658SMarcel Moolenaar #define XF_ENC_WIDE 1 /* Wide characters (wchar_t) */ 39831337658SMarcel Moolenaar #define XF_ENC_UTF8 2 /* UTF-8 */ 39931337658SMarcel Moolenaar #define XF_ENC_LOCALE 3 /* Current locale */ 40031337658SMarcel Moolenaar 40131337658SMarcel Moolenaar /* 40231337658SMarcel Moolenaar * A place to parse printf-style format flags for each field 40331337658SMarcel Moolenaar */ 40431337658SMarcel Moolenaar typedef struct xo_format_s { 40531337658SMarcel Moolenaar unsigned char xf_fc; /* Format character */ 40631337658SMarcel Moolenaar unsigned char xf_enc; /* Encoding of the string (XF_ENC_*) */ 40731337658SMarcel Moolenaar unsigned char xf_skip; /* Skip this field */ 40831337658SMarcel Moolenaar unsigned char xf_lflag; /* 'l' (long) */ 40931337658SMarcel Moolenaar unsigned char xf_hflag;; /* 'h' (half) */ 41031337658SMarcel Moolenaar unsigned char xf_jflag; /* 'j' (intmax_t) */ 41131337658SMarcel Moolenaar unsigned char xf_tflag; /* 't' (ptrdiff_t) */ 41231337658SMarcel Moolenaar unsigned char xf_zflag; /* 'z' (size_t) */ 41331337658SMarcel Moolenaar unsigned char xf_qflag; /* 'q' (quad_t) */ 41431337658SMarcel Moolenaar unsigned char xf_seen_minus; /* Seen a minus */ 41531337658SMarcel Moolenaar int xf_leading_zero; /* Seen a leading zero (zero fill) */ 41631337658SMarcel Moolenaar unsigned xf_dots; /* Seen one or more '.'s */ 41731337658SMarcel Moolenaar int xf_width[XF_WIDTH_NUM]; /* Width/precision/size numeric fields */ 41831337658SMarcel Moolenaar unsigned xf_stars; /* Seen one or more '*'s */ 41931337658SMarcel Moolenaar unsigned char xf_star[XF_WIDTH_NUM]; /* Seen one or more '*'s */ 42031337658SMarcel Moolenaar } xo_format_t; 42131337658SMarcel Moolenaar 42231337658SMarcel Moolenaar /* 423d1a0d267SMarcel Moolenaar * This structure represents the parsed field information, suitable for 424d1a0d267SMarcel Moolenaar * processing by xo_do_emit and anything else that needs to parse fields. 425d1a0d267SMarcel Moolenaar * Note that all pointers point to the main format string. 426d1a0d267SMarcel Moolenaar * 427d1a0d267SMarcel Moolenaar * XXX This is a first step toward compilable or cachable format 428d1a0d267SMarcel Moolenaar * strings. We can also cache the results of dgettext when no format 429d1a0d267SMarcel Moolenaar * is used, assuming the 'p' modifier has _not_ been set. 43031337658SMarcel Moolenaar */ 431d1a0d267SMarcel Moolenaar typedef struct xo_field_info_s { 432d1a0d267SMarcel Moolenaar xo_xff_flags_t xfi_flags; /* Flags for this field */ 433d1a0d267SMarcel Moolenaar unsigned xfi_ftype; /* Field type, as character (e.g. 'V') */ 434d1a0d267SMarcel Moolenaar const char *xfi_start; /* Start of field in the format string */ 435d1a0d267SMarcel Moolenaar const char *xfi_content; /* Field's content */ 436d1a0d267SMarcel Moolenaar const char *xfi_format; /* Field's Format */ 437d1a0d267SMarcel Moolenaar const char *xfi_encoding; /* Field's encoding format */ 438d1a0d267SMarcel Moolenaar const char *xfi_next; /* Next character in format string */ 4398a6eceffSPhil Shafer ssize_t xfi_len; /* Length of field */ 4408a6eceffSPhil Shafer ssize_t xfi_clen; /* Content length */ 4418a6eceffSPhil Shafer ssize_t xfi_flen; /* Format length */ 4428a6eceffSPhil Shafer ssize_t xfi_elen; /* Encoding length */ 443d1a0d267SMarcel Moolenaar unsigned xfi_fnum; /* Field number (if used; 0 otherwise) */ 444d1a0d267SMarcel Moolenaar unsigned xfi_renum; /* Reordered number (0 == no renumbering) */ 445d1a0d267SMarcel Moolenaar } xo_field_info_t; 446d1a0d267SMarcel Moolenaar 447d1a0d267SMarcel Moolenaar /* 448d1a0d267SMarcel Moolenaar * We keep a 'default' handle to allow callers to avoid having to 449d1a0d267SMarcel Moolenaar * allocate one. Passing NULL to any of our functions will use 450d1a0d267SMarcel Moolenaar * this default handle. Most functions have a variant that doesn't 451d1a0d267SMarcel Moolenaar * require a handle at all, since most output is to stdout, which 452d1a0d267SMarcel Moolenaar * the default handle handles handily. 453d1a0d267SMarcel Moolenaar */ 454d1a0d267SMarcel Moolenaar static THREAD_LOCAL(xo_handle_t) xo_default_handle; 455d1a0d267SMarcel Moolenaar static THREAD_LOCAL(int) xo_default_inited; 45631337658SMarcel Moolenaar static int xo_locale_inited; 457545ddfbeSMarcel Moolenaar static const char *xo_program; 45831337658SMarcel Moolenaar 45931337658SMarcel Moolenaar /* 46031337658SMarcel Moolenaar * To allow libxo to be used in diverse environment, we allow the 46131337658SMarcel Moolenaar * caller to give callbacks for memory allocation. 46231337658SMarcel Moolenaar */ 463d1a0d267SMarcel Moolenaar xo_realloc_func_t xo_realloc = realloc; 464d1a0d267SMarcel Moolenaar xo_free_func_t xo_free = free; 46531337658SMarcel Moolenaar 46631337658SMarcel Moolenaar /* Forward declarations */ 46731337658SMarcel Moolenaar static void 46831337658SMarcel Moolenaar xo_failure (xo_handle_t *xop, const char *fmt, ...); 46931337658SMarcel Moolenaar 4708a6eceffSPhil Shafer static ssize_t 471545ddfbeSMarcel Moolenaar xo_transition (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name, 472545ddfbeSMarcel Moolenaar xo_state_t new_state); 473545ddfbeSMarcel Moolenaar 474f2b7bf8aSPhil Shafer static int 475f2b7bf8aSPhil Shafer xo_set_options_simple (xo_handle_t *xop, const char *input); 476f2b7bf8aSPhil Shafer 477f2b7bf8aSPhil Shafer static int 478f2b7bf8aSPhil Shafer xo_color_find (const char *str); 479f2b7bf8aSPhil Shafer 48031337658SMarcel Moolenaar static void 48131337658SMarcel Moolenaar xo_buf_append_div (xo_handle_t *xop, const char *class, xo_xff_flags_t flags, 4828a6eceffSPhil Shafer const char *name, ssize_t nlen, 4838a6eceffSPhil Shafer const char *value, ssize_t vlen, 484264104f2SPhil Shafer const char *fmt, ssize_t flen, 4858a6eceffSPhil Shafer const char *encoding, ssize_t elen); 48631337658SMarcel Moolenaar 48731337658SMarcel Moolenaar static void 48831337658SMarcel Moolenaar xo_anchor_clear (xo_handle_t *xop); 48931337658SMarcel Moolenaar 49031337658SMarcel Moolenaar /* 491788ca347SMarcel Moolenaar * xo_style is used to retrieve the current style. When we're built 492788ca347SMarcel Moolenaar * for "text only" mode, we use this function to drive the removal 493788ca347SMarcel Moolenaar * of most of the code in libxo. We return a constant and the compiler 494788ca347SMarcel Moolenaar * happily removes the non-text code that is not longer executed. This 495788ca347SMarcel Moolenaar * trims our code nicely without needing to trampel perfectly readable 496788ca347SMarcel Moolenaar * code with ifdefs. 497788ca347SMarcel Moolenaar */ 498d1a0d267SMarcel Moolenaar static inline xo_style_t 499788ca347SMarcel Moolenaar xo_style (xo_handle_t *xop UNUSED) 500788ca347SMarcel Moolenaar { 501788ca347SMarcel Moolenaar #ifdef LIBXO_TEXT_ONLY 502788ca347SMarcel Moolenaar return XO_STYLE_TEXT; 503788ca347SMarcel Moolenaar #else /* LIBXO_TEXT_ONLY */ 504788ca347SMarcel Moolenaar return xop->xo_style; 505788ca347SMarcel Moolenaar #endif /* LIBXO_TEXT_ONLY */ 506788ca347SMarcel Moolenaar } 507788ca347SMarcel Moolenaar 508788ca347SMarcel Moolenaar /* 50931337658SMarcel Moolenaar * Callback to write data to a FILE pointer 51031337658SMarcel Moolenaar */ 5118a6eceffSPhil Shafer static xo_ssize_t 51231337658SMarcel Moolenaar xo_write_to_file (void *opaque, const char *data) 51331337658SMarcel Moolenaar { 51431337658SMarcel Moolenaar FILE *fp = (FILE *) opaque; 515545ddfbeSMarcel Moolenaar 51631337658SMarcel Moolenaar return fprintf(fp, "%s", data); 51731337658SMarcel Moolenaar } 51831337658SMarcel Moolenaar 51931337658SMarcel Moolenaar /* 52031337658SMarcel Moolenaar * Callback to close a file 52131337658SMarcel Moolenaar */ 52231337658SMarcel Moolenaar static void 52331337658SMarcel Moolenaar xo_close_file (void *opaque) 52431337658SMarcel Moolenaar { 52531337658SMarcel Moolenaar FILE *fp = (FILE *) opaque; 526545ddfbeSMarcel Moolenaar 52731337658SMarcel Moolenaar fclose(fp); 52831337658SMarcel Moolenaar } 52931337658SMarcel Moolenaar 53031337658SMarcel Moolenaar /* 531545ddfbeSMarcel Moolenaar * Callback to flush a FILE pointer 532545ddfbeSMarcel Moolenaar */ 533545ddfbeSMarcel Moolenaar static int 534545ddfbeSMarcel Moolenaar xo_flush_file (void *opaque) 535545ddfbeSMarcel Moolenaar { 536545ddfbeSMarcel Moolenaar FILE *fp = (FILE *) opaque; 537545ddfbeSMarcel Moolenaar 538545ddfbeSMarcel Moolenaar return fflush(fp); 539545ddfbeSMarcel Moolenaar } 540545ddfbeSMarcel Moolenaar 541545ddfbeSMarcel Moolenaar /* 542d1a0d267SMarcel Moolenaar * Use a rotating stock of buffers to make a printable string 54331337658SMarcel Moolenaar */ 544d1a0d267SMarcel Moolenaar #define XO_NUMBUFS 8 545d1a0d267SMarcel Moolenaar #define XO_SMBUFSZ 128 546d1a0d267SMarcel Moolenaar 547d1a0d267SMarcel Moolenaar static const char * 548d1a0d267SMarcel Moolenaar xo_printable (const char *str) 54931337658SMarcel Moolenaar { 550d1a0d267SMarcel Moolenaar static THREAD_LOCAL(char) bufset[XO_NUMBUFS][XO_SMBUFSZ]; 551d1a0d267SMarcel Moolenaar static THREAD_LOCAL(int) bufnum = 0; 552d1a0d267SMarcel Moolenaar 553d1a0d267SMarcel Moolenaar if (str == NULL) 554d1a0d267SMarcel Moolenaar return ""; 555d1a0d267SMarcel Moolenaar 556d1a0d267SMarcel Moolenaar if (++bufnum == XO_NUMBUFS) 557d1a0d267SMarcel Moolenaar bufnum = 0; 558d1a0d267SMarcel Moolenaar 559d1a0d267SMarcel Moolenaar char *res = bufset[bufnum], *cp, *ep; 560d1a0d267SMarcel Moolenaar 561d1a0d267SMarcel Moolenaar for (cp = res, ep = res + XO_SMBUFSZ - 1; *str && cp < ep; cp++, str++) { 562d1a0d267SMarcel Moolenaar if (*str == '\n') { 563d1a0d267SMarcel Moolenaar *cp++ = '\\'; 564d1a0d267SMarcel Moolenaar *cp = 'n'; 565d1a0d267SMarcel Moolenaar } else if (*str == '\r') { 566d1a0d267SMarcel Moolenaar *cp++ = '\\'; 567d1a0d267SMarcel Moolenaar *cp = 'r'; 568d1a0d267SMarcel Moolenaar } else if (*str == '\"') { 569d1a0d267SMarcel Moolenaar *cp++ = '\\'; 570d1a0d267SMarcel Moolenaar *cp = '"'; 571d1a0d267SMarcel Moolenaar } else 572d1a0d267SMarcel Moolenaar *cp = *str; 57331337658SMarcel Moolenaar } 57431337658SMarcel Moolenaar 575d1a0d267SMarcel Moolenaar *cp = '\0'; 576d1a0d267SMarcel Moolenaar return res; 57731337658SMarcel Moolenaar } 57831337658SMarcel Moolenaar 57931337658SMarcel Moolenaar static int 58031337658SMarcel Moolenaar xo_depth_check (xo_handle_t *xop, int depth) 58131337658SMarcel Moolenaar { 58231337658SMarcel Moolenaar xo_stack_t *xsp; 58331337658SMarcel Moolenaar 58431337658SMarcel Moolenaar if (depth >= xop->xo_stack_size) { 585d1a0d267SMarcel Moolenaar depth += XO_DEPTH; /* Extra room */ 586d1a0d267SMarcel Moolenaar 58731337658SMarcel Moolenaar xsp = xo_realloc(xop->xo_stack, sizeof(xop->xo_stack[0]) * depth); 58831337658SMarcel Moolenaar if (xsp == NULL) { 58931337658SMarcel Moolenaar xo_failure(xop, "xo_depth_check: out of memory (%d)", depth); 590d1a0d267SMarcel Moolenaar return -1; 59131337658SMarcel Moolenaar } 59231337658SMarcel Moolenaar 59331337658SMarcel Moolenaar int count = depth - xop->xo_stack_size; 59431337658SMarcel Moolenaar 59531337658SMarcel Moolenaar bzero(xsp + xop->xo_stack_size, count * sizeof(*xsp)); 59631337658SMarcel Moolenaar xop->xo_stack_size = depth; 59731337658SMarcel Moolenaar xop->xo_stack = xsp; 59831337658SMarcel Moolenaar } 59931337658SMarcel Moolenaar 60031337658SMarcel Moolenaar return 0; 60131337658SMarcel Moolenaar } 60231337658SMarcel Moolenaar 60331337658SMarcel Moolenaar void 60431337658SMarcel Moolenaar xo_no_setlocale (void) 60531337658SMarcel Moolenaar { 60631337658SMarcel Moolenaar xo_locale_inited = 1; /* Skip initialization */ 60731337658SMarcel Moolenaar } 60831337658SMarcel Moolenaar 60931337658SMarcel Moolenaar /* 610545ddfbeSMarcel Moolenaar * We need to decide if stdout is line buffered (_IOLBF). Lacking a 611545ddfbeSMarcel Moolenaar * standard way to decide this (e.g. getlinebuf()), we have configure 612788ca347SMarcel Moolenaar * look to find __flbf, which glibc supported. If not, we'll rely on 613788ca347SMarcel Moolenaar * isatty, with the assumption that terminals are the only thing 614545ddfbeSMarcel Moolenaar * that's line buffered. We _could_ test for "steam._flags & _IOLBF", 615545ddfbeSMarcel Moolenaar * which is all __flbf does, but that's even tackier. Like a 616545ddfbeSMarcel Moolenaar * bedazzled Elvis outfit on an ugly lap dog sort of tacky. Not 617545ddfbeSMarcel Moolenaar * something we're willing to do. 618545ddfbeSMarcel Moolenaar */ 619545ddfbeSMarcel Moolenaar static int 620545ddfbeSMarcel Moolenaar xo_is_line_buffered (FILE *stream) 621545ddfbeSMarcel Moolenaar { 622545ddfbeSMarcel Moolenaar #if HAVE___FLBF 623545ddfbeSMarcel Moolenaar if (__flbf(stream)) 624545ddfbeSMarcel Moolenaar return 1; 625545ddfbeSMarcel Moolenaar #else /* HAVE___FLBF */ 626545ddfbeSMarcel Moolenaar if (isatty(fileno(stream))) 627545ddfbeSMarcel Moolenaar return 1; 628545ddfbeSMarcel Moolenaar #endif /* HAVE___FLBF */ 629545ddfbeSMarcel Moolenaar return 0; 630545ddfbeSMarcel Moolenaar } 631545ddfbeSMarcel Moolenaar 632545ddfbeSMarcel Moolenaar /* 63331337658SMarcel Moolenaar * Initialize an xo_handle_t, using both static defaults and 63431337658SMarcel Moolenaar * the global settings from the LIBXO_OPTIONS environment 63531337658SMarcel Moolenaar * variable. 63631337658SMarcel Moolenaar */ 63731337658SMarcel Moolenaar static void 63831337658SMarcel Moolenaar xo_init_handle (xo_handle_t *xop) 63931337658SMarcel Moolenaar { 64031337658SMarcel Moolenaar xop->xo_opaque = stdout; 64131337658SMarcel Moolenaar xop->xo_write = xo_write_to_file; 642545ddfbeSMarcel Moolenaar xop->xo_flush = xo_flush_file; 643545ddfbeSMarcel Moolenaar 644545ddfbeSMarcel Moolenaar if (xo_is_line_buffered(stdout)) 645d1a0d267SMarcel Moolenaar XOF_SET(xop, XOF_FLUSH_LINE); 64631337658SMarcel Moolenaar 64731337658SMarcel Moolenaar /* 64831337658SMarcel Moolenaar * We need to initialize the locale, which isn't really pretty. 64931337658SMarcel Moolenaar * Libraries should depend on their caller to set up the 65031337658SMarcel Moolenaar * environment. But we really can't count on the caller to do 65131337658SMarcel Moolenaar * this, because well, they won't. Trust me. 65231337658SMarcel Moolenaar */ 65331337658SMarcel Moolenaar if (!xo_locale_inited) { 65431337658SMarcel Moolenaar xo_locale_inited = 1; /* Only do this once */ 65531337658SMarcel Moolenaar 65631337658SMarcel Moolenaar const char *cp = getenv("LC_CTYPE"); 65731337658SMarcel Moolenaar if (cp == NULL) 65831337658SMarcel Moolenaar cp = getenv("LANG"); 65931337658SMarcel Moolenaar if (cp == NULL) 66031337658SMarcel Moolenaar cp = getenv("LC_ALL"); 66131337658SMarcel Moolenaar if (cp == NULL) 662d1a0d267SMarcel Moolenaar cp = "C"; /* Default for C programs */ 663c600d307SMarcel Moolenaar (void) setlocale(LC_CTYPE, cp); 66431337658SMarcel Moolenaar } 66531337658SMarcel Moolenaar 66631337658SMarcel Moolenaar /* 66731337658SMarcel Moolenaar * Initialize only the xo_buffers we know we'll need; the others 66831337658SMarcel Moolenaar * can be allocated as needed. 66931337658SMarcel Moolenaar */ 67031337658SMarcel Moolenaar xo_buf_init(&xop->xo_data); 67131337658SMarcel Moolenaar xo_buf_init(&xop->xo_fmt); 67231337658SMarcel Moolenaar 673d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_INIT_IN_PROGRESS)) 674d1a0d267SMarcel Moolenaar return; 675d1a0d267SMarcel Moolenaar XOIF_SET(xop, XOIF_INIT_IN_PROGRESS); 676d1a0d267SMarcel Moolenaar 67731337658SMarcel Moolenaar xop->xo_indent_by = XO_INDENT_BY; 67831337658SMarcel Moolenaar xo_depth_check(xop, XO_DEPTH); 67931337658SMarcel Moolenaar 680d1a0d267SMarcel Moolenaar XOIF_CLEAR(xop, XOIF_INIT_IN_PROGRESS); 68131337658SMarcel Moolenaar } 68231337658SMarcel Moolenaar 68331337658SMarcel Moolenaar /* 68431337658SMarcel Moolenaar * Initialize the default handle. 68531337658SMarcel Moolenaar */ 68631337658SMarcel Moolenaar static void 68731337658SMarcel Moolenaar xo_default_init (void) 68831337658SMarcel Moolenaar { 68931337658SMarcel Moolenaar xo_handle_t *xop = &xo_default_handle; 69031337658SMarcel Moolenaar 69131337658SMarcel Moolenaar xo_init_handle(xop); 69231337658SMarcel Moolenaar 693f2b7bf8aSPhil Shafer #if !defined(NO_LIBXO_OPTIONS) 694f2b7bf8aSPhil Shafer if (!XOF_ISSET(xop, XOF_NO_ENV)) { 695f2b7bf8aSPhil Shafer char *env = getenv("LIBXO_OPTIONS"); 6962f784130SPhil Shafer 697f2b7bf8aSPhil Shafer if (env) 698f2b7bf8aSPhil Shafer xo_set_options_simple(xop, env); 699f2b7bf8aSPhil Shafer 700f2b7bf8aSPhil Shafer } 701f2b7bf8aSPhil Shafer #endif /* NO_LIBXO_OPTIONS */ 702f2b7bf8aSPhil Shafer 70331337658SMarcel Moolenaar xo_default_inited = 1; 70431337658SMarcel Moolenaar } 70531337658SMarcel Moolenaar 70631337658SMarcel Moolenaar /* 70731337658SMarcel Moolenaar * Cheap convenience function to return either the argument, or 70831337658SMarcel Moolenaar * the internal handle, after it has been initialized. The usage 70931337658SMarcel Moolenaar * is: 71031337658SMarcel Moolenaar * xop = xo_default(xop); 71131337658SMarcel Moolenaar */ 71231337658SMarcel Moolenaar static xo_handle_t * 71331337658SMarcel Moolenaar xo_default (xo_handle_t *xop) 71431337658SMarcel Moolenaar { 71531337658SMarcel Moolenaar if (xop == NULL) { 71631337658SMarcel Moolenaar if (xo_default_inited == 0) 71731337658SMarcel Moolenaar xo_default_init(); 71831337658SMarcel Moolenaar xop = &xo_default_handle; 71931337658SMarcel Moolenaar } 72031337658SMarcel Moolenaar 72131337658SMarcel Moolenaar return xop; 72231337658SMarcel Moolenaar } 72331337658SMarcel Moolenaar 72431337658SMarcel Moolenaar /* 72531337658SMarcel Moolenaar * Return the number of spaces we should be indenting. If 726788ca347SMarcel Moolenaar * we are pretty-printing, this is indent * indent_by. 72731337658SMarcel Moolenaar */ 72831337658SMarcel Moolenaar static int 72931337658SMarcel Moolenaar xo_indent (xo_handle_t *xop) 73031337658SMarcel Moolenaar { 73131337658SMarcel Moolenaar int rc = 0; 73231337658SMarcel Moolenaar 73331337658SMarcel Moolenaar xop = xo_default(xop); 73431337658SMarcel Moolenaar 735d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_PRETTY)) { 73631337658SMarcel Moolenaar rc = xop->xo_indent * xop->xo_indent_by; 737d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_TOP_EMITTED)) 73831337658SMarcel Moolenaar rc += xop->xo_indent_by; 73931337658SMarcel Moolenaar } 74031337658SMarcel Moolenaar 741545ddfbeSMarcel Moolenaar return (rc > 0) ? rc : 0; 74231337658SMarcel Moolenaar } 74331337658SMarcel Moolenaar 74431337658SMarcel Moolenaar static void 74531337658SMarcel Moolenaar xo_buf_indent (xo_handle_t *xop, int indent) 74631337658SMarcel Moolenaar { 74731337658SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_data; 74831337658SMarcel Moolenaar 74931337658SMarcel Moolenaar if (indent <= 0) 75031337658SMarcel Moolenaar indent = xo_indent(xop); 75131337658SMarcel Moolenaar 75231337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, indent)) 75331337658SMarcel Moolenaar return; 75431337658SMarcel Moolenaar 75531337658SMarcel Moolenaar memset(xbp->xb_curp, ' ', indent); 75631337658SMarcel Moolenaar xbp->xb_curp += indent; 75731337658SMarcel Moolenaar } 75831337658SMarcel Moolenaar 75931337658SMarcel Moolenaar static char xo_xml_amp[] = "&"; 76031337658SMarcel Moolenaar static char xo_xml_lt[] = "<"; 76131337658SMarcel Moolenaar static char xo_xml_gt[] = ">"; 76231337658SMarcel Moolenaar static char xo_xml_quot[] = """; 76331337658SMarcel Moolenaar 7648a6eceffSPhil Shafer static ssize_t 7658a6eceffSPhil Shafer xo_escape_xml (xo_buffer_t *xbp, ssize_t len, xo_xff_flags_t flags) 76631337658SMarcel Moolenaar { 7678a6eceffSPhil Shafer ssize_t slen; 7688a6eceffSPhil Shafer ssize_t delta = 0; 76931337658SMarcel Moolenaar char *cp, *ep, *ip; 77031337658SMarcel Moolenaar const char *sp; 7718a6eceffSPhil Shafer int attr = XOF_BIT_ISSET(flags, XFF_ATTR); 77231337658SMarcel Moolenaar 77331337658SMarcel Moolenaar for (cp = xbp->xb_curp, ep = cp + len; cp < ep; cp++) { 77431337658SMarcel Moolenaar /* We're subtracting 2: 1 for the NUL, 1 for the char we replace */ 77531337658SMarcel Moolenaar if (*cp == '<') 77631337658SMarcel Moolenaar delta += sizeof(xo_xml_lt) - 2; 77731337658SMarcel Moolenaar else if (*cp == '>') 77831337658SMarcel Moolenaar delta += sizeof(xo_xml_gt) - 2; 77931337658SMarcel Moolenaar else if (*cp == '&') 78031337658SMarcel Moolenaar delta += sizeof(xo_xml_amp) - 2; 78131337658SMarcel Moolenaar else if (attr && *cp == '"') 78231337658SMarcel Moolenaar delta += sizeof(xo_xml_quot) - 2; 78331337658SMarcel Moolenaar } 78431337658SMarcel Moolenaar 78531337658SMarcel Moolenaar if (delta == 0) /* Nothing to escape; bail */ 78631337658SMarcel Moolenaar return len; 78731337658SMarcel Moolenaar 78831337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, delta)) /* No room; bail, but don't append */ 78931337658SMarcel Moolenaar return 0; 79031337658SMarcel Moolenaar 79131337658SMarcel Moolenaar ep = xbp->xb_curp; 79231337658SMarcel Moolenaar cp = ep + len; 79331337658SMarcel Moolenaar ip = cp + delta; 79431337658SMarcel Moolenaar do { 79531337658SMarcel Moolenaar cp -= 1; 79631337658SMarcel Moolenaar ip -= 1; 79731337658SMarcel Moolenaar 79831337658SMarcel Moolenaar if (*cp == '<') 79931337658SMarcel Moolenaar sp = xo_xml_lt; 80031337658SMarcel Moolenaar else if (*cp == '>') 80131337658SMarcel Moolenaar sp = xo_xml_gt; 80231337658SMarcel Moolenaar else if (*cp == '&') 80331337658SMarcel Moolenaar sp = xo_xml_amp; 80431337658SMarcel Moolenaar else if (attr && *cp == '"') 80531337658SMarcel Moolenaar sp = xo_xml_quot; 80631337658SMarcel Moolenaar else { 80731337658SMarcel Moolenaar *ip = *cp; 80831337658SMarcel Moolenaar continue; 80931337658SMarcel Moolenaar } 81031337658SMarcel Moolenaar 81131337658SMarcel Moolenaar slen = strlen(sp); 81231337658SMarcel Moolenaar ip -= slen - 1; 81331337658SMarcel Moolenaar memcpy(ip, sp, slen); 81431337658SMarcel Moolenaar 81531337658SMarcel Moolenaar } while (cp > ep && cp != ip); 81631337658SMarcel Moolenaar 81731337658SMarcel Moolenaar return len + delta; 81831337658SMarcel Moolenaar } 81931337658SMarcel Moolenaar 8208a6eceffSPhil Shafer static ssize_t 8218a6eceffSPhil Shafer xo_escape_json (xo_buffer_t *xbp, ssize_t len, xo_xff_flags_t flags UNUSED) 82231337658SMarcel Moolenaar { 8238a6eceffSPhil Shafer ssize_t delta = 0; 82431337658SMarcel Moolenaar char *cp, *ep, *ip; 82531337658SMarcel Moolenaar 82631337658SMarcel Moolenaar for (cp = xbp->xb_curp, ep = cp + len; cp < ep; cp++) { 827545ddfbeSMarcel Moolenaar if (*cp == '\\' || *cp == '"') 82831337658SMarcel Moolenaar delta += 1; 829545ddfbeSMarcel Moolenaar else if (*cp == '\n' || *cp == '\r') 83031337658SMarcel Moolenaar delta += 1; 83131337658SMarcel Moolenaar } 83231337658SMarcel Moolenaar 83331337658SMarcel Moolenaar if (delta == 0) /* Nothing to escape; bail */ 83431337658SMarcel Moolenaar return len; 83531337658SMarcel Moolenaar 83631337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, delta)) /* No room; bail, but don't append */ 83731337658SMarcel Moolenaar return 0; 83831337658SMarcel Moolenaar 83931337658SMarcel Moolenaar ep = xbp->xb_curp; 84031337658SMarcel Moolenaar cp = ep + len; 84131337658SMarcel Moolenaar ip = cp + delta; 84231337658SMarcel Moolenaar do { 84331337658SMarcel Moolenaar cp -= 1; 84431337658SMarcel Moolenaar ip -= 1; 84531337658SMarcel Moolenaar 846545ddfbeSMarcel Moolenaar if (*cp == '\\' || *cp == '"') { 84731337658SMarcel Moolenaar *ip-- = *cp; 84831337658SMarcel Moolenaar *ip = '\\'; 849545ddfbeSMarcel Moolenaar } else if (*cp == '\n') { 850545ddfbeSMarcel Moolenaar *ip-- = 'n'; 851545ddfbeSMarcel Moolenaar *ip = '\\'; 852545ddfbeSMarcel Moolenaar } else if (*cp == '\r') { 853545ddfbeSMarcel Moolenaar *ip-- = 'r'; 854545ddfbeSMarcel Moolenaar *ip = '\\'; 855545ddfbeSMarcel Moolenaar } else { 856545ddfbeSMarcel Moolenaar *ip = *cp; 857545ddfbeSMarcel Moolenaar } 85831337658SMarcel Moolenaar 85931337658SMarcel Moolenaar } while (cp > ep && cp != ip); 86031337658SMarcel Moolenaar 86131337658SMarcel Moolenaar return len + delta; 86231337658SMarcel Moolenaar } 86331337658SMarcel Moolenaar 86431337658SMarcel Moolenaar /* 865d1a0d267SMarcel Moolenaar * PARAM-VALUE = UTF-8-STRING ; characters '"', '\' and 866d1a0d267SMarcel Moolenaar * ; ']' MUST be escaped. 86731337658SMarcel Moolenaar */ 8688a6eceffSPhil Shafer static ssize_t 8698a6eceffSPhil Shafer xo_escape_sdparams (xo_buffer_t *xbp, ssize_t len, xo_xff_flags_t flags UNUSED) 87031337658SMarcel Moolenaar { 8718a6eceffSPhil Shafer ssize_t delta = 0; 872d1a0d267SMarcel Moolenaar char *cp, *ep, *ip; 87331337658SMarcel Moolenaar 874d1a0d267SMarcel Moolenaar for (cp = xbp->xb_curp, ep = cp + len; cp < ep; cp++) { 875d1a0d267SMarcel Moolenaar if (*cp == '\\' || *cp == '"' || *cp == ']') 876d1a0d267SMarcel Moolenaar delta += 1; 87731337658SMarcel Moolenaar } 87831337658SMarcel Moolenaar 879d1a0d267SMarcel Moolenaar if (delta == 0) /* Nothing to escape; bail */ 880d1a0d267SMarcel Moolenaar return len; 881788ca347SMarcel Moolenaar 882d1a0d267SMarcel Moolenaar if (!xo_buf_has_room(xbp, delta)) /* No room; bail, but don't append */ 883d1a0d267SMarcel Moolenaar return 0; 884788ca347SMarcel Moolenaar 885d1a0d267SMarcel Moolenaar ep = xbp->xb_curp; 886d1a0d267SMarcel Moolenaar cp = ep + len; 887d1a0d267SMarcel Moolenaar ip = cp + delta; 888d1a0d267SMarcel Moolenaar do { 889d1a0d267SMarcel Moolenaar cp -= 1; 890d1a0d267SMarcel Moolenaar ip -= 1; 891d1a0d267SMarcel Moolenaar 892d1a0d267SMarcel Moolenaar if (*cp == '\\' || *cp == '"' || *cp == ']') { 893d1a0d267SMarcel Moolenaar *ip-- = *cp; 894d1a0d267SMarcel Moolenaar *ip = '\\'; 895d1a0d267SMarcel Moolenaar } else { 896d1a0d267SMarcel Moolenaar *ip = *cp; 897d1a0d267SMarcel Moolenaar } 898d1a0d267SMarcel Moolenaar 899d1a0d267SMarcel Moolenaar } while (cp > ep && cp != ip); 900d1a0d267SMarcel Moolenaar 901d1a0d267SMarcel Moolenaar return len + delta; 902788ca347SMarcel Moolenaar } 903788ca347SMarcel Moolenaar 90431337658SMarcel Moolenaar static void 90531337658SMarcel Moolenaar xo_buf_escape (xo_handle_t *xop, xo_buffer_t *xbp, 9068a6eceffSPhil Shafer const char *str, ssize_t len, xo_xff_flags_t flags) 90731337658SMarcel Moolenaar { 90831337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, len)) 90931337658SMarcel Moolenaar return; 91031337658SMarcel Moolenaar 91131337658SMarcel Moolenaar memcpy(xbp->xb_curp, str, len); 91231337658SMarcel Moolenaar 913788ca347SMarcel Moolenaar switch (xo_style(xop)) { 91431337658SMarcel Moolenaar case XO_STYLE_XML: 91531337658SMarcel Moolenaar case XO_STYLE_HTML: 916d1a0d267SMarcel Moolenaar len = xo_escape_xml(xbp, len, flags); 91731337658SMarcel Moolenaar break; 91831337658SMarcel Moolenaar 91931337658SMarcel Moolenaar case XO_STYLE_JSON: 920d1a0d267SMarcel Moolenaar len = xo_escape_json(xbp, len, flags); 921d1a0d267SMarcel Moolenaar break; 922d1a0d267SMarcel Moolenaar 923d1a0d267SMarcel Moolenaar case XO_STYLE_SDPARAMS: 924d1a0d267SMarcel Moolenaar len = xo_escape_sdparams(xbp, len, flags); 92531337658SMarcel Moolenaar break; 92631337658SMarcel Moolenaar } 92731337658SMarcel Moolenaar 92831337658SMarcel Moolenaar xbp->xb_curp += len; 92931337658SMarcel Moolenaar } 93031337658SMarcel Moolenaar 93131337658SMarcel Moolenaar /* 93231337658SMarcel Moolenaar * Write the current contents of the data buffer using the handle's 93331337658SMarcel Moolenaar * xo_write function. 93431337658SMarcel Moolenaar */ 9358a6eceffSPhil Shafer static ssize_t 93631337658SMarcel Moolenaar xo_write (xo_handle_t *xop) 93731337658SMarcel Moolenaar { 9388a6eceffSPhil Shafer ssize_t rc = 0; 93931337658SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_data; 94031337658SMarcel Moolenaar 94131337658SMarcel Moolenaar if (xbp->xb_curp != xbp->xb_bufp) { 94231337658SMarcel Moolenaar xo_buf_append(xbp, "", 1); /* Append ending NUL */ 94331337658SMarcel Moolenaar xo_anchor_clear(xop); 944d1a0d267SMarcel Moolenaar if (xop->xo_write) 945545ddfbeSMarcel Moolenaar rc = xop->xo_write(xop->xo_opaque, xbp->xb_bufp); 94631337658SMarcel Moolenaar xbp->xb_curp = xbp->xb_bufp; 94731337658SMarcel Moolenaar } 94831337658SMarcel Moolenaar 94931337658SMarcel Moolenaar /* Turn off the flags that don't survive across writes */ 950d1a0d267SMarcel Moolenaar XOIF_CLEAR(xop, XOIF_UNITS_PENDING); 951545ddfbeSMarcel Moolenaar 952545ddfbeSMarcel Moolenaar return rc; 95331337658SMarcel Moolenaar } 95431337658SMarcel Moolenaar 95531337658SMarcel Moolenaar /* 95631337658SMarcel Moolenaar * Format arguments into our buffer. If a custom formatter has been set, 95731337658SMarcel Moolenaar * we use that to do the work; otherwise we vsnprintf(). 95831337658SMarcel Moolenaar */ 9598a6eceffSPhil Shafer static ssize_t 96031337658SMarcel Moolenaar xo_vsnprintf (xo_handle_t *xop, xo_buffer_t *xbp, const char *fmt, va_list vap) 96131337658SMarcel Moolenaar { 96231337658SMarcel Moolenaar va_list va_local; 9638a6eceffSPhil Shafer ssize_t rc; 9648a6eceffSPhil Shafer ssize_t left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp); 96531337658SMarcel Moolenaar 96631337658SMarcel Moolenaar va_copy(va_local, vap); 96731337658SMarcel Moolenaar 96831337658SMarcel Moolenaar if (xop->xo_formatter) 96931337658SMarcel Moolenaar rc = xop->xo_formatter(xop, xbp->xb_curp, left, fmt, va_local); 97031337658SMarcel Moolenaar else 97131337658SMarcel Moolenaar rc = vsnprintf(xbp->xb_curp, left, fmt, va_local); 97231337658SMarcel Moolenaar 973788ca347SMarcel Moolenaar if (rc >= left) { 974c600d307SMarcel Moolenaar if (!xo_buf_has_room(xbp, rc)) { 975c600d307SMarcel Moolenaar va_end(va_local); 97631337658SMarcel Moolenaar return -1; 977c600d307SMarcel Moolenaar } 97831337658SMarcel Moolenaar 97931337658SMarcel Moolenaar /* 98031337658SMarcel Moolenaar * After we call vsnprintf(), the stage of vap is not defined. 98131337658SMarcel Moolenaar * We need to copy it before we pass. Then we have to do our 98231337658SMarcel Moolenaar * own logic below to move it along. This is because the 983788ca347SMarcel Moolenaar * implementation can have va_list be a pointer (bsd) or a 98431337658SMarcel Moolenaar * structure (macosx) or anything in between. 98531337658SMarcel Moolenaar */ 98631337658SMarcel Moolenaar 98731337658SMarcel Moolenaar va_end(va_local); /* Reset vap to the start */ 98831337658SMarcel Moolenaar va_copy(va_local, vap); 98931337658SMarcel Moolenaar 99031337658SMarcel Moolenaar left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp); 99131337658SMarcel Moolenaar if (xop->xo_formatter) 992788ca347SMarcel Moolenaar rc = xop->xo_formatter(xop, xbp->xb_curp, left, fmt, va_local); 99331337658SMarcel Moolenaar else 99431337658SMarcel Moolenaar rc = vsnprintf(xbp->xb_curp, left, fmt, va_local); 99531337658SMarcel Moolenaar } 99631337658SMarcel Moolenaar va_end(va_local); 99731337658SMarcel Moolenaar 99831337658SMarcel Moolenaar return rc; 99931337658SMarcel Moolenaar } 100031337658SMarcel Moolenaar 100131337658SMarcel Moolenaar /* 1002ee5cf116SPhil Shafer * Print some data through the handle. 100331337658SMarcel Moolenaar */ 10048a6eceffSPhil Shafer static ssize_t 100531337658SMarcel Moolenaar xo_printf_v (xo_handle_t *xop, const char *fmt, va_list vap) 100631337658SMarcel Moolenaar { 100731337658SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_data; 10088a6eceffSPhil Shafer ssize_t left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp); 10098a6eceffSPhil Shafer ssize_t rc; 101031337658SMarcel Moolenaar va_list va_local; 101131337658SMarcel Moolenaar 101231337658SMarcel Moolenaar va_copy(va_local, vap); 101331337658SMarcel Moolenaar 101431337658SMarcel Moolenaar rc = vsnprintf(xbp->xb_curp, left, fmt, va_local); 101531337658SMarcel Moolenaar 1016d1a0d267SMarcel Moolenaar if (rc >= left) { 1017c600d307SMarcel Moolenaar if (!xo_buf_has_room(xbp, rc)) { 1018c600d307SMarcel Moolenaar va_end(va_local); 101931337658SMarcel Moolenaar return -1; 1020c600d307SMarcel Moolenaar } 102131337658SMarcel Moolenaar 102231337658SMarcel Moolenaar va_end(va_local); /* Reset vap to the start */ 102331337658SMarcel Moolenaar va_copy(va_local, vap); 102431337658SMarcel Moolenaar 102531337658SMarcel Moolenaar left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp); 102631337658SMarcel Moolenaar rc = vsnprintf(xbp->xb_curp, left, fmt, va_local); 102731337658SMarcel Moolenaar } 102831337658SMarcel Moolenaar 102931337658SMarcel Moolenaar va_end(va_local); 103031337658SMarcel Moolenaar 103131337658SMarcel Moolenaar if (rc > 0) 103231337658SMarcel Moolenaar xbp->xb_curp += rc; 103331337658SMarcel Moolenaar 103431337658SMarcel Moolenaar return rc; 103531337658SMarcel Moolenaar } 103631337658SMarcel Moolenaar 10378a6eceffSPhil Shafer static ssize_t 103831337658SMarcel Moolenaar xo_printf (xo_handle_t *xop, const char *fmt, ...) 103931337658SMarcel Moolenaar { 10408a6eceffSPhil Shafer ssize_t rc; 104131337658SMarcel Moolenaar va_list vap; 104231337658SMarcel Moolenaar 104331337658SMarcel Moolenaar va_start(vap, fmt); 104431337658SMarcel Moolenaar 104531337658SMarcel Moolenaar rc = xo_printf_v(xop, fmt, vap); 104631337658SMarcel Moolenaar 104731337658SMarcel Moolenaar va_end(vap); 104831337658SMarcel Moolenaar return rc; 104931337658SMarcel Moolenaar } 105031337658SMarcel Moolenaar 105131337658SMarcel Moolenaar /* 105231337658SMarcel Moolenaar * These next few function are make The Essential UTF-8 Ginsu Knife. 105331337658SMarcel Moolenaar * Identify an input and output character, and convert it. 105431337658SMarcel Moolenaar */ 1055f2b7bf8aSPhil Shafer static uint8_t xo_utf8_data_bits[5] = { 0, 0x7f, 0x1f, 0x0f, 0x07 }; 1056f2b7bf8aSPhil Shafer static uint8_t xo_utf8_len_bits[5] = { 0, 0x00, 0xc0, 0xe0, 0xf0 }; 105731337658SMarcel Moolenaar 1058f2b7bf8aSPhil Shafer /* 1059f2b7bf8aSPhil Shafer * If the byte has a high-bit set, it's UTF-8, not ASCII. 1060f2b7bf8aSPhil Shafer */ 106131337658SMarcel Moolenaar static int 106231337658SMarcel Moolenaar xo_is_utf8 (char ch) 106331337658SMarcel Moolenaar { 106431337658SMarcel Moolenaar return (ch & 0x80); 106531337658SMarcel Moolenaar } 106631337658SMarcel Moolenaar 1067f2b7bf8aSPhil Shafer /* 1068f2b7bf8aSPhil Shafer * Look at the high bits of the first byte to determine the length 1069f2b7bf8aSPhil Shafer * of the UTF-8 character. 1070f2b7bf8aSPhil Shafer */ 10718a6eceffSPhil Shafer static inline ssize_t 107231337658SMarcel Moolenaar xo_utf8_to_wc_len (const char *buf) 107331337658SMarcel Moolenaar { 1074f2b7bf8aSPhil Shafer uint8_t bval = (uint8_t) *buf; 10758a6eceffSPhil Shafer ssize_t len; 107631337658SMarcel Moolenaar 1077f2b7bf8aSPhil Shafer if ((bval & 0x80) == 0x0) 107831337658SMarcel Moolenaar len = 1; 1079f2b7bf8aSPhil Shafer else if ((bval & 0xe0) == 0xc0) 108031337658SMarcel Moolenaar len = 2; 1081f2b7bf8aSPhil Shafer else if ((bval & 0xf0) == 0xe0) 108231337658SMarcel Moolenaar len = 3; 1083f2b7bf8aSPhil Shafer else if ((bval & 0xf8) == 0xf0) 108431337658SMarcel Moolenaar len = 4; 108531337658SMarcel Moolenaar else 108631337658SMarcel Moolenaar len = -1; 108731337658SMarcel Moolenaar 108831337658SMarcel Moolenaar return len; 108931337658SMarcel Moolenaar } 109031337658SMarcel Moolenaar 10918a6eceffSPhil Shafer static ssize_t 10928a6eceffSPhil Shafer xo_buf_utf8_len (xo_handle_t *xop, const char *buf, ssize_t bufsiz) 109331337658SMarcel Moolenaar { 109431337658SMarcel Moolenaar unsigned b = (unsigned char) *buf; 10958a6eceffSPhil Shafer ssize_t len, i; 109631337658SMarcel Moolenaar 109731337658SMarcel Moolenaar len = xo_utf8_to_wc_len(buf); 1098f2b7bf8aSPhil Shafer if (len < 0) { 109931337658SMarcel Moolenaar xo_failure(xop, "invalid UTF-8 data: %02hhx", b); 110031337658SMarcel Moolenaar return -1; 110131337658SMarcel Moolenaar } 110231337658SMarcel Moolenaar 110331337658SMarcel Moolenaar if (len > bufsiz) { 110431337658SMarcel Moolenaar xo_failure(xop, "invalid UTF-8 data (short): %02hhx (%d/%d)", 110531337658SMarcel Moolenaar b, len, bufsiz); 110631337658SMarcel Moolenaar return -1; 110731337658SMarcel Moolenaar } 110831337658SMarcel Moolenaar 110931337658SMarcel Moolenaar for (i = 2; i < len; i++) { 111031337658SMarcel Moolenaar b = (unsigned char ) buf[i]; 111131337658SMarcel Moolenaar if ((b & 0xc0) != 0x80) { 111231337658SMarcel Moolenaar xo_failure(xop, "invalid UTF-8 data (byte %d): %x", i, b); 111331337658SMarcel Moolenaar return -1; 111431337658SMarcel Moolenaar } 111531337658SMarcel Moolenaar } 111631337658SMarcel Moolenaar 111731337658SMarcel Moolenaar return len; 111831337658SMarcel Moolenaar } 111931337658SMarcel Moolenaar 112031337658SMarcel Moolenaar /* 112131337658SMarcel Moolenaar * Build a wide character from the input buffer; the number of 112231337658SMarcel Moolenaar * bits we pull off the first character is dependent on the length, 112331337658SMarcel Moolenaar * but we put 6 bits off all other bytes. 112431337658SMarcel Moolenaar */ 112542ff34c3SPhil Shafer static inline wchar_t 11268a6eceffSPhil Shafer xo_utf8_char (const char *buf, ssize_t len) 112731337658SMarcel Moolenaar { 112842ff34c3SPhil Shafer /* Most common case: singleton byte */ 112942ff34c3SPhil Shafer if (len == 1) 113042ff34c3SPhil Shafer return (unsigned char) buf[0]; 113142ff34c3SPhil Shafer 11328a6eceffSPhil Shafer ssize_t i; 113331337658SMarcel Moolenaar wchar_t wc; 113431337658SMarcel Moolenaar const unsigned char *cp = (const unsigned char *) buf; 113531337658SMarcel Moolenaar 1136f2b7bf8aSPhil Shafer wc = *cp & xo_utf8_data_bits[len]; 113731337658SMarcel Moolenaar for (i = 1; i < len; i++) { 1138f2b7bf8aSPhil Shafer wc <<= 6; /* Low six bits have data */ 113931337658SMarcel Moolenaar wc |= cp[i] & 0x3f; 114031337658SMarcel Moolenaar if ((cp[i] & 0xc0) != 0x80) 114131337658SMarcel Moolenaar return (wchar_t) -1; 114231337658SMarcel Moolenaar } 114331337658SMarcel Moolenaar 114431337658SMarcel Moolenaar return wc; 114531337658SMarcel Moolenaar } 114631337658SMarcel Moolenaar 114731337658SMarcel Moolenaar /* 114831337658SMarcel Moolenaar * Determine the number of bytes needed to encode a wide character. 114931337658SMarcel Moolenaar */ 11508a6eceffSPhil Shafer static ssize_t 115131337658SMarcel Moolenaar xo_utf8_emit_len (wchar_t wc) 115231337658SMarcel Moolenaar { 11538a6eceffSPhil Shafer ssize_t len; 115431337658SMarcel Moolenaar 115531337658SMarcel Moolenaar if ((wc & ((1 << 7) - 1)) == wc) /* Simple case */ 115631337658SMarcel Moolenaar len = 1; 115731337658SMarcel Moolenaar else if ((wc & ((1 << 11) - 1)) == wc) 115831337658SMarcel Moolenaar len = 2; 115931337658SMarcel Moolenaar else if ((wc & ((1 << 16) - 1)) == wc) 116031337658SMarcel Moolenaar len = 3; 116131337658SMarcel Moolenaar else if ((wc & ((1 << 21) - 1)) == wc) 116231337658SMarcel Moolenaar len = 4; 116331337658SMarcel Moolenaar else 1164f2b7bf8aSPhil Shafer len = -1; /* Invalid */ 116531337658SMarcel Moolenaar 116631337658SMarcel Moolenaar return len; 116731337658SMarcel Moolenaar } 116831337658SMarcel Moolenaar 1169f2b7bf8aSPhil Shafer /* 11702f784130SPhil Shafer * Emit one wide character into the given buffer 1171f2b7bf8aSPhil Shafer */ 117231337658SMarcel Moolenaar static void 11738a6eceffSPhil Shafer xo_utf8_emit_char (char *buf, ssize_t len, wchar_t wc) 117431337658SMarcel Moolenaar { 11758a6eceffSPhil Shafer ssize_t i; 117631337658SMarcel Moolenaar 117731337658SMarcel Moolenaar if (len == 1) { /* Simple case */ 117831337658SMarcel Moolenaar buf[0] = wc & 0x7f; 117931337658SMarcel Moolenaar return; 118031337658SMarcel Moolenaar } 118131337658SMarcel Moolenaar 11822f784130SPhil Shafer /* Start with the low bits and insert them, six bits at a time */ 118331337658SMarcel Moolenaar for (i = len - 1; i >= 0; i--) { 118431337658SMarcel Moolenaar buf[i] = 0x80 | (wc & 0x3f); 1185f2b7bf8aSPhil Shafer wc >>= 6; /* Drop the low six bits */ 118631337658SMarcel Moolenaar } 118731337658SMarcel Moolenaar 1188f2b7bf8aSPhil Shafer /* Finish off the first byte with the length bits */ 1189f2b7bf8aSPhil Shafer buf[0] &= xo_utf8_data_bits[len]; /* Clear out the length bits */ 1190f2b7bf8aSPhil Shafer buf[0] |= xo_utf8_len_bits[len]; /* Drop in new length bits */ 119131337658SMarcel Moolenaar } 119231337658SMarcel Moolenaar 1193f2b7bf8aSPhil Shafer /* 1194f2b7bf8aSPhil Shafer * Append a single UTF-8 character to a buffer, converting it to locale 1195f2b7bf8aSPhil Shafer * encoding. Returns the number of columns consumed by that character, 1196f2b7bf8aSPhil Shafer * as best we can determine it. 1197f2b7bf8aSPhil Shafer */ 11988a6eceffSPhil Shafer static ssize_t 119931337658SMarcel Moolenaar xo_buf_append_locale_from_utf8 (xo_handle_t *xop, xo_buffer_t *xbp, 12008a6eceffSPhil Shafer const char *ibuf, ssize_t ilen) 120131337658SMarcel Moolenaar { 120231337658SMarcel Moolenaar wchar_t wc; 12038a6eceffSPhil Shafer ssize_t len; 120431337658SMarcel Moolenaar 120531337658SMarcel Moolenaar /* 120631337658SMarcel Moolenaar * Build our wide character from the input buffer; the number of 120731337658SMarcel Moolenaar * bits we pull off the first character is dependent on the length, 120831337658SMarcel Moolenaar * but we put 6 bits off all other bytes. 120931337658SMarcel Moolenaar */ 121031337658SMarcel Moolenaar wc = xo_utf8_char(ibuf, ilen); 121131337658SMarcel Moolenaar if (wc == (wchar_t) -1) { 1212f2b7bf8aSPhil Shafer xo_failure(xop, "invalid UTF-8 byte sequence"); 121331337658SMarcel Moolenaar return 0; 121431337658SMarcel Moolenaar } 121531337658SMarcel Moolenaar 1216d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_NO_LOCALE)) { 121731337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, ilen)) 121831337658SMarcel Moolenaar return 0; 121931337658SMarcel Moolenaar 122031337658SMarcel Moolenaar memcpy(xbp->xb_curp, ibuf, ilen); 122131337658SMarcel Moolenaar xbp->xb_curp += ilen; 122231337658SMarcel Moolenaar 122331337658SMarcel Moolenaar } else { 122431337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, MB_LEN_MAX + 1)) 122531337658SMarcel Moolenaar return 0; 122631337658SMarcel Moolenaar 122731337658SMarcel Moolenaar bzero(&xop->xo_mbstate, sizeof(xop->xo_mbstate)); 122831337658SMarcel Moolenaar len = wcrtomb(xbp->xb_curp, wc, &xop->xo_mbstate); 122931337658SMarcel Moolenaar 123031337658SMarcel Moolenaar if (len <= 0) { 123131337658SMarcel Moolenaar xo_failure(xop, "could not convert wide char: %lx", 123231337658SMarcel Moolenaar (unsigned long) wc); 123331337658SMarcel Moolenaar return 0; 123431337658SMarcel Moolenaar } 123531337658SMarcel Moolenaar xbp->xb_curp += len; 123631337658SMarcel Moolenaar } 123731337658SMarcel Moolenaar 1238d1a0d267SMarcel Moolenaar return xo_wcwidth(wc); 123931337658SMarcel Moolenaar } 124031337658SMarcel Moolenaar 1241f2b7bf8aSPhil Shafer /* 1242f2b7bf8aSPhil Shafer * Append a UTF-8 string to a buffer, converting it into locale encoding 1243f2b7bf8aSPhil Shafer */ 124431337658SMarcel Moolenaar static void 124531337658SMarcel Moolenaar xo_buf_append_locale (xo_handle_t *xop, xo_buffer_t *xbp, 12468a6eceffSPhil Shafer const char *cp, ssize_t len) 124731337658SMarcel Moolenaar { 124831337658SMarcel Moolenaar const char *sp = cp, *ep = cp + len; 12498a6eceffSPhil Shafer ssize_t save_off = xbp->xb_bufp - xbp->xb_curp; 12508a6eceffSPhil Shafer ssize_t slen; 125131337658SMarcel Moolenaar int cols = 0; 125231337658SMarcel Moolenaar 125331337658SMarcel Moolenaar for ( ; cp < ep; cp++) { 125431337658SMarcel Moolenaar if (!xo_is_utf8(*cp)) { 125531337658SMarcel Moolenaar cols += 1; 125631337658SMarcel Moolenaar continue; 125731337658SMarcel Moolenaar } 125831337658SMarcel Moolenaar 125931337658SMarcel Moolenaar /* 126031337658SMarcel Moolenaar * We're looking at a non-ascii UTF-8 character. 126131337658SMarcel Moolenaar * First we copy the previous data. 126231337658SMarcel Moolenaar * Then we need find the length and validate it. 126331337658SMarcel Moolenaar * Then we turn it into a wide string. 126431337658SMarcel Moolenaar * Then we turn it into a localized string. 126531337658SMarcel Moolenaar * Then we repeat. Isn't i18n fun? 126631337658SMarcel Moolenaar */ 126731337658SMarcel Moolenaar if (sp != cp) 126831337658SMarcel Moolenaar xo_buf_append(xbp, sp, cp - sp); /* Append previous data */ 126931337658SMarcel Moolenaar 127031337658SMarcel Moolenaar slen = xo_buf_utf8_len(xop, cp, ep - cp); 127131337658SMarcel Moolenaar if (slen <= 0) { 127231337658SMarcel Moolenaar /* Bad data; back it all out */ 127331337658SMarcel Moolenaar xbp->xb_curp = xbp->xb_bufp + save_off; 127431337658SMarcel Moolenaar return; 127531337658SMarcel Moolenaar } 127631337658SMarcel Moolenaar 127731337658SMarcel Moolenaar cols += xo_buf_append_locale_from_utf8(xop, xbp, cp, slen); 127831337658SMarcel Moolenaar 1279ee5cf116SPhil Shafer /* Next time through, we'll start at the next character */ 128031337658SMarcel Moolenaar cp += slen - 1; 128131337658SMarcel Moolenaar sp = cp + 1; 128231337658SMarcel Moolenaar } 128331337658SMarcel Moolenaar 128431337658SMarcel Moolenaar /* Update column values */ 1285d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_COLUMNS)) 128631337658SMarcel Moolenaar xop->xo_columns += cols; 1287d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_ANCHOR)) 128831337658SMarcel Moolenaar xop->xo_anchor_columns += cols; 128931337658SMarcel Moolenaar 129031337658SMarcel Moolenaar /* Before we fall into the basic logic below, we need reset len */ 129131337658SMarcel Moolenaar len = ep - sp; 129231337658SMarcel Moolenaar if (len != 0) /* Append trailing data */ 129331337658SMarcel Moolenaar xo_buf_append(xbp, sp, len); 129431337658SMarcel Moolenaar } 129531337658SMarcel Moolenaar 129631337658SMarcel Moolenaar /* 1297d1a0d267SMarcel Moolenaar * Append the given string to the given buffer, without escaping or 1298d1a0d267SMarcel Moolenaar * character set conversion. This is the straight copy to the data 1299d1a0d267SMarcel Moolenaar * buffer with no fanciness. 130031337658SMarcel Moolenaar */ 130131337658SMarcel Moolenaar static void 13028a6eceffSPhil Shafer xo_data_append (xo_handle_t *xop, const char *str, ssize_t len) 130331337658SMarcel Moolenaar { 130431337658SMarcel Moolenaar xo_buf_append(&xop->xo_data, str, len); 130531337658SMarcel Moolenaar } 130631337658SMarcel Moolenaar 130731337658SMarcel Moolenaar /* 130831337658SMarcel Moolenaar * Append the given string to the given buffer 130931337658SMarcel Moolenaar */ 131031337658SMarcel Moolenaar static void 13118a6eceffSPhil Shafer xo_data_escape (xo_handle_t *xop, const char *str, ssize_t len) 131231337658SMarcel Moolenaar { 131331337658SMarcel Moolenaar xo_buf_escape(xop, &xop->xo_data, str, len, 0); 131431337658SMarcel Moolenaar } 131531337658SMarcel Moolenaar 131642ff34c3SPhil Shafer #ifdef LIBXO_NO_RETAIN 131742ff34c3SPhil Shafer /* 131842ff34c3SPhil Shafer * Empty implementations of the retain logic 131942ff34c3SPhil Shafer */ 132042ff34c3SPhil Shafer 132142ff34c3SPhil Shafer void 132242ff34c3SPhil Shafer xo_retain_clear_all (void) 132342ff34c3SPhil Shafer { 132442ff34c3SPhil Shafer return; 132542ff34c3SPhil Shafer } 132642ff34c3SPhil Shafer 132742ff34c3SPhil Shafer void 132842ff34c3SPhil Shafer xo_retain_clear (const char *fmt UNUSED) 132942ff34c3SPhil Shafer { 133042ff34c3SPhil Shafer return; 133142ff34c3SPhil Shafer } 133242ff34c3SPhil Shafer static void 133342ff34c3SPhil Shafer xo_retain_add (const char *fmt UNUSED, xo_field_info_t *fields UNUSED, 133442ff34c3SPhil Shafer unsigned num_fields UNUSED) 133542ff34c3SPhil Shafer { 133642ff34c3SPhil Shafer return; 133742ff34c3SPhil Shafer } 133842ff34c3SPhil Shafer 133942ff34c3SPhil Shafer static int 134042ff34c3SPhil Shafer xo_retain_find (const char *fmt UNUSED, xo_field_info_t **valp UNUSED, 134142ff34c3SPhil Shafer unsigned *nump UNUSED) 134242ff34c3SPhil Shafer { 134342ff34c3SPhil Shafer return -1; 134442ff34c3SPhil Shafer } 134542ff34c3SPhil Shafer 134642ff34c3SPhil Shafer #else /* !LIBXO_NO_RETAIN */ 134742ff34c3SPhil Shafer /* 134842ff34c3SPhil Shafer * Retain: We retain parsed field definitions to enhance performance, 134942ff34c3SPhil Shafer * especially inside loops. We depend on the caller treating the format 135042ff34c3SPhil Shafer * strings as immutable, so that we can retain pointers into them. We 135142ff34c3SPhil Shafer * hold the pointers in a hash table, so allow quick access. Retained 135242ff34c3SPhil Shafer * information is retained until xo_retain_clear is called. 135342ff34c3SPhil Shafer */ 135442ff34c3SPhil Shafer 135542ff34c3SPhil Shafer /* 135642ff34c3SPhil Shafer * xo_retain_entry_t holds information about one retained set of 135742ff34c3SPhil Shafer * parsed fields. 135842ff34c3SPhil Shafer */ 135942ff34c3SPhil Shafer typedef struct xo_retain_entry_s { 136042ff34c3SPhil Shafer struct xo_retain_entry_s *xre_next; /* Pointer to next (older) entry */ 136142ff34c3SPhil Shafer unsigned long xre_hits; /* Number of times we've hit */ 136242ff34c3SPhil Shafer const char *xre_format; /* Pointer to format string */ 136342ff34c3SPhil Shafer unsigned xre_num_fields; /* Number of fields saved */ 136442ff34c3SPhil Shafer xo_field_info_t *xre_fields; /* Pointer to fields */ 136542ff34c3SPhil Shafer } xo_retain_entry_t; 136642ff34c3SPhil Shafer 136742ff34c3SPhil Shafer /* 136842ff34c3SPhil Shafer * xo_retain_t holds a complete set of parsed fields as a hash table. 136942ff34c3SPhil Shafer */ 137042ff34c3SPhil Shafer #ifndef XO_RETAIN_SIZE 137142ff34c3SPhil Shafer #define XO_RETAIN_SIZE 6 137242ff34c3SPhil Shafer #endif /* XO_RETAIN_SIZE */ 137342ff34c3SPhil Shafer #define RETAIN_HASH_SIZE (1<<XO_RETAIN_SIZE) 137442ff34c3SPhil Shafer 137542ff34c3SPhil Shafer typedef struct xo_retain_s { 137642ff34c3SPhil Shafer xo_retain_entry_t *xr_bucket[RETAIN_HASH_SIZE]; 137742ff34c3SPhil Shafer } xo_retain_t; 137842ff34c3SPhil Shafer 137942ff34c3SPhil Shafer static THREAD_LOCAL(xo_retain_t) xo_retain; 138042ff34c3SPhil Shafer static THREAD_LOCAL(unsigned) xo_retain_count; 138142ff34c3SPhil Shafer 138242ff34c3SPhil Shafer /* 138342ff34c3SPhil Shafer * Simple hash function based on Thomas Wang's paper. The original is 138442ff34c3SPhil Shafer * gone, but an archive is available on the Way Back Machine: 138542ff34c3SPhil Shafer * 138642ff34c3SPhil Shafer * http://web.archive.org/web/20071223173210/\ 138742ff34c3SPhil Shafer * http://www.concentric.net/~Ttwang/tech/inthash.htm 138842ff34c3SPhil Shafer * 138942ff34c3SPhil Shafer * For our purposes, we can assume the low four bits are uninteresting 139042ff34c3SPhil Shafer * since any string less that 16 bytes wouldn't be worthy of 139142ff34c3SPhil Shafer * retaining. We toss the high bits also, since these bits are likely 139242ff34c3SPhil Shafer * to be common among constant format strings. We then run Wang's 139342ff34c3SPhil Shafer * algorithm, and cap the result at RETAIN_HASH_SIZE. 139442ff34c3SPhil Shafer */ 139542ff34c3SPhil Shafer static unsigned 139642ff34c3SPhil Shafer xo_retain_hash (const char *fmt) 139742ff34c3SPhil Shafer { 139842ff34c3SPhil Shafer volatile uintptr_t iptr = (uintptr_t) (const void *) fmt; 139942ff34c3SPhil Shafer 140042ff34c3SPhil Shafer /* Discard low four bits and high bits; they aren't interesting */ 140142ff34c3SPhil Shafer uint32_t val = (uint32_t) ((iptr >> 4) & (((1 << 24) - 1))); 140242ff34c3SPhil Shafer 140342ff34c3SPhil Shafer val = (val ^ 61) ^ (val >> 16); 140442ff34c3SPhil Shafer val = val + (val << 3); 140542ff34c3SPhil Shafer val = val ^ (val >> 4); 140642ff34c3SPhil Shafer val = val * 0x3a8f05c5; /* My large prime number */ 140742ff34c3SPhil Shafer val = val ^ (val >> 15); 140842ff34c3SPhil Shafer val &= RETAIN_HASH_SIZE - 1; 140942ff34c3SPhil Shafer 141042ff34c3SPhil Shafer return val; 141142ff34c3SPhil Shafer } 141242ff34c3SPhil Shafer 141342ff34c3SPhil Shafer /* 141442ff34c3SPhil Shafer * Walk all buckets, clearing all retained entries 141542ff34c3SPhil Shafer */ 141642ff34c3SPhil Shafer void 141742ff34c3SPhil Shafer xo_retain_clear_all (void) 141842ff34c3SPhil Shafer { 141942ff34c3SPhil Shafer int i; 142042ff34c3SPhil Shafer xo_retain_entry_t *xrep, *next; 142142ff34c3SPhil Shafer 142242ff34c3SPhil Shafer for (i = 0; i < RETAIN_HASH_SIZE; i++) { 142342ff34c3SPhil Shafer for (xrep = xo_retain.xr_bucket[i]; xrep; xrep = next) { 142442ff34c3SPhil Shafer next = xrep->xre_next; 142542ff34c3SPhil Shafer xo_free(xrep); 142642ff34c3SPhil Shafer } 142742ff34c3SPhil Shafer xo_retain.xr_bucket[i] = NULL; 142842ff34c3SPhil Shafer } 142942ff34c3SPhil Shafer xo_retain_count = 0; 143042ff34c3SPhil Shafer } 143142ff34c3SPhil Shafer 143242ff34c3SPhil Shafer /* 143342ff34c3SPhil Shafer * Walk all buckets, clearing all retained entries 143442ff34c3SPhil Shafer */ 143542ff34c3SPhil Shafer void 143642ff34c3SPhil Shafer xo_retain_clear (const char *fmt) 143742ff34c3SPhil Shafer { 143842ff34c3SPhil Shafer xo_retain_entry_t **xrepp; 143942ff34c3SPhil Shafer unsigned hash = xo_retain_hash(fmt); 144042ff34c3SPhil Shafer 144142ff34c3SPhil Shafer for (xrepp = &xo_retain.xr_bucket[hash]; *xrepp; 144242ff34c3SPhil Shafer xrepp = &(*xrepp)->xre_next) { 144342ff34c3SPhil Shafer if ((*xrepp)->xre_format == fmt) { 144442ff34c3SPhil Shafer *xrepp = (*xrepp)->xre_next; 144542ff34c3SPhil Shafer xo_retain_count -= 1; 144642ff34c3SPhil Shafer return; 144742ff34c3SPhil Shafer } 144842ff34c3SPhil Shafer } 144942ff34c3SPhil Shafer } 145042ff34c3SPhil Shafer 145142ff34c3SPhil Shafer /* 145242ff34c3SPhil Shafer * Search the hash for an entry matching 'fmt'; return it's fields. 145342ff34c3SPhil Shafer */ 145442ff34c3SPhil Shafer static int 145542ff34c3SPhil Shafer xo_retain_find (const char *fmt, xo_field_info_t **valp, unsigned *nump) 145642ff34c3SPhil Shafer { 145742ff34c3SPhil Shafer if (xo_retain_count == 0) 145842ff34c3SPhil Shafer return -1; 145942ff34c3SPhil Shafer 146042ff34c3SPhil Shafer unsigned hash = xo_retain_hash(fmt); 146142ff34c3SPhil Shafer xo_retain_entry_t *xrep; 146242ff34c3SPhil Shafer 146342ff34c3SPhil Shafer for (xrep = xo_retain.xr_bucket[hash]; xrep != NULL; 146442ff34c3SPhil Shafer xrep = xrep->xre_next) { 146542ff34c3SPhil Shafer if (xrep->xre_format == fmt) { 146642ff34c3SPhil Shafer *valp = xrep->xre_fields; 146742ff34c3SPhil Shafer *nump = xrep->xre_num_fields; 146842ff34c3SPhil Shafer xrep->xre_hits += 1; 146942ff34c3SPhil Shafer return 0; 147042ff34c3SPhil Shafer } 147142ff34c3SPhil Shafer } 147242ff34c3SPhil Shafer 147342ff34c3SPhil Shafer return -1; 147442ff34c3SPhil Shafer } 147542ff34c3SPhil Shafer 147642ff34c3SPhil Shafer static void 147742ff34c3SPhil Shafer xo_retain_add (const char *fmt, xo_field_info_t *fields, unsigned num_fields) 147842ff34c3SPhil Shafer { 147942ff34c3SPhil Shafer unsigned hash = xo_retain_hash(fmt); 148042ff34c3SPhil Shafer xo_retain_entry_t *xrep; 14818a6eceffSPhil Shafer ssize_t sz = sizeof(*xrep) + (num_fields + 1) * sizeof(*fields); 148242ff34c3SPhil Shafer xo_field_info_t *xfip; 148342ff34c3SPhil Shafer 148442ff34c3SPhil Shafer xrep = xo_realloc(NULL, sz); 148542ff34c3SPhil Shafer if (xrep == NULL) 148642ff34c3SPhil Shafer return; 148742ff34c3SPhil Shafer 148842ff34c3SPhil Shafer xfip = (xo_field_info_t *) &xrep[1]; 148942ff34c3SPhil Shafer memcpy(xfip, fields, num_fields * sizeof(*fields)); 149042ff34c3SPhil Shafer 149142ff34c3SPhil Shafer bzero(xrep, sizeof(*xrep)); 149242ff34c3SPhil Shafer 149342ff34c3SPhil Shafer xrep->xre_format = fmt; 149442ff34c3SPhil Shafer xrep->xre_fields = xfip; 149542ff34c3SPhil Shafer xrep->xre_num_fields = num_fields; 149642ff34c3SPhil Shafer 149742ff34c3SPhil Shafer /* Record the field info in the retain bucket */ 149842ff34c3SPhil Shafer xrep->xre_next = xo_retain.xr_bucket[hash]; 149942ff34c3SPhil Shafer xo_retain.xr_bucket[hash] = xrep; 150042ff34c3SPhil Shafer xo_retain_count += 1; 150142ff34c3SPhil Shafer } 150242ff34c3SPhil Shafer 150342ff34c3SPhil Shafer #endif /* !LIBXO_NO_RETAIN */ 150442ff34c3SPhil Shafer 150531337658SMarcel Moolenaar /* 150631337658SMarcel Moolenaar * Generate a warning. Normally, this is a text message written to 150731337658SMarcel Moolenaar * standard error. If the XOF_WARN_XML flag is set, then we generate 150831337658SMarcel Moolenaar * XMLified content on standard output. 150931337658SMarcel Moolenaar */ 151031337658SMarcel Moolenaar static void 151131337658SMarcel Moolenaar xo_warn_hcv (xo_handle_t *xop, int code, int check_warn, 151231337658SMarcel Moolenaar const char *fmt, va_list vap) 151331337658SMarcel Moolenaar { 151431337658SMarcel Moolenaar xop = xo_default(xop); 1515d1a0d267SMarcel Moolenaar if (check_warn && !XOF_ISSET(xop, XOF_WARN)) 151631337658SMarcel Moolenaar return; 151731337658SMarcel Moolenaar 151831337658SMarcel Moolenaar if (fmt == NULL) 151931337658SMarcel Moolenaar return; 152031337658SMarcel Moolenaar 15218a6eceffSPhil Shafer ssize_t len = strlen(fmt); 15228a6eceffSPhil Shafer ssize_t plen = xo_program ? strlen(xo_program) : 0; 1523545ddfbeSMarcel Moolenaar char *newfmt = alloca(len + 1 + plen + 2); /* NUL, and ": " */ 152431337658SMarcel Moolenaar 152531337658SMarcel Moolenaar if (plen) { 152631337658SMarcel Moolenaar memcpy(newfmt, xo_program, plen); 152731337658SMarcel Moolenaar newfmt[plen++] = ':'; 152831337658SMarcel Moolenaar newfmt[plen++] = ' '; 152931337658SMarcel Moolenaar } 15302f784130SPhil Shafer 153131337658SMarcel Moolenaar memcpy(newfmt + plen, fmt, len); 153231337658SMarcel Moolenaar newfmt[len + plen] = '\0'; 153331337658SMarcel Moolenaar 1534d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_WARN_XML)) { 153531337658SMarcel Moolenaar static char err_open[] = "<error>"; 153631337658SMarcel Moolenaar static char err_close[] = "</error>"; 153731337658SMarcel Moolenaar static char msg_open[] = "<message>"; 153831337658SMarcel Moolenaar static char msg_close[] = "</message>"; 153931337658SMarcel Moolenaar 154031337658SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_data; 154131337658SMarcel Moolenaar 154231337658SMarcel Moolenaar xo_buf_append(xbp, err_open, sizeof(err_open) - 1); 154331337658SMarcel Moolenaar xo_buf_append(xbp, msg_open, sizeof(msg_open) - 1); 154431337658SMarcel Moolenaar 154531337658SMarcel Moolenaar va_list va_local; 154631337658SMarcel Moolenaar va_copy(va_local, vap); 154731337658SMarcel Moolenaar 15488a6eceffSPhil Shafer ssize_t left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp); 15498a6eceffSPhil Shafer ssize_t rc = vsnprintf(xbp->xb_curp, left, newfmt, vap); 15502f784130SPhil Shafer 1551d1a0d267SMarcel Moolenaar if (rc >= left) { 1552c600d307SMarcel Moolenaar if (!xo_buf_has_room(xbp, rc)) { 1553c600d307SMarcel Moolenaar va_end(va_local); 155431337658SMarcel Moolenaar return; 1555c600d307SMarcel Moolenaar } 155631337658SMarcel Moolenaar 155731337658SMarcel Moolenaar va_end(vap); /* Reset vap to the start */ 155831337658SMarcel Moolenaar va_copy(vap, va_local); 155931337658SMarcel Moolenaar 156031337658SMarcel Moolenaar left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp); 156131337658SMarcel Moolenaar rc = vsnprintf(xbp->xb_curp, left, fmt, vap); 156231337658SMarcel Moolenaar } 15632f784130SPhil Shafer 156431337658SMarcel Moolenaar va_end(va_local); 156531337658SMarcel Moolenaar 156631337658SMarcel Moolenaar rc = xo_escape_xml(xbp, rc, 1); 156731337658SMarcel Moolenaar xbp->xb_curp += rc; 156831337658SMarcel Moolenaar 156931337658SMarcel Moolenaar xo_buf_append(xbp, msg_close, sizeof(msg_close) - 1); 157031337658SMarcel Moolenaar xo_buf_append(xbp, err_close, sizeof(err_close) - 1); 157131337658SMarcel Moolenaar 1572545ddfbeSMarcel Moolenaar if (code >= 0) { 157331337658SMarcel Moolenaar const char *msg = strerror(code); 15742f784130SPhil Shafer 157531337658SMarcel Moolenaar if (msg) { 157631337658SMarcel Moolenaar xo_buf_append(xbp, ": ", 2); 157731337658SMarcel Moolenaar xo_buf_append(xbp, msg, strlen(msg)); 157831337658SMarcel Moolenaar } 157931337658SMarcel Moolenaar } 158031337658SMarcel Moolenaar 1581d1a0d267SMarcel Moolenaar xo_buf_append(xbp, "\n", 1); /* Append newline and NUL to string */ 1582545ddfbeSMarcel Moolenaar (void) xo_write(xop); 158331337658SMarcel Moolenaar 158431337658SMarcel Moolenaar } else { 158531337658SMarcel Moolenaar vfprintf(stderr, newfmt, vap); 1586545ddfbeSMarcel Moolenaar if (code >= 0) { 1587545ddfbeSMarcel Moolenaar const char *msg = strerror(code); 15882f784130SPhil Shafer 1589545ddfbeSMarcel Moolenaar if (msg) 1590545ddfbeSMarcel Moolenaar fprintf(stderr, ": %s", msg); 1591545ddfbeSMarcel Moolenaar } 1592545ddfbeSMarcel Moolenaar fprintf(stderr, "\n"); 159331337658SMarcel Moolenaar } 159431337658SMarcel Moolenaar } 159531337658SMarcel Moolenaar 159631337658SMarcel Moolenaar void 159731337658SMarcel Moolenaar xo_warn_hc (xo_handle_t *xop, int code, const char *fmt, ...) 159831337658SMarcel Moolenaar { 159931337658SMarcel Moolenaar va_list vap; 160031337658SMarcel Moolenaar 160131337658SMarcel Moolenaar va_start(vap, fmt); 160231337658SMarcel Moolenaar xo_warn_hcv(xop, code, 0, fmt, vap); 160331337658SMarcel Moolenaar va_end(vap); 160431337658SMarcel Moolenaar } 160531337658SMarcel Moolenaar 160631337658SMarcel Moolenaar void 160731337658SMarcel Moolenaar xo_warn_c (int code, const char *fmt, ...) 160831337658SMarcel Moolenaar { 160931337658SMarcel Moolenaar va_list vap; 161031337658SMarcel Moolenaar 161131337658SMarcel Moolenaar va_start(vap, fmt); 1612545ddfbeSMarcel Moolenaar xo_warn_hcv(NULL, code, 0, fmt, vap); 161331337658SMarcel Moolenaar va_end(vap); 161431337658SMarcel Moolenaar } 161531337658SMarcel Moolenaar 161631337658SMarcel Moolenaar void 161731337658SMarcel Moolenaar xo_warn (const char *fmt, ...) 161831337658SMarcel Moolenaar { 161931337658SMarcel Moolenaar int code = errno; 162031337658SMarcel Moolenaar va_list vap; 162131337658SMarcel Moolenaar 162231337658SMarcel Moolenaar va_start(vap, fmt); 162331337658SMarcel Moolenaar xo_warn_hcv(NULL, code, 0, fmt, vap); 162431337658SMarcel Moolenaar va_end(vap); 162531337658SMarcel Moolenaar } 162631337658SMarcel Moolenaar 162731337658SMarcel Moolenaar void 162831337658SMarcel Moolenaar xo_warnx (const char *fmt, ...) 162931337658SMarcel Moolenaar { 163031337658SMarcel Moolenaar va_list vap; 163131337658SMarcel Moolenaar 163231337658SMarcel Moolenaar va_start(vap, fmt); 163331337658SMarcel Moolenaar xo_warn_hcv(NULL, -1, 0, fmt, vap); 163431337658SMarcel Moolenaar va_end(vap); 163531337658SMarcel Moolenaar } 163631337658SMarcel Moolenaar 163731337658SMarcel Moolenaar void 163831337658SMarcel Moolenaar xo_err (int eval, const char *fmt, ...) 163931337658SMarcel Moolenaar { 164031337658SMarcel Moolenaar int code = errno; 164131337658SMarcel Moolenaar va_list vap; 164231337658SMarcel Moolenaar 164331337658SMarcel Moolenaar va_start(vap, fmt); 164431337658SMarcel Moolenaar xo_warn_hcv(NULL, code, 0, fmt, vap); 164531337658SMarcel Moolenaar va_end(vap); 164631337658SMarcel Moolenaar xo_finish(); 164731337658SMarcel Moolenaar exit(eval); 164831337658SMarcel Moolenaar } 164931337658SMarcel Moolenaar 165031337658SMarcel Moolenaar void 165131337658SMarcel Moolenaar xo_errx (int eval, const char *fmt, ...) 165231337658SMarcel Moolenaar { 165331337658SMarcel Moolenaar va_list vap; 165431337658SMarcel Moolenaar 165531337658SMarcel Moolenaar va_start(vap, fmt); 165631337658SMarcel Moolenaar xo_warn_hcv(NULL, -1, 0, fmt, vap); 165731337658SMarcel Moolenaar va_end(vap); 165831337658SMarcel Moolenaar xo_finish(); 165931337658SMarcel Moolenaar exit(eval); 166031337658SMarcel Moolenaar } 166131337658SMarcel Moolenaar 166231337658SMarcel Moolenaar void 166331337658SMarcel Moolenaar xo_errc (int eval, int code, const char *fmt, ...) 166431337658SMarcel Moolenaar { 166531337658SMarcel Moolenaar va_list vap; 166631337658SMarcel Moolenaar 166731337658SMarcel Moolenaar va_start(vap, fmt); 166831337658SMarcel Moolenaar xo_warn_hcv(NULL, code, 0, fmt, vap); 166931337658SMarcel Moolenaar va_end(vap); 167031337658SMarcel Moolenaar xo_finish(); 167131337658SMarcel Moolenaar exit(eval); 167231337658SMarcel Moolenaar } 167331337658SMarcel Moolenaar 167431337658SMarcel Moolenaar /* 167531337658SMarcel Moolenaar * Generate a warning. Normally, this is a text message written to 167631337658SMarcel Moolenaar * standard error. If the XOF_WARN_XML flag is set, then we generate 167731337658SMarcel Moolenaar * XMLified content on standard output. 167831337658SMarcel Moolenaar */ 167931337658SMarcel Moolenaar void 168031337658SMarcel Moolenaar xo_message_hcv (xo_handle_t *xop, int code, const char *fmt, va_list vap) 168131337658SMarcel Moolenaar { 168231337658SMarcel Moolenaar static char msg_open[] = "<message>"; 168331337658SMarcel Moolenaar static char msg_close[] = "</message>"; 168431337658SMarcel Moolenaar xo_buffer_t *xbp; 16858a6eceffSPhil Shafer ssize_t rc; 168631337658SMarcel Moolenaar va_list va_local; 168731337658SMarcel Moolenaar 168831337658SMarcel Moolenaar xop = xo_default(xop); 168931337658SMarcel Moolenaar 169031337658SMarcel Moolenaar if (fmt == NULL || *fmt == '\0') 169131337658SMarcel Moolenaar return; 169231337658SMarcel Moolenaar 169331337658SMarcel Moolenaar int need_nl = (fmt[strlen(fmt) - 1] != '\n'); 169431337658SMarcel Moolenaar 1695788ca347SMarcel Moolenaar switch (xo_style(xop)) { 169631337658SMarcel Moolenaar case XO_STYLE_XML: 169731337658SMarcel Moolenaar xbp = &xop->xo_data; 1698d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_PRETTY)) 169931337658SMarcel Moolenaar xo_buf_indent(xop, xop->xo_indent_by); 170031337658SMarcel Moolenaar xo_buf_append(xbp, msg_open, sizeof(msg_open) - 1); 170131337658SMarcel Moolenaar 170231337658SMarcel Moolenaar va_copy(va_local, vap); 170331337658SMarcel Moolenaar 17048a6eceffSPhil Shafer ssize_t left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp); 17052f784130SPhil Shafer 170631337658SMarcel Moolenaar rc = vsnprintf(xbp->xb_curp, left, fmt, vap); 1707d1a0d267SMarcel Moolenaar if (rc >= left) { 1708c600d307SMarcel Moolenaar if (!xo_buf_has_room(xbp, rc)) { 1709c600d307SMarcel Moolenaar va_end(va_local); 171031337658SMarcel Moolenaar return; 1711c600d307SMarcel Moolenaar } 171231337658SMarcel Moolenaar 171331337658SMarcel Moolenaar va_end(vap); /* Reset vap to the start */ 171431337658SMarcel Moolenaar va_copy(vap, va_local); 171531337658SMarcel Moolenaar 171631337658SMarcel Moolenaar left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp); 171731337658SMarcel Moolenaar rc = vsnprintf(xbp->xb_curp, left, fmt, vap); 171831337658SMarcel Moolenaar } 17192f784130SPhil Shafer 172031337658SMarcel Moolenaar va_end(va_local); 172131337658SMarcel Moolenaar 1722d1a0d267SMarcel Moolenaar rc = xo_escape_xml(xbp, rc, 0); 172331337658SMarcel Moolenaar xbp->xb_curp += rc; 172431337658SMarcel Moolenaar 172531337658SMarcel Moolenaar if (need_nl && code > 0) { 172631337658SMarcel Moolenaar const char *msg = strerror(code); 17272f784130SPhil Shafer 172831337658SMarcel Moolenaar if (msg) { 172931337658SMarcel Moolenaar xo_buf_append(xbp, ": ", 2); 173031337658SMarcel Moolenaar xo_buf_append(xbp, msg, strlen(msg)); 173131337658SMarcel Moolenaar } 173231337658SMarcel Moolenaar } 173331337658SMarcel Moolenaar 173431337658SMarcel Moolenaar if (need_nl) 1735d1a0d267SMarcel Moolenaar xo_buf_append(xbp, "\n", 1); /* Append newline and NUL to string */ 1736d1a0d267SMarcel Moolenaar 1737d1a0d267SMarcel Moolenaar xo_buf_append(xbp, msg_close, sizeof(msg_close) - 1); 1738d1a0d267SMarcel Moolenaar 1739d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_PRETTY)) 1740d1a0d267SMarcel Moolenaar xo_buf_append(xbp, "\n", 1); /* Append newline and NUL to string */ 1741d1a0d267SMarcel Moolenaar 1742545ddfbeSMarcel Moolenaar (void) xo_write(xop); 174331337658SMarcel Moolenaar break; 174431337658SMarcel Moolenaar 174531337658SMarcel Moolenaar case XO_STYLE_HTML: 174631337658SMarcel Moolenaar { 174731337658SMarcel Moolenaar char buf[BUFSIZ], *bp = buf, *cp; 17488a6eceffSPhil Shafer ssize_t bufsiz = sizeof(buf); 17498a6eceffSPhil Shafer ssize_t rc2; 175031337658SMarcel Moolenaar 175131337658SMarcel Moolenaar va_copy(va_local, vap); 175231337658SMarcel Moolenaar 1753c600d307SMarcel Moolenaar rc = vsnprintf(bp, bufsiz, fmt, va_local); 175431337658SMarcel Moolenaar if (rc > bufsiz) { 175531337658SMarcel Moolenaar bufsiz = rc + BUFSIZ; 175631337658SMarcel Moolenaar bp = alloca(bufsiz); 175731337658SMarcel Moolenaar va_end(va_local); 175831337658SMarcel Moolenaar va_copy(va_local, vap); 1759c600d307SMarcel Moolenaar rc = vsnprintf(bp, bufsiz, fmt, va_local); 176031337658SMarcel Moolenaar } 17612f784130SPhil Shafer 1762c600d307SMarcel Moolenaar va_end(va_local); 176331337658SMarcel Moolenaar cp = bp + rc; 176431337658SMarcel Moolenaar 176531337658SMarcel Moolenaar if (need_nl) { 176631337658SMarcel Moolenaar rc2 = snprintf(cp, bufsiz - rc, "%s%s\n", 176731337658SMarcel Moolenaar (code > 0) ? ": " : "", 176831337658SMarcel Moolenaar (code > 0) ? strerror(code) : ""); 176931337658SMarcel Moolenaar if (rc2 > 0) 177031337658SMarcel Moolenaar rc += rc2; 177131337658SMarcel Moolenaar } 177231337658SMarcel Moolenaar 1773264104f2SPhil Shafer xo_buf_append_div(xop, "message", 0, NULL, 0, bp, rc, 1774264104f2SPhil Shafer NULL, 0, NULL, 0); 177531337658SMarcel Moolenaar } 177631337658SMarcel Moolenaar break; 177731337658SMarcel Moolenaar 177831337658SMarcel Moolenaar case XO_STYLE_JSON: 1779d1a0d267SMarcel Moolenaar case XO_STYLE_SDPARAMS: 1780d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER: 1781d1a0d267SMarcel Moolenaar /* No means of representing messages */ 1782d1a0d267SMarcel Moolenaar return; 178331337658SMarcel Moolenaar 178431337658SMarcel Moolenaar case XO_STYLE_TEXT: 178531337658SMarcel Moolenaar rc = xo_printf_v(xop, fmt, vap); 178631337658SMarcel Moolenaar /* 178731337658SMarcel Moolenaar * XXX need to handle UTF-8 widths 178831337658SMarcel Moolenaar */ 178931337658SMarcel Moolenaar if (rc > 0) { 1790d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_COLUMNS)) 179131337658SMarcel Moolenaar xop->xo_columns += rc; 1792d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_ANCHOR)) 179331337658SMarcel Moolenaar xop->xo_anchor_columns += rc; 179431337658SMarcel Moolenaar } 179531337658SMarcel Moolenaar 179631337658SMarcel Moolenaar if (need_nl && code > 0) { 179731337658SMarcel Moolenaar const char *msg = strerror(code); 17982f784130SPhil Shafer 179931337658SMarcel Moolenaar if (msg) { 180031337658SMarcel Moolenaar xo_printf(xop, ": %s", msg); 180131337658SMarcel Moolenaar } 180231337658SMarcel Moolenaar } 180331337658SMarcel Moolenaar if (need_nl) 180431337658SMarcel Moolenaar xo_printf(xop, "\n"); 180531337658SMarcel Moolenaar 180631337658SMarcel Moolenaar break; 180731337658SMarcel Moolenaar } 180831337658SMarcel Moolenaar 180942ff34c3SPhil Shafer switch (xo_style(xop)) { 181042ff34c3SPhil Shafer case XO_STYLE_HTML: 181142ff34c3SPhil Shafer if (XOIF_ISSET(xop, XOIF_DIV_OPEN)) { 181242ff34c3SPhil Shafer static char div_close[] = "</div>"; 18132f784130SPhil Shafer 181442ff34c3SPhil Shafer XOIF_CLEAR(xop, XOIF_DIV_OPEN); 181542ff34c3SPhil Shafer xo_data_append(xop, div_close, sizeof(div_close) - 1); 181642ff34c3SPhil Shafer 181742ff34c3SPhil Shafer if (XOF_ISSET(xop, XOF_PRETTY)) 181842ff34c3SPhil Shafer xo_data_append(xop, "\n", 1); 181942ff34c3SPhil Shafer } 182042ff34c3SPhil Shafer break; 182142ff34c3SPhil Shafer } 182242ff34c3SPhil Shafer 1823545ddfbeSMarcel Moolenaar (void) xo_flush_h(xop); 182431337658SMarcel Moolenaar } 182531337658SMarcel Moolenaar 182631337658SMarcel Moolenaar void 182731337658SMarcel Moolenaar xo_message_hc (xo_handle_t *xop, int code, const char *fmt, ...) 182831337658SMarcel Moolenaar { 182931337658SMarcel Moolenaar va_list vap; 183031337658SMarcel Moolenaar 183131337658SMarcel Moolenaar va_start(vap, fmt); 183231337658SMarcel Moolenaar xo_message_hcv(xop, code, fmt, vap); 183331337658SMarcel Moolenaar va_end(vap); 183431337658SMarcel Moolenaar } 183531337658SMarcel Moolenaar 183631337658SMarcel Moolenaar void 183731337658SMarcel Moolenaar xo_message_c (int code, const char *fmt, ...) 183831337658SMarcel Moolenaar { 183931337658SMarcel Moolenaar va_list vap; 184031337658SMarcel Moolenaar 184131337658SMarcel Moolenaar va_start(vap, fmt); 184231337658SMarcel Moolenaar xo_message_hcv(NULL, code, fmt, vap); 184331337658SMarcel Moolenaar va_end(vap); 184431337658SMarcel Moolenaar } 184531337658SMarcel Moolenaar 184631337658SMarcel Moolenaar void 1847d1a0d267SMarcel Moolenaar xo_message_e (const char *fmt, ...) 184831337658SMarcel Moolenaar { 184931337658SMarcel Moolenaar int code = errno; 185031337658SMarcel Moolenaar va_list vap; 185131337658SMarcel Moolenaar 185231337658SMarcel Moolenaar va_start(vap, fmt); 185331337658SMarcel Moolenaar xo_message_hcv(NULL, code, fmt, vap); 185431337658SMarcel Moolenaar va_end(vap); 185531337658SMarcel Moolenaar } 185631337658SMarcel Moolenaar 1857d1a0d267SMarcel Moolenaar void 1858d1a0d267SMarcel Moolenaar xo_message (const char *fmt, ...) 1859d1a0d267SMarcel Moolenaar { 1860d1a0d267SMarcel Moolenaar va_list vap; 1861d1a0d267SMarcel Moolenaar 1862d1a0d267SMarcel Moolenaar va_start(vap, fmt); 1863d1a0d267SMarcel Moolenaar xo_message_hcv(NULL, 0, fmt, vap); 1864d1a0d267SMarcel Moolenaar va_end(vap); 1865d1a0d267SMarcel Moolenaar } 1866d1a0d267SMarcel Moolenaar 186731337658SMarcel Moolenaar static void 186831337658SMarcel Moolenaar xo_failure (xo_handle_t *xop, const char *fmt, ...) 186931337658SMarcel Moolenaar { 1870d1a0d267SMarcel Moolenaar if (!XOF_ISSET(xop, XOF_WARN)) 187131337658SMarcel Moolenaar return; 187231337658SMarcel Moolenaar 187331337658SMarcel Moolenaar va_list vap; 187431337658SMarcel Moolenaar 187531337658SMarcel Moolenaar va_start(vap, fmt); 187631337658SMarcel Moolenaar xo_warn_hcv(xop, -1, 1, fmt, vap); 187731337658SMarcel Moolenaar va_end(vap); 187831337658SMarcel Moolenaar } 187931337658SMarcel Moolenaar 188031337658SMarcel Moolenaar /** 188131337658SMarcel Moolenaar * Create a handle for use by later libxo functions. 188231337658SMarcel Moolenaar * 188331337658SMarcel Moolenaar * Note: normal use of libxo does not require a distinct handle, since 188431337658SMarcel Moolenaar * the default handle (used when NULL is passed) generates text on stdout. 188531337658SMarcel Moolenaar * 1886f2b7bf8aSPhil Shafer * @param style Style of output desired (XO_STYLE_* value) 1887f2b7bf8aSPhil Shafer * @param flags Set of XOF_* flags in use with this handle 1888f2b7bf8aSPhil Shafer * @return Newly allocated handle 1889f2b7bf8aSPhil Shafer * @see xo_destroy 189031337658SMarcel Moolenaar */ 189131337658SMarcel Moolenaar xo_handle_t * 189231337658SMarcel Moolenaar xo_create (xo_style_t style, xo_xof_flags_t flags) 189331337658SMarcel Moolenaar { 189431337658SMarcel Moolenaar xo_handle_t *xop = xo_realloc(NULL, sizeof(*xop)); 189531337658SMarcel Moolenaar 189631337658SMarcel Moolenaar if (xop) { 189731337658SMarcel Moolenaar bzero(xop, sizeof(*xop)); 189831337658SMarcel Moolenaar 189931337658SMarcel Moolenaar xop->xo_style = style; 1900d1a0d267SMarcel Moolenaar XOF_SET(xop, flags); 190131337658SMarcel Moolenaar xo_init_handle(xop); 1902d1a0d267SMarcel Moolenaar xop->xo_style = style; /* Reset style (see LIBXO_OPTIONS) */ 190331337658SMarcel Moolenaar } 190431337658SMarcel Moolenaar 190531337658SMarcel Moolenaar return xop; 190631337658SMarcel Moolenaar } 190731337658SMarcel Moolenaar 190831337658SMarcel Moolenaar /** 190931337658SMarcel Moolenaar * Create a handle that will write to the given file. Use 191031337658SMarcel Moolenaar * the XOF_CLOSE_FP flag to have the file closed on xo_destroy(). 1911f2b7bf8aSPhil Shafer * 1912f2b7bf8aSPhil Shafer * @param fp FILE pointer to use 1913f2b7bf8aSPhil Shafer * @param style Style of output desired (XO_STYLE_* value) 1914f2b7bf8aSPhil Shafer * @param flags Set of XOF_* flags to use with this handle 1915f2b7bf8aSPhil Shafer * @return Newly allocated handle 1916f2b7bf8aSPhil Shafer * @see xo_destroy 191731337658SMarcel Moolenaar */ 191831337658SMarcel Moolenaar xo_handle_t * 191931337658SMarcel Moolenaar xo_create_to_file (FILE *fp, xo_style_t style, xo_xof_flags_t flags) 192031337658SMarcel Moolenaar { 192131337658SMarcel Moolenaar xo_handle_t *xop = xo_create(style, flags); 192231337658SMarcel Moolenaar 192331337658SMarcel Moolenaar if (xop) { 192431337658SMarcel Moolenaar xop->xo_opaque = fp; 192531337658SMarcel Moolenaar xop->xo_write = xo_write_to_file; 192631337658SMarcel Moolenaar xop->xo_close = xo_close_file; 1927545ddfbeSMarcel Moolenaar xop->xo_flush = xo_flush_file; 192831337658SMarcel Moolenaar } 192931337658SMarcel Moolenaar 193031337658SMarcel Moolenaar return xop; 193131337658SMarcel Moolenaar } 193231337658SMarcel Moolenaar 193331337658SMarcel Moolenaar /** 193442ff34c3SPhil Shafer * Set the default handler to output to a file. 1935f2b7bf8aSPhil Shafer * 1936f2b7bf8aSPhil Shafer * @param xop libxo handle 1937f2b7bf8aSPhil Shafer * @param fp FILE pointer to use 1938f2b7bf8aSPhil Shafer * @return 0 on success, non-zero on failure 193942ff34c3SPhil Shafer */ 194042ff34c3SPhil Shafer int 194142ff34c3SPhil Shafer xo_set_file_h (xo_handle_t *xop, FILE *fp) 194242ff34c3SPhil Shafer { 194342ff34c3SPhil Shafer xop = xo_default(xop); 194442ff34c3SPhil Shafer 194542ff34c3SPhil Shafer if (fp == NULL) { 194642ff34c3SPhil Shafer xo_failure(xop, "xo_set_file: NULL fp"); 194742ff34c3SPhil Shafer return -1; 194842ff34c3SPhil Shafer } 194942ff34c3SPhil Shafer 195042ff34c3SPhil Shafer xop->xo_opaque = fp; 195142ff34c3SPhil Shafer xop->xo_write = xo_write_to_file; 195242ff34c3SPhil Shafer xop->xo_close = xo_close_file; 195342ff34c3SPhil Shafer xop->xo_flush = xo_flush_file; 195442ff34c3SPhil Shafer 195542ff34c3SPhil Shafer return 0; 195642ff34c3SPhil Shafer } 195742ff34c3SPhil Shafer 195842ff34c3SPhil Shafer /** 195942ff34c3SPhil Shafer * Set the default handler to output to a file. 1960f2b7bf8aSPhil Shafer * 1961f2b7bf8aSPhil Shafer * @param fp FILE pointer to use 1962f2b7bf8aSPhil Shafer * @return 0 on success, non-zero on failure 196342ff34c3SPhil Shafer */ 196442ff34c3SPhil Shafer int 196542ff34c3SPhil Shafer xo_set_file (FILE *fp) 196642ff34c3SPhil Shafer { 196742ff34c3SPhil Shafer return xo_set_file_h(NULL, fp); 196842ff34c3SPhil Shafer } 196942ff34c3SPhil Shafer 197042ff34c3SPhil Shafer /** 197131337658SMarcel Moolenaar * Release any resources held by the handle. 1972f2b7bf8aSPhil Shafer * 1973f2b7bf8aSPhil Shafer * @param xop XO handle to alter (or NULL for default handle) 197431337658SMarcel Moolenaar */ 197531337658SMarcel Moolenaar void 1976c600d307SMarcel Moolenaar xo_destroy (xo_handle_t *xop_arg) 197731337658SMarcel Moolenaar { 1978c600d307SMarcel Moolenaar xo_handle_t *xop = xo_default(xop_arg); 197931337658SMarcel Moolenaar 1980d1a0d267SMarcel Moolenaar xo_flush_h(xop); 1981d1a0d267SMarcel Moolenaar 1982d1a0d267SMarcel Moolenaar if (xop->xo_close && XOF_ISSET(xop, XOF_CLOSE_FP)) 198331337658SMarcel Moolenaar xop->xo_close(xop->xo_opaque); 198431337658SMarcel Moolenaar 198531337658SMarcel Moolenaar xo_free(xop->xo_stack); 198631337658SMarcel Moolenaar xo_buf_cleanup(&xop->xo_data); 198731337658SMarcel Moolenaar xo_buf_cleanup(&xop->xo_fmt); 198831337658SMarcel Moolenaar xo_buf_cleanup(&xop->xo_predicate); 198931337658SMarcel Moolenaar xo_buf_cleanup(&xop->xo_attrs); 1990788ca347SMarcel Moolenaar xo_buf_cleanup(&xop->xo_color_buf); 1991788ca347SMarcel Moolenaar 1992788ca347SMarcel Moolenaar if (xop->xo_version) 1993788ca347SMarcel Moolenaar xo_free(xop->xo_version); 199431337658SMarcel Moolenaar 1995c600d307SMarcel Moolenaar if (xop_arg == NULL) { 1996545ddfbeSMarcel Moolenaar bzero(&xo_default_handle, sizeof(xo_default_handle)); 199731337658SMarcel Moolenaar xo_default_inited = 0; 199831337658SMarcel Moolenaar } else 199931337658SMarcel Moolenaar xo_free(xop); 200031337658SMarcel Moolenaar } 200131337658SMarcel Moolenaar 200231337658SMarcel Moolenaar /** 200331337658SMarcel Moolenaar * Record a new output style to use for the given handle (or default if 200431337658SMarcel Moolenaar * handle is NULL). This output style will be used for any future output. 200531337658SMarcel Moolenaar * 2006f2b7bf8aSPhil Shafer * @param xop XO handle to alter (or NULL for default handle) 2007f2b7bf8aSPhil Shafer * @param style new output style (XO_STYLE_*) 200831337658SMarcel Moolenaar */ 200931337658SMarcel Moolenaar void 201031337658SMarcel Moolenaar xo_set_style (xo_handle_t *xop, xo_style_t style) 201131337658SMarcel Moolenaar { 201231337658SMarcel Moolenaar xop = xo_default(xop); 201331337658SMarcel Moolenaar xop->xo_style = style; 201431337658SMarcel Moolenaar } 201531337658SMarcel Moolenaar 2016f2b7bf8aSPhil Shafer /** 2017f2b7bf8aSPhil Shafer * Return the current style of a handle 2018f2b7bf8aSPhil Shafer * 2019f2b7bf8aSPhil Shafer * @param xop XO handle to access 2020f2b7bf8aSPhil Shafer * @return The handle's current style 2021f2b7bf8aSPhil Shafer */ 202231337658SMarcel Moolenaar xo_style_t 202331337658SMarcel Moolenaar xo_get_style (xo_handle_t *xop) 202431337658SMarcel Moolenaar { 202531337658SMarcel Moolenaar xop = xo_default(xop); 2026788ca347SMarcel Moolenaar return xo_style(xop); 202731337658SMarcel Moolenaar } 202831337658SMarcel Moolenaar 2029f2b7bf8aSPhil Shafer /** 2030f2b7bf8aSPhil Shafer * Return the XO_STYLE_* value matching a given name 2031f2b7bf8aSPhil Shafer * 2032f2b7bf8aSPhil Shafer * @param name String name of a style 2033f2b7bf8aSPhil Shafer * @return XO_STYLE_* value 2034f2b7bf8aSPhil Shafer */ 203531337658SMarcel Moolenaar static int 203631337658SMarcel Moolenaar xo_name_to_style (const char *name) 203731337658SMarcel Moolenaar { 203831337658SMarcel Moolenaar if (strcmp(name, "xml") == 0) 203931337658SMarcel Moolenaar return XO_STYLE_XML; 204031337658SMarcel Moolenaar else if (strcmp(name, "json") == 0) 204131337658SMarcel Moolenaar return XO_STYLE_JSON; 2042d1a0d267SMarcel Moolenaar else if (strcmp(name, "encoder") == 0) 2043d1a0d267SMarcel Moolenaar return XO_STYLE_ENCODER; 204431337658SMarcel Moolenaar else if (strcmp(name, "text") == 0) 204531337658SMarcel Moolenaar return XO_STYLE_TEXT; 204631337658SMarcel Moolenaar else if (strcmp(name, "html") == 0) 204731337658SMarcel Moolenaar return XO_STYLE_HTML; 2048d1a0d267SMarcel Moolenaar else if (strcmp(name, "sdparams") == 0) 2049d1a0d267SMarcel Moolenaar return XO_STYLE_SDPARAMS; 205031337658SMarcel Moolenaar 205131337658SMarcel Moolenaar return -1; 205231337658SMarcel Moolenaar } 205331337658SMarcel Moolenaar 205431337658SMarcel Moolenaar /* 2055d1a0d267SMarcel Moolenaar * Indicate if the style is an "encoding" one as opposed to a "display" one. 2056d1a0d267SMarcel Moolenaar */ 2057d1a0d267SMarcel Moolenaar static int 2058d1a0d267SMarcel Moolenaar xo_style_is_encoding (xo_handle_t *xop) 2059d1a0d267SMarcel Moolenaar { 2060d1a0d267SMarcel Moolenaar if (xo_style(xop) == XO_STYLE_JSON 2061d1a0d267SMarcel Moolenaar || xo_style(xop) == XO_STYLE_XML 2062d1a0d267SMarcel Moolenaar || xo_style(xop) == XO_STYLE_SDPARAMS 2063d1a0d267SMarcel Moolenaar || xo_style(xop) == XO_STYLE_ENCODER) 2064d1a0d267SMarcel Moolenaar return 1; 2065d1a0d267SMarcel Moolenaar return 0; 2066d1a0d267SMarcel Moolenaar } 2067d1a0d267SMarcel Moolenaar 2068d1a0d267SMarcel Moolenaar /* Simple name-value mapping */ 2069d1a0d267SMarcel Moolenaar typedef struct xo_mapping_s { 2070f2b7bf8aSPhil Shafer xo_xff_flags_t xm_value; /* Flag value */ 2071f2b7bf8aSPhil Shafer const char *xm_name; /* String name */ 2072d1a0d267SMarcel Moolenaar } xo_mapping_t; 2073d1a0d267SMarcel Moolenaar 2074d1a0d267SMarcel Moolenaar static xo_xff_flags_t 20758a6eceffSPhil Shafer xo_name_lookup (xo_mapping_t *map, const char *value, ssize_t len) 2076d1a0d267SMarcel Moolenaar { 2077d1a0d267SMarcel Moolenaar if (len == 0) 2078d1a0d267SMarcel Moolenaar return 0; 2079d1a0d267SMarcel Moolenaar 2080d1a0d267SMarcel Moolenaar if (len < 0) 2081d1a0d267SMarcel Moolenaar len = strlen(value); 2082d1a0d267SMarcel Moolenaar 2083d1a0d267SMarcel Moolenaar while (isspace((int) *value)) { 2084d1a0d267SMarcel Moolenaar value += 1; 2085d1a0d267SMarcel Moolenaar len -= 1; 2086d1a0d267SMarcel Moolenaar } 2087d1a0d267SMarcel Moolenaar 2088d1a0d267SMarcel Moolenaar while (isspace((int) value[len])) 2089d1a0d267SMarcel Moolenaar len -= 1; 2090d1a0d267SMarcel Moolenaar 2091d1a0d267SMarcel Moolenaar if (*value == '\0') 2092d1a0d267SMarcel Moolenaar return 0; 2093d1a0d267SMarcel Moolenaar 2094d1a0d267SMarcel Moolenaar for ( ; map->xm_name; map++) 2095d1a0d267SMarcel Moolenaar if (strncmp(map->xm_name, value, len) == 0) 2096d1a0d267SMarcel Moolenaar return map->xm_value; 2097d1a0d267SMarcel Moolenaar 2098d1a0d267SMarcel Moolenaar return 0; 2099d1a0d267SMarcel Moolenaar } 2100d1a0d267SMarcel Moolenaar 2101d1a0d267SMarcel Moolenaar #ifdef NOT_NEEDED_YET 2102d1a0d267SMarcel Moolenaar static const char * 2103d1a0d267SMarcel Moolenaar xo_value_lookup (xo_mapping_t *map, xo_xff_flags_t value) 2104d1a0d267SMarcel Moolenaar { 2105d1a0d267SMarcel Moolenaar if (value == 0) 2106d1a0d267SMarcel Moolenaar return NULL; 2107d1a0d267SMarcel Moolenaar 2108d1a0d267SMarcel Moolenaar for ( ; map->xm_name; map++) 2109d1a0d267SMarcel Moolenaar if (map->xm_value == value) 2110d1a0d267SMarcel Moolenaar return map->xm_name; 2111d1a0d267SMarcel Moolenaar 2112d1a0d267SMarcel Moolenaar return NULL; 2113d1a0d267SMarcel Moolenaar } 2114d1a0d267SMarcel Moolenaar #endif /* NOT_NEEDED_YET */ 2115d1a0d267SMarcel Moolenaar 2116d1a0d267SMarcel Moolenaar static xo_mapping_t xo_xof_names[] = { 2117d1a0d267SMarcel Moolenaar { XOF_COLOR_ALLOWED, "color" }, 2118f2b7bf8aSPhil Shafer { XOF_COLOR, "color-force" }, 2119d1a0d267SMarcel Moolenaar { XOF_COLUMNS, "columns" }, 2120d1a0d267SMarcel Moolenaar { XOF_DTRT, "dtrt" }, 2121d1a0d267SMarcel Moolenaar { XOF_FLUSH, "flush" }, 21228a6eceffSPhil Shafer { XOF_FLUSH_LINE, "flush-line" }, 2123d1a0d267SMarcel Moolenaar { XOF_IGNORE_CLOSE, "ignore-close" }, 2124d1a0d267SMarcel Moolenaar { XOF_INFO, "info" }, 2125d1a0d267SMarcel Moolenaar { XOF_KEYS, "keys" }, 2126d1a0d267SMarcel Moolenaar { XOF_LOG_GETTEXT, "log-gettext" }, 2127d1a0d267SMarcel Moolenaar { XOF_LOG_SYSLOG, "log-syslog" }, 2128d1a0d267SMarcel Moolenaar { XOF_NO_HUMANIZE, "no-humanize" }, 2129d1a0d267SMarcel Moolenaar { XOF_NO_LOCALE, "no-locale" }, 213042ff34c3SPhil Shafer { XOF_RETAIN_NONE, "no-retain" }, 2131d1a0d267SMarcel Moolenaar { XOF_NO_TOP, "no-top" }, 2132d1a0d267SMarcel Moolenaar { XOF_NOT_FIRST, "not-first" }, 2133d1a0d267SMarcel Moolenaar { XOF_PRETTY, "pretty" }, 213442ff34c3SPhil Shafer { XOF_RETAIN_ALL, "retain" }, 2135d1a0d267SMarcel Moolenaar { XOF_UNDERSCORES, "underscores" }, 2136d1a0d267SMarcel Moolenaar { XOF_UNITS, "units" }, 2137d1a0d267SMarcel Moolenaar { XOF_WARN, "warn" }, 2138d1a0d267SMarcel Moolenaar { XOF_WARN_XML, "warn-xml" }, 2139d1a0d267SMarcel Moolenaar { XOF_XPATH, "xpath" }, 2140d1a0d267SMarcel Moolenaar { 0, NULL } 2141d1a0d267SMarcel Moolenaar }; 2142d1a0d267SMarcel Moolenaar 2143f2b7bf8aSPhil Shafer /* Options available via the environment variable ($LIBXO_OPTIONS) */ 2144f2b7bf8aSPhil Shafer static xo_mapping_t xo_xof_simple_names[] = { 2145f2b7bf8aSPhil Shafer { XOF_COLOR_ALLOWED, "color" }, 2146f2b7bf8aSPhil Shafer { XOF_FLUSH, "flush" }, 2147f2b7bf8aSPhil Shafer { XOF_FLUSH_LINE, "flush-line" }, 2148f2b7bf8aSPhil Shafer { XOF_NO_HUMANIZE, "no-humanize" }, 2149f2b7bf8aSPhil Shafer { XOF_NO_LOCALE, "no-locale" }, 2150f2b7bf8aSPhil Shafer { XOF_RETAIN_NONE, "no-retain" }, 2151f2b7bf8aSPhil Shafer { XOF_PRETTY, "pretty" }, 2152f2b7bf8aSPhil Shafer { XOF_RETAIN_ALL, "retain" }, 2153f2b7bf8aSPhil Shafer { XOF_UNDERSCORES, "underscores" }, 2154f2b7bf8aSPhil Shafer { XOF_WARN, "warn" }, 2155f2b7bf8aSPhil Shafer { 0, NULL } 2156f2b7bf8aSPhil Shafer }; 2157f2b7bf8aSPhil Shafer 2158d1a0d267SMarcel Moolenaar /* 215931337658SMarcel Moolenaar * Convert string name to XOF_* flag value. 216031337658SMarcel Moolenaar * Not all are useful. Or safe. Or sane. 216131337658SMarcel Moolenaar */ 216231337658SMarcel Moolenaar static unsigned 216331337658SMarcel Moolenaar xo_name_to_flag (const char *name) 216431337658SMarcel Moolenaar { 2165d1a0d267SMarcel Moolenaar return (unsigned) xo_name_lookup(xo_xof_names, name, -1); 216631337658SMarcel Moolenaar } 216731337658SMarcel Moolenaar 2168f2b7bf8aSPhil Shafer /** 2169f2b7bf8aSPhil Shafer * Set the style of an libxo handle based on a string name 2170f2b7bf8aSPhil Shafer * 2171f2b7bf8aSPhil Shafer * @param xop XO handle 2172f2b7bf8aSPhil Shafer * @param name String value of name 2173f2b7bf8aSPhil Shafer * @return 0 on success, non-zero on failure 2174f2b7bf8aSPhil Shafer */ 217531337658SMarcel Moolenaar int 217631337658SMarcel Moolenaar xo_set_style_name (xo_handle_t *xop, const char *name) 217731337658SMarcel Moolenaar { 217831337658SMarcel Moolenaar if (name == NULL) 217931337658SMarcel Moolenaar return -1; 218031337658SMarcel Moolenaar 218131337658SMarcel Moolenaar int style = xo_name_to_style(name); 21822f784130SPhil Shafer 218331337658SMarcel Moolenaar if (style < 0) 218431337658SMarcel Moolenaar return -1; 218531337658SMarcel Moolenaar 218631337658SMarcel Moolenaar xo_set_style(xop, style); 218731337658SMarcel Moolenaar return 0; 218831337658SMarcel Moolenaar } 218931337658SMarcel Moolenaar 219031337658SMarcel Moolenaar /* 2191f2b7bf8aSPhil Shafer * Fill in the color map, based on the input string; currently unimplemented 2192f2b7bf8aSPhil Shafer * Look for something like "colors=red/blue+green/yellow" as fg/bg pairs. 2193f2b7bf8aSPhil Shafer */ 2194f2b7bf8aSPhil Shafer static void 2195f2b7bf8aSPhil Shafer xo_set_color_map (xo_handle_t *xop, char *value) 2196f2b7bf8aSPhil Shafer { 2197f2b7bf8aSPhil Shafer #ifdef LIBXO_TEXT_ONLY 2198f2b7bf8aSPhil Shafer return; 2199f2b7bf8aSPhil Shafer #endif /* LIBXO_TEXT_ONLY */ 2200f2b7bf8aSPhil Shafer 2201f2b7bf8aSPhil Shafer char *cp, *ep, *vp, *np; 2202f2b7bf8aSPhil Shafer ssize_t len = value ? strlen(value) + 1 : 0; 2203f2b7bf8aSPhil Shafer int num = 1, fg, bg; 2204f2b7bf8aSPhil Shafer 2205f2b7bf8aSPhil Shafer for (cp = value, ep = cp + len - 1; cp && *cp && cp < ep; cp = np) { 2206f2b7bf8aSPhil Shafer np = strchr(cp, '+'); 2207f2b7bf8aSPhil Shafer if (np) 2208f2b7bf8aSPhil Shafer *np++ = '\0'; 2209f2b7bf8aSPhil Shafer 2210f2b7bf8aSPhil Shafer vp = strchr(cp, '/'); 2211f2b7bf8aSPhil Shafer if (vp) 2212f2b7bf8aSPhil Shafer *vp++ = '\0'; 2213f2b7bf8aSPhil Shafer 2214f2b7bf8aSPhil Shafer fg = *cp ? xo_color_find(cp) : -1; 2215f2b7bf8aSPhil Shafer bg = (vp && *vp) ? xo_color_find(vp) : -1; 2216f2b7bf8aSPhil Shafer 2217f2b7bf8aSPhil Shafer xop->xo_color_map_fg[num] = (fg < 0) ? num : fg; 2218f2b7bf8aSPhil Shafer xop->xo_color_map_bg[num] = (bg < 0) ? num : bg; 2219f2b7bf8aSPhil Shafer if (++num > XO_NUM_COLORS) 2220f2b7bf8aSPhil Shafer break; 2221f2b7bf8aSPhil Shafer } 2222f2b7bf8aSPhil Shafer 2223f2b7bf8aSPhil Shafer /* If no color initialization happened, then we don't need the map */ 2224f2b7bf8aSPhil Shafer if (num > 0) 2225f2b7bf8aSPhil Shafer XOF_SET(xop, XOF_COLOR_MAP); 2226f2b7bf8aSPhil Shafer else 2227f2b7bf8aSPhil Shafer XOF_CLEAR(xop, XOF_COLOR_MAP); 2228f2b7bf8aSPhil Shafer 2229f2b7bf8aSPhil Shafer /* Fill in the rest of the colors with the defaults */ 2230f2b7bf8aSPhil Shafer for ( ; num < XO_NUM_COLORS; num++) 2231f2b7bf8aSPhil Shafer xop->xo_color_map_fg[num] = xop->xo_color_map_bg[num] = num; 2232f2b7bf8aSPhil Shafer } 2233f2b7bf8aSPhil Shafer 2234f2b7bf8aSPhil Shafer static int 2235f2b7bf8aSPhil Shafer xo_set_options_simple (xo_handle_t *xop, const char *input) 2236f2b7bf8aSPhil Shafer { 2237f2b7bf8aSPhil Shafer xo_xof_flags_t new_flag; 2238f2b7bf8aSPhil Shafer char *cp, *ep, *vp, *np, *bp; 2239f2b7bf8aSPhil Shafer ssize_t len = strlen(input) + 1; 2240f2b7bf8aSPhil Shafer 2241f2b7bf8aSPhil Shafer bp = alloca(len); 2242f2b7bf8aSPhil Shafer memcpy(bp, input, len); 2243f2b7bf8aSPhil Shafer 2244f2b7bf8aSPhil Shafer for (cp = bp, ep = cp + len - 1; cp && cp < ep; cp = np) { 2245f2b7bf8aSPhil Shafer np = strchr(cp, ','); 2246f2b7bf8aSPhil Shafer if (np) 2247f2b7bf8aSPhil Shafer *np++ = '\0'; 2248f2b7bf8aSPhil Shafer 2249f2b7bf8aSPhil Shafer vp = strchr(cp, '='); 2250f2b7bf8aSPhil Shafer if (vp) 2251f2b7bf8aSPhil Shafer *vp++ = '\0'; 2252f2b7bf8aSPhil Shafer 2253f2b7bf8aSPhil Shafer if (strcmp("colors", cp) == 0) { 2254f2b7bf8aSPhil Shafer xo_set_color_map(xop, vp); 2255f2b7bf8aSPhil Shafer continue; 2256f2b7bf8aSPhil Shafer } 2257f2b7bf8aSPhil Shafer 2258f2b7bf8aSPhil Shafer new_flag = xo_name_lookup(xo_xof_simple_names, cp, -1); 2259f2b7bf8aSPhil Shafer if (new_flag != 0) { 2260f2b7bf8aSPhil Shafer XOF_SET(xop, new_flag); 2261f2b7bf8aSPhil Shafer } else if (strcmp(cp, "no-color") == 0) { 2262f2b7bf8aSPhil Shafer XOF_CLEAR(xop, XOF_COLOR_ALLOWED); 2263f2b7bf8aSPhil Shafer } else { 2264f2b7bf8aSPhil Shafer xo_failure(xop, "unknown simple option: %s", cp); 2265f2b7bf8aSPhil Shafer return -1; 2266f2b7bf8aSPhil Shafer } 2267f2b7bf8aSPhil Shafer } 2268f2b7bf8aSPhil Shafer 2269f2b7bf8aSPhil Shafer return 0; 2270f2b7bf8aSPhil Shafer } 2271f2b7bf8aSPhil Shafer 2272f2b7bf8aSPhil Shafer /** 227331337658SMarcel Moolenaar * Set the options for a handle using a string of options 227431337658SMarcel Moolenaar * passed in. The input is a comma-separated set of names 227531337658SMarcel Moolenaar * and optional values: "xml,pretty,indent=4" 2276f2b7bf8aSPhil Shafer * 2277f2b7bf8aSPhil Shafer * @param xop XO handle 2278f2b7bf8aSPhil Shafer * @param input Comma-separated set of option values 2279f2b7bf8aSPhil Shafer * @return 0 on success, non-zero on failure 228031337658SMarcel Moolenaar */ 228131337658SMarcel Moolenaar int 228231337658SMarcel Moolenaar xo_set_options (xo_handle_t *xop, const char *input) 228331337658SMarcel Moolenaar { 228431337658SMarcel Moolenaar char *cp, *ep, *vp, *np, *bp; 22858a6eceffSPhil Shafer int style = -1, new_style, rc = 0; 22868a6eceffSPhil Shafer ssize_t len; 228731337658SMarcel Moolenaar xo_xof_flags_t new_flag; 228831337658SMarcel Moolenaar 228931337658SMarcel Moolenaar if (input == NULL) 229031337658SMarcel Moolenaar return 0; 229131337658SMarcel Moolenaar 229231337658SMarcel Moolenaar xop = xo_default(xop); 229331337658SMarcel Moolenaar 2294788ca347SMarcel Moolenaar #ifdef LIBXO_COLOR_ON_BY_DEFAULT 2295788ca347SMarcel Moolenaar /* If the installer used --enable-color-on-by-default, then we allow it */ 2296d1a0d267SMarcel Moolenaar XOF_SET(xop, XOF_COLOR_ALLOWED); 2297788ca347SMarcel Moolenaar #endif /* LIBXO_COLOR_ON_BY_DEFAULT */ 2298788ca347SMarcel Moolenaar 229931337658SMarcel Moolenaar /* 230031337658SMarcel Moolenaar * We support a simpler, old-school style of giving option 230131337658SMarcel Moolenaar * also, using a single character for each option. It's 230231337658SMarcel Moolenaar * ideal for lazy people, such as myself. 230331337658SMarcel Moolenaar */ 230431337658SMarcel Moolenaar if (*input == ':') { 23058a6eceffSPhil Shafer ssize_t sz; 230631337658SMarcel Moolenaar 230731337658SMarcel Moolenaar for (input++ ; *input; input++) { 230831337658SMarcel Moolenaar switch (*input) { 2309788ca347SMarcel Moolenaar case 'c': 2310d1a0d267SMarcel Moolenaar XOF_SET(xop, XOF_COLOR_ALLOWED); 2311788ca347SMarcel Moolenaar break; 2312788ca347SMarcel Moolenaar 231331337658SMarcel Moolenaar case 'f': 2314d1a0d267SMarcel Moolenaar XOF_SET(xop, XOF_FLUSH); 231531337658SMarcel Moolenaar break; 231631337658SMarcel Moolenaar 2317545ddfbeSMarcel Moolenaar case 'F': 2318d1a0d267SMarcel Moolenaar XOF_SET(xop, XOF_FLUSH_LINE); 2319d1a0d267SMarcel Moolenaar break; 2320d1a0d267SMarcel Moolenaar 2321d1a0d267SMarcel Moolenaar case 'g': 2322d1a0d267SMarcel Moolenaar XOF_SET(xop, XOF_LOG_GETTEXT); 2323545ddfbeSMarcel Moolenaar break; 2324545ddfbeSMarcel Moolenaar 232531337658SMarcel Moolenaar case 'H': 232631337658SMarcel Moolenaar xop->xo_style = XO_STYLE_HTML; 232731337658SMarcel Moolenaar break; 232831337658SMarcel Moolenaar 232931337658SMarcel Moolenaar case 'I': 2330d1a0d267SMarcel Moolenaar XOF_SET(xop, XOF_INFO); 233131337658SMarcel Moolenaar break; 233231337658SMarcel Moolenaar 233331337658SMarcel Moolenaar case 'i': 233431337658SMarcel Moolenaar sz = strspn(input + 1, "0123456789"); 233531337658SMarcel Moolenaar if (sz > 0) { 233631337658SMarcel Moolenaar xop->xo_indent_by = atoi(input + 1); 233731337658SMarcel Moolenaar input += sz - 1; /* Skip value */ 233831337658SMarcel Moolenaar } 233931337658SMarcel Moolenaar break; 234031337658SMarcel Moolenaar 234131337658SMarcel Moolenaar case 'J': 234231337658SMarcel Moolenaar xop->xo_style = XO_STYLE_JSON; 234331337658SMarcel Moolenaar break; 234431337658SMarcel Moolenaar 2345d1a0d267SMarcel Moolenaar case 'k': 2346d1a0d267SMarcel Moolenaar XOF_SET(xop, XOF_KEYS); 2347d1a0d267SMarcel Moolenaar break; 2348d1a0d267SMarcel Moolenaar 2349d1a0d267SMarcel Moolenaar case 'n': 2350d1a0d267SMarcel Moolenaar XOF_SET(xop, XOF_NO_HUMANIZE); 2351d1a0d267SMarcel Moolenaar break; 2352d1a0d267SMarcel Moolenaar 235331337658SMarcel Moolenaar case 'P': 2354d1a0d267SMarcel Moolenaar XOF_SET(xop, XOF_PRETTY); 235531337658SMarcel Moolenaar break; 235631337658SMarcel Moolenaar 235731337658SMarcel Moolenaar case 'T': 235831337658SMarcel Moolenaar xop->xo_style = XO_STYLE_TEXT; 235931337658SMarcel Moolenaar break; 236031337658SMarcel Moolenaar 236131337658SMarcel Moolenaar case 'U': 2362d1a0d267SMarcel Moolenaar XOF_SET(xop, XOF_UNITS); 236331337658SMarcel Moolenaar break; 236431337658SMarcel Moolenaar 236531337658SMarcel Moolenaar case 'u': 2366d1a0d267SMarcel Moolenaar XOF_SET(xop, XOF_UNDERSCORES); 236731337658SMarcel Moolenaar break; 236831337658SMarcel Moolenaar 236931337658SMarcel Moolenaar case 'W': 2370d1a0d267SMarcel Moolenaar XOF_SET(xop, XOF_WARN); 237131337658SMarcel Moolenaar break; 237231337658SMarcel Moolenaar 237331337658SMarcel Moolenaar case 'X': 237431337658SMarcel Moolenaar xop->xo_style = XO_STYLE_XML; 237531337658SMarcel Moolenaar break; 237631337658SMarcel Moolenaar 237731337658SMarcel Moolenaar case 'x': 2378d1a0d267SMarcel Moolenaar XOF_SET(xop, XOF_XPATH); 237931337658SMarcel Moolenaar break; 238031337658SMarcel Moolenaar } 238131337658SMarcel Moolenaar } 238231337658SMarcel Moolenaar return 0; 238331337658SMarcel Moolenaar } 238431337658SMarcel Moolenaar 238531337658SMarcel Moolenaar len = strlen(input) + 1; 238631337658SMarcel Moolenaar bp = alloca(len); 238731337658SMarcel Moolenaar memcpy(bp, input, len); 238831337658SMarcel Moolenaar 238931337658SMarcel Moolenaar for (cp = bp, ep = cp + len - 1; cp && cp < ep; cp = np) { 239031337658SMarcel Moolenaar np = strchr(cp, ','); 239131337658SMarcel Moolenaar if (np) 239231337658SMarcel Moolenaar *np++ = '\0'; 239331337658SMarcel Moolenaar 239431337658SMarcel Moolenaar vp = strchr(cp, '='); 239531337658SMarcel Moolenaar if (vp) 239631337658SMarcel Moolenaar *vp++ = '\0'; 239731337658SMarcel Moolenaar 2398788ca347SMarcel Moolenaar if (strcmp("colors", cp) == 0) { 2399f2b7bf8aSPhil Shafer xo_set_color_map(xop, vp); 2400788ca347SMarcel Moolenaar continue; 2401788ca347SMarcel Moolenaar } 2402788ca347SMarcel Moolenaar 2403d1a0d267SMarcel Moolenaar /* 2404d1a0d267SMarcel Moolenaar * For options, we don't allow "encoder" since we want to 2405d1a0d267SMarcel Moolenaar * handle it explicitly below as "encoder=xxx". 2406d1a0d267SMarcel Moolenaar */ 240731337658SMarcel Moolenaar new_style = xo_name_to_style(cp); 2408d1a0d267SMarcel Moolenaar if (new_style >= 0 && new_style != XO_STYLE_ENCODER) { 240931337658SMarcel Moolenaar if (style >= 0) 241031337658SMarcel Moolenaar xo_warnx("ignoring multiple styles: '%s'", cp); 241131337658SMarcel Moolenaar else 241231337658SMarcel Moolenaar style = new_style; 241331337658SMarcel Moolenaar } else { 241431337658SMarcel Moolenaar new_flag = xo_name_to_flag(cp); 241531337658SMarcel Moolenaar if (new_flag != 0) 2416d1a0d267SMarcel Moolenaar XOF_SET(xop, new_flag); 2417f2b7bf8aSPhil Shafer else if (strcmp(cp, "no-color") == 0) 2418d1a0d267SMarcel Moolenaar XOF_CLEAR(xop, XOF_COLOR_ALLOWED); 2419f2b7bf8aSPhil Shafer else if (strcmp(cp, "indent") == 0) { 2420d1a0d267SMarcel Moolenaar if (vp) 242131337658SMarcel Moolenaar xop->xo_indent_by = atoi(vp); 2422d1a0d267SMarcel Moolenaar else 2423d1a0d267SMarcel Moolenaar xo_failure(xop, "missing value for indent option"); 2424d1a0d267SMarcel Moolenaar } else if (strcmp(cp, "encoder") == 0) { 2425d1a0d267SMarcel Moolenaar if (vp == NULL) 2426d1a0d267SMarcel Moolenaar xo_failure(xop, "missing value for encoder option"); 2427d1a0d267SMarcel Moolenaar else { 2428d1a0d267SMarcel Moolenaar if (xo_encoder_init(xop, vp)) { 2429d1a0d267SMarcel Moolenaar xo_failure(xop, "encoder not found: %s", vp); 2430d1a0d267SMarcel Moolenaar rc = -1; 2431d1a0d267SMarcel Moolenaar } 2432d1a0d267SMarcel Moolenaar } 2433d1a0d267SMarcel Moolenaar 243431337658SMarcel Moolenaar } else { 2435d1a0d267SMarcel Moolenaar xo_warnx("unknown libxo option value: '%s'", cp); 243631337658SMarcel Moolenaar rc = -1; 243731337658SMarcel Moolenaar } 243831337658SMarcel Moolenaar } 243931337658SMarcel Moolenaar } 244031337658SMarcel Moolenaar 244131337658SMarcel Moolenaar if (style > 0) 244231337658SMarcel Moolenaar xop->xo_style= style; 244331337658SMarcel Moolenaar 244431337658SMarcel Moolenaar return rc; 244531337658SMarcel Moolenaar } 244631337658SMarcel Moolenaar 244731337658SMarcel Moolenaar /** 244831337658SMarcel Moolenaar * Set one or more flags for a given handle (or default if handle is NULL). 244931337658SMarcel Moolenaar * These flags will affect future output. 245031337658SMarcel Moolenaar * 2451f2b7bf8aSPhil Shafer * @param xop XO handle to alter (or NULL for default handle) 2452f2b7bf8aSPhil Shafer * @param flags Flags to be set (XOF_*) 245331337658SMarcel Moolenaar */ 245431337658SMarcel Moolenaar void 245531337658SMarcel Moolenaar xo_set_flags (xo_handle_t *xop, xo_xof_flags_t flags) 245631337658SMarcel Moolenaar { 245731337658SMarcel Moolenaar xop = xo_default(xop); 245831337658SMarcel Moolenaar 2459d1a0d267SMarcel Moolenaar XOF_SET(xop, flags); 246031337658SMarcel Moolenaar } 246131337658SMarcel Moolenaar 2462f2b7bf8aSPhil Shafer /** 2463f2b7bf8aSPhil Shafer * Accessor to return the current set of flags for a handle 2464f2b7bf8aSPhil Shafer * @param xop XO handle 2465f2b7bf8aSPhil Shafer * @return Current set of flags 2466f2b7bf8aSPhil Shafer */ 246731337658SMarcel Moolenaar xo_xof_flags_t 246831337658SMarcel Moolenaar xo_get_flags (xo_handle_t *xop) 246931337658SMarcel Moolenaar { 247031337658SMarcel Moolenaar xop = xo_default(xop); 247131337658SMarcel Moolenaar 247231337658SMarcel Moolenaar return xop->xo_flags; 247331337658SMarcel Moolenaar } 247431337658SMarcel Moolenaar 2475f2b7bf8aSPhil Shafer /** 2476f2b7bf8aSPhil Shafer * strndup with a twist: len < 0 means len = strlen(str) 2477d1a0d267SMarcel Moolenaar */ 2478d1a0d267SMarcel Moolenaar static char * 24798a6eceffSPhil Shafer xo_strndup (const char *str, ssize_t len) 2480d1a0d267SMarcel Moolenaar { 2481d1a0d267SMarcel Moolenaar if (len < 0) 2482d1a0d267SMarcel Moolenaar len = strlen(str); 2483d1a0d267SMarcel Moolenaar 2484d1a0d267SMarcel Moolenaar char *cp = xo_realloc(NULL, len + 1); 2485d1a0d267SMarcel Moolenaar if (cp) { 2486d1a0d267SMarcel Moolenaar memcpy(cp, str, len); 2487d1a0d267SMarcel Moolenaar cp[len] = '\0'; 2488d1a0d267SMarcel Moolenaar } 2489d1a0d267SMarcel Moolenaar 2490d1a0d267SMarcel Moolenaar return cp; 2491d1a0d267SMarcel Moolenaar } 2492d1a0d267SMarcel Moolenaar 249331337658SMarcel Moolenaar /** 249431337658SMarcel Moolenaar * Record a leading prefix for the XPath we generate. This allows the 249531337658SMarcel Moolenaar * generated data to be placed within an XML hierarchy but still have 249631337658SMarcel Moolenaar * accurate XPath expressions. 249731337658SMarcel Moolenaar * 2498f2b7bf8aSPhil Shafer * @param xop XO handle to alter (or NULL for default handle) 2499f2b7bf8aSPhil Shafer * @param path The XPath expression 250031337658SMarcel Moolenaar */ 250131337658SMarcel Moolenaar void 250231337658SMarcel Moolenaar xo_set_leading_xpath (xo_handle_t *xop, const char *path) 250331337658SMarcel Moolenaar { 250431337658SMarcel Moolenaar xop = xo_default(xop); 250531337658SMarcel Moolenaar 250631337658SMarcel Moolenaar if (xop->xo_leading_xpath) { 250731337658SMarcel Moolenaar xo_free(xop->xo_leading_xpath); 250831337658SMarcel Moolenaar xop->xo_leading_xpath = NULL; 250931337658SMarcel Moolenaar } 251031337658SMarcel Moolenaar 251131337658SMarcel Moolenaar if (path == NULL) 251231337658SMarcel Moolenaar return; 251331337658SMarcel Moolenaar 2514d1a0d267SMarcel Moolenaar xop->xo_leading_xpath = xo_strndup(path, -1); 251531337658SMarcel Moolenaar } 251631337658SMarcel Moolenaar 251731337658SMarcel Moolenaar /** 251831337658SMarcel Moolenaar * Record the info data for a set of tags 251931337658SMarcel Moolenaar * 2520f2b7bf8aSPhil Shafer * @param xop XO handle to alter (or NULL for default handle) 2521f2b7bf8aSPhil Shafer * @param info Info data (xo_info_t) to be recorded (or NULL) (MUST BE SORTED) 2522f2b7bf8aSPhil Shafer * @pararm count Number of entries in info (or -1 to count them ourselves) 252331337658SMarcel Moolenaar */ 252431337658SMarcel Moolenaar void 252531337658SMarcel Moolenaar xo_set_info (xo_handle_t *xop, xo_info_t *infop, int count) 252631337658SMarcel Moolenaar { 252731337658SMarcel Moolenaar xop = xo_default(xop); 252831337658SMarcel Moolenaar 252931337658SMarcel Moolenaar if (count < 0 && infop) { 253031337658SMarcel Moolenaar xo_info_t *xip; 253131337658SMarcel Moolenaar 253231337658SMarcel Moolenaar for (xip = infop, count = 0; xip->xi_name; xip++, count++) 253331337658SMarcel Moolenaar continue; 253431337658SMarcel Moolenaar } 253531337658SMarcel Moolenaar 253631337658SMarcel Moolenaar xop->xo_info = infop; 253731337658SMarcel Moolenaar xop->xo_info_count = count; 253831337658SMarcel Moolenaar } 253931337658SMarcel Moolenaar 254031337658SMarcel Moolenaar /** 254131337658SMarcel Moolenaar * Set the formatter callback for a handle. The callback should 254231337658SMarcel Moolenaar * return a newly formatting contents of a formatting instruction, 254331337658SMarcel Moolenaar * meaning the bits inside the braces. 254431337658SMarcel Moolenaar */ 254531337658SMarcel Moolenaar void 254631337658SMarcel Moolenaar xo_set_formatter (xo_handle_t *xop, xo_formatter_t func, 254731337658SMarcel Moolenaar xo_checkpointer_t cfunc) 254831337658SMarcel Moolenaar { 254931337658SMarcel Moolenaar xop = xo_default(xop); 255031337658SMarcel Moolenaar 255131337658SMarcel Moolenaar xop->xo_formatter = func; 255231337658SMarcel Moolenaar xop->xo_checkpointer = cfunc; 255331337658SMarcel Moolenaar } 255431337658SMarcel Moolenaar 255531337658SMarcel Moolenaar /** 255631337658SMarcel Moolenaar * Clear one or more flags for a given handle (or default if handle is NULL). 255731337658SMarcel Moolenaar * These flags will affect future output. 255831337658SMarcel Moolenaar * 2559f2b7bf8aSPhil Shafer * @param xop XO handle to alter (or NULL for default handle) 2560f2b7bf8aSPhil Shafer * @param flags Flags to be cleared (XOF_*) 256131337658SMarcel Moolenaar */ 256231337658SMarcel Moolenaar void 256331337658SMarcel Moolenaar xo_clear_flags (xo_handle_t *xop, xo_xof_flags_t flags) 256431337658SMarcel Moolenaar { 256531337658SMarcel Moolenaar xop = xo_default(xop); 256631337658SMarcel Moolenaar 2567d1a0d267SMarcel Moolenaar XOF_CLEAR(xop, flags); 256831337658SMarcel Moolenaar } 256931337658SMarcel Moolenaar 2570545ddfbeSMarcel Moolenaar static const char * 2571545ddfbeSMarcel Moolenaar xo_state_name (xo_state_t state) 2572545ddfbeSMarcel Moolenaar { 2573545ddfbeSMarcel Moolenaar static const char *names[] = { 2574545ddfbeSMarcel Moolenaar "init", 2575545ddfbeSMarcel Moolenaar "open_container", 2576545ddfbeSMarcel Moolenaar "close_container", 2577545ddfbeSMarcel Moolenaar "open_list", 2578545ddfbeSMarcel Moolenaar "close_list", 2579545ddfbeSMarcel Moolenaar "open_instance", 2580545ddfbeSMarcel Moolenaar "close_instance", 2581545ddfbeSMarcel Moolenaar "open_leaf_list", 2582545ddfbeSMarcel Moolenaar "close_leaf_list", 2583545ddfbeSMarcel Moolenaar "discarding", 2584545ddfbeSMarcel Moolenaar "marker", 2585545ddfbeSMarcel Moolenaar "emit", 2586545ddfbeSMarcel Moolenaar "emit_leaf_list", 2587545ddfbeSMarcel Moolenaar "finish", 2588545ddfbeSMarcel Moolenaar NULL 2589545ddfbeSMarcel Moolenaar }; 2590545ddfbeSMarcel Moolenaar 2591545ddfbeSMarcel Moolenaar if (state < (sizeof(names) / sizeof(names[0]))) 2592545ddfbeSMarcel Moolenaar return names[state]; 2593545ddfbeSMarcel Moolenaar 2594545ddfbeSMarcel Moolenaar return "unknown"; 2595545ddfbeSMarcel Moolenaar } 2596545ddfbeSMarcel Moolenaar 259731337658SMarcel Moolenaar static void 259831337658SMarcel Moolenaar xo_line_ensure_open (xo_handle_t *xop, xo_xff_flags_t flags UNUSED) 259931337658SMarcel Moolenaar { 260031337658SMarcel Moolenaar static char div_open[] = "<div class=\"line\">"; 260131337658SMarcel Moolenaar static char div_open_blank[] = "<div class=\"blank-line\">"; 260231337658SMarcel Moolenaar 2603d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_DIV_OPEN)) 260431337658SMarcel Moolenaar return; 260531337658SMarcel Moolenaar 2606788ca347SMarcel Moolenaar if (xo_style(xop) != XO_STYLE_HTML) 260731337658SMarcel Moolenaar return; 260831337658SMarcel Moolenaar 2609d1a0d267SMarcel Moolenaar XOIF_SET(xop, XOIF_DIV_OPEN); 261031337658SMarcel Moolenaar if (flags & XFF_BLANK_LINE) 261131337658SMarcel Moolenaar xo_data_append(xop, div_open_blank, sizeof(div_open_blank) - 1); 261231337658SMarcel Moolenaar else 261331337658SMarcel Moolenaar xo_data_append(xop, div_open, sizeof(div_open) - 1); 261431337658SMarcel Moolenaar 2615d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_PRETTY)) 261631337658SMarcel Moolenaar xo_data_append(xop, "\n", 1); 261731337658SMarcel Moolenaar } 261831337658SMarcel Moolenaar 261931337658SMarcel Moolenaar static void 262031337658SMarcel Moolenaar xo_line_close (xo_handle_t *xop) 262131337658SMarcel Moolenaar { 262231337658SMarcel Moolenaar static char div_close[] = "</div>"; 262331337658SMarcel Moolenaar 2624788ca347SMarcel Moolenaar switch (xo_style(xop)) { 262531337658SMarcel Moolenaar case XO_STYLE_HTML: 2626d1a0d267SMarcel Moolenaar if (!XOIF_ISSET(xop, XOIF_DIV_OPEN)) 262731337658SMarcel Moolenaar xo_line_ensure_open(xop, 0); 262831337658SMarcel Moolenaar 2629d1a0d267SMarcel Moolenaar XOIF_CLEAR(xop, XOIF_DIV_OPEN); 263031337658SMarcel Moolenaar xo_data_append(xop, div_close, sizeof(div_close) - 1); 263131337658SMarcel Moolenaar 2632d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_PRETTY)) 263331337658SMarcel Moolenaar xo_data_append(xop, "\n", 1); 263431337658SMarcel Moolenaar break; 263531337658SMarcel Moolenaar 263631337658SMarcel Moolenaar case XO_STYLE_TEXT: 263731337658SMarcel Moolenaar xo_data_append(xop, "\n", 1); 263831337658SMarcel Moolenaar break; 263931337658SMarcel Moolenaar } 264031337658SMarcel Moolenaar } 264131337658SMarcel Moolenaar 264231337658SMarcel Moolenaar static int 264331337658SMarcel Moolenaar xo_info_compare (const void *key, const void *data) 264431337658SMarcel Moolenaar { 264531337658SMarcel Moolenaar const char *name = key; 264631337658SMarcel Moolenaar const xo_info_t *xip = data; 264731337658SMarcel Moolenaar 264831337658SMarcel Moolenaar return strcmp(name, xip->xi_name); 264931337658SMarcel Moolenaar } 265031337658SMarcel Moolenaar 265131337658SMarcel Moolenaar 265231337658SMarcel Moolenaar static xo_info_t * 26538a6eceffSPhil Shafer xo_info_find (xo_handle_t *xop, const char *name, ssize_t nlen) 265431337658SMarcel Moolenaar { 265531337658SMarcel Moolenaar xo_info_t *xip; 265631337658SMarcel Moolenaar char *cp = alloca(nlen + 1); /* Need local copy for NUL termination */ 265731337658SMarcel Moolenaar 265831337658SMarcel Moolenaar memcpy(cp, name, nlen); 265931337658SMarcel Moolenaar cp[nlen] = '\0'; 266031337658SMarcel Moolenaar 266131337658SMarcel Moolenaar xip = bsearch(cp, xop->xo_info, xop->xo_info_count, 266231337658SMarcel Moolenaar sizeof(xop->xo_info[0]), xo_info_compare); 266331337658SMarcel Moolenaar return xip; 266431337658SMarcel Moolenaar } 266531337658SMarcel Moolenaar 266631337658SMarcel Moolenaar #define CONVERT(_have, _need) (((_have) << 8) | (_need)) 266731337658SMarcel Moolenaar 266831337658SMarcel Moolenaar /* 266931337658SMarcel Moolenaar * Check to see that the conversion is safe and sane. 267031337658SMarcel Moolenaar */ 267131337658SMarcel Moolenaar static int 267231337658SMarcel Moolenaar xo_check_conversion (xo_handle_t *xop, int have_enc, int need_enc) 267331337658SMarcel Moolenaar { 267431337658SMarcel Moolenaar switch (CONVERT(have_enc, need_enc)) { 267531337658SMarcel Moolenaar case CONVERT(XF_ENC_UTF8, XF_ENC_UTF8): 267631337658SMarcel Moolenaar case CONVERT(XF_ENC_UTF8, XF_ENC_LOCALE): 267731337658SMarcel Moolenaar case CONVERT(XF_ENC_WIDE, XF_ENC_UTF8): 267831337658SMarcel Moolenaar case CONVERT(XF_ENC_WIDE, XF_ENC_LOCALE): 267931337658SMarcel Moolenaar case CONVERT(XF_ENC_LOCALE, XF_ENC_LOCALE): 268031337658SMarcel Moolenaar case CONVERT(XF_ENC_LOCALE, XF_ENC_UTF8): 268131337658SMarcel Moolenaar return 0; 268231337658SMarcel Moolenaar 268331337658SMarcel Moolenaar default: 268431337658SMarcel Moolenaar xo_failure(xop, "invalid conversion (%c:%c)", have_enc, need_enc); 268531337658SMarcel Moolenaar return 1; 268631337658SMarcel Moolenaar } 268731337658SMarcel Moolenaar } 268831337658SMarcel Moolenaar 268931337658SMarcel Moolenaar static int 269031337658SMarcel Moolenaar xo_format_string_direct (xo_handle_t *xop, xo_buffer_t *xbp, 269131337658SMarcel Moolenaar xo_xff_flags_t flags, 26928a6eceffSPhil Shafer const wchar_t *wcp, const char *cp, 26938a6eceffSPhil Shafer ssize_t len, int max, 269431337658SMarcel Moolenaar int need_enc, int have_enc) 269531337658SMarcel Moolenaar { 269631337658SMarcel Moolenaar int cols = 0; 2697c600d307SMarcel Moolenaar wchar_t wc = 0; 26988a6eceffSPhil Shafer ssize_t ilen, olen; 26998a6eceffSPhil Shafer ssize_t width; 27008a6eceffSPhil Shafer int attr = XOF_BIT_ISSET(flags, XFF_ATTR); 270131337658SMarcel Moolenaar const char *sp; 270231337658SMarcel Moolenaar 270331337658SMarcel Moolenaar if (len > 0 && !xo_buf_has_room(xbp, len)) 270431337658SMarcel Moolenaar return 0; 270531337658SMarcel Moolenaar 270631337658SMarcel Moolenaar for (;;) { 270731337658SMarcel Moolenaar if (len == 0) 270831337658SMarcel Moolenaar break; 270931337658SMarcel Moolenaar 271031337658SMarcel Moolenaar if (cp) { 271131337658SMarcel Moolenaar if (*cp == '\0') 271231337658SMarcel Moolenaar break; 271331337658SMarcel Moolenaar if ((flags & XFF_UNESCAPE) && (*cp == '\\' || *cp == '%')) { 271431337658SMarcel Moolenaar cp += 1; 271531337658SMarcel Moolenaar len -= 1; 2716264104f2SPhil Shafer if (len == 0 || *cp == '\0') 2717264104f2SPhil Shafer break; 271831337658SMarcel Moolenaar } 271931337658SMarcel Moolenaar } 272031337658SMarcel Moolenaar 272131337658SMarcel Moolenaar if (wcp && *wcp == L'\0') 272231337658SMarcel Moolenaar break; 272331337658SMarcel Moolenaar 272431337658SMarcel Moolenaar ilen = 0; 272531337658SMarcel Moolenaar 272631337658SMarcel Moolenaar switch (have_enc) { 272731337658SMarcel Moolenaar case XF_ENC_WIDE: /* Wide character */ 272831337658SMarcel Moolenaar wc = *wcp++; 272931337658SMarcel Moolenaar ilen = 1; 273031337658SMarcel Moolenaar break; 273131337658SMarcel Moolenaar 273231337658SMarcel Moolenaar case XF_ENC_UTF8: /* UTF-8 */ 273331337658SMarcel Moolenaar ilen = xo_utf8_to_wc_len(cp); 273431337658SMarcel Moolenaar if (ilen < 0) { 273531337658SMarcel Moolenaar xo_failure(xop, "invalid UTF-8 character: %02hhx", *cp); 2736d1a0d267SMarcel Moolenaar return -1; /* Can't continue; we can't find the end */ 273731337658SMarcel Moolenaar } 273831337658SMarcel Moolenaar 273931337658SMarcel Moolenaar if (len > 0 && len < ilen) { 274031337658SMarcel Moolenaar len = 0; /* Break out of the loop */ 274131337658SMarcel Moolenaar continue; 274231337658SMarcel Moolenaar } 274331337658SMarcel Moolenaar 274431337658SMarcel Moolenaar wc = xo_utf8_char(cp, ilen); 274531337658SMarcel Moolenaar if (wc == (wchar_t) -1) { 274631337658SMarcel Moolenaar xo_failure(xop, "invalid UTF-8 character: %02hhx/%d", 274731337658SMarcel Moolenaar *cp, ilen); 2748d1a0d267SMarcel Moolenaar return -1; /* Can't continue; we can't find the end */ 274931337658SMarcel Moolenaar } 275031337658SMarcel Moolenaar cp += ilen; 275131337658SMarcel Moolenaar break; 275231337658SMarcel Moolenaar 275331337658SMarcel Moolenaar case XF_ENC_LOCALE: /* Native locale */ 275431337658SMarcel Moolenaar ilen = (len > 0) ? len : MB_LEN_MAX; 275531337658SMarcel Moolenaar ilen = mbrtowc(&wc, cp, ilen, &xop->xo_mbstate); 275631337658SMarcel Moolenaar if (ilen < 0) { /* Invalid data; skip */ 275731337658SMarcel Moolenaar xo_failure(xop, "invalid mbs char: %02hhx", *cp); 2758dbf26257SAlexander Kabaev wc = L'?'; 2759dbf26257SAlexander Kabaev ilen = 1; 276031337658SMarcel Moolenaar } 2761d1a0d267SMarcel Moolenaar 276231337658SMarcel Moolenaar if (ilen == 0) { /* Hit a wide NUL character */ 276331337658SMarcel Moolenaar len = 0; 276431337658SMarcel Moolenaar continue; 276531337658SMarcel Moolenaar } 276631337658SMarcel Moolenaar 276731337658SMarcel Moolenaar cp += ilen; 276831337658SMarcel Moolenaar break; 276931337658SMarcel Moolenaar } 277031337658SMarcel Moolenaar 277131337658SMarcel Moolenaar /* Reduce len, but not below zero */ 277231337658SMarcel Moolenaar if (len > 0) { 277331337658SMarcel Moolenaar len -= ilen; 277431337658SMarcel Moolenaar if (len < 0) 277531337658SMarcel Moolenaar len = 0; 277631337658SMarcel Moolenaar } 277731337658SMarcel Moolenaar 277831337658SMarcel Moolenaar /* 277931337658SMarcel Moolenaar * Find the width-in-columns of this character, which must be done 278031337658SMarcel Moolenaar * in wide characters, since we lack a mbswidth() function. If 278131337658SMarcel Moolenaar * it doesn't fit 278231337658SMarcel Moolenaar */ 2783d1a0d267SMarcel Moolenaar width = xo_wcwidth(wc); 278431337658SMarcel Moolenaar if (width < 0) 278531337658SMarcel Moolenaar width = iswcntrl(wc) ? 0 : 1; 278631337658SMarcel Moolenaar 2787788ca347SMarcel Moolenaar if (xo_style(xop) == XO_STYLE_TEXT || xo_style(xop) == XO_STYLE_HTML) { 278831337658SMarcel Moolenaar if (max > 0 && cols + width > max) 278931337658SMarcel Moolenaar break; 279031337658SMarcel Moolenaar } 279131337658SMarcel Moolenaar 279231337658SMarcel Moolenaar switch (need_enc) { 279331337658SMarcel Moolenaar case XF_ENC_UTF8: 279431337658SMarcel Moolenaar 279531337658SMarcel Moolenaar /* Output in UTF-8 needs to be escaped, based on the style */ 2796788ca347SMarcel Moolenaar switch (xo_style(xop)) { 279731337658SMarcel Moolenaar case XO_STYLE_XML: 279831337658SMarcel Moolenaar case XO_STYLE_HTML: 279931337658SMarcel Moolenaar if (wc == '<') 280031337658SMarcel Moolenaar sp = xo_xml_lt; 280131337658SMarcel Moolenaar else if (wc == '>') 280231337658SMarcel Moolenaar sp = xo_xml_gt; 280331337658SMarcel Moolenaar else if (wc == '&') 280431337658SMarcel Moolenaar sp = xo_xml_amp; 280531337658SMarcel Moolenaar else if (attr && wc == '"') 280631337658SMarcel Moolenaar sp = xo_xml_quot; 280731337658SMarcel Moolenaar else 280831337658SMarcel Moolenaar break; 280931337658SMarcel Moolenaar 28108a6eceffSPhil Shafer ssize_t slen = strlen(sp); 281131337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, slen - 1)) 281231337658SMarcel Moolenaar return -1; 281331337658SMarcel Moolenaar 281431337658SMarcel Moolenaar memcpy(xbp->xb_curp, sp, slen); 281531337658SMarcel Moolenaar xbp->xb_curp += slen; 281631337658SMarcel Moolenaar goto done_with_encoding; /* Need multi-level 'break' */ 281731337658SMarcel Moolenaar 281831337658SMarcel Moolenaar case XO_STYLE_JSON: 2819545ddfbeSMarcel Moolenaar if (wc != '\\' && wc != '"' && wc != '\n' && wc != '\r') 282031337658SMarcel Moolenaar break; 282131337658SMarcel Moolenaar 282231337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, 2)) 282331337658SMarcel Moolenaar return -1; 282431337658SMarcel Moolenaar 282531337658SMarcel Moolenaar *xbp->xb_curp++ = '\\'; 2826545ddfbeSMarcel Moolenaar if (wc == '\n') 2827545ddfbeSMarcel Moolenaar wc = 'n'; 2828545ddfbeSMarcel Moolenaar else if (wc == '\r') 2829545ddfbeSMarcel Moolenaar wc = 'r'; 2830545ddfbeSMarcel Moolenaar else wc = wc & 0x7f; 2831545ddfbeSMarcel Moolenaar 2832545ddfbeSMarcel Moolenaar *xbp->xb_curp++ = wc; 283331337658SMarcel Moolenaar goto done_with_encoding; 2834d1a0d267SMarcel Moolenaar 2835d1a0d267SMarcel Moolenaar case XO_STYLE_SDPARAMS: 2836d1a0d267SMarcel Moolenaar if (wc != '\\' && wc != '"' && wc != ']') 2837d1a0d267SMarcel Moolenaar break; 2838d1a0d267SMarcel Moolenaar 2839d1a0d267SMarcel Moolenaar if (!xo_buf_has_room(xbp, 2)) 2840d1a0d267SMarcel Moolenaar return -1; 2841d1a0d267SMarcel Moolenaar 2842d1a0d267SMarcel Moolenaar *xbp->xb_curp++ = '\\'; 2843d1a0d267SMarcel Moolenaar wc = wc & 0x7f; 2844d1a0d267SMarcel Moolenaar *xbp->xb_curp++ = wc; 2845d1a0d267SMarcel Moolenaar goto done_with_encoding; 284631337658SMarcel Moolenaar } 284731337658SMarcel Moolenaar 284831337658SMarcel Moolenaar olen = xo_utf8_emit_len(wc); 284931337658SMarcel Moolenaar if (olen < 0) { 285031337658SMarcel Moolenaar xo_failure(xop, "ignoring bad length"); 285131337658SMarcel Moolenaar continue; 285231337658SMarcel Moolenaar } 285331337658SMarcel Moolenaar 285431337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, olen)) 285531337658SMarcel Moolenaar return -1; 285631337658SMarcel Moolenaar 285731337658SMarcel Moolenaar xo_utf8_emit_char(xbp->xb_curp, olen, wc); 285831337658SMarcel Moolenaar xbp->xb_curp += olen; 285931337658SMarcel Moolenaar break; 286031337658SMarcel Moolenaar 286131337658SMarcel Moolenaar case XF_ENC_LOCALE: 286231337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, MB_LEN_MAX + 1)) 286331337658SMarcel Moolenaar return -1; 286431337658SMarcel Moolenaar 286531337658SMarcel Moolenaar olen = wcrtomb(xbp->xb_curp, wc, &xop->xo_mbstate); 286631337658SMarcel Moolenaar if (olen <= 0) { 286731337658SMarcel Moolenaar xo_failure(xop, "could not convert wide char: %lx", 286831337658SMarcel Moolenaar (unsigned long) wc); 286931337658SMarcel Moolenaar width = 1; 287031337658SMarcel Moolenaar *xbp->xb_curp++ = '?'; 287131337658SMarcel Moolenaar } else 287231337658SMarcel Moolenaar xbp->xb_curp += olen; 287331337658SMarcel Moolenaar break; 287431337658SMarcel Moolenaar } 287531337658SMarcel Moolenaar 287631337658SMarcel Moolenaar done_with_encoding: 287731337658SMarcel Moolenaar cols += width; 287831337658SMarcel Moolenaar } 287931337658SMarcel Moolenaar 288031337658SMarcel Moolenaar return cols; 288131337658SMarcel Moolenaar } 288231337658SMarcel Moolenaar 288331337658SMarcel Moolenaar static int 2884d1a0d267SMarcel Moolenaar xo_needed_encoding (xo_handle_t *xop) 2885d1a0d267SMarcel Moolenaar { 2886d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_UTF8)) /* Check the override flag */ 2887d1a0d267SMarcel Moolenaar return XF_ENC_UTF8; 2888d1a0d267SMarcel Moolenaar 2889d1a0d267SMarcel Moolenaar if (xo_style(xop) == XO_STYLE_TEXT) /* Text means locale */ 2890d1a0d267SMarcel Moolenaar return XF_ENC_LOCALE; 2891d1a0d267SMarcel Moolenaar 2892d1a0d267SMarcel Moolenaar return XF_ENC_UTF8; /* Otherwise, we love UTF-8 */ 2893d1a0d267SMarcel Moolenaar } 2894d1a0d267SMarcel Moolenaar 28958a6eceffSPhil Shafer static ssize_t 289631337658SMarcel Moolenaar xo_format_string (xo_handle_t *xop, xo_buffer_t *xbp, xo_xff_flags_t flags, 289731337658SMarcel Moolenaar xo_format_t *xfp) 289831337658SMarcel Moolenaar { 289931337658SMarcel Moolenaar static char null[] = "(null)"; 2900d1a0d267SMarcel Moolenaar static char null_no_quotes[] = "null"; 2901a0f704ffSMarcel Moolenaar 290231337658SMarcel Moolenaar char *cp = NULL; 290331337658SMarcel Moolenaar wchar_t *wcp = NULL; 29048a6eceffSPhil Shafer ssize_t len; 29058a6eceffSPhil Shafer ssize_t cols = 0, rc = 0; 29068a6eceffSPhil Shafer ssize_t off = xbp->xb_curp - xbp->xb_bufp, off2; 2907d1a0d267SMarcel Moolenaar int need_enc = xo_needed_encoding(xop); 290831337658SMarcel Moolenaar 290931337658SMarcel Moolenaar if (xo_check_conversion(xop, xfp->xf_enc, need_enc)) 291031337658SMarcel Moolenaar return 0; 291131337658SMarcel Moolenaar 2912a0f704ffSMarcel Moolenaar len = xfp->xf_width[XF_WIDTH_SIZE]; 2913a0f704ffSMarcel Moolenaar 2914d1a0d267SMarcel Moolenaar if (xfp->xf_fc == 'm') { 2915d1a0d267SMarcel Moolenaar cp = strerror(xop->xo_errno); 2916d1a0d267SMarcel Moolenaar if (len < 0) 2917d1a0d267SMarcel Moolenaar len = cp ? strlen(cp) : 0; 2918d1a0d267SMarcel Moolenaar goto normal_string; 2919d1a0d267SMarcel Moolenaar 2920d1a0d267SMarcel Moolenaar } else if (xfp->xf_enc == XF_ENC_WIDE) { 292131337658SMarcel Moolenaar wcp = va_arg(xop->xo_vap, wchar_t *); 292231337658SMarcel Moolenaar if (xfp->xf_skip) 292331337658SMarcel Moolenaar return 0; 292431337658SMarcel Moolenaar 2925a0f704ffSMarcel Moolenaar /* 2926a0f704ffSMarcel Moolenaar * Dont' deref NULL; use the traditional "(null)" instead 2927a0f704ffSMarcel Moolenaar * of the more accurate "who's been a naughty boy, then?". 2928a0f704ffSMarcel Moolenaar */ 2929a0f704ffSMarcel Moolenaar if (wcp == NULL) { 2930a0f704ffSMarcel Moolenaar cp = null; 2931a0f704ffSMarcel Moolenaar len = sizeof(null) - 1; 2932a0f704ffSMarcel Moolenaar } 2933a0f704ffSMarcel Moolenaar 293431337658SMarcel Moolenaar } else { 293531337658SMarcel Moolenaar cp = va_arg(xop->xo_vap, char *); /* UTF-8 or native */ 2936d1a0d267SMarcel Moolenaar 2937d1a0d267SMarcel Moolenaar normal_string: 293831337658SMarcel Moolenaar if (xfp->xf_skip) 293931337658SMarcel Moolenaar return 0; 294031337658SMarcel Moolenaar 2941a0f704ffSMarcel Moolenaar /* Echo "Dont' deref NULL" logic */ 2942a0f704ffSMarcel Moolenaar if (cp == NULL) { 2943d1a0d267SMarcel Moolenaar if ((flags & XFF_NOQUOTE) && xo_style_is_encoding(xop)) { 2944d1a0d267SMarcel Moolenaar cp = null_no_quotes; 2945d1a0d267SMarcel Moolenaar len = sizeof(null_no_quotes) - 1; 2946d1a0d267SMarcel Moolenaar } else { 2947a0f704ffSMarcel Moolenaar cp = null; 2948a0f704ffSMarcel Moolenaar len = sizeof(null) - 1; 2949a0f704ffSMarcel Moolenaar } 2950d1a0d267SMarcel Moolenaar } 2951a0f704ffSMarcel Moolenaar 295231337658SMarcel Moolenaar /* 295331337658SMarcel Moolenaar * Optimize the most common case, which is "%s". We just 295431337658SMarcel Moolenaar * need to copy the complete string to the output buffer. 295531337658SMarcel Moolenaar */ 295631337658SMarcel Moolenaar if (xfp->xf_enc == need_enc 295731337658SMarcel Moolenaar && xfp->xf_width[XF_WIDTH_MIN] < 0 295831337658SMarcel Moolenaar && xfp->xf_width[XF_WIDTH_SIZE] < 0 295931337658SMarcel Moolenaar && xfp->xf_width[XF_WIDTH_MAX] < 0 2960d1a0d267SMarcel Moolenaar && !(XOIF_ISSET(xop, XOIF_ANCHOR) 2961d1a0d267SMarcel Moolenaar || XOF_ISSET(xop, XOF_COLUMNS))) { 296231337658SMarcel Moolenaar len = strlen(cp); 296331337658SMarcel Moolenaar xo_buf_escape(xop, xbp, cp, len, flags); 296431337658SMarcel Moolenaar 296531337658SMarcel Moolenaar /* 296631337658SMarcel Moolenaar * Our caller expects xb_curp left untouched, so we have 296731337658SMarcel Moolenaar * to reset it and return the number of bytes written to 296831337658SMarcel Moolenaar * the buffer. 296931337658SMarcel Moolenaar */ 297031337658SMarcel Moolenaar off2 = xbp->xb_curp - xbp->xb_bufp; 297131337658SMarcel Moolenaar rc = off2 - off; 297231337658SMarcel Moolenaar xbp->xb_curp = xbp->xb_bufp + off; 297331337658SMarcel Moolenaar 297431337658SMarcel Moolenaar return rc; 297531337658SMarcel Moolenaar } 297631337658SMarcel Moolenaar } 297731337658SMarcel Moolenaar 297831337658SMarcel Moolenaar cols = xo_format_string_direct(xop, xbp, flags, wcp, cp, len, 297931337658SMarcel Moolenaar xfp->xf_width[XF_WIDTH_MAX], 298031337658SMarcel Moolenaar need_enc, xfp->xf_enc); 298131337658SMarcel Moolenaar if (cols < 0) 298231337658SMarcel Moolenaar goto bail; 298331337658SMarcel Moolenaar 298431337658SMarcel Moolenaar /* 298531337658SMarcel Moolenaar * xo_buf_append* will move xb_curp, so we save/restore it. 298631337658SMarcel Moolenaar */ 298731337658SMarcel Moolenaar off2 = xbp->xb_curp - xbp->xb_bufp; 298831337658SMarcel Moolenaar rc = off2 - off; 298931337658SMarcel Moolenaar xbp->xb_curp = xbp->xb_bufp + off; 299031337658SMarcel Moolenaar 299131337658SMarcel Moolenaar if (cols < xfp->xf_width[XF_WIDTH_MIN]) { 299231337658SMarcel Moolenaar /* 299331337658SMarcel Moolenaar * Find the number of columns needed to display the string. 299431337658SMarcel Moolenaar * If we have the original wide string, we just call wcswidth, 299531337658SMarcel Moolenaar * but if we did the work ourselves, then we need to do it. 299631337658SMarcel Moolenaar */ 299731337658SMarcel Moolenaar int delta = xfp->xf_width[XF_WIDTH_MIN] - cols; 2998ee5cf116SPhil Shafer if (!xo_buf_has_room(xbp, xfp->xf_width[XF_WIDTH_MIN])) 299931337658SMarcel Moolenaar goto bail; 300031337658SMarcel Moolenaar 300131337658SMarcel Moolenaar /* 300231337658SMarcel Moolenaar * If seen_minus, then pad on the right; otherwise move it so 300331337658SMarcel Moolenaar * we can pad on the left. 300431337658SMarcel Moolenaar */ 300531337658SMarcel Moolenaar if (xfp->xf_seen_minus) { 300631337658SMarcel Moolenaar cp = xbp->xb_curp + rc; 300731337658SMarcel Moolenaar } else { 300831337658SMarcel Moolenaar cp = xbp->xb_curp; 300931337658SMarcel Moolenaar memmove(xbp->xb_curp + delta, xbp->xb_curp, rc); 301031337658SMarcel Moolenaar } 301131337658SMarcel Moolenaar 301231337658SMarcel Moolenaar /* Set the padding */ 301331337658SMarcel Moolenaar memset(cp, (xfp->xf_leading_zero > 0) ? '0' : ' ', delta); 301431337658SMarcel Moolenaar rc += delta; 301531337658SMarcel Moolenaar cols += delta; 301631337658SMarcel Moolenaar } 301731337658SMarcel Moolenaar 3018d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_COLUMNS)) 301931337658SMarcel Moolenaar xop->xo_columns += cols; 3020d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_ANCHOR)) 302131337658SMarcel Moolenaar xop->xo_anchor_columns += cols; 302231337658SMarcel Moolenaar 302331337658SMarcel Moolenaar return rc; 302431337658SMarcel Moolenaar 302531337658SMarcel Moolenaar bail: 302631337658SMarcel Moolenaar xbp->xb_curp = xbp->xb_bufp + off; 302731337658SMarcel Moolenaar return 0; 302831337658SMarcel Moolenaar } 302931337658SMarcel Moolenaar 3030d1a0d267SMarcel Moolenaar /* 3031d1a0d267SMarcel Moolenaar * Look backwards in a buffer to find a numeric value 3032d1a0d267SMarcel Moolenaar */ 3033d1a0d267SMarcel Moolenaar static int 30348a6eceffSPhil Shafer xo_buf_find_last_number (xo_buffer_t *xbp, ssize_t start_offset) 3035d1a0d267SMarcel Moolenaar { 3036d1a0d267SMarcel Moolenaar int rc = 0; /* Fail with zero */ 3037d1a0d267SMarcel Moolenaar int digit = 1; 3038d1a0d267SMarcel Moolenaar char *sp = xbp->xb_bufp; 3039d1a0d267SMarcel Moolenaar char *cp = sp + start_offset; 3040d1a0d267SMarcel Moolenaar 3041d1a0d267SMarcel Moolenaar while (--cp >= sp) 3042d1a0d267SMarcel Moolenaar if (isdigit((int) *cp)) 3043d1a0d267SMarcel Moolenaar break; 3044d1a0d267SMarcel Moolenaar 3045d1a0d267SMarcel Moolenaar for ( ; cp >= sp; cp--) { 3046d1a0d267SMarcel Moolenaar if (!isdigit((int) *cp)) 3047d1a0d267SMarcel Moolenaar break; 3048d1a0d267SMarcel Moolenaar rc += (*cp - '0') * digit; 3049d1a0d267SMarcel Moolenaar digit *= 10; 3050d1a0d267SMarcel Moolenaar } 3051d1a0d267SMarcel Moolenaar 3052d1a0d267SMarcel Moolenaar return rc; 3053d1a0d267SMarcel Moolenaar } 3054d1a0d267SMarcel Moolenaar 30558a6eceffSPhil Shafer static ssize_t 30568a6eceffSPhil Shafer xo_count_utf8_cols (const char *str, ssize_t len) 3057d1a0d267SMarcel Moolenaar { 30588a6eceffSPhil Shafer ssize_t tlen; 3059d1a0d267SMarcel Moolenaar wchar_t wc; 30608a6eceffSPhil Shafer ssize_t cols = 0; 3061d1a0d267SMarcel Moolenaar const char *ep = str + len; 3062d1a0d267SMarcel Moolenaar 3063d1a0d267SMarcel Moolenaar while (str < ep) { 3064d1a0d267SMarcel Moolenaar tlen = xo_utf8_to_wc_len(str); 3065d1a0d267SMarcel Moolenaar if (tlen < 0) /* Broken input is very bad */ 3066d1a0d267SMarcel Moolenaar return cols; 3067d1a0d267SMarcel Moolenaar 3068d1a0d267SMarcel Moolenaar wc = xo_utf8_char(str, tlen); 3069d1a0d267SMarcel Moolenaar if (wc == (wchar_t) -1) 3070d1a0d267SMarcel Moolenaar return cols; 3071d1a0d267SMarcel Moolenaar 3072d1a0d267SMarcel Moolenaar /* We only print printable characters */ 3073d1a0d267SMarcel Moolenaar if (iswprint((wint_t) wc)) { 3074d1a0d267SMarcel Moolenaar /* 3075d1a0d267SMarcel Moolenaar * Find the width-in-columns of this character, which must be done 3076d1a0d267SMarcel Moolenaar * in wide characters, since we lack a mbswidth() function. 3077d1a0d267SMarcel Moolenaar */ 30788a6eceffSPhil Shafer ssize_t width = xo_wcwidth(wc); 3079d1a0d267SMarcel Moolenaar if (width < 0) 3080d1a0d267SMarcel Moolenaar width = iswcntrl(wc) ? 0 : 1; 3081d1a0d267SMarcel Moolenaar 3082d1a0d267SMarcel Moolenaar cols += width; 3083d1a0d267SMarcel Moolenaar } 3084d1a0d267SMarcel Moolenaar 3085d1a0d267SMarcel Moolenaar str += tlen; 3086d1a0d267SMarcel Moolenaar } 3087d1a0d267SMarcel Moolenaar 3088d1a0d267SMarcel Moolenaar return cols; 3089d1a0d267SMarcel Moolenaar } 3090d1a0d267SMarcel Moolenaar 3091d1a0d267SMarcel Moolenaar #ifdef HAVE_GETTEXT 3092d1a0d267SMarcel Moolenaar static inline const char * 3093d1a0d267SMarcel Moolenaar xo_dgettext (xo_handle_t *xop, const char *str) 3094d1a0d267SMarcel Moolenaar { 3095d1a0d267SMarcel Moolenaar const char *domainname = xop->xo_gt_domain; 3096d1a0d267SMarcel Moolenaar const char *res; 3097d1a0d267SMarcel Moolenaar 3098d1a0d267SMarcel Moolenaar res = dgettext(domainname, str); 3099d1a0d267SMarcel Moolenaar 3100d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_LOG_GETTEXT)) 3101d1a0d267SMarcel Moolenaar fprintf(stderr, "xo: gettext: %s%s%smsgid \"%s\" returns \"%s\"\n", 3102d1a0d267SMarcel Moolenaar domainname ? "domain \"" : "", xo_printable(domainname), 3103d1a0d267SMarcel Moolenaar domainname ? "\", " : "", xo_printable(str), xo_printable(res)); 3104d1a0d267SMarcel Moolenaar 3105d1a0d267SMarcel Moolenaar return res; 3106d1a0d267SMarcel Moolenaar } 3107d1a0d267SMarcel Moolenaar 3108d1a0d267SMarcel Moolenaar static inline const char * 3109d1a0d267SMarcel Moolenaar xo_dngettext (xo_handle_t *xop, const char *sing, const char *plural, 3110d1a0d267SMarcel Moolenaar unsigned long int n) 3111d1a0d267SMarcel Moolenaar { 3112d1a0d267SMarcel Moolenaar const char *domainname = xop->xo_gt_domain; 3113d1a0d267SMarcel Moolenaar const char *res; 3114d1a0d267SMarcel Moolenaar 3115d1a0d267SMarcel Moolenaar res = dngettext(domainname, sing, plural, n); 3116d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_LOG_GETTEXT)) 3117d1a0d267SMarcel Moolenaar fprintf(stderr, "xo: gettext: %s%s%s" 3118d1a0d267SMarcel Moolenaar "msgid \"%s\", msgid_plural \"%s\" (%lu) returns \"%s\"\n", 3119d1a0d267SMarcel Moolenaar domainname ? "domain \"" : "", 3120d1a0d267SMarcel Moolenaar xo_printable(domainname), domainname ? "\", " : "", 3121d1a0d267SMarcel Moolenaar xo_printable(sing), 3122d1a0d267SMarcel Moolenaar xo_printable(plural), n, xo_printable(res)); 3123d1a0d267SMarcel Moolenaar 3124d1a0d267SMarcel Moolenaar return res; 3125d1a0d267SMarcel Moolenaar } 3126d1a0d267SMarcel Moolenaar #else /* HAVE_GETTEXT */ 3127d1a0d267SMarcel Moolenaar static inline const char * 3128d1a0d267SMarcel Moolenaar xo_dgettext (xo_handle_t *xop UNUSED, const char *str) 3129d1a0d267SMarcel Moolenaar { 3130d1a0d267SMarcel Moolenaar return str; 3131d1a0d267SMarcel Moolenaar } 3132d1a0d267SMarcel Moolenaar 3133d1a0d267SMarcel Moolenaar static inline const char * 3134d1a0d267SMarcel Moolenaar xo_dngettext (xo_handle_t *xop UNUSED, const char *singular, 3135d1a0d267SMarcel Moolenaar const char *plural, unsigned long int n) 3136d1a0d267SMarcel Moolenaar { 3137d1a0d267SMarcel Moolenaar return (n == 1) ? singular : plural; 3138d1a0d267SMarcel Moolenaar } 3139d1a0d267SMarcel Moolenaar #endif /* HAVE_GETTEXT */ 3140d1a0d267SMarcel Moolenaar 3141d1a0d267SMarcel Moolenaar /* 3142d1a0d267SMarcel Moolenaar * This is really _re_formatting, since the normal format code has 3143d1a0d267SMarcel Moolenaar * generated a beautiful string into xo_data, starting at 3144d1a0d267SMarcel Moolenaar * start_offset. We need to see if it's plural, which means 3145d1a0d267SMarcel Moolenaar * comma-separated options, or singular. Then we make the appropriate 3146d1a0d267SMarcel Moolenaar * call to d[n]gettext() to get the locale-based version. Note that 3147d1a0d267SMarcel Moolenaar * both input and output of gettext() this should be UTF-8. 3148d1a0d267SMarcel Moolenaar */ 31498a6eceffSPhil Shafer static ssize_t 3150d1a0d267SMarcel Moolenaar xo_format_gettext (xo_handle_t *xop, xo_xff_flags_t flags, 31518a6eceffSPhil Shafer ssize_t start_offset, ssize_t cols, int need_enc) 3152d1a0d267SMarcel Moolenaar { 3153d1a0d267SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_data; 3154d1a0d267SMarcel Moolenaar 3155d1a0d267SMarcel Moolenaar if (!xo_buf_has_room(xbp, 1)) 3156d1a0d267SMarcel Moolenaar return cols; 3157d1a0d267SMarcel Moolenaar 3158d1a0d267SMarcel Moolenaar xbp->xb_curp[0] = '\0'; /* NUL-terminate the input string */ 3159d1a0d267SMarcel Moolenaar 3160d1a0d267SMarcel Moolenaar char *cp = xbp->xb_bufp + start_offset; 31618a6eceffSPhil Shafer ssize_t len = xbp->xb_curp - cp; 3162d1a0d267SMarcel Moolenaar const char *newstr = NULL; 3163d1a0d267SMarcel Moolenaar 3164d1a0d267SMarcel Moolenaar /* 3165d1a0d267SMarcel Moolenaar * The plural flag asks us to look backwards at the last numeric 3166d1a0d267SMarcel Moolenaar * value rendered and disect the string into two pieces. 3167d1a0d267SMarcel Moolenaar */ 3168d1a0d267SMarcel Moolenaar if (flags & XFF_GT_PLURAL) { 3169d1a0d267SMarcel Moolenaar int n = xo_buf_find_last_number(xbp, start_offset); 3170d1a0d267SMarcel Moolenaar char *two = memchr(cp, (int) ',', len); 3171d1a0d267SMarcel Moolenaar if (two == NULL) { 3172d1a0d267SMarcel Moolenaar xo_failure(xop, "no comma in plural gettext field: '%s'", cp); 3173d1a0d267SMarcel Moolenaar return cols; 3174d1a0d267SMarcel Moolenaar } 3175d1a0d267SMarcel Moolenaar 3176d1a0d267SMarcel Moolenaar if (two == cp) { 3177d1a0d267SMarcel Moolenaar xo_failure(xop, "nothing before comma in plural gettext " 3178d1a0d267SMarcel Moolenaar "field: '%s'", cp); 3179d1a0d267SMarcel Moolenaar return cols; 3180d1a0d267SMarcel Moolenaar } 3181d1a0d267SMarcel Moolenaar 3182d1a0d267SMarcel Moolenaar if (two == xbp->xb_curp) { 3183d1a0d267SMarcel Moolenaar xo_failure(xop, "nothing after comma in plural gettext " 3184d1a0d267SMarcel Moolenaar "field: '%s'", cp); 3185d1a0d267SMarcel Moolenaar return cols; 3186d1a0d267SMarcel Moolenaar } 3187d1a0d267SMarcel Moolenaar 3188d1a0d267SMarcel Moolenaar *two++ = '\0'; 3189d1a0d267SMarcel Moolenaar if (flags & XFF_GT_FIELD) { 3190d1a0d267SMarcel Moolenaar newstr = xo_dngettext(xop, cp, two, n); 3191d1a0d267SMarcel Moolenaar } else { 3192d1a0d267SMarcel Moolenaar /* Don't do a gettext() look up, just get the plural form */ 3193d1a0d267SMarcel Moolenaar newstr = (n == 1) ? cp : two; 3194d1a0d267SMarcel Moolenaar } 3195d1a0d267SMarcel Moolenaar 3196d1a0d267SMarcel Moolenaar /* 3197d1a0d267SMarcel Moolenaar * If we returned the first string, optimize a bit by 3198d1a0d267SMarcel Moolenaar * backing up over comma 3199d1a0d267SMarcel Moolenaar */ 3200d1a0d267SMarcel Moolenaar if (newstr == cp) { 3201d1a0d267SMarcel Moolenaar xbp->xb_curp = two - 1; /* One for comma */ 3202d1a0d267SMarcel Moolenaar /* 3203d1a0d267SMarcel Moolenaar * If the caller wanted UTF8, we're done; nothing changed, 3204d1a0d267SMarcel Moolenaar * but we need to count the columns used. 3205d1a0d267SMarcel Moolenaar */ 3206d1a0d267SMarcel Moolenaar if (need_enc == XF_ENC_UTF8) 3207d1a0d267SMarcel Moolenaar return xo_count_utf8_cols(cp, xbp->xb_curp - cp); 3208d1a0d267SMarcel Moolenaar } 3209d1a0d267SMarcel Moolenaar 3210d1a0d267SMarcel Moolenaar } else { 3211d1a0d267SMarcel Moolenaar /* The simple case (singular) */ 3212d1a0d267SMarcel Moolenaar newstr = xo_dgettext(xop, cp); 3213d1a0d267SMarcel Moolenaar 3214d1a0d267SMarcel Moolenaar if (newstr == cp) { 3215d1a0d267SMarcel Moolenaar /* If the caller wanted UTF8, we're done; nothing changed */ 3216d1a0d267SMarcel Moolenaar if (need_enc == XF_ENC_UTF8) 3217d1a0d267SMarcel Moolenaar return cols; 3218d1a0d267SMarcel Moolenaar } 3219d1a0d267SMarcel Moolenaar } 3220d1a0d267SMarcel Moolenaar 3221d1a0d267SMarcel Moolenaar /* 3222d1a0d267SMarcel Moolenaar * Since the new string string might be in gettext's buffer or 3223d1a0d267SMarcel Moolenaar * in the buffer (as the plural form), we make a copy. 3224d1a0d267SMarcel Moolenaar */ 32258a6eceffSPhil Shafer ssize_t nlen = strlen(newstr); 3226d1a0d267SMarcel Moolenaar char *newcopy = alloca(nlen + 1); 3227d1a0d267SMarcel Moolenaar memcpy(newcopy, newstr, nlen + 1); 3228d1a0d267SMarcel Moolenaar 3229d1a0d267SMarcel Moolenaar xbp->xb_curp = xbp->xb_bufp + start_offset; /* Reset the buffer */ 3230d1a0d267SMarcel Moolenaar return xo_format_string_direct(xop, xbp, flags, NULL, newcopy, nlen, 0, 3231d1a0d267SMarcel Moolenaar need_enc, XF_ENC_UTF8); 3232d1a0d267SMarcel Moolenaar } 3233d1a0d267SMarcel Moolenaar 323431337658SMarcel Moolenaar static void 32358a6eceffSPhil Shafer xo_data_append_content (xo_handle_t *xop, const char *str, ssize_t len, 3236d1a0d267SMarcel Moolenaar xo_xff_flags_t flags) 323731337658SMarcel Moolenaar { 323831337658SMarcel Moolenaar int cols; 3239d1a0d267SMarcel Moolenaar int need_enc = xo_needed_encoding(xop); 32408a6eceffSPhil Shafer ssize_t start_offset = xo_buf_offset(&xop->xo_data); 324131337658SMarcel Moolenaar 3242d1a0d267SMarcel Moolenaar cols = xo_format_string_direct(xop, &xop->xo_data, XFF_UNESCAPE | flags, 324331337658SMarcel Moolenaar NULL, str, len, -1, 324431337658SMarcel Moolenaar need_enc, XF_ENC_UTF8); 3245d1a0d267SMarcel Moolenaar if (flags & XFF_GT_FLAGS) 3246d1a0d267SMarcel Moolenaar cols = xo_format_gettext(xop, flags, start_offset, cols, need_enc); 324731337658SMarcel Moolenaar 3248d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_COLUMNS)) 324931337658SMarcel Moolenaar xop->xo_columns += cols; 3250d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_ANCHOR)) 325131337658SMarcel Moolenaar xop->xo_anchor_columns += cols; 325231337658SMarcel Moolenaar } 325331337658SMarcel Moolenaar 3254f2b7bf8aSPhil Shafer /** 3255f2b7bf8aSPhil Shafer * Bump one of the 'width' values in a format strings (e.g. "%40.50.60s"). 3256f2b7bf8aSPhil Shafer * @param xfp Formatting instructions 3257f2b7bf8aSPhil Shafer * @param digit Single digit (0-9) of input 3258f2b7bf8aSPhil Shafer */ 325931337658SMarcel Moolenaar static void 326031337658SMarcel Moolenaar xo_bump_width (xo_format_t *xfp, int digit) 326131337658SMarcel Moolenaar { 326231337658SMarcel Moolenaar int *ip = &xfp->xf_width[xfp->xf_dots]; 326331337658SMarcel Moolenaar 326431337658SMarcel Moolenaar *ip = ((*ip > 0) ? *ip : 0) * 10 + digit; 326531337658SMarcel Moolenaar } 326631337658SMarcel Moolenaar 32678a6eceffSPhil Shafer static ssize_t 32688a6eceffSPhil Shafer xo_trim_ws (xo_buffer_t *xbp, ssize_t len) 326931337658SMarcel Moolenaar { 327031337658SMarcel Moolenaar char *cp, *sp, *ep; 32718a6eceffSPhil Shafer ssize_t delta; 327231337658SMarcel Moolenaar 327331337658SMarcel Moolenaar /* First trim leading space */ 327431337658SMarcel Moolenaar for (cp = sp = xbp->xb_curp, ep = cp + len; cp < ep; cp++) { 327531337658SMarcel Moolenaar if (*cp != ' ') 327631337658SMarcel Moolenaar break; 327731337658SMarcel Moolenaar } 327831337658SMarcel Moolenaar 327931337658SMarcel Moolenaar delta = cp - sp; 328031337658SMarcel Moolenaar if (delta) { 328131337658SMarcel Moolenaar len -= delta; 328231337658SMarcel Moolenaar memmove(sp, cp, len); 328331337658SMarcel Moolenaar } 328431337658SMarcel Moolenaar 328531337658SMarcel Moolenaar /* Then trim off the end */ 328631337658SMarcel Moolenaar for (cp = xbp->xb_curp, sp = ep = cp + len; cp < ep; ep--) { 328731337658SMarcel Moolenaar if (ep[-1] != ' ') 328831337658SMarcel Moolenaar break; 328931337658SMarcel Moolenaar } 329031337658SMarcel Moolenaar 329131337658SMarcel Moolenaar delta = sp - ep; 329231337658SMarcel Moolenaar if (delta) { 329331337658SMarcel Moolenaar len -= delta; 329431337658SMarcel Moolenaar cp[len] = '\0'; 329531337658SMarcel Moolenaar } 329631337658SMarcel Moolenaar 329731337658SMarcel Moolenaar return len; 329831337658SMarcel Moolenaar } 329931337658SMarcel Moolenaar 3300d1a0d267SMarcel Moolenaar /* 3301d1a0d267SMarcel Moolenaar * Interface to format a single field. The arguments are in xo_vap, 3302d1a0d267SMarcel Moolenaar * and the format is in 'fmt'. If 'xbp' is null, we use xop->xo_data; 3303d1a0d267SMarcel Moolenaar * this is the most common case. 3304d1a0d267SMarcel Moolenaar */ 33058a6eceffSPhil Shafer static ssize_t 3306d1a0d267SMarcel Moolenaar xo_do_format_field (xo_handle_t *xop, xo_buffer_t *xbp, 33078a6eceffSPhil Shafer const char *fmt, ssize_t flen, xo_xff_flags_t flags) 330831337658SMarcel Moolenaar { 330931337658SMarcel Moolenaar xo_format_t xf; 331031337658SMarcel Moolenaar const char *cp, *ep, *sp, *xp = NULL; 33118a6eceffSPhil Shafer ssize_t rc, cols; 3312788ca347SMarcel Moolenaar int style = (flags & XFF_XML) ? XO_STYLE_XML : xo_style(xop); 33138a6eceffSPhil Shafer unsigned make_output = !(flags & XFF_NO_OUTPUT) ? 1 : 0; 3314d1a0d267SMarcel Moolenaar int need_enc = xo_needed_encoding(xop); 3315d1a0d267SMarcel Moolenaar int real_need_enc = need_enc; 33168a6eceffSPhil Shafer ssize_t old_cols = xop->xo_columns; 3317d1a0d267SMarcel Moolenaar 3318d1a0d267SMarcel Moolenaar /* The gettext interface is UTF-8, so we'll need that for now */ 3319d1a0d267SMarcel Moolenaar if (flags & XFF_GT_FIELD) 3320d1a0d267SMarcel Moolenaar need_enc = XF_ENC_UTF8; 332131337658SMarcel Moolenaar 332231337658SMarcel Moolenaar if (xbp == NULL) 332331337658SMarcel Moolenaar xbp = &xop->xo_data; 332431337658SMarcel Moolenaar 33258a6eceffSPhil Shafer ssize_t start_offset = xo_buf_offset(xbp); 3326d1a0d267SMarcel Moolenaar 332731337658SMarcel Moolenaar for (cp = fmt, ep = fmt + flen; cp < ep; cp++) { 3328d1a0d267SMarcel Moolenaar /* 3329d1a0d267SMarcel Moolenaar * Since we're starting a new field, save the starting offset. 3330d1a0d267SMarcel Moolenaar * We'll need this later for field-related operations. 3331d1a0d267SMarcel Moolenaar */ 3332d1a0d267SMarcel Moolenaar 333331337658SMarcel Moolenaar if (*cp != '%') { 333431337658SMarcel Moolenaar add_one: 333531337658SMarcel Moolenaar if (xp == NULL) 333631337658SMarcel Moolenaar xp = cp; 333731337658SMarcel Moolenaar 333831337658SMarcel Moolenaar if (*cp == '\\' && cp[1] != '\0') 333931337658SMarcel Moolenaar cp += 1; 334031337658SMarcel Moolenaar continue; 334131337658SMarcel Moolenaar 334231337658SMarcel Moolenaar } if (cp + 1 < ep && cp[1] == '%') { 334331337658SMarcel Moolenaar cp += 1; 334431337658SMarcel Moolenaar goto add_one; 334531337658SMarcel Moolenaar } 334631337658SMarcel Moolenaar 334731337658SMarcel Moolenaar if (xp) { 334831337658SMarcel Moolenaar if (make_output) { 334931337658SMarcel Moolenaar cols = xo_format_string_direct(xop, xbp, flags | XFF_UNESCAPE, 335031337658SMarcel Moolenaar NULL, xp, cp - xp, -1, 335131337658SMarcel Moolenaar need_enc, XF_ENC_UTF8); 3352d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_COLUMNS)) 335331337658SMarcel Moolenaar xop->xo_columns += cols; 3354d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_ANCHOR)) 335531337658SMarcel Moolenaar xop->xo_anchor_columns += cols; 335631337658SMarcel Moolenaar } 335731337658SMarcel Moolenaar 335831337658SMarcel Moolenaar xp = NULL; 335931337658SMarcel Moolenaar } 336031337658SMarcel Moolenaar 336131337658SMarcel Moolenaar bzero(&xf, sizeof(xf)); 336231337658SMarcel Moolenaar xf.xf_leading_zero = -1; 336331337658SMarcel Moolenaar xf.xf_width[0] = xf.xf_width[1] = xf.xf_width[2] = -1; 336431337658SMarcel Moolenaar 336531337658SMarcel Moolenaar /* 336631337658SMarcel Moolenaar * "%@" starts an XO-specific set of flags: 336731337658SMarcel Moolenaar * @X@ - XML-only field; ignored if style isn't XML 336831337658SMarcel Moolenaar */ 336931337658SMarcel Moolenaar if (cp[1] == '@') { 337031337658SMarcel Moolenaar for (cp += 2; cp < ep; cp++) { 337131337658SMarcel Moolenaar if (*cp == '@') { 337231337658SMarcel Moolenaar break; 337331337658SMarcel Moolenaar } 337431337658SMarcel Moolenaar if (*cp == '*') { 337531337658SMarcel Moolenaar /* 337631337658SMarcel Moolenaar * '*' means there's a "%*.*s" value in vap that 337731337658SMarcel Moolenaar * we want to ignore 337831337658SMarcel Moolenaar */ 3379d1a0d267SMarcel Moolenaar if (!XOF_ISSET(xop, XOF_NO_VA_ARG)) 338031337658SMarcel Moolenaar va_arg(xop->xo_vap, int); 338131337658SMarcel Moolenaar } 338231337658SMarcel Moolenaar } 338331337658SMarcel Moolenaar } 338431337658SMarcel Moolenaar 338531337658SMarcel Moolenaar /* Hidden fields are only visible to JSON and XML */ 3386d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XFF_ENCODE_ONLY)) { 338731337658SMarcel Moolenaar if (style != XO_STYLE_XML 3388d1a0d267SMarcel Moolenaar && !xo_style_is_encoding(xop)) 338931337658SMarcel Moolenaar xf.xf_skip = 1; 3390d1a0d267SMarcel Moolenaar } else if (XOF_ISSET(xop, XFF_DISPLAY_ONLY)) { 339131337658SMarcel Moolenaar if (style != XO_STYLE_TEXT 3392788ca347SMarcel Moolenaar && xo_style(xop) != XO_STYLE_HTML) 339331337658SMarcel Moolenaar xf.xf_skip = 1; 339431337658SMarcel Moolenaar } 339531337658SMarcel Moolenaar 339631337658SMarcel Moolenaar if (!make_output) 339731337658SMarcel Moolenaar xf.xf_skip = 1; 339831337658SMarcel Moolenaar 339931337658SMarcel Moolenaar /* 340031337658SMarcel Moolenaar * Looking at one piece of a format; find the end and 340131337658SMarcel Moolenaar * call snprintf. Then advance xo_vap on our own. 340231337658SMarcel Moolenaar * 340331337658SMarcel Moolenaar * Note that 'n', 'v', and '$' are not supported. 340431337658SMarcel Moolenaar */ 340531337658SMarcel Moolenaar sp = cp; /* Save start pointer */ 340631337658SMarcel Moolenaar for (cp += 1; cp < ep; cp++) { 340731337658SMarcel Moolenaar if (*cp == 'l') 340831337658SMarcel Moolenaar xf.xf_lflag += 1; 340931337658SMarcel Moolenaar else if (*cp == 'h') 341031337658SMarcel Moolenaar xf.xf_hflag += 1; 341131337658SMarcel Moolenaar else if (*cp == 'j') 341231337658SMarcel Moolenaar xf.xf_jflag += 1; 341331337658SMarcel Moolenaar else if (*cp == 't') 341431337658SMarcel Moolenaar xf.xf_tflag += 1; 341531337658SMarcel Moolenaar else if (*cp == 'z') 341631337658SMarcel Moolenaar xf.xf_zflag += 1; 341731337658SMarcel Moolenaar else if (*cp == 'q') 341831337658SMarcel Moolenaar xf.xf_qflag += 1; 341931337658SMarcel Moolenaar else if (*cp == '.') { 342031337658SMarcel Moolenaar if (++xf.xf_dots >= XF_WIDTH_NUM) { 342131337658SMarcel Moolenaar xo_failure(xop, "Too many dots in format: '%s'", fmt); 342231337658SMarcel Moolenaar return -1; 342331337658SMarcel Moolenaar } 342431337658SMarcel Moolenaar } else if (*cp == '-') 342531337658SMarcel Moolenaar xf.xf_seen_minus = 1; 342631337658SMarcel Moolenaar else if (isdigit((int) *cp)) { 342731337658SMarcel Moolenaar if (xf.xf_leading_zero < 0) 342831337658SMarcel Moolenaar xf.xf_leading_zero = (*cp == '0'); 342931337658SMarcel Moolenaar xo_bump_width(&xf, *cp - '0'); 343031337658SMarcel Moolenaar } else if (*cp == '*') { 343131337658SMarcel Moolenaar xf.xf_stars += 1; 343231337658SMarcel Moolenaar xf.xf_star[xf.xf_dots] = 1; 3433d1a0d267SMarcel Moolenaar } else if (strchr("diouxXDOUeEfFgGaAcCsSpm", *cp) != NULL) 343431337658SMarcel Moolenaar break; 343531337658SMarcel Moolenaar else if (*cp == 'n' || *cp == 'v') { 343631337658SMarcel Moolenaar xo_failure(xop, "unsupported format: '%s'", fmt); 343731337658SMarcel Moolenaar return -1; 343831337658SMarcel Moolenaar } 343931337658SMarcel Moolenaar } 344031337658SMarcel Moolenaar 344131337658SMarcel Moolenaar if (cp == ep) 344231337658SMarcel Moolenaar xo_failure(xop, "field format missing format character: %s", 344331337658SMarcel Moolenaar fmt); 344431337658SMarcel Moolenaar 344531337658SMarcel Moolenaar xf.xf_fc = *cp; 344631337658SMarcel Moolenaar 3447d1a0d267SMarcel Moolenaar if (!XOF_ISSET(xop, XOF_NO_VA_ARG)) { 344831337658SMarcel Moolenaar if (*cp == 's' || *cp == 'S') { 344931337658SMarcel Moolenaar /* Handle "%*.*.*s" */ 345031337658SMarcel Moolenaar int s; 345131337658SMarcel Moolenaar for (s = 0; s < XF_WIDTH_NUM; s++) { 345231337658SMarcel Moolenaar if (xf.xf_star[s]) { 345331337658SMarcel Moolenaar xf.xf_width[s] = va_arg(xop->xo_vap, int); 345431337658SMarcel Moolenaar 345531337658SMarcel Moolenaar /* Normalize a negative width value */ 345631337658SMarcel Moolenaar if (xf.xf_width[s] < 0) { 345731337658SMarcel Moolenaar if (s == 0) { 345831337658SMarcel Moolenaar xf.xf_width[0] = -xf.xf_width[0]; 345931337658SMarcel Moolenaar xf.xf_seen_minus = 1; 346031337658SMarcel Moolenaar } else 346131337658SMarcel Moolenaar xf.xf_width[s] = -1; /* Ignore negative values */ 346231337658SMarcel Moolenaar } 346331337658SMarcel Moolenaar } 346431337658SMarcel Moolenaar } 346531337658SMarcel Moolenaar } 346631337658SMarcel Moolenaar } 346731337658SMarcel Moolenaar 346831337658SMarcel Moolenaar /* If no max is given, it defaults to size */ 346931337658SMarcel Moolenaar if (xf.xf_width[XF_WIDTH_MAX] < 0 && xf.xf_width[XF_WIDTH_SIZE] >= 0) 347031337658SMarcel Moolenaar xf.xf_width[XF_WIDTH_MAX] = xf.xf_width[XF_WIDTH_SIZE]; 347131337658SMarcel Moolenaar 347231337658SMarcel Moolenaar if (xf.xf_fc == 'D' || xf.xf_fc == 'O' || xf.xf_fc == 'U') 347331337658SMarcel Moolenaar xf.xf_lflag = 1; 347431337658SMarcel Moolenaar 347531337658SMarcel Moolenaar if (!xf.xf_skip) { 347631337658SMarcel Moolenaar xo_buffer_t *fbp = &xop->xo_fmt; 34778a6eceffSPhil Shafer ssize_t len = cp - sp + 1; 347831337658SMarcel Moolenaar if (!xo_buf_has_room(fbp, len + 1)) 347931337658SMarcel Moolenaar return -1; 348031337658SMarcel Moolenaar 348131337658SMarcel Moolenaar char *newfmt = fbp->xb_curp; 348231337658SMarcel Moolenaar memcpy(newfmt, sp, len); 348331337658SMarcel Moolenaar newfmt[0] = '%'; /* If we skipped over a "%@...@s" format */ 348431337658SMarcel Moolenaar newfmt[len] = '\0'; 348531337658SMarcel Moolenaar 348631337658SMarcel Moolenaar /* 348731337658SMarcel Moolenaar * Bad news: our strings are UTF-8, but the stock printf 348831337658SMarcel Moolenaar * functions won't handle field widths for wide characters 348931337658SMarcel Moolenaar * correctly. So we have to handle this ourselves. 349031337658SMarcel Moolenaar */ 349131337658SMarcel Moolenaar if (xop->xo_formatter == NULL 3492d1a0d267SMarcel Moolenaar && (xf.xf_fc == 's' || xf.xf_fc == 'S' 3493d1a0d267SMarcel Moolenaar || xf.xf_fc == 'm')) { 3494d1a0d267SMarcel Moolenaar 3495d1a0d267SMarcel Moolenaar xf.xf_enc = (xf.xf_fc == 'm') ? XF_ENC_UTF8 3496d1a0d267SMarcel Moolenaar : (xf.xf_lflag || (xf.xf_fc == 'S')) ? XF_ENC_WIDE 3497d1a0d267SMarcel Moolenaar : xf.xf_hflag ? XF_ENC_LOCALE : XF_ENC_UTF8; 3498d1a0d267SMarcel Moolenaar 349931337658SMarcel Moolenaar rc = xo_format_string(xop, xbp, flags, &xf); 350031337658SMarcel Moolenaar 3501d1a0d267SMarcel Moolenaar if ((flags & XFF_TRIM_WS) && xo_style_is_encoding(xop)) 350231337658SMarcel Moolenaar rc = xo_trim_ws(xbp, rc); 350331337658SMarcel Moolenaar 350431337658SMarcel Moolenaar } else { 3505f2b7bf8aSPhil Shafer ssize_t columns = rc = xo_vsnprintf(xop, xbp, newfmt, 3506f2b7bf8aSPhil Shafer xop->xo_vap); 350731337658SMarcel Moolenaar 350831337658SMarcel Moolenaar /* 350931337658SMarcel Moolenaar * For XML and HTML, we need "&<>" processing; for JSON, 351031337658SMarcel Moolenaar * it's quotes. Text gets nothing. 351131337658SMarcel Moolenaar */ 351231337658SMarcel Moolenaar switch (style) { 351331337658SMarcel Moolenaar case XO_STYLE_XML: 351431337658SMarcel Moolenaar if (flags & XFF_TRIM_WS) 351531337658SMarcel Moolenaar columns = rc = xo_trim_ws(xbp, rc); 3516ee5cf116SPhil Shafer /* FALLTHRU */ 351731337658SMarcel Moolenaar case XO_STYLE_HTML: 351831337658SMarcel Moolenaar rc = xo_escape_xml(xbp, rc, (flags & XFF_ATTR)); 351931337658SMarcel Moolenaar break; 352031337658SMarcel Moolenaar 352131337658SMarcel Moolenaar case XO_STYLE_JSON: 352231337658SMarcel Moolenaar if (flags & XFF_TRIM_WS) 352331337658SMarcel Moolenaar columns = rc = xo_trim_ws(xbp, rc); 3524d1a0d267SMarcel Moolenaar rc = xo_escape_json(xbp, rc, 0); 3525d1a0d267SMarcel Moolenaar break; 3526d1a0d267SMarcel Moolenaar 3527d1a0d267SMarcel Moolenaar case XO_STYLE_SDPARAMS: 3528d1a0d267SMarcel Moolenaar if (flags & XFF_TRIM_WS) 3529d1a0d267SMarcel Moolenaar columns = rc = xo_trim_ws(xbp, rc); 3530d1a0d267SMarcel Moolenaar rc = xo_escape_sdparams(xbp, rc, 0); 3531d1a0d267SMarcel Moolenaar break; 3532d1a0d267SMarcel Moolenaar 3533d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER: 3534d1a0d267SMarcel Moolenaar if (flags & XFF_TRIM_WS) 3535d1a0d267SMarcel Moolenaar columns = rc = xo_trim_ws(xbp, rc); 353631337658SMarcel Moolenaar break; 353731337658SMarcel Moolenaar } 353831337658SMarcel Moolenaar 353931337658SMarcel Moolenaar /* 3540d1a0d267SMarcel Moolenaar * We can assume all the non-%s data we've 3541d1a0d267SMarcel Moolenaar * added is ASCII, so the columns and bytes are the 3542d1a0d267SMarcel Moolenaar * same. xo_format_string handles all the fancy 3543d1a0d267SMarcel Moolenaar * string conversions and updates xo_anchor_columns 3544d1a0d267SMarcel Moolenaar * accordingly. 354531337658SMarcel Moolenaar */ 3546d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_COLUMNS)) 354731337658SMarcel Moolenaar xop->xo_columns += columns; 3548d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_ANCHOR)) 354931337658SMarcel Moolenaar xop->xo_anchor_columns += columns; 355031337658SMarcel Moolenaar } 355131337658SMarcel Moolenaar 355231337658SMarcel Moolenaar xbp->xb_curp += rc; 355331337658SMarcel Moolenaar } 355431337658SMarcel Moolenaar 355531337658SMarcel Moolenaar /* 355631337658SMarcel Moolenaar * Now for the tricky part: we need to move the argument pointer 355731337658SMarcel Moolenaar * along by the amount needed. 355831337658SMarcel Moolenaar */ 3559d1a0d267SMarcel Moolenaar if (!XOF_ISSET(xop, XOF_NO_VA_ARG)) { 356031337658SMarcel Moolenaar 356131337658SMarcel Moolenaar if (xf.xf_fc == 's' ||xf.xf_fc == 'S') { 356231337658SMarcel Moolenaar /* 356331337658SMarcel Moolenaar * The 'S' and 's' formats are normally handled in 356431337658SMarcel Moolenaar * xo_format_string, but if we skipped it, then we 356531337658SMarcel Moolenaar * need to pop it. 356631337658SMarcel Moolenaar */ 356731337658SMarcel Moolenaar if (xf.xf_skip) 356831337658SMarcel Moolenaar va_arg(xop->xo_vap, char *); 356931337658SMarcel Moolenaar 3570d1a0d267SMarcel Moolenaar } else if (xf.xf_fc == 'm') { 3571d1a0d267SMarcel Moolenaar /* Nothing on the stack for "%m" */ 3572d1a0d267SMarcel Moolenaar 357331337658SMarcel Moolenaar } else { 357431337658SMarcel Moolenaar int s; 357531337658SMarcel Moolenaar for (s = 0; s < XF_WIDTH_NUM; s++) { 357631337658SMarcel Moolenaar if (xf.xf_star[s]) 357731337658SMarcel Moolenaar va_arg(xop->xo_vap, int); 357831337658SMarcel Moolenaar } 357931337658SMarcel Moolenaar 358031337658SMarcel Moolenaar if (strchr("diouxXDOU", xf.xf_fc) != NULL) { 358131337658SMarcel Moolenaar if (xf.xf_hflag > 1) { 358231337658SMarcel Moolenaar va_arg(xop->xo_vap, int); 358331337658SMarcel Moolenaar 358431337658SMarcel Moolenaar } else if (xf.xf_hflag > 0) { 358531337658SMarcel Moolenaar va_arg(xop->xo_vap, int); 358631337658SMarcel Moolenaar 358731337658SMarcel Moolenaar } else if (xf.xf_lflag > 1) { 358831337658SMarcel Moolenaar va_arg(xop->xo_vap, unsigned long long); 358931337658SMarcel Moolenaar 359031337658SMarcel Moolenaar } else if (xf.xf_lflag > 0) { 359131337658SMarcel Moolenaar va_arg(xop->xo_vap, unsigned long); 359231337658SMarcel Moolenaar 359331337658SMarcel Moolenaar } else if (xf.xf_jflag > 0) { 359431337658SMarcel Moolenaar va_arg(xop->xo_vap, intmax_t); 359531337658SMarcel Moolenaar 359631337658SMarcel Moolenaar } else if (xf.xf_tflag > 0) { 359731337658SMarcel Moolenaar va_arg(xop->xo_vap, ptrdiff_t); 359831337658SMarcel Moolenaar 359931337658SMarcel Moolenaar } else if (xf.xf_zflag > 0) { 360031337658SMarcel Moolenaar va_arg(xop->xo_vap, size_t); 360131337658SMarcel Moolenaar 360231337658SMarcel Moolenaar } else if (xf.xf_qflag > 0) { 360331337658SMarcel Moolenaar va_arg(xop->xo_vap, quad_t); 360431337658SMarcel Moolenaar 360531337658SMarcel Moolenaar } else { 360631337658SMarcel Moolenaar va_arg(xop->xo_vap, int); 360731337658SMarcel Moolenaar } 360831337658SMarcel Moolenaar } else if (strchr("eEfFgGaA", xf.xf_fc) != NULL) 360931337658SMarcel Moolenaar if (xf.xf_lflag) 361031337658SMarcel Moolenaar va_arg(xop->xo_vap, long double); 361131337658SMarcel Moolenaar else 361231337658SMarcel Moolenaar va_arg(xop->xo_vap, double); 361331337658SMarcel Moolenaar 361431337658SMarcel Moolenaar else if (xf.xf_fc == 'C' || (xf.xf_fc == 'c' && xf.xf_lflag)) 361531337658SMarcel Moolenaar va_arg(xop->xo_vap, wint_t); 361631337658SMarcel Moolenaar 361731337658SMarcel Moolenaar else if (xf.xf_fc == 'c') 361831337658SMarcel Moolenaar va_arg(xop->xo_vap, int); 361931337658SMarcel Moolenaar 362031337658SMarcel Moolenaar else if (xf.xf_fc == 'p') 362131337658SMarcel Moolenaar va_arg(xop->xo_vap, void *); 362231337658SMarcel Moolenaar } 362331337658SMarcel Moolenaar } 362431337658SMarcel Moolenaar } 362531337658SMarcel Moolenaar 362631337658SMarcel Moolenaar if (xp) { 362731337658SMarcel Moolenaar if (make_output) { 362831337658SMarcel Moolenaar cols = xo_format_string_direct(xop, xbp, flags | XFF_UNESCAPE, 362931337658SMarcel Moolenaar NULL, xp, cp - xp, -1, 363031337658SMarcel Moolenaar need_enc, XF_ENC_UTF8); 3631d1a0d267SMarcel Moolenaar 3632d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_COLUMNS)) 363331337658SMarcel Moolenaar xop->xo_columns += cols; 3634d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_ANCHOR)) 363531337658SMarcel Moolenaar xop->xo_anchor_columns += cols; 363631337658SMarcel Moolenaar } 363731337658SMarcel Moolenaar 363831337658SMarcel Moolenaar xp = NULL; 363931337658SMarcel Moolenaar } 364031337658SMarcel Moolenaar 3641d1a0d267SMarcel Moolenaar if (flags & XFF_GT_FLAGS) { 3642d1a0d267SMarcel Moolenaar /* 3643d1a0d267SMarcel Moolenaar * Handle gettext()ing the field by looking up the value 3644d1a0d267SMarcel Moolenaar * and then copying it in, while converting to locale, if 3645d1a0d267SMarcel Moolenaar * needed. 3646d1a0d267SMarcel Moolenaar */ 36478a6eceffSPhil Shafer ssize_t new_cols = xo_format_gettext(xop, flags, start_offset, 3648d1a0d267SMarcel Moolenaar old_cols, real_need_enc); 3649d1a0d267SMarcel Moolenaar 3650d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_COLUMNS)) 3651d1a0d267SMarcel Moolenaar xop->xo_columns += new_cols - old_cols; 3652d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_ANCHOR)) 3653d1a0d267SMarcel Moolenaar xop->xo_anchor_columns += new_cols - old_cols; 3654d1a0d267SMarcel Moolenaar } 3655d1a0d267SMarcel Moolenaar 365631337658SMarcel Moolenaar return 0; 365731337658SMarcel Moolenaar } 365831337658SMarcel Moolenaar 3659264104f2SPhil Shafer /* 3660264104f2SPhil Shafer * Remove any numeric precision/width format from the format string by 3661264104f2SPhil Shafer * inserting the "%" after the [0-9]+, returning the substring. 3662264104f2SPhil Shafer */ 366331337658SMarcel Moolenaar static char * 366431337658SMarcel Moolenaar xo_fix_encoding (xo_handle_t *xop UNUSED, char *encoding) 366531337658SMarcel Moolenaar { 366631337658SMarcel Moolenaar char *cp = encoding; 366731337658SMarcel Moolenaar 366831337658SMarcel Moolenaar if (cp[0] != '%' || !isdigit((int) cp[1])) 366931337658SMarcel Moolenaar return encoding; 367031337658SMarcel Moolenaar 367131337658SMarcel Moolenaar for (cp += 2; *cp; cp++) { 367231337658SMarcel Moolenaar if (!isdigit((int) *cp)) 367331337658SMarcel Moolenaar break; 367431337658SMarcel Moolenaar } 367531337658SMarcel Moolenaar 3676264104f2SPhil Shafer *--cp = '%'; /* Back off and insert the '%' */ 367731337658SMarcel Moolenaar 367831337658SMarcel Moolenaar return cp; 367931337658SMarcel Moolenaar } 368031337658SMarcel Moolenaar 368131337658SMarcel Moolenaar static void 3682788ca347SMarcel Moolenaar xo_color_append_html (xo_handle_t *xop) 3683788ca347SMarcel Moolenaar { 3684788ca347SMarcel Moolenaar /* 3685788ca347SMarcel Moolenaar * If the color buffer has content, we add it now. It's already 3686788ca347SMarcel Moolenaar * prebuilt and ready, since we want to add it to every <div>. 3687788ca347SMarcel Moolenaar */ 3688788ca347SMarcel Moolenaar if (!xo_buf_is_empty(&xop->xo_color_buf)) { 3689788ca347SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_color_buf; 3690788ca347SMarcel Moolenaar 3691788ca347SMarcel Moolenaar xo_data_append(xop, xbp->xb_bufp, xbp->xb_curp - xbp->xb_bufp); 3692788ca347SMarcel Moolenaar } 3693788ca347SMarcel Moolenaar } 3694788ca347SMarcel Moolenaar 3695d1a0d267SMarcel Moolenaar /* 3696d1a0d267SMarcel Moolenaar * A wrapper for humanize_number that autoscales, since the 3697d1a0d267SMarcel Moolenaar * HN_AUTOSCALE flag scales as needed based on the size of 3698d1a0d267SMarcel Moolenaar * the output buffer, not the size of the value. I also 3699d1a0d267SMarcel Moolenaar * wish HN_DECIMAL was more imperative, without the <10 3700d1a0d267SMarcel Moolenaar * test. But the boat only goes where we want when we hold 3701d1a0d267SMarcel Moolenaar * the rudder, so xo_humanize fixes part of the problem. 3702d1a0d267SMarcel Moolenaar */ 37038a6eceffSPhil Shafer static ssize_t 37048a6eceffSPhil Shafer xo_humanize (char *buf, ssize_t len, uint64_t value, int flags) 3705d1a0d267SMarcel Moolenaar { 3706d1a0d267SMarcel Moolenaar int scale = 0; 3707d1a0d267SMarcel Moolenaar 3708d1a0d267SMarcel Moolenaar if (value) { 3709d1a0d267SMarcel Moolenaar uint64_t left = value; 3710d1a0d267SMarcel Moolenaar 3711d1a0d267SMarcel Moolenaar if (flags & HN_DIVISOR_1000) { 3712d1a0d267SMarcel Moolenaar for ( ; left; scale++) 3713d1a0d267SMarcel Moolenaar left /= 1000; 3714d1a0d267SMarcel Moolenaar } else { 3715d1a0d267SMarcel Moolenaar for ( ; left; scale++) 3716d1a0d267SMarcel Moolenaar left /= 1024; 3717d1a0d267SMarcel Moolenaar } 3718d1a0d267SMarcel Moolenaar scale -= 1; 3719d1a0d267SMarcel Moolenaar } 3720d1a0d267SMarcel Moolenaar 3721d1a0d267SMarcel Moolenaar return xo_humanize_number(buf, len, value, "", scale, flags); 3722d1a0d267SMarcel Moolenaar } 3723d1a0d267SMarcel Moolenaar 3724d1a0d267SMarcel Moolenaar /* 3725d1a0d267SMarcel Moolenaar * This is an area where we can save information from the handle for 3726d1a0d267SMarcel Moolenaar * later restoration. We need to know what data was rendered to know 3727d1a0d267SMarcel Moolenaar * what needs cleaned up. 3728d1a0d267SMarcel Moolenaar */ 3729d1a0d267SMarcel Moolenaar typedef struct xo_humanize_save_s { 37308a6eceffSPhil Shafer ssize_t xhs_offset; /* Saved xo_offset */ 37318a6eceffSPhil Shafer ssize_t xhs_columns; /* Saved xo_columns */ 37328a6eceffSPhil Shafer ssize_t xhs_anchor_columns; /* Saved xo_anchor_columns */ 3733d1a0d267SMarcel Moolenaar } xo_humanize_save_t; 3734d1a0d267SMarcel Moolenaar 3735d1a0d267SMarcel Moolenaar /* 3736d1a0d267SMarcel Moolenaar * Format a "humanized" value for a numeric, meaning something nice 3737d1a0d267SMarcel Moolenaar * like "44M" instead of "44470272". We autoscale, choosing the 3738d1a0d267SMarcel Moolenaar * most appropriate value for K/M/G/T/P/E based on the value given. 3739d1a0d267SMarcel Moolenaar */ 3740d1a0d267SMarcel Moolenaar static void 3741d1a0d267SMarcel Moolenaar xo_format_humanize (xo_handle_t *xop, xo_buffer_t *xbp, 3742d1a0d267SMarcel Moolenaar xo_humanize_save_t *savep, xo_xff_flags_t flags) 3743d1a0d267SMarcel Moolenaar { 3744d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_NO_HUMANIZE)) 3745d1a0d267SMarcel Moolenaar return; 3746d1a0d267SMarcel Moolenaar 37478a6eceffSPhil Shafer ssize_t end_offset = xbp->xb_curp - xbp->xb_bufp; 3748d1a0d267SMarcel Moolenaar if (end_offset == savep->xhs_offset) /* Huh? Nothing to render */ 3749d1a0d267SMarcel Moolenaar return; 3750d1a0d267SMarcel Moolenaar 3751d1a0d267SMarcel Moolenaar /* 3752d1a0d267SMarcel Moolenaar * We have a string that's allegedly a number. We want to 3753d1a0d267SMarcel Moolenaar * humanize it, which means turning it back into a number 3754d1a0d267SMarcel Moolenaar * and calling xo_humanize_number on it. 3755d1a0d267SMarcel Moolenaar */ 3756d1a0d267SMarcel Moolenaar uint64_t value; 3757d1a0d267SMarcel Moolenaar char *ep; 3758d1a0d267SMarcel Moolenaar 3759d1a0d267SMarcel Moolenaar xo_buf_append(xbp, "", 1); /* NUL-terminate it */ 3760d1a0d267SMarcel Moolenaar 3761d1a0d267SMarcel Moolenaar value = strtoull(xbp->xb_bufp + savep->xhs_offset, &ep, 0); 3762d1a0d267SMarcel Moolenaar if (!(value == ULLONG_MAX && errno == ERANGE) 3763d1a0d267SMarcel Moolenaar && (ep != xbp->xb_bufp + savep->xhs_offset)) { 3764d1a0d267SMarcel Moolenaar /* 3765d1a0d267SMarcel Moolenaar * There are few values where humanize_number needs 3766d1a0d267SMarcel Moolenaar * more bytes than the original value. I've used 3767d1a0d267SMarcel Moolenaar * 10 as a rectal number to cover those scenarios. 3768d1a0d267SMarcel Moolenaar */ 3769d1a0d267SMarcel Moolenaar if (xo_buf_has_room(xbp, 10)) { 3770d1a0d267SMarcel Moolenaar xbp->xb_curp = xbp->xb_bufp + savep->xhs_offset; 3771d1a0d267SMarcel Moolenaar 37728a6eceffSPhil Shafer ssize_t rc; 37738a6eceffSPhil Shafer ssize_t left = (xbp->xb_bufp + xbp->xb_size) - xbp->xb_curp; 3774d1a0d267SMarcel Moolenaar int hn_flags = HN_NOSPACE; /* On by default */ 3775d1a0d267SMarcel Moolenaar 3776d1a0d267SMarcel Moolenaar if (flags & XFF_HN_SPACE) 3777d1a0d267SMarcel Moolenaar hn_flags &= ~HN_NOSPACE; 3778d1a0d267SMarcel Moolenaar 3779d1a0d267SMarcel Moolenaar if (flags & XFF_HN_DECIMAL) 3780d1a0d267SMarcel Moolenaar hn_flags |= HN_DECIMAL; 3781d1a0d267SMarcel Moolenaar 3782d1a0d267SMarcel Moolenaar if (flags & XFF_HN_1000) 3783d1a0d267SMarcel Moolenaar hn_flags |= HN_DIVISOR_1000; 3784d1a0d267SMarcel Moolenaar 37858a6eceffSPhil Shafer rc = xo_humanize(xbp->xb_curp, left, value, hn_flags); 3786d1a0d267SMarcel Moolenaar if (rc > 0) { 3787d1a0d267SMarcel Moolenaar xbp->xb_curp += rc; 3788d1a0d267SMarcel Moolenaar xop->xo_columns = savep->xhs_columns + rc; 3789d1a0d267SMarcel Moolenaar xop->xo_anchor_columns = savep->xhs_anchor_columns + rc; 3790d1a0d267SMarcel Moolenaar } 3791d1a0d267SMarcel Moolenaar } 3792d1a0d267SMarcel Moolenaar } 3793d1a0d267SMarcel Moolenaar } 3794d1a0d267SMarcel Moolenaar 3795264104f2SPhil Shafer /* 3796264104f2SPhil Shafer * Convenience function that either append a fixed value (if one is 3797264104f2SPhil Shafer * given) or formats a field using a format string. If it's 3798264104f2SPhil Shafer * encode_only, then we can't skip formatting the field, since it may 3799264104f2SPhil Shafer * be pulling arguments off the stack. 3800264104f2SPhil Shafer */ 3801264104f2SPhil Shafer static inline void 3802264104f2SPhil Shafer xo_simple_field (xo_handle_t *xop, unsigned encode_only, 3803264104f2SPhil Shafer const char *value, ssize_t vlen, 3804264104f2SPhil Shafer const char *fmt, ssize_t flen, xo_xff_flags_t flags) 3805264104f2SPhil Shafer { 3806264104f2SPhil Shafer if (encode_only) 3807264104f2SPhil Shafer flags |= XFF_NO_OUTPUT; 3808264104f2SPhil Shafer 3809264104f2SPhil Shafer if (vlen == 0) 3810264104f2SPhil Shafer xo_do_format_field(xop, NULL, fmt, flen, flags); 3811264104f2SPhil Shafer else if (!encode_only) 3812264104f2SPhil Shafer xo_data_append_content(xop, value, vlen, flags); 3813264104f2SPhil Shafer } 3814264104f2SPhil Shafer 3815264104f2SPhil Shafer /* 3816264104f2SPhil Shafer * Html mode: append a <div> to the output buffer contain a field 3817264104f2SPhil Shafer * along with all the supporting information indicated by the flags. 3818264104f2SPhil Shafer */ 3819788ca347SMarcel Moolenaar static void 382031337658SMarcel Moolenaar xo_buf_append_div (xo_handle_t *xop, const char *class, xo_xff_flags_t flags, 38218a6eceffSPhil Shafer const char *name, ssize_t nlen, 38228a6eceffSPhil Shafer const char *value, ssize_t vlen, 3823264104f2SPhil Shafer const char *fmt, ssize_t flen, 38248a6eceffSPhil Shafer const char *encoding, ssize_t elen) 382531337658SMarcel Moolenaar { 382631337658SMarcel Moolenaar static char div_start[] = "<div class=\""; 382731337658SMarcel Moolenaar static char div_tag[] = "\" data-tag=\""; 382831337658SMarcel Moolenaar static char div_xpath[] = "\" data-xpath=\""; 382931337658SMarcel Moolenaar static char div_key[] = "\" data-key=\"key"; 383031337658SMarcel Moolenaar static char div_end[] = "\">"; 383131337658SMarcel Moolenaar static char div_close[] = "</div>"; 383231337658SMarcel Moolenaar 3833a321cc5dSPhil Shafer /* The encoding format defaults to the normal format */ 3834264104f2SPhil Shafer if (encoding == NULL && fmt != NULL) { 3835264104f2SPhil Shafer char *enc = alloca(flen + 1); 3836264104f2SPhil Shafer memcpy(enc, fmt, flen); 3837264104f2SPhil Shafer enc[flen] = '\0'; 3838a321cc5dSPhil Shafer encoding = xo_fix_encoding(xop, enc); 3839a321cc5dSPhil Shafer elen = strlen(encoding); 3840a321cc5dSPhil Shafer } 3841a321cc5dSPhil Shafer 384231337658SMarcel Moolenaar /* 384331337658SMarcel Moolenaar * To build our XPath predicate, we need to save the va_list before 384431337658SMarcel Moolenaar * we format our data, and then restore it before we format the 384531337658SMarcel Moolenaar * xpath expression. 384631337658SMarcel Moolenaar * Display-only keys implies that we've got an encode-only key 384731337658SMarcel Moolenaar * elsewhere, so we don't use them from making predicates. 384831337658SMarcel Moolenaar */ 384931337658SMarcel Moolenaar int need_predidate = 385031337658SMarcel Moolenaar (name && (flags & XFF_KEY) && !(flags & XFF_DISPLAY_ONLY) 38518a6eceffSPhil Shafer && XOF_ISSET(xop, XOF_XPATH)) ? 1 : 0; 385231337658SMarcel Moolenaar 385331337658SMarcel Moolenaar if (need_predidate) { 385431337658SMarcel Moolenaar va_list va_local; 385531337658SMarcel Moolenaar 385631337658SMarcel Moolenaar va_copy(va_local, xop->xo_vap); 385731337658SMarcel Moolenaar if (xop->xo_checkpointer) 385831337658SMarcel Moolenaar xop->xo_checkpointer(xop, xop->xo_vap, 0); 385931337658SMarcel Moolenaar 386031337658SMarcel Moolenaar /* 386131337658SMarcel Moolenaar * Build an XPath predicate expression to match this key. 386231337658SMarcel Moolenaar * We use the format buffer. 386331337658SMarcel Moolenaar */ 386431337658SMarcel Moolenaar xo_buffer_t *pbp = &xop->xo_predicate; 386531337658SMarcel Moolenaar pbp->xb_curp = pbp->xb_bufp; /* Restart buffer */ 386631337658SMarcel Moolenaar 386731337658SMarcel Moolenaar xo_buf_append(pbp, "[", 1); 386831337658SMarcel Moolenaar xo_buf_escape(xop, pbp, name, nlen, 0); 3869d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_PRETTY)) 387031337658SMarcel Moolenaar xo_buf_append(pbp, " = '", 4); 387131337658SMarcel Moolenaar else 387231337658SMarcel Moolenaar xo_buf_append(pbp, "='", 2); 387331337658SMarcel Moolenaar 3874d1a0d267SMarcel Moolenaar xo_xff_flags_t pflags = flags | XFF_XML | XFF_ATTR; 3875d1a0d267SMarcel Moolenaar pflags &= ~(XFF_NO_OUTPUT | XFF_ENCODE_ONLY); 3876d1a0d267SMarcel Moolenaar xo_do_format_field(xop, pbp, encoding, elen, pflags); 387731337658SMarcel Moolenaar 387831337658SMarcel Moolenaar xo_buf_append(pbp, "']", 2); 387931337658SMarcel Moolenaar 388031337658SMarcel Moolenaar /* Now we record this predicate expression in the stack */ 388131337658SMarcel Moolenaar xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth]; 38828a6eceffSPhil Shafer ssize_t olen = xsp->xs_keys ? strlen(xsp->xs_keys) : 0; 38838a6eceffSPhil Shafer ssize_t dlen = pbp->xb_curp - pbp->xb_bufp; 388431337658SMarcel Moolenaar 388531337658SMarcel Moolenaar char *cp = xo_realloc(xsp->xs_keys, olen + dlen + 1); 388631337658SMarcel Moolenaar if (cp) { 388731337658SMarcel Moolenaar memcpy(cp + olen, pbp->xb_bufp, dlen); 388831337658SMarcel Moolenaar cp[olen + dlen] = '\0'; 388931337658SMarcel Moolenaar xsp->xs_keys = cp; 389031337658SMarcel Moolenaar } 389131337658SMarcel Moolenaar 389231337658SMarcel Moolenaar /* Now we reset the xo_vap as if we were never here */ 389331337658SMarcel Moolenaar va_end(xop->xo_vap); 389431337658SMarcel Moolenaar va_copy(xop->xo_vap, va_local); 389531337658SMarcel Moolenaar va_end(va_local); 389631337658SMarcel Moolenaar if (xop->xo_checkpointer) 389731337658SMarcel Moolenaar xop->xo_checkpointer(xop, xop->xo_vap, 1); 389831337658SMarcel Moolenaar } 389931337658SMarcel Moolenaar 390031337658SMarcel Moolenaar if (flags & XFF_ENCODE_ONLY) { 390131337658SMarcel Moolenaar /* 3902ee5cf116SPhil Shafer * Even if this is encode-only, we need to go through the 390331337658SMarcel Moolenaar * work of formatting it to make sure the args are cleared 3904264104f2SPhil Shafer * from xo_vap. This is not true when vlen is zero, since 3905264104f2SPhil Shafer * that means our "value" isn't on the stack. 390631337658SMarcel Moolenaar */ 3907264104f2SPhil Shafer xo_simple_field(xop, TRUE, NULL, 0, encoding, elen, flags); 390831337658SMarcel Moolenaar return; 390931337658SMarcel Moolenaar } 391031337658SMarcel Moolenaar 391131337658SMarcel Moolenaar xo_line_ensure_open(xop, 0); 391231337658SMarcel Moolenaar 3913d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_PRETTY)) 391431337658SMarcel Moolenaar xo_buf_indent(xop, xop->xo_indent_by); 391531337658SMarcel Moolenaar 391631337658SMarcel Moolenaar xo_data_append(xop, div_start, sizeof(div_start) - 1); 391731337658SMarcel Moolenaar xo_data_append(xop, class, strlen(class)); 391831337658SMarcel Moolenaar 3919788ca347SMarcel Moolenaar /* 3920788ca347SMarcel Moolenaar * If the color buffer has content, we add it now. It's already 3921788ca347SMarcel Moolenaar * prebuilt and ready, since we want to add it to every <div>. 3922788ca347SMarcel Moolenaar */ 3923788ca347SMarcel Moolenaar if (!xo_buf_is_empty(&xop->xo_color_buf)) { 3924788ca347SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_color_buf; 3925788ca347SMarcel Moolenaar 3926788ca347SMarcel Moolenaar xo_data_append(xop, xbp->xb_bufp, xbp->xb_curp - xbp->xb_bufp); 3927788ca347SMarcel Moolenaar } 3928788ca347SMarcel Moolenaar 392931337658SMarcel Moolenaar if (name) { 393031337658SMarcel Moolenaar xo_data_append(xop, div_tag, sizeof(div_tag) - 1); 393131337658SMarcel Moolenaar xo_data_escape(xop, name, nlen); 393231337658SMarcel Moolenaar 393331337658SMarcel Moolenaar /* 393431337658SMarcel Moolenaar * Save the offset at which we'd place units. See xo_format_units. 393531337658SMarcel Moolenaar */ 3936d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_UNITS)) { 3937d1a0d267SMarcel Moolenaar XOIF_SET(xop, XOIF_UNITS_PENDING); 393831337658SMarcel Moolenaar /* 393931337658SMarcel Moolenaar * Note: We need the '+1' here because we know we've not 394031337658SMarcel Moolenaar * added the closing quote. We add one, knowing the quote 394131337658SMarcel Moolenaar * will be added shortly. 394231337658SMarcel Moolenaar */ 394331337658SMarcel Moolenaar xop->xo_units_offset = 394431337658SMarcel Moolenaar xop->xo_data.xb_curp -xop->xo_data.xb_bufp + 1; 394531337658SMarcel Moolenaar } 394631337658SMarcel Moolenaar 3947d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_XPATH)) { 394831337658SMarcel Moolenaar int i; 394931337658SMarcel Moolenaar xo_stack_t *xsp; 395031337658SMarcel Moolenaar 395131337658SMarcel Moolenaar xo_data_append(xop, div_xpath, sizeof(div_xpath) - 1); 395231337658SMarcel Moolenaar if (xop->xo_leading_xpath) 395331337658SMarcel Moolenaar xo_data_append(xop, xop->xo_leading_xpath, 395431337658SMarcel Moolenaar strlen(xop->xo_leading_xpath)); 395531337658SMarcel Moolenaar 395631337658SMarcel Moolenaar for (i = 0; i <= xop->xo_depth; i++) { 395731337658SMarcel Moolenaar xsp = &xop->xo_stack[i]; 395831337658SMarcel Moolenaar if (xsp->xs_name == NULL) 395931337658SMarcel Moolenaar continue; 396031337658SMarcel Moolenaar 3961545ddfbeSMarcel Moolenaar /* 3962545ddfbeSMarcel Moolenaar * XSS_OPEN_LIST and XSS_OPEN_LEAF_LIST stack frames 3963545ddfbeSMarcel Moolenaar * are directly under XSS_OPEN_INSTANCE frames so we 3964545ddfbeSMarcel Moolenaar * don't need to put these in our XPath expressions. 3965545ddfbeSMarcel Moolenaar */ 3966545ddfbeSMarcel Moolenaar if (xsp->xs_state == XSS_OPEN_LIST 3967545ddfbeSMarcel Moolenaar || xsp->xs_state == XSS_OPEN_LEAF_LIST) 3968545ddfbeSMarcel Moolenaar continue; 3969545ddfbeSMarcel Moolenaar 397031337658SMarcel Moolenaar xo_data_append(xop, "/", 1); 397131337658SMarcel Moolenaar xo_data_escape(xop, xsp->xs_name, strlen(xsp->xs_name)); 397231337658SMarcel Moolenaar if (xsp->xs_keys) { 397331337658SMarcel Moolenaar /* Don't show keys for the key field */ 397431337658SMarcel Moolenaar if (i != xop->xo_depth || !(flags & XFF_KEY)) 397531337658SMarcel Moolenaar xo_data_append(xop, xsp->xs_keys, strlen(xsp->xs_keys)); 397631337658SMarcel Moolenaar } 397731337658SMarcel Moolenaar } 397831337658SMarcel Moolenaar 397931337658SMarcel Moolenaar xo_data_append(xop, "/", 1); 398031337658SMarcel Moolenaar xo_data_escape(xop, name, nlen); 398131337658SMarcel Moolenaar } 398231337658SMarcel Moolenaar 3983d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_INFO) && xop->xo_info) { 398431337658SMarcel Moolenaar static char in_type[] = "\" data-type=\""; 398531337658SMarcel Moolenaar static char in_help[] = "\" data-help=\""; 398631337658SMarcel Moolenaar 398731337658SMarcel Moolenaar xo_info_t *xip = xo_info_find(xop, name, nlen); 398831337658SMarcel Moolenaar if (xip) { 398931337658SMarcel Moolenaar if (xip->xi_type) { 399031337658SMarcel Moolenaar xo_data_append(xop, in_type, sizeof(in_type) - 1); 399131337658SMarcel Moolenaar xo_data_escape(xop, xip->xi_type, strlen(xip->xi_type)); 399231337658SMarcel Moolenaar } 399331337658SMarcel Moolenaar if (xip->xi_help) { 399431337658SMarcel Moolenaar xo_data_append(xop, in_help, sizeof(in_help) - 1); 399531337658SMarcel Moolenaar xo_data_escape(xop, xip->xi_help, strlen(xip->xi_help)); 399631337658SMarcel Moolenaar } 399731337658SMarcel Moolenaar } 399831337658SMarcel Moolenaar } 399931337658SMarcel Moolenaar 4000d1a0d267SMarcel Moolenaar if ((flags & XFF_KEY) && XOF_ISSET(xop, XOF_KEYS)) 400131337658SMarcel Moolenaar xo_data_append(xop, div_key, sizeof(div_key) - 1); 400231337658SMarcel Moolenaar } 400331337658SMarcel Moolenaar 4004d1a0d267SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_data; 40058a6eceffSPhil Shafer ssize_t base_offset = xbp->xb_curp - xbp->xb_bufp; 4006d1a0d267SMarcel Moolenaar 400731337658SMarcel Moolenaar xo_data_append(xop, div_end, sizeof(div_end) - 1); 400831337658SMarcel Moolenaar 4009d1a0d267SMarcel Moolenaar xo_humanize_save_t save; /* Save values for humanizing logic */ 4010d1a0d267SMarcel Moolenaar 4011d1a0d267SMarcel Moolenaar save.xhs_offset = xbp->xb_curp - xbp->xb_bufp; 4012d1a0d267SMarcel Moolenaar save.xhs_columns = xop->xo_columns; 4013d1a0d267SMarcel Moolenaar save.xhs_anchor_columns = xop->xo_anchor_columns; 4014d1a0d267SMarcel Moolenaar 4015264104f2SPhil Shafer xo_simple_field(xop, FALSE, value, vlen, fmt, flen, flags); 4016d1a0d267SMarcel Moolenaar 4017d1a0d267SMarcel Moolenaar if (flags & XFF_HUMANIZE) { 4018d1a0d267SMarcel Moolenaar /* 4019d1a0d267SMarcel Moolenaar * Unlike text style, we want to retain the original value and 4020d1a0d267SMarcel Moolenaar * stuff it into the "data-number" attribute. 4021d1a0d267SMarcel Moolenaar */ 4022d1a0d267SMarcel Moolenaar static const char div_number[] = "\" data-number=\""; 40238a6eceffSPhil Shafer ssize_t div_len = sizeof(div_number) - 1; 4024d1a0d267SMarcel Moolenaar 40258a6eceffSPhil Shafer ssize_t end_offset = xbp->xb_curp - xbp->xb_bufp; 40268a6eceffSPhil Shafer ssize_t olen = end_offset - save.xhs_offset; 4027d1a0d267SMarcel Moolenaar 4028d1a0d267SMarcel Moolenaar char *cp = alloca(olen + 1); 4029d1a0d267SMarcel Moolenaar memcpy(cp, xbp->xb_bufp + save.xhs_offset, olen); 4030d1a0d267SMarcel Moolenaar cp[olen] = '\0'; 4031d1a0d267SMarcel Moolenaar 4032d1a0d267SMarcel Moolenaar xo_format_humanize(xop, xbp, &save, flags); 4033d1a0d267SMarcel Moolenaar 4034d1a0d267SMarcel Moolenaar if (xo_buf_has_room(xbp, div_len + olen)) { 40358a6eceffSPhil Shafer ssize_t new_offset = xbp->xb_curp - xbp->xb_bufp; 4036d1a0d267SMarcel Moolenaar 4037d1a0d267SMarcel Moolenaar 4038d1a0d267SMarcel Moolenaar /* Move the humanized string off to the left */ 4039d1a0d267SMarcel Moolenaar memmove(xbp->xb_bufp + base_offset + div_len + olen, 4040d1a0d267SMarcel Moolenaar xbp->xb_bufp + base_offset, new_offset - base_offset); 4041d1a0d267SMarcel Moolenaar 4042d1a0d267SMarcel Moolenaar /* Copy the data_number attribute name */ 4043d1a0d267SMarcel Moolenaar memcpy(xbp->xb_bufp + base_offset, div_number, div_len); 4044d1a0d267SMarcel Moolenaar 4045d1a0d267SMarcel Moolenaar /* Copy the original long value */ 4046d1a0d267SMarcel Moolenaar memcpy(xbp->xb_bufp + base_offset + div_len, cp, olen); 4047d1a0d267SMarcel Moolenaar xbp->xb_curp += div_len + olen; 4048d1a0d267SMarcel Moolenaar } 4049d1a0d267SMarcel Moolenaar } 405031337658SMarcel Moolenaar 405131337658SMarcel Moolenaar xo_data_append(xop, div_close, sizeof(div_close) - 1); 405231337658SMarcel Moolenaar 4053d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_PRETTY)) 405431337658SMarcel Moolenaar xo_data_append(xop, "\n", 1); 405531337658SMarcel Moolenaar } 405631337658SMarcel Moolenaar 405731337658SMarcel Moolenaar static void 40588a6eceffSPhil Shafer xo_format_text (xo_handle_t *xop, const char *str, ssize_t len) 405931337658SMarcel Moolenaar { 4060788ca347SMarcel Moolenaar switch (xo_style(xop)) { 406131337658SMarcel Moolenaar case XO_STYLE_TEXT: 406231337658SMarcel Moolenaar xo_buf_append_locale(xop, &xop->xo_data, str, len); 406331337658SMarcel Moolenaar break; 406431337658SMarcel Moolenaar 406531337658SMarcel Moolenaar case XO_STYLE_HTML: 4066264104f2SPhil Shafer xo_buf_append_div(xop, "text", 0, NULL, 0, str, len, NULL, 0, NULL, 0); 406731337658SMarcel Moolenaar break; 406831337658SMarcel Moolenaar } 406931337658SMarcel Moolenaar } 407031337658SMarcel Moolenaar 407131337658SMarcel Moolenaar static void 407242ff34c3SPhil Shafer xo_format_title (xo_handle_t *xop, xo_field_info_t *xfip, 4073264104f2SPhil Shafer const char *value, ssize_t vlen) 407431337658SMarcel Moolenaar { 4075d1a0d267SMarcel Moolenaar const char *fmt = xfip->xfi_format; 40768a6eceffSPhil Shafer ssize_t flen = xfip->xfi_flen; 4077d1a0d267SMarcel Moolenaar xo_xff_flags_t flags = xfip->xfi_flags; 4078d1a0d267SMarcel Moolenaar 4079788ca347SMarcel Moolenaar static char div_open[] = "<div class=\"title"; 4080788ca347SMarcel Moolenaar static char div_middle[] = "\">"; 408131337658SMarcel Moolenaar static char div_close[] = "</div>"; 408231337658SMarcel Moolenaar 4083545ddfbeSMarcel Moolenaar if (flen == 0) { 4084545ddfbeSMarcel Moolenaar fmt = "%s"; 4085545ddfbeSMarcel Moolenaar flen = 2; 4086545ddfbeSMarcel Moolenaar } 4087545ddfbeSMarcel Moolenaar 4088788ca347SMarcel Moolenaar switch (xo_style(xop)) { 408931337658SMarcel Moolenaar case XO_STYLE_XML: 409031337658SMarcel Moolenaar case XO_STYLE_JSON: 4091d1a0d267SMarcel Moolenaar case XO_STYLE_SDPARAMS: 4092d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER: 409331337658SMarcel Moolenaar /* 409431337658SMarcel Moolenaar * Even though we don't care about text, we need to do 409531337658SMarcel Moolenaar * enough parsing work to skip over the right bits of xo_vap. 409631337658SMarcel Moolenaar */ 4097264104f2SPhil Shafer xo_simple_field(xop, TRUE, value, vlen, fmt, flen, flags); 409831337658SMarcel Moolenaar return; 409931337658SMarcel Moolenaar } 410031337658SMarcel Moolenaar 410131337658SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_data; 41028a6eceffSPhil Shafer ssize_t start = xbp->xb_curp - xbp->xb_bufp; 41038a6eceffSPhil Shafer ssize_t left = xbp->xb_size - start; 41048a6eceffSPhil Shafer ssize_t rc; 410531337658SMarcel Moolenaar 4106788ca347SMarcel Moolenaar if (xo_style(xop) == XO_STYLE_HTML) { 410731337658SMarcel Moolenaar xo_line_ensure_open(xop, 0); 4108d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_PRETTY)) 410931337658SMarcel Moolenaar xo_buf_indent(xop, xop->xo_indent_by); 411031337658SMarcel Moolenaar xo_buf_append(&xop->xo_data, div_open, sizeof(div_open) - 1); 4111788ca347SMarcel Moolenaar xo_color_append_html(xop); 4112788ca347SMarcel Moolenaar xo_buf_append(&xop->xo_data, div_middle, sizeof(div_middle) - 1); 411331337658SMarcel Moolenaar } 411431337658SMarcel Moolenaar 411531337658SMarcel Moolenaar start = xbp->xb_curp - xbp->xb_bufp; /* Reset start */ 4116264104f2SPhil Shafer if (vlen) { 411731337658SMarcel Moolenaar char *newfmt = alloca(flen + 1); 411831337658SMarcel Moolenaar memcpy(newfmt, fmt, flen); 411931337658SMarcel Moolenaar newfmt[flen] = '\0'; 412031337658SMarcel Moolenaar 412131337658SMarcel Moolenaar /* If len is non-zero, the format string apply to the name */ 4122264104f2SPhil Shafer char *newstr = alloca(vlen + 1); 4123264104f2SPhil Shafer memcpy(newstr, value, vlen); 4124264104f2SPhil Shafer newstr[vlen] = '\0'; 412531337658SMarcel Moolenaar 4126264104f2SPhil Shafer if (newstr[vlen - 1] == 's') { 412731337658SMarcel Moolenaar char *bp; 412831337658SMarcel Moolenaar 412931337658SMarcel Moolenaar rc = snprintf(NULL, 0, newfmt, newstr); 413031337658SMarcel Moolenaar if (rc > 0) { 413131337658SMarcel Moolenaar /* 413231337658SMarcel Moolenaar * We have to do this the hard way, since we might need 413331337658SMarcel Moolenaar * the columns. 413431337658SMarcel Moolenaar */ 413531337658SMarcel Moolenaar bp = alloca(rc + 1); 413631337658SMarcel Moolenaar rc = snprintf(bp, rc + 1, newfmt, newstr); 4137d1a0d267SMarcel Moolenaar 4138d1a0d267SMarcel Moolenaar xo_data_append_content(xop, bp, rc, flags); 413931337658SMarcel Moolenaar } 414031337658SMarcel Moolenaar goto move_along; 414131337658SMarcel Moolenaar 414231337658SMarcel Moolenaar } else { 414331337658SMarcel Moolenaar rc = snprintf(xbp->xb_curp, left, newfmt, newstr); 4144d1a0d267SMarcel Moolenaar if (rc >= left) { 414531337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, rc)) 414631337658SMarcel Moolenaar return; 414731337658SMarcel Moolenaar left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp); 414831337658SMarcel Moolenaar rc = snprintf(xbp->xb_curp, left, newfmt, newstr); 414931337658SMarcel Moolenaar } 415031337658SMarcel Moolenaar 415131337658SMarcel Moolenaar if (rc > 0) { 4152d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_COLUMNS)) 415331337658SMarcel Moolenaar xop->xo_columns += rc; 4154d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_ANCHOR)) 415531337658SMarcel Moolenaar xop->xo_anchor_columns += rc; 415631337658SMarcel Moolenaar } 415731337658SMarcel Moolenaar } 415831337658SMarcel Moolenaar 415931337658SMarcel Moolenaar } else { 4160d1a0d267SMarcel Moolenaar xo_do_format_field(xop, NULL, fmt, flen, flags); 416131337658SMarcel Moolenaar 4162d1a0d267SMarcel Moolenaar /* xo_do_format_field moved curp, so we need to reset it */ 416331337658SMarcel Moolenaar rc = xbp->xb_curp - (xbp->xb_bufp + start); 416431337658SMarcel Moolenaar xbp->xb_curp = xbp->xb_bufp + start; 416531337658SMarcel Moolenaar } 416631337658SMarcel Moolenaar 416731337658SMarcel Moolenaar /* If we're styling HTML, then we need to escape it */ 4168788ca347SMarcel Moolenaar if (xo_style(xop) == XO_STYLE_HTML) { 416931337658SMarcel Moolenaar rc = xo_escape_xml(xbp, rc, 0); 417031337658SMarcel Moolenaar } 417131337658SMarcel Moolenaar 417231337658SMarcel Moolenaar if (rc > 0) 417331337658SMarcel Moolenaar xbp->xb_curp += rc; 417431337658SMarcel Moolenaar 417531337658SMarcel Moolenaar move_along: 4176788ca347SMarcel Moolenaar if (xo_style(xop) == XO_STYLE_HTML) { 417731337658SMarcel Moolenaar xo_data_append(xop, div_close, sizeof(div_close) - 1); 4178d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_PRETTY)) 417931337658SMarcel Moolenaar xo_data_append(xop, "\n", 1); 418031337658SMarcel Moolenaar } 418131337658SMarcel Moolenaar } 418231337658SMarcel Moolenaar 418331337658SMarcel Moolenaar static void 418431337658SMarcel Moolenaar xo_format_prep (xo_handle_t *xop, xo_xff_flags_t flags) 418531337658SMarcel Moolenaar { 418631337658SMarcel Moolenaar if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST) { 418731337658SMarcel Moolenaar xo_data_append(xop, ",", 1); 4188d1a0d267SMarcel Moolenaar if (!(flags & XFF_LEAF_LIST) && XOF_ISSET(xop, XOF_PRETTY)) 418931337658SMarcel Moolenaar xo_data_append(xop, "\n", 1); 419031337658SMarcel Moolenaar } else 419131337658SMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 419231337658SMarcel Moolenaar } 419331337658SMarcel Moolenaar 419431337658SMarcel Moolenaar #if 0 419531337658SMarcel Moolenaar /* Useful debugging function */ 419631337658SMarcel Moolenaar void 419731337658SMarcel Moolenaar xo_arg (xo_handle_t *xop); 419831337658SMarcel Moolenaar void 419931337658SMarcel Moolenaar xo_arg (xo_handle_t *xop) 420031337658SMarcel Moolenaar { 420131337658SMarcel Moolenaar xop = xo_default(xop); 420231337658SMarcel Moolenaar fprintf(stderr, "0x%x", va_arg(xop->xo_vap, unsigned)); 420331337658SMarcel Moolenaar } 420431337658SMarcel Moolenaar #endif /* 0 */ 420531337658SMarcel Moolenaar 420631337658SMarcel Moolenaar static void 42078a6eceffSPhil Shafer xo_format_value (xo_handle_t *xop, const char *name, ssize_t nlen, 4208264104f2SPhil Shafer const char *value, ssize_t vlen, 4209264104f2SPhil Shafer const char *fmt, ssize_t flen, 42108a6eceffSPhil Shafer const char *encoding, ssize_t elen, xo_xff_flags_t flags) 421131337658SMarcel Moolenaar { 4212d1a0d267SMarcel Moolenaar int pretty = XOF_ISSET(xop, XOF_PRETTY); 421331337658SMarcel Moolenaar int quote; 421431337658SMarcel Moolenaar 4215545ddfbeSMarcel Moolenaar /* 4216545ddfbeSMarcel Moolenaar * Before we emit a value, we need to know that the frame is ready. 4217545ddfbeSMarcel Moolenaar */ 4218545ddfbeSMarcel Moolenaar xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth]; 4219545ddfbeSMarcel Moolenaar 4220545ddfbeSMarcel Moolenaar if (flags & XFF_LEAF_LIST) { 4221545ddfbeSMarcel Moolenaar /* 4222545ddfbeSMarcel Moolenaar * Check if we've already started to emit normal leafs 4223545ddfbeSMarcel Moolenaar * or if we're not in a leaf list. 4224545ddfbeSMarcel Moolenaar */ 4225545ddfbeSMarcel Moolenaar if ((xsp->xs_flags & (XSF_EMIT | XSF_EMIT_KEY)) 4226545ddfbeSMarcel Moolenaar || !(xsp->xs_flags & XSF_EMIT_LEAF_LIST)) { 4227545ddfbeSMarcel Moolenaar char nbuf[nlen + 1]; 4228545ddfbeSMarcel Moolenaar memcpy(nbuf, name, nlen); 4229545ddfbeSMarcel Moolenaar nbuf[nlen] = '\0'; 4230545ddfbeSMarcel Moolenaar 42318a6eceffSPhil Shafer ssize_t rc = xo_transition(xop, 0, nbuf, XSS_EMIT_LEAF_LIST); 4232545ddfbeSMarcel Moolenaar if (rc < 0) 4233545ddfbeSMarcel Moolenaar flags |= XFF_DISPLAY_ONLY | XFF_ENCODE_ONLY; 4234545ddfbeSMarcel Moolenaar else 4235545ddfbeSMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_EMIT_LEAF_LIST; 4236545ddfbeSMarcel Moolenaar } 4237545ddfbeSMarcel Moolenaar 4238545ddfbeSMarcel Moolenaar xsp = &xop->xo_stack[xop->xo_depth]; 4239545ddfbeSMarcel Moolenaar if (xsp->xs_name) { 4240545ddfbeSMarcel Moolenaar name = xsp->xs_name; 4241545ddfbeSMarcel Moolenaar nlen = strlen(name); 4242545ddfbeSMarcel Moolenaar } 4243545ddfbeSMarcel Moolenaar 4244545ddfbeSMarcel Moolenaar } else if (flags & XFF_KEY) { 4245545ddfbeSMarcel Moolenaar /* Emitting a 'k' (key) field */ 4246545ddfbeSMarcel Moolenaar if ((xsp->xs_flags & XSF_EMIT) && !(flags & XFF_DISPLAY_ONLY)) { 4247545ddfbeSMarcel Moolenaar xo_failure(xop, "key field emitted after normal value field: '%.*s'", 4248545ddfbeSMarcel Moolenaar nlen, name); 4249545ddfbeSMarcel Moolenaar 4250545ddfbeSMarcel Moolenaar } else if (!(xsp->xs_flags & XSF_EMIT_KEY)) { 4251545ddfbeSMarcel Moolenaar char nbuf[nlen + 1]; 4252545ddfbeSMarcel Moolenaar memcpy(nbuf, name, nlen); 4253545ddfbeSMarcel Moolenaar nbuf[nlen] = '\0'; 4254545ddfbeSMarcel Moolenaar 42558a6eceffSPhil Shafer ssize_t rc = xo_transition(xop, 0, nbuf, XSS_EMIT); 4256545ddfbeSMarcel Moolenaar if (rc < 0) 4257545ddfbeSMarcel Moolenaar flags |= XFF_DISPLAY_ONLY | XFF_ENCODE_ONLY; 4258545ddfbeSMarcel Moolenaar else 4259545ddfbeSMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_EMIT_KEY; 4260545ddfbeSMarcel Moolenaar 4261545ddfbeSMarcel Moolenaar xsp = &xop->xo_stack[xop->xo_depth]; 4262545ddfbeSMarcel Moolenaar xsp->xs_flags |= XSF_EMIT_KEY; 4263545ddfbeSMarcel Moolenaar } 4264545ddfbeSMarcel Moolenaar 4265545ddfbeSMarcel Moolenaar } else { 4266545ddfbeSMarcel Moolenaar /* Emitting a normal value field */ 4267545ddfbeSMarcel Moolenaar if ((xsp->xs_flags & XSF_EMIT_LEAF_LIST) 4268545ddfbeSMarcel Moolenaar || !(xsp->xs_flags & XSF_EMIT)) { 4269545ddfbeSMarcel Moolenaar char nbuf[nlen + 1]; 4270545ddfbeSMarcel Moolenaar memcpy(nbuf, name, nlen); 4271545ddfbeSMarcel Moolenaar nbuf[nlen] = '\0'; 4272545ddfbeSMarcel Moolenaar 42738a6eceffSPhil Shafer ssize_t rc = xo_transition(xop, 0, nbuf, XSS_EMIT); 4274545ddfbeSMarcel Moolenaar if (rc < 0) 4275545ddfbeSMarcel Moolenaar flags |= XFF_DISPLAY_ONLY | XFF_ENCODE_ONLY; 4276545ddfbeSMarcel Moolenaar else 4277545ddfbeSMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_EMIT; 4278545ddfbeSMarcel Moolenaar 4279545ddfbeSMarcel Moolenaar xsp = &xop->xo_stack[xop->xo_depth]; 4280545ddfbeSMarcel Moolenaar xsp->xs_flags |= XSF_EMIT; 4281545ddfbeSMarcel Moolenaar } 4282545ddfbeSMarcel Moolenaar } 4283545ddfbeSMarcel Moolenaar 4284d1a0d267SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_data; 4285d1a0d267SMarcel Moolenaar xo_humanize_save_t save; /* Save values for humanizing logic */ 4286d1a0d267SMarcel Moolenaar 4287788ca347SMarcel Moolenaar switch (xo_style(xop)) { 428831337658SMarcel Moolenaar case XO_STYLE_TEXT: 428931337658SMarcel Moolenaar if (flags & XFF_ENCODE_ONLY) 429031337658SMarcel Moolenaar flags |= XFF_NO_OUTPUT; 4291d1a0d267SMarcel Moolenaar 4292d1a0d267SMarcel Moolenaar save.xhs_offset = xbp->xb_curp - xbp->xb_bufp; 4293d1a0d267SMarcel Moolenaar save.xhs_columns = xop->xo_columns; 4294d1a0d267SMarcel Moolenaar save.xhs_anchor_columns = xop->xo_anchor_columns; 4295d1a0d267SMarcel Moolenaar 4296264104f2SPhil Shafer xo_simple_field(xop, FALSE, value, vlen, fmt, flen, flags); 4297d1a0d267SMarcel Moolenaar 4298d1a0d267SMarcel Moolenaar if (flags & XFF_HUMANIZE) 4299d1a0d267SMarcel Moolenaar xo_format_humanize(xop, xbp, &save, flags); 430031337658SMarcel Moolenaar break; 430131337658SMarcel Moolenaar 430231337658SMarcel Moolenaar case XO_STYLE_HTML: 430331337658SMarcel Moolenaar if (flags & XFF_ENCODE_ONLY) 430431337658SMarcel Moolenaar flags |= XFF_NO_OUTPUT; 4305d1a0d267SMarcel Moolenaar 4306264104f2SPhil Shafer xo_buf_append_div(xop, "data", flags, name, nlen, value, vlen, 4307264104f2SPhil Shafer fmt, flen, encoding, elen); 430831337658SMarcel Moolenaar break; 430931337658SMarcel Moolenaar 431031337658SMarcel Moolenaar case XO_STYLE_XML: 431131337658SMarcel Moolenaar /* 431231337658SMarcel Moolenaar * Even though we're not making output, we still need to 431331337658SMarcel Moolenaar * let the formatting code handle the va_arg popping. 431431337658SMarcel Moolenaar */ 431531337658SMarcel Moolenaar if (flags & XFF_DISPLAY_ONLY) { 4316264104f2SPhil Shafer xo_simple_field(xop, TRUE, value, vlen, fmt, flen, flags); 431731337658SMarcel Moolenaar break; 431831337658SMarcel Moolenaar } 431931337658SMarcel Moolenaar 432031337658SMarcel Moolenaar if (encoding) { 4321264104f2SPhil Shafer fmt = encoding; 432231337658SMarcel Moolenaar flen = elen; 432331337658SMarcel Moolenaar } else { 432431337658SMarcel Moolenaar char *enc = alloca(flen + 1); 4325264104f2SPhil Shafer memcpy(enc, fmt, flen); 432631337658SMarcel Moolenaar enc[flen] = '\0'; 4327264104f2SPhil Shafer fmt = xo_fix_encoding(xop, enc); 4328264104f2SPhil Shafer flen = strlen(fmt); 432931337658SMarcel Moolenaar } 433031337658SMarcel Moolenaar 433131337658SMarcel Moolenaar if (nlen == 0) { 433231337658SMarcel Moolenaar static char missing[] = "missing-field-name"; 4333264104f2SPhil Shafer xo_failure(xop, "missing field name: %s", fmt); 433431337658SMarcel Moolenaar name = missing; 433531337658SMarcel Moolenaar nlen = sizeof(missing) - 1; 433631337658SMarcel Moolenaar } 433731337658SMarcel Moolenaar 433831337658SMarcel Moolenaar if (pretty) 433931337658SMarcel Moolenaar xo_buf_indent(xop, -1); 434031337658SMarcel Moolenaar xo_data_append(xop, "<", 1); 434131337658SMarcel Moolenaar xo_data_escape(xop, name, nlen); 434231337658SMarcel Moolenaar 434331337658SMarcel Moolenaar if (xop->xo_attrs.xb_curp != xop->xo_attrs.xb_bufp) { 434431337658SMarcel Moolenaar xo_data_append(xop, xop->xo_attrs.xb_bufp, 434531337658SMarcel Moolenaar xop->xo_attrs.xb_curp - xop->xo_attrs.xb_bufp); 434631337658SMarcel Moolenaar xop->xo_attrs.xb_curp = xop->xo_attrs.xb_bufp; 434731337658SMarcel Moolenaar } 434831337658SMarcel Moolenaar 434931337658SMarcel Moolenaar /* 435031337658SMarcel Moolenaar * We indicate 'key' fields using the 'key' attribute. While 435131337658SMarcel Moolenaar * this is really committing the crime of mixing meta-data with 435231337658SMarcel Moolenaar * data, it's often useful. Especially when format meta-data is 435331337658SMarcel Moolenaar * difficult to come by. 435431337658SMarcel Moolenaar */ 4355d1a0d267SMarcel Moolenaar if ((flags & XFF_KEY) && XOF_ISSET(xop, XOF_KEYS)) { 435631337658SMarcel Moolenaar static char attr[] = " key=\"key\""; 435731337658SMarcel Moolenaar xo_data_append(xop, attr, sizeof(attr) - 1); 435831337658SMarcel Moolenaar } 435931337658SMarcel Moolenaar 436031337658SMarcel Moolenaar /* 436131337658SMarcel Moolenaar * Save the offset at which we'd place units. See xo_format_units. 436231337658SMarcel Moolenaar */ 4363d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_UNITS)) { 4364d1a0d267SMarcel Moolenaar XOIF_SET(xop, XOIF_UNITS_PENDING); 436531337658SMarcel Moolenaar xop->xo_units_offset = xop->xo_data.xb_curp -xop->xo_data.xb_bufp; 436631337658SMarcel Moolenaar } 436731337658SMarcel Moolenaar 436831337658SMarcel Moolenaar xo_data_append(xop, ">", 1); 4369264104f2SPhil Shafer 4370264104f2SPhil Shafer xo_simple_field(xop, FALSE, value, vlen, fmt, flen, flags); 4371264104f2SPhil Shafer 437231337658SMarcel Moolenaar xo_data_append(xop, "</", 2); 437331337658SMarcel Moolenaar xo_data_escape(xop, name, nlen); 437431337658SMarcel Moolenaar xo_data_append(xop, ">", 1); 437531337658SMarcel Moolenaar if (pretty) 437631337658SMarcel Moolenaar xo_data_append(xop, "\n", 1); 437731337658SMarcel Moolenaar break; 437831337658SMarcel Moolenaar 437931337658SMarcel Moolenaar case XO_STYLE_JSON: 438031337658SMarcel Moolenaar if (flags & XFF_DISPLAY_ONLY) { 4381264104f2SPhil Shafer xo_simple_field(xop, TRUE, value, vlen, fmt, flen, flags); 438231337658SMarcel Moolenaar break; 438331337658SMarcel Moolenaar } 438431337658SMarcel Moolenaar 438531337658SMarcel Moolenaar if (encoding) { 4386264104f2SPhil Shafer fmt = encoding; 438731337658SMarcel Moolenaar flen = elen; 438831337658SMarcel Moolenaar } else { 438931337658SMarcel Moolenaar char *enc = alloca(flen + 1); 4390264104f2SPhil Shafer memcpy(enc, fmt, flen); 439131337658SMarcel Moolenaar enc[flen] = '\0'; 4392264104f2SPhil Shafer fmt = xo_fix_encoding(xop, enc); 4393264104f2SPhil Shafer flen = strlen(fmt); 439431337658SMarcel Moolenaar } 439531337658SMarcel Moolenaar 43968a6eceffSPhil Shafer int first = (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST) 43978a6eceffSPhil Shafer ? 0 : 1; 439831337658SMarcel Moolenaar 439931337658SMarcel Moolenaar xo_format_prep(xop, flags); 440031337658SMarcel Moolenaar 440131337658SMarcel Moolenaar if (flags & XFF_QUOTE) 440231337658SMarcel Moolenaar quote = 1; 440331337658SMarcel Moolenaar else if (flags & XFF_NOQUOTE) 440431337658SMarcel Moolenaar quote = 0; 4405264104f2SPhil Shafer else if (vlen != 0) 4406264104f2SPhil Shafer quote = 1; 440731337658SMarcel Moolenaar else if (flen == 0) { 440831337658SMarcel Moolenaar quote = 0; 4409264104f2SPhil Shafer fmt = "true"; /* JSON encodes empty tags as a boolean true */ 441031337658SMarcel Moolenaar flen = 4; 4411264104f2SPhil Shafer } else if (strchr("diouDOUeEfFgG", fmt[flen - 1]) == NULL) 441231337658SMarcel Moolenaar quote = 1; 441331337658SMarcel Moolenaar else 441431337658SMarcel Moolenaar quote = 0; 441531337658SMarcel Moolenaar 441631337658SMarcel Moolenaar if (nlen == 0) { 441731337658SMarcel Moolenaar static char missing[] = "missing-field-name"; 4418264104f2SPhil Shafer xo_failure(xop, "missing field name: %s", fmt); 441931337658SMarcel Moolenaar name = missing; 442031337658SMarcel Moolenaar nlen = sizeof(missing) - 1; 442131337658SMarcel Moolenaar } 442231337658SMarcel Moolenaar 442331337658SMarcel Moolenaar if (flags & XFF_LEAF_LIST) { 4424788ca347SMarcel Moolenaar if (!first && pretty) 4425788ca347SMarcel Moolenaar xo_data_append(xop, "\n", 1); 4426788ca347SMarcel Moolenaar if (pretty) 442731337658SMarcel Moolenaar xo_buf_indent(xop, -1); 442831337658SMarcel Moolenaar } else { 442931337658SMarcel Moolenaar if (pretty) 443031337658SMarcel Moolenaar xo_buf_indent(xop, -1); 443131337658SMarcel Moolenaar xo_data_append(xop, "\"", 1); 443231337658SMarcel Moolenaar 443331337658SMarcel Moolenaar xbp = &xop->xo_data; 44348a6eceffSPhil Shafer ssize_t off = xbp->xb_curp - xbp->xb_bufp; 443531337658SMarcel Moolenaar 443631337658SMarcel Moolenaar xo_data_escape(xop, name, nlen); 443731337658SMarcel Moolenaar 4438d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_UNDERSCORES)) { 44398a6eceffSPhil Shafer ssize_t coff = xbp->xb_curp - xbp->xb_bufp; 44408a6eceffSPhil Shafer for ( ; off < coff; off++) 444131337658SMarcel Moolenaar if (xbp->xb_bufp[off] == '-') 444231337658SMarcel Moolenaar xbp->xb_bufp[off] = '_'; 444331337658SMarcel Moolenaar } 444431337658SMarcel Moolenaar xo_data_append(xop, "\":", 2); 444531337658SMarcel Moolenaar if (pretty) 444631337658SMarcel Moolenaar xo_data_append(xop, " ", 1); 4447788ca347SMarcel Moolenaar } 4448788ca347SMarcel Moolenaar 444931337658SMarcel Moolenaar if (quote) 445031337658SMarcel Moolenaar xo_data_append(xop, "\"", 1); 445131337658SMarcel Moolenaar 4452264104f2SPhil Shafer xo_simple_field(xop, FALSE, value, vlen, fmt, flen, flags); 445331337658SMarcel Moolenaar 445431337658SMarcel Moolenaar if (quote) 445531337658SMarcel Moolenaar xo_data_append(xop, "\"", 1); 445631337658SMarcel Moolenaar break; 4457d1a0d267SMarcel Moolenaar 4458d1a0d267SMarcel Moolenaar case XO_STYLE_SDPARAMS: 4459d1a0d267SMarcel Moolenaar if (flags & XFF_DISPLAY_ONLY) { 4460264104f2SPhil Shafer xo_simple_field(xop, TRUE, value, vlen, fmt, flen, flags); 4461d1a0d267SMarcel Moolenaar break; 4462d1a0d267SMarcel Moolenaar } 4463d1a0d267SMarcel Moolenaar 4464d1a0d267SMarcel Moolenaar if (encoding) { 4465264104f2SPhil Shafer fmt = encoding; 4466d1a0d267SMarcel Moolenaar flen = elen; 4467d1a0d267SMarcel Moolenaar } else { 4468d1a0d267SMarcel Moolenaar char *enc = alloca(flen + 1); 4469264104f2SPhil Shafer memcpy(enc, fmt, flen); 4470d1a0d267SMarcel Moolenaar enc[flen] = '\0'; 4471264104f2SPhil Shafer fmt = xo_fix_encoding(xop, enc); 4472264104f2SPhil Shafer flen = strlen(fmt); 4473d1a0d267SMarcel Moolenaar } 4474d1a0d267SMarcel Moolenaar 4475d1a0d267SMarcel Moolenaar if (nlen == 0) { 4476d1a0d267SMarcel Moolenaar static char missing[] = "missing-field-name"; 4477264104f2SPhil Shafer xo_failure(xop, "missing field name: %s", fmt); 4478d1a0d267SMarcel Moolenaar name = missing; 4479d1a0d267SMarcel Moolenaar nlen = sizeof(missing) - 1; 4480d1a0d267SMarcel Moolenaar } 4481d1a0d267SMarcel Moolenaar 4482d1a0d267SMarcel Moolenaar xo_data_escape(xop, name, nlen); 4483d1a0d267SMarcel Moolenaar xo_data_append(xop, "=\"", 2); 4484264104f2SPhil Shafer 4485264104f2SPhil Shafer xo_simple_field(xop, FALSE, value, vlen, fmt, flen, flags); 4486264104f2SPhil Shafer 4487d1a0d267SMarcel Moolenaar xo_data_append(xop, "\" ", 2); 4488d1a0d267SMarcel Moolenaar break; 4489d1a0d267SMarcel Moolenaar 4490d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER: 4491d1a0d267SMarcel Moolenaar if (flags & XFF_DISPLAY_ONLY) { 4492264104f2SPhil Shafer xo_simple_field(xop, TRUE, value, vlen, fmt, flen, flags); 4493d1a0d267SMarcel Moolenaar break; 4494d1a0d267SMarcel Moolenaar } 4495d1a0d267SMarcel Moolenaar 4496d1a0d267SMarcel Moolenaar if (flags & XFF_QUOTE) 4497d1a0d267SMarcel Moolenaar quote = 1; 4498d1a0d267SMarcel Moolenaar else if (flags & XFF_NOQUOTE) 4499d1a0d267SMarcel Moolenaar quote = 0; 4500d1a0d267SMarcel Moolenaar else if (flen == 0) { 4501d1a0d267SMarcel Moolenaar quote = 0; 4502264104f2SPhil Shafer fmt = "true"; /* JSON encodes empty tags as a boolean true */ 4503d1a0d267SMarcel Moolenaar flen = 4; 4504264104f2SPhil Shafer } else if (strchr("diouxXDOUeEfFgGaAcCp", fmt[flen - 1]) == NULL) 4505d1a0d267SMarcel Moolenaar quote = 1; 4506d1a0d267SMarcel Moolenaar else 4507d1a0d267SMarcel Moolenaar quote = 0; 4508d1a0d267SMarcel Moolenaar 4509d1a0d267SMarcel Moolenaar if (encoding) { 4510264104f2SPhil Shafer fmt = encoding; 4511d1a0d267SMarcel Moolenaar flen = elen; 4512d1a0d267SMarcel Moolenaar } else { 4513d1a0d267SMarcel Moolenaar char *enc = alloca(flen + 1); 4514264104f2SPhil Shafer memcpy(enc, fmt, flen); 4515d1a0d267SMarcel Moolenaar enc[flen] = '\0'; 4516264104f2SPhil Shafer fmt = xo_fix_encoding(xop, enc); 4517264104f2SPhil Shafer flen = strlen(fmt); 4518d1a0d267SMarcel Moolenaar } 4519d1a0d267SMarcel Moolenaar 4520d1a0d267SMarcel Moolenaar if (nlen == 0) { 4521d1a0d267SMarcel Moolenaar static char missing[] = "missing-field-name"; 4522264104f2SPhil Shafer xo_failure(xop, "missing field name: %s", fmt); 4523d1a0d267SMarcel Moolenaar name = missing; 4524d1a0d267SMarcel Moolenaar nlen = sizeof(missing) - 1; 4525d1a0d267SMarcel Moolenaar } 4526d1a0d267SMarcel Moolenaar 45278a6eceffSPhil Shafer ssize_t name_offset = xo_buf_offset(&xop->xo_data); 4528d1a0d267SMarcel Moolenaar xo_data_append(xop, name, nlen); 4529d1a0d267SMarcel Moolenaar xo_data_append(xop, "", 1); 4530d1a0d267SMarcel Moolenaar 45318a6eceffSPhil Shafer ssize_t value_offset = xo_buf_offset(&xop->xo_data); 4532264104f2SPhil Shafer 4533264104f2SPhil Shafer xo_simple_field(xop, FALSE, value, vlen, fmt, flen, flags); 4534264104f2SPhil Shafer 4535d1a0d267SMarcel Moolenaar xo_data_append(xop, "", 1); 4536d1a0d267SMarcel Moolenaar 4537d1a0d267SMarcel Moolenaar xo_encoder_handle(xop, quote ? XO_OP_STRING : XO_OP_CONTENT, 4538d1a0d267SMarcel Moolenaar xo_buf_data(&xop->xo_data, name_offset), 4539f2b7bf8aSPhil Shafer xo_buf_data(&xop->xo_data, value_offset), flags); 4540d1a0d267SMarcel Moolenaar xo_buf_reset(&xop->xo_data); 4541d1a0d267SMarcel Moolenaar break; 454231337658SMarcel Moolenaar } 454331337658SMarcel Moolenaar } 454431337658SMarcel Moolenaar 454531337658SMarcel Moolenaar static void 454642ff34c3SPhil Shafer xo_set_gettext_domain (xo_handle_t *xop, xo_field_info_t *xfip, 45478a6eceffSPhil Shafer const char *str, ssize_t len) 4548d1a0d267SMarcel Moolenaar { 4549d1a0d267SMarcel Moolenaar const char *fmt = xfip->xfi_format; 45508a6eceffSPhil Shafer ssize_t flen = xfip->xfi_flen; 4551d1a0d267SMarcel Moolenaar 4552d1a0d267SMarcel Moolenaar /* Start by discarding previous domain */ 4553d1a0d267SMarcel Moolenaar if (xop->xo_gt_domain) { 4554d1a0d267SMarcel Moolenaar xo_free(xop->xo_gt_domain); 4555d1a0d267SMarcel Moolenaar xop->xo_gt_domain = NULL; 4556d1a0d267SMarcel Moolenaar } 4557d1a0d267SMarcel Moolenaar 4558d1a0d267SMarcel Moolenaar /* An empty {G:} means no domainname */ 4559d1a0d267SMarcel Moolenaar if (len == 0 && flen == 0) 4560d1a0d267SMarcel Moolenaar return; 4561d1a0d267SMarcel Moolenaar 45628a6eceffSPhil Shafer ssize_t start_offset = -1; 4563d1a0d267SMarcel Moolenaar if (len == 0 && flen != 0) { 4564d1a0d267SMarcel Moolenaar /* Need to do format the data to get the domainname from args */ 4565d1a0d267SMarcel Moolenaar start_offset = xop->xo_data.xb_curp - xop->xo_data.xb_bufp; 4566d1a0d267SMarcel Moolenaar xo_do_format_field(xop, NULL, fmt, flen, 0); 4567d1a0d267SMarcel Moolenaar 45688a6eceffSPhil Shafer ssize_t end_offset = xop->xo_data.xb_curp - xop->xo_data.xb_bufp; 4569d1a0d267SMarcel Moolenaar len = end_offset - start_offset; 4570d1a0d267SMarcel Moolenaar str = xop->xo_data.xb_bufp + start_offset; 4571d1a0d267SMarcel Moolenaar } 4572d1a0d267SMarcel Moolenaar 4573d1a0d267SMarcel Moolenaar xop->xo_gt_domain = xo_strndup(str, len); 4574d1a0d267SMarcel Moolenaar 4575d1a0d267SMarcel Moolenaar /* Reset the current buffer point to avoid emitting the name as output */ 4576d1a0d267SMarcel Moolenaar if (start_offset >= 0) 4577d1a0d267SMarcel Moolenaar xop->xo_data.xb_curp = xop->xo_data.xb_bufp + start_offset; 4578d1a0d267SMarcel Moolenaar } 4579d1a0d267SMarcel Moolenaar 4580d1a0d267SMarcel Moolenaar static void 458131337658SMarcel Moolenaar xo_format_content (xo_handle_t *xop, const char *class_name, 4582d1a0d267SMarcel Moolenaar const char *tag_name, 4583264104f2SPhil Shafer const char *value, ssize_t vlen, 4584264104f2SPhil Shafer const char *fmt, ssize_t flen, 4585d1a0d267SMarcel Moolenaar xo_xff_flags_t flags) 458631337658SMarcel Moolenaar { 4587788ca347SMarcel Moolenaar switch (xo_style(xop)) { 458831337658SMarcel Moolenaar case XO_STYLE_TEXT: 4589264104f2SPhil Shafer xo_simple_field(xop, FALSE, value, vlen, fmt, flen, flags); 459031337658SMarcel Moolenaar break; 459131337658SMarcel Moolenaar 459231337658SMarcel Moolenaar case XO_STYLE_HTML: 4593264104f2SPhil Shafer xo_buf_append_div(xop, class_name, flags, NULL, 0, 4594264104f2SPhil Shafer value, vlen, fmt, flen, NULL, 0); 459531337658SMarcel Moolenaar break; 459631337658SMarcel Moolenaar 459731337658SMarcel Moolenaar case XO_STYLE_XML: 4598d1a0d267SMarcel Moolenaar case XO_STYLE_JSON: 4599d1a0d267SMarcel Moolenaar case XO_STYLE_SDPARAMS: 4600d1a0d267SMarcel Moolenaar if (tag_name) { 4601d1a0d267SMarcel Moolenaar xo_open_container_h(xop, tag_name); 4602264104f2SPhil Shafer xo_format_value(xop, "message", 7, value, vlen, 4603264104f2SPhil Shafer fmt, flen, NULL, 0, flags); 4604d1a0d267SMarcel Moolenaar xo_close_container_h(xop, tag_name); 460531337658SMarcel Moolenaar 460631337658SMarcel Moolenaar } else { 460731337658SMarcel Moolenaar /* 460831337658SMarcel Moolenaar * Even though we don't care about labels, we need to do 460931337658SMarcel Moolenaar * enough parsing work to skip over the right bits of xo_vap. 461031337658SMarcel Moolenaar */ 4611264104f2SPhil Shafer xo_simple_field(xop, TRUE, value, vlen, fmt, flen, flags); 461231337658SMarcel Moolenaar } 461331337658SMarcel Moolenaar break; 461431337658SMarcel Moolenaar 4615d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER: 4616264104f2SPhil Shafer xo_simple_field(xop, TRUE, value, vlen, fmt, flen, flags); 461731337658SMarcel Moolenaar break; 461831337658SMarcel Moolenaar } 461931337658SMarcel Moolenaar } 462031337658SMarcel Moolenaar 4621788ca347SMarcel Moolenaar static const char *xo_color_names[] = { 4622788ca347SMarcel Moolenaar "default", /* XO_COL_DEFAULT */ 4623788ca347SMarcel Moolenaar "black", /* XO_COL_BLACK */ 4624788ca347SMarcel Moolenaar "red", /* XO_CLOR_RED */ 4625788ca347SMarcel Moolenaar "green", /* XO_COL_GREEN */ 4626788ca347SMarcel Moolenaar "yellow", /* XO_COL_YELLOW */ 4627788ca347SMarcel Moolenaar "blue", /* XO_COL_BLUE */ 4628788ca347SMarcel Moolenaar "magenta", /* XO_COL_MAGENTA */ 4629788ca347SMarcel Moolenaar "cyan", /* XO_COL_CYAN */ 4630788ca347SMarcel Moolenaar "white", /* XO_COL_WHITE */ 4631788ca347SMarcel Moolenaar NULL 4632788ca347SMarcel Moolenaar }; 4633788ca347SMarcel Moolenaar 4634788ca347SMarcel Moolenaar static int 4635788ca347SMarcel Moolenaar xo_color_find (const char *str) 4636788ca347SMarcel Moolenaar { 4637788ca347SMarcel Moolenaar int i; 4638788ca347SMarcel Moolenaar 4639788ca347SMarcel Moolenaar for (i = 0; xo_color_names[i]; i++) { 4640788ca347SMarcel Moolenaar if (strcmp(xo_color_names[i], str) == 0) 4641788ca347SMarcel Moolenaar return i; 4642788ca347SMarcel Moolenaar } 4643788ca347SMarcel Moolenaar 4644788ca347SMarcel Moolenaar return -1; 4645788ca347SMarcel Moolenaar } 4646788ca347SMarcel Moolenaar 4647788ca347SMarcel Moolenaar static const char *xo_effect_names[] = { 4648788ca347SMarcel Moolenaar "reset", /* XO_EFF_RESET */ 4649788ca347SMarcel Moolenaar "normal", /* XO_EFF_NORMAL */ 4650788ca347SMarcel Moolenaar "bold", /* XO_EFF_BOLD */ 4651788ca347SMarcel Moolenaar "underline", /* XO_EFF_UNDERLINE */ 4652788ca347SMarcel Moolenaar "inverse", /* XO_EFF_INVERSE */ 4653788ca347SMarcel Moolenaar NULL 4654788ca347SMarcel Moolenaar }; 4655788ca347SMarcel Moolenaar 4656788ca347SMarcel Moolenaar static const char *xo_effect_on_codes[] = { 4657788ca347SMarcel Moolenaar "0", /* XO_EFF_RESET */ 4658788ca347SMarcel Moolenaar "0", /* XO_EFF_NORMAL */ 4659788ca347SMarcel Moolenaar "1", /* XO_EFF_BOLD */ 4660788ca347SMarcel Moolenaar "4", /* XO_EFF_UNDERLINE */ 4661788ca347SMarcel Moolenaar "7", /* XO_EFF_INVERSE */ 4662788ca347SMarcel Moolenaar NULL 4663788ca347SMarcel Moolenaar }; 4664788ca347SMarcel Moolenaar 4665788ca347SMarcel Moolenaar #if 0 4666788ca347SMarcel Moolenaar /* 4667788ca347SMarcel Moolenaar * See comment below re: joy of terminal standards. These can 4668788ca347SMarcel Moolenaar * be use by just adding: 4669d1a0d267SMarcel Moolenaar * + if (newp->xoc_effects & bit) 4670788ca347SMarcel Moolenaar * code = xo_effect_on_codes[i]; 4671788ca347SMarcel Moolenaar * + else 4672788ca347SMarcel Moolenaar * + code = xo_effect_off_codes[i]; 4673788ca347SMarcel Moolenaar * in xo_color_handle_text. 4674788ca347SMarcel Moolenaar */ 4675788ca347SMarcel Moolenaar static const char *xo_effect_off_codes[] = { 4676788ca347SMarcel Moolenaar "0", /* XO_EFF_RESET */ 4677788ca347SMarcel Moolenaar "0", /* XO_EFF_NORMAL */ 4678788ca347SMarcel Moolenaar "21", /* XO_EFF_BOLD */ 4679788ca347SMarcel Moolenaar "24", /* XO_EFF_UNDERLINE */ 4680788ca347SMarcel Moolenaar "27", /* XO_EFF_INVERSE */ 4681788ca347SMarcel Moolenaar NULL 4682788ca347SMarcel Moolenaar }; 4683788ca347SMarcel Moolenaar #endif /* 0 */ 4684788ca347SMarcel Moolenaar 4685788ca347SMarcel Moolenaar static int 4686788ca347SMarcel Moolenaar xo_effect_find (const char *str) 4687788ca347SMarcel Moolenaar { 4688788ca347SMarcel Moolenaar int i; 4689788ca347SMarcel Moolenaar 4690788ca347SMarcel Moolenaar for (i = 0; xo_effect_names[i]; i++) { 4691788ca347SMarcel Moolenaar if (strcmp(xo_effect_names[i], str) == 0) 4692788ca347SMarcel Moolenaar return i; 4693788ca347SMarcel Moolenaar } 4694788ca347SMarcel Moolenaar 4695788ca347SMarcel Moolenaar return -1; 4696788ca347SMarcel Moolenaar } 4697788ca347SMarcel Moolenaar 4698788ca347SMarcel Moolenaar static void 4699788ca347SMarcel Moolenaar xo_colors_parse (xo_handle_t *xop, xo_colors_t *xocp, char *str) 4700788ca347SMarcel Moolenaar { 4701788ca347SMarcel Moolenaar #ifdef LIBXO_TEXT_ONLY 4702788ca347SMarcel Moolenaar return; 4703788ca347SMarcel Moolenaar #endif /* LIBXO_TEXT_ONLY */ 4704788ca347SMarcel Moolenaar 4705788ca347SMarcel Moolenaar char *cp, *ep, *np, *xp; 47068a6eceffSPhil Shafer ssize_t len = strlen(str); 4707788ca347SMarcel Moolenaar int rc; 4708788ca347SMarcel Moolenaar 4709788ca347SMarcel Moolenaar /* 4710788ca347SMarcel Moolenaar * Possible tokens: colors, bg-colors, effects, no-effects, "reset". 4711788ca347SMarcel Moolenaar */ 4712788ca347SMarcel Moolenaar for (cp = str, ep = cp + len - 1; cp && cp < ep; cp = np) { 4713788ca347SMarcel Moolenaar /* Trim leading whitespace */ 4714788ca347SMarcel Moolenaar while (isspace((int) *cp)) 4715788ca347SMarcel Moolenaar cp += 1; 4716788ca347SMarcel Moolenaar 4717788ca347SMarcel Moolenaar np = strchr(cp, ','); 4718788ca347SMarcel Moolenaar if (np) 4719788ca347SMarcel Moolenaar *np++ = '\0'; 4720788ca347SMarcel Moolenaar 4721788ca347SMarcel Moolenaar /* Trim trailing whitespace */ 4722788ca347SMarcel Moolenaar xp = cp + strlen(cp) - 1; 4723788ca347SMarcel Moolenaar while (isspace(*xp) && xp > cp) 4724788ca347SMarcel Moolenaar *xp-- = '\0'; 4725788ca347SMarcel Moolenaar 4726788ca347SMarcel Moolenaar if (cp[0] == 'f' && cp[1] == 'g' && cp[2] == '-') { 4727788ca347SMarcel Moolenaar rc = xo_color_find(cp + 3); 4728788ca347SMarcel Moolenaar if (rc < 0) 4729788ca347SMarcel Moolenaar goto unknown; 4730788ca347SMarcel Moolenaar 4731788ca347SMarcel Moolenaar xocp->xoc_col_fg = rc; 4732788ca347SMarcel Moolenaar 4733788ca347SMarcel Moolenaar } else if (cp[0] == 'b' && cp[1] == 'g' && cp[2] == '-') { 4734788ca347SMarcel Moolenaar rc = xo_color_find(cp + 3); 4735788ca347SMarcel Moolenaar if (rc < 0) 4736788ca347SMarcel Moolenaar goto unknown; 4737788ca347SMarcel Moolenaar xocp->xoc_col_bg = rc; 4738788ca347SMarcel Moolenaar 4739788ca347SMarcel Moolenaar } else if (cp[0] == 'n' && cp[1] == 'o' && cp[2] == '-') { 4740788ca347SMarcel Moolenaar rc = xo_effect_find(cp + 3); 4741788ca347SMarcel Moolenaar if (rc < 0) 4742788ca347SMarcel Moolenaar goto unknown; 4743788ca347SMarcel Moolenaar xocp->xoc_effects &= ~(1 << rc); 4744788ca347SMarcel Moolenaar 4745788ca347SMarcel Moolenaar } else { 4746788ca347SMarcel Moolenaar rc = xo_effect_find(cp); 4747788ca347SMarcel Moolenaar if (rc < 0) 4748788ca347SMarcel Moolenaar goto unknown; 4749788ca347SMarcel Moolenaar xocp->xoc_effects |= 1 << rc; 4750788ca347SMarcel Moolenaar 4751788ca347SMarcel Moolenaar switch (1 << rc) { 4752788ca347SMarcel Moolenaar case XO_EFF_RESET: 4753788ca347SMarcel Moolenaar xocp->xoc_col_fg = xocp->xoc_col_bg = 0; 4754788ca347SMarcel Moolenaar /* Note: not "|=" since we want to wipe out the old value */ 4755788ca347SMarcel Moolenaar xocp->xoc_effects = XO_EFF_RESET; 4756788ca347SMarcel Moolenaar break; 4757788ca347SMarcel Moolenaar 4758788ca347SMarcel Moolenaar case XO_EFF_NORMAL: 4759788ca347SMarcel Moolenaar xocp->xoc_effects &= ~(XO_EFF_BOLD | XO_EFF_UNDERLINE 4760788ca347SMarcel Moolenaar | XO_EFF_INVERSE | XO_EFF_NORMAL); 4761788ca347SMarcel Moolenaar break; 4762788ca347SMarcel Moolenaar } 4763788ca347SMarcel Moolenaar } 4764788ca347SMarcel Moolenaar continue; 4765788ca347SMarcel Moolenaar 4766788ca347SMarcel Moolenaar unknown: 4767d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_WARN)) 4768788ca347SMarcel Moolenaar xo_failure(xop, "unknown color/effect string detected: '%s'", cp); 4769788ca347SMarcel Moolenaar } 4770788ca347SMarcel Moolenaar } 4771788ca347SMarcel Moolenaar 4772788ca347SMarcel Moolenaar static inline int 4773788ca347SMarcel Moolenaar xo_colors_enabled (xo_handle_t *xop UNUSED) 4774788ca347SMarcel Moolenaar { 4775788ca347SMarcel Moolenaar #ifdef LIBXO_TEXT_ONLY 4776788ca347SMarcel Moolenaar return 0; 4777788ca347SMarcel Moolenaar #else /* LIBXO_TEXT_ONLY */ 4778d1a0d267SMarcel Moolenaar return XOF_ISSET(xop, XOF_COLOR); 4779788ca347SMarcel Moolenaar #endif /* LIBXO_TEXT_ONLY */ 4780788ca347SMarcel Moolenaar } 4781788ca347SMarcel Moolenaar 4782f2b7bf8aSPhil Shafer /* 4783f2b7bf8aSPhil Shafer * If the color map is in use (--libxo colors=xxxx), then update 4784f2b7bf8aSPhil Shafer * the incoming foreground and background colors from the map. 4785f2b7bf8aSPhil Shafer */ 4786f2b7bf8aSPhil Shafer static void 4787f2b7bf8aSPhil Shafer xo_colors_update (xo_handle_t *xop, xo_colors_t *newp) 4788f2b7bf8aSPhil Shafer { 4789f2b7bf8aSPhil Shafer #ifdef LIBXO_TEXT_ONLY 4790f2b7bf8aSPhil Shafer return; 4791f2b7bf8aSPhil Shafer #endif /* LIBXO_TEXT_ONLY */ 4792f2b7bf8aSPhil Shafer 4793f2b7bf8aSPhil Shafer xo_color_t fg = newp->xoc_col_fg; 4794f2b7bf8aSPhil Shafer if (XOF_ISSET(xop, XOF_COLOR_MAP) && fg < XO_NUM_COLORS) 4795f2b7bf8aSPhil Shafer fg = xop->xo_color_map_fg[fg]; /* Fetch from color map */ 4796f2b7bf8aSPhil Shafer newp->xoc_col_fg = fg; 4797f2b7bf8aSPhil Shafer 4798f2b7bf8aSPhil Shafer xo_color_t bg = newp->xoc_col_bg; 4799f2b7bf8aSPhil Shafer if (XOF_ISSET(xop, XOF_COLOR_MAP) && bg < XO_NUM_COLORS) 4800f2b7bf8aSPhil Shafer bg = xop->xo_color_map_bg[bg]; /* Fetch from color map */ 4801f2b7bf8aSPhil Shafer newp->xoc_col_bg = bg; 4802f2b7bf8aSPhil Shafer } 4803f2b7bf8aSPhil Shafer 4804788ca347SMarcel Moolenaar static void 480542ff34c3SPhil Shafer xo_colors_handle_text (xo_handle_t *xop, xo_colors_t *newp) 4806788ca347SMarcel Moolenaar { 4807788ca347SMarcel Moolenaar char buf[BUFSIZ]; 4808788ca347SMarcel Moolenaar char *cp = buf, *ep = buf + sizeof(buf); 4809788ca347SMarcel Moolenaar unsigned i, bit; 4810788ca347SMarcel Moolenaar xo_colors_t *oldp = &xop->xo_colors; 481142ff34c3SPhil Shafer const char *code = NULL; 4812788ca347SMarcel Moolenaar 4813788ca347SMarcel Moolenaar /* 4814788ca347SMarcel Moolenaar * Start the buffer with an escape. We don't want to add the '[' 4815788ca347SMarcel Moolenaar * now, since we let xo_effect_text_add unconditionally add the ';'. 4816788ca347SMarcel Moolenaar * We'll replace the first ';' with a '[' when we're done. 4817788ca347SMarcel Moolenaar */ 4818788ca347SMarcel Moolenaar *cp++ = 0x1b; /* Escape */ 4819788ca347SMarcel Moolenaar 4820788ca347SMarcel Moolenaar /* 4821788ca347SMarcel Moolenaar * Terminals were designed back in the age before "certainty" was 4822788ca347SMarcel Moolenaar * invented, when standards were more what you'd call "guidelines" 4823788ca347SMarcel Moolenaar * than actual rules. Anyway we can't depend on them to operate 4824788ca347SMarcel Moolenaar * correctly. So when display attributes are changed, we punt, 4825788ca347SMarcel Moolenaar * reseting them all and turning back on the ones we want to keep. 4826788ca347SMarcel Moolenaar * Longer, but should be completely reliable. Savvy? 4827788ca347SMarcel Moolenaar */ 4828788ca347SMarcel Moolenaar if (oldp->xoc_effects != (newp->xoc_effects & oldp->xoc_effects)) { 4829788ca347SMarcel Moolenaar newp->xoc_effects |= XO_EFF_RESET; 4830788ca347SMarcel Moolenaar oldp->xoc_effects = 0; 4831788ca347SMarcel Moolenaar } 4832788ca347SMarcel Moolenaar 4833788ca347SMarcel Moolenaar for (i = 0, bit = 1; xo_effect_names[i]; i++, bit <<= 1) { 4834788ca347SMarcel Moolenaar if ((newp->xoc_effects & bit) == (oldp->xoc_effects & bit)) 4835788ca347SMarcel Moolenaar continue; 4836788ca347SMarcel Moolenaar 4837788ca347SMarcel Moolenaar code = xo_effect_on_codes[i]; 4838788ca347SMarcel Moolenaar 4839788ca347SMarcel Moolenaar cp += snprintf(cp, ep - cp, ";%s", code); 4840788ca347SMarcel Moolenaar if (cp >= ep) 4841788ca347SMarcel Moolenaar return; /* Should not occur */ 4842788ca347SMarcel Moolenaar 4843788ca347SMarcel Moolenaar if (bit == XO_EFF_RESET) { 4844788ca347SMarcel Moolenaar /* Mark up the old value so we can detect current values as new */ 4845788ca347SMarcel Moolenaar oldp->xoc_effects = 0; 4846788ca347SMarcel Moolenaar oldp->xoc_col_fg = oldp->xoc_col_bg = XO_COL_DEFAULT; 4847788ca347SMarcel Moolenaar } 4848788ca347SMarcel Moolenaar } 4849788ca347SMarcel Moolenaar 4850f2b7bf8aSPhil Shafer xo_color_t fg = newp->xoc_col_fg; 4851f2b7bf8aSPhil Shafer if (fg != oldp->xoc_col_fg) { 4852788ca347SMarcel Moolenaar cp += snprintf(cp, ep - cp, ";3%u", 4853f2b7bf8aSPhil Shafer (fg != XO_COL_DEFAULT) ? fg - 1 : 9); 4854788ca347SMarcel Moolenaar } 4855788ca347SMarcel Moolenaar 4856f2b7bf8aSPhil Shafer xo_color_t bg = newp->xoc_col_bg; 4857f2b7bf8aSPhil Shafer if (bg != oldp->xoc_col_bg) { 4858788ca347SMarcel Moolenaar cp += snprintf(cp, ep - cp, ";4%u", 4859f2b7bf8aSPhil Shafer (bg != XO_COL_DEFAULT) ? bg - 1 : 9); 4860788ca347SMarcel Moolenaar } 4861788ca347SMarcel Moolenaar 4862788ca347SMarcel Moolenaar if (cp - buf != 1 && cp < ep - 3) { 4863788ca347SMarcel Moolenaar buf[1] = '['; /* Overwrite leading ';' */ 4864788ca347SMarcel Moolenaar *cp++ = 'm'; 4865788ca347SMarcel Moolenaar *cp = '\0'; 4866788ca347SMarcel Moolenaar xo_buf_append(&xop->xo_data, buf, cp - buf); 4867788ca347SMarcel Moolenaar } 4868788ca347SMarcel Moolenaar } 4869788ca347SMarcel Moolenaar 4870788ca347SMarcel Moolenaar static void 4871788ca347SMarcel Moolenaar xo_colors_handle_html (xo_handle_t *xop, xo_colors_t *newp) 4872788ca347SMarcel Moolenaar { 4873788ca347SMarcel Moolenaar xo_colors_t *oldp = &xop->xo_colors; 4874788ca347SMarcel Moolenaar 4875788ca347SMarcel Moolenaar /* 4876788ca347SMarcel Moolenaar * HTML colors are mostly trivial: fill in xo_color_buf with 4877788ca347SMarcel Moolenaar * a set of class tags representing the colors and effects. 4878788ca347SMarcel Moolenaar */ 4879788ca347SMarcel Moolenaar 4880788ca347SMarcel Moolenaar /* If nothing changed, then do nothing */ 4881788ca347SMarcel Moolenaar if (oldp->xoc_effects == newp->xoc_effects 4882788ca347SMarcel Moolenaar && oldp->xoc_col_fg == newp->xoc_col_fg 4883788ca347SMarcel Moolenaar && oldp->xoc_col_bg == newp->xoc_col_bg) 4884788ca347SMarcel Moolenaar return; 4885788ca347SMarcel Moolenaar 4886788ca347SMarcel Moolenaar unsigned i, bit; 4887788ca347SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_color_buf; 4888788ca347SMarcel Moolenaar 4889788ca347SMarcel Moolenaar xo_buf_reset(xbp); /* We rebuild content after each change */ 4890788ca347SMarcel Moolenaar 4891788ca347SMarcel Moolenaar for (i = 0, bit = 1; xo_effect_names[i]; i++, bit <<= 1) { 4892788ca347SMarcel Moolenaar if (!(newp->xoc_effects & bit)) 4893788ca347SMarcel Moolenaar continue; 4894788ca347SMarcel Moolenaar 4895788ca347SMarcel Moolenaar xo_buf_append_str(xbp, " effect-"); 4896788ca347SMarcel Moolenaar xo_buf_append_str(xbp, xo_effect_names[i]); 4897788ca347SMarcel Moolenaar } 4898788ca347SMarcel Moolenaar 4899788ca347SMarcel Moolenaar const char *fg = NULL; 4900788ca347SMarcel Moolenaar const char *bg = NULL; 4901788ca347SMarcel Moolenaar 4902788ca347SMarcel Moolenaar if (newp->xoc_col_fg != XO_COL_DEFAULT) 4903788ca347SMarcel Moolenaar fg = xo_color_names[newp->xoc_col_fg]; 4904788ca347SMarcel Moolenaar if (newp->xoc_col_bg != XO_COL_DEFAULT) 4905788ca347SMarcel Moolenaar bg = xo_color_names[newp->xoc_col_bg]; 4906788ca347SMarcel Moolenaar 4907788ca347SMarcel Moolenaar if (newp->xoc_effects & XO_EFF_INVERSE) { 4908788ca347SMarcel Moolenaar const char *tmp = fg; 4909788ca347SMarcel Moolenaar fg = bg; 4910788ca347SMarcel Moolenaar bg = tmp; 4911788ca347SMarcel Moolenaar if (fg == NULL) 4912788ca347SMarcel Moolenaar fg = "inverse"; 4913788ca347SMarcel Moolenaar if (bg == NULL) 4914788ca347SMarcel Moolenaar bg = "inverse"; 4915788ca347SMarcel Moolenaar 4916788ca347SMarcel Moolenaar } 4917788ca347SMarcel Moolenaar 4918788ca347SMarcel Moolenaar if (fg) { 4919788ca347SMarcel Moolenaar xo_buf_append_str(xbp, " color-fg-"); 4920788ca347SMarcel Moolenaar xo_buf_append_str(xbp, fg); 4921788ca347SMarcel Moolenaar } 4922788ca347SMarcel Moolenaar 4923788ca347SMarcel Moolenaar if (bg) { 4924788ca347SMarcel Moolenaar xo_buf_append_str(xbp, " color-bg-"); 4925788ca347SMarcel Moolenaar xo_buf_append_str(xbp, bg); 4926788ca347SMarcel Moolenaar } 4927788ca347SMarcel Moolenaar } 4928788ca347SMarcel Moolenaar 4929788ca347SMarcel Moolenaar static void 493042ff34c3SPhil Shafer xo_format_colors (xo_handle_t *xop, xo_field_info_t *xfip, 4931264104f2SPhil Shafer const char *value, ssize_t vlen) 4932788ca347SMarcel Moolenaar { 4933d1a0d267SMarcel Moolenaar const char *fmt = xfip->xfi_format; 49348a6eceffSPhil Shafer ssize_t flen = xfip->xfi_flen; 4935d1a0d267SMarcel Moolenaar 4936788ca347SMarcel Moolenaar xo_buffer_t xb; 4937788ca347SMarcel Moolenaar 4938788ca347SMarcel Moolenaar /* If the string is static and we've in an encoding style, bail */ 4939264104f2SPhil Shafer if (vlen != 0 && xo_style_is_encoding(xop)) 4940788ca347SMarcel Moolenaar return; 4941788ca347SMarcel Moolenaar 4942788ca347SMarcel Moolenaar xo_buf_init(&xb); 4943788ca347SMarcel Moolenaar 4944264104f2SPhil Shafer if (vlen) 4945264104f2SPhil Shafer xo_buf_append(&xb, value, vlen); 4946788ca347SMarcel Moolenaar else if (flen) 4947d1a0d267SMarcel Moolenaar xo_do_format_field(xop, &xb, fmt, flen, 0); 4948788ca347SMarcel Moolenaar else 4949788ca347SMarcel Moolenaar xo_buf_append(&xb, "reset", 6); /* Default if empty */ 4950788ca347SMarcel Moolenaar 4951788ca347SMarcel Moolenaar if (xo_colors_enabled(xop)) { 4952788ca347SMarcel Moolenaar switch (xo_style(xop)) { 4953788ca347SMarcel Moolenaar case XO_STYLE_TEXT: 4954788ca347SMarcel Moolenaar case XO_STYLE_HTML: 4955788ca347SMarcel Moolenaar xo_buf_append(&xb, "", 1); 4956788ca347SMarcel Moolenaar 4957788ca347SMarcel Moolenaar xo_colors_t xoc = xop->xo_colors; 4958788ca347SMarcel Moolenaar xo_colors_parse(xop, &xoc, xb.xb_bufp); 4959f2b7bf8aSPhil Shafer xo_colors_update(xop, &xoc); 4960788ca347SMarcel Moolenaar 4961788ca347SMarcel Moolenaar if (xo_style(xop) == XO_STYLE_TEXT) { 4962788ca347SMarcel Moolenaar /* 4963788ca347SMarcel Moolenaar * Text mode means emitting the colors as ANSI character 4964788ca347SMarcel Moolenaar * codes. This will allow people who like colors to have 4965788ca347SMarcel Moolenaar * colors. The issue is, of course conflicting with the 4966788ca347SMarcel Moolenaar * user's perfectly reasonable color scheme. Which leads 4967788ca347SMarcel Moolenaar * to the hell of LSCOLORS, where even app need to have 4968788ca347SMarcel Moolenaar * customization hooks for adjusting colors. Instead we 4969788ca347SMarcel Moolenaar * provide a simpler-but-still-annoying answer where one 4970788ca347SMarcel Moolenaar * can map colors to other colors. 4971788ca347SMarcel Moolenaar */ 4972788ca347SMarcel Moolenaar xo_colors_handle_text(xop, &xoc); 4973788ca347SMarcel Moolenaar xoc.xoc_effects &= ~XO_EFF_RESET; /* After handling it */ 4974788ca347SMarcel Moolenaar 4975788ca347SMarcel Moolenaar } else { 4976788ca347SMarcel Moolenaar /* 4977788ca347SMarcel Moolenaar * HTML output is wrapped in divs, so the color information 4978788ca347SMarcel Moolenaar * must appear in every div until cleared. Most pathetic. 4979788ca347SMarcel Moolenaar * Most unavoidable. 4980788ca347SMarcel Moolenaar */ 4981788ca347SMarcel Moolenaar xoc.xoc_effects &= ~XO_EFF_RESET; /* Before handling effects */ 4982788ca347SMarcel Moolenaar xo_colors_handle_html(xop, &xoc); 4983788ca347SMarcel Moolenaar } 4984788ca347SMarcel Moolenaar 4985788ca347SMarcel Moolenaar xop->xo_colors = xoc; 4986788ca347SMarcel Moolenaar break; 4987788ca347SMarcel Moolenaar 4988788ca347SMarcel Moolenaar case XO_STYLE_XML: 4989788ca347SMarcel Moolenaar case XO_STYLE_JSON: 4990d1a0d267SMarcel Moolenaar case XO_STYLE_SDPARAMS: 4991d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER: 4992788ca347SMarcel Moolenaar /* 4993788ca347SMarcel Moolenaar * Nothing to do; we did all that work just to clear the stack of 4994788ca347SMarcel Moolenaar * formatting arguments. 4995788ca347SMarcel Moolenaar */ 4996788ca347SMarcel Moolenaar break; 4997788ca347SMarcel Moolenaar } 4998788ca347SMarcel Moolenaar } 4999788ca347SMarcel Moolenaar 5000788ca347SMarcel Moolenaar xo_buf_cleanup(&xb); 5001788ca347SMarcel Moolenaar } 5002788ca347SMarcel Moolenaar 500331337658SMarcel Moolenaar static void 500442ff34c3SPhil Shafer xo_format_units (xo_handle_t *xop, xo_field_info_t *xfip, 5005264104f2SPhil Shafer const char *value, ssize_t vlen) 500631337658SMarcel Moolenaar { 5007d1a0d267SMarcel Moolenaar const char *fmt = xfip->xfi_format; 50088a6eceffSPhil Shafer ssize_t flen = xfip->xfi_flen; 5009d1a0d267SMarcel Moolenaar xo_xff_flags_t flags = xfip->xfi_flags; 5010d1a0d267SMarcel Moolenaar 501131337658SMarcel Moolenaar static char units_start_xml[] = " units=\""; 501231337658SMarcel Moolenaar static char units_start_html[] = " data-units=\""; 501331337658SMarcel Moolenaar 5014d1a0d267SMarcel Moolenaar if (!XOIF_ISSET(xop, XOIF_UNITS_PENDING)) { 5015264104f2SPhil Shafer xo_format_content(xop, "units", NULL, value, vlen, fmt, flen, flags); 501631337658SMarcel Moolenaar return; 501731337658SMarcel Moolenaar } 501831337658SMarcel Moolenaar 501931337658SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_data; 50208a6eceffSPhil Shafer ssize_t start = xop->xo_units_offset; 50218a6eceffSPhil Shafer ssize_t stop = xbp->xb_curp - xbp->xb_bufp; 502231337658SMarcel Moolenaar 5023788ca347SMarcel Moolenaar if (xo_style(xop) == XO_STYLE_XML) 502431337658SMarcel Moolenaar xo_buf_append(xbp, units_start_xml, sizeof(units_start_xml) - 1); 5025788ca347SMarcel Moolenaar else if (xo_style(xop) == XO_STYLE_HTML) 502631337658SMarcel Moolenaar xo_buf_append(xbp, units_start_html, sizeof(units_start_html) - 1); 502731337658SMarcel Moolenaar else 502831337658SMarcel Moolenaar return; 502931337658SMarcel Moolenaar 5030264104f2SPhil Shafer if (vlen) 5031264104f2SPhil Shafer xo_data_escape(xop, value, vlen); 503231337658SMarcel Moolenaar else 5033d1a0d267SMarcel Moolenaar xo_do_format_field(xop, NULL, fmt, flen, flags); 503431337658SMarcel Moolenaar 503531337658SMarcel Moolenaar xo_buf_append(xbp, "\"", 1); 503631337658SMarcel Moolenaar 50378a6eceffSPhil Shafer ssize_t now = xbp->xb_curp - xbp->xb_bufp; 50388a6eceffSPhil Shafer ssize_t delta = now - stop; 5039d1a0d267SMarcel Moolenaar if (delta <= 0) { /* Strange; no output to move */ 504031337658SMarcel Moolenaar xbp->xb_curp = xbp->xb_bufp + stop; /* Reset buffer to prior state */ 504131337658SMarcel Moolenaar return; 504231337658SMarcel Moolenaar } 504331337658SMarcel Moolenaar 504431337658SMarcel Moolenaar /* 504531337658SMarcel Moolenaar * Now we're in it alright. We've need to insert the unit value 504631337658SMarcel Moolenaar * we just created into the right spot. We make a local copy, 504731337658SMarcel Moolenaar * move it and then insert our copy. We know there's room in the 504831337658SMarcel Moolenaar * buffer, since we're just moving this around. 504931337658SMarcel Moolenaar */ 505031337658SMarcel Moolenaar char *buf = alloca(delta); 505131337658SMarcel Moolenaar 505231337658SMarcel Moolenaar memcpy(buf, xbp->xb_bufp + stop, delta); 505331337658SMarcel Moolenaar memmove(xbp->xb_bufp + start + delta, xbp->xb_bufp + start, stop - start); 505431337658SMarcel Moolenaar memmove(xbp->xb_bufp + start, buf, delta); 505531337658SMarcel Moolenaar } 505631337658SMarcel Moolenaar 50578a6eceffSPhil Shafer static ssize_t 505842ff34c3SPhil Shafer xo_find_width (xo_handle_t *xop, xo_field_info_t *xfip, 5059264104f2SPhil Shafer const char *value, ssize_t vlen) 506031337658SMarcel Moolenaar { 5061d1a0d267SMarcel Moolenaar const char *fmt = xfip->xfi_format; 50628a6eceffSPhil Shafer ssize_t flen = xfip->xfi_flen; 5063d1a0d267SMarcel Moolenaar 506431337658SMarcel Moolenaar long width = 0; 506531337658SMarcel Moolenaar char *bp; 506631337658SMarcel Moolenaar char *cp; 506731337658SMarcel Moolenaar 5068264104f2SPhil Shafer if (vlen) { 5069264104f2SPhil Shafer bp = alloca(vlen + 1); /* Make local NUL-terminated copy of value */ 5070264104f2SPhil Shafer memcpy(bp, value, vlen); 5071264104f2SPhil Shafer bp[vlen] = '\0'; 507231337658SMarcel Moolenaar 507331337658SMarcel Moolenaar width = strtol(bp, &cp, 0); 507431337658SMarcel Moolenaar if (width == LONG_MIN || width == LONG_MAX 507531337658SMarcel Moolenaar || bp == cp || *cp != '\0' ) { 507631337658SMarcel Moolenaar width = 0; 507731337658SMarcel Moolenaar xo_failure(xop, "invalid width for anchor: '%s'", bp); 507831337658SMarcel Moolenaar } 507931337658SMarcel Moolenaar } else if (flen) { 508031337658SMarcel Moolenaar if (flen != 2 || strncmp("%d", fmt, flen) != 0) 508131337658SMarcel Moolenaar xo_failure(xop, "invalid width format: '%*.*s'", flen, flen, fmt); 5082d1a0d267SMarcel Moolenaar if (!XOF_ISSET(xop, XOF_NO_VA_ARG)) 508331337658SMarcel Moolenaar width = va_arg(xop->xo_vap, int); 508431337658SMarcel Moolenaar } 508531337658SMarcel Moolenaar 508631337658SMarcel Moolenaar return width; 508731337658SMarcel Moolenaar } 508831337658SMarcel Moolenaar 508931337658SMarcel Moolenaar static void 509031337658SMarcel Moolenaar xo_anchor_clear (xo_handle_t *xop) 509131337658SMarcel Moolenaar { 5092d1a0d267SMarcel Moolenaar XOIF_CLEAR(xop, XOIF_ANCHOR); 509331337658SMarcel Moolenaar xop->xo_anchor_offset = 0; 509431337658SMarcel Moolenaar xop->xo_anchor_columns = 0; 509531337658SMarcel Moolenaar xop->xo_anchor_min_width = 0; 509631337658SMarcel Moolenaar } 509731337658SMarcel Moolenaar 509831337658SMarcel Moolenaar /* 509931337658SMarcel Moolenaar * An anchor is a marker used to delay field width implications. 510031337658SMarcel Moolenaar * Imagine the format string "{[:10}{min:%d}/{cur:%d}/{max:%d}{:]}". 510131337658SMarcel Moolenaar * We are looking for output like " 1/4/5" 510231337658SMarcel Moolenaar * 510331337658SMarcel Moolenaar * To make this work, we record the anchor and then return to 510431337658SMarcel Moolenaar * format it when the end anchor tag is seen. 510531337658SMarcel Moolenaar */ 510631337658SMarcel Moolenaar static void 510742ff34c3SPhil Shafer xo_anchor_start (xo_handle_t *xop, xo_field_info_t *xfip, 5108264104f2SPhil Shafer const char *value, ssize_t vlen) 510931337658SMarcel Moolenaar { 5110788ca347SMarcel Moolenaar if (xo_style(xop) != XO_STYLE_TEXT && xo_style(xop) != XO_STYLE_HTML) 511131337658SMarcel Moolenaar return; 511231337658SMarcel Moolenaar 5113d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_ANCHOR)) 511431337658SMarcel Moolenaar xo_failure(xop, "the anchor already recording is discarded"); 511531337658SMarcel Moolenaar 5116d1a0d267SMarcel Moolenaar XOIF_SET(xop, XOIF_ANCHOR); 511731337658SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_data; 511831337658SMarcel Moolenaar xop->xo_anchor_offset = xbp->xb_curp - xbp->xb_bufp; 511931337658SMarcel Moolenaar xop->xo_anchor_columns = 0; 512031337658SMarcel Moolenaar 512131337658SMarcel Moolenaar /* 512231337658SMarcel Moolenaar * Now we find the width, if possible. If it's not there, 512331337658SMarcel Moolenaar * we'll get it on the end anchor. 512431337658SMarcel Moolenaar */ 5125264104f2SPhil Shafer xop->xo_anchor_min_width = xo_find_width(xop, xfip, value, vlen); 512631337658SMarcel Moolenaar } 512731337658SMarcel Moolenaar 512831337658SMarcel Moolenaar static void 512942ff34c3SPhil Shafer xo_anchor_stop (xo_handle_t *xop, xo_field_info_t *xfip, 5130264104f2SPhil Shafer const char *value, ssize_t vlen) 513131337658SMarcel Moolenaar { 5132788ca347SMarcel Moolenaar if (xo_style(xop) != XO_STYLE_TEXT && xo_style(xop) != XO_STYLE_HTML) 513331337658SMarcel Moolenaar return; 513431337658SMarcel Moolenaar 5135d1a0d267SMarcel Moolenaar if (!XOIF_ISSET(xop, XOIF_ANCHOR)) { 513631337658SMarcel Moolenaar xo_failure(xop, "no start anchor"); 513731337658SMarcel Moolenaar return; 513831337658SMarcel Moolenaar } 513931337658SMarcel Moolenaar 5140d1a0d267SMarcel Moolenaar XOIF_CLEAR(xop, XOIF_UNITS_PENDING); 514131337658SMarcel Moolenaar 5142264104f2SPhil Shafer ssize_t width = xo_find_width(xop, xfip, value, vlen); 514331337658SMarcel Moolenaar if (width == 0) 514431337658SMarcel Moolenaar width = xop->xo_anchor_min_width; 514531337658SMarcel Moolenaar 514631337658SMarcel Moolenaar if (width == 0) /* No width given; nothing to do */ 514731337658SMarcel Moolenaar goto done; 514831337658SMarcel Moolenaar 514931337658SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_data; 51508a6eceffSPhil Shafer ssize_t start = xop->xo_anchor_offset; 51518a6eceffSPhil Shafer ssize_t stop = xbp->xb_curp - xbp->xb_bufp; 51528a6eceffSPhil Shafer ssize_t abswidth = (width > 0) ? width : -width; 51538a6eceffSPhil Shafer ssize_t blen = abswidth - xop->xo_anchor_columns; 515431337658SMarcel Moolenaar 515531337658SMarcel Moolenaar if (blen <= 0) /* Already over width */ 515631337658SMarcel Moolenaar goto done; 515731337658SMarcel Moolenaar 515831337658SMarcel Moolenaar if (abswidth > XO_MAX_ANCHOR_WIDTH) { 515931337658SMarcel Moolenaar xo_failure(xop, "width over %u are not supported", 516031337658SMarcel Moolenaar XO_MAX_ANCHOR_WIDTH); 516131337658SMarcel Moolenaar goto done; 516231337658SMarcel Moolenaar } 516331337658SMarcel Moolenaar 516431337658SMarcel Moolenaar /* Make a suitable padding field and emit it */ 516531337658SMarcel Moolenaar char *buf = alloca(blen); 516631337658SMarcel Moolenaar memset(buf, ' ', blen); 5167d1a0d267SMarcel Moolenaar xo_format_content(xop, "padding", NULL, buf, blen, NULL, 0, 0); 516831337658SMarcel Moolenaar 516931337658SMarcel Moolenaar if (width < 0) /* Already left justified */ 517031337658SMarcel Moolenaar goto done; 517131337658SMarcel Moolenaar 51728a6eceffSPhil Shafer ssize_t now = xbp->xb_curp - xbp->xb_bufp; 51738a6eceffSPhil Shafer ssize_t delta = now - stop; 5174d1a0d267SMarcel Moolenaar if (delta <= 0) /* Strange; no output to move */ 517531337658SMarcel Moolenaar goto done; 517631337658SMarcel Moolenaar 517731337658SMarcel Moolenaar /* 517831337658SMarcel Moolenaar * Now we're in it alright. We've need to insert the padding data 517931337658SMarcel Moolenaar * we just created (which might be an HTML <div> or text) before 518031337658SMarcel Moolenaar * the formatted data. We make a local copy, move it and then 518131337658SMarcel Moolenaar * insert our copy. We know there's room in the buffer, since 518231337658SMarcel Moolenaar * we're just moving this around. 518331337658SMarcel Moolenaar */ 518431337658SMarcel Moolenaar if (delta > blen) 518531337658SMarcel Moolenaar buf = alloca(delta); /* Expand buffer if needed */ 518631337658SMarcel Moolenaar 518731337658SMarcel Moolenaar memcpy(buf, xbp->xb_bufp + stop, delta); 518831337658SMarcel Moolenaar memmove(xbp->xb_bufp + start + delta, xbp->xb_bufp + start, stop - start); 518931337658SMarcel Moolenaar memmove(xbp->xb_bufp + start, buf, delta); 519031337658SMarcel Moolenaar 519131337658SMarcel Moolenaar done: 519231337658SMarcel Moolenaar xo_anchor_clear(xop); 519331337658SMarcel Moolenaar } 519431337658SMarcel Moolenaar 5195d1a0d267SMarcel Moolenaar static const char * 5196d1a0d267SMarcel Moolenaar xo_class_name (int ftype) 519731337658SMarcel Moolenaar { 5198d1a0d267SMarcel Moolenaar switch (ftype) { 5199d1a0d267SMarcel Moolenaar case 'D': return "decoration"; 5200d1a0d267SMarcel Moolenaar case 'E': return "error"; 5201d1a0d267SMarcel Moolenaar case 'L': return "label"; 5202d1a0d267SMarcel Moolenaar case 'N': return "note"; 5203d1a0d267SMarcel Moolenaar case 'P': return "padding"; 5204d1a0d267SMarcel Moolenaar case 'W': return "warning"; 520531337658SMarcel Moolenaar } 520631337658SMarcel Moolenaar 5207d1a0d267SMarcel Moolenaar return NULL; 520831337658SMarcel Moolenaar } 520931337658SMarcel Moolenaar 5210d1a0d267SMarcel Moolenaar static const char * 5211d1a0d267SMarcel Moolenaar xo_tag_name (int ftype) 5212d1a0d267SMarcel Moolenaar { 5213d1a0d267SMarcel Moolenaar switch (ftype) { 5214d1a0d267SMarcel Moolenaar case 'E': return "__error"; 5215d1a0d267SMarcel Moolenaar case 'W': return "__warning"; 5216d1a0d267SMarcel Moolenaar } 5217d1a0d267SMarcel Moolenaar 5218d1a0d267SMarcel Moolenaar return NULL; 5219d1a0d267SMarcel Moolenaar } 5220d1a0d267SMarcel Moolenaar 5221d1a0d267SMarcel Moolenaar static int 5222d1a0d267SMarcel Moolenaar xo_role_wants_default_format (int ftype) 5223d1a0d267SMarcel Moolenaar { 5224d1a0d267SMarcel Moolenaar switch (ftype) { 5225d1a0d267SMarcel Moolenaar /* These roles can be completely empty and/or without formatting */ 5226d1a0d267SMarcel Moolenaar case 'C': 5227d1a0d267SMarcel Moolenaar case 'G': 5228d1a0d267SMarcel Moolenaar case '[': 5229d1a0d267SMarcel Moolenaar case ']': 5230d1a0d267SMarcel Moolenaar return 0; 5231d1a0d267SMarcel Moolenaar } 5232d1a0d267SMarcel Moolenaar 5233d1a0d267SMarcel Moolenaar return 1; 5234d1a0d267SMarcel Moolenaar } 5235d1a0d267SMarcel Moolenaar 5236d1a0d267SMarcel Moolenaar static xo_mapping_t xo_role_names[] = { 5237d1a0d267SMarcel Moolenaar { 'C', "color" }, 5238d1a0d267SMarcel Moolenaar { 'D', "decoration" }, 5239d1a0d267SMarcel Moolenaar { 'E', "error" }, 5240d1a0d267SMarcel Moolenaar { 'L', "label" }, 5241d1a0d267SMarcel Moolenaar { 'N', "note" }, 5242d1a0d267SMarcel Moolenaar { 'P', "padding" }, 5243d1a0d267SMarcel Moolenaar { 'T', "title" }, 5244d1a0d267SMarcel Moolenaar { 'U', "units" }, 5245d1a0d267SMarcel Moolenaar { 'V', "value" }, 5246d1a0d267SMarcel Moolenaar { 'W', "warning" }, 5247d1a0d267SMarcel Moolenaar { '[', "start-anchor" }, 5248d1a0d267SMarcel Moolenaar { ']', "stop-anchor" }, 5249d1a0d267SMarcel Moolenaar { 0, NULL } 5250d1a0d267SMarcel Moolenaar }; 5251d1a0d267SMarcel Moolenaar 5252d1a0d267SMarcel Moolenaar #define XO_ROLE_EBRACE '{' /* Escaped braces */ 5253d1a0d267SMarcel Moolenaar #define XO_ROLE_TEXT '+' 5254d1a0d267SMarcel Moolenaar #define XO_ROLE_NEWLINE '\n' 5255d1a0d267SMarcel Moolenaar 5256d1a0d267SMarcel Moolenaar static xo_mapping_t xo_modifier_names[] = { 525742ff34c3SPhil Shafer { XFF_ARGUMENT, "argument" }, 5258d1a0d267SMarcel Moolenaar { XFF_COLON, "colon" }, 5259d1a0d267SMarcel Moolenaar { XFF_COMMA, "comma" }, 5260d1a0d267SMarcel Moolenaar { XFF_DISPLAY_ONLY, "display" }, 5261d1a0d267SMarcel Moolenaar { XFF_ENCODE_ONLY, "encoding" }, 5262d1a0d267SMarcel Moolenaar { XFF_GT_FIELD, "gettext" }, 5263d1a0d267SMarcel Moolenaar { XFF_HUMANIZE, "humanize" }, 5264d1a0d267SMarcel Moolenaar { XFF_HUMANIZE, "hn" }, 5265d1a0d267SMarcel Moolenaar { XFF_HN_SPACE, "hn-space" }, 5266d1a0d267SMarcel Moolenaar { XFF_HN_DECIMAL, "hn-decimal" }, 5267d1a0d267SMarcel Moolenaar { XFF_HN_1000, "hn-1000" }, 5268d1a0d267SMarcel Moolenaar { XFF_KEY, "key" }, 5269d1a0d267SMarcel Moolenaar { XFF_LEAF_LIST, "leaf-list" }, 5270d1a0d267SMarcel Moolenaar { XFF_LEAF_LIST, "list" }, 5271d1a0d267SMarcel Moolenaar { XFF_NOQUOTE, "no-quotes" }, 5272d1a0d267SMarcel Moolenaar { XFF_NOQUOTE, "no-quote" }, 5273d1a0d267SMarcel Moolenaar { XFF_GT_PLURAL, "plural" }, 5274d1a0d267SMarcel Moolenaar { XFF_QUOTE, "quotes" }, 5275d1a0d267SMarcel Moolenaar { XFF_QUOTE, "quote" }, 5276d1a0d267SMarcel Moolenaar { XFF_TRIM_WS, "trim" }, 5277d1a0d267SMarcel Moolenaar { XFF_WS, "white" }, 5278d1a0d267SMarcel Moolenaar { 0, NULL } 5279d1a0d267SMarcel Moolenaar }; 5280d1a0d267SMarcel Moolenaar 5281d1a0d267SMarcel Moolenaar #ifdef NOT_NEEDED_YET 5282d1a0d267SMarcel Moolenaar static xo_mapping_t xo_modifier_short_names[] = { 5283d1a0d267SMarcel Moolenaar { XFF_COLON, "c" }, 5284d1a0d267SMarcel Moolenaar { XFF_DISPLAY_ONLY, "d" }, 5285d1a0d267SMarcel Moolenaar { XFF_ENCODE_ONLY, "e" }, 5286d1a0d267SMarcel Moolenaar { XFF_GT_FIELD, "g" }, 5287d1a0d267SMarcel Moolenaar { XFF_HUMANIZE, "h" }, 5288d1a0d267SMarcel Moolenaar { XFF_KEY, "k" }, 5289d1a0d267SMarcel Moolenaar { XFF_LEAF_LIST, "l" }, 5290d1a0d267SMarcel Moolenaar { XFF_NOQUOTE, "n" }, 5291d1a0d267SMarcel Moolenaar { XFF_GT_PLURAL, "p" }, 5292d1a0d267SMarcel Moolenaar { XFF_QUOTE, "q" }, 5293d1a0d267SMarcel Moolenaar { XFF_TRIM_WS, "t" }, 5294d1a0d267SMarcel Moolenaar { XFF_WS, "w" }, 5295d1a0d267SMarcel Moolenaar { 0, NULL } 5296d1a0d267SMarcel Moolenaar }; 5297d1a0d267SMarcel Moolenaar #endif /* NOT_NEEDED_YET */ 5298d1a0d267SMarcel Moolenaar 5299d1a0d267SMarcel Moolenaar static int 5300d1a0d267SMarcel Moolenaar xo_count_fields (xo_handle_t *xop UNUSED, const char *fmt) 5301d1a0d267SMarcel Moolenaar { 5302d1a0d267SMarcel Moolenaar int rc = 1; 5303d1a0d267SMarcel Moolenaar const char *cp; 5304d1a0d267SMarcel Moolenaar 5305d1a0d267SMarcel Moolenaar for (cp = fmt; *cp; cp++) 5306d1a0d267SMarcel Moolenaar if (*cp == '{' || *cp == '\n') 5307d1a0d267SMarcel Moolenaar rc += 1; 5308d1a0d267SMarcel Moolenaar 5309d1a0d267SMarcel Moolenaar return rc * 2 + 1; 5310d1a0d267SMarcel Moolenaar } 531131337658SMarcel Moolenaar 531231337658SMarcel Moolenaar /* 5313d1a0d267SMarcel Moolenaar * The field format is: 531431337658SMarcel Moolenaar * '{' modifiers ':' content [ '/' print-fmt [ '/' encode-fmt ]] '}' 5315d1a0d267SMarcel Moolenaar * Roles are optional and include the following field types: 531631337658SMarcel Moolenaar * 'D': decoration; something non-text and non-data (colons, commmas) 531731337658SMarcel Moolenaar * 'E': error message 5318d1a0d267SMarcel Moolenaar * 'G': gettext() the entire string; optional domainname as content 531931337658SMarcel Moolenaar * 'L': label; text preceding data 532031337658SMarcel Moolenaar * 'N': note; text following data 532131337658SMarcel Moolenaar * 'P': padding; whitespace 532231337658SMarcel Moolenaar * 'T': Title, where 'content' is a column title 532331337658SMarcel Moolenaar * 'U': Units, where 'content' is the unit label 532431337658SMarcel Moolenaar * 'V': value, where 'content' is the name of the field (the default) 532531337658SMarcel Moolenaar * 'W': warning message 532631337658SMarcel Moolenaar * '[': start a section of anchored text 532731337658SMarcel Moolenaar * ']': end a section of anchored text 5328d1a0d267SMarcel Moolenaar * The following modifiers are also supported: 532942ff34c3SPhil Shafer * 'a': content is provided via argument (const char *), not descriptor 533031337658SMarcel Moolenaar * 'c': flag: emit a colon after the label 5331d1a0d267SMarcel Moolenaar * 'd': field is only emitted for display styles (text and html) 5332d1a0d267SMarcel Moolenaar * 'e': field is only emitted for encoding styles (xml and json) 5333d1a0d267SMarcel Moolenaar * 'g': gettext() the field 5334d1a0d267SMarcel Moolenaar * 'h': humanize a numeric value (only for display styles) 533531337658SMarcel Moolenaar * 'k': this field is a key, suitable for XPath predicates 533631337658SMarcel Moolenaar * 'l': a leaf-list, a simple list of values 533731337658SMarcel Moolenaar * 'n': no quotes around this field 5338d1a0d267SMarcel Moolenaar * 'p': the field has plural gettext semantics (ngettext) 533931337658SMarcel Moolenaar * 'q': add quotes around this field 534031337658SMarcel Moolenaar * 't': trim whitespace around the value 534131337658SMarcel Moolenaar * 'w': emit a blank after the label 534231337658SMarcel Moolenaar * The print-fmt and encode-fmt strings is the printf-style formating 534331337658SMarcel Moolenaar * for this data. JSON and XML will use the encoding-fmt, if present. 534431337658SMarcel Moolenaar * If the encode-fmt is not provided, it defaults to the print-fmt. 534531337658SMarcel Moolenaar * If the print-fmt is not provided, it defaults to 's'. 534631337658SMarcel Moolenaar */ 5347d1a0d267SMarcel Moolenaar static const char * 5348d1a0d267SMarcel Moolenaar xo_parse_roles (xo_handle_t *xop, const char *fmt, 5349d1a0d267SMarcel Moolenaar const char *basep, xo_field_info_t *xfip) 5350d1a0d267SMarcel Moolenaar { 5351d1a0d267SMarcel Moolenaar const char *sp; 5352d1a0d267SMarcel Moolenaar unsigned ftype = 0; 5353d1a0d267SMarcel Moolenaar xo_xff_flags_t flags = 0; 5354d1a0d267SMarcel Moolenaar uint8_t fnum = 0; 535531337658SMarcel Moolenaar 535642ff34c3SPhil Shafer for (sp = basep; sp && *sp; sp++) { 535731337658SMarcel Moolenaar if (*sp == ':' || *sp == '/' || *sp == '}') 535831337658SMarcel Moolenaar break; 535931337658SMarcel Moolenaar 536031337658SMarcel Moolenaar if (*sp == '\\') { 536131337658SMarcel Moolenaar if (sp[1] == '\0') { 536231337658SMarcel Moolenaar xo_failure(xop, "backslash at the end of string"); 5363d1a0d267SMarcel Moolenaar return NULL; 536431337658SMarcel Moolenaar } 5365d1a0d267SMarcel Moolenaar 5366d1a0d267SMarcel Moolenaar /* Anything backslashed is ignored */ 536731337658SMarcel Moolenaar sp += 1; 536831337658SMarcel Moolenaar continue; 536931337658SMarcel Moolenaar } 537031337658SMarcel Moolenaar 5371d1a0d267SMarcel Moolenaar if (*sp == ',') { 5372d1a0d267SMarcel Moolenaar const char *np; 5373d1a0d267SMarcel Moolenaar for (np = ++sp; *np; np++) 5374d1a0d267SMarcel Moolenaar if (*np == ':' || *np == '/' || *np == '}' || *np == ',') 5375d1a0d267SMarcel Moolenaar break; 5376d1a0d267SMarcel Moolenaar 53778a6eceffSPhil Shafer ssize_t slen = np - sp; 5378d1a0d267SMarcel Moolenaar if (slen > 0) { 5379d1a0d267SMarcel Moolenaar xo_xff_flags_t value; 5380d1a0d267SMarcel Moolenaar 5381d1a0d267SMarcel Moolenaar value = xo_name_lookup(xo_role_names, sp, slen); 5382d1a0d267SMarcel Moolenaar if (value) 5383d1a0d267SMarcel Moolenaar ftype = value; 5384d1a0d267SMarcel Moolenaar else { 5385d1a0d267SMarcel Moolenaar value = xo_name_lookup(xo_modifier_names, sp, slen); 5386d1a0d267SMarcel Moolenaar if (value) 5387d1a0d267SMarcel Moolenaar flags |= value; 5388d1a0d267SMarcel Moolenaar else 5389d1a0d267SMarcel Moolenaar xo_failure(xop, "unknown keyword ignored: '%.*s'", 5390d1a0d267SMarcel Moolenaar slen, sp); 5391d1a0d267SMarcel Moolenaar } 5392d1a0d267SMarcel Moolenaar } 5393d1a0d267SMarcel Moolenaar 5394d1a0d267SMarcel Moolenaar sp = np - 1; 5395d1a0d267SMarcel Moolenaar continue; 5396d1a0d267SMarcel Moolenaar } 5397d1a0d267SMarcel Moolenaar 539831337658SMarcel Moolenaar switch (*sp) { 5399788ca347SMarcel Moolenaar case 'C': 540031337658SMarcel Moolenaar case 'D': 540131337658SMarcel Moolenaar case 'E': 5402d1a0d267SMarcel Moolenaar case 'G': 540331337658SMarcel Moolenaar case 'L': 540431337658SMarcel Moolenaar case 'N': 540531337658SMarcel Moolenaar case 'P': 540631337658SMarcel Moolenaar case 'T': 540731337658SMarcel Moolenaar case 'U': 540831337658SMarcel Moolenaar case 'V': 540931337658SMarcel Moolenaar case 'W': 541031337658SMarcel Moolenaar case '[': 541131337658SMarcel Moolenaar case ']': 541231337658SMarcel Moolenaar if (ftype != 0) { 5413d1a0d267SMarcel Moolenaar xo_failure(xop, "field descriptor uses multiple types: '%s'", 5414d1a0d267SMarcel Moolenaar xo_printable(fmt)); 5415d1a0d267SMarcel Moolenaar return NULL; 541631337658SMarcel Moolenaar } 541731337658SMarcel Moolenaar ftype = *sp; 541831337658SMarcel Moolenaar break; 541931337658SMarcel Moolenaar 5420d1a0d267SMarcel Moolenaar case '0': 5421d1a0d267SMarcel Moolenaar case '1': 5422d1a0d267SMarcel Moolenaar case '2': 5423d1a0d267SMarcel Moolenaar case '3': 5424d1a0d267SMarcel Moolenaar case '4': 5425d1a0d267SMarcel Moolenaar case '5': 5426d1a0d267SMarcel Moolenaar case '6': 5427d1a0d267SMarcel Moolenaar case '7': 5428d1a0d267SMarcel Moolenaar case '8': 5429d1a0d267SMarcel Moolenaar case '9': 5430d1a0d267SMarcel Moolenaar fnum = (fnum * 10) + (*sp - '0'); 5431d1a0d267SMarcel Moolenaar break; 5432d1a0d267SMarcel Moolenaar 543342ff34c3SPhil Shafer case 'a': 543442ff34c3SPhil Shafer flags |= XFF_ARGUMENT; 543542ff34c3SPhil Shafer break; 543642ff34c3SPhil Shafer 543731337658SMarcel Moolenaar case 'c': 543831337658SMarcel Moolenaar flags |= XFF_COLON; 543931337658SMarcel Moolenaar break; 544031337658SMarcel Moolenaar 544131337658SMarcel Moolenaar case 'd': 544231337658SMarcel Moolenaar flags |= XFF_DISPLAY_ONLY; 544331337658SMarcel Moolenaar break; 544431337658SMarcel Moolenaar 544531337658SMarcel Moolenaar case 'e': 544631337658SMarcel Moolenaar flags |= XFF_ENCODE_ONLY; 544731337658SMarcel Moolenaar break; 544831337658SMarcel Moolenaar 5449d1a0d267SMarcel Moolenaar case 'g': 5450d1a0d267SMarcel Moolenaar flags |= XFF_GT_FIELD; 5451d1a0d267SMarcel Moolenaar break; 5452d1a0d267SMarcel Moolenaar 5453d1a0d267SMarcel Moolenaar case 'h': 5454d1a0d267SMarcel Moolenaar flags |= XFF_HUMANIZE; 5455d1a0d267SMarcel Moolenaar break; 5456d1a0d267SMarcel Moolenaar 545731337658SMarcel Moolenaar case 'k': 545831337658SMarcel Moolenaar flags |= XFF_KEY; 545931337658SMarcel Moolenaar break; 546031337658SMarcel Moolenaar 546131337658SMarcel Moolenaar case 'l': 546231337658SMarcel Moolenaar flags |= XFF_LEAF_LIST; 546331337658SMarcel Moolenaar break; 546431337658SMarcel Moolenaar 546531337658SMarcel Moolenaar case 'n': 546631337658SMarcel Moolenaar flags |= XFF_NOQUOTE; 546731337658SMarcel Moolenaar break; 546831337658SMarcel Moolenaar 5469d1a0d267SMarcel Moolenaar case 'p': 5470d1a0d267SMarcel Moolenaar flags |= XFF_GT_PLURAL; 5471d1a0d267SMarcel Moolenaar break; 5472d1a0d267SMarcel Moolenaar 547331337658SMarcel Moolenaar case 'q': 547431337658SMarcel Moolenaar flags |= XFF_QUOTE; 547531337658SMarcel Moolenaar break; 547631337658SMarcel Moolenaar 547731337658SMarcel Moolenaar case 't': 547831337658SMarcel Moolenaar flags |= XFF_TRIM_WS; 547931337658SMarcel Moolenaar break; 548031337658SMarcel Moolenaar 548131337658SMarcel Moolenaar case 'w': 548231337658SMarcel Moolenaar flags |= XFF_WS; 548331337658SMarcel Moolenaar break; 548431337658SMarcel Moolenaar 548531337658SMarcel Moolenaar default: 5486d1a0d267SMarcel Moolenaar xo_failure(xop, "field descriptor uses unknown modifier: '%s'", 5487d1a0d267SMarcel Moolenaar xo_printable(fmt)); 548831337658SMarcel Moolenaar /* 548931337658SMarcel Moolenaar * No good answer here; a bad format will likely 549031337658SMarcel Moolenaar * mean a core file. We just return and hope 549131337658SMarcel Moolenaar * the caller notices there's no output, and while 5492d1a0d267SMarcel Moolenaar * that seems, well, bad, there's nothing better. 549331337658SMarcel Moolenaar */ 5494d1a0d267SMarcel Moolenaar return NULL; 5495d1a0d267SMarcel Moolenaar } 5496d1a0d267SMarcel Moolenaar 5497d1a0d267SMarcel Moolenaar if (ftype == 'N' || ftype == 'U') { 5498d1a0d267SMarcel Moolenaar if (flags & XFF_COLON) { 5499d1a0d267SMarcel Moolenaar xo_failure(xop, "colon modifier on 'N' or 'U' field ignored: " 5500d1a0d267SMarcel Moolenaar "'%s'", xo_printable(fmt)); 5501d1a0d267SMarcel Moolenaar flags &= ~XFF_COLON; 5502d1a0d267SMarcel Moolenaar } 550331337658SMarcel Moolenaar } 550431337658SMarcel Moolenaar } 550531337658SMarcel Moolenaar 5506d1a0d267SMarcel Moolenaar xfip->xfi_flags = flags; 5507d1a0d267SMarcel Moolenaar xfip->xfi_ftype = ftype ?: 'V'; 5508d1a0d267SMarcel Moolenaar xfip->xfi_fnum = fnum; 5509d1a0d267SMarcel Moolenaar 5510d1a0d267SMarcel Moolenaar return sp; 5511d1a0d267SMarcel Moolenaar } 5512d1a0d267SMarcel Moolenaar 5513d1a0d267SMarcel Moolenaar /* 5514d1a0d267SMarcel Moolenaar * Number any remaining fields that need numbers. Note that some 5515d1a0d267SMarcel Moolenaar * field types (text, newline, escaped braces) never get numbers. 5516d1a0d267SMarcel Moolenaar */ 5517d1a0d267SMarcel Moolenaar static void 5518d1a0d267SMarcel Moolenaar xo_gettext_finish_numbering_fields (xo_handle_t *xop UNUSED, 5519d1a0d267SMarcel Moolenaar const char *fmt UNUSED, 5520d1a0d267SMarcel Moolenaar xo_field_info_t *fields) 5521d1a0d267SMarcel Moolenaar { 5522d1a0d267SMarcel Moolenaar xo_field_info_t *xfip; 5523d1a0d267SMarcel Moolenaar unsigned fnum, max_fields; 5524d1a0d267SMarcel Moolenaar uint64_t bits = 0; 5525d1a0d267SMarcel Moolenaar 5526d1a0d267SMarcel Moolenaar /* First make a list of add the explicitly used bits */ 5527d1a0d267SMarcel Moolenaar for (xfip = fields, fnum = 0; xfip->xfi_ftype; xfip++) { 5528d1a0d267SMarcel Moolenaar switch (xfip->xfi_ftype) { 5529d1a0d267SMarcel Moolenaar case XO_ROLE_NEWLINE: /* Don't get numbered */ 5530d1a0d267SMarcel Moolenaar case XO_ROLE_TEXT: 5531d1a0d267SMarcel Moolenaar case XO_ROLE_EBRACE: 5532d1a0d267SMarcel Moolenaar case 'G': 5533d1a0d267SMarcel Moolenaar continue; 5534d1a0d267SMarcel Moolenaar } 5535d1a0d267SMarcel Moolenaar 5536d1a0d267SMarcel Moolenaar fnum += 1; 5537d1a0d267SMarcel Moolenaar if (fnum >= 63) 5538d1a0d267SMarcel Moolenaar break; 5539d1a0d267SMarcel Moolenaar 5540d1a0d267SMarcel Moolenaar if (xfip->xfi_fnum) 5541d1a0d267SMarcel Moolenaar bits |= 1 << xfip->xfi_fnum; 5542d1a0d267SMarcel Moolenaar } 5543d1a0d267SMarcel Moolenaar 5544d1a0d267SMarcel Moolenaar max_fields = fnum; 5545d1a0d267SMarcel Moolenaar 5546d1a0d267SMarcel Moolenaar for (xfip = fields, fnum = 0; xfip->xfi_ftype; xfip++) { 5547d1a0d267SMarcel Moolenaar switch (xfip->xfi_ftype) { 5548d1a0d267SMarcel Moolenaar case XO_ROLE_NEWLINE: /* Don't get numbered */ 5549d1a0d267SMarcel Moolenaar case XO_ROLE_TEXT: 5550d1a0d267SMarcel Moolenaar case XO_ROLE_EBRACE: 5551d1a0d267SMarcel Moolenaar case 'G': 5552d1a0d267SMarcel Moolenaar continue; 5553d1a0d267SMarcel Moolenaar } 5554d1a0d267SMarcel Moolenaar 5555d1a0d267SMarcel Moolenaar if (xfip->xfi_fnum != 0) 5556d1a0d267SMarcel Moolenaar continue; 5557d1a0d267SMarcel Moolenaar 5558d1a0d267SMarcel Moolenaar /* Find the next unassigned field */ 5559d1a0d267SMarcel Moolenaar for (fnum++; bits & (1 << fnum); fnum++) 5560d1a0d267SMarcel Moolenaar continue; 5561d1a0d267SMarcel Moolenaar 5562d1a0d267SMarcel Moolenaar if (fnum > max_fields) 5563d1a0d267SMarcel Moolenaar break; 5564d1a0d267SMarcel Moolenaar 5565d1a0d267SMarcel Moolenaar xfip->xfi_fnum = fnum; /* Mark the field number */ 5566d1a0d267SMarcel Moolenaar bits |= 1 << fnum; /* Mark it used */ 5567d1a0d267SMarcel Moolenaar } 5568d1a0d267SMarcel Moolenaar } 5569d1a0d267SMarcel Moolenaar 5570d1a0d267SMarcel Moolenaar /* 5571ee5cf116SPhil Shafer * The format string uses field numbers, so we need to whiffle through it 5572d1a0d267SMarcel Moolenaar * and make sure everything's sane and lovely. 5573d1a0d267SMarcel Moolenaar */ 5574d1a0d267SMarcel Moolenaar static int 5575d1a0d267SMarcel Moolenaar xo_parse_field_numbers (xo_handle_t *xop, const char *fmt, 5576d1a0d267SMarcel Moolenaar xo_field_info_t *fields, unsigned num_fields) 5577d1a0d267SMarcel Moolenaar { 5578d1a0d267SMarcel Moolenaar xo_field_info_t *xfip; 5579d1a0d267SMarcel Moolenaar unsigned field, fnum; 5580d1a0d267SMarcel Moolenaar uint64_t bits = 0; 5581d1a0d267SMarcel Moolenaar 5582d1a0d267SMarcel Moolenaar for (xfip = fields, field = 0; field < num_fields; xfip++, field++) { 5583d1a0d267SMarcel Moolenaar /* Fields default to 1:1 with natural position */ 5584d1a0d267SMarcel Moolenaar if (xfip->xfi_fnum == 0) 5585d1a0d267SMarcel Moolenaar xfip->xfi_fnum = field + 1; 5586d1a0d267SMarcel Moolenaar else if (xfip->xfi_fnum > num_fields) { 5587d1a0d267SMarcel Moolenaar xo_failure(xop, "field number exceeds number of fields: '%s'", fmt); 5588d1a0d267SMarcel Moolenaar return -1; 5589d1a0d267SMarcel Moolenaar } 5590d1a0d267SMarcel Moolenaar 5591d1a0d267SMarcel Moolenaar fnum = xfip->xfi_fnum - 1; /* Move to zero origin */ 5592d1a0d267SMarcel Moolenaar if (fnum < 64) { /* Only test what fits */ 5593d1a0d267SMarcel Moolenaar if (bits & (1 << fnum)) { 5594d1a0d267SMarcel Moolenaar xo_failure(xop, "field number %u reused: '%s'", 5595d1a0d267SMarcel Moolenaar xfip->xfi_fnum, fmt); 5596d1a0d267SMarcel Moolenaar return -1; 5597d1a0d267SMarcel Moolenaar } 5598d1a0d267SMarcel Moolenaar bits |= 1 << fnum; 5599d1a0d267SMarcel Moolenaar } 5600d1a0d267SMarcel Moolenaar } 5601d1a0d267SMarcel Moolenaar 5602d1a0d267SMarcel Moolenaar return 0; 5603d1a0d267SMarcel Moolenaar } 5604d1a0d267SMarcel Moolenaar 5605d1a0d267SMarcel Moolenaar static int 5606d1a0d267SMarcel Moolenaar xo_parse_fields (xo_handle_t *xop, xo_field_info_t *fields, 5607d1a0d267SMarcel Moolenaar unsigned num_fields, const char *fmt) 5608d1a0d267SMarcel Moolenaar { 5609d1a0d267SMarcel Moolenaar const char *cp, *sp, *ep, *basep; 5610d1a0d267SMarcel Moolenaar unsigned field = 0; 5611d1a0d267SMarcel Moolenaar xo_field_info_t *xfip = fields; 5612d1a0d267SMarcel Moolenaar unsigned seen_fnum = 0; 5613d1a0d267SMarcel Moolenaar 5614d1a0d267SMarcel Moolenaar for (cp = fmt; *cp && field < num_fields; field++, xfip++) { 5615d1a0d267SMarcel Moolenaar xfip->xfi_start = cp; 5616d1a0d267SMarcel Moolenaar 5617d1a0d267SMarcel Moolenaar if (*cp == '\n') { 5618d1a0d267SMarcel Moolenaar xfip->xfi_ftype = XO_ROLE_NEWLINE; 5619d1a0d267SMarcel Moolenaar xfip->xfi_len = 1; 5620d1a0d267SMarcel Moolenaar cp += 1; 5621d1a0d267SMarcel Moolenaar continue; 5622d1a0d267SMarcel Moolenaar } 5623d1a0d267SMarcel Moolenaar 5624d1a0d267SMarcel Moolenaar if (*cp != '{') { 5625d1a0d267SMarcel Moolenaar /* Normal text */ 5626d1a0d267SMarcel Moolenaar for (sp = cp; *sp; sp++) { 5627d1a0d267SMarcel Moolenaar if (*sp == '{' || *sp == '\n') 5628d1a0d267SMarcel Moolenaar break; 5629d1a0d267SMarcel Moolenaar } 5630d1a0d267SMarcel Moolenaar 5631d1a0d267SMarcel Moolenaar xfip->xfi_ftype = XO_ROLE_TEXT; 5632d1a0d267SMarcel Moolenaar xfip->xfi_content = cp; 5633d1a0d267SMarcel Moolenaar xfip->xfi_clen = sp - cp; 5634d1a0d267SMarcel Moolenaar xfip->xfi_next = sp; 5635d1a0d267SMarcel Moolenaar 5636d1a0d267SMarcel Moolenaar cp = sp; 5637d1a0d267SMarcel Moolenaar continue; 5638d1a0d267SMarcel Moolenaar } 5639d1a0d267SMarcel Moolenaar 5640d1a0d267SMarcel Moolenaar if (cp[1] == '{') { /* Start of {{escaped braces}} */ 5641d1a0d267SMarcel Moolenaar xfip->xfi_start = cp + 1; /* Start at second brace */ 5642d1a0d267SMarcel Moolenaar xfip->xfi_ftype = XO_ROLE_EBRACE; 5643d1a0d267SMarcel Moolenaar 5644d1a0d267SMarcel Moolenaar cp += 2; /* Skip over _both_ characters */ 5645d1a0d267SMarcel Moolenaar for (sp = cp; *sp; sp++) { 5646d1a0d267SMarcel Moolenaar if (*sp == '}' && sp[1] == '}') 5647d1a0d267SMarcel Moolenaar break; 5648d1a0d267SMarcel Moolenaar } 5649d1a0d267SMarcel Moolenaar if (*sp == '\0') { 5650d1a0d267SMarcel Moolenaar xo_failure(xop, "missing closing '}}': '%s'", 5651d1a0d267SMarcel Moolenaar xo_printable(fmt)); 5652d1a0d267SMarcel Moolenaar return -1; 5653d1a0d267SMarcel Moolenaar } 5654d1a0d267SMarcel Moolenaar 5655d1a0d267SMarcel Moolenaar xfip->xfi_len = sp - xfip->xfi_start + 1; 5656d1a0d267SMarcel Moolenaar 5657d1a0d267SMarcel Moolenaar /* Move along the string, but don't run off the end */ 5658d1a0d267SMarcel Moolenaar if (*sp == '}' && sp[1] == '}') 5659d1a0d267SMarcel Moolenaar sp += 2; 5660d1a0d267SMarcel Moolenaar cp = *sp ? sp : sp; 5661d1a0d267SMarcel Moolenaar xfip->xfi_next = cp; 5662d1a0d267SMarcel Moolenaar continue; 5663d1a0d267SMarcel Moolenaar } 5664d1a0d267SMarcel Moolenaar 5665d1a0d267SMarcel Moolenaar /* We are looking at the start of a field definition */ 5666d1a0d267SMarcel Moolenaar xfip->xfi_start = basep = cp + 1; 5667d1a0d267SMarcel Moolenaar 5668d1a0d267SMarcel Moolenaar const char *format = NULL; 56698a6eceffSPhil Shafer ssize_t flen = 0; 5670d1a0d267SMarcel Moolenaar 5671d1a0d267SMarcel Moolenaar /* Looking at roles and modifiers */ 5672d1a0d267SMarcel Moolenaar sp = xo_parse_roles(xop, fmt, basep, xfip); 5673d1a0d267SMarcel Moolenaar if (sp == NULL) { 5674d1a0d267SMarcel Moolenaar /* xo_failure has already been called */ 5675d1a0d267SMarcel Moolenaar return -1; 5676d1a0d267SMarcel Moolenaar } 5677d1a0d267SMarcel Moolenaar 5678d1a0d267SMarcel Moolenaar if (xfip->xfi_fnum) 5679d1a0d267SMarcel Moolenaar seen_fnum = 1; 5680d1a0d267SMarcel Moolenaar 5681d1a0d267SMarcel Moolenaar /* Looking at content */ 568231337658SMarcel Moolenaar if (*sp == ':') { 568331337658SMarcel Moolenaar for (ep = ++sp; *sp; sp++) { 568431337658SMarcel Moolenaar if (*sp == '}' || *sp == '/') 568531337658SMarcel Moolenaar break; 568631337658SMarcel Moolenaar if (*sp == '\\') { 568731337658SMarcel Moolenaar if (sp[1] == '\0') { 568831337658SMarcel Moolenaar xo_failure(xop, "backslash at the end of string"); 568931337658SMarcel Moolenaar return -1; 569031337658SMarcel Moolenaar } 569131337658SMarcel Moolenaar sp += 1; 569231337658SMarcel Moolenaar continue; 569331337658SMarcel Moolenaar } 569431337658SMarcel Moolenaar } 569531337658SMarcel Moolenaar if (ep != sp) { 5696d1a0d267SMarcel Moolenaar xfip->xfi_clen = sp - ep; 5697d1a0d267SMarcel Moolenaar xfip->xfi_content = ep; 569831337658SMarcel Moolenaar } 569931337658SMarcel Moolenaar } else { 5700d1a0d267SMarcel Moolenaar xo_failure(xop, "missing content (':'): '%s'", xo_printable(fmt)); 570131337658SMarcel Moolenaar return -1; 570231337658SMarcel Moolenaar } 570331337658SMarcel Moolenaar 5704d1a0d267SMarcel Moolenaar /* Looking at main (display) format */ 570531337658SMarcel Moolenaar if (*sp == '/') { 570631337658SMarcel Moolenaar for (ep = ++sp; *sp; sp++) { 570731337658SMarcel Moolenaar if (*sp == '}' || *sp == '/') 570831337658SMarcel Moolenaar break; 570931337658SMarcel Moolenaar if (*sp == '\\') { 571031337658SMarcel Moolenaar if (sp[1] == '\0') { 571131337658SMarcel Moolenaar xo_failure(xop, "backslash at the end of string"); 571231337658SMarcel Moolenaar return -1; 571331337658SMarcel Moolenaar } 571431337658SMarcel Moolenaar sp += 1; 571531337658SMarcel Moolenaar continue; 571631337658SMarcel Moolenaar } 571731337658SMarcel Moolenaar } 571831337658SMarcel Moolenaar flen = sp - ep; 571931337658SMarcel Moolenaar format = ep; 572031337658SMarcel Moolenaar } 572131337658SMarcel Moolenaar 5722d1a0d267SMarcel Moolenaar /* Looking at encoding format */ 572331337658SMarcel Moolenaar if (*sp == '/') { 572431337658SMarcel Moolenaar for (ep = ++sp; *sp; sp++) { 572531337658SMarcel Moolenaar if (*sp == '}') 572631337658SMarcel Moolenaar break; 572731337658SMarcel Moolenaar } 5728d1a0d267SMarcel Moolenaar 5729d1a0d267SMarcel Moolenaar xfip->xfi_encoding = ep; 5730d1a0d267SMarcel Moolenaar xfip->xfi_elen = sp - ep; 573131337658SMarcel Moolenaar } 573231337658SMarcel Moolenaar 5733d1a0d267SMarcel Moolenaar if (*sp != '}') { 5734d1a0d267SMarcel Moolenaar xo_failure(xop, "missing closing '}': %s", xo_printable(fmt)); 573531337658SMarcel Moolenaar return -1; 573631337658SMarcel Moolenaar } 573731337658SMarcel Moolenaar 5738d1a0d267SMarcel Moolenaar xfip->xfi_len = sp - xfip->xfi_start; 5739d1a0d267SMarcel Moolenaar xfip->xfi_next = ++sp; 5740d1a0d267SMarcel Moolenaar 5741d1a0d267SMarcel Moolenaar /* If we have content, then we have a default format */ 574242ff34c3SPhil Shafer if (xfip->xfi_clen || format || (xfip->xfi_flags & XFF_ARGUMENT)) { 5743d1a0d267SMarcel Moolenaar if (format) { 5744d1a0d267SMarcel Moolenaar xfip->xfi_format = format; 5745d1a0d267SMarcel Moolenaar xfip->xfi_flen = flen; 5746d1a0d267SMarcel Moolenaar } else if (xo_role_wants_default_format(xfip->xfi_ftype)) { 574742ff34c3SPhil Shafer xfip->xfi_format = xo_default_format; 5748d1a0d267SMarcel Moolenaar xfip->xfi_flen = 2; 5749d1a0d267SMarcel Moolenaar } 575031337658SMarcel Moolenaar } 575131337658SMarcel Moolenaar 5752d1a0d267SMarcel Moolenaar cp = sp; 5753d1a0d267SMarcel Moolenaar } 5754545ddfbeSMarcel Moolenaar 5755d1a0d267SMarcel Moolenaar int rc = 0; 5756d1a0d267SMarcel Moolenaar 5757d1a0d267SMarcel Moolenaar /* 5758d1a0d267SMarcel Moolenaar * If we saw a field number on at least one field, then we need 5759d1a0d267SMarcel Moolenaar * to enforce some rules and/or guidelines. 5760d1a0d267SMarcel Moolenaar */ 5761d1a0d267SMarcel Moolenaar if (seen_fnum) 5762d1a0d267SMarcel Moolenaar rc = xo_parse_field_numbers(xop, fmt, fields, field); 5763d1a0d267SMarcel Moolenaar 5764d1a0d267SMarcel Moolenaar return rc; 5765d1a0d267SMarcel Moolenaar } 5766d1a0d267SMarcel Moolenaar 5767d1a0d267SMarcel Moolenaar /* 5768d1a0d267SMarcel Moolenaar * We are passed a pointer to a format string just past the "{G:}" 5769d1a0d267SMarcel Moolenaar * field. We build a simplified version of the format string. 5770d1a0d267SMarcel Moolenaar */ 5771d1a0d267SMarcel Moolenaar static int 5772d1a0d267SMarcel Moolenaar xo_gettext_simplify_format (xo_handle_t *xop UNUSED, 5773d1a0d267SMarcel Moolenaar xo_buffer_t *xbp, 5774d1a0d267SMarcel Moolenaar xo_field_info_t *fields, 5775d1a0d267SMarcel Moolenaar int this_field, 5776d1a0d267SMarcel Moolenaar const char *fmt UNUSED, 5777d1a0d267SMarcel Moolenaar xo_simplify_field_func_t field_cb) 5778d1a0d267SMarcel Moolenaar { 5779d1a0d267SMarcel Moolenaar unsigned ftype; 5780d1a0d267SMarcel Moolenaar xo_xff_flags_t flags; 5781d1a0d267SMarcel Moolenaar int field = this_field + 1; 5782d1a0d267SMarcel Moolenaar xo_field_info_t *xfip; 5783d1a0d267SMarcel Moolenaar char ch; 5784d1a0d267SMarcel Moolenaar 5785d1a0d267SMarcel Moolenaar for (xfip = &fields[field]; xfip->xfi_ftype; xfip++, field++) { 5786d1a0d267SMarcel Moolenaar ftype = xfip->xfi_ftype; 5787d1a0d267SMarcel Moolenaar flags = xfip->xfi_flags; 5788d1a0d267SMarcel Moolenaar 5789d1a0d267SMarcel Moolenaar if ((flags & XFF_GT_FIELD) && xfip->xfi_content && ftype != 'V') { 5790d1a0d267SMarcel Moolenaar if (field_cb) 5791d1a0d267SMarcel Moolenaar field_cb(xfip->xfi_content, xfip->xfi_clen, 5792d1a0d267SMarcel Moolenaar (flags & XFF_GT_PLURAL) ? 1 : 0); 5793d1a0d267SMarcel Moolenaar } 5794d1a0d267SMarcel Moolenaar 5795d1a0d267SMarcel Moolenaar switch (ftype) { 5796d1a0d267SMarcel Moolenaar case 'G': 5797d1a0d267SMarcel Moolenaar /* Ignore gettext roles */ 5798d1a0d267SMarcel Moolenaar break; 5799d1a0d267SMarcel Moolenaar 5800d1a0d267SMarcel Moolenaar case XO_ROLE_NEWLINE: 5801d1a0d267SMarcel Moolenaar xo_buf_append(xbp, "\n", 1); 5802d1a0d267SMarcel Moolenaar break; 5803d1a0d267SMarcel Moolenaar 5804d1a0d267SMarcel Moolenaar case XO_ROLE_EBRACE: 5805d1a0d267SMarcel Moolenaar xo_buf_append(xbp, "{", 1); 5806d1a0d267SMarcel Moolenaar xo_buf_append(xbp, xfip->xfi_content, xfip->xfi_clen); 5807d1a0d267SMarcel Moolenaar xo_buf_append(xbp, "}", 1); 5808d1a0d267SMarcel Moolenaar break; 5809d1a0d267SMarcel Moolenaar 5810d1a0d267SMarcel Moolenaar case XO_ROLE_TEXT: 5811d1a0d267SMarcel Moolenaar xo_buf_append(xbp, xfip->xfi_content, xfip->xfi_clen); 5812d1a0d267SMarcel Moolenaar break; 5813d1a0d267SMarcel Moolenaar 5814d1a0d267SMarcel Moolenaar default: 5815d1a0d267SMarcel Moolenaar xo_buf_append(xbp, "{", 1); 5816d1a0d267SMarcel Moolenaar if (ftype != 'V') { 5817d1a0d267SMarcel Moolenaar ch = ftype; 5818d1a0d267SMarcel Moolenaar xo_buf_append(xbp, &ch, 1); 5819d1a0d267SMarcel Moolenaar } 5820d1a0d267SMarcel Moolenaar 5821d1a0d267SMarcel Moolenaar unsigned fnum = xfip->xfi_fnum ?: 0; 5822d1a0d267SMarcel Moolenaar if (fnum) { 5823d1a0d267SMarcel Moolenaar char num[12]; 5824d1a0d267SMarcel Moolenaar /* Field numbers are origin 1, not 0, following printf(3) */ 5825d1a0d267SMarcel Moolenaar snprintf(num, sizeof(num), "%u", fnum); 5826d1a0d267SMarcel Moolenaar xo_buf_append(xbp, num, strlen(num)); 5827d1a0d267SMarcel Moolenaar } 5828d1a0d267SMarcel Moolenaar 5829d1a0d267SMarcel Moolenaar xo_buf_append(xbp, ":", 1); 5830d1a0d267SMarcel Moolenaar xo_buf_append(xbp, xfip->xfi_content, xfip->xfi_clen); 5831d1a0d267SMarcel Moolenaar xo_buf_append(xbp, "}", 1); 5832d1a0d267SMarcel Moolenaar } 5833d1a0d267SMarcel Moolenaar } 5834d1a0d267SMarcel Moolenaar 5835d1a0d267SMarcel Moolenaar xo_buf_append(xbp, "", 1); 5836d1a0d267SMarcel Moolenaar return 0; 5837d1a0d267SMarcel Moolenaar } 5838d1a0d267SMarcel Moolenaar 5839d1a0d267SMarcel Moolenaar void 5840d1a0d267SMarcel Moolenaar xo_dump_fields (xo_field_info_t *); /* Fake prototype for debug function */ 5841d1a0d267SMarcel Moolenaar void 5842d1a0d267SMarcel Moolenaar xo_dump_fields (xo_field_info_t *fields) 5843d1a0d267SMarcel Moolenaar { 5844d1a0d267SMarcel Moolenaar xo_field_info_t *xfip; 5845d1a0d267SMarcel Moolenaar 5846d1a0d267SMarcel Moolenaar for (xfip = fields; xfip->xfi_ftype; xfip++) { 5847d1a0d267SMarcel Moolenaar printf("%lu(%u): %lx [%c/%u] [%.*s] [%.*s] [%.*s]\n", 5848d1a0d267SMarcel Moolenaar (unsigned long) (xfip - fields), xfip->xfi_fnum, 5849d1a0d267SMarcel Moolenaar (unsigned long) xfip->xfi_flags, 5850d1a0d267SMarcel Moolenaar isprint((int) xfip->xfi_ftype) ? xfip->xfi_ftype : ' ', 5851d1a0d267SMarcel Moolenaar xfip->xfi_ftype, 58528a6eceffSPhil Shafer (int) xfip->xfi_clen, xfip->xfi_content ?: "", 58538a6eceffSPhil Shafer (int) xfip->xfi_flen, xfip->xfi_format ?: "", 58548a6eceffSPhil Shafer (int) xfip->xfi_elen, xfip->xfi_encoding ?: ""); 5855d1a0d267SMarcel Moolenaar } 5856d1a0d267SMarcel Moolenaar } 5857d1a0d267SMarcel Moolenaar 5858d1a0d267SMarcel Moolenaar #ifdef HAVE_GETTEXT 5859d1a0d267SMarcel Moolenaar /* 5860d1a0d267SMarcel Moolenaar * Find the field that matches the given field number 5861d1a0d267SMarcel Moolenaar */ 5862d1a0d267SMarcel Moolenaar static xo_field_info_t * 5863d1a0d267SMarcel Moolenaar xo_gettext_find_field (xo_field_info_t *fields, unsigned fnum) 5864d1a0d267SMarcel Moolenaar { 5865d1a0d267SMarcel Moolenaar xo_field_info_t *xfip; 5866d1a0d267SMarcel Moolenaar 5867d1a0d267SMarcel Moolenaar for (xfip = fields; xfip->xfi_ftype; xfip++) 5868d1a0d267SMarcel Moolenaar if (xfip->xfi_fnum == fnum) 5869d1a0d267SMarcel Moolenaar return xfip; 5870d1a0d267SMarcel Moolenaar 5871d1a0d267SMarcel Moolenaar return NULL; 5872d1a0d267SMarcel Moolenaar } 5873d1a0d267SMarcel Moolenaar 5874d1a0d267SMarcel Moolenaar /* 5875d1a0d267SMarcel Moolenaar * At this point, we need to consider if the fields have been reordered, 5876d1a0d267SMarcel Moolenaar * such as "The {:adjective} {:noun}" to "La {:noun} {:adjective}". 5877d1a0d267SMarcel Moolenaar * 5878d1a0d267SMarcel Moolenaar * We need to rewrite the new_fields using the old fields order, 5879d1a0d267SMarcel Moolenaar * so that we can render the message using the arguments as they 5880d1a0d267SMarcel Moolenaar * appear on the stack. It's a lot of work, but we don't really 5881d1a0d267SMarcel Moolenaar * want to (eventually) fall into the standard printf code which 5882d1a0d267SMarcel Moolenaar * means using the arguments straight (and in order) from the 5883d1a0d267SMarcel Moolenaar * varargs we were originally passed. 5884d1a0d267SMarcel Moolenaar */ 5885d1a0d267SMarcel Moolenaar static void 5886d1a0d267SMarcel Moolenaar xo_gettext_rewrite_fields (xo_handle_t *xop UNUSED, 5887d1a0d267SMarcel Moolenaar xo_field_info_t *fields, unsigned max_fields) 5888d1a0d267SMarcel Moolenaar { 5889d1a0d267SMarcel Moolenaar xo_field_info_t tmp[max_fields]; 5890d1a0d267SMarcel Moolenaar bzero(tmp, max_fields * sizeof(tmp[0])); 5891d1a0d267SMarcel Moolenaar 5892d1a0d267SMarcel Moolenaar unsigned fnum = 0; 5893d1a0d267SMarcel Moolenaar xo_field_info_t *newp, *outp, *zp; 5894d1a0d267SMarcel Moolenaar for (newp = fields, outp = tmp; newp->xfi_ftype; newp++, outp++) { 5895d1a0d267SMarcel Moolenaar switch (newp->xfi_ftype) { 5896d1a0d267SMarcel Moolenaar case XO_ROLE_NEWLINE: /* Don't get numbered */ 5897d1a0d267SMarcel Moolenaar case XO_ROLE_TEXT: 5898d1a0d267SMarcel Moolenaar case XO_ROLE_EBRACE: 5899d1a0d267SMarcel Moolenaar case 'G': 5900d1a0d267SMarcel Moolenaar *outp = *newp; 5901d1a0d267SMarcel Moolenaar outp->xfi_renum = 0; 5902d1a0d267SMarcel Moolenaar continue; 5903d1a0d267SMarcel Moolenaar } 5904d1a0d267SMarcel Moolenaar 5905d1a0d267SMarcel Moolenaar zp = xo_gettext_find_field(fields, ++fnum); 5906d1a0d267SMarcel Moolenaar if (zp == NULL) { /* Should not occur */ 5907d1a0d267SMarcel Moolenaar *outp = *newp; 5908d1a0d267SMarcel Moolenaar outp->xfi_renum = 0; 5909d1a0d267SMarcel Moolenaar continue; 5910d1a0d267SMarcel Moolenaar } 5911d1a0d267SMarcel Moolenaar 5912d1a0d267SMarcel Moolenaar *outp = *zp; 5913d1a0d267SMarcel Moolenaar outp->xfi_renum = newp->xfi_fnum; 5914d1a0d267SMarcel Moolenaar } 5915d1a0d267SMarcel Moolenaar 5916d1a0d267SMarcel Moolenaar memcpy(fields, tmp, max_fields * sizeof(tmp[0])); 5917d1a0d267SMarcel Moolenaar } 5918d1a0d267SMarcel Moolenaar 5919d1a0d267SMarcel Moolenaar /* 5920d1a0d267SMarcel Moolenaar * We've got two lists of fields, the old list from the original 5921d1a0d267SMarcel Moolenaar * format string and the new one from the parsed gettext reply. The 5922d1a0d267SMarcel Moolenaar * new list has the localized words, where the old list has the 5923d1a0d267SMarcel Moolenaar * formatting information. We need to combine them into a single list 5924d1a0d267SMarcel Moolenaar * (the new list). 5925d1a0d267SMarcel Moolenaar * 5926d1a0d267SMarcel Moolenaar * If the list needs to be reordered, then we've got more serious work 5927d1a0d267SMarcel Moolenaar * to do. 5928d1a0d267SMarcel Moolenaar */ 5929d1a0d267SMarcel Moolenaar static int 5930d1a0d267SMarcel Moolenaar xo_gettext_combine_formats (xo_handle_t *xop, const char *fmt UNUSED, 5931d1a0d267SMarcel Moolenaar const char *gtfmt, xo_field_info_t *old_fields, 5932d1a0d267SMarcel Moolenaar xo_field_info_t *new_fields, unsigned new_max_fields, 5933d1a0d267SMarcel Moolenaar int *reorderedp) 5934d1a0d267SMarcel Moolenaar { 5935d1a0d267SMarcel Moolenaar int reordered = 0; 5936d1a0d267SMarcel Moolenaar xo_field_info_t *newp, *oldp, *startp = old_fields; 5937d1a0d267SMarcel Moolenaar 5938d1a0d267SMarcel Moolenaar xo_gettext_finish_numbering_fields(xop, fmt, old_fields); 5939d1a0d267SMarcel Moolenaar 5940d1a0d267SMarcel Moolenaar for (newp = new_fields; newp->xfi_ftype; newp++) { 5941d1a0d267SMarcel Moolenaar switch (newp->xfi_ftype) { 5942d1a0d267SMarcel Moolenaar case XO_ROLE_NEWLINE: 5943d1a0d267SMarcel Moolenaar case XO_ROLE_TEXT: 5944d1a0d267SMarcel Moolenaar case XO_ROLE_EBRACE: 5945d1a0d267SMarcel Moolenaar continue; 5946d1a0d267SMarcel Moolenaar 5947d1a0d267SMarcel Moolenaar case 'V': 5948d1a0d267SMarcel Moolenaar for (oldp = startp; oldp->xfi_ftype; oldp++) { 5949d1a0d267SMarcel Moolenaar if (oldp->xfi_ftype != 'V') 5950d1a0d267SMarcel Moolenaar continue; 5951d1a0d267SMarcel Moolenaar if (newp->xfi_clen != oldp->xfi_clen 5952d1a0d267SMarcel Moolenaar || strncmp(newp->xfi_content, oldp->xfi_content, 5953d1a0d267SMarcel Moolenaar oldp->xfi_clen) != 0) { 5954d1a0d267SMarcel Moolenaar reordered = 1; 5955d1a0d267SMarcel Moolenaar continue; 5956d1a0d267SMarcel Moolenaar } 5957d1a0d267SMarcel Moolenaar startp = oldp + 1; 5958d1a0d267SMarcel Moolenaar break; 5959d1a0d267SMarcel Moolenaar } 5960d1a0d267SMarcel Moolenaar 5961d1a0d267SMarcel Moolenaar /* Didn't find it on the first pass (starting from start) */ 5962d1a0d267SMarcel Moolenaar if (oldp->xfi_ftype == 0) { 5963d1a0d267SMarcel Moolenaar for (oldp = old_fields; oldp < startp; oldp++) { 5964d1a0d267SMarcel Moolenaar if (oldp->xfi_ftype != 'V') 5965d1a0d267SMarcel Moolenaar continue; 5966d1a0d267SMarcel Moolenaar if (newp->xfi_clen != oldp->xfi_clen) 5967d1a0d267SMarcel Moolenaar continue; 5968d1a0d267SMarcel Moolenaar if (strncmp(newp->xfi_content, oldp->xfi_content, 5969d1a0d267SMarcel Moolenaar oldp->xfi_clen) != 0) 5970d1a0d267SMarcel Moolenaar continue; 5971d1a0d267SMarcel Moolenaar reordered = 1; 5972d1a0d267SMarcel Moolenaar break; 5973d1a0d267SMarcel Moolenaar } 5974d1a0d267SMarcel Moolenaar if (oldp == startp) { 5975d1a0d267SMarcel Moolenaar /* Field not found */ 5976d1a0d267SMarcel Moolenaar xo_failure(xop, "post-gettext format can't find field " 5977d1a0d267SMarcel Moolenaar "'%.*s' in format '%s'", 5978d1a0d267SMarcel Moolenaar newp->xfi_clen, newp->xfi_content, 5979d1a0d267SMarcel Moolenaar xo_printable(gtfmt)); 5980d1a0d267SMarcel Moolenaar return -1; 5981d1a0d267SMarcel Moolenaar } 5982d1a0d267SMarcel Moolenaar } 5983d1a0d267SMarcel Moolenaar break; 5984d1a0d267SMarcel Moolenaar 5985d1a0d267SMarcel Moolenaar default: 5986d1a0d267SMarcel Moolenaar /* 5987d1a0d267SMarcel Moolenaar * Other fields don't have names for us to use, so if 5988d1a0d267SMarcel Moolenaar * the types aren't the same, then we'll have to assume 5989d1a0d267SMarcel Moolenaar * the original field is a match. 5990d1a0d267SMarcel Moolenaar */ 5991d1a0d267SMarcel Moolenaar for (oldp = startp; oldp->xfi_ftype; oldp++) { 5992d1a0d267SMarcel Moolenaar if (oldp->xfi_ftype == 'V') /* Can't go past these */ 5993d1a0d267SMarcel Moolenaar break; 5994d1a0d267SMarcel Moolenaar if (oldp->xfi_ftype == newp->xfi_ftype) 5995d1a0d267SMarcel Moolenaar goto copy_it; /* Assumably we have a match */ 5996d1a0d267SMarcel Moolenaar } 5997d1a0d267SMarcel Moolenaar continue; 5998d1a0d267SMarcel Moolenaar } 5999d1a0d267SMarcel Moolenaar 6000d1a0d267SMarcel Moolenaar /* 6001d1a0d267SMarcel Moolenaar * Found a match; copy over appropriate fields 6002d1a0d267SMarcel Moolenaar */ 6003d1a0d267SMarcel Moolenaar copy_it: 6004d1a0d267SMarcel Moolenaar newp->xfi_flags = oldp->xfi_flags; 6005d1a0d267SMarcel Moolenaar newp->xfi_fnum = oldp->xfi_fnum; 6006d1a0d267SMarcel Moolenaar newp->xfi_format = oldp->xfi_format; 6007d1a0d267SMarcel Moolenaar newp->xfi_flen = oldp->xfi_flen; 6008d1a0d267SMarcel Moolenaar newp->xfi_encoding = oldp->xfi_encoding; 6009d1a0d267SMarcel Moolenaar newp->xfi_elen = oldp->xfi_elen; 6010d1a0d267SMarcel Moolenaar } 6011d1a0d267SMarcel Moolenaar 6012d1a0d267SMarcel Moolenaar *reorderedp = reordered; 6013d1a0d267SMarcel Moolenaar if (reordered) { 6014d1a0d267SMarcel Moolenaar xo_gettext_finish_numbering_fields(xop, fmt, new_fields); 6015d1a0d267SMarcel Moolenaar xo_gettext_rewrite_fields(xop, new_fields, new_max_fields); 6016d1a0d267SMarcel Moolenaar } 6017d1a0d267SMarcel Moolenaar 6018d1a0d267SMarcel Moolenaar return 0; 6019d1a0d267SMarcel Moolenaar } 6020d1a0d267SMarcel Moolenaar 6021d1a0d267SMarcel Moolenaar /* 6022d1a0d267SMarcel Moolenaar * We don't want to make gettext() calls here with a complete format 6023d1a0d267SMarcel Moolenaar * string, since that means changing a flag would mean a 6024d1a0d267SMarcel Moolenaar * labor-intensive re-translation expense. Instead we build a 6025d1a0d267SMarcel Moolenaar * simplified form with a reduced level of detail, perform a lookup on 6026d1a0d267SMarcel Moolenaar * that string and then re-insert the formating info. 6027d1a0d267SMarcel Moolenaar * 6028d1a0d267SMarcel Moolenaar * So something like: 6029d1a0d267SMarcel Moolenaar * xo_emit("{G:}close {:fd/%ld} returned {g:error/%m} {:test/%6.6s}\n", ...) 6030d1a0d267SMarcel Moolenaar * would have a lookup string of: 6031d1a0d267SMarcel Moolenaar * "close {:fd} returned {:error} {:test}\n" 6032d1a0d267SMarcel Moolenaar * 6033d1a0d267SMarcel Moolenaar * We also need to handling reordering of fields, where the gettext() 6034d1a0d267SMarcel Moolenaar * reply string uses fields in a different order than the original 6035d1a0d267SMarcel Moolenaar * format string: 6036d1a0d267SMarcel Moolenaar * "cluse-a {:fd} retoorned {:test}. Bork {:error} Bork. Bork.\n" 6037d1a0d267SMarcel Moolenaar * If we have to reorder fields within the message, then things get 6038d1a0d267SMarcel Moolenaar * complicated. See xo_gettext_rewrite_fields. 6039d1a0d267SMarcel Moolenaar * 6040d1a0d267SMarcel Moolenaar * Summary: i18n aighn't cheap. 6041d1a0d267SMarcel Moolenaar */ 6042d1a0d267SMarcel Moolenaar static const char * 604342ff34c3SPhil Shafer xo_gettext_build_format (xo_handle_t *xop, 604442ff34c3SPhil Shafer xo_field_info_t *fields, int this_field, 6045d1a0d267SMarcel Moolenaar const char *fmt, char **new_fmtp) 6046d1a0d267SMarcel Moolenaar { 6047d1a0d267SMarcel Moolenaar if (xo_style_is_encoding(xop)) 6048d1a0d267SMarcel Moolenaar goto bail; 6049d1a0d267SMarcel Moolenaar 6050d1a0d267SMarcel Moolenaar xo_buffer_t xb; 6051d1a0d267SMarcel Moolenaar xo_buf_init(&xb); 6052d1a0d267SMarcel Moolenaar 6053d1a0d267SMarcel Moolenaar if (xo_gettext_simplify_format(xop, &xb, fields, 6054d1a0d267SMarcel Moolenaar this_field, fmt, NULL)) 6055d1a0d267SMarcel Moolenaar goto bail2; 6056d1a0d267SMarcel Moolenaar 6057d1a0d267SMarcel Moolenaar const char *gtfmt = xo_dgettext(xop, xb.xb_bufp); 6058d1a0d267SMarcel Moolenaar if (gtfmt == NULL || gtfmt == fmt || strcmp(gtfmt, fmt) == 0) 6059d1a0d267SMarcel Moolenaar goto bail2; 6060d1a0d267SMarcel Moolenaar 6061d1a0d267SMarcel Moolenaar char *new_fmt = xo_strndup(gtfmt, -1); 6062d1a0d267SMarcel Moolenaar if (new_fmt == NULL) 6063d1a0d267SMarcel Moolenaar goto bail2; 6064d1a0d267SMarcel Moolenaar 6065f2b7bf8aSPhil Shafer xo_buf_cleanup(&xb); 6066f2b7bf8aSPhil Shafer 6067d1a0d267SMarcel Moolenaar *new_fmtp = new_fmt; 6068d1a0d267SMarcel Moolenaar return new_fmt; 6069d1a0d267SMarcel Moolenaar 6070d1a0d267SMarcel Moolenaar bail2: 6071d1a0d267SMarcel Moolenaar xo_buf_cleanup(&xb); 6072d1a0d267SMarcel Moolenaar bail: 6073d1a0d267SMarcel Moolenaar *new_fmtp = NULL; 6074d1a0d267SMarcel Moolenaar return fmt; 6075d1a0d267SMarcel Moolenaar } 6076d1a0d267SMarcel Moolenaar 6077d1a0d267SMarcel Moolenaar static void 6078d1a0d267SMarcel Moolenaar xo_gettext_rebuild_content (xo_handle_t *xop, xo_field_info_t *fields, 60798a6eceffSPhil Shafer ssize_t *fstart, unsigned min_fstart, 60808a6eceffSPhil Shafer ssize_t *fend, unsigned max_fend) 6081d1a0d267SMarcel Moolenaar { 6082d1a0d267SMarcel Moolenaar xo_field_info_t *xfip; 6083d1a0d267SMarcel Moolenaar char *buf; 60848a6eceffSPhil Shafer ssize_t base = fstart[min_fstart]; 60858a6eceffSPhil Shafer ssize_t blen = fend[max_fend] - base; 6086d1a0d267SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_data; 6087d1a0d267SMarcel Moolenaar 6088d1a0d267SMarcel Moolenaar if (blen == 0) 6089d1a0d267SMarcel Moolenaar return; 6090d1a0d267SMarcel Moolenaar 6091d1a0d267SMarcel Moolenaar buf = xo_realloc(NULL, blen); 6092d1a0d267SMarcel Moolenaar if (buf == NULL) 6093d1a0d267SMarcel Moolenaar return; 6094d1a0d267SMarcel Moolenaar 6095d1a0d267SMarcel Moolenaar memcpy(buf, xbp->xb_bufp + fstart[min_fstart], blen); /* Copy our data */ 6096d1a0d267SMarcel Moolenaar 60978a6eceffSPhil Shafer unsigned field = min_fstart, len, fnum; 60988a6eceffSPhil Shafer ssize_t soff, doff = base; 6099d1a0d267SMarcel Moolenaar xo_field_info_t *zp; 6100d1a0d267SMarcel Moolenaar 6101d1a0d267SMarcel Moolenaar /* 6102d1a0d267SMarcel Moolenaar * Be aware there are two competing views of "field number": we 6103d1a0d267SMarcel Moolenaar * want the user to thing in terms of "The {1:size}" where {G:}, 6104d1a0d267SMarcel Moolenaar * newlines, escaped braces, and text don't have numbers. But is 6105d1a0d267SMarcel Moolenaar * also the internal view, where we have an array of 6106d1a0d267SMarcel Moolenaar * xo_field_info_t and every field have an index. fnum, fstart[] 6107d1a0d267SMarcel Moolenaar * and fend[] are the latter, but xfi_renum is the former. 6108d1a0d267SMarcel Moolenaar */ 6109d1a0d267SMarcel Moolenaar for (xfip = fields + field; xfip->xfi_ftype; xfip++, field++) { 6110d1a0d267SMarcel Moolenaar fnum = field; 6111d1a0d267SMarcel Moolenaar if (xfip->xfi_renum) { 6112d1a0d267SMarcel Moolenaar zp = xo_gettext_find_field(fields, xfip->xfi_renum); 6113d1a0d267SMarcel Moolenaar fnum = zp ? zp - fields : field; 6114d1a0d267SMarcel Moolenaar } 6115d1a0d267SMarcel Moolenaar 6116d1a0d267SMarcel Moolenaar soff = fstart[fnum]; 6117d1a0d267SMarcel Moolenaar len = fend[fnum] - soff; 6118d1a0d267SMarcel Moolenaar 6119d1a0d267SMarcel Moolenaar if (len > 0) { 6120d1a0d267SMarcel Moolenaar soff -= base; 6121d1a0d267SMarcel Moolenaar memcpy(xbp->xb_bufp + doff, buf + soff, len); 6122d1a0d267SMarcel Moolenaar doff += len; 6123d1a0d267SMarcel Moolenaar } 6124d1a0d267SMarcel Moolenaar } 6125d1a0d267SMarcel Moolenaar 6126d1a0d267SMarcel Moolenaar xo_free(buf); 6127d1a0d267SMarcel Moolenaar } 6128d1a0d267SMarcel Moolenaar #else /* HAVE_GETTEXT */ 6129d1a0d267SMarcel Moolenaar static const char * 6130d1a0d267SMarcel Moolenaar xo_gettext_build_format (xo_handle_t *xop UNUSED, 6131d1a0d267SMarcel Moolenaar xo_field_info_t *fields UNUSED, 6132d1a0d267SMarcel Moolenaar int this_field UNUSED, 6133d1a0d267SMarcel Moolenaar const char *fmt UNUSED, char **new_fmtp) 6134d1a0d267SMarcel Moolenaar { 6135d1a0d267SMarcel Moolenaar *new_fmtp = NULL; 6136d1a0d267SMarcel Moolenaar return fmt; 6137d1a0d267SMarcel Moolenaar } 6138d1a0d267SMarcel Moolenaar 6139d1a0d267SMarcel Moolenaar static int 6140d1a0d267SMarcel Moolenaar xo_gettext_combine_formats (xo_handle_t *xop UNUSED, const char *fmt UNUSED, 6141d1a0d267SMarcel Moolenaar const char *gtfmt UNUSED, 6142d1a0d267SMarcel Moolenaar xo_field_info_t *old_fields UNUSED, 6143d1a0d267SMarcel Moolenaar xo_field_info_t *new_fields UNUSED, 6144d1a0d267SMarcel Moolenaar unsigned new_max_fields UNUSED, 6145d1a0d267SMarcel Moolenaar int *reorderedp UNUSED) 6146d1a0d267SMarcel Moolenaar { 6147d1a0d267SMarcel Moolenaar return -1; 6148d1a0d267SMarcel Moolenaar } 6149d1a0d267SMarcel Moolenaar 6150d1a0d267SMarcel Moolenaar static void 6151d1a0d267SMarcel Moolenaar xo_gettext_rebuild_content (xo_handle_t *xop UNUSED, 6152d1a0d267SMarcel Moolenaar xo_field_info_t *fields UNUSED, 61538a6eceffSPhil Shafer ssize_t *fstart UNUSED, unsigned min_fstart UNUSED, 61548a6eceffSPhil Shafer ssize_t *fend UNUSED, unsigned max_fend UNUSED) 6155d1a0d267SMarcel Moolenaar { 6156d1a0d267SMarcel Moolenaar return; 6157d1a0d267SMarcel Moolenaar } 6158d1a0d267SMarcel Moolenaar #endif /* HAVE_GETTEXT */ 6159d1a0d267SMarcel Moolenaar 6160d1a0d267SMarcel Moolenaar /* 616142ff34c3SPhil Shafer * Emit a set of fields. This is really the core of libxo. 6162d1a0d267SMarcel Moolenaar */ 61638a6eceffSPhil Shafer static ssize_t 616442ff34c3SPhil Shafer xo_do_emit_fields (xo_handle_t *xop, xo_field_info_t *fields, 616542ff34c3SPhil Shafer unsigned max_fields, const char *fmt) 6166d1a0d267SMarcel Moolenaar { 6167d1a0d267SMarcel Moolenaar int gettext_inuse = 0; 6168d1a0d267SMarcel Moolenaar int gettext_changed = 0; 6169d1a0d267SMarcel Moolenaar int gettext_reordered = 0; 617042ff34c3SPhil Shafer unsigned ftype; 617142ff34c3SPhil Shafer xo_xff_flags_t flags; 6172d1a0d267SMarcel Moolenaar xo_field_info_t *new_fields = NULL; 617342ff34c3SPhil Shafer xo_field_info_t *xfip; 617442ff34c3SPhil Shafer unsigned field; 61758a6eceffSPhil Shafer ssize_t rc = 0; 617642ff34c3SPhil Shafer 6177d1a0d267SMarcel Moolenaar int flush = XOF_ISSET(xop, XOF_FLUSH); 6178d1a0d267SMarcel Moolenaar int flush_line = XOF_ISSET(xop, XOF_FLUSH_LINE); 6179d1a0d267SMarcel Moolenaar char *new_fmt = NULL; 6180d1a0d267SMarcel Moolenaar 6181d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_REORDER) || xo_style(xop) == XO_STYLE_ENCODER) 6182d1a0d267SMarcel Moolenaar flush_line = 0; 6183d1a0d267SMarcel Moolenaar 6184d1a0d267SMarcel Moolenaar /* 6185d1a0d267SMarcel Moolenaar * Some overhead for gettext; if the fields in the msgstr returned 6186d1a0d267SMarcel Moolenaar * by gettext are reordered, then we need to record start and end 6187d1a0d267SMarcel Moolenaar * for each field. We'll go ahead and render the fields in the 6188d1a0d267SMarcel Moolenaar * normal order, but later we can then reconstruct the reordered 6189d1a0d267SMarcel Moolenaar * fields using these fstart/fend values. 6190d1a0d267SMarcel Moolenaar */ 6191d1a0d267SMarcel Moolenaar unsigned flimit = max_fields * 2; /* Pessimistic limit */ 6192d1a0d267SMarcel Moolenaar unsigned min_fstart = flimit - 1; 6193d1a0d267SMarcel Moolenaar unsigned max_fend = 0; /* Highest recorded fend[] entry */ 61948a6eceffSPhil Shafer ssize_t fstart[flimit]; 6195d1a0d267SMarcel Moolenaar bzero(fstart, flimit * sizeof(fstart[0])); 61968a6eceffSPhil Shafer ssize_t fend[flimit]; 6197d1a0d267SMarcel Moolenaar bzero(fend, flimit * sizeof(fend[0])); 6198d1a0d267SMarcel Moolenaar 6199f2b7bf8aSPhil Shafer for (xfip = fields, field = 0; field < max_fields && xfip->xfi_ftype; 6200d1a0d267SMarcel Moolenaar xfip++, field++) { 6201d1a0d267SMarcel Moolenaar ftype = xfip->xfi_ftype; 6202d1a0d267SMarcel Moolenaar flags = xfip->xfi_flags; 6203d1a0d267SMarcel Moolenaar 6204d1a0d267SMarcel Moolenaar /* Record field start offset */ 6205d1a0d267SMarcel Moolenaar if (gettext_reordered) { 6206d1a0d267SMarcel Moolenaar fstart[field] = xo_buf_offset(&xop->xo_data); 6207d1a0d267SMarcel Moolenaar if (min_fstart > field) 6208d1a0d267SMarcel Moolenaar min_fstart = field; 6209d1a0d267SMarcel Moolenaar } 6210d1a0d267SMarcel Moolenaar 621142ff34c3SPhil Shafer const char *content = xfip->xfi_content; 62128a6eceffSPhil Shafer ssize_t clen = xfip->xfi_clen; 621342ff34c3SPhil Shafer 621442ff34c3SPhil Shafer if (flags & XFF_ARGUMENT) { 621542ff34c3SPhil Shafer /* 621642ff34c3SPhil Shafer * Argument flag means the content isn't given in the descriptor, 621742ff34c3SPhil Shafer * but as a UTF-8 string ('const char *') argument in xo_vap. 621842ff34c3SPhil Shafer */ 621942ff34c3SPhil Shafer content = va_arg(xop->xo_vap, char *); 622042ff34c3SPhil Shafer clen = content ? strlen(content) : 0; 622142ff34c3SPhil Shafer } 622242ff34c3SPhil Shafer 6223d1a0d267SMarcel Moolenaar if (ftype == XO_ROLE_NEWLINE) { 6224d1a0d267SMarcel Moolenaar xo_line_close(xop); 6225d1a0d267SMarcel Moolenaar if (flush_line && xo_flush_h(xop) < 0) 6226d1a0d267SMarcel Moolenaar return -1; 6227d1a0d267SMarcel Moolenaar goto bottom; 6228d1a0d267SMarcel Moolenaar 6229d1a0d267SMarcel Moolenaar } else if (ftype == XO_ROLE_EBRACE) { 6230d1a0d267SMarcel Moolenaar xo_format_text(xop, xfip->xfi_start, xfip->xfi_len); 6231d1a0d267SMarcel Moolenaar goto bottom; 6232d1a0d267SMarcel Moolenaar 6233d1a0d267SMarcel Moolenaar } else if (ftype == XO_ROLE_TEXT) { 6234d1a0d267SMarcel Moolenaar /* Normal text */ 6235d1a0d267SMarcel Moolenaar xo_format_text(xop, xfip->xfi_content, xfip->xfi_clen); 6236d1a0d267SMarcel Moolenaar goto bottom; 6237d1a0d267SMarcel Moolenaar } 6238d1a0d267SMarcel Moolenaar 6239d1a0d267SMarcel Moolenaar /* 6240d1a0d267SMarcel Moolenaar * Notes and units need the 'w' flag handled before the content. 6241d1a0d267SMarcel Moolenaar */ 6242d1a0d267SMarcel Moolenaar if (ftype == 'N' || ftype == 'U') { 6243d1a0d267SMarcel Moolenaar if (flags & XFF_WS) { 6244d1a0d267SMarcel Moolenaar xo_format_content(xop, "padding", NULL, " ", 1, 6245d1a0d267SMarcel Moolenaar NULL, 0, flags); 6246264104f2SPhil Shafer flags &= ~XFF_WS; /* Prevent later handling of this flag */ 6247d1a0d267SMarcel Moolenaar } 6248d1a0d267SMarcel Moolenaar } 6249d1a0d267SMarcel Moolenaar 6250d1a0d267SMarcel Moolenaar if (ftype == 'V') 6251264104f2SPhil Shafer xo_format_value(xop, content, clen, NULL, 0, 6252d1a0d267SMarcel Moolenaar xfip->xfi_format, xfip->xfi_flen, 6253d1a0d267SMarcel Moolenaar xfip->xfi_encoding, xfip->xfi_elen, flags); 6254d1a0d267SMarcel Moolenaar else if (ftype == '[') 625542ff34c3SPhil Shafer xo_anchor_start(xop, xfip, content, clen); 6256545ddfbeSMarcel Moolenaar else if (ftype == ']') 625742ff34c3SPhil Shafer xo_anchor_stop(xop, xfip, content, clen); 6258788ca347SMarcel Moolenaar else if (ftype == 'C') 625942ff34c3SPhil Shafer xo_format_colors(xop, xfip, content, clen); 6260545ddfbeSMarcel Moolenaar 6261d1a0d267SMarcel Moolenaar else if (ftype == 'G') { 6262d1a0d267SMarcel Moolenaar /* 6263d1a0d267SMarcel Moolenaar * A {G:domain} field; disect the domain name and translate 6264d1a0d267SMarcel Moolenaar * the remaining portion of the input string. If the user 6265d1a0d267SMarcel Moolenaar * didn't put the {G:} at the start of the format string, then 6266d1a0d267SMarcel Moolenaar * assumably they just want us to translate the rest of it. 6267d1a0d267SMarcel Moolenaar * Since gettext returns strings in a static buffer, we make 6268d1a0d267SMarcel Moolenaar * a copy in new_fmt. 6269d1a0d267SMarcel Moolenaar */ 627042ff34c3SPhil Shafer xo_set_gettext_domain(xop, xfip, content, clen); 6271d1a0d267SMarcel Moolenaar 6272d1a0d267SMarcel Moolenaar if (!gettext_inuse) { /* Only translate once */ 6273d1a0d267SMarcel Moolenaar gettext_inuse = 1; 6274d1a0d267SMarcel Moolenaar if (new_fmt) { 6275d1a0d267SMarcel Moolenaar xo_free(new_fmt); 6276d1a0d267SMarcel Moolenaar new_fmt = NULL; 6277545ddfbeSMarcel Moolenaar } 6278545ddfbeSMarcel Moolenaar 6279d1a0d267SMarcel Moolenaar xo_gettext_build_format(xop, fields, field, 6280d1a0d267SMarcel Moolenaar xfip->xfi_next, &new_fmt); 6281d1a0d267SMarcel Moolenaar if (new_fmt) { 6282d1a0d267SMarcel Moolenaar gettext_changed = 1; 6283d1a0d267SMarcel Moolenaar 6284d1a0d267SMarcel Moolenaar unsigned new_max_fields = xo_count_fields(xop, new_fmt); 6285d1a0d267SMarcel Moolenaar 6286d1a0d267SMarcel Moolenaar if (++new_max_fields < max_fields) 6287d1a0d267SMarcel Moolenaar new_max_fields = max_fields; 6288d1a0d267SMarcel Moolenaar 6289d1a0d267SMarcel Moolenaar /* Leave a blank slot at the beginning */ 62908a6eceffSPhil Shafer ssize_t sz = (new_max_fields + 1) * sizeof(xo_field_info_t); 6291d1a0d267SMarcel Moolenaar new_fields = alloca(sz); 6292d1a0d267SMarcel Moolenaar bzero(new_fields, sz); 6293d1a0d267SMarcel Moolenaar 6294d1a0d267SMarcel Moolenaar if (!xo_parse_fields(xop, new_fields + 1, 6295d1a0d267SMarcel Moolenaar new_max_fields, new_fmt)) { 6296d1a0d267SMarcel Moolenaar gettext_reordered = 0; 6297d1a0d267SMarcel Moolenaar 6298d1a0d267SMarcel Moolenaar if (!xo_gettext_combine_formats(xop, fmt, new_fmt, 6299d1a0d267SMarcel Moolenaar fields, new_fields + 1, 6300d1a0d267SMarcel Moolenaar new_max_fields, &gettext_reordered)) { 6301d1a0d267SMarcel Moolenaar 6302d1a0d267SMarcel Moolenaar if (gettext_reordered) { 6303d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_LOG_GETTEXT)) 6304d1a0d267SMarcel Moolenaar xo_failure(xop, "gettext finds reordered " 6305d1a0d267SMarcel Moolenaar "fields in '%s' and '%s'", 6306d1a0d267SMarcel Moolenaar xo_printable(fmt), 6307d1a0d267SMarcel Moolenaar xo_printable(new_fmt)); 6308d1a0d267SMarcel Moolenaar flush_line = 0; /* Must keep at content */ 6309d1a0d267SMarcel Moolenaar XOIF_SET(xop, XOIF_REORDER); 6310d1a0d267SMarcel Moolenaar } 6311d1a0d267SMarcel Moolenaar 6312d1a0d267SMarcel Moolenaar field = -1; /* Will be incremented at top of loop */ 6313d1a0d267SMarcel Moolenaar xfip = new_fields; 6314d1a0d267SMarcel Moolenaar max_fields = new_max_fields; 6315d1a0d267SMarcel Moolenaar } 6316d1a0d267SMarcel Moolenaar } 6317d1a0d267SMarcel Moolenaar } 6318d1a0d267SMarcel Moolenaar } 6319d1a0d267SMarcel Moolenaar continue; 6320d1a0d267SMarcel Moolenaar 632142ff34c3SPhil Shafer } else if (clen || xfip->xfi_format) { 6322d1a0d267SMarcel Moolenaar 6323d1a0d267SMarcel Moolenaar const char *class_name = xo_class_name(ftype); 6324d1a0d267SMarcel Moolenaar if (class_name) 6325d1a0d267SMarcel Moolenaar xo_format_content(xop, class_name, xo_tag_name(ftype), 632642ff34c3SPhil Shafer content, clen, 6327d1a0d267SMarcel Moolenaar xfip->xfi_format, xfip->xfi_flen, flags); 632831337658SMarcel Moolenaar else if (ftype == 'T') 632942ff34c3SPhil Shafer xo_format_title(xop, xfip, content, clen); 6330d1a0d267SMarcel Moolenaar else if (ftype == 'U') 633142ff34c3SPhil Shafer xo_format_units(xop, xfip, content, clen); 6332d1a0d267SMarcel Moolenaar else 6333d1a0d267SMarcel Moolenaar xo_failure(xop, "unknown field type: '%c'", ftype); 6334545ddfbeSMarcel Moolenaar } 633531337658SMarcel Moolenaar 633631337658SMarcel Moolenaar if (flags & XFF_COLON) 6337d1a0d267SMarcel Moolenaar xo_format_content(xop, "decoration", NULL, ":", 1, NULL, 0, 0); 633831337658SMarcel Moolenaar 6339d1a0d267SMarcel Moolenaar if (flags & XFF_WS) 6340d1a0d267SMarcel Moolenaar xo_format_content(xop, "padding", NULL, " ", 1, NULL, 0, 0); 6341d1a0d267SMarcel Moolenaar 6342d1a0d267SMarcel Moolenaar bottom: 6343d1a0d267SMarcel Moolenaar /* Record the end-of-field offset */ 6344d1a0d267SMarcel Moolenaar if (gettext_reordered) { 6345d1a0d267SMarcel Moolenaar fend[field] = xo_buf_offset(&xop->xo_data); 6346d1a0d267SMarcel Moolenaar max_fend = field; 634731337658SMarcel Moolenaar } 634831337658SMarcel Moolenaar } 634931337658SMarcel Moolenaar 6350d1a0d267SMarcel Moolenaar if (gettext_changed && gettext_reordered) { 6351d1a0d267SMarcel Moolenaar /* Final step: rebuild the content using the rendered fields */ 6352d1a0d267SMarcel Moolenaar xo_gettext_rebuild_content(xop, new_fields + 1, fstart, min_fstart, 6353d1a0d267SMarcel Moolenaar fend, max_fend); 6354d1a0d267SMarcel Moolenaar } 6355d1a0d267SMarcel Moolenaar 6356d1a0d267SMarcel Moolenaar XOIF_CLEAR(xop, XOIF_REORDER); 6357d1a0d267SMarcel Moolenaar 6358ee5cf116SPhil Shafer /* 6359ee5cf116SPhil Shafer * If we've got enough data, flush it. 6360ee5cf116SPhil Shafer */ 6361ee5cf116SPhil Shafer if (xo_buf_offset(&xop->xo_data) > XO_BUF_HIGH_WATER) 6362ee5cf116SPhil Shafer flush = 1; 6363ee5cf116SPhil Shafer 636431337658SMarcel Moolenaar /* If we don't have an anchor, write the text out */ 6365d1a0d267SMarcel Moolenaar if (flush && !XOIF_ISSET(xop, XOIF_ANCHOR)) { 6366545ddfbeSMarcel Moolenaar if (xo_write(xop) < 0) 6367545ddfbeSMarcel Moolenaar rc = -1; /* Report failure */ 636842ff34c3SPhil Shafer else if (xo_flush_h(xop) < 0) 6369545ddfbeSMarcel Moolenaar rc = -1; 6370545ddfbeSMarcel Moolenaar } 637131337658SMarcel Moolenaar 6372d1a0d267SMarcel Moolenaar if (new_fmt) 6373d1a0d267SMarcel Moolenaar xo_free(new_fmt); 6374d1a0d267SMarcel Moolenaar 6375d1a0d267SMarcel Moolenaar /* 6376d1a0d267SMarcel Moolenaar * We've carried the gettext domainname inside our handle just for 6377d1a0d267SMarcel Moolenaar * convenience, but we need to ensure it doesn't survive across 6378d1a0d267SMarcel Moolenaar * xo_emit calls. 6379d1a0d267SMarcel Moolenaar */ 6380d1a0d267SMarcel Moolenaar if (xop->xo_gt_domain) { 6381d1a0d267SMarcel Moolenaar xo_free(xop->xo_gt_domain); 6382d1a0d267SMarcel Moolenaar xop->xo_gt_domain = NULL; 6383d1a0d267SMarcel Moolenaar } 6384d1a0d267SMarcel Moolenaar 63858a6eceffSPhil Shafer return (rc < 0) ? rc : xop->xo_columns; 638631337658SMarcel Moolenaar } 638731337658SMarcel Moolenaar 6388d1a0d267SMarcel Moolenaar /* 638942ff34c3SPhil Shafer * Parse and emit a set of fields 639042ff34c3SPhil Shafer */ 639142ff34c3SPhil Shafer static int 639242ff34c3SPhil Shafer xo_do_emit (xo_handle_t *xop, xo_emit_flags_t flags, const char *fmt) 639342ff34c3SPhil Shafer { 639442ff34c3SPhil Shafer xop->xo_columns = 0; /* Always reset it */ 639542ff34c3SPhil Shafer xop->xo_errno = errno; /* Save for "%m" */ 639642ff34c3SPhil Shafer 639742ff34c3SPhil Shafer if (fmt == NULL) 639842ff34c3SPhil Shafer return 0; 639942ff34c3SPhil Shafer 640042ff34c3SPhil Shafer unsigned max_fields; 640142ff34c3SPhil Shafer xo_field_info_t *fields = NULL; 640242ff34c3SPhil Shafer 640342ff34c3SPhil Shafer /* Adjust XOEF_RETAIN based on global flags */ 640442ff34c3SPhil Shafer if (XOF_ISSET(xop, XOF_RETAIN_ALL)) 640542ff34c3SPhil Shafer flags |= XOEF_RETAIN; 640642ff34c3SPhil Shafer if (XOF_ISSET(xop, XOF_RETAIN_NONE)) 640742ff34c3SPhil Shafer flags &= ~XOEF_RETAIN; 640842ff34c3SPhil Shafer 640942ff34c3SPhil Shafer /* 641042ff34c3SPhil Shafer * Check for 'retain' flag, telling us to retain the field 641142ff34c3SPhil Shafer * information. If we've already saved it, then we can avoid 641242ff34c3SPhil Shafer * re-parsing the format string. 641342ff34c3SPhil Shafer */ 641442ff34c3SPhil Shafer if (!(flags & XOEF_RETAIN) 641542ff34c3SPhil Shafer || xo_retain_find(fmt, &fields, &max_fields) != 0 641642ff34c3SPhil Shafer || fields == NULL) { 641742ff34c3SPhil Shafer 641842ff34c3SPhil Shafer /* Nothing retained; parse the format string */ 641942ff34c3SPhil Shafer max_fields = xo_count_fields(xop, fmt); 642042ff34c3SPhil Shafer fields = alloca(max_fields * sizeof(fields[0])); 642142ff34c3SPhil Shafer bzero(fields, max_fields * sizeof(fields[0])); 642242ff34c3SPhil Shafer 642342ff34c3SPhil Shafer if (xo_parse_fields(xop, fields, max_fields, fmt)) 642442ff34c3SPhil Shafer return -1; /* Warning already displayed */ 642542ff34c3SPhil Shafer 642642ff34c3SPhil Shafer if (flags & XOEF_RETAIN) { 642742ff34c3SPhil Shafer /* Retain the info */ 642842ff34c3SPhil Shafer xo_retain_add(fmt, fields, max_fields); 642942ff34c3SPhil Shafer } 643042ff34c3SPhil Shafer } 643142ff34c3SPhil Shafer 643242ff34c3SPhil Shafer return xo_do_emit_fields(xop, fields, max_fields, fmt); 643342ff34c3SPhil Shafer } 643442ff34c3SPhil Shafer 643542ff34c3SPhil Shafer /* 6436d1a0d267SMarcel Moolenaar * Rebuild a format string in a gettext-friendly format. This function 6437d1a0d267SMarcel Moolenaar * is exposed to tools can perform this function. See xo(1). 6438d1a0d267SMarcel Moolenaar */ 6439d1a0d267SMarcel Moolenaar char * 6440d1a0d267SMarcel Moolenaar xo_simplify_format (xo_handle_t *xop, const char *fmt, int with_numbers, 6441d1a0d267SMarcel Moolenaar xo_simplify_field_func_t field_cb) 6442d1a0d267SMarcel Moolenaar { 6443d1a0d267SMarcel Moolenaar xop = xo_default(xop); 6444d1a0d267SMarcel Moolenaar 6445d1a0d267SMarcel Moolenaar xop->xo_columns = 0; /* Always reset it */ 6446d1a0d267SMarcel Moolenaar xop->xo_errno = errno; /* Save for "%m" */ 6447d1a0d267SMarcel Moolenaar 6448d1a0d267SMarcel Moolenaar unsigned max_fields = xo_count_fields(xop, fmt); 6449d1a0d267SMarcel Moolenaar xo_field_info_t fields[max_fields]; 6450d1a0d267SMarcel Moolenaar 6451d1a0d267SMarcel Moolenaar bzero(fields, max_fields * sizeof(fields[0])); 6452d1a0d267SMarcel Moolenaar 6453d1a0d267SMarcel Moolenaar if (xo_parse_fields(xop, fields, max_fields, fmt)) 6454d1a0d267SMarcel Moolenaar return NULL; /* Warning already displayed */ 6455d1a0d267SMarcel Moolenaar 6456d1a0d267SMarcel Moolenaar xo_buffer_t xb; 6457d1a0d267SMarcel Moolenaar xo_buf_init(&xb); 6458d1a0d267SMarcel Moolenaar 6459d1a0d267SMarcel Moolenaar if (with_numbers) 6460d1a0d267SMarcel Moolenaar xo_gettext_finish_numbering_fields(xop, fmt, fields); 6461d1a0d267SMarcel Moolenaar 6462d1a0d267SMarcel Moolenaar if (xo_gettext_simplify_format(xop, &xb, fields, -1, fmt, field_cb)) 6463d1a0d267SMarcel Moolenaar return NULL; 6464d1a0d267SMarcel Moolenaar 6465d1a0d267SMarcel Moolenaar return xb.xb_bufp; 6466d1a0d267SMarcel Moolenaar } 6467d1a0d267SMarcel Moolenaar 64688a6eceffSPhil Shafer xo_ssize_t 646931337658SMarcel Moolenaar xo_emit_hv (xo_handle_t *xop, const char *fmt, va_list vap) 647031337658SMarcel Moolenaar { 64718a6eceffSPhil Shafer ssize_t rc; 647231337658SMarcel Moolenaar 647331337658SMarcel Moolenaar xop = xo_default(xop); 647431337658SMarcel Moolenaar va_copy(xop->xo_vap, vap); 647542ff34c3SPhil Shafer rc = xo_do_emit(xop, 0, fmt); 647631337658SMarcel Moolenaar va_end(xop->xo_vap); 647731337658SMarcel Moolenaar bzero(&xop->xo_vap, sizeof(xop->xo_vap)); 647831337658SMarcel Moolenaar 647931337658SMarcel Moolenaar return rc; 648031337658SMarcel Moolenaar } 648131337658SMarcel Moolenaar 64828a6eceffSPhil Shafer xo_ssize_t 648331337658SMarcel Moolenaar xo_emit_h (xo_handle_t *xop, const char *fmt, ...) 648431337658SMarcel Moolenaar { 64858a6eceffSPhil Shafer ssize_t rc; 648631337658SMarcel Moolenaar 648731337658SMarcel Moolenaar xop = xo_default(xop); 648831337658SMarcel Moolenaar va_start(xop->xo_vap, fmt); 648942ff34c3SPhil Shafer rc = xo_do_emit(xop, 0, fmt); 649031337658SMarcel Moolenaar va_end(xop->xo_vap); 649131337658SMarcel Moolenaar bzero(&xop->xo_vap, sizeof(xop->xo_vap)); 649231337658SMarcel Moolenaar 649331337658SMarcel Moolenaar return rc; 649431337658SMarcel Moolenaar } 649531337658SMarcel Moolenaar 64968a6eceffSPhil Shafer xo_ssize_t 649731337658SMarcel Moolenaar xo_emit (const char *fmt, ...) 649831337658SMarcel Moolenaar { 649931337658SMarcel Moolenaar xo_handle_t *xop = xo_default(NULL); 65008a6eceffSPhil Shafer ssize_t rc; 650131337658SMarcel Moolenaar 650231337658SMarcel Moolenaar va_start(xop->xo_vap, fmt); 650342ff34c3SPhil Shafer rc = xo_do_emit(xop, 0, fmt); 650431337658SMarcel Moolenaar va_end(xop->xo_vap); 650531337658SMarcel Moolenaar bzero(&xop->xo_vap, sizeof(xop->xo_vap)); 650631337658SMarcel Moolenaar 650731337658SMarcel Moolenaar return rc; 650831337658SMarcel Moolenaar } 650931337658SMarcel Moolenaar 65108a6eceffSPhil Shafer xo_ssize_t 651142ff34c3SPhil Shafer xo_emit_hvf (xo_handle_t *xop, xo_emit_flags_t flags, 651242ff34c3SPhil Shafer const char *fmt, va_list vap) 651342ff34c3SPhil Shafer { 65148a6eceffSPhil Shafer ssize_t rc; 651542ff34c3SPhil Shafer 651642ff34c3SPhil Shafer xop = xo_default(xop); 651742ff34c3SPhil Shafer va_copy(xop->xo_vap, vap); 651842ff34c3SPhil Shafer rc = xo_do_emit(xop, flags, fmt); 651942ff34c3SPhil Shafer va_end(xop->xo_vap); 652042ff34c3SPhil Shafer bzero(&xop->xo_vap, sizeof(xop->xo_vap)); 652142ff34c3SPhil Shafer 652242ff34c3SPhil Shafer return rc; 652342ff34c3SPhil Shafer } 652442ff34c3SPhil Shafer 65258a6eceffSPhil Shafer xo_ssize_t 652642ff34c3SPhil Shafer xo_emit_hf (xo_handle_t *xop, xo_emit_flags_t flags, const char *fmt, ...) 652742ff34c3SPhil Shafer { 65288a6eceffSPhil Shafer ssize_t rc; 652942ff34c3SPhil Shafer 653042ff34c3SPhil Shafer xop = xo_default(xop); 653142ff34c3SPhil Shafer va_start(xop->xo_vap, fmt); 653242ff34c3SPhil Shafer rc = xo_do_emit(xop, flags, fmt); 653342ff34c3SPhil Shafer va_end(xop->xo_vap); 653442ff34c3SPhil Shafer bzero(&xop->xo_vap, sizeof(xop->xo_vap)); 653542ff34c3SPhil Shafer 653642ff34c3SPhil Shafer return rc; 653742ff34c3SPhil Shafer } 653842ff34c3SPhil Shafer 65398a6eceffSPhil Shafer xo_ssize_t 654042ff34c3SPhil Shafer xo_emit_f (xo_emit_flags_t flags, const char *fmt, ...) 654142ff34c3SPhil Shafer { 654242ff34c3SPhil Shafer xo_handle_t *xop = xo_default(NULL); 65438a6eceffSPhil Shafer ssize_t rc; 654442ff34c3SPhil Shafer 654542ff34c3SPhil Shafer va_start(xop->xo_vap, fmt); 654642ff34c3SPhil Shafer rc = xo_do_emit(xop, flags, fmt); 654742ff34c3SPhil Shafer va_end(xop->xo_vap); 654842ff34c3SPhil Shafer bzero(&xop->xo_vap, sizeof(xop->xo_vap)); 654942ff34c3SPhil Shafer 655042ff34c3SPhil Shafer return rc; 655142ff34c3SPhil Shafer } 655242ff34c3SPhil Shafer 655342ff34c3SPhil Shafer /* 655442ff34c3SPhil Shafer * Emit a single field by providing the info information typically provided 655542ff34c3SPhil Shafer * inside the field description (role, modifiers, and formats). This is 655642ff34c3SPhil Shafer * a convenience function to avoid callers using snprintf to build field 655742ff34c3SPhil Shafer * descriptions. 655842ff34c3SPhil Shafer */ 65598a6eceffSPhil Shafer xo_ssize_t 656042ff34c3SPhil Shafer xo_emit_field_hv (xo_handle_t *xop, const char *rolmod, const char *contents, 656142ff34c3SPhil Shafer const char *fmt, const char *efmt, 656242ff34c3SPhil Shafer va_list vap) 656342ff34c3SPhil Shafer { 65648a6eceffSPhil Shafer ssize_t rc; 656542ff34c3SPhil Shafer 656642ff34c3SPhil Shafer xop = xo_default(xop); 656742ff34c3SPhil Shafer 656842ff34c3SPhil Shafer if (rolmod == NULL) 656942ff34c3SPhil Shafer rolmod = "V"; 657042ff34c3SPhil Shafer 657142ff34c3SPhil Shafer xo_field_info_t xfi; 657242ff34c3SPhil Shafer 657342ff34c3SPhil Shafer bzero(&xfi, sizeof(xfi)); 657442ff34c3SPhil Shafer 657542ff34c3SPhil Shafer const char *cp; 657642ff34c3SPhil Shafer cp = xo_parse_roles(xop, rolmod, rolmod, &xfi); 657742ff34c3SPhil Shafer if (cp == NULL) 657842ff34c3SPhil Shafer return -1; 657942ff34c3SPhil Shafer 658042ff34c3SPhil Shafer xfi.xfi_start = fmt; 658142ff34c3SPhil Shafer xfi.xfi_content = contents; 658242ff34c3SPhil Shafer xfi.xfi_format = fmt; 658342ff34c3SPhil Shafer xfi.xfi_encoding = efmt; 658442ff34c3SPhil Shafer xfi.xfi_clen = contents ? strlen(contents) : 0; 658542ff34c3SPhil Shafer xfi.xfi_flen = fmt ? strlen(fmt) : 0; 658642ff34c3SPhil Shafer xfi.xfi_elen = efmt ? strlen(efmt) : 0; 658742ff34c3SPhil Shafer 658842ff34c3SPhil Shafer /* If we have content, then we have a default format */ 658942ff34c3SPhil Shafer if (contents && fmt == NULL 659042ff34c3SPhil Shafer && xo_role_wants_default_format(xfi.xfi_ftype)) { 659142ff34c3SPhil Shafer xfi.xfi_format = xo_default_format; 659242ff34c3SPhil Shafer xfi.xfi_flen = 2; 659342ff34c3SPhil Shafer } 659442ff34c3SPhil Shafer 659542ff34c3SPhil Shafer va_copy(xop->xo_vap, vap); 659642ff34c3SPhil Shafer 659742ff34c3SPhil Shafer rc = xo_do_emit_fields(xop, &xfi, 1, fmt ?: contents ?: "field"); 659842ff34c3SPhil Shafer 659942ff34c3SPhil Shafer va_end(xop->xo_vap); 660042ff34c3SPhil Shafer 660142ff34c3SPhil Shafer return rc; 660242ff34c3SPhil Shafer } 660342ff34c3SPhil Shafer 66048a6eceffSPhil Shafer xo_ssize_t 660542ff34c3SPhil Shafer xo_emit_field_h (xo_handle_t *xop, const char *rolmod, const char *contents, 660642ff34c3SPhil Shafer const char *fmt, const char *efmt, ...) 660742ff34c3SPhil Shafer { 66088a6eceffSPhil Shafer ssize_t rc; 660942ff34c3SPhil Shafer va_list vap; 661042ff34c3SPhil Shafer 661142ff34c3SPhil Shafer va_start(vap, efmt); 661242ff34c3SPhil Shafer rc = xo_emit_field_hv(xop, rolmod, contents, fmt, efmt, vap); 661342ff34c3SPhil Shafer va_end(vap); 661442ff34c3SPhil Shafer 661542ff34c3SPhil Shafer return rc; 661642ff34c3SPhil Shafer } 661742ff34c3SPhil Shafer 66188a6eceffSPhil Shafer xo_ssize_t 661942ff34c3SPhil Shafer xo_emit_field (const char *rolmod, const char *contents, 662042ff34c3SPhil Shafer const char *fmt, const char *efmt, ...) 662142ff34c3SPhil Shafer { 66228a6eceffSPhil Shafer ssize_t rc; 662342ff34c3SPhil Shafer va_list vap; 662442ff34c3SPhil Shafer 662542ff34c3SPhil Shafer va_start(vap, efmt); 662642ff34c3SPhil Shafer rc = xo_emit_field_hv(NULL, rolmod, contents, fmt, efmt, vap); 662742ff34c3SPhil Shafer va_end(vap); 662842ff34c3SPhil Shafer 662942ff34c3SPhil Shafer return rc; 663042ff34c3SPhil Shafer } 663142ff34c3SPhil Shafer 66328a6eceffSPhil Shafer xo_ssize_t 663331337658SMarcel Moolenaar xo_attr_hv (xo_handle_t *xop, const char *name, const char *fmt, va_list vap) 663431337658SMarcel Moolenaar { 66358a6eceffSPhil Shafer const ssize_t extra = 5; /* space, equals, quote, quote, and nul */ 663631337658SMarcel Moolenaar xop = xo_default(xop); 663731337658SMarcel Moolenaar 66388a6eceffSPhil Shafer ssize_t rc = 0; 66398a6eceffSPhil Shafer ssize_t nlen = strlen(name); 664031337658SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_attrs; 66418a6eceffSPhil Shafer ssize_t name_offset, value_offset; 664231337658SMarcel Moolenaar 6643d1a0d267SMarcel Moolenaar switch (xo_style(xop)) { 6644d1a0d267SMarcel Moolenaar case XO_STYLE_XML: 664531337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, nlen + extra)) 664631337658SMarcel Moolenaar return -1; 664731337658SMarcel Moolenaar 664831337658SMarcel Moolenaar *xbp->xb_curp++ = ' '; 664931337658SMarcel Moolenaar memcpy(xbp->xb_curp, name, nlen); 665031337658SMarcel Moolenaar xbp->xb_curp += nlen; 665131337658SMarcel Moolenaar *xbp->xb_curp++ = '='; 665231337658SMarcel Moolenaar *xbp->xb_curp++ = '"'; 665331337658SMarcel Moolenaar 6654d1a0d267SMarcel Moolenaar rc = xo_vsnprintf(xop, xbp, fmt, vap); 665531337658SMarcel Moolenaar 6656d1a0d267SMarcel Moolenaar if (rc >= 0) { 665731337658SMarcel Moolenaar rc = xo_escape_xml(xbp, rc, 1); 665831337658SMarcel Moolenaar xbp->xb_curp += rc; 665931337658SMarcel Moolenaar } 666031337658SMarcel Moolenaar 666131337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, 2)) 666231337658SMarcel Moolenaar return -1; 666331337658SMarcel Moolenaar 666431337658SMarcel Moolenaar *xbp->xb_curp++ = '"'; 666531337658SMarcel Moolenaar *xbp->xb_curp = '\0'; 666631337658SMarcel Moolenaar 6667d1a0d267SMarcel Moolenaar rc += nlen + extra; 6668d1a0d267SMarcel Moolenaar break; 6669d1a0d267SMarcel Moolenaar 6670d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER: 6671d1a0d267SMarcel Moolenaar name_offset = xo_buf_offset(xbp); 6672d1a0d267SMarcel Moolenaar xo_buf_append(xbp, name, nlen); 6673d1a0d267SMarcel Moolenaar xo_buf_append(xbp, "", 1); 6674d1a0d267SMarcel Moolenaar 6675d1a0d267SMarcel Moolenaar value_offset = xo_buf_offset(xbp); 6676d1a0d267SMarcel Moolenaar rc = xo_vsnprintf(xop, xbp, fmt, vap); 6677d1a0d267SMarcel Moolenaar if (rc >= 0) { 6678d1a0d267SMarcel Moolenaar xbp->xb_curp += rc; 6679d1a0d267SMarcel Moolenaar *xbp->xb_curp = '\0'; 6680d1a0d267SMarcel Moolenaar rc = xo_encoder_handle(xop, XO_OP_ATTRIBUTE, 6681d1a0d267SMarcel Moolenaar xo_buf_data(xbp, name_offset), 6682f2b7bf8aSPhil Shafer xo_buf_data(xbp, value_offset), 0); 6683d1a0d267SMarcel Moolenaar } 6684d1a0d267SMarcel Moolenaar } 6685d1a0d267SMarcel Moolenaar 6686d1a0d267SMarcel Moolenaar return rc; 668731337658SMarcel Moolenaar } 668831337658SMarcel Moolenaar 66898a6eceffSPhil Shafer xo_ssize_t 669031337658SMarcel Moolenaar xo_attr_h (xo_handle_t *xop, const char *name, const char *fmt, ...) 669131337658SMarcel Moolenaar { 66928a6eceffSPhil Shafer ssize_t rc; 669331337658SMarcel Moolenaar va_list vap; 669431337658SMarcel Moolenaar 669531337658SMarcel Moolenaar va_start(vap, fmt); 669631337658SMarcel Moolenaar rc = xo_attr_hv(xop, name, fmt, vap); 669731337658SMarcel Moolenaar va_end(vap); 669831337658SMarcel Moolenaar 669931337658SMarcel Moolenaar return rc; 670031337658SMarcel Moolenaar } 670131337658SMarcel Moolenaar 67028a6eceffSPhil Shafer xo_ssize_t 670331337658SMarcel Moolenaar xo_attr (const char *name, const char *fmt, ...) 670431337658SMarcel Moolenaar { 67058a6eceffSPhil Shafer ssize_t rc; 670631337658SMarcel Moolenaar va_list vap; 670731337658SMarcel Moolenaar 670831337658SMarcel Moolenaar va_start(vap, fmt); 670931337658SMarcel Moolenaar rc = xo_attr_hv(NULL, name, fmt, vap); 671031337658SMarcel Moolenaar va_end(vap); 671131337658SMarcel Moolenaar 671231337658SMarcel Moolenaar return rc; 671331337658SMarcel Moolenaar } 671431337658SMarcel Moolenaar 671531337658SMarcel Moolenaar static void 671631337658SMarcel Moolenaar xo_stack_set_flags (xo_handle_t *xop) 671731337658SMarcel Moolenaar { 6718d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_NOT_FIRST)) { 671931337658SMarcel Moolenaar xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth]; 672031337658SMarcel Moolenaar 672131337658SMarcel Moolenaar xsp->xs_flags |= XSF_NOT_FIRST; 6722d1a0d267SMarcel Moolenaar XOF_CLEAR(xop, XOF_NOT_FIRST); 672331337658SMarcel Moolenaar } 672431337658SMarcel Moolenaar } 672531337658SMarcel Moolenaar 672631337658SMarcel Moolenaar static void 672731337658SMarcel Moolenaar xo_depth_change (xo_handle_t *xop, const char *name, 6728545ddfbeSMarcel Moolenaar int delta, int indent, xo_state_t state, xo_xsf_flags_t flags) 672931337658SMarcel Moolenaar { 6730788ca347SMarcel Moolenaar if (xo_style(xop) == XO_STYLE_HTML || xo_style(xop) == XO_STYLE_TEXT) 6731545ddfbeSMarcel Moolenaar indent = 0; 6732545ddfbeSMarcel Moolenaar 6733d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_DTRT)) 673431337658SMarcel Moolenaar flags |= XSF_DTRT; 673531337658SMarcel Moolenaar 673631337658SMarcel Moolenaar if (delta >= 0) { /* Push operation */ 673731337658SMarcel Moolenaar if (xo_depth_check(xop, xop->xo_depth + delta)) 673831337658SMarcel Moolenaar return; 673931337658SMarcel Moolenaar 674031337658SMarcel Moolenaar xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth + delta]; 674131337658SMarcel Moolenaar xsp->xs_flags = flags; 6742545ddfbeSMarcel Moolenaar xsp->xs_state = state; 674331337658SMarcel Moolenaar xo_stack_set_flags(xop); 674431337658SMarcel Moolenaar 6745545ddfbeSMarcel Moolenaar if (name == NULL) 6746545ddfbeSMarcel Moolenaar name = XO_FAILURE_NAME; 674731337658SMarcel Moolenaar 6748d1a0d267SMarcel Moolenaar xsp->xs_name = xo_strndup(name, -1); 674931337658SMarcel Moolenaar 675031337658SMarcel Moolenaar } else { /* Pop operation */ 675131337658SMarcel Moolenaar if (xop->xo_depth == 0) { 6752d1a0d267SMarcel Moolenaar if (!XOF_ISSET(xop, XOF_IGNORE_CLOSE)) 675331337658SMarcel Moolenaar xo_failure(xop, "close with empty stack: '%s'", name); 675431337658SMarcel Moolenaar return; 675531337658SMarcel Moolenaar } 675631337658SMarcel Moolenaar 675731337658SMarcel Moolenaar xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth]; 6758d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_WARN)) { 675931337658SMarcel Moolenaar const char *top = xsp->xs_name; 6760f2b7bf8aSPhil Shafer if (top != NULL && name != NULL && strcmp(name, top) != 0) { 676131337658SMarcel Moolenaar xo_failure(xop, "incorrect close: '%s' .vs. '%s'", 676231337658SMarcel Moolenaar name, top); 676331337658SMarcel Moolenaar return; 676431337658SMarcel Moolenaar } 676531337658SMarcel Moolenaar if ((xsp->xs_flags & XSF_LIST) != (flags & XSF_LIST)) { 676631337658SMarcel Moolenaar xo_failure(xop, "list close on list confict: '%s'", 676731337658SMarcel Moolenaar name); 676831337658SMarcel Moolenaar return; 676931337658SMarcel Moolenaar } 677031337658SMarcel Moolenaar if ((xsp->xs_flags & XSF_INSTANCE) != (flags & XSF_INSTANCE)) { 677131337658SMarcel Moolenaar xo_failure(xop, "list close on instance confict: '%s'", 677231337658SMarcel Moolenaar name); 677331337658SMarcel Moolenaar return; 677431337658SMarcel Moolenaar } 677531337658SMarcel Moolenaar } 677631337658SMarcel Moolenaar 677731337658SMarcel Moolenaar if (xsp->xs_name) { 677831337658SMarcel Moolenaar xo_free(xsp->xs_name); 677931337658SMarcel Moolenaar xsp->xs_name = NULL; 678031337658SMarcel Moolenaar } 678131337658SMarcel Moolenaar if (xsp->xs_keys) { 678231337658SMarcel Moolenaar xo_free(xsp->xs_keys); 678331337658SMarcel Moolenaar xsp->xs_keys = NULL; 678431337658SMarcel Moolenaar } 678531337658SMarcel Moolenaar } 678631337658SMarcel Moolenaar 678731337658SMarcel Moolenaar xop->xo_depth += delta; /* Record new depth */ 678831337658SMarcel Moolenaar xop->xo_indent += indent; 678931337658SMarcel Moolenaar } 679031337658SMarcel Moolenaar 679131337658SMarcel Moolenaar void 679231337658SMarcel Moolenaar xo_set_depth (xo_handle_t *xop, int depth) 679331337658SMarcel Moolenaar { 679431337658SMarcel Moolenaar xop = xo_default(xop); 679531337658SMarcel Moolenaar 679631337658SMarcel Moolenaar if (xo_depth_check(xop, depth)) 679731337658SMarcel Moolenaar return; 679831337658SMarcel Moolenaar 679931337658SMarcel Moolenaar xop->xo_depth += depth; 680031337658SMarcel Moolenaar xop->xo_indent += depth; 680131337658SMarcel Moolenaar } 680231337658SMarcel Moolenaar 680331337658SMarcel Moolenaar static xo_xsf_flags_t 68048a6eceffSPhil Shafer xo_stack_flags (xo_xof_flags_t xflags) 680531337658SMarcel Moolenaar { 680631337658SMarcel Moolenaar if (xflags & XOF_DTRT) 680731337658SMarcel Moolenaar return XSF_DTRT; 680831337658SMarcel Moolenaar return 0; 680931337658SMarcel Moolenaar } 681031337658SMarcel Moolenaar 6811788ca347SMarcel Moolenaar static void 6812788ca347SMarcel Moolenaar xo_emit_top (xo_handle_t *xop, const char *ppn) 6813788ca347SMarcel Moolenaar { 6814788ca347SMarcel Moolenaar xo_printf(xop, "%*s{%s", xo_indent(xop), "", ppn); 6815d1a0d267SMarcel Moolenaar XOIF_SET(xop, XOIF_TOP_EMITTED); 6816788ca347SMarcel Moolenaar 6817788ca347SMarcel Moolenaar if (xop->xo_version) { 6818788ca347SMarcel Moolenaar xo_printf(xop, "%*s\"__version\": \"%s\", %s", 6819788ca347SMarcel Moolenaar xo_indent(xop), "", xop->xo_version, ppn); 6820788ca347SMarcel Moolenaar xo_free(xop->xo_version); 6821788ca347SMarcel Moolenaar xop->xo_version = NULL; 6822788ca347SMarcel Moolenaar } 6823788ca347SMarcel Moolenaar } 6824788ca347SMarcel Moolenaar 68258a6eceffSPhil Shafer static ssize_t 6826545ddfbeSMarcel Moolenaar xo_do_open_container (xo_handle_t *xop, xo_xof_flags_t flags, const char *name) 682731337658SMarcel Moolenaar { 68288a6eceffSPhil Shafer ssize_t rc = 0; 6829d1a0d267SMarcel Moolenaar const char *ppn = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : ""; 683031337658SMarcel Moolenaar const char *pre_nl = ""; 683131337658SMarcel Moolenaar 683231337658SMarcel Moolenaar if (name == NULL) { 683331337658SMarcel Moolenaar xo_failure(xop, "NULL passed for container name"); 683431337658SMarcel Moolenaar name = XO_FAILURE_NAME; 683531337658SMarcel Moolenaar } 683631337658SMarcel Moolenaar 683731337658SMarcel Moolenaar flags |= xop->xo_flags; /* Pick up handle flags */ 683831337658SMarcel Moolenaar 6839788ca347SMarcel Moolenaar switch (xo_style(xop)) { 684031337658SMarcel Moolenaar case XO_STYLE_XML: 6841545ddfbeSMarcel Moolenaar rc = xo_printf(xop, "%*s<%s", xo_indent(xop), "", name); 6842545ddfbeSMarcel Moolenaar 6843545ddfbeSMarcel Moolenaar if (xop->xo_attrs.xb_curp != xop->xo_attrs.xb_bufp) { 6844545ddfbeSMarcel Moolenaar rc += xop->xo_attrs.xb_curp - xop->xo_attrs.xb_bufp; 6845545ddfbeSMarcel Moolenaar xo_data_append(xop, xop->xo_attrs.xb_bufp, 6846545ddfbeSMarcel Moolenaar xop->xo_attrs.xb_curp - xop->xo_attrs.xb_bufp); 6847545ddfbeSMarcel Moolenaar xop->xo_attrs.xb_curp = xop->xo_attrs.xb_bufp; 6848545ddfbeSMarcel Moolenaar } 6849545ddfbeSMarcel Moolenaar 6850545ddfbeSMarcel Moolenaar rc += xo_printf(xop, ">%s", ppn); 685131337658SMarcel Moolenaar break; 685231337658SMarcel Moolenaar 685331337658SMarcel Moolenaar case XO_STYLE_JSON: 685431337658SMarcel Moolenaar xo_stack_set_flags(xop); 685531337658SMarcel Moolenaar 6856d1a0d267SMarcel Moolenaar if (!XOF_ISSET(xop, XOF_NO_TOP) 6857d1a0d267SMarcel Moolenaar && !XOIF_ISSET(xop, XOIF_TOP_EMITTED)) 6858788ca347SMarcel Moolenaar xo_emit_top(xop, ppn); 685931337658SMarcel Moolenaar 686031337658SMarcel Moolenaar if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST) 6861d1a0d267SMarcel Moolenaar pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? ",\n" : ", "; 686231337658SMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 686331337658SMarcel Moolenaar 686431337658SMarcel Moolenaar rc = xo_printf(xop, "%s%*s\"%s\": {%s", 686531337658SMarcel Moolenaar pre_nl, xo_indent(xop), "", name, ppn); 686631337658SMarcel Moolenaar break; 6867d1a0d267SMarcel Moolenaar 6868d1a0d267SMarcel Moolenaar case XO_STYLE_SDPARAMS: 6869d1a0d267SMarcel Moolenaar break; 6870d1a0d267SMarcel Moolenaar 6871d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER: 6872f2b7bf8aSPhil Shafer rc = xo_encoder_handle(xop, XO_OP_OPEN_CONTAINER, name, NULL, flags); 6873d1a0d267SMarcel Moolenaar break; 687431337658SMarcel Moolenaar } 687531337658SMarcel Moolenaar 6876545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, 1, 1, XSS_OPEN_CONTAINER, 6877545ddfbeSMarcel Moolenaar xo_stack_flags(flags)); 6878545ddfbeSMarcel Moolenaar 687931337658SMarcel Moolenaar return rc; 688031337658SMarcel Moolenaar } 688131337658SMarcel Moolenaar 6882545ddfbeSMarcel Moolenaar static int 6883545ddfbeSMarcel Moolenaar xo_open_container_hf (xo_handle_t *xop, xo_xof_flags_t flags, const char *name) 6884545ddfbeSMarcel Moolenaar { 6885545ddfbeSMarcel Moolenaar return xo_transition(xop, flags, name, XSS_OPEN_CONTAINER); 6886545ddfbeSMarcel Moolenaar } 6887545ddfbeSMarcel Moolenaar 68888a6eceffSPhil Shafer xo_ssize_t 688931337658SMarcel Moolenaar xo_open_container_h (xo_handle_t *xop, const char *name) 689031337658SMarcel Moolenaar { 689131337658SMarcel Moolenaar return xo_open_container_hf(xop, 0, name); 689231337658SMarcel Moolenaar } 689331337658SMarcel Moolenaar 68948a6eceffSPhil Shafer xo_ssize_t 689531337658SMarcel Moolenaar xo_open_container (const char *name) 689631337658SMarcel Moolenaar { 689731337658SMarcel Moolenaar return xo_open_container_hf(NULL, 0, name); 689831337658SMarcel Moolenaar } 689931337658SMarcel Moolenaar 69008a6eceffSPhil Shafer xo_ssize_t 690131337658SMarcel Moolenaar xo_open_container_hd (xo_handle_t *xop, const char *name) 690231337658SMarcel Moolenaar { 690331337658SMarcel Moolenaar return xo_open_container_hf(xop, XOF_DTRT, name); 690431337658SMarcel Moolenaar } 690531337658SMarcel Moolenaar 69068a6eceffSPhil Shafer xo_ssize_t 690731337658SMarcel Moolenaar xo_open_container_d (const char *name) 690831337658SMarcel Moolenaar { 690931337658SMarcel Moolenaar return xo_open_container_hf(NULL, XOF_DTRT, name); 691031337658SMarcel Moolenaar } 691131337658SMarcel Moolenaar 6912545ddfbeSMarcel Moolenaar static int 6913545ddfbeSMarcel Moolenaar xo_do_close_container (xo_handle_t *xop, const char *name) 691431337658SMarcel Moolenaar { 691531337658SMarcel Moolenaar xop = xo_default(xop); 691631337658SMarcel Moolenaar 69178a6eceffSPhil Shafer ssize_t rc = 0; 6918d1a0d267SMarcel Moolenaar const char *ppn = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : ""; 691931337658SMarcel Moolenaar const char *pre_nl = ""; 692031337658SMarcel Moolenaar 692131337658SMarcel Moolenaar if (name == NULL) { 692231337658SMarcel Moolenaar xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth]; 692331337658SMarcel Moolenaar 692431337658SMarcel Moolenaar name = xsp->xs_name; 692531337658SMarcel Moolenaar if (name) { 69268a6eceffSPhil Shafer ssize_t len = strlen(name) + 1; 692731337658SMarcel Moolenaar /* We need to make a local copy; xo_depth_change will free it */ 692831337658SMarcel Moolenaar char *cp = alloca(len); 692931337658SMarcel Moolenaar memcpy(cp, name, len); 693031337658SMarcel Moolenaar name = cp; 6931545ddfbeSMarcel Moolenaar } else if (!(xsp->xs_flags & XSF_DTRT)) { 6932545ddfbeSMarcel Moolenaar xo_failure(xop, "missing name without 'dtrt' mode"); 693331337658SMarcel Moolenaar name = XO_FAILURE_NAME; 693431337658SMarcel Moolenaar } 6935545ddfbeSMarcel Moolenaar } 693631337658SMarcel Moolenaar 6937788ca347SMarcel Moolenaar switch (xo_style(xop)) { 693831337658SMarcel Moolenaar case XO_STYLE_XML: 6939545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, -1, -1, XSS_CLOSE_CONTAINER, 0); 694031337658SMarcel Moolenaar rc = xo_printf(xop, "%*s</%s>%s", xo_indent(xop), "", name, ppn); 694131337658SMarcel Moolenaar break; 694231337658SMarcel Moolenaar 694331337658SMarcel Moolenaar case XO_STYLE_JSON: 6944d1a0d267SMarcel Moolenaar pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : ""; 694531337658SMarcel Moolenaar ppn = (xop->xo_depth <= 1) ? "\n" : ""; 694631337658SMarcel Moolenaar 6947545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, -1, -1, XSS_CLOSE_CONTAINER, 0); 694831337658SMarcel Moolenaar rc = xo_printf(xop, "%s%*s}%s", pre_nl, xo_indent(xop), "", ppn); 694931337658SMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 695031337658SMarcel Moolenaar break; 695131337658SMarcel Moolenaar 695231337658SMarcel Moolenaar case XO_STYLE_HTML: 695331337658SMarcel Moolenaar case XO_STYLE_TEXT: 6954545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, -1, 0, XSS_CLOSE_CONTAINER, 0); 695531337658SMarcel Moolenaar break; 6956d1a0d267SMarcel Moolenaar 6957d1a0d267SMarcel Moolenaar case XO_STYLE_SDPARAMS: 6958d1a0d267SMarcel Moolenaar break; 6959d1a0d267SMarcel Moolenaar 6960d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER: 6961d1a0d267SMarcel Moolenaar xo_depth_change(xop, name, -1, 0, XSS_CLOSE_CONTAINER, 0); 6962f2b7bf8aSPhil Shafer rc = xo_encoder_handle(xop, XO_OP_CLOSE_CONTAINER, name, NULL, 0); 6963d1a0d267SMarcel Moolenaar break; 696431337658SMarcel Moolenaar } 696531337658SMarcel Moolenaar 696631337658SMarcel Moolenaar return rc; 696731337658SMarcel Moolenaar } 696831337658SMarcel Moolenaar 69698a6eceffSPhil Shafer xo_ssize_t 6970545ddfbeSMarcel Moolenaar xo_close_container_h (xo_handle_t *xop, const char *name) 6971545ddfbeSMarcel Moolenaar { 6972545ddfbeSMarcel Moolenaar return xo_transition(xop, 0, name, XSS_CLOSE_CONTAINER); 6973545ddfbeSMarcel Moolenaar } 6974545ddfbeSMarcel Moolenaar 69758a6eceffSPhil Shafer xo_ssize_t 697631337658SMarcel Moolenaar xo_close_container (const char *name) 697731337658SMarcel Moolenaar { 697831337658SMarcel Moolenaar return xo_close_container_h(NULL, name); 697931337658SMarcel Moolenaar } 698031337658SMarcel Moolenaar 69818a6eceffSPhil Shafer xo_ssize_t 698231337658SMarcel Moolenaar xo_close_container_hd (xo_handle_t *xop) 698331337658SMarcel Moolenaar { 698431337658SMarcel Moolenaar return xo_close_container_h(xop, NULL); 698531337658SMarcel Moolenaar } 698631337658SMarcel Moolenaar 69878a6eceffSPhil Shafer xo_ssize_t 698831337658SMarcel Moolenaar xo_close_container_d (void) 698931337658SMarcel Moolenaar { 699031337658SMarcel Moolenaar return xo_close_container_h(NULL, NULL); 699131337658SMarcel Moolenaar } 699231337658SMarcel Moolenaar 699331337658SMarcel Moolenaar static int 6994545ddfbeSMarcel Moolenaar xo_do_open_list (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name) 699531337658SMarcel Moolenaar { 69968a6eceffSPhil Shafer ssize_t rc = 0; 6997545ddfbeSMarcel Moolenaar int indent = 0; 6998545ddfbeSMarcel Moolenaar 699931337658SMarcel Moolenaar xop = xo_default(xop); 700031337658SMarcel Moolenaar 7001d1a0d267SMarcel Moolenaar const char *ppn = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : ""; 700231337658SMarcel Moolenaar const char *pre_nl = ""; 700331337658SMarcel Moolenaar 7004d1a0d267SMarcel Moolenaar switch (xo_style(xop)) { 7005d1a0d267SMarcel Moolenaar case XO_STYLE_JSON: 7006d1a0d267SMarcel Moolenaar 7007545ddfbeSMarcel Moolenaar indent = 1; 7008d1a0d267SMarcel Moolenaar if (!XOF_ISSET(xop, XOF_NO_TOP) 7009d1a0d267SMarcel Moolenaar && !XOIF_ISSET(xop, XOIF_TOP_EMITTED)) 7010788ca347SMarcel Moolenaar xo_emit_top(xop, ppn); 701131337658SMarcel Moolenaar 701231337658SMarcel Moolenaar if (name == NULL) { 701331337658SMarcel Moolenaar xo_failure(xop, "NULL passed for list name"); 701431337658SMarcel Moolenaar name = XO_FAILURE_NAME; 701531337658SMarcel Moolenaar } 701631337658SMarcel Moolenaar 701731337658SMarcel Moolenaar xo_stack_set_flags(xop); 701831337658SMarcel Moolenaar 701931337658SMarcel Moolenaar if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST) 7020d1a0d267SMarcel Moolenaar pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? ",\n" : ", "; 702131337658SMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 702231337658SMarcel Moolenaar 702331337658SMarcel Moolenaar rc = xo_printf(xop, "%s%*s\"%s\": [%s", 702431337658SMarcel Moolenaar pre_nl, xo_indent(xop), "", name, ppn); 7025d1a0d267SMarcel Moolenaar break; 7026d1a0d267SMarcel Moolenaar 7027d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER: 7028f2b7bf8aSPhil Shafer rc = xo_encoder_handle(xop, XO_OP_OPEN_LIST, name, NULL, flags); 7029d1a0d267SMarcel Moolenaar break; 7030545ddfbeSMarcel Moolenaar } 7031545ddfbeSMarcel Moolenaar 7032545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, 1, indent, XSS_OPEN_LIST, 7033545ddfbeSMarcel Moolenaar XSF_LIST | xo_stack_flags(flags)); 703431337658SMarcel Moolenaar 703531337658SMarcel Moolenaar return rc; 703631337658SMarcel Moolenaar } 703731337658SMarcel Moolenaar 7038545ddfbeSMarcel Moolenaar static int 7039545ddfbeSMarcel Moolenaar xo_open_list_hf (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name) 7040545ddfbeSMarcel Moolenaar { 7041545ddfbeSMarcel Moolenaar return xo_transition(xop, flags, name, XSS_OPEN_LIST); 7042545ddfbeSMarcel Moolenaar } 7043545ddfbeSMarcel Moolenaar 70448a6eceffSPhil Shafer xo_ssize_t 704542ff34c3SPhil Shafer xo_open_list_h (xo_handle_t *xop, const char *name) 704631337658SMarcel Moolenaar { 704731337658SMarcel Moolenaar return xo_open_list_hf(xop, 0, name); 704831337658SMarcel Moolenaar } 704931337658SMarcel Moolenaar 70508a6eceffSPhil Shafer xo_ssize_t 705131337658SMarcel Moolenaar xo_open_list (const char *name) 705231337658SMarcel Moolenaar { 705331337658SMarcel Moolenaar return xo_open_list_hf(NULL, 0, name); 705431337658SMarcel Moolenaar } 705531337658SMarcel Moolenaar 70568a6eceffSPhil Shafer xo_ssize_t 705742ff34c3SPhil Shafer xo_open_list_hd (xo_handle_t *xop, const char *name) 705831337658SMarcel Moolenaar { 705931337658SMarcel Moolenaar return xo_open_list_hf(xop, XOF_DTRT, name); 706031337658SMarcel Moolenaar } 706131337658SMarcel Moolenaar 70628a6eceffSPhil Shafer xo_ssize_t 706331337658SMarcel Moolenaar xo_open_list_d (const char *name) 706431337658SMarcel Moolenaar { 706531337658SMarcel Moolenaar return xo_open_list_hf(NULL, XOF_DTRT, name); 706631337658SMarcel Moolenaar } 706731337658SMarcel Moolenaar 7068545ddfbeSMarcel Moolenaar static int 7069545ddfbeSMarcel Moolenaar xo_do_close_list (xo_handle_t *xop, const char *name) 707031337658SMarcel Moolenaar { 70718a6eceffSPhil Shafer ssize_t rc = 0; 707231337658SMarcel Moolenaar const char *pre_nl = ""; 707331337658SMarcel Moolenaar 707431337658SMarcel Moolenaar if (name == NULL) { 707531337658SMarcel Moolenaar xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth]; 707631337658SMarcel Moolenaar 707731337658SMarcel Moolenaar name = xsp->xs_name; 707831337658SMarcel Moolenaar if (name) { 70798a6eceffSPhil Shafer ssize_t len = strlen(name) + 1; 708031337658SMarcel Moolenaar /* We need to make a local copy; xo_depth_change will free it */ 708131337658SMarcel Moolenaar char *cp = alloca(len); 708231337658SMarcel Moolenaar memcpy(cp, name, len); 708331337658SMarcel Moolenaar name = cp; 7084545ddfbeSMarcel Moolenaar } else if (!(xsp->xs_flags & XSF_DTRT)) { 7085545ddfbeSMarcel Moolenaar xo_failure(xop, "missing name without 'dtrt' mode"); 708631337658SMarcel Moolenaar name = XO_FAILURE_NAME; 708731337658SMarcel Moolenaar } 7088545ddfbeSMarcel Moolenaar } 708931337658SMarcel Moolenaar 7090d1a0d267SMarcel Moolenaar switch (xo_style(xop)) { 7091d1a0d267SMarcel Moolenaar case XO_STYLE_JSON: 709231337658SMarcel Moolenaar if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST) 7093d1a0d267SMarcel Moolenaar pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : ""; 709431337658SMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 709531337658SMarcel Moolenaar 7096545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, -1, -1, XSS_CLOSE_LIST, XSF_LIST); 709731337658SMarcel Moolenaar rc = xo_printf(xop, "%s%*s]", pre_nl, xo_indent(xop), ""); 709831337658SMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 7099d1a0d267SMarcel Moolenaar break; 710031337658SMarcel Moolenaar 7101d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER: 7102d1a0d267SMarcel Moolenaar xo_depth_change(xop, name, -1, 0, XSS_CLOSE_LIST, XSF_LIST); 7103f2b7bf8aSPhil Shafer rc = xo_encoder_handle(xop, XO_OP_CLOSE_LIST, name, NULL, 0); 7104d1a0d267SMarcel Moolenaar break; 7105d1a0d267SMarcel Moolenaar 7106d1a0d267SMarcel Moolenaar default: 7107545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, -1, 0, XSS_CLOSE_LIST, XSF_LIST); 7108545ddfbeSMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 7109d1a0d267SMarcel Moolenaar break; 7110545ddfbeSMarcel Moolenaar } 7111545ddfbeSMarcel Moolenaar 7112a0f704ffSMarcel Moolenaar return rc; 711331337658SMarcel Moolenaar } 711431337658SMarcel Moolenaar 71158a6eceffSPhil Shafer xo_ssize_t 7116545ddfbeSMarcel Moolenaar xo_close_list_h (xo_handle_t *xop, const char *name) 7117545ddfbeSMarcel Moolenaar { 7118545ddfbeSMarcel Moolenaar return xo_transition(xop, 0, name, XSS_CLOSE_LIST); 7119545ddfbeSMarcel Moolenaar } 7120545ddfbeSMarcel Moolenaar 71218a6eceffSPhil Shafer xo_ssize_t 712231337658SMarcel Moolenaar xo_close_list (const char *name) 712331337658SMarcel Moolenaar { 712431337658SMarcel Moolenaar return xo_close_list_h(NULL, name); 712531337658SMarcel Moolenaar } 712631337658SMarcel Moolenaar 71278a6eceffSPhil Shafer xo_ssize_t 712831337658SMarcel Moolenaar xo_close_list_hd (xo_handle_t *xop) 712931337658SMarcel Moolenaar { 713031337658SMarcel Moolenaar return xo_close_list_h(xop, NULL); 713131337658SMarcel Moolenaar } 713231337658SMarcel Moolenaar 71338a6eceffSPhil Shafer xo_ssize_t 713431337658SMarcel Moolenaar xo_close_list_d (void) 713531337658SMarcel Moolenaar { 713631337658SMarcel Moolenaar return xo_close_list_h(NULL, NULL); 713731337658SMarcel Moolenaar } 713831337658SMarcel Moolenaar 713931337658SMarcel Moolenaar static int 7140545ddfbeSMarcel Moolenaar xo_do_open_leaf_list (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name) 7141545ddfbeSMarcel Moolenaar { 71428a6eceffSPhil Shafer ssize_t rc = 0; 7143545ddfbeSMarcel Moolenaar int indent = 0; 7144545ddfbeSMarcel Moolenaar 7145545ddfbeSMarcel Moolenaar xop = xo_default(xop); 7146545ddfbeSMarcel Moolenaar 7147d1a0d267SMarcel Moolenaar const char *ppn = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : ""; 7148545ddfbeSMarcel Moolenaar const char *pre_nl = ""; 7149545ddfbeSMarcel Moolenaar 7150d1a0d267SMarcel Moolenaar switch (xo_style(xop)) { 7151d1a0d267SMarcel Moolenaar case XO_STYLE_JSON: 7152545ddfbeSMarcel Moolenaar indent = 1; 7153545ddfbeSMarcel Moolenaar 7154d1a0d267SMarcel Moolenaar if (!XOF_ISSET(xop, XOF_NO_TOP)) { 7155d1a0d267SMarcel Moolenaar if (!XOIF_ISSET(xop, XOIF_TOP_EMITTED)) { 7156545ddfbeSMarcel Moolenaar xo_printf(xop, "%*s{%s", xo_indent(xop), "", ppn); 7157d1a0d267SMarcel Moolenaar XOIF_SET(xop, XOIF_TOP_EMITTED); 7158545ddfbeSMarcel Moolenaar } 7159545ddfbeSMarcel Moolenaar } 7160545ddfbeSMarcel Moolenaar 7161545ddfbeSMarcel Moolenaar if (name == NULL) { 7162545ddfbeSMarcel Moolenaar xo_failure(xop, "NULL passed for list name"); 7163545ddfbeSMarcel Moolenaar name = XO_FAILURE_NAME; 7164545ddfbeSMarcel Moolenaar } 7165545ddfbeSMarcel Moolenaar 7166545ddfbeSMarcel Moolenaar xo_stack_set_flags(xop); 7167545ddfbeSMarcel Moolenaar 7168545ddfbeSMarcel Moolenaar if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST) 7169d1a0d267SMarcel Moolenaar pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? ",\n" : ", "; 7170545ddfbeSMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 7171545ddfbeSMarcel Moolenaar 7172545ddfbeSMarcel Moolenaar rc = xo_printf(xop, "%s%*s\"%s\": [%s", 7173545ddfbeSMarcel Moolenaar pre_nl, xo_indent(xop), "", name, ppn); 7174d1a0d267SMarcel Moolenaar break; 7175d1a0d267SMarcel Moolenaar 7176d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER: 7177f2b7bf8aSPhil Shafer rc = xo_encoder_handle(xop, XO_OP_OPEN_LEAF_LIST, name, NULL, flags); 7178d1a0d267SMarcel Moolenaar break; 7179545ddfbeSMarcel Moolenaar } 7180545ddfbeSMarcel Moolenaar 7181545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, 1, indent, XSS_OPEN_LEAF_LIST, 7182545ddfbeSMarcel Moolenaar XSF_LIST | xo_stack_flags(flags)); 7183545ddfbeSMarcel Moolenaar 7184545ddfbeSMarcel Moolenaar return rc; 7185545ddfbeSMarcel Moolenaar } 7186545ddfbeSMarcel Moolenaar 7187545ddfbeSMarcel Moolenaar static int 7188545ddfbeSMarcel Moolenaar xo_do_close_leaf_list (xo_handle_t *xop, const char *name) 7189545ddfbeSMarcel Moolenaar { 71908a6eceffSPhil Shafer ssize_t rc = 0; 7191545ddfbeSMarcel Moolenaar const char *pre_nl = ""; 7192545ddfbeSMarcel Moolenaar 7193545ddfbeSMarcel Moolenaar if (name == NULL) { 7194545ddfbeSMarcel Moolenaar xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth]; 7195545ddfbeSMarcel Moolenaar 7196545ddfbeSMarcel Moolenaar name = xsp->xs_name; 7197545ddfbeSMarcel Moolenaar if (name) { 71988a6eceffSPhil Shafer ssize_t len = strlen(name) + 1; 7199545ddfbeSMarcel Moolenaar /* We need to make a local copy; xo_depth_change will free it */ 7200545ddfbeSMarcel Moolenaar char *cp = alloca(len); 7201545ddfbeSMarcel Moolenaar memcpy(cp, name, len); 7202545ddfbeSMarcel Moolenaar name = cp; 7203545ddfbeSMarcel Moolenaar } else if (!(xsp->xs_flags & XSF_DTRT)) { 7204545ddfbeSMarcel Moolenaar xo_failure(xop, "missing name without 'dtrt' mode"); 7205545ddfbeSMarcel Moolenaar name = XO_FAILURE_NAME; 7206545ddfbeSMarcel Moolenaar } 7207545ddfbeSMarcel Moolenaar } 7208545ddfbeSMarcel Moolenaar 7209d1a0d267SMarcel Moolenaar switch (xo_style(xop)) { 7210d1a0d267SMarcel Moolenaar case XO_STYLE_JSON: 7211545ddfbeSMarcel Moolenaar if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST) 7212d1a0d267SMarcel Moolenaar pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : ""; 7213545ddfbeSMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 7214545ddfbeSMarcel Moolenaar 7215545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, -1, -1, XSS_CLOSE_LEAF_LIST, XSF_LIST); 7216545ddfbeSMarcel Moolenaar rc = xo_printf(xop, "%s%*s]", pre_nl, xo_indent(xop), ""); 7217545ddfbeSMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 7218d1a0d267SMarcel Moolenaar break; 7219545ddfbeSMarcel Moolenaar 7220d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER: 7221f2b7bf8aSPhil Shafer rc = xo_encoder_handle(xop, XO_OP_CLOSE_LEAF_LIST, name, NULL, 0); 7222ee5cf116SPhil Shafer /* FALLTHRU */ 7223d1a0d267SMarcel Moolenaar 7224d1a0d267SMarcel Moolenaar default: 7225545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, -1, 0, XSS_CLOSE_LEAF_LIST, XSF_LIST); 7226545ddfbeSMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 7227d1a0d267SMarcel Moolenaar break; 7228545ddfbeSMarcel Moolenaar } 7229545ddfbeSMarcel Moolenaar 7230545ddfbeSMarcel Moolenaar return rc; 7231545ddfbeSMarcel Moolenaar } 7232545ddfbeSMarcel Moolenaar 7233545ddfbeSMarcel Moolenaar static int 7234545ddfbeSMarcel Moolenaar xo_do_open_instance (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name) 723531337658SMarcel Moolenaar { 723631337658SMarcel Moolenaar xop = xo_default(xop); 723731337658SMarcel Moolenaar 72388a6eceffSPhil Shafer ssize_t rc = 0; 7239d1a0d267SMarcel Moolenaar const char *ppn = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : ""; 724031337658SMarcel Moolenaar const char *pre_nl = ""; 724131337658SMarcel Moolenaar 724231337658SMarcel Moolenaar flags |= xop->xo_flags; 724331337658SMarcel Moolenaar 724431337658SMarcel Moolenaar if (name == NULL) { 724531337658SMarcel Moolenaar xo_failure(xop, "NULL passed for instance name"); 724631337658SMarcel Moolenaar name = XO_FAILURE_NAME; 724731337658SMarcel Moolenaar } 724831337658SMarcel Moolenaar 7249788ca347SMarcel Moolenaar switch (xo_style(xop)) { 725031337658SMarcel Moolenaar case XO_STYLE_XML: 7251545ddfbeSMarcel Moolenaar rc = xo_printf(xop, "%*s<%s", xo_indent(xop), "", name); 7252545ddfbeSMarcel Moolenaar 7253545ddfbeSMarcel Moolenaar if (xop->xo_attrs.xb_curp != xop->xo_attrs.xb_bufp) { 7254545ddfbeSMarcel Moolenaar rc += xop->xo_attrs.xb_curp - xop->xo_attrs.xb_bufp; 7255545ddfbeSMarcel Moolenaar xo_data_append(xop, xop->xo_attrs.xb_bufp, 7256545ddfbeSMarcel Moolenaar xop->xo_attrs.xb_curp - xop->xo_attrs.xb_bufp); 7257545ddfbeSMarcel Moolenaar xop->xo_attrs.xb_curp = xop->xo_attrs.xb_bufp; 7258545ddfbeSMarcel Moolenaar } 7259545ddfbeSMarcel Moolenaar 7260545ddfbeSMarcel Moolenaar rc += xo_printf(xop, ">%s", ppn); 726131337658SMarcel Moolenaar break; 726231337658SMarcel Moolenaar 726331337658SMarcel Moolenaar case XO_STYLE_JSON: 726431337658SMarcel Moolenaar xo_stack_set_flags(xop); 726531337658SMarcel Moolenaar 726631337658SMarcel Moolenaar if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST) 7267d1a0d267SMarcel Moolenaar pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? ",\n" : ", "; 726831337658SMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 726931337658SMarcel Moolenaar 727031337658SMarcel Moolenaar rc = xo_printf(xop, "%s%*s{%s", 727131337658SMarcel Moolenaar pre_nl, xo_indent(xop), "", ppn); 727231337658SMarcel Moolenaar break; 7273d1a0d267SMarcel Moolenaar 7274d1a0d267SMarcel Moolenaar case XO_STYLE_SDPARAMS: 7275d1a0d267SMarcel Moolenaar break; 7276d1a0d267SMarcel Moolenaar 7277d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER: 7278f2b7bf8aSPhil Shafer rc = xo_encoder_handle(xop, XO_OP_OPEN_INSTANCE, name, NULL, flags); 7279d1a0d267SMarcel Moolenaar break; 728031337658SMarcel Moolenaar } 728131337658SMarcel Moolenaar 7282545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, 1, 1, XSS_OPEN_INSTANCE, xo_stack_flags(flags)); 7283545ddfbeSMarcel Moolenaar 728431337658SMarcel Moolenaar return rc; 728531337658SMarcel Moolenaar } 728631337658SMarcel Moolenaar 7287545ddfbeSMarcel Moolenaar static int 7288545ddfbeSMarcel Moolenaar xo_open_instance_hf (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name) 7289545ddfbeSMarcel Moolenaar { 7290545ddfbeSMarcel Moolenaar return xo_transition(xop, flags, name, XSS_OPEN_INSTANCE); 7291545ddfbeSMarcel Moolenaar } 7292545ddfbeSMarcel Moolenaar 72938a6eceffSPhil Shafer xo_ssize_t 729431337658SMarcel Moolenaar xo_open_instance_h (xo_handle_t *xop, const char *name) 729531337658SMarcel Moolenaar { 729631337658SMarcel Moolenaar return xo_open_instance_hf(xop, 0, name); 729731337658SMarcel Moolenaar } 729831337658SMarcel Moolenaar 72998a6eceffSPhil Shafer xo_ssize_t 730031337658SMarcel Moolenaar xo_open_instance (const char *name) 730131337658SMarcel Moolenaar { 730231337658SMarcel Moolenaar return xo_open_instance_hf(NULL, 0, name); 730331337658SMarcel Moolenaar } 730431337658SMarcel Moolenaar 73058a6eceffSPhil Shafer xo_ssize_t 730631337658SMarcel Moolenaar xo_open_instance_hd (xo_handle_t *xop, const char *name) 730731337658SMarcel Moolenaar { 730831337658SMarcel Moolenaar return xo_open_instance_hf(xop, XOF_DTRT, name); 730931337658SMarcel Moolenaar } 731031337658SMarcel Moolenaar 73118a6eceffSPhil Shafer xo_ssize_t 731231337658SMarcel Moolenaar xo_open_instance_d (const char *name) 731331337658SMarcel Moolenaar { 731431337658SMarcel Moolenaar return xo_open_instance_hf(NULL, XOF_DTRT, name); 731531337658SMarcel Moolenaar } 731631337658SMarcel Moolenaar 7317545ddfbeSMarcel Moolenaar static int 7318545ddfbeSMarcel Moolenaar xo_do_close_instance (xo_handle_t *xop, const char *name) 731931337658SMarcel Moolenaar { 732031337658SMarcel Moolenaar xop = xo_default(xop); 732131337658SMarcel Moolenaar 73228a6eceffSPhil Shafer ssize_t rc = 0; 7323d1a0d267SMarcel Moolenaar const char *ppn = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : ""; 732431337658SMarcel Moolenaar const char *pre_nl = ""; 732531337658SMarcel Moolenaar 732631337658SMarcel Moolenaar if (name == NULL) { 732731337658SMarcel Moolenaar xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth]; 732831337658SMarcel Moolenaar 732931337658SMarcel Moolenaar name = xsp->xs_name; 733031337658SMarcel Moolenaar if (name) { 73318a6eceffSPhil Shafer ssize_t len = strlen(name) + 1; 733231337658SMarcel Moolenaar /* We need to make a local copy; xo_depth_change will free it */ 733331337658SMarcel Moolenaar char *cp = alloca(len); 733431337658SMarcel Moolenaar memcpy(cp, name, len); 733531337658SMarcel Moolenaar name = cp; 7336545ddfbeSMarcel Moolenaar } else if (!(xsp->xs_flags & XSF_DTRT)) { 7337545ddfbeSMarcel Moolenaar xo_failure(xop, "missing name without 'dtrt' mode"); 733831337658SMarcel Moolenaar name = XO_FAILURE_NAME; 733931337658SMarcel Moolenaar } 7340545ddfbeSMarcel Moolenaar } 734131337658SMarcel Moolenaar 7342788ca347SMarcel Moolenaar switch (xo_style(xop)) { 734331337658SMarcel Moolenaar case XO_STYLE_XML: 7344545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, -1, -1, XSS_CLOSE_INSTANCE, 0); 734531337658SMarcel Moolenaar rc = xo_printf(xop, "%*s</%s>%s", xo_indent(xop), "", name, ppn); 734631337658SMarcel Moolenaar break; 734731337658SMarcel Moolenaar 734831337658SMarcel Moolenaar case XO_STYLE_JSON: 7349d1a0d267SMarcel Moolenaar pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : ""; 735031337658SMarcel Moolenaar 7351545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, -1, -1, XSS_CLOSE_INSTANCE, 0); 735231337658SMarcel Moolenaar rc = xo_printf(xop, "%s%*s}", pre_nl, xo_indent(xop), ""); 735331337658SMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST; 735431337658SMarcel Moolenaar break; 735531337658SMarcel Moolenaar 735631337658SMarcel Moolenaar case XO_STYLE_HTML: 735731337658SMarcel Moolenaar case XO_STYLE_TEXT: 7358545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, -1, 0, XSS_CLOSE_INSTANCE, 0); 735931337658SMarcel Moolenaar break; 7360d1a0d267SMarcel Moolenaar 7361d1a0d267SMarcel Moolenaar case XO_STYLE_SDPARAMS: 7362d1a0d267SMarcel Moolenaar break; 7363d1a0d267SMarcel Moolenaar 7364d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER: 7365d1a0d267SMarcel Moolenaar xo_depth_change(xop, name, -1, 0, XSS_CLOSE_INSTANCE, 0); 7366f2b7bf8aSPhil Shafer rc = xo_encoder_handle(xop, XO_OP_CLOSE_INSTANCE, name, NULL, 0); 7367d1a0d267SMarcel Moolenaar break; 736831337658SMarcel Moolenaar } 736931337658SMarcel Moolenaar 737031337658SMarcel Moolenaar return rc; 737131337658SMarcel Moolenaar } 737231337658SMarcel Moolenaar 73738a6eceffSPhil Shafer xo_ssize_t 7374545ddfbeSMarcel Moolenaar xo_close_instance_h (xo_handle_t *xop, const char *name) 7375545ddfbeSMarcel Moolenaar { 7376545ddfbeSMarcel Moolenaar return xo_transition(xop, 0, name, XSS_CLOSE_INSTANCE); 7377545ddfbeSMarcel Moolenaar } 7378545ddfbeSMarcel Moolenaar 73798a6eceffSPhil Shafer xo_ssize_t 738031337658SMarcel Moolenaar xo_close_instance (const char *name) 738131337658SMarcel Moolenaar { 738231337658SMarcel Moolenaar return xo_close_instance_h(NULL, name); 738331337658SMarcel Moolenaar } 738431337658SMarcel Moolenaar 73858a6eceffSPhil Shafer xo_ssize_t 738631337658SMarcel Moolenaar xo_close_instance_hd (xo_handle_t *xop) 738731337658SMarcel Moolenaar { 738831337658SMarcel Moolenaar return xo_close_instance_h(xop, NULL); 738931337658SMarcel Moolenaar } 739031337658SMarcel Moolenaar 73918a6eceffSPhil Shafer xo_ssize_t 739231337658SMarcel Moolenaar xo_close_instance_d (void) 739331337658SMarcel Moolenaar { 739431337658SMarcel Moolenaar return xo_close_instance_h(NULL, NULL); 739531337658SMarcel Moolenaar } 739631337658SMarcel Moolenaar 7397545ddfbeSMarcel Moolenaar static int 7398545ddfbeSMarcel Moolenaar xo_do_close_all (xo_handle_t *xop, xo_stack_t *limit) 7399545ddfbeSMarcel Moolenaar { 7400545ddfbeSMarcel Moolenaar xo_stack_t *xsp; 74018a6eceffSPhil Shafer ssize_t rc = 0; 7402545ddfbeSMarcel Moolenaar xo_xsf_flags_t flags; 7403545ddfbeSMarcel Moolenaar 7404545ddfbeSMarcel Moolenaar for (xsp = &xop->xo_stack[xop->xo_depth]; xsp >= limit; xsp--) { 7405545ddfbeSMarcel Moolenaar switch (xsp->xs_state) { 7406545ddfbeSMarcel Moolenaar case XSS_INIT: 7407545ddfbeSMarcel Moolenaar /* Nothing */ 7408545ddfbeSMarcel Moolenaar rc = 0; 7409545ddfbeSMarcel Moolenaar break; 7410545ddfbeSMarcel Moolenaar 7411545ddfbeSMarcel Moolenaar case XSS_OPEN_CONTAINER: 7412545ddfbeSMarcel Moolenaar rc = xo_do_close_container(xop, NULL); 7413545ddfbeSMarcel Moolenaar break; 7414545ddfbeSMarcel Moolenaar 7415545ddfbeSMarcel Moolenaar case XSS_OPEN_LIST: 7416545ddfbeSMarcel Moolenaar rc = xo_do_close_list(xop, NULL); 7417545ddfbeSMarcel Moolenaar break; 7418545ddfbeSMarcel Moolenaar 7419545ddfbeSMarcel Moolenaar case XSS_OPEN_INSTANCE: 7420545ddfbeSMarcel Moolenaar rc = xo_do_close_instance(xop, NULL); 7421545ddfbeSMarcel Moolenaar break; 7422545ddfbeSMarcel Moolenaar 7423545ddfbeSMarcel Moolenaar case XSS_OPEN_LEAF_LIST: 7424545ddfbeSMarcel Moolenaar rc = xo_do_close_leaf_list(xop, NULL); 7425545ddfbeSMarcel Moolenaar break; 7426545ddfbeSMarcel Moolenaar 7427545ddfbeSMarcel Moolenaar case XSS_MARKER: 7428545ddfbeSMarcel Moolenaar flags = xsp->xs_flags & XSF_MARKER_FLAGS; 7429545ddfbeSMarcel Moolenaar xo_depth_change(xop, xsp->xs_name, -1, 0, XSS_MARKER, 0); 7430545ddfbeSMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= flags; 7431545ddfbeSMarcel Moolenaar rc = 0; 7432545ddfbeSMarcel Moolenaar break; 7433545ddfbeSMarcel Moolenaar } 7434545ddfbeSMarcel Moolenaar 7435545ddfbeSMarcel Moolenaar if (rc < 0) 7436545ddfbeSMarcel Moolenaar xo_failure(xop, "close %d failed: %d", xsp->xs_state, rc); 7437545ddfbeSMarcel Moolenaar } 7438545ddfbeSMarcel Moolenaar 7439545ddfbeSMarcel Moolenaar return 0; 7440545ddfbeSMarcel Moolenaar } 7441545ddfbeSMarcel Moolenaar 7442545ddfbeSMarcel Moolenaar /* 7443545ddfbeSMarcel Moolenaar * This function is responsible for clearing out whatever is needed 7444545ddfbeSMarcel Moolenaar * to get to the desired state, if possible. 7445545ddfbeSMarcel Moolenaar */ 7446545ddfbeSMarcel Moolenaar static int 7447545ddfbeSMarcel Moolenaar xo_do_close (xo_handle_t *xop, const char *name, xo_state_t new_state) 7448545ddfbeSMarcel Moolenaar { 7449545ddfbeSMarcel Moolenaar xo_stack_t *xsp, *limit = NULL; 74508a6eceffSPhil Shafer ssize_t rc; 7451545ddfbeSMarcel Moolenaar xo_state_t need_state = new_state; 7452545ddfbeSMarcel Moolenaar 7453545ddfbeSMarcel Moolenaar if (new_state == XSS_CLOSE_CONTAINER) 7454545ddfbeSMarcel Moolenaar need_state = XSS_OPEN_CONTAINER; 7455545ddfbeSMarcel Moolenaar else if (new_state == XSS_CLOSE_LIST) 7456545ddfbeSMarcel Moolenaar need_state = XSS_OPEN_LIST; 7457545ddfbeSMarcel Moolenaar else if (new_state == XSS_CLOSE_INSTANCE) 7458545ddfbeSMarcel Moolenaar need_state = XSS_OPEN_INSTANCE; 7459545ddfbeSMarcel Moolenaar else if (new_state == XSS_CLOSE_LEAF_LIST) 7460545ddfbeSMarcel Moolenaar need_state = XSS_OPEN_LEAF_LIST; 7461545ddfbeSMarcel Moolenaar else if (new_state == XSS_MARKER) 7462545ddfbeSMarcel Moolenaar need_state = XSS_MARKER; 7463545ddfbeSMarcel Moolenaar else 7464545ddfbeSMarcel Moolenaar return 0; /* Unknown or useless new states are ignored */ 7465545ddfbeSMarcel Moolenaar 7466545ddfbeSMarcel Moolenaar for (xsp = &xop->xo_stack[xop->xo_depth]; xsp > xop->xo_stack; xsp--) { 7467545ddfbeSMarcel Moolenaar /* 7468545ddfbeSMarcel Moolenaar * Marker's normally stop us from going any further, unless 7469545ddfbeSMarcel Moolenaar * we are popping a marker (new_state == XSS_MARKER). 7470545ddfbeSMarcel Moolenaar */ 7471545ddfbeSMarcel Moolenaar if (xsp->xs_state == XSS_MARKER && need_state != XSS_MARKER) { 7472545ddfbeSMarcel Moolenaar if (name) { 7473545ddfbeSMarcel Moolenaar xo_failure(xop, "close (xo_%s) fails at marker '%s'; " 7474545ddfbeSMarcel Moolenaar "not found '%s'", 7475545ddfbeSMarcel Moolenaar xo_state_name(new_state), 7476545ddfbeSMarcel Moolenaar xsp->xs_name, name); 7477545ddfbeSMarcel Moolenaar return 0; 7478545ddfbeSMarcel Moolenaar 7479545ddfbeSMarcel Moolenaar } else { 7480545ddfbeSMarcel Moolenaar limit = xsp; 7481545ddfbeSMarcel Moolenaar xo_failure(xop, "close stops at marker '%s'", xsp->xs_name); 7482545ddfbeSMarcel Moolenaar } 7483545ddfbeSMarcel Moolenaar break; 7484545ddfbeSMarcel Moolenaar } 7485545ddfbeSMarcel Moolenaar 7486545ddfbeSMarcel Moolenaar if (xsp->xs_state != need_state) 7487545ddfbeSMarcel Moolenaar continue; 7488545ddfbeSMarcel Moolenaar 7489545ddfbeSMarcel Moolenaar if (name && xsp->xs_name && strcmp(name, xsp->xs_name) != 0) 7490545ddfbeSMarcel Moolenaar continue; 7491545ddfbeSMarcel Moolenaar 7492545ddfbeSMarcel Moolenaar limit = xsp; 7493545ddfbeSMarcel Moolenaar break; 7494545ddfbeSMarcel Moolenaar } 7495545ddfbeSMarcel Moolenaar 7496545ddfbeSMarcel Moolenaar if (limit == NULL) { 7497545ddfbeSMarcel Moolenaar xo_failure(xop, "xo_%s can't find match for '%s'", 7498545ddfbeSMarcel Moolenaar xo_state_name(new_state), name); 7499545ddfbeSMarcel Moolenaar return 0; 7500545ddfbeSMarcel Moolenaar } 7501545ddfbeSMarcel Moolenaar 7502545ddfbeSMarcel Moolenaar rc = xo_do_close_all(xop, limit); 7503545ddfbeSMarcel Moolenaar 7504545ddfbeSMarcel Moolenaar return rc; 7505545ddfbeSMarcel Moolenaar } 7506545ddfbeSMarcel Moolenaar 7507545ddfbeSMarcel Moolenaar /* 7508545ddfbeSMarcel Moolenaar * We are in a given state and need to transition to the new state. 7509545ddfbeSMarcel Moolenaar */ 75108a6eceffSPhil Shafer static ssize_t 7511545ddfbeSMarcel Moolenaar xo_transition (xo_handle_t *xop, xo_xsf_flags_t flags, const char *name, 7512545ddfbeSMarcel Moolenaar xo_state_t new_state) 7513545ddfbeSMarcel Moolenaar { 7514545ddfbeSMarcel Moolenaar xo_stack_t *xsp; 75158a6eceffSPhil Shafer ssize_t rc = 0; 7516545ddfbeSMarcel Moolenaar int old_state, on_marker; 7517545ddfbeSMarcel Moolenaar 7518545ddfbeSMarcel Moolenaar xop = xo_default(xop); 7519545ddfbeSMarcel Moolenaar 7520545ddfbeSMarcel Moolenaar xsp = &xop->xo_stack[xop->xo_depth]; 7521545ddfbeSMarcel Moolenaar old_state = xsp->xs_state; 7522545ddfbeSMarcel Moolenaar on_marker = (old_state == XSS_MARKER); 7523545ddfbeSMarcel Moolenaar 7524545ddfbeSMarcel Moolenaar /* If there's a marker on top of the stack, we need to find a real state */ 7525545ddfbeSMarcel Moolenaar while (old_state == XSS_MARKER) { 7526545ddfbeSMarcel Moolenaar if (xsp == xop->xo_stack) 7527545ddfbeSMarcel Moolenaar break; 7528545ddfbeSMarcel Moolenaar xsp -= 1; 7529545ddfbeSMarcel Moolenaar old_state = xsp->xs_state; 7530545ddfbeSMarcel Moolenaar } 7531545ddfbeSMarcel Moolenaar 7532545ddfbeSMarcel Moolenaar /* 7533545ddfbeSMarcel Moolenaar * At this point, the list of possible states are: 7534545ddfbeSMarcel Moolenaar * XSS_INIT, XSS_OPEN_CONTAINER, XSS_OPEN_LIST, 7535545ddfbeSMarcel Moolenaar * XSS_OPEN_INSTANCE, XSS_OPEN_LEAF_LIST, XSS_DISCARDING 7536545ddfbeSMarcel Moolenaar */ 7537545ddfbeSMarcel Moolenaar switch (XSS_TRANSITION(old_state, new_state)) { 7538545ddfbeSMarcel Moolenaar 7539545ddfbeSMarcel Moolenaar open_container: 7540545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_INIT, XSS_OPEN_CONTAINER): 7541545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_OPEN_CONTAINER): 7542545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_OPEN_CONTAINER): 7543545ddfbeSMarcel Moolenaar rc = xo_do_open_container(xop, flags, name); 7544545ddfbeSMarcel Moolenaar break; 7545545ddfbeSMarcel Moolenaar 7546545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LIST, XSS_OPEN_CONTAINER): 7547545ddfbeSMarcel Moolenaar if (on_marker) 7548545ddfbeSMarcel Moolenaar goto marker_prevents_close; 7549545ddfbeSMarcel Moolenaar rc = xo_do_close_list(xop, NULL); 7550545ddfbeSMarcel Moolenaar if (rc >= 0) 7551545ddfbeSMarcel Moolenaar goto open_container; 7552545ddfbeSMarcel Moolenaar break; 7553545ddfbeSMarcel Moolenaar 7554545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_OPEN_CONTAINER): 7555545ddfbeSMarcel Moolenaar if (on_marker) 7556545ddfbeSMarcel Moolenaar goto marker_prevents_close; 7557545ddfbeSMarcel Moolenaar rc = xo_do_close_leaf_list(xop, NULL); 7558545ddfbeSMarcel Moolenaar if (rc >= 0) 7559545ddfbeSMarcel Moolenaar goto open_container; 7560545ddfbeSMarcel Moolenaar break; 7561545ddfbeSMarcel Moolenaar 7562545ddfbeSMarcel Moolenaar /*close_container:*/ 7563545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_CLOSE_CONTAINER): 7564545ddfbeSMarcel Moolenaar if (on_marker) 7565545ddfbeSMarcel Moolenaar goto marker_prevents_close; 7566545ddfbeSMarcel Moolenaar rc = xo_do_close(xop, name, new_state); 7567545ddfbeSMarcel Moolenaar break; 7568545ddfbeSMarcel Moolenaar 7569545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_INIT, XSS_CLOSE_CONTAINER): 7570545ddfbeSMarcel Moolenaar /* This is an exception for "xo --close" */ 7571545ddfbeSMarcel Moolenaar rc = xo_do_close_container(xop, name); 7572545ddfbeSMarcel Moolenaar break; 7573545ddfbeSMarcel Moolenaar 7574545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LIST, XSS_CLOSE_CONTAINER): 7575545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_CLOSE_CONTAINER): 7576545ddfbeSMarcel Moolenaar if (on_marker) 7577545ddfbeSMarcel Moolenaar goto marker_prevents_close; 7578545ddfbeSMarcel Moolenaar rc = xo_do_close(xop, name, new_state); 7579545ddfbeSMarcel Moolenaar break; 7580545ddfbeSMarcel Moolenaar 7581545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_CLOSE_CONTAINER): 7582545ddfbeSMarcel Moolenaar if (on_marker) 7583545ddfbeSMarcel Moolenaar goto marker_prevents_close; 7584545ddfbeSMarcel Moolenaar rc = xo_do_close_leaf_list(xop, NULL); 7585545ddfbeSMarcel Moolenaar if (rc >= 0) 7586545ddfbeSMarcel Moolenaar rc = xo_do_close(xop, name, new_state); 7587545ddfbeSMarcel Moolenaar break; 7588545ddfbeSMarcel Moolenaar 7589545ddfbeSMarcel Moolenaar open_list: 7590545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_INIT, XSS_OPEN_LIST): 7591545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_OPEN_LIST): 7592545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_OPEN_LIST): 7593545ddfbeSMarcel Moolenaar rc = xo_do_open_list(xop, flags, name); 7594545ddfbeSMarcel Moolenaar break; 7595545ddfbeSMarcel Moolenaar 7596545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LIST, XSS_OPEN_LIST): 7597545ddfbeSMarcel Moolenaar if (on_marker) 7598545ddfbeSMarcel Moolenaar goto marker_prevents_close; 7599545ddfbeSMarcel Moolenaar rc = xo_do_close_list(xop, NULL); 7600545ddfbeSMarcel Moolenaar if (rc >= 0) 7601545ddfbeSMarcel Moolenaar goto open_list; 7602545ddfbeSMarcel Moolenaar break; 7603545ddfbeSMarcel Moolenaar 7604545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_OPEN_LIST): 7605545ddfbeSMarcel Moolenaar if (on_marker) 7606545ddfbeSMarcel Moolenaar goto marker_prevents_close; 7607545ddfbeSMarcel Moolenaar rc = xo_do_close_leaf_list(xop, NULL); 7608545ddfbeSMarcel Moolenaar if (rc >= 0) 7609545ddfbeSMarcel Moolenaar goto open_list; 7610545ddfbeSMarcel Moolenaar break; 7611545ddfbeSMarcel Moolenaar 7612545ddfbeSMarcel Moolenaar /*close_list:*/ 7613545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LIST, XSS_CLOSE_LIST): 7614545ddfbeSMarcel Moolenaar if (on_marker) 7615545ddfbeSMarcel Moolenaar goto marker_prevents_close; 7616545ddfbeSMarcel Moolenaar rc = xo_do_close(xop, name, new_state); 7617545ddfbeSMarcel Moolenaar break; 7618545ddfbeSMarcel Moolenaar 7619545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_INIT, XSS_CLOSE_LIST): 7620545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_CLOSE_LIST): 7621545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_CLOSE_LIST): 7622545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_CLOSE_LIST): 7623545ddfbeSMarcel Moolenaar rc = xo_do_close(xop, name, new_state); 7624545ddfbeSMarcel Moolenaar break; 7625545ddfbeSMarcel Moolenaar 7626545ddfbeSMarcel Moolenaar open_instance: 7627545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LIST, XSS_OPEN_INSTANCE): 7628545ddfbeSMarcel Moolenaar rc = xo_do_open_instance(xop, flags, name); 7629545ddfbeSMarcel Moolenaar break; 7630545ddfbeSMarcel Moolenaar 7631545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_INIT, XSS_OPEN_INSTANCE): 7632788ca347SMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_OPEN_INSTANCE): 7633545ddfbeSMarcel Moolenaar rc = xo_do_open_list(xop, flags, name); 7634545ddfbeSMarcel Moolenaar if (rc >= 0) 7635545ddfbeSMarcel Moolenaar goto open_instance; 7636545ddfbeSMarcel Moolenaar break; 7637545ddfbeSMarcel Moolenaar 7638545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_OPEN_INSTANCE): 7639545ddfbeSMarcel Moolenaar if (on_marker) { 7640545ddfbeSMarcel Moolenaar rc = xo_do_open_list(xop, flags, name); 7641545ddfbeSMarcel Moolenaar } else { 7642545ddfbeSMarcel Moolenaar rc = xo_do_close_instance(xop, NULL); 7643545ddfbeSMarcel Moolenaar } 7644545ddfbeSMarcel Moolenaar if (rc >= 0) 7645545ddfbeSMarcel Moolenaar goto open_instance; 7646545ddfbeSMarcel Moolenaar break; 7647545ddfbeSMarcel Moolenaar 7648545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_OPEN_INSTANCE): 7649545ddfbeSMarcel Moolenaar if (on_marker) 7650545ddfbeSMarcel Moolenaar goto marker_prevents_close; 7651545ddfbeSMarcel Moolenaar rc = xo_do_close_leaf_list(xop, NULL); 7652545ddfbeSMarcel Moolenaar if (rc >= 0) 7653545ddfbeSMarcel Moolenaar goto open_instance; 7654545ddfbeSMarcel Moolenaar break; 7655545ddfbeSMarcel Moolenaar 7656545ddfbeSMarcel Moolenaar /*close_instance:*/ 7657545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_CLOSE_INSTANCE): 7658545ddfbeSMarcel Moolenaar if (on_marker) 7659545ddfbeSMarcel Moolenaar goto marker_prevents_close; 7660545ddfbeSMarcel Moolenaar rc = xo_do_close_instance(xop, name); 7661545ddfbeSMarcel Moolenaar break; 7662545ddfbeSMarcel Moolenaar 7663545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_INIT, XSS_CLOSE_INSTANCE): 7664545ddfbeSMarcel Moolenaar /* This one makes no sense; ignore it */ 7665788ca347SMarcel Moolenaar xo_failure(xop, "xo_close_instance ignored when called from " 7666788ca347SMarcel Moolenaar "initial state ('%s')", name ?: "(unknown)"); 7667545ddfbeSMarcel Moolenaar break; 7668545ddfbeSMarcel Moolenaar 7669545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_CLOSE_INSTANCE): 7670545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LIST, XSS_CLOSE_INSTANCE): 7671545ddfbeSMarcel Moolenaar if (on_marker) 7672545ddfbeSMarcel Moolenaar goto marker_prevents_close; 7673545ddfbeSMarcel Moolenaar rc = xo_do_close(xop, name, new_state); 7674545ddfbeSMarcel Moolenaar break; 7675545ddfbeSMarcel Moolenaar 7676545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_CLOSE_INSTANCE): 7677545ddfbeSMarcel Moolenaar if (on_marker) 7678545ddfbeSMarcel Moolenaar goto marker_prevents_close; 7679545ddfbeSMarcel Moolenaar rc = xo_do_close_leaf_list(xop, NULL); 7680545ddfbeSMarcel Moolenaar if (rc >= 0) 7681545ddfbeSMarcel Moolenaar rc = xo_do_close(xop, name, new_state); 7682545ddfbeSMarcel Moolenaar break; 7683545ddfbeSMarcel Moolenaar 7684545ddfbeSMarcel Moolenaar open_leaf_list: 7685545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_OPEN_LEAF_LIST): 7686545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_OPEN_LEAF_LIST): 7687545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_INIT, XSS_OPEN_LEAF_LIST): 7688545ddfbeSMarcel Moolenaar rc = xo_do_open_leaf_list(xop, flags, name); 7689545ddfbeSMarcel Moolenaar break; 7690545ddfbeSMarcel Moolenaar 7691545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LIST, XSS_OPEN_LEAF_LIST): 7692545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_OPEN_LEAF_LIST): 7693545ddfbeSMarcel Moolenaar if (on_marker) 7694545ddfbeSMarcel Moolenaar goto marker_prevents_close; 7695545ddfbeSMarcel Moolenaar rc = xo_do_close_list(xop, NULL); 7696545ddfbeSMarcel Moolenaar if (rc >= 0) 7697545ddfbeSMarcel Moolenaar goto open_leaf_list; 7698545ddfbeSMarcel Moolenaar break; 7699545ddfbeSMarcel Moolenaar 7700545ddfbeSMarcel Moolenaar /*close_leaf_list:*/ 7701545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_CLOSE_LEAF_LIST): 7702545ddfbeSMarcel Moolenaar if (on_marker) 7703545ddfbeSMarcel Moolenaar goto marker_prevents_close; 7704545ddfbeSMarcel Moolenaar rc = xo_do_close_leaf_list(xop, name); 7705545ddfbeSMarcel Moolenaar break; 7706545ddfbeSMarcel Moolenaar 7707545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_INIT, XSS_CLOSE_LEAF_LIST): 7708545ddfbeSMarcel Moolenaar /* Makes no sense; ignore */ 7709788ca347SMarcel Moolenaar xo_failure(xop, "xo_close_leaf_list ignored when called from " 7710788ca347SMarcel Moolenaar "initial state ('%s')", name ?: "(unknown)"); 7711545ddfbeSMarcel Moolenaar break; 7712545ddfbeSMarcel Moolenaar 7713545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_CLOSE_LEAF_LIST): 7714545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LIST, XSS_CLOSE_LEAF_LIST): 7715545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_CLOSE_LEAF_LIST): 7716545ddfbeSMarcel Moolenaar if (on_marker) 7717545ddfbeSMarcel Moolenaar goto marker_prevents_close; 7718545ddfbeSMarcel Moolenaar rc = xo_do_close(xop, name, new_state); 7719545ddfbeSMarcel Moolenaar break; 7720545ddfbeSMarcel Moolenaar 7721545ddfbeSMarcel Moolenaar /*emit:*/ 7722545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_EMIT): 7723545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_EMIT): 7724545ddfbeSMarcel Moolenaar break; 7725545ddfbeSMarcel Moolenaar 7726545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LIST, XSS_EMIT): 7727545ddfbeSMarcel Moolenaar if (on_marker) 7728545ddfbeSMarcel Moolenaar goto marker_prevents_close; 7729545ddfbeSMarcel Moolenaar rc = xo_do_close(xop, NULL, XSS_CLOSE_LIST); 7730545ddfbeSMarcel Moolenaar break; 7731545ddfbeSMarcel Moolenaar 7732545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_INIT, XSS_EMIT): 7733545ddfbeSMarcel Moolenaar break; 7734545ddfbeSMarcel Moolenaar 7735545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_EMIT): 7736545ddfbeSMarcel Moolenaar if (on_marker) 7737545ddfbeSMarcel Moolenaar goto marker_prevents_close; 7738545ddfbeSMarcel Moolenaar rc = xo_do_close_leaf_list(xop, NULL); 7739545ddfbeSMarcel Moolenaar break; 7740545ddfbeSMarcel Moolenaar 7741545ddfbeSMarcel Moolenaar /*emit_leaf_list:*/ 7742545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_INIT, XSS_EMIT_LEAF_LIST): 7743545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_EMIT_LEAF_LIST): 7744545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_EMIT_LEAF_LIST): 7745545ddfbeSMarcel Moolenaar rc = xo_do_open_leaf_list(xop, flags, name); 7746545ddfbeSMarcel Moolenaar break; 7747545ddfbeSMarcel Moolenaar 7748545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_EMIT_LEAF_LIST): 7749545ddfbeSMarcel Moolenaar break; 7750545ddfbeSMarcel Moolenaar 7751545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LIST, XSS_EMIT_LEAF_LIST): 7752545ddfbeSMarcel Moolenaar /* 7753545ddfbeSMarcel Moolenaar * We need to be backward compatible with the pre-xo_open_leaf_list 7754545ddfbeSMarcel Moolenaar * API, where both lists and leaf-lists were opened as lists. So 7755545ddfbeSMarcel Moolenaar * if we find an open list that hasn't had anything written to it, 7756545ddfbeSMarcel Moolenaar * we'll accept it. 7757545ddfbeSMarcel Moolenaar */ 7758545ddfbeSMarcel Moolenaar break; 7759545ddfbeSMarcel Moolenaar 7760545ddfbeSMarcel Moolenaar default: 7761545ddfbeSMarcel Moolenaar xo_failure(xop, "unknown transition: (%u -> %u)", 7762545ddfbeSMarcel Moolenaar xsp->xs_state, new_state); 7763545ddfbeSMarcel Moolenaar } 7764545ddfbeSMarcel Moolenaar 776542ff34c3SPhil Shafer /* Handle the flush flag */ 776642ff34c3SPhil Shafer if (rc >= 0 && XOF_ISSET(xop, XOF_FLUSH)) 776742ff34c3SPhil Shafer if (xo_flush_h(xop)) 776842ff34c3SPhil Shafer rc = -1; 776942ff34c3SPhil Shafer 7770545ddfbeSMarcel Moolenaar return rc; 7771545ddfbeSMarcel Moolenaar 7772545ddfbeSMarcel Moolenaar marker_prevents_close: 7773545ddfbeSMarcel Moolenaar xo_failure(xop, "marker '%s' prevents transition from %s to %s", 7774545ddfbeSMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_name, 7775545ddfbeSMarcel Moolenaar xo_state_name(old_state), xo_state_name(new_state)); 7776545ddfbeSMarcel Moolenaar return -1; 7777545ddfbeSMarcel Moolenaar } 7778545ddfbeSMarcel Moolenaar 77798a6eceffSPhil Shafer xo_ssize_t 7780545ddfbeSMarcel Moolenaar xo_open_marker_h (xo_handle_t *xop, const char *name) 7781545ddfbeSMarcel Moolenaar { 7782545ddfbeSMarcel Moolenaar xop = xo_default(xop); 7783545ddfbeSMarcel Moolenaar 7784545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, 1, 0, XSS_MARKER, 7785545ddfbeSMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags & XSF_MARKER_FLAGS); 7786545ddfbeSMarcel Moolenaar 7787545ddfbeSMarcel Moolenaar return 0; 7788545ddfbeSMarcel Moolenaar } 7789545ddfbeSMarcel Moolenaar 77908a6eceffSPhil Shafer xo_ssize_t 7791545ddfbeSMarcel Moolenaar xo_open_marker (const char *name) 7792545ddfbeSMarcel Moolenaar { 7793545ddfbeSMarcel Moolenaar return xo_open_marker_h(NULL, name); 7794545ddfbeSMarcel Moolenaar } 7795545ddfbeSMarcel Moolenaar 77968a6eceffSPhil Shafer xo_ssize_t 7797545ddfbeSMarcel Moolenaar xo_close_marker_h (xo_handle_t *xop, const char *name) 7798545ddfbeSMarcel Moolenaar { 7799545ddfbeSMarcel Moolenaar xop = xo_default(xop); 7800545ddfbeSMarcel Moolenaar 7801545ddfbeSMarcel Moolenaar return xo_do_close(xop, name, XSS_MARKER); 7802545ddfbeSMarcel Moolenaar } 7803545ddfbeSMarcel Moolenaar 78048a6eceffSPhil Shafer xo_ssize_t 7805545ddfbeSMarcel Moolenaar xo_close_marker (const char *name) 7806545ddfbeSMarcel Moolenaar { 7807545ddfbeSMarcel Moolenaar return xo_close_marker_h(NULL, name); 7808545ddfbeSMarcel Moolenaar } 7809545ddfbeSMarcel Moolenaar 7810d1a0d267SMarcel Moolenaar /* 7811d1a0d267SMarcel Moolenaar * Record custom output functions into the xo handle, allowing 7812d1a0d267SMarcel Moolenaar * integration with a variety of output frameworks. 7813d1a0d267SMarcel Moolenaar */ 781431337658SMarcel Moolenaar void 781531337658SMarcel Moolenaar xo_set_writer (xo_handle_t *xop, void *opaque, xo_write_func_t write_func, 7816545ddfbeSMarcel Moolenaar xo_close_func_t close_func, xo_flush_func_t flush_func) 781731337658SMarcel Moolenaar { 781831337658SMarcel Moolenaar xop = xo_default(xop); 781931337658SMarcel Moolenaar 782031337658SMarcel Moolenaar xop->xo_opaque = opaque; 782131337658SMarcel Moolenaar xop->xo_write = write_func; 782231337658SMarcel Moolenaar xop->xo_close = close_func; 7823545ddfbeSMarcel Moolenaar xop->xo_flush = flush_func; 782431337658SMarcel Moolenaar } 782531337658SMarcel Moolenaar 782631337658SMarcel Moolenaar void 782731337658SMarcel Moolenaar xo_set_allocator (xo_realloc_func_t realloc_func, xo_free_func_t free_func) 782831337658SMarcel Moolenaar { 782931337658SMarcel Moolenaar xo_realloc = realloc_func; 783031337658SMarcel Moolenaar xo_free = free_func; 783131337658SMarcel Moolenaar } 783231337658SMarcel Moolenaar 78338a6eceffSPhil Shafer xo_ssize_t 783431337658SMarcel Moolenaar xo_flush_h (xo_handle_t *xop) 783531337658SMarcel Moolenaar { 78368a6eceffSPhil Shafer ssize_t rc; 783731337658SMarcel Moolenaar 783831337658SMarcel Moolenaar xop = xo_default(xop); 783931337658SMarcel Moolenaar 7840788ca347SMarcel Moolenaar switch (xo_style(xop)) { 7841d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER: 7842f2b7bf8aSPhil Shafer xo_encoder_handle(xop, XO_OP_FLUSH, NULL, NULL, 0); 784331337658SMarcel Moolenaar } 784431337658SMarcel Moolenaar 7845545ddfbeSMarcel Moolenaar rc = xo_write(xop); 7846545ddfbeSMarcel Moolenaar if (rc >= 0 && xop->xo_flush) 7847545ddfbeSMarcel Moolenaar if (xop->xo_flush(xop->xo_opaque) < 0) 7848545ddfbeSMarcel Moolenaar return -1; 7849545ddfbeSMarcel Moolenaar 7850545ddfbeSMarcel Moolenaar return rc; 785131337658SMarcel Moolenaar } 785231337658SMarcel Moolenaar 78538a6eceffSPhil Shafer xo_ssize_t 785431337658SMarcel Moolenaar xo_flush (void) 785531337658SMarcel Moolenaar { 7856545ddfbeSMarcel Moolenaar return xo_flush_h(NULL); 785731337658SMarcel Moolenaar } 785831337658SMarcel Moolenaar 78598a6eceffSPhil Shafer xo_ssize_t 786031337658SMarcel Moolenaar xo_finish_h (xo_handle_t *xop) 786131337658SMarcel Moolenaar { 786231337658SMarcel Moolenaar const char *cp = ""; 786331337658SMarcel Moolenaar xop = xo_default(xop); 786431337658SMarcel Moolenaar 7865d1a0d267SMarcel Moolenaar if (!XOF_ISSET(xop, XOF_NO_CLOSE)) 7866545ddfbeSMarcel Moolenaar xo_do_close_all(xop, xop->xo_stack); 7867545ddfbeSMarcel Moolenaar 7868788ca347SMarcel Moolenaar switch (xo_style(xop)) { 786931337658SMarcel Moolenaar case XO_STYLE_JSON: 7870d1a0d267SMarcel Moolenaar if (!XOF_ISSET(xop, XOF_NO_TOP)) { 7871d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_TOP_EMITTED)) 7872d1a0d267SMarcel Moolenaar XOIF_CLEAR(xop, XOIF_TOP_EMITTED); /* Turn off before output */ 787331337658SMarcel Moolenaar else 787431337658SMarcel Moolenaar cp = "{ "; 787531337658SMarcel Moolenaar xo_printf(xop, "%*s%s}\n",xo_indent(xop), "", cp); 787631337658SMarcel Moolenaar } 787731337658SMarcel Moolenaar break; 7878d1a0d267SMarcel Moolenaar 7879d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER: 7880f2b7bf8aSPhil Shafer xo_encoder_handle(xop, XO_OP_FINISH, NULL, NULL, 0); 7881d1a0d267SMarcel Moolenaar break; 788231337658SMarcel Moolenaar } 788331337658SMarcel Moolenaar 7884545ddfbeSMarcel Moolenaar return xo_flush_h(xop); 788531337658SMarcel Moolenaar } 788631337658SMarcel Moolenaar 78878a6eceffSPhil Shafer xo_ssize_t 788831337658SMarcel Moolenaar xo_finish (void) 788931337658SMarcel Moolenaar { 7890545ddfbeSMarcel Moolenaar return xo_finish_h(NULL); 789131337658SMarcel Moolenaar } 789231337658SMarcel Moolenaar 789331337658SMarcel Moolenaar /* 7894d1a0d267SMarcel Moolenaar * xo_finish_atexit is suitable for atexit() calls, to force clear up 7895d1a0d267SMarcel Moolenaar * and finalizing output. 7896d1a0d267SMarcel Moolenaar */ 7897d1a0d267SMarcel Moolenaar void 7898d1a0d267SMarcel Moolenaar xo_finish_atexit (void) 7899d1a0d267SMarcel Moolenaar { 7900d1a0d267SMarcel Moolenaar (void) xo_finish_h(NULL); 7901d1a0d267SMarcel Moolenaar } 7902d1a0d267SMarcel Moolenaar 7903d1a0d267SMarcel Moolenaar /* 790431337658SMarcel Moolenaar * Generate an error message, such as would be displayed on stderr 790531337658SMarcel Moolenaar */ 790631337658SMarcel Moolenaar void 790731337658SMarcel Moolenaar xo_error_hv (xo_handle_t *xop, const char *fmt, va_list vap) 790831337658SMarcel Moolenaar { 790931337658SMarcel Moolenaar xop = xo_default(xop); 791031337658SMarcel Moolenaar 791131337658SMarcel Moolenaar /* 791231337658SMarcel Moolenaar * If the format string doesn't end with a newline, we pop 791331337658SMarcel Moolenaar * one on ourselves. 791431337658SMarcel Moolenaar */ 79158a6eceffSPhil Shafer ssize_t len = strlen(fmt); 791631337658SMarcel Moolenaar if (len > 0 && fmt[len - 1] != '\n') { 791731337658SMarcel Moolenaar char *newfmt = alloca(len + 2); 791831337658SMarcel Moolenaar memcpy(newfmt, fmt, len); 791931337658SMarcel Moolenaar newfmt[len] = '\n'; 792031337658SMarcel Moolenaar newfmt[len] = '\0'; 792131337658SMarcel Moolenaar fmt = newfmt; 792231337658SMarcel Moolenaar } 792331337658SMarcel Moolenaar 7924788ca347SMarcel Moolenaar switch (xo_style(xop)) { 792531337658SMarcel Moolenaar case XO_STYLE_TEXT: 792631337658SMarcel Moolenaar vfprintf(stderr, fmt, vap); 792731337658SMarcel Moolenaar break; 792831337658SMarcel Moolenaar 792931337658SMarcel Moolenaar case XO_STYLE_HTML: 793031337658SMarcel Moolenaar va_copy(xop->xo_vap, vap); 793131337658SMarcel Moolenaar 7932264104f2SPhil Shafer xo_buf_append_div(xop, "error", 0, NULL, 0, NULL, 0, 7933264104f2SPhil Shafer fmt, strlen(fmt), NULL, 0); 793431337658SMarcel Moolenaar 7935d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_DIV_OPEN)) 793631337658SMarcel Moolenaar xo_line_close(xop); 793731337658SMarcel Moolenaar 793831337658SMarcel Moolenaar xo_write(xop); 793931337658SMarcel Moolenaar 794031337658SMarcel Moolenaar va_end(xop->xo_vap); 794131337658SMarcel Moolenaar bzero(&xop->xo_vap, sizeof(xop->xo_vap)); 794231337658SMarcel Moolenaar break; 794331337658SMarcel Moolenaar 794431337658SMarcel Moolenaar case XO_STYLE_XML: 7945545ddfbeSMarcel Moolenaar case XO_STYLE_JSON: 794631337658SMarcel Moolenaar va_copy(xop->xo_vap, vap); 794731337658SMarcel Moolenaar 794831337658SMarcel Moolenaar xo_open_container_h(xop, "error"); 7949264104f2SPhil Shafer xo_format_value(xop, "message", 7, NULL, 0, 7950264104f2SPhil Shafer fmt, strlen(fmt), NULL, 0, 0); 795131337658SMarcel Moolenaar xo_close_container_h(xop, "error"); 795231337658SMarcel Moolenaar 795331337658SMarcel Moolenaar va_end(xop->xo_vap); 795431337658SMarcel Moolenaar bzero(&xop->xo_vap, sizeof(xop->xo_vap)); 795531337658SMarcel Moolenaar break; 7956d1a0d267SMarcel Moolenaar 7957d1a0d267SMarcel Moolenaar case XO_STYLE_SDPARAMS: 7958d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER: 7959d1a0d267SMarcel Moolenaar break; 796031337658SMarcel Moolenaar } 796131337658SMarcel Moolenaar } 796231337658SMarcel Moolenaar 796331337658SMarcel Moolenaar void 796431337658SMarcel Moolenaar xo_error_h (xo_handle_t *xop, const char *fmt, ...) 796531337658SMarcel Moolenaar { 796631337658SMarcel Moolenaar va_list vap; 796731337658SMarcel Moolenaar 796831337658SMarcel Moolenaar va_start(vap, fmt); 796931337658SMarcel Moolenaar xo_error_hv(xop, fmt, vap); 797031337658SMarcel Moolenaar va_end(vap); 797131337658SMarcel Moolenaar } 797231337658SMarcel Moolenaar 797331337658SMarcel Moolenaar /* 797431337658SMarcel Moolenaar * Generate an error message, such as would be displayed on stderr 797531337658SMarcel Moolenaar */ 797631337658SMarcel Moolenaar void 797731337658SMarcel Moolenaar xo_error (const char *fmt, ...) 797831337658SMarcel Moolenaar { 797931337658SMarcel Moolenaar va_list vap; 798031337658SMarcel Moolenaar 798131337658SMarcel Moolenaar va_start(vap, fmt); 798231337658SMarcel Moolenaar xo_error_hv(NULL, fmt, vap); 798331337658SMarcel Moolenaar va_end(vap); 798431337658SMarcel Moolenaar } 798531337658SMarcel Moolenaar 7986d1a0d267SMarcel Moolenaar /* 7987d1a0d267SMarcel Moolenaar * Parse any libxo-specific options from the command line, removing them 7988d1a0d267SMarcel Moolenaar * so the main() argument parsing won't see them. We return the new value 7989d1a0d267SMarcel Moolenaar * for argc or -1 for error. If an error occurred, the program should 7990d1a0d267SMarcel Moolenaar * exit. A suitable error message has already been displayed. 7991d1a0d267SMarcel Moolenaar */ 799231337658SMarcel Moolenaar int 799331337658SMarcel Moolenaar xo_parse_args (int argc, char **argv) 799431337658SMarcel Moolenaar { 799531337658SMarcel Moolenaar static char libxo_opt[] = "--libxo"; 799631337658SMarcel Moolenaar char *cp; 799731337658SMarcel Moolenaar int i, save; 799831337658SMarcel Moolenaar 799931337658SMarcel Moolenaar /* Save our program name for xo_err and friends */ 800031337658SMarcel Moolenaar xo_program = argv[0]; 800131337658SMarcel Moolenaar cp = strrchr(xo_program, '/'); 800231337658SMarcel Moolenaar if (cp) 800331337658SMarcel Moolenaar xo_program = cp + 1; 800431337658SMarcel Moolenaar 8005f2b7bf8aSPhil Shafer xo_handle_t *xop = xo_default(NULL); 8006f2b7bf8aSPhil Shafer 800731337658SMarcel Moolenaar for (save = i = 1; i < argc; i++) { 800831337658SMarcel Moolenaar if (argv[i] == NULL 800931337658SMarcel Moolenaar || strncmp(argv[i], libxo_opt, sizeof(libxo_opt) - 1) != 0) { 801031337658SMarcel Moolenaar if (save != i) 801131337658SMarcel Moolenaar argv[save] = argv[i]; 801231337658SMarcel Moolenaar save += 1; 801331337658SMarcel Moolenaar continue; 801431337658SMarcel Moolenaar } 801531337658SMarcel Moolenaar 801631337658SMarcel Moolenaar cp = argv[i] + sizeof(libxo_opt) - 1; 8017ee5cf116SPhil Shafer if (*cp == '\0') { 801831337658SMarcel Moolenaar cp = argv[++i]; 8019ee5cf116SPhil Shafer if (cp == NULL) { 802031337658SMarcel Moolenaar xo_warnx("missing libxo option"); 802131337658SMarcel Moolenaar return -1; 802231337658SMarcel Moolenaar } 802331337658SMarcel Moolenaar 8024f2b7bf8aSPhil Shafer if (xo_set_options(xop, cp) < 0) 802531337658SMarcel Moolenaar return -1; 802631337658SMarcel Moolenaar } else if (*cp == ':') { 8027f2b7bf8aSPhil Shafer if (xo_set_options(xop, cp) < 0) 802831337658SMarcel Moolenaar return -1; 802931337658SMarcel Moolenaar 803031337658SMarcel Moolenaar } else if (*cp == '=') { 8031f2b7bf8aSPhil Shafer if (xo_set_options(xop, ++cp) < 0) 803231337658SMarcel Moolenaar return -1; 803331337658SMarcel Moolenaar 803431337658SMarcel Moolenaar } else if (*cp == '-') { 803531337658SMarcel Moolenaar cp += 1; 803631337658SMarcel Moolenaar if (strcmp(cp, "check") == 0) { 803731337658SMarcel Moolenaar exit(XO_HAS_LIBXO); 803831337658SMarcel Moolenaar 803931337658SMarcel Moolenaar } else { 804031337658SMarcel Moolenaar xo_warnx("unknown libxo option: '%s'", argv[i]); 804131337658SMarcel Moolenaar return -1; 804231337658SMarcel Moolenaar } 804331337658SMarcel Moolenaar } else { 804431337658SMarcel Moolenaar xo_warnx("unknown libxo option: '%s'", argv[i]); 804531337658SMarcel Moolenaar return -1; 804631337658SMarcel Moolenaar } 804731337658SMarcel Moolenaar } 804831337658SMarcel Moolenaar 8049f2b7bf8aSPhil Shafer /* 8050f2b7bf8aSPhil Shafer * We only want to do color output on terminals, but we only want 8051f2b7bf8aSPhil Shafer * to do this if the user has asked for color. 8052f2b7bf8aSPhil Shafer */ 8053f2b7bf8aSPhil Shafer if (XOF_ISSET(xop, XOF_COLOR_ALLOWED) && isatty(1)) 8054f2b7bf8aSPhil Shafer XOF_SET(xop, XOF_COLOR); 8055f2b7bf8aSPhil Shafer 805631337658SMarcel Moolenaar argv[save] = NULL; 805731337658SMarcel Moolenaar return save; 805831337658SMarcel Moolenaar } 805931337658SMarcel Moolenaar 8060d1a0d267SMarcel Moolenaar /* 8061d1a0d267SMarcel Moolenaar * Debugging function that dumps the current stack of open libxo constructs, 8062d1a0d267SMarcel Moolenaar * suitable for calling from the debugger. 8063d1a0d267SMarcel Moolenaar */ 8064545ddfbeSMarcel Moolenaar void 8065545ddfbeSMarcel Moolenaar xo_dump_stack (xo_handle_t *xop) 8066545ddfbeSMarcel Moolenaar { 8067545ddfbeSMarcel Moolenaar int i; 8068545ddfbeSMarcel Moolenaar xo_stack_t *xsp; 8069545ddfbeSMarcel Moolenaar 8070545ddfbeSMarcel Moolenaar xop = xo_default(xop); 8071545ddfbeSMarcel Moolenaar 8072545ddfbeSMarcel Moolenaar fprintf(stderr, "Stack dump:\n"); 8073545ddfbeSMarcel Moolenaar 8074545ddfbeSMarcel Moolenaar xsp = xop->xo_stack; 8075545ddfbeSMarcel Moolenaar for (i = 1, xsp++; i <= xop->xo_depth; i++, xsp++) { 8076545ddfbeSMarcel Moolenaar fprintf(stderr, " [%d] %s '%s' [%x]\n", 8077545ddfbeSMarcel Moolenaar i, xo_state_name(xsp->xs_state), 8078545ddfbeSMarcel Moolenaar xsp->xs_name ?: "--", xsp->xs_flags); 8079545ddfbeSMarcel Moolenaar } 8080545ddfbeSMarcel Moolenaar } 8081545ddfbeSMarcel Moolenaar 8082d1a0d267SMarcel Moolenaar /* 8083d1a0d267SMarcel Moolenaar * Record the program name used for error messages 8084d1a0d267SMarcel Moolenaar */ 8085545ddfbeSMarcel Moolenaar void 8086545ddfbeSMarcel Moolenaar xo_set_program (const char *name) 8087545ddfbeSMarcel Moolenaar { 8088545ddfbeSMarcel Moolenaar xo_program = name; 8089545ddfbeSMarcel Moolenaar } 8090545ddfbeSMarcel Moolenaar 8091788ca347SMarcel Moolenaar void 809242ff34c3SPhil Shafer xo_set_version_h (xo_handle_t *xop, const char *version) 8093788ca347SMarcel Moolenaar { 8094788ca347SMarcel Moolenaar xop = xo_default(xop); 8095788ca347SMarcel Moolenaar 8096788ca347SMarcel Moolenaar if (version == NULL || strchr(version, '"') != NULL) 8097788ca347SMarcel Moolenaar return; 8098788ca347SMarcel Moolenaar 8099d1a0d267SMarcel Moolenaar if (!xo_style_is_encoding(xop)) 8100d1a0d267SMarcel Moolenaar return; 8101d1a0d267SMarcel Moolenaar 8102788ca347SMarcel Moolenaar switch (xo_style(xop)) { 8103788ca347SMarcel Moolenaar case XO_STYLE_XML: 8104788ca347SMarcel Moolenaar /* For XML, we record this as an attribute for the first tag */ 81058a6eceffSPhil Shafer xo_attr_h(xop, "version", "%s", version); 8106788ca347SMarcel Moolenaar break; 8107788ca347SMarcel Moolenaar 8108788ca347SMarcel Moolenaar case XO_STYLE_JSON: 8109788ca347SMarcel Moolenaar /* 8110d1a0d267SMarcel Moolenaar * For JSON, we record the version string in our handle, and emit 8111788ca347SMarcel Moolenaar * it in xo_emit_top. 8112788ca347SMarcel Moolenaar */ 8113d1a0d267SMarcel Moolenaar xop->xo_version = xo_strndup(version, -1); 8114d1a0d267SMarcel Moolenaar break; 8115d1a0d267SMarcel Moolenaar 8116d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER: 8117f2b7bf8aSPhil Shafer xo_encoder_handle(xop, XO_OP_VERSION, NULL, version, 0); 8118788ca347SMarcel Moolenaar break; 8119788ca347SMarcel Moolenaar } 8120788ca347SMarcel Moolenaar } 8121788ca347SMarcel Moolenaar 8122d1a0d267SMarcel Moolenaar /* 8123ee5cf116SPhil Shafer * Set the version number for the API content being carried through 8124d1a0d267SMarcel Moolenaar * the xo handle. 8125d1a0d267SMarcel Moolenaar */ 8126788ca347SMarcel Moolenaar void 8127788ca347SMarcel Moolenaar xo_set_version (const char *version) 8128788ca347SMarcel Moolenaar { 8129788ca347SMarcel Moolenaar xo_set_version_h(NULL, version); 8130788ca347SMarcel Moolenaar } 8131788ca347SMarcel Moolenaar 8132d1a0d267SMarcel Moolenaar /* 8133d1a0d267SMarcel Moolenaar * Generate a warning. Normally, this is a text message written to 8134d1a0d267SMarcel Moolenaar * standard error. If the XOF_WARN_XML flag is set, then we generate 8135d1a0d267SMarcel Moolenaar * XMLified content on standard output. 8136d1a0d267SMarcel Moolenaar */ 8137d1a0d267SMarcel Moolenaar void 8138d1a0d267SMarcel Moolenaar xo_emit_warn_hcv (xo_handle_t *xop, int as_warning, int code, 8139d1a0d267SMarcel Moolenaar const char *fmt, va_list vap) 814031337658SMarcel Moolenaar { 8141d1a0d267SMarcel Moolenaar xop = xo_default(xop); 814231337658SMarcel Moolenaar 8143d1a0d267SMarcel Moolenaar if (fmt == NULL) 8144d1a0d267SMarcel Moolenaar return; 814531337658SMarcel Moolenaar 8146d1a0d267SMarcel Moolenaar xo_open_marker_h(xop, "xo_emit_warn_hcv"); 8147d1a0d267SMarcel Moolenaar xo_open_container_h(xop, as_warning ? "__warning" : "__error"); 814831337658SMarcel Moolenaar 8149d1a0d267SMarcel Moolenaar if (xo_program) 8150d1a0d267SMarcel Moolenaar xo_emit("{wc:program}", xo_program); 815131337658SMarcel Moolenaar 8152d1a0d267SMarcel Moolenaar if (xo_style(xop) == XO_STYLE_XML || xo_style(xop) == XO_STYLE_JSON) { 8153d1a0d267SMarcel Moolenaar va_list ap; 8154d1a0d267SMarcel Moolenaar xo_handle_t temp; 815531337658SMarcel Moolenaar 8156d1a0d267SMarcel Moolenaar bzero(&temp, sizeof(temp)); 8157d1a0d267SMarcel Moolenaar temp.xo_style = XO_STYLE_TEXT; 8158d1a0d267SMarcel Moolenaar xo_buf_init(&temp.xo_data); 8159d1a0d267SMarcel Moolenaar xo_depth_check(&temp, XO_DEPTH); 816031337658SMarcel Moolenaar 8161d1a0d267SMarcel Moolenaar va_copy(ap, vap); 8162d1a0d267SMarcel Moolenaar (void) xo_emit_hv(&temp, fmt, ap); 8163d1a0d267SMarcel Moolenaar va_end(ap); 816431337658SMarcel Moolenaar 8165d1a0d267SMarcel Moolenaar xo_buffer_t *src = &temp.xo_data; 8166d1a0d267SMarcel Moolenaar xo_format_value(xop, "message", 7, src->xb_bufp, 8167264104f2SPhil Shafer src->xb_curp - src->xb_bufp, NULL, 0, NULL, 0, 0); 816831337658SMarcel Moolenaar 8169d1a0d267SMarcel Moolenaar xo_free(temp.xo_stack); 8170d1a0d267SMarcel Moolenaar xo_buf_cleanup(src); 817131337658SMarcel Moolenaar } 817231337658SMarcel Moolenaar 8173d1a0d267SMarcel Moolenaar (void) xo_emit_hv(xop, fmt, vap); 817431337658SMarcel Moolenaar 81758a6eceffSPhil Shafer ssize_t len = strlen(fmt); 8176d1a0d267SMarcel Moolenaar if (len > 0 && fmt[len - 1] != '\n') { 8177d1a0d267SMarcel Moolenaar if (code > 0) { 8178d1a0d267SMarcel Moolenaar const char *msg = strerror(code); 8179d1a0d267SMarcel Moolenaar if (msg) 8180d1a0d267SMarcel Moolenaar xo_emit_h(xop, ": {G:strerror}{g:error/%s}", msg); 8181d1a0d267SMarcel Moolenaar } 8182d1a0d267SMarcel Moolenaar xo_emit("\n"); 818331337658SMarcel Moolenaar } 818431337658SMarcel Moolenaar 8185d1a0d267SMarcel Moolenaar xo_close_marker_h(xop, "xo_emit_warn_hcv"); 8186d1a0d267SMarcel Moolenaar xo_flush_h(xop); 818731337658SMarcel Moolenaar } 818831337658SMarcel Moolenaar 8189d1a0d267SMarcel Moolenaar void 8190d1a0d267SMarcel Moolenaar xo_emit_warn_hc (xo_handle_t *xop, int code, const char *fmt, ...) 8191d1a0d267SMarcel Moolenaar { 8192d1a0d267SMarcel Moolenaar va_list vap; 819331337658SMarcel Moolenaar 8194d1a0d267SMarcel Moolenaar va_start(vap, fmt); 8195d1a0d267SMarcel Moolenaar xo_emit_warn_hcv(xop, 1, code, fmt, vap); 8196d1a0d267SMarcel Moolenaar va_end(vap); 819731337658SMarcel Moolenaar } 819831337658SMarcel Moolenaar 8199d1a0d267SMarcel Moolenaar void 8200d1a0d267SMarcel Moolenaar xo_emit_warn_c (int code, const char *fmt, ...) 8201d1a0d267SMarcel Moolenaar { 8202d1a0d267SMarcel Moolenaar va_list vap; 820331337658SMarcel Moolenaar 8204d1a0d267SMarcel Moolenaar va_start(vap, fmt); 8205d1a0d267SMarcel Moolenaar xo_emit_warn_hcv(NULL, 1, code, fmt, vap); 8206d1a0d267SMarcel Moolenaar va_end(vap); 8207d1a0d267SMarcel Moolenaar } 820831337658SMarcel Moolenaar 8209d1a0d267SMarcel Moolenaar void 8210d1a0d267SMarcel Moolenaar xo_emit_warn (const char *fmt, ...) 8211d1a0d267SMarcel Moolenaar { 8212d1a0d267SMarcel Moolenaar int code = errno; 8213d1a0d267SMarcel Moolenaar va_list vap; 8214d1a0d267SMarcel Moolenaar 8215d1a0d267SMarcel Moolenaar va_start(vap, fmt); 8216d1a0d267SMarcel Moolenaar xo_emit_warn_hcv(NULL, 1, code, fmt, vap); 8217d1a0d267SMarcel Moolenaar va_end(vap); 8218d1a0d267SMarcel Moolenaar } 8219d1a0d267SMarcel Moolenaar 8220d1a0d267SMarcel Moolenaar void 8221d1a0d267SMarcel Moolenaar xo_emit_warnx (const char *fmt, ...) 8222d1a0d267SMarcel Moolenaar { 8223d1a0d267SMarcel Moolenaar va_list vap; 8224d1a0d267SMarcel Moolenaar 8225d1a0d267SMarcel Moolenaar va_start(vap, fmt); 8226d1a0d267SMarcel Moolenaar xo_emit_warn_hcv(NULL, 1, -1, fmt, vap); 8227d1a0d267SMarcel Moolenaar va_end(vap); 8228d1a0d267SMarcel Moolenaar } 8229d1a0d267SMarcel Moolenaar 8230d1a0d267SMarcel Moolenaar void 8231d1a0d267SMarcel Moolenaar xo_emit_err_v (int eval, int code, const char *fmt, va_list vap) 8232d1a0d267SMarcel Moolenaar { 8233d1a0d267SMarcel Moolenaar xo_emit_warn_hcv(NULL, 0, code, fmt, vap); 823431337658SMarcel Moolenaar xo_finish(); 8235d1a0d267SMarcel Moolenaar exit(eval); 823631337658SMarcel Moolenaar } 8237d1a0d267SMarcel Moolenaar 8238d1a0d267SMarcel Moolenaar void 8239d1a0d267SMarcel Moolenaar xo_emit_err (int eval, const char *fmt, ...) 8240d1a0d267SMarcel Moolenaar { 8241d1a0d267SMarcel Moolenaar int code = errno; 8242d1a0d267SMarcel Moolenaar va_list vap; 8243d1a0d267SMarcel Moolenaar va_start(vap, fmt); 8244d1a0d267SMarcel Moolenaar xo_emit_err_v(0, code, fmt, vap); 8245d1a0d267SMarcel Moolenaar va_end(vap); 8246d1a0d267SMarcel Moolenaar exit(eval); 8247d1a0d267SMarcel Moolenaar } 8248d1a0d267SMarcel Moolenaar 8249d1a0d267SMarcel Moolenaar void 8250d1a0d267SMarcel Moolenaar xo_emit_errx (int eval, const char *fmt, ...) 8251d1a0d267SMarcel Moolenaar { 8252d1a0d267SMarcel Moolenaar va_list vap; 8253d1a0d267SMarcel Moolenaar 8254d1a0d267SMarcel Moolenaar va_start(vap, fmt); 8255d1a0d267SMarcel Moolenaar xo_emit_err_v(0, -1, fmt, vap); 8256d1a0d267SMarcel Moolenaar va_end(vap); 8257d1a0d267SMarcel Moolenaar xo_finish(); 8258d1a0d267SMarcel Moolenaar exit(eval); 8259d1a0d267SMarcel Moolenaar } 8260d1a0d267SMarcel Moolenaar 8261d1a0d267SMarcel Moolenaar void 8262d1a0d267SMarcel Moolenaar xo_emit_errc (int eval, int code, const char *fmt, ...) 8263d1a0d267SMarcel Moolenaar { 8264d1a0d267SMarcel Moolenaar va_list vap; 8265d1a0d267SMarcel Moolenaar 8266d1a0d267SMarcel Moolenaar va_start(vap, fmt); 8267d1a0d267SMarcel Moolenaar xo_emit_warn_hcv(NULL, 0, code, fmt, vap); 8268d1a0d267SMarcel Moolenaar va_end(vap); 8269d1a0d267SMarcel Moolenaar xo_finish(); 8270d1a0d267SMarcel Moolenaar exit(eval); 8271d1a0d267SMarcel Moolenaar } 8272d1a0d267SMarcel Moolenaar 8273d1a0d267SMarcel Moolenaar /* 8274d1a0d267SMarcel Moolenaar * Get the opaque private pointer for an xo handle 8275d1a0d267SMarcel Moolenaar */ 8276d1a0d267SMarcel Moolenaar void * 8277d1a0d267SMarcel Moolenaar xo_get_private (xo_handle_t *xop) 8278d1a0d267SMarcel Moolenaar { 8279d1a0d267SMarcel Moolenaar xop = xo_default(xop); 8280d1a0d267SMarcel Moolenaar return xop->xo_private; 8281d1a0d267SMarcel Moolenaar } 8282d1a0d267SMarcel Moolenaar 8283d1a0d267SMarcel Moolenaar /* 8284d1a0d267SMarcel Moolenaar * Set the opaque private pointer for an xo handle. 8285d1a0d267SMarcel Moolenaar */ 8286d1a0d267SMarcel Moolenaar void 8287d1a0d267SMarcel Moolenaar xo_set_private (xo_handle_t *xop, void *opaque) 8288d1a0d267SMarcel Moolenaar { 8289d1a0d267SMarcel Moolenaar xop = xo_default(xop); 8290d1a0d267SMarcel Moolenaar xop->xo_private = opaque; 8291d1a0d267SMarcel Moolenaar } 8292d1a0d267SMarcel Moolenaar 8293d1a0d267SMarcel Moolenaar /* 8294d1a0d267SMarcel Moolenaar * Get the encoder function 8295d1a0d267SMarcel Moolenaar */ 8296d1a0d267SMarcel Moolenaar xo_encoder_func_t 8297d1a0d267SMarcel Moolenaar xo_get_encoder (xo_handle_t *xop) 8298d1a0d267SMarcel Moolenaar { 8299d1a0d267SMarcel Moolenaar xop = xo_default(xop); 8300d1a0d267SMarcel Moolenaar return xop->xo_encoder; 8301d1a0d267SMarcel Moolenaar } 8302d1a0d267SMarcel Moolenaar 8303d1a0d267SMarcel Moolenaar /* 8304d1a0d267SMarcel Moolenaar * Record an encoder callback function in an xo handle. 8305d1a0d267SMarcel Moolenaar */ 8306d1a0d267SMarcel Moolenaar void 8307d1a0d267SMarcel Moolenaar xo_set_encoder (xo_handle_t *xop, xo_encoder_func_t encoder) 8308d1a0d267SMarcel Moolenaar { 8309d1a0d267SMarcel Moolenaar xop = xo_default(xop); 8310d1a0d267SMarcel Moolenaar 8311d1a0d267SMarcel Moolenaar xop->xo_style = XO_STYLE_ENCODER; 8312d1a0d267SMarcel Moolenaar xop->xo_encoder = encoder; 8313d1a0d267SMarcel Moolenaar } 8314