GCC Code Coverage Report


Directory: ../src/
File: /home/joels/Current/lispbm/src/extensions/string_extensions.c
Date: 2024-11-05 17:11:09
Exec Total Coverage
Lines: 386 422 91.5%
Functions: 21 21 100.0%
Branches: 217 254 85.4%

Line Branch Exec Source
1 /*
2 Copyright 2022, 2023, 2024 Joel Svensson svenssonjoel@yahoo.se
3 Copyright 2022, 2023 Benjamin Vedder
4 Copyright 2024 Rasmus Söderhielm rasmus.soderhielm@gmail.com
5
6 This program is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "extensions.h"
21 #include "lbm_memory.h"
22 #include "heap.h"
23 #include "fundamental.h"
24 #include "lbm_c_interop.h"
25 #include "eval_cps.h"
26 #include "print.h"
27
28 #include <ctype.h>
29
30 #ifndef MIN
31 #define MIN(a,b) (((a)<(b))?(a):(b))
32 #endif
33 #ifndef MAX
34 #define MAX(a,b) (((a)>(b))?(a):(b))
35 #endif
36
37 static char print_val_buffer[256];
38
39 static lbm_uint sym_left;
40 static lbm_uint sym_case_insensitive;
41
42
43 384352 static size_t strlen_max(const char *s, size_t maxlen) {
44 size_t i;
45
2/2
✓ Branch 0 taken 1219836 times.
✓ Branch 1 taken 28 times.
1219864 for (i = 0; i < maxlen; i ++) {
46
2/2
✓ Branch 0 taken 384324 times.
✓ Branch 1 taken 835512 times.
1219836 if (s[i] == 0) break;
47 }
48 384352 return i;
49 }
50
51 192580 static bool dec_str_size(lbm_value v, char **data, size_t *size) {
52 192580 bool result = false;
53
2/2
✓ Branch 1 taken 192524 times.
✓ Branch 2 taken 56 times.
192580 if (lbm_is_array_r(v)) {
54 192524 lbm_array_header_t *array = (lbm_array_header_t*) lbm_car(v);
55 192524 *data = (char*)array->data;
56 192524 *size = array->size;
57 192524 result = true;
58 }
59 192580 return result;
60 }
61
62 40154 static lbm_value ext_str_from_n(lbm_value *args, lbm_uint argn) {
63
4/4
✓ Branch 0 taken 196 times.
✓ Branch 1 taken 39958 times.
✓ Branch 2 taken 84 times.
✓ Branch 3 taken 112 times.
40154 if (argn != 1 && argn != 2) {
64 84 lbm_set_error_reason((char*)lbm_error_str_num_args);
65 84 return ENC_SYM_EERROR;
66 }
67
2/2
✓ Branch 1 taken 56 times.
✓ Branch 2 taken 40014 times.
40070 if (!lbm_is_number(args[0])) {
68 56 return ENC_SYM_TERROR;
69 }
70
71
4/4
✓ Branch 0 taken 112 times.
✓ Branch 1 taken 39902 times.
✓ Branch 3 taken 28 times.
✓ Branch 4 taken 84 times.
40014 if (argn == 2 && !lbm_is_array_r(args[1])) {
72 28 return ENC_SYM_TERROR;
73 }
74
75 39986 char *format = 0;
76
2/2
✓ Branch 0 taken 84 times.
✓ Branch 1 taken 39902 times.
39986 if (argn == 2) {
77 84 format = lbm_dec_str(args[1]);
78 }
79
80 char buffer[100];
81 39986 size_t len = 0;
82
83
2/2
✓ Branch 1 taken 224 times.
✓ Branch 2 taken 39762 times.
39986 switch (lbm_type_of(args[0])) {
84 224 case LBM_TYPE_DOUBLE: /* fall through */
85 case LBM_TYPE_FLOAT:
86
2/2
✓ Branch 0 taken 168 times.
✓ Branch 1 taken 56 times.
224 if (!format) {
87 168 format = "%g";
88 }
89 224 len = (size_t)snprintf(buffer, sizeof(buffer), format, lbm_dec_as_double(args[0]));
90 224 break;
91
92 39762 default:
93
2/2
✓ Branch 0 taken 39734 times.
✓ Branch 1 taken 28 times.
39762 if (!format) {
94 39734 format = "%d";
95 }
96 39762 len = (size_t)snprintf(buffer, sizeof(buffer), format, lbm_dec_as_i32(args[0]));
97 39762 break;
98 }
99
100 39986 len = MIN(len, sizeof(buffer));
101
102 lbm_value res;
103
2/2
✓ Branch 1 taken 39956 times.
✓ Branch 2 taken 30 times.
39986 if (lbm_create_array(&res, len + 1)) {
104 39956 lbm_array_header_t *arr = (lbm_array_header_t*)lbm_car(res);
105 39956 memcpy(arr->data, buffer, len);
106 39956 ((char*)(arr->data))[len] = '\0';
107 39956 return res;
108 } else {
109 30 return ENC_SYM_MERROR;
110 }
111 }
112
113 // signature: (str-join strings [delim]) -> str
114 96080 static lbm_value ext_str_join(lbm_value *args, lbm_uint argn) {
115 // This function does not check that the string arguments contain any
116 // terminating null bytes.
117
118
4/4
✓ Branch 0 taken 84348 times.
✓ Branch 1 taken 11732 times.
✓ Branch 2 taken 28 times.
✓ Branch 3 taken 84320 times.
96080 if (argn != 1 && argn != 2) {
119 28 lbm_set_error_reason((char *)lbm_error_str_num_args);
120 28 return ENC_SYM_EERROR;
121 }
122
123 96052 size_t str_len = 0;
124 96052 size_t str_count = 0;
125
2/2
✓ Branch 1 taken 28 times.
✓ Branch 2 taken 96024 times.
96052 if (!lbm_is_list(args[0])) {
126 28 lbm_set_error_reason((char *)lbm_error_str_incorrect_arg);
127 28 lbm_set_error_suspect(args[0]);
128 28 return ENC_SYM_TERROR;
129 }
130
2/2
✓ Branch 1 taken 192132 times.
✓ Branch 2 taken 95996 times.
288128 for (lbm_value current = args[0]; lbm_is_cons(current); current = lbm_cdr(current)) {
131 192132 lbm_value car_val = lbm_car(current);
132 192132 char *str = NULL;
133 192132 size_t arr_size = 0;
134
2/2
✓ Branch 1 taken 192104 times.
✓ Branch 2 taken 28 times.
192132 if (dec_str_size(car_val, &str, &arr_size)) {
135 192104 str_len += strlen_max(str, arr_size);
136 192104 str_count += 1;
137 } else {
138 28 lbm_set_error_reason((char *)lbm_error_str_incorrect_arg);
139 28 lbm_set_error_suspect(args[0]);
140 28 return ENC_SYM_TERROR;
141 }
142 }
143
144 95996 const char *delim = "";
145
2/2
✓ Branch 0 taken 84264 times.
✓ Branch 1 taken 11732 times.
95996 if (argn >= 2) {
146 84264 delim = lbm_dec_str(args[1]);
147
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 84236 times.
84264 if (!delim) {
148 28 lbm_set_error_reason((char *)lbm_error_str_incorrect_arg);
149 28 lbm_set_error_suspect(args[1]);
150 28 return ENC_SYM_TERROR;
151 }
152 }
153
154 95968 size_t delim_len = strlen(delim);
155
2/2
✓ Branch 0 taken 67880 times.
✓ Branch 1 taken 28088 times.
95968 if (str_count > 0) {
156 67880 str_len += (str_count - 1) * delim_len;
157 }
158
159 lbm_value result;
160
2/2
✓ Branch 1 taken 96 times.
✓ Branch 2 taken 95872 times.
95968 if (!lbm_create_array(&result, str_len + 1)) {
161 96 return ENC_SYM_MERROR;
162 }
163 95872 char *result_str = lbm_dec_str(result);
164
165 95872 size_t i = 0;
166 95872 size_t offset = 0;
167
2/2
✓ Branch 1 taken 191856 times.
✓ Branch 2 taken 95872 times.
287728 for (lbm_value current = args[0]; lbm_is_cons(current); current = lbm_cdr(current)) {
168 191856 lbm_value car_val = lbm_car(current);
169 // All arrays have been prechecked.
170 191856 lbm_array_header_t *array = (lbm_array_header_t*) lbm_car(car_val);
171 191856 char *str = (char*)array->data;
172 191856 size_t len = strlen_max(str, array->size);
173
174 191856 memcpy(result_str + offset, str, len);
175 191856 offset += len;
176
177
2/2
✓ Branch 0 taken 124040 times.
✓ Branch 1 taken 67816 times.
191856 if (i != str_count - 1) {
178 124040 memcpy(result_str + offset, delim, delim_len);
179 124040 offset += delim_len;
180 }
181 191856 i++;
182 }
183
184 95872 result_str[str_len] = '\0';
185
186 95872 return result;
187 }
188
189 448 static lbm_value ext_str_to_i(lbm_value *args, lbm_uint argn) {
190
4/4
✓ Branch 0 taken 56 times.
✓ Branch 1 taken 392 times.
✓ Branch 2 taken 28 times.
✓ Branch 3 taken 28 times.
448 if (argn != 1 && argn != 2) {
191 28 lbm_set_error_reason((char*)lbm_error_str_num_args);
192 28 return ENC_SYM_EERROR;
193 }
194
195 420 char *str = lbm_dec_str(args[0]);
196
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 392 times.
420 if (!str) {
197 28 return ENC_SYM_TERROR;
198 }
199
200 392 int base = 0;
201
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 364 times.
392 if (argn == 2) {
202
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 28 times.
28 if (!lbm_is_number(args[1])) {
203 return ENC_SYM_TERROR;
204 }
205
206 28 base = (int)lbm_dec_as_u32(args[1]);
207 }
208
209 392 return lbm_enc_i32((int32_t)strtol(str, NULL, base));
210 }
211
212 112 static lbm_value ext_str_to_f(lbm_value *args, lbm_uint argn) {
213
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 84 times.
112 if (argn != 1) {
214 28 lbm_set_error_reason((char*)lbm_error_str_num_args);
215 28 return ENC_SYM_EERROR;
216 }
217
218 84 char *str = lbm_dec_str(args[0]);
219
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 56 times.
84 if (!str) {
220 28 return ENC_SYM_TERROR;
221 }
222
223 56 return lbm_enc_float(strtof(str, NULL));
224 }
225
226 112 static lbm_value ext_str_part(lbm_value *args, lbm_uint argn) {
227
5/6
✓ Branch 0 taken 84 times.
✓ Branch 1 taken 28 times.
✓ Branch 2 taken 56 times.
✓ Branch 3 taken 28 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 84 times.
112 if ((argn != 2 && argn != 3) || !lbm_is_number(args[1])) {
228 28 lbm_set_error_reason((char*)lbm_error_str_num_args);
229 28 return ENC_SYM_TERROR;
230 }
231
232 84 size_t str_arr_len = 0;
233 84 char *str = NULL;//lbm_dec_str(args[0]);
234
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 84 times.
84 if (!dec_str_size(args[0], &str, &str_arr_len)) {
235 return ENC_SYM_TERROR;
236 }
237
238 84 uint32_t len = (uint32_t)strlen_max(str, str_arr_len);
239
240 84 uint32_t start = lbm_dec_as_u32(args[1]);
241
242
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 84 times.
84 if (start >= len) {
243 return ENC_SYM_EERROR;
244 }
245
246 84 uint32_t n = len - start;
247
2/2
✓ Branch 0 taken 56 times.
✓ Branch 1 taken 28 times.
84 if (argn == 3) {
248
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 56 times.
56 if (!lbm_is_number(args[2])) {
249 return ENC_SYM_TERROR;
250 }
251
252
1/2
✓ Branch 1 taken 56 times.
✗ Branch 2 not taken.
56 n = MIN(lbm_dec_as_u32(args[2]), n);
253 }
254
255 lbm_value res;
256
1/2
✓ Branch 1 taken 84 times.
✗ Branch 2 not taken.
84 if (lbm_create_array(&res, n + 1)) {
257 84 lbm_array_header_t *arr = (lbm_array_header_t*)lbm_car(res);
258 84 memcpy(arr->data, str + start, n);
259 84 ((char*)(arr->data))[n] = '\0';
260 84 return res;
261 } else {
262 return ENC_SYM_MERROR;
263 }
264 }
265
266 140 static lbm_value ext_str_split(lbm_value *args, lbm_uint argn) {
267
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 112 times.
140 if (argn != 2) {
268 28 lbm_set_error_reason((char*)lbm_error_str_num_args);
269 28 return ENC_SYM_EERROR;
270 }
271
272 112 size_t str_arr_size = 0;
273 112 char *str = NULL; //lbm_dec_str(args[0]);
274
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 112 times.
112 if (!dec_str_size(args[0], &str, &str_arr_size)) {
275 return ENC_SYM_TERROR;
276 }
277
278 112 char *split = lbm_dec_str(args[1]);
279 112 int step = 0;
280
2/2
✓ Branch 0 taken 56 times.
✓ Branch 1 taken 56 times.
112 if (!split) {
281
1/2
✓ Branch 1 taken 56 times.
✗ Branch 2 not taken.
56 if (lbm_is_number(args[1])) {
282
2/2
✓ Branch 1 taken 28 times.
✓ Branch 2 taken 28 times.
56 step = MAX(lbm_dec_as_i32(args[1]), 1);
283 56 lbm_value res = ENC_SYM_NIL;
284 56 int len = (int)strlen_max(str, str_arr_size);
285
2/2
✓ Branch 0 taken 560 times.
✓ Branch 1 taken 56 times.
616 for (int i = len / step;i >= 0;i--) {
286 560 int ind_now = i * step;
287
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 532 times.
560 if (ind_now >= len) {
288 28 continue;
289 }
290
291 532 int step_now = step;
292
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 532 times.
560 while ((ind_now + step_now) > len) {
293 28 step_now--;
294 }
295
296 lbm_value tok;
297
1/2
✓ Branch 1 taken 532 times.
✗ Branch 2 not taken.
532 if (lbm_create_array(&tok, (lbm_uint)step_now + 1)) {
298 532 lbm_array_header_t *arr = (lbm_array_header_t*)lbm_car(tok);
299 532 memcpy(arr->data, str + ind_now, (unsigned int)step_now);
300 532 ((char*)(arr->data))[step_now] = '\0';
301 532 res = lbm_cons(tok, res);
302 } else {
303 return ENC_SYM_MERROR;
304 }
305 }
306 56 return res;
307 } else {
308 return ENC_SYM_TERROR;
309 }
310 } else {
311 56 lbm_value res = ENC_SYM_NIL;
312 56 const char *s = str;
313
2/2
✓ Branch 0 taken 224 times.
✓ Branch 1 taken 56 times.
280 while (*(s += strspn(s, split)) != '\0') {
314 224 size_t len = strcspn(s, split);
315
316 lbm_value tok;
317
1/2
✓ Branch 1 taken 224 times.
✗ Branch 2 not taken.
224 if (lbm_create_array(&tok, len + 1)) {
318 224 lbm_array_header_t *arr = (lbm_array_header_t*)lbm_car(tok);
319 224 memcpy(arr->data, s, len);
320 224 ((char*)(arr->data))[len] = '\0';
321 224 res = lbm_cons(tok, res);
322 } else {
323 return ENC_SYM_MERROR;
324 }
325 224 s += len;
326 }
327 56 return lbm_list_destructive_reverse(res);
328 }
329 }
330
331 // Todo: Clean this up for 64bit
332 84 static lbm_value ext_str_replace(lbm_value *args, lbm_uint argn) {
333
4/4
✓ Branch 0 taken 56 times.
✓ Branch 1 taken 28 times.
✓ Branch 2 taken 28 times.
✓ Branch 3 taken 28 times.
84 if (argn != 2 && argn != 3) {
334 28 lbm_set_error_reason((char*)lbm_error_str_num_args);
335 28 return ENC_SYM_EERROR;
336 }
337
338 56 size_t orig_arr_size = 0;
339 56 char *orig = NULL; // lbm_dec_str(args[0]);
340
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 56 times.
56 if (!dec_str_size(args[0], &orig, &orig_arr_size)) {
341 return ENC_SYM_TERROR;
342 }
343
344 56 size_t rep_arr_size = 0;
345 56 char *rep = NULL; //lbm_dec_str(args[1]);
346
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 56 times.
56 if (!dec_str_size(args[1], &rep, &rep_arr_size)) {
347 return ENC_SYM_TERROR;
348 }
349
350 56 size_t with_arr_size = 0;
351 56 char *with = "";
352
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 28 times.
56 if (argn == 3) {
353
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 28 times.
28 if (!dec_str_size(args[2], &with, &with_arr_size)) {
354 return ENC_SYM_TERROR;
355 }
356 }
357
358 // See https://stackoverflow.com/questions/779875/what-function-is-to-replace-a-substring-from-a-string-in-c
359 //char *result; // the return string
360 char *ins; // the next insert point
361 char *tmp; // varies
362 size_t len_rep; // length of rep (the string to remove)
363 size_t len_with; // length of with (the string to replace rep with)
364 size_t len_front; // distance between rep and end of last rep
365 int count; // number of replacements
366
367 56 len_rep = strlen_max(rep, rep_arr_size);
368
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 56 times.
56 if (len_rep == 0) {
369 return args[0]; // empty rep causes infinite loop during count
370 }
371
372 56 len_with = strlen_max(with,with_arr_size);
373
374 // count the number of replacements needed
375 56 ins = orig;
376
2/2
✓ Branch 0 taken 56 times.
✓ Branch 1 taken 56 times.
112 for (count = 0; (tmp = strstr(ins, rep)); ++count) {
377 56 ins = tmp + len_rep;
378 }
379
380 56 size_t len_res = strlen_max(orig, orig_arr_size) + (len_with - len_rep) * (unsigned int)count + 1;
381 lbm_value lbm_res;
382
1/2
✓ Branch 1 taken 56 times.
✗ Branch 2 not taken.
56 if (lbm_create_array(&lbm_res, len_res)) {
383 56 lbm_array_header_t *arr = (lbm_array_header_t*)lbm_car(lbm_res);
384 56 tmp = (char*)arr->data;
385 } else {
386 return ENC_SYM_MERROR;
387 }
388
389 // first time through the loop, all the variable are set correctly
390 // from here on,
391 // tmp points to the end of the result string
392 // ins points to the next occurrence of rep in orig
393 // orig points to the remainder of orig after "end of rep"
394
2/2
✓ Branch 0 taken 56 times.
✓ Branch 1 taken 56 times.
112 while (count--) {
395 56 ins = strstr(orig, rep);
396 56 len_front = (size_t)ins - (size_t)orig;
397 56 tmp = strncpy(tmp, orig, len_front) + len_front;
398 56 tmp = strncpy(tmp, with, len_with) + len_with;
399 56 orig += len_front + len_rep; // move to next "end of rep"
400 }
401 56 strcpy(tmp, orig);
402
403 56 return lbm_res;
404 }
405
406 112 static lbm_value change_case(lbm_value *args, lbm_uint argn, bool to_upper) {
407
2/2
✓ Branch 0 taken 56 times.
✓ Branch 1 taken 56 times.
112 if (argn != 1) {
408 56 lbm_set_error_reason((char*)lbm_error_str_num_args);
409 56 return ENC_SYM_EERROR;
410 }
411
412 56 size_t orig_arr_size = 0;
413 56 char *orig = NULL; //lbm_dec_str(args[0]);
414
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 56 times.
56 if (!dec_str_size(args[0], &orig, &orig_arr_size)) {
415 return ENC_SYM_TERROR;
416 }
417
418 56 size_t len = strlen_max(orig,orig_arr_size);
419 lbm_value lbm_res;
420
1/2
✓ Branch 1 taken 56 times.
✗ Branch 2 not taken.
56 if (lbm_create_array(&lbm_res, len + 1)) {
421 56 lbm_array_header_t *arr = (lbm_array_header_t*)lbm_car(lbm_res);
422
2/2
✓ Branch 0 taken 280 times.
✓ Branch 1 taken 56 times.
336 for (unsigned int i = 0;i < len;i++) {
423
2/2
✓ Branch 0 taken 140 times.
✓ Branch 1 taken 140 times.
280 if (to_upper) {
424 140 ((char*)(arr->data))[i] = (char)toupper(orig[i]);
425 } else {
426 140 ((char*)(arr->data))[i] = (char)tolower(orig[i]);
427 }
428 }
429 56 ((char*)(arr->data))[len] = '\0';
430 56 return lbm_res;
431 } else {
432 return ENC_SYM_MERROR;
433 }
434 }
435
436 56 static lbm_value ext_str_to_lower(lbm_value *args, lbm_uint argn) {
437 56 return change_case(args, argn, false);
438 }
439
440 56 static lbm_value ext_str_to_upper(lbm_value *args, lbm_uint argn) {
441 56 return change_case(args,argn, true);
442 }
443
444 336 static lbm_value ext_str_cmp(lbm_value *args, lbm_uint argn) {
445
4/4
✓ Branch 0 taken 56 times.
✓ Branch 1 taken 280 times.
✓ Branch 2 taken 28 times.
✓ Branch 3 taken 28 times.
336 if (argn != 2 && argn != 3) {
446 28 lbm_set_error_reason((char*)lbm_error_str_num_args);
447 28 return ENC_SYM_EERROR;
448 }
449
450 308 char *str1 = lbm_dec_str(args[0]);
451
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 280 times.
308 if (!str1) {
452 28 return ENC_SYM_TERROR;
453 }
454
455 280 char *str2 = lbm_dec_str(args[1]);
456
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 252 times.
280 if (!str2) {
457 28 return ENC_SYM_TERROR;
458 }
459
460 252 int n = -1;
461
2/2
✓ Branch 0 taken 28 times.
✓ Branch 1 taken 224 times.
252 if (argn == 3) {
462
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 if (!lbm_is_number(args[2])) {
463 28 return ENC_SYM_TERROR;
464 }
465
466 n = lbm_dec_as_i32(args[2]);
467 }
468
469
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 224 times.
224 if (n > 0) {
470 return lbm_enc_i(strncmp(str1, str2, (unsigned int)n));
471 } else {
472 224 return lbm_enc_i(strcmp(str1, str2));
473 }
474 }
475
476 // TODO: This is very similar to ext-print. Maybe they can share code.
477 980 static lbm_value to_str(char *delimiter, lbm_value *args, lbm_uint argn) {
478 980 const int str_len = 300;
479 980 char *str = lbm_malloc((lbm_uint)str_len);
480
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 980 times.
980 if (!str) {
481 return ENC_SYM_MERROR;
482 }
483
484 980 int str_ofs = 0;
485
486
2/2
✓ Branch 0 taken 1288 times.
✓ Branch 1 taken 980 times.
2268 for (lbm_uint i = 0; i < argn; i ++) {
487 1288 lbm_value t = args[i];
488 1288 int max = str_len - str_ofs - 1;
489
490 char *arr_str;
491 1288 int chars = 0;
492
493
2/2
✓ Branch 1 taken 252 times.
✓ Branch 2 taken 1036 times.
1288 if (lbm_value_is_printable_string(t, &arr_str)) {
494
2/2
✓ Branch 0 taken 168 times.
✓ Branch 1 taken 84 times.
252 if (str_ofs == 0) {
495 168 chars = snprintf(str + str_ofs, (unsigned int)max, "%s", arr_str);
496 } else {
497 84 chars = snprintf(str + str_ofs, (unsigned int)max, "%s%s", delimiter, arr_str);
498 }
499 } else {
500 1036 lbm_print_value(print_val_buffer, 256, t);
501
2/2
✓ Branch 0 taken 812 times.
✓ Branch 1 taken 224 times.
1036 if (str_ofs == 0) {
502 812 chars = snprintf(str + str_ofs, (unsigned int)max, "%s", print_val_buffer);
503 } else {
504 224 chars = snprintf(str + str_ofs, (unsigned int)max, "%s%s", delimiter, print_val_buffer);
505 }
506 }
507
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1288 times.
1288 if (chars >= max) {
508 str_ofs += max;
509 } else {
510 1288 str_ofs += chars;
511 }
512 }
513
514 lbm_value res;
515
1/2
✓ Branch 1 taken 980 times.
✗ Branch 2 not taken.
980 if (lbm_create_array(&res, (lbm_uint)str_ofs + 1)) {
516 980 lbm_array_header_t *arr = (lbm_array_header_t*)lbm_car(res);
517 980 strncpy((char*)arr->data, str, (unsigned int)str_ofs + 1);
518 980 lbm_free(str);
519 980 return res;
520 } else {
521 lbm_free(str);
522 return ENC_SYM_MERROR;
523 }
524 }
525
526 952 static lbm_value ext_to_str(lbm_value *args, lbm_uint argn) {
527 952 return to_str(" ", args, argn);
528 }
529
530 28 static lbm_value ext_to_str_delim(lbm_value *args, lbm_uint argn) {
531
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 28 times.
28 if (argn < 1) {
532 lbm_set_error_reason((char*)lbm_error_str_num_args);
533 return ENC_SYM_EERROR;
534 }
535
536 28 char *delim = lbm_dec_str(args[0]);
537
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 28 times.
28 if (!delim) {
538 return ENC_SYM_TERROR;
539 }
540
541 28 return to_str(delim, args + 1, argn - 1);
542 }
543
544 84 static lbm_value ext_str_len(lbm_value *args, lbm_uint argn) {
545
2/2
✓ Branch 1 taken 28 times.
✓ Branch 2 taken 56 times.
84 LBM_CHECK_ARGN(1);
546
547 56 size_t str_arr_size = 0;
548 56 char *str = NULL; //lbm_dec_str(args[0]);
549
2/2
✓ Branch 1 taken 28 times.
✓ Branch 2 taken 28 times.
56 if (!dec_str_size(args[0], &str, &str_arr_size)) {
550 28 return ENC_SYM_TERROR;
551 }
552
553 28 return lbm_enc_i((int)strlen_max(str, str_arr_size));
554 }
555
556 84 static lbm_value ext_str_replicate(lbm_value *args, lbm_uint argn) {
557
2/2
✓ Branch 0 taken 56 times.
✓ Branch 1 taken 28 times.
84 if (argn != 2) {
558 56 lbm_set_error_reason((char*)lbm_error_str_num_args);
559 56 return ENC_SYM_EERROR;
560 }
561
562 28 lbm_value res = ENC_SYM_TERROR;
563
564
2/4
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 28 times.
✗ Branch 4 not taken.
56 if (lbm_is_number(args[0]) &&
565 28 lbm_is_number(args[1])) {
566 28 uint32_t len = lbm_dec_as_u32(args[0]);
567 28 uint8_t c = lbm_dec_as_char(args[1]);
568
569 lbm_value lbm_res;
570
1/2
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
28 if (lbm_create_array(&lbm_res, len + 1)) {
571 28 lbm_array_header_t *arr = (lbm_array_header_t*)lbm_car(lbm_res);
572
2/2
✓ Branch 0 taken 112 times.
✓ Branch 1 taken 28 times.
140 for (unsigned int i = 0;i < len;i++) {
573 112 ((char*)(arr->data))[i] = (char)c;
574 }
575 28 ((char*)(arr->data))[len] = '\0';
576 28 res = lbm_res;
577 } else {
578 res = ENC_SYM_MERROR;
579 }
580 }
581 28 return res;
582 }
583
584 756 bool ci_strncmp(const char *str1, const char *str2,int n) {
585 756 bool res = true;
586
2/2
✓ Branch 0 taken 1148 times.
✓ Branch 1 taken 252 times.
1400 for (int i = 0; i < n; i ++) {
587
2/2
✓ Branch 0 taken 504 times.
✓ Branch 1 taken 644 times.
1148 if (tolower(str1[i]) != tolower(str2[i])) {
588 504 res = false;
589 504 break;
590 }
591 }
592 756 return res;
593 }
594
595 // signature: (str-find str:byte-array substr [start:int] [occurrence:int] [dir] [case_sensitivity]) -> int
596 // where
597 // seq = string|(..string)
598 // dir = 'left|'right
599 // case_sensitivity = 'case-sensitive | 'case-insensitive
600 952 static lbm_value ext_str_find(lbm_value *args, lbm_uint argn) {
601
3/4
✓ Branch 0 taken 924 times.
✓ Branch 1 taken 28 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 924 times.
952 if (argn < 2 || 6 < argn) {
602 28 lbm_set_error_reason((char *)lbm_error_str_num_args);
603 28 return ENC_SYM_EERROR;
604 }
605
2/2
✓ Branch 1 taken 28 times.
✓ Branch 2 taken 896 times.
924 if (!lbm_is_array_r(args[0])) {
606 28 lbm_set_error_suspect(args[0]);
607 28 lbm_set_error_reason((char *)lbm_error_str_incorrect_arg);
608 28 return ENC_SYM_TERROR;
609 }
610
611 896 lbm_array_header_t *str_header = (lbm_array_header_t *)lbm_car(args[0]);
612 896 const char *str = (const char *)str_header->data;
613 896 lbm_int str_size = (lbm_int)str_header->size;
614
615 // Guaranteed to be list containing strings.
616 lbm_value substrings;
617 896 lbm_int min_substr_len = LBM_INT_MAX;
618
2/2
✓ Branch 1 taken 728 times.
✓ Branch 2 taken 168 times.
896 if (lbm_is_array_r(args[1])) {
619 728 substrings = lbm_cons(args[1], ENC_SYM_NIL);
620
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 728 times.
728 if (substrings == ENC_SYM_MERROR) {
621 return ENC_SYM_MERROR;
622 }
623 728 lbm_array_header_t *header = (lbm_array_header_t *)lbm_car(args[1]);
624
625 728 lbm_int len = (lbm_int)header->size - 1;
626
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 728 times.
728 if (len < 0) {
627 // substr is zero length array
628 return lbm_enc_i(-1);
629 }
630 728 min_substr_len = len;
631
1/2
✓ Branch 1 taken 168 times.
✗ Branch 2 not taken.
168 } else if (lbm_is_list(args[1])) {
632
2/2
✓ Branch 2 taken 224 times.
✓ Branch 3 taken 168 times.
392 for (lbm_value current = args[1]; lbm_is_cons(current); current = lbm_cdr(current)) {
633 224 lbm_value car_val = lbm_car(current);
634
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 224 times.
224 if (!lbm_is_array_r(car_val)) {
635 lbm_set_error_suspect(args[1]);
636 lbm_set_error_reason((char *)lbm_error_str_incorrect_arg);
637 return ENC_SYM_TERROR;
638 }
639
640 224 lbm_array_header_t *header = (lbm_array_header_t *)lbm_car(car_val);
641
642 224 lbm_int len = (lbm_int)header->size - 1;
643
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 224 times.
224 if (len < 0) {
644 // substr is zero length array
645 continue;
646 }
647
2/2
✓ Branch 0 taken 140 times.
✓ Branch 1 taken 84 times.
224 if (len < min_substr_len) {
648 140 min_substr_len = len;
649 }
650 }
651 168 substrings = args[1];
652 } else {
653 lbm_set_error_suspect(args[1]);
654 lbm_set_error_reason((char *)lbm_error_str_incorrect_arg);
655 return ENC_SYM_TERROR;
656 }
657
658 896 bool to_right = true;
659 896 bool case_sensitive = true;
660
661 896 int nums[2] = {0, 0};
662 896 bool nums_set[2] = {false, false};
663 896 int num_ix = 0;
664
665
666
2/2
✓ Branch 0 taken 2940 times.
✓ Branch 1 taken 896 times.
3836 for (int i = 0; i < (int)argn; i ++ ) {
667
3/4
✓ Branch 1 taken 644 times.
✓ Branch 2 taken 2296 times.
✓ Branch 3 taken 644 times.
✗ Branch 4 not taken.
2940 if (lbm_is_number(args[i]) && num_ix < 2) {
668 644 nums_set[num_ix] = true;
669 644 nums[num_ix++] = lbm_dec_as_int(args[i]);
670 }
671
2/2
✓ Branch 1 taken 532 times.
✓ Branch 2 taken 2408 times.
2940 if (lbm_is_symbol(args[i])) {
672 532 lbm_uint symbol = lbm_dec_sym(args[i]);
673
2/2
✓ Branch 0 taken 280 times.
✓ Branch 1 taken 252 times.
532 if (symbol == sym_left) {
674 280 to_right = false;
675
2/2
✓ Branch 0 taken 196 times.
✓ Branch 1 taken 56 times.
252 } else if (symbol == sym_case_insensitive) {
676 196 case_sensitive = false;
677 }
678 }
679 }
680
681 896 uint32_t occurrence = 0;
682
2/2
✓ Branch 0 taken 616 times.
✓ Branch 1 taken 280 times.
896 lbm_int start = to_right ? 0 : str_size - min_substr_len;
683
2/2
✓ Branch 0 taken 504 times.
✓ Branch 1 taken 392 times.
896 if (nums_set[0]) {
684 504 start = nums[0];
685 }
686
2/2
✓ Branch 0 taken 140 times.
✓ Branch 1 taken 756 times.
896 if (nums_set[1]) {
687 140 occurrence = (uint32_t)nums[1];
688 }
689
690
2/2
✓ Branch 0 taken 224 times.
✓ Branch 1 taken 672 times.
896 if (start < 0) {
691 // start: -1 starts the search at the character index before the final null
692 // byte index.
693 224 start = str_size - 1 + start;
694 }
695
696
4/4
✓ Branch 0 taken 280 times.
✓ Branch 1 taken 616 times.
✓ Branch 2 taken 28 times.
✓ Branch 3 taken 252 times.
896 if (!to_right && (start > str_size - min_substr_len)) {
697 28 start = str_size - min_substr_len;
698 }
699
4/4
✓ Branch 0 taken 616 times.
✓ Branch 1 taken 252 times.
✓ Branch 2 taken 28 times.
✓ Branch 3 taken 588 times.
868 else if (to_right && (start < 0)) {
700 28 start = 0;
701 }
702
703
2/2
✓ Branch 0 taken 616 times.
✓ Branch 1 taken 280 times.
896 lbm_int dir = to_right ? 1 : -1;
704
4/4
✓ Branch 0 taken 1484 times.
✓ Branch 1 taken 476 times.
✓ Branch 2 taken 1820 times.
✓ Branch 3 taken 140 times.
1960 for (lbm_int i = start; to_right ? (i <= str_size - min_substr_len) : (i >= 0); i += dir) {
705
2/2
✓ Branch 2 taken 2072 times.
✓ Branch 3 taken 1064 times.
3136 for (lbm_value current = substrings; lbm_is_cons(current); current = lbm_cdr(current)) {
706 2072 lbm_array_header_t *header = (lbm_array_header_t *)lbm_car(lbm_car(current));
707 2072 lbm_int substr_len = (lbm_int)header->size - 1;
708 2072 const char *substr = (const char *)header->data;
709
710 2072 if (
711
1/2
✓ Branch 0 taken 2072 times.
✗ Branch 1 not taken.
2072 i > str_size - substr_len // substr length runs over str end.
712
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2072 times.
2072 || substr_len < 0 // empty substr substr was zero bytes in size
713 ) {
714 continue;
715 }
716
717
4/4
✓ Branch 0 taken 1316 times.
✓ Branch 1 taken 756 times.
✓ Branch 2 taken 672 times.
✓ Branch 3 taken 644 times.
2072 if ((case_sensitive && memcmp(&str[i], substr, (size_t)substr_len) == 0) ||
718
4/4
✓ Branch 0 taken 756 times.
✓ Branch 1 taken 672 times.
✓ Branch 3 taken 252 times.
✓ Branch 4 taken 504 times.
1428 (!case_sensitive && ci_strncmp(&str[i], substr, (int)substr_len))) {
719
2/2
✓ Branch 0 taken 756 times.
✓ Branch 1 taken 140 times.
896 if (occurrence == 0) {
720 756 return lbm_enc_i(i);
721 }
722 140 occurrence -= 1;
723 }
724 }
725 }
726
727 140 return lbm_enc_i(-1);
728 }
729
730 21504 void lbm_string_extensions_init(void) {
731
732 21504 lbm_add_symbol_const("left", &sym_left);
733 21504 lbm_add_symbol_const("nocase", &sym_case_insensitive);
734
735 21504 lbm_add_extension("str-from-n", ext_str_from_n);
736 21504 lbm_add_extension("str-join", ext_str_join);
737 21504 lbm_add_extension("str-to-i", ext_str_to_i);
738 21504 lbm_add_extension("str-to-f", ext_str_to_f);
739 21504 lbm_add_extension("str-part", ext_str_part);
740 21504 lbm_add_extension("str-split", ext_str_split);
741 21504 lbm_add_extension("str-replace", ext_str_replace);
742 21504 lbm_add_extension("str-to-lower", ext_str_to_lower);
743 21504 lbm_add_extension("str-to-upper", ext_str_to_upper);
744 21504 lbm_add_extension("str-cmp", ext_str_cmp);
745 21504 lbm_add_extension("to-str", ext_to_str);
746 21504 lbm_add_extension("to-str-delim", ext_to_str_delim);
747 21504 lbm_add_extension("str-len", ext_str_len);
748 21504 lbm_add_extension("str-replicate", ext_str_replicate);
749 21504 lbm_add_extension("str-find", ext_str_find);
750 21504 }
751