1// This file is part of SmallBASIC
2//
3// strings
4//
5// This program is distributed under the terms of the GPL v2.0 or later
6// Download the GNU Public License (GPL) from www.gnu.org
7//
8// Copyright(C) 2000 Nicholas Christopoulos
9
10#include "common/str.h"
11#include "common/fmt.h"
12
13#define BUF_SIZE 256
14
15/**
16 * removes spaces and returns a new string
17 */
18char *trimdup(const char *str) {
19 char *buf;
20 char *p;
21
22 buf = malloc(strlen(str) + 1);
23 strcpy(buf, str);
24
25 if (*str == '\0') {
26 return buf;
27 }
28
29 p = (char *) str;
30 while (is_wspace(*p)) {
31 p++;
32 }
33 strcpy(buf, p);
34
35 if (*p != '\0') {
36 p = buf;
37 while (*p) {
38 p++;
39 }
40 p--;
41 while (p > buf && is_wspace(*p)) {
42 p--;
43 }
44 p++;
45 *p = '\0';
46 }
47 return buf;
48}
49
50/**
51 * whether the string contains any whitespace characters
52 */
53int has_wspace(const char *s) {
54 int result = 0;
55 int i;
56 for (i = 0; s != NULL && s[i] && !result; i++) {
57 result |= is_wspace(s[i]);
58 }
59 return result;
60}
61
62/**
63 * removes spaces
64 */
65void str_alltrim(char *str) {
66 if (str && has_wspace(str)) {
67 char *buf = trimdup(str);
68 strcpy(str, buf);
69 free(buf);
70 }
71}
72
73/**
74 * caseless string compare
75 */
76int strcaselessn(const char *s1, int s1n, const char *s2, int s2n) {
77 int result, i;
78 for (i = 0;; i++) {
79 if (i == s1n || i == s2n) {
80 result = s1n < s2n ? -1 : s1n > s2n ? 1 : 0;
81 break;
82 }
83 char c1 = s1[i];
84 char c2 = s2[i];
85 if (c1 != c2) {
86 c1 = to_lower(c1);
87 c2 = to_lower(c2);
88 if (c1 != c2) {
89 result = c1 < c2 ? -1 : 1;
90 break;
91 }
92 }
93 }
94 return result;
95}
96
97/**
98 * transdup
99 */
100char *transdup(const char *src, const char *what, const char *with, int ignore_case) {
101 int lwhat = strlen(what);
102 int lwith = strlen(with);
103 int size = BUF_SIZE;
104 char *p = (char *)src;
105 char *dest = malloc(size);
106 char *d = dest;
107
108 *d = '\0';
109
110 while (*p) {
111 int eq;
112 if (ignore_case) {
113 eq = strncasecmp(p, what, lwhat);
114 }
115 else {
116 eq = strncmp(p, what, lwhat);
117 }
118 if (eq == 0) {
119 if ((d - dest) + lwith >= size - 1) {
120 int len = d - dest;
121 size += BUF_SIZE;
122 dest = realloc(dest, size);
123 d = dest + len;
124 }
125 memcpy(d, with, lwith);
126 d += lwith;
127 p += (lwhat - 1);
128 } else {
129 if ((d - dest) + 1 >= size - 1) {
130 int len = d - dest;
131 size += BUF_SIZE;
132 dest = realloc(dest, size);
133 d = dest + len;
134 }
135 *d = *p;
136 d++;
137 }
138 p++;
139 }
140
141 *d = '\0';
142 return dest;
143}
144
145/**
146 * strstr with support for quotes
147 */
148char *q_strstr(const char *s1, const char *s2, const char *pairs) {
149 char *z;
150 int l2;
151 int wait_q, open_q, level_q;
152
153 char *p = (char *) s1;
154 l2 = strlen(s2);
155 wait_q = open_q = level_q = 0;
156
157 while (*p) {
158 if (*p == wait_q) { // i am waiting that. level down
159 level_q--;
160 if (level_q <= 0) { // level = 0
161 level_q = 0;
162 wait_q = 0;
163 }
164 } else if ((z = strchr(pairs, *p)) != NULL) { // character is a
165 // delimiter;
166 // level up
167 open_q = ((z - pairs) + 1) % 2; // true, if its a 'begin'
168 // delimiter
169
170 if (wait_q && open_q) {
171 // open_q of our pair?
172 if (*(z + 1) == wait_q) {
173 // increase level
174 level_q++;
175 }
176 } else if (wait_q) {
177 // do nothing, I am waitting something
178 // else
179 } else {
180 // its a new section
181 if (open_q) {
182 level_q++; // level = 1
183 wait_q = *(z + 1); // what to wait for
184 }
185 }
186 } else if (wait_q == 0) { // it is a regular character
187 if (strncmp(p, s2, l2) == 0) {
188 return p;
189 }
190 }
191 // next
192 p++;
193 }
194
195 return NULL;
196}
197
198/**
199 * is_alpha
200 */
201int is_alpha(int ch) {
202 if (ch == 0) {
203 return 0;
204 }
205 if ((ch > 64 && ch < 91) || (ch > 96 && ch < 123)) {
206 return -1;
207 }
208 return (ch & 0x80); // +foreign
209}
210
211/**
212 * returns true if the ch is alphanumeric
213 */
214int is_alnum(int ch) {
215 if (ch == 0) {
216 return 0;
217 }
218 return (is_alpha(ch) || is_digit(ch));
219}
220
221/**
222 * returns true if the ch is an 'empty' character
223 */
224int is_space(int ch) {
225 return (ch == ' ' || ch == '\t' || ch == '\f' || ch == '\n' || ch == '\r' || ch == '\v') ? -1 : 0;
226}
227
228/**
229 * returns true if 'text' contains digits only
230 */
231int is_all_digits(const char *text) {
232 const char *p = text;
233
234 if (p == NULL) {
235 return 0;
236 }
237 if (*p == '\0') {
238 return 0;
239 }
240 while (*p) {
241 if (!is_digit(*p)) {
242 return 0;
243 }
244 p++;
245 }
246 return 1;
247}
248
249/**
250 * converts the 'str' string to uppercase
251 */
252char *strupper(char *str) {
253 char *p = str;
254
255 if (p == NULL) {
256 return 0;
257 }
258 while (*p) {
259 *p = to_upper(*p);
260 p++;
261 }
262 return str;
263}
264
265/**
266 * converts the 'str' string to lowercase
267 */
268char *strlower(char *str) {
269 char *p = str;
270
271 if (p == NULL) {
272 return 0;
273 }
274 while (*p) {
275 *p = to_lower(*p);
276 p++;
277 }
278 return str;
279}
280
281/**
282 * Returns the number of a string (constant numeric expression)
283 *
284 * type <=0 = error
285 * 1 = int32_t
286 * 2 = double
287 *
288 * Warning: octals are different from C (QB compatibility: 009 = 9)
289 */
290char *get_numexpr(char *text, char *dest, int *type, var_int_t *lv, var_num_t *dv) {
291 char *p = text;
292 char *d = dest;
293 char *epos = NULL;
294 byte base = 10;
295 byte dpc = 0, e_fmt = 0, eop = '+';
296 int sign = 1;
297 var_num_t power = 1.0;
298 var_num_t num;
299
300 *type = 0;
301 *lv = 0;
302 *dv = 0.0;
303
304 if (p == NULL) {
305 *dest = '\0';
306 return NULL;
307 }
308 // spaces
309 while (is_space(*p)) {
310 p++;
311 }
312 // sign
313 if ((*p == '-' || *p == '+') && strchr("0123456789.", *(p + 1)) &&
314 *(p + 1) != '\0'){if (*p == '-') {
315 sign = -1;
316 }
317 // don't copy it
318 p++;
319 }
320
321 //
322 // resolve the base (hex, octal and binary)
323 //
324 if ((*p == '&') || (*p == '0' && (*(p + 1) != '\0' && strchr("HXBO", to_upper(*(p + 1))) != NULL))) {
325 p++;
326 switch (*p) {
327 case 'H':
328 case 'h':
329 case 'X':
330 case 'x':
331 base = 16;
332 break;
333 case 'O':
334 case 'o':
335 base = 8;
336 break;
337 case 'B':
338 case 'b':
339 base = 2;
340 break;
341 default:
342 // Unknown base
343 *type = -1;
344 return p;
345 }
346
347 p++;
348 }
349 //
350 // copy parts of number
351 //
352 if (base == 16) {
353 // copy hex
354 while (is_hexdigit(*p)) {
355 *d = to_upper(*p);
356 d++;
357 p++;
358 }
359 } else if (base != 10) {
360 // copy octal | bin
361 while (is_digit(*p)) {
362 *d++ = *p++;
363 }
364 } else if (is_digit(*p) || *p == '.') {
365 // copy number (first part)
366 while (is_digit(*p) || *p == '.') {
367 if (*p == '.') {
368 dpc++;
369 if (dpc > 1) {
370 // DP ERROR
371 *type = -2;
372 break;
373 }
374 }
375 *d++ = *p++;
376 }
377
378 // check second part
379 if ((*p == 'E' || *p == 'e') && (*type == 0)) {
380 epos = d;
381 // E
382 *d++ = *p++;
383
384 if (*p == '+' || *p == '-' || is_digit(*p) || *p == '.') {
385 dpc = 0;
386
387 // copy second part (power)
388 if (*p == '+' || *p == '-') {
389 *d++ = *p++;
390 if (strchr("+-*/\\^", *p) != 0) {
391 // stupid E format
392 // (1E--9 || 1E++9)
393 e_fmt = 1;
394 eop = *p;
395 *d++ = *p++;
396 if (*p == '+' || *p == '-') {
397 *d++ = *p++;
398 }
399 }
400 }
401 // power
402 while (is_digit(*p) || *p == '.') {
403 if (*p == '.') {
404 dpc++;
405 if (dpc > 1) {
406 // DP ERROR (second part)
407 *type = -4;
408 break;
409 }
410 }
411 *d++ = *p++;
412 // after E
413 }
414 }
415 else {
416 // E+- ERROR
417 *type = -3;
418 }
419 }
420 } else {
421 // NOT A NUMBER
422 *type = -9;
423 }
424 *d = '\0';
425
426 //
427 // finaly, calculate the number
428 //
429 if (*type == 0) {
430 switch (base) {
431 case 10:
432 if (dpc || (epos != NULL) || (strlen(dest) > 8)) {
433 // double
434 *type = 2;
435 if (epos) {
436 if (e_fmt) {
437 int r_type = 1;
438
439 *epos = '\0';
440 num = sb_strtof(dest) * ((double) sign);
441 // restore E
442 *epos = 'E';
443
444 power = sb_strtof(epos + 3);
445
446 if (r_type > 0) {
447 switch (eop) {
448 case '+':
449 *dv = num + power;
450 break;
451 case '-':
452 *dv = num - power;
453 break;
454 case '*':
455 *dv = num * power;
456 break;
457 case '/':
458 if (ABS(power) != 0.0) {
459 *dv = num / power;
460 } else {
461 *dv = 0;
462 }
463 break;
464 case '\\':
465 if ((long) power != 0) {
466 *type = 1;
467 *lv = num / (long) power;
468 } else {
469 *type = 1;
470 *lv = 0;
471 }
472 break;
473 case '^':
474 *dv = pow(num, power);
475 break;
476 }
477 }
478 } else {
479 *epos = '\0';
480 power = pow(10, sb_strtof(epos + 1));
481 *dv = sb_strtof(dest) * ((double) sign) * power;
482 *epos = 'E';
483 }
484 } else {
485 *dv = sb_strtof(dest) * ((double) sign);
486 }
487 } else {
488 // dpc = 0 && epos = 0
489 *type = 1;
490 *lv = xstrtol(dest) * sign;
491 }
492 break;
493 case 16:
494 *type = 1;
495 *lv = hextol(dest);
496 break;
497 case 8:
498 *type = 1;
499 *lv = octtol(dest);
500 break;
501 case 2:
502 *type = 1;
503 *lv = bintol(dest);
504 break;
505 }
506 }
507 if (is_alpha(*p)) {
508 // its not a number
509 *type = -9;
510 }
511 while (is_space(*p)) {
512 p++;
513 }
514 return p;
515}
516
517/**
518 * numexpr_sb_strtof
519 */
520var_num_t numexpr_sb_strtof(char *source) {
521 char buf[BUF_SIZE];
522 int type;
523 var_int_t lv;
524 var_num_t dv;
525
526 get_numexpr(source, buf, &type, &lv, &dv);
527
528 if (type == 1) {
529 return (var_num_t) lv;
530 } else if (type == 2) {
531 return dv;
532 }
533 return 0.0;
534}
535
536/**
537 * numexpr_strtol
538 */
539var_int_t numexpr_strtol(char *source) {
540 char buf[BUF_SIZE], *np;
541 int type;
542 var_int_t lv;
543 var_num_t dv;
544
545 np = get_numexpr(source, buf, &type, &lv, &dv);
546
547 if (type == 1 && *np == '\0') {
548 return lv;
549 } else if (type == 2 && *np == '\0') {
550 return (var_int_t) dv;
551 }
552 return 0;
553}
554
555/**
556 * convertion: binary to decimal
557 */
558long bintol(const char *str) {
559 long r = 0;
560 char *p = (char *) str;
561
562 if (p == NULL) {
563 return 0;
564 }
565 while (*p) {
566 if (*p == 48 || *p == 49) {
567 // 01
568 r = (r << 1) + ((*p) - 48);
569 }
570 p++;
571 }
572 return r;
573}
574
575/**
576 * convertion: octal to decimal
577 */
578long octtol(const char *str) {
579 long r = 0;
580 char *p = (char *) str;
581
582 if (p == NULL) {
583 return 0;
584 }
585 while (*p) {
586 if (*p >= 48 && *p <= 55) {
587 // 01234567
588 r = (r << 3) + ((*p) - 48);
589 }
590 p++;
591 }
592 return r;
593}
594
595/**
596 * convertion: hexadecimal to decimal
597 */
598long hextol(const char *str) {
599 long r = 0;
600 char *p = (char *) str;
601
602 if (p == NULL) {
603 return 0;
604 }
605 while (*p) {
606 if (is_digit(*p)) {
607 // 0123456789
608 r = (r << 4) + ((*p) - 48);
609 } else if (*p >= 65 && *p <= 70) {
610 // ABCDEF
611 r = (r << 4) + ((*p) - 55);
612 } else if (*p >= 97 && *p <= 102) {
613 // abcdef
614 r = (r << 4) + ((*p) - 87);
615 }
616 p++;
617 }
618 return r;
619}
620
621/**
622 * string to double
623 */
624var_num_t sb_strtof(const char *str) {
625 char *p = (char *) str;
626 var_num_t r = 0.0;
627 int negate = 0;
628 int places = 0;
629
630 if (p != NULL) {
631 if (*p == '-') {
632 negate = 1;
633 p++;
634 } else if (*p == '+') {
635 p++;
636 }
637 int dot = 0;
638 while (*p) {
639 if (is_digit(*p)) {
640 r = r * 10.0f + (*p - '0');
641 if (dot) {
642 places++;
643 }
644 } else if (*p == '.') {
645 dot = 1;
646 } else if (*p == ' ') {
647 break;
648 } else {
649 r = 0;
650 break;
651 }
652 p++;
653 }
654 }
655 if (places) {
656 r /= pow(10, places);
657 }
658 return negate ? -r : r;
659}
660
661/**
662 * xstrtol
663 */
664long xstrtol(const char *str) {
665 if (str == NULL) {
666 return 0;
667 }
668 return atoi(str);
669}
670
671/**
672 * whether the string is a number
673 */
674int is_number(const char *str) {
675 char *p = (char *) str;
676 int dpc = 0, cnt = 0;
677
678 if (str == NULL) {
679 return 0;
680 }
681 if (*p == '+' || *p == '-') {
682 p++;
683 }
684 while (*p) {
685 if (strchr("0123456789.", *p) == NULL) {
686 return 0;
687 } else {
688 cnt++;
689 }
690 if (*p == '.') {
691 dpc++;
692 if (dpc > 1) {
693 return 0;
694 }
695 }
696 p++;
697 }
698 if (cnt) {
699 return 1;
700 }
701 return 0;
702}
703
704/**
705 * double to string
706 */
707char *ftostr(var_num_t num, char *dest) {
708 bestfta(num, dest);
709 return dest;
710}
711
712/**
713 * ltostr
714 */
715char *ltostr(var_int_t num, char *dest) {
716 sprintf(dest, VAR_INT_FMT, num);
717 return dest;
718}
719
720/**
721 * returns whether the character is whitespace
722 */
723int is_wspace(int c) {
724 return (c != 0 && (c == ' ' || c == '\t' || c == '\r' ||
725 c == '\n' || c == '\v' || c == '\f'));
726}
727
728/**
729 * squeeze (& strdup)
730 */
731char *sqzdup(const char *source) {
732 char *rp, *p, *d;
733 int lc = 0;
734
735 rp = malloc(strlen(source) + 1);
736 p = (char *) source;
737 d = rp;
738
739 while (*p != '\0' && is_wspace(*p))
740 p++;
741
742 while (*p) {
743 if (is_wspace(*p)) {
744 if (!lc) {
745 lc = 1;
746 if (p > source) {
747 if (is_alpha(*(p - 1)) || is_digit(*(p - 1)))
748 *d++ = ' ';
749 else {
750 char *nc;
751
752 nc = p;
753 while (*nc != '\0' && is_wspace(*nc))
754 nc++;
755 if (is_alpha(*nc) || is_digit(*nc)
756 )
757 *d++ = ' ';
758 }
759 }
760 }
761 } else {
762 (lc = 0, *d++ = *p);
763 }
764 p++;
765 }
766
767 *d = '\0';
768 if (d > rp) {
769 if (is_wspace(*(d - 1))) {
770 *(d - 1) = '\0';
771 }
772 }
773
774 return rp;
775}
776
777/**
778 * enclose, returns a newly created string
779 */
780char *encldup(const char *source, const char *pairs) {
781 int l = strlen(source);
782 char *rp = malloc(l + 3);
783
784 memcpy(rp + 1, source, l);
785 *(rp) = pairs[0];
786 if (pairs[1]) {
787 *(rp + l + 1) = pairs[1];
788 } else {
789 *(rp + l + 1) = pairs[0];
790 }
791 *(rp + l + 2) = '\0';
792 return rp;
793}
794
795/**
796 * disclose, returns a newly created string
797 */
798char *discldup(const char *source, const char *pairs, const char *ignpairs) {
799 char *np, *z;
800 int wait_p = 0, level_p = 0;
801 int wait_q = 0, level_q = 0;
802 int record = 0;
803
804 char *rp = strdup(source);
805 char *r = rp;
806 char *p = (char *) source;
807
808 while (*p) {
809 // ignore pairs
810 if (*p == wait_q) {
811 // ignore pair - level down
812 level_q--;
813 if (level_q <= 0) {
814 level_q = 0;
815 wait_q = 0;
816 }
817 } else if ((z = strchr(ignpairs, *p)) != NULL) {
818 int open_q = ((z - ignpairs) + 1) % 2;
819
820 if (wait_q && open_q) {
821 if (*(z + 1) == wait_q) {
822 // open_q of our pair?
823 level_q++;
824 }
825 } else if (wait_q) {
826 // do nothing, I am waitting something
827 } else {
828 // new pair
829 if (open_q) {
830 level_q++;
831 wait_q = *(z + 1);
832 }
833 }
834 }
835 // primary pairs
836 else if (*p == wait_p && wait_q == 0) { // primary pair - level
837 // down
838 level_p--;
839 if (level_p <= 0) {
840 // store and exit
841 record = 0;
842 break;
843 }
844 } else if ((z = strchr(pairs, *p)) != NULL && wait_q == 0) {
845 int open_p = ((z - pairs) + 1) % 2;
846
847 if (wait_p && open_p) {
848 if (*(z + 1) == wait_p) {
849 // open_q of our pair?
850 level_p++;
851 }
852 } else if (wait_p) {
853 // do nothing, I am waitting something
854 } else {
855 // new pair
856 if (open_p) {
857 level_p++;
858 wait_p = *(z + 1);
859 record = 1;
860 }
861 }
862 }
863 // next
864 if (record == 1) {
865 // ignore the first
866 record++;
867 } else if (record == 2) {
868 *r++ = *p;
869 }
870 p++;
871 }
872
873 *r = '\0';
874 // actually, resize down
875 np = strdup(rp);
876 free(rp);
877
878 return np;
879}
880
881/**
882 * C-Style control codes
883 */
884char *cstrdup(const char *source) {
885 char *buf, *p, *d;
886
887 buf = malloc(strlen(source) + 1);
888 p = (char *) source;
889 d = buf;
890 while (*p) {
891 if (*p == '\\') {
892 p++;
893 switch (*p) {
894 case 'e':
895 *d++ = '\033';
896 break;
897 case 'v':
898 *d++ = '\v';
899 break;
900 case 't':
901 *d++ = '\t';
902 break;
903 case 'r':
904 *d++ = '\r';
905 break;
906 case 'n':
907 *d++ = '\n';
908 break;
909 case 'b':
910 *d++ = '\b';
911 break;
912 case '\'':
913 *d++ = '\'';
914 break;
915 case '\"':
916 *d++ = '\"';
917 break;
918 case 'a':
919 *d++ = '\a';
920 break;
921 case 'f':
922 *d++ = '\f';
923 break;
924 case '\\':
925 *d++ = '\\';
926 break;
927 case 'x': // hex
928 if (is_hexdigit(*(p + 1)) && is_hexdigit(*(p + 2))) {
929 int c = 0;
930
931 p++;
932 if (is_digit(*p))
933 c |= ((*p - '0') << 4);
934 else
935 c |= (((to_upper(*p) - 'A') + 10) << 4);
936 p++;
937 if (is_digit(*p))
938 c |= *p - '0';
939 else
940 c |= (to_upper(*p) - 'A') + 10;
941
942 *d++ = c;
943 } else
944 *d++ = '\0';
945 break;
946 case '0': // oct
947 case '1':
948 case '2':
949 case '3':
950 case '4':
951 case '5':
952 case '6':
953 case '7':
954 if (is_octdigit(*(p + 1)) && is_octdigit(*(p + 2))) {
955 int c = 0;
956
957 c |= ((*p - '0') << 6);
958 p++;
959 c |= ((*p - '0') << 3);
960 p++;
961 c |= (*p - '0');
962
963 *d++ = c;
964 } else
965 *d++ = '\0';
966 break;
967
968 default:
969 *d++ = *p;
970 }
971
972 p++;
973 } else
974 *d++ = *p++;
975 }
976
977 *d = '\0';
978 return buf;
979}
980
981/**
982 * basic-string to c-string convertion
983 */
984char *bstrdup(const char *source) {
985 char *buf, *p, *d;
986
987 buf = malloc(strlen(source) * 4 + 1);
988 p = (char *) source;
989 d = buf;
990 while (*p) {
991 if (*p < 32 && *p >= 0) {
992 switch (*p) {
993 case '\033':
994 *d++ = '\\';
995 *d++ = 'e';
996 break;
997 case '\v':
998 *d++ = '\\';
999 *d++ = 'v';
1000 break;
1001 case '\t':
1002 *d++ = '\\';
1003 *d++ = 't';
1004 break;
1005 case '\r':
1006 *d++ = '\\';
1007 *d++ = 'r';
1008 break;
1009 case '\n':
1010 *d++ = '\\';
1011 *d++ = 'n';
1012 break;
1013 case '\b':
1014 *d++ = '\\';
1015 *d++ = 'b';
1016 break;
1017 case '\'':
1018 *d++ = '\\';
1019 *d++ = '\'';
1020 break;
1021 case '\"':
1022 *d++ = '\\';
1023 *d++ = '\"';
1024 break;
1025 case '\a':
1026 *d++ = '\\';
1027 *d++ = 'a';
1028 break;
1029 case '\f':
1030 *d++ = '\\';
1031 *d++ = 'f';
1032 break;
1033 case '\\':
1034 *d++ = '\\';
1035 *d++ = '\\';
1036 break;
1037 default:
1038 *d++ = '\\';
1039 *d++ = 'x';
1040 *d++ = to_hexdigit((*p & 0xF0) >> 4);
1041 *d++ = to_hexdigit(*p & 0xF);
1042 }
1043
1044 p++;
1045 } else
1046 *d++ = *p++;
1047 }
1048
1049 *d = '\0';
1050 return buf;
1051}
1052
1053/**
1054 * baseof
1055 */
1056const char *baseof(const char *source, int delim) {
1057 const char *p;
1058
1059 p = strrchr(source, delim);
1060 if (p) {
1061 return p + 1;
1062 }
1063 return source;
1064}
1065
1066/**
1067 * memory dump
1068 */
1069void hex_dump(const unsigned char *block, int size) {
1070#if defined(_UnixOS) || defined(_DOS)
1071 int i, j;
1072
1073 printf("\n---HexDump---\n\t");
1074 for (i = 0; i < size; i++) {
1075 printf("%02X ", block[i]);
1076 if (((i + 1) % 16) == 0 || (i == size - 1)) {
1077 printf(" %04x ", i);
1078 for (j = ((i - 15 <= 0) ? 0 : i - 15); j <= i; j++) {
1079 if (block[j] < 32) {
1080 printf(".");
1081 } else {
1082 printf("%c", block[j]);
1083 }
1084 }
1085 printf("\n\t");
1086 }
1087 }
1088
1089 printf("\n");
1090#endif
1091}
1092
1093void cstr_init(cstr *cs, int size) {
1094 cs->length = 0;
1095 cs->size = size < 1 ? 1 : size;
1096 cs->buf = malloc(size);
1097 cs->buf[0] = '\0';
1098}
1099
1100void cstr_append(cstr *cs, const char *str) {
1101 cstr_append_i(cs, str, strlen(str));
1102}
1103
1104void cstr_append_i(cstr *cs, const char *str, int len) {
1105 if (len > 0) {
1106 if (cs->size - cs->length < len + 1) {
1107 cs->size += len + 1;
1108 cs->buf = realloc(cs->buf, cs->size);
1109 }
1110 strlcat(cs->buf, str, cs->size);
1111 cs->length += len;
1112 cs->buf[cs->length] = '\0';
1113 }
1114}
1115