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 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 * 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 * 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 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 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 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 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 * 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 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 * 284 opts_cmdargs(struct opts *opts) 285 { 286 return (opts->op_cmdargs); 287 } 288 289 static void 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 * 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 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 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 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 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 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 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 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 529 main(int argc, char *argv[]) 530 { 531 struct opts *opts = NULL; 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