netcdf-c/udunits/lib/udunits-1.c
2011-02-17 12:09:59 +00:00

659 lines
12 KiB
C

/*
* Copyright 2008, 2009 University Corporation for Atmospheric Research
*
* This file is part of the UDUNITS-2 package. See the file LICENSE
* in the top-level source-directory of the package for copying and
* redistribution conditions.
*/
#include <errno.h>
#include <search.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "udunits.h"
static ut_system* unitSystem = NULL;
static ut_unit* encodedTimeUnit = NULL;
static ut_unit* second = NULL;
static char* buffer;
static int buflen = 80;
static void* unit2s = NULL;
/*
* Initialize the units(3) package.
*/
int
utInit(
const char *path)
{
int status;
(void)ut_set_error_message_handler(ut_ignore);
if (unitSystem != NULL) {
ut_free_system(unitSystem);
unitSystem = NULL;
}
unitSystem = ut_read_xml(NULL);
if (unitSystem == NULL) {
status = ut_get_status() == UT_PARSE
? UT_ESYNTAX
: UT_EIO;
}
else {
second = ut_get_unit_by_name(unitSystem, "second");
encodedTimeUnit =
ut_offset_by_time(second, ut_encode_time(2001, 1, 1, 0, 0, 0.0));
buffer = malloc(buflen);
if (buffer == NULL) {
buflen = 0;
status = UT_EALLOC;
}
else {
status = 0;
}
}
return status;
}
/*
* Indicate if the units(3) package has been initialized.
*/
int
utIsInit()
{
return unitSystem != NULL;
}
static int
compare(
const void* key1,
const void* key2)
{
return key1 < key2 ? -1 : key1 == key2 ? 0 : 1;
}
static void
freeIfAllocated(
utUnit* const unit)
{
if (tdelete(unit->unit2, &unit2s, compare) != NULL) {
ut_free(unit->unit2);
}
unit->unit2 = NULL;
}
void
utFree(
utUnit* const unit)
{
freeIfAllocated(unit);
}
void
utIni(
utUnit* const unit)
{
if (unit != NULL) {
unit->unit2 = NULL;
}
}
static int
setUnit(
utUnit* const unit,
ut_unit* const unit2)
{
int status;
if (tsearch(unit2, &unit2s, compare) == NULL) {
status = UT_EALLOC;
}
else {
freeIfAllocated(unit);
unit->unit2 = unit2;
status = 0;
}
return status;
}
/*
* Decode a formatted unit specification into a unit-structure.
*/
int
utScan(
const char *spec,
utUnit *unit)
{
int status;
if (spec == NULL || unit == NULL) {
status = UT_EINVALID;
}
else {
ut_unit* ut_unit = ut_parse(unitSystem, spec, UT_ASCII);
if (ut_unit != NULL) {
status = setUnit(unit, ut_unit);
}
else {
status = ut_get_status();
if (status == UT_BAD_ARG) {
status = unitSystem == NULL
? UT_ENOINIT
: UT_EINVALID;
}
else if (status == UT_SYNTAX) {
status = UT_ESYNTAX;
}
else if (status == UT_UNKNOWN) {
status = UT_EUNKNOWN;
}
else {
status = UT_EALLOC;
}
}
}
return status;
}
/*
* Convert a temporal value into a UTC Gregorian date and time.
*/
int
utCalendar(
double value,
const utUnit *unit,
int *year,
int *month,
int *day,
int *hour,
int *minute,
float *second)
{
int status = 0; /* success */
cv_converter* converter = ut_get_converter(unit->unit2, encodedTimeUnit);
if (converter == NULL) {
status = encodedTimeUnit == NULL ? UT_ENOINIT : UT_EINVALID;
}
else {
double encodedTime = cv_convert_double(converter, value);
double sec, res;
ut_decode_time(encodedTime, year, month, day, hour, minute, &sec, &res);
*second = (float)sec;
cv_free(converter);
}
return status;
}
/*
* Convert a date into a temporal value. The date is assumed to
* use the Gregorian calendar if on or after noon, October 15, 1582;
* otherwise, the date is assumed to use the Julian calendar.
*/
int
utInvCalendar(
int year,
int month,
int day,
int hour,
int minute,
double second,
const utUnit *unit,
double *value)
{
int status = 0; /* success */
cv_converter* converter = ut_get_converter(encodedTimeUnit, unit->unit2);
if (converter == NULL) {
status = encodedTimeUnit == NULL ? UT_ENOINIT : UT_EINVALID;
}
else {
double encodedTime =
ut_encode_time(year, month, day, hour, minute, second);
*value = cv_convert_double(converter, encodedTime);
cv_free(converter);
}
return status;
}
static ut_status
isTimeVisitBasic(
const ut_unit* unit,
void* arg)
{
return (ut_status)ut_are_convertible(unit, second);
}
static ut_status
isTimeVisitProduct(
const ut_unit* unit,
int count,
const ut_unit* const* basicUnits,
const int* powers,
void* arg)
{
int isTime;
if (!ut_are_convertible(unit, second)) {
isTime = 0;
}
else {
int i;
isTime = 0;
for (i = 0; i < count; i++) {
if (ut_are_convertible(basicUnits[i], second) && powers[i] == 1) {
isTime = 1;
break;
}
}
}
return (ut_status)isTime;
}
static ut_status
isTimeVisitGalilean(
const ut_unit* unit,
double scale,
const ut_unit* underlyingUnit,
double origin,
void* arg)
{
return (ut_status)0;
}
static ut_status
isTimeVisitTimestamp(
const ut_unit* unit,
const ut_unit* timeUnit,
double origin,
void* arg)
{
return (ut_status)1;
}
static ut_status
isTimeVisitLogarithmic(
const ut_unit* unit,
double base,
const ut_unit* reference,
void* arg)
{
return (ut_status)0;
}
/*
* Indicate if a unit structure refers to a unit of time.
*/
int
utIsTime(
const utUnit *up)
{
ut_visitor visitor;
visitor.visit_basic = isTimeVisitBasic;
visitor.visit_product = isTimeVisitProduct;
visitor.visit_galilean = isTimeVisitGalilean;
visitor.visit_timestamp = isTimeVisitTimestamp;
visitor.visit_logarithmic = isTimeVisitLogarithmic;
return ut_accept_visitor(up->unit2, &visitor, NULL);
}
static ut_status
hasOriginVisitBasic(
const ut_unit* unit,
void* arg)
{
return (ut_status)0;
}
static ut_status
hasOriginVisitProduct(
const ut_unit* unit,
int count,
const ut_unit* const* basicUnits,
const int* powers,
void* arg)
{
return (ut_status)0;
}
static ut_status
hasOriginVisitGalilean(
const ut_unit* unit,
double scale,
const ut_unit* underlyingUnit,
double origin,
void* arg)
{
return (ut_status)1;
}
static ut_status
hasOriginVisitTimestamp(
const ut_unit* unit,
const ut_unit* timeUnit,
double origin,
void* arg)
{
return (ut_status)1;
}
static ut_status
hasOriginVisitLogarithmic(
const ut_unit* unit,
double base,
const ut_unit* reference,
void* arg)
{
return (ut_status)0;
}
/*
* Indicate if a unit structure has an origin.
*/
int
utHasOrigin(
const utUnit *up)
{
ut_visitor visitor;
visitor.visit_basic = hasOriginVisitBasic;
visitor.visit_product = hasOriginVisitProduct;
visitor.visit_galilean = hasOriginVisitGalilean;
visitor.visit_timestamp = hasOriginVisitTimestamp;
visitor.visit_logarithmic = hasOriginVisitLogarithmic;
return ut_accept_visitor(up->unit2, &visitor, NULL);
}
static utUnit*
resultingUnit(
utUnit* result,
ut_unit* const unit2)
{
if (unit2 == NULL) {
result = NULL;
}
else if (result != NULL) {
if (setUnit(result, unit2) != 0) {
result == NULL;
}
}
return result;
}
/*
* Clear a unit structure.
*/
utUnit*
utClear(
utUnit *unit)
{
return resultingUnit(unit, ut_get_dimensionless_unit_one(unitSystem));
}
/*
* Copy a unit-structure.
*/
utUnit*
utCopy(
const utUnit *source,
utUnit *dest)
{
return source == NULL
? NULL
: dest == NULL
? NULL
: resultingUnit(dest, ut_clone(source->unit2));
}
/*
* Multiply one unit-structure by another.
*/
utUnit*
utMultiply(
const utUnit *term1,
const utUnit *term2,
utUnit *result)
{
return term1 == NULL || term2 == NULL
? NULL
: resultingUnit(result, ut_multiply(term1->unit2, term2->unit2));
}
/*
* Divide one unit-structure by another.
*/
utUnit*
utDivide(
const utUnit *numer,
const utUnit *denom,
utUnit *result)
{
return numer == NULL || denom == NULL
? NULL
: resultingUnit(result, ut_divide(numer->unit2, denom->unit2));
}
/*
* Form the reciprocal of a unit-structure.
*/
utUnit*
utInvert(
const utUnit *unit,
utUnit *result)
{
return unit == NULL ? NULL : resultingUnit(result, ut_invert(unit->unit2));
}
/*
* Raise a unit-structure to a power.
*/
utUnit*
utRaise(
const utUnit *unit,
int power,
utUnit *result)
{
return unit == NULL
? NULL
: resultingUnit(result, ut_raise(unit->unit2, power));
}
/*
* Shift the origin of a unit-structure by an arithmetic amount.
*/
utUnit*
utShift(
const utUnit *unit,
double amount,
utUnit *result)
{
return unit == NULL
? NULL
: resultingUnit(result, ut_offset(unit->unit2, amount));
}
/*
* Scale a unit-structure.
*/
utUnit*
utScale(
const utUnit *unit,
double factor,
utUnit *result)
{
return unit == NULL
? NULL
: resultingUnit(result, ut_scale(factor, unit->unit2));
}
/*
* Compute the conversion factor between two unit-structures.
*/
int
utConvert(
const utUnit *from,
const utUnit *to,
double *slope,
double *intercept)
{
int status;
cv_converter* converter = ut_get_converter(from->unit2, to->unit2);
if (converter == NULL) {
status = ut_get_status();
if (status == UT_BAD_ARG) {
status = UT_EINVALID;
}
else if (status == UT_NOT_SAME_SYSTEM) {
status = UT_ENOINIT;
}
else if (status == UT_MEANINGLESS) {
status = UT_ECONVERT;
}
else {
status = UT_EALLOC;
}
}
else {
*intercept = cv_convert_double(converter, 0.0);
*slope = cv_convert_double(converter, 1.0) - *intercept;
status = 0;
}
return status;
}
/*
* Encode a unit-structure into a formatted unit-specification.
*/
int
utPrint(
const utUnit *unit,
char **buf)
{
int status;
for (;;) {
int len = ut_format(unit->unit2, buffer, buflen, UT_ASCII);
if (len == -1) {
status = ut_get_status();
if (status == UT_BAD_ARG) {
status = UT_EINVALID;
}
else {
status = UT_EALLOC;
}
break;
}
if (len < buflen) {
*buf = buffer;
status = 0;
break;
}
else {
int newLen = buflen * 2;
char* newBuf = malloc(newLen);
if (newBuf == NULL) {
status = UT_EALLOC;
break;
}
buffer = newBuf;
buflen = newLen;
}
}
return status;
}
/*
* Add a unit to the units database.
*/
int
utAdd(
char *name,
int hasPlural,
const utUnit *unit)
{
int status = ut_map_name_to_unit(name, UT_ASCII, unit->unit2);
if (status == UT_SUCCESS) {
status = ut_map_unit_to_name(unit->unit2, name, UT_ASCII);
if (status == UT_SUCCESS) {
if (!hasPlural) {
status = UT_SUCCESS;
}
else {
extern const char* ut_form_plural(const char*);
const char* plural = ut_form_plural(name);
status = ut_map_name_to_unit(plural, UT_ASCII, unit->unit2);
} /* unit has plural name */
if (status != UT_SUCCESS) {
(void)ut_unmap_unit_to_name(unit->unit2, UT_ASCII);
}
} /* unit mapped to name */
if (status != UT_SUCCESS) {
(void)ut_unmap_name_to_unit(unitSystem, name, UT_ASCII);
}
} /* singular name mapped to unit */
return status == UT_SUCCESS
? 0
: status == UT_EXISTS
? UT_DUP
: UT_EALLOC;
}
/*
* Return the unit corresponding to a unit-specification.
*
*/
int
utFind(
char *spec,
utUnit *up)
{
int status;
ut_unit* unit = ut_parse(unitSystem, spec, UT_ASCII);
if (unit == NULL) {
status = ut_get_status();
if (status == UT_BAD_ARG) {
status = UT_EINVALID;
}
else if (status == UT_SYNTAX) {
status = UT_ESYNTAX;
}
else if (status == UT_UNKNOWN) {
status = UT_EUNKNOWN;
}
else if (status == UT_OS) {
status = UT_EALLOC;
}
}
else {
status = setUnit(up, unit);
}
return status;
}
/*
* Terminate use of this package.
*/
void
utTerm()
{
ut_free(second);
second = NULL;
ut_free(encodedTimeUnit);
encodedTimeUnit = NULL;
ut_free_system(unitSystem);
unitSystem = NULL;
}