#include <sm/gen.h>
SM_RCSID("@(#)$Id: exc.c,v 1.49 2006/12/19 19:28:09 ca Exp $")
#include <ctype.h>
#include <string.h>
#include <sm/errstring.h>
#include <sm/exc.h>
#include <sm/heap.h>
#include <sm/string.h>
#include <sm/varargs.h>
#include <sm/io.h>
const char SmExcMagic[] = "sm_exc";
const char SmExcTypeMagic[] = "sm_exc_type";
void
sm_etype_printf(exc, stream)
SM_EXC_T *exc;
SM_FILE_T *stream;
{
size_t n = strlen(exc->exc_type->etype_argformat);
const char *p, *s;
char format;
for (p = exc->exc_type->etype_printcontext; *p != '\0'; ++p)
{
if (*p != '%')
{
(void) sm_io_putc(stream, SM_TIME_DEFAULT, *p);
continue;
}
++p;
if (*p == '\0')
{
(void) sm_io_putc(stream, SM_TIME_DEFAULT, '%');
break;
}
if (*p == '%')
{
(void) sm_io_putc(stream, SM_TIME_DEFAULT, '%');
continue;
}
format = '\0';
if (isalpha(*p))
{
format = *p++;
if (*p == '\0')
{
(void) sm_io_putc(stream, SM_TIME_DEFAULT, '%');
(void) sm_io_putc(stream, SM_TIME_DEFAULT,
format);
break;
}
}
if (isdigit(*p))
{
size_t i = *p - '0';
if (i < n)
{
switch (exc->exc_type->etype_argformat[i])
{
case 's':
case 'r':
s = exc->exc_argv[i].v_str;
if (s == NULL)
s = "(null)";
sm_io_fputs(stream, SM_TIME_DEFAULT, s);
continue;
case 'i':
sm_io_fprintf(stream,
SM_TIME_DEFAULT,
format == 'o' ? "%o"
: format == 'x' ? "%x"
: "%d",
exc->exc_argv[i].v_int);
continue;
case 'l':
sm_io_fprintf(stream,
SM_TIME_DEFAULT,
format == 'o' ? "%lo"
: format == 'x' ? "%lx"
: "%ld",
exc->exc_argv[i].v_long);
continue;
case 'e':
sm_exc_write(exc->exc_argv[i].v_exc,
stream);
continue;
}
}
}
(void) sm_io_putc(stream, SM_TIME_DEFAULT, '%');
if (format)
(void) sm_io_putc(stream, SM_TIME_DEFAULT, format);
(void) sm_io_putc(stream, SM_TIME_DEFAULT, *p);
}
}
static void
sm_etype_os_print __P((
SM_EXC_T *exc,
SM_FILE_T *stream));
static void
sm_etype_os_print(exc, stream)
SM_EXC_T *exc;
SM_FILE_T *stream;
{
int err = exc->exc_argv[0].v_int;
char *syscall = exc->exc_argv[1].v_str;
char *sysargs = exc->exc_argv[2].v_str;
if (sysargs)
sm_io_fprintf(stream, SM_TIME_DEFAULT, "%s: %s failed: %s",
sysargs, syscall, sm_errstring(err));
else
sm_io_fprintf(stream, SM_TIME_DEFAULT, "%s failed: %s", syscall,
sm_errstring(err));
}
const SM_EXC_TYPE_T SmEtypeOs =
{
SmExcTypeMagic,
"E:sm.os",
"isr",
sm_etype_os_print,
NULL,
};
const SM_EXC_TYPE_T SmEtypeErr =
{
SmExcTypeMagic,
"E:sm.err",
"r",
sm_etype_printf,
"%0",
};
static SM_EXC_T *sm_exc_vnew_x __P((const SM_EXC_TYPE_T *, va_list SM_NONVOLATILE));
static SM_EXC_T *
sm_exc_vnew_x(etype, ap)
const SM_EXC_TYPE_T *etype;
va_list SM_NONVOLATILE ap;
{
SM_EXC_T * volatile exc = NULL;
int volatile si = 0;
SM_VAL_T * volatile argv = NULL;
int i, argc;
SM_REQUIRE_ISA(etype, SmExcTypeMagic);
argc = strlen(etype->etype_argformat);
SM_TRY
{
exc = sm_malloc_x(sizeof(SM_EXC_T));
exc->sm_magic = SmExcMagic;
exc->exc_refcount = 1;
exc->exc_type = etype;
exc->exc_argv = NULL;
argv = sm_malloc_x(argc * sizeof(SM_VAL_T));
exc->exc_argv = argv;
for (i = 0; i < argc; ++i)
{
switch (etype->etype_argformat[i])
{
case 'i':
argv[i].v_int = SM_VA_ARG(ap, int);
break;
case 'l':
argv[i].v_long = SM_VA_ARG(ap, long);
break;
case 'e':
argv[i].v_exc = SM_VA_ARG(ap, SM_EXC_T*);
break;
case 's':
argv[i].v_str = SM_VA_ARG(ap, char*);
break;
case 'r':
SM_REQUIRE(etype->etype_argformat[i+1] == '\0');
argv[i].v_str = SM_VA_ARG(ap, char*);
break;
default:
sm_abort("sm_exc_vnew_x: bad argformat '%c'",
etype->etype_argformat[i]);
}
}
for (si = 0; si < argc; ++si)
{
switch (etype->etype_argformat[si])
{
case 's':
{
char *str = argv[si].v_str;
if (str != NULL)
argv[si].v_str = sm_strdup_x(str);
}
break;
case 'r':
{
char *fmt = argv[si].v_str;
if (fmt != NULL)
argv[si].v_str = sm_vstringf_x(fmt, ap);
}
break;
}
}
}
SM_EXCEPT(e, "*")
{
if (exc == NULL || argv == NULL)
{
for (i = 0; i < argc; ++i)
{
switch (etype->etype_argformat[i])
{
case 'i':
(void) SM_VA_ARG(ap, int);
break;
case 'l':
(void) SM_VA_ARG(ap, long);
break;
case 'e':
sm_exc_free(SM_VA_ARG(ap, SM_EXC_T*));
break;
case 's':
case 'r':
(void) SM_VA_ARG(ap, char*);
break;
}
}
}
else
{
for (i = 0; i < argc; ++i)
{
switch (etype->etype_argformat[i])
{
case 'e':
sm_exc_free(argv[i].v_exc);
break;
case 's':
case 'r':
if (i < si)
sm_free(argv[i].v_str);
break;
}
}
sm_free(argv);
}
sm_free(exc);
sm_exc_raise_x(e);
}
SM_END_TRY
return exc;
}
SM_EXC_T *
#if SM_VA_STD
sm_exc_new_x(
const SM_EXC_TYPE_T *etype,
...)
#else
sm_exc_new_x(etype, va_alist)
const SM_EXC_TYPE_T *etype;
va_dcl
#endif
{
SM_EXC_T *exc;
SM_VA_LOCAL_DECL
SM_VA_START(ap, etype);
exc = sm_exc_vnew_x(etype, ap);
SM_VA_END(ap);
return exc;
}
void
sm_exc_free(exc)
SM_EXC_T *exc;
{
if (exc == NULL)
return;
SM_REQUIRE(exc->sm_magic == SmExcMagic);
if (exc->exc_refcount == 0)
return;
if (--exc->exc_refcount == 0)
{
int i, c;
for (i = 0; (c = exc->exc_type->etype_argformat[i]) != '\0';
++i)
{
switch (c)
{
case 's':
case 'r':
sm_free(exc->exc_argv[i].v_str);
break;
case 'e':
sm_exc_free(exc->exc_argv[i].v_exc);
break;
}
}
exc->sm_magic = NULL;
sm_free(exc->exc_argv);
sm_free(exc);
}
}
bool
sm_exc_match(exc, pattern)
SM_EXC_T *exc;
const char *pattern;
{
if (exc == NULL)
return false;
SM_REQUIRE(exc->sm_magic == SmExcMagic);
return sm_match(exc->exc_type->etype_category, pattern);
}
void
sm_exc_write(exc, stream)
SM_EXC_T *exc;
SM_FILE_T *stream;
{
SM_REQUIRE_ISA(exc, SmExcMagic);
exc->exc_type->etype_print(exc, stream);
}
void
sm_exc_print(exc, stream)
SM_EXC_T *exc;
SM_FILE_T *stream;
{
SM_REQUIRE_ISA(exc, SmExcMagic);
exc->exc_type->etype_print(exc, stream);
(void) sm_io_putc(stream, SM_TIME_DEFAULT, '\n');
}
SM_EXC_HANDLER_T *SmExcHandler = NULL;
static SM_EXC_DEFAULT_HANDLER_T SmExcDefaultHandler = NULL;
void
sm_exc_newthread(h)
SM_EXC_DEFAULT_HANDLER_T h;
{
SmExcHandler = NULL;
SmExcDefaultHandler = h;
}
void SM_DEAD_D
sm_exc_raise_x(exc)
SM_EXC_T *exc;
{
SM_REQUIRE_ISA(exc, SmExcMagic);
if (SmExcHandler == NULL)
{
if (SmExcDefaultHandler != NULL)
{
SM_EXC_DEFAULT_HANDLER_T h;
h = SmExcDefaultHandler;
SmExcDefaultHandler = NULL;
(*h)(exc);
}
sm_exc_print(exc, smioerr);
exit(255);
}
if (SmExcHandler->eh_value == NULL)
SmExcHandler->eh_value = exc;
else
sm_exc_free(exc);
sm_longjmp_nosig(SmExcHandler->eh_context, 1);
}
void SM_DEAD_D
#if SM_VA_STD
sm_exc_raisenew_x(
const SM_EXC_TYPE_T *etype,
...)
#else
sm_exc_raisenew_x(etype, va_alist)
const SM_EXC_TYPE_T *etype;
va_dcl
#endif
{
SM_EXC_T *exc;
SM_VA_LOCAL_DECL
SM_VA_START(ap, etype);
exc = sm_exc_vnew_x(etype, ap);
SM_VA_END(ap);
sm_exc_raise_x(exc);
}