1 /*
2    Copyright (c) 2002 Perry Rapp
3    "The MIT license"
4    Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
5    The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
7 */
8 /*======================================================================
9  * zstr.c -- dynamic buffer strings in C
10  *====================================================================*/
11 
12 #include "llstdlib.h"
13 /* llstdlib.h pulls in standard.h, config.h, sys_inc.h */
14 #include "arch.h" /* vsnprintf */
15 #include "zstr.h"
16 #include "vtable.h"
17 
18 #ifndef INCLUDED_STDARG_H
19 #include <stdarg.h>
20 #define INCLUDED_STDARG_H
21 #endif
22 
23 /*********************************************
24  * local types
25  *********************************************/
26 
27 /*
28 Each ZSTR takes two mallocs
29 But this cannot easily be changed, as clients
30 now assume that ZSTR pointers are stable
31 */
32 struct tag_zstr {
33 	struct tag_vtable * vtable; /* generic object table (see vtable.h) */
34 	int magic;
35 	char * str;
36 	char * end;
37 	unsigned int max;
38 };
39 
40 /*********************************************
41  * local function prototypes
42  *********************************************/
43 
44 /* alphabetical */
45 static void dbgchk(ZSTR);
46 static void init_zstr_vtable(ZSTR zstr);
47 static unsigned int safelen(const char *txt);
48 static void zalloc(ZSTR zstr, unsigned int newmax);
49 static OBJECT zstr_copy(OBJECT obj, int deep);
50 static void zstr_destructor(VTABLE *obj);
51 
52 /*********************************************
53  * local variables
54  *********************************************/
55 
56 /* class vtable for ZSTR objects */
57 static struct tag_vtable vtable_for_zstr = {
58 	VTABLE_MAGIC
59 	, "zstring"
60 	, &zstr_destructor
61 	, &nonrefcountable_isref
62 	, 0
63 	, 0
64 	, &zstr_copy /* copy_fnc */
65 	, &generic_get_type_name
66 };
67 
68 #define DEFSIZE 64
69 
70 #define DBGCHK(zq) dbgchk(zq)
71 
72 /*********************************************
73  * local function definitions
74  * body of module
75  *********************************************/
76 
77 
78 #ifdef TEST_ZSTR
79 static char *
safez(char * str)80 safez (char * str)
81 {
82 	return str ? str : "";
83 }
84 int
main()85 main()
86 {
87 	ZSTR zstr = zs_new();
88 	ASSERT(zstr);
89 
90 	printf("zstr=%s\n", safez(zs_str(zstr)));
91 
92 	zs_cpy(zstr, "dogs");
93 	ASSERT(0 == strcmp(zs_str(zstr), "dogs"));
94 	ASSERT(4 == zs_len(zstr));
95 
96 	printf("zstr=%s\n", safez(zs_str(zstr)));
97 
98 
99 	zs_del(&zstr);
100 	ASSERT(!zstr);
101 }
102 #endif
103 
104 
105 /* reallocate buffer to be at least newmax */
106 static void
zalloc(ZSTR zstr,unsigned int newmax)107 zalloc (ZSTR zstr, unsigned int newmax)
108 {
109 	char * ptr;
110 	int len = zs_len(zstr);
111 	while (zstr->max < newmax)
112 		zstr->max = zstr->max << 1;
113 	ptr = (char *)malloc(zstr->max);
114 	/* use memcpy not strcpy in case has embedded nulls */
115 	memcpy(ptr, zstr->str, len+1);
116 	free(zstr->str);
117 	zstr->str = ptr;
118 	zstr->end = zstr->str + len;
119 	DBGCHK(zstr);
120 }
121 /* validate zstring */
122 static void
dbgchk(ZSTR zstr)123 dbgchk (ZSTR zstr)
124 {
125 	ASSERT(zstr);
126 	ASSERT(zstr->magic == 7843);
127 	ASSERT(zstr->str);
128 	ASSERT(zstr->end);
129 	ASSERT(zstr->max);
130 	ASSERT(zstr->end >= zstr->str);
131 	ASSERT(zstr->end < (zstr->str + zstr->max));
132 	ASSERT(zstr->magic == 7843);
133 }
134 /* create & return new zstring */
135 ZSTR
zs_new(void)136 zs_new (void)
137 {
138 	return zs_newn(DEFSIZE);
139 }
140 /* create & return new zstring with underlying buffer at least min bytes */
141 ZSTR
zs_newn(unsigned int min)142 zs_newn (unsigned int min)
143 {
144 	ZSTR zstr = (ZSTR)malloc(sizeof(*zstr));
145 	unsigned int bksiz = (min<2048)?(min<64?32:128):(min<16384?2048:16384);
146 	while (bksiz < min)
147 		bksiz = bksiz << 1;
148 	init_zstr_vtable(zstr);
149 	zstr->str = (char *)malloc(bksiz);
150 	zstr->str[0] = 0;
151 	zstr->end = zstr->str;
152 	zstr->max = bksiz;
153 	zstr->magic = 7843;
154 	DBGCHK(zstr);
155 	return zstr;
156 }
157 /* strlen but it handles null */
158 static unsigned int
safelen(const char * txt)159 safelen (const char *txt)
160 {
161 	return txt ? strlen(txt) : 0;
162 }
163 /* create & return new zstring containing copy of input */
164 ZSTR
zs_news(const char * str)165 zs_news (const char * str)
166 {
167 	ZSTR zstr=zs_newn(safelen(str)+4);
168 	zs_sets(zstr, str);
169 	return zstr;
170 }
171 /* create & return new zstring with copy of input zstring */
172 ZSTR
zs_newz(ZCSTR zsrc)173 zs_newz (ZCSTR zsrc)
174 {
175 	ZSTR zstr = zs_newn(zs_len(zsrc)+4);
176 	zs_setz(zstr, zsrc);
177 	return zstr;
178 }
179 /* create & return new zstring copying from printf style input */
180 ZSTR
zs_newf(const char * fmt,...)181 zs_newf (const char * fmt, ...)
182 {
183 	ZSTR zstr;
184 	va_list args;
185 	va_start(args, fmt);
186 	zstr = zs_newvf(fmt, args);
187 	va_end(args);
188 	return zstr;
189 }
190 /* create & return new zstring copying from varargs input */
191 ZSTR
zs_newvf(const char * fmt,va_list args)192 zs_newvf (const char * fmt, va_list args)
193 {
194 	ZSTR zstr = zs_newn(strlen(fmt)+8);
195 	zs_setvf(zstr, fmt, args);
196 	return zstr;
197 }
198 /* create & return new zstring containing first len bytes of str */
199 ZSTR
zs_newsubs(const char * str,unsigned int len)200 zs_newsubs (const char * str, unsigned int len)
201 {
202 	ZSTR zstr = zs_news(str);
203 	ASSERT(len<=safelen(str));
204 	zstr->str[len]=0;
205 	return zstr;
206 }
207 /* delete zstring & clear caller's pointer */
208 void
zs_free(ZSTR * pzstr)209 zs_free (ZSTR * pzstr)
210 {
211 	ZSTR zstr = *pzstr;
212 	if  (!zstr) return;
213 	DBGCHK(zstr);
214 	free(zstr->str);
215 	free(zstr);
216 	*pzstr = NULL;
217 }
218 /* return current string */
219 STRING
zs_str(ZCSTR zstr)220 zs_str (ZCSTR zstr)
221 {
222 	if (!zstr) return "";
223 	DBGCHK(zstr);
224 	return zstr->str;
225 }
226 /* return current length of string */
227 unsigned int
zs_len(ZCSTR zstr)228 zs_len (ZCSTR zstr)
229 {
230 	if (!zstr) return 0;
231 	DBGCHK(zstr);
232 	return zstr->end - zstr->str;
233 }
234 /* return current size of underlying buffer */
235 unsigned int
zs_allocsize(ZCSTR zstr)236 zs_allocsize (ZCSTR zstr)
237 {
238 	if (!zstr) return 0;
239 	DBGCHK(zstr);
240 	return zstr->max;
241 }
242 /* update state because caller changed string */
243 /* Assumes simple zero-terminated string */
244 char *
zs_fix(ZSTR zstr)245 zs_fix (ZSTR zstr)
246 {
247 	DBGCHK(zstr);
248 	zstr->end = zstr->str + strlen(zstr->str);
249 	return zstr->str;
250 }
251 /* chop off string to specified length */
252 void
zs_chop(ZSTR zstr,unsigned int len)253 zs_chop (ZSTR zstr, unsigned int len)
254 {
255 	DBGCHK(zstr);
256 	if (len < zs_len(zstr)) {
257 		zstr->str[len] = 0;
258 		zstr->end = zstr->str + len;
259 	}
260 }
261 /* set length directly; caller may use this if using embedded nulls */
262 char *
zs_set_len(ZSTR zstr,unsigned int len)263 zs_set_len (ZSTR zstr, unsigned int len)
264 {
265 	DBGCHK(zstr);
266 	if (len == (unsigned int)-1) {
267 		len = strlen(zstr->str);
268 	}
269 	ASSERT(len < zstr->max);
270 	zstr->end = zstr->str + len;
271 	return zstr->str;
272 }
273 /* set zstring value to input zero-terminated string*/
274 static char *
zs_set_with_len(ZSTR zstr,const char * txt,unsigned int tlen)275 zs_set_with_len (ZSTR zstr, const char * txt, unsigned int tlen)
276 {
277 	DBGCHK(zstr);
278 	zs_reserve(zstr, tlen+1);
279 	if (tlen) {
280 		strcpy(zstr->str, txt);
281 		zstr->end = zstr->str + tlen;
282 	}
283 	return zstr->str;
284 }
285 /* set zstring value to input zero-terminated string*/
286 char *
zs_sets(ZSTR zstr,const char * txt)287 zs_sets (ZSTR zstr, const char * txt)
288 {
289 	unsigned int tlen = safelen(txt);
290 	return zs_set_with_len(zstr, txt, tlen);
291 }
292 /* set zstring value to copy of another zstring value */
293 char *
zs_setz(ZSTR zstr,ZCSTR zsrc)294 zs_setz (ZSTR zstr, ZCSTR zsrc)
295 {
296 	const char * txt = zsrc ? zsrc->str : "";
297 	unsigned int tlen = zsrc ? (zsrc->end)-(zsrc->str) : 0;
298 	DBGCHK(zstr);
299 	return zs_set_with_len(zstr, txt, tlen);
300 }
301 /* append zero-terminated input to zstring */
302 char *
zs_apps(ZSTR zstr,const char * txt)303 zs_apps (ZSTR zstr, const char * txt)
304 {
305 	int tlen = safelen(txt);
306 	DBGCHK(zstr);
307 	zs_reserve(zstr, zs_len(zstr)+tlen+1);
308 	if (tlen) {
309 		strcpy(zstr->end, txt);
310 		zstr->end += tlen;
311 	}
312 	return zstr->str;
313 }
314 /* append input zstring to zstring */
315 char *
zs_appz(ZSTR zstr,ZCSTR zsrc)316 zs_appz(ZSTR zstr, ZCSTR zsrc)
317 {
318 	if (!zsrc) return zstr->str;
319 	return zs_apps(zstr, zsrc->str);
320 }
321 /* append input character to zstring */
322 char *
zs_appc(ZSTR zstr,char ch)323 zs_appc (ZSTR zstr, char ch)
324 {
325 	char buffer[2];
326 	buffer[0] = ch;
327 	buffer[1] = 0;
328 	return zs_apps(zstr, buffer);
329 }
330 /* set printf style input to zstring */
331 char *
zs_setf(ZSTR zstr,const char * fmt,...)332 zs_setf (ZSTR zstr, const char * fmt, ...)
333 {
334 	va_list args;
335 	va_start(args, fmt);
336 	zs_setvf(zstr, fmt, args);
337 	va_end(args);
338 	return zstr->str;
339 }
340 /* append printf style input to zstring */
341 char *
zs_appf(ZSTR zstr,const char * fmt,...)342 zs_appf (ZSTR zstr, const char * fmt, ...)
343 {
344 	va_list args;
345 	va_start(args, fmt);
346 	zs_appvf(zstr, fmt, args);
347 	va_end(args);
348 	return zstr->str;
349 }
350 /* set varargs printf style input to zstring */
351 char *
zs_setvf(ZSTR zstr,const char * fmt,va_list args)352 zs_setvf (ZSTR zstr, const char * fmt, va_list args)
353 {
354 	zs_clear(zstr);
355 	zs_appvf(zstr, fmt, args);
356 	return zstr->str;
357 }
358 /* append varargs printf style input to zstring */
359 char *
zs_appvf(ZSTR zstr,const char * fmt,va_list args)360 zs_appvf (ZSTR zstr, const char * fmt, va_list args)
361 {
362 	/* if we know that the system implementation of snprintf was
363 	standards conformant, we could use snprintf(0, ...), but how to tell ? */
364 	static char buffer[4096];
365 	vsnprintf(buffer, sizeof(buffer), fmt, args);
366 	zs_apps(zstr, buffer);
367 	return zstr->str;
368 }
369 /* set zstring to empty */
370 char *
zs_clear(ZSTR zstr)371 zs_clear (ZSTR zstr)
372 {
373 	DBGCHK(zstr);
374 	zstr->str[0] = 0;
375 	zstr->end = zstr->str;
376 	return zstr->str;
377 }
378 /* ensure at least min bytes in underlying buffer */
379 char *
zs_reserve(ZSTR zstr,unsigned int min)380 zs_reserve (ZSTR zstr, unsigned int min)
381 {
382 	DBGCHK(zstr);
383 	if (min > zstr->max)
384 		zalloc(zstr, min);
385 	return zstr->str;
386 }
387 /* add at least min bytes more to underlying buffer */
388 char *
zs_reserve_extra(ZSTR zstr,unsigned int delta)389 zs_reserve_extra (ZSTR zstr, unsigned int delta)
390 {
391 	zalloc(zstr, (zstr->max)+delta);
392 	return zstr->str;
393 }
394 /* move data from pzsrc to zstr (clearing pzsrc) */
395 void
zs_move(ZSTR zstr,ZSTR * pzsrc)396 zs_move (ZSTR zstr, ZSTR * pzsrc)
397 {
398 	DBGCHK(zstr);
399 	ASSERT(pzsrc);
400 	ASSERT(*pzsrc);
401 	free(zstr->str);
402 	memcpy(zstr, (*pzsrc), sizeof(*zstr));
403 	free(*pzsrc);
404 	*pzsrc = 0;
405 }
406 /*========================================
407  * init_zstr_vtable -- set this zstr's vtable
408  *======================================*/
409 static void
init_zstr_vtable(ZSTR zstr)410 init_zstr_vtable (ZSTR zstr)
411 {
412 	zstr->vtable = &vtable_for_zstr;
413 }
414 /*=================================================
415  * zstr_destructor -- destructor for zstr
416  *  (destructor entry in vtable)
417  *===============================================*/
418 static void
zstr_destructor(VTABLE * obj)419 zstr_destructor (VTABLE *obj)
420 {
421 	ZSTR zstr = (ZSTR)obj;
422 	ASSERT((*obj)->vtable_class == vtable_for_zstr.vtable_class);
423 	zs_free(&zstr);
424 }
425 /*=================================================
426  * zstr_copy -- copy for zstr
427  *===============================================*/
428 static OBJECT
zstr_copy(OBJECT obj,int deep)429 zstr_copy (OBJECT obj, int deep)
430 {
431 	ZSTR zstr = (ZSTR)obj, znew=0;
432 	ASSERT((*obj)->vtable_class == vtable_for_zstr.vtable_class);
433 	deep=deep; /* unused */
434 	ASSERT((*obj)->vtable_class == vtable_for_zstr.vtable_class);
435 	znew = zs_newz(zstr);
436 	return (OBJECT)znew;
437 }
438 
439