xref: /illumos-gate/usr/src/lib/libdwarf/common/dwarfstring.c (revision 09fbbb7d1b5a956a9b81304070dcff318a4a158e)
1 /*
2 Copyright (c) 2019-2019, David Anderson
3 All rights reserved.
4 
5 Redistribution and use in source and binary forms, with
6 or without modification, are permitted provided that the
7 following conditions are met:
8 
9     Redistributions of source code must retain the above
10     copyright notice, this list of conditions and the following
11     disclaimer.
12 
13     Redistributions in binary form must reproduce the above
14     copyright notice, this list of conditions and the following
15     disclaimer in the documentation and/or other materials
16     provided with the distribution.
17 
18 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
19 CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
20 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
23 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
29 OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
30 EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32 
33 /*  A lighly generalized data buffer.
34     Works for more than just strings,
35     but has features (such as ensuring
36     data always has a NUL byte following
37     the data area used) most useful for C strings.
38 
39     All these return either TRUE (the values altered)
40     or FALSE (something went wrong, quite likely
41     the caller presented a bad format string for the
42     value).
43 */
44 
45 #include "config.h"
46 #include <stdio.h> /* for malloc */
47 #ifdef HAVE_STDLIB_H
48 #include <stdlib.h> /* for malloc */
49 #endif /* HAVE_STDLIB_H */
50 #include <string.h> /* for strlen */
51 #ifdef HAVE_MALLOC_H
52 /* Useful include for some Windows compilers. */
53 #include <malloc.h>
54 #endif /* HAVE_MALLOC_H */
55 #include "dwarfstring.h"
56 #ifndef TRUE
57 #define TRUE 1
58 #endif /* TRUE */
59 #ifndef FALSE
60 #define FALSE 0
61 #endif /* FALSE */
62 
63 #ifdef HAVE_UNUSED_ATTRIBUTE
64 #define  UNUSEDARG __attribute__ ((unused))
65 #else
66 #define  UNUSEDARG
67 #endif
68 
69 
70 static unsigned long minimumnewlen = 30;
71 /*
72 struct dwarfstring_s {
73    char *        s_data;
74    unsigned long s_size;
75    unsigned long s_avail;
76    unsigned char s_malloc;
77 };
78 */
79 
80 int
81 dwarfstring_constructor(struct dwarfstring_s *g)
82 {
83     g->s_data = "";
84     g->s_size = 0;
85     g->s_avail = 0;
86     g->s_malloc = FALSE;
87     return TRUE;
88 }
89 
90 static int
91 dwarfstring_resize_to(struct dwarfstring_s *g,unsigned long newlen)
92 {
93     char *b = 0;
94     unsigned long lastpos =
95         g->s_size - g->s_avail;
96     unsigned long malloclen = newlen+1;
97 
98     if(malloclen < minimumnewlen) {
99         malloclen = minimumnewlen;
100     }
101     b = malloc(malloclen);
102     if (!b) {
103         return FALSE;
104     }
105     if (lastpos > 0) {
106         memcpy(b,g->s_data,lastpos);
107     }
108     if (g->s_malloc) {
109         free(g->s_data);
110         g->s_data = 0;
111     }
112     g->s_data = b;
113     g->s_data[lastpos] = 0;
114     g->s_size = newlen;
115     g->s_avail = newlen - lastpos;
116     g->s_malloc = TRUE;
117     return TRUE;
118 }
119 
120 int
121 dwarfstring_reset(struct dwarfstring_s *g)
122 {
123     if (!g->s_size) {
124         /* In initial condition, nothing to do. */
125         return TRUE;
126     }
127     g->s_avail = g->s_size;
128     g->s_data[0] = 0;
129     return TRUE;
130 }
131 
132 int
133 dwarfstring_constructor_fixed(struct dwarfstring_s *g,unsigned long len)
134 {
135     int r = FALSE;
136 
137     dwarfstring_constructor(g);
138     if (len == 0) {
139         return TRUE;
140     }
141     r = dwarfstring_resize_to(g,len);
142     if (!r) {
143         return FALSE;
144     }
145     return TRUE;
146 }
147 
148 int
149 dwarfstring_constructor_static(struct dwarfstring_s *g,
150     char * space,
151     unsigned long len)
152 {
153     dwarfstring_constructor(g);
154     g->s_data = space;
155     g->s_data[0] = 0;
156     g->s_size = len;
157     g->s_avail = len;
158     g->s_malloc = FALSE;
159     return TRUE;
160 }
161 
162 void
163 dwarfstring_destructor(struct dwarfstring_s *g)
164 {
165     if (g->s_malloc) {
166         free(g->s_data);
167         g->s_data = 0;
168         g->s_malloc = 0;
169     }
170     dwarfstring_constructor(g);
171 }
172 
173 /*  For the case where one wants just the first 'len'
174     characters of 'str'. NUL terminator provided
175     for you in s_data.
176 */
177 int
178 dwarfstring_append_length(struct dwarfstring_s *g,char *str,
179     unsigned long slen)
180 {
181     unsigned long lastpos = g->s_size - g->s_avail;
182     int r = 0;
183 
184     if (!str  || slen ==0) {
185         return TRUE;
186     }
187     if (slen >= g->s_avail) {
188         unsigned long newlen = 0;
189 
190         newlen = g->s_size + slen+2;
191         r = dwarfstring_resize_to(g,newlen);
192         if (!r) {
193             return FALSE;
194         }
195     }
196     memcpy(g->s_data + lastpos,str,slen);
197     g->s_avail -= slen;
198     g->s_data[g->s_size - g->s_avail] = 0;
199     return TRUE;
200 }
201 
202 int
203 dwarfstring_append(struct dwarfstring_s *g,char *str)
204 {
205     unsigned long dlen = 0;
206 
207     if(!str) {
208         return TRUE;
209     }
210     dlen = strlen(str);
211     return dwarfstring_append_length(g,str,dlen);
212 }
213 
214 char *
215 dwarfstring_string(struct dwarfstring_s *g)
216 {
217     return g->s_data;
218 }
219 
220 unsigned long
221 dwarfstring_strlen(struct dwarfstring_s *g)
222 {
223     return g->s_size - g->s_avail;
224 }
225 
226 static int
227 _dwarfstring_append_spaces(dwarfstring *data,
228    size_t count)
229 {
230     int res = 0;
231     char spacebuf[] = {"                                       "};
232     size_t charct = sizeof(spacebuf)-1;
233     size_t l = count;
234 
235     while (l > charct) {
236         res = dwarfstring_append_length(data,spacebuf,charct);
237         l -= charct;
238         if (res != TRUE) {
239             return res;
240         }
241     }
242     /* ASSERT: l > 0 */
243     res = dwarfstring_append_length(data,spacebuf,l);
244     return res;
245 }
246 static int
247 _dwarfstring_append_zeros(dwarfstring *data, size_t l)
248 {
249     int res = 0;
250     static char zeros[] = {"0000000000000000000000000000000000000000"};
251     size_t charct = sizeof(zeros)-1;
252 
253     while (l > charct) {
254         res = dwarfstring_append_length(data,zeros,charct);
255         l -= charct;
256         if (res != TRUE) {
257             return res;
258         }
259     }
260     /* ASSERT: l > 0 */
261     dwarfstring_append_length(data,zeros,l);
262     return res;
263 }
264 
265 
266 int dwarfstring_append_printf_s(dwarfstring *data,
267     char *format,char *s)
268 {
269     size_t stringlen = strlen(s);
270     size_t next = 0;
271     long val = 0;
272     char *endptr = 0;
273     const char *numptr = 0;
274     /* was %[-]fixedlen.  Zero means no len provided. */
275     size_t fixedlen = 0;
276     /* was %-, nonzero means left-justify */
277     long leftjustify = 0;
278     size_t prefixlen = 0;
279     int res = 0;
280 
281     while (format[next] && format[next] != '%') {
282         ++next;
283         ++prefixlen;
284     }
285     if (prefixlen) {
286         dwarfstring_append_length(data,format,prefixlen);
287     }
288     if (!format[next]) {
289         return TRUE;
290     }
291     next++;
292     if (format[next] == '-') {
293         leftjustify++;
294         next++;
295     }
296     numptr = format+next;
297     val = strtol(numptr,&endptr,10);
298     if ( endptr != numptr) {
299         fixedlen = val;
300     }
301     next = (endptr - format);
302     if (format[next] != 's') {
303         return FALSE;
304     }
305     next++;
306 
307     if (fixedlen && (stringlen >= fixedlen)) {
308         /*  Ignore  leftjustify (if any) and the stringlen
309             as the actual string overrides those. */
310         leftjustify = 0;
311     }
312     if (leftjustify) {
313 
314         dwarfstring_append_length(data,s,stringlen);
315         if(fixedlen) {
316             size_t trailingspaces = fixedlen - stringlen;
317 
318             _dwarfstring_append_spaces(data,trailingspaces);
319         }
320     } else {
321         if (fixedlen && fixedlen < stringlen) {
322             /*  This lets us have fixedlen < stringlen by
323                 taking all the chars from s*/
324             dwarfstring_append_length(data,s,stringlen);
325         } else {
326             if(fixedlen) {
327                 size_t leadingspaces = fixedlen - stringlen;
328                 size_t k = 0;
329 
330                 for ( ; k < leadingspaces; ++k) {
331                     dwarfstring_append_length(data," ",1);
332                 }
333             }
334             dwarfstring_append_length(data,s,stringlen);
335         }
336     }
337     if (!format[next]) {
338         return TRUE;
339     }
340     {
341         char * startpt = format+next;
342         size_t suffixlen = strlen(startpt);
343 
344         res = dwarfstring_append_length(data,startpt,suffixlen);
345     }
346     return res;
347 }
348 
349 static char v32m[] = {"-2147483648"};
350 static char v64m[] = {"-9223372036854775808"};
351 static char dtable[10] = {
352 '0','1','2','3','4','5','6','7','8','9'
353 };
354 static char xtable[16] = {
355 '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'
356 };
357 static char Xtable[16] = {
358 '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'
359 };
360 
361 /*  We deal with formats like:
362     %d   %5d %05d %+d %+5d %-5d (and ld and lld too). */
363 int dwarfstring_append_printf_i(dwarfstring *data,
364     char *format,
365     dwarfstring_i v)
366 {
367     int res = TRUE;
368     size_t next = 0;
369     long val = 0;
370     char *endptr = 0;
371     const char *numptr = 0;
372     size_t fixedlen = 0;
373     int leadingzero = 0;
374     int minuscount = 0; /*left justify */
375     int pluscount = 0;
376     int lcount = 0;
377     int ucount = 0;
378     int dcount = 0;
379     int xcount = 0;
380     int Xcount = 0;
381     char *ctable = dtable;
382     size_t prefixlen = 0;
383     int done = 0;
384 
385     while (format[next] && format[next] != '%') {
386         ++next;
387         ++prefixlen;
388     }
389     dwarfstring_append_length(data,format,prefixlen);
390     if (format[next] != '%') {
391         /*   No % operator found, we are done */
392         return TRUE;
393     }
394     next++;
395     if (format[next] == '-') {
396         minuscount++;
397         return FALSE;
398     }
399     if (format[next] == '+') {
400         pluscount++;
401         next++;
402     }
403     if (format[next] == '0') {
404         leadingzero = 1;
405         next++;
406     }
407     numptr = format+next;
408     val = strtol(numptr,&endptr,10);
409     if ( endptr != numptr) {
410         fixedlen = val;
411     }
412     next = (endptr - format);
413     /*  Following is lx lu or u or llx llu , we take
414         all this to mean 64 bits, */
415 #if defined(_WIN32) && defined(HAVE_NONSTANDARD_PRINTF_64_FORMAT)
416     if (format[next] == 'I') {
417         /*lcount++;*/
418         next++;
419     }
420     if (format[next] == '6') {
421         /*lcount++;*/
422         next++;
423     }
424     if (format[next] == '4') {
425         /*lcount++;*/
426         next++;
427     }
428 #endif /* HAVE_NONSTANDARD_PRINTF_64_FORMAT */
429     if (format[next] == 'l') {
430         lcount++;
431         next++;
432     }
433     if (format[next] == 'l') {
434         lcount++;
435         next++;
436     }
437     if (format[next] == 'u') {
438         ucount++;
439         next++;
440     }
441     if (format[next] == 'd') {
442         dcount++;
443         next++;
444     }
445     if (format[next] == 'x') {
446         xcount++;
447         next++;
448     }
449     if (format[next] == 'X') {
450         Xcount++;
451         next++;
452     }
453     if (format[next] == 's') {
454         /* ESBERR("ESBERR_pct_scount_in_i"); */
455         return FALSE;
456     }
457     if (xcount || Xcount) {
458         /*  Use the printf_u for %x and the like
459             just copying the entire format makes
460             it easier for coders to understand
461             nothing much was done */
462         dwarfstring_append(data,format+prefixlen);
463         return FALSE;
464     }
465     if (!dcount || (lcount >2) ||
466         (Xcount+xcount+dcount+ucount) > 1) {
467         /* error */
468         /* ESBERR("ESBERR_xcount_etc_i"); */
469         return FALSE;
470     }
471     if (pluscount && minuscount) {
472         /* We don't allow  format +- */
473         return FALSE;
474     }
475     {
476         char digbuf[36];
477         char *digptr = digbuf+sizeof(digbuf) -1;
478         size_t digcharlen = 0;
479         dwarfstring_i remaining = v;
480         int vissigned = 0;
481         dwarfstring_i divisor = 10;
482 
483         *digptr = 0;
484         --digptr;
485         if (v < 0) {
486             vissigned = 1;
487             /*  This test is for twos-complement
488                 machines and would be better done via
489                 configure with a compile-time check
490                 so we do not need a size test at runtime. */
491             if (sizeof(v) == 8) {
492                 dwarfstring_u vm = 0x7fffffffffffffffULL;
493                 if (vm == (dwarfstring_u)~v) {
494                     memcpy(digbuf,v64m,sizeof(v64m));
495                     digcharlen = sizeof(v64m)-1;
496                     digptr = digbuf;
497                     done = 1;
498                 } else {
499                     remaining = -v;
500                 }
501             } else if (sizeof(v) == 4) {
502                 dwarfstring_u vm = 0x7fffffffL;
503                 if (vm == (dwarfstring_u)~v) {
504                     memcpy(digbuf,v32m,sizeof(v32m));
505                     digcharlen = sizeof(v32m)-1;
506                     digptr = digbuf;
507                     done = 1;
508                 } else {
509                     remaining = -v;
510                 }
511             }else {
512                 /* ESBERR("ESBERR_sizeof_v_i"); */
513                 /* error */
514                 return FALSE;
515             }
516         }
517         if(!done) {
518             for ( ;; ) {
519                 dwarfstring_u dig = 0;
520 
521                 dig = remaining % divisor;
522                 remaining /= divisor;
523                 *digptr = ctable[dig];
524                 digcharlen++;
525                 if (!remaining) {
526                     break;
527                 }
528                 --digptr;
529             }
530             if (vissigned) { /* could check minuscount instead */
531                 --digptr;
532                 digcharlen++;
533                 *digptr = '-';
534             } else if (pluscount) {
535                 --digptr;
536                 digcharlen++;
537                 *digptr = '+';
538             }
539         }
540         if (fixedlen > 0) {
541             if (fixedlen <= digcharlen) {
542                 dwarfstring_append_length(data,digptr,digcharlen);
543             } else {
544                 size_t prefixcount = fixedlen - digcharlen;
545                 if (!leadingzero) {
546                     _dwarfstring_append_spaces(data,prefixcount);
547                     dwarfstring_append_length(data,digptr,digcharlen);
548                 } else {
549                     if (*digptr == '-') {
550                         dwarfstring_append_length(data,"-",1);
551                         _dwarfstring_append_zeros(data,prefixcount);
552                         digptr++;
553                         dwarfstring_append_length(data,digptr,
554                             digcharlen-1);
555                     } else if (*digptr == '+') {
556                         dwarfstring_append_length(data,"+",1);
557                         _dwarfstring_append_zeros(data,prefixcount);
558                         digptr++;
559                         dwarfstring_append_length(data,digptr,
560                             digcharlen-1);
561                     } else {
562                         _dwarfstring_append_zeros(data,prefixcount);
563                         dwarfstring_append_length(data,digptr,
564                             digcharlen);
565                     }
566                 }
567             }
568         } else {
569             res = dwarfstring_append_length(data,digptr,digcharlen);
570         }
571     }
572     if (format[next]) {
573         size_t trailinglen = strlen(format+next);
574         res = dwarfstring_append_length(data,format+next,trailinglen);
575     }
576     return res;
577 }
578 
579 #if 0
580 /*  Counts hex chars. divide by two to get bytes from input
581     integer. */
582 static unsigned
583 trimleadingzeros(char *ptr,unsigned digits,unsigned keepcount)
584 {
585     char *cp = ptr;
586     unsigned leadzeroscount = 0;
587     unsigned trimoff = 0;
588 
589     for(; *cp; ++cp) {
590         if (*cp == '0') {
591             leadzeroscount++;
592             continue;
593         }
594     }
595     trimoff = keepcount - digits;
596     if (trimoff&1) {
597         trimoff--;
598     }
599     return trimoff;
600 }
601 #endif /* 0 */
602 
603 /* With gcc version 5.4.0 20160609  a version using
604    const char *formatp instead of format[next]
605    and deleting the 'next' variable
606    is a few hundredths of a second slower, repeatably.
607 
608    We deal with formats like:
609    %u   %5u %05u (and ld and lld too).
610    %x   %5x %05x (and ld and lld too).  */
611 
612 int dwarfstring_append_printf_u(dwarfstring *data,
613     char *format,
614     dwarfstring_u v)
615 {
616 
617     size_t next = 0;
618     long val = 0;
619     char *endptr = 0;
620     const char *numptr = 0;
621     size_t fixedlen = 0;
622     int leadingzero = 0;
623     int lcount = 0;
624     int ucount = 0;
625     int dcount = 0;
626     int xcount = 0;
627     int Xcount = 0;
628     char *ctable = 0;
629     size_t divisor = 0;
630     size_t prefixlen = 0;
631 
632     while (format[next] && format[next] != '%') {
633         ++next;
634         ++prefixlen;
635     }
636     dwarfstring_append_length(data,format,prefixlen);
637     if (format[next] != '%') {
638         /*   No % operator found, we are done */
639         return TRUE;
640     }
641     next++;
642     if (format[next] == '-') {
643         /*ESBERR("ESBERR_printf_u - format not supported"); */
644         next++;
645     }
646     if (format[next] == '0') {
647         leadingzero = 1;
648         next++;
649     }
650     numptr = format+next;
651     val = strtol(numptr,&endptr,10);
652     if ( endptr != numptr) {
653         fixedlen = val;
654     }
655     next = (endptr - format);
656     /*  Following is lx lu or u or llx llu , we take
657         all this to mean 64 bits, */
658 #if defined(_WIN32) && defined(HAVE_NONSTANDARD_PRINTF_64_FORMAT)
659     if (format[next] == 'I') {
660         /*lcount++;*/
661         next++;
662     }
663     if (format[next] == '6') {
664         /*lcount++;*/
665         next++;
666     }
667     if (format[next] == '4') {
668         /*lcount++;*/
669         next++;
670     }
671 #endif /* HAVE_NONSTANDARD_PRINTF_64_FORMAT */
672     if (format[next] == 'l') {
673         lcount++;
674         next++;
675     }
676     if (format[next] == 'l') {
677         lcount++;
678         next++;
679     }
680     if (format[next] == 'u') {
681         ucount++;
682         next++;
683     }
684     if (format[next] == 'd') {
685         dcount++;
686         next++;
687     }
688     if (format[next] == 'x') {
689         xcount++;
690         next++;
691     }
692     if (format[next] == 'X') {
693         Xcount++;
694         next++;
695     }
696     if (format[next] == 's') {
697         /* ESBERR("ESBERR_pct_scount_in_u"); */
698         return FALSE;
699     }
700 
701     if ( (Xcount +xcount+dcount+ucount) > 1) {
702         /* ESBERR("ESBERR_pct_xcount_etc_u"); */
703         return FALSE;
704     }
705     if (lcount > 2) {
706         /* ESBERR("ESBERR_pct_lcount_error_u"); */
707         /* error */
708         return FALSE;
709     }
710     if (dcount > 0) {
711         /*ESBERR("ESBERR_pct_dcount_error_u");*/
712         /* error */
713         return FALSE;
714     }
715     if (ucount) {
716         divisor = 10;
717         ctable = dtable;
718     } else {
719         divisor = 16;
720         if (xcount) {
721             ctable = xtable;
722         } else {
723             ctable = Xtable;
724         }
725     }
726     {
727         char digbuf[36];
728         char *digptr = 0;
729         unsigned digcharlen = 0;
730         dwarfstring_u remaining = v;
731 
732         if (divisor == 16) {
733             digptr = digbuf+sizeof(digbuf) -1;
734             for ( ;; ) {
735                 dwarfstring_u dig;
736                 dig = remaining & 0xf;
737                 remaining = remaining >> 4;
738                 *digptr = ctable[dig];
739                 ++digcharlen;
740                 if (!remaining) {
741                     break;
742                 }
743                 --digptr;
744             }
745         } else {
746             digptr = digbuf+sizeof(digbuf) -1;
747             *digptr = 0;
748             --digptr;
749             for ( ;; ) {
750                 dwarfstring_u dig;
751                 dig = remaining % divisor;
752                 remaining /= divisor;
753                 *digptr = ctable[dig];
754                 ++digcharlen;
755                 if (!remaining) {
756                     break;
757                 }
758                 --digptr;
759             }
760         }
761         if (fixedlen <= digcharlen) {
762             dwarfstring_append_length(data,digptr,digcharlen);
763         } else {
764             if (!leadingzero) {
765                 size_t justcount = fixedlen - digcharlen;
766                 _dwarfstring_append_spaces(data,justcount);
767                 dwarfstring_append_length(data,digptr,digcharlen);
768             } else {
769                 size_t prefixcount = fixedlen - digcharlen;
770                 _dwarfstring_append_zeros(data,prefixcount);
771                 dwarfstring_append_length(data,digptr,digcharlen);
772             }
773         }
774     }
775     if (format[next]) {
776         size_t trailinglen = strlen(format+next);
777         dwarfstring_append_length(data,format+next,trailinglen);
778     }
779     return FALSE;
780 }
781