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