GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/units/units.c Lines: 0 328 0.0 %
Date: 2017-11-07 Branches: 0 246 0.0 %

Line Branch Exec Source
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
}