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