]> granicus.if.org Git - postgresql/blob - src/interfaces/libpq/pqexpbuffer.c
libpq: Report strerror on pthread_mutex_lock() failure
[postgresql] / src / interfaces / libpq / pqexpbuffer.c
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().
12  *
13  * It does rely on vsnprintf(); if configure finds that libc doesn't provide
14  * a usable vsnprintf(), then a copy of our own implementation of it will
15  * be linked into libpq.
16  *
17  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
18  * Portions Copyright (c) 1994, Regents of the University of California
19  *
20  * src/interfaces/libpq/pqexpbuffer.c
21  *
22  *-------------------------------------------------------------------------
23  */
24
25 #include "postgres_fe.h"
26
27 #include <limits.h>
28
29 #include "pqexpbuffer.h"
30
31 #ifdef WIN32
32 #include "win32.h"
33 #endif
34
35
36 /* All "broken" PQExpBuffers point to this string. */
37 static const char oom_buffer[1] = "";
38
39
40 /*
41  * markPQExpBufferBroken
42  *
43  * Put a PQExpBuffer in "broken" state if it isn't already.
44  */
45 static void
46 markPQExpBufferBroken(PQExpBuffer str)
47 {
48         if (str->data != oom_buffer)
49                 free(str->data);
50
51         /*
52          * Casting away const here is a bit ugly, but it seems preferable to not
53          * marking oom_buffer const.  We want to do that to encourage the compiler
54          * to put oom_buffer in read-only storage, so that anyone who tries to
55          * scribble on a broken PQExpBuffer will get a failure.
56          */
57         str->data = (char *) oom_buffer;
58         str->len = 0;
59         str->maxlen = 0;
60 }
61
62 /*
63  * createPQExpBuffer
64  *
65  * Create an empty 'PQExpBufferData' & return a pointer to it.
66  */
67 PQExpBuffer
68 createPQExpBuffer(void)
69 {
70         PQExpBuffer res;
71
72         res = (PQExpBuffer) malloc(sizeof(PQExpBufferData));
73         if (res != NULL)
74                 initPQExpBuffer(res);
75
76         return res;
77 }
78
79 /*
80  * initPQExpBuffer
81  *
82  * Initialize a PQExpBufferData struct (with previously undefined contents)
83  * to describe an empty string.
84  */
85 void
86 initPQExpBuffer(PQExpBuffer str)
87 {
88         str->data = (char *) malloc(INITIAL_EXPBUFFER_SIZE);
89         if (str->data == NULL)
90         {
91                 str->data = (char *) oom_buffer;                /* see comment above */
92                 str->maxlen = 0;
93                 str->len = 0;
94         }
95         else
96         {
97                 str->maxlen = INITIAL_EXPBUFFER_SIZE;
98                 str->len = 0;
99                 str->data[0] = '\0';
100         }
101 }
102
103 /*
104  * destroyPQExpBuffer(str);
105  *
106  *              free()s both the data buffer and the PQExpBufferData.
107  *              This is the inverse of createPQExpBuffer().
108  */
109 void
110 destroyPQExpBuffer(PQExpBuffer str)
111 {
112         if (str)
113         {
114                 termPQExpBuffer(str);
115                 free(str);
116         }
117 }
118
119 /*
120  * termPQExpBuffer(str)
121  *              free()s the data buffer but not the PQExpBufferData itself.
122  *              This is the inverse of initPQExpBuffer().
123  */
124 void
125 termPQExpBuffer(PQExpBuffer str)
126 {
127         if (str->data != oom_buffer)
128                 free(str->data);
129         /* just for luck, make the buffer validly empty. */
130         str->data = (char *) oom_buffer;        /* see comment above */
131         str->maxlen = 0;
132         str->len = 0;
133 }
134
135 /*
136  * resetPQExpBuffer
137  *              Reset a PQExpBuffer to empty
138  *
139  * Note: if possible, a "broken" PQExpBuffer is returned to normal.
140  */
141 void
142 resetPQExpBuffer(PQExpBuffer str)
143 {
144         if (str)
145         {
146                 if (str->data != oom_buffer)
147                 {
148                         str->len = 0;
149                         str->data[0] = '\0';
150                 }
151                 else
152                 {
153                         /* try to reinitialize to valid state */
154                         initPQExpBuffer(str);
155                 }
156         }
157 }
158
159 /*
160  * enlargePQExpBuffer
161  * Make sure there is enough space for 'needed' more bytes in the buffer
162  * ('needed' does not include the terminating null).
163  *
164  * Returns 1 if OK, 0 if failed to enlarge buffer.      (In the latter case
165  * the buffer is left in "broken" state.)
166  */
167 int
168 enlargePQExpBuffer(PQExpBuffer str, size_t needed)
169 {
170         size_t          newlen;
171         char       *newdata;
172
173         if (PQExpBufferBroken(str))
174                 return 0;                               /* already failed */
175
176         /*
177          * Guard against ridiculous "needed" values, which can occur if we're fed
178          * bogus data.  Without this, we can get an overflow or infinite loop in
179          * the following.
180          */
181         if (needed >= ((size_t) INT_MAX - str->len))
182         {
183                 markPQExpBufferBroken(str);
184                 return 0;
185         }
186
187         needed += str->len + 1;         /* total space required now */
188
189         /* Because of the above test, we now have needed <= INT_MAX */
190
191         if (needed <= str->maxlen)
192                 return 1;                               /* got enough space already */
193
194         /*
195          * We don't want to allocate just a little more space with each append;
196          * for efficiency, double the buffer size each time it overflows.
197          * Actually, we might need to more than double it if 'needed' is big...
198          */
199         newlen = (str->maxlen > 0) ? (2 * str->maxlen) : 64;
200         while (needed > newlen)
201                 newlen = 2 * newlen;
202
203         /*
204          * Clamp to INT_MAX in case we went past it.  Note we are assuming here
205          * that INT_MAX <= UINT_MAX/2, else the above loop could overflow.      We
206          * will still have newlen >= needed.
207          */
208         if (newlen > (size_t) INT_MAX)
209                 newlen = (size_t) INT_MAX;
210
211         newdata = (char *) realloc(str->data, newlen);
212         if (newdata != NULL)
213         {
214                 str->data = newdata;
215                 str->maxlen = newlen;
216                 return 1;
217         }
218
219         markPQExpBufferBroken(str);
220         return 0;
221 }
222
223 /*
224  * printfPQExpBuffer
225  * Format text data under the control of fmt (an sprintf-like format string)
226  * and insert it into str.      More space is allocated to str if necessary.
227  * This is a convenience routine that does the same thing as
228  * resetPQExpBuffer() followed by appendPQExpBuffer().
229  */
230 void
231 printfPQExpBuffer(PQExpBuffer str, const char *fmt,...)
232 {
233         va_list         args;
234         size_t          avail;
235         int                     nprinted;
236
237         resetPQExpBuffer(str);
238
239         if (PQExpBufferBroken(str))
240                 return;                                 /* already failed */
241
242         for (;;)
243         {
244                 /*
245                  * Try to format the given string into the available space; but if
246                  * there's hardly any space, don't bother trying, just fall through to
247                  * enlarge the buffer first.
248                  */
249                 if (str->maxlen > str->len + 16)
250                 {
251                         avail = str->maxlen - str->len - 1;
252                         va_start(args, fmt);
253                         nprinted = vsnprintf(str->data + str->len, avail,
254                                                                  fmt, args);
255                         va_end(args);
256
257                         /*
258                          * Note: some versions of vsnprintf return the number of chars
259                          * actually stored, but at least one returns -1 on failure. Be
260                          * conservative about believing whether the print worked.
261                          */
262                         if (nprinted >= 0 && nprinted < (int) avail - 1)
263                         {
264                                 /* Success.  Note nprinted does not include trailing null. */
265                                 str->len += nprinted;
266                                 break;
267                         }
268                 }
269                 /* Double the buffer size and try again. */
270                 if (!enlargePQExpBuffer(str, str->maxlen))
271                         return;                         /* oops, out of memory */
272         }
273 }
274
275 /*
276  * appendPQExpBuffer
277  *
278  * Format text data under the control of fmt (an sprintf-like format string)
279  * and append it to whatever is already in str.  More space is allocated
280  * to str if necessary.  This is sort of like a combination of sprintf and
281  * strcat.
282  */
283 void
284 appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
285 {
286         va_list         args;
287         size_t          avail;
288         int                     nprinted;
289
290         if (PQExpBufferBroken(str))
291                 return;                                 /* already failed */
292
293         for (;;)
294         {
295                 /*
296                  * Try to format the given string into the available space; but if
297                  * there's hardly any space, don't bother trying, just fall through to
298                  * enlarge the buffer first.
299                  */
300                 if (str->maxlen > str->len + 16)
301                 {
302                         avail = str->maxlen - str->len - 1;
303                         va_start(args, fmt);
304                         nprinted = vsnprintf(str->data + str->len, avail,
305                                                                  fmt, args);
306                         va_end(args);
307
308                         /*
309                          * Note: some versions of vsnprintf return the number of chars
310                          * actually stored, but at least one returns -1 on failure. Be
311                          * conservative about believing whether the print worked.
312                          */
313                         if (nprinted >= 0 && nprinted < (int) avail - 1)
314                         {
315                                 /* Success.  Note nprinted does not include trailing null. */
316                                 str->len += nprinted;
317                                 break;
318                         }
319                 }
320                 /* Double the buffer size and try again. */
321                 if (!enlargePQExpBuffer(str, str->maxlen))
322                         return;                         /* oops, out of memory */
323         }
324 }
325
326 /*
327  * appendPQExpBufferStr
328  * Append the given string to a PQExpBuffer, allocating more space
329  * if necessary.
330  */
331 void
332 appendPQExpBufferStr(PQExpBuffer str, const char *data)
333 {
334         appendBinaryPQExpBuffer(str, data, strlen(data));
335 }
336
337 /*
338  * appendPQExpBufferChar
339  * Append a single byte to str.
340  * Like appendPQExpBuffer(str, "%c", ch) but much faster.
341  */
342 void
343 appendPQExpBufferChar(PQExpBuffer str, char ch)
344 {
345         /* Make more room if needed */
346         if (!enlargePQExpBuffer(str, 1))
347                 return;
348
349         /* OK, append the character */
350         str->data[str->len] = ch;
351         str->len++;
352         str->data[str->len] = '\0';
353 }
354
355 /*
356  * appendBinaryPQExpBuffer
357  *
358  * Append arbitrary binary data to a PQExpBuffer, allocating more space
359  * if necessary.
360  */
361 void
362 appendBinaryPQExpBuffer(PQExpBuffer str, const char *data, size_t datalen)
363 {
364         /* Make more room if needed */
365         if (!enlargePQExpBuffer(str, datalen))
366                 return;
367
368         /* OK, append the data */
369         memcpy(str->data + str->len, data, datalen);
370         str->len += datalen;
371
372         /*
373          * Keep a trailing null in place, even though it's probably useless for
374          * binary data...
375          */
376         str->data[str->len] = '\0';
377 }