Logo Search packages:      
Sourcecode: udunits version File versions  Download package

idToUnitMap.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 */
}

Generated by  Doxygen 1.6.0   Back to index