mirror of
https://github.com/samba-team/samba.git
synced 2025-12-18 08:23:51 +03:00
925 lines
22 KiB
C
925 lines
22 KiB
C
/**
|
|
* @file mprPrintf.c
|
|
* @brief Printf routines safe for embedded programming
|
|
* @overview This module provides safe replacements for the standard
|
|
* printf formatting routines.
|
|
* @remarks Most routines in this file are not thread-safe. It is the callers
|
|
* responsibility to perform all thread synchronization.
|
|
*/
|
|
|
|
/*
|
|
* @copy default
|
|
*
|
|
* Copyright (c) Mbedthis Software LLC, 2003-2006. All Rights Reserved.
|
|
*
|
|
* This software is distributed under commercial and open source licenses.
|
|
* You may use the GPL open source license described below or you may acquire
|
|
* a commercial license from Mbedthis Software. You agree to be fully bound
|
|
* by the terms of either license. Consult the LICENSE.TXT distributed with
|
|
* this software for full details.
|
|
*
|
|
* This software is open source; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the
|
|
* Free Software Foundation; either version 2 of the License, or (at your
|
|
* option) any later version. See the GNU General Public License for more
|
|
* details at: http://www.mbedthis.com/downloads/gplLicense.html
|
|
*
|
|
* This program is distributed WITHOUT ANY WARRANTY; without even the
|
|
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
*
|
|
* This GPL license does NOT permit incorporating this software into
|
|
* proprietary programs. If you are unable to comply with the GPL, you must
|
|
* acquire a commercial license to use this software. Commercial licenses
|
|
* for this software and support services are available from Mbedthis
|
|
* Software at http://www.mbedthis.com
|
|
*
|
|
* @end
|
|
*/
|
|
|
|
/********************************** Includes **********************************/
|
|
/*
|
|
* We need to use the underlying str(cpy) routines to implement our safe
|
|
* alternatives
|
|
*/
|
|
#if !DOXYGEN
|
|
#define UNSAFE_FUNCTIONS_OK 1
|
|
#endif
|
|
|
|
#include "mpr.h"
|
|
|
|
/*********************************** Defines **********************************/
|
|
/*
|
|
* Class definitions
|
|
*/
|
|
#define CLASS_NORMAL 0 /* [All other] Normal characters */
|
|
#define CLASS_PERCENT 1 /* [%] Begin format */
|
|
#define CLASS_MODIFIER 2 /* [-+ #,] Modifiers */
|
|
#define CLASS_ZERO 3 /* [0] Special modifier */
|
|
#define CLASS_STAR 4 /* [*] Width supplied by arg */
|
|
#define CLASS_DIGIT 5 /* [1-9] Field widths */
|
|
#define CLASS_DOT 6 /* [.] Introduce precision */
|
|
#define CLASS_BITS 7 /* [hlL] Length bits */
|
|
#define CLASS_TYPE 8 /* [cdfinopsSuxX] Type specifiers */
|
|
|
|
#define STATE_NORMAL 0 /* Normal chars in format string */
|
|
#define STATE_PERCENT 1 /* "%" */
|
|
#define STATE_MODIFIER 2 /* Read flag */
|
|
#define STATE_WIDTH 3 /* Width spec */
|
|
#define STATE_DOT 4 /* "." */
|
|
#define STATE_PRECISION 5 /* Precision spec */
|
|
#define STATE_BITS 6 /* Size spec */
|
|
#define STATE_TYPE 7 /* Data type */
|
|
#define STATE_COUNT 8
|
|
|
|
/*
|
|
* Format: %[modifier][width][precision][bits][type]
|
|
*
|
|
* #define CLASS_MODIFIER 2 [-+ #,] Modifiers
|
|
* #define CLASS_BITS 7 [hlL] Length bits
|
|
*/
|
|
|
|
|
|
/*
|
|
* Flags
|
|
*/
|
|
#define SPRINTF_LEFT 0x1 /* Left align */
|
|
#define SPRINTF_SIGN 0x2 /* Always sign the result */
|
|
#define SPRINTF_LEAD_SPACE 0x4 /* put leading space for +ve numbers */
|
|
#define SPRINTF_ALTERNATE 0x8 /* Alternate format */
|
|
#define SPRINTF_LEAD_ZERO 0x10 /* Zero pad */
|
|
#define SPRINTF_SHORT 0x20 /* 16-bit */
|
|
#define SPRINTF_LONG 0x40 /* 32-bit */
|
|
#if BLD_FEATURE_INT64
|
|
#define SPRINTF_LONGLONG 0x80 /* 64-bit */
|
|
#endif
|
|
#define SPRINTF_COMMA 0x100 /* Thousand comma separators */
|
|
#define SPRINTF_UPPER_CASE 0x200 /* As the name says for numbers */
|
|
|
|
typedef struct Format {
|
|
uchar *buf;
|
|
uchar *endbuf;
|
|
uchar *start;
|
|
uchar *end;
|
|
int growBy;
|
|
int maxsize;
|
|
|
|
int precision;
|
|
int radix;
|
|
int width;
|
|
int flags;
|
|
int len;
|
|
} Format;
|
|
|
|
static int growBuf(MPR_LOC_DEC(ctx, loc), Format *fmt);
|
|
|
|
#define BPUT(ctx, loc, fmt, c) \
|
|
if (1) { \
|
|
/* Less one to allow room for the null */ \
|
|
if ((fmt)->end >= ((fmt)->endbuf - sizeof(char))) { \
|
|
if (growBuf(MPR_LOC_PASS(ctx, loc), fmt)) { \
|
|
*(fmt)->end++ = (c); \
|
|
} \
|
|
} else { \
|
|
*(fmt)->end++ = (c); \
|
|
} \
|
|
} else
|
|
|
|
#define BPUTNULL(ctx, loc, fmt) \
|
|
if (1) { \
|
|
if ((fmt)->end > (fmt)->endbuf) { \
|
|
if (growBuf(MPR_LOC_PASS(ctx, loc), fmt)) { \
|
|
*(fmt)->end = '\0'; \
|
|
} \
|
|
} else { \
|
|
*(fmt)->end = '\0'; \
|
|
} \
|
|
} else
|
|
|
|
/******************************************************************************/
|
|
|
|
#if BLD_FEATURE_INT64
|
|
#define unum uint64
|
|
#define num int64
|
|
#else
|
|
#define unum uint
|
|
#define num int
|
|
#endif
|
|
|
|
/***************************** Forward Declarations ***************************/
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
static int getState(char c, int state);
|
|
static int mprSprintfCore(MPR_LOC_DEC(ctx, loc), char **s,
|
|
int maxsize, const char *fmt, va_list arg);
|
|
static void outNum(MPR_LOC_DEC(ctx, loc), Format *fmt, const char *prefix,
|
|
unum val);
|
|
|
|
#if BLD_FEATURE_FLOATING_POINT
|
|
static void outFloat(MPR_LOC_DEC(ctx, loc), Format *fmt, char specChar,
|
|
double value);
|
|
#endif
|
|
|
|
/******************************************************************************/
|
|
|
|
int mprPrintf(MprCtx ctx, const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
char *buf;
|
|
int len;
|
|
MprApp *app;
|
|
|
|
/* No asserts here as this is used as part of assert reporting */
|
|
|
|
app = mprGetApp(ctx);
|
|
|
|
va_start(ap, fmt);
|
|
len = mprAllocVsprintf(MPR_LOC_ARGS(ctx), &buf, 0, fmt, ap);
|
|
va_end(ap);
|
|
if (len >= 0 && app->console) {
|
|
len = mprWrite(app->console, buf, len);
|
|
}
|
|
mprFree(buf);
|
|
|
|
return len;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
int mprErrorPrintf(MprCtx ctx, const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
char *buf;
|
|
int len;
|
|
MprApp *app;
|
|
|
|
/* No asserts here as this is used as part of assert reporting */
|
|
|
|
app = mprGetApp(ctx);
|
|
|
|
va_start(ap, fmt);
|
|
len = mprAllocVsprintf(MPR_LOC_ARGS(ctx), &buf, 0, fmt, ap);
|
|
va_end(ap);
|
|
if (len >= 0 && app->error) {
|
|
len = mprWrite(app->error, buf, len);
|
|
}
|
|
mprFree(buf);
|
|
|
|
return len;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
int mprFprintf(MprFile *file, const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
char *buf;
|
|
int len;
|
|
|
|
if (file == 0) {
|
|
return MPR_ERR_BAD_HANDLE;
|
|
}
|
|
|
|
va_start(ap, fmt);
|
|
len = mprAllocVsprintf(MPR_LOC_ARGS(file), &buf, 0, fmt, ap);
|
|
va_end(ap);
|
|
|
|
if (len >= 0) {
|
|
len = mprWrite(file, buf, len);
|
|
}
|
|
mprFree(buf);
|
|
return len;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/*
|
|
* Printf with a static buffer. Used internally only. WILL NOT MALLOC.
|
|
*/
|
|
|
|
int mprStaticPrintf(MprCtx ctx, const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
char buf[MPR_MAX_STRING];
|
|
char *bufp;
|
|
int len;
|
|
MprApp *app;
|
|
|
|
app = mprGetApp(ctx);
|
|
|
|
va_start(ap, fmt);
|
|
bufp = buf;
|
|
len = mprSprintfCore(MPR_LOC_ARGS(0), &bufp, MPR_MAX_STRING, fmt, ap);
|
|
va_end(ap);
|
|
if (len >= 0) {
|
|
len = mprWrite(app->console, buf, len);
|
|
}
|
|
return len;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
int mprSprintf(char *buf, int n, const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
int result;
|
|
|
|
mprAssert(buf);
|
|
mprAssert(fmt);
|
|
mprAssert(n > 0);
|
|
|
|
va_start(ap, fmt);
|
|
result = mprSprintfCore(MPR_LOC_ARGS(0), &buf, n, fmt, ap);
|
|
va_end(ap);
|
|
return result;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
int mprVsprintf(char *buf, int n, const char *fmt, va_list arg)
|
|
{
|
|
mprAssert(buf);
|
|
mprAssert(fmt);
|
|
mprAssert(n > 0);
|
|
|
|
return mprSprintfCore(MPR_LOC_ARGS(0), &buf, n, fmt, arg);
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
int mprAllocSprintf(MPR_LOC_DEC(ctx, loc), char **buf, int maxSize,
|
|
const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
int result;
|
|
|
|
mprAssert(buf);
|
|
mprAssert(fmt);
|
|
|
|
*buf = 0;
|
|
va_start(ap, fmt);
|
|
result = mprSprintfCore(MPR_LOC_PASS(ctx, loc), buf, maxSize, fmt, ap);
|
|
va_end(ap);
|
|
return result;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
int mprAllocVsprintf(MPR_LOC_DEC(ctx, loc), char **buf, int maxSize,
|
|
const char *fmt, va_list arg)
|
|
{
|
|
mprAssert(buf);
|
|
mprAssert(fmt);
|
|
|
|
*buf = 0;
|
|
return mprSprintfCore(MPR_LOC_PASS(ctx, loc), buf, maxSize, fmt, arg);
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
static int getState(char c, int state)
|
|
{
|
|
/*
|
|
* Declared here to remove all static / globals
|
|
* FUTURE OPT -- need to measure this. Could be slow on BREW.
|
|
*/
|
|
|
|
char stateMap[] = {
|
|
/* STATES: Normal Percent Modifier Width Dot Prec Bits Type */
|
|
/* CLASS 0 1 2 3 4 5 6 7 */
|
|
/* Normal 0 */ 0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* Percent 1 */ 1, 0, 1, 1, 1, 1, 1, 1,
|
|
/* Modifier 2 */ 0, 2, 2, 0, 0, 0, 0, 0,
|
|
/* Zero 3 */ 0, 2, 2, 3, 0, 5, 0, 0,
|
|
/* Star 4 */ 0, 3, 3, 0, 5, 0, 0, 0,
|
|
/* Digit 5 */ 0, 3, 3, 3, 5, 5, 0, 0,
|
|
/* Dot 6 */ 0, 4, 4, 4, 0, 0, 0, 0,
|
|
/* Bits 7 */ 0, 6, 6, 6, 6, 6, 6, 0,
|
|
/* Types 8 */ 0, 7, 7, 7, 7, 7, 7, 0,
|
|
};
|
|
|
|
/*
|
|
* Format: %[modifier][width][precision][bits][type]
|
|
*/
|
|
char classMap[] = {
|
|
/* 0 ' ' ! " # $ % & ' */
|
|
2, 0, 0, 2, 0, 1, 0, 0,
|
|
/* 07 ( ) * + , - . / */
|
|
0, 0, 4, 2, 2, 2, 6, 0,
|
|
/* 10 0 1 2 3 4 5 6 7 */
|
|
3, 5, 5, 5, 5, 5, 5, 5,
|
|
/* 17 8 9 : ; < = > ? */
|
|
5, 5, 0, 0, 0, 0, 0, 0,
|
|
/* 20 @ A B C D E F G */
|
|
0, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 27 H I J K L M N O */
|
|
0, 0, 0, 0, 7, 0, 0, 0,
|
|
/* 30 P Q R S T U V W */
|
|
0, 0, 0, 8, 0, 0, 0, 0,
|
|
/* 37 X Y Z [ \ ] ^ _ */
|
|
8, 0, 0, 0, 0, 0, 0, 0,
|
|
/* 40 ' a b c d e f g */
|
|
0, 0, 0, 8, 8, 0, 8, 0,
|
|
/* 47 h i j k l m n o */
|
|
7, 8, 0, 0, 7, 0, 8, 8,
|
|
/* 50 p q r s t u v w */
|
|
8, 0, 0, 8, 0, 8, 0, 0,
|
|
/* 57 x y z */
|
|
8, 0, 0,
|
|
};
|
|
|
|
int chrClass;
|
|
|
|
if (c < ' ' || c > 'z') {
|
|
chrClass = CLASS_NORMAL;
|
|
} else {
|
|
mprAssert((c - ' ') < (int) sizeof(classMap));
|
|
chrClass = classMap[(c - ' ')];
|
|
}
|
|
mprAssert((chrClass * STATE_COUNT + state) < (int) sizeof(stateMap));
|
|
state = stateMap[chrClass * STATE_COUNT + state];
|
|
return state;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
static int mprSprintfCore(MPR_LOC_DEC(ctx, loc), char **bufPtr,
|
|
int maxsize, const char *spec, va_list arg)
|
|
{
|
|
Format fmt;
|
|
char *cp;
|
|
char c;
|
|
char *sValue;
|
|
num iValue;
|
|
unum uValue;
|
|
int count, i, len, state;
|
|
|
|
mprAssert(bufPtr);
|
|
mprAssert(spec);
|
|
|
|
if (*bufPtr != 0) {
|
|
mprAssert(maxsize > 0);
|
|
fmt.buf = (uchar*) *bufPtr;
|
|
fmt.endbuf = &fmt.buf[maxsize];
|
|
fmt.growBy = 0;
|
|
} else {
|
|
if (maxsize <= 0) {
|
|
maxsize = MAXINT;
|
|
}
|
|
|
|
len = min(MPR_DEFAULT_ALLOC, maxsize);
|
|
fmt.buf = (uchar*) mprAllocBlock(MPR_LOC_PASS(ctx, loc), len);
|
|
fmt.endbuf = &fmt.buf[len];
|
|
fmt.growBy = MPR_DEFAULT_ALLOC * 2;
|
|
}
|
|
|
|
fmt.maxsize = maxsize;
|
|
fmt.start = fmt.buf;
|
|
fmt.end = fmt.buf;
|
|
fmt.len = 0;
|
|
*fmt.start = '\0';
|
|
|
|
state = STATE_NORMAL;
|
|
|
|
while ((c = *spec++) != '\0') {
|
|
state = getState(c, state);
|
|
|
|
switch (state) {
|
|
case STATE_NORMAL:
|
|
BPUT(ctx, loc, &fmt, c);
|
|
break;
|
|
|
|
case STATE_PERCENT:
|
|
fmt.precision = -1;
|
|
fmt.width = 0;
|
|
fmt.flags = 0;
|
|
break;
|
|
|
|
case STATE_MODIFIER:
|
|
switch (c) {
|
|
case '+':
|
|
fmt.flags |= SPRINTF_SIGN;
|
|
break;
|
|
case '-':
|
|
fmt.flags |= SPRINTF_LEFT;
|
|
break;
|
|
case '#':
|
|
fmt.flags |= SPRINTF_ALTERNATE;
|
|
break;
|
|
case '0':
|
|
fmt.flags |= SPRINTF_LEAD_ZERO;
|
|
break;
|
|
case ' ':
|
|
fmt.flags |= SPRINTF_LEAD_SPACE;
|
|
break;
|
|
case ',':
|
|
fmt.flags |= SPRINTF_COMMA;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case STATE_WIDTH:
|
|
if (c == '*') {
|
|
fmt.width = va_arg(arg, int);
|
|
if (fmt.width < 0) {
|
|
fmt.width = -fmt.width;
|
|
fmt.flags |= SPRINTF_LEFT;
|
|
}
|
|
} else {
|
|
while (isdigit((int)c)) {
|
|
fmt.width = fmt.width * 10 + (c - '0');
|
|
c = *spec++;
|
|
}
|
|
spec--;
|
|
}
|
|
break;
|
|
|
|
case STATE_DOT:
|
|
fmt.precision = 0;
|
|
fmt.flags &= ~SPRINTF_LEAD_ZERO;
|
|
break;
|
|
|
|
case STATE_PRECISION:
|
|
if (c == '*') {
|
|
fmt.precision = va_arg(arg, int);
|
|
} else {
|
|
while (isdigit((int) c)) {
|
|
fmt.precision = fmt.precision * 10 + (c - '0');
|
|
c = *spec++;
|
|
}
|
|
spec--;
|
|
}
|
|
break;
|
|
|
|
case STATE_BITS:
|
|
switch (c) {
|
|
#if BLD_FEATURE_INT64
|
|
case 'L':
|
|
fmt.flags |= SPRINTF_LONGLONG; /* 64 bit */
|
|
break;
|
|
#endif
|
|
|
|
case 'l':
|
|
fmt.flags |= SPRINTF_LONG;
|
|
break;
|
|
|
|
case 'h':
|
|
fmt.flags |= SPRINTF_SHORT;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case STATE_TYPE:
|
|
switch (c) {
|
|
#if BLD_FEATURE_FLOATING_POINT
|
|
case 'e':
|
|
case 'g':
|
|
case 'f':
|
|
fmt.radix = 10;
|
|
outFloat(MPR_LOC_PASS(ctx, loc), &fmt, c,
|
|
(double) va_arg(arg, double));
|
|
break;
|
|
#endif
|
|
case 'c':
|
|
BPUT(ctx, loc, &fmt, (char) va_arg(arg, int));
|
|
break;
|
|
|
|
case 's':
|
|
case 'S':
|
|
sValue = va_arg(arg, char*);
|
|
if (sValue == 0) {
|
|
sValue = "null";
|
|
len = strlen(sValue);
|
|
} else if (fmt.flags & SPRINTF_ALTERNATE) {
|
|
sValue++;
|
|
len = (int) *sValue;
|
|
} else if (fmt.precision >= 0) {
|
|
/*
|
|
* Can't use strlen(), the string may not have a null
|
|
*/
|
|
cp = sValue;
|
|
for (len = 0; len < fmt.precision; len++) {
|
|
if (*cp++ == '\0') {
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
len = strlen(sValue);
|
|
}
|
|
if (!(fmt.flags & SPRINTF_LEFT)) {
|
|
for (i = len; i < fmt.width; i++) {
|
|
BPUT(ctx, loc, &fmt, (char) ' ');
|
|
}
|
|
}
|
|
for (i = 0; i < len && *sValue; i++) {
|
|
BPUT(ctx, loc, &fmt, *sValue++);
|
|
}
|
|
if (fmt.flags & SPRINTF_LEFT) {
|
|
for (i = len; i < fmt.width; i++) {
|
|
BPUT(ctx, loc, &fmt, (char) ' ');
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 'i':
|
|
;
|
|
case 'd':
|
|
fmt.radix = 10;
|
|
if (fmt.flags & SPRINTF_SHORT) {
|
|
iValue = (short) va_arg(arg, int);
|
|
} else if (fmt.flags & SPRINTF_LONG) {
|
|
iValue = va_arg(arg, long);
|
|
#if BLD_FEATURE_INT64
|
|
} else if (fmt.flags & SPRINTF_LONGLONG) {
|
|
iValue = va_arg(arg, num);
|
|
#endif
|
|
} else {
|
|
iValue = va_arg(arg, int);
|
|
}
|
|
if (iValue >= 0) {
|
|
if (fmt.flags & SPRINTF_LEAD_SPACE) {
|
|
outNum(MPR_LOC_PASS(ctx, loc), &fmt, " ", iValue);
|
|
} else if (fmt.flags & SPRINTF_SIGN) {
|
|
outNum(MPR_LOC_PASS(ctx, loc), &fmt, "+", iValue);
|
|
} else {
|
|
outNum(MPR_LOC_PASS(ctx, loc), &fmt, 0, iValue);
|
|
}
|
|
} else {
|
|
outNum(MPR_LOC_PASS(ctx, loc), &fmt, "-", -iValue);
|
|
}
|
|
break;
|
|
|
|
case 'X':
|
|
fmt.flags |= SPRINTF_UPPER_CASE;
|
|
/* Fall through */
|
|
case 'o':
|
|
case 'x':
|
|
case 'u':
|
|
if (fmt.flags & SPRINTF_SHORT) {
|
|
uValue = (ushort) va_arg(arg, uint);
|
|
} else if (fmt.flags & SPRINTF_LONG) {
|
|
uValue = va_arg(arg, ulong);
|
|
#if BLD_FEATURE_INT64
|
|
} else if (fmt.flags & SPRINTF_LONGLONG) {
|
|
uValue = va_arg(arg, unum);
|
|
#endif
|
|
} else {
|
|
uValue = va_arg(arg, uint);
|
|
}
|
|
if (c == 'u') {
|
|
fmt.radix = 10;
|
|
outNum(MPR_LOC_PASS(ctx, loc), &fmt, 0, uValue);
|
|
} else if (c == 'o') {
|
|
fmt.radix = 8;
|
|
if (fmt.flags & SPRINTF_ALTERNATE && uValue != 0) {
|
|
outNum(MPR_LOC_PASS(ctx, loc), &fmt, "0", uValue);
|
|
} else {
|
|
outNum(MPR_LOC_PASS(ctx, loc), &fmt, 0, uValue);
|
|
}
|
|
} else {
|
|
fmt.radix = 16;
|
|
if (fmt.flags & SPRINTF_ALTERNATE && uValue != 0) {
|
|
if (c == 'X') {
|
|
outNum(MPR_LOC_PASS(ctx, loc), &fmt, "0X", uValue);
|
|
} else {
|
|
outNum(MPR_LOC_PASS(ctx, loc), &fmt, "0x", uValue);
|
|
}
|
|
} else {
|
|
outNum(MPR_LOC_PASS(ctx, loc), &fmt, 0, uValue);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 'n': /* Count of chars seen thus far */
|
|
if (fmt.flags & SPRINTF_SHORT) {
|
|
short *count = va_arg(arg, short*);
|
|
*count = fmt.end - fmt.start;
|
|
} else if (fmt.flags & SPRINTF_LONG) {
|
|
long *count = va_arg(arg, long*);
|
|
*count = fmt.end - fmt.start;
|
|
} else {
|
|
int *count = va_arg(arg, int *);
|
|
*count = fmt.end - fmt.start;
|
|
}
|
|
break;
|
|
|
|
case 'p': /* Pointer */
|
|
#if __WORDSIZE == 64 && BLD_FEATURE_INT64
|
|
uValue = (unum) va_arg(arg, void*);
|
|
#else
|
|
uValue = (uint) (int) va_arg(arg, void*);
|
|
#endif
|
|
fmt.radix = 16;
|
|
outNum(MPR_LOC_PASS(ctx, loc), &fmt, "0x", uValue);
|
|
break;
|
|
|
|
default:
|
|
BPUT(ctx, loc, &fmt, c);
|
|
}
|
|
}
|
|
}
|
|
BPUTNULL(ctx, loc, &fmt);
|
|
|
|
count = fmt.end - fmt.start;
|
|
if (*bufPtr == 0) {
|
|
*bufPtr = (char*) fmt.buf;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
/*
|
|
* Output a number according to the given format. If BLD_FEATURE_INT64 is
|
|
* defined, then uses 64 bits universally. Slower but smaller code.
|
|
*/
|
|
|
|
static void outNum(MPR_LOC_DEC(ctx, loc), Format *fmt, const char *prefix,
|
|
unum value)
|
|
{
|
|
char numBuf[64];
|
|
char *cp;
|
|
char *endp;
|
|
char c;
|
|
int letter, len, leadingZeros, i, fill;
|
|
|
|
endp = &numBuf[sizeof(numBuf) - 1];
|
|
*endp = '\0';
|
|
cp = endp;
|
|
|
|
/*
|
|
* Convert to ascii
|
|
*/
|
|
if (fmt->radix == 16) {
|
|
do {
|
|
letter = (int) (value % fmt->radix);
|
|
if (letter > 9) {
|
|
if (fmt->flags & SPRINTF_UPPER_CASE) {
|
|
letter = 'A' + letter - 10;
|
|
} else {
|
|
letter = 'a' + letter - 10;
|
|
}
|
|
} else {
|
|
letter += '0';
|
|
}
|
|
*--cp = letter;
|
|
value /= fmt->radix;
|
|
} while (value > 0);
|
|
|
|
} else if (fmt->flags & SPRINTF_COMMA) {
|
|
i = 1;
|
|
do {
|
|
*--cp = '0' + (int) (value % fmt->radix);
|
|
value /= fmt->radix;
|
|
if ((i++ % 3) == 0 && value > 0) {
|
|
*--cp = ',';
|
|
}
|
|
} while (value > 0);
|
|
} else {
|
|
do {
|
|
*--cp = '0' + (int) (value % fmt->radix);
|
|
value /= fmt->radix;
|
|
} while (value > 0);
|
|
}
|
|
|
|
len = endp - cp;
|
|
fill = fmt->width - len;
|
|
|
|
if (prefix != 0) {
|
|
fill -= strlen(prefix);
|
|
}
|
|
leadingZeros = (fmt->precision > len) ? fmt->precision - len : 0;
|
|
fill -= leadingZeros;
|
|
|
|
if (!(fmt->flags & SPRINTF_LEFT)) {
|
|
c = (fmt->flags & SPRINTF_LEAD_ZERO) ? '0': ' ';
|
|
for (i = 0; i < fill; i++) {
|
|
BPUT(ctx, loc, fmt, c);
|
|
}
|
|
}
|
|
if (prefix != 0) {
|
|
while (*prefix) {
|
|
BPUT(ctx, loc, fmt, *prefix++);
|
|
}
|
|
}
|
|
for (i = 0; i < leadingZeros; i++) {
|
|
BPUT(ctx, loc, fmt, '0');
|
|
}
|
|
while (*cp) {
|
|
BPUT(ctx, loc, fmt, *cp);
|
|
cp++;
|
|
}
|
|
if (fmt->flags & SPRINTF_LEFT) {
|
|
for (i = 0; i < fill; i++) {
|
|
BPUT(ctx, loc, fmt, ' ');
|
|
}
|
|
}
|
|
}
|
|
|
|
/******************************************************************************/
|
|
#if BLD_FEATURE_FLOATING_POINT
|
|
/*
|
|
* Output a floating point number
|
|
*/
|
|
|
|
static void outFloat(MPR_LOC_DEC(ctx, loc), Format *fmt, char specChar,
|
|
double value)
|
|
{
|
|
char *cp;
|
|
#if FUTURE
|
|
char numBuf[64];
|
|
char *endp;
|
|
char c;
|
|
int letter, len, leadingZeros, i, fill, width, precision;
|
|
|
|
endp = &numBuf[sizeof(numBuf) - 1];
|
|
*endp = '\0';
|
|
|
|
precision = fmt->precision;
|
|
if (precision < 0) {
|
|
precision = 6;
|
|
} else if (precision > (sizeof(numBuf) - 1)) {
|
|
precision = (sizeof(numBuf) - 1);
|
|
}
|
|
width = min(fmt->width, sizeof(numBuf) - 1);
|
|
|
|
if (__isnanl(value)) {
|
|
"nan"
|
|
} else if (__isinfl(value)) {
|
|
"infinity"
|
|
} else if (value < 0) {
|
|
prefix = "-";
|
|
} else if (fmt.flags & SPRINTF_LEAD_SPACE) {
|
|
prefix = " ";
|
|
} else if (fmt.flags & SPRINTF_SIGN) {
|
|
prefix = "+";
|
|
}
|
|
|
|
|
|
/*
|
|
* Do the exponent part
|
|
*/
|
|
cp = &numBuf[sizeof(numBuf) - precision];
|
|
for (i = 0; i < precision; i++) {
|
|
*cp++ = '0' + (int) (value % fmt->radix);
|
|
value /= fmt->radix;
|
|
}
|
|
|
|
/*
|
|
* Do the decimal part
|
|
*/
|
|
if (fmt->flags & SPRINTF_COMMA) {
|
|
i = 1;
|
|
do {
|
|
*--cp = '0' + (int) (value % fmt->radix);
|
|
value /= fmt->radix;
|
|
if ((i++ % 3) == 0 && value > 0) {
|
|
*--cp = ',';
|
|
}
|
|
} while (value >= 1.0);
|
|
|
|
} else {
|
|
do {
|
|
*--cp = '0' + (int) (value % fmt->radix);
|
|
value /= fmt->radix;
|
|
} while (value > 1.0);
|
|
}
|
|
|
|
len = endp - cp;
|
|
fill = fmt->width - len;
|
|
|
|
if (prefix != 0) {
|
|
fill -= strlen(prefix);
|
|
}
|
|
|
|
leadingZeros = (fmt->precision > len) ? fmt->precision - len : 0;
|
|
fill -= leadingZeros;
|
|
|
|
if (!(fmt->flags & SPRINTF_LEFT)) {
|
|
c = (fmt->flags & SPRINTF_LEAD_ZERO) ? '0': ' ';
|
|
for (i = 0; i < fill; i++) {
|
|
BPUT(ctx, loc, fmt, c);
|
|
}
|
|
}
|
|
if (prefix != 0) {
|
|
BPUT(ctx, loc, fmt, prefix);
|
|
}
|
|
for (i = 0; i < leadingZeros; i++) {
|
|
BPUT(ctx, loc, fmt, '0');
|
|
}
|
|
BPUT(ctx, loc, fmt, cp);
|
|
if (fmt->flags & SPRINTF_LEFT) {
|
|
for (i = 0; i < fill; i++) {
|
|
BPUT(ctx, loc, fmt, ' ');
|
|
}
|
|
}
|
|
#else
|
|
char numBuf[64];
|
|
if (specChar == 'f') {
|
|
sprintf(numBuf, "%*.*f", fmt->width, fmt->precision, value);
|
|
} else if (specChar == 'g') {
|
|
sprintf(numBuf, "%*.*g", fmt->width, fmt->precision, value);
|
|
} else if (specChar == 'e') {
|
|
sprintf(numBuf, "%*.*e", fmt->width, fmt->precision, value);
|
|
}
|
|
for (cp = numBuf; *cp; cp++) {
|
|
BPUT(ctx, loc, fmt, *cp);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#endif /* BLD_FEATURE_FLOATING_POINT */
|
|
/******************************************************************************/
|
|
/*
|
|
* Grow the buffer to fit new data. Return 1 if the buffer can grow.
|
|
* Grow using the growBy size specified when creating the buffer.
|
|
*/
|
|
|
|
static int growBuf(MPR_LOC_DEC(ctx, loc), Format *fmt)
|
|
{
|
|
uchar *newbuf;
|
|
int buflen;
|
|
|
|
buflen = fmt->endbuf - fmt->buf;
|
|
if (fmt->maxsize >= 0 && buflen >= fmt->maxsize) {
|
|
return 0;
|
|
}
|
|
if (fmt->growBy < 0) {
|
|
/*
|
|
* User supplied buffer
|
|
*/
|
|
return 0;
|
|
}
|
|
|
|
newbuf = (uchar*) mprAlloc(ctx, buflen + fmt->growBy);
|
|
if (fmt->buf) {
|
|
memcpy(newbuf, fmt->buf, buflen);
|
|
mprFree(fmt->buf);
|
|
}
|
|
|
|
buflen += fmt->growBy;
|
|
fmt->end = newbuf + (fmt->end - fmt->buf);
|
|
fmt->start = newbuf + (fmt->start - fmt->buf);
|
|
fmt->buf = newbuf;
|
|
fmt->endbuf = &fmt->buf[buflen];
|
|
|
|
/*
|
|
* Increase growBy to reduce overhead
|
|
*/
|
|
if ((buflen + (fmt->growBy * 2)) < fmt->maxsize) {
|
|
fmt->growBy *= 2;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/******************************************************************************/
|
|
|
|
/*
|
|
* Local variables:
|
|
* tab-width: 4
|
|
* c-basic-offset: 4
|
|
* End:
|
|
* vim:tw=78
|
|
* vim600: sw=4 ts=4 fdm=marker
|
|
* vim<600: sw=4 ts=4
|
|
*/
|