1
0
mirror of https://github.com/cc65/cc65.git synced 2024-09-28 10:55:43 +00:00

Added a basic vsnprintf implementation to work around problems with compilers

that don't have it.
Added SB_VPrintf and SB_Printf as safe replacement for sprintf function
with the output going into a string buffer.


git-svn-id: svn://svn.cc65.org/cc65/trunk@3327 b7a2c559-68d2-44c3-8de9-860c34a00d81
This commit is contained in:
cuz 2004-12-15 21:23:22 +00:00
parent 569f57bc1b
commit e43766ea93
4 changed files with 626 additions and 26 deletions

View File

@ -36,8 +36,9 @@
#include <string.h>
/* common */
#include "xmalloc.h"
#include "strbuf.h"
#include "xmalloc.h"
#include "xsprintf.h"
@ -102,7 +103,7 @@ void FreeStrBuf (StrBuf* B)
static void SB_Realloc (StrBuf* B, unsigned NewSize)
void SB_Realloc (StrBuf* B, unsigned NewSize)
/* Reallocate the string buffer space, make sure at least NewSize bytes are
* available.
*/
@ -304,7 +305,7 @@ void SB_Move (StrBuf* Target, StrBuf* Source)
int SB_Compare (const StrBuf* S1, const StrBuf* S2)
/* Do a lexical compare of S1 and S2. See strcmp for result codes. */
{
{
int Result;
if (S1->Len < S2->Len) {
Result = memcmp (S1->Buf, S2->Buf, S1->Len);
@ -326,3 +327,55 @@ int SB_Compare (const StrBuf* S1, const StrBuf* S2)
void SB_VPrintf (StrBuf* S, const char* Format, va_list ap)
/* printf function with S as target. The function is safe, which means that
* the current contents of S are discarded, and are allocated again with
* a matching size for the output. The function will call FAIL when problems
* are detected (anything that let xsnprintf return -1).
*/
{
va_list tmp;
int SizeNeeded;
/* Since we must determine the space needed anyway, we will try with
* the currently allocated memory. If the call succeeds, we've saved
* an allocation. If not, we have to reallocate and try again.
*/
va_copy (tmp, ap);
SizeNeeded = xvsnprintf (S->Buf, S->Allocated, Format, ap);
va_end (tmp);
/* Check the result, the xvsnprintf function should not fail */
CHECK (SizeNeeded >= 0);
/* Check if we must reallocate */
if ((unsigned) SizeNeeded >= S->Allocated) {
/* Must retry. Don't use Realloc to avoid copying */
xfree (S->Buf);
S->Allocated = SizeNeeded + 1; /* Account for '\0' */
S->Buf = xmalloc (S->Allocated);
(void) xvsnprintf (S->Buf, S->Allocated, Format, ap);
}
/* Update string buffer variables */
S->Len = SizeNeeded;
S->Index = 0;
}
void SB_Printf (StrBuf* S, const char* Format, ...)
/* vprintf function with S as target. The function is safe, which means that
* the current contents of S are discarded, and are allocated again with
* a matching size for the output. The function will call FAIL when problems
* are detected (anything that let xsnprintf return -1).
*/
{
va_list ap;
va_start (ap, Format);
SB_VPrintf (S, Format, ap);
va_end (ap);
}

View File

@ -38,6 +38,7 @@
#include <stdarg.h>
#include <string.h>
/* common */
@ -90,6 +91,11 @@ StrBuf* NewStrBuf (void);
void FreeStrBuf (StrBuf* B);
/* Free a string buffer */
void SB_Realloc (StrBuf* B, unsigned NewSize);
/* Reallocate the string buffer space, make sure at least NewSize bytes are
* available.
*/
#if defined(HAVE_INLINE)
INLINE unsigned SB_GetLen (const StrBuf* B)
/* Return the length of the buffer contents */
@ -141,7 +147,7 @@ INLINE char SB_At (const StrBuf* B, unsigned Index)
char SB_At (const StrBuf* B, unsigned Index);
/* Get a character from the buffer */
#endif
#if defined(HAVE_INLINE)
INLINE char SB_AtUnchecked (const StrBuf* B, unsigned Index)
/* Get a character from the buffer */
@ -346,6 +352,20 @@ void SB_Move (StrBuf* Target, StrBuf* Source);
int SB_Compare (const StrBuf* S1, const StrBuf* S2);
/* Do a lexical compare of S1 and S2. See strcmp for result codes. */
void SB_VPrintf (StrBuf* S, const char* Format, va_list ap);
/* printf function with S as target. The function is safe, which means that
* the current contents of S are discarded, and are allocated again with
* a matching size for the output. The function will call FAIL when problems
* are detected (anything that let xsnprintf return -1).
*/
void SB_Printf (StrBuf* S, const char* Format, ...);
/* vprintf function with S as target. The function is safe, which means that
* the current contents of S are discarded, and are allocated again with
* a matching size for the output. The function will call FAIL when problems
* are detected (anything that let xsnprintf return -1).
*/
/* End of strbuf.h */

View File

@ -6,10 +6,10 @@
/* */
/* */
/* */
/* (C) 2000-2002 Ullrich von Bassewitz */
/* Wacholderweg 14 */
/* D-70597 Stuttgart */
/* EMail: uz@musoftware.de */
/* (C) 2000-2004 Ullrich von Bassewitz */
/* Römerstrasse 52 */
/* D-70794 Filderstadt */
/* EMail: uz@cc65.org */
/* */
/* */
/* This software is provided 'as-is', without any expressed or implied */
@ -34,12 +34,536 @@
#include <stdio.h>
#include <stddef.h>
#include <string.h>
#include "chartype.h"
#include "check.h"
#include "xsprintf.h"
/*****************************************************************************/
/* 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
* older compilers, and some that implement it in some way or the other, don't
* adhere to the standard (for example Microsoft with its _vsnprintf).
*/
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,
lmSizeT,
lmPtrDiffT,
lmLongDouble,
/* Unsupported modifiers */
lmLongLong = lmLong,
lmIntMax = 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);
}
}
static long NextIVal (PrintfCtrl*P)
/* 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);
case lmSizeT: return (unsigned long) va_arg (P->ap, size_t);
case lmPtrDiffT: return (long) va_arg (P->ap, ptrdiff_t);
default: FAIL ("Invalid type size in NextIVal");
}
}
static unsigned long NextUVal (PrintfCtrl*P)
/* 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);
case lmLong: return va_arg (P->ap, unsigned long);
case lmSizeT: return (unsigned long) va_arg (P->ap, size_t);
case lmPtrDiffT: return (long) va_arg (P->ap, ptrdiff_t);
default: FAIL ("Invalid type size in NextUVal");
}
}
static void ToStr (PrintfCtrl* P, unsigned long Val)
/* 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;
}
static void FormatInt (PrintfCtrl* P, unsigned long Val)
/* 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 */
if ((P->Flags & fUnsigned) == 0 && ((long) Val) < 0) {
Val = -((long) Val);
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);
/* 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. */
if ((P->Flags & fPrec) == 0 || P->Prec <= P->ArgLen) {
Lead[LeadCount++] = '0';
}
}
}
/* Determine the amount of precision padding needed */
if ((P->Flags & fPrec) != 0 && P->ArgLen < P->Prec) {
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);
}
}
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 */
P.ap = ap;
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 == '*') {
P.Width = va_arg (ap, int);
/* 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 == '*') {
P.Prec = va_arg (ap, int);
/* A negative precision argument is taken as if the precision
* were omitted.
*/
if (P.Prec < 0) {
P.Flags &= ~fPrec;
}
} 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.
*/
while (IsDigit (F = *Format++)) ;
P.Flags &= ~fPrec;
}
}
/* 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) {
P.Flags &= fZero;
}
/* 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':
SBuf[0] = (char) va_arg (ap, int);
SBuf[1] = '\0';
FormatStr (&P, SBuf);
break;
case 's':
SPtr = va_arg (ap, const char*);
CHECK (SPtr != 0);
FormatStr (&P, SPtr);
break;
default:
/* Invalid format spec */
FAIL ("Invalid format specifier in xvsnprintf");
}
}
/* 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;
}
/*****************************************************************************/
/* Code */
/*****************************************************************************/
@ -52,7 +576,7 @@ int xsprintf (char* Buf, size_t BufSize, const char* Format, ...)
int Res;
va_list ap;
va_start (ap, Format);
va_start (ap, Format);
Res = xvsprintf (Buf, BufSize, Format, ap);
va_end (ap);
@ -64,17 +588,8 @@ int xsprintf (char* Buf, size_t BufSize, const char* Format, ...)
int xvsprintf (char* Buf, size_t BufSize, const char* Format, va_list ap)
/* Replacement function for sprintf */
{
#if defined(__WATCOMC__)
int Res = _vbprintf (Buf, BufSize, Format, ap);
#elif defined(__GNUC__) && !defined(__GO32__) && !defined(__MINGW32__)
int Res = vsnprintf (Buf, BufSize, Format, ap);
#elif defined(_MSC_VER)
int Res = _vsnprintf (Buf, BufSize, Format, ap);
#else
/* Unsafe version */
int Res = vsprintf (Buf, Format, ap);
#endif
CHECK (Res >= 0 && (unsigned) Res < BufSize);
int Res = xvsnprintf (Buf, BufSize, Format, ap);
CHECK (Res >= 0 && (unsigned) (Res+1) < BufSize);
return Res;
}

