116186Salex/* 216186Salex * units.c Copyright (c) 1993 by Adrian Mariano (adrian@cam.cornell.edu) 316186Salex * 416186Salex * Redistribution and use in source and binary forms, with or without 516186Salex * modification, are permitted provided that the following conditions 616186Salex * are met: 716186Salex * 1. Redistributions of source code must retain the above copyright 816186Salex * notice, this list of conditions and the following disclaimer. 916186Salex * 2. The name of the author may not be used to endorse or promote products 1016186Salex * derived from this software without specific prior written permission. 1116186Salex * Disclaimer: This software is provided by the author "as is". The author 1216186Salex * shall not be liable for any damages caused in any way by this software. 1316186Salex * 1416186Salex * I would appreciate (though I do not require) receiving a copy of any 1516186Salex * improvements you might make to this program. 1616186Salex */ 1716186Salex 1828502Scharnier#ifndef lint 1928502Scharnierstatic const char rcsid[] = 2050477Speter "$FreeBSD$"; 2128502Scharnier#endif /* not lint */ 2228502Scharnier 2316186Salex#include <ctype.h> 2428502Scharnier#include <err.h> 2516186Salex#include <stdio.h> 2628502Scharnier#include <stdlib.h> 2716186Salex#include <string.h> 2828502Scharnier#include <unistd.h> 2916186Salex 3016186Salex#include "pathnames.h" 3116186Salex 3216186Salex#define VERSION "1.0" 3316186Salex 3416186Salex#ifndef UNITSFILE 3516186Salex#define UNITSFILE _PATH_UNITSLIB 3616186Salex#endif 3716186Salex 3816186Salex#define MAXUNITS 1000 3973229Sdwmalone#define MAXPREFIXES 100 4016186Salex 4116186Salex#define MAXSUBUNITS 500 4216186Salex 4316186Salex#define PRIMITIVECHAR '!' 4416186Salex 45227194Sedstatic const char *powerstring = "^"; 4616186Salex 47227194Sedstatic struct { 4816186Salex char *uname; 4916186Salex char *uval; 5016186Salex} unittable[MAXUNITS]; 5116186Salex 5216186Salexstruct unittype { 5316186Salex char *numerator[MAXSUBUNITS]; 5416186Salex char *denominator[MAXSUBUNITS]; 5516186Salex double factor; 56181786Sdwmalone double offset; 57181786Sdwmalone int quantity; 5816186Salex}; 5916186Salex 60227194Sedstatic struct { 6116186Salex char *prefixname; 6216186Salex char *prefixval; 6316186Salex} prefixtable[MAXPREFIXES]; 6416186Salex 6516186Salex 66227194Sedstatic char NULLUNIT[] = ""; 6716186Salex 6864053Skris#ifdef MSDOS 6973229Sdwmalone#define SEPARATOR ";" 7064053Skris#else 7164053Skris#define SEPARATOR ":" 7264053Skris#endif 7364053Skris 74227194Sedstatic int unitcount; 75227194Sedstatic int prefixcount; 7616186Salex 7778698Sdwmalonechar *dupstr(const char *str); 7878698Sdwmalonevoid readunits(const char *userfile); 7978698Sdwmalonevoid initializeunit(struct unittype * theunit); 8078698Sdwmaloneint addsubunit(char *product[], char *toadd); 8178698Sdwmalonevoid showunit(struct unittype * theunit); 8278698Sdwmalonevoid zeroerror(void); 83181786Sdwmaloneint addunit(struct unittype *theunit, char *toadd, int flip, int quantity); 8478698Sdwmaloneint compare(const void *item1, const void *item2); 8578698Sdwmalonevoid sortunit(struct unittype * theunit); 8678698Sdwmalonevoid cancelunit(struct unittype * theunit); 8778698Sdwmalonechar *lookupunit(const char *unit); 8878698Sdwmaloneint reduceproduct(struct unittype * theunit, int flip); 8978698Sdwmaloneint reduceunit(struct unittype * theunit); 9078698Sdwmaloneint compareproducts(char **one, char **two); 9178698Sdwmaloneint compareunits(struct unittype * first, struct unittype * second); 9278698Sdwmaloneint completereduce(struct unittype * unit); 9378698Sdwmalonevoid showanswer(struct unittype * have, struct unittype * want); 9478698Sdwmalonevoid usage(void); 9516186Salex 9616186Salexchar * 9773229Sdwmalonedupstr(const char *str) 9816186Salex{ 9916186Salex char *ret; 10016186Salex 10116186Salex ret = malloc(strlen(str) + 1); 10228502Scharnier if (!ret) 10328502Scharnier errx(3, "memory allocation error"); 10416186Salex strcpy(ret, str); 10516186Salex return (ret); 10616186Salex} 10716186Salex 10816186Salex 10916186Salexvoid 11073229Sdwmalonereadunits(const char *userfile) 11116186Salex{ 11216186Salex FILE *unitfile; 11373229Sdwmalone char line[512], *lineptr; 11416186Salex int len, linenum, i; 11516186Salex 11616186Salex unitcount = 0; 11716186Salex linenum = 0; 11816186Salex 11916186Salex if (userfile) { 12016186Salex unitfile = fopen(userfile, "rt"); 12128502Scharnier if (!unitfile) 12228502Scharnier errx(1, "unable to open units file '%s'", userfile); 12316186Salex } 12416186Salex else { 12516186Salex unitfile = fopen(UNITSFILE, "rt"); 12616186Salex if (!unitfile) { 12716186Salex char *direc, *env; 12816186Salex char filename[1000]; 12916186Salex 13016186Salex env = getenv("PATH"); 13116186Salex if (env) { 13264053Skris direc = strtok(env, SEPARATOR); 13316186Salex while (direc) { 13464053Skris snprintf(filename, sizeof(filename), 13564053Skris "%s/%s", direc, UNITSFILE); 13616186Salex unitfile = fopen(filename, "rt"); 13716186Salex if (unitfile) 13816186Salex break; 13964053Skris direc = strtok(NULL, SEPARATOR); 14016186Salex } 14116186Salex } 14228502Scharnier if (!unitfile) 14328502Scharnier errx(1, "can't find units file '%s'", UNITSFILE); 14416186Salex } 14516186Salex } 14616186Salex while (!feof(unitfile)) { 14773229Sdwmalone if (!fgets(line, sizeof(line), unitfile)) 14816186Salex break; 14916186Salex linenum++; 15016186Salex lineptr = line; 15116186Salex if (*lineptr == '/') 15216186Salex continue; 15316186Salex lineptr += strspn(lineptr, " \n\t"); 15416186Salex len = strcspn(lineptr, " \n\t"); 15516186Salex lineptr[len] = 0; 15616186Salex if (!strlen(lineptr)) 15716186Salex continue; 15816186Salex if (lineptr[strlen(lineptr) - 1] == '-') { /* it's a prefix */ 15916186Salex if (prefixcount == MAXPREFIXES) { 16028502Scharnier warnx("memory for prefixes exceeded in line %d", linenum); 16116186Salex continue; 16216186Salex } 16316186Salex lineptr[strlen(lineptr) - 1] = 0; 16416186Salex prefixtable[prefixcount].prefixname = dupstr(lineptr); 16516186Salex for (i = 0; i < prefixcount; i++) 16616186Salex if (!strcmp(prefixtable[i].prefixname, lineptr)) { 16728502Scharnier warnx("redefinition of prefix '%s' on line %d ignored", 16816186Salex lineptr, linenum); 16916186Salex continue; 17016186Salex } 17116186Salex lineptr += len + 1; 17273229Sdwmalone lineptr += strspn(lineptr, " \n\t"); 17373229Sdwmalone len = strcspn(lineptr, "\n\t"); 17473229Sdwmalone if (len == 0) { 17573229Sdwmalone warnx("unexpected end of prefix on line %d", 17673229Sdwmalone linenum); 17716186Salex continue; 17816186Salex } 17916186Salex lineptr[len] = 0; 18016186Salex prefixtable[prefixcount++].prefixval = dupstr(lineptr); 18116186Salex } 18216186Salex else { /* it's not a prefix */ 18316186Salex if (unitcount == MAXUNITS) { 18428502Scharnier warnx("memory for units exceeded in line %d", linenum); 18516186Salex continue; 18616186Salex } 18716186Salex unittable[unitcount].uname = dupstr(lineptr); 18816186Salex for (i = 0; i < unitcount; i++) 18916186Salex if (!strcmp(unittable[i].uname, lineptr)) { 19028502Scharnier warnx("redefinition of unit '%s' on line %d ignored", 19116186Salex lineptr, linenum); 19216186Salex continue; 19316186Salex } 19416186Salex lineptr += len + 1; 19516186Salex lineptr += strspn(lineptr, " \n\t"); 19616186Salex if (!strlen(lineptr)) { 19773229Sdwmalone warnx("unexpected end of unit on line %d", 19873229Sdwmalone linenum); 19916186Salex continue; 20016186Salex } 20116186Salex len = strcspn(lineptr, "\n\t"); 20216186Salex lineptr[len] = 0; 20316186Salex unittable[unitcount++].uval = dupstr(lineptr); 20416186Salex } 20516186Salex } 20616186Salex fclose(unitfile); 20716186Salex} 20816186Salex 20916186Salexvoid 21016186Salexinitializeunit(struct unittype * theunit) 21116186Salex{ 212181786Sdwmalone theunit->numerator[0] = theunit->denominator[0] = NULL; 21316186Salex theunit->factor = 1.0; 214181786Sdwmalone theunit->offset = 0.0; 215181786Sdwmalone theunit->quantity = 0; 21616186Salex} 21716186Salex 21816186Salex 21916186Salexint 22016186Salexaddsubunit(char *product[], char *toadd) 22116186Salex{ 22216186Salex char **ptr; 22316186Salex 22416186Salex for (ptr = product; *ptr && *ptr != NULLUNIT; ptr++); 22516186Salex if (ptr >= product + MAXSUBUNITS) { 22628502Scharnier warnx("memory overflow in unit reduction"); 22716186Salex return 1; 22816186Salex } 22916186Salex if (!*ptr) 23016186Salex *(ptr + 1) = 0; 23116186Salex *ptr = dupstr(toadd); 23216186Salex return 0; 23316186Salex} 23416186Salex 23516186Salex 23616186Salexvoid 23716186Salexshowunit(struct unittype * theunit) 23816186Salex{ 23916186Salex char **ptr; 24016186Salex int printedslash; 24116186Salex int counter = 1; 24216186Salex 24316186Salex printf("\t%.8g", theunit->factor); 244181786Sdwmalone if (theunit->offset) 245181786Sdwmalone printf("&%.8g", theunit->offset); 24616186Salex for (ptr = theunit->numerator; *ptr; ptr++) { 24716186Salex if (ptr > theunit->numerator && **ptr && 24816186Salex !strcmp(*ptr, *(ptr - 1))) 24916186Salex counter++; 25016186Salex else { 25116186Salex if (counter > 1) 25216186Salex printf("%s%d", powerstring, counter); 25316186Salex if (**ptr) 25416186Salex printf(" %s", *ptr); 25516186Salex counter = 1; 25616186Salex } 25716186Salex } 25816186Salex if (counter > 1) 25916186Salex printf("%s%d", powerstring, counter); 26016186Salex counter = 1; 26116186Salex printedslash = 0; 26216186Salex for (ptr = theunit->denominator; *ptr; ptr++) { 26316186Salex if (ptr > theunit->denominator && **ptr && 26416186Salex !strcmp(*ptr, *(ptr - 1))) 26516186Salex counter++; 26616186Salex else { 26716186Salex if (counter > 1) 26816186Salex printf("%s%d", powerstring, counter); 26916186Salex if (**ptr) { 27016186Salex if (!printedslash) 27116186Salex printf(" /"); 27216186Salex printedslash = 1; 27316186Salex printf(" %s", *ptr); 27416186Salex } 27516186Salex counter = 1; 27616186Salex } 27716186Salex } 27816186Salex if (counter > 1) 27916186Salex printf("%s%d", powerstring, counter); 28016186Salex printf("\n"); 28116186Salex} 28216186Salex 28316186Salex 28416186Salexvoid 28573229Sdwmalonezeroerror(void) 28616186Salex{ 28728502Scharnier warnx("unit reduces to zero"); 28816186Salex} 28916186Salex 29016186Salex/* 29116186Salex Adds the specified string to the unit. 29216186Salex Flip is 0 for adding normally, 1 for adding reciprocal. 293181786Sdwmalone Quantity is 1 if this is a quantity to be converted rather than a pure unit. 29416186Salex 29516186Salex Returns 0 for successful addition, nonzero on error. 29616186Salex*/ 29716186Salex 29816186Salexint 299181786Sdwmaloneaddunit(struct unittype * theunit, char *toadd, int flip, int quantity) 30016186Salex{ 30116186Salex char *scratch, *savescr; 30216186Salex char *item; 303181786Sdwmalone char *divider, *slash, *offset; 30416186Salex int doingtop; 30516186Salex 30664053Skris if (!strlen(toadd)) 30764053Skris return 1; 30864053Skris 30916186Salex savescr = scratch = dupstr(toadd); 31016186Salex for (slash = scratch + 1; *slash; slash++) 31116186Salex if (*slash == '-' && 31216186Salex (tolower(*(slash - 1)) != 'e' || 31316186Salex !strchr(".0123456789", *(slash + 1)))) 31416186Salex *slash = ' '; 31516186Salex slash = strchr(scratch, '/'); 31616186Salex if (slash) 31716186Salex *slash = 0; 31816186Salex doingtop = 1; 31916186Salex do { 32016186Salex item = strtok(scratch, " *\t\n/"); 32116186Salex while (item) { 32216186Salex if (strchr("0123456789.", *item)) { /* item is a number */ 323181786Sdwmalone double num, offsetnum; 32416186Salex 325181786Sdwmalone if (quantity) 326181786Sdwmalone theunit->quantity = 1; 327181786Sdwmalone 328181786Sdwmalone offset = strchr(item, '&'); 329181786Sdwmalone if (offset) { 330181786Sdwmalone *offset = 0; 331181786Sdwmalone offsetnum = atof(offset+1); 332181786Sdwmalone } else 333181786Sdwmalone offsetnum = 0.0; 334181786Sdwmalone 33516186Salex divider = strchr(item, '|'); 33616186Salex if (divider) { 33716186Salex *divider = 0; 33816186Salex num = atof(item); 33916186Salex if (!num) { 34016186Salex zeroerror(); 34116186Salex return 1; 34216186Salex } 343181786Sdwmalone if (doingtop ^ flip) { 34416186Salex theunit->factor *= num; 345181786Sdwmalone theunit->offset *= num; 346181786Sdwmalone } else { 34716186Salex theunit->factor /= num; 348181786Sdwmalone theunit->offset /= num; 349181786Sdwmalone } 35016186Salex num = atof(divider + 1); 35116186Salex if (!num) { 35216186Salex zeroerror(); 35316186Salex return 1; 35416186Salex } 355181786Sdwmalone if (doingtop ^ flip) { 35616186Salex theunit->factor /= num; 357181786Sdwmalone theunit->offset /= num; 358181786Sdwmalone } else { 35916186Salex theunit->factor *= num; 360181786Sdwmalone theunit->offset *= num; 361181786Sdwmalone } 36216186Salex } 36316186Salex else { 36416186Salex num = atof(item); 36516186Salex if (!num) { 36616186Salex zeroerror(); 36716186Salex return 1; 36816186Salex } 369181786Sdwmalone if (doingtop ^ flip) { 37016186Salex theunit->factor *= num; 371181786Sdwmalone theunit->offset *= num; 372181786Sdwmalone } else { 37316186Salex theunit->factor /= num; 374181786Sdwmalone theunit->offset /= num; 375181786Sdwmalone } 37616186Salex } 377181786Sdwmalone if (doingtop ^ flip) 378181786Sdwmalone theunit->offset += offsetnum; 37916186Salex } 38016186Salex else { /* item is not a number */ 38116186Salex int repeat = 1; 38216186Salex 38316186Salex if (strchr("23456789", 38416186Salex item[strlen(item) - 1])) { 38516186Salex repeat = item[strlen(item) - 1] - '0'; 38616186Salex item[strlen(item) - 1] = 0; 38716186Salex } 38816186Salex for (; repeat; repeat--) 38916186Salex if (addsubunit(doingtop ^ flip ? theunit->numerator : theunit->denominator, item)) 39016186Salex return 1; 39116186Salex } 39216186Salex item = strtok(NULL, " *\t/\n"); 39316186Salex } 39416186Salex doingtop--; 39516186Salex if (slash) { 39616186Salex scratch = slash + 1; 39716186Salex } 39816186Salex else 39916186Salex doingtop--; 40016186Salex } while (doingtop >= 0); 40116186Salex free(savescr); 40216186Salex return 0; 40316186Salex} 40416186Salex 40516186Salex 40616186Salexint 40716186Salexcompare(const void *item1, const void *item2) 40816186Salex{ 409100826Sdwmalone return strcmp(*(const char * const *)item1, *(const char * const *)item2); 41016186Salex} 41116186Salex 41216186Salex 41316186Salexvoid 41416186Salexsortunit(struct unittype * theunit) 41516186Salex{ 41616186Salex char **ptr; 41773229Sdwmalone unsigned int count; 41816186Salex 41916186Salex for (count = 0, ptr = theunit->numerator; *ptr; ptr++, count++); 42016186Salex qsort(theunit->numerator, count, sizeof(char *), compare); 42116186Salex for (count = 0, ptr = theunit->denominator; *ptr; ptr++, count++); 42216186Salex qsort(theunit->denominator, count, sizeof(char *), compare); 42316186Salex} 42416186Salex 42516186Salex 42616186Salexvoid 42716186Salexcancelunit(struct unittype * theunit) 42816186Salex{ 42916186Salex char **den, **num; 43016186Salex int comp; 43116186Salex 43216186Salex den = theunit->denominator; 43316186Salex num = theunit->numerator; 43416186Salex 43516186Salex while (*num && *den) { 43616186Salex comp = strcmp(*den, *num); 43716186Salex if (!comp) { 43816186Salex/* if (*den!=NULLUNIT) free(*den); 43916186Salex if (*num!=NULLUNIT) free(*num);*/ 44016186Salex *den++ = NULLUNIT; 44116186Salex *num++ = NULLUNIT; 44216186Salex } 44316186Salex else if (comp < 0) 44416186Salex den++; 44516186Salex else 44616186Salex num++; 44716186Salex } 44816186Salex} 44916186Salex 45016186Salex 45116186Salex 45216186Salex 45316186Salex/* 45416186Salex Looks up the definition for the specified unit. 45516186Salex Returns a pointer to the definition or a null pointer 45616186Salex if the specified unit does not appear in the units table. 45716186Salex*/ 45816186Salex 45916186Salexstatic char buffer[100]; /* buffer for lookupunit answers with 46016186Salex prefixes */ 46116186Salex 46216186Salexchar * 46373229Sdwmalonelookupunit(const char *unit) 46416186Salex{ 46516186Salex int i; 46616186Salex char *copy; 46716186Salex 46816186Salex for (i = 0; i < unitcount; i++) { 46916186Salex if (!strcmp(unittable[i].uname, unit)) 47016186Salex return unittable[i].uval; 47116186Salex } 47216186Salex 47316186Salex if (unit[strlen(unit) - 1] == '^') { 47416186Salex copy = dupstr(unit); 47516186Salex copy[strlen(copy) - 1] = 0; 47616186Salex for (i = 0; i < unitcount; i++) { 47716186Salex if (!strcmp(unittable[i].uname, copy)) { 47864053Skris strlcpy(buffer, copy, sizeof(buffer)); 47916186Salex free(copy); 48016186Salex return buffer; 48116186Salex } 48216186Salex } 48316186Salex free(copy); 48416186Salex } 48516186Salex if (unit[strlen(unit) - 1] == 's') { 48616186Salex copy = dupstr(unit); 48716186Salex copy[strlen(copy) - 1] = 0; 48816186Salex for (i = 0; i < unitcount; i++) { 48916186Salex if (!strcmp(unittable[i].uname, copy)) { 49064053Skris strlcpy(buffer, copy, sizeof(buffer)); 49116186Salex free(copy); 49216186Salex return buffer; 49316186Salex } 49416186Salex } 49516186Salex if (copy[strlen(copy) - 1] == 'e') { 49616186Salex copy[strlen(copy) - 1] = 0; 49716186Salex for (i = 0; i < unitcount; i++) { 49816186Salex if (!strcmp(unittable[i].uname, copy)) { 49964053Skris strlcpy(buffer, copy, sizeof(buffer)); 50016186Salex free(copy); 50116186Salex return buffer; 50216186Salex } 50316186Salex } 50416186Salex } 50516186Salex free(copy); 50616186Salex } 50716186Salex for (i = 0; i < prefixcount; i++) { 50873229Sdwmalone size_t len = strlen(prefixtable[i].prefixname); 50973229Sdwmalone if (!strncmp(prefixtable[i].prefixname, unit, len)) { 51073229Sdwmalone if (!strlen(unit + len) || lookupunit(unit + len)) { 51164053Skris snprintf(buffer, sizeof(buffer), "%s %s", 51273229Sdwmalone prefixtable[i].prefixval, unit + len); 51316186Salex return buffer; 51416186Salex } 51516186Salex } 51616186Salex } 51716186Salex return 0; 51816186Salex} 51916186Salex 52016186Salex 52116186Salex 52216186Salex/* 52316186Salex reduces a product of symbolic units to primitive units. 52416186Salex The three low bits are used to return flags: 52516186Salex 52616186Salex bit 0 (1) set on if reductions were performed without error. 52716186Salex bit 1 (2) set on if no reductions are performed. 52816186Salex bit 2 (4) set on if an unknown unit is discovered. 52916186Salex*/ 53016186Salex 53116186Salex 53216186Salex#define ERROR 4 53316186Salex 53416186Salexint 53516186Salexreduceproduct(struct unittype * theunit, int flip) 53616186Salex{ 53716186Salex 53816186Salex char *toadd; 53916186Salex char **product; 54016186Salex int didsomething = 2; 54116186Salex 54216186Salex if (flip) 54316186Salex product = theunit->denominator; 54416186Salex else 54516186Salex product = theunit->numerator; 54616186Salex 54716186Salex for (; *product; product++) { 54816186Salex 54916186Salex for (;;) { 55016186Salex if (!strlen(*product)) 55116186Salex break; 55216186Salex toadd = lookupunit(*product); 55316186Salex if (!toadd) { 55416186Salex printf("unknown unit '%s'\n", *product); 55516186Salex return ERROR; 55616186Salex } 55716186Salex if (strchr(toadd, PRIMITIVECHAR)) 55816186Salex break; 55916186Salex didsomething = 1; 56016186Salex if (*product != NULLUNIT) { 56116186Salex free(*product); 56216186Salex *product = NULLUNIT; 56316186Salex } 564181786Sdwmalone if (addunit(theunit, toadd, flip, 0)) 56516186Salex return ERROR; 56616186Salex } 56716186Salex } 56816186Salex return didsomething; 56916186Salex} 57016186Salex 57116186Salex 57216186Salex/* 57316186Salex Reduces numerator and denominator of the specified unit. 57416186Salex Returns 0 on success, or 1 on unknown unit error. 57516186Salex*/ 57616186Salex 57716186Salexint 57816186Salexreduceunit(struct unittype * theunit) 57916186Salex{ 58016186Salex int ret; 58116186Salex 58216186Salex ret = 1; 58316186Salex while (ret & 1) { 58416186Salex ret = reduceproduct(theunit, 0) | reduceproduct(theunit, 1); 58516186Salex if (ret & 4) 58616186Salex return 1; 58716186Salex } 58816186Salex return 0; 58916186Salex} 59016186Salex 59116186Salex 59216186Salexint 59316186Salexcompareproducts(char **one, char **two) 59416186Salex{ 59516186Salex while (*one || *two) { 59616186Salex if (!*one && *two != NULLUNIT) 59716186Salex return 1; 59816186Salex if (!*two && *one != NULLUNIT) 59916186Salex return 1; 60016186Salex if (*one == NULLUNIT) 60116186Salex one++; 60216186Salex else if (*two == NULLUNIT) 60316186Salex two++; 60416186Salex else if (strcmp(*one, *two)) 60516186Salex return 1; 60616186Salex else 60716186Salex one++, two++; 60816186Salex } 60916186Salex return 0; 61016186Salex} 61116186Salex 61216186Salex 61316186Salex/* Return zero if units are compatible, nonzero otherwise */ 61416186Salex 61516186Salexint 61616186Salexcompareunits(struct unittype * first, struct unittype * second) 61716186Salex{ 61816186Salex return 61916186Salex compareproducts(first->numerator, second->numerator) || 62016186Salex compareproducts(first->denominator, second->denominator); 62116186Salex} 62216186Salex 62316186Salex 62416186Salexint 62516186Salexcompletereduce(struct unittype * unit) 62616186Salex{ 62716186Salex if (reduceunit(unit)) 62816186Salex return 1; 62916186Salex sortunit(unit); 63016186Salex cancelunit(unit); 63116186Salex return 0; 63216186Salex} 63316186Salex 63416186Salex 63516186Salexvoid 63616186Salexshowanswer(struct unittype * have, struct unittype * want) 63716186Salex{ 63816186Salex if (compareunits(have, want)) { 63916186Salex printf("conformability error\n"); 64016186Salex showunit(have); 64116186Salex showunit(want); 64216186Salex } 643181786Sdwmalone else if (have->offset != want->offset) { 644181786Sdwmalone if (want->quantity) 645181786Sdwmalone printf("WARNING: conversion of non-proportional quantities.\n"); 646181786Sdwmalone printf("\t"); 647181786Sdwmalone if (have->quantity) 648181786Sdwmalone printf("%.8g\n", 649181786Sdwmalone (have->factor + have->offset-want->offset)/want->factor); 650181786Sdwmalone else 651181786Sdwmalone printf(" (-> x*%.8g %+.8g)\n\t (<- y*%.8g %+.8g)\n", 652181786Sdwmalone have->factor / want->factor, 653181786Sdwmalone (have->offset-want->offset)/want->factor, 654181786Sdwmalone want->factor / have->factor, 655181786Sdwmalone (want->offset - have->offset)/have->factor); 656181786Sdwmalone } 65716186Salex else 65816186Salex printf("\t* %.8g\n\t/ %.8g\n", have->factor / want->factor, 65916186Salex want->factor / have->factor); 66016186Salex} 66116186Salex 66216186Salex 66316186Salexvoid 66473229Sdwmaloneusage(void) 66516186Salex{ 66628502Scharnier fprintf(stderr, 66728502Scharnier "usage: units [-f unitsfile] [-q] [-v] [from-unit to-unit]\n"); 66816186Salex exit(3); 66916186Salex} 67016186Salex 67116186Salex 67216186Salexint 67316186Salexmain(int argc, char **argv) 67416186Salex{ 67516186Salex 67616186Salex struct unittype have, want; 67716186Salex char havestr[81], wantstr[81]; 67816186Salex int optchar; 67916186Salex char *userfile = 0; 68016186Salex int quiet = 0; 68116186Salex 68216186Salex while ((optchar = getopt(argc, argv, "vqf:")) != -1) { 68316186Salex switch (optchar) { 68416186Salex case 'f': 68516186Salex userfile = optarg; 68616186Salex break; 68716186Salex case 'q': 68816186Salex quiet = 1; 68916186Salex break; 69016186Salex case 'v': 69116186Salex fprintf(stderr, "\n units version %s Copyright (c) 1993 by Adrian Mariano\n", 69216186Salex VERSION); 69316186Salex fprintf(stderr, " This program may be freely distributed\n"); 69416186Salex usage(); 69516186Salex default: 69616186Salex usage(); 69716186Salex break; 69816186Salex } 69916186Salex } 70016186Salex 70116186Salex if (optind != argc - 2 && optind != argc) 70216186Salex usage(); 70316186Salex 70416186Salex readunits(userfile); 70516186Salex 70616186Salex if (optind == argc - 2) { 70764053Skris strlcpy(havestr, argv[optind], sizeof(havestr)); 70864053Skris strlcpy(wantstr, argv[optind + 1], sizeof(wantstr)); 70916186Salex initializeunit(&have); 710181786Sdwmalone addunit(&have, havestr, 0, 1); 71116186Salex completereduce(&have); 71216186Salex initializeunit(&want); 713181786Sdwmalone addunit(&want, wantstr, 0, 1); 71416186Salex completereduce(&want); 71516186Salex showanswer(&have, &want); 71616186Salex } 71716186Salex else { 71816186Salex if (!quiet) 71928502Scharnier printf("%d units, %d prefixes\n", unitcount, 72016186Salex prefixcount); 72116186Salex for (;;) { 72216186Salex do { 72316186Salex initializeunit(&have); 72416186Salex if (!quiet) 72516186Salex printf("You have: "); 72673229Sdwmalone if (!fgets(havestr, sizeof(havestr), stdin)) { 72716190Salex if (!quiet) 72816190Salex putchar('\n'); 72916186Salex exit(0); 73016186Salex } 731181786Sdwmalone } while (addunit(&have, havestr, 0, 1) || 73216186Salex completereduce(&have)); 73316186Salex do { 73416186Salex initializeunit(&want); 73516186Salex if (!quiet) 73616186Salex printf("You want: "); 73773229Sdwmalone if (!fgets(wantstr, sizeof(wantstr), stdin)) { 73816186Salex if (!quiet) 73916186Salex putchar('\n'); 74016186Salex exit(0); 74116186Salex } 742181786Sdwmalone } while (addunit(&want, wantstr, 0, 1) || 74316186Salex completereduce(&want)); 74416186Salex showanswer(&have, &want); 74516186Salex } 74616186Salex } 74716190Salex 74816190Salex return(0); 74916186Salex} 750