1 /* $NetBSD: units.c,v 1.15 2006/05/01 00:00:12 christos Exp $ */
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
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.
27 #include "pathnames.h"
32 #define UNITSFILE _PATH_UNITSLIB
36 #define MAXPREFIXES 50
38 #define MAXSUBUNITS 500
40 #define PRIMITIVECHAR '!'
42 const char *powerstring
= "^";
47 } unittable
[MAXUNITS
];
50 const char *numerator
[MAXSUBUNITS
];
51 const char *denominator
[MAXSUBUNITS
];
56 const char *prefixname
;
57 const char *prefixval
;
58 } prefixtable
[MAXPREFIXES
];
61 const char *NULLUNIT
= "";
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 *));
91 dupstr(const char *str
)
97 err(3, "Memory allocation error");
103 readerror(int linenum
)
105 warnx("Error in units file '%s' line %d", UNITSFILE
, linenum
);
110 readunits(const char *userfile
)
113 char line
[80], *lineptr
;
120 unitfile
= fopen(userfile
, "rt");
122 err(1, "Unable to open units file '%s'", userfile
);
125 unitfile
= fopen(UNITSFILE
, "rt");
131 env
= getenv("PATH");
133 if (strchr(env
, ';'))
134 strlcpy(separator
, ";",
137 strlcpy(separator
, ":",
139 direc
= strtok(env
, separator
);
141 strlcpy(filename
, "", sizeof(filename
));
142 strlcat(filename
, direc
,
144 strlcat(filename
, "/",
146 strlcat(filename
, UNITSFILE
,
148 unitfile
= fopen(filename
, "rt");
151 direc
= strtok(NULL
, separator
);
155 errx(1, "Can't find units file '%s'",
159 while (!feof(unitfile
)) {
160 if (!fgets(line
, 79, unitfile
))
166 lineptr
+= strspn(lineptr
, " \n\t");
167 len
= strcspn(lineptr
, " \n\t");
169 if (!strlen(lineptr
))
171 if (lineptr
[strlen(lineptr
) - 1] == '-') { /* it's a prefix */
172 if (prefixcount
== MAXPREFIXES
) {
173 warnx("Memory for prefixes exceeded in line %d",
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
)) {
182 "Redefinition of prefix '%s' on line %d ignored",
187 if (!strlen(lineptr
)) {
191 lineptr
+= strspn(lineptr
, " \n\t");
192 len
= strcspn(lineptr
, "\n\t");
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",
202 unittable
[unitcount
].uname
= dupstr(lineptr
);
203 for (i
= 0; i
< unitcount
; i
++)
204 if (!strcmp(unittable
[i
].uname
, lineptr
)) {
206 "Redefinition of unit '%s' on line %d ignored",
211 lineptr
+= strspn(lineptr
, " \n\t");
212 if (!strlen(lineptr
)) {
216 len
= strcspn(lineptr
, "\n\t");
218 unittable
[unitcount
++].uval
= dupstr(lineptr
);
225 initializeunit(struct unittype
* theunit
)
227 theunit
->factor
= 1.0;
228 theunit
->numerator
[0] = theunit
->denominator
[0] = NULL
;
233 addsubunit(const char *product
[], const char *toadd
)
237 for (ptr
= product
; *ptr
&& *ptr
!= NULLUNIT
; ptr
++);
238 if (ptr
>= product
+ MAXSUBUNITS
) {
239 warnx("Memory overflow in unit reduction");
244 *ptr
= dupstr(toadd
);
250 showunit(struct unittype
* theunit
)
256 printf("\t%.8g", theunit
->factor
);
257 for (ptr
= theunit
->numerator
; *ptr
; ptr
++) {
258 if (ptr
> theunit
->numerator
&& **ptr
&&
259 !strcmp(*ptr
, *(ptr
- 1)))
263 printf("%s%d", powerstring
, counter
);
270 printf("%s%d", powerstring
, counter
);
273 for (ptr
= theunit
->denominator
; *ptr
; ptr
++) {
274 if (ptr
> theunit
->denominator
&& **ptr
&&
275 !strcmp(*ptr
, *(ptr
- 1)))
279 printf("%s%d", powerstring
, counter
);
290 printf("%s%d", powerstring
, counter
);
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.
309 addunit(struct unittype
* theunit
, const char *toadd
, int flip
)
311 char *scratch
, *savescr
;
313 char *divider
, *slash
;
316 savescr
= scratch
= dupstr(toadd
);
317 for (slash
= scratch
+ 1; *slash
; slash
++)
319 (tolower((unsigned char)*(slash
- 1)) != 'e' ||
320 !strchr(".0123456789", *(slash
+ 1))))
322 slash
= strchr(scratch
, '/');
327 item
= strtok(scratch
, " *\t\n/");
329 if (strchr("0123456789.", *item
)) { /* item is a number */
332 divider
= strchr(item
, '|');
341 theunit
->factor
*= num
;
343 theunit
->factor
/= num
;
344 num
= atof(divider
+ 1);
350 theunit
->factor
/= num
;
352 theunit
->factor
*= num
;
361 theunit
->factor
*= num
;
363 theunit
->factor
/= num
;
367 else { /* item is not a number */
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
))
379 item
= strtok(NULL
, " *\t/\n");
387 } while (doingtop
>= 0);
394 compare(const void *item1
, const void *item2
)
396 return strcmp(*(const char * const *) item1
,
397 *(const char * const *) item2
);
402 sortunit(struct unittype
* theunit
)
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
);
415 cancelunit(struct unittype
* theunit
)
417 const char **den
, **num
;
420 den
= theunit
->denominator
;
421 num
= theunit
->numerator
;
423 while (*num
&& *den
) {
424 comp
= strcmp(*den
, *num
);
426 /* if (*den!=NULLUNIT) free(*den);
427 if (*num!=NULLUNIT) free(*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
451 lookupunit(const char *unit
)
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] == '^') {
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
));
473 if (unit
[strlen(unit
) - 1] == 's') {
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
));
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
));
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
,
502 strlcat(buffer
, " ", sizeof(buffer
));
503 strlcat(buffer
, unit
, sizeof(buffer
));
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.
526 reduceproduct(struct unittype
* theunit
, int flip
)
530 const char **product
;
531 int didsomething
= 2;
534 product
= theunit
->denominator
;
536 product
= theunit
->numerator
;
538 for (; *product
; product
++) {
541 if (!strlen(*product
))
543 toadd
= lookupunit(*product
);
545 printf("unknown unit '%s'\n", *product
);
548 if (strchr(toadd
, PRIMITIVECHAR
))
551 if (*product
!= NULLUNIT
) {
552 free(__UNCONST(*product
));
555 if (addunit(theunit
, toadd
, flip
))
564 Reduces numerator and denominator of the specified unit.
565 Returns 0 on success, or 1 on unknown unit error.
569 reduceunit(struct unittype
* theunit
)
575 ret
= reduceproduct(theunit
, 0) | reduceproduct(theunit
, 1);
584 compareproducts(const char **one
, const char **two
)
586 while (*one
|| *two
) {
587 if (!*one
&& *two
!= NULLUNIT
)
589 if (!*two
&& *one
!= NULLUNIT
)
591 if (*one
== NULLUNIT
)
593 else if (*two
== NULLUNIT
)
595 else if (*one
&& *two
&& strcmp(*one
, *two
))
604 /* Return zero if units are compatible, nonzero otherwise */
607 compareunits(struct unittype
* first
, struct unittype
* second
)
610 compareproducts(first
->numerator
, second
->numerator
) ||
611 compareproducts(first
->denominator
, second
->denominator
);
615 compareunitsreciprocal(struct unittype
* first
, struct unittype
* second
)
618 compareproducts(first
->numerator
, second
->denominator
) ||
619 compareproducts(first
->denominator
, second
->numerator
);
624 completereduce(struct unittype
* unit
)
626 if (reduceunit(unit
))
635 showanswer(struct unittype
* have
, struct unittype
* want
)
637 if (compareunits(have
, want
)) {
638 if (compareunitsreciprocal(have
, want
)) {
639 printf("conformability error\n");
643 printf("\treciprocal conversion\n");
644 printf("\t* %.8g\n\t/ %.8g\n", 1 / (have
->factor
* want
->factor
),
645 want
->factor
* have
->factor
);
649 printf("\t* %.8g\n\t/ %.8g\n", have
->factor
/ want
->factor
,
650 want
->factor
/ have
->factor
);
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");
667 main(int argc
, char **argv
)
670 struct unittype have
, want
;
671 char havestr
[81], wantstr
[81];
673 const char *userfile
= 0;
676 while ((optchar
= getopt(argc
, argv
, "vqf:")) != -1) {
685 fprintf(stderr
, "\n units version %s Copyright (c) 1993 by Adrian Mariano\n",
687 fprintf(stderr
, " This program may be freely distributed\n");
698 if (argc
!= 3 && argc
!= 2 && argc
!= 0)
704 strlcpy(havestr
, argv
[0], sizeof(havestr
));
705 strlcat(havestr
, " ", sizeof(havestr
));
706 strlcat(havestr
, argv
[1], sizeof(havestr
));
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
);
725 printf("%d units, %d prefixes\n\n", unitcount
,
729 initializeunit(&have
);
731 printf("You have: ");
732 if (!fgets(havestr
, 80, stdin
)) {
737 } while (addunit(&have
, havestr
, 0) ||
738 completereduce(&have
));
740 initializeunit(&want
);
742 printf("You want: ");
743 if (!fgets(wantstr
, 80, stdin
)) {
748 } while (addunit(&want
, wantstr
, 0) ||
749 completereduce(&want
));
750 showanswer(&have
, &want
);