]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/libxo/xo/xo.c
Merge sendmail 8.15.2 to HEAD
[FreeBSD/FreeBSD.git] / contrib / libxo / xo / xo.c
1 /*
2  * Copyright (c) 2014, Juniper Networks, Inc.
3  * All rights reserved.
4  * This SOFTWARE is licensed under the LICENSE provided in the
5  * ../Copyright file. By downloading, installing, copying, or otherwise
6  * using the SOFTWARE, you agree to be bound by the terms of that
7  * LICENSE.
8  * Phil Shafer, July 2014
9  */
10
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <stdarg.h>
14 #include <string.h>
15
16 #include "xoconfig.h"
17 #include "xo.h"
18 #include "xoversion.h"
19
20 #include <getopt.h>             /* Include after xo.h for testing */
21
22 #ifndef UNUSED
23 #define UNUSED __attribute__ ((__unused__))
24 #endif /* UNUSED */
25
26 static int opt_warn;            /* Enable warnings */
27
28 static char **save_argv;
29 static char **checkpoint_argv;
30
31 static char *
32 next_arg (void)
33 {
34     char *cp = *save_argv;
35
36     if (cp == NULL)
37         xo_errx(1, "missing argument");
38
39     save_argv += 1;
40     return cp;
41 }
42
43 static void
44 prep_arg (char *fmt)
45 {
46     char *cp, *fp;
47
48     for (cp = fp = fmt; *cp; cp++, fp++) {
49         if (*cp != '\\') {
50             if (cp != fp)
51                 *fp = *cp;
52             continue;
53         }
54
55         switch (*++cp) {
56         case 'n':
57             *fp = '\n';
58             break;
59
60         case 'r':
61             *fp = '\r';
62             break;
63
64         case 'b':
65             *fp = '\b';
66             break;
67
68         case 'e':
69             *fp = '\e';
70             break;
71
72         default:
73             *fp = *cp;
74         }
75     }
76
77     *fp = '\0';
78 }
79
80 static void
81 checkpoint (xo_handle_t *xop UNUSED, va_list vap UNUSED, int restore)
82 {
83     if (restore)
84         save_argv = checkpoint_argv;
85     else
86         checkpoint_argv = save_argv;
87 }
88
89 /*
90  * Our custom formatter is responsible for combining format string pieces
91  * with our command line arguments to build strings.  This involves faking
92  * some printf-style logic.
93  */
94 static int
95 formatter (xo_handle_t *xop, char *buf, int bufsiz,
96            const char *fmt, va_list vap UNUSED)
97 {
98     int lflag = 0, hflag = 0, jflag = 0, tflag = 0,
99         zflag = 0, qflag = 0, star1 = 0, star2 = 0;
100     int rc = 0;
101     int w1 = 0, w2 = 0;
102     const char *cp;
103
104     for (cp = fmt + 1; *cp; cp++) {
105         if (*cp == 'l')
106             lflag += 1;
107         else if (*cp == 'h')
108             hflag += 1;
109         else if (*cp == 'j')
110             jflag += 1;
111         else if (*cp == 't')
112             tflag += 1;
113         else if (*cp == 'z')
114             zflag += 1;
115         else if (*cp == 'q')
116             qflag += 1;
117         else if (*cp == '*') {
118             if (star1 == 0)
119                 star1 = 1;
120             else
121                 star2 = 1;
122         } else if (strchr("diouxXDOUeEfFgGaAcCsSp", *cp) != NULL)
123             break;
124         else if (*cp == 'n' || *cp == 'v') {
125             if (opt_warn)
126                 xo_error_h(xop, "unsupported format: '%s'", fmt);
127             return -1;
128         }
129     }
130
131     char fc = *cp;
132
133     /* Handle "%*.*s" */
134     if (star1)
135         w1 = strtol(next_arg(), NULL, 0);
136     if (star2 > 1)
137         w2 = strtol(next_arg(), NULL, 0);
138
139     if (fc == 'D' || fc == 'O' || fc == 'U')
140         lflag = 1;
141
142     if (strchr("diD", fc) != NULL) {
143         long long value = strtoll(next_arg(), NULL, 0);
144         if (star1 && star2)
145             rc = snprintf(buf, bufsiz, fmt, w1, w2, value);
146         else if (star1)
147             rc = snprintf(buf, bufsiz, fmt, w1, value);
148         else
149             rc = snprintf(buf, bufsiz, fmt, value);
150
151     } else if (strchr("ouxXOUp", fc) != NULL) {
152         unsigned long long value = strtoull(next_arg(), NULL, 0);
153         if (star1 && star2)
154             rc = snprintf(buf, bufsiz, fmt, w1, w2, value);
155         else if (star1)
156             rc = snprintf(buf, bufsiz, fmt, w1, value);
157         else
158             rc = snprintf(buf, bufsiz, fmt, value);
159
160     } else if (strchr("eEfFgGaA", fc) != NULL) {
161         double value = strtold(next_arg(), NULL);
162         if (star1 && star2)
163             rc = snprintf(buf, bufsiz, fmt, w1, w2, value);
164         else if (star1)
165             rc = snprintf(buf, bufsiz, fmt, w1, value);
166         else
167             rc = snprintf(buf, bufsiz, fmt, value);
168
169     } else if (fc == 'C' || fc == 'c' || fc == 'S' || fc == 's') {
170         char *value = next_arg();
171         if (star1 && star2)
172             rc = snprintf(buf, bufsiz, fmt, w1, w2, value);
173         else if (star1)
174             rc = snprintf(buf, bufsiz, fmt, w1, value);
175         else
176             rc = snprintf(buf, bufsiz, fmt, value);
177     }
178
179     return rc;
180 }
181
182 static void
183 print_version (void)
184 {
185     fprintf(stderr, "libxo version %s%s\n",
186             xo_version, xo_version_extra);
187     fprintf(stderr, "xo version %s%s\n",
188             LIBXO_VERSION, LIBXO_VERSION_EXTRA);
189 }
190
191 static void
192 print_help (void)
193 {
194     fprintf(stderr,
195 "Usage: xo [options] format [fields]\n"
196 "    --close <path>        Close tags for the given path\n"
197 "    --depth <num>         Set the depth for pretty printing\n"
198 "    --help                Display this help text\n"
199 "    --html OR -H          Generate HTML output\n"
200 "    --json OR -J          Generate JSON output\n"
201 "    --leading-xpath <path> OR -l <path> "
202             "Add a prefix to generated XPaths (HTML)\n"
203 "    --open <path>         Open tags for the given path\n"
204 "    --pretty OR -p        Make 'pretty' output (add indent, newlines)\n"
205 "    --style <style> OR -s <style>  "
206             "Generate given style (xml, json, text, html)\n"
207 "    --text OR -T          Generate text output (the default style)\n"
208 "    --version             Display version information\n"
209 "    --warn OR -W          Display warnings in text on stderr\n"
210 "    --warn-xml            Display warnings in xml on stdout\n"
211 "    --wrap <path>         Wrap output in a set of containers\n"
212 "    --xml OR -X           Generate XML output\n"
213 "    --xpath               Add XPath data to HTML output\n");
214 }
215
216 static struct opts {
217     int o_depth;
218     int o_help;
219     int o_not_first;
220     int o_xpath;
221     int o_version;
222     int o_warn_xml;
223     int o_wrap;
224 } opts;
225
226 static struct option long_opts[] = {
227     { "close", required_argument, NULL, 'c' },
228     { "depth", required_argument, &opts.o_depth, 1 },
229     { "help", no_argument, &opts.o_help, 1 },
230     { "html", no_argument, NULL, 'H' },
231     { "json", no_argument, NULL, 'J' },
232     { "leading-xpath", required_argument, NULL, 'l' },
233     { "not-first", no_argument, &opts.o_not_first, 1 },
234     { "open", required_argument, NULL, 'o' },
235     { "option", required_argument, NULL, 'O' },
236     { "pretty", no_argument, NULL, 'p' },
237     { "style", required_argument, NULL, 's' },
238     { "text", no_argument, NULL, 'T' },
239     { "xml", no_argument, NULL, 'X' },
240     { "xpath", no_argument, &opts.o_xpath, 1 },
241     { "version", no_argument, &opts.o_version, 1 },
242     { "warn", no_argument, NULL, 'W' },
243     { "warn-xml", no_argument, &opts.o_warn_xml, 1 },
244     { "wrap", required_argument, &opts.o_wrap, 1 },
245     { NULL, 0, NULL, 0 }
246 };
247
248 int
249 main (int argc UNUSED, char **argv)
250 {
251     char *fmt = NULL, *cp, *np;
252     char *opt_opener = NULL, *opt_closer = NULL, *opt_wrapper = NULL;
253     char *opt_options = NULL;
254     int opt_depth = 0;
255     int opt_not_first = 0;
256     int rc;
257
258     argc = xo_parse_args(argc, argv);
259     if (argc < 0)
260         return 1;
261
262     while ((rc = getopt_long(argc, argv, "c:HJl:ps:TXW",
263                                 long_opts, NULL)) != -1) {
264         switch (rc) {
265         case 'c':
266             opt_closer = optarg;
267             xo_set_flags(NULL, XOF_IGNORE_CLOSE);
268             break;
269
270         case 'H':
271             xo_set_style(NULL, XO_STYLE_HTML);
272             break;
273
274         case 'J':
275             xo_set_style(NULL, XO_STYLE_JSON);
276             break;
277
278         case 'l':
279             xo_set_leading_xpath(NULL, optarg);
280             break;
281
282         case 'O':
283             opt_options = optarg;
284             break;
285
286         case 'o':
287             opt_opener = optarg;
288             break;
289
290         case 'p':
291             xo_set_flags(NULL, XOF_PRETTY);
292             break;
293
294         case 's':
295             if (xo_set_style_name(NULL, optarg) < 0)
296                 xo_errx(1, "unknown style: %s", optarg);
297             break;
298
299         case 'T':
300             xo_set_style(NULL, XO_STYLE_TEXT);
301             break;
302
303         case 'X':
304             xo_set_style(NULL, XO_STYLE_XML);
305             break;
306
307         case 'W':
308             opt_warn = 1;
309             xo_set_flags(NULL, XOF_WARN);
310             break;
311
312         case ':':
313             xo_errx(1, "missing argument");
314             break;
315
316         case 0:
317             if (opts.o_depth) {
318                 opt_depth = atoi(optarg);
319                 
320             } else if (opts.o_help) {
321                 print_help();
322                 return 1;
323
324             } else if (opts.o_not_first) {
325                 opt_not_first = 1;
326
327             } else if (opts.o_xpath) {
328                 xo_set_flags(NULL, XOF_XPATH);
329
330             } else if (opts.o_version) {
331                 print_version();
332                 return 0;
333
334             } else if (opts.o_warn_xml) {
335                 opt_warn = 1;
336                 xo_set_flags(NULL, XOF_WARN | XOF_WARN_XML);
337
338             } else if (opts.o_wrap) {
339                 opt_wrapper = optarg;
340
341             } else {
342                 print_help();
343                 return 1;
344             }
345
346             bzero(&opts, sizeof(opts)); /* Reset all the options */
347             break;
348
349         default:
350             print_help();
351             return 1;
352         }
353     }
354
355     argc -= optind;
356     argv += optind;
357
358     if (opt_options) {
359         rc = xo_set_options(NULL, opt_options);
360         if (rc < 0)
361             xo_errx(1, "invalid options: %s", opt_options);
362     }
363
364     xo_set_formatter(NULL, formatter, checkpoint);
365     xo_set_flags(NULL, XOF_NO_VA_ARG | XOF_NO_TOP | XOF_NO_CLOSE);
366
367     fmt = *argv++;
368     if (opt_opener == NULL && opt_closer == NULL && fmt == NULL) {
369         print_help();
370         return 1;
371     }
372
373     if (opt_not_first)
374         xo_set_flags(NULL, XOF_NOT_FIRST);
375
376     if (opt_closer) {
377         opt_depth += 1;
378         for (cp = opt_closer; cp && *cp; cp = np) {
379             np = strchr(cp, '/');
380             if (np == NULL)
381                 break;
382             np += 1;
383             opt_depth += 1;
384         }
385     }
386
387     if (opt_depth > 0)
388         xo_set_depth(NULL, opt_depth);
389
390     if (opt_opener) {
391         for (cp = opt_opener; cp && *cp; cp = np) {
392             np = strchr(cp, '/');
393             if (np)
394                 *np = '\0';
395             xo_open_container(cp);
396             if (np)
397                 *np++ = '/';
398         }
399     }
400
401     if (opt_wrapper) {
402         for (cp = opt_wrapper; cp && *cp; cp = np) {
403             np = strchr(cp, '/');
404             if (np)
405                 *np = '\0';
406             xo_open_container(cp);
407             if (np)
408                 *np++ = '/';
409         }
410     }
411
412     if (fmt && *fmt) {
413         save_argv = argv;
414         prep_arg(fmt);
415         xo_emit(fmt);
416     }
417
418     while (opt_wrapper) {
419         np = strrchr(opt_wrapper, '/');
420         xo_close_container(np ? np + 1 : opt_wrapper);
421         if (np)
422             *np = '\0';
423         else
424             opt_wrapper = NULL;
425     }
426
427     while (opt_closer) {
428         np = strrchr(opt_closer, '/');
429         xo_close_container(np ? np + 1 : opt_closer);
430         if (np)
431             *np = '\0';
432         else
433             opt_closer = NULL;
434     }
435
436     xo_finish();
437
438     return 0;
439 }