Windows-Server-2003/sdktools/units/units.c

1015 lines
22 KiB
C

#include <windows.h>
#include <stdio.h>
#include <signal.h>
#include <float.h>
#include <time.h>
#include <malloc.h>
#include <stdlib.h>
#include <math.h>
static char Name[] = "Ken Reneris. Units ala Unix style";
#define DBL double
#define MAXTYPE 5
extern UCHAR *UnitTab[];
UCHAR token[] = " \t\n";
typedef struct _UNIT {
struct _UNIT *Next;
PSZ UnitName;
PSZ Conversion;
} UNIT, *PUNIT;
PUNIT UnitList;
BOOLEAN Muund;
typedef struct _HALFVALUE {
DBL Accum;
ULONG NoType;
struct {
PUNIT Unit;
ULONG Pow;
} Type [MAXTYPE];
} HALFVALUE, *PHALFVALUE;
typedef struct _FULLVALUE {
ULONG Fuzz;
HALFVALUE Nom;
HALFVALUE Dom;
} FULLVALUE, *PFULLVALUE;
struct {
PSZ Prefix;
ULONG stringlen;
DBL Scaler;
} BuiltInScalers[] = {
"giga", 4, 1000000000.0,
"mega", 4, 1000000.0,
"kilo", 4, 1000.0,
"centi", 5, 0.01,
"milli", 5, 0.001,
"micro", 5, 0.000001,
"nano", 4, 0.000000001,
"decinano", 8, 0.0000000001,
NULL
};
struct {
PSZ BaseType;
PSZ DefaultDump;
} BuiltInDumps[] = {
"sec", "hour second millisec microsec nttime nanosec",
"bit", "terabyte gigabyte megabyte kilobyte dword byte bit",
"meter", "km meter cm mm micron mile feet yard inch",
"kg", "kb gram kilokg milligram ton lb ounce dram",
NULL
};
#define ASSERT(_exp_) if (_exp_) { AssertFailed (__FILE__, __LINE__); }
PVOID zalloc (IN ULONG len);
PSZ StrDup (IN PSZ String);
VOID ReadUnitTab (VOID);
PUNIT LookupUnit (PSZ UnitName);
VOID DumpValue (IN PFULLVALUE Value);
VOID InitializeValue (IN PFULLVALUE Value);
VOID ConvertValue (PFULLVALUE, PSZ, PFULLVALUE, PSZ);
BOOLEAN ProcessString (PUNIT, PSZ, PFULLVALUE);
PSZ CopyUnitName (PSZ Out, PSZ String);
PSZ CopyWord (PSZ Out, PSZ String);
PSZ SkipSpace (PSZ String);
BOOLEAN MatchPattern (PUCHAR String, PUCHAR Pattern);
VOID ReduceTypes (IN OUT PHALFVALUE MValue, IN OUT PHALFVALUE DValue);
VOID AddTypes (IN OUT PHALFVALUE Dest, IN PHALFVALUE Child);
BOOLEAN DumpMatchingTypes (PSZ Str);
VOID GetInput (PSZ Desc, PSZ Str);
VOID AssertFailed (PSZ FileName, ULONG LineNo);
void __cdecl main(int argc, char **argv)
{
UCHAR have[80], want[80], want2[200];
FULLVALUE hValue, wValue;
PSZ p;
ULONG i;
ReadUnitTab ();
if (argc > 1) {
//
// Arguments on the command line. Argv[1] is "have"
//
argv++;
argc -= 2;
strcpy (have, *(argv++));
if (DumpMatchingTypes (have)) {
exit (0);
}
if (!ProcessString (NULL, have, &hValue)) {
printf ("Usage: Units [have] [want]\n");
exit (1);
}
//
// If no Argv[2], then check for default dump
//
if (!argc && hValue.Nom.NoType) {
for (i=0; BuiltInDumps[i].BaseType; i++) {
if (strcmp (BuiltInDumps[i].BaseType, hValue.Nom.Type[0].Unit->UnitName) == 0) {
//
// Dump defaults
//
p = BuiltInDumps[i].DefaultDump;
while (*p) {
p = CopyWord(want, p);
if (ProcessString (NULL, want, &wValue)) {
ConvertValue (&hValue, have, &wValue, want);
}
}
break;
}
}
}
//
// Dump argv[2..n]
//
for ( ; argc; argc--, argv++) {
if (!ProcessString (NULL, *argv, &wValue)) {
exit (1);
}
ConvertValue (&hValue, have, &wValue, *argv);
}
exit (1);
}
//
// Interactive... ask "have" & "want"
//
for (; ;) {
for (; ;) {
GetInput ("You have: ", have);
if (ProcessString (NULL, have, &hValue)) {
break;
}
}
GetInput ("You want: ", want2);
p = want2;
do {
p = CopyWord (want, p);
if (ProcessString (NULL, want, &wValue)) {
ConvertValue (&hValue, have, &wValue, want);
}
} while (*p);
printf ("\n");
}
return ;
}
VOID
GetInput (
PSZ Desc,
PSZ Str
)
{
for (; ;) {
printf (Desc);
if (!gets(Str)) {
exit(1);
}
_strlwr (Str);
if (strcmp (Str, "q") == 0) {
exit (1);
}
if (!DumpMatchingTypes (Str)) {
break;
}
}
}
BOOLEAN DumpMatchingTypes (PSZ Str)
{
PSZ Title, Line;
ULONG LineNo;
UCHAR UnitName[80];
BOOLEAN DumpTitle;
if (!strchr (Str, '*') && !strchr (Str, '?') && !strchr(Str, '[')) {
return FALSE;
}
//
// Dump matching known unitnames
//
printf ("\nKnown types/groups matching: %s\n", Str);
Title = NULL;
for (LineNo = 0; UnitTab[LineNo]; LineNo++) {
Line = UnitTab[LineNo];
if (Line[0] == '/') {
Title = Line;
DumpTitle = MatchPattern (Title, Str);
}
CopyUnitName (UnitName, Line);
if (!UnitName[0]) {
continue;
}
if (MatchPattern (UnitName, Str) || DumpTitle) {
if (Title) {
printf ("%s\n", Title);
Title = NULL;
}
printf (" %s\n", Line);
}
}
printf ("\n");
return TRUE;
}
PSZ SkipSpace (PSZ String)
{
while (*String && (*String == ' ' || *String < ' ' || *String == '^')) {
String ++;
}
return String;
}
PSZ CopyNumber (PSZ Out, PSZ String)
{
while (*String >= '0' && *String <= '9' || *String == '.') {
*(Out++) = *(String++);
}
*Out = 0;
return String;
}
PSZ CopyWord (PSZ Out, PSZ String)
{
UCHAR c;
while (*String) {
if (*String <= ' ') {
break;
}
*(Out++) = *(String++);
}
*Out = 0;
return SkipSpace (String);
}
PSZ CopyUnitName (PSZ Out, PSZ String)
{
UCHAR c;
while (c = *String) {
if (c >= '0' && c <= '9' || c == '.') {
break;
}
if (c == '-' || c == '+' || c == '/' || c == ' ') {
break;
}
if (c == '^' || c < ' ') {
String++;
continue;
}
*(Out++) = *(String++);
}
*Out = 0;
return String;
}
VOID
AssertFailed (PSZ FileName, ULONG LineNo)
{
printf ("Assert failed - file %s line %d\n", FileName, LineNo);
exit (1);
}
PSZ
GetBaseType (
IN PSZ Out,
IN PHALFVALUE HValue
)
{
ULONG i;
if (HValue->NoType == 0) {
Out += sprintf (Out, "constant");
} else {
for (i=0; i < HValue->NoType; i++) {
Out += sprintf (Out, "%s%s", i ? "-" : "", HValue->Type[i].Unit->UnitName);
if (HValue->Type[i].Pow != 1) {
Out += sprintf (Out, "^%d", HValue->Type[i].Pow);
}
}
}
return Out;
}
VOID
GetBaseTypes (
OUT PSZ Out,
IN PFULLVALUE Value
)
/**
* Returns ascii dump of values data type
*/
{
PUNIT Unit;
ULONG i;
Out = GetBaseType (Out, &Value->Nom);
if (Value->Dom.NoType) {
Out += sprintf (Out, "/");
Out = GetBaseType (Out, &Value->Dom);
}
}
VOID
DumpValue (
IN PFULLVALUE Value
)
{
UCHAR s[80];
GetBaseTypes (s, Value);
printf ("%g/%g type %s\n", Value->Nom.Accum, Value->Dom.Accum, s);
}
VOID
SortType (
IN PHALFVALUE Value
)
{
ULONG i, j;
ULONG hpow;
PUNIT hunit;
//
// Sort by lowest power, then alphabetical
//
for (i=0; i < Value->NoType; i++) {
for (j=i+1; j < Value->NoType; j++) {
if (Value->Type[i].Pow > Value->Type[j].Pow ||
(Value->Type[i].Pow == Value->Type[j].Pow &&
strcmp (Value->Type[i].Unit->UnitName, Value->Type[j].Unit->UnitName) > 0)) {
// swap
hpow = Value->Type[i].Pow;
hunit = Value->Type[i].Unit;
Value->Type[i].Pow = Value->Type[j].Pow;
Value->Type[i].Unit = Value->Type[j].Unit;
Value->Type[j].Pow = hpow;
Value->Type[j].Unit = hunit;
}
}
}
}
VOID
FormatDbl (
OUT PSZ s,
IN DBL Value
)
/**
* Function to print double "Value" into string "s". This
* functions sole purpose is to get a better readable reresentation
* of the value into ascii
*/
{
PSZ p1, p2, dp;
UCHAR t[80];
LONG i;
i = 18 - sprintf (t, "%.1f", Value);
if (i < 0) {
i = 0;
}
sprintf (t, "%.*f", i, Value);
//
// strip off trailing zeros
//
for (dp=t; *dp; dp++) {
if (*dp == '.') {
for (p1=p2=dp+1; *p2; p2++) {
if (*p2 != '0') {
p1 = p2;
}
}
p1[1] = 0;
if (p1 == dp+1 && p1[0] == '0') {
// it's ".0" remove the whole thing
*dp = 0;
}
break;
}
}
i = (LONG)(dp - t); // # of digits before decimal point
i = i % 3;
if (i == 0) {
i = 3;
}
//
// Copy to decimal point while adding commas
//
for (p1=s, p2=t; *p2 && *p2 != '.'; p2++) {
if (i-- == 0) {
*(p1++) = ',';
i = 2;
}
*(p1++) = *p2;
}
//
// Copy remainer
//
do {
*(p1++) = *p2;
} while (*(p2++));
//
// Did result == 0? Probabily lost precision
//
if (strcmp (s, "0") == 0) {
sprintf (s, "%.18g", Value);
}
}
VOID
ConvertValue (
IN PFULLVALUE hValue,
IN PSZ have,
IN PFULLVALUE wValue,
IN PSZ want
)
{
DBL ans;
UCHAR s1[80], s2[80], cf[80];
FULLVALUE Junk1, Junk2;
BOOLEAN flag;
DBL hAccum, wAccum;
PSZ p1, p2, p3, p4, p5;
have = SkipSpace(have);
want = SkipSpace(want);
hAccum = hValue->Nom.Accum / hValue->Dom.Accum;
wAccum = wValue->Nom.Accum / wValue->Dom.Accum;
ans = hAccum / wAccum;
p3 = "";
p5 = NULL;
//
// See if types match by checking if they cancle each other out
//
Junk1 = *hValue;
Junk2 = *wValue;
AddTypes (&Junk1.Nom, &Junk2.Dom);
AddTypes (&Junk1.Dom, &Junk2.Nom);
ReduceTypes (&Junk1.Nom, &Junk1.Dom);
if (Junk1.Nom.NoType + Junk1.Dom.NoType != 0) {
//
// See if types are inverse
//
Junk1 = *hValue;
Junk2 = *wValue;
AddTypes (&Junk1.Nom, &Junk2.Nom);
AddTypes (&Junk1.Dom, &Junk2.Dom);
ReduceTypes (&Junk1.Nom, &Junk1.Dom);
if (Junk1.Nom.NoType + Junk1.Dom.NoType == 0) {
// inverse result
ans = 1.0 / (hAccum / (1.0 / wAccum));
p5 = "Warning";
} else {
// types are not conforming
p5 = "Conformance";
}
}
cf[0] = 0;
if (p5) {
SortType (&hValue->Nom);
SortType (&hValue->Dom);
SortType (&wValue->Nom);
SortType (&wValue->Dom);
GetBaseTypes (s1, hValue);
GetBaseTypes (s2, wValue);
sprintf (cf, " (%s: %s -> %s)", p5, s1, s2);
}
FormatDbl (s1, ans); // fancy
sprintf (s2, "%.g", ans); // bland
p1 = (have[0] >= 'a' && have[0] <= 'z') ? "1" : "";
p2 = (hValue->Fuzz | wValue->Fuzz) ? "(fuzzy) " : "",
printf (" %s%s -> %s %s%s%s%s\n", p1, have, s1, p2, p3, want, cf);
p4 = strchr (s2, 'e');
if (p4 && !strchr(s1,'e') && atoi(p4+2) > 9) {
// print bland answer as well
printf (" %s%s -> %s %s%s%s%s\n", p1, have, s2, p2, p3, want, cf);
}
}
BOOLEAN
ProcessString (
IN PUNIT Unit,
IN PSZ String,
OUT PFULLVALUE ReturnValue
)
{
UCHAR s[80], c;
ULONG i, j;
FULLVALUE ChildValue;
PHALFVALUE MValue, DValue, hldvalue;
ReturnValue->Fuzz = 0;
MValue = &ReturnValue->Nom;
DValue = &ReturnValue->Dom;
MValue->Accum = 1.0;
MValue->NoType = 0;
DValue->Accum = 1.0;
DValue->NoType = 0;
String = SkipSpace(String);
c = *String;
if (c == '*') {
//
// This is a base value
//
MValue->NoType = 1;
MValue->Type[0].Unit = Unit;
MValue->Type[0].Pow = 1;
return TRUE;
}
if (c >= '0' && c <= '9' || c == '.') {
//
// Constant multiplcation
//
String = CopyNumber (s, String);
String = SkipSpace(String);
MValue->Accum *= atof(s);
}
if (*String == '|') {
//
// Constant Division
//
String++;
String = CopyNumber (s, String);
if (s[0]) {
DValue->Accum *= atof(s);
}
}
if (*String == '+' || *String == '-') {
//
// 10^x
//
s[0] = *(String++);
String = CopyNumber (s+1, String);
MValue->Accum *= pow (10.0, atof(s));
}
for (; ;) {
String = SkipSpace(String);
if (!*String) {
break;
}
switch (*String) {
case '/':
// flip denominator & numerator
hldvalue = MValue;
MValue = DValue;
DValue = hldvalue;
String++;
continue; // get next token
case '-':
// skip these
String++;
continue;
default:
break;
}
//
// Find sub unit type
//
String = CopyUnitName (s, String);
Unit = LookupUnit (s);
if (!Unit) {
//
// Check for common scaler prefix on keyword
//
for (i=0; BuiltInScalers[i].Prefix; i++) {
if (strncmp (s,
BuiltInScalers[i].Prefix,
BuiltInScalers[i].stringlen) == 0) {
// Scale the value & skip word prefix
MValue->Accum *= BuiltInScalers[i].Scaler;
Unit = LookupUnit (s + BuiltInScalers[i].stringlen);
break;
}
}
if (!Unit) {
printf ("Unit type '%s' unkown\n", s);
return FALSE;
}
}
//
// Get conversion value for this component
//
if (!ProcessString (Unit, Unit->Conversion, &ChildValue)) {
return FALSE;
}
if (strcmp (Unit->UnitName, "fuzz") == 0) {
ReturnValue->Fuzz = 1;
}
if (*String >= '1' && *String <= '9') {
// raise power
i = *(String++) - '0';
ChildValue.Nom.Accum = pow (ChildValue.Nom.Accum, i);
ChildValue.Dom.Accum = pow (ChildValue.Dom.Accum, i);
for (j=0; j < ChildValue.Nom.NoType; j++) {
ChildValue.Nom.Type[j].Pow *= i;
}
for (j=0; j < ChildValue.Dom.NoType; j++) {
ChildValue.Dom.Type[i].Pow *= i;
}
}
//
// Merge values from child
//
ReturnValue->Fuzz |= ChildValue.Fuzz;
MValue->Accum *= ChildValue.Nom.Accum;
DValue->Accum *= ChildValue.Dom.Accum;
//
// Merge data types from child
//
AddTypes (MValue, &ChildValue.Nom);
AddTypes (DValue, &ChildValue.Dom);
ReduceTypes (MValue, DValue);
}
return TRUE;
}
VOID
AddTypes (
IN OUT PHALFVALUE Dest,
IN PHALFVALUE Child
)
/**
* Add's types from Child to Dest. If the data type already exist in
* dest then it's power is raised; otherwise the new type is added to the list
*
*/
{
ULONG i, j;
for (i=0; i < Child->NoType; i++) {
for (j=0; j < Dest->NoType; j++) {
if (Child->Type[i].Unit == Dest->Type[j].Unit) {
// unit already is destionation - move it
Dest->Type[j].Pow += Child->Type[i].Pow;
Child->Type[i].Unit = NULL;
Child->Type[i].Pow = 0;
}
}
if (Child->Type[i].Unit) {
// unit not in destionation - add it
j = (Dest->NoType++);
ASSERT (j >= MAXTYPE);
Dest->Type[j].Unit = Child->Type[i].Unit;
Dest->Type[j].Pow = Child->Type[i].Pow;
}
}
}
VOID
ReduceTypes (
IN OUT PHALFVALUE MValue,
IN OUT PHALFVALUE DValue
)
/**
* Divides & cancles data types.
*/
{
ULONG i, j, k;
BOOLEAN Restart;
Restart = TRUE;
while (Restart) {
Restart = FALSE;
for (i=0; i < MValue->NoType; i++) {
for (j=0; j < DValue->NoType; j++) {
if (MValue->Type[i].Unit == DValue->Type[j].Unit) {
// matching types - reduce
MValue->Type[i].Pow -= DValue->Type[j].Pow;
DValue->Type[j].Unit = NULL;
DValue->Type[j].Pow = 0;
}
if (DValue->Type[j].Pow == 0) {
// pull this type out of the denominator
for (k=j+1; k < DValue->NoType; k++) {
DValue->Type[k-1] = DValue->Type[k];
}
DValue->NoType -= 1;
Restart = TRUE;
continue;
}
}
if (MValue->Type[i].Pow == 0) {
// pull this type out of the numerator
for (k=i+1; k < DValue->NoType; k++) {
DValue->Type[k-1] = DValue->Type[k];
}
MValue->NoType -= 1;
Restart = TRUE;
continue;
}
}
}
}
VOID
ReadUnitTab (VOID)
{
UCHAR Line[80], s[80];
ULONG LineNo;
PUNIT Unit;
PSZ p, p1;
for (LineNo = 0; UnitTab[LineNo]; LineNo++) {
strcpy (Line, UnitTab[LineNo]);
//
// Strip off trailing blanks
//
for (p=p1=Line; *p; p++) {
if (*p != ' ') {
p1 = p;
}
}
p1[1] = 0;
//
// First word is type of unit
//
p = SkipSpace (Line);
if (*p == 0 || *p == '/') {
continue;
}
p = CopyUnitName (s, p);
Unit = zalloc (sizeof(UNIT));
Unit->UnitName = StrDup (s);
//
// Rest of line is Conversion string
//
p = SkipSpace (p);
Unit->Conversion = StrDup (p);
//
// Add Unit to list of all known units
//
Unit->Next = UnitList;
UnitList = Unit;
}
}
PUNIT LookupUnit (PSZ UnitName)
{
PUNIT Unit;
UCHAR Name[40];
ULONG i;
for (i=0; UnitName[i]; i++) {
Name[i] = UnitName[i];
if (Name[i] >= '0' && Name[i] <= '9') {
break;
}
}
Name[i] = 0;
for (Unit=UnitList; Unit; Unit = Unit->Next) {
if (strcmp (Unit->UnitName, Name) == 0) {
break;
}
}
return Unit;
}
PSZ StrDup (IN PSZ String)
{
ULONG len;
PSZ p;
// allocate & duplicate string
len = strlen(String)+1;
p = malloc (len);
if (!p) {
printf ("Out of memory\n");
exit (1);
}
memcpy (p, String, len);
return p;
}
PVOID zalloc (IN ULONG len)
{
PVOID p;
// allocate & zero memory
p = malloc (len);
if (!p) {
printf ("Out of memory\n");
exit (1);
}
memset (p, 0, len);
return p;
}
/*** MatchPattern - check if string matches pattern
*
* Supports:
* * - Matches any number of characters (including zero)
* ? - Matches any 1 character
* [set] - Matches any charater to charater in set
* (set can be a list or range)
*
*/
BOOLEAN MatchPattern (PUCHAR String, PUCHAR Pattern)
{
UCHAR c, p, l;
for (; ;) {
switch (p = *Pattern++) {
case 0: // end of pattern
return *String ? FALSE : TRUE; // if end of string TRUE
case '*':
while (*String) { // match zero or more char
if (MatchPattern (String++, Pattern))
return TRUE;
}
return MatchPattern (String, Pattern);
case '?':
if (*String++ == 0) // match any one char
return FALSE; // not end of string
break;
case '[':
if ( (c = *String++) == 0) // match char set
return FALSE; // syntax
c = (UCHAR)tolower(c);
l = 0;
while (p = *Pattern++) {
if (p == ']') // if end of char set, then
return FALSE; // no match found
if (p == '-') { // check a range of chars?
p = *Pattern; // get high limit of range
if (p == 0 || p == ']')
return FALSE; // syntax
if (c >= l && c <= p)
break; // if in range, move on
}
l = p;
if (c == p) // if char matches this element
break; // move on
}
while (p && p != ']') // got a match in char set
p = *Pattern++; // skip to end of set
break;
default:
c = *String++;
if (tolower(c) != p) // check for exact char
return FALSE; // not a match
break;
}
}
}