1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software Foundation,
14 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15 *
16 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
17 * All rights reserved.
18 * Dynamically sized string ADT
19 */
20
21 /** \file
22 * \ingroup bli
23 */
24
25 #include <stdlib.h> /* malloc */
26 #include <string.h>
27
28 #include "BLI_dynstr.h"
29 #include "BLI_memarena.h"
30 #include "BLI_string.h"
31 #include "BLI_utildefines.h"
32 #include "MEM_guardedalloc.h"
33
34 #ifdef _WIN32
35 # ifndef vsnprintf
36 # define vsnprintf _vsnprintf
37 # endif
38 #endif
39
40 #ifndef va_copy
41 # ifdef __va_copy
42 # define va_copy(a, b) __va_copy(a, b)
43 # else /* !__va_copy */
44 # define va_copy(a, b) ((a) = (b))
45 # endif /* __va_copy */
46 #endif /* va_copy */
47
48 /***/
49
50 typedef struct DynStrElem DynStrElem;
51 struct DynStrElem {
52 DynStrElem *next;
53
54 char *str;
55 };
56
57 struct DynStr {
58 DynStrElem *elems, *last;
59 int curlen;
60 MemArena *memarena;
61 };
62
63 /***/
64
65 /**
66 * Create a new DynStr.
67 *
68 * \return Pointer to a new DynStr.
69 */
BLI_dynstr_new(void)70 DynStr *BLI_dynstr_new(void)
71 {
72 DynStr *ds = MEM_mallocN(sizeof(*ds), "DynStr");
73 ds->elems = ds->last = NULL;
74 ds->curlen = 0;
75 ds->memarena = NULL;
76
77 return ds;
78 }
79
80 /**
81 * Create a new DynStr.
82 *
83 * \return Pointer to a new DynStr.
84 */
BLI_dynstr_new_memarena(void)85 DynStr *BLI_dynstr_new_memarena(void)
86 {
87 DynStr *ds = MEM_mallocN(sizeof(*ds), "DynStr");
88 ds->elems = ds->last = NULL;
89 ds->curlen = 0;
90 ds->memarena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
91
92 return ds;
93 }
94
dynstr_alloc(DynStr * __restrict ds,size_t size)95 BLI_INLINE void *dynstr_alloc(DynStr *__restrict ds, size_t size)
96 {
97 return ds->memarena ? BLI_memarena_alloc(ds->memarena, size) : malloc(size);
98 }
99
100 /**
101 * Append a c-string to a DynStr.
102 *
103 * \param ds: The DynStr to append to.
104 * \param cstr: The c-string to append.
105 */
BLI_dynstr_append(DynStr * __restrict ds,const char * cstr)106 void BLI_dynstr_append(DynStr *__restrict ds, const char *cstr)
107 {
108 DynStrElem *dse = dynstr_alloc(ds, sizeof(*dse));
109 int cstrlen = strlen(cstr);
110
111 dse->str = dynstr_alloc(ds, cstrlen + 1);
112 memcpy(dse->str, cstr, cstrlen + 1);
113 dse->next = NULL;
114
115 if (!ds->last) {
116 ds->last = ds->elems = dse;
117 }
118 else {
119 ds->last = ds->last->next = dse;
120 }
121
122 ds->curlen += cstrlen;
123 }
124
125 /**
126 * Append a length clamped c-string to a DynStr.
127 *
128 * \param ds: The DynStr to append to.
129 * \param cstr: The c-string to append.
130 * \param len: The maximum length of the c-string to copy.
131 */
BLI_dynstr_nappend(DynStr * __restrict ds,const char * cstr,int len)132 void BLI_dynstr_nappend(DynStr *__restrict ds, const char *cstr, int len)
133 {
134 DynStrElem *dse = dynstr_alloc(ds, sizeof(*dse));
135 int cstrlen = BLI_strnlen(cstr, len);
136
137 dse->str = dynstr_alloc(ds, cstrlen + 1);
138 memcpy(dse->str, cstr, cstrlen);
139 dse->str[cstrlen] = '\0';
140 dse->next = NULL;
141
142 if (!ds->last) {
143 ds->last = ds->elems = dse;
144 }
145 else {
146 ds->last = ds->last->next = dse;
147 }
148
149 ds->curlen += cstrlen;
150 }
151
BLI_dynstr_vappendf(DynStr * __restrict ds,const char * __restrict format,va_list args)152 void BLI_dynstr_vappendf(DynStr *__restrict ds, const char *__restrict format, va_list args)
153 {
154 char *message, fixedmessage[256];
155 int len = sizeof(fixedmessage);
156 const int maxlen = 65536;
157 int retval;
158
159 while (1) {
160 va_list args_cpy;
161 if (len == sizeof(fixedmessage)) {
162 message = fixedmessage;
163 }
164 else {
165 message = MEM_callocN(sizeof(char) * len, "BLI_dynstr_appendf");
166 }
167
168 /* cant reuse the same args, so work on a copy */
169 va_copy(args_cpy, args);
170 retval = vsnprintf(message, len, format, args_cpy);
171 va_end(args_cpy);
172
173 if (retval == -1) {
174 /* -1 means not enough space, but on windows it may also mean
175 * there is a formatting error, so we impose a maximum length */
176 if (message != fixedmessage) {
177 MEM_freeN(message);
178 }
179 message = NULL;
180
181 len *= 2;
182 if (len > maxlen) {
183 fprintf(stderr, "BLI_dynstr_append text too long or format error.\n");
184 break;
185 }
186 }
187 else if (retval >= len) {
188 /* in C99 the actual length required is returned */
189 if (message != fixedmessage) {
190 MEM_freeN(message);
191 }
192 message = NULL;
193
194 /* retval doesn't include \0 terminator */
195 len = retval + 1;
196 }
197 else {
198 break;
199 }
200 }
201
202 if (message) {
203 BLI_dynstr_append(ds, message);
204
205 if (message != fixedmessage) {
206 MEM_freeN(message);
207 }
208 }
209 }
210
211 /**
212 * Append a c-string to a DynStr, but with formatting like printf.
213 *
214 * \param ds: The DynStr to append to.
215 * \param format: The printf format string to use.
216 */
BLI_dynstr_appendf(DynStr * __restrict ds,const char * __restrict format,...)217 void BLI_dynstr_appendf(DynStr *__restrict ds, const char *__restrict format, ...)
218 {
219 va_list args;
220 char *message, fixedmessage[256];
221 int len = sizeof(fixedmessage);
222 const int maxlen = 65536;
223 int retval;
224
225 /* note that it's tempting to just call BLI_dynstr_vappendf here
226 * and avoid code duplication, that crashes on some system because
227 * va_start/va_end have to be called for each vsnprintf call */
228
229 while (1) {
230 if (len == sizeof(fixedmessage)) {
231 message = fixedmessage;
232 }
233 else {
234 message = MEM_callocN(sizeof(char) * (len), "BLI_dynstr_appendf");
235 }
236
237 va_start(args, format);
238 retval = vsnprintf(message, len, format, args);
239 va_end(args);
240
241 if (retval == -1) {
242 /* -1 means not enough space, but on windows it may also mean
243 * there is a formatting error, so we impose a maximum length */
244 if (message != fixedmessage) {
245 MEM_freeN(message);
246 }
247 message = NULL;
248
249 len *= 2;
250 if (len > maxlen) {
251 fprintf(stderr, "BLI_dynstr_append text too long or format error.\n");
252 break;
253 }
254 }
255 else if (retval >= len) {
256 /* in C99 the actual length required is returned */
257 if (message != fixedmessage) {
258 MEM_freeN(message);
259 }
260 message = NULL;
261
262 /* retval doesn't include \0 terminator */
263 len = retval + 1;
264 }
265 else {
266 break;
267 }
268 }
269
270 if (message) {
271 BLI_dynstr_append(ds, message);
272
273 if (message != fixedmessage) {
274 MEM_freeN(message);
275 }
276 }
277 }
278
279 /**
280 * Find the length of a DynStr.
281 *
282 * \param ds: The DynStr of interest.
283 * \return The length of \a ds.
284 */
BLI_dynstr_get_len(DynStr * ds)285 int BLI_dynstr_get_len(DynStr *ds)
286 {
287 return ds->curlen;
288 }
289
290 /**
291 * Get a DynStr's contents as a c-string.
292 * The \a rets argument must be allocated to be at
293 * least the size of ``BLI_dynstr_get_len(ds) + 1``.
294 *
295 * \param ds: The DynStr of interest.
296 * \param rets: The string to fill.
297 */
BLI_dynstr_get_cstring_ex(DynStr * __restrict ds,char * __restrict rets)298 void BLI_dynstr_get_cstring_ex(DynStr *__restrict ds, char *__restrict rets)
299 {
300 char *s;
301 DynStrElem *dse;
302
303 for (s = rets, dse = ds->elems; dse; dse = dse->next) {
304 int slen = strlen(dse->str);
305
306 memcpy(s, dse->str, slen);
307
308 s += slen;
309 }
310 BLI_assert((s - rets) == ds->curlen);
311 rets[ds->curlen] = '\0';
312 }
313
314 /**
315 * Get a DynStr's contents as a c-string.
316 * <i> The returned c-string should be freed
317 * using MEM_freeN. </i>
318 *
319 * \param ds: The DynStr of interest.
320 * \return The contents of \a ds as a c-string.
321 */
BLI_dynstr_get_cstring(DynStr * ds)322 char *BLI_dynstr_get_cstring(DynStr *ds)
323 {
324 char *rets = MEM_mallocN(ds->curlen + 1, "dynstr_cstring");
325 BLI_dynstr_get_cstring_ex(ds, rets);
326 return rets;
327 }
328
329 /**
330 * Clear the DynStr
331 *
332 * \param ds: The DynStr to clear.
333 */
BLI_dynstr_clear(DynStr * ds)334 void BLI_dynstr_clear(DynStr *ds)
335 {
336 if (ds->memarena) {
337 BLI_memarena_clear(ds->memarena);
338 }
339 else {
340 for (DynStrElem *dse_next, *dse = ds->elems; dse; dse = dse_next) {
341 dse_next = dse->next;
342
343 free(dse->str);
344 free(dse);
345 }
346 }
347
348 ds->elems = ds->last = NULL;
349 ds->curlen = 0;
350 }
351
352 /**
353 * Free the DynStr
354 *
355 * \param ds: The DynStr to free.
356 */
BLI_dynstr_free(DynStr * ds)357 void BLI_dynstr_free(DynStr *ds)
358 {
359 if (ds->memarena) {
360 BLI_memarena_free(ds->memarena);
361 }
362 else {
363 BLI_dynstr_clear(ds);
364 }
365
366 MEM_freeN(ds);
367 }
368