1 /*
2 ** Copyright (C) 2001-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 ** sku-options.c
11 **
12 ** Suresh L Konda
13 **
14 ** 12/4/2001
15 **
16 ** Routines to support long option parsing with multiple sets of options.
17 **
18 ** Four functions are exported:
19 ** skOptionsSetup();
20 ** skOptionsTeardown();
21 ** skOptionsRegister();
22 ** skOptionsParse();
23 **
24 ** Each client calls skOptionsRegister with:
25 ** 1. a pointer to struct option []
26 ** 2. a handler to process the option. The handler will be called with two
27 ** arguments:
28 ** 1. the clientData
29 ** 2. the original val value passed to the registry via
30 ** options associated with this option
31 ** 3. the optarg returned by getopt();
32 ** 3. an opaque pointer to arbitrary data called clientData which
33 ** Error Return : 1
34 **
35 ** Once all clients have registered, then call skOptionsParse with argc, argv
36 ** which parses the options and calls the handler as required.
37 **
38 ** It returns -1 on error or optind if OK. Thus, argv[optind] is the first
39 ** non-option argument given to the application.
40 **
41 ** Currently, we do NOT do flag versus val handling: flag is always
42 ** assumed to be NULL and val is the appropriate unique entity that
43 ** allows the handler to deal with the option to be parsed. It is
44 ** suggested that the caller use a distinct index value in the val part.
45 **
46 **
47 */
48
49 #include <silk/silk.h>
50
51 RCSIDENT("$SiLK: sku-options.c ef14e54179be 2020-04-14 21:57:45Z mthomas $");
52
53 #include <silk/utils.h>
54 #include <silk/sksite.h>
55 #include <silk/silk_files.h>
56 #include <silk/skstringmap.h>
57
58
59 /* TYPEDEFS AND DEFINES */
60
61 /* Where to write --version information */
62 #define VERS_FH stdout
63
64 /* Start options at this offset to avoid having an option with index
65 * of '?' (63) which is the value used to indicate an error. */
66 #define OPTION_OFFSET 64
67
68 /* Initial size of options arrays, and number of options to add to the
69 * arrays */
70 #define OPTION_ARRAY_NUM_ENTRIES 16
71
72 /* Message to print when out of memory */
73 #define SK_OPTION_NO_MEMORY(nomem_obj) \
74 skAppPrintOutOfMemory(#nomem_obj)
75
76 /* Name of environment variable containing the default value for the
77 * --ip-format switch */
78 #define SK_IP_FORMAT_ENVAR "SILK_IP_FORMAT"
79
80 /* Name of environment variable containing the default value for the
81 * --timestamp-format switch */
82 #define SK_TIMESTAMP_FORMAT_ENVAR "SILK_TIMESTAMP_FORMAT"
83
84
85 /*
86 * struct option has the following definition:
87 *
88 * struct option {
89 * char *name;
90 * int has_arg;
91 * int *flag;
92 * int val;
93 * };
94 *
95 */
96
97 typedef struct sk_options_map_st {
98 /* the callback function provided by the caller */
99 optHandler om_handler;
100 /* the callback data provided by the caller */
101 clientData om_data;
102 /* the index provided by the caller*/
103 int om_index;
104 } sk_options_map_t;
105
106 typedef struct sk_options_st {
107 /* function to use to print usage */
108 usage_fn_t o_usage_fn;
109 /* for printing version info */
110 usage_fn_t o_version_fn;
111 /* array of all options for this app */
112 struct option *o_options;
113 /* array mapping options to a particular options handler */
114 sk_options_map_t *o_map;
115 /* global option count */
116 size_t o_count;
117 /* size of the arrays */
118 size_t o_capacity;
119 } sk_options_t;
120
121
122
123 /* LOCAL VARIABLES */
124
125 static sk_options_t app_options_static;
126
127 static sk_options_t *app_options = &app_options_static;
128
129 typedef enum {
130 OPT_VAL_HELP, OPT_VAL_VERSION
131 } defaultOptionsEnum;
132
133 /* options that everyone gets */
134 static struct option defaultOptions[] = {
135 {"help", NO_ARG, 0, OPT_VAL_HELP},
136 {"version", NO_ARG, 0, OPT_VAL_VERSION},
137 {0,0,0,0} /* sentinel */
138 };
139
140 static const char *defaultHelp[] = {
141 "Print this usage output and exit. Def. No",
142 "Print this program's version and exit. Def. No",
143 (char*)NULL /* sentinel */
144 };
145
146 /* All shortened forms of help should invoke help. This lets us
147 * define options like --help-foo and --help-bar. */
148 static const struct option optionAliases[] = {
149 {"hel" , NO_ARG, 0, OPT_VAL_HELP},
150 {"he", NO_ARG, 0, OPT_VAL_HELP},
151 {"h", NO_ARG, 0, OPT_VAL_HELP},
152 {0,0,0,0} /* sentinel */
153 };
154
155
156 /* FUNCTION DEFINITONS */
157
158 void
skOptionsDefaultUsage(FILE * fh)159 skOptionsDefaultUsage(
160 FILE *fh)
161 {
162 int i;
163 for (i = 0; defaultOptions[i].name; ++i) {
164 fprintf(fh, "--%s %s. %s\n", defaultOptions[i].name,
165 SK_OPTION_HAS_ARG(defaultOptions[i]), defaultHelp[i]);
166 }
167 }
168
169
170 /*
171 * printVersion();
172 *
173 * Print version information and information about how SiLK was
174 * configured.
175 */
176 static void
printVersion(void)177 printVersion(
178 void)
179 {
180 #define COPYRIGHT_LICENSE \
181 ("Copyright (C) 2001-2020 by Carnegie Mellon University\n" \
182 "GNU General Public License (GPL) Rights" \
183 " pursuant to Version 2, June 1991.\n" \
184 "Some included library code covered by LGPL 2.1;" \
185 " see source for details.\n" \
186 "Government Purpose License Rights (GPLR)" \
187 " pursuant to DFARS 252.227-7013.")
188
189 uint8_t default_compmethod;
190 uint8_t i;
191 char comp_name[SK_MAX_STRLEN_FILE_FORMAT+1];
192 const char *packing_logic;
193 const char *python_dir = SILK_PYTHON_SITE_PKG;
194
195 fprintf(VERS_FH, "%s: part of %s %s; configuration settings:\n",
196 skAppName(), SK_PACKAGE_NAME, SK_PACKAGE_VERSION);
197
198 fprintf(VERS_FH, " * %-32s %s\n",
199 "Root of packed data tree:", sksiteGetDefaultRootDir());
200
201 #ifndef SK_PACKING_LOGIC_PATH
202 packing_logic = "Run-time plug-in";
203 #else
204 packing_logic = SK_PACKING_LOGIC_PATH;
205 if (strrchr(packing_logic, '/')) {
206 packing_logic = 1 + strrchr(packing_logic, '/');
207 }
208 #endif
209 fprintf(VERS_FH, " * %-32s %s\n",
210 "Packing logic:", packing_logic);
211
212 fprintf(VERS_FH, " * %-32s %s\n",
213 "Timezone support:",
214 #if SK_ENABLE_LOCALTIME
215 "local"
216 #else
217 "UTC"
218 #endif
219 );
220
221 default_compmethod = skCompMethodGetDefault();
222 skCompMethodGetName(comp_name, sizeof(comp_name), default_compmethod);
223 fprintf(VERS_FH, " * %-32s %s [default]",
224 "Available compression methods:", comp_name);
225
226 for (i = 0; skCompMethodCheck(i); ++i) {
227 if (i == default_compmethod) {
228 continue;
229 }
230 if (SK_COMPMETHOD_IS_AVAIL != skCompMethodCheck(i)) {
231 /* not available */
232 continue;
233 }
234 skCompMethodGetName(comp_name, sizeof(comp_name), i);
235 fprintf(VERS_FH, ", %s", comp_name);
236 }
237 fprintf(VERS_FH, "\n");
238
239 fprintf(VERS_FH, " * %-32s %s\n",
240 "IPv6 network connections:",
241 #if SK_ENABLE_INET6_NETWORKING
242 "yes"
243 #else
244 "no"
245 #endif
246 );
247
248 fprintf(VERS_FH, " * %-32s %s\n",
249 "IPv6 flow record support:",
250 #if SK_ENABLE_IPV6
251 "yes"
252 #else
253 "no"
254 #endif
255 );
256
257 fprintf(VERS_FH, " * %-32s %s\n",
258 "IPset record compatibility:",
259 #if !defined(SK_IPSET_DEFAULT_VERSION)
260 "1.0.0"
261 #elif SK_IPSET_DEFAULT_VERSION == 5
262 "3.14.0"
263 #elif SK_IPSET_DEFAULT_VERSION == 4
264 "3.7.0"
265 #else
266 "1.0.0"
267 #endif
268 );
269
270 fprintf(VERS_FH, " * %-32s %s\n",
271 "IPFIX/NetFlow9/sFlow collection:",
272 #if SK_ENABLE_IPFIX
273 "ipfix,netflow9,sflow"
274 #else
275 "no"
276 #endif
277 );
278
279
280 fprintf(VERS_FH, " * %-32s %s\n",
281 "Transport encryption:",
282 #if SK_ENABLE_GNUTLS
283 "GnuTLS"
284 #else
285 "no"
286 #endif
287 );
288
289 fprintf(VERS_FH, " * %-32s %s\n",
290 "PySiLK support:", ((python_dir[0]) ? python_dir : "no"));
291
292 fprintf(VERS_FH, " * %-32s %s\n",
293 "Enable assert():",
294 #ifndef NDEBUG
295 "yes"
296 #else
297 "no"
298 #endif
299 );
300
301 fprintf(VERS_FH,
302 ("%s\n"
303 "Send bug reports, feature requests, and comments to %s.\n"),
304 COPYRIGHT_LICENSE, SK_PACKAGE_BUGREPORT);
305 }
306
307
308 /*
309 * status = defaultOptionsHandler(cData, opt_index, opt_arg);
310 *
311 * Called by skOptionsParse() to handle the default/global options
312 * defined in the defaultOptions[] array. This handler will exit
313 * the application.
314 */
315 static int
defaultOptionsHandler(clientData UNUSED (cData),int opt_index,char UNUSED (* opt_arg))316 defaultOptionsHandler(
317 clientData UNUSED(cData),
318 int opt_index,
319 char UNUSED(*opt_arg))
320 {
321 switch ((defaultOptionsEnum)opt_index) {
322 case OPT_VAL_HELP:
323 app_options->o_usage_fn();
324 break;
325
326 case OPT_VAL_VERSION:
327 app_options->o_version_fn();
328 break;
329 }
330
331 skAppUnregister();
332 exit(EXIT_SUCCESS);
333 return 0; /* NOTREACHED */
334 }
335
336
337 static void
defaultHelpOutput(void)338 defaultHelpOutput(
339 void)
340 {
341 skAppStandardUsage(stdout, "", NULL, NULL);
342 }
343
344
345 void
skOptionsSetup(void)346 skOptionsSetup(
347 void)
348 {
349 /* check whether already called */
350 if (app_options->o_usage_fn) {
351 return;
352 }
353
354 /* tell getopt_long() that it should print errors */
355 opterr = 1;
356
357 /* set a default usage function */
358 skOptionsSetUsageCallback(&defaultHelpOutput);
359
360 /* set the version function */
361 skOptionsSetVersionCallback(&printVersion);
362
363 /* allocate initial space */
364 app_options->o_options = (struct option*)calloc(OPTION_ARRAY_NUM_ENTRIES,
365 sizeof(struct option));
366 app_options->o_map = (sk_options_map_t*)calloc(OPTION_ARRAY_NUM_ENTRIES,
367 sizeof(sk_options_map_t));
368 if (!app_options->o_options || !app_options->o_map) {
369 SK_OPTION_NO_MEMORY(app_options->o_options);
370 exit(EXIT_FAILURE);
371 }
372
373 app_options->o_count = 0;
374 app_options->o_capacity = OPTION_ARRAY_NUM_ENTRIES;
375
376 /* add default switches */
377 if (skOptionsRegister(defaultOptions, defaultOptionsHandler, NULL)) {
378 skAppPrintErr("Unable to set default options");
379 exit(EXIT_FAILURE);
380 }
381 if (skOptionsRegister(optionAliases, defaultOptionsHandler, NULL)) {
382 skAppPrintErr("Unable to set default options");
383 exit(EXIT_FAILURE);
384 }
385 }
386
387
388 void
skOptionsSetUsageCallback(usage_fn_t help_fn)389 skOptionsSetUsageCallback(
390 usage_fn_t help_fn)
391 {
392 app_options->o_usage_fn = help_fn;
393 }
394
395
396 void
skOptionsSetVersionCallback(usage_fn_t version_fn)397 skOptionsSetVersionCallback(
398 usage_fn_t version_fn)
399 {
400 app_options->o_version_fn = version_fn;
401 }
402
403
404 void
skOptionsTeardown(void)405 skOptionsTeardown(
406 void)
407 {
408 if ( app_options->o_options == 0) {
409 return;
410 }
411 free(app_options->o_options);
412 free(app_options->o_map);
413 app_options->o_options = /* (struct option (*)[1]) */ NULL;
414 app_options->o_map = /* (sk_options_map (*)[1]) */ NULL;
415 return;
416 }
417
418
419 int
skOptionsRegister(const struct option * options,optHandler handler,clientData cData)420 skOptionsRegister(
421 const struct option *options,
422 optHandler handler,
423 clientData cData)
424 {
425 return skOptionsRegisterCount(options, 0, handler, cData);
426 }
427
428
429 int
skOptionsRegisterCount(const struct option * new_options,size_t num_options,optHandler handler,clientData cData)430 skOptionsRegisterCount(
431 const struct option *new_options,
432 size_t num_options,
433 optHandler handler,
434 clientData cData)
435 {
436 struct option *cur_options;
437 size_t i;
438 size_t j;
439 size_t new_capacity;
440 void *old_mem;
441
442 if (app_options->o_usage_fn == NULL) {
443 skAppPrintErr("Must call skOptionsSetup() before registering options");
444 return -1;
445 }
446
447 /* count the options that were passed in */
448 if (num_options == 0) {
449 for (j = 0; new_options[j].name; ++j)
450 ;
451 num_options = j;
452 } else {
453 for (j = 0; j < num_options && new_options[j].name; ++j)
454 ;
455 num_options = j;
456 }
457
458 if (0 == num_options) {
459 /* empty options list */
460 return 0;
461 }
462
463 /* New total number of options will be the current value plus the
464 * number of options passed in. */
465 new_capacity = app_options->o_count + num_options;
466
467 /* Determine whether we need to grow the arrays. The capacity
468 * must remain greater than the count, because we need to have one
469 * blank space for the sentinel. */
470 if (new_capacity >= app_options->o_capacity) {
471
472 /* allow space for several additional entries */
473 new_capacity += OPTION_ARRAY_NUM_ENTRIES;
474
475 /*
476 * Get or grow the space for the arrays.
477 */
478 old_mem = app_options->o_options;
479 app_options->o_options
480 = (struct option*)realloc(app_options->o_options,
481 (new_capacity * sizeof(struct option)));
482 if (app_options->o_options == NULL) {
483 app_options->o_options = (struct option*)old_mem;
484 SK_OPTION_NO_MEMORY(app_options->o_options);
485 return -1;
486 }
487
488 old_mem = app_options->o_map;
489 app_options->o_map
490 = (sk_options_map_t*)realloc(app_options->o_map,
491 (new_capacity
492 * sizeof(sk_options_map_t)));
493 if (app_options->o_map == NULL) {
494 app_options->o_map = (sk_options_map_t*)old_mem;
495 SK_OPTION_NO_MEMORY(app_options->o_map);
496 return -1;
497 }
498
499 app_options->o_capacity = new_capacity;
500 }
501
502 for (j = 0; j < num_options; ++j, ++new_options) {
503 /* check for name clashes */
504 for (i = 0, cur_options = app_options->o_options;
505 i < app_options->o_count;
506 ++i, ++cur_options)
507 {
508 if (strcmp(cur_options->name, new_options->name)==0) {
509 skAppPrintErr("Cannot register option '%s': name already used",
510 new_options->name);
511 return -1;
512 }
513 }
514
515 assert(cur_options == &app_options->o_options[app_options->o_count]);
516
517 /* a clean new entry. record it. */
518 cur_options->name = new_options->name;
519 cur_options->has_arg = new_options->has_arg;
520 cur_options->flag = new_options->flag;
521
522 /* the 'val' used internally is the OPTION_OFFSET plus the
523 * index into the 'o_map' array; the o_map array will be used to
524 * get the 'val' the called handed us. */
525 cur_options->val = OPTION_OFFSET + app_options->o_count;
526
527 /* original val to be returned with handler */
528 app_options->o_map[app_options->o_count].om_index = new_options->val;
529 app_options->o_map[app_options->o_count].om_handler = handler;
530 app_options->o_map[app_options->o_count].om_data = cData;
531
532 ++app_options->o_count;
533 }
534
535 /* set the sentinel for o_options */
536 memset(&app_options->o_options[app_options->o_count], 0,
537 sizeof(struct option));
538
539 return 0;
540 }
541
542
543 /*
544 * skOptionsParse:
545 * Adjust the global options array to allow for the help
546 * option. If help is selected by the user, call the stashed
547 * usageFunction. Parse input options given a set of
548 * pre-registered options and their handlers. For each
549 * legitimate option, call the handler.
550 * SideEffects:
551 * The individual handlers update whatever datastruture they wish
552 * to via the clientData argument to the handler.
553 * Return:
554 * optind which points at the first non-option argument passed if
555 * all is OK. If not OK, the return -1 for error.
556 */
557 int
skOptionsParse(int argc,char ** argv)558 skOptionsParse(
559 int argc,
560 char **argv)
561 {
562 int done = 0;
563 int c;
564 int idx;
565
566 while (! done) {
567 int option_index;
568 #ifdef SK_HAVE_GETOPT_LONG_ONLY
569 c = getopt_long_only(argc, argv, "",
570 (const struct option *)app_options->o_options,
571 &option_index);
572 #else
573 c = _getopt_internal(argc, argv, "",
574 (const struct option *)app_options->o_options,
575 &option_index, 1);
576 #endif
577 switch (c) {
578
579 case '?':
580 /*skAppPrintErr("Invalid or ambiguous option"); */
581 return -1;
582
583 case -1:
584 done = 1;
585 break;
586
587 default:
588 /* a legit value: call the handler */
589 idx = c - OPTION_OFFSET;
590 if (app_options->o_map[idx].om_handler(
591 app_options->o_map[idx].om_data,
592 app_options->o_map[idx].om_index,
593 optarg))
594 {
595 /* handler indicated an error */
596 return -1;
597 }
598 break;
599 }
600 }
601
602 return optind;
603 }
604
605
606 /* find shortest unique prefix for the option 'option_name' */
607 int
skOptionsGetShortestPrefix(const char * option_name)608 skOptionsGetShortestPrefix(
609 const char *option_name)
610 {
611 struct option *opt = NULL;
612 const char *cp;
613 const char *sp;
614 int longest = 0;
615 size_t i;
616 int j;
617
618 /* check that the input inupt */
619 if (option_name == NULL || option_name[0] == '\0') {
620 return -1;
621 }
622
623 /* find 'option_name' in the list of all options */
624 for (i = 0, opt = app_options->o_options;
625 i < app_options->o_count;
626 ++i, ++opt)
627 {
628 if (0 == strcmp(option_name, opt->name)) {
629 break;
630 }
631 }
632
633 if (i == app_options->o_count) {
634 /* did not find 'option_name' in the list of options, or no
635 * options have been registered. */
636 return -1;
637 }
638
639 for (i = 0; i < app_options->o_count; ++i) {
640 if (opt->val == app_options->o_options[i].val) {
641 /* skip options that map to same value as 'option_name' */
642 continue;
643 }
644
645 /* find the character where the strings differ */
646 for (j = 1, cp = option_name, sp = app_options->o_options[i].name;
647 *cp && *sp && *cp == *sp;
648 ++j, ++cp, ++sp)
649 ; /* empty */
650
651 if (*cp == '\0') {
652 /* reached end of option_name. if *sp is NUL, we have
653 * matched ourself, which we should have avoided
654 * above. */
655 assert(*sp != '\0');
656
657 /* since option_name is a substring of
658 * o_options[].name, the full option name is always
659 * required. */
660 return j;
661 }
662
663 if (j > longest) {
664 longest = j;
665 }
666 }
667
668 return longest;
669 }
670
671
672 /* check whether dirname exists */
673 int
skOptionsCheckDirectory(const char * dirname,const char * option_name)674 skOptionsCheckDirectory(
675 const char *dirname,
676 const char *option_name)
677 {
678 if (!dirname || !dirname[0]) {
679 skAppPrintErr("Invalid %s: The directory name is empty",
680 option_name);
681 return -1;
682 }
683 if (strlen(dirname)+1 >= PATH_MAX) {
684 skAppPrintErr("Invalid %s: The directory name is too long",
685 option_name);
686 return -1;
687 }
688 if (!skDirExists(dirname)) {
689 skAppPrintErr("Invalid %s: Nonexistent path '%s'",
690 option_name, dirname);
691 return -1;
692 }
693 if (dirname[0] != '/') {
694 skAppPrintErr(("Invalid %s: Must use complete path"
695 " ('%s' does not begin with slash)"),
696 option_name, dirname);
697 return -1;
698 }
699 return 0;
700 }
701
702
703 #if 0
704 /* verify argument contains printable characters other than space */
705 int
706 skOptionsCheckContainsPrintable(
707 const char *opt_argument,
708 const char *option_name)
709 {
710 const char *cp;
711
712 if (opt_argument) {
713 cp = opt_argument;
714 while (*cp && (!isprint((int)*cp) || *cp == ' ')) {
715 ++cp;
716 }
717 if (*cp == '\0') {
718 skAppPrintErr(("Invalid %s: Argument does not contain printable"
719 " characters"),
720 option_name);
721 return -1;
722 }
723 }
724 return 0;
725 }
726 #endif /* 0 */
727
728
729 /* *******************************************************************
730 * Support for setting the temporary directory
731 */
732
733 static struct option tempdir_option[] = {
734 {"temp-directory", REQUIRED_ARG, 0, 0},
735 {0,0,0,0} /* sentinel */
736 };
737
738 static int
tempdir_option_handler(clientData cData,int UNUSED (opt_index),char * opt_arg)739 tempdir_option_handler(
740 clientData cData,
741 int UNUSED(opt_index),
742 char *opt_arg)
743 {
744 const char **var_location = (const char**)cData;
745
746 assert(opt_index == 0);
747 assert(opt_arg);
748 *var_location = opt_arg;
749 return 0;
750 }
751
752 int
skOptionsTempDirRegister(const char ** var_location)753 skOptionsTempDirRegister(
754 const char **var_location)
755 {
756 if (var_location == NULL) {
757 return -1;
758 }
759 return skOptionsRegister(tempdir_option, tempdir_option_handler,
760 (clientData)var_location);
761 }
762
763 void
skOptionsTempDirUsage(FILE * fh)764 skOptionsTempDirUsage(
765 FILE *fh)
766 {
767 fprintf(fh,
768 ("--%s %s. Store temporary files in this directory.\n"
769 "\tDef. $" SK_TEMPDIR_ENVAR1 " or $" SK_TEMPDIR_ENVAR2
770 #ifdef SK_TEMPDIR_DEFAULT
771 " or " SK_TEMPDIR_DEFAULT
772 #endif
773 "\n"),
774 tempdir_option[0].name, SK_OPTION_HAS_ARG(tempdir_option[0]));
775 }
776
777
778
779 /* *******************************************************************
780 * Support for formatting IP addresses
781 */
782
783 /* flags passed to skOptionsIPFormatRegister() that determines what
784 * switches to enable */
785 static uint32_t ip_format_flags = 0;
786
787 /* some values in ipformat_names[] may not be combined. this array
788 * holds values used to check for invalid combinations. Each entry is
789 * two 16 bit values where the lower bits indicate the parameter and
790 * the upper 16 bits are the mask of values it conflicts with. */
791 static const uint32_t ip_format_param_group[] = {
792 /* bits | mask */
793 0x0001 | (0x000F << 16), /* canonical */
794 0x0002 | (0x000F << 16), /* decimal */
795 0x0004 | (0x000F << 16), /* hexadecimal */
796 0x0008 | (0x000F << 16), /* no-mixed */
797 0x0000 | (0x0000 << 16), /* zero-padded */
798 0x0010 | (0x0030 << 16), /* map-v4 */
799 0x0020 | (0x0030 << 16), /* unmap-v6 */
800 0x0018 | (0x003F << 16) /* force-ipv6 */
801 };
802
803
804 enum ipformat_option_en {
805 OPT_VAL_IP_FORMAT, OPT_VAL_INTEGER_IPS, OPT_VAL_ZERO_PAD_IPS
806 };
807
808 static const struct option ipformat_option[] = {
809 {"ip-format", REQUIRED_ARG, 0, OPT_VAL_IP_FORMAT},
810 {"integer-ips", NO_ARG, 0, OPT_VAL_INTEGER_IPS},
811 {"zero-pad-ips", NO_ARG, 0, OPT_VAL_ZERO_PAD_IPS},
812 {0,0,0,0} /* sentinel */
813 };
814
815 /* printed IP address formats: the first of these will be the default */
816 static const sk_stringmap_entry_t ipformat_names[] = {
817 {"canonical", SKIPADDR_CANONICAL,
818 "in canonical format (192.0.2.1, 2001:db8::1)",
819 &ip_format_param_group[0]},
820 {"decimal", SKIPADDR_DECIMAL,
821 "as integer number in decimal format",
822 &ip_format_param_group[1]},
823 {"hexadecimal", SKIPADDR_HEXADECIMAL,
824 "as integer number in hexadecimal format",
825 &ip_format_param_group[2]},
826 {"no-mixed", SKIPADDR_NO_MIXED,
827 "in canonical format but no mixed IPv4/IPv6 for IPv6 IPs",
828 &ip_format_param_group[3]},
829 {"zero-padded", SKIPADDR_ZEROPAD,
830 "pad result to its maximum width with zeros",
831 &ip_format_param_group[4]},
832 {"map-v4", SKIPADDR_MAP_V4,
833 "map IPv4 into ::ffff:0:0/96 netblock prior to formatting",
834 &ip_format_param_group[5]},
835 {"unmap-v6", SKIPADDR_UNMAP_V6,
836 "convert IPv6 in ::ffff:0:0/96 to IPv4 prior to formatting",
837 &ip_format_param_group[6]},
838 {"force-ipv6", SKIPADDR_FORCE_IPV6,
839 "alias equivalent to \"map-v4,no-mixed\"",
840 &ip_format_param_group[7]},
841 SK_STRINGMAP_SENTINEL
842 };
843
844
845 /*
846 * If the SK_OPTION_IP_FORMAT_UNMAP_V6 flag was passed to
847 * skOptionsIPFormatRegister(), enable unmap-v6 (SKIPADDR_UNMAP_V6)
848 * in the in the ip formatting flags unless the user selected
849 * decimal or hexadecimal as the format, or the user specified
850 * map-v4 (SKIPADDR_MAP_V4).
851 */
852 static void
ipformat_check_unmapv6(uint32_t * out_flags)853 ipformat_check_unmapv6(
854 uint32_t *out_flags)
855 {
856 if (ip_format_flags & SK_OPTION_IP_FORMAT_UNMAP_V6) {
857 switch (*out_flags & 0x7f) {
858 case SKIPADDR_DECIMAL:
859 case SKIPADDR_HEXADECIMAL:
860 break;
861 default:
862 if (0 == (SKIPADDR_MAP_V4 & *out_flags)) {
863 *out_flags |= SKIPADDR_UNMAP_V6;
864 }
865 break;
866 }
867 }
868 }
869
870
871 /*
872 * status = ipformat_option_parse(format_string, out_flags);
873 *
874 * Parse the ip-format value contained in 'format_string' and set
875 * 'out_flags' to the result of parsing the string. Return 0 on
876 * success, or -1 if parsing of the value fails.
877 */
878 static int
ipformat_option_parse(const char * format,uint32_t * out_flags,const char * option_name)879 ipformat_option_parse(
880 const char *format,
881 uint32_t *out_flags,
882 const char *option_name)
883 {
884 char *errmsg;
885 sk_stringmap_t *str_map = NULL;
886 sk_stringmap_iter_t *iter = NULL;
887 sk_stringmap_entry_t *found_entry;
888 const sk_stringmap_entry_t *entry;
889 uint32_t groups_seen = 0;
890 uint32_t bits;
891 uint32_t mask;
892 int rv = -1;
893
894 assert(sizeof(ip_format_param_group)/sizeof(ip_format_param_group[0])
895 == (sizeof(ipformat_names)/sizeof(ipformat_names[0]) - 1));
896
897 /* create a stringmap of the available ip formats */
898 if (SKSTRINGMAP_OK != skStringMapCreate(&str_map)) {
899 skAppPrintOutOfMemory(NULL);
900 goto END;
901 }
902 if (skStringMapAddEntries(str_map, -1, ipformat_names) != SKSTRINGMAP_OK){
903 skAppPrintOutOfMemory(NULL);
904 goto END;
905 }
906
907 /* attempt to match */
908 if (skStringMapParse(str_map, format, SKSTRINGMAP_DUPES_ERROR,
909 &iter, &errmsg))
910 {
911 skAppPrintErr("Invalid %s: %s", option_name, errmsg);
912 goto END;
913 }
914
915 *out_flags = 0;
916
917 while (skStringMapIterNext(iter, &found_entry, NULL) == SK_ITERATOR_OK) {
918 bits = 0xFFFF & *(uint32_t *)found_entry->userdata;
919 mask = (*(uint32_t *)found_entry->userdata) >> 16;
920 /* check whether have seen another argument in this group */
921 if (groups_seen & (mask & ~bits)) {
922 /* yes, we have; generate error msg and return */
923 char buf[256] = "";
924 int first = 1;
925 for (entry = ipformat_names; entry->name; ++entry) {
926 uint32_t b = 0xFFFF & *(uint32_t *)entry->userdata;
927 if (b & (groups_seen & mask)) {
928 if (first) {
929 first = 0;
930 } else {
931 strncat(buf, ",", sizeof(buf)-strlen(buf)-1);
932 }
933 strncat(buf, entry->name, sizeof(buf)-strlen(buf)-1);
934 }
935 }
936 skAppPrintErr("Invalid %s: May not combine %s with %s",
937 option_name, found_entry->name, buf);
938 goto END;
939 }
940 groups_seen |= bits;
941 *out_flags |= found_entry->id;
942 }
943
944 ipformat_check_unmapv6(out_flags);
945
946 rv = 0;
947
948 END:
949 skStringMapDestroy(str_map);
950 skStringMapIterDestroy(iter);
951 return rv;
952 }
953
954 static int
ipformat_option_handler(clientData cData,int opt_index,char * opt_arg)955 ipformat_option_handler(
956 clientData cData,
957 int opt_index,
958 char *opt_arg)
959 {
960 uint32_t *var_location = (uint32_t*)cData;
961
962 switch ((enum ipformat_option_en)opt_index) {
963 case OPT_VAL_IP_FORMAT:
964 if (ipformat_option_parse(
965 opt_arg, var_location, ipformat_option[opt_index].name))
966 {
967 return 1;
968 }
969 break;
970 case OPT_VAL_INTEGER_IPS:
971 assert(ip_format_flags & SK_OPTION_IP_FORMAT_INTEGER_IPS);
972 if (ipformat_option_parse(
973 "decimal", var_location, ipformat_option[opt_index].name))
974 {
975 skAbort();
976 }
977 break;
978 case OPT_VAL_ZERO_PAD_IPS:
979 assert(ip_format_flags & SK_OPTION_IP_FORMAT_ZERO_PAD_IPS);
980 if (ipformat_option_parse(
981 "zero-padded", var_location, ipformat_option[opt_index].name))
982 {
983 skAbort();
984 }
985 break;
986 }
987
988 return 0;
989 }
990
991 int
skOptionsIPFormatRegister(uint32_t * var_location,uint32_t flags)992 skOptionsIPFormatRegister(
993 uint32_t *var_location,
994 uint32_t flags)
995 {
996 struct option opts[2];
997 const char *env;
998 uint32_t tmp_val = 0;
999 unsigned int i;
1000 int rv = 0;
1001
1002 if (var_location == NULL) {
1003 return -1;
1004 }
1005
1006 ip_format_flags = flags;
1007 ipformat_check_unmapv6(var_location);
1008
1009 env = getenv(SK_IP_FORMAT_ENVAR);
1010 if (env && env[0]) {
1011 if (0 == ipformat_option_parse(env, &tmp_val, SK_IP_FORMAT_ENVAR)) {
1012 *var_location = tmp_val;
1013 }
1014 }
1015
1016 memset(opts, 0, sizeof(opts));
1017
1018 for (i = 0; ipformat_option[i].name; ++i) {
1019 if ((0 == i) || (ip_format_flags & (1 << (i - 1)))) {
1020 memcpy(opts, &ipformat_option[i], sizeof(struct option));
1021 rv = skOptionsRegister(opts, ipformat_option_handler,
1022 (clientData)var_location);
1023 if (rv) {
1024 return rv;
1025 }
1026 }
1027 }
1028 return rv;
1029 }
1030
1031
1032 /*
1033 * skOptionsIPFormatUsage(fh);
1034 *
1035 * Print the description of the argument to the --ip-format
1036 * switch to the 'fh' file handle.
1037 */
1038 void
skOptionsIPFormatUsage(FILE * fh)1039 skOptionsIPFormatUsage(
1040 FILE *fh)
1041 {
1042 const sk_stringmap_entry_t *e;
1043 char defaults[256] = "";
1044
1045 if (0 == (ip_format_flags & SK_OPTION_IP_FORMAT_UNMAP_V6)) {
1046 strncpy(defaults, ipformat_names[0].name, sizeof(defaults));
1047 } else {
1048 for (e = ipformat_names; e->name; ++e) {
1049 if (e->id == SKIPADDR_UNMAP_V6) {
1050 snprintf(defaults, sizeof(defaults), "%s,%s",
1051 ipformat_names[0].name, e->name);
1052 break;
1053 }
1054 }
1055 }
1056 assert(defaults[0]);
1057
1058 fprintf(fh, ("--%s %s. Print each IP address in the specified format.\n"
1059 "\tDef. $" SK_IP_FORMAT_ENVAR " or %s. Choices:\n"),
1060 ipformat_option[OPT_VAL_IP_FORMAT].name,
1061 SK_OPTION_HAS_ARG(ipformat_option[OPT_VAL_IP_FORMAT]), defaults);
1062 for (e = ipformat_names; e->name; ++e) {
1063 if (e->id == SKIPADDR_ZEROPAD) {
1064 fprintf(fh, "\tThe following may be combined with the above:\n");
1065 }
1066 fprintf(fh, "\t%-11s - %s\n", e->name, e->description);
1067 }
1068
1069 if (ip_format_flags & SK_OPTION_IP_FORMAT_INTEGER_IPS) {
1070 fprintf(fh, "--%s %s. DEPRECATED. Equivalent to --ip-format=decimal\n",
1071 ipformat_option[OPT_VAL_INTEGER_IPS].name,
1072 SK_OPTION_HAS_ARG(ipformat_option[OPT_VAL_INTEGER_IPS]));
1073 }
1074 if (ip_format_flags & SK_OPTION_IP_FORMAT_ZERO_PAD_IPS) {
1075 fprintf(fh,
1076 "--%s %s. DEPRECATED. Equivalent to --ip-format=zero-padded\n",
1077 ipformat_option[OPT_VAL_ZERO_PAD_IPS].name,
1078 SK_OPTION_HAS_ARG(ipformat_option[OPT_VAL_ZERO_PAD_IPS]));
1079 }
1080 }
1081
1082
1083 /* *******************************************************************
1084 * Support for formatting Timestamps
1085 */
1086
1087 static uint32_t time_format_flags = 0;
1088
1089 static char time_format_epoch_name[256];
1090
1091 enum time_format_option_en {
1092 OPT_VAL_TIMESTAMP_FORMAT, OPT_VAL_EPOCH_TIME, OPT_VAL_LEGACY_TIMESTAMPS
1093 };
1094
1095 static const struct option time_format_option[] = {
1096 {"timestamp-format", REQUIRED_ARG, 0, OPT_VAL_TIMESTAMP_FORMAT},
1097 {"epoch-time", NO_ARG, 0, OPT_VAL_EPOCH_TIME},
1098 {"legacy-timestamps", OPTIONAL_ARG, 0, OPT_VAL_LEGACY_TIMESTAMPS},
1099 {0,0,0,0} /* sentinel */
1100 };
1101
1102 /* timestamp formats: the first of these will be the default */
1103 static const sk_stringmap_entry_t time_format_names[] = {
1104 {"default", 0, "yyyy/mm/ddThh:mm:ss", NULL},
1105 {"iso", SKTIMESTAMP_ISO, "yyyy-mm-dd hh:mm:ss", NULL},
1106 {"m/d/y", SKTIMESTAMP_MMDDYYYY, "mm/dd/yyyy hh:mm:ss", NULL},
1107 {"epoch", SKTIMESTAMP_EPOCH,
1108 "seconds since UNIX epoch; ignores timezone", NULL},
1109 SK_STRINGMAP_SENTINEL
1110 };
1111 static const sk_stringmap_entry_t time_format_zones[] = {
1112 {"utc", SKTIMESTAMP_UTC, "use UTC", NULL},
1113 {"local", SKTIMESTAMP_LOCAL,
1114 "use TZ environment variable or local timezone", NULL},
1115 SK_STRINGMAP_SENTINEL
1116 };
1117 static const sk_stringmap_entry_t time_format_misc[] = {
1118 {"no-msec", SKTIMESTAMP_NOMSEC, "truncate milliseconds", NULL},
1119 SK_STRINGMAP_SENTINEL
1120 };
1121
1122 /*
1123 * status = time_format_option_parse(format_string, out_flags, from_environ);
1124 *
1125 * Parse the timestamp-format value contained in 'format_string'
1126 * and set 'out_flags' to the result of parsing the string. Return
1127 * 0 on success, or -1 if parsing of the value fails.
1128 *
1129 * If 'from_environ' is true, assume 'format' was set from an
1130 * environment variable.
1131 */
1132 static int
time_format_option_parse(const char * format,uint32_t * out_flags,const char * option_name)1133 time_format_option_parse(
1134 const char *format,
1135 uint32_t *out_flags,
1136 const char *option_name)
1137 {
1138 char buf[256];
1139 char *errmsg;
1140 sk_stringmap_t *str_map = NULL;
1141 sk_stringmap_iter_t *iter = NULL;
1142 sk_stringmap_entry_t *found_entry;
1143 const sk_stringmap_entry_t *entry;
1144 int name_seen = 0;
1145 int zone_seen = 0;
1146 int from_environ = 0;
1147 int rv = -1;
1148
1149 if (option_name && 0 == strcmp(option_name, SK_TIMESTAMP_FORMAT_ENVAR)) {
1150 from_environ = 1;
1151 }
1152
1153 /* create a stringmap of the available timestamp formats */
1154 if (SKSTRINGMAP_OK != skStringMapCreate(&str_map)) {
1155 skAppPrintOutOfMemory(NULL);
1156 goto END;
1157 }
1158 if (skStringMapAddEntries(str_map, -1, time_format_names)
1159 != SKSTRINGMAP_OK)
1160 {
1161 skAppPrintOutOfMemory(NULL);
1162 goto END;
1163 }
1164 if (skStringMapAddEntries(str_map, -1, time_format_zones)
1165 != SKSTRINGMAP_OK)
1166 {
1167 skAppPrintOutOfMemory(NULL);
1168 goto END;
1169 }
1170 if (from_environ
1171 || 0 == (time_format_flags & (SK_OPTION_TIMESTAMP_NEVER_MSEC
1172 | SK_OPTION_TIMESTAMP_ALWAYS_MSEC)))
1173 {
1174 if (skStringMapAddEntries(str_map, -1, time_format_misc)
1175 != SKSTRINGMAP_OK)
1176 {
1177 skAppPrintOutOfMemory(NULL);
1178 goto END;
1179 }
1180 }
1181
1182 /* attempt to match */
1183 if (skStringMapParse(str_map, format, SKSTRINGMAP_DUPES_ERROR,
1184 &iter, &errmsg))
1185 {
1186 skAppPrintErr("Invalid %s: %s", option_name, errmsg);
1187 goto END;
1188 }
1189
1190 *out_flags = 0;
1191 if (time_format_flags & SK_OPTION_TIMESTAMP_NEVER_MSEC) {
1192 *out_flags |= SKTIMESTAMP_NOMSEC;
1193 }
1194
1195 while (skStringMapIterNext(iter, &found_entry, NULL) == SK_ITERATOR_OK) {
1196 *out_flags |= found_entry->id;
1197 switch (found_entry->id) {
1198 case SKTIMESTAMP_NOMSEC:
1199 if (time_format_flags & SK_OPTION_TIMESTAMP_ALWAYS_MSEC) {
1200 /* this should only occur when 'from_environ' is true;
1201 * disable the no-msec setting. */
1202 assert(from_environ);
1203 *out_flags = *out_flags & ~SKTIMESTAMP_NOMSEC;
1204 }
1205 break;
1206
1207 case 0:
1208 case SKTIMESTAMP_EPOCH:
1209 case SKTIMESTAMP_ISO:
1210 case SKTIMESTAMP_MMDDYYYY:
1211 if (name_seen) {
1212 entry = time_format_names;
1213 strncpy(buf, entry->name, sizeof(buf));
1214 for (++entry; entry->name; ++entry) {
1215 strncat(buf, ",", sizeof(buf)-strlen(buf)-1);
1216 strncat(buf, entry->name, sizeof(buf)-strlen(buf)-1);
1217 }
1218 skAppPrintErr("Invalid %s: May only specify one of %s",
1219 option_name, buf);
1220 goto END;
1221 }
1222 name_seen = 1;
1223 break;
1224
1225 case SKTIMESTAMP_UTC:
1226 case SKTIMESTAMP_LOCAL:
1227 if (zone_seen) {
1228 entry = time_format_zones;
1229 strncpy(buf, entry->name, sizeof(buf));
1230 for (++entry; entry->name; ++entry) {
1231 strncat(buf, ",", sizeof(buf)-strlen(buf)-1);
1232 strncat(buf, entry->name, sizeof(buf)-strlen(buf)-1);
1233 }
1234 skAppPrintErr("Invalid %s: May only specify one of %s",
1235 option_name, buf);
1236 goto END;
1237 }
1238 zone_seen = 1;
1239 break;
1240
1241 default:
1242 skAbortBadCase(found_entry->id);
1243 }
1244 }
1245
1246 rv = 0;
1247
1248 END:
1249 if (str_map) {
1250 skStringMapDestroy(str_map);
1251 }
1252 if (iter) {
1253 skStringMapIterDestroy(iter);
1254 }
1255 return rv;
1256 }
1257
1258 static int
time_format_option_handler(clientData cData,int opt_index,char * opt_arg)1259 time_format_option_handler(
1260 clientData cData,
1261 int opt_index,
1262 char *opt_arg)
1263 {
1264 uint32_t *var_location = (uint32_t*)cData;
1265
1266 switch ((enum time_format_option_en)opt_index) {
1267 case OPT_VAL_TIMESTAMP_FORMAT:
1268 if (time_format_option_parse(
1269 opt_arg, var_location, time_format_option[opt_index].name))
1270 {
1271 return 1;
1272 }
1273 break;
1274
1275 case OPT_VAL_EPOCH_TIME:
1276 if (time_format_option_parse(
1277 "epoch", var_location, time_format_option[opt_index].name))
1278 {
1279 skAbort();
1280 }
1281 break;
1282
1283 case OPT_VAL_LEGACY_TIMESTAMPS:
1284 if ((opt_arg == NULL) || (opt_arg[0] == '\0') || (opt_arg[0] == '1')) {
1285 if (time_format_flags & (SK_OPTION_TIMESTAMP_NEVER_MSEC
1286 | SK_OPTION_TIMESTAMP_ALWAYS_MSEC))
1287 {
1288 if (time_format_option_parse(
1289 "m/d/y", var_location,
1290 time_format_option[opt_index].name))
1291 {
1292 skAbort();
1293 }
1294 } else {
1295 if (time_format_option_parse(
1296 "m/d/y,no-msec", var_location,
1297 time_format_option[opt_index].name))
1298 {
1299 skAbort();
1300 }
1301 }
1302 } else if (time_format_option_parse(
1303 time_format_names[0].name, var_location,
1304 time_format_option[opt_index].name))
1305 {
1306 skAbort();
1307 }
1308 break;
1309 }
1310 return 0;
1311 }
1312
1313 int
skOptionsTimestampFormatRegister(uint32_t * var_location,uint32_t flags,...)1314 skOptionsTimestampFormatRegister(
1315 uint32_t *var_location,
1316 uint32_t flags,
1317 ...)
1318 {
1319 struct option opts[4];
1320 const struct option *tfo;
1321 const char *env;
1322 uint32_t tmp_val = 0;
1323 unsigned int num_opts;
1324 va_list arg;
1325
1326 assert(sizeof(opts) >= sizeof(time_format_option));
1327
1328 va_start(arg, flags);
1329 if (var_location == NULL) {
1330 va_end(arg);
1331 return -1;
1332 }
1333
1334 time_format_flags = flags;
1335 if (time_format_flags & SK_OPTION_TIMESTAMP_NEVER_MSEC) {
1336 *var_location |= SKTIMESTAMP_NOMSEC;
1337 }
1338
1339 env = getenv(SK_TIMESTAMP_FORMAT_ENVAR);
1340 if (env && env[0]) {
1341 if (time_format_option_parse(env, &tmp_val, SK_TIMESTAMP_FORMAT_ENVAR)
1342 == 0)
1343 {
1344 *var_location = tmp_val;
1345 }
1346 }
1347
1348 /* copy --timestamp-format */
1349 memset(opts, 0, sizeof(opts));
1350 num_opts = 0;
1351
1352 for (tfo = time_format_option; tfo->name; ++tfo) {
1353 assert(num_opts < sizeof(opts)/sizeof(opts[0]));
1354 switch ((enum time_format_option_en)tfo->val) {
1355 case OPT_VAL_TIMESTAMP_FORMAT:
1356 memcpy(&opts[num_opts], tfo, sizeof(opts[0]));
1357 ++num_opts;
1358 break;
1359
1360 case OPT_VAL_LEGACY_TIMESTAMPS:
1361 if (time_format_flags & SK_OPTION_TIMESTAMP_OPTION_LEGACY){
1362 memcpy(&opts[num_opts], tfo, sizeof(opts[0]));
1363 ++num_opts;
1364 }
1365 break;
1366
1367 case OPT_VAL_EPOCH_TIME:
1368 if (time_format_flags & SK_OPTION_TIMESTAMP_OPTION_EPOCH_NAME) {
1369 snprintf(time_format_epoch_name,sizeof(time_format_epoch_name),
1370 "%s", va_arg(arg, char *));
1371 memcpy(&opts[num_opts], tfo, sizeof(opts[0]));
1372 opts[num_opts].name = time_format_epoch_name;
1373 ++num_opts;
1374 } else if (time_format_flags & SK_OPTION_TIMESTAMP_OPTION_EPOCH) {
1375 memcpy(&opts[num_opts], tfo, sizeof(opts[0]));
1376 ++num_opts;
1377 }
1378 break;
1379 }
1380 }
1381
1382 va_end(arg);
1383
1384 return skOptionsRegister(opts, time_format_option_handler,
1385 (clientData)var_location);
1386 }
1387
1388
1389 void
skOptionsTimestampFormatUsage(FILE * fh)1390 skOptionsTimestampFormatUsage(
1391 FILE *fh)
1392 {
1393 const sk_stringmap_entry_t *e;
1394 const char *label;
1395 const char *sss;
1396 const struct option *tfo;
1397
1398 /* whether to include milliseconds in timestamp help */
1399 if (time_format_flags & SK_OPTION_TIMESTAMP_NEVER_MSEC) {
1400 sss = "";
1401 } else {
1402 sss = ".sss";
1403 }
1404
1405 for (tfo = time_format_option; tfo->name; ++tfo) {
1406 switch ((enum time_format_option_en)tfo->val) {
1407 case OPT_VAL_TIMESTAMP_FORMAT:
1408 fprintf(
1409 fh,
1410 ("--%s %s. Print each timestamp in this format and timezone.\n"
1411 "\tDef. $" SK_TIMESTAMP_FORMAT_ENVAR " or %s,%s. Choices:\n"),
1412 tfo->name, SK_OPTION_HAS_ARG(*tfo),
1413 time_format_names[0].name,
1414 time_format_zones[(SK_ENABLE_LOCALTIME != 0)].name);
1415 label = "Format:";
1416 for (e = time_format_names; e->name; ++e) {
1417 if (SKTIMESTAMP_EPOCH == e->id) {
1418 sss = "";
1419 }
1420 fprintf(fh, "\t%-10s%-8s - %s%s\n",
1421 label, e->name, e->description, sss);
1422 label = "";
1423 }
1424 label = "Timezone:";
1425 for (e = time_format_zones; e->name; ++e) {
1426 fprintf(fh, "\t%-10s%-8s - %s\n",
1427 label, e->name, e->description);
1428 label = "";
1429 }
1430 if (0 == (time_format_flags & (SK_OPTION_TIMESTAMP_NEVER_MSEC
1431 | SK_OPTION_TIMESTAMP_ALWAYS_MSEC)))
1432 {
1433 label = "Misc:";
1434 for (e = time_format_misc; e->name; ++e) {
1435 fprintf(fh, "\t%-10s%-8s - %s\n",
1436 label, e->name, e->description);
1437 label = "";
1438 }
1439 }
1440 break;
1441
1442 case OPT_VAL_EPOCH_TIME:
1443 if (time_format_flags & SK_OPTION_TIMESTAMP_OPTION_EPOCH_NAME) {
1444 fprintf(fh, ("--%s %s. DEPRECATED."
1445 " Equivalent to --%s=epoch\n"),
1446 time_format_epoch_name, SK_OPTION_HAS_ARG(*tfo),
1447 time_format_option[OPT_VAL_TIMESTAMP_FORMAT].name);
1448 } else if (time_format_flags & SK_OPTION_TIMESTAMP_OPTION_EPOCH) {
1449 fprintf(fh, ("--%s %s. DEPRECATED."
1450 " Equivalent to --%s=epoch\n"),
1451 tfo->name, SK_OPTION_HAS_ARG(*tfo),
1452 time_format_option[OPT_VAL_TIMESTAMP_FORMAT].name);
1453 }
1454 break;
1455
1456 case OPT_VAL_LEGACY_TIMESTAMPS:
1457 if (time_format_flags & SK_OPTION_TIMESTAMP_OPTION_LEGACY) {
1458 fprintf(
1459 fh, "--%s %s. DEPRECATED. Equivalent to --%s=m/d/y%s\n",
1460 tfo->name, SK_OPTION_HAS_ARG(*tfo),
1461 time_format_option[OPT_VAL_TIMESTAMP_FORMAT].name,
1462 ((time_format_flags & (SK_OPTION_TIMESTAMP_NEVER_MSEC
1463 | SK_OPTION_TIMESTAMP_ALWAYS_MSEC))
1464 ? ""
1465 : ",no-msec"));
1466 }
1467 break;
1468 }
1469 }
1470 }
1471
1472
1473 #if SK_SUPPORT_CONF_FILE
1474 /*
1475 * readline:
1476 * Read a line (including newline) from a file. Will also read a
1477 * last line (terminated by EOF) properly.
1478 * SideEffects:
1479 * Moves the read position of file to the next line.
1480 * Return:
1481 * A newly allocated string containing the next line. NULL at
1482 * EOF, or if there is a problem.
1483 */
1484 static char *
readline(FILE * file)1485 readline(
1486 FILE *file)
1487 {
1488 static int gapsize = 64;
1489 char *line;
1490 int blocksize = 1;
1491 int writepoint = 0;
1492 char *retval = NULL;
1493
1494 if (file == NULL) {
1495 return NULL;
1496 }
1497
1498 /* Initial allocation for line */
1499 line = (char *)malloc(sizeof(char) * gapsize);
1500 if (line == NULL) {
1501 return NULL;
1502 }
1503
1504 for (;;) {
1505 /* How many chars are left? */
1506 size_t empty = gapsize * blocksize - writepoint;
1507 char *wp = &line[writepoint];
1508
1509 /* Get chars */
1510 if (fgets(wp, empty, file) == NULL) {
1511 if (writepoint != 0) {
1512 /* End of file */
1513 retval = strdup(line);
1514 }
1515 goto end;
1516 }
1517
1518 /* If we haven't reached the end of the line, realloc. */
1519 if ((strlen(wp) == empty - 1) &&
1520 (wp[empty - 2] != '\n'))
1521 {
1522 char *tmpline;
1523 writepoint = gapsize * blocksize - 1;
1524 tmpline = realloc(line, sizeof(char) * (gapsize * (++blocksize)));
1525 if (tmpline) {
1526 line = tmpline;
1527 } else {
1528 goto end;
1529 }
1530 } else {
1531 /* We've reached the end of the line. */
1532 break;
1533 }
1534 }
1535
1536 /* Allocate only enough space for the line. */
1537 retval = strdup(line);
1538
1539 end:
1540 /* Cleanup */
1541 free(line);
1542
1543 return retval;
1544 }
1545
1546
1547 /*
1548 * optionsHandleConfFile:
1549 *
1550 * Loads a configuration file. The configuration file consists of
1551 * a series of newline-terminated lines. A line consisting of
1552 * only whitespace, or whose first non-whitespace character is a
1553 * `#' character is ignored. All other lines should consist of a
1554 * single option name followed by the option's value (if any),
1555 * separated by whitespace. Whitespace at the beginning and end
1556 * of the line is ignored.
1557 *
1558 * BUGS:
1559 * If you intersperse switches (options) and arguments, arguments
1560 * before the configuration file is parsed will not be seen.
1561 *
1562 * Return:
1563 * 0 if ok. -1 else
1564 */
1565 int
optionsHandleConfFile(char * filename)1566 optionsHandleConfFile(
1567 char *filename)
1568 {
1569 static int gapsize = 10;
1570 int num_lines = 0;
1571 int num_alloc = 0;
1572 char **lines = NULL;
1573 char *line = NULL;
1574 FILE *file;
1575 int retval = -1;
1576 int i;
1577 char **argv = NULL;
1578 int argc = 0;
1579 int saved_optind;
1580
1581 if (filename == NULL) {
1582 skAppPrintErr("NULL configuration filename");
1583 return -1;
1584 }
1585
1586 /* Open the file */
1587 file = fopen(filename, "r");
1588 if (file == NULL) {
1589 skAppPrintErr("Could not open \"%s\" for reading.", filename);
1590 return -1;
1591 }
1592
1593 /* Alloc the line buffer */
1594 num_alloc = gapsize;
1595 lines = (char **)malloc(sizeof(char *) * num_alloc);
1596 if (lines == NULL) {
1597 skAppPrintErr("Memory allocation error.");
1598 goto end;
1599 }
1600
1601 /* Read in the lines */
1602 while ((line = readline(file))) {
1603 char *newline;
1604 size_t len;
1605 char *c;
1606
1607 /* Strip it */
1608 len = skStrip(line);
1609
1610 /* Elide commented or empty lines. */
1611 if (line[0] == '\0' || line[0] == '#') {
1612 free(line);
1613 continue;
1614 }
1615
1616 /* Allocate space for the line, plus two characters. */
1617 c = newline = (char *)malloc(sizeof(char) * (len + 3));
1618
1619 /* Copy the line, prepending hyphens */
1620 *c++ = '-';
1621 *c++ = '-';
1622 strncpy(c, line, (len+1));
1623 free(line);
1624 lines[num_lines++] = newline;
1625
1626 /* Allocate more space, if necessary */
1627 if (num_lines > num_alloc) {
1628 char **tmp;
1629
1630 num_alloc += gapsize;
1631 tmp = realloc(lines, sizeof(char *) * num_alloc);
1632 if (tmp == NULL) {
1633 goto end;
1634 }
1635 lines = tmp;
1636 }
1637 }
1638
1639 /* Allocate space for argv-style pointer */
1640 argv = (char **)malloc(sizeof(char *) * num_lines * 2 + 1);
1641 if (argv == NULL) {
1642 goto end;
1643 }
1644 /* First operand is program name, ignored */
1645 argv[argc++] = "";
1646
1647 /* Parse the lines. */
1648 for (i = 0; i < num_lines; i++) {
1649 /* Set the next argument to the beginning of the line */
1650 char *c = argv[argc++] = lines[i];
1651
1652 /* Find a space */
1653 while (*c && !isspace((int)*c)) {
1654 c++;
1655 }
1656 if (*c) {
1657 /* If we found a space, end the first arg, and find the
1658 option value. */
1659 *c++ = '\0';
1660 while (isspace((int)*c)) { /* Don't need to check for 0
1661 due to strip */
1662 c++;
1663 }
1664 /* Set the next argument to the option value. */
1665 argv[argc++] = c;
1666 }
1667 }
1668
1669 saved_optind = optind;
1670 #ifdef SK_USE_OPTRESET
1671 optreset = 1;
1672 #endif
1673 #ifdef SK_HAVE_GETOPT_LONG_ONLY
1674 optind = 1;
1675 #else
1676 optind = 0;
1677 #endif
1678 /* Parse the options */
1679 if (skOptionsParse(argc, argv) != -1) {
1680 retval = 0;
1681 }
1682 optind = saved_optind;
1683 #ifdef SK_USE_OPTRESET
1684 optreset = 1;
1685 #endif
1686
1687 end:
1688 /* Cleanup */
1689 if (file) {
1690 fclose(file);
1691 }
1692 if (argv) {
1693 free(argv);
1694 }
1695 if (lines) {
1696 for (i = 0; i < num_lines; i++) {
1697 free(lines[i]);
1698 }
1699 free(lines);
1700 }
1701 return retval;
1702 }
1703 #endif /* SK_SUPPORT_CONF_FILE */
1704
1705
1706 /*
1707 ** Local Variables:
1708 ** mode:c
1709 ** indent-tabs-mode:nil
1710 ** c-basic-offset:4
1711 ** End:
1712 */
1713