xref: /illumos-gate/usr/src/cmd/logadm/opts.c (revision 7c478bd9)
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