2000-06-01 16:22:30 +00:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/* */
|
|
|
|
|
/* xsprintf.c */
|
|
|
|
|
/* */
|
|
|
|
|
/* Replacement sprintf function */
|
|
|
|
|
/* */
|
|
|
|
|
/* */
|
|
|
|
|
/* */
|
2004-12-15 21:23:22 +00:00
|
|
|
|
/* (C) 2000-2004 Ullrich von Bassewitz */
|
|
|
|
|
/* R<>merstrasse 52 */
|
|
|
|
|
/* D-70794 Filderstadt */
|
|
|
|
|
/* EMail: uz@cc65.org */
|
2000-06-01 16:22:30 +00:00
|
|
|
|
/* */
|
|
|
|
|
/* */
|
|
|
|
|
/* This software is provided 'as-is', without any expressed or implied */
|
|
|
|
|
/* warranty. In no event will the authors be held liable for any damages */
|
|
|
|
|
/* arising from the use of this software. */
|
|
|
|
|
/* */
|
|
|
|
|
/* Permission is granted to anyone to use this software for any purpose, */
|
|
|
|
|
/* including commercial applications, and to alter it and redistribute it */
|
|
|
|
|
/* freely, subject to the following restrictions: */
|
|
|
|
|
/* */
|
|
|
|
|
/* 1. The origin of this software must not be misrepresented; you must not */
|
|
|
|
|
/* claim that you wrote the original software. If you use this software */
|
|
|
|
|
/* in a product, an acknowledgment in the product documentation would be */
|
|
|
|
|
/* appreciated but is not required. */
|
|
|
|
|
/* 2. Altered source versions must be plainly marked as such, and must not */
|
|
|
|
|
/* be misrepresented as being the original software. */
|
|
|
|
|
/* 3. This notice may not be removed or altered from any source */
|
|
|
|
|
/* distribution. */
|
|
|
|
|
/* */
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include <stdio.h>
|
2004-12-15 21:23:22 +00:00
|
|
|
|
#include <stddef.h>
|
|
|
|
|
#include <string.h>
|
2004-12-16 22:38:07 +00:00
|
|
|
|
#include <limits.h>
|
2000-06-01 16:22:30 +00:00
|
|
|
|
|
2004-12-16 22:38:07 +00:00
|
|
|
|
/* common */
|
2004-12-15 21:23:22 +00:00
|
|
|
|
#include "chartype.h"
|
2002-03-16 20:45:41 +00:00
|
|
|
|
#include "check.h"
|
2004-12-16 22:38:07 +00:00
|
|
|
|
#include "inttypes.h"
|
2004-12-18 14:28:42 +00:00
|
|
|
|
#include "va_copy.h"
|
2000-06-01 16:22:30 +00:00
|
|
|
|
#include "xsprintf.h"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2004-12-15 21:23:22 +00:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/* vsnprintf */
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* The following is a very basic vsnprintf like function called xvsnprintf. It
|
|
|
|
|
* features only the basic format specifiers (especially the floating point
|
|
|
|
|
* stuff is missing), but may be extended if required. Reason for supplying
|
|
|
|
|
* my own implementation is that vsnprintf is standard but not implemented by
|
2004-12-16 22:38:07 +00:00
|
|
|
|
* older compilers, and some that implement it, don't adhere to the standard
|
|
|
|
|
* (for example Microsoft with its _vsnprintf).
|
2004-12-15 21:23:22 +00:00
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
|
|
|
|
|
|
/* Variable argument list pointer */
|
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
|
|
/* Output buffer */
|
|
|
|
|
char* Buf;
|
|
|
|
|
size_t BufSize;
|
|
|
|
|
size_t BufFill;
|
|
|
|
|
|
|
|
|
|
/* Argument string buffer and string buffer pointer. The string buffer
|
|
|
|
|
* must be big enough to hold a converted integer of the largest type
|
|
|
|
|
* including an optional sign and terminating zero.
|
|
|
|
|
*/
|
|
|
|
|
char ArgBuf[256];
|
|
|
|
|
int ArgLen;
|
|
|
|
|
|
|
|
|
|
/* Flags */
|
|
|
|
|
enum {
|
|
|
|
|
fNone = 0x0000,
|
|
|
|
|
fMinus = 0x0001,
|
|
|
|
|
fPlus = 0x0002,
|
|
|
|
|
fSpace = 0x0004,
|
|
|
|
|
fHash = 0x0008,
|
|
|
|
|
fZero = 0x0010,
|
|
|
|
|
fWidth = 0x0020,
|
|
|
|
|
fPrec = 0x0040,
|
|
|
|
|
fUnsigned = 0x0080,
|
|
|
|
|
fUpcase = 0x0100
|
|
|
|
|
} Flags;
|
|
|
|
|
|
|
|
|
|
/* Conversion base and table */
|
|
|
|
|
unsigned Base;
|
|
|
|
|
const char* CharTable;
|
|
|
|
|
|
|
|
|
|
/* Field width */
|
|
|
|
|
int Width;
|
|
|
|
|
|
|
|
|
|
/* Precision */
|
|
|
|
|
int Prec;
|
|
|
|
|
|
|
|
|
|
/* Length modifier */
|
|
|
|
|
enum {
|
|
|
|
|
lmChar,
|
|
|
|
|
lmShort,
|
|
|
|
|
lmInt,
|
|
|
|
|
lmLong,
|
2004-12-16 22:38:07 +00:00
|
|
|
|
lmIntMax,
|
2004-12-15 21:23:22 +00:00
|
|
|
|
lmSizeT,
|
|
|
|
|
lmPtrDiffT,
|
|
|
|
|
lmLongDouble,
|
|
|
|
|
|
|
|
|
|
/* Unsupported modifiers */
|
|
|
|
|
lmLongLong = lmLong,
|
|
|
|
|
|
|
|
|
|
/* Default length is integer */
|
|
|
|
|
lmDefault = lmInt
|
|
|
|
|
} LengthMod;
|
|
|
|
|
|
|
|
|
|
} PrintfCtrl;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void AddChar (PrintfCtrl* P, char C)
|
|
|
|
|
/* Store one character in the output buffer if there's enough room. */
|
|
|
|
|
{
|
|
|
|
|
if (++P->BufFill <= P->BufSize) {
|
|
|
|
|
*P->Buf++ = C;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void AddPadding (PrintfCtrl* P, char C, unsigned Count)
|
|
|
|
|
/* Add some amount of padding */
|
|
|
|
|
{
|
|
|
|
|
while (Count--) {
|
|
|
|
|
AddChar (P, C);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2004-12-16 22:38:07 +00:00
|
|
|
|
static intmax_t NextIVal (PrintfCtrl*P)
|
2004-12-15 21:23:22 +00:00
|
|
|
|
/* Read the next integer value from the variable argument list */
|
|
|
|
|
{
|
|
|
|
|
switch (P->LengthMod) {
|
|
|
|
|
case lmChar: return (char) va_arg (P->ap, int);
|
|
|
|
|
case lmShort: return (short) va_arg (P->ap, int);
|
|
|
|
|
case lmInt: return (int) va_arg (P->ap, int);
|
|
|
|
|
case lmLong: return (long) va_arg (P->ap, long);
|
2004-12-16 22:38:07 +00:00
|
|
|
|
case lmIntMax: return va_arg (P->ap, intmax_t);
|
|
|
|
|
case lmSizeT: return (uintmax_t) va_arg (P->ap, size_t);
|
2004-12-15 21:23:22 +00:00
|
|
|
|
case lmPtrDiffT: return (long) va_arg (P->ap, ptrdiff_t);
|
2004-12-18 14:28:42 +00:00
|
|
|
|
default:
|
|
|
|
|
FAIL ("Invalid type size in NextIVal");
|
|
|
|
|
return 0;
|
2004-12-15 21:23:22 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2004-12-16 22:38:07 +00:00
|
|
|
|
static uintmax_t NextUVal (PrintfCtrl*P)
|
2004-12-15 21:23:22 +00:00
|
|
|
|
/* Read the next unsigned integer value from the variable argument list */
|
|
|
|
|
{
|
|
|
|
|
switch (P->LengthMod) {
|
|
|
|
|
case lmChar: return (unsigned char) va_arg (P->ap, unsigned);
|
|
|
|
|
case lmShort: return (unsigned short) va_arg (P->ap, unsigned);
|
|
|
|
|
case lmInt: return (unsigned int) va_arg (P->ap, unsigned int);
|
2004-12-16 22:38:07 +00:00
|
|
|
|
case lmLong: return (unsigned long) va_arg (P->ap, unsigned long);
|
|
|
|
|
case lmIntMax: return va_arg (P->ap, uintmax_t);
|
|
|
|
|
case lmSizeT: return va_arg (P->ap, size_t);
|
|
|
|
|
case lmPtrDiffT: return (intmax_t) va_arg (P->ap, ptrdiff_t);
|
2004-12-18 14:28:42 +00:00
|
|
|
|
default:
|
|
|
|
|
FAIL ("Invalid type size in NextUVal");
|
|
|
|
|
return 0;
|
2004-12-15 21:23:22 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2004-12-16 22:38:07 +00:00
|
|
|
|
static void ToStr (PrintfCtrl* P, uintmax_t Val)
|
2004-12-15 21:23:22 +00:00
|
|
|
|
/* Convert the given value to a (reversed) string */
|
|
|
|
|
{
|
|
|
|
|
char* S = P->ArgBuf;
|
|
|
|
|
while (Val) {
|
|
|
|
|
*S++ = P->CharTable[Val % P->Base];
|
|
|
|
|
Val /= P->Base;
|
|
|
|
|
}
|
|
|
|
|
P->ArgLen = S - P->ArgBuf;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2004-12-16 22:38:07 +00:00
|
|
|
|
static void FormatInt (PrintfCtrl* P, uintmax_t Val)
|
2004-12-15 21:23:22 +00:00
|
|
|
|
/* Convert the integer value */
|
|
|
|
|
{
|
|
|
|
|
char Lead[5];
|
|
|
|
|
unsigned LeadCount = 0;
|
|
|
|
|
unsigned PrecPadding;
|
|
|
|
|
unsigned WidthPadding;
|
|
|
|
|
unsigned I;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Determine the translation table */
|
|
|
|
|
P->CharTable = (P->Flags & fUpcase)? "0123456789ABCDEF" : "0123456789abcedf";
|
|
|
|
|
|
|
|
|
|
/* Check if the value is negative */
|
2004-12-16 22:38:07 +00:00
|
|
|
|
if ((P->Flags & fUnsigned) == 0 && ((intmax_t) Val) < 0) {
|
|
|
|
|
Val = -((intmax_t) Val);
|
2004-12-15 21:23:22 +00:00
|
|
|
|
Lead[LeadCount++] = '-';
|
|
|
|
|
} else if ((P->Flags & fPlus) != 0) {
|
|
|
|
|
Lead[LeadCount++] = '+';
|
|
|
|
|
} else if ((P->Flags & fSpace) != 0) {
|
|
|
|
|
Lead[LeadCount++] = ' ';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Convert the value into a (reversed string). */
|
|
|
|
|
ToStr (P, Val);
|
|
|
|
|
|
2004-12-16 20:26:46 +00:00
|
|
|
|
/* The default precision for all integer conversions is one. This means
|
2004-12-16 22:38:07 +00:00
|
|
|
|
* that the fPrec flag is always set and does not need to be checked
|
2004-12-16 20:26:46 +00:00
|
|
|
|
* later on.
|
|
|
|
|
*/
|
|
|
|
|
if ((P->Flags & fPrec) == 0) {
|
|
|
|
|
P->Flags |= fPrec;
|
|
|
|
|
P->Prec = 1;
|
|
|
|
|
}
|
|
|
|
|
|
2004-12-15 21:23:22 +00:00
|
|
|
|
/* Determine the leaders for alternative forms */
|
|
|
|
|
if ((P->Flags & fHash) != 0) {
|
|
|
|
|
if (P->Base == 16) {
|
|
|
|
|
/* Start with 0x */
|
|
|
|
|
Lead[LeadCount++] = '0';
|
|
|
|
|
Lead[LeadCount++] = (P->Flags & fUpcase)? 'X' : 'x';
|
|
|
|
|
} else if (P->Base == 8) {
|
|
|
|
|
/* Alternative form for 'o': always add a leading zero. */
|
2004-12-16 20:26:46 +00:00
|
|
|
|
if (P->Prec <= P->ArgLen) {
|
2004-12-15 21:23:22 +00:00
|
|
|
|
Lead[LeadCount++] = '0';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Determine the amount of precision padding needed */
|
2004-12-16 20:26:46 +00:00
|
|
|
|
if (P->ArgLen < P->Prec) {
|
2004-12-15 21:23:22 +00:00
|
|
|
|
PrecPadding = P->Prec - P->ArgLen;
|
|
|
|
|
} else {
|
|
|
|
|
PrecPadding = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Determine the width padding needed */
|
|
|
|
|
if ((P->Flags & fWidth) != 0) {
|
|
|
|
|
int CurWidth = LeadCount + PrecPadding + P->ArgLen;
|
|
|
|
|
if (CurWidth < P->Width) {
|
|
|
|
|
WidthPadding = P->Width - CurWidth;
|
|
|
|
|
} else {
|
|
|
|
|
WidthPadding = 0;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
WidthPadding = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Output left space padding if any */
|
|
|
|
|
if ((P->Flags & (fMinus | fZero)) == 0 && WidthPadding > 0) {
|
|
|
|
|
AddPadding (P, ' ', WidthPadding);
|
|
|
|
|
WidthPadding = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Leader */
|
|
|
|
|
for (I = 0; I < LeadCount; ++I) {
|
|
|
|
|
AddChar (P, Lead[I]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Left zero padding if any */
|
|
|
|
|
if ((P->Flags & fZero) != 0 && WidthPadding > 0) {
|
|
|
|
|
AddPadding (P, '0', WidthPadding);
|
|
|
|
|
WidthPadding = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Precision padding */
|
|
|
|
|
if (PrecPadding > 0) {
|
|
|
|
|
AddPadding (P, '0', PrecPadding);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* The number itself. Beware: It's reversed! */
|
|
|
|
|
while (P->ArgLen > 0) {
|
|
|
|
|
AddChar (P, P->ArgBuf[--P->ArgLen]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Right width padding if any */
|
|
|
|
|
if (WidthPadding > 0) {
|
|
|
|
|
AddPadding (P, ' ', WidthPadding);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void FormatStr (PrintfCtrl* P, const char* Val)
|
|
|
|
|
/* Convert the string */
|
|
|
|
|
{
|
|
|
|
|
unsigned WidthPadding;
|
|
|
|
|
|
|
|
|
|
/* Get the string length limited to the precision. Beware: We cannot use
|
|
|
|
|
* strlen here, because if a precision is given, the string may not be
|
|
|
|
|
* zero terminated.
|
|
|
|
|
*/
|
|
|
|
|
int Len;
|
|
|
|
|
if ((P->Flags & fPrec) != 0) {
|
|
|
|
|
const char* S = memchr (Val, '\0', P->Prec);
|
|
|
|
|
if (S == 0) {
|
|
|
|
|
/* Not zero terminated */
|
|
|
|
|
Len = P->Prec;
|
|
|
|
|
} else {
|
|
|
|
|
/* Terminating zero found */
|
|
|
|
|
Len = S - Val;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
Len = strlen (Val);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Determine the width padding needed */
|
|
|
|
|
if ((P->Flags & fWidth) != 0 && P->Width > Len) {
|
|
|
|
|
WidthPadding = P->Width - Len;
|
|
|
|
|
} else {
|
|
|
|
|
WidthPadding = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Output left padding */
|
|
|
|
|
if ((P->Flags & fMinus) != 0 && WidthPadding > 0) {
|
|
|
|
|
AddPadding (P, ' ', WidthPadding);
|
|
|
|
|
WidthPadding = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Output the string */
|
|
|
|
|
while (Len--) {
|
|
|
|
|
AddChar (P, *Val++);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Output right padding if any */
|
|
|
|
|
if (WidthPadding > 0) {
|
|
|
|
|
AddPadding (P, ' ', WidthPadding);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2004-12-16 22:38:07 +00:00
|
|
|
|
static void StoreOffset (PrintfCtrl* P)
|
|
|
|
|
/* Store the current output offset (%n format spec) */
|
|
|
|
|
{
|
|
|
|
|
switch (P->LengthMod) {
|
|
|
|
|
case lmChar: *va_arg (P->ap, int*) = P->BufFill;
|
|
|
|
|
case lmShort: *va_arg (P->ap, int*) = P->BufFill;
|
|
|
|
|
case lmInt: *va_arg (P->ap, int*) = P->BufFill;
|
|
|
|
|
case lmLong: *va_arg (P->ap, long*) = P->BufFill;
|
|
|
|
|
case lmIntMax: *va_arg (P->ap, intmax_t*) = P->BufFill;
|
|
|
|
|
case lmSizeT: *va_arg (P->ap, size_t*) = P->BufFill;
|
|
|
|
|
case lmPtrDiffT: *va_arg (P->ap, ptrdiff_t*) = P->BufFill;
|
|
|
|
|
default: FAIL ("Invalid size modifier for %n format spec in xvsnprintf");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2004-12-15 21:23:22 +00:00
|
|
|
|
int xvsnprintf (char* Buf, size_t Size, const char* Format, va_list ap)
|
|
|
|
|
/* A basic vsnprintf implementation. Does currently only support integer
|
|
|
|
|
* formats.
|
|
|
|
|
*/
|
|
|
|
|
{
|
|
|
|
|
PrintfCtrl P;
|
|
|
|
|
int Done;
|
|
|
|
|
char F;
|
|
|
|
|
char SBuf[2];
|
|
|
|
|
const char* SPtr;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Initialize the control structure */
|
2004-12-18 14:28:42 +00:00
|
|
|
|
va_copy (P.ap, ap);
|
2004-12-15 21:23:22 +00:00
|
|
|
|
P.Buf = Buf;
|
|
|
|
|
P.BufSize = Size;
|
|
|
|
|
P.BufFill = 0;
|
|
|
|
|
|
|
|
|
|
/* Parse the format string */
|
|
|
|
|
while ((F = *Format++) != '\0') {
|
|
|
|
|
|
|
|
|
|
if (F != '%') {
|
|
|
|
|
/* Not a format specifier, just copy */
|
|
|
|
|
AddChar (&P, F);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Check for %% */
|
|
|
|
|
if (*Format == '%') {
|
|
|
|
|
++Format;
|
|
|
|
|
AddChar (&P, '%');
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* It's a format specifier. Check for flags. */
|
|
|
|
|
F = *Format++;
|
|
|
|
|
P.Flags = fNone;
|
|
|
|
|
Done = 0;
|
|
|
|
|
while (F != '\0' && !Done) {
|
|
|
|
|
switch (F) {
|
|
|
|
|
case '-': P.Flags |= fMinus; F = *Format++; break;
|
|
|
|
|
case '+': P.Flags |= fPlus; F = *Format++; break;
|
|
|
|
|
case ' ': P.Flags |= fSpace; F = *Format++; break;
|
|
|
|
|
case '#': P.Flags |= fHash; F = *Format++; break;
|
|
|
|
|
case '0': P.Flags |= fZero; F = *Format++; break;
|
|
|
|
|
default: Done = 1; break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/* Optional field width */
|
|
|
|
|
if (F == '*') {
|
2004-12-16 20:07:49 +00:00
|
|
|
|
P.Width = va_arg (P.ap, int);
|
2004-12-15 21:23:22 +00:00
|
|
|
|
/* A negative field width argument is taken as a - flag followed
|
|
|
|
|
* by a positive field width.
|
|
|
|
|
*/
|
|
|
|
|
if (P.Width < 0) {
|
|
|
|
|
P.Flags |= fMinus;
|
|
|
|
|
P.Width = -P.Width;
|
|
|
|
|
}
|
|
|
|
|
F = *Format++;
|
|
|
|
|
P.Flags |= fWidth;
|
|
|
|
|
} else if (IsDigit (F)) {
|
|
|
|
|
P.Width = F - '0';
|
|
|
|
|
while (1) {
|
|
|
|
|
F = *Format++;
|
|
|
|
|
if (!IsDigit (F)) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
P.Width = P.Width * 10 + (F - '0');
|
|
|
|
|
}
|
|
|
|
|
P.Flags |= fWidth;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Optional precision */
|
|
|
|
|
if (F == '.') {
|
|
|
|
|
F = *Format++;
|
|
|
|
|
P.Flags |= fPrec;
|
|
|
|
|
if (F == '*') {
|
2004-12-16 20:07:49 +00:00
|
|
|
|
P.Prec = va_arg (P.ap, int);
|
2004-12-15 21:23:22 +00:00
|
|
|
|
/* A negative precision argument is taken as if the precision
|
|
|
|
|
* were omitted.
|
|
|
|
|
*/
|
|
|
|
|
if (P.Prec < 0) {
|
|
|
|
|
P.Flags &= ~fPrec;
|
|
|
|
|
}
|
2004-12-16 20:07:49 +00:00
|
|
|
|
F = *Format++; /* Skip the '*' */
|
2004-12-15 21:23:22 +00:00
|
|
|
|
} else if (IsDigit (F)) {
|
|
|
|
|
P.Prec = F - '0';
|
|
|
|
|
while (1) {
|
|
|
|
|
F = *Format++;
|
|
|
|
|
if (!IsDigit (F)) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
P.Prec = P.Prec * 10 + (F - '0');
|
|
|
|
|
}
|
|
|
|
|
} else if (F == '-') {
|
|
|
|
|
/* A negative precision argument is taken as if the precision
|
|
|
|
|
* were omitted.
|
|
|
|
|
*/
|
2004-12-16 20:07:49 +00:00
|
|
|
|
F = *Format++; /* Skip the minus */
|
2004-12-15 21:23:22 +00:00
|
|
|
|
while (IsDigit (F = *Format++)) ;
|
|
|
|
|
P.Flags &= ~fPrec;
|
2004-12-16 20:07:49 +00:00
|
|
|
|
} else {
|
|
|
|
|
P.Prec = 0;
|
2004-12-15 21:23:22 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Optional length modifier */
|
|
|
|
|
P.LengthMod = lmDefault;
|
|
|
|
|
switch (F) {
|
|
|
|
|
|
|
|
|
|
case 'h':
|
|
|
|
|
F = *Format++;
|
|
|
|
|
if (F == 'h') {
|
|
|
|
|
F = *Format++;
|
|
|
|
|
P.LengthMod = lmChar;
|
|
|
|
|
} else {
|
|
|
|
|
P.LengthMod = lmShort;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'l':
|
|
|
|
|
F = *Format++;
|
|
|
|
|
if (F == 'l') {
|
|
|
|
|
F = *Format++;
|
|
|
|
|
P.LengthMod = lmLongLong;
|
|
|
|
|
} else {
|
|
|
|
|
P.LengthMod = lmLong;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'j':
|
|
|
|
|
P.LengthMod = lmIntMax;
|
|
|
|
|
F = *Format++;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'z':
|
|
|
|
|
P.LengthMod = lmSizeT;
|
|
|
|
|
F = *Format++;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 't':
|
|
|
|
|
P.LengthMod = lmPtrDiffT;
|
|
|
|
|
F = *Format++;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'L':
|
|
|
|
|
P.LengthMod = lmLongDouble;
|
|
|
|
|
F = *Format++;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If the space and + flags both appear, the space flag is ignored */
|
|
|
|
|
if ((P.Flags & (fSpace | fPlus)) == (fSpace | fPlus)) {
|
|
|
|
|
P.Flags &= ~fSpace;
|
|
|
|
|
}
|
|
|
|
|
/* If the 0 and - flags both appear, the 0 flag is ignored */
|
|
|
|
|
if ((P.Flags & (fZero | fMinus)) == (fZero | fMinus)) {
|
|
|
|
|
P.Flags &= ~fZero;
|
|
|
|
|
}
|
|
|
|
|
/* If a precision is specified, the 0 flag is ignored */
|
|
|
|
|
if (P.Flags & fPrec) {
|
2004-12-16 20:07:49 +00:00
|
|
|
|
P.Flags &= ~fZero;
|
2004-12-15 21:23:22 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Conversion specifier */
|
|
|
|
|
switch (F) {
|
|
|
|
|
|
|
|
|
|
case 'd':
|
|
|
|
|
case 'i':
|
|
|
|
|
P.Base = 10;
|
|
|
|
|
FormatInt (&P, NextIVal (&P));
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'o':
|
|
|
|
|
P.Flags |= fUnsigned;
|
|
|
|
|
P.Base = 8;
|
|
|
|
|
FormatInt (&P, NextUVal (&P));
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'u':
|
|
|
|
|
P.Flags |= fUnsigned;
|
|
|
|
|
P.Base = 10;
|
|
|
|
|
FormatInt (&P, NextUVal (&P));
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'X':
|
|
|
|
|
P.Flags |= (fUnsigned | fUpcase);
|
|
|
|
|
/* FALLTHROUGH */
|
|
|
|
|
case 'x':
|
|
|
|
|
P.Base = 16;
|
|
|
|
|
FormatInt (&P, NextUVal (&P));
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'c':
|
2004-12-16 20:07:49 +00:00
|
|
|
|
SBuf[0] = (char) va_arg (P.ap, int);
|
2004-12-15 21:23:22 +00:00
|
|
|
|
SBuf[1] = '\0';
|
|
|
|
|
FormatStr (&P, SBuf);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 's':
|
2004-12-16 20:07:49 +00:00
|
|
|
|
SPtr = va_arg (P.ap, const char*);
|
2004-12-15 21:23:22 +00:00
|
|
|
|
CHECK (SPtr != 0);
|
|
|
|
|
FormatStr (&P, SPtr);
|
|
|
|
|
break;
|
|
|
|
|
|
2004-12-16 22:38:07 +00:00
|
|
|
|
case 'p':
|
|
|
|
|
/* Use hex format for pointers */
|
|
|
|
|
P.Flags |= (fUnsigned | fPrec);
|
|
|
|
|
P.Prec = ((sizeof (void*) * CHAR_BIT) + 3) / 4;
|
|
|
|
|
P.Base = 16;
|
|
|
|
|
FormatInt (&P, (uintptr_t) va_arg (P.ap, void*));
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case 'n':
|
|
|
|
|
StoreOffset (&P);
|
|
|
|
|
break;
|
|
|
|
|
|
2004-12-15 21:23:22 +00:00
|
|
|
|
default:
|
|
|
|
|
/* Invalid format spec */
|
|
|
|
|
FAIL ("Invalid format specifier in xvsnprintf");
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2004-12-18 14:28:42 +00:00
|
|
|
|
/* We don't need P.ap any longer */
|
|
|
|
|
va_end (P.ap);
|
|
|
|
|
|
2004-12-15 21:23:22 +00:00
|
|
|
|
/* Terminate the output string and return the number of chars that had
|
|
|
|
|
* been written if the buffer was large enough.
|
|
|
|
|
* Beware: The terminating zero is not counted for the function result!
|
|
|
|
|
*/
|
|
|
|
|
AddChar (&P, '\0');
|
|
|
|
|
return P.BufFill - 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int xsnprintf (char* Buf, size_t Size, const char* Format, ...)
|
|
|
|
|
/* A basic snprintf implementation. Does currently only support integer
|
|
|
|
|
* formats.
|
|
|
|
|
*/
|
|
|
|
|
{
|
|
|
|
|
int Res;
|
|
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
|
|
va_start (ap, Format);
|
|
|
|
|
Res = xvsnprintf (Buf, Size, Format, ap);
|
|
|
|
|
va_end (ap);
|
|
|
|
|
|
|
|
|
|
return Res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2000-06-01 16:22:30 +00:00
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
/* Code */
|
|
|
|
|
/*****************************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int xsprintf (char* Buf, size_t BufSize, const char* Format, ...)
|
|
|
|
|
/* Replacement function for sprintf */
|
|
|
|
|
{
|
|
|
|
|
int Res;
|
|
|
|
|
va_list ap;
|
|
|
|
|
|
2004-12-15 21:23:22 +00:00
|
|
|
|
va_start (ap, Format);
|
2000-06-01 16:22:30 +00:00
|
|
|
|
Res = xvsprintf (Buf, BufSize, Format, ap);
|
|
|
|
|
va_end (ap);
|
|
|
|
|
|
|
|
|
|
return Res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int xvsprintf (char* Buf, size_t BufSize, const char* Format, va_list ap)
|
|
|
|
|
/* Replacement function for sprintf */
|
|
|
|
|
{
|
2004-12-15 21:23:22 +00:00
|
|
|
|
int Res = xvsnprintf (Buf, BufSize, Format, ap);
|
|
|
|
|
CHECK (Res >= 0 && (unsigned) (Res+1) < BufSize);
|
2000-06-08 18:18:20 +00:00
|
|
|
|
return Res;
|
2000-06-01 16:22:30 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|