summaryrefslogtreecommitdiff
path: root/src/std_strlprintf.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/std_strlprintf.c')
-rw-r--r--src/std_strlprintf.c759
1 files changed, 759 insertions, 0 deletions
diff --git a/src/std_strlprintf.c b/src/std_strlprintf.c
new file mode 100644
index 0000000..c927dc8
--- /dev/null
+++ b/src/std_strlprintf.c
@@ -0,0 +1,759 @@
+/*
+ * 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;
+}