1 | // This file is part of SmallBASIC |
2 | // |
3 | // SmallBASIC variable manager. |
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/sys.h" |
11 | #include "common/sberr.h" |
12 | |
13 | #define INT_STR_LEN 64 |
14 | #define VAR_POOL_SIZE 8192 |
15 | |
16 | var_t var_pool[VAR_POOL_SIZE]; |
17 | var_t *var_pool_head; |
18 | |
19 | void v_init_pool() { |
20 | for (uint32_t i = 0; i < VAR_POOL_SIZE; i++) { |
21 | v_init(&var_pool[i]); |
22 | var_pool[i].pooled = 1; |
23 | if (i + 1 < VAR_POOL_SIZE) { |
24 | var_pool[i].v.pool_next = &var_pool[i + 1]; |
25 | } else { |
26 | var_pool[i].v.pool_next = NULL; |
27 | } |
28 | } |
29 | var_pool_head = &var_pool[0]; |
30 | } |
31 | |
32 | /* |
33 | * creates and returns a new variable |
34 | */ |
35 | var_t *v_new() { |
36 | var_t *result = var_pool_head; |
37 | if (result != NULL) { |
38 | // remove an item from the free-list |
39 | var_pool_head = result->v.pool_next; |
40 | } else { |
41 | // pool exhausted |
42 | result = (var_t *)malloc(sizeof(var_t)); |
43 | result->pooled = 0; |
44 | } |
45 | v_init(result); |
46 | return result; |
47 | } |
48 | |
49 | void v_pool_free(var_t *var) { |
50 | // insert back into the free list |
51 | var->v.pool_next = var_pool_head; |
52 | var_pool_head = var; |
53 | } |
54 | |
55 | uint32_t v_get_capacity(uint32_t size) { |
56 | return size + (size / 2) + 1; |
57 | } |
58 | |
59 | // allocate capacity in the array container |
60 | void v_alloc_capacity(var_t *var, uint32_t size) { |
61 | uint32_t capacity = v_get_capacity(size); |
62 | v_capacity(var) = capacity; |
63 | v_asize(var) = size; |
64 | v_data(var) = (var_t *)malloc(sizeof(var_t) * capacity); |
65 | if (!v_data(var)) { |
66 | err_memory(); |
67 | } else { |
68 | for (uint32_t i = 0; i < capacity; i++) { |
69 | var_t *e = v_elem(var, i); |
70 | e->pooled = 0; |
71 | v_init(e); |
72 | } |
73 | } |
74 | } |
75 | |
76 | // create an new empty array |
77 | void v_init_array(var_t *var) { |
78 | v_capacity(var) = 0; |
79 | v_asize(var) = 0; |
80 | v_data(var) = NULL; |
81 | v_maxdim(var) = 1; |
82 | v_ubound(var, 0) = opt_base; |
83 | v_lbound(var, 0) = opt_base; |
84 | } |
85 | |
86 | // create an array of the given size |
87 | void v_new_array(var_t *var, uint32_t size) { |
88 | var->type = V_ARRAY; |
89 | v_alloc_capacity(var, size); |
90 | } |
91 | |
92 | void v_set_array1_size(var_t *var, uint32_t size) { |
93 | v_asize(var) = size; |
94 | v_maxdim(var) = 1; |
95 | v_ubound(var, 0) = v_lbound(var, 0) + (size - 1); |
96 | } |
97 | |
98 | void v_copy_array(var_t *dest, const var_t *src) { |
99 | dest->type = V_ARRAY; |
100 | v_alloc_capacity(dest, v_asize(src)); |
101 | |
102 | // copy dimensions |
103 | v_maxdim(dest) = v_maxdim(src); |
104 | for (int i = 0; i < v_maxdim(src); i++) { |
105 | v_ubound(dest, i) = v_ubound(src, i); |
106 | v_lbound(dest, i) = v_lbound(src, i); |
107 | } |
108 | |
109 | // copy each element |
110 | uint32_t v_size = v_asize(src); |
111 | for (uint32_t i = 0; i < v_size; i++) { |
112 | var_t *dest_vp = v_elem(dest, i); |
113 | v_init(dest_vp); |
114 | v_set(dest_vp, v_elem(src, i)); |
115 | } |
116 | } |
117 | |
118 | void v_array_free(var_t *var) { |
119 | uint32_t v_size = v_capacity(var); |
120 | if (v_size && v_data(var)) { |
121 | for (uint32_t i = 0; i < v_size; i++) { |
122 | v_free(v_elem(var, i)); |
123 | } |
124 | free(var->v.a.data); |
125 | } |
126 | } |
127 | |
128 | void v_init_str(var_t *var, int length) { |
129 | var->type = V_STR; |
130 | var->v.p.ptr = malloc(length + 1); |
131 | var->v.p.ptr[0] = '\0'; |
132 | var->v.p.length = length + 1; |
133 | var->v.p.owner = 1; |
134 | } |
135 | |
136 | void v_move_str(var_t *var, char *str) { |
137 | var->type = V_STR; |
138 | var->v.p.ptr = str; |
139 | var->v.p.length = strlen(str) + 1; |
140 | var->v.p.owner = 1; |
141 | } |
142 | |
143 | /* |
144 | * returns true if the user's program must use this var as an empty var |
145 | * this is usefull for arrays |
146 | */ |
147 | int v_isempty(var_t *var) { |
148 | switch (var->type) { |
149 | case V_INT: |
150 | return (var->v.i == 0); |
151 | case V_NUM: |
152 | return (var->v.n == 0.0); |
153 | case V_STR: |
154 | return (v_strlen(var) == 0); |
155 | case V_ARRAY: |
156 | return (v_asize(var) == 0); |
157 | case V_PTR: |
158 | return (var->v.ap.p == 0); |
159 | case V_MAP: |
160 | return map_is_empty(var); |
161 | case V_REF: |
162 | return v_isempty(var->v.ref); |
163 | } |
164 | |
165 | return 1; |
166 | } |
167 | |
168 | int v_strlen(const var_t *v) { |
169 | int result; |
170 | if (v->type == V_STR) { |
171 | result = v->v.p.length; |
172 | if (result && v->v.p.ptr[result - 1] == '\0') { |
173 | result--; |
174 | } |
175 | } else { |
176 | result = 0; |
177 | } |
178 | return result; |
179 | } |
180 | |
181 | /* |
182 | * returns the length of the variable |
183 | */ |
184 | int v_length(var_t *var) { |
185 | char tmpsb[INT_STR_LEN]; |
186 | |
187 | switch (var->type) { |
188 | case V_INT: |
189 | ltostr(var->v.i, tmpsb); |
190 | return strlen(tmpsb); |
191 | case V_NUM: |
192 | ftostr(var->v.n, tmpsb); |
193 | return strlen(tmpsb); |
194 | case V_STR: |
195 | return v_strlen(var); |
196 | case V_ARRAY: |
197 | return v_asize(var); |
198 | case V_PTR: |
199 | ltostr(var->v.ap.p, tmpsb); |
200 | return strlen(tmpsb); |
201 | case V_MAP: |
202 | return map_length(var); |
203 | case V_REF: |
204 | return v_length(var->v.ref); |
205 | } |
206 | return 0; |
207 | } |
208 | |
209 | /* |
210 | * resize an existing array |
211 | */ |
212 | void v_resize_array(var_t *v, uint32_t size) { |
213 | if (v->type != V_ARRAY) { |
214 | err_varisnotarray(); |
215 | } else if ((int)size < 0) { |
216 | err_evargerr(); |
217 | } else if (size == v_asize(v)) { |
218 | // already at target size |
219 | } else if (size == 0) { |
220 | v_free(v); |
221 | v_init_array(v); |
222 | v->type = V_ARRAY; |
223 | } else if (size < v_asize(v)) { |
224 | // resize down. free discarded elements |
225 | uint32_t v_size = v_asize(v); |
226 | for (uint32_t i = size; i < v_size; i++) { |
227 | v_free(v_elem(v, i)); |
228 | } |
229 | v_set_array1_size(v, size); |
230 | } else if (size <= v_capacity(v)) { |
231 | // use existing capacity |
232 | v_set_array1_size(v, size); |
233 | } else { |
234 | // insufficient capacity |
235 | uint32_t prev_size = v_asize(v); |
236 | if (prev_size == 0) { |
237 | v_alloc_capacity(v, size); |
238 | } else if (prev_size < size) { |
239 | // resize & copy |
240 | uint32_t capacity = v_get_capacity(size); |
241 | v_capacity(v) = capacity; |
242 | v_data(v) = (var_t *)realloc(v_data(v), sizeof(var_t) * capacity); |
243 | for (uint32_t i = prev_size; i < capacity; i++) { |
244 | var_t *e = v_elem(v, i); |
245 | e->pooled = 0; |
246 | v_init(e); |
247 | } |
248 | } |
249 | |
250 | // init vars |
251 | for (uint32_t i = prev_size; i < size; i++) { |
252 | v_init(v_elem(v, i)); |
253 | } |
254 | |
255 | v_set_array1_size(v, size); |
256 | } |
257 | } |
258 | |
259 | /* |
260 | * create RxC array |
261 | */ |
262 | void v_tomatrix(var_t *v, int r, int c) { |
263 | v_free(v); |
264 | v_new_array(v, r * c); |
265 | v_maxdim(v) = 2; |
266 | v_lbound(v, 0) = v_lbound(v, 1) = opt_base; |
267 | v_ubound(v, 0) = opt_base + (r - 1); |
268 | v_ubound(v, 1) = opt_base + (c - 1); |
269 | } |
270 | |
271 | /* |
272 | * create array |
273 | */ |
274 | void v_toarray1(var_t *v, uint32_t r) { |
275 | v_free(v); |
276 | v->type = V_ARRAY; |
277 | if (r > 0) { |
278 | v_new_array(v, r); |
279 | v_maxdim(v) = 1; |
280 | v_lbound(v, 0) = opt_base; |
281 | v_ubound(v, 0) = opt_base + (r - 1); |
282 | } else { |
283 | v_init_array(v); |
284 | } |
285 | } |
286 | |
287 | /* |
288 | * returns true if the variable v is not empty (0 for nums) |
289 | */ |
290 | int v_is_nonzero(var_t *v) { |
291 | switch (v->type) { |
292 | case V_INT: |
293 | return (v->v.i != 0); |
294 | case V_NUM: |
295 | return (ABS(v->v.n) > 1E-308); |
296 | case V_STR: |
297 | return (v->v.p.length != 0); |
298 | case V_ARRAY: |
299 | return (v_asize(v) != 0); |
300 | case V_PTR: |
301 | return (v->v.ap.p != 0); |
302 | case V_MAP: |
303 | return !map_is_empty(v); |
304 | }; |
305 | return 0; |
306 | } |
307 | |
308 | /* |
309 | * compare the variable a with the variable b |
310 | * returns |
311 | * -1 a < b |
312 | * +1 a > b |
313 | * 0 a = b |
314 | */ |
315 | int v_compare(var_t *a, var_t *b) { |
316 | var_num_t dt; |
317 | var_int_t di; |
318 | if (a == 0 || b == 0) { |
319 | err_evsyntax(); |
320 | return 0; |
321 | } |
322 | |
323 | if (a->type == V_INT && b->type == V_INT) { |
324 | di = (a->v.i - b->v.i); |
325 | return (di < 0 ? -1 : di > 0 ? 1 : 0); |
326 | } else if ((a->type == V_INT || a->type == V_NUM) && |
327 | (b->type == V_INT || b->type == V_NUM)) { |
328 | var_num_t left = (a->type == V_NUM) ? a->v.n : a->v.i; |
329 | var_num_t right = (b->type == V_NUM) ? b->v.n : b->v.i; |
330 | if (fabs(left - right) < EPSILON) { |
331 | return 0; |
332 | } else { |
333 | return (left - right) < 0.0 ? -1 : 1; |
334 | } |
335 | } |
336 | if ((a->type == V_STR) && (b->type == V_STR)) { |
337 | return strcmp(a->v.p.ptr, b->v.p.ptr); |
338 | } |
339 | if ((a->type == V_STR) && (b->type == V_NUM)) { |
340 | if (a->v.p.ptr[0] == '\0' || is_number(a->v.p.ptr)) { |
341 | // compare nums |
342 | dt = v_getval(a); |
343 | return (dt < b->v.n) ? -1 : ((dt == b->v.n) ? 0 : 1); |
344 | } |
345 | return 1; |
346 | } |
347 | if ((a->type == V_NUM) && (b->type == V_STR)) { |
348 | if (b->v.p.ptr[0] == '\0' || is_number(b->v.p.ptr)) { |
349 | // compare nums |
350 | dt = v_getval(b); |
351 | return (dt < a->v.n) ? 1 : ((dt == a->v.n) ? 0 : -1); |
352 | } |
353 | return - 1; |
354 | } |
355 | if ((a->type == V_STR) && (b->type == V_INT)) { |
356 | if (a->v.p.ptr[0] == '\0' || is_number(a->v.p.ptr)) { |
357 | // compare nums |
358 | di = v_igetval(a); |
359 | return (di < b->v.i) ? -1 : ((di == b->v.i) ? 0 : 1); |
360 | } |
361 | return 1; |
362 | } |
363 | if ((a->type == V_INT) && (b->type == V_STR)) { |
364 | if (b->v.p.ptr[0] == '\0' || is_number(b->v.p.ptr)) { |
365 | // compare nums |
366 | di = v_igetval(b); |
367 | return (di < a->v.i) ? 1 : ((di == a->v.i) ? 0 : -1); |
368 | } |
369 | return - 1; |
370 | } |
371 | if ((a->type == V_ARRAY) && (b->type == V_ARRAY)) { |
372 | // check size |
373 | if (v_asize(a) < v_asize(b)) { |
374 | return -1; |
375 | } else if (v_asize(a) > v_asize(b)) { |
376 | return 1; |
377 | } |
378 | // check every element |
379 | for (uint32_t i = 0; i < v_asize(a); i++) { |
380 | var_t *ea = v_elem(a, i); |
381 | var_t *eb = v_elem(b, i); |
382 | int ci = v_compare(ea, eb); |
383 | if (ci != 0) { |
384 | return ci; |
385 | } |
386 | } |
387 | // equal |
388 | return 0; |
389 | } |
390 | |
391 | if (a->type == V_MAP && b->type == V_MAP) { |
392 | return map_compare(a, b); |
393 | } |
394 | |
395 | if (a->type == V_NIL || b->type == V_NIL) { |
396 | // return equal (0) when both NONE |
397 | return (a->type != b->type); |
398 | } |
399 | |
400 | err_evtype(); |
401 | return 1; |
402 | } |
403 | |
404 | /* |
405 | * add two variables |
406 | * result = a + b |
407 | */ |
408 | void v_add(var_t *result, var_t *a, var_t *b) { |
409 | char tmpsb[INT_STR_LEN]; |
410 | |
411 | if (a->type == V_STR && b->type == V_STR) { |
412 | int length = strlen(a->v.p.ptr) + strlen(b->v.p.ptr); |
413 | v_init_str(result, length); |
414 | strcpy(result->v.p.ptr, a->v.p.ptr); |
415 | strcat(result->v.p.ptr, b->v.p.ptr); |
416 | return; |
417 | } else if (a->type == V_INT && b->type == V_INT) { |
418 | result->type = V_INT; |
419 | result->v.i = a->v.i + b->v.i; |
420 | return; |
421 | } else if (a->type == V_NUM && b->type == V_NUM) { |
422 | result->type = V_NUM; |
423 | result->v.n = a->v.n + b->v.n; |
424 | return; |
425 | } else if (a->type == V_NUM && b->type == V_INT) { |
426 | result->type = V_NUM; |
427 | result->v.n = a->v.n + b->v.i; |
428 | return; |
429 | } else if (a->type == V_INT && b->type == V_NUM) { |
430 | result->type = V_NUM; |
431 | result->v.n = a->v.i + b->v.n; |
432 | return; |
433 | } else if (a->type == V_STR && (b->type == V_INT || b->type == V_NUM)) { |
434 | if (is_number(a->v.p.ptr)) { |
435 | result->type = V_NUM; |
436 | if (b->type == V_INT) { |
437 | result->v.n = b->v.i + v_getval(a); |
438 | } else { |
439 | result->v.n = b->v.n + v_getval(a); |
440 | } |
441 | } else { |
442 | v_init_str(result, strlen(a->v.p.ptr) + INT_STR_LEN); |
443 | strcpy(result->v.p.ptr, a->v.p.ptr); |
444 | if (b->type == V_INT) { |
445 | ltostr(b->v.i, tmpsb); |
446 | } else { |
447 | ftostr(b->v.n, tmpsb); |
448 | } |
449 | strcat(result->v.p.ptr, tmpsb); |
450 | result->v.p.length = strlen(result->v.p.ptr) + 1; |
451 | } |
452 | } else if ((a->type == V_INT || a->type == V_NUM) && b->type == V_STR) { |
453 | if (is_number(b->v.p.ptr)) { |
454 | result->type = V_NUM; |
455 | if (a->type == V_INT) { |
456 | result->v.n = a->v.i + v_getval(b); |
457 | } else { |
458 | result->v.n = a->v.n + v_getval(b); |
459 | } |
460 | } else { |
461 | v_init_str(result, strlen(b->v.p.ptr) + INT_STR_LEN); |
462 | if (a->type == V_INT) { |
463 | ltostr(a->v.i, tmpsb); |
464 | } else { |
465 | ftostr(a->v.n, tmpsb); |
466 | } |
467 | strcpy(result->v.p.ptr, tmpsb); |
468 | strcat(result->v.p.ptr, b->v.p.ptr); |
469 | result->v.p.length = strlen(result->v.p.ptr) + 1; |
470 | } |
471 | } else if (b->type == V_MAP) { |
472 | char *map = map_to_str(b); |
473 | v_set(result, a); |
474 | v_strcat(result, map); |
475 | free(map); |
476 | } |
477 | } |
478 | |
479 | /* |
480 | * assign (dest = src) |
481 | */ |
482 | void v_set(var_t *dest, const var_t *src) { |
483 | v_free(dest); |
484 | dest->const_flag = 0; |
485 | dest->type = src->type; |
486 | |
487 | switch (src->type) { |
488 | case V_INT: |
489 | dest->v.i = src->v.i; |
490 | break; |
491 | case V_NUM: |
492 | dest->v.n = src->v.n; |
493 | break; |
494 | case V_STR: |
495 | if (src->v.p.owner) { |
496 | dest->v.p.length = v_strlen(src) + 1; |
497 | dest->v.p.ptr = (char *)malloc(dest->v.p.length); |
498 | dest->v.p.owner = 1; |
499 | strcpy(dest->v.p.ptr, src->v.p.ptr); |
500 | } else { |
501 | dest->v.p.length = src->v.p.length; |
502 | dest->v.p.ptr = src->v.p.ptr; |
503 | dest->v.p.owner = 0; |
504 | } |
505 | break; |
506 | case V_ARRAY: |
507 | if (v_asize(src)) { |
508 | v_copy_array(dest, src); |
509 | } else { |
510 | v_init_array(dest); |
511 | } |
512 | break; |
513 | case V_PTR: |
514 | dest->v.ap.p = src->v.ap.p; |
515 | dest->v.ap.v = src->v.ap.v; |
516 | break; |
517 | case V_MAP: |
518 | // reset type since not yet a map |
519 | dest->type = 0; |
520 | map_set(dest, (const var_p_t)src); |
521 | break; |
522 | case V_REF: |
523 | dest->v.ref = src->v.ref; |
524 | break; |
525 | case V_FUNC: |
526 | dest->v.fn.cb = src->v.fn.cb; |
527 | dest->v.fn.id = src->v.fn.id; |
528 | break; |
529 | case V_NIL: |
530 | dest->type = V_NIL; |
531 | dest->const_flag = 1; |
532 | break; |
533 | } |
534 | } |
535 | |
536 | /* |
537 | * assign (dest = src) |
538 | */ |
539 | void v_move(var_t *dest, const var_t *src) { |
540 | v_free(dest); |
541 | dest->const_flag = 0; |
542 | dest->type = src->type; |
543 | |
544 | switch (src->type) { |
545 | case V_INT: |
546 | dest->v.i = src->v.i; |
547 | break; |
548 | case V_NUM: |
549 | dest->v.n = src->v.n; |
550 | break; |
551 | case V_STR: |
552 | dest->v.p.ptr = src->v.p.ptr; |
553 | dest->v.p.length = src->v.p.length; |
554 | dest->v.p.owner = src->v.p.owner; |
555 | break; |
556 | case V_ARRAY: |
557 | memcpy(&dest->v.a, &src->v.a, sizeof(src->v.a)); |
558 | break; |
559 | case V_PTR: |
560 | dest->v.ap.p = src->v.ap.p; |
561 | dest->v.ap.v = src->v.ap.v; |
562 | break; |
563 | case V_MAP: |
564 | dest->v.m.map = src->v.m.map; |
565 | dest->v.m.count = src->v.m.count; |
566 | dest->v.m.size = src->v.m.size; |
567 | dest->v.m.id = src->v.m.id; |
568 | break; |
569 | case V_REF: |
570 | dest->v.ref = src->v.ref; |
571 | break; |
572 | case V_FUNC: |
573 | dest->v.fn.cb = src->v.fn.cb; |
574 | dest->v.fn.id = src->v.fn.id; |
575 | break; |
576 | case V_NIL: |
577 | dest->type = V_NIL; |
578 | dest->const_flag = 1; |
579 | break; |
580 | } |
581 | } |
582 | |
583 | /* |
584 | * return a full copy of the 'source' |
585 | */ |
586 | var_t *v_clone(const var_t *source) { |
587 | var_t *vnew = v_new(); |
588 | v_set(vnew, source); |
589 | return vnew; |
590 | } |
591 | |
592 | /* |
593 | * add b to a |
594 | */ |
595 | void v_inc(var_t *a, var_t *b) { |
596 | if (a->type == V_INT && b->type == V_INT) { |
597 | a->v.i += b->v.i; |
598 | } else if (a->type == V_NUM && b->type == V_NUM) { |
599 | a->v.n += b->v.n; |
600 | } else if (a->type == V_NUM && b->type == V_INT) { |
601 | a->v.n += b->v.i; |
602 | } else if (a->type == V_INT && b->type == V_NUM) { |
603 | a->type = V_NUM; |
604 | a->v.n = a->v.i + b->v.n; |
605 | } else { |
606 | err_varnotnum(); |
607 | } |
608 | } |
609 | |
610 | /* |
611 | * returns the sign of a variable |
612 | */ |
613 | int v_sign(var_t *x) { |
614 | if (x->type == V_INT) { |
615 | return (x->v.i < 0) ? -1 : ((x->v.i == 0) ? 0 : 1); |
616 | } else if (x->type == V_NUM) { |
617 | return (x->v.n < 0) ? -1 : ((x->v.n == 0) ? 0 : 1); |
618 | } |
619 | err_varnotnum(); |
620 | return 0; |
621 | } |
622 | |
623 | /* |
624 | * setup a string variable |
625 | */ |
626 | void v_createstr(var_t *v, const char *src) { |
627 | v_init_str(v, strlen(src)); |
628 | strcpy(v->v.p.ptr, src); |
629 | } |
630 | |
631 | /* |
632 | * returns the string representation of the variable |
633 | */ |
634 | char *v_str(var_t *arg) { |
635 | char *buffer; |
636 | switch (arg->type) { |
637 | case V_INT: |
638 | buffer = malloc(INT_STR_LEN); |
639 | ltostr(arg->v.i, buffer); |
640 | break; |
641 | case V_NUM: |
642 | buffer = malloc(INT_STR_LEN); |
643 | ftostr(arg->v.n, buffer); |
644 | break; |
645 | case V_STR: |
646 | buffer = strdup(arg->v.p.ptr); |
647 | break; |
648 | case V_ARRAY: |
649 | case V_MAP: |
650 | buffer = map_to_str(arg); |
651 | break; |
652 | case V_FUNC: |
653 | case V_PTR: |
654 | buffer = malloc(5); |
655 | strcpy(buffer, "func" ); |
656 | break; |
657 | case V_NIL: |
658 | buffer = malloc(5); |
659 | strcpy(buffer, SB_KW_NONE_STR); |
660 | break; |
661 | default: |
662 | buffer = malloc(1); |
663 | buffer[0] = '\0'; |
664 | break; |
665 | } |
666 | return buffer; |
667 | } |
668 | |
669 | /* |
670 | * converts the variable to string-variable |
671 | */ |
672 | void v_tostr(var_t *arg) { |
673 | if (arg->type != V_STR) { |
674 | char *tmp = v_str(arg); |
675 | v_free(arg); |
676 | v_init_str(arg, strlen(tmp)); |
677 | strcpy(arg->v.p.ptr, tmp); |
678 | free(tmp); |
679 | } |
680 | } |
681 | |
682 | /* |
683 | * set the value of 'var' to string |
684 | */ |
685 | void v_setstr(var_t *var, const char *str) { |
686 | if (var->type != V_STR || strcmp(str, var->v.p.ptr) != 0) { |
687 | v_free(var); |
688 | v_init_str(var, strlen(str)); |
689 | strcpy(var->v.p.ptr, str); |
690 | } |
691 | } |
692 | |
693 | void v_setstrn(var_t *var, const char *str, int len) { |
694 | if (var->type != V_STR || strncmp(str, var->v.p.ptr, len) != 0) { |
695 | v_free(var); |
696 | v_init_str(var, len); |
697 | strlcpy(var->v.p.ptr, str, len + 1); |
698 | } |
699 | } |
700 | |
701 | /* |
702 | * adds a string to current string value |
703 | */ |
704 | void v_strcat(var_t *var, const char *str) { |
705 | if (var->type == V_INT || var->type == V_NUM) { |
706 | v_tostr(var); |
707 | } |
708 | if (var->type == V_STR) { |
709 | if (var->v.p.owner) { |
710 | var->v.p.length = strlen(var->v.p.ptr) + strlen(str) + 1; |
711 | var->v.p.ptr = realloc(var->v.p.ptr, var->v.p.length); |
712 | strcat(var->v.p.ptr, str); |
713 | } else { |
714 | // mutate into owner string |
715 | char *p = var->v.p.ptr; |
716 | int len = var->v.p.length + strlen(str); |
717 | v_init_str(var, len); |
718 | strcpy(var->v.p.ptr, p); |
719 | strcat(var->v.p.ptr, str); |
720 | } |
721 | |
722 | } else { |
723 | err_typemismatch(); |
724 | } |
725 | } |
726 | |
727 | /* |
728 | * set the value of 'var' to n |
729 | */ |
730 | void v_setreal(var_t *var, var_num_t n) { |
731 | v_free(var); |
732 | var->type = V_NUM; |
733 | var->v.n = n; |
734 | } |
735 | |
736 | /* |
737 | * set the value of 'var' to i |
738 | */ |
739 | void v_setint(var_t *var, var_int_t i) { |
740 | v_free(var); |
741 | var->type = V_INT; |
742 | var->v.i = i; |
743 | } |
744 | |
745 | /* |
746 | * return the string value of 'var' |
747 | */ |
748 | char *v_getstr(var_t *var) { |
749 | if (var->type != V_STR) { |
750 | v_tostr(var); |
751 | } |
752 | return var->v.p.ptr; |
753 | } |
754 | |
755 | /* |
756 | * set an empty string |
757 | */ |
758 | void v_zerostr(var_t *r) { |
759 | v_free(r); |
760 | v_init_str(r, 0); |
761 | r->v.p.ptr[0] = '\0'; |
762 | } |
763 | |
764 | /* |
765 | * convert's a user's string to variable |
766 | * |
767 | * its decides in what format to store the value |
768 | * its used mostly by 'input' functions |
769 | */ |
770 | void v_input2var(const char *str, var_t *var) { |
771 | v_free(var); |
772 | |
773 | if (!str || str[0] == '\0') { |
774 | // no data |
775 | v_setstr(var, str); |
776 | } else { |
777 | int type; |
778 | var_int_t lv; |
779 | var_num_t dv; |
780 | |
781 | char *buf = strdup(str); |
782 | char *sb = strdup(str); |
783 | char *np = get_numexpr(sb, buf, &type, &lv, &dv); |
784 | |
785 | if (type == 1 && *np == '\0') { |
786 | v_setint(var, lv); |
787 | } else if (type == 2 && *np == '\0') { |
788 | v_setreal(var, dv); |
789 | } else { |
790 | v_setstr(var, str); |
791 | } |
792 | free(sb); |
793 | free(buf); |
794 | } |
795 | } |
796 | |
797 | void v_create_func(var_p_t map, const char *name, method cb) { |
798 | var_p_t v_func = map_add_var(map, name, 0); |
799 | v_func->type = V_FUNC; |
800 | v_func->v.fn.cb = cb; |
801 | v_func->v.fn.id = 0; |
802 | } |
803 | |