]> git.sur5r.net Git - cc65/blobdiff - src/common/xsprintf.c
Only for jumps, the lib uses named asm labels in branches
[cc65] / src / common / xsprintf.c
index 8c7dc33eb0ac60c6f7a78712ade4835e6ede04b4..a7d26d5ef76ec12c87ee5104976e96826ab0b744 100644 (file)
@@ -1,13 +1,13 @@
 /*****************************************************************************/
 /*                                                                           */
-/*                               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 {
 
@@ -68,9 +73,9 @@ 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;
 
@@ -104,13 +109,13 @@ typedef struct {
         lmShort,
         lmInt,
         lmLong,
+        lmIntMax,
         lmSizeT,
         lmPtrDiffT,
         lmLongDouble,
 
         /* Unsupported modifiers */
         lmLongLong = lmLong,
-        lmIntMax = lmLong,
 
         /* Default length is integer */
         lmDefault = lmInt
@@ -140,7 +145,7 @@ static void AddPadding (PrintfCtrl* P, char C, unsigned Count)
 
 
 
-static long NextIVal (PrintfCtrl*P)
+static intmax_t NextIVal (PrintfCtrl*P)
 /* Read the next integer value from the variable argument list */
 {
     switch (P->LengthMod) {
@@ -148,31 +153,37 @@ static long NextIVal (PrintfCtrl*P)
         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;
@@ -185,7 +196,7 @@ static void ToStr (PrintfCtrl* P, unsigned long Val)
 
 
 
-static void FormatInt (PrintfCtrl* P, unsigned long Val)
+static void FormatInt (PrintfCtrl* P, uintmax_t Val)
 /* Convert the integer value */
 {
     char Lead[5];
@@ -196,11 +207,11 @@ static void FormatInt (PrintfCtrl* P, unsigned long Val)
 
 
     /* 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++] = '+';
@@ -211,6 +222,15 @@ static void FormatInt (PrintfCtrl* P, unsigned long Val)
     /* 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) {
@@ -219,14 +239,14 @@ static void FormatInt (PrintfCtrl* P, unsigned long Val)
             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;
@@ -285,9 +305,9 @@ static void FormatStr (PrintfCtrl* P, const char* Val)
     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);
@@ -328,20 +348,38 @@ static void FormatStr (PrintfCtrl* P, const char* Val)
 
 
 
+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;
@@ -378,10 +416,10 @@ int xvsnprintf (char* Buf, size_t Size, const char* Format, va_list ap)
         }
         /* 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;
@@ -405,13 +443,14 @@ int xvsnprintf (char* Buf, size_t Size, const char* Format, va_list ap)
             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) {
@@ -423,10 +462,13 @@ int xvsnprintf (char* Buf, size_t Size, const char* Format, va_list ap)
                 }
             } 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;
             }
         }
 
@@ -486,7 +528,7 @@ int xvsnprintf (char* Buf, size_t Size, const char* Format, va_list ap)
         }
         /* If a precision is specified, the 0 flag is ignored */
         if (P.Flags & fPrec) {
-            P.Flags &= fZero;
+            P.Flags &= ~fZero;
         }
 
         /* Conversion specifier */
@@ -519,17 +561,56 @@ int xvsnprintf (char* Buf, size_t Size, const char* Format, va_list ap)
                 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");
@@ -537,10 +618,13 @@ int xvsnprintf (char* Buf, size_t Size, const char* Format, va_list ap)
         }
     }
 
+    /* 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;
 }
@@ -549,8 +633,8 @@ int xvsnprintf (char* Buf, size_t Size, const char* Format, va_list ap)
 
 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;
@@ -565,7 +649,7 @@ int xsnprintf (char* Buf, size_t Size, const char* Format, ...)
 
 
 /*****************************************************************************/
-/*                                          Code                                    */
+/*                                   Code                                    */
 /*****************************************************************************/
 
 
@@ -592,6 +676,3 @@ int xvsprintf (char* Buf, size_t BufSize, const char* Format, va_list ap)
     CHECK (Res >= 0 && (unsigned) (Res+1) < BufSize);
     return Res;
 }
-
-
-