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

formatter.c

/*
 * Copyright (C) 2011 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:
 */
00037 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)
#define SUBTRACT_SIZET(a, b)    ((a) > (b) ? (a) - (b) : 0)

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          Success. Number of bytes that would be printed if
 *                    "size" were sufficiently large excluding the
 *                    terminating NUL.
 */
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".
 *    size        The size of "buf".
 * Returns:
 *    -1          Failure.  The identifier for "unit" could not be
 *                obtained.
 *    else          Success. Number of bytes that would be printed if
 *                    "size" were sufficiently large excluding the
 *                    terminating NUL.
 */
static int
printBasic(
    const ut_unit* const      unit,
    char* const               buf,
    const size_t              size,
    IdGetter                  getId,
    ut_encoding               encoding)
{
    const char* const   id = getId(unit, encoding);

    return
      id == NULL
          ? -1
          : snprintf(buf, size, "%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 bytes that would be printed if
 *                    "size" were sufficiently large excluding the
 *                    terminating 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.
 *    size        The size of "buf" in bytes.
 *    getId       Returns the identifier for a unit.
 * Returns:
 *    -1          Failure.  See errno.
 *    else          Success. Number of bytes that would be printed if
 *                    "size" were sufficiently large excluding the
 *                    terminating NUL.
 */
static int
asciiPrintProduct(
    const ut_unit* const* const     basicUnits,
    const int* const          powers,
    const int                 count,
    char* const               buf,
    size_t                size,
    IdGetter                  getId)
{
    int           nchar = snprintf(buf, size, "%s", "");

    if (nchar >= 0) {
        int i;

        size = SUBTRACT_SIZET(size, nchar);

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

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

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

                nchar += n;
                size = SUBTRACT_SIZET(size, n);
            }

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

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

            nchar += n;
            size = SUBTRACT_SIZET(size, n);

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

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

                nchar += n;
                size = SUBTRACT_SIZET(size, 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.
 *    size        The size of "buf" in bytes.
 *    getId       Returns the identifier for a unit.
 * Returns:
 *    -1          Failure.  See errno.
 *    else          Success. Number of bytes that would be printed if
 *                    "size" were sufficiently large excluding the
 *                    terminating NUL.
 */
static int
utf8PrintProduct(
    const ut_unit* const* const     basicUnits,
    const int* const          powers,
    const int                 count,
    char* const               buf,
    size_t                size,
    IdGetter                  getId)
{
    int           nchar = snprintf(buf, size, "%s", "");

    if (nchar >= 0) {
        int iBasic;

        size = SUBTRACT_SIZET(size, nchar);

        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, size, "%s", "\xc2\xb7");

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

                    nchar += n;
                    size = SUBTRACT_SIZET(size, n);
                }

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

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

                nchar += n;
                size = SUBTRACT_SIZET(size, 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, size, "%s", "\xe2\x81\xbb");

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

                        nchar += n;
                        size = SUBTRACT_SIZET(size, 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, size, "%s",
                                        exponentStrings[digit[idig]]);

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

                                nchar += n;
                                size = SUBTRACT_SIZET(size, 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.
 *    size        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 that would be printed if
 *                    "size" were sufficiently large excluding the
 *                    terminating NUL.
 */
static int
latin1PrintBasics(
    char* const               buf,
    size_t              size,
    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, size, "%s", ""); /* 0xb7 */

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

            nchar += n;
                size = SUBTRACT_SIZET(size, n);
          }

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

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

            nchar += n;
            size = SUBTRACT_SIZET(size, n);
            needSeparator = 1;

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

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

                nchar += n;
                size = SUBTRACT_SIZET(size, 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.
 *    size        The size of "buf" in bytes.
 *    getId       Returns the identifier for a unit.
 * Returns:
 *    -1          Failure.  See errno.
 *    else          Success. Number of bytes that would be printed if
 *                    "size" were sufficiently large excluding the
 *                    terminating NUL.
 */
static int
latin1PrintProduct(
    const ut_unit* const* const     basicUnits,
    const int* const          powers,
    const int                 count,
    char* const               buf,
    size_t                size,
    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, size, 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, size, "%s", "");

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

                size = SUBTRACT_SIZET(size, nchar);

                if (positiveCount == 0) {
                    n = snprintf(buf+nchar, size, "%s", "1");
                    if (0 > n) {
                        nchar = n;
                    }
                    else {
                        nchar += n;
                        size = SUBTRACT_SIZET(size, n);
                    }
                }
                else {
                    n = latin1PrintBasics(buf+nchar, size, basicUnits,
                            powers, order, positiveCount, getId);
                    if (0 > n) {
                        nchar = n;
                    }
                    else {
                        nchar += n;
                        size = SUBTRACT_SIZET(size, n);
                    }
                }

                if (nchar >= 0 && negativeCount > 0) {
                    n = snprintf(buf+nchar, size, "%s",
                        negativeCount == 1 ? "/" : "/(");
                    if (0 > n) {
                        nchar = n;
                    }
                    else {
                        nchar += n;
                        size = SUBTRACT_SIZET(size, n);

                        n = latin1PrintBasics(buf+nchar, size, basicUnits,
                                powers, order+positiveCount, negativeCount,
                                getId);
                        if (0 > n) {
                            nchar = n;
                        }
                        else {
                            nchar += n;
                            size = SUBTRACT_SIZET(size, n);

                            if (negativeCount > 1) {
                                n = snprintf(buf+nchar, size, "%s", ")");
                                if (0 > n) {
                                    nchar = n;
                                }
                                else {
                                    nchar += n;
                                    size = SUBTRACT_SIZET(size, 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 that would be printed if
 *                    "size" were sufficiently large excluding the
 *                    terminating NUL.
 */
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.
 *    size        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 that would be printed if
 *                    "size" were sufficiently large excluding the
 *                    terminating NUL.
 */
static int
printGalilean(
    double                  scale,
    const ut_unit* const    unit,
    double                  offset,
    char* const             buf,
    size_t                  size,
    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, size, needParens ? "(%.*g " : "%.*g ", DBL_DIG,
                scale);
        if (0 > n) {
            nchar = n;
        }
        else {
            nchar += n;
            size = SUBTRACT_SIZET(size, n);
        }
    }

    if (0 <= nchar) {
        n = format(unit, buf+nchar, size, RETURNS_NAME(getId),
                getDefinition, encoding, 1);

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

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

            if (nchar >= 0) {
                if (needParens) {
                    n = snprintf(buf+nchar, size, "%s", ")");
                    if (0 > n) {
                        nchar = n;
                    }
                    else {
                        nchar += n;
                        size = SUBTRACT_SIZET(size, 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 that would be printed if
 *                    "size" were sufficiently large excluding the
 *                    terminating NUL.
 */
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.
 *    size        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 that would be printed if
 *                    "size" were sufficiently large excluding the
 *                    terminating NUL.
 */
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,
    size_t          size,
    IdGetter            getId,
    const int           getDefinition,
    const ut_encoding   encoding,
    const int           addParens)
{
    int           n;
    int           nchar = 0;

    if (addParens) {
      n = snprintf(buf, size, "%s", "(");
        if (0 > n) {
            nchar = -1;
        }
        else {
            nchar += n;
            size = SUBTRACT_SIZET(size, n);
        }
    }

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

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

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

          n =  snprintf(buf+nchar, size,
            useSeparators
                ? " %s %d-%02d-%02d %02d:%02d"
                : " %s %d%02d%02dT%02d%02d",
            useNames ? "since" : "@",
            year, month, day, hour, minute);
            if (0 > n) {
                nchar = -1;
            }
            else {
                nchar += n;
                size = SUBTRACT_SIZET(size, n);
            }

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

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

            if (nchar >= 0) {
                    n = snprintf(buf+nchar, size, "%s", 
                            addParens ? " UTC)" : " UTC");
                    if (0 > n) {
                        nchar = -1;
                    }
                    else {
                        nchar += n;
                        size = SUBTRACT_SIZET(size, 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 that would be printed if
 *                    "size" were sufficiently large excluding the
 *                    terminating NUL.
 */
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.
 *    size        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 that would be printed if
 *                    "size" were sufficiently large excluding the
 *                    terminating NUL.
 */
static int
printLogarithmic(
    const double          base,
    const ut_unit* const      reference,
    char*               buf,
    size_t                      size,
    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, size, "lb(re %s%s)", amount, refSpec);
      }
      else if (base == M_E) {
          nchar = snprintf(buf, size, "ln(re %s%s)", amount, refSpec);
      }
      else if (base == 10) {
          nchar = snprintf(buf, size, "lg(re %s%s)", amount, refSpec);
      }
      else {
          nchar = snprintf(buf, size,
            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 bytes that would be printed if
 *                    "size" were sufficiently large excluding the
 *                    terminating NUL.
 */
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