1273562Smarcel/* 2273562Smarcel * Copyright (c) 2014, Juniper Networks, Inc. 3273562Smarcel * All rights reserved. 4273562Smarcel * This SOFTWARE is licensed under the LICENSE provided in the 5273562Smarcel * ../Copyright file. By downloading, installing, copying, or otherwise 6273562Smarcel * using the SOFTWARE, you agree to be bound by the terms of that 7273562Smarcel * LICENSE. 8273562Smarcel * Phil Shafer, July 2014 9273562Smarcel */ 10273562Smarcel 11273562Smarcel#include <stdio.h> 12273562Smarcel#include <stdlib.h> 13273562Smarcel#include <stdarg.h> 14273562Smarcel#include <string.h> 15273562Smarcel 16287111Smarcel#include "xo_config.h" 17273562Smarcel#include "xo.h" 18273562Smarcel 19273562Smarcel#include <getopt.h> /* Include after xo.h for testing */ 20273562Smarcel 21273562Smarcel#ifndef UNUSED 22273562Smarcel#define UNUSED __attribute__ ((__unused__)) 23273562Smarcel#endif /* UNUSED */ 24273562Smarcel 25273562Smarcelstatic int opt_warn; /* Enable warnings */ 26273562Smarcel 27273562Smarcelstatic char **save_argv; 28273562Smarcelstatic char **checkpoint_argv; 29273562Smarcel 30273562Smarcelstatic char * 31273562Smarcelnext_arg (void) 32273562Smarcel{ 33273562Smarcel char *cp = *save_argv; 34273562Smarcel 35273562Smarcel if (cp == NULL) 36273562Smarcel xo_errx(1, "missing argument"); 37273562Smarcel 38273562Smarcel save_argv += 1; 39273562Smarcel return cp; 40273562Smarcel} 41273562Smarcel 42273562Smarcelstatic void 43273562Smarcelprep_arg (char *fmt) 44273562Smarcel{ 45273562Smarcel char *cp, *fp; 46273562Smarcel 47273562Smarcel for (cp = fp = fmt; *cp; cp++, fp++) { 48273562Smarcel if (*cp != '\\') { 49273562Smarcel if (cp != fp) 50273562Smarcel *fp = *cp; 51273562Smarcel continue; 52273562Smarcel } 53273562Smarcel 54273562Smarcel switch (*++cp) { 55273562Smarcel case 'n': 56273562Smarcel *fp = '\n'; 57273562Smarcel break; 58273562Smarcel 59273562Smarcel case 'r': 60273562Smarcel *fp = '\r'; 61273562Smarcel break; 62273562Smarcel 63273562Smarcel case 'b': 64273562Smarcel *fp = '\b'; 65273562Smarcel break; 66273562Smarcel 67273562Smarcel case 'e': 68273562Smarcel *fp = '\e'; 69273562Smarcel break; 70273562Smarcel 71273562Smarcel default: 72273562Smarcel *fp = *cp; 73273562Smarcel } 74273562Smarcel } 75273562Smarcel 76273562Smarcel *fp = '\0'; 77273562Smarcel} 78273562Smarcel 79273562Smarcelstatic void 80273562Smarcelcheckpoint (xo_handle_t *xop UNUSED, va_list vap UNUSED, int restore) 81273562Smarcel{ 82273562Smarcel if (restore) 83273562Smarcel save_argv = checkpoint_argv; 84273562Smarcel else 85273562Smarcel checkpoint_argv = save_argv; 86273562Smarcel} 87273562Smarcel 88273562Smarcel/* 89273562Smarcel * Our custom formatter is responsible for combining format string pieces 90273562Smarcel * with our command line arguments to build strings. This involves faking 91273562Smarcel * some printf-style logic. 92273562Smarcel */ 93322172Sphilstatic xo_ssize_t 94322172Sphilformatter (xo_handle_t *xop, char *buf, xo_ssize_t bufsiz, 95273562Smarcel const char *fmt, va_list vap UNUSED) 96273562Smarcel{ 97287111Smarcel int lflag UNUSED = 0; /* Parse long flag, though currently ignored */ 98287111Smarcel int hflag = 0, jflag = 0, tflag = 0, 99273562Smarcel zflag = 0, qflag = 0, star1 = 0, star2 = 0; 100273562Smarcel int rc = 0; 101273562Smarcel int w1 = 0, w2 = 0; 102273562Smarcel const char *cp; 103273562Smarcel 104273562Smarcel for (cp = fmt + 1; *cp; cp++) { 105273562Smarcel if (*cp == 'l') 106273562Smarcel lflag += 1; 107273562Smarcel else if (*cp == 'h') 108273562Smarcel hflag += 1; 109273562Smarcel else if (*cp == 'j') 110273562Smarcel jflag += 1; 111273562Smarcel else if (*cp == 't') 112273562Smarcel tflag += 1; 113273562Smarcel else if (*cp == 'z') 114273562Smarcel zflag += 1; 115273562Smarcel else if (*cp == 'q') 116273562Smarcel qflag += 1; 117273562Smarcel else if (*cp == '*') { 118273562Smarcel if (star1 == 0) 119273562Smarcel star1 = 1; 120273562Smarcel else 121273562Smarcel star2 = 1; 122273562Smarcel } else if (strchr("diouxXDOUeEfFgGaAcCsSp", *cp) != NULL) 123273562Smarcel break; 124273562Smarcel else if (*cp == 'n' || *cp == 'v') { 125273562Smarcel if (opt_warn) 126273562Smarcel xo_error_h(xop, "unsupported format: '%s'", fmt); 127273562Smarcel return -1; 128273562Smarcel } 129273562Smarcel } 130273562Smarcel 131273562Smarcel char fc = *cp; 132273562Smarcel 133273562Smarcel /* Handle "%*.*s" */ 134273562Smarcel if (star1) 135273562Smarcel w1 = strtol(next_arg(), NULL, 0); 136273562Smarcel if (star2 > 1) 137273562Smarcel w2 = strtol(next_arg(), NULL, 0); 138273562Smarcel 139273562Smarcel if (fc == 'D' || fc == 'O' || fc == 'U') 140273562Smarcel lflag = 1; 141273562Smarcel 142273562Smarcel if (strchr("diD", fc) != NULL) { 143273562Smarcel long long value = strtoll(next_arg(), NULL, 0); 144273562Smarcel if (star1 && star2) 145273562Smarcel rc = snprintf(buf, bufsiz, fmt, w1, w2, value); 146273562Smarcel else if (star1) 147273562Smarcel rc = snprintf(buf, bufsiz, fmt, w1, value); 148273562Smarcel else 149273562Smarcel rc = snprintf(buf, bufsiz, fmt, value); 150273562Smarcel 151273562Smarcel } else if (strchr("ouxXOUp", fc) != NULL) { 152273562Smarcel unsigned long long value = strtoull(next_arg(), NULL, 0); 153273562Smarcel if (star1 && star2) 154273562Smarcel rc = snprintf(buf, bufsiz, fmt, w1, w2, value); 155273562Smarcel else if (star1) 156273562Smarcel rc = snprintf(buf, bufsiz, fmt, w1, value); 157273562Smarcel else 158273562Smarcel rc = snprintf(buf, bufsiz, fmt, value); 159273562Smarcel 160273562Smarcel } else if (strchr("eEfFgGaA", fc) != NULL) { 161273562Smarcel double value = strtold(next_arg(), NULL); 162273562Smarcel if (star1 && star2) 163273562Smarcel rc = snprintf(buf, bufsiz, fmt, w1, w2, value); 164273562Smarcel else if (star1) 165273562Smarcel rc = snprintf(buf, bufsiz, fmt, w1, value); 166273562Smarcel else 167273562Smarcel rc = snprintf(buf, bufsiz, fmt, value); 168273562Smarcel 169273562Smarcel } else if (fc == 'C' || fc == 'c' || fc == 'S' || fc == 's') { 170273562Smarcel char *value = next_arg(); 171273562Smarcel if (star1 && star2) 172273562Smarcel rc = snprintf(buf, bufsiz, fmt, w1, w2, value); 173273562Smarcel else if (star1) 174273562Smarcel rc = snprintf(buf, bufsiz, fmt, w1, value); 175273562Smarcel else 176273562Smarcel rc = snprintf(buf, bufsiz, fmt, value); 177273562Smarcel } 178273562Smarcel 179273562Smarcel return rc; 180273562Smarcel} 181273562Smarcel 182273562Smarcelstatic void 183273562Smarcelprint_version (void) 184273562Smarcel{ 185273562Smarcel fprintf(stderr, "libxo version %s%s\n", 186273562Smarcel xo_version, xo_version_extra); 187273562Smarcel fprintf(stderr, "xo version %s%s\n", 188273562Smarcel LIBXO_VERSION, LIBXO_VERSION_EXTRA); 189273562Smarcel} 190273562Smarcel 191273562Smarcelstatic void 192273562Smarcelprint_help (void) 193273562Smarcel{ 194273562Smarcel fprintf(stderr, 195273562Smarcel"Usage: xo [options] format [fields]\n" 196273562Smarcel" --close <path> Close tags for the given path\n" 197273562Smarcel" --depth <num> Set the depth for pretty printing\n" 198273562Smarcel" --help Display this help text\n" 199273562Smarcel" --html OR -H Generate HTML output\n" 200273562Smarcel" --json OR -J Generate JSON output\n" 201273562Smarcel" --leading-xpath <path> OR -l <path> " 202273562Smarcel "Add a prefix to generated XPaths (HTML)\n" 203273562Smarcel" --open <path> Open tags for the given path\n" 204287111Smarcel" --option <opts> -or -O <opts> Give formatting options\n" 205273562Smarcel" --pretty OR -p Make 'pretty' output (add indent, newlines)\n" 206273562Smarcel" --style <style> OR -s <style> " 207273562Smarcel "Generate given style (xml, json, text, html)\n" 208273562Smarcel" --text OR -T Generate text output (the default style)\n" 209273562Smarcel" --version Display version information\n" 210273562Smarcel" --warn OR -W Display warnings in text on stderr\n" 211273562Smarcel" --warn-xml Display warnings in xml on stdout\n" 212273562Smarcel" --wrap <path> Wrap output in a set of containers\n" 213273562Smarcel" --xml OR -X Generate XML output\n" 214273562Smarcel" --xpath Add XPath data to HTML output\n"); 215273562Smarcel} 216273562Smarcel 217273562Smarcelstatic struct opts { 218273562Smarcel int o_depth; 219273562Smarcel int o_help; 220273562Smarcel int o_not_first; 221273562Smarcel int o_xpath; 222273562Smarcel int o_version; 223273562Smarcel int o_warn_xml; 224273562Smarcel int o_wrap; 225273562Smarcel} opts; 226273562Smarcel 227273562Smarcelstatic struct option long_opts[] = { 228273562Smarcel { "close", required_argument, NULL, 'c' }, 229273562Smarcel { "depth", required_argument, &opts.o_depth, 1 }, 230273562Smarcel { "help", no_argument, &opts.o_help, 1 }, 231273562Smarcel { "html", no_argument, NULL, 'H' }, 232273562Smarcel { "json", no_argument, NULL, 'J' }, 233273562Smarcel { "leading-xpath", required_argument, NULL, 'l' }, 234273562Smarcel { "not-first", no_argument, &opts.o_not_first, 1 }, 235273562Smarcel { "open", required_argument, NULL, 'o' }, 236273562Smarcel { "option", required_argument, NULL, 'O' }, 237273562Smarcel { "pretty", no_argument, NULL, 'p' }, 238273562Smarcel { "style", required_argument, NULL, 's' }, 239273562Smarcel { "text", no_argument, NULL, 'T' }, 240273562Smarcel { "xml", no_argument, NULL, 'X' }, 241273562Smarcel { "xpath", no_argument, &opts.o_xpath, 1 }, 242273562Smarcel { "version", no_argument, &opts.o_version, 1 }, 243273562Smarcel { "warn", no_argument, NULL, 'W' }, 244273562Smarcel { "warn-xml", no_argument, &opts.o_warn_xml, 1 }, 245273562Smarcel { "wrap", required_argument, &opts.o_wrap, 1 }, 246273562Smarcel { NULL, 0, NULL, 0 } 247273562Smarcel}; 248273562Smarcel 249273562Smarcelint 250273562Smarcelmain (int argc UNUSED, char **argv) 251273562Smarcel{ 252273562Smarcel char *fmt = NULL, *cp, *np; 253273562Smarcel char *opt_opener = NULL, *opt_closer = NULL, *opt_wrapper = NULL; 254273562Smarcel char *opt_options = NULL; 255273562Smarcel int opt_depth = 0; 256273562Smarcel int opt_not_first = 0; 257273562Smarcel int rc; 258273562Smarcel 259273562Smarcel argc = xo_parse_args(argc, argv); 260273562Smarcel if (argc < 0) 261273562Smarcel return 1; 262273562Smarcel 263287111Smarcel while ((rc = getopt_long(argc, argv, "c:HJl:O:o:ps:TXW", 264273562Smarcel long_opts, NULL)) != -1) { 265273562Smarcel switch (rc) { 266273562Smarcel case 'c': 267273562Smarcel opt_closer = optarg; 268273562Smarcel xo_set_flags(NULL, XOF_IGNORE_CLOSE); 269273562Smarcel break; 270273562Smarcel 271273562Smarcel case 'H': 272273562Smarcel xo_set_style(NULL, XO_STYLE_HTML); 273273562Smarcel break; 274273562Smarcel 275273562Smarcel case 'J': 276273562Smarcel xo_set_style(NULL, XO_STYLE_JSON); 277273562Smarcel break; 278273562Smarcel 279273562Smarcel case 'l': 280273562Smarcel xo_set_leading_xpath(NULL, optarg); 281273562Smarcel break; 282273562Smarcel 283273562Smarcel case 'O': 284273562Smarcel opt_options = optarg; 285273562Smarcel break; 286273562Smarcel 287273562Smarcel case 'o': 288273562Smarcel opt_opener = optarg; 289273562Smarcel break; 290273562Smarcel 291273562Smarcel case 'p': 292273562Smarcel xo_set_flags(NULL, XOF_PRETTY); 293273562Smarcel break; 294273562Smarcel 295273562Smarcel case 's': 296273562Smarcel if (xo_set_style_name(NULL, optarg) < 0) 297273562Smarcel xo_errx(1, "unknown style: %s", optarg); 298273562Smarcel break; 299273562Smarcel 300273562Smarcel case 'T': 301273562Smarcel xo_set_style(NULL, XO_STYLE_TEXT); 302273562Smarcel break; 303273562Smarcel 304273562Smarcel case 'X': 305273562Smarcel xo_set_style(NULL, XO_STYLE_XML); 306273562Smarcel break; 307273562Smarcel 308273562Smarcel case 'W': 309273562Smarcel opt_warn = 1; 310273562Smarcel xo_set_flags(NULL, XOF_WARN); 311273562Smarcel break; 312273562Smarcel 313273562Smarcel case ':': 314273562Smarcel xo_errx(1, "missing argument"); 315273562Smarcel break; 316273562Smarcel 317273562Smarcel case 0: 318273562Smarcel if (opts.o_depth) { 319273562Smarcel opt_depth = atoi(optarg); 320273562Smarcel 321273562Smarcel } else if (opts.o_help) { 322273562Smarcel print_help(); 323273562Smarcel return 1; 324273562Smarcel 325273562Smarcel } else if (opts.o_not_first) { 326273562Smarcel opt_not_first = 1; 327273562Smarcel 328273562Smarcel } else if (opts.o_xpath) { 329273562Smarcel xo_set_flags(NULL, XOF_XPATH); 330273562Smarcel 331273562Smarcel } else if (opts.o_version) { 332273562Smarcel print_version(); 333273562Smarcel return 0; 334273562Smarcel 335273562Smarcel } else if (opts.o_warn_xml) { 336273562Smarcel opt_warn = 1; 337273562Smarcel xo_set_flags(NULL, XOF_WARN | XOF_WARN_XML); 338273562Smarcel 339273562Smarcel } else if (opts.o_wrap) { 340273562Smarcel opt_wrapper = optarg; 341273562Smarcel 342273562Smarcel } else { 343273562Smarcel print_help(); 344273562Smarcel return 1; 345273562Smarcel } 346273562Smarcel 347273562Smarcel bzero(&opts, sizeof(opts)); /* Reset all the options */ 348273562Smarcel break; 349273562Smarcel 350273562Smarcel default: 351273562Smarcel print_help(); 352273562Smarcel return 1; 353273562Smarcel } 354273562Smarcel } 355273562Smarcel 356273562Smarcel argc -= optind; 357273562Smarcel argv += optind; 358273562Smarcel 359273562Smarcel if (opt_options) { 360273562Smarcel rc = xo_set_options(NULL, opt_options); 361273562Smarcel if (rc < 0) 362273562Smarcel xo_errx(1, "invalid options: %s", opt_options); 363273562Smarcel } 364273562Smarcel 365273562Smarcel xo_set_formatter(NULL, formatter, checkpoint); 366277353Smarcel xo_set_flags(NULL, XOF_NO_VA_ARG | XOF_NO_TOP | XOF_NO_CLOSE); 367273562Smarcel 368273562Smarcel fmt = *argv++; 369273562Smarcel if (opt_opener == NULL && opt_closer == NULL && fmt == NULL) { 370273562Smarcel print_help(); 371273562Smarcel return 1; 372273562Smarcel } 373273562Smarcel 374273562Smarcel if (opt_not_first) 375273562Smarcel xo_set_flags(NULL, XOF_NOT_FIRST); 376273562Smarcel 377273562Smarcel if (opt_closer) { 378273562Smarcel opt_depth += 1; 379273562Smarcel for (cp = opt_closer; cp && *cp; cp = np) { 380273562Smarcel np = strchr(cp, '/'); 381273562Smarcel if (np == NULL) 382273562Smarcel break; 383273562Smarcel np += 1; 384273562Smarcel opt_depth += 1; 385273562Smarcel } 386273562Smarcel } 387273562Smarcel 388273562Smarcel if (opt_depth > 0) 389273562Smarcel xo_set_depth(NULL, opt_depth); 390273562Smarcel 391273562Smarcel if (opt_opener) { 392273562Smarcel for (cp = opt_opener; cp && *cp; cp = np) { 393273562Smarcel np = strchr(cp, '/'); 394273562Smarcel if (np) 395273562Smarcel *np = '\0'; 396273562Smarcel xo_open_container(cp); 397273562Smarcel if (np) 398273562Smarcel *np++ = '/'; 399273562Smarcel } 400273562Smarcel } 401273562Smarcel 402273562Smarcel if (opt_wrapper) { 403273562Smarcel for (cp = opt_wrapper; cp && *cp; cp = np) { 404273562Smarcel np = strchr(cp, '/'); 405273562Smarcel if (np) 406273562Smarcel *np = '\0'; 407273562Smarcel xo_open_container(cp); 408273562Smarcel if (np) 409273562Smarcel *np++ = '/'; 410273562Smarcel } 411273562Smarcel } 412273562Smarcel 413273562Smarcel if (fmt && *fmt) { 414273562Smarcel save_argv = argv; 415273562Smarcel prep_arg(fmt); 416273562Smarcel xo_emit(fmt); 417273562Smarcel } 418273562Smarcel 419273562Smarcel while (opt_wrapper) { 420273562Smarcel np = strrchr(opt_wrapper, '/'); 421273562Smarcel xo_close_container(np ? np + 1 : opt_wrapper); 422273562Smarcel if (np) 423273562Smarcel *np = '\0'; 424273562Smarcel else 425273562Smarcel opt_wrapper = NULL; 426273562Smarcel } 427273562Smarcel 428273562Smarcel while (opt_closer) { 429273562Smarcel np = strrchr(opt_closer, '/'); 430273562Smarcel xo_close_container(np ? np + 1 : opt_closer); 431273562Smarcel if (np) 432273562Smarcel *np = '\0'; 433273562Smarcel else 434273562Smarcel opt_closer = NULL; 435273562Smarcel } 436273562Smarcel 437273562Smarcel xo_finish(); 438273562Smarcel 439273562Smarcel return 0; 440273562Smarcel} 441