1 /*****************************************************************************/
5 /* Replacement sprintf function */
9 /* (C) 2000-2004 Ullrich von Bassewitz */
10 /* Römerstrasse 52 */
11 /* D-70794 Filderstadt */
12 /* EMail: uz@cc65.org */
15 /* This software is provided 'as-is', without any expressed or implied */
16 /* warranty. In no event will the authors be held liable for any damages */
17 /* arising from the use of this software. */
19 /* Permission is granted to anyone to use this software for any purpose, */
20 /* including commercial applications, and to alter it and redistribute it */
21 /* freely, subject to the following restrictions: */
23 /* 1. The origin of this software must not be misrepresented; you must not */
24 /* claim that you wrote the original software. If you use this software */
25 /* in a product, an acknowledgment in the product documentation would be */
26 /* appreciated but is not required. */
27 /* 2. Altered source versions must be plainly marked as such, and must not */
28 /* be misrepresented as being the original software. */
29 /* 3. This notice may not be removed or altered from any source */
32 /*****************************************************************************/
46 /*****************************************************************************/
48 /*****************************************************************************/
52 /* The following is a very basic vsnprintf like function called xvsnprintf. It
53 * features only the basic format specifiers (especially the floating point
54 * stuff is missing), but may be extended if required. Reason for supplying
55 * my own implementation is that vsnprintf is standard but not implemented by
56 * older compilers, and some that implement it in some way or the other, don't
57 * adhere to the standard (for example Microsoft with its _vsnprintf).
62 /* Variable argument list pointer */
70 /* Argument string buffer and string buffer pointer. The string buffer
71 * must be big enough to hold a converted integer of the largest type
72 * including an optional sign and terminating zero.
91 /* Conversion base and table */
93 const char* CharTable;
101 /* Length modifier */
111 /* Unsupported modifiers */
115 /* Default length is integer */
123 static void AddChar (PrintfCtrl* P, char C)
124 /* Store one character in the output buffer if there's enough room. */
126 if (++P->BufFill <= P->BufSize) {
133 static void AddPadding (PrintfCtrl* P, char C, unsigned Count)
134 /* Add some amount of padding */
143 static long NextIVal (PrintfCtrl*P)
144 /* Read the next integer value from the variable argument list */
146 switch (P->LengthMod) {
147 case lmChar: return (char) va_arg (P->ap, int);
148 case lmShort: return (short) va_arg (P->ap, int);
149 case lmInt: return (int) va_arg (P->ap, int);
150 case lmLong: return (long) va_arg (P->ap, long);
151 case lmSizeT: return (unsigned long) va_arg (P->ap, size_t);
152 case lmPtrDiffT: return (long) va_arg (P->ap, ptrdiff_t);
153 default: FAIL ("Invalid type size in NextIVal");
159 static unsigned long NextUVal (PrintfCtrl*P)
160 /* Read the next unsigned integer value from the variable argument list */
162 switch (P->LengthMod) {
163 case lmChar: return (unsigned char) va_arg (P->ap, unsigned);
164 case lmShort: return (unsigned short) va_arg (P->ap, unsigned);
165 case lmInt: return (unsigned int) va_arg (P->ap, unsigned int);
166 case lmLong: return va_arg (P->ap, unsigned long);
167 case lmSizeT: return (unsigned long) va_arg (P->ap, size_t);
168 case lmPtrDiffT: return (long) va_arg (P->ap, ptrdiff_t);
169 default: FAIL ("Invalid type size in NextUVal");
175 static void ToStr (PrintfCtrl* P, unsigned long Val)
176 /* Convert the given value to a (reversed) string */
180 *S++ = P->CharTable[Val % P->Base];
183 P->ArgLen = S - P->ArgBuf;
188 static void FormatInt (PrintfCtrl* P, unsigned long Val)
189 /* Convert the integer value */
192 unsigned LeadCount = 0;
193 unsigned PrecPadding;
194 unsigned WidthPadding;
198 /* Determine the translation table */
199 P->CharTable = (P->Flags & fUpcase)? "0123456789ABCDEF" : "0123456789abcedf";
201 /* Check if the value is negative */
202 if ((P->Flags & fUnsigned) == 0 && ((long) Val) < 0) {
204 Lead[LeadCount++] = '-';
205 } else if ((P->Flags & fPlus) != 0) {
206 Lead[LeadCount++] = '+';
207 } else if ((P->Flags & fSpace) != 0) {
208 Lead[LeadCount++] = ' ';
211 /* Convert the value into a (reversed string). */
214 /* Determine the leaders for alternative forms */
215 if ((P->Flags & fHash) != 0) {
218 Lead[LeadCount++] = '0';
219 Lead[LeadCount++] = (P->Flags & fUpcase)? 'X' : 'x';
220 } else if (P->Base == 8) {
221 /* Alternative form for 'o': always add a leading zero. */
222 if ((P->Flags & fPrec) == 0 || P->Prec <= P->ArgLen) {
223 Lead[LeadCount++] = '0';
228 /* Determine the amount of precision padding needed */
229 if ((P->Flags & fPrec) != 0 && P->ArgLen < P->Prec) {
230 PrecPadding = P->Prec - P->ArgLen;
235 /* Determine the width padding needed */
236 if ((P->Flags & fWidth) != 0) {
237 int CurWidth = LeadCount + PrecPadding + P->ArgLen;
238 if (CurWidth < P->Width) {
239 WidthPadding = P->Width - CurWidth;
247 /* Output left space padding if any */
248 if ((P->Flags & (fMinus | fZero)) == 0 && WidthPadding > 0) {
249 AddPadding (P, ' ', WidthPadding);
254 for (I = 0; I < LeadCount; ++I) {
255 AddChar (P, Lead[I]);
258 /* Left zero padding if any */
259 if ((P->Flags & fZero) != 0 && WidthPadding > 0) {
260 AddPadding (P, '0', WidthPadding);
264 /* Precision padding */
265 if (PrecPadding > 0) {
266 AddPadding (P, '0', PrecPadding);
269 /* The number itself. Beware: It's reversed! */
270 while (P->ArgLen > 0) {
271 AddChar (P, P->ArgBuf[--P->ArgLen]);
274 /* Right width padding if any */
275 if (WidthPadding > 0) {
276 AddPadding (P, ' ', WidthPadding);
282 static void FormatStr (PrintfCtrl* P, const char* Val)
283 /* Convert the string */
285 unsigned WidthPadding;
287 /* Get the string length limited to the precision. Beware: We cannot use
288 * strlen here, because if a precision is given, the string may not be
292 if ((P->Flags & fPrec) != 0) {
293 const char* S = memchr (Val, '\0', P->Prec);
295 /* Not zero terminated */
298 /* Terminating zero found */
305 /* Determine the width padding needed */
306 if ((P->Flags & fWidth) != 0 && P->Width > Len) {
307 WidthPadding = P->Width - Len;
312 /* Output left padding */
313 if ((P->Flags & fMinus) != 0 && WidthPadding > 0) {
314 AddPadding (P, ' ', WidthPadding);
318 /* Output the string */
323 /* Output right padding if any */
324 if (WidthPadding > 0) {
325 AddPadding (P, ' ', WidthPadding);
331 int xvsnprintf (char* Buf, size_t Size, const char* Format, va_list ap)
332 /* A basic vsnprintf implementation. Does currently only support integer
343 /* Initialize the control structure */
349 /* Parse the format string */
350 while ((F = *Format++) != '\0') {
353 /* Not a format specifier, just copy */
359 if (*Format == '%') {
365 /* It's a format specifier. Check for flags. */
369 while (F != '\0' && !Done) {
371 case '-': P.Flags |= fMinus; F = *Format++; break;
372 case '+': P.Flags |= fPlus; F = *Format++; break;
373 case ' ': P.Flags |= fSpace; F = *Format++; break;
374 case '#': P.Flags |= fHash; F = *Format++; break;
375 case '0': P.Flags |= fZero; F = *Format++; break;
376 default: Done = 1; break;
379 /* Optional field width */
381 P.Width = va_arg (ap, int);
382 /* A negative field width argument is taken as a - flag followed
383 * by a positive field width.
391 } else if (IsDigit (F)) {
398 P.Width = P.Width * 10 + (F - '0');
403 /* Optional precision */
408 P.Prec = va_arg (ap, int);
409 /* A negative precision argument is taken as if the precision
415 } else if (IsDigit (F)) {
422 P.Prec = P.Prec * 10 + (F - '0');
424 } else if (F == '-') {
425 /* A negative precision argument is taken as if the precision
428 while (IsDigit (F = *Format++)) ;
433 /* Optional length modifier */
434 P.LengthMod = lmDefault;
441 P.LengthMod = lmChar;
443 P.LengthMod = lmShort;
451 P.LengthMod = lmLongLong;
453 P.LengthMod = lmLong;
458 P.LengthMod = lmIntMax;
463 P.LengthMod = lmSizeT;
468 P.LengthMod = lmPtrDiffT;
473 P.LengthMod = lmLongDouble;
479 /* If the space and + flags both appear, the space flag is ignored */
480 if ((P.Flags & (fSpace | fPlus)) == (fSpace | fPlus)) {
483 /* If the 0 and - flags both appear, the 0 flag is ignored */
484 if ((P.Flags & (fZero | fMinus)) == (fZero | fMinus)) {
487 /* If a precision is specified, the 0 flag is ignored */
488 if (P.Flags & fPrec) {
492 /* Conversion specifier */
498 FormatInt (&P, NextIVal (&P));
502 P.Flags |= fUnsigned;
504 FormatInt (&P, NextUVal (&P));
508 P.Flags |= fUnsigned;
510 FormatInt (&P, NextUVal (&P));
514 P.Flags |= (fUnsigned | fUpcase);
518 FormatInt (&P, NextUVal (&P));
522 SBuf[0] = (char) va_arg (ap, int);
524 FormatStr (&P, SBuf);
528 SPtr = va_arg (ap, const char*);
530 FormatStr (&P, SPtr);
534 /* Invalid format spec */
535 FAIL ("Invalid format specifier in xvsnprintf");
540 /* Terminate the output string and return the number of chars that had
541 * been written if the buffer was large enough.
542 * Beware: The terminating zero is not counted for the function result!
545 return P.BufFill - 1;
550 int xsnprintf (char* Buf, size_t Size, const char* Format, ...)
551 /* A basic snprintf implementation. Does currently only support integer
558 va_start (ap, Format);
559 Res = xvsnprintf (Buf, Size, Format, ap);
567 /*****************************************************************************/
569 /*****************************************************************************/
573 int xsprintf (char* Buf, size_t BufSize, const char* Format, ...)
574 /* Replacement function for sprintf */
579 va_start (ap, Format);
580 Res = xvsprintf (Buf, BufSize, Format, ap);
588 int xvsprintf (char* Buf, size_t BufSize, const char* Format, va_list ap)
589 /* Replacement function for sprintf */
591 int Res = xvsnprintf (Buf, BufSize, Format, ap);
592 CHECK (Res >= 0 && (unsigned) (Res+1) < BufSize);