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