1 |
|
|
/* $OpenBSD: units.c,v 1.22 2015/10/09 01:37:09 deraadt Exp $ */ |
2 |
|
|
/* $NetBSD: units.c,v 1.6 1996/04/06 06:01:03 thorpej Exp $ */ |
3 |
|
|
|
4 |
|
|
/* |
5 |
|
|
* units.c Copyright (c) 1993 by Adrian Mariano (adrian@cam.cornell.edu) |
6 |
|
|
* |
7 |
|
|
* Redistribution and use in source and binary forms, with or without |
8 |
|
|
* modification, are permitted provided that the following conditions |
9 |
|
|
* are met: |
10 |
|
|
* 1. Redistributions of source code must retain the above copyright |
11 |
|
|
* notice, this list of conditions and the following disclaimer. |
12 |
|
|
* 2. The name of the author may not be used to endorse or promote products |
13 |
|
|
* derived from this software without specific prior written permission. |
14 |
|
|
* Disclaimer: This software is provided by the author "as is". The author |
15 |
|
|
* shall not be liable for any damages caused in any way by this software. |
16 |
|
|
* |
17 |
|
|
* I would appreciate (though I do not require) receiving a copy of any |
18 |
|
|
* improvements you might make to this program. |
19 |
|
|
*/ |
20 |
|
|
|
21 |
|
|
#include <ctype.h> |
22 |
|
|
#include <stdio.h> |
23 |
|
|
#include <string.h> |
24 |
|
|
#include <stdlib.h> |
25 |
|
|
#include <unistd.h> |
26 |
|
|
#include <err.h> |
27 |
|
|
|
28 |
|
|
#define UNITSFILE "/usr/share/misc/units.lib" |
29 |
|
|
|
30 |
|
|
#define VERSION "1.0" |
31 |
|
|
|
32 |
|
|
#define MAXUNITS 1000 |
33 |
|
|
#define MAXPREFIXES 100 |
34 |
|
|
|
35 |
|
|
#define MAXSUBUNITS 500 |
36 |
|
|
|
37 |
|
|
#define PRIMITIVECHAR '!' |
38 |
|
|
|
39 |
|
|
char *powerstring = "^"; |
40 |
|
|
|
41 |
|
|
struct { |
42 |
|
|
char *uname; |
43 |
|
|
char *uval; |
44 |
|
|
} unittable[MAXUNITS]; |
45 |
|
|
|
46 |
|
|
struct unittype { |
47 |
|
|
char *numerator[MAXSUBUNITS]; |
48 |
|
|
char *denominator[MAXSUBUNITS]; |
49 |
|
|
double factor; |
50 |
|
|
}; |
51 |
|
|
|
52 |
|
|
struct { |
53 |
|
|
char *prefixname; |
54 |
|
|
char *prefixval; |
55 |
|
|
} prefixtable[MAXPREFIXES]; |
56 |
|
|
|
57 |
|
|
|
58 |
|
|
char *NULLUNIT = ""; |
59 |
|
|
|
60 |
|
|
int unitcount; |
61 |
|
|
int prefixcount; |
62 |
|
|
|
63 |
|
|
char *dupstr(char *); |
64 |
|
|
void readunits(char *); |
65 |
|
|
void initializeunit(struct unittype *); |
66 |
|
|
int addsubunit(char *[], char *); |
67 |
|
|
void showunit(struct unittype *); |
68 |
|
|
void zeroerror(void); |
69 |
|
|
int addunit(struct unittype *, char *, int); |
70 |
|
|
int compare(const void *, const void *); |
71 |
|
|
void sortunit(struct unittype *); |
72 |
|
|
void cancelunit(struct unittype *); |
73 |
|
|
char *lookupunit(char *); |
74 |
|
|
int reduceproduct(struct unittype *, int); |
75 |
|
|
int reduceunit(struct unittype *); |
76 |
|
|
int compareproducts(char **, char **); |
77 |
|
|
int compareunits(struct unittype *, struct unittype *); |
78 |
|
|
int completereduce(struct unittype *); |
79 |
|
|
void showanswer(struct unittype *, struct unittype *); |
80 |
|
|
void usage(void); |
81 |
|
|
|
82 |
|
|
char * |
83 |
|
|
dupstr(char *str) |
84 |
|
|
{ |
85 |
|
|
char *ret; |
86 |
|
|
|
87 |
|
|
ret = strdup(str); |
88 |
|
|
if (!ret) { |
89 |
|
|
fprintf(stderr, "Memory allocation error\n"); |
90 |
|
|
exit(3); |
91 |
|
|
} |
92 |
|
|
return (ret); |
93 |
|
|
} |
94 |
|
|
|
95 |
|
|
|
96 |
|
|
void |
97 |
|
|
readunits(char *userfile) |
98 |
|
|
{ |
99 |
|
|
char line[512], *lineptr; |
100 |
|
|
int len, linenum, i; |
101 |
|
|
FILE *unitfile; |
102 |
|
|
|
103 |
|
|
unitcount = 0; |
104 |
|
|
linenum = 0; |
105 |
|
|
|
106 |
|
|
if (userfile) { |
107 |
|
|
unitfile = fopen(userfile, "r"); |
108 |
|
|
if (!unitfile) { |
109 |
|
|
fprintf(stderr, "Unable to open units file '%s'\n", |
110 |
|
|
userfile); |
111 |
|
|
exit(1); |
112 |
|
|
} |
113 |
|
|
} else { |
114 |
|
|
unitfile = fopen(UNITSFILE, "r"); |
115 |
|
|
if (!unitfile) { |
116 |
|
|
fprintf(stderr, "Can't find units file '%s'\n", |
117 |
|
|
UNITSFILE); |
118 |
|
|
exit(1); |
119 |
|
|
} |
120 |
|
|
} |
121 |
|
|
while (!feof(unitfile)) { |
122 |
|
|
if (!fgets(line, sizeof(line), unitfile)) |
123 |
|
|
break; |
124 |
|
|
linenum++; |
125 |
|
|
lineptr = line; |
126 |
|
|
if (*lineptr == '/') |
127 |
|
|
continue; |
128 |
|
|
lineptr += strspn(lineptr, " \n\t"); |
129 |
|
|
len = strcspn(lineptr, " \n\t"); |
130 |
|
|
lineptr[len] = 0; |
131 |
|
|
if (!strlen(lineptr)) |
132 |
|
|
continue; |
133 |
|
|
if (lineptr[strlen(lineptr) - 1] == '-') { /* it's a prefix */ |
134 |
|
|
if (prefixcount == MAXPREFIXES) { |
135 |
|
|
fprintf(stderr, |
136 |
|
|
"Memory for prefixes exceeded in line %d\n", |
137 |
|
|
linenum); |
138 |
|
|
continue; |
139 |
|
|
} |
140 |
|
|
|
141 |
|
|
lineptr[strlen(lineptr) - 1] = 0; |
142 |
|
|
for (i = 0; i < prefixcount; i++) { |
143 |
|
|
if (!strcmp(prefixtable[i].prefixname, lineptr)) |
144 |
|
|
break; |
145 |
|
|
} |
146 |
|
|
if (i < prefixcount) { |
147 |
|
|
fprintf(stderr, "Redefinition of prefix '%s' " |
148 |
|
|
"on line %d ignored\n", lineptr, linenum); |
149 |
|
|
continue; /* skip duplicate prefix */ |
150 |
|
|
} |
151 |
|
|
|
152 |
|
|
prefixtable[prefixcount].prefixname = dupstr(lineptr); |
153 |
|
|
lineptr += len + 1; |
154 |
|
|
lineptr += strspn(lineptr, " \n\t"); |
155 |
|
|
len = strcspn(lineptr, "\n\t"); |
156 |
|
|
if (len == 0) { |
157 |
|
|
fprintf(stderr, "Unexpected end of prefix on " |
158 |
|
|
"line %d\n", linenum); |
159 |
|
|
free(prefixtable[prefixcount].prefixname); |
160 |
|
|
continue; |
161 |
|
|
} |
162 |
|
|
lineptr[len] = 0; |
163 |
|
|
prefixtable[prefixcount++].prefixval = dupstr(lineptr); |
164 |
|
|
} else { /* it's not a prefix */ |
165 |
|
|
if (unitcount == MAXUNITS) { |
166 |
|
|
fprintf(stderr, |
167 |
|
|
"Memory for units exceeded in line %d\n", |
168 |
|
|
linenum); |
169 |
|
|
continue; |
170 |
|
|
} |
171 |
|
|
|
172 |
|
|
for (i = 0; i < unitcount; i++) { |
173 |
|
|
if (!strcmp(unittable[i].uname, lineptr)) |
174 |
|
|
break; |
175 |
|
|
} |
176 |
|
|
if (i < unitcount) { |
177 |
|
|
fprintf(stderr, "Redefinition of unit '%s' " |
178 |
|
|
"on line %d ignored\n", lineptr, linenum); |
179 |
|
|
continue; /* skip duplicate unit */ |
180 |
|
|
} |
181 |
|
|
|
182 |
|
|
unittable[unitcount].uname = dupstr(lineptr); |
183 |
|
|
lineptr += len + 1; |
184 |
|
|
lineptr += strspn(lineptr, " \n\t"); |
185 |
|
|
if (!strlen(lineptr)) { |
186 |
|
|
fprintf(stderr, "Unexpected end of unit on " |
187 |
|
|
"line %d\n", linenum); |
188 |
|
|
free(unittable[unitcount].uname); |
189 |
|
|
continue; |
190 |
|
|
} |
191 |
|
|
len = strcspn(lineptr, "\n\t"); |
192 |
|
|
lineptr[len] = 0; |
193 |
|
|
unittable[unitcount++].uval = dupstr(lineptr); |
194 |
|
|
} |
195 |
|
|
} |
196 |
|
|
fclose(unitfile); |
197 |
|
|
} |
198 |
|
|
|
199 |
|
|
void |
200 |
|
|
initializeunit(struct unittype *theunit) |
201 |
|
|
{ |
202 |
|
|
theunit->factor = 1.0; |
203 |
|
|
theunit->numerator[0] = theunit->denominator[0] = NULL; |
204 |
|
|
} |
205 |
|
|
|
206 |
|
|
|
207 |
|
|
int |
208 |
|
|
addsubunit(char *product[], char *toadd) |
209 |
|
|
{ |
210 |
|
|
char **ptr; |
211 |
|
|
|
212 |
|
|
for (ptr = product; *ptr && *ptr != NULLUNIT; ptr++); |
213 |
|
|
if (ptr >= product + MAXSUBUNITS) { |
214 |
|
|
fprintf(stderr, "Memory overflow in unit reduction\n"); |
215 |
|
|
return 1; |
216 |
|
|
} |
217 |
|
|
if (!*ptr) |
218 |
|
|
*(ptr + 1) = 0; |
219 |
|
|
*ptr = dupstr(toadd); |
220 |
|
|
return 0; |
221 |
|
|
} |
222 |
|
|
|
223 |
|
|
|
224 |
|
|
void |
225 |
|
|
showunit(struct unittype *theunit) |
226 |
|
|
{ |
227 |
|
|
char **ptr; |
228 |
|
|
int printedslash; |
229 |
|
|
int counter = 1; |
230 |
|
|
|
231 |
|
|
printf("\t%.8g", theunit->factor); |
232 |
|
|
for (ptr = theunit->numerator; *ptr; ptr++) { |
233 |
|
|
if (ptr > theunit->numerator && **ptr && |
234 |
|
|
!strcmp(*ptr, *(ptr - 1))) |
235 |
|
|
counter++; |
236 |
|
|
else { |
237 |
|
|
if (counter > 1) |
238 |
|
|
printf("%s%d", powerstring, counter); |
239 |
|
|
if (**ptr) |
240 |
|
|
printf(" %s", *ptr); |
241 |
|
|
counter = 1; |
242 |
|
|
} |
243 |
|
|
} |
244 |
|
|
if (counter > 1) |
245 |
|
|
printf("%s%d", powerstring, counter); |
246 |
|
|
counter = 1; |
247 |
|
|
printedslash = 0; |
248 |
|
|
for (ptr = theunit->denominator; *ptr; ptr++) { |
249 |
|
|
if (ptr > theunit->denominator && **ptr && |
250 |
|
|
!strcmp(*ptr, *(ptr - 1))) |
251 |
|
|
counter++; |
252 |
|
|
else { |
253 |
|
|
if (counter > 1) |
254 |
|
|
printf("%s%d", powerstring, counter); |
255 |
|
|
if (**ptr) { |
256 |
|
|
if (!printedslash) |
257 |
|
|
printf(" /"); |
258 |
|
|
printedslash = 1; |
259 |
|
|
printf(" %s", *ptr); |
260 |
|
|
} |
261 |
|
|
counter = 1; |
262 |
|
|
} |
263 |
|
|
} |
264 |
|
|
if (counter > 1) |
265 |
|
|
printf("%s%d", powerstring, counter); |
266 |
|
|
printf("\n"); |
267 |
|
|
} |
268 |
|
|
|
269 |
|
|
|
270 |
|
|
void |
271 |
|
|
zeroerror(void) |
272 |
|
|
{ |
273 |
|
|
fprintf(stderr, "Unit reduces to zero\n"); |
274 |
|
|
} |
275 |
|
|
|
276 |
|
|
/* |
277 |
|
|
Adds the specified string to the unit. |
278 |
|
|
Flip is 0 for adding normally, 1 for adding reciprocal. |
279 |
|
|
|
280 |
|
|
Returns 0 for successful addition, nonzero on error. |
281 |
|
|
*/ |
282 |
|
|
|
283 |
|
|
int |
284 |
|
|
addunit(struct unittype *theunit, char *toadd, int flip) |
285 |
|
|
{ |
286 |
|
|
char *scratch, *savescr; |
287 |
|
|
char *item; |
288 |
|
|
char *divider, *slash; |
289 |
|
|
int doingtop; |
290 |
|
|
|
291 |
|
|
savescr = scratch = dupstr(toadd); |
292 |
|
|
for (slash = scratch + 1; *slash; slash++) |
293 |
|
|
if (*slash == '-' && |
294 |
|
|
(tolower((unsigned char)*(slash - 1)) != 'e' || |
295 |
|
|
!strchr(".0123456789", *(slash + 1)))) |
296 |
|
|
*slash = ' '; |
297 |
|
|
slash = strchr(scratch, '/'); |
298 |
|
|
if (slash) |
299 |
|
|
*slash = 0; |
300 |
|
|
doingtop = 1; |
301 |
|
|
do { |
302 |
|
|
item = strtok(scratch, " *\t\n/"); |
303 |
|
|
while (item) { |
304 |
|
|
if (strchr("0123456789.", *item)) { /* item is a number */ |
305 |
|
|
double num; |
306 |
|
|
|
307 |
|
|
divider = strchr(item, '|'); |
308 |
|
|
if (divider) { |
309 |
|
|
*divider = 0; |
310 |
|
|
num = atof(item); |
311 |
|
|
if (!num) { |
312 |
|
|
zeroerror(); |
313 |
|
|
free(savescr); |
314 |
|
|
return 1; |
315 |
|
|
} |
316 |
|
|
if (doingtop ^ flip) |
317 |
|
|
theunit->factor *= num; |
318 |
|
|
else |
319 |
|
|
theunit->factor /= num; |
320 |
|
|
num = atof(divider + 1); |
321 |
|
|
if (!num) { |
322 |
|
|
zeroerror(); |
323 |
|
|
free(savescr); |
324 |
|
|
return 1; |
325 |
|
|
} |
326 |
|
|
if (doingtop ^ flip) |
327 |
|
|
theunit->factor /= num; |
328 |
|
|
else |
329 |
|
|
theunit->factor *= num; |
330 |
|
|
} else { |
331 |
|
|
num = atof(item); |
332 |
|
|
if (!num) { |
333 |
|
|
zeroerror(); |
334 |
|
|
free(savescr); |
335 |
|
|
return 1; |
336 |
|
|
} |
337 |
|
|
if (doingtop ^ flip) |
338 |
|
|
theunit->factor *= num; |
339 |
|
|
else |
340 |
|
|
theunit->factor /= num; |
341 |
|
|
|
342 |
|
|
} |
343 |
|
|
} else { /* item is not a number */ |
344 |
|
|
int repeat = 1; |
345 |
|
|
|
346 |
|
|
if (strchr("23456789", |
347 |
|
|
item[strlen(item) - 1])) { |
348 |
|
|
repeat = item[strlen(item) - 1] - '0'; |
349 |
|
|
item[strlen(item) - 1] = 0; |
350 |
|
|
} |
351 |
|
|
for (; repeat; repeat--) |
352 |
|
|
if (addsubunit(doingtop ^ flip |
353 |
|
|
? theunit->numerator |
354 |
|
|
: theunit->denominator, item)) { |
355 |
|
|
free(savescr); |
356 |
|
|
return 1; |
357 |
|
|
} |
358 |
|
|
} |
359 |
|
|
item = strtok(NULL, " *\t/\n"); |
360 |
|
|
} |
361 |
|
|
doingtop--; |
362 |
|
|
if (slash) { |
363 |
|
|
scratch = slash + 1; |
364 |
|
|
} else |
365 |
|
|
doingtop--; |
366 |
|
|
} while (doingtop >= 0); |
367 |
|
|
free(savescr); |
368 |
|
|
return 0; |
369 |
|
|
} |
370 |
|
|
|
371 |
|
|
|
372 |
|
|
int |
373 |
|
|
compare(const void *item1, const void *item2) |
374 |
|
|
{ |
375 |
|
|
return strcmp(*(char **) item1, *(char **) item2); |
376 |
|
|
} |
377 |
|
|
|
378 |
|
|
|
379 |
|
|
void |
380 |
|
|
sortunit(struct unittype *theunit) |
381 |
|
|
{ |
382 |
|
|
char **ptr; |
383 |
|
|
int count; |
384 |
|
|
|
385 |
|
|
for (count = 0, ptr = theunit->numerator; *ptr; ptr++, count++); |
386 |
|
|
qsort(theunit->numerator, count, sizeof(char *), compare); |
387 |
|
|
for (count = 0, ptr = theunit->denominator; *ptr; ptr++, count++); |
388 |
|
|
qsort(theunit->denominator, count, sizeof(char *), compare); |
389 |
|
|
} |
390 |
|
|
|
391 |
|
|
|
392 |
|
|
void |
393 |
|
|
cancelunit(struct unittype *theunit) |
394 |
|
|
{ |
395 |
|
|
char **den, **num; |
396 |
|
|
int comp; |
397 |
|
|
|
398 |
|
|
den = theunit->denominator; |
399 |
|
|
num = theunit->numerator; |
400 |
|
|
|
401 |
|
|
while (*num && *den) { |
402 |
|
|
comp = strcmp(*den, *num); |
403 |
|
|
if (!comp) { |
404 |
|
|
*den++ = NULLUNIT; |
405 |
|
|
*num++ = NULLUNIT; |
406 |
|
|
} else if (comp < 0) |
407 |
|
|
den++; |
408 |
|
|
else |
409 |
|
|
num++; |
410 |
|
|
} |
411 |
|
|
} |
412 |
|
|
|
413 |
|
|
|
414 |
|
|
|
415 |
|
|
|
416 |
|
|
/* |
417 |
|
|
Looks up the definition for the specified unit. |
418 |
|
|
Returns a pointer to the definition or a null pointer |
419 |
|
|
if the specified unit does not appear in the units table. |
420 |
|
|
*/ |
421 |
|
|
|
422 |
|
|
static char buffer[500]; /* buffer for lookupunit answers with |
423 |
|
|
prefixes */ |
424 |
|
|
|
425 |
|
|
char * |
426 |
|
|
lookupunit(char *unit) |
427 |
|
|
{ |
428 |
|
|
size_t len; |
429 |
|
|
int i; |
430 |
|
|
char *copy; |
431 |
|
|
|
432 |
|
|
for (i = 0; i < unitcount; i++) { |
433 |
|
|
if (!strcmp(unittable[i].uname, unit)) |
434 |
|
|
return unittable[i].uval; |
435 |
|
|
} |
436 |
|
|
|
437 |
|
|
len = strlen(unit); |
438 |
|
|
if (len == 0) |
439 |
|
|
return NULL; |
440 |
|
|
if (unit[len - 1] == '^') { |
441 |
|
|
copy = dupstr(unit); |
442 |
|
|
copy[len - 1] = '\0'; |
443 |
|
|
for (i = 0; i < unitcount; i++) { |
444 |
|
|
if (!strcmp(unittable[i].uname, copy)) { |
445 |
|
|
strlcpy(buffer, copy, sizeof(buffer)); |
446 |
|
|
free(copy); |
447 |
|
|
return buffer; |
448 |
|
|
} |
449 |
|
|
} |
450 |
|
|
free(copy); |
451 |
|
|
} |
452 |
|
|
if (unit[len - 1] == 's') { |
453 |
|
|
copy = dupstr(unit); |
454 |
|
|
copy[len - 1] = '\0'; |
455 |
|
|
--len; |
456 |
|
|
for (i = 0; i < unitcount; i++) { |
457 |
|
|
if (!strcmp(unittable[i].uname, copy)) { |
458 |
|
|
strlcpy(buffer, copy, sizeof(buffer)); |
459 |
|
|
free(copy); |
460 |
|
|
return buffer; |
461 |
|
|
} |
462 |
|
|
} |
463 |
|
|
if (len != 0 && copy[len - 1] == 'e') { |
464 |
|
|
copy[len - 1] = 0; |
465 |
|
|
for (i = 0; i < unitcount; i++) { |
466 |
|
|
if (!strcmp(unittable[i].uname, copy)) { |
467 |
|
|
strlcpy(buffer, copy, sizeof(buffer)); |
468 |
|
|
free(copy); |
469 |
|
|
return buffer; |
470 |
|
|
} |
471 |
|
|
} |
472 |
|
|
} |
473 |
|
|
free(copy); |
474 |
|
|
} |
475 |
|
|
for (i = 0; i < prefixcount; i++) { |
476 |
|
|
len = strlen(prefixtable[i].prefixname); |
477 |
|
|
if (!strncmp(prefixtable[i].prefixname, unit, len)) { |
478 |
|
|
if (!strlen(unit + len) || lookupunit(unit + len)) { |
479 |
|
|
snprintf(buffer, sizeof(buffer), "%s %s", |
480 |
|
|
prefixtable[i].prefixval, unit + len); |
481 |
|
|
return buffer; |
482 |
|
|
} |
483 |
|
|
} |
484 |
|
|
} |
485 |
|
|
return NULL; |
486 |
|
|
} |
487 |
|
|
|
488 |
|
|
|
489 |
|
|
|
490 |
|
|
/* |
491 |
|
|
reduces a product of symbolic units to primitive units. |
492 |
|
|
The three low bits are used to return flags: |
493 |
|
|
|
494 |
|
|
bit 0 (1) set on if reductions were performed without error. |
495 |
|
|
bit 1 (2) set on if no reductions are performed. |
496 |
|
|
bit 2 (4) set on if an unknown unit is discovered. |
497 |
|
|
*/ |
498 |
|
|
|
499 |
|
|
|
500 |
|
|
#define ERROR 4 |
501 |
|
|
|
502 |
|
|
int |
503 |
|
|
reduceproduct(struct unittype *theunit, int flip) |
504 |
|
|
{ |
505 |
|
|
char *toadd, **product; |
506 |
|
|
int didsomething = 2; |
507 |
|
|
|
508 |
|
|
if (flip) |
509 |
|
|
product = theunit->denominator; |
510 |
|
|
else |
511 |
|
|
product = theunit->numerator; |
512 |
|
|
|
513 |
|
|
for (; *product; product++) { |
514 |
|
|
|
515 |
|
|
for (;;) { |
516 |
|
|
if (!strlen(*product)) |
517 |
|
|
break; |
518 |
|
|
toadd = lookupunit(*product); |
519 |
|
|
if (!toadd) { |
520 |
|
|
printf("unknown unit '%s'\n", *product); |
521 |
|
|
return ERROR; |
522 |
|
|
} |
523 |
|
|
if (strchr(toadd, PRIMITIVECHAR)) |
524 |
|
|
break; |
525 |
|
|
didsomething = 1; |
526 |
|
|
if (*product != NULLUNIT) { |
527 |
|
|
free(*product); |
528 |
|
|
*product = NULLUNIT; |
529 |
|
|
} |
530 |
|
|
if (addunit(theunit, toadd, flip)) |
531 |
|
|
return ERROR; |
532 |
|
|
} |
533 |
|
|
} |
534 |
|
|
return didsomething; |
535 |
|
|
} |
536 |
|
|
|
537 |
|
|
|
538 |
|
|
/* |
539 |
|
|
Reduces numerator and denominator of the specified unit. |
540 |
|
|
Returns 0 on success, or 1 on unknown unit error. |
541 |
|
|
*/ |
542 |
|
|
|
543 |
|
|
int |
544 |
|
|
reduceunit(struct unittype *theunit) |
545 |
|
|
{ |
546 |
|
|
int ret; |
547 |
|
|
|
548 |
|
|
ret = 1; |
549 |
|
|
while (ret & 1) { |
550 |
|
|
ret = reduceproduct(theunit, 0) | reduceproduct(theunit, 1); |
551 |
|
|
if (ret & 4) |
552 |
|
|
return 1; |
553 |
|
|
} |
554 |
|
|
return 0; |
555 |
|
|
} |
556 |
|
|
|
557 |
|
|
|
558 |
|
|
int |
559 |
|
|
compareproducts(char **one, char **two) |
560 |
|
|
{ |
561 |
|
|
while (*one || *two) { |
562 |
|
|
if (!*one && *two != NULLUNIT) |
563 |
|
|
return 1; |
564 |
|
|
if (!*two && *one != NULLUNIT) |
565 |
|
|
return 1; |
566 |
|
|
if (*one == NULLUNIT) |
567 |
|
|
one++; |
568 |
|
|
else if (*two == NULLUNIT) |
569 |
|
|
two++; |
570 |
|
|
else if (strcmp(*one, *two)) |
571 |
|
|
return 1; |
572 |
|
|
else |
573 |
|
|
one++, two++; |
574 |
|
|
} |
575 |
|
|
return 0; |
576 |
|
|
} |
577 |
|
|
|
578 |
|
|
|
579 |
|
|
/* Return zero if units are compatible, nonzero otherwise */ |
580 |
|
|
|
581 |
|
|
int |
582 |
|
|
compareunits(struct unittype *first, struct unittype *second) |
583 |
|
|
{ |
584 |
|
|
return compareproducts(first->numerator, second->numerator) || |
585 |
|
|
compareproducts(first->denominator, second->denominator); |
586 |
|
|
} |
587 |
|
|
|
588 |
|
|
|
589 |
|
|
int |
590 |
|
|
completereduce(struct unittype *unit) |
591 |
|
|
{ |
592 |
|
|
if (reduceunit(unit)) |
593 |
|
|
return 1; |
594 |
|
|
sortunit(unit); |
595 |
|
|
cancelunit(unit); |
596 |
|
|
return 0; |
597 |
|
|
} |
598 |
|
|
|
599 |
|
|
|
600 |
|
|
void |
601 |
|
|
showanswer(struct unittype *have, struct unittype *want) |
602 |
|
|
{ |
603 |
|
|
if (compareunits(have, want)) { |
604 |
|
|
printf("conformability error\n"); |
605 |
|
|
showunit(have); |
606 |
|
|
showunit(want); |
607 |
|
|
} else |
608 |
|
|
printf("\t* %.8g\n\t/ %.8g\n", have->factor / want->factor, |
609 |
|
|
want->factor / have->factor); |
610 |
|
|
} |
611 |
|
|
|
612 |
|
|
|
613 |
|
|
void |
614 |
|
|
usage(void) |
615 |
|
|
{ |
616 |
|
|
fprintf(stderr, |
617 |
|
|
"usage: units [-qv] [-f filename] [[count] from-unit to-unit]\n"); |
618 |
|
|
exit(3); |
619 |
|
|
} |
620 |
|
|
|
621 |
|
|
|
622 |
|
|
int |
623 |
|
|
main(int argc, char **argv) |
624 |
|
|
{ |
625 |
|
|
|
626 |
|
|
struct unittype have, want; |
627 |
|
|
char havestr[81], wantstr[81]; |
628 |
|
|
int optchar; |
629 |
|
|
char *userfile = 0; |
630 |
|
|
int quiet = 0; |
631 |
|
|
|
632 |
|
|
extern char *optarg; |
633 |
|
|
extern int optind; |
634 |
|
|
|
635 |
|
|
if (pledge("stdio rpath flock cpath wpath", NULL) == -1) |
636 |
|
|
err(1, "pledge"); |
637 |
|
|
|
638 |
|
|
while ((optchar = getopt(argc, argv, "vqf:")) != -1) { |
639 |
|
|
switch (optchar) { |
640 |
|
|
case 'f': |
641 |
|
|
userfile = optarg; |
642 |
|
|
break; |
643 |
|
|
case 'q': |
644 |
|
|
quiet = 1; |
645 |
|
|
break; |
646 |
|
|
case 'v': |
647 |
|
|
fprintf(stderr, |
648 |
|
|
"units version %s Copyright (c) 1993 by Adrian Mariano\n", |
649 |
|
|
VERSION); |
650 |
|
|
fprintf(stderr, |
651 |
|
|
"This program may be freely distributed\n"); |
652 |
|
|
usage(); |
653 |
|
|
default: |
654 |
|
|
usage(); |
655 |
|
|
break; |
656 |
|
|
} |
657 |
|
|
} |
658 |
|
|
|
659 |
|
|
argc -= optind; |
660 |
|
|
argv += optind; |
661 |
|
|
|
662 |
|
|
if (argc != 3 && argc != 2 && argc != 0) |
663 |
|
|
usage(); |
664 |
|
|
|
665 |
|
|
readunits(userfile); |
666 |
|
|
|
667 |
|
|
if (pledge("stdio flock rpath cpath wpath", NULL) == -1) |
668 |
|
|
err(1, "pledge"); |
669 |
|
|
|
670 |
|
|
if (argc == 3) { |
671 |
|
|
strlcpy(havestr, argv[0], sizeof(havestr)); |
672 |
|
|
strlcat(havestr, " ", sizeof(havestr)); |
673 |
|
|
strlcat(havestr, argv[1], sizeof(havestr)); |
674 |
|
|
argc--; |
675 |
|
|
argv++; |
676 |
|
|
argv[0] = havestr; |
677 |
|
|
} |
678 |
|
|
|
679 |
|
|
if (argc == 2) { |
680 |
|
|
strlcpy(havestr, argv[0], sizeof(havestr)); |
681 |
|
|
strlcpy(wantstr, argv[1], sizeof(wantstr)); |
682 |
|
|
initializeunit(&have); |
683 |
|
|
addunit(&have, havestr, 0); |
684 |
|
|
completereduce(&have); |
685 |
|
|
initializeunit(&want); |
686 |
|
|
addunit(&want, wantstr, 0); |
687 |
|
|
completereduce(&want); |
688 |
|
|
showanswer(&have, &want); |
689 |
|
|
} else { |
690 |
|
|
if (!quiet) |
691 |
|
|
printf("%d units, %d prefixes\n", unitcount, |
692 |
|
|
prefixcount); |
693 |
|
|
for (;;) { |
694 |
|
|
do { |
695 |
|
|
initializeunit(&have); |
696 |
|
|
if (!quiet) |
697 |
|
|
printf("You have: "); |
698 |
|
|
if (!fgets(havestr, sizeof(havestr), stdin)) { |
699 |
|
|
if (!quiet) |
700 |
|
|
putchar('\n'); |
701 |
|
|
exit(0); |
702 |
|
|
} |
703 |
|
|
} while (addunit(&have, havestr, 0) || |
704 |
|
|
completereduce(&have)); |
705 |
|
|
do { |
706 |
|
|
initializeunit(&want); |
707 |
|
|
if (!quiet) |
708 |
|
|
printf("You want: "); |
709 |
|
|
if (!fgets(wantstr, sizeof(wantstr), stdin)) { |
710 |
|
|
if (!quiet) |
711 |
|
|
putchar('\n'); |
712 |
|
|
exit(0); |
713 |
|
|
} |
714 |
|
|
} while (addunit(&want, wantstr, 0) || |
715 |
|
|
completereduce(&want)); |
716 |
|
|
showanswer(&have, &want); |
717 |
|
|
} |
718 |
|
|
} |
719 |
|
|
return (0); |
720 |
|
|
} |