1 /*
2 	dstring.c
3 
4 	dynamic string buffer functions
5 
6 	Copyright (C) 2002  Brian Koropoff.
7 
8 	This program is free software; you can redistribute it and/or
9 	modify it under the terms of the GNU General Public License
10 	as published by the Free Software Foundation; either version 2
11 	of the License, or (at your option) any later version.
12 
13 	This program is distributed in the hope that it will be useful,
14 	but WITHOUT ANY WARRANTY; without even the implied warranty of
15 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16 
17 	See the GNU General Public License for more details.
18 
19 	You should have received a copy of the GNU General Public License
20 	along with this program; if not, write to:
21 
22 		Free Software Foundation, Inc.
23 		59 Temple Place - Suite 330
24 		Boston, MA  02111-1307, USA
25 
26 */
27 
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31 
32 #include <stdlib.h>
33 #include <string.h>
34 #include "QF/sys.h"
35 #include "QF/dstring.h"
36 
37 #include "compat.h"
38 
39 static void *
dstring_alloc(void * data,size_t size)40 dstring_alloc (void *data, size_t size)
41 {
42 	return calloc (1, size);
43 }
44 
45 static void
dstring_free(void * data,void * ptr)46 dstring_free (void *data, void *ptr)
47 {
48 	free (ptr);
49 }
50 
51 static void *
dstring_realloc(void * data,void * ptr,size_t size)52 dstring_realloc (void *data, void *ptr, size_t size)
53 {
54 	return realloc (ptr, size);
55 }
56 
57 VISIBLE dstring_mem_t dstring_default_mem = {
58 	dstring_alloc,
59 	dstring_free,
60 	dstring_realloc,
61 	0
62 };
63 
64 inline dstring_t  *
_dstring_new(dstring_mem_t * mem)65 _dstring_new (dstring_mem_t *mem)
66 {
67 	dstring_t  *new;
68 
69 	new = mem->alloc (mem->data, sizeof (dstring_t));
70 
71 	if (!new)
72 		Sys_Error ("dstring_new: Failed to allocate memory.");
73 	new->mem = mem;
74 	return new;
75 }
76 
77 VISIBLE dstring_t *
dstring_new(void)78 dstring_new (void)
79 {
80 	return _dstring_new (&dstring_default_mem);
81 }
82 
83 VISIBLE void
dstring_delete(dstring_t * dstr)84 dstring_delete (dstring_t *dstr)
85 {
86 	if (dstr->str)
87 		dstr->mem->free (dstr->mem->data, dstr->str);
88 	dstr->mem->free (dstr->mem->data, dstr);
89 }
90 
91 VISIBLE inline void
dstring_adjust(dstring_t * dstr)92 dstring_adjust (dstring_t *dstr)
93 {
94 	if (dstr->size > dstr->truesize) {
95 		dstr->truesize = (dstr->size + 1023) & ~1023;
96 		dstr->str = dstr->mem->realloc (dstr->mem->data, dstr->str,
97 										dstr->truesize);
98 		if (!dstr->str)
99 			Sys_Error ("dstring_adjust:  Failed to reallocate memory.");
100 	}
101 }
102 
103 VISIBLE char *
dstring_reserve(dstring_t * dstr,unsigned len)104 dstring_reserve (dstring_t *dstr, unsigned len)
105 {
106 	dstr->size += len;
107 	dstring_adjust (dstr);
108 	return dstr->str + dstr->size - len;
109 }
110 
111 VISIBLE void
dstring_copy(dstring_t * dstr,const char * data,unsigned int len)112 dstring_copy (dstring_t *dstr, const char *data, unsigned int len)
113 {
114 	dstr->size = len;
115 	dstring_adjust (dstr);
116 	memcpy (dstr->str, data, len);
117 }
118 
119 VISIBLE void
dstring_append(dstring_t * dstr,const char * data,unsigned int len)120 dstring_append (dstring_t *dstr, const char *data, unsigned int len)
121 {
122 	unsigned int ins = dstr->size;		// Save insertion point
123 
124 	dstr->size += len;
125 	dstring_adjust (dstr);
126 	memcpy (dstr->str + ins, data, len);
127 }
128 
129 VISIBLE void
dstring_insert(dstring_t * dstr,unsigned int pos,const char * data,unsigned int len)130 dstring_insert (dstring_t *dstr, unsigned int pos, const char *data,
131 				unsigned int len)
132 {
133 	unsigned int oldsize = dstr->size;
134 
135 	if (pos > dstr->size)
136 		pos = dstr->size;
137 	dstr->size += len;
138 	dstring_adjust (dstr);
139 	memmove (dstr->str + pos + len, dstr->str + pos, oldsize - pos);
140 	memcpy (dstr->str + pos, data, len);
141 }
142 
143 VISIBLE void
dstring_snip(dstring_t * dstr,unsigned int pos,unsigned int len)144 dstring_snip (dstring_t *dstr, unsigned int pos, unsigned int len)
145 {
146 	if (pos > dstr->size)
147 		pos = dstr->size;
148 	if (pos + len > dstr->size)
149 		len = dstr->size - pos;
150 	if (!len)
151 		return;
152 	memmove (dstr->str + pos, dstr->str + pos + len, dstr->size - pos - len);
153 	dstr->size -= len;
154 	dstring_adjust (dstr);
155 }
156 
157 VISIBLE void
dstring_clear(dstring_t * dstr)158 dstring_clear (dstring_t *dstr)
159 {
160 	dstr->size = 0;
161 	dstring_adjust (dstr);
162 }
163 
164 VISIBLE void
dstring_replace(dstring_t * dstr,unsigned int pos,unsigned int rlen,const char * data,unsigned int len)165 dstring_replace (dstring_t *dstr, unsigned int pos, unsigned int rlen,
166 				const char *data, unsigned int len)
167 {
168 	unsigned int oldsize = dstr->size;
169 	if (pos > dstr->size)
170 		pos = dstr->size;
171 	if (rlen > dstr->size - pos)
172 		rlen = dstr->size - pos;
173 	if (rlen < len) {
174 		dstr->size += len - rlen;
175 		dstring_adjust (dstr);
176 		memmove (dstr->str + pos + len, dstr->str + pos + rlen,
177 				 oldsize - (pos + rlen));
178 	} else if (rlen > len) {
179 		memmove (dstr->str + pos + len, dstr->str + pos + rlen,
180 				 oldsize - (pos + rlen));
181 		dstr->size -= rlen - len;
182 		dstring_adjust (dstr);
183 	}
184 	memcpy (dstr->str + pos, data, len);
185 }
186 
187 VISIBLE char *
dstring_freeze(dstring_t * dstr)188 dstring_freeze (dstring_t *dstr)
189 {
190 	char *str = dstr->mem->realloc (dstr->mem->data, dstr->str, dstr->size);
191 	dstr->mem->free (dstr->mem->data, dstr);
192 	return str;
193 }
194 
195 VISIBLE inline dstring_t  *
_dstring_newstr(dstring_mem_t * mem)196 _dstring_newstr (dstring_mem_t *mem)
197 {
198 	dstring_t  *new;
199 
200 	new = mem->alloc (mem->data, sizeof (dstring_t));
201 
202 	if (!new)
203 		Sys_Error ("dstring_newstr:  Failed to allocate memory.");
204 	new->mem = mem;
205 	new->size = 1;
206 	dstring_adjust (new);
207 	new->str[0] = 0;
208 	return new;
209 }
210 
211 VISIBLE dstring_t *
dstring_newstr(void)212 dstring_newstr (void)
213 {
214 	return _dstring_newstr (&dstring_default_mem);
215 }
216 
217 VISIBLE dstring_t *
dstring_strdup(const char * str)218 dstring_strdup (const char *str)
219 {
220 	dstring_t  *dstr = dstring_new ();
221 	dstring_copystr (dstr, str);
222 	return dstr;
223 }
224 
225 VISIBLE char *
dstring_reservestr(dstring_t * dstr,unsigned len)226 dstring_reservestr (dstring_t *dstr, unsigned len)
227 {
228 	int         pos = dstr->size;
229 	if (pos && !dstr->str[pos - 1])
230 		pos--;
231 	dstr->size = pos + len + 1;
232 	dstring_adjust (dstr);
233 	return dstr->str + pos;
234 }
235 
236 VISIBLE void
dstring_copystr(dstring_t * dstr,const char * str)237 dstring_copystr (dstring_t *dstr, const char *str)
238 {
239 	dstr->size = strlen (str) + 1;
240 	dstring_adjust (dstr);
241 	strcpy (dstr->str, str);
242 }
243 
244 VISIBLE void
dstring_copysubstr(dstring_t * dstr,const char * str,unsigned int len)245 dstring_copysubstr (dstring_t *dstr, const char *str, unsigned int len)
246 {
247 	len = strnlen (str, len);
248 
249 	dstr->size = len + 1;
250 	dstring_adjust (dstr);
251 	strncpy (dstr->str, str, len);
252 	dstr->str[len] = 0;
253 }
254 
255 VISIBLE void
dstring_appendstr(dstring_t * dstr,const char * str)256 dstring_appendstr (dstring_t *dstr, const char *str)
257 {
258 	unsigned int pos = strnlen (dstr->str, dstr->size);
259 	unsigned int len = strlen (str);
260 
261 	dstr->size = pos + len + 1;
262 	dstring_adjust (dstr);
263 	strcpy (dstr->str + pos, str);
264 }
265 
266 VISIBLE void
dstring_appendsubstr(dstring_t * dstr,const char * str,unsigned int len)267 dstring_appendsubstr (dstring_t *dstr, const char *str, unsigned int len)
268 {
269 	unsigned int pos = strnlen (dstr->str, dstr->size);
270 
271 	len = strnlen (str, len);
272 	dstr->size = pos + len + 1;
273 	dstring_adjust (dstr);
274 	strncpy (dstr->str + pos, str, len);
275 	dstr->str[pos + len] = 0;
276 }
277 
278 VISIBLE void
dstring_insertstr(dstring_t * dstr,unsigned int pos,const char * str)279 dstring_insertstr (dstring_t *dstr, unsigned int pos, const char *str)
280 {
281 	// Don't insert strlen + 1 to achieve concatenation
282 	dstring_insert (dstr, pos, str, strlen (str));
283 }
284 
285 VISIBLE void
dstring_insertsubstr(dstring_t * dstr,unsigned int pos,const char * str,unsigned int len)286 dstring_insertsubstr (dstring_t *dstr, unsigned int pos, const char *str,
287 					  unsigned int len)
288 {
289 	len = strnlen (str, len);
290 
291 	dstring_insert (dstr, pos, str, len);
292 }
293 
294 VISIBLE void
dstring_clearstr(dstring_t * dstr)295 dstring_clearstr (dstring_t *dstr)
296 {
297 	dstr->size = 1;
298 	dstring_adjust (dstr);
299 	dstr->str[0] = 0;
300 }
301 
302 #if defined (HAVE_VA_COPY)
303 # define VA_COPY(a,b) va_copy (a, b)
304 #elif defined (HAVE__VA_COPY)
305 # define VA_COPY(a,b) __va_copy (a, b)
306 #else
307 # define VA_COPY(a,b) memcpy (a, b, sizeof (a))
308 #endif
309 
310 static int
_dvsprintf(dstring_t * dstr,int offs,const char * fmt,va_list args)311 _dvsprintf (dstring_t *dstr, int offs, const char *fmt, va_list args)
312 {
313 	int         size;
314 
315 #ifdef VA_LIST_IS_ARRAY
316 	va_list     tmp_args;
317 	VA_COPY (tmp_args, args);
318 #endif
319 
320 	if (!dstr->truesize)
321 		dstring_clearstr (dstr); // Make it a string
322 	// Some vsnprintf implementations return -1 on truncation
323 	while ((size = vsnprintf (dstr->str + offs, dstr->truesize - offs - 1, fmt,
324 							  args)) == -1) {
325 		dstr->size = (dstr->truesize & ~1023) + 1024;
326 		dstring_adjust (dstr);
327 #ifdef VA_LIST_IS_ARRAY
328 		VA_COPY (args, tmp_args);
329 #endif
330 	}
331 	dstr->size = size + offs + 2;
332 	// "Proper" implementations return the required size
333 	if (dstr->size > dstr->truesize) {
334 		dstring_adjust (dstr);
335 #ifdef VA_LIST_IS_ARRAY
336 		VA_COPY (args, tmp_args);
337 #endif
338 		vsnprintf (dstr->str + offs, dstr->truesize - offs - 1, fmt, args);
339 	}
340 	dstr->size = size + offs + 1;
341 	dstr->str[dstr->size - 1] = 0;
342 	return size;
343 }
344 
345 VISIBLE int
dvsprintf(dstring_t * dstr,const char * fmt,va_list args)346 dvsprintf (dstring_t *dstr, const char *fmt, va_list args)
347 {
348 	return _dvsprintf (dstr, 0, fmt, args);
349 }
350 
351 VISIBLE int
dsprintf(dstring_t * dstr,const char * fmt,...)352 dsprintf (dstring_t *dstr, const char *fmt, ...)
353 {
354 	va_list     args;
355 	int         ret;
356 
357 	va_start (args, fmt);
358 	ret = _dvsprintf (dstr, 0, fmt, args);
359 	va_end (args);
360 
361 	return ret;
362 }
363 
364 VISIBLE int
davsprintf(dstring_t * dstr,const char * fmt,va_list args)365 davsprintf (dstring_t *dstr, const char *fmt, va_list args)
366 {
367 	int         offs = 0;
368 
369 	if (dstr->size)
370 		offs = dstr->size - 1;
371 	return _dvsprintf (dstr, offs, fmt, args);
372 }
373 
374 VISIBLE int
dasprintf(dstring_t * dstr,const char * fmt,...)375 dasprintf (dstring_t *dstr, const char *fmt, ...)
376 {
377 	va_list     args;
378 	int         ret;
379 	int         offs = 0;
380 
381 	if (dstr->size)
382 		offs = dstr->size - 1;
383 	va_start (args, fmt);
384 	ret = _dvsprintf (dstr, offs, fmt, args);
385 	va_end (args);
386 
387 	return ret;
388 }
389