]> granicus.if.org Git - postgresql/blob - src/interfaces/libpq/pqexpbuffer.c
Change Copyright from PostgreSQL, Inc to PostgreSQL Global Development Group.
[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-2001, PostgreSQL Global Development Group
18  * Portions Copyright (c) 1994, Regents of the University of California
19  *
20  * $Header: /cvsroot/pgsql/src/interfaces/libpq/pqexpbuffer.c,v 1.8 2001/01/24 19:43:31 momjian Exp $
21  *
22  *-------------------------------------------------------------------------
23  */
24
25 #include "postgres.h"
26 #include "pqexpbuffer.h"
27
28 #ifdef WIN32
29 #include <stdarg.h>
30 #include <stdio.h>
31 #include "win32.h"
32 #endif
33
34 /*
35  * createPQExpBuffer
36  *
37  * Create an empty 'PQExpBufferData' & return a pointer to it.
38  */
39 PQExpBuffer
40 createPQExpBuffer(void)
41 {
42         PQExpBuffer res;
43
44         res = (PQExpBuffer) malloc(sizeof(PQExpBufferData));
45         if (res != NULL)
46                 initPQExpBuffer(res);
47
48         return res;
49 }
50
51 /*
52  * initPQExpBuffer
53  *
54  * Initialize a PQExpBufferData struct (with previously undefined contents)
55  * to describe an empty string.
56  */
57 void
58 initPQExpBuffer(PQExpBuffer str)
59 {
60         str->data = (char *) malloc(INITIAL_EXPBUFFER_SIZE);
61         if (str->data == NULL)
62         {
63                 str->maxlen = 0;
64                 str->len = 0;
65         }
66         else
67         {
68                 str->maxlen = INITIAL_EXPBUFFER_SIZE;
69                 str->len = 0;
70                 str->data[0] = '\0';
71         }
72 }
73
74 /*------------------------
75  * destroyPQExpBuffer(str);
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;
179                  * but if there's hardly any space, don't bother trying,
180                  * just fall through to enlarge the buffer first.
181                  *----------
182                  */
183                 if (str->maxlen > str->len + 16)
184                 {
185                         avail = str->maxlen - str->len - 1;
186                         va_start(args, fmt);
187                         nprinted = vsnprintf(str->data + str->len, avail,
188                                                                  fmt, args);
189                         va_end(args);
190
191                         /*
192                          * Note: some versions of vsnprintf return the number of chars
193                          * actually stored, but at least one returns -1 on failure. Be
194                          * conservative about believing whether the print worked.
195                          */
196                         if (nprinted >= 0 && nprinted < avail - 1)
197                         {
198                                 /* Success.  Note nprinted does not include trailing null. */
199                                 str->len += nprinted;
200                                 break;
201                         }
202                 }
203                 /* Double the buffer size and try again. */
204                 if (!enlargePQExpBuffer(str, str->maxlen))
205                         return;                         /* oops, out of memory */
206         }
207 }
208
209 /*------------------------
210  * appendPQExpBuffer
211  *
212  * Format text data under the control of fmt (an sprintf-like format string)
213  * and append it to whatever is already in str.  More space is allocated
214  * to str if necessary.  This is sort of like a combination of sprintf and
215  * strcat.
216  */
217 void
218 appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
219 {
220         va_list         args;
221         size_t          avail;
222         int                     nprinted;
223
224         for (;;)
225         {
226                 /*----------
227                  * Try to format the given string into the available space;
228                  * but if there's hardly any space, don't bother trying,
229                  * just fall through to enlarge the buffer first.
230                  *----------
231                  */
232                 if (str->maxlen > str->len + 16)
233                 {
234                         avail = str->maxlen - str->len - 1;
235                         va_start(args, fmt);
236                         nprinted = vsnprintf(str->data + str->len, avail,
237                                                                  fmt, args);
238                         va_end(args);
239
240                         /*
241                          * Note: some versions of vsnprintf return the number of chars
242                          * actually stored, but at least one returns -1 on failure. Be
243                          * conservative about believing whether the print worked.
244                          */
245                         if (nprinted >= 0 && nprinted < avail - 1)
246                         {
247                                 /* Success.  Note nprinted does not include trailing null. */
248                                 str->len += nprinted;
249                                 break;
250                         }
251                 }
252                 /* Double the buffer size and try again. */
253                 if (!enlargePQExpBuffer(str, str->maxlen))
254                         return;                         /* oops, out of memory */
255         }
256 }
257
258 /*------------------------
259  * appendPQExpBufferStr
260  * Append the given string to a PQExpBuffer, allocating more space
261  * if necessary.
262  */
263 void
264 appendPQExpBufferStr(PQExpBuffer str, const char *data)
265 {
266         appendBinaryPQExpBuffer(str, data, strlen(data));
267 }
268
269 /*------------------------
270  * appendPQExpBufferChar
271  * Append a single byte to str.
272  * Like appendPQExpBuffer(str, "%c", ch) but much faster.
273  */
274 void
275 appendPQExpBufferChar(PQExpBuffer str, char ch)
276 {
277         /* Make more room if needed */
278         if (!enlargePQExpBuffer(str, 1))
279                 return;
280
281         /* OK, append the character */
282         str->data[str->len] = ch;
283         str->len++;
284         str->data[str->len] = '\0';
285 }
286
287 /*
288  * appendBinaryPQExpBuffer
289  *
290  * Append arbitrary binary data to a PQExpBuffer, allocating more space
291  * if necessary.
292  */
293 void
294 appendBinaryPQExpBuffer(PQExpBuffer str, const char *data, size_t datalen)
295 {
296         /* Make more room if needed */
297         if (!enlargePQExpBuffer(str, datalen))
298                 return;
299
300         /* OK, append the data */
301         memcpy(str->data + str->len, data, datalen);
302         str->len += datalen;
303
304         /*
305          * Keep a trailing null in place, even though it's probably useless
306          * for binary data...
307          */
308         str->data[str->len] = '\0';
309 }