text: /*
   * Copyright 2013 University Corporation for Atmospheric Research
   *
   * This file is part of the UDUNITS-2 package.  See the file COPYRIGHT
   * in the top-level source-directory of the package for copying and
   * redistribution conditions.
   */
  /*
   * Module for system-to-pointer maps.
   *
   * This module is thread-compatible but not thread-safe.
   */
  
  /*LINTLIBRARY*/
  
  #ifndef	_XOPEN_SOURCE
  #   define _XOPEN_SOURCE 600
  #endif
  
  #ifdef _MSC_VER
  #include "tsearch.h"
  #else
  #include <search.h>
  #endif
  #include <stdlib.h>
  
  #include "systemMap.h"
  #include "udunits2.h"
  
  struct SystemMap {
      void*	tree;
  };
  
  typedef struct {
      const ut_system*	system;
      void*		ptr;
  } Entry;
  
  
  /*
   * Compares two entries according to their unit-system pointers.
   *
   * Arguments:
   *	entry1	Pointer to the first entry.
   *	entry2	Pointer to the second entry.
   * Returns:
   *	-1	The first entry's key is less than the second's.
   *	 0	The first entry's key is equal to the second's.
   *	 1	The first entry's key is greater than the second's.
   */
  static int
  compareEntries(
      const void*	entry1,
      const void*	entry2)
  {
      const ut_system* const	system1 = ((Entry*)entry1)->system;
      const ut_system* const	system2 = ((Entry*)entry2)->system;
  
      return system1 < system2 ? -1 : system1 == system2 ? 0 : 1;
  }
  
  
  /*
   * Returns a new instance of a system-map.
   *
   * Arguments:
   *	compare	Function for comparing keys.
   * Returns:
   *	NULL	Operating-system failure.  See "errno".
   *	else	Pointer to the new map.
   */
  SystemMap*
  smNew()
  {
      SystemMap*	map = (SystemMap*)malloc(sizeof(SystemMap));
  
      if (map != NULL)
  	map->tree = NULL;
  
      return map;
  }
  
  
  /*
   * Returns the address of the pointer to which a unit-system maps.
   *
   * Arguments:
   *	map	Pointer to the system-map.
   *	system	Pointer to the unit-system.
   * Returns:
   *	NULL	There is no pointer associated with "system".
   *	else	Address of the pointer to which "system" maps.
   */
  void**
  smFind(
      const SystemMap* const	map,
      const void* const		system)
  {
      Entry	targetEntry;
      Entry**	treeEntry;
  
      targetEntry.system = system;
      treeEntry = tfind(&targetEntry, &map->tree, compareEntries);
  
      return
  	treeEntry == NULL
  	    ? NULL
  	    : &(*treeEntry)->ptr;
  }
  
  
  /*
   * Returns the address of the pointer to which a unit-system maps -- creating a
   * new entry if necessary.  If a new entry is created, then the pointer whose
   * address is returned will be NULL.
   *
   * Arguments:
   *	map	Pointer to the system-map.
   *	system	Pointer to the unit-system.
   * Returns:
   *	NULL	Operating system failure.  See "errno".
   *	else	Address of the pointer to which "system" maps.
   */
  void**
  smSearch(
      SystemMap* const	map,
      const void*		system)
  {
      void**	addr = NULL;		/* failure */
      Entry*	targetEntry = (Entry*)malloc(sizeof(Entry));
  
      if (targetEntry != NULL) {
  	Entry**	treeEntry;
  
  	targetEntry->system = system;
  	targetEntry->ptr = NULL;
  	treeEntry = tsearch(targetEntry, &map->tree, compareEntries);
  
  	if (treeEntry == NULL) {
  	    free(targetEntry);
  	}
  	else {
  	    addr = &(*treeEntry)->ptr;
  
  	    if (targetEntry != *treeEntry)
  		free(targetEntry);
  	}
      }
  
      return addr;
  }
  
  
  /*
   * Removes the system-map entry that corresponds to a unit-system.
   *
   * Arguments:
   *	map	Pointer to the map.
   *	system	Pointer to the unit-system.
   */
  void
  smRemove(
      SystemMap* const	map,
      const void* const	system)
  {
      Entry	targetEntry;
      Entry**	treeEntry;
  
      targetEntry.system = system;
      treeEntry = tfind(&targetEntry, &map->tree, compareEntries);
  
      if (treeEntry != NULL) {
  	Entry*	entry = *treeEntry;
  
  	(void)tdelete(entry, &map->tree, compareEntries);
  	free(entry);
      }
  }
  
  
  /*
   * Frees a system-map.  This function should be called when a system-map is no
   * longer needed.
   *
   * Arguments:
   *	map	Pointer to the system-map to be freed or NULL.  Use of "map"
   *		upon return results in undefined behavior.
   */
  void
  smFree(
      SystemMap* const	map)
  {
      if (map != NULL) {
  	while (map->tree != NULL) {
  	    Entry*	entry = *(Entry**)map->tree;
  
  	    tdelete(entry, &map->tree, compareEntries);
  	    free(entry);
  	}
  
  	free(map);
      }
  }
  