"Fossies" - the Fresh Open Source Software Archive

Member "udunits-2.2.28/lib/prefix.c" (7 Dec 2020, 12627 Bytes) of package /linux/privat/udunits-2.2.28.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "prefix.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 2.2.26_vs_2.2.28.

    1 /*
    2  * Copyright 2020 University Corporation for Atmospheric Research
    3  *
    4  * This file is part of the UDUNITS-2 package.  See the file COPYRIGHT
    5  * in the top-level source-directory of the package for copying and
    6  * redistribution conditions.
    7  */
    8 /*
    9  * Module for handling unit prefixes -- both names and symbols.
   10  */
   11 
   12 /*LINTLIBRARY*/
   13 
   14 #include "config.h"
   15 
   16 #include "prefix.h"
   17 #include "udunits2.h"
   18 #include "systemMap.h"
   19 
   20 #include <ctype.h>
   21 #include <errno.h>
   22 #ifdef _MSC_VER
   23 #include "tsearch.h"
   24 #else
   25 #include <search.h>
   26 #endif
   27 #include <stdlib.h>
   28 #include <string.h>
   29 
   30 typedef struct {
   31     void*   tree;
   32     int     (*compare)(const void*, const void*);
   33 } PrefixToValueMap;
   34 
   35 typedef struct {
   36     void*   nextTree;
   37     double  value;
   38     size_t  position;   /* origin-0 index of character in prefix */
   39     int     character;
   40 } PrefixSearchEntry;
   41 
   42 static SystemMap*   systemToNameToValue = NULL;
   43 static SystemMap*   systemToSymbolToValue = NULL;
   44 
   45 
   46 /******************************************************************************
   47  * Prefix Search Entry:
   48  ******************************************************************************/
   49 
   50 
   51 static PrefixSearchEntry*
   52 pseNew(
   53     int         character,
   54     size_t      position)
   55 {
   56     PrefixSearchEntry*  entry = malloc(sizeof(PrefixSearchEntry));
   57 
   58     if (entry == NULL) {
   59     ut_set_status(UT_OS);
   60     ut_handle_error_message(strerror(errno));
   61     ut_handle_error_message(
   62             "Couldn't allocate %lu-byte prefix-search-entry",
   63         sizeof(PrefixSearchEntry));
   64     }
   65     else {
   66     entry->character = character;
   67     entry->position = position;
   68     entry->value = 0;
   69     entry->nextTree = NULL;
   70     }
   71 
   72     return entry;
   73 }
   74 
   75 
   76 static void
   77 pseFree(
   78     PrefixSearchEntry* const    entry)
   79 {
   80     free(entry);
   81 }
   82 
   83 
   84 static int
   85 pseSensitiveCompare(
   86     const void* const   entry1,
   87     const void* const   entry2)
   88 {
   89     int char1 = ((const PrefixSearchEntry*)entry1)->character;
   90     int char2 = ((const PrefixSearchEntry*)entry2)->character;
   91 
   92     return char1 < char2 ? -1 : char1 == char2 ? 0 : 1;
   93 }
   94 
   95 
   96 static int
   97 pseInsensitiveCompare(
   98     const void* const   entry1,
   99     const void* const   entry2)
  100 {
  101     int char1 = tolower(((const PrefixSearchEntry*)entry1)->character);
  102     int char2 = tolower(((const PrefixSearchEntry*)entry2)->character);
  103 
  104     return char1 < char2 ? -1 : char1 == char2 ? 0 : 1;
  105 }
  106 
  107 
  108 /******************************************************************************
  109  * Prefix-to-Value Map:
  110  ******************************************************************************/
  111 
  112 
  113 static PrefixToValueMap*
  114 ptvmNew(
  115     int     (*compare)(const void*, const void*))
  116 {
  117     PrefixToValueMap*   map =
  118     (PrefixToValueMap*)malloc(sizeof(PrefixToValueMap));
  119 
  120     if (map != NULL) {
  121     map->tree = NULL;
  122     map->compare = compare;
  123     }
  124 
  125     return map;
  126 }
  127 
  128 
  129 /*
  130  * Returns the prefix search-entry that matches an identifier.  Inserts a
  131  * new prefix search-entry if no matching element is found.  Note that the
  132  * returned entry might have a different prefix value if it was previously
  133  * inserted.
  134  *
  135  * Arguments:
  136  *  map     Pointer to the prefix-to-value map.
  137  *  id      The prefix identifier.  May be freed upon return.
  138  *  value       The prefix value.
  139  * Returns:
  140  *  NULL        "map" is NULL.
  141  *  NULL        "id" is NULL or the empty string.
  142  *  NULL        "value" is 0.
  143  *  NULL        Insufficient storage space is available.
  144  *  else        Pointer to the prefix-search-entry that matches "id".
  145  */
  146 static const PrefixSearchEntry*
  147 ptvmSearch(
  148     PrefixToValueMap*   map,
  149     const char* const   id,
  150     const double    value)
  151 {
  152     PrefixSearchEntry*  entry = NULL;   /* failure */
  153 
  154     if (id != NULL && map != NULL && value != 0) {
  155     size_t  len = strlen(id);
  156 
  157     if (len > 0) {
  158         size_t          i;
  159         PrefixSearchEntry* const*   treeEntry = NULL;
  160         void**          tree = &map->tree;
  161 
  162         for (i = 0; i < len; i++) {
  163         PrefixSearchEntry* const    newEntry = pseNew(id[i], i);
  164 
  165         if (newEntry == NULL)
  166             break;
  167 
  168         treeEntry = tsearch(newEntry, tree, map->compare);
  169 
  170         if (treeEntry == NULL) {
  171             pseFree(newEntry);
  172             break;
  173         }
  174 
  175         tree = &(*treeEntry)->nextTree; /* next binary-search tree */
  176 
  177         if (newEntry != *treeEntry)
  178             pseFree(newEntry);
  179         }
  180 
  181         if (i >= len) {
  182         entry = *treeEntry;
  183 
  184         if (entry->value == 0)
  185             entry->value = value;
  186         }
  187     }
  188     }
  189 
  190     return entry;
  191 }
  192 
  193 
  194 /*
  195  * Returns the prefix search-entry that matches the beginning of a string.
  196  *
  197  * Arguments:
  198  *  map     Pointer to the prefix-to-value map.
  199  *  string      Pointer to the string to be examined for a prefix.
  200  * Returns:
  201  *  NULL        "map" is NULL.
  202  *  NULL        "string" is NULL or the empty string.
  203  *  NULL        "value" is 0.
  204  *  else        Pointer to the prefix-search-entry that matches the
  205  *          beginning of "string".
  206  */
  207 static const PrefixSearchEntry*
  208 ptvmFind(
  209     PrefixToValueMap* const map,
  210     const char* const       string)
  211 {
  212     PrefixSearchEntry*  entry = NULL;   /* failure */
  213 
  214     if (string != NULL && map != NULL && strlen(string) > 0) {
  215     size_t  len = strlen(string);
  216 
  217     if (len > 0) {
  218         size_t          i;
  219         PrefixSearchEntry*      lastEntry = NULL;
  220         void**          tree = &map->tree;
  221 
  222         for (i = 0; i < len; i++) {
  223         PrefixSearchEntry       targetEntry;
  224         PrefixSearchEntry* const*   treeEntry;
  225 
  226         targetEntry.character = string[i];
  227         treeEntry = tfind(&targetEntry, tree, map->compare);
  228 
  229         if (treeEntry == NULL)
  230             break;
  231 
  232         lastEntry = *treeEntry;
  233 
  234         tree = &(*treeEntry)->nextTree; /* next binary-search tree */
  235         }
  236 
  237         if (lastEntry != NULL && lastEntry->value != 0)
  238         entry = lastEntry;
  239     }
  240     }
  241 
  242     return entry;
  243 }
  244 
  245 
  246 /******************************************************************************
  247  * Public API:
  248  ******************************************************************************/
  249 
  250 
  251 /*
  252  * Adds a prefix to a unit-system.
  253  *
  254  * Arguments:
  255  *  system      Pointer to the unit-system.
  256  *  prefix      Pointer to the prefix (e.g., "mega", "M").  May be freed
  257  *          upon return.
  258  *  value       The value of the prefix (e.g., 1e6).
  259  *  systemMap   Pointer to system-map.
  260  *  compare     Prefix comparison function.
  261  * Returns:
  262  *  UT_SUCCESS  Success.
  263  *  UT_BAD_ARG  "system" is NULL, "prefix" is NULL or empty, or "value"
  264  *                      is 0.
  265  *  UT_EXISTS   "prefix" already maps to a different value.
  266  *  UT_OS       Operating-system failure.  See "errno".
  267  */
  268 static ut_status
  269 addPrefix(
  270     ut_system* const    system,
  271     const char* const   prefix,
  272     const double    value,
  273     SystemMap** const   systemMap,
  274     int         (*compare)(const void*, const void*))
  275 {
  276     ut_status       status;
  277 
  278     if (system == NULL) {
  279     status = UT_BAD_ARG;
  280     }
  281     else if (prefix == NULL || strlen(prefix) == 0) {
  282     status = UT_BAD_ARG;
  283     }
  284     else if (value == 0) {
  285     status = UT_BAD_ARG;
  286     }
  287     else {
  288     if (*systemMap == NULL) {
  289         *systemMap = smNew();
  290 
  291         if (*systemMap == NULL)
  292         status = UT_OS;
  293     }
  294 
  295     if (*systemMap != NULL) {
  296         PrefixToValueMap** const    prefixToValue =
  297         (PrefixToValueMap**)smSearch(*systemMap, system);
  298 
  299         if (prefixToValue == NULL) {
  300         status = UT_OS;
  301         }
  302         else {
  303         if (*prefixToValue == NULL) {
  304             *prefixToValue = ptvmNew(compare);
  305 
  306             if (*prefixToValue == NULL)
  307             status = UT_OS;
  308         }
  309 
  310         if (*prefixToValue != NULL) {
  311             const PrefixSearchEntry*    entry =
  312             ptvmSearch(*prefixToValue, prefix, value);
  313 
  314             status =
  315             entry == NULL
  316                 ? UT_OS
  317                 : (entry->value == value)
  318                 ? UT_SUCCESS
  319                 : UT_EXISTS;
  320         }
  321         }               /* have system-map entry */
  322     }               /* have system-map */
  323     }                   /* valid arguments */
  324 
  325     return status;
  326 }
  327 
  328 
  329 /*
  330  * Adds a name-prefix to a unit-system.  A name-prefix is something like "mega"
  331  * or "milli".  Comparisons between name-prefixes are case-insensitive.
  332  *
  333  * Arguments:
  334  *  system      Pointer to the unit-system.
  335  *  name        Pointer to the name-prefix (e.g., "mega").  May be freed
  336  *          upon return.
  337  *  value       The value of the prefix (e.g., 1e6).
  338  * Returns:
  339  *  UT_SUCCESS  Success.
  340  *  UT_BAD_ARG  "system" or "name" is NULL, or "value" is 0.
  341  *  UT_EXISTS   "name" already maps to a different value.
  342  *  UT_OS       Operating-system failure.  See "errno".
  343  */
  344 ut_status
  345 ut_add_name_prefix(
  346     ut_system* const    system,
  347     const char* const   name,
  348     const double    value)
  349 {
  350     ut_set_status(addPrefix(system, name, value, &systemToNameToValue,
  351     pseInsensitiveCompare));
  352 
  353     return ut_get_status();
  354 }
  355 
  356 
  357 /*
  358  * Adds a symbol-prefix to a unit-system.  A symbol-prefix is something like
  359  * "M" or "y".  Comparisons between symbol-prefixes are case-sensitive.
  360  *
  361  * Arguments:
  362  *  system      Pointer to the unit-system.
  363  *  symbol      Pointer to the symbol-prefix (e.g., "M").  May be freed
  364  *          upon return.
  365  *  value       The value of the prefix (e.g., 1e6).
  366  * Returns:
  367  *  UT_SUCCESS  Success.
  368  *  UT_BADSYSTEM    "system" or "symbol" is NULL.
  369  *  UT_BAD_ARG  "value" is 0.
  370  *  UT_EXISTS   "symbol" already maps to a different value.
  371  *  UT_OS       Operating-system failure.  See "errno".
  372  */
  373 ut_status
  374 ut_add_symbol_prefix(
  375     ut_system* const    system,
  376     const char* const   symbol,
  377     const double    value)
  378 {
  379     ut_set_status(addPrefix(system, symbol, value, &systemToSymbolToValue,
  380     pseSensitiveCompare));
  381 
  382     return ut_get_status();
  383 }
  384 
  385 
  386 /*
  387  * Finds a prefix of a unit-system.
  388  *
  389  * Arguments:
  390  *  system      Pointer to the unit-system.
  391  *  systemMap   Pointer to system-map.
  392  *  string      Pointer to the string to be examined for a prefix.
  393  *  compare     Prefix comparison function.
  394  *  value       NULL or pointer to the memory location to receive the
  395  *          value of the name-prefix, if one is discovered.
  396  *  len     NULL or pointer to the memory location to receive the
  397  *          number of characters in the name-prefix, if one is
  398  *          discovered.
  399  *
  400  * Returns:
  401  *  UT_SUCCESS  Success.
  402  *  UT_BAD_ARG  "system" is NULL, "systemMap" is NULL, "compare" is
  403  *                      NULL, "string" is NULL or empty, or "value" is 0.
  404  *  UT_OS       Operating-system failure.  See "errno".
  405  *  UT_UNKNOWN  No prefix-to-value map is associated with "system".
  406  *  UT_UNKNOWN  No prefix found in the prefix-to-value map associated
  407  *          with "system".
  408  */
  409 static ut_status
  410 findPrefix(
  411     ut_system* const    system,
  412     SystemMap* const    systemMap,
  413     const char* const   string,
  414     double* const   value,
  415     size_t* const   len)
  416 {
  417     ut_status       status;
  418 
  419     if (system == NULL) {
  420     status = UT_BAD_ARG;
  421     }
  422     else if (systemMap == NULL) {
  423     status = UT_BAD_ARG;
  424     }
  425     else if (string == NULL || strlen(string) == 0) {
  426     status = UT_BAD_ARG;
  427     }
  428     else {
  429     PrefixToValueMap** const    prefixToValue =
  430         (PrefixToValueMap**)smFind(systemMap, system);
  431 
  432     if (prefixToValue == NULL) {
  433         status = UT_UNKNOWN;
  434     }
  435     else {
  436         const PrefixSearchEntry*    entry =
  437         ptvmFind(*prefixToValue, string);
  438 
  439         if (entry == NULL) {
  440         status = UT_UNKNOWN;
  441         }
  442         else {
  443         if (value != NULL)
  444             *value = entry->value;
  445 
  446         if (len != NULL)
  447             *len = entry->position + 1;
  448 
  449         status = UT_SUCCESS;
  450         }               /* have prefix entry */
  451     }               /* have system-map entry */
  452     }                   /* valid arguments */
  453 
  454     return status;
  455 }
  456 
  457 
  458 /*
  459  * Examines a string for a name-prefix and returns the length of the name-prefix
  460  * and its value if one is discovered.
  461  *
  462  * Arguments:
  463  *  system  Pointer to the unit-system.
  464  *  string  Pointer to the string to be examined for a name-prefix.
  465  *  value   NULL or pointer to the memory location to receive the value of
  466  *      the name-prefix, if one is discovered.
  467  *  len NULL or pointer to the memory location to receive the number of
  468  *      characters in the name-prefix, if one is discovered.
  469  * Returns:
  470  *  UT_BAD_ARG  "string" is NULL.
  471  *  UT_UNKNOWN  A name-prefix was not discovered.
  472  *  UT_SUCCESS  Success.  "*value" and "*len" will be set if non-NULL.
  473  */
  474 ut_status
  475 utGetPrefixByName(
  476     ut_system* const    system,
  477     const char* const   string,
  478     double* const   value,
  479     size_t* const   len)
  480 {
  481     return
  482     string == NULL
  483         ? UT_BAD_ARG
  484         : findPrefix(system, systemToNameToValue, string, value, len);
  485 }
  486 
  487 
  488 /*
  489  * Examines a string for a symbol-prefix and returns the length of the
  490  * symbol-prefix and its value if one is discovered.
  491  *
  492  * Arguments:
  493  *  system  Pointer to the unit-system.
  494  *  string  Pointer to the string to be examined for a symbol-prefix.
  495  *  value   NULL or pointer to the memory location to receive the value of
  496  *      the symbol-prefix, if one is discovered.
  497  *  len NULL or pointer to the memory location to receive the number of
  498  *      characters in the symbol-prefix, if one is discovered.
  499  * Returns:
  500  *  UT_BAD_ARG  "string" is NULL.
  501  *  UT_UNKNOWN  A symbol-prefix was not discovered.
  502  *  UT_SUCCESS  Success.  "*value" and "*len" will be set if non-NULL.
  503  */
  504 ut_status
  505 utGetPrefixBySymbol(
  506     ut_system* const    system,
  507     const char* const   string,
  508     double* const   value,
  509     size_t* const   len)
  510 {
  511     return
  512     string == NULL
  513         ? UT_BAD_ARG
  514         : findPrefix(system, systemToSymbolToValue, string, value, len);
  515 }