1/* $NetBSD: units.c,v 1.27 2016/02/05 03:32:49 dholland Exp $ */ 2 3/* 4 * units.c Copyright (c) 1993 by Adrian Mariano (adrian@cam.cornell.edu) 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. The name of the author may not be used to endorse or promote products 12 * derived from this software without specific prior written permission. 13 * Disclaimer: This software is provided by the author "as is". The author 14 * shall not be liable for any damages caused in any way by this software. 15 * 16 * I would appreciate (though I do not require) receiving a copy of any 17 * improvements you might make to this program. 18 */ 19 20#include <assert.h> 21#include <ctype.h> 22#include <err.h> 23#include <float.h> 24#include <stdio.h> 25#include <string.h> 26#include <stdlib.h> 27#include <unistd.h> 28 29#include "pathnames.h" 30 31#define VERSION "1.0" 32 33#ifndef UNITSFILE 34#define UNITSFILE _PATH_UNITSLIB 35#endif 36 37#define MAXUNITS 1000 38#define MAXPREFIXES 50 39 40#define MAXSUBUNITS 500 41 42#define PRIMITIVECHAR '!' 43 44static int precision = 8; /* for printf with "%.*g" format */ 45 46static const char *errprefix = NULL; /* if not NULL, then prepend this 47 * to error messages and send them to 48 * stdout instead of stderr. 49 */ 50 51static const char *powerstring = "^"; 52 53static struct { 54 const char *uname; 55 const char *uval; 56} unittable[MAXUNITS]; 57 58struct unittype { 59 const char *numerator[MAXSUBUNITS]; 60 const char *denominator[MAXSUBUNITS]; 61 double factor; 62}; 63 64struct { 65 const char *prefixname; 66 const char *prefixval; 67} prefixtable[MAXPREFIXES]; 68 69 70static const char *NULLUNIT = ""; 71 72static int unitcount; 73static int prefixcount; 74 75 76static int addsubunit(const char *[], const char *); 77static int addunit(struct unittype *, const char *, int); 78static void cancelunit(struct unittype *); 79static int compare(const void *, const void *); 80static int compareproducts(const char **, const char **); 81static int compareunits(struct unittype *, struct unittype *); 82static int compareunitsreciprocal(struct unittype *, struct unittype *); 83static int completereduce(struct unittype *); 84static void initializeunit(struct unittype *); 85static void readerror(int); 86static void readunits(const char *); 87static int reduceproduct(struct unittype *, int); 88static int reduceunit(struct unittype *); 89static void showanswer(struct unittype *, struct unittype *); 90static void showunit(struct unittype *); 91static void sortunit(struct unittype *); 92__dead static void usage(void); 93static void zeroerror(void); 94static char *dupstr(const char *); 95static const char *lookupunit(const char *); 96 97static char * 98dupstr(const char *str) 99{ 100 char *ret; 101 102 ret = strdup(str); 103 if (!ret) 104 err(3, "Memory allocation error"); 105 return (ret); 106} 107 108 109static __printflike(1, 2) void 110mywarnx(const char *fmt, ...) 111{ 112 va_list args; 113 114 va_start(args, fmt); 115 if (errprefix) { 116 /* warn to stdout, with errprefix prepended */ 117 printf("%s", errprefix); 118 vprintf(fmt, args); 119 printf("%s", "\n"); 120 } else { 121 /* warn to stderr */ 122 vwarnx(fmt, args); 123 } 124 va_end(args); 125} 126 127static void 128readerror(int linenum) 129{ 130 mywarnx("Error in units file '%s' line %d", UNITSFILE, linenum); 131} 132 133 134static void 135readunits(const char *userfile) 136{ 137 FILE *unitfile; 138 char line[80], *lineptr; 139 int len, linenum, i, isdup; 140 141 unitcount = 0; 142 linenum = 0; 143 144 if (userfile) { 145 unitfile = fopen(userfile, "rt"); 146 if (!unitfile) 147 err(1, "Unable to open units file '%s'", userfile); 148 } 149 else { 150 unitfile = fopen(UNITSFILE, "rt"); 151 if (!unitfile) { 152 char *direc, *env; 153 char filename[1000]; 154 char separator[2]; 155 156 env = getenv("PATH"); 157 if (env) { 158 if (strchr(env, ';')) 159 strlcpy(separator, ";", 160 sizeof(separator)); 161 else 162 strlcpy(separator, ":", 163 sizeof(separator)); 164 direc = strtok(env, separator); 165 while (direc) { 166 strlcpy(filename, "", sizeof(filename)); 167 strlcat(filename, direc, 168 sizeof(filename)); 169 strlcat(filename, "/", 170 sizeof(filename)); 171 strlcat(filename, UNITSFILE, 172 sizeof(filename)); 173 unitfile = fopen(filename, "rt"); 174 if (unitfile) 175 break; 176 direc = strtok(NULL, separator); 177 } 178 } 179 if (!unitfile) 180 errx(1, "Can't find units file '%s'", 181 UNITSFILE); 182 } 183 } 184 while (!feof(unitfile)) { 185 if (!fgets(line, 79, unitfile)) 186 break; 187 linenum++; 188 lineptr = line; 189 if (*lineptr == '/') 190 continue; 191 lineptr += strspn(lineptr, " \n\t"); 192 len = strcspn(lineptr, " \n\t"); 193 lineptr[len] = 0; 194 if (!strlen(lineptr)) 195 continue; 196 if (lineptr[strlen(lineptr) - 1] == '-') { /* it's a prefix */ 197 if (prefixcount == MAXPREFIXES) { 198 mywarnx( 199 "Memory for prefixes exceeded in line %d", 200 linenum); 201 continue; 202 } 203 lineptr[strlen(lineptr) - 1] = 0; 204 for (isdup = 0, i = 0; i < prefixcount; i++) { 205 if (!strcmp(prefixtable[i].prefixname, 206 lineptr)) { 207 isdup = 1; 208 break; 209 } 210 } 211 if (isdup) { 212 mywarnx( 213 "Redefinition of prefix '%s' on line %d ignored", 214 lineptr, linenum); 215 continue; 216 } 217 prefixtable[prefixcount].prefixname = dupstr(lineptr); 218 lineptr += len + 1; 219 if (!strlen(lineptr)) { 220 readerror(linenum); 221 continue; 222 } 223 lineptr += strspn(lineptr, " \n\t"); 224 len = strcspn(lineptr, "\n\t"); 225 lineptr[len] = 0; 226 prefixtable[prefixcount++].prefixval = dupstr(lineptr); 227 } 228 else { /* it's not a prefix */ 229 if (unitcount == MAXUNITS) { 230 mywarnx("Memory for units exceeded in line %d", 231 linenum); 232 continue; 233 } 234 for (isdup = 0, i = 0; i < unitcount; i++) { 235 if (!strcmp(unittable[i].uname, lineptr)) { 236 isdup = 1; 237 break; 238 } 239 } 240 if (isdup) { 241 mywarnx( 242 "Redefinition of unit '%s' on line %d ignored", 243 lineptr, linenum); 244 continue; 245 } 246 unittable[unitcount].uname = dupstr(lineptr); 247 lineptr += len + 1; 248 lineptr += strspn(lineptr, " \n\t"); 249 if (!strlen(lineptr)) { 250 readerror(linenum); 251 continue; 252 } 253 len = strcspn(lineptr, "\n\t"); 254 lineptr[len] = 0; 255 unittable[unitcount++].uval = dupstr(lineptr); 256 } 257 } 258 fclose(unitfile); 259} 260 261static void 262initializeunit(struct unittype * theunit) 263{ 264 theunit->factor = 1.0; 265 theunit->numerator[0] = theunit->denominator[0] = NULL; 266} 267 268static int 269addsubunit(const char *product[], const char *toadd) 270{ 271 const char **ptr; 272 273 for (ptr = product; *ptr && *ptr != NULLUNIT; ptr++); 274 if (ptr >= product + MAXSUBUNITS) { 275 mywarnx("Memory overflow in unit reduction"); 276 return 1; 277 } 278 if (!*ptr) 279 *(ptr + 1) = 0; 280 *ptr = dupstr(toadd); 281 return 0; 282} 283 284static void 285showunit(struct unittype * theunit) 286{ 287 const char **ptr; 288 int printedslash; 289 int counter = 1; 290 291 printf("\t%.*g", precision, theunit->factor); 292 for (ptr = theunit->numerator; *ptr; ptr++) { 293 if (ptr > theunit->numerator && **ptr && 294 !strcmp(*ptr, *(ptr - 1))) 295 counter++; 296 else { 297 if (counter > 1) 298 printf("%s%d", powerstring, counter); 299 if (**ptr) 300 printf(" %s", *ptr); 301 counter = 1; 302 } 303 } 304 if (counter > 1) 305 printf("%s%d", powerstring, counter); 306 counter = 1; 307 printedslash = 0; 308 for (ptr = theunit->denominator; *ptr; ptr++) { 309 if (ptr > theunit->denominator && **ptr && 310 !strcmp(*ptr, *(ptr - 1))) 311 counter++; 312 else { 313 if (counter > 1) 314 printf("%s%d", powerstring, counter); 315 if (**ptr) { 316 if (!printedslash) 317 printf(" /"); 318 printedslash = 1; 319 printf(" %s", *ptr); 320 } 321 counter = 1; 322 } 323 } 324 if (counter > 1) 325 printf("%s%d", powerstring, counter); 326 printf("\n"); 327} 328 329static void 330zeroerror(void) 331{ 332 mywarnx("Unit reduces to zero"); 333} 334 335/* 336 Adds the specified string to the unit. 337 Flip is 0 for adding normally, 1 for adding reciprocal. 338 339 Returns 0 for successful addition, nonzero on error. 340*/ 341 342static int 343addunit(struct unittype * theunit, const char *toadd, int flip) 344{ 345 char *scratch, *savescr; 346 char *item; 347 char *divider, *slash; 348 char *minus; 349 size_t pos, len; 350 int doingtop; 351 352 savescr = scratch = dupstr(toadd); 353 354 /* 355 * "foot-pound" is the same as "foot pound". But don't 356 * trash minus signs on numbers. 357 * 358 * 20160204 dholland: this used to let through only minus 359 * signs at the beginning of the string or in the middle of a 360 * floating constant (e.g. 3.6e-5), and a minus sign at the 361 * beginning of the string failed further on. I have changed 362 * it so any minus sign before a digit (or decimal point) is 363 * treated as going with that digit. 364 * 365 * Note that this changed the interpretation of certain 366 * marginally valid inputs like "3 N-5 s"; that used to be 367 * interpreted as "3 N 5 s" or 15 N s, but now it reads as 368 * "3 N -5 s" or -15 N s. However, it also makes negative 369 * exponents on units work, which used to be silently trashed. 370 */ 371 for (minus = scratch + 1; *minus; minus++) { 372 if (*minus != '-') { 373 continue; 374 } 375 if (strchr(".0123456789", *(minus + 1))) { 376 continue; 377 } 378 *minus = ' '; 379 } 380 381 /* Process up to the next / in one go. */ 382 383 slash = strchr(scratch, '/'); 384 if (slash) 385 *slash = 0; 386 doingtop = 1; 387 do { 388 item = strtok(scratch, " *\t\n/"); 389 while (item) { 390 if ((*item == '-' && strchr("0123456789.", *(item+1))) 391 || strchr("0123456789.", *item)) { 392 393 /* item starts with a number */ 394 char *endptr; 395 double num; 396 397 divider = strchr(item, '|'); 398 if (divider) { 399 *divider = 0; 400 num = strtod(item, &endptr); 401 if (!num) { 402 zeroerror(); 403 return 1; 404 } 405 if (endptr != divider) { 406 /* "6foo|2" is an error */ 407 mywarnx("Junk before '|'"); 408 return 1; 409 } 410 if (doingtop ^ flip) 411 theunit->factor *= num; 412 else 413 theunit->factor /= num; 414 num = strtod(divider + 1, &endptr); 415 if (!num) { 416 zeroerror(); 417 return 1; 418 } 419 if (doingtop ^ flip) 420 theunit->factor /= num; 421 else 422 theunit->factor *= num; 423 if (*endptr) { 424 /* "6|2foo" is like "6|2 foo" */ 425 item = endptr; 426 continue; 427 } 428 } 429 else { 430 num = strtod(item, &endptr); 431 if (!num) { 432 zeroerror(); 433 return 1; 434 } 435 if (doingtop ^ flip) 436 theunit->factor *= num; 437 else 438 theunit->factor /= num; 439 if (*endptr) { 440 /* "3foo" is like "3 foo" */ 441 item = endptr; 442 continue; 443 } 444 } 445 } 446 else { /* item is not a number */ 447 int repeat = 1; 448 int flipthis = 0; 449 450 pos = len = strlen(item); 451 assert(pos > 0); 452 while (strchr("0123456789", item[pos - 1])) { 453 pos--; 454 /* string began with non-digit */ 455 assert(pos > 0); 456 } 457 if (pos < len) { 458 if (pos > 1 && item[pos - 1] == '-' && 459 item[pos - 2] == '^') { 460 /* allow negative exponents */ 461 pos--; 462 } 463 /* have an exponent */ 464 repeat = strtol(item + pos, NULL, 10); 465 item[pos] = 0; 466 if (repeat == 0) { 467 /* not really the right msg */ 468 zeroerror(); 469 return 1; 470 } 471 if (repeat < 0) { 472 flipthis = 1; 473 repeat = -repeat; 474 } 475 } 476 flipthis ^= doingtop ^ flip; 477 for (; repeat; repeat--) 478 if (addsubunit(flipthis ? theunit->numerator : theunit->denominator, item)) 479 return 1; 480 } 481 item = strtok(NULL, " *\t/\n"); 482 } 483 doingtop--; 484 if (slash) { 485 scratch = slash + 1; 486 } 487 else 488 doingtop--; 489 } while (doingtop >= 0); 490 free(savescr); 491 return 0; 492} 493 494static int 495compare(const void *item1, const void *item2) 496{ 497 return strcmp(*(const char * const *) item1, 498 *(const char * const *) item2); 499} 500 501static void 502sortunit(struct unittype * theunit) 503{ 504 const char **ptr; 505 int count; 506 507 for (count = 0, ptr = theunit->numerator; *ptr; ptr++, count++); 508 qsort(theunit->numerator, count, sizeof(char *), compare); 509 for (count = 0, ptr = theunit->denominator; *ptr; ptr++, count++); 510 qsort(theunit->denominator, count, sizeof(char *), compare); 511} 512 513static void 514cancelunit(struct unittype * theunit) 515{ 516 const char **den, **num; 517 int comp; 518 519 den = theunit->denominator; 520 num = theunit->numerator; 521 522 while (*num && *den) { 523 comp = strcmp(*den, *num); 524 if (!comp) { 525/* if (*den!=NULLUNIT) free(*den); 526 if (*num!=NULLUNIT) free(*num);*/ 527 *den++ = NULLUNIT; 528 *num++ = NULLUNIT; 529 } 530 else if (comp < 0) 531 den++; 532 else 533 num++; 534 } 535} 536 537 538 539 540/* 541 Looks up the definition for the specified unit. 542 Returns a pointer to the definition or a null pointer 543 if the specified unit does not appear in the units table. 544*/ 545 546static char buffer[100]; /* buffer for lookupunit answers with 547 prefixes */ 548 549static const char * 550lookupunit(const char *unit) 551{ 552 int i; 553 char *copy; 554 555 for (i = 0; i < unitcount; i++) { 556 if (!strcmp(unittable[i].uname, unit)) 557 return unittable[i].uval; 558 } 559 560 if (unit[strlen(unit) - 1] == '^') { 561 copy = dupstr(unit); 562 copy[strlen(copy) - 1] = 0; 563 for (i = 0; i < unitcount; i++) { 564 if (!strcmp(unittable[i].uname, copy)) { 565 strlcpy(buffer, copy, sizeof(buffer)); 566 free(copy); 567 return buffer; 568 } 569 } 570 free(copy); 571 } 572 if (unit[strlen(unit) - 1] == 's') { 573 copy = dupstr(unit); 574 copy[strlen(copy) - 1] = 0; 575 for (i = 0; i < unitcount; i++) { 576 if (!strcmp(unittable[i].uname, copy)) { 577 strlcpy(buffer, copy, sizeof(buffer)); 578 free(copy); 579 return buffer; 580 } 581 } 582 if (copy[strlen(copy) - 1] == 'e') { 583 copy[strlen(copy) - 1] = 0; 584 for (i = 0; i < unitcount; i++) { 585 if (!strcmp(unittable[i].uname, copy)) { 586 strlcpy(buffer, copy, sizeof(buffer)); 587 free(copy); 588 return buffer; 589 } 590 } 591 } 592 free(copy); 593 } 594 for (i = 0; i < prefixcount; i++) { 595 if (!strncmp(prefixtable[i].prefixname, unit, 596 strlen(prefixtable[i].prefixname))) { 597 unit += strlen(prefixtable[i].prefixname); 598 if (!strlen(unit) || lookupunit(unit)) { 599 strlcpy(buffer, prefixtable[i].prefixval, 600 sizeof(buffer)); 601 strlcat(buffer, " ", sizeof(buffer)); 602 strlcat(buffer, unit, sizeof(buffer)); 603 return buffer; 604 } 605 } 606 } 607 return 0; 608} 609 610 611 612/* 613 reduces a product of symbolic units to primitive units. 614 The three low bits are used to return flags: 615 616 bit 0 (1) set on if reductions were performed without error. 617 bit 1 (2) set on if no reductions are performed. 618 bit 2 (4) set on if an unknown unit is discovered. 619*/ 620 621 622#define ERROR 4 623 624static int 625reduceproduct(struct unittype * theunit, int flip) 626{ 627 628 const char *toadd; 629 const char **product; 630 int didsomething = 2; 631 632 if (flip) 633 product = theunit->denominator; 634 else 635 product = theunit->numerator; 636 637 for (; *product; product++) { 638 639 for (;;) { 640 if (!strlen(*product)) 641 break; 642 toadd = lookupunit(*product); 643 if (!toadd) { 644 mywarnx("Unknown unit '%s'", *product); 645 return ERROR; 646 } 647 if (strchr(toadd, PRIMITIVECHAR)) 648 break; 649 didsomething = 1; 650 if (*product != NULLUNIT) { 651 free(__UNCONST(*product)); 652 *product = NULLUNIT; 653 } 654 if (addunit(theunit, toadd, flip)) 655 return ERROR; 656 } 657 } 658 return didsomething; 659} 660 661 662/* 663 Reduces numerator and denominator of the specified unit. 664 Returns 0 on success, or 1 on unknown unit error. 665*/ 666 667static int 668reduceunit(struct unittype * theunit) 669{ 670 int ret; 671 672 ret = 1; 673 while (ret & 1) { 674 ret = reduceproduct(theunit, 0) | reduceproduct(theunit, 1); 675 if (ret & 4) 676 return 1; 677 } 678 return 0; 679} 680 681static int 682compareproducts(const char **one, const char **two) 683{ 684 while (*one || *two) { 685 if (!*one && *two != NULLUNIT) 686 return 1; 687 if (!*two && *one != NULLUNIT) 688 return 1; 689 if (*one == NULLUNIT) 690 one++; 691 else if (*two == NULLUNIT) 692 two++; 693 else if (*one && *two && strcmp(*one, *two)) 694 return 1; 695 else 696 one++, two++; 697 } 698 return 0; 699} 700 701 702/* Return zero if units are compatible, nonzero otherwise */ 703 704static int 705compareunits(struct unittype * first, struct unittype * second) 706{ 707 return 708 compareproducts(first->numerator, second->numerator) || 709 compareproducts(first->denominator, second->denominator); 710} 711 712static int 713compareunitsreciprocal(struct unittype * first, struct unittype * second) 714{ 715 return 716 compareproducts(first->numerator, second->denominator) || 717 compareproducts(first->denominator, second->numerator); 718} 719 720 721static int 722completereduce(struct unittype * unit) 723{ 724 if (reduceunit(unit)) 725 return 1; 726 sortunit(unit); 727 cancelunit(unit); 728 return 0; 729} 730 731 732static void 733showanswer(struct unittype * have, struct unittype * want) 734{ 735 if (compareunits(have, want)) { 736 if (compareunitsreciprocal(have, want)) { 737 printf("conformability error\n"); 738 showunit(have); 739 showunit(want); 740 } else { 741 printf("\treciprocal conversion\n"); 742 printf("\t* %.*g\n\t/ %.*g\n", 743 precision, 1 / (have->factor * want->factor), 744 precision, want->factor * have->factor); 745 } 746 } 747 else 748 printf("\t* %.*g\n\t/ %.*g\n", 749 precision, have->factor / want->factor, 750 precision, want->factor / have->factor); 751} 752 753static int 754listunits(int expand) 755{ 756 struct unittype theunit; 757 const char *thename; 758 const char *thedefn; 759 int errors = 0; 760 int i; 761 int printexpansion; 762 763 /* 764 * send error and warning messages to stdout, 765 * and make them look like comments. 766 */ 767 errprefix = "/ "; 768 769#if 0 /* debug */ 770 printf("/ expand=%d precision=%d unitcount=%d prefixcount=%d\n", 771 expand, precision, unitcount, prefixcount); 772#endif 773 774 /* 1. Dump all primitive units, e.g. "m !a!", "kg !b!", ... */ 775 printf("/ Primitive units\n"); 776 for (i = 0; i < unitcount; i++) { 777 thename = unittable[i].uname; 778 thedefn = unittable[i].uval; 779 if (thedefn[0] == PRIMITIVECHAR) { 780 printf("%s\t%s\n", thename, thedefn); 781 } 782 } 783 784 /* 2. Dump all prefixes, e.g. "yotta- 1e24", "zetta- 1e21", ... */ 785 printf("/ Prefixes\n"); 786 for (i = 0; i < prefixcount; i++) { 787 printexpansion = expand; 788 thename = prefixtable[i].prefixname; 789 thedefn = prefixtable[i].prefixval; 790 if (expand) { 791 /* 792 * prefix names are sometimes identical to unit 793 * names, so we have to expand thedefn instead of 794 * expanding thename. 795 */ 796 initializeunit(&theunit); 797 if (addunit(&theunit, thedefn, 0) != 0 798 || completereduce(&theunit) != 0) { 799 errors++; 800 printexpansion = 0; 801 mywarnx("Error in prefix '%s-'", thename); 802 } 803 } 804 if (printexpansion) { 805 printf("%s-", thename); 806 showunit(&theunit); 807 } else 808 printf("%s-\t%s\n", thename, thedefn); 809 } 810 811 /* 3. Dump all other units. */ 812 printf("/ Other units\n"); 813 for (i = 0; i < unitcount; i++) { 814 printexpansion = expand; 815 thename = unittable[i].uname; 816 thedefn = unittable[i].uval; 817 if (thedefn[0] == PRIMITIVECHAR) 818 continue; 819 if (expand) { 820 /* 821 * expand thename, not thedefn, so that 822 * we can catch errors in the name itself. 823 * e.g. a name that contains a hyphen 824 * will be interpreted as multiplication. 825 */ 826 initializeunit(&theunit); 827 if (addunit(&theunit, thename, 0) != 0 828 || completereduce(&theunit) != 0) { 829 errors++; 830 printexpansion = 0; 831 mywarnx("Error in unit '%s'", thename); 832 } 833 } 834 if (printexpansion) { 835 printf("%s", thename); 836 showunit(&theunit); 837 } else 838 printf("%s\t%s\n", thename, thedefn); 839 } 840 841 if (errors) 842 mywarnx("Definitions with errors: %d", errors); 843 return (errors ? 1 : 0); 844} 845 846static void 847usage(void) 848{ 849 fprintf(stderr, 850 "\nunits [-Llqv] [-f filename] [[count] from-unit to-unit]\n"); 851 fprintf(stderr, "\n -f specify units file\n"); 852 fprintf(stderr, " -L list units in standardized base units\n"); 853 fprintf(stderr, " -l list units\n"); 854 fprintf(stderr, " -q suppress prompting (quiet)\n"); 855 fprintf(stderr, " -v print version number\n"); 856 exit(3); 857} 858 859int 860main(int argc, char **argv) 861{ 862 863 struct unittype have, want; 864 char havestr[81], wantstr[81]; 865 int optchar; 866 const char *userfile = 0; 867 int list = 0, listexpand = 0; 868 int quiet = 0; 869 870 while ((optchar = getopt(argc, argv, "lLvqf:")) != -1) { 871 switch (optchar) { 872 case 'l': 873 list = 1; 874 break; 875 case 'L': 876 list = 1; 877 listexpand = 1; 878 precision = DBL_DIG; 879 break; 880 case 'f': 881 userfile = optarg; 882 break; 883 case 'q': 884 quiet = 1; 885 break; 886 case 'v': 887 fprintf(stderr, "\n units version %s Copyright (c) 1993 by Adrian Mariano\n", 888 VERSION); 889 fprintf(stderr, " This program may be freely distributed\n"); 890 usage(); 891 default: 892 usage(); 893 break; 894 } 895 } 896 897 argc -= optind; 898 argv += optind; 899 900 if ((argc != 3 && argc != 2 && argc != 0) 901 || (list && argc != 0)) 902 usage(); 903 904 if (list) 905 errprefix = "/ "; /* set this before reading the file */ 906 907 readunits(userfile); 908 909 if (list) 910 return listunits(listexpand); 911 912 if (argc == 3) { 913 strlcpy(havestr, argv[0], sizeof(havestr)); 914 strlcat(havestr, " ", sizeof(havestr)); 915 strlcat(havestr, argv[1], sizeof(havestr)); 916 argc--; 917 argv++; 918 argv[0] = havestr; 919 } 920 921 if (argc == 2) { 922 strlcpy(havestr, argv[0], sizeof(havestr)); 923 strlcpy(wantstr, argv[1], sizeof(wantstr)); 924 initializeunit(&have); 925 addunit(&have, havestr, 0); 926 completereduce(&have); 927 initializeunit(&want); 928 addunit(&want, wantstr, 0); 929 completereduce(&want); 930 showanswer(&have, &want); 931 } 932 else { 933 if (!quiet) 934 printf("%d units, %d prefixes\n\n", unitcount, 935 prefixcount); 936 for (;;) { 937 do { 938 initializeunit(&have); 939 if (!quiet) 940 printf("You have: "); 941 if (!fgets(havestr, 80, stdin)) { 942 if (!quiet) 943 putchar('\n'); 944 exit(0); 945 } 946 } while (addunit(&have, havestr, 0) || 947 completereduce(&have)); 948 do { 949 initializeunit(&want); 950 if (!quiet) 951 printf("You want: "); 952 if (!fgets(wantstr, 80, stdin)) { 953 if (!quiet) 954 putchar('\n'); 955 exit(0); 956 } 957 } while (addunit(&want, wantstr, 0) || 958 completereduce(&want)); 959 showanswer(&have, &want); 960 } 961 } 962 return (0); 963} 964