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