/*****************************************************************************/
/* */
-/* xsprintf.c */
+/* xsprintf.c */
/* */
-/* Replacement sprintf function */
+/* Replacement sprintf function */
/* */
/* */
/* */
/* (C) 2000-2004 Ullrich von Bassewitz */
-/* Römerstrasse 52 */
+/* Roemerstrasse 52 */
/* D-70794 Filderstadt */
/* EMail: uz@cc65.org */
/* */
#include <stdio.h>
#include <stddef.h>
#include <string.h>
+#include <limits.h>
+/* common */
#include "chartype.h"
#include "check.h"
+#include "inttypes.h"
+#include "strbuf.h"
+#include "va_copy.h"
#include "xsprintf.h"
/* 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).
- */
+** 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, don't adhere to the standard
+** (for example Microsoft with its _vsnprintf).
+*/
typedef struct {
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.
- */
+ ** 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;
lmShort,
lmInt,
lmLong,
+ lmIntMax,
lmSizeT,
lmPtrDiffT,
lmLongDouble,
/* Unsupported modifiers */
lmLongLong = lmLong,
- lmIntMax = lmLong,
/* Default length is integer */
lmDefault = lmInt
-static long NextIVal (PrintfCtrl*P)
+static intmax_t NextIVal (PrintfCtrl*P)
/* Read the next integer value from the variable argument list */
{
switch (P->LengthMod) {
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 lmIntMax: return va_arg (P->ap, intmax_t);
+ case lmSizeT: return (uintmax_t) va_arg (P->ap, size_t);
case lmPtrDiffT: return (long) va_arg (P->ap, ptrdiff_t);
- default: FAIL ("Invalid type size in NextIVal");
+ default:
+ FAIL ("Invalid type size in NextIVal");
+ return 0;
}
}
-static unsigned long NextUVal (PrintfCtrl*P)
+static uintmax_t 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");
+ 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);
+ default:
+ FAIL ("Invalid type size in NextUVal");
+ return 0;
}
}
-static void ToStr (PrintfCtrl* P, unsigned long Val)
+static void ToStr (PrintfCtrl* P, uintmax_t Val)
/* Convert the given value to a (reversed) string */
{
char* S = P->ArgBuf;
-static void FormatInt (PrintfCtrl* P, unsigned long Val)
+static void FormatInt (PrintfCtrl* P, uintmax_t Val)
/* Convert the integer value */
{
char Lead[5];
/* Determine the translation table */
- P->CharTable = (P->Flags & fUpcase)? "0123456789ABCDEF" : "0123456789abcedf";
+ P->CharTable = (P->Flags & fUpcase)? "0123456789ABCDEF" : "0123456789abcdef";
/* Check if the value is negative */
- if ((P->Flags & fUnsigned) == 0 && ((long) Val) < 0) {
- Val = -((long) Val);
+ if ((P->Flags & fUnsigned) == 0 && ((intmax_t) Val) < 0) {
+ Val = -((intmax_t) Val);
Lead[LeadCount++] = '-';
} else if ((P->Flags & fPlus) != 0) {
Lead[LeadCount++] = '+';
/* Convert the value into a (reversed string). */
ToStr (P, Val);
+ /* The default precision for all integer conversions is one. This means
+ ** that the fPrec flag is always set and does not need to be checked
+ ** later on.
+ */
+ if ((P->Flags & fPrec) == 0) {
+ P->Flags |= fPrec;
+ P->Prec = 1;
+ }
+
/* Determine the leaders for alternative forms */
if ((P->Flags & fHash) != 0) {
if (P->Base == 16) {
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) {
+ if (P->Prec <= P->ArgLen) {
Lead[LeadCount++] = '0';
}
}
}
/* Determine the amount of precision padding needed */
- if ((P->Flags & fPrec) != 0 && P->ArgLen < P->Prec) {
+ if (P->ArgLen < P->Prec) {
PrecPadding = P->Prec - P->ArgLen;
} else {
PrecPadding = 0;
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.
- */
+ ** 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);
+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; break;
+ case lmShort: *va_arg (P->ap, int*) = P->BufFill; break;
+ case lmInt: *va_arg (P->ap, int*) = P->BufFill; break;
+ case lmLong: *va_arg (P->ap, long*) = P->BufFill; break;
+ case lmIntMax: *va_arg (P->ap, intmax_t*) = P->BufFill; break;
+ case lmSizeT: *va_arg (P->ap, size_t*) = P->BufFill; break;
+ case lmPtrDiffT: *va_arg (P->ap, ptrdiff_t*) = P->BufFill; break;
+ default: FAIL ("Invalid size modifier for %n format spec. in xvsnprintf()");
+ }
+}
+
+
+
int xvsnprintf (char* Buf, size_t Size, const char* Format, va_list ap)
/* A basic vsnprintf implementation. Does currently only support integer
- * formats.
- */
+** formats.
+*/
{
PrintfCtrl P;
int Done;
char F;
char SBuf[2];
const char* SPtr;
+ int UseStrBuf = 0;
/* Initialize the control structure */
- P.ap = ap;
+ va_copy (P.ap, ap);
P.Buf = Buf;
P.BufSize = Size;
P.BufFill = 0;
}
/* Optional field width */
if (F == '*') {
- P.Width = va_arg (ap, int);
+ P.Width = va_arg (P.ap, int);
/* A negative field width argument is taken as a - flag followed
- * by a positive field width.
- */
+ ** by a positive field width.
+ */
if (P.Width < 0) {
P.Flags |= fMinus;
P.Width = -P.Width;
F = *Format++;
P.Flags |= fPrec;
if (F == '*') {
- P.Prec = va_arg (ap, int);
+ P.Prec = va_arg (P.ap, int);
/* A negative precision argument is taken as if the precision
- * were omitted.
- */
+ ** were omitted.
+ */
if (P.Prec < 0) {
P.Flags &= ~fPrec;
}
+ F = *Format++; /* Skip the '*' */
} else if (IsDigit (F)) {
P.Prec = F - '0';
while (1) {
}
} else if (F == '-') {
/* A negative precision argument is taken as if the precision
- * were omitted.
- */
+ ** were omitted.
+ */
+ F = *Format++; /* Skip the minus */
while (IsDigit (F = *Format++)) ;
P.Flags &= ~fPrec;
+ } else {
+ P.Prec = 0;
}
}
}
/* If a precision is specified, the 0 flag is ignored */
if (P.Flags & fPrec) {
- P.Flags &= fZero;
+ P.Flags &= ~fZero;
}
/* Conversion specifier */
break;
case 'c':
- SBuf[0] = (char) va_arg (ap, int);
+ SBuf[0] = (char) va_arg (P.ap, int);
SBuf[1] = '\0';
FormatStr (&P, SBuf);
break;
case 's':
- SPtr = va_arg (ap, const char*);
+ SPtr = va_arg (P.ap, const char*);
CHECK (SPtr != 0);
FormatStr (&P, SPtr);
break;
+ case 'p':
+ /* See comment at top of header file */
+ if (UseStrBuf) {
+ /* Argument is StrBuf */
+ const StrBuf* S = va_arg (P.ap, const StrBuf*);
+ CHECK (S != 0);
+ /* Handle the length by using a precision */
+ if ((P.Flags & fPrec) != 0) {
+ /* Precision already specified, use length of string
+ ** if less.
+ */
+ if ((unsigned) P.Prec > SB_GetLen (S)) {
+ P.Prec = SB_GetLen (S);
+ }
+ } else {
+ /* No precision, add it */
+ P.Flags |= fPrec;
+ P.Prec = SB_GetLen (S);
+ }
+ FormatStr (&P, SB_GetConstBuf (S));
+ UseStrBuf = 0; /* Reset flag */
+ } else {
+ /* 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 'm':
+ /* See comment at top of header file */
+ UseStrBuf = 1;
+ break;
+
+ case 'n':
+ StoreOffset (&P);
+ break;
+
default:
/* Invalid format spec */
FAIL ("Invalid format specifier in xvsnprintf");
}
}
+ /* We don't need P.ap any longer */
+ va_end (P.ap);
+
/* 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!
- */
+ ** 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.
- */
+** formats.
+*/
{
int Res;
va_list ap;
/*****************************************************************************/
-/* Code */
+/* Code */
/*****************************************************************************/
CHECK (Res >= 0 && (unsigned) (Res+1) < BufSize);
return Res;
}
-
-
-