1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21
22 /*
23 * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26 /*
27 * logadm/opts.c -- options handling routines
28 */
29
30 #include <stdio.h>
31 #include <libintl.h>
32 #include <stdlib.h>
33 #include <ctype.h>
34 #include <strings.h>
35 #include <time.h>
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <errno.h>
39 #include "err.h"
40 #include "lut.h"
41 #include "fn.h"
42 #include "opts.h"
43
44 /* forward declarations for private functions */
45 static struct optinfo *opt_info(int c);
46 static void opts_setcmdarg(struct opts *opts, const char *cmdarg);
47
48 /* info created by opts_parse(), private to this module */
49 struct opts {
50 struct lut *op_raw; /* the raw text for the options */
51 struct lut *op_ints; /* the int values for the options */
52 struct fn_list *op_cmdargs; /* the op_cmdargs */
53 };
54
55 static off_t opts_parse_ctime(const char *o, const char *optarg);
56 static off_t opts_parse_bytes(const char *o, const char *optarg);
57 static off_t opts_parse_atopi(const char *o, const char *optarg);
58 static off_t opts_parse_seconds(const char *o, const char *optarg);
59
60 static struct lut *Info; /* table driving parsing */
61
62 /* table that drives argument parsing */
63 struct optinfo Opttable[] = {
64 { "e", OPTTYPE_STRING, NULL, OPTF_CLI|OPTF_CONF },
65 { "F", OPTTYPE_STRING, NULL, OPTF_CLI },
66 { "f", OPTTYPE_STRING, NULL, OPTF_CLI },
67 { "h", OPTTYPE_BOOLEAN, NULL, OPTF_CLI },
68 { "l", OPTTYPE_BOOLEAN, NULL, OPTF_CLI|OPTF_CONF },
69 { "N", OPTTYPE_BOOLEAN, NULL, OPTF_CLI|OPTF_CONF },
70 { "n", OPTTYPE_BOOLEAN, NULL, OPTF_CLI },
71 { "r", OPTTYPE_BOOLEAN, NULL, OPTF_CLI },
72 { "V", OPTTYPE_BOOLEAN, NULL, OPTF_CLI },
73 { "v", OPTTYPE_BOOLEAN, NULL, OPTF_CLI },
74 { "w", OPTTYPE_STRING, NULL, OPTF_CLI },
75 { "p", OPTTYPE_INT, opts_parse_seconds, OPTF_CLI|OPTF_CONF },
76 { "P", OPTTYPE_INT, opts_parse_ctime, OPTF_CLI|OPTF_CONF },
77 { "s", OPTTYPE_INT, opts_parse_bytes, OPTF_CLI|OPTF_CONF },
78 { "a", OPTTYPE_STRING, NULL, OPTF_CLI|OPTF_CONF },
79 { "b", OPTTYPE_STRING, NULL, OPTF_CLI|OPTF_CONF },
80 { "c", OPTTYPE_BOOLEAN, NULL, OPTF_CLI|OPTF_CONF },
81 { "g", OPTTYPE_STRING, NULL, OPTF_CLI|OPTF_CONF },
82 { "m", OPTTYPE_INT, opts_parse_atopi, OPTF_CLI|OPTF_CONF },
83 { "M", OPTTYPE_STRING, NULL, OPTF_CLI|OPTF_CONF },
84 { "o", OPTTYPE_STRING, NULL, OPTF_CLI|OPTF_CONF },
85 { "R", OPTTYPE_STRING, NULL, OPTF_CLI|OPTF_CONF },
86 { "t", OPTTYPE_STRING, NULL, OPTF_CLI|OPTF_CONF },
87 { "z", OPTTYPE_INT, opts_parse_atopi, OPTF_CLI|OPTF_CONF },
88 { "A", OPTTYPE_INT, opts_parse_seconds, OPTF_CLI|OPTF_CONF },
89 { "C", OPTTYPE_INT, opts_parse_atopi, OPTF_CLI|OPTF_CONF },
90 { "E", OPTTYPE_STRING, NULL, OPTF_CLI|OPTF_CONF },
91 { "S", OPTTYPE_INT, opts_parse_bytes, OPTF_CLI|OPTF_CONF },
92 { "T", OPTTYPE_STRING, NULL, OPTF_CLI|OPTF_CONF },
93 };
94
95 int Opttable_cnt = sizeof (Opttable) / sizeof (struct optinfo);
96
97 /*
98 * opts_init -- set current options parsing table
99 */
100 void
opts_init(struct optinfo * table,int numentries)101 opts_init(struct optinfo *table, int numentries)
102 {
103 while (numentries-- > 0) {
104 Info = lut_add(Info, table->oi_o, table);
105 table++;
106 }
107 }
108
109 /*
110 * opt_info -- fetch the optinfo struct for the given option
111 */
112 static struct optinfo *
opt_info(int c)113 opt_info(int c)
114 {
115 char lhs[2];
116 lhs[0] = c;
117 lhs[1] = '\0';
118 return ((struct optinfo *)lut_lookup(Info, lhs));
119 }
120
121 /*
122 * opts_parse -- parse an argv-style list of options
123 *
124 * prints a message to stderr and calls err(EF_FILE|EF_JMP, ...) on error
125 */
126 struct opts *
opts_parse(struct opts * opts,char ** argv,int flags)127 opts_parse(struct opts *opts, char **argv, int flags)
128 {
129 int dashdash = 0;
130 char *ptr;
131
132 if (opts == NULL) {
133 opts = MALLOC(sizeof (*opts));
134 opts->op_raw = opts->op_ints = NULL;
135 opts->op_cmdargs = fn_list_new(NULL);
136 }
137
138 /* no words to process, just return empty opts struct */
139 if (argv == NULL)
140 return (opts);
141
142 /* foreach word... */
143 for (; (ptr = *argv) != NULL; argv++) {
144 if (dashdash || *ptr != '-') {
145 /* found a cmdarg */
146 opts_setcmdarg(opts, ptr);
147 continue;
148 }
149 if (*++ptr == '\0')
150 err(EF_FILE|EF_JMP, "Illegal option: dash by itself");
151 if (*ptr == '-') {
152 /* (here's where support for --longname would go) */
153 if (*(ptr + 1) != '\0')
154 err(EF_FILE|EF_JMP, "Illegal option: -%s", ptr);
155 dashdash++;
156 continue;
157 }
158 for (; *ptr; ptr++) {
159 struct optinfo *info = opt_info(*ptr);
160
161 /* see if option was in our parsing table */
162 if (info == NULL)
163 err(EF_FILE|EF_JMP, "Illegal option: %c", *ptr);
164
165 /* see if context allows this option */
166 if ((flags & OPTF_CLI) &&
167 (info->oi_flags & OPTF_CLI) == 0)
168 err(EF_FILE|EF_JMP,
169 "Option '%c' not allowed on "
170 "command line", *ptr);
171
172 if ((flags & OPTF_CONF) &&
173 (info->oi_flags & OPTF_CONF) == 0)
174 err(EF_FILE|EF_JMP,
175 "Option '%c' not allowed in "
176 "configuration file", *ptr);
177
178 /* for boolean options, we have all the info we need */
179 if (info->oi_t == OPTTYPE_BOOLEAN) {
180 (void) opts_set(opts, info->oi_o, "");
181 continue;
182 }
183
184 /* option expects argument */
185 if (*++ptr == '\0' &&
186 ((ptr = *++argv) == NULL || *ptr == '-'))
187 err(EF_FILE|EF_JMP,
188 "Option '%c' requires an argument",
189 info->oi_o[0]);
190 opts_set(opts, info->oi_o, ptr);
191 break;
192 }
193 }
194
195 return (opts);
196 }
197
198 /*
199 * opts_free -- free a struct opts previously allocated by opts_parse()
200 */
201 void
opts_free(struct opts * opts)202 opts_free(struct opts *opts)
203 {
204 if (opts) {
205 lut_free(opts->op_raw, NULL);
206 lut_free(opts->op_ints, NULL);
207 fn_list_free(opts->op_cmdargs);
208 FREE(opts);
209 }
210 }
211
212 /*
213 * opts_set -- set an option
214 */
215 void
opts_set(struct opts * opts,const char * o,const char * optarg)216 opts_set(struct opts *opts, const char *o, const char *optarg)
217 {
218 off_t *rval;
219 struct optinfo *info = opt_info(*o);
220
221 rval = MALLOC(sizeof (off_t));
222 opts->op_raw = lut_add(opts->op_raw, o, (void *)optarg);
223
224 if (info->oi_parser) {
225 *rval = (*info->oi_parser)(o, optarg);
226 opts->op_ints = lut_add(opts->op_ints, o, (void *)rval);
227 }
228 }
229
230 /*
231 * opts_setcmdarg -- add a cmdarg to the list of op_cmdargs
232 */
233 static void
opts_setcmdarg(struct opts * opts,const char * cmdarg)234 opts_setcmdarg(struct opts *opts, const char *cmdarg)
235 {
236 fn_list_adds(opts->op_cmdargs, cmdarg);
237 }
238
239 /*
240 * opts_count -- return count of the options in *options that are set
241 */
242 int
opts_count(struct opts * opts,const char * options)243 opts_count(struct opts *opts, const char *options)
244 {
245 int count = 0;
246
247 for (; *options; options++) {
248 char lhs[2];
249 lhs[0] = *options;
250 lhs[1] = '\0';
251 if (lut_lookup(opts->op_raw, lhs))
252 count++;
253 }
254 return (count);
255 }
256
257 /*
258 * opts_optarg -- return the optarg for the given option, NULL if not set
259 */
260 const char *
opts_optarg(struct opts * opts,const char * o)261 opts_optarg(struct opts *opts, const char *o)
262 {
263 return ((char *)lut_lookup(opts->op_raw, o));
264 }
265
266 /*
267 * opts_optarg_int -- return the int value for the given option
268 */
269 off_t
opts_optarg_int(struct opts * opts,const char * o)270 opts_optarg_int(struct opts *opts, const char *o)
271 {
272 off_t *ret;
273
274 ret = (off_t *)lut_lookup(opts->op_ints, o);
275 if (ret != NULL)
276 return (*ret);
277 return (0);
278 }
279
280 /*
281 * opts_cmdargs -- return list of op_cmdargs
282 */
283 struct fn_list *
opts_cmdargs(struct opts * opts)284 opts_cmdargs(struct opts *opts)
285 {
286 return (opts->op_cmdargs);
287 }
288
289 static void
merger(const char * lhs,void * rhs,void * arg)290 merger(const char *lhs, void *rhs, void *arg)
291 {
292 struct lut **destlutp = (struct lut **)arg;
293
294 *destlutp = lut_add(*destlutp, lhs, rhs);
295 }
296
297 /*
298 * opts_merge -- merge two option lists together
299 */
300 struct opts *
opts_merge(struct opts * back,struct opts * front)301 opts_merge(struct opts *back, struct opts *front)
302 {
303 struct opts *ret = MALLOC(sizeof (struct opts));
304
305 ret->op_raw = lut_dup(back->op_raw);
306 lut_walk(front->op_raw, merger, &(ret->op_raw));
307
308 ret->op_ints = lut_dup(back->op_ints);
309 lut_walk(front->op_ints, merger, &(ret->op_ints));
310
311 ret->op_cmdargs = fn_list_dup(back->op_cmdargs);
312
313 return (ret);
314 }
315
316 /*
317 * opts_parse_ctime -- parse a ctime format optarg
318 */
319 static off_t
opts_parse_ctime(const char * o,const char * optarg)320 opts_parse_ctime(const char *o, const char *optarg)
321 {
322 struct tm tm;
323 off_t ret;
324
325 if (strptime(optarg, "%a %b %e %T %Z %Y", &tm) == NULL &&
326 strptime(optarg, "%c", &tm) == NULL)
327 err(EF_FILE|EF_JMP,
328 "Option '%c' requires ctime-style time", *o);
329 errno = 0;
330 if ((ret = (off_t)mktime(&tm)) == -1 && errno)
331 err(EF_FILE|EF_SYS|EF_JMP, "Option '%c' Illegal time", *o);
332
333 return (ret);
334 }
335
336 /*
337 * opts_parse_atopi -- parse a positive integer format optarg
338 */
339 static off_t
opts_parse_atopi(const char * o,const char * optarg)340 opts_parse_atopi(const char *o, const char *optarg)
341 {
342 off_t ret = atoll(optarg);
343
344 while (isdigit(*optarg))
345 optarg++;
346
347 if (*optarg)
348 err(EF_FILE|EF_JMP,
349 "Option '%c' requires non-negative number", *o);
350
351 return (ret);
352 }
353
354 /*
355 * opts_parse_atopi -- parse a size format optarg into bytes
356 */
357 static off_t
opts_parse_bytes(const char * o,const char * optarg)358 opts_parse_bytes(const char *o, const char *optarg)
359 {
360 off_t ret = atoll(optarg);
361 while (isdigit(*optarg))
362 optarg++;
363
364 switch (*optarg) {
365 case 'g':
366 case 'G':
367 ret *= 1024;
368 /*FALLTHROUGH*/
369 case 'm':
370 case 'M':
371 ret *= 1024;
372 /*FALLTHROUGH*/
373 case 'k':
374 case 'K':
375 ret *= 1024;
376 /*FALLTHROUGH*/
377 case 'b':
378 case 'B':
379 if (optarg[1] == '\0')
380 return (ret);
381 }
382
383 err(EF_FILE|EF_JMP,
384 "Option '%c' requires number with suffix from [bkmg]", *o);
385 /*NOTREACHED*/
386 return (0);
387 }
388
389 /*
390 * opts_parse_seconds -- parse a time format optarg into seconds
391 */
392 static off_t
opts_parse_seconds(const char * o,const char * optarg)393 opts_parse_seconds(const char *o, const char *optarg)
394 {
395 off_t ret;
396
397 if (strcasecmp(optarg, "now") == 0)
398 return (OPTP_NOW);
399
400 if (strcasecmp(optarg, "never") == 0)
401 return (OPTP_NEVER);
402
403 ret = atoll(optarg);
404 while (isdigit(*optarg))
405 optarg++;
406
407 if (optarg[1] == '\0')
408 switch (*optarg) {
409 case 'h':
410 case 'H':
411 ret *= 60 * 60;
412 return (ret);
413 case 'd':
414 case 'D':
415 ret *= 60 * 60 * 24;
416 return (ret);
417 case 'w':
418 case 'W':
419 ret *= 60 * 60 * 24 * 7;
420 return (ret);
421 case 'm':
422 case 'M':
423 ret *= 60 * 60 * 24 * 30;
424 return (ret);
425 case 'y':
426 case 'Y':
427 ret *= 60 * 60 * 24 * 365;
428 return (ret);
429 }
430
431 err(EF_FILE|EF_JMP,
432 "Option '%c' requires number with suffix from [hdwmy]", *o);
433 /*NOTREACHED*/
434 return (0);
435 }
436
437 /* info passed between opts_print() and printer() */
438 struct printerinfo {
439 FILE *stream;
440 int isswitch;
441 char *exclude;
442 };
443
444 /* helper function for opts_print() */
445 static void
printer(const char * lhs,void * rhs,void * arg)446 printer(const char *lhs, void *rhs, void *arg)
447 {
448 struct printerinfo *pip = (struct printerinfo *)arg;
449 char *s = (char *)rhs;
450
451 if (pip->isswitch) {
452 char *ep = pip->exclude;
453 while (ep && *ep)
454 if (*ep++ == *lhs)
455 return;
456 }
457
458 (void) fprintf(pip->stream, " %s%s", (pip->isswitch) ? "-" : "", lhs);
459 if (s && *s) {
460 (void) fprintf(pip->stream, " ");
461 opts_printword(s, pip->stream);
462 }
463 }
464
465 /*
466 * opts_printword -- print a word, quoting as necessary
467 */
468 void
opts_printword(const char * word,FILE * stream)469 opts_printword(const char *word, FILE *stream)
470 {
471 char *q = "";
472
473 if (word != NULL) {
474 if (strchr(word, ' ') || strchr(word, '\t') ||
475 strchr(word, '$') || strchr(word, '[') ||
476 strchr(word, '?') || strchr(word, '{') ||
477 strchr(word, '`') || strchr(word, ';')) {
478 if (strchr(word, '\'') == NULL)
479 q = "'";
480 else if (strchr(word, '"') == NULL)
481 q = "\"";
482 else
483 err(EF_FILE|EF_JMP,
484 "Can't protect quotes in <%s>", word);
485 (void) fprintf(stream, "%s%s%s", q, word, q);
486 } else
487 (void) fprintf(stream, "%s", word);
488 }
489 }
490
491 /*
492 * opts_print -- print options to stream, leaving out those in "exclude"
493 */
494 void
opts_print(struct opts * opts,FILE * stream,char * exclude)495 opts_print(struct opts *opts, FILE *stream, char *exclude)
496 {
497 struct printerinfo pi;
498 struct fn *fnp;
499
500 pi.stream = stream;
501 pi.isswitch = 1;
502 pi.exclude = exclude;
503
504 lut_walk(opts->op_raw, printer, &pi);
505
506 fn_list_rewind(opts->op_cmdargs);
507 while ((fnp = fn_list_next(opts->op_cmdargs)) != NULL) {
508 (void) fprintf(stream, " ");
509 opts_printword(fn_s(fnp), stream);
510 }
511 }
512
513 #ifdef TESTMODULE
514
515 /* table that drives argument parsing */
516 static struct optinfo Testopttable[] = {
517 { "a", OPTTYPE_BOOLEAN, NULL, OPTF_CLI },
518 { "b", OPTTYPE_STRING, NULL, OPTF_CLI },
519 { "c", OPTTYPE_INT, opts_parse_seconds, OPTF_CLI|OPTF_CONF },
520 { "d", OPTTYPE_INT, opts_parse_ctime, OPTF_CLI|OPTF_CONF },
521 { "e", OPTTYPE_INT, opts_parse_bytes, OPTF_CLI|OPTF_CONF },
522 { "f", OPTTYPE_INT, opts_parse_atopi, OPTF_CLI|OPTF_CONF },
523 };
524
525 /*
526 * test main for opts module, usage: a.out options...
527 */
528 int
main(int argc,char * argv[])529 main(int argc, char *argv[])
530 {
531 struct opts *opts;
532
533 err_init(argv[0]);
534 setbuf(stdout, NULL);
535
536 opts_init(Testopttable,
537 sizeof (Testopttable) / sizeof (struct optinfo));
538
539 argv++;
540
541 if (SETJMP)
542 err(0, "opts parsing failed");
543 else
544 opts = opts_parse(NULL, argv, OPTF_CLI);
545
546 printf("options:");
547 opts_print(opts, stdout, NULL);
548 printf("\n");
549
550 err_done(0);
551 /* NOTREACHED */
552 return (0);
553 }
554
555 #endif /* TESTMODULE */
556