1/*-------------------------------------------------------------------------
2 *
3 * pqexpbuffer.c
4 *
5 * PQExpBuffer provides an indefinitely-extensible string data type.
6 * It can be used to buffer either ordinary C strings (null-terminated text)
7 * or arbitrary binary data. All storage is allocated with malloc().
8 *
9 * This module is essentially the same as the backend's StringInfo data type,
10 * but it is intended for use in frontend libpq and client applications.
11 * Thus, it does not rely on palloc() nor elog(), nor psprintf.c which
12 * will exit() on error.
13 *
14 * It does rely on vsnprintf(); if configure finds that libc doesn't provide
15 * a usable vsnprintf(), then a copy of our own implementation of it will
16 * be linked into libpq.
17 *
18 * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
19 * Portions Copyright (c) 1994, Regents of the University of California
20 *
21 * src/interfaces/libpq/pqexpbuffer.c
22 *
23 *-------------------------------------------------------------------------
24 */
25
26#include "postgres_fe.h"
27
28#include <limits.h>
29
30#include "pqexpbuffer.h"
31
32#ifdef WIN32
33#include "win32.h"
34#endif
35
36
37/* All "broken" PQExpBuffers point to this string. */
38static const char oom_buffer[1] = "";
39
40/* Need a char * for unconstify() compatibility */
41static const char *oom_buffer_ptr = oom_buffer;
42
43static bool appendPQExpBufferVA(PQExpBuffer str, const char *fmt, va_list args) pg_attribute_printf(2, 0);
44
45
46/*
47 * markPQExpBufferBroken
48 *
49 * Put a PQExpBuffer in "broken" state if it isn't already.
50 */
51static void
52markPQExpBufferBroken(PQExpBuffer str)
53{
54 if (str->data != oom_buffer)
55 free(str->data);
56
57 /*
58 * Casting away const here is a bit ugly, but it seems preferable to not
59 * marking oom_buffer const. We want to do that to encourage the compiler
60 * to put oom_buffer in read-only storage, so that anyone who tries to
61 * scribble on a broken PQExpBuffer will get a failure.
62 */
63 str->data = unconstify(char *, oom_buffer_ptr);
64 str->len = 0;
65 str->maxlen = 0;
66}
67
68/*
69 * createPQExpBuffer
70 *
71 * Create an empty 'PQExpBufferData' & return a pointer to it.
72 */
73PQExpBuffer
74createPQExpBuffer(void)
75{
76 PQExpBuffer res;
77
78 res = (PQExpBuffer) malloc(sizeof(PQExpBufferData));
79 if (res != NULL)
80 initPQExpBuffer(res);
81
82 return res;
83}
84
85/*
86 * initPQExpBuffer
87 *
88 * Initialize a PQExpBufferData struct (with previously undefined contents)
89 * to describe an empty string.
90 */
91void
92initPQExpBuffer(PQExpBuffer str)
93{
94 str->data = (char *) malloc(INITIAL_EXPBUFFER_SIZE);
95 if (str->data == NULL)
96 {
97 str->data = unconstify(char *, oom_buffer_ptr); /* see comment above */
98 str->maxlen = 0;
99 str->len = 0;
100 }
101 else
102 {
103 str->maxlen = INITIAL_EXPBUFFER_SIZE;
104 str->len = 0;
105 str->data[0] = '\0';
106 }
107}
108
109/*
110 * destroyPQExpBuffer(str);
111 *
112 * free()s both the data buffer and the PQExpBufferData.
113 * This is the inverse of createPQExpBuffer().
114 */
115void
116destroyPQExpBuffer(PQExpBuffer str)
117{
118 if (str)
119 {
120 termPQExpBuffer(str);
121 free(str);
122 }
123}
124
125/*
126 * termPQExpBuffer(str)
127 * free()s the data buffer but not the PQExpBufferData itself.
128 * This is the inverse of initPQExpBuffer().
129 */
130void
131termPQExpBuffer(PQExpBuffer str)
132{
133 if (str->data != oom_buffer)
134 free(str->data);
135 /* just for luck, make the buffer validly empty. */
136 str->data = unconstify(char *, oom_buffer_ptr); /* see comment above */
137 str->maxlen = 0;
138 str->len = 0;
139}
140
141/*
142 * resetPQExpBuffer
143 * Reset a PQExpBuffer to empty
144 *
145 * Note: if possible, a "broken" PQExpBuffer is returned to normal.
146 */
147void
148resetPQExpBuffer(PQExpBuffer str)
149{
150 if (str)
151 {
152 if (str->data != oom_buffer)
153 {
154 str->len = 0;
155 str->data[0] = '\0';
156 }
157 else
158 {
159 /* try to reinitialize to valid state */
160 initPQExpBuffer(str);
161 }
162 }
163}
164
165/*
166 * enlargePQExpBuffer
167 * Make sure there is enough space for 'needed' more bytes in the buffer
168 * ('needed' does not include the terminating null).
169 *
170 * Returns 1 if OK, 0 if failed to enlarge buffer. (In the latter case
171 * the buffer is left in "broken" state.)
172 */
173int
174enlargePQExpBuffer(PQExpBuffer str, size_t needed)
175{
176 size_t newlen;
177 char *newdata;
178
179 if (PQExpBufferBroken(str))
180 return 0; /* already failed */
181
182 /*
183 * Guard against ridiculous "needed" values, which can occur if we're fed
184 * bogus data. Without this, we can get an overflow or infinite loop in
185 * the following.
186 */
187 if (needed >= ((size_t) INT_MAX - str->len))
188 {
189 markPQExpBufferBroken(str);
190 return 0;
191 }
192
193 needed += str->len + 1; /* total space required now */
194
195 /* Because of the above test, we now have needed <= INT_MAX */
196
197 if (needed <= str->maxlen)
198 return 1; /* got enough space already */
199
200 /*
201 * We don't want to allocate just a little more space with each append;
202 * for efficiency, double the buffer size each time it overflows.
203 * Actually, we might need to more than double it if 'needed' is big...
204 */
205 newlen = (str->maxlen > 0) ? (2 * str->maxlen) : 64;
206 while (needed > newlen)
207 newlen = 2 * newlen;
208
209 /*
210 * Clamp to INT_MAX in case we went past it. Note we are assuming here
211 * that INT_MAX <= UINT_MAX/2, else the above loop could overflow. We
212 * will still have newlen >= needed.
213 */
214 if (newlen > (size_t) INT_MAX)
215 newlen = (size_t) INT_MAX;
216
217 newdata = (char *) realloc(str->data, newlen);
218 if (newdata != NULL)
219 {
220 str->data = newdata;
221 str->maxlen = newlen;
222 return 1;
223 }
224
225 markPQExpBufferBroken(str);
226 return 0;
227}
228
229/*
230 * printfPQExpBuffer
231 * Format text data under the control of fmt (an sprintf-like format string)
232 * and insert it into str. More space is allocated to str if necessary.
233 * This is a convenience routine that does the same thing as
234 * resetPQExpBuffer() followed by appendPQExpBuffer().
235 */
236void
237printfPQExpBuffer(PQExpBuffer str, const char *fmt,...)
238{
239 int save_errno = errno;
240 va_list args;
241 bool done;
242
243 resetPQExpBuffer(str);
244
245 if (PQExpBufferBroken(str))
246 return; /* already failed */
247
248 /* Loop in case we have to retry after enlarging the buffer. */
249 do
250 {
251 errno = save_errno;
252 va_start(args, fmt);
253 done = appendPQExpBufferVA(str, fmt, args);
254 va_end(args);
255 } while (!done);
256}
257
258/*
259 * appendPQExpBuffer
260 *
261 * Format text data under the control of fmt (an sprintf-like format string)
262 * and append it to whatever is already in str. More space is allocated
263 * to str if necessary. This is sort of like a combination of sprintf and
264 * strcat.
265 */
266void
267appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
268{
269 int save_errno = errno;
270 va_list args;
271 bool done;
272
273 if (PQExpBufferBroken(str))
274 return; /* already failed */
275
276 /* Loop in case we have to retry after enlarging the buffer. */
277 do
278 {
279 errno = save_errno;
280 va_start(args, fmt);
281 done = appendPQExpBufferVA(str, fmt, args);
282 va_end(args);
283 } while (!done);
284}
285
286/*
287 * appendPQExpBufferVA
288 * Shared guts of printfPQExpBuffer/appendPQExpBuffer.
289 * Attempt to format data and append it to str. Returns true if done
290 * (either successful or hard failure), false if need to retry.
291 *
292 * Caution: callers must be sure to preserve their entry-time errno
293 * when looping, in case the fmt contains "%m".
294 */
295static bool
296appendPQExpBufferVA(PQExpBuffer str, const char *fmt, va_list args)
297{
298 size_t avail;
299 size_t needed;
300 int nprinted;
301
302 /*
303 * Try to format the given string into the available space; but if there's
304 * hardly any space, don't bother trying, just enlarge the buffer first.
305 */
306 if (str->maxlen > str->len + 16)
307 {
308 avail = str->maxlen - str->len;
309
310 nprinted = vsnprintf(str->data + str->len, avail, fmt, args);
311
312 /*
313 * If vsnprintf reports an error, fail (we assume this means there's
314 * something wrong with the format string).
315 */
316 if (unlikely(nprinted < 0))
317 {
318 markPQExpBufferBroken(str);
319 return true;
320 }
321
322 if ((size_t) nprinted < avail)
323 {
324 /* Success. Note nprinted does not include trailing null. */
325 str->len += nprinted;
326 return true;
327 }
328
329 /*
330 * We assume a C99-compliant vsnprintf, so believe its estimate of the
331 * required space, and add one for the trailing null. (If it's wrong,
332 * the logic will still work, but we may loop multiple times.)
333 *
334 * Choke if the required space would exceed INT_MAX, since str->maxlen
335 * can't represent more than that.
336 */
337 if (unlikely(nprinted > INT_MAX - 1))
338 {
339 markPQExpBufferBroken(str);
340 return true;
341 }
342 needed = nprinted + 1;
343 }
344 else
345 {
346 /*
347 * We have to guess at how much to enlarge, since we're skipping the
348 * formatting work. Fortunately, because of enlargePQExpBuffer's
349 * preference for power-of-2 sizes, this number isn't very sensitive;
350 * the net effect is that we'll double the buffer size before trying
351 * to run vsnprintf, which seems sensible.
352 */
353 needed = 32;
354 }
355
356 /* Increase the buffer size and try again. */
357 if (!enlargePQExpBuffer(str, needed))
358 return true; /* oops, out of memory */
359
360 return false;
361}
362
363/*
364 * appendPQExpBufferStr
365 * Append the given string to a PQExpBuffer, allocating more space
366 * if necessary.
367 */
368void
369appendPQExpBufferStr(PQExpBuffer str, const char *data)
370{
371 appendBinaryPQExpBuffer(str, data, strlen(data));
372}
373
374/*
375 * appendPQExpBufferChar
376 * Append a single byte to str.
377 * Like appendPQExpBuffer(str, "%c", ch) but much faster.
378 */
379void
380appendPQExpBufferChar(PQExpBuffer str, char ch)
381{
382 /* Make more room if needed */
383 if (!enlargePQExpBuffer(str, 1))
384 return;
385
386 /* OK, append the character */
387 str->data[str->len] = ch;
388 str->len++;
389 str->data[str->len] = '\0';
390}
391
392/*
393 * appendBinaryPQExpBuffer
394 *
395 * Append arbitrary binary data to a PQExpBuffer, allocating more space
396 * if necessary.
397 */
398void
399appendBinaryPQExpBuffer(PQExpBuffer str, const char *data, size_t datalen)
400{
401 /* Make more room if needed */
402 if (!enlargePQExpBuffer(str, datalen))
403 return;
404
405 /* OK, append the data */
406 memcpy(str->data + str->len, data, datalen);
407 str->len += datalen;
408
409 /*
410 * Keep a trailing null in place, even though it's probably useless for
411 * binary data...
412 */
413 str->data[str->len] = '\0';
414}
415