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

formatter.c

/*
 * Copyright 2008, 2009 University Corporation for Atmospheric Research
 *
 * This file is part of the UDUNITS-2 package.  See the file LICENSE
 * in the top-level source-directory of the package for copying and
 * redistribution conditions.
 */
/*
 * This module is thread-compatible but not thread-safe.
 */
/*LINTLIBRARY*/

#ifndef     _XOPEN_SOURCE
#   define _XOPEN_SOURCE 500
#endif

#include <ctype.h>
#include <float.h>
#include <limits.h>
#include <math.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include "udunits2.h"
#include "unitToIdMap.h"

typedef const char*     (*IdGetter)(const ut_unit*, ut_encoding);
typedef     int         (*ProductPrinter)(const ut_unit* const*, const int*,
    int, char*, size_t, IdGetter);

/*
 * Formatting parameters:
 */
typedef struct {
    IdGetter            getId;
    ProductPrinter      printProduct;
    char*         buf;
    size_t        size;
    int                 getDefinition;
    ut_encoding         encoding;
    int                 addParens;
    int                 nchar;
} FormatPar;

#undef ABS
#define ABS(x)                ((x) < 0 ? -(x) : (x))
#define RETURNS_NAME(getId)   ((getId) == getName)

static int
asciiPrintProduct(
    const ut_unit* const* const     basicUnits,
    const int* const          powers,
    const int                 count,
    char* const               buf,
    const size_t        max,
    IdGetter                  getId);
static int
latin1PrintProduct(
    const ut_unit* const* const     basicUnits,
    const int* const          powers,
    const int                 count,
    char* const               buf,
    const size_t        max,
    IdGetter                  getId);
static int
utf8PrintProduct(
    const ut_unit* const* const     basicUnits,
    const int* const          powers,
    const int                 count,
    char* const               buf,
    const size_t        max,
    IdGetter                  getId);

static ut_visitor formatter;


/*
 * Returns a name for a unit.
 *
 * Arguments:
 *    unit        Pointer to the unit to have it's name returned.
 *    encoding    The encoding of the name to be returned.
 * Returns:
 *    NULL        A name is not available in the desired encoding.
 *    else        Pointer to the name.
 */
static const char*
getName(
    const ut_unit* const      unit,
    const ut_encoding   encoding)
{
    const char*   name;

    name = ut_get_name(unit, encoding);

    if (name == NULL)
      name = ut_get_name(unit, UT_ASCII);

    return name;
}


/*
 * Returns a symbol for a unit.
 *
 * Arguments:
 *    unit        Pointer to the unit to have it's symbol returned.
 *    encoding    The encoding of the symbol to be returned.
 * Returns:
 *    NULL        A symbol is not available in the desired encoding.
 *    else        Pointer to the symbol.
 */
static const char*
getSymbol(
    const ut_unit* const      unit,
    const ut_encoding   encoding)
{
    const char*   symbol;

    symbol = ut_get_symbol(unit, encoding);

    if (symbol == NULL)
      symbol = ut_get_symbol(unit, UT_ASCII);

    return symbol;
}


/*
 * Formats a unit.
 *
 * Arguments:
 *    unit        Pointer to the unit to be formatted.
 *    buf         Pointer to the buffer into which to print the formatted
 *                unit.
 *    size        Size of the buffer.
 *    useNames    Use unit names rather than unit symbols.
 *    getDefinition     Returns the definition of "unit" in terms of basic
 *                units.
 *    encoding    The type of encoding to use.
 *    addParens   Whether or not to add bracketing parentheses if
 *                whitespace is printed.
 * Returns:
 *    -1    Failure:  "utFormStatus()" will be
 *              UT_BAD_ARG    "unit" is NULL or "buf" is NULL.
 *    else  Number of characters printed in "buf".
 */
static int
format(
    const ut_unit* const      unit,
    char*                 buf,
    size_t                size,
    const int                   useNames,
    const int                   getDefinition,
    ut_encoding                 encoding,
    const int                   addParens)
{
    int     nchar = -1; /* failure */

    if (unit == NULL) {
      ut_set_status(UT_BAD_ARG);
      ut_handle_error_message("format(): NULL unit argument");
    }
    else if (buf == NULL) {
      ut_set_status(UT_BAD_ARG);
      ut_handle_error_message("format(): NULL buffer argument");
    }
    else {
      FormatPar   formatPar;

      formatPar.buf = buf;
      formatPar.size = size;
      formatPar.getId = useNames ? getName : getSymbol;
      formatPar.getDefinition = getDefinition;
      formatPar.encoding = encoding;
      formatPar.printProduct =
          encoding == UT_ASCII
            ? asciiPrintProduct
            : encoding == UT_LATIN1
                ? latin1PrintProduct
                : utf8PrintProduct;
      formatPar.addParens = addParens;
      formatPar.nchar = 0;

      if (ut_accept_visitor(unit, &formatter, &formatPar) == UT_SUCCESS)
          nchar = formatPar.nchar;
    }

    return nchar;
}


