This archive contains answers to questions sent to Unidata support through mid-2025. Note that the archive is no longer being updated. We provide the archive for reference; many of the answers presented here remain technically correct, even if somewhat outdated. For the most up-to-date information on the use of NSF Unidata software and data services, please consult the Software Documentation first.
Jianfu Pan, >Date: Tue, 22 Feb 2000 10:17:35 -0500 (EST) >From: Jianfu Pan <address@hidden> >Organization: NASA/GSFC >To: address@hidden (Steve Emmerson) >Subject: Re: 20000218: FCALLSCFUN4, FCALLSCFUN8 problem in uniunits >installation >Keywords: 200002181939.MAA10304 In the above message, you wrote: > Sorry for the late response. Here are the answers to your questions: > > 1. A Fortran "REAL"; > > size is 8 bytes. > > > > 2. A Fortran "DOUBLE PRECISION"; > > 16 bytes. > > > > 3. A C "float" > > 8 bytes. > > > > 4. A C "double" > > 8 bytes. Thanks. I've enclosed a modified version of the file "lib/utlib.c" which should solve your problem. Please replace your version with it and try to build the package. Please let me know if this helps. Regards, Steve Emmerson <http://www.unidata.ucar.edu> --------Begin lib/utlib c /* * $Id: utlib.c,v 1.18 1999/09/08 16:18:53 steve Exp $ * * Support functions for the units(3) library. */ /*LINTLIBRARY*/ #ifndef _XOPEN_SOURCE # define _XOPEN_SOURCE #endif #ifndef _ANSI_C_SOURCE # define _ANSI_C_SOURCE #endif #include <udposix.h> #include <stddef.h> /* for ptrdiff_t */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <ctype.h> #include <math.h> #include <assert.h> #include <search.h> #include <math.h> /* for modf(), floor(), log10(), ceil() */ #include <limits.h> /* for *PATH_MAX */ #include <float.h> /* for DBL_DIG */ #include "cfortran.h" /* for FORTRAN support */ #include "udunits.h" #include "utprivate.h" /* * cfortran.h support for unit arguments: */ /* C-returning-to-FORTRAN: */ #define PUNIT_cfFZ(UN,LN) utUnit* fcallsc(UN,LN)(void /* The following might be wrong and require modification for the next * version of cfortran.h */ #define PUNIT_cfINT(N,A,B,X,Y,Z) SIMPLE_cfINT(N,A,B,X,Y,Z) #define PUNIT_cfUU(PUNIT,A0) utUnit* A0 #define PUNIT_cfL A0 = #define PUNIT_cfI return A0; #define PUNIT_cfK /* FORTRAN-calling-C: */ #define PPUNIT_cfV( T,A,B,F) SIMPLE_cfV(T,A,B,F) #define PPUNIT_cfSEP(T, B) SIMPLE_cfSEP(T,B) #define PPUNIT_cfINT(N,A,B,X,Y,Z) SIMPLE_cfINT(N,A,B,X,Y,Z) #define PPUNIT_cfSTR(N,T,A,B,C,D,E) SIMPLE_cfSTR(N,T,A,B,C,D,E) #define PPUNIT_cfCC( T,A,B) SIMPLE_cfCC(T,A,B) #define PPUNIT_cfAA( T,A,B) PPUNIT_cfB(T,A) /* Argument B not used. */ #define PPUNIT_cfU( T,A) PPUNIT_cfN(T,A) #define PPUNIT_cfN( T,A) utUnit** A #define PPUNIT_cfT(M,I,A,B,D) *A /* Output, Fortran character buffer: */ #define CBUF_cfINT(N,A,B,X,Y,Z) STRING_cfINT(N,A,B,X,Y,Z) #define CBUF_cfSEP(T, B) STRING_cfSEP(T,B) #define CBUF_cfN( T,A) STRING_cfN(T,A) #define CBUF_cfSTR(N,T,A,B,C,D,E) STRING_cfSTR(N,T,A,B,C,D,E) #if defined(vmsFortran) # define CBUF_cfT(M,I,A,B,D) A->dsc$a_pointer, A->dsc$w_length #elif defined(CRAYFortran) # define CBUF_cfT(M,I,A,B,D) _fcdtocp(A), _fcdlen(A) #else # define CBUF_cfT(M,I,A,B,D) A, D #endif #ifndef PATH_MAX # define PATH_MAX _POSIX_PATH_MAX #endif #undef DUPSTR #define DUPSTR(s) strcpy((char*)malloc(strlen(s)+1), s) #undef ABS #define ABS(a) ((a) < 0 ? -(a) : (a)) #undef MIN #define MIN(a,b) ((a) < (b) ? (a) : (b)) #undef MAX #define MAX(a,b) ((a) > (b) ? (a) : (b)) typedef struct { char *name; int nchr; int HasPlural; utUnit unit; } UnitEntry; typedef struct { char *name; /* prefix string (e.g. "milli") */ UtFactor factor; /* corresponding multiplying factor */ short nchar; /* size of prefix string excluding EOS */ } PrefixEntry; /* * Prefix table in the order required by the prefix-entry comparison function. * The names are Based on ANSI/IEEE Std 260-1978 (a.k.a. ANSI Y10.19-1969) -- * reaffirmed 1985. The names must be unique. * * NB: The short-prefix symbol corresponding to the prefix "micro" has been * changed here from the standard one (the Greek letter "mu") to the symbol * "u". */ #define PE(name, factor) {name, factor, sizeof(name)-1} static PrefixEntry PrefixTable[] = { PE("E", 1e18), PE("G", 1e9), PE("M", 1e6), PE("P", 1e15), PE("T", 1e12), PE("Y", 1e24), PE("Z", 1e21), PE("a", 1e-18), PE("atto", 1e-18), PE("c", 1e-2), PE("centi", 1e-2), PE("d", 1e-1), PE("da", 1e1), PE("deca", 1e1), /* Spelling according to "ISO 2955: Information * processing -- Representation of SI and other units * in systems with limited character sets". */ PE("deci", 1e-1), PE("deka", 1e1), /* Spelling according to "ASTM Designation: E 380 - 85: * Standard for METRIC PRACTICE" and "ANSI/IEEE Std * 260-1978 (Reaffirmed 1985): IEEE Standard Letter * Symbols for Units of Measurement". */ PE("exa", 1e18), PE("f", 1e-15), PE("femto", 1e-15), PE("giga", 1e9), PE("h", 1e2), PE("hecto", 1e2), PE("k", 1e3), PE("kilo", 1e3), PE("m", 1e-3), PE("mega", 1e6), PE("micro", 1e-6), PE("milli", 1e-3), PE("n", 1e-9), PE("nano", 1e-9), PE("p", 1e-12), PE("peta", 1e15), PE("pico", 1e-12), PE("tera", 1e12), PE("u", 1e-6), PE("y", 1e-24), PE("yocto", 1e-24), PE("yotta", 1e24), PE("z", 1e-21), PE("zepto", 1e-21), PE("zetta", 1e21), NULL }; static void *root = NULL; static int initialized = 0; /* module initialized = no */ static int NumberBaseUnits = 0; /* number of base units */ static int HaveStdTimeUnit = 0; /* standard time unit set? */ static char *input_buf; /* scanner input buffer */ static char *input_ptr; /* scanner input position */ static char *unput_ptr; /* position in unput() buffer */ static char unput_buf[512]; /* scanner unput() buffer */ static char linebuf[512]; /* input units-specification buffer */ static char BaseName[UT_MAXNUM_BASE_QUANTITIES][UT_NAMELEN]; static FILE *UtFile; /* input units-file */ static char UnitsFilePath[PATH_MAX];/* input units-file path */ static utUnit StdTimeUnit; /* standard time unit for determining * if a unit structure refers to a unit * of time */ /* * The following two functions convert between Julian day number and * Gregorian/Julian dates (Julian dates are used prior to October 15, * 1582; Gregorian dates are used after that). Julian day number 0 is * midday, January 1, 4713 BCE. The Gregorian calendar was adopted * midday, October 15, 1582. * * Author: Robert Iles, March 1994 * * C Porter: Steve Emmerson, October 1995 * * Original: http://www.nag.co.uk:70/nagware/Examples/calendar.f90 * * There is no warranty on this code. */ /* * Convert a Julian day number to a Gregorian/Julian date. */ void julday_to_gregdate(julday, year, month, day) unsigned long julday; /* Julian day number to convert */ int *year; /* Gregorian year (out) */ int *month; /* Gregorian month (1-12) (out) */ int *day; /* Gregorian day (1-31) (out) */ { #if INT_MAX <= 0X7FFF long ja, jb, jd; #else int ja, jb, jd; #endif int jc; int je, iday, imonth, iyear; double xc; if (julday < 2299161) ja = julday; else { int ia = ((julday - 1867216) - 0.25) / 36524.25; ja = julday + 1 + ia - (int)(0.25 * ia); } jb = ja + 1524; xc = ((jb - 2439870) - 122.1) / 365.25; jc = 6680.0 + xc; jd = 365 * jc + (int)(0.25 * jc); je = (int)((jb - jd) / 30.6001); iday = (int)(jb - jd - (int)(30.6001 * je)); imonth = je - 1; if (imonth > 12) imonth -= 12; iyear = jc - 4715; if (imonth > 2) iyear -= 1; if (iyear <= 0) iyear -= 1; *year = iyear; *month = imonth; *day = iday; } /* * Convert a Gregorian/Julian date to a Julian day number. * * The Gregorian calendar was adopted midday, October 15, 1582. */ unsigned long gregdate_to_julday(year, month, day) int year; /* Gregorian year */ int month; /* Gregorian month (1-12) */ int day; /* Gregorian day (1-31) */ { #if INT_MAX <= 0X7FFF long igreg = 15 + 31 * (10 + (12 * 1582)); long iy; /* signed, origin 0 year */ long ja; /* Julian century */ long jm; /* Julian month */ long jy; /* Julian year */ #else int igreg = 15 + 31 * (10 + (12 * 1582)); int iy; /* signed, origin 0 year */ int ja; /* Julian century */ int jm; /* Julian month */ int jy; /* Julian year */ #endif unsigned long julday; /* returned Julian day number */ /* * Because there is no 0 BC or 0 AD, assume the user wants the start of * the common era if they specify year 0. */ if (year == 0) year = 1; iy = year; if (year < 0) iy++; if (month > 2) { jy = iy; jm = month + 1; } else { jy = iy - 1; jm = month + 13; } /* * Note: SLIGHTLY STRANGE CONSTRUCTIONS REQUIRED TO AVOID PROBLEMS WITH * OPTIMISATION OR GENERAL ERRORS UNDER VMS! */ julday = day + (int)(30.6001 * jm); if (jy >= 0) { julday += 365 * jy; julday += 0.25 * jy; } else { double xi = 365.25 * jy; if ((int)xi != xi) xi -= 1; julday += (int)xi; } julday += 1720995; if (day + (31* (month + (12 * iy))) >= igreg) { ja = jy/100; julday -= ja; julday += 2; julday += ja/4; } return julday; } /* * Encode a date as a double-precision value. */ double utencdate(year, month, day) int year; int month; int day; { return ((long)gregdate_to_julday(year, month, day) - (long)gregdate_to_julday(2001, 1, 1)) * 86400.0; } /* * Encode a time as a double-precision value. */ double utencclock(hours, minutes, seconds) int hours; int minutes; double seconds; { return (hours*60 + minutes)*60 + seconds; } /* * Decompose a value into a set of values accounting for uncertainty. */ static void decomp(value, uncer, nbasis, basis, count) double value; double uncer; /* >= 0 */ int nbasis; double *basis; /* all values > 0 */ double *count; { int i; for (i = 0; i < nbasis; i++) { double r = fmod(value, basis[i]); /* remainder */ /* Adjust remainder to minimum magnitude. */ if (ABS(2*r) > basis[i]) r += r > 0 ? -basis[i] : basis[i]; if (ABS(r) <= uncer) { /* The value equals a basis multiple within the uncertainty. */ double half = value < 0 ? -basis[i]/2 : basis[i]/2; modf((value+half)/basis[i], count+i); break; } value = basis[i] * modf(value/basis[i], count+i); } for (i++; i < nbasis; i++) count[i] = 0; } /* * Decode a time from a double-precision value. */ static void dectime(value, year, month, day, hour, minute, second) double value; int *year; int *month; int *day; int *hour; int *minute; float *second; { long days; long hours; long minutes; double seconds; double uncer; /* uncertainty of input value */ typedef union { double vec[7]; struct { double days; double hours12; double hours; double minutes10; double minutes; double seconds10; double seconds; } ind; } Basis; Basis counts; static Basis basis; basis.ind.days = 86400; basis.ind.hours12 = 43200; basis.ind.hours = 3600; basis.ind.minutes10 = 600; basis.ind.minutes = 60; basis.ind.seconds10 = 10; basis.ind.seconds = 1; uncer = ldexp(value < 0 ? -value : value, -DBL_MANT_DIG); days = floor(value/86400.0); value -= days * 86400.0; /* make positive excess */ decomp(value, uncer, sizeof(basis.vec)/sizeof(basis.vec[0]), basis.vec, counts.vec); days += counts.ind.days; hours = (int)counts.ind.hours12 * 12 + (int)counts.ind.hours; minutes = (int)counts.ind.minutes10 * 10 + (int)counts.ind.minutes; seconds = (int)counts.ind.seconds10 * 10 + counts.ind.seconds; *second = seconds; *minute = minutes; *hour = hours; julday_to_gregdate(gregdate_to_julday(2001, 1, 1) + days, year, month, day); } /* * Indicate whether or not the given unit structure refers to a unit of time. * * This function returns: * 1 unit structure is a unit of time; * 0 unit structure is not a unit of time. */ int utIsTime(up) const utUnit *up; { int status; if (!initialized || !HaveStdTimeUnit) { status = 0; } else { int iquan; for (iquan = 0; iquan < UT_MAXNUM_BASE_QUANTITIES; ++iquan) if (up->power[iquan] != StdTimeUnit.power[iquan]) break; status = iquan == UT_MAXNUM_BASE_QUANTITIES; } return status; } /* * FORTRAN interface to the above function. */ FCALLSCFUN1(INT,utIsTime,UTTIME,uttime, PPUNIT) /* * Indicate whether or not the given unit structure has an origin. * * This function returns: * 1 unit structure has an origin; * 0 unit structure doesn't have an origin. */ int utHasOrigin(up) const utUnit *up; { return initialized && up->hasorigin; } /* * FORTRAN interface to the above function. */ FCALLSCFUN1(INT,utHasOrigin,UTORIGIN,utorigin, PPUNIT) /* * Clear a unit structure by setting it to the dimensionless identity * unit structure. * * This function returns a pointer to the unit structure. */ utUnit* utClear(unit) utUnit *unit; { register int iquan; unit->hasorigin = 0; unit->origin = 0.0; unit->factor = 1.0; for (iquan = 0; iquan < UT_MAXNUM_BASE_QUANTITIES; ++iquan) unit->power[iquan] = 0; return unit; } /* * FORTRAN interface to the above function. */ FCALLSCSUB1(utClear,UTCLR,utclr, PPUNIT) /* * Copy a unit structure. * * This function returns a pointer to the destination unit structure. */ utUnit* utCopy(source, dest) const utUnit *source; utUnit *dest; { assert(source != NULL); assert(dest != NULL); *dest = *source; return dest; } /* * FORTRAN interface to the above function. */ FCALLSCSUB2(utCopy,UTCPY,utcpy, PPUNIT,PPUNIT) /* * Set the exponent of the given base quantity to unity. * * This function returns: * NULL error; * !NULL success (points to the input unit structure). */ utUnit* utSetPower(unit, position) utUnit *unit; int position; { utUnit *result = NULL; if (position < 0 || position >= UT_MAXNUM_BASE_QUANTITIES) { (void) fprintf(stderr, "udunits(3): %d is an invalid quantity index. Valid range is 0-%d\n", position, UT_MAXNUM_BASE_QUANTITIES); } else { unit->power[position] = 1; result = unit; } return result; } /* * Shift the origin of a unit structure. * * This function returns a pointer to the destination unit structure. */ utUnit* utShift(source, amount, result) utUnit *source; double amount; utUnit *result; { assert(source != NULL); assert(result != NULL); (void) utCopy(source, result); result->origin = source->origin + amount*result->factor; result->hasorigin = 1; return result; } /* * FORTRAN interface to the above function. */ FCALLSCSUB3(utShift,UTORIG,utorig, PPUNIT,DOUBLE,PPUNIT) /* * Scale a "unit" structure. * * This function returns a pointer to the destination unit structure. */ utUnit* utScale(source, factor, result) utUnit *source; double factor; utUnit *result; { assert(source != NULL); assert(result != NULL); (void) utCopy(source, result); result->factor *= factor; result->origin *= factor; return result; } /* * FORTRAN interface to the above function. */ FCALLSCSUB3(utScale,UTSCAL,utscal, PPUNIT,DOUBLE,PPUNIT) /* * Multiply two unit-structures. * * This function returns * NULL failure; * pointer to `result' success. */ utUnit* utMultiply(term1, term2, result) utUnit *term1, *term2, *result; { utUnit *res = NULL; if (term2->hasorigin && term1->hasorigin) { (void) fprintf(stderr, "udunits(3): Can't multiply units with origins\n"); } else { int iquan; result->factor = term1->factor * term2->factor; result->origin = term1->hasorigin ? term1->origin * term2->factor : term2->origin * term1->factor; result->hasorigin = term1->hasorigin || term2->hasorigin; for (iquan = 0; iquan < UT_MAXNUM_BASE_QUANTITIES; ++iquan) result->power[iquan] = term1->power[iquan] + term2->power[iquan]; res = result; } return res; } /* * FORTRAN interface to the above function. */ FCALLSCSUB3(utMultiply,UTMULT,utmult, PPUNIT,PPUNIT,PPUNIT) /* * Form the reciprocal of an internal unit specification. * * This function returns * NULL failure; * pointer to `result' success. */ utUnit* utInvert(source, result) utUnit *source, *result; { utUnit *res = NULL; if (source->hasorigin) { (void) fprintf(stderr, "udunits(3): Can't invert a unit with an origin\n"); } else { int iquan; result->factor = 1./source->factor; result->origin = 0.0; result->hasorigin = 0; for (iquan = 0; iquan < UT_MAXNUM_BASE_QUANTITIES; ++iquan) result->power[iquan] = -source->power[iquan]; res = result; } return res; } /* * FORTRAN interface to the above function. */ FCALLSCSUB2(utInvert,UTINV,utinv, PPUNIT,PPUNIT) /* * Divide two unit-structures. * * This function returns * NULL failure; * pointer to `result' success. */ utUnit* utDivide(numer, denom, result) utUnit *numer, *denom, *result; { utUnit *res = NULL; if (denom->hasorigin && numer->hasorigin) { (void) fprintf(stderr, "udunits(3): Can't divide units with origins\n"); } else { int iquan; result->factor = numer->factor / denom->factor; result->origin = numer->origin; result->hasorigin = numer->hasorigin; for (iquan = 0; iquan < UT_MAXNUM_BASE_QUANTITIES; ++iquan) result->power[iquan] = numer->power[iquan] - denom->power[iquan]; res = result; } return res; } /* * FORTRAN interface to the above function. */ FCALLSCSUB3(utDivide,UTDIV,utdiv, PPUNIT,PPUNIT,PPUNIT) /* * Raise a unit-structure to a given power. * * This function returns * NULL failure; * pointer to `result' success. */ utUnit* utRaise(source, power, result) utUnit *source; int power; utUnit *result; { utUnit *res = NULL; if (source->hasorigin) { (void) fprintf(stderr, "udunits(3): Can't exponentiate a unit with an origin\n"); } else { int iquan; result->factor = pow(source->factor, (double)power); result->origin = 0.0; result->hasorigin = 0; for (iquan = 0; iquan < UT_MAXNUM_BASE_QUANTITIES; ++iquan) result->power[iquan] = source->power[iquan] * power; res = result; } return res; } /* * FORTRAN interface to the above function. */ FCALLSCSUB3(utRaise,UTEXP,utexp, PPUNIT,INT,PPUNIT) /* * Open a units-file. * * This function returns: * 0 success * UT_ENOFILE couldn't open file */ static int OpenUnits(filename) char *filename; { int status; /* return status */ if ((UtFile = fopen(filename, "r")) == NULL) { (void) fprintf(stderr, "udunits(3): Couldn't open units database \"%s\": ", filename); perror(""); status = UT_ENOFILE; } else { UtLineno = 0; linebuf[sizeof(linebuf)-1] = 0; status = 0; } return status; } /* * Close a units file. * * This function returns void. */ static void CloseUnits() { if (UtFile != NULL) { (void)fclose(UtFile); UtFile = NULL; } } /* * Decode a unit specification. * * This function returns: * 0 success * UT_ESYNTAX syntax error * UT_EUNKNOWN unknown specification */ static int DecodeUnit(spec, unit) char *spec; utUnit *unit; { char specbuf[512]; /* Remove leading whitespace */ while (isspace(*spec)) ++spec; (void) strncpy(specbuf, spec, sizeof(specbuf)); specbuf[sizeof(specbuf)-1] = 0; /* Remove trailing whitespace */ { char *cp; for (cp = specbuf + strlen(specbuf); cp > specbuf && isspace(cp[-1]); --cp) ; /* EMPTY */ *cp = 0; } input_ptr = input_buf = specbuf; unput_ptr = unput_buf; (void)utClear(FinalUnit = unit); UnitNotFound = 0; utrestart((FILE*)0); return utparse() == 0 ? 0 : UnitNotFound ? UT_EUNKNOWN : UT_ESYNTAX; } /* * Scan (decode) the next entry in the units file. * If the specification is for a base unit, then the returned unit-structure * is cleared except that the value of the next available exponent slot is * set to one. A base unit is the unit for a fundamental (base) physical * quantity (e.g. the unit `meter' for the base physical quantity `length'). * * This function returns: * 0 success * UT_EOF end-of-file encountered * UT_EIO I/O error * UT_ESYNTAX syntax error * UT_EUNKNOWN unknown specification * UT_EALLOC allocation failure */ static int ScanUnit(name, sizename, unit, HasPlural) char *name; /* name buffer */ size_t sizename; /* size of name buffer */ utUnit *unit; /* unit specification */ int *HasPlural; /* specification has a plural form? */ { int status = 0; /* return status = success */ for (;;) { char *cp; static char WhiteSpace[] = " \t"; ++UtLineno; if (fgets(linebuf, (int)sizeof(linebuf), UtFile) == NULL) { if (feof(UtFile)) { status = UT_EOF; } else { (void) perror("fgets()"); status = UT_EIO; } break; } if (linebuf[strlen(linebuf)-1] != '\n') { (void) fprintf(stderr, "udunits(3): Input-line longer than %lu-byte buffer\n", (unsigned long)sizeof(linebuf)); status = UT_ESYNTAX; break; } /* Truncate at comment character */ if ((cp = strchr(linebuf, '#')) != NULL) *cp = 0; /* Trim trailing whitespace */ for (cp = linebuf + strlen(linebuf); cp > linebuf; --cp) if (!isspace(cp[-1])) break; *cp = 0; /* Find first "black" character */ cp = linebuf + strspn(linebuf, WhiteSpace); if (*cp != 0) { int n = strcspn(cp, WhiteSpace); char buf[512]; assert(sizeof(buf) > strlen(linebuf)); assert((size_t)sizename > strlen(linebuf)); (void)strncpy(name, cp, n); name[n] = 0; cp += n; cp += strspn(cp, WhiteSpace); n = strcspn(cp, WhiteSpace); (void)strncpy(buf, cp, n); name[n] = 0; if (strcmp(buf, "P") == 0) { *HasPlural = 1; } else if (strcmp(buf, "S") == 0) { *HasPlural = 0; } else { (void) fprintf(stderr, "udunits(3): Invalid single/plural indicator \"%s\"\n", buf); status = UT_ESYNTAX; break; } cp += n; cp += strspn(cp, WhiteSpace); (void)strcpy(buf, cp); if (buf[0] == 0) { (void)utClear(unit); if (utSetPower(unit, NumberBaseUnits) == NULL) { (void) fprintf(stderr, "udunits(3): Couldn't set base unit #%d\n", NumberBaseUnits); status = UT_EALLOC; } else { (void)strncpy(BaseName[NumberBaseUnits], name, UT_NAMELEN-1); ++NumberBaseUnits; } } else { if ((status = DecodeUnit(buf, unit)) != 0) { (void) fprintf(stderr, "udunits(3): Couldn't decode \"%s\" definition \"%s\"\n", name, buf); } } break; } /* if not a layout line */ } /* input-line loop */ if (status != 0 && status != UT_EOF) (void) fprintf(stderr, "udunits(3): Error occurred at line %d\n", UtLineno); return status; } /* * Read and decode the entries in the units-file, adding them to the units- * table. * * This function returns: * 0 success * UT_ENOFILE no units-file * UT_ESYNTAX syntax error * UT_EUNKNOWN unknown specification * UT_EALLOC allocation failure * UT_EIO I/O error */ static int ReadUnits(path) char *path; { int status; /* return status */ if ((status = OpenUnits(path)) == 0) { for (;;) { int HasPlural; char name[512]; utUnit unit; if ((status = ScanUnit(name, sizeof(name), &unit, &HasPlural)) == UT_EOF) { status = 0; break; } else if (status == 0) { if ((status = utAdd(name, HasPlural, &unit)) != 0) { (void) fprintf(stderr, "udunits(3): Couldn't add \"%s\" to units-table\n", name); break; } } else { (void) fprintf(stderr, "udunits(3): Couldn't read units-file \"%s\"\n", path); break; } } CloseUnits(); } /* units-file opened */ return status; } /* * Initialize the units(3) package. * * This function returns: * 0 success * UT_ENOFILE no units-file * UT_ESYNTAX syntax error in units-file * UT_EUNKNOWN unknown specification in units-file * UT_EIO units-file I/O error * UT_EALLOC allocation failure */ int utInit(path) const char *path; { int status; char pathbuf[PATH_MAX+1]; if (path == NULL || path[0] == 0) { path = getenv("UDUNITS_PATH"); if (path == NULL || path[0] == 0) path = strcpy(pathbuf, UT_DEFAULT_PATH); } if (initialized && strcmp(path, UnitsFilePath) == 0) { (void) fprintf(stderr, "udunits(3): Already initialized from file \"%s\"\n", path); status = 0; } else { utTerm(); status = ReadUnits(path); if (status == 0) { (void) strncpy(UnitsFilePath, path, sizeof(UnitsFilePath)-1); initialized = 1; if (utScan("second", &StdTimeUnit) == 0) { int iquan; int seen_one = 0; for (iquan = 0; iquan < UT_MAXNUM_BASE_QUANTITIES; ++iquan) { if (StdTimeUnit.power[iquan] != 0) { if (seen_one) break; seen_one = 1; } } HaveStdTimeUnit = seen_one && iquan == UT_MAXNUM_BASE_QUANTITIES; } } else { utTerm(); } } /* (void) fprintf(stderr, "utInit(): returning %d\n", status); */ return status; } /* * FORTRAN interface to the above function. */ FCALLSCFUN1(INT,utInit,UTOPEN,utopen, STRING) /* * Decode a unit specification. * * This function returns: * 0 success * UT_ENOINIT the package hasn't been initialized * UT_EUNKNOWN unknown specification * UT_ESYNTAX syntax error * UT_EINVALID NULL unit argument */ int utScan(spec, up) const char *spec; utUnit *up; { int status; /* return status = success */ if (spec == NULL) { status = UT_EUNKNOWN; } else { if (up == NULL) { status = UT_EINVALID; } else { if (!initialized) { (void) fprintf(stderr, "udunits(3): Package hasn't been initialized\n"); status = UT_ENOINIT; } else { utLexReset(); status = DecodeUnit(spec, up); } } } return status; } /* * FORTRAN interface to the above function. */ FCALLSCFUN2(INT,utScan,UTDEC,utdec, STRING,PPUNIT) /* * Encode a unit-structure into a formatted unit-secification. * * This function returns: * 0 success * UT_ENOINIT the package hasn't been initialized * UT_EINVALID the unit-structure is invalid * * On error, the string argument is set to the nil-pointer. */ int utPrint(unit, s) register const utUnit *unit; char **s; { int status; if (!initialized) { (void) fprintf(stderr, "udunits(3): Package hasn't been initialized\n"); *s = NULL; status = UT_ENOINIT; } else { if (unit->factor == 0.0) { *s = NULL; status = UT_EINVALID; } else { register int iquan; register char *buf = linebuf; *buf = 0; /* Print the scale factor if it's non-unity. */ if (unit->factor != 1.0) { (void)sprintf(buf, "%.*g ", DBL_DIG, unit->factor); buf += strlen(buf); } /* Append the dimensions in terms of base quanitities. */ for (iquan = 0; iquan < UT_MAXNUM_BASE_QUANTITIES; ++iquan) { if (unit->power[iquan] != 0) { if (unit->power[iquan] == 1) { (void)sprintf(buf, "%s ", BaseName[iquan]); } else { (void)sprintf(buf, "%s%d ", BaseName[iquan], unit->power[iquan]); } buf += strlen(buf); } } /* Append the origin-shift if it exists. */ if (unit->hasorigin) { if (utIsTime(unit)) { int year, month, day; int hours; int minutes; int ndigsec; /* precision of seconds in * number of digits */ float seconds; dectime(unit->origin, &year, &month, &day, &hours, &minutes, &seconds); (void)sprintf(buf-1, "s since %d-%02d-%02d %02d:%02d ", year, month, day, hours, minutes); buf += strlen(buf); ndigsec = DBL_DIG - (int) ceil(log10(ABS(unit->origin / utencclock(0, 0, 1.0)))); ndigsec = MIN(ndigsec, DBL_DIG); if (ndigsec > 0) { int precision = MAX(0, ndigsec-2); #if 0 double integral_secs; double fractional_secs = modf(seconds, &integral_secs); if (precision <= 0) (void) sprintf(buf-1, ":%02d", (int)integral_secs); else (void) sprintf(buf-1, ":%02d.%.*f", (int)integral_secs, precision, fractional_secs); #endif (void)sprintf(buf-1, ":%0*.*f ", precision+3, precision, seconds); } (void) strcat(buf, "UTC "); } else { (void)sprintf(buf, "@ %.*g ", DBL_DIG, unit->origin); } buf += strlen(buf); } /* Remove trailing space. */ if (buf > linebuf) buf[-1] = 0; *s = linebuf; status = 0; } /* unit-structure is valid */ } /* package is initialized */ return status; } /* * FORTRAN helper function for the above. */ static int utPrint_help(unit, buf, size) utUnit *unit; char *buf; size_t size; { char *s; int status = utPrint(unit, &s); if (status == 0) { size_t len = strlen(s); if (len <= size) { (void) memcpy(buf, s, len); (void) memset(buf+len, ' ', size-len); } else { (void) memcpy(buf, s, size); status = UT_ENOROOM; } } return status; } /* * FORTRAN interface to the above functionality. */ FCALLSCFUN2(INT,utPrint_help,UTENC,utenc, PPUNIT,CBUF) /* * Compare two strings. * * This routine is used instead of strncmp(3) in order to obtain greater * control over the returned value. * * This function returns the index of the first, non-matching character or * the length of the strings if all characters match. */ static ptrdiff_t mystrcmp(s1, s2) register char *s1, *s2; { register char *start = s1; for (; *s1 != 0 && *s2 != 0 && *s1 == *s2; ++s1, ++s2) continue; return s1 - start; } /* * Compare two nodes in the units-table by comparing their names. * * This function returns: * <0 first argument less than second * =0 first and second arguments are equal * >0 first argument greater than second */ static int CompareNodes(node1, node2) const void *node1; const void *node2; { /* * The following 2 lines might cause lint(1) to emit warnings about * "possible pointer alignment". Thse may safely be ignored. */ register char *name1 = ((UnitEntry*)node1)->name; register char *name2 = ((UnitEntry*)node2)->name; register int status = name1[0] - name2[0]; /* quick comparison */ if (status == 0) { /* * Quick comparison failed. Perform a long comparison. */ status = mystrcmp(name1, name2); status = name1[status] - name2[status]; } return status; } /* * Compare a target and a node in the units-table by comparing their names. * Allow the target name to be the plural of the node name if appropriate. * * This function returns: * <0 first argument less than second * =0 first and second arguments are equal * >0 first argument greater than second */ static int FindNodes(node1, node2) const void *node1; const void *node2; { /* * The following 2 lines might cause lint(1) to emit warnings about * "possible pointer alignment". Thse may safely be ignored. */ register char *name1 = ((UnitEntry*)node1)->name; register char *name2 = ((UnitEntry*)node2)->name; register int status = name1[0] - name2[0]; /* quick comparison */ if (status == 0) { /* * Quick comparison failed. Perform a long comparison. */ int i = mystrcmp(name1, name2); status = name1[i] - name2[i]; if (status == 's') { /* * The following 2 lines might cause lint(1) to emit warnings about * "possible pointer alignment". Thse may safely be ignored. */ UnitEntry *n1 = (UnitEntry*)node1; UnitEntry *n2 = (UnitEntry*)node2; if (n2->HasPlural && i == n2->nchr && n2->nchr+1 == n1->nchr) status = 0; } } return status; } /* * Create a new node. * * This function returns: * !=NULL success * ==NULL failure */ static UnitEntry* CreateNode(name, HasPlural, unit) char *name; int HasPlural; utUnit *unit; { int nchr = strlen(name); UnitEntry *node = NULL; if (nchr+1 > UT_NAMELEN) { (void) fprintf(stderr, "udunits(3): The name \"%s\" is too long\n", name); } else { /* * The following line might cause lint(1) to emit a warning about * "possible pointer alignment". This may safely be ignored. */ node = (UnitEntry*)malloc(sizeof(UnitEntry)); if (node == NULL) { (void) fprintf(stderr, "udunits(3): Couldn't allocate new entry\n"); } else { if ((node->name = DUPSTR(name)) == NULL) { (void) fprintf(stderr, "udunits(3): Couldn't duplicate name\n"); (void) free((char*)node); } else { node->nchr = strlen(node->name); node->HasPlural = HasPlural; (void) utCopy(unit, &node->unit); } } } return node; } /* * Copy construct a node (C++ terminology). */ static UnitEntry* CopyCtorNode(node) const UnitEntry *node; { return CreateNode(node->name, node->HasPlural, &node->unit); } /* * Destroy a node. * * This function returns void. */ static void DestroyNode(node) UnitEntry *node; { if (node != NULL) { if (node->name != NULL) /* * The following line might cause lint(1) to emit a warning about * "possible pointer alignment". This may safely be ignored. */ (void) free((char*)node->name); (void) free((char*)node); } } /* * Assign one node to another. */ static void AssignNode(to, from) UnitEntry *to; const UnitEntry *from; { DestroyNode(to); to->name = DUPSTR(from->name); to->nchr = from->nchr; to->HasPlural = from->HasPlural; to->unit = from->unit; } /* * Add a unit-structure to the units-table. * * This function returns: * 0 success * UT_EALLOC memory allocation failure */ int utAdd(name, HasPlural, unit) char *name; int HasPlural; utUnit *unit; { int status = 0; /* return status = success */ UnitEntry *nodep = CreateNode(name, HasPlural, unit); if (nodep == NULL) status = UT_EALLOC; else { UnitEntry **found = (UnitEntry**)tsearch((void*)nodep, &root, CompareNodes); if (found == NULL) { (void) fprintf(stderr, "udunits(3): Couldn't expand units-table for unit \"%s\"\n", name); status = UT_EALLOC; DestroyNode(nodep); } else if (*found != nodep) { (void) fprintf(stderr, "udunits(3): Replacing unit \"%s\"\n", name); DestroyNode(*found); *found = nodep; } } return status; } /* * Find the entry in the units-table corresponding to a given name. * If an entry isn't found, try again using the singular form, if * appropriate. * * This function returns: * NULL entry not found; * !NULL entry found. */ static UnitEntry* FindUnit(name) char *name; { UnitEntry node; UnitEntry **found; node.name = (char*)name; node.nchr = strlen(name); /* * The following line might cause lint(1) to emit a warning about * "possible pointer alignment". This may safely be ignored. */ found = (UnitEntry**)tfind((void*)&node, &root, FindNodes); if (found == NULL) { /* * Not found. If appropriate, try again with singular form. */ if (node.nchr > 1 && node.name[node.nchr-1] == 's') { char buf[UT_NAMELEN]; assert(sizeof(buf) > node.nchr-1); node.name = strncpy(buf, name, --node.nchr); node.name[node.nchr] = 0; found = (UnitEntry**)tfind((void*)&node, &root, /* * The following line might cause lint(1) to emit a warning about * "possible pointer alignment". This may safely be ignored. */ FindNodes); /* * Ensure that a plural form is allowed. */ if (found != NULL && !(*found)->HasPlural) found = NULL; } } return found == NULL ? NULL : *found; } /* * Find the entry in a prefix-table corresponding to a possible * prefix. A linear-search of the prefix-table is performed. * An attempt is made to insure that the longest, possible, matching * entry is returned. * * NB: A binary-search of the table is not possible because, for example, * the prefix-entry for the input specification "mzmeters" (where "mz" is * a made-up prefix) would be after the entry "micro", but the prefix-entry * corresponding to the specification "mm" (i.e. "m") lies before "micro". * Thus, the binary-search comparison-function can't indicate which direction * to go. * * This function returns: * NULL not found * !NULL found */ static PrefixEntry* FindPrefix(spec) char *spec; { PrefixEntry *found = NULL; register PrefixEntry *entry; for (entry = PrefixTable; entry->name != NULL; ++entry) { register int status; if (entry->name[0] - spec[0] < 0 || (status = strncmp(entry->name, spec, entry->nchar)) < 0) continue; if (status > 0) break; if (found == NULL || found->nchar < entry->nchar) found = entry; } return found; } /* * Return the unit-structure corresponding to a unit-specification. * * NB: * It is permissible for the specification to consist solely * of a prefix (e.g "milli"). * * An empty specification returns a dimensionless, unity unit-structure. * * On failure, the output unit-structure is unmodified. * * This function returns: * 0 found (the output unit-structure is set). * UT_ENOINIT the units-table hasn't been initialized * UT_EUNKNOWN not found */ int utFind(spec, up) char *spec; utUnit *up; { int status = 0; /* return status = found */ UnitEntry *entry = NULL; double factor = 1; if (root == NULL) { (void) fprintf(stderr, "udunits(3): Units-table is empty\n"); status = UT_ENOINIT; } else { while (*spec != 0) { PrefixEntry *PrefixEnt; /* * See if the specification is an isolated unit (e.g. "meter"). * We're done if it is. */ if ((entry = FindUnit(spec)) != NULL) break; /* * See if the specification has a multiplying prefix. If * so, then use the prefix's dimensionless value, skip * over the prefix characters, and rescan. */ if ((PrefixEnt = FindPrefix(spec)) != NULL) { factor *= PrefixEnt->factor; spec += strlen(PrefixEnt->name); continue; } status = UT_EUNKNOWN; break; } /* while something to decode */ } /* units-table is initialized */ if (status == 0) (void)utScale(entry == NULL ? utClear(up) : &entry->unit, factor, up); return status; } /* * Compute the conversion factor between two unit-structures. * * This function returns: * 0 success. * UT_ENOINIT the units-table hasn't been initialized * UT_EINVALID a structure is invalid * UT_ECONVERT the structures are not convertable */ int utConvert(from, to, slope, intercept) const utUnit *from; const utUnit *to; double *slope, *intercept; { int status = 0; if (!initialized) { (void) fprintf(stderr, "udunits(3): Package hasn't been initialized\n"); status = UT_ENOINIT; } else { if (from->factor == 0.0 || to->factor == 0.0) { status = UT_EINVALID; } else { register int iquan; for (iquan = 0; iquan < UT_MAXNUM_BASE_QUANTITIES; ++iquan) if (from->power[iquan] != to->power[iquan]) { status = UT_ECONVERT; break; } if (status == 0) { /* * Allow convertions between units with origins (e.g. * Celsius) and those without (e.g. Kelvin) by using * the fact that those without origins have a zero * origin value. */ *slope = from->factor / to->factor; *intercept = (from->origin - to->origin) / to->factor; } } } /* package is initialized */ return status; } static int f_utConvert(from, to, slope, intercept) const utUnit *from; const utUnit *to; DOUBLE_PRECISION *slope; DOUBLE_PRECISION *intercept; { double tmpSlope; double tmpIntercept; int status; status = utConvert(from, to, &tmpSlope, &tmpIntercept); *slope = tmpSlope; *intercept = tmpIntercept; return status; } /* * FORTRAN interface to the above functionality. */ FCALLSCFUN4(INT,f_utConvert,UTCVT,utcvt, PPUNIT,PPUNIT,PDOUBLE,PDOUBLE) /* * Convert a Gregorian/Julian date and time into a temporal value. * * Returns: * 0 success * UT_EINVALID not a unit of time * UT_ENOINIT the units-table hasn't been initialized */ int utInvCalendar(year, month, day, hour, minute, second, unit, value) int year; int month; int day; int hour; int minute; double second; utUnit *unit; double *value; { int status; if (!utIsTime(unit) || !unit->hasorigin) { status = UT_EINVALID; } else { *value = (utencdate(year, month, day) + utencclock(hour, minute, second) - unit->origin) / unit->factor; status = 0; } return status; } static int f_utInvCalendar(year, month, day, hour, minute, second, unit, value) int year; int month; int day; int hour; int minute; double second; utUnit *unit; DOUBLE_PRECISION *value; { double tmpValue; int status = utInvCalendar(year, month, day, hour, minute, second, unit, &tmpValue); *value = tmpValue; return status; } /* * FORTRAN interface to the above function. */ FCALLSCFUN8(INT,f_utInvCalendar,UTICALTIME,uticaltime, INT,INT,INT,INT, INT,FLOAT,PPUNIT,PDOUBLE) /* * Convert a temporal value into UTC Gregorian/Julian date and time. * * Returns: * 0: success * UT_EINVALID: not a unit of time */ int utCalendar(value, unit, year, month, day, hour, minute, second) double value; utUnit *unit; int *year; int *month; int *day; int *hour; int *minute; float *second; { int status; float sec; if (!utIsTime(unit) || !unit->hasorigin) { status = UT_EINVALID; } else { dectime(unit->origin + value*unit->factor, year, month, day, hour, minute, &sec); *second = sec; status = 0; } return status; } /* * FORTRAN interface to the above function. */ FCALLSCFUN8(INT,utCalendar,UTCALTIME,utcaltime, DOUBLE,PPUNIT,PINT,PINT,PINT,PINT,PINT,PFLOAT) /* * Create a unit structure. */ utUnit* utNew() { return utClear((utUnit*)malloc(sizeof(utUnit))); } /* * FORTRAN interface to the above functionality. */ FCALLSCFUN0(PUNIT,utNew,UTMAKE,utmake) /* * Destroy a unit structure that was created by utNew(). */ void utDestroy(unit) utUnit *unit; { if (unit != NULL) free((voidp)unit); } /* * FORTRAN interface to the above functionality. */ FCALLSCSUB1(utDestroy,UTFREE,utfree, PPUNIT) /* * Free allocated nodes. */ static void NodeDeleter(node, order, level) const void *node; VISIT order; /*ARGSUSED*/ int level; { if (order == leaf || order == endorder) { UnitEntry *entry = *(UnitEntry**)node; (void)tdelete(entry, &root, CompareNodes); DestroyNode(entry); } } /* * Terminate use of this package. * * This function returns void. */ void utTerm() { if (root != NULL) { twalk(root, NodeDeleter); root = NULL; } initialized = 0; NumberBaseUnits = 0; UnitsFilePath[0] = 0; HaveStdTimeUnit = 0; } /* * FORTRAN interface to the above functionality. */ FCALLSCSUB0(utTerm,UTCLS,utcls) /* * Return the next character to the scanner. */ int utinput() { return unput_buf < unput_ptr ? *--unput_ptr : *input_ptr == 0 ? EOF : *input_ptr++; } /* * Save the given character to be returned by the next call to utinput(). */ void utunput(c) int c; { *unput_ptr++ = c; } /* * LEX "wrap-up" routine. Indicate no more input. */ int utwrap() { return 1; } /* * YACC error routine: */ void uterror(s) char *s; { register int i; (void) fprintf(stderr, "udunits(3): %s:\n", s); (void) fputs(input_buf, stderr); (void) putc('\n', stderr); for (i = 0; i < input_ptr - input_buf; ++i) (void) putc(' ', stderr); (void) fputs("^\n", stderr); }