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