"Fossies" - the Fresh Open Source Software Archive

Member "udunits-2.2.28/lib/formatter.c" (8 Dec 2020, 38899 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 "formatter.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.
   10  */
   11 /*LINTLIBRARY*/
   12 
   13 #include "config.h"
   14 
   15 #include "udunits2.h"
   16 #include "unitToIdMap.h"
   17 
   18 #ifdef _MSC_VER
   19 #define _USE_MATH_DEFINES
   20 #endif
   21 
   22 #include <ctype.h>
   23 #include <float.h>
   24 #include <limits.h>
   25 #include <math.h>
   26 #include <stddef.h>
   27 #include <stdio.h>
   28 #include <stdlib.h>
   29 #include <string.h>
   30 #include <time.h>
   31 
   32 typedef const char* (*IdGetter)(const ut_unit*, ut_encoding);
   33 typedef int     (*ProductPrinter)(const ut_unit* const*, const int*,
   34     int, char*, size_t, IdGetter);
   35 
   36 /*
   37  * Formatting parameters:
   38  */
   39 typedef struct {
   40     IdGetter        getId;
   41     ProductPrinter  printProduct;
   42     char*       buf;
   43     size_t      size;
   44     int         getDefinition;
   45     ut_encoding     encoding;
   46     int         addParens;
   47     int         nchar;
   48 } FormatPar;
   49 
   50 #undef ABS
   51 #define ABS(x)          ((x) < 0 ? -(x) : (x))
   52 #define RETURNS_NAME(getId) ((getId) == getName)
   53 #define SUBTRACT_SIZET(a, b)    ((a) > (b) ? (a) - (b) : 0)
   54 
   55 static int
   56 asciiPrintProduct(
   57     const ut_unit* const* const basicUnits,
   58     const int* const        powers,
   59     const int           count,
   60     char* const         buf,
   61     size_t              max,
   62     IdGetter            getId);
   63 static int
   64 latin1PrintProduct(
   65     const ut_unit* const* const basicUnits,
   66     const int* const        powers,
   67     const int           count,
   68     char* const         buf,
   69     size_t              max,
   70     IdGetter            getId);
   71 static int
   72 utf8PrintProduct(
   73     const ut_unit* const* const basicUnits,
   74     const int* const        powers,
   75     const int           count,
   76     char* const         buf,
   77     size_t              max,
   78     IdGetter            getId);
   79 
   80 static ut_visitor   formatter;
   81 
   82 
   83 /*
   84  * Returns a name for a unit.
   85  *
   86  * Arguments:
   87  *  unit        Pointer to the unit to have it's name returned.
   88  *  encoding    The encoding of the name to be returned.
   89  * Returns:
   90  *  NULL        A name is not available in the desired encoding.
   91  *  else        Pointer to the name.
   92  */
   93 static const char*
   94 getName(
   95     const ut_unit* const    unit,
   96     const ut_encoding   encoding)
   97 {
   98     const char* name;
   99 
  100     name = ut_get_name(unit, encoding);
  101 
  102     if (name == NULL)
  103     name = ut_get_name(unit, UT_ASCII);
  104 
  105     return name;
  106 }
  107 
  108 
  109 /*
  110  * Returns a symbol for a unit.
  111  *
  112  * Arguments:
  113  *  unit        Pointer to the unit to have it's symbol returned.
  114  *  encoding    The encoding of the symbol to be returned.
  115  * Returns:
  116  *  NULL        A symbol is not available in the desired encoding.
  117  *  else        Pointer to the symbol.
  118  */
  119 static const char*
  120 getSymbol(
  121     const ut_unit* const    unit,
  122     const ut_encoding   encoding)
  123 {
  124     const char* symbol;
  125 
  126     symbol = ut_get_symbol(unit, encoding);
  127 
  128     if (symbol == NULL)
  129     symbol = ut_get_symbol(unit, UT_ASCII);
  130 
  131     return symbol;
  132 }
  133 
  134 
  135 /*
  136  * Formats a unit.
  137  *
  138  * Arguments:
  139  *  unit        Pointer to the unit to be formatted.
  140  *  buf     Pointer to the buffer into which to print the formatted
  141  *          unit.
  142  *  size        Size of the buffer.
  143  *  useNames    Use unit names rather than unit symbols.
  144  *  getDefinition   Returns the definition of "unit" in terms of basic
  145  *          units.
  146  *  encoding    The type of encoding to use.
  147  *  addParens   Whether or not to add bracketing parentheses if
  148  *          whitespace is printed.
  149  * Returns:
  150  *  -1          Failure:  "utFormStatus()" will be
  151  *                  UT_BAD_ARG  "unit" is NULL or "buf" is NULL.
  152  *  else            Success. Number of bytes that would be printed if
  153  *                  "size" were sufficiently large excluding the
  154  *                  terminating NUL.
  155  */
  156 static int
  157 format(
  158     const ut_unit* const    unit,
  159     char*               buf,
  160     size_t              size,
  161     const int               useNames,
  162     const int               getDefinition,
  163     ut_encoding             encoding,
  164     const int               addParens)
  165 {
  166     int nchar = -1; /* failure */
  167 
  168     if (unit == NULL) {
  169     ut_set_status(UT_BAD_ARG);
  170     ut_handle_error_message("format(): NULL unit argument");
  171     }
  172     else if (buf == NULL) {
  173     ut_set_status(UT_BAD_ARG);
  174     ut_handle_error_message("format(): NULL buffer argument");
  175     }
  176     else {
  177     FormatPar   formatPar;
  178 
  179     formatPar.buf = buf;
  180     formatPar.size = size;
  181     formatPar.getId = useNames ? getName : getSymbol;
  182     formatPar.getDefinition = getDefinition;
  183     formatPar.encoding = encoding;
  184     formatPar.printProduct =
  185         encoding == UT_ASCII
  186         ? asciiPrintProduct
  187         : encoding == UT_LATIN1
  188             ? latin1PrintProduct
  189             : utf8PrintProduct;
  190     formatPar.addParens = addParens;
  191     formatPar.nchar = 0;
  192 
  193     if (ut_accept_visitor(unit, &formatter, &formatPar) == UT_SUCCESS)
  194         nchar = formatPar.nchar;
  195     }
  196 
  197     return nchar;
  198 }
  199 
  200 
  201 /*******************************************************************************
  202  * Basic-Unit Formatting:
  203  ******************************************************************************/
  204 
  205 /*
  206  * Prints a basic-unit.
  207  *
  208  * Arguments:
  209  *  unit        The basic-unit to be printed.
  210  *  buf     The buffer into which to print "unit".
  211  *  size        The size of "buf".
  212  * Returns:
  213  *  -1      Failure.  The identifier for "unit" could not be
  214  *          obtained.
  215  *  else            Success. Number of bytes that would be printed if
  216  *                  "size" were sufficiently large excluding the
  217  *                  terminating NUL.
  218  */
  219 static int
  220 printBasic(
  221     const ut_unit* const    unit,
  222     char* const             buf,
  223     const size_t            size,
  224     IdGetter                getId,
  225     ut_encoding             encoding)
  226 {
  227     const char* const   id = getId(unit, encoding);
  228 
  229     return
  230     id == NULL
  231         ? -1
  232         : snprintf(buf, size, "%s", id);
  233 }
  234 
  235 
  236 /*
  237  * Formats a basic-unit.
  238  *
  239  * Arguments:
  240  *  unit        The basic-unit to be formatted.
  241  *  arg     The formatting parameters.
  242  * Returns:
  243  *  -1      Failure.  The identifier for "unit" could not be
  244  *          obtained.
  245  *  else            Success. Number of bytes that would be printed if
  246  *                  "size" were sufficiently large excluding the
  247  *                  terminating NUL.
  248  */
  249 static ut_status
  250 formatBasic(
  251     const ut_unit* const    unit,
  252     void*               arg)
  253 {
  254     FormatPar*  formatPar = (FormatPar*)arg;
  255     int     nchar = printBasic(unit, formatPar->buf, formatPar->size,
  256     formatPar->getId, formatPar->encoding);
  257 
  258     formatPar->nchar = nchar < 0 ? nchar : formatPar->nchar + nchar;
  259 
  260     return nchar < 0 ? UT_VISIT_ERROR : UT_SUCCESS;
  261 }
  262 
  263 
  264 /*******************************************************************************
  265  * Product-Unit Formatting:
  266  ******************************************************************************/
  267 
  268 /*
  269  * Prints a product-unit using the ASCII character-set.
  270  *
  271  * Arguments:
  272  *  basicUnits  Pointer to pointers to the basic-units that constitute
  273  *          the product-unit.
  274  *  powers      Pointer to the powers associated with each basic-unit.
  275  *  count       The number of basic-units.
  276  *  buf     Pointer to the buffer into which to print the basic-
  277  *          units.
  278  *  size        The size of "buf" in bytes.
  279  *  getId       Returns the identifier for a unit.
  280  * Returns:
  281  *  -1      Failure.  See errno.
  282  *  else            Success. Number of bytes that would be printed if
  283  *                  "size" were sufficiently large excluding the
  284  *                  terminating NUL.
  285  */
  286 static int
  287 asciiPrintProduct(
  288     const ut_unit* const* const basicUnits,
  289     const int* const        powers,
  290     const int           count,
  291     char* const         buf,
  292     size_t              size,
  293     IdGetter            getId)
  294 {
  295     int     nchar = snprintf(buf, size, "%s", "");
  296 
  297     if (nchar >= 0) {
  298         int i;
  299 
  300         size = SUBTRACT_SIZET(size, nchar);
  301 
  302         for (i = 0; i < count && nchar >= 0; i++) {
  303             int n;
  304 
  305             /*
  306              * Append separator if appropriate.
  307              */
  308             if (nchar > 0) {
  309                 n = RETURNS_NAME(getId)
  310                     ? snprintf(buf+nchar, size, "%s", "-")
  311                     : snprintf(buf+nchar, size, "%s", ".");
  312 
  313                 if (n < 0) {
  314                     nchar = n;
  315                     break;
  316                 }
  317 
  318                 nchar += n;
  319                 size = SUBTRACT_SIZET(size, n);
  320             }
  321 
  322             /*
  323              * Append unit identifier.
  324              */
  325             n = printBasic(basicUnits[i], buf+nchar, size, getId, UT_ASCII);
  326 
  327             if (n < 0) {
  328                 nchar = n;
  329                 break;
  330             }
  331 
  332             nchar += n;
  333             size = SUBTRACT_SIZET(size, n);
  334 
  335             /*
  336              * Append exponent if appropriate.
  337              */
  338             if (powers[i] != 1) {
  339                 n = RETURNS_NAME(getId)
  340                     ? snprintf(buf+nchar, size, "^%d", powers[i])
  341                     : snprintf(buf+nchar, size, "%d", powers[i]);
  342 
  343                 if (n < 0) {
  344                     nchar = n;
  345                     break;
  346                 }
  347 
  348                 nchar += n;
  349                 size = SUBTRACT_SIZET(size, n);
  350             }
  351         }               /* loop over basic-units */
  352     }               /* "buf" initialized */
  353 
  354     return nchar;
  355 }
  356 
  357 
  358 /*
  359  * Prints a product of basic-units using the UTF-8 character-set.
  360  *
  361  * Arguments:
  362  *  basicUnits  Pointer to pointers to the basic-units whose product
  363  *          is to be printed.
  364  *  powers      Pointer to the powers associated with each basic-unit.
  365  *  count       The number of basic-units.
  366  *  buf     Pointer to the buffer into which to print the basic-
  367  *          units.
  368  *  size        The size of "buf" in bytes.
  369  *  getId       Returns the identifier for a unit.
  370  * Returns:
  371  *  -1      Failure.  See errno.
  372  *  else            Success. Number of bytes that would be printed if
  373  *                  "size" were sufficiently large excluding the
  374  *                  terminating NUL.
  375  */
  376 static int
  377 utf8PrintProduct(
  378     const ut_unit* const* const basicUnits,
  379     const int* const        powers,
  380     const int           count,
  381     char* const         buf,
  382     size_t              size,
  383     IdGetter            getId)
  384 {
  385     int     nchar = snprintf(buf, size, "%s", "");
  386 
  387     if (nchar >= 0) {
  388         int iBasic;
  389 
  390         size = SUBTRACT_SIZET(size, nchar);
  391 
  392         for (iBasic = 0; iBasic < count; iBasic++) {
  393             int power = powers[iBasic];
  394 
  395             if (power != 0) {
  396                 /*
  397                  * The current basic-unit must be printed.
  398                  */
  399                 int n;
  400 
  401                 if (nchar > 0) {
  402                     /*
  403                      * Append mid-dot separator.
  404                      */
  405                     n = snprintf(buf+nchar, size, "%s", "\xc2\xb7");
  406 
  407                     if (n < 0) {
  408                         nchar = n;
  409                         break;
  410                     }
  411 
  412                     nchar += n;
  413                     size = SUBTRACT_SIZET(size, n);
  414                 }
  415 
  416                 /*
  417                  * Append unit identifier.
  418                  */
  419                 n = printBasic(basicUnits[iBasic], buf+nchar, size, getId,
  420                         UT_UTF8);
  421 
  422                 if (n < 0) {
  423                     nchar = n;
  424                     break;
  425                 }
  426 
  427                 nchar += n;
  428                 size = SUBTRACT_SIZET(size, n);
  429 
  430                 if (power != 1) {
  431                     /*
  432                      * Append exponent.
  433                      */
  434                     static const char*  exponentStrings[10] = {
  435                         "\xe2\x81\xb0", /* 0 */
  436                         "\xc2\xb9", /* 1 */
  437                         "\xc2\xb2", /* 2 */
  438                         "\xc2\xb3", /* 3 */
  439                         "\xe2\x81\xb4", /* 4 */
  440                         "\xe2\x81\xb5", /* 5 */
  441                         "\xe2\x81\xb6", /* 6 */
  442                         "\xe2\x81\xb7", /* 7 */
  443                         "\xe2\x81\xb8", /* 8 */
  444                         "\xe2\x81\xb9", /* 9 */
  445                     };
  446 
  447                     if (power < 0) {
  448                         /*
  449                          * Append superscript minus sign.
  450                          */
  451                         n = snprintf(buf+nchar, size, "%s", "\xe2\x81\xbb");
  452 
  453                         if (n < 0) {
  454                             nchar = n;
  455                             break;
  456                         }
  457 
  458                         nchar += n;
  459                         size = SUBTRACT_SIZET(size, n);
  460                         power = -power;
  461                     }
  462 
  463                     /*
  464                      * Append UTF-8 encoding of exponent magnitude.
  465                      */
  466                     {
  467                         static int* digit = NULL;
  468 
  469                         digit = realloc(digit, (size_t)((sizeof(powers[0])*
  470                                         CHAR_BIT*(M_LOG10E/M_LOG2E)) + 1));
  471 
  472                         if (digit == NULL) {
  473                             nchar = -1;
  474                         }
  475                         else {
  476                             int idig = 0;
  477 
  478                             for (; power > 0; power /= 10)
  479                                 digit[idig++] = power % 10;
  480 
  481                             while (idig-- > 0) {
  482                                 n = snprintf(buf+nchar, size, "%s",
  483                                         exponentStrings[digit[idig]]);
  484 
  485                                 if (n < 0) {
  486                                     nchar = n;
  487                                     break;
  488                                 }
  489 
  490                                 nchar += n;
  491                                 size = SUBTRACT_SIZET(size, n);
  492                             }
  493 
  494                             if (nchar < 0)
  495                                 break;
  496                         }
  497                     }       /* exponent digits block */
  498                 }       /* must print exponent */
  499             }           /* must print basic-unit */
  500         }               /* loop over basic-units */
  501     }               /* "buf" initialized */
  502 
  503     return nchar;
  504 }
  505 
  506 
  507 static const int*   globalPowers = NULL;
  508 
  509 
  510 static int
  511 compareExponents(
  512     const void* i,
  513     const void* j)
  514 {
  515     return globalPowers[*(const int*)j] - globalPowers[*(const int*)i];
  516 }
  517 
  518 
  519 /*
  520  * Returns the order of basic-unit powers in decreasing order.
  521  *
  522  * Arguments:
  523  *  powers      Pointer to the powers of the basic-units.
  524  *  count       The number of powers.
  525  *  positiveCount   Pointer to pointer to the number of positive powers.
  526  *          Set on and only on success.
  527  *  negativeCount   Pointer to pointer to the number of negative powers.
  528  *          Set on and only on success.
  529  * Returns:
  530  *  NULL        Failure.  See errno.
  531  *  else        Success.  Pointer to indexes of "powers" in decreasing
  532  *          order.
  533  */
  534 static void
  535 getBasicOrder(
  536     const int* const    powers,
  537     const int       count,
  538     int* const      order,
  539     int* const      positiveCount,
  540     int* const      negativeCount)
  541 {
  542     int     nNeg = 0;
  543     int     nPos = 0;
  544     int     n = 0;
  545     int     i;
  546 
  547     for (i = 0; i < count; i++) {
  548     if (powers[i] < 0) {
  549         ++nNeg;
  550         order[n++] = i;
  551     }
  552     else if (powers[i] > 0) {
  553         ++nPos;
  554         order[n++] = i;
  555     }
  556     }
  557 
  558     *negativeCount = nNeg;
  559     *positiveCount = nPos;
  560     globalPowers = powers;
  561 
  562     qsort(order, n, sizeof(int), compareExponents);
  563 }
  564 
  565 
  566 /*
  567  * Prints the product of a set of basic-units using the ISO-8859-1 (Latin-1)
  568  * character-set.
  569  *
  570  * Arguments:
  571  *  buf     Pointer to the buffer into which to print the basic-
  572  *          units.
  573  *  size        The size of "buf" in bytes.
  574  *  basicUnits  Pointer to pointers to the basic-units.
  575  *  powers      Pointer to the powers associated with each basic-unit.
  576  *  order       Pointer to indexes of "powers".  "order[i]" is the
  577  *          index of "basicUnits" and "powers" for the "i"th
  578  *          position.
  579  *  count       The number of basic-units.
  580  *  getId       Returns the identifier for a unit.
  581  * Returns:
  582  *  -1      Failure.  See errno.
  583  *  else            Success. Number of bytes that would be printed if
  584  *                  "size" were sufficiently large excluding the
  585  *                  terminating NUL.
  586  */
  587 static int
  588 latin1PrintBasics(
  589     char* const         buf,
  590     size_t          size,
  591     const ut_unit* const*   basicUnits,
  592     const int* const        powers,
  593     const int* const        order,
  594     const int           count,
  595     IdGetter            getId)
  596 {
  597     int needSeparator = 0;
  598     int nchar = 0;
  599     int i;
  600 
  601     for (i = 0; i < count; i++) {
  602     int n;
  603     int j = order[i];
  604     int power = ABS(powers[j]);
  605 
  606     if (power != 0) {
  607         if (needSeparator) {
  608         n = snprintf(buf+nchar, size, "%s", "\xb7");    /* raised dot */
  609 
  610         if (n < 0) {
  611             nchar = n;
  612             break;
  613         }
  614 
  615         nchar += n;
  616                 size = SUBTRACT_SIZET(size, n);
  617         }
  618 
  619             /*
  620              * Append unit identifier.
  621              */
  622             n = printBasic(basicUnits[j], buf+nchar, size, getId, UT_LATIN1);
  623 
  624             if (n < 0) {
  625                 nchar = n;
  626                 break;
  627             }
  628 
  629             nchar += n;
  630             size = SUBTRACT_SIZET(size, n);
  631             needSeparator = 1;
  632 
  633             /*
  634              * Append exponent if appropriate.
  635              */
  636             if (power != 1) {
  637                 n = snprintf(buf+nchar, size, "%s",
  638                     power == 2 ? "\xb2" : "\xb3");  /* superscript 2, superscript 3 */
  639 
  640                 if (n < 0) {
  641                     nchar = n;
  642                     break;
  643                 }
  644 
  645                 nchar += n;
  646                 size = SUBTRACT_SIZET(size, n);
  647             }
  648     }       /* exponent not zero */
  649     }           /* loop over positive exponents */
  650 
  651     return nchar;
  652 }
  653 
  654 
  655 /*
  656  * Prints a product-unit using the ISO-8859-1 (Latin-1) character-set.
  657  *
  658  * Arguments:
  659  *  basicUnits  Pointer to pointers to the basic-units that constitute
  660  *          the product-unit.
  661  *  powers      Pointer to the powers associated with each basic-unit.
  662  *  count       The number of basic-units.
  663  *  buf     Pointer to the buffer into which to print the basic-
  664  *          units.
  665  *  size        The size of "buf" in bytes.
  666  *  getId       Returns the identifier for a unit.
  667  * Returns:
  668  *  -1      Failure.  See errno.
  669  *  else            Success. Number of bytes that would be printed if
  670  *                  "size" were sufficiently large excluding the
  671  *                  terminating NUL.
  672  */
  673 static int
  674 latin1PrintProduct(
  675     const ut_unit* const* const basicUnits,
  676     const int* const        powers,
  677     const int           count,
  678     char* const         buf,
  679     size_t              size,
  680     IdGetter            getId)
  681 {
  682     int             nchar;
  683     int             i;
  684 
  685     for (i = 0; i < count; i++)
  686     if (powers[i] < -3 || powers[i] > 3)
  687         break;
  688 
  689     if (i < count) {
  690     /*
  691      * At least one exponent can't be represented in ISO 8859-1.  Use
  692      * the ASCII encoding instead.
  693      */
  694     nchar = asciiPrintProduct(basicUnits, powers, count, buf, size, getId);
  695     }
  696     else {
  697     int     positiveCount;
  698     int     negativeCount;
  699     int*        order = malloc(count*sizeof(int));
  700 
  701     if (order == NULL) {
  702         nchar = -1;
  703     }
  704     else {
  705         getBasicOrder(powers, count, order, &positiveCount, &negativeCount);
  706 
  707             nchar = snprintf(buf, size, "%s", "");
  708 
  709             if (nchar >= 0 && (positiveCount + negativeCount > 0)) {
  710                 int     n;
  711 
  712                 size = SUBTRACT_SIZET(size, nchar);
  713 
  714                 if (positiveCount == 0) {
  715                     n = snprintf(buf+nchar, size, "%s", "1");
  716                     if (0 > n) {
  717                         nchar = n;
  718                     }
  719                     else {
  720                         nchar += n;
  721                         size = SUBTRACT_SIZET(size, n);
  722                     }
  723                 }
  724                 else {
  725                     n = latin1PrintBasics(buf+nchar, size, basicUnits,
  726                             powers, order, positiveCount, getId);
  727                     if (0 > n) {
  728                         nchar = n;
  729                     }
  730                     else {
  731                         nchar += n;
  732                         size = SUBTRACT_SIZET(size, n);
  733                     }
  734                 }
  735 
  736                 if (nchar >= 0 && negativeCount > 0) {
  737                     n = snprintf(buf+nchar, size, "%s",
  738                         negativeCount == 1 ? "/" : "/(");
  739                     if (0 > n) {
  740                         nchar = n;
  741                     }
  742                     else {
  743                         nchar += n;
  744                         size = SUBTRACT_SIZET(size, n);
  745 
  746                         n = latin1PrintBasics(buf+nchar, size, basicUnits,
  747                                 powers, order+positiveCount, negativeCount,
  748                                 getId);
  749                         if (0 > n) {
  750                             nchar = n;
  751                         }
  752                         else {
  753                             nchar += n;
  754                             size = SUBTRACT_SIZET(size, n);
  755 
  756                             if (negativeCount > 1) {
  757                                 n = snprintf(buf+nchar, size, "%s", ")");
  758                                 if (0 > n) {
  759                                     nchar = n;
  760                                 }
  761                                 else {
  762                                     nchar += n;
  763                                     // size = SUBTRACT_SIZET(size, n); // not used
  764                                 }
  765                             }
  766                         }
  767                     }               /* solidus appended */
  768                 }           /* positive exponents printed */
  769             }               /* "buf" initialized */
  770 
  771         (void)free(order);
  772     }               /* "order" allocated */
  773     }                   /* using Latin-1 encoding */
  774 
  775     return nchar;
  776 }
  777 
  778 
  779 /*
  780  * Prints a product-unit.
  781  *
  782  * Arguments:
  783  *  unit        Pointer to the product-unit to be formatted.
  784  *  count       The number of basic-units that constitute the
  785  *          product-unit.
  786  *  basicUnits  Pointer to pointers to the basic-units that constitute
  787  *          the product-unit.
  788  *  powers      Pointer to the powers associated with each basic-unit
  789  *          of "basicUnits".
  790  *  arg     The formatting parameters.
  791  * Returns:
  792  *  -1      Failure.  See errno.
  793  *  else            Success. Number of bytes that would be printed if
  794  *                  "size" were sufficiently large excluding the
  795  *                  terminating NUL.
  796  */
  797 static ut_status
  798 formatProduct(
  799     const ut_unit* const    unit,
  800     const int           count,
  801     const ut_unit* const* const basicUnits,
  802     const int* const        powers,
  803     void*           arg)
  804 {
  805     FormatPar*  formatPar = (FormatPar*)arg;
  806     int     nchar;
  807 
  808     if (ut_compare(unit,
  809         ut_get_dimensionless_unit_one(ut_get_system(unit))) == 0) {
  810     /*
  811      * The dimensionless unit one is special.
  812      */
  813     (void)strncpy(formatPar->buf, "1", formatPar->size);
  814     nchar = formatPar->size > 0 ? 1 : 0;
  815     }
  816     else {
  817     if (formatPar->getDefinition) {
  818         nchar = formatPar->printProduct(basicUnits, powers, count,
  819         formatPar->buf, formatPar->size, formatPar->getId);
  820     }
  821     else {
  822             const char* id = formatPar->getId(unit, formatPar->encoding);
  823 
  824             nchar =
  825                 id == NULL
  826                     ? formatPar->printProduct(basicUnits, powers, count,
  827                         formatPar->buf, formatPar->size, formatPar->getId)
  828                     : snprintf(formatPar->buf, formatPar->size, "%s", id);
  829     }
  830     }
  831     formatPar->nchar = nchar < 0 ? nchar : formatPar->nchar + nchar;
  832 
  833     return nchar < 0 ? UT_VISIT_ERROR : UT_SUCCESS;
  834 }
  835 
  836 
  837 /*******************************************************************************
  838  * Galilean-Unit Formatting:
  839  ******************************************************************************/
  840 
  841 /*
  842  * Prints a Galilean-unit.
  843  *
  844  * Arguments:
  845  *  scale       The number of "unit"s in the Galilean-unit.
  846  *  unit        Pointer to the unit underlying the Galilean-unit.
  847  *  offset      The offset of the Galilean-unit in units of "unit".
  848  *  buf     Pointer to the buffer into which to print the Galilean-
  849  *          unit.
  850  *  size        The size of "buf" in bytes.
  851  *  getId       Returns the identifier for a unit.
  852  *  getDefinition   Returns the definition of "unit" in terms of basic
  853  *          units.
  854  *  encoding    The type of encoding to use.
  855  *  addParens   Whether or not to add bracketing parentheses if
  856  *          whitespace is printed.
  857  * Returns:
  858  *  -1      Failure.  See errno.
  859  *  else            Success. Number of bytes that would be printed if
  860  *                  "size" were sufficiently large excluding the
  861  *                  terminating NUL.
  862  */
  863 static int
  864 printGalilean(
  865     double                  scale,
  866     const ut_unit* const    unit,
  867     double                  offset,
  868     char* const             buf,
  869     size_t                  size,
  870     IdGetter                getId,
  871     const int               getDefinition,
  872     const ut_encoding       encoding,
  873     const int               addParens)
  874 {
  875     int         n;
  876     int         nchar = 0;
  877     int         needParens = 0;
  878 
  879     if (scale != 1) {
  880         needParens = addParens;
  881         n = snprintf(buf, size, needParens ? "(%.*g " : "%.*g ", DBL_DIG,
  882                 scale);
  883         if (0 > n) {
  884             nchar = n;
  885         }
  886         else {
  887             nchar += n;
  888             size = SUBTRACT_SIZET(size, n);
  889         }
  890     }
  891 
  892     if (0 <= nchar) {
  893         n = format(unit, buf+nchar, size, RETURNS_NAME(getId),
  894                 getDefinition, encoding, 1);
  895 
  896         if (n < 0) {
  897             nchar = n;
  898         }
  899         else {
  900             nchar += n;
  901             size = SUBTRACT_SIZET(size, n);
  902 
  903             if (offset != 0) {
  904                 needParens = addParens;
  905                 n = RETURNS_NAME(getId)
  906                     ? snprintf(buf+nchar, size, " from %.*g", DBL_DIG,
  907                             offset)
  908                     : snprintf(buf+nchar, size, " @ %.*g", DBL_DIG, offset);
  909                 if (0 > n) {
  910                     nchar = n;
  911                 }
  912                 else {
  913                     nchar += n;
  914                     size = SUBTRACT_SIZET(size, n);
  915                 }
  916             }           /* non-zero offset */
  917 
  918             if (nchar >= 0) {
  919                 if (needParens) {
  920                     n = snprintf(buf+nchar, size, "%s", ")");
  921                     if (0 > n) {
  922                         nchar = n;
  923                     }
  924                     else {
  925                         nchar += n;
  926                         // size = SUBTRACT_SIZET(size, n); // Not used
  927                     }
  928                 }
  929             }                   /* printed offset if appropriate */
  930         }               /* underlying unit printed */
  931     }                       /* scale printed if appropriate */
  932 
  933     return nchar;
  934 }
  935 
  936 
  937 /*
  938  * Formats a Galilean-unit.
  939  *
  940  * Arguments:
  941  *  unit        Pointer to the Galilean-unit to be formatted.
  942  *  scale       The number of "underlyingUnit"s in "unit".
  943  *  underlyingUnit  Pointer to the unit that underlies "unit".
  944  *  offset      The offset of "unit" in units of "underlyingUnit".
  945  *  arg     Pointer to the formatting parameters.
  946  * Returns:
  947  *  -1      Failure.  See errno.
  948  *  else            Success. Number of bytes that would be printed if
  949  *                  "size" were sufficiently large excluding the
  950  *                  terminating NUL.
  951  */
  952 static ut_status
  953 formatGalilean(
  954     const ut_unit* const    unit,
  955     const double            scale,
  956     const ut_unit* const    underlyingUnit,
  957     double              offset,
  958     void*               arg)
  959 {
  960     FormatPar*  formatPar = (FormatPar*)arg;
  961     int     nchar;
  962 
  963     if (formatPar->getDefinition) {
  964     nchar = printGalilean(scale, underlyingUnit, offset, formatPar->buf,
  965         formatPar->size, formatPar->getId, formatPar->getDefinition,
  966         formatPar->encoding, formatPar->addParens);
  967     }
  968     else {
  969     const char* id = formatPar->getId(unit, formatPar->encoding);
  970 
  971     nchar =
  972         id == NULL
  973         ? printGalilean(scale, underlyingUnit, offset, formatPar->buf,
  974             formatPar->size, formatPar->getId, formatPar->getDefinition,
  975             formatPar->encoding, formatPar->addParens)
  976         : snprintf(formatPar->buf, formatPar->size, "%s", id);
  977     }
  978 
  979     formatPar->nchar = nchar < 0 ? nchar : formatPar->nchar + nchar;
  980 
  981     return nchar < 0 ? UT_VISIT_ERROR : UT_SUCCESS;
  982 }
  983 
  984 
  985 /*******************************************************************************
  986  * Timestamp-Unit Formatting:
  987  ******************************************************************************/
  988 
  989 /*
  990  * Prints a timestamp-unit.
  991  *
  992  * Arguments:
  993  *  underlyingUnit  Pointer to the unit underlying the timestamp-unit.
  994  *  year        The UTC year of the origin.
  995  *  month       The UTC month of the origin (1 through 12).
  996  *  day     The UTC day of the origin (1 through 32).
  997  *  hour        The UTC hour of the origin (0 through 23).
  998  *  minute      The UTC minute of the origin (0 through 59).
  999  *  second      The UTC second of the origin (0 through 60).
 1000  *  resolution  The resolution of the origin in seconds.
 1001  *  buf     Pointer to the buffer into which to print the
 1002  *          timestamp-unit.
 1003  *  size        The size of "buf" in bytes.
 1004  *  getId       Returns the identifier for a unit.
 1005  *  getDefinition   Returns the definition of "unit" in terms of basic
 1006  *          units.
 1007  *  encoding    The type of encoding to use.
 1008  *  addParens   Whether or not to add bracketing parentheses if
 1009  *          whitespace is printed.
 1010  * Returns:
 1011  *  -1      Failure.  See errno.
 1012  *  else            Success. Number of bytes that would be printed if
 1013  *                  "size" were sufficiently large excluding the
 1014  *                  terminating NUL.
 1015  */
 1016 static int
 1017 printTimestamp(
 1018     const ut_unit* const    underlyingUnit,
 1019     const int       year,
 1020     const int       month,
 1021     const int       day,
 1022     const int       hour,
 1023     const int       minute,
 1024     const double    second,
 1025     const double    resolution,
 1026     char* const     buf,
 1027     size_t          size,
 1028     IdGetter        getId,
 1029     const int       getDefinition,
 1030     const ut_encoding   encoding,
 1031     const int       addParens)
 1032 {
 1033     int     n;
 1034     int     nchar = 0;
 1035 
 1036     if (addParens) {
 1037     n = snprintf(buf, size, "%s", "(");
 1038         if (0 > n) {
 1039             nchar = -1;
 1040         }
 1041         else {
 1042             nchar += n;
 1043             size = SUBTRACT_SIZET(size, n);
 1044         }
 1045     }
 1046 
 1047     if (nchar >= 0) {
 1048     int useNames = RETURNS_NAME(getId);
 1049 
 1050         n = format(underlyingUnit, buf+nchar, size, useNames, getDefinition,
 1051                 encoding, 1);
 1052     nchar = n < 0 ? n : nchar + n;
 1053 
 1054     if (nchar >= 0) {
 1055             size = SUBTRACT_SIZET(size, n);
 1056 
 1057             int useSeparators = useNames || year < 1000 || year > 9999;
 1058 
 1059         n =  snprintf(buf+nchar, size,
 1060         useSeparators
 1061             ? " %s %d-%02d-%02d %02d:%02d"
 1062             : " %s %d%02d%02dT%02d%02d",
 1063         useNames ? "since" : "@",
 1064         year, month, day, hour, minute);
 1065             if (0 > n) {
 1066                 nchar = -1;
 1067             }
 1068             else {
 1069                 nchar += n;
 1070                 size = SUBTRACT_SIZET(size, n);
 1071             }
 1072 
 1073         if (nchar >= 0) {
 1074         int decimalCount = resolution <= 0.0
 1075                 ? 9 // Nanosecond resolution
 1076                 : -(int)floor(log10(resolution));
 1077 
 1078         if (decimalCount > -2) {
 1079             n = snprintf(buf+nchar, size,
 1080                 useSeparators ? ":%0*.*f" : "%0*.*f",
 1081                 decimalCount+3, decimalCount, second);
 1082                     if (0 > n) {
 1083                         nchar = -1;
 1084                     }
 1085                     else {
 1086                         nchar += n;
 1087                         size = SUBTRACT_SIZET(size, n);
 1088                     }
 1089         }           /* sufficient precision for seconds */
 1090 
 1091         if (nchar >= 0) {
 1092                     n = snprintf(buf+nchar, size, "%s",
 1093                             addParens ? " UTC)" : " UTC");
 1094                     if (0 > n) {
 1095                         nchar = -1;
 1096                     }
 1097                     else {
 1098                         nchar += n;
 1099                         // size = SUBTRACT_SIZET(size, n); // Not used
 1100                     }
 1101         }           /* printed seconds if appropriate */
 1102         }               /* printed year through minute */
 1103     }               /* underlying unit printed */
 1104     }                   /* leading "(" printed if appropriate */
 1105 
 1106     return nchar;
 1107 }
 1108 
 1109 
 1110 /*
 1111  * Formats a timestamp-unit.
 1112  *
 1113  * Arguments:
 1114  *  unit        Pointer to the timestamp-unit to be formatted.
 1115  *  underlyingUnit  Pointer to the unit that underlies "unit".
 1116  *      origin          The encoded origin of the timestamp-unit.
 1117  *  arg     Pointer to the formatting parameters.
 1118  * Returns:
 1119  *  -1      Failure.  See errno.
 1120  *  else            Success. Number of bytes that would be printed if
 1121  *                  "size" were sufficiently large excluding the
 1122  *                  terminating NUL.
 1123  */
 1124 static ut_status
 1125 formatTimestamp(
 1126     const ut_unit* const    unit,
 1127     const ut_unit* const    underlyingUnit,
 1128     const double        origin,
 1129     void*           arg)
 1130 {
 1131     FormatPar*      formatPar = (FormatPar*)arg;
 1132     int         nchar;
 1133     int             year;
 1134     int             month;
 1135     int             day;
 1136     int             hour;
 1137     int             minute;
 1138     double          second;
 1139     double              resolution;
 1140 
 1141     ut_decode_time(origin, &year, &month, &day, &hour, &minute, &second,
 1142         &resolution);
 1143 
 1144     if (formatPar->getDefinition) {
 1145     nchar = printTimestamp(underlyingUnit, year, month, day, hour, minute,
 1146         second, resolution, formatPar->buf, formatPar->size,
 1147         formatPar->getId, formatPar->getDefinition, formatPar->encoding,
 1148         formatPar->addParens);
 1149     }
 1150     else {
 1151     const char* id = formatPar->getId(unit, formatPar->encoding);
 1152 
 1153     nchar =
 1154         id == NULL
 1155         ? printTimestamp(underlyingUnit, year, month, day, hour, minute,
 1156             second, resolution, formatPar->buf, formatPar->size,
 1157             formatPar->getId, formatPar->getDefinition,
 1158             formatPar->encoding, formatPar->addParens)
 1159         : snprintf(formatPar->buf, formatPar->size, "%s", id);
 1160     }
 1161 
 1162     formatPar->nchar = nchar < 0 ? nchar : formatPar->nchar + nchar;
 1163 
 1164     return nchar < 0 ? UT_VISIT_ERROR : UT_SUCCESS;
 1165 }
 1166 
 1167 
 1168 /*******************************************************************************
 1169  * Logarithmic-Unit Formatting:
 1170  ******************************************************************************/
 1171 
 1172 /*
 1173  * Prints a logarithmic-unit.
 1174  *
 1175  * Arguments:
 1176  *      base            The base of the logarithm (e.g., 2, M_E, 10).
 1177  *  reference   Pointer to the reference-level of the logarithmic-unit.
 1178  *  buf     Pointer to the buffer into which to print the
 1179  *          logarithmic-unit.
 1180  *  size        The size of "buf" in bytes.
 1181  *  getId       Returns the identifier for a unit.
 1182  *  getDefinition   Returns the definition of "unit" in terms of basic
 1183  *          units.
 1184  *  encoding    The type of encoding to use.
 1185  *  addParens   Whether or not to add bracketing parentheses if
 1186  *          whitespace is printed.
 1187  * Returns:
 1188  *  -1      Failure.  See errno.
 1189  *  else            Success. Number of bytes that would be printed if
 1190  *                  "size" were sufficiently large excluding the
 1191  *                  terminating NUL.
 1192  */
 1193 static int
 1194 printLogarithmic(
 1195     const double            base,
 1196     const ut_unit* const    reference,
 1197     char*               buf,
 1198     size_t                  size,
 1199     IdGetter                getId,
 1200     const int               getDefinition,
 1201     const ut_encoding       encoding,
 1202     const int               addParens)
 1203 {
 1204     char    refSpec[512];
 1205     int     nchar = format(reference, refSpec, sizeof(refSpec)-1,
 1206     RETURNS_NAME(getId), getDefinition, encoding, 0);
 1207 
 1208     if (nchar >= 0) {
 1209     const char* amount;
 1210 
 1211     refSpec[nchar] = 0;
 1212     amount = isalpha(refSpec[0]) ? "1 " : "";
 1213 
 1214     if (base == 2) {
 1215         nchar = snprintf(buf, size, "lb(re %s%s)", amount, refSpec);
 1216     }
 1217     else if (base == M_E) {
 1218         nchar = snprintf(buf, size, "ln(re %s%s)", amount, refSpec);
 1219     }
 1220     else if (base == 10) {
 1221         nchar = snprintf(buf, size, "lg(re %s%s)", amount, refSpec);
 1222     }
 1223     else {
 1224         nchar = snprintf(buf, size,
 1225         addParens ? "(%.*g ln(re %s%s))" : "%.*g ln(re %s%s)",
 1226         DBL_DIG, 1/log(base), amount, refSpec);
 1227     }
 1228     }                   /* printed reference unit */
 1229 
 1230     return nchar;
 1231 }
 1232 
 1233 
 1234 /*
 1235  * Formats a logarithmic-unit.
 1236  *
 1237  * Arguments:
 1238  *  unit        Pointer to the logarithmic-unit to be formatted.
 1239  *      base            The base of the logarithm (e.g., 2, M_E, 10).
 1240  *  reference   Pointer to the reference-level of "unit".
 1241  *  arg     Pointer to the formatting parameters.
 1242  * Returns:
 1243  *  UT_VISIT_ERROR  Failure.
 1244  *  UT_SUCCESS  Success.
 1245  */
 1246 static ut_status
 1247 formatLogarithmic(
 1248     const ut_unit* const    unit,
 1249     const double            base,
 1250     const ut_unit* const    reference,
 1251     void*               arg)
 1252 {
 1253     FormatPar*  formatPar = (FormatPar*)arg;
 1254     int     nchar;
 1255 
 1256     if (formatPar->getDefinition) {
 1257     nchar = printLogarithmic(base, reference, formatPar->buf,
 1258         formatPar->size, formatPar->getId, formatPar->getDefinition,
 1259         formatPar->encoding, formatPar->addParens);
 1260     }
 1261     else {
 1262     const char* id = formatPar->getId(unit, formatPar->encoding);
 1263 
 1264     nchar =
 1265         id == NULL
 1266         ? printLogarithmic(base, reference, formatPar->buf,
 1267             formatPar->size, formatPar->getId, formatPar->getDefinition,
 1268             formatPar->encoding, formatPar->addParens)
 1269         : snprintf(formatPar->buf, formatPar->size, "%s", id);
 1270     }
 1271 
 1272     formatPar->nchar = nchar < 0 ? nchar : formatPar->nchar + nchar;
 1273 
 1274     return nchar < 0 ? UT_VISIT_ERROR : UT_SUCCESS;
 1275 }
 1276 
 1277 
 1278 /*******************************************************************************
 1279  * This module as a unit-visitor:
 1280  ******************************************************************************/
 1281 
 1282 static ut_visitor   formatter = {
 1283     formatBasic,
 1284     formatProduct,
 1285     formatGalilean,
 1286     formatTimestamp,
 1287     formatLogarithmic
 1288 };
 1289 
 1290 
 1291 
 1292 /******************************************************************************
 1293  * Public API:
 1294  ******************************************************************************/
 1295 
 1296 /*
 1297  * Formats a unit.
 1298  *
 1299  * Arguments:
 1300  *  unit        Pointer to the unit to be formatted.
 1301  *  buf     Pointer to the buffer into which to format "unit".
 1302  *  size        Size of the buffer in bytes.
 1303  *  opts        Formatting options: bitwise-OR of zero or more of the
 1304  *          following:
 1305  *              UT_NAMES        Use unit names instead of
 1306  *                      symbols
 1307  *                          UT_DEFINITION       The formatted string should be
 1308  *                                              the definition of "unit" in
 1309  *                                              terms of basic-units instead of
 1310  *                      stopping any expansion at the
 1311  *                      highest level possible.
 1312  *              UT_ASCII        The string should be formatted
 1313  *                      using the ASCII character set
 1314  *                      (default).
 1315  *              UT_LATIN1       The string should be formatted
 1316  *                      using the ISO Latin-1 (alias
 1317  *                      ISO-8859-1) character set.
 1318  *              UT_UTF8     The string should be formatted
 1319  *                      using the UTF-8 character set.
 1320  *          UT_LATIN1 and UT_UTF8 are mutually exclusive: they may
 1321  *          not both be specified.
 1322  * Returns:
 1323  *  -1      Failure:  "ut_get_status()" will be
 1324  *              UT_BAD_ARG      "unit" or "buf" is NULL, or both
 1325  *                                              UT_LATIN1 and UT_UTF8 specified.
 1326  *              UT_CANT_FORMAT  "unit" can't be formatted in
 1327  *                      the desired manner.
 1328  *  else            Success. Number of bytes that would be printed if
 1329  *                  "size" were sufficiently large excluding the
 1330  *                  terminating NUL.
 1331  */
 1332 int
 1333 ut_format(
 1334     const ut_unit* const    unit,
 1335     char*               buf,
 1336     size_t              size,
 1337     unsigned                opts)
 1338 {
 1339     int         nchar = -1; /* failure */
 1340     const int       useNames = opts & UT_NAMES;
 1341     const int       getDefinition = opts & UT_DEFINITION;
 1342     const ut_encoding   encoding =
 1343         (ut_encoding)(opts & (unsigned)(UT_ASCII | UT_LATIN1 | UT_UTF8));
 1344 
 1345     if (unit == NULL || buf == NULL) {
 1346     ut_set_status(UT_BAD_ARG);
 1347     ut_handle_error_message("NULL argument");
 1348     }
 1349     else if ((encoding & UT_LATIN1) && (encoding & UT_UTF8)) {
 1350     ut_set_status(UT_BAD_ARG);
 1351     ut_handle_error_message("Both UT_LATIN1 and UT_UTF8 specified");
 1352     }
 1353     else {
 1354     nchar = format(unit, buf, size, useNames, getDefinition, encoding, 0);
 1355 
 1356     if (nchar < 0) {
 1357         ut_set_status(UT_CANT_FORMAT);
 1358         ut_handle_error_message("Couldn't format unit");
 1359     }
 1360     else {
 1361         ut_set_status(UT_SUCCESS);
 1362     }
 1363     }
 1364 
 1365     return nchar;
 1366 }