2 * units.c Copyright (c) 1993 by Adrian Mariano (adrian@cam.cornell.edu)
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. The name of the author may not be used to endorse or promote products
10 * derived from this software without specific prior written permission.
11 * Disclaimer: This software is provided by the author "as is". The author
12 * shall not be liable for any damages caused in any way by this software.
14 * I would appreciate (though I do not require) receiving a copy of any
15 * improvements you might make to this program.
17 * $FreeBSD: src/usr.bin/units/units.c,v 1.6.2.2 2001/03/04 09:22:35 kris Exp $
18 * $DragonFly: src/usr.bin/units/units.c,v 1.4 2006/08/13 19:19:44 swildner Exp $
28 #include "pathnames.h"
33 #define UNITSFILE _PATH_UNITSLIB
37 #define MAXPREFIXES 100
39 #define MAXSUBUNITS 500
41 #define PRIMITIVECHAR '!'
43 const char *powerstring
= "^";
48 } unittable
[MAXUNITS
];
51 char *numerator
[MAXSUBUNITS
];
52 char *denominator
[MAXSUBUNITS
];
59 } prefixtable
[MAXPREFIXES
];
74 static int addsubunit(char *[], char *);
75 static int addunit(struct unittype
*, char *, int);
76 static void cancelunit(struct unittype
*);
77 static int compare(const void *, const void *);
78 static int compareproducts(char **, char **);
79 static int compareunits(struct unittype
*, struct unittype
*);
80 static int completereduce(struct unittype
*);
81 static char *dupstr(const char *);
82 static void initializeunit(struct unittype
*);
83 static char *lookupunit(const char *);
84 static void readunits(const char *);
85 static int reduceproduct(struct unittype
*, int);
86 static int reduceunit(struct unittype
*);
87 static void showanswer(struct unittype
*, struct unittype
*);
88 static void showunit(struct unittype
*);
89 static void sortunit(struct unittype
*);
90 static void usage(void);
91 static void zeroerror(void);
95 dupstr(const char *str
)
99 ret
= malloc(strlen(str
) + 1);
101 errx(3, "memory allocation error");
108 readunits(const char *userfile
)
111 char line
[512], *lineptr
;
118 unitfile
= fopen(userfile
, "rt");
120 errx(1, "unable to open units file '%s'", userfile
);
123 unitfile
= fopen(UNITSFILE
, "rt");
128 env
= getenv("PATH");
130 direc
= strtok(env
, SEPARATOR
);
132 snprintf(filename
, sizeof(filename
),
133 "%s/%s", direc
, UNITSFILE
);
134 unitfile
= fopen(filename
, "rt");
137 direc
= strtok(NULL
, SEPARATOR
);
141 errx(1, "can't find units file '%s'", UNITSFILE
);
144 while (!feof(unitfile
)) {
145 if (!fgets(line
, sizeof(line
), unitfile
))
151 lineptr
+= strspn(lineptr
, " \n\t");
152 len
= strcspn(lineptr
, " \n\t");
154 if (!strlen(lineptr
))
156 if (lineptr
[strlen(lineptr
) - 1] == '-') { /* it's a prefix */
157 if (prefixcount
== MAXPREFIXES
) {
158 warnx("memory for prefixes exceeded in line %d", linenum
);
161 lineptr
[strlen(lineptr
) - 1] = 0;
162 prefixtable
[prefixcount
].prefixname
= dupstr(lineptr
);
163 for (i
= 0; i
< prefixcount
; i
++)
164 if (!strcmp(prefixtable
[i
].prefixname
, lineptr
)) {
165 warnx("redefinition of prefix '%s' on line %d ignored",
170 lineptr
+= strspn(lineptr
, " \n\t");
171 len
= strcspn(lineptr
, "\n\t");
173 warnx("unexpected end of prefix on line %d",
178 prefixtable
[prefixcount
++].prefixval
= dupstr(lineptr
);
180 else { /* it's not a prefix */
181 if (unitcount
== MAXUNITS
) {
182 warnx("memory for units exceeded in line %d", linenum
);
185 unittable
[unitcount
].uname
= dupstr(lineptr
);
186 for (i
= 0; i
< unitcount
; i
++)
187 if (!strcmp(unittable
[i
].uname
, lineptr
)) {
188 warnx("redefinition of unit '%s' on line %d ignored",
193 lineptr
+= strspn(lineptr
, " \n\t");
194 if (!strlen(lineptr
)) {
195 warnx("unexpected end of unit on line %d",
199 len
= strcspn(lineptr
, "\n\t");
201 unittable
[unitcount
++].uval
= dupstr(lineptr
);
208 initializeunit(struct unittype
* theunit
)
210 theunit
->factor
= 1.0;
211 theunit
->numerator
[0] = theunit
->denominator
[0] = NULL
;
216 addsubunit(char *product
[], char *toadd
)
220 for (ptr
= product
; *ptr
&& *ptr
!= NULLUNIT
; ptr
++);
221 if (ptr
>= product
+ MAXSUBUNITS
) {
222 warnx("memory overflow in unit reduction");
227 *ptr
= dupstr(toadd
);
233 showunit(struct unittype
* theunit
)
239 printf("\t%.8g", theunit
->factor
);
240 for (ptr
= theunit
->numerator
; *ptr
; ptr
++) {
241 if (ptr
> theunit
->numerator
&& **ptr
&&
242 !strcmp(*ptr
, *(ptr
- 1)))
246 printf("%s%d", powerstring
, counter
);
253 printf("%s%d", powerstring
, counter
);
256 for (ptr
= theunit
->denominator
; *ptr
; ptr
++) {
257 if (ptr
> theunit
->denominator
&& **ptr
&&
258 !strcmp(*ptr
, *(ptr
- 1)))
262 printf("%s%d", powerstring
, counter
);
273 printf("%s%d", powerstring
, counter
);
281 warnx("unit reduces to zero");
285 Adds the specified string to the unit.
286 Flip is 0 for adding normally, 1 for adding reciprocal.
288 Returns 0 for successful addition, nonzero on error.
292 addunit(struct unittype
* theunit
, char *toadd
, int flip
)
294 char *scratch
, *savescr
;
296 char *divider
, *slash
;
302 savescr
= scratch
= dupstr(toadd
);
303 for (slash
= scratch
+ 1; *slash
; slash
++)
305 (tolower(*(slash
- 1)) != 'e' ||
306 !strchr(".0123456789", *(slash
+ 1))))
308 slash
= strchr(scratch
, '/');
313 item
= strtok(scratch
, " *\t\n/");
315 if (strchr("0123456789.", *item
)) { /* item is a number */
318 divider
= strchr(item
, '|');
327 theunit
->factor
*= num
;
329 theunit
->factor
/= num
;
330 num
= atof(divider
+ 1);
336 theunit
->factor
/= num
;
338 theunit
->factor
*= num
;
347 theunit
->factor
*= num
;
349 theunit
->factor
/= num
;
353 else { /* item is not a number */
356 if (strchr("23456789",
357 item
[strlen(item
) - 1])) {
358 repeat
= item
[strlen(item
) - 1] - '0';
359 item
[strlen(item
) - 1] = 0;
361 for (; repeat
; repeat
--)
362 if (addsubunit(doingtop
^ flip
? theunit
->numerator
: theunit
->denominator
, item
))
365 item
= strtok(NULL
, " *\t/\n");
373 } while (doingtop
>= 0);
380 compare(const void *item1
, const void *item2
)
382 return strcmp(*(const char * const *)item1
, *(const char * const *)item2
);
387 sortunit(struct unittype
* theunit
)
392 for (count
= 0, ptr
= theunit
->numerator
; *ptr
; ptr
++, count
++);
393 qsort(theunit
->numerator
, count
, sizeof(char *), compare
);
394 for (count
= 0, ptr
= theunit
->denominator
; *ptr
; ptr
++, count
++);
395 qsort(theunit
->denominator
, count
, sizeof(char *), compare
);
400 cancelunit(struct unittype
* theunit
)
405 den
= theunit
->denominator
;
406 num
= theunit
->numerator
;
408 while (*num
&& *den
) {
409 comp
= strcmp(*den
, *num
);
411 /* if (*den!=NULLUNIT) free(*den);
412 if (*num!=NULLUNIT) free(*num);*/
427 Looks up the definition for the specified unit.
428 Returns a pointer to the definition or a null pointer
429 if the specified unit does not appear in the units table.
432 static char buffer
[100]; /* buffer for lookupunit answers with
436 lookupunit(const char *unit
)
441 for (i
= 0; i
< unitcount
; i
++) {
442 if (!strcmp(unittable
[i
].uname
, unit
))
443 return unittable
[i
].uval
;
446 if (unit
[strlen(unit
) - 1] == '^') {
448 copy
[strlen(copy
) - 1] = 0;
449 for (i
= 0; i
< unitcount
; i
++) {
450 if (!strcmp(unittable
[i
].uname
, copy
)) {
451 strlcpy(buffer
, copy
, sizeof(buffer
));
458 if (unit
[strlen(unit
) - 1] == 's') {
460 copy
[strlen(copy
) - 1] = 0;
461 for (i
= 0; i
< unitcount
; i
++) {
462 if (!strcmp(unittable
[i
].uname
, copy
)) {
463 strlcpy(buffer
, copy
, sizeof(buffer
));
468 if (copy
[strlen(copy
) - 1] == 'e') {
469 copy
[strlen(copy
) - 1] = 0;
470 for (i
= 0; i
< unitcount
; i
++) {
471 if (!strcmp(unittable
[i
].uname
, copy
)) {
472 strlcpy(buffer
, copy
, sizeof(buffer
));
480 for (i
= 0; i
< prefixcount
; i
++) {
481 size_t len
= strlen(prefixtable
[i
].prefixname
);
482 if (!strncmp(prefixtable
[i
].prefixname
, unit
, len
)) {
483 if (!strlen(unit
+ len
) || lookupunit(unit
+ len
)) {
484 snprintf(buffer
, sizeof(buffer
), "%s %s",
485 prefixtable
[i
].prefixval
, unit
+ len
);
496 reduces a product of symbolic units to primitive units.
497 The three low bits are used to return flags:
499 bit 0 (1) set on if reductions were performed without error.
500 bit 1 (2) set on if no reductions are performed.
501 bit 2 (4) set on if an unknown unit is discovered.
508 reduceproduct(struct unittype
* theunit
, int flip
)
513 int didsomething
= 2;
516 product
= theunit
->denominator
;
518 product
= theunit
->numerator
;
520 for (; *product
; product
++) {
523 if (!strlen(*product
))
525 toadd
= lookupunit(*product
);
527 printf("unknown unit '%s'\n", *product
);
530 if (strchr(toadd
, PRIMITIVECHAR
))
533 if (*product
!= NULLUNIT
) {
537 if (addunit(theunit
, toadd
, flip
))
546 Reduces numerator and denominator of the specified unit.
547 Returns 0 on success, or 1 on unknown unit error.
551 reduceunit(struct unittype
* theunit
)
557 ret
= reduceproduct(theunit
, 0) | reduceproduct(theunit
, 1);
566 compareproducts(char **one
, char **two
)
568 while (*one
|| *two
) {
569 if (!*one
&& *two
!= NULLUNIT
)
571 if (!*two
&& *one
!= NULLUNIT
)
573 if (*one
== NULLUNIT
)
575 else if (*two
== NULLUNIT
)
577 else if (strcmp(*one
, *two
))
586 /* Return zero if units are compatible, nonzero otherwise */
589 compareunits(struct unittype
* first
, struct unittype
* second
)
592 compareproducts(first
->numerator
, second
->numerator
) ||
593 compareproducts(first
->denominator
, second
->denominator
);
598 completereduce(struct unittype
* unit
)
600 if (reduceunit(unit
))
609 showanswer(struct unittype
* have
, struct unittype
* want
)
611 if (compareunits(have
, want
)) {
612 printf("conformability error\n");
617 printf("\t* %.8g\n\t/ %.8g\n", have
->factor
/ want
->factor
,
618 want
->factor
/ have
->factor
);
626 "usage: units [-f unitsfile] [-q] [-v] [from-unit to-unit]\n");
632 main(int argc
, char **argv
)
635 struct unittype have
, want
;
636 char havestr
[81], wantstr
[81];
641 while ((optchar
= getopt(argc
, argv
, "vqf:")) != -1) {
650 fprintf(stderr
, "\n units version %s Copyright (c) 1993 by Adrian Mariano\n",
652 fprintf(stderr
, " This program may be freely distributed\n");
660 if (optind
!= argc
- 2 && optind
!= argc
)
665 if (optind
== argc
- 2) {
666 strlcpy(havestr
, argv
[optind
], sizeof(havestr
));
667 strlcpy(wantstr
, argv
[optind
+ 1], sizeof(wantstr
));
668 initializeunit(&have
);
669 addunit(&have
, havestr
, 0);
670 completereduce(&have
);
671 initializeunit(&want
);
672 addunit(&want
, wantstr
, 0);
673 completereduce(&want
);
674 showanswer(&have
, &want
);
678 printf("%d units, %d prefixes\n", unitcount
,
682 initializeunit(&have
);
684 printf("You have: ");
685 if (!fgets(havestr
, sizeof(havestr
), stdin
)) {
690 } while (addunit(&have
, havestr
, 0) ||
691 completereduce(&have
));
693 initializeunit(&want
);
695 printf("You want: ");
696 if (!fgets(wantstr
, sizeof(wantstr
), stdin
)) {
701 } while (addunit(&want
, wantstr
, 0) ||
702 completereduce(&want
));
703 showanswer(&have
, &want
);