mirror of
https://github.com/Unidata/netcdf-c.git
synced 2024-11-27 07:30:33 +08:00
1251 lines
30 KiB
C
1251 lines
30 KiB
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", "<EFBFBD>"); /* 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 ? "<EFBFBD>" : "<EFBFBD>"); /* 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;
|
||
}
|