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