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 */
25time_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 */
43int 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 */
109void 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 */
132void 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
154void 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 */
169void 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 */
191void 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 */
229void print_var(var_t *v) {
230 pv_writevar(v, PV_CONSOLE, 0);
231}
232
233/*
234 * write a variable to a file
235 */
236void 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 */
243void logprint_var(var_t *v) {
244 pv_writevar(v, PV_LOG, 0);
245}
246
247/*
248 * skip parameter
249 */
250void 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 */
323void 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 */
339var_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 */
366var_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 */
377void 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 */
398var_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 */
412var_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
423var_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
436var_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
455var_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 */
484int 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 */
499void 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 */
510void 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 */
521void 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 */
532pt_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 */
584ipt_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 */
636int 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 */
746int 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 */
857void 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 */
882int 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 */
976int 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 */
1026int 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