"Fossies" - the Fresh Open Source Software Archive

Member "udunits-2.2.28/lib/xml.c" (7 Dec 2020, 58716 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 "xml.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  * This module is thread-compatible but not thread-safe.  Multi-threaded
   10  * access must be externally synchronized.
   11  */
   12 
   13 /*LINTLIBRARY*/
   14 
   15 #include <config.h>
   16 
   17 #include "udunits2.h"
   18 
   19 #if defined(__linux__)
   20 #   ifndef _GNU_SOURCE
   21 #       define _GNU_SOURCE
   22 #   endif
   23 #endif
   24 
   25 #include <assert.h>
   26 #include <errno.h>
   27 #include <fcntl.h>
   28 #ifndef _MSC_VER
   29 #include <libgen.h>
   30 #endif
   31 #include <limits.h>
   32 #include <stdio.h>
   33 #include <stdlib.h>
   34 #include <string.h>
   35 #ifndef _MSC_VER
   36 #include <strings.h>
   37 #include <unistd.h>
   38 #endif
   39 #include <sys/stat.h>
   40 #include <sys/types.h>
   41 #if defined(__linux__)
   42 #include <dlfcn.h>
   43 #elif defined(__APPLE__)
   44 #define _DARWIN_C_SOURCE
   45 #include <dlfcn.h>
   46 #elif defined _WIN32
   47 #include <windows.h>
   48 #endif
   49 #include <expat.h>
   50 
   51 #ifndef DLL_UDUNITS2
   52 #   define XML_STATIC
   53 #endif
   54 
   55 #ifndef PATH_MAX
   56 #   define PATH_MAX 4096 // Includes terminating NUL
   57 #endif
   58 
   59 #define NAME_SIZE 128
   60 #define ACCUMULATE_TEXT \
   61     XML_SetCharacterDataHandler(currFile->parser, accumulateText)
   62 #define IGNORE_TEXT \
   63     XML_SetCharacterDataHandler(currFile->parser, NULL)
   64 
   65 typedef enum {
   66     START,
   67     UNIT_SYSTEM,
   68     PREFIX,
   69     UNIT,
   70     UNIT_NAME,
   71     ALIASES,
   72     ALIAS_NAME
   73 } ElementType;
   74 
   75 typedef struct {
   76     const char* path;
   77     char    singular[NAME_SIZE];
   78     char    plural[NAME_SIZE];
   79     char        symbol[NAME_SIZE];
   80     double      value;
   81     XML_Parser  parser;
   82     ut_unit*    unit;
   83     ElementType context;
   84     ut_encoding xmlEncoding;
   85     ut_encoding textEncoding;
   86     int         fd;
   87     int         skipDepth;
   88     int     prefixAdded;
   89     int         haveValue;
   90     int     isBase;
   91     int         isDimensionless;
   92     int         noPLural;
   93     int         nameSeen;
   94     int         symbolSeen;
   95 } File;
   96 
   97 typedef struct {
   98     char       ascii[NAME_SIZE];
   99     char       latin1[NAME_SIZE];
  100     char       latin1Nbsp[NAME_SIZE];
  101     char       utf8[NAME_SIZE];
  102     char       utf8Nbsp[NAME_SIZE];
  103 } Identifiers;
  104 
  105 static ut_status readXml(
  106     const char* const   path);
  107 
  108 static File*            currFile = NULL;
  109 static ut_system*   unitSystem = NULL;
  110 static char*            text = NULL;
  111 static size_t           nbytes = 0; /// Number of characters excluding NUL
  112 
  113 
  114 /*
  115  * Returns the plural form of a name.
  116  *
  117  * Arguments:
  118  *      singular        Pointer to the singular form of a name.
  119  * Returns:
  120  *      Pointer to the plural form of "singular".  Client must not free.  May be
  121  *      overwritten by subsequent calls.
  122  */
  123 const char*
  124 ut_form_plural(
  125     const char* singular)
  126 {
  127     static char buf[NAME_SIZE];
  128     const char* plural = NULL;      /* failure */
  129 
  130     if (singular != NULL) {
  131         size_t length = strlen(singular);
  132 
  133     if (length + 3 >= sizeof(buf)) {
  134             ut_set_status(UT_SYNTAX);
  135         ut_handle_error_message("Singular form is too long");
  136         XML_StopParser(currFile->parser, 0);
  137     }
  138     else if (length > 0) {
  139         (void)strcpy(buf, singular);
  140 
  141         if (length == 1) {
  142         (void)strcpy(buf+length, "s");
  143         }
  144         else {
  145         char    lastChar = singular[length-1];
  146 
  147         if (lastChar == 'y') {
  148             char    penultimateChar = singular[length-2];
  149 
  150             if (penultimateChar == 'a' || penultimateChar == 'e' ||
  151                 penultimateChar == 'i' || penultimateChar == 'o' ||
  152                 penultimateChar == 'u') {
  153             (void)strcpy(buf+length, "s");
  154             }
  155             else {
  156             (void)strcpy(buf+length-1, "ies");
  157             }
  158         }
  159         else {
  160             if (lastChar == 's' || lastChar == 'x' || lastChar == 'z' ||
  161                 (length >= 2 && (
  162                 strcmp(singular+length-2, "ch") == 0 ||
  163                 strcmp(singular+length-2, "sh") == 0))) {
  164             (void)strcpy(buf+length, "es");
  165             }
  166             else {
  167             (void)strcpy(buf+length, "s");
  168             }
  169         }
  170         }
  171 
  172         plural = buf;
  173         }
  174     }
  175 
  176     return plural;
  177 }
  178 
  179 
  180 /*
  181  * Substitutes one substring for all occurrences another in a string.
  182  *
  183  * Arguments:
  184  *      inString        Pointer to the input string.
  185  *      str             Pointer to the substring to be replaced.
  186  *      outString       Pointer to the output string buffer.
  187  *      repl            Pointer to the replacement substring.
  188  *      size            Size of the output string buffer, including the
  189  *                      terminating NUL.
  190  * Returns:
  191  *      0               Failure.  The output string buffer is too small.
  192  *      else            Success.
  193  */
  194 static int
  195 substitute(
  196     const char* const   inString,
  197     const char*         str,
  198     char* const         outString,
  199     const char*         repl,
  200     const size_t        size)
  201 {
  202     const char* in = inString;
  203     char*       out = outString;
  204     char*       beyond = outString + size;
  205     size_t      strLen = strlen(str);
  206     size_t      replLen = strlen(repl);
  207 
  208     while (*in) {
  209         size_t      nbytes;
  210         char*       cp = strstr(in, str);
  211 
  212         if (cp == NULL) {
  213             nbytes = strlen(in);
  214 
  215             if (out + nbytes >= beyond) {
  216                 ut_set_status(UT_SYNTAX);
  217                 ut_handle_error_message("String \"%s\" is too long", inString);
  218                 return 0;
  219             }
  220 
  221             (void)strncpy(out, in, nbytes);
  222 
  223             out += nbytes;
  224 
  225             break;
  226         }
  227         else {
  228             nbytes = (size_t)(cp - in);
  229 
  230             if (out + nbytes + replLen >= beyond) {
  231                 ut_set_status(UT_SYNTAX);
  232                 ut_handle_error_message("String \"%s\" is too long", inString);
  233                 return 0;
  234             }
  235 
  236             (void)strncpy(out, in, nbytes);
  237 
  238             out += nbytes;
  239 
  240             (void)strncpy(out, repl, replLen);
  241 
  242             out += replLen;
  243             in += nbytes + strLen;
  244         }
  245     }
  246 
  247     *out = 0;
  248 
  249     return 1;
  250 }
  251 
  252 
  253 #define IS_ASCII(c)     (((c) & '\x80') == 0)
  254 
  255 
  256 /*
  257  * Returns the UTF-8 string equivalent to a Latin-1 string.
  258  *
  259  * Arguments:
  260  *      inString        Pointer to the input, Latin-1 string.
  261  *      outString       Pointer to the output, UTF-8, string buffer.
  262  *      size            Size of "outString".
  263  * Returns:
  264  *      0               Failure.  "outString" is too small.
  265  *      1               Success.
  266  */
  267 static int
  268 latin1_to_utf8(
  269     const char* inString,
  270     char*       outString,
  271     size_t      size)
  272 {
  273     size_t                      numSpecial = 0;
  274     const unsigned char*        in;
  275     unsigned char*              out;
  276 
  277     assert(inString != NULL);
  278     assert(outString != NULL);
  279 
  280     /*
  281      * Compute the number of non-ASCII characters.
  282      */
  283     for (in = (const unsigned char*)inString; *in; in++)
  284         if (!IS_ASCII(*in))
  285             numSpecial++;
  286 
  287     if (size < ((size_t)((char*)in - (char*)inString) + numSpecial + 1)) {
  288         ut_set_status(UT_SYNTAX);
  289         ut_handle_error_message("Identifier \"%s\" is too long", inString);
  290         return 0;
  291     }
  292 
  293     /*
  294      * Convert the string.
  295      */
  296     for (in = (const unsigned char*)inString, out = (unsigned char*)outString;
  297             *in; ++in) {
  298         if (IS_ASCII(*in)) {
  299             *out++ = *in;
  300         }
  301         else {
  302             *out++ = 0xC0 | ((0xC0 & *in) >> 6);
  303             *out++ = 0x80 | (0x3F & *in);
  304         }
  305     }
  306 
  307     *out = 0;
  308 
  309     return 1;
  310 }
  311 
  312 
  313 /*
  314  * Returns the Latin-1 string equivalent to a UTF-8 string, if possible.
  315  *
  316  * Arguments:
  317  *      inString        Pointer to the input, UTF-8 string.
  318  *      outString       Pointer to the output, Latin-1, string buffer.
  319  *      size            Size of "outString".
  320  * Returns:
  321  *     -1               Failure.  "outString" is too small.
  322  *      0               "inString" can't be represented in Latin-1.
  323  *      1               Success.
  324  */
  325 static int
  326 utf8_to_latin1(
  327     const char* inString,
  328     char*       outString,
  329     size_t      size)
  330 {
  331     size_t                      numSpecial = 0;
  332     const unsigned char*        in;
  333     unsigned char*              out;
  334 
  335     assert(inString != NULL);
  336     assert(outString != NULL);
  337 
  338     /*
  339      * Compute the number of non-ASCII characters.
  340      */
  341     for (in = (const unsigned char*)inString; *in; in++) {
  342         if (*in > 0xC3)
  343             return 0;
  344 
  345         if (!IS_ASCII(*in)) {
  346             numSpecial++;
  347             in++;
  348         }
  349     }
  350 
  351     if (size < ((size_t)((char*)in - (char*)inString) - numSpecial + 1)) {
  352         ut_set_status(UT_SYNTAX);
  353         ut_handle_error_message("Identifier \"%s\" is too long", inString);
  354         return -1;
  355     }
  356 
  357     /*
  358      * Convert the string.
  359      */
  360     for (in = (const unsigned char*)inString, out = (unsigned char*)outString;
  361             *in; ++out) {
  362         if (IS_ASCII(*in)) {
  363             *out = *in++;
  364         }
  365         else {
  366             *out = (*in++ & 0x3) << 6;
  367             *out |= (*in++ & 0x3F);
  368         }
  369     }
  370 
  371     *out = 0;
  372 
  373     return 1;
  374 }
  375 
  376 
  377 #define LATIN1_NBSP "\xA0"
  378 #define UTF8_NBSP "\xC2\xA0"
  379 
  380 
  381 /*
  382  * Creates the regular and NBSP forms of a Latin-1 string.
  383  *
  384  * Arguments:
  385  *      latin1          Pointer to the input Latin-1 string.
  386  *      nonNbsp         Pointer to an output string buffer of size NAME_SIZE
  387  *                      that will be identical to "latin1" but with all
  388  *                      non-breaking spaces replaced with underscores.
  389  *      nbsp            Pointer to an output string buffer of size NAME_SIZE
  390  *                      that will be identical to "latin1" but with all
  391  *                      underscores replaced with non-breaking spaces.
  392  */
  393 static void
  394 make_latin1_forms(
  395     const char* latin1,
  396     char*       nonNbsp,
  397     char*       nbsp)
  398 {
  399     if (strchr(latin1, '_')) {
  400         substitute(latin1, "_", nbsp, LATIN1_NBSP, NAME_SIZE);
  401         substitute(nbsp, LATIN1_NBSP, nonNbsp, "_", NAME_SIZE);
  402     }
  403     else if (strstr(latin1, LATIN1_NBSP)) {
  404         substitute(latin1, LATIN1_NBSP, nonNbsp, "_", NAME_SIZE);
  405         substitute(nonNbsp, "_", nbsp, LATIN1_NBSP, NAME_SIZE);
  406     }
  407     else {
  408         (void)strcpy(nonNbsp, latin1);
  409         *nbsp = 0;
  410     }
  411 }
  412 
  413 
  414 /*
  415  * Creates the regular and NBSP forms of a UTF-8 string.
  416  *
  417  * Arguments:
  418  *      utf8            Pointer to the input UTF-8 string.
  419  *      nonNbsp         Pointer to an output string buffer of size NAME_SIZE
  420  *                      that will be identical to "utf8" but with all
  421  *                      non-breaking spaces replaced with underscores.
  422  *      nbsp            Pointer to an output string buffer of size NAME_SIZE
  423  *                      that will be identical to "utf8" but with all
  424  *                      underscores replaced with non-breaking spaces.
  425  */
  426 static int
  427 make_utf8_forms(
  428     const char* utf8,
  429     char*       nonNbsp,
  430     char*       nbsp)
  431 {
  432     int         success;
  433 
  434     if (strchr(utf8, '_')) {
  435         success = substitute(utf8, "_", nbsp, UTF8_NBSP, NAME_SIZE);
  436 
  437         if (success)
  438             success = substitute(nbsp, UTF8_NBSP, nonNbsp, "_", NAME_SIZE);
  439     }
  440     else if (strstr(utf8, UTF8_NBSP)) {
  441         success = substitute(utf8, UTF8_NBSP, nonNbsp, "_", NAME_SIZE);
  442 
  443         if (success)
  444             success = substitute(nonNbsp, "_", nbsp, UTF8_NBSP, NAME_SIZE);
  445     }
  446     else {
  447         (void)strcpy(nonNbsp, utf8);
  448         *nbsp = 0;
  449         success = 1;
  450     }
  451 
  452     return success;
  453 }
  454 
  455 
  456 /*
  457  * Creates derivatives of an identifier, which are all legitimate combinations
  458  * of ASCII, Latin-1, and UTF-8, on the one hand, and underscore vs.
  459  * non-breaking spaces on the other.
  460  *
  461  * Arguments:
  462  *      id              Pointer to the identifier.
  463  *      encoding        The encoding of "id".
  464  *      ids             Pointer to an "Identifier" structure.
  465  * Returns:
  466  *      0               Failure.
  467  *      else            Success.  If a combination is not possible or is
  468  *                      identical to a simpler combination, then the first
  469  *                      character of the relevant member of "ids" will be NUL.
  470  */
  471 static int
  472 makeDerivatives(
  473     const char* const   id,
  474     const ut_encoding   encoding,
  475     Identifiers*        ids)
  476 {
  477     int                 success = 1;
  478 
  479     assert(id != NULL);
  480     assert(ids != NULL);
  481 
  482     if (strlen(id) > NAME_SIZE - 1) {
  483         ut_set_status(UT_SYNTAX);
  484         ut_handle_error_message("Identifier \"%s\" is too long", id);
  485         success = 0;
  486     }
  487     else {
  488         ids->ascii[0] = 0;
  489         ids->latin1[0] = 0;
  490         ids->latin1Nbsp[0] = 0;
  491         ids->utf8[0] = 0;
  492         ids->utf8Nbsp[0] = 0;
  493 
  494         if (encoding == UT_ASCII) {
  495             (void)strcpy(ids->ascii, id);
  496 
  497             if (strchr(id, '_')) {
  498                 (void)substitute(id, "_", ids->latin1Nbsp, LATIN1_NBSP,
  499                     NAME_SIZE);
  500                 success =
  501                     latin1_to_utf8(ids->latin1Nbsp, ids->utf8Nbsp, NAME_SIZE);
  502             }
  503         }
  504         else if (encoding == UT_LATIN1) {
  505             make_latin1_forms(id, ids->latin1, ids->latin1Nbsp);
  506 
  507             success = latin1_to_utf8(ids->latin1, ids->utf8, NAME_SIZE) &&
  508                 latin1_to_utf8(ids->latin1Nbsp, ids->utf8Nbsp, NAME_SIZE);
  509         }
  510         else {
  511             success = make_utf8_forms(id, ids->utf8, ids->utf8Nbsp) &&
  512                 utf8_to_latin1(ids->utf8, ids->latin1, NAME_SIZE) != -1 &&
  513                 utf8_to_latin1(ids->utf8Nbsp, ids->latin1Nbsp, NAME_SIZE) != -1;
  514         }
  515 
  516         if (success) {
  517             if (strcmp(ids->ascii, ids->latin1) == 0)
  518                 ids->latin1[0] = 0;
  519             if (strcmp(ids->ascii, ids->latin1Nbsp) == 0)
  520                 ids->latin1Nbsp[0] = 0;
  521             if (strcmp(ids->ascii, ids->utf8) == 0)
  522                 ids->utf8[0] = 0;
  523             if (strcmp(ids->ascii, ids->utf8Nbsp) == 0)
  524                 ids->utf8Nbsp[0] = 0;
  525         }
  526     }
  527 
  528     return success;
  529 }
  530 
  531 
  532 /*
  533  * Maps a unit to an identifier.
  534  *
  535  * Arguments:
  536  *      unit            Pointer to the unit.
  537  *      id              Pointer to the identifier.
  538  *      encoding        The encoding of "id".
  539  *      isName          Whether or not "id" is a name.
  540  * Returns:
  541  *      0               Failure.
  542  *      else            Success.
  543  */
  544 static int
  545 mapUnitToId(
  546     ut_unit* const        unit,
  547     const char* const     id,
  548     ut_encoding           encoding,
  549     int                   isName)
  550 {
  551     int                 success = 0;             /* failure */
  552     ut_status           (*func)(const ut_unit*, const char*, ut_encoding);
  553     const char*         desc;
  554 
  555     if (isName) {
  556         func = ut_map_unit_to_name;
  557         desc = "name";
  558     }
  559     else {
  560         func = ut_map_unit_to_symbol;
  561         desc = "symbol";
  562     }
  563 
  564     if (func(unit, id, encoding) != UT_SUCCESS) {
  565         ut_set_status(UT_PARSE);
  566         ut_handle_error_message("Couldn't map unit to %s \"%s\"", desc, id);
  567     }
  568     else {
  569         success = 1;
  570     }
  571 
  572     return success;
  573 }
  574 
  575 
  576 /*
  577  * Maps a unit to identifiers.
  578  *
  579  * Arguments:
  580  *      unit            Pointer to the unit.
  581  *      id              Pointer to the identifier upon which to base all
  582  *                      derived identifiers.
  583  *      encoding        The encoding of "id".
  584  *      isName          Whether or not "id" is a name.
  585  * Returns:
  586  *      0               Failure.
  587  *      else            Success.
  588  */
  589 static int
  590 mapUnitToIds(
  591     ut_unit* const        unit,
  592     const char* const     id,
  593     ut_encoding           encoding,
  594     int                   isName)
  595 {
  596     int                 success = 1;             /* success */
  597     Identifiers         ids;
  598 
  599     if (!makeDerivatives(id, encoding, &ids)) {
  600         success = 0;
  601     }
  602     else {
  603         if (ids.ascii[0])
  604             success = mapUnitToId(unit, ids.ascii, UT_ASCII, isName);
  605         if (success && ids.latin1[0])
  606             success = mapUnitToId(unit, ids.latin1, UT_LATIN1, isName);
  607         if (success && ids.latin1Nbsp[0])
  608             success = mapUnitToId(unit, ids.latin1Nbsp, UT_LATIN1, isName);
  609         if (success && ids.utf8[0])
  610             success = mapUnitToId(unit, ids.utf8, UT_UTF8, isName);
  611         if (success && ids.utf8Nbsp[0])
  612             success = mapUnitToId(unit, ids.utf8Nbsp, UT_UTF8, isName);
  613     }
  614 
  615     return success;
  616 }
  617 
  618 
  619 /*
  620  * Maps a unit to a name and all derivatives of the name.
  621  *
  622  * Arguments:
  623  *      unit            Pointer to the unit.
  624  *      name            Pointer to the name upon which to base all derived names.
  625  *      encoding        The encoding of "name".
  626  * Returns:
  627  *      0               Failure.
  628  *      else            Success.
  629  */
  630 static int
  631 mapUnitToNames(
  632     ut_unit* const        unit,
  633     const char* const     name,
  634     ut_encoding           encoding)
  635 {
  636     return mapUnitToIds(unit, name, encoding, 1);
  637 }
  638 
  639 
  640 /*
  641  * Maps a unit to a symbol and all derivatives of the symbol.
  642  *
  643  * Arguments:
  644  *      unit            Pointer to the unit.
  645  *      symbol          Pointer to the symbol upon which to base all derived
  646  *                      symbols.
  647  *      encoding        The encoding of "symbol".
  648  * Returns:
  649  *      0               Failure.
  650  *      else            Success.
  651  */
  652 static int
  653 mapUnitToSymbols(
  654     ut_unit* const        unit,
  655     const char* const     symbol,
  656     ut_encoding           encoding)
  657 {
  658     return mapUnitToIds(unit, symbol, encoding, 0);
  659 }
  660 
  661 
  662 /*
  663  * Maps an identifier to a unit.
  664  *
  665  * Arguments:
  666  *      id              Pointer to the identifier.
  667  *      encoding        The character encoding of "id".
  668  *      unit            Pointer to the unit.
  669  *      isName          Whether or not "id" is a name.
  670  * Returns:
  671  *      0               Failure.
  672  *      else            Success.
  673  */
  674 static int
  675 mapIdToUnit(
  676     const char*         id,
  677     const ut_encoding   encoding,
  678     ut_unit*            unit,
  679     const int           isName)
  680 {
  681     int     success = 0;        /* failure */
  682     ut_unit*    prev = ut_get_unit_by_name(unitSystem, id);
  683 
  684     if (prev == NULL)
  685     prev = ut_get_unit_by_symbol(unitSystem, id);
  686 
  687     if (prev != NULL) {
  688     char    buf[128];
  689     int nchar = ut_format(prev, buf, sizeof(buf),
  690         UT_ASCII | UT_DEFINITION | UT_NAMES);
  691 
  692         ut_set_status(UT_PARSE);
  693     ut_handle_error_message(
  694         "Duplicate definition for \"%s\" at \"%s\":%d", id,
  695             currFile->path, XML_GetCurrentLineNumber(currFile->parser));
  696 
  697     if (nchar < 0)
  698         nchar =
  699                 ut_format(prev, buf, sizeof(buf), UT_ASCII | UT_DEFINITION);
  700 
  701     if (nchar >= 0 && nchar < sizeof(buf)) {
  702         buf[nchar] = 0;
  703 
  704             ut_set_status(UT_PARSE);
  705         ut_handle_error_message("Previous definition was \"%s\"", buf);
  706     }
  707 
  708         XML_StopParser(currFile->parser, 0);
  709     }
  710     else {
  711     /*
  712      * Take prefixes into account for a prior definition by using
  713          * ut_parse().
  714      */
  715     prev = ut_parse(unitSystem, id, encoding);
  716 
  717     if ((isName
  718                     ? ut_map_name_to_unit(id, encoding, unit)
  719                     : ut_map_symbol_to_unit(id, encoding, unit))
  720                 != UT_SUCCESS) {
  721             ut_set_status(UT_PARSE);
  722         ut_handle_error_message("Couldn't map %s \"%s\" to unit",
  723         isName ? "name" : "symbol", id);
  724         XML_StopParser(currFile->parser, 0);
  725     }
  726     else {
  727         if (prev != NULL) {
  728         char    buf[128];
  729         int nchar = ut_format(prev, buf, sizeof(buf),
  730             UT_ASCII | UT_DEFINITION | UT_NAMES);
  731 
  732         if (nchar < 0)
  733             nchar = ut_format(prev, buf, sizeof(buf),
  734             UT_ASCII | UT_DEFINITION);
  735 
  736         if (nchar < 0 || nchar >= sizeof(buf)) {
  737                     ut_set_status(UT_PARSE);
  738             ut_handle_error_message("Definition of \"%s\" in \"%s\", "
  739                         "line %d, overrides prefixed-unit", id,
  740                         currFile->path,
  741             XML_GetCurrentLineNumber(currFile->parser));
  742         }
  743         else {
  744             buf[nchar] = 0;
  745 
  746                     ut_set_status(UT_PARSE);
  747             ut_handle_error_message("Definition of \"%s\" in \"%s\", "
  748                         "line %d, overrides prefixed-unit \"%s\"",
  749                         id, currFile->path,
  750                         XML_GetCurrentLineNumber(currFile->parser), buf);
  751         }
  752         }
  753 
  754         success = 1;
  755     }
  756     }
  757 
  758     ut_free(prev);                      /* NULL safe */
  759 
  760     return success;
  761 }
  762 
  763 
  764 /*
  765  * Maps an identifier and all derivatives to a unit.
  766  *
  767  * Arguments:
  768  *      id              Pointer to the identifier.
  769  *      encoding        The encoding of "id".
  770  *      unit            Pointer to the unit.
  771  *      isName          Whether or not "id" is a name.
  772  * Returns:
  773  *      0               Failure.
  774  *      else            Success.
  775  */
  776 static int
  777 mapIdsToUnit(
  778     const char* const   id,
  779     const ut_encoding   encoding,
  780     ut_unit* const      unit,
  781     const int           isName)
  782 {
  783     Identifiers         ids;
  784     int                 success = 1;
  785 
  786     if (!makeDerivatives(id, encoding, &ids)) {
  787         success = 0;
  788     }
  789     else {
  790         if (ids.ascii[0])
  791             success = mapIdToUnit(ids.ascii, UT_ASCII, unit, isName);
  792         if (success && ids.latin1[0])
  793             success = mapIdToUnit(ids.latin1, UT_LATIN1, unit, isName);
  794         if (success && ids.latin1Nbsp[0])
  795             success = mapIdToUnit(ids.latin1Nbsp, UT_LATIN1, unit, isName);
  796         if (success && ids.utf8[0])
  797             success = mapIdToUnit(ids.utf8, UT_UTF8, unit, isName);
  798         if (success && ids.utf8Nbsp[0])
  799             success = mapIdToUnit(ids.utf8Nbsp, UT_UTF8, unit, isName);
  800     }
  801 
  802     return success;
  803 }
  804 
  805 
  806 /*
  807  * Maps a name and all derivatives to a unit.
  808  *
  809  * Arguments:
  810  *      name            Pointer to the name.
  811  *      encoding        The encoding of "name".
  812  *      unit            Pointer to the unit.
  813  * Returns:
  814  *      0               Failure.
  815  *      else            Success.
  816  */
  817 static int
  818 mapNamesToUnit(
  819     const char* const   name,
  820     const ut_encoding   encoding,
  821     ut_unit* const  unit)
  822 {
  823     return mapIdsToUnit(name, encoding, unit, 1);
  824 }
  825 
  826 
  827 /*
  828  * Maps a symbol and all derivatives to a unit.
  829  *
  830  * Arguments:
  831  *      symbol          Pointer to the symbol.
  832  *      encoding        The encoding of "symbol".
  833  *      unit            Pointer to the unit.
  834  * Returns:
  835  *      0               Failure.
  836  *      else            Success.
  837  */
  838 static int
  839 mapSymbolsToUnit(
  840     const char* const   symbol,
  841     const ut_encoding   encoding,
  842     ut_unit* const  unit)
  843 {
  844     return mapIdsToUnit(symbol, encoding, unit, 0);
  845 }
  846 
  847 
  848 /*
  849  * Maps between a unit and a name.
  850  *
  851  * Arguments:
  852  *      unit            Pointer to the unit.
  853  *      name            Pointer to the name  .
  854  *      encoding        The encoding of "name".
  855  * Returns:
  856  *      0               Failure.
  857  *      else            Success.
  858  */
  859 static int
  860 mapUnitAndName(
  861     ut_unit* const        unit,
  862     const char* const     name,
  863     ut_encoding           encoding)
  864 {
  865     return
  866         mapNamesToUnit(name, encoding, unit) &&
  867         mapUnitToNames(unit, name, encoding);
  868 }
  869 
  870 
  871 /*
  872  * Maps between a unit and a symbol.
  873  *
  874  * Arguments:
  875  *      unit            Pointer to the unit.
  876  *      symbol          Pointer to the symbol  .
  877  *      encoding        The encoding of "symbol".
  878  * Returns:
  879  *      0               Failure.
  880  *      else            Success.
  881  */
  882 static int
  883 mapUnitAndSymbol(
  884     ut_unit* const        unit,
  885     const char* const     symbol,
  886     ut_encoding           encoding)
  887 {
  888     return
  889         mapSymbolsToUnit(symbol, encoding, unit) &&
  890         mapUnitToSymbols(unit, symbol, encoding);
  891 }
  892 
  893 
  894 /*
  895  * Initializes a "File" data-structure to the default state.
  896  */
  897 static void
  898 fileInit(
  899     File* const file)
  900 {
  901     file->context = START;
  902     file->skipDepth = 0;
  903     file->value = 0;
  904     file->xmlEncoding = UT_ASCII;
  905     file->textEncoding = UT_ASCII;
  906     file->unit = NULL;
  907     file->fd = -1;
  908     file->parser = NULL;
  909     file->isBase = 0;
  910     file->isDimensionless = 0;
  911     file->haveValue = 0;
  912     file->noPLural = 0;
  913     file->nameSeen = 0;
  914     file->symbolSeen = 0;
  915     file->path = NULL;
  916     (void)memset(file->singular, 0, sizeof(file->singular));
  917     (void)memset(file->plural, 0, sizeof(file->plural));
  918     (void)memset(file->symbol, 0, sizeof(file->symbol));
  919 }
  920 
  921 
  922 /*
  923  * Clears the text buffer for elements.
  924  */
  925 static void
  926 clearText(void)
  927 {
  928     if (text != NULL)
  929     *text = 0;
  930 
  931     nbytes = 0;
  932     currFile->textEncoding = UT_ASCII;
  933 }
  934 
  935 
  936 
  937 
  938 /*
  939  * Accumulates the textual portion of an element.
  940  */
  941 static void
  942 accumulateText(
  943     void*       data,
  944     const char*     string,     /* input text in UTF-8 */
  945     int         len)
  946 {
  947     char*   tmp = realloc(text, nbytes + len + 1);
  948 
  949     if (tmp == NULL) {
  950         ut_set_status(UT_OS);
  951     ut_handle_error_message(strerror(errno));
  952     ut_handle_error_message("Couldn't reallocate %lu-byte text buffer",
  953         nbytes+len+1);
  954     XML_StopParser(currFile->parser, 0);
  955     }
  956     else {
  957         int     i;
  958 
  959         text = tmp;
  960 
  961         for (i = 0; i < len; i++) {
  962             text[nbytes++] = string[i];
  963 
  964             if (!IS_ASCII(string[i]))
  965                 currFile->textEncoding = UT_UTF8;
  966         }
  967 
  968     text[nbytes] = 0;
  969     }
  970 }
  971 
  972 
  973 #if 0
  974 /*
  975  * Converts the accumulated text from UTF-8 to user-specified encoding.
  976  *
  977  * Returns:
  978  *      0       Failure: the text could not be converted.
  979  *      else    Success.
  980  */
  981 static int
  982 convertText(void)
  983 {
  984     int         success = 1;
  985 
  986     if (currFile->encoding == UT_ASCII) {
  987         unsigned char*   cp;
  988 
  989         for (cp = text; *cp && IS_ASCII(*cp); cp++)
  990             /* EMPTY */;
  991 
  992         if (*cp) {
  993             ut_set_status(UT_SYNTAX);
  994             ut_handle_error_message("Character isn't US-ASCII: %#x", *cp);
  995             XML_StopParser(currFile->parser, 0);
  996 
  997             success = 0;
  998         }
  999         else {
 1000             textEncoding = UT_ASCII;
 1001         }
 1002     }
 1003     else if (currFile->encoding == UT_LATIN1) {
 1004         unsigned char*       in;
 1005         unsigned char*       out;
 1006 
 1007         for (in = out = text; *in; ++in, ++out) {
 1008             if (IS_ASCII(*in)) {
 1009                 *out = *in;
 1010             }
 1011             else {
 1012                 if (*in <= 0xC3) {
 1013                     *out = (unsigned char)((*in++ & 0x3) << 6);
 1014                     *out |= (unsigned char)(*in & 0x3F);
 1015                 }
 1016                 else {
 1017                     ut_set_status(UT_SYNTAX);
 1018                     ut_handle_error_message(
 1019                         "Character is not representable in ISO-8859-1 "
 1020                         "(Latin-1): %d", 1+(int)((char*)out - text));
 1021                     XML_StopParser(currFile->parser, 0);
 1022 
 1023                     success = 0;
 1024 
 1025                     break;
 1026                 }
 1027             }
 1028         }
 1029 
 1030         *out = 0;
 1031         nbytes = out - (unsigned char*)text;
 1032         textEncoding = UT_LATIN1;
 1033     }
 1034 
 1035     return success;
 1036 }
 1037 #endif
 1038 
 1039 
 1040 /*
 1041  * Handles the start of a <unit-system> element.
 1042  */
 1043 static void
 1044 startUnitSystem(
 1045     void*       data,
 1046     const char**    atts)
 1047 {
 1048     if (currFile->context != START) {
 1049     ut_set_status(UT_PARSE);
 1050     ut_handle_error_message("Wrong place for <unit-system> element");
 1051     XML_StopParser(currFile->parser, 0);
 1052     }
 1053 
 1054     currFile->context = UNIT_SYSTEM;
 1055 }
 1056 
 1057 
 1058 /*
 1059  * Handles the end of a <unit-system> element.
 1060  */
 1061 static void
 1062 endUnitSystem(
 1063     void*       data)
 1064 {}
 1065 
 1066 
 1067 /*
 1068  * Handles the start of a <prefix> element.
 1069  */
 1070 static void
 1071 startPrefix(
 1072     void*       data,
 1073     const char* const*  atts)
 1074 {
 1075     if (currFile->context != UNIT_SYSTEM) {
 1076         ut_set_status(UT_PARSE);
 1077     ut_handle_error_message("Wrong place for <prefix> element");
 1078     }
 1079     else {
 1080     currFile->prefixAdded = 0;
 1081     currFile->haveValue = 0;
 1082     }
 1083 
 1084     currFile->context = PREFIX;
 1085 }
 1086 
 1087 
 1088 /*
 1089  * Handles the end of a <prefix> element.
 1090  */
 1091 static void
 1092 endPrefix(
 1093     void*       data)
 1094 {
 1095     if (!currFile->haveValue || !currFile->prefixAdded) {
 1096         ut_set_status(UT_PARSE);
 1097     ut_handle_error_message("Prefix incompletely specified");
 1098     XML_StopParser(currFile->parser, 0);
 1099     }
 1100     else {
 1101     currFile->haveValue = 0;
 1102     }
 1103 
 1104     currFile->context = UNIT_SYSTEM;
 1105 }
 1106 
 1107 
 1108 /*
 1109  * Handles the start of a <unit> element.
 1110  */
 1111 static void
 1112 startUnit(
 1113     void*       data,
 1114     const char**    atts)
 1115 {
 1116     if (currFile->context != UNIT_SYSTEM) {
 1117         ut_set_status(UT_PARSE);
 1118     ut_handle_error_message("Wrong place for <unit> element");
 1119     XML_StopParser(currFile->parser, 0);
 1120     }
 1121     else {
 1122     ut_free(currFile->unit);
 1123     currFile->unit = NULL;
 1124     currFile->isBase = 0;
 1125     currFile->isDimensionless = 0;
 1126         currFile->singular[0] = 0;
 1127         currFile->plural[0] = 0;
 1128         currFile->symbol[0] = 0;
 1129         currFile->nameSeen = 0;
 1130         currFile->symbolSeen = 0;
 1131     }
 1132 
 1133     currFile->context = UNIT;
 1134 }
 1135 
 1136 
 1137 /*
 1138  * Handles the end of a <unit> element.
 1139  */
 1140 static void
 1141 endUnit(
 1142     void*       data)
 1143 {
 1144     if (currFile->isBase) {
 1145         if (!currFile->nameSeen) {
 1146             ut_set_status(UT_PARSE);
 1147             ut_handle_error_message("Base unit needs a name");
 1148             XML_StopParser(currFile->parser, 0);
 1149         }
 1150         if (!currFile->symbolSeen) {
 1151             ut_set_status(UT_PARSE);
 1152             ut_handle_error_message("Base unit needs a symbol");
 1153             XML_StopParser(currFile->parser, 0);
 1154         }
 1155     }
 1156 
 1157     ut_free(currFile->unit);
 1158     currFile->unit = NULL;
 1159     currFile->context = UNIT_SYSTEM;
 1160 }
 1161 
 1162 
 1163 /*
 1164  * Handles the start of a <base> element.
 1165  */
 1166 static void
 1167 startBase(
 1168     void*       data,
 1169     const char**    atts)
 1170 {
 1171     if (currFile->context != UNIT) {
 1172         ut_set_status(UT_PARSE);
 1173     ut_handle_error_message("Wrong place for <base> element");
 1174     XML_StopParser(currFile->parser, 0);
 1175     }
 1176     else {
 1177     if (currFile->isDimensionless) {
 1178             ut_set_status(UT_PARSE);
 1179         ut_handle_error_message(
 1180         "<dimensionless> and <base> are mutually exclusive");
 1181         XML_StopParser(currFile->parser, 0);
 1182     }
 1183     else if (currFile->unit != NULL) {
 1184             ut_set_status(UT_PARSE);
 1185         ut_handle_error_message("<base> and <def> are mutually exclusive");
 1186         XML_StopParser(currFile->parser, 0);
 1187     }
 1188     else if (currFile->isBase) {
 1189             ut_set_status(UT_PARSE);
 1190         ut_handle_error_message("<base> element already seen");
 1191         XML_StopParser(currFile->parser, 0);
 1192     }
 1193     }
 1194 }
 1195 
 1196 
 1197 /*
 1198  * Handles the end of a <base> element.
 1199  */
 1200 static void
 1201 endBase(
 1202     void*       data)
 1203 {
 1204     currFile->unit = ut_new_base_unit(unitSystem);
 1205 
 1206     if (currFile->unit == NULL) {
 1207         ut_set_status(UT_PARSE);
 1208     ut_handle_error_message("Couldn't create new base unit");
 1209     XML_StopParser(currFile->parser, 0);
 1210     }
 1211     else {
 1212     currFile->isBase = 1;
 1213     }
 1214 }
 1215 
 1216 
 1217 /*
 1218  * Handles the start of a <dimensionless> element.
 1219  */
 1220 static void
 1221 startDimensionless(
 1222     void*       data,
 1223     const char**    atts)
 1224 {
 1225     if (currFile->context != UNIT) {
 1226         ut_set_status(UT_PARSE);
 1227     ut_handle_error_message("Wrong place for <dimensionless> element");
 1228     XML_StopParser(currFile->parser, 0);
 1229     }
 1230     else {
 1231     if (currFile->isBase) {
 1232             ut_set_status(UT_PARSE);
 1233         ut_handle_error_message(
 1234         "<dimensionless> and <base> are mutually exclusive");
 1235         XML_StopParser(currFile->parser, 0);
 1236     }
 1237     else if (currFile->unit != NULL) {
 1238             ut_set_status(UT_PARSE);
 1239         ut_handle_error_message(
 1240         "<dimensionless> and <def> are mutually exclusive");
 1241         XML_StopParser(currFile->parser, 0);
 1242     }
 1243     else if (currFile->isDimensionless) {
 1244             ut_set_status(UT_PARSE);
 1245         ut_handle_error_message("<dimensionless> element already seen");
 1246         XML_StopParser(currFile->parser, 0);
 1247     }
 1248     }
 1249 }
 1250 
 1251 
 1252 /*
 1253  * Handles the end of a <dimensionless> element.
 1254  */
 1255 static void
 1256 endDimensionless(
 1257     void*       data)
 1258 {
 1259     currFile->unit = ut_new_dimensionless_unit(unitSystem);
 1260 
 1261     if (currFile->unit == NULL) {
 1262         ut_set_status(UT_PARSE);
 1263     ut_handle_error_message("Couldn't create new dimensionless unit");
 1264     XML_StopParser(currFile->parser, 0);
 1265     }
 1266     else {
 1267     currFile->isDimensionless = 1;
 1268     }
 1269 }
 1270 
 1271 
 1272 /*
 1273  * Handles the start of a <def> element.
 1274  */
 1275 static void
 1276 startDef(
 1277     void*       data,
 1278     const char**    atts)
 1279 {
 1280     if (currFile->context != UNIT) {
 1281         ut_set_status(UT_PARSE);
 1282     ut_handle_error_message("Wrong place for <def> element");
 1283     XML_StopParser(currFile->parser, 0);
 1284     }
 1285     else if (currFile->isBase) {
 1286         ut_set_status(UT_PARSE);
 1287     ut_handle_error_message(
 1288         "<base> and <def> are mutually exclusive");
 1289     XML_StopParser(currFile->parser, 0);
 1290     }
 1291     else if (currFile->isDimensionless) {
 1292         ut_set_status(UT_PARSE);
 1293     ut_handle_error_message(
 1294         "<dimensionless> and <def> are mutually exclusive");
 1295     XML_StopParser(currFile->parser, 0);
 1296     }
 1297     else if (currFile->unit != NULL) {
 1298         ut_set_status(UT_PARSE);
 1299     ut_handle_error_message("<def> element already seen");
 1300     XML_StopParser(currFile->parser, 0);
 1301     }
 1302     else {
 1303     clearText();
 1304         ACCUMULATE_TEXT;
 1305     }
 1306 }
 1307 
 1308 
 1309 /*
 1310  * Handles the end of a <def> element.
 1311  */
 1312 static void
 1313 endDef(
 1314     void*       data)
 1315 {
 1316     if (nbytes == 0) {
 1317         ut_set_status(UT_PARSE);
 1318     ut_handle_error_message("Empty unit definition");
 1319     XML_StopParser(currFile->parser, 0);
 1320     }
 1321     else {
 1322     currFile->unit = ut_parse(unitSystem, text, currFile->textEncoding);
 1323 
 1324     if (currFile->unit == NULL) {
 1325             ut_set_status(UT_PARSE);
 1326         ut_handle_error_message(
 1327                 "Couldn't parse unit specification \"%s\"", text);
 1328         XML_StopParser(currFile->parser, 0);
 1329     }
 1330     }
 1331 }
 1332 
 1333 
 1334 /*
 1335  * Handles the start of a <name> element.
 1336  */
 1337 static void
 1338 startName(
 1339     void*       data,
 1340     const char**    atts)
 1341 {
 1342     if (currFile->context == PREFIX) {
 1343         if (!currFile->haveValue) {
 1344             ut_set_status(UT_PARSE);
 1345             ut_handle_error_message("No previous <value> element");
 1346             XML_StopParser(currFile->parser, 0);
 1347         }
 1348         else {
 1349             clearText();
 1350             ACCUMULATE_TEXT;
 1351         }
 1352     }
 1353     else if (currFile->context == UNIT || currFile->context == ALIASES) {
 1354         if (currFile->unit == NULL) {
 1355             ut_set_status(UT_PARSE);
 1356             ut_handle_error_message(
 1357                 "No previous <base>, <dimensionless>, or <def> element");
 1358             XML_StopParser(currFile->parser, 0);
 1359         }
 1360         else {
 1361             currFile->noPLural = 0;
 1362             currFile->singular[0] = 0;
 1363            currFile->plural[0] = 0;
 1364             currFile->context =
 1365                 currFile->context == UNIT ? UNIT_NAME : ALIAS_NAME;
 1366         }
 1367     }
 1368     else {
 1369         ut_set_status(UT_PARSE);
 1370         ut_handle_error_message("Wrong place for <name> element");
 1371         XML_StopParser(currFile->parser, 0);
 1372     }
 1373 }
 1374 
 1375 
 1376 /*
 1377  * Handles the end of a <name> element.
 1378  */
 1379 static void
 1380 endName(
 1381     void*       data)
 1382 {
 1383     if (currFile->context == PREFIX) {
 1384     if (!currFile->haveValue) {
 1385             ut_set_status(UT_PARSE);
 1386         ut_handle_error_message("No previous <value> element");
 1387         XML_StopParser(currFile->parser, 0);
 1388     }
 1389     else {
 1390         if (ut_add_name_prefix(unitSystem, text, currFile->value) !=
 1391                     UT_SUCCESS) {
 1392                 ut_set_status(UT_PARSE);
 1393         ut_handle_error_message(
 1394             "Couldn't map name-prefix \"%s\" to value %g", text,
 1395                     currFile->value);
 1396         XML_StopParser(currFile->parser, 0);
 1397         }
 1398         else {
 1399         currFile->prefixAdded = 1;
 1400         }
 1401     }
 1402     }
 1403     else if (currFile->context == UNIT_NAME) {
 1404         if (currFile->singular[0] == 0) {
 1405             ut_set_status(UT_PARSE);
 1406             ut_handle_error_message("<name> needs a <singular>");
 1407             XML_StopParser(currFile->parser, 0);
 1408         }
 1409         else {
 1410             if (!mapUnitAndName(currFile->unit, currFile->singular,
 1411                     currFile->textEncoding)) {
 1412                 XML_StopParser(currFile->parser, 0);
 1413             }
 1414             else {
 1415                 if (!currFile->noPLural) {
 1416                     const char* plural = NULL;
 1417 
 1418                     if (currFile->plural[0] != 0) {
 1419                         plural = currFile->plural;
 1420                     }
 1421                     else if (currFile->singular[0] != 0) {
 1422                         plural = ut_form_plural(currFile->singular);
 1423 
 1424                         if (plural == NULL) {
 1425                             ut_set_status(UT_PARSE);
 1426                             ut_handle_error_message("Couldn't form plural of "
 1427                                 "\"%s\"", currFile->singular);
 1428                             XML_StopParser(currFile->parser, 0);
 1429                         }
 1430                     }
 1431 
 1432                     if (plural != NULL) {
 1433                         /*
 1434                          * Because the unit is already mapped to the singular
 1435                          * name, it is not mapped to the plural name.
 1436                          */
 1437                         if (!mapNamesToUnit(plural, currFile->textEncoding,
 1438                                 currFile->unit)) {
 1439                             XML_StopParser(currFile->parser, 0);
 1440                         }
 1441                     }
 1442                 }                       /* <noplural/> not specified */
 1443                 if (strcmp(currFile->singular, "second") == 0) {
 1444                     if (ut_set_second(currFile->unit) != UT_SUCCESS) {
 1445                         ut_handle_error_message(
 1446                             "Couldn't set \"second\" unit in unit-system");
 1447                         XML_StopParser(currFile->parser, 0);
 1448                     }
 1449                 }                       /* unit was 'second' unit */
 1450             }                           /* unit mapped to singular name */
 1451         }                               /* singular name specified */
 1452 
 1453         currFile->nameSeen = 1;
 1454         currFile->context = UNIT;
 1455     }                   /* defining name for unit */
 1456     else if (currFile->context == ALIAS_NAME) {
 1457     if (currFile->singular[0] == 0) {
 1458             ut_set_status(UT_PARSE);
 1459             ut_handle_error_message("<name> needs a <singular>");
 1460             XML_StopParser(currFile->parser, 0);
 1461         }
 1462         else {
 1463             if (!mapNamesToUnit(currFile->singular, currFile->textEncoding,
 1464                     currFile->unit)) {
 1465                 XML_StopParser(currFile->parser, 0);
 1466             }
 1467 
 1468             if (!currFile->noPLural) {
 1469                 const char* plural = NULL;
 1470 
 1471                 if (currFile->plural[0] != 0) {
 1472                     plural = currFile->plural;
 1473                 }
 1474                 else if (currFile->singular[0] != 0) {
 1475                     plural = ut_form_plural(currFile->singular);
 1476 
 1477                     if (plural == NULL) {
 1478                         ut_set_status(UT_PARSE);
 1479                         ut_handle_error_message("Couldn't form plural of "
 1480                             "\"%s\"", currFile->singular);
 1481                         XML_StopParser(currFile->parser, 0);
 1482                     }
 1483                 }
 1484 
 1485                 if (plural != NULL) {
 1486                     if (!mapNamesToUnit(plural, currFile->textEncoding,
 1487                             currFile->unit))
 1488                         XML_StopParser(currFile->parser, 0);
 1489                 }
 1490             }                           /* <noplural> not specified */
 1491         }                               /* singular name specified */
 1492 
 1493         currFile->context = ALIASES;
 1494     }                                   /* defining name for alias */
 1495     else {
 1496     assert(0);
 1497     }
 1498 }
 1499 
 1500 
 1501 /*
 1502  * Handles the start of a <singular> element.
 1503  */
 1504 static void
 1505 startSingular(
 1506     void*       data,
 1507     const char**    atts)
 1508 {
 1509     if (currFile->context != UNIT_NAME && currFile->context != ALIAS_NAME) {
 1510         ut_set_status(UT_PARSE);
 1511     ut_handle_error_message("Wrong place for <singular> element");
 1512     XML_StopParser(currFile->parser, 0);
 1513     }
 1514     else if (currFile->singular[0] != 0) {
 1515         ut_set_status(UT_PARSE);
 1516     ut_handle_error_message("<singular> element already seen");
 1517     XML_StopParser(currFile->parser, 0);
 1518     }
 1519     else {
 1520     clearText();
 1521         ACCUMULATE_TEXT;
 1522     }
 1523 }
 1524 
 1525 
 1526 /*
 1527  * Handles the end of a <singular> element.
 1528  */
 1529 static void
 1530 endSingular(
 1531     void*       data)
 1532 {
 1533     if (nbytes >= NAME_SIZE) {
 1534         ut_set_status(UT_PARSE);
 1535         ut_handle_error_message("Name \"%s\" is too long", text);
 1536         XML_StopParser(currFile->parser, 0);
 1537     }
 1538     else {
 1539         (void)strncpy(currFile->singular, text, NAME_SIZE);
 1540     }
 1541 }
 1542 
 1543 
 1544 /*
 1545  * Handles the start of a <plural> element.
 1546  */
 1547 static void
 1548 startPlural(
 1549     void*       data,
 1550     const char**    atts)
 1551 {
 1552     if (currFile->context != UNIT_NAME && currFile->context != ALIAS_NAME ) {
 1553         ut_set_status(UT_PARSE);
 1554     ut_handle_error_message("Wrong place for <plural> element");
 1555     XML_StopParser(currFile->parser, 0);
 1556     }
 1557     else if (currFile->noPLural || currFile->plural[0] != 0) {
 1558         ut_set_status(UT_PARSE);
 1559     ut_handle_error_message("<plural> or <noplural> element already seen");
 1560     XML_StopParser(currFile->parser, 0);
 1561     }
 1562     else {
 1563     clearText();
 1564         ACCUMULATE_TEXT;
 1565     }
 1566 }
 1567 
 1568 
 1569 /*
 1570  * Handles the end of a <plural> element.
 1571  */
 1572 static void
 1573 endPlural(
 1574     void*       data)
 1575 {
 1576     if (nbytes == 0) {
 1577         ut_set_status(UT_PARSE);
 1578         ut_handle_error_message("Empty <plural> element");
 1579         XML_StopParser(currFile->parser, 0);
 1580     }
 1581     else if (nbytes >= NAME_SIZE) {
 1582         ut_set_status(UT_PARSE);
 1583         ut_handle_error_message("Plural name \"%s\" is too long", text);
 1584         XML_StopParser(currFile->parser, 0);
 1585     }
 1586     else {
 1587         (void)strncpy(currFile->plural, text, NAME_SIZE);
 1588     }
 1589 }
 1590 
 1591 
 1592 /*
 1593  * Handles the start of a <noplural> element.
 1594  */
 1595 static void
 1596 startNoPlural(
 1597     void*       data,
 1598     const char**    atts)
 1599 {
 1600     if (currFile->context != UNIT_NAME && currFile->context != ALIAS_NAME) {
 1601         ut_set_status(UT_PARSE);
 1602     ut_handle_error_message("Wrong place for <noplural> element");
 1603     XML_StopParser(currFile->parser, 0);
 1604     }
 1605     else if (currFile->plural[0] != 0) {
 1606         ut_set_status(UT_PARSE);
 1607     ut_handle_error_message("<plural> element already seen");
 1608     XML_StopParser(currFile->parser, 0);
 1609     }
 1610 }
 1611 
 1612 
 1613 /*
 1614  * Handles the end of a <noplural> element.
 1615  */
 1616 static void
 1617 endNoPlural(
 1618     void*       data)
 1619 {
 1620     currFile->noPLural = 1;
 1621 }
 1622 
 1623 
 1624 /*
 1625  * Handles the start of a <symbol> element.
 1626  */
 1627 static void
 1628 startSymbol(
 1629     void*       data,
 1630     const char**    atts)
 1631 {
 1632     if (currFile->context == PREFIX) {
 1633         if (!currFile->haveValue) {
 1634             ut_set_status(UT_PARSE);
 1635             ut_handle_error_message("No previous <value> element");
 1636             XML_StopParser(currFile->parser, 0);
 1637         }
 1638         else {
 1639             clearText();
 1640             ACCUMULATE_TEXT;
 1641         }
 1642     }
 1643     else if (currFile->context == UNIT || currFile->context == ALIASES) {
 1644         if (currFile->unit == NULL) {
 1645             ut_set_status(UT_PARSE);
 1646             ut_handle_error_message(
 1647                 "No previous <base>, <dimensionless>, or <def> element");
 1648             XML_StopParser(currFile->parser, 0);
 1649         }
 1650         else {
 1651             clearText();
 1652             ACCUMULATE_TEXT;
 1653         }
 1654     }
 1655     else {
 1656         ut_set_status(UT_PARSE);
 1657         ut_handle_error_message("Wrong place for <symbol> element");
 1658         XML_StopParser(currFile->parser, 0);
 1659     }
 1660 }
 1661 
 1662 
 1663 /*
 1664  * Handles the end of a <symbol> element.
 1665  */
 1666 static void
 1667 endSymbol(
 1668     void*       data)
 1669 {
 1670     if (currFile->context == PREFIX) {
 1671         if (ut_add_symbol_prefix(unitSystem, text, currFile->value) !=
 1672                 UT_SUCCESS) {
 1673             ut_set_status(UT_PARSE);
 1674             ut_handle_error_message(
 1675                 "Couldn't map symbol-prefix \"%s\" to value %g",
 1676                 text, currFile->value);
 1677             XML_StopParser(currFile->parser, 0);
 1678         }
 1679         else {
 1680             currFile->prefixAdded = 1;
 1681         }
 1682     }
 1683     else if (currFile->context == UNIT) {
 1684         if (!mapUnitAndSymbol(currFile->unit, text, currFile->textEncoding))
 1685             XML_StopParser(currFile->parser, 0);
 1686 
 1687         currFile->symbolSeen = 1;
 1688     }
 1689     else if (currFile->context == ALIASES) {
 1690         if (!mapSymbolsToUnit(text, currFile->textEncoding, currFile->unit))
 1691             XML_StopParser(currFile->parser, 0);
 1692     }
 1693 }
 1694 
 1695 
 1696 /*
 1697  * Handles the start of a <value> element.
 1698  */
 1699 static void
 1700 startValue(
 1701     void*       data,
 1702     const char**    atts)
 1703 {
 1704     if (currFile->context != PREFIX) {
 1705         ut_set_status(UT_PARSE);
 1706     ut_handle_error_message("Wrong place for <value> element");
 1707     XML_StopParser(currFile->parser, 0);
 1708     }
 1709     else if (currFile->haveValue) {
 1710         ut_set_status(UT_PARSE);
 1711     ut_handle_error_message("<value> element already seen");
 1712     XML_StopParser(currFile->parser, 0);
 1713     }
 1714     else {
 1715     clearText();
 1716         ACCUMULATE_TEXT;
 1717     }
 1718 }
 1719 
 1720 
 1721 /*
 1722  * Handles the end of a <value> element.
 1723  */
 1724 static void
 1725 endValue(
 1726     void*   data)
 1727 {
 1728     char*   endPtr;
 1729 
 1730     errno = 0;
 1731     currFile->value = strtod(text, &endPtr);
 1732 
 1733     if (errno != 0) {
 1734         ut_set_status(UT_PARSE);
 1735     ut_handle_error_message(strerror(errno));
 1736     ut_handle_error_message("Couldn't decode numeric prefix value \"%s\"",
 1737             text);
 1738     XML_StopParser(currFile->parser, 0);
 1739     }
 1740     else if (*endPtr != 0) {
 1741         ut_set_status(UT_PARSE);
 1742     ut_handle_error_message("Invalid numeric prefix value \"%s\"", text);
 1743     XML_StopParser(currFile->parser, 0);
 1744     }
 1745     else {
 1746     currFile->haveValue = 1;
 1747     }
 1748 }
 1749 
 1750 
 1751 /*
 1752  * Handles the start of an <alias> element.
 1753  */
 1754 static void
 1755 startAliases(
 1756     void*       data,
 1757     const char**    atts)
 1758 {
 1759     if (currFile->context != UNIT) {
 1760         ut_set_status(UT_PARSE);
 1761     ut_handle_error_message("Wrong place for <aliases> element");
 1762     XML_StopParser(currFile->parser, 0);
 1763     }
 1764 
 1765     currFile->context = ALIASES;
 1766 }
 1767 
 1768 
 1769 /*
 1770  * Handles the end of an <alias> element.
 1771  */
 1772 static void
 1773 endAliases(
 1774     void*       data)
 1775 {
 1776     currFile->context = UNIT;
 1777 }
 1778 
 1779 
 1780 /*
 1781  * Handles the start of an <import> element.
 1782  */
 1783 static void
 1784 startImport(
 1785     void*       data,
 1786     const char**    atts)
 1787 {
 1788     if (currFile->context != UNIT_SYSTEM) {
 1789         ut_set_status(UT_PARSE);
 1790     ut_handle_error_message("Wrong place for <import> element");
 1791     XML_StopParser(currFile->parser, 0);
 1792     }
 1793     else {
 1794     clearText();
 1795         ACCUMULATE_TEXT;
 1796     }
 1797 }
 1798 
 1799 
 1800 /*
 1801  * Handles the end of an <import> element.
 1802  */
 1803 static void
 1804 endImport(
 1805     void*       data)
 1806 {
 1807     char        buf[PATH_MAX];
 1808     const char* path;
 1809 
 1810     if (text[0] == '/') {
 1811         path = text;
 1812     }
 1813     else {
 1814         (void)snprintf(buf, sizeof(buf),
 1815 #ifdef _MSC_VER
 1816             // The directory pathname has a trailing backslash on Windows
 1817             "%s%s",
 1818 #else
 1819             "%s/%s",
 1820 #endif
 1821             XML_GetBase(currFile->parser), text);
 1822 
 1823         buf[sizeof(buf)-1] = 0;
 1824         path = buf;
 1825     }
 1826 
 1827     ut_set_status(readXml(path));
 1828 
 1829     if (ut_get_status() != UT_SUCCESS)
 1830         XML_StopParser(currFile->parser, 0);
 1831 }
 1832 
 1833 
 1834 /*
 1835  * Handles the start of an element.
 1836  */
 1837 static void
 1838 startElement(
 1839     void*       data,
 1840     const XML_Char* name,
 1841     const XML_Char**    atts)
 1842 {
 1843     if (currFile->skipDepth) {
 1844     currFile->skipDepth++;
 1845     }
 1846     else {
 1847     clearText();
 1848 
 1849     if (strcasecmp(name, "unit-system") == 0) {
 1850         startUnitSystem(data, atts);
 1851     }
 1852     else if (strcasecmp(name, "prefix") == 0) {
 1853         startPrefix(data, atts);
 1854     }
 1855     else if (strcasecmp(name, "unit") == 0) {
 1856         startUnit(data, atts);
 1857     }
 1858     else if (strcasecmp(name, "base") == 0) {
 1859         startBase(data, atts);
 1860     }
 1861     else if (strcasecmp(name, "dimensionless") == 0) {
 1862         startDimensionless(data, atts);
 1863     }
 1864     else if (strcasecmp(name, "def") == 0) {
 1865         startDef(data, atts);
 1866     }
 1867     else if (strcasecmp(name, "value") == 0) {
 1868         startValue(data, atts);
 1869     }
 1870     else if (strcasecmp(name, "name") == 0) {
 1871         startName(data, atts);
 1872     }
 1873     else if (strcasecmp(name, "singular") == 0) {
 1874         startSingular(data, atts);
 1875     }
 1876     else if (strcasecmp(name, "plural") == 0) {
 1877         startPlural(data, atts);
 1878     }
 1879     else if (strcasecmp(name, "symbol") == 0) {
 1880         startSymbol(data, atts);
 1881     }
 1882     else if (strcasecmp(name, "aliases") == 0) {
 1883         startAliases(data, atts);
 1884     }
 1885     else if (strcasecmp(name, "import") == 0) {
 1886         startImport(data, atts);
 1887     }
 1888     else {
 1889         currFile->skipDepth = 1;
 1890     }
 1891     }
 1892 }
 1893 
 1894 
 1895 /*
 1896  * Handles the end of an element.
 1897  */
 1898 static void
 1899 endElement(
 1900     void*       data,
 1901     const XML_Char* name)
 1902 {
 1903     if (currFile->skipDepth != 0) {
 1904     --currFile->skipDepth;
 1905     }
 1906     else {
 1907         if (strcasecmp(name, "unit-system") == 0) {
 1908             endUnitSystem(data);
 1909         }
 1910         else if (strcasecmp(name, "prefix") == 0) {
 1911         endPrefix(data);
 1912     }
 1913     else if (strcasecmp(name, "unit") == 0) {
 1914         endUnit(data);
 1915     }
 1916     else if (strcasecmp(name, "base") == 0) {
 1917         endBase(data);
 1918     }
 1919     else if (strcasecmp(name, "dimensionless") == 0) {
 1920         endDimensionless(data);
 1921     }
 1922     else if (strcasecmp(name, "def") == 0) {
 1923         endDef(data);
 1924     }
 1925     else if (strcasecmp(name, "value") == 0) {
 1926         endValue(data);
 1927     }
 1928     else if (strcasecmp(name, "name") == 0) {
 1929         endName(data);
 1930     }
 1931     else if (strcasecmp(name, "singular") == 0) {
 1932         endSingular(data);
 1933     }
 1934     else if (strcasecmp(name, "plural") == 0) {
 1935         endPlural(data);
 1936     }
 1937     else if (strcasecmp(name, "symbol") == 0) {
 1938         endSymbol(data);
 1939     }
 1940     else if (strcasecmp(name, "aliases") == 0) {
 1941         endAliases(data);
 1942     }
 1943     else if (strcasecmp(name, "import") == 0) {
 1944         endImport(data);
 1945     }
 1946     else {
 1947             ut_set_status(UT_PARSE);
 1948         ut_handle_error_message("Unknown element \"<%s>\"", name);
 1949         XML_StopParser(currFile->parser, 0);
 1950     }
 1951     }
 1952 
 1953     IGNORE_TEXT;
 1954 }
 1955 
 1956 
 1957 /*
 1958  * Handles the header of an XML file.
 1959  */
 1960 static void
 1961 declareXml(
 1962     void*   data,
 1963     const char* version,
 1964     const char* encoding,
 1965     int     standalone)
 1966 {
 1967     if (strcasecmp(encoding, "US-ASCII") == 0) {
 1968     currFile->xmlEncoding = UT_ASCII;
 1969     }
 1970     else if (strcasecmp(encoding, "ISO-8859-1") == 0) {
 1971     currFile->xmlEncoding = UT_LATIN1;
 1972     }
 1973     else if (strcasecmp(encoding, "UTF-8") == 0) {
 1974     currFile->xmlEncoding = UT_UTF8;
 1975     }
 1976     else {
 1977         ut_set_status(UT_PARSE);
 1978     ut_handle_error_message("Unknown XML encoding \"%s\"", encoding);
 1979     XML_StopParser(currFile->parser, 0);
 1980     }
 1981 }
 1982 
 1983 
 1984 /*
 1985  * Reads an XML file into the unit-system with the given XML parser.
 1986  *
 1987  * Arguments:
 1988  *      parser          Pointer to the XML parser.
 1989  *      path            Pointer to the pathname of the XML file.
 1990  * Returns:
 1991  *      UT_SUCCESS      Success.
 1992  *      UT_OPEN_ARG     File "path" couldn't be opened.  See "errno".
 1993  *      UT_OS           Operating-system error.  See "errno".
 1994  *      UT_PARSE        Parse failure.
 1995  */
 1996 static ut_status
 1997 readXmlWithParser(
 1998     XML_Parser          parser,
 1999     const char* const   path)
 2000 {
 2001     ut_status   status = UT_SUCCESS;    /* success */
 2002     File        file;
 2003 
 2004     assert(parser != NULL);
 2005     assert(path != NULL);
 2006 
 2007     fileInit(&file);
 2008 
 2009     file.fd = open(path, O_RDONLY);
 2010 
 2011     if (file.fd == -1) {
 2012         status = UT_OPEN_ARG;
 2013         ut_set_status(status);
 2014         ut_handle_error_message(strerror(errno));
 2015         ut_handle_error_message("Couldn't open file \"%s\"", path);
 2016     }
 2017     else {
 2018         int         nbytes;
 2019         File* const     prevFile = currFile;
 2020 
 2021         file.path = path;
 2022         file.parser = parser;
 2023         currFile = &file;
 2024 
 2025         do {
 2026             char    buf[BUFSIZ];    /* from <stdio.h> */
 2027 
 2028             nbytes = read(file.fd, buf, sizeof(buf));
 2029 
 2030             if (nbytes < 0) {
 2031                 status = UT_OS;
 2032                 ut_set_status(status);
 2033                 ut_handle_error_message(strerror(errno));
 2034             }
 2035             else {
 2036                 if (XML_Parse(file.parser, buf, nbytes, nbytes == 0)
 2037                         != XML_STATUS_OK) {
 2038                     status = UT_PARSE;
 2039                     ut_set_status(status);
 2040                     ut_handle_error_message(
 2041                         XML_ErrorString(XML_GetErrorCode(file.parser)));
 2042                 }
 2043             }
 2044         } while (status == UT_SUCCESS && nbytes > 0);
 2045 
 2046         if (status != UT_SUCCESS) {
 2047             /*
 2048              * Parsing of the XML file terminated prematurely.
 2049              */
 2050             ut_handle_error_message("File \"%s\", line %d, column %d",
 2051                 path, XML_GetCurrentLineNumber(file.parser),
 2052                 XML_GetCurrentColumnNumber(file.parser));
 2053         }
 2054 
 2055         currFile = prevFile;
 2056 
 2057         (void)close(file.fd);
 2058         file.fd = -1;
 2059     }                                   /* "file.fd" open */
 2060 
 2061     return status;
 2062 }
 2063 
 2064 
 2065 /*
 2066  * Reads an XML file into the unit-system.
 2067  *
 2068  * Arguments:
 2069  *      path            Pointer to the pathname of the XML file.
 2070  * Returns:
 2071  *      UT_SUCCESS      Success.
 2072  *      UT_OPEN_ARG     File "path" couldn't be opened.  See "errno".
 2073  *      UT_OS           Operating-system error.  See "errno".
 2074  *      UT_PARSE        Parse failure.
 2075  */
 2076 static ut_status
 2077 readXml(
 2078     const char* const   path)
 2079 {
 2080     ut_status       status;
 2081     XML_Parser      parser = XML_ParserCreate(NULL);
 2082 
 2083     if (parser == NULL) {
 2084         status = UT_OS;
 2085         ut_set_status(status);
 2086         ut_handle_error_message(strerror(errno));
 2087         ut_handle_error_message("Couldn't create XML parser");
 2088     }
 2089     else {
 2090         char base[PATH_MAX];
 2091 #ifdef _MSC_VER
 2092         {
 2093             char drive[_MAX_DRIVE+1]; // Will have trailing colon
 2094             char directory[_MAX_DIR+1]; // Will have trailing backslash
 2095             _splitpath(path, drive, directory, NULL, NULL);
 2096             (void)snprintf(base, sizeof(base), "%s%s", drive, directory);
 2097             base[sizeof(base)-1] = 0;
 2098         }
 2099 #else
 2100         {
 2101             // Temporary buffer used because `dirname()` modifies its argument
 2102             char tmp[strlen(path)+1];
 2103             (void)strcpy(tmp, path);
 2104             (void)strncpy(base, dirname(tmp), sizeof(base));
 2105             base[sizeof(base)-1] = 0;
 2106         }
 2107 #endif
 2108 
 2109         if (XML_SetBase(parser, base) != XML_STATUS_OK) {
 2110             status = UT_OS;
 2111             ut_set_status(status);
 2112             ut_handle_error_message(strerror(errno));
 2113             ut_handle_error_message("XML_SetBase(\"%s\") failure", base);
 2114         }
 2115         else {
 2116             XML_SetXmlDeclHandler(parser, declareXml);
 2117             XML_SetElementHandler(parser, startElement, endElement);
 2118             XML_SetCharacterDataHandler(parser, NULL);
 2119 
 2120             status = readXmlWithParser(parser, path);
 2121         }                           /* parser "base" set */
 2122 
 2123         XML_ParserFree(parser);
 2124     }                               /* parser != NULL */
 2125 
 2126     return status;
 2127 }
 2128 
 2129 
 2130 /* A bit hacky but much better than modifying binaries. */
 2131 static const char*
 2132 default_udunits2_xml_path()
 2133 {
 2134     // Returned absolute pathname of XML database
 2135     static char absXmlPathname[PATH_MAX];
 2136 
 2137     if (absXmlPathname[0] == 0) {
 2138         const char* prefix = NULL; // Installation directory
 2139 
 2140 #       if defined(__APPLE__) || defined(__linux__)
 2141             Dl_info     info;
 2142             const char  sep = '/'; // Pathname component separator
 2143             char        buf[PATH_MAX];
 2144             const char  relXmlPathname[] = "share/udunits/udunits2.xml";
 2145 
 2146             // The following should get pathname of shared-library
 2147             if (!dladdr(default_udunits2_xml_path, &info)) {
 2148                 prefix = NULL;
 2149             }
 2150             else {
 2151                 strncpy(buf, info.dli_fname, sizeof(buf))[sizeof(buf)-1] = 0;
 2152                 memmove(buf, dirname(buf), sizeof(buf)); // "lib"
 2153                 memmove(buf, dirname(buf), sizeof(buf)); // "lib/.."
 2154                 prefix = buf;
 2155             }
 2156 #       elif defined(_WIN32)
 2157             const char sep = '\\'; // Pathname component separator
 2158             char       buf[MAX_PATH * 4];
 2159             const char relXmlPathname[] = "share\\udunits\\udunits2.xml";
 2160             HMODULE    hModule = NULL;
 2161 
 2162             GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
 2163                     (LPCTSTR)default_udunits2_xml_path, &hModule);
 2164 
 2165             GetModuleFileName(hModule, buf, sizeof(buf)); // Library pathname
 2166             *strrchr(buf, sep) = 0; // "lib"
 2167             *strrchr(buf, sep) = 0; // "lib/.."
 2168 
 2169             prefix = buf;
 2170 #       endif
 2171 
 2172         if (prefix == NULL) {
 2173             strncpy(absXmlPathname, DEFAULT_UDUNITS2_XML_PATH,
 2174                     sizeof(absXmlPathname));
 2175         } // Use default pathname
 2176         else {
 2177             int prefixLen = strlen(prefix);
 2178             if (prefix[prefixLen-1] == sep) {
 2179                 --prefixLen;
 2180                 if (prefix[prefixLen-1] == sep)
 2181                     --prefixLen;
 2182             }
 2183 
 2184             snprintf(absXmlPathname, sizeof(absXmlPathname), "%.*s%c%s",
 2185                     prefixLen, prefix, sep, relXmlPathname);
 2186         } // Use computed pathname
 2187 
 2188         absXmlPathname[sizeof(absXmlPathname)-1] = 0; // Ensure NUL terminated
 2189     } // `absXmlPathname` not set
 2190 
 2191     return absXmlPathname;
 2192 }
 2193 
 2194 /**
 2195  * Returns the pathname of the XML database.
 2196  *
 2197  * @param path      The pathname of the XML file or NULL.
 2198  * @param status    Status. One of UT_OPEN_ARG, UT_OPEN_ENV, or UT_OPEN_DEFAULT.
 2199  * @return          If "path" is not NULL, then it is returned; otherwise, the
 2200  *                  pathname specified by the environment variable
 2201  *                  UDUNITS2_XML_PATH is returned if set; otherwise, the
 2202  *                  compile-time pathname of the installed, default, unit
 2203  *                  database is returned. Caller must not free.
 2204  */
 2205 const char*
 2206 ut_get_path_xml(
 2207     const char* path,
 2208     ut_status*  status)
 2209 {
 2210     if (path != NULL) {
 2211         *status = UT_OPEN_ARG;
 2212     }
 2213     else {
 2214         path = getenv("UDUNITS2_XML_PATH");
 2215 
 2216         if (path != NULL) {
 2217             *status = UT_OPEN_ENV;
 2218         }
 2219         else {
 2220             path = default_udunits2_xml_path();
 2221             *status = UT_OPEN_DEFAULT;
 2222         }
 2223     }
 2224     return path;
 2225 }
 2226 
 2227 
 2228 /**
 2229  * Returns the unit-system corresponding to an XML file.  This is the usual way
 2230  * that a client will obtain a unit-system.
 2231  *
 2232  * @param path  The pathname of the XML file or NULL.  If NULL, then the
 2233  *              pathname specified by the environment variable UDUNITS2_XML_PATH
 2234  *              is used if set; otherwise, the compile-time pathname of the
 2235  *              installed, default, unit database is used.
 2236  * @retval NULL Failure. "ut_get_status()" will be one of the following:
 2237  *                  UT_OPEN_ARG     "path" is non-NULL but file couldn't be
 2238  *                                  opened. See "errno" for reason.
 2239  *                  UT_OPEN_ENV     "path" is NULL and environment variable
 2240  *                                  UDUNITS2_XML_PATH is set but file couldn't
 2241  *                                  be opened.  See "errno" for reason.
 2242  *                  UT_OPEN_DEFAULT "path" is NULL, environment variable
 2243  *                                  UDUNITS2_XML_PATH is unset, and the
 2244  *                                  installed, default, unit database couldn't
 2245  *                                  be opened. See "errno" for reason.
 2246  *                  UT_PARSE        Couldn't parse unit database.
 2247  *                  UT_OS           Operating-system error.  See "errno".
 2248  * @return      Pointer to the unit-system defined by "path".
 2249  */
 2250 ut_system*
 2251 ut_read_xml(
 2252     const char* path)
 2253 {
 2254     ut_set_status(UT_SUCCESS);
 2255 
 2256     unitSystem = ut_new_system();
 2257 
 2258     if (unitSystem == NULL) {
 2259         ut_handle_error_message("Couldn't create new unit-system");
 2260     }
 2261     else {
 2262         ut_status       status;
 2263         ut_status       openError;
 2264 
 2265         status = readXml(ut_get_path_xml(path, &openError));
 2266 
 2267         if (status == UT_OPEN_ARG) {
 2268             status = openError;
 2269         }
 2270         if (status != UT_SUCCESS) {
 2271             ut_free_system(unitSystem);
 2272             unitSystem = NULL;
 2273         }
 2274 
 2275         ut_set_status(status);
 2276     }                       /* unitSystem != NULL */
 2277 
 2278     return unitSystem;
 2279 }