units.c revision 1.26 1 /* $NetBSD: units.c,v 1.26 2016/02/05 03:30:08 dholland Exp $ */
2
3 /*
4 * units.c Copyright (c) 1993 by Adrian Mariano (adrian (at) 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
44 static int precision = 8; /* for printf with "%.*g" format */
45
46 static 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
51 static const char *powerstring = "^";
52
53 static struct {
54 const char *uname;
55 const char *uval;
56 } unittable[MAXUNITS];
57
58 struct unittype {
59 const char *numerator[MAXSUBUNITS];
60 const char *denominator[MAXSUBUNITS];
61 double factor;
62 };
63
64 struct {
65 const char *prefixname;
66 const char *prefixval;
67 } prefixtable[MAXPREFIXES];
68
69
70 static const char *NULLUNIT = "";
71
72 static int unitcount;
73 static int prefixcount;
74
75
76 static int addsubunit(const char *[], const char *);
77 static int addunit(struct unittype *, const char *, int);
78 static void cancelunit(struct unittype *);
79 static int compare(const void *, const void *);
80 static int compareproducts(const char **, const char **);
81 static int compareunits(struct unittype *, struct unittype *);
82 static int compareunitsreciprocal(struct unittype *, struct unittype *);
83 static int completereduce(struct unittype *);
84 static void initializeunit(struct unittype *);
85 static void readerror(int);
86 static void readunits(const char *);
87 static int reduceproduct(struct unittype *, int);
88 static int reduceunit(struct unittype *);
89 static void showanswer(struct unittype *, struct unittype *);
90 static void showunit(struct unittype *);
91 static void sortunit(struct unittype *);
92 __dead static void usage(void);
93 static void zeroerror(void);
94 static char *dupstr(const char *);
95 static const char *lookupunit(const char *);
96
97 static char *
98 dupstr(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
109 static __printflike(1, 2) void
110 mywarnx(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
127 static void
128 readerror(int linenum)
129 {
130 mywarnx("Error in units file '%s' line %d", UNITSFILE, linenum);
131 }
132
133
134 static void
135 readunits(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
261 static void
262 initializeunit(struct unittype * theunit)
263 {
264 theunit->factor = 1.0;
265 theunit->numerator[0] = theunit->denominator[0] = NULL;
266 }
267
268 static int
269 addsubunit(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
284 static void
285 showunit(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
329 static void
330 zeroerror(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
342 static int
343 addunit(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 > 0 && item[pos - 1] == '-') {
459 /* allow negative exponents */
460 pos--;
461 }
462 /* have an exponent */
463 repeat = strtol(item + pos, NULL, 10);
464 item[pos] = 0;
465 if (repeat == 0) {
466 /* not really the right msg */
467 zeroerror();
468 return 1;
469 }
470 if (repeat < 0) {
471 flipthis = 1;
472 repeat = -repeat;
473 }
474 }
475 flipthis ^= doingtop ^ flip;
476 for (; repeat; repeat--)
477 if (addsubunit(flipthis ? theunit->numerator : theunit->denominator, item))
478 return 1;
479 }
480 item = strtok(NULL, " *\t/\n");
481 }
482 doingtop--;
483 if (slash) {
484 scratch = slash + 1;
485 }
486 else
487 doingtop--;
488 } while (doingtop >= 0);
489 free(savescr);
490 return 0;
491 }
492
493 static int
494 compare(const void *item1, const void *item2)
495 {
496 return strcmp(*(const char * const *) item1,
497 *(const char * const *) item2);
498 }
499
500 static void
501 sortunit(struct unittype * theunit)
502 {
503 const char **ptr;
504 int count;
505
506 for (count = 0, ptr = theunit->numerator; *ptr; ptr++, count++);
507 qsort(theunit->numerator, count, sizeof(char *), compare);
508 for (count = 0, ptr = theunit->denominator; *ptr; ptr++, count++);
509 qsort(theunit->denominator, count, sizeof(char *), compare);
510 }
511
512 static void
513 cancelunit(struct unittype * theunit)
514 {
515 const char **den, **num;
516 int comp;
517
518 den = theunit->denominator;
519 num = theunit->numerator;
520
521 while (*num && *den) {
522 comp = strcmp(*den, *num);
523 if (!comp) {
524 /* if (*den!=NULLUNIT) free(*den);
525 if (*num!=NULLUNIT) free(*num);*/
526 *den++ = NULLUNIT;
527 *num++ = NULLUNIT;
528 }
529 else if (comp < 0)
530 den++;
531 else
532 num++;
533 }
534 }
535
536
537
538
539 /*
540 Looks up the definition for the specified unit.
541 Returns a pointer to the definition or a null pointer
542 if the specified unit does not appear in the units table.
543 */
544
545 static char buffer[100]; /* buffer for lookupunit answers with
546 prefixes */
547
548 static const char *
549 lookupunit(const char *unit)
550 {
551 int i;
552 char *copy;
553
554 for (i = 0; i < unitcount; i++) {
555 if (!strcmp(unittable[i].uname, unit))
556 return unittable[i].uval;
557 }
558
559 if (unit[strlen(unit) - 1] == '^') {
560 copy = dupstr(unit);
561 copy[strlen(copy) - 1] = 0;
562 for (i = 0; i < unitcount; i++) {
563 if (!strcmp(unittable[i].uname, copy)) {
564 strlcpy(buffer, copy, sizeof(buffer));
565 free(copy);
566 return buffer;
567 }
568 }
569 free(copy);
570 }
571 if (unit[strlen(unit) - 1] == 's') {
572 copy = dupstr(unit);
573 copy[strlen(copy) - 1] = 0;
574 for (i = 0; i < unitcount; i++) {
575 if (!strcmp(unittable[i].uname, copy)) {
576 strlcpy(buffer, copy, sizeof(buffer));
577 free(copy);
578 return buffer;
579 }
580 }
581 if (copy[strlen(copy) - 1] == 'e') {
582 copy[strlen(copy) - 1] = 0;
583 for (i = 0; i < unitcount; i++) {
584 if (!strcmp(unittable[i].uname, copy)) {
585 strlcpy(buffer, copy, sizeof(buffer));
586 free(copy);
587 return buffer;
588 }
589 }
590 }
591 free(copy);
592 }
593 for (i = 0; i < prefixcount; i++) {
594 if (!strncmp(prefixtable[i].prefixname, unit,
595 strlen(prefixtable[i].prefixname))) {
596 unit += strlen(prefixtable[i].prefixname);
597 if (!strlen(unit) || lookupunit(unit)) {
598 strlcpy(buffer, prefixtable[i].prefixval,
599 sizeof(buffer));
600 strlcat(buffer, " ", sizeof(buffer));
601 strlcat(buffer, unit, sizeof(buffer));
602 return buffer;
603 }
604 }
605 }
606 return 0;
607 }
608
609
610
611 /*
612 reduces a product of symbolic units to primitive units.
613 The three low bits are used to return flags:
614
615 bit 0 (1) set on if reductions were performed without error.
616 bit 1 (2) set on if no reductions are performed.
617 bit 2 (4) set on if an unknown unit is discovered.
618 */
619
620
621 #define ERROR 4
622
623 static int
624 reduceproduct(struct unittype * theunit, int flip)
625 {
626
627 const char *toadd;
628 const char **product;
629 int didsomething = 2;
630
631 if (flip)
632 product = theunit->denominator;
633 else
634 product = theunit->numerator;
635
636 for (; *product; product++) {
637
638 for (;;) {
639 if (!strlen(*product))
640 break;
641 toadd = lookupunit(*product);
642 if (!toadd) {
643 mywarnx("Unknown unit '%s'", *product);
644 return ERROR;
645 }
646 if (strchr(toadd, PRIMITIVECHAR))
647 break;
648 didsomething = 1;
649 if (*product != NULLUNIT) {
650 free(__UNCONST(*product));
651 *product = NULLUNIT;
652 }
653 if (addunit(theunit, toadd, flip))
654 return ERROR;
655 }
656 }
657 return didsomething;
658 }
659
660
661 /*
662 Reduces numerator and denominator of the specified unit.
663 Returns 0 on success, or 1 on unknown unit error.
664 */
665
666 static int
667 reduceunit(struct unittype * theunit)
668 {
669 int ret;
670
671 ret = 1;
672 while (ret & 1) {
673 ret = reduceproduct(theunit, 0) | reduceproduct(theunit, 1);
674 if (ret & 4)
675 return 1;
676 }
677 return 0;
678 }
679
680 static int
681 compareproducts(const char **one, const char **two)
682 {
683 while (*one || *two) {
684 if (!*one && *two != NULLUNIT)
685 return 1;
686 if (!*two && *one != NULLUNIT)
687 return 1;
688 if (*one == NULLUNIT)
689 one++;
690 else if (*two == NULLUNIT)
691 two++;
692 else if (*one && *two && strcmp(*one, *two))
693 return 1;
694 else
695 one++, two++;
696 }
697 return 0;
698 }
699
700
701 /* Return zero if units are compatible, nonzero otherwise */
702
703 static int
704 compareunits(struct unittype * first, struct unittype * second)
705 {
706 return
707 compareproducts(first->numerator, second->numerator) ||
708 compareproducts(first->denominator, second->denominator);
709 }
710
711 static int
712 compareunitsreciprocal(struct unittype * first, struct unittype * second)
713 {
714 return
715 compareproducts(first->numerator, second->denominator) ||
716 compareproducts(first->denominator, second->numerator);
717 }
718
719
720 static int
721 completereduce(struct unittype * unit)
722 {
723 if (reduceunit(unit))
724 return 1;
725 sortunit(unit);
726 cancelunit(unit);
727 return 0;
728 }
729
730
731 static void
732 showanswer(struct unittype * have, struct unittype * want)
733 {
734 if (compareunits(have, want)) {
735 if (compareunitsreciprocal(have, want)) {
736 printf("conformability error\n");
737 showunit(have);
738 showunit(want);
739 } else {
740 printf("\treciprocal conversion\n");
741 printf("\t* %.*g\n\t/ %.*g\n",
742 precision, 1 / (have->factor * want->factor),
743 precision, want->factor * have->factor);
744 }
745 }
746 else
747 printf("\t* %.*g\n\t/ %.*g\n",
748 precision, have->factor / want->factor,
749 precision, want->factor / have->factor);
750 }
751
752 static int
753 listunits(int expand)
754 {
755 struct unittype theunit;
756 const char *thename;
757 const char *thedefn;
758 int errors = 0;
759 int i;
760 int printexpansion;
761
762 /*
763 * send error and warning messages to stdout,
764 * and make them look like comments.
765 */
766 errprefix = "/ ";
767
768 #if 0 /* debug */
769 printf("/ expand=%d precision=%d unitcount=%d prefixcount=%d\n",
770 expand, precision, unitcount, prefixcount);
771 #endif
772
773 /* 1. Dump all primitive units, e.g. "m !a!", "kg !b!", ... */
774 printf("/ Primitive units\n");
775 for (i = 0; i < unitcount; i++) {
776 thename = unittable[i].uname;
777 thedefn = unittable[i].uval;
778 if (thedefn[0] == PRIMITIVECHAR) {
779 printf("%s\t%s\n", thename, thedefn);
780 }
781 }
782
783 /* 2. Dump all prefixes, e.g. "yotta- 1e24", "zetta- 1e21", ... */
784 printf("/ Prefixes\n");
785 for (i = 0; i < prefixcount; i++) {
786 printexpansion = expand;
787 thename = prefixtable[i].prefixname;
788 thedefn = prefixtable[i].prefixval;
789 if (expand) {
790 /*
791 * prefix names are sometimes identical to unit
792 * names, so we have to expand thedefn instead of
793 * expanding thename.
794 */
795 initializeunit(&theunit);
796 if (addunit(&theunit, thedefn, 0) != 0
797 || completereduce(&theunit) != 0) {
798 errors++;
799 printexpansion = 0;
800 mywarnx("Error in prefix '%s-'", thename);
801 }
802 }
803 if (printexpansion) {
804 printf("%s-", thename);
805 showunit(&theunit);
806 } else
807 printf("%s-\t%s\n", thename, thedefn);
808 }
809
810 /* 3. Dump all other units. */
811 printf("/ Other units\n");
812 for (i = 0; i < unitcount; i++) {
813 printexpansion = expand;
814 thename = unittable[i].uname;
815 thedefn = unittable[i].uval;
816 if (thedefn[0] == PRIMITIVECHAR)
817 continue;
818 if (expand) {
819 /*
820 * expand thename, not thedefn, so that
821 * we can catch errors in the name itself.
822 * e.g. a name that contains a hyphen
823 * will be interpreted as multiplication.
824 */
825 initializeunit(&theunit);
826 if (addunit(&theunit, thename, 0) != 0
827 || completereduce(&theunit) != 0) {
828 errors++;
829 printexpansion = 0;
830 mywarnx("Error in unit '%s'", thename);
831 }
832 }
833 if (printexpansion) {
834 printf("%s", thename);
835 showunit(&theunit);
836 } else
837 printf("%s\t%s\n", thename, thedefn);
838 }
839
840 if (errors)
841 mywarnx("Definitions with errors: %d", errors);
842 return (errors ? 1 : 0);
843 }
844
845 static void
846 usage(void)
847 {
848 fprintf(stderr,
849 "\nunits [-Llqv] [-f filename] [[count] from-unit to-unit]\n");
850 fprintf(stderr, "\n -f specify units file\n");
851 fprintf(stderr, " -L list units in standardized base units\n");
852 fprintf(stderr, " -l list units\n");
853 fprintf(stderr, " -q suppress prompting (quiet)\n");
854 fprintf(stderr, " -v print version number\n");
855 exit(3);
856 }
857
858 int
859 main(int argc, char **argv)
860 {
861
862 struct unittype have, want;
863 char havestr[81], wantstr[81];
864 int optchar;
865 const char *userfile = 0;
866 int list = 0, listexpand = 0;
867 int quiet = 0;
868
869 while ((optchar = getopt(argc, argv, "lLvqf:")) != -1) {
870 switch (optchar) {
871 case 'l':
872 list = 1;
873 break;
874 case 'L':
875 list = 1;
876 listexpand = 1;
877 precision = DBL_DIG;
878 break;
879 case 'f':
880 userfile = optarg;
881 break;
882 case 'q':
883 quiet = 1;
884 break;
885 case 'v':
886 fprintf(stderr, "\n units version %s Copyright (c) 1993 by Adrian Mariano\n",
887 VERSION);
888 fprintf(stderr, " This program may be freely distributed\n");
889 usage();
890 default:
891 usage();
892 break;
893 }
894 }
895
896 argc -= optind;
897 argv += optind;
898
899 if ((argc != 3 && argc != 2 && argc != 0)
900 || (list && argc != 0))
901 usage();
902
903 if (list)
904 errprefix = "/ "; /* set this before reading the file */
905
906 readunits(userfile);
907
908 if (list)
909 return listunits(listexpand);
910
911 if (argc == 3) {
912 strlcpy(havestr, argv[0], sizeof(havestr));
913 strlcat(havestr, " ", sizeof(havestr));
914 strlcat(havestr, argv[1], sizeof(havestr));
915 argc--;
916 argv++;
917 argv[0] = havestr;
918 }
919
920 if (argc == 2) {
921 strlcpy(havestr, argv[0], sizeof(havestr));
922 strlcpy(wantstr, argv[1], sizeof(wantstr));
923 initializeunit(&have);
924 addunit(&have, havestr, 0);
925 completereduce(&have);
926 initializeunit(&want);
927 addunit(&want, wantstr, 0);
928 completereduce(&want);
929 showanswer(&have, &want);
930 }
931 else {
932 if (!quiet)
933 printf("%d units, %d prefixes\n\n", unitcount,
934 prefixcount);
935 for (;;) {
936 do {
937 initializeunit(&have);
938 if (!quiet)
939 printf("You have: ");
940 if (!fgets(havestr, 80, stdin)) {
941 if (!quiet)
942 putchar('\n');
943 exit(0);
944 }
945 } while (addunit(&have, havestr, 0) ||
946 completereduce(&have));
947 do {
948 initializeunit(&want);
949 if (!quiet)
950 printf("You want: ");
951 if (!fgets(wantstr, 80, stdin)) {
952 if (!quiet)
953 putchar('\n');
954 exit(0);
955 }
956 } while (addunit(&want, wantstr, 0) ||
957 completereduce(&want));
958 showanswer(&have, &want);
959 }
960 }
961 return (0);
962 }
963