]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/units/units.c
Bring down 0.4.5 vendor files and other catchups with the distribution tarball.
[FreeBSD/FreeBSD.git] / usr.bin / units / units.c
1 /*
2  * units.c   Copyright (c) 1993 by Adrian Mariano (adrian@cam.cornell.edu)
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
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.
13  *
14  * I would appreciate (though I do not require) receiving a copy of any
15  * improvements you might make to this program.
16  */
17
18 #ifndef lint
19 static const char rcsid[] =
20   "$FreeBSD$";
21 #endif /* not lint */
22
23 #include <ctype.h>
24 #include <err.h>
25 #include <errno.h>
26 #include <histedit.h>
27 #include <getopt.h>
28 #include <stdbool.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <unistd.h>
33
34 #include <sys/capsicum.h>
35
36 #include "pathnames.h"
37
38 #ifndef UNITSFILE
39 #define UNITSFILE _PATH_UNITSLIB
40 #endif
41
42 #define MAXUNITS 1000
43 #define MAXPREFIXES 100
44
45 #define MAXSUBUNITS 500
46
47 #define PRIMITIVECHAR '!'
48
49 static const char *powerstring = "^";
50
51 static struct {
52         char *uname;
53         char *uval;
54 }      unittable[MAXUNITS];
55
56 struct unittype {
57         char *numerator[MAXSUBUNITS];
58         char *denominator[MAXSUBUNITS];
59         double factor;
60         double offset;
61         int quantity;
62 };
63
64 static struct {
65         char *prefixname;
66         char *prefixval;
67 }      prefixtable[MAXPREFIXES];
68
69
70 static char NULLUNIT[] = "";
71
72 #define SEPARATOR      ":"
73
74 static int unitcount;
75 static int prefixcount;
76 static bool verbose = false;
77 static bool terse = false;
78 static const char * outputformat;
79 static const char * havestr;
80 static const char * wantstr;
81
82 static int       addsubunit(char *product[], char *toadd);
83 static int       addunit(struct unittype *theunit, const char *toadd, int flip, int quantity);
84 static void      cancelunit(struct unittype * theunit);
85 static int       compare(const void *item1, const void *item2);
86 static int       compareproducts(char **one, char **two);
87 static int       compareunits(struct unittype * first, struct unittype * second);
88 static int       completereduce(struct unittype * unit);
89 static char     *dupstr(const char *str);
90 static void      initializeunit(struct unittype * theunit);
91 static char     *lookupunit(const char *unit);
92 static void      readunits(const char *userfile);
93 static int       reduceproduct(struct unittype * theunit, int flip);
94 static int       reduceunit(struct unittype * theunit);
95 static void      showanswer(struct unittype * have, struct unittype * want);
96 static void      showunit(struct unittype * theunit);
97 static void      sortunit(struct unittype * theunit);
98 static void      usage(void);
99 static void      zeroerror(void);
100
101 static const char* promptstr = "";
102
103 static const char * prompt(EditLine *e __unused) {
104         return promptstr;
105 }
106
107 static char *
108 dupstr(const char *str)
109 {
110         char *ret;
111
112         ret = strdup(str);
113         if (!ret)
114                 err(3, "dupstr");
115         return (ret);
116 }
117
118
119 static void 
120 readunits(const char *userfile)
121 {
122         FILE *unitfile;
123         char line[512], *lineptr;
124         int len, linenum, i;
125         cap_rights_t unitfilerights;
126
127         unitcount = 0;
128         linenum = 0;
129
130         if (userfile) {
131                 unitfile = fopen(userfile, "rt");
132                 if (!unitfile)
133                         errx(1, "unable to open units file '%s'", userfile);
134         }
135         else {
136                 unitfile = fopen(UNITSFILE, "rt");
137                 if (!unitfile) {
138                         char *direc, *env;
139                         char filename[1000];
140
141                         env = getenv("PATH");
142                         if (env) {
143                                 direc = strtok(env, SEPARATOR);
144                                 while (direc) {
145                                         snprintf(filename, sizeof(filename),
146                                             "%s/%s", direc, UNITSFILE);
147                                         unitfile = fopen(filename, "rt");
148                                         if (unitfile)
149                                                 break;
150                                         direc = strtok(NULL, SEPARATOR);
151                                 }
152                         }
153                         if (!unitfile)
154                                 errx(1, "can't find units file '%s'", UNITSFILE);
155                 }
156         }
157         cap_rights_init(&unitfilerights, CAP_READ, CAP_FSTAT);
158         if (cap_rights_limit(fileno(unitfile), &unitfilerights) < 0
159                 && errno != ENOSYS)
160                 err(1, "cap_rights_limit() failed");
161         while (!feof(unitfile)) {
162                 if (!fgets(line, sizeof(line), unitfile))
163                         break;
164                 linenum++;
165                 lineptr = line;
166                 if (*lineptr == '/' || *lineptr == '#')
167                         continue;
168                 lineptr += strspn(lineptr, " \n\t");
169                 len = strcspn(lineptr, " \n\t");
170                 lineptr[len] = 0;
171                 if (!strlen(lineptr))
172                         continue;
173                 if (lineptr[strlen(lineptr) - 1] == '-') { /* it's a prefix */
174                         if (prefixcount == MAXPREFIXES) {
175                                 warnx("memory for prefixes exceeded in line %d", linenum);
176                                 continue;
177                         }
178                         lineptr[strlen(lineptr) - 1] = 0;
179                         prefixtable[prefixcount].prefixname = dupstr(lineptr);
180                         for (i = 0; i < prefixcount; i++)
181                                 if (!strcmp(prefixtable[i].prefixname, lineptr)) {
182                                         warnx("redefinition of prefix '%s' on line %d ignored",
183                                             lineptr, linenum);
184                                         continue;
185                                 }
186                         lineptr += len + 1;
187                         lineptr += strspn(lineptr, " \n\t");
188                         len = strcspn(lineptr, "\n\t");
189                         if (len == 0) {
190                                 warnx("unexpected end of prefix on line %d",
191                                     linenum);
192                                 continue;
193                         }
194                         lineptr[len] = 0;
195                         prefixtable[prefixcount++].prefixval = dupstr(lineptr);
196                 }
197                 else {          /* it's not a prefix */
198                         if (unitcount == MAXUNITS) {
199                                 warnx("memory for units exceeded in line %d", linenum);
200                                 continue;
201                         }
202                         unittable[unitcount].uname = dupstr(lineptr);
203                         for (i = 0; i < unitcount; i++)
204                                 if (!strcmp(unittable[i].uname, lineptr)) {
205                                         warnx("redefinition of unit '%s' on line %d ignored",
206                                             lineptr, linenum);
207                                         continue;
208                                 }
209                         lineptr += len + 1;
210                         lineptr += strspn(lineptr, " \n\t");
211                         if (!strlen(lineptr)) {
212                                 warnx("unexpected end of unit on line %d",
213                                     linenum);
214                                 continue;
215                         }
216                         len = strcspn(lineptr, "\n\t");
217                         lineptr[len] = 0;
218                         unittable[unitcount++].uval = dupstr(lineptr);
219                 }
220         }
221         fclose(unitfile);
222 }
223
224 static void 
225 initializeunit(struct unittype * theunit)
226 {
227         theunit->numerator[0] = theunit->denominator[0] = NULL;
228         theunit->factor = 1.0;
229         theunit->offset = 0.0;
230         theunit->quantity = 0;
231 }
232
233
234 static int 
235 addsubunit(char *product[], char *toadd)
236 {
237         char **ptr;
238
239         for (ptr = product; *ptr && *ptr != NULLUNIT; ptr++);
240         if (ptr >= product + MAXSUBUNITS) {
241                 warnx("memory overflow in unit reduction");
242                 return 1;
243         }
244         if (!*ptr)
245                 *(ptr + 1) = NULL;
246         *ptr = dupstr(toadd);
247         return 0;
248 }
249
250
251 static void 
252 showunit(struct unittype * theunit)
253 {
254         char **ptr;
255         int printedslash;
256         int counter = 1;
257
258         printf("%.8g", theunit->factor);
259         if (theunit->offset)
260                 printf("&%.8g", theunit->offset);
261         for (ptr = theunit->numerator; *ptr; ptr++) {
262                 if (ptr > theunit->numerator && **ptr &&
263                     !strcmp(*ptr, *(ptr - 1)))
264                         counter++;
265                 else {
266                         if (counter > 1)
267                                 printf("%s%d", powerstring, counter);
268                         if (**ptr)
269                                 printf(" %s", *ptr);
270                         counter = 1;
271                 }
272         }
273         if (counter > 1)
274                 printf("%s%d", powerstring, counter);
275         counter = 1;
276         printedslash = 0;
277         for (ptr = theunit->denominator; *ptr; ptr++) {
278                 if (ptr > theunit->denominator && **ptr &&
279                     !strcmp(*ptr, *(ptr - 1)))
280                         counter++;
281                 else {
282                         if (counter > 1)
283                                 printf("%s%d", powerstring, counter);
284                         if (**ptr) {
285                                 if (!printedslash)
286                                         printf(" /");
287                                 printedslash = 1;
288                                 printf(" %s", *ptr);
289                         }
290                         counter = 1;
291                 }
292         }
293         if (counter > 1)
294                 printf("%s%d", powerstring, counter);
295         printf("\n");
296 }
297
298
299 void 
300 zeroerror(void)
301 {
302         warnx("unit reduces to zero");
303 }
304
305 /*
306    Adds the specified string to the unit.
307    Flip is 0 for adding normally, 1 for adding reciprocal.
308    Quantity is 1 if this is a quantity to be converted rather than a pure unit.
309
310    Returns 0 for successful addition, nonzero on error.
311 */
312
313 static int 
314 addunit(struct unittype * theunit, const char *toadd, int flip, int quantity)
315 {
316         char *scratch, *savescr;
317         char *item;
318         char *divider, *slash, *offset;
319         int doingtop;
320
321         if (!strlen(toadd))
322                 return 1;
323         
324         savescr = scratch = dupstr(toadd);
325         for (slash = scratch + 1; *slash; slash++)
326                 if (*slash == '-' &&
327                     (tolower(*(slash - 1)) != 'e' ||
328                     !strchr(".0123456789", *(slash + 1))))
329                         *slash = ' ';
330         slash = strchr(scratch, '/');
331         if (slash)
332                 *slash = 0;
333         doingtop = 1;
334         do {
335                 item = strtok(scratch, " *\t\n/");
336                 while (item) {
337                         if (strchr("0123456789.", *item)) { /* item is a number */
338                                 double num, offsetnum;
339
340                                 if (quantity)
341                                         theunit->quantity = 1;
342
343                                 offset = strchr(item, '&');
344                                 if (offset) {
345                                         *offset = 0;
346                                         offsetnum = atof(offset+1);
347                                 } else
348                                         offsetnum = 0.0;
349
350                                 divider = strchr(item, '|');
351                                 if (divider) {
352                                         *divider = 0;
353                                         num = atof(item);
354                                         if (!num) {
355                                                 zeroerror();
356                                                 return 1;
357                                         }
358                                         if (doingtop ^ flip) {
359                                                 theunit->factor *= num;
360                                                 theunit->offset *= num;
361                                         } else {
362                                                 theunit->factor /= num;
363                                                 theunit->offset /= num;
364                                         }
365                                         num = atof(divider + 1);
366                                         if (!num) {
367                                                 zeroerror();
368                                                 return 1;
369                                         }
370                                         if (doingtop ^ flip) {
371                                                 theunit->factor /= num;
372                                                 theunit->offset /= num;
373                                         } else {
374                                                 theunit->factor *= num;
375                                                 theunit->offset *= num;
376                                         }
377                                 }
378                                 else {
379                                         num = atof(item);
380                                         if (!num) {
381                                                 zeroerror();
382                                                 return 1;
383                                         }
384                                         if (doingtop ^ flip) {
385                                                 theunit->factor *= num;
386                                                 theunit->offset *= num;
387                                         } else {
388                                                 theunit->factor /= num;
389                                                 theunit->offset /= num;
390                                         }
391                                 }
392                                 if (doingtop ^ flip)
393                                         theunit->offset += offsetnum;
394                         }
395                         else {  /* item is not a number */
396                                 int repeat = 1;
397
398                                 if (strchr("23456789",
399                                     item[strlen(item) - 1])) {
400                                         repeat = item[strlen(item) - 1] - '0';
401                                         item[strlen(item) - 1] = 0;
402                                 }
403                                 for (; repeat; repeat--)
404                                         if (addsubunit(doingtop ^ flip ? theunit->numerator : theunit->denominator, item))
405                                                 return 1;
406                         }
407                         item = strtok(NULL, " *\t/\n");
408                 }
409                 doingtop--;
410                 if (slash) {
411                         scratch = slash + 1;
412                 }
413                 else
414                         doingtop--;
415         } while (doingtop >= 0);
416         free(savescr);
417         return 0;
418 }
419
420
421 static int 
422 compare(const void *item1, const void *item2)
423 {
424         return strcmp(*(const char * const *)item1, *(const char * const *)item2);
425 }
426
427
428 static void 
429 sortunit(struct unittype * theunit)
430 {
431         char **ptr;
432         unsigned int count;
433
434         for (count = 0, ptr = theunit->numerator; *ptr; ptr++, count++);
435         qsort(theunit->numerator, count, sizeof(char *), compare);
436         for (count = 0, ptr = theunit->denominator; *ptr; ptr++, count++);
437         qsort(theunit->denominator, count, sizeof(char *), compare);
438 }
439
440
441 void 
442 cancelunit(struct unittype * theunit)
443 {
444         char **den, **num;
445         int comp;
446
447         den = theunit->denominator;
448         num = theunit->numerator;
449
450         while (*num && *den) {
451                 comp = strcmp(*den, *num);
452                 if (!comp) {
453 /*      if (*den!=NULLUNIT) free(*den);
454       if (*num!=NULLUNIT) free(*num);*/
455                         *den++ = NULLUNIT;
456                         *num++ = NULLUNIT;
457                 }
458                 else if (comp < 0)
459                         den++;
460                 else
461                         num++;
462         }
463 }
464
465
466
467
468 /*
469    Looks up the definition for the specified unit.
470    Returns a pointer to the definition or a null pointer
471    if the specified unit does not appear in the units table.
472 */
473
474 static char buffer[100];        /* buffer for lookupunit answers with
475                                    prefixes */
476
477 char *
478 lookupunit(const char *unit)
479 {
480         int i;
481         char *copy;
482
483         for (i = 0; i < unitcount; i++) {
484                 if (!strcmp(unittable[i].uname, unit))
485                         return unittable[i].uval;
486         }
487
488         if (unit[strlen(unit) - 1] == '^') {
489                 copy = dupstr(unit);
490                 copy[strlen(copy) - 1] = 0;
491                 for (i = 0; i < unitcount; i++) {
492                         if (!strcmp(unittable[i].uname, copy)) {
493                                 strlcpy(buffer, copy, sizeof(buffer));
494                                 free(copy);
495                                 return buffer;
496                         }
497                 }
498                 free(copy);
499         }
500         if (unit[strlen(unit) - 1] == 's') {
501                 copy = dupstr(unit);
502                 copy[strlen(copy) - 1] = 0;
503                 for (i = 0; i < unitcount; i++) {
504                         if (!strcmp(unittable[i].uname, copy)) {
505                                 strlcpy(buffer, copy, sizeof(buffer));
506                                 free(copy);
507                                 return buffer;
508                         }
509                 }
510                 if (copy[strlen(copy) - 1] == 'e') {
511                         copy[strlen(copy) - 1] = 0;
512                         for (i = 0; i < unitcount; i++) {
513                                 if (!strcmp(unittable[i].uname, copy)) {
514                                         strlcpy(buffer, copy, sizeof(buffer));
515                                         free(copy);
516                                         return buffer;
517                                 }
518                         }
519                 }
520                 free(copy);
521         }
522         for (i = 0; i < prefixcount; i++) {
523                 size_t len = strlen(prefixtable[i].prefixname);
524                 if (!strncmp(prefixtable[i].prefixname, unit, len)) {
525                         if (!strlen(unit + len) || lookupunit(unit + len)) {
526                                 snprintf(buffer, sizeof(buffer), "%s %s",
527                                     prefixtable[i].prefixval, unit + len);
528                                 return buffer;
529                         }
530                 }
531         }
532         return 0;
533 }
534
535
536
537 /*
538    reduces a product of symbolic units to primitive units.
539    The three low bits are used to return flags:
540
541      bit 0 (1) set on if reductions were performed without error.
542      bit 1 (2) set on if no reductions are performed.
543      bit 2 (4) set on if an unknown unit is discovered.
544 */
545
546
547 #define ERROR 4
548
549 static int 
550 reduceproduct(struct unittype * theunit, int flip)
551 {
552
553         char *toadd;
554         char **product;
555         int didsomething = 2;
556
557         if (flip)
558                 product = theunit->denominator;
559         else
560                 product = theunit->numerator;
561
562         for (; *product; product++) {
563
564                 for (;;) {
565                         if (!strlen(*product))
566                                 break;
567                         toadd = lookupunit(*product);
568                         if (!toadd) {
569                                 printf("unknown unit '%s'\n", *product);
570                                 return ERROR;
571                         }
572                         if (strchr(toadd, PRIMITIVECHAR))
573                                 break;
574                         didsomething = 1;
575                         if (*product != NULLUNIT) {
576                                 free(*product);
577                                 *product = NULLUNIT;
578                         }
579                         if (addunit(theunit, toadd, flip, 0))
580                                 return ERROR;
581                 }
582         }
583         return didsomething;
584 }
585
586
587 /*
588    Reduces numerator and denominator of the specified unit.
589    Returns 0 on success, or 1 on unknown unit error.
590 */
591
592 static int 
593 reduceunit(struct unittype * theunit)
594 {
595         int ret;
596
597         ret = 1;
598         while (ret & 1) {
599                 ret = reduceproduct(theunit, 0) | reduceproduct(theunit, 1);
600                 if (ret & 4)
601                         return 1;
602         }
603         return 0;
604 }
605
606
607 static int 
608 compareproducts(char **one, char **two)
609 {
610         while (*one || *two) {
611                 if (!*one && *two != NULLUNIT)
612                         return 1;
613                 if (!*two && *one != NULLUNIT)
614                         return 1;
615                 if (*one == NULLUNIT)
616                         one++;
617                 else if (*two == NULLUNIT)
618                         two++;
619                 else if (strcmp(*one, *two))
620                         return 1;
621                 else
622                         one++, two++;
623         }
624         return 0;
625 }
626
627
628 /* Return zero if units are compatible, nonzero otherwise */
629
630 static int 
631 compareunits(struct unittype * first, struct unittype * second)
632 {
633         return
634         compareproducts(first->numerator, second->numerator) ||
635         compareproducts(first->denominator, second->denominator);
636 }
637
638
639 static int 
640 completereduce(struct unittype * unit)
641 {
642         if (reduceunit(unit))
643                 return 1;
644         sortunit(unit);
645         cancelunit(unit);
646         return 0;
647 }
648
649 static void 
650 showanswer(struct unittype * have, struct unittype * want)
651 {
652         double ans;
653         char* oformat;
654
655         if (compareunits(have, want)) {
656                 printf("conformability error\n");
657                 if (verbose)
658                         printf("\t%s = ", havestr);
659                 else if (!terse)
660                         printf("\t");
661                 showunit(have);
662                 if (!terse) {
663                         if (verbose)
664                                 printf("\t%s = ", wantstr);
665                         else
666                                 printf("\t");
667                         showunit(want);
668                 }
669         }
670         else if (have->offset != want->offset) {
671                 if (want->quantity)
672                         printf("WARNING: conversion of non-proportional quantities.\n");
673                 if (have->quantity) {
674                         asprintf(&oformat, "\t%s\n", outputformat);
675                         printf(oformat,
676                             (have->factor + have->offset-want->offset)/want->factor);
677                         free(oformat);
678                 }
679                 else {
680                         asprintf(&oformat, "\t (-> x*%sg %sg)\n\t (<- y*%sg %sg)\n",
681                             outputformat, outputformat, outputformat, outputformat);
682                         printf(oformat,
683                             have->factor / want->factor,
684                             (have->offset-want->offset)/want->factor,
685                             want->factor / have->factor,
686                             (want->offset - have->offset)/have->factor);
687                 }
688         }
689         else {
690                 ans = have->factor / want->factor;
691
692                 if (verbose) {
693                         printf("\t%s = ", havestr);
694                         printf(outputformat, ans);
695                         printf(" * %s", wantstr);
696                         printf("\n");
697                 }
698                 else if (terse) {
699                         printf(outputformat, ans);
700                         printf("\n");
701                 }
702                 else {
703                         printf("\t* ");
704                         printf(outputformat, ans);
705                         printf("\n");
706                 }
707
708                 if (verbose) {
709                         printf("\t%s = (1 / ", havestr);
710                         printf(outputformat, 1/ans);
711                         printf(") * %s\n", wantstr);
712                 }
713                 else if (!terse) {
714                         printf("\t/ ");
715                         printf(outputformat, 1/ans);
716                         printf("\n");
717                 }
718         }
719 }
720
721
722 static void 
723 usage(void)
724 {
725         fprintf(stderr,
726                 "usage: units [-f unitsfile] [-UVq] [from-unit to-unit]\n");
727         exit(3);
728 }
729
730 static struct option longopts[] = {
731         {"help", no_argument, NULL, 'h'},
732         {"exponential", no_argument, NULL, 'e'},
733         {"file", required_argument, NULL, 'f'},
734         {"output-format", required_argument, NULL, 'o'},
735         {"quiet", no_argument, NULL, 'q'},
736         {"terse", no_argument, NULL, 't'},
737         {"unitsfile", no_argument, NULL, 'U'},
738         {"verbose", no_argument, NULL, 'v'},
739         {"version", no_argument, NULL, 'V'},
740         { 0, 0, 0, 0 }
741 };
742
743
744 int
745 main(int argc, char **argv)
746 {
747
748         struct unittype have, want;
749         int optchar;
750         bool quiet;
751         bool readfile;
752         History *inhistory;
753         EditLine *el;
754         HistEvent ev;
755         int inputsz;
756
757         quiet = false;
758         readfile = false;
759         outputformat = "%.8g";
760         while ((optchar = getopt_long(argc, argv, "+ehf:oqtvUV", longopts, NULL)) != -1) {
761                 switch (optchar) {
762                 case 'e':
763                         outputformat = "%6e";
764                         break;
765                 case 'f':
766                         readfile = true;
767                         if (strlen(optarg) == 0)
768                                 readunits(NULL);
769                         else
770                                 readunits(optarg);
771                         break;
772                 case 'q':
773                         quiet = true;
774                         break;
775                 case 't':
776                         terse = true;
777                         break;
778                 case 'o':
779                         outputformat = optarg;
780                         break;
781                 case 'v':
782                         verbose = true;
783                         break;
784                 case 'V':
785                         fprintf(stderr, "FreeBSD units\n");
786                         /* FALLTHROUGH */
787                 case 'U':
788                         if (access(UNITSFILE, F_OK) == 0)
789                                 printf("%s\n", UNITSFILE);
790                         else
791                                 printf("Units data file not found");
792                         exit(0);
793                         break;
794                 case 'h':
795                         /* FALLTHROUGH */
796
797                 default:
798                         usage();
799                 }
800         }
801
802         if (!readfile)
803                 readunits(NULL);
804
805         if (optind == argc - 2) {
806                 if (cap_enter() < 0 && errno != ENOSYS)
807                         err(1, "unable to enter capability mode");
808
809                 havestr = argv[optind];
810                 wantstr = argv[optind + 1];
811                 initializeunit(&have);
812                 addunit(&have, havestr, 0, 1);
813                 completereduce(&have);
814                 initializeunit(&want);
815                 addunit(&want, wantstr, 0, 1);
816                 completereduce(&want);
817                 showanswer(&have, &want);
818         } else {
819                 inhistory = history_init();
820                 el = el_init(argv[0], stdin, stdout, stderr);
821                 el_set(el, EL_PROMPT, &prompt);
822                 el_set(el, EL_EDITOR, "emacs");
823                 el_set(el, EL_SIGNAL, 1);
824                 el_set(el, EL_HIST, history, inhistory);
825                 el_source(el, NULL);
826                 history(inhistory, &ev, H_SETSIZE, 800);
827                 if (inhistory == 0)
828                         err(1, "Could not initialize history");
829
830                 if (cap_enter() < 0 && errno != ENOSYS)
831                         err(1, "unable to enter capability mode");
832
833                 if (!quiet)
834                         printf("%d units, %d prefixes\n", unitcount,
835                             prefixcount);
836                 for (;;) {
837                         do {
838                                 initializeunit(&have);
839                                 if (!quiet)
840                                         promptstr = "You have: ";
841                                 havestr = el_gets(el, &inputsz);
842                                 if (havestr == NULL)
843                                         exit(0);
844                                 if (inputsz > 0)
845                                         history(inhistory, &ev, H_ENTER,
846                                         havestr);
847                         } while (addunit(&have, havestr, 0, 1) ||
848                             completereduce(&have));
849                         do {
850                                 initializeunit(&want);
851                                 if (!quiet)
852                                         promptstr = "You want: ";
853                                 wantstr = el_gets(el, &inputsz);
854                                 if (wantstr == NULL)
855                                         exit(0);
856                                 if (inputsz > 0)
857                                         history(inhistory, &ev, H_ENTER,
858                                         wantstr);
859                         } while (addunit(&want, wantstr, 0, 1) ||
860                             completereduce(&want));
861                         showanswer(&have, &want);
862                 }
863
864                 history_end(inhistory);
865                 el_end(el);
866         }
867
868         return (0);
869 }