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