/*************************************************************************** gb_error.c Errors management routines (c) 2000-2007 Benoit Minisini This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ***************************************************************************/ #define __GB_ERROR_C #include "gb_common.h" #include #include #include "gb_buffer.h" #include "gb_error.h" #include "gbx_debug.h" #include "gbx_exec.h" #include "gbx_api.h" //#define DEBUG_ERROR 1 ERROR_CONTEXT *ERROR_current = NULL; ERROR_INFO ERROR_last = { 0 }; bool ERROR_backtrace = FALSE; static int _lock = 0; static const char *_message[65] = { /* 0 E_UNKNOWN */ "Unknown error", /* 1 E_MEMORY */ "Out of memory", /* 2 E_CLASS */ ".Cannot load class '&1': &2&3", /* 3 E_STACK */ "Stack overflow", /* 4 E_NEPARAM */ "Not enough arguments", /* 5 E_TMPARAM */ "Too many arguments", /* 6 E_TYPE */ ".Type mismatch: wanted &1, got &2 instead", /* 7 E_OVERFLOW */ "Overflow", /* 8 E_ILLEGAL */ "Illegal instruction", /* 9 E_NFUNC */ "Not a function", /* 10 E_CSTATIC */ ".Class '&1' is not creatable", /* 11 E_NSYMBOL */ ".Unknown symbol '&1' in class '&2'", /* 12 E_NOBJECT */ "Not an object", /* 13 E_NULL */ "Null object", /* 14 E_STATIC */ ".'&1.&2' is static", /* 15 E_NREAD */ ".'&1.&2' is write only", /* 16 E_NWRITE */ ".'&1.&2' is read only", /* 17 E_NPROPERTY */ ".'&1.&2' is not a property", /* 18 E_NRETURN */ "No return value", /* 19 E_MATH*/ "Mathematic error", /* 20 E_ARG */ "Bad argument", /* 21 E_BOUND */ "Out of bounds", /* 22 E_NDIM */ "Bad number of dimensions", /* 23 E_NARRAY */ "Not an array", /* 24 E_MAIN */ "No startup method", /* 25 E_NNEW */ "No instanciation method", /* 26 E_ZERO */ "Division by zero", /* 27 E_LIBRARY */ ".Cannot load component '&1': &2", /* 28 E_EVENT */ ".Bad event handler in &1.&2(): &3", /* 29 E_IOBJECT */ "Invalid object", /* 30 E_ENUM */ "Not an enumeration", /* 31 E_UCONV */ "Unsupported string conversion", /* 32 E_CONV */ "Bad string conversion", /* 33 E_DATE */ "Invalid date", /* 34 E_BADPATH */ "Invalid path", /* 35 E_OPEN */ ".Cannot open file '&1': &2", /* 36 E_PROJECT */ ".Bad project file: line &1: &2", /* 37 E_FULL */ "Device is full", /* 38 E_EXIST */ "File already exists", /* &1 */ /* 39 E_EOF */ "End of file", /* 40 E_FORMAT */ "Bad format string", /* 41 E_DYNAMIC */ ".'&1.&2' is not static", /* 42 E_SYSTEM */ ".System error #&1: &2", /* 43 E_ACCESS */ "Access forbidden", /* 44 E_TOOLONG */ "File name is too long", /* 45 E_NEXIST */ "File or directory does not exist", /* &1 */ /* 46 E_DIR */ "File is a directory", /* &1 */ /* 47 E_READ */ "Read error", /* 48 E_WRITE */ "Write error", /* 49 E_NDIR */ ".Not a directory: &1", /* 50 E_REGEXP */ ".Bad regular expression: &1", /* 51 E_ARCH */ ".Bad archive: &1: &2", /* 52 E_REGISTER */ ".Cannot register class '&1'", /* 53 E_CLOSED */ "Stream is closed", /* 54 E_VIRTUAL */ "Bad use of virtual class", /* 55 E_STOP */ "STOP instruction encountered", /* 56 E_STRING */ "Too many simultaneous new strings", /* 57 E_EVAL */ ".Bad expression: &1", /* 58 E_LOCK */ "File is locked", /* 59 E_PARENT */ "No parent class", /* 60 E_EXTLIB */ ".Cannot find dynamic library '&1': &2", /* 61 E_EXTSYM */ ".Cannot find symbol '&2' in dynamic library '&1'", /* 62 E_BYREF */ "Argument cannot be passed by reference", /* 63 E_OVERRIDE */ ".'&1.&2' is badly overridden in class '&3'", NULL }; void ERROR_lock() { _lock++; } void ERROR_unlock() { _lock--; } void ERROR_reset(ERROR_INFO *info) { info->code = 0; if (info->free) { STRING_unref(&info->msg); info->free = FALSE; } info->msg = NULL; if (info->backtrace) { #if DEBUG_ERROR fprintf(stderr, "ERROR_reset: DEBUG_free_backtrace: <<%p>>\n", info->backtrace); #endif DEBUG_free_backtrace(&info->backtrace); info->backtrace = NULL; } } void ERROR_clear() { if (_lock) { #if DEBUG_ERROR fprintf(stderr, "ERROR_clear: (%p) *LOCKED*\n", ERROR_current); #endif return; } #if DEBUG_ERROR fprintf(stderr, "ERROR_clear: (%p)\n", ERROR_current); #endif ERROR_reset(&ERROR_current->info); } void ERROR_enter(ERROR_CONTEXT *err) { err->prev = ERROR_current; err->info.code = 0; err->info.free = FALSE; err->info.msg = NULL; err->info.backtrace = NULL; ERROR_current = err; #if DEBUG_ERROR fprintf(stderr, "ERROR_enter: (%p)\n", ERROR_current); fprintf(stderr, ">> ERROR_enter"); { ERROR_CONTEXT *e = err; while (e) { fprintf(stderr, " -> %p", e); e = e->prev; } fprintf(stderr, "\n"); } #endif } void ERROR_leave(ERROR_CONTEXT *err) { if (err->prev == ERROR_LEAVE_DONE) return; #if DEBUG_ERROR fprintf(stderr, "<< ERROR_leave"); { ERROR_CONTEXT *e = err; while (e) { fprintf(stderr, " -> %p", e); e = e->prev; } fprintf(stderr, " : %d %s\n", err->info.code, err->info.msg); } #endif //if (!err->prev) // BREAKPOINT(); ERROR_current = err->prev; if (ERROR_current) { #if DEBUG_ERROR fprintf(stderr, "ERROR_leave: (%p)\n", ERROR_current); #endif ERROR_reset(&ERROR_current->info); ERROR_current->info = err->info; } else ERROR_reset(&err->info); err->prev = ERROR_LEAVE_DONE; //ERROR_reset(err); } void ERROR_propagate() { #if DEBUG_ERROR fprintf(stderr, "ERROR_propagate: %d %s\n", ERROR_current->info.code, ERROR_current->info.msg); #endif if (ERROR_current->ret) ERROR_leave(ERROR_current); longjmp(ERROR_current->env, 1); } const char *ERROR_get(void) { /* if (code > 0 && code < 256) return strerror(code); else return ERROR_Message[code - 256]; */ return strerror(errno); } static int get_message_length(const char *pattern, char *arg[]) { uchar c; int len = 0; for(;;) { c = *pattern++; if (!c) break; if (c == '&') { c = *pattern++; if (c >= '1' && c <= '4') len += strlen(arg[c - '1']); } else len++; } return len; } void ERROR_define(const char *pattern, char *arg[]) { uchar c; bool subst; char *msg = NULL; int len; ERROR_clear(); if ((intptr_t)pattern >= 0 && (intptr_t)pattern < 256) { ERROR_current->info.code = (int)(intptr_t)pattern; pattern = _message[(int)(intptr_t)pattern]; } else if ((intptr_t)pattern == E_ABORT) { ERROR_current->info.code = E_ABORT; pattern = ""; } else ERROR_current->info.code = E_CUSTOM; if (*pattern == '.' || ERROR_current->info.code == E_CUSTOM) //(arg) //(index(pattern, '&')) { if (*pattern == '.') pattern++; subst = FALSE; len = get_message_length(pattern, arg); if (len) { STRING_new(&msg, NULL, len); ERROR_current->info.msg = msg; ERROR_current->info.free = TRUE; for (;;) { c = *pattern++; if (c == 0) break; if (c == '&') { c = *pattern++; if (c >= '1' && c <= '4') { c -= '1'; len = strlen(arg[c]); memcpy(msg, arg[c], len); msg += len; } } else *msg++ = c; } *msg = 0; } } else { ERROR_current->info.msg = (char *)pattern; ERROR_current->info.free = FALSE; } //fprintf(stderr, "ERROR_define: %p %d %p '%s'\n", ERROR_current, ERROR_current->info.code, ERROR_current->info.msg, ERROR_current->info.msg); //STRING_add_char(&ERROR_current->info.msg, 0); ERROR_current->info.cp = CP; ERROR_current->info.fp = FP; ERROR_current->info.pc = PC; if (EXEC_debug || (ERROR_backtrace && (CP && CP->debug))) { //DEBUG_free_backtrace(&ERROR_current->info.backtrace); ERROR_current->info.backtrace = DEBUG_backtrace(); #if DEBUG_ERROR fprintf(stderr, "ERROR_define: (%p) DEBUG_backtrace: <<%p>>\n", ERROR_current, ERROR_current->info.backtrace); #endif } #if DEBUG_ERROR fprintf(stderr, "ERROR_define: %s\n", ERROR_current->info.msg); #endif } void THROW(int code, ...) { va_list args; int i; char *arg[4]; va_start(args, code); for (i = 0; i < 4; i++) arg[i] = va_arg(args, char *); ERROR_define((char *)(intptr_t)code, arg); PROPAGATE(); } void THROW_SYSTEM(int err, const char *path) { char buf[6]; switch(err) { case ENOENT: THROW(E_NEXIST, path); case EISDIR: THROW(E_DIR, path); case ENOTDIR: THROW(E_NDIR, path); case ENOMEM: THROW(E_MEMORY); case EACCES: THROW(E_ACCESS); case ENAMETOOLONG: THROW(E_TOOLONG); case ENOSPC: THROW(E_FULL); case EEXIST: THROW(E_EXIST, path); default: sprintf(buf, "%d", err); THROW(E_SYSTEM, buf, strerror(err)); } } void ERROR_panic(const char *error, ...) { va_list args; va_start(args, error); fflush(NULL); fprintf(stderr, "\n** INTERNAL ERROR **\n** "); vfprintf(stderr, error, args); putc('\n', stderr); if (ERROR_current->info.code) { ERROR_print(); } fprintf(stderr, "** Program aborting. Sorry! :-(\n"); /*abort();*/ _exit(1); } void ERROR_print_at(FILE *where, bool msgonly, bool newline) { if (!ERROR_current->info.code) return; if (!msgonly) { if (ERROR_current->info.cp && ERROR_current->info.fp && ERROR_current->info.pc) fprintf(where, "%s: ", DEBUG_get_position(ERROR_current->info.cp, ERROR_current->info.fp, ERROR_current->info.pc)); else fprintf(where, "ERROR: "); /*if (ERROR_current->info.code > 0 && ERROR_current->info.code < 256) fprintf(where, "%ld:", ERROR_current->info.code);*/ if (ERROR_current->info.code > 0) fprintf(where, "#%d: ", ERROR_current->info.code); } fprintf(where, "%s", ERROR_current->info.msg); if (newline) fputc('\n', where); } void ERROR_print(void) { static bool lock = FALSE; DEBUG_BACKTRACE *bt = ERROR_current->info.backtrace; ERROR_print_at(stderr, FALSE, TRUE); if (bt) { int i; for(i = 0; i < ARRAY_count(bt); i++) fprintf(stderr, "%d: %s\n", i, DEBUG_get_position(bt[i].cp, bt[i].fp, bt[i].pc)); } if (EXEC_main_hook_done && !EXEC_debug && EXEC_Hook.error && !lock) { lock = TRUE; GAMBAS_DoNotRaiseEvent = TRUE; HOOK(error)(ERROR_current->info.code, ERROR_current->info.msg, DEBUG_get_position(ERROR_current->info.cp, ERROR_current->info.fp, ERROR_current->info.pc)); lock = FALSE; } } void ERROR_save(ERROR_INFO *save) { ERROR_reset(save); *save = ERROR_current->info; CLEAR(&ERROR_current->info); //ERROR_reset(&ERROR_current->info); } void ERROR_restore(ERROR_INFO *save) { ERROR_reset(&ERROR_current->info); ERROR_current->info = *save; CLEAR(save); //ERROR_reset(save); } void ERROR_set_last(void) { ERROR_reset(&ERROR_last); ERROR_last = ERROR_current->info; if (ERROR_last.free) STRING_ref(ERROR_last.msg); ERROR_last.backtrace = DEBUG_copy_backtrace(ERROR_last.backtrace); #if DEBUG_ERROR fprintf(stderr, "ERROR_set_last: DEBUG_copy_backtrace: <<%p>>\n", ERROR_last.backtrace); #endif }