View File

@ -6,10 +6,10 @@
/* */
/* */
/* */
/* (C) 2000 Ullrich von Bassewitz */
/* Wacholderweg 14 */
/* D-70597 Stuttgart */
/* EMail: uz@musoftware.de */
/* (C) 2000-2004 Ullrich von Bassewitz */
/* Römerstrasse 52 */
/* D-70794 Filderstadt */
/* EMail: uz@cc65.org */
/* */
/* */
/* This software is provided 'as-is', without any expressed or implied */
@ -51,13 +51,25 @@
int xvsnprintf (char* Buf, size_t Size, const char* Format, va_list ap)
attribute ((format (printf, 3, 0)));
/* A basic vsnprintf implementation. Does currently only support integer
* formats.
*/
int xsnprintf (char* Buf, size_t Size, const char* Format, ...)
attribute ((format (printf, 3, 4)));
/* A basic snprintf implementation. Does currently only support integer
* formats.
*/
int xsprintf (char* Buf, size_t BufSize, const char* Format, ...)
attribute ((format (printf, 3, 4)));
/* Replacement function for sprintf */
/* Replacement function for sprintf. Will FAIL on errors. */
int xvsprintf (char* Buf, size_t BufSize, const char* Format, va_list ap)
attribute ((format (printf, 3, 0)));
/* Replacement function for sprintf */
/* Replacement function for sprintf. Will FAIL on errors. */