1 /*
2 Copyright (C) 2005 Silicon Graphics, Inc. All Rights Reserved.
3 Portions Copyright (C) 2013-2016 David Anderson. All Rights Reserved.
4 This program is free software; you can redistribute it and/or modify it
5 under the terms of version 2 of the GNU General Public License as
6 published by the Free Software Foundation.
7
8 This program is distributed in the hope that it would be useful, but
9 WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11
12 Further, this software is distributed without any warranty that it is
13 free of the rightful claim of any third person regarding infringement
14 or the like. Any license provided herein, whether implied or
15 otherwise, applies only to this software file. Patent licenses, if
16 any, provided herein do not apply to combinations of this program with
17 other software, or any other product whatsoever.
18
19 You should have received a copy of the GNU General Public License along
20 with this program; if not, write the Free Software Foundation, Inc., 51
21 Franklin Street - Fifth Floor, Boston MA 02110-1301, USA.
22 */
23
24 /* esb.c
25 extensible string buffer.
26
27 A simple means (vaguely like a C++ class) that
28 enables safely saving strings of arbitrary length built up
29 in small pieces.
30
31 We really do allow only C strings here. NUL bytes
32 in a string result in adding only up to the NUL (and
33 in the case of certain interfaces here a warning
34 to stderr).
35
36 Do selftest as follows:
37 gcc -DSELFTEST esb.c
38 ./a.out
39 valgrind --leak-check=full ./a.out
40
41 The functions assume that
42 pointer arguments of all kinds are not NULL.
43 */
44
45 #ifndef SELFTEST
46 #include "globals.h"
47 #else
48 #include <stdio.h> /* SELFTEST */
49 #include <string.h> /* SELFTEST */
50 #include <stdlib.h> /* SELFTEST */
51 typedef char * string; /* SELFTEST */
52 #include <stdarg.h> /* For va_start va_arg va_list */
53 #endif
54 #include "esb.h"
55
56 /* INITIAL_ALLOC value takes no account of space for a trailing NUL,
57 the NUL is accounted for in init_esb_string
58 and in later tests against esb_allocated_size. */
59 #ifdef SELFTEST
60 #define INITIAL_ALLOC 1 /* SELFTEST */
61 #else
62 /* There is nothing magic about this size.
63 It is just big enough to avoid most resizing. */
64 #define INITIAL_ALLOC 16
65 #endif
66 /* Allow for final NUL */
67 static size_t alloc_size = INITIAL_ALLOC;
68
69 /* NULL device used when printing formatted strings */
70 static FILE *null_device_handle = 0;
71 #if _WIN32
72 #define NULL_DEVICE_NAME "NUL"
73 #else
74 #define NULL_DEVICE_NAME "/dev/null"
75 #endif /* _WIN32 */
76
77 /* Open the null device used during formatting printing */
esb_open_null_device(void)78 FILE *esb_open_null_device(void)
79 {
80 if (!null_device_handle) {
81 null_device_handle = fopen(NULL_DEVICE_NAME,"w");
82 }
83 return null_device_handle;
84 }
85
86 /* Close the null device used during formatting printing */
esb_close_null_device(void)87 void esb_close_null_device(void)
88 {
89 if (null_device_handle) {
90 fclose(null_device_handle);
91 }
92 }
93
94 static void
init_esb_string(struct esb_s * data,size_t min_len)95 init_esb_string(struct esb_s *data, size_t min_len)
96 {
97 char* d;
98
99 if (data->esb_allocated_size > 0) {
100 return;
101 }
102 /* Only esb_constructor applied. Allow for string space. */
103 if (min_len <= alloc_size) {
104 min_len = alloc_size +1;/* Allow for NUL at end */
105 } else {
106 min_len++ ; /* Allow for NUL at end */
107 }
108 d = malloc(min_len);
109 if (!d) {
110 fprintf(stderr,
111 "dwarfdump is out of memory allocating %lu bytes\n",
112 (unsigned long) min_len);
113 exit(5);
114 }
115 data->esb_string = d;
116 data->esb_allocated_size = min_len;
117 data->esb_string[0] = 0;
118 data->esb_used_bytes = 0;
119 }
120
121 /* Make more room. Leaving contents unchanged, effectively.
122 The NUL byte at end has room and this preserves that room.
123 */
124 static void
esb_allocate_more(struct esb_s * data,size_t len)125 esb_allocate_more(struct esb_s *data, size_t len)
126 {
127 size_t new_size = data->esb_allocated_size + len;
128 char* newd = 0;
129
130 if (new_size < alloc_size) {
131 new_size = alloc_size;
132 }
133 newd = realloc(data->esb_string, new_size);
134 if (!newd) {
135 fprintf(stderr, "dwarfdump is out of memory re-allocating "
136 "%lu bytes\n", (unsigned long) new_size);
137 exit(5);
138 }
139 /* If the area was reallocated by realloc() the earlier
140 space was free()d by realloc(). */
141 data->esb_string = newd;
142 data->esb_allocated_size = new_size;
143 }
144
145 void
esb_force_allocation(struct esb_s * data,size_t minlen)146 esb_force_allocation(struct esb_s *data, size_t minlen)
147 {
148 if (data->esb_allocated_size < minlen) {
149 size_t increment = minlen - data->esb_allocated_size;
150 esb_allocate_more(data,increment);
151 }
152 }
153
154 static void
155 esb_appendn_internal(struct esb_s *data, const char * in_string, size_t len);
156
157 void
esb_appendn(struct esb_s * data,const char * in_string,size_t len)158 esb_appendn(struct esb_s *data, const char * in_string, size_t len)
159 {
160 size_t full_len = strlen(in_string);
161
162 if (full_len < len) {
163 fprintf(stderr, "dwarfdump esb internal error, bad string length "
164 " %lu < %lu \n",
165 (unsigned long) full_len, (unsigned long) len);
166 len = full_len;
167 }
168
169 esb_appendn_internal(data, in_string, len);
170 }
171
172 /* The length is gotten from the in_string itself. */
173 void
esb_append(struct esb_s * data,const char * in_string)174 esb_append(struct esb_s *data, const char * in_string)
175 {
176 size_t len = 0;
177 if(in_string) {
178 len = strlen(in_string);
179 if (len) {
180 esb_appendn_internal(data, in_string, len);
181 }
182 }
183 }
184
185 /* The 'len' is believed. Do not pass in strings < len bytes long. */
186 static void
esb_appendn_internal(struct esb_s * data,const char * in_string,size_t len)187 esb_appendn_internal(struct esb_s *data, const char * in_string, size_t len)
188 {
189 size_t remaining = 0;
190 size_t needed = len;
191
192 if (data->esb_allocated_size == 0) {
193 size_t maxlen = (len >= alloc_size)? (len):alloc_size;
194
195 init_esb_string(data, maxlen);
196 }
197 /* ASSERT: data->esb_allocated_size > data->esb_used_bytes */
198 remaining = data->esb_allocated_size - data->esb_used_bytes;
199 if (remaining <= needed) {
200 esb_allocate_more(data,len);
201 }
202 strncpy(&data->esb_string[data->esb_used_bytes], in_string, len);
203 data->esb_used_bytes += len;
204 /* Insist on explicit NUL terminator */
205 data->esb_string[data->esb_used_bytes] = 0;
206 }
207
208 /* Always returns an empty string or a non-empty string. Never 0. */
209 char*
esb_get_string(struct esb_s * data)210 esb_get_string(struct esb_s *data)
211 {
212 if (data->esb_allocated_size == 0) {
213 init_esb_string(data, alloc_size);
214 }
215 return data->esb_string;
216 }
217
218
219 /* Sets esb_used_bytes to zero. The string is not freed and
220 esb_allocated_size is unchanged. */
221 void
esb_empty_string(struct esb_s * data)222 esb_empty_string(struct esb_s *data)
223 {
224 if (data->esb_allocated_size == 0) {
225 init_esb_string(data, alloc_size);
226 }
227 data->esb_used_bytes = 0;
228 data->esb_string[0] = 0;
229 }
230
231
232 /* Return esb_used_bytes. */
233 size_t
esb_string_len(struct esb_s * data)234 esb_string_len(struct esb_s *data)
235 {
236 return data->esb_used_bytes;
237 }
238
239 /* *data is presumed to contain garbage, not values, and
240 is properly initialized here. */
241 void
esb_constructor(struct esb_s * data)242 esb_constructor(struct esb_s *data)
243 {
244 memset(data, 0, sizeof(*data));
245 }
246
247 /* The string is freed, contents of *data set to zeroes. */
248 void
esb_destructor(struct esb_s * data)249 esb_destructor(struct esb_s *data)
250 {
251 if (data->esb_string) {
252 free(data->esb_string);
253 data->esb_string = 0;
254 }
255 esb_constructor(data);
256 }
257
258
259 /* To get all paths in the code tested, this sets the
260 allocation/reallocation to the given value, which can be quite small
261 but must not be zero. */
262 void
esb_alloc_size(size_t size)263 esb_alloc_size(size_t size)
264 {
265 alloc_size = size;
266 }
267
268 size_t
esb_get_allocated_size(struct esb_s * data)269 esb_get_allocated_size(struct esb_s *data)
270 {
271 return data->esb_allocated_size;
272 }
273
274 /* Make more room. Leaving contents unchanged, effectively.
275 The NUL byte at end has room and this preserves that room.
276 */
277 static void
esb_allocate_more_if_needed(struct esb_s * data,const char * in_string,va_list ap)278 esb_allocate_more_if_needed(struct esb_s *data,
279 const char *in_string,va_list ap)
280 {
281 #ifndef _WIN32
282 static char a_buffer[512];
283 #endif /* _WIN32*/
284
285 int netlen = 0;
286 va_list ap_copy;
287
288 /* Preserve the original argument list, to be used a second time */
289 va_copy(ap_copy,ap);
290
291 #ifdef _WIN32
292 netlen = vfprintf(null_device_handle,in_string,ap_copy);
293 #else
294 netlen = vsnprintf(a_buffer,sizeof(a_buffer),in_string,ap_copy);
295 #endif /* _WIN32*/
296
297 /* "The object ap may be passed as an argument to another
298 function; if that function invokes the va_arg()
299 macro with parameter ap, the value of ap in the calling
300 function is unspecified and shall be passed to the va_end()
301 macro prior to any further reference to ap."
302 Single Unix Specification. */
303 va_end(ap_copy);
304
305 /* Allocate enough space to hold the full text */
306 esb_force_allocation(data,netlen + 1);
307 }
308
309 /* Append a formatted string */
310 void
esb_append_printf_ap(struct esb_s * data,const char * in_string,va_list ap)311 esb_append_printf_ap(struct esb_s *data,const char *in_string,va_list ap)
312 {
313 int netlen = 0;
314 int expandedlen = 0;
315
316 /* Allocate enough space for the input string */
317 esb_allocate_more_if_needed(data,in_string,ap);
318
319 netlen = data->esb_allocated_size - data->esb_used_bytes;
320 expandedlen =
321 vsnprintf(&data->esb_string[data->esb_used_bytes],
322 netlen,in_string,ap);
323 if (expandedlen < 0) {
324 /* There was an error.
325 Do nothing. */
326 return;
327 }
328 if (netlen < expandedlen) {
329 /* If data was too small, the max written was one less than
330 netlen. */
331 data->esb_used_bytes += netlen - 1;
332 } else {
333 data->esb_used_bytes += expandedlen;
334 }
335 }
336
337 /* Append a formatted string */
338 void
esb_append_printf(struct esb_s * data,const char * in_string,...)339 esb_append_printf(struct esb_s *data,const char *in_string, ...)
340 {
341 va_list ap;
342 va_start(ap,in_string);
343 esb_append_printf_ap(data,in_string,ap);
344 /* "The object ap may be passed as an argument to another
345 function; if that function invokes the va_arg()
346 macro with parameter ap, the value of ap in the calling
347 function is unspecified and shall be passed to the va_end()
348 macro prior to any further reference to ap."
349 Single Unix Specification. */
350 va_end(ap);
351 }
352
353 /* Get a copy of the internal data buffer.
354 It is up to the code calling this
355 to free() the string using the
356 pointer returned here. */
357 char*
esb_get_copy(struct esb_s * data)358 esb_get_copy(struct esb_s *data)
359 {
360 char* copy = NULL;
361 size_t len = esb_string_len(data);
362 if (len) {
363 copy = (char*)malloc(len + 1);
364 strcpy(copy,esb_get_string(data));
365 }
366 return copy;
367 }
368
369
370 #ifdef SELFTEST
371 static int failcount = 0;
372 void
validate_esb(int instance,struct esb_s * d,size_t explen,size_t expalloc,const char * expout)373 validate_esb(int instance,
374 struct esb_s* d,
375 size_t explen,
376 size_t expalloc,
377 const char *expout)
378 {
379 printf("TEST instance %d\n",instance);
380 if (esb_string_len(d) != explen) {
381 ++failcount;
382 printf("FAIL instance %d esb_string_len() %u explen %u\n",
383 instance,(unsigned)esb_string_len(d),(unsigned)explen);
384 }
385 if (d->esb_allocated_size != expalloc) {
386 ++failcount;
387 printf("FAIL instance %d esb_allocated_size %u expalloc %u\n",
388 instance,(unsigned)d->esb_allocated_size,(unsigned)expalloc);
389 }
390 if(strcmp(esb_get_string(d),expout)) {
391 ++failcount;
392 printf("FAIL instance %d esb_get_stringr %s expstr %s\n",
393 instance,esb_get_string(d),expout);
394 }
395 }
396 void
trialprint_1(struct esb_s * d,char * format,...)397 trialprint_1(struct esb_s *d, char *format,...)
398 {
399 va_list ap;
400
401 va_start(ap,format);
402 esb_append_printf_ap(d,format,ap);
403 va_end(ap);
404 }
405
406 void
trialprint(struct esb_s * d)407 trialprint(struct esb_s *d)
408 {
409 const char * s = "insert me";
410 trialprint_1(d,"aaaa %s bbbb",s);
411 }
412
413
main()414 int main()
415 {
416 {
417 struct esb_s d;
418 esb_constructor(&d);
419 esb_append(&d,"a");
420 validate_esb(1,&d,1,2,"a");
421 esb_append(&d,"b");
422 validate_esb(2,&d,2,3,"ab");
423 esb_append(&d,"c");
424 validate_esb(3,&d,3,4,"abc");
425 esb_empty_string(&d);
426 validate_esb(4,&d,0,4,"");
427 esb_destructor(&d);
428 }
429 {
430 struct esb_s d;
431 esb_constructor(&d);
432 esb_append(&d,"aa");
433 validate_esb(6,&d,2,3,"aa");
434 esb_append(&d,"bbb");
435 validate_esb(7,&d,5,6,"aabbb");
436 esb_append(&d,"c");
437 validate_esb(8,&d,6,7,"aabbbc");
438 esb_empty_string(&d);
439 validate_esb(9,&d,0,7,"");
440 esb_destructor(&d);
441 }
442 {
443 struct esb_s d;
444 static char oddarray[7] = {'a','b',0,'c','c','d',0};
445 esb_constructor(&d);
446 fprintf(stderr,"esb_appendn call error(intentional). Expect msg on stderr\n");
447 /* This provokes a msg on stderr. Bad input. */
448 esb_appendn(&d,oddarray,6);
449 validate_esb(10,&d,2,3,"ab");
450 esb_appendn(&d,"cc",1);
451 validate_esb(11,&d,3,4,"abc");
452 esb_empty_string(&d);
453 validate_esb(12,&d,0,4,"");
454 esb_destructor(&d);
455 }
456 {
457 struct esb_s d;
458 esb_constructor(&d);
459
460 esb_force_allocation(&d,7);
461 esb_append(&d,"aaaa i");
462 validate_esb(13,&d,6,7,"aaaa i");
463 esb_destructor(&d);
464 }
465 {
466 struct esb_s d5;
467 esb_constructor(&d5);
468
469 esb_force_allocation(&d5,50);
470 trialprint(&d5);
471 validate_esb(14,&d5,19,50,"aaaa insert me bbbb");
472 esb_destructor(&d5);
473 }
474 {
475 struct esb_s d;
476 struct esb_s e;
477 char* result = NULL;
478 esb_constructor(&d);
479 esb_constructor(&e);
480
481 esb_append(&d,"abcde fghij klmno pqrst");
482 validate_esb(15,&d,23,24,"abcde fghij klmno pqrst");
483
484 result = esb_get_copy(&d);
485 esb_append(&e,result);
486 validate_esb(16,&e,23,24,"abcde fghij klmno pqrst");
487 esb_destructor(&d);
488 esb_destructor(&e);
489 }
490
491 if (failcount) {
492 printf("FAIL esb test\n");
493 exit(1);
494 }
495 printf("PASS esb test\n");
496 exit(0);
497 }
498 #endif /* SELFTEST */
499