/*******************************************************************************
 * Basic-Unit Formatting:
 ******************************************************************************/

/*
 * Prints a basic-unit.
 *
 * Arguments:
 *    unit        The basic-unit to be printed.
 *    buf         The buffer into which to print "unit".
 *    max         The size of "buf".
 * Returns:
 *    -1          Failure.  The identifier for "unit" could not be
 *                obtained.
 *    else        Success.  Number of characters printed, excluding any
 *                trailing NUL.
 */
static int
printBasic(
    const ut_unit* const      unit,
    char* const         buf,
    size_t        max,
    IdGetter            getId,
    ut_encoding         encoding)
{
    const char* const   id = getId(unit, encoding);

    return
      id == NULL
          ? -1
          : snprintf(buf, max, "%s", id);
}


/*
 * Formats a basic-unit.
 *
 * Arguments:
 *    unit        The basic-unit to be formatted.
 *    arg         The formatting parameters.
 * Returns:
 *    -1          Failure.  The identifier for "unit" could not be
 *                obtained.
 *    else        Success.  Number of characters formatted, excluding any
 *                trailing NUL.
 */
static ut_status
formatBasic(
    const ut_unit* const      unit,
    void*         arg)
{
    FormatPar*    formatPar = (FormatPar*)arg;
    int           nchar = printBasic(unit, formatPar->buf, formatPar->size,
      formatPar->getId, formatPar->encoding);

    formatPar->nchar = nchar < 0 ? nchar : formatPar->nchar + nchar;

    return nchar < 0 ? UT_VISIT_ERROR : UT_SUCCESS;
}


/*******************************************************************************
 * Product-Unit Formatting:
 ******************************************************************************/

/*
 * Prints a product-unit using the ASCII character-set.
 *
 * Arguments:
 *    basicUnits  Pointer to pointers to the basic-units that constitute
 *                the product-unit.
 *    powers            Pointer to the powers associated with each basic-unit.
 *    count       The number of basic-units.
 *    buf         Pointer to the buffer into which to print the basic-
 *                units.
 *    max         The size of "buf" in bytes.
 *    getId       Returns the identifier for a unit.
 * Returns:
 *    -1          Failure.  See errno.
 *    else        Success.  Number of bytes printed.
 */
static int
asciiPrintProduct(
    const ut_unit* const* const     basicUnits,
    const int* const          powers,
    const int                 count,
    char* const               buf,
    const size_t        max,
    IdGetter                  getId)
{
    int           nchar = snprintf(buf, max, "%s", "");

    if (nchar >= 0) {
      int   i;

      for (i = 0; i < count; i++) {
          int     n;

          /*
           * Append separator if appropriate.
           */
          if (nchar > 0) {
            n = RETURNS_NAME(getId)
                ? snprintf(buf+nchar, max-nchar, "%s", "-")
                : snprintf(buf+nchar, max-nchar, "%s", ".");

            if (n < 0) {
                nchar = n;
                break;
            }

            nchar += n;
          }

          /*
           * Append unit identifier.
           */
          n = printBasic(basicUnits[i], buf+nchar, max-nchar, getId,
            UT_ASCII);

          if (n < 0) {
            nchar = n;
            break;
          }

          nchar += n;

          /*
           * Append exponent if appropriate.
           */
          if (powers[i] != 1) {
            n = RETURNS_NAME(getId)
                ? snprintf(buf+nchar, max-nchar, "^%d", powers[i])
                : snprintf(buf+nchar, max-nchar, "%d", powers[i]);

            if (n < 0) {
                nchar = n;
                break;
            }

            nchar += n;
          }
      }                       /* loop over basic-units */
    }                         /* "buf" initialized */

    return nchar;
}


