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