1// This file is part of SmallBASIC
2//
3// SmallBASIC run-time errors
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/smbas.h"
11#include "common/pproc.h"
12#include "common/messages.h"
13#include "common/var.h"
14#include <string.h>
15#include <errno.h>
16
17void err_title_msg(const char *seg, const char *file, int line) {
18 gsb_last_line = line;
19 gsb_last_error = prog_error;
20 strlcpy(gsb_last_file, file, sizeof(gsb_last_file));
21 log_printf("\n\033[0m");
22 log_printf("\033[7m * %s-%s %s:%d * \033[0m\n\n", seg, WORD_ERROR_AT, file, line);
23}
24
25void err_detail_msg(const char *descr) {
26 strlcpy(prog_errmsg, descr, sizeof(prog_errmsg));
27 strlcpy(gsb_last_errmsg, prog_errmsg, sizeof(gsb_last_errmsg));
28 log_printf("\033[4m%s:\033[0m\n%s\n", WORD_DESCRIPTION, descr);
29 log_printf("\033[80m\033[0m");
30}
31
32void va_err_detail_msg(const char *format, va_list args, unsigned size) {
33 char *buff = malloc(size + 1);
34 buff[0] = '\0';
35 vsnprintf(buff, size + 1, format, args);
36 buff[size] = '\0';
37 err_detail_msg(buff);
38 free(buff);
39}
40
41void err_stack_msg() {
42 int header = 0;
43
44 // log the stack trace
45 for (int i_stack = prog_stack_count; i_stack > 0; i_stack--) {
46 stknode_t node = prog_stack[i_stack - 1];
47 switch (node.type) {
48 case 0xFF:
49 case kwBYREF:
50 case kwTYPE_CRVAR:
51 // ignore these types
52 break;
53
54 default:
55 for (int i_kw = 0; keyword_table[i_kw].name[0] != '\0'; i_kw++) {
56 if (node.type == keyword_table[i_kw].code) {
57 if (!header) {
58 log_printf("\033[4mStack:\033[0m\n");
59 header = 1;
60 }
61 dev_log_stack(keyword_table[i_kw].name, node.type, node.line);
62 log_printf(" %s: %d", keyword_table[i_kw].name, node.line);
63 break;
64 }
65 }
66 }
67 }
68}
69
70/**
71 * raise a pre-execution/compiler error
72 */
73void sc_raise(const char *format, ...) {
74 if (!comp_error) {
75 comp_error = 1;
76
77 va_list args;
78 va_start(args, format);
79 unsigned size = vsnprintf(NULL, 0, format, args);
80 va_end(args);
81
82 if (comp_bc_sec) {
83 err_title_msg(WORD_COMP, comp_bc_sec, comp_line);
84 }
85 if (size) {
86 va_start(args, format);
87 va_err_detail_msg(format, args, size);
88 va_end(args);
89 }
90 }
91}
92
93/**
94 * run-time error
95 */
96void rt_raise(const char *format, ...) {
97 if (!gsb_last_error && ctask && !prog_error && prog_source) {
98 prog_error = errRuntime;
99
100 va_list args;
101 va_start(args, format);
102 unsigned size = vsnprintf(NULL, 0, format, args);
103 va_end(args);
104
105 err_title_msg(WORD_RTE, prog_file, prog_line);
106 if (size) {
107 va_start(args, format);
108 va_err_detail_msg(format, args, size);
109 va_end(args);
110 }
111 err_stack_msg();
112 }
113}
114
115/**
116 * run-time syntax error
117 */
118void err_syntax(int keyword, const char *fmt) {
119 if (!gsb_last_error && prog_source) {
120 char *buff = malloc(SB_TEXTLINE_SIZE + 1);
121 char *fmt_p = (char *)fmt;
122
123 prog_error = errSyntax;
124 buff[0] = '\0';
125 if (keyword != -1) {
126 if (kw_getfuncname(keyword, buff) ||
127 kw_getprocname(keyword, buff) ||
128 kw_getcmdname(keyword, buff)) {
129 strcpy(buff, " ");
130 }
131 }
132
133 while (*fmt_p) {
134 if (*fmt_p == '%') {
135 fmt_p++;
136 switch (*fmt_p) {
137 case 'S':
138 strcat(buff, "STRING");
139 break;
140 case 'I':
141 strcat(buff, "INTEGER");
142 break;
143 case 'F':
144 strcat(buff, "REAL");
145 break;
146 case 'P':
147 strcat(buff, "VARIABLE");
148 break;
149 case 'G':
150 strcat(buff, "FUNC");
151 break;
152 case 'N':
153 strcat(buff, "NUM");
154 break;
155 }
156 } else {
157 int len = strlen(buff);
158 buff[len] = *fmt_p;
159 buff[len + 1] = '\0';
160 }
161 fmt_p++;
162 }
163
164 err_title_msg(WORD_RTE, prog_file, prog_line);
165 err_detail_msg(ERR_SYNTAX);
166 err_stack_msg();
167 log_printf("Expected:");
168 log_printf(buff);
169 log_printf("\n");
170 free(buff);
171 }
172}
173
174void err_missing_rp() {
175 rt_raise(ERR_MISSING_RP);
176}
177
178void err_matdim() {
179 rt_raise(ERR_MATRIX_DIM);
180}
181
182void err_noargs() {
183 rt_raise(ERR_NO_ARGS);
184}
185
186void err_syntax_unknown() {
187 rt_raise(ERR_SYNTAX);
188}
189
190void err_parm_num(int found, int expected) {
191 rt_raise(ERR_PARAM_NUM, found, expected);
192}
193
194void err_parm_limit(int count) {
195 rt_raise(MSG_PARNUM_LIMIT, count);
196}
197
198void err_stackoverflow(void) {
199 rt_raise(ERR_STACK_OVERFLOW);
200}
201
202void err_stackunderflow(void) {
203 rt_raise(ERR_STACK_UNDERFLOW);
204}
205
206// generic stack error
207void err_stackmess() {
208 rt_raise(ERR_STACK);
209}
210
211void err_arrmis_lp(void) {
212 rt_raise(ERR_ARRAY_MISSING_LP);
213}
214
215void err_arrmis_rp(void) {
216 rt_raise(ERR_ARRAY_MISSING_RP);
217}
218
219void err_arridx(int i, int m) {
220 err_throw(ERR_ARRAY_RANGE, i, m);
221}
222
223void err_typemismatch(void) {
224 rt_raise(ERR_TYPE);
225}
226
227// parameter with wrong value
228void err_argerr(void) {
229 rt_raise(ERR_PARAM);
230}
231
232void err_varisarray(void) {
233 err_throw(EVAL_VAR_IS_ARRAY);
234}
235
236void err_varisnotarray(void) {
237 err_throw(EVAL_VAR_IS_NOT_ARRAY);
238}
239
240void err_vararridx(int i, int m) {
241 err_throw(ERR_ARRAY_RANGE, i, m);
242}
243
244void err_varnotnum(void) {
245 rt_raise(EVAL_NOT_A_NUM);
246}
247
248void err_evsyntax(void) {
249 rt_raise(EVAL_SYNTAX);
250}
251
252void err_evtype(void) {
253 rt_raise(EVAL_TYPE);
254}
255
256void err_evargerr(void) {
257 rt_raise(EVAL_PARAM);
258}
259
260void err_unsup(void) {
261 rt_raise(ERR_UNSUPPORTED);
262}
263
264void err_const(void) {
265 rt_raise(ERR_CONST);
266}
267
268void err_notavar(void) {
269 rt_raise(ERR_NOT_A_VAR);
270}
271
272void err_out_of_range(void) {
273 err_throw(ERR_RANGE);
274}
275
276void err_missing_sep(void) {
277 rt_raise(ERR_MISSING_SEP_OR_PAR);
278}
279
280void err_division_by_zero(void) {
281 err_throw(ERR_DIVZERO);
282}
283
284void err_matop(void) {
285 rt_raise(ERR_OPERATOR);
286}
287
288void err_matsig(void) {
289 rt_raise(ERR_MATSIG);
290}
291
292void err_missing_lp(void) {
293 rt_raise(ERR_MISSING_LP);
294}
295
296void err_parfmt(const char *fmt) {
297 rt_raise(ERR_PARFMT, fmt);
298}
299
300// UDP/F: parameter is 'by reference' so const not allowed
301void err_parm_byref(int n) {
302 rt_raise(ERR_BYREF, n);
303}
304
305void err_fopen(void) {
306 rt_raise(ERR_BAD_FILE_HANDLE);
307}
308
309// no separator found
310void err_syntaxsep(const char *seps) {
311 rt_raise(ERR_SEP_FMT, seps);
312}
313
314void err_missing_comma(void) {
315 rt_raise(ERR_SEP_FMT, ",");
316}
317
318void err_parsepoly(int idx, int mark) {
319 rt_raise(ERR_POLY, idx, mark);
320}
321
322void err_bfn_err(long code) {
323 rt_raise(ERR_CRITICAL_MISSING_FUNC, code);
324}
325
326void err_pcode_err(long pcode) {
327 rt_raise(ERR_CRITICAL_MISSING_PROC, pcode);
328}
329
330void err_run_err(const char *file) {
331 rt_raise(ERR_RUN_FILE, file);
332}
333
334void err_ref_var() {
335 rt_raise(ERR_REF_VAR);
336}
337
338void err_ref_circ_var() {
339 rt_raise(ERR_REF_CIRC_VAR);
340}
341
342void err_array() {
343 rt_raise(MSG_ARRAY_SE);
344}
345
346void err_form_input() {
347 err_throw(ERR_FORM_INPUT);
348}
349
350void err_memory() {
351 rt_raise(ERR_MEMORY);
352}
353
354void err_network() {
355 rt_raise(ERR_NETWORK);
356}
357
358/**
359 * the DONE message
360 */
361void inf_done() {
362 dev_printf("\n\033[0m\033[80m\033[7m * %s * \033[0m\n", WORD_DONE);
363}
364
365// the BREAK message
366void inf_break(int pline) {
367 gsb_last_line = pline;
368 gsb_last_error = prog_error;
369 strcpy(gsb_last_file, prog_file);
370 sprintf(gsb_last_errmsg, "%s %d", WORD_BREAK_AT, pline);
371
372 dev_settextcolor(15, 0);
373 log_printf("\n\033[0m\033[80m\033[7m * %s %d * \033[0m\n", WORD_BREAK_AT, pline);
374}
375
376// assign error to variable or match with next expression
377int err_throw_catch(const char *err, var_t **catch_var) {
378 var_t *arg;
379 var_t v_catch;
380 int caught = 1;
381 switch (code_peek()) {
382 case kwTYPE_VAR:
383 arg = code_getvarptr();
384 v_setstr(arg, err);
385 *catch_var = arg;
386 break;
387 case kwTYPE_STR:
388 v_init(&v_catch);
389 eval(&v_catch);
390 // catch is conditional on matching error
391 caught = (v_catch.type == V_STR && strstr(err, v_catch.v.p.ptr) != NULL);
392 v_free(&v_catch);
393 break;
394 case kwTYPE_EOC:
395 case kwTYPE_LINE:
396 break;
397 default:
398 rt_raise(ERR_INVALID_CATCH);
399 break;
400 }
401 return caught;
402}
403
404// returns the position for the most TRY in the stack
405int err_find_try(int position) {
406 int result = -1;
407 while (position) {
408 if (prog_stack[--position].type == kwTRY) {
409 result = position;
410 break;
411 }
412 }
413 return result;
414}
415
416// throw string
417void err_throw_str(const char *err) {
418 int caught = 0;
419 int throw_sp = prog_stack_count;
420 int try_sp = err_find_try(throw_sp);
421 int trace_done = 0;
422 var_t *catch_var = NULL;
423
424 if (!prog_error && try_sp != -1) {
425 bcip_t catch_ip = prog_stack[try_sp].x.vtry.catch_ip;
426 // position after kwCATCH
427 code_jump(catch_ip + 1);
428
429 // skip "end try" address
430 code_getaddr();
431
432 // fetch next catch in the current block
433 catch_ip = code_getaddr();
434
435 caught = err_throw_catch(err, &catch_var);
436 int reset_sp = try_sp;
437
438 while (!caught && (catch_ip != INVALID_ADDR || try_sp != -1)) {
439 // find in the current block
440 while (!caught && catch_ip != INVALID_ADDR) {
441 code_jump(catch_ip + 1);
442 code_getaddr();
443 catch_ip = code_getaddr();
444 caught = err_throw_catch(err, &catch_var);
445 }
446 // find in the next outer block
447 if (!caught && try_sp != -1) {
448 try_sp = err_find_try(try_sp);
449 if (try_sp != -1) {
450 reset_sp = try_sp;
451 catch_ip = prog_stack[try_sp].x.vtry.catch_ip;
452 code_jump(catch_ip + 1);
453 code_getaddr();
454 catch_ip = code_getaddr();
455 caught = err_throw_catch(err, &catch_var);
456 }
457 }
458 }
459
460 if (!caught) {
461 err_title_msg(WORD_RTE, prog_file, prog_line);
462 err_detail_msg(err);
463 err_stack_msg();
464 trace_done = 1;
465 }
466
467 // cleanup the stack
468 int sp;
469 int nodeType = 0;
470 for (sp = throw_sp; sp > reset_sp; sp--) {
471 nodeType = code_pop_and_free();
472 }
473 if (nodeType != kwTRY) {
474 err_stackmess();
475 }
476 }
477 if (!caught) {
478 prog_error = errRuntime;
479 if (!trace_done) {
480 err_title_msg(WORD_RTE, prog_file, prog_line);
481 err_detail_msg(err);
482 err_stack_msg();
483 }
484 } else {
485 stknode_t *node = code_push(kwCATCH);
486 node->x.vcatch.catch_var = catch_var;
487 prog_error = errThrow;
488 }
489}
490
491// throw internal error
492void err_throw(const char *fmt, ...) {
493 if (!gsb_last_error && prog_source) {
494 va_list ap;
495 va_start(ap, fmt);
496 char *err = malloc(SB_TEXTLINE_SIZE + 1);
497 vsprintf(err, fmt, ap);
498 va_end(ap);
499 err_throw_str(err);
500 free(err);
501 }
502}
503
504// throw user error
505void cmd_throw() {
506 if (!gsb_last_error) {
507 var_t v_throw;
508 v_init(&v_throw);
509 const char *err = "";
510 byte code = code_peek();
511 if (code != kwTYPE_EOC && code != kwTYPE_LINE) {
512 eval(&v_throw);
513 if (v_throw.type == V_STR) {
514 err = v_throw.v.p.ptr;
515 }
516 }
517 err_throw_str(err);
518 v_free(&v_throw);
519 }
520}
521
522void err_file(uint32_t code) {
523 if (!gsb_last_error) {
524 char *err = malloc(SB_TEXTLINE_SIZE + 1);
525 sprintf(err, FSERR_FMT, code, strerror(code));
526 strupper(err);
527 err_throw_str(err);
528 free(err);
529 }
530}
531
532void err_file_not_found() {
533 err_throw(FSERR_NOT_FOUND);
534}
535
536int err_handle_error(const char *err, var_p_t var) {
537 int result;
538 if (prog_error == errThrow) {
539 result = 1;
540 } else if (prog_error) {
541 rt_raise(err);
542 result = 1;
543 } else {
544 result = 0;
545 }
546 if (result && var) {
547 v_free(var);
548 }
549 return result;
550}
551