131337658SMarcel Moolenaar /* 231337658SMarcel Moolenaar * Copyright (c) 2014, 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 1931337658SMarcel Moolenaar #include <getopt.h> /* Include after xo.h for testing */ 2031337658SMarcel Moolenaar 2131337658SMarcel Moolenaar #ifndef UNUSED 2231337658SMarcel Moolenaar #define UNUSED __attribute__ ((__unused__)) 2331337658SMarcel Moolenaar #endif /* UNUSED */ 2431337658SMarcel Moolenaar 2531337658SMarcel Moolenaar static int opt_warn; /* Enable warnings */ 2631337658SMarcel Moolenaar 2731337658SMarcel Moolenaar static char **save_argv; 2831337658SMarcel Moolenaar static char **checkpoint_argv; 2931337658SMarcel Moolenaar 3031337658SMarcel Moolenaar static char * 3131337658SMarcel Moolenaar next_arg (void) 3231337658SMarcel Moolenaar { 3331337658SMarcel Moolenaar char *cp = *save_argv; 3431337658SMarcel Moolenaar 3531337658SMarcel Moolenaar if (cp == NULL) 3631337658SMarcel Moolenaar xo_errx(1, "missing argument"); 3731337658SMarcel Moolenaar 3831337658SMarcel Moolenaar save_argv += 1; 3931337658SMarcel Moolenaar return cp; 4031337658SMarcel Moolenaar } 4131337658SMarcel Moolenaar 4231337658SMarcel Moolenaar static void 4331337658SMarcel Moolenaar prep_arg (char *fmt) 4431337658SMarcel Moolenaar { 4531337658SMarcel Moolenaar char *cp, *fp; 4631337658SMarcel Moolenaar 4731337658SMarcel Moolenaar for (cp = fp = fmt; *cp; cp++, fp++) { 4831337658SMarcel Moolenaar if (*cp != '\\') { 4931337658SMarcel Moolenaar if (cp != fp) 5031337658SMarcel Moolenaar *fp = *cp; 5131337658SMarcel Moolenaar continue; 5231337658SMarcel Moolenaar } 5331337658SMarcel Moolenaar 5431337658SMarcel Moolenaar switch (*++cp) { 5531337658SMarcel Moolenaar case 'n': 5631337658SMarcel Moolenaar *fp = '\n'; 5731337658SMarcel Moolenaar break; 5831337658SMarcel Moolenaar 5931337658SMarcel Moolenaar case 'r': 6031337658SMarcel Moolenaar *fp = '\r'; 6131337658SMarcel Moolenaar break; 6231337658SMarcel Moolenaar 6331337658SMarcel Moolenaar case 'b': 6431337658SMarcel Moolenaar *fp = '\b'; 6531337658SMarcel Moolenaar break; 6631337658SMarcel Moolenaar 6731337658SMarcel Moolenaar case 'e': 6831337658SMarcel Moolenaar *fp = '\e'; 6931337658SMarcel Moolenaar break; 7031337658SMarcel Moolenaar 7131337658SMarcel Moolenaar default: 7231337658SMarcel Moolenaar *fp = *cp; 7331337658SMarcel Moolenaar } 7431337658SMarcel Moolenaar } 7531337658SMarcel Moolenaar 7631337658SMarcel Moolenaar *fp = '\0'; 7731337658SMarcel Moolenaar } 7831337658SMarcel Moolenaar 7931337658SMarcel Moolenaar static void 8031337658SMarcel Moolenaar checkpoint (xo_handle_t *xop UNUSED, va_list vap UNUSED, int restore) 8131337658SMarcel Moolenaar { 8231337658SMarcel Moolenaar if (restore) 8331337658SMarcel Moolenaar save_argv = checkpoint_argv; 8431337658SMarcel Moolenaar else 8531337658SMarcel Moolenaar checkpoint_argv = save_argv; 8631337658SMarcel Moolenaar } 8731337658SMarcel Moolenaar 8831337658SMarcel Moolenaar /* 8931337658SMarcel Moolenaar * Our custom formatter is responsible for combining format string pieces 9031337658SMarcel Moolenaar * with our command line arguments to build strings. This involves faking 9131337658SMarcel Moolenaar * some printf-style logic. 9231337658SMarcel Moolenaar */ 93*8a6eceffSPhil Shafer static xo_ssize_t 94*8a6eceffSPhil Shafer formatter (xo_handle_t *xop, char *buf, xo_ssize_t bufsiz, 9531337658SMarcel Moolenaar const char *fmt, va_list vap UNUSED) 9631337658SMarcel Moolenaar { 97d1a0d267SMarcel Moolenaar int lflag UNUSED = 0; /* Parse long flag, though currently ignored */ 98d1a0d267SMarcel Moolenaar int hflag = 0, jflag = 0, tflag = 0, 9931337658SMarcel Moolenaar zflag = 0, qflag = 0, star1 = 0, star2 = 0; 10031337658SMarcel Moolenaar int rc = 0; 10131337658SMarcel Moolenaar int w1 = 0, w2 = 0; 10231337658SMarcel Moolenaar const char *cp; 10331337658SMarcel Moolenaar 10431337658SMarcel Moolenaar for (cp = fmt + 1; *cp; cp++) { 10531337658SMarcel Moolenaar if (*cp == 'l') 10631337658SMarcel Moolenaar lflag += 1; 10731337658SMarcel Moolenaar else if (*cp == 'h') 10831337658SMarcel Moolenaar hflag += 1; 10931337658SMarcel Moolenaar else if (*cp == 'j') 11031337658SMarcel Moolenaar jflag += 1; 11131337658SMarcel Moolenaar else if (*cp == 't') 11231337658SMarcel Moolenaar tflag += 1; 11331337658SMarcel Moolenaar else if (*cp == 'z') 11431337658SMarcel Moolenaar zflag += 1; 11531337658SMarcel Moolenaar else if (*cp == 'q') 11631337658SMarcel Moolenaar qflag += 1; 11731337658SMarcel Moolenaar else if (*cp == '*') { 11831337658SMarcel Moolenaar if (star1 == 0) 11931337658SMarcel Moolenaar star1 = 1; 12031337658SMarcel Moolenaar else 12131337658SMarcel Moolenaar star2 = 1; 12231337658SMarcel Moolenaar } else if (strchr("diouxXDOUeEfFgGaAcCsSp", *cp) != NULL) 12331337658SMarcel Moolenaar break; 12431337658SMarcel Moolenaar else if (*cp == 'n' || *cp == 'v') { 12531337658SMarcel Moolenaar if (opt_warn) 12631337658SMarcel Moolenaar xo_error_h(xop, "unsupported format: '%s'", fmt); 12731337658SMarcel Moolenaar return -1; 12831337658SMarcel Moolenaar } 12931337658SMarcel Moolenaar } 13031337658SMarcel Moolenaar 13131337658SMarcel Moolenaar char fc = *cp; 13231337658SMarcel Moolenaar 13331337658SMarcel Moolenaar /* Handle "%*.*s" */ 13431337658SMarcel Moolenaar if (star1) 13531337658SMarcel Moolenaar w1 = strtol(next_arg(), NULL, 0); 13631337658SMarcel Moolenaar if (star2 > 1) 13731337658SMarcel Moolenaar w2 = strtol(next_arg(), NULL, 0); 13831337658SMarcel Moolenaar 13931337658SMarcel Moolenaar if (fc == 'D' || fc == 'O' || fc == 'U') 14031337658SMarcel Moolenaar lflag = 1; 14131337658SMarcel Moolenaar 14231337658SMarcel Moolenaar if (strchr("diD", fc) != NULL) { 14331337658SMarcel Moolenaar long long value = strtoll(next_arg(), NULL, 0); 14431337658SMarcel Moolenaar if (star1 && star2) 14531337658SMarcel Moolenaar rc = snprintf(buf, bufsiz, fmt, w1, w2, value); 14631337658SMarcel Moolenaar else if (star1) 14731337658SMarcel Moolenaar rc = snprintf(buf, bufsiz, fmt, w1, value); 14831337658SMarcel Moolenaar else 14931337658SMarcel Moolenaar rc = snprintf(buf, bufsiz, fmt, value); 15031337658SMarcel Moolenaar 15131337658SMarcel Moolenaar } else if (strchr("ouxXOUp", fc) != NULL) { 15231337658SMarcel Moolenaar unsigned long long value = strtoull(next_arg(), NULL, 0); 15331337658SMarcel Moolenaar if (star1 && star2) 15431337658SMarcel Moolenaar rc = snprintf(buf, bufsiz, fmt, w1, w2, value); 15531337658SMarcel Moolenaar else if (star1) 15631337658SMarcel Moolenaar rc = snprintf(buf, bufsiz, fmt, w1, value); 15731337658SMarcel Moolenaar else 15831337658SMarcel Moolenaar rc = snprintf(buf, bufsiz, fmt, value); 15931337658SMarcel Moolenaar 16031337658SMarcel Moolenaar } else if (strchr("eEfFgGaA", fc) != NULL) { 16131337658SMarcel Moolenaar double value = strtold(next_arg(), NULL); 16231337658SMarcel Moolenaar if (star1 && star2) 16331337658SMarcel Moolenaar rc = snprintf(buf, bufsiz, fmt, w1, w2, value); 16431337658SMarcel Moolenaar else if (star1) 16531337658SMarcel Moolenaar rc = snprintf(buf, bufsiz, fmt, w1, value); 16631337658SMarcel Moolenaar else 16731337658SMarcel Moolenaar rc = snprintf(buf, bufsiz, fmt, value); 16831337658SMarcel Moolenaar 16931337658SMarcel Moolenaar } else if (fc == 'C' || fc == 'c' || fc == 'S' || fc == 's') { 17031337658SMarcel Moolenaar char *value = next_arg(); 17131337658SMarcel Moolenaar if (star1 && star2) 17231337658SMarcel Moolenaar rc = snprintf(buf, bufsiz, fmt, w1, w2, value); 17331337658SMarcel Moolenaar else if (star1) 17431337658SMarcel Moolenaar rc = snprintf(buf, bufsiz, fmt, w1, value); 17531337658SMarcel Moolenaar else 17631337658SMarcel Moolenaar rc = snprintf(buf, bufsiz, fmt, value); 17731337658SMarcel Moolenaar } 17831337658SMarcel Moolenaar 17931337658SMarcel Moolenaar return rc; 18031337658SMarcel Moolenaar } 18131337658SMarcel Moolenaar 18231337658SMarcel Moolenaar static void 18331337658SMarcel Moolenaar print_version (void) 18431337658SMarcel Moolenaar { 18531337658SMarcel Moolenaar fprintf(stderr, "libxo version %s%s\n", 18631337658SMarcel Moolenaar xo_version, xo_version_extra); 18731337658SMarcel Moolenaar fprintf(stderr, "xo version %s%s\n", 18831337658SMarcel Moolenaar LIBXO_VERSION, LIBXO_VERSION_EXTRA); 18931337658SMarcel Moolenaar } 19031337658SMarcel Moolenaar 19131337658SMarcel Moolenaar static void 19231337658SMarcel Moolenaar print_help (void) 19331337658SMarcel Moolenaar { 19431337658SMarcel Moolenaar fprintf(stderr, 19531337658SMarcel Moolenaar "Usage: xo [options] format [fields]\n" 19631337658SMarcel Moolenaar " --close <path> Close tags for the given path\n" 19731337658SMarcel Moolenaar " --depth <num> Set the depth for pretty printing\n" 19831337658SMarcel Moolenaar " --help Display this help text\n" 19931337658SMarcel Moolenaar " --html OR -H Generate HTML output\n" 20031337658SMarcel Moolenaar " --json OR -J Generate JSON output\n" 20131337658SMarcel Moolenaar " --leading-xpath <path> OR -l <path> " 20231337658SMarcel Moolenaar "Add a prefix to generated XPaths (HTML)\n" 20331337658SMarcel Moolenaar " --open <path> Open tags for the given path\n" 204d1a0d267SMarcel Moolenaar " --option <opts> -or -O <opts> Give formatting options\n" 20531337658SMarcel Moolenaar " --pretty OR -p Make 'pretty' output (add indent, newlines)\n" 20631337658SMarcel Moolenaar " --style <style> OR -s <style> " 20731337658SMarcel Moolenaar "Generate given style (xml, json, text, html)\n" 20831337658SMarcel Moolenaar " --text OR -T Generate text output (the default style)\n" 20931337658SMarcel Moolenaar " --version Display version information\n" 21031337658SMarcel Moolenaar " --warn OR -W Display warnings in text on stderr\n" 21131337658SMarcel Moolenaar " --warn-xml Display warnings in xml on stdout\n" 21231337658SMarcel Moolenaar " --wrap <path> Wrap output in a set of containers\n" 21331337658SMarcel Moolenaar " --xml OR -X Generate XML output\n" 21431337658SMarcel Moolenaar " --xpath Add XPath data to HTML output\n"); 21531337658SMarcel Moolenaar } 21631337658SMarcel Moolenaar 21731337658SMarcel Moolenaar static struct opts { 21831337658SMarcel Moolenaar int o_depth; 21931337658SMarcel Moolenaar int o_help; 22031337658SMarcel Moolenaar int o_not_first; 22131337658SMarcel Moolenaar int o_xpath; 22231337658SMarcel Moolenaar int o_version; 22331337658SMarcel Moolenaar int o_warn_xml; 22431337658SMarcel Moolenaar int o_wrap; 22531337658SMarcel Moolenaar } opts; 22631337658SMarcel Moolenaar 22731337658SMarcel Moolenaar static struct option long_opts[] = { 22831337658SMarcel Moolenaar { "close", required_argument, NULL, 'c' }, 22931337658SMarcel Moolenaar { "depth", required_argument, &opts.o_depth, 1 }, 23031337658SMarcel Moolenaar { "help", no_argument, &opts.o_help, 1 }, 23131337658SMarcel Moolenaar { "html", no_argument, NULL, 'H' }, 23231337658SMarcel Moolenaar { "json", no_argument, NULL, 'J' }, 23331337658SMarcel Moolenaar { "leading-xpath", required_argument, NULL, 'l' }, 23431337658SMarcel Moolenaar { "not-first", no_argument, &opts.o_not_first, 1 }, 23531337658SMarcel Moolenaar { "open", required_argument, NULL, 'o' }, 23631337658SMarcel Moolenaar { "option", required_argument, NULL, 'O' }, 23731337658SMarcel Moolenaar { "pretty", no_argument, NULL, 'p' }, 23831337658SMarcel Moolenaar { "style", required_argument, NULL, 's' }, 23931337658SMarcel Moolenaar { "text", no_argument, NULL, 'T' }, 24031337658SMarcel Moolenaar { "xml", no_argument, NULL, 'X' }, 24131337658SMarcel Moolenaar { "xpath", no_argument, &opts.o_xpath, 1 }, 24231337658SMarcel Moolenaar { "version", no_argument, &opts.o_version, 1 }, 24331337658SMarcel Moolenaar { "warn", no_argument, NULL, 'W' }, 24431337658SMarcel Moolenaar { "warn-xml", no_argument, &opts.o_warn_xml, 1 }, 24531337658SMarcel Moolenaar { "wrap", required_argument, &opts.o_wrap, 1 }, 24631337658SMarcel Moolenaar { NULL, 0, NULL, 0 } 24731337658SMarcel Moolenaar }; 24831337658SMarcel Moolenaar 24931337658SMarcel Moolenaar int 25031337658SMarcel Moolenaar main (int argc UNUSED, char **argv) 25131337658SMarcel Moolenaar { 25231337658SMarcel Moolenaar char *fmt = NULL, *cp, *np; 25331337658SMarcel Moolenaar char *opt_opener = NULL, *opt_closer = NULL, *opt_wrapper = NULL; 25431337658SMarcel Moolenaar char *opt_options = NULL; 25531337658SMarcel Moolenaar int opt_depth = 0; 25631337658SMarcel Moolenaar int opt_not_first = 0; 25731337658SMarcel Moolenaar int rc; 25831337658SMarcel Moolenaar 25931337658SMarcel Moolenaar argc = xo_parse_args(argc, argv); 26031337658SMarcel Moolenaar if (argc < 0) 26131337658SMarcel Moolenaar return 1; 26231337658SMarcel Moolenaar 263d1a0d267SMarcel Moolenaar while ((rc = getopt_long(argc, argv, "c:HJl:O:o:ps:TXW", 26431337658SMarcel Moolenaar long_opts, NULL)) != -1) { 26531337658SMarcel Moolenaar switch (rc) { 26631337658SMarcel Moolenaar case 'c': 26731337658SMarcel Moolenaar opt_closer = optarg; 26831337658SMarcel Moolenaar xo_set_flags(NULL, XOF_IGNORE_CLOSE); 26931337658SMarcel Moolenaar break; 27031337658SMarcel Moolenaar 27131337658SMarcel Moolenaar case 'H': 27231337658SMarcel Moolenaar xo_set_style(NULL, XO_STYLE_HTML); 27331337658SMarcel Moolenaar break; 27431337658SMarcel Moolenaar 27531337658SMarcel Moolenaar case 'J': 27631337658SMarcel Moolenaar xo_set_style(NULL, XO_STYLE_JSON); 27731337658SMarcel Moolenaar break; 27831337658SMarcel Moolenaar 27931337658SMarcel Moolenaar case 'l': 28031337658SMarcel Moolenaar xo_set_leading_xpath(NULL, optarg); 28131337658SMarcel Moolenaar break; 28231337658SMarcel Moolenaar 28331337658SMarcel Moolenaar case 'O': 28431337658SMarcel Moolenaar opt_options = optarg; 28531337658SMarcel Moolenaar break; 28631337658SMarcel Moolenaar 28731337658SMarcel Moolenaar case 'o': 28831337658SMarcel Moolenaar opt_opener = optarg; 28931337658SMarcel Moolenaar break; 29031337658SMarcel Moolenaar 29131337658SMarcel Moolenaar case 'p': 29231337658SMarcel Moolenaar xo_set_flags(NULL, XOF_PRETTY); 29331337658SMarcel Moolenaar break; 29431337658SMarcel Moolenaar 29531337658SMarcel Moolenaar case 's': 29631337658SMarcel Moolenaar if (xo_set_style_name(NULL, optarg) < 0) 29731337658SMarcel Moolenaar xo_errx(1, "unknown style: %s", optarg); 29831337658SMarcel Moolenaar break; 29931337658SMarcel Moolenaar 30031337658SMarcel Moolenaar case 'T': 30131337658SMarcel Moolenaar xo_set_style(NULL, XO_STYLE_TEXT); 30231337658SMarcel Moolenaar break; 30331337658SMarcel Moolenaar 30431337658SMarcel Moolenaar case 'X': 30531337658SMarcel Moolenaar xo_set_style(NULL, XO_STYLE_XML); 30631337658SMarcel Moolenaar break; 30731337658SMarcel Moolenaar 30831337658SMarcel Moolenaar case 'W': 30931337658SMarcel Moolenaar opt_warn = 1; 31031337658SMarcel Moolenaar xo_set_flags(NULL, XOF_WARN); 31131337658SMarcel Moolenaar break; 31231337658SMarcel Moolenaar 31331337658SMarcel Moolenaar case ':': 31431337658SMarcel Moolenaar xo_errx(1, "missing argument"); 31531337658SMarcel Moolenaar break; 31631337658SMarcel Moolenaar 31731337658SMarcel Moolenaar case 0: 31831337658SMarcel Moolenaar if (opts.o_depth) { 31931337658SMarcel Moolenaar opt_depth = atoi(optarg); 32031337658SMarcel Moolenaar 32131337658SMarcel Moolenaar } else if (opts.o_help) { 32231337658SMarcel Moolenaar print_help(); 32331337658SMarcel Moolenaar return 1; 32431337658SMarcel Moolenaar 32531337658SMarcel Moolenaar } else if (opts.o_not_first) { 32631337658SMarcel Moolenaar opt_not_first = 1; 32731337658SMarcel Moolenaar 32831337658SMarcel Moolenaar } else if (opts.o_xpath) { 32931337658SMarcel Moolenaar xo_set_flags(NULL, XOF_XPATH); 33031337658SMarcel Moolenaar 33131337658SMarcel Moolenaar } else if (opts.o_version) { 33231337658SMarcel Moolenaar print_version(); 33331337658SMarcel Moolenaar return 0; 33431337658SMarcel Moolenaar 33531337658SMarcel Moolenaar } else if (opts.o_warn_xml) { 33631337658SMarcel Moolenaar opt_warn = 1; 33731337658SMarcel Moolenaar xo_set_flags(NULL, XOF_WARN | XOF_WARN_XML); 33831337658SMarcel Moolenaar 33931337658SMarcel Moolenaar } else if (opts.o_wrap) { 34031337658SMarcel Moolenaar opt_wrapper = optarg; 34131337658SMarcel Moolenaar 34231337658SMarcel Moolenaar } else { 34331337658SMarcel Moolenaar print_help(); 34431337658SMarcel Moolenaar return 1; 34531337658SMarcel Moolenaar } 34631337658SMarcel Moolenaar 34731337658SMarcel Moolenaar bzero(&opts, sizeof(opts)); /* Reset all the options */ 34831337658SMarcel Moolenaar break; 34931337658SMarcel Moolenaar 35031337658SMarcel Moolenaar default: 35131337658SMarcel Moolenaar print_help(); 35231337658SMarcel Moolenaar return 1; 35331337658SMarcel Moolenaar } 35431337658SMarcel Moolenaar } 35531337658SMarcel Moolenaar 35631337658SMarcel Moolenaar argc -= optind; 35731337658SMarcel Moolenaar argv += optind; 35831337658SMarcel Moolenaar 35931337658SMarcel Moolenaar if (opt_options) { 36031337658SMarcel Moolenaar rc = xo_set_options(NULL, opt_options); 36131337658SMarcel Moolenaar if (rc < 0) 36231337658SMarcel Moolenaar xo_errx(1, "invalid options: %s", opt_options); 36331337658SMarcel Moolenaar } 36431337658SMarcel Moolenaar 36531337658SMarcel Moolenaar xo_set_formatter(NULL, formatter, checkpoint); 366545ddfbeSMarcel Moolenaar xo_set_flags(NULL, XOF_NO_VA_ARG | XOF_NO_TOP | XOF_NO_CLOSE); 36731337658SMarcel Moolenaar 36831337658SMarcel Moolenaar fmt = *argv++; 36931337658SMarcel Moolenaar if (opt_opener == NULL && opt_closer == NULL && fmt == NULL) { 37031337658SMarcel Moolenaar print_help(); 37131337658SMarcel Moolenaar return 1; 37231337658SMarcel Moolenaar } 37331337658SMarcel Moolenaar 37431337658SMarcel Moolenaar if (opt_not_first) 37531337658SMarcel Moolenaar xo_set_flags(NULL, XOF_NOT_FIRST); 37631337658SMarcel Moolenaar 37731337658SMarcel Moolenaar if (opt_closer) { 37831337658SMarcel Moolenaar opt_depth += 1; 37931337658SMarcel Moolenaar for (cp = opt_closer; cp && *cp; cp = np) { 38031337658SMarcel Moolenaar np = strchr(cp, '/'); 38131337658SMarcel Moolenaar if (np == NULL) 38231337658SMarcel Moolenaar break; 38331337658SMarcel Moolenaar np += 1; 38431337658SMarcel Moolenaar opt_depth += 1; 38531337658SMarcel Moolenaar } 38631337658SMarcel Moolenaar } 38731337658SMarcel Moolenaar 38831337658SMarcel Moolenaar if (opt_depth > 0) 38931337658SMarcel Moolenaar xo_set_depth(NULL, opt_depth); 39031337658SMarcel Moolenaar 39131337658SMarcel Moolenaar if (opt_opener) { 39231337658SMarcel Moolenaar for (cp = opt_opener; cp && *cp; cp = np) { 39331337658SMarcel Moolenaar np = strchr(cp, '/'); 39431337658SMarcel Moolenaar if (np) 39531337658SMarcel Moolenaar *np = '\0'; 39631337658SMarcel Moolenaar xo_open_container(cp); 39731337658SMarcel Moolenaar if (np) 39831337658SMarcel Moolenaar *np++ = '/'; 39931337658SMarcel Moolenaar } 40031337658SMarcel Moolenaar } 40131337658SMarcel Moolenaar 40231337658SMarcel Moolenaar if (opt_wrapper) { 40331337658SMarcel Moolenaar for (cp = opt_wrapper; cp && *cp; cp = np) { 40431337658SMarcel Moolenaar np = strchr(cp, '/'); 40531337658SMarcel Moolenaar if (np) 40631337658SMarcel Moolenaar *np = '\0'; 40731337658SMarcel Moolenaar xo_open_container(cp); 40831337658SMarcel Moolenaar if (np) 40931337658SMarcel Moolenaar *np++ = '/'; 41031337658SMarcel Moolenaar } 41131337658SMarcel Moolenaar } 41231337658SMarcel Moolenaar 41331337658SMarcel Moolenaar if (fmt && *fmt) { 41431337658SMarcel Moolenaar save_argv = argv; 41531337658SMarcel Moolenaar prep_arg(fmt); 41631337658SMarcel Moolenaar xo_emit(fmt); 41731337658SMarcel Moolenaar } 41831337658SMarcel Moolenaar 41931337658SMarcel Moolenaar while (opt_wrapper) { 42031337658SMarcel Moolenaar np = strrchr(opt_wrapper, '/'); 42131337658SMarcel Moolenaar xo_close_container(np ? np + 1 : opt_wrapper); 42231337658SMarcel Moolenaar if (np) 42331337658SMarcel Moolenaar *np = '\0'; 42431337658SMarcel Moolenaar else 42531337658SMarcel Moolenaar opt_wrapper = NULL; 42631337658SMarcel Moolenaar } 42731337658SMarcel Moolenaar 42831337658SMarcel Moolenaar while (opt_closer) { 42931337658SMarcel Moolenaar np = strrchr(opt_closer, '/'); 43031337658SMarcel Moolenaar xo_close_container(np ? np + 1 : opt_closer); 43131337658SMarcel Moolenaar if (np) 43231337658SMarcel Moolenaar *np = '\0'; 43331337658SMarcel Moolenaar else 43431337658SMarcel Moolenaar opt_closer = NULL; 43531337658SMarcel Moolenaar } 43631337658SMarcel Moolenaar 43731337658SMarcel Moolenaar xo_finish(); 43831337658SMarcel Moolenaar 43931337658SMarcel Moolenaar return 0; 44031337658SMarcel Moolenaar } 441