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