Patrick Welche <prlw1@cam.ac.uk>
[netbsd-mini2440.git] / usr.bin / units / units.c
blob3e606c196d07133fb718aaa32343c936d23ec1d1
1 /* $NetBSD: units.c,v 1.15 2006/05/01 00:00:12 christos Exp $ */
3 /*
4 * units.c Copyright (c) 1993 by Adrian Mariano (adrian@cam.cornell.edu)
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.
16 * I would appreciate (though I do not require) receiving a copy of any
17 * improvements you might make to this program.
20 #include <ctype.h>
21 #include <err.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <stdlib.h>
25 #include <unistd.h>
27 #include "pathnames.h"
29 #define VERSION "1.0"
31 #ifndef UNITSFILE
32 #define UNITSFILE _PATH_UNITSLIB
33 #endif
35 #define MAXUNITS 1000
36 #define MAXPREFIXES 50
38 #define MAXSUBUNITS 500
40 #define PRIMITIVECHAR '!'
42 const char *powerstring = "^";
44 struct {
45 const char *uname;
46 const char *uval;
47 } unittable[MAXUNITS];
49 struct unittype {
50 const char *numerator[MAXSUBUNITS];
51 const char *denominator[MAXSUBUNITS];
52 double factor;
55 struct {
56 const char *prefixname;
57 const char *prefixval;
58 } prefixtable[MAXPREFIXES];
61 const char *NULLUNIT = "";
63 int unitcount;
64 int prefixcount;
67 int addsubunit __P((const char *[], const char *));
68 int addunit __P((struct unittype *, const char *, int));
69 void cancelunit __P((struct unittype *));
70 int compare __P((const void *, const void *));
71 int compareproducts __P((const char **, const char **));
72 int compareunits __P((struct unittype *, struct unittype *));
73 int compareunitsreciprocal __P((struct unittype *, struct unittype *));
74 int completereduce __P((struct unittype *));
75 void initializeunit __P((struct unittype *));
76 int main __P((int, char **));
77 void readerror __P((int));
78 void readunits __P((const char *));
79 int reduceproduct __P((struct unittype *, int));
80 int reduceunit __P((struct unittype *));
81 void showanswer __P((struct unittype *, struct unittype *));
82 void showunit __P((struct unittype *));
83 void sortunit __P((struct unittype *));
84 void usage __P((void));
85 void zeroerror __P((void));
86 char *dupstr __P((const char *));
87 const char *lookupunit __P((const char *));
90 char *
91 dupstr(const char *str)
93 char *ret;
95 ret = strdup(str);
96 if (!ret)
97 err(3, "Memory allocation error");
98 return (ret);
102 void
103 readerror(int linenum)
105 warnx("Error in units file '%s' line %d", UNITSFILE, linenum);
109 void
110 readunits(const char *userfile)
112 FILE *unitfile;
113 char line[80], *lineptr;
114 int len, linenum, i;
116 unitcount = 0;
117 linenum = 0;
119 if (userfile) {
120 unitfile = fopen(userfile, "rt");
121 if (!unitfile)
122 err(1, "Unable to open units file '%s'", userfile);
124 else {
125 unitfile = fopen(UNITSFILE, "rt");
126 if (!unitfile) {
127 char *direc, *env;
128 char filename[1000];
129 char separator[2];
131 env = getenv("PATH");
132 if (env) {
133 if (strchr(env, ';'))
134 strlcpy(separator, ";",
135 sizeof(separator));
136 else
137 strlcpy(separator, ":",
138 sizeof(separator));
139 direc = strtok(env, separator);
140 while (direc) {
141 strlcpy(filename, "", sizeof(filename));
142 strlcat(filename, direc,
143 sizeof(filename));
144 strlcat(filename, "/",
145 sizeof(filename));
146 strlcat(filename, UNITSFILE,
147 sizeof(filename));
148 unitfile = fopen(filename, "rt");
149 if (unitfile)
150 break;
151 direc = strtok(NULL, separator);
154 if (!unitfile)
155 errx(1, "Can't find units file '%s'",
156 UNITSFILE);
159 while (!feof(unitfile)) {
160 if (!fgets(line, 79, unitfile))
161 break;
162 linenum++;
163 lineptr = line;
164 if (*lineptr == '/')
165 continue;
166 lineptr += strspn(lineptr, " \n\t");
167 len = strcspn(lineptr, " \n\t");
168 lineptr[len] = 0;
169 if (!strlen(lineptr))
170 continue;
171 if (lineptr[strlen(lineptr) - 1] == '-') { /* it's a prefix */
172 if (prefixcount == MAXPREFIXES) {
173 warnx("Memory for prefixes exceeded in line %d",
174 linenum);
175 continue;
177 lineptr[strlen(lineptr) - 1] = 0;
178 prefixtable[prefixcount].prefixname = dupstr(lineptr);
179 for (i = 0; i < prefixcount; i++)
180 if (!strcmp(prefixtable[i].prefixname, lineptr)) {
181 warnx(
182 "Redefinition of prefix '%s' on line %d ignored",
183 lineptr, linenum);
184 continue;
186 lineptr += len + 1;
187 if (!strlen(lineptr)) {
188 readerror(linenum);
189 continue;
191 lineptr += strspn(lineptr, " \n\t");
192 len = strcspn(lineptr, "\n\t");
193 lineptr[len] = 0;
194 prefixtable[prefixcount++].prefixval = dupstr(lineptr);
196 else { /* it's not a prefix */
197 if (unitcount == MAXUNITS) {
198 warnx("Memory for units exceeded in line %d",
199 linenum);
200 continue;
202 unittable[unitcount].uname = dupstr(lineptr);
203 for (i = 0; i < unitcount; i++)
204 if (!strcmp(unittable[i].uname, lineptr)) {
205 warnx(
206 "Redefinition of unit '%s' on line %d ignored",
207 lineptr, linenum);
208 continue;
210 lineptr += len + 1;
211 lineptr += strspn(lineptr, " \n\t");
212 if (!strlen(lineptr)) {
213 readerror(linenum);
214 continue;
216 len = strcspn(lineptr, "\n\t");
217 lineptr[len] = 0;
218 unittable[unitcount++].uval = dupstr(lineptr);
221 fclose(unitfile);
224 void
225 initializeunit(struct unittype * theunit)
227 theunit->factor = 1.0;
228 theunit->numerator[0] = theunit->denominator[0] = NULL;
232 int
233 addsubunit(const char *product[], const char *toadd)
235 const char **ptr;
237 for (ptr = product; *ptr && *ptr != NULLUNIT; ptr++);
238 if (ptr >= product + MAXSUBUNITS) {
239 warnx("Memory overflow in unit reduction");
240 return 1;
242 if (!*ptr)
243 *(ptr + 1) = 0;
244 *ptr = dupstr(toadd);
245 return 0;
249 void
250 showunit(struct unittype * theunit)
252 const char **ptr;
253 int printedslash;
254 int counter = 1;
256 printf("\t%.8g", theunit->factor);
257 for (ptr = theunit->numerator; *ptr; ptr++) {
258 if (ptr > theunit->numerator && **ptr &&
259 !strcmp(*ptr, *(ptr - 1)))
260 counter++;
261 else {
262 if (counter > 1)
263 printf("%s%d", powerstring, counter);
264 if (**ptr)
265 printf(" %s", *ptr);
266 counter = 1;
269 if (counter > 1)
270 printf("%s%d", powerstring, counter);
271 counter = 1;
272 printedslash = 0;
273 for (ptr = theunit->denominator; *ptr; ptr++) {
274 if (ptr > theunit->denominator && **ptr &&
275 !strcmp(*ptr, *(ptr - 1)))
276 counter++;
277 else {
278 if (counter > 1)
279 printf("%s%d", powerstring, counter);
280 if (**ptr) {
281 if (!printedslash)
282 printf(" /");
283 printedslash = 1;
284 printf(" %s", *ptr);
286 counter = 1;
289 if (counter > 1)
290 printf("%s%d", powerstring, counter);
291 printf("\n");
295 void
296 zeroerror()
298 warnx("Unit reduces to zero");
302 Adds the specified string to the unit.
303 Flip is 0 for adding normally, 1 for adding reciprocal.
305 Returns 0 for successful addition, nonzero on error.
308 int
309 addunit(struct unittype * theunit, const char *toadd, int flip)
311 char *scratch, *savescr;
312 char *item;
313 char *divider, *slash;
314 int doingtop;
316 savescr = scratch = dupstr(toadd);
317 for (slash = scratch + 1; *slash; slash++)
318 if (*slash == '-' &&
319 (tolower((unsigned char)*(slash - 1)) != 'e' ||
320 !strchr(".0123456789", *(slash + 1))))
321 *slash = ' ';
322 slash = strchr(scratch, '/');
323 if (slash)
324 *slash = 0;
325 doingtop = 1;
326 do {
327 item = strtok(scratch, " *\t\n/");
328 while (item) {
329 if (strchr("0123456789.", *item)) { /* item is a number */
330 double num;
332 divider = strchr(item, '|');
333 if (divider) {
334 *divider = 0;
335 num = atof(item);
336 if (!num) {
337 zeroerror();
338 return 1;
340 if (doingtop ^ flip)
341 theunit->factor *= num;
342 else
343 theunit->factor /= num;
344 num = atof(divider + 1);
345 if (!num) {
346 zeroerror();
347 return 1;
349 if (doingtop ^ flip)
350 theunit->factor /= num;
351 else
352 theunit->factor *= num;
354 else {
355 num = atof(item);
356 if (!num) {
357 zeroerror();
358 return 1;
360 if (doingtop ^ flip)
361 theunit->factor *= num;
362 else
363 theunit->factor /= num;
367 else { /* item is not a number */
368 int repeat = 1;
370 if (strchr("23456789",
371 item[strlen(item) - 1])) {
372 repeat = item[strlen(item) - 1] - '0';
373 item[strlen(item) - 1] = 0;
375 for (; repeat; repeat--)
376 if (addsubunit(doingtop ^ flip ? theunit->numerator : theunit->denominator, item))
377 return 1;
379 item = strtok(NULL, " *\t/\n");
381 doingtop--;
382 if (slash) {
383 scratch = slash + 1;
385 else
386 doingtop--;
387 } while (doingtop >= 0);
388 free(savescr);
389 return 0;
393 int
394 compare(const void *item1, const void *item2)
396 return strcmp(*(const char * const *) item1,
397 *(const char * const *) item2);
401 void
402 sortunit(struct unittype * theunit)
404 const char **ptr;
405 int count;
407 for (count = 0, ptr = theunit->numerator; *ptr; ptr++, count++);
408 qsort(theunit->numerator, count, sizeof(char *), compare);
409 for (count = 0, ptr = theunit->denominator; *ptr; ptr++, count++);
410 qsort(theunit->denominator, count, sizeof(char *), compare);
414 void
415 cancelunit(struct unittype * theunit)
417 const char **den, **num;
418 int comp;
420 den = theunit->denominator;
421 num = theunit->numerator;
423 while (*num && *den) {
424 comp = strcmp(*den, *num);
425 if (!comp) {
426 /* if (*den!=NULLUNIT) free(*den);
427 if (*num!=NULLUNIT) free(*num);*/
428 *den++ = NULLUNIT;
429 *num++ = NULLUNIT;
431 else if (comp < 0)
432 den++;
433 else
434 num++;
442 Looks up the definition for the specified unit.
443 Returns a pointer to the definition or a null pointer
444 if the specified unit does not appear in the units table.
447 static char buffer[100]; /* buffer for lookupunit answers with
448 prefixes */
450 const char *
451 lookupunit(const char *unit)
453 int i;
454 char *copy;
456 for (i = 0; i < unitcount; i++) {
457 if (!strcmp(unittable[i].uname, unit))
458 return unittable[i].uval;
461 if (unit[strlen(unit) - 1] == '^') {
462 copy = dupstr(unit);
463 copy[strlen(copy) - 1] = 0;
464 for (i = 0; i < unitcount; i++) {
465 if (!strcmp(unittable[i].uname, copy)) {
466 strlcpy(buffer, copy, sizeof(buffer));
467 free(copy);
468 return buffer;
471 free(copy);
473 if (unit[strlen(unit) - 1] == 's') {
474 copy = dupstr(unit);
475 copy[strlen(copy) - 1] = 0;
476 for (i = 0; i < unitcount; i++) {
477 if (!strcmp(unittable[i].uname, copy)) {
478 strlcpy(buffer, copy, sizeof(buffer));
479 free(copy);
480 return buffer;
483 if (copy[strlen(copy) - 1] == 'e') {
484 copy[strlen(copy) - 1] = 0;
485 for (i = 0; i < unitcount; i++) {
486 if (!strcmp(unittable[i].uname, copy)) {
487 strlcpy(buffer, copy, sizeof(buffer));
488 free(copy);
489 return buffer;
493 free(copy);
495 for (i = 0; i < prefixcount; i++) {
496 if (!strncmp(prefixtable[i].prefixname, unit,
497 strlen(prefixtable[i].prefixname))) {
498 unit += strlen(prefixtable[i].prefixname);
499 if (!strlen(unit) || lookupunit(unit)) {
500 strlcpy(buffer, prefixtable[i].prefixval,
501 sizeof(buffer));
502 strlcat(buffer, " ", sizeof(buffer));
503 strlcat(buffer, unit, sizeof(buffer));
504 return buffer;
508 return 0;
514 reduces a product of symbolic units to primitive units.
515 The three low bits are used to return flags:
517 bit 0 (1) set on if reductions were performed without error.
518 bit 1 (2) set on if no reductions are performed.
519 bit 2 (4) set on if an unknown unit is discovered.
523 #define ERROR 4
525 int
526 reduceproduct(struct unittype * theunit, int flip)
529 const char *toadd;
530 const char **product;
531 int didsomething = 2;
533 if (flip)
534 product = theunit->denominator;
535 else
536 product = theunit->numerator;
538 for (; *product; product++) {
540 for (;;) {
541 if (!strlen(*product))
542 break;
543 toadd = lookupunit(*product);
544 if (!toadd) {
545 printf("unknown unit '%s'\n", *product);
546 return ERROR;
548 if (strchr(toadd, PRIMITIVECHAR))
549 break;
550 didsomething = 1;
551 if (*product != NULLUNIT) {
552 free(__UNCONST(*product));
553 *product = NULLUNIT;
555 if (addunit(theunit, toadd, flip))
556 return ERROR;
559 return didsomething;
564 Reduces numerator and denominator of the specified unit.
565 Returns 0 on success, or 1 on unknown unit error.
568 int
569 reduceunit(struct unittype * theunit)
571 int ret;
573 ret = 1;
574 while (ret & 1) {
575 ret = reduceproduct(theunit, 0) | reduceproduct(theunit, 1);
576 if (ret & 4)
577 return 1;
579 return 0;
583 int
584 compareproducts(const char **one, const char **two)
586 while (*one || *two) {
587 if (!*one && *two != NULLUNIT)
588 return 1;
589 if (!*two && *one != NULLUNIT)
590 return 1;
591 if (*one == NULLUNIT)
592 one++;
593 else if (*two == NULLUNIT)
594 two++;
595 else if (*one && *two && strcmp(*one, *two))
596 return 1;
597 else
598 one++, two++;
600 return 0;
604 /* Return zero if units are compatible, nonzero otherwise */
606 int
607 compareunits(struct unittype * first, struct unittype * second)
609 return
610 compareproducts(first->numerator, second->numerator) ||
611 compareproducts(first->denominator, second->denominator);
614 int
615 compareunitsreciprocal(struct unittype * first, struct unittype * second)
617 return
618 compareproducts(first->numerator, second->denominator) ||
619 compareproducts(first->denominator, second->numerator);
623 int
624 completereduce(struct unittype * unit)
626 if (reduceunit(unit))
627 return 1;
628 sortunit(unit);
629 cancelunit(unit);
630 return 0;
634 void
635 showanswer(struct unittype * have, struct unittype * want)
637 if (compareunits(have, want)) {
638 if (compareunitsreciprocal(have, want)) {
639 printf("conformability error\n");
640 showunit(have);
641 showunit(want);
642 } else {
643 printf("\treciprocal conversion\n");
644 printf("\t* %.8g\n\t/ %.8g\n", 1 / (have->factor * want->factor),
645 want->factor * have->factor);
648 else
649 printf("\t* %.8g\n\t/ %.8g\n", have->factor / want->factor,
650 want->factor / have->factor);
654 void
655 usage()
657 fprintf(stderr,
658 "\nunits [-f unitsfile] [-q] [-v] [from-unit to-unit]\n");
659 fprintf(stderr, "\n -f specify units file\n");
660 fprintf(stderr, " -q suppress prompting (quiet)\n");
661 fprintf(stderr, " -v print version number\n");
662 exit(3);
667 main(int argc, char **argv)
670 struct unittype have, want;
671 char havestr[81], wantstr[81];
672 int optchar;
673 const char *userfile = 0;
674 int quiet = 0;
676 while ((optchar = getopt(argc, argv, "vqf:")) != -1) {
677 switch (optchar) {
678 case 'f':
679 userfile = optarg;
680 break;
681 case 'q':
682 quiet = 1;
683 break;
684 case 'v':
685 fprintf(stderr, "\n units version %s Copyright (c) 1993 by Adrian Mariano\n",
686 VERSION);
687 fprintf(stderr, " This program may be freely distributed\n");
688 usage();
689 default:
690 usage();
691 break;
695 argc -= optind;
696 argv += optind;
698 if (argc != 3 && argc != 2 && argc != 0)
699 usage();
701 readunits(userfile);
703 if (argc == 3) {
704 strlcpy(havestr, argv[0], sizeof(havestr));
705 strlcat(havestr, " ", sizeof(havestr));
706 strlcat(havestr, argv[1], sizeof(havestr));
707 argc--;
708 argv++;
709 argv[0] = havestr;
712 if (argc == 2) {
713 strlcpy(havestr, argv[0], sizeof(havestr));
714 strlcpy(wantstr, argv[1], sizeof(wantstr));
715 initializeunit(&have);
716 addunit(&have, havestr, 0);
717 completereduce(&have);
718 initializeunit(&want);
719 addunit(&want, wantstr, 0);
720 completereduce(&want);
721 showanswer(&have, &want);
723 else {
724 if (!quiet)
725 printf("%d units, %d prefixes\n\n", unitcount,
726 prefixcount);
727 for (;;) {
728 do {
729 initializeunit(&have);
730 if (!quiet)
731 printf("You have: ");
732 if (!fgets(havestr, 80, stdin)) {
733 if (!quiet)
734 putchar('\n');
735 exit(0);
737 } while (addunit(&have, havestr, 0) ||
738 completereduce(&have));
739 do {
740 initializeunit(&want);
741 if (!quiet)
742 printf("You want: ");
743 if (!fgets(wantstr, 80, stdin)) {
744 if (!quiet)
745 putchar('\n');
746 exit(0);
748 } while (addunit(&want, wantstr, 0) ||
749 completereduce(&want));
750 showanswer(&have, &want);
753 return (0);