/*
 * Prints a product of basic-units using the UTF-8 character-set.
 *
 * Arguments:
 *    basicUnits  Pointer to pointers to the basic-units whose product
 *                is to be printed.
 *    powers            Pointer to the powers associated with each basic-unit.
 *    count       The number of basic-units.
 *    buf         Pointer to the buffer into which to print the basic-
 *                units.
 *    max         The size of "buf" in bytes.
 *    getId       Returns the identifier for a unit.
 * Returns:
 *    -1          Failure.  See errno.
 *    else        Success.  Number of bytes printed.
 */
static int
utf8PrintProduct(
    const ut_unit* const* const     basicUnits,
    const int* const          powers,
    const int                 count,
    char* const               buf,
    const size_t        max,
    IdGetter                  getId)
{
    int           nchar = snprintf(buf, max, "%s", "");

    if (nchar >= 0) {
      int   iBasic;

      for (iBasic = 0; iBasic < count; iBasic++) {
          int     power = powers[iBasic];

          if (power != 0) {
            /*
             * The current basic-unit must be printed.
             */
            int   n;

            if (nchar > 0) {
                /*
                 * Append mid-dot separator.
                 */
                n = snprintf(buf+nchar, max-nchar, "%s", "\xc2\xb7");

                if (n < 0) {
                  nchar = n;
                  break;
                }

                nchar += n;
            }

            /*
             * Append unit identifier.
             */
            n = printBasic(basicUnits[iBasic], buf+nchar, max-nchar,
                getId, UT_UTF8);

            if (n < 0) {
                nchar = n;
                break;
            }

            nchar += n;

            if (power != 1) {
                /*
                 * Append exponent.
                 */
                static const char*  exponentStrings[10] = {
                  "\xe2\x81\xb0",   /* 0 */
                  "\xc2\xb9", /* 1 */
                  "\xc2\xb2", /* 2 */
                  "\xc2\xb3", /* 3 */
                  "\xe2\x81\xb4",   /* 4 */
                  "\xe2\x81\xb5",   /* 5 */
                  "\xe2\x81\xb6",   /* 6 */
                  "\xe2\x81\xb7",   /* 7 */
                  "\xe2\x81\xb8",   /* 8 */
                  "\xe2\x81\xb9",   /* 9 */
                };

                if (power < 0) {
                  /*
                   * Append superscript minus sign.
                   */
                  n = snprintf(buf+nchar, max-nchar, "%s",
                            "\xe2\x81\xbb");

                  if (n < 0) {
                      nchar = n;
                      break;
                  }

                  nchar += n;
                  power = -power;
                }

                /*
                 * Append UTF-8 encoding of exponent magnitude.
                 */
                {
                  static int* digit = NULL;

                  digit = realloc(digit, (size_t)((sizeof(powers[0])*
                            CHAR_BIT*(M_LOG10E/M_LOG2E)) + 1));

                  if (digit == NULL) {
                      nchar = -1;
                  }
                  else {
                      int     idig = 0;

                      for (; power > 0; power /= 10)
                        digit[idig++] = power % 10;

                      while (idig-- > 0) {
                        n = snprintf(buf+nchar, max-nchar, "%s",
                              exponentStrings[digit[idig]]);

                        if (n < 0) {
                            nchar = n;
                            break;
                        }

                        nchar += n;
                      }

                      if (nchar < 0)
                        break;
                  }
                }             /* exponent digits block */
            }                 /* must print exponent */
          }                   /* must print basic-unit */
      }                       /* loop over basic-units */
    }                         /* "buf" initialized */

    return nchar;
}


static const int* globalPowers = NULL;


static int
compareExponents(
    const void*   i,
    const void*   j)
{
    return globalPowers[*(const int*)j] - globalPowers[*(const int*)i];
}


/*
 * Returns the order of basic-unit powers in decreasing order.
 *
 * Arguments:
 *    powers            Pointer to the powers of the basic-units.
 *    count       The number of powers.
 *    positiveCount     Pointer to pointer to the number of positive powers.
 *                Set on and only on success.
 *    negativeCount     Pointer to pointer to the number of negative powers.
 *                Set on and only on success.
 * Returns:
 *    NULL        Failure.  See errno.
 *    else        Success.  Pointer to indexes of "powers" in decreasing
 *                order.
 */
static void
getBasicOrder(
    const int* const    powers,
    const int           count,
    int* const          order,
    int* const          positiveCount,
    int* const          negativeCount)
{
    int           nNeg = 0;
    int           nPos = 0;
    int           n = 0;
    int           i;

    for (i = 0; i < count; i++) {
      if (powers[i] < 0) {
          ++nNeg;
          order[n++] = i;
      }
      else if (powers[i] > 0) {
          ++nPos;
          order[n++] = i;
      }
    }

    *negativeCount = nNeg;
    *positiveCount = nPos;
    globalPowers = powers;

    qsort(order, n, sizeof(int), compareExponents);
}


