xref: /freebsd/contrib/libxo/xo/xo.c (revision d1a0d267)
131337658SMarcel Moolenaar /*
231337658SMarcel Moolenaar  * Copyright (c) 2014-2019, Juniper Networks, Inc.
331337658SMarcel Moolenaar  * All rights reserved.
431337658SMarcel Moolenaar  * This SOFTWARE is licensed under the LICENSE provided in the
531337658SMarcel Moolenaar  * ../Copyright file. By downloading, installing, copying, or otherwise
631337658SMarcel Moolenaar  * using the SOFTWARE, you agree to be bound by the terms of that
731337658SMarcel Moolenaar  * LICENSE.
831337658SMarcel Moolenaar  * Phil Shafer, July 2014
931337658SMarcel Moolenaar  */
1031337658SMarcel Moolenaar 
1131337658SMarcel Moolenaar #include <stdio.h>
1231337658SMarcel Moolenaar #include <stdlib.h>
1331337658SMarcel Moolenaar #include <stdarg.h>
1431337658SMarcel Moolenaar #include <string.h>
1531337658SMarcel Moolenaar 
16d1a0d267SMarcel Moolenaar #include "xo_config.h"
1731337658SMarcel Moolenaar #include "xo.h"
1831337658SMarcel Moolenaar #include "xo_explicit.h"
1931337658SMarcel Moolenaar 
2031337658SMarcel Moolenaar #include <getopt.h>		/* Include after xo.h for testing */
2131337658SMarcel Moolenaar 
2231337658SMarcel Moolenaar #ifndef UNUSED
2331337658SMarcel Moolenaar #define UNUSED __attribute__ ((__unused__))
2431337658SMarcel Moolenaar #endif /* UNUSED */
2531337658SMarcel Moolenaar 
2631337658SMarcel Moolenaar static int opt_warn;		/* Enable warnings */
2731337658SMarcel Moolenaar 
2831337658SMarcel Moolenaar static char **save_argv;
2931337658SMarcel Moolenaar static char **checkpoint_argv;
3031337658SMarcel Moolenaar 
3131337658SMarcel Moolenaar static char *
next_arg(void)3231337658SMarcel Moolenaar next_arg (void)
3331337658SMarcel Moolenaar {
3431337658SMarcel Moolenaar     char *cp = *save_argv;
3531337658SMarcel Moolenaar 
3631337658SMarcel Moolenaar     if (cp == NULL)
3731337658SMarcel Moolenaar 	xo_errx(1, "missing argument");
3831337658SMarcel Moolenaar 
3931337658SMarcel Moolenaar     save_argv += 1;
4031337658SMarcel Moolenaar     return cp;
4131337658SMarcel Moolenaar }
4231337658SMarcel Moolenaar 
4331337658SMarcel Moolenaar static void
prep_arg(char * fmt)4431337658SMarcel Moolenaar prep_arg (char *fmt)
4531337658SMarcel Moolenaar {
4631337658SMarcel Moolenaar     char *cp, *fp;
4731337658SMarcel Moolenaar 
4831337658SMarcel Moolenaar     for (cp = fp = fmt; *cp; cp++, fp++) {
4931337658SMarcel Moolenaar 	if (*cp != '\\') {
5031337658SMarcel Moolenaar 	    if (cp != fp)
5131337658SMarcel Moolenaar 		*fp = *cp;
5231337658SMarcel Moolenaar 	    continue;
5331337658SMarcel Moolenaar 	}
5431337658SMarcel Moolenaar 
5531337658SMarcel Moolenaar 	switch (*++cp) {
5631337658SMarcel Moolenaar 	case 'n':
5731337658SMarcel Moolenaar 	    *fp = '\n';
5831337658SMarcel Moolenaar 	    break;
5931337658SMarcel Moolenaar 
6031337658SMarcel Moolenaar 	case 'r':
6131337658SMarcel Moolenaar 	    *fp = '\r';
6231337658SMarcel Moolenaar 	    break;
6331337658SMarcel Moolenaar 
6431337658SMarcel Moolenaar 	case 'b':
6531337658SMarcel Moolenaar 	    *fp = '\b';
6631337658SMarcel Moolenaar 	    break;
6731337658SMarcel Moolenaar 
6831337658SMarcel Moolenaar 	case 'e':
6931337658SMarcel Moolenaar 	    *fp = '\e';
7031337658SMarcel Moolenaar 	    break;
7131337658SMarcel Moolenaar 
7231337658SMarcel Moolenaar 	default:
7331337658SMarcel Moolenaar 	    *fp = *cp;
7431337658SMarcel Moolenaar 	}
7531337658SMarcel Moolenaar     }
7631337658SMarcel Moolenaar 
7731337658SMarcel Moolenaar     *fp = '\0';
7831337658SMarcel Moolenaar }
7931337658SMarcel Moolenaar 
8031337658SMarcel Moolenaar static void
checkpoint(xo_handle_t * xop UNUSED,va_list vap UNUSED,int restore)8131337658SMarcel Moolenaar checkpoint (xo_handle_t *xop UNUSED, va_list vap UNUSED, int restore)
8231337658SMarcel Moolenaar {
8331337658SMarcel Moolenaar     if (restore)
8431337658SMarcel Moolenaar 	save_argv = checkpoint_argv;
8531337658SMarcel Moolenaar     else
8631337658SMarcel Moolenaar 	checkpoint_argv = save_argv;
8731337658SMarcel Moolenaar }
8831337658SMarcel Moolenaar 
8931337658SMarcel Moolenaar /*
9031337658SMarcel Moolenaar  * Our custom formatter is responsible for combining format string pieces
9131337658SMarcel Moolenaar  * with our command line arguments to build strings.  This involves faking
9231337658SMarcel Moolenaar  * some printf-style logic.
9331337658SMarcel Moolenaar  */
9431337658SMarcel Moolenaar static xo_ssize_t
formatter(xo_handle_t * xop,char * buf,xo_ssize_t bufsiz,const char * fmt,va_list vap UNUSED)9531337658SMarcel Moolenaar formatter (xo_handle_t *xop, char *buf, xo_ssize_t bufsiz,
9631337658SMarcel Moolenaar 	   const char *fmt, va_list vap UNUSED)
97d1a0d267SMarcel Moolenaar {
98d1a0d267SMarcel Moolenaar     int lflag UNUSED = 0;	/* Parse long flag, though currently ignored */
9931337658SMarcel Moolenaar     int hflag = 0, jflag = 0, tflag = 0,
10031337658SMarcel Moolenaar 	zflag = 0, qflag = 0, star1 = 0, star2 = 0;
10131337658SMarcel Moolenaar     int rc = 0;
10231337658SMarcel Moolenaar     int w1 = 0, w2 = 0;
10331337658SMarcel Moolenaar     const char *cp;
10431337658SMarcel Moolenaar 
10531337658SMarcel Moolenaar     for (cp = fmt + 1; *cp; cp++) {
10631337658SMarcel Moolenaar 	if (*cp == 'l')
10731337658SMarcel Moolenaar 	    lflag += 1;
10831337658SMarcel Moolenaar 	else if (*cp == 'h')
10931337658SMarcel Moolenaar 	    hflag += 1;
11031337658SMarcel Moolenaar 	else if (*cp == 'j')
11131337658SMarcel Moolenaar 	    jflag += 1;
11231337658SMarcel Moolenaar 	else if (*cp == 't')
11331337658SMarcel Moolenaar 	    tflag += 1;
11431337658SMarcel Moolenaar 	else if (*cp == 'z')
11531337658SMarcel Moolenaar 	    zflag += 1;
11631337658SMarcel Moolenaar 	else if (*cp == 'q')
11731337658SMarcel Moolenaar 	    qflag += 1;
11831337658SMarcel Moolenaar 	else if (*cp == '*') {
11931337658SMarcel Moolenaar 	    if (star1 == 0)
12031337658SMarcel Moolenaar 		star1 = 1;
12131337658SMarcel Moolenaar 	    else
12231337658SMarcel Moolenaar 		star2 = 1;
12331337658SMarcel Moolenaar 	} else if (strchr("diouxXDOUeEfFgGaAcCsSp", *cp) != NULL)
12431337658SMarcel Moolenaar 	    break;
12531337658SMarcel Moolenaar 	else if (*cp == 'n' || *cp == 'v') {
12631337658SMarcel Moolenaar 	    if (opt_warn)
12731337658SMarcel Moolenaar 		xo_error_h(xop, "unsupported format: '%s'", fmt);
12831337658SMarcel Moolenaar 	    return -1;
12931337658SMarcel Moolenaar 	}
13031337658SMarcel Moolenaar     }
13131337658SMarcel Moolenaar 
13231337658SMarcel Moolenaar     char fc = *cp;
13331337658SMarcel Moolenaar 
13431337658SMarcel Moolenaar     /* Handle "%*.*s" */
13531337658SMarcel Moolenaar     if (star1)
13631337658SMarcel Moolenaar 	w1 = strtol(next_arg(), NULL, 0);
13731337658SMarcel Moolenaar     if (star2 > 1)
13831337658SMarcel Moolenaar 	w2 = strtol(next_arg(), NULL, 0);
13931337658SMarcel Moolenaar 
14031337658SMarcel Moolenaar     if (fc == 'D' || fc == 'O' || fc == 'U')
14131337658SMarcel Moolenaar 	lflag = 1;
14231337658SMarcel Moolenaar 
14331337658SMarcel Moolenaar     if (strchr("diD", fc) != NULL) {
14431337658SMarcel Moolenaar 	long long value = strtoll(next_arg(), NULL, 0);
14531337658SMarcel Moolenaar 	if (star1 && star2)
14631337658SMarcel Moolenaar 	    rc = snprintf(buf, bufsiz, fmt, w1, w2, value);
14731337658SMarcel Moolenaar 	else if (star1)
14831337658SMarcel Moolenaar 	    rc = snprintf(buf, bufsiz, fmt, w1, value);
14931337658SMarcel Moolenaar 	else
15031337658SMarcel Moolenaar 	    rc = snprintf(buf, bufsiz, fmt, value);
15131337658SMarcel Moolenaar 
15231337658SMarcel Moolenaar     } else if (strchr("ouxXOUp", fc) != NULL) {
15331337658SMarcel Moolenaar 	unsigned long long value = strtoull(next_arg(), NULL, 0);
15431337658SMarcel Moolenaar 	if (star1 && star2)
15531337658SMarcel Moolenaar 	    rc = snprintf(buf, bufsiz, fmt, w1, w2, value);
15631337658SMarcel Moolenaar 	else if (star1)
15731337658SMarcel Moolenaar 	    rc = snprintf(buf, bufsiz, fmt, w1, value);
15831337658SMarcel Moolenaar 	else
15931337658SMarcel Moolenaar 	    rc = snprintf(buf, bufsiz, fmt, value);
16031337658SMarcel Moolenaar 
16131337658SMarcel Moolenaar     } else if (strchr("eEfFgGaA", fc) != NULL) {
16231337658SMarcel Moolenaar 	double value = strtold(next_arg(), NULL);
16331337658SMarcel Moolenaar 	if (star1 && star2)
16431337658SMarcel Moolenaar 	    rc = snprintf(buf, bufsiz, fmt, w1, w2, value);
16531337658SMarcel Moolenaar 	else if (star1)
16631337658SMarcel Moolenaar 	    rc = snprintf(buf, bufsiz, fmt, w1, value);
16731337658SMarcel Moolenaar 	else
16831337658SMarcel Moolenaar 	    rc = snprintf(buf, bufsiz, fmt, value);
16931337658SMarcel Moolenaar 
17031337658SMarcel Moolenaar     } else if (fc == 'C' || fc == 'c' || fc == 'S' || fc == 's') {
17131337658SMarcel Moolenaar 	char *value = next_arg();
17231337658SMarcel Moolenaar 	if (star1 && star2)
17331337658SMarcel Moolenaar 	    rc = snprintf(buf, bufsiz, fmt, w1, w2, value);
17431337658SMarcel Moolenaar 	else if (star1)
17531337658SMarcel Moolenaar 	    rc = snprintf(buf, bufsiz, fmt, w1, value);
17631337658SMarcel Moolenaar 	else
17731337658SMarcel Moolenaar 	    rc = snprintf(buf, bufsiz, fmt, value);
17831337658SMarcel Moolenaar     }
17931337658SMarcel Moolenaar 
18031337658SMarcel Moolenaar     return rc;
18131337658SMarcel Moolenaar }
18231337658SMarcel Moolenaar 
18331337658SMarcel Moolenaar static void
print_version(void)18431337658SMarcel Moolenaar print_version (void)
18531337658SMarcel Moolenaar {
18631337658SMarcel Moolenaar     fprintf(stderr, "libxo version %s%s\n",
18731337658SMarcel Moolenaar 	    xo_version, xo_version_extra);
18831337658SMarcel Moolenaar     fprintf(stderr, "xo version %s%s\n",
18931337658SMarcel Moolenaar 	    LIBXO_VERSION, LIBXO_VERSION_EXTRA);
19031337658SMarcel Moolenaar }
19131337658SMarcel Moolenaar 
19231337658SMarcel Moolenaar static void
print_help(void)19331337658SMarcel Moolenaar print_help (void)
19431337658SMarcel Moolenaar {
19531337658SMarcel Moolenaar     fprintf(stderr,
19631337658SMarcel Moolenaar "Usage: xo [options] format [fields]\n"
19731337658SMarcel Moolenaar "    --close <path>        Close tags for the given path\n"
19831337658SMarcel Moolenaar "    --close-instance <name> Close an open instance name\n"
19931337658SMarcel Moolenaar "    --close-list <name>   Close an open list name\n"
20031337658SMarcel Moolenaar "    --continuation OR -C  Output belongs on same line as previous output\n"
20131337658SMarcel Moolenaar "    --depth <num>         Set the depth for pretty printing\n"
20231337658SMarcel Moolenaar "    --help                Display this help text\n"
20331337658SMarcel Moolenaar "    --html OR -H          Generate HTML output\n"
204d1a0d267SMarcel Moolenaar "    --instance OR -I <name> Wrap in an instance of the given name\n"
20531337658SMarcel Moolenaar "    --json OR -J          Generate JSON output\n"
20631337658SMarcel Moolenaar "    --leading-xpath <path> OR -l <path> "
20731337658SMarcel Moolenaar 	    "Add a prefix to generated XPaths (HTML)\n"
20831337658SMarcel Moolenaar "    --not-first           Indicate this object is not the first (JSON)\n"
20931337658SMarcel Moolenaar "    --open <path>         Open tags for the given path\n"
21031337658SMarcel Moolenaar "    --open-instance <name> Open an instance given by name\n"
21131337658SMarcel Moolenaar "    --open-list <name>   Open a list given by name\n"
21231337658SMarcel Moolenaar "    --option <opts> -or -O <opts>  Give formatting options\n"
21331337658SMarcel Moolenaar "    --pretty OR -p        Make 'pretty' output (add indent, newlines)\n"
21431337658SMarcel Moolenaar "    --style <style> OR -s <style>  "
21531337658SMarcel Moolenaar 	    "Generate given style (xml, json, text, html)\n"
21631337658SMarcel Moolenaar "    --text OR -T          Generate text output (the default style)\n"
21731337658SMarcel Moolenaar "    --top-wrap            Generate a top-level object wrapper (JSON)\n"
21831337658SMarcel Moolenaar "    --version             Display version information\n"
21931337658SMarcel Moolenaar "    --warn OR -W          Display warnings in text on stderr\n"
22031337658SMarcel Moolenaar "    --warn-xml            Display warnings in xml on stdout\n"
22131337658SMarcel Moolenaar "    --wrap <path>         Wrap output in a set of containers\n"
22231337658SMarcel Moolenaar "    --xml OR -X           Generate XML output\n"
22331337658SMarcel Moolenaar "    --xpath               Add XPath data to HTML output\n");
22431337658SMarcel Moolenaar }
22531337658SMarcel Moolenaar 
22631337658SMarcel Moolenaar static struct opts {
22731337658SMarcel Moolenaar     int o_close_instance;
22831337658SMarcel Moolenaar     int o_close_list;
22931337658SMarcel Moolenaar     int o_depth;
23031337658SMarcel Moolenaar     int o_help;
23131337658SMarcel Moolenaar     int o_not_first;
23231337658SMarcel Moolenaar     int o_open_instance;
23331337658SMarcel Moolenaar     int o_open_list;
23431337658SMarcel Moolenaar     int o_top_wrap;
23531337658SMarcel Moolenaar     int o_version;
23631337658SMarcel Moolenaar     int o_warn_xml;
23731337658SMarcel Moolenaar     int o_wrap;
23831337658SMarcel Moolenaar     int o_xpath;
23931337658SMarcel Moolenaar } opts;
24031337658SMarcel Moolenaar 
24131337658SMarcel Moolenaar static struct option long_opts[] = {
24231337658SMarcel Moolenaar     { "close", required_argument, NULL, 'c' },
24331337658SMarcel Moolenaar     { "close-instance", required_argument, &opts.o_close_instance, 1 },
24431337658SMarcel Moolenaar     { "close-list", required_argument, &opts.o_close_list, 1 },
24531337658SMarcel Moolenaar     { "continuation", no_argument, NULL, 'C' },
24631337658SMarcel Moolenaar     { "depth", required_argument, &opts.o_depth, 1 },
24731337658SMarcel Moolenaar     { "help", no_argument, &opts.o_help, 1 },
24831337658SMarcel Moolenaar     { "html", no_argument, NULL, 'H' },
24931337658SMarcel Moolenaar     { "instance", required_argument, NULL, 'I' },
25031337658SMarcel Moolenaar     { "json", no_argument, NULL, 'J' },
25131337658SMarcel Moolenaar     { "leading-xpath", required_argument, NULL, 'l' },
25231337658SMarcel Moolenaar     { "not-first", no_argument, &opts.o_not_first, 1 },
25331337658SMarcel Moolenaar     { "open", required_argument, NULL, 'o' },
25431337658SMarcel Moolenaar     { "open-instance", required_argument, &opts.o_open_instance, 1 },
25531337658SMarcel Moolenaar     { "open-list", required_argument, &opts.o_open_list, 1 },
25631337658SMarcel Moolenaar     { "option", required_argument, NULL, 'O' },
25731337658SMarcel Moolenaar     { "pretty", no_argument, NULL, 'p' },
25831337658SMarcel Moolenaar     { "style", required_argument, NULL, 's' },
25931337658SMarcel Moolenaar     { "text", no_argument, NULL, 'T' },
26031337658SMarcel Moolenaar     { "top-wrap", no_argument, &opts.o_top_wrap, 1 },
26131337658SMarcel Moolenaar     { "xml", no_argument, NULL, 'X' },
26231337658SMarcel Moolenaar     { "xpath", no_argument, &opts.o_xpath, 1 },
263d1a0d267SMarcel Moolenaar     { "version", no_argument, &opts.o_version, 1 },
26431337658SMarcel Moolenaar     { "warn", no_argument, NULL, 'W' },
26531337658SMarcel Moolenaar     { "warn-xml", no_argument, &opts.o_warn_xml, 1 },
26631337658SMarcel Moolenaar     { "wrap", required_argument, &opts.o_wrap, 1 },
26731337658SMarcel Moolenaar     { NULL, 0, NULL, 0 }
26831337658SMarcel Moolenaar };
26931337658SMarcel Moolenaar 
27031337658SMarcel Moolenaar int
main(int argc UNUSED,char ** argv)27131337658SMarcel Moolenaar main (int argc UNUSED, char **argv)
27231337658SMarcel Moolenaar {
27331337658SMarcel Moolenaar     char *fmt = NULL, *cp, *np;
27431337658SMarcel Moolenaar     char *opt_opener = NULL, *opt_closer = NULL, *opt_wrapper = NULL;
27531337658SMarcel Moolenaar     char *opt_options = NULL;
27631337658SMarcel Moolenaar     char *opt_instance = NULL;
27731337658SMarcel Moolenaar     char *opt_name = NULL;
27831337658SMarcel Moolenaar     xo_state_t new_state = 0;
27931337658SMarcel Moolenaar     int opt_depth = 0;
28031337658SMarcel Moolenaar     int opt_not_first = 0;
28131337658SMarcel Moolenaar     int opt_top_wrap = 0;
28231337658SMarcel Moolenaar     int rc;
28331337658SMarcel Moolenaar 
28431337658SMarcel Moolenaar     argc = xo_parse_args(argc, argv);
28531337658SMarcel Moolenaar     if (argc < 0)
28631337658SMarcel Moolenaar 	return 1;
28731337658SMarcel Moolenaar 
28831337658SMarcel Moolenaar     while ((rc = getopt_long(argc, argv, "Cc:HJl:O:o:ps:TXW",
28931337658SMarcel Moolenaar 				long_opts, NULL)) != -1) {
29031337658SMarcel Moolenaar 	switch (rc) {
29131337658SMarcel Moolenaar 	case 'C':
29231337658SMarcel Moolenaar 	    xo_set_flags(NULL, XOF_CONTINUATION);
29331337658SMarcel Moolenaar 	    break;
29431337658SMarcel Moolenaar 
29531337658SMarcel Moolenaar 	case 'c':
29631337658SMarcel Moolenaar 	    opt_closer = optarg;
29731337658SMarcel Moolenaar 	    xo_set_flags(NULL, XOF_IGNORE_CLOSE);
29831337658SMarcel Moolenaar 	    break;
29931337658SMarcel Moolenaar 
30031337658SMarcel Moolenaar 	case 'H':
30131337658SMarcel Moolenaar 	    xo_set_style(NULL, XO_STYLE_HTML);
30231337658SMarcel Moolenaar 	    break;
30331337658SMarcel Moolenaar 
30431337658SMarcel Moolenaar 	case 'I':
30531337658SMarcel Moolenaar 	    opt_instance = optarg;
30631337658SMarcel Moolenaar 	    break;
30731337658SMarcel Moolenaar 
30831337658SMarcel Moolenaar 	case 'J':
30931337658SMarcel Moolenaar 	    xo_set_style(NULL, XO_STYLE_JSON);
31031337658SMarcel Moolenaar 	    break;
31131337658SMarcel Moolenaar 
31231337658SMarcel Moolenaar 	case 'l':
31331337658SMarcel Moolenaar 	    xo_set_leading_xpath(NULL, optarg);
31431337658SMarcel Moolenaar 	    break;
31531337658SMarcel Moolenaar 
31631337658SMarcel Moolenaar 	case 'O':
31731337658SMarcel Moolenaar 	    opt_options = optarg;
31831337658SMarcel Moolenaar 	    break;
31931337658SMarcel Moolenaar 
32031337658SMarcel Moolenaar 	case 'o':
32131337658SMarcel Moolenaar 	    opt_opener = optarg;
32231337658SMarcel Moolenaar 	    break;
32331337658SMarcel Moolenaar 
32431337658SMarcel Moolenaar 	case 'p':
32531337658SMarcel Moolenaar 	    xo_set_flags(NULL, XOF_PRETTY);
32631337658SMarcel Moolenaar 	    break;
32731337658SMarcel Moolenaar 
32831337658SMarcel Moolenaar 	case 's':
32931337658SMarcel Moolenaar 	    if (xo_set_style_name(NULL, optarg) < 0)
33031337658SMarcel Moolenaar 		xo_errx(1, "unknown style: %s", optarg);
33131337658SMarcel Moolenaar 	    break;
33231337658SMarcel Moolenaar 
33331337658SMarcel Moolenaar 	case 'T':
33431337658SMarcel Moolenaar 	    xo_set_style(NULL, XO_STYLE_TEXT);
33531337658SMarcel Moolenaar 	    break;
33631337658SMarcel Moolenaar 
33731337658SMarcel Moolenaar 	case 'X':
33831337658SMarcel Moolenaar 	    xo_set_style(NULL, XO_STYLE_XML);
33931337658SMarcel Moolenaar 	    break;
34031337658SMarcel Moolenaar 
34131337658SMarcel Moolenaar 	case 'W':
34231337658SMarcel Moolenaar 	    opt_warn = 1;
34331337658SMarcel Moolenaar 	    xo_set_flags(NULL, XOF_WARN);
34431337658SMarcel Moolenaar 	    break;
34531337658SMarcel Moolenaar 
34631337658SMarcel Moolenaar 	case ':':
34731337658SMarcel Moolenaar 	    xo_errx(1, "missing argument");
34831337658SMarcel Moolenaar 	    break;
34931337658SMarcel Moolenaar 
35031337658SMarcel Moolenaar 	case 0:
35131337658SMarcel Moolenaar 	    if (opts.o_depth) {
35231337658SMarcel Moolenaar 		opt_depth = atoi(optarg);
35331337658SMarcel Moolenaar 
35431337658SMarcel Moolenaar 	    } else if (opts.o_help) {
35531337658SMarcel Moolenaar 		print_help();
35631337658SMarcel Moolenaar 		return 1;
35731337658SMarcel Moolenaar 
35831337658SMarcel Moolenaar 	    } else if (opts.o_not_first) {
35931337658SMarcel Moolenaar 		opt_not_first = 1;
36031337658SMarcel Moolenaar 
36131337658SMarcel Moolenaar 	    } else if (opts.o_xpath) {
36231337658SMarcel Moolenaar 		xo_set_flags(NULL, XOF_XPATH);
36331337658SMarcel Moolenaar 
36431337658SMarcel Moolenaar 	    } else if (opts.o_version) {
36531337658SMarcel Moolenaar 		print_version();
366545ddfbeSMarcel Moolenaar 		return 0;
36731337658SMarcel Moolenaar 
36831337658SMarcel Moolenaar 	    } else if (opts.o_warn_xml) {
36931337658SMarcel Moolenaar 		opt_warn = 1;
37031337658SMarcel Moolenaar 		xo_set_flags(NULL, XOF_WARN | XOF_WARN_XML);
37131337658SMarcel Moolenaar 
37231337658SMarcel Moolenaar 	    } else if (opts.o_wrap) {
37331337658SMarcel Moolenaar 		opt_wrapper = optarg;
37431337658SMarcel Moolenaar 
37531337658SMarcel Moolenaar 	    } else if (opts.o_top_wrap) {
37631337658SMarcel Moolenaar 		opt_top_wrap = 1;
37731337658SMarcel Moolenaar 
37831337658SMarcel Moolenaar 	    } else if (opts.o_open_list) {
37931337658SMarcel Moolenaar 		if (opt_name)
38031337658SMarcel Moolenaar 		    xo_errx(1, "only one open/close list/instance allowed: %s",
38131337658SMarcel Moolenaar 			    optarg);
38231337658SMarcel Moolenaar 
38331337658SMarcel Moolenaar 		opt_name = optarg;
38431337658SMarcel Moolenaar 		new_state = XSS_OPEN_LIST;
38531337658SMarcel Moolenaar 
38631337658SMarcel Moolenaar 	    } else if (opts.o_open_instance) {
38731337658SMarcel Moolenaar 		if (opt_name)
38831337658SMarcel Moolenaar 		    xo_errx(1, "only one open/close list/instance allowed: %s",
38931337658SMarcel Moolenaar 			    optarg);
39031337658SMarcel Moolenaar 
39131337658SMarcel Moolenaar 		opt_name = optarg;
39231337658SMarcel Moolenaar 		new_state = XSS_OPEN_INSTANCE;
39331337658SMarcel Moolenaar 
39431337658SMarcel Moolenaar 	    } else if (opts.o_close_list) {
39531337658SMarcel Moolenaar 		if (opt_name)
39631337658SMarcel Moolenaar 		    xo_errx(1, "only one open/close list/instance allowed: %s",
39731337658SMarcel Moolenaar 			    optarg);
39831337658SMarcel Moolenaar 
39931337658SMarcel Moolenaar 		opt_name = optarg;
40031337658SMarcel Moolenaar 		new_state = XSS_CLOSE_LIST;
40131337658SMarcel Moolenaar 
40231337658SMarcel Moolenaar 	    } else if (opts.o_close_instance) {
40331337658SMarcel Moolenaar 		if (opt_name)
40431337658SMarcel Moolenaar 		    xo_errx(1, "only one open/close list/instance allowed: %s",
40531337658SMarcel Moolenaar 			    optarg);
40631337658SMarcel Moolenaar 
40731337658SMarcel Moolenaar 		opt_name = optarg;
40831337658SMarcel Moolenaar 		new_state = XSS_CLOSE_INSTANCE;
40931337658SMarcel Moolenaar 
41031337658SMarcel Moolenaar 	    } else {
41131337658SMarcel Moolenaar 		print_help();
41231337658SMarcel Moolenaar 		return 1;
41331337658SMarcel Moolenaar 	    }
41431337658SMarcel Moolenaar 
41531337658SMarcel Moolenaar 	    bzero(&opts, sizeof(opts)); /* Reset all the options */
41631337658SMarcel Moolenaar 	    break;
41731337658SMarcel Moolenaar 
41831337658SMarcel Moolenaar 	default:
41931337658SMarcel Moolenaar 	    print_help();
42031337658SMarcel Moolenaar 	    return 1;
42131337658SMarcel Moolenaar 	}
42231337658SMarcel Moolenaar     }
42331337658SMarcel Moolenaar 
42431337658SMarcel Moolenaar     argc -= optind;
42531337658SMarcel Moolenaar     argv += optind;
42631337658SMarcel Moolenaar 
42731337658SMarcel Moolenaar     if (opt_options) {
42831337658SMarcel Moolenaar 	rc = xo_set_options(NULL, opt_options);
42931337658SMarcel Moolenaar 	if (rc < 0)
43031337658SMarcel Moolenaar 	    xo_errx(1, "invalid options: %s", opt_options);
43131337658SMarcel Moolenaar     }
43231337658SMarcel Moolenaar 
43331337658SMarcel Moolenaar     xo_set_formatter(NULL, formatter, checkpoint);
43431337658SMarcel Moolenaar     xo_set_flags(NULL, XOF_NO_VA_ARG | XOF_NO_TOP | XOF_NO_CLOSE);
43531337658SMarcel Moolenaar 
43631337658SMarcel Moolenaar     /*
43731337658SMarcel Moolenaar      * If we have some explicit state change, handle it
43831337658SMarcel Moolenaar      */
43931337658SMarcel Moolenaar     if (new_state) {
44031337658SMarcel Moolenaar 	if (opt_depth > 0)
441 	    xo_set_depth(NULL, opt_depth);
442 
443 	if (opt_not_first)
444 	    xo_set_flags(NULL, XOF_NOT_FIRST);
445 
446 	xo_explicit_transition(NULL, new_state, opt_name, 0);
447 	xo_finish();
448 	exit(0);
449     }
450 
451     fmt = *argv++;
452     if (opt_opener == NULL && opt_closer == NULL && fmt == NULL) {
453 	print_help();
454 	return 1;
455     }
456 
457     if (opt_top_wrap) {
458 	/* If we have a closing path, we'll be one extra level deeper */
459 	if (opt_closer && xo_get_style(NULL) == XO_STYLE_JSON)
460 	    opt_depth += 1;
461 	else
462 	    xo_clear_flags(NULL, XOF_NO_TOP);
463     }
464 
465     if (opt_closer) {
466 	opt_depth += 1;
467 	for (cp = opt_closer; cp && *cp; cp = np) {
468 	    np = strchr(cp, '/');
469 	    if (np == NULL)
470 		break;
471 	    np += 1;
472 	    opt_depth += 1;
473 	}
474     }
475 
476     if (opt_depth > 0)
477 	xo_set_depth(NULL, opt_depth);
478 
479     if (opt_not_first)
480 	xo_set_flags(NULL, XOF_NOT_FIRST);
481 
482     /* If there's an opening hierarchy, open each element as a container */
483     if (opt_opener) {
484 	for (cp = opt_opener; cp && *cp; cp = np) {
485 	    np = strchr(cp, '/');
486 	    if (np)
487 		*np = '\0';
488 	    xo_open_container(cp);
489 	    if (np)
490 		np += 1;
491 	}
492     }
493 
494     /* If there's an wrapper hierarchy, open each element as a container */
495     if (opt_wrapper) {
496 	for (cp = opt_wrapper; cp && *cp; cp = np) {
497 	    np = strchr(cp, '/');
498 	    if (np)
499 		*np = '\0';
500 	    xo_open_container(cp);
501 	    if (np)
502 		*np++ = '/';	/* Put it back */
503 	}
504     }
505 
506     if (opt_instance)
507 	xo_open_instance(opt_instance);
508 
509     /* If there's a format string, call xo_emit to emit the contents */
510     if (fmt && *fmt) {
511 	save_argv = argv;
512 	prep_arg(fmt);
513 	xo_emit(fmt);		/* This call does the real formatting */
514     }
515 
516     if (opt_instance)
517 	xo_close_instance(opt_instance);
518 
519     /* If there's an wrapper hierarchy, close each element's container */
520     while (opt_wrapper) {
521 	np = strrchr(opt_wrapper, '/');
522 	xo_close_container(np ? np + 1 : opt_wrapper);
523 	if (np)
524 	    *np = '\0';
525 	else
526 	    opt_wrapper = NULL;
527     }
528 
529     /* Remember to undo the depth before calling xo_finish() */
530     opt_depth = (opt_closer && opt_top_wrap) ? -1 : 0;
531 
532     /* If there's an closing hierarchy, close each element's container */
533     while (opt_closer) {
534 	np = strrchr(opt_closer, '/');
535 	xo_close_container(np ? np + 1 : opt_closer);
536 	if (np)
537 	    *np = '\0';
538 	else
539 	    opt_closer = NULL;
540     }
541 
542     /* If there's a closer and a wrapper, we need to clean it up */
543     if (opt_depth) {
544 	xo_set_depth(NULL, opt_depth);
545 	xo_clear_flags(NULL, XOF_NO_TOP);
546     }
547 
548     /* If we're wrapping the entire content, skip the closer */
549     if (opt_top_wrap && opt_opener)
550 	xo_set_flags(NULL, XOF_NO_TOP);
551 
552     xo_finish();
553 
554     return 0;
555 }
556