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[] = 20d4d0763aSPhilippe Charnier "$Id$"; 21d4d0763aSPhilippe Charnier #endif /* not lint */ 22d4d0763aSPhilippe Charnier 23a3031715SAlexander Langer #include <ctype.h> 24d4d0763aSPhilippe Charnier #include <err.h> 25a3031715SAlexander Langer #include <stdio.h> 26a3031715SAlexander Langer #include <stdlib.h> 27d4d0763aSPhilippe Charnier #include <string.h> 28d4d0763aSPhilippe Charnier #include <unistd.h> 29a3031715SAlexander Langer 30a3031715SAlexander Langer #include "pathnames.h" 31a3031715SAlexander Langer 32a3031715SAlexander Langer #define VERSION "1.0" 33a3031715SAlexander Langer 34a3031715SAlexander Langer #ifndef UNITSFILE 35a3031715SAlexander Langer #define UNITSFILE _PATH_UNITSLIB 36a3031715SAlexander Langer #endif 37a3031715SAlexander Langer 38a3031715SAlexander Langer #define MAXUNITS 1000 39a3031715SAlexander Langer #define MAXPREFIXES 50 40a3031715SAlexander Langer 41a3031715SAlexander Langer #define MAXSUBUNITS 500 42a3031715SAlexander Langer 43a3031715SAlexander Langer #define PRIMITIVECHAR '!' 44a3031715SAlexander Langer 45a3031715SAlexander Langer char *powerstring = "^"; 46a3031715SAlexander Langer 47a3031715SAlexander Langer struct { 48a3031715SAlexander Langer char *uname; 49a3031715SAlexander Langer char *uval; 50a3031715SAlexander Langer } unittable[MAXUNITS]; 51a3031715SAlexander Langer 52a3031715SAlexander Langer struct unittype { 53a3031715SAlexander Langer char *numerator[MAXSUBUNITS]; 54a3031715SAlexander Langer char *denominator[MAXSUBUNITS]; 55a3031715SAlexander Langer double factor; 56a3031715SAlexander Langer }; 57a3031715SAlexander Langer 58a3031715SAlexander Langer struct { 59a3031715SAlexander Langer char *prefixname; 60a3031715SAlexander Langer char *prefixval; 61a3031715SAlexander Langer } prefixtable[MAXPREFIXES]; 62a3031715SAlexander Langer 63a3031715SAlexander Langer 64a3031715SAlexander Langer char *NULLUNIT = ""; 65a3031715SAlexander Langer 66a3031715SAlexander Langer int unitcount; 67a3031715SAlexander Langer int prefixcount; 68a3031715SAlexander Langer 69a3031715SAlexander Langer 70a3031715SAlexander Langer char * 71a3031715SAlexander Langer dupstr(char *str) 72a3031715SAlexander Langer { 73a3031715SAlexander Langer char *ret; 74a3031715SAlexander Langer 75a3031715SAlexander Langer ret = malloc(strlen(str) + 1); 76d4d0763aSPhilippe Charnier if (!ret) 77d4d0763aSPhilippe Charnier errx(3, "memory allocation error"); 78a3031715SAlexander Langer strcpy(ret, str); 79a3031715SAlexander Langer return (ret); 80a3031715SAlexander Langer } 81a3031715SAlexander Langer 82a3031715SAlexander Langer 83a3031715SAlexander Langer void 84a3031715SAlexander Langer readerror(int linenum) 85a3031715SAlexander Langer { 86d4d0763aSPhilippe Charnier warnx("error in units file '%s' line %d", UNITSFILE, linenum); 87a3031715SAlexander Langer } 88a3031715SAlexander Langer 89a3031715SAlexander Langer 90a3031715SAlexander Langer void 91a3031715SAlexander Langer readunits(char *userfile) 92a3031715SAlexander Langer { 93a3031715SAlexander Langer FILE *unitfile; 94a3031715SAlexander Langer char line[80], *lineptr; 95a3031715SAlexander Langer int len, linenum, i; 96a3031715SAlexander Langer 97a3031715SAlexander Langer unitcount = 0; 98a3031715SAlexander Langer linenum = 0; 99a3031715SAlexander Langer 100a3031715SAlexander Langer if (userfile) { 101a3031715SAlexander Langer unitfile = fopen(userfile, "rt"); 102d4d0763aSPhilippe Charnier if (!unitfile) 103d4d0763aSPhilippe Charnier errx(1, "unable to open units file '%s'", userfile); 104a3031715SAlexander Langer } 105a3031715SAlexander Langer else { 106a3031715SAlexander Langer unitfile = fopen(UNITSFILE, "rt"); 107a3031715SAlexander Langer if (!unitfile) { 108a3031715SAlexander Langer char *direc, *env; 109a3031715SAlexander Langer char filename[1000]; 110a3031715SAlexander Langer char separator[2]; 111a3031715SAlexander Langer 112a3031715SAlexander Langer env = getenv("PATH"); 113a3031715SAlexander Langer if (env) { 114a3031715SAlexander Langer if (strchr(env, ';')) 115a3031715SAlexander Langer strcpy(separator, ";"); 116a3031715SAlexander Langer else 117a3031715SAlexander Langer strcpy(separator, ":"); 118a3031715SAlexander Langer direc = strtok(env, separator); 119a3031715SAlexander Langer while (direc) { 120a3031715SAlexander Langer strcpy(filename, ""); 121a3031715SAlexander Langer strncat(filename, direc, 999); 122a3031715SAlexander Langer strncat(filename, "/", 123a3031715SAlexander Langer 999 - strlen(filename)); 124a3031715SAlexander Langer strncat(filename, UNITSFILE, 125a3031715SAlexander Langer 999 - strlen(filename)); 126a3031715SAlexander Langer unitfile = fopen(filename, "rt"); 127a3031715SAlexander Langer if (unitfile) 128a3031715SAlexander Langer break; 129a3031715SAlexander Langer direc = strtok(NULL, separator); 130a3031715SAlexander Langer } 131a3031715SAlexander Langer } 132d4d0763aSPhilippe Charnier if (!unitfile) 133d4d0763aSPhilippe Charnier errx(1, "can't find units file '%s'", UNITSFILE); 134a3031715SAlexander Langer } 135a3031715SAlexander Langer } 136a3031715SAlexander Langer while (!feof(unitfile)) { 137a3031715SAlexander Langer if (!fgets(line, 79, unitfile)) 138a3031715SAlexander Langer break; 139a3031715SAlexander Langer linenum++; 140a3031715SAlexander Langer lineptr = line; 141a3031715SAlexander Langer if (*lineptr == '/') 142a3031715SAlexander Langer continue; 143a3031715SAlexander Langer lineptr += strspn(lineptr, " \n\t"); 144a3031715SAlexander Langer len = strcspn(lineptr, " \n\t"); 145a3031715SAlexander Langer lineptr[len] = 0; 146a3031715SAlexander Langer if (!strlen(lineptr)) 147a3031715SAlexander Langer continue; 148a3031715SAlexander Langer if (lineptr[strlen(lineptr) - 1] == '-') { /* it's a prefix */ 149a3031715SAlexander Langer if (prefixcount == MAXPREFIXES) { 150d4d0763aSPhilippe Charnier warnx("memory for prefixes exceeded in line %d", linenum); 151a3031715SAlexander Langer continue; 152a3031715SAlexander Langer } 153a3031715SAlexander Langer lineptr[strlen(lineptr) - 1] = 0; 154a3031715SAlexander Langer prefixtable[prefixcount].prefixname = dupstr(lineptr); 155a3031715SAlexander Langer for (i = 0; i < prefixcount; i++) 156a3031715SAlexander Langer if (!strcmp(prefixtable[i].prefixname, lineptr)) { 157d4d0763aSPhilippe Charnier warnx("redefinition of prefix '%s' on line %d ignored", 158a3031715SAlexander Langer lineptr, linenum); 159a3031715SAlexander Langer continue; 160a3031715SAlexander Langer } 161a3031715SAlexander Langer lineptr += len + 1; 162a3031715SAlexander Langer if (!strlen(lineptr)) { 163a3031715SAlexander Langer readerror(linenum); 164a3031715SAlexander Langer continue; 165a3031715SAlexander Langer } 166a3031715SAlexander Langer lineptr += strspn(lineptr, " \n\t"); 167a3031715SAlexander Langer len = strcspn(lineptr, "\n\t"); 168a3031715SAlexander Langer lineptr[len] = 0; 169a3031715SAlexander Langer prefixtable[prefixcount++].prefixval = dupstr(lineptr); 170a3031715SAlexander Langer } 171a3031715SAlexander Langer else { /* it's not a prefix */ 172a3031715SAlexander Langer if (unitcount == MAXUNITS) { 173d4d0763aSPhilippe Charnier warnx("memory for units exceeded in line %d", linenum); 174a3031715SAlexander Langer continue; 175a3031715SAlexander Langer } 176a3031715SAlexander Langer unittable[unitcount].uname = dupstr(lineptr); 177a3031715SAlexander Langer for (i = 0; i < unitcount; i++) 178a3031715SAlexander Langer if (!strcmp(unittable[i].uname, lineptr)) { 179d4d0763aSPhilippe Charnier warnx("redefinition of unit '%s' on line %d ignored", 180a3031715SAlexander Langer lineptr, linenum); 181a3031715SAlexander Langer continue; 182a3031715SAlexander Langer } 183a3031715SAlexander Langer lineptr += len + 1; 184a3031715SAlexander Langer lineptr += strspn(lineptr, " \n\t"); 185a3031715SAlexander Langer if (!strlen(lineptr)) { 186a3031715SAlexander Langer readerror(linenum); 187a3031715SAlexander Langer continue; 188a3031715SAlexander Langer } 189a3031715SAlexander Langer len = strcspn(lineptr, "\n\t"); 190a3031715SAlexander Langer lineptr[len] = 0; 191a3031715SAlexander Langer unittable[unitcount++].uval = dupstr(lineptr); 192a3031715SAlexander Langer } 193a3031715SAlexander Langer } 194a3031715SAlexander Langer fclose(unitfile); 195a3031715SAlexander Langer } 196a3031715SAlexander Langer 197a3031715SAlexander Langer void 198a3031715SAlexander Langer initializeunit(struct unittype * theunit) 199a3031715SAlexander Langer { 200a3031715SAlexander Langer theunit->factor = 1.0; 201a3031715SAlexander Langer theunit->numerator[0] = theunit->denominator[0] = NULL; 202a3031715SAlexander Langer } 203a3031715SAlexander Langer 204a3031715SAlexander Langer 205a3031715SAlexander Langer int 206a3031715SAlexander Langer addsubunit(char *product[], char *toadd) 207a3031715SAlexander Langer { 208a3031715SAlexander Langer char **ptr; 209a3031715SAlexander Langer 210a3031715SAlexander Langer for (ptr = product; *ptr && *ptr != NULLUNIT; ptr++); 211a3031715SAlexander Langer if (ptr >= product + MAXSUBUNITS) { 212d4d0763aSPhilippe Charnier warnx("memory overflow in unit reduction"); 213a3031715SAlexander Langer return 1; 214a3031715SAlexander Langer } 215a3031715SAlexander Langer if (!*ptr) 216a3031715SAlexander Langer *(ptr + 1) = 0; 217a3031715SAlexander Langer *ptr = dupstr(toadd); 218a3031715SAlexander Langer return 0; 219a3031715SAlexander Langer } 220a3031715SAlexander Langer 221a3031715SAlexander Langer 222a3031715SAlexander Langer void 223a3031715SAlexander Langer showunit(struct unittype * theunit) 224a3031715SAlexander Langer { 225a3031715SAlexander Langer char **ptr; 226a3031715SAlexander Langer int printedslash; 227a3031715SAlexander Langer int counter = 1; 228a3031715SAlexander Langer 229a3031715SAlexander Langer printf("\t%.8g", theunit->factor); 230a3031715SAlexander Langer for (ptr = theunit->numerator; *ptr; ptr++) { 231a3031715SAlexander Langer if (ptr > theunit->numerator && **ptr && 232a3031715SAlexander Langer !strcmp(*ptr, *(ptr - 1))) 233a3031715SAlexander Langer counter++; 234a3031715SAlexander Langer else { 235a3031715SAlexander Langer if (counter > 1) 236a3031715SAlexander Langer printf("%s%d", powerstring, counter); 237a3031715SAlexander Langer if (**ptr) 238a3031715SAlexander Langer printf(" %s", *ptr); 239a3031715SAlexander Langer counter = 1; 240a3031715SAlexander Langer } 241a3031715SAlexander Langer } 242a3031715SAlexander Langer if (counter > 1) 243a3031715SAlexander Langer printf("%s%d", powerstring, counter); 244a3031715SAlexander Langer counter = 1; 245a3031715SAlexander Langer printedslash = 0; 246a3031715SAlexander Langer for (ptr = theunit->denominator; *ptr; ptr++) { 247a3031715SAlexander Langer if (ptr > theunit->denominator && **ptr && 248a3031715SAlexander Langer !strcmp(*ptr, *(ptr - 1))) 249a3031715SAlexander Langer counter++; 250a3031715SAlexander Langer else { 251a3031715SAlexander Langer if (counter > 1) 252a3031715SAlexander Langer printf("%s%d", powerstring, counter); 253a3031715SAlexander Langer if (**ptr) { 254a3031715SAlexander Langer if (!printedslash) 255a3031715SAlexander Langer printf(" /"); 256a3031715SAlexander Langer printedslash = 1; 257a3031715SAlexander Langer printf(" %s", *ptr); 258a3031715SAlexander Langer } 259a3031715SAlexander Langer counter = 1; 260a3031715SAlexander Langer } 261a3031715SAlexander Langer } 262a3031715SAlexander Langer if (counter > 1) 263a3031715SAlexander Langer printf("%s%d", powerstring, counter); 264a3031715SAlexander Langer printf("\n"); 265a3031715SAlexander Langer } 266a3031715SAlexander Langer 267a3031715SAlexander Langer 268a3031715SAlexander Langer void 269a3031715SAlexander Langer zeroerror() 270a3031715SAlexander Langer { 271d4d0763aSPhilippe Charnier warnx("unit reduces to zero"); 272a3031715SAlexander Langer } 273a3031715SAlexander Langer 274a3031715SAlexander Langer /* 275a3031715SAlexander Langer Adds the specified string to the unit. 276a3031715SAlexander Langer Flip is 0 for adding normally, 1 for adding reciprocal. 277a3031715SAlexander Langer 278a3031715SAlexander Langer Returns 0 for successful addition, nonzero on error. 279a3031715SAlexander Langer */ 280a3031715SAlexander Langer 281a3031715SAlexander Langer int 282a3031715SAlexander Langer addunit(struct unittype * theunit, char *toadd, int flip) 283a3031715SAlexander Langer { 284a3031715SAlexander Langer char *scratch, *savescr; 285a3031715SAlexander Langer char *item; 286a3031715SAlexander Langer char *divider, *slash; 287a3031715SAlexander Langer int doingtop; 288a3031715SAlexander Langer 289a3031715SAlexander Langer savescr = scratch = dupstr(toadd); 290a3031715SAlexander Langer for (slash = scratch + 1; *slash; slash++) 291a3031715SAlexander Langer if (*slash == '-' && 292a3031715SAlexander Langer (tolower(*(slash - 1)) != 'e' || 293a3031715SAlexander Langer !strchr(".0123456789", *(slash + 1)))) 294a3031715SAlexander Langer *slash = ' '; 295a3031715SAlexander Langer slash = strchr(scratch, '/'); 296a3031715SAlexander Langer if (slash) 297a3031715SAlexander Langer *slash = 0; 298a3031715SAlexander Langer doingtop = 1; 299a3031715SAlexander Langer do { 300a3031715SAlexander Langer item = strtok(scratch, " *\t\n/"); 301a3031715SAlexander Langer while (item) { 302a3031715SAlexander Langer if (strchr("0123456789.", *item)) { /* item is a number */ 303a3031715SAlexander Langer double num; 304a3031715SAlexander Langer 305a3031715SAlexander Langer divider = strchr(item, '|'); 306a3031715SAlexander Langer if (divider) { 307a3031715SAlexander Langer *divider = 0; 308a3031715SAlexander Langer num = atof(item); 309a3031715SAlexander Langer if (!num) { 310a3031715SAlexander Langer zeroerror(); 311a3031715SAlexander Langer return 1; 312a3031715SAlexander Langer } 313a3031715SAlexander Langer if (doingtop ^ flip) 314a3031715SAlexander Langer theunit->factor *= num; 315a3031715SAlexander Langer else 316a3031715SAlexander Langer theunit->factor /= num; 317a3031715SAlexander Langer num = atof(divider + 1); 318a3031715SAlexander Langer if (!num) { 319a3031715SAlexander Langer zeroerror(); 320a3031715SAlexander Langer return 1; 321a3031715SAlexander Langer } 322a3031715SAlexander Langer if (doingtop ^ flip) 323a3031715SAlexander Langer theunit->factor /= num; 324a3031715SAlexander Langer else 325a3031715SAlexander Langer theunit->factor *= num; 326a3031715SAlexander Langer } 327a3031715SAlexander Langer else { 328a3031715SAlexander Langer num = atof(item); 329a3031715SAlexander Langer if (!num) { 330a3031715SAlexander Langer zeroerror(); 331a3031715SAlexander Langer return 1; 332a3031715SAlexander Langer } 333a3031715SAlexander Langer if (doingtop ^ flip) 334a3031715SAlexander Langer theunit->factor *= num; 335a3031715SAlexander Langer else 336a3031715SAlexander Langer theunit->factor /= num; 337a3031715SAlexander Langer 338a3031715SAlexander Langer } 339a3031715SAlexander Langer } 340a3031715SAlexander Langer else { /* item is not a number */ 341a3031715SAlexander Langer int repeat = 1; 342a3031715SAlexander Langer 343a3031715SAlexander Langer if (strchr("23456789", 344a3031715SAlexander Langer item[strlen(item) - 1])) { 345a3031715SAlexander Langer repeat = item[strlen(item) - 1] - '0'; 346a3031715SAlexander Langer item[strlen(item) - 1] = 0; 347a3031715SAlexander Langer } 348a3031715SAlexander Langer for (; repeat; repeat--) 349a3031715SAlexander Langer if (addsubunit(doingtop ^ flip ? theunit->numerator : theunit->denominator, item)) 350a3031715SAlexander Langer return 1; 351a3031715SAlexander Langer } 352a3031715SAlexander Langer item = strtok(NULL, " *\t/\n"); 353a3031715SAlexander Langer } 354a3031715SAlexander Langer doingtop--; 355a3031715SAlexander Langer if (slash) { 356a3031715SAlexander Langer scratch = slash + 1; 357a3031715SAlexander Langer } 358a3031715SAlexander Langer else 359a3031715SAlexander Langer doingtop--; 360a3031715SAlexander Langer } while (doingtop >= 0); 361a3031715SAlexander Langer free(savescr); 362a3031715SAlexander Langer return 0; 363a3031715SAlexander Langer } 364a3031715SAlexander Langer 365a3031715SAlexander Langer 366a3031715SAlexander Langer int 367a3031715SAlexander Langer compare(const void *item1, const void *item2) 368a3031715SAlexander Langer { 369a3031715SAlexander Langer return strcmp(*(char **) item1, *(char **) item2); 370a3031715SAlexander Langer } 371a3031715SAlexander Langer 372a3031715SAlexander Langer 373a3031715SAlexander Langer void 374a3031715SAlexander Langer sortunit(struct unittype * theunit) 375a3031715SAlexander Langer { 376a3031715SAlexander Langer char **ptr; 377a3031715SAlexander Langer int count; 378a3031715SAlexander Langer 379a3031715SAlexander Langer for (count = 0, ptr = theunit->numerator; *ptr; ptr++, count++); 380a3031715SAlexander Langer qsort(theunit->numerator, count, sizeof(char *), compare); 381a3031715SAlexander Langer for (count = 0, ptr = theunit->denominator; *ptr; ptr++, count++); 382a3031715SAlexander Langer qsort(theunit->denominator, count, sizeof(char *), compare); 383a3031715SAlexander Langer } 384a3031715SAlexander Langer 385a3031715SAlexander Langer 386a3031715SAlexander Langer void 387a3031715SAlexander Langer cancelunit(struct unittype * theunit) 388a3031715SAlexander Langer { 389a3031715SAlexander Langer char **den, **num; 390a3031715SAlexander Langer int comp; 391a3031715SAlexander Langer 392a3031715SAlexander Langer den = theunit->denominator; 393a3031715SAlexander Langer num = theunit->numerator; 394a3031715SAlexander Langer 395a3031715SAlexander Langer while (*num && *den) { 396a3031715SAlexander Langer comp = strcmp(*den, *num); 397a3031715SAlexander Langer if (!comp) { 398a3031715SAlexander Langer /* if (*den!=NULLUNIT) free(*den); 399a3031715SAlexander Langer if (*num!=NULLUNIT) free(*num);*/ 400a3031715SAlexander Langer *den++ = NULLUNIT; 401a3031715SAlexander Langer *num++ = NULLUNIT; 402a3031715SAlexander Langer } 403a3031715SAlexander Langer else if (comp < 0) 404a3031715SAlexander Langer den++; 405a3031715SAlexander Langer else 406a3031715SAlexander Langer num++; 407a3031715SAlexander Langer } 408a3031715SAlexander Langer } 409a3031715SAlexander Langer 410a3031715SAlexander Langer 411a3031715SAlexander Langer 412a3031715SAlexander Langer 413a3031715SAlexander Langer /* 414a3031715SAlexander Langer Looks up the definition for the specified unit. 415a3031715SAlexander Langer Returns a pointer to the definition or a null pointer 416a3031715SAlexander Langer if the specified unit does not appear in the units table. 417a3031715SAlexander Langer */ 418a3031715SAlexander Langer 419a3031715SAlexander Langer static char buffer[100]; /* buffer for lookupunit answers with 420a3031715SAlexander Langer prefixes */ 421a3031715SAlexander Langer 422a3031715SAlexander Langer char * 423a3031715SAlexander Langer lookupunit(char *unit) 424a3031715SAlexander Langer { 425a3031715SAlexander Langer int i; 426a3031715SAlexander Langer char *copy; 427a3031715SAlexander Langer 428a3031715SAlexander Langer for (i = 0; i < unitcount; i++) { 429a3031715SAlexander Langer if (!strcmp(unittable[i].uname, unit)) 430a3031715SAlexander Langer return unittable[i].uval; 431a3031715SAlexander Langer } 432a3031715SAlexander Langer 433a3031715SAlexander Langer if (unit[strlen(unit) - 1] == '^') { 434a3031715SAlexander Langer copy = dupstr(unit); 435a3031715SAlexander Langer copy[strlen(copy) - 1] = 0; 436a3031715SAlexander Langer for (i = 0; i < unitcount; i++) { 437a3031715SAlexander Langer if (!strcmp(unittable[i].uname, copy)) { 438a3031715SAlexander Langer strcpy(buffer, copy); 439a3031715SAlexander Langer free(copy); 440a3031715SAlexander Langer return buffer; 441a3031715SAlexander Langer } 442a3031715SAlexander Langer } 443a3031715SAlexander Langer free(copy); 444a3031715SAlexander Langer } 445a3031715SAlexander Langer if (unit[strlen(unit) - 1] == 's') { 446a3031715SAlexander Langer copy = dupstr(unit); 447a3031715SAlexander Langer copy[strlen(copy) - 1] = 0; 448a3031715SAlexander Langer for (i = 0; i < unitcount; i++) { 449a3031715SAlexander Langer if (!strcmp(unittable[i].uname, copy)) { 450a3031715SAlexander Langer strcpy(buffer, copy); 451a3031715SAlexander Langer free(copy); 452a3031715SAlexander Langer return buffer; 453a3031715SAlexander Langer } 454a3031715SAlexander Langer } 455a3031715SAlexander Langer if (copy[strlen(copy) - 1] == 'e') { 456a3031715SAlexander Langer copy[strlen(copy) - 1] = 0; 457a3031715SAlexander Langer for (i = 0; i < unitcount; i++) { 458a3031715SAlexander Langer if (!strcmp(unittable[i].uname, copy)) { 459a3031715SAlexander Langer strcpy(buffer, copy); 460a3031715SAlexander Langer free(copy); 461a3031715SAlexander Langer return buffer; 462a3031715SAlexander Langer } 463a3031715SAlexander Langer } 464a3031715SAlexander Langer } 465a3031715SAlexander Langer free(copy); 466a3031715SAlexander Langer } 467a3031715SAlexander Langer for (i = 0; i < prefixcount; i++) { 468a3031715SAlexander Langer if (!strncmp(prefixtable[i].prefixname, unit, 469a3031715SAlexander Langer strlen(prefixtable[i].prefixname))) { 470a3031715SAlexander Langer unit += strlen(prefixtable[i].prefixname); 471a3031715SAlexander Langer if (!strlen(unit) || lookupunit(unit)) { 472a3031715SAlexander Langer strcpy(buffer, prefixtable[i].prefixval); 473a3031715SAlexander Langer strcat(buffer, " "); 474a3031715SAlexander Langer strcat(buffer, unit); 475a3031715SAlexander Langer return buffer; 476a3031715SAlexander Langer } 477a3031715SAlexander Langer } 478a3031715SAlexander Langer } 479a3031715SAlexander Langer return 0; 480a3031715SAlexander Langer } 481a3031715SAlexander Langer 482a3031715SAlexander Langer 483a3031715SAlexander Langer 484a3031715SAlexander Langer /* 485a3031715SAlexander Langer reduces a product of symbolic units to primitive units. 486a3031715SAlexander Langer The three low bits are used to return flags: 487a3031715SAlexander Langer 488a3031715SAlexander Langer bit 0 (1) set on if reductions were performed without error. 489a3031715SAlexander Langer bit 1 (2) set on if no reductions are performed. 490a3031715SAlexander Langer bit 2 (4) set on if an unknown unit is discovered. 491a3031715SAlexander Langer */ 492a3031715SAlexander Langer 493a3031715SAlexander Langer 494a3031715SAlexander Langer #define ERROR 4 495a3031715SAlexander Langer 496a3031715SAlexander Langer int 497a3031715SAlexander Langer reduceproduct(struct unittype * theunit, int flip) 498a3031715SAlexander Langer { 499a3031715SAlexander Langer 500a3031715SAlexander Langer char *toadd; 501a3031715SAlexander Langer char **product; 502a3031715SAlexander Langer int didsomething = 2; 503a3031715SAlexander Langer 504a3031715SAlexander Langer if (flip) 505a3031715SAlexander Langer product = theunit->denominator; 506a3031715SAlexander Langer else 507a3031715SAlexander Langer product = theunit->numerator; 508a3031715SAlexander Langer 509a3031715SAlexander Langer for (; *product; product++) { 510a3031715SAlexander Langer 511a3031715SAlexander Langer for (;;) { 512a3031715SAlexander Langer if (!strlen(*product)) 513a3031715SAlexander Langer break; 514a3031715SAlexander Langer toadd = lookupunit(*product); 515a3031715SAlexander Langer if (!toadd) { 516a3031715SAlexander Langer printf("unknown unit '%s'\n", *product); 517a3031715SAlexander Langer return ERROR; 518a3031715SAlexander Langer } 519a3031715SAlexander Langer if (strchr(toadd, PRIMITIVECHAR)) 520a3031715SAlexander Langer break; 521a3031715SAlexander Langer didsomething = 1; 522a3031715SAlexander Langer if (*product != NULLUNIT) { 523a3031715SAlexander Langer free(*product); 524a3031715SAlexander Langer *product = NULLUNIT; 525a3031715SAlexander Langer } 526a3031715SAlexander Langer if (addunit(theunit, toadd, flip)) 527a3031715SAlexander Langer return ERROR; 528a3031715SAlexander Langer } 529a3031715SAlexander Langer } 530a3031715SAlexander Langer return didsomething; 531a3031715SAlexander Langer } 532a3031715SAlexander Langer 533a3031715SAlexander Langer 534a3031715SAlexander Langer /* 535a3031715SAlexander Langer Reduces numerator and denominator of the specified unit. 536a3031715SAlexander Langer Returns 0 on success, or 1 on unknown unit error. 537a3031715SAlexander Langer */ 538a3031715SAlexander Langer 539a3031715SAlexander Langer int 540a3031715SAlexander Langer reduceunit(struct unittype * theunit) 541a3031715SAlexander Langer { 542a3031715SAlexander Langer int ret; 543a3031715SAlexander Langer 544a3031715SAlexander Langer ret = 1; 545a3031715SAlexander Langer while (ret & 1) { 546a3031715SAlexander Langer ret = reduceproduct(theunit, 0) | reduceproduct(theunit, 1); 547a3031715SAlexander Langer if (ret & 4) 548a3031715SAlexander Langer return 1; 549a3031715SAlexander Langer } 550a3031715SAlexander Langer return 0; 551a3031715SAlexander Langer } 552a3031715SAlexander Langer 553a3031715SAlexander Langer 554a3031715SAlexander Langer int 555a3031715SAlexander Langer compareproducts(char **one, char **two) 556a3031715SAlexander Langer { 557a3031715SAlexander Langer while (*one || *two) { 558a3031715SAlexander Langer if (!*one && *two != NULLUNIT) 559a3031715SAlexander Langer return 1; 560a3031715SAlexander Langer if (!*two && *one != NULLUNIT) 561a3031715SAlexander Langer return 1; 562a3031715SAlexander Langer if (*one == NULLUNIT) 563a3031715SAlexander Langer one++; 564a3031715SAlexander Langer else if (*two == NULLUNIT) 565a3031715SAlexander Langer two++; 566a3031715SAlexander Langer else if (strcmp(*one, *two)) 567a3031715SAlexander Langer return 1; 568a3031715SAlexander Langer else 569a3031715SAlexander Langer one++, two++; 570a3031715SAlexander Langer } 571a3031715SAlexander Langer return 0; 572a3031715SAlexander Langer } 573a3031715SAlexander Langer 574a3031715SAlexander Langer 575a3031715SAlexander Langer /* Return zero if units are compatible, nonzero otherwise */ 576a3031715SAlexander Langer 577a3031715SAlexander Langer int 578a3031715SAlexander Langer compareunits(struct unittype * first, struct unittype * second) 579a3031715SAlexander Langer { 580a3031715SAlexander Langer return 581a3031715SAlexander Langer compareproducts(first->numerator, second->numerator) || 582a3031715SAlexander Langer compareproducts(first->denominator, second->denominator); 583a3031715SAlexander Langer } 584a3031715SAlexander Langer 585a3031715SAlexander Langer 586a3031715SAlexander Langer int 587a3031715SAlexander Langer completereduce(struct unittype * unit) 588a3031715SAlexander Langer { 589a3031715SAlexander Langer if (reduceunit(unit)) 590a3031715SAlexander Langer return 1; 591a3031715SAlexander Langer sortunit(unit); 592a3031715SAlexander Langer cancelunit(unit); 593a3031715SAlexander Langer return 0; 594a3031715SAlexander Langer } 595a3031715SAlexander Langer 596a3031715SAlexander Langer 597a3031715SAlexander Langer void 598a3031715SAlexander Langer showanswer(struct unittype * have, struct unittype * want) 599a3031715SAlexander Langer { 600a3031715SAlexander Langer if (compareunits(have, want)) { 601a3031715SAlexander Langer printf("conformability error\n"); 602a3031715SAlexander Langer showunit(have); 603a3031715SAlexander Langer showunit(want); 604a3031715SAlexander Langer } 605a3031715SAlexander Langer else 606a3031715SAlexander Langer printf("\t* %.8g\n\t/ %.8g\n", have->factor / want->factor, 607a3031715SAlexander Langer want->factor / have->factor); 608a3031715SAlexander Langer } 609a3031715SAlexander Langer 610a3031715SAlexander Langer 611a3031715SAlexander Langer void 612a3031715SAlexander Langer usage() 613a3031715SAlexander Langer { 614d4d0763aSPhilippe Charnier fprintf(stderr, 615d4d0763aSPhilippe Charnier "usage: units [-f unitsfile] [-q] [-v] [from-unit to-unit]\n"); 616a3031715SAlexander Langer exit(3); 617a3031715SAlexander Langer } 618a3031715SAlexander Langer 619a3031715SAlexander Langer 620a3031715SAlexander Langer int 621a3031715SAlexander Langer main(int argc, char **argv) 622a3031715SAlexander Langer { 623a3031715SAlexander Langer 624a3031715SAlexander Langer struct unittype have, want; 625a3031715SAlexander Langer char havestr[81], wantstr[81]; 626a3031715SAlexander Langer int optchar; 627a3031715SAlexander Langer char *userfile = 0; 628a3031715SAlexander Langer int quiet = 0; 629a3031715SAlexander Langer 630a3031715SAlexander Langer while ((optchar = getopt(argc, argv, "vqf:")) != -1) { 631a3031715SAlexander Langer switch (optchar) { 632a3031715SAlexander Langer case 'f': 633a3031715SAlexander Langer userfile = optarg; 634a3031715SAlexander Langer break; 635a3031715SAlexander Langer case 'q': 636a3031715SAlexander Langer quiet = 1; 637a3031715SAlexander Langer break; 638a3031715SAlexander Langer case 'v': 639a3031715SAlexander Langer fprintf(stderr, "\n units version %s Copyright (c) 1993 by Adrian Mariano\n", 640a3031715SAlexander Langer VERSION); 641a3031715SAlexander Langer fprintf(stderr, " This program may be freely distributed\n"); 642a3031715SAlexander Langer usage(); 643a3031715SAlexander Langer default: 644a3031715SAlexander Langer usage(); 645a3031715SAlexander Langer break; 646a3031715SAlexander Langer } 647a3031715SAlexander Langer } 648a3031715SAlexander Langer 649a3031715SAlexander Langer if (optind != argc - 2 && optind != argc) 650a3031715SAlexander Langer usage(); 651a3031715SAlexander Langer 652a3031715SAlexander Langer readunits(userfile); 653a3031715SAlexander Langer 654a3031715SAlexander Langer if (optind == argc - 2) { 655a3031715SAlexander Langer strcpy(havestr, argv[optind]); 656a3031715SAlexander Langer strcpy(wantstr, argv[optind + 1]); 657a3031715SAlexander Langer initializeunit(&have); 658a3031715SAlexander Langer addunit(&have, havestr, 0); 659a3031715SAlexander Langer completereduce(&have); 660a3031715SAlexander Langer initializeunit(&want); 661a3031715SAlexander Langer addunit(&want, wantstr, 0); 662a3031715SAlexander Langer completereduce(&want); 663a3031715SAlexander Langer showanswer(&have, &want); 664a3031715SAlexander Langer } 665a3031715SAlexander Langer else { 666a3031715SAlexander Langer if (!quiet) 667d4d0763aSPhilippe Charnier printf("%d units, %d prefixes\n", unitcount, 668a3031715SAlexander Langer prefixcount); 669a3031715SAlexander Langer for (;;) { 670a3031715SAlexander Langer do { 671a3031715SAlexander Langer initializeunit(&have); 672a3031715SAlexander Langer if (!quiet) 673a3031715SAlexander Langer printf("You have: "); 674a3031715SAlexander Langer if (!fgets(havestr, 80, stdin)) { 6754c0c227dSAlexander Langer if (!quiet) 676a3031715SAlexander Langer putchar('\n'); 677a3031715SAlexander Langer exit(0); 678a3031715SAlexander Langer } 679a3031715SAlexander Langer } while (addunit(&have, havestr, 0) || 680a3031715SAlexander Langer completereduce(&have)); 681a3031715SAlexander Langer do { 682a3031715SAlexander Langer initializeunit(&want); 683a3031715SAlexander Langer if (!quiet) 684a3031715SAlexander Langer printf("You want: "); 685a3031715SAlexander Langer if (!fgets(wantstr, 80, stdin)) { 686a3031715SAlexander Langer if (!quiet) 687a3031715SAlexander Langer putchar('\n'); 688a3031715SAlexander Langer exit(0); 689a3031715SAlexander Langer } 690a3031715SAlexander Langer } while (addunit(&want, wantstr, 0) || 691a3031715SAlexander Langer completereduce(&want)); 692a3031715SAlexander Langer showanswer(&have, &want); 693a3031715SAlexander Langer } 694a3031715SAlexander Langer } 6954c0c227dSAlexander Langer 6964c0c227dSAlexander Langer return(0); 697a3031715SAlexander Langer } 698