xo.c revision 273562
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 16273562Smarcel#include "xoconfig.h" 17273562Smarcel#include "xo.h" 18273562Smarcel#include "xoversion.h" 19273562Smarcel 20273562Smarcel#include <getopt.h> /* Include after xo.h for testing */ 21273562Smarcel 22273562Smarcel#ifndef UNUSED 23273562Smarcel#define UNUSED __attribute__ ((__unused__)) 24273562Smarcel#endif /* UNUSED */ 25273562Smarcel 26273562Smarcelstatic int opt_warn; /* Enable warnings */ 27273562Smarcel 28273562Smarcelstatic char **save_argv; 29273562Smarcelstatic char **checkpoint_argv; 30273562Smarcel 31273562Smarcelstatic char * 32273562Smarcelnext_arg (void) 33273562Smarcel{ 34273562Smarcel char *cp = *save_argv; 35273562Smarcel 36273562Smarcel if (cp == NULL) 37273562Smarcel xo_errx(1, "missing argument"); 38273562Smarcel 39273562Smarcel save_argv += 1; 40273562Smarcel return cp; 41273562Smarcel} 42273562Smarcel 43273562Smarcelstatic void 44273562Smarcelprep_arg (char *fmt) 45273562Smarcel{ 46273562Smarcel char *cp, *fp; 47273562Smarcel 48273562Smarcel for (cp = fp = fmt; *cp; cp++, fp++) { 49273562Smarcel if (*cp != '\\') { 50273562Smarcel if (cp != fp) 51273562Smarcel *fp = *cp; 52273562Smarcel continue; 53273562Smarcel } 54273562Smarcel 55273562Smarcel switch (*++cp) { 56273562Smarcel case 'n': 57273562Smarcel *fp = '\n'; 58273562Smarcel break; 59273562Smarcel 60273562Smarcel case 'r': 61273562Smarcel *fp = '\r'; 62273562Smarcel break; 63273562Smarcel 64273562Smarcel case 'b': 65273562Smarcel *fp = '\b'; 66273562Smarcel break; 67273562Smarcel 68273562Smarcel case 'e': 69273562Smarcel *fp = '\e'; 70273562Smarcel break; 71273562Smarcel 72273562Smarcel default: 73273562Smarcel *fp = *cp; 74273562Smarcel } 75273562Smarcel } 76273562Smarcel 77273562Smarcel *fp = '\0'; 78273562Smarcel} 79273562Smarcel 80273562Smarcelstatic void 81273562Smarcelcheckpoint (xo_handle_t *xop UNUSED, va_list vap UNUSED, int restore) 82273562Smarcel{ 83273562Smarcel if (restore) 84273562Smarcel save_argv = checkpoint_argv; 85273562Smarcel else 86273562Smarcel checkpoint_argv = save_argv; 87273562Smarcel} 88273562Smarcel 89273562Smarcel/* 90273562Smarcel * Our custom formatter is responsible for combining format string pieces 91273562Smarcel * with our command line arguments to build strings. This involves faking 92273562Smarcel * some printf-style logic. 93273562Smarcel */ 94273562Smarcelstatic int 95273562Smarcelformatter (xo_handle_t *xop, char *buf, int bufsiz, 96273562Smarcel const char *fmt, va_list vap UNUSED) 97273562Smarcel{ 98273562Smarcel int lflag = 0, 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" 204273562Smarcel" --pretty OR -p Make 'pretty' output (add indent, newlines)\n" 205273562Smarcel" --style <style> OR -s <style> " 206273562Smarcel "Generate given style (xml, json, text, html)\n" 207273562Smarcel" --text OR -T Generate text output (the default style)\n" 208273562Smarcel" --version Display version information\n" 209273562Smarcel" --warn OR -W Display warnings in text on stderr\n" 210273562Smarcel" --warn-xml Display warnings in xml on stdout\n" 211273562Smarcel" --wrap <path> Wrap output in a set of containers\n" 212273562Smarcel" --xml OR -X Generate XML output\n" 213273562Smarcel" --xpath Add XPath data to HTML output\n"); 214273562Smarcel} 215273562Smarcel 216273562Smarcelstatic struct opts { 217273562Smarcel int o_depth; 218273562Smarcel int o_help; 219273562Smarcel int o_not_first; 220273562Smarcel int o_xpath; 221273562Smarcel int o_version; 222273562Smarcel int o_warn_xml; 223273562Smarcel int o_wrap; 224273562Smarcel} opts; 225273562Smarcel 226273562Smarcelstatic struct option long_opts[] = { 227273562Smarcel { "close", required_argument, NULL, 'c' }, 228273562Smarcel { "depth", required_argument, &opts.o_depth, 1 }, 229273562Smarcel { "help", no_argument, &opts.o_help, 1 }, 230273562Smarcel { "html", no_argument, NULL, 'H' }, 231273562Smarcel { "json", no_argument, NULL, 'J' }, 232273562Smarcel { "leading-xpath", required_argument, NULL, 'l' }, 233273562Smarcel { "not-first", no_argument, &opts.o_not_first, 1 }, 234273562Smarcel { "open", required_argument, NULL, 'o' }, 235273562Smarcel { "option", required_argument, NULL, 'O' }, 236273562Smarcel { "pretty", no_argument, NULL, 'p' }, 237273562Smarcel { "style", required_argument, NULL, 's' }, 238273562Smarcel { "text", no_argument, NULL, 'T' }, 239273562Smarcel { "xml", no_argument, NULL, 'X' }, 240273562Smarcel { "xpath", no_argument, &opts.o_xpath, 1 }, 241273562Smarcel { "version", no_argument, &opts.o_version, 1 }, 242273562Smarcel { "warn", no_argument, NULL, 'W' }, 243273562Smarcel { "warn-xml", no_argument, &opts.o_warn_xml, 1 }, 244273562Smarcel { "wrap", required_argument, &opts.o_wrap, 1 }, 245273562Smarcel { NULL, 0, NULL, 0 } 246273562Smarcel}; 247273562Smarcel 248273562Smarcelint 249273562Smarcelmain (int argc UNUSED, char **argv) 250273562Smarcel{ 251273562Smarcel char *fmt = NULL, *cp, *np; 252273562Smarcel char *opt_opener = NULL, *opt_closer = NULL, *opt_wrapper = NULL; 253273562Smarcel char *opt_options = NULL; 254273562Smarcel int opt_depth = 0; 255273562Smarcel int opt_not_first = 0; 256273562Smarcel int rc; 257273562Smarcel 258273562Smarcel argc = xo_parse_args(argc, argv); 259273562Smarcel if (argc < 0) 260273562Smarcel return 1; 261273562Smarcel 262273562Smarcel while ((rc = getopt_long(argc, argv, "c:HJl:ps:TXW", 263273562Smarcel long_opts, NULL)) != -1) { 264273562Smarcel switch (rc) { 265273562Smarcel case 'c': 266273562Smarcel opt_closer = optarg; 267273562Smarcel xo_set_flags(NULL, XOF_IGNORE_CLOSE); 268273562Smarcel break; 269273562Smarcel 270273562Smarcel case 'H': 271273562Smarcel xo_set_style(NULL, XO_STYLE_HTML); 272273562Smarcel break; 273273562Smarcel 274273562Smarcel case 'J': 275273562Smarcel xo_set_style(NULL, XO_STYLE_JSON); 276273562Smarcel break; 277273562Smarcel 278273562Smarcel case 'l': 279273562Smarcel xo_set_leading_xpath(NULL, optarg); 280273562Smarcel break; 281273562Smarcel 282273562Smarcel case 'O': 283273562Smarcel opt_options = optarg; 284273562Smarcel break; 285273562Smarcel 286273562Smarcel case 'o': 287273562Smarcel opt_opener = optarg; 288273562Smarcel break; 289273562Smarcel 290273562Smarcel case 'p': 291273562Smarcel xo_set_flags(NULL, XOF_PRETTY); 292273562Smarcel break; 293273562Smarcel 294273562Smarcel case 's': 295273562Smarcel if (xo_set_style_name(NULL, optarg) < 0) 296273562Smarcel xo_errx(1, "unknown style: %s", optarg); 297273562Smarcel break; 298273562Smarcel 299273562Smarcel case 'T': 300273562Smarcel xo_set_style(NULL, XO_STYLE_TEXT); 301273562Smarcel break; 302273562Smarcel 303273562Smarcel case 'X': 304273562Smarcel xo_set_style(NULL, XO_STYLE_XML); 305273562Smarcel break; 306273562Smarcel 307273562Smarcel case 'W': 308273562Smarcel opt_warn = 1; 309273562Smarcel xo_set_flags(NULL, XOF_WARN); 310273562Smarcel break; 311273562Smarcel 312273562Smarcel case ':': 313273562Smarcel xo_errx(1, "missing argument"); 314273562Smarcel break; 315273562Smarcel 316273562Smarcel case 0: 317273562Smarcel if (opts.o_depth) { 318273562Smarcel opt_depth = atoi(optarg); 319273562Smarcel 320273562Smarcel } else if (opts.o_help) { 321273562Smarcel print_help(); 322273562Smarcel return 1; 323273562Smarcel 324273562Smarcel } else if (opts.o_not_first) { 325273562Smarcel opt_not_first = 1; 326273562Smarcel 327273562Smarcel } else if (opts.o_xpath) { 328273562Smarcel xo_set_flags(NULL, XOF_XPATH); 329273562Smarcel 330273562Smarcel } else if (opts.o_version) { 331273562Smarcel print_version(); 332273562Smarcel return 0; 333273562Smarcel 334273562Smarcel } else if (opts.o_warn_xml) { 335273562Smarcel opt_warn = 1; 336273562Smarcel xo_set_flags(NULL, XOF_WARN | XOF_WARN_XML); 337273562Smarcel 338273562Smarcel } else if (opts.o_wrap) { 339273562Smarcel opt_wrapper = optarg; 340273562Smarcel 341273562Smarcel } else { 342273562Smarcel print_help(); 343273562Smarcel return 1; 344273562Smarcel } 345273562Smarcel 346273562Smarcel bzero(&opts, sizeof(opts)); /* Reset all the options */ 347273562Smarcel break; 348273562Smarcel 349273562Smarcel default: 350273562Smarcel print_help(); 351273562Smarcel return 1; 352273562Smarcel } 353273562Smarcel } 354273562Smarcel 355273562Smarcel argc -= optind; 356273562Smarcel argv += optind; 357273562Smarcel 358273562Smarcel if (opt_options) { 359273562Smarcel rc = xo_set_options(NULL, opt_options); 360273562Smarcel if (rc < 0) 361273562Smarcel xo_errx(1, "invalid options: %s", opt_options); 362273562Smarcel } 363273562Smarcel 364273562Smarcel xo_set_formatter(NULL, formatter, checkpoint); 365273562Smarcel xo_set_flags(NULL, XOF_NO_VA_ARG); 366273562Smarcel xo_set_flags(NULL, XOF_NO_TOP); 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