xref: /freebsd/contrib/libxo/xo/xo.c (revision 5e203a9ddb201fcc70a0fc796120a3262b89a5ad)
131337658SMarcel Moolenaar /*
2*5e203a9dSPhil Shafer  * Copyright (c) 2014-2019, Juniper Networks, Inc.
331337658SMarcel Moolenaar  * All rights reserved.
431337658SMarcel Moolenaar  * This SOFTWARE is licensed under the LICENSE provided in the
531337658SMarcel Moolenaar  * ../Copyright file. By downloading, installing, copying, or otherwise
631337658SMarcel Moolenaar  * using the SOFTWARE, you agree to be bound by the terms of that
731337658SMarcel Moolenaar  * LICENSE.
831337658SMarcel Moolenaar  * Phil Shafer, July 2014
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"
18406a584dSPhil Shafer #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 *
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
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
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  */
948a6eceffSPhil Shafer static xo_ssize_t
958a6eceffSPhil Shafer formatter (xo_handle_t *xop, char *buf, xo_ssize_t bufsiz,
9631337658SMarcel Moolenaar 	   const char *fmt, va_list vap UNUSED)
9731337658SMarcel Moolenaar {
98d1a0d267SMarcel Moolenaar     int lflag UNUSED = 0;	/* Parse long flag, though currently ignored */
99d1a0d267SMarcel 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
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
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"
198406a584dSPhil Shafer "    --close-instance <name> Close an open instance name\n"
199406a584dSPhil Shafer "    --close-list <name>   Close an open list name\n"
200406a584dSPhil Shafer "    --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"
20431337658SMarcel Moolenaar "    --json OR -J          Generate JSON output\n"
20531337658SMarcel Moolenaar "    --leading-xpath <path> OR -l <path> "
20631337658SMarcel Moolenaar 	    "Add a prefix to generated XPaths (HTML)\n"
207406a584dSPhil Shafer "    --not-first           Indicate this object is not the first (JSON)\n"
20831337658SMarcel Moolenaar "    --open <path>         Open tags for the given path\n"
209406a584dSPhil Shafer "    --open-instance <name> Open an instance given by name\n"
210406a584dSPhil Shafer "    --open-list <name>   Open a list given by name\n"
211d1a0d267SMarcel Moolenaar "    --option <opts> -or -O <opts>  Give formatting options\n"
21231337658SMarcel Moolenaar "    --pretty OR -p        Make 'pretty' output (add indent, newlines)\n"
21331337658SMarcel Moolenaar "    --style <style> OR -s <style>  "
21431337658SMarcel Moolenaar 	    "Generate given style (xml, json, text, html)\n"
21531337658SMarcel Moolenaar "    --text OR -T          Generate text output (the default style)\n"
216406a584dSPhil Shafer "    --top-wrap            Generate a top-level object wrapper (JSON)\n"
21731337658SMarcel Moolenaar "    --version             Display version information\n"
21831337658SMarcel Moolenaar "    --warn OR -W          Display warnings in text on stderr\n"
21931337658SMarcel Moolenaar "    --warn-xml            Display warnings in xml on stdout\n"
22031337658SMarcel Moolenaar "    --wrap <path>         Wrap output in a set of containers\n"
22131337658SMarcel Moolenaar "    --xml OR -X           Generate XML output\n"
22231337658SMarcel Moolenaar "    --xpath               Add XPath data to HTML output\n");
22331337658SMarcel Moolenaar }
22431337658SMarcel Moolenaar 
22531337658SMarcel Moolenaar static struct opts {
226406a584dSPhil Shafer     int o_close_instance;
227406a584dSPhil Shafer     int o_close_list;
22831337658SMarcel Moolenaar     int o_depth;
22931337658SMarcel Moolenaar     int o_help;
23031337658SMarcel Moolenaar     int o_not_first;
231406a584dSPhil Shafer     int o_open_instance;
232406a584dSPhil Shafer     int o_open_list;
233406a584dSPhil Shafer     int o_top_wrap;
23431337658SMarcel Moolenaar     int o_version;
23531337658SMarcel Moolenaar     int o_warn_xml;
23631337658SMarcel Moolenaar     int o_wrap;
237406a584dSPhil Shafer     int o_xpath;
23831337658SMarcel Moolenaar } opts;
23931337658SMarcel Moolenaar 
24031337658SMarcel Moolenaar static struct option long_opts[] = {
24131337658SMarcel Moolenaar     { "close", required_argument, NULL, 'c' },
242406a584dSPhil Shafer     { "close-instance", required_argument, &opts.o_close_instance, 1 },
243406a584dSPhil Shafer     { "close-list", required_argument, &opts.o_close_list, 1 },
244406a584dSPhil Shafer     { "continuation", no_argument, NULL, 'C' },
24531337658SMarcel Moolenaar     { "depth", required_argument, &opts.o_depth, 1 },
24631337658SMarcel Moolenaar     { "help", no_argument, &opts.o_help, 1 },
24731337658SMarcel Moolenaar     { "html", no_argument, NULL, 'H' },
24831337658SMarcel Moolenaar     { "json", no_argument, NULL, 'J' },
24931337658SMarcel Moolenaar     { "leading-xpath", required_argument, NULL, 'l' },
25031337658SMarcel Moolenaar     { "not-first", no_argument, &opts.o_not_first, 1 },
25131337658SMarcel Moolenaar     { "open", required_argument, NULL, 'o' },
252406a584dSPhil Shafer     { "open-instance", required_argument, &opts.o_open_instance, 1 },
253406a584dSPhil Shafer     { "open-list", required_argument, &opts.o_open_list, 1 },
25431337658SMarcel Moolenaar     { "option", required_argument, NULL, 'O' },
25531337658SMarcel Moolenaar     { "pretty", no_argument, NULL, 'p' },
25631337658SMarcel Moolenaar     { "style", required_argument, NULL, 's' },
25731337658SMarcel Moolenaar     { "text", no_argument, NULL, 'T' },
258406a584dSPhil Shafer     { "top-wrap", no_argument, &opts.o_top_wrap, 1 },
25931337658SMarcel Moolenaar     { "xml", no_argument, NULL, 'X' },
26031337658SMarcel Moolenaar     { "xpath", no_argument, &opts.o_xpath, 1 },
26131337658SMarcel Moolenaar     { "version", no_argument, &opts.o_version, 1 },
26231337658SMarcel Moolenaar     { "warn", no_argument, NULL, 'W' },
26331337658SMarcel Moolenaar     { "warn-xml", no_argument, &opts.o_warn_xml, 1 },
26431337658SMarcel Moolenaar     { "wrap", required_argument, &opts.o_wrap, 1 },
26531337658SMarcel Moolenaar     { NULL, 0, NULL, 0 }
26631337658SMarcel Moolenaar };
26731337658SMarcel Moolenaar 
26831337658SMarcel Moolenaar int
26931337658SMarcel Moolenaar main (int argc UNUSED, char **argv)
27031337658SMarcel Moolenaar {
27131337658SMarcel Moolenaar     char *fmt = NULL, *cp, *np;
27231337658SMarcel Moolenaar     char *opt_opener = NULL, *opt_closer = NULL, *opt_wrapper = NULL;
27331337658SMarcel Moolenaar     char *opt_options = NULL;
274406a584dSPhil Shafer     char *opt_name = NULL;
275406a584dSPhil Shafer     xo_state_t new_state = 0;
27631337658SMarcel Moolenaar     int opt_depth = 0;
27731337658SMarcel Moolenaar     int opt_not_first = 0;
278406a584dSPhil Shafer     int opt_top_wrap = 0;
27931337658SMarcel Moolenaar     int rc;
28031337658SMarcel Moolenaar 
28131337658SMarcel Moolenaar     argc = xo_parse_args(argc, argv);
28231337658SMarcel Moolenaar     if (argc < 0)
28331337658SMarcel Moolenaar 	return 1;
28431337658SMarcel Moolenaar 
285406a584dSPhil Shafer     while ((rc = getopt_long(argc, argv, "Cc:HJl:O:o:ps:TXW",
28631337658SMarcel Moolenaar 				long_opts, NULL)) != -1) {
28731337658SMarcel Moolenaar 	switch (rc) {
288406a584dSPhil Shafer 	case 'C':
289406a584dSPhil Shafer 	    xo_set_flags(NULL, XOF_CONTINUATION);
290406a584dSPhil Shafer 	    break;
291406a584dSPhil Shafer 
29231337658SMarcel Moolenaar 	case 'c':
29331337658SMarcel Moolenaar 	    opt_closer = optarg;
29431337658SMarcel Moolenaar 	    xo_set_flags(NULL, XOF_IGNORE_CLOSE);
29531337658SMarcel Moolenaar 	    break;
29631337658SMarcel Moolenaar 
29731337658SMarcel Moolenaar 	case 'H':
29831337658SMarcel Moolenaar 	    xo_set_style(NULL, XO_STYLE_HTML);
29931337658SMarcel Moolenaar 	    break;
30031337658SMarcel Moolenaar 
30131337658SMarcel Moolenaar 	case 'J':
30231337658SMarcel Moolenaar 	    xo_set_style(NULL, XO_STYLE_JSON);
30331337658SMarcel Moolenaar 	    break;
30431337658SMarcel Moolenaar 
30531337658SMarcel Moolenaar 	case 'l':
30631337658SMarcel Moolenaar 	    xo_set_leading_xpath(NULL, optarg);
30731337658SMarcel Moolenaar 	    break;
30831337658SMarcel Moolenaar 
30931337658SMarcel Moolenaar 	case 'O':
31031337658SMarcel Moolenaar 	    opt_options = optarg;
31131337658SMarcel Moolenaar 	    break;
31231337658SMarcel Moolenaar 
31331337658SMarcel Moolenaar 	case 'o':
31431337658SMarcel Moolenaar 	    opt_opener = optarg;
31531337658SMarcel Moolenaar 	    break;
31631337658SMarcel Moolenaar 
31731337658SMarcel Moolenaar 	case 'p':
31831337658SMarcel Moolenaar 	    xo_set_flags(NULL, XOF_PRETTY);
31931337658SMarcel Moolenaar 	    break;
32031337658SMarcel Moolenaar 
32131337658SMarcel Moolenaar 	case 's':
32231337658SMarcel Moolenaar 	    if (xo_set_style_name(NULL, optarg) < 0)
32331337658SMarcel Moolenaar 		xo_errx(1, "unknown style: %s", optarg);
32431337658SMarcel Moolenaar 	    break;
32531337658SMarcel Moolenaar 
32631337658SMarcel Moolenaar 	case 'T':
32731337658SMarcel Moolenaar 	    xo_set_style(NULL, XO_STYLE_TEXT);
32831337658SMarcel Moolenaar 	    break;
32931337658SMarcel Moolenaar 
33031337658SMarcel Moolenaar 	case 'X':
33131337658SMarcel Moolenaar 	    xo_set_style(NULL, XO_STYLE_XML);
33231337658SMarcel Moolenaar 	    break;
33331337658SMarcel Moolenaar 
33431337658SMarcel Moolenaar 	case 'W':
33531337658SMarcel Moolenaar 	    opt_warn = 1;
33631337658SMarcel Moolenaar 	    xo_set_flags(NULL, XOF_WARN);
33731337658SMarcel Moolenaar 	    break;
33831337658SMarcel Moolenaar 
33931337658SMarcel Moolenaar 	case ':':
34031337658SMarcel Moolenaar 	    xo_errx(1, "missing argument");
34131337658SMarcel Moolenaar 	    break;
34231337658SMarcel Moolenaar 
34331337658SMarcel Moolenaar 	case 0:
34431337658SMarcel Moolenaar 	    if (opts.o_depth) {
34531337658SMarcel Moolenaar 		opt_depth = atoi(optarg);
34631337658SMarcel Moolenaar 
34731337658SMarcel Moolenaar 	    } else if (opts.o_help) {
34831337658SMarcel Moolenaar 		print_help();
34931337658SMarcel Moolenaar 		return 1;
35031337658SMarcel Moolenaar 
35131337658SMarcel Moolenaar 	    } else if (opts.o_not_first) {
35231337658SMarcel Moolenaar 		opt_not_first = 1;
35331337658SMarcel Moolenaar 
35431337658SMarcel Moolenaar 	    } else if (opts.o_xpath) {
35531337658SMarcel Moolenaar 		xo_set_flags(NULL, XOF_XPATH);
35631337658SMarcel Moolenaar 
35731337658SMarcel Moolenaar 	    } else if (opts.o_version) {
35831337658SMarcel Moolenaar 		print_version();
35931337658SMarcel Moolenaar 		return 0;
36031337658SMarcel Moolenaar 
36131337658SMarcel Moolenaar 	    } else if (opts.o_warn_xml) {
36231337658SMarcel Moolenaar 		opt_warn = 1;
36331337658SMarcel Moolenaar 		xo_set_flags(NULL, XOF_WARN | XOF_WARN_XML);
36431337658SMarcel Moolenaar 
36531337658SMarcel Moolenaar 	    } else if (opts.o_wrap) {
36631337658SMarcel Moolenaar 		opt_wrapper = optarg;
36731337658SMarcel Moolenaar 
368406a584dSPhil Shafer 	    } else if (opts.o_top_wrap) {
369406a584dSPhil Shafer 		opt_top_wrap = 1;
370406a584dSPhil Shafer 
371406a584dSPhil Shafer 	    } else if (opts.o_open_list) {
372406a584dSPhil Shafer 		if (opt_name)
373406a584dSPhil Shafer 		    xo_errx(1, "only one open/close list/instance allowed: %s",
374406a584dSPhil Shafer 			    optarg);
375406a584dSPhil Shafer 
376406a584dSPhil Shafer 		opt_name = optarg;
377406a584dSPhil Shafer 		new_state = XSS_OPEN_LIST;
378406a584dSPhil Shafer 
379406a584dSPhil Shafer 	    } else if (opts.o_open_instance) {
380406a584dSPhil Shafer 		if (opt_name)
381406a584dSPhil Shafer 		    xo_errx(1, "only one open/close list/instance allowed: %s",
382406a584dSPhil Shafer 			    optarg);
383406a584dSPhil Shafer 
384406a584dSPhil Shafer 		opt_name = optarg;
385406a584dSPhil Shafer 		new_state = XSS_OPEN_INSTANCE;
386406a584dSPhil Shafer 
387406a584dSPhil Shafer 	    } else if (opts.o_close_list) {
388406a584dSPhil Shafer 		if (opt_name)
389406a584dSPhil Shafer 		    xo_errx(1, "only one open/close list/instance allowed: %s",
390406a584dSPhil Shafer 			    optarg);
391406a584dSPhil Shafer 
392406a584dSPhil Shafer 		opt_name = optarg;
393406a584dSPhil Shafer 		new_state = XSS_CLOSE_LIST;
394406a584dSPhil Shafer 
395406a584dSPhil Shafer 	    } else if (opts.o_close_instance) {
396406a584dSPhil Shafer 		if (opt_name)
397406a584dSPhil Shafer 		    xo_errx(1, "only one open/close list/instance allowed: %s",
398406a584dSPhil Shafer 			    optarg);
399406a584dSPhil Shafer 
400406a584dSPhil Shafer 		opt_name = optarg;
401406a584dSPhil Shafer 		new_state = XSS_CLOSE_INSTANCE;
402406a584dSPhil Shafer 
40331337658SMarcel Moolenaar 	    } else {
40431337658SMarcel Moolenaar 		print_help();
40531337658SMarcel Moolenaar 		return 1;
40631337658SMarcel Moolenaar 	    }
40731337658SMarcel Moolenaar 
40831337658SMarcel Moolenaar 	    bzero(&opts, sizeof(opts)); /* Reset all the options */
40931337658SMarcel Moolenaar 	    break;
41031337658SMarcel Moolenaar 
41131337658SMarcel Moolenaar 	default:
41231337658SMarcel Moolenaar 	    print_help();
41331337658SMarcel Moolenaar 	    return 1;
41431337658SMarcel Moolenaar 	}
41531337658SMarcel Moolenaar     }
41631337658SMarcel Moolenaar 
41731337658SMarcel Moolenaar     argc -= optind;
41831337658SMarcel Moolenaar     argv += optind;
41931337658SMarcel Moolenaar 
42031337658SMarcel Moolenaar     if (opt_options) {
42131337658SMarcel Moolenaar 	rc = xo_set_options(NULL, opt_options);
42231337658SMarcel Moolenaar 	if (rc < 0)
42331337658SMarcel Moolenaar 	    xo_errx(1, "invalid options: %s", opt_options);
42431337658SMarcel Moolenaar     }
42531337658SMarcel Moolenaar 
42631337658SMarcel Moolenaar     xo_set_formatter(NULL, formatter, checkpoint);
427545ddfbeSMarcel Moolenaar     xo_set_flags(NULL, XOF_NO_VA_ARG | XOF_NO_TOP | XOF_NO_CLOSE);
42831337658SMarcel Moolenaar 
429406a584dSPhil Shafer     /*
430406a584dSPhil Shafer      * If we have some explicit state change, handle it
431406a584dSPhil Shafer      */
432406a584dSPhil Shafer     if (new_state) {
433406a584dSPhil Shafer 	if (opt_depth > 0)
434406a584dSPhil Shafer 	    xo_set_depth(NULL, opt_depth);
435406a584dSPhil Shafer 
436406a584dSPhil Shafer 	if (opt_not_first)
437406a584dSPhil Shafer 	    xo_set_flags(NULL, XOF_NOT_FIRST);
438406a584dSPhil Shafer 
439406a584dSPhil Shafer 	xo_explicit_transition(NULL, new_state, opt_name, 0);
440406a584dSPhil Shafer 	xo_finish();
441406a584dSPhil Shafer 	exit(0);
442406a584dSPhil Shafer     }
443406a584dSPhil Shafer 
44431337658SMarcel Moolenaar     fmt = *argv++;
44531337658SMarcel Moolenaar     if (opt_opener == NULL && opt_closer == NULL && fmt == NULL) {
44631337658SMarcel Moolenaar 	print_help();
44731337658SMarcel Moolenaar 	return 1;
44831337658SMarcel Moolenaar     }
44931337658SMarcel Moolenaar 
450406a584dSPhil Shafer     if (opt_top_wrap) {
451406a584dSPhil Shafer 	/* If we have a closing path, we'll be one extra level deeper */
452406a584dSPhil Shafer 	if (opt_closer && xo_get_style(NULL) == XO_STYLE_JSON)
453406a584dSPhil Shafer 	    opt_depth += 1;
454406a584dSPhil Shafer 	else
455406a584dSPhil Shafer 	    xo_clear_flags(NULL, XOF_NO_TOP);
456406a584dSPhil Shafer     }
45731337658SMarcel Moolenaar 
45831337658SMarcel Moolenaar     if (opt_closer) {
45931337658SMarcel Moolenaar 	opt_depth += 1;
46031337658SMarcel Moolenaar 	for (cp = opt_closer; cp && *cp; cp = np) {
46131337658SMarcel Moolenaar 	    np = strchr(cp, '/');
46231337658SMarcel Moolenaar 	    if (np == NULL)
46331337658SMarcel Moolenaar 		break;
46431337658SMarcel Moolenaar 	    np += 1;
46531337658SMarcel Moolenaar 	    opt_depth += 1;
46631337658SMarcel Moolenaar 	}
46731337658SMarcel Moolenaar     }
46831337658SMarcel Moolenaar 
46931337658SMarcel Moolenaar     if (opt_depth > 0)
47031337658SMarcel Moolenaar 	xo_set_depth(NULL, opt_depth);
47131337658SMarcel Moolenaar 
472406a584dSPhil Shafer     if (opt_not_first)
473406a584dSPhil Shafer 	xo_set_flags(NULL, XOF_NOT_FIRST);
474406a584dSPhil Shafer 
475406a584dSPhil Shafer     /* If there's an opening hierarchy, open each element as a container */
47631337658SMarcel Moolenaar     if (opt_opener) {
47731337658SMarcel Moolenaar 	for (cp = opt_opener; cp && *cp; cp = np) {
47831337658SMarcel Moolenaar 	    np = strchr(cp, '/');
47931337658SMarcel Moolenaar 	    if (np)
48031337658SMarcel Moolenaar 		*np = '\0';
48131337658SMarcel Moolenaar 	    xo_open_container(cp);
48231337658SMarcel Moolenaar 	    if (np)
483406a584dSPhil Shafer 		np += 1;
48431337658SMarcel Moolenaar 	}
48531337658SMarcel Moolenaar     }
48631337658SMarcel Moolenaar 
487406a584dSPhil Shafer     /* If there's an wrapper hierarchy, open each element as a container */
48831337658SMarcel Moolenaar     if (opt_wrapper) {
48931337658SMarcel Moolenaar 	for (cp = opt_wrapper; cp && *cp; cp = np) {
49031337658SMarcel Moolenaar 	    np = strchr(cp, '/');
49131337658SMarcel Moolenaar 	    if (np)
49231337658SMarcel Moolenaar 		*np = '\0';
49331337658SMarcel Moolenaar 	    xo_open_container(cp);
49431337658SMarcel Moolenaar 	    if (np)
495406a584dSPhil Shafer 		*np++ = '/';	/* Put it back */
49631337658SMarcel Moolenaar 	}
49731337658SMarcel Moolenaar     }
49831337658SMarcel Moolenaar 
499406a584dSPhil Shafer     /* If there's a format string, call xo_emit to emit the contents */
50031337658SMarcel Moolenaar     if (fmt && *fmt) {
50131337658SMarcel Moolenaar 	save_argv = argv;
50231337658SMarcel Moolenaar 	prep_arg(fmt);
503406a584dSPhil Shafer 	xo_emit(fmt);		/* This call does the real formatting */
50431337658SMarcel Moolenaar     }
50531337658SMarcel Moolenaar 
506406a584dSPhil Shafer     /* If there's an wrapper hierarchy, close each element's container */
50731337658SMarcel Moolenaar     while (opt_wrapper) {
50831337658SMarcel Moolenaar 	np = strrchr(opt_wrapper, '/');
50931337658SMarcel Moolenaar 	xo_close_container(np ? np + 1 : opt_wrapper);
51031337658SMarcel Moolenaar 	if (np)
51131337658SMarcel Moolenaar 	    *np = '\0';
51231337658SMarcel Moolenaar 	else
51331337658SMarcel Moolenaar 	    opt_wrapper = NULL;
51431337658SMarcel Moolenaar     }
51531337658SMarcel Moolenaar 
516406a584dSPhil Shafer     /* Remember to undo the depth before calling xo_finish() */
517406a584dSPhil Shafer     opt_depth = (opt_closer && opt_top_wrap) ? -1 : 0;
518406a584dSPhil Shafer 
519406a584dSPhil Shafer     /* If there's an closing hierarchy, close each element's container */
52031337658SMarcel Moolenaar     while (opt_closer) {
52131337658SMarcel Moolenaar 	np = strrchr(opt_closer, '/');
52231337658SMarcel Moolenaar 	xo_close_container(np ? np + 1 : opt_closer);
52331337658SMarcel Moolenaar 	if (np)
52431337658SMarcel Moolenaar 	    *np = '\0';
52531337658SMarcel Moolenaar 	else
52631337658SMarcel Moolenaar 	    opt_closer = NULL;
52731337658SMarcel Moolenaar     }
52831337658SMarcel Moolenaar 
529406a584dSPhil Shafer     /* If there's a closer and a wrapper, we need to clean it up */
530406a584dSPhil Shafer     if (opt_depth) {
531406a584dSPhil Shafer 	xo_set_depth(NULL, opt_depth);
532406a584dSPhil Shafer 	xo_clear_flags(NULL, XOF_NO_TOP);
533406a584dSPhil Shafer     }
534406a584dSPhil Shafer 
535406a584dSPhil Shafer     /* If we're wrapping the entire content, skip the closer */
536406a584dSPhil Shafer     if (opt_top_wrap && opt_opener)
537406a584dSPhil Shafer 	xo_set_flags(NULL, XOF_NO_TOP);
538406a584dSPhil Shafer 
53931337658SMarcel Moolenaar     xo_finish();
54031337658SMarcel Moolenaar 
54131337658SMarcel Moolenaar     return 0;
54231337658SMarcel Moolenaar }
543