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

513 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.
*/
/*
* Module for handling unit prefixes -- both names and symbols.
*/
/*LINTLIBRARY*/
#ifndef _XOPEN_SOURCE
# define _XOPEN_SOURCE 500
#endif
#include <ctype.h>
#include <errno.h>
#include <search.h>
#include <stdlib.h>
#include <string.h>
#include "udunits2.h"
#include "systemMap.h"
typedef struct {
void* tree;
int (*compare)(const void*, const void*);
} PrefixToValueMap;
typedef struct {
void* nextTree;
double value;
size_t position; /* origin-0 index of character in prefix */
int character;
} PrefixSearchEntry;
static SystemMap* systemToNameToValue = NULL;
static SystemMap* systemToSymbolToValue = NULL;
/******************************************************************************
* Prefix Search Entry:
******************************************************************************/
static PrefixSearchEntry*
pseNew(
int character,
size_t position)
{
PrefixSearchEntry* entry = malloc(sizeof(PrefixSearchEntry));
if (entry == NULL) {
ut_set_status(UT_OS);
ut_handle_error_message(strerror(errno));
ut_handle_error_message(
"Couldn't allocate %lu-byte prefix-search-entry",
sizeof(PrefixSearchEntry));
}
else {
entry->character = character;
entry->position = position;
entry->value = 0;
entry->nextTree = NULL;
}
return entry;
}
static void
pseFree(
PrefixSearchEntry* const entry)
{
free(entry);
}
static int
pseSensitiveCompare(
const void* const entry1,
const void* const entry2)
{
int char1 = ((const PrefixSearchEntry*)entry1)->character;
int char2 = ((const PrefixSearchEntry*)entry2)->character;
return char1 < char2 ? -1 : char1 == char2 ? 0 : 1;
}
static int
pseInsensitiveCompare(
const void* const entry1,
const void* const entry2)
{
int char1 = tolower(((const PrefixSearchEntry*)entry1)->character);
int char2 = tolower(((const PrefixSearchEntry*)entry2)->character);
return char1 < char2 ? -1 : char1 == char2 ? 0 : 1;
}
/******************************************************************************
* Prefix-to-Value Map:
******************************************************************************/
static PrefixToValueMap*
ptvmNew(
int (*compare)(const void*, const void*))
{
PrefixToValueMap* map =
(PrefixToValueMap*)malloc(sizeof(PrefixToValueMap));
if (map != NULL) {
map->tree = NULL;
map->compare = compare;
}
return map;
}
/*
* Returns the prefix search-entry that matches an identifier. Inserts a
* new prefix search-entry if no matching element is found. Note that the
* returned entry might have a different prefix value if it was previously
* inserted.
*
* Arguments:
* map Pointer to the prefix-to-value map.
* id The prefix identifier. May be freed upon return.
* value The prefix value.
* Returns:
* NULL "map" is NULL.
* NULL "id" is NULL or the empty string.
* NULL "value" is 0.
* NULL Insufficient storage space is available.
* else Pointer to the prefix-search-entry that matches "id".
*/
static const PrefixSearchEntry*
ptvmSearch(
PrefixToValueMap* map,
const char* const id,
const double value)
{
PrefixSearchEntry* entry = NULL; /* failure */
if (id != NULL && map != NULL && value != 0) {
size_t len = strlen(id);
if (len > 0) {
size_t i;
PrefixSearchEntry* const* treeEntry = NULL;
void** tree = &map->tree;
for (i = 0; i < len; i++) {
PrefixSearchEntry* const newEntry = pseNew(id[i], i);
if (newEntry == NULL)
break;
treeEntry = tsearch(newEntry, tree, map->compare);
if (treeEntry == NULL) {
pseFree(newEntry);
break;
}
tree = &(*treeEntry)->nextTree; /* next binary-search tree */
if (newEntry != *treeEntry)
pseFree(newEntry);
}
if (i >= len) {
entry = *treeEntry;
if (entry->value == 0)
entry->value = value;
}
}
}
return entry;
}
/*
* Returns the prefix search-entry that matches the beginning of a string.
*
* Arguments:
* map Pointer to the prefix-to-value map.
* string Pointer to the string to be examined for a prefix.
* Returns:
* NULL "map" is NULL.
* NULL "string" is NULL or the empty string.
* NULL "value" is 0.
* else Pointer to the prefix-search-entry that matches the
* beginning of "string".
*/
static const PrefixSearchEntry*
ptvmFind(
PrefixToValueMap* const map,
const char* const string)
{
PrefixSearchEntry* entry = NULL; /* failure */
if (string != NULL && map != NULL && strlen(string) > 0) {
size_t len = strlen(string);
if (len > 0) {
size_t i;
PrefixSearchEntry* lastEntry = NULL;
void** tree = &map->tree;
for (i = 0; i < len; i++) {
PrefixSearchEntry targetEntry;
PrefixSearchEntry* const* treeEntry;
targetEntry.character = string[i];
treeEntry = tfind(&targetEntry, tree, map->compare);
if (treeEntry == NULL)
break;
lastEntry = *treeEntry;
tree = &(*treeEntry)->nextTree; /* next binary-search tree */
}
if (lastEntry != NULL && lastEntry->value != 0)
entry = lastEntry;
}
}
return entry;
}
/******************************************************************************
* Public API:
******************************************************************************/
/*
* Adds a prefix to a unit-system.
*
* Arguments:
* system Pointer to the unit-system.
* prefix Pointer to the prefix (e.g., "mega", "M"). May be freed
* upon return.
* value The value of the prefix (e.g., 1e6).
* systemMap Pointer to system-map.
* compare Prefix comparison function.
* Returns:
* UT_SUCCESS Success.
* UT_BAD_ARG "system" is NULL, "prefix" is NULL or empty, or "value"
* is 0.
* UT_EXISTS "prefix" already maps to a different value.
* UT_OS Operating-system failure. See "errno".
*/
static ut_status
addPrefix(
ut_system* const system,
const char* const prefix,
const double value,
SystemMap** const systemMap,
int (*compare)(const void*, const void*))
{
ut_status status;
if (system == NULL) {
status = UT_BAD_ARG;
}
else if (prefix == NULL || strlen(prefix) == 0) {
status = UT_BAD_ARG;
}
else if (value == 0) {
status = UT_BAD_ARG;
}
else {
if (*systemMap == NULL) {
*systemMap = smNew();
if (*systemMap == NULL)
status = UT_OS;
}
if (*systemMap != NULL) {
PrefixToValueMap** const prefixToValue =
(PrefixToValueMap**)smSearch(*systemMap, system);
if (prefixToValue == NULL) {
status = UT_OS;
}
else {
if (*prefixToValue == NULL) {
*prefixToValue = ptvmNew(compare);
if (*prefixToValue == NULL)
status = UT_OS;
}
if (*prefixToValue != NULL) {
const PrefixSearchEntry* entry =
ptvmSearch(*prefixToValue, prefix, value);
status =
entry == NULL
? UT_OS
: (entry->value == value)
? UT_SUCCESS
: UT_EXISTS;
}
} /* have system-map entry */
} /* have system-map */
} /* valid arguments */
return status;
}
/*
* Adds a name-prefix to a unit-system. A name-prefix is something like "mega"
* or "milli". Comparisons between name-prefixes are case-insensitive.
*
* Arguments:
* system Pointer to the unit-system.
* name Pointer to the name-prefix (e.g., "mega"). May be freed
* upon return.
* value The value of the prefix (e.g., 1e6).
* Returns:
* UT_SUCCESS Success.
* UT_BAD_ARG "system" or "name" is NULL, or "value" is 0.
* UT_EXISTS "name" already maps to a different value.
* UT_OS Operating-system failure. See "errno".
*/
ut_status
ut_add_name_prefix(
ut_system* const system,
const char* const name,
const double value)
{
ut_set_status(addPrefix(system, name, value, &systemToNameToValue,
pseInsensitiveCompare));
return ut_get_status();
}
/*
* Adds a symbol-prefix to a unit-system. A symbol-prefix is something like
* "M" or "y". Comparisons between symbol-prefixes are case-sensitive.
*
* Arguments:
* system Pointer to the unit-system.
* symbol Pointer to the symbol-prefix (e.g., "M"). May be freed
* upon return.
* value The value of the prefix (e.g., 1e6).
* Returns:
* UT_SUCCESS Success.
* UT_BADSYSTEM "system" or "symbol" is NULL.
* UT_BAD_ARG "value" is 0.
* UT_EXISTS "symbol" already maps to a different value.
* UT_OS Operating-system failure. See "errno".
*/
ut_status
ut_add_symbol_prefix(
ut_system* const system,
const char* const symbol,
const double value)
{
ut_set_status(addPrefix(system, symbol, value, &systemToSymbolToValue,
pseSensitiveCompare));
return ut_get_status();
}
/*
* Finds a prefix of a unit-system.
*
* Arguments:
* system Pointer to the unit-system.
* systemMap Pointer to system-map.
* string Pointer to the string to be examined for a prefix.
* compare Prefix comparison function.
* value NULL or pointer to the memory location to receive the
* value of the name-prefix, if one is discovered.
* len NULL or pointer to the memory location to receive the
* number of characters in the name-prefix, if one is
* discovered.
*
* Returns:
* UT_SUCCESS Success.
* UT_BAD_ARG "system" is NULL, "systemMap" is NULL, "compare" is
* NULL, "string" is NULL or empty, or "value" is 0.
* UT_OS Operating-system failure. See "errno".
* UT_UNKNOWN No prefix-to-value map is associated with "system".
* UT_UNKNOWN No prefix found in the prefix-to-value map associated
* with "system".
*/
static ut_status
findPrefix(
ut_system* const system,
SystemMap* const systemMap,
const char* const string,
double* const value,
size_t* const len)
{
ut_status status;
if (system == NULL) {
status = UT_BAD_ARG;
}
else if (systemMap == NULL) {
status = UT_BAD_ARG;
}
else if (string == NULL || strlen(string) == 0) {
status = UT_BAD_ARG;
}
else {
PrefixToValueMap** const prefixToValue =
(PrefixToValueMap**)smFind(systemMap, system);
if (prefixToValue == NULL) {
status = UT_UNKNOWN;
}
else {
const PrefixSearchEntry* entry =
ptvmFind(*prefixToValue, string);
if (entry == NULL) {
status = UT_UNKNOWN;
}
else {
if (value != NULL)
*value = entry->value;
if (len != NULL)
*len = entry->position + 1;
status = UT_SUCCESS;
} /* have prefix entry */
} /* have system-map entry */
} /* valid arguments */
return status;
}
/*
* Examines a string for a name-prefix and returns the length of the name-prefix
* and its value if one is discovered.
*
* Arguments:
* system Pointer to the unit-system.
* string Pointer to the string to be examined for a name-prefix.
* value NULL or pointer to the memory location to receive the value of
* the name-prefix, if one is discovered.
* len NULL or pointer to the memory location to receive the number of
* characters in the name-prefix, if one is discovered.
* Returns:
* UT_BAD_ARG "string" is NULL.
* UT_UNKNOWN A name-prefix was not discovered.
* UT_SUCCESS Success. "*value" and "*len" will be set if non-NULL.
*/
ut_status
utGetPrefixByName(
ut_system* const system,
const char* const string,
double* const value,
size_t* const len)
{
return
string == NULL
? UT_BAD_ARG
: findPrefix(system, systemToNameToValue, string, value, len);
}
/*
* Examines a string for a symbol-prefix and returns the length of the
* symbol-prefix and its value if one is discovered.
*
* Arguments:
* system Pointer to the unit-system.
* string Pointer to the string to be examined for a symbol-prefix.
* value NULL or pointer to the memory location to receive the value of
* the symbol-prefix, if one is discovered.
* len NULL or pointer to the memory location to receive the number of
* characters in the symbol-prefix, if one is discovered.
* Returns:
* UT_BAD_ARG "string" is NULL.
* UT_UNKNOWN A symbol-prefix was not discovered.
* UT_SUCCESS Success. "*value" and "*len" will be set if non-NULL.
*/
ut_status
utGetPrefixBySymbol(
ut_system* const system,
const char* const string,
double* const value,
size_t* const len)
{
return
string == NULL
? UT_BAD_ARG
: findPrefix(system, systemToSymbolToValue, string, value, len);
}