netcdf-c/udunits/lib/idToUnitMap.c
2010-06-03 13:24:43 +00:00

554 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.
*/
/*
* Identifier-to-unit map.
*/
/*LINTLIBRARY*/
#ifndef _XOPEN_SOURCE
# define _XOPEN_SOURCE 500
#endif
#include <assert.h>
#include <search.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include "udunits2.h"
#include "unitAndId.h"
#include "systemMap.h"
typedef struct {
int (*compare)(const void*, const void*);
void* tree;
} IdToUnitMap;
static SystemMap* systemToNameToUnit;
static SystemMap* systemToSymbolToUnit;
static int
sensitiveCompare(
const void* const node1,
const void* const node2)
{
return strcmp(((const UnitAndId*)node1)->id,
((const UnitAndId*)node2)->id);
}
static int
insensitiveCompare(
const void* const node1,
const void* const node2)
{
return strcasecmp(((const UnitAndId*)node1)->id,
((const UnitAndId*)node2)->id);
}
static IdToUnitMap*
itumNew(
int (*compare)(const void*, const void*))
{
IdToUnitMap* map = (IdToUnitMap*)malloc(sizeof(IdToUnitMap));
if (map != NULL) {
map->tree = NULL;
map->compare = compare;
}
return map;
}
/*
* Frees an identifier-to-unit map. All entries are freed.
*
* Arguments:
* map Pointer to the identifier-to-unit map.
* Returns:
*/
static void
itumFree(
IdToUnitMap* map)
{
if (map != NULL) {
while (map->tree != NULL) {
UnitAndId* uai = *(UnitAndId**)map->tree;
(void)tdelete(uai, &map->tree, map->compare);
uaiFree(uai);
}
free(map);
} /* valid arguments */
}
/*
* Adds an entry to an identifier-to-unit map.
*
* Arguments:
* map The database.
* id The identifier. May be freed upon return.
* unit The unit. May be freed upon return.
* Returns:
* UT_OS Operating-system error. See "errno".
* UT_EXISTS "id" already maps to a different unit.
* UT_SUCCESS Success.
*/
static ut_status
itumAdd(
IdToUnitMap* map,
const char* const id,
const ut_unit* const unit)
{
ut_status status;
UnitAndId* targetEntry;
assert(map != NULL);
assert(id != NULL);
assert(unit != NULL);
targetEntry = uaiNew(unit, id);
if (targetEntry != NULL) {
UnitAndId** treeEntry = tsearch(targetEntry, &map->tree,
map->compare);
if (treeEntry == NULL) {
uaiFree(targetEntry);
status = UT_OS;
}
else {
if (ut_compare((*treeEntry)->unit, unit) == 0) {
status = UT_SUCCESS;
}
else {
status = UT_EXISTS;
ut_set_status(status);
ut_handle_error_message(
"\"%s\" already maps to existing but different unit", id);
}
if (targetEntry != *treeEntry)
uaiFree(targetEntry);
} /* found entry */
} /* "targetEntry" allocated */
return status;
}
/*
* Removes an entry to an identifier-to-unit map.
*
* Arguments:
* map The database.
* id The identifier. May be freed upon return.
* Returns:
* UT_SUCCESS Success.
*/
static ut_status
itumRemove(
IdToUnitMap* map,
const char* const id)
{
UnitAndId targetEntry;
UnitAndId** treeEntry;
assert(map != NULL);
assert(id != NULL);
targetEntry.id = (char*)id;
treeEntry = tfind(&targetEntry, &map->tree, map->compare);
if (treeEntry != NULL) {
UnitAndId* uai = *treeEntry;
(void)tdelete(uai, &map->tree, map->compare);
uaiFree(uai);
}
return UT_SUCCESS;
}
/*
* Finds the entry in an identifier-to-unit map that corresponds to an
* identifer.
*
* Arguments:
* map The identifier-to-unit map.
* id The identifier to be used as the key in the search.
* Returns:
* NULL Failure. "map" doesn't contain an entry that corresponds
* to "id".
* else Pointer to the entry corresponding to "id".
*/
static const UnitAndId*
itumFind(
IdToUnitMap* map,
const char* const id)
{
UnitAndId* entry = NULL; /* failure */
UnitAndId targetEntry;
UnitAndId** treeEntry;
assert(map != NULL);
assert(id != NULL);
targetEntry.id = (char*)id;
treeEntry = tfind(&targetEntry, &map->tree, map->compare);
if (treeEntry != NULL)
entry = *treeEntry;
return entry;
}
/*
* Adds to a particular unit-system a mapping from an identifier to a unit.
*
* Arguments:
* systemMap Address of the pointer to the system-map.
* id Pointer to the identifier. May be freed upon return.
* unit Pointer to the unit. May be freed upon return.
* compare Pointer to comparison function for unit-identifiers.
* Returns:
* UT_BAD_ARG "id" is NULL or "unit" is NULL.
* UT_OS Operating-sytem failure. See "errno".
* UT_SUCCESS Success.
*/
static ut_status
mapIdToUnit(
SystemMap** const systemMap,
const char* const id,
const ut_unit* const unit,
int (*compare)(const void*, const void*))
{
ut_status status = UT_SUCCESS;
if (id == NULL) {
status = UT_BAD_ARG;
}
else if (unit == NULL) {
status = UT_BAD_ARG;
}
else {
ut_system* system = ut_get_system(unit);
if (*systemMap == NULL) {
*systemMap = smNew();
if (*systemMap == NULL)
status = UT_OS;
}
if (*systemMap != NULL) {
IdToUnitMap** const idToUnit =
(IdToUnitMap**)smSearch(*systemMap, system);
if (idToUnit == NULL) {
status = UT_OS;
}
else {
if (*idToUnit == NULL) {
*idToUnit = itumNew(compare);
if (*idToUnit == NULL)
status = UT_OS;
}
if (*idToUnit != NULL)
status = itumAdd(*idToUnit, id, unit);
} /* have system-map entry */
} /* have system-map */
} /* valid arguments */
return status;
}
/*
* Removes the mapping from an identifier to a unit.
*
* Arguments:
* systemMap Address of the pointer to the system-map.
* id Pointer to the identifier. May be freed upon return.
* system Pointer to the unit-system associated with the mapping.
* Returns:
* UT_BAD_ARG "id" is NULL, "system" is NULL, or "compare" is NULL.
* UT_SUCCESS Success.
*/
static ut_status
unmapId(
SystemMap* const systemMap,
const char* const id,
ut_system* system)
{
ut_status status;
if (systemMap == NULL || id == NULL || system == NULL) {
status = UT_BAD_ARG;
}
else {
IdToUnitMap** const idToUnit =
(IdToUnitMap**)smFind(systemMap, system);
status =
(idToUnit == NULL || *idToUnit == NULL)
? UT_SUCCESS
: itumRemove(*idToUnit, id);
} /* valid arguments */
return status;
}
/*
* Adds a mapping from a name to a unit.
*
* Arguments:
* name Pointer to the name to be mapped to "unit". May be
* freed upon return.
* encoding The character encoding of "name".
* unit Pointer to the unit to be mapped-to by "name". May be
* freed upon return.
* Returns:
* UT_BAD_ARG "name" or "unit" is NULL.
* UT_OS Operating-system error. See "errno".
* UT_EXISTS "name" already maps to a different unit.
* UT_SUCCESS Success.
*/
ut_status
ut_map_name_to_unit(
const char* const name,
const ut_encoding encoding,
const ut_unit* const unit)
{
ut_set_status(
mapIdToUnit(&systemToNameToUnit, name, unit, insensitiveCompare));
return ut_get_status();
}
/*
* Removes a mapping from a name to a unit. After this function,
* ut_get_unit_by_name(system,name) will no longer return a unit.
*
* Arguments:
* system The unit-system to which the unit belongs.
* name The name of the unit.
* encoding The character encoding of "name".
* Returns:
* UT_SUCCESS Success.
* UT_BAD_ARG "system" or "name" is NULL.
*/
ut_status
ut_unmap_name_to_unit(
ut_system* system,
const char* const name,
const ut_encoding encoding)
{
ut_set_status(unmapId(systemToNameToUnit, name, system));
return ut_get_status();
}
/*
* Adds a mapping from a symbol to a unit.
*
* Arguments:
* symbol Pointer to the symbol to be mapped to "unit". May be
* freed upon return.
* encoding The character encoding of "symbol".
* unit Pointer to the unit to be mapped-to by "symbol". May
* be freed upon return.
* Returns:
* UT_BAD_ARG "symbol" or "unit" is NULL.
* UT_OS Operating-system error. See "errno".
* UT_EXISTS "symbol" already maps to a different unit.
* UT_SUCCESS Success.
*/
ut_status
ut_map_symbol_to_unit(
const char* const symbol,
const ut_encoding encoding,
const ut_unit* const unit)
{
ut_set_status(
mapIdToUnit(&systemToSymbolToUnit, symbol, unit, sensitiveCompare));
return ut_get_status();
}
/*
* Removes a mapping from a symbol to a unit. After this function,
* ut_get_unit_by_symbol(system,symbol) will no longer return a unit.
*
* Arguments:
* system The unit-system to which the unit belongs.
* symbol The symbol of the unit.
* encoding The character encoding of "symbol".
* Returns:
* UT_SUCCESS Success.
* UT_BAD_ARG "system" or "symbol" is NULL.
*/
ut_status
ut_unmap_symbol_to_unit(
ut_system* system,
const char* const symbol,
const ut_encoding encoding)
{
ut_set_status(unmapId(systemToSymbolToUnit, symbol, system));
return ut_get_status();
}
/*
* Returns the unit to which an identifier maps in a particular unit-system.
*
* Arguments:
* systemMap NULL or pointer to the system-map. If NULL, then
* NULL will be returned.
* system Pointer to the unit-system.
* id Pointer to the identifier.
* Returns:
* NULL Failure. "ut_get_status()" will be:
* UT_BAD_ARG "system" is NULL or "id" is NULL.
* else Pointer to the unit in "system" with the identifier "id".
* Should be passed to ut_free() when no longer needed.
*/
static ut_unit*
getUnitById(
const SystemMap* const systemMap,
const ut_system* const system,
const char* const id)
{
ut_unit* unit = NULL; /* failure */
if (system == NULL) {
ut_set_status(UT_BAD_ARG);
ut_handle_error_message("getUnitById(): NULL unit-system argument");
}
else if (id == NULL) {
ut_set_status(UT_BAD_ARG);
ut_handle_error_message("getUnitById(): NULL identifier argument");
}
else if (systemMap != NULL) {
IdToUnitMap** const idToUnit =
(IdToUnitMap**)smFind(systemMap, system);
if (idToUnit != NULL) {
const UnitAndId* uai = itumFind(*idToUnit, id);
if (uai != NULL)
unit = ut_clone(uai->unit);
}
} /* valid arguments */
return unit;
}
/*
* Returns the unit with a given name from a unit-system. Name comparisons
* are case-insensitive.
*
* Arguments:
* system Pointer to the unit-system.
* name Pointer to the name of the unit to be returned.
* Returns:
* NULL Failure. "ut_get_status()" will be
* UT_SUCCESS "name" doesn't map to a unit of
* "system".
* UT_BAD_ARG "system" or "name" is NULL.
* else Pointer to the unit of the unit-system with the given name.
* The pointer should be passed to ut_free() when the unit is
* no longer needed.
*/
ut_unit*
ut_get_unit_by_name(
const ut_system* const system,
const char* const name)
{
ut_set_status(UT_SUCCESS);
return getUnitById(systemToNameToUnit, system, name);
}
/*
* Returns the unit with a given symbol from a unit-system. Symbol
* comparisons are case-sensitive.
*
* Arguments:
* system Pointer to the unit-system.
* symbol Pointer to the symbol associated with the unit to be
* returned.
* Returns:
* NULL Failure. "ut_get_status()" will be
* UT_SUCCESS "symbol" doesn't map to a unit of
* "system".
* UT_BAD_ARG "system" or "symbol" is NULL.
* else Pointer to the unit in the unit-system with the given symbol.
* The pointer should be passed to ut_free() when the unit is no
* longer needed.
*/
ut_unit*
ut_get_unit_by_symbol(
const ut_system* const system,
const char* const symbol)
{
ut_set_status(UT_SUCCESS);
return getUnitById(systemToSymbolToUnit, system, symbol);
}
/*
* Frees resources associated with a unit-system.
*
* Arguments:
* system Pointer to the unit-system to have its associated
* resources freed.
*/
void
itumFreeSystem(
ut_system* system)
{
if (system != NULL) {
SystemMap* systemMaps[2];
int i;
systemMaps[0] = systemToNameToUnit;
systemMaps[1] = systemToSymbolToUnit;
for (i = 0; i < 2; i++) {
if (systemMaps[i] != NULL) {
IdToUnitMap** const idToUnit =
(IdToUnitMap**)smFind(systemMaps[i], system);
if (idToUnit != NULL)
itumFree(*idToUnit);
smRemove(systemMaps[i], system);
}
}
} /* valid arguments */
}