1 | // This file is part of SmallBASIC |
2 | // |
3 | // SmallBASIC, library API |
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/pproc.h" |
12 | #include "common/messages.h" |
13 | #include "common/inet.h" |
14 | |
15 | #include <limits.h> |
16 | #include <dirent.h> |
17 | |
18 | #define MAX_PARAM 8 |
19 | |
20 | /* |
21 | * returns the last-modified time of the file |
22 | * |
23 | * on error returns 0L |
24 | */ |
25 | time_t sys_filetime(const char *file) { |
26 | struct stat st; |
27 | |
28 | if (stat(file, &st) == 0) { |
29 | return st.st_mtime; |
30 | } |
31 | return 0L; |
32 | } |
33 | |
34 | /* |
35 | * search a set of directories for the given file |
36 | * directories on path must be separated with symbol ':' (unix) or ';' (dos/win) |
37 | * |
38 | * @param path the path |
39 | * @param file the file |
40 | * @param retbuf a buffer to store the full-path-name file (can be NULL) |
41 | * @return non-zero if found |
42 | */ |
43 | int sys_search_path(const char *path, const char *file, char *retbuf) { |
44 | char cur_path[OS_PATHNAME_SIZE + 1]; |
45 | int found = 0; |
46 | |
47 | if (path == NULL || strlen(path) == 0) { |
48 | return 0; |
49 | } |
50 | const char *ps = path; |
51 | const char *p; |
52 | do { |
53 | // next element, build cur_path |
54 | p = strchr(ps, ':'); |
55 | if (!p) { |
56 | strlcpy(cur_path, ps, sizeof(cur_path)); |
57 | } else { |
58 | strncpy(cur_path, ps, p - ps); |
59 | cur_path[p - ps] = '\0'; |
60 | ps = p + 1; |
61 | } |
62 | |
63 | // fix home directory |
64 | if (cur_path[0] == '~') { |
65 | char *old_path = malloc(strlen(cur_path)); |
66 | strcpy(old_path, cur_path + 1); |
67 | if (getenv("HOME" )) { |
68 | strlcpy(cur_path, getenv("HOME" ), sizeof(cur_path)); |
69 | } else { |
70 | strlcpy(cur_path, getenv("HOMEDRIVE" ), sizeof(cur_path)); |
71 | strlcat(cur_path, getenv("HOMEPATH" ), sizeof(cur_path)); |
72 | } |
73 | strlcat(cur_path, "/" , sizeof(cur_path)); |
74 | strlcat(cur_path, old_path, sizeof(cur_path)); |
75 | free(old_path); |
76 | } |
77 | |
78 | DIR *dp = opendir(cur_path); |
79 | if (dp != NULL) { |
80 | struct dirent *entry; |
81 | while (!found && (entry = readdir(dp)) != NULL) { |
82 | if (strcasecmp(entry->d_name, file) == 0) { |
83 | int end = strlen(cur_path); |
84 | strcat(cur_path, "/" ); |
85 | strcat(cur_path, entry->d_name); |
86 | if (access(cur_path, R_OK) == 0) { |
87 | if (retbuf) { |
88 | strcpy(retbuf, cur_path); |
89 | } |
90 | found = 1; |
91 | } else { |
92 | cur_path[end] = '\0'; |
93 | } |
94 | } |
95 | } |
96 | closedir(dp); |
97 | } |
98 | } while (p && !found); |
99 | return found; |
100 | } |
101 | |
102 | /* |
103 | * execute a user's expression (using one variable) |
104 | * (note: keyword USE) |
105 | * |
106 | * var - the variable (the X) |
107 | * ip - expression's address |
108 | */ |
109 | void exec_usefunc(var_t *var, bcip_t ip) { |
110 | // save X |
111 | var_t *old_x = v_clone(tvar[SYSVAR_X]); |
112 | |
113 | // run |
114 | v_set(tvar[SYSVAR_X], var); |
115 | v_free(var); |
116 | code_jump(ip); |
117 | eval(var); |
118 | |
119 | // restore X |
120 | v_set(tvar[SYSVAR_X], old_x); |
121 | v_free(old_x); |
122 | v_detach(old_x); |
123 | } |
124 | |
125 | /* |
126 | * execute a user's expression (using two variable) |
127 | * |
128 | * var1 - the first variable (the X) |
129 | * var2 - the second variable (the Y) |
130 | * ip - expression's address |
131 | */ |
132 | void exec_usefunc2(var_t *var1, var_t *var2, bcip_t ip) { |
133 | // save X |
134 | var_t *old_x = v_clone(tvar[SYSVAR_X]); |
135 | var_t *old_y = v_clone(tvar[SYSVAR_Y]); |
136 | |
137 | // run |
138 | v_set(tvar[SYSVAR_X], var1); |
139 | v_free(var1); |
140 | v_set(tvar[SYSVAR_Y], var2); |
141 | v_free(var2); |
142 | code_jump(ip); |
143 | eval(var1); |
144 | |
145 | // restore X,Y |
146 | v_set(tvar[SYSVAR_X], old_x); |
147 | v_free(old_x); |
148 | v_detach(old_x); |
149 | v_set(tvar[SYSVAR_Y], old_y); |
150 | v_free(old_y); |
151 | v_detach(old_y); |
152 | } |
153 | |
154 | void pv_write_str(char *str, var_t *vp) { |
155 | vp->v.p.length += strlen(str); |
156 | if (vp->v.p.ptr == NULL) { |
157 | vp->v.p.ptr = malloc(vp->v.p.length + 1); |
158 | vp->v.p.owner = 1; |
159 | strcpy(vp->v.p.ptr, str); |
160 | } else { |
161 | vp->v.p.ptr = realloc(vp->v.p.ptr, vp->v.p.length + 1); |
162 | strcat(vp->v.p.ptr, str); |
163 | } |
164 | } |
165 | |
166 | /* |
167 | * Write string to output device |
168 | */ |
169 | void pv_write(char *str, int method, intptr_t handle) { |
170 | switch (method) { |
171 | case PV_FILE: |
172 | dev_fwrite((int)handle, (byte *)str, strlen(str)); |
173 | break; |
174 | case PV_LOG: |
175 | lwrite(str); |
176 | break; |
177 | case PV_STRING: |
178 | pv_write_str(str, (var_t *)handle); |
179 | break; |
180 | case PV_NET: |
181 | net_print((socket_t)handle, (const char *)str); |
182 | break; |
183 | default: |
184 | dev_print(str); |
185 | } |
186 | } |
187 | |
188 | /* |
189 | * just prints the value of variable 'var' |
190 | */ |
191 | void pv_writevar(var_t *var, int method, intptr_t handle) { |
192 | char tmpsb[64]; |
193 | |
194 | // start with a clean buffer |
195 | memset(tmpsb, 0, sizeof(tmpsb)); |
196 | |
197 | switch (var->type) { |
198 | case V_INT: |
199 | ltostr(var->v.i, tmpsb); |
200 | pv_write(tmpsb, method, handle); |
201 | break; |
202 | case V_NUM: |
203 | ftostr(var->v.n, tmpsb); |
204 | pv_write(tmpsb, method, handle); |
205 | break; |
206 | case V_STR: |
207 | pv_write(var->v.p.ptr, method, handle); |
208 | break; |
209 | case V_ARRAY: |
210 | case V_MAP: |
211 | map_write(var, method, handle); |
212 | break; |
213 | case V_PTR: |
214 | ltostr(var->v.ap.p, tmpsb); |
215 | pv_write(tmpsb, method, handle); |
216 | break; |
217 | case V_REF: |
218 | pv_writevar(var->v.ref, method, handle); |
219 | break; |
220 | case V_NIL: |
221 | pv_write(SB_KW_NONE_STR, method, handle); |
222 | break; |
223 | } |
224 | } |
225 | |
226 | /* |
227 | * write a variable to console |
228 | */ |
229 | void print_var(var_t *v) { |
230 | pv_writevar(v, PV_CONSOLE, 0); |
231 | } |
232 | |
233 | /* |
234 | * write a variable to a file |
235 | */ |
236 | void fprint_var(intptr_t handle, var_t *v) { |
237 | pv_writevar(v, PV_FILE, handle); |
238 | } |
239 | |
240 | /* |
241 | * write a variable to log-file |
242 | */ |
243 | void logprint_var(var_t *v) { |
244 | pv_writevar(v, PV_LOG, 0); |
245 | } |
246 | |
247 | /* |
248 | * skip parameter |
249 | */ |
250 | void par_skip() { |
251 | byte exitf = 0; |
252 | uint32_t len; |
253 | int level = 0; |
254 | |
255 | do { |
256 | switch (code_peek()) { |
257 | case kwTYPE_INT: // integer |
258 | prog_ip += OS_INTSZ + 1; |
259 | break; |
260 | case kwTYPE_NUM: // number |
261 | prog_ip += OS_REALSZ + 1; |
262 | break; |
263 | case kwTYPE_STR: // string: [2/4B-len][data] |
264 | prog_ip++; |
265 | memcpy(&len, prog_source + prog_ip, OS_STRLEN); |
266 | len += OS_STRLEN; |
267 | prog_ip += len; |
268 | break; |
269 | case kwTYPE_VAR: // [addr|id] |
270 | prog_ip += ADDRSZ + 1; |
271 | break; |
272 | case kwTYPE_CALLF: |
273 | prog_ip += CODESZ + 1; |
274 | break; |
275 | case kwTYPE_CALLEXTF: |
276 | prog_ip += (ADDRSZ * 2) + 1; |
277 | break; |
278 | case kwTYPE_LEVEL_BEGIN: // left parenthesis |
279 | level++; |
280 | prog_ip++; |
281 | break; |
282 | case kwTYPE_LEVEL_END: // right parenthesis |
283 | prog_ip++; |
284 | level--; |
285 | if (level <= 0) |
286 | exitf = 1; |
287 | break; |
288 | case kwTYPE_LOGOPR: |
289 | case kwTYPE_CMPOPR: |
290 | case kwTYPE_ADDOPR: |
291 | case kwTYPE_MULOPR: |
292 | case kwTYPE_POWOPR: |
293 | case kwTYPE_UNROPR: // [1B data] |
294 | prog_ip += 2; |
295 | break; |
296 | case kwTYPE_CALL_UDF: // [true-ip][false-ip] |
297 | prog_ip += BC_CTRLSZ + 1; |
298 | break; |
299 | case kwTYPE_LINE: |
300 | case kwTYPE_EOC: |
301 | case kwUSE: |
302 | if (level != 0) { |
303 | rt_raise("Block error!" ); |
304 | } |
305 | exitf = 1; |
306 | break; |
307 | case kwTYPE_SEP: |
308 | if (level <= 0) { |
309 | exitf = 1; |
310 | } else { |
311 | prog_ip += 2; |
312 | } |
313 | break; |
314 | default: |
315 | prog_ip++; |
316 | } |
317 | } while (!exitf); |
318 | } |
319 | |
320 | /* |
321 | * get next parameter as var_t |
322 | */ |
323 | void par_getvar(var_t *var) { |
324 | switch (code_peek()) { |
325 | case kwTYPE_LINE: |
326 | case kwTYPE_EOC: |
327 | case kwTYPE_SEP: |
328 | err_syntax(-1, "%P" ); |
329 | return; |
330 | default: |
331 | eval(var); |
332 | break; |
333 | }; |
334 | } |
335 | |
336 | /* |
337 | * get next parameter as var_t/array |
338 | */ |
339 | var_t *par_getvarray() { |
340 | var_t *var; |
341 | |
342 | switch (code_peek()) { |
343 | case kwTYPE_LINE: |
344 | case kwTYPE_EOC: |
345 | case kwTYPE_SEP: |
346 | err_syntax(-1, "%P" ); |
347 | return NULL; |
348 | case kwTYPE_VAR: |
349 | var = code_getvarptr(); |
350 | if (!prog_error) { |
351 | if (var->type != V_ARRAY) { |
352 | return NULL; |
353 | } |
354 | } |
355 | return var; |
356 | default: |
357 | err_typemismatch(); |
358 | break; |
359 | }; |
360 | return NULL; |
361 | } |
362 | |
363 | /* |
364 | * get next parameter as var_t |
365 | */ |
366 | var_t *par_getvar_ptr() { |
367 | if (code_peek() != kwTYPE_VAR) { |
368 | err_syntax(-1, "%P" ); |
369 | return NULL; |
370 | } |
371 | return code_getvarptr(); |
372 | } |
373 | |
374 | /* |
375 | * get next parameter as var_t |
376 | */ |
377 | void par_getstr(var_t *var) { |
378 | switch (code_peek()) { |
379 | case kwTYPE_LINE: |
380 | case kwTYPE_EOC: |
381 | case kwTYPE_SEP: |
382 | err_syntax(-1, "%S" ); |
383 | return; |
384 | default: |
385 | v_init(var); |
386 | eval(var); |
387 | break; |
388 | }; |
389 | |
390 | if (var->type != V_STR) { |
391 | v_tostr(var); |
392 | } |
393 | } |
394 | |
395 | /* |
396 | * get next parameter as long |
397 | */ |
398 | var_int_t par_getint() { |
399 | var_t var; |
400 | |
401 | v_init(&var); |
402 | par_getvar(&var); |
403 | var_int_t i = v_getint(&var); |
404 | v_free(&var); |
405 | |
406 | return i; |
407 | } |
408 | |
409 | /* |
410 | * get next parameter as double |
411 | */ |
412 | var_num_t par_getnum() { |
413 | var_t var; |
414 | |
415 | v_init(&var); |
416 | par_getvar(&var); |
417 | var_num_t f = v_getval(&var); |
418 | v_free(&var); |
419 | |
420 | return f; |
421 | } |
422 | |
423 | var_int_t par_next_int(int sep) { |
424 | var_int_t result; |
425 | if (prog_error) { |
426 | result = 0; |
427 | } else { |
428 | result = par_getint(); |
429 | } |
430 | if (!prog_error && (sep || code_peek() == kwTYPE_SEP)) { |
431 | par_getcomma(); |
432 | } |
433 | return result; |
434 | } |
435 | |
436 | var_t *par_next_str(var_t *arg, int sep) { |
437 | var_t *result; |
438 | if (prog_error) { |
439 | result = NULL; |
440 | } else if (code_isvar()) { |
441 | result = code_getvarptr(); |
442 | } else { |
443 | eval(arg); |
444 | result = arg; |
445 | if (result->type != V_STR) { |
446 | v_tostr(arg); |
447 | } |
448 | } |
449 | if (!prog_error && (sep || code_peek() == kwTYPE_SEP)) { |
450 | par_getcomma(); |
451 | } |
452 | return result; |
453 | } |
454 | |
455 | var_int_t par_getval(var_int_t def) { |
456 | var_int_t result; |
457 | if (prog_error) { |
458 | result = def; |
459 | } else { |
460 | var_t var; |
461 | switch (code_peek()) { |
462 | case kwTYPE_LINE: |
463 | case kwTYPE_EOC: |
464 | case kwTYPE_SEP: |
465 | case kwTYPE_LEVEL_END: |
466 | case kwTYPE_EVPUSH: |
467 | result = def; |
468 | break; |
469 | default: |
470 | v_init(&var); |
471 | eval(&var); |
472 | result = prog_error ? 0 : v_getint(&var); |
473 | v_free(&var); |
474 | break; |
475 | } |
476 | } |
477 | return result; |
478 | } |
479 | |
480 | /* |
481 | * no-error if the next byte is separator |
482 | * returns the separator |
483 | */ |
484 | int par_getsep() { |
485 | switch (code_peek()) { |
486 | case kwTYPE_SEP: |
487 | code_skipnext(); |
488 | return code_getnext(); |
489 | default: |
490 | err_missing_sep(); |
491 | }; |
492 | |
493 | return 0; |
494 | } |
495 | |
496 | /* |
497 | * no-error if the next byte is the separator ',' |
498 | */ |
499 | void par_getcomma() { |
500 | if (par_getsep() != ',') { |
501 | if (!prog_error) { |
502 | err_missing_comma(); |
503 | } |
504 | } |
505 | } |
506 | |
507 | /* |
508 | * no-error if the next byte is the separator ';' |
509 | */ |
510 | void par_getsemicolon() { |
511 | if (par_getsep() != ';') { |
512 | if (!prog_error) { |
513 | err_syntaxsep(";" ); |
514 | } |
515 | } |
516 | } |
517 | |
518 | /* |
519 | * no-error if the next byte is the separator '#' |
520 | */ |
521 | void par_getsharp() { |
522 | if (par_getsep() != '#') { |
523 | if (!prog_error) { |
524 | err_syntaxsep("#" ); |
525 | } |
526 | } |
527 | } |
528 | |
529 | /* |
530 | * retrieve a 2D point (double) |
531 | */ |
532 | pt_t par_getpt() { |
533 | pt_t pt; |
534 | var_t *var; |
535 | byte alloc = 0; |
536 | |
537 | pt.x = pt.y = 0; |
538 | |
539 | // first parameter |
540 | if (code_isvar()) { |
541 | var = code_getvarptr(); |
542 | } else { |
543 | alloc = 1; |
544 | var = v_new(); |
545 | eval(var); |
546 | } |
547 | |
548 | if (!prog_error) { |
549 | if (var->type == V_ARRAY) { |
550 | // array |
551 | if (v_asize(var) != 2) { |
552 | rt_raise(ERR_POLY_POINT); |
553 | } else { |
554 | pt.x = v_getreal(v_elem(var, 0)); |
555 | pt.y = v_getreal(v_elem(var, 1)); |
556 | } |
557 | } else { |
558 | // non-arrays |
559 | pt.x = v_getreal(var); |
560 | par_getcomma(); |
561 | if (!prog_error) { |
562 | var_t v2; |
563 | v_init(&v2); |
564 | eval(&v2); |
565 | if (!prog_error) { |
566 | pt.y = v_getreal(&v2); |
567 | } |
568 | v_free(&v2); |
569 | } |
570 | } |
571 | } |
572 | // clean-up |
573 | if (alloc) { |
574 | v_free(var); |
575 | v_detach(var); |
576 | } |
577 | |
578 | return pt; |
579 | } |
580 | |
581 | /* |
582 | * retrieve a 2D point (integer) |
583 | */ |
584 | ipt_t par_getipt() { |
585 | ipt_t pt; |
586 | var_t *var; |
587 | byte alloc = 0; |
588 | |
589 | pt.x = pt.y = 0; |
590 | |
591 | // first parameter |
592 | if (code_isvar()) { |
593 | var = code_getvarptr(); |
594 | } else { |
595 | alloc = 1; |
596 | var = v_new(); |
597 | eval(var); |
598 | } |
599 | |
600 | if (!prog_error) { |
601 | if (var->type == V_ARRAY) { |
602 | // array |
603 | if (v_asize(var) != 2) { |
604 | rt_raise(ERR_POLY_POINT); |
605 | } else { |
606 | pt.x = v_getint(v_elem(var, 0)); |
607 | pt.y = v_getint(v_elem(var, 1)); |
608 | } |
609 | } else { |
610 | // non-arrays |
611 | pt.x = v_getint(var); |
612 | par_getcomma(); |
613 | if (!prog_error) { |
614 | var_t v2; |
615 | v_init(&v2); |
616 | eval(&v2); |
617 | if (!prog_error) { |
618 | pt.y = v_getint(&v2); |
619 | } |
620 | v_free(&v2); |
621 | } |
622 | } |
623 | } |
624 | // clean-up |
625 | if (alloc) { |
626 | v_free(var); |
627 | v_detach(var); |
628 | } |
629 | |
630 | return pt; |
631 | } |
632 | |
633 | /* |
634 | * retrieve a 2D polyline |
635 | */ |
636 | int par_getpoly(pt_t **poly_pp) { |
637 | pt_t *poly = NULL; |
638 | var_t *var, *el; |
639 | int count = 0; |
640 | byte style = 0, alloc = 0; |
641 | |
642 | // get array |
643 | if (code_isvar()) { |
644 | var = par_getvarray(); |
645 | if (var == NULL || prog_error) { |
646 | return 0; |
647 | } |
648 | } else { |
649 | var = v_new(); |
650 | eval(var); |
651 | alloc = 1; |
652 | } |
653 | |
654 | // zero-length or non array |
655 | if (var->type != V_ARRAY || v_asize(var) == 0) { |
656 | if (alloc) { |
657 | v_free(var); |
658 | v_detach(var); |
659 | } |
660 | return 0; |
661 | } |
662 | |
663 | el = v_elem(var, 0); |
664 | if (el->type == V_ARRAY) { |
665 | style = 1; // nested --- [ [x1,y1], [x2,y2], ... ] |
666 | } |
667 | // else style = 0; // 2x2 or 1x --- [ x1, y1, x2, y2, ... ] |
668 | |
669 | // error check |
670 | if (style == 1) { |
671 | if (v_asize(el) != 2) { |
672 | err_parsepoly(-1, 1); |
673 | if (alloc) { |
674 | v_free(var); |
675 | v_detach(var); |
676 | } |
677 | return 0; |
678 | } |
679 | |
680 | count = v_asize(var); |
681 | } else if (style == 0) { |
682 | if ((v_asize(var) % 2) != 0) { |
683 | err_parsepoly(-1, 2); |
684 | if (alloc) { |
685 | v_free(var); |
686 | v_detach(var); |
687 | } |
688 | return 0; |
689 | } |
690 | |
691 | count = v_asize(var) >> 1; |
692 | } |
693 | // build array |
694 | *poly_pp = poly = malloc(sizeof(pt_t) * count); |
695 | |
696 | if (style == 1) { |
697 | int i; |
698 | |
699 | for (i = 0; i < count; i++) { |
700 | // get point |
701 | el = v_elem(var, i); |
702 | |
703 | // error check |
704 | if (el->type != V_ARRAY) { |
705 | err_parsepoly(i, 3); |
706 | } else if (v_asize(el) != 2) { |
707 | err_parsepoly(i, 4); |
708 | } |
709 | if (prog_error) { |
710 | break; |
711 | } |
712 | // store point |
713 | poly[i].x = v_getreal(v_elem(el, 0)); |
714 | poly[i].y = v_getreal(v_elem(el, 1)); |
715 | } |
716 | } else if (style == 0) { |
717 | int i, j; |
718 | |
719 | for (i = j = 0; i < count; i++, j += 2) { |
720 | // error check |
721 | if (prog_error) { |
722 | break; |
723 | } |
724 | // store point |
725 | poly[i].x = v_getreal(v_elem(var, j)); |
726 | poly[i].y = v_getreal(v_elem(var, j + 1)); |
727 | } |
728 | } |
729 | // clean-up |
730 | if (prog_error) { |
731 | free(poly); |
732 | *poly_pp = NULL; |
733 | count = 0; |
734 | } |
735 | if (alloc) { |
736 | v_free(var); |
737 | v_detach(var); |
738 | } |
739 | |
740 | return count; |
741 | } |
742 | |
743 | /* |
744 | * retrieve a 2D polyline (integers) |
745 | */ |
746 | int par_getipoly(ipt_t **poly_pp) { |
747 | ipt_t *poly = NULL; |
748 | var_t *var, *el; |
749 | int count = 0; |
750 | byte style = 0, alloc = 0; |
751 | |
752 | // get array |
753 | if (code_isvar()) { |
754 | var = par_getvarray(); |
755 | if (var == NULL || prog_error) { |
756 | return 0; |
757 | } |
758 | } else { |
759 | var = v_new(); |
760 | eval(var); |
761 | alloc = 1; |
762 | } |
763 | |
764 | // zero-length or non array |
765 | if (var->type != V_ARRAY || v_asize(var) == 0) { |
766 | if (alloc) { |
767 | v_free(var); |
768 | v_detach(var); |
769 | } |
770 | return 0; |
771 | } |
772 | // |
773 | el = v_elem(var, 0); |
774 | if (el && el->type == V_ARRAY) { |
775 | style = 1; // nested --- [ [x1,y1], [x2,y2], ... ] |
776 | } |
777 | // else |
778 | // style = 0; // 2x2 or 1x --- [ x1, y1, x2, y2, ... ] |
779 | |
780 | // error check |
781 | if (style == 1) { |
782 | if (v_asize(el) != 2) { |
783 | err_parsepoly(-1, 1); |
784 | if (alloc) { |
785 | v_free(var); |
786 | v_detach(var); |
787 | } |
788 | return 0; |
789 | } |
790 | |
791 | count = v_asize(var); |
792 | } else if (style == 0) { |
793 | if ((v_asize(var) % 2) != 0) { |
794 | err_parsepoly(-1, 2); |
795 | if (alloc) { |
796 | v_free(var); |
797 | v_detach(var); |
798 | } |
799 | return 0; |
800 | } |
801 | |
802 | count = v_asize(var) >> 1; |
803 | } |
804 | // build array |
805 | *poly_pp = poly = malloc(sizeof(ipt_t) * count); |
806 | |
807 | if (style == 1) { |
808 | int i; |
809 | |
810 | for (i = 0; i < count; i++) { |
811 | // get point |
812 | el = v_elem(var, i); |
813 | |
814 | // error check |
815 | if (el->type != V_ARRAY) { |
816 | err_parsepoly(i, 3); |
817 | } else if (v_asize(el) != 2) { |
818 | err_parsepoly(i, 4); |
819 | } |
820 | if (prog_error) { |
821 | break; |
822 | } |
823 | // store point |
824 | poly[i].x = v_getint(v_elem(el, 0)); |
825 | poly[i].y = v_getint(v_elem(el, 1)); |
826 | } |
827 | } else if (style == 0) { |
828 | int i, j; |
829 | |
830 | for (i = j = 0; i < count; i++, j += 2) { |
831 | // error check |
832 | if (prog_error) { |
833 | break; |
834 | } |
835 | // store point |
836 | poly[i].x = v_getint(v_elem(var, j)); |
837 | poly[i].y = v_getint(v_elem(var, j + 1)); |
838 | } |
839 | } |
840 | // clean-up |
841 | if (prog_error) { |
842 | free(poly); |
843 | *poly_pp = NULL; |
844 | count = 0; |
845 | } |
846 | if (alloc) { |
847 | v_free(var); |
848 | v_detach(var); |
849 | } |
850 | |
851 | return count; |
852 | } |
853 | |
854 | /* |
855 | * destroy a parameter-table which was created by par_getparlist |
856 | */ |
857 | void par_freepartable(par_t **ptable_pp, int pcount) { |
858 | par_t *ptable = *ptable_pp; |
859 | if (ptable) { |
860 | for (int i = 0; i < pcount; i++) { |
861 | if (ptable[i].flags & PAR_BYVAL) { |
862 | v_free(ptable[i].var); |
863 | v_detach(ptable[i].var); |
864 | } |
865 | } |
866 | free(ptable); |
867 | } |
868 | *ptable_pp = NULL; |
869 | } |
870 | |
871 | /* |
872 | * builds a parameter table |
873 | * |
874 | * ptable_pp = pointer to an ptable |
875 | * valid_sep = valid separators (';) |
876 | * |
877 | * returns the number of the parameters, OR, -1 on error |
878 | * |
879 | * YOU MUST FREE THAT TABLE BY USING par_freepartable() |
880 | * IF THERE IS NO ERROR, CALL TO par_freepartable IS NOT NEEDED |
881 | */ |
882 | int par_getpartable(par_t **ptable_pp, const char *valid_sep) { |
883 | bcip_t ofs; |
884 | char vsep[8]; |
885 | |
886 | // initialize |
887 | var_t *par = NULL; |
888 | byte last_sep = 0; |
889 | int pcount = 0; |
890 | par_t *ptable = *ptable_pp = malloc(sizeof(par_t) * MAX_PARAM); |
891 | |
892 | if (valid_sep) { |
893 | strlcpy(vsep, valid_sep, sizeof(vsep)); |
894 | } else { |
895 | strlcpy(vsep, "," , sizeof(vsep)); |
896 | } |
897 | |
898 | // start |
899 | byte ready = 0; |
900 | do { |
901 | switch (code_peek()) { |
902 | case kwTYPE_EOC: |
903 | case kwTYPE_LINE: |
904 | case kwUSE: |
905 | case kwTYPE_LEVEL_END: // end of parameters |
906 | ready = 1; |
907 | break; |
908 | case kwTYPE_SEP: // separator |
909 | code_skipnext(); |
910 | |
911 | // check parameters separator |
912 | if (strchr(vsep, (last_sep = code_getnext())) == NULL) { |
913 | if (strlen(vsep) <= 1) { |
914 | err_missing_comma(); |
915 | } else { |
916 | err_syntaxsep(vsep); |
917 | } |
918 | par_freepartable(ptable_pp, pcount); |
919 | return -1; |
920 | } |
921 | // update par.next_sep |
922 | if (pcount) { |
923 | ptable[pcount - 1].next_sep = last_sep; |
924 | } |
925 | break; |
926 | case kwTYPE_VAR: // variable |
927 | ofs = prog_ip; // store IP |
928 | |
929 | ptable[pcount].flags = 0; |
930 | ptable[pcount].prev_sep = last_sep; |
931 | ptable[pcount].next_sep = 0; |
932 | |
933 | if (code_isvar()) { |
934 | // push parameter |
935 | ptable[pcount].var = code_getvarptr(); |
936 | if (++pcount == MAX_PARAM) { |
937 | par_freepartable(ptable_pp, pcount); |
938 | err_parfmt(__FILE__); |
939 | return -1; |
940 | } |
941 | break; |
942 | } |
943 | // Its no a single variable, its an expression |
944 | // restore IP |
945 | prog_ip = ofs; |
946 | |
947 | // no 'break' here |
948 | default: |
949 | // default --- expression (BYVAL ONLY) |
950 | par = v_new(); |
951 | eval(par); |
952 | if (!prog_error) { |
953 | // push parameter |
954 | ptable[pcount].var = par; |
955 | ptable[pcount].flags = PAR_BYVAL; |
956 | if (++pcount == MAX_PARAM) { |
957 | par_freepartable(ptable_pp, pcount); |
958 | err_parfmt(__FILE__); |
959 | return -1; |
960 | } |
961 | } else { |
962 | v_free(par); |
963 | v_detach(par); |
964 | par_freepartable(ptable_pp, pcount); |
965 | return -1; |
966 | } |
967 | } |
968 | |
969 | } while (!ready); |
970 | |
971 | return pcount; |
972 | } |
973 | |
974 | /* |
975 | */ |
976 | int par_massget_type_check(char fmt, par_t *par) { |
977 | switch (fmt) { |
978 | case 'S': |
979 | case 's': |
980 | return (par->var->type == V_STR); |
981 | case 'I': |
982 | case 'i': |
983 | case 'F': |
984 | case 'f': |
985 | return (par->var->type == V_NUM || par->var->type == V_INT); |
986 | } |
987 | return 0; |
988 | } |
989 | |
990 | /* |
991 | * Parsing parameters with scanf-style |
992 | * returns the parameter-count or -1 (error) |
993 | * |
994 | * Format: |
995 | * -------- |
996 | * capital character = the parameter is required |
997 | * small character = optional parameter |
998 | * |
999 | * I = var_int_t (var_int_t*) |
1000 | * F = var_num_t (var_num_t*) |
1001 | * S = string (char*) |
1002 | * P = variable's ptr (var_t*) |
1003 | * |
1004 | * Example: |
1005 | * -------- |
1006 | * var_int_t i1, i2 = -1; // -1 is the default value for i2 |
1007 | * char *s1 = NULL; // NULL is the default value for s1 |
1008 | * var_t *v = NULL; // NULL is the default value for v |
1009 | * |
1010 | * // the first integer is required, the second is optional |
1011 | * // the string is optional too |
1012 | * pc = par_massget("Iis", &i1, &i2, &s1, &v); |
1013 | * |
1014 | * if ( pc != -1 ) { |
1015 | * // no error; also, you can use prog_error because par_massget() will call rt_raise() on error |
1016 | * printf("required integer = %d\n", i1); |
1017 | * |
1018 | * // if there is no optional parameters, the default value will be returned |
1019 | * if ( i2 != -1 ) printf("optional integer found = %d\n", i2); |
1020 | * if ( s1 ) printf("optional string found = %s\n", s1); |
1021 | * if ( v ) { printf("optional variable's ptr found"); v_free(v); } |
1022 | * } |
1023 | * |
1024 | * pfree2(s1, v); |
1025 | */ |
1026 | int par_massget(const char *fmt, ...) { |
1027 | // get ptable |
1028 | par_t *ptable; |
1029 | int pcount = par_getpartable(&ptable, NULL); |
1030 | if (pcount == -1) { |
1031 | free(ptable); |
1032 | return -1; |
1033 | } |
1034 | |
1035 | // count pars |
1036 | char *fmt_p = (char *)fmt; |
1037 | int optcount = 0; |
1038 | int rqcount = 0; |
1039 | while (*fmt_p) { |
1040 | if (*fmt_p >= 'a') { |
1041 | optcount++; |
1042 | } else { |
1043 | rqcount++; |
1044 | } |
1045 | fmt_p++; |
1046 | } |
1047 | |
1048 | if (rqcount > pcount) { |
1049 | err_parfmt(fmt); |
1050 | } |
1051 | else { |
1052 | // parse |
1053 | int curpar = 0; |
1054 | int opt = 0; |
1055 | int ignore = 0; |
1056 | va_list ap; |
1057 | |
1058 | va_start(ap, fmt); |
1059 | fmt_p = (char *)fmt; |
1060 | while (*fmt_p) { |
1061 | if (*fmt_p >= 'a' && optcount && |
1062 | ((curpar < pcount) ? par_massget_type_check(*fmt_p, &ptable[curpar]) : 0)) { |
1063 | (optcount--, opt = 1, ignore = 0); |
1064 | } |
1065 | else if (*fmt_p >= 'a' && optcount) { |
1066 | (optcount--, opt = 0, ignore = 1); |
1067 | } |
1068 | else if (*fmt_p < 'a' && rqcount) { |
1069 | (rqcount--, opt = 0, ignore = 0); |
1070 | } |
1071 | else { |
1072 | err_parfmt(fmt); |
1073 | break; |
1074 | } |
1075 | |
1076 | if (pcount <= curpar && ignore == 0) { |
1077 | err_parfmt(fmt); |
1078 | break; |
1079 | } |
1080 | |
1081 | char **s; |
1082 | var_int_t *i; |
1083 | var_num_t *f; |
1084 | var_t **vt; |
1085 | |
1086 | switch (*fmt_p) { |
1087 | case 's': |
1088 | // optional string |
1089 | if (!opt) { |
1090 | // advance ap to next position |
1091 | va_arg(ap, char **); |
1092 | break; |
1093 | } |
1094 | case 'S': |
1095 | // string |
1096 | s = va_arg(ap, char **); |
1097 | *s = strdup(v_getstr(ptable[curpar].var)); |
1098 | curpar++; |
1099 | break; |
1100 | case 'i': |
1101 | // optional integer |
1102 | if (!opt) { |
1103 | va_arg(ap, var_int_t *); |
1104 | break; |
1105 | } |
1106 | case 'I': |
1107 | // integer |
1108 | i = va_arg(ap, var_int_t *); |
1109 | *i = v_getint(ptable[curpar].var); |
1110 | curpar++; |
1111 | break; |
1112 | case 'f': |
1113 | // optional real (var_num_t) |
1114 | if (!opt) { |
1115 | va_arg(ap, var_num_t *); |
1116 | break; |
1117 | } |
1118 | case 'F': |
1119 | // real (var_num_t) |
1120 | f = va_arg(ap, var_num_t *); |
1121 | *f = v_getreal(ptable[curpar].var); |
1122 | curpar++; |
1123 | break; |
1124 | case 'p': |
1125 | // optional variable |
1126 | if (!opt) { |
1127 | va_arg(ap, var_t **); |
1128 | break; |
1129 | } |
1130 | case 'P': |
1131 | // variable |
1132 | vt = va_arg(ap, var_t **); |
1133 | if (ptable[curpar].flags == 0)// byref |
1134 | *vt = ptable[curpar].var; |
1135 | else { |
1136 | err_syntax(-1, "%P" ); |
1137 | break; |
1138 | } |
1139 | curpar++; |
1140 | break; |
1141 | } |
1142 | |
1143 | fmt_p++; |
1144 | } |
1145 | va_end(ap); |
1146 | } |
1147 | |
1148 | par_freepartable(&ptable, pcount); |
1149 | if (prog_error) { |
1150 | return -1; |
1151 | } |
1152 | return pcount; |
1153 | } |
1154 | |