1 #define _GNU_SOURCE   /* But only when HAVE_ASPRINTF */
2 #include <stdarg.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <limits.h>
6 #include <string.h>
7 
8 #include "xmlrpc_config.h"  /* For HAVE_ASPRINTF, __inline__ */
9 #include "bool.h"
10 #include "casprintf.h"
11 
12 
13 
14 static __inline__ void
newVsnprintf(char * const buffer,size_t const bufferSize,const char * const fmt,va_list varargs,size_t * const formattedSizeP)15 newVsnprintf(char *       const buffer,
16              size_t       const bufferSize,
17              const char * const fmt,
18              va_list            varargs,
19              size_t *     const formattedSizeP) {
20 /*----------------------------------------------------------------------------
21    This is vsnprintf() with the new behavior, where not fitting in the buffer
22    is not a failure.
23 
24    Unfortunately, we can't practically return the size of the formatted string
25    if the C library has old vsnprintf() and the formatted string doesn't fit
26    in the buffer, so in that case we just return something larger than the
27    buffer.
28 -----------------------------------------------------------------------------*/
29     if (bufferSize > INT_MAX/2) {
30         /* There's a danger we won't be able to coerce the return value
31            of XMLRPC_VSNPRINTF to an integer (which we have to do because,
32            while for POSIX its return value is ssize_t, on Windows it is int),
33            or return double the buffer size.
34         */
35         *formattedSizeP = 0;
36     } else {
37         int rc;
38 
39         rc = XMLRPC_VSNPRINTF(buffer, bufferSize, fmt, varargs);
40 
41         if (rc < 0) {
42             /* We have old vsnprintf() (or Windows) and the formatted value
43                doesn't fit in the buffer, but we don't know how big a buffer it
44                needs.
45             */
46             *formattedSizeP = bufferSize * 2;
47         } else {
48             /* Either the string fits in the buffer or we have new vsnprintf()
49                which tells us how big the string is regardless.
50             */
51             *formattedSizeP = rc;
52         }
53     }
54 }
55 
56 
57 
58 static __inline__ int
simpleVasprintf(char ** const resultP,const char * const fmt,va_list varargs)59 simpleVasprintf(char **      const resultP,
60                 const char * const fmt,
61                 va_list            varargs) {
62 /*----------------------------------------------------------------------------
63    This is a poor man's implementation of vasprintf(), of GNU fame.
64 -----------------------------------------------------------------------------*/
65     int retval;
66     char * buffer;
67     size_t bufferSize;
68     bool outOfMemory;
69 
70     for (buffer = NULL, bufferSize = 4096, outOfMemory = false;
71          !buffer && !outOfMemory;
72         ) {
73 
74         buffer = malloc(bufferSize);
75         if (!buffer)
76             outOfMemory = true;
77         else {
78             size_t bytesNeeded;
79             newVsnprintf(buffer, bufferSize, fmt, varargs, &bytesNeeded);
80             if (bytesNeeded > bufferSize) {
81                 free(buffer);
82                 buffer = NULL;
83                 bufferSize = bytesNeeded;
84             }
85         }
86     }
87     if (outOfMemory)
88         retval = -1;
89     else {
90         retval = strlen(buffer);
91         *resultP = buffer;
92     }
93     return retval;
94 }
95 
96 
97 
98 const char * const strsol = "[Insufficient memory to build string]";
99 
100 
101 
102 void
cvasprintf(const char ** const retvalP,const char * const fmt,va_list varargs)103 cvasprintf(const char ** const retvalP,
104            const char *  const fmt,
105            va_list             varargs) {
106 
107     char * string;
108     int rc;
109 
110 #if HAVE_ASPRINTF
111     rc = vasprintf(&string, fmt, varargs);
112 #else
113     rc = simpleVasprintf(&string, fmt, varargs);
114 #endif
115 
116     if (rc < 0)
117         *retvalP = strsol;
118     else
119         *retvalP = string;
120 }
121 
122 
123 
124 void GNU_PRINTF_ATTR(2,3)
casprintf(const char ** const retvalP,const char * const fmt,...)125 casprintf(const char ** const retvalP, const char * const fmt, ...) {
126 
127     va_list varargs;  /* mysterious structure used by variable arg facility */
128 
129     va_start(varargs, fmt); /* start up the mysterious variable arg facility */
130 
131     cvasprintf(retvalP, fmt, varargs);
132 
133     va_end(varargs);
134 }
135 
136 
137 
138 void
strfree(const char * const string)139 strfree(const char * const string) {
140 
141     if (string != strsol)
142         free((void *)string);
143 }
144