/*
 * Prints the product of a set of basic-units using the ISO-8859-1 (Latin-1)
 * character-set.
 *
 * Arguments:
 *    buf         Pointer to the buffer into which to print the basic-
 *                units.
 *    max         The size of "buf" in bytes.
 *    basicUnits  Pointer to pointers to the basic-units.
 *    powers            Pointer to the powers associated with each basic-unit.
 *    order       Pointer to indexes of "powers".  "order[i]" is the
 *                index of "basicUnits" and "powers" for the "i"th
 *                position.
 *    count       The number of basic-units.
 *    getId       Returns the identifier for a unit.
 * Returns:
 *    -1          Failure.  See errno.
 *    else        Success.  Number of bytes printed.
 */
static int
latin1PrintBasics(
    char* const               buf,
    size_t              max,
    const ut_unit* const*     basicUnits,
    const int* const          powers,
    const int* const          order,
    const int                 count,
    IdGetter                  getId)
{
    int     needSeparator = 0;
    int     nchar = 0;
    int     i;

    for (i = 0; i < count; i++) {
      int   n;
      int   j = order[i];
      int   power = ABS(powers[j]);

      if (power != 0) {
          if (needSeparator) {
            n = snprintf(buf+nchar, max-nchar, "%s", "");  /* 0xb7 */

            if (n < 0) {
                nchar = n;
                break;
            }

            nchar += n;
          }

          /*
           * Append unit identifier.
           */
          n = printBasic(basicUnits[j], buf+nchar, max-nchar, getId,
            UT_LATIN1);

          if (n < 0) {
            nchar = n;
            break;
          }

          nchar += n;
          needSeparator = 1;

          /*
           * Append exponent if appropriate.
           */
          if (power != 1) {
            n = snprintf(buf+nchar, max-nchar, "%s",
                power == 2 ? "" : "");  /* 0xb2 0xb3 */

            if (n < 0) {
                nchar = n;
                break;
            }

            nchar += n;
          }
      }           /* exponent not zero */
    }             /* loop over positive exponents */

    return nchar;
}


/*
 * Prints a product-unit using the ISO-8859-1 (Latin-1) character-set.
 *
 * Arguments:
 *    basicUnits  Pointer to pointers to the basic-units that constitute
 *                the product-unit.
 *    powers            Pointer to the powers associated with each basic-unit.
 *    count       The number of basic-units.
 *    buf         Pointer to the buffer into which to print the basic-
 *                units.
 *    max         The size of "buf" in bytes.
 *    getId       Returns the identifier for a unit.
 * Returns:
 *    -1          Failure.  See errno.
 *    else        Success.  Number of bytes printed.
 */
static int
latin1PrintProduct(
    const ut_unit* const* const     basicUnits,
    const int* const          powers,
    const int                 count,
    char* const               buf,
    const size_t        max,
    IdGetter                  getId)
{
    int                       nchar;
    int                       i;

    for (i = 0; i < count; i++)
      if (powers[i] < -3 || powers[i] > 3)
          break;

    if (i < count) {
      /*
       * At least one exponent can't be represented in ISO 8859-1.  Use
       * the ASCII encoding instead.
       */
      nchar = asciiPrintProduct(basicUnits, powers, count, buf, max, getId);
    }
    else {
      int         positiveCount;
      int         negativeCount;
      int*        order = malloc(count*sizeof(int));

      if (order == NULL) {
          nchar = -1;
      }
      else {
          getBasicOrder(powers, count, order, &positiveCount, &negativeCount);

          nchar = snprintf(buf, max, "%s", "");

          if (nchar >= 0 && (positiveCount + negativeCount > 0)) {
            int         n;

            if (positiveCount == 0) {
                n = snprintf(buf+nchar, max-nchar, "%s", "1");
                nchar = n < 0 ? n : nchar + n;
            }
            else {
                n = latin1PrintBasics(buf+nchar, max-nchar, basicUnits,
                  powers, order, positiveCount, getId);
                nchar = n < 0 ? n : nchar + n;
            }

            if (nchar >= 0 && negativeCount > 0) {
                n = snprintf(buf+nchar, max-nchar, "%s", 
                  negativeCount == 1 ? "/" : "/(");
                nchar = n < 0 ? n : nchar + n;

                if (nchar >= 0) {
                  n = latin1PrintBasics(buf+nchar, max-nchar, basicUnits,
                      powers, order+positiveCount, negativeCount, getId);
                  nchar = n < 0 ? n : nchar + n;

                  if (nchar >= 0 && negativeCount > 1) {
                      n = snprintf(buf+nchar, max-nchar, "%s", ")");
                      nchar = n < 0 ? n : nchar + n;
                  }
                }             /* solidus appended */
            }                 /* positive exponents printed */
          }                   /* "buf" initialized */

          (void)free(order);
      }                       /* "order" allocated */
    }                         /* using Latin-1 encoding */

    return nchar;
}


