xref: /freebsd/contrib/libxo/xo/xo.c (revision 76afb20c58adb296f09857aed214b91464242264)
131337658SMarcel Moolenaar /*
25e203a9dSPhil Shafer  * Copyright (c) 2014-2019, Juniper Networks, Inc.
331337658SMarcel Moolenaar  * All rights reserved.
431337658SMarcel Moolenaar  * This SOFTWARE is licensed under the LICENSE provided in the
531337658SMarcel Moolenaar  * ../Copyright file. By downloading, installing, copying, or otherwise
631337658SMarcel Moolenaar  * using the SOFTWARE, you agree to be bound by the terms of that
731337658SMarcel Moolenaar  * LICENSE.
831337658SMarcel Moolenaar  * Phil Shafer, July 2014
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 *
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  */
948a6eceffSPhil Shafer static xo_ssize_t
formatter(xo_handle_t * xop,char * buf,xo_ssize_t bufsiz,const char * fmt,va_list vap UNUSED)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
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"
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"
204*76afb20cSPhil Shafer "    --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"
208406a584dSPhil Shafer "    --not-first           Indicate this object is not the first (JSON)\n"
20931337658SMarcel Moolenaar "    --open <path>         Open tags for the given path\n"
210406a584dSPhil Shafer "    --open-instance <name> Open an instance given by name\n"
211406a584dSPhil Shafer "    --open-list <name>   Open a list given by name\n"
212d1a0d267SMarcel 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"
217406a584dSPhil Shafer "    --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 {
227406a584dSPhil Shafer     int o_close_instance;
228406a584dSPhil Shafer     int o_close_list;
22931337658SMarcel Moolenaar     int o_depth;
23031337658SMarcel Moolenaar     int o_help;
23131337658SMarcel Moolenaar     int o_not_first;
232406a584dSPhil Shafer     int o_open_instance;
233406a584dSPhil Shafer     int o_open_list;
234406a584dSPhil Shafer     int o_top_wrap;
23531337658SMarcel Moolenaar     int o_version;
23631337658SMarcel Moolenaar     int o_warn_xml;
23731337658SMarcel Moolenaar     int o_wrap;
238406a584dSPhil Shafer     int o_xpath;
23931337658SMarcel Moolenaar } opts;
24031337658SMarcel Moolenaar 
24131337658SMarcel Moolenaar static struct option long_opts[] = {
24231337658SMarcel Moolenaar     { "close", required_argument, NULL, 'c' },
243406a584dSPhil Shafer     { "close-instance", required_argument, &opts.o_close_instance, 1 },
244406a584dSPhil Shafer     { "close-list", required_argument, &opts.o_close_list, 1 },
245406a584dSPhil Shafer     { "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' },
249*76afb20cSPhil Shafer     { "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' },
254406a584dSPhil Shafer     { "open-instance", required_argument, &opts.o_open_instance, 1 },
255406a584dSPhil Shafer     { "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' },
260406a584dSPhil Shafer     { "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 },
26331337658SMarcel 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;
276*76afb20cSPhil Shafer     char *opt_instance = NULL;
277406a584dSPhil Shafer     char *opt_name = NULL;
278406a584dSPhil Shafer     xo_state_t new_state = 0;
27931337658SMarcel Moolenaar     int opt_depth = 0;
28031337658SMarcel Moolenaar     int opt_not_first = 0;
281406a584dSPhil Shafer     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 
288406a584dSPhil Shafer     while ((rc = getopt_long(argc, argv, "Cc:HJl:O:o:ps:TXW",
28931337658SMarcel Moolenaar 				long_opts, NULL)) != -1) {
29031337658SMarcel Moolenaar 	switch (rc) {
291406a584dSPhil Shafer 	case 'C':
292406a584dSPhil Shafer 	    xo_set_flags(NULL, XOF_CONTINUATION);
293406a584dSPhil Shafer 	    break;
294406a584dSPhil Shafer 
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 
304*76afb20cSPhil Shafer 	case 'I':
305*76afb20cSPhil Shafer 	    opt_instance = optarg;
306*76afb20cSPhil Shafer 	    break;
307*76afb20cSPhil Shafer 
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();
36631337658SMarcel 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 
375406a584dSPhil Shafer 	    } else if (opts.o_top_wrap) {
376406a584dSPhil Shafer 		opt_top_wrap = 1;
377406a584dSPhil Shafer 
378406a584dSPhil Shafer 	    } else if (opts.o_open_list) {
379406a584dSPhil Shafer 		if (opt_name)
380406a584dSPhil Shafer 		    xo_errx(1, "only one open/close list/instance allowed: %s",
381406a584dSPhil Shafer 			    optarg);
382406a584dSPhil Shafer 
383406a584dSPhil Shafer 		opt_name = optarg;
384406a584dSPhil Shafer 		new_state = XSS_OPEN_LIST;
385406a584dSPhil Shafer 
386406a584dSPhil Shafer 	    } else if (opts.o_open_instance) {
387406a584dSPhil Shafer 		if (opt_name)
388406a584dSPhil Shafer 		    xo_errx(1, "only one open/close list/instance allowed: %s",
389406a584dSPhil Shafer 			    optarg);
390406a584dSPhil Shafer 
391406a584dSPhil Shafer 		opt_name = optarg;
392406a584dSPhil Shafer 		new_state = XSS_OPEN_INSTANCE;
393406a584dSPhil Shafer 
394406a584dSPhil Shafer 	    } else if (opts.o_close_list) {
395406a584dSPhil Shafer 		if (opt_name)
396406a584dSPhil Shafer 		    xo_errx(1, "only one open/close list/instance allowed: %s",
397406a584dSPhil Shafer 			    optarg);
398406a584dSPhil Shafer 
399406a584dSPhil Shafer 		opt_name = optarg;
400406a584dSPhil Shafer 		new_state = XSS_CLOSE_LIST;
401406a584dSPhil Shafer 
402406a584dSPhil Shafer 	    } else if (opts.o_close_instance) {
403406a584dSPhil Shafer 		if (opt_name)
404406a584dSPhil Shafer 		    xo_errx(1, "only one open/close list/instance allowed: %s",
405406a584dSPhil Shafer 			    optarg);
406406a584dSPhil Shafer 
407406a584dSPhil Shafer 		opt_name = optarg;
408406a584dSPhil Shafer 		new_state = XSS_CLOSE_INSTANCE;
409406a584dSPhil Shafer 
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);
434545ddfbeSMarcel Moolenaar     xo_set_flags(NULL, XOF_NO_VA_ARG | XOF_NO_TOP | XOF_NO_CLOSE);
43531337658SMarcel Moolenaar 
436406a584dSPhil Shafer     /*
437406a584dSPhil Shafer      * If we have some explicit state change, handle it
438406a584dSPhil Shafer      */
439406a584dSPhil Shafer     if (new_state) {
440406a584dSPhil Shafer 	if (opt_depth > 0)
441406a584dSPhil Shafer 	    xo_set_depth(NULL, opt_depth);
442406a584dSPhil Shafer 
443406a584dSPhil Shafer 	if (opt_not_first)
444406a584dSPhil Shafer 	    xo_set_flags(NULL, XOF_NOT_FIRST);
445406a584dSPhil Shafer 
446406a584dSPhil Shafer 	xo_explicit_transition(NULL, new_state, opt_name, 0);
447406a584dSPhil Shafer 	xo_finish();
448406a584dSPhil Shafer 	exit(0);
449406a584dSPhil Shafer     }
450406a584dSPhil Shafer 
45131337658SMarcel Moolenaar     fmt = *argv++;
45231337658SMarcel Moolenaar     if (opt_opener == NULL && opt_closer == NULL && fmt == NULL) {
45331337658SMarcel Moolenaar 	print_help();
45431337658SMarcel Moolenaar 	return 1;
45531337658SMarcel Moolenaar     }
45631337658SMarcel Moolenaar 
457406a584dSPhil Shafer     if (opt_top_wrap) {
458406a584dSPhil Shafer 	/* If we have a closing path, we'll be one extra level deeper */
459406a584dSPhil Shafer 	if (opt_closer && xo_get_style(NULL) == XO_STYLE_JSON)
460406a584dSPhil Shafer 	    opt_depth += 1;
461406a584dSPhil Shafer 	else
462406a584dSPhil Shafer 	    xo_clear_flags(NULL, XOF_NO_TOP);
463406a584dSPhil Shafer     }
46431337658SMarcel Moolenaar 
46531337658SMarcel Moolenaar     if (opt_closer) {
46631337658SMarcel Moolenaar 	opt_depth += 1;
46731337658SMarcel Moolenaar 	for (cp = opt_closer; cp && *cp; cp = np) {
46831337658SMarcel Moolenaar 	    np = strchr(cp, '/');
46931337658SMarcel Moolenaar 	    if (np == NULL)
47031337658SMarcel Moolenaar 		break;
47131337658SMarcel Moolenaar 	    np += 1;
47231337658SMarcel Moolenaar 	    opt_depth += 1;
47331337658SMarcel Moolenaar 	}
47431337658SMarcel Moolenaar     }
47531337658SMarcel Moolenaar 
47631337658SMarcel Moolenaar     if (opt_depth > 0)
47731337658SMarcel Moolenaar 	xo_set_depth(NULL, opt_depth);
47831337658SMarcel Moolenaar 
479406a584dSPhil Shafer     if (opt_not_first)
480406a584dSPhil Shafer 	xo_set_flags(NULL, XOF_NOT_FIRST);
481406a584dSPhil Shafer 
482406a584dSPhil Shafer     /* If there's an opening hierarchy, open each element as a container */
48331337658SMarcel Moolenaar     if (opt_opener) {
48431337658SMarcel Moolenaar 	for (cp = opt_opener; cp && *cp; cp = np) {
48531337658SMarcel Moolenaar 	    np = strchr(cp, '/');
48631337658SMarcel Moolenaar 	    if (np)
48731337658SMarcel Moolenaar 		*np = '\0';
48831337658SMarcel Moolenaar 	    xo_open_container(cp);
48931337658SMarcel Moolenaar 	    if (np)
490406a584dSPhil Shafer 		np += 1;
49131337658SMarcel Moolenaar 	}
49231337658SMarcel Moolenaar     }
49331337658SMarcel Moolenaar 
494406a584dSPhil Shafer     /* If there's an wrapper hierarchy, open each element as a container */
49531337658SMarcel Moolenaar     if (opt_wrapper) {
49631337658SMarcel Moolenaar 	for (cp = opt_wrapper; cp && *cp; cp = np) {
49731337658SMarcel Moolenaar 	    np = strchr(cp, '/');
49831337658SMarcel Moolenaar 	    if (np)
49931337658SMarcel Moolenaar 		*np = '\0';
50031337658SMarcel Moolenaar 	    xo_open_container(cp);
50131337658SMarcel Moolenaar 	    if (np)
502406a584dSPhil Shafer 		*np++ = '/';	/* Put it back */
50331337658SMarcel Moolenaar 	}
50431337658SMarcel Moolenaar     }
50531337658SMarcel Moolenaar 
506*76afb20cSPhil Shafer     if (opt_instance)
507*76afb20cSPhil Shafer 	xo_open_instance(opt_instance);
508*76afb20cSPhil Shafer 
509406a584dSPhil Shafer     /* If there's a format string, call xo_emit to emit the contents */
51031337658SMarcel Moolenaar     if (fmt && *fmt) {
51131337658SMarcel Moolenaar 	save_argv = argv;
51231337658SMarcel Moolenaar 	prep_arg(fmt);
513406a584dSPhil Shafer 	xo_emit(fmt);		/* This call does the real formatting */
51431337658SMarcel Moolenaar     }
51531337658SMarcel Moolenaar 
516*76afb20cSPhil Shafer     if (opt_instance)
517*76afb20cSPhil Shafer 	xo_close_instance(opt_instance);
518*76afb20cSPhil Shafer 
519406a584dSPhil Shafer     /* If there's an wrapper hierarchy, close each element's container */
52031337658SMarcel Moolenaar     while (opt_wrapper) {
52131337658SMarcel Moolenaar 	np = strrchr(opt_wrapper, '/');
52231337658SMarcel Moolenaar 	xo_close_container(np ? np + 1 : opt_wrapper);
52331337658SMarcel Moolenaar 	if (np)
52431337658SMarcel Moolenaar 	    *np = '\0';
52531337658SMarcel Moolenaar 	else
52631337658SMarcel Moolenaar 	    opt_wrapper = NULL;
52731337658SMarcel Moolenaar     }
52831337658SMarcel Moolenaar 
529406a584dSPhil Shafer     /* Remember to undo the depth before calling xo_finish() */
530406a584dSPhil Shafer     opt_depth = (opt_closer && opt_top_wrap) ? -1 : 0;
531406a584dSPhil Shafer 
532406a584dSPhil Shafer     /* If there's an closing hierarchy, close each element's container */
53331337658SMarcel Moolenaar     while (opt_closer) {
53431337658SMarcel Moolenaar 	np = strrchr(opt_closer, '/');
53531337658SMarcel Moolenaar 	xo_close_container(np ? np + 1 : opt_closer);
53631337658SMarcel Moolenaar 	if (np)
53731337658SMarcel Moolenaar 	    *np = '\0';
53831337658SMarcel Moolenaar 	else
53931337658SMarcel Moolenaar 	    opt_closer = NULL;
54031337658SMarcel Moolenaar     }
54131337658SMarcel Moolenaar 
542406a584dSPhil Shafer     /* If there's a closer and a wrapper, we need to clean it up */
543406a584dSPhil Shafer     if (opt_depth) {
544406a584dSPhil Shafer 	xo_set_depth(NULL, opt_depth);
545406a584dSPhil Shafer 	xo_clear_flags(NULL, XOF_NO_TOP);
546406a584dSPhil Shafer     }
547406a584dSPhil Shafer 
548406a584dSPhil Shafer     /* If we're wrapping the entire content, skip the closer */
549406a584dSPhil Shafer     if (opt_top_wrap && opt_opener)
550406a584dSPhil Shafer 	xo_set_flags(NULL, XOF_NO_TOP);
551406a584dSPhil Shafer 
55231337658SMarcel Moolenaar     xo_finish();
55331337658SMarcel Moolenaar 
55431337658SMarcel Moolenaar     return 0;
55531337658SMarcel Moolenaar }
556