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 /*****************************************************************************/
49 /*****************************************************************************/
51 /*****************************************************************************/
55 /* The following is a very basic vsnprintf like function called xvsnprintf. It
56 * features only the basic format specifiers (especially the floating point
57 * stuff is missing), but may be extended if required. Reason for supplying
58 * my own implementation is that vsnprintf is standard but not implemented by
59 * older compilers, and some that implement it, don't adhere to the standard
60 * (for example Microsoft with its _vsnprintf).
65 /* Variable argument list pointer */
73 /* Argument string buffer and string buffer pointer. The string buffer
74 * must be big enough to hold a converted integer of the largest type
75 * including an optional sign and terminating zero.
94 /* Conversion base and table */
96 const char* CharTable;
104 /* Length modifier */
115 /* Unsupported modifiers */
118 /* Default length is integer */
126 static void AddChar (PrintfCtrl* P, char C)
127 /* Store one character in the output buffer if there's enough room. */
129 if (++P->BufFill <= P->BufSize) {
136 static void AddPadding (PrintfCtrl* P, char C, unsigned Count)
137 /* Add some amount of padding */
146 static intmax_t NextIVal (PrintfCtrl*P)
147 /* Read the next integer value from the variable argument list */
149 switch (P->LengthMod) {
150 case lmChar: return (char) va_arg (P->ap, int);
151 case lmShort: return (short) va_arg (P->ap, int);
152 case lmInt: return (int) va_arg (P->ap, int);
153 case lmLong: return (long) va_arg (P->ap, long);
154 case lmIntMax: return va_arg (P->ap, intmax_t);
155 case lmSizeT: return (uintmax_t) va_arg (P->ap, size_t);
156 case lmPtrDiffT: return (long) va_arg (P->ap, ptrdiff_t);
157 default: FAIL ("Invalid type size in NextIVal");
163 static uintmax_t NextUVal (PrintfCtrl*P)
164 /* Read the next unsigned integer value from the variable argument list */
166 switch (P->LengthMod) {
167 case lmChar: return (unsigned char) va_arg (P->ap, unsigned);
168 case lmShort: return (unsigned short) va_arg (P->ap, unsigned);
169 case lmInt: return (unsigned int) va_arg (P->ap, unsigned int);
170 case lmLong: return (unsigned long) va_arg (P->ap, unsigned long);
171 case lmIntMax: return va_arg (P->ap, uintmax_t);
172 case lmSizeT: return va_arg (P->ap, size_t);
173 case lmPtrDiffT: return (intmax_t) va_arg (P->ap, ptrdiff_t);
174 default: FAIL ("Invalid type size in NextUVal");
180 static void ToStr (PrintfCtrl* P, uintmax_t Val)
181 /* Convert the given value to a (reversed) string */
185 *S++ = P->CharTable[Val % P->Base];
188 P->ArgLen = S - P->ArgBuf;
193 static void FormatInt (PrintfCtrl* P, uintmax_t Val)
194 /* Convert the integer value */
197 unsigned LeadCount = 0;
198 unsigned PrecPadding;
199 unsigned WidthPadding;
203 /* Determine the translation table */
204 P->CharTable = (P->Flags & fUpcase)? "0123456789ABCDEF" : "0123456789abcedf";
206 /* Check if the value is negative */
207 if ((P->Flags & fUnsigned) == 0 && ((intmax_t) Val) < 0) {
208 Val = -((intmax_t) Val);
209 Lead[LeadCount++] = '-';
210 } else if ((P->Flags & fPlus) != 0) {
211 Lead[LeadCount++] = '+';
212 } else if ((P->Flags & fSpace) != 0) {
213 Lead[LeadCount++] = ' ';
216 /* Convert the value into a (reversed string). */
219 /* The default precision for all integer conversions is one. This means
220 * that the fPrec flag is always set and does not need to be checked
223 if ((P->Flags & fPrec) == 0) {
228 /* Determine the leaders for alternative forms */
229 if ((P->Flags & fHash) != 0) {
232 Lead[LeadCount++] = '0';
233 Lead[LeadCount++] = (P->Flags & fUpcase)? 'X' : 'x';
234 } else if (P->Base == 8) {
235 /* Alternative form for 'o': always add a leading zero. */
236 if (P->Prec <= P->ArgLen) {
237 Lead[LeadCount++] = '0';
242 /* Determine the amount of precision padding needed */
243 if (P->ArgLen < P->Prec) {
244 PrecPadding = P->Prec - P->ArgLen;
249 /* Determine the width padding needed */
250 if ((P->Flags & fWidth) != 0) {
251 int CurWidth = LeadCount + PrecPadding + P->ArgLen;
252 if (CurWidth < P->Width) {
253 WidthPadding = P->Width - CurWidth;
261 /* Output left space padding if any */
262 if ((P->Flags & (fMinus | fZero)) == 0 && WidthPadding > 0) {
263 AddPadding (P, ' ', WidthPadding);
268 for (I = 0; I < LeadCount; ++I) {
269 AddChar (P, Lead[I]);
272 /* Left zero padding if any */
273 if ((P->Flags & fZero) != 0 && WidthPadding > 0) {
274 AddPadding (P, '0', WidthPadding);
278 /* Precision padding */
279 if (PrecPadding > 0) {
280 AddPadding (P, '0', PrecPadding);
283 /* The number itself. Beware: It's reversed! */
284 while (P->ArgLen > 0) {
285 AddChar (P, P->ArgBuf[--P->ArgLen]);
288 /* Right width padding if any */
289 if (WidthPadding > 0) {
290 AddPadding (P, ' ', WidthPadding);
296 static void FormatStr (PrintfCtrl* P, const char* Val)
297 /* Convert the string */
299 unsigned WidthPadding;
301 /* Get the string length limited to the precision. Beware: We cannot use
302 * strlen here, because if a precision is given, the string may not be
306 if ((P->Flags & fPrec) != 0) {
307 const char* S = memchr (Val, '\0', P->Prec);
309 /* Not zero terminated */
312 /* Terminating zero found */
319 /* Determine the width padding needed */
320 if ((P->Flags & fWidth) != 0 && P->Width > Len) {
321 WidthPadding = P->Width - Len;
326 /* Output left padding */
327 if ((P->Flags & fMinus) != 0 && WidthPadding > 0) {
328 AddPadding (P, ' ', WidthPadding);
332 /* Output the string */
337 /* Output right padding if any */
338 if (WidthPadding > 0) {
339 AddPadding (P, ' ', WidthPadding);
345 static void StoreOffset (PrintfCtrl* P)
346 /* Store the current output offset (%n format spec) */
348 switch (P->LengthMod) {
349 case lmChar: *va_arg (P->ap, int*) = P->BufFill;
350 case lmShort: *va_arg (P->ap, int*) = P->BufFill;
351 case lmInt: *va_arg (P->ap, int*) = P->BufFill;
352 case lmLong: *va_arg (P->ap, long*) = P->BufFill;
353 case lmIntMax: *va_arg (P->ap, intmax_t*) = P->BufFill;
354 case lmSizeT: *va_arg (P->ap, size_t*) = P->BufFill;
355 case lmPtrDiffT: *va_arg (P->ap, ptrdiff_t*) = P->BufFill;
356 default: FAIL ("Invalid size modifier for %n format spec in xvsnprintf");
362 int xvsnprintf (char* Buf, size_t Size, const char* Format, va_list ap)
363 /* A basic vsnprintf implementation. Does currently only support integer
374 /* Initialize the control structure */
380 /* Parse the format string */
381 while ((F = *Format++) != '\0') {
384 /* Not a format specifier, just copy */
390 if (*Format == '%') {
396 /* It's a format specifier. Check for flags. */
400 while (F != '\0' && !Done) {
402 case '-': P.Flags |= fMinus; F = *Format++; break;
403 case '+': P.Flags |= fPlus; F = *Format++; break;
404 case ' ': P.Flags |= fSpace; F = *Format++; break;
405 case '#': P.Flags |= fHash; F = *Format++; break;
406 case '0': P.Flags |= fZero; F = *Format++; break;
407 default: Done = 1; break;
410 /* Optional field width */
412 P.Width = va_arg (P.ap, int);
413 /* A negative field width argument is taken as a - flag followed
414 * by a positive field width.
422 } else if (IsDigit (F)) {
429 P.Width = P.Width * 10 + (F - '0');
434 /* Optional precision */
439 P.Prec = va_arg (P.ap, int);
440 /* A negative precision argument is taken as if the precision
446 F = *Format++; /* Skip the '*' */
447 } else if (IsDigit (F)) {
454 P.Prec = P.Prec * 10 + (F - '0');
456 } else if (F == '-') {
457 /* A negative precision argument is taken as if the precision
460 F = *Format++; /* Skip the minus */
461 while (IsDigit (F = *Format++)) ;
468 /* Optional length modifier */
469 P.LengthMod = lmDefault;
476 P.LengthMod = lmChar;
478 P.LengthMod = lmShort;
486 P.LengthMod = lmLongLong;
488 P.LengthMod = lmLong;
493 P.LengthMod = lmIntMax;
498 P.LengthMod = lmSizeT;
503 P.LengthMod = lmPtrDiffT;
508 P.LengthMod = lmLongDouble;
514 /* If the space and + flags both appear, the space flag is ignored */
515 if ((P.Flags & (fSpace | fPlus)) == (fSpace | fPlus)) {
518 /* If the 0 and - flags both appear, the 0 flag is ignored */
519 if ((P.Flags & (fZero | fMinus)) == (fZero | fMinus)) {
522 /* If a precision is specified, the 0 flag is ignored */
523 if (P.Flags & fPrec) {
527 /* Conversion specifier */
533 FormatInt (&P, NextIVal (&P));
537 P.Flags |= fUnsigned;
539 FormatInt (&P, NextUVal (&P));
543 P.Flags |= fUnsigned;
545 FormatInt (&P, NextUVal (&P));
549 P.Flags |= (fUnsigned | fUpcase);
553 FormatInt (&P, NextUVal (&P));
557 SBuf[0] = (char) va_arg (P.ap, int);
559 FormatStr (&P, SBuf);
563 SPtr = va_arg (P.ap, const char*);
565 FormatStr (&P, SPtr);
569 /* Use hex format for pointers */
570 P.Flags |= (fUnsigned | fPrec);
571 P.Prec = ((sizeof (void*) * CHAR_BIT) + 3) / 4;
573 FormatInt (&P, (uintptr_t) va_arg (P.ap, void*));
581 /* Invalid format spec */
582 FAIL ("Invalid format specifier in xvsnprintf");
587 /* Terminate the output string and return the number of chars that had
588 * been written if the buffer was large enough.
589 * Beware: The terminating zero is not counted for the function result!
592 return P.BufFill - 1;
597 int xsnprintf (char* Buf, size_t Size, const char* Format, ...)
598 /* A basic snprintf implementation. Does currently only support integer
605 va_start (ap, Format);
606 Res = xvsnprintf (Buf, Size, Format, ap);
614 /*****************************************************************************/
616 /*****************************************************************************/
620 int xsprintf (char* Buf, size_t BufSize, const char* Format, ...)
621 /* Replacement function for sprintf */
626 va_start (ap, Format);
627 Res = xvsprintf (Buf, BufSize, Format, ap);
635 int xvsprintf (char* Buf, size_t BufSize, const char* Format, va_list ap)
636 /* Replacement function for sprintf */
638 int Res = xvsnprintf (Buf, BufSize, Format, ap);
639 CHECK (Res >= 0 && (unsigned) (Res+1) < BufSize);