Home | History | Annotate | Download | only in diskomizer
      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 #pragma ident	"@(#)args.c	1.53	09/05/26 SMI"
     23 
     24 /*
     25  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     26  * Use is subject to license terms.
     27  */
     28 
     29 /*
     30  * Based on a general purpose argument and configuration handling routines.
     31  * This is macro hell. This all works by including the file arg_vals.h
     32  * multiple times each time having defined the macros:
     33  *
     34  * SET_OPENING_BLURB(A)  A is a string that will get printed at the
     35  *	top of any output file containing the options.
     36  * START_SECTION(A, B)	Used to group options. A is the name of the
     37  *	group and B is a string that describes the section.
     38  * END_SECTION(A) Used to end a group of options. The A should match
     39  *	the A of START_SECTION above.
     40  *
     41  *
     42  * STARG(X, B, C)   to handle strings. The string "NULL" is special.
     43  *	Do not use it as one of your options.
     44  * ILIST(A, B, C) to handle comma lists with bracketed weightings.
     45  * BARG(X, B, C)	to handle single bits, boolean types.
     46  * CARG(X, B, C)	to handle chars.
     47  * SARG(X, B, C)	to handle shorts.
     48  * IARG(X, B, C)	to handle ints.
     49  * LLARG(X, B, C)	to handle long longs.
     50  * LARG(X, B, C)	to handle longs.
     51  *
     52  * the arguments to the args macros are all the same:
     53  *
     54  *	X	the name of the option.  This must be unique and will be the
     55  *		name of the element in the structure.
     56  *	B	the default value. This must be of the correct type for the
     57  *		macro.
     58  *	C	A string that describes the argument. This forms the coment
     59  *		used in the options files and when the usage messages is
     60  * 		printed.  It should not contain any special chars. Eg \n or
     61  *		\t or \r.
     62  *
     63  * After each include the macros get undefined. This way to add a configuration
     64  * option the only file you edit it arg_vals.h. What is more this args.c and
     65  * args.h can and are used by many different programs to generate many
     66  * different option structures.
     67  *
     68  * All your program has to do is call do_args(argc, argv). The program
     69  * then will take the standard four options:
     70  *
     71  *	-f option_file
     72  *		Causes options to be read from the named file.
     73  *	-o option=foo
     74  *		causes a specific option to be set.
     75  *	-h file
     76  *		Print the HTML usage into the file named, using the file '-'
     77  *		caused the HTML to be written to standard out and exit.
     78  *	-d file
     79  *		Print the current values of the options into the named file.
     80  *	-?
     81  *		Print all the options to standard out.
     82  *
     83  *	These options can be combined and are actioned in sequence.
     84  *
     85  *	prog -f foo -o bar=plap -d one -o bar=spam -d two
     86  *
     87  *	Reads all the options from the file foo, then sets the option
     88  *	bar to be plap, then prints the settings into the file "one"
     89  *	then sets bar to be spame and prints the options into the file
     90  *	"two".
     91  *
     92  * Additionally all options are stored in an option store so that
     93  * shared libraries can have options that are not known about by
     94  * diskomizer at compile time. The store is simply a sorted list.
     95  *
     96  * For reasonable numbers of options the search speed is acceptable
     97  * it is assumed that the consumers of options will cache their values
     98  * to minimize the number of calls into the option_store.
     99  *
    100  * See the comment in args.h for usage of the option store from shared
    101  * libraries.
    102  */
    103 #include "arg_inc.h"
    104 #include <stdlib.h>
    105 #include <stdio.h>
    106 #include <stdarg.h>
    107 #include <sys/types.h>
    108 #include <sys/stat.h>
    109 #include <fcntl.h>
    110 #include <sys/mman.h>
    111 #include <errno.h>
    112 #include <unistd.h>
    113 #include <limits.h>
    114 #include <diskomizer/log.h>
    115 #include <usage_tracking/usage_tracking.h>
    116 #include "disko_usage_track.h"
    117 #include "time.h"
    118 
    119 #define	COMMA ','
    120 #define	DOLLAR '$'
    121 #define	OPEN_BRACE '{'
    122 #define	CLOSE_BRACE '}'
    123 #define	OPEN_BRACKET '('
    124 #define	CLOSE_BRACKET ')'
    125 #define	LINE_LEN 75
    126 
    127 #define	DISKOMIZER_PATH "DISKOMIZER_PATH"
    128 
    129 static const char OPEN_BRACKET_STR[] = "(,";
    130 static const char CLOSE_BRACKET_STR[] = ")";
    131 
    132 #include "args.h"
    133 
    134 static const char def_diskomizer_path[] = ":/opt/SUNWdiskomizer/etc";
    135 static const char nil_str[] = "(nil)";
    136 
    137 #define	NOT_NULL_FREE(X) if (X != NULL) free(X);
    138 #define	NIL_STR(X) (X != NULL ? X : nil_str)
    139 #include <libintl.h>
    140 
    141 typedef long long (*operator_t)(long long x, long long y);
    142 static long long
    143 	(*choose_operator(char *str, char **newstr))(long long x, long long y);
    144 static long long set(long long x, long long y);
    145 /*
    146  * These structures are used to keep track of state information for the
    147  * parsing  of lines so that correct line numbers can be reported when
    148  * errors occur.
    149  */
    150 struct lineinfo {
    151 	int start;
    152 	int end;
    153 };
    154 struct option_info {
    155 	char *filename;
    156 	char *line;
    157 	uint_t no_report:1;
    158 	uint_t had_error:1;
    159 	uint_t built_in_option:1;
    160 	uint_t used:1;
    161 	const char *endchrs;
    162 	struct lineinfo lineno;
    163 };
    164 
    165 struct option_entry {
    166 	const char *key;
    167 	int len;
    168 	char *value;
    169 	long long lv;
    170 	struct option_info info;
    171 };
    172 
    173 struct option_store {
    174 	int refcount;
    175 	int nentries;
    176 	struct option_entry *entries;
    177 };
    178 struct option_store opt_store;
    179 
    180 
    181 static void print_html(const char *prog, const char *filename);
    182 static void print_defaults(FILE *f);
    183 static int print_option_file(const char *prog, const char *file, FILE *f);
    184 static void set_defaults(void);
    185 static int process_option(char *option, struct option_info *infop);
    186 static int print_file(const char *prog, const char *filename,
    187 	int (*print_func)(const char *, const char *, FILE *));
    188 
    189 static option_status_t get_long_option(const char *key, long *out);
    190 static option_status_t get_long_long_option(const char *key, long long *out);
    191 static long long do_maths(long long cur, char *str, char **nstr,
    192 		struct option_info *infop);
    193 static void print_all_option_entries(FILE *f);
    194 
    195 #define	STARG(A, B, C)
    196 #define	ILIST(A, B, C)
    197 #define	SARG(A, B, C)
    198 #define	LLARG(A, B, C)
    199 #define	LARG(A, B, C)
    200 #define	CARG(A, B, C)
    201 #define	BARG(A, B, C)
    202 #define	IARG(A, B, C)
    203 #define	START_SECTION(A, B)
    204 #define	END_SECTION(A)
    205 #define	SET_OPENING_BLURB(A) const char OPENING_STR[] = A;
    206 #include "arg_vals.h"
    207 #undef START_SECTION
    208 #undef SET_OPENING_BLURB
    209 #define	SET_OPENING_BLURB(A)
    210 struct opt_sections {
    211 #define	START_SECTION(A, B) char *A;
    212 #include "arg_vals.h"
    213 #undef START_SECTION
    214 };
    215 static struct opt_sections opts_scomments = {
    216 #define	START_SECTION(A, B) B,
    217 #include "arg_vals.h"
    218 #undef START_SECTION
    219 };
    220 static struct opt_sections opts_snames = {
    221 #define	START_SECTION(A, B) #A,
    222 #include "arg_vals.h"
    223 
    224 };
    225 #undef STARG
    226 #undef ILIST
    227 #undef SARG
    228 #undef LLARG
    229 #undef LARG
    230 #undef CARG
    231 #undef BARG
    232 #undef IARG
    233 #undef START_SECTION
    234 #undef END_SECTION
    235 #undef SET_OPENING_BLURB
    236 
    237 #define	START_SECTION(A, B)
    238 #define	END_SECTION(A)
    239 #define	SET_OPENING_BLURB(A)
    240 struct options opts;
    241 struct opts_comments {
    242 #define	SARG(B, D, C) char *B;
    243 #define	STARG SARG
    244 #define	IARG SARG
    245 #define	CARG SARG
    246 #define	BARG SARG
    247 #define	LLARG SARG
    248 #define	LARG SARG
    249 #define	ILIST SARG
    250 #include "arg_vals.h"
    251 #undef	SARG
    252 };
    253 
    254 static struct opts_comments opts_comments = {
    255 #define	SARG(B, D, C) C,
    256 #include "arg_vals.h"
    257 #undef SARG
    258 };
    259 static struct opts_comments opts_names = {
    260 #define	SARG(B, D, C) #B,
    261 #include "arg_vals.h"
    262 #undef SARG
    263 };
    264 static struct opts_comments opts_def_strings = {
    265 #define	SARG(A, B, C) #B,
    266 #undef ILIST
    267 #define	ILIST(A, B, C) B,
    268 #include "arg_vals.h"
    269 #undef STARG
    270 #undef SARG
    271 #undef LLARG
    272 #undef LARG
    273 #undef CARG
    274 #undef BARG
    275 #undef IARG
    276 #undef ILIST
    277 #undef START_SECTION
    278 #undef END_SECTION
    279 #undef SET_OPENING_BLURB
    280 };
    281 static const char min_max_str[] = "# Minimum value 0, Maximum value";
    282 static const char HASH[] = "# ";
    283 
    284 static int
    285 option_cmp(const void *a, const void *b)
    286 {
    287 	struct option_entry *as, *bs;
    288 	as = (struct option_entry *)a;
    289 	bs = (struct option_entry *)b;
    290 	if (bs->len != as->len) {
    291 		return (bs->len - as->len);
    292 	}
    293 	return (strncasecmp(as->key, bs->key, bs->len));
    294 }
    295 
    296 static int
    297 check_and_free_option_store()
    298 {
    299 	int i;
    300 	int err = 0;
    301 
    302 	for (i = 0; i < opt_store.nentries; i++) {
    303 		if (opt_store.entries[i].info.used == 0 &&
    304 		    opt_store.entries[i].info.built_in_option == 0) {
    305 			err++;
    306 			if (opt_store.entries[i].info.filename != NULL) {
    307 				fprintf(stderr,
    308 				    "Unknown option \"%.*s\" at line"
    309 				    " %d in file \"%s\"\n",
    310 				    opt_store.entries[i].len,
    311 				    opt_store.entries[i].key,
    312 				    opt_store.entries[i].info.lineno.start,
    313 				    opt_store.entries[i].info.filename);
    314 			} else {
    315 				fprintf(stderr, "Unknown option \"%.*s\" "
    316 				    "at argument %d on command line\n",
    317 				    opt_store.entries[i].len,
    318 				    opt_store.entries[i].key,
    319 				    opt_store.entries[i].info.lineno.end - 1);
    320 			}
    321 		}
    322 		free(opt_store.entries[i].value);
    323 	}
    324 	free(opt_store.entries);
    325 	opt_store.entries = NULL;
    326 	opt_store.nentries = 0;
    327 	return (err);
    328 }
    329 
    330 static int
    331 store_option_with_val(char *key, char *value, long long *x,
    332 	struct option_info *infop)
    333 {
    334 	struct option_entry e;
    335 	struct option_entry *ne;
    336 
    337 	if ((ne = realloc(opt_store.entries, (opt_store.nentries + 1) *
    338 		sizeof (struct option_entry))) == NULL) {
    339 		return (0);
    340 	}
    341 
    342 	opt_store.entries = ne;
    343 
    344 	if ((e.value = strdup(value)) == NULL)
    345 		return (0);
    346 	e.key = key;
    347 	e.len = strlen(key);
    348 	e.info = *infop;
    349 	e.info.no_report = 1;
    350 	e.info.had_error = 0;
    351 	e.info.used = 0;
    352 	e.info.endchrs = NULL;
    353 
    354 	ne = bsearch(&e, opt_store.entries, opt_store.nentries,
    355 	    sizeof (struct option_entry), option_cmp);
    356 	if (ne == NULL) {
    357 		if (x == NULL) {
    358 			e.lv = do_maths(0, e.value, NULL, &e.info);
    359 		} else {
    360 			e.lv = *x;
    361 		}
    362 		opt_store.entries[opt_store.nentries++] = e;
    363 		qsort(opt_store.entries, opt_store.nentries,
    364 		    sizeof (struct option_entry), option_cmp);
    365 	} else {
    366 		if (x == NULL) {
    367 			e.lv = ne->lv;
    368 		} else {
    369 			e.lv = *x;
    370 		}
    371 		free(ne->value);
    372 		*ne = e;
    373 		ne->lv = do_maths(e.lv, e.value, NULL, &e.info);
    374 	}
    375 
    376 	return (1);
    377 }
    378 static int
    379 store_option(char *key, char *value, struct option_info *infop)
    380 {
    381 	return (store_option_with_val(key, value, NULL, infop));
    382 }
    383 
    384 /*PRINTFLIKE2*/
    385 static void
    386 parse_error(struct option_info *infop, const char *fmt, ...)
    387 {
    388 	va_list ap;
    389 	va_start(ap, fmt);
    390 	if (!infop) {
    391 		vlog(LOG_WARNING, fmt, ap);
    392 		return;
    393 	}
    394 	if (infop->no_report) {
    395 		infop->had_error = 1;
    396 		return;
    397 	}
    398 	if (infop->filename) {
    399 		if (infop->lineno.start >= infop->lineno.end - 1) {
    400 			plog(LOG_WARNING, gettext("Parse error: at line %d "
    401 			    "of file \"%s\"\n"),
    402 			    infop->lineno.start, infop->filename);
    403 		} else {
    404 			plog(LOG_WARNING,
    405 			    gettext("Parse error: between line %d and %d "
    406 			    "of file \"%s\"\n"),
    407 			    infop->lineno.start, infop->lineno.end - 1,
    408 			    infop->filename);
    409 		}
    410 		if (infop->line) {
    411 			plog(LOG_WARNING, "Error processing option \"%s\"\n",
    412 			    infop->line);
    413 		}
    414 	} else {
    415 		plog(LOG_WARNING, "Parse error, on command line option "
    416 		    "\"%s\"\n", infop->line);
    417 	}
    418 	vlog(LOG_WARNING, fmt, ap);
    419 }
    420 void
    421 usage(const char *prog)
    422 {
    423 	const char *tmp = strrchr(prog, '/');
    424 	if (tmp == NULL)
    425 		tmp = prog;
    426 	else
    427 		tmp = tmp + 1;
    428 	(void) fprintf(stderr,
    429 	    gettext("USAGE: %s [ -f option_file ][ -o option=value]\n"), tmp);
    430 	(void) fprintf(stderr,
    431 	    gettext("For options type\n       %s -?\n"), tmp);
    432 	exit(1);
    433 }
    434 /*
    435  * check_ends.
    436  *	returns true if c is one of the endchrs
    437  */
    438 static int
    439 check_ends(char c, struct option_info *infop)
    440 {
    441 	int x = (infop != NULL && infop->endchrs != NULL &&
    442 	    strchr(infop->endchrs, c) != NULL);
    443 	return (x);
    444 }
    445 
    446 static long long
    447 my_strtoll(char *in, char **out, int base, struct option_info *infop)
    448 {
    449 	char *end;
    450 	char *unused;
    451 	long long temp;
    452 	temp = strtoll(in, &end, base);
    453 	if (end != NULL && !isspace(*end)) {
    454 		if (*end == 'c' || *end == 'C') {
    455 			end++;
    456 		} else if (*end == 'b' || *end == 'B') {
    457 			end++;
    458 			temp *= 512;
    459 		} else if (*end == 'k' || *end == 'K') {
    460 			end++;
    461 			temp *= 1024;
    462 		} else if (*end == 'm' || *end == 'M') {
    463 			end++;
    464 			temp *= (1024 * 1024);
    465 		} else if (*end == 'g' || *end == 'G') {
    466 			end++;
    467 			temp *= (1LL<<30);
    468 		} else if (*end == 't' || *end == 'T') {
    469 			end++;
    470 			temp *= (1LL<<40);
    471 		} else if (*end == 'p' || *end == 'P') {
    472 			end++;
    473 			temp *= (1LL<<50);
    474 		} else if (*end == 'e' || *end == 'E') {
    475 			end++;
    476 			temp *= (1LL<<60);
    477 		} else if (*end != NULL && !isspace(*end) &&
    478 		    !check_ends(*end, infop) &&
    479 		    choose_operator(end, &unused) == set) {
    480 			parse_error(infop,
    481 			    gettext("Expected one of [bkmgtpe]"
    482 			    " a space or an operator not \"%c\"\n"),
    483 			    *end++);
    484 		}
    485 	}
    486 	if (out != NULL)
    487 		*out = end;
    488 	return (temp);
    489 }
    490 static int
    491 has_white_space(const char *str)
    492 {
    493 	while (*str != NULL) {
    494 		if (isspace(*(str++))) {
    495 			return (1);
    496 		}
    497 	}
    498 	return (0);
    499 }
    500 
    501 void
    502 print_args(int argc, char **argv, void (*pprintf)(const char *fmt, ...))
    503 {
    504 	int i;
    505 	char *str = NULL;
    506 	int len = 0;
    507 
    508 	for (i = 0; i < argc; i++) {
    509 		char *tmp;
    510 		char *quote;
    511 		int olen = len;
    512 
    513 		len += (strlen(argv[i]));
    514 
    515 		if (has_white_space(argv[i])) {
    516 			quote = "\"";
    517 			len += 3;
    518 		} else {
    519 			quote = "";
    520 			len += 1;
    521 		}
    522 
    523 		tmp = realloc(str, len + 1);
    524 		if (tmp == NULL) {
    525 			free(str);
    526 			return;
    527 		} else {
    528 			str = tmp;
    529 		}
    530 		if (i == 0) {
    531 			tmp = "";
    532 			len--;
    533 		} else {
    534 			tmp = " ";
    535 		}
    536 
    537 		snprintf(&str[olen], len + 1 - olen, "%s%s%s%s", tmp, quote,
    538 		    argv[i], quote);
    539 	}
    540 	pprintf("%s\n", str);
    541 	free(str);
    542 }
    543 
    544 int
    545 do_args(int argc, char **argv, void (*pprintf)(char *fmt, ...),
    546 	const char *path)
    547 {
    548 	extern char *optarg;
    549 	int c;
    550 
    551 	opts_init();
    552 	set_defaults();
    553 
    554 	while ((c = getopt(argc, argv, "?f:o:h:d:")) != EOF) {
    555 		switch (c) {
    556 			case 'f':
    557 				if (!read_config_file(optarg, pprintf, path))
    558 					return (0);
    559 				break;
    560 			case 'o': {
    561 				struct option_info info;
    562 
    563 				info.filename = NULL;
    564 				info.lineno.end  = optind;
    565 				info.line = strdup(optarg);
    566 				info.no_report = 0;
    567 				(void) process_option(optarg, &info);
    568 				if (info.line)
    569 					free(info.line);
    570 				break;
    571 			}
    572 			case 'h': /* print the html */
    573 				print_html(*argv, optarg);
    574 				break;
    575 			case 'd': /* dump the current options */
    576 				(void) print_file(*argv, optarg,
    577 				    print_option_file);
    578 				break;
    579 			case '?':
    580 				print_defaults(stdout);
    581 				exit(0);
    582 			default:
    583 				usage(*argv);
    584 		}
    585 	}
    586 	return (1);
    587 }
    588 static char *
    589 get_next_line(char *line, char **ret, struct lineinfo *linenop)
    590 {
    591 	char *x, *y;
    592 	char out = 0;
    593 	char waswhite = 0;
    594 	char comment = 0;
    595 	for (y = x = line; *x != NULL && out == 0; ) {
    596 		switch (*x) {
    597 			case '\\':
    598 				if (x == line)
    599 					line++;
    600 				x++;
    601 				if (*x == NULL)
    602 					out = 1;
    603 				else {
    604 					if (x == line)
    605 						line++;
    606 					if (*x == '\n')
    607 						linenop->end += 1;
    608 					x++;
    609 				}
    610 				break;
    611 			case '#':
    612 				x++;
    613 				comment = 1;
    614 				break;
    615 			case '\n':
    616 				x++;
    617 				out = 1;
    618 				waswhite = 0;
    619 				linenop->end += 1;
    620 				break;
    621 			case ' ':
    622 			case '\t':
    623 				if (0 == comment && 0 == waswhite) {
    624 					*y++ = *x++;
    625 					waswhite = 1;
    626 				} else
    627 					x++;
    628 				break;
    629 			default:
    630 				if (!comment)
    631 					*y++ = *x++;
    632 				else
    633 					x++;
    634 				waswhite = 0;
    635 				break;
    636 		}
    637 	}
    638 	*ret = x;
    639 	*y = NULL;
    640 	return (line);
    641 }
    642 static struct ilist
    643 parse_comma_list(char *in, struct option_info *infop)
    644 {
    645 	char *x = in;
    646 	long long temp, wtemp;
    647 	int *res;
    648 	struct ilist out;
    649 	int count = 0;
    650 	struct option_info info;
    651 
    652 
    653 	out.wlen = 0;
    654 	out.vals = NULL;
    655 	out.weightings = NULL;
    656 	if (infop) {
    657 		info = *infop;
    658 	} else {
    659 		info.built_in_option = 1;
    660 		info.filename = "(built in default)";
    661 		info.line = NULL;
    662 	}
    663 
    664 	do {
    665 		char *end;
    666 		int i;
    667 
    668 		info.endchrs = OPEN_BRACKET_STR;
    669 		errno = 0;
    670 		temp = do_maths(0, x, &end, &info);
    671 		if (errno) {
    672 			parse_error(&info,
    673 			    gettext("error parsing \"%c\" in lint %s\n"),
    674 			    *x, in);
    675 		}
    676 		if (temp == 0)
    677 			break;
    678 		if (end != NULL) {
    679 			if (*end == OPEN_BRACKET) {
    680 				x = end + 1;
    681 				errno = 0;
    682 				info.endchrs = CLOSE_BRACKET_STR;
    683 				wtemp = do_maths(0, x, &end, &info);
    684 					if (errno) {
    685 						parse_error(&info,
    686 						    gettext("error parsing "
    687 						    "\"%c\" in lint %s\n"),
    688 						    *x, in);
    689 					}
    690 				if (*end != CLOSE_BRACKET) {
    691 					parse_error(&info, gettext(
    692 					    "error parsing list %s\n"), in);
    693 					exit(1);
    694 				}
    695 				end++;
    696 				if (wtemp == 0)
    697 					continue;
    698 			} else {
    699 				wtemp = 1;
    700 			}
    701 			i = out.wlen;
    702 			out.wlen += wtemp;
    703 			res = realloc(out.weightings, out.wlen *
    704 			    sizeof (int));
    705 			if (res == NULL) {
    706 				REALLOC_ERROR(out.weightings, out.wlen *
    707 				    sizeof (int));
    708 				exit(1);
    709 			}
    710 			out.weightings = res;
    711 			while (i < out.wlen) {
    712 				out.weightings[i++] = count;
    713 			}
    714 
    715 			if (*end == COMMA)
    716 				end++;
    717 		}
    718 		if (temp > UINT_MAX) {
    719 			(void) fprintf(stderr, gettext(
    720 			    "%lld (%#llx) greater than %d (%#x)\n"),
    721 			    temp, temp, UINT_MAX, UINT_MAX);
    722 			exit(1);
    723 		}
    724 		x = end;
    725 		res = realloc(out.vals, ++count * sizeof (int));
    726 		if (res == NULL) {
    727 			REALLOC_ERROR(out.vals, count * sizeof (int));
    728 			exit(1);
    729 		}
    730 		res[count-1] = (int)temp;
    731 		out.vals = res;
    732 	} while (temp != 0);
    733 
    734 	if (infop) {
    735 		const char *saveendchrs;
    736 		saveendchrs = infop->endchrs;
    737 		*infop  = info;
    738 		infop->endchrs = saveendchrs;
    739 	}
    740 	return (out);
    741 }
    742 static void
    743 usage_track_list(struct ilist *ilp)
    744 {
    745 	int i;
    746 	int last = -1;
    747 	int count = 0;
    748 	char x[128];
    749 
    750 	for (i = 0; i < ilp->wlen; i++) {
    751 		if (ilp->weightings[i] != last) {
    752 			if (count) {
    753 				(void) sprintf(x, "(%d)", count);
    754 				USAGE_TRACKING_STORE_VALUE(x);
    755 			}
    756 			(void) sprintf(x, "%s%#x", i ? ", " : "",
    757 			    ilp->vals[ilp->weightings[i]]);
    758 			USAGE_TRACKING_STORE_VALUE(x);
    759 			last = ilp->weightings[i];
    760 			count = 1;
    761 		} else {
    762 			count++;
    763 		}
    764 	}
    765 	USAGE_TRACKING_STORE_VALUE("\n");
    766 }
    767 
    768 static void
    769 fprint_ilist(FILE *out, struct ilist *ilp)
    770 {
    771 	int i;
    772 	int last = -1;
    773 	int count = 0;
    774 
    775 	for (i = 0; i < ilp->wlen; i++) {
    776 		if (ilp->weightings[i] != last) {
    777 			if (count) {
    778 				(void) fprintf(out, "(%d)", count);
    779 			}
    780 			(void) fprintf(out, "%s%#x", i ? ", " : "",
    781 			    ilp->vals[ilp->weightings[i]]);
    782 			last = ilp->weightings[i];
    783 			count = 1;
    784 		} else {
    785 			count++;
    786 		}
    787 	}
    788 	(void) fprintf(out, "\n");
    789 }
    790 static long long
    791 add(long long x, long long y)
    792 {
    793 	return (x + y);
    794 }
    795 static long long
    796 sub(long long x, long long y)
    797 {
    798 	return (x - y);
    799 }
    800 static long long
    801 mul(long long x, long long y)
    802 {
    803 	return (x * y);
    804 }
    805 static long long
    806 divide(long long x, long long y)
    807 {
    808 	return (x / y);
    809 }
    810 static long long
    811 mod(long long x, long long y)
    812 {
    813 	return (x % y);
    814 }
    815 static long long
    816 and(long long x, long long y)
    817 {
    818 	union {
    819 		long long ll;
    820 		uint_t i[sizeof (long long)/sizeof (uint_t)];
    821 	} xu, yu, r;
    822 	int i;
    823 
    824 #ifdef __lint
    825 	memset(&r, 0, sizeof (r));
    826 #endif
    827 	xu.ll = x;
    828 	yu.ll = y;
    829 
    830 	for (i = 0; i < sizeof (long long)/sizeof (uint_t); i++) {
    831 		r.i[i] = xu.i[i] & yu.i[i];
    832 	}
    833 	return (r.ll);
    834 }
    835 static long long
    836 or(long long x, long long y)
    837 {
    838 	union {
    839 		long long ll;
    840 		uint_t i[sizeof (long long)/sizeof (uint_t)];
    841 	} xu, yu, r;
    842 	int i;
    843 
    844 	xu.ll = x;
    845 	yu.ll = y;
    846 #ifdef __lint
    847 	memset(&r, 0, sizeof (r));
    848 #endif
    849 
    850 	for (i = 0; i < sizeof (long long)/sizeof (uint_t); i++) {
    851 		r.i[i] = xu.i[i] | yu.i[i];
    852 	}
    853 	return (r.ll);
    854 }
    855 static long long
    856 xor(long long x, long long y)
    857 {
    858 	union {
    859 		long long ll;
    860 		uint_t i[sizeof (long long)/sizeof (uint_t)];
    861 	} xu, yu, r;
    862 	int i;
    863 
    864 #ifdef __lint
    865 	memset(&r, 0, sizeof (r));
    866 #endif
    867 	xu.ll = x;
    868 	yu.ll = y;
    869 
    870 	for (i = 0; i < sizeof (long long)/sizeof (uint_t); i++) {
    871 		r.i[i] = xu.i[i] ^ yu.i[i];
    872 	}
    873 	return (r.ll);
    874 }
    875 /*ARGSUSED*/
    876 static long long
    877 set(long long x, long long y)
    878 {
    879 	return (y);
    880 }
    881 static int
    882 get_var(char *key, char **out, long long *v, struct option_info *infop)
    883 {
    884 #define	START_SECTION(A, B)
    885 #define	END_SECTION(A)
    886 #define	SET_OPENING_BLURB(A)
    887 	char *x, *env;
    888 	char c;
    889 #define	STARG(B, D, C)
    890 #define	LLARG(B, D, C) if (!strcasecmp(key, opts_names.B))  {\
    891 	*out = key + strlen(opts_names.B);\
    892 	*v = opts.B;\
    893 	return (1);\
    894 }
    895 #define	ILIST(A, B, C)
    896 #define	LARG(B, D, C) LLARG(B, D, C)
    897 #define	IARG(B, D, C) LLARG(B, D, C)
    898 #define	CARG(B, D, C) LLARG(B, D, C)
    899 #define	BARG(B, D, C) LLARG(B, D, C)
    900 #define	SARG(B, D, C) LLARG(B, D, C)
    901 #include "arg_vals.h"
    902 #undef STARG
    903 #undef SARG
    904 #undef CARG
    905 #undef BARG
    906 #undef IARG
    907 #undef LLARG
    908 #undef LARG
    909 #undef ILIST
    910 #undef START_SECTION
    911 #undef END_SECTION
    912 #undef SET_OPENING_BLURB
    913 	x = key;
    914 	while (!isspace(*x) && *x != NULL)
    915 		++x;
    916 	c = *x;
    917 	*x = NULL;
    918 
    919 	if (get_long_long_option(key, v) == OPT_FAILED &&
    920 			(env = getenv(key)) != NULL) {
    921 		*x = c;
    922 		errno = 0;
    923 		*v = my_strtoll(env, NULL, 0, infop);
    924 		if (errno != 0)
    925 			return (0);
    926 		return (1);
    927 	}
    928 	return (1);
    929 }
    930 static int
    931 do_variable(char *str, char **out, long long *ret, struct option_info *infop)
    932 {
    933 	char *line = str;
    934 	while (isspace(*str))
    935 		++str;
    936 
    937 	if (*str == DOLLAR) {
    938 		if (*(++str) == OPEN_BRACE) {
    939 			char *x;
    940 
    941 			str++;
    942 			x = strchr(str, CLOSE_BRACE);
    943 			if (x == NULL) {
    944 				parse_error(infop, gettext(
    945 				    "Error parsing variable: no close "
    946 				    "brace: %s\n"), line);
    947 				exit(1);
    948 			}
    949 			*x = NULL;
    950 			if (get_var(str, out, ret, infop) == 0) {
    951 				exit(1);
    952 			}
    953 			*x = CLOSE_BRACE;
    954 			str = ++x;
    955 		} else {
    956 			if (get_var(str, out, ret, infop) == 0) {
    957 				exit(1);
    958 			}
    959 			str = *out;
    960 		}
    961 		*out = str;
    962 		return (1);
    963 	}
    964 	*out = str;
    965 	return (0);
    966 }
    967 struct operators {
    968 	char op;
    969 	operator_t func;
    970 };
    971 static struct operators operators[] = {
    972 	{ '+', add },
    973 	{ '-', sub },
    974 	{ '*', mul },
    975 	{ '/', divide },
    976 	{ '%', mod },
    977 	{ '&', and },
    978 	{ '|', or },
    979 	{ '^', xor },
    980 	{ '=', set },
    981 	{ ' ', NULL }
    982 };
    983 
    984 static long long
    985 (*choose_operator(char *str, char **newstr))(long long x, long long y)
    986 {
    987 	struct operators *ops;
    988 	while (isspace(*str))
    989 		++str;
    990 
    991 	for (ops = &operators[0]; ops->func != NULL; ops++) {
    992 		if (ops->op == *str) {
    993 			*newstr = str + 1;
    994 			return (ops->func);
    995 		}
    996 	}
    997 	*newstr = str;
    998 	return (set);
    999 }
   1000 static long long
   1001 do_maths(long long cur, char *str, char **nstr, struct option_info *infop)
   1002 {
   1003 	long long (*op)(long long, long long);
   1004 	long long x;
   1005 	int v;
   1006 	char *newstr;
   1007 	char *line = str;
   1008 
   1009 	while (isspace(*str))
   1010 		++str;
   1011 	if (do_variable(str, &newstr, &x, infop))
   1012 		cur = x;
   1013 	str = newstr;
   1014 	op = choose_operator(str, &newstr);
   1015 	str = newstr;
   1016 	while (isspace(*str))
   1017 		++str;
   1018 	if (*str == OPEN_BRACKET) {
   1019 		x = do_maths(0, ++str, &newstr, infop);
   1020 		while (isspace(*str))
   1021 			++str;
   1022 		if (*str == CLOSE_BRACKET)
   1023 			++str;
   1024 	} else if (*str == CLOSE_BRACKET) {
   1025 		if (nstr)
   1026 			*nstr = str;
   1027 		return (cur);
   1028 	} else if (!(v = do_variable(str, &newstr, &x, infop)) &&
   1029 	    strchr("0123456789", *str) == NULL &&
   1030 	    (infop == NULL || infop->endchrs == NULL ||
   1031 	    strchr(infop->endchrs, *str) == NULL)) {
   1032 		parse_error(infop, gettext("error at char \"%c\" "
   1033 		    "expected one of [0-9] or (\n"), *str);
   1034 		if (nstr)
   1035 			*nstr = str;
   1036 		return (cur);
   1037 	} else if (*str == NULL) {
   1038 		if (nstr)
   1039 			*nstr = str;
   1040 		return (cur);
   1041 	} else if (!v) {
   1042 		errno = 0;
   1043 		x = my_strtoll(str, &newstr, 0, infop);
   1044 		if (errno) {
   1045 			parse_error(infop, gettext(
   1046 			    "error Bad number at \"%c\" in %s\n"),
   1047 			    *str, line);
   1048 			if (nstr)
   1049 				*nstr = newstr;
   1050 			return (cur);
   1051 		}
   1052 	}
   1053 	str = newstr;
   1054 	while (isspace(*str))
   1055 		++str;
   1056 	if (*str == NULL || *str == CLOSE_BRACKET || check_ends(*str, infop)) {
   1057 		if (nstr)
   1058 			*nstr = str;
   1059 		cur = op(cur, x);
   1060 		if (*str != NULL && !check_ends(*str, infop)) {
   1061 			str++;
   1062 			return (do_maths(cur, str, nstr, infop));
   1063 		} else {
   1064 			return (cur);
   1065 		}
   1066 	} else {
   1067 		return (do_maths(op(cur, x), str, nstr, infop));
   1068 	}
   1069 }
   1070 static int
   1071 do_key_value(char *key, char *value, struct option_info *infop)
   1072 {
   1073 /* STARG String ARG */
   1074 #define	START_SECTION(A, B)
   1075 #define	END_SECTION(A)
   1076 #define	SET_OPENING_BLURB(A)
   1077 #ifdef NOT
   1078 #define	STARG(B, D, C) if (!strcasecmp(key, opts_names.B))  {\
   1079 	NOT_NULL_FREE(opts.B);\
   1080 	if (strcmp(value, "NULL") == 0) \
   1081 		opts.B = NULL; \
   1082 	else if ((opts.B = strdup(value)) == NULL) \
   1083 		return (0); \
   1084 	else \
   1085 		return (1); \
   1086 }
   1087 #else
   1088 #define	STARG(B, D, C) if (!strcasecmp(key, opts_names.B))  {\
   1089 	if (strcmp(value, "NULL") == 0) \
   1090 		opts.B = NULL; \
   1091 	else {\
   1092 		opts.B = value; \
   1093 		return (1); \
   1094 	} \
   1095 }
   1096 #endif
   1097 #define	NARROW_ARG(A, B, C) if (!strcasecmp(key, opts_names.C)) {\
   1098 	long x; \
   1099 	x = do_maths(opts.C, value, NULL, infop);\
   1100 	if (x > B) \
   1101 		opts.C = B; \
   1102 	else \
   1103 		opts.C = (A) x; \
   1104 	return (1); \
   1105 }
   1106 #define	LLARG(B, D, C) if (!strcasecmp(key, opts_names.B))  {\
   1107 	opts.B = do_maths(opts.B, value, NULL, infop);\
   1108 	return (1); \
   1109 }
   1110 #define	ILIST(A, B, C) if (!strcasecmp(key, opts_names.A))  {\
   1111 		opts.A = parse_comma_list(value, infop);\
   1112 		return (1); \
   1113 	}
   1114 #define	LARG(B, D, C) NARROW_ARG(ulong_t, ULONG_MAX, B)
   1115 #define	IARG(B, D, C) NARROW_ARG(uint_t, UINT_MAX, B)
   1116 #define	CARG(B, D, C) NARROW_ARG(uchar_t, UCHAR_MAX, B)
   1117 #define	BARG(B, D, C) NARROW_ARG(uchar_t, 1, B)
   1118 #define	SARG(B, D, C) NARROW_ARG(ushort_t, USHRT_MAX, B)
   1119 #include "arg_vals.h"
   1120 #undef STARG
   1121 #undef SARG
   1122 #undef CARG
   1123 #undef BARG
   1124 #undef IARG
   1125 #undef LLARG
   1126 #undef LARG
   1127 #undef ILIST
   1128 #undef START_SECTION
   1129 #undef END_SECTION
   1130 #undef SET_OPENING_BLURB
   1131 	return (0);
   1132 }
   1133 static int
   1134 process_option(char *option, struct option_info *infop)
   1135 {
   1136 	char *value;
   1137 	char c;
   1138 	int x;
   1139 
   1140 	if (*option == '#' || (value = strchr(option, '=')) == NULL)
   1141 		return (0);
   1142 
   1143 	c = *value;
   1144 	*value = NULL;
   1145 	/* We have option pointing to the option and value to the value */
   1146 	x = do_key_value(option, value+1, infop);
   1147 	if (x == 0) {
   1148 		infop->built_in_option = 0;
   1149 		(void) store_option(option, value+1, infop);
   1150 		*value = c;
   1151 		(void) putenv(option);
   1152 	} else {
   1153 		infop->built_in_option = 1;
   1154 		(void) store_option(option, value+1, infop);
   1155 		*value = c;
   1156 	}
   1157 	return (1);
   1158 }
   1159 
   1160 #define	START_SECTION(A, B)
   1161 #define	END_SECTION(A)
   1162 #define	SET_OPENING_BLURB(A)
   1163 static void
   1164 set_defaults(void)
   1165 {
   1166 	struct option_info info;
   1167 	long long x;
   1168 	info.built_in_option = 1;
   1169 	info.filename = "(built in default)";
   1170 	info.line = NULL;
   1171 	info.endchrs = NULL;
   1172 
   1173 
   1174 #define	STARG(B, D, C) opts.B = (D == NULL ? NULL : strdup(D)); \
   1175 	(void) store_option(#B, \
   1176 	(D == NULL ? "NULL" : strdup(D)), &info);
   1177 #define	SARG(B, D, C) x = opts.B = D;\
   1178 	(void) store_option_with_val(#B, #D, &x, &info);
   1179 #define	IARG(B, D, C) SARG(B, D, C)
   1180 #define	CARG(B, D, C) SARG(B, D, C)
   1181 #define	BARG(B, D, C) SARG(B, D, C)
   1182 #define	LLARG(B, D, C) SARG(B, D, C)
   1183 #define	LARG(B, D, C) SARG(B, D, C)
   1184 #define	ILIST(A, B, C) opts.A = parse_comma_list(B, NULL);
   1185 #include "arg_vals.h"
   1186 #undef	STARG
   1187 #undef	SARG
   1188 #undef	IARG
   1189 #undef	CARG
   1190 #undef	BARG
   1191 #undef	LLARG
   1192 #undef	LARG
   1193 #undef	ILIST
   1194 }
   1195 #undef START_SECTION
   1196 #undef END_SECTION
   1197 #undef SET_OPENING_BLURB
   1198 static const char *
   1199 search_back(const char *start, const char *from, const char *str, int len)
   1200 {
   1201 	while (from >= start) {
   1202 		int i;
   1203 
   1204 		for (i = 0; i < len; i++)
   1205 			if (str[i] == *from)
   1206 				return (from);
   1207 		from--;
   1208 	}
   1209 	return (NULL);
   1210 }
   1211 static char *
   1212 strip_html(const char *str)
   1213 {
   1214 	char *tmp, *ret;
   1215 	const char *in;
   1216 	int depth = 0;
   1217 	if ((ret = strdup(str)) == NULL)
   1218 		return (ret);
   1219 	in = str;
   1220 
   1221 	tmp = ret;
   1222 	while (*str != NULL) {
   1223 		if (*str == '<') {
   1224 			depth++;
   1225 			str++;
   1226 		} else if (*str == '>') {
   1227 			if (--depth < 0) {
   1228 				(void) fprintf(stderr, gettext(
   1229 				    "Unbalanced html tags\n\n%s\n"), in);
   1230 			}
   1231 			str++;
   1232 		} else if (depth == 0)
   1233 			*tmp++ = *str++;
   1234 		else
   1235 			str++;
   1236 	}
   1237 	*tmp = NULL;
   1238 	return (ret);
   1239 }
   1240 static const char *
   1241 search_forward(const char *str, char c, int len)
   1242 {
   1243 	int x;
   1244 
   1245 	for (x = 0; x < len; x++) {
   1246 		if (str[x] == NULL)
   1247 			return (NULL);
   1248 		if (str[x] == c)
   1249 			return (&str[x]);
   1250 	}
   1251 	return (NULL);
   1252 }
   1253 
   1254 
   1255 static void
   1256 print_line(FILE *f, const char *line, const char *newline, const char *endl)
   1257 {
   1258 	const char *this_line;
   1259 	const char *x;
   1260 
   1261 	for (this_line = line; /* until break */; /* nothing */) {
   1262 		int len;
   1263 		if ((x = search_forward(this_line, '\n', LINE_LEN)) == NULL) {
   1264 			if (strlen(this_line) < LINE_LEN) {
   1265 				(void) fprintf(f, "%s%s\n", newline,
   1266 				    this_line);
   1267 				break;
   1268 			} else {
   1269 				x = search_back(this_line,
   1270 				    &this_line[LINE_LEN - 1], " \t", 2);
   1271 			}
   1272 		}
   1273 		if (x != NULL) {
   1274 			/*LINTED*/
   1275 			len = x - this_line;
   1276 		} else
   1277 			len = (int)strlen(this_line);
   1278 		(void) fprintf(f, "%s%.*s", newline, len, this_line);
   1279 
   1280 		if (x == NULL) {
   1281 			(void) fprintf(f, "\n");
   1282 			break;
   1283 		} else {
   1284 			(void) fprintf(f, "%s", endl == NULL ? "\n" : endl);
   1285 			this_line = x + 1;
   1286 		}
   1287 	}
   1288 }
   1289 static void
   1290 print_comment(FILE *f, const char *newline,
   1291 	const char *option,  const char *str)
   1292 {
   1293 	char *to_free;
   1294 
   1295 	if ((to_free = strip_html(str)) == NULL)
   1296 		return;
   1297 
   1298 	if (option != NULL)
   1299 		(void) fprintf(f, "%s\n%s%s\n", newline, newline, option);
   1300 	print_line(f, to_free, newline, NULL);
   1301 	free(to_free);
   1302 }
   1303 static void
   1304 print_copyright(FILE *f, const char *newline, const char *endline)
   1305 {
   1306 	(void) fprintf(f, "%s%s\n%s\tDiskomizer %s%s\n%s\t"
   1307 	    "Copyright Sun Microsystems Inc 1988-%s%s\n%s%s\n",
   1308 	    newline, endline, newline, VERSION, endline,
   1309 	    newline, THIS_YEAR, endline, newline, endline);
   1310 }
   1311 static void
   1312 print_max_min_disclaimer(FILE *f, const char *newline, char *str)
   1313 {
   1314 	print_comment(f, newline, str, gettext(
   1315 	    "The maximum and minimum values listed are the values at which "
   1316 	    "the options are capped. They do not mean that "
   1317 	    "the program will run with the values given and should not"
   1318 	    " be taken to be recommended values."));
   1319 	(void) fprintf(f, "%s\n", newline);
   1320 }
   1321 int
   1322 print_options(FILE *f)
   1323 {
   1324 	char x[256];
   1325 	int i;
   1326 	char buf[1024];
   1327 	struct opts_comments *opts_namesp;
   1328 
   1329 #define	UPPER(A) { \
   1330 	for (i = 0; A[i] != NULL; i++) { \
   1331 		if (islower(A[i])) { \
   1332 			x[i] = toupper(A[i]); \
   1333 		} else { \
   1334 			x[i] = A[i]; \
   1335 		} \
   1336 	} \
   1337 	x[i] = NULL; \
   1338 }
   1339 
   1340 
   1341 	print_copyright(f, HASH, "");
   1342 	print_comment(f, HASH, NULL, OPENING_STR);
   1343 	print_max_min_disclaimer(f, HASH, "Option file");
   1344 	(void) fprintf(f, "# %s=%s\n", gettext("Option Name"),
   1345 		gettext("Value"));
   1346 	(void) fprintf(f, "# %s=%s\n", "-----------", "-----");
   1347 #define	START_SECTION(A, B) UPPER(opts_snames.A); \
   1348 	print_comment(f, HASH, x, opts_scomments.A);
   1349 #define	END_SECTION(A)
   1350 #define	SET_OPENING_BLURB(A)
   1351 #define	ILIST(A, B, C) UPPER(opts_names.A); \
   1352 	print_comment(f, HASH, x, gettext(opts_comments.A)); \
   1353 	(void) fprintf(f, "%s=", x); \
   1354 	fprint_ilist(f, &opts.A);
   1355 #define	IARG(A, B, C) UPPER(opts_names.A); \
   1356 	print_comment(f, HASH, x, gettext(opts_comments.A)); \
   1357 	(void) fprintf(f, "%s %u\n", min_max_str, UINT_MAX); \
   1358 	(void) sprintf(buf, gettext("Default is \"%s\" = %u  (%#x)"), \
   1359 		opts_def_strings.A, B, B); \
   1360 	print_comment(f, HASH, NULL, &buf[0]); \
   1361 	(void) fprintf(f, "%s=%u # %#x\n", x, opts.A, opts.A);
   1362 #define	CARG(A, B, C) UPPER(opts_names.A); \
   1363 	print_comment(f, HASH, x, gettext(opts_comments.A)); \
   1364 	(void) fprintf(f, "%s %u\n", min_max_str, \
   1365 		(int)UCHAR_MAX); \
   1366 	(void) sprintf(buf, gettext("Default is \"%s\" = %u  (%#x)"), \
   1367 		opts_def_strings.A, B, B); \
   1368 	print_comment(f, HASH, NULL, &buf[0]); \
   1369 	(void) fprintf(f, "%s=%d # %#2.x\n", x, opts.A, opts.A);
   1370 #define	BARG(A, B, C) UPPER(opts_names.A); \
   1371 	print_comment(f, HASH, x, gettext(opts_comments.A)); \
   1372 	(void) fprintf(f, "%s %u\n", min_max_str, 1); \
   1373 	(void) sprintf(buf, gettext("Default is \"%s\" = %u  (%#x)"), \
   1374 		opts_def_strings.A, B, B); \
   1375 	print_comment(f, HASH, NULL, &buf[0]); \
   1376 	(void) fprintf(f, "%s=%d\n", x, opts.A);
   1377 #define	STARG(A, B, C) UPPER(opts_names.A); \
   1378 	print_comment(f, HASH, x, gettext(opts_comments.A)); \
   1379 	(void) fprintf(f, "%s=", x); \
   1380 	print_line(f, opts.A == NULL ? "NULL" : opts.A, "", " \\\n\t");
   1381 #define	SARG(A, B, C) UPPER(opts_names.A); \
   1382 	print_comment(f, HASH, x, gettext(opts_comments.A)); \
   1383 	(void) fprintf(f, "%s %u\n", min_max_str, \
   1384 		USHRT_MAX); \
   1385 	(void) fprintf(f, "%s=%d # %#4.x\n", x, opts.A, opts.A);
   1386 #define	LLARG(A, B, C) UPPER(opts_names.A); \
   1387 	print_comment(f, HASH, x, gettext(opts_comments.A)); \
   1388 	(void) fprintf(f, "%s %llu\n",  min_max_str,\
   1389 		LONGLONG_MAX); \
   1390 	(void) sprintf(buf, gettext("Default is \"%s\" = %llu  (%#llx)"), \
   1391 		opts_def_strings.A, B, B); \
   1392 	print_comment(f, HASH, NULL, &buf[0]); \
   1393 	(void) fprintf(f, "%s=%llu # %#llx\n", x, opts.A, opts.A);
   1394 #define	LARG(A, B, C) UPPER(opts_names.A); \
   1395 	print_comment(f, HASH, x, gettext(opts_comments.A)); \
   1396 	(void) fprintf(f, "%s %lu\n",  min_max_str,\
   1397 		ULONG_MAX); \
   1398 	(void) sprintf(buf, gettext("Default is \"%s\" = %lu  (%#lx)"), \
   1399 		opts_def_strings.A, B, B); \
   1400 	print_comment(f, HASH, NULL, &buf[0]); \
   1401 	(void) fprintf(f, "%s=%lu # %#lx\n", x, opts.A, opts.A);
   1402 #include "arg_vals.h"
   1403 #undef UPPER
   1404 #undef STARG
   1405 #undef ILIST
   1406 #undef SARG
   1407 #undef LLARG
   1408 #undef LARG
   1409 #undef CARG
   1410 #undef BARG
   1411 #undef IARG
   1412 #undef START_SECTION
   1413 #undef END_SECTION
   1414 #undef SET_OPENING_BLURB
   1415 	print_all_option_entries(f);
   1416 	return (1);
   1417 }
   1418 int
   1419 save_options(const char *file)
   1420 {
   1421 	return (print_file(NULL, file, print_option_file));
   1422 }
   1423 
   1424 /*ARGSUSED*/
   1425 static int
   1426 print_option_file(const char *prog, const char *file, FILE *f)
   1427 {
   1428 	return (print_options(f));
   1429 }
   1430 /*ARGSUSED*/
   1431 static int
   1432 print_html_file(const char *prog, const char *filename, FILE *f)
   1433 {
   1434 #define	LIST_STRING "<LI><B><A NAME=\"%s\">%s</A></B>\n<BR>\n"
   1435 #define	TITLE_STRING "<LI><H3><A NAME=\"%s\">%s</A></H3>\n"
   1436 #define	CONTENT_STRING "%s\n<P>\n<OL>\n"
   1437 /*
   1438  * lint will check the printf like format strings, but only if they are
   1439  * done as macros and not if as constant strings. Hence this
   1440  * giggery pokery. Constant strings take up less space in the binary.
   1441  */
   1442 #ifndef __lint
   1443 	const char LI_STR[] = LIST_STRING;
   1444 	const char TITLE_STR[] = TITLE_STRING;
   1445 	const char CONTENT_STR[] = CONTENT_STRING;
   1446 #else
   1447 #define	LI_STR LIST_STRING
   1448 #define	TITLE_STR TITLE_STRING
   1449 #define	CONTENT_STR CONTENT_STRING
   1450 #endif
   1451 	char x[256];
   1452 	char *now;
   1453 	int i;
   1454 
   1455 	now = alloc_time_now_fmt("%Y" "%m" "%d:%H" "%M" "%S00");
   1456 
   1457 
   1458 	(void) fprintf(f, "<HTML>\n<!--START_PAGE_CONFIG\n"
   1459 	    "page.sidebar=off\n"
   1460 	    "page.greybar=on\n"
   1461 	    "page.greybar.entry.1=Overview|\"diskomizer.html\"\n"
   1462 	    "page.greybar.entry.2="
   1463 	    "Getting Started|\"Getting_started.html\"\n"
   1464 	    "page.greybar.entry.3=Options %s|\"Options-%s.html\"\n"
   1465 	    "page.greybar.entry.4=Options %s|\"Options-%s.html\"\n"
   1466 	    "page.greybar.entry.5=Options %s|\"Options-%s.html\"\n"
   1467 	    "page.greybar.entry.6=New Features|\"new-features.html\"\n"
   1468 	    "page.greybar.entry.7=Interpreting Errors|"
   1469 	    "\"Interpreting_Errors.html\"\n"
   1470 	    "END_PAGE_CONFIG-->\n",
   1471 #ifdef __sparc
   1472 	    "i86pc", "i86pc",
   1473 	    "AMD 64", "amd64",
   1474 	    LONG_BIT == 32 ? "SPARC v9" : "SPARC v7",
   1475 	    LONG_BIT == 32 ? "64" : "32"
   1476 #else
   1477 	    "SPARC v7", "32",
   1478 	    "SPARC v9", "64",
   1479 	    LONG_BIT == 32 ? "AMD 64" : "i86pc",
   1480 	    LONG_BIT == 32 ? "amd64" : "i86pc"
   1481 #endif
   1482 	    /* for cystye */);
   1483 	(void) fprintf(f,
   1484 	    "<HEAD>\n\t<TITLE>Diskomizer Options</TITLE>\n"
   1485 	    "\t<META NAME=\"GENERATOR\" CONTENT=\"diskomizer %s\">\n"
   1486 	    "\t<META NAME=\"CREATED\" CONTENT=\"%s\">\n"
   1487 	    "\t<META NAME=\"CHANGEDBY\" CONTENT=\" \">\n"
   1488 	    "\t<META NAME=\"CHANGED\" CONTENT=\"%s\">\n"
   1489 	    "\t<META NAME=\"KEYWORDS\" CONTENT="
   1490 	    "\"diskomizer disk testing stress test\">\n"
   1491 	    "\t<META HTTP-EQUIV=\"Content-Type\" "
   1492 	    /* CSTYLED */
   1493 	    "CONTENT=\"text/html; charset=iso-8859-1\">\n",
   1494 	    VERSION, now, now);
   1495 	free(now);
   1496 	(void) fprintf(f, "</HEAD>\n<BODY>\n");
   1497 	print_copyright(f, "<! ", ">");
   1498 	(void) fprintf(f, "<H1>Diskomizer %s</H1><P>\n", VERSION);
   1499 	(void) fprintf(f, "%s\n<P>\n", OPENING_STR);
   1500 	print_max_min_disclaimer(f, "", NULL);
   1501 	(void) fprintf(f, "<HR>\n");
   1502 	(void) fprintf(f, "<OL>\n");
   1503 #define	LIST_ITEM(A) (void) fprintf(f, LI_STR, A, A)
   1504 
   1505 #define	UPPER(A) { \
   1506 	for (i = 0; A[i] != NULL; i++) { \
   1507 		if (islower(A[i])) { \
   1508 			x[i] = toupper(A[i]); \
   1509 		} else { \
   1510 			x[i] = A[i]; \
   1511 		} \
   1512 	} \
   1513 	x[i] = NULL; \
   1514 }
   1515 
   1516 #define	START_SECTION(A, B) UPPER(opts_snames.A); \
   1517 	(void) fprintf(f, TITLE_STR, opts_snames.A, opts_snames.A); \
   1518 	if (opts_scomments.A != NULL) \
   1519 		(void) fprintf(f, CONTENT_STR, opts_scomments.A); \
   1520 	else \
   1521 		(void) fprintf(f, "<OL>");
   1522 #define	END_SECTION(A) (void) fprintf(f, "</OL>");
   1523 #define	SET_OPENING_BLURB(A)
   1524 #define	ILIST(A, B, C) UPPER(opts_names.A); \
   1525 	LIST_ITEM(x); \
   1526 	(void) fprintf(f, "%s", opts_comments.A); \
   1527 	(void) fprintf(f, "<BR>\n%s %u\n", &min_max_str[2], UINT_MAX); \
   1528 	(void) fprintf(f, "<BR>Default is \"%s\"", opts_def_strings.A); \
   1529 	(void) fprintf(f, "<BR>%s=%s\n", x, B);
   1530 #define	IARG(A, B, C) UPPER(opts_names.A); \
   1531 	LIST_ITEM(x); \
   1532 	(void) fprintf(f, "%s", opts_comments.A); \
   1533 	(void) fprintf(f, "<BR>%s %u\n", &min_max_str[2], UINT_MAX); \
   1534 	(void) fprintf(f, "<BR>Default is \"%s\"  = %u (%#x)", \
   1535 		opts_def_strings.A, B, B); \
   1536 	(void) fprintf(f, "<BR>%s=%u\n", x, B);
   1537 #define	CARG(A, B, C) UPPER(opts_names.A); \
   1538 	LIST_ITEM(x); \
   1539 	(void) fprintf(f, "%s", opts_comments.A); \
   1540 	(void) fprintf(f, "<BR>%s %u\n", &min_max_str[2], \
   1541 		(int)UCHAR_MAX); \
   1542 	(void) fprintf(f, "<BR>Default is \"%s\"  = %u (%#x)", \
   1543 		opts_def_strings.A, B, B); \
   1544 	(void) fprintf(f, "<BR>%s=%d\n", x, B);
   1545 #define	BARG(A, B, C) UPPER(opts_names.A); \
   1546 	LIST_ITEM(x); \
   1547 	(void) fprintf(f, "%s", opts_comments.A); \
   1548 	(void) fprintf(f, "<BR>%s %u\n", &min_max_str[2], 1); \
   1549 	(void) fprintf(f, "<BR>Default is \"%s\"  = %u (%#x)", \
   1550 		opts_def_strings.A, B, B); \
   1551 	(void) fprintf(f, "<BR>%s=%d\n", x, B);
   1552 #define	STARG(A, B, C) UPPER(opts_names.A); \
   1553 	LIST_ITEM(x); \
   1554 	(void) fprintf(f, "%s\n", opts_comments.A); \
   1555 	(void) fprintf(f, "<BR>Default is %s", opts_def_strings.A); \
   1556 	(void) fprintf(f, "<BR>%s=%s\n", x, B == NULL? "NULL" : B);
   1557 #define	SARG(A, B, C) UPPER(opts_names.A); \
   1558 	LIST_ITEM(x); \
   1559 	(void) fprintf(f, "%s", opts_comments.A); \
   1560 	(void) fprintf(f, "<BR>%s %u\n", &min_max_str[2], \
   1561 		USHRT_MAX); \
   1562 	(void) fprintf(f, "<BR>Default is \"%s\" = %u (%#x)", \
   1563 		opts_def_strings.A, B, B); \
   1564 	(void) fprintf(f, "<BR>%s=%s\n", x, opts_names.A);
   1565 #define	LLARG(A, B, C) UPPER(opts_names.A); \
   1566 	LIST_ITEM(x); \
   1567 	(void) fprintf(f, "%s", opts_comments.A); \
   1568 	(void) fprintf(f, "<BR>%s %llu\n", &min_max_str[2], \
   1569 		LONGLONG_MAX); \
   1570 	(void) fprintf(f, "<BR>Default is \"%s\" = %llu (%#llx)", \
   1571 		opts_def_strings.A, B, B); \
   1572 	(void) fprintf(f, "<BR>%s=%llu\n", x, B);
   1573 #define	LARG(A, B, C) UPPER(opts_names.A); \
   1574 	LIST_ITEM(x); \
   1575 	(void) fprintf(f, "%s", opts_comments.A); \
   1576 	(void) fprintf(f, "<BR>%s %lu\n",  &min_max_str[2], \
   1577 		ULONG_MAX); \
   1578 	(void) fprintf(f, "<BR>Default is \"%s\" = %lu (%#lx)", \
   1579 		opts_def_strings.A, B, B); \
   1580 	(void) fprintf(f, "<BR>%s=%lu\n", x, B);
   1581 #include "arg_vals.h"
   1582 (void) fprintf(f, "</OL>\n<HR>\n");
   1583 print_copyright(f, "<BR>", "");
   1584 (void) fprintf(f, "</BODY>\n</HTML>\n");
   1585 #undef UPPER
   1586 #undef ILIST
   1587 #undef STARG
   1588 #undef SARG
   1589 #undef LLARG
   1590 #undef LARG
   1591 #undef CARG
   1592 #undef BARG
   1593 #undef IARG
   1594 #undef START_SECTION
   1595 #undef END_SECTION
   1596 #undef SET_OPENING_BLURB
   1597 	return (1);
   1598 }
   1599 
   1600 static void
   1601 report_back_opts()
   1602 {
   1603 	char *section;
   1604 	char x[256];
   1605 	int i;
   1606 	static const char option_str[] = "options";
   1607 
   1608 	USAGE_TRACKING_OPEN_KEY(option_str, NULL, NULL);
   1609 #define	UPPER(A) { \
   1610 	for (i = 0; A[i] != NULL; i++) { \
   1611 		if (islower(A[i])) { \
   1612 			x[i] = toupper(A[i]); \
   1613 		} else { \
   1614 			x[i] = A[i]; \
   1615 		} \
   1616 	} \
   1617 	x[i] = NULL; \
   1618 }
   1619 
   1620 #define	START_SECTION(A, B) section = opts_snames.A; \
   1621 	UPPER(section); \
   1622 	USAGE_TRACKING_OPEN_KEY(section, NULL, NULL);
   1623 
   1624 #define	END_SECTION(A) USAGE_TRACKING_CLOSE_KEY();
   1625 #define	SET_OPENING_BLURB(A)
   1626 #define	STARG(A, B, C) \
   1627 		UPPER(opts_names.A); \
   1628 		USAGE_TRACKING_STORE_KEY_VALUE(x, NIL_STR(opts.A));
   1629 #define	ILIST(A, B, C) \
   1630 		UPPER(opts_names.A); \
   1631 		USAGE_TRACKING_OPEN_KEY(x, NULL, NULL); \
   1632 		usage_track_list(&opts.A); \
   1633 		USAGE_TRACKING_CLOSE_KEY();
   1634 #define	IARG(A, B, C) \
   1635 		UPPER(opts_names.A); \
   1636 		USAGE_TRACKING_STORE_KEY_VALUE_INT(x, opts.A);
   1637 #define	CARG(A, B, C) IARG(A, B, C)
   1638 #define	BARG(A, B, C) IARG(A, B, C)
   1639 #define	SARG(A, B, C) IARG(A, B, C)
   1640 #define	LLARG(A, B, C) IARG(A, B, C)
   1641 #define	LARG(A, B, C) IARG(A, B, C)
   1642 
   1643 #include "arg_vals.h"
   1644 
   1645 #undef UPPER
   1646 #undef ILIST
   1647 #undef STARG
   1648 #undef SARG
   1649 #undef LLARG
   1650 #undef LARG
   1651 #undef CARG
   1652 #undef BARG
   1653 #undef IARG
   1654 #undef START_SECTION
   1655 #undef END_SECTION
   1656 #undef SET_OPENING_BLURB
   1657 
   1658 	USAGE_TRACKING_CLOSE_KEY();
   1659 }
   1660 
   1661 static int
   1662 print_file(const char *prog, const char *filename,
   1663 	int (*print_func)(const char *, const char *, FILE *))
   1664 {
   1665 	if (strcmp(filename, "-")) {
   1666 		FILE *f;
   1667 		if ((f = fopen(filename, "w")) == NULL) {
   1668 			FOPEN_ERROR(filename, "w");
   1669 			return (0);
   1670 		}
   1671 		print_func(prog, filename, f);
   1672 		(void) fflush(f);
   1673 		if (fclose(f) != 0) {
   1674 			CLOSE_ERROR(fileno(f), filename);
   1675 			return (0);
   1676 		}
   1677 	} else {
   1678 		print_func(prog, filename, stdout);
   1679 		(void) fflush(stdout);
   1680 	}
   1681 	return (1);
   1682 }
   1683 static void
   1684 print_html(const char *prog, const char *filename)
   1685 {
   1686 	(void) print_file(prog, filename, print_html_file);
   1687 	exit(0);
   1688 }
   1689 
   1690 static void
   1691 print_defaults(FILE *f)
   1692 {
   1693 	char buf[1024];
   1694 	int i;
   1695 
   1696 	print_max_min_disclaimer(f, HASH, NULL);
   1697 	(void) fprintf(f, "# %s=%s\n", "Option Name", "Default Value");
   1698 	(void) fprintf(f, "# %s=%s\n", "-----------", "-------------");
   1699 
   1700 #define	UPPER(A) { \
   1701 	for (i = 0; A[i] != NULL; i++) { \
   1702 		if (islower(A[i])) { \
   1703 			buf[i] = toupper(A[i]); \
   1704 		} else { \
   1705 			buf[i] = A[i]; \
   1706 		} \
   1707 	} \
   1708 	buf[i] = NULL; \
   1709 }
   1710 
   1711 #define	START_SECTION(A, B) UPPER(opts_snames.A); \
   1712 	print_comment(f, HASH, buf, opts_scomments.A);
   1713 #define	END_SECTION(A)
   1714 #define	SET_OPENING_BLURB(A)
   1715 #define	ILIST(A, B, C) UPPER(opts_names.A); \
   1716 	print_comment(f, HASH, buf, opts_comments.A); \
   1717 	(void) fprintf(f, "%s %u\n", min_max_str, UINT_MAX); \
   1718 	(void) fprintf(f, "%s=%s\n", buf, B);
   1719 #define	IARG(A, B, C) UPPER(opts_names.A); \
   1720 	print_comment(f, HASH, buf, opts_comments.A); \
   1721 	(void) fprintf(f, "%s %u\n", min_max_str, UINT_MAX); \
   1722 	(void) sprintf(buf, "Default is \"%s\" = %u  (%#x)", \
   1723 		opts_def_strings.A, B, B); \
   1724 	print_comment(f, HASH, NULL, &buf[0]); \
   1725 	(void) fprintf(f, "%s=%u\n", buf, B);
   1726 #define	CARG(A, B, C) UPPER(opts_names.A); \
   1727 	print_comment(f, HASH, buf, opts_comments.A); \
   1728 	(void) fprintf(f, "%s %u\n", min_max_str, \
   1729 		(int)UCHAR_MAX); \
   1730 	(void) fprintf(f, "%s=%d\n", buf, B);
   1731 #define	BARG(A, B, C) UPPER(opts_names.A); \
   1732 	print_comment(f, HASH, buf, opts_comments.A); \
   1733 	(void) fprintf(f, "%s %u\n", min_max_str, 1); \
   1734 	(void) fprintf(f, "%s=%d\n", buf, B);
   1735 #define	STARG(A, B, C) UPPER(opts_names.A); \
   1736 	print_comment(f, HASH, buf, opts_comments.A); \
   1737 	(void) fprintf(f, "%s=%s\n", buf, B == NULL? "NULL" : B);
   1738 #define	SARG(A, B, C) UPPER(opts_names.A); \
   1739 	print_comment(f, HASH, buf, opts_comments.A); \
   1740 	(void) fprintf(f, "%s %u\n", min_max_str, \
   1741 		USHRT_MAX); \
   1742 	(void) fprintf(f, "%s=%s\n", buf, opts_names.A);
   1743 #define	LLARG(A, B, C) UPPER(opts_names.A); \
   1744 	print_comment(f, HASH, buf, opts_comments.A); \
   1745 	(void) fprintf(f, "%s %llx\n", min_max_str, \
   1746 		LONGLONG_MAX); \
   1747 	(void) sprintf(buf, "Default is \"%s\" = %llu  (%#llx)", \
   1748 		opts_def_strings.A, B, B); \
   1749 	print_comment(f, HASH, NULL, &buf[0]); \
   1750 	(void) fprintf(f, "%s=%llu\n", buf, B);
   1751 #define	LARG(A, B, C) UPPER(opts_names.A); \
   1752 	print_comment(f, HASH, buf, opts_comments.A); \
   1753 	(void) fprintf(f, "%s %lu\n",  min_max_str, \
   1754 		ULONG_MAX); \
   1755 	(void) sprintf(buf, "Default is \"%s\" = %lu (%#lx)", \
   1756 		opts_def_strings.A, B, B); \
   1757 	print_comment(f, HASH, NULL, &buf[0]); \
   1758 	(void) fprintf(f, "%s=%lu\n", buf, B);
   1759 #include "arg_vals.h"
   1760 #undef UPPER
   1761 #undef ILIST
   1762 #undef STARG
   1763 #undef SARG
   1764 #undef LLARG
   1765 #undef LARG
   1766 #undef CARG
   1767 #undef BARG
   1768 #undef IARG
   1769 #undef START_SECTION
   1770 #undef END_SECTION
   1771 #undef SET_OPENING_BLURB
   1772 }
   1773 int
   1774 read_config_file(char *file, void (*pprintf)(char *fmt, ...), const char *path)
   1775 {
   1776 	time_t last_read = 0;
   1777 	size_t len;
   1778 	char *ptr, *start;
   1779 	char *this, *next;
   1780 	int count = 0;
   1781 	struct option_info info;
   1782 
   1783 	if ((ptr = map_file(file, &last_read, &len, pprintf, path)) == NULL) {
   1784 		pprintf("Unable to map option file: %s\n", file);
   1785 		return (0);
   1786 	}
   1787 	start = ptr;
   1788 	if (*(ptr+len-1) != '\n') {
   1789 		pprintf("Incomplete last line %s\n", file);
   1790 		(void) munmap(ptr, len);
   1791 		return (0);
   1792 	}
   1793 	*(ptr+len-1) = NULL;
   1794 
   1795 	info.filename = file;
   1796 	info.lineno.end  = 1;
   1797 	info.no_report = 0;
   1798 	info.endchrs = "";
   1799 	while (ptr != NULL && *ptr != NULL) {
   1800 		info.lineno.start  = info.lineno.end;
   1801 		this = get_next_line(ptr, &next, &info.lineno);
   1802 
   1803 		info.line = strdup(this);
   1804 		if (*this != NULL && process_option(this, &info))
   1805 			count++;
   1806 		if (info.line != NULL)
   1807 			free(info.line);
   1808 		ptr = next;
   1809 	}
   1810 	(void) mprotect(start, len, PROT_READ);
   1811 	return (count);
   1812 }
   1813 /*
   1814  * Map a file, if the last_readp points to a time that is less
   1815  * than the last modify time of the file.
   1816  */
   1817 char *
   1818 map_file(char *file, time_t *last_readp, size_t *lenp,
   1819 	void (*pprintf)(char *fmt, ...), const char *path)
   1820 {
   1821 	struct stat buf;
   1822 	char *ptr;
   1823 	char *to_free = NULL;
   1824 	int fd;
   1825 
   1826 	if (path == NULL || strchr(file, '/') != NULL) {
   1827 		if ((fd = open(file, O_RDONLY)) == -1) {
   1828 			OPEN_ERROR(file, O_RDONLY);
   1829 			return (NULL);
   1830 		}
   1831 	} else {
   1832 		char *tmp;
   1833 		char *opaque;
   1834 		char *x;
   1835 		x = strdup(path);
   1836 		tmp = x;
   1837 		/*
   1838 		 * if the diskomizer path starts with a colon then
   1839 		 * start by opening the current directory
   1840 		 */
   1841 		if (*tmp == ':') {
   1842 			fd = open(file, O_RDONLY);
   1843 		} else {
   1844 			fd = -1;
   1845 		}
   1846 		while (fd == -1 &&
   1847 		    (tmp = strtok_r(tmp, ":", &opaque)) != NULL) {
   1848 			to_free = realloc(to_free,
   1849 			    strlen(tmp) + strlen(file) + 2);
   1850 			(void) sprintf(to_free, "%s%s%s",
   1851 			    tmp, *tmp == NULL? "" : "/", file);
   1852 
   1853 			fd = open(to_free, O_RDONLY);
   1854 			tmp = NULL;
   1855 		}
   1856 		free(x);
   1857 		if (fd == -1) {
   1858 			free(to_free);
   1859 			return (NULL);
   1860 		}
   1861 		file = to_free;
   1862 	}
   1863 	if (fstat(fd, &buf) == -1) {
   1864 		pprintf("Can't fstat %s: %s\n", file, strerror(errno));
   1865 		(void) close(fd);
   1866 		free(to_free);
   1867 		return (NULL);
   1868 	}
   1869 
   1870 	if ((buf.st_mode & S_IFDIR) != 0 || buf.st_mtime <= *last_readp ||
   1871 	    buf.st_size == 0) {
   1872 		(void) close(fd);
   1873 		free(to_free);
   1874 		return (NULL);
   1875 	}
   1876 
   1877 	if ((ptr = mmap(0, buf.st_size, PROT_READ|PROT_WRITE, MAP_PRIVATE,
   1878 	    fd, 0)) == MAP_FAILED) {
   1879 		pprintf("Can't mmap %s: %s\n", file, strerror(errno));
   1880 		(void) close(fd);
   1881 		free(to_free);
   1882 		return (NULL);
   1883 	}
   1884 	(void) close(fd);
   1885 	/*LINTED*/
   1886 	*lenp = buf.st_size;
   1887 	*last_readp = buf.st_mtime;
   1888 	if (to_free != NULL)
   1889 		free(to_free);
   1890 	return (ptr);
   1891 }
   1892 static void
   1893 print_all_option_entries(FILE *f)
   1894 {
   1895 	int i;
   1896 
   1897 	fprintf(f, gettext(
   1898 	    "#\n# External options\n#These options were defined but "
   1899 	    "not understood by diskomizer, they could be for use "
   1900 	    "with shared libraries.\n#\n"));
   1901 
   1902 	for (i = 0; i < opt_store.nentries; i++) {
   1903 		if (opt_store.entries[i].info.built_in_option == 0) {
   1904 			fprintf(f, "%.*s=%s\n", opt_store.entries[i].len,
   1905 			    opt_store.entries[i].key,
   1906 			    opt_store.entries[i].value);
   1907 		}
   1908 	}
   1909 }
   1910 #define	OVERFLOW_WARNING(A, B, K) \
   1911 	if ((A)->lv > B) {\
   1912 		plog(LOG_WARNING, \
   1913 			gettext("%s(%lld) overflows %d at %s:%d\n"), \
   1914 			K, (A)->lv, B, \
   1915 				NULL == (A)->info.filename ? \
   1916 				"(Command line)" : (A)->info.filename, \
   1917 				(A)->info.lineno.end); \
   1918 	}
   1919 static struct option_entry *
   1920 get_option_entry(const char *key)
   1921 {
   1922 	struct option_entry e;
   1923 	struct option_entry *r;
   1924 
   1925 	e.key = key;
   1926 	e.len = strlen(key);
   1927 
   1928 	if (opt_store.nentries) {
   1929 		r = ((struct option_entry *)
   1930 		    bsearch(&e, opt_store.entries, opt_store.nentries,
   1931 		    sizeof (struct option_entry), option_cmp));
   1932 		if (r != NULL) {
   1933 			r->info.used = 1;
   1934 		}
   1935 		return (r);
   1936 	} else {
   1937 		return (NULL);
   1938 	}
   1939 }
   1940 static option_status_t
   1941 get_char_option(const char *key, char *out)
   1942 {
   1943 	struct option_entry *e;
   1944 
   1945 	if ((e = get_option_entry(key)) == NULL)
   1946 		return (OPT_FAILED);
   1947 	if (e->info.had_error) {
   1948 		*out = (char)do_maths(0, e->value, NULL, &e->info);
   1949 		return (OPT_FAILED);
   1950 	} else {
   1951 		*out = (char)e->lv;
   1952 		OVERFLOW_WARNING(e, CHAR_MAX, key);
   1953 	}
   1954 	return (OPT_OK);
   1955 
   1956 }
   1957 static option_status_t
   1958 get_bool_option(const char *key, char *out)
   1959 {
   1960 	return (get_char_option(key, out));
   1961 }
   1962 
   1963 static option_status_t
   1964 get_short_option(const char *key, short *out)
   1965 {
   1966 	struct option_entry *e;
   1967 
   1968 	if ((e = get_option_entry(key)) == NULL)
   1969 		return (OPT_FAILED);
   1970 	if (e->info.had_error) {
   1971 		*out = (short)do_maths(0, e->value, NULL, &e->info);
   1972 		return (OPT_FAILED);
   1973 	} else {
   1974 		*out = (short)e->lv;
   1975 		OVERFLOW_WARNING(e, SHRT_MAX, key);
   1976 	}
   1977 	return (OPT_OK);
   1978 
   1979 }
   1980 static option_status_t
   1981 get_int_option(const char *key, int *out)
   1982 {
   1983 	struct option_entry *e;
   1984 
   1985 	if ((e = get_option_entry(key)) == NULL)
   1986 		return (OPT_FAILED);
   1987 	if (e->info.had_error) {
   1988 		*out = (int)do_maths(0, e->value, NULL, &e->info);
   1989 		return (OPT_FAILED);
   1990 	} else {
   1991 		*out = (int)e->lv;
   1992 		OVERFLOW_WARNING(e, INT_MAX, key);
   1993 	}
   1994 	return (OPT_OK);
   1995 }
   1996 static option_status_t
   1997 get_long_option(const char *key, long *out)
   1998 {
   1999 	struct option_entry *e;
   2000 
   2001 	if ((e = get_option_entry(key)) == NULL)
   2002 		return (OPT_FAILED);
   2003 	if (e->info.had_error) {
   2004 		*out = (long)do_maths(0, e->value, NULL, &e->info);
   2005 		return (OPT_FAILED);
   2006 	} else {
   2007 		*out = (long)e->lv;
   2008 		OVERFLOW_WARNING(e, LONG_MAX, key);
   2009 	}
   2010 	return (OPT_OK);
   2011 }
   2012 static option_status_t
   2013 get_long_long_option(const char *key, long long *out)
   2014 {
   2015 	struct option_entry *e;
   2016 
   2017 	if ((e = get_option_entry(key)) == NULL)
   2018 		return (OPT_FAILED);
   2019 	*out = (long long)do_maths(0, e->value, NULL, &e->info);
   2020 	return (OPT_OK);
   2021 }
   2022 static option_status_t
   2023 get_str_option(const char *key, char **out)
   2024 {
   2025 	struct option_entry *e;
   2026 
   2027 	if ((e = get_option_entry(key)) == NULL)
   2028 		return (OPT_FAILED);
   2029 	*out = e->value;
   2030 	return (OPT_OK);
   2031 }
   2032 
   2033 const struct option_ops option_ops = {
   2034 	get_bool_option,
   2035 	get_char_option,
   2036 	get_short_option,
   2037 	get_int_option,
   2038 	get_long_option,
   2039 	get_long_long_option,
   2040 	get_str_option,
   2041 };
   2042 
   2043 const struct option_ops *
   2044 opts_init(void)
   2045 {
   2046 	opt_store.refcount++;
   2047 	return (&option_ops);
   2048 }
   2049 
   2050 /*
   2051  * opts_fini.  Return 0 if this is the last close and all the options have
   2052  * been used.
   2053  * Return a positive int if this is the last close but there are unused
   2054  * options.
   2055  * Return a negative int if this is not the last close.
   2056  */
   2057 int
   2058 opts_fini()
   2059 {
   2060 	if (--opt_store.refcount == 0) {
   2061 		report_back_opts();
   2062 		return (check_and_free_option_store());
   2063 	}
   2064 	return (opt_store.refcount * -1);
   2065 }
   2066 
   2067 int
   2068 load_ilist(struct ilist *ilp, int *vals, int len)
   2069 {
   2070 	int i;
   2071 	if ((ilp->vals = malloc(len * sizeof (uint32_t))) == NULL) {
   2072 		MALLOC_ERROR((long)sizeof (uint32_t));
   2073 		return (1);
   2074 	}
   2075 	ilp->weightings = NULL;
   2076 	ilp->wlen = len;
   2077 	for (i = 0; i < len; i++) {
   2078 		ilp->vals[i] = vals[i];
   2079 	}
   2080 	return (0);
   2081 }
   2082 
   2083 /*
   2084  * Set the diskomizer path.  This is the etc directory based on the root of
   2085  * the installation. If we are relocated then we need to try and work out
   2086  * where it has been installed.
   2087  *
   2088  * We expect the path to be $ROOT/bin/$(uname -i)/binary and from that have
   2089  * the path be $ROOT/etc.
   2090  */
   2091 const char *
   2092 set_diskomizer_path(void)
   2093 {
   2094 	const char *p;
   2095 	char *x;
   2096 	char *wx;
   2097 	int i;
   2098 
   2099 	if ((p = getenv(DISKOMIZER_PATH)) != NULL) {
   2100 		return (p);
   2101 	}
   2102 
   2103 	if ((p = getexecname()) == NULL) {
   2104 		return (def_diskomizer_path);
   2105 	}
   2106 
   2107 	/* Is the path fully qualified? */
   2108 	if (p[0] != '/') {
   2109 		char *wd;
   2110 
   2111 		wd = getcwd(NULL, 1024);
   2112 		if (wd == NULL ||
   2113 		    (x = (char *)malloc(MAX(strlen(wd) + strlen(p) + 3, 5))) ==
   2114 		    NULL) {
   2115 			return (def_diskomizer_path);
   2116 		}
   2117 		sprintf(x, ":%s/%s", wd, p);
   2118 		free(wd);
   2119 	} else {
   2120 		/*
   2121 		 * strlen p must be at least 2, both of which will get
   2122 		 * deleted so we add 4 to give enough space for ":/etc"
   2123 		 * with a traling NUL.
   2124 		 */
   2125 		if ((x = (char *)malloc(strlen(p) + 4)) == NULL) {
   2126 			return (def_diskomizer_path);
   2127 		}
   2128 		sprintf(x, ":%s", p);
   2129 	}
   2130 	/* loose the binary name */
   2131 	wx = strrchr(x, '/');
   2132 	*wx = NULL;
   2133 	/* Now loose 2 directories, "$(uname -i)" and "bin" */
   2134 	for (i = 0; i < 2; i++) {
   2135 		if ((wx = strrchr(x, '/')) != NULL) {
   2136 			*wx = NULL;
   2137 		} else {
   2138 			break;
   2139 		}
   2140 	}
   2141 	strcat(x, "/etc");
   2142 	/* Now reduce the memory used to what is really required */
   2143 	if ((wx = realloc(x, strlen(x) + 1)) != NULL) {
   2144 		x = wx;
   2145 	}
   2146 	return (x);
   2147 }
   2148