1 /*
2 ** Copyright (C) 2011-2020 by Carnegie Mellon University.
3 **
4 ** @OPENSOURCE_LICENSE_START@
5 ** See license information in ../../LICENSE.txt
6 ** @OPENSOURCE_LICENSE_END@
7 */
8
9 /*
10 ** skoptionsctx.c
11 **
12 ** Support for --xargs, reading from stdin, and looping over
13 ** filenames on the command line.
14 **
15 ** Mark Thomas
16 ** May 2011
17 **
18 */
19
20 #include <silk/silk.h>
21
22 RCSIDENT("$SiLK: skoptionsctx.c ef14e54179be 2020-04-14 21:57:45Z mthomas $");
23
24 #include <silk/utils.h>
25 #include <silk/skstream.h>
26
27
28 /* LOCAL DEFINES AND TYPEDEFS */
29
30 #define PATH_IS_STDIN(path) \
31 (0 == strcmp((path), "-") || 0 == strcmp((path), "stdin"))
32 #define PATH_IS_STDOUT(path) \
33 (0 == strcmp((path), "-") || 0 == strcmp((path), "stdout"))
34
35 /* typedef struct sk_options_ctx_st sk_options_ctx_t; */
36 struct sk_options_ctx_st {
37 sk_options_ctx_open_cb_t open_cb_fn;
38 FILE *print_filenames;
39 skstream_t *xargs;
40 skstream_t *copy_input;
41 const char *input_pipe;
42 char **argv;
43 int argc;
44 int arg_index;
45 unsigned int flags;
46 unsigned stdin_used :1;
47 unsigned stdout_used :1;
48 unsigned parse_ok :1;
49 unsigned init_ok :1;
50 unsigned init_failed :1;
51 unsigned read_stdin :1;
52 unsigned no_more_inputs :1;
53 };
54
55
56 /* LOCAL VARIABLE DEFINITIONS */
57
58 static const struct options_ctx_options_st {
59 struct option opt;
60 const char *help;
61 } options_ctx_options[] = {
62 {{"print-filenames", NO_ARG, 0, SK_OPTIONS_CTX_PRINT_FILENAMES},
63 ("Print input filenames while processing. Def. no")},
64 {{"copy-input", REQUIRED_ARG, 0, SK_OPTIONS_CTX_COPY_INPUT},
65 ("Copy all input SiLK Flows to given pipe or file. Def. No")},
66 {{"input-pipe", REQUIRED_ARG, 0, SK_OPTIONS_CTX_INPUT_PIPE},
67 ("Get input byte stream from pipe (stdin|pipe).\n"
68 "\tThis switch is deprecated and will be removed in a future release.\n"
69 "\tDefault is stdin if no filenames are given on the command line")},
70 {{"xargs", OPTIONAL_ARG, 0, SK_OPTIONS_CTX_XARGS},
71 ("Read the names of the files to process from named text file,\n"
72 "\tone name per line, or from the standard input if no parameter."
73 " Def. no")},
74 {{0, 0, 0, 0}, 0} /* sentinel */
75 };
76
77
78
79 /* FUNCTION DEFINITIONS */
80
81 static const char *
optionsCtxSwitchName(int opt_index)82 optionsCtxSwitchName(
83 int opt_index)
84 {
85 size_t i;
86
87 for (i = 0; options_ctx_options[i].help; ++i) {
88 if (options_ctx_options[i].opt.val == opt_index) {
89 return options_ctx_options[i].opt.name;
90 }
91 }
92 skAbortBadCase(opt_index);
93 }
94
95 static int
optionsCtxHandler(clientData cData,int opt_index,char * opt_arg)96 optionsCtxHandler(
97 clientData cData,
98 int opt_index,
99 char *opt_arg)
100 {
101 sk_options_ctx_t *arg_ctx = (sk_options_ctx_t*)cData;
102 int rv;
103
104 if (opt_arg && strlen(opt_arg) == strspn(opt_arg, "\t\n\v\f\r ")) {
105 skAppPrintErr("Invalid %s: Argument contains only whitespace",
106 optionsCtxSwitchName(opt_index));
107 return 1;
108 }
109
110 switch (opt_index) {
111 case SK_OPTIONS_CTX_PRINT_FILENAMES:
112 arg_ctx->print_filenames = stderr;
113 break;
114
115 case SK_OPTIONS_CTX_COPY_INPUT:
116 if (arg_ctx->copy_input) {
117 skAppPrintErr("Invalid %s: Switch used multiple times",
118 optionsCtxSwitchName(opt_index));
119 return 1;
120 }
121 if (NULL == opt_arg || PATH_IS_STDOUT(opt_arg)) {
122 if (arg_ctx->stdout_used) {
123 skAppPrintErr("Multiple outputs attempt"
124 " to use standard output");
125 return 1;
126 }
127 arg_ctx->stdout_used = 1;
128 }
129 if ((rv = skStreamCreate(&arg_ctx->copy_input, SK_IO_WRITE,
130 SK_CONTENT_SILK_FLOW))
131 || (rv = skStreamBind(arg_ctx->copy_input, opt_arg)))
132 {
133 skStreamPrintLastErr(arg_ctx->copy_input, rv, skAppPrintErr);
134 skStreamDestroy(&arg_ctx->copy_input);
135 return 1;
136 }
137 break;
138
139 case SK_OPTIONS_CTX_XARGS:
140 if (arg_ctx->xargs) {
141 skAppPrintErr("Invalid %s: Switch used multiple times",
142 optionsCtxSwitchName(opt_index));
143 return 1;
144 }
145 if (NULL == opt_arg || PATH_IS_STDIN(opt_arg)) {
146 if (arg_ctx->stdin_used) {
147 skAppPrintErr("Multiple inputs attempt to use standard input");
148 return 1;
149 }
150 arg_ctx->stdin_used = 1;
151 }
152 if ((rv = skStreamCreate(&arg_ctx->xargs, SK_IO_READ, SK_CONTENT_TEXT))
153 || (rv = skStreamBind(arg_ctx->xargs, (opt_arg ? opt_arg : "-"))))
154 {
155 skStreamPrintLastErr(arg_ctx->xargs, rv, &skAppPrintErr);
156 skStreamDestroy(&arg_ctx->xargs);
157 return 1;
158 }
159 break;
160
161 case SK_OPTIONS_CTX_INPUT_PIPE:
162 if (arg_ctx->input_pipe) {
163 skAppPrintErr("Invalid %s: Switch used multiple times",
164 optionsCtxSwitchName(opt_index));
165 return 1;
166 }
167 if (NULL == opt_arg || PATH_IS_STDIN(opt_arg)) {
168 if (FILEIsATty(stdin)
169 && (arg_ctx->flags & (SK_OPTIONS_CTX_INPUT_BINARY
170 | SK_OPTIONS_CTX_INPUT_SILK_FLOW)))
171 {
172 skAppPrintErr(("Invalid %s '%s': "
173 "Will not read binary data on a terminal"),
174 optionsCtxSwitchName(SK_OPTIONS_CTX_INPUT_PIPE),
175 opt_arg);
176 return 1;
177 }
178 if (arg_ctx->stdin_used) {
179 skAppPrintErr("Multiple inputs attempt to use standard input");
180 return 1;
181 }
182 arg_ctx->stdin_used = 1;
183 }
184 arg_ctx->input_pipe = opt_arg;
185 break;
186
187 default:
188 skAbortBadCase(opt_index);
189 }
190
191 return 0;
192 }
193
194
195 int
skOptionsCtxCopyStreamClose(sk_options_ctx_t * arg_ctx,sk_msg_fn_t err_fn)196 skOptionsCtxCopyStreamClose(
197 sk_options_ctx_t *arg_ctx,
198 sk_msg_fn_t err_fn)
199 {
200 int rv;
201
202 if (arg_ctx->copy_input && arg_ctx->init_ok) {
203 rv = skStreamClose(arg_ctx->copy_input);
204 if (rv && err_fn) {
205 skStreamPrintLastErr(arg_ctx->copy_input, rv, err_fn);
206 }
207 return rv;
208 }
209 return 0;
210 }
211
212
213 int
skOptionsCtxCopyStreamIsActive(const sk_options_ctx_t * arg_ctx)214 skOptionsCtxCopyStreamIsActive(
215 const sk_options_ctx_t *arg_ctx)
216 {
217 return ((arg_ctx->copy_input) ? 1 : 0);
218 }
219
220
221 int
skOptionsCtxCopyStreamIsStdout(const sk_options_ctx_t * arg_ctx)222 skOptionsCtxCopyStreamIsStdout(
223 const sk_options_ctx_t *arg_ctx)
224 {
225 if (arg_ctx->copy_input) {
226 return PATH_IS_STDOUT(skStreamGetPathname(arg_ctx->copy_input));
227 }
228 return 0;
229 }
230
231
232 int
skOptionsCtxCountArgs(const sk_options_ctx_t * arg_ctx)233 skOptionsCtxCountArgs(
234 const sk_options_ctx_t *arg_ctx)
235 {
236 if (!arg_ctx->parse_ok) {
237 return -1;
238 }
239 return (arg_ctx->argc - arg_ctx->arg_index);
240 }
241
242
243 int
skOptionsCtxCreate(sk_options_ctx_t ** arg_ctx,unsigned int flags)244 skOptionsCtxCreate(
245 sk_options_ctx_t **arg_ctx,
246 unsigned int flags)
247 {
248 *arg_ctx = (sk_options_ctx_t*)calloc(1, sizeof(sk_options_ctx_t));
249 if (NULL == *arg_ctx) {
250 return -1;
251 }
252 (*arg_ctx)->flags = flags;
253 return 0;
254 }
255
256
257 int
skOptionsCtxDestroy(sk_options_ctx_t ** arg_ctx)258 skOptionsCtxDestroy(
259 sk_options_ctx_t **arg_ctx)
260 {
261 sk_options_ctx_t *ctx;
262 int rv = 0;
263
264 if (NULL == arg_ctx || NULL == *arg_ctx) {
265 return 0;
266 }
267 ctx = *arg_ctx;
268 *arg_ctx = NULL;
269
270 skStreamDestroy(&ctx->xargs);
271 if (ctx->copy_input) {
272 if (ctx->init_ok) {
273 rv = skStreamClose(ctx->copy_input);
274 }
275 skStreamDestroy(&ctx->copy_input);
276 }
277 free(ctx);
278 return rv;
279 }
280
281
282 FILE *
skOptionsCtxGetPrintFilenames(const sk_options_ctx_t * arg_ctx)283 skOptionsCtxGetPrintFilenames(
284 const sk_options_ctx_t *arg_ctx)
285 {
286 return arg_ctx->print_filenames;
287 }
288
289
290 int
skOptionsCtxNextArgument(sk_options_ctx_t * arg_ctx,char ** arg)291 skOptionsCtxNextArgument(
292 sk_options_ctx_t *arg_ctx,
293 char **arg)
294 {
295 static char buf[PATH_MAX];
296 int rv;
297
298 assert(arg_ctx);
299 assert(arg);
300
301 if (arg_ctx->no_more_inputs) {
302 return 1;
303 }
304 if (!arg_ctx->parse_ok || arg_ctx->init_failed) {
305 return -1;
306 }
307 if (!arg_ctx->init_ok) {
308 rv = skOptionsCtxOpenStreams(arg_ctx, NULL);
309 if (rv) {
310 return rv;
311 }
312 }
313
314 if (arg_ctx->xargs) {
315 for (;;) {
316 rv = skStreamGetLine(arg_ctx->xargs, buf, sizeof(buf), NULL);
317 if (SKSTREAM_OK == rv) {
318 *arg = buf;
319 return 0;
320 }
321 if (SKSTREAM_ERR_LONG_LINE == rv) {
322 continue;
323 }
324 arg_ctx->no_more_inputs = 1;
325 if (SKSTREAM_ERR_EOF == rv) {
326 return 1;
327 }
328 skStreamPrintLastErr(arg_ctx->xargs, rv, skAppPrintErr);
329 return -1;
330 }
331 }
332 if (arg_ctx->input_pipe) {
333 arg_ctx->no_more_inputs = 1;
334 *arg = (char*)arg_ctx->input_pipe;
335 return 0;
336 }
337 if (arg_ctx->read_stdin) {
338 arg_ctx->no_more_inputs = 1;
339 *arg = (char*)"-";
340 return 0;
341 }
342 if (arg_ctx->arg_index < arg_ctx->argc) {
343 *arg = arg_ctx->argv[arg_ctx->arg_index];
344 ++arg_ctx->arg_index;
345 return 0;
346 }
347 arg_ctx->no_more_inputs = 1;
348 return 1;
349 }
350
351
352 int
skOptionsCtxNextSilkFile(sk_options_ctx_t * arg_ctx,skstream_t ** stream,sk_msg_fn_t err_fn)353 skOptionsCtxNextSilkFile(
354 sk_options_ctx_t *arg_ctx,
355 skstream_t **stream,
356 sk_msg_fn_t err_fn)
357 {
358 char *path;
359 int rv;
360
361 for (;;) {
362 rv = skOptionsCtxNextArgument(arg_ctx, &path);
363 if (rv != 0) {
364 return rv;
365 }
366 rv = skStreamOpenSilkFlow(stream, path, SK_IO_READ);
367 if (rv != SKSTREAM_OK) {
368 if (err_fn) {
369 skStreamPrintLastErr(*stream, rv, err_fn);
370 skStreamDestroy(stream);
371 }
372 return -1;
373 }
374 if (arg_ctx->open_cb_fn) {
375 rv = arg_ctx->open_cb_fn(*stream);
376 if (rv) {
377 if (rv > 0) {
378 skStreamDestroy(stream);
379 continue;
380 }
381 return rv;
382 }
383 }
384 if (arg_ctx->copy_input) {
385 skStreamSetCopyInput(*stream, arg_ctx->copy_input);
386 }
387 if (arg_ctx->print_filenames) {
388 fprintf(arg_ctx->print_filenames, "%s\n", path);
389 }
390 return 0;
391 }
392 }
393
394
395 int
skOptionsCtxOpenStreams(sk_options_ctx_t * arg_ctx,sk_msg_fn_t err_fn)396 skOptionsCtxOpenStreams(
397 sk_options_ctx_t *arg_ctx,
398 sk_msg_fn_t err_fn)
399 {
400 int rv;
401
402 if (!arg_ctx->parse_ok) {
403 return -1;
404 }
405 if (arg_ctx->init_ok) {
406 return 0;
407 }
408 if (arg_ctx->init_failed) {
409 return -1;
410 }
411
412 if (arg_ctx->xargs) {
413 rv = skStreamOpen(arg_ctx->xargs);
414 if (rv) {
415 if (err_fn) {
416 skStreamPrintLastErr(arg_ctx->xargs, rv, err_fn);
417 }
418 arg_ctx->init_failed = 1;
419 return -1;
420 }
421 }
422 if (arg_ctx->copy_input) {
423 rv = skStreamOpen(arg_ctx->copy_input);
424 if (rv) {
425 if (err_fn) {
426 skStreamPrintLastErr(arg_ctx->copy_input, rv, err_fn);
427 }
428 arg_ctx->init_failed = 1;
429 return -1;
430 }
431 }
432
433 arg_ctx->init_ok = 1;
434 return 0;
435 }
436
437
438 /* FIXME: consider adding a separate flags parameter here */
439 int
skOptionsCtxOptionsParse(sk_options_ctx_t * arg_ctx,int argc,char ** argv)440 skOptionsCtxOptionsParse(
441 sk_options_ctx_t *arg_ctx,
442 int argc,
443 char **argv)
444 {
445 if (NULL == arg_ctx) {
446 return skOptionsParse(argc, argv);
447 }
448
449 arg_ctx->argc = argc;
450 arg_ctx->argv = argv;
451 arg_ctx->arg_index = skOptionsParse(argc, argv);
452 if (arg_ctx->arg_index < 0) {
453 return arg_ctx->arg_index;
454 }
455
456 /*
457 * if (ignore_non_switch_args) {
458 * return arg_index;
459 * }
460 */
461
462 /* handle case where all args are specified with switches */
463 if (arg_ctx->flags & SK_OPTIONS_CTX_SWITCHES_ONLY) {
464 if (arg_ctx->arg_index != argc) {
465 skAppPrintErr("Too many arguments or unrecognized switch '%s'",
466 argv[arg_ctx->arg_index]);
467 return -1;
468 }
469 return 0;
470 }
471
472 /* some sort of input is required */
473
474 if (arg_ctx->xargs) {
475 if (arg_ctx->input_pipe) {
476 skAppPrintErr("May not use both --%s and --%s",
477 optionsCtxSwitchName(SK_OPTIONS_CTX_XARGS),
478 optionsCtxSwitchName(SK_OPTIONS_CTX_INPUT_PIPE));
479 return 1;
480 }
481 if (arg_ctx->arg_index != argc) {
482 skAppPrintErr("May not use --%s and give files on command line",
483 optionsCtxSwitchName(SK_OPTIONS_CTX_XARGS));
484 return -1;
485 }
486 arg_ctx->parse_ok = 1;
487 return 0;
488 }
489
490 if (arg_ctx->input_pipe) {
491 if (arg_ctx->arg_index != argc) {
492 skAppPrintErr("May not use --%s and give files on command line",
493 optionsCtxSwitchName(SK_OPTIONS_CTX_INPUT_PIPE));
494 return -1;
495 }
496 arg_ctx->parse_ok = 1;
497 return 0;
498 }
499
500 if (!(arg_ctx->flags & SK_OPTIONS_CTX_ALLOW_STDIN)) {
501 if (arg_ctx->arg_index == argc) {
502 skAppPrintErr("No input files specified on the command line");
503 return -1;
504 }
505 arg_ctx->parse_ok = 1;
506 return 0;
507 }
508
509 /* stdin or files listed on command line allowed */
510
511 if (arg_ctx->arg_index < argc) {
512 arg_ctx->parse_ok = 1;
513 return 0;
514 }
515
516 if (FILEIsATty(stdin)
517 && (arg_ctx->flags &
518 (SK_OPTIONS_CTX_INPUT_BINARY | SK_OPTIONS_CTX_INPUT_SILK_FLOW)))
519 {
520 skAppPrintErr("No input files specified on the command line"
521 " and standard input is a terminal");
522 return -1;
523 }
524 if (arg_ctx->stdin_used) {
525 skAppPrintErr("Multiple inputs attempt to use standard input");
526 return 1;
527 }
528 arg_ctx->stdin_used = 1;
529 arg_ctx->read_stdin = 1;
530
531 arg_ctx->parse_ok = 1;
532 return 0;
533 }
534
535
536 int
skOptionsCtxOptionsRegister(const sk_options_ctx_t * arg_ctx)537 skOptionsCtxOptionsRegister(
538 const sk_options_ctx_t *arg_ctx)
539 {
540 size_t i;
541 int rv = 0;
542
543 for (i = 0; options_ctx_options[i].help && 0 == rv; ++i) {
544 if (arg_ctx->flags & options_ctx_options[i].opt.val) {
545 rv = skOptionsRegisterCount(&options_ctx_options[i].opt, 1,
546 optionsCtxHandler,(clientData)arg_ctx);
547 }
548 }
549 return rv;
550 }
551
552 void
skOptionsCtxOptionsUsage(const sk_options_ctx_t * arg_ctx,FILE * fh)553 skOptionsCtxOptionsUsage(
554 const sk_options_ctx_t *arg_ctx,
555 FILE *fh)
556 {
557 size_t i;
558
559 for (i = 0; options_ctx_options[i].help; ++i) {
560 if (arg_ctx->flags & options_ctx_options[i].opt.val) {
561 fprintf(fh, "--%s %s. %s\n", options_ctx_options[i].opt.name,
562 SK_OPTION_HAS_ARG(options_ctx_options[i].opt),
563 options_ctx_options[i].help);
564 }
565 }
566 }
567
568
569 void
skOptionsCtxSetOpenCallback(sk_options_ctx_t * arg_ctx,sk_options_ctx_open_cb_t open_callback_fn)570 skOptionsCtxSetOpenCallback(
571 sk_options_ctx_t *arg_ctx,
572 sk_options_ctx_open_cb_t open_callback_fn)
573 {
574 assert(arg_ctx);
575 arg_ctx->open_cb_fn = open_callback_fn;
576 }
577
578
579 /*
580 ** Local Variables:
581 ** mode:c
582 ** indent-tabs-mode:nil
583 ** c-basic-offset:4
584 ** End:
585 */
586