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 | */ |
18 | char *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 | */ |
53 | int 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 | */ |
65 | void 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 | */ |
76 | int 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 | */ |
100 | char *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 | */ |
148 | char *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 | */ |
201 | int 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 | */ |
214 | int 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 | */ |
224 | int 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 | */ |
231 | int 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 | */ |
252 | char *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 | */ |
268 | char *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 | */ |
290 | char *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 | */ |
520 | var_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 | */ |
539 | var_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 | */ |
558 | long 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 | */ |
578 | long 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 | */ |
598 | long 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 | */ |
624 | var_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 | */ |
664 | long 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 | */ |
674 | int 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 | */ |
707 | char *ftostr(var_num_t num, char *dest) { |
708 | bestfta(num, dest); |
709 | return dest; |
710 | } |
711 | |
712 | /** |
713 | * ltostr |
714 | */ |
715 | char *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 | */ |
723 | int 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 | */ |
731 | char *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 | */ |
780 | char *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 | */ |
798 | char *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 | */ |
884 | char *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 | */ |
984 | char *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 | */ |
1056 | const 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 | */ |
1069 | void 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 | |
1093 | void 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 | |
1100 | void cstr_append(cstr *cs, const char *str) { |
1101 | cstr_append_i(cs, str, strlen(str)); |
1102 | } |
1103 | |
1104 | void 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 | |