1a3031715SAlexander Langer /* 2a3031715SAlexander Langer * units.c Copyright (c) 1993 by Adrian Mariano (adrian@cam.cornell.edu) 3a3031715SAlexander Langer * 4a3031715SAlexander Langer * Redistribution and use in source and binary forms, with or without 5a3031715SAlexander Langer * modification, are permitted provided that the following conditions 6a3031715SAlexander Langer * are met: 7a3031715SAlexander Langer * 1. Redistributions of source code must retain the above copyright 8a3031715SAlexander Langer * notice, this list of conditions and the following disclaimer. 9a3031715SAlexander Langer * 2. The name of the author may not be used to endorse or promote products 10a3031715SAlexander Langer * derived from this software without specific prior written permission. 11a3031715SAlexander Langer * Disclaimer: This software is provided by the author "as is". The author 12a3031715SAlexander Langer * shall not be liable for any damages caused in any way by this software. 13a3031715SAlexander Langer * 14a3031715SAlexander Langer * I would appreciate (though I do not require) receiving a copy of any 15a3031715SAlexander Langer * improvements you might make to this program. 16a3031715SAlexander Langer */ 17a3031715SAlexander Langer 18d4d0763aSPhilippe Charnier #ifndef lint 19d4d0763aSPhilippe Charnier static const char rcsid[] = 20c3aac50fSPeter Wemm "$FreeBSD$"; 21d4d0763aSPhilippe Charnier #endif /* not lint */ 22d4d0763aSPhilippe Charnier 23a3031715SAlexander Langer #include <ctype.h> 24d4d0763aSPhilippe Charnier #include <err.h> 25ac117d5dSEitan Adler #include <errno.h> 26a3031715SAlexander Langer #include <stdio.h> 27a3031715SAlexander Langer #include <stdlib.h> 28d4d0763aSPhilippe Charnier #include <string.h> 29d4d0763aSPhilippe Charnier #include <unistd.h> 30a3031715SAlexander Langer 31ac117d5dSEitan Adler #include <sys/capsicum.h> 32ac117d5dSEitan Adler 33a3031715SAlexander Langer #include "pathnames.h" 34a3031715SAlexander Langer 35a3031715SAlexander Langer #ifndef UNITSFILE 36a3031715SAlexander Langer #define UNITSFILE _PATH_UNITSLIB 37a3031715SAlexander Langer #endif 38a3031715SAlexander Langer 39a3031715SAlexander Langer #define MAXUNITS 1000 4017ad860fSDavid Malone #define MAXPREFIXES 100 41a3031715SAlexander Langer 42a3031715SAlexander Langer #define MAXSUBUNITS 500 43a3031715SAlexander Langer 44a3031715SAlexander Langer #define PRIMITIVECHAR '!' 45a3031715SAlexander Langer 4690a29505SEd Schouten static const char *powerstring = "^"; 47a3031715SAlexander Langer 4890a29505SEd Schouten static struct { 49a3031715SAlexander Langer char *uname; 50a3031715SAlexander Langer char *uval; 51a3031715SAlexander Langer } unittable[MAXUNITS]; 52a3031715SAlexander Langer 53a3031715SAlexander Langer struct unittype { 54a3031715SAlexander Langer char *numerator[MAXSUBUNITS]; 55a3031715SAlexander Langer char *denominator[MAXSUBUNITS]; 56a3031715SAlexander Langer double factor; 579c95bc1cSDavid Malone double offset; 589c95bc1cSDavid Malone int quantity; 59a3031715SAlexander Langer }; 60a3031715SAlexander Langer 6190a29505SEd Schouten static struct { 62a3031715SAlexander Langer char *prefixname; 63a3031715SAlexander Langer char *prefixval; 64a3031715SAlexander Langer } prefixtable[MAXPREFIXES]; 65a3031715SAlexander Langer 66a3031715SAlexander Langer 6790a29505SEd Schouten static char NULLUNIT[] = ""; 68a3031715SAlexander Langer 69ffd044a6SKris Kennaway #ifdef MSDOS 7017ad860fSDavid Malone #define SEPARATOR ";" 71ffd044a6SKris Kennaway #else 72ffd044a6SKris Kennaway #define SEPARATOR ":" 73ffd044a6SKris Kennaway #endif 74ffd044a6SKris Kennaway 7590a29505SEd Schouten static int unitcount; 7690a29505SEd Schouten static int prefixcount; 77a3031715SAlexander Langer 785386636dSDavid Malone char *dupstr(const char *str); 795386636dSDavid Malone void readunits(const char *userfile); 805386636dSDavid Malone void initializeunit(struct unittype * theunit); 815386636dSDavid Malone int addsubunit(char *product[], char *toadd); 825386636dSDavid Malone void showunit(struct unittype * theunit); 835386636dSDavid Malone void zeroerror(void); 849c95bc1cSDavid Malone int addunit(struct unittype *theunit, char *toadd, int flip, int quantity); 855386636dSDavid Malone int compare(const void *item1, const void *item2); 865386636dSDavid Malone void sortunit(struct unittype * theunit); 875386636dSDavid Malone void cancelunit(struct unittype * theunit); 885386636dSDavid Malone char *lookupunit(const char *unit); 895386636dSDavid Malone int reduceproduct(struct unittype * theunit, int flip); 905386636dSDavid Malone int reduceunit(struct unittype * theunit); 915386636dSDavid Malone int compareproducts(char **one, char **two); 925386636dSDavid Malone int compareunits(struct unittype * first, struct unittype * second); 935386636dSDavid Malone int completereduce(struct unittype * unit); 945386636dSDavid Malone void showanswer(struct unittype * have, struct unittype * want); 955386636dSDavid Malone void usage(void); 96a3031715SAlexander Langer 97a3031715SAlexander Langer char * 9817ad860fSDavid Malone dupstr(const char *str) 99a3031715SAlexander Langer { 100a3031715SAlexander Langer char *ret; 101a3031715SAlexander Langer 102a3031715SAlexander Langer ret = malloc(strlen(str) + 1); 103d4d0763aSPhilippe Charnier if (!ret) 104d4d0763aSPhilippe Charnier errx(3, "memory allocation error"); 105a3031715SAlexander Langer strcpy(ret, str); 106a3031715SAlexander Langer return (ret); 107a3031715SAlexander Langer } 108a3031715SAlexander Langer 109a3031715SAlexander Langer 110a3031715SAlexander Langer void 11117ad860fSDavid Malone readunits(const char *userfile) 112a3031715SAlexander Langer { 113a3031715SAlexander Langer FILE *unitfile; 11417ad860fSDavid Malone char line[512], *lineptr; 115a3031715SAlexander Langer int len, linenum, i; 116ac117d5dSEitan Adler cap_rights_t unitfilerights; 117a3031715SAlexander Langer 118a3031715SAlexander Langer unitcount = 0; 119a3031715SAlexander Langer linenum = 0; 120a3031715SAlexander Langer 121a3031715SAlexander Langer if (userfile) { 122a3031715SAlexander Langer unitfile = fopen(userfile, "rt"); 123d4d0763aSPhilippe Charnier if (!unitfile) 124d4d0763aSPhilippe Charnier errx(1, "unable to open units file '%s'", userfile); 125a3031715SAlexander Langer } 126a3031715SAlexander Langer else { 127a3031715SAlexander Langer unitfile = fopen(UNITSFILE, "rt"); 128a3031715SAlexander Langer if (!unitfile) { 129a3031715SAlexander Langer char *direc, *env; 130a3031715SAlexander Langer char filename[1000]; 131a3031715SAlexander Langer 132a3031715SAlexander Langer env = getenv("PATH"); 133a3031715SAlexander Langer if (env) { 134ffd044a6SKris Kennaway direc = strtok(env, SEPARATOR); 135a3031715SAlexander Langer while (direc) { 136ffd044a6SKris Kennaway snprintf(filename, sizeof(filename), 137ffd044a6SKris Kennaway "%s/%s", direc, UNITSFILE); 138a3031715SAlexander Langer unitfile = fopen(filename, "rt"); 139a3031715SAlexander Langer if (unitfile) 140a3031715SAlexander Langer break; 141ffd044a6SKris Kennaway direc = strtok(NULL, SEPARATOR); 142a3031715SAlexander Langer } 143a3031715SAlexander Langer } 144d4d0763aSPhilippe Charnier if (!unitfile) 145d4d0763aSPhilippe Charnier errx(1, "can't find units file '%s'", UNITSFILE); 146a3031715SAlexander Langer } 147a3031715SAlexander Langer } 148ac117d5dSEitan Adler if (cap_enter() < 0 && errno != ENOSYS) 149ac117d5dSEitan Adler err(1, "unable to enter capability mode"); 150ac117d5dSEitan Adler cap_rights_init(&unitfilerights, CAP_READ, CAP_FSTAT); 151ac117d5dSEitan Adler if (cap_rights_limit(fileno(unitfile), &unitfilerights) < 0 152ac117d5dSEitan Adler && errno != ENOSYS) 153ac117d5dSEitan Adler err(1, "cap_rights_limit() failed"); 154a3031715SAlexander Langer while (!feof(unitfile)) { 15517ad860fSDavid Malone if (!fgets(line, sizeof(line), unitfile)) 156a3031715SAlexander Langer break; 157a3031715SAlexander Langer linenum++; 158a3031715SAlexander Langer lineptr = line; 159a3031715SAlexander Langer if (*lineptr == '/') 160a3031715SAlexander Langer continue; 161a3031715SAlexander Langer lineptr += strspn(lineptr, " \n\t"); 162a3031715SAlexander Langer len = strcspn(lineptr, " \n\t"); 163a3031715SAlexander Langer lineptr[len] = 0; 164a3031715SAlexander Langer if (!strlen(lineptr)) 165a3031715SAlexander Langer continue; 166a3031715SAlexander Langer if (lineptr[strlen(lineptr) - 1] == '-') { /* it's a prefix */ 167a3031715SAlexander Langer if (prefixcount == MAXPREFIXES) { 168d4d0763aSPhilippe Charnier warnx("memory for prefixes exceeded in line %d", linenum); 169a3031715SAlexander Langer continue; 170a3031715SAlexander Langer } 171a3031715SAlexander Langer lineptr[strlen(lineptr) - 1] = 0; 172a3031715SAlexander Langer prefixtable[prefixcount].prefixname = dupstr(lineptr); 173a3031715SAlexander Langer for (i = 0; i < prefixcount; i++) 174a3031715SAlexander Langer if (!strcmp(prefixtable[i].prefixname, lineptr)) { 175d4d0763aSPhilippe Charnier warnx("redefinition of prefix '%s' on line %d ignored", 176a3031715SAlexander Langer lineptr, linenum); 177a3031715SAlexander Langer continue; 178a3031715SAlexander Langer } 179a3031715SAlexander Langer lineptr += len + 1; 180a3031715SAlexander Langer lineptr += strspn(lineptr, " \n\t"); 181a3031715SAlexander Langer len = strcspn(lineptr, "\n\t"); 18217ad860fSDavid Malone if (len == 0) { 18317ad860fSDavid Malone warnx("unexpected end of prefix on line %d", 18417ad860fSDavid Malone linenum); 18517ad860fSDavid Malone continue; 18617ad860fSDavid Malone } 187a3031715SAlexander Langer lineptr[len] = 0; 188a3031715SAlexander Langer prefixtable[prefixcount++].prefixval = dupstr(lineptr); 189a3031715SAlexander Langer } 190a3031715SAlexander Langer else { /* it's not a prefix */ 191a3031715SAlexander Langer if (unitcount == MAXUNITS) { 192d4d0763aSPhilippe Charnier warnx("memory for units exceeded in line %d", linenum); 193a3031715SAlexander Langer continue; 194a3031715SAlexander Langer } 195a3031715SAlexander Langer unittable[unitcount].uname = dupstr(lineptr); 196a3031715SAlexander Langer for (i = 0; i < unitcount; i++) 197a3031715SAlexander Langer if (!strcmp(unittable[i].uname, lineptr)) { 198d4d0763aSPhilippe Charnier warnx("redefinition of unit '%s' on line %d ignored", 199a3031715SAlexander Langer lineptr, linenum); 200a3031715SAlexander Langer continue; 201a3031715SAlexander Langer } 202a3031715SAlexander Langer lineptr += len + 1; 203a3031715SAlexander Langer lineptr += strspn(lineptr, " \n\t"); 204a3031715SAlexander Langer if (!strlen(lineptr)) { 20517ad860fSDavid Malone warnx("unexpected end of unit on line %d", 20617ad860fSDavid Malone linenum); 207a3031715SAlexander Langer continue; 208a3031715SAlexander Langer } 209a3031715SAlexander Langer len = strcspn(lineptr, "\n\t"); 210a3031715SAlexander Langer lineptr[len] = 0; 211a3031715SAlexander Langer unittable[unitcount++].uval = dupstr(lineptr); 212a3031715SAlexander Langer } 213a3031715SAlexander Langer } 214a3031715SAlexander Langer fclose(unitfile); 215a3031715SAlexander Langer } 216a3031715SAlexander Langer 217a3031715SAlexander Langer void 218a3031715SAlexander Langer initializeunit(struct unittype * theunit) 219a3031715SAlexander Langer { 220a3031715SAlexander Langer theunit->numerator[0] = theunit->denominator[0] = NULL; 2219c95bc1cSDavid Malone theunit->factor = 1.0; 2229c95bc1cSDavid Malone theunit->offset = 0.0; 2239c95bc1cSDavid Malone theunit->quantity = 0; 224a3031715SAlexander Langer } 225a3031715SAlexander Langer 226a3031715SAlexander Langer 227a3031715SAlexander Langer int 228a3031715SAlexander Langer addsubunit(char *product[], char *toadd) 229a3031715SAlexander Langer { 230a3031715SAlexander Langer char **ptr; 231a3031715SAlexander Langer 232a3031715SAlexander Langer for (ptr = product; *ptr && *ptr != NULLUNIT; ptr++); 233a3031715SAlexander Langer if (ptr >= product + MAXSUBUNITS) { 234d4d0763aSPhilippe Charnier warnx("memory overflow in unit reduction"); 235a3031715SAlexander Langer return 1; 236a3031715SAlexander Langer } 237a3031715SAlexander Langer if (!*ptr) 238a3031715SAlexander Langer *(ptr + 1) = 0; 239a3031715SAlexander Langer *ptr = dupstr(toadd); 240a3031715SAlexander Langer return 0; 241a3031715SAlexander Langer } 242a3031715SAlexander Langer 243a3031715SAlexander Langer 244a3031715SAlexander Langer void 245a3031715SAlexander Langer showunit(struct unittype * theunit) 246a3031715SAlexander Langer { 247a3031715SAlexander Langer char **ptr; 248a3031715SAlexander Langer int printedslash; 249a3031715SAlexander Langer int counter = 1; 250a3031715SAlexander Langer 251a3031715SAlexander Langer printf("\t%.8g", theunit->factor); 2529c95bc1cSDavid Malone if (theunit->offset) 2539c95bc1cSDavid Malone printf("&%.8g", theunit->offset); 254a3031715SAlexander Langer for (ptr = theunit->numerator; *ptr; ptr++) { 255a3031715SAlexander Langer if (ptr > theunit->numerator && **ptr && 256a3031715SAlexander Langer !strcmp(*ptr, *(ptr - 1))) 257a3031715SAlexander Langer counter++; 258a3031715SAlexander Langer else { 259a3031715SAlexander Langer if (counter > 1) 260a3031715SAlexander Langer printf("%s%d", powerstring, counter); 261a3031715SAlexander Langer if (**ptr) 262a3031715SAlexander Langer printf(" %s", *ptr); 263a3031715SAlexander Langer counter = 1; 264a3031715SAlexander Langer } 265a3031715SAlexander Langer } 266a3031715SAlexander Langer if (counter > 1) 267a3031715SAlexander Langer printf("%s%d", powerstring, counter); 268a3031715SAlexander Langer counter = 1; 269a3031715SAlexander Langer printedslash = 0; 270a3031715SAlexander Langer for (ptr = theunit->denominator; *ptr; ptr++) { 271a3031715SAlexander Langer if (ptr > theunit->denominator && **ptr && 272a3031715SAlexander Langer !strcmp(*ptr, *(ptr - 1))) 273a3031715SAlexander Langer counter++; 274a3031715SAlexander Langer else { 275a3031715SAlexander Langer if (counter > 1) 276a3031715SAlexander Langer printf("%s%d", powerstring, counter); 277a3031715SAlexander Langer if (**ptr) { 278a3031715SAlexander Langer if (!printedslash) 279a3031715SAlexander Langer printf(" /"); 280a3031715SAlexander Langer printedslash = 1; 281a3031715SAlexander Langer printf(" %s", *ptr); 282a3031715SAlexander Langer } 283a3031715SAlexander Langer counter = 1; 284a3031715SAlexander Langer } 285a3031715SAlexander Langer } 286a3031715SAlexander Langer if (counter > 1) 287a3031715SAlexander Langer printf("%s%d", powerstring, counter); 288a3031715SAlexander Langer printf("\n"); 289a3031715SAlexander Langer } 290a3031715SAlexander Langer 291a3031715SAlexander Langer 292a3031715SAlexander Langer void 29317ad860fSDavid Malone zeroerror(void) 294a3031715SAlexander Langer { 295d4d0763aSPhilippe Charnier warnx("unit reduces to zero"); 296a3031715SAlexander Langer } 297a3031715SAlexander Langer 298a3031715SAlexander Langer /* 299a3031715SAlexander Langer Adds the specified string to the unit. 300a3031715SAlexander Langer Flip is 0 for adding normally, 1 for adding reciprocal. 3019c95bc1cSDavid Malone Quantity is 1 if this is a quantity to be converted rather than a pure unit. 302a3031715SAlexander Langer 303a3031715SAlexander Langer Returns 0 for successful addition, nonzero on error. 304a3031715SAlexander Langer */ 305a3031715SAlexander Langer 306a3031715SAlexander Langer int 3079c95bc1cSDavid Malone addunit(struct unittype * theunit, char *toadd, int flip, int quantity) 308a3031715SAlexander Langer { 309a3031715SAlexander Langer char *scratch, *savescr; 310a3031715SAlexander Langer char *item; 3119c95bc1cSDavid Malone char *divider, *slash, *offset; 312a3031715SAlexander Langer int doingtop; 313a3031715SAlexander Langer 314ffd044a6SKris Kennaway if (!strlen(toadd)) 315ffd044a6SKris Kennaway return 1; 316ffd044a6SKris Kennaway 317a3031715SAlexander Langer savescr = scratch = dupstr(toadd); 318a3031715SAlexander Langer for (slash = scratch + 1; *slash; slash++) 319a3031715SAlexander Langer if (*slash == '-' && 320a3031715SAlexander Langer (tolower(*(slash - 1)) != 'e' || 321a3031715SAlexander Langer !strchr(".0123456789", *(slash + 1)))) 322a3031715SAlexander Langer *slash = ' '; 323a3031715SAlexander Langer slash = strchr(scratch, '/'); 324a3031715SAlexander Langer if (slash) 325a3031715SAlexander Langer *slash = 0; 326a3031715SAlexander Langer doingtop = 1; 327a3031715SAlexander Langer do { 328a3031715SAlexander Langer item = strtok(scratch, " *\t\n/"); 329a3031715SAlexander Langer while (item) { 330a3031715SAlexander Langer if (strchr("0123456789.", *item)) { /* item is a number */ 3319c95bc1cSDavid Malone double num, offsetnum; 3329c95bc1cSDavid Malone 3339c95bc1cSDavid Malone if (quantity) 3349c95bc1cSDavid Malone theunit->quantity = 1; 3359c95bc1cSDavid Malone 3369c95bc1cSDavid Malone offset = strchr(item, '&'); 3379c95bc1cSDavid Malone if (offset) { 3389c95bc1cSDavid Malone *offset = 0; 3399c95bc1cSDavid Malone offsetnum = atof(offset+1); 3409c95bc1cSDavid Malone } else 3419c95bc1cSDavid Malone offsetnum = 0.0; 342a3031715SAlexander Langer 343a3031715SAlexander Langer divider = strchr(item, '|'); 344a3031715SAlexander Langer if (divider) { 345a3031715SAlexander Langer *divider = 0; 346a3031715SAlexander Langer num = atof(item); 347a3031715SAlexander Langer if (!num) { 348a3031715SAlexander Langer zeroerror(); 349a3031715SAlexander Langer return 1; 350a3031715SAlexander Langer } 3519c95bc1cSDavid Malone if (doingtop ^ flip) { 352a3031715SAlexander Langer theunit->factor *= num; 3539c95bc1cSDavid Malone theunit->offset *= num; 3549c95bc1cSDavid Malone } else { 355a3031715SAlexander Langer theunit->factor /= num; 3569c95bc1cSDavid Malone theunit->offset /= num; 3579c95bc1cSDavid Malone } 358a3031715SAlexander Langer num = atof(divider + 1); 359a3031715SAlexander Langer if (!num) { 360a3031715SAlexander Langer zeroerror(); 361a3031715SAlexander Langer return 1; 362a3031715SAlexander Langer } 3639c95bc1cSDavid Malone if (doingtop ^ flip) { 364a3031715SAlexander Langer theunit->factor /= num; 3659c95bc1cSDavid Malone theunit->offset /= num; 3669c95bc1cSDavid Malone } else { 367a3031715SAlexander Langer theunit->factor *= num; 3689c95bc1cSDavid Malone theunit->offset *= num; 3699c95bc1cSDavid Malone } 370a3031715SAlexander Langer } 371a3031715SAlexander Langer else { 372a3031715SAlexander Langer num = atof(item); 373a3031715SAlexander Langer if (!num) { 374a3031715SAlexander Langer zeroerror(); 375a3031715SAlexander Langer return 1; 376a3031715SAlexander Langer } 3779c95bc1cSDavid Malone if (doingtop ^ flip) { 378a3031715SAlexander Langer theunit->factor *= num; 3799c95bc1cSDavid Malone theunit->offset *= num; 3809c95bc1cSDavid Malone } else { 381a3031715SAlexander Langer theunit->factor /= num; 3829c95bc1cSDavid Malone theunit->offset /= num; 383a3031715SAlexander Langer } 384a3031715SAlexander Langer } 3859c95bc1cSDavid Malone if (doingtop ^ flip) 3869c95bc1cSDavid Malone theunit->offset += offsetnum; 3879c95bc1cSDavid Malone } 388a3031715SAlexander Langer else { /* item is not a number */ 389a3031715SAlexander Langer int repeat = 1; 390a3031715SAlexander Langer 391a3031715SAlexander Langer if (strchr("23456789", 392a3031715SAlexander Langer item[strlen(item) - 1])) { 393a3031715SAlexander Langer repeat = item[strlen(item) - 1] - '0'; 394a3031715SAlexander Langer item[strlen(item) - 1] = 0; 395a3031715SAlexander Langer } 396a3031715SAlexander Langer for (; repeat; repeat--) 397a3031715SAlexander Langer if (addsubunit(doingtop ^ flip ? theunit->numerator : theunit->denominator, item)) 398a3031715SAlexander Langer return 1; 399a3031715SAlexander Langer } 400a3031715SAlexander Langer item = strtok(NULL, " *\t/\n"); 401a3031715SAlexander Langer } 402a3031715SAlexander Langer doingtop--; 403a3031715SAlexander Langer if (slash) { 404a3031715SAlexander Langer scratch = slash + 1; 405a3031715SAlexander Langer } 406a3031715SAlexander Langer else 407a3031715SAlexander Langer doingtop--; 408a3031715SAlexander Langer } while (doingtop >= 0); 409a3031715SAlexander Langer free(savescr); 410a3031715SAlexander Langer return 0; 411a3031715SAlexander Langer } 412a3031715SAlexander Langer 413a3031715SAlexander Langer 414a3031715SAlexander Langer int 415a3031715SAlexander Langer compare(const void *item1, const void *item2) 416a3031715SAlexander Langer { 41750bb7724SDavid Malone return strcmp(*(const char * const *)item1, *(const char * const *)item2); 418a3031715SAlexander Langer } 419a3031715SAlexander Langer 420a3031715SAlexander Langer 421a3031715SAlexander Langer void 422a3031715SAlexander Langer sortunit(struct unittype * theunit) 423a3031715SAlexander Langer { 424a3031715SAlexander Langer char **ptr; 42517ad860fSDavid Malone unsigned int count; 426a3031715SAlexander Langer 427a3031715SAlexander Langer for (count = 0, ptr = theunit->numerator; *ptr; ptr++, count++); 428a3031715SAlexander Langer qsort(theunit->numerator, count, sizeof(char *), compare); 429a3031715SAlexander Langer for (count = 0, ptr = theunit->denominator; *ptr; ptr++, count++); 430a3031715SAlexander Langer qsort(theunit->denominator, count, sizeof(char *), compare); 431a3031715SAlexander Langer } 432a3031715SAlexander Langer 433a3031715SAlexander Langer 434a3031715SAlexander Langer void 435a3031715SAlexander Langer cancelunit(struct unittype * theunit) 436a3031715SAlexander Langer { 437a3031715SAlexander Langer char **den, **num; 438a3031715SAlexander Langer int comp; 439a3031715SAlexander Langer 440a3031715SAlexander Langer den = theunit->denominator; 441a3031715SAlexander Langer num = theunit->numerator; 442a3031715SAlexander Langer 443a3031715SAlexander Langer while (*num && *den) { 444a3031715SAlexander Langer comp = strcmp(*den, *num); 445a3031715SAlexander Langer if (!comp) { 446a3031715SAlexander Langer /* if (*den!=NULLUNIT) free(*den); 447a3031715SAlexander Langer if (*num!=NULLUNIT) free(*num);*/ 448a3031715SAlexander Langer *den++ = NULLUNIT; 449a3031715SAlexander Langer *num++ = NULLUNIT; 450a3031715SAlexander Langer } 451a3031715SAlexander Langer else if (comp < 0) 452a3031715SAlexander Langer den++; 453a3031715SAlexander Langer else 454a3031715SAlexander Langer num++; 455a3031715SAlexander Langer } 456a3031715SAlexander Langer } 457a3031715SAlexander Langer 458a3031715SAlexander Langer 459a3031715SAlexander Langer 460a3031715SAlexander Langer 461a3031715SAlexander Langer /* 462a3031715SAlexander Langer Looks up the definition for the specified unit. 463a3031715SAlexander Langer Returns a pointer to the definition or a null pointer 464a3031715SAlexander Langer if the specified unit does not appear in the units table. 465a3031715SAlexander Langer */ 466a3031715SAlexander Langer 467a3031715SAlexander Langer static char buffer[100]; /* buffer for lookupunit answers with 468a3031715SAlexander Langer prefixes */ 469a3031715SAlexander Langer 470a3031715SAlexander Langer char * 47117ad860fSDavid Malone lookupunit(const char *unit) 472a3031715SAlexander Langer { 473a3031715SAlexander Langer int i; 474a3031715SAlexander Langer char *copy; 475a3031715SAlexander Langer 476a3031715SAlexander Langer for (i = 0; i < unitcount; i++) { 477a3031715SAlexander Langer if (!strcmp(unittable[i].uname, unit)) 478a3031715SAlexander Langer return unittable[i].uval; 479a3031715SAlexander Langer } 480a3031715SAlexander Langer 481a3031715SAlexander Langer if (unit[strlen(unit) - 1] == '^') { 482a3031715SAlexander Langer copy = dupstr(unit); 483a3031715SAlexander Langer copy[strlen(copy) - 1] = 0; 484a3031715SAlexander Langer for (i = 0; i < unitcount; i++) { 485a3031715SAlexander Langer if (!strcmp(unittable[i].uname, copy)) { 486ffd044a6SKris Kennaway strlcpy(buffer, copy, sizeof(buffer)); 487a3031715SAlexander Langer free(copy); 488a3031715SAlexander Langer return buffer; 489a3031715SAlexander Langer } 490a3031715SAlexander Langer } 491a3031715SAlexander Langer free(copy); 492a3031715SAlexander Langer } 493a3031715SAlexander Langer if (unit[strlen(unit) - 1] == 's') { 494a3031715SAlexander Langer copy = dupstr(unit); 495a3031715SAlexander Langer copy[strlen(copy) - 1] = 0; 496a3031715SAlexander Langer for (i = 0; i < unitcount; i++) { 497a3031715SAlexander Langer if (!strcmp(unittable[i].uname, copy)) { 498ffd044a6SKris Kennaway strlcpy(buffer, copy, sizeof(buffer)); 499a3031715SAlexander Langer free(copy); 500a3031715SAlexander Langer return buffer; 501a3031715SAlexander Langer } 502a3031715SAlexander Langer } 503a3031715SAlexander Langer if (copy[strlen(copy) - 1] == 'e') { 504a3031715SAlexander Langer copy[strlen(copy) - 1] = 0; 505a3031715SAlexander Langer for (i = 0; i < unitcount; i++) { 506a3031715SAlexander Langer if (!strcmp(unittable[i].uname, copy)) { 507ffd044a6SKris Kennaway strlcpy(buffer, copy, sizeof(buffer)); 508a3031715SAlexander Langer free(copy); 509a3031715SAlexander Langer return buffer; 510a3031715SAlexander Langer } 511a3031715SAlexander Langer } 512a3031715SAlexander Langer } 513a3031715SAlexander Langer free(copy); 514a3031715SAlexander Langer } 515a3031715SAlexander Langer for (i = 0; i < prefixcount; i++) { 51617ad860fSDavid Malone size_t len = strlen(prefixtable[i].prefixname); 51717ad860fSDavid Malone if (!strncmp(prefixtable[i].prefixname, unit, len)) { 51817ad860fSDavid Malone if (!strlen(unit + len) || lookupunit(unit + len)) { 519ffd044a6SKris Kennaway snprintf(buffer, sizeof(buffer), "%s %s", 52017ad860fSDavid Malone prefixtable[i].prefixval, unit + len); 521a3031715SAlexander Langer return buffer; 522a3031715SAlexander Langer } 523a3031715SAlexander Langer } 524a3031715SAlexander Langer } 525a3031715SAlexander Langer return 0; 526a3031715SAlexander Langer } 527a3031715SAlexander Langer 528a3031715SAlexander Langer 529a3031715SAlexander Langer 530a3031715SAlexander Langer /* 531a3031715SAlexander Langer reduces a product of symbolic units to primitive units. 532a3031715SAlexander Langer The three low bits are used to return flags: 533a3031715SAlexander Langer 534a3031715SAlexander Langer bit 0 (1) set on if reductions were performed without error. 535a3031715SAlexander Langer bit 1 (2) set on if no reductions are performed. 536a3031715SAlexander Langer bit 2 (4) set on if an unknown unit is discovered. 537a3031715SAlexander Langer */ 538a3031715SAlexander Langer 539a3031715SAlexander Langer 540a3031715SAlexander Langer #define ERROR 4 541a3031715SAlexander Langer 542a3031715SAlexander Langer int 543a3031715SAlexander Langer reduceproduct(struct unittype * theunit, int flip) 544a3031715SAlexander Langer { 545a3031715SAlexander Langer 546a3031715SAlexander Langer char *toadd; 547a3031715SAlexander Langer char **product; 548a3031715SAlexander Langer int didsomething = 2; 549a3031715SAlexander Langer 550a3031715SAlexander Langer if (flip) 551a3031715SAlexander Langer product = theunit->denominator; 552a3031715SAlexander Langer else 553a3031715SAlexander Langer product = theunit->numerator; 554a3031715SAlexander Langer 555a3031715SAlexander Langer for (; *product; product++) { 556a3031715SAlexander Langer 557a3031715SAlexander Langer for (;;) { 558a3031715SAlexander Langer if (!strlen(*product)) 559a3031715SAlexander Langer break; 560a3031715SAlexander Langer toadd = lookupunit(*product); 561a3031715SAlexander Langer if (!toadd) { 562a3031715SAlexander Langer printf("unknown unit '%s'\n", *product); 563a3031715SAlexander Langer return ERROR; 564a3031715SAlexander Langer } 565a3031715SAlexander Langer if (strchr(toadd, PRIMITIVECHAR)) 566a3031715SAlexander Langer break; 567a3031715SAlexander Langer didsomething = 1; 568a3031715SAlexander Langer if (*product != NULLUNIT) { 569a3031715SAlexander Langer free(*product); 570a3031715SAlexander Langer *product = NULLUNIT; 571a3031715SAlexander Langer } 5729c95bc1cSDavid Malone if (addunit(theunit, toadd, flip, 0)) 573a3031715SAlexander Langer return ERROR; 574a3031715SAlexander Langer } 575a3031715SAlexander Langer } 576a3031715SAlexander Langer return didsomething; 577a3031715SAlexander Langer } 578a3031715SAlexander Langer 579a3031715SAlexander Langer 580a3031715SAlexander Langer /* 581a3031715SAlexander Langer Reduces numerator and denominator of the specified unit. 582a3031715SAlexander Langer Returns 0 on success, or 1 on unknown unit error. 583a3031715SAlexander Langer */ 584a3031715SAlexander Langer 585a3031715SAlexander Langer int 586a3031715SAlexander Langer reduceunit(struct unittype * theunit) 587a3031715SAlexander Langer { 588a3031715SAlexander Langer int ret; 589a3031715SAlexander Langer 590a3031715SAlexander Langer ret = 1; 591a3031715SAlexander Langer while (ret & 1) { 592a3031715SAlexander Langer ret = reduceproduct(theunit, 0) | reduceproduct(theunit, 1); 593a3031715SAlexander Langer if (ret & 4) 594a3031715SAlexander Langer return 1; 595a3031715SAlexander Langer } 596a3031715SAlexander Langer return 0; 597a3031715SAlexander Langer } 598a3031715SAlexander Langer 599a3031715SAlexander Langer 600a3031715SAlexander Langer int 601a3031715SAlexander Langer compareproducts(char **one, char **two) 602a3031715SAlexander Langer { 603a3031715SAlexander Langer while (*one || *two) { 604a3031715SAlexander Langer if (!*one && *two != NULLUNIT) 605a3031715SAlexander Langer return 1; 606a3031715SAlexander Langer if (!*two && *one != NULLUNIT) 607a3031715SAlexander Langer return 1; 608a3031715SAlexander Langer if (*one == NULLUNIT) 609a3031715SAlexander Langer one++; 610a3031715SAlexander Langer else if (*two == NULLUNIT) 611a3031715SAlexander Langer two++; 612a3031715SAlexander Langer else if (strcmp(*one, *two)) 613a3031715SAlexander Langer return 1; 614a3031715SAlexander Langer else 615a3031715SAlexander Langer one++, two++; 616a3031715SAlexander Langer } 617a3031715SAlexander Langer return 0; 618a3031715SAlexander Langer } 619a3031715SAlexander Langer 620a3031715SAlexander Langer 621a3031715SAlexander Langer /* Return zero if units are compatible, nonzero otherwise */ 622a3031715SAlexander Langer 623a3031715SAlexander Langer int 624a3031715SAlexander Langer compareunits(struct unittype * first, struct unittype * second) 625a3031715SAlexander Langer { 626a3031715SAlexander Langer return 627a3031715SAlexander Langer compareproducts(first->numerator, second->numerator) || 628a3031715SAlexander Langer compareproducts(first->denominator, second->denominator); 629a3031715SAlexander Langer } 630a3031715SAlexander Langer 631a3031715SAlexander Langer 632a3031715SAlexander Langer int 633a3031715SAlexander Langer completereduce(struct unittype * unit) 634a3031715SAlexander Langer { 635a3031715SAlexander Langer if (reduceunit(unit)) 636a3031715SAlexander Langer return 1; 637a3031715SAlexander Langer sortunit(unit); 638a3031715SAlexander Langer cancelunit(unit); 639a3031715SAlexander Langer return 0; 640a3031715SAlexander Langer } 641a3031715SAlexander Langer 642a3031715SAlexander Langer 643a3031715SAlexander Langer void 644a3031715SAlexander Langer showanswer(struct unittype * have, struct unittype * want) 645a3031715SAlexander Langer { 646a3031715SAlexander Langer if (compareunits(have, want)) { 647a3031715SAlexander Langer printf("conformability error\n"); 648a3031715SAlexander Langer showunit(have); 649a3031715SAlexander Langer showunit(want); 650a3031715SAlexander Langer } 6519c95bc1cSDavid Malone else if (have->offset != want->offset) { 6529c95bc1cSDavid Malone if (want->quantity) 6539c95bc1cSDavid Malone printf("WARNING: conversion of non-proportional quantities.\n"); 6549c95bc1cSDavid Malone printf("\t"); 6559c95bc1cSDavid Malone if (have->quantity) 6569c95bc1cSDavid Malone printf("%.8g\n", 6579c95bc1cSDavid Malone (have->factor + have->offset-want->offset)/want->factor); 6589c95bc1cSDavid Malone else 6599c95bc1cSDavid Malone printf(" (-> x*%.8g %+.8g)\n\t (<- y*%.8g %+.8g)\n", 6609c95bc1cSDavid Malone have->factor / want->factor, 6619c95bc1cSDavid Malone (have->offset-want->offset)/want->factor, 6629c95bc1cSDavid Malone want->factor / have->factor, 6639c95bc1cSDavid Malone (want->offset - have->offset)/have->factor); 6649c95bc1cSDavid Malone } 665a3031715SAlexander Langer else 666a3031715SAlexander Langer printf("\t* %.8g\n\t/ %.8g\n", have->factor / want->factor, 667a3031715SAlexander Langer want->factor / have->factor); 668a3031715SAlexander Langer } 669a3031715SAlexander Langer 670a3031715SAlexander Langer 671a3031715SAlexander Langer void 67217ad860fSDavid Malone usage(void) 673a3031715SAlexander Langer { 674d4d0763aSPhilippe Charnier fprintf(stderr, 675d4d0763aSPhilippe Charnier "usage: units [-f unitsfile] [-q] [-v] [from-unit to-unit]\n"); 676a3031715SAlexander Langer exit(3); 677a3031715SAlexander Langer } 678a3031715SAlexander Langer 679a3031715SAlexander Langer 680a3031715SAlexander Langer int 681a3031715SAlexander Langer main(int argc, char **argv) 682a3031715SAlexander Langer { 683a3031715SAlexander Langer 684a3031715SAlexander Langer struct unittype have, want; 685a3031715SAlexander Langer char havestr[81], wantstr[81]; 686a3031715SAlexander Langer int optchar; 687a3031715SAlexander Langer char *userfile = 0; 688a3031715SAlexander Langer int quiet = 0; 689a3031715SAlexander Langer 690*6d12a834SEitan Adler while ((optchar = getopt(argc, argv, "Vqf:")) != -1) { 691a3031715SAlexander Langer switch (optchar) { 692a3031715SAlexander Langer case 'f': 693a3031715SAlexander Langer userfile = optarg; 694a3031715SAlexander Langer break; 695a3031715SAlexander Langer case 'q': 696a3031715SAlexander Langer quiet = 1; 697a3031715SAlexander Langer break; 698*6d12a834SEitan Adler case 'V': 699*6d12a834SEitan Adler fprintf(stderr, "FreeBSD units\n"); 700a3031715SAlexander Langer usage(); 701a3031715SAlexander Langer break; 702*6d12a834SEitan Adler default: 703*6d12a834SEitan Adler usage(); 704a3031715SAlexander Langer } 705a3031715SAlexander Langer } 706a3031715SAlexander Langer 707a3031715SAlexander Langer if (optind != argc - 2 && optind != argc) 708a3031715SAlexander Langer usage(); 709a3031715SAlexander Langer 710a3031715SAlexander Langer readunits(userfile); 711a3031715SAlexander Langer 712a3031715SAlexander Langer if (optind == argc - 2) { 713ffd044a6SKris Kennaway strlcpy(havestr, argv[optind], sizeof(havestr)); 714ffd044a6SKris Kennaway strlcpy(wantstr, argv[optind + 1], sizeof(wantstr)); 715a3031715SAlexander Langer initializeunit(&have); 7169c95bc1cSDavid Malone addunit(&have, havestr, 0, 1); 717a3031715SAlexander Langer completereduce(&have); 718a3031715SAlexander Langer initializeunit(&want); 7199c95bc1cSDavid Malone addunit(&want, wantstr, 0, 1); 720a3031715SAlexander Langer completereduce(&want); 721a3031715SAlexander Langer showanswer(&have, &want); 722a3031715SAlexander Langer } 723a3031715SAlexander Langer else { 724a3031715SAlexander Langer if (!quiet) 725d4d0763aSPhilippe Charnier printf("%d units, %d prefixes\n", unitcount, 726a3031715SAlexander Langer prefixcount); 727a3031715SAlexander Langer for (;;) { 728a3031715SAlexander Langer do { 729a3031715SAlexander Langer initializeunit(&have); 730a3031715SAlexander Langer if (!quiet) 731a3031715SAlexander Langer printf("You have: "); 73217ad860fSDavid Malone if (!fgets(havestr, sizeof(havestr), stdin)) { 7334c0c227dSAlexander Langer if (!quiet) 734a3031715SAlexander Langer putchar('\n'); 735a3031715SAlexander Langer exit(0); 736a3031715SAlexander Langer } 7379c95bc1cSDavid Malone } while (addunit(&have, havestr, 0, 1) || 738a3031715SAlexander Langer completereduce(&have)); 739a3031715SAlexander Langer do { 740a3031715SAlexander Langer initializeunit(&want); 741a3031715SAlexander Langer if (!quiet) 742a3031715SAlexander Langer printf("You want: "); 74317ad860fSDavid Malone if (!fgets(wantstr, sizeof(wantstr), stdin)) { 744a3031715SAlexander Langer if (!quiet) 745a3031715SAlexander Langer putchar('\n'); 746a3031715SAlexander Langer exit(0); 747a3031715SAlexander Langer } 7489c95bc1cSDavid Malone } while (addunit(&want, wantstr, 0, 1) || 749a3031715SAlexander Langer completereduce(&want)); 750a3031715SAlexander Langer showanswer(&have, &want); 751a3031715SAlexander Langer } 752a3031715SAlexander Langer } 7534c0c227dSAlexander Langer 7544c0c227dSAlexander Langer return(0); 755a3031715SAlexander Langer } 756