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