760 lines
21 KiB
C
760 lines
21 KiB
C
/*
|
|
* Copyright (c) 2019, The Linux Foundation. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are
|
|
* met:
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above
|
|
* copyright notice, this list of conditions and the following
|
|
* disclaimer in the documentation and/or other materials provided
|
|
* with the distribution.
|
|
* * Neither the name of The Linux Foundation nor the names of its
|
|
* contributors may be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
|
|
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
|
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
|
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
|
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
|
|
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "AEEstd.h"
|
|
#include "AEEBufBound.h"
|
|
#include "AEEsmath.h"
|
|
#include "AEEStdErr.h"
|
|
#include "std_dtoa.h"
|
|
//#include "math.h"
|
|
|
|
//==============================================================================
|
|
// Macro definitions
|
|
//==============================================================================
|
|
|
|
#define ISDIGIT(c) ( (c) >= '0' && (c) <= '9')
|
|
#define TOLOWER(c) ( (c) | 32 ) // works only for letters
|
|
#define FAILED(b) ( (b) != AEE_SUCCESS ? TRUE : FALSE )
|
|
#define CLEANUP_ON_ERROR(b,l) if( FAILED( b ) ) { goto l; }
|
|
#define ROUND(d, p) fp_round( d, p )
|
|
#define FP_POW_10(n) fp_pow_10(n)
|
|
|
|
//==============================================================================
|
|
// Type definitions
|
|
//==============================================================================
|
|
|
|
|
|
// Formatting flags
|
|
|
|
#define FF_PLUS 1 // '+'
|
|
#define FF_MINUS 2 // '-'
|
|
#define FF_POUND 4 // '#'
|
|
#define FF_BLANK 8 // ' '
|
|
#define FF_ZERO 16 // '0'
|
|
|
|
typedef struct {
|
|
|
|
// Parsed values (from "%..." expression)
|
|
|
|
int flags; // FF_PLUS, FF_MINUS, etc.
|
|
char cType; // d, s, c, x, X, etc.
|
|
int32 nWidth; // number preceding '.' : controls padding
|
|
int32 nPrecision; // number following '.' (-1 if not given)
|
|
|
|
// Computed values
|
|
|
|
const char * pszStr; // string holding prefix + value
|
|
int nPrefix; // # of numeric prefix bytes in pszStr[]
|
|
int nLen; // length of string (after prefix)
|
|
int nNumWidth; // minimum numeric value size (pad with '0')
|
|
|
|
} FieldFormat;
|
|
|
|
typedef int (*pfnFormatFloat)(FieldFormat* me, double dNumber, char* pcBuffer);
|
|
|
|
//==============================================================================
|
|
// Function definitions
|
|
//==============================================================================
|
|
|
|
// Read an unsigned decimal integer
|
|
//
|
|
static int ScanDecimal(const char **ppsz)
|
|
{
|
|
int n = 0;
|
|
const char *psz;
|
|
|
|
for (psz = *ppsz; ISDIGIT(*psz); ++psz) {
|
|
n = n*10 + (int) (*psz - '0');
|
|
}
|
|
*ppsz = psz;
|
|
return n;
|
|
}
|
|
|
|
|
|
#define FORMATNUMBER_SIZE 24 // octal: 22 + '0' + null ; decimal: 20 + sign + null
|
|
|
|
|
|
// Convert number to string, setting computed fields in FieldFormat.
|
|
//
|
|
// pcBuf[] must have room for at least FORMATNUMBER_SIZE characters
|
|
// return value: length of string.
|
|
//
|
|
static __inline void
|
|
FormatNumber(FieldFormat *me, char pcBuf[FORMATNUMBER_SIZE], uint64 uNum64)
|
|
{
|
|
char cType = me->cType;
|
|
const char *cpszDigits;
|
|
char *pc = pcBuf;
|
|
int nBase;
|
|
char *pcRev;
|
|
|
|
if (cType == 'p') {
|
|
cType = 'X';
|
|
me->nPrecision = 8;
|
|
}
|
|
|
|
if (me->nPrecision >= 0) {
|
|
me->nNumWidth = me->nPrecision;
|
|
// Odd thing: '0' flag is ignored for numbers when precision is
|
|
// specified.
|
|
me->flags &= ~FF_ZERO;
|
|
} else {
|
|
me->nNumWidth = 1;
|
|
}
|
|
|
|
// Output prefix
|
|
|
|
if (( 'd' == cType || 'i' == cType)) {
|
|
if ((int64)uNum64 < 0) {
|
|
*pc++ = '-';
|
|
uNum64 = (uint64)-(int64)uNum64;
|
|
} else if (me->flags & FF_PLUS) {
|
|
*pc++ = '+';
|
|
} else if (me->flags & FF_BLANK) {
|
|
*pc++ = ' ';
|
|
}
|
|
}
|
|
|
|
if ((me->flags & FF_POUND) && 0 != uNum64) {
|
|
if ('x' == TOLOWER(cType)) {
|
|
*pc++ = '0';
|
|
*pc++ = cType;
|
|
} else if ('o' == cType) {
|
|
*pc++ = '0';
|
|
// Odd thing about libc printf: "0" prefix counts as part of minimum
|
|
// width, but "0x" prefix does not.
|
|
--me->nNumWidth;
|
|
}
|
|
}
|
|
me->nPrefix = pc - pcBuf;
|
|
|
|
// Output unsigned numeric value
|
|
|
|
nBase = ('o' == cType ? 8 :
|
|
'x' == TOLOWER(cType) ? 16 :
|
|
10);
|
|
cpszDigits = ((cType == 'X') ? "0123456789ABCDEF"
|
|
: "0123456789abcdef");
|
|
|
|
pcRev = pc;
|
|
|
|
while (uNum64) {
|
|
*pc++ = cpszDigits[uNum64 % (unsigned)nBase];
|
|
uNum64 /= (unsigned)nBase;
|
|
}
|
|
|
|
*pc = '\0';
|
|
|
|
me->pszStr = pcBuf;
|
|
me->nLen = pc - pcRev;
|
|
|
|
// Reverse string
|
|
|
|
--pc;
|
|
for (; pcRev < pc; ++pcRev, --pc) {
|
|
char c = *pc;
|
|
*pc = *pcRev;
|
|
*pcRev = c;
|
|
}
|
|
}
|
|
|
|
//
|
|
// This function converts the input floating point number dNumber to an
|
|
// ASCII string using either %f or %F formatting. This functions assumes
|
|
// that dNumer is a valid floating point number (i.e., dNumber is NOT
|
|
// +/-INF or NaN). The size of the output buffer pcBuffer should be at
|
|
// least STD_DTOA_FORMAT_FLOAT_SIZE.
|
|
//
|
|
static int ConvertFloat(FieldFormat* me, double dNumber, char* pcBuffer,
|
|
int nBufSize)
|
|
{
|
|
int nError = AEE_SUCCESS;
|
|
int32 nPrecision = 0;
|
|
int nIndex = 0;
|
|
BufBound OutBuf;
|
|
char szIntegerPart[STD_DTOA_FORMAT_INTEGER_SIZE] = {0};
|
|
char szFractionPart[STD_DTOA_FORMAT_FRACTION_SIZE] = {0};
|
|
int nExponent = 0;
|
|
char cType = TOLOWER(me->cType);
|
|
|
|
// Set the precision for conversion
|
|
nPrecision = me->nPrecision;
|
|
if (nPrecision < 0) {
|
|
// No precision was specified, set it to the default value if the
|
|
// format specifier is not %a
|
|
if (cType != 'a') {
|
|
nPrecision = STD_DTOA_DEFAULT_FLOAT_PRECISION;
|
|
}
|
|
}
|
|
else if ((0 == nPrecision) && ('g' == cType)) {
|
|
nPrecision = 1;
|
|
}
|
|
|
|
if (cType != 'a') {
|
|
// For %g, check whether to use %e of %f formatting style.
|
|
// Also, set the precision value accordingly since in this case the user
|
|
// specified value is really the number of significant digits.
|
|
// These next few steps should be skipped if the input number is 0.
|
|
if (dNumber != 0.0) {
|
|
nExponent = fp_log_10(dNumber);
|
|
if ('g' == cType) {
|
|
if ((nExponent < -4) || (nExponent >= nPrecision)) {
|
|
cType = 'e';
|
|
nPrecision = nPrecision - 1;
|
|
}
|
|
else {
|
|
cType = 'f';
|
|
nPrecision = nPrecision - nExponent - 1;
|
|
}
|
|
}
|
|
|
|
// For %e, convert the number to the form d.ddd
|
|
if ('e' == cType) {
|
|
dNumber = dNumber / FP_POW_10(nExponent);
|
|
}
|
|
|
|
// Now, round the number to the specified precision
|
|
dNumber = ROUND(dNumber, nPrecision);
|
|
|
|
// For %e, the rounding operation may have resulted in a number dd.ddd
|
|
// Reconvert it to the form d.ddd
|
|
if (('e' == cType) && ((dNumber >= 10.0) || (dNumber <= -10.0))) {
|
|
dNumber = dNumber / 10.0;
|
|
nExponent++;
|
|
}
|
|
}
|
|
|
|
// Convert the decmial number to string
|
|
nError = std_dtoa_decimal(dNumber, nPrecision, szIntegerPart, szFractionPart);
|
|
CLEANUP_ON_ERROR(nError, bail);
|
|
}
|
|
else
|
|
{
|
|
// Conver the hex floating point number to string
|
|
nError = std_dtoa_hex(dNumber, nPrecision, me->cType, szIntegerPart,
|
|
szFractionPart, &nExponent);
|
|
CLEANUP_ON_ERROR(nError, bail);
|
|
}
|
|
|
|
|
|
//
|
|
// Write the output as per the specified format.
|
|
// First: Check for any prefixes that need to be added to the output.
|
|
// The only possible prefixes are '-', '+' or ' '. The following rules
|
|
// are applicable:
|
|
// 1. One and only one prefix will be applicable at any time.
|
|
// 2. If the number is negative, then '+' and ' ' are not applicable.
|
|
// 3. For positive numbers, the prefix '+' takes precedence over ' '.
|
|
//
|
|
// In addition, we were dealing with a hex floating point number (%a),
|
|
// then we need to write of the 0x prefix.
|
|
//
|
|
BufBound_Init(&OutBuf, pcBuffer, nBufSize);
|
|
if (dNumber < 0.0) {
|
|
// The '-' sign would have already been added to the szIntegerPart by
|
|
// the conversion function.
|
|
me->nPrefix = 1;
|
|
}
|
|
if (dNumber >= 0.0){
|
|
if (me->flags & FF_PLUS) {
|
|
BufBound_Putc(&OutBuf, '+');
|
|
me->nPrefix = 1;
|
|
}
|
|
else if(me->flags & FF_BLANK) {
|
|
BufBound_Putc(&OutBuf, ' ');
|
|
me->nPrefix = 1;
|
|
}
|
|
}
|
|
|
|
// For %a, write out the 0x prefix
|
|
if ('a' == cType) {
|
|
BufBound_Putc(&OutBuf, '0');
|
|
BufBound_Putc(&OutBuf, ('a' == me->cType) ? 'x' : 'X');
|
|
me->nPrefix += 2;
|
|
}
|
|
|
|
// Second: Write the integer part
|
|
BufBound_Puts(&OutBuf, szIntegerPart);
|
|
|
|
// Third: Write the decimal point followed by the fraction part.
|
|
// For %g, we need to truncate the trailing zeros in the fraction.
|
|
// Skip this if the '#' flag is specified
|
|
if (!(me->flags & FF_POUND) && ('g' == TOLOWER(me->cType))) {
|
|
for (nIndex = std_strlen(szFractionPart) - 1;
|
|
(nIndex >= 0) && (szFractionPart[nIndex] == '0'); nIndex--) {
|
|
szFractionPart[nIndex] = '\0';
|
|
}
|
|
}
|
|
|
|
// The decimal point is specified only if there are some decimal digits.
|
|
// However, if the '#' format specifier is present then the decimal point
|
|
// will be present.
|
|
if ((me->flags & FF_POUND) || (*szFractionPart != 0)) {
|
|
BufBound_Putc(&OutBuf, '.');
|
|
|
|
// Write the fraction part
|
|
BufBound_Puts(&OutBuf, szFractionPart);
|
|
}
|
|
|
|
// For %e and %a, write out the exponent
|
|
if (('e' == cType) || ('a' == cType)) {
|
|
char* pcExpStart = NULL;
|
|
char* pcExpEnd = NULL;
|
|
char cTemp = 0;
|
|
|
|
if ('a' == me->cType) {
|
|
BufBound_Putc(&OutBuf, 'p');
|
|
}
|
|
else if ('A' == me->cType) {
|
|
BufBound_Putc(&OutBuf, 'P');
|
|
}
|
|
else if (('e' == me->cType) || ('g' == me->cType)) {
|
|
BufBound_Putc(&OutBuf, 'e');
|
|
}
|
|
else {
|
|
BufBound_Putc(&OutBuf, 'E');
|
|
}
|
|
|
|
// Write the exponent sign
|
|
if (nExponent < 0) {
|
|
BufBound_Putc(&OutBuf, '-');
|
|
nExponent = -nExponent;
|
|
}
|
|
else {
|
|
BufBound_Putc(&OutBuf, '+');
|
|
}
|
|
|
|
// Write out the exponent.
|
|
// For %e, the exponent should at least be two digits.
|
|
// The exponent to be written will be at most 4 digits as any
|
|
// overflow would have been take care of by now.
|
|
if (BufBound_Left(&OutBuf) >= 4) {
|
|
if ('e' == cType) {
|
|
if (nExponent < 10) {
|
|
BufBound_Putc(&OutBuf, '0');
|
|
}
|
|
}
|
|
|
|
pcExpStart = OutBuf.pcWrite;
|
|
do {
|
|
BufBound_Putc(&OutBuf, '0' + (nExponent % 10));
|
|
nExponent /= 10;
|
|
} while (nExponent);
|
|
pcExpEnd = OutBuf.pcWrite - 1;
|
|
|
|
// Reverse the exponent
|
|
for (; pcExpStart < pcExpEnd; pcExpStart++, pcExpEnd--) {
|
|
cTemp = *pcExpStart;
|
|
*pcExpStart = *pcExpEnd;
|
|
*pcExpEnd = cTemp;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Null-terminate the string
|
|
BufBound_ForceNullTerm(&OutBuf);
|
|
|
|
// Set the output parameters
|
|
// We do not care if there was enough space in the output buffer or not.
|
|
// The output would be truncated to a maximum length of
|
|
// STD_DTOA_FORMAT_FLOAT_SIZE.
|
|
me->pszStr = OutBuf.pcBuf;
|
|
me->nLen = BufBound_ReallyWrote(&OutBuf) - me->nPrefix - 1;
|
|
|
|
bail:
|
|
|
|
return nError;
|
|
}
|
|
|
|
//
|
|
// This is a wrapper function that converts an input floating point number
|
|
// to a string based on a given format specifier %e, %f or %g. It first checks
|
|
// if the specified number is a valid floating point number before calling
|
|
// the function that does the conversion.
|
|
//
|
|
// The size of the output buffer pcBuffer should be at least STD_DTOA_FORMAT_FLOAT_SIZE.
|
|
//
|
|
static int FormatFloat(FieldFormat* me, double dNumber,
|
|
char pcBuffer[STD_DTOA_FORMAT_FLOAT_SIZE])
|
|
{
|
|
int nError = AEE_SUCCESS;
|
|
FloatingPointType NumberType = FP_TYPE_UNKOWN;
|
|
|
|
// Check for error conditions
|
|
if (NULL == pcBuffer) {
|
|
nError = AEE_EBADPARM;
|
|
goto bail;
|
|
}
|
|
|
|
// Initialize the output params first
|
|
me->nLen = 0;
|
|
me->nPrefix = 0;
|
|
|
|
// Check for special cases such as NaN and Infinity
|
|
nError = fp_check_special_cases(dNumber, &NumberType);
|
|
CLEANUP_ON_ERROR(nError, bail);
|
|
|
|
switch(NumberType) {
|
|
case FP_TYPE_NEGATIVE_INF:
|
|
|
|
if (('E' == me->cType) || ('F' == me->cType) || ('G' == me->cType)) {
|
|
me->nLen = std_strlcpy(pcBuffer, STD_DTOA_NEGATIVE_INF_UPPER_CASE,
|
|
STD_DTOA_FORMAT_FLOAT_SIZE);
|
|
}
|
|
else {
|
|
me->nLen = std_strlcpy(pcBuffer, STD_DTOA_NEGATIVE_INF_LOWER_CASE,
|
|
STD_DTOA_FORMAT_FLOAT_SIZE);
|
|
}
|
|
|
|
// Don't pad with 0's
|
|
me->flags &= ~FF_ZERO;
|
|
|
|
break;
|
|
|
|
case FP_TYPE_POSITIVE_INF:
|
|
|
|
if (('E' == me->cType) || ('F' == me->cType) || ('G' == me->cType)) {
|
|
me->nLen = std_strlcpy(pcBuffer, STD_DTOA_POSITIVE_INF_UPPER_CASE,
|
|
STD_DTOA_FORMAT_FLOAT_SIZE);
|
|
}
|
|
else {
|
|
me->nLen = std_strlcpy(pcBuffer, STD_DTOA_POSITIVE_INF_LOWER_CASE,
|
|
STD_DTOA_FORMAT_FLOAT_SIZE);
|
|
}
|
|
|
|
// Don't pad with 0's
|
|
me->flags &= ~FF_ZERO;
|
|
|
|
break;
|
|
|
|
case FP_TYPE_NAN:
|
|
|
|
if (('E' == me->cType) || ('F' == me->cType) || ('G' == me->cType)) {
|
|
me->nLen = std_strlcpy(pcBuffer, STD_DTOA_NAN_UPPER_CASE,
|
|
STD_DTOA_FORMAT_FLOAT_SIZE);
|
|
}
|
|
else
|
|
{
|
|
me->nLen = std_strlcpy(pcBuffer, STD_DTOA_NAN_LOWER_CASE,
|
|
STD_DTOA_FORMAT_FLOAT_SIZE);
|
|
}
|
|
|
|
// Don't pad with 0's
|
|
me->flags &= ~FF_ZERO;
|
|
|
|
break;
|
|
|
|
case FP_TYPE_GENERAL:
|
|
|
|
nError = ConvertFloat(me, dNumber, pcBuffer,
|
|
STD_DTOA_FORMAT_FLOAT_SIZE);
|
|
CLEANUP_ON_ERROR(nError, bail);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
// This should only happen if this function has been modified
|
|
// to support other special cases and this block has not been
|
|
// updated.
|
|
nError = AEE_EFAILED;
|
|
goto bail;
|
|
}
|
|
|
|
// Set the output parameters
|
|
me->pszStr = pcBuffer;
|
|
|
|
|
|
bail:
|
|
|
|
return nError;
|
|
}
|
|
|
|
static int std_strlprintf_inner(char *pszDest, int nDestSize,
|
|
const char *cpszFmt, AEEVaList args,
|
|
pfnFormatFloat pfnFormatFloatFunc)
|
|
{
|
|
BufBound bb;
|
|
const char *pcIn = cpszFmt;
|
|
|
|
BufBound_Init(&bb, pszDest, nDestSize);
|
|
|
|
for (;;) {
|
|
FieldFormat ff;
|
|
const char *pcEsc;
|
|
char achBuf[FORMATNUMBER_SIZE];
|
|
char achBuf2[STD_DTOA_FORMAT_FLOAT_SIZE];
|
|
char cType;
|
|
boolean bLong = 0;
|
|
|
|
pcEsc = std_strchrend(pcIn, '%');
|
|
BufBound_Write(&bb, pcIn, pcEsc-pcIn);
|
|
|
|
if (0 == *pcEsc) {
|
|
break;
|
|
}
|
|
pcIn = pcEsc+1;
|
|
|
|
//----------------------------------------------------
|
|
// Consume "%..." specifiers:
|
|
//
|
|
// %[FLAGS] [WIDTH] [.PRECISION] [{h | l | I64 | L}]
|
|
//----------------------------------------------------
|
|
|
|
std_memset(&ff, 0, sizeof(FieldFormat));
|
|
ff.nPrecision = -1;
|
|
|
|
// Consume all flags
|
|
for (;;) {
|
|
int f;
|
|
|
|
f = (('+' == *pcIn) ? FF_PLUS :
|
|
('-' == *pcIn) ? FF_MINUS :
|
|
('#' == *pcIn) ? FF_POUND :
|
|
(' ' == *pcIn) ? FF_BLANK :
|
|
('0' == *pcIn) ? FF_ZERO : 0);
|
|
|
|
if (0 == f) {
|
|
break;
|
|
}
|
|
|
|
ff.flags |= f;
|
|
++pcIn;
|
|
}
|
|
|
|
// Consume width
|
|
if ('*' == *pcIn) {
|
|
AEEVA_ARG(args, ff.nWidth, int32);
|
|
pcIn++;
|
|
} else {
|
|
ff.nWidth = ScanDecimal(&pcIn);
|
|
}
|
|
if ((ff.flags & FF_MINUS) && ff.nWidth > 0) {
|
|
ff.nWidth = -ff.nWidth;
|
|
}
|
|
|
|
// Consume precision
|
|
if ('.' == *pcIn) {
|
|
pcIn++;
|
|
if ('*' == *pcIn) { // Can be *... (given in int * param)
|
|
AEEVA_ARG(args, ff.nPrecision, int32);
|
|
pcIn++;
|
|
} else {
|
|
ff.nPrecision = ScanDecimal(&pcIn);
|
|
}
|
|
}
|
|
|
|
// Consume size designator
|
|
{
|
|
static const struct {
|
|
char szPre[3];
|
|
boolean b64;
|
|
} a[] = {
|
|
{ "l", 0, },
|
|
{ "ll", 1, },
|
|
{ "L", 1, },
|
|
{ "j", 1, },
|
|
{ "h", 0, },
|
|
{ "hh", 0, },
|
|
{ "z", 0 }
|
|
};
|
|
|
|
int n = STD_ARRAY_SIZE(a);
|
|
|
|
while (--n >= 0) {
|
|
const char *psz = std_strbegins(pcIn, a[n].szPre);
|
|
if ((const char*)0 != psz) {
|
|
pcIn = psz;
|
|
bLong = a[n].b64;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//----------------------------------------------------
|
|
//
|
|
// Format output values
|
|
//
|
|
//----------------------------------------------------
|
|
|
|
ff.cType = cType = *pcIn++;
|
|
|
|
if ('s' == cType) {
|
|
|
|
// String
|
|
char *psz;
|
|
|
|
AEEVA_ARG(args, psz, char*);
|
|
ff.pszStr = psz;
|
|
ff.nLen = std_strlen(psz);
|
|
if (ff.nPrecision >= 0 && ff.nPrecision < ff.nLen) {
|
|
ff.nLen = ff.nPrecision;
|
|
}
|
|
|
|
} else if ('c' == cType) {
|
|
|
|
// char
|
|
AEEVA_ARG(args, achBuf[0], int);
|
|
achBuf[1] = '\0';
|
|
ff.pszStr = achBuf;
|
|
ff.nLen = 1;
|
|
|
|
} else if ('u' == cType ||
|
|
'o' == cType ||
|
|
'd' == cType ||
|
|
'i' == cType ||
|
|
'p' == cType ||
|
|
'x' == TOLOWER(cType) ) {
|
|
|
|
// int
|
|
uint64 uArg64;
|
|
|
|
if (bLong) {
|
|
AEEVA_ARG(args, uArg64, int64); // See how much room needed
|
|
} else {
|
|
uint32 uArg32;
|
|
AEEVA_ARG(args, uArg32, int32); // See how much room needed
|
|
uArg64 = uArg32;
|
|
if ('d' == cType || 'i' == cType) {
|
|
uArg64 = (uint64)(int64)(int32)uArg32;
|
|
}
|
|
}
|
|
|
|
FormatNumber(&ff, achBuf, uArg64);
|
|
|
|
} else if (pfnFormatFloatFunc &&
|
|
('e' == TOLOWER(cType) ||
|
|
'f' == TOLOWER(cType) ||
|
|
'g' == TOLOWER(cType) ||
|
|
'a' == TOLOWER(cType))) {
|
|
|
|
// float
|
|
int nError = AEE_SUCCESS;
|
|
double dNumber;
|
|
|
|
AEEVA_ARG(args, dNumber, double);
|
|
nError = pfnFormatFloatFunc(&ff, dNumber, achBuf2);
|
|
if (FAILED(nError)) {
|
|
continue;
|
|
}
|
|
|
|
} else if ('\0' == cType) {
|
|
|
|
// premature end
|
|
break;
|
|
|
|
} else {
|
|
// Unknown type
|
|
BufBound_Putc(&bb, cType);
|
|
continue;
|
|
}
|
|
|
|
// FieldFormat computed variables + nWidth controls output
|
|
|
|
if (ff.flags & FF_ZERO) {
|
|
ff.nNumWidth = ff.nWidth - ff.nPrefix;
|
|
}
|
|
|
|
{
|
|
int nLen1 = ff.nLen;
|
|
int nLen2 = STD_MAX(ff.nNumWidth, nLen1) + ff.nPrefix;
|
|
|
|
// Putnc() safely ignores negative sizes
|
|
BufBound_Putnc(&bb, ' ', smath_Sub(ff.nWidth,nLen2));
|
|
BufBound_Write(&bb, ff.pszStr, ff.nPrefix);
|
|
BufBound_Putnc(&bb, '0', smath_Sub(ff.nNumWidth, nLen1));
|
|
BufBound_Write(&bb, ff.pszStr+ff.nPrefix, nLen1);
|
|
BufBound_Putnc(&bb, ' ', smath_Sub(-nLen2, ff.nWidth));
|
|
}
|
|
}
|
|
|
|
AEEVA_END(args);
|
|
|
|
BufBound_ForceNullTerm(&bb);
|
|
|
|
/* Return number of bytes required regardless if buffer bound was reached */
|
|
|
|
/* Note that we subtract 1 because the NUL byte which was added in
|
|
BufBound_ForceNullTerm() is counted as a written byte; the semantics
|
|
of both the ...printf() functions and the strl...() functions call for
|
|
the NUL byte to be excluded from the count. */
|
|
|
|
return BufBound_Wrote(&bb)-1;
|
|
}
|
|
|
|
int std_vstrlprintf(char *pszDest, int nDestSize,
|
|
const char *cpszFmt,
|
|
AEEVaList args)
|
|
{
|
|
return std_strlprintf_inner(pszDest, nDestSize, cpszFmt, args, NULL);
|
|
}
|
|
|
|
int std_vsnprintf(char *pszDest, int nDestSize,
|
|
const char *cpszFmt,
|
|
AEEVaList args)
|
|
/*
|
|
Same as std_vstrlprintf with the additional support of floating point
|
|
conversion specifiers - %e, %f, %g and %a
|
|
*/
|
|
{
|
|
return std_strlprintf_inner(pszDest, nDestSize, cpszFmt, args, FormatFloat);
|
|
}
|
|
|
|
int std_strlprintf(char *pszDest, int nDestSize, const char *pszFmt, ...)
|
|
{
|
|
int nRet;
|
|
AEEVaList args;
|
|
|
|
AEEVA_START(args, pszFmt);
|
|
|
|
nRet = std_vstrlprintf(pszDest, nDestSize, pszFmt, args);
|
|
|
|
AEEVA_END(args);
|
|
|
|
return nRet;
|
|
}
|
|
|
|
int std_snprintf(char *pszDest, int nDestSize, const char *pszFmt, ...)
|
|
/*
|
|
Same as std_strlprintf with the additional support of floating point
|
|
conversion specifiers - %e, %f, %g and %a
|
|
*/
|
|
{
|
|
int nRet;
|
|
AEEVaList args;
|
|
|
|
AEEVA_START(args, pszFmt);
|
|
|
|
nRet = std_vsnprintf(pszDest, nDestSize, pszFmt, args);
|
|
|
|
AEEVA_END(args);
|
|
|
|
return nRet;
|
|
}
|