131337658SMarcel Moolenaar /*
25e203a9dSPhil Shafer * Copyright (c) 2014-2019, 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"
50406a584dSPhil Shafer #include "xo_explicit.h"
51d1a0d267SMarcel Moolenaar
52d1a0d267SMarcel Moolenaar /*
53d1a0d267SMarcel Moolenaar * We ask wcwidth() to do an impossible job, really. It's supposed to
54d1a0d267SMarcel Moolenaar * need to tell us the number of columns consumed to display a unicode
55d1a0d267SMarcel Moolenaar * character. It returns that number without any sort of context, but
56d1a0d267SMarcel Moolenaar * we know they are characters whose glyph differs based on placement
57d1a0d267SMarcel Moolenaar * (end of word, middle of word, etc) and many that affect characters
58d1a0d267SMarcel Moolenaar * previously emitted. Without content, it can't hope to tell us.
59d1a0d267SMarcel Moolenaar * But it's the only standard tool we've got, so we use it. We would
60ee5cf116SPhil Shafer * use wcswidth() but it typically just loops through adding the results
61d1a0d267SMarcel Moolenaar * of wcwidth() calls in an entirely unhelpful way.
62d1a0d267SMarcel Moolenaar *
63d1a0d267SMarcel Moolenaar * Even then, there are many poor implementations (macosx), so we have
64d1a0d267SMarcel Moolenaar * to carry our own. We could have configure.ac test this (with
65d1a0d267SMarcel Moolenaar * something like 'assert(wcwidth(0x200d) == 0)'), but it would have
66d1a0d267SMarcel Moolenaar * to run a binary, which breaks cross-compilation. Hmm... I could
67d1a0d267SMarcel Moolenaar * run this test at init time and make a warning for our dear user.
68d1a0d267SMarcel Moolenaar *
69d1a0d267SMarcel Moolenaar * Anyhow, it remains a best-effort sort of thing. And it's all made
70d1a0d267SMarcel Moolenaar * more hopeless because we assume the display code doing the rendering is
71d1a0d267SMarcel Moolenaar * playing by the same rules we are. If it display 0x200d as a square
72d1a0d267SMarcel Moolenaar * box or a funky question mark, the output will be hosed.
73d1a0d267SMarcel Moolenaar */
74d1a0d267SMarcel Moolenaar #ifdef LIBXO_WCWIDTH
75d1a0d267SMarcel Moolenaar #include "xo_wcwidth.h"
76d1a0d267SMarcel Moolenaar #else /* LIBXO_WCWIDTH */
77d1a0d267SMarcel Moolenaar #define xo_wcwidth(_x) wcwidth(_x)
78d1a0d267SMarcel Moolenaar #endif /* LIBXO_WCWIDTH */
7931337658SMarcel Moolenaar
80545ddfbeSMarcel Moolenaar #ifdef HAVE_STDIO_EXT_H
81545ddfbeSMarcel Moolenaar #include <stdio_ext.h>
82545ddfbeSMarcel Moolenaar #endif /* HAVE_STDIO_EXT_H */
83545ddfbeSMarcel Moolenaar
84d1a0d267SMarcel Moolenaar /*
85d1a0d267SMarcel Moolenaar * humanize_number is a great function, unless you don't have it. So
86d1a0d267SMarcel Moolenaar * we carry one in our pocket.
87d1a0d267SMarcel Moolenaar */
88d1a0d267SMarcel Moolenaar #ifdef HAVE_HUMANIZE_NUMBER
89d1a0d267SMarcel Moolenaar #include <libutil.h>
90d1a0d267SMarcel Moolenaar #define xo_humanize_number humanize_number
91d1a0d267SMarcel Moolenaar #else /* HAVE_HUMANIZE_NUMBER */
92d1a0d267SMarcel Moolenaar #include "xo_humanize.h"
93d1a0d267SMarcel Moolenaar #endif /* HAVE_HUMANIZE_NUMBER */
94d1a0d267SMarcel Moolenaar
95d1a0d267SMarcel Moolenaar #ifdef HAVE_GETTEXT
96d1a0d267SMarcel Moolenaar #include <libintl.h>
97d1a0d267SMarcel Moolenaar #endif /* HAVE_GETTEXT */
98d1a0d267SMarcel Moolenaar
99264104f2SPhil Shafer /* Rather lame that we can't count on these... */
100264104f2SPhil Shafer #ifndef FALSE
101264104f2SPhil Shafer #define FALSE 0
102264104f2SPhil Shafer #endif
103264104f2SPhil Shafer #ifndef TRUE
104264104f2SPhil Shafer #define TRUE 1
105264104f2SPhil Shafer #endif
106264104f2SPhil Shafer
107d1a0d267SMarcel Moolenaar /*
108d1a0d267SMarcel Moolenaar * Three styles of specifying thread-local variables are supported.
109ee5cf116SPhil Shafer * configure.ac has the brains to run each possibility through the
110d1a0d267SMarcel Moolenaar * compiler and see what works; we are left to define the THREAD_LOCAL
111d1a0d267SMarcel Moolenaar * macro to the right value. Most toolchains (clang, gcc) use
112d1a0d267SMarcel Moolenaar * "before", but some (borland) use "after" and I've heard of some
113d1a0d267SMarcel Moolenaar * (ms) that use __declspec. Any others out there?
114d1a0d267SMarcel Moolenaar */
115d1a0d267SMarcel Moolenaar #define THREAD_LOCAL_before 1
116d1a0d267SMarcel Moolenaar #define THREAD_LOCAL_after 2
117d1a0d267SMarcel Moolenaar #define THREAD_LOCAL_declspec 3
118d1a0d267SMarcel Moolenaar
119d1a0d267SMarcel Moolenaar #ifndef HAVE_THREAD_LOCAL
120d1a0d267SMarcel Moolenaar #define THREAD_LOCAL(_x) _x
121d1a0d267SMarcel Moolenaar #elif HAVE_THREAD_LOCAL == THREAD_LOCAL_before
122d1a0d267SMarcel Moolenaar #define THREAD_LOCAL(_x) __thread _x
123d1a0d267SMarcel Moolenaar #elif HAVE_THREAD_LOCAL == THREAD_LOCAL_after
124d1a0d267SMarcel Moolenaar #define THREAD_LOCAL(_x) _x __thread
125d1a0d267SMarcel Moolenaar #elif HAVE_THREAD_LOCAL == THREAD_LOCAL_declspec
126d1a0d267SMarcel Moolenaar #define THREAD_LOCAL(_x) __declspec(_x)
127d1a0d267SMarcel Moolenaar #else
128d1a0d267SMarcel Moolenaar #error unknown thread-local setting
129d1a0d267SMarcel Moolenaar #endif /* HAVE_THREADS_H */
130d1a0d267SMarcel Moolenaar
13131337658SMarcel Moolenaar const char xo_version[] = LIBXO_VERSION;
13231337658SMarcel Moolenaar const char xo_version_extra[] = LIBXO_VERSION_EXTRA;
13342ff34c3SPhil Shafer static const char xo_default_format[] = "%s";
13431337658SMarcel Moolenaar
13531337658SMarcel Moolenaar #ifndef UNUSED
13631337658SMarcel Moolenaar #define UNUSED __attribute__ ((__unused__))
13731337658SMarcel Moolenaar #endif /* UNUSED */
13831337658SMarcel Moolenaar
13931337658SMarcel Moolenaar #define XO_INDENT_BY 2 /* Amount to indent when pretty printing */
140d1a0d267SMarcel Moolenaar #define XO_DEPTH 128 /* Default stack depth */
141fd5e3f3eSPhil Shafer #define XO_MAX_ANCHOR_WIDTH (8*1024) /* Anything wider is just silly */
14231337658SMarcel Moolenaar
14331337658SMarcel Moolenaar #define XO_FAILURE_NAME "failure"
14431337658SMarcel Moolenaar
14531337658SMarcel Moolenaar /* Flags for the stack frame */
14631337658SMarcel Moolenaar typedef unsigned xo_xsf_flags_t; /* XSF_* flags */
14731337658SMarcel Moolenaar #define XSF_NOT_FIRST (1<<0) /* Not the first element */
14831337658SMarcel Moolenaar #define XSF_LIST (1<<1) /* Frame is a list */
14931337658SMarcel Moolenaar #define XSF_INSTANCE (1<<2) /* Frame is an instance */
15031337658SMarcel Moolenaar #define XSF_DTRT (1<<3) /* Save the name for DTRT mode */
15131337658SMarcel Moolenaar
152545ddfbeSMarcel Moolenaar #define XSF_CONTENT (1<<4) /* Some content has been emitted */
153545ddfbeSMarcel Moolenaar #define XSF_EMIT (1<<5) /* Some field has been emitted */
154545ddfbeSMarcel Moolenaar #define XSF_EMIT_KEY (1<<6) /* A key has been emitted */
155545ddfbeSMarcel Moolenaar #define XSF_EMIT_LEAF_LIST (1<<7) /* A leaf-list field has been emitted */
156545ddfbeSMarcel Moolenaar
157545ddfbeSMarcel Moolenaar /* These are the flags we propagate between markers and their parents */
158545ddfbeSMarcel Moolenaar #define XSF_MARKER_FLAGS \
159545ddfbeSMarcel Moolenaar (XSF_NOT_FIRST | XSF_CONTENT | XSF_EMIT | XSF_EMIT_KEY | XSF_EMIT_LEAF_LIST )
160545ddfbeSMarcel Moolenaar
161545ddfbeSMarcel Moolenaar /*
162406a584dSPhil Shafer * Turn the transition between two states into a number suitable for
163406a584dSPhil Shafer * a "switch" statement.
164545ddfbeSMarcel Moolenaar */
165545ddfbeSMarcel Moolenaar #define XSS_TRANSITION(_old, _new) ((_old) << 8 | (_new))
166545ddfbeSMarcel Moolenaar
16731337658SMarcel Moolenaar /*
16831337658SMarcel Moolenaar * xo_stack_t: As we open and close containers and levels, we
16931337658SMarcel Moolenaar * create a stack of frames to track them. This is needed for
17031337658SMarcel Moolenaar * XOF_WARN and XOF_XPATH.
17131337658SMarcel Moolenaar */
17231337658SMarcel Moolenaar typedef struct xo_stack_s {
17331337658SMarcel Moolenaar xo_xsf_flags_t xs_flags; /* Flags for this frame */
174545ddfbeSMarcel Moolenaar xo_state_t xs_state; /* State for this stack frame */
17531337658SMarcel Moolenaar char *xs_name; /* Name (for XPath value) */
17631337658SMarcel Moolenaar char *xs_keys; /* XPath predicate for any key fields */
17731337658SMarcel Moolenaar } xo_stack_t;
17831337658SMarcel Moolenaar
179d1a0d267SMarcel Moolenaar /*
180d1a0d267SMarcel Moolenaar * libxo supports colors and effects, for those who like them.
181d1a0d267SMarcel Moolenaar * XO_COL_* ("colors") refers to fancy ansi codes, while X__EFF_*
182d1a0d267SMarcel Moolenaar * ("effects") are bits since we need to maintain state.
183d1a0d267SMarcel Moolenaar */
184f2b7bf8aSPhil Shafer typedef uint8_t xo_color_t;
185788ca347SMarcel Moolenaar #define XO_COL_DEFAULT 0
186788ca347SMarcel Moolenaar #define XO_COL_BLACK 1
187788ca347SMarcel Moolenaar #define XO_COL_RED 2
188788ca347SMarcel Moolenaar #define XO_COL_GREEN 3
189788ca347SMarcel Moolenaar #define XO_COL_YELLOW 4
190788ca347SMarcel Moolenaar #define XO_COL_BLUE 5
191788ca347SMarcel Moolenaar #define XO_COL_MAGENTA 6
192788ca347SMarcel Moolenaar #define XO_COL_CYAN 7
193788ca347SMarcel Moolenaar #define XO_COL_WHITE 8
194788ca347SMarcel Moolenaar
195788ca347SMarcel Moolenaar #define XO_NUM_COLORS 9
196788ca347SMarcel Moolenaar
197788ca347SMarcel Moolenaar /*
198788ca347SMarcel Moolenaar * Yes, there's no blink. We're civilized. We like users. Blink
199788ca347SMarcel Moolenaar * isn't something one does to someone you like. Friends don't let
200788ca347SMarcel Moolenaar * friends use blink. On friends. You know what I mean. Blink is
201788ca347SMarcel Moolenaar * like, well, it's like bursting into show tunes at a funeral. It's
202788ca347SMarcel Moolenaar * just not done. Not something anyone wants. And on those rare
203d1a0d267SMarcel Moolenaar * instances where it might actually be appropriate, it's still wrong,
204d1a0d267SMarcel Moolenaar * since it's likely done by the wrong person for the wrong reason.
205d1a0d267SMarcel Moolenaar * Just like blink. And if I implemented blink, I'd be like a funeral
206788ca347SMarcel Moolenaar * director who adds "Would you like us to burst into show tunes?" on
207d1a0d267SMarcel Moolenaar * the list of questions asked while making funeral arrangements.
208788ca347SMarcel Moolenaar * It's formalizing wrongness in the wrong way. And we're just too
209788ca347SMarcel Moolenaar * civilized to do that. Hhhmph!
210788ca347SMarcel Moolenaar */
211788ca347SMarcel Moolenaar #define XO_EFF_RESET (1<<0)
212788ca347SMarcel Moolenaar #define XO_EFF_NORMAL (1<<1)
213788ca347SMarcel Moolenaar #define XO_EFF_BOLD (1<<2)
214788ca347SMarcel Moolenaar #define XO_EFF_UNDERLINE (1<<3)
215788ca347SMarcel Moolenaar #define XO_EFF_INVERSE (1<<4)
216788ca347SMarcel Moolenaar
217d1a0d267SMarcel Moolenaar #define XO_EFF_CLEAR_BITS XO_EFF_RESET /* Reset gets reset, surprisingly */
218788ca347SMarcel Moolenaar
219788ca347SMarcel Moolenaar typedef uint8_t xo_effect_t;
220788ca347SMarcel Moolenaar typedef struct xo_colors_s {
221788ca347SMarcel Moolenaar xo_effect_t xoc_effects; /* Current effect set */
222788ca347SMarcel Moolenaar xo_color_t xoc_col_fg; /* Foreground color */
223788ca347SMarcel Moolenaar xo_color_t xoc_col_bg; /* Background color */
224788ca347SMarcel Moolenaar } xo_colors_t;
225788ca347SMarcel Moolenaar
22631337658SMarcel Moolenaar /*
22731337658SMarcel Moolenaar * xo_handle_t: this is the principle data structure for libxo.
228d1a0d267SMarcel Moolenaar * It's used as a store for state, options, content, and all manor
229d1a0d267SMarcel Moolenaar * of other information.
23031337658SMarcel Moolenaar */
23131337658SMarcel Moolenaar struct xo_handle_s {
232d1a0d267SMarcel Moolenaar xo_xof_flags_t xo_flags; /* Flags (XOF_*) from the user*/
233d1a0d267SMarcel Moolenaar xo_xof_flags_t xo_iflags; /* Internal flags (XOIF_*) */
234d1a0d267SMarcel Moolenaar xo_style_t xo_style; /* XO_STYLE_* value */
23531337658SMarcel Moolenaar unsigned short xo_indent; /* Indent level (if pretty) */
23631337658SMarcel Moolenaar unsigned short xo_indent_by; /* Indent amount (tab stop) */
23731337658SMarcel Moolenaar xo_write_func_t xo_write; /* Write callback */
238a0f704ffSMarcel Moolenaar xo_close_func_t xo_close; /* Close callback */
239545ddfbeSMarcel Moolenaar xo_flush_func_t xo_flush; /* Flush callback */
24031337658SMarcel Moolenaar xo_formatter_t xo_formatter; /* Custom formating function */
24131337658SMarcel Moolenaar xo_checkpointer_t xo_checkpointer; /* Custom formating support function */
24231337658SMarcel Moolenaar void *xo_opaque; /* Opaque data for write function */
24331337658SMarcel Moolenaar xo_buffer_t xo_data; /* Output data */
24431337658SMarcel Moolenaar xo_buffer_t xo_fmt; /* Work area for building format strings */
24531337658SMarcel Moolenaar xo_buffer_t xo_attrs; /* Work area for building XML attributes */
24631337658SMarcel Moolenaar xo_buffer_t xo_predicate; /* Work area for building XPath predicates */
24731337658SMarcel Moolenaar xo_stack_t *xo_stack; /* Stack pointer */
24831337658SMarcel Moolenaar int xo_depth; /* Depth of stack */
24931337658SMarcel Moolenaar int xo_stack_size; /* Size of the stack */
25031337658SMarcel Moolenaar xo_info_t *xo_info; /* Info fields for all elements */
25131337658SMarcel Moolenaar int xo_info_count; /* Number of info entries */
25231337658SMarcel Moolenaar va_list xo_vap; /* Variable arguments (stdargs) */
25331337658SMarcel Moolenaar char *xo_leading_xpath; /* A leading XPath expression */
25431337658SMarcel Moolenaar mbstate_t xo_mbstate; /* Multi-byte character conversion state */
2558a6eceffSPhil Shafer ssize_t xo_anchor_offset; /* Start of anchored text */
2568a6eceffSPhil Shafer ssize_t xo_anchor_columns; /* Number of columns since the start anchor */
2578a6eceffSPhil Shafer ssize_t xo_anchor_min_width; /* Desired width of anchored text */
2588a6eceffSPhil Shafer ssize_t xo_units_offset; /* Start of units insertion point */
2598a6eceffSPhil Shafer ssize_t xo_columns; /* Columns emitted during this xo_emit call */
260f2b7bf8aSPhil Shafer #ifndef LIBXO_TEXT_ONLY
261406a584dSPhil Shafer xo_color_t xo_color_map_fg[XO_NUM_COLORS]; /* Foreground color mappings */
262406a584dSPhil Shafer xo_color_t xo_color_map_bg[XO_NUM_COLORS]; /* Background color mappings */
263f2b7bf8aSPhil Shafer #endif /* LIBXO_TEXT_ONLY */
264788ca347SMarcel Moolenaar xo_colors_t xo_colors; /* Current color and effect values */
265788ca347SMarcel Moolenaar xo_buffer_t xo_color_buf; /* HTML: buffer of colors and effects */
266788ca347SMarcel Moolenaar char *xo_version; /* Version string */
267d1a0d267SMarcel Moolenaar int xo_errno; /* Saved errno for "%m" */
268d1a0d267SMarcel Moolenaar char *xo_gt_domain; /* Gettext domain, suitable for dgettext(3) */
269d1a0d267SMarcel Moolenaar xo_encoder_func_t xo_encoder; /* Encoding function */
270d1a0d267SMarcel Moolenaar void *xo_private; /* Private data for external encoders */
27131337658SMarcel Moolenaar };
27231337658SMarcel Moolenaar
273d1a0d267SMarcel Moolenaar /* Flag operations */
274d1a0d267SMarcel Moolenaar #define XOF_BIT_ISSET(_flag, _bit) (((_flag) & (_bit)) ? 1 : 0)
275d1a0d267SMarcel Moolenaar #define XOF_BIT_SET(_flag, _bit) do { (_flag) |= (_bit); } while (0)
276d1a0d267SMarcel Moolenaar #define XOF_BIT_CLEAR(_flag, _bit) do { (_flag) &= ~(_bit); } while (0)
277d1a0d267SMarcel Moolenaar
278d1a0d267SMarcel Moolenaar #define XOF_ISSET(_xop, _bit) XOF_BIT_ISSET(_xop->xo_flags, _bit)
279d1a0d267SMarcel Moolenaar #define XOF_SET(_xop, _bit) XOF_BIT_SET(_xop->xo_flags, _bit)
280d1a0d267SMarcel Moolenaar #define XOF_CLEAR(_xop, _bit) XOF_BIT_CLEAR(_xop->xo_flags, _bit)
281d1a0d267SMarcel Moolenaar
282d1a0d267SMarcel Moolenaar #define XOIF_ISSET(_xop, _bit) XOF_BIT_ISSET(_xop->xo_iflags, _bit)
283d1a0d267SMarcel Moolenaar #define XOIF_SET(_xop, _bit) XOF_BIT_SET(_xop->xo_iflags, _bit)
284d1a0d267SMarcel Moolenaar #define XOIF_CLEAR(_xop, _bit) XOF_BIT_CLEAR(_xop->xo_iflags, _bit)
285d1a0d267SMarcel Moolenaar
286d1a0d267SMarcel Moolenaar /* Internal flags */
287d1a0d267SMarcel Moolenaar #define XOIF_REORDER XOF_BIT(0) /* Reordering fields; record field info */
288d1a0d267SMarcel Moolenaar #define XOIF_DIV_OPEN XOF_BIT(1) /* A <div> is open */
289d1a0d267SMarcel Moolenaar #define XOIF_TOP_EMITTED XOF_BIT(2) /* The top JSON braces have been emitted */
290d1a0d267SMarcel Moolenaar #define XOIF_ANCHOR XOF_BIT(3) /* An anchor is in place */
291d1a0d267SMarcel Moolenaar
292d1a0d267SMarcel Moolenaar #define XOIF_UNITS_PENDING XOF_BIT(4) /* We have a units-insertion pending */
293d1a0d267SMarcel Moolenaar #define XOIF_INIT_IN_PROGRESS XOF_BIT(5) /* Init of handle is in progress */
294406a584dSPhil Shafer #define XOIF_MADE_OUTPUT XOF_BIT(6) /* Have already made output */
295d1a0d267SMarcel Moolenaar
29631337658SMarcel Moolenaar /*
29731337658SMarcel Moolenaar * Normal printf has width and precision, which for strings operate as
29831337658SMarcel Moolenaar * min and max number of columns. But this depends on the idea that
29931337658SMarcel Moolenaar * one byte means one column, which UTF-8 and multi-byte characters
30031337658SMarcel Moolenaar * pitches on its ear. It may take 40 bytes of data to populate 14
30131337658SMarcel Moolenaar * columns, but we can't go off looking at 40 bytes of data without the
30231337658SMarcel Moolenaar * caller's permission for fear/knowledge that we'll generate core files.
30331337658SMarcel Moolenaar *
30431337658SMarcel Moolenaar * So we make three values, distinguishing between "max column" and
30531337658SMarcel Moolenaar * "number of bytes that we will inspect inspect safely" We call the
30631337658SMarcel Moolenaar * later "size", and make the format "%[[<min>].[[<size>].<max>]]s".
30731337658SMarcel Moolenaar *
30831337658SMarcel Moolenaar * Under the "first do no harm" theory, we default "max" to "size".
30931337658SMarcel Moolenaar * This is a reasonable assumption for folks that don't grok the
31031337658SMarcel Moolenaar * MBS/WCS/UTF-8 world, and while it will be annoying, it will never
31131337658SMarcel Moolenaar * be evil.
31231337658SMarcel Moolenaar *
31331337658SMarcel Moolenaar * For example, xo_emit("{:tag/%-14.14s}", buf) will make 14
31431337658SMarcel Moolenaar * columns of output, but will never look at more than 14 bytes of the
31531337658SMarcel Moolenaar * input buffer. This is mostly compatible with printf and caller's
31631337658SMarcel Moolenaar * expectations.
31731337658SMarcel Moolenaar *
31831337658SMarcel Moolenaar * In contrast xo_emit("{:tag/%-14..14s}", buf) will look at however
31931337658SMarcel Moolenaar * many bytes (or until a NUL is seen) are needed to fill 14 columns
32031337658SMarcel Moolenaar * of output. xo_emit("{:tag/%-14.*.14s}", xx, buf) will look at up
32131337658SMarcel Moolenaar * to xx bytes (or until a NUL is seen) in order to fill 14 columns
32231337658SMarcel Moolenaar * of output.
32331337658SMarcel Moolenaar *
32431337658SMarcel Moolenaar * It's fairly amazing how a good idea (handle all languages of the
32531337658SMarcel Moolenaar * world) blows such a big hole in the bottom of the fairly weak boat
32631337658SMarcel Moolenaar * that is C string handling. The simplicity and completenesss are
32731337658SMarcel Moolenaar * sunk in ways we haven't even begun to understand.
32831337658SMarcel Moolenaar */
32931337658SMarcel Moolenaar #define XF_WIDTH_MIN 0 /* Minimal width */
33031337658SMarcel Moolenaar #define XF_WIDTH_SIZE 1 /* Maximum number of bytes to examine */
33131337658SMarcel Moolenaar #define XF_WIDTH_MAX 2 /* Maximum width */
33231337658SMarcel Moolenaar #define XF_WIDTH_NUM 3 /* Numeric fields in printf (min.size.max) */
33331337658SMarcel Moolenaar
33431337658SMarcel Moolenaar /* Input and output string encodings */
33531337658SMarcel Moolenaar #define XF_ENC_WIDE 1 /* Wide characters (wchar_t) */
33631337658SMarcel Moolenaar #define XF_ENC_UTF8 2 /* UTF-8 */
33731337658SMarcel Moolenaar #define XF_ENC_LOCALE 3 /* Current locale */
33831337658SMarcel Moolenaar
33931337658SMarcel Moolenaar /*
34031337658SMarcel Moolenaar * A place to parse printf-style format flags for each field
34131337658SMarcel Moolenaar */
34231337658SMarcel Moolenaar typedef struct xo_format_s {
34331337658SMarcel Moolenaar unsigned char xf_fc; /* Format character */
34431337658SMarcel Moolenaar unsigned char xf_enc; /* Encoding of the string (XF_ENC_*) */
34531337658SMarcel Moolenaar unsigned char xf_skip; /* Skip this field */
34631337658SMarcel Moolenaar unsigned char xf_lflag; /* 'l' (long) */
34731337658SMarcel Moolenaar unsigned char xf_hflag;; /* 'h' (half) */
34831337658SMarcel Moolenaar unsigned char xf_jflag; /* 'j' (intmax_t) */
34931337658SMarcel Moolenaar unsigned char xf_tflag; /* 't' (ptrdiff_t) */
35031337658SMarcel Moolenaar unsigned char xf_zflag; /* 'z' (size_t) */
35131337658SMarcel Moolenaar unsigned char xf_qflag; /* 'q' (quad_t) */
35231337658SMarcel Moolenaar unsigned char xf_seen_minus; /* Seen a minus */
35331337658SMarcel Moolenaar int xf_leading_zero; /* Seen a leading zero (zero fill) */
35431337658SMarcel Moolenaar unsigned xf_dots; /* Seen one or more '.'s */
35531337658SMarcel Moolenaar int xf_width[XF_WIDTH_NUM]; /* Width/precision/size numeric fields */
35631337658SMarcel Moolenaar unsigned xf_stars; /* Seen one or more '*'s */
35731337658SMarcel Moolenaar unsigned char xf_star[XF_WIDTH_NUM]; /* Seen one or more '*'s */
35831337658SMarcel Moolenaar } xo_format_t;
35931337658SMarcel Moolenaar
36031337658SMarcel Moolenaar /*
361d1a0d267SMarcel Moolenaar * This structure represents the parsed field information, suitable for
362d1a0d267SMarcel Moolenaar * processing by xo_do_emit and anything else that needs to parse fields.
363d1a0d267SMarcel Moolenaar * Note that all pointers point to the main format string.
364d1a0d267SMarcel Moolenaar *
365d1a0d267SMarcel Moolenaar * XXX This is a first step toward compilable or cachable format
366d1a0d267SMarcel Moolenaar * strings. We can also cache the results of dgettext when no format
367d1a0d267SMarcel Moolenaar * is used, assuming the 'p' modifier has _not_ been set.
36831337658SMarcel Moolenaar */
369d1a0d267SMarcel Moolenaar typedef struct xo_field_info_s {
370d1a0d267SMarcel Moolenaar xo_xff_flags_t xfi_flags; /* Flags for this field */
371d1a0d267SMarcel Moolenaar unsigned xfi_ftype; /* Field type, as character (e.g. 'V') */
372d1a0d267SMarcel Moolenaar const char *xfi_start; /* Start of field in the format string */
373d1a0d267SMarcel Moolenaar const char *xfi_content; /* Field's content */
374d1a0d267SMarcel Moolenaar const char *xfi_format; /* Field's Format */
375d1a0d267SMarcel Moolenaar const char *xfi_encoding; /* Field's encoding format */
376d1a0d267SMarcel Moolenaar const char *xfi_next; /* Next character in format string */
3778a6eceffSPhil Shafer ssize_t xfi_len; /* Length of field */
3788a6eceffSPhil Shafer ssize_t xfi_clen; /* Content length */
3798a6eceffSPhil Shafer ssize_t xfi_flen; /* Format length */
3808a6eceffSPhil Shafer ssize_t xfi_elen; /* Encoding length */
381d1a0d267SMarcel Moolenaar unsigned xfi_fnum; /* Field number (if used; 0 otherwise) */
382d1a0d267SMarcel Moolenaar unsigned xfi_renum; /* Reordered number (0 == no renumbering) */
383d1a0d267SMarcel Moolenaar } xo_field_info_t;
384d1a0d267SMarcel Moolenaar
385d1a0d267SMarcel Moolenaar /*
386d1a0d267SMarcel Moolenaar * We keep a 'default' handle to allow callers to avoid having to
387d1a0d267SMarcel Moolenaar * allocate one. Passing NULL to any of our functions will use
388d1a0d267SMarcel Moolenaar * this default handle. Most functions have a variant that doesn't
389d1a0d267SMarcel Moolenaar * require a handle at all, since most output is to stdout, which
390d1a0d267SMarcel Moolenaar * the default handle handles handily.
391d1a0d267SMarcel Moolenaar */
392d1a0d267SMarcel Moolenaar static THREAD_LOCAL(xo_handle_t) xo_default_handle;
393d1a0d267SMarcel Moolenaar static THREAD_LOCAL(int) xo_default_inited;
39431337658SMarcel Moolenaar static int xo_locale_inited;
395545ddfbeSMarcel Moolenaar static const char *xo_program;
39631337658SMarcel Moolenaar
39731337658SMarcel Moolenaar /*
39831337658SMarcel Moolenaar * To allow libxo to be used in diverse environment, we allow the
39931337658SMarcel Moolenaar * caller to give callbacks for memory allocation.
40031337658SMarcel Moolenaar */
401d1a0d267SMarcel Moolenaar xo_realloc_func_t xo_realloc = realloc;
402d1a0d267SMarcel Moolenaar xo_free_func_t xo_free = free;
40331337658SMarcel Moolenaar
40431337658SMarcel Moolenaar /* Forward declarations */
4058a6eceffSPhil Shafer static ssize_t
406406a584dSPhil Shafer xo_transition (xo_handle_t *xop, xo_xof_flags_t flags, const char *name,
407545ddfbeSMarcel Moolenaar xo_state_t new_state);
408545ddfbeSMarcel Moolenaar
409f2b7bf8aSPhil Shafer static int
410f2b7bf8aSPhil Shafer xo_set_options_simple (xo_handle_t *xop, const char *input);
411f2b7bf8aSPhil Shafer
412f2b7bf8aSPhil Shafer static int
413f2b7bf8aSPhil Shafer xo_color_find (const char *str);
414f2b7bf8aSPhil Shafer
41531337658SMarcel Moolenaar static void
41631337658SMarcel Moolenaar xo_buf_append_div (xo_handle_t *xop, const char *class, xo_xff_flags_t flags,
4178a6eceffSPhil Shafer const char *name, ssize_t nlen,
4188a6eceffSPhil Shafer const char *value, ssize_t vlen,
419264104f2SPhil Shafer const char *fmt, ssize_t flen,
4208a6eceffSPhil Shafer const char *encoding, ssize_t elen);
42131337658SMarcel Moolenaar
42231337658SMarcel Moolenaar static void
42331337658SMarcel Moolenaar xo_anchor_clear (xo_handle_t *xop);
42431337658SMarcel Moolenaar
42531337658SMarcel Moolenaar /*
426788ca347SMarcel Moolenaar * xo_style is used to retrieve the current style. When we're built
427788ca347SMarcel Moolenaar * for "text only" mode, we use this function to drive the removal
428788ca347SMarcel Moolenaar * of most of the code in libxo. We return a constant and the compiler
429788ca347SMarcel Moolenaar * happily removes the non-text code that is not longer executed. This
430788ca347SMarcel Moolenaar * trims our code nicely without needing to trampel perfectly readable
431788ca347SMarcel Moolenaar * code with ifdefs.
432788ca347SMarcel Moolenaar */
433d1a0d267SMarcel Moolenaar static inline xo_style_t
xo_style(xo_handle_t * xop UNUSED)434788ca347SMarcel Moolenaar xo_style (xo_handle_t *xop UNUSED)
435788ca347SMarcel Moolenaar {
436788ca347SMarcel Moolenaar #ifdef LIBXO_TEXT_ONLY
437788ca347SMarcel Moolenaar return XO_STYLE_TEXT;
438788ca347SMarcel Moolenaar #else /* LIBXO_TEXT_ONLY */
439788ca347SMarcel Moolenaar return xop->xo_style;
440788ca347SMarcel Moolenaar #endif /* LIBXO_TEXT_ONLY */
441788ca347SMarcel Moolenaar }
442788ca347SMarcel Moolenaar
443788ca347SMarcel Moolenaar /*
444406a584dSPhil Shafer * Allow the compiler to optimize out non-text-only code while
445406a584dSPhil Shafer * still compiling it.
446406a584dSPhil Shafer */
447406a584dSPhil Shafer static inline int
xo_text_only(void)448406a584dSPhil Shafer xo_text_only (void)
449406a584dSPhil Shafer {
450406a584dSPhil Shafer #ifdef LIBXO_TEXT_ONLY
451406a584dSPhil Shafer return TRUE;
452406a584dSPhil Shafer #else /* LIBXO_TEXT_ONLY */
453406a584dSPhil Shafer return FALSE;
454406a584dSPhil Shafer #endif /* LIBXO_TEXT_ONLY */
455406a584dSPhil Shafer }
456406a584dSPhil Shafer
457406a584dSPhil Shafer /*
45831337658SMarcel Moolenaar * Callback to write data to a FILE pointer
45931337658SMarcel Moolenaar */
4608a6eceffSPhil Shafer static xo_ssize_t
xo_write_to_file(void * opaque,const char * data)46131337658SMarcel Moolenaar xo_write_to_file (void *opaque, const char *data)
46231337658SMarcel Moolenaar {
46331337658SMarcel Moolenaar FILE *fp = (FILE *) opaque;
464545ddfbeSMarcel Moolenaar
46531337658SMarcel Moolenaar return fprintf(fp, "%s", data);
46631337658SMarcel Moolenaar }
46731337658SMarcel Moolenaar
46831337658SMarcel Moolenaar /*
46931337658SMarcel Moolenaar * Callback to close a file
47031337658SMarcel Moolenaar */
47131337658SMarcel Moolenaar static void
xo_close_file(void * opaque)47231337658SMarcel Moolenaar xo_close_file (void *opaque)
47331337658SMarcel Moolenaar {
47431337658SMarcel Moolenaar FILE *fp = (FILE *) opaque;
475545ddfbeSMarcel Moolenaar
47631337658SMarcel Moolenaar fclose(fp);
47731337658SMarcel Moolenaar }
47831337658SMarcel Moolenaar
47931337658SMarcel Moolenaar /*
480545ddfbeSMarcel Moolenaar * Callback to flush a FILE pointer
481545ddfbeSMarcel Moolenaar */
482545ddfbeSMarcel Moolenaar static int
xo_flush_file(void * opaque)483545ddfbeSMarcel Moolenaar xo_flush_file (void *opaque)
484545ddfbeSMarcel Moolenaar {
485545ddfbeSMarcel Moolenaar FILE *fp = (FILE *) opaque;
486545ddfbeSMarcel Moolenaar
487545ddfbeSMarcel Moolenaar return fflush(fp);
488545ddfbeSMarcel Moolenaar }
489545ddfbeSMarcel Moolenaar
490545ddfbeSMarcel Moolenaar /*
491d1a0d267SMarcel Moolenaar * Use a rotating stock of buffers to make a printable string
49231337658SMarcel Moolenaar */
493d1a0d267SMarcel Moolenaar #define XO_NUMBUFS 8
494d1a0d267SMarcel Moolenaar #define XO_SMBUFSZ 128
495d1a0d267SMarcel Moolenaar
496d1a0d267SMarcel Moolenaar static const char *
xo_printable(const char * str)497d1a0d267SMarcel Moolenaar xo_printable (const char *str)
49831337658SMarcel Moolenaar {
499d1a0d267SMarcel Moolenaar static THREAD_LOCAL(char) bufset[XO_NUMBUFS][XO_SMBUFSZ];
500d1a0d267SMarcel Moolenaar static THREAD_LOCAL(int) bufnum = 0;
501d1a0d267SMarcel Moolenaar
502d1a0d267SMarcel Moolenaar if (str == NULL)
503d1a0d267SMarcel Moolenaar return "";
504d1a0d267SMarcel Moolenaar
505d1a0d267SMarcel Moolenaar if (++bufnum == XO_NUMBUFS)
506d1a0d267SMarcel Moolenaar bufnum = 0;
507d1a0d267SMarcel Moolenaar
508d1a0d267SMarcel Moolenaar char *res = bufset[bufnum], *cp, *ep;
509d1a0d267SMarcel Moolenaar
510d1a0d267SMarcel Moolenaar for (cp = res, ep = res + XO_SMBUFSZ - 1; *str && cp < ep; cp++, str++) {
511d1a0d267SMarcel Moolenaar if (*str == '\n') {
512d1a0d267SMarcel Moolenaar *cp++ = '\\';
513d1a0d267SMarcel Moolenaar *cp = 'n';
514d1a0d267SMarcel Moolenaar } else if (*str == '\r') {
515d1a0d267SMarcel Moolenaar *cp++ = '\\';
516d1a0d267SMarcel Moolenaar *cp = 'r';
517d1a0d267SMarcel Moolenaar } else if (*str == '\"') {
518d1a0d267SMarcel Moolenaar *cp++ = '\\';
519d1a0d267SMarcel Moolenaar *cp = '"';
520d1a0d267SMarcel Moolenaar } else
521d1a0d267SMarcel Moolenaar *cp = *str;
52231337658SMarcel Moolenaar }
52331337658SMarcel Moolenaar
524d1a0d267SMarcel Moolenaar *cp = '\0';
525d1a0d267SMarcel Moolenaar return res;
52631337658SMarcel Moolenaar }
52731337658SMarcel Moolenaar
52831337658SMarcel Moolenaar static int
xo_depth_check(xo_handle_t * xop,int depth)52931337658SMarcel Moolenaar xo_depth_check (xo_handle_t *xop, int depth)
53031337658SMarcel Moolenaar {
53131337658SMarcel Moolenaar xo_stack_t *xsp;
53231337658SMarcel Moolenaar
53331337658SMarcel Moolenaar if (depth >= xop->xo_stack_size) {
534d1a0d267SMarcel Moolenaar depth += XO_DEPTH; /* Extra room */
535d1a0d267SMarcel Moolenaar
53631337658SMarcel Moolenaar xsp = xo_realloc(xop->xo_stack, sizeof(xop->xo_stack[0]) * depth);
53731337658SMarcel Moolenaar if (xsp == NULL) {
53831337658SMarcel Moolenaar xo_failure(xop, "xo_depth_check: out of memory (%d)", depth);
539d1a0d267SMarcel Moolenaar return -1;
54031337658SMarcel Moolenaar }
54131337658SMarcel Moolenaar
54231337658SMarcel Moolenaar int count = depth - xop->xo_stack_size;
54331337658SMarcel Moolenaar
54431337658SMarcel Moolenaar bzero(xsp + xop->xo_stack_size, count * sizeof(*xsp));
54531337658SMarcel Moolenaar xop->xo_stack_size = depth;
54631337658SMarcel Moolenaar xop->xo_stack = xsp;
54731337658SMarcel Moolenaar }
54831337658SMarcel Moolenaar
54931337658SMarcel Moolenaar return 0;
55031337658SMarcel Moolenaar }
55131337658SMarcel Moolenaar
55231337658SMarcel Moolenaar void
xo_no_setlocale(void)55331337658SMarcel Moolenaar xo_no_setlocale (void)
55431337658SMarcel Moolenaar {
55531337658SMarcel Moolenaar xo_locale_inited = 1; /* Skip initialization */
55631337658SMarcel Moolenaar }
55731337658SMarcel Moolenaar
55831337658SMarcel Moolenaar /*
559406a584dSPhil Shafer * For XML, the first character of a tag cannot be numeric, but people
560406a584dSPhil Shafer * will likely not notice. So we people-proof them by forcing a leading
561406a584dSPhil Shafer * underscore if they use invalid tags. Note that this doesn't cover
562406a584dSPhil Shafer * all broken tags, just this fairly specific case.
563406a584dSPhil Shafer */
564406a584dSPhil Shafer static const char *
xo_xml_leader_len(xo_handle_t * xop,const char * name,xo_ssize_t nlen)565406a584dSPhil Shafer xo_xml_leader_len (xo_handle_t *xop, const char *name, xo_ssize_t nlen)
566406a584dSPhil Shafer {
567c703b76eSPhil Shafer if (name == NULL || isalpha(name[0]) || name[0] == '_')
568406a584dSPhil Shafer return "";
569406a584dSPhil Shafer
570406a584dSPhil Shafer xo_failure(xop, "invalid XML tag name: '%.*s'", nlen, name);
571406a584dSPhil Shafer return "_";
572406a584dSPhil Shafer }
573406a584dSPhil Shafer
574406a584dSPhil Shafer static const char *
xo_xml_leader(xo_handle_t * xop,const char * name)575406a584dSPhil Shafer xo_xml_leader (xo_handle_t *xop, const char *name)
576406a584dSPhil Shafer {
577406a584dSPhil Shafer return xo_xml_leader_len(xop, name, strlen(name));
578406a584dSPhil Shafer }
579406a584dSPhil Shafer
580406a584dSPhil Shafer /*
581545ddfbeSMarcel Moolenaar * We need to decide if stdout is line buffered (_IOLBF). Lacking a
582545ddfbeSMarcel Moolenaar * standard way to decide this (e.g. getlinebuf()), we have configure
583788ca347SMarcel Moolenaar * look to find __flbf, which glibc supported. If not, we'll rely on
584788ca347SMarcel Moolenaar * isatty, with the assumption that terminals are the only thing
585545ddfbeSMarcel Moolenaar * that's line buffered. We _could_ test for "steam._flags & _IOLBF",
586545ddfbeSMarcel Moolenaar * which is all __flbf does, but that's even tackier. Like a
587545ddfbeSMarcel Moolenaar * bedazzled Elvis outfit on an ugly lap dog sort of tacky. Not
588545ddfbeSMarcel Moolenaar * something we're willing to do.
589545ddfbeSMarcel Moolenaar */
590545ddfbeSMarcel Moolenaar static int
xo_is_line_buffered(FILE * stream)591545ddfbeSMarcel Moolenaar xo_is_line_buffered (FILE *stream)
592545ddfbeSMarcel Moolenaar {
593545ddfbeSMarcel Moolenaar #if HAVE___FLBF
594545ddfbeSMarcel Moolenaar if (__flbf(stream))
595545ddfbeSMarcel Moolenaar return 1;
596545ddfbeSMarcel Moolenaar #else /* HAVE___FLBF */
597545ddfbeSMarcel Moolenaar if (isatty(fileno(stream)))
598545ddfbeSMarcel Moolenaar return 1;
599545ddfbeSMarcel Moolenaar #endif /* HAVE___FLBF */
600545ddfbeSMarcel Moolenaar return 0;
601545ddfbeSMarcel Moolenaar }
602545ddfbeSMarcel Moolenaar
603545ddfbeSMarcel Moolenaar /*
60431337658SMarcel Moolenaar * Initialize an xo_handle_t, using both static defaults and
60531337658SMarcel Moolenaar * the global settings from the LIBXO_OPTIONS environment
60631337658SMarcel Moolenaar * variable.
60731337658SMarcel Moolenaar */
60831337658SMarcel Moolenaar static void
xo_init_handle(xo_handle_t * xop)60931337658SMarcel Moolenaar xo_init_handle (xo_handle_t *xop)
61031337658SMarcel Moolenaar {
61131337658SMarcel Moolenaar xop->xo_opaque = stdout;
61231337658SMarcel Moolenaar xop->xo_write = xo_write_to_file;
613545ddfbeSMarcel Moolenaar xop->xo_flush = xo_flush_file;
614545ddfbeSMarcel Moolenaar
615545ddfbeSMarcel Moolenaar if (xo_is_line_buffered(stdout))
616d1a0d267SMarcel Moolenaar XOF_SET(xop, XOF_FLUSH_LINE);
61731337658SMarcel Moolenaar
61831337658SMarcel Moolenaar /*
61931337658SMarcel Moolenaar * We need to initialize the locale, which isn't really pretty.
62031337658SMarcel Moolenaar * Libraries should depend on their caller to set up the
62131337658SMarcel Moolenaar * environment. But we really can't count on the caller to do
62231337658SMarcel Moolenaar * this, because well, they won't. Trust me.
62331337658SMarcel Moolenaar */
62431337658SMarcel Moolenaar if (!xo_locale_inited) {
62531337658SMarcel Moolenaar xo_locale_inited = 1; /* Only do this once */
62631337658SMarcel Moolenaar
62776afb20cSPhil Shafer #ifdef __FreeBSD__ /* Who does The Right Thing */
62876afb20cSPhil Shafer const char *cp = "";
62976afb20cSPhil Shafer #else /* __FreeBSD__ */
63076afb20cSPhil Shafer const char *cp = getenv("LC_ALL");
63176afb20cSPhil Shafer if (cp == NULL)
63276afb20cSPhil Shafer cp = getenv("LC_CTYPE");
63331337658SMarcel Moolenaar if (cp == NULL)
63431337658SMarcel Moolenaar cp = getenv("LANG");
63531337658SMarcel Moolenaar if (cp == NULL)
636d1a0d267SMarcel Moolenaar cp = "C"; /* Default for C programs */
63776afb20cSPhil Shafer #endif /* __FreeBSD__ */
63876afb20cSPhil Shafer
639c600d307SMarcel Moolenaar (void) setlocale(LC_CTYPE, cp);
64031337658SMarcel Moolenaar }
64131337658SMarcel Moolenaar
64231337658SMarcel Moolenaar /*
64331337658SMarcel Moolenaar * Initialize only the xo_buffers we know we'll need; the others
64431337658SMarcel Moolenaar * can be allocated as needed.
64531337658SMarcel Moolenaar */
64631337658SMarcel Moolenaar xo_buf_init(&xop->xo_data);
64731337658SMarcel Moolenaar xo_buf_init(&xop->xo_fmt);
64831337658SMarcel Moolenaar
649d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_INIT_IN_PROGRESS))
650d1a0d267SMarcel Moolenaar return;
651d1a0d267SMarcel Moolenaar XOIF_SET(xop, XOIF_INIT_IN_PROGRESS);
652d1a0d267SMarcel Moolenaar
65331337658SMarcel Moolenaar xop->xo_indent_by = XO_INDENT_BY;
65431337658SMarcel Moolenaar xo_depth_check(xop, XO_DEPTH);
65531337658SMarcel Moolenaar
656d1a0d267SMarcel Moolenaar XOIF_CLEAR(xop, XOIF_INIT_IN_PROGRESS);
65731337658SMarcel Moolenaar }
65831337658SMarcel Moolenaar
65931337658SMarcel Moolenaar /*
66031337658SMarcel Moolenaar * Initialize the default handle.
66131337658SMarcel Moolenaar */
66231337658SMarcel Moolenaar static void
xo_default_init(void)66331337658SMarcel Moolenaar xo_default_init (void)
66431337658SMarcel Moolenaar {
66531337658SMarcel Moolenaar xo_handle_t *xop = &xo_default_handle;
66631337658SMarcel Moolenaar
66731337658SMarcel Moolenaar xo_init_handle(xop);
66831337658SMarcel Moolenaar
669f2b7bf8aSPhil Shafer #if !defined(NO_LIBXO_OPTIONS)
670f2b7bf8aSPhil Shafer if (!XOF_ISSET(xop, XOF_NO_ENV)) {
671f2b7bf8aSPhil Shafer char *env = getenv("LIBXO_OPTIONS");
6722f784130SPhil Shafer
673f2b7bf8aSPhil Shafer if (env)
674f2b7bf8aSPhil Shafer xo_set_options_simple(xop, env);
675f2b7bf8aSPhil Shafer
676f2b7bf8aSPhil Shafer }
677f2b7bf8aSPhil Shafer #endif /* NO_LIBXO_OPTIONS */
678f2b7bf8aSPhil Shafer
67931337658SMarcel Moolenaar xo_default_inited = 1;
68031337658SMarcel Moolenaar }
68131337658SMarcel Moolenaar
68231337658SMarcel Moolenaar /*
68331337658SMarcel Moolenaar * Cheap convenience function to return either the argument, or
68431337658SMarcel Moolenaar * the internal handle, after it has been initialized. The usage
68531337658SMarcel Moolenaar * is:
68631337658SMarcel Moolenaar * xop = xo_default(xop);
68731337658SMarcel Moolenaar */
68831337658SMarcel Moolenaar static xo_handle_t *
xo_default(xo_handle_t * xop)68931337658SMarcel Moolenaar xo_default (xo_handle_t *xop)
69031337658SMarcel Moolenaar {
69131337658SMarcel Moolenaar if (xop == NULL) {
69231337658SMarcel Moolenaar if (xo_default_inited == 0)
69331337658SMarcel Moolenaar xo_default_init();
69431337658SMarcel Moolenaar xop = &xo_default_handle;
69531337658SMarcel Moolenaar }
69631337658SMarcel Moolenaar
69731337658SMarcel Moolenaar return xop;
69831337658SMarcel Moolenaar }
69931337658SMarcel Moolenaar
70031337658SMarcel Moolenaar /*
70131337658SMarcel Moolenaar * Return the number of spaces we should be indenting. If
702788ca347SMarcel Moolenaar * we are pretty-printing, this is indent * indent_by.
70331337658SMarcel Moolenaar */
70431337658SMarcel Moolenaar static int
xo_indent(xo_handle_t * xop)70531337658SMarcel Moolenaar xo_indent (xo_handle_t *xop)
70631337658SMarcel Moolenaar {
70731337658SMarcel Moolenaar int rc = 0;
70831337658SMarcel Moolenaar
70931337658SMarcel Moolenaar xop = xo_default(xop);
71031337658SMarcel Moolenaar
711d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_PRETTY)) {
71231337658SMarcel Moolenaar rc = xop->xo_indent * xop->xo_indent_by;
713d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_TOP_EMITTED))
71431337658SMarcel Moolenaar rc += xop->xo_indent_by;
71531337658SMarcel Moolenaar }
71631337658SMarcel Moolenaar
717545ddfbeSMarcel Moolenaar return (rc > 0) ? rc : 0;
71831337658SMarcel Moolenaar }
71931337658SMarcel Moolenaar
72031337658SMarcel Moolenaar static void
xo_buf_indent(xo_handle_t * xop,int indent)72131337658SMarcel Moolenaar xo_buf_indent (xo_handle_t *xop, int indent)
72231337658SMarcel Moolenaar {
72331337658SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_data;
72431337658SMarcel Moolenaar
72531337658SMarcel Moolenaar if (indent <= 0)
72631337658SMarcel Moolenaar indent = xo_indent(xop);
72731337658SMarcel Moolenaar
72831337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, indent))
72931337658SMarcel Moolenaar return;
73031337658SMarcel Moolenaar
73131337658SMarcel Moolenaar memset(xbp->xb_curp, ' ', indent);
73231337658SMarcel Moolenaar xbp->xb_curp += indent;
73331337658SMarcel Moolenaar }
73431337658SMarcel Moolenaar
73531337658SMarcel Moolenaar static char xo_xml_amp[] = "&";
73631337658SMarcel Moolenaar static char xo_xml_lt[] = "<";
73731337658SMarcel Moolenaar static char xo_xml_gt[] = ">";
73831337658SMarcel Moolenaar static char xo_xml_quot[] = """;
73931337658SMarcel Moolenaar
7408a6eceffSPhil Shafer static ssize_t
xo_escape_xml(xo_buffer_t * xbp,ssize_t len,xo_xff_flags_t flags)7418a6eceffSPhil Shafer xo_escape_xml (xo_buffer_t *xbp, ssize_t len, xo_xff_flags_t flags)
74231337658SMarcel Moolenaar {
7438a6eceffSPhil Shafer ssize_t slen;
7448a6eceffSPhil Shafer ssize_t delta = 0;
74531337658SMarcel Moolenaar char *cp, *ep, *ip;
74631337658SMarcel Moolenaar const char *sp;
7478a6eceffSPhil Shafer int attr = XOF_BIT_ISSET(flags, XFF_ATTR);
74831337658SMarcel Moolenaar
74931337658SMarcel Moolenaar for (cp = xbp->xb_curp, ep = cp + len; cp < ep; cp++) {
75031337658SMarcel Moolenaar /* We're subtracting 2: 1 for the NUL, 1 for the char we replace */
75131337658SMarcel Moolenaar if (*cp == '<')
75231337658SMarcel Moolenaar delta += sizeof(xo_xml_lt) - 2;
75331337658SMarcel Moolenaar else if (*cp == '>')
75431337658SMarcel Moolenaar delta += sizeof(xo_xml_gt) - 2;
75531337658SMarcel Moolenaar else if (*cp == '&')
75631337658SMarcel Moolenaar delta += sizeof(xo_xml_amp) - 2;
75731337658SMarcel Moolenaar else if (attr && *cp == '"')
75831337658SMarcel Moolenaar delta += sizeof(xo_xml_quot) - 2;
75931337658SMarcel Moolenaar }
76031337658SMarcel Moolenaar
76131337658SMarcel Moolenaar if (delta == 0) /* Nothing to escape; bail */
76231337658SMarcel Moolenaar return len;
76331337658SMarcel Moolenaar
76431337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, delta)) /* No room; bail, but don't append */
76531337658SMarcel Moolenaar return 0;
76631337658SMarcel Moolenaar
76731337658SMarcel Moolenaar ep = xbp->xb_curp;
76831337658SMarcel Moolenaar cp = ep + len;
76931337658SMarcel Moolenaar ip = cp + delta;
77031337658SMarcel Moolenaar do {
77131337658SMarcel Moolenaar cp -= 1;
77231337658SMarcel Moolenaar ip -= 1;
77331337658SMarcel Moolenaar
77431337658SMarcel Moolenaar if (*cp == '<')
77531337658SMarcel Moolenaar sp = xo_xml_lt;
77631337658SMarcel Moolenaar else if (*cp == '>')
77731337658SMarcel Moolenaar sp = xo_xml_gt;
77831337658SMarcel Moolenaar else if (*cp == '&')
77931337658SMarcel Moolenaar sp = xo_xml_amp;
78031337658SMarcel Moolenaar else if (attr && *cp == '"')
78131337658SMarcel Moolenaar sp = xo_xml_quot;
78231337658SMarcel Moolenaar else {
78331337658SMarcel Moolenaar *ip = *cp;
78431337658SMarcel Moolenaar continue;
78531337658SMarcel Moolenaar }
78631337658SMarcel Moolenaar
78731337658SMarcel Moolenaar slen = strlen(sp);
78831337658SMarcel Moolenaar ip -= slen - 1;
78931337658SMarcel Moolenaar memcpy(ip, sp, slen);
79031337658SMarcel Moolenaar
79131337658SMarcel Moolenaar } while (cp > ep && cp != ip);
79231337658SMarcel Moolenaar
79331337658SMarcel Moolenaar return len + delta;
79431337658SMarcel Moolenaar }
79531337658SMarcel Moolenaar
7968a6eceffSPhil Shafer static ssize_t
xo_escape_json(xo_buffer_t * xbp,ssize_t len,xo_xff_flags_t flags UNUSED)7978a6eceffSPhil Shafer xo_escape_json (xo_buffer_t *xbp, ssize_t len, xo_xff_flags_t flags UNUSED)
79831337658SMarcel Moolenaar {
7998a6eceffSPhil Shafer ssize_t delta = 0;
80031337658SMarcel Moolenaar char *cp, *ep, *ip;
80131337658SMarcel Moolenaar
80231337658SMarcel Moolenaar for (cp = xbp->xb_curp, ep = cp + len; cp < ep; cp++) {
803545ddfbeSMarcel Moolenaar if (*cp == '\\' || *cp == '"')
80431337658SMarcel Moolenaar delta += 1;
805545ddfbeSMarcel Moolenaar else if (*cp == '\n' || *cp == '\r')
80631337658SMarcel Moolenaar delta += 1;
80731337658SMarcel Moolenaar }
80831337658SMarcel Moolenaar
80931337658SMarcel Moolenaar if (delta == 0) /* Nothing to escape; bail */
81031337658SMarcel Moolenaar return len;
81131337658SMarcel Moolenaar
81231337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, delta)) /* No room; bail, but don't append */
81331337658SMarcel Moolenaar return 0;
81431337658SMarcel Moolenaar
81531337658SMarcel Moolenaar ep = xbp->xb_curp;
81631337658SMarcel Moolenaar cp = ep + len;
81731337658SMarcel Moolenaar ip = cp + delta;
81831337658SMarcel Moolenaar do {
81931337658SMarcel Moolenaar cp -= 1;
82031337658SMarcel Moolenaar ip -= 1;
82131337658SMarcel Moolenaar
822545ddfbeSMarcel Moolenaar if (*cp == '\\' || *cp == '"') {
82331337658SMarcel Moolenaar *ip-- = *cp;
82431337658SMarcel Moolenaar *ip = '\\';
825545ddfbeSMarcel Moolenaar } else if (*cp == '\n') {
826545ddfbeSMarcel Moolenaar *ip-- = 'n';
827545ddfbeSMarcel Moolenaar *ip = '\\';
828545ddfbeSMarcel Moolenaar } else if (*cp == '\r') {
829545ddfbeSMarcel Moolenaar *ip-- = 'r';
830545ddfbeSMarcel Moolenaar *ip = '\\';
831545ddfbeSMarcel Moolenaar } else {
832545ddfbeSMarcel Moolenaar *ip = *cp;
833545ddfbeSMarcel Moolenaar }
83431337658SMarcel Moolenaar
83531337658SMarcel Moolenaar } while (cp > ep && cp != ip);
83631337658SMarcel Moolenaar
83731337658SMarcel Moolenaar return len + delta;
83831337658SMarcel Moolenaar }
83931337658SMarcel Moolenaar
84031337658SMarcel Moolenaar /*
841d1a0d267SMarcel Moolenaar * PARAM-VALUE = UTF-8-STRING ; characters '"', '\' and
842d1a0d267SMarcel Moolenaar * ; ']' MUST be escaped.
84331337658SMarcel Moolenaar */
8448a6eceffSPhil Shafer static ssize_t
xo_escape_sdparams(xo_buffer_t * xbp,ssize_t len,xo_xff_flags_t flags UNUSED)8458a6eceffSPhil Shafer xo_escape_sdparams (xo_buffer_t *xbp, ssize_t len, xo_xff_flags_t flags UNUSED)
84631337658SMarcel Moolenaar {
8478a6eceffSPhil Shafer ssize_t delta = 0;
848d1a0d267SMarcel Moolenaar char *cp, *ep, *ip;
84931337658SMarcel Moolenaar
850d1a0d267SMarcel Moolenaar for (cp = xbp->xb_curp, ep = cp + len; cp < ep; cp++) {
851d1a0d267SMarcel Moolenaar if (*cp == '\\' || *cp == '"' || *cp == ']')
852d1a0d267SMarcel Moolenaar delta += 1;
85331337658SMarcel Moolenaar }
85431337658SMarcel Moolenaar
855d1a0d267SMarcel Moolenaar if (delta == 0) /* Nothing to escape; bail */
856d1a0d267SMarcel Moolenaar return len;
857788ca347SMarcel Moolenaar
858d1a0d267SMarcel Moolenaar if (!xo_buf_has_room(xbp, delta)) /* No room; bail, but don't append */
859d1a0d267SMarcel Moolenaar return 0;
860788ca347SMarcel Moolenaar
861d1a0d267SMarcel Moolenaar ep = xbp->xb_curp;
862d1a0d267SMarcel Moolenaar cp = ep + len;
863d1a0d267SMarcel Moolenaar ip = cp + delta;
864d1a0d267SMarcel Moolenaar do {
865d1a0d267SMarcel Moolenaar cp -= 1;
866d1a0d267SMarcel Moolenaar ip -= 1;
867d1a0d267SMarcel Moolenaar
868d1a0d267SMarcel Moolenaar if (*cp == '\\' || *cp == '"' || *cp == ']') {
869d1a0d267SMarcel Moolenaar *ip-- = *cp;
870d1a0d267SMarcel Moolenaar *ip = '\\';
871d1a0d267SMarcel Moolenaar } else {
872d1a0d267SMarcel Moolenaar *ip = *cp;
873d1a0d267SMarcel Moolenaar }
874d1a0d267SMarcel Moolenaar
875d1a0d267SMarcel Moolenaar } while (cp > ep && cp != ip);
876d1a0d267SMarcel Moolenaar
877d1a0d267SMarcel Moolenaar return len + delta;
878788ca347SMarcel Moolenaar }
879788ca347SMarcel Moolenaar
88031337658SMarcel Moolenaar static void
xo_buf_escape(xo_handle_t * xop,xo_buffer_t * xbp,const char * str,ssize_t len,xo_xff_flags_t flags)88131337658SMarcel Moolenaar xo_buf_escape (xo_handle_t *xop, xo_buffer_t *xbp,
8828a6eceffSPhil Shafer const char *str, ssize_t len, xo_xff_flags_t flags)
88331337658SMarcel Moolenaar {
88431337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, len))
88531337658SMarcel Moolenaar return;
88631337658SMarcel Moolenaar
88731337658SMarcel Moolenaar memcpy(xbp->xb_curp, str, len);
88831337658SMarcel Moolenaar
889788ca347SMarcel Moolenaar switch (xo_style(xop)) {
89031337658SMarcel Moolenaar case XO_STYLE_XML:
89131337658SMarcel Moolenaar case XO_STYLE_HTML:
892d1a0d267SMarcel Moolenaar len = xo_escape_xml(xbp, len, flags);
89331337658SMarcel Moolenaar break;
89431337658SMarcel Moolenaar
89531337658SMarcel Moolenaar case XO_STYLE_JSON:
896d1a0d267SMarcel Moolenaar len = xo_escape_json(xbp, len, flags);
897d1a0d267SMarcel Moolenaar break;
898d1a0d267SMarcel Moolenaar
899d1a0d267SMarcel Moolenaar case XO_STYLE_SDPARAMS:
900d1a0d267SMarcel Moolenaar len = xo_escape_sdparams(xbp, len, flags);
90131337658SMarcel Moolenaar break;
90231337658SMarcel Moolenaar }
90331337658SMarcel Moolenaar
90431337658SMarcel Moolenaar xbp->xb_curp += len;
90531337658SMarcel Moolenaar }
90631337658SMarcel Moolenaar
90731337658SMarcel Moolenaar /*
90831337658SMarcel Moolenaar * Write the current contents of the data buffer using the handle's
90931337658SMarcel Moolenaar * xo_write function.
91031337658SMarcel Moolenaar */
9118a6eceffSPhil Shafer static ssize_t
xo_write(xo_handle_t * xop)91231337658SMarcel Moolenaar xo_write (xo_handle_t *xop)
91331337658SMarcel Moolenaar {
9148a6eceffSPhil Shafer ssize_t rc = 0;
91531337658SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_data;
91631337658SMarcel Moolenaar
91731337658SMarcel Moolenaar if (xbp->xb_curp != xbp->xb_bufp) {
91831337658SMarcel Moolenaar xo_buf_append(xbp, "", 1); /* Append ending NUL */
91931337658SMarcel Moolenaar xo_anchor_clear(xop);
920d1a0d267SMarcel Moolenaar if (xop->xo_write)
921545ddfbeSMarcel Moolenaar rc = xop->xo_write(xop->xo_opaque, xbp->xb_bufp);
92231337658SMarcel Moolenaar xbp->xb_curp = xbp->xb_bufp;
92331337658SMarcel Moolenaar }
92431337658SMarcel Moolenaar
92531337658SMarcel Moolenaar /* Turn off the flags that don't survive across writes */
926d1a0d267SMarcel Moolenaar XOIF_CLEAR(xop, XOIF_UNITS_PENDING);
927545ddfbeSMarcel Moolenaar
928545ddfbeSMarcel Moolenaar return rc;
92931337658SMarcel Moolenaar }
93031337658SMarcel Moolenaar
93131337658SMarcel Moolenaar /*
93231337658SMarcel Moolenaar * Format arguments into our buffer. If a custom formatter has been set,
93331337658SMarcel Moolenaar * we use that to do the work; otherwise we vsnprintf().
93431337658SMarcel Moolenaar */
9358a6eceffSPhil Shafer static ssize_t
xo_vsnprintf(xo_handle_t * xop,xo_buffer_t * xbp,const char * fmt,va_list vap)93631337658SMarcel Moolenaar xo_vsnprintf (xo_handle_t *xop, xo_buffer_t *xbp, const char *fmt, va_list vap)
93731337658SMarcel Moolenaar {
93831337658SMarcel Moolenaar va_list va_local;
9398a6eceffSPhil Shafer ssize_t rc;
9408a6eceffSPhil Shafer ssize_t left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp);
94131337658SMarcel Moolenaar
94231337658SMarcel Moolenaar va_copy(va_local, vap);
94331337658SMarcel Moolenaar
94431337658SMarcel Moolenaar if (xop->xo_formatter)
94531337658SMarcel Moolenaar rc = xop->xo_formatter(xop, xbp->xb_curp, left, fmt, va_local);
94631337658SMarcel Moolenaar else
94731337658SMarcel Moolenaar rc = vsnprintf(xbp->xb_curp, left, fmt, va_local);
94831337658SMarcel Moolenaar
949788ca347SMarcel Moolenaar if (rc >= left) {
950c600d307SMarcel Moolenaar if (!xo_buf_has_room(xbp, rc)) {
951c600d307SMarcel Moolenaar va_end(va_local);
95231337658SMarcel Moolenaar return -1;
953c600d307SMarcel Moolenaar }
95431337658SMarcel Moolenaar
95531337658SMarcel Moolenaar /*
95631337658SMarcel Moolenaar * After we call vsnprintf(), the stage of vap is not defined.
95731337658SMarcel Moolenaar * We need to copy it before we pass. Then we have to do our
95831337658SMarcel Moolenaar * own logic below to move it along. This is because the
959788ca347SMarcel Moolenaar * implementation can have va_list be a pointer (bsd) or a
96031337658SMarcel Moolenaar * structure (macosx) or anything in between.
96131337658SMarcel Moolenaar */
96231337658SMarcel Moolenaar
96331337658SMarcel Moolenaar va_end(va_local); /* Reset vap to the start */
96431337658SMarcel Moolenaar va_copy(va_local, vap);
96531337658SMarcel Moolenaar
96631337658SMarcel Moolenaar left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp);
96731337658SMarcel Moolenaar if (xop->xo_formatter)
968788ca347SMarcel Moolenaar rc = xop->xo_formatter(xop, xbp->xb_curp, left, fmt, va_local);
96931337658SMarcel Moolenaar else
97031337658SMarcel Moolenaar rc = vsnprintf(xbp->xb_curp, left, fmt, va_local);
97131337658SMarcel Moolenaar }
97231337658SMarcel Moolenaar va_end(va_local);
97331337658SMarcel Moolenaar
97431337658SMarcel Moolenaar return rc;
97531337658SMarcel Moolenaar }
97631337658SMarcel Moolenaar
97731337658SMarcel Moolenaar /*
978ee5cf116SPhil Shafer * Print some data through the handle.
97931337658SMarcel Moolenaar */
9808a6eceffSPhil Shafer static ssize_t
xo_printf_v(xo_handle_t * xop,const char * fmt,va_list vap)98131337658SMarcel Moolenaar xo_printf_v (xo_handle_t *xop, const char *fmt, va_list vap)
98231337658SMarcel Moolenaar {
98331337658SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_data;
9848a6eceffSPhil Shafer ssize_t left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp);
9858a6eceffSPhil Shafer ssize_t rc;
98631337658SMarcel Moolenaar va_list va_local;
98731337658SMarcel Moolenaar
98831337658SMarcel Moolenaar va_copy(va_local, vap);
98931337658SMarcel Moolenaar
99031337658SMarcel Moolenaar rc = vsnprintf(xbp->xb_curp, left, fmt, va_local);
99131337658SMarcel Moolenaar
992d1a0d267SMarcel Moolenaar if (rc >= left) {
993c600d307SMarcel Moolenaar if (!xo_buf_has_room(xbp, rc)) {
994c600d307SMarcel Moolenaar va_end(va_local);
99531337658SMarcel Moolenaar return -1;
996c600d307SMarcel Moolenaar }
99731337658SMarcel Moolenaar
99831337658SMarcel Moolenaar va_end(va_local); /* Reset vap to the start */
99931337658SMarcel Moolenaar va_copy(va_local, vap);
100031337658SMarcel Moolenaar
100131337658SMarcel Moolenaar left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp);
100231337658SMarcel Moolenaar rc = vsnprintf(xbp->xb_curp, left, fmt, va_local);
100331337658SMarcel Moolenaar }
100431337658SMarcel Moolenaar
100531337658SMarcel Moolenaar va_end(va_local);
100631337658SMarcel Moolenaar
100731337658SMarcel Moolenaar if (rc > 0)
100831337658SMarcel Moolenaar xbp->xb_curp += rc;
100931337658SMarcel Moolenaar
101031337658SMarcel Moolenaar return rc;
101131337658SMarcel Moolenaar }
101231337658SMarcel Moolenaar
10138a6eceffSPhil Shafer static ssize_t
xo_printf(xo_handle_t * xop,const char * fmt,...)101431337658SMarcel Moolenaar xo_printf (xo_handle_t *xop, const char *fmt, ...)
101531337658SMarcel Moolenaar {
10168a6eceffSPhil Shafer ssize_t rc;
101731337658SMarcel Moolenaar va_list vap;
101831337658SMarcel Moolenaar
101931337658SMarcel Moolenaar va_start(vap, fmt);
102031337658SMarcel Moolenaar
102131337658SMarcel Moolenaar rc = xo_printf_v(xop, fmt, vap);
102231337658SMarcel Moolenaar
102331337658SMarcel Moolenaar va_end(vap);
102431337658SMarcel Moolenaar return rc;
102531337658SMarcel Moolenaar }
102631337658SMarcel Moolenaar
102731337658SMarcel Moolenaar /*
102831337658SMarcel Moolenaar * These next few function are make The Essential UTF-8 Ginsu Knife.
102931337658SMarcel Moolenaar * Identify an input and output character, and convert it.
103031337658SMarcel Moolenaar */
1031f2b7bf8aSPhil Shafer static uint8_t xo_utf8_data_bits[5] = { 0, 0x7f, 0x1f, 0x0f, 0x07 };
1032f2b7bf8aSPhil Shafer static uint8_t xo_utf8_len_bits[5] = { 0, 0x00, 0xc0, 0xe0, 0xf0 };
103331337658SMarcel Moolenaar
1034f2b7bf8aSPhil Shafer /*
1035f2b7bf8aSPhil Shafer * If the byte has a high-bit set, it's UTF-8, not ASCII.
1036f2b7bf8aSPhil Shafer */
103731337658SMarcel Moolenaar static int
xo_is_utf8(char ch)103831337658SMarcel Moolenaar xo_is_utf8 (char ch)
103931337658SMarcel Moolenaar {
104031337658SMarcel Moolenaar return (ch & 0x80);
104131337658SMarcel Moolenaar }
104231337658SMarcel Moolenaar
1043f2b7bf8aSPhil Shafer /*
1044f2b7bf8aSPhil Shafer * Look at the high bits of the first byte to determine the length
1045f2b7bf8aSPhil Shafer * of the UTF-8 character.
1046f2b7bf8aSPhil Shafer */
10478a6eceffSPhil Shafer static inline ssize_t
xo_utf8_to_wc_len(const char * buf)104831337658SMarcel Moolenaar xo_utf8_to_wc_len (const char *buf)
104931337658SMarcel Moolenaar {
1050f2b7bf8aSPhil Shafer uint8_t bval = (uint8_t) *buf;
10518a6eceffSPhil Shafer ssize_t len;
105231337658SMarcel Moolenaar
1053f2b7bf8aSPhil Shafer if ((bval & 0x80) == 0x0)
105431337658SMarcel Moolenaar len = 1;
1055f2b7bf8aSPhil Shafer else if ((bval & 0xe0) == 0xc0)
105631337658SMarcel Moolenaar len = 2;
1057f2b7bf8aSPhil Shafer else if ((bval & 0xf0) == 0xe0)
105831337658SMarcel Moolenaar len = 3;
1059f2b7bf8aSPhil Shafer else if ((bval & 0xf8) == 0xf0)
106031337658SMarcel Moolenaar len = 4;
106131337658SMarcel Moolenaar else
106231337658SMarcel Moolenaar len = -1;
106331337658SMarcel Moolenaar
106431337658SMarcel Moolenaar return len;
106531337658SMarcel Moolenaar }
106631337658SMarcel Moolenaar
10678a6eceffSPhil Shafer static ssize_t
xo_buf_utf8_len(xo_handle_t * xop,const char * buf,ssize_t bufsiz)10688a6eceffSPhil Shafer xo_buf_utf8_len (xo_handle_t *xop, const char *buf, ssize_t bufsiz)
106931337658SMarcel Moolenaar {
107031337658SMarcel Moolenaar unsigned b = (unsigned char) *buf;
10718a6eceffSPhil Shafer ssize_t len, i;
107231337658SMarcel Moolenaar
107331337658SMarcel Moolenaar len = xo_utf8_to_wc_len(buf);
1074f2b7bf8aSPhil Shafer if (len < 0) {
107531337658SMarcel Moolenaar xo_failure(xop, "invalid UTF-8 data: %02hhx", b);
107631337658SMarcel Moolenaar return -1;
107731337658SMarcel Moolenaar }
107831337658SMarcel Moolenaar
107931337658SMarcel Moolenaar if (len > bufsiz) {
108031337658SMarcel Moolenaar xo_failure(xop, "invalid UTF-8 data (short): %02hhx (%d/%d)",
108131337658SMarcel Moolenaar b, len, bufsiz);
108231337658SMarcel Moolenaar return -1;
108331337658SMarcel Moolenaar }
108431337658SMarcel Moolenaar
108531337658SMarcel Moolenaar for (i = 2; i < len; i++) {
108631337658SMarcel Moolenaar b = (unsigned char ) buf[i];
108731337658SMarcel Moolenaar if ((b & 0xc0) != 0x80) {
108831337658SMarcel Moolenaar xo_failure(xop, "invalid UTF-8 data (byte %d): %x", i, b);
108931337658SMarcel Moolenaar return -1;
109031337658SMarcel Moolenaar }
109131337658SMarcel Moolenaar }
109231337658SMarcel Moolenaar
109331337658SMarcel Moolenaar return len;
109431337658SMarcel Moolenaar }
109531337658SMarcel Moolenaar
109631337658SMarcel Moolenaar /*
109731337658SMarcel Moolenaar * Build a wide character from the input buffer; the number of
109831337658SMarcel Moolenaar * bits we pull off the first character is dependent on the length,
109931337658SMarcel Moolenaar * but we put 6 bits off all other bytes.
110031337658SMarcel Moolenaar */
110142ff34c3SPhil Shafer static inline wchar_t
xo_utf8_char(const char * buf,ssize_t len)11028a6eceffSPhil Shafer xo_utf8_char (const char *buf, ssize_t len)
110331337658SMarcel Moolenaar {
110442ff34c3SPhil Shafer /* Most common case: singleton byte */
110542ff34c3SPhil Shafer if (len == 1)
110642ff34c3SPhil Shafer return (unsigned char) buf[0];
110742ff34c3SPhil Shafer
11088a6eceffSPhil Shafer ssize_t i;
110931337658SMarcel Moolenaar wchar_t wc;
111031337658SMarcel Moolenaar const unsigned char *cp = (const unsigned char *) buf;
111131337658SMarcel Moolenaar
1112f2b7bf8aSPhil Shafer wc = *cp & xo_utf8_data_bits[len];
111331337658SMarcel Moolenaar for (i = 1; i < len; i++) {
1114f2b7bf8aSPhil Shafer wc <<= 6; /* Low six bits have data */
111531337658SMarcel Moolenaar wc |= cp[i] & 0x3f;
111631337658SMarcel Moolenaar if ((cp[i] & 0xc0) != 0x80)
111731337658SMarcel Moolenaar return (wchar_t) -1;
111831337658SMarcel Moolenaar }
111931337658SMarcel Moolenaar
112031337658SMarcel Moolenaar return wc;
112131337658SMarcel Moolenaar }
112231337658SMarcel Moolenaar
112331337658SMarcel Moolenaar /*
112431337658SMarcel Moolenaar * Determine the number of bytes needed to encode a wide character.
112531337658SMarcel Moolenaar */
11268a6eceffSPhil Shafer static ssize_t
xo_utf8_emit_len(wchar_t wc)112731337658SMarcel Moolenaar xo_utf8_emit_len (wchar_t wc)
112831337658SMarcel Moolenaar {
11298a6eceffSPhil Shafer ssize_t len;
113031337658SMarcel Moolenaar
113131337658SMarcel Moolenaar if ((wc & ((1 << 7) - 1)) == wc) /* Simple case */
113231337658SMarcel Moolenaar len = 1;
113331337658SMarcel Moolenaar else if ((wc & ((1 << 11) - 1)) == wc)
113431337658SMarcel Moolenaar len = 2;
113531337658SMarcel Moolenaar else if ((wc & ((1 << 16) - 1)) == wc)
113631337658SMarcel Moolenaar len = 3;
113731337658SMarcel Moolenaar else if ((wc & ((1 << 21) - 1)) == wc)
113831337658SMarcel Moolenaar len = 4;
113931337658SMarcel Moolenaar else
1140f2b7bf8aSPhil Shafer len = -1; /* Invalid */
114131337658SMarcel Moolenaar
114231337658SMarcel Moolenaar return len;
114331337658SMarcel Moolenaar }
114431337658SMarcel Moolenaar
1145f2b7bf8aSPhil Shafer /*
11462f784130SPhil Shafer * Emit one wide character into the given buffer
1147f2b7bf8aSPhil Shafer */
114831337658SMarcel Moolenaar static void
xo_utf8_emit_char(char * buf,ssize_t len,wchar_t wc)11498a6eceffSPhil Shafer xo_utf8_emit_char (char *buf, ssize_t len, wchar_t wc)
115031337658SMarcel Moolenaar {
11518a6eceffSPhil Shafer ssize_t i;
115231337658SMarcel Moolenaar
115331337658SMarcel Moolenaar if (len == 1) { /* Simple case */
115431337658SMarcel Moolenaar buf[0] = wc & 0x7f;
115531337658SMarcel Moolenaar return;
115631337658SMarcel Moolenaar }
115731337658SMarcel Moolenaar
11582f784130SPhil Shafer /* Start with the low bits and insert them, six bits at a time */
115931337658SMarcel Moolenaar for (i = len - 1; i >= 0; i--) {
116031337658SMarcel Moolenaar buf[i] = 0x80 | (wc & 0x3f);
1161f2b7bf8aSPhil Shafer wc >>= 6; /* Drop the low six bits */
116231337658SMarcel Moolenaar }
116331337658SMarcel Moolenaar
1164f2b7bf8aSPhil Shafer /* Finish off the first byte with the length bits */
1165f2b7bf8aSPhil Shafer buf[0] &= xo_utf8_data_bits[len]; /* Clear out the length bits */
1166f2b7bf8aSPhil Shafer buf[0] |= xo_utf8_len_bits[len]; /* Drop in new length bits */
116731337658SMarcel Moolenaar }
116831337658SMarcel Moolenaar
1169f2b7bf8aSPhil Shafer /*
1170f2b7bf8aSPhil Shafer * Append a single UTF-8 character to a buffer, converting it to locale
1171f2b7bf8aSPhil Shafer * encoding. Returns the number of columns consumed by that character,
1172f2b7bf8aSPhil Shafer * as best we can determine it.
1173f2b7bf8aSPhil Shafer */
11748a6eceffSPhil Shafer static ssize_t
xo_buf_append_locale_from_utf8(xo_handle_t * xop,xo_buffer_t * xbp,const char * ibuf,ssize_t ilen)117531337658SMarcel Moolenaar xo_buf_append_locale_from_utf8 (xo_handle_t *xop, xo_buffer_t *xbp,
11768a6eceffSPhil Shafer const char *ibuf, ssize_t ilen)
117731337658SMarcel Moolenaar {
117831337658SMarcel Moolenaar wchar_t wc;
11798a6eceffSPhil Shafer ssize_t len;
118031337658SMarcel Moolenaar
118131337658SMarcel Moolenaar /*
118231337658SMarcel Moolenaar * Build our wide character from the input buffer; the number of
118331337658SMarcel Moolenaar * bits we pull off the first character is dependent on the length,
118431337658SMarcel Moolenaar * but we put 6 bits off all other bytes.
118531337658SMarcel Moolenaar */
118631337658SMarcel Moolenaar wc = xo_utf8_char(ibuf, ilen);
118731337658SMarcel Moolenaar if (wc == (wchar_t) -1) {
1188f2b7bf8aSPhil Shafer xo_failure(xop, "invalid UTF-8 byte sequence");
118931337658SMarcel Moolenaar return 0;
119031337658SMarcel Moolenaar }
119131337658SMarcel Moolenaar
1192d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_NO_LOCALE)) {
119331337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, ilen))
119431337658SMarcel Moolenaar return 0;
119531337658SMarcel Moolenaar
119631337658SMarcel Moolenaar memcpy(xbp->xb_curp, ibuf, ilen);
119731337658SMarcel Moolenaar xbp->xb_curp += ilen;
119831337658SMarcel Moolenaar
119931337658SMarcel Moolenaar } else {
120031337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, MB_LEN_MAX + 1))
120131337658SMarcel Moolenaar return 0;
120231337658SMarcel Moolenaar
120331337658SMarcel Moolenaar bzero(&xop->xo_mbstate, sizeof(xop->xo_mbstate));
120431337658SMarcel Moolenaar len = wcrtomb(xbp->xb_curp, wc, &xop->xo_mbstate);
120531337658SMarcel Moolenaar
120631337658SMarcel Moolenaar if (len <= 0) {
120731337658SMarcel Moolenaar xo_failure(xop, "could not convert wide char: %lx",
120831337658SMarcel Moolenaar (unsigned long) wc);
120931337658SMarcel Moolenaar return 0;
121031337658SMarcel Moolenaar }
121131337658SMarcel Moolenaar xbp->xb_curp += len;
121231337658SMarcel Moolenaar }
121331337658SMarcel Moolenaar
1214d1a0d267SMarcel Moolenaar return xo_wcwidth(wc);
121531337658SMarcel Moolenaar }
121631337658SMarcel Moolenaar
1217f2b7bf8aSPhil Shafer /*
1218f2b7bf8aSPhil Shafer * Append a UTF-8 string to a buffer, converting it into locale encoding
1219f2b7bf8aSPhil Shafer */
122031337658SMarcel Moolenaar static void
xo_buf_append_locale(xo_handle_t * xop,xo_buffer_t * xbp,const char * cp,ssize_t len)122131337658SMarcel Moolenaar xo_buf_append_locale (xo_handle_t *xop, xo_buffer_t *xbp,
12228a6eceffSPhil Shafer const char *cp, ssize_t len)
122331337658SMarcel Moolenaar {
122431337658SMarcel Moolenaar const char *sp = cp, *ep = cp + len;
12258a6eceffSPhil Shafer ssize_t save_off = xbp->xb_bufp - xbp->xb_curp;
12268a6eceffSPhil Shafer ssize_t slen;
122731337658SMarcel Moolenaar int cols = 0;
122831337658SMarcel Moolenaar
122931337658SMarcel Moolenaar for ( ; cp < ep; cp++) {
123031337658SMarcel Moolenaar if (!xo_is_utf8(*cp)) {
123131337658SMarcel Moolenaar cols += 1;
123231337658SMarcel Moolenaar continue;
123331337658SMarcel Moolenaar }
123431337658SMarcel Moolenaar
123531337658SMarcel Moolenaar /*
123631337658SMarcel Moolenaar * We're looking at a non-ascii UTF-8 character.
123731337658SMarcel Moolenaar * First we copy the previous data.
123831337658SMarcel Moolenaar * Then we need find the length and validate it.
123931337658SMarcel Moolenaar * Then we turn it into a wide string.
124031337658SMarcel Moolenaar * Then we turn it into a localized string.
124131337658SMarcel Moolenaar * Then we repeat. Isn't i18n fun?
124231337658SMarcel Moolenaar */
124331337658SMarcel Moolenaar if (sp != cp)
124431337658SMarcel Moolenaar xo_buf_append(xbp, sp, cp - sp); /* Append previous data */
124531337658SMarcel Moolenaar
124631337658SMarcel Moolenaar slen = xo_buf_utf8_len(xop, cp, ep - cp);
124731337658SMarcel Moolenaar if (slen <= 0) {
124831337658SMarcel Moolenaar /* Bad data; back it all out */
124931337658SMarcel Moolenaar xbp->xb_curp = xbp->xb_bufp + save_off;
125031337658SMarcel Moolenaar return;
125131337658SMarcel Moolenaar }
125231337658SMarcel Moolenaar
125331337658SMarcel Moolenaar cols += xo_buf_append_locale_from_utf8(xop, xbp, cp, slen);
125431337658SMarcel Moolenaar
1255ee5cf116SPhil Shafer /* Next time through, we'll start at the next character */
125631337658SMarcel Moolenaar cp += slen - 1;
125731337658SMarcel Moolenaar sp = cp + 1;
125831337658SMarcel Moolenaar }
125931337658SMarcel Moolenaar
126031337658SMarcel Moolenaar /* Update column values */
1261d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_COLUMNS))
126231337658SMarcel Moolenaar xop->xo_columns += cols;
1263d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_ANCHOR))
126431337658SMarcel Moolenaar xop->xo_anchor_columns += cols;
126531337658SMarcel Moolenaar
126631337658SMarcel Moolenaar /* Before we fall into the basic logic below, we need reset len */
126731337658SMarcel Moolenaar len = ep - sp;
126831337658SMarcel Moolenaar if (len != 0) /* Append trailing data */
126931337658SMarcel Moolenaar xo_buf_append(xbp, sp, len);
127031337658SMarcel Moolenaar }
127131337658SMarcel Moolenaar
127231337658SMarcel Moolenaar /*
1273d1a0d267SMarcel Moolenaar * Append the given string to the given buffer, without escaping or
1274d1a0d267SMarcel Moolenaar * character set conversion. This is the straight copy to the data
1275d1a0d267SMarcel Moolenaar * buffer with no fanciness.
127631337658SMarcel Moolenaar */
127731337658SMarcel Moolenaar static void
xo_data_append(xo_handle_t * xop,const char * str,ssize_t len)12788a6eceffSPhil Shafer xo_data_append (xo_handle_t *xop, const char *str, ssize_t len)
127931337658SMarcel Moolenaar {
128031337658SMarcel Moolenaar xo_buf_append(&xop->xo_data, str, len);
128131337658SMarcel Moolenaar }
128231337658SMarcel Moolenaar
128331337658SMarcel Moolenaar /*
128431337658SMarcel Moolenaar * Append the given string to the given buffer
128531337658SMarcel Moolenaar */
128631337658SMarcel Moolenaar static void
xo_data_escape(xo_handle_t * xop,const char * str,ssize_t len)12878a6eceffSPhil Shafer xo_data_escape (xo_handle_t *xop, const char *str, ssize_t len)
128831337658SMarcel Moolenaar {
128931337658SMarcel Moolenaar xo_buf_escape(xop, &xop->xo_data, str, len, 0);
129031337658SMarcel Moolenaar }
129131337658SMarcel Moolenaar
129242ff34c3SPhil Shafer #ifdef LIBXO_NO_RETAIN
129342ff34c3SPhil Shafer /*
129442ff34c3SPhil Shafer * Empty implementations of the retain logic
129542ff34c3SPhil Shafer */
129642ff34c3SPhil Shafer
129742ff34c3SPhil Shafer void
xo_retain_clear_all(void)129842ff34c3SPhil Shafer xo_retain_clear_all (void)
129942ff34c3SPhil Shafer {
130042ff34c3SPhil Shafer return;
130142ff34c3SPhil Shafer }
130242ff34c3SPhil Shafer
130342ff34c3SPhil Shafer void
xo_retain_clear(const char * fmt UNUSED)130442ff34c3SPhil Shafer xo_retain_clear (const char *fmt UNUSED)
130542ff34c3SPhil Shafer {
130642ff34c3SPhil Shafer return;
130742ff34c3SPhil Shafer }
130842ff34c3SPhil Shafer static void
xo_retain_add(const char * fmt UNUSED,xo_field_info_t * fields UNUSED,unsigned num_fields UNUSED)130942ff34c3SPhil Shafer xo_retain_add (const char *fmt UNUSED, xo_field_info_t *fields UNUSED,
131042ff34c3SPhil Shafer unsigned num_fields UNUSED)
131142ff34c3SPhil Shafer {
131242ff34c3SPhil Shafer return;
131342ff34c3SPhil Shafer }
131442ff34c3SPhil Shafer
131542ff34c3SPhil Shafer static int
xo_retain_find(const char * fmt UNUSED,xo_field_info_t ** valp UNUSED,unsigned * nump UNUSED)131642ff34c3SPhil Shafer xo_retain_find (const char *fmt UNUSED, xo_field_info_t **valp UNUSED,
131742ff34c3SPhil Shafer unsigned *nump UNUSED)
131842ff34c3SPhil Shafer {
131942ff34c3SPhil Shafer return -1;
132042ff34c3SPhil Shafer }
132142ff34c3SPhil Shafer
132242ff34c3SPhil Shafer #else /* !LIBXO_NO_RETAIN */
132342ff34c3SPhil Shafer /*
132442ff34c3SPhil Shafer * Retain: We retain parsed field definitions to enhance performance,
132542ff34c3SPhil Shafer * especially inside loops. We depend on the caller treating the format
132642ff34c3SPhil Shafer * strings as immutable, so that we can retain pointers into them. We
132742ff34c3SPhil Shafer * hold the pointers in a hash table, so allow quick access. Retained
132842ff34c3SPhil Shafer * information is retained until xo_retain_clear is called.
132942ff34c3SPhil Shafer */
133042ff34c3SPhil Shafer
133142ff34c3SPhil Shafer /*
133242ff34c3SPhil Shafer * xo_retain_entry_t holds information about one retained set of
133342ff34c3SPhil Shafer * parsed fields.
133442ff34c3SPhil Shafer */
133542ff34c3SPhil Shafer typedef struct xo_retain_entry_s {
133642ff34c3SPhil Shafer struct xo_retain_entry_s *xre_next; /* Pointer to next (older) entry */
133742ff34c3SPhil Shafer unsigned long xre_hits; /* Number of times we've hit */
133842ff34c3SPhil Shafer const char *xre_format; /* Pointer to format string */
133942ff34c3SPhil Shafer unsigned xre_num_fields; /* Number of fields saved */
134042ff34c3SPhil Shafer xo_field_info_t *xre_fields; /* Pointer to fields */
134142ff34c3SPhil Shafer } xo_retain_entry_t;
134242ff34c3SPhil Shafer
134342ff34c3SPhil Shafer /*
134442ff34c3SPhil Shafer * xo_retain_t holds a complete set of parsed fields as a hash table.
134542ff34c3SPhil Shafer */
134642ff34c3SPhil Shafer #ifndef XO_RETAIN_SIZE
134742ff34c3SPhil Shafer #define XO_RETAIN_SIZE 6
134842ff34c3SPhil Shafer #endif /* XO_RETAIN_SIZE */
134942ff34c3SPhil Shafer #define RETAIN_HASH_SIZE (1<<XO_RETAIN_SIZE)
135042ff34c3SPhil Shafer
135142ff34c3SPhil Shafer typedef struct xo_retain_s {
135242ff34c3SPhil Shafer xo_retain_entry_t *xr_bucket[RETAIN_HASH_SIZE];
135342ff34c3SPhil Shafer } xo_retain_t;
135442ff34c3SPhil Shafer
135542ff34c3SPhil Shafer static THREAD_LOCAL(xo_retain_t) xo_retain;
135642ff34c3SPhil Shafer static THREAD_LOCAL(unsigned) xo_retain_count;
135742ff34c3SPhil Shafer
135842ff34c3SPhil Shafer /*
135942ff34c3SPhil Shafer * Simple hash function based on Thomas Wang's paper. The original is
136042ff34c3SPhil Shafer * gone, but an archive is available on the Way Back Machine:
136142ff34c3SPhil Shafer *
136242ff34c3SPhil Shafer * http://web.archive.org/web/20071223173210/\
136342ff34c3SPhil Shafer * http://www.concentric.net/~Ttwang/tech/inthash.htm
136442ff34c3SPhil Shafer *
136542ff34c3SPhil Shafer * For our purposes, we can assume the low four bits are uninteresting
136642ff34c3SPhil Shafer * since any string less that 16 bytes wouldn't be worthy of
136742ff34c3SPhil Shafer * retaining. We toss the high bits also, since these bits are likely
136842ff34c3SPhil Shafer * to be common among constant format strings. We then run Wang's
136942ff34c3SPhil Shafer * algorithm, and cap the result at RETAIN_HASH_SIZE.
137042ff34c3SPhil Shafer */
137142ff34c3SPhil Shafer static unsigned
xo_retain_hash(const char * fmt)137242ff34c3SPhil Shafer xo_retain_hash (const char *fmt)
137342ff34c3SPhil Shafer {
137442ff34c3SPhil Shafer volatile uintptr_t iptr = (uintptr_t) (const void *) fmt;
137542ff34c3SPhil Shafer
137642ff34c3SPhil Shafer /* Discard low four bits and high bits; they aren't interesting */
137742ff34c3SPhil Shafer uint32_t val = (uint32_t) ((iptr >> 4) & (((1 << 24) - 1)));
137842ff34c3SPhil Shafer
137942ff34c3SPhil Shafer val = (val ^ 61) ^ (val >> 16);
138042ff34c3SPhil Shafer val = val + (val << 3);
138142ff34c3SPhil Shafer val = val ^ (val >> 4);
138242ff34c3SPhil Shafer val = val * 0x3a8f05c5; /* My large prime number */
138342ff34c3SPhil Shafer val = val ^ (val >> 15);
138442ff34c3SPhil Shafer val &= RETAIN_HASH_SIZE - 1;
138542ff34c3SPhil Shafer
138642ff34c3SPhil Shafer return val;
138742ff34c3SPhil Shafer }
138842ff34c3SPhil Shafer
138942ff34c3SPhil Shafer /*
139042ff34c3SPhil Shafer * Walk all buckets, clearing all retained entries
139142ff34c3SPhil Shafer */
139242ff34c3SPhil Shafer void
xo_retain_clear_all(void)139342ff34c3SPhil Shafer xo_retain_clear_all (void)
139442ff34c3SPhil Shafer {
139542ff34c3SPhil Shafer int i;
139642ff34c3SPhil Shafer xo_retain_entry_t *xrep, *next;
139742ff34c3SPhil Shafer
139842ff34c3SPhil Shafer for (i = 0; i < RETAIN_HASH_SIZE; i++) {
139942ff34c3SPhil Shafer for (xrep = xo_retain.xr_bucket[i]; xrep; xrep = next) {
140042ff34c3SPhil Shafer next = xrep->xre_next;
140142ff34c3SPhil Shafer xo_free(xrep);
140242ff34c3SPhil Shafer }
140342ff34c3SPhil Shafer xo_retain.xr_bucket[i] = NULL;
140442ff34c3SPhil Shafer }
140542ff34c3SPhil Shafer xo_retain_count = 0;
140642ff34c3SPhil Shafer }
140742ff34c3SPhil Shafer
140842ff34c3SPhil Shafer /*
140942ff34c3SPhil Shafer * Walk all buckets, clearing all retained entries
141042ff34c3SPhil Shafer */
141142ff34c3SPhil Shafer void
xo_retain_clear(const char * fmt)141242ff34c3SPhil Shafer xo_retain_clear (const char *fmt)
141342ff34c3SPhil Shafer {
141442ff34c3SPhil Shafer xo_retain_entry_t **xrepp;
141542ff34c3SPhil Shafer unsigned hash = xo_retain_hash(fmt);
141642ff34c3SPhil Shafer
141742ff34c3SPhil Shafer for (xrepp = &xo_retain.xr_bucket[hash]; *xrepp;
141842ff34c3SPhil Shafer xrepp = &(*xrepp)->xre_next) {
141942ff34c3SPhil Shafer if ((*xrepp)->xre_format == fmt) {
142042ff34c3SPhil Shafer *xrepp = (*xrepp)->xre_next;
142142ff34c3SPhil Shafer xo_retain_count -= 1;
142242ff34c3SPhil Shafer return;
142342ff34c3SPhil Shafer }
142442ff34c3SPhil Shafer }
142542ff34c3SPhil Shafer }
142642ff34c3SPhil Shafer
142742ff34c3SPhil Shafer /*
142842ff34c3SPhil Shafer * Search the hash for an entry matching 'fmt'; return it's fields.
142942ff34c3SPhil Shafer */
143042ff34c3SPhil Shafer static int
xo_retain_find(const char * fmt,xo_field_info_t ** valp,unsigned * nump)143142ff34c3SPhil Shafer xo_retain_find (const char *fmt, xo_field_info_t **valp, unsigned *nump)
143242ff34c3SPhil Shafer {
143342ff34c3SPhil Shafer if (xo_retain_count == 0)
143442ff34c3SPhil Shafer return -1;
143542ff34c3SPhil Shafer
143642ff34c3SPhil Shafer unsigned hash = xo_retain_hash(fmt);
143742ff34c3SPhil Shafer xo_retain_entry_t *xrep;
143842ff34c3SPhil Shafer
143942ff34c3SPhil Shafer for (xrep = xo_retain.xr_bucket[hash]; xrep != NULL;
144042ff34c3SPhil Shafer xrep = xrep->xre_next) {
144142ff34c3SPhil Shafer if (xrep->xre_format == fmt) {
144242ff34c3SPhil Shafer *valp = xrep->xre_fields;
144342ff34c3SPhil Shafer *nump = xrep->xre_num_fields;
144442ff34c3SPhil Shafer xrep->xre_hits += 1;
144542ff34c3SPhil Shafer return 0;
144642ff34c3SPhil Shafer }
144742ff34c3SPhil Shafer }
144842ff34c3SPhil Shafer
144942ff34c3SPhil Shafer return -1;
145042ff34c3SPhil Shafer }
145142ff34c3SPhil Shafer
145242ff34c3SPhil Shafer static void
xo_retain_add(const char * fmt,xo_field_info_t * fields,unsigned num_fields)145342ff34c3SPhil Shafer xo_retain_add (const char *fmt, xo_field_info_t *fields, unsigned num_fields)
145442ff34c3SPhil Shafer {
145542ff34c3SPhil Shafer unsigned hash = xo_retain_hash(fmt);
145642ff34c3SPhil Shafer xo_retain_entry_t *xrep;
14578a6eceffSPhil Shafer ssize_t sz = sizeof(*xrep) + (num_fields + 1) * sizeof(*fields);
145842ff34c3SPhil Shafer xo_field_info_t *xfip;
145942ff34c3SPhil Shafer
146042ff34c3SPhil Shafer xrep = xo_realloc(NULL, sz);
146142ff34c3SPhil Shafer if (xrep == NULL)
146242ff34c3SPhil Shafer return;
146342ff34c3SPhil Shafer
146442ff34c3SPhil Shafer xfip = (xo_field_info_t *) &xrep[1];
146542ff34c3SPhil Shafer memcpy(xfip, fields, num_fields * sizeof(*fields));
146642ff34c3SPhil Shafer
146742ff34c3SPhil Shafer bzero(xrep, sizeof(*xrep));
146842ff34c3SPhil Shafer
146942ff34c3SPhil Shafer xrep->xre_format = fmt;
147042ff34c3SPhil Shafer xrep->xre_fields = xfip;
147142ff34c3SPhil Shafer xrep->xre_num_fields = num_fields;
147242ff34c3SPhil Shafer
147342ff34c3SPhil Shafer /* Record the field info in the retain bucket */
147442ff34c3SPhil Shafer xrep->xre_next = xo_retain.xr_bucket[hash];
147542ff34c3SPhil Shafer xo_retain.xr_bucket[hash] = xrep;
147642ff34c3SPhil Shafer xo_retain_count += 1;
147742ff34c3SPhil Shafer }
147842ff34c3SPhil Shafer
147942ff34c3SPhil Shafer #endif /* !LIBXO_NO_RETAIN */
148042ff34c3SPhil Shafer
148131337658SMarcel Moolenaar /*
148231337658SMarcel Moolenaar * Generate a warning. Normally, this is a text message written to
148331337658SMarcel Moolenaar * standard error. If the XOF_WARN_XML flag is set, then we generate
148431337658SMarcel Moolenaar * XMLified content on standard output.
148531337658SMarcel Moolenaar */
148631337658SMarcel Moolenaar static void
xo_warn_hcv(xo_handle_t * xop,int code,int check_warn,const char * fmt,va_list vap)148731337658SMarcel Moolenaar xo_warn_hcv (xo_handle_t *xop, int code, int check_warn,
148831337658SMarcel Moolenaar const char *fmt, va_list vap)
148931337658SMarcel Moolenaar {
149031337658SMarcel Moolenaar xop = xo_default(xop);
1491d1a0d267SMarcel Moolenaar if (check_warn && !XOF_ISSET(xop, XOF_WARN))
149231337658SMarcel Moolenaar return;
149331337658SMarcel Moolenaar
149431337658SMarcel Moolenaar if (fmt == NULL)
149531337658SMarcel Moolenaar return;
149631337658SMarcel Moolenaar
14978a6eceffSPhil Shafer ssize_t len = strlen(fmt);
14988a6eceffSPhil Shafer ssize_t plen = xo_program ? strlen(xo_program) : 0;
1499545ddfbeSMarcel Moolenaar char *newfmt = alloca(len + 1 + plen + 2); /* NUL, and ": " */
150031337658SMarcel Moolenaar
150131337658SMarcel Moolenaar if (plen) {
150231337658SMarcel Moolenaar memcpy(newfmt, xo_program, plen);
150331337658SMarcel Moolenaar newfmt[plen++] = ':';
150431337658SMarcel Moolenaar newfmt[plen++] = ' ';
150531337658SMarcel Moolenaar }
15062f784130SPhil Shafer
150731337658SMarcel Moolenaar memcpy(newfmt + plen, fmt, len);
150831337658SMarcel Moolenaar newfmt[len + plen] = '\0';
150931337658SMarcel Moolenaar
1510d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_WARN_XML)) {
151131337658SMarcel Moolenaar static char err_open[] = "<error>";
151231337658SMarcel Moolenaar static char err_close[] = "</error>";
151331337658SMarcel Moolenaar static char msg_open[] = "<message>";
151431337658SMarcel Moolenaar static char msg_close[] = "</message>";
151531337658SMarcel Moolenaar
151631337658SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_data;
151731337658SMarcel Moolenaar
151831337658SMarcel Moolenaar xo_buf_append(xbp, err_open, sizeof(err_open) - 1);
151931337658SMarcel Moolenaar xo_buf_append(xbp, msg_open, sizeof(msg_open) - 1);
152031337658SMarcel Moolenaar
152131337658SMarcel Moolenaar va_list va_local;
152231337658SMarcel Moolenaar va_copy(va_local, vap);
152331337658SMarcel Moolenaar
15248a6eceffSPhil Shafer ssize_t left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp);
15258a6eceffSPhil Shafer ssize_t rc = vsnprintf(xbp->xb_curp, left, newfmt, vap);
15262f784130SPhil Shafer
1527d1a0d267SMarcel Moolenaar if (rc >= left) {
1528c600d307SMarcel Moolenaar if (!xo_buf_has_room(xbp, rc)) {
1529c600d307SMarcel Moolenaar va_end(va_local);
153031337658SMarcel Moolenaar return;
1531c600d307SMarcel Moolenaar }
153231337658SMarcel Moolenaar
153331337658SMarcel Moolenaar va_end(vap); /* Reset vap to the start */
153431337658SMarcel Moolenaar va_copy(vap, va_local);
153531337658SMarcel Moolenaar
153631337658SMarcel Moolenaar left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp);
153731337658SMarcel Moolenaar rc = vsnprintf(xbp->xb_curp, left, fmt, vap);
153831337658SMarcel Moolenaar }
15392f784130SPhil Shafer
154031337658SMarcel Moolenaar va_end(va_local);
154131337658SMarcel Moolenaar
154231337658SMarcel Moolenaar rc = xo_escape_xml(xbp, rc, 1);
154331337658SMarcel Moolenaar xbp->xb_curp += rc;
154431337658SMarcel Moolenaar
154531337658SMarcel Moolenaar xo_buf_append(xbp, msg_close, sizeof(msg_close) - 1);
154631337658SMarcel Moolenaar xo_buf_append(xbp, err_close, sizeof(err_close) - 1);
154731337658SMarcel Moolenaar
1548545ddfbeSMarcel Moolenaar if (code >= 0) {
154931337658SMarcel Moolenaar const char *msg = strerror(code);
15502f784130SPhil Shafer
155131337658SMarcel Moolenaar if (msg) {
155231337658SMarcel Moolenaar xo_buf_append(xbp, ": ", 2);
155331337658SMarcel Moolenaar xo_buf_append(xbp, msg, strlen(msg));
155431337658SMarcel Moolenaar }
155531337658SMarcel Moolenaar }
155631337658SMarcel Moolenaar
1557d1a0d267SMarcel Moolenaar xo_buf_append(xbp, "\n", 1); /* Append newline and NUL to string */
1558545ddfbeSMarcel Moolenaar (void) xo_write(xop);
155931337658SMarcel Moolenaar
156031337658SMarcel Moolenaar } else {
156131337658SMarcel Moolenaar vfprintf(stderr, newfmt, vap);
1562545ddfbeSMarcel Moolenaar if (code >= 0) {
1563545ddfbeSMarcel Moolenaar const char *msg = strerror(code);
15642f784130SPhil Shafer
1565545ddfbeSMarcel Moolenaar if (msg)
1566545ddfbeSMarcel Moolenaar fprintf(stderr, ": %s", msg);
1567545ddfbeSMarcel Moolenaar }
1568545ddfbeSMarcel Moolenaar fprintf(stderr, "\n");
156931337658SMarcel Moolenaar }
157031337658SMarcel Moolenaar }
157131337658SMarcel Moolenaar
157231337658SMarcel Moolenaar void
xo_warn_hc(xo_handle_t * xop,int code,const char * fmt,...)157331337658SMarcel Moolenaar xo_warn_hc (xo_handle_t *xop, int code, const char *fmt, ...)
157431337658SMarcel Moolenaar {
157531337658SMarcel Moolenaar va_list vap;
157631337658SMarcel Moolenaar
157731337658SMarcel Moolenaar va_start(vap, fmt);
157831337658SMarcel Moolenaar xo_warn_hcv(xop, code, 0, fmt, vap);
157931337658SMarcel Moolenaar va_end(vap);
158031337658SMarcel Moolenaar }
158131337658SMarcel Moolenaar
158231337658SMarcel Moolenaar void
xo_warn_c(int code,const char * fmt,...)158331337658SMarcel Moolenaar xo_warn_c (int code, const char *fmt, ...)
158431337658SMarcel Moolenaar {
158531337658SMarcel Moolenaar va_list vap;
158631337658SMarcel Moolenaar
158731337658SMarcel Moolenaar va_start(vap, fmt);
1588545ddfbeSMarcel Moolenaar xo_warn_hcv(NULL, code, 0, fmt, vap);
158931337658SMarcel Moolenaar va_end(vap);
159031337658SMarcel Moolenaar }
159131337658SMarcel Moolenaar
159231337658SMarcel Moolenaar void
xo_warn(const char * fmt,...)159331337658SMarcel Moolenaar xo_warn (const char *fmt, ...)
159431337658SMarcel Moolenaar {
159531337658SMarcel Moolenaar int code = errno;
159631337658SMarcel Moolenaar va_list vap;
159731337658SMarcel Moolenaar
159831337658SMarcel Moolenaar va_start(vap, fmt);
159931337658SMarcel Moolenaar xo_warn_hcv(NULL, code, 0, fmt, vap);
160031337658SMarcel Moolenaar va_end(vap);
160131337658SMarcel Moolenaar }
160231337658SMarcel Moolenaar
160331337658SMarcel Moolenaar void
xo_warnx(const char * fmt,...)160431337658SMarcel Moolenaar xo_warnx (const char *fmt, ...)
160531337658SMarcel Moolenaar {
160631337658SMarcel Moolenaar va_list vap;
160731337658SMarcel Moolenaar
160831337658SMarcel Moolenaar va_start(vap, fmt);
160931337658SMarcel Moolenaar xo_warn_hcv(NULL, -1, 0, fmt, vap);
161031337658SMarcel Moolenaar va_end(vap);
161131337658SMarcel Moolenaar }
161231337658SMarcel Moolenaar
161331337658SMarcel Moolenaar void
xo_err(int eval,const char * fmt,...)161431337658SMarcel Moolenaar xo_err (int eval, const char *fmt, ...)
161531337658SMarcel Moolenaar {
161631337658SMarcel Moolenaar int code = errno;
161731337658SMarcel Moolenaar va_list vap;
161831337658SMarcel Moolenaar
161931337658SMarcel Moolenaar va_start(vap, fmt);
162031337658SMarcel Moolenaar xo_warn_hcv(NULL, code, 0, fmt, vap);
162131337658SMarcel Moolenaar va_end(vap);
162231337658SMarcel Moolenaar xo_finish();
162331337658SMarcel Moolenaar exit(eval);
162431337658SMarcel Moolenaar }
162531337658SMarcel Moolenaar
162631337658SMarcel Moolenaar void
xo_errx(int eval,const char * fmt,...)162731337658SMarcel Moolenaar xo_errx (int eval, const char *fmt, ...)
162831337658SMarcel Moolenaar {
162931337658SMarcel Moolenaar va_list vap;
163031337658SMarcel Moolenaar
163131337658SMarcel Moolenaar va_start(vap, fmt);
163231337658SMarcel Moolenaar xo_warn_hcv(NULL, -1, 0, fmt, vap);
163331337658SMarcel Moolenaar va_end(vap);
163431337658SMarcel Moolenaar xo_finish();
163531337658SMarcel Moolenaar exit(eval);
163631337658SMarcel Moolenaar }
163731337658SMarcel Moolenaar
163831337658SMarcel Moolenaar void
xo_errc(int eval,int code,const char * fmt,...)163931337658SMarcel Moolenaar xo_errc (int eval, int code, const char *fmt, ...)
164031337658SMarcel Moolenaar {
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 /*
165131337658SMarcel Moolenaar * Generate a warning. Normally, this is a text message written to
165231337658SMarcel Moolenaar * standard error. If the XOF_WARN_XML flag is set, then we generate
165331337658SMarcel Moolenaar * XMLified content on standard output.
165431337658SMarcel Moolenaar */
165531337658SMarcel Moolenaar void
xo_message_hcv(xo_handle_t * xop,int code,const char * fmt,va_list vap)165631337658SMarcel Moolenaar xo_message_hcv (xo_handle_t *xop, int code, const char *fmt, va_list vap)
165731337658SMarcel Moolenaar {
165831337658SMarcel Moolenaar static char msg_open[] = "<message>";
165931337658SMarcel Moolenaar static char msg_close[] = "</message>";
166031337658SMarcel Moolenaar xo_buffer_t *xbp;
16618a6eceffSPhil Shafer ssize_t rc;
166231337658SMarcel Moolenaar va_list va_local;
166331337658SMarcel Moolenaar
166431337658SMarcel Moolenaar xop = xo_default(xop);
166531337658SMarcel Moolenaar
166631337658SMarcel Moolenaar if (fmt == NULL || *fmt == '\0')
166731337658SMarcel Moolenaar return;
166831337658SMarcel Moolenaar
166931337658SMarcel Moolenaar int need_nl = (fmt[strlen(fmt) - 1] != '\n');
167031337658SMarcel Moolenaar
1671788ca347SMarcel Moolenaar switch (xo_style(xop)) {
167231337658SMarcel Moolenaar case XO_STYLE_XML:
167331337658SMarcel Moolenaar xbp = &xop->xo_data;
1674d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_PRETTY))
167531337658SMarcel Moolenaar xo_buf_indent(xop, xop->xo_indent_by);
167631337658SMarcel Moolenaar xo_buf_append(xbp, msg_open, sizeof(msg_open) - 1);
167731337658SMarcel Moolenaar
167831337658SMarcel Moolenaar va_copy(va_local, vap);
167931337658SMarcel Moolenaar
16808a6eceffSPhil Shafer ssize_t left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp);
16812f784130SPhil Shafer
168231337658SMarcel Moolenaar rc = vsnprintf(xbp->xb_curp, left, fmt, vap);
1683d1a0d267SMarcel Moolenaar if (rc >= left) {
1684c600d307SMarcel Moolenaar if (!xo_buf_has_room(xbp, rc)) {
1685c600d307SMarcel Moolenaar va_end(va_local);
168631337658SMarcel Moolenaar return;
1687c600d307SMarcel Moolenaar }
168831337658SMarcel Moolenaar
168931337658SMarcel Moolenaar va_end(vap); /* Reset vap to the start */
169031337658SMarcel Moolenaar va_copy(vap, va_local);
169131337658SMarcel Moolenaar
169231337658SMarcel Moolenaar left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp);
169331337658SMarcel Moolenaar rc = vsnprintf(xbp->xb_curp, left, fmt, vap);
169431337658SMarcel Moolenaar }
16952f784130SPhil Shafer
169631337658SMarcel Moolenaar va_end(va_local);
169731337658SMarcel Moolenaar
1698d1a0d267SMarcel Moolenaar rc = xo_escape_xml(xbp, rc, 0);
169931337658SMarcel Moolenaar xbp->xb_curp += rc;
170031337658SMarcel Moolenaar
170131337658SMarcel Moolenaar if (need_nl && code > 0) {
170231337658SMarcel Moolenaar const char *msg = strerror(code);
17032f784130SPhil Shafer
170431337658SMarcel Moolenaar if (msg) {
170531337658SMarcel Moolenaar xo_buf_append(xbp, ": ", 2);
170631337658SMarcel Moolenaar xo_buf_append(xbp, msg, strlen(msg));
170731337658SMarcel Moolenaar }
170831337658SMarcel Moolenaar }
170931337658SMarcel Moolenaar
171031337658SMarcel Moolenaar if (need_nl)
1711d1a0d267SMarcel Moolenaar xo_buf_append(xbp, "\n", 1); /* Append newline and NUL to string */
1712d1a0d267SMarcel Moolenaar
1713d1a0d267SMarcel Moolenaar xo_buf_append(xbp, msg_close, sizeof(msg_close) - 1);
1714d1a0d267SMarcel Moolenaar
1715d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_PRETTY))
1716d1a0d267SMarcel Moolenaar xo_buf_append(xbp, "\n", 1); /* Append newline and NUL to string */
1717d1a0d267SMarcel Moolenaar
1718545ddfbeSMarcel Moolenaar (void) xo_write(xop);
171931337658SMarcel Moolenaar break;
172031337658SMarcel Moolenaar
172131337658SMarcel Moolenaar case XO_STYLE_HTML:
172231337658SMarcel Moolenaar {
172331337658SMarcel Moolenaar char buf[BUFSIZ], *bp = buf, *cp;
17248a6eceffSPhil Shafer ssize_t bufsiz = sizeof(buf);
17258a6eceffSPhil Shafer ssize_t rc2;
172631337658SMarcel Moolenaar
172731337658SMarcel Moolenaar va_copy(va_local, vap);
172831337658SMarcel Moolenaar
1729c600d307SMarcel Moolenaar rc = vsnprintf(bp, bufsiz, fmt, va_local);
173031337658SMarcel Moolenaar if (rc > bufsiz) {
173131337658SMarcel Moolenaar bufsiz = rc + BUFSIZ;
173231337658SMarcel Moolenaar bp = alloca(bufsiz);
173331337658SMarcel Moolenaar va_end(va_local);
173431337658SMarcel Moolenaar va_copy(va_local, vap);
1735c600d307SMarcel Moolenaar rc = vsnprintf(bp, bufsiz, fmt, va_local);
173631337658SMarcel Moolenaar }
17372f784130SPhil Shafer
1738c600d307SMarcel Moolenaar va_end(va_local);
173931337658SMarcel Moolenaar cp = bp + rc;
174031337658SMarcel Moolenaar
174131337658SMarcel Moolenaar if (need_nl) {
174231337658SMarcel Moolenaar rc2 = snprintf(cp, bufsiz - rc, "%s%s\n",
174331337658SMarcel Moolenaar (code > 0) ? ": " : "",
174431337658SMarcel Moolenaar (code > 0) ? strerror(code) : "");
174531337658SMarcel Moolenaar if (rc2 > 0)
174631337658SMarcel Moolenaar rc += rc2;
174731337658SMarcel Moolenaar }
174831337658SMarcel Moolenaar
1749264104f2SPhil Shafer xo_buf_append_div(xop, "message", 0, NULL, 0, bp, rc,
1750264104f2SPhil Shafer NULL, 0, NULL, 0);
175131337658SMarcel Moolenaar }
175231337658SMarcel Moolenaar break;
175331337658SMarcel Moolenaar
175431337658SMarcel Moolenaar case XO_STYLE_JSON:
1755d1a0d267SMarcel Moolenaar case XO_STYLE_SDPARAMS:
1756d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER:
1757d1a0d267SMarcel Moolenaar /* No means of representing messages */
1758d1a0d267SMarcel Moolenaar return;
175931337658SMarcel Moolenaar
176031337658SMarcel Moolenaar case XO_STYLE_TEXT:
176131337658SMarcel Moolenaar rc = xo_printf_v(xop, fmt, vap);
176231337658SMarcel Moolenaar /*
176331337658SMarcel Moolenaar * XXX need to handle UTF-8 widths
176431337658SMarcel Moolenaar */
176531337658SMarcel Moolenaar if (rc > 0) {
1766d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_COLUMNS))
176731337658SMarcel Moolenaar xop->xo_columns += rc;
1768d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_ANCHOR))
176931337658SMarcel Moolenaar xop->xo_anchor_columns += rc;
177031337658SMarcel Moolenaar }
177131337658SMarcel Moolenaar
177231337658SMarcel Moolenaar if (need_nl && code > 0) {
177331337658SMarcel Moolenaar const char *msg = strerror(code);
17742f784130SPhil Shafer
177531337658SMarcel Moolenaar if (msg) {
177631337658SMarcel Moolenaar xo_printf(xop, ": %s", msg);
177731337658SMarcel Moolenaar }
177831337658SMarcel Moolenaar }
177931337658SMarcel Moolenaar if (need_nl)
178031337658SMarcel Moolenaar xo_printf(xop, "\n");
178131337658SMarcel Moolenaar
178231337658SMarcel Moolenaar break;
178331337658SMarcel Moolenaar }
178431337658SMarcel Moolenaar
178542ff34c3SPhil Shafer switch (xo_style(xop)) {
178642ff34c3SPhil Shafer case XO_STYLE_HTML:
178742ff34c3SPhil Shafer if (XOIF_ISSET(xop, XOIF_DIV_OPEN)) {
178842ff34c3SPhil Shafer static char div_close[] = "</div>";
17892f784130SPhil Shafer
179042ff34c3SPhil Shafer XOIF_CLEAR(xop, XOIF_DIV_OPEN);
179142ff34c3SPhil Shafer xo_data_append(xop, div_close, sizeof(div_close) - 1);
179242ff34c3SPhil Shafer
179342ff34c3SPhil Shafer if (XOF_ISSET(xop, XOF_PRETTY))
179442ff34c3SPhil Shafer xo_data_append(xop, "\n", 1);
179542ff34c3SPhil Shafer }
179642ff34c3SPhil Shafer break;
179742ff34c3SPhil Shafer }
179842ff34c3SPhil Shafer
1799545ddfbeSMarcel Moolenaar (void) xo_flush_h(xop);
180031337658SMarcel Moolenaar }
180131337658SMarcel Moolenaar
180231337658SMarcel Moolenaar void
xo_message_hc(xo_handle_t * xop,int code,const char * fmt,...)180331337658SMarcel Moolenaar xo_message_hc (xo_handle_t *xop, int code, const char *fmt, ...)
180431337658SMarcel Moolenaar {
180531337658SMarcel Moolenaar va_list vap;
180631337658SMarcel Moolenaar
180731337658SMarcel Moolenaar va_start(vap, fmt);
180831337658SMarcel Moolenaar xo_message_hcv(xop, code, fmt, vap);
180931337658SMarcel Moolenaar va_end(vap);
181031337658SMarcel Moolenaar }
181131337658SMarcel Moolenaar
181231337658SMarcel Moolenaar void
xo_message_c(int code,const char * fmt,...)181331337658SMarcel Moolenaar xo_message_c (int code, const char *fmt, ...)
181431337658SMarcel Moolenaar {
181531337658SMarcel Moolenaar va_list vap;
181631337658SMarcel Moolenaar
181731337658SMarcel Moolenaar va_start(vap, fmt);
181831337658SMarcel Moolenaar xo_message_hcv(NULL, code, fmt, vap);
181931337658SMarcel Moolenaar va_end(vap);
182031337658SMarcel Moolenaar }
182131337658SMarcel Moolenaar
182231337658SMarcel Moolenaar void
xo_message_e(const char * fmt,...)1823d1a0d267SMarcel Moolenaar xo_message_e (const char *fmt, ...)
182431337658SMarcel Moolenaar {
182531337658SMarcel Moolenaar int code = errno;
182631337658SMarcel Moolenaar va_list vap;
182731337658SMarcel Moolenaar
182831337658SMarcel Moolenaar va_start(vap, fmt);
182931337658SMarcel Moolenaar xo_message_hcv(NULL, code, fmt, vap);
183031337658SMarcel Moolenaar va_end(vap);
183131337658SMarcel Moolenaar }
183231337658SMarcel Moolenaar
1833d1a0d267SMarcel Moolenaar void
xo_message(const char * fmt,...)1834d1a0d267SMarcel Moolenaar xo_message (const char *fmt, ...)
1835d1a0d267SMarcel Moolenaar {
1836d1a0d267SMarcel Moolenaar va_list vap;
1837d1a0d267SMarcel Moolenaar
1838d1a0d267SMarcel Moolenaar va_start(vap, fmt);
1839d1a0d267SMarcel Moolenaar xo_message_hcv(NULL, 0, fmt, vap);
1840d1a0d267SMarcel Moolenaar va_end(vap);
1841d1a0d267SMarcel Moolenaar }
1842d1a0d267SMarcel Moolenaar
184376afb20cSPhil Shafer void
xo_failure(xo_handle_t * xop,const char * fmt,...)184431337658SMarcel Moolenaar xo_failure (xo_handle_t *xop, const char *fmt, ...)
184531337658SMarcel Moolenaar {
1846d1a0d267SMarcel Moolenaar if (!XOF_ISSET(xop, XOF_WARN))
184731337658SMarcel Moolenaar return;
184831337658SMarcel Moolenaar
184931337658SMarcel Moolenaar va_list vap;
185031337658SMarcel Moolenaar
185131337658SMarcel Moolenaar va_start(vap, fmt);
185231337658SMarcel Moolenaar xo_warn_hcv(xop, -1, 1, fmt, vap);
185331337658SMarcel Moolenaar va_end(vap);
185431337658SMarcel Moolenaar }
185531337658SMarcel Moolenaar
185631337658SMarcel Moolenaar /**
185731337658SMarcel Moolenaar * Create a handle for use by later libxo functions.
185831337658SMarcel Moolenaar *
185931337658SMarcel Moolenaar * Note: normal use of libxo does not require a distinct handle, since
186031337658SMarcel Moolenaar * the default handle (used when NULL is passed) generates text on stdout.
186131337658SMarcel Moolenaar *
1862f2b7bf8aSPhil Shafer * @param style Style of output desired (XO_STYLE_* value)
1863f2b7bf8aSPhil Shafer * @param flags Set of XOF_* flags in use with this handle
1864f2b7bf8aSPhil Shafer * @return Newly allocated handle
1865f2b7bf8aSPhil Shafer * @see xo_destroy
186631337658SMarcel Moolenaar */
186731337658SMarcel Moolenaar xo_handle_t *
xo_create(xo_style_t style,xo_xof_flags_t flags)186831337658SMarcel Moolenaar xo_create (xo_style_t style, xo_xof_flags_t flags)
186931337658SMarcel Moolenaar {
187031337658SMarcel Moolenaar xo_handle_t *xop = xo_realloc(NULL, sizeof(*xop));
187131337658SMarcel Moolenaar
187231337658SMarcel Moolenaar if (xop) {
187331337658SMarcel Moolenaar bzero(xop, sizeof(*xop));
187431337658SMarcel Moolenaar
187531337658SMarcel Moolenaar xop->xo_style = style;
1876d1a0d267SMarcel Moolenaar XOF_SET(xop, flags);
187731337658SMarcel Moolenaar xo_init_handle(xop);
1878d1a0d267SMarcel Moolenaar xop->xo_style = style; /* Reset style (see LIBXO_OPTIONS) */
187931337658SMarcel Moolenaar }
188031337658SMarcel Moolenaar
188131337658SMarcel Moolenaar return xop;
188231337658SMarcel Moolenaar }
188331337658SMarcel Moolenaar
188431337658SMarcel Moolenaar /**
188531337658SMarcel Moolenaar * Create a handle that will write to the given file. Use
188631337658SMarcel Moolenaar * the XOF_CLOSE_FP flag to have the file closed on xo_destroy().
1887f2b7bf8aSPhil Shafer *
1888f2b7bf8aSPhil Shafer * @param fp FILE pointer to use
1889f2b7bf8aSPhil Shafer * @param style Style of output desired (XO_STYLE_* value)
1890f2b7bf8aSPhil Shafer * @param flags Set of XOF_* flags to use with this handle
1891f2b7bf8aSPhil Shafer * @return Newly allocated handle
1892f2b7bf8aSPhil Shafer * @see xo_destroy
189331337658SMarcel Moolenaar */
189431337658SMarcel Moolenaar xo_handle_t *
xo_create_to_file(FILE * fp,xo_style_t style,xo_xof_flags_t flags)189531337658SMarcel Moolenaar xo_create_to_file (FILE *fp, xo_style_t style, xo_xof_flags_t flags)
189631337658SMarcel Moolenaar {
189731337658SMarcel Moolenaar xo_handle_t *xop = xo_create(style, flags);
189831337658SMarcel Moolenaar
189931337658SMarcel Moolenaar if (xop) {
190031337658SMarcel Moolenaar xop->xo_opaque = fp;
190131337658SMarcel Moolenaar xop->xo_write = xo_write_to_file;
190231337658SMarcel Moolenaar xop->xo_close = xo_close_file;
1903545ddfbeSMarcel Moolenaar xop->xo_flush = xo_flush_file;
190431337658SMarcel Moolenaar }
190531337658SMarcel Moolenaar
190631337658SMarcel Moolenaar return xop;
190731337658SMarcel Moolenaar }
190831337658SMarcel Moolenaar
190931337658SMarcel Moolenaar /**
191042ff34c3SPhil Shafer * Set the default handler to output to a file.
1911f2b7bf8aSPhil Shafer *
1912f2b7bf8aSPhil Shafer * @param xop libxo handle
1913f2b7bf8aSPhil Shafer * @param fp FILE pointer to use
1914f2b7bf8aSPhil Shafer * @return 0 on success, non-zero on failure
191542ff34c3SPhil Shafer */
191642ff34c3SPhil Shafer int
xo_set_file_h(xo_handle_t * xop,FILE * fp)191742ff34c3SPhil Shafer xo_set_file_h (xo_handle_t *xop, FILE *fp)
191842ff34c3SPhil Shafer {
191942ff34c3SPhil Shafer xop = xo_default(xop);
192042ff34c3SPhil Shafer
192142ff34c3SPhil Shafer if (fp == NULL) {
192242ff34c3SPhil Shafer xo_failure(xop, "xo_set_file: NULL fp");
192342ff34c3SPhil Shafer return -1;
192442ff34c3SPhil Shafer }
192542ff34c3SPhil Shafer
192642ff34c3SPhil Shafer xop->xo_opaque = fp;
192742ff34c3SPhil Shafer xop->xo_write = xo_write_to_file;
192842ff34c3SPhil Shafer xop->xo_close = xo_close_file;
192942ff34c3SPhil Shafer xop->xo_flush = xo_flush_file;
193042ff34c3SPhil Shafer
193142ff34c3SPhil Shafer return 0;
193242ff34c3SPhil Shafer }
193342ff34c3SPhil Shafer
193442ff34c3SPhil Shafer /**
193542ff34c3SPhil Shafer * Set the default handler to output to a file.
1936f2b7bf8aSPhil Shafer *
1937f2b7bf8aSPhil Shafer * @param fp FILE pointer to use
1938f2b7bf8aSPhil Shafer * @return 0 on success, non-zero on failure
193942ff34c3SPhil Shafer */
194042ff34c3SPhil Shafer int
xo_set_file(FILE * fp)194142ff34c3SPhil Shafer xo_set_file (FILE *fp)
194242ff34c3SPhil Shafer {
194342ff34c3SPhil Shafer return xo_set_file_h(NULL, fp);
194442ff34c3SPhil Shafer }
194542ff34c3SPhil Shafer
194642ff34c3SPhil Shafer /**
194731337658SMarcel Moolenaar * Release any resources held by the handle.
1948f2b7bf8aSPhil Shafer *
1949f2b7bf8aSPhil Shafer * @param xop XO handle to alter (or NULL for default handle)
195031337658SMarcel Moolenaar */
195131337658SMarcel Moolenaar void
xo_destroy(xo_handle_t * xop_arg)1952c600d307SMarcel Moolenaar xo_destroy (xo_handle_t *xop_arg)
195331337658SMarcel Moolenaar {
1954c600d307SMarcel Moolenaar xo_handle_t *xop = xo_default(xop_arg);
195531337658SMarcel Moolenaar
1956d1a0d267SMarcel Moolenaar xo_flush_h(xop);
1957d1a0d267SMarcel Moolenaar
1958d1a0d267SMarcel Moolenaar if (xop->xo_close && XOF_ISSET(xop, XOF_CLOSE_FP))
195931337658SMarcel Moolenaar xop->xo_close(xop->xo_opaque);
196031337658SMarcel Moolenaar
196131337658SMarcel Moolenaar xo_free(xop->xo_stack);
196231337658SMarcel Moolenaar xo_buf_cleanup(&xop->xo_data);
196331337658SMarcel Moolenaar xo_buf_cleanup(&xop->xo_fmt);
196431337658SMarcel Moolenaar xo_buf_cleanup(&xop->xo_predicate);
196531337658SMarcel Moolenaar xo_buf_cleanup(&xop->xo_attrs);
1966788ca347SMarcel Moolenaar xo_buf_cleanup(&xop->xo_color_buf);
1967788ca347SMarcel Moolenaar
1968788ca347SMarcel Moolenaar if (xop->xo_version)
1969788ca347SMarcel Moolenaar xo_free(xop->xo_version);
197031337658SMarcel Moolenaar
1971c600d307SMarcel Moolenaar if (xop_arg == NULL) {
1972545ddfbeSMarcel Moolenaar bzero(&xo_default_handle, sizeof(xo_default_handle));
197331337658SMarcel Moolenaar xo_default_inited = 0;
197431337658SMarcel Moolenaar } else
197531337658SMarcel Moolenaar xo_free(xop);
197631337658SMarcel Moolenaar }
197731337658SMarcel Moolenaar
197831337658SMarcel Moolenaar /**
197931337658SMarcel Moolenaar * Record a new output style to use for the given handle (or default if
198031337658SMarcel Moolenaar * handle is NULL). This output style will be used for any future output.
198131337658SMarcel Moolenaar *
1982f2b7bf8aSPhil Shafer * @param xop XO handle to alter (or NULL for default handle)
1983f2b7bf8aSPhil Shafer * @param style new output style (XO_STYLE_*)
198431337658SMarcel Moolenaar */
198531337658SMarcel Moolenaar void
xo_set_style(xo_handle_t * xop,xo_style_t style)198631337658SMarcel Moolenaar xo_set_style (xo_handle_t *xop, xo_style_t style)
198731337658SMarcel Moolenaar {
198831337658SMarcel Moolenaar xop = xo_default(xop);
198931337658SMarcel Moolenaar xop->xo_style = style;
199031337658SMarcel Moolenaar }
199131337658SMarcel Moolenaar
1992f2b7bf8aSPhil Shafer /**
1993f2b7bf8aSPhil Shafer * Return the current style of a handle
1994f2b7bf8aSPhil Shafer *
1995f2b7bf8aSPhil Shafer * @param xop XO handle to access
1996f2b7bf8aSPhil Shafer * @return The handle's current style
1997f2b7bf8aSPhil Shafer */
199831337658SMarcel Moolenaar xo_style_t
xo_get_style(xo_handle_t * xop)199931337658SMarcel Moolenaar xo_get_style (xo_handle_t *xop)
200031337658SMarcel Moolenaar {
200131337658SMarcel Moolenaar xop = xo_default(xop);
2002788ca347SMarcel Moolenaar return xo_style(xop);
200331337658SMarcel Moolenaar }
200431337658SMarcel Moolenaar
2005f2b7bf8aSPhil Shafer /**
2006f2b7bf8aSPhil Shafer * Return the XO_STYLE_* value matching a given name
2007f2b7bf8aSPhil Shafer *
2008f2b7bf8aSPhil Shafer * @param name String name of a style
2009f2b7bf8aSPhil Shafer * @return XO_STYLE_* value
2010f2b7bf8aSPhil Shafer */
201131337658SMarcel Moolenaar static int
xo_name_to_style(const char * name)201231337658SMarcel Moolenaar xo_name_to_style (const char *name)
201331337658SMarcel Moolenaar {
201476afb20cSPhil Shafer if (xo_streq(name, "xml"))
201531337658SMarcel Moolenaar return XO_STYLE_XML;
201676afb20cSPhil Shafer else if (xo_streq(name, "json"))
201731337658SMarcel Moolenaar return XO_STYLE_JSON;
201876afb20cSPhil Shafer else if (xo_streq(name, "encoder"))
2019d1a0d267SMarcel Moolenaar return XO_STYLE_ENCODER;
202076afb20cSPhil Shafer else if (xo_streq(name, "text"))
202131337658SMarcel Moolenaar return XO_STYLE_TEXT;
202276afb20cSPhil Shafer else if (xo_streq(name, "html"))
202331337658SMarcel Moolenaar return XO_STYLE_HTML;
202476afb20cSPhil Shafer else if (xo_streq(name, "sdparams"))
2025d1a0d267SMarcel Moolenaar return XO_STYLE_SDPARAMS;
202631337658SMarcel Moolenaar
202731337658SMarcel Moolenaar return -1;
202831337658SMarcel Moolenaar }
202931337658SMarcel Moolenaar
203031337658SMarcel Moolenaar /*
2031d1a0d267SMarcel Moolenaar * Indicate if the style is an "encoding" one as opposed to a "display" one.
2032d1a0d267SMarcel Moolenaar */
2033d1a0d267SMarcel Moolenaar static int
xo_style_is_encoding(xo_handle_t * xop)2034d1a0d267SMarcel Moolenaar xo_style_is_encoding (xo_handle_t *xop)
2035d1a0d267SMarcel Moolenaar {
2036d1a0d267SMarcel Moolenaar if (xo_style(xop) == XO_STYLE_JSON
2037d1a0d267SMarcel Moolenaar || xo_style(xop) == XO_STYLE_XML
2038d1a0d267SMarcel Moolenaar || xo_style(xop) == XO_STYLE_SDPARAMS
2039d1a0d267SMarcel Moolenaar || xo_style(xop) == XO_STYLE_ENCODER)
2040d1a0d267SMarcel Moolenaar return 1;
2041d1a0d267SMarcel Moolenaar return 0;
2042d1a0d267SMarcel Moolenaar }
2043d1a0d267SMarcel Moolenaar
2044d1a0d267SMarcel Moolenaar /* Simple name-value mapping */
2045d1a0d267SMarcel Moolenaar typedef struct xo_mapping_s {
2046f2b7bf8aSPhil Shafer xo_xff_flags_t xm_value; /* Flag value */
2047f2b7bf8aSPhil Shafer const char *xm_name; /* String name */
2048d1a0d267SMarcel Moolenaar } xo_mapping_t;
2049d1a0d267SMarcel Moolenaar
2050d1a0d267SMarcel Moolenaar static xo_xff_flags_t
xo_name_lookup(xo_mapping_t * map,const char * value,ssize_t len)20518a6eceffSPhil Shafer xo_name_lookup (xo_mapping_t *map, const char *value, ssize_t len)
2052d1a0d267SMarcel Moolenaar {
2053d1a0d267SMarcel Moolenaar if (len == 0)
2054d1a0d267SMarcel Moolenaar return 0;
2055d1a0d267SMarcel Moolenaar
2056d1a0d267SMarcel Moolenaar if (len < 0)
2057d1a0d267SMarcel Moolenaar len = strlen(value);
2058d1a0d267SMarcel Moolenaar
2059d1a0d267SMarcel Moolenaar while (isspace((int) *value)) {
2060d1a0d267SMarcel Moolenaar value += 1;
2061d1a0d267SMarcel Moolenaar len -= 1;
2062d1a0d267SMarcel Moolenaar }
2063d1a0d267SMarcel Moolenaar
2064d1a0d267SMarcel Moolenaar while (isspace((int) value[len]))
2065d1a0d267SMarcel Moolenaar len -= 1;
2066d1a0d267SMarcel Moolenaar
2067d1a0d267SMarcel Moolenaar if (*value == '\0')
2068d1a0d267SMarcel Moolenaar return 0;
2069d1a0d267SMarcel Moolenaar
2070d1a0d267SMarcel Moolenaar for ( ; map->xm_name; map++)
2071d1a0d267SMarcel Moolenaar if (strncmp(map->xm_name, value, len) == 0)
2072d1a0d267SMarcel Moolenaar return map->xm_value;
2073d1a0d267SMarcel Moolenaar
2074d1a0d267SMarcel Moolenaar return 0;
2075d1a0d267SMarcel Moolenaar }
2076d1a0d267SMarcel Moolenaar
2077d1a0d267SMarcel Moolenaar #ifdef NOT_NEEDED_YET
2078d1a0d267SMarcel Moolenaar static const char *
xo_value_lookup(xo_mapping_t * map,xo_xff_flags_t value)2079d1a0d267SMarcel Moolenaar xo_value_lookup (xo_mapping_t *map, xo_xff_flags_t value)
2080d1a0d267SMarcel Moolenaar {
2081d1a0d267SMarcel Moolenaar if (value == 0)
2082d1a0d267SMarcel Moolenaar return NULL;
2083d1a0d267SMarcel Moolenaar
2084d1a0d267SMarcel Moolenaar for ( ; map->xm_name; map++)
2085d1a0d267SMarcel Moolenaar if (map->xm_value == value)
2086d1a0d267SMarcel Moolenaar return map->xm_name;
2087d1a0d267SMarcel Moolenaar
2088d1a0d267SMarcel Moolenaar return NULL;
2089d1a0d267SMarcel Moolenaar }
2090d1a0d267SMarcel Moolenaar #endif /* NOT_NEEDED_YET */
2091d1a0d267SMarcel Moolenaar
2092d1a0d267SMarcel Moolenaar static xo_mapping_t xo_xof_names[] = {
2093d1a0d267SMarcel Moolenaar { XOF_COLOR_ALLOWED, "color" },
2094f2b7bf8aSPhil Shafer { XOF_COLOR, "color-force" },
2095d1a0d267SMarcel Moolenaar { XOF_COLUMNS, "columns" },
2096d1a0d267SMarcel Moolenaar { XOF_DTRT, "dtrt" },
2097d1a0d267SMarcel Moolenaar { XOF_FLUSH, "flush" },
20988a6eceffSPhil Shafer { XOF_FLUSH_LINE, "flush-line" },
2099d1a0d267SMarcel Moolenaar { XOF_IGNORE_CLOSE, "ignore-close" },
2100d1a0d267SMarcel Moolenaar { XOF_INFO, "info" },
2101d1a0d267SMarcel Moolenaar { XOF_KEYS, "keys" },
2102d1a0d267SMarcel Moolenaar { XOF_LOG_GETTEXT, "log-gettext" },
2103d1a0d267SMarcel Moolenaar { XOF_LOG_SYSLOG, "log-syslog" },
2104d1a0d267SMarcel Moolenaar { XOF_NO_HUMANIZE, "no-humanize" },
2105d1a0d267SMarcel Moolenaar { XOF_NO_LOCALE, "no-locale" },
210642ff34c3SPhil Shafer { XOF_RETAIN_NONE, "no-retain" },
2107d1a0d267SMarcel Moolenaar { XOF_NO_TOP, "no-top" },
2108d1a0d267SMarcel Moolenaar { XOF_NOT_FIRST, "not-first" },
2109d1a0d267SMarcel Moolenaar { XOF_PRETTY, "pretty" },
211042ff34c3SPhil Shafer { XOF_RETAIN_ALL, "retain" },
2111d1a0d267SMarcel Moolenaar { XOF_UNDERSCORES, "underscores" },
2112d1a0d267SMarcel Moolenaar { XOF_UNITS, "units" },
2113d1a0d267SMarcel Moolenaar { XOF_WARN, "warn" },
2114d1a0d267SMarcel Moolenaar { XOF_WARN_XML, "warn-xml" },
2115d1a0d267SMarcel Moolenaar { XOF_XPATH, "xpath" },
2116d1a0d267SMarcel Moolenaar { 0, NULL }
2117d1a0d267SMarcel Moolenaar };
2118d1a0d267SMarcel Moolenaar
2119f2b7bf8aSPhil Shafer /* Options available via the environment variable ($LIBXO_OPTIONS) */
2120f2b7bf8aSPhil Shafer static xo_mapping_t xo_xof_simple_names[] = {
2121f2b7bf8aSPhil Shafer { XOF_COLOR_ALLOWED, "color" },
2122f2b7bf8aSPhil Shafer { XOF_FLUSH, "flush" },
2123f2b7bf8aSPhil Shafer { XOF_FLUSH_LINE, "flush-line" },
2124f2b7bf8aSPhil Shafer { XOF_NO_HUMANIZE, "no-humanize" },
2125f2b7bf8aSPhil Shafer { XOF_NO_LOCALE, "no-locale" },
2126f2b7bf8aSPhil Shafer { XOF_RETAIN_NONE, "no-retain" },
2127f2b7bf8aSPhil Shafer { XOF_PRETTY, "pretty" },
2128f2b7bf8aSPhil Shafer { XOF_RETAIN_ALL, "retain" },
2129f2b7bf8aSPhil Shafer { XOF_UNDERSCORES, "underscores" },
2130f2b7bf8aSPhil Shafer { XOF_WARN, "warn" },
2131f2b7bf8aSPhil Shafer { 0, NULL }
2132f2b7bf8aSPhil Shafer };
2133f2b7bf8aSPhil Shafer
2134d1a0d267SMarcel Moolenaar /*
213531337658SMarcel Moolenaar * Convert string name to XOF_* flag value.
213631337658SMarcel Moolenaar * Not all are useful. Or safe. Or sane.
213731337658SMarcel Moolenaar */
213831337658SMarcel Moolenaar static unsigned
xo_name_to_flag(const char * name)213931337658SMarcel Moolenaar xo_name_to_flag (const char *name)
214031337658SMarcel Moolenaar {
2141d1a0d267SMarcel Moolenaar return (unsigned) xo_name_lookup(xo_xof_names, name, -1);
214231337658SMarcel Moolenaar }
214331337658SMarcel Moolenaar
2144f2b7bf8aSPhil Shafer /**
2145f2b7bf8aSPhil Shafer * Set the style of an libxo handle based on a string name
2146f2b7bf8aSPhil Shafer *
2147f2b7bf8aSPhil Shafer * @param xop XO handle
2148f2b7bf8aSPhil Shafer * @param name String value of name
2149f2b7bf8aSPhil Shafer * @return 0 on success, non-zero on failure
2150f2b7bf8aSPhil Shafer */
215131337658SMarcel Moolenaar int
xo_set_style_name(xo_handle_t * xop,const char * name)215231337658SMarcel Moolenaar xo_set_style_name (xo_handle_t *xop, const char *name)
215331337658SMarcel Moolenaar {
215431337658SMarcel Moolenaar if (name == NULL)
215531337658SMarcel Moolenaar return -1;
215631337658SMarcel Moolenaar
215731337658SMarcel Moolenaar int style = xo_name_to_style(name);
21582f784130SPhil Shafer
215931337658SMarcel Moolenaar if (style < 0)
216031337658SMarcel Moolenaar return -1;
216131337658SMarcel Moolenaar
216231337658SMarcel Moolenaar xo_set_style(xop, style);
216331337658SMarcel Moolenaar return 0;
216431337658SMarcel Moolenaar }
216531337658SMarcel Moolenaar
216631337658SMarcel Moolenaar /*
2167f2b7bf8aSPhil Shafer * Fill in the color map, based on the input string; currently unimplemented
2168f2b7bf8aSPhil Shafer * Look for something like "colors=red/blue+green/yellow" as fg/bg pairs.
2169f2b7bf8aSPhil Shafer */
2170f2b7bf8aSPhil Shafer static void
xo_set_color_map(xo_handle_t * xop,char * value)2171f2b7bf8aSPhil Shafer xo_set_color_map (xo_handle_t *xop, char *value)
2172f2b7bf8aSPhil Shafer {
2173406a584dSPhil Shafer if (xo_text_only())
2174f2b7bf8aSPhil Shafer return;
2175f2b7bf8aSPhil Shafer
2176f2b7bf8aSPhil Shafer char *cp, *ep, *vp, *np;
2177f2b7bf8aSPhil Shafer ssize_t len = value ? strlen(value) + 1 : 0;
2178f2b7bf8aSPhil Shafer int num = 1, fg, bg;
2179f2b7bf8aSPhil Shafer
2180f2b7bf8aSPhil Shafer for (cp = value, ep = cp + len - 1; cp && *cp && cp < ep; cp = np) {
2181f2b7bf8aSPhil Shafer np = strchr(cp, '+');
2182f2b7bf8aSPhil Shafer if (np)
2183f2b7bf8aSPhil Shafer *np++ = '\0';
2184f2b7bf8aSPhil Shafer
2185f2b7bf8aSPhil Shafer vp = strchr(cp, '/');
2186f2b7bf8aSPhil Shafer if (vp)
2187f2b7bf8aSPhil Shafer *vp++ = '\0';
2188f2b7bf8aSPhil Shafer
2189f2b7bf8aSPhil Shafer fg = *cp ? xo_color_find(cp) : -1;
2190f2b7bf8aSPhil Shafer bg = (vp && *vp) ? xo_color_find(vp) : -1;
2191f2b7bf8aSPhil Shafer
2192406a584dSPhil Shafer #ifndef LIBXO_TEXT_ONLY
2193f2b7bf8aSPhil Shafer xop->xo_color_map_fg[num] = (fg < 0) ? num : fg;
2194f2b7bf8aSPhil Shafer xop->xo_color_map_bg[num] = (bg < 0) ? num : bg;
2195406a584dSPhil Shafer #endif /* LIBXO_TEXT_ONLY */
2196406a584dSPhil Shafer
2197f2b7bf8aSPhil Shafer if (++num > XO_NUM_COLORS)
2198f2b7bf8aSPhil Shafer break;
2199f2b7bf8aSPhil Shafer }
2200f2b7bf8aSPhil Shafer
2201f2b7bf8aSPhil Shafer /* If no color initialization happened, then we don't need the map */
220276afb20cSPhil Shafer if (num > 1)
2203f2b7bf8aSPhil Shafer XOF_SET(xop, XOF_COLOR_MAP);
2204f2b7bf8aSPhil Shafer else
2205f2b7bf8aSPhil Shafer XOF_CLEAR(xop, XOF_COLOR_MAP);
2206f2b7bf8aSPhil Shafer
2207406a584dSPhil Shafer #ifndef LIBXO_TEXT_ONLY
2208f2b7bf8aSPhil Shafer /* Fill in the rest of the colors with the defaults */
2209f2b7bf8aSPhil Shafer for ( ; num < XO_NUM_COLORS; num++)
2210f2b7bf8aSPhil Shafer xop->xo_color_map_fg[num] = xop->xo_color_map_bg[num] = num;
2211406a584dSPhil Shafer #endif /* LIBXO_TEXT_ONLY */
2212f2b7bf8aSPhil Shafer }
2213f2b7bf8aSPhil Shafer
2214f2b7bf8aSPhil Shafer static int
xo_set_options_simple(xo_handle_t * xop,const char * input)2215f2b7bf8aSPhil Shafer xo_set_options_simple (xo_handle_t *xop, const char *input)
2216f2b7bf8aSPhil Shafer {
2217f2b7bf8aSPhil Shafer xo_xof_flags_t new_flag;
2218f2b7bf8aSPhil Shafer char *cp, *ep, *vp, *np, *bp;
2219f2b7bf8aSPhil Shafer ssize_t len = strlen(input) + 1;
2220f2b7bf8aSPhil Shafer
2221f2b7bf8aSPhil Shafer bp = alloca(len);
2222f2b7bf8aSPhil Shafer memcpy(bp, input, len);
2223f2b7bf8aSPhil Shafer
2224f2b7bf8aSPhil Shafer for (cp = bp, ep = cp + len - 1; cp && cp < ep; cp = np) {
2225f2b7bf8aSPhil Shafer np = strchr(cp, ',');
2226f2b7bf8aSPhil Shafer if (np)
2227f2b7bf8aSPhil Shafer *np++ = '\0';
2228f2b7bf8aSPhil Shafer
2229f2b7bf8aSPhil Shafer vp = strchr(cp, '=');
2230f2b7bf8aSPhil Shafer if (vp)
2231f2b7bf8aSPhil Shafer *vp++ = '\0';
2232f2b7bf8aSPhil Shafer
223376afb20cSPhil Shafer if (xo_streq("colors", cp)) {
2234f2b7bf8aSPhil Shafer xo_set_color_map(xop, vp);
2235f2b7bf8aSPhil Shafer continue;
2236f2b7bf8aSPhil Shafer }
2237f2b7bf8aSPhil Shafer
2238f2b7bf8aSPhil Shafer new_flag = xo_name_lookup(xo_xof_simple_names, cp, -1);
2239f2b7bf8aSPhil Shafer if (new_flag != 0) {
2240f2b7bf8aSPhil Shafer XOF_SET(xop, new_flag);
224176afb20cSPhil Shafer } else if (xo_streq(cp, "no-color")) {
2242f2b7bf8aSPhil Shafer XOF_CLEAR(xop, XOF_COLOR_ALLOWED);
2243f2b7bf8aSPhil Shafer } else {
2244f2b7bf8aSPhil Shafer xo_failure(xop, "unknown simple option: %s", cp);
2245f2b7bf8aSPhil Shafer return -1;
2246f2b7bf8aSPhil Shafer }
2247f2b7bf8aSPhil Shafer }
2248f2b7bf8aSPhil Shafer
2249f2b7bf8aSPhil Shafer return 0;
2250f2b7bf8aSPhil Shafer }
2251f2b7bf8aSPhil Shafer
2252f2b7bf8aSPhil Shafer /**
225331337658SMarcel Moolenaar * Set the options for a handle using a string of options
225431337658SMarcel Moolenaar * passed in. The input is a comma-separated set of names
225531337658SMarcel Moolenaar * and optional values: "xml,pretty,indent=4"
2256f2b7bf8aSPhil Shafer *
2257f2b7bf8aSPhil Shafer * @param xop XO handle
2258f2b7bf8aSPhil Shafer * @param input Comma-separated set of option values
2259f2b7bf8aSPhil Shafer * @return 0 on success, non-zero on failure
226031337658SMarcel Moolenaar */
226131337658SMarcel Moolenaar int
xo_set_options(xo_handle_t * xop,const char * input)226231337658SMarcel Moolenaar xo_set_options (xo_handle_t *xop, const char *input)
226331337658SMarcel Moolenaar {
226431337658SMarcel Moolenaar char *cp, *ep, *vp, *np, *bp;
22658a6eceffSPhil Shafer int style = -1, new_style, rc = 0;
22668a6eceffSPhil Shafer ssize_t len;
226731337658SMarcel Moolenaar xo_xof_flags_t new_flag;
226831337658SMarcel Moolenaar
226931337658SMarcel Moolenaar if (input == NULL)
227031337658SMarcel Moolenaar return 0;
227131337658SMarcel Moolenaar
227231337658SMarcel Moolenaar xop = xo_default(xop);
227331337658SMarcel Moolenaar
2274788ca347SMarcel Moolenaar #ifdef LIBXO_COLOR_ON_BY_DEFAULT
2275788ca347SMarcel Moolenaar /* If the installer used --enable-color-on-by-default, then we allow it */
2276d1a0d267SMarcel Moolenaar XOF_SET(xop, XOF_COLOR_ALLOWED);
2277788ca347SMarcel Moolenaar #endif /* LIBXO_COLOR_ON_BY_DEFAULT */
2278788ca347SMarcel Moolenaar
227931337658SMarcel Moolenaar /*
228031337658SMarcel Moolenaar * We support a simpler, old-school style of giving option
228131337658SMarcel Moolenaar * also, using a single character for each option. It's
228231337658SMarcel Moolenaar * ideal for lazy people, such as myself.
228331337658SMarcel Moolenaar */
228431337658SMarcel Moolenaar if (*input == ':') {
22858a6eceffSPhil Shafer ssize_t sz;
228631337658SMarcel Moolenaar
228731337658SMarcel Moolenaar for (input++ ; *input; input++) {
228831337658SMarcel Moolenaar switch (*input) {
2289788ca347SMarcel Moolenaar case 'c':
2290d1a0d267SMarcel Moolenaar XOF_SET(xop, XOF_COLOR_ALLOWED);
2291788ca347SMarcel Moolenaar break;
2292788ca347SMarcel Moolenaar
229331337658SMarcel Moolenaar case 'f':
2294d1a0d267SMarcel Moolenaar XOF_SET(xop, XOF_FLUSH);
229531337658SMarcel Moolenaar break;
229631337658SMarcel Moolenaar
2297545ddfbeSMarcel Moolenaar case 'F':
2298d1a0d267SMarcel Moolenaar XOF_SET(xop, XOF_FLUSH_LINE);
2299d1a0d267SMarcel Moolenaar break;
2300d1a0d267SMarcel Moolenaar
2301d1a0d267SMarcel Moolenaar case 'g':
2302d1a0d267SMarcel Moolenaar XOF_SET(xop, XOF_LOG_GETTEXT);
2303545ddfbeSMarcel Moolenaar break;
2304545ddfbeSMarcel Moolenaar
230531337658SMarcel Moolenaar case 'H':
230631337658SMarcel Moolenaar xop->xo_style = XO_STYLE_HTML;
230731337658SMarcel Moolenaar break;
230831337658SMarcel Moolenaar
230931337658SMarcel Moolenaar case 'I':
2310d1a0d267SMarcel Moolenaar XOF_SET(xop, XOF_INFO);
231131337658SMarcel Moolenaar break;
231231337658SMarcel Moolenaar
231331337658SMarcel Moolenaar case 'i':
231431337658SMarcel Moolenaar sz = strspn(input + 1, "0123456789");
231531337658SMarcel Moolenaar if (sz > 0) {
231631337658SMarcel Moolenaar xop->xo_indent_by = atoi(input + 1);
231731337658SMarcel Moolenaar input += sz - 1; /* Skip value */
231831337658SMarcel Moolenaar }
231931337658SMarcel Moolenaar break;
232031337658SMarcel Moolenaar
232131337658SMarcel Moolenaar case 'J':
232231337658SMarcel Moolenaar xop->xo_style = XO_STYLE_JSON;
232331337658SMarcel Moolenaar break;
232431337658SMarcel Moolenaar
2325d1a0d267SMarcel Moolenaar case 'k':
2326d1a0d267SMarcel Moolenaar XOF_SET(xop, XOF_KEYS);
2327d1a0d267SMarcel Moolenaar break;
2328d1a0d267SMarcel Moolenaar
2329d1a0d267SMarcel Moolenaar case 'n':
2330d1a0d267SMarcel Moolenaar XOF_SET(xop, XOF_NO_HUMANIZE);
2331d1a0d267SMarcel Moolenaar break;
2332d1a0d267SMarcel Moolenaar
233331337658SMarcel Moolenaar case 'P':
2334d1a0d267SMarcel Moolenaar XOF_SET(xop, XOF_PRETTY);
233531337658SMarcel Moolenaar break;
233631337658SMarcel Moolenaar
233731337658SMarcel Moolenaar case 'T':
233831337658SMarcel Moolenaar xop->xo_style = XO_STYLE_TEXT;
233931337658SMarcel Moolenaar break;
234031337658SMarcel Moolenaar
234131337658SMarcel Moolenaar case 'U':
2342d1a0d267SMarcel Moolenaar XOF_SET(xop, XOF_UNITS);
234331337658SMarcel Moolenaar break;
234431337658SMarcel Moolenaar
234531337658SMarcel Moolenaar case 'u':
2346d1a0d267SMarcel Moolenaar XOF_SET(xop, XOF_UNDERSCORES);
234731337658SMarcel Moolenaar break;
234831337658SMarcel Moolenaar
234931337658SMarcel Moolenaar case 'W':
2350d1a0d267SMarcel Moolenaar XOF_SET(xop, XOF_WARN);
235131337658SMarcel Moolenaar break;
235231337658SMarcel Moolenaar
235331337658SMarcel Moolenaar case 'X':
235431337658SMarcel Moolenaar xop->xo_style = XO_STYLE_XML;
235531337658SMarcel Moolenaar break;
235631337658SMarcel Moolenaar
235731337658SMarcel Moolenaar case 'x':
2358d1a0d267SMarcel Moolenaar XOF_SET(xop, XOF_XPATH);
235931337658SMarcel Moolenaar break;
236031337658SMarcel Moolenaar }
236131337658SMarcel Moolenaar }
236231337658SMarcel Moolenaar return 0;
236331337658SMarcel Moolenaar }
236431337658SMarcel Moolenaar
236531337658SMarcel Moolenaar len = strlen(input) + 1;
236631337658SMarcel Moolenaar bp = alloca(len);
236731337658SMarcel Moolenaar memcpy(bp, input, len);
236831337658SMarcel Moolenaar
236931337658SMarcel Moolenaar for (cp = bp, ep = cp + len - 1; cp && cp < ep; cp = np) {
237031337658SMarcel Moolenaar np = strchr(cp, ',');
237131337658SMarcel Moolenaar if (np)
237231337658SMarcel Moolenaar *np++ = '\0';
237331337658SMarcel Moolenaar
23745c5819b2SPhil Shafer /*
23755c5819b2SPhil Shafer * "@foo" is a shorthand for "encoder=foo". This is driven
23765c5819b2SPhil Shafer * chiefly by a desire to make pluggable encoders not appear
23775c5819b2SPhil Shafer * so distinct from built-in encoders.
23785c5819b2SPhil Shafer */
23795c5819b2SPhil Shafer if (*cp == '@') {
23805c5819b2SPhil Shafer vp = cp + 1;
23815c5819b2SPhil Shafer
23825c5819b2SPhil Shafer if (*vp == '\0')
23835c5819b2SPhil Shafer xo_failure(xop, "missing value for encoder option");
23845c5819b2SPhil Shafer else {
23855c5819b2SPhil Shafer rc = xo_encoder_init(xop, vp);
23865c5819b2SPhil Shafer if (rc)
23875c5819b2SPhil Shafer xo_warnx("error initializing encoder: %s", vp);
23885c5819b2SPhil Shafer }
23895c5819b2SPhil Shafer
23905c5819b2SPhil Shafer continue;
23915c5819b2SPhil Shafer }
23925c5819b2SPhil Shafer
239331337658SMarcel Moolenaar vp = strchr(cp, '=');
239431337658SMarcel Moolenaar if (vp)
239531337658SMarcel Moolenaar *vp++ = '\0';
239631337658SMarcel Moolenaar
239776afb20cSPhil Shafer if (xo_streq("colors", cp)) {
2398f2b7bf8aSPhil Shafer xo_set_color_map(xop, vp);
2399788ca347SMarcel Moolenaar continue;
2400788ca347SMarcel Moolenaar }
2401788ca347SMarcel Moolenaar
2402d1a0d267SMarcel Moolenaar /*
2403d1a0d267SMarcel Moolenaar * For options, we don't allow "encoder" since we want to
2404d1a0d267SMarcel Moolenaar * handle it explicitly below as "encoder=xxx".
2405d1a0d267SMarcel Moolenaar */
240631337658SMarcel Moolenaar new_style = xo_name_to_style(cp);
2407d1a0d267SMarcel Moolenaar if (new_style >= 0 && new_style != XO_STYLE_ENCODER) {
240831337658SMarcel Moolenaar if (style >= 0)
240931337658SMarcel Moolenaar xo_warnx("ignoring multiple styles: '%s'", cp);
241031337658SMarcel Moolenaar else
241131337658SMarcel Moolenaar style = new_style;
241231337658SMarcel Moolenaar } else {
241331337658SMarcel Moolenaar new_flag = xo_name_to_flag(cp);
241431337658SMarcel Moolenaar if (new_flag != 0)
2415d1a0d267SMarcel Moolenaar XOF_SET(xop, new_flag);
241676afb20cSPhil Shafer else if (xo_streq(cp, "no-color"))
2417d1a0d267SMarcel Moolenaar XOF_CLEAR(xop, XOF_COLOR_ALLOWED);
241876afb20cSPhil Shafer else if (xo_streq(cp, "indent")) {
2419d1a0d267SMarcel Moolenaar if (vp)
242031337658SMarcel Moolenaar xop->xo_indent_by = atoi(vp);
2421d1a0d267SMarcel Moolenaar else
2422d1a0d267SMarcel Moolenaar xo_failure(xop, "missing value for indent option");
242376afb20cSPhil Shafer } else if (xo_streq(cp, "encoder")) {
2424d1a0d267SMarcel Moolenaar if (vp == NULL)
2425d1a0d267SMarcel Moolenaar xo_failure(xop, "missing value for encoder option");
2426d1a0d267SMarcel Moolenaar else {
242776afb20cSPhil Shafer rc = xo_encoder_init(xop, vp);
242876afb20cSPhil Shafer if (rc)
242976afb20cSPhil Shafer xo_warnx("error initializing encoder: %s", vp);
2430d1a0d267SMarcel Moolenaar }
2431d1a0d267SMarcel Moolenaar
243231337658SMarcel Moolenaar } else {
2433d1a0d267SMarcel Moolenaar xo_warnx("unknown libxo option value: '%s'", cp);
243431337658SMarcel Moolenaar rc = -1;
243531337658SMarcel Moolenaar }
243631337658SMarcel Moolenaar }
243731337658SMarcel Moolenaar }
243831337658SMarcel Moolenaar
243931337658SMarcel Moolenaar if (style > 0)
244031337658SMarcel Moolenaar xop->xo_style= style;
244131337658SMarcel Moolenaar
244231337658SMarcel Moolenaar return rc;
244331337658SMarcel Moolenaar }
244431337658SMarcel Moolenaar
244531337658SMarcel Moolenaar /**
244631337658SMarcel Moolenaar * Set one or more flags for a given handle (or default if handle is NULL).
244731337658SMarcel Moolenaar * These flags will affect future output.
244831337658SMarcel Moolenaar *
2449f2b7bf8aSPhil Shafer * @param xop XO handle to alter (or NULL for default handle)
2450f2b7bf8aSPhil Shafer * @param flags Flags to be set (XOF_*)
245131337658SMarcel Moolenaar */
245231337658SMarcel Moolenaar void
xo_set_flags(xo_handle_t * xop,xo_xof_flags_t flags)245331337658SMarcel Moolenaar xo_set_flags (xo_handle_t *xop, xo_xof_flags_t flags)
245431337658SMarcel Moolenaar {
245531337658SMarcel Moolenaar xop = xo_default(xop);
245631337658SMarcel Moolenaar
2457d1a0d267SMarcel Moolenaar XOF_SET(xop, flags);
245831337658SMarcel Moolenaar }
245931337658SMarcel Moolenaar
2460f2b7bf8aSPhil Shafer /**
2461f2b7bf8aSPhil Shafer * Accessor to return the current set of flags for a handle
2462f2b7bf8aSPhil Shafer * @param xop XO handle
2463f2b7bf8aSPhil Shafer * @return Current set of flags
2464f2b7bf8aSPhil Shafer */
246531337658SMarcel Moolenaar xo_xof_flags_t
xo_get_flags(xo_handle_t * xop)246631337658SMarcel Moolenaar xo_get_flags (xo_handle_t *xop)
246731337658SMarcel Moolenaar {
246831337658SMarcel Moolenaar xop = xo_default(xop);
246931337658SMarcel Moolenaar
247031337658SMarcel Moolenaar return xop->xo_flags;
247131337658SMarcel Moolenaar }
247231337658SMarcel Moolenaar
2473f2b7bf8aSPhil Shafer /**
2474f2b7bf8aSPhil Shafer * strndup with a twist: len < 0 means len = strlen(str)
2475d1a0d267SMarcel Moolenaar */
2476d1a0d267SMarcel Moolenaar static char *
xo_strndup(const char * str,ssize_t len)24778a6eceffSPhil Shafer xo_strndup (const char *str, ssize_t len)
2478d1a0d267SMarcel Moolenaar {
2479d1a0d267SMarcel Moolenaar if (len < 0)
2480d1a0d267SMarcel Moolenaar len = strlen(str);
2481d1a0d267SMarcel Moolenaar
2482d1a0d267SMarcel Moolenaar char *cp = xo_realloc(NULL, len + 1);
2483d1a0d267SMarcel Moolenaar if (cp) {
2484d1a0d267SMarcel Moolenaar memcpy(cp, str, len);
2485d1a0d267SMarcel Moolenaar cp[len] = '\0';
2486d1a0d267SMarcel Moolenaar }
2487d1a0d267SMarcel Moolenaar
2488d1a0d267SMarcel Moolenaar return cp;
2489d1a0d267SMarcel Moolenaar }
2490d1a0d267SMarcel Moolenaar
249131337658SMarcel Moolenaar /**
249231337658SMarcel Moolenaar * Record a leading prefix for the XPath we generate. This allows the
249331337658SMarcel Moolenaar * generated data to be placed within an XML hierarchy but still have
249431337658SMarcel Moolenaar * accurate XPath expressions.
249531337658SMarcel Moolenaar *
2496f2b7bf8aSPhil Shafer * @param xop XO handle to alter (or NULL for default handle)
2497f2b7bf8aSPhil Shafer * @param path The XPath expression
249831337658SMarcel Moolenaar */
249931337658SMarcel Moolenaar void
xo_set_leading_xpath(xo_handle_t * xop,const char * path)250031337658SMarcel Moolenaar xo_set_leading_xpath (xo_handle_t *xop, const char *path)
250131337658SMarcel Moolenaar {
250231337658SMarcel Moolenaar xop = xo_default(xop);
250331337658SMarcel Moolenaar
250431337658SMarcel Moolenaar if (xop->xo_leading_xpath) {
250531337658SMarcel Moolenaar xo_free(xop->xo_leading_xpath);
250631337658SMarcel Moolenaar xop->xo_leading_xpath = NULL;
250731337658SMarcel Moolenaar }
250831337658SMarcel Moolenaar
250931337658SMarcel Moolenaar if (path == NULL)
251031337658SMarcel Moolenaar return;
251131337658SMarcel Moolenaar
2512d1a0d267SMarcel Moolenaar xop->xo_leading_xpath = xo_strndup(path, -1);
251331337658SMarcel Moolenaar }
251431337658SMarcel Moolenaar
251531337658SMarcel Moolenaar /**
251631337658SMarcel Moolenaar * Record the info data for a set of tags
251731337658SMarcel Moolenaar *
2518f2b7bf8aSPhil Shafer * @param xop XO handle to alter (or NULL for default handle)
2519f2b7bf8aSPhil Shafer * @param info Info data (xo_info_t) to be recorded (or NULL) (MUST BE SORTED)
2520f2b7bf8aSPhil Shafer * @pararm count Number of entries in info (or -1 to count them ourselves)
252131337658SMarcel Moolenaar */
252231337658SMarcel Moolenaar void
xo_set_info(xo_handle_t * xop,xo_info_t * infop,int count)252331337658SMarcel Moolenaar xo_set_info (xo_handle_t *xop, xo_info_t *infop, int count)
252431337658SMarcel Moolenaar {
252531337658SMarcel Moolenaar xop = xo_default(xop);
252631337658SMarcel Moolenaar
252731337658SMarcel Moolenaar if (count < 0 && infop) {
252831337658SMarcel Moolenaar xo_info_t *xip;
252931337658SMarcel Moolenaar
253031337658SMarcel Moolenaar for (xip = infop, count = 0; xip->xi_name; xip++, count++)
253131337658SMarcel Moolenaar continue;
253231337658SMarcel Moolenaar }
253331337658SMarcel Moolenaar
253431337658SMarcel Moolenaar xop->xo_info = infop;
253531337658SMarcel Moolenaar xop->xo_info_count = count;
253631337658SMarcel Moolenaar }
253731337658SMarcel Moolenaar
253831337658SMarcel Moolenaar /**
253931337658SMarcel Moolenaar * Set the formatter callback for a handle. The callback should
254031337658SMarcel Moolenaar * return a newly formatting contents of a formatting instruction,
254131337658SMarcel Moolenaar * meaning the bits inside the braces.
254231337658SMarcel Moolenaar */
254331337658SMarcel Moolenaar void
xo_set_formatter(xo_handle_t * xop,xo_formatter_t func,xo_checkpointer_t cfunc)254431337658SMarcel Moolenaar xo_set_formatter (xo_handle_t *xop, xo_formatter_t func,
254531337658SMarcel Moolenaar xo_checkpointer_t cfunc)
254631337658SMarcel Moolenaar {
254731337658SMarcel Moolenaar xop = xo_default(xop);
254831337658SMarcel Moolenaar
254931337658SMarcel Moolenaar xop->xo_formatter = func;
255031337658SMarcel Moolenaar xop->xo_checkpointer = cfunc;
255131337658SMarcel Moolenaar }
255231337658SMarcel Moolenaar
255331337658SMarcel Moolenaar /**
255431337658SMarcel Moolenaar * Clear one or more flags for a given handle (or default if handle is NULL).
255531337658SMarcel Moolenaar * These flags will affect future output.
255631337658SMarcel Moolenaar *
2557f2b7bf8aSPhil Shafer * @param xop XO handle to alter (or NULL for default handle)
2558f2b7bf8aSPhil Shafer * @param flags Flags to be cleared (XOF_*)
255931337658SMarcel Moolenaar */
256031337658SMarcel Moolenaar void
xo_clear_flags(xo_handle_t * xop,xo_xof_flags_t flags)256131337658SMarcel Moolenaar xo_clear_flags (xo_handle_t *xop, xo_xof_flags_t flags)
256231337658SMarcel Moolenaar {
256331337658SMarcel Moolenaar xop = xo_default(xop);
256431337658SMarcel Moolenaar
2565d1a0d267SMarcel Moolenaar XOF_CLEAR(xop, flags);
256631337658SMarcel Moolenaar }
256731337658SMarcel Moolenaar
2568545ddfbeSMarcel Moolenaar static const char *
xo_state_name(xo_state_t state)2569545ddfbeSMarcel Moolenaar xo_state_name (xo_state_t state)
2570545ddfbeSMarcel Moolenaar {
2571545ddfbeSMarcel Moolenaar static const char *names[] = {
2572545ddfbeSMarcel Moolenaar "init",
2573545ddfbeSMarcel Moolenaar "open_container",
2574545ddfbeSMarcel Moolenaar "close_container",
2575545ddfbeSMarcel Moolenaar "open_list",
2576545ddfbeSMarcel Moolenaar "close_list",
2577545ddfbeSMarcel Moolenaar "open_instance",
2578545ddfbeSMarcel Moolenaar "close_instance",
2579545ddfbeSMarcel Moolenaar "open_leaf_list",
2580545ddfbeSMarcel Moolenaar "close_leaf_list",
2581545ddfbeSMarcel Moolenaar "discarding",
2582545ddfbeSMarcel Moolenaar "marker",
2583545ddfbeSMarcel Moolenaar "emit",
2584545ddfbeSMarcel Moolenaar "emit_leaf_list",
2585545ddfbeSMarcel Moolenaar "finish",
2586545ddfbeSMarcel Moolenaar NULL
2587545ddfbeSMarcel Moolenaar };
2588545ddfbeSMarcel Moolenaar
2589545ddfbeSMarcel Moolenaar if (state < (sizeof(names) / sizeof(names[0])))
2590545ddfbeSMarcel Moolenaar return names[state];
2591545ddfbeSMarcel Moolenaar
2592545ddfbeSMarcel Moolenaar return "unknown";
2593545ddfbeSMarcel Moolenaar }
2594545ddfbeSMarcel Moolenaar
259531337658SMarcel Moolenaar static void
xo_line_ensure_open(xo_handle_t * xop,xo_xff_flags_t flags UNUSED)259631337658SMarcel Moolenaar xo_line_ensure_open (xo_handle_t *xop, xo_xff_flags_t flags UNUSED)
259731337658SMarcel Moolenaar {
259831337658SMarcel Moolenaar static char div_open[] = "<div class=\"line\">";
259931337658SMarcel Moolenaar static char div_open_blank[] = "<div class=\"blank-line\">";
260031337658SMarcel Moolenaar
2601406a584dSPhil Shafer if (XOF_ISSET(xop, XOF_CONTINUATION)) {
2602406a584dSPhil Shafer XOF_CLEAR(xop, XOF_CONTINUATION);
2603406a584dSPhil Shafer XOIF_SET(xop, XOIF_DIV_OPEN);
2604406a584dSPhil Shafer return;
2605406a584dSPhil Shafer }
2606406a584dSPhil Shafer
2607d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_DIV_OPEN))
260831337658SMarcel Moolenaar return;
260931337658SMarcel Moolenaar
2610788ca347SMarcel Moolenaar if (xo_style(xop) != XO_STYLE_HTML)
261131337658SMarcel Moolenaar return;
261231337658SMarcel Moolenaar
2613d1a0d267SMarcel Moolenaar XOIF_SET(xop, XOIF_DIV_OPEN);
261431337658SMarcel Moolenaar if (flags & XFF_BLANK_LINE)
261531337658SMarcel Moolenaar xo_data_append(xop, div_open_blank, sizeof(div_open_blank) - 1);
261631337658SMarcel Moolenaar else
261731337658SMarcel Moolenaar xo_data_append(xop, div_open, sizeof(div_open) - 1);
261831337658SMarcel Moolenaar
2619d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_PRETTY))
262031337658SMarcel Moolenaar xo_data_append(xop, "\n", 1);
262131337658SMarcel Moolenaar }
262231337658SMarcel Moolenaar
262331337658SMarcel Moolenaar static void
xo_line_close(xo_handle_t * xop)262431337658SMarcel Moolenaar xo_line_close (xo_handle_t *xop)
262531337658SMarcel Moolenaar {
262631337658SMarcel Moolenaar static char div_close[] = "</div>";
262731337658SMarcel Moolenaar
2628788ca347SMarcel Moolenaar switch (xo_style(xop)) {
262931337658SMarcel Moolenaar case XO_STYLE_HTML:
2630d1a0d267SMarcel Moolenaar if (!XOIF_ISSET(xop, XOIF_DIV_OPEN))
263131337658SMarcel Moolenaar xo_line_ensure_open(xop, 0);
263231337658SMarcel Moolenaar
2633d1a0d267SMarcel Moolenaar XOIF_CLEAR(xop, XOIF_DIV_OPEN);
263431337658SMarcel Moolenaar xo_data_append(xop, div_close, sizeof(div_close) - 1);
263531337658SMarcel Moolenaar
2636d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_PRETTY))
263731337658SMarcel Moolenaar xo_data_append(xop, "\n", 1);
263831337658SMarcel Moolenaar break;
263931337658SMarcel Moolenaar
264031337658SMarcel Moolenaar case XO_STYLE_TEXT:
264131337658SMarcel Moolenaar xo_data_append(xop, "\n", 1);
264231337658SMarcel Moolenaar break;
264331337658SMarcel Moolenaar }
264431337658SMarcel Moolenaar }
264531337658SMarcel Moolenaar
264631337658SMarcel Moolenaar static int
xo_info_compare(const void * key,const void * data)264731337658SMarcel Moolenaar xo_info_compare (const void *key, const void *data)
264831337658SMarcel Moolenaar {
264931337658SMarcel Moolenaar const char *name = key;
265031337658SMarcel Moolenaar const xo_info_t *xip = data;
265131337658SMarcel Moolenaar
265231337658SMarcel Moolenaar return strcmp(name, xip->xi_name);
265331337658SMarcel Moolenaar }
265431337658SMarcel Moolenaar
265531337658SMarcel Moolenaar
265631337658SMarcel Moolenaar static xo_info_t *
xo_info_find(xo_handle_t * xop,const char * name,ssize_t nlen)26578a6eceffSPhil Shafer xo_info_find (xo_handle_t *xop, const char *name, ssize_t nlen)
265831337658SMarcel Moolenaar {
265931337658SMarcel Moolenaar xo_info_t *xip;
266031337658SMarcel Moolenaar char *cp = alloca(nlen + 1); /* Need local copy for NUL termination */
266131337658SMarcel Moolenaar
266231337658SMarcel Moolenaar memcpy(cp, name, nlen);
266331337658SMarcel Moolenaar cp[nlen] = '\0';
266431337658SMarcel Moolenaar
266531337658SMarcel Moolenaar xip = bsearch(cp, xop->xo_info, xop->xo_info_count,
266631337658SMarcel Moolenaar sizeof(xop->xo_info[0]), xo_info_compare);
266731337658SMarcel Moolenaar return xip;
266831337658SMarcel Moolenaar }
266931337658SMarcel Moolenaar
267031337658SMarcel Moolenaar #define CONVERT(_have, _need) (((_have) << 8) | (_need))
267131337658SMarcel Moolenaar
267231337658SMarcel Moolenaar /*
267331337658SMarcel Moolenaar * Check to see that the conversion is safe and sane.
267431337658SMarcel Moolenaar */
267531337658SMarcel Moolenaar static int
xo_check_conversion(xo_handle_t * xop,int have_enc,int need_enc)267631337658SMarcel Moolenaar xo_check_conversion (xo_handle_t *xop, int have_enc, int need_enc)
267731337658SMarcel Moolenaar {
267831337658SMarcel Moolenaar switch (CONVERT(have_enc, need_enc)) {
267931337658SMarcel Moolenaar case CONVERT(XF_ENC_UTF8, XF_ENC_UTF8):
268031337658SMarcel Moolenaar case CONVERT(XF_ENC_UTF8, XF_ENC_LOCALE):
268131337658SMarcel Moolenaar case CONVERT(XF_ENC_WIDE, XF_ENC_UTF8):
268231337658SMarcel Moolenaar case CONVERT(XF_ENC_WIDE, XF_ENC_LOCALE):
268331337658SMarcel Moolenaar case CONVERT(XF_ENC_LOCALE, XF_ENC_LOCALE):
268431337658SMarcel Moolenaar case CONVERT(XF_ENC_LOCALE, XF_ENC_UTF8):
268531337658SMarcel Moolenaar return 0;
268631337658SMarcel Moolenaar
268731337658SMarcel Moolenaar default:
268831337658SMarcel Moolenaar xo_failure(xop, "invalid conversion (%c:%c)", have_enc, need_enc);
268931337658SMarcel Moolenaar return 1;
269031337658SMarcel Moolenaar }
269131337658SMarcel Moolenaar }
269231337658SMarcel Moolenaar
269331337658SMarcel Moolenaar static int
xo_format_string_direct(xo_handle_t * xop,xo_buffer_t * xbp,xo_xff_flags_t flags,const wchar_t * wcp,const char * cp,ssize_t len,int max,int need_enc,int have_enc)269431337658SMarcel Moolenaar xo_format_string_direct (xo_handle_t *xop, xo_buffer_t *xbp,
269531337658SMarcel Moolenaar xo_xff_flags_t flags,
26968a6eceffSPhil Shafer const wchar_t *wcp, const char *cp,
26978a6eceffSPhil Shafer ssize_t len, int max,
269831337658SMarcel Moolenaar int need_enc, int have_enc)
269931337658SMarcel Moolenaar {
270031337658SMarcel Moolenaar int cols = 0;
2701c600d307SMarcel Moolenaar wchar_t wc = 0;
27028a6eceffSPhil Shafer ssize_t ilen, olen;
27038a6eceffSPhil Shafer ssize_t width;
27048a6eceffSPhil Shafer int attr = XOF_BIT_ISSET(flags, XFF_ATTR);
270531337658SMarcel Moolenaar const char *sp;
270631337658SMarcel Moolenaar
270731337658SMarcel Moolenaar if (len > 0 && !xo_buf_has_room(xbp, len))
270831337658SMarcel Moolenaar return 0;
270931337658SMarcel Moolenaar
271031337658SMarcel Moolenaar for (;;) {
271131337658SMarcel Moolenaar if (len == 0)
271231337658SMarcel Moolenaar break;
271331337658SMarcel Moolenaar
271431337658SMarcel Moolenaar if (cp) {
271531337658SMarcel Moolenaar if (*cp == '\0')
271631337658SMarcel Moolenaar break;
271731337658SMarcel Moolenaar if ((flags & XFF_UNESCAPE) && (*cp == '\\' || *cp == '%')) {
271831337658SMarcel Moolenaar cp += 1;
271931337658SMarcel Moolenaar len -= 1;
2720264104f2SPhil Shafer if (len == 0 || *cp == '\0')
2721264104f2SPhil Shafer break;
272231337658SMarcel Moolenaar }
272331337658SMarcel Moolenaar }
272431337658SMarcel Moolenaar
272531337658SMarcel Moolenaar if (wcp && *wcp == L'\0')
272631337658SMarcel Moolenaar break;
272731337658SMarcel Moolenaar
272831337658SMarcel Moolenaar ilen = 0;
272931337658SMarcel Moolenaar
273031337658SMarcel Moolenaar switch (have_enc) {
273131337658SMarcel Moolenaar case XF_ENC_WIDE: /* Wide character */
273231337658SMarcel Moolenaar wc = *wcp++;
273331337658SMarcel Moolenaar ilen = 1;
273431337658SMarcel Moolenaar break;
273531337658SMarcel Moolenaar
273631337658SMarcel Moolenaar case XF_ENC_UTF8: /* UTF-8 */
273731337658SMarcel Moolenaar ilen = xo_utf8_to_wc_len(cp);
273831337658SMarcel Moolenaar if (ilen < 0) {
273931337658SMarcel Moolenaar xo_failure(xop, "invalid UTF-8 character: %02hhx", *cp);
2740d1a0d267SMarcel Moolenaar return -1; /* Can't continue; we can't find the end */
274131337658SMarcel Moolenaar }
274231337658SMarcel Moolenaar
274331337658SMarcel Moolenaar if (len > 0 && len < ilen) {
274431337658SMarcel Moolenaar len = 0; /* Break out of the loop */
274531337658SMarcel Moolenaar continue;
274631337658SMarcel Moolenaar }
274731337658SMarcel Moolenaar
274831337658SMarcel Moolenaar wc = xo_utf8_char(cp, ilen);
274931337658SMarcel Moolenaar if (wc == (wchar_t) -1) {
275031337658SMarcel Moolenaar xo_failure(xop, "invalid UTF-8 character: %02hhx/%d",
275131337658SMarcel Moolenaar *cp, ilen);
2752d1a0d267SMarcel Moolenaar return -1; /* Can't continue; we can't find the end */
275331337658SMarcel Moolenaar }
275431337658SMarcel Moolenaar cp += ilen;
275531337658SMarcel Moolenaar break;
275631337658SMarcel Moolenaar
275731337658SMarcel Moolenaar case XF_ENC_LOCALE: /* Native locale */
275831337658SMarcel Moolenaar ilen = (len > 0) ? len : MB_LEN_MAX;
275931337658SMarcel Moolenaar ilen = mbrtowc(&wc, cp, ilen, &xop->xo_mbstate);
276031337658SMarcel Moolenaar if (ilen < 0) { /* Invalid data; skip */
276131337658SMarcel Moolenaar xo_failure(xop, "invalid mbs char: %02hhx", *cp);
2762dbf26257SAlexander Kabaev wc = L'?';
2763dbf26257SAlexander Kabaev ilen = 1;
276431337658SMarcel Moolenaar }
2765d1a0d267SMarcel Moolenaar
276631337658SMarcel Moolenaar if (ilen == 0) { /* Hit a wide NUL character */
276731337658SMarcel Moolenaar len = 0;
276831337658SMarcel Moolenaar continue;
276931337658SMarcel Moolenaar }
277031337658SMarcel Moolenaar
277131337658SMarcel Moolenaar cp += ilen;
277231337658SMarcel Moolenaar break;
277331337658SMarcel Moolenaar }
277431337658SMarcel Moolenaar
277531337658SMarcel Moolenaar /* Reduce len, but not below zero */
277631337658SMarcel Moolenaar if (len > 0) {
277731337658SMarcel Moolenaar len -= ilen;
277831337658SMarcel Moolenaar if (len < 0)
277931337658SMarcel Moolenaar len = 0;
278031337658SMarcel Moolenaar }
278131337658SMarcel Moolenaar
278231337658SMarcel Moolenaar /*
278331337658SMarcel Moolenaar * Find the width-in-columns of this character, which must be done
278431337658SMarcel Moolenaar * in wide characters, since we lack a mbswidth() function. If
278531337658SMarcel Moolenaar * it doesn't fit
278631337658SMarcel Moolenaar */
2787d1a0d267SMarcel Moolenaar width = xo_wcwidth(wc);
278831337658SMarcel Moolenaar if (width < 0)
278931337658SMarcel Moolenaar width = iswcntrl(wc) ? 0 : 1;
279031337658SMarcel Moolenaar
2791788ca347SMarcel Moolenaar if (xo_style(xop) == XO_STYLE_TEXT || xo_style(xop) == XO_STYLE_HTML) {
279231337658SMarcel Moolenaar if (max > 0 && cols + width > max)
279331337658SMarcel Moolenaar break;
279431337658SMarcel Moolenaar }
279531337658SMarcel Moolenaar
279631337658SMarcel Moolenaar switch (need_enc) {
279731337658SMarcel Moolenaar case XF_ENC_UTF8:
279831337658SMarcel Moolenaar
279931337658SMarcel Moolenaar /* Output in UTF-8 needs to be escaped, based on the style */
2800788ca347SMarcel Moolenaar switch (xo_style(xop)) {
280131337658SMarcel Moolenaar case XO_STYLE_XML:
280231337658SMarcel Moolenaar case XO_STYLE_HTML:
280331337658SMarcel Moolenaar if (wc == '<')
280431337658SMarcel Moolenaar sp = xo_xml_lt;
280531337658SMarcel Moolenaar else if (wc == '>')
280631337658SMarcel Moolenaar sp = xo_xml_gt;
280731337658SMarcel Moolenaar else if (wc == '&')
280831337658SMarcel Moolenaar sp = xo_xml_amp;
280931337658SMarcel Moolenaar else if (attr && wc == '"')
281031337658SMarcel Moolenaar sp = xo_xml_quot;
281131337658SMarcel Moolenaar else
281231337658SMarcel Moolenaar break;
281331337658SMarcel Moolenaar
28148a6eceffSPhil Shafer ssize_t slen = strlen(sp);
281531337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, slen - 1))
281631337658SMarcel Moolenaar return -1;
281731337658SMarcel Moolenaar
281831337658SMarcel Moolenaar memcpy(xbp->xb_curp, sp, slen);
281931337658SMarcel Moolenaar xbp->xb_curp += slen;
282031337658SMarcel Moolenaar goto done_with_encoding; /* Need multi-level 'break' */
282131337658SMarcel Moolenaar
282231337658SMarcel Moolenaar case XO_STYLE_JSON:
2823545ddfbeSMarcel Moolenaar if (wc != '\\' && wc != '"' && wc != '\n' && wc != '\r')
282431337658SMarcel Moolenaar break;
282531337658SMarcel Moolenaar
282631337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, 2))
282731337658SMarcel Moolenaar return -1;
282831337658SMarcel Moolenaar
282931337658SMarcel Moolenaar *xbp->xb_curp++ = '\\';
2830545ddfbeSMarcel Moolenaar if (wc == '\n')
2831545ddfbeSMarcel Moolenaar wc = 'n';
2832545ddfbeSMarcel Moolenaar else if (wc == '\r')
2833545ddfbeSMarcel Moolenaar wc = 'r';
2834545ddfbeSMarcel Moolenaar else wc = wc & 0x7f;
2835545ddfbeSMarcel Moolenaar
2836545ddfbeSMarcel Moolenaar *xbp->xb_curp++ = wc;
283731337658SMarcel Moolenaar goto done_with_encoding;
2838d1a0d267SMarcel Moolenaar
2839d1a0d267SMarcel Moolenaar case XO_STYLE_SDPARAMS:
2840d1a0d267SMarcel Moolenaar if (wc != '\\' && wc != '"' && wc != ']')
2841d1a0d267SMarcel Moolenaar break;
2842d1a0d267SMarcel Moolenaar
2843d1a0d267SMarcel Moolenaar if (!xo_buf_has_room(xbp, 2))
2844d1a0d267SMarcel Moolenaar return -1;
2845d1a0d267SMarcel Moolenaar
2846d1a0d267SMarcel Moolenaar *xbp->xb_curp++ = '\\';
2847d1a0d267SMarcel Moolenaar wc = wc & 0x7f;
2848d1a0d267SMarcel Moolenaar *xbp->xb_curp++ = wc;
2849d1a0d267SMarcel Moolenaar goto done_with_encoding;
285031337658SMarcel Moolenaar }
285131337658SMarcel Moolenaar
285231337658SMarcel Moolenaar olen = xo_utf8_emit_len(wc);
285331337658SMarcel Moolenaar if (olen < 0) {
285431337658SMarcel Moolenaar xo_failure(xop, "ignoring bad length");
285531337658SMarcel Moolenaar continue;
285631337658SMarcel Moolenaar }
285731337658SMarcel Moolenaar
285831337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, olen))
285931337658SMarcel Moolenaar return -1;
286031337658SMarcel Moolenaar
286131337658SMarcel Moolenaar xo_utf8_emit_char(xbp->xb_curp, olen, wc);
286231337658SMarcel Moolenaar xbp->xb_curp += olen;
286331337658SMarcel Moolenaar break;
286431337658SMarcel Moolenaar
286531337658SMarcel Moolenaar case XF_ENC_LOCALE:
286631337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, MB_LEN_MAX + 1))
286731337658SMarcel Moolenaar return -1;
286831337658SMarcel Moolenaar
286931337658SMarcel Moolenaar olen = wcrtomb(xbp->xb_curp, wc, &xop->xo_mbstate);
287031337658SMarcel Moolenaar if (olen <= 0) {
287131337658SMarcel Moolenaar xo_failure(xop, "could not convert wide char: %lx",
287231337658SMarcel Moolenaar (unsigned long) wc);
287331337658SMarcel Moolenaar width = 1;
287431337658SMarcel Moolenaar *xbp->xb_curp++ = '?';
287531337658SMarcel Moolenaar } else
287631337658SMarcel Moolenaar xbp->xb_curp += olen;
287731337658SMarcel Moolenaar break;
287831337658SMarcel Moolenaar }
287931337658SMarcel Moolenaar
288031337658SMarcel Moolenaar done_with_encoding:
288131337658SMarcel Moolenaar cols += width;
288231337658SMarcel Moolenaar }
288331337658SMarcel Moolenaar
288431337658SMarcel Moolenaar return cols;
288531337658SMarcel Moolenaar }
288631337658SMarcel Moolenaar
288731337658SMarcel Moolenaar static int
xo_needed_encoding(xo_handle_t * xop)2888d1a0d267SMarcel Moolenaar xo_needed_encoding (xo_handle_t *xop)
2889d1a0d267SMarcel Moolenaar {
2890d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_UTF8)) /* Check the override flag */
2891d1a0d267SMarcel Moolenaar return XF_ENC_UTF8;
2892d1a0d267SMarcel Moolenaar
2893d1a0d267SMarcel Moolenaar if (xo_style(xop) == XO_STYLE_TEXT) /* Text means locale */
2894d1a0d267SMarcel Moolenaar return XF_ENC_LOCALE;
2895d1a0d267SMarcel Moolenaar
2896d1a0d267SMarcel Moolenaar return XF_ENC_UTF8; /* Otherwise, we love UTF-8 */
2897d1a0d267SMarcel Moolenaar }
2898d1a0d267SMarcel Moolenaar
28998a6eceffSPhil Shafer static ssize_t
xo_format_string(xo_handle_t * xop,xo_buffer_t * xbp,xo_xff_flags_t flags,xo_format_t * xfp)290031337658SMarcel Moolenaar xo_format_string (xo_handle_t *xop, xo_buffer_t *xbp, xo_xff_flags_t flags,
290131337658SMarcel Moolenaar xo_format_t *xfp)
290231337658SMarcel Moolenaar {
290331337658SMarcel Moolenaar static char null[] = "(null)";
2904d1a0d267SMarcel Moolenaar static char null_no_quotes[] = "null";
2905a0f704ffSMarcel Moolenaar
290631337658SMarcel Moolenaar char *cp = NULL;
290731337658SMarcel Moolenaar wchar_t *wcp = NULL;
29088a6eceffSPhil Shafer ssize_t len;
29098a6eceffSPhil Shafer ssize_t cols = 0, rc = 0;
29108a6eceffSPhil Shafer ssize_t off = xbp->xb_curp - xbp->xb_bufp, off2;
2911d1a0d267SMarcel Moolenaar int need_enc = xo_needed_encoding(xop);
291231337658SMarcel Moolenaar
291331337658SMarcel Moolenaar if (xo_check_conversion(xop, xfp->xf_enc, need_enc))
291431337658SMarcel Moolenaar return 0;
291531337658SMarcel Moolenaar
2916a0f704ffSMarcel Moolenaar len = xfp->xf_width[XF_WIDTH_SIZE];
2917a0f704ffSMarcel Moolenaar
2918d1a0d267SMarcel Moolenaar if (xfp->xf_fc == 'm') {
2919d1a0d267SMarcel Moolenaar cp = strerror(xop->xo_errno);
2920d1a0d267SMarcel Moolenaar if (len < 0)
2921d1a0d267SMarcel Moolenaar len = cp ? strlen(cp) : 0;
2922d1a0d267SMarcel Moolenaar goto normal_string;
2923d1a0d267SMarcel Moolenaar
2924d1a0d267SMarcel Moolenaar } else if (xfp->xf_enc == XF_ENC_WIDE) {
292531337658SMarcel Moolenaar wcp = va_arg(xop->xo_vap, wchar_t *);
292631337658SMarcel Moolenaar if (xfp->xf_skip)
292731337658SMarcel Moolenaar return 0;
292831337658SMarcel Moolenaar
2929a0f704ffSMarcel Moolenaar /*
2930a0f704ffSMarcel Moolenaar * Dont' deref NULL; use the traditional "(null)" instead
2931a0f704ffSMarcel Moolenaar * of the more accurate "who's been a naughty boy, then?".
2932a0f704ffSMarcel Moolenaar */
2933a0f704ffSMarcel Moolenaar if (wcp == NULL) {
2934a0f704ffSMarcel Moolenaar cp = null;
2935a0f704ffSMarcel Moolenaar len = sizeof(null) - 1;
2936a0f704ffSMarcel Moolenaar }
2937a0f704ffSMarcel Moolenaar
293831337658SMarcel Moolenaar } else {
293931337658SMarcel Moolenaar cp = va_arg(xop->xo_vap, char *); /* UTF-8 or native */
2940d1a0d267SMarcel Moolenaar
2941d1a0d267SMarcel Moolenaar normal_string:
294231337658SMarcel Moolenaar if (xfp->xf_skip)
294331337658SMarcel Moolenaar return 0;
294431337658SMarcel Moolenaar
2945a0f704ffSMarcel Moolenaar /* Echo "Dont' deref NULL" logic */
2946a0f704ffSMarcel Moolenaar if (cp == NULL) {
2947d1a0d267SMarcel Moolenaar if ((flags & XFF_NOQUOTE) && xo_style_is_encoding(xop)) {
2948d1a0d267SMarcel Moolenaar cp = null_no_quotes;
2949d1a0d267SMarcel Moolenaar len = sizeof(null_no_quotes) - 1;
2950d1a0d267SMarcel Moolenaar } else {
2951a0f704ffSMarcel Moolenaar cp = null;
2952a0f704ffSMarcel Moolenaar len = sizeof(null) - 1;
2953a0f704ffSMarcel Moolenaar }
2954d1a0d267SMarcel Moolenaar }
2955a0f704ffSMarcel Moolenaar
295631337658SMarcel Moolenaar /*
295731337658SMarcel Moolenaar * Optimize the most common case, which is "%s". We just
295831337658SMarcel Moolenaar * need to copy the complete string to the output buffer.
295931337658SMarcel Moolenaar */
296031337658SMarcel Moolenaar if (xfp->xf_enc == need_enc
296131337658SMarcel Moolenaar && xfp->xf_width[XF_WIDTH_MIN] < 0
296231337658SMarcel Moolenaar && xfp->xf_width[XF_WIDTH_SIZE] < 0
296331337658SMarcel Moolenaar && xfp->xf_width[XF_WIDTH_MAX] < 0
2964d1a0d267SMarcel Moolenaar && !(XOIF_ISSET(xop, XOIF_ANCHOR)
2965d1a0d267SMarcel Moolenaar || XOF_ISSET(xop, XOF_COLUMNS))) {
296631337658SMarcel Moolenaar len = strlen(cp);
296731337658SMarcel Moolenaar xo_buf_escape(xop, xbp, cp, len, flags);
296831337658SMarcel Moolenaar
296931337658SMarcel Moolenaar /*
297031337658SMarcel Moolenaar * Our caller expects xb_curp left untouched, so we have
297131337658SMarcel Moolenaar * to reset it and return the number of bytes written to
297231337658SMarcel Moolenaar * the buffer.
297331337658SMarcel Moolenaar */
297431337658SMarcel Moolenaar off2 = xbp->xb_curp - xbp->xb_bufp;
297531337658SMarcel Moolenaar rc = off2 - off;
297631337658SMarcel Moolenaar xbp->xb_curp = xbp->xb_bufp + off;
297731337658SMarcel Moolenaar
297831337658SMarcel Moolenaar return rc;
297931337658SMarcel Moolenaar }
298031337658SMarcel Moolenaar }
298131337658SMarcel Moolenaar
298231337658SMarcel Moolenaar cols = xo_format_string_direct(xop, xbp, flags, wcp, cp, len,
298331337658SMarcel Moolenaar xfp->xf_width[XF_WIDTH_MAX],
298431337658SMarcel Moolenaar need_enc, xfp->xf_enc);
298531337658SMarcel Moolenaar if (cols < 0)
298631337658SMarcel Moolenaar goto bail;
298731337658SMarcel Moolenaar
298831337658SMarcel Moolenaar /*
298931337658SMarcel Moolenaar * xo_buf_append* will move xb_curp, so we save/restore it.
299031337658SMarcel Moolenaar */
299131337658SMarcel Moolenaar off2 = xbp->xb_curp - xbp->xb_bufp;
299231337658SMarcel Moolenaar rc = off2 - off;
299331337658SMarcel Moolenaar xbp->xb_curp = xbp->xb_bufp + off;
299431337658SMarcel Moolenaar
299531337658SMarcel Moolenaar if (cols < xfp->xf_width[XF_WIDTH_MIN]) {
299631337658SMarcel Moolenaar /*
299731337658SMarcel Moolenaar * Find the number of columns needed to display the string.
299831337658SMarcel Moolenaar * If we have the original wide string, we just call wcswidth,
299931337658SMarcel Moolenaar * but if we did the work ourselves, then we need to do it.
300031337658SMarcel Moolenaar */
300131337658SMarcel Moolenaar int delta = xfp->xf_width[XF_WIDTH_MIN] - cols;
3002ee5cf116SPhil Shafer if (!xo_buf_has_room(xbp, xfp->xf_width[XF_WIDTH_MIN]))
300331337658SMarcel Moolenaar goto bail;
300431337658SMarcel Moolenaar
300531337658SMarcel Moolenaar /*
300631337658SMarcel Moolenaar * If seen_minus, then pad on the right; otherwise move it so
300731337658SMarcel Moolenaar * we can pad on the left.
300831337658SMarcel Moolenaar */
300931337658SMarcel Moolenaar if (xfp->xf_seen_minus) {
301031337658SMarcel Moolenaar cp = xbp->xb_curp + rc;
301131337658SMarcel Moolenaar } else {
301231337658SMarcel Moolenaar cp = xbp->xb_curp;
301331337658SMarcel Moolenaar memmove(xbp->xb_curp + delta, xbp->xb_curp, rc);
301431337658SMarcel Moolenaar }
301531337658SMarcel Moolenaar
301631337658SMarcel Moolenaar /* Set the padding */
301731337658SMarcel Moolenaar memset(cp, (xfp->xf_leading_zero > 0) ? '0' : ' ', delta);
301831337658SMarcel Moolenaar rc += delta;
301931337658SMarcel Moolenaar cols += delta;
302031337658SMarcel Moolenaar }
302131337658SMarcel Moolenaar
3022d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_COLUMNS))
302331337658SMarcel Moolenaar xop->xo_columns += cols;
3024d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_ANCHOR))
302531337658SMarcel Moolenaar xop->xo_anchor_columns += cols;
302631337658SMarcel Moolenaar
302731337658SMarcel Moolenaar return rc;
302831337658SMarcel Moolenaar
302931337658SMarcel Moolenaar bail:
303031337658SMarcel Moolenaar xbp->xb_curp = xbp->xb_bufp + off;
303131337658SMarcel Moolenaar return 0;
303231337658SMarcel Moolenaar }
303331337658SMarcel Moolenaar
3034d1a0d267SMarcel Moolenaar /*
3035d1a0d267SMarcel Moolenaar * Look backwards in a buffer to find a numeric value
3036d1a0d267SMarcel Moolenaar */
3037d1a0d267SMarcel Moolenaar static int
xo_buf_find_last_number(xo_buffer_t * xbp,ssize_t start_offset)30388a6eceffSPhil Shafer xo_buf_find_last_number (xo_buffer_t *xbp, ssize_t start_offset)
3039d1a0d267SMarcel Moolenaar {
3040d1a0d267SMarcel Moolenaar int rc = 0; /* Fail with zero */
3041d1a0d267SMarcel Moolenaar int digit = 1;
3042d1a0d267SMarcel Moolenaar char *sp = xbp->xb_bufp;
3043d1a0d267SMarcel Moolenaar char *cp = sp + start_offset;
3044d1a0d267SMarcel Moolenaar
3045d1a0d267SMarcel Moolenaar while (--cp >= sp)
3046d1a0d267SMarcel Moolenaar if (isdigit((int) *cp))
3047d1a0d267SMarcel Moolenaar break;
3048d1a0d267SMarcel Moolenaar
3049d1a0d267SMarcel Moolenaar for ( ; cp >= sp; cp--) {
3050d1a0d267SMarcel Moolenaar if (!isdigit((int) *cp))
3051d1a0d267SMarcel Moolenaar break;
3052d1a0d267SMarcel Moolenaar rc += (*cp - '0') * digit;
3053d1a0d267SMarcel Moolenaar digit *= 10;
3054d1a0d267SMarcel Moolenaar }
3055d1a0d267SMarcel Moolenaar
3056d1a0d267SMarcel Moolenaar return rc;
3057d1a0d267SMarcel Moolenaar }
3058d1a0d267SMarcel Moolenaar
30598a6eceffSPhil Shafer static ssize_t
xo_count_utf8_cols(const char * str,ssize_t len)30608a6eceffSPhil Shafer xo_count_utf8_cols (const char *str, ssize_t len)
3061d1a0d267SMarcel Moolenaar {
30628a6eceffSPhil Shafer ssize_t tlen;
3063d1a0d267SMarcel Moolenaar wchar_t wc;
30648a6eceffSPhil Shafer ssize_t cols = 0;
3065d1a0d267SMarcel Moolenaar const char *ep = str + len;
3066d1a0d267SMarcel Moolenaar
3067d1a0d267SMarcel Moolenaar while (str < ep) {
3068d1a0d267SMarcel Moolenaar tlen = xo_utf8_to_wc_len(str);
3069d1a0d267SMarcel Moolenaar if (tlen < 0) /* Broken input is very bad */
3070d1a0d267SMarcel Moolenaar return cols;
3071d1a0d267SMarcel Moolenaar
3072d1a0d267SMarcel Moolenaar wc = xo_utf8_char(str, tlen);
3073d1a0d267SMarcel Moolenaar if (wc == (wchar_t) -1)
3074d1a0d267SMarcel Moolenaar return cols;
3075d1a0d267SMarcel Moolenaar
3076d1a0d267SMarcel Moolenaar /* We only print printable characters */
3077d1a0d267SMarcel Moolenaar if (iswprint((wint_t) wc)) {
3078d1a0d267SMarcel Moolenaar /*
3079d1a0d267SMarcel Moolenaar * Find the width-in-columns of this character, which must be done
3080d1a0d267SMarcel Moolenaar * in wide characters, since we lack a mbswidth() function.
3081d1a0d267SMarcel Moolenaar */
30828a6eceffSPhil Shafer ssize_t width = xo_wcwidth(wc);
3083d1a0d267SMarcel Moolenaar if (width < 0)
3084d1a0d267SMarcel Moolenaar width = iswcntrl(wc) ? 0 : 1;
3085d1a0d267SMarcel Moolenaar
3086d1a0d267SMarcel Moolenaar cols += width;
3087d1a0d267SMarcel Moolenaar }
3088d1a0d267SMarcel Moolenaar
3089d1a0d267SMarcel Moolenaar str += tlen;
3090d1a0d267SMarcel Moolenaar }
3091d1a0d267SMarcel Moolenaar
3092d1a0d267SMarcel Moolenaar return cols;
3093d1a0d267SMarcel Moolenaar }
3094d1a0d267SMarcel Moolenaar
3095d1a0d267SMarcel Moolenaar #ifdef HAVE_GETTEXT
3096d1a0d267SMarcel Moolenaar static inline const char *
xo_dgettext(xo_handle_t * xop,const char * str)3097d1a0d267SMarcel Moolenaar xo_dgettext (xo_handle_t *xop, const char *str)
3098d1a0d267SMarcel Moolenaar {
3099d1a0d267SMarcel Moolenaar const char *domainname = xop->xo_gt_domain;
3100d1a0d267SMarcel Moolenaar const char *res;
3101d1a0d267SMarcel Moolenaar
3102d1a0d267SMarcel Moolenaar res = dgettext(domainname, str);
3103d1a0d267SMarcel Moolenaar
3104d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_LOG_GETTEXT))
3105d1a0d267SMarcel Moolenaar fprintf(stderr, "xo: gettext: %s%s%smsgid \"%s\" returns \"%s\"\n",
3106d1a0d267SMarcel Moolenaar domainname ? "domain \"" : "", xo_printable(domainname),
3107d1a0d267SMarcel Moolenaar domainname ? "\", " : "", xo_printable(str), xo_printable(res));
3108d1a0d267SMarcel Moolenaar
3109d1a0d267SMarcel Moolenaar return res;
3110d1a0d267SMarcel Moolenaar }
3111d1a0d267SMarcel Moolenaar
3112d1a0d267SMarcel Moolenaar static inline const char *
xo_dngettext(xo_handle_t * xop,const char * sing,const char * plural,unsigned long int n)3113d1a0d267SMarcel Moolenaar xo_dngettext (xo_handle_t *xop, const char *sing, const char *plural,
3114d1a0d267SMarcel Moolenaar unsigned long int n)
3115d1a0d267SMarcel Moolenaar {
3116d1a0d267SMarcel Moolenaar const char *domainname = xop->xo_gt_domain;
3117d1a0d267SMarcel Moolenaar const char *res;
3118d1a0d267SMarcel Moolenaar
3119d1a0d267SMarcel Moolenaar res = dngettext(domainname, sing, plural, n);
3120d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_LOG_GETTEXT))
3121d1a0d267SMarcel Moolenaar fprintf(stderr, "xo: gettext: %s%s%s"
3122d1a0d267SMarcel Moolenaar "msgid \"%s\", msgid_plural \"%s\" (%lu) returns \"%s\"\n",
3123d1a0d267SMarcel Moolenaar domainname ? "domain \"" : "",
3124d1a0d267SMarcel Moolenaar xo_printable(domainname), domainname ? "\", " : "",
3125d1a0d267SMarcel Moolenaar xo_printable(sing),
3126d1a0d267SMarcel Moolenaar xo_printable(plural), n, xo_printable(res));
3127d1a0d267SMarcel Moolenaar
3128d1a0d267SMarcel Moolenaar return res;
3129d1a0d267SMarcel Moolenaar }
3130d1a0d267SMarcel Moolenaar #else /* HAVE_GETTEXT */
3131d1a0d267SMarcel Moolenaar static inline const char *
xo_dgettext(xo_handle_t * xop UNUSED,const char * str)3132d1a0d267SMarcel Moolenaar xo_dgettext (xo_handle_t *xop UNUSED, const char *str)
3133d1a0d267SMarcel Moolenaar {
3134d1a0d267SMarcel Moolenaar return str;
3135d1a0d267SMarcel Moolenaar }
3136d1a0d267SMarcel Moolenaar
3137d1a0d267SMarcel Moolenaar static inline const char *
xo_dngettext(xo_handle_t * xop UNUSED,const char * singular,const char * plural,unsigned long int n)3138d1a0d267SMarcel Moolenaar xo_dngettext (xo_handle_t *xop UNUSED, const char *singular,
3139d1a0d267SMarcel Moolenaar const char *plural, unsigned long int n)
3140d1a0d267SMarcel Moolenaar {
3141d1a0d267SMarcel Moolenaar return (n == 1) ? singular : plural;
3142d1a0d267SMarcel Moolenaar }
3143d1a0d267SMarcel Moolenaar #endif /* HAVE_GETTEXT */
3144d1a0d267SMarcel Moolenaar
3145d1a0d267SMarcel Moolenaar /*
3146d1a0d267SMarcel Moolenaar * This is really _re_formatting, since the normal format code has
3147d1a0d267SMarcel Moolenaar * generated a beautiful string into xo_data, starting at
3148d1a0d267SMarcel Moolenaar * start_offset. We need to see if it's plural, which means
3149d1a0d267SMarcel Moolenaar * comma-separated options, or singular. Then we make the appropriate
3150d1a0d267SMarcel Moolenaar * call to d[n]gettext() to get the locale-based version. Note that
3151d1a0d267SMarcel Moolenaar * both input and output of gettext() this should be UTF-8.
3152d1a0d267SMarcel Moolenaar */
31538a6eceffSPhil Shafer static ssize_t
xo_format_gettext(xo_handle_t * xop,xo_xff_flags_t flags,ssize_t start_offset,ssize_t cols,int need_enc)3154d1a0d267SMarcel Moolenaar xo_format_gettext (xo_handle_t *xop, xo_xff_flags_t flags,
31558a6eceffSPhil Shafer ssize_t start_offset, ssize_t cols, int need_enc)
3156d1a0d267SMarcel Moolenaar {
3157d1a0d267SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_data;
3158d1a0d267SMarcel Moolenaar
3159d1a0d267SMarcel Moolenaar if (!xo_buf_has_room(xbp, 1))
3160d1a0d267SMarcel Moolenaar return cols;
3161d1a0d267SMarcel Moolenaar
3162d1a0d267SMarcel Moolenaar xbp->xb_curp[0] = '\0'; /* NUL-terminate the input string */
3163d1a0d267SMarcel Moolenaar
3164d1a0d267SMarcel Moolenaar char *cp = xbp->xb_bufp + start_offset;
31658a6eceffSPhil Shafer ssize_t len = xbp->xb_curp - cp;
3166d1a0d267SMarcel Moolenaar const char *newstr = NULL;
3167d1a0d267SMarcel Moolenaar
3168d1a0d267SMarcel Moolenaar /*
3169d1a0d267SMarcel Moolenaar * The plural flag asks us to look backwards at the last numeric
3170d1a0d267SMarcel Moolenaar * value rendered and disect the string into two pieces.
3171d1a0d267SMarcel Moolenaar */
3172d1a0d267SMarcel Moolenaar if (flags & XFF_GT_PLURAL) {
3173d1a0d267SMarcel Moolenaar int n = xo_buf_find_last_number(xbp, start_offset);
3174d1a0d267SMarcel Moolenaar char *two = memchr(cp, (int) ',', len);
3175d1a0d267SMarcel Moolenaar if (two == NULL) {
3176d1a0d267SMarcel Moolenaar xo_failure(xop, "no comma in plural gettext field: '%s'", cp);
3177d1a0d267SMarcel Moolenaar return cols;
3178d1a0d267SMarcel Moolenaar }
3179d1a0d267SMarcel Moolenaar
3180d1a0d267SMarcel Moolenaar if (two == cp) {
3181d1a0d267SMarcel Moolenaar xo_failure(xop, "nothing before comma in plural gettext "
3182d1a0d267SMarcel Moolenaar "field: '%s'", cp);
3183d1a0d267SMarcel Moolenaar return cols;
3184d1a0d267SMarcel Moolenaar }
3185d1a0d267SMarcel Moolenaar
3186d1a0d267SMarcel Moolenaar if (two == xbp->xb_curp) {
3187d1a0d267SMarcel Moolenaar xo_failure(xop, "nothing after comma in plural gettext "
3188d1a0d267SMarcel Moolenaar "field: '%s'", cp);
3189d1a0d267SMarcel Moolenaar return cols;
3190d1a0d267SMarcel Moolenaar }
3191d1a0d267SMarcel Moolenaar
3192d1a0d267SMarcel Moolenaar *two++ = '\0';
3193d1a0d267SMarcel Moolenaar if (flags & XFF_GT_FIELD) {
3194d1a0d267SMarcel Moolenaar newstr = xo_dngettext(xop, cp, two, n);
3195d1a0d267SMarcel Moolenaar } else {
3196d1a0d267SMarcel Moolenaar /* Don't do a gettext() look up, just get the plural form */
3197d1a0d267SMarcel Moolenaar newstr = (n == 1) ? cp : two;
3198d1a0d267SMarcel Moolenaar }
3199d1a0d267SMarcel Moolenaar
3200d1a0d267SMarcel Moolenaar /*
3201d1a0d267SMarcel Moolenaar * If we returned the first string, optimize a bit by
3202d1a0d267SMarcel Moolenaar * backing up over comma
3203d1a0d267SMarcel Moolenaar */
3204d1a0d267SMarcel Moolenaar if (newstr == cp) {
3205d1a0d267SMarcel Moolenaar xbp->xb_curp = two - 1; /* One for comma */
3206d1a0d267SMarcel Moolenaar /*
3207d1a0d267SMarcel Moolenaar * If the caller wanted UTF8, we're done; nothing changed,
3208d1a0d267SMarcel Moolenaar * but we need to count the columns used.
3209d1a0d267SMarcel Moolenaar */
3210d1a0d267SMarcel Moolenaar if (need_enc == XF_ENC_UTF8)
3211d1a0d267SMarcel Moolenaar return xo_count_utf8_cols(cp, xbp->xb_curp - cp);
3212d1a0d267SMarcel Moolenaar }
3213d1a0d267SMarcel Moolenaar
3214d1a0d267SMarcel Moolenaar } else {
3215d1a0d267SMarcel Moolenaar /* The simple case (singular) */
3216d1a0d267SMarcel Moolenaar newstr = xo_dgettext(xop, cp);
3217d1a0d267SMarcel Moolenaar
3218d1a0d267SMarcel Moolenaar if (newstr == cp) {
3219d1a0d267SMarcel Moolenaar /* If the caller wanted UTF8, we're done; nothing changed */
3220d1a0d267SMarcel Moolenaar if (need_enc == XF_ENC_UTF8)
3221d1a0d267SMarcel Moolenaar return cols;
3222d1a0d267SMarcel Moolenaar }
3223d1a0d267SMarcel Moolenaar }
3224d1a0d267SMarcel Moolenaar
3225d1a0d267SMarcel Moolenaar /*
3226d1a0d267SMarcel Moolenaar * Since the new string string might be in gettext's buffer or
3227d1a0d267SMarcel Moolenaar * in the buffer (as the plural form), we make a copy.
3228d1a0d267SMarcel Moolenaar */
32298a6eceffSPhil Shafer ssize_t nlen = strlen(newstr);
3230d1a0d267SMarcel Moolenaar char *newcopy = alloca(nlen + 1);
3231d1a0d267SMarcel Moolenaar memcpy(newcopy, newstr, nlen + 1);
3232d1a0d267SMarcel Moolenaar
3233d1a0d267SMarcel Moolenaar xbp->xb_curp = xbp->xb_bufp + start_offset; /* Reset the buffer */
3234d1a0d267SMarcel Moolenaar return xo_format_string_direct(xop, xbp, flags, NULL, newcopy, nlen, 0,
3235d1a0d267SMarcel Moolenaar need_enc, XF_ENC_UTF8);
3236d1a0d267SMarcel Moolenaar }
3237d1a0d267SMarcel Moolenaar
323831337658SMarcel Moolenaar static void
xo_data_append_content(xo_handle_t * xop,const char * str,ssize_t len,xo_xff_flags_t flags)32398a6eceffSPhil Shafer xo_data_append_content (xo_handle_t *xop, const char *str, ssize_t len,
3240d1a0d267SMarcel Moolenaar xo_xff_flags_t flags)
324131337658SMarcel Moolenaar {
324231337658SMarcel Moolenaar int cols;
3243d1a0d267SMarcel Moolenaar int need_enc = xo_needed_encoding(xop);
32448a6eceffSPhil Shafer ssize_t start_offset = xo_buf_offset(&xop->xo_data);
324531337658SMarcel Moolenaar
3246d1a0d267SMarcel Moolenaar cols = xo_format_string_direct(xop, &xop->xo_data, XFF_UNESCAPE | flags,
324731337658SMarcel Moolenaar NULL, str, len, -1,
324831337658SMarcel Moolenaar need_enc, XF_ENC_UTF8);
3249d1a0d267SMarcel Moolenaar if (flags & XFF_GT_FLAGS)
3250d1a0d267SMarcel Moolenaar cols = xo_format_gettext(xop, flags, start_offset, cols, need_enc);
325131337658SMarcel Moolenaar
3252d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_COLUMNS))
325331337658SMarcel Moolenaar xop->xo_columns += cols;
3254d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_ANCHOR))
325531337658SMarcel Moolenaar xop->xo_anchor_columns += cols;
325631337658SMarcel Moolenaar }
325731337658SMarcel Moolenaar
3258f2b7bf8aSPhil Shafer /**
3259f2b7bf8aSPhil Shafer * Bump one of the 'width' values in a format strings (e.g. "%40.50.60s").
3260f2b7bf8aSPhil Shafer * @param xfp Formatting instructions
3261f2b7bf8aSPhil Shafer * @param digit Single digit (0-9) of input
3262f2b7bf8aSPhil Shafer */
326331337658SMarcel Moolenaar static void
xo_bump_width(xo_format_t * xfp,int digit)326431337658SMarcel Moolenaar xo_bump_width (xo_format_t *xfp, int digit)
326531337658SMarcel Moolenaar {
326631337658SMarcel Moolenaar int *ip = &xfp->xf_width[xfp->xf_dots];
326731337658SMarcel Moolenaar
326831337658SMarcel Moolenaar *ip = ((*ip > 0) ? *ip : 0) * 10 + digit;
326931337658SMarcel Moolenaar }
327031337658SMarcel Moolenaar
32718a6eceffSPhil Shafer static ssize_t
xo_trim_ws(xo_buffer_t * xbp,ssize_t len)32728a6eceffSPhil Shafer xo_trim_ws (xo_buffer_t *xbp, ssize_t len)
327331337658SMarcel Moolenaar {
327431337658SMarcel Moolenaar char *cp, *sp, *ep;
32758a6eceffSPhil Shafer ssize_t delta;
327631337658SMarcel Moolenaar
327731337658SMarcel Moolenaar /* First trim leading space */
327831337658SMarcel Moolenaar for (cp = sp = xbp->xb_curp, ep = cp + len; cp < ep; cp++) {
327931337658SMarcel Moolenaar if (*cp != ' ')
328031337658SMarcel Moolenaar break;
328131337658SMarcel Moolenaar }
328231337658SMarcel Moolenaar
328331337658SMarcel Moolenaar delta = cp - sp;
328431337658SMarcel Moolenaar if (delta) {
328531337658SMarcel Moolenaar len -= delta;
328631337658SMarcel Moolenaar memmove(sp, cp, len);
328731337658SMarcel Moolenaar }
328831337658SMarcel Moolenaar
328931337658SMarcel Moolenaar /* Then trim off the end */
329031337658SMarcel Moolenaar for (cp = xbp->xb_curp, sp = ep = cp + len; cp < ep; ep--) {
329131337658SMarcel Moolenaar if (ep[-1] != ' ')
329231337658SMarcel Moolenaar break;
329331337658SMarcel Moolenaar }
329431337658SMarcel Moolenaar
329531337658SMarcel Moolenaar delta = sp - ep;
329631337658SMarcel Moolenaar if (delta) {
329731337658SMarcel Moolenaar len -= delta;
329831337658SMarcel Moolenaar cp[len] = '\0';
329931337658SMarcel Moolenaar }
330031337658SMarcel Moolenaar
330131337658SMarcel Moolenaar return len;
330231337658SMarcel Moolenaar }
330331337658SMarcel Moolenaar
3304d1a0d267SMarcel Moolenaar /*
3305d1a0d267SMarcel Moolenaar * Interface to format a single field. The arguments are in xo_vap,
3306d1a0d267SMarcel Moolenaar * and the format is in 'fmt'. If 'xbp' is null, we use xop->xo_data;
3307d1a0d267SMarcel Moolenaar * this is the most common case.
3308d1a0d267SMarcel Moolenaar */
33098a6eceffSPhil Shafer static ssize_t
xo_do_format_field(xo_handle_t * xop,xo_buffer_t * xbp,const char * fmt,ssize_t flen,xo_xff_flags_t flags)3310d1a0d267SMarcel Moolenaar xo_do_format_field (xo_handle_t *xop, xo_buffer_t *xbp,
33118a6eceffSPhil Shafer const char *fmt, ssize_t flen, xo_xff_flags_t flags)
331231337658SMarcel Moolenaar {
331331337658SMarcel Moolenaar xo_format_t xf;
331431337658SMarcel Moolenaar const char *cp, *ep, *sp, *xp = NULL;
33158a6eceffSPhil Shafer ssize_t rc, cols;
3316788ca347SMarcel Moolenaar int style = (flags & XFF_XML) ? XO_STYLE_XML : xo_style(xop);
33178a6eceffSPhil Shafer unsigned make_output = !(flags & XFF_NO_OUTPUT) ? 1 : 0;
3318d1a0d267SMarcel Moolenaar int need_enc = xo_needed_encoding(xop);
3319d1a0d267SMarcel Moolenaar int real_need_enc = need_enc;
33208a6eceffSPhil Shafer ssize_t old_cols = xop->xo_columns;
3321d1a0d267SMarcel Moolenaar
3322d1a0d267SMarcel Moolenaar /* The gettext interface is UTF-8, so we'll need that for now */
3323d1a0d267SMarcel Moolenaar if (flags & XFF_GT_FIELD)
3324d1a0d267SMarcel Moolenaar need_enc = XF_ENC_UTF8;
332531337658SMarcel Moolenaar
332631337658SMarcel Moolenaar if (xbp == NULL)
332731337658SMarcel Moolenaar xbp = &xop->xo_data;
332831337658SMarcel Moolenaar
33298a6eceffSPhil Shafer ssize_t start_offset = xo_buf_offset(xbp);
3330d1a0d267SMarcel Moolenaar
333131337658SMarcel Moolenaar for (cp = fmt, ep = fmt + flen; cp < ep; cp++) {
3332d1a0d267SMarcel Moolenaar /*
3333d1a0d267SMarcel Moolenaar * Since we're starting a new field, save the starting offset.
3334d1a0d267SMarcel Moolenaar * We'll need this later for field-related operations.
3335d1a0d267SMarcel Moolenaar */
3336d1a0d267SMarcel Moolenaar
333731337658SMarcel Moolenaar if (*cp != '%') {
333831337658SMarcel Moolenaar add_one:
333931337658SMarcel Moolenaar if (xp == NULL)
334031337658SMarcel Moolenaar xp = cp;
334131337658SMarcel Moolenaar
334231337658SMarcel Moolenaar if (*cp == '\\' && cp[1] != '\0')
334331337658SMarcel Moolenaar cp += 1;
334431337658SMarcel Moolenaar continue;
334531337658SMarcel Moolenaar
334676afb20cSPhil Shafer } else if (cp + 1 < ep && cp[1] == '%') {
334731337658SMarcel Moolenaar cp += 1;
334831337658SMarcel Moolenaar goto add_one;
334931337658SMarcel Moolenaar }
335031337658SMarcel Moolenaar
335131337658SMarcel Moolenaar if (xp) {
335231337658SMarcel Moolenaar if (make_output) {
335331337658SMarcel Moolenaar cols = xo_format_string_direct(xop, xbp, flags | XFF_UNESCAPE,
335431337658SMarcel Moolenaar NULL, xp, cp - xp, -1,
335531337658SMarcel Moolenaar need_enc, XF_ENC_UTF8);
3356d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_COLUMNS))
335731337658SMarcel Moolenaar xop->xo_columns += cols;
3358d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_ANCHOR))
335931337658SMarcel Moolenaar xop->xo_anchor_columns += cols;
336031337658SMarcel Moolenaar }
336131337658SMarcel Moolenaar
336231337658SMarcel Moolenaar xp = NULL;
336331337658SMarcel Moolenaar }
336431337658SMarcel Moolenaar
336531337658SMarcel Moolenaar bzero(&xf, sizeof(xf));
336631337658SMarcel Moolenaar xf.xf_leading_zero = -1;
336731337658SMarcel Moolenaar xf.xf_width[0] = xf.xf_width[1] = xf.xf_width[2] = -1;
336831337658SMarcel Moolenaar
336931337658SMarcel Moolenaar /*
337031337658SMarcel Moolenaar * "%@" starts an XO-specific set of flags:
337131337658SMarcel Moolenaar * @X@ - XML-only field; ignored if style isn't XML
337231337658SMarcel Moolenaar */
337331337658SMarcel Moolenaar if (cp[1] == '@') {
337431337658SMarcel Moolenaar for (cp += 2; cp < ep; cp++) {
337531337658SMarcel Moolenaar if (*cp == '@') {
337631337658SMarcel Moolenaar break;
337731337658SMarcel Moolenaar }
337831337658SMarcel Moolenaar if (*cp == '*') {
337931337658SMarcel Moolenaar /*
338031337658SMarcel Moolenaar * '*' means there's a "%*.*s" value in vap that
338131337658SMarcel Moolenaar * we want to ignore
338231337658SMarcel Moolenaar */
3383d1a0d267SMarcel Moolenaar if (!XOF_ISSET(xop, XOF_NO_VA_ARG))
338431337658SMarcel Moolenaar va_arg(xop->xo_vap, int);
338531337658SMarcel Moolenaar }
338631337658SMarcel Moolenaar }
338731337658SMarcel Moolenaar }
338831337658SMarcel Moolenaar
338931337658SMarcel Moolenaar /* Hidden fields are only visible to JSON and XML */
3390d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XFF_ENCODE_ONLY)) {
339131337658SMarcel Moolenaar if (style != XO_STYLE_XML
3392d1a0d267SMarcel Moolenaar && !xo_style_is_encoding(xop))
339331337658SMarcel Moolenaar xf.xf_skip = 1;
3394d1a0d267SMarcel Moolenaar } else if (XOF_ISSET(xop, XFF_DISPLAY_ONLY)) {
339531337658SMarcel Moolenaar if (style != XO_STYLE_TEXT
3396788ca347SMarcel Moolenaar && xo_style(xop) != XO_STYLE_HTML)
339731337658SMarcel Moolenaar xf.xf_skip = 1;
339831337658SMarcel Moolenaar }
339931337658SMarcel Moolenaar
340031337658SMarcel Moolenaar if (!make_output)
340131337658SMarcel Moolenaar xf.xf_skip = 1;
340231337658SMarcel Moolenaar
340331337658SMarcel Moolenaar /*
340431337658SMarcel Moolenaar * Looking at one piece of a format; find the end and
340531337658SMarcel Moolenaar * call snprintf. Then advance xo_vap on our own.
340631337658SMarcel Moolenaar *
340731337658SMarcel Moolenaar * Note that 'n', 'v', and '$' are not supported.
340831337658SMarcel Moolenaar */
340931337658SMarcel Moolenaar sp = cp; /* Save start pointer */
341031337658SMarcel Moolenaar for (cp += 1; cp < ep; cp++) {
341131337658SMarcel Moolenaar if (*cp == 'l')
341231337658SMarcel Moolenaar xf.xf_lflag += 1;
341331337658SMarcel Moolenaar else if (*cp == 'h')
341431337658SMarcel Moolenaar xf.xf_hflag += 1;
341531337658SMarcel Moolenaar else if (*cp == 'j')
341631337658SMarcel Moolenaar xf.xf_jflag += 1;
341731337658SMarcel Moolenaar else if (*cp == 't')
341831337658SMarcel Moolenaar xf.xf_tflag += 1;
341931337658SMarcel Moolenaar else if (*cp == 'z')
342031337658SMarcel Moolenaar xf.xf_zflag += 1;
342131337658SMarcel Moolenaar else if (*cp == 'q')
342231337658SMarcel Moolenaar xf.xf_qflag += 1;
342331337658SMarcel Moolenaar else if (*cp == '.') {
342431337658SMarcel Moolenaar if (++xf.xf_dots >= XF_WIDTH_NUM) {
342531337658SMarcel Moolenaar xo_failure(xop, "Too many dots in format: '%s'", fmt);
342631337658SMarcel Moolenaar return -1;
342731337658SMarcel Moolenaar }
342831337658SMarcel Moolenaar } else if (*cp == '-')
342931337658SMarcel Moolenaar xf.xf_seen_minus = 1;
343031337658SMarcel Moolenaar else if (isdigit((int) *cp)) {
343131337658SMarcel Moolenaar if (xf.xf_leading_zero < 0)
343231337658SMarcel Moolenaar xf.xf_leading_zero = (*cp == '0');
343331337658SMarcel Moolenaar xo_bump_width(&xf, *cp - '0');
343431337658SMarcel Moolenaar } else if (*cp == '*') {
343531337658SMarcel Moolenaar xf.xf_stars += 1;
343631337658SMarcel Moolenaar xf.xf_star[xf.xf_dots] = 1;
3437d1a0d267SMarcel Moolenaar } else if (strchr("diouxXDOUeEfFgGaAcCsSpm", *cp) != NULL)
343831337658SMarcel Moolenaar break;
343931337658SMarcel Moolenaar else if (*cp == 'n' || *cp == 'v') {
344031337658SMarcel Moolenaar xo_failure(xop, "unsupported format: '%s'", fmt);
344131337658SMarcel Moolenaar return -1;
344231337658SMarcel Moolenaar }
344331337658SMarcel Moolenaar }
344431337658SMarcel Moolenaar
344531337658SMarcel Moolenaar if (cp == ep)
344631337658SMarcel Moolenaar xo_failure(xop, "field format missing format character: %s",
344731337658SMarcel Moolenaar fmt);
344831337658SMarcel Moolenaar
344931337658SMarcel Moolenaar xf.xf_fc = *cp;
345031337658SMarcel Moolenaar
3451d1a0d267SMarcel Moolenaar if (!XOF_ISSET(xop, XOF_NO_VA_ARG)) {
345231337658SMarcel Moolenaar if (*cp == 's' || *cp == 'S') {
345331337658SMarcel Moolenaar /* Handle "%*.*.*s" */
345431337658SMarcel Moolenaar int s;
345531337658SMarcel Moolenaar for (s = 0; s < XF_WIDTH_NUM; s++) {
345631337658SMarcel Moolenaar if (xf.xf_star[s]) {
345731337658SMarcel Moolenaar xf.xf_width[s] = va_arg(xop->xo_vap, int);
345831337658SMarcel Moolenaar
345931337658SMarcel Moolenaar /* Normalize a negative width value */
346031337658SMarcel Moolenaar if (xf.xf_width[s] < 0) {
346131337658SMarcel Moolenaar if (s == 0) {
346231337658SMarcel Moolenaar xf.xf_width[0] = -xf.xf_width[0];
346331337658SMarcel Moolenaar xf.xf_seen_minus = 1;
346431337658SMarcel Moolenaar } else
346531337658SMarcel Moolenaar xf.xf_width[s] = -1; /* Ignore negative values */
346631337658SMarcel Moolenaar }
346731337658SMarcel Moolenaar }
346831337658SMarcel Moolenaar }
346931337658SMarcel Moolenaar }
347031337658SMarcel Moolenaar }
347131337658SMarcel Moolenaar
347231337658SMarcel Moolenaar /* If no max is given, it defaults to size */
347331337658SMarcel Moolenaar if (xf.xf_width[XF_WIDTH_MAX] < 0 && xf.xf_width[XF_WIDTH_SIZE] >= 0)
347431337658SMarcel Moolenaar xf.xf_width[XF_WIDTH_MAX] = xf.xf_width[XF_WIDTH_SIZE];
347531337658SMarcel Moolenaar
347631337658SMarcel Moolenaar if (xf.xf_fc == 'D' || xf.xf_fc == 'O' || xf.xf_fc == 'U')
347731337658SMarcel Moolenaar xf.xf_lflag = 1;
347831337658SMarcel Moolenaar
347931337658SMarcel Moolenaar if (!xf.xf_skip) {
348031337658SMarcel Moolenaar xo_buffer_t *fbp = &xop->xo_fmt;
34818a6eceffSPhil Shafer ssize_t len = cp - sp + 1;
348231337658SMarcel Moolenaar if (!xo_buf_has_room(fbp, len + 1))
348331337658SMarcel Moolenaar return -1;
348431337658SMarcel Moolenaar
348531337658SMarcel Moolenaar char *newfmt = fbp->xb_curp;
348631337658SMarcel Moolenaar memcpy(newfmt, sp, len);
348731337658SMarcel Moolenaar newfmt[0] = '%'; /* If we skipped over a "%@...@s" format */
348831337658SMarcel Moolenaar newfmt[len] = '\0';
348931337658SMarcel Moolenaar
349031337658SMarcel Moolenaar /*
349131337658SMarcel Moolenaar * Bad news: our strings are UTF-8, but the stock printf
349231337658SMarcel Moolenaar * functions won't handle field widths for wide characters
349331337658SMarcel Moolenaar * correctly. So we have to handle this ourselves.
349431337658SMarcel Moolenaar */
349531337658SMarcel Moolenaar if (xop->xo_formatter == NULL
3496d1a0d267SMarcel Moolenaar && (xf.xf_fc == 's' || xf.xf_fc == 'S'
3497d1a0d267SMarcel Moolenaar || xf.xf_fc == 'm')) {
3498d1a0d267SMarcel Moolenaar
3499d1a0d267SMarcel Moolenaar xf.xf_enc = (xf.xf_fc == 'm') ? XF_ENC_UTF8
3500d1a0d267SMarcel Moolenaar : (xf.xf_lflag || (xf.xf_fc == 'S')) ? XF_ENC_WIDE
3501d1a0d267SMarcel Moolenaar : xf.xf_hflag ? XF_ENC_LOCALE : XF_ENC_UTF8;
3502d1a0d267SMarcel Moolenaar
350331337658SMarcel Moolenaar rc = xo_format_string(xop, xbp, flags, &xf);
350431337658SMarcel Moolenaar
3505d1a0d267SMarcel Moolenaar if ((flags & XFF_TRIM_WS) && xo_style_is_encoding(xop))
350631337658SMarcel Moolenaar rc = xo_trim_ws(xbp, rc);
350731337658SMarcel Moolenaar
350831337658SMarcel Moolenaar } else {
3509f2b7bf8aSPhil Shafer ssize_t columns = rc = xo_vsnprintf(xop, xbp, newfmt,
3510f2b7bf8aSPhil Shafer xop->xo_vap);
351131337658SMarcel Moolenaar
3512406a584dSPhil Shafer if (rc > 0) {
351331337658SMarcel Moolenaar /*
351431337658SMarcel Moolenaar * For XML and HTML, we need "&<>" processing; for JSON,
351531337658SMarcel Moolenaar * it's quotes. Text gets nothing.
351631337658SMarcel Moolenaar */
351731337658SMarcel Moolenaar switch (style) {
351831337658SMarcel Moolenaar case XO_STYLE_XML:
351931337658SMarcel Moolenaar if (flags & XFF_TRIM_WS)
352031337658SMarcel Moolenaar columns = rc = xo_trim_ws(xbp, rc);
3521ee5cf116SPhil Shafer /* FALLTHRU */
352231337658SMarcel Moolenaar case XO_STYLE_HTML:
352331337658SMarcel Moolenaar rc = xo_escape_xml(xbp, rc, (flags & XFF_ATTR));
352431337658SMarcel Moolenaar break;
352531337658SMarcel Moolenaar
352631337658SMarcel Moolenaar case XO_STYLE_JSON:
352731337658SMarcel Moolenaar if (flags & XFF_TRIM_WS)
352831337658SMarcel Moolenaar columns = rc = xo_trim_ws(xbp, rc);
3529d1a0d267SMarcel Moolenaar rc = xo_escape_json(xbp, rc, 0);
3530d1a0d267SMarcel Moolenaar break;
3531d1a0d267SMarcel Moolenaar
3532d1a0d267SMarcel Moolenaar case XO_STYLE_SDPARAMS:
3533d1a0d267SMarcel Moolenaar if (flags & XFF_TRIM_WS)
3534d1a0d267SMarcel Moolenaar columns = rc = xo_trim_ws(xbp, rc);
3535d1a0d267SMarcel Moolenaar rc = xo_escape_sdparams(xbp, rc, 0);
3536d1a0d267SMarcel Moolenaar break;
3537d1a0d267SMarcel Moolenaar
3538d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER:
3539d1a0d267SMarcel Moolenaar if (flags & XFF_TRIM_WS)
3540d1a0d267SMarcel Moolenaar columns = rc = xo_trim_ws(xbp, rc);
354131337658SMarcel Moolenaar break;
354231337658SMarcel Moolenaar }
354331337658SMarcel Moolenaar
354431337658SMarcel Moolenaar /*
3545d1a0d267SMarcel Moolenaar * We can assume all the non-%s data we've
3546d1a0d267SMarcel Moolenaar * added is ASCII, so the columns and bytes are the
3547d1a0d267SMarcel Moolenaar * same. xo_format_string handles all the fancy
3548d1a0d267SMarcel Moolenaar * string conversions and updates xo_anchor_columns
3549d1a0d267SMarcel Moolenaar * accordingly.
355031337658SMarcel Moolenaar */
3551d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_COLUMNS))
355231337658SMarcel Moolenaar xop->xo_columns += columns;
3553d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_ANCHOR))
355431337658SMarcel Moolenaar xop->xo_anchor_columns += columns;
355531337658SMarcel Moolenaar }
3556406a584dSPhil Shafer }
355731337658SMarcel Moolenaar
3558406a584dSPhil Shafer if (rc > 0)
355931337658SMarcel Moolenaar xbp->xb_curp += rc;
356031337658SMarcel Moolenaar }
356131337658SMarcel Moolenaar
356231337658SMarcel Moolenaar /*
356331337658SMarcel Moolenaar * Now for the tricky part: we need to move the argument pointer
356431337658SMarcel Moolenaar * along by the amount needed.
356531337658SMarcel Moolenaar */
3566d1a0d267SMarcel Moolenaar if (!XOF_ISSET(xop, XOF_NO_VA_ARG)) {
356731337658SMarcel Moolenaar
356831337658SMarcel Moolenaar if (xf.xf_fc == 's' ||xf.xf_fc == 'S') {
356931337658SMarcel Moolenaar /*
357031337658SMarcel Moolenaar * The 'S' and 's' formats are normally handled in
357131337658SMarcel Moolenaar * xo_format_string, but if we skipped it, then we
357231337658SMarcel Moolenaar * need to pop it.
357331337658SMarcel Moolenaar */
357431337658SMarcel Moolenaar if (xf.xf_skip)
357531337658SMarcel Moolenaar va_arg(xop->xo_vap, char *);
357631337658SMarcel Moolenaar
3577d1a0d267SMarcel Moolenaar } else if (xf.xf_fc == 'm') {
3578d1a0d267SMarcel Moolenaar /* Nothing on the stack for "%m" */
3579d1a0d267SMarcel Moolenaar
358031337658SMarcel Moolenaar } else {
358131337658SMarcel Moolenaar int s;
358231337658SMarcel Moolenaar for (s = 0; s < XF_WIDTH_NUM; s++) {
358331337658SMarcel Moolenaar if (xf.xf_star[s])
358431337658SMarcel Moolenaar va_arg(xop->xo_vap, int);
358531337658SMarcel Moolenaar }
358631337658SMarcel Moolenaar
358731337658SMarcel Moolenaar if (strchr("diouxXDOU", xf.xf_fc) != NULL) {
358831337658SMarcel Moolenaar if (xf.xf_hflag > 1) {
358931337658SMarcel Moolenaar va_arg(xop->xo_vap, int);
359031337658SMarcel Moolenaar
359131337658SMarcel Moolenaar } else if (xf.xf_hflag > 0) {
359231337658SMarcel Moolenaar va_arg(xop->xo_vap, int);
359331337658SMarcel Moolenaar
359431337658SMarcel Moolenaar } else if (xf.xf_lflag > 1) {
359531337658SMarcel Moolenaar va_arg(xop->xo_vap, unsigned long long);
359631337658SMarcel Moolenaar
359731337658SMarcel Moolenaar } else if (xf.xf_lflag > 0) {
359831337658SMarcel Moolenaar va_arg(xop->xo_vap, unsigned long);
359931337658SMarcel Moolenaar
360031337658SMarcel Moolenaar } else if (xf.xf_jflag > 0) {
360131337658SMarcel Moolenaar va_arg(xop->xo_vap, intmax_t);
360231337658SMarcel Moolenaar
360331337658SMarcel Moolenaar } else if (xf.xf_tflag > 0) {
360431337658SMarcel Moolenaar va_arg(xop->xo_vap, ptrdiff_t);
360531337658SMarcel Moolenaar
360631337658SMarcel Moolenaar } else if (xf.xf_zflag > 0) {
360731337658SMarcel Moolenaar va_arg(xop->xo_vap, size_t);
360831337658SMarcel Moolenaar
360931337658SMarcel Moolenaar } else if (xf.xf_qflag > 0) {
361031337658SMarcel Moolenaar va_arg(xop->xo_vap, quad_t);
361131337658SMarcel Moolenaar
361231337658SMarcel Moolenaar } else {
361331337658SMarcel Moolenaar va_arg(xop->xo_vap, int);
361431337658SMarcel Moolenaar }
361531337658SMarcel Moolenaar } else if (strchr("eEfFgGaA", xf.xf_fc) != NULL)
361631337658SMarcel Moolenaar if (xf.xf_lflag)
361731337658SMarcel Moolenaar va_arg(xop->xo_vap, long double);
361831337658SMarcel Moolenaar else
361931337658SMarcel Moolenaar va_arg(xop->xo_vap, double);
362031337658SMarcel Moolenaar
362131337658SMarcel Moolenaar else if (xf.xf_fc == 'C' || (xf.xf_fc == 'c' && xf.xf_lflag))
362231337658SMarcel Moolenaar va_arg(xop->xo_vap, wint_t);
362331337658SMarcel Moolenaar
362431337658SMarcel Moolenaar else if (xf.xf_fc == 'c')
362531337658SMarcel Moolenaar va_arg(xop->xo_vap, int);
362631337658SMarcel Moolenaar
362731337658SMarcel Moolenaar else if (xf.xf_fc == 'p')
362831337658SMarcel Moolenaar va_arg(xop->xo_vap, void *);
362931337658SMarcel Moolenaar }
363031337658SMarcel Moolenaar }
363131337658SMarcel Moolenaar }
363231337658SMarcel Moolenaar
363331337658SMarcel Moolenaar if (xp) {
363431337658SMarcel Moolenaar if (make_output) {
363531337658SMarcel Moolenaar cols = xo_format_string_direct(xop, xbp, flags | XFF_UNESCAPE,
363631337658SMarcel Moolenaar NULL, xp, cp - xp, -1,
363731337658SMarcel Moolenaar need_enc, XF_ENC_UTF8);
3638d1a0d267SMarcel Moolenaar
3639d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_COLUMNS))
364031337658SMarcel Moolenaar xop->xo_columns += cols;
3641d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_ANCHOR))
364231337658SMarcel Moolenaar xop->xo_anchor_columns += cols;
364331337658SMarcel Moolenaar }
364431337658SMarcel Moolenaar
364531337658SMarcel Moolenaar xp = NULL;
364631337658SMarcel Moolenaar }
364731337658SMarcel Moolenaar
3648d1a0d267SMarcel Moolenaar if (flags & XFF_GT_FLAGS) {
3649d1a0d267SMarcel Moolenaar /*
3650d1a0d267SMarcel Moolenaar * Handle gettext()ing the field by looking up the value
3651d1a0d267SMarcel Moolenaar * and then copying it in, while converting to locale, if
3652d1a0d267SMarcel Moolenaar * needed.
3653d1a0d267SMarcel Moolenaar */
36548a6eceffSPhil Shafer ssize_t new_cols = xo_format_gettext(xop, flags, start_offset,
3655d1a0d267SMarcel Moolenaar old_cols, real_need_enc);
3656d1a0d267SMarcel Moolenaar
3657d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_COLUMNS))
3658d1a0d267SMarcel Moolenaar xop->xo_columns += new_cols - old_cols;
3659d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_ANCHOR))
3660d1a0d267SMarcel Moolenaar xop->xo_anchor_columns += new_cols - old_cols;
3661d1a0d267SMarcel Moolenaar }
3662d1a0d267SMarcel Moolenaar
366331337658SMarcel Moolenaar return 0;
366431337658SMarcel Moolenaar }
366531337658SMarcel Moolenaar
3666264104f2SPhil Shafer /*
3667264104f2SPhil Shafer * Remove any numeric precision/width format from the format string by
3668264104f2SPhil Shafer * inserting the "%" after the [0-9]+, returning the substring.
3669264104f2SPhil Shafer */
367031337658SMarcel Moolenaar static char *
xo_fix_encoding(xo_handle_t * xop UNUSED,char * encoding)367131337658SMarcel Moolenaar xo_fix_encoding (xo_handle_t *xop UNUSED, char *encoding)
367231337658SMarcel Moolenaar {
367331337658SMarcel Moolenaar char *cp = encoding;
367431337658SMarcel Moolenaar
367531337658SMarcel Moolenaar if (cp[0] != '%' || !isdigit((int) cp[1]))
367631337658SMarcel Moolenaar return encoding;
367731337658SMarcel Moolenaar
367831337658SMarcel Moolenaar for (cp += 2; *cp; cp++) {
367931337658SMarcel Moolenaar if (!isdigit((int) *cp))
368031337658SMarcel Moolenaar break;
368131337658SMarcel Moolenaar }
368231337658SMarcel Moolenaar
3683264104f2SPhil Shafer *--cp = '%'; /* Back off and insert the '%' */
368431337658SMarcel Moolenaar
368531337658SMarcel Moolenaar return cp;
368631337658SMarcel Moolenaar }
368731337658SMarcel Moolenaar
368831337658SMarcel Moolenaar static void
xo_color_append_html(xo_handle_t * xop)3689788ca347SMarcel Moolenaar xo_color_append_html (xo_handle_t *xop)
3690788ca347SMarcel Moolenaar {
3691788ca347SMarcel Moolenaar /*
3692788ca347SMarcel Moolenaar * If the color buffer has content, we add it now. It's already
3693788ca347SMarcel Moolenaar * prebuilt and ready, since we want to add it to every <div>.
3694788ca347SMarcel Moolenaar */
3695788ca347SMarcel Moolenaar if (!xo_buf_is_empty(&xop->xo_color_buf)) {
3696788ca347SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_color_buf;
3697788ca347SMarcel Moolenaar
3698788ca347SMarcel Moolenaar xo_data_append(xop, xbp->xb_bufp, xbp->xb_curp - xbp->xb_bufp);
3699788ca347SMarcel Moolenaar }
3700788ca347SMarcel Moolenaar }
3701788ca347SMarcel Moolenaar
3702d1a0d267SMarcel Moolenaar /*
3703d1a0d267SMarcel Moolenaar * A wrapper for humanize_number that autoscales, since the
3704d1a0d267SMarcel Moolenaar * HN_AUTOSCALE flag scales as needed based on the size of
3705d1a0d267SMarcel Moolenaar * the output buffer, not the size of the value. I also
3706d1a0d267SMarcel Moolenaar * wish HN_DECIMAL was more imperative, without the <10
3707d1a0d267SMarcel Moolenaar * test. But the boat only goes where we want when we hold
3708d1a0d267SMarcel Moolenaar * the rudder, so xo_humanize fixes part of the problem.
3709d1a0d267SMarcel Moolenaar */
37108a6eceffSPhil Shafer static ssize_t
xo_humanize(char * buf,ssize_t len,uint64_t value,int flags)37118a6eceffSPhil Shafer xo_humanize (char *buf, ssize_t len, uint64_t value, int flags)
3712d1a0d267SMarcel Moolenaar {
3713d1a0d267SMarcel Moolenaar int scale = 0;
3714d1a0d267SMarcel Moolenaar
3715d1a0d267SMarcel Moolenaar if (value) {
3716d1a0d267SMarcel Moolenaar uint64_t left = value;
3717d1a0d267SMarcel Moolenaar
3718d1a0d267SMarcel Moolenaar if (flags & HN_DIVISOR_1000) {
3719d1a0d267SMarcel Moolenaar for ( ; left; scale++)
3720d1a0d267SMarcel Moolenaar left /= 1000;
3721d1a0d267SMarcel Moolenaar } else {
3722d1a0d267SMarcel Moolenaar for ( ; left; scale++)
3723d1a0d267SMarcel Moolenaar left /= 1024;
3724d1a0d267SMarcel Moolenaar }
3725d1a0d267SMarcel Moolenaar scale -= 1;
3726d1a0d267SMarcel Moolenaar }
3727d1a0d267SMarcel Moolenaar
3728d1a0d267SMarcel Moolenaar return xo_humanize_number(buf, len, value, "", scale, flags);
3729d1a0d267SMarcel Moolenaar }
3730d1a0d267SMarcel Moolenaar
3731d1a0d267SMarcel Moolenaar /*
3732d1a0d267SMarcel Moolenaar * This is an area where we can save information from the handle for
3733d1a0d267SMarcel Moolenaar * later restoration. We need to know what data was rendered to know
3734d1a0d267SMarcel Moolenaar * what needs cleaned up.
3735d1a0d267SMarcel Moolenaar */
3736d1a0d267SMarcel Moolenaar typedef struct xo_humanize_save_s {
37378a6eceffSPhil Shafer ssize_t xhs_offset; /* Saved xo_offset */
37388a6eceffSPhil Shafer ssize_t xhs_columns; /* Saved xo_columns */
37398a6eceffSPhil Shafer ssize_t xhs_anchor_columns; /* Saved xo_anchor_columns */
3740d1a0d267SMarcel Moolenaar } xo_humanize_save_t;
3741d1a0d267SMarcel Moolenaar
3742d1a0d267SMarcel Moolenaar /*
3743d1a0d267SMarcel Moolenaar * Format a "humanized" value for a numeric, meaning something nice
3744d1a0d267SMarcel Moolenaar * like "44M" instead of "44470272". We autoscale, choosing the
3745d1a0d267SMarcel Moolenaar * most appropriate value for K/M/G/T/P/E based on the value given.
3746d1a0d267SMarcel Moolenaar */
3747d1a0d267SMarcel Moolenaar static void
xo_format_humanize(xo_handle_t * xop,xo_buffer_t * xbp,xo_humanize_save_t * savep,xo_xff_flags_t flags)3748d1a0d267SMarcel Moolenaar xo_format_humanize (xo_handle_t *xop, xo_buffer_t *xbp,
3749d1a0d267SMarcel Moolenaar xo_humanize_save_t *savep, xo_xff_flags_t flags)
3750d1a0d267SMarcel Moolenaar {
3751d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_NO_HUMANIZE))
3752d1a0d267SMarcel Moolenaar return;
3753d1a0d267SMarcel Moolenaar
37548a6eceffSPhil Shafer ssize_t end_offset = xbp->xb_curp - xbp->xb_bufp;
3755d1a0d267SMarcel Moolenaar if (end_offset == savep->xhs_offset) /* Huh? Nothing to render */
3756d1a0d267SMarcel Moolenaar return;
3757d1a0d267SMarcel Moolenaar
3758d1a0d267SMarcel Moolenaar /*
3759d1a0d267SMarcel Moolenaar * We have a string that's allegedly a number. We want to
3760d1a0d267SMarcel Moolenaar * humanize it, which means turning it back into a number
3761d1a0d267SMarcel Moolenaar * and calling xo_humanize_number on it.
3762d1a0d267SMarcel Moolenaar */
3763d1a0d267SMarcel Moolenaar uint64_t value;
3764d1a0d267SMarcel Moolenaar char *ep;
3765d1a0d267SMarcel Moolenaar
3766d1a0d267SMarcel Moolenaar xo_buf_append(xbp, "", 1); /* NUL-terminate it */
3767d1a0d267SMarcel Moolenaar
3768d1a0d267SMarcel Moolenaar value = strtoull(xbp->xb_bufp + savep->xhs_offset, &ep, 0);
3769d1a0d267SMarcel Moolenaar if (!(value == ULLONG_MAX && errno == ERANGE)
3770d1a0d267SMarcel Moolenaar && (ep != xbp->xb_bufp + savep->xhs_offset)) {
3771d1a0d267SMarcel Moolenaar /*
3772d1a0d267SMarcel Moolenaar * There are few values where humanize_number needs
3773d1a0d267SMarcel Moolenaar * more bytes than the original value. I've used
3774d1a0d267SMarcel Moolenaar * 10 as a rectal number to cover those scenarios.
3775d1a0d267SMarcel Moolenaar */
3776d1a0d267SMarcel Moolenaar if (xo_buf_has_room(xbp, 10)) {
3777d1a0d267SMarcel Moolenaar xbp->xb_curp = xbp->xb_bufp + savep->xhs_offset;
3778d1a0d267SMarcel Moolenaar
37798a6eceffSPhil Shafer ssize_t rc;
37808a6eceffSPhil Shafer ssize_t left = (xbp->xb_bufp + xbp->xb_size) - xbp->xb_curp;
3781d1a0d267SMarcel Moolenaar int hn_flags = HN_NOSPACE; /* On by default */
3782d1a0d267SMarcel Moolenaar
3783d1a0d267SMarcel Moolenaar if (flags & XFF_HN_SPACE)
3784d1a0d267SMarcel Moolenaar hn_flags &= ~HN_NOSPACE;
3785d1a0d267SMarcel Moolenaar
3786d1a0d267SMarcel Moolenaar if (flags & XFF_HN_DECIMAL)
3787d1a0d267SMarcel Moolenaar hn_flags |= HN_DECIMAL;
3788d1a0d267SMarcel Moolenaar
3789d1a0d267SMarcel Moolenaar if (flags & XFF_HN_1000)
3790d1a0d267SMarcel Moolenaar hn_flags |= HN_DIVISOR_1000;
3791d1a0d267SMarcel Moolenaar
37928a6eceffSPhil Shafer rc = xo_humanize(xbp->xb_curp, left, value, hn_flags);
3793d1a0d267SMarcel Moolenaar if (rc > 0) {
3794d1a0d267SMarcel Moolenaar xbp->xb_curp += rc;
3795d1a0d267SMarcel Moolenaar xop->xo_columns = savep->xhs_columns + rc;
3796d1a0d267SMarcel Moolenaar xop->xo_anchor_columns = savep->xhs_anchor_columns + rc;
3797d1a0d267SMarcel Moolenaar }
3798d1a0d267SMarcel Moolenaar }
3799d1a0d267SMarcel Moolenaar }
3800d1a0d267SMarcel Moolenaar }
3801d1a0d267SMarcel Moolenaar
3802264104f2SPhil Shafer /*
3803264104f2SPhil Shafer * Convenience function that either append a fixed value (if one is
3804264104f2SPhil Shafer * given) or formats a field using a format string. If it's
3805264104f2SPhil Shafer * encode_only, then we can't skip formatting the field, since it may
3806264104f2SPhil Shafer * be pulling arguments off the stack.
3807264104f2SPhil Shafer */
3808264104f2SPhil Shafer static inline void
xo_simple_field(xo_handle_t * xop,unsigned encode_only,const char * value,ssize_t vlen,const char * fmt,ssize_t flen,xo_xff_flags_t flags)3809264104f2SPhil Shafer xo_simple_field (xo_handle_t *xop, unsigned encode_only,
3810264104f2SPhil Shafer const char *value, ssize_t vlen,
3811264104f2SPhil Shafer const char *fmt, ssize_t flen, xo_xff_flags_t flags)
3812264104f2SPhil Shafer {
3813264104f2SPhil Shafer if (encode_only)
3814264104f2SPhil Shafer flags |= XFF_NO_OUTPUT;
3815264104f2SPhil Shafer
3816264104f2SPhil Shafer if (vlen == 0)
3817264104f2SPhil Shafer xo_do_format_field(xop, NULL, fmt, flen, flags);
3818264104f2SPhil Shafer else if (!encode_only)
3819264104f2SPhil Shafer xo_data_append_content(xop, value, vlen, flags);
3820264104f2SPhil Shafer }
3821264104f2SPhil Shafer
3822264104f2SPhil Shafer /*
3823264104f2SPhil Shafer * Html mode: append a <div> to the output buffer contain a field
3824264104f2SPhil Shafer * along with all the supporting information indicated by the flags.
3825264104f2SPhil Shafer */
3826788ca347SMarcel Moolenaar static void
xo_buf_append_div(xo_handle_t * xop,const char * class,xo_xff_flags_t flags,const char * name,ssize_t nlen,const char * value,ssize_t vlen,const char * fmt,ssize_t flen,const char * encoding,ssize_t elen)382731337658SMarcel Moolenaar xo_buf_append_div (xo_handle_t *xop, const char *class, xo_xff_flags_t flags,
38288a6eceffSPhil Shafer const char *name, ssize_t nlen,
38298a6eceffSPhil Shafer const char *value, ssize_t vlen,
3830264104f2SPhil Shafer const char *fmt, ssize_t flen,
38318a6eceffSPhil Shafer const char *encoding, ssize_t elen)
383231337658SMarcel Moolenaar {
383331337658SMarcel Moolenaar static char div_start[] = "<div class=\"";
383431337658SMarcel Moolenaar static char div_tag[] = "\" data-tag=\"";
383531337658SMarcel Moolenaar static char div_xpath[] = "\" data-xpath=\"";
383631337658SMarcel Moolenaar static char div_key[] = "\" data-key=\"key";
383731337658SMarcel Moolenaar static char div_end[] = "\">";
383831337658SMarcel Moolenaar static char div_close[] = "</div>";
383931337658SMarcel Moolenaar
3840a321cc5dSPhil Shafer /* The encoding format defaults to the normal format */
3841264104f2SPhil Shafer if (encoding == NULL && fmt != NULL) {
3842264104f2SPhil Shafer char *enc = alloca(flen + 1);
3843264104f2SPhil Shafer memcpy(enc, fmt, flen);
3844264104f2SPhil Shafer enc[flen] = '\0';
3845a321cc5dSPhil Shafer encoding = xo_fix_encoding(xop, enc);
3846a321cc5dSPhil Shafer elen = strlen(encoding);
3847a321cc5dSPhil Shafer }
3848a321cc5dSPhil Shafer
384931337658SMarcel Moolenaar /*
385031337658SMarcel Moolenaar * To build our XPath predicate, we need to save the va_list before
385131337658SMarcel Moolenaar * we format our data, and then restore it before we format the
385231337658SMarcel Moolenaar * xpath expression.
385331337658SMarcel Moolenaar * Display-only keys implies that we've got an encode-only key
385431337658SMarcel Moolenaar * elsewhere, so we don't use them from making predicates.
385531337658SMarcel Moolenaar */
385631337658SMarcel Moolenaar int need_predidate =
385731337658SMarcel Moolenaar (name && (flags & XFF_KEY) && !(flags & XFF_DISPLAY_ONLY)
38588a6eceffSPhil Shafer && XOF_ISSET(xop, XOF_XPATH)) ? 1 : 0;
385931337658SMarcel Moolenaar
386031337658SMarcel Moolenaar if (need_predidate) {
386131337658SMarcel Moolenaar va_list va_local;
386231337658SMarcel Moolenaar
386331337658SMarcel Moolenaar va_copy(va_local, xop->xo_vap);
386431337658SMarcel Moolenaar if (xop->xo_checkpointer)
386531337658SMarcel Moolenaar xop->xo_checkpointer(xop, xop->xo_vap, 0);
386631337658SMarcel Moolenaar
386731337658SMarcel Moolenaar /*
386831337658SMarcel Moolenaar * Build an XPath predicate expression to match this key.
386931337658SMarcel Moolenaar * We use the format buffer.
387031337658SMarcel Moolenaar */
387131337658SMarcel Moolenaar xo_buffer_t *pbp = &xop->xo_predicate;
387231337658SMarcel Moolenaar pbp->xb_curp = pbp->xb_bufp; /* Restart buffer */
387331337658SMarcel Moolenaar
387431337658SMarcel Moolenaar xo_buf_append(pbp, "[", 1);
387531337658SMarcel Moolenaar xo_buf_escape(xop, pbp, name, nlen, 0);
3876d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_PRETTY))
387731337658SMarcel Moolenaar xo_buf_append(pbp, " = '", 4);
387831337658SMarcel Moolenaar else
387931337658SMarcel Moolenaar xo_buf_append(pbp, "='", 2);
388031337658SMarcel Moolenaar
3881d1a0d267SMarcel Moolenaar xo_xff_flags_t pflags = flags | XFF_XML | XFF_ATTR;
3882d1a0d267SMarcel Moolenaar pflags &= ~(XFF_NO_OUTPUT | XFF_ENCODE_ONLY);
3883d1a0d267SMarcel Moolenaar xo_do_format_field(xop, pbp, encoding, elen, pflags);
388431337658SMarcel Moolenaar
388531337658SMarcel Moolenaar xo_buf_append(pbp, "']", 2);
388631337658SMarcel Moolenaar
388731337658SMarcel Moolenaar /* Now we record this predicate expression in the stack */
388831337658SMarcel Moolenaar xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth];
38898a6eceffSPhil Shafer ssize_t olen = xsp->xs_keys ? strlen(xsp->xs_keys) : 0;
38908a6eceffSPhil Shafer ssize_t dlen = pbp->xb_curp - pbp->xb_bufp;
389131337658SMarcel Moolenaar
389231337658SMarcel Moolenaar char *cp = xo_realloc(xsp->xs_keys, olen + dlen + 1);
389331337658SMarcel Moolenaar if (cp) {
389431337658SMarcel Moolenaar memcpy(cp + olen, pbp->xb_bufp, dlen);
389531337658SMarcel Moolenaar cp[olen + dlen] = '\0';
389631337658SMarcel Moolenaar xsp->xs_keys = cp;
389731337658SMarcel Moolenaar }
389831337658SMarcel Moolenaar
389931337658SMarcel Moolenaar /* Now we reset the xo_vap as if we were never here */
390031337658SMarcel Moolenaar va_end(xop->xo_vap);
390131337658SMarcel Moolenaar va_copy(xop->xo_vap, va_local);
390231337658SMarcel Moolenaar va_end(va_local);
390331337658SMarcel Moolenaar if (xop->xo_checkpointer)
390431337658SMarcel Moolenaar xop->xo_checkpointer(xop, xop->xo_vap, 1);
390531337658SMarcel Moolenaar }
390631337658SMarcel Moolenaar
390731337658SMarcel Moolenaar if (flags & XFF_ENCODE_ONLY) {
390831337658SMarcel Moolenaar /*
3909ee5cf116SPhil Shafer * Even if this is encode-only, we need to go through the
391031337658SMarcel Moolenaar * work of formatting it to make sure the args are cleared
3911264104f2SPhil Shafer * from xo_vap. This is not true when vlen is zero, since
3912264104f2SPhil Shafer * that means our "value" isn't on the stack.
391331337658SMarcel Moolenaar */
3914264104f2SPhil Shafer xo_simple_field(xop, TRUE, NULL, 0, encoding, elen, flags);
391531337658SMarcel Moolenaar return;
391631337658SMarcel Moolenaar }
391731337658SMarcel Moolenaar
391831337658SMarcel Moolenaar xo_line_ensure_open(xop, 0);
391931337658SMarcel Moolenaar
3920d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_PRETTY))
392131337658SMarcel Moolenaar xo_buf_indent(xop, xop->xo_indent_by);
392231337658SMarcel Moolenaar
392331337658SMarcel Moolenaar xo_data_append(xop, div_start, sizeof(div_start) - 1);
392431337658SMarcel Moolenaar xo_data_append(xop, class, strlen(class));
392531337658SMarcel Moolenaar
3926788ca347SMarcel Moolenaar /*
3927788ca347SMarcel Moolenaar * If the color buffer has content, we add it now. It's already
3928788ca347SMarcel Moolenaar * prebuilt and ready, since we want to add it to every <div>.
3929788ca347SMarcel Moolenaar */
3930788ca347SMarcel Moolenaar if (!xo_buf_is_empty(&xop->xo_color_buf)) {
3931788ca347SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_color_buf;
3932788ca347SMarcel Moolenaar
3933788ca347SMarcel Moolenaar xo_data_append(xop, xbp->xb_bufp, xbp->xb_curp - xbp->xb_bufp);
3934788ca347SMarcel Moolenaar }
3935788ca347SMarcel Moolenaar
393631337658SMarcel Moolenaar if (name) {
393731337658SMarcel Moolenaar xo_data_append(xop, div_tag, sizeof(div_tag) - 1);
393831337658SMarcel Moolenaar xo_data_escape(xop, name, nlen);
393931337658SMarcel Moolenaar
394031337658SMarcel Moolenaar /*
394131337658SMarcel Moolenaar * Save the offset at which we'd place units. See xo_format_units.
394231337658SMarcel Moolenaar */
3943d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_UNITS)) {
3944d1a0d267SMarcel Moolenaar XOIF_SET(xop, XOIF_UNITS_PENDING);
394531337658SMarcel Moolenaar /*
394631337658SMarcel Moolenaar * Note: We need the '+1' here because we know we've not
394731337658SMarcel Moolenaar * added the closing quote. We add one, knowing the quote
394831337658SMarcel Moolenaar * will be added shortly.
394931337658SMarcel Moolenaar */
395031337658SMarcel Moolenaar xop->xo_units_offset =
395131337658SMarcel Moolenaar xop->xo_data.xb_curp -xop->xo_data.xb_bufp + 1;
395231337658SMarcel Moolenaar }
395331337658SMarcel Moolenaar
3954d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_XPATH)) {
395531337658SMarcel Moolenaar int i;
395631337658SMarcel Moolenaar xo_stack_t *xsp;
395731337658SMarcel Moolenaar
395831337658SMarcel Moolenaar xo_data_append(xop, div_xpath, sizeof(div_xpath) - 1);
395931337658SMarcel Moolenaar if (xop->xo_leading_xpath)
396031337658SMarcel Moolenaar xo_data_append(xop, xop->xo_leading_xpath,
396131337658SMarcel Moolenaar strlen(xop->xo_leading_xpath));
396231337658SMarcel Moolenaar
396331337658SMarcel Moolenaar for (i = 0; i <= xop->xo_depth; i++) {
396431337658SMarcel Moolenaar xsp = &xop->xo_stack[i];
396531337658SMarcel Moolenaar if (xsp->xs_name == NULL)
396631337658SMarcel Moolenaar continue;
396731337658SMarcel Moolenaar
3968545ddfbeSMarcel Moolenaar /*
3969545ddfbeSMarcel Moolenaar * XSS_OPEN_LIST and XSS_OPEN_LEAF_LIST stack frames
3970545ddfbeSMarcel Moolenaar * are directly under XSS_OPEN_INSTANCE frames so we
3971545ddfbeSMarcel Moolenaar * don't need to put these in our XPath expressions.
3972545ddfbeSMarcel Moolenaar */
3973545ddfbeSMarcel Moolenaar if (xsp->xs_state == XSS_OPEN_LIST
3974545ddfbeSMarcel Moolenaar || xsp->xs_state == XSS_OPEN_LEAF_LIST)
3975545ddfbeSMarcel Moolenaar continue;
3976545ddfbeSMarcel Moolenaar
397731337658SMarcel Moolenaar xo_data_append(xop, "/", 1);
397831337658SMarcel Moolenaar xo_data_escape(xop, xsp->xs_name, strlen(xsp->xs_name));
397931337658SMarcel Moolenaar if (xsp->xs_keys) {
398031337658SMarcel Moolenaar /* Don't show keys for the key field */
398131337658SMarcel Moolenaar if (i != xop->xo_depth || !(flags & XFF_KEY))
398231337658SMarcel Moolenaar xo_data_append(xop, xsp->xs_keys, strlen(xsp->xs_keys));
398331337658SMarcel Moolenaar }
398431337658SMarcel Moolenaar }
398531337658SMarcel Moolenaar
398631337658SMarcel Moolenaar xo_data_append(xop, "/", 1);
398731337658SMarcel Moolenaar xo_data_escape(xop, name, nlen);
398831337658SMarcel Moolenaar }
398931337658SMarcel Moolenaar
3990d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_INFO) && xop->xo_info) {
399131337658SMarcel Moolenaar static char in_type[] = "\" data-type=\"";
399231337658SMarcel Moolenaar static char in_help[] = "\" data-help=\"";
399331337658SMarcel Moolenaar
399431337658SMarcel Moolenaar xo_info_t *xip = xo_info_find(xop, name, nlen);
399531337658SMarcel Moolenaar if (xip) {
399631337658SMarcel Moolenaar if (xip->xi_type) {
399731337658SMarcel Moolenaar xo_data_append(xop, in_type, sizeof(in_type) - 1);
399831337658SMarcel Moolenaar xo_data_escape(xop, xip->xi_type, strlen(xip->xi_type));
399931337658SMarcel Moolenaar }
400031337658SMarcel Moolenaar if (xip->xi_help) {
400131337658SMarcel Moolenaar xo_data_append(xop, in_help, sizeof(in_help) - 1);
400231337658SMarcel Moolenaar xo_data_escape(xop, xip->xi_help, strlen(xip->xi_help));
400331337658SMarcel Moolenaar }
400431337658SMarcel Moolenaar }
400531337658SMarcel Moolenaar }
400631337658SMarcel Moolenaar
4007d1a0d267SMarcel Moolenaar if ((flags & XFF_KEY) && XOF_ISSET(xop, XOF_KEYS))
400831337658SMarcel Moolenaar xo_data_append(xop, div_key, sizeof(div_key) - 1);
400931337658SMarcel Moolenaar }
401031337658SMarcel Moolenaar
4011d1a0d267SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_data;
40128a6eceffSPhil Shafer ssize_t base_offset = xbp->xb_curp - xbp->xb_bufp;
4013d1a0d267SMarcel Moolenaar
401431337658SMarcel Moolenaar xo_data_append(xop, div_end, sizeof(div_end) - 1);
401531337658SMarcel Moolenaar
4016d1a0d267SMarcel Moolenaar xo_humanize_save_t save; /* Save values for humanizing logic */
4017d1a0d267SMarcel Moolenaar
4018d1a0d267SMarcel Moolenaar save.xhs_offset = xbp->xb_curp - xbp->xb_bufp;
4019d1a0d267SMarcel Moolenaar save.xhs_columns = xop->xo_columns;
4020d1a0d267SMarcel Moolenaar save.xhs_anchor_columns = xop->xo_anchor_columns;
4021d1a0d267SMarcel Moolenaar
4022264104f2SPhil Shafer xo_simple_field(xop, FALSE, value, vlen, fmt, flen, flags);
4023d1a0d267SMarcel Moolenaar
4024d1a0d267SMarcel Moolenaar if (flags & XFF_HUMANIZE) {
4025d1a0d267SMarcel Moolenaar /*
4026d1a0d267SMarcel Moolenaar * Unlike text style, we want to retain the original value and
4027d1a0d267SMarcel Moolenaar * stuff it into the "data-number" attribute.
4028d1a0d267SMarcel Moolenaar */
4029d1a0d267SMarcel Moolenaar static const char div_number[] = "\" data-number=\"";
40308a6eceffSPhil Shafer ssize_t div_len = sizeof(div_number) - 1;
4031d1a0d267SMarcel Moolenaar
40328a6eceffSPhil Shafer ssize_t end_offset = xbp->xb_curp - xbp->xb_bufp;
40338a6eceffSPhil Shafer ssize_t olen = end_offset - save.xhs_offset;
4034d1a0d267SMarcel Moolenaar
4035d1a0d267SMarcel Moolenaar char *cp = alloca(olen + 1);
4036d1a0d267SMarcel Moolenaar memcpy(cp, xbp->xb_bufp + save.xhs_offset, olen);
4037d1a0d267SMarcel Moolenaar cp[olen] = '\0';
4038d1a0d267SMarcel Moolenaar
4039d1a0d267SMarcel Moolenaar xo_format_humanize(xop, xbp, &save, flags);
4040d1a0d267SMarcel Moolenaar
4041d1a0d267SMarcel Moolenaar if (xo_buf_has_room(xbp, div_len + olen)) {
40428a6eceffSPhil Shafer ssize_t new_offset = xbp->xb_curp - xbp->xb_bufp;
4043d1a0d267SMarcel Moolenaar
4044d1a0d267SMarcel Moolenaar
4045d1a0d267SMarcel Moolenaar /* Move the humanized string off to the left */
4046d1a0d267SMarcel Moolenaar memmove(xbp->xb_bufp + base_offset + div_len + olen,
4047d1a0d267SMarcel Moolenaar xbp->xb_bufp + base_offset, new_offset - base_offset);
4048d1a0d267SMarcel Moolenaar
4049d1a0d267SMarcel Moolenaar /* Copy the data_number attribute name */
4050d1a0d267SMarcel Moolenaar memcpy(xbp->xb_bufp + base_offset, div_number, div_len);
4051d1a0d267SMarcel Moolenaar
4052d1a0d267SMarcel Moolenaar /* Copy the original long value */
4053d1a0d267SMarcel Moolenaar memcpy(xbp->xb_bufp + base_offset + div_len, cp, olen);
4054d1a0d267SMarcel Moolenaar xbp->xb_curp += div_len + olen;
4055d1a0d267SMarcel Moolenaar }
4056d1a0d267SMarcel Moolenaar }
405731337658SMarcel Moolenaar
405831337658SMarcel Moolenaar xo_data_append(xop, div_close, sizeof(div_close) - 1);
405931337658SMarcel Moolenaar
4060d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_PRETTY))
406131337658SMarcel Moolenaar xo_data_append(xop, "\n", 1);
406231337658SMarcel Moolenaar }
406331337658SMarcel Moolenaar
406431337658SMarcel Moolenaar static void
xo_format_text(xo_handle_t * xop,const char * str,ssize_t len)40658a6eceffSPhil Shafer xo_format_text (xo_handle_t *xop, const char *str, ssize_t len)
406631337658SMarcel Moolenaar {
4067788ca347SMarcel Moolenaar switch (xo_style(xop)) {
406831337658SMarcel Moolenaar case XO_STYLE_TEXT:
406931337658SMarcel Moolenaar xo_buf_append_locale(xop, &xop->xo_data, str, len);
407031337658SMarcel Moolenaar break;
407131337658SMarcel Moolenaar
407231337658SMarcel Moolenaar case XO_STYLE_HTML:
4073264104f2SPhil Shafer xo_buf_append_div(xop, "text", 0, NULL, 0, str, len, NULL, 0, NULL, 0);
407431337658SMarcel Moolenaar break;
407531337658SMarcel Moolenaar }
407631337658SMarcel Moolenaar }
407731337658SMarcel Moolenaar
407831337658SMarcel Moolenaar static void
xo_format_title(xo_handle_t * xop,xo_field_info_t * xfip,const char * value,ssize_t vlen)407942ff34c3SPhil Shafer xo_format_title (xo_handle_t *xop, xo_field_info_t *xfip,
4080264104f2SPhil Shafer const char *value, ssize_t vlen)
408131337658SMarcel Moolenaar {
4082d1a0d267SMarcel Moolenaar const char *fmt = xfip->xfi_format;
40838a6eceffSPhil Shafer ssize_t flen = xfip->xfi_flen;
4084d1a0d267SMarcel Moolenaar xo_xff_flags_t flags = xfip->xfi_flags;
4085d1a0d267SMarcel Moolenaar
4086788ca347SMarcel Moolenaar static char div_open[] = "<div class=\"title";
4087788ca347SMarcel Moolenaar static char div_middle[] = "\">";
408831337658SMarcel Moolenaar static char div_close[] = "</div>";
408931337658SMarcel Moolenaar
4090545ddfbeSMarcel Moolenaar if (flen == 0) {
4091545ddfbeSMarcel Moolenaar fmt = "%s";
4092545ddfbeSMarcel Moolenaar flen = 2;
4093545ddfbeSMarcel Moolenaar }
4094545ddfbeSMarcel Moolenaar
4095788ca347SMarcel Moolenaar switch (xo_style(xop)) {
409631337658SMarcel Moolenaar case XO_STYLE_XML:
409731337658SMarcel Moolenaar case XO_STYLE_JSON:
4098d1a0d267SMarcel Moolenaar case XO_STYLE_SDPARAMS:
4099d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER:
410031337658SMarcel Moolenaar /*
410131337658SMarcel Moolenaar * Even though we don't care about text, we need to do
410231337658SMarcel Moolenaar * enough parsing work to skip over the right bits of xo_vap.
410331337658SMarcel Moolenaar */
4104264104f2SPhil Shafer xo_simple_field(xop, TRUE, value, vlen, fmt, flen, flags);
410531337658SMarcel Moolenaar return;
410631337658SMarcel Moolenaar }
410731337658SMarcel Moolenaar
410831337658SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_data;
41098a6eceffSPhil Shafer ssize_t start = xbp->xb_curp - xbp->xb_bufp;
41108a6eceffSPhil Shafer ssize_t left = xbp->xb_size - start;
41118a6eceffSPhil Shafer ssize_t rc;
411231337658SMarcel Moolenaar
4113788ca347SMarcel Moolenaar if (xo_style(xop) == XO_STYLE_HTML) {
411431337658SMarcel Moolenaar xo_line_ensure_open(xop, 0);
4115d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_PRETTY))
411631337658SMarcel Moolenaar xo_buf_indent(xop, xop->xo_indent_by);
411731337658SMarcel Moolenaar xo_buf_append(&xop->xo_data, div_open, sizeof(div_open) - 1);
4118788ca347SMarcel Moolenaar xo_color_append_html(xop);
4119788ca347SMarcel Moolenaar xo_buf_append(&xop->xo_data, div_middle, sizeof(div_middle) - 1);
412031337658SMarcel Moolenaar }
412131337658SMarcel Moolenaar
412231337658SMarcel Moolenaar start = xbp->xb_curp - xbp->xb_bufp; /* Reset start */
4123264104f2SPhil Shafer if (vlen) {
412431337658SMarcel Moolenaar char *newfmt = alloca(flen + 1);
412531337658SMarcel Moolenaar memcpy(newfmt, fmt, flen);
412631337658SMarcel Moolenaar newfmt[flen] = '\0';
412731337658SMarcel Moolenaar
412831337658SMarcel Moolenaar /* If len is non-zero, the format string apply to the name */
4129264104f2SPhil Shafer char *newstr = alloca(vlen + 1);
4130264104f2SPhil Shafer memcpy(newstr, value, vlen);
4131264104f2SPhil Shafer newstr[vlen] = '\0';
413231337658SMarcel Moolenaar
4133264104f2SPhil Shafer if (newstr[vlen - 1] == 's') {
413431337658SMarcel Moolenaar char *bp;
413531337658SMarcel Moolenaar
413631337658SMarcel Moolenaar rc = snprintf(NULL, 0, newfmt, newstr);
413731337658SMarcel Moolenaar if (rc > 0) {
413831337658SMarcel Moolenaar /*
413931337658SMarcel Moolenaar * We have to do this the hard way, since we might need
414031337658SMarcel Moolenaar * the columns.
414131337658SMarcel Moolenaar */
414231337658SMarcel Moolenaar bp = alloca(rc + 1);
414331337658SMarcel Moolenaar rc = snprintf(bp, rc + 1, newfmt, newstr);
4144d1a0d267SMarcel Moolenaar
4145d1a0d267SMarcel Moolenaar xo_data_append_content(xop, bp, rc, flags);
414631337658SMarcel Moolenaar }
414731337658SMarcel Moolenaar goto move_along;
414831337658SMarcel Moolenaar
414931337658SMarcel Moolenaar } else {
415031337658SMarcel Moolenaar rc = snprintf(xbp->xb_curp, left, newfmt, newstr);
4151d1a0d267SMarcel Moolenaar if (rc >= left) {
415231337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, rc))
415331337658SMarcel Moolenaar return;
415431337658SMarcel Moolenaar left = xbp->xb_size - (xbp->xb_curp - xbp->xb_bufp);
415531337658SMarcel Moolenaar rc = snprintf(xbp->xb_curp, left, newfmt, newstr);
415631337658SMarcel Moolenaar }
415731337658SMarcel Moolenaar
415831337658SMarcel Moolenaar if (rc > 0) {
4159d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_COLUMNS))
416031337658SMarcel Moolenaar xop->xo_columns += rc;
4161d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_ANCHOR))
416231337658SMarcel Moolenaar xop->xo_anchor_columns += rc;
416331337658SMarcel Moolenaar }
416431337658SMarcel Moolenaar }
416531337658SMarcel Moolenaar
416631337658SMarcel Moolenaar } else {
4167d1a0d267SMarcel Moolenaar xo_do_format_field(xop, NULL, fmt, flen, flags);
416831337658SMarcel Moolenaar
4169d1a0d267SMarcel Moolenaar /* xo_do_format_field moved curp, so we need to reset it */
417031337658SMarcel Moolenaar rc = xbp->xb_curp - (xbp->xb_bufp + start);
417131337658SMarcel Moolenaar xbp->xb_curp = xbp->xb_bufp + start;
417231337658SMarcel Moolenaar }
417331337658SMarcel Moolenaar
417431337658SMarcel Moolenaar /* If we're styling HTML, then we need to escape it */
4175788ca347SMarcel Moolenaar if (xo_style(xop) == XO_STYLE_HTML) {
417631337658SMarcel Moolenaar rc = xo_escape_xml(xbp, rc, 0);
417731337658SMarcel Moolenaar }
417831337658SMarcel Moolenaar
417931337658SMarcel Moolenaar if (rc > 0)
418031337658SMarcel Moolenaar xbp->xb_curp += rc;
418131337658SMarcel Moolenaar
418231337658SMarcel Moolenaar move_along:
4183788ca347SMarcel Moolenaar if (xo_style(xop) == XO_STYLE_HTML) {
418431337658SMarcel Moolenaar xo_data_append(xop, div_close, sizeof(div_close) - 1);
4185d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_PRETTY))
418631337658SMarcel Moolenaar xo_data_append(xop, "\n", 1);
418731337658SMarcel Moolenaar }
418831337658SMarcel Moolenaar }
418931337658SMarcel Moolenaar
4190983afe33SPhil Shafer /*
4191983afe33SPhil Shafer * strspn() with a string length
4192983afe33SPhil Shafer */
4193983afe33SPhil Shafer static ssize_t
xo_strnspn(const char * str,size_t len,const char * accept)4194983afe33SPhil Shafer xo_strnspn (const char *str, size_t len, const char *accept)
4195983afe33SPhil Shafer {
4196983afe33SPhil Shafer ssize_t i;
4197983afe33SPhil Shafer const char *cp, *ep;
4198983afe33SPhil Shafer
4199983afe33SPhil Shafer for (i = 0, cp = str, ep = str + len; cp < ep && *cp != '\0'; i++, cp++) {
4200983afe33SPhil Shafer if (strchr(accept, *cp) == NULL)
4201983afe33SPhil Shafer break;
4202983afe33SPhil Shafer }
4203983afe33SPhil Shafer
4204983afe33SPhil Shafer return i;
4205983afe33SPhil Shafer }
4206983afe33SPhil Shafer
4207983afe33SPhil Shafer /*
4208983afe33SPhil Shafer * Decide if a format string should be considered "numeric",
4209983afe33SPhil Shafer * in the sense that the number does not need to be quoted.
4210983afe33SPhil Shafer * This means that it consists only of a single numeric field
4211983afe33SPhil Shafer * with nothing exotic or "interesting". This means that
4212983afe33SPhil Shafer * static values are never considered numeric.
4213983afe33SPhil Shafer */
4214983afe33SPhil Shafer static int
xo_format_is_numeric(const char * fmt,ssize_t flen)4215983afe33SPhil Shafer xo_format_is_numeric (const char *fmt, ssize_t flen)
4216983afe33SPhil Shafer {
4217983afe33SPhil Shafer if (flen <= 0 || *fmt++ != '%') /* Must start with '%' */
4218983afe33SPhil Shafer return FALSE;
4219983afe33SPhil Shafer flen -= 1;
4220983afe33SPhil Shafer
4221983afe33SPhil Shafer /* Handle leading flags; don't want "#" since JSON can't handle hex */
4222983afe33SPhil Shafer ssize_t spn = xo_strnspn(fmt, flen, "0123456789.*+ -");
4223983afe33SPhil Shafer if (spn >= flen)
4224983afe33SPhil Shafer return FALSE;
4225983afe33SPhil Shafer
4226983afe33SPhil Shafer fmt += spn; /* Move along the input string */
4227983afe33SPhil Shafer flen -= spn;
4228983afe33SPhil Shafer
4229983afe33SPhil Shafer /* Handle the length modifiers */
4230983afe33SPhil Shafer spn = xo_strnspn(fmt, flen, "hljtqz");
4231983afe33SPhil Shafer if (spn >= flen)
4232983afe33SPhil Shafer return FALSE;
4233983afe33SPhil Shafer
4234983afe33SPhil Shafer fmt += spn; /* Move along the input string */
4235983afe33SPhil Shafer flen -= spn;
4236983afe33SPhil Shafer
4237983afe33SPhil Shafer if (flen != 1) /* Should only be one character left */
4238983afe33SPhil Shafer return FALSE;
4239983afe33SPhil Shafer
4240983afe33SPhil Shafer return (strchr("diouDOUeEfFgG", *fmt) == NULL) ? FALSE : TRUE;
4241983afe33SPhil Shafer }
4242983afe33SPhil Shafer
4243406a584dSPhil Shafer /*
4244406a584dSPhil Shafer * Update the stack flags using the object flags, allowing callers
4245406a584dSPhil Shafer * to monkey with the stack flags without even knowing they exist.
4246406a584dSPhil Shafer */
4247406a584dSPhil Shafer static void
xo_stack_set_flags(xo_handle_t * xop)4248406a584dSPhil Shafer xo_stack_set_flags (xo_handle_t *xop)
4249406a584dSPhil Shafer {
4250406a584dSPhil Shafer if (XOF_ISSET(xop, XOF_NOT_FIRST)) {
4251406a584dSPhil Shafer xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth];
4252406a584dSPhil Shafer
4253406a584dSPhil Shafer xsp->xs_flags |= XSF_NOT_FIRST;
4254406a584dSPhil Shafer XOF_CLEAR(xop, XOF_NOT_FIRST);
4255406a584dSPhil Shafer }
4256406a584dSPhil Shafer }
4257406a584dSPhil Shafer
425831337658SMarcel Moolenaar static void
xo_format_prep(xo_handle_t * xop,xo_xff_flags_t flags)425931337658SMarcel Moolenaar xo_format_prep (xo_handle_t *xop, xo_xff_flags_t flags)
426031337658SMarcel Moolenaar {
426131337658SMarcel Moolenaar if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST) {
426231337658SMarcel Moolenaar xo_data_append(xop, ",", 1);
4263d1a0d267SMarcel Moolenaar if (!(flags & XFF_LEAF_LIST) && XOF_ISSET(xop, XOF_PRETTY))
426431337658SMarcel Moolenaar xo_data_append(xop, "\n", 1);
426531337658SMarcel Moolenaar } else
426631337658SMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST;
426731337658SMarcel Moolenaar }
426831337658SMarcel Moolenaar
426931337658SMarcel Moolenaar #if 0
427031337658SMarcel Moolenaar /* Useful debugging function */
427131337658SMarcel Moolenaar void
427231337658SMarcel Moolenaar xo_arg (xo_handle_t *xop);
427331337658SMarcel Moolenaar void
427431337658SMarcel Moolenaar xo_arg (xo_handle_t *xop)
427531337658SMarcel Moolenaar {
427631337658SMarcel Moolenaar xop = xo_default(xop);
427731337658SMarcel Moolenaar fprintf(stderr, "0x%x", va_arg(xop->xo_vap, unsigned));
427831337658SMarcel Moolenaar }
427931337658SMarcel Moolenaar #endif /* 0 */
428031337658SMarcel Moolenaar
428131337658SMarcel Moolenaar static void
xo_format_value(xo_handle_t * xop,const char * name,ssize_t nlen,const char * value,ssize_t vlen,const char * fmt,ssize_t flen,const char * encoding,ssize_t elen,xo_xff_flags_t flags)42828a6eceffSPhil Shafer xo_format_value (xo_handle_t *xop, const char *name, ssize_t nlen,
4283264104f2SPhil Shafer const char *value, ssize_t vlen,
4284264104f2SPhil Shafer const char *fmt, ssize_t flen,
42858a6eceffSPhil Shafer const char *encoding, ssize_t elen, xo_xff_flags_t flags)
428631337658SMarcel Moolenaar {
4287d1a0d267SMarcel Moolenaar int pretty = XOF_ISSET(xop, XOF_PRETTY);
428831337658SMarcel Moolenaar int quote;
428931337658SMarcel Moolenaar
4290545ddfbeSMarcel Moolenaar /*
4291545ddfbeSMarcel Moolenaar * Before we emit a value, we need to know that the frame is ready.
4292545ddfbeSMarcel Moolenaar */
4293545ddfbeSMarcel Moolenaar xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth];
4294545ddfbeSMarcel Moolenaar
4295545ddfbeSMarcel Moolenaar if (flags & XFF_LEAF_LIST) {
4296545ddfbeSMarcel Moolenaar /*
4297545ddfbeSMarcel Moolenaar * Check if we've already started to emit normal leafs
4298545ddfbeSMarcel Moolenaar * or if we're not in a leaf list.
4299545ddfbeSMarcel Moolenaar */
4300545ddfbeSMarcel Moolenaar if ((xsp->xs_flags & (XSF_EMIT | XSF_EMIT_KEY))
4301545ddfbeSMarcel Moolenaar || !(xsp->xs_flags & XSF_EMIT_LEAF_LIST)) {
4302545ddfbeSMarcel Moolenaar char nbuf[nlen + 1];
4303545ddfbeSMarcel Moolenaar memcpy(nbuf, name, nlen);
4304545ddfbeSMarcel Moolenaar nbuf[nlen] = '\0';
4305545ddfbeSMarcel Moolenaar
43068a6eceffSPhil Shafer ssize_t rc = xo_transition(xop, 0, nbuf, XSS_EMIT_LEAF_LIST);
4307545ddfbeSMarcel Moolenaar if (rc < 0)
4308545ddfbeSMarcel Moolenaar flags |= XFF_DISPLAY_ONLY | XFF_ENCODE_ONLY;
4309545ddfbeSMarcel Moolenaar else
4310545ddfbeSMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_EMIT_LEAF_LIST;
4311545ddfbeSMarcel Moolenaar }
4312545ddfbeSMarcel Moolenaar
4313545ddfbeSMarcel Moolenaar xsp = &xop->xo_stack[xop->xo_depth];
4314545ddfbeSMarcel Moolenaar if (xsp->xs_name) {
4315545ddfbeSMarcel Moolenaar name = xsp->xs_name;
4316545ddfbeSMarcel Moolenaar nlen = strlen(name);
4317545ddfbeSMarcel Moolenaar }
4318545ddfbeSMarcel Moolenaar
4319545ddfbeSMarcel Moolenaar } else if (flags & XFF_KEY) {
4320545ddfbeSMarcel Moolenaar /* Emitting a 'k' (key) field */
4321545ddfbeSMarcel Moolenaar if ((xsp->xs_flags & XSF_EMIT) && !(flags & XFF_DISPLAY_ONLY)) {
4322545ddfbeSMarcel Moolenaar xo_failure(xop, "key field emitted after normal value field: '%.*s'",
4323545ddfbeSMarcel Moolenaar nlen, name);
4324545ddfbeSMarcel Moolenaar
4325545ddfbeSMarcel Moolenaar } else if (!(xsp->xs_flags & XSF_EMIT_KEY)) {
4326545ddfbeSMarcel Moolenaar char nbuf[nlen + 1];
4327545ddfbeSMarcel Moolenaar memcpy(nbuf, name, nlen);
4328545ddfbeSMarcel Moolenaar nbuf[nlen] = '\0';
4329545ddfbeSMarcel Moolenaar
43308a6eceffSPhil Shafer ssize_t rc = xo_transition(xop, 0, nbuf, XSS_EMIT);
4331545ddfbeSMarcel Moolenaar if (rc < 0)
4332545ddfbeSMarcel Moolenaar flags |= XFF_DISPLAY_ONLY | XFF_ENCODE_ONLY;
4333545ddfbeSMarcel Moolenaar else
4334545ddfbeSMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_EMIT_KEY;
4335545ddfbeSMarcel Moolenaar
4336545ddfbeSMarcel Moolenaar xsp = &xop->xo_stack[xop->xo_depth];
4337545ddfbeSMarcel Moolenaar xsp->xs_flags |= XSF_EMIT_KEY;
4338545ddfbeSMarcel Moolenaar }
4339545ddfbeSMarcel Moolenaar
4340545ddfbeSMarcel Moolenaar } else {
4341545ddfbeSMarcel Moolenaar /* Emitting a normal value field */
4342545ddfbeSMarcel Moolenaar if ((xsp->xs_flags & XSF_EMIT_LEAF_LIST)
4343545ddfbeSMarcel Moolenaar || !(xsp->xs_flags & XSF_EMIT)) {
4344545ddfbeSMarcel Moolenaar char nbuf[nlen + 1];
4345545ddfbeSMarcel Moolenaar memcpy(nbuf, name, nlen);
4346545ddfbeSMarcel Moolenaar nbuf[nlen] = '\0';
4347545ddfbeSMarcel Moolenaar
43488a6eceffSPhil Shafer ssize_t rc = xo_transition(xop, 0, nbuf, XSS_EMIT);
4349545ddfbeSMarcel Moolenaar if (rc < 0)
4350545ddfbeSMarcel Moolenaar flags |= XFF_DISPLAY_ONLY | XFF_ENCODE_ONLY;
4351545ddfbeSMarcel Moolenaar else
4352545ddfbeSMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_EMIT;
4353545ddfbeSMarcel Moolenaar
4354545ddfbeSMarcel Moolenaar xsp = &xop->xo_stack[xop->xo_depth];
4355545ddfbeSMarcel Moolenaar xsp->xs_flags |= XSF_EMIT;
4356545ddfbeSMarcel Moolenaar }
4357545ddfbeSMarcel Moolenaar }
4358545ddfbeSMarcel Moolenaar
4359d1a0d267SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_data;
4360d1a0d267SMarcel Moolenaar xo_humanize_save_t save; /* Save values for humanizing logic */
4361d1a0d267SMarcel Moolenaar
4362406a584dSPhil Shafer const char *leader = xo_xml_leader_len(xop, name, nlen);
4363406a584dSPhil Shafer
4364788ca347SMarcel Moolenaar switch (xo_style(xop)) {
436531337658SMarcel Moolenaar case XO_STYLE_TEXT:
436631337658SMarcel Moolenaar if (flags & XFF_ENCODE_ONLY)
436731337658SMarcel Moolenaar flags |= XFF_NO_OUTPUT;
4368d1a0d267SMarcel Moolenaar
4369d1a0d267SMarcel Moolenaar save.xhs_offset = xbp->xb_curp - xbp->xb_bufp;
4370d1a0d267SMarcel Moolenaar save.xhs_columns = xop->xo_columns;
4371d1a0d267SMarcel Moolenaar save.xhs_anchor_columns = xop->xo_anchor_columns;
4372d1a0d267SMarcel Moolenaar
4373264104f2SPhil Shafer xo_simple_field(xop, FALSE, value, vlen, fmt, flen, flags);
4374d1a0d267SMarcel Moolenaar
4375d1a0d267SMarcel Moolenaar if (flags & XFF_HUMANIZE)
4376d1a0d267SMarcel Moolenaar xo_format_humanize(xop, xbp, &save, flags);
437731337658SMarcel Moolenaar break;
437831337658SMarcel Moolenaar
437931337658SMarcel Moolenaar case XO_STYLE_HTML:
438031337658SMarcel Moolenaar if (flags & XFF_ENCODE_ONLY)
438131337658SMarcel Moolenaar flags |= XFF_NO_OUTPUT;
4382d1a0d267SMarcel Moolenaar
4383264104f2SPhil Shafer xo_buf_append_div(xop, "data", flags, name, nlen, value, vlen,
4384264104f2SPhil Shafer fmt, flen, encoding, elen);
438531337658SMarcel Moolenaar break;
438631337658SMarcel Moolenaar
438731337658SMarcel Moolenaar case XO_STYLE_XML:
438831337658SMarcel Moolenaar /*
438931337658SMarcel Moolenaar * Even though we're not making output, we still need to
439031337658SMarcel Moolenaar * let the formatting code handle the va_arg popping.
439131337658SMarcel Moolenaar */
439231337658SMarcel Moolenaar if (flags & XFF_DISPLAY_ONLY) {
4393264104f2SPhil Shafer xo_simple_field(xop, TRUE, value, vlen, fmt, flen, flags);
439431337658SMarcel Moolenaar break;
439531337658SMarcel Moolenaar }
439631337658SMarcel Moolenaar
439731337658SMarcel Moolenaar if (encoding) {
4398264104f2SPhil Shafer fmt = encoding;
439931337658SMarcel Moolenaar flen = elen;
440031337658SMarcel Moolenaar } else {
440131337658SMarcel Moolenaar char *enc = alloca(flen + 1);
4402264104f2SPhil Shafer memcpy(enc, fmt, flen);
440331337658SMarcel Moolenaar enc[flen] = '\0';
4404264104f2SPhil Shafer fmt = xo_fix_encoding(xop, enc);
4405264104f2SPhil Shafer flen = strlen(fmt);
440631337658SMarcel Moolenaar }
440731337658SMarcel Moolenaar
440831337658SMarcel Moolenaar if (nlen == 0) {
440931337658SMarcel Moolenaar static char missing[] = "missing-field-name";
4410264104f2SPhil Shafer xo_failure(xop, "missing field name: %s", fmt);
441131337658SMarcel Moolenaar name = missing;
441231337658SMarcel Moolenaar nlen = sizeof(missing) - 1;
441331337658SMarcel Moolenaar }
441431337658SMarcel Moolenaar
441531337658SMarcel Moolenaar if (pretty)
441631337658SMarcel Moolenaar xo_buf_indent(xop, -1);
441731337658SMarcel Moolenaar xo_data_append(xop, "<", 1);
4418406a584dSPhil Shafer if (*leader)
4419406a584dSPhil Shafer xo_data_append(xop, leader, 1);
442031337658SMarcel Moolenaar xo_data_escape(xop, name, nlen);
442131337658SMarcel Moolenaar
442231337658SMarcel Moolenaar if (xop->xo_attrs.xb_curp != xop->xo_attrs.xb_bufp) {
442331337658SMarcel Moolenaar xo_data_append(xop, xop->xo_attrs.xb_bufp,
442431337658SMarcel Moolenaar xop->xo_attrs.xb_curp - xop->xo_attrs.xb_bufp);
442531337658SMarcel Moolenaar xop->xo_attrs.xb_curp = xop->xo_attrs.xb_bufp;
442631337658SMarcel Moolenaar }
442731337658SMarcel Moolenaar
442831337658SMarcel Moolenaar /*
442931337658SMarcel Moolenaar * We indicate 'key' fields using the 'key' attribute. While
443031337658SMarcel Moolenaar * this is really committing the crime of mixing meta-data with
443131337658SMarcel Moolenaar * data, it's often useful. Especially when format meta-data is
443231337658SMarcel Moolenaar * difficult to come by.
443331337658SMarcel Moolenaar */
4434d1a0d267SMarcel Moolenaar if ((flags & XFF_KEY) && XOF_ISSET(xop, XOF_KEYS)) {
443531337658SMarcel Moolenaar static char attr[] = " key=\"key\"";
443631337658SMarcel Moolenaar xo_data_append(xop, attr, sizeof(attr) - 1);
443731337658SMarcel Moolenaar }
443831337658SMarcel Moolenaar
443931337658SMarcel Moolenaar /*
444031337658SMarcel Moolenaar * Save the offset at which we'd place units. See xo_format_units.
444131337658SMarcel Moolenaar */
4442d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_UNITS)) {
4443d1a0d267SMarcel Moolenaar XOIF_SET(xop, XOIF_UNITS_PENDING);
444431337658SMarcel Moolenaar xop->xo_units_offset = xop->xo_data.xb_curp -xop->xo_data.xb_bufp;
444531337658SMarcel Moolenaar }
444631337658SMarcel Moolenaar
444731337658SMarcel Moolenaar xo_data_append(xop, ">", 1);
4448264104f2SPhil Shafer
4449264104f2SPhil Shafer xo_simple_field(xop, FALSE, value, vlen, fmt, flen, flags);
4450264104f2SPhil Shafer
445131337658SMarcel Moolenaar xo_data_append(xop, "</", 2);
4452406a584dSPhil Shafer if (*leader)
4453406a584dSPhil Shafer xo_data_append(xop, leader, 1);
445431337658SMarcel Moolenaar xo_data_escape(xop, name, nlen);
445531337658SMarcel Moolenaar xo_data_append(xop, ">", 1);
445631337658SMarcel Moolenaar if (pretty)
445731337658SMarcel Moolenaar xo_data_append(xop, "\n", 1);
445831337658SMarcel Moolenaar break;
445931337658SMarcel Moolenaar
446031337658SMarcel Moolenaar case XO_STYLE_JSON:
446131337658SMarcel Moolenaar if (flags & XFF_DISPLAY_ONLY) {
4462264104f2SPhil Shafer xo_simple_field(xop, TRUE, value, vlen, fmt, flen, flags);
446331337658SMarcel Moolenaar break;
446431337658SMarcel Moolenaar }
446531337658SMarcel Moolenaar
446631337658SMarcel Moolenaar if (encoding) {
4467264104f2SPhil Shafer fmt = encoding;
446831337658SMarcel Moolenaar flen = elen;
446931337658SMarcel Moolenaar } else {
447031337658SMarcel Moolenaar char *enc = alloca(flen + 1);
4471264104f2SPhil Shafer memcpy(enc, fmt, flen);
447231337658SMarcel Moolenaar enc[flen] = '\0';
4473264104f2SPhil Shafer fmt = xo_fix_encoding(xop, enc);
4474264104f2SPhil Shafer flen = strlen(fmt);
447531337658SMarcel Moolenaar }
447631337658SMarcel Moolenaar
4477406a584dSPhil Shafer xo_stack_set_flags(xop);
4478406a584dSPhil Shafer
44798a6eceffSPhil Shafer int first = (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST)
44808a6eceffSPhil Shafer ? 0 : 1;
448131337658SMarcel Moolenaar
448231337658SMarcel Moolenaar xo_format_prep(xop, flags);
448331337658SMarcel Moolenaar
448431337658SMarcel Moolenaar if (flags & XFF_QUOTE)
448531337658SMarcel Moolenaar quote = 1;
448631337658SMarcel Moolenaar else if (flags & XFF_NOQUOTE)
448731337658SMarcel Moolenaar quote = 0;
4488264104f2SPhil Shafer else if (vlen != 0)
4489264104f2SPhil Shafer quote = 1;
449031337658SMarcel Moolenaar else if (flen == 0) {
449131337658SMarcel Moolenaar quote = 0;
4492264104f2SPhil Shafer fmt = "true"; /* JSON encodes empty tags as a boolean true */
449331337658SMarcel Moolenaar flen = 4;
4494983afe33SPhil Shafer } else if (xo_format_is_numeric(fmt, flen))
449531337658SMarcel Moolenaar quote = 0;
4496983afe33SPhil Shafer else
4497983afe33SPhil Shafer quote = 1;
449831337658SMarcel Moolenaar
449931337658SMarcel Moolenaar if (nlen == 0) {
450031337658SMarcel Moolenaar static char missing[] = "missing-field-name";
4501264104f2SPhil Shafer xo_failure(xop, "missing field name: %s", fmt);
450231337658SMarcel Moolenaar name = missing;
450331337658SMarcel Moolenaar nlen = sizeof(missing) - 1;
450431337658SMarcel Moolenaar }
450531337658SMarcel Moolenaar
450631337658SMarcel Moolenaar if (flags & XFF_LEAF_LIST) {
4507788ca347SMarcel Moolenaar if (!first && pretty)
4508788ca347SMarcel Moolenaar xo_data_append(xop, "\n", 1);
4509788ca347SMarcel Moolenaar if (pretty)
451031337658SMarcel Moolenaar xo_buf_indent(xop, -1);
451131337658SMarcel Moolenaar } else {
451231337658SMarcel Moolenaar if (pretty)
451331337658SMarcel Moolenaar xo_buf_indent(xop, -1);
451431337658SMarcel Moolenaar xo_data_append(xop, "\"", 1);
451531337658SMarcel Moolenaar
451631337658SMarcel Moolenaar xbp = &xop->xo_data;
45178a6eceffSPhil Shafer ssize_t off = xbp->xb_curp - xbp->xb_bufp;
451831337658SMarcel Moolenaar
451931337658SMarcel Moolenaar xo_data_escape(xop, name, nlen);
452031337658SMarcel Moolenaar
4521d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_UNDERSCORES)) {
45228a6eceffSPhil Shafer ssize_t coff = xbp->xb_curp - xbp->xb_bufp;
45238a6eceffSPhil Shafer for ( ; off < coff; off++)
452431337658SMarcel Moolenaar if (xbp->xb_bufp[off] == '-')
452531337658SMarcel Moolenaar xbp->xb_bufp[off] = '_';
452631337658SMarcel Moolenaar }
452731337658SMarcel Moolenaar xo_data_append(xop, "\":", 2);
452831337658SMarcel Moolenaar if (pretty)
452931337658SMarcel Moolenaar xo_data_append(xop, " ", 1);
4530788ca347SMarcel Moolenaar }
4531788ca347SMarcel Moolenaar
453231337658SMarcel Moolenaar if (quote)
453331337658SMarcel Moolenaar xo_data_append(xop, "\"", 1);
453431337658SMarcel Moolenaar
4535264104f2SPhil Shafer xo_simple_field(xop, FALSE, value, vlen, fmt, flen, flags);
453631337658SMarcel Moolenaar
453731337658SMarcel Moolenaar if (quote)
453831337658SMarcel Moolenaar xo_data_append(xop, "\"", 1);
453931337658SMarcel Moolenaar break;
4540d1a0d267SMarcel Moolenaar
4541d1a0d267SMarcel Moolenaar case XO_STYLE_SDPARAMS:
4542d1a0d267SMarcel Moolenaar if (flags & XFF_DISPLAY_ONLY) {
4543264104f2SPhil Shafer xo_simple_field(xop, TRUE, value, vlen, fmt, flen, flags);
4544d1a0d267SMarcel Moolenaar break;
4545d1a0d267SMarcel Moolenaar }
4546d1a0d267SMarcel Moolenaar
4547d1a0d267SMarcel Moolenaar if (encoding) {
4548264104f2SPhil Shafer fmt = encoding;
4549d1a0d267SMarcel Moolenaar flen = elen;
4550d1a0d267SMarcel Moolenaar } else {
4551d1a0d267SMarcel Moolenaar char *enc = alloca(flen + 1);
4552264104f2SPhil Shafer memcpy(enc, fmt, flen);
4553d1a0d267SMarcel Moolenaar enc[flen] = '\0';
4554264104f2SPhil Shafer fmt = xo_fix_encoding(xop, enc);
4555264104f2SPhil Shafer flen = strlen(fmt);
4556d1a0d267SMarcel Moolenaar }
4557d1a0d267SMarcel Moolenaar
4558d1a0d267SMarcel Moolenaar if (nlen == 0) {
4559d1a0d267SMarcel Moolenaar static char missing[] = "missing-field-name";
4560264104f2SPhil Shafer xo_failure(xop, "missing field name: %s", fmt);
4561d1a0d267SMarcel Moolenaar name = missing;
4562d1a0d267SMarcel Moolenaar nlen = sizeof(missing) - 1;
4563d1a0d267SMarcel Moolenaar }
4564d1a0d267SMarcel Moolenaar
4565d1a0d267SMarcel Moolenaar xo_data_escape(xop, name, nlen);
4566d1a0d267SMarcel Moolenaar xo_data_append(xop, "=\"", 2);
4567264104f2SPhil Shafer
4568264104f2SPhil Shafer xo_simple_field(xop, FALSE, value, vlen, fmt, flen, flags);
4569264104f2SPhil Shafer
4570d1a0d267SMarcel Moolenaar xo_data_append(xop, "\" ", 2);
4571d1a0d267SMarcel Moolenaar break;
4572d1a0d267SMarcel Moolenaar
4573d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER:
4574d1a0d267SMarcel Moolenaar if (flags & XFF_DISPLAY_ONLY) {
4575264104f2SPhil Shafer xo_simple_field(xop, TRUE, value, vlen, fmt, flen, flags);
4576d1a0d267SMarcel Moolenaar break;
4577d1a0d267SMarcel Moolenaar }
4578d1a0d267SMarcel Moolenaar
4579d1a0d267SMarcel Moolenaar if (flags & XFF_QUOTE)
4580d1a0d267SMarcel Moolenaar quote = 1;
4581d1a0d267SMarcel Moolenaar else if (flags & XFF_NOQUOTE)
4582d1a0d267SMarcel Moolenaar quote = 0;
4583d1a0d267SMarcel Moolenaar else if (flen == 0) {
4584d1a0d267SMarcel Moolenaar quote = 0;
4585264104f2SPhil Shafer fmt = "true"; /* JSON encodes empty tags as a boolean true */
4586d1a0d267SMarcel Moolenaar flen = 4;
4587264104f2SPhil Shafer } else if (strchr("diouxXDOUeEfFgGaAcCp", fmt[flen - 1]) == NULL)
4588d1a0d267SMarcel Moolenaar quote = 1;
4589d1a0d267SMarcel Moolenaar else
4590d1a0d267SMarcel Moolenaar quote = 0;
4591d1a0d267SMarcel Moolenaar
4592d1a0d267SMarcel Moolenaar if (encoding) {
4593264104f2SPhil Shafer fmt = encoding;
4594d1a0d267SMarcel Moolenaar flen = elen;
4595d1a0d267SMarcel Moolenaar } else {
4596d1a0d267SMarcel Moolenaar char *enc = alloca(flen + 1);
4597264104f2SPhil Shafer memcpy(enc, fmt, flen);
4598d1a0d267SMarcel Moolenaar enc[flen] = '\0';
4599264104f2SPhil Shafer fmt = xo_fix_encoding(xop, enc);
4600264104f2SPhil Shafer flen = strlen(fmt);
4601d1a0d267SMarcel Moolenaar }
4602d1a0d267SMarcel Moolenaar
4603d1a0d267SMarcel Moolenaar if (nlen == 0) {
4604d1a0d267SMarcel Moolenaar static char missing[] = "missing-field-name";
4605264104f2SPhil Shafer xo_failure(xop, "missing field name: %s", fmt);
4606d1a0d267SMarcel Moolenaar name = missing;
4607d1a0d267SMarcel Moolenaar nlen = sizeof(missing) - 1;
4608d1a0d267SMarcel Moolenaar }
4609d1a0d267SMarcel Moolenaar
46108a6eceffSPhil Shafer ssize_t name_offset = xo_buf_offset(&xop->xo_data);
4611d1a0d267SMarcel Moolenaar xo_data_append(xop, name, nlen);
4612d1a0d267SMarcel Moolenaar xo_data_append(xop, "", 1);
4613d1a0d267SMarcel Moolenaar
46148a6eceffSPhil Shafer ssize_t value_offset = xo_buf_offset(&xop->xo_data);
4615264104f2SPhil Shafer
4616264104f2SPhil Shafer xo_simple_field(xop, FALSE, value, vlen, fmt, flen, flags);
4617264104f2SPhil Shafer
4618d1a0d267SMarcel Moolenaar xo_data_append(xop, "", 1);
4619d1a0d267SMarcel Moolenaar
4620d1a0d267SMarcel Moolenaar xo_encoder_handle(xop, quote ? XO_OP_STRING : XO_OP_CONTENT,
4621d1a0d267SMarcel Moolenaar xo_buf_data(&xop->xo_data, name_offset),
4622f2b7bf8aSPhil Shafer xo_buf_data(&xop->xo_data, value_offset), flags);
4623d1a0d267SMarcel Moolenaar xo_buf_reset(&xop->xo_data);
4624d1a0d267SMarcel Moolenaar break;
462531337658SMarcel Moolenaar }
462631337658SMarcel Moolenaar }
462731337658SMarcel Moolenaar
462831337658SMarcel Moolenaar static void
xo_set_gettext_domain(xo_handle_t * xop,xo_field_info_t * xfip,const char * str,ssize_t len)462942ff34c3SPhil Shafer xo_set_gettext_domain (xo_handle_t *xop, xo_field_info_t *xfip,
46308a6eceffSPhil Shafer const char *str, ssize_t len)
4631d1a0d267SMarcel Moolenaar {
4632d1a0d267SMarcel Moolenaar const char *fmt = xfip->xfi_format;
46338a6eceffSPhil Shafer ssize_t flen = xfip->xfi_flen;
4634d1a0d267SMarcel Moolenaar
4635d1a0d267SMarcel Moolenaar /* Start by discarding previous domain */
4636d1a0d267SMarcel Moolenaar if (xop->xo_gt_domain) {
4637d1a0d267SMarcel Moolenaar xo_free(xop->xo_gt_domain);
4638d1a0d267SMarcel Moolenaar xop->xo_gt_domain = NULL;
4639d1a0d267SMarcel Moolenaar }
4640d1a0d267SMarcel Moolenaar
4641d1a0d267SMarcel Moolenaar /* An empty {G:} means no domainname */
4642d1a0d267SMarcel Moolenaar if (len == 0 && flen == 0)
4643d1a0d267SMarcel Moolenaar return;
4644d1a0d267SMarcel Moolenaar
46458a6eceffSPhil Shafer ssize_t start_offset = -1;
4646d1a0d267SMarcel Moolenaar if (len == 0 && flen != 0) {
4647d1a0d267SMarcel Moolenaar /* Need to do format the data to get the domainname from args */
4648d1a0d267SMarcel Moolenaar start_offset = xop->xo_data.xb_curp - xop->xo_data.xb_bufp;
4649d1a0d267SMarcel Moolenaar xo_do_format_field(xop, NULL, fmt, flen, 0);
4650d1a0d267SMarcel Moolenaar
46518a6eceffSPhil Shafer ssize_t end_offset = xop->xo_data.xb_curp - xop->xo_data.xb_bufp;
4652d1a0d267SMarcel Moolenaar len = end_offset - start_offset;
4653d1a0d267SMarcel Moolenaar str = xop->xo_data.xb_bufp + start_offset;
4654d1a0d267SMarcel Moolenaar }
4655d1a0d267SMarcel Moolenaar
4656d1a0d267SMarcel Moolenaar xop->xo_gt_domain = xo_strndup(str, len);
4657d1a0d267SMarcel Moolenaar
4658d1a0d267SMarcel Moolenaar /* Reset the current buffer point to avoid emitting the name as output */
4659d1a0d267SMarcel Moolenaar if (start_offset >= 0)
4660d1a0d267SMarcel Moolenaar xop->xo_data.xb_curp = xop->xo_data.xb_bufp + start_offset;
4661d1a0d267SMarcel Moolenaar }
4662d1a0d267SMarcel Moolenaar
4663d1a0d267SMarcel Moolenaar static void
xo_format_content(xo_handle_t * xop,const char * class_name,const char * tag_name,const char * value,ssize_t vlen,const char * fmt,ssize_t flen,xo_xff_flags_t flags)466431337658SMarcel Moolenaar xo_format_content (xo_handle_t *xop, const char *class_name,
4665d1a0d267SMarcel Moolenaar const char *tag_name,
4666264104f2SPhil Shafer const char *value, ssize_t vlen,
4667264104f2SPhil Shafer const char *fmt, ssize_t flen,
4668d1a0d267SMarcel Moolenaar xo_xff_flags_t flags)
466931337658SMarcel Moolenaar {
4670788ca347SMarcel Moolenaar switch (xo_style(xop)) {
467131337658SMarcel Moolenaar case XO_STYLE_TEXT:
4672264104f2SPhil Shafer xo_simple_field(xop, FALSE, value, vlen, fmt, flen, flags);
467331337658SMarcel Moolenaar break;
467431337658SMarcel Moolenaar
467531337658SMarcel Moolenaar case XO_STYLE_HTML:
4676264104f2SPhil Shafer xo_buf_append_div(xop, class_name, flags, NULL, 0,
4677264104f2SPhil Shafer value, vlen, fmt, flen, NULL, 0);
467831337658SMarcel Moolenaar break;
467931337658SMarcel Moolenaar
468031337658SMarcel Moolenaar case XO_STYLE_XML:
4681d1a0d267SMarcel Moolenaar case XO_STYLE_JSON:
4682d1a0d267SMarcel Moolenaar case XO_STYLE_SDPARAMS:
4683d1a0d267SMarcel Moolenaar if (tag_name) {
4684d1a0d267SMarcel Moolenaar xo_open_container_h(xop, tag_name);
4685264104f2SPhil Shafer xo_format_value(xop, "message", 7, value, vlen,
4686264104f2SPhil Shafer fmt, flen, NULL, 0, flags);
4687d1a0d267SMarcel Moolenaar xo_close_container_h(xop, tag_name);
468831337658SMarcel Moolenaar
468931337658SMarcel Moolenaar } else {
469031337658SMarcel Moolenaar /*
469131337658SMarcel Moolenaar * Even though we don't care about labels, we need to do
469231337658SMarcel Moolenaar * enough parsing work to skip over the right bits of xo_vap.
469331337658SMarcel Moolenaar */
4694264104f2SPhil Shafer xo_simple_field(xop, TRUE, value, vlen, fmt, flen, flags);
469531337658SMarcel Moolenaar }
469631337658SMarcel Moolenaar break;
469731337658SMarcel Moolenaar
4698d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER:
4699264104f2SPhil Shafer xo_simple_field(xop, TRUE, value, vlen, fmt, flen, flags);
470031337658SMarcel Moolenaar break;
470131337658SMarcel Moolenaar }
470231337658SMarcel Moolenaar }
470331337658SMarcel Moolenaar
4704788ca347SMarcel Moolenaar static const char *xo_color_names[] = {
4705788ca347SMarcel Moolenaar "default", /* XO_COL_DEFAULT */
4706788ca347SMarcel Moolenaar "black", /* XO_COL_BLACK */
4707788ca347SMarcel Moolenaar "red", /* XO_CLOR_RED */
4708788ca347SMarcel Moolenaar "green", /* XO_COL_GREEN */
4709788ca347SMarcel Moolenaar "yellow", /* XO_COL_YELLOW */
4710788ca347SMarcel Moolenaar "blue", /* XO_COL_BLUE */
4711788ca347SMarcel Moolenaar "magenta", /* XO_COL_MAGENTA */
4712788ca347SMarcel Moolenaar "cyan", /* XO_COL_CYAN */
4713788ca347SMarcel Moolenaar "white", /* XO_COL_WHITE */
4714788ca347SMarcel Moolenaar NULL
4715788ca347SMarcel Moolenaar };
4716788ca347SMarcel Moolenaar
4717788ca347SMarcel Moolenaar static int
xo_color_find(const char * str)4718788ca347SMarcel Moolenaar xo_color_find (const char *str)
4719788ca347SMarcel Moolenaar {
4720788ca347SMarcel Moolenaar int i;
4721788ca347SMarcel Moolenaar
4722788ca347SMarcel Moolenaar for (i = 0; xo_color_names[i]; i++) {
472376afb20cSPhil Shafer if (xo_streq(xo_color_names[i], str))
4724788ca347SMarcel Moolenaar return i;
4725788ca347SMarcel Moolenaar }
4726788ca347SMarcel Moolenaar
4727788ca347SMarcel Moolenaar return -1;
4728788ca347SMarcel Moolenaar }
4729788ca347SMarcel Moolenaar
4730788ca347SMarcel Moolenaar static const char *xo_effect_names[] = {
4731788ca347SMarcel Moolenaar "reset", /* XO_EFF_RESET */
4732788ca347SMarcel Moolenaar "normal", /* XO_EFF_NORMAL */
4733788ca347SMarcel Moolenaar "bold", /* XO_EFF_BOLD */
4734788ca347SMarcel Moolenaar "underline", /* XO_EFF_UNDERLINE */
4735788ca347SMarcel Moolenaar "inverse", /* XO_EFF_INVERSE */
4736788ca347SMarcel Moolenaar NULL
4737788ca347SMarcel Moolenaar };
4738788ca347SMarcel Moolenaar
4739788ca347SMarcel Moolenaar static const char *xo_effect_on_codes[] = {
4740788ca347SMarcel Moolenaar "0", /* XO_EFF_RESET */
4741788ca347SMarcel Moolenaar "0", /* XO_EFF_NORMAL */
4742788ca347SMarcel Moolenaar "1", /* XO_EFF_BOLD */
4743788ca347SMarcel Moolenaar "4", /* XO_EFF_UNDERLINE */
4744788ca347SMarcel Moolenaar "7", /* XO_EFF_INVERSE */
4745788ca347SMarcel Moolenaar NULL
4746788ca347SMarcel Moolenaar };
4747788ca347SMarcel Moolenaar
4748788ca347SMarcel Moolenaar #if 0
4749788ca347SMarcel Moolenaar /*
4750788ca347SMarcel Moolenaar * See comment below re: joy of terminal standards. These can
4751788ca347SMarcel Moolenaar * be use by just adding:
4752d1a0d267SMarcel Moolenaar * + if (newp->xoc_effects & bit)
4753788ca347SMarcel Moolenaar * code = xo_effect_on_codes[i];
4754788ca347SMarcel Moolenaar * + else
4755788ca347SMarcel Moolenaar * + code = xo_effect_off_codes[i];
4756788ca347SMarcel Moolenaar * in xo_color_handle_text.
4757788ca347SMarcel Moolenaar */
4758788ca347SMarcel Moolenaar static const char *xo_effect_off_codes[] = {
4759788ca347SMarcel Moolenaar "0", /* XO_EFF_RESET */
4760788ca347SMarcel Moolenaar "0", /* XO_EFF_NORMAL */
4761788ca347SMarcel Moolenaar "21", /* XO_EFF_BOLD */
4762788ca347SMarcel Moolenaar "24", /* XO_EFF_UNDERLINE */
4763788ca347SMarcel Moolenaar "27", /* XO_EFF_INVERSE */
4764788ca347SMarcel Moolenaar NULL
4765788ca347SMarcel Moolenaar };
4766788ca347SMarcel Moolenaar #endif /* 0 */
4767788ca347SMarcel Moolenaar
4768788ca347SMarcel Moolenaar static int
xo_effect_find(const char * str)4769788ca347SMarcel Moolenaar xo_effect_find (const char *str)
4770788ca347SMarcel Moolenaar {
4771788ca347SMarcel Moolenaar int i;
4772788ca347SMarcel Moolenaar
4773788ca347SMarcel Moolenaar for (i = 0; xo_effect_names[i]; i++) {
477476afb20cSPhil Shafer if (xo_streq(xo_effect_names[i], str))
4775788ca347SMarcel Moolenaar return i;
4776788ca347SMarcel Moolenaar }
4777788ca347SMarcel Moolenaar
4778788ca347SMarcel Moolenaar return -1;
4779788ca347SMarcel Moolenaar }
4780788ca347SMarcel Moolenaar
4781788ca347SMarcel Moolenaar static void
xo_colors_parse(xo_handle_t * xop,xo_colors_t * xocp,char * str)4782788ca347SMarcel Moolenaar xo_colors_parse (xo_handle_t *xop, xo_colors_t *xocp, char *str)
4783788ca347SMarcel Moolenaar {
4784406a584dSPhil Shafer if (xo_text_only())
4785788ca347SMarcel Moolenaar return;
4786788ca347SMarcel Moolenaar
4787788ca347SMarcel Moolenaar char *cp, *ep, *np, *xp;
47888a6eceffSPhil Shafer ssize_t len = strlen(str);
4789788ca347SMarcel Moolenaar int rc;
4790788ca347SMarcel Moolenaar
4791788ca347SMarcel Moolenaar /*
4792788ca347SMarcel Moolenaar * Possible tokens: colors, bg-colors, effects, no-effects, "reset".
4793788ca347SMarcel Moolenaar */
4794788ca347SMarcel Moolenaar for (cp = str, ep = cp + len - 1; cp && cp < ep; cp = np) {
4795788ca347SMarcel Moolenaar /* Trim leading whitespace */
4796788ca347SMarcel Moolenaar while (isspace((int) *cp))
4797788ca347SMarcel Moolenaar cp += 1;
4798788ca347SMarcel Moolenaar
4799788ca347SMarcel Moolenaar np = strchr(cp, ',');
4800788ca347SMarcel Moolenaar if (np)
4801788ca347SMarcel Moolenaar *np++ = '\0';
4802788ca347SMarcel Moolenaar
4803788ca347SMarcel Moolenaar /* Trim trailing whitespace */
4804788ca347SMarcel Moolenaar xp = cp + strlen(cp) - 1;
4805788ca347SMarcel Moolenaar while (isspace(*xp) && xp > cp)
4806788ca347SMarcel Moolenaar *xp-- = '\0';
4807788ca347SMarcel Moolenaar
4808788ca347SMarcel Moolenaar if (cp[0] == 'f' && cp[1] == 'g' && cp[2] == '-') {
4809788ca347SMarcel Moolenaar rc = xo_color_find(cp + 3);
4810788ca347SMarcel Moolenaar if (rc < 0)
4811788ca347SMarcel Moolenaar goto unknown;
4812788ca347SMarcel Moolenaar
4813788ca347SMarcel Moolenaar xocp->xoc_col_fg = rc;
4814788ca347SMarcel Moolenaar
4815788ca347SMarcel Moolenaar } else if (cp[0] == 'b' && cp[1] == 'g' && cp[2] == '-') {
4816788ca347SMarcel Moolenaar rc = xo_color_find(cp + 3);
4817788ca347SMarcel Moolenaar if (rc < 0)
4818788ca347SMarcel Moolenaar goto unknown;
4819788ca347SMarcel Moolenaar xocp->xoc_col_bg = rc;
4820788ca347SMarcel Moolenaar
4821788ca347SMarcel Moolenaar } else if (cp[0] == 'n' && cp[1] == 'o' && cp[2] == '-') {
4822788ca347SMarcel Moolenaar rc = xo_effect_find(cp + 3);
4823788ca347SMarcel Moolenaar if (rc < 0)
4824788ca347SMarcel Moolenaar goto unknown;
4825788ca347SMarcel Moolenaar xocp->xoc_effects &= ~(1 << rc);
4826788ca347SMarcel Moolenaar
4827788ca347SMarcel Moolenaar } else {
4828788ca347SMarcel Moolenaar rc = xo_effect_find(cp);
4829788ca347SMarcel Moolenaar if (rc < 0)
4830788ca347SMarcel Moolenaar goto unknown;
4831788ca347SMarcel Moolenaar xocp->xoc_effects |= 1 << rc;
4832788ca347SMarcel Moolenaar
4833788ca347SMarcel Moolenaar switch (1 << rc) {
4834788ca347SMarcel Moolenaar case XO_EFF_RESET:
4835788ca347SMarcel Moolenaar xocp->xoc_col_fg = xocp->xoc_col_bg = 0;
4836788ca347SMarcel Moolenaar /* Note: not "|=" since we want to wipe out the old value */
4837788ca347SMarcel Moolenaar xocp->xoc_effects = XO_EFF_RESET;
4838788ca347SMarcel Moolenaar break;
4839788ca347SMarcel Moolenaar
4840788ca347SMarcel Moolenaar case XO_EFF_NORMAL:
4841788ca347SMarcel Moolenaar xocp->xoc_effects &= ~(XO_EFF_BOLD | XO_EFF_UNDERLINE
4842788ca347SMarcel Moolenaar | XO_EFF_INVERSE | XO_EFF_NORMAL);
4843788ca347SMarcel Moolenaar break;
4844788ca347SMarcel Moolenaar }
4845788ca347SMarcel Moolenaar }
4846788ca347SMarcel Moolenaar continue;
4847788ca347SMarcel Moolenaar
4848788ca347SMarcel Moolenaar unknown:
4849d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_WARN))
4850788ca347SMarcel Moolenaar xo_failure(xop, "unknown color/effect string detected: '%s'", cp);
4851788ca347SMarcel Moolenaar }
4852788ca347SMarcel Moolenaar }
4853788ca347SMarcel Moolenaar
4854788ca347SMarcel Moolenaar static inline int
xo_colors_enabled(xo_handle_t * xop UNUSED)4855788ca347SMarcel Moolenaar xo_colors_enabled (xo_handle_t *xop UNUSED)
4856788ca347SMarcel Moolenaar {
4857788ca347SMarcel Moolenaar #ifdef LIBXO_TEXT_ONLY
4858788ca347SMarcel Moolenaar return 0;
4859788ca347SMarcel Moolenaar #else /* LIBXO_TEXT_ONLY */
4860d1a0d267SMarcel Moolenaar return XOF_ISSET(xop, XOF_COLOR);
4861788ca347SMarcel Moolenaar #endif /* LIBXO_TEXT_ONLY */
4862788ca347SMarcel Moolenaar }
4863788ca347SMarcel Moolenaar
4864f2b7bf8aSPhil Shafer /*
4865f2b7bf8aSPhil Shafer * If the color map is in use (--libxo colors=xxxx), then update
4866f2b7bf8aSPhil Shafer * the incoming foreground and background colors from the map.
4867f2b7bf8aSPhil Shafer */
4868f2b7bf8aSPhil Shafer static void
xo_colors_update(xo_handle_t * xop UNUSED,xo_colors_t * newp UNUSED)4869406a584dSPhil Shafer xo_colors_update (xo_handle_t *xop UNUSED, xo_colors_t *newp UNUSED)
4870f2b7bf8aSPhil Shafer {
4871406a584dSPhil Shafer #ifndef LIBXO_TEXT_ONLY
4872f2b7bf8aSPhil Shafer xo_color_t fg = newp->xoc_col_fg;
4873f2b7bf8aSPhil Shafer if (XOF_ISSET(xop, XOF_COLOR_MAP) && fg < XO_NUM_COLORS)
4874f2b7bf8aSPhil Shafer fg = xop->xo_color_map_fg[fg]; /* Fetch from color map */
4875f2b7bf8aSPhil Shafer newp->xoc_col_fg = fg;
4876f2b7bf8aSPhil Shafer
4877f2b7bf8aSPhil Shafer xo_color_t bg = newp->xoc_col_bg;
4878f2b7bf8aSPhil Shafer if (XOF_ISSET(xop, XOF_COLOR_MAP) && bg < XO_NUM_COLORS)
4879f2b7bf8aSPhil Shafer bg = xop->xo_color_map_bg[bg]; /* Fetch from color map */
4880f2b7bf8aSPhil Shafer newp->xoc_col_bg = bg;
4881406a584dSPhil Shafer #endif /* LIBXO_TEXT_ONLY */
4882f2b7bf8aSPhil Shafer }
4883f2b7bf8aSPhil Shafer
4884788ca347SMarcel Moolenaar static void
xo_colors_handle_text(xo_handle_t * xop,xo_colors_t * newp)488542ff34c3SPhil Shafer xo_colors_handle_text (xo_handle_t *xop, xo_colors_t *newp)
4886788ca347SMarcel Moolenaar {
4887788ca347SMarcel Moolenaar char buf[BUFSIZ];
4888788ca347SMarcel Moolenaar char *cp = buf, *ep = buf + sizeof(buf);
4889788ca347SMarcel Moolenaar unsigned i, bit;
4890788ca347SMarcel Moolenaar xo_colors_t *oldp = &xop->xo_colors;
489142ff34c3SPhil Shafer const char *code = NULL;
4892788ca347SMarcel Moolenaar
4893788ca347SMarcel Moolenaar /*
4894788ca347SMarcel Moolenaar * Start the buffer with an escape. We don't want to add the '['
4895788ca347SMarcel Moolenaar * now, since we let xo_effect_text_add unconditionally add the ';'.
4896788ca347SMarcel Moolenaar * We'll replace the first ';' with a '[' when we're done.
4897788ca347SMarcel Moolenaar */
4898788ca347SMarcel Moolenaar *cp++ = 0x1b; /* Escape */
4899788ca347SMarcel Moolenaar
4900788ca347SMarcel Moolenaar /*
4901788ca347SMarcel Moolenaar * Terminals were designed back in the age before "certainty" was
4902788ca347SMarcel Moolenaar * invented, when standards were more what you'd call "guidelines"
4903788ca347SMarcel Moolenaar * than actual rules. Anyway we can't depend on them to operate
4904788ca347SMarcel Moolenaar * correctly. So when display attributes are changed, we punt,
4905788ca347SMarcel Moolenaar * reseting them all and turning back on the ones we want to keep.
4906788ca347SMarcel Moolenaar * Longer, but should be completely reliable. Savvy?
4907788ca347SMarcel Moolenaar */
4908788ca347SMarcel Moolenaar if (oldp->xoc_effects != (newp->xoc_effects & oldp->xoc_effects)) {
4909788ca347SMarcel Moolenaar newp->xoc_effects |= XO_EFF_RESET;
4910788ca347SMarcel Moolenaar oldp->xoc_effects = 0;
4911788ca347SMarcel Moolenaar }
4912788ca347SMarcel Moolenaar
4913788ca347SMarcel Moolenaar for (i = 0, bit = 1; xo_effect_names[i]; i++, bit <<= 1) {
4914788ca347SMarcel Moolenaar if ((newp->xoc_effects & bit) == (oldp->xoc_effects & bit))
4915788ca347SMarcel Moolenaar continue;
4916788ca347SMarcel Moolenaar
4917788ca347SMarcel Moolenaar code = xo_effect_on_codes[i];
4918788ca347SMarcel Moolenaar
4919788ca347SMarcel Moolenaar cp += snprintf(cp, ep - cp, ";%s", code);
4920788ca347SMarcel Moolenaar if (cp >= ep)
4921788ca347SMarcel Moolenaar return; /* Should not occur */
4922788ca347SMarcel Moolenaar
4923788ca347SMarcel Moolenaar if (bit == XO_EFF_RESET) {
4924788ca347SMarcel Moolenaar /* Mark up the old value so we can detect current values as new */
4925788ca347SMarcel Moolenaar oldp->xoc_effects = 0;
4926788ca347SMarcel Moolenaar oldp->xoc_col_fg = oldp->xoc_col_bg = XO_COL_DEFAULT;
4927788ca347SMarcel Moolenaar }
4928788ca347SMarcel Moolenaar }
4929788ca347SMarcel Moolenaar
4930f2b7bf8aSPhil Shafer xo_color_t fg = newp->xoc_col_fg;
4931f2b7bf8aSPhil Shafer if (fg != oldp->xoc_col_fg) {
4932788ca347SMarcel Moolenaar cp += snprintf(cp, ep - cp, ";3%u",
4933f2b7bf8aSPhil Shafer (fg != XO_COL_DEFAULT) ? fg - 1 : 9);
4934788ca347SMarcel Moolenaar }
4935788ca347SMarcel Moolenaar
4936f2b7bf8aSPhil Shafer xo_color_t bg = newp->xoc_col_bg;
4937f2b7bf8aSPhil Shafer if (bg != oldp->xoc_col_bg) {
4938788ca347SMarcel Moolenaar cp += snprintf(cp, ep - cp, ";4%u",
4939f2b7bf8aSPhil Shafer (bg != XO_COL_DEFAULT) ? bg - 1 : 9);
4940788ca347SMarcel Moolenaar }
4941788ca347SMarcel Moolenaar
4942788ca347SMarcel Moolenaar if (cp - buf != 1 && cp < ep - 3) {
4943788ca347SMarcel Moolenaar buf[1] = '['; /* Overwrite leading ';' */
4944788ca347SMarcel Moolenaar *cp++ = 'm';
4945788ca347SMarcel Moolenaar *cp = '\0';
4946788ca347SMarcel Moolenaar xo_buf_append(&xop->xo_data, buf, cp - buf);
4947788ca347SMarcel Moolenaar }
4948788ca347SMarcel Moolenaar }
4949788ca347SMarcel Moolenaar
4950788ca347SMarcel Moolenaar static void
xo_colors_handle_html(xo_handle_t * xop,xo_colors_t * newp)4951788ca347SMarcel Moolenaar xo_colors_handle_html (xo_handle_t *xop, xo_colors_t *newp)
4952788ca347SMarcel Moolenaar {
4953788ca347SMarcel Moolenaar xo_colors_t *oldp = &xop->xo_colors;
4954788ca347SMarcel Moolenaar
4955788ca347SMarcel Moolenaar /*
4956788ca347SMarcel Moolenaar * HTML colors are mostly trivial: fill in xo_color_buf with
4957788ca347SMarcel Moolenaar * a set of class tags representing the colors and effects.
4958788ca347SMarcel Moolenaar */
4959788ca347SMarcel Moolenaar
4960788ca347SMarcel Moolenaar /* If nothing changed, then do nothing */
4961788ca347SMarcel Moolenaar if (oldp->xoc_effects == newp->xoc_effects
4962788ca347SMarcel Moolenaar && oldp->xoc_col_fg == newp->xoc_col_fg
4963788ca347SMarcel Moolenaar && oldp->xoc_col_bg == newp->xoc_col_bg)
4964788ca347SMarcel Moolenaar return;
4965788ca347SMarcel Moolenaar
4966788ca347SMarcel Moolenaar unsigned i, bit;
4967788ca347SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_color_buf;
4968788ca347SMarcel Moolenaar
4969788ca347SMarcel Moolenaar xo_buf_reset(xbp); /* We rebuild content after each change */
4970788ca347SMarcel Moolenaar
4971788ca347SMarcel Moolenaar for (i = 0, bit = 1; xo_effect_names[i]; i++, bit <<= 1) {
4972788ca347SMarcel Moolenaar if (!(newp->xoc_effects & bit))
4973788ca347SMarcel Moolenaar continue;
4974788ca347SMarcel Moolenaar
4975788ca347SMarcel Moolenaar xo_buf_append_str(xbp, " effect-");
4976788ca347SMarcel Moolenaar xo_buf_append_str(xbp, xo_effect_names[i]);
4977788ca347SMarcel Moolenaar }
4978788ca347SMarcel Moolenaar
4979788ca347SMarcel Moolenaar const char *fg = NULL;
4980788ca347SMarcel Moolenaar const char *bg = NULL;
4981788ca347SMarcel Moolenaar
4982788ca347SMarcel Moolenaar if (newp->xoc_col_fg != XO_COL_DEFAULT)
4983788ca347SMarcel Moolenaar fg = xo_color_names[newp->xoc_col_fg];
4984788ca347SMarcel Moolenaar if (newp->xoc_col_bg != XO_COL_DEFAULT)
4985788ca347SMarcel Moolenaar bg = xo_color_names[newp->xoc_col_bg];
4986788ca347SMarcel Moolenaar
4987788ca347SMarcel Moolenaar if (newp->xoc_effects & XO_EFF_INVERSE) {
4988788ca347SMarcel Moolenaar const char *tmp = fg;
4989788ca347SMarcel Moolenaar fg = bg;
4990788ca347SMarcel Moolenaar bg = tmp;
4991788ca347SMarcel Moolenaar if (fg == NULL)
4992788ca347SMarcel Moolenaar fg = "inverse";
4993788ca347SMarcel Moolenaar if (bg == NULL)
4994788ca347SMarcel Moolenaar bg = "inverse";
4995788ca347SMarcel Moolenaar
4996788ca347SMarcel Moolenaar }
4997788ca347SMarcel Moolenaar
4998788ca347SMarcel Moolenaar if (fg) {
4999788ca347SMarcel Moolenaar xo_buf_append_str(xbp, " color-fg-");
5000788ca347SMarcel Moolenaar xo_buf_append_str(xbp, fg);
5001788ca347SMarcel Moolenaar }
5002788ca347SMarcel Moolenaar
5003788ca347SMarcel Moolenaar if (bg) {
5004788ca347SMarcel Moolenaar xo_buf_append_str(xbp, " color-bg-");
5005788ca347SMarcel Moolenaar xo_buf_append_str(xbp, bg);
5006788ca347SMarcel Moolenaar }
5007788ca347SMarcel Moolenaar }
5008788ca347SMarcel Moolenaar
5009788ca347SMarcel Moolenaar static void
xo_format_colors(xo_handle_t * xop,xo_field_info_t * xfip,const char * value,ssize_t vlen)501042ff34c3SPhil Shafer xo_format_colors (xo_handle_t *xop, xo_field_info_t *xfip,
5011264104f2SPhil Shafer const char *value, ssize_t vlen)
5012788ca347SMarcel Moolenaar {
5013d1a0d267SMarcel Moolenaar const char *fmt = xfip->xfi_format;
50148a6eceffSPhil Shafer ssize_t flen = xfip->xfi_flen;
5015d1a0d267SMarcel Moolenaar
5016788ca347SMarcel Moolenaar xo_buffer_t xb;
5017788ca347SMarcel Moolenaar
5018788ca347SMarcel Moolenaar /* If the string is static and we've in an encoding style, bail */
5019264104f2SPhil Shafer if (vlen != 0 && xo_style_is_encoding(xop))
5020788ca347SMarcel Moolenaar return;
5021788ca347SMarcel Moolenaar
5022788ca347SMarcel Moolenaar xo_buf_init(&xb);
5023788ca347SMarcel Moolenaar
5024264104f2SPhil Shafer if (vlen)
5025264104f2SPhil Shafer xo_buf_append(&xb, value, vlen);
5026788ca347SMarcel Moolenaar else if (flen)
5027d1a0d267SMarcel Moolenaar xo_do_format_field(xop, &xb, fmt, flen, 0);
5028788ca347SMarcel Moolenaar else
5029788ca347SMarcel Moolenaar xo_buf_append(&xb, "reset", 6); /* Default if empty */
5030788ca347SMarcel Moolenaar
5031788ca347SMarcel Moolenaar if (xo_colors_enabled(xop)) {
5032788ca347SMarcel Moolenaar switch (xo_style(xop)) {
5033788ca347SMarcel Moolenaar case XO_STYLE_TEXT:
5034788ca347SMarcel Moolenaar case XO_STYLE_HTML:
5035788ca347SMarcel Moolenaar xo_buf_append(&xb, "", 1);
5036788ca347SMarcel Moolenaar
5037788ca347SMarcel Moolenaar xo_colors_t xoc = xop->xo_colors;
5038788ca347SMarcel Moolenaar xo_colors_parse(xop, &xoc, xb.xb_bufp);
5039f2b7bf8aSPhil Shafer xo_colors_update(xop, &xoc);
5040788ca347SMarcel Moolenaar
5041788ca347SMarcel Moolenaar if (xo_style(xop) == XO_STYLE_TEXT) {
5042788ca347SMarcel Moolenaar /*
5043788ca347SMarcel Moolenaar * Text mode means emitting the colors as ANSI character
5044788ca347SMarcel Moolenaar * codes. This will allow people who like colors to have
5045788ca347SMarcel Moolenaar * colors. The issue is, of course conflicting with the
5046788ca347SMarcel Moolenaar * user's perfectly reasonable color scheme. Which leads
5047788ca347SMarcel Moolenaar * to the hell of LSCOLORS, where even app need to have
5048788ca347SMarcel Moolenaar * customization hooks for adjusting colors. Instead we
5049788ca347SMarcel Moolenaar * provide a simpler-but-still-annoying answer where one
5050788ca347SMarcel Moolenaar * can map colors to other colors.
5051788ca347SMarcel Moolenaar */
5052788ca347SMarcel Moolenaar xo_colors_handle_text(xop, &xoc);
5053788ca347SMarcel Moolenaar xoc.xoc_effects &= ~XO_EFF_RESET; /* After handling it */
5054788ca347SMarcel Moolenaar
5055788ca347SMarcel Moolenaar } else {
5056788ca347SMarcel Moolenaar /*
5057788ca347SMarcel Moolenaar * HTML output is wrapped in divs, so the color information
5058788ca347SMarcel Moolenaar * must appear in every div until cleared. Most pathetic.
5059788ca347SMarcel Moolenaar * Most unavoidable.
5060788ca347SMarcel Moolenaar */
5061788ca347SMarcel Moolenaar xoc.xoc_effects &= ~XO_EFF_RESET; /* Before handling effects */
5062788ca347SMarcel Moolenaar xo_colors_handle_html(xop, &xoc);
5063788ca347SMarcel Moolenaar }
5064788ca347SMarcel Moolenaar
5065788ca347SMarcel Moolenaar xop->xo_colors = xoc;
5066788ca347SMarcel Moolenaar break;
5067788ca347SMarcel Moolenaar
5068788ca347SMarcel Moolenaar case XO_STYLE_XML:
5069788ca347SMarcel Moolenaar case XO_STYLE_JSON:
5070d1a0d267SMarcel Moolenaar case XO_STYLE_SDPARAMS:
5071d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER:
5072788ca347SMarcel Moolenaar /*
5073788ca347SMarcel Moolenaar * Nothing to do; we did all that work just to clear the stack of
5074788ca347SMarcel Moolenaar * formatting arguments.
5075788ca347SMarcel Moolenaar */
5076788ca347SMarcel Moolenaar break;
5077788ca347SMarcel Moolenaar }
5078788ca347SMarcel Moolenaar }
5079788ca347SMarcel Moolenaar
5080788ca347SMarcel Moolenaar xo_buf_cleanup(&xb);
5081788ca347SMarcel Moolenaar }
5082788ca347SMarcel Moolenaar
508331337658SMarcel Moolenaar static void
xo_format_units(xo_handle_t * xop,xo_field_info_t * xfip,const char * value,ssize_t vlen)508442ff34c3SPhil Shafer xo_format_units (xo_handle_t *xop, xo_field_info_t *xfip,
5085264104f2SPhil Shafer const char *value, ssize_t vlen)
508631337658SMarcel Moolenaar {
5087d1a0d267SMarcel Moolenaar const char *fmt = xfip->xfi_format;
50888a6eceffSPhil Shafer ssize_t flen = xfip->xfi_flen;
5089d1a0d267SMarcel Moolenaar xo_xff_flags_t flags = xfip->xfi_flags;
5090d1a0d267SMarcel Moolenaar
509131337658SMarcel Moolenaar static char units_start_xml[] = " units=\"";
509231337658SMarcel Moolenaar static char units_start_html[] = " data-units=\"";
509331337658SMarcel Moolenaar
5094d1a0d267SMarcel Moolenaar if (!XOIF_ISSET(xop, XOIF_UNITS_PENDING)) {
5095264104f2SPhil Shafer xo_format_content(xop, "units", NULL, value, vlen, fmt, flen, flags);
509631337658SMarcel Moolenaar return;
509731337658SMarcel Moolenaar }
509831337658SMarcel Moolenaar
509931337658SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_data;
51008a6eceffSPhil Shafer ssize_t start = xop->xo_units_offset;
51018a6eceffSPhil Shafer ssize_t stop = xbp->xb_curp - xbp->xb_bufp;
510231337658SMarcel Moolenaar
5103788ca347SMarcel Moolenaar if (xo_style(xop) == XO_STYLE_XML)
510431337658SMarcel Moolenaar xo_buf_append(xbp, units_start_xml, sizeof(units_start_xml) - 1);
5105788ca347SMarcel Moolenaar else if (xo_style(xop) == XO_STYLE_HTML)
510631337658SMarcel Moolenaar xo_buf_append(xbp, units_start_html, sizeof(units_start_html) - 1);
510731337658SMarcel Moolenaar else
510831337658SMarcel Moolenaar return;
510931337658SMarcel Moolenaar
5110264104f2SPhil Shafer if (vlen)
5111264104f2SPhil Shafer xo_data_escape(xop, value, vlen);
511231337658SMarcel Moolenaar else
5113d1a0d267SMarcel Moolenaar xo_do_format_field(xop, NULL, fmt, flen, flags);
511431337658SMarcel Moolenaar
511531337658SMarcel Moolenaar xo_buf_append(xbp, "\"", 1);
511631337658SMarcel Moolenaar
51178a6eceffSPhil Shafer ssize_t now = xbp->xb_curp - xbp->xb_bufp;
51188a6eceffSPhil Shafer ssize_t delta = now - stop;
5119d1a0d267SMarcel Moolenaar if (delta <= 0) { /* Strange; no output to move */
512031337658SMarcel Moolenaar xbp->xb_curp = xbp->xb_bufp + stop; /* Reset buffer to prior state */
512131337658SMarcel Moolenaar return;
512231337658SMarcel Moolenaar }
512331337658SMarcel Moolenaar
512431337658SMarcel Moolenaar /*
512531337658SMarcel Moolenaar * Now we're in it alright. We've need to insert the unit value
512631337658SMarcel Moolenaar * we just created into the right spot. We make a local copy,
512731337658SMarcel Moolenaar * move it and then insert our copy. We know there's room in the
512831337658SMarcel Moolenaar * buffer, since we're just moving this around.
512931337658SMarcel Moolenaar */
513031337658SMarcel Moolenaar char *buf = alloca(delta);
513131337658SMarcel Moolenaar
513231337658SMarcel Moolenaar memcpy(buf, xbp->xb_bufp + stop, delta);
513331337658SMarcel Moolenaar memmove(xbp->xb_bufp + start + delta, xbp->xb_bufp + start, stop - start);
513431337658SMarcel Moolenaar memmove(xbp->xb_bufp + start, buf, delta);
513531337658SMarcel Moolenaar }
513631337658SMarcel Moolenaar
51378a6eceffSPhil Shafer static ssize_t
xo_find_width(xo_handle_t * xop,xo_field_info_t * xfip,const char * value,ssize_t vlen)513842ff34c3SPhil Shafer xo_find_width (xo_handle_t *xop, xo_field_info_t *xfip,
5139264104f2SPhil Shafer const char *value, ssize_t vlen)
514031337658SMarcel Moolenaar {
5141d1a0d267SMarcel Moolenaar const char *fmt = xfip->xfi_format;
51428a6eceffSPhil Shafer ssize_t flen = xfip->xfi_flen;
5143d1a0d267SMarcel Moolenaar
514431337658SMarcel Moolenaar long width = 0;
514531337658SMarcel Moolenaar char *bp;
514631337658SMarcel Moolenaar char *cp;
514731337658SMarcel Moolenaar
5148264104f2SPhil Shafer if (vlen) {
5149264104f2SPhil Shafer bp = alloca(vlen + 1); /* Make local NUL-terminated copy of value */
5150264104f2SPhil Shafer memcpy(bp, value, vlen);
5151264104f2SPhil Shafer bp[vlen] = '\0';
515231337658SMarcel Moolenaar
515331337658SMarcel Moolenaar width = strtol(bp, &cp, 0);
5154fd5e3f3eSPhil Shafer if (width == LONG_MIN || width == LONG_MAX || bp == cp || *cp != '\0') {
5155fd5e3f3eSPhil Shafer width = 0;
5156fd5e3f3eSPhil Shafer xo_failure(xop, "invalid width for anchor: '%s'", bp);
5157fd5e3f3eSPhil Shafer }
5158fd5e3f3eSPhil Shafer } else if (flen) {
5159fd5e3f3eSPhil Shafer /*
5160fd5e3f3eSPhil Shafer * We really expect the format for width to be "{:/%d}" or
5161fd5e3f3eSPhil Shafer * "{:/%u}", so if that's the case, we just grab our width off
5162fd5e3f3eSPhil Shafer * the argument list. But we need to avoid optimized logic if
5163fd5e3f3eSPhil Shafer * there's a custom formatter.
5164fd5e3f3eSPhil Shafer */
5165fd5e3f3eSPhil Shafer if (xop->xo_formatter == NULL && flen == 2
5166fd5e3f3eSPhil Shafer && strncmp("%d", fmt, flen) == 0) {
5167fd5e3f3eSPhil Shafer if (!XOF_ISSET(xop, XOF_NO_VA_ARG))
5168fd5e3f3eSPhil Shafer width = va_arg(xop->xo_vap, int);
5169fd5e3f3eSPhil Shafer } else if (xop->xo_formatter == NULL && flen == 2
5170fd5e3f3eSPhil Shafer && strncmp("%u", fmt, flen) == 0) {
5171fd5e3f3eSPhil Shafer if (!XOF_ISSET(xop, XOF_NO_VA_ARG))
5172fd5e3f3eSPhil Shafer width = va_arg(xop->xo_vap, unsigned);
5173fd5e3f3eSPhil Shafer } else {
5174fd5e3f3eSPhil Shafer /*
5175fd5e3f3eSPhil Shafer * So we have a format and it's not a simple one like
5176fd5e3f3eSPhil Shafer * "{:/%d}". That means we need to format the field,
5177fd5e3f3eSPhil Shafer * extract the value from the formatted output, and then
5178fd5e3f3eSPhil Shafer * discard that output.
5179fd5e3f3eSPhil Shafer */
5180fd5e3f3eSPhil Shafer int anchor_was_set = FALSE;
5181fd5e3f3eSPhil Shafer xo_buffer_t *xbp = &xop->xo_data;
5182fd5e3f3eSPhil Shafer ssize_t start_offset = xo_buf_offset(xbp);
5183fd5e3f3eSPhil Shafer bp = xo_buf_cur(xbp); /* Save start of the string */
5184fd5e3f3eSPhil Shafer cp = NULL;
5185fd5e3f3eSPhil Shafer
5186fd5e3f3eSPhil Shafer if (XOIF_ISSET(xop, XOIF_ANCHOR)) {
5187fd5e3f3eSPhil Shafer XOIF_CLEAR(xop, XOIF_ANCHOR);
5188fd5e3f3eSPhil Shafer anchor_was_set = TRUE;
5189fd5e3f3eSPhil Shafer }
5190fd5e3f3eSPhil Shafer
5191fd5e3f3eSPhil Shafer ssize_t rc = xo_do_format_field(xop, xbp, fmt, flen, 0);
5192fd5e3f3eSPhil Shafer if (rc >= 0) {
5193fd5e3f3eSPhil Shafer xo_buf_append(xbp, "", 1); /* Append a NUL */
5194fd5e3f3eSPhil Shafer
5195fd5e3f3eSPhil Shafer width = strtol(bp, &cp, 0);
519631337658SMarcel Moolenaar if (width == LONG_MIN || width == LONG_MAX
519731337658SMarcel Moolenaar || bp == cp || *cp != '\0') {
519831337658SMarcel Moolenaar width = 0;
519931337658SMarcel Moolenaar xo_failure(xop, "invalid width for anchor: '%s'", bp);
520031337658SMarcel Moolenaar }
5201fd5e3f3eSPhil Shafer }
5202fd5e3f3eSPhil Shafer
5203fd5e3f3eSPhil Shafer /* Reset the cur pointer to where we found it */
5204fd5e3f3eSPhil Shafer xbp->xb_curp = xbp->xb_bufp + start_offset;
5205fd5e3f3eSPhil Shafer if (anchor_was_set)
5206fd5e3f3eSPhil Shafer XOIF_SET(xop, XOIF_ANCHOR);
5207fd5e3f3eSPhil Shafer }
520831337658SMarcel Moolenaar }
520931337658SMarcel Moolenaar
521031337658SMarcel Moolenaar return width;
521131337658SMarcel Moolenaar }
521231337658SMarcel Moolenaar
521331337658SMarcel Moolenaar static void
xo_anchor_clear(xo_handle_t * xop)521431337658SMarcel Moolenaar xo_anchor_clear (xo_handle_t *xop)
521531337658SMarcel Moolenaar {
5216d1a0d267SMarcel Moolenaar XOIF_CLEAR(xop, XOIF_ANCHOR);
521731337658SMarcel Moolenaar xop->xo_anchor_offset = 0;
521831337658SMarcel Moolenaar xop->xo_anchor_columns = 0;
521931337658SMarcel Moolenaar xop->xo_anchor_min_width = 0;
522031337658SMarcel Moolenaar }
522131337658SMarcel Moolenaar
522231337658SMarcel Moolenaar /*
522331337658SMarcel Moolenaar * An anchor is a marker used to delay field width implications.
522431337658SMarcel Moolenaar * Imagine the format string "{[:10}{min:%d}/{cur:%d}/{max:%d}{:]}".
522531337658SMarcel Moolenaar * We are looking for output like " 1/4/5"
522631337658SMarcel Moolenaar *
522731337658SMarcel Moolenaar * To make this work, we record the anchor and then return to
522831337658SMarcel Moolenaar * format it when the end anchor tag is seen.
522931337658SMarcel Moolenaar */
523031337658SMarcel Moolenaar static void
xo_anchor_start(xo_handle_t * xop,xo_field_info_t * xfip,const char * value,ssize_t vlen)523142ff34c3SPhil Shafer xo_anchor_start (xo_handle_t *xop, xo_field_info_t *xfip,
5232264104f2SPhil Shafer const char *value, ssize_t vlen)
523331337658SMarcel Moolenaar {
5234d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_ANCHOR))
523531337658SMarcel Moolenaar xo_failure(xop, "the anchor already recording is discarded");
523631337658SMarcel Moolenaar
5237d1a0d267SMarcel Moolenaar XOIF_SET(xop, XOIF_ANCHOR);
523831337658SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_data;
523931337658SMarcel Moolenaar xop->xo_anchor_offset = xbp->xb_curp - xbp->xb_bufp;
524031337658SMarcel Moolenaar xop->xo_anchor_columns = 0;
524131337658SMarcel Moolenaar
524231337658SMarcel Moolenaar /*
524331337658SMarcel Moolenaar * Now we find the width, if possible. If it's not there,
524431337658SMarcel Moolenaar * we'll get it on the end anchor.
524531337658SMarcel Moolenaar */
5246264104f2SPhil Shafer xop->xo_anchor_min_width = xo_find_width(xop, xfip, value, vlen);
524731337658SMarcel Moolenaar }
524831337658SMarcel Moolenaar
524931337658SMarcel Moolenaar static void
xo_anchor_stop(xo_handle_t * xop,xo_field_info_t * xfip,const char * value,ssize_t vlen)525042ff34c3SPhil Shafer xo_anchor_stop (xo_handle_t *xop, xo_field_info_t *xfip,
5251264104f2SPhil Shafer const char *value, ssize_t vlen)
525231337658SMarcel Moolenaar {
5253d1a0d267SMarcel Moolenaar if (!XOIF_ISSET(xop, XOIF_ANCHOR)) {
525431337658SMarcel Moolenaar xo_failure(xop, "no start anchor");
525531337658SMarcel Moolenaar return;
525631337658SMarcel Moolenaar }
525731337658SMarcel Moolenaar
5258d1a0d267SMarcel Moolenaar XOIF_CLEAR(xop, XOIF_UNITS_PENDING);
525931337658SMarcel Moolenaar
5260264104f2SPhil Shafer ssize_t width = xo_find_width(xop, xfip, value, vlen);
526131337658SMarcel Moolenaar if (width == 0)
526231337658SMarcel Moolenaar width = xop->xo_anchor_min_width;
526331337658SMarcel Moolenaar
526431337658SMarcel Moolenaar if (width == 0) /* No width given; nothing to do */
526531337658SMarcel Moolenaar goto done;
526631337658SMarcel Moolenaar
526731337658SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_data;
52688a6eceffSPhil Shafer ssize_t start = xop->xo_anchor_offset;
52698a6eceffSPhil Shafer ssize_t stop = xbp->xb_curp - xbp->xb_bufp;
52708a6eceffSPhil Shafer ssize_t abswidth = (width > 0) ? width : -width;
52718a6eceffSPhil Shafer ssize_t blen = abswidth - xop->xo_anchor_columns;
527231337658SMarcel Moolenaar
527331337658SMarcel Moolenaar if (blen <= 0) /* Already over width */
527431337658SMarcel Moolenaar goto done;
527531337658SMarcel Moolenaar
527631337658SMarcel Moolenaar if (abswidth > XO_MAX_ANCHOR_WIDTH) {
527731337658SMarcel Moolenaar xo_failure(xop, "width over %u are not supported",
527831337658SMarcel Moolenaar XO_MAX_ANCHOR_WIDTH);
527931337658SMarcel Moolenaar goto done;
528031337658SMarcel Moolenaar }
528131337658SMarcel Moolenaar
528231337658SMarcel Moolenaar /* Make a suitable padding field and emit it */
528331337658SMarcel Moolenaar char *buf = alloca(blen);
528431337658SMarcel Moolenaar memset(buf, ' ', blen);
5285d1a0d267SMarcel Moolenaar xo_format_content(xop, "padding", NULL, buf, blen, NULL, 0, 0);
528631337658SMarcel Moolenaar
528731337658SMarcel Moolenaar if (width < 0) /* Already left justified */
528831337658SMarcel Moolenaar goto done;
528931337658SMarcel Moolenaar
52908a6eceffSPhil Shafer ssize_t now = xbp->xb_curp - xbp->xb_bufp;
52918a6eceffSPhil Shafer ssize_t delta = now - stop;
5292d1a0d267SMarcel Moolenaar if (delta <= 0) /* Strange; no output to move */
529331337658SMarcel Moolenaar goto done;
529431337658SMarcel Moolenaar
529531337658SMarcel Moolenaar /*
529631337658SMarcel Moolenaar * Now we're in it alright. We've need to insert the padding data
529731337658SMarcel Moolenaar * we just created (which might be an HTML <div> or text) before
529831337658SMarcel Moolenaar * the formatted data. We make a local copy, move it and then
529931337658SMarcel Moolenaar * insert our copy. We know there's room in the buffer, since
530031337658SMarcel Moolenaar * we're just moving this around.
530131337658SMarcel Moolenaar */
530231337658SMarcel Moolenaar if (delta > blen)
530331337658SMarcel Moolenaar buf = alloca(delta); /* Expand buffer if needed */
530431337658SMarcel Moolenaar
530531337658SMarcel Moolenaar memcpy(buf, xbp->xb_bufp + stop, delta);
530631337658SMarcel Moolenaar memmove(xbp->xb_bufp + start + delta, xbp->xb_bufp + start, stop - start);
530731337658SMarcel Moolenaar memmove(xbp->xb_bufp + start, buf, delta);
530831337658SMarcel Moolenaar
530931337658SMarcel Moolenaar done:
531031337658SMarcel Moolenaar xo_anchor_clear(xop);
531131337658SMarcel Moolenaar }
531231337658SMarcel Moolenaar
5313d1a0d267SMarcel Moolenaar static const char *
xo_class_name(int ftype)5314d1a0d267SMarcel Moolenaar xo_class_name (int ftype)
531531337658SMarcel Moolenaar {
5316d1a0d267SMarcel Moolenaar switch (ftype) {
5317d1a0d267SMarcel Moolenaar case 'D': return "decoration";
5318d1a0d267SMarcel Moolenaar case 'E': return "error";
5319d1a0d267SMarcel Moolenaar case 'L': return "label";
5320d1a0d267SMarcel Moolenaar case 'N': return "note";
5321d1a0d267SMarcel Moolenaar case 'P': return "padding";
5322d1a0d267SMarcel Moolenaar case 'W': return "warning";
532331337658SMarcel Moolenaar }
532431337658SMarcel Moolenaar
5325d1a0d267SMarcel Moolenaar return NULL;
532631337658SMarcel Moolenaar }
532731337658SMarcel Moolenaar
5328d1a0d267SMarcel Moolenaar static const char *
xo_tag_name(int ftype)5329d1a0d267SMarcel Moolenaar xo_tag_name (int ftype)
5330d1a0d267SMarcel Moolenaar {
5331d1a0d267SMarcel Moolenaar switch (ftype) {
5332d1a0d267SMarcel Moolenaar case 'E': return "__error";
5333d1a0d267SMarcel Moolenaar case 'W': return "__warning";
5334d1a0d267SMarcel Moolenaar }
5335d1a0d267SMarcel Moolenaar
5336d1a0d267SMarcel Moolenaar return NULL;
5337d1a0d267SMarcel Moolenaar }
5338d1a0d267SMarcel Moolenaar
5339d1a0d267SMarcel Moolenaar static int
xo_role_wants_default_format(int ftype)5340d1a0d267SMarcel Moolenaar xo_role_wants_default_format (int ftype)
5341d1a0d267SMarcel Moolenaar {
5342d1a0d267SMarcel Moolenaar switch (ftype) {
5343d1a0d267SMarcel Moolenaar /* These roles can be completely empty and/or without formatting */
5344d1a0d267SMarcel Moolenaar case 'C':
5345d1a0d267SMarcel Moolenaar case 'G':
5346d1a0d267SMarcel Moolenaar case '[':
5347d1a0d267SMarcel Moolenaar case ']':
5348d1a0d267SMarcel Moolenaar return 0;
5349d1a0d267SMarcel Moolenaar }
5350d1a0d267SMarcel Moolenaar
5351d1a0d267SMarcel Moolenaar return 1;
5352d1a0d267SMarcel Moolenaar }
5353d1a0d267SMarcel Moolenaar
5354d1a0d267SMarcel Moolenaar static xo_mapping_t xo_role_names[] = {
5355d1a0d267SMarcel Moolenaar { 'C', "color" },
5356d1a0d267SMarcel Moolenaar { 'D', "decoration" },
5357d1a0d267SMarcel Moolenaar { 'E', "error" },
5358d1a0d267SMarcel Moolenaar { 'L', "label" },
5359d1a0d267SMarcel Moolenaar { 'N', "note" },
5360d1a0d267SMarcel Moolenaar { 'P', "padding" },
5361d1a0d267SMarcel Moolenaar { 'T', "title" },
5362d1a0d267SMarcel Moolenaar { 'U', "units" },
5363d1a0d267SMarcel Moolenaar { 'V', "value" },
5364d1a0d267SMarcel Moolenaar { 'W', "warning" },
5365d1a0d267SMarcel Moolenaar { '[', "start-anchor" },
5366d1a0d267SMarcel Moolenaar { ']', "stop-anchor" },
5367d1a0d267SMarcel Moolenaar { 0, NULL }
5368d1a0d267SMarcel Moolenaar };
5369d1a0d267SMarcel Moolenaar
5370d1a0d267SMarcel Moolenaar #define XO_ROLE_EBRACE '{' /* Escaped braces */
5371d1a0d267SMarcel Moolenaar #define XO_ROLE_TEXT '+'
5372d1a0d267SMarcel Moolenaar #define XO_ROLE_NEWLINE '\n'
5373d1a0d267SMarcel Moolenaar
5374d1a0d267SMarcel Moolenaar static xo_mapping_t xo_modifier_names[] = {
537542ff34c3SPhil Shafer { XFF_ARGUMENT, "argument" },
5376d1a0d267SMarcel Moolenaar { XFF_COLON, "colon" },
5377d1a0d267SMarcel Moolenaar { XFF_COMMA, "comma" },
5378d1a0d267SMarcel Moolenaar { XFF_DISPLAY_ONLY, "display" },
5379d1a0d267SMarcel Moolenaar { XFF_ENCODE_ONLY, "encoding" },
5380d1a0d267SMarcel Moolenaar { XFF_GT_FIELD, "gettext" },
5381d1a0d267SMarcel Moolenaar { XFF_HUMANIZE, "humanize" },
5382d1a0d267SMarcel Moolenaar { XFF_HUMANIZE, "hn" },
5383d1a0d267SMarcel Moolenaar { XFF_HN_SPACE, "hn-space" },
5384d1a0d267SMarcel Moolenaar { XFF_HN_DECIMAL, "hn-decimal" },
5385d1a0d267SMarcel Moolenaar { XFF_HN_1000, "hn-1000" },
5386d1a0d267SMarcel Moolenaar { XFF_KEY, "key" },
5387d1a0d267SMarcel Moolenaar { XFF_LEAF_LIST, "leaf-list" },
5388d1a0d267SMarcel Moolenaar { XFF_LEAF_LIST, "list" },
5389d1a0d267SMarcel Moolenaar { XFF_NOQUOTE, "no-quotes" },
5390d1a0d267SMarcel Moolenaar { XFF_NOQUOTE, "no-quote" },
5391d1a0d267SMarcel Moolenaar { XFF_GT_PLURAL, "plural" },
5392d1a0d267SMarcel Moolenaar { XFF_QUOTE, "quotes" },
5393d1a0d267SMarcel Moolenaar { XFF_QUOTE, "quote" },
5394d1a0d267SMarcel Moolenaar { XFF_TRIM_WS, "trim" },
5395d1a0d267SMarcel Moolenaar { XFF_WS, "white" },
5396d1a0d267SMarcel Moolenaar { 0, NULL }
5397d1a0d267SMarcel Moolenaar };
5398d1a0d267SMarcel Moolenaar
5399d1a0d267SMarcel Moolenaar #ifdef NOT_NEEDED_YET
5400d1a0d267SMarcel Moolenaar static xo_mapping_t xo_modifier_short_names[] = {
5401d1a0d267SMarcel Moolenaar { XFF_COLON, "c" },
5402d1a0d267SMarcel Moolenaar { XFF_DISPLAY_ONLY, "d" },
5403d1a0d267SMarcel Moolenaar { XFF_ENCODE_ONLY, "e" },
5404d1a0d267SMarcel Moolenaar { XFF_GT_FIELD, "g" },
5405d1a0d267SMarcel Moolenaar { XFF_HUMANIZE, "h" },
5406d1a0d267SMarcel Moolenaar { XFF_KEY, "k" },
5407d1a0d267SMarcel Moolenaar { XFF_LEAF_LIST, "l" },
5408d1a0d267SMarcel Moolenaar { XFF_NOQUOTE, "n" },
5409d1a0d267SMarcel Moolenaar { XFF_GT_PLURAL, "p" },
5410d1a0d267SMarcel Moolenaar { XFF_QUOTE, "q" },
5411d1a0d267SMarcel Moolenaar { XFF_TRIM_WS, "t" },
5412d1a0d267SMarcel Moolenaar { XFF_WS, "w" },
5413d1a0d267SMarcel Moolenaar { 0, NULL }
5414d1a0d267SMarcel Moolenaar };
5415d1a0d267SMarcel Moolenaar #endif /* NOT_NEEDED_YET */
5416d1a0d267SMarcel Moolenaar
5417d1a0d267SMarcel Moolenaar static int
xo_count_fields(xo_handle_t * xop UNUSED,const char * fmt)5418d1a0d267SMarcel Moolenaar xo_count_fields (xo_handle_t *xop UNUSED, const char *fmt)
5419d1a0d267SMarcel Moolenaar {
5420d1a0d267SMarcel Moolenaar int rc = 1;
5421d1a0d267SMarcel Moolenaar const char *cp;
5422d1a0d267SMarcel Moolenaar
5423d1a0d267SMarcel Moolenaar for (cp = fmt; *cp; cp++)
5424d1a0d267SMarcel Moolenaar if (*cp == '{' || *cp == '\n')
5425d1a0d267SMarcel Moolenaar rc += 1;
5426d1a0d267SMarcel Moolenaar
5427d1a0d267SMarcel Moolenaar return rc * 2 + 1;
5428d1a0d267SMarcel Moolenaar }
542931337658SMarcel Moolenaar
543031337658SMarcel Moolenaar /*
5431d1a0d267SMarcel Moolenaar * The field format is:
543231337658SMarcel Moolenaar * '{' modifiers ':' content [ '/' print-fmt [ '/' encode-fmt ]] '}'
5433d1a0d267SMarcel Moolenaar * Roles are optional and include the following field types:
543431337658SMarcel Moolenaar * 'D': decoration; something non-text and non-data (colons, commmas)
543531337658SMarcel Moolenaar * 'E': error message
5436d1a0d267SMarcel Moolenaar * 'G': gettext() the entire string; optional domainname as content
543731337658SMarcel Moolenaar * 'L': label; text preceding data
543831337658SMarcel Moolenaar * 'N': note; text following data
543931337658SMarcel Moolenaar * 'P': padding; whitespace
544031337658SMarcel Moolenaar * 'T': Title, where 'content' is a column title
544131337658SMarcel Moolenaar * 'U': Units, where 'content' is the unit label
544231337658SMarcel Moolenaar * 'V': value, where 'content' is the name of the field (the default)
544331337658SMarcel Moolenaar * 'W': warning message
544431337658SMarcel Moolenaar * '[': start a section of anchored text
544531337658SMarcel Moolenaar * ']': end a section of anchored text
5446d1a0d267SMarcel Moolenaar * The following modifiers are also supported:
544742ff34c3SPhil Shafer * 'a': content is provided via argument (const char *), not descriptor
544831337658SMarcel Moolenaar * 'c': flag: emit a colon after the label
5449d1a0d267SMarcel Moolenaar * 'd': field is only emitted for display styles (text and html)
5450d1a0d267SMarcel Moolenaar * 'e': field is only emitted for encoding styles (xml and json)
5451d1a0d267SMarcel Moolenaar * 'g': gettext() the field
5452d1a0d267SMarcel Moolenaar * 'h': humanize a numeric value (only for display styles)
545331337658SMarcel Moolenaar * 'k': this field is a key, suitable for XPath predicates
545431337658SMarcel Moolenaar * 'l': a leaf-list, a simple list of values
545531337658SMarcel Moolenaar * 'n': no quotes around this field
5456d1a0d267SMarcel Moolenaar * 'p': the field has plural gettext semantics (ngettext)
545731337658SMarcel Moolenaar * 'q': add quotes around this field
545831337658SMarcel Moolenaar * 't': trim whitespace around the value
545931337658SMarcel Moolenaar * 'w': emit a blank after the label
546031337658SMarcel Moolenaar * The print-fmt and encode-fmt strings is the printf-style formating
546131337658SMarcel Moolenaar * for this data. JSON and XML will use the encoding-fmt, if present.
546231337658SMarcel Moolenaar * If the encode-fmt is not provided, it defaults to the print-fmt.
546331337658SMarcel Moolenaar * If the print-fmt is not provided, it defaults to 's'.
546431337658SMarcel Moolenaar */
5465d1a0d267SMarcel Moolenaar static const char *
xo_parse_roles(xo_handle_t * xop,const char * fmt,const char * basep,xo_field_info_t * xfip)5466d1a0d267SMarcel Moolenaar xo_parse_roles (xo_handle_t *xop, const char *fmt,
5467d1a0d267SMarcel Moolenaar const char *basep, xo_field_info_t *xfip)
5468d1a0d267SMarcel Moolenaar {
5469d1a0d267SMarcel Moolenaar const char *sp;
5470d1a0d267SMarcel Moolenaar unsigned ftype = 0;
5471d1a0d267SMarcel Moolenaar xo_xff_flags_t flags = 0;
5472d1a0d267SMarcel Moolenaar uint8_t fnum = 0;
547331337658SMarcel Moolenaar
547442ff34c3SPhil Shafer for (sp = basep; sp && *sp; sp++) {
547531337658SMarcel Moolenaar if (*sp == ':' || *sp == '/' || *sp == '}')
547631337658SMarcel Moolenaar break;
547731337658SMarcel Moolenaar
547831337658SMarcel Moolenaar if (*sp == '\\') {
547931337658SMarcel Moolenaar if (sp[1] == '\0') {
548031337658SMarcel Moolenaar xo_failure(xop, "backslash at the end of string");
5481d1a0d267SMarcel Moolenaar return NULL;
548231337658SMarcel Moolenaar }
5483d1a0d267SMarcel Moolenaar
5484d1a0d267SMarcel Moolenaar /* Anything backslashed is ignored */
548531337658SMarcel Moolenaar sp += 1;
548631337658SMarcel Moolenaar continue;
548731337658SMarcel Moolenaar }
548831337658SMarcel Moolenaar
5489d1a0d267SMarcel Moolenaar if (*sp == ',') {
5490d1a0d267SMarcel Moolenaar const char *np;
5491d1a0d267SMarcel Moolenaar for (np = ++sp; *np; np++)
5492d1a0d267SMarcel Moolenaar if (*np == ':' || *np == '/' || *np == '}' || *np == ',')
5493d1a0d267SMarcel Moolenaar break;
5494d1a0d267SMarcel Moolenaar
54958a6eceffSPhil Shafer ssize_t slen = np - sp;
5496d1a0d267SMarcel Moolenaar if (slen > 0) {
5497d1a0d267SMarcel Moolenaar xo_xff_flags_t value;
5498d1a0d267SMarcel Moolenaar
5499d1a0d267SMarcel Moolenaar value = xo_name_lookup(xo_role_names, sp, slen);
5500d1a0d267SMarcel Moolenaar if (value)
5501d1a0d267SMarcel Moolenaar ftype = value;
5502d1a0d267SMarcel Moolenaar else {
5503d1a0d267SMarcel Moolenaar value = xo_name_lookup(xo_modifier_names, sp, slen);
5504d1a0d267SMarcel Moolenaar if (value)
5505d1a0d267SMarcel Moolenaar flags |= value;
5506d1a0d267SMarcel Moolenaar else
5507d1a0d267SMarcel Moolenaar xo_failure(xop, "unknown keyword ignored: '%.*s'",
5508d1a0d267SMarcel Moolenaar slen, sp);
5509d1a0d267SMarcel Moolenaar }
5510d1a0d267SMarcel Moolenaar }
5511d1a0d267SMarcel Moolenaar
5512d1a0d267SMarcel Moolenaar sp = np - 1;
5513d1a0d267SMarcel Moolenaar continue;
5514d1a0d267SMarcel Moolenaar }
5515d1a0d267SMarcel Moolenaar
551631337658SMarcel Moolenaar switch (*sp) {
5517788ca347SMarcel Moolenaar case 'C':
551831337658SMarcel Moolenaar case 'D':
551931337658SMarcel Moolenaar case 'E':
5520d1a0d267SMarcel Moolenaar case 'G':
552131337658SMarcel Moolenaar case 'L':
552231337658SMarcel Moolenaar case 'N':
552331337658SMarcel Moolenaar case 'P':
552431337658SMarcel Moolenaar case 'T':
552531337658SMarcel Moolenaar case 'U':
552631337658SMarcel Moolenaar case 'V':
552731337658SMarcel Moolenaar case 'W':
552831337658SMarcel Moolenaar case '[':
552931337658SMarcel Moolenaar case ']':
553031337658SMarcel Moolenaar if (ftype != 0) {
5531d1a0d267SMarcel Moolenaar xo_failure(xop, "field descriptor uses multiple types: '%s'",
5532d1a0d267SMarcel Moolenaar xo_printable(fmt));
5533d1a0d267SMarcel Moolenaar return NULL;
553431337658SMarcel Moolenaar }
553531337658SMarcel Moolenaar ftype = *sp;
553631337658SMarcel Moolenaar break;
553731337658SMarcel Moolenaar
5538d1a0d267SMarcel Moolenaar case '0':
5539d1a0d267SMarcel Moolenaar case '1':
5540d1a0d267SMarcel Moolenaar case '2':
5541d1a0d267SMarcel Moolenaar case '3':
5542d1a0d267SMarcel Moolenaar case '4':
5543d1a0d267SMarcel Moolenaar case '5':
5544d1a0d267SMarcel Moolenaar case '6':
5545d1a0d267SMarcel Moolenaar case '7':
5546d1a0d267SMarcel Moolenaar case '8':
5547d1a0d267SMarcel Moolenaar case '9':
5548d1a0d267SMarcel Moolenaar fnum = (fnum * 10) + (*sp - '0');
5549d1a0d267SMarcel Moolenaar break;
5550d1a0d267SMarcel Moolenaar
555142ff34c3SPhil Shafer case 'a':
555242ff34c3SPhil Shafer flags |= XFF_ARGUMENT;
555342ff34c3SPhil Shafer break;
555442ff34c3SPhil Shafer
555531337658SMarcel Moolenaar case 'c':
555631337658SMarcel Moolenaar flags |= XFF_COLON;
555731337658SMarcel Moolenaar break;
555831337658SMarcel Moolenaar
555931337658SMarcel Moolenaar case 'd':
556031337658SMarcel Moolenaar flags |= XFF_DISPLAY_ONLY;
556131337658SMarcel Moolenaar break;
556231337658SMarcel Moolenaar
556331337658SMarcel Moolenaar case 'e':
556431337658SMarcel Moolenaar flags |= XFF_ENCODE_ONLY;
556531337658SMarcel Moolenaar break;
556631337658SMarcel Moolenaar
5567d1a0d267SMarcel Moolenaar case 'g':
5568d1a0d267SMarcel Moolenaar flags |= XFF_GT_FIELD;
5569d1a0d267SMarcel Moolenaar break;
5570d1a0d267SMarcel Moolenaar
5571d1a0d267SMarcel Moolenaar case 'h':
5572d1a0d267SMarcel Moolenaar flags |= XFF_HUMANIZE;
5573d1a0d267SMarcel Moolenaar break;
5574d1a0d267SMarcel Moolenaar
557531337658SMarcel Moolenaar case 'k':
557631337658SMarcel Moolenaar flags |= XFF_KEY;
557731337658SMarcel Moolenaar break;
557831337658SMarcel Moolenaar
557931337658SMarcel Moolenaar case 'l':
558031337658SMarcel Moolenaar flags |= XFF_LEAF_LIST;
558131337658SMarcel Moolenaar break;
558231337658SMarcel Moolenaar
558331337658SMarcel Moolenaar case 'n':
558431337658SMarcel Moolenaar flags |= XFF_NOQUOTE;
558531337658SMarcel Moolenaar break;
558631337658SMarcel Moolenaar
5587d1a0d267SMarcel Moolenaar case 'p':
5588d1a0d267SMarcel Moolenaar flags |= XFF_GT_PLURAL;
5589d1a0d267SMarcel Moolenaar break;
5590d1a0d267SMarcel Moolenaar
559131337658SMarcel Moolenaar case 'q':
559231337658SMarcel Moolenaar flags |= XFF_QUOTE;
559331337658SMarcel Moolenaar break;
559431337658SMarcel Moolenaar
559531337658SMarcel Moolenaar case 't':
559631337658SMarcel Moolenaar flags |= XFF_TRIM_WS;
559731337658SMarcel Moolenaar break;
559831337658SMarcel Moolenaar
559931337658SMarcel Moolenaar case 'w':
560031337658SMarcel Moolenaar flags |= XFF_WS;
560131337658SMarcel Moolenaar break;
560231337658SMarcel Moolenaar
560331337658SMarcel Moolenaar default:
5604d1a0d267SMarcel Moolenaar xo_failure(xop, "field descriptor uses unknown modifier: '%s'",
5605d1a0d267SMarcel Moolenaar xo_printable(fmt));
560631337658SMarcel Moolenaar /*
560731337658SMarcel Moolenaar * No good answer here; a bad format will likely
560831337658SMarcel Moolenaar * mean a core file. We just return and hope
560931337658SMarcel Moolenaar * the caller notices there's no output, and while
5610d1a0d267SMarcel Moolenaar * that seems, well, bad, there's nothing better.
561131337658SMarcel Moolenaar */
5612d1a0d267SMarcel Moolenaar return NULL;
5613d1a0d267SMarcel Moolenaar }
5614d1a0d267SMarcel Moolenaar
5615d1a0d267SMarcel Moolenaar if (ftype == 'N' || ftype == 'U') {
5616d1a0d267SMarcel Moolenaar if (flags & XFF_COLON) {
5617d1a0d267SMarcel Moolenaar xo_failure(xop, "colon modifier on 'N' or 'U' field ignored: "
5618d1a0d267SMarcel Moolenaar "'%s'", xo_printable(fmt));
5619d1a0d267SMarcel Moolenaar flags &= ~XFF_COLON;
5620d1a0d267SMarcel Moolenaar }
562131337658SMarcel Moolenaar }
562231337658SMarcel Moolenaar }
562331337658SMarcel Moolenaar
5624d1a0d267SMarcel Moolenaar xfip->xfi_flags = flags;
5625d1a0d267SMarcel Moolenaar xfip->xfi_ftype = ftype ?: 'V';
5626d1a0d267SMarcel Moolenaar xfip->xfi_fnum = fnum;
5627d1a0d267SMarcel Moolenaar
5628d1a0d267SMarcel Moolenaar return sp;
5629d1a0d267SMarcel Moolenaar }
5630d1a0d267SMarcel Moolenaar
5631d1a0d267SMarcel Moolenaar /*
5632d1a0d267SMarcel Moolenaar * Number any remaining fields that need numbers. Note that some
5633d1a0d267SMarcel Moolenaar * field types (text, newline, escaped braces) never get numbers.
5634d1a0d267SMarcel Moolenaar */
5635d1a0d267SMarcel Moolenaar static void
xo_gettext_finish_numbering_fields(xo_handle_t * xop UNUSED,const char * fmt UNUSED,xo_field_info_t * fields)5636d1a0d267SMarcel Moolenaar xo_gettext_finish_numbering_fields (xo_handle_t *xop UNUSED,
5637d1a0d267SMarcel Moolenaar const char *fmt UNUSED,
5638d1a0d267SMarcel Moolenaar xo_field_info_t *fields)
5639d1a0d267SMarcel Moolenaar {
5640d1a0d267SMarcel Moolenaar xo_field_info_t *xfip;
5641d1a0d267SMarcel Moolenaar unsigned fnum, max_fields;
5642d1a0d267SMarcel Moolenaar uint64_t bits = 0;
564376afb20cSPhil Shafer const uint64_t one = 1; /* Avoid "1ULL" */
5644d1a0d267SMarcel Moolenaar
5645d1a0d267SMarcel Moolenaar /* First make a list of add the explicitly used bits */
5646d1a0d267SMarcel Moolenaar for (xfip = fields, fnum = 0; xfip->xfi_ftype; xfip++) {
5647d1a0d267SMarcel Moolenaar switch (xfip->xfi_ftype) {
5648d1a0d267SMarcel Moolenaar case XO_ROLE_NEWLINE: /* Don't get numbered */
5649d1a0d267SMarcel Moolenaar case XO_ROLE_TEXT:
5650d1a0d267SMarcel Moolenaar case XO_ROLE_EBRACE:
5651d1a0d267SMarcel Moolenaar case 'G':
5652d1a0d267SMarcel Moolenaar continue;
5653d1a0d267SMarcel Moolenaar }
5654d1a0d267SMarcel Moolenaar
5655d1a0d267SMarcel Moolenaar fnum += 1;
5656d1a0d267SMarcel Moolenaar if (fnum >= 63)
5657d1a0d267SMarcel Moolenaar break;
5658d1a0d267SMarcel Moolenaar
5659d1a0d267SMarcel Moolenaar if (xfip->xfi_fnum)
566076afb20cSPhil Shafer bits |= one << xfip->xfi_fnum;
5661d1a0d267SMarcel Moolenaar }
5662d1a0d267SMarcel Moolenaar
5663d1a0d267SMarcel Moolenaar max_fields = fnum;
5664d1a0d267SMarcel Moolenaar
5665d1a0d267SMarcel Moolenaar for (xfip = fields, fnum = 0; xfip->xfi_ftype; xfip++) {
5666d1a0d267SMarcel Moolenaar switch (xfip->xfi_ftype) {
5667d1a0d267SMarcel Moolenaar case XO_ROLE_NEWLINE: /* Don't get numbered */
5668d1a0d267SMarcel Moolenaar case XO_ROLE_TEXT:
5669d1a0d267SMarcel Moolenaar case XO_ROLE_EBRACE:
5670d1a0d267SMarcel Moolenaar case 'G':
5671d1a0d267SMarcel Moolenaar continue;
5672d1a0d267SMarcel Moolenaar }
5673d1a0d267SMarcel Moolenaar
5674d1a0d267SMarcel Moolenaar if (xfip->xfi_fnum != 0)
5675d1a0d267SMarcel Moolenaar continue;
5676d1a0d267SMarcel Moolenaar
5677d1a0d267SMarcel Moolenaar /* Find the next unassigned field */
567876afb20cSPhil Shafer for (fnum++; bits & (one << fnum); fnum++)
5679d1a0d267SMarcel Moolenaar continue;
5680d1a0d267SMarcel Moolenaar
5681d1a0d267SMarcel Moolenaar if (fnum > max_fields)
5682d1a0d267SMarcel Moolenaar break;
5683d1a0d267SMarcel Moolenaar
5684d1a0d267SMarcel Moolenaar xfip->xfi_fnum = fnum; /* Mark the field number */
568576afb20cSPhil Shafer bits |= one << fnum; /* Mark it used */
5686d1a0d267SMarcel Moolenaar }
5687d1a0d267SMarcel Moolenaar }
5688d1a0d267SMarcel Moolenaar
5689d1a0d267SMarcel Moolenaar /*
5690ee5cf116SPhil Shafer * The format string uses field numbers, so we need to whiffle through it
5691d1a0d267SMarcel Moolenaar * and make sure everything's sane and lovely.
5692d1a0d267SMarcel Moolenaar */
5693d1a0d267SMarcel Moolenaar static int
xo_parse_field_numbers(xo_handle_t * xop,const char * fmt,xo_field_info_t * fields,unsigned num_fields)5694d1a0d267SMarcel Moolenaar xo_parse_field_numbers (xo_handle_t *xop, const char *fmt,
5695d1a0d267SMarcel Moolenaar xo_field_info_t *fields, unsigned num_fields)
5696d1a0d267SMarcel Moolenaar {
5697d1a0d267SMarcel Moolenaar xo_field_info_t *xfip;
5698d1a0d267SMarcel Moolenaar unsigned field, fnum;
5699d1a0d267SMarcel Moolenaar uint64_t bits = 0;
570076afb20cSPhil Shafer const uint64_t one = 1; /* Avoid 1ULL */
5701d1a0d267SMarcel Moolenaar
5702d1a0d267SMarcel Moolenaar for (xfip = fields, field = 0; field < num_fields; xfip++, field++) {
5703d1a0d267SMarcel Moolenaar /* Fields default to 1:1 with natural position */
5704d1a0d267SMarcel Moolenaar if (xfip->xfi_fnum == 0)
5705d1a0d267SMarcel Moolenaar xfip->xfi_fnum = field + 1;
5706d1a0d267SMarcel Moolenaar else if (xfip->xfi_fnum > num_fields) {
5707d1a0d267SMarcel Moolenaar xo_failure(xop, "field number exceeds number of fields: '%s'", fmt);
5708d1a0d267SMarcel Moolenaar return -1;
5709d1a0d267SMarcel Moolenaar }
5710d1a0d267SMarcel Moolenaar
5711d1a0d267SMarcel Moolenaar fnum = xfip->xfi_fnum - 1; /* Move to zero origin */
5712d1a0d267SMarcel Moolenaar if (fnum < 64) { /* Only test what fits */
571376afb20cSPhil Shafer if (bits & (one << fnum)) {
5714d1a0d267SMarcel Moolenaar xo_failure(xop, "field number %u reused: '%s'",
5715d1a0d267SMarcel Moolenaar xfip->xfi_fnum, fmt);
5716d1a0d267SMarcel Moolenaar return -1;
5717d1a0d267SMarcel Moolenaar }
571876afb20cSPhil Shafer bits |= one << fnum;
5719d1a0d267SMarcel Moolenaar }
5720d1a0d267SMarcel Moolenaar }
5721d1a0d267SMarcel Moolenaar
5722d1a0d267SMarcel Moolenaar return 0;
5723d1a0d267SMarcel Moolenaar }
5724d1a0d267SMarcel Moolenaar
5725d1a0d267SMarcel Moolenaar static int
xo_parse_fields(xo_handle_t * xop,xo_field_info_t * fields,unsigned num_fields,const char * fmt)5726d1a0d267SMarcel Moolenaar xo_parse_fields (xo_handle_t *xop, xo_field_info_t *fields,
5727d1a0d267SMarcel Moolenaar unsigned num_fields, const char *fmt)
5728d1a0d267SMarcel Moolenaar {
5729d1a0d267SMarcel Moolenaar const char *cp, *sp, *ep, *basep;
5730d1a0d267SMarcel Moolenaar unsigned field = 0;
5731d1a0d267SMarcel Moolenaar xo_field_info_t *xfip = fields;
5732d1a0d267SMarcel Moolenaar unsigned seen_fnum = 0;
5733d1a0d267SMarcel Moolenaar
5734d1a0d267SMarcel Moolenaar for (cp = fmt; *cp && field < num_fields; field++, xfip++) {
5735d1a0d267SMarcel Moolenaar xfip->xfi_start = cp;
5736d1a0d267SMarcel Moolenaar
5737d1a0d267SMarcel Moolenaar if (*cp == '\n') {
5738d1a0d267SMarcel Moolenaar xfip->xfi_ftype = XO_ROLE_NEWLINE;
5739d1a0d267SMarcel Moolenaar xfip->xfi_len = 1;
5740d1a0d267SMarcel Moolenaar cp += 1;
5741d1a0d267SMarcel Moolenaar continue;
5742d1a0d267SMarcel Moolenaar }
5743d1a0d267SMarcel Moolenaar
5744d1a0d267SMarcel Moolenaar if (*cp != '{') {
5745d1a0d267SMarcel Moolenaar /* Normal text */
5746d1a0d267SMarcel Moolenaar for (sp = cp; *sp; sp++) {
5747d1a0d267SMarcel Moolenaar if (*sp == '{' || *sp == '\n')
5748d1a0d267SMarcel Moolenaar break;
5749d1a0d267SMarcel Moolenaar }
5750d1a0d267SMarcel Moolenaar
5751d1a0d267SMarcel Moolenaar xfip->xfi_ftype = XO_ROLE_TEXT;
5752d1a0d267SMarcel Moolenaar xfip->xfi_content = cp;
5753d1a0d267SMarcel Moolenaar xfip->xfi_clen = sp - cp;
5754d1a0d267SMarcel Moolenaar xfip->xfi_next = sp;
5755d1a0d267SMarcel Moolenaar
5756d1a0d267SMarcel Moolenaar cp = sp;
5757d1a0d267SMarcel Moolenaar continue;
5758d1a0d267SMarcel Moolenaar }
5759d1a0d267SMarcel Moolenaar
5760d1a0d267SMarcel Moolenaar if (cp[1] == '{') { /* Start of {{escaped braces}} */
5761d1a0d267SMarcel Moolenaar xfip->xfi_start = cp + 1; /* Start at second brace */
5762d1a0d267SMarcel Moolenaar xfip->xfi_ftype = XO_ROLE_EBRACE;
5763d1a0d267SMarcel Moolenaar
5764d1a0d267SMarcel Moolenaar cp += 2; /* Skip over _both_ characters */
5765d1a0d267SMarcel Moolenaar for (sp = cp; *sp; sp++) {
5766d1a0d267SMarcel Moolenaar if (*sp == '}' && sp[1] == '}')
5767d1a0d267SMarcel Moolenaar break;
5768d1a0d267SMarcel Moolenaar }
5769d1a0d267SMarcel Moolenaar if (*sp == '\0') {
5770d1a0d267SMarcel Moolenaar xo_failure(xop, "missing closing '}}': '%s'",
5771d1a0d267SMarcel Moolenaar xo_printable(fmt));
5772d1a0d267SMarcel Moolenaar return -1;
5773d1a0d267SMarcel Moolenaar }
5774d1a0d267SMarcel Moolenaar
5775d1a0d267SMarcel Moolenaar xfip->xfi_len = sp - xfip->xfi_start + 1;
5776d1a0d267SMarcel Moolenaar
5777d1a0d267SMarcel Moolenaar /* Move along the string, but don't run off the end */
577876afb20cSPhil Shafer if (*sp == '}' && sp[1] == '}') /* Paranoid; must be true */
5779d1a0d267SMarcel Moolenaar sp += 2;
578076afb20cSPhil Shafer
578176afb20cSPhil Shafer cp = sp;
5782d1a0d267SMarcel Moolenaar xfip->xfi_next = cp;
5783d1a0d267SMarcel Moolenaar continue;
5784d1a0d267SMarcel Moolenaar }
5785d1a0d267SMarcel Moolenaar
5786d1a0d267SMarcel Moolenaar /* We are looking at the start of a field definition */
5787d1a0d267SMarcel Moolenaar xfip->xfi_start = basep = cp + 1;
5788d1a0d267SMarcel Moolenaar
5789d1a0d267SMarcel Moolenaar const char *format = NULL;
57908a6eceffSPhil Shafer ssize_t flen = 0;
5791d1a0d267SMarcel Moolenaar
5792d1a0d267SMarcel Moolenaar /* Looking at roles and modifiers */
5793d1a0d267SMarcel Moolenaar sp = xo_parse_roles(xop, fmt, basep, xfip);
5794d1a0d267SMarcel Moolenaar if (sp == NULL) {
5795d1a0d267SMarcel Moolenaar /* xo_failure has already been called */
5796d1a0d267SMarcel Moolenaar return -1;
5797d1a0d267SMarcel Moolenaar }
5798d1a0d267SMarcel Moolenaar
5799d1a0d267SMarcel Moolenaar if (xfip->xfi_fnum)
5800d1a0d267SMarcel Moolenaar seen_fnum = 1;
5801d1a0d267SMarcel Moolenaar
5802d1a0d267SMarcel Moolenaar /* Looking at content */
580331337658SMarcel Moolenaar if (*sp == ':') {
580431337658SMarcel Moolenaar for (ep = ++sp; *sp; sp++) {
580531337658SMarcel Moolenaar if (*sp == '}' || *sp == '/')
580631337658SMarcel Moolenaar break;
580731337658SMarcel Moolenaar if (*sp == '\\') {
580831337658SMarcel Moolenaar if (sp[1] == '\0') {
580931337658SMarcel Moolenaar xo_failure(xop, "backslash at the end of string");
581031337658SMarcel Moolenaar return -1;
581131337658SMarcel Moolenaar }
581231337658SMarcel Moolenaar sp += 1;
581331337658SMarcel Moolenaar continue;
581431337658SMarcel Moolenaar }
581531337658SMarcel Moolenaar }
581631337658SMarcel Moolenaar if (ep != sp) {
5817d1a0d267SMarcel Moolenaar xfip->xfi_clen = sp - ep;
5818d1a0d267SMarcel Moolenaar xfip->xfi_content = ep;
581931337658SMarcel Moolenaar }
582031337658SMarcel Moolenaar } else {
5821d1a0d267SMarcel Moolenaar xo_failure(xop, "missing content (':'): '%s'", xo_printable(fmt));
582231337658SMarcel Moolenaar return -1;
582331337658SMarcel Moolenaar }
582431337658SMarcel Moolenaar
5825d1a0d267SMarcel Moolenaar /* Looking at main (display) format */
582631337658SMarcel Moolenaar if (*sp == '/') {
582731337658SMarcel Moolenaar for (ep = ++sp; *sp; sp++) {
582831337658SMarcel Moolenaar if (*sp == '}' || *sp == '/')
582931337658SMarcel Moolenaar break;
583031337658SMarcel Moolenaar if (*sp == '\\') {
583131337658SMarcel Moolenaar if (sp[1] == '\0') {
583231337658SMarcel Moolenaar xo_failure(xop, "backslash at the end of string");
583331337658SMarcel Moolenaar return -1;
583431337658SMarcel Moolenaar }
583531337658SMarcel Moolenaar sp += 1;
583631337658SMarcel Moolenaar continue;
583731337658SMarcel Moolenaar }
583831337658SMarcel Moolenaar }
583931337658SMarcel Moolenaar flen = sp - ep;
584031337658SMarcel Moolenaar format = ep;
584131337658SMarcel Moolenaar }
584231337658SMarcel Moolenaar
5843d1a0d267SMarcel Moolenaar /* Looking at encoding format */
584431337658SMarcel Moolenaar if (*sp == '/') {
584531337658SMarcel Moolenaar for (ep = ++sp; *sp; sp++) {
584631337658SMarcel Moolenaar if (*sp == '}')
584731337658SMarcel Moolenaar break;
584831337658SMarcel Moolenaar }
5849d1a0d267SMarcel Moolenaar
5850d1a0d267SMarcel Moolenaar xfip->xfi_encoding = ep;
5851d1a0d267SMarcel Moolenaar xfip->xfi_elen = sp - ep;
585231337658SMarcel Moolenaar }
585331337658SMarcel Moolenaar
5854d1a0d267SMarcel Moolenaar if (*sp != '}') {
5855d1a0d267SMarcel Moolenaar xo_failure(xop, "missing closing '}': %s", xo_printable(fmt));
585631337658SMarcel Moolenaar return -1;
585731337658SMarcel Moolenaar }
585831337658SMarcel Moolenaar
5859d1a0d267SMarcel Moolenaar xfip->xfi_len = sp - xfip->xfi_start;
5860d1a0d267SMarcel Moolenaar xfip->xfi_next = ++sp;
5861d1a0d267SMarcel Moolenaar
5862d1a0d267SMarcel Moolenaar /* If we have content, then we have a default format */
586342ff34c3SPhil Shafer if (xfip->xfi_clen || format || (xfip->xfi_flags & XFF_ARGUMENT)) {
5864d1a0d267SMarcel Moolenaar if (format) {
5865d1a0d267SMarcel Moolenaar xfip->xfi_format = format;
5866d1a0d267SMarcel Moolenaar xfip->xfi_flen = flen;
5867d1a0d267SMarcel Moolenaar } else if (xo_role_wants_default_format(xfip->xfi_ftype)) {
586842ff34c3SPhil Shafer xfip->xfi_format = xo_default_format;
5869d1a0d267SMarcel Moolenaar xfip->xfi_flen = 2;
5870d1a0d267SMarcel Moolenaar }
587131337658SMarcel Moolenaar }
587231337658SMarcel Moolenaar
5873d1a0d267SMarcel Moolenaar cp = sp;
5874d1a0d267SMarcel Moolenaar }
5875545ddfbeSMarcel Moolenaar
5876d1a0d267SMarcel Moolenaar int rc = 0;
5877d1a0d267SMarcel Moolenaar
5878d1a0d267SMarcel Moolenaar /*
5879d1a0d267SMarcel Moolenaar * If we saw a field number on at least one field, then we need
5880d1a0d267SMarcel Moolenaar * to enforce some rules and/or guidelines.
5881d1a0d267SMarcel Moolenaar */
5882d1a0d267SMarcel Moolenaar if (seen_fnum)
5883d1a0d267SMarcel Moolenaar rc = xo_parse_field_numbers(xop, fmt, fields, field);
5884d1a0d267SMarcel Moolenaar
5885d1a0d267SMarcel Moolenaar return rc;
5886d1a0d267SMarcel Moolenaar }
5887d1a0d267SMarcel Moolenaar
5888d1a0d267SMarcel Moolenaar /*
5889d1a0d267SMarcel Moolenaar * We are passed a pointer to a format string just past the "{G:}"
5890d1a0d267SMarcel Moolenaar * field. We build a simplified version of the format string.
5891d1a0d267SMarcel Moolenaar */
5892d1a0d267SMarcel Moolenaar static int
xo_gettext_simplify_format(xo_handle_t * xop UNUSED,xo_buffer_t * xbp,xo_field_info_t * fields,int this_field,const char * fmt UNUSED,xo_simplify_field_func_t field_cb)5893d1a0d267SMarcel Moolenaar xo_gettext_simplify_format (xo_handle_t *xop UNUSED,
5894d1a0d267SMarcel Moolenaar xo_buffer_t *xbp,
5895d1a0d267SMarcel Moolenaar xo_field_info_t *fields,
5896d1a0d267SMarcel Moolenaar int this_field,
5897d1a0d267SMarcel Moolenaar const char *fmt UNUSED,
5898d1a0d267SMarcel Moolenaar xo_simplify_field_func_t field_cb)
5899d1a0d267SMarcel Moolenaar {
5900d1a0d267SMarcel Moolenaar unsigned ftype;
5901d1a0d267SMarcel Moolenaar xo_xff_flags_t flags;
5902d1a0d267SMarcel Moolenaar int field = this_field + 1;
5903d1a0d267SMarcel Moolenaar xo_field_info_t *xfip;
5904d1a0d267SMarcel Moolenaar char ch;
5905d1a0d267SMarcel Moolenaar
5906d1a0d267SMarcel Moolenaar for (xfip = &fields[field]; xfip->xfi_ftype; xfip++, field++) {
5907d1a0d267SMarcel Moolenaar ftype = xfip->xfi_ftype;
5908d1a0d267SMarcel Moolenaar flags = xfip->xfi_flags;
5909d1a0d267SMarcel Moolenaar
5910d1a0d267SMarcel Moolenaar if ((flags & XFF_GT_FIELD) && xfip->xfi_content && ftype != 'V') {
5911d1a0d267SMarcel Moolenaar if (field_cb)
5912d1a0d267SMarcel Moolenaar field_cb(xfip->xfi_content, xfip->xfi_clen,
5913d1a0d267SMarcel Moolenaar (flags & XFF_GT_PLURAL) ? 1 : 0);
5914d1a0d267SMarcel Moolenaar }
5915d1a0d267SMarcel Moolenaar
5916d1a0d267SMarcel Moolenaar switch (ftype) {
5917d1a0d267SMarcel Moolenaar case 'G':
5918d1a0d267SMarcel Moolenaar /* Ignore gettext roles */
5919d1a0d267SMarcel Moolenaar break;
5920d1a0d267SMarcel Moolenaar
5921d1a0d267SMarcel Moolenaar case XO_ROLE_NEWLINE:
5922d1a0d267SMarcel Moolenaar xo_buf_append(xbp, "\n", 1);
5923d1a0d267SMarcel Moolenaar break;
5924d1a0d267SMarcel Moolenaar
5925d1a0d267SMarcel Moolenaar case XO_ROLE_EBRACE:
5926d1a0d267SMarcel Moolenaar xo_buf_append(xbp, "{", 1);
5927d1a0d267SMarcel Moolenaar xo_buf_append(xbp, xfip->xfi_content, xfip->xfi_clen);
5928d1a0d267SMarcel Moolenaar xo_buf_append(xbp, "}", 1);
5929d1a0d267SMarcel Moolenaar break;
5930d1a0d267SMarcel Moolenaar
5931d1a0d267SMarcel Moolenaar case XO_ROLE_TEXT:
5932d1a0d267SMarcel Moolenaar xo_buf_append(xbp, xfip->xfi_content, xfip->xfi_clen);
5933d1a0d267SMarcel Moolenaar break;
5934d1a0d267SMarcel Moolenaar
5935d1a0d267SMarcel Moolenaar default:
5936d1a0d267SMarcel Moolenaar xo_buf_append(xbp, "{", 1);
5937d1a0d267SMarcel Moolenaar if (ftype != 'V') {
5938d1a0d267SMarcel Moolenaar ch = ftype;
5939d1a0d267SMarcel Moolenaar xo_buf_append(xbp, &ch, 1);
5940d1a0d267SMarcel Moolenaar }
5941d1a0d267SMarcel Moolenaar
5942d1a0d267SMarcel Moolenaar unsigned fnum = xfip->xfi_fnum ?: 0;
5943d1a0d267SMarcel Moolenaar if (fnum) {
5944d1a0d267SMarcel Moolenaar char num[12];
5945d1a0d267SMarcel Moolenaar /* Field numbers are origin 1, not 0, following printf(3) */
5946d1a0d267SMarcel Moolenaar snprintf(num, sizeof(num), "%u", fnum);
5947d1a0d267SMarcel Moolenaar xo_buf_append(xbp, num, strlen(num));
5948d1a0d267SMarcel Moolenaar }
5949d1a0d267SMarcel Moolenaar
5950d1a0d267SMarcel Moolenaar xo_buf_append(xbp, ":", 1);
5951d1a0d267SMarcel Moolenaar xo_buf_append(xbp, xfip->xfi_content, xfip->xfi_clen);
5952d1a0d267SMarcel Moolenaar xo_buf_append(xbp, "}", 1);
5953d1a0d267SMarcel Moolenaar }
5954d1a0d267SMarcel Moolenaar }
5955d1a0d267SMarcel Moolenaar
5956d1a0d267SMarcel Moolenaar xo_buf_append(xbp, "", 1);
5957d1a0d267SMarcel Moolenaar return 0;
5958d1a0d267SMarcel Moolenaar }
5959d1a0d267SMarcel Moolenaar
5960d1a0d267SMarcel Moolenaar void
5961d1a0d267SMarcel Moolenaar xo_dump_fields (xo_field_info_t *); /* Fake prototype for debug function */
5962d1a0d267SMarcel Moolenaar void
xo_dump_fields(xo_field_info_t * fields)5963d1a0d267SMarcel Moolenaar xo_dump_fields (xo_field_info_t *fields)
5964d1a0d267SMarcel Moolenaar {
5965d1a0d267SMarcel Moolenaar xo_field_info_t *xfip;
5966d1a0d267SMarcel Moolenaar
5967d1a0d267SMarcel Moolenaar for (xfip = fields; xfip->xfi_ftype; xfip++) {
5968d1a0d267SMarcel Moolenaar printf("%lu(%u): %lx [%c/%u] [%.*s] [%.*s] [%.*s]\n",
5969d1a0d267SMarcel Moolenaar (unsigned long) (xfip - fields), xfip->xfi_fnum,
5970d1a0d267SMarcel Moolenaar (unsigned long) xfip->xfi_flags,
5971d1a0d267SMarcel Moolenaar isprint((int) xfip->xfi_ftype) ? xfip->xfi_ftype : ' ',
5972d1a0d267SMarcel Moolenaar xfip->xfi_ftype,
59738a6eceffSPhil Shafer (int) xfip->xfi_clen, xfip->xfi_content ?: "",
59748a6eceffSPhil Shafer (int) xfip->xfi_flen, xfip->xfi_format ?: "",
59758a6eceffSPhil Shafer (int) xfip->xfi_elen, xfip->xfi_encoding ?: "");
5976d1a0d267SMarcel Moolenaar }
5977d1a0d267SMarcel Moolenaar }
5978d1a0d267SMarcel Moolenaar
5979d1a0d267SMarcel Moolenaar #ifdef HAVE_GETTEXT
5980d1a0d267SMarcel Moolenaar /*
5981d1a0d267SMarcel Moolenaar * Find the field that matches the given field number
5982d1a0d267SMarcel Moolenaar */
5983d1a0d267SMarcel Moolenaar static xo_field_info_t *
xo_gettext_find_field(xo_field_info_t * fields,unsigned fnum)5984d1a0d267SMarcel Moolenaar xo_gettext_find_field (xo_field_info_t *fields, unsigned fnum)
5985d1a0d267SMarcel Moolenaar {
5986d1a0d267SMarcel Moolenaar xo_field_info_t *xfip;
5987d1a0d267SMarcel Moolenaar
5988d1a0d267SMarcel Moolenaar for (xfip = fields; xfip->xfi_ftype; xfip++)
5989d1a0d267SMarcel Moolenaar if (xfip->xfi_fnum == fnum)
5990d1a0d267SMarcel Moolenaar return xfip;
5991d1a0d267SMarcel Moolenaar
5992d1a0d267SMarcel Moolenaar return NULL;
5993d1a0d267SMarcel Moolenaar }
5994d1a0d267SMarcel Moolenaar
5995d1a0d267SMarcel Moolenaar /*
5996d1a0d267SMarcel Moolenaar * At this point, we need to consider if the fields have been reordered,
5997d1a0d267SMarcel Moolenaar * such as "The {:adjective} {:noun}" to "La {:noun} {:adjective}".
5998d1a0d267SMarcel Moolenaar *
5999d1a0d267SMarcel Moolenaar * We need to rewrite the new_fields using the old fields order,
6000d1a0d267SMarcel Moolenaar * so that we can render the message using the arguments as they
6001d1a0d267SMarcel Moolenaar * appear on the stack. It's a lot of work, but we don't really
6002d1a0d267SMarcel Moolenaar * want to (eventually) fall into the standard printf code which
6003d1a0d267SMarcel Moolenaar * means using the arguments straight (and in order) from the
6004d1a0d267SMarcel Moolenaar * varargs we were originally passed.
6005d1a0d267SMarcel Moolenaar */
6006d1a0d267SMarcel Moolenaar static void
xo_gettext_rewrite_fields(xo_handle_t * xop UNUSED,xo_field_info_t * fields,unsigned max_fields)6007d1a0d267SMarcel Moolenaar xo_gettext_rewrite_fields (xo_handle_t *xop UNUSED,
6008d1a0d267SMarcel Moolenaar xo_field_info_t *fields, unsigned max_fields)
6009d1a0d267SMarcel Moolenaar {
6010d1a0d267SMarcel Moolenaar xo_field_info_t tmp[max_fields];
6011d1a0d267SMarcel Moolenaar bzero(tmp, max_fields * sizeof(tmp[0]));
6012d1a0d267SMarcel Moolenaar
6013d1a0d267SMarcel Moolenaar unsigned fnum = 0;
6014d1a0d267SMarcel Moolenaar xo_field_info_t *newp, *outp, *zp;
6015d1a0d267SMarcel Moolenaar for (newp = fields, outp = tmp; newp->xfi_ftype; newp++, outp++) {
6016d1a0d267SMarcel Moolenaar switch (newp->xfi_ftype) {
6017d1a0d267SMarcel Moolenaar case XO_ROLE_NEWLINE: /* Don't get numbered */
6018d1a0d267SMarcel Moolenaar case XO_ROLE_TEXT:
6019d1a0d267SMarcel Moolenaar case XO_ROLE_EBRACE:
6020d1a0d267SMarcel Moolenaar case 'G':
6021d1a0d267SMarcel Moolenaar *outp = *newp;
6022d1a0d267SMarcel Moolenaar outp->xfi_renum = 0;
6023d1a0d267SMarcel Moolenaar continue;
6024d1a0d267SMarcel Moolenaar }
6025d1a0d267SMarcel Moolenaar
6026d1a0d267SMarcel Moolenaar zp = xo_gettext_find_field(fields, ++fnum);
6027d1a0d267SMarcel Moolenaar if (zp == NULL) { /* Should not occur */
6028d1a0d267SMarcel Moolenaar *outp = *newp;
6029d1a0d267SMarcel Moolenaar outp->xfi_renum = 0;
6030d1a0d267SMarcel Moolenaar continue;
6031d1a0d267SMarcel Moolenaar }
6032d1a0d267SMarcel Moolenaar
6033d1a0d267SMarcel Moolenaar *outp = *zp;
6034d1a0d267SMarcel Moolenaar outp->xfi_renum = newp->xfi_fnum;
6035d1a0d267SMarcel Moolenaar }
6036d1a0d267SMarcel Moolenaar
6037d1a0d267SMarcel Moolenaar memcpy(fields, tmp, max_fields * sizeof(tmp[0]));
6038d1a0d267SMarcel Moolenaar }
6039d1a0d267SMarcel Moolenaar
6040d1a0d267SMarcel Moolenaar /*
6041d1a0d267SMarcel Moolenaar * We've got two lists of fields, the old list from the original
6042d1a0d267SMarcel Moolenaar * format string and the new one from the parsed gettext reply. The
6043d1a0d267SMarcel Moolenaar * new list has the localized words, where the old list has the
6044d1a0d267SMarcel Moolenaar * formatting information. We need to combine them into a single list
6045d1a0d267SMarcel Moolenaar * (the new list).
6046d1a0d267SMarcel Moolenaar *
6047d1a0d267SMarcel Moolenaar * If the list needs to be reordered, then we've got more serious work
6048d1a0d267SMarcel Moolenaar * to do.
6049d1a0d267SMarcel Moolenaar */
6050d1a0d267SMarcel Moolenaar static int
xo_gettext_combine_formats(xo_handle_t * xop,const char * fmt UNUSED,const char * gtfmt,xo_field_info_t * old_fields,xo_field_info_t * new_fields,unsigned new_max_fields,int * reorderedp)6051d1a0d267SMarcel Moolenaar xo_gettext_combine_formats (xo_handle_t *xop, const char *fmt UNUSED,
6052d1a0d267SMarcel Moolenaar const char *gtfmt, xo_field_info_t *old_fields,
6053d1a0d267SMarcel Moolenaar xo_field_info_t *new_fields, unsigned new_max_fields,
6054d1a0d267SMarcel Moolenaar int *reorderedp)
6055d1a0d267SMarcel Moolenaar {
6056d1a0d267SMarcel Moolenaar int reordered = 0;
6057d1a0d267SMarcel Moolenaar xo_field_info_t *newp, *oldp, *startp = old_fields;
6058d1a0d267SMarcel Moolenaar
6059d1a0d267SMarcel Moolenaar xo_gettext_finish_numbering_fields(xop, fmt, old_fields);
6060d1a0d267SMarcel Moolenaar
6061d1a0d267SMarcel Moolenaar for (newp = new_fields; newp->xfi_ftype; newp++) {
6062d1a0d267SMarcel Moolenaar switch (newp->xfi_ftype) {
6063d1a0d267SMarcel Moolenaar case XO_ROLE_NEWLINE:
6064d1a0d267SMarcel Moolenaar case XO_ROLE_TEXT:
6065d1a0d267SMarcel Moolenaar case XO_ROLE_EBRACE:
6066d1a0d267SMarcel Moolenaar continue;
6067d1a0d267SMarcel Moolenaar
6068d1a0d267SMarcel Moolenaar case 'V':
6069d1a0d267SMarcel Moolenaar for (oldp = startp; oldp->xfi_ftype; oldp++) {
6070d1a0d267SMarcel Moolenaar if (oldp->xfi_ftype != 'V')
6071d1a0d267SMarcel Moolenaar continue;
6072d1a0d267SMarcel Moolenaar if (newp->xfi_clen != oldp->xfi_clen
6073d1a0d267SMarcel Moolenaar || strncmp(newp->xfi_content, oldp->xfi_content,
6074d1a0d267SMarcel Moolenaar oldp->xfi_clen) != 0) {
6075d1a0d267SMarcel Moolenaar reordered = 1;
6076d1a0d267SMarcel Moolenaar continue;
6077d1a0d267SMarcel Moolenaar }
6078d1a0d267SMarcel Moolenaar startp = oldp + 1;
6079d1a0d267SMarcel Moolenaar break;
6080d1a0d267SMarcel Moolenaar }
6081d1a0d267SMarcel Moolenaar
6082d1a0d267SMarcel Moolenaar /* Didn't find it on the first pass (starting from start) */
6083d1a0d267SMarcel Moolenaar if (oldp->xfi_ftype == 0) {
6084d1a0d267SMarcel Moolenaar for (oldp = old_fields; oldp < startp; oldp++) {
6085d1a0d267SMarcel Moolenaar if (oldp->xfi_ftype != 'V')
6086d1a0d267SMarcel Moolenaar continue;
6087d1a0d267SMarcel Moolenaar if (newp->xfi_clen != oldp->xfi_clen)
6088d1a0d267SMarcel Moolenaar continue;
6089d1a0d267SMarcel Moolenaar if (strncmp(newp->xfi_content, oldp->xfi_content,
6090d1a0d267SMarcel Moolenaar oldp->xfi_clen) != 0)
6091d1a0d267SMarcel Moolenaar continue;
6092d1a0d267SMarcel Moolenaar reordered = 1;
6093d1a0d267SMarcel Moolenaar break;
6094d1a0d267SMarcel Moolenaar }
6095d1a0d267SMarcel Moolenaar if (oldp == startp) {
6096d1a0d267SMarcel Moolenaar /* Field not found */
6097d1a0d267SMarcel Moolenaar xo_failure(xop, "post-gettext format can't find field "
6098d1a0d267SMarcel Moolenaar "'%.*s' in format '%s'",
6099d1a0d267SMarcel Moolenaar newp->xfi_clen, newp->xfi_content,
6100d1a0d267SMarcel Moolenaar xo_printable(gtfmt));
6101d1a0d267SMarcel Moolenaar return -1;
6102d1a0d267SMarcel Moolenaar }
6103d1a0d267SMarcel Moolenaar }
6104d1a0d267SMarcel Moolenaar break;
6105d1a0d267SMarcel Moolenaar
6106d1a0d267SMarcel Moolenaar default:
6107d1a0d267SMarcel Moolenaar /*
6108d1a0d267SMarcel Moolenaar * Other fields don't have names for us to use, so if
6109d1a0d267SMarcel Moolenaar * the types aren't the same, then we'll have to assume
6110d1a0d267SMarcel Moolenaar * the original field is a match.
6111d1a0d267SMarcel Moolenaar */
6112d1a0d267SMarcel Moolenaar for (oldp = startp; oldp->xfi_ftype; oldp++) {
6113d1a0d267SMarcel Moolenaar if (oldp->xfi_ftype == 'V') /* Can't go past these */
6114d1a0d267SMarcel Moolenaar break;
6115d1a0d267SMarcel Moolenaar if (oldp->xfi_ftype == newp->xfi_ftype)
6116d1a0d267SMarcel Moolenaar goto copy_it; /* Assumably we have a match */
6117d1a0d267SMarcel Moolenaar }
6118d1a0d267SMarcel Moolenaar continue;
6119d1a0d267SMarcel Moolenaar }
6120d1a0d267SMarcel Moolenaar
6121d1a0d267SMarcel Moolenaar /*
6122d1a0d267SMarcel Moolenaar * Found a match; copy over appropriate fields
6123d1a0d267SMarcel Moolenaar */
6124d1a0d267SMarcel Moolenaar copy_it:
6125d1a0d267SMarcel Moolenaar newp->xfi_flags = oldp->xfi_flags;
6126d1a0d267SMarcel Moolenaar newp->xfi_fnum = oldp->xfi_fnum;
6127d1a0d267SMarcel Moolenaar newp->xfi_format = oldp->xfi_format;
6128d1a0d267SMarcel Moolenaar newp->xfi_flen = oldp->xfi_flen;
6129d1a0d267SMarcel Moolenaar newp->xfi_encoding = oldp->xfi_encoding;
6130d1a0d267SMarcel Moolenaar newp->xfi_elen = oldp->xfi_elen;
6131d1a0d267SMarcel Moolenaar }
6132d1a0d267SMarcel Moolenaar
6133d1a0d267SMarcel Moolenaar *reorderedp = reordered;
6134d1a0d267SMarcel Moolenaar if (reordered) {
6135d1a0d267SMarcel Moolenaar xo_gettext_finish_numbering_fields(xop, fmt, new_fields);
6136d1a0d267SMarcel Moolenaar xo_gettext_rewrite_fields(xop, new_fields, new_max_fields);
6137d1a0d267SMarcel Moolenaar }
6138d1a0d267SMarcel Moolenaar
6139d1a0d267SMarcel Moolenaar return 0;
6140d1a0d267SMarcel Moolenaar }
6141d1a0d267SMarcel Moolenaar
6142d1a0d267SMarcel Moolenaar /*
6143d1a0d267SMarcel Moolenaar * We don't want to make gettext() calls here with a complete format
6144d1a0d267SMarcel Moolenaar * string, since that means changing a flag would mean a
6145d1a0d267SMarcel Moolenaar * labor-intensive re-translation expense. Instead we build a
6146d1a0d267SMarcel Moolenaar * simplified form with a reduced level of detail, perform a lookup on
6147d1a0d267SMarcel Moolenaar * that string and then re-insert the formating info.
6148d1a0d267SMarcel Moolenaar *
6149d1a0d267SMarcel Moolenaar * So something like:
6150d1a0d267SMarcel Moolenaar * xo_emit("{G:}close {:fd/%ld} returned {g:error/%m} {:test/%6.6s}\n", ...)
6151d1a0d267SMarcel Moolenaar * would have a lookup string of:
6152d1a0d267SMarcel Moolenaar * "close {:fd} returned {:error} {:test}\n"
6153d1a0d267SMarcel Moolenaar *
6154d1a0d267SMarcel Moolenaar * We also need to handling reordering of fields, where the gettext()
6155d1a0d267SMarcel Moolenaar * reply string uses fields in a different order than the original
6156d1a0d267SMarcel Moolenaar * format string:
6157d1a0d267SMarcel Moolenaar * "cluse-a {:fd} retoorned {:test}. Bork {:error} Bork. Bork.\n"
6158d1a0d267SMarcel Moolenaar * If we have to reorder fields within the message, then things get
6159d1a0d267SMarcel Moolenaar * complicated. See xo_gettext_rewrite_fields.
6160d1a0d267SMarcel Moolenaar *
6161d1a0d267SMarcel Moolenaar * Summary: i18n aighn't cheap.
6162d1a0d267SMarcel Moolenaar */
6163d1a0d267SMarcel Moolenaar static const char *
xo_gettext_build_format(xo_handle_t * xop,xo_field_info_t * fields,int this_field,const char * fmt,char ** new_fmtp)616442ff34c3SPhil Shafer xo_gettext_build_format (xo_handle_t *xop,
616542ff34c3SPhil Shafer xo_field_info_t *fields, int this_field,
6166d1a0d267SMarcel Moolenaar const char *fmt, char **new_fmtp)
6167d1a0d267SMarcel Moolenaar {
6168d1a0d267SMarcel Moolenaar if (xo_style_is_encoding(xop))
6169d1a0d267SMarcel Moolenaar goto bail;
6170d1a0d267SMarcel Moolenaar
6171d1a0d267SMarcel Moolenaar xo_buffer_t xb;
6172d1a0d267SMarcel Moolenaar xo_buf_init(&xb);
6173d1a0d267SMarcel Moolenaar
6174d1a0d267SMarcel Moolenaar if (xo_gettext_simplify_format(xop, &xb, fields,
6175d1a0d267SMarcel Moolenaar this_field, fmt, NULL))
6176d1a0d267SMarcel Moolenaar goto bail2;
6177d1a0d267SMarcel Moolenaar
6178d1a0d267SMarcel Moolenaar const char *gtfmt = xo_dgettext(xop, xb.xb_bufp);
617976afb20cSPhil Shafer if (gtfmt == NULL || gtfmt == fmt || xo_streq(gtfmt, fmt))
6180d1a0d267SMarcel Moolenaar goto bail2;
6181d1a0d267SMarcel Moolenaar
6182d1a0d267SMarcel Moolenaar char *new_fmt = xo_strndup(gtfmt, -1);
6183d1a0d267SMarcel Moolenaar if (new_fmt == NULL)
6184d1a0d267SMarcel Moolenaar goto bail2;
6185d1a0d267SMarcel Moolenaar
6186f2b7bf8aSPhil Shafer xo_buf_cleanup(&xb);
6187f2b7bf8aSPhil Shafer
6188d1a0d267SMarcel Moolenaar *new_fmtp = new_fmt;
6189d1a0d267SMarcel Moolenaar return new_fmt;
6190d1a0d267SMarcel Moolenaar
6191d1a0d267SMarcel Moolenaar bail2:
6192d1a0d267SMarcel Moolenaar xo_buf_cleanup(&xb);
6193d1a0d267SMarcel Moolenaar bail:
6194d1a0d267SMarcel Moolenaar *new_fmtp = NULL;
6195d1a0d267SMarcel Moolenaar return fmt;
6196d1a0d267SMarcel Moolenaar }
6197d1a0d267SMarcel Moolenaar
6198d1a0d267SMarcel Moolenaar static void
xo_gettext_rebuild_content(xo_handle_t * xop,xo_field_info_t * fields,ssize_t * fstart,unsigned min_fstart,ssize_t * fend,unsigned max_fend)6199d1a0d267SMarcel Moolenaar xo_gettext_rebuild_content (xo_handle_t *xop, xo_field_info_t *fields,
62008a6eceffSPhil Shafer ssize_t *fstart, unsigned min_fstart,
62018a6eceffSPhil Shafer ssize_t *fend, unsigned max_fend)
6202d1a0d267SMarcel Moolenaar {
6203d1a0d267SMarcel Moolenaar xo_field_info_t *xfip;
6204d1a0d267SMarcel Moolenaar char *buf;
62058a6eceffSPhil Shafer ssize_t base = fstart[min_fstart];
62068a6eceffSPhil Shafer ssize_t blen = fend[max_fend] - base;
6207d1a0d267SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_data;
6208d1a0d267SMarcel Moolenaar
6209d1a0d267SMarcel Moolenaar if (blen == 0)
6210d1a0d267SMarcel Moolenaar return;
6211d1a0d267SMarcel Moolenaar
6212d1a0d267SMarcel Moolenaar buf = xo_realloc(NULL, blen);
6213d1a0d267SMarcel Moolenaar if (buf == NULL)
6214d1a0d267SMarcel Moolenaar return;
6215d1a0d267SMarcel Moolenaar
6216d1a0d267SMarcel Moolenaar memcpy(buf, xbp->xb_bufp + fstart[min_fstart], blen); /* Copy our data */
6217d1a0d267SMarcel Moolenaar
62188a6eceffSPhil Shafer unsigned field = min_fstart, len, fnum;
62198a6eceffSPhil Shafer ssize_t soff, doff = base;
6220d1a0d267SMarcel Moolenaar xo_field_info_t *zp;
6221d1a0d267SMarcel Moolenaar
6222d1a0d267SMarcel Moolenaar /*
6223d1a0d267SMarcel Moolenaar * Be aware there are two competing views of "field number": we
6224d1a0d267SMarcel Moolenaar * want the user to thing in terms of "The {1:size}" where {G:},
6225d1a0d267SMarcel Moolenaar * newlines, escaped braces, and text don't have numbers. But is
6226d1a0d267SMarcel Moolenaar * also the internal view, where we have an array of
6227d1a0d267SMarcel Moolenaar * xo_field_info_t and every field have an index. fnum, fstart[]
6228d1a0d267SMarcel Moolenaar * and fend[] are the latter, but xfi_renum is the former.
6229d1a0d267SMarcel Moolenaar */
6230d1a0d267SMarcel Moolenaar for (xfip = fields + field; xfip->xfi_ftype; xfip++, field++) {
6231d1a0d267SMarcel Moolenaar fnum = field;
6232d1a0d267SMarcel Moolenaar if (xfip->xfi_renum) {
6233d1a0d267SMarcel Moolenaar zp = xo_gettext_find_field(fields, xfip->xfi_renum);
6234d1a0d267SMarcel Moolenaar fnum = zp ? zp - fields : field;
6235d1a0d267SMarcel Moolenaar }
6236d1a0d267SMarcel Moolenaar
6237d1a0d267SMarcel Moolenaar soff = fstart[fnum];
6238d1a0d267SMarcel Moolenaar len = fend[fnum] - soff;
6239d1a0d267SMarcel Moolenaar
6240d1a0d267SMarcel Moolenaar if (len > 0) {
6241d1a0d267SMarcel Moolenaar soff -= base;
6242d1a0d267SMarcel Moolenaar memcpy(xbp->xb_bufp + doff, buf + soff, len);
6243d1a0d267SMarcel Moolenaar doff += len;
6244d1a0d267SMarcel Moolenaar }
6245d1a0d267SMarcel Moolenaar }
6246d1a0d267SMarcel Moolenaar
6247d1a0d267SMarcel Moolenaar xo_free(buf);
6248d1a0d267SMarcel Moolenaar }
6249d1a0d267SMarcel Moolenaar #else /* HAVE_GETTEXT */
6250d1a0d267SMarcel Moolenaar static const char *
xo_gettext_build_format(xo_handle_t * xop UNUSED,xo_field_info_t * fields UNUSED,int this_field UNUSED,const char * fmt UNUSED,char ** new_fmtp)6251d1a0d267SMarcel Moolenaar xo_gettext_build_format (xo_handle_t *xop UNUSED,
6252d1a0d267SMarcel Moolenaar xo_field_info_t *fields UNUSED,
6253d1a0d267SMarcel Moolenaar int this_field UNUSED,
6254d1a0d267SMarcel Moolenaar const char *fmt UNUSED, char **new_fmtp)
6255d1a0d267SMarcel Moolenaar {
6256d1a0d267SMarcel Moolenaar *new_fmtp = NULL;
6257d1a0d267SMarcel Moolenaar return fmt;
6258d1a0d267SMarcel Moolenaar }
6259d1a0d267SMarcel Moolenaar
6260d1a0d267SMarcel Moolenaar static int
xo_gettext_combine_formats(xo_handle_t * xop UNUSED,const char * fmt UNUSED,const char * gtfmt UNUSED,xo_field_info_t * old_fields UNUSED,xo_field_info_t * new_fields UNUSED,unsigned new_max_fields UNUSED,int * reorderedp UNUSED)6261d1a0d267SMarcel Moolenaar xo_gettext_combine_formats (xo_handle_t *xop UNUSED, const char *fmt UNUSED,
6262d1a0d267SMarcel Moolenaar const char *gtfmt UNUSED,
6263d1a0d267SMarcel Moolenaar xo_field_info_t *old_fields UNUSED,
6264d1a0d267SMarcel Moolenaar xo_field_info_t *new_fields UNUSED,
6265d1a0d267SMarcel Moolenaar unsigned new_max_fields UNUSED,
6266d1a0d267SMarcel Moolenaar int *reorderedp UNUSED)
6267d1a0d267SMarcel Moolenaar {
6268d1a0d267SMarcel Moolenaar return -1;
6269d1a0d267SMarcel Moolenaar }
6270d1a0d267SMarcel Moolenaar
6271d1a0d267SMarcel Moolenaar static void
xo_gettext_rebuild_content(xo_handle_t * xop UNUSED,xo_field_info_t * fields UNUSED,ssize_t * fstart UNUSED,unsigned min_fstart UNUSED,ssize_t * fend UNUSED,unsigned max_fend UNUSED)6272d1a0d267SMarcel Moolenaar xo_gettext_rebuild_content (xo_handle_t *xop UNUSED,
6273d1a0d267SMarcel Moolenaar xo_field_info_t *fields UNUSED,
62748a6eceffSPhil Shafer ssize_t *fstart UNUSED, unsigned min_fstart UNUSED,
62758a6eceffSPhil Shafer ssize_t *fend UNUSED, unsigned max_fend UNUSED)
6276d1a0d267SMarcel Moolenaar {
6277d1a0d267SMarcel Moolenaar return;
6278d1a0d267SMarcel Moolenaar }
6279d1a0d267SMarcel Moolenaar #endif /* HAVE_GETTEXT */
6280d1a0d267SMarcel Moolenaar
6281d1a0d267SMarcel Moolenaar /*
628242ff34c3SPhil Shafer * Emit a set of fields. This is really the core of libxo.
6283d1a0d267SMarcel Moolenaar */
62848a6eceffSPhil Shafer static ssize_t
xo_do_emit_fields(xo_handle_t * xop,xo_field_info_t * fields,unsigned max_fields,const char * fmt)628542ff34c3SPhil Shafer xo_do_emit_fields (xo_handle_t *xop, xo_field_info_t *fields,
628642ff34c3SPhil Shafer unsigned max_fields, const char *fmt)
6287d1a0d267SMarcel Moolenaar {
6288d1a0d267SMarcel Moolenaar int gettext_inuse = 0;
6289d1a0d267SMarcel Moolenaar int gettext_changed = 0;
6290d1a0d267SMarcel Moolenaar int gettext_reordered = 0;
629142ff34c3SPhil Shafer unsigned ftype;
629242ff34c3SPhil Shafer xo_xff_flags_t flags;
6293d1a0d267SMarcel Moolenaar xo_field_info_t *new_fields = NULL;
629442ff34c3SPhil Shafer xo_field_info_t *xfip;
629542ff34c3SPhil Shafer unsigned field;
62968a6eceffSPhil Shafer ssize_t rc = 0;
629742ff34c3SPhil Shafer
6298d1a0d267SMarcel Moolenaar int flush = XOF_ISSET(xop, XOF_FLUSH);
6299d1a0d267SMarcel Moolenaar int flush_line = XOF_ISSET(xop, XOF_FLUSH_LINE);
6300d1a0d267SMarcel Moolenaar char *new_fmt = NULL;
6301d1a0d267SMarcel Moolenaar
6302d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_REORDER) || xo_style(xop) == XO_STYLE_ENCODER)
6303d1a0d267SMarcel Moolenaar flush_line = 0;
6304d1a0d267SMarcel Moolenaar
6305d1a0d267SMarcel Moolenaar /*
6306d1a0d267SMarcel Moolenaar * Some overhead for gettext; if the fields in the msgstr returned
6307d1a0d267SMarcel Moolenaar * by gettext are reordered, then we need to record start and end
6308d1a0d267SMarcel Moolenaar * for each field. We'll go ahead and render the fields in the
6309d1a0d267SMarcel Moolenaar * normal order, but later we can then reconstruct the reordered
6310d1a0d267SMarcel Moolenaar * fields using these fstart/fend values.
6311d1a0d267SMarcel Moolenaar */
6312d1a0d267SMarcel Moolenaar unsigned flimit = max_fields * 2; /* Pessimistic limit */
6313d1a0d267SMarcel Moolenaar unsigned min_fstart = flimit - 1;
6314d1a0d267SMarcel Moolenaar unsigned max_fend = 0; /* Highest recorded fend[] entry */
63158a6eceffSPhil Shafer ssize_t fstart[flimit];
6316d1a0d267SMarcel Moolenaar bzero(fstart, flimit * sizeof(fstart[0]));
63178a6eceffSPhil Shafer ssize_t fend[flimit];
6318d1a0d267SMarcel Moolenaar bzero(fend, flimit * sizeof(fend[0]));
6319d1a0d267SMarcel Moolenaar
6320f2b7bf8aSPhil Shafer for (xfip = fields, field = 0; field < max_fields && xfip->xfi_ftype;
6321d1a0d267SMarcel Moolenaar xfip++, field++) {
6322d1a0d267SMarcel Moolenaar ftype = xfip->xfi_ftype;
6323d1a0d267SMarcel Moolenaar flags = xfip->xfi_flags;
6324d1a0d267SMarcel Moolenaar
6325d1a0d267SMarcel Moolenaar /* Record field start offset */
6326d1a0d267SMarcel Moolenaar if (gettext_reordered) {
6327d1a0d267SMarcel Moolenaar fstart[field] = xo_buf_offset(&xop->xo_data);
6328d1a0d267SMarcel Moolenaar if (min_fstart > field)
6329d1a0d267SMarcel Moolenaar min_fstart = field;
6330d1a0d267SMarcel Moolenaar }
6331d1a0d267SMarcel Moolenaar
633242ff34c3SPhil Shafer const char *content = xfip->xfi_content;
63338a6eceffSPhil Shafer ssize_t clen = xfip->xfi_clen;
633442ff34c3SPhil Shafer
633542ff34c3SPhil Shafer if (flags & XFF_ARGUMENT) {
633642ff34c3SPhil Shafer /*
633742ff34c3SPhil Shafer * Argument flag means the content isn't given in the descriptor,
633842ff34c3SPhil Shafer * but as a UTF-8 string ('const char *') argument in xo_vap.
633942ff34c3SPhil Shafer */
634042ff34c3SPhil Shafer content = va_arg(xop->xo_vap, char *);
634142ff34c3SPhil Shafer clen = content ? strlen(content) : 0;
634242ff34c3SPhil Shafer }
634342ff34c3SPhil Shafer
6344d1a0d267SMarcel Moolenaar if (ftype == XO_ROLE_NEWLINE) {
6345d1a0d267SMarcel Moolenaar xo_line_close(xop);
6346d1a0d267SMarcel Moolenaar if (flush_line && xo_flush_h(xop) < 0)
6347d1a0d267SMarcel Moolenaar return -1;
6348d1a0d267SMarcel Moolenaar goto bottom;
6349d1a0d267SMarcel Moolenaar
6350d1a0d267SMarcel Moolenaar } else if (ftype == XO_ROLE_EBRACE) {
6351d1a0d267SMarcel Moolenaar xo_format_text(xop, xfip->xfi_start, xfip->xfi_len);
6352d1a0d267SMarcel Moolenaar goto bottom;
6353d1a0d267SMarcel Moolenaar
6354d1a0d267SMarcel Moolenaar } else if (ftype == XO_ROLE_TEXT) {
6355d1a0d267SMarcel Moolenaar /* Normal text */
6356d1a0d267SMarcel Moolenaar xo_format_text(xop, xfip->xfi_content, xfip->xfi_clen);
6357d1a0d267SMarcel Moolenaar goto bottom;
6358d1a0d267SMarcel Moolenaar }
6359d1a0d267SMarcel Moolenaar
6360d1a0d267SMarcel Moolenaar /*
6361d1a0d267SMarcel Moolenaar * Notes and units need the 'w' flag handled before the content.
6362d1a0d267SMarcel Moolenaar */
6363d1a0d267SMarcel Moolenaar if (ftype == 'N' || ftype == 'U') {
6364d1a0d267SMarcel Moolenaar if (flags & XFF_WS) {
6365d1a0d267SMarcel Moolenaar xo_format_content(xop, "padding", NULL, " ", 1,
6366d1a0d267SMarcel Moolenaar NULL, 0, flags);
6367264104f2SPhil Shafer flags &= ~XFF_WS; /* Prevent later handling of this flag */
6368d1a0d267SMarcel Moolenaar }
6369d1a0d267SMarcel Moolenaar }
6370d1a0d267SMarcel Moolenaar
6371d1a0d267SMarcel Moolenaar if (ftype == 'V')
6372264104f2SPhil Shafer xo_format_value(xop, content, clen, NULL, 0,
6373d1a0d267SMarcel Moolenaar xfip->xfi_format, xfip->xfi_flen,
6374d1a0d267SMarcel Moolenaar xfip->xfi_encoding, xfip->xfi_elen, flags);
6375d1a0d267SMarcel Moolenaar else if (ftype == '[')
637642ff34c3SPhil Shafer xo_anchor_start(xop, xfip, content, clen);
6377545ddfbeSMarcel Moolenaar else if (ftype == ']')
637842ff34c3SPhil Shafer xo_anchor_stop(xop, xfip, content, clen);
6379788ca347SMarcel Moolenaar else if (ftype == 'C')
638042ff34c3SPhil Shafer xo_format_colors(xop, xfip, content, clen);
6381545ddfbeSMarcel Moolenaar
6382d1a0d267SMarcel Moolenaar else if (ftype == 'G') {
6383d1a0d267SMarcel Moolenaar /*
6384d1a0d267SMarcel Moolenaar * A {G:domain} field; disect the domain name and translate
6385d1a0d267SMarcel Moolenaar * the remaining portion of the input string. If the user
6386d1a0d267SMarcel Moolenaar * didn't put the {G:} at the start of the format string, then
6387d1a0d267SMarcel Moolenaar * assumably they just want us to translate the rest of it.
6388d1a0d267SMarcel Moolenaar * Since gettext returns strings in a static buffer, we make
6389d1a0d267SMarcel Moolenaar * a copy in new_fmt.
6390d1a0d267SMarcel Moolenaar */
639142ff34c3SPhil Shafer xo_set_gettext_domain(xop, xfip, content, clen);
6392d1a0d267SMarcel Moolenaar
6393d1a0d267SMarcel Moolenaar if (!gettext_inuse) { /* Only translate once */
6394d1a0d267SMarcel Moolenaar gettext_inuse = 1;
6395d1a0d267SMarcel Moolenaar if (new_fmt) {
6396d1a0d267SMarcel Moolenaar xo_free(new_fmt);
6397d1a0d267SMarcel Moolenaar new_fmt = NULL;
6398545ddfbeSMarcel Moolenaar }
6399545ddfbeSMarcel Moolenaar
6400d1a0d267SMarcel Moolenaar xo_gettext_build_format(xop, fields, field,
6401d1a0d267SMarcel Moolenaar xfip->xfi_next, &new_fmt);
6402d1a0d267SMarcel Moolenaar if (new_fmt) {
6403d1a0d267SMarcel Moolenaar gettext_changed = 1;
6404d1a0d267SMarcel Moolenaar
6405d1a0d267SMarcel Moolenaar unsigned new_max_fields = xo_count_fields(xop, new_fmt);
6406d1a0d267SMarcel Moolenaar
6407d1a0d267SMarcel Moolenaar if (++new_max_fields < max_fields)
6408d1a0d267SMarcel Moolenaar new_max_fields = max_fields;
6409d1a0d267SMarcel Moolenaar
6410d1a0d267SMarcel Moolenaar /* Leave a blank slot at the beginning */
64118a6eceffSPhil Shafer ssize_t sz = (new_max_fields + 1) * sizeof(xo_field_info_t);
6412d1a0d267SMarcel Moolenaar new_fields = alloca(sz);
6413d1a0d267SMarcel Moolenaar bzero(new_fields, sz);
6414d1a0d267SMarcel Moolenaar
6415d1a0d267SMarcel Moolenaar if (!xo_parse_fields(xop, new_fields + 1,
6416d1a0d267SMarcel Moolenaar new_max_fields, new_fmt)) {
6417d1a0d267SMarcel Moolenaar gettext_reordered = 0;
6418d1a0d267SMarcel Moolenaar
6419d1a0d267SMarcel Moolenaar if (!xo_gettext_combine_formats(xop, fmt, new_fmt,
6420d1a0d267SMarcel Moolenaar fields, new_fields + 1,
6421d1a0d267SMarcel Moolenaar new_max_fields, &gettext_reordered)) {
6422d1a0d267SMarcel Moolenaar
6423d1a0d267SMarcel Moolenaar if (gettext_reordered) {
6424d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_LOG_GETTEXT))
6425d1a0d267SMarcel Moolenaar xo_failure(xop, "gettext finds reordered "
6426d1a0d267SMarcel Moolenaar "fields in '%s' and '%s'",
6427d1a0d267SMarcel Moolenaar xo_printable(fmt),
6428d1a0d267SMarcel Moolenaar xo_printable(new_fmt));
6429d1a0d267SMarcel Moolenaar flush_line = 0; /* Must keep at content */
6430d1a0d267SMarcel Moolenaar XOIF_SET(xop, XOIF_REORDER);
6431d1a0d267SMarcel Moolenaar }
6432d1a0d267SMarcel Moolenaar
6433d1a0d267SMarcel Moolenaar field = -1; /* Will be incremented at top of loop */
6434d1a0d267SMarcel Moolenaar xfip = new_fields;
6435d1a0d267SMarcel Moolenaar max_fields = new_max_fields;
6436d1a0d267SMarcel Moolenaar }
6437d1a0d267SMarcel Moolenaar }
6438d1a0d267SMarcel Moolenaar }
6439d1a0d267SMarcel Moolenaar }
6440d1a0d267SMarcel Moolenaar continue;
6441d1a0d267SMarcel Moolenaar
644242ff34c3SPhil Shafer } else if (clen || xfip->xfi_format) {
6443d1a0d267SMarcel Moolenaar
6444d1a0d267SMarcel Moolenaar const char *class_name = xo_class_name(ftype);
6445d1a0d267SMarcel Moolenaar if (class_name)
6446d1a0d267SMarcel Moolenaar xo_format_content(xop, class_name, xo_tag_name(ftype),
644742ff34c3SPhil Shafer content, clen,
6448d1a0d267SMarcel Moolenaar xfip->xfi_format, xfip->xfi_flen, flags);
644931337658SMarcel Moolenaar else if (ftype == 'T')
645042ff34c3SPhil Shafer xo_format_title(xop, xfip, content, clen);
6451d1a0d267SMarcel Moolenaar else if (ftype == 'U')
645242ff34c3SPhil Shafer xo_format_units(xop, xfip, content, clen);
6453d1a0d267SMarcel Moolenaar else
6454d1a0d267SMarcel Moolenaar xo_failure(xop, "unknown field type: '%c'", ftype);
6455545ddfbeSMarcel Moolenaar }
645631337658SMarcel Moolenaar
645731337658SMarcel Moolenaar if (flags & XFF_COLON)
6458d1a0d267SMarcel Moolenaar xo_format_content(xop, "decoration", NULL, ":", 1, NULL, 0, 0);
645931337658SMarcel Moolenaar
6460d1a0d267SMarcel Moolenaar if (flags & XFF_WS)
6461d1a0d267SMarcel Moolenaar xo_format_content(xop, "padding", NULL, " ", 1, NULL, 0, 0);
6462d1a0d267SMarcel Moolenaar
6463d1a0d267SMarcel Moolenaar bottom:
6464d1a0d267SMarcel Moolenaar /* Record the end-of-field offset */
6465d1a0d267SMarcel Moolenaar if (gettext_reordered) {
6466d1a0d267SMarcel Moolenaar fend[field] = xo_buf_offset(&xop->xo_data);
6467d1a0d267SMarcel Moolenaar max_fend = field;
646831337658SMarcel Moolenaar }
646931337658SMarcel Moolenaar }
647031337658SMarcel Moolenaar
6471d1a0d267SMarcel Moolenaar if (gettext_changed && gettext_reordered) {
6472d1a0d267SMarcel Moolenaar /* Final step: rebuild the content using the rendered fields */
6473d1a0d267SMarcel Moolenaar xo_gettext_rebuild_content(xop, new_fields + 1, fstart, min_fstart,
6474d1a0d267SMarcel Moolenaar fend, max_fend);
6475d1a0d267SMarcel Moolenaar }
6476d1a0d267SMarcel Moolenaar
6477d1a0d267SMarcel Moolenaar XOIF_CLEAR(xop, XOIF_REORDER);
6478d1a0d267SMarcel Moolenaar
6479ee5cf116SPhil Shafer /*
6480ee5cf116SPhil Shafer * If we've got enough data, flush it.
6481ee5cf116SPhil Shafer */
6482ee5cf116SPhil Shafer if (xo_buf_offset(&xop->xo_data) > XO_BUF_HIGH_WATER)
6483ee5cf116SPhil Shafer flush = 1;
6484ee5cf116SPhil Shafer
648531337658SMarcel Moolenaar /* If we don't have an anchor, write the text out */
6486d1a0d267SMarcel Moolenaar if (flush && !XOIF_ISSET(xop, XOIF_ANCHOR)) {
6487406a584dSPhil Shafer if (xo_flush_h(xop) < 0)
6488545ddfbeSMarcel Moolenaar rc = -1;
6489545ddfbeSMarcel Moolenaar }
649031337658SMarcel Moolenaar
6491d1a0d267SMarcel Moolenaar if (new_fmt)
6492d1a0d267SMarcel Moolenaar xo_free(new_fmt);
6493d1a0d267SMarcel Moolenaar
6494d1a0d267SMarcel Moolenaar /*
6495d1a0d267SMarcel Moolenaar * We've carried the gettext domainname inside our handle just for
6496d1a0d267SMarcel Moolenaar * convenience, but we need to ensure it doesn't survive across
6497d1a0d267SMarcel Moolenaar * xo_emit calls.
6498d1a0d267SMarcel Moolenaar */
6499d1a0d267SMarcel Moolenaar if (xop->xo_gt_domain) {
6500d1a0d267SMarcel Moolenaar xo_free(xop->xo_gt_domain);
6501d1a0d267SMarcel Moolenaar xop->xo_gt_domain = NULL;
6502d1a0d267SMarcel Moolenaar }
6503d1a0d267SMarcel Moolenaar
65048a6eceffSPhil Shafer return (rc < 0) ? rc : xop->xo_columns;
650531337658SMarcel Moolenaar }
650631337658SMarcel Moolenaar
6507d1a0d267SMarcel Moolenaar /*
650842ff34c3SPhil Shafer * Parse and emit a set of fields
650942ff34c3SPhil Shafer */
651042ff34c3SPhil Shafer static int
xo_do_emit(xo_handle_t * xop,xo_emit_flags_t flags,const char * fmt)651142ff34c3SPhil Shafer xo_do_emit (xo_handle_t *xop, xo_emit_flags_t flags, const char *fmt)
651242ff34c3SPhil Shafer {
651342ff34c3SPhil Shafer xop->xo_columns = 0; /* Always reset it */
651442ff34c3SPhil Shafer xop->xo_errno = errno; /* Save for "%m" */
651542ff34c3SPhil Shafer
651642ff34c3SPhil Shafer if (fmt == NULL)
651742ff34c3SPhil Shafer return 0;
651842ff34c3SPhil Shafer
651942ff34c3SPhil Shafer unsigned max_fields;
652042ff34c3SPhil Shafer xo_field_info_t *fields = NULL;
652142ff34c3SPhil Shafer
652242ff34c3SPhil Shafer /* Adjust XOEF_RETAIN based on global flags */
652342ff34c3SPhil Shafer if (XOF_ISSET(xop, XOF_RETAIN_ALL))
652442ff34c3SPhil Shafer flags |= XOEF_RETAIN;
652542ff34c3SPhil Shafer if (XOF_ISSET(xop, XOF_RETAIN_NONE))
652642ff34c3SPhil Shafer flags &= ~XOEF_RETAIN;
652742ff34c3SPhil Shafer
652842ff34c3SPhil Shafer /*
652942ff34c3SPhil Shafer * Check for 'retain' flag, telling us to retain the field
653042ff34c3SPhil Shafer * information. If we've already saved it, then we can avoid
653142ff34c3SPhil Shafer * re-parsing the format string.
653242ff34c3SPhil Shafer */
653342ff34c3SPhil Shafer if (!(flags & XOEF_RETAIN)
653442ff34c3SPhil Shafer || xo_retain_find(fmt, &fields, &max_fields) != 0
653542ff34c3SPhil Shafer || fields == NULL) {
653642ff34c3SPhil Shafer
653742ff34c3SPhil Shafer /* Nothing retained; parse the format string */
653842ff34c3SPhil Shafer max_fields = xo_count_fields(xop, fmt);
653942ff34c3SPhil Shafer fields = alloca(max_fields * sizeof(fields[0]));
654042ff34c3SPhil Shafer bzero(fields, max_fields * sizeof(fields[0]));
654142ff34c3SPhil Shafer
654242ff34c3SPhil Shafer if (xo_parse_fields(xop, fields, max_fields, fmt))
654342ff34c3SPhil Shafer return -1; /* Warning already displayed */
654442ff34c3SPhil Shafer
654542ff34c3SPhil Shafer if (flags & XOEF_RETAIN) {
654642ff34c3SPhil Shafer /* Retain the info */
654742ff34c3SPhil Shafer xo_retain_add(fmt, fields, max_fields);
654842ff34c3SPhil Shafer }
654942ff34c3SPhil Shafer }
655042ff34c3SPhil Shafer
655142ff34c3SPhil Shafer return xo_do_emit_fields(xop, fields, max_fields, fmt);
655242ff34c3SPhil Shafer }
655342ff34c3SPhil Shafer
655442ff34c3SPhil Shafer /*
6555d1a0d267SMarcel Moolenaar * Rebuild a format string in a gettext-friendly format. This function
6556d1a0d267SMarcel Moolenaar * is exposed to tools can perform this function. See xo(1).
6557d1a0d267SMarcel Moolenaar */
6558d1a0d267SMarcel Moolenaar char *
xo_simplify_format(xo_handle_t * xop,const char * fmt,int with_numbers,xo_simplify_field_func_t field_cb)6559d1a0d267SMarcel Moolenaar xo_simplify_format (xo_handle_t *xop, const char *fmt, int with_numbers,
6560d1a0d267SMarcel Moolenaar xo_simplify_field_func_t field_cb)
6561d1a0d267SMarcel Moolenaar {
6562d1a0d267SMarcel Moolenaar xop = xo_default(xop);
6563d1a0d267SMarcel Moolenaar
6564d1a0d267SMarcel Moolenaar xop->xo_columns = 0; /* Always reset it */
6565d1a0d267SMarcel Moolenaar xop->xo_errno = errno; /* Save for "%m" */
6566d1a0d267SMarcel Moolenaar
6567d1a0d267SMarcel Moolenaar unsigned max_fields = xo_count_fields(xop, fmt);
6568d1a0d267SMarcel Moolenaar xo_field_info_t fields[max_fields];
6569d1a0d267SMarcel Moolenaar
6570d1a0d267SMarcel Moolenaar bzero(fields, max_fields * sizeof(fields[0]));
6571d1a0d267SMarcel Moolenaar
6572d1a0d267SMarcel Moolenaar if (xo_parse_fields(xop, fields, max_fields, fmt))
6573d1a0d267SMarcel Moolenaar return NULL; /* Warning already displayed */
6574d1a0d267SMarcel Moolenaar
6575d1a0d267SMarcel Moolenaar xo_buffer_t xb;
6576d1a0d267SMarcel Moolenaar xo_buf_init(&xb);
6577d1a0d267SMarcel Moolenaar
6578d1a0d267SMarcel Moolenaar if (with_numbers)
6579d1a0d267SMarcel Moolenaar xo_gettext_finish_numbering_fields(xop, fmt, fields);
6580d1a0d267SMarcel Moolenaar
6581d1a0d267SMarcel Moolenaar if (xo_gettext_simplify_format(xop, &xb, fields, -1, fmt, field_cb))
6582d1a0d267SMarcel Moolenaar return NULL;
6583d1a0d267SMarcel Moolenaar
6584d1a0d267SMarcel Moolenaar return xb.xb_bufp;
6585d1a0d267SMarcel Moolenaar }
6586d1a0d267SMarcel Moolenaar
65878a6eceffSPhil Shafer xo_ssize_t
xo_emit_hv(xo_handle_t * xop,const char * fmt,va_list vap)658831337658SMarcel Moolenaar xo_emit_hv (xo_handle_t *xop, const char *fmt, va_list vap)
658931337658SMarcel Moolenaar {
65908a6eceffSPhil Shafer ssize_t rc;
659131337658SMarcel Moolenaar
659231337658SMarcel Moolenaar xop = xo_default(xop);
659331337658SMarcel Moolenaar va_copy(xop->xo_vap, vap);
659442ff34c3SPhil Shafer rc = xo_do_emit(xop, 0, fmt);
659531337658SMarcel Moolenaar va_end(xop->xo_vap);
659631337658SMarcel Moolenaar bzero(&xop->xo_vap, sizeof(xop->xo_vap));
659731337658SMarcel Moolenaar
659831337658SMarcel Moolenaar return rc;
659931337658SMarcel Moolenaar }
660031337658SMarcel Moolenaar
66018a6eceffSPhil Shafer xo_ssize_t
xo_emit_h(xo_handle_t * xop,const char * fmt,...)660231337658SMarcel Moolenaar xo_emit_h (xo_handle_t *xop, const char *fmt, ...)
660331337658SMarcel Moolenaar {
66048a6eceffSPhil Shafer ssize_t rc;
660531337658SMarcel Moolenaar
660631337658SMarcel Moolenaar xop = xo_default(xop);
660731337658SMarcel Moolenaar va_start(xop->xo_vap, fmt);
660842ff34c3SPhil Shafer rc = xo_do_emit(xop, 0, fmt);
660931337658SMarcel Moolenaar va_end(xop->xo_vap);
661031337658SMarcel Moolenaar bzero(&xop->xo_vap, sizeof(xop->xo_vap));
661131337658SMarcel Moolenaar
661231337658SMarcel Moolenaar return rc;
661331337658SMarcel Moolenaar }
661431337658SMarcel Moolenaar
66158a6eceffSPhil Shafer xo_ssize_t
xo_emit(const char * fmt,...)661631337658SMarcel Moolenaar xo_emit (const char *fmt, ...)
661731337658SMarcel Moolenaar {
661831337658SMarcel Moolenaar xo_handle_t *xop = xo_default(NULL);
66198a6eceffSPhil Shafer ssize_t rc;
662031337658SMarcel Moolenaar
662131337658SMarcel Moolenaar va_start(xop->xo_vap, fmt);
662242ff34c3SPhil Shafer rc = xo_do_emit(xop, 0, fmt);
662331337658SMarcel Moolenaar va_end(xop->xo_vap);
662431337658SMarcel Moolenaar bzero(&xop->xo_vap, sizeof(xop->xo_vap));
662531337658SMarcel Moolenaar
662631337658SMarcel Moolenaar return rc;
662731337658SMarcel Moolenaar }
662831337658SMarcel Moolenaar
66298a6eceffSPhil Shafer xo_ssize_t
xo_emit_hvf(xo_handle_t * xop,xo_emit_flags_t flags,const char * fmt,va_list vap)663042ff34c3SPhil Shafer xo_emit_hvf (xo_handle_t *xop, xo_emit_flags_t flags,
663142ff34c3SPhil Shafer const char *fmt, va_list vap)
663242ff34c3SPhil Shafer {
66338a6eceffSPhil Shafer ssize_t rc;
663442ff34c3SPhil Shafer
663542ff34c3SPhil Shafer xop = xo_default(xop);
663642ff34c3SPhil Shafer va_copy(xop->xo_vap, vap);
663742ff34c3SPhil Shafer rc = xo_do_emit(xop, flags, fmt);
663842ff34c3SPhil Shafer va_end(xop->xo_vap);
663942ff34c3SPhil Shafer bzero(&xop->xo_vap, sizeof(xop->xo_vap));
664042ff34c3SPhil Shafer
664142ff34c3SPhil Shafer return rc;
664242ff34c3SPhil Shafer }
664342ff34c3SPhil Shafer
66448a6eceffSPhil Shafer xo_ssize_t
xo_emit_hf(xo_handle_t * xop,xo_emit_flags_t flags,const char * fmt,...)664542ff34c3SPhil Shafer xo_emit_hf (xo_handle_t *xop, xo_emit_flags_t flags, const char *fmt, ...)
664642ff34c3SPhil Shafer {
66478a6eceffSPhil Shafer ssize_t rc;
664842ff34c3SPhil Shafer
664942ff34c3SPhil Shafer xop = xo_default(xop);
665042ff34c3SPhil Shafer va_start(xop->xo_vap, fmt);
665142ff34c3SPhil Shafer rc = xo_do_emit(xop, flags, fmt);
665242ff34c3SPhil Shafer va_end(xop->xo_vap);
665342ff34c3SPhil Shafer bzero(&xop->xo_vap, sizeof(xop->xo_vap));
665442ff34c3SPhil Shafer
665542ff34c3SPhil Shafer return rc;
665642ff34c3SPhil Shafer }
665742ff34c3SPhil Shafer
66588a6eceffSPhil Shafer xo_ssize_t
xo_emit_f(xo_emit_flags_t flags,const char * fmt,...)665942ff34c3SPhil Shafer xo_emit_f (xo_emit_flags_t flags, const char *fmt, ...)
666042ff34c3SPhil Shafer {
666142ff34c3SPhil Shafer xo_handle_t *xop = xo_default(NULL);
66628a6eceffSPhil Shafer ssize_t rc;
666342ff34c3SPhil Shafer
666442ff34c3SPhil Shafer va_start(xop->xo_vap, fmt);
666542ff34c3SPhil Shafer rc = xo_do_emit(xop, flags, fmt);
666642ff34c3SPhil Shafer va_end(xop->xo_vap);
666742ff34c3SPhil Shafer bzero(&xop->xo_vap, sizeof(xop->xo_vap));
666842ff34c3SPhil Shafer
666942ff34c3SPhil Shafer return rc;
667042ff34c3SPhil Shafer }
667142ff34c3SPhil Shafer
667242ff34c3SPhil Shafer /*
667342ff34c3SPhil Shafer * Emit a single field by providing the info information typically provided
667442ff34c3SPhil Shafer * inside the field description (role, modifiers, and formats). This is
667542ff34c3SPhil Shafer * a convenience function to avoid callers using snprintf to build field
667642ff34c3SPhil Shafer * descriptions.
667742ff34c3SPhil Shafer */
66788a6eceffSPhil Shafer xo_ssize_t
xo_emit_field_hv(xo_handle_t * xop,const char * rolmod,const char * contents,const char * fmt,const char * efmt,va_list vap)667942ff34c3SPhil Shafer xo_emit_field_hv (xo_handle_t *xop, const char *rolmod, const char *contents,
668042ff34c3SPhil Shafer const char *fmt, const char *efmt,
668142ff34c3SPhil Shafer va_list vap)
668242ff34c3SPhil Shafer {
66838a6eceffSPhil Shafer ssize_t rc;
668442ff34c3SPhil Shafer
668542ff34c3SPhil Shafer xop = xo_default(xop);
668642ff34c3SPhil Shafer
668742ff34c3SPhil Shafer if (rolmod == NULL)
668842ff34c3SPhil Shafer rolmod = "V";
668942ff34c3SPhil Shafer
669042ff34c3SPhil Shafer xo_field_info_t xfi;
669142ff34c3SPhil Shafer
669242ff34c3SPhil Shafer bzero(&xfi, sizeof(xfi));
669342ff34c3SPhil Shafer
669442ff34c3SPhil Shafer const char *cp;
669542ff34c3SPhil Shafer cp = xo_parse_roles(xop, rolmod, rolmod, &xfi);
669642ff34c3SPhil Shafer if (cp == NULL)
669742ff34c3SPhil Shafer return -1;
669842ff34c3SPhil Shafer
669942ff34c3SPhil Shafer xfi.xfi_start = fmt;
670042ff34c3SPhil Shafer xfi.xfi_content = contents;
670142ff34c3SPhil Shafer xfi.xfi_format = fmt;
670242ff34c3SPhil Shafer xfi.xfi_encoding = efmt;
670342ff34c3SPhil Shafer xfi.xfi_clen = contents ? strlen(contents) : 0;
670442ff34c3SPhil Shafer xfi.xfi_flen = fmt ? strlen(fmt) : 0;
670542ff34c3SPhil Shafer xfi.xfi_elen = efmt ? strlen(efmt) : 0;
670642ff34c3SPhil Shafer
670742ff34c3SPhil Shafer /* If we have content, then we have a default format */
670842ff34c3SPhil Shafer if (contents && fmt == NULL
670942ff34c3SPhil Shafer && xo_role_wants_default_format(xfi.xfi_ftype)) {
671042ff34c3SPhil Shafer xfi.xfi_format = xo_default_format;
671142ff34c3SPhil Shafer xfi.xfi_flen = 2;
671242ff34c3SPhil Shafer }
671342ff34c3SPhil Shafer
671442ff34c3SPhil Shafer va_copy(xop->xo_vap, vap);
671542ff34c3SPhil Shafer
671642ff34c3SPhil Shafer rc = xo_do_emit_fields(xop, &xfi, 1, fmt ?: contents ?: "field");
671742ff34c3SPhil Shafer
671842ff34c3SPhil Shafer va_end(xop->xo_vap);
671942ff34c3SPhil Shafer
672042ff34c3SPhil Shafer return rc;
672142ff34c3SPhil Shafer }
672242ff34c3SPhil Shafer
67238a6eceffSPhil Shafer xo_ssize_t
xo_emit_field_h(xo_handle_t * xop,const char * rolmod,const char * contents,const char * fmt,const char * efmt,...)672442ff34c3SPhil Shafer xo_emit_field_h (xo_handle_t *xop, const char *rolmod, const char *contents,
672542ff34c3SPhil Shafer const char *fmt, const char *efmt, ...)
672642ff34c3SPhil Shafer {
67278a6eceffSPhil Shafer ssize_t rc;
672842ff34c3SPhil Shafer va_list vap;
672942ff34c3SPhil Shafer
673042ff34c3SPhil Shafer va_start(vap, efmt);
673142ff34c3SPhil Shafer rc = xo_emit_field_hv(xop, rolmod, contents, fmt, efmt, vap);
673242ff34c3SPhil Shafer va_end(vap);
673342ff34c3SPhil Shafer
673442ff34c3SPhil Shafer return rc;
673542ff34c3SPhil Shafer }
673642ff34c3SPhil Shafer
67378a6eceffSPhil Shafer xo_ssize_t
xo_emit_field(const char * rolmod,const char * contents,const char * fmt,const char * efmt,...)673842ff34c3SPhil Shafer xo_emit_field (const char *rolmod, const char *contents,
673942ff34c3SPhil Shafer const char *fmt, const char *efmt, ...)
674042ff34c3SPhil Shafer {
67418a6eceffSPhil Shafer ssize_t rc;
674242ff34c3SPhil Shafer va_list vap;
674342ff34c3SPhil Shafer
674442ff34c3SPhil Shafer va_start(vap, efmt);
674542ff34c3SPhil Shafer rc = xo_emit_field_hv(NULL, rolmod, contents, fmt, efmt, vap);
674642ff34c3SPhil Shafer va_end(vap);
674742ff34c3SPhil Shafer
674842ff34c3SPhil Shafer return rc;
674942ff34c3SPhil Shafer }
675042ff34c3SPhil Shafer
67518a6eceffSPhil Shafer xo_ssize_t
xo_attr_hv(xo_handle_t * xop,const char * name,const char * fmt,va_list vap)675231337658SMarcel Moolenaar xo_attr_hv (xo_handle_t *xop, const char *name, const char *fmt, va_list vap)
675331337658SMarcel Moolenaar {
67548a6eceffSPhil Shafer const ssize_t extra = 5; /* space, equals, quote, quote, and nul */
675531337658SMarcel Moolenaar xop = xo_default(xop);
675631337658SMarcel Moolenaar
67578a6eceffSPhil Shafer ssize_t rc = 0;
67588a6eceffSPhil Shafer ssize_t nlen = strlen(name);
675931337658SMarcel Moolenaar xo_buffer_t *xbp = &xop->xo_attrs;
67608a6eceffSPhil Shafer ssize_t name_offset, value_offset;
676131337658SMarcel Moolenaar
6762d1a0d267SMarcel Moolenaar switch (xo_style(xop)) {
6763d1a0d267SMarcel Moolenaar case XO_STYLE_XML:
676431337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, nlen + extra))
676531337658SMarcel Moolenaar return -1;
676631337658SMarcel Moolenaar
676731337658SMarcel Moolenaar *xbp->xb_curp++ = ' ';
676831337658SMarcel Moolenaar memcpy(xbp->xb_curp, name, nlen);
676931337658SMarcel Moolenaar xbp->xb_curp += nlen;
677031337658SMarcel Moolenaar *xbp->xb_curp++ = '=';
677131337658SMarcel Moolenaar *xbp->xb_curp++ = '"';
677231337658SMarcel Moolenaar
6773d1a0d267SMarcel Moolenaar rc = xo_vsnprintf(xop, xbp, fmt, vap);
677431337658SMarcel Moolenaar
6775d1a0d267SMarcel Moolenaar if (rc >= 0) {
677631337658SMarcel Moolenaar rc = xo_escape_xml(xbp, rc, 1);
677731337658SMarcel Moolenaar xbp->xb_curp += rc;
677831337658SMarcel Moolenaar }
677931337658SMarcel Moolenaar
678031337658SMarcel Moolenaar if (!xo_buf_has_room(xbp, 2))
678131337658SMarcel Moolenaar return -1;
678231337658SMarcel Moolenaar
678331337658SMarcel Moolenaar *xbp->xb_curp++ = '"';
678431337658SMarcel Moolenaar *xbp->xb_curp = '\0';
678531337658SMarcel Moolenaar
6786d1a0d267SMarcel Moolenaar rc += nlen + extra;
6787d1a0d267SMarcel Moolenaar break;
6788d1a0d267SMarcel Moolenaar
6789d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER:
6790d1a0d267SMarcel Moolenaar name_offset = xo_buf_offset(xbp);
6791d1a0d267SMarcel Moolenaar xo_buf_append(xbp, name, nlen);
6792d1a0d267SMarcel Moolenaar xo_buf_append(xbp, "", 1);
6793d1a0d267SMarcel Moolenaar
6794d1a0d267SMarcel Moolenaar value_offset = xo_buf_offset(xbp);
6795d1a0d267SMarcel Moolenaar rc = xo_vsnprintf(xop, xbp, fmt, vap);
6796d1a0d267SMarcel Moolenaar if (rc >= 0) {
6797d1a0d267SMarcel Moolenaar xbp->xb_curp += rc;
6798d1a0d267SMarcel Moolenaar *xbp->xb_curp = '\0';
6799d1a0d267SMarcel Moolenaar rc = xo_encoder_handle(xop, XO_OP_ATTRIBUTE,
6800d1a0d267SMarcel Moolenaar xo_buf_data(xbp, name_offset),
6801f2b7bf8aSPhil Shafer xo_buf_data(xbp, value_offset), 0);
6802d1a0d267SMarcel Moolenaar }
6803d1a0d267SMarcel Moolenaar }
6804d1a0d267SMarcel Moolenaar
6805d1a0d267SMarcel Moolenaar return rc;
680631337658SMarcel Moolenaar }
680731337658SMarcel Moolenaar
68088a6eceffSPhil Shafer xo_ssize_t
xo_attr_h(xo_handle_t * xop,const char * name,const char * fmt,...)680931337658SMarcel Moolenaar xo_attr_h (xo_handle_t *xop, const char *name, const char *fmt, ...)
681031337658SMarcel Moolenaar {
68118a6eceffSPhil Shafer ssize_t rc;
681231337658SMarcel Moolenaar va_list vap;
681331337658SMarcel Moolenaar
681431337658SMarcel Moolenaar va_start(vap, fmt);
681531337658SMarcel Moolenaar rc = xo_attr_hv(xop, name, fmt, vap);
681631337658SMarcel Moolenaar va_end(vap);
681731337658SMarcel Moolenaar
681831337658SMarcel Moolenaar return rc;
681931337658SMarcel Moolenaar }
682031337658SMarcel Moolenaar
68218a6eceffSPhil Shafer xo_ssize_t
xo_attr(const char * name,const char * fmt,...)682231337658SMarcel Moolenaar xo_attr (const char *name, const char *fmt, ...)
682331337658SMarcel Moolenaar {
68248a6eceffSPhil Shafer ssize_t rc;
682531337658SMarcel Moolenaar va_list vap;
682631337658SMarcel Moolenaar
682731337658SMarcel Moolenaar va_start(vap, fmt);
682831337658SMarcel Moolenaar rc = xo_attr_hv(NULL, name, fmt, vap);
682931337658SMarcel Moolenaar va_end(vap);
683031337658SMarcel Moolenaar
683131337658SMarcel Moolenaar return rc;
683231337658SMarcel Moolenaar }
683331337658SMarcel Moolenaar
683431337658SMarcel Moolenaar static void
xo_depth_change(xo_handle_t * xop,const char * name,int delta,int indent,xo_state_t state,xo_xsf_flags_t flags)683531337658SMarcel Moolenaar xo_depth_change (xo_handle_t *xop, const char *name,
6836545ddfbeSMarcel Moolenaar int delta, int indent, xo_state_t state, xo_xsf_flags_t flags)
683731337658SMarcel Moolenaar {
6838788ca347SMarcel Moolenaar if (xo_style(xop) == XO_STYLE_HTML || xo_style(xop) == XO_STYLE_TEXT)
6839545ddfbeSMarcel Moolenaar indent = 0;
6840545ddfbeSMarcel Moolenaar
6841d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_DTRT))
684231337658SMarcel Moolenaar flags |= XSF_DTRT;
684331337658SMarcel Moolenaar
684431337658SMarcel Moolenaar if (delta >= 0) { /* Push operation */
684531337658SMarcel Moolenaar if (xo_depth_check(xop, xop->xo_depth + delta))
684631337658SMarcel Moolenaar return;
684731337658SMarcel Moolenaar
684831337658SMarcel Moolenaar xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth + delta];
684931337658SMarcel Moolenaar xsp->xs_flags = flags;
6850545ddfbeSMarcel Moolenaar xsp->xs_state = state;
685131337658SMarcel Moolenaar xo_stack_set_flags(xop);
685231337658SMarcel Moolenaar
6853545ddfbeSMarcel Moolenaar if (name == NULL)
6854545ddfbeSMarcel Moolenaar name = XO_FAILURE_NAME;
685531337658SMarcel Moolenaar
6856d1a0d267SMarcel Moolenaar xsp->xs_name = xo_strndup(name, -1);
685731337658SMarcel Moolenaar
685831337658SMarcel Moolenaar } else { /* Pop operation */
685931337658SMarcel Moolenaar if (xop->xo_depth == 0) {
6860d1a0d267SMarcel Moolenaar if (!XOF_ISSET(xop, XOF_IGNORE_CLOSE))
686131337658SMarcel Moolenaar xo_failure(xop, "close with empty stack: '%s'", name);
686231337658SMarcel Moolenaar return;
686331337658SMarcel Moolenaar }
686431337658SMarcel Moolenaar
686531337658SMarcel Moolenaar xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth];
6866d1a0d267SMarcel Moolenaar if (XOF_ISSET(xop, XOF_WARN)) {
686731337658SMarcel Moolenaar const char *top = xsp->xs_name;
686876afb20cSPhil Shafer if (top != NULL && name != NULL && !xo_streq(name, top)) {
686931337658SMarcel Moolenaar xo_failure(xop, "incorrect close: '%s' .vs. '%s'",
687031337658SMarcel Moolenaar name, top);
687131337658SMarcel Moolenaar return;
687231337658SMarcel Moolenaar }
687331337658SMarcel Moolenaar if ((xsp->xs_flags & XSF_LIST) != (flags & XSF_LIST)) {
687431337658SMarcel Moolenaar xo_failure(xop, "list close on list confict: '%s'",
687531337658SMarcel Moolenaar name);
687631337658SMarcel Moolenaar return;
687731337658SMarcel Moolenaar }
687831337658SMarcel Moolenaar if ((xsp->xs_flags & XSF_INSTANCE) != (flags & XSF_INSTANCE)) {
687931337658SMarcel Moolenaar xo_failure(xop, "list close on instance confict: '%s'",
688031337658SMarcel Moolenaar name);
688131337658SMarcel Moolenaar return;
688231337658SMarcel Moolenaar }
688331337658SMarcel Moolenaar }
688431337658SMarcel Moolenaar
688531337658SMarcel Moolenaar if (xsp->xs_name) {
688631337658SMarcel Moolenaar xo_free(xsp->xs_name);
688731337658SMarcel Moolenaar xsp->xs_name = NULL;
688831337658SMarcel Moolenaar }
688931337658SMarcel Moolenaar if (xsp->xs_keys) {
689031337658SMarcel Moolenaar xo_free(xsp->xs_keys);
689131337658SMarcel Moolenaar xsp->xs_keys = NULL;
689231337658SMarcel Moolenaar }
689331337658SMarcel Moolenaar }
689431337658SMarcel Moolenaar
689531337658SMarcel Moolenaar xop->xo_depth += delta; /* Record new depth */
689631337658SMarcel Moolenaar xop->xo_indent += indent;
689731337658SMarcel Moolenaar }
689831337658SMarcel Moolenaar
689931337658SMarcel Moolenaar void
xo_set_depth(xo_handle_t * xop,int depth)690031337658SMarcel Moolenaar xo_set_depth (xo_handle_t *xop, int depth)
690131337658SMarcel Moolenaar {
690231337658SMarcel Moolenaar xop = xo_default(xop);
690331337658SMarcel Moolenaar
690431337658SMarcel Moolenaar if (xo_depth_check(xop, depth))
690531337658SMarcel Moolenaar return;
690631337658SMarcel Moolenaar
690731337658SMarcel Moolenaar xop->xo_depth += depth;
690831337658SMarcel Moolenaar xop->xo_indent += depth;
6909406a584dSPhil Shafer
6910406a584dSPhil Shafer /*
6911406a584dSPhil Shafer * Handling the "top wrapper" for JSON is a bit of a pain. Here
6912406a584dSPhil Shafer * we need to detect that the depth has been changed to set the
6913406a584dSPhil Shafer * "XOIF_TOP_EMITTED" flag correctly.
6914406a584dSPhil Shafer */
6915406a584dSPhil Shafer if (xop->xo_style == XO_STYLE_JSON
6916406a584dSPhil Shafer && !XOF_ISSET(xop, XOF_NO_TOP) && xop->xo_depth > 0)
6917406a584dSPhil Shafer XOIF_SET(xop, XOIF_TOP_EMITTED);
691831337658SMarcel Moolenaar }
691931337658SMarcel Moolenaar
692031337658SMarcel Moolenaar static xo_xsf_flags_t
xo_stack_flags(xo_xof_flags_t xflags)69218a6eceffSPhil Shafer xo_stack_flags (xo_xof_flags_t xflags)
692231337658SMarcel Moolenaar {
692331337658SMarcel Moolenaar if (xflags & XOF_DTRT)
692431337658SMarcel Moolenaar return XSF_DTRT;
692531337658SMarcel Moolenaar return 0;
692631337658SMarcel Moolenaar }
692731337658SMarcel Moolenaar
6928788ca347SMarcel Moolenaar static void
xo_emit_top(xo_handle_t * xop,const char * ppn)6929788ca347SMarcel Moolenaar xo_emit_top (xo_handle_t *xop, const char *ppn)
6930788ca347SMarcel Moolenaar {
6931788ca347SMarcel Moolenaar xo_printf(xop, "%*s{%s", xo_indent(xop), "", ppn);
6932d1a0d267SMarcel Moolenaar XOIF_SET(xop, XOIF_TOP_EMITTED);
6933788ca347SMarcel Moolenaar
6934788ca347SMarcel Moolenaar if (xop->xo_version) {
6935788ca347SMarcel Moolenaar xo_printf(xop, "%*s\"__version\": \"%s\", %s",
6936788ca347SMarcel Moolenaar xo_indent(xop), "", xop->xo_version, ppn);
6937788ca347SMarcel Moolenaar xo_free(xop->xo_version);
6938788ca347SMarcel Moolenaar xop->xo_version = NULL;
6939788ca347SMarcel Moolenaar }
6940788ca347SMarcel Moolenaar }
6941788ca347SMarcel Moolenaar
69428a6eceffSPhil Shafer static ssize_t
xo_do_open_container(xo_handle_t * xop,xo_xof_flags_t flags,const char * name)6943545ddfbeSMarcel Moolenaar xo_do_open_container (xo_handle_t *xop, xo_xof_flags_t flags, const char *name)
694431337658SMarcel Moolenaar {
69458a6eceffSPhil Shafer ssize_t rc = 0;
6946d1a0d267SMarcel Moolenaar const char *ppn = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : "";
694731337658SMarcel Moolenaar const char *pre_nl = "";
694831337658SMarcel Moolenaar
694931337658SMarcel Moolenaar if (name == NULL) {
695031337658SMarcel Moolenaar xo_failure(xop, "NULL passed for container name");
695131337658SMarcel Moolenaar name = XO_FAILURE_NAME;
695231337658SMarcel Moolenaar }
695331337658SMarcel Moolenaar
6954406a584dSPhil Shafer const char *leader = xo_xml_leader(xop, name);
695531337658SMarcel Moolenaar flags |= xop->xo_flags; /* Pick up handle flags */
695631337658SMarcel Moolenaar
6957788ca347SMarcel Moolenaar switch (xo_style(xop)) {
695831337658SMarcel Moolenaar case XO_STYLE_XML:
6959406a584dSPhil Shafer rc = xo_printf(xop, "%*s<%s%s", xo_indent(xop), "", leader, name);
6960545ddfbeSMarcel Moolenaar
6961545ddfbeSMarcel Moolenaar if (xop->xo_attrs.xb_curp != xop->xo_attrs.xb_bufp) {
6962545ddfbeSMarcel Moolenaar rc += xop->xo_attrs.xb_curp - xop->xo_attrs.xb_bufp;
6963545ddfbeSMarcel Moolenaar xo_data_append(xop, xop->xo_attrs.xb_bufp,
6964545ddfbeSMarcel Moolenaar xop->xo_attrs.xb_curp - xop->xo_attrs.xb_bufp);
6965545ddfbeSMarcel Moolenaar xop->xo_attrs.xb_curp = xop->xo_attrs.xb_bufp;
6966545ddfbeSMarcel Moolenaar }
6967545ddfbeSMarcel Moolenaar
6968545ddfbeSMarcel Moolenaar rc += xo_printf(xop, ">%s", ppn);
696931337658SMarcel Moolenaar break;
697031337658SMarcel Moolenaar
697131337658SMarcel Moolenaar case XO_STYLE_JSON:
697231337658SMarcel Moolenaar xo_stack_set_flags(xop);
697331337658SMarcel Moolenaar
6974d1a0d267SMarcel Moolenaar if (!XOF_ISSET(xop, XOF_NO_TOP)
6975d1a0d267SMarcel Moolenaar && !XOIF_ISSET(xop, XOIF_TOP_EMITTED))
6976788ca347SMarcel Moolenaar xo_emit_top(xop, ppn);
697731337658SMarcel Moolenaar
697831337658SMarcel Moolenaar if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST)
6979d1a0d267SMarcel Moolenaar pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? ",\n" : ", ";
698031337658SMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST;
698131337658SMarcel Moolenaar
698234b867caSPhil Shafer /* If we need underscores, make a local copy and doctor it */
698334b867caSPhil Shafer const char *new_name = name;
698434b867caSPhil Shafer if (XOF_ISSET(xop, XOF_UNDERSCORES)) {
698534b867caSPhil Shafer size_t len = strlen(name);
698634b867caSPhil Shafer const char *old_name = name;
698734b867caSPhil Shafer char *buf, *cp, *ep;
698834b867caSPhil Shafer
698934b867caSPhil Shafer buf = alloca(len + 1);
699034b867caSPhil Shafer for (cp = buf, ep = buf + len + 1; cp < ep; cp++, old_name++)
699134b867caSPhil Shafer *cp = (*old_name == '-') ? '_' : *old_name;
699234b867caSPhil Shafer new_name = buf;
699334b867caSPhil Shafer }
699434b867caSPhil Shafer
699531337658SMarcel Moolenaar rc = xo_printf(xop, "%s%*s\"%s\": {%s",
699634b867caSPhil Shafer pre_nl, xo_indent(xop), "", new_name, ppn);
699731337658SMarcel Moolenaar break;
6998d1a0d267SMarcel Moolenaar
6999d1a0d267SMarcel Moolenaar case XO_STYLE_SDPARAMS:
7000d1a0d267SMarcel Moolenaar break;
7001d1a0d267SMarcel Moolenaar
7002d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER:
7003f2b7bf8aSPhil Shafer rc = xo_encoder_handle(xop, XO_OP_OPEN_CONTAINER, name, NULL, flags);
7004d1a0d267SMarcel Moolenaar break;
700531337658SMarcel Moolenaar }
700631337658SMarcel Moolenaar
7007545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, 1, 1, XSS_OPEN_CONTAINER,
7008545ddfbeSMarcel Moolenaar xo_stack_flags(flags));
7009545ddfbeSMarcel Moolenaar
701031337658SMarcel Moolenaar return rc;
701131337658SMarcel Moolenaar }
701231337658SMarcel Moolenaar
7013406a584dSPhil Shafer xo_ssize_t
xo_open_container_hf(xo_handle_t * xop,xo_xof_flags_t flags,const char * name)7014545ddfbeSMarcel Moolenaar xo_open_container_hf (xo_handle_t *xop, xo_xof_flags_t flags, const char *name)
7015545ddfbeSMarcel Moolenaar {
7016545ddfbeSMarcel Moolenaar return xo_transition(xop, flags, name, XSS_OPEN_CONTAINER);
7017545ddfbeSMarcel Moolenaar }
7018545ddfbeSMarcel Moolenaar
70198a6eceffSPhil Shafer xo_ssize_t
xo_open_container_h(xo_handle_t * xop,const char * name)702031337658SMarcel Moolenaar xo_open_container_h (xo_handle_t *xop, const char *name)
702131337658SMarcel Moolenaar {
702231337658SMarcel Moolenaar return xo_open_container_hf(xop, 0, name);
702331337658SMarcel Moolenaar }
702431337658SMarcel Moolenaar
70258a6eceffSPhil Shafer xo_ssize_t
xo_open_container(const char * name)702631337658SMarcel Moolenaar xo_open_container (const char *name)
702731337658SMarcel Moolenaar {
702831337658SMarcel Moolenaar return xo_open_container_hf(NULL, 0, name);
702931337658SMarcel Moolenaar }
703031337658SMarcel Moolenaar
70318a6eceffSPhil Shafer xo_ssize_t
xo_open_container_hd(xo_handle_t * xop,const char * name)703231337658SMarcel Moolenaar xo_open_container_hd (xo_handle_t *xop, const char *name)
703331337658SMarcel Moolenaar {
703431337658SMarcel Moolenaar return xo_open_container_hf(xop, XOF_DTRT, name);
703531337658SMarcel Moolenaar }
703631337658SMarcel Moolenaar
70378a6eceffSPhil Shafer xo_ssize_t
xo_open_container_d(const char * name)703831337658SMarcel Moolenaar xo_open_container_d (const char *name)
703931337658SMarcel Moolenaar {
704031337658SMarcel Moolenaar return xo_open_container_hf(NULL, XOF_DTRT, name);
704131337658SMarcel Moolenaar }
704231337658SMarcel Moolenaar
7043545ddfbeSMarcel Moolenaar static int
xo_do_close_container(xo_handle_t * xop,const char * name)7044545ddfbeSMarcel Moolenaar xo_do_close_container (xo_handle_t *xop, const char *name)
704531337658SMarcel Moolenaar {
704631337658SMarcel Moolenaar xop = xo_default(xop);
704731337658SMarcel Moolenaar
70488a6eceffSPhil Shafer ssize_t rc = 0;
7049d1a0d267SMarcel Moolenaar const char *ppn = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : "";
705031337658SMarcel Moolenaar const char *pre_nl = "";
705131337658SMarcel Moolenaar
705231337658SMarcel Moolenaar if (name == NULL) {
705331337658SMarcel Moolenaar xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth];
705431337658SMarcel Moolenaar
705531337658SMarcel Moolenaar name = xsp->xs_name;
705631337658SMarcel Moolenaar if (name) {
70578a6eceffSPhil Shafer ssize_t len = strlen(name) + 1;
705831337658SMarcel Moolenaar /* We need to make a local copy; xo_depth_change will free it */
705931337658SMarcel Moolenaar char *cp = alloca(len);
706031337658SMarcel Moolenaar memcpy(cp, name, len);
706131337658SMarcel Moolenaar name = cp;
7062545ddfbeSMarcel Moolenaar } else if (!(xsp->xs_flags & XSF_DTRT)) {
7063545ddfbeSMarcel Moolenaar xo_failure(xop, "missing name without 'dtrt' mode");
706431337658SMarcel Moolenaar name = XO_FAILURE_NAME;
706531337658SMarcel Moolenaar }
7066545ddfbeSMarcel Moolenaar }
706731337658SMarcel Moolenaar
7068406a584dSPhil Shafer const char *leader = xo_xml_leader(xop, name);
7069406a584dSPhil Shafer
7070788ca347SMarcel Moolenaar switch (xo_style(xop)) {
707131337658SMarcel Moolenaar case XO_STYLE_XML:
7072545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, -1, -1, XSS_CLOSE_CONTAINER, 0);
7073406a584dSPhil Shafer rc = xo_printf(xop, "%*s</%s%s>%s", xo_indent(xop), "", leader, name, ppn);
707431337658SMarcel Moolenaar break;
707531337658SMarcel Moolenaar
707631337658SMarcel Moolenaar case XO_STYLE_JSON:
7077406a584dSPhil Shafer xo_stack_set_flags(xop);
7078406a584dSPhil Shafer
7079d1a0d267SMarcel Moolenaar pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : "";
7080406a584dSPhil Shafer ppn = "";
708131337658SMarcel Moolenaar
7082545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, -1, -1, XSS_CLOSE_CONTAINER, 0);
708331337658SMarcel Moolenaar rc = xo_printf(xop, "%s%*s}%s", pre_nl, xo_indent(xop), "", ppn);
708431337658SMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST;
708531337658SMarcel Moolenaar break;
708631337658SMarcel Moolenaar
708731337658SMarcel Moolenaar case XO_STYLE_HTML:
708831337658SMarcel Moolenaar case XO_STYLE_TEXT:
7089545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, -1, 0, XSS_CLOSE_CONTAINER, 0);
709031337658SMarcel Moolenaar break;
7091d1a0d267SMarcel Moolenaar
7092d1a0d267SMarcel Moolenaar case XO_STYLE_SDPARAMS:
7093d1a0d267SMarcel Moolenaar break;
7094d1a0d267SMarcel Moolenaar
7095d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER:
7096d1a0d267SMarcel Moolenaar xo_depth_change(xop, name, -1, 0, XSS_CLOSE_CONTAINER, 0);
7097f2b7bf8aSPhil Shafer rc = xo_encoder_handle(xop, XO_OP_CLOSE_CONTAINER, name, NULL, 0);
7098d1a0d267SMarcel Moolenaar break;
709931337658SMarcel Moolenaar }
710031337658SMarcel Moolenaar
710131337658SMarcel Moolenaar return rc;
710231337658SMarcel Moolenaar }
710331337658SMarcel Moolenaar
71048a6eceffSPhil Shafer xo_ssize_t
xo_close_container_h(xo_handle_t * xop,const char * name)7105545ddfbeSMarcel Moolenaar xo_close_container_h (xo_handle_t *xop, const char *name)
7106545ddfbeSMarcel Moolenaar {
7107545ddfbeSMarcel Moolenaar return xo_transition(xop, 0, name, XSS_CLOSE_CONTAINER);
7108545ddfbeSMarcel Moolenaar }
7109545ddfbeSMarcel Moolenaar
71108a6eceffSPhil Shafer xo_ssize_t
xo_close_container(const char * name)711131337658SMarcel Moolenaar xo_close_container (const char *name)
711231337658SMarcel Moolenaar {
711331337658SMarcel Moolenaar return xo_close_container_h(NULL, name);
711431337658SMarcel Moolenaar }
711531337658SMarcel Moolenaar
71168a6eceffSPhil Shafer xo_ssize_t
xo_close_container_hd(xo_handle_t * xop)711731337658SMarcel Moolenaar xo_close_container_hd (xo_handle_t *xop)
711831337658SMarcel Moolenaar {
711931337658SMarcel Moolenaar return xo_close_container_h(xop, NULL);
712031337658SMarcel Moolenaar }
712131337658SMarcel Moolenaar
71228a6eceffSPhil Shafer xo_ssize_t
xo_close_container_d(void)712331337658SMarcel Moolenaar xo_close_container_d (void)
712431337658SMarcel Moolenaar {
712531337658SMarcel Moolenaar return xo_close_container_h(NULL, NULL);
712631337658SMarcel Moolenaar }
712731337658SMarcel Moolenaar
712831337658SMarcel Moolenaar static int
xo_do_open_list(xo_handle_t * xop,xo_xof_flags_t flags,const char * name)7129406a584dSPhil Shafer xo_do_open_list (xo_handle_t *xop, xo_xof_flags_t flags, const char *name)
713031337658SMarcel Moolenaar {
71318a6eceffSPhil Shafer ssize_t rc = 0;
7132545ddfbeSMarcel Moolenaar int indent = 0;
7133545ddfbeSMarcel Moolenaar
713431337658SMarcel Moolenaar xop = xo_default(xop);
713531337658SMarcel Moolenaar
7136d1a0d267SMarcel Moolenaar const char *ppn = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : "";
713731337658SMarcel Moolenaar const char *pre_nl = "";
713831337658SMarcel Moolenaar
7139d1a0d267SMarcel Moolenaar switch (xo_style(xop)) {
7140d1a0d267SMarcel Moolenaar case XO_STYLE_JSON:
7141d1a0d267SMarcel Moolenaar
7142545ddfbeSMarcel Moolenaar indent = 1;
7143d1a0d267SMarcel Moolenaar if (!XOF_ISSET(xop, XOF_NO_TOP)
7144d1a0d267SMarcel Moolenaar && !XOIF_ISSET(xop, XOIF_TOP_EMITTED))
7145788ca347SMarcel Moolenaar xo_emit_top(xop, ppn);
714631337658SMarcel Moolenaar
714731337658SMarcel Moolenaar if (name == NULL) {
714831337658SMarcel Moolenaar xo_failure(xop, "NULL passed for list name");
714931337658SMarcel Moolenaar name = XO_FAILURE_NAME;
715031337658SMarcel Moolenaar }
715131337658SMarcel Moolenaar
715231337658SMarcel Moolenaar xo_stack_set_flags(xop);
715331337658SMarcel Moolenaar
715431337658SMarcel Moolenaar if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST)
7155d1a0d267SMarcel Moolenaar pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? ",\n" : ", ";
715631337658SMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST;
715731337658SMarcel Moolenaar
715834b867caSPhil Shafer /* If we need underscores, make a local copy and doctor it */
715934b867caSPhil Shafer const char *new_name = name;
716034b867caSPhil Shafer if (XOF_ISSET(xop, XOF_UNDERSCORES)) {
716134b867caSPhil Shafer size_t len = strlen(name);
716234b867caSPhil Shafer const char *old_name = name;
716334b867caSPhil Shafer char *buf, *cp, *ep;
716434b867caSPhil Shafer
716534b867caSPhil Shafer buf = alloca(len + 1);
716634b867caSPhil Shafer for (cp = buf, ep = buf + len + 1; cp < ep; cp++, old_name++)
716734b867caSPhil Shafer *cp = (*old_name == '-') ? '_' : *old_name;
716834b867caSPhil Shafer new_name = buf;
716934b867caSPhil Shafer }
717034b867caSPhil Shafer
717131337658SMarcel Moolenaar rc = xo_printf(xop, "%s%*s\"%s\": [%s",
717234b867caSPhil Shafer pre_nl, xo_indent(xop), "", new_name, ppn);
7173d1a0d267SMarcel Moolenaar break;
7174d1a0d267SMarcel Moolenaar
7175d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER:
7176f2b7bf8aSPhil Shafer rc = xo_encoder_handle(xop, XO_OP_OPEN_LIST, name, NULL, flags);
7177d1a0d267SMarcel Moolenaar break;
7178545ddfbeSMarcel Moolenaar }
7179545ddfbeSMarcel Moolenaar
7180545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, 1, indent, XSS_OPEN_LIST,
7181545ddfbeSMarcel Moolenaar XSF_LIST | xo_stack_flags(flags));
718231337658SMarcel Moolenaar
718331337658SMarcel Moolenaar return rc;
718431337658SMarcel Moolenaar }
718531337658SMarcel Moolenaar
7186406a584dSPhil Shafer xo_ssize_t
xo_open_list_hf(xo_handle_t * xop,xo_xof_flags_t flags,const char * name)7187406a584dSPhil Shafer xo_open_list_hf (xo_handle_t *xop, xo_xof_flags_t flags, const char *name)
7188545ddfbeSMarcel Moolenaar {
7189545ddfbeSMarcel Moolenaar return xo_transition(xop, flags, name, XSS_OPEN_LIST);
7190545ddfbeSMarcel Moolenaar }
7191545ddfbeSMarcel Moolenaar
71928a6eceffSPhil Shafer xo_ssize_t
xo_open_list_h(xo_handle_t * xop,const char * name)719342ff34c3SPhil Shafer xo_open_list_h (xo_handle_t *xop, const char *name)
719431337658SMarcel Moolenaar {
719531337658SMarcel Moolenaar return xo_open_list_hf(xop, 0, name);
719631337658SMarcel Moolenaar }
719731337658SMarcel Moolenaar
71988a6eceffSPhil Shafer xo_ssize_t
xo_open_list(const char * name)719931337658SMarcel Moolenaar xo_open_list (const char *name)
720031337658SMarcel Moolenaar {
720131337658SMarcel Moolenaar return xo_open_list_hf(NULL, 0, name);
720231337658SMarcel Moolenaar }
720331337658SMarcel Moolenaar
72048a6eceffSPhil Shafer xo_ssize_t
xo_open_list_hd(xo_handle_t * xop,const char * name)720542ff34c3SPhil Shafer xo_open_list_hd (xo_handle_t *xop, const char *name)
720631337658SMarcel Moolenaar {
720731337658SMarcel Moolenaar return xo_open_list_hf(xop, XOF_DTRT, name);
720831337658SMarcel Moolenaar }
720931337658SMarcel Moolenaar
72108a6eceffSPhil Shafer xo_ssize_t
xo_open_list_d(const char * name)721131337658SMarcel Moolenaar xo_open_list_d (const char *name)
721231337658SMarcel Moolenaar {
721331337658SMarcel Moolenaar return xo_open_list_hf(NULL, XOF_DTRT, name);
721431337658SMarcel Moolenaar }
721531337658SMarcel Moolenaar
7216545ddfbeSMarcel Moolenaar static int
xo_do_close_list(xo_handle_t * xop,const char * name)7217545ddfbeSMarcel Moolenaar xo_do_close_list (xo_handle_t *xop, const char *name)
721831337658SMarcel Moolenaar {
72198a6eceffSPhil Shafer ssize_t rc = 0;
722031337658SMarcel Moolenaar const char *pre_nl = "";
722131337658SMarcel Moolenaar
722231337658SMarcel Moolenaar if (name == NULL) {
722331337658SMarcel Moolenaar xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth];
722431337658SMarcel Moolenaar
722531337658SMarcel Moolenaar name = xsp->xs_name;
722631337658SMarcel Moolenaar if (name) {
72278a6eceffSPhil Shafer ssize_t len = strlen(name) + 1;
722831337658SMarcel Moolenaar /* We need to make a local copy; xo_depth_change will free it */
722931337658SMarcel Moolenaar char *cp = alloca(len);
723031337658SMarcel Moolenaar memcpy(cp, name, len);
723131337658SMarcel Moolenaar name = cp;
7232545ddfbeSMarcel Moolenaar } else if (!(xsp->xs_flags & XSF_DTRT)) {
7233545ddfbeSMarcel Moolenaar xo_failure(xop, "missing name without 'dtrt' mode");
723431337658SMarcel Moolenaar name = XO_FAILURE_NAME;
723531337658SMarcel Moolenaar }
7236545ddfbeSMarcel Moolenaar }
723731337658SMarcel Moolenaar
7238d1a0d267SMarcel Moolenaar switch (xo_style(xop)) {
7239d1a0d267SMarcel Moolenaar case XO_STYLE_JSON:
724031337658SMarcel Moolenaar if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST)
7241d1a0d267SMarcel Moolenaar pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : "";
724231337658SMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST;
724331337658SMarcel Moolenaar
7244545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, -1, -1, XSS_CLOSE_LIST, XSF_LIST);
724531337658SMarcel Moolenaar rc = xo_printf(xop, "%s%*s]", pre_nl, xo_indent(xop), "");
724631337658SMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST;
7247d1a0d267SMarcel Moolenaar break;
724831337658SMarcel Moolenaar
7249d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER:
7250d1a0d267SMarcel Moolenaar xo_depth_change(xop, name, -1, 0, XSS_CLOSE_LIST, XSF_LIST);
7251f2b7bf8aSPhil Shafer rc = xo_encoder_handle(xop, XO_OP_CLOSE_LIST, name, NULL, 0);
7252d1a0d267SMarcel Moolenaar break;
7253d1a0d267SMarcel Moolenaar
7254d1a0d267SMarcel Moolenaar default:
7255545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, -1, 0, XSS_CLOSE_LIST, XSF_LIST);
7256545ddfbeSMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST;
7257d1a0d267SMarcel Moolenaar break;
7258545ddfbeSMarcel Moolenaar }
7259545ddfbeSMarcel Moolenaar
7260a0f704ffSMarcel Moolenaar return rc;
726131337658SMarcel Moolenaar }
726231337658SMarcel Moolenaar
72638a6eceffSPhil Shafer xo_ssize_t
xo_close_list_h(xo_handle_t * xop,const char * name)7264545ddfbeSMarcel Moolenaar xo_close_list_h (xo_handle_t *xop, const char *name)
7265545ddfbeSMarcel Moolenaar {
7266545ddfbeSMarcel Moolenaar return xo_transition(xop, 0, name, XSS_CLOSE_LIST);
7267545ddfbeSMarcel Moolenaar }
7268545ddfbeSMarcel Moolenaar
72698a6eceffSPhil Shafer xo_ssize_t
xo_close_list(const char * name)727031337658SMarcel Moolenaar xo_close_list (const char *name)
727131337658SMarcel Moolenaar {
727231337658SMarcel Moolenaar return xo_close_list_h(NULL, name);
727331337658SMarcel Moolenaar }
727431337658SMarcel Moolenaar
72758a6eceffSPhil Shafer xo_ssize_t
xo_close_list_hd(xo_handle_t * xop)727631337658SMarcel Moolenaar xo_close_list_hd (xo_handle_t *xop)
727731337658SMarcel Moolenaar {
727831337658SMarcel Moolenaar return xo_close_list_h(xop, NULL);
727931337658SMarcel Moolenaar }
728031337658SMarcel Moolenaar
72818a6eceffSPhil Shafer xo_ssize_t
xo_close_list_d(void)728231337658SMarcel Moolenaar xo_close_list_d (void)
728331337658SMarcel Moolenaar {
728431337658SMarcel Moolenaar return xo_close_list_h(NULL, NULL);
728531337658SMarcel Moolenaar }
728631337658SMarcel Moolenaar
728731337658SMarcel Moolenaar static int
xo_do_open_leaf_list(xo_handle_t * xop,xo_xof_flags_t flags,const char * name)7288406a584dSPhil Shafer xo_do_open_leaf_list (xo_handle_t *xop, xo_xof_flags_t flags, const char *name)
7289545ddfbeSMarcel Moolenaar {
72908a6eceffSPhil Shafer ssize_t rc = 0;
7291545ddfbeSMarcel Moolenaar int indent = 0;
7292545ddfbeSMarcel Moolenaar
7293545ddfbeSMarcel Moolenaar xop = xo_default(xop);
7294545ddfbeSMarcel Moolenaar
7295d1a0d267SMarcel Moolenaar const char *ppn = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : "";
7296545ddfbeSMarcel Moolenaar const char *pre_nl = "";
7297545ddfbeSMarcel Moolenaar
7298d1a0d267SMarcel Moolenaar switch (xo_style(xop)) {
7299d1a0d267SMarcel Moolenaar case XO_STYLE_JSON:
7300545ddfbeSMarcel Moolenaar indent = 1;
7301545ddfbeSMarcel Moolenaar
7302d1a0d267SMarcel Moolenaar if (!XOF_ISSET(xop, XOF_NO_TOP)) {
7303d1a0d267SMarcel Moolenaar if (!XOIF_ISSET(xop, XOIF_TOP_EMITTED)) {
7304545ddfbeSMarcel Moolenaar xo_printf(xop, "%*s{%s", xo_indent(xop), "", ppn);
7305d1a0d267SMarcel Moolenaar XOIF_SET(xop, XOIF_TOP_EMITTED);
7306545ddfbeSMarcel Moolenaar }
7307545ddfbeSMarcel Moolenaar }
7308545ddfbeSMarcel Moolenaar
7309545ddfbeSMarcel Moolenaar if (name == NULL) {
7310545ddfbeSMarcel Moolenaar xo_failure(xop, "NULL passed for list name");
7311545ddfbeSMarcel Moolenaar name = XO_FAILURE_NAME;
7312545ddfbeSMarcel Moolenaar }
7313545ddfbeSMarcel Moolenaar
7314545ddfbeSMarcel Moolenaar xo_stack_set_flags(xop);
7315545ddfbeSMarcel Moolenaar
7316545ddfbeSMarcel Moolenaar if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST)
7317d1a0d267SMarcel Moolenaar pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? ",\n" : ", ";
7318545ddfbeSMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST;
7319545ddfbeSMarcel Moolenaar
7320545ddfbeSMarcel Moolenaar rc = xo_printf(xop, "%s%*s\"%s\": [%s",
7321545ddfbeSMarcel Moolenaar pre_nl, xo_indent(xop), "", name, ppn);
7322d1a0d267SMarcel Moolenaar break;
7323d1a0d267SMarcel Moolenaar
7324d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER:
7325f2b7bf8aSPhil Shafer rc = xo_encoder_handle(xop, XO_OP_OPEN_LEAF_LIST, name, NULL, flags);
7326d1a0d267SMarcel Moolenaar break;
7327545ddfbeSMarcel Moolenaar }
7328545ddfbeSMarcel Moolenaar
7329545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, 1, indent, XSS_OPEN_LEAF_LIST,
7330545ddfbeSMarcel Moolenaar XSF_LIST | xo_stack_flags(flags));
7331545ddfbeSMarcel Moolenaar
7332545ddfbeSMarcel Moolenaar return rc;
7333545ddfbeSMarcel Moolenaar }
7334545ddfbeSMarcel Moolenaar
7335545ddfbeSMarcel Moolenaar static int
xo_do_close_leaf_list(xo_handle_t * xop,const char * name)7336545ddfbeSMarcel Moolenaar xo_do_close_leaf_list (xo_handle_t *xop, const char *name)
7337545ddfbeSMarcel Moolenaar {
73388a6eceffSPhil Shafer ssize_t rc = 0;
7339545ddfbeSMarcel Moolenaar const char *pre_nl = "";
7340545ddfbeSMarcel Moolenaar
7341545ddfbeSMarcel Moolenaar if (name == NULL) {
7342545ddfbeSMarcel Moolenaar xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth];
7343545ddfbeSMarcel Moolenaar
7344545ddfbeSMarcel Moolenaar name = xsp->xs_name;
7345545ddfbeSMarcel Moolenaar if (name) {
73468a6eceffSPhil Shafer ssize_t len = strlen(name) + 1;
7347545ddfbeSMarcel Moolenaar /* We need to make a local copy; xo_depth_change will free it */
7348545ddfbeSMarcel Moolenaar char *cp = alloca(len);
7349545ddfbeSMarcel Moolenaar memcpy(cp, name, len);
7350545ddfbeSMarcel Moolenaar name = cp;
7351545ddfbeSMarcel Moolenaar } else if (!(xsp->xs_flags & XSF_DTRT)) {
7352545ddfbeSMarcel Moolenaar xo_failure(xop, "missing name without 'dtrt' mode");
7353545ddfbeSMarcel Moolenaar name = XO_FAILURE_NAME;
7354545ddfbeSMarcel Moolenaar }
7355545ddfbeSMarcel Moolenaar }
7356545ddfbeSMarcel Moolenaar
7357d1a0d267SMarcel Moolenaar switch (xo_style(xop)) {
7358d1a0d267SMarcel Moolenaar case XO_STYLE_JSON:
7359545ddfbeSMarcel Moolenaar if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST)
7360d1a0d267SMarcel Moolenaar pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : "";
7361545ddfbeSMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST;
7362545ddfbeSMarcel Moolenaar
7363545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, -1, -1, XSS_CLOSE_LEAF_LIST, XSF_LIST);
7364545ddfbeSMarcel Moolenaar rc = xo_printf(xop, "%s%*s]", pre_nl, xo_indent(xop), "");
7365545ddfbeSMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST;
7366d1a0d267SMarcel Moolenaar break;
7367545ddfbeSMarcel Moolenaar
7368d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER:
7369f2b7bf8aSPhil Shafer rc = xo_encoder_handle(xop, XO_OP_CLOSE_LEAF_LIST, name, NULL, 0);
7370ee5cf116SPhil Shafer /* FALLTHRU */
7371d1a0d267SMarcel Moolenaar
7372d1a0d267SMarcel Moolenaar default:
7373545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, -1, 0, XSS_CLOSE_LEAF_LIST, XSF_LIST);
7374545ddfbeSMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST;
7375d1a0d267SMarcel Moolenaar break;
7376545ddfbeSMarcel Moolenaar }
7377545ddfbeSMarcel Moolenaar
7378545ddfbeSMarcel Moolenaar return rc;
7379545ddfbeSMarcel Moolenaar }
7380545ddfbeSMarcel Moolenaar
7381545ddfbeSMarcel Moolenaar static int
xo_do_open_instance(xo_handle_t * xop,xo_xof_flags_t flags,const char * name)7382406a584dSPhil Shafer xo_do_open_instance (xo_handle_t *xop, xo_xof_flags_t flags, const char *name)
738331337658SMarcel Moolenaar {
738431337658SMarcel Moolenaar xop = xo_default(xop);
738531337658SMarcel Moolenaar
73868a6eceffSPhil Shafer ssize_t rc = 0;
7387d1a0d267SMarcel Moolenaar const char *ppn = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : "";
738831337658SMarcel Moolenaar const char *pre_nl = "";
738931337658SMarcel Moolenaar
739031337658SMarcel Moolenaar if (name == NULL) {
739131337658SMarcel Moolenaar xo_failure(xop, "NULL passed for instance name");
739231337658SMarcel Moolenaar name = XO_FAILURE_NAME;
739331337658SMarcel Moolenaar }
739431337658SMarcel Moolenaar
7395406a584dSPhil Shafer const char *leader = xo_xml_leader(xop, name);
7396406a584dSPhil Shafer flags |= xop->xo_flags;
7397406a584dSPhil Shafer
7398788ca347SMarcel Moolenaar switch (xo_style(xop)) {
739931337658SMarcel Moolenaar case XO_STYLE_XML:
7400406a584dSPhil Shafer rc = xo_printf(xop, "%*s<%s%s", xo_indent(xop), "", leader, name);
7401545ddfbeSMarcel Moolenaar
7402545ddfbeSMarcel Moolenaar if (xop->xo_attrs.xb_curp != xop->xo_attrs.xb_bufp) {
7403545ddfbeSMarcel Moolenaar rc += xop->xo_attrs.xb_curp - xop->xo_attrs.xb_bufp;
7404545ddfbeSMarcel Moolenaar xo_data_append(xop, xop->xo_attrs.xb_bufp,
7405545ddfbeSMarcel Moolenaar xop->xo_attrs.xb_curp - xop->xo_attrs.xb_bufp);
7406545ddfbeSMarcel Moolenaar xop->xo_attrs.xb_curp = xop->xo_attrs.xb_bufp;
7407545ddfbeSMarcel Moolenaar }
7408545ddfbeSMarcel Moolenaar
7409545ddfbeSMarcel Moolenaar rc += xo_printf(xop, ">%s", ppn);
741031337658SMarcel Moolenaar break;
741131337658SMarcel Moolenaar
741231337658SMarcel Moolenaar case XO_STYLE_JSON:
741331337658SMarcel Moolenaar xo_stack_set_flags(xop);
741431337658SMarcel Moolenaar
741531337658SMarcel Moolenaar if (xop->xo_stack[xop->xo_depth].xs_flags & XSF_NOT_FIRST)
7416d1a0d267SMarcel Moolenaar pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? ",\n" : ", ";
741731337658SMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST;
741831337658SMarcel Moolenaar
741931337658SMarcel Moolenaar rc = xo_printf(xop, "%s%*s{%s",
742031337658SMarcel Moolenaar pre_nl, xo_indent(xop), "", ppn);
742131337658SMarcel Moolenaar break;
7422d1a0d267SMarcel Moolenaar
7423d1a0d267SMarcel Moolenaar case XO_STYLE_SDPARAMS:
7424d1a0d267SMarcel Moolenaar break;
7425d1a0d267SMarcel Moolenaar
7426d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER:
7427f2b7bf8aSPhil Shafer rc = xo_encoder_handle(xop, XO_OP_OPEN_INSTANCE, name, NULL, flags);
7428d1a0d267SMarcel Moolenaar break;
742931337658SMarcel Moolenaar }
743031337658SMarcel Moolenaar
7431545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, 1, 1, XSS_OPEN_INSTANCE, xo_stack_flags(flags));
7432545ddfbeSMarcel Moolenaar
743331337658SMarcel Moolenaar return rc;
743431337658SMarcel Moolenaar }
743531337658SMarcel Moolenaar
7436406a584dSPhil Shafer xo_ssize_t
xo_open_instance_hf(xo_handle_t * xop,xo_xof_flags_t flags,const char * name)7437406a584dSPhil Shafer xo_open_instance_hf (xo_handle_t *xop, xo_xof_flags_t flags, const char *name)
7438545ddfbeSMarcel Moolenaar {
7439545ddfbeSMarcel Moolenaar return xo_transition(xop, flags, name, XSS_OPEN_INSTANCE);
7440545ddfbeSMarcel Moolenaar }
7441545ddfbeSMarcel Moolenaar
74428a6eceffSPhil Shafer xo_ssize_t
xo_open_instance_h(xo_handle_t * xop,const char * name)744331337658SMarcel Moolenaar xo_open_instance_h (xo_handle_t *xop, const char *name)
744431337658SMarcel Moolenaar {
744531337658SMarcel Moolenaar return xo_open_instance_hf(xop, 0, name);
744631337658SMarcel Moolenaar }
744731337658SMarcel Moolenaar
74488a6eceffSPhil Shafer xo_ssize_t
xo_open_instance(const char * name)744931337658SMarcel Moolenaar xo_open_instance (const char *name)
745031337658SMarcel Moolenaar {
745131337658SMarcel Moolenaar return xo_open_instance_hf(NULL, 0, name);
745231337658SMarcel Moolenaar }
745331337658SMarcel Moolenaar
74548a6eceffSPhil Shafer xo_ssize_t
xo_open_instance_hd(xo_handle_t * xop,const char * name)745531337658SMarcel Moolenaar xo_open_instance_hd (xo_handle_t *xop, const char *name)
745631337658SMarcel Moolenaar {
745731337658SMarcel Moolenaar return xo_open_instance_hf(xop, XOF_DTRT, name);
745831337658SMarcel Moolenaar }
745931337658SMarcel Moolenaar
74608a6eceffSPhil Shafer xo_ssize_t
xo_open_instance_d(const char * name)746131337658SMarcel Moolenaar xo_open_instance_d (const char *name)
746231337658SMarcel Moolenaar {
746331337658SMarcel Moolenaar return xo_open_instance_hf(NULL, XOF_DTRT, name);
746431337658SMarcel Moolenaar }
746531337658SMarcel Moolenaar
7466545ddfbeSMarcel Moolenaar static int
xo_do_close_instance(xo_handle_t * xop,const char * name)7467545ddfbeSMarcel Moolenaar xo_do_close_instance (xo_handle_t *xop, const char *name)
746831337658SMarcel Moolenaar {
746931337658SMarcel Moolenaar xop = xo_default(xop);
747031337658SMarcel Moolenaar
74718a6eceffSPhil Shafer ssize_t rc = 0;
7472d1a0d267SMarcel Moolenaar const char *ppn = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : "";
747331337658SMarcel Moolenaar const char *pre_nl = "";
747431337658SMarcel Moolenaar
747531337658SMarcel Moolenaar if (name == NULL) {
747631337658SMarcel Moolenaar xo_stack_t *xsp = &xop->xo_stack[xop->xo_depth];
747731337658SMarcel Moolenaar
747831337658SMarcel Moolenaar name = xsp->xs_name;
747931337658SMarcel Moolenaar if (name) {
74808a6eceffSPhil Shafer ssize_t len = strlen(name) + 1;
748131337658SMarcel Moolenaar /* We need to make a local copy; xo_depth_change will free it */
748231337658SMarcel Moolenaar char *cp = alloca(len);
748331337658SMarcel Moolenaar memcpy(cp, name, len);
748431337658SMarcel Moolenaar name = cp;
7485545ddfbeSMarcel Moolenaar } else if (!(xsp->xs_flags & XSF_DTRT)) {
7486545ddfbeSMarcel Moolenaar xo_failure(xop, "missing name without 'dtrt' mode");
748731337658SMarcel Moolenaar name = XO_FAILURE_NAME;
748831337658SMarcel Moolenaar }
7489545ddfbeSMarcel Moolenaar }
749031337658SMarcel Moolenaar
7491406a584dSPhil Shafer const char *leader = xo_xml_leader(xop, name);
7492406a584dSPhil Shafer
7493788ca347SMarcel Moolenaar switch (xo_style(xop)) {
749431337658SMarcel Moolenaar case XO_STYLE_XML:
7495545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, -1, -1, XSS_CLOSE_INSTANCE, 0);
7496406a584dSPhil Shafer rc = xo_printf(xop, "%*s</%s%s>%s", xo_indent(xop), "", leader, name, ppn);
749731337658SMarcel Moolenaar break;
749831337658SMarcel Moolenaar
749931337658SMarcel Moolenaar case XO_STYLE_JSON:
7500d1a0d267SMarcel Moolenaar pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : "";
750131337658SMarcel Moolenaar
7502545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, -1, -1, XSS_CLOSE_INSTANCE, 0);
750331337658SMarcel Moolenaar rc = xo_printf(xop, "%s%*s}", pre_nl, xo_indent(xop), "");
750431337658SMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= XSF_NOT_FIRST;
750531337658SMarcel Moolenaar break;
750631337658SMarcel Moolenaar
750731337658SMarcel Moolenaar case XO_STYLE_HTML:
750831337658SMarcel Moolenaar case XO_STYLE_TEXT:
7509545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, -1, 0, XSS_CLOSE_INSTANCE, 0);
751031337658SMarcel Moolenaar break;
7511d1a0d267SMarcel Moolenaar
7512d1a0d267SMarcel Moolenaar case XO_STYLE_SDPARAMS:
7513d1a0d267SMarcel Moolenaar break;
7514d1a0d267SMarcel Moolenaar
7515d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER:
7516d1a0d267SMarcel Moolenaar xo_depth_change(xop, name, -1, 0, XSS_CLOSE_INSTANCE, 0);
7517f2b7bf8aSPhil Shafer rc = xo_encoder_handle(xop, XO_OP_CLOSE_INSTANCE, name, NULL, 0);
7518d1a0d267SMarcel Moolenaar break;
751931337658SMarcel Moolenaar }
752031337658SMarcel Moolenaar
752131337658SMarcel Moolenaar return rc;
752231337658SMarcel Moolenaar }
752331337658SMarcel Moolenaar
75248a6eceffSPhil Shafer xo_ssize_t
xo_close_instance_h(xo_handle_t * xop,const char * name)7525545ddfbeSMarcel Moolenaar xo_close_instance_h (xo_handle_t *xop, const char *name)
7526545ddfbeSMarcel Moolenaar {
7527545ddfbeSMarcel Moolenaar return xo_transition(xop, 0, name, XSS_CLOSE_INSTANCE);
7528545ddfbeSMarcel Moolenaar }
7529545ddfbeSMarcel Moolenaar
75308a6eceffSPhil Shafer xo_ssize_t
xo_close_instance(const char * name)753131337658SMarcel Moolenaar xo_close_instance (const char *name)
753231337658SMarcel Moolenaar {
753331337658SMarcel Moolenaar return xo_close_instance_h(NULL, name);
753431337658SMarcel Moolenaar }
753531337658SMarcel Moolenaar
75368a6eceffSPhil Shafer xo_ssize_t
xo_close_instance_hd(xo_handle_t * xop)753731337658SMarcel Moolenaar xo_close_instance_hd (xo_handle_t *xop)
753831337658SMarcel Moolenaar {
753931337658SMarcel Moolenaar return xo_close_instance_h(xop, NULL);
754031337658SMarcel Moolenaar }
754131337658SMarcel Moolenaar
75428a6eceffSPhil Shafer xo_ssize_t
xo_close_instance_d(void)754331337658SMarcel Moolenaar xo_close_instance_d (void)
754431337658SMarcel Moolenaar {
754531337658SMarcel Moolenaar return xo_close_instance_h(NULL, NULL);
754631337658SMarcel Moolenaar }
754731337658SMarcel Moolenaar
7548545ddfbeSMarcel Moolenaar static int
xo_do_close_all(xo_handle_t * xop,xo_stack_t * limit)7549545ddfbeSMarcel Moolenaar xo_do_close_all (xo_handle_t *xop, xo_stack_t *limit)
7550545ddfbeSMarcel Moolenaar {
7551545ddfbeSMarcel Moolenaar xo_stack_t *xsp;
75528a6eceffSPhil Shafer ssize_t rc = 0;
7553545ddfbeSMarcel Moolenaar xo_xsf_flags_t flags;
7554545ddfbeSMarcel Moolenaar
7555545ddfbeSMarcel Moolenaar for (xsp = &xop->xo_stack[xop->xo_depth]; xsp >= limit; xsp--) {
7556545ddfbeSMarcel Moolenaar switch (xsp->xs_state) {
7557545ddfbeSMarcel Moolenaar case XSS_INIT:
7558545ddfbeSMarcel Moolenaar /* Nothing */
7559545ddfbeSMarcel Moolenaar rc = 0;
7560545ddfbeSMarcel Moolenaar break;
7561545ddfbeSMarcel Moolenaar
7562545ddfbeSMarcel Moolenaar case XSS_OPEN_CONTAINER:
7563545ddfbeSMarcel Moolenaar rc = xo_do_close_container(xop, NULL);
7564545ddfbeSMarcel Moolenaar break;
7565545ddfbeSMarcel Moolenaar
7566545ddfbeSMarcel Moolenaar case XSS_OPEN_LIST:
7567545ddfbeSMarcel Moolenaar rc = xo_do_close_list(xop, NULL);
7568545ddfbeSMarcel Moolenaar break;
7569545ddfbeSMarcel Moolenaar
7570545ddfbeSMarcel Moolenaar case XSS_OPEN_INSTANCE:
7571545ddfbeSMarcel Moolenaar rc = xo_do_close_instance(xop, NULL);
7572545ddfbeSMarcel Moolenaar break;
7573545ddfbeSMarcel Moolenaar
7574545ddfbeSMarcel Moolenaar case XSS_OPEN_LEAF_LIST:
7575545ddfbeSMarcel Moolenaar rc = xo_do_close_leaf_list(xop, NULL);
7576545ddfbeSMarcel Moolenaar break;
7577545ddfbeSMarcel Moolenaar
7578545ddfbeSMarcel Moolenaar case XSS_MARKER:
7579545ddfbeSMarcel Moolenaar flags = xsp->xs_flags & XSF_MARKER_FLAGS;
7580545ddfbeSMarcel Moolenaar xo_depth_change(xop, xsp->xs_name, -1, 0, XSS_MARKER, 0);
7581545ddfbeSMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags |= flags;
7582545ddfbeSMarcel Moolenaar rc = 0;
7583545ddfbeSMarcel Moolenaar break;
7584545ddfbeSMarcel Moolenaar }
7585545ddfbeSMarcel Moolenaar
7586545ddfbeSMarcel Moolenaar if (rc < 0)
7587545ddfbeSMarcel Moolenaar xo_failure(xop, "close %d failed: %d", xsp->xs_state, rc);
7588545ddfbeSMarcel Moolenaar }
7589545ddfbeSMarcel Moolenaar
7590545ddfbeSMarcel Moolenaar return 0;
7591545ddfbeSMarcel Moolenaar }
7592545ddfbeSMarcel Moolenaar
7593545ddfbeSMarcel Moolenaar /*
7594545ddfbeSMarcel Moolenaar * This function is responsible for clearing out whatever is needed
7595545ddfbeSMarcel Moolenaar * to get to the desired state, if possible.
7596545ddfbeSMarcel Moolenaar */
7597545ddfbeSMarcel Moolenaar static int
xo_do_close(xo_handle_t * xop,const char * name,xo_state_t new_state)7598545ddfbeSMarcel Moolenaar xo_do_close (xo_handle_t *xop, const char *name, xo_state_t new_state)
7599545ddfbeSMarcel Moolenaar {
7600545ddfbeSMarcel Moolenaar xo_stack_t *xsp, *limit = NULL;
76018a6eceffSPhil Shafer ssize_t rc;
7602545ddfbeSMarcel Moolenaar xo_state_t need_state = new_state;
7603545ddfbeSMarcel Moolenaar
7604545ddfbeSMarcel Moolenaar if (new_state == XSS_CLOSE_CONTAINER)
7605545ddfbeSMarcel Moolenaar need_state = XSS_OPEN_CONTAINER;
7606545ddfbeSMarcel Moolenaar else if (new_state == XSS_CLOSE_LIST)
7607545ddfbeSMarcel Moolenaar need_state = XSS_OPEN_LIST;
7608545ddfbeSMarcel Moolenaar else if (new_state == XSS_CLOSE_INSTANCE)
7609545ddfbeSMarcel Moolenaar need_state = XSS_OPEN_INSTANCE;
7610545ddfbeSMarcel Moolenaar else if (new_state == XSS_CLOSE_LEAF_LIST)
7611545ddfbeSMarcel Moolenaar need_state = XSS_OPEN_LEAF_LIST;
7612545ddfbeSMarcel Moolenaar else if (new_state == XSS_MARKER)
7613545ddfbeSMarcel Moolenaar need_state = XSS_MARKER;
7614545ddfbeSMarcel Moolenaar else
7615545ddfbeSMarcel Moolenaar return 0; /* Unknown or useless new states are ignored */
7616545ddfbeSMarcel Moolenaar
7617545ddfbeSMarcel Moolenaar for (xsp = &xop->xo_stack[xop->xo_depth]; xsp > xop->xo_stack; xsp--) {
7618545ddfbeSMarcel Moolenaar /*
7619545ddfbeSMarcel Moolenaar * Marker's normally stop us from going any further, unless
7620545ddfbeSMarcel Moolenaar * we are popping a marker (new_state == XSS_MARKER).
7621545ddfbeSMarcel Moolenaar */
7622545ddfbeSMarcel Moolenaar if (xsp->xs_state == XSS_MARKER && need_state != XSS_MARKER) {
7623545ddfbeSMarcel Moolenaar if (name) {
7624545ddfbeSMarcel Moolenaar xo_failure(xop, "close (xo_%s) fails at marker '%s'; "
7625545ddfbeSMarcel Moolenaar "not found '%s'",
7626545ddfbeSMarcel Moolenaar xo_state_name(new_state),
7627545ddfbeSMarcel Moolenaar xsp->xs_name, name);
7628545ddfbeSMarcel Moolenaar return 0;
7629545ddfbeSMarcel Moolenaar
7630545ddfbeSMarcel Moolenaar } else {
7631545ddfbeSMarcel Moolenaar limit = xsp;
7632545ddfbeSMarcel Moolenaar xo_failure(xop, "close stops at marker '%s'", xsp->xs_name);
7633545ddfbeSMarcel Moolenaar }
7634545ddfbeSMarcel Moolenaar break;
7635545ddfbeSMarcel Moolenaar }
7636545ddfbeSMarcel Moolenaar
7637545ddfbeSMarcel Moolenaar if (xsp->xs_state != need_state)
7638545ddfbeSMarcel Moolenaar continue;
7639545ddfbeSMarcel Moolenaar
764076afb20cSPhil Shafer if (name && xsp->xs_name && !xo_streq(name, xsp->xs_name))
7641545ddfbeSMarcel Moolenaar continue;
7642545ddfbeSMarcel Moolenaar
7643545ddfbeSMarcel Moolenaar limit = xsp;
7644545ddfbeSMarcel Moolenaar break;
7645545ddfbeSMarcel Moolenaar }
7646545ddfbeSMarcel Moolenaar
7647545ddfbeSMarcel Moolenaar if (limit == NULL) {
7648545ddfbeSMarcel Moolenaar xo_failure(xop, "xo_%s can't find match for '%s'",
7649545ddfbeSMarcel Moolenaar xo_state_name(new_state), name);
7650545ddfbeSMarcel Moolenaar return 0;
7651545ddfbeSMarcel Moolenaar }
7652545ddfbeSMarcel Moolenaar
7653545ddfbeSMarcel Moolenaar rc = xo_do_close_all(xop, limit);
7654545ddfbeSMarcel Moolenaar
7655545ddfbeSMarcel Moolenaar return rc;
7656545ddfbeSMarcel Moolenaar }
7657545ddfbeSMarcel Moolenaar
7658545ddfbeSMarcel Moolenaar /*
7659545ddfbeSMarcel Moolenaar * We are in a given state and need to transition to the new state.
7660545ddfbeSMarcel Moolenaar */
76618a6eceffSPhil Shafer static ssize_t
xo_transition(xo_handle_t * xop,xo_xof_flags_t flags,const char * name,xo_state_t new_state)7662406a584dSPhil Shafer xo_transition (xo_handle_t *xop, xo_xof_flags_t flags, const char *name,
7663545ddfbeSMarcel Moolenaar xo_state_t new_state)
7664545ddfbeSMarcel Moolenaar {
7665545ddfbeSMarcel Moolenaar xo_stack_t *xsp;
76668a6eceffSPhil Shafer ssize_t rc = 0;
7667545ddfbeSMarcel Moolenaar int old_state, on_marker;
7668545ddfbeSMarcel Moolenaar
7669545ddfbeSMarcel Moolenaar xop = xo_default(xop);
7670545ddfbeSMarcel Moolenaar
7671545ddfbeSMarcel Moolenaar xsp = &xop->xo_stack[xop->xo_depth];
7672545ddfbeSMarcel Moolenaar old_state = xsp->xs_state;
7673545ddfbeSMarcel Moolenaar on_marker = (old_state == XSS_MARKER);
7674545ddfbeSMarcel Moolenaar
7675545ddfbeSMarcel Moolenaar /* If there's a marker on top of the stack, we need to find a real state */
7676545ddfbeSMarcel Moolenaar while (old_state == XSS_MARKER) {
7677545ddfbeSMarcel Moolenaar if (xsp == xop->xo_stack)
7678545ddfbeSMarcel Moolenaar break;
7679545ddfbeSMarcel Moolenaar xsp -= 1;
7680545ddfbeSMarcel Moolenaar old_state = xsp->xs_state;
7681545ddfbeSMarcel Moolenaar }
7682545ddfbeSMarcel Moolenaar
7683545ddfbeSMarcel Moolenaar /*
7684545ddfbeSMarcel Moolenaar * At this point, the list of possible states are:
7685545ddfbeSMarcel Moolenaar * XSS_INIT, XSS_OPEN_CONTAINER, XSS_OPEN_LIST,
7686545ddfbeSMarcel Moolenaar * XSS_OPEN_INSTANCE, XSS_OPEN_LEAF_LIST, XSS_DISCARDING
7687545ddfbeSMarcel Moolenaar */
7688545ddfbeSMarcel Moolenaar switch (XSS_TRANSITION(old_state, new_state)) {
7689545ddfbeSMarcel Moolenaar
7690545ddfbeSMarcel Moolenaar open_container:
7691545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_INIT, XSS_OPEN_CONTAINER):
7692545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_OPEN_CONTAINER):
7693545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_OPEN_CONTAINER):
7694545ddfbeSMarcel Moolenaar rc = xo_do_open_container(xop, flags, name);
7695545ddfbeSMarcel Moolenaar break;
7696545ddfbeSMarcel Moolenaar
7697545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LIST, XSS_OPEN_CONTAINER):
7698545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_OPEN_CONTAINER):
7699545ddfbeSMarcel Moolenaar if (on_marker)
7700545ddfbeSMarcel Moolenaar goto marker_prevents_close;
7701545ddfbeSMarcel Moolenaar rc = xo_do_close_leaf_list(xop, NULL);
7702545ddfbeSMarcel Moolenaar if (rc >= 0)
7703545ddfbeSMarcel Moolenaar goto open_container;
7704545ddfbeSMarcel Moolenaar break;
7705545ddfbeSMarcel Moolenaar
7706545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_INIT, XSS_CLOSE_CONTAINER):
7707545ddfbeSMarcel Moolenaar /* This is an exception for "xo --close" */
7708545ddfbeSMarcel Moolenaar rc = xo_do_close_container(xop, name);
7709545ddfbeSMarcel Moolenaar break;
7710545ddfbeSMarcel Moolenaar
771176afb20cSPhil Shafer /*close_container:*/
771276afb20cSPhil Shafer case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_CLOSE_CONTAINER):
7713545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LIST, XSS_CLOSE_CONTAINER):
7714545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_CLOSE_CONTAINER):
7715545ddfbeSMarcel Moolenaar if (on_marker)
7716545ddfbeSMarcel Moolenaar goto marker_prevents_close;
7717545ddfbeSMarcel Moolenaar rc = xo_do_close(xop, name, new_state);
7718545ddfbeSMarcel Moolenaar break;
7719545ddfbeSMarcel Moolenaar
7720545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_CLOSE_CONTAINER):
7721545ddfbeSMarcel Moolenaar if (on_marker)
7722545ddfbeSMarcel Moolenaar goto marker_prevents_close;
7723545ddfbeSMarcel Moolenaar rc = xo_do_close_leaf_list(xop, NULL);
7724545ddfbeSMarcel Moolenaar if (rc >= 0)
7725545ddfbeSMarcel Moolenaar rc = xo_do_close(xop, name, new_state);
7726545ddfbeSMarcel Moolenaar break;
7727545ddfbeSMarcel Moolenaar
7728545ddfbeSMarcel Moolenaar open_list:
7729545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_INIT, XSS_OPEN_LIST):
7730545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_OPEN_LIST):
7731545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_OPEN_LIST):
7732545ddfbeSMarcel Moolenaar rc = xo_do_open_list(xop, flags, name);
7733545ddfbeSMarcel Moolenaar break;
7734545ddfbeSMarcel Moolenaar
7735545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LIST, XSS_OPEN_LIST):
7736545ddfbeSMarcel Moolenaar if (on_marker)
7737545ddfbeSMarcel Moolenaar goto marker_prevents_close;
7738545ddfbeSMarcel Moolenaar rc = xo_do_close_list(xop, NULL);
7739545ddfbeSMarcel Moolenaar if (rc >= 0)
7740545ddfbeSMarcel Moolenaar goto open_list;
7741545ddfbeSMarcel Moolenaar break;
7742545ddfbeSMarcel Moolenaar
7743545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_OPEN_LIST):
7744545ddfbeSMarcel Moolenaar if (on_marker)
7745545ddfbeSMarcel Moolenaar goto marker_prevents_close;
7746545ddfbeSMarcel Moolenaar rc = xo_do_close_leaf_list(xop, NULL);
7747545ddfbeSMarcel Moolenaar if (rc >= 0)
7748545ddfbeSMarcel Moolenaar goto open_list;
7749545ddfbeSMarcel Moolenaar break;
7750545ddfbeSMarcel Moolenaar
7751545ddfbeSMarcel Moolenaar /*close_list:*/
7752545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LIST, XSS_CLOSE_LIST):
7753545ddfbeSMarcel Moolenaar if (on_marker)
7754545ddfbeSMarcel Moolenaar goto marker_prevents_close;
7755545ddfbeSMarcel Moolenaar rc = xo_do_close(xop, name, new_state);
7756545ddfbeSMarcel Moolenaar break;
7757545ddfbeSMarcel Moolenaar
7758545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_INIT, XSS_CLOSE_LIST):
7759545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_CLOSE_LIST):
7760545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_CLOSE_LIST):
7761545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_CLOSE_LIST):
7762545ddfbeSMarcel Moolenaar rc = xo_do_close(xop, name, new_state);
7763545ddfbeSMarcel Moolenaar break;
7764545ddfbeSMarcel Moolenaar
7765545ddfbeSMarcel Moolenaar open_instance:
7766545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LIST, XSS_OPEN_INSTANCE):
7767545ddfbeSMarcel Moolenaar rc = xo_do_open_instance(xop, flags, name);
7768545ddfbeSMarcel Moolenaar break;
7769545ddfbeSMarcel Moolenaar
7770545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_INIT, XSS_OPEN_INSTANCE):
7771788ca347SMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_OPEN_INSTANCE):
7772545ddfbeSMarcel Moolenaar rc = xo_do_open_list(xop, flags, name);
7773545ddfbeSMarcel Moolenaar if (rc >= 0)
7774545ddfbeSMarcel Moolenaar goto open_instance;
7775545ddfbeSMarcel Moolenaar break;
7776545ddfbeSMarcel Moolenaar
7777545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_OPEN_INSTANCE):
7778545ddfbeSMarcel Moolenaar if (on_marker) {
7779545ddfbeSMarcel Moolenaar rc = xo_do_open_list(xop, flags, name);
7780545ddfbeSMarcel Moolenaar } else {
7781545ddfbeSMarcel Moolenaar rc = xo_do_close_instance(xop, NULL);
7782545ddfbeSMarcel Moolenaar }
7783545ddfbeSMarcel Moolenaar if (rc >= 0)
7784545ddfbeSMarcel Moolenaar goto open_instance;
7785545ddfbeSMarcel Moolenaar break;
7786545ddfbeSMarcel Moolenaar
7787545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_OPEN_INSTANCE):
7788545ddfbeSMarcel Moolenaar if (on_marker)
7789545ddfbeSMarcel Moolenaar goto marker_prevents_close;
7790545ddfbeSMarcel Moolenaar rc = xo_do_close_leaf_list(xop, NULL);
7791545ddfbeSMarcel Moolenaar if (rc >= 0)
7792545ddfbeSMarcel Moolenaar goto open_instance;
7793545ddfbeSMarcel Moolenaar break;
7794545ddfbeSMarcel Moolenaar
7795545ddfbeSMarcel Moolenaar /*close_instance:*/
7796545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_CLOSE_INSTANCE):
7797545ddfbeSMarcel Moolenaar if (on_marker)
7798545ddfbeSMarcel Moolenaar goto marker_prevents_close;
7799545ddfbeSMarcel Moolenaar rc = xo_do_close_instance(xop, name);
7800545ddfbeSMarcel Moolenaar break;
7801545ddfbeSMarcel Moolenaar
7802545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_INIT, XSS_CLOSE_INSTANCE):
7803545ddfbeSMarcel Moolenaar /* This one makes no sense; ignore it */
7804788ca347SMarcel Moolenaar xo_failure(xop, "xo_close_instance ignored when called from "
7805788ca347SMarcel Moolenaar "initial state ('%s')", name ?: "(unknown)");
7806545ddfbeSMarcel Moolenaar break;
7807545ddfbeSMarcel Moolenaar
7808545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_CLOSE_INSTANCE):
7809545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LIST, XSS_CLOSE_INSTANCE):
7810545ddfbeSMarcel Moolenaar if (on_marker)
7811545ddfbeSMarcel Moolenaar goto marker_prevents_close;
7812545ddfbeSMarcel Moolenaar rc = xo_do_close(xop, name, new_state);
7813545ddfbeSMarcel Moolenaar break;
7814545ddfbeSMarcel Moolenaar
7815545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_CLOSE_INSTANCE):
7816545ddfbeSMarcel Moolenaar if (on_marker)
7817545ddfbeSMarcel Moolenaar goto marker_prevents_close;
7818545ddfbeSMarcel Moolenaar rc = xo_do_close_leaf_list(xop, NULL);
7819545ddfbeSMarcel Moolenaar if (rc >= 0)
7820545ddfbeSMarcel Moolenaar rc = xo_do_close(xop, name, new_state);
7821545ddfbeSMarcel Moolenaar break;
7822545ddfbeSMarcel Moolenaar
7823545ddfbeSMarcel Moolenaar open_leaf_list:
7824545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_OPEN_LEAF_LIST):
7825545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_OPEN_LEAF_LIST):
7826545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_INIT, XSS_OPEN_LEAF_LIST):
7827545ddfbeSMarcel Moolenaar rc = xo_do_open_leaf_list(xop, flags, name);
7828545ddfbeSMarcel Moolenaar break;
7829545ddfbeSMarcel Moolenaar
7830545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LIST, XSS_OPEN_LEAF_LIST):
7831545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_OPEN_LEAF_LIST):
7832545ddfbeSMarcel Moolenaar if (on_marker)
7833545ddfbeSMarcel Moolenaar goto marker_prevents_close;
7834545ddfbeSMarcel Moolenaar rc = xo_do_close_list(xop, NULL);
7835545ddfbeSMarcel Moolenaar if (rc >= 0)
7836545ddfbeSMarcel Moolenaar goto open_leaf_list;
7837545ddfbeSMarcel Moolenaar break;
7838545ddfbeSMarcel Moolenaar
7839545ddfbeSMarcel Moolenaar /*close_leaf_list:*/
7840545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_CLOSE_LEAF_LIST):
7841545ddfbeSMarcel Moolenaar if (on_marker)
7842545ddfbeSMarcel Moolenaar goto marker_prevents_close;
7843545ddfbeSMarcel Moolenaar rc = xo_do_close_leaf_list(xop, name);
7844545ddfbeSMarcel Moolenaar break;
7845545ddfbeSMarcel Moolenaar
7846545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_INIT, XSS_CLOSE_LEAF_LIST):
7847545ddfbeSMarcel Moolenaar /* Makes no sense; ignore */
7848788ca347SMarcel Moolenaar xo_failure(xop, "xo_close_leaf_list ignored when called from "
7849788ca347SMarcel Moolenaar "initial state ('%s')", name ?: "(unknown)");
7850545ddfbeSMarcel Moolenaar break;
7851545ddfbeSMarcel Moolenaar
7852545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_CLOSE_LEAF_LIST):
7853545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LIST, XSS_CLOSE_LEAF_LIST):
7854545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_CLOSE_LEAF_LIST):
7855545ddfbeSMarcel Moolenaar if (on_marker)
7856545ddfbeSMarcel Moolenaar goto marker_prevents_close;
7857545ddfbeSMarcel Moolenaar rc = xo_do_close(xop, name, new_state);
7858545ddfbeSMarcel Moolenaar break;
7859545ddfbeSMarcel Moolenaar
7860545ddfbeSMarcel Moolenaar /*emit:*/
7861545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_EMIT):
7862545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_EMIT):
7863545ddfbeSMarcel Moolenaar break;
7864545ddfbeSMarcel Moolenaar
7865545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LIST, XSS_EMIT):
7866545ddfbeSMarcel Moolenaar if (on_marker)
7867545ddfbeSMarcel Moolenaar goto marker_prevents_close;
7868545ddfbeSMarcel Moolenaar rc = xo_do_close(xop, NULL, XSS_CLOSE_LIST);
7869545ddfbeSMarcel Moolenaar break;
7870545ddfbeSMarcel Moolenaar
7871545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_INIT, XSS_EMIT):
7872545ddfbeSMarcel Moolenaar break;
7873545ddfbeSMarcel Moolenaar
7874545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_EMIT):
7875545ddfbeSMarcel Moolenaar if (on_marker)
7876545ddfbeSMarcel Moolenaar goto marker_prevents_close;
7877545ddfbeSMarcel Moolenaar rc = xo_do_close_leaf_list(xop, NULL);
7878545ddfbeSMarcel Moolenaar break;
7879545ddfbeSMarcel Moolenaar
7880545ddfbeSMarcel Moolenaar /*emit_leaf_list:*/
7881545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_INIT, XSS_EMIT_LEAF_LIST):
7882545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_CONTAINER, XSS_EMIT_LEAF_LIST):
7883545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_INSTANCE, XSS_EMIT_LEAF_LIST):
7884545ddfbeSMarcel Moolenaar rc = xo_do_open_leaf_list(xop, flags, name);
7885545ddfbeSMarcel Moolenaar break;
7886545ddfbeSMarcel Moolenaar
7887545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LEAF_LIST, XSS_EMIT_LEAF_LIST):
7888545ddfbeSMarcel Moolenaar break;
7889545ddfbeSMarcel Moolenaar
7890545ddfbeSMarcel Moolenaar case XSS_TRANSITION(XSS_OPEN_LIST, XSS_EMIT_LEAF_LIST):
7891545ddfbeSMarcel Moolenaar /*
7892545ddfbeSMarcel Moolenaar * We need to be backward compatible with the pre-xo_open_leaf_list
7893545ddfbeSMarcel Moolenaar * API, where both lists and leaf-lists were opened as lists. So
7894545ddfbeSMarcel Moolenaar * if we find an open list that hasn't had anything written to it,
7895545ddfbeSMarcel Moolenaar * we'll accept it.
7896545ddfbeSMarcel Moolenaar */
7897545ddfbeSMarcel Moolenaar break;
7898545ddfbeSMarcel Moolenaar
7899545ddfbeSMarcel Moolenaar default:
7900545ddfbeSMarcel Moolenaar xo_failure(xop, "unknown transition: (%u -> %u)",
7901545ddfbeSMarcel Moolenaar xsp->xs_state, new_state);
7902545ddfbeSMarcel Moolenaar }
7903545ddfbeSMarcel Moolenaar
790442ff34c3SPhil Shafer /* Handle the flush flag */
790542ff34c3SPhil Shafer if (rc >= 0 && XOF_ISSET(xop, XOF_FLUSH))
7906406a584dSPhil Shafer if (xo_flush_h(xop) < 0)
790742ff34c3SPhil Shafer rc = -1;
790842ff34c3SPhil Shafer
7909406a584dSPhil Shafer /* We have now official made output */
7910406a584dSPhil Shafer XOIF_SET(xop, XOIF_MADE_OUTPUT);
7911406a584dSPhil Shafer
7912545ddfbeSMarcel Moolenaar return rc;
7913545ddfbeSMarcel Moolenaar
7914545ddfbeSMarcel Moolenaar marker_prevents_close:
7915545ddfbeSMarcel Moolenaar xo_failure(xop, "marker '%s' prevents transition from %s to %s",
7916545ddfbeSMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_name,
7917545ddfbeSMarcel Moolenaar xo_state_name(old_state), xo_state_name(new_state));
7918545ddfbeSMarcel Moolenaar return -1;
7919545ddfbeSMarcel Moolenaar }
7920545ddfbeSMarcel Moolenaar
79218a6eceffSPhil Shafer xo_ssize_t
xo_open_marker_h(xo_handle_t * xop,const char * name)7922545ddfbeSMarcel Moolenaar xo_open_marker_h (xo_handle_t *xop, const char *name)
7923545ddfbeSMarcel Moolenaar {
7924545ddfbeSMarcel Moolenaar xop = xo_default(xop);
7925545ddfbeSMarcel Moolenaar
7926545ddfbeSMarcel Moolenaar xo_depth_change(xop, name, 1, 0, XSS_MARKER,
7927545ddfbeSMarcel Moolenaar xop->xo_stack[xop->xo_depth].xs_flags & XSF_MARKER_FLAGS);
7928545ddfbeSMarcel Moolenaar
7929545ddfbeSMarcel Moolenaar return 0;
7930545ddfbeSMarcel Moolenaar }
7931545ddfbeSMarcel Moolenaar
79328a6eceffSPhil Shafer xo_ssize_t
xo_open_marker(const char * name)7933545ddfbeSMarcel Moolenaar xo_open_marker (const char *name)
7934545ddfbeSMarcel Moolenaar {
7935545ddfbeSMarcel Moolenaar return xo_open_marker_h(NULL, name);
7936545ddfbeSMarcel Moolenaar }
7937545ddfbeSMarcel Moolenaar
79388a6eceffSPhil Shafer xo_ssize_t
xo_close_marker_h(xo_handle_t * xop,const char * name)7939545ddfbeSMarcel Moolenaar xo_close_marker_h (xo_handle_t *xop, const char *name)
7940545ddfbeSMarcel Moolenaar {
7941545ddfbeSMarcel Moolenaar xop = xo_default(xop);
7942545ddfbeSMarcel Moolenaar
7943545ddfbeSMarcel Moolenaar return xo_do_close(xop, name, XSS_MARKER);
7944545ddfbeSMarcel Moolenaar }
7945545ddfbeSMarcel Moolenaar
79468a6eceffSPhil Shafer xo_ssize_t
xo_close_marker(const char * name)7947545ddfbeSMarcel Moolenaar xo_close_marker (const char *name)
7948545ddfbeSMarcel Moolenaar {
7949545ddfbeSMarcel Moolenaar return xo_close_marker_h(NULL, name);
7950545ddfbeSMarcel Moolenaar }
7951545ddfbeSMarcel Moolenaar
7952d1a0d267SMarcel Moolenaar /*
7953d1a0d267SMarcel Moolenaar * Record custom output functions into the xo handle, allowing
7954d1a0d267SMarcel Moolenaar * integration with a variety of output frameworks.
7955d1a0d267SMarcel Moolenaar */
795631337658SMarcel Moolenaar void
xo_set_writer(xo_handle_t * xop,void * opaque,xo_write_func_t write_func,xo_close_func_t close_func,xo_flush_func_t flush_func)795731337658SMarcel Moolenaar xo_set_writer (xo_handle_t *xop, void *opaque, xo_write_func_t write_func,
7958545ddfbeSMarcel Moolenaar xo_close_func_t close_func, xo_flush_func_t flush_func)
795931337658SMarcel Moolenaar {
796031337658SMarcel Moolenaar xop = xo_default(xop);
796131337658SMarcel Moolenaar
796231337658SMarcel Moolenaar xop->xo_opaque = opaque;
796331337658SMarcel Moolenaar xop->xo_write = write_func;
796431337658SMarcel Moolenaar xop->xo_close = close_func;
7965545ddfbeSMarcel Moolenaar xop->xo_flush = flush_func;
796631337658SMarcel Moolenaar }
796731337658SMarcel Moolenaar
796831337658SMarcel Moolenaar void
xo_set_allocator(xo_realloc_func_t realloc_func,xo_free_func_t free_func)796931337658SMarcel Moolenaar xo_set_allocator (xo_realloc_func_t realloc_func, xo_free_func_t free_func)
797031337658SMarcel Moolenaar {
797131337658SMarcel Moolenaar xo_realloc = realloc_func;
797231337658SMarcel Moolenaar xo_free = free_func;
797331337658SMarcel Moolenaar }
797431337658SMarcel Moolenaar
79758a6eceffSPhil Shafer xo_ssize_t
xo_flush_h(xo_handle_t * xop)797631337658SMarcel Moolenaar xo_flush_h (xo_handle_t *xop)
797731337658SMarcel Moolenaar {
79788a6eceffSPhil Shafer ssize_t rc;
797931337658SMarcel Moolenaar
798031337658SMarcel Moolenaar xop = xo_default(xop);
798131337658SMarcel Moolenaar
7982788ca347SMarcel Moolenaar switch (xo_style(xop)) {
7983d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER:
7984f2b7bf8aSPhil Shafer xo_encoder_handle(xop, XO_OP_FLUSH, NULL, NULL, 0);
798531337658SMarcel Moolenaar }
798631337658SMarcel Moolenaar
7987545ddfbeSMarcel Moolenaar rc = xo_write(xop);
7988545ddfbeSMarcel Moolenaar if (rc >= 0 && xop->xo_flush)
7989545ddfbeSMarcel Moolenaar if (xop->xo_flush(xop->xo_opaque) < 0)
7990545ddfbeSMarcel Moolenaar return -1;
7991545ddfbeSMarcel Moolenaar
7992545ddfbeSMarcel Moolenaar return rc;
799331337658SMarcel Moolenaar }
799431337658SMarcel Moolenaar
79958a6eceffSPhil Shafer xo_ssize_t
xo_flush(void)799631337658SMarcel Moolenaar xo_flush (void)
799731337658SMarcel Moolenaar {
7998545ddfbeSMarcel Moolenaar return xo_flush_h(NULL);
799931337658SMarcel Moolenaar }
800031337658SMarcel Moolenaar
80018a6eceffSPhil Shafer xo_ssize_t
xo_finish_h(xo_handle_t * xop)800231337658SMarcel Moolenaar xo_finish_h (xo_handle_t *xop)
800331337658SMarcel Moolenaar {
8004406a584dSPhil Shafer const char *open_if_empty = "";
800531337658SMarcel Moolenaar xop = xo_default(xop);
800631337658SMarcel Moolenaar
8007d1a0d267SMarcel Moolenaar if (!XOF_ISSET(xop, XOF_NO_CLOSE))
8008545ddfbeSMarcel Moolenaar xo_do_close_all(xop, xop->xo_stack);
8009545ddfbeSMarcel Moolenaar
8010788ca347SMarcel Moolenaar switch (xo_style(xop)) {
801131337658SMarcel Moolenaar case XO_STYLE_JSON:
8012d1a0d267SMarcel Moolenaar if (!XOF_ISSET(xop, XOF_NO_TOP)) {
8013406a584dSPhil Shafer const char *pre_nl = XOF_ISSET(xop, XOF_PRETTY) ? "\n" : "";
8014406a584dSPhil Shafer
8015d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_TOP_EMITTED))
8016d1a0d267SMarcel Moolenaar XOIF_CLEAR(xop, XOIF_TOP_EMITTED); /* Turn off before output */
8017406a584dSPhil Shafer else if (!XOIF_ISSET(xop, XOIF_MADE_OUTPUT)) {
8018406a584dSPhil Shafer open_if_empty = "{ ";
8019406a584dSPhil Shafer pre_nl = "";
8020406a584dSPhil Shafer }
8021406a584dSPhil Shafer
8022406a584dSPhil Shafer xo_printf(xop, "%s%*s%s}\n",
8023406a584dSPhil Shafer pre_nl, xo_indent(xop), "", open_if_empty);
802431337658SMarcel Moolenaar }
802531337658SMarcel Moolenaar break;
8026d1a0d267SMarcel Moolenaar
8027d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER:
8028f2b7bf8aSPhil Shafer xo_encoder_handle(xop, XO_OP_FINISH, NULL, NULL, 0);
8029d1a0d267SMarcel Moolenaar break;
803031337658SMarcel Moolenaar }
803131337658SMarcel Moolenaar
8032545ddfbeSMarcel Moolenaar return xo_flush_h(xop);
803331337658SMarcel Moolenaar }
803431337658SMarcel Moolenaar
80358a6eceffSPhil Shafer xo_ssize_t
xo_finish(void)803631337658SMarcel Moolenaar xo_finish (void)
803731337658SMarcel Moolenaar {
8038545ddfbeSMarcel Moolenaar return xo_finish_h(NULL);
803931337658SMarcel Moolenaar }
804031337658SMarcel Moolenaar
804131337658SMarcel Moolenaar /*
8042d1a0d267SMarcel Moolenaar * xo_finish_atexit is suitable for atexit() calls, to force clear up
8043d1a0d267SMarcel Moolenaar * and finalizing output.
8044d1a0d267SMarcel Moolenaar */
8045d1a0d267SMarcel Moolenaar void
xo_finish_atexit(void)8046d1a0d267SMarcel Moolenaar xo_finish_atexit (void)
8047d1a0d267SMarcel Moolenaar {
8048d1a0d267SMarcel Moolenaar (void) xo_finish_h(NULL);
8049d1a0d267SMarcel Moolenaar }
8050d1a0d267SMarcel Moolenaar
8051d1a0d267SMarcel Moolenaar /*
805231337658SMarcel Moolenaar * Generate an error message, such as would be displayed on stderr
805331337658SMarcel Moolenaar */
805431337658SMarcel Moolenaar void
xo_errorn_hv(xo_handle_t * xop,int need_newline,const char * fmt,va_list vap)80555c5819b2SPhil Shafer xo_errorn_hv (xo_handle_t *xop, int need_newline, const char *fmt, va_list vap)
805631337658SMarcel Moolenaar {
805731337658SMarcel Moolenaar xop = xo_default(xop);
805831337658SMarcel Moolenaar
805931337658SMarcel Moolenaar /*
806031337658SMarcel Moolenaar * If the format string doesn't end with a newline, we pop
806131337658SMarcel Moolenaar * one on ourselves.
806231337658SMarcel Moolenaar */
80635c5819b2SPhil Shafer if (need_newline) {
80648a6eceffSPhil Shafer ssize_t len = strlen(fmt);
806531337658SMarcel Moolenaar if (len > 0 && fmt[len - 1] != '\n') {
806631337658SMarcel Moolenaar char *newfmt = alloca(len + 2);
806731337658SMarcel Moolenaar memcpy(newfmt, fmt, len);
806831337658SMarcel Moolenaar newfmt[len] = '\n';
806976afb20cSPhil Shafer newfmt[len + 1] = '\0';
807031337658SMarcel Moolenaar fmt = newfmt;
807131337658SMarcel Moolenaar }
80725c5819b2SPhil Shafer }
807331337658SMarcel Moolenaar
8074788ca347SMarcel Moolenaar switch (xo_style(xop)) {
807531337658SMarcel Moolenaar case XO_STYLE_TEXT:
807631337658SMarcel Moolenaar vfprintf(stderr, fmt, vap);
807731337658SMarcel Moolenaar break;
807831337658SMarcel Moolenaar
807931337658SMarcel Moolenaar case XO_STYLE_HTML:
808031337658SMarcel Moolenaar va_copy(xop->xo_vap, vap);
808131337658SMarcel Moolenaar
8082264104f2SPhil Shafer xo_buf_append_div(xop, "error", 0, NULL, 0, NULL, 0,
8083264104f2SPhil Shafer fmt, strlen(fmt), NULL, 0);
808431337658SMarcel Moolenaar
8085d1a0d267SMarcel Moolenaar if (XOIF_ISSET(xop, XOIF_DIV_OPEN))
808631337658SMarcel Moolenaar xo_line_close(xop);
808731337658SMarcel Moolenaar
808831337658SMarcel Moolenaar xo_write(xop);
808931337658SMarcel Moolenaar
809031337658SMarcel Moolenaar va_end(xop->xo_vap);
809131337658SMarcel Moolenaar bzero(&xop->xo_vap, sizeof(xop->xo_vap));
809231337658SMarcel Moolenaar break;
809331337658SMarcel Moolenaar
809431337658SMarcel Moolenaar case XO_STYLE_XML:
8095545ddfbeSMarcel Moolenaar case XO_STYLE_JSON:
809631337658SMarcel Moolenaar va_copy(xop->xo_vap, vap);
809731337658SMarcel Moolenaar
809831337658SMarcel Moolenaar xo_open_container_h(xop, "error");
8099264104f2SPhil Shafer xo_format_value(xop, "message", 7, NULL, 0,
8100264104f2SPhil Shafer fmt, strlen(fmt), NULL, 0, 0);
810131337658SMarcel Moolenaar xo_close_container_h(xop, "error");
810231337658SMarcel Moolenaar
810331337658SMarcel Moolenaar va_end(xop->xo_vap);
810431337658SMarcel Moolenaar bzero(&xop->xo_vap, sizeof(xop->xo_vap));
810531337658SMarcel Moolenaar break;
8106d1a0d267SMarcel Moolenaar
8107d1a0d267SMarcel Moolenaar case XO_STYLE_SDPARAMS:
8108d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER:
8109d1a0d267SMarcel Moolenaar break;
811031337658SMarcel Moolenaar }
811131337658SMarcel Moolenaar }
811231337658SMarcel Moolenaar
811331337658SMarcel Moolenaar void
xo_error_h(xo_handle_t * xop,const char * fmt,...)811431337658SMarcel Moolenaar xo_error_h (xo_handle_t *xop, const char *fmt, ...)
811531337658SMarcel Moolenaar {
811631337658SMarcel Moolenaar va_list vap;
811731337658SMarcel Moolenaar
811831337658SMarcel Moolenaar va_start(vap, fmt);
81195c5819b2SPhil Shafer xo_errorn_hv(xop, 0, fmt, vap);
812031337658SMarcel Moolenaar va_end(vap);
812131337658SMarcel Moolenaar }
812231337658SMarcel Moolenaar
812331337658SMarcel Moolenaar /*
812431337658SMarcel Moolenaar * Generate an error message, such as would be displayed on stderr
812531337658SMarcel Moolenaar */
812631337658SMarcel Moolenaar void
xo_error(const char * fmt,...)812731337658SMarcel Moolenaar xo_error (const char *fmt, ...)
812831337658SMarcel Moolenaar {
812931337658SMarcel Moolenaar va_list vap;
813031337658SMarcel Moolenaar
813131337658SMarcel Moolenaar va_start(vap, fmt);
81325c5819b2SPhil Shafer xo_errorn_hv(NULL, 0, fmt, vap);
81335c5819b2SPhil Shafer va_end(vap);
81345c5819b2SPhil Shafer }
81355c5819b2SPhil Shafer
81365c5819b2SPhil Shafer void
xo_errorn_h(xo_handle_t * xop,const char * fmt,...)81375c5819b2SPhil Shafer xo_errorn_h (xo_handle_t *xop, const char *fmt, ...)
81385c5819b2SPhil Shafer {
81395c5819b2SPhil Shafer va_list vap;
81405c5819b2SPhil Shafer
81415c5819b2SPhil Shafer va_start(vap, fmt);
81425c5819b2SPhil Shafer xo_errorn_hv(xop, 1, fmt, vap);
81435c5819b2SPhil Shafer va_end(vap);
81445c5819b2SPhil Shafer }
81455c5819b2SPhil Shafer
81465c5819b2SPhil Shafer /*
81475c5819b2SPhil Shafer * Generate an error message, such as would be displayed on stderr
81485c5819b2SPhil Shafer */
81495c5819b2SPhil Shafer void
xo_errorn(const char * fmt,...)81505c5819b2SPhil Shafer xo_errorn (const char *fmt, ...)
81515c5819b2SPhil Shafer {
81525c5819b2SPhil Shafer va_list vap;
81535c5819b2SPhil Shafer
81545c5819b2SPhil Shafer va_start(vap, fmt);
81555c5819b2SPhil Shafer xo_errorn_hv(NULL, 1, fmt, vap);
815631337658SMarcel Moolenaar va_end(vap);
815731337658SMarcel Moolenaar }
815831337658SMarcel Moolenaar
8159d1a0d267SMarcel Moolenaar /*
8160d1a0d267SMarcel Moolenaar * Parse any libxo-specific options from the command line, removing them
8161d1a0d267SMarcel Moolenaar * so the main() argument parsing won't see them. We return the new value
8162d1a0d267SMarcel Moolenaar * for argc or -1 for error. If an error occurred, the program should
8163d1a0d267SMarcel Moolenaar * exit. A suitable error message has already been displayed.
8164d1a0d267SMarcel Moolenaar */
816531337658SMarcel Moolenaar int
xo_parse_args(int argc,char ** argv)816631337658SMarcel Moolenaar xo_parse_args (int argc, char **argv)
816731337658SMarcel Moolenaar {
816831337658SMarcel Moolenaar static char libxo_opt[] = "--libxo";
816931337658SMarcel Moolenaar char *cp;
817031337658SMarcel Moolenaar int i, save;
817131337658SMarcel Moolenaar
81725c5819b2SPhil Shafer /*
81735c5819b2SPhil Shafer * If xo_set_program has always been called, we honor that value
81745c5819b2SPhil Shafer */
81755c5819b2SPhil Shafer if (xo_program == NULL) {
817631337658SMarcel Moolenaar /* Save our program name for xo_err and friends */
817731337658SMarcel Moolenaar xo_program = argv[0];
817831337658SMarcel Moolenaar cp = strrchr(xo_program, '/');
817931337658SMarcel Moolenaar if (cp)
818076afb20cSPhil Shafer xo_program = ++cp;
818167322d16SPhil Shafer else
818267322d16SPhil Shafer cp = argv[0]; /* Reset to front of string */
818376afb20cSPhil Shafer
81845c5819b2SPhil Shafer /*
81855c5819b2SPhil Shafer * GNU libtool add an annoying ".test" as the program
81865c5819b2SPhil Shafer * extension; we remove it. libtool also adds a "lt-" prefix
81875c5819b2SPhil Shafer * that we cannot remove.
81885c5819b2SPhil Shafer */
818976afb20cSPhil Shafer size_t len = strlen(xo_program);
819076afb20cSPhil Shafer static const char gnu_ext[] = ".test";
819176afb20cSPhil Shafer if (len >= sizeof(gnu_ext)) {
819267322d16SPhil Shafer cp += len + 1 - sizeof(gnu_ext);
819376afb20cSPhil Shafer if (xo_streq(cp, gnu_ext))
819476afb20cSPhil Shafer *cp = '\0';
819576afb20cSPhil Shafer }
81965c5819b2SPhil Shafer }
819731337658SMarcel Moolenaar
8198f2b7bf8aSPhil Shafer xo_handle_t *xop = xo_default(NULL);
8199f2b7bf8aSPhil Shafer
820031337658SMarcel Moolenaar for (save = i = 1; i < argc; i++) {
820131337658SMarcel Moolenaar if (argv[i] == NULL
820231337658SMarcel Moolenaar || strncmp(argv[i], libxo_opt, sizeof(libxo_opt) - 1) != 0) {
820331337658SMarcel Moolenaar if (save != i)
820431337658SMarcel Moolenaar argv[save] = argv[i];
820531337658SMarcel Moolenaar save += 1;
820631337658SMarcel Moolenaar continue;
820731337658SMarcel Moolenaar }
820831337658SMarcel Moolenaar
820931337658SMarcel Moolenaar cp = argv[i] + sizeof(libxo_opt) - 1;
8210ee5cf116SPhil Shafer if (*cp == '\0') {
821131337658SMarcel Moolenaar cp = argv[++i];
8212ee5cf116SPhil Shafer if (cp == NULL) {
821331337658SMarcel Moolenaar xo_warnx("missing libxo option");
821431337658SMarcel Moolenaar return -1;
821531337658SMarcel Moolenaar }
821631337658SMarcel Moolenaar
8217f2b7bf8aSPhil Shafer if (xo_set_options(xop, cp) < 0)
821831337658SMarcel Moolenaar return -1;
821931337658SMarcel Moolenaar } else if (*cp == ':') {
8220f2b7bf8aSPhil Shafer if (xo_set_options(xop, cp) < 0)
822131337658SMarcel Moolenaar return -1;
822231337658SMarcel Moolenaar
822331337658SMarcel Moolenaar } else if (*cp == '=') {
8224f2b7bf8aSPhil Shafer if (xo_set_options(xop, ++cp) < 0)
822531337658SMarcel Moolenaar return -1;
822631337658SMarcel Moolenaar
822731337658SMarcel Moolenaar } else if (*cp == '-') {
822831337658SMarcel Moolenaar cp += 1;
822976afb20cSPhil Shafer if (xo_streq(cp, "check")) {
823031337658SMarcel Moolenaar exit(XO_HAS_LIBXO);
823131337658SMarcel Moolenaar
823231337658SMarcel Moolenaar } else {
823331337658SMarcel Moolenaar xo_warnx("unknown libxo option: '%s'", argv[i]);
823431337658SMarcel Moolenaar return -1;
823531337658SMarcel Moolenaar }
823631337658SMarcel Moolenaar } else {
823731337658SMarcel Moolenaar xo_warnx("unknown libxo option: '%s'", argv[i]);
823831337658SMarcel Moolenaar return -1;
823931337658SMarcel Moolenaar }
824031337658SMarcel Moolenaar }
824131337658SMarcel Moolenaar
8242f2b7bf8aSPhil Shafer /*
8243f2b7bf8aSPhil Shafer * We only want to do color output on terminals, but we only want
8244f2b7bf8aSPhil Shafer * to do this if the user has asked for color.
8245f2b7bf8aSPhil Shafer */
8246f2b7bf8aSPhil Shafer if (XOF_ISSET(xop, XOF_COLOR_ALLOWED) && isatty(1))
8247f2b7bf8aSPhil Shafer XOF_SET(xop, XOF_COLOR);
8248f2b7bf8aSPhil Shafer
824931337658SMarcel Moolenaar argv[save] = NULL;
825031337658SMarcel Moolenaar return save;
825131337658SMarcel Moolenaar }
825231337658SMarcel Moolenaar
8253d1a0d267SMarcel Moolenaar /*
8254d1a0d267SMarcel Moolenaar * Debugging function that dumps the current stack of open libxo constructs,
8255d1a0d267SMarcel Moolenaar * suitable for calling from the debugger.
8256d1a0d267SMarcel Moolenaar */
8257545ddfbeSMarcel Moolenaar void
xo_dump_stack(xo_handle_t * xop)8258545ddfbeSMarcel Moolenaar xo_dump_stack (xo_handle_t *xop)
8259545ddfbeSMarcel Moolenaar {
8260545ddfbeSMarcel Moolenaar int i;
8261545ddfbeSMarcel Moolenaar xo_stack_t *xsp;
8262545ddfbeSMarcel Moolenaar
8263545ddfbeSMarcel Moolenaar xop = xo_default(xop);
8264545ddfbeSMarcel Moolenaar
8265545ddfbeSMarcel Moolenaar fprintf(stderr, "Stack dump:\n");
8266545ddfbeSMarcel Moolenaar
8267545ddfbeSMarcel Moolenaar xsp = xop->xo_stack;
8268545ddfbeSMarcel Moolenaar for (i = 1, xsp++; i <= xop->xo_depth; i++, xsp++) {
8269545ddfbeSMarcel Moolenaar fprintf(stderr, " [%d] %s '%s' [%x]\n",
8270545ddfbeSMarcel Moolenaar i, xo_state_name(xsp->xs_state),
8271545ddfbeSMarcel Moolenaar xsp->xs_name ?: "--", xsp->xs_flags);
8272545ddfbeSMarcel Moolenaar }
8273545ddfbeSMarcel Moolenaar }
8274545ddfbeSMarcel Moolenaar
8275d1a0d267SMarcel Moolenaar /*
8276d1a0d267SMarcel Moolenaar * Record the program name used for error messages
8277d1a0d267SMarcel Moolenaar */
8278545ddfbeSMarcel Moolenaar void
xo_set_program(const char * name)8279545ddfbeSMarcel Moolenaar xo_set_program (const char *name)
8280545ddfbeSMarcel Moolenaar {
8281545ddfbeSMarcel Moolenaar xo_program = name;
8282545ddfbeSMarcel Moolenaar }
8283545ddfbeSMarcel Moolenaar
8284788ca347SMarcel Moolenaar void
xo_set_version_h(xo_handle_t * xop,const char * version)828542ff34c3SPhil Shafer xo_set_version_h (xo_handle_t *xop, const char *version)
8286788ca347SMarcel Moolenaar {
8287788ca347SMarcel Moolenaar xop = xo_default(xop);
8288788ca347SMarcel Moolenaar
8289788ca347SMarcel Moolenaar if (version == NULL || strchr(version, '"') != NULL)
8290788ca347SMarcel Moolenaar return;
8291788ca347SMarcel Moolenaar
8292d1a0d267SMarcel Moolenaar if (!xo_style_is_encoding(xop))
8293d1a0d267SMarcel Moolenaar return;
8294d1a0d267SMarcel Moolenaar
8295788ca347SMarcel Moolenaar switch (xo_style(xop)) {
8296788ca347SMarcel Moolenaar case XO_STYLE_XML:
8297788ca347SMarcel Moolenaar /* For XML, we record this as an attribute for the first tag */
82988a6eceffSPhil Shafer xo_attr_h(xop, "version", "%s", version);
8299788ca347SMarcel Moolenaar break;
8300788ca347SMarcel Moolenaar
8301788ca347SMarcel Moolenaar case XO_STYLE_JSON:
8302788ca347SMarcel Moolenaar /*
8303d1a0d267SMarcel Moolenaar * For JSON, we record the version string in our handle, and emit
8304788ca347SMarcel Moolenaar * it in xo_emit_top.
8305788ca347SMarcel Moolenaar */
8306d1a0d267SMarcel Moolenaar xop->xo_version = xo_strndup(version, -1);
8307d1a0d267SMarcel Moolenaar break;
8308d1a0d267SMarcel Moolenaar
8309d1a0d267SMarcel Moolenaar case XO_STYLE_ENCODER:
8310f2b7bf8aSPhil Shafer xo_encoder_handle(xop, XO_OP_VERSION, NULL, version, 0);
8311788ca347SMarcel Moolenaar break;
8312788ca347SMarcel Moolenaar }
8313788ca347SMarcel Moolenaar }
8314788ca347SMarcel Moolenaar
8315d1a0d267SMarcel Moolenaar /*
8316ee5cf116SPhil Shafer * Set the version number for the API content being carried through
8317d1a0d267SMarcel Moolenaar * the xo handle.
8318d1a0d267SMarcel Moolenaar */
8319788ca347SMarcel Moolenaar void
xo_set_version(const char * version)8320788ca347SMarcel Moolenaar xo_set_version (const char *version)
8321788ca347SMarcel Moolenaar {
8322788ca347SMarcel Moolenaar xo_set_version_h(NULL, version);
8323788ca347SMarcel Moolenaar }
8324788ca347SMarcel Moolenaar
8325d1a0d267SMarcel Moolenaar /*
8326d1a0d267SMarcel Moolenaar * Generate a warning. Normally, this is a text message written to
8327d1a0d267SMarcel Moolenaar * standard error. If the XOF_WARN_XML flag is set, then we generate
8328d1a0d267SMarcel Moolenaar * XMLified content on standard output.
8329d1a0d267SMarcel Moolenaar */
8330d1a0d267SMarcel Moolenaar void
xo_emit_warn_hcv(xo_handle_t * xop,int as_warning,int code,const char * fmt,va_list vap)8331d1a0d267SMarcel Moolenaar xo_emit_warn_hcv (xo_handle_t *xop, int as_warning, int code,
8332d1a0d267SMarcel Moolenaar const char *fmt, va_list vap)
833331337658SMarcel Moolenaar {
8334d1a0d267SMarcel Moolenaar xop = xo_default(xop);
833531337658SMarcel Moolenaar
8336d1a0d267SMarcel Moolenaar if (fmt == NULL)
8337d1a0d267SMarcel Moolenaar return;
833831337658SMarcel Moolenaar
8339d1a0d267SMarcel Moolenaar xo_open_marker_h(xop, "xo_emit_warn_hcv");
8340d1a0d267SMarcel Moolenaar xo_open_container_h(xop, as_warning ? "__warning" : "__error");
834131337658SMarcel Moolenaar
8342d1a0d267SMarcel Moolenaar if (xo_program)
8343d1a0d267SMarcel Moolenaar xo_emit("{wc:program}", xo_program);
834431337658SMarcel Moolenaar
8345d1a0d267SMarcel Moolenaar if (xo_style(xop) == XO_STYLE_XML || xo_style(xop) == XO_STYLE_JSON) {
8346d1a0d267SMarcel Moolenaar va_list ap;
8347d1a0d267SMarcel Moolenaar xo_handle_t temp;
834831337658SMarcel Moolenaar
8349d1a0d267SMarcel Moolenaar bzero(&temp, sizeof(temp));
8350d1a0d267SMarcel Moolenaar temp.xo_style = XO_STYLE_TEXT;
8351d1a0d267SMarcel Moolenaar xo_buf_init(&temp.xo_data);
8352d1a0d267SMarcel Moolenaar xo_depth_check(&temp, XO_DEPTH);
835331337658SMarcel Moolenaar
8354d1a0d267SMarcel Moolenaar va_copy(ap, vap);
8355d1a0d267SMarcel Moolenaar (void) xo_emit_hv(&temp, fmt, ap);
8356d1a0d267SMarcel Moolenaar va_end(ap);
835731337658SMarcel Moolenaar
8358d1a0d267SMarcel Moolenaar xo_buffer_t *src = &temp.xo_data;
8359d1a0d267SMarcel Moolenaar xo_format_value(xop, "message", 7, src->xb_bufp,
8360264104f2SPhil Shafer src->xb_curp - src->xb_bufp, NULL, 0, NULL, 0, 0);
836131337658SMarcel Moolenaar
8362d1a0d267SMarcel Moolenaar xo_free(temp.xo_stack);
8363d1a0d267SMarcel Moolenaar xo_buf_cleanup(src);
836431337658SMarcel Moolenaar }
836531337658SMarcel Moolenaar
8366d1a0d267SMarcel Moolenaar (void) xo_emit_hv(xop, fmt, vap);
836731337658SMarcel Moolenaar
83688a6eceffSPhil Shafer ssize_t len = strlen(fmt);
8369d1a0d267SMarcel Moolenaar if (len > 0 && fmt[len - 1] != '\n') {
8370d1a0d267SMarcel Moolenaar if (code > 0) {
8371d1a0d267SMarcel Moolenaar const char *msg = strerror(code);
8372d1a0d267SMarcel Moolenaar if (msg)
8373d1a0d267SMarcel Moolenaar xo_emit_h(xop, ": {G:strerror}{g:error/%s}", msg);
8374d1a0d267SMarcel Moolenaar }
8375d1a0d267SMarcel Moolenaar xo_emit("\n");
837631337658SMarcel Moolenaar }
837731337658SMarcel Moolenaar
8378d1a0d267SMarcel Moolenaar xo_close_marker_h(xop, "xo_emit_warn_hcv");
8379d1a0d267SMarcel Moolenaar xo_flush_h(xop);
838031337658SMarcel Moolenaar }
838131337658SMarcel Moolenaar
8382d1a0d267SMarcel Moolenaar void
xo_emit_warn_hc(xo_handle_t * xop,int code,const char * fmt,...)8383d1a0d267SMarcel Moolenaar xo_emit_warn_hc (xo_handle_t *xop, int code, const char *fmt, ...)
8384d1a0d267SMarcel Moolenaar {
8385d1a0d267SMarcel Moolenaar va_list vap;
838631337658SMarcel Moolenaar
8387d1a0d267SMarcel Moolenaar va_start(vap, fmt);
8388d1a0d267SMarcel Moolenaar xo_emit_warn_hcv(xop, 1, code, fmt, vap);
8389d1a0d267SMarcel Moolenaar va_end(vap);
839031337658SMarcel Moolenaar }
839131337658SMarcel Moolenaar
8392d1a0d267SMarcel Moolenaar void
xo_emit_warn_c(int code,const char * fmt,...)8393d1a0d267SMarcel Moolenaar xo_emit_warn_c (int code, const char *fmt, ...)
8394d1a0d267SMarcel Moolenaar {
8395d1a0d267SMarcel Moolenaar va_list vap;
839631337658SMarcel Moolenaar
8397d1a0d267SMarcel Moolenaar va_start(vap, fmt);
8398d1a0d267SMarcel Moolenaar xo_emit_warn_hcv(NULL, 1, code, fmt, vap);
8399d1a0d267SMarcel Moolenaar va_end(vap);
8400d1a0d267SMarcel Moolenaar }
840131337658SMarcel Moolenaar
8402d1a0d267SMarcel Moolenaar void
xo_emit_warn(const char * fmt,...)8403d1a0d267SMarcel Moolenaar xo_emit_warn (const char *fmt, ...)
8404d1a0d267SMarcel Moolenaar {
8405d1a0d267SMarcel Moolenaar int code = errno;
8406d1a0d267SMarcel Moolenaar va_list vap;
8407d1a0d267SMarcel Moolenaar
8408d1a0d267SMarcel Moolenaar va_start(vap, fmt);
8409d1a0d267SMarcel Moolenaar xo_emit_warn_hcv(NULL, 1, code, fmt, vap);
8410d1a0d267SMarcel Moolenaar va_end(vap);
8411d1a0d267SMarcel Moolenaar }
8412d1a0d267SMarcel Moolenaar
8413d1a0d267SMarcel Moolenaar void
xo_emit_warnx(const char * fmt,...)8414d1a0d267SMarcel Moolenaar xo_emit_warnx (const char *fmt, ...)
8415d1a0d267SMarcel Moolenaar {
8416d1a0d267SMarcel Moolenaar va_list vap;
8417d1a0d267SMarcel Moolenaar
8418d1a0d267SMarcel Moolenaar va_start(vap, fmt);
8419d1a0d267SMarcel Moolenaar xo_emit_warn_hcv(NULL, 1, -1, fmt, vap);
8420d1a0d267SMarcel Moolenaar va_end(vap);
8421d1a0d267SMarcel Moolenaar }
8422d1a0d267SMarcel Moolenaar
8423d1a0d267SMarcel Moolenaar void
xo_emit_err_v(int eval,int code,const char * fmt,va_list vap)8424d1a0d267SMarcel Moolenaar xo_emit_err_v (int eval, int code, const char *fmt, va_list vap)
8425d1a0d267SMarcel Moolenaar {
8426d1a0d267SMarcel Moolenaar xo_emit_warn_hcv(NULL, 0, code, fmt, vap);
842731337658SMarcel Moolenaar xo_finish();
8428d1a0d267SMarcel Moolenaar exit(eval);
842931337658SMarcel Moolenaar }
8430d1a0d267SMarcel Moolenaar
8431d1a0d267SMarcel Moolenaar void
xo_emit_err(int eval,const char * fmt,...)8432d1a0d267SMarcel Moolenaar xo_emit_err (int eval, const char *fmt, ...)
8433d1a0d267SMarcel Moolenaar {
8434d1a0d267SMarcel Moolenaar int code = errno;
8435d1a0d267SMarcel Moolenaar va_list vap;
8436d1a0d267SMarcel Moolenaar va_start(vap, fmt);
843776afb20cSPhil Shafer xo_emit_err_v(eval, code, fmt, vap);
843876afb20cSPhil Shafer /*NOTREACHED*/
8439d1a0d267SMarcel Moolenaar }
8440d1a0d267SMarcel Moolenaar
8441d1a0d267SMarcel Moolenaar void
xo_emit_errx(int eval,const char * fmt,...)8442d1a0d267SMarcel Moolenaar xo_emit_errx (int eval, const char *fmt, ...)
8443d1a0d267SMarcel Moolenaar {
8444d1a0d267SMarcel Moolenaar va_list vap;
8445d1a0d267SMarcel Moolenaar
8446d1a0d267SMarcel Moolenaar va_start(vap, fmt);
844776afb20cSPhil Shafer xo_emit_err_v(eval, -1, fmt, vap); /* This will exit */
844876afb20cSPhil Shafer /*NOTREACHED*/
8449d1a0d267SMarcel Moolenaar }
8450d1a0d267SMarcel Moolenaar
8451d1a0d267SMarcel Moolenaar void
xo_emit_errc(int eval,int code,const char * fmt,...)8452d1a0d267SMarcel Moolenaar xo_emit_errc (int eval, int code, const char *fmt, ...)
8453d1a0d267SMarcel Moolenaar {
8454d1a0d267SMarcel Moolenaar va_list vap;
8455d1a0d267SMarcel Moolenaar
8456d1a0d267SMarcel Moolenaar va_start(vap, fmt);
845776afb20cSPhil Shafer xo_emit_err_v(eval, code, fmt, vap); /* This will exit */
845876afb20cSPhil Shafer /*NOTREACHED*/
8459d1a0d267SMarcel Moolenaar }
8460d1a0d267SMarcel Moolenaar
8461d1a0d267SMarcel Moolenaar /*
8462d1a0d267SMarcel Moolenaar * Get the opaque private pointer for an xo handle
8463d1a0d267SMarcel Moolenaar */
8464d1a0d267SMarcel Moolenaar void *
xo_get_private(xo_handle_t * xop)8465d1a0d267SMarcel Moolenaar xo_get_private (xo_handle_t *xop)
8466d1a0d267SMarcel Moolenaar {
8467d1a0d267SMarcel Moolenaar xop = xo_default(xop);
8468d1a0d267SMarcel Moolenaar return xop->xo_private;
8469d1a0d267SMarcel Moolenaar }
8470d1a0d267SMarcel Moolenaar
8471d1a0d267SMarcel Moolenaar /*
8472d1a0d267SMarcel Moolenaar * Set the opaque private pointer for an xo handle.
8473d1a0d267SMarcel Moolenaar */
8474d1a0d267SMarcel Moolenaar void
xo_set_private(xo_handle_t * xop,void * opaque)8475d1a0d267SMarcel Moolenaar xo_set_private (xo_handle_t *xop, void *opaque)
8476d1a0d267SMarcel Moolenaar {
8477d1a0d267SMarcel Moolenaar xop = xo_default(xop);
8478d1a0d267SMarcel Moolenaar xop->xo_private = opaque;
8479d1a0d267SMarcel Moolenaar }
8480d1a0d267SMarcel Moolenaar
8481d1a0d267SMarcel Moolenaar /*
8482d1a0d267SMarcel Moolenaar * Get the encoder function
8483d1a0d267SMarcel Moolenaar */
8484d1a0d267SMarcel Moolenaar xo_encoder_func_t
xo_get_encoder(xo_handle_t * xop)8485d1a0d267SMarcel Moolenaar xo_get_encoder (xo_handle_t *xop)
8486d1a0d267SMarcel Moolenaar {
8487d1a0d267SMarcel Moolenaar xop = xo_default(xop);
8488d1a0d267SMarcel Moolenaar return xop->xo_encoder;
8489d1a0d267SMarcel Moolenaar }
8490d1a0d267SMarcel Moolenaar
8491d1a0d267SMarcel Moolenaar /*
8492d1a0d267SMarcel Moolenaar * Record an encoder callback function in an xo handle.
8493d1a0d267SMarcel Moolenaar */
8494d1a0d267SMarcel Moolenaar void
xo_set_encoder(xo_handle_t * xop,xo_encoder_func_t encoder)8495d1a0d267SMarcel Moolenaar xo_set_encoder (xo_handle_t *xop, xo_encoder_func_t encoder)
8496d1a0d267SMarcel Moolenaar {
8497d1a0d267SMarcel Moolenaar xop = xo_default(xop);
8498d1a0d267SMarcel Moolenaar
8499d1a0d267SMarcel Moolenaar xop->xo_style = XO_STYLE_ENCODER;
8500d1a0d267SMarcel Moolenaar xop->xo_encoder = encoder;
8501d1a0d267SMarcel Moolenaar }
8502406a584dSPhil Shafer
8503406a584dSPhil Shafer /*
8504406a584dSPhil Shafer * The xo(1) utility needs to be able to open and close lists and
8505406a584dSPhil Shafer * instances, but since it's called without "state", we cannot
8506406a584dSPhil Shafer * rely on the state transitions (in xo_transition) to DTRT, so
8507406a584dSPhil Shafer * we have a mechanism for external parties to "force" transitions
8508406a584dSPhil Shafer * that would otherwise be impossible. This is not a general
8509406a584dSPhil Shafer * mechanism, and is really tailored only for xo(1).
8510406a584dSPhil Shafer */
8511406a584dSPhil Shafer void
xo_explicit_transition(xo_handle_t * xop,xo_state_t new_state,const char * name,xo_xof_flags_t flags)8512406a584dSPhil Shafer xo_explicit_transition (xo_handle_t *xop, xo_state_t new_state,
8513406a584dSPhil Shafer const char *name, xo_xof_flags_t flags)
8514406a584dSPhil Shafer {
8515406a584dSPhil Shafer xo_xsf_flags_t xsf_flags;
8516406a584dSPhil Shafer
8517406a584dSPhil Shafer xop = xo_default(xop);
8518406a584dSPhil Shafer
8519406a584dSPhil Shafer switch (new_state) {
8520406a584dSPhil Shafer
8521406a584dSPhil Shafer case XSS_OPEN_LIST:
8522406a584dSPhil Shafer xo_do_open_list(xop, flags, name);
8523406a584dSPhil Shafer break;
8524406a584dSPhil Shafer
8525406a584dSPhil Shafer case XSS_OPEN_INSTANCE:
8526406a584dSPhil Shafer xo_do_open_instance(xop, flags, name);
8527406a584dSPhil Shafer break;
8528406a584dSPhil Shafer
8529406a584dSPhil Shafer case XSS_CLOSE_INSTANCE:
8530406a584dSPhil Shafer xo_depth_change(xop, name, 1, 1, XSS_OPEN_INSTANCE,
8531406a584dSPhil Shafer xo_stack_flags(flags));
8532406a584dSPhil Shafer xo_stack_set_flags(xop);
8533406a584dSPhil Shafer xo_do_close_instance(xop, name);
8534406a584dSPhil Shafer break;
8535406a584dSPhil Shafer
8536406a584dSPhil Shafer case XSS_CLOSE_LIST:
8537406a584dSPhil Shafer xsf_flags = XOF_ISSET(xop, XOF_NOT_FIRST) ? XSF_NOT_FIRST : 0;
8538406a584dSPhil Shafer
8539406a584dSPhil Shafer xo_depth_change(xop, name, 1, 1, XSS_OPEN_LIST,
8540406a584dSPhil Shafer XSF_LIST | xsf_flags | xo_stack_flags(flags));
8541406a584dSPhil Shafer xo_do_close_list(xop, name);
8542406a584dSPhil Shafer break;
8543406a584dSPhil Shafer }
8544406a584dSPhil Shafer }
8545