190792Sgshapiro/*
2261363Sgshapiro * Copyright (c) 2000-2002 Proofpoint, Inc. and its suppliers.
390792Sgshapiro *	All rights reserved.
490792Sgshapiro *
590792Sgshapiro * By using this file, you agree to the terms and conditions set
690792Sgshapiro * forth in the LICENSE file which can be found at the top level of
790792Sgshapiro * the sendmail distribution.
890792Sgshapiro *
990792Sgshapiro */
1090792Sgshapiro
1190792Sgshapiro#include <sm/gen.h>
12266692SgshapiroSM_RCSID("@(#)$Id: exc.c,v 1.50 2013-11-22 20:51:42 ca Exp $")
1390792Sgshapiro
1490792Sgshapiro/*
1590792Sgshapiro**  exception handling
1690792Sgshapiro**  For documentation, see exc.html
1790792Sgshapiro*/
1890792Sgshapiro
1990792Sgshapiro#include <ctype.h>
2090792Sgshapiro#include <string.h>
2190792Sgshapiro
2290792Sgshapiro#include <sm/errstring.h>
2390792Sgshapiro#include <sm/exc.h>
2490792Sgshapiro#include <sm/heap.h>
2590792Sgshapiro#include <sm/string.h>
2690792Sgshapiro#include <sm/varargs.h>
2790792Sgshapiro#include <sm/io.h>
2890792Sgshapiro
2990792Sgshapiroconst char SmExcMagic[] = "sm_exc";
3090792Sgshapiroconst char SmExcTypeMagic[] = "sm_exc_type";
3190792Sgshapiro
3290792Sgshapiro/*
3390792Sgshapiro**  SM_ETYPE_PRINTF -- printf for exception types.
3490792Sgshapiro**
3590792Sgshapiro**	Parameters:
3690792Sgshapiro**		exc -- exception.
3790792Sgshapiro**		stream -- file for output.
3890792Sgshapiro**
3990792Sgshapiro**	Returns:
4090792Sgshapiro**		none.
4190792Sgshapiro*/
4290792Sgshapiro
4390792Sgshapiro/*
4490792Sgshapiro**  A simple formatted print function that can be used as the print function
4590792Sgshapiro**  by most exception types.  It prints the printcontext string, interpreting
4690792Sgshapiro**  occurrences of %0 through %9 as references to the argument vector.
4790792Sgshapiro**  If exception argument 3 is an int or long, then %3 will print the
4890792Sgshapiro**  argument in decimal, and %o3 or %x3 will print it in octal or hex.
4990792Sgshapiro*/
5090792Sgshapiro
5190792Sgshapirovoid
5290792Sgshapirosm_etype_printf(exc, stream)
5390792Sgshapiro	SM_EXC_T *exc;
5490792Sgshapiro	SM_FILE_T *stream;
5590792Sgshapiro{
5690792Sgshapiro	size_t n = strlen(exc->exc_type->etype_argformat);
5790792Sgshapiro	const char *p, *s;
5890792Sgshapiro	char format;
5990792Sgshapiro
6090792Sgshapiro	for (p = exc->exc_type->etype_printcontext; *p != '\0'; ++p)
6190792Sgshapiro	{
6290792Sgshapiro		if (*p != '%')
6390792Sgshapiro		{
6490792Sgshapiro			(void) sm_io_putc(stream, SM_TIME_DEFAULT, *p);
6590792Sgshapiro			continue;
6690792Sgshapiro		}
6790792Sgshapiro		++p;
6890792Sgshapiro		if (*p == '\0')
6990792Sgshapiro		{
7090792Sgshapiro			(void) sm_io_putc(stream, SM_TIME_DEFAULT, '%');
7190792Sgshapiro			break;
7290792Sgshapiro		}
7390792Sgshapiro		if (*p == '%')
7490792Sgshapiro		{
7590792Sgshapiro			(void) sm_io_putc(stream, SM_TIME_DEFAULT, '%');
7690792Sgshapiro			continue;
7790792Sgshapiro		}
7890792Sgshapiro		format = '\0';
7990792Sgshapiro		if (isalpha(*p))
8090792Sgshapiro		{
8190792Sgshapiro			format = *p++;
8290792Sgshapiro			if (*p == '\0')
8390792Sgshapiro			{
8490792Sgshapiro				(void) sm_io_putc(stream, SM_TIME_DEFAULT, '%');
8590792Sgshapiro				(void) sm_io_putc(stream, SM_TIME_DEFAULT,
8690792Sgshapiro						  format);
8790792Sgshapiro				break;
8890792Sgshapiro			}
8990792Sgshapiro		}
9090792Sgshapiro		if (isdigit(*p))
9190792Sgshapiro		{
9290792Sgshapiro			size_t i = *p - '0';
9390792Sgshapiro			if (i < n)
9490792Sgshapiro			{
9590792Sgshapiro				switch (exc->exc_type->etype_argformat[i])
9690792Sgshapiro				{
9790792Sgshapiro				  case 's':
9890792Sgshapiro				  case 'r':
9990792Sgshapiro					s = exc->exc_argv[i].v_str;
10090792Sgshapiro					if (s == NULL)
10190792Sgshapiro						s = "(null)";
10290792Sgshapiro					sm_io_fputs(stream, SM_TIME_DEFAULT, s);
10390792Sgshapiro					continue;
10490792Sgshapiro				  case 'i':
10590792Sgshapiro					sm_io_fprintf(stream,
10690792Sgshapiro						SM_TIME_DEFAULT,
10790792Sgshapiro						format == 'o' ? "%o"
10890792Sgshapiro						: format == 'x' ? "%x"
10990792Sgshapiro								: "%d",
11090792Sgshapiro						exc->exc_argv[i].v_int);
11190792Sgshapiro					continue;
11290792Sgshapiro				  case 'l':
11390792Sgshapiro					sm_io_fprintf(stream,
11490792Sgshapiro						SM_TIME_DEFAULT,
11590792Sgshapiro						format == 'o' ? "%lo"
11690792Sgshapiro						: format == 'x' ? "%lx"
11790792Sgshapiro								: "%ld",
11890792Sgshapiro						exc->exc_argv[i].v_long);
11990792Sgshapiro					continue;
12090792Sgshapiro				  case 'e':
12190792Sgshapiro					sm_exc_write(exc->exc_argv[i].v_exc,
12290792Sgshapiro						stream);
12390792Sgshapiro					continue;
12490792Sgshapiro				}
12590792Sgshapiro			}
12690792Sgshapiro		}
12790792Sgshapiro		(void) sm_io_putc(stream, SM_TIME_DEFAULT, '%');
12890792Sgshapiro		if (format)
12990792Sgshapiro			(void) sm_io_putc(stream, SM_TIME_DEFAULT, format);
13090792Sgshapiro		(void) sm_io_putc(stream, SM_TIME_DEFAULT, *p);
13190792Sgshapiro	}
13290792Sgshapiro}
13390792Sgshapiro
13490792Sgshapiro/*
13590792Sgshapiro**  Standard exception types.
13690792Sgshapiro*/
13790792Sgshapiro
13890792Sgshapiro/*
13990792Sgshapiro**  SM_ETYPE_OS_PRINT -- Print OS related exception.
14090792Sgshapiro**
14190792Sgshapiro**	Parameters:
14290792Sgshapiro**		exc -- exception.
14390792Sgshapiro**		stream -- file for output.
14490792Sgshapiro**
14590792Sgshapiro**	Returns:
14690792Sgshapiro**		none.
14790792Sgshapiro*/
14890792Sgshapiro
14990792Sgshapirostatic void
15090792Sgshapirosm_etype_os_print __P((
15190792Sgshapiro	SM_EXC_T *exc,
15290792Sgshapiro	SM_FILE_T *stream));
15390792Sgshapiro
15490792Sgshapirostatic void
15590792Sgshapirosm_etype_os_print(exc, stream)
15690792Sgshapiro	SM_EXC_T *exc;
15790792Sgshapiro	SM_FILE_T *stream;
15890792Sgshapiro{
15990792Sgshapiro	int err = exc->exc_argv[0].v_int;
16090792Sgshapiro	char *syscall = exc->exc_argv[1].v_str;
16190792Sgshapiro	char *sysargs = exc->exc_argv[2].v_str;
16290792Sgshapiro
16390792Sgshapiro	if (sysargs)
16490792Sgshapiro		sm_io_fprintf(stream, SM_TIME_DEFAULT, "%s: %s failed: %s",
16590792Sgshapiro			      sysargs, syscall, sm_errstring(err));
16690792Sgshapiro	else
16790792Sgshapiro		sm_io_fprintf(stream, SM_TIME_DEFAULT, "%s failed: %s", syscall,
16890792Sgshapiro			      sm_errstring(err));
16990792Sgshapiro}
17090792Sgshapiro
17190792Sgshapiro/*
17290792Sgshapiro**  SmEtypeOs represents the failure of a Unix system call.
17390792Sgshapiro**  The three arguments are:
17490792Sgshapiro**   int errno (eg, ENOENT)
17590792Sgshapiro**   char *syscall (eg, "open")
17690792Sgshapiro**   char *sysargs (eg, NULL or "/etc/mail/sendmail.cf")
17790792Sgshapiro*/
17890792Sgshapiro
17990792Sgshapiroconst SM_EXC_TYPE_T SmEtypeOs =
18090792Sgshapiro{
18190792Sgshapiro	SmExcTypeMagic,
18290792Sgshapiro	"E:sm.os",
18390792Sgshapiro	"isr",
18490792Sgshapiro	sm_etype_os_print,
18590792Sgshapiro	NULL,
18690792Sgshapiro};
18790792Sgshapiro
18890792Sgshapiro/*
18990792Sgshapiro**  SmEtypeErr is a completely generic error which should only be
19090792Sgshapiro**  used in applications and test programs.  Libraries should use
19190792Sgshapiro**  more specific exception codes.
19290792Sgshapiro*/
19390792Sgshapiro
19490792Sgshapiroconst SM_EXC_TYPE_T SmEtypeErr =
19590792Sgshapiro{
19690792Sgshapiro	SmExcTypeMagic,
19790792Sgshapiro	"E:sm.err",
19890792Sgshapiro	"r",
19990792Sgshapiro	sm_etype_printf,
20090792Sgshapiro	"%0",
20190792Sgshapiro};
20290792Sgshapiro
20390792Sgshapiro/*
20490792Sgshapiro**  SM_EXC_VNEW_X -- Construct a new exception object.
20590792Sgshapiro**
20690792Sgshapiro**	Parameters:
20790792Sgshapiro**		etype -- type of exception.
20890792Sgshapiro**		ap -- varargs.
20990792Sgshapiro**
21090792Sgshapiro**	Returns:
21190792Sgshapiro**		pointer to exception object.
21290792Sgshapiro*/
21390792Sgshapiro
21490792Sgshapiro/*
21590792Sgshapiro**  This is an auxiliary function called by sm_exc_new_x and sm_exc_raisenew_x.
21690792Sgshapiro**
21790792Sgshapiro**  If an exception is raised, then to avoid a storage leak, we must:
21890792Sgshapiro**  (a) Free all storage we have allocated.
21990792Sgshapiro**  (b) Free all exception arguments in the varargs list.
22090792Sgshapiro**  Getting this right is tricky.
22190792Sgshapiro**
22290792Sgshapiro**  To see why (b) is required, consider the code fragment
22390792Sgshapiro**     SM_EXCEPT(exc, "*")
22490792Sgshapiro**         sm_exc_raisenew_x(&MyEtype, exc);
22590792Sgshapiro**     SM_END_TRY
22690792Sgshapiro**  In the normal case, sm_exc_raisenew_x will allocate and raise a new
22790792Sgshapiro**  exception E that owns exc.  When E is eventually freed, exc is also freed.
22890792Sgshapiro**  In the exceptional case, sm_exc_raisenew_x must free exc before raising
22990792Sgshapiro**  an out-of-memory exception so that exc is not leaked.
23090792Sgshapiro*/
23190792Sgshapiro
232168515Sgshapirostatic SM_EXC_T *sm_exc_vnew_x __P((const SM_EXC_TYPE_T *, va_list SM_NONVOLATILE));
233168515Sgshapiro
234168515Sgshapirostatic SM_EXC_T *
23590792Sgshapirosm_exc_vnew_x(etype, ap)
23690792Sgshapiro	const SM_EXC_TYPE_T *etype;
23790792Sgshapiro	va_list SM_NONVOLATILE ap;
23890792Sgshapiro{
23990792Sgshapiro	/*
24090792Sgshapiro	**  All variables that are modified in the SM_TRY clause and
24190792Sgshapiro	**  referenced in the SM_EXCEPT clause must be declared volatile.
24290792Sgshapiro	*/
24390792Sgshapiro
24490792Sgshapiro	/* NOTE: Type of si, i, and argc *must* match */
24590792Sgshapiro	SM_EXC_T * volatile exc = NULL;
24690792Sgshapiro	int volatile si = 0;
24790792Sgshapiro	SM_VAL_T * volatile argv = NULL;
24890792Sgshapiro	int i, argc;
24990792Sgshapiro
25090792Sgshapiro	SM_REQUIRE_ISA(etype, SmExcTypeMagic);
25190792Sgshapiro	argc = strlen(etype->etype_argformat);
25290792Sgshapiro	SM_TRY
25390792Sgshapiro	{
25490792Sgshapiro		/*
25590792Sgshapiro		**  Step 1.  Allocate the exception structure.
25690792Sgshapiro		**  On failure, scan the varargs list and free all
25790792Sgshapiro		**  exception arguments.
25890792Sgshapiro		*/
25990792Sgshapiro
26090792Sgshapiro		exc = sm_malloc_x(sizeof(SM_EXC_T));
26190792Sgshapiro		exc->sm_magic = SmExcMagic;
26290792Sgshapiro		exc->exc_refcount = 1;
26390792Sgshapiro		exc->exc_type = etype;
26490792Sgshapiro		exc->exc_argv = NULL;
26590792Sgshapiro
26690792Sgshapiro		/*
26790792Sgshapiro		**  Step 2.  Allocate the argument vector.
26890792Sgshapiro		**  On failure, free exc, scan the varargs list and free all
26990792Sgshapiro		**  exception arguments.  On success, scan the varargs list,
27090792Sgshapiro		**  and copy the arguments into argv.
27190792Sgshapiro		*/
27290792Sgshapiro
27390792Sgshapiro		argv = sm_malloc_x(argc * sizeof(SM_VAL_T));
27490792Sgshapiro		exc->exc_argv = argv;
27590792Sgshapiro		for (i = 0; i < argc; ++i)
27690792Sgshapiro		{
27790792Sgshapiro			switch (etype->etype_argformat[i])
27890792Sgshapiro			{
27990792Sgshapiro			  case 'i':
28090792Sgshapiro				argv[i].v_int = SM_VA_ARG(ap, int);
28190792Sgshapiro				break;
28290792Sgshapiro			  case 'l':
28390792Sgshapiro				argv[i].v_long = SM_VA_ARG(ap, long);
28490792Sgshapiro				break;
28590792Sgshapiro			  case 'e':
28690792Sgshapiro				argv[i].v_exc = SM_VA_ARG(ap, SM_EXC_T*);
28790792Sgshapiro				break;
28890792Sgshapiro			  case 's':
28990792Sgshapiro				argv[i].v_str = SM_VA_ARG(ap, char*);
29090792Sgshapiro				break;
29190792Sgshapiro			  case 'r':
29290792Sgshapiro				SM_REQUIRE(etype->etype_argformat[i+1] == '\0');
29390792Sgshapiro				argv[i].v_str = SM_VA_ARG(ap, char*);
29490792Sgshapiro				break;
29590792Sgshapiro			  default:
29690792Sgshapiro				sm_abort("sm_exc_vnew_x: bad argformat '%c'",
29790792Sgshapiro					etype->etype_argformat[i]);
29890792Sgshapiro			}
29990792Sgshapiro		}
30090792Sgshapiro
30190792Sgshapiro		/*
30290792Sgshapiro		**  Step 3.  Scan argv, and allocate space for all
30390792Sgshapiro		**  string arguments.  si is the number of elements
30490792Sgshapiro		**  of argv that have been processed so far.
30590792Sgshapiro		**  On failure, free exc, argv, all the exception arguments
30690792Sgshapiro		**  and all of the strings that have been copied.
30790792Sgshapiro		*/
30890792Sgshapiro
30990792Sgshapiro		for (si = 0; si < argc; ++si)
31090792Sgshapiro		{
31190792Sgshapiro			switch (etype->etype_argformat[si])
31290792Sgshapiro			{
31390792Sgshapiro			  case 's':
31490792Sgshapiro			    {
31590792Sgshapiro				char *str = argv[si].v_str;
31690792Sgshapiro				if (str != NULL)
31790792Sgshapiro				    argv[si].v_str = sm_strdup_x(str);
31890792Sgshapiro			    }
31990792Sgshapiro			    break;
32090792Sgshapiro			  case 'r':
32190792Sgshapiro			    {
32290792Sgshapiro				char *fmt = argv[si].v_str;
32390792Sgshapiro				if (fmt != NULL)
32490792Sgshapiro				    argv[si].v_str = sm_vstringf_x(fmt, ap);
32590792Sgshapiro			    }
32690792Sgshapiro			    break;
32790792Sgshapiro			}
32890792Sgshapiro		}
32990792Sgshapiro	}
33090792Sgshapiro	SM_EXCEPT(e, "*")
33190792Sgshapiro	{
33290792Sgshapiro		if (exc == NULL || argv == NULL)
33390792Sgshapiro		{
33490792Sgshapiro			/*
33590792Sgshapiro			**  Failure in step 1 or step 2.
33690792Sgshapiro			**  Scan ap and free all exception arguments.
33790792Sgshapiro			*/
33890792Sgshapiro
33990792Sgshapiro			for (i = 0; i < argc; ++i)
34090792Sgshapiro			{
34190792Sgshapiro				switch (etype->etype_argformat[i])
34290792Sgshapiro				{
34390792Sgshapiro				  case 'i':
34490792Sgshapiro					(void) SM_VA_ARG(ap, int);
34590792Sgshapiro					break;
34690792Sgshapiro				  case 'l':
34790792Sgshapiro					(void) SM_VA_ARG(ap, long);
34890792Sgshapiro					break;
34990792Sgshapiro				  case 'e':
35090792Sgshapiro					sm_exc_free(SM_VA_ARG(ap, SM_EXC_T*));
35190792Sgshapiro					break;
35290792Sgshapiro				  case 's':
35390792Sgshapiro				  case 'r':
35490792Sgshapiro					(void) SM_VA_ARG(ap, char*);
35590792Sgshapiro					break;
35690792Sgshapiro				}
35790792Sgshapiro			}
35890792Sgshapiro		}
35990792Sgshapiro		else
36090792Sgshapiro		{
36190792Sgshapiro			/*
36290792Sgshapiro			**  Failure in step 3.  Scan argv and free
36390792Sgshapiro			**  all exception arguments and all string
36490792Sgshapiro			**  arguments that have been duplicated.
36590792Sgshapiro			**  Then free argv.
36690792Sgshapiro			*/
36790792Sgshapiro
36890792Sgshapiro			for (i = 0; i < argc; ++i)
36990792Sgshapiro			{
37090792Sgshapiro				switch (etype->etype_argformat[i])
37190792Sgshapiro				{
37290792Sgshapiro				  case 'e':
37390792Sgshapiro					sm_exc_free(argv[i].v_exc);
37490792Sgshapiro					break;
37590792Sgshapiro				  case 's':
37690792Sgshapiro				  case 'r':
37790792Sgshapiro					if (i < si)
37890792Sgshapiro						sm_free(argv[i].v_str);
37990792Sgshapiro					break;
38090792Sgshapiro				}
38190792Sgshapiro			}
38290792Sgshapiro			sm_free(argv);
38390792Sgshapiro		}
38490792Sgshapiro		sm_free(exc);
38590792Sgshapiro		sm_exc_raise_x(e);
38690792Sgshapiro	}
38790792Sgshapiro	SM_END_TRY
38890792Sgshapiro
38990792Sgshapiro	return exc;
39090792Sgshapiro}
39190792Sgshapiro
39290792Sgshapiro/*
39390792Sgshapiro**  SM_EXC_NEW_X -- Construct a new exception object.
39490792Sgshapiro**
39590792Sgshapiro**	Parameters:
39690792Sgshapiro**		etype -- type of exception.
39790792Sgshapiro**		... -- varargs.
39890792Sgshapiro**
39990792Sgshapiro**	Returns:
40090792Sgshapiro**		pointer to exception object.
40190792Sgshapiro*/
40290792Sgshapiro
40390792SgshapiroSM_EXC_T *
40490792Sgshapiro#if SM_VA_STD
40590792Sgshapirosm_exc_new_x(
40690792Sgshapiro	const SM_EXC_TYPE_T *etype,
40790792Sgshapiro	...)
40890792Sgshapiro#else /* SM_VA_STD */
40990792Sgshapirosm_exc_new_x(etype, va_alist)
41090792Sgshapiro	const SM_EXC_TYPE_T *etype;
41190792Sgshapiro	va_dcl
41290792Sgshapiro#endif /* SM_VA_STD */
41390792Sgshapiro{
41490792Sgshapiro	SM_EXC_T *exc;
41590792Sgshapiro	SM_VA_LOCAL_DECL
41690792Sgshapiro
41790792Sgshapiro	SM_VA_START(ap, etype);
41890792Sgshapiro	exc = sm_exc_vnew_x(etype, ap);
41990792Sgshapiro	SM_VA_END(ap);
42090792Sgshapiro	return exc;
42190792Sgshapiro}
42290792Sgshapiro
42390792Sgshapiro/*
42490792Sgshapiro**  SM_EXC_FREE -- Destroy a reference to an exception object.
42590792Sgshapiro**
42690792Sgshapiro**	Parameters:
42790792Sgshapiro**		exc -- exception object.
42890792Sgshapiro**
42990792Sgshapiro**	Returns:
43090792Sgshapiro**		none.
43190792Sgshapiro*/
43290792Sgshapiro
43390792Sgshapirovoid
43490792Sgshapirosm_exc_free(exc)
43590792Sgshapiro	SM_EXC_T *exc;
43690792Sgshapiro{
43790792Sgshapiro	if (exc == NULL)
43890792Sgshapiro		return;
43990792Sgshapiro	SM_REQUIRE(exc->sm_magic == SmExcMagic);
44090792Sgshapiro	if (exc->exc_refcount == 0)
44190792Sgshapiro		return;
44290792Sgshapiro	if (--exc->exc_refcount == 0)
44390792Sgshapiro	{
44490792Sgshapiro		int i, c;
44590792Sgshapiro
44690792Sgshapiro		for (i = 0; (c = exc->exc_type->etype_argformat[i]) != '\0';
44790792Sgshapiro		     ++i)
44890792Sgshapiro		{
44990792Sgshapiro			switch (c)
45090792Sgshapiro			{
45190792Sgshapiro			  case 's':
45290792Sgshapiro			  case 'r':
45390792Sgshapiro				sm_free(exc->exc_argv[i].v_str);
45490792Sgshapiro				break;
45590792Sgshapiro			  case 'e':
45690792Sgshapiro				sm_exc_free(exc->exc_argv[i].v_exc);
45790792Sgshapiro				break;
45890792Sgshapiro			}
45990792Sgshapiro		}
46090792Sgshapiro		exc->sm_magic = NULL;
46190792Sgshapiro		sm_free(exc->exc_argv);
46290792Sgshapiro		sm_free(exc);
46390792Sgshapiro	}
46490792Sgshapiro}
46590792Sgshapiro
46690792Sgshapiro/*
46790792Sgshapiro**  SM_EXC_MATCH -- Match exception category against a glob pattern.
46890792Sgshapiro**
46990792Sgshapiro**	Parameters:
47090792Sgshapiro**		exc -- exception.
47190792Sgshapiro**		pattern -- glob pattern.
47290792Sgshapiro**
47390792Sgshapiro**	Returns:
47490792Sgshapiro**		true iff match.
47590792Sgshapiro*/
47690792Sgshapiro
47790792Sgshapirobool
47890792Sgshapirosm_exc_match(exc, pattern)
47990792Sgshapiro	SM_EXC_T *exc;
48090792Sgshapiro	const char *pattern;
48190792Sgshapiro{
48290792Sgshapiro	if (exc == NULL)
48390792Sgshapiro		return false;
48490792Sgshapiro	SM_REQUIRE(exc->sm_magic == SmExcMagic);
48590792Sgshapiro	return sm_match(exc->exc_type->etype_category, pattern);
48690792Sgshapiro}
48790792Sgshapiro
48890792Sgshapiro/*
48990792Sgshapiro**  SM_EXC_WRITE -- Write exception message to a stream (wo trailing newline).
49090792Sgshapiro**
49190792Sgshapiro**	Parameters:
49290792Sgshapiro**		exc -- exception.
49390792Sgshapiro**		stream -- file for output.
49490792Sgshapiro**
49590792Sgshapiro**	Returns:
49690792Sgshapiro**		none.
49790792Sgshapiro*/
49890792Sgshapiro
49990792Sgshapirovoid
50090792Sgshapirosm_exc_write(exc, stream)
50190792Sgshapiro	SM_EXC_T *exc;
50290792Sgshapiro	SM_FILE_T *stream;
50390792Sgshapiro{
50490792Sgshapiro	SM_REQUIRE_ISA(exc, SmExcMagic);
50590792Sgshapiro	exc->exc_type->etype_print(exc, stream);
50690792Sgshapiro}
50790792Sgshapiro
50890792Sgshapiro/*
50990792Sgshapiro**  SM_EXC_PRINT -- Print exception message to a stream (with trailing newline).
51090792Sgshapiro**
51190792Sgshapiro**	Parameters:
51290792Sgshapiro**		exc -- exception.
51390792Sgshapiro**		stream -- file for output.
51490792Sgshapiro**
51590792Sgshapiro**	Returns:
51690792Sgshapiro**		none.
51790792Sgshapiro*/
51890792Sgshapiro
51990792Sgshapirovoid
52090792Sgshapirosm_exc_print(exc, stream)
52190792Sgshapiro	SM_EXC_T *exc;
52290792Sgshapiro	SM_FILE_T *stream;
52390792Sgshapiro{
52490792Sgshapiro	SM_REQUIRE_ISA(exc, SmExcMagic);
52590792Sgshapiro	exc->exc_type->etype_print(exc, stream);
52690792Sgshapiro	(void) sm_io_putc(stream, SM_TIME_DEFAULT, '\n');
52790792Sgshapiro}
52890792Sgshapiro
52990792SgshapiroSM_EXC_HANDLER_T *SmExcHandler = NULL;
53090792Sgshapirostatic SM_EXC_DEFAULT_HANDLER_T SmExcDefaultHandler = NULL;
53190792Sgshapiro
53290792Sgshapiro/*
53390792Sgshapiro**  SM_EXC_NEWTHREAD -- Initialize exception handling for new process/thread.
53490792Sgshapiro**
53590792Sgshapiro**	Parameters:
53690792Sgshapiro**		h -- default exception handler.
53790792Sgshapiro**
53890792Sgshapiro**	Returns:
53990792Sgshapiro**		none.
54090792Sgshapiro*/
54190792Sgshapiro
54290792Sgshapiro/*
54390792Sgshapiro**  Initialize a new process or a new thread by clearing the
54490792Sgshapiro**  exception handler stack and optionally setting a default
54590792Sgshapiro**  exception handler function.  Call this at the beginning of main,
54690792Sgshapiro**  or in a new process after calling fork, or in a new thread.
54790792Sgshapiro**
54890792Sgshapiro**  This function is a luxury, not a necessity.
54990792Sgshapiro**  If h != NULL then you can get the same effect by
55090792Sgshapiro**  wrapping the body of main, or the body of a forked child
55190792Sgshapiro**  or a new thread in SM_TRY ... SM_EXCEPT(e,"*") h(e); SM_END_TRY.
55290792Sgshapiro*/
55390792Sgshapiro
55490792Sgshapirovoid
55590792Sgshapirosm_exc_newthread(h)
55690792Sgshapiro	SM_EXC_DEFAULT_HANDLER_T h;
55790792Sgshapiro{
55890792Sgshapiro	SmExcHandler = NULL;
55990792Sgshapiro	SmExcDefaultHandler = h;
56090792Sgshapiro}
56190792Sgshapiro
56290792Sgshapiro/*
56390792Sgshapiro**  SM_EXC_RAISE_X -- Raise an exception.
56490792Sgshapiro**
56590792Sgshapiro**	Parameters:
56690792Sgshapiro**		exc -- exception.
56790792Sgshapiro**
56890792Sgshapiro**	Returns:
56990792Sgshapiro**		doesn't.
57090792Sgshapiro*/
57190792Sgshapiro
572125820Sgshapirovoid SM_DEAD_D
57390792Sgshapirosm_exc_raise_x(exc)
57490792Sgshapiro	SM_EXC_T *exc;
57590792Sgshapiro{
57690792Sgshapiro	SM_REQUIRE_ISA(exc, SmExcMagic);
57790792Sgshapiro
57890792Sgshapiro	if (SmExcHandler == NULL)
57990792Sgshapiro	{
58090792Sgshapiro		if (SmExcDefaultHandler != NULL)
58190792Sgshapiro		{
58290792Sgshapiro			SM_EXC_DEFAULT_HANDLER_T h;
58390792Sgshapiro
58490792Sgshapiro			/*
58590792Sgshapiro			**  If defined, the default handler is expected
58690792Sgshapiro			**  to terminate the current thread of execution
58790792Sgshapiro			**  using exit() or pthread_exit().
58890792Sgshapiro			**  If it instead returns normally, then we fall
58990792Sgshapiro			**  through to the default case below.  If it
59090792Sgshapiro			**  raises an exception, then sm_exc_raise_x is
59190792Sgshapiro			**  re-entered and, because we set SmExcDefaultHandler
59290792Sgshapiro			**  to NULL before invoking h, we will again
59390792Sgshapiro			**  end up in the default case below.
59490792Sgshapiro			*/
59590792Sgshapiro
59690792Sgshapiro			h = SmExcDefaultHandler;
59790792Sgshapiro			SmExcDefaultHandler = NULL;
59890792Sgshapiro			(*h)(exc);
59990792Sgshapiro		}
60090792Sgshapiro
60190792Sgshapiro		/*
60290792Sgshapiro		**  No exception handler, so print the error and exit.
60390792Sgshapiro		**  To override this behaviour on a program wide basis,
60490792Sgshapiro		**  call sm_exc_newthread or put an exception handler in main().
60590792Sgshapiro		**
60690792Sgshapiro		**  XXX TODO: map the exception category to an exit code
60790792Sgshapiro		**  XXX from <sysexits.h>.
60890792Sgshapiro		*/
60990792Sgshapiro
61090792Sgshapiro		sm_exc_print(exc, smioerr);
61190792Sgshapiro		exit(255);
61290792Sgshapiro	}
61390792Sgshapiro
61490792Sgshapiro	if (SmExcHandler->eh_value == NULL)
61590792Sgshapiro		SmExcHandler->eh_value = exc;
61690792Sgshapiro	else
61790792Sgshapiro		sm_exc_free(exc);
61890792Sgshapiro
61990792Sgshapiro	sm_longjmp_nosig(SmExcHandler->eh_context, 1);
62090792Sgshapiro}
62190792Sgshapiro
62290792Sgshapiro/*
62390792Sgshapiro**  SM_EXC_RAISENEW_X -- shorthand for sm_exc_raise_x(sm_exc_new_x(...))
62490792Sgshapiro**
62590792Sgshapiro**	Parameters:
62690792Sgshapiro**		etype -- type of exception.
62790792Sgshapiro**		ap -- varargs.
62890792Sgshapiro**
62990792Sgshapiro**	Returns:
63090792Sgshapiro**		none.
63190792Sgshapiro*/
63290792Sgshapiro
633125820Sgshapirovoid SM_DEAD_D
63490792Sgshapiro#if SM_VA_STD
63590792Sgshapirosm_exc_raisenew_x(
63690792Sgshapiro	const SM_EXC_TYPE_T *etype,
63790792Sgshapiro	...)
63890792Sgshapiro#else
63990792Sgshapirosm_exc_raisenew_x(etype, va_alist)
64090792Sgshapiro	const SM_EXC_TYPE_T *etype;
64190792Sgshapiro	va_dcl
64290792Sgshapiro#endif
64390792Sgshapiro{
64490792Sgshapiro	SM_EXC_T *exc;
64590792Sgshapiro	SM_VA_LOCAL_DECL
64690792Sgshapiro
64790792Sgshapiro	SM_VA_START(ap, etype);
64890792Sgshapiro	exc = sm_exc_vnew_x(etype, ap);
64990792Sgshapiro	SM_VA_END(ap);
65090792Sgshapiro	sm_exc_raise_x(exc);
65190792Sgshapiro}
652