/*
 * Prints a product-unit.
 *
 * Arguments:
 *    unit        Pointer to the product-unit to be formatted.
 *    count       The number of basic-units that constitute the 
 *                product-unit.
 *    basicUnits  Pointer to pointers to the basic-units that constitute
 *                the product-unit.
 *    powers            Pointer to the powers associated with each basic-unit
 *                of "basicUnits".
 *    arg         The formatting parameters.
 * Returns:
 *    -1          Failure.  See errno.
 *    else        Success.  Number of bytes printed.
 */
static ut_status
formatProduct(
    const ut_unit* const      unit,
    const int                 count,
    const ut_unit* const* const     basicUnits,
    const int* const          powers,
    void*               arg)
{
    FormatPar*    formatPar = (FormatPar*)arg;
    int           nchar;

    if (ut_compare(unit,
          ut_get_dimensionless_unit_one(ut_get_system(unit))) == 0) {
      /*
       * The dimensionless unit one is special.
       */
      (void)strncpy(formatPar->buf, "1", formatPar->size);
      nchar = formatPar->size > 0 ? 1 : 0;
    }
    else {
      if (formatPar->getDefinition) {
          nchar = formatPar->printProduct(basicUnits, powers, count,
            formatPar->buf, formatPar->size, formatPar->getId);
      }
      else {
          const char*   id = formatPar->getId(unit, formatPar->encoding);

          nchar = 
            id == NULL
                ? formatPar->printProduct(basicUnits, powers, count,
                  formatPar->buf, formatPar->size, formatPar->getId)
                : snprintf(formatPar->buf, formatPar->size, "%s", id);
      }
    }
    formatPar->nchar = nchar < 0 ? nchar : formatPar->nchar + nchar;

    return nchar < 0 ? UT_VISIT_ERROR : UT_SUCCESS;
}


/*******************************************************************************
 * Galilean-Unit Formatting:
 ******************************************************************************/

/*
 * Prints a Galilean-unit.
 *
 * Arguments:
 *    scale       The number of "unit"s in the Galilean-unit.
 *    unit        Pointer to the unit underlying the Galilean-unit.
 *    offset            The offset of the Galilean-unit in units of "unit".
 *    buf         Pointer to the buffer into which to print the Galilean-
 *                unit.
 *    max         The size of "buf" in bytes.
 *    getId       Returns the identifier for a unit.
 *    getDefinition     Returns the definition of "unit" in terms of basic
 *                units.
 *    encoding    The type of encoding to use.
 *    addParens   Whether or not to add bracketing parentheses if 
 *                whitespace is printed.
 * Returns:
 *    -1          Failure.  See errno.
 *    else        Success.  Number of bytes printed.
 */
static int
printGalilean(
    double        scale,
    const ut_unit* const      unit,
    double        offset,
    char* const         buf,
    const size_t  max,
    IdGetter            getId,
    const int           getDefinition,
    const ut_encoding   encoding,
    const int           addParens)
{
    int                 n;
    int                 nchar = 0;
    int                 needParens = 0;

    if (scale != 1) {
      needParens = addParens;
      n = snprintf(buf, max, needParens ? "(%.*g " : "%.*g ", DBL_DIG, scale);
      nchar = n < 0 ? n : nchar + n;
    }

    if (nchar >= 0) {
      n = format(unit, buf+nchar, max-nchar, RETURNS_NAME(getId),
          getDefinition, encoding, 1);

      if (n < 0) {
          nchar = n;
      }
      else {
          nchar += n;

          if (offset != 0) {
            needParens = addParens;
            n = RETURNS_NAME(getId)
                ? snprintf(buf+nchar, max-nchar, " from %.*g", DBL_DIG,
                  offset)
                : snprintf(buf+nchar, max-nchar, " @ %.*g", DBL_DIG,
                  offset);
            nchar = n < 0 ? n : nchar + n;
          }                   /* non-zero offset */

          if (nchar >= 0) {
            if (needParens) {
                n = snprintf(buf+nchar, max-nchar, "%s", ")");
                nchar = n < 0 ? n : nchar + n;
            }
          }                   /* printed offset if appropriate */
      }                       /* underlying unit printed */
    }                         /* scale printed if appropriate */

    return nchar;
}


/*
 * Formats a Galilean-unit.
 *
 * Arguments:
 *    unit        Pointer to the Galilean-unit to be formatted.
 *    scale       The number of "underlyingUnit"s in "unit".
 *    underlyingUnit    Pointer to the unit that underlies "unit".
 *    offset            The offset of "unit" in units of "underlyingUnit".
 *    arg         Pointer to the formatting parameters.
 * Returns:
 *    -1          Failure.  See errno.
 *    else        Success.  Number of bytes printed.
 */
static ut_status
formatGalilean(
    const ut_unit* const      unit,
    const double  scale,
    const ut_unit* const      underlyingUnit,
    double        offset,
    void*         arg)
{
    FormatPar*    formatPar = (FormatPar*)arg;
    int           nchar;

    if (formatPar->getDefinition) {
      nchar = printGalilean(scale, underlyingUnit, offset, formatPar->buf,
          formatPar->size, formatPar->getId, formatPar->getDefinition,
          formatPar->encoding, formatPar->addParens);
    }
    else {
      const char* id = formatPar->getId(unit, formatPar->encoding);

      nchar = 
          id == NULL
            ? printGalilean(scale, underlyingUnit, offset, formatPar->buf,
                formatPar->size, formatPar->getId, formatPar->getDefinition,
                formatPar->encoding, formatPar->addParens)
            : snprintf(formatPar->buf, formatPar->size, "%s", id);
    }

    formatPar->nchar = nchar < 0 ? nchar : formatPar->nchar + nchar;

    return nchar < 0 ? UT_VISIT_ERROR : UT_SUCCESS;
}


/*******************************************************************************
 * Timestamp-Unit Formatting:
 ******************************************************************************/

/*
 * Prints a timestamp-unit.
 *
 * Arguments:
 *    underlyingUnit    Pointer to the unit underlying the timestamp-unit.
 *    year        The UTC year of the origin.
 *    month       The UTC month of the origin (1 through 12).
 *    day         The UTC day of the origin (1 through 32).
 *    hour        The UTC hour of the origin (0 through 23).
 *    minute            The UTC minute of the origin (0 through 59).
 *    second            The UTC second of the origin (0 through 60).
 *    resolution  The resolution of the origin in seconds.
 *    buf         Pointer to the buffer into which to print the
 *                timestamp-unit.
 *    max         The size of "buf" in bytes.
 *    getId       Returns the identifier for a unit.
 *    getDefinition     Returns the definition of "unit" in terms of basic
 *                units.
 *    encoding    The type of encoding to use.
 *    addParens   Whether or not to add bracketing parentheses if 
 *                whitespace is printed.
 * Returns:
 *    -1          Failure.  See errno.
 *    else        Success.  Number of bytes printed.
 */
static int
printTimestamp(
    const ut_unit* const      underlyingUnit,
    const int           year,
    const int           month,
    const int           day,
    const int           hour,
    const int           minute,
    const double  second,
    const double  resolution,
    char* const         buf,
    const size_t  max,
    IdGetter            getId,
    const int           getDefinition,
    const ut_encoding   encoding,
    const int           addParens)
{
    int           n;
    int           nchar = 0;

    if (addParens) {
      n = snprintf(buf, max, "%s", "(");
      nchar = n < 0 ? n : nchar + n;
    }

    if (nchar >= 0) {
      int   useNames = RETURNS_NAME(getId);

      n = format(underlyingUnit, buf+nchar, max-nchar, useNames,
          getDefinition, encoding, 1);
      nchar = n < 0 ? n : nchar + n;

      if (nchar >= 0) {
          int     useSeparators = useNames || year < 1000 || year > 9999;

          n =  snprintf(buf+nchar, max-nchar,
            useSeparators
                ? " %s %d-%02d-%02d %02d:%02d"
                : " %s %d%02d%02dT%02d%02d",
            useNames ? "since" : "@",
            year, month, day, hour, minute);
          nchar = n < 0 ? n : nchar + n;

          if (nchar >= 0) {
            int   decimalCount = -(int)floor(log10(resolution));

            if (decimalCount > -2) {
                n = snprintf(buf+nchar, max-nchar, 
                      useSeparators ? ":%0*.*f" : "%0*.*f",
                      decimalCount+3, decimalCount, second);
                nchar = n < 0 ? n : nchar + n;
            }                 /* sufficient precision for seconds */

            if (nchar >= 0) {
                n = snprintf(buf+nchar, max-nchar, "%s",
                  addParens ? " UTC)" : " UTC");
                nchar = n < 0 ? n : nchar + n;
            }                 /* printed seconds if appropriate */
          }                   /* printed year through minute */
      }                       /* underlying unit printed */
    }                         /* leading "(" printed if appropriate */

    return nchar;
}


/*
 * Formats a timestamp-unit.
 *
 * Arguments:
 *    unit        Pointer to the timestamp-unit to be formatted.
 *    underlyingUnit    Pointer to the unit that underlies "unit".
 *      origin          The encoded origin of the timestamp-unit.
 *    arg         Pointer to the formatting parameters.
 * Returns:
 *    -1          Failure.  See errno.
 *    else        Success.  Number of bytes printed.
 */
static ut_status
formatTimestamp(
    const ut_unit* const      unit,
    const ut_unit* const      underlyingUnit,
    const double        origin,
    void*               arg)
{
    FormatPar*    formatPar = (FormatPar*)arg;
    int           nchar;
    int                   year;
    int                   month;
    int                   day;
    int                   hour;
    int                   minute;
    double        second;
    double              resolution;

    ut_decode_time(origin, &year, &month, &day, &hour, &minute, &second,
        &resolution);

    if (formatPar->getDefinition) {
      nchar = printTimestamp(underlyingUnit, year, month, day, hour, minute,
          second, resolution, formatPar->buf, formatPar->size,
          formatPar->getId, formatPar->getDefinition, formatPar->encoding,
          formatPar->addParens);
    }
    else {
      const char* id = formatPar->getId(unit, formatPar->encoding);

      nchar = 
          id == NULL
            ? printTimestamp(underlyingUnit, year, month, day, hour, minute,
                second, resolution, formatPar->buf, formatPar->size,
                formatPar->getId, formatPar->getDefinition,
                formatPar->encoding, formatPar->addParens)
            : snprintf(formatPar->buf, formatPar->size, "%s", id);
    }

    formatPar->nchar = nchar < 0 ? nchar : formatPar->nchar + nchar;

    return nchar < 0 ? UT_VISIT_ERROR : UT_SUCCESS;
}


/*******************************************************************************
 * Logarithmic-Unit Formatting:
 ******************************************************************************/

/*
 * Prints a logarithmic-unit.
 *
 * Arguments:
 *      base            The base of the logarithm (e.g., 2, M_E, 10).
 *    reference   Pointer to the reference-level of the logarithmic-unit.
 *    buf         Pointer to the buffer into which to print the
 *                logarithmic-unit.
 *    max         The size of "buf" in bytes.
 *    getId       Returns the identifier for a unit.
 *    getDefinition     Returns the definition of "unit" in terms of basic
 *                units.
 *    encoding    The type of encoding to use.
 *    addParens   Whether or not to add bracketing parentheses if 
 *                whitespace is printed.
 * Returns:
 *    -1          Failure.  See errno.
 *    else        Success.  Number of bytes printed.
 */
static int
printLogarithmic(
    const double  base,
    const ut_unit* const      reference,
    char*         buf,
    const size_t  max,
    IdGetter            getId,
    const int           getDefinition,
    const ut_encoding   encoding,
    const int           addParens)
{
    char    refSpec[512];
    int           nchar = format(reference, refSpec, sizeof(refSpec)-1,
      RETURNS_NAME(getId), getDefinition, encoding, 0);

    if (nchar >= 0) {
      const char* amount;

      refSpec[nchar] = 0;
      amount = isalpha(refSpec[0]) ? "1 " : "";

      if (base == 2) {
          nchar = snprintf(buf, max, "lb(re %s%s)", amount, refSpec);
      }
      else if (base == M_E) {
          nchar = snprintf(buf, max, "ln(re %s%s)", amount, refSpec);
      }
      else if (base == 10) {
          nchar = snprintf(buf, max, "lg(re %s%s)", amount, refSpec);
      }
      else {
          nchar = snprintf(buf, max,
            addParens ? "(%.*g ln(re %s%s))" : "%.*g ln(re %s%s)",
            DBL_DIG, 1/log(base), amount, refSpec);
      }
    }                         /* printed reference unit */

    return nchar;
}


/*
 * Formats a logarithmic-unit.
 *
 * Arguments:
 *    unit        Pointer to the logarithmic-unit to be formatted.
 *      base            The base of the logarithm (e.g., 2, M_E, 10).
 *    reference   Pointer to the reference-level of "unit".
 *    arg         Pointer to the formatting parameters.
 * Returns:
 *    UT_VISIT_ERROR    Failure.
 *    UT_SUCCESS  Success.
 */
static ut_status
formatLogarithmic(
    const ut_unit* const      unit,
    const double  base,
    const ut_unit* const      reference,
    void*         arg)
{
    FormatPar*    formatPar = (FormatPar*)arg;
    int           nchar;

    if (formatPar->getDefinition) {
      nchar = printLogarithmic(base, reference, formatPar->buf,
          formatPar->size, formatPar->getId, formatPar->getDefinition,
          formatPar->encoding, formatPar->addParens);
    }
    else {
      const char* id = formatPar->getId(unit, formatPar->encoding);

      nchar = 
          id == NULL
            ? printLogarithmic(base, reference, formatPar->buf,
                formatPar->size, formatPar->getId, formatPar->getDefinition,
                formatPar->encoding, formatPar->addParens)
            : snprintf(formatPar->buf, formatPar->size, "%s", id);
    }

    formatPar->nchar = nchar < 0 ? nchar : formatPar->nchar + nchar;

    return nchar < 0 ? UT_VISIT_ERROR : UT_SUCCESS;
}


/*******************************************************************************
 * This module as a unit-visitor:
 ******************************************************************************/

static ut_visitor formatter = {
    formatBasic,
    formatProduct,
    formatGalilean,
    formatTimestamp,
    formatLogarithmic
};



/******************************************************************************
 * Public API:
 ******************************************************************************/

/*
 * Formats a unit.
 *
 * Arguments:
 *    unit        Pointer to the unit to be formatted.
 *    buf         Pointer to the buffer into which to format "unit".
 *    size        Size of the buffer in bytes.
 *    opts        Formatting options: bitwise-OR of zero or more of the
 *                following:
 *                    UT_NAMES            Use unit names instead of
 *                                  symbols
 *                          UT_DEFINITION       The formatted string should be
 *                                              the definition of "unit" in
 *                                              terms of basic-units instead of
 *                                  stopping any expansion at the
 *                                  highest level possible.
 *                    UT_ASCII            The string should be formatted
 *                                  using the ASCII character set
 *                                  (default).
 *                    UT_LATIN1           The string should be formatted
 *                                  using the ISO Latin-1 (alias
 *                                  ISO-8859-1) character set.
 *                    UT_UTF8       The string should be formatted
 *                                  using the UTF-8 character set.
 *                UT_LATIN1 and UT_UTF8 are mutually exclusive: they may
 *                not both be specified.
 * Returns:
 *    -1          Failure:  "ut_get_status()" will be
 *                    UT_BAD_ARG          "unit" or "buf" is NULL, or both
 *                                              UT_LATIN1 and UT_UTF8 specified.
 *                    UT_CANT_FORMAT      "unit" can't be formatted in
 *                                  the desired manner.
 *      else            Success.  Number of characters printed in "buf".  If
 *                the number is equal to the size of the buffer, then the
 *                buffer is too small to have a terminating NUL character.
 */
int
ut_format(
    const ut_unit* const      unit,
    char*                 buf,
    size_t                size,
    unsigned                    opts)
{
    int                 nchar = -1; /* failure */
    const int           useNames = opts & UT_NAMES;
    const int           getDefinition = opts & UT_DEFINITION;
    const ut_encoding   encoding =
        (ut_encoding)(opts & (unsigned)(UT_ASCII | UT_LATIN1 | UT_UTF8));

    if (unit == NULL || buf == NULL) {
      ut_set_status(UT_BAD_ARG);
      ut_handle_error_message("NULL argument");
    }
    else if ((encoding & UT_LATIN1) && (encoding & UT_UTF8)) {
      ut_set_status(UT_BAD_ARG);
      ut_handle_error_message("Both UT_LATIN1 and UT_UTF8 specified");
    }
    else {
      nchar = format(unit, buf, size, useNames, getDefinition, encoding, 0);

      if (nchar < 0) {
          ut_set_status(UT_CANT_FORMAT);
          ut_handle_error_message("Couldn't format unit");
      }
      else {
          ut_set_status(UT_SUCCESS);
      }
    }

    return nchar;
}

Generated by  Doxygen 1.6.0   Back to index