]> granicus.if.org Git - postgresql/blob - src/interfaces/libpq/pqexpbuffer.c
Pgindent run for 8.0.
[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-2004, PostgreSQL Global Development Group
18  * Portions Copyright (c) 1994, Regents of the University of California
19  *
20  * $PostgreSQL: pgsql/src/interfaces/libpq/pqexpbuffer.c,v 1.19 2004/08/29 05:07:00 momjian Exp $
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  * createPQExpBuffer
37  *
38  * Create an empty 'PQExpBufferData' & return a pointer to it.
39  */
40 PQExpBuffer
41 createPQExpBuffer(void)
42 {
43         PQExpBuffer res;
44
45         res = (PQExpBuffer) malloc(sizeof(PQExpBufferData));
46         if (res != NULL)
47                 initPQExpBuffer(res);
48
49         return res;
50 }
51
52 /*
53  * initPQExpBuffer
54  *
55  * Initialize a PQExpBufferData struct (with previously undefined contents)
56  * to describe an empty string.
57  */
58 void
59 initPQExpBuffer(PQExpBuffer str)
60 {
61         str->data = (char *) malloc(INITIAL_EXPBUFFER_SIZE);
62         if (str->data == NULL)
63         {
64                 str->maxlen = 0;
65                 str->len = 0;
66         }
67         else
68         {
69                 str->maxlen = INITIAL_EXPBUFFER_SIZE;
70                 str->len = 0;
71                 str->data[0] = '\0';
72         }
73 }
74
75 /*
76  * destroyPQExpBuffer(str);
77  *
78  *              free()s both the data buffer and the PQExpBufferData.
79  *              This is the inverse of createPQExpBuffer().
80  */
81 void
82 destroyPQExpBuffer(PQExpBuffer str)
83 {
84         if (str)
85         {
86                 termPQExpBuffer(str);
87                 free(str);
88         }
89 }
90
91 /*
92  * termPQExpBuffer(str)
93  *              free()s the data buffer but not the PQExpBufferData itself.
94  *              This is the inverse of initPQExpBuffer().
95  */
96 void
97 termPQExpBuffer(PQExpBuffer str)
98 {
99         if (str->data)
100         {
101                 free(str->data);
102                 str->data = NULL;
103         }
104         /* just for luck, make the buffer validly empty. */
105         str->maxlen = 0;
106         str->len = 0;
107 }
108
109 /*
110  * resetPQExpBuffer
111  *              Reset a PQExpBuffer to empty
112  */
113 void
114 resetPQExpBuffer(PQExpBuffer str)
115 {
116         if (str)
117         {
118                 str->len = 0;
119                 if (str->data)
120                         str->data[0] = '\0';
121         }
122 }
123
124 /*
125  * enlargePQExpBuffer
126  * Make sure there is enough space for 'needed' more bytes in the buffer
127  * ('needed' does not include the terminating null).
128  *
129  * Returns 1 if OK, 0 if failed to enlarge buffer.
130  */
131 int
132 enlargePQExpBuffer(PQExpBuffer str, size_t needed)
133 {
134         size_t          newlen;
135         char       *newdata;
136
137         /*
138          * Guard against ridiculous "needed" values, which can occur if we're
139          * fed bogus data.      Without this, we can get an overflow or infinite
140          * loop in the following.
141          */
142         if (needed >= ((size_t) INT_MAX - str->len))
143                 return 0;
144
145         needed += str->len + 1;         /* total space required now */
146
147         /* Because of the above test, we now have needed <= INT_MAX */
148
149         if (needed <= str->maxlen)
150                 return 1;                               /* got enough space already */
151
152         /*
153          * We don't want to allocate just a little more space with each
154          * append; for efficiency, double the buffer size each time it
155          * overflows. Actually, we might need to more than double it if
156          * 'needed' is big...
157          */
158         newlen = (str->maxlen > 0) ? (2 * str->maxlen) : 64;
159         while (needed > newlen)
160                 newlen = 2 * newlen;
161
162         /*
163          * Clamp to INT_MAX in case we went past it.  Note we are assuming
164          * here that INT_MAX <= UINT_MAX/2, else the above loop could
165          * overflow.  We will still have newlen >= needed.
166          */
167         if (newlen > (size_t) INT_MAX)
168                 newlen = (size_t) INT_MAX;
169
170         newdata = (char *) realloc(str->data, newlen);
171         if (newdata != NULL)
172         {
173                 str->data = newdata;
174                 str->maxlen = newlen;
175                 return 1;
176         }
177         return 0;
178 }
179
180 /*
181  * printfPQExpBuffer
182  * Format text data under the control of fmt (an sprintf-like format string)
183  * and insert it into str.      More space is allocated to str if necessary.
184  * This is a convenience routine that does the same thing as
185  * resetPQExpBuffer() followed by appendPQExpBuffer().
186  */
187 void
188 printfPQExpBuffer(PQExpBuffer str, const char *fmt,...)
189 {
190         va_list         args;
191         size_t          avail;
192         int                     nprinted;
193
194         resetPQExpBuffer(str);
195
196         for (;;)
197         {
198                 /*
199                  * Try to format the given string into the available space; but if
200                  * there's hardly any space, don't bother trying, just fall
201                  * through to enlarge the buffer first.
202                  */
203                 if (str->maxlen > str->len + 16)
204                 {
205                         avail = str->maxlen - str->len - 1;
206                         va_start(args, fmt);
207                         nprinted = vsnprintf(str->data + str->len, avail,
208                                                                  fmt, args);
209                         va_end(args);
210
211                         /*
212                          * Note: some versions of vsnprintf return the number of chars
213                          * actually stored, but at least one returns -1 on failure. Be
214                          * conservative about believing whether the print worked.
215                          */
216                         if (nprinted >= 0 && nprinted < (int) avail - 1)
217                         {
218                                 /* Success.  Note nprinted does not include trailing null. */
219                                 str->len += nprinted;
220                                 break;
221                         }
222                 }
223                 /* Double the buffer size and try again. */
224                 if (!enlargePQExpBuffer(str, str->maxlen))
225                         return;                         /* oops, out of memory */
226         }
227 }
228
229 /*
230  * appendPQExpBuffer
231  *
232  * Format text data under the control of fmt (an sprintf-like format string)
233  * and append it to whatever is already in str.  More space is allocated
234  * to str if necessary.  This is sort of like a combination of sprintf and
235  * strcat.
236  */
237 void
238 appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
239 {
240         va_list         args;
241         size_t          avail;
242         int                     nprinted;
243
244         for (;;)
245         {
246                 /*
247                  * Try to format the given string into the available space; but if
248                  * there's hardly any space, don't bother trying, just fall
249                  * through to enlarge the buffer first.
250                  */
251                 if (str->maxlen > str->len + 16)
252                 {
253                         avail = str->maxlen - str->len - 1;
254                         va_start(args, fmt);
255                         nprinted = vsnprintf(str->data + str->len, avail,
256                                                                  fmt, args);
257                         va_end(args);
258
259                         /*
260                          * Note: some versions of vsnprintf return the number of chars
261                          * actually stored, but at least one returns -1 on failure. Be
262                          * conservative about believing whether the print worked.
263                          */
264                         if (nprinted >= 0 && nprinted < (int) avail - 1)
265                         {
266                                 /* Success.  Note nprinted does not include trailing null. */
267                                 str->len += nprinted;
268                                 break;
269                         }
270                 }
271                 /* Double the buffer size and try again. */
272                 if (!enlargePQExpBuffer(str, str->maxlen))
273                         return;                         /* oops, out of memory */
274         }
275 }
276
277 /*
278  * appendPQExpBufferStr
279  * Append the given string to a PQExpBuffer, allocating more space
280  * if necessary.
281  */
282 void
283 appendPQExpBufferStr(PQExpBuffer str, const char *data)
284 {
285         appendBinaryPQExpBuffer(str, data, strlen(data));
286 }
287
288 /*
289  * appendPQExpBufferChar
290  * Append a single byte to str.
291  * Like appendPQExpBuffer(str, "%c", ch) but much faster.
292  */
293 void
294 appendPQExpBufferChar(PQExpBuffer str, char ch)
295 {
296         /* Make more room if needed */
297         if (!enlargePQExpBuffer(str, 1))
298                 return;
299
300         /* OK, append the character */
301         str->data[str->len] = ch;
302         str->len++;
303         str->data[str->len] = '\0';
304 }
305
306 /*
307  * appendBinaryPQExpBuffer
308  *
309  * Append arbitrary binary data to a PQExpBuffer, allocating more space
310  * if necessary.
311  */
312 void
313 appendBinaryPQExpBuffer(PQExpBuffer str, const char *data, size_t datalen)
314 {
315         /* Make more room if needed */
316         if (!enlargePQExpBuffer(str, datalen))
317                 return;
318
319         /* OK, append the data */
320         memcpy(str->data + str->len, data, datalen);
321         str->len += datalen;
322
323         /*
324          * Keep a trailing null in place, even though it's probably useless
325          * for binary data...
326          */
327         str->data[str->len] = '\0';
328 }