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> 26647be2c3SEitan Adler #include <histedit.h> 275192ff45SEitan Adler #include <getopt.h> 28647be2c3SEitan Adler #include <stdbool.h> 29a3031715SAlexander Langer #include <stdio.h> 30a3031715SAlexander Langer #include <stdlib.h> 31d4d0763aSPhilippe Charnier #include <string.h> 32d4d0763aSPhilippe Charnier #include <unistd.h> 33a3031715SAlexander Langer 34ac117d5dSEitan Adler #include <sys/capsicum.h> 35ac117d5dSEitan Adler 36a3031715SAlexander Langer #include "pathnames.h" 37a3031715SAlexander Langer 38a3031715SAlexander Langer #ifndef UNITSFILE 39a3031715SAlexander Langer #define UNITSFILE _PATH_UNITSLIB 40a3031715SAlexander Langer #endif 41a3031715SAlexander Langer 42a3031715SAlexander Langer #define MAXUNITS 1000 4317ad860fSDavid Malone #define MAXPREFIXES 100 44a3031715SAlexander Langer 45a3031715SAlexander Langer #define MAXSUBUNITS 500 46a3031715SAlexander Langer 47a3031715SAlexander Langer #define PRIMITIVECHAR '!' 48a3031715SAlexander Langer 4990a29505SEd Schouten static const char *powerstring = "^"; 50a3031715SAlexander Langer 5190a29505SEd Schouten static struct { 52a3031715SAlexander Langer char *uname; 53a3031715SAlexander Langer char *uval; 54a3031715SAlexander Langer } unittable[MAXUNITS]; 55a3031715SAlexander Langer 56a3031715SAlexander Langer struct unittype { 57a3031715SAlexander Langer char *numerator[MAXSUBUNITS]; 58a3031715SAlexander Langer char *denominator[MAXSUBUNITS]; 59a3031715SAlexander Langer double factor; 609c95bc1cSDavid Malone double offset; 619c95bc1cSDavid Malone int quantity; 62a3031715SAlexander Langer }; 63a3031715SAlexander Langer 6490a29505SEd Schouten static struct { 65a3031715SAlexander Langer char *prefixname; 66a3031715SAlexander Langer char *prefixval; 67a3031715SAlexander Langer } prefixtable[MAXPREFIXES]; 68a3031715SAlexander Langer 69a3031715SAlexander Langer 7090a29505SEd Schouten static char NULLUNIT[] = ""; 71a3031715SAlexander Langer 72ffd044a6SKris Kennaway #ifdef MSDOS 7317ad860fSDavid Malone #define SEPARATOR ";" 74ffd044a6SKris Kennaway #else 75ffd044a6SKris Kennaway #define SEPARATOR ":" 76ffd044a6SKris Kennaway #endif 77ffd044a6SKris Kennaway 7890a29505SEd Schouten static int unitcount; 7990a29505SEd Schouten static int prefixcount; 809653775eSEitan Adler static bool verbose = false; 818d61f393SEitan Adler static bool terse = false; 829653775eSEitan Adler static const char * havestr; 839653775eSEitan Adler static const char * wantstr; 849653775eSEitan Adler 85dc8c916dSEitan Adler static int addsubunit(char *product[], char *toadd); 86dc8c916dSEitan Adler static int addunit(struct unittype *theunit, const char *toadd, int flip, int quantity); 87dc8c916dSEitan Adler static void cancelunit(struct unittype * theunit); 88dc8c916dSEitan Adler static int compare(const void *item1, const void *item2); 89dc8c916dSEitan Adler static int compareproducts(char **one, char **two); 90dc8c916dSEitan Adler static int compareunits(struct unittype * first, struct unittype * second); 91dc8c916dSEitan Adler static int completereduce(struct unittype * unit); 92dc8c916dSEitan Adler static char *dupstr(const char *str); 93dc8c916dSEitan Adler static void initializeunit(struct unittype * theunit); 94dc8c916dSEitan Adler static char *lookupunit(const char *unit); 95dc8c916dSEitan Adler static void readunits(const char *userfile); 96dc8c916dSEitan Adler static int reduceproduct(struct unittype * theunit, int flip); 97dc8c916dSEitan Adler static int reduceunit(struct unittype * theunit); 98dc8c916dSEitan Adler static void showanswer(struct unittype * have, struct unittype * want); 99dc8c916dSEitan Adler static void showunit(struct unittype * theunit); 100dc8c916dSEitan Adler static void sortunit(struct unittype * theunit); 101dc8c916dSEitan Adler static void usage(void); 102dc8c916dSEitan Adler static void zeroerror(void); 103a3031715SAlexander Langer 104647be2c3SEitan Adler static const char* promptstr = ""; 105647be2c3SEitan Adler 106647be2c3SEitan Adler static const char * prompt(EditLine *e __unused) { 107647be2c3SEitan Adler return promptstr; 108647be2c3SEitan Adler } 109647be2c3SEitan Adler 110a3031715SAlexander Langer char * 11117ad860fSDavid Malone dupstr(const char *str) 112a3031715SAlexander Langer { 113a3031715SAlexander Langer char *ret; 114a3031715SAlexander Langer 115cfa8c236SEitan Adler ret = strdup(str); 116d4d0763aSPhilippe Charnier if (!ret) 117cfa8c236SEitan Adler err(3, "dupstr"); 118a3031715SAlexander Langer return (ret); 119a3031715SAlexander Langer } 120a3031715SAlexander Langer 121a3031715SAlexander Langer 122a3031715SAlexander Langer void 12317ad860fSDavid Malone readunits(const char *userfile) 124a3031715SAlexander Langer { 125a3031715SAlexander Langer FILE *unitfile; 12617ad860fSDavid Malone char line[512], *lineptr; 127a3031715SAlexander Langer int len, linenum, i; 128ac117d5dSEitan Adler cap_rights_t unitfilerights; 129a3031715SAlexander Langer 130a3031715SAlexander Langer unitcount = 0; 131a3031715SAlexander Langer linenum = 0; 132a3031715SAlexander Langer 133a3031715SAlexander Langer if (userfile) { 134a3031715SAlexander Langer unitfile = fopen(userfile, "rt"); 135d4d0763aSPhilippe Charnier if (!unitfile) 136d4d0763aSPhilippe Charnier errx(1, "unable to open units file '%s'", userfile); 137a3031715SAlexander Langer } 138a3031715SAlexander Langer else { 139a3031715SAlexander Langer unitfile = fopen(UNITSFILE, "rt"); 140a3031715SAlexander Langer if (!unitfile) { 141a3031715SAlexander Langer char *direc, *env; 142a3031715SAlexander Langer char filename[1000]; 143a3031715SAlexander Langer 144a3031715SAlexander Langer env = getenv("PATH"); 145a3031715SAlexander Langer if (env) { 146ffd044a6SKris Kennaway direc = strtok(env, SEPARATOR); 147a3031715SAlexander Langer while (direc) { 148ffd044a6SKris Kennaway snprintf(filename, sizeof(filename), 149ffd044a6SKris Kennaway "%s/%s", direc, UNITSFILE); 150a3031715SAlexander Langer unitfile = fopen(filename, "rt"); 151a3031715SAlexander Langer if (unitfile) 152a3031715SAlexander Langer break; 153ffd044a6SKris Kennaway direc = strtok(NULL, SEPARATOR); 154a3031715SAlexander Langer } 155a3031715SAlexander Langer } 156d4d0763aSPhilippe Charnier if (!unitfile) 157d4d0763aSPhilippe Charnier errx(1, "can't find units file '%s'", UNITSFILE); 158a3031715SAlexander Langer } 159a3031715SAlexander Langer } 160ac117d5dSEitan Adler cap_rights_init(&unitfilerights, CAP_READ, CAP_FSTAT); 161ac117d5dSEitan Adler if (cap_rights_limit(fileno(unitfile), &unitfilerights) < 0 162ac117d5dSEitan Adler && errno != ENOSYS) 163ac117d5dSEitan Adler err(1, "cap_rights_limit() failed"); 164a3031715SAlexander Langer while (!feof(unitfile)) { 16517ad860fSDavid Malone if (!fgets(line, sizeof(line), unitfile)) 166a3031715SAlexander Langer break; 167a3031715SAlexander Langer linenum++; 168a3031715SAlexander Langer lineptr = line; 169*0ce97bdfSEitan Adler if (*lineptr == '/' || *lineptr == '#') 170a3031715SAlexander Langer continue; 171a3031715SAlexander Langer lineptr += strspn(lineptr, " \n\t"); 172a3031715SAlexander Langer len = strcspn(lineptr, " \n\t"); 173a3031715SAlexander Langer lineptr[len] = 0; 174a3031715SAlexander Langer if (!strlen(lineptr)) 175a3031715SAlexander Langer continue; 176a3031715SAlexander Langer if (lineptr[strlen(lineptr) - 1] == '-') { /* it's a prefix */ 177a3031715SAlexander Langer if (prefixcount == MAXPREFIXES) { 178d4d0763aSPhilippe Charnier warnx("memory for prefixes exceeded in line %d", linenum); 179a3031715SAlexander Langer continue; 180a3031715SAlexander Langer } 181a3031715SAlexander Langer lineptr[strlen(lineptr) - 1] = 0; 182a3031715SAlexander Langer prefixtable[prefixcount].prefixname = dupstr(lineptr); 183a3031715SAlexander Langer for (i = 0; i < prefixcount; i++) 184a3031715SAlexander Langer if (!strcmp(prefixtable[i].prefixname, lineptr)) { 185d4d0763aSPhilippe Charnier warnx("redefinition of prefix '%s' on line %d ignored", 186a3031715SAlexander Langer lineptr, linenum); 187a3031715SAlexander Langer continue; 188a3031715SAlexander Langer } 189a3031715SAlexander Langer lineptr += len + 1; 190a3031715SAlexander Langer lineptr += strspn(lineptr, " \n\t"); 191a3031715SAlexander Langer len = strcspn(lineptr, "\n\t"); 19217ad860fSDavid Malone if (len == 0) { 19317ad860fSDavid Malone warnx("unexpected end of prefix on line %d", 19417ad860fSDavid Malone linenum); 19517ad860fSDavid Malone continue; 19617ad860fSDavid Malone } 197a3031715SAlexander Langer lineptr[len] = 0; 198a3031715SAlexander Langer prefixtable[prefixcount++].prefixval = dupstr(lineptr); 199a3031715SAlexander Langer } 200a3031715SAlexander Langer else { /* it's not a prefix */ 201a3031715SAlexander Langer if (unitcount == MAXUNITS) { 202d4d0763aSPhilippe Charnier warnx("memory for units exceeded in line %d", linenum); 203a3031715SAlexander Langer continue; 204a3031715SAlexander Langer } 205a3031715SAlexander Langer unittable[unitcount].uname = dupstr(lineptr); 206a3031715SAlexander Langer for (i = 0; i < unitcount; i++) 207a3031715SAlexander Langer if (!strcmp(unittable[i].uname, lineptr)) { 208d4d0763aSPhilippe Charnier warnx("redefinition of unit '%s' on line %d ignored", 209a3031715SAlexander Langer lineptr, linenum); 210a3031715SAlexander Langer continue; 211a3031715SAlexander Langer } 212a3031715SAlexander Langer lineptr += len + 1; 213a3031715SAlexander Langer lineptr += strspn(lineptr, " \n\t"); 214a3031715SAlexander Langer if (!strlen(lineptr)) { 21517ad860fSDavid Malone warnx("unexpected end of unit on line %d", 21617ad860fSDavid Malone linenum); 217a3031715SAlexander Langer continue; 218a3031715SAlexander Langer } 219a3031715SAlexander Langer len = strcspn(lineptr, "\n\t"); 220a3031715SAlexander Langer lineptr[len] = 0; 221a3031715SAlexander Langer unittable[unitcount++].uval = dupstr(lineptr); 222a3031715SAlexander Langer } 223a3031715SAlexander Langer } 224a3031715SAlexander Langer fclose(unitfile); 225a3031715SAlexander Langer } 226a3031715SAlexander Langer 227a3031715SAlexander Langer void 228a3031715SAlexander Langer initializeunit(struct unittype * theunit) 229a3031715SAlexander Langer { 230a3031715SAlexander Langer theunit->numerator[0] = theunit->denominator[0] = NULL; 2319c95bc1cSDavid Malone theunit->factor = 1.0; 2329c95bc1cSDavid Malone theunit->offset = 0.0; 2339c95bc1cSDavid Malone theunit->quantity = 0; 234a3031715SAlexander Langer } 235a3031715SAlexander Langer 236a3031715SAlexander Langer 237a3031715SAlexander Langer int 238a3031715SAlexander Langer addsubunit(char *product[], char *toadd) 239a3031715SAlexander Langer { 240a3031715SAlexander Langer char **ptr; 241a3031715SAlexander Langer 242a3031715SAlexander Langer for (ptr = product; *ptr && *ptr != NULLUNIT; ptr++); 243a3031715SAlexander Langer if (ptr >= product + MAXSUBUNITS) { 244d4d0763aSPhilippe Charnier warnx("memory overflow in unit reduction"); 245a3031715SAlexander Langer return 1; 246a3031715SAlexander Langer } 247a3031715SAlexander Langer if (!*ptr) 248956c78c9SEitan Adler *(ptr + 1) = NULL; 249a3031715SAlexander Langer *ptr = dupstr(toadd); 250a3031715SAlexander Langer return 0; 251a3031715SAlexander Langer } 252a3031715SAlexander Langer 253a3031715SAlexander Langer 254a3031715SAlexander Langer void 255a3031715SAlexander Langer showunit(struct unittype * theunit) 256a3031715SAlexander Langer { 257a3031715SAlexander Langer char **ptr; 258a3031715SAlexander Langer int printedslash; 259a3031715SAlexander Langer int counter = 1; 260a3031715SAlexander Langer 2619653775eSEitan Adler printf("%.8g", theunit->factor); 2629c95bc1cSDavid Malone if (theunit->offset) 2639c95bc1cSDavid Malone printf("&%.8g", theunit->offset); 264a3031715SAlexander Langer for (ptr = theunit->numerator; *ptr; ptr++) { 265a3031715SAlexander Langer if (ptr > theunit->numerator && **ptr && 266a3031715SAlexander Langer !strcmp(*ptr, *(ptr - 1))) 267a3031715SAlexander Langer counter++; 268a3031715SAlexander Langer else { 269a3031715SAlexander Langer if (counter > 1) 270a3031715SAlexander Langer printf("%s%d", powerstring, counter); 271a3031715SAlexander Langer if (**ptr) 272a3031715SAlexander Langer printf(" %s", *ptr); 273a3031715SAlexander Langer counter = 1; 274a3031715SAlexander Langer } 275a3031715SAlexander Langer } 276a3031715SAlexander Langer if (counter > 1) 277a3031715SAlexander Langer printf("%s%d", powerstring, counter); 278a3031715SAlexander Langer counter = 1; 279a3031715SAlexander Langer printedslash = 0; 280a3031715SAlexander Langer for (ptr = theunit->denominator; *ptr; ptr++) { 281a3031715SAlexander Langer if (ptr > theunit->denominator && **ptr && 282a3031715SAlexander Langer !strcmp(*ptr, *(ptr - 1))) 283a3031715SAlexander Langer counter++; 284a3031715SAlexander Langer else { 285a3031715SAlexander Langer if (counter > 1) 286a3031715SAlexander Langer printf("%s%d", powerstring, counter); 287a3031715SAlexander Langer if (**ptr) { 288a3031715SAlexander Langer if (!printedslash) 289a3031715SAlexander Langer printf(" /"); 290a3031715SAlexander Langer printedslash = 1; 291a3031715SAlexander Langer printf(" %s", *ptr); 292a3031715SAlexander Langer } 293a3031715SAlexander Langer counter = 1; 294a3031715SAlexander Langer } 295a3031715SAlexander Langer } 296a3031715SAlexander Langer if (counter > 1) 297a3031715SAlexander Langer printf("%s%d", powerstring, counter); 298a3031715SAlexander Langer printf("\n"); 299a3031715SAlexander Langer } 300a3031715SAlexander Langer 301a3031715SAlexander Langer 302a3031715SAlexander Langer void 30317ad860fSDavid Malone zeroerror(void) 304a3031715SAlexander Langer { 305d4d0763aSPhilippe Charnier warnx("unit reduces to zero"); 306a3031715SAlexander Langer } 307a3031715SAlexander Langer 308a3031715SAlexander Langer /* 309a3031715SAlexander Langer Adds the specified string to the unit. 310a3031715SAlexander Langer Flip is 0 for adding normally, 1 for adding reciprocal. 3119c95bc1cSDavid Malone Quantity is 1 if this is a quantity to be converted rather than a pure unit. 312a3031715SAlexander Langer 313a3031715SAlexander Langer Returns 0 for successful addition, nonzero on error. 314a3031715SAlexander Langer */ 315a3031715SAlexander Langer 316a3031715SAlexander Langer int 317647be2c3SEitan Adler addunit(struct unittype * theunit, const char *toadd, int flip, int quantity) 318a3031715SAlexander Langer { 319a3031715SAlexander Langer char *scratch, *savescr; 320a3031715SAlexander Langer char *item; 3219c95bc1cSDavid Malone char *divider, *slash, *offset; 322a3031715SAlexander Langer int doingtop; 323a3031715SAlexander Langer 324ffd044a6SKris Kennaway if (!strlen(toadd)) 325ffd044a6SKris Kennaway return 1; 326ffd044a6SKris Kennaway 327a3031715SAlexander Langer savescr = scratch = dupstr(toadd); 328a3031715SAlexander Langer for (slash = scratch + 1; *slash; slash++) 329a3031715SAlexander Langer if (*slash == '-' && 330a3031715SAlexander Langer (tolower(*(slash - 1)) != 'e' || 331a3031715SAlexander Langer !strchr(".0123456789", *(slash + 1)))) 332a3031715SAlexander Langer *slash = ' '; 333a3031715SAlexander Langer slash = strchr(scratch, '/'); 334a3031715SAlexander Langer if (slash) 335a3031715SAlexander Langer *slash = 0; 336a3031715SAlexander Langer doingtop = 1; 337a3031715SAlexander Langer do { 338a3031715SAlexander Langer item = strtok(scratch, " *\t\n/"); 339a3031715SAlexander Langer while (item) { 340a3031715SAlexander Langer if (strchr("0123456789.", *item)) { /* item is a number */ 3419c95bc1cSDavid Malone double num, offsetnum; 3429c95bc1cSDavid Malone 3439c95bc1cSDavid Malone if (quantity) 3449c95bc1cSDavid Malone theunit->quantity = 1; 3459c95bc1cSDavid Malone 3469c95bc1cSDavid Malone offset = strchr(item, '&'); 3479c95bc1cSDavid Malone if (offset) { 3489c95bc1cSDavid Malone *offset = 0; 3499c95bc1cSDavid Malone offsetnum = atof(offset+1); 3509c95bc1cSDavid Malone } else 3519c95bc1cSDavid Malone offsetnum = 0.0; 352a3031715SAlexander Langer 353a3031715SAlexander Langer divider = strchr(item, '|'); 354a3031715SAlexander Langer if (divider) { 355a3031715SAlexander Langer *divider = 0; 356a3031715SAlexander Langer num = atof(item); 357a3031715SAlexander Langer if (!num) { 358a3031715SAlexander Langer zeroerror(); 359a3031715SAlexander Langer return 1; 360a3031715SAlexander Langer } 3619c95bc1cSDavid Malone if (doingtop ^ flip) { 362a3031715SAlexander Langer theunit->factor *= num; 3639c95bc1cSDavid Malone theunit->offset *= num; 3649c95bc1cSDavid Malone } else { 365a3031715SAlexander Langer theunit->factor /= num; 3669c95bc1cSDavid Malone theunit->offset /= num; 3679c95bc1cSDavid Malone } 368a3031715SAlexander Langer num = atof(divider + 1); 369a3031715SAlexander Langer if (!num) { 370a3031715SAlexander Langer zeroerror(); 371a3031715SAlexander Langer return 1; 372a3031715SAlexander Langer } 3739c95bc1cSDavid Malone if (doingtop ^ flip) { 374a3031715SAlexander Langer theunit->factor /= num; 3759c95bc1cSDavid Malone theunit->offset /= num; 3769c95bc1cSDavid Malone } else { 377a3031715SAlexander Langer theunit->factor *= num; 3789c95bc1cSDavid Malone theunit->offset *= num; 3799c95bc1cSDavid Malone } 380a3031715SAlexander Langer } 381a3031715SAlexander Langer else { 382a3031715SAlexander Langer num = atof(item); 383a3031715SAlexander Langer if (!num) { 384a3031715SAlexander Langer zeroerror(); 385a3031715SAlexander Langer return 1; 386a3031715SAlexander Langer } 3879c95bc1cSDavid Malone if (doingtop ^ flip) { 388a3031715SAlexander Langer theunit->factor *= num; 3899c95bc1cSDavid Malone theunit->offset *= num; 3909c95bc1cSDavid Malone } else { 391a3031715SAlexander Langer theunit->factor /= num; 3929c95bc1cSDavid Malone theunit->offset /= num; 393a3031715SAlexander Langer } 394a3031715SAlexander Langer } 3959c95bc1cSDavid Malone if (doingtop ^ flip) 3969c95bc1cSDavid Malone theunit->offset += offsetnum; 3979c95bc1cSDavid Malone } 398a3031715SAlexander Langer else { /* item is not a number */ 399a3031715SAlexander Langer int repeat = 1; 400a3031715SAlexander Langer 401a3031715SAlexander Langer if (strchr("23456789", 402a3031715SAlexander Langer item[strlen(item) - 1])) { 403a3031715SAlexander Langer repeat = item[strlen(item) - 1] - '0'; 404a3031715SAlexander Langer item[strlen(item) - 1] = 0; 405a3031715SAlexander Langer } 406a3031715SAlexander Langer for (; repeat; repeat--) 407a3031715SAlexander Langer if (addsubunit(doingtop ^ flip ? theunit->numerator : theunit->denominator, item)) 408a3031715SAlexander Langer return 1; 409a3031715SAlexander Langer } 410a3031715SAlexander Langer item = strtok(NULL, " *\t/\n"); 411a3031715SAlexander Langer } 412a3031715SAlexander Langer doingtop--; 413a3031715SAlexander Langer if (slash) { 414a3031715SAlexander Langer scratch = slash + 1; 415a3031715SAlexander Langer } 416a3031715SAlexander Langer else 417a3031715SAlexander Langer doingtop--; 418a3031715SAlexander Langer } while (doingtop >= 0); 419a3031715SAlexander Langer free(savescr); 420a3031715SAlexander Langer return 0; 421a3031715SAlexander Langer } 422a3031715SAlexander Langer 423a3031715SAlexander Langer 424a3031715SAlexander Langer int 425a3031715SAlexander Langer compare(const void *item1, const void *item2) 426a3031715SAlexander Langer { 42750bb7724SDavid Malone return strcmp(*(const char * const *)item1, *(const char * const *)item2); 428a3031715SAlexander Langer } 429a3031715SAlexander Langer 430a3031715SAlexander Langer 431a3031715SAlexander Langer void 432a3031715SAlexander Langer sortunit(struct unittype * theunit) 433a3031715SAlexander Langer { 434a3031715SAlexander Langer char **ptr; 43517ad860fSDavid Malone unsigned int count; 436a3031715SAlexander Langer 437a3031715SAlexander Langer for (count = 0, ptr = theunit->numerator; *ptr; ptr++, count++); 438a3031715SAlexander Langer qsort(theunit->numerator, count, sizeof(char *), compare); 439a3031715SAlexander Langer for (count = 0, ptr = theunit->denominator; *ptr; ptr++, count++); 440a3031715SAlexander Langer qsort(theunit->denominator, count, sizeof(char *), compare); 441a3031715SAlexander Langer } 442a3031715SAlexander Langer 443a3031715SAlexander Langer 444a3031715SAlexander Langer void 445a3031715SAlexander Langer cancelunit(struct unittype * theunit) 446a3031715SAlexander Langer { 447a3031715SAlexander Langer char **den, **num; 448a3031715SAlexander Langer int comp; 449a3031715SAlexander Langer 450a3031715SAlexander Langer den = theunit->denominator; 451a3031715SAlexander Langer num = theunit->numerator; 452a3031715SAlexander Langer 453a3031715SAlexander Langer while (*num && *den) { 454a3031715SAlexander Langer comp = strcmp(*den, *num); 455a3031715SAlexander Langer if (!comp) { 456a3031715SAlexander Langer /* if (*den!=NULLUNIT) free(*den); 457a3031715SAlexander Langer if (*num!=NULLUNIT) free(*num);*/ 458a3031715SAlexander Langer *den++ = NULLUNIT; 459a3031715SAlexander Langer *num++ = NULLUNIT; 460a3031715SAlexander Langer } 461a3031715SAlexander Langer else if (comp < 0) 462a3031715SAlexander Langer den++; 463a3031715SAlexander Langer else 464a3031715SAlexander Langer num++; 465a3031715SAlexander Langer } 466a3031715SAlexander Langer } 467a3031715SAlexander Langer 468a3031715SAlexander Langer 469a3031715SAlexander Langer 470a3031715SAlexander Langer 471a3031715SAlexander Langer /* 472a3031715SAlexander Langer Looks up the definition for the specified unit. 473a3031715SAlexander Langer Returns a pointer to the definition or a null pointer 474a3031715SAlexander Langer if the specified unit does not appear in the units table. 475a3031715SAlexander Langer */ 476a3031715SAlexander Langer 477a3031715SAlexander Langer static char buffer[100]; /* buffer for lookupunit answers with 478a3031715SAlexander Langer prefixes */ 479a3031715SAlexander Langer 480a3031715SAlexander Langer char * 48117ad860fSDavid Malone lookupunit(const char *unit) 482a3031715SAlexander Langer { 483a3031715SAlexander Langer int i; 484a3031715SAlexander Langer char *copy; 485a3031715SAlexander Langer 486a3031715SAlexander Langer for (i = 0; i < unitcount; i++) { 487a3031715SAlexander Langer if (!strcmp(unittable[i].uname, unit)) 488a3031715SAlexander Langer return unittable[i].uval; 489a3031715SAlexander Langer } 490a3031715SAlexander Langer 491a3031715SAlexander Langer if (unit[strlen(unit) - 1] == '^') { 492a3031715SAlexander Langer copy = dupstr(unit); 493a3031715SAlexander Langer copy[strlen(copy) - 1] = 0; 494a3031715SAlexander Langer for (i = 0; i < unitcount; i++) { 495a3031715SAlexander Langer if (!strcmp(unittable[i].uname, copy)) { 496ffd044a6SKris Kennaway strlcpy(buffer, copy, sizeof(buffer)); 497a3031715SAlexander Langer free(copy); 498a3031715SAlexander Langer return buffer; 499a3031715SAlexander Langer } 500a3031715SAlexander Langer } 501a3031715SAlexander Langer free(copy); 502a3031715SAlexander Langer } 503a3031715SAlexander Langer if (unit[strlen(unit) - 1] == 's') { 504a3031715SAlexander Langer copy = dupstr(unit); 505a3031715SAlexander Langer copy[strlen(copy) - 1] = 0; 506a3031715SAlexander Langer for (i = 0; i < unitcount; i++) { 507a3031715SAlexander Langer if (!strcmp(unittable[i].uname, copy)) { 508ffd044a6SKris Kennaway strlcpy(buffer, copy, sizeof(buffer)); 509a3031715SAlexander Langer free(copy); 510a3031715SAlexander Langer return buffer; 511a3031715SAlexander Langer } 512a3031715SAlexander Langer } 513a3031715SAlexander Langer if (copy[strlen(copy) - 1] == 'e') { 514a3031715SAlexander Langer copy[strlen(copy) - 1] = 0; 515a3031715SAlexander Langer for (i = 0; i < unitcount; i++) { 516a3031715SAlexander Langer if (!strcmp(unittable[i].uname, copy)) { 517ffd044a6SKris Kennaway strlcpy(buffer, copy, sizeof(buffer)); 518a3031715SAlexander Langer free(copy); 519a3031715SAlexander Langer return buffer; 520a3031715SAlexander Langer } 521a3031715SAlexander Langer } 522a3031715SAlexander Langer } 523a3031715SAlexander Langer free(copy); 524a3031715SAlexander Langer } 525a3031715SAlexander Langer for (i = 0; i < prefixcount; i++) { 52617ad860fSDavid Malone size_t len = strlen(prefixtable[i].prefixname); 52717ad860fSDavid Malone if (!strncmp(prefixtable[i].prefixname, unit, len)) { 52817ad860fSDavid Malone if (!strlen(unit + len) || lookupunit(unit + len)) { 529ffd044a6SKris Kennaway snprintf(buffer, sizeof(buffer), "%s %s", 53017ad860fSDavid Malone prefixtable[i].prefixval, unit + len); 531a3031715SAlexander Langer return buffer; 532a3031715SAlexander Langer } 533a3031715SAlexander Langer } 534a3031715SAlexander Langer } 535a3031715SAlexander Langer return 0; 536a3031715SAlexander Langer } 537a3031715SAlexander Langer 538a3031715SAlexander Langer 539a3031715SAlexander Langer 540a3031715SAlexander Langer /* 541a3031715SAlexander Langer reduces a product of symbolic units to primitive units. 542a3031715SAlexander Langer The three low bits are used to return flags: 543a3031715SAlexander Langer 544a3031715SAlexander Langer bit 0 (1) set on if reductions were performed without error. 545a3031715SAlexander Langer bit 1 (2) set on if no reductions are performed. 546a3031715SAlexander Langer bit 2 (4) set on if an unknown unit is discovered. 547a3031715SAlexander Langer */ 548a3031715SAlexander Langer 549a3031715SAlexander Langer 550a3031715SAlexander Langer #define ERROR 4 551a3031715SAlexander Langer 552a3031715SAlexander Langer int 553a3031715SAlexander Langer reduceproduct(struct unittype * theunit, int flip) 554a3031715SAlexander Langer { 555a3031715SAlexander Langer 556a3031715SAlexander Langer char *toadd; 557a3031715SAlexander Langer char **product; 558a3031715SAlexander Langer int didsomething = 2; 559a3031715SAlexander Langer 560a3031715SAlexander Langer if (flip) 561a3031715SAlexander Langer product = theunit->denominator; 562a3031715SAlexander Langer else 563a3031715SAlexander Langer product = theunit->numerator; 564a3031715SAlexander Langer 565a3031715SAlexander Langer for (; *product; product++) { 566a3031715SAlexander Langer 567a3031715SAlexander Langer for (;;) { 568a3031715SAlexander Langer if (!strlen(*product)) 569a3031715SAlexander Langer break; 570a3031715SAlexander Langer toadd = lookupunit(*product); 571a3031715SAlexander Langer if (!toadd) { 572a3031715SAlexander Langer printf("unknown unit '%s'\n", *product); 573a3031715SAlexander Langer return ERROR; 574a3031715SAlexander Langer } 575a3031715SAlexander Langer if (strchr(toadd, PRIMITIVECHAR)) 576a3031715SAlexander Langer break; 577a3031715SAlexander Langer didsomething = 1; 578a3031715SAlexander Langer if (*product != NULLUNIT) { 579a3031715SAlexander Langer free(*product); 580a3031715SAlexander Langer *product = NULLUNIT; 581a3031715SAlexander Langer } 5829c95bc1cSDavid Malone if (addunit(theunit, toadd, flip, 0)) 583a3031715SAlexander Langer return ERROR; 584a3031715SAlexander Langer } 585a3031715SAlexander Langer } 586a3031715SAlexander Langer return didsomething; 587a3031715SAlexander Langer } 588a3031715SAlexander Langer 589a3031715SAlexander Langer 590a3031715SAlexander Langer /* 591a3031715SAlexander Langer Reduces numerator and denominator of the specified unit. 592a3031715SAlexander Langer Returns 0 on success, or 1 on unknown unit error. 593a3031715SAlexander Langer */ 594a3031715SAlexander Langer 595a3031715SAlexander Langer int 596a3031715SAlexander Langer reduceunit(struct unittype * theunit) 597a3031715SAlexander Langer { 598a3031715SAlexander Langer int ret; 599a3031715SAlexander Langer 600a3031715SAlexander Langer ret = 1; 601a3031715SAlexander Langer while (ret & 1) { 602a3031715SAlexander Langer ret = reduceproduct(theunit, 0) | reduceproduct(theunit, 1); 603a3031715SAlexander Langer if (ret & 4) 604a3031715SAlexander Langer return 1; 605a3031715SAlexander Langer } 606a3031715SAlexander Langer return 0; 607a3031715SAlexander Langer } 608a3031715SAlexander Langer 609a3031715SAlexander Langer 610a3031715SAlexander Langer int 611a3031715SAlexander Langer compareproducts(char **one, char **two) 612a3031715SAlexander Langer { 613a3031715SAlexander Langer while (*one || *two) { 614a3031715SAlexander Langer if (!*one && *two != NULLUNIT) 615a3031715SAlexander Langer return 1; 616a3031715SAlexander Langer if (!*two && *one != NULLUNIT) 617a3031715SAlexander Langer return 1; 618a3031715SAlexander Langer if (*one == NULLUNIT) 619a3031715SAlexander Langer one++; 620a3031715SAlexander Langer else if (*two == NULLUNIT) 621a3031715SAlexander Langer two++; 622a3031715SAlexander Langer else if (strcmp(*one, *two)) 623a3031715SAlexander Langer return 1; 624a3031715SAlexander Langer else 625a3031715SAlexander Langer one++, two++; 626a3031715SAlexander Langer } 627a3031715SAlexander Langer return 0; 628a3031715SAlexander Langer } 629a3031715SAlexander Langer 630a3031715SAlexander Langer 631a3031715SAlexander Langer /* Return zero if units are compatible, nonzero otherwise */ 632a3031715SAlexander Langer 633a3031715SAlexander Langer int 634a3031715SAlexander Langer compareunits(struct unittype * first, struct unittype * second) 635a3031715SAlexander Langer { 636a3031715SAlexander Langer return 637a3031715SAlexander Langer compareproducts(first->numerator, second->numerator) || 638a3031715SAlexander Langer compareproducts(first->denominator, second->denominator); 639a3031715SAlexander Langer } 640a3031715SAlexander Langer 641a3031715SAlexander Langer 642a3031715SAlexander Langer int 643a3031715SAlexander Langer completereduce(struct unittype * unit) 644a3031715SAlexander Langer { 645a3031715SAlexander Langer if (reduceunit(unit)) 646a3031715SAlexander Langer return 1; 647a3031715SAlexander Langer sortunit(unit); 648a3031715SAlexander Langer cancelunit(unit); 649a3031715SAlexander Langer return 0; 650a3031715SAlexander Langer } 651a3031715SAlexander Langer 652a3031715SAlexander Langer void 653a3031715SAlexander Langer showanswer(struct unittype * have, struct unittype * want) 654a3031715SAlexander Langer { 6559653775eSEitan Adler double ans; 6569653775eSEitan Adler 657a3031715SAlexander Langer if (compareunits(have, want)) { 658a3031715SAlexander Langer printf("conformability error\n"); 6599653775eSEitan Adler if (verbose) 6609653775eSEitan Adler printf("\t%s = ", havestr); 6618d61f393SEitan Adler else if (!terse) 6629653775eSEitan Adler printf("\t"); 663a3031715SAlexander Langer showunit(have); 6648d61f393SEitan Adler if (!terse) { 6659653775eSEitan Adler if (verbose) 6669653775eSEitan Adler printf("\t%s = ", wantstr); 6679653775eSEitan Adler else 6689653775eSEitan Adler printf("\t"); 669a3031715SAlexander Langer showunit(want); 670a3031715SAlexander Langer } 6718d61f393SEitan Adler } 6729c95bc1cSDavid Malone else if (have->offset != want->offset) { 6739c95bc1cSDavid Malone if (want->quantity) 6749c95bc1cSDavid Malone printf("WARNING: conversion of non-proportional quantities.\n"); 6759c95bc1cSDavid Malone if (have->quantity) 6769653775eSEitan Adler printf("\t%.8g\n", 6779c95bc1cSDavid Malone (have->factor + have->offset-want->offset)/want->factor); 6789653775eSEitan Adler else { 6799653775eSEitan Adler printf("\t (-> x*%.8g %+.8g)\n\t (<- y*%.8g %+.8g)\n", 6809c95bc1cSDavid Malone have->factor / want->factor, 6819c95bc1cSDavid Malone (have->offset-want->offset)/want->factor, 6829c95bc1cSDavid Malone want->factor / have->factor, 6839c95bc1cSDavid Malone (want->offset - have->offset)/have->factor); 6849c95bc1cSDavid Malone } 6859653775eSEitan Adler } 6869653775eSEitan Adler else { 6879653775eSEitan Adler ans = have->factor / want->factor; 6889653775eSEitan Adler if (verbose) 6899653775eSEitan Adler printf("\t%s = %.8g * %s\n", havestr, ans, wantstr); 6908d61f393SEitan Adler else if (terse) 6918d61f393SEitan Adler printf("%.8g\n", ans); 692a3031715SAlexander Langer else 6939653775eSEitan Adler printf("\t* %.8g\n", ans); 6949653775eSEitan Adler 6959653775eSEitan Adler if (verbose) 6969653775eSEitan Adler printf("\t%s = (1 / %.8g) * %s\n", havestr, 1/ans, wantstr); 6978d61f393SEitan Adler else if (!terse) 6989653775eSEitan Adler printf("\t/ %.8g\n", 1/ans); 6999653775eSEitan Adler } 700a3031715SAlexander Langer } 701a3031715SAlexander Langer 702a3031715SAlexander Langer 703a913717bSEitan Adler static void 70417ad860fSDavid Malone usage(void) 705a3031715SAlexander Langer { 706d4d0763aSPhilippe Charnier fprintf(stderr, 707114022f5SEitan Adler "usage: units [-f unitsfile] [-UVq] [from-unit to-unit]\n"); 708a3031715SAlexander Langer exit(3); 709a3031715SAlexander Langer } 710a3031715SAlexander Langer 7115192ff45SEitan Adler static struct option longopts[] = { 712a913717bSEitan Adler {"help", no_argument, NULL, 'h'}, 7135192ff45SEitan Adler {"file", required_argument, NULL, 'f'}, 7145192ff45SEitan Adler {"quiet", no_argument, NULL, 'q'}, 7158d61f393SEitan Adler {"terse", no_argument, NULL, 't'}, 7165192ff45SEitan Adler {"unitsfile", no_argument, NULL, 'U'}, 7178d61f393SEitan Adler {"verbose", no_argument, NULL, 'v'}, 7185192ff45SEitan Adler {"version", no_argument, NULL, 'V'}, 7195192ff45SEitan Adler { 0, 0, 0, 0 } 7205192ff45SEitan Adler }; 7215192ff45SEitan Adler 722a3031715SAlexander Langer 723a3031715SAlexander Langer int 724a3031715SAlexander Langer main(int argc, char **argv) 725a3031715SAlexander Langer { 726a3031715SAlexander Langer 727a3031715SAlexander Langer struct unittype have, want; 728a3031715SAlexander Langer int optchar; 729647be2c3SEitan Adler bool quiet; 7303af08201SEitan Adler bool readfile; 731647be2c3SEitan Adler History *inhistory; 732647be2c3SEitan Adler EditLine *el; 733647be2c3SEitan Adler HistEvent ev; 734647be2c3SEitan Adler int inputsz; 735a3031715SAlexander Langer 736647be2c3SEitan Adler quiet = false; 7373af08201SEitan Adler readfile = false; 7388d61f393SEitan Adler while ((optchar = getopt_long(argc, argv, "+hf:qtvUV", longopts, NULL)) != -1) { 739a3031715SAlexander Langer switch (optchar) { 740a3031715SAlexander Langer case 'f': 7413af08201SEitan Adler readfile = true; 7423af08201SEitan Adler if (strlen(optarg) == 0) 7433af08201SEitan Adler readunits(NULL); 7443af08201SEitan Adler else 7453af08201SEitan Adler readunits(optarg); 746a3031715SAlexander Langer break; 747a3031715SAlexander Langer case 'q': 748647be2c3SEitan Adler quiet = true; 749a3031715SAlexander Langer break; 7508d61f393SEitan Adler case 't': 7518d61f393SEitan Adler terse = true; 7528d61f393SEitan Adler break; 7539653775eSEitan Adler case 'v': 7549653775eSEitan Adler verbose = true; 7559653775eSEitan Adler break; 756db9def8bSEitan Adler case 'V': 757db9def8bSEitan Adler fprintf(stderr, "FreeBSD units\n"); 758db9def8bSEitan Adler /* FALLTHROUGH */ 759114022f5SEitan Adler case 'U': 760114022f5SEitan Adler if (access(UNITSFILE, F_OK) == 0) 761114022f5SEitan Adler printf("%s\n", UNITSFILE); 762114022f5SEitan Adler else 763114022f5SEitan Adler printf("Units data file not found"); 764114022f5SEitan Adler exit(0); 765114022f5SEitan Adler break; 766a913717bSEitan Adler case 'h': 767a913717bSEitan Adler /* FALLTHROUGH */ 768a913717bSEitan Adler 7696d12a834SEitan Adler default: 7706d12a834SEitan Adler usage(); 771a3031715SAlexander Langer } 772a3031715SAlexander Langer } 773a3031715SAlexander Langer 7743af08201SEitan Adler if (!readfile) 7753af08201SEitan Adler readunits(NULL); 776a3031715SAlexander Langer 777647be2c3SEitan Adler inhistory = history_init(); 778647be2c3SEitan Adler el = el_init(argv[0], stdin, stdout, stderr); 779647be2c3SEitan Adler el_set(el, EL_PROMPT, &prompt); 780647be2c3SEitan Adler el_set(el, EL_EDITOR, "emacs"); 781647be2c3SEitan Adler el_set(el, EL_SIGNAL, 1); 782647be2c3SEitan Adler el_set(el, EL_HIST, history, inhistory); 783cada5528SEitan Adler el_source(el, NULL); 784647be2c3SEitan Adler history(inhistory, &ev, H_SETSIZE, 800); 785647be2c3SEitan Adler if (inhistory == 0) 78687ddbe82SEitan Adler err(1, "Could not initialize history"); 787647be2c3SEitan Adler 7883af08201SEitan Adler if (cap_enter() < 0 && errno != ENOSYS) 7893af08201SEitan Adler err(1, "unable to enter capability mode"); 790a3031715SAlexander Langer 791a3031715SAlexander Langer if (optind == argc - 2) { 792647be2c3SEitan Adler havestr = argv[optind]; 793647be2c3SEitan Adler wantstr = argv[optind + 1]; 794a3031715SAlexander Langer initializeunit(&have); 7959c95bc1cSDavid Malone addunit(&have, havestr, 0, 1); 796a3031715SAlexander Langer completereduce(&have); 797a3031715SAlexander Langer initializeunit(&want); 7989c95bc1cSDavid Malone addunit(&want, wantstr, 0, 1); 799a3031715SAlexander Langer completereduce(&want); 800a3031715SAlexander Langer showanswer(&have, &want); 801a3031715SAlexander Langer } 802a3031715SAlexander Langer else { 803a3031715SAlexander Langer if (!quiet) 804d4d0763aSPhilippe Charnier printf("%d units, %d prefixes\n", unitcount, 805a3031715SAlexander Langer prefixcount); 806a3031715SAlexander Langer for (;;) { 807a3031715SAlexander Langer do { 808a3031715SAlexander Langer initializeunit(&have); 809a3031715SAlexander Langer if (!quiet) 810647be2c3SEitan Adler promptstr = "You have: "; 811647be2c3SEitan Adler havestr = el_gets(el, &inputsz); 812647be2c3SEitan Adler if (havestr == NULL) 813a3031715SAlexander Langer exit(0); 814647be2c3SEitan Adler if (inputsz > 0) 815647be2c3SEitan Adler history(inhistory, &ev, H_ENTER, 816647be2c3SEitan Adler havestr); 8179c95bc1cSDavid Malone } while (addunit(&have, havestr, 0, 1) || 818a3031715SAlexander Langer completereduce(&have)); 819a3031715SAlexander Langer do { 820a3031715SAlexander Langer initializeunit(&want); 821a3031715SAlexander Langer if (!quiet) 822647be2c3SEitan Adler promptstr = "You want: "; 823647be2c3SEitan Adler wantstr = el_gets(el, &inputsz); 824647be2c3SEitan Adler if (wantstr == NULL) 825a3031715SAlexander Langer exit(0); 826647be2c3SEitan Adler if (inputsz > 0) 827647be2c3SEitan Adler history(inhistory, &ev, H_ENTER, 828647be2c3SEitan Adler wantstr); 8299c95bc1cSDavid Malone } while (addunit(&want, wantstr, 0, 1) || 830a3031715SAlexander Langer completereduce(&want)); 831a3031715SAlexander Langer showanswer(&have, &want); 832a3031715SAlexander Langer } 833a3031715SAlexander Langer } 8344c0c227dSAlexander Langer 835647be2c3SEitan Adler history_end(inhistory); 836373ca6f9SEitan Adler el_end(el); 8374c0c227dSAlexander Langer return (0); 838a3031715SAlexander Langer } 839