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