xref: /freebsd-src/contrib/sendmail/libsm/exc.c (revision e2c0e292e8a7ca00ba99bcfccc9e637f45c3e8b1)
140266059SGregory Neil Shapiro /*
25dd76dd0SGregory Neil Shapiro  * Copyright (c) 2000-2002 Proofpoint, Inc. and its suppliers.
340266059SGregory Neil Shapiro  *	All rights reserved.
440266059SGregory Neil Shapiro  *
540266059SGregory Neil Shapiro  * By using this file, you agree to the terms and conditions set
640266059SGregory Neil Shapiro  * forth in the LICENSE file which can be found at the top level of
740266059SGregory Neil Shapiro  * the sendmail distribution.
840266059SGregory Neil Shapiro  *
940266059SGregory Neil Shapiro  */
1040266059SGregory Neil Shapiro 
1140266059SGregory Neil Shapiro #include <sm/gen.h>
124313cc83SGregory Neil Shapiro SM_RCSID("@(#)$Id: exc.c,v 1.50 2013-11-22 20:51:42 ca Exp $")
1340266059SGregory Neil Shapiro 
1440266059SGregory Neil Shapiro /*
1540266059SGregory Neil Shapiro **  exception handling
1640266059SGregory Neil Shapiro **  For documentation, see exc.html
1740266059SGregory Neil Shapiro */
1840266059SGregory Neil Shapiro 
1940266059SGregory Neil Shapiro #include <ctype.h>
2040266059SGregory Neil Shapiro #include <string.h>
2140266059SGregory Neil Shapiro 
2240266059SGregory Neil Shapiro #include <sm/errstring.h>
2340266059SGregory Neil Shapiro #include <sm/exc.h>
2440266059SGregory Neil Shapiro #include <sm/heap.h>
2540266059SGregory Neil Shapiro #include <sm/string.h>
2640266059SGregory Neil Shapiro #include <sm/varargs.h>
2740266059SGregory Neil Shapiro #include <sm/io.h>
2840266059SGregory Neil Shapiro 
2940266059SGregory Neil Shapiro const char SmExcMagic[] = "sm_exc";
3040266059SGregory Neil Shapiro const char SmExcTypeMagic[] = "sm_exc_type";
3140266059SGregory Neil Shapiro 
3240266059SGregory Neil Shapiro /*
3340266059SGregory Neil Shapiro **  SM_ETYPE_PRINTF -- printf for exception types.
3440266059SGregory Neil Shapiro **
3540266059SGregory Neil Shapiro **	Parameters:
3640266059SGregory Neil Shapiro **		exc -- exception.
3740266059SGregory Neil Shapiro **		stream -- file for output.
3840266059SGregory Neil Shapiro **
3940266059SGregory Neil Shapiro **	Returns:
4040266059SGregory Neil Shapiro **		none.
4140266059SGregory Neil Shapiro */
4240266059SGregory Neil Shapiro 
4340266059SGregory Neil Shapiro /*
4440266059SGregory Neil Shapiro **  A simple formatted print function that can be used as the print function
4540266059SGregory Neil Shapiro **  by most exception types.  It prints the printcontext string, interpreting
4640266059SGregory Neil Shapiro **  occurrences of %0 through %9 as references to the argument vector.
4740266059SGregory Neil Shapiro **  If exception argument 3 is an int or long, then %3 will print the
4840266059SGregory Neil Shapiro **  argument in decimal, and %o3 or %x3 will print it in octal or hex.
4940266059SGregory Neil Shapiro */
5040266059SGregory Neil Shapiro 
5140266059SGregory Neil Shapiro void
sm_etype_printf(exc,stream)5240266059SGregory Neil Shapiro sm_etype_printf(exc, stream)
5340266059SGregory Neil Shapiro 	SM_EXC_T *exc;
5440266059SGregory Neil Shapiro 	SM_FILE_T *stream;
5540266059SGregory Neil Shapiro {
5640266059SGregory Neil Shapiro 	size_t n = strlen(exc->exc_type->etype_argformat);
5740266059SGregory Neil Shapiro 	const char *p, *s;
5840266059SGregory Neil Shapiro 	char format;
5940266059SGregory Neil Shapiro 
6040266059SGregory Neil Shapiro 	for (p = exc->exc_type->etype_printcontext; *p != '\0'; ++p)
6140266059SGregory Neil Shapiro 	{
6240266059SGregory Neil Shapiro 		if (*p != '%')
6340266059SGregory Neil Shapiro 		{
6440266059SGregory Neil Shapiro 			(void) sm_io_putc(stream, SM_TIME_DEFAULT, *p);
6540266059SGregory Neil Shapiro 			continue;
6640266059SGregory Neil Shapiro 		}
6740266059SGregory Neil Shapiro 		++p;
6840266059SGregory Neil Shapiro 		if (*p == '\0')
6940266059SGregory Neil Shapiro 		{
7040266059SGregory Neil Shapiro 			(void) sm_io_putc(stream, SM_TIME_DEFAULT, '%');
7140266059SGregory Neil Shapiro 			break;
7240266059SGregory Neil Shapiro 		}
7340266059SGregory Neil Shapiro 		if (*p == '%')
7440266059SGregory Neil Shapiro 		{
7540266059SGregory Neil Shapiro 			(void) sm_io_putc(stream, SM_TIME_DEFAULT, '%');
7640266059SGregory Neil Shapiro 			continue;
7740266059SGregory Neil Shapiro 		}
7840266059SGregory Neil Shapiro 		format = '\0';
7940266059SGregory Neil Shapiro 		if (isalpha(*p))
8040266059SGregory Neil Shapiro 		{
8140266059SGregory Neil Shapiro 			format = *p++;
8240266059SGregory Neil Shapiro 			if (*p == '\0')
8340266059SGregory Neil Shapiro 			{
8440266059SGregory Neil Shapiro 				(void) sm_io_putc(stream, SM_TIME_DEFAULT, '%');
8540266059SGregory Neil Shapiro 				(void) sm_io_putc(stream, SM_TIME_DEFAULT,
8640266059SGregory Neil Shapiro 						  format);
8740266059SGregory Neil Shapiro 				break;
8840266059SGregory Neil Shapiro 			}
8940266059SGregory Neil Shapiro 		}
9040266059SGregory Neil Shapiro 		if (isdigit(*p))
9140266059SGregory Neil Shapiro 		{
9240266059SGregory Neil Shapiro 			size_t i = *p - '0';
9340266059SGregory Neil Shapiro 			if (i < n)
9440266059SGregory Neil Shapiro 			{
9540266059SGregory Neil Shapiro 				switch (exc->exc_type->etype_argformat[i])
9640266059SGregory Neil Shapiro 				{
9740266059SGregory Neil Shapiro 				  case 's':
9840266059SGregory Neil Shapiro 				  case 'r':
9940266059SGregory Neil Shapiro 					s = exc->exc_argv[i].v_str;
10040266059SGregory Neil Shapiro 					if (s == NULL)
10140266059SGregory Neil Shapiro 						s = "(null)";
10240266059SGregory Neil Shapiro 					sm_io_fputs(stream, SM_TIME_DEFAULT, s);
10340266059SGregory Neil Shapiro 					continue;
10440266059SGregory Neil Shapiro 				  case 'i':
10540266059SGregory Neil Shapiro 					sm_io_fprintf(stream,
10640266059SGregory Neil Shapiro 						SM_TIME_DEFAULT,
10740266059SGregory Neil Shapiro 						format == 'o' ? "%o"
10840266059SGregory Neil Shapiro 						: format == 'x' ? "%x"
10940266059SGregory Neil Shapiro 								: "%d",
11040266059SGregory Neil Shapiro 						exc->exc_argv[i].v_int);
11140266059SGregory Neil Shapiro 					continue;
11240266059SGregory Neil Shapiro 				  case 'l':
11340266059SGregory Neil Shapiro 					sm_io_fprintf(stream,
11440266059SGregory Neil Shapiro 						SM_TIME_DEFAULT,
11540266059SGregory Neil Shapiro 						format == 'o' ? "%lo"
11640266059SGregory Neil Shapiro 						: format == 'x' ? "%lx"
11740266059SGregory Neil Shapiro 								: "%ld",
11840266059SGregory Neil Shapiro 						exc->exc_argv[i].v_long);
11940266059SGregory Neil Shapiro 					continue;
12040266059SGregory Neil Shapiro 				  case 'e':
12140266059SGregory Neil Shapiro 					sm_exc_write(exc->exc_argv[i].v_exc,
12240266059SGregory Neil Shapiro 						stream);
12340266059SGregory Neil Shapiro 					continue;
12440266059SGregory Neil Shapiro 				}
12540266059SGregory Neil Shapiro 			}
12640266059SGregory Neil Shapiro 		}
12740266059SGregory Neil Shapiro 		(void) sm_io_putc(stream, SM_TIME_DEFAULT, '%');
12840266059SGregory Neil Shapiro 		if (format)
12940266059SGregory Neil Shapiro 			(void) sm_io_putc(stream, SM_TIME_DEFAULT, format);
13040266059SGregory Neil Shapiro 		(void) sm_io_putc(stream, SM_TIME_DEFAULT, *p);
13140266059SGregory Neil Shapiro 	}
13240266059SGregory Neil Shapiro }
13340266059SGregory Neil Shapiro 
13440266059SGregory Neil Shapiro /*
13540266059SGregory Neil Shapiro **  Standard exception types.
13640266059SGregory Neil Shapiro */
13740266059SGregory Neil Shapiro 
13840266059SGregory Neil Shapiro /*
13940266059SGregory Neil Shapiro **  SM_ETYPE_OS_PRINT -- Print OS related exception.
14040266059SGregory Neil Shapiro **
14140266059SGregory Neil Shapiro **	Parameters:
14240266059SGregory Neil Shapiro **		exc -- exception.
14340266059SGregory Neil Shapiro **		stream -- file for output.
14440266059SGregory Neil Shapiro **
14540266059SGregory Neil Shapiro **	Returns:
14640266059SGregory Neil Shapiro **		none.
14740266059SGregory Neil Shapiro */
14840266059SGregory Neil Shapiro 
14940266059SGregory Neil Shapiro static void
15040266059SGregory Neil Shapiro sm_etype_os_print __P((
15140266059SGregory Neil Shapiro 	SM_EXC_T *exc,
15240266059SGregory Neil Shapiro 	SM_FILE_T *stream));
15340266059SGregory Neil Shapiro 
15440266059SGregory Neil Shapiro static void
sm_etype_os_print(exc,stream)15540266059SGregory Neil Shapiro sm_etype_os_print(exc, stream)
15640266059SGregory Neil Shapiro 	SM_EXC_T *exc;
15740266059SGregory Neil Shapiro 	SM_FILE_T *stream;
15840266059SGregory Neil Shapiro {
15940266059SGregory Neil Shapiro 	int err = exc->exc_argv[0].v_int;
16040266059SGregory Neil Shapiro 	char *syscall = exc->exc_argv[1].v_str;
16140266059SGregory Neil Shapiro 	char *sysargs = exc->exc_argv[2].v_str;
16240266059SGregory Neil Shapiro 
16340266059SGregory Neil Shapiro 	if (sysargs)
16440266059SGregory Neil Shapiro 		sm_io_fprintf(stream, SM_TIME_DEFAULT, "%s: %s failed: %s",
16540266059SGregory Neil Shapiro 			      sysargs, syscall, sm_errstring(err));
16640266059SGregory Neil Shapiro 	else
16740266059SGregory Neil Shapiro 		sm_io_fprintf(stream, SM_TIME_DEFAULT, "%s failed: %s", syscall,
16840266059SGregory Neil Shapiro 			      sm_errstring(err));
16940266059SGregory Neil Shapiro }
17040266059SGregory Neil Shapiro 
17140266059SGregory Neil Shapiro /*
17240266059SGregory Neil Shapiro **  SmEtypeOs represents the failure of a Unix system call.
17340266059SGregory Neil Shapiro **  The three arguments are:
17440266059SGregory Neil Shapiro **   int errno (eg, ENOENT)
17540266059SGregory Neil Shapiro **   char *syscall (eg, "open")
17640266059SGregory Neil Shapiro **   char *sysargs (eg, NULL or "/etc/mail/sendmail.cf")
17740266059SGregory Neil Shapiro */
17840266059SGregory Neil Shapiro 
17940266059SGregory Neil Shapiro const SM_EXC_TYPE_T SmEtypeOs =
18040266059SGregory Neil Shapiro {
18140266059SGregory Neil Shapiro 	SmExcTypeMagic,
18240266059SGregory Neil Shapiro 	"E:sm.os",
18340266059SGregory Neil Shapiro 	"isr",
18440266059SGregory Neil Shapiro 	sm_etype_os_print,
18540266059SGregory Neil Shapiro 	NULL,
18640266059SGregory Neil Shapiro };
18740266059SGregory Neil Shapiro 
18840266059SGregory Neil Shapiro /*
18940266059SGregory Neil Shapiro **  SmEtypeErr is a completely generic error which should only be
19040266059SGregory Neil Shapiro **  used in applications and test programs.  Libraries should use
19140266059SGregory Neil Shapiro **  more specific exception codes.
19240266059SGregory Neil Shapiro */
19340266059SGregory Neil Shapiro 
19440266059SGregory Neil Shapiro const SM_EXC_TYPE_T SmEtypeErr =
19540266059SGregory Neil Shapiro {
19640266059SGregory Neil Shapiro 	SmExcTypeMagic,
19740266059SGregory Neil Shapiro 	"E:sm.err",
19840266059SGregory Neil Shapiro 	"r",
19940266059SGregory Neil Shapiro 	sm_etype_printf,
20040266059SGregory Neil Shapiro 	"%0",
20140266059SGregory Neil Shapiro };
20240266059SGregory Neil Shapiro 
20340266059SGregory Neil Shapiro /*
20440266059SGregory Neil Shapiro **  SM_EXC_VNEW_X -- Construct a new exception object.
20540266059SGregory Neil Shapiro **
20640266059SGregory Neil Shapiro **	Parameters:
20740266059SGregory Neil Shapiro **		etype -- type of exception.
20840266059SGregory Neil Shapiro **		ap -- varargs.
20940266059SGregory Neil Shapiro **
21040266059SGregory Neil Shapiro **	Returns:
21140266059SGregory Neil Shapiro **		pointer to exception object.
21240266059SGregory Neil Shapiro */
21340266059SGregory Neil Shapiro 
21440266059SGregory Neil Shapiro /*
21540266059SGregory Neil Shapiro **  This is an auxiliary function called by sm_exc_new_x and sm_exc_raisenew_x.
21640266059SGregory Neil Shapiro **
21740266059SGregory Neil Shapiro **  If an exception is raised, then to avoid a storage leak, we must:
21840266059SGregory Neil Shapiro **  (a) Free all storage we have allocated.
21940266059SGregory Neil Shapiro **  (b) Free all exception arguments in the varargs list.
22040266059SGregory Neil Shapiro **  Getting this right is tricky.
22140266059SGregory Neil Shapiro **
22240266059SGregory Neil Shapiro **  To see why (b) is required, consider the code fragment
22340266059SGregory Neil Shapiro **     SM_EXCEPT(exc, "*")
22440266059SGregory Neil Shapiro **         sm_exc_raisenew_x(&MyEtype, exc);
22540266059SGregory Neil Shapiro **     SM_END_TRY
22640266059SGregory Neil Shapiro **  In the normal case, sm_exc_raisenew_x will allocate and raise a new
22740266059SGregory Neil Shapiro **  exception E that owns exc.  When E is eventually freed, exc is also freed.
22840266059SGregory Neil Shapiro **  In the exceptional case, sm_exc_raisenew_x must free exc before raising
22940266059SGregory Neil Shapiro **  an out-of-memory exception so that exc is not leaked.
23040266059SGregory Neil Shapiro */
23140266059SGregory Neil Shapiro 
232*5b0945b5SGregory Neil Shapiro static SM_EXC_T *sm_exc_vnew_x __P((const SM_EXC_TYPE_T *, va_list));
233d0cef73dSGregory Neil Shapiro 
234d0cef73dSGregory Neil Shapiro static SM_EXC_T *
sm_exc_vnew_x(etype,ap)23540266059SGregory Neil Shapiro sm_exc_vnew_x(etype, ap)
23640266059SGregory Neil Shapiro 	const SM_EXC_TYPE_T *etype;
237*5b0945b5SGregory Neil Shapiro 	va_list ap;
23840266059SGregory Neil Shapiro {
23940266059SGregory Neil Shapiro 	/*
24040266059SGregory Neil Shapiro 	**  All variables that are modified in the SM_TRY clause and
24140266059SGregory Neil Shapiro 	**  referenced in the SM_EXCEPT clause must be declared volatile.
24240266059SGregory Neil Shapiro 	*/
24340266059SGregory Neil Shapiro 
24440266059SGregory Neil Shapiro 	/* NOTE: Type of si, i, and argc *must* match */
24540266059SGregory Neil Shapiro 	SM_EXC_T * volatile exc = NULL;
24640266059SGregory Neil Shapiro 	int volatile si = 0;
24740266059SGregory Neil Shapiro 	SM_VAL_T * volatile argv = NULL;
24840266059SGregory Neil Shapiro 	int i, argc;
24940266059SGregory Neil Shapiro 
25040266059SGregory Neil Shapiro 	SM_REQUIRE_ISA(etype, SmExcTypeMagic);
25140266059SGregory Neil Shapiro 	argc = strlen(etype->etype_argformat);
25240266059SGregory Neil Shapiro 	SM_TRY
25340266059SGregory Neil Shapiro 	{
25440266059SGregory Neil Shapiro 		/*
25540266059SGregory Neil Shapiro 		**  Step 1.  Allocate the exception structure.
25640266059SGregory Neil Shapiro 		**  On failure, scan the varargs list and free all
25740266059SGregory Neil Shapiro 		**  exception arguments.
25840266059SGregory Neil Shapiro 		*/
25940266059SGregory Neil Shapiro 
26040266059SGregory Neil Shapiro 		exc = sm_malloc_x(sizeof(SM_EXC_T));
26140266059SGregory Neil Shapiro 		exc->sm_magic = SmExcMagic;
26240266059SGregory Neil Shapiro 		exc->exc_refcount = 1;
26340266059SGregory Neil Shapiro 		exc->exc_type = etype;
26440266059SGregory Neil Shapiro 		exc->exc_argv = NULL;
26540266059SGregory Neil Shapiro 
26640266059SGregory Neil Shapiro 		/*
26740266059SGregory Neil Shapiro 		**  Step 2.  Allocate the argument vector.
26840266059SGregory Neil Shapiro 		**  On failure, free exc, scan the varargs list and free all
26940266059SGregory Neil Shapiro 		**  exception arguments.  On success, scan the varargs list,
27040266059SGregory Neil Shapiro 		**  and copy the arguments into argv.
27140266059SGregory Neil Shapiro 		*/
27240266059SGregory Neil Shapiro 
27340266059SGregory Neil Shapiro 		argv = sm_malloc_x(argc * sizeof(SM_VAL_T));
27440266059SGregory Neil Shapiro 		exc->exc_argv = argv;
27540266059SGregory Neil Shapiro 		for (i = 0; i < argc; ++i)
27640266059SGregory Neil Shapiro 		{
27740266059SGregory Neil Shapiro 			switch (etype->etype_argformat[i])
27840266059SGregory Neil Shapiro 			{
27940266059SGregory Neil Shapiro 			  case 'i':
28040266059SGregory Neil Shapiro 				argv[i].v_int = SM_VA_ARG(ap, int);
28140266059SGregory Neil Shapiro 				break;
28240266059SGregory Neil Shapiro 			  case 'l':
28340266059SGregory Neil Shapiro 				argv[i].v_long = SM_VA_ARG(ap, long);
28440266059SGregory Neil Shapiro 				break;
28540266059SGregory Neil Shapiro 			  case 'e':
28640266059SGregory Neil Shapiro 				argv[i].v_exc = SM_VA_ARG(ap, SM_EXC_T*);
28740266059SGregory Neil Shapiro 				break;
28840266059SGregory Neil Shapiro 			  case 's':
28940266059SGregory Neil Shapiro 				argv[i].v_str = SM_VA_ARG(ap, char*);
29040266059SGregory Neil Shapiro 				break;
29140266059SGregory Neil Shapiro 			  case 'r':
29240266059SGregory Neil Shapiro 				SM_REQUIRE(etype->etype_argformat[i+1] == '\0');
29340266059SGregory Neil Shapiro 				argv[i].v_str = SM_VA_ARG(ap, char*);
29440266059SGregory Neil Shapiro 				break;
29540266059SGregory Neil Shapiro 			  default:
29640266059SGregory Neil Shapiro 				sm_abort("sm_exc_vnew_x: bad argformat '%c'",
29740266059SGregory Neil Shapiro 					etype->etype_argformat[i]);
29840266059SGregory Neil Shapiro 			}
29940266059SGregory Neil Shapiro 		}
30040266059SGregory Neil Shapiro 
30140266059SGregory Neil Shapiro 		/*
30240266059SGregory Neil Shapiro 		**  Step 3.  Scan argv, and allocate space for all
30340266059SGregory Neil Shapiro 		**  string arguments.  si is the number of elements
30440266059SGregory Neil Shapiro 		**  of argv that have been processed so far.
30540266059SGregory Neil Shapiro 		**  On failure, free exc, argv, all the exception arguments
30640266059SGregory Neil Shapiro 		**  and all of the strings that have been copied.
30740266059SGregory Neil Shapiro 		*/
30840266059SGregory Neil Shapiro 
30940266059SGregory Neil Shapiro 		for (si = 0; si < argc; ++si)
31040266059SGregory Neil Shapiro 		{
31140266059SGregory Neil Shapiro 			switch (etype->etype_argformat[si])
31240266059SGregory Neil Shapiro 			{
31340266059SGregory Neil Shapiro 			  case 's':
31440266059SGregory Neil Shapiro 			    {
31540266059SGregory Neil Shapiro 				char *str = argv[si].v_str;
31640266059SGregory Neil Shapiro 				if (str != NULL)
31740266059SGregory Neil Shapiro 				    argv[si].v_str = sm_strdup_x(str);
31840266059SGregory Neil Shapiro 			    }
31940266059SGregory Neil Shapiro 			    break;
32040266059SGregory Neil Shapiro 			  case 'r':
32140266059SGregory Neil Shapiro 			    {
32240266059SGregory Neil Shapiro 				char *fmt = argv[si].v_str;
32340266059SGregory Neil Shapiro 				if (fmt != NULL)
32440266059SGregory Neil Shapiro 				    argv[si].v_str = sm_vstringf_x(fmt, ap);
32540266059SGregory Neil Shapiro 			    }
32640266059SGregory Neil Shapiro 			    break;
32740266059SGregory Neil Shapiro 			}
32840266059SGregory Neil Shapiro 		}
32940266059SGregory Neil Shapiro 	}
33040266059SGregory Neil Shapiro 	SM_EXCEPT(e, "*")
33140266059SGregory Neil Shapiro 	{
33240266059SGregory Neil Shapiro 		if (exc == NULL || argv == NULL)
33340266059SGregory Neil Shapiro 		{
33440266059SGregory Neil Shapiro 			/*
33540266059SGregory Neil Shapiro 			**  Failure in step 1 or step 2.
33640266059SGregory Neil Shapiro 			**  Scan ap and free all exception arguments.
33740266059SGregory Neil Shapiro 			*/
33840266059SGregory Neil Shapiro 
33940266059SGregory Neil Shapiro 			for (i = 0; i < argc; ++i)
34040266059SGregory Neil Shapiro 			{
34140266059SGregory Neil Shapiro 				switch (etype->etype_argformat[i])
34240266059SGregory Neil Shapiro 				{
34340266059SGregory Neil Shapiro 				  case 'i':
34440266059SGregory Neil Shapiro 					(void) SM_VA_ARG(ap, int);
34540266059SGregory Neil Shapiro 					break;
34640266059SGregory Neil Shapiro 				  case 'l':
34740266059SGregory Neil Shapiro 					(void) SM_VA_ARG(ap, long);
34840266059SGregory Neil Shapiro 					break;
34940266059SGregory Neil Shapiro 				  case 'e':
35040266059SGregory Neil Shapiro 					sm_exc_free(SM_VA_ARG(ap, SM_EXC_T*));
35140266059SGregory Neil Shapiro 					break;
35240266059SGregory Neil Shapiro 				  case 's':
35340266059SGregory Neil Shapiro 				  case 'r':
35440266059SGregory Neil Shapiro 					(void) SM_VA_ARG(ap, char*);
35540266059SGregory Neil Shapiro 					break;
35640266059SGregory Neil Shapiro 				}
35740266059SGregory Neil Shapiro 			}
35840266059SGregory Neil Shapiro 		}
35940266059SGregory Neil Shapiro 		else
36040266059SGregory Neil Shapiro 		{
36140266059SGregory Neil Shapiro 			/*
36240266059SGregory Neil Shapiro 			**  Failure in step 3.  Scan argv and free
36340266059SGregory Neil Shapiro 			**  all exception arguments and all string
36440266059SGregory Neil Shapiro 			**  arguments that have been duplicated.
36540266059SGregory Neil Shapiro 			**  Then free argv.
36640266059SGregory Neil Shapiro 			*/
36740266059SGregory Neil Shapiro 
36840266059SGregory Neil Shapiro 			for (i = 0; i < argc; ++i)
36940266059SGregory Neil Shapiro 			{
37040266059SGregory Neil Shapiro 				switch (etype->etype_argformat[i])
37140266059SGregory Neil Shapiro 				{
37240266059SGregory Neil Shapiro 				  case 'e':
37340266059SGregory Neil Shapiro 					sm_exc_free(argv[i].v_exc);
37440266059SGregory Neil Shapiro 					break;
37540266059SGregory Neil Shapiro 				  case 's':
37640266059SGregory Neil Shapiro 				  case 'r':
37740266059SGregory Neil Shapiro 					if (i < si)
37840266059SGregory Neil Shapiro 						sm_free(argv[i].v_str);
37940266059SGregory Neil Shapiro 					break;
38040266059SGregory Neil Shapiro 				}
38140266059SGregory Neil Shapiro 			}
38240266059SGregory Neil Shapiro 			sm_free(argv);
38340266059SGregory Neil Shapiro 		}
38440266059SGregory Neil Shapiro 		sm_free(exc);
38540266059SGregory Neil Shapiro 		sm_exc_raise_x(e);
38640266059SGregory Neil Shapiro 	}
38740266059SGregory Neil Shapiro 	SM_END_TRY
38840266059SGregory Neil Shapiro 
38940266059SGregory Neil Shapiro 	return exc;
39040266059SGregory Neil Shapiro }
39140266059SGregory Neil Shapiro 
39240266059SGregory Neil Shapiro /*
39340266059SGregory Neil Shapiro **  SM_EXC_NEW_X -- Construct a new exception object.
39440266059SGregory Neil Shapiro **
39540266059SGregory Neil Shapiro **	Parameters:
39640266059SGregory Neil Shapiro **		etype -- type of exception.
39740266059SGregory Neil Shapiro **		... -- varargs.
39840266059SGregory Neil Shapiro **
39940266059SGregory Neil Shapiro **	Returns:
40040266059SGregory Neil Shapiro **		pointer to exception object.
40140266059SGregory Neil Shapiro */
40240266059SGregory Neil Shapiro 
40340266059SGregory Neil Shapiro SM_EXC_T *
40440266059SGregory Neil Shapiro #if SM_VA_STD
sm_exc_new_x(const SM_EXC_TYPE_T * etype,...)40540266059SGregory Neil Shapiro sm_exc_new_x(
40640266059SGregory Neil Shapiro 	const SM_EXC_TYPE_T *etype,
40740266059SGregory Neil Shapiro 	...)
40840266059SGregory Neil Shapiro #else /* SM_VA_STD */
40940266059SGregory Neil Shapiro sm_exc_new_x(etype, va_alist)
41040266059SGregory Neil Shapiro 	const SM_EXC_TYPE_T *etype;
41140266059SGregory Neil Shapiro 	va_dcl
41240266059SGregory Neil Shapiro #endif /* SM_VA_STD */
41340266059SGregory Neil Shapiro {
41440266059SGregory Neil Shapiro 	SM_EXC_T *exc;
41540266059SGregory Neil Shapiro 	SM_VA_LOCAL_DECL
41640266059SGregory Neil Shapiro 
41740266059SGregory Neil Shapiro 	SM_VA_START(ap, etype);
41840266059SGregory Neil Shapiro 	exc = sm_exc_vnew_x(etype, ap);
41940266059SGregory Neil Shapiro 	SM_VA_END(ap);
42040266059SGregory Neil Shapiro 	return exc;
42140266059SGregory Neil Shapiro }
42240266059SGregory Neil Shapiro 
42340266059SGregory Neil Shapiro /*
42440266059SGregory Neil Shapiro **  SM_EXC_FREE -- Destroy a reference to an exception object.
42540266059SGregory Neil Shapiro **
42640266059SGregory Neil Shapiro **	Parameters:
42740266059SGregory Neil Shapiro **		exc -- exception object.
42840266059SGregory Neil Shapiro **
42940266059SGregory Neil Shapiro **	Returns:
43040266059SGregory Neil Shapiro **		none.
43140266059SGregory Neil Shapiro */
43240266059SGregory Neil Shapiro 
43340266059SGregory Neil Shapiro void
sm_exc_free(exc)43440266059SGregory Neil Shapiro sm_exc_free(exc)
43540266059SGregory Neil Shapiro 	SM_EXC_T *exc;
43640266059SGregory Neil Shapiro {
43740266059SGregory Neil Shapiro 	if (exc == NULL)
43840266059SGregory Neil Shapiro 		return;
43940266059SGregory Neil Shapiro 	SM_REQUIRE(exc->sm_magic == SmExcMagic);
44040266059SGregory Neil Shapiro 	if (exc->exc_refcount == 0)
44140266059SGregory Neil Shapiro 		return;
44240266059SGregory Neil Shapiro 	if (--exc->exc_refcount == 0)
44340266059SGregory Neil Shapiro 	{
44440266059SGregory Neil Shapiro 		int i, c;
44540266059SGregory Neil Shapiro 
44640266059SGregory Neil Shapiro 		for (i = 0; (c = exc->exc_type->etype_argformat[i]) != '\0';
44740266059SGregory Neil Shapiro 		     ++i)
44840266059SGregory Neil Shapiro 		{
44940266059SGregory Neil Shapiro 			switch (c)
45040266059SGregory Neil Shapiro 			{
45140266059SGregory Neil Shapiro 			  case 's':
45240266059SGregory Neil Shapiro 			  case 'r':
45340266059SGregory Neil Shapiro 				sm_free(exc->exc_argv[i].v_str);
45440266059SGregory Neil Shapiro 				break;
45540266059SGregory Neil Shapiro 			  case 'e':
45640266059SGregory Neil Shapiro 				sm_exc_free(exc->exc_argv[i].v_exc);
45740266059SGregory Neil Shapiro 				break;
45840266059SGregory Neil Shapiro 			}
45940266059SGregory Neil Shapiro 		}
46040266059SGregory Neil Shapiro 		exc->sm_magic = NULL;
46140266059SGregory Neil Shapiro 		sm_free(exc->exc_argv);
46240266059SGregory Neil Shapiro 		sm_free(exc);
46340266059SGregory Neil Shapiro 	}
46440266059SGregory Neil Shapiro }
46540266059SGregory Neil Shapiro 
46640266059SGregory Neil Shapiro /*
46740266059SGregory Neil Shapiro **  SM_EXC_MATCH -- Match exception category against a glob pattern.
46840266059SGregory Neil Shapiro **
46940266059SGregory Neil Shapiro **	Parameters:
47040266059SGregory Neil Shapiro **		exc -- exception.
47140266059SGregory Neil Shapiro **		pattern -- glob pattern.
47240266059SGregory Neil Shapiro **
47340266059SGregory Neil Shapiro **	Returns:
47440266059SGregory Neil Shapiro **		true iff match.
47540266059SGregory Neil Shapiro */
47640266059SGregory Neil Shapiro 
47740266059SGregory Neil Shapiro bool
sm_exc_match(exc,pattern)47840266059SGregory Neil Shapiro sm_exc_match(exc, pattern)
47940266059SGregory Neil Shapiro 	SM_EXC_T *exc;
48040266059SGregory Neil Shapiro 	const char *pattern;
48140266059SGregory Neil Shapiro {
48240266059SGregory Neil Shapiro 	if (exc == NULL)
48340266059SGregory Neil Shapiro 		return false;
48440266059SGregory Neil Shapiro 	SM_REQUIRE(exc->sm_magic == SmExcMagic);
48540266059SGregory Neil Shapiro 	return sm_match(exc->exc_type->etype_category, pattern);
48640266059SGregory Neil Shapiro }
48740266059SGregory Neil Shapiro 
48840266059SGregory Neil Shapiro /*
48940266059SGregory Neil Shapiro **  SM_EXC_WRITE -- Write exception message to a stream (wo trailing newline).
49040266059SGregory Neil Shapiro **
49140266059SGregory Neil Shapiro **	Parameters:
49240266059SGregory Neil Shapiro **		exc -- exception.
49340266059SGregory Neil Shapiro **		stream -- file for output.
49440266059SGregory Neil Shapiro **
49540266059SGregory Neil Shapiro **	Returns:
49640266059SGregory Neil Shapiro **		none.
49740266059SGregory Neil Shapiro */
49840266059SGregory Neil Shapiro 
49940266059SGregory Neil Shapiro void
sm_exc_write(exc,stream)50040266059SGregory Neil Shapiro sm_exc_write(exc, stream)
50140266059SGregory Neil Shapiro 	SM_EXC_T *exc;
50240266059SGregory Neil Shapiro 	SM_FILE_T *stream;
50340266059SGregory Neil Shapiro {
50440266059SGregory Neil Shapiro 	SM_REQUIRE_ISA(exc, SmExcMagic);
50540266059SGregory Neil Shapiro 	exc->exc_type->etype_print(exc, stream);
50640266059SGregory Neil Shapiro }
50740266059SGregory Neil Shapiro 
50840266059SGregory Neil Shapiro /*
50940266059SGregory Neil Shapiro **  SM_EXC_PRINT -- Print exception message to a stream (with trailing newline).
51040266059SGregory Neil Shapiro **
51140266059SGregory Neil Shapiro **	Parameters:
51240266059SGregory Neil Shapiro **		exc -- exception.
51340266059SGregory Neil Shapiro **		stream -- file for output.
51440266059SGregory Neil Shapiro **
51540266059SGregory Neil Shapiro **	Returns:
51640266059SGregory Neil Shapiro **		none.
51740266059SGregory Neil Shapiro */
51840266059SGregory Neil Shapiro 
51940266059SGregory Neil Shapiro void
sm_exc_print(exc,stream)52040266059SGregory Neil Shapiro sm_exc_print(exc, stream)
52140266059SGregory Neil Shapiro 	SM_EXC_T *exc;
52240266059SGregory Neil Shapiro 	SM_FILE_T *stream;
52340266059SGregory Neil Shapiro {
52440266059SGregory Neil Shapiro 	SM_REQUIRE_ISA(exc, SmExcMagic);
52540266059SGregory Neil Shapiro 	exc->exc_type->etype_print(exc, stream);
52640266059SGregory Neil Shapiro 	(void) sm_io_putc(stream, SM_TIME_DEFAULT, '\n');
52740266059SGregory Neil Shapiro }
52840266059SGregory Neil Shapiro 
52940266059SGregory Neil Shapiro SM_EXC_HANDLER_T *SmExcHandler = NULL;
53040266059SGregory Neil Shapiro static SM_EXC_DEFAULT_HANDLER_T SmExcDefaultHandler = NULL;
53140266059SGregory Neil Shapiro 
53240266059SGregory Neil Shapiro /*
53340266059SGregory Neil Shapiro **  SM_EXC_NEWTHREAD -- Initialize exception handling for new process/thread.
53440266059SGregory Neil Shapiro **
53540266059SGregory Neil Shapiro **	Parameters:
53640266059SGregory Neil Shapiro **		h -- default exception handler.
53740266059SGregory Neil Shapiro **
53840266059SGregory Neil Shapiro **	Returns:
53940266059SGregory Neil Shapiro **		none.
54040266059SGregory Neil Shapiro */
54140266059SGregory Neil Shapiro 
54240266059SGregory Neil Shapiro /*
54340266059SGregory Neil Shapiro **  Initialize a new process or a new thread by clearing the
54440266059SGregory Neil Shapiro **  exception handler stack and optionally setting a default
54540266059SGregory Neil Shapiro **  exception handler function.  Call this at the beginning of main,
54640266059SGregory Neil Shapiro **  or in a new process after calling fork, or in a new thread.
54740266059SGregory Neil Shapiro **
54840266059SGregory Neil Shapiro **  This function is a luxury, not a necessity.
54940266059SGregory Neil Shapiro **  If h != NULL then you can get the same effect by
55040266059SGregory Neil Shapiro **  wrapping the body of main, or the body of a forked child
55140266059SGregory Neil Shapiro **  or a new thread in SM_TRY ... SM_EXCEPT(e,"*") h(e); SM_END_TRY.
55240266059SGregory Neil Shapiro */
55340266059SGregory Neil Shapiro 
55440266059SGregory Neil Shapiro void
sm_exc_newthread(h)55540266059SGregory Neil Shapiro sm_exc_newthread(h)
55640266059SGregory Neil Shapiro 	SM_EXC_DEFAULT_HANDLER_T h;
55740266059SGregory Neil Shapiro {
55840266059SGregory Neil Shapiro 	SmExcHandler = NULL;
55940266059SGregory Neil Shapiro 	SmExcDefaultHandler = h;
56040266059SGregory Neil Shapiro }
56140266059SGregory Neil Shapiro 
56240266059SGregory Neil Shapiro /*
56340266059SGregory Neil Shapiro **  SM_EXC_RAISE_X -- Raise an exception.
56440266059SGregory Neil Shapiro **
56540266059SGregory Neil Shapiro **	Parameters:
56640266059SGregory Neil Shapiro **		exc -- exception.
56740266059SGregory Neil Shapiro **
56840266059SGregory Neil Shapiro **	Returns:
56940266059SGregory Neil Shapiro **		doesn't.
57040266059SGregory Neil Shapiro */
57140266059SGregory Neil Shapiro 
572323f6dcbSGregory Neil Shapiro void SM_DEAD_D
sm_exc_raise_x(exc)57340266059SGregory Neil Shapiro sm_exc_raise_x(exc)
57440266059SGregory Neil Shapiro 	SM_EXC_T *exc;
57540266059SGregory Neil Shapiro {
57640266059SGregory Neil Shapiro 	SM_REQUIRE_ISA(exc, SmExcMagic);
57740266059SGregory Neil Shapiro 
57840266059SGregory Neil Shapiro 	if (SmExcHandler == NULL)
57940266059SGregory Neil Shapiro 	{
58040266059SGregory Neil Shapiro 		if (SmExcDefaultHandler != NULL)
58140266059SGregory Neil Shapiro 		{
58240266059SGregory Neil Shapiro 			SM_EXC_DEFAULT_HANDLER_T h;
58340266059SGregory Neil Shapiro 
58440266059SGregory Neil Shapiro 			/*
58540266059SGregory Neil Shapiro 			**  If defined, the default handler is expected
58640266059SGregory Neil Shapiro 			**  to terminate the current thread of execution
58740266059SGregory Neil Shapiro 			**  using exit() or pthread_exit().
58840266059SGregory Neil Shapiro 			**  If it instead returns normally, then we fall
58940266059SGregory Neil Shapiro 			**  through to the default case below.  If it
59040266059SGregory Neil Shapiro 			**  raises an exception, then sm_exc_raise_x is
59140266059SGregory Neil Shapiro 			**  re-entered and, because we set SmExcDefaultHandler
59240266059SGregory Neil Shapiro 			**  to NULL before invoking h, we will again
59340266059SGregory Neil Shapiro 			**  end up in the default case below.
59440266059SGregory Neil Shapiro 			*/
59540266059SGregory Neil Shapiro 
59640266059SGregory Neil Shapiro 			h = SmExcDefaultHandler;
59740266059SGregory Neil Shapiro 			SmExcDefaultHandler = NULL;
59840266059SGregory Neil Shapiro 			(*h)(exc);
59940266059SGregory Neil Shapiro 		}
60040266059SGregory Neil Shapiro 
60140266059SGregory Neil Shapiro 		/*
60240266059SGregory Neil Shapiro 		**  No exception handler, so print the error and exit.
60340266059SGregory Neil Shapiro 		**  To override this behaviour on a program wide basis,
60440266059SGregory Neil Shapiro 		**  call sm_exc_newthread or put an exception handler in main().
60540266059SGregory Neil Shapiro 		**
60640266059SGregory Neil Shapiro 		**  XXX TODO: map the exception category to an exit code
60740266059SGregory Neil Shapiro 		**  XXX from <sysexits.h>.
60840266059SGregory Neil Shapiro 		*/
60940266059SGregory Neil Shapiro 
61040266059SGregory Neil Shapiro 		sm_exc_print(exc, smioerr);
61140266059SGregory Neil Shapiro 		exit(255);
61240266059SGregory Neil Shapiro 	}
61340266059SGregory Neil Shapiro 
61440266059SGregory Neil Shapiro 	if (SmExcHandler->eh_value == NULL)
61540266059SGregory Neil Shapiro 		SmExcHandler->eh_value = exc;
61640266059SGregory Neil Shapiro 	else
61740266059SGregory Neil Shapiro 		sm_exc_free(exc);
61840266059SGregory Neil Shapiro 
61940266059SGregory Neil Shapiro 	sm_longjmp_nosig(SmExcHandler->eh_context, 1);
62040266059SGregory Neil Shapiro }
62140266059SGregory Neil Shapiro 
62240266059SGregory Neil Shapiro /*
62340266059SGregory Neil Shapiro **  SM_EXC_RAISENEW_X -- shorthand for sm_exc_raise_x(sm_exc_new_x(...))
62440266059SGregory Neil Shapiro **
62540266059SGregory Neil Shapiro **	Parameters:
62640266059SGregory Neil Shapiro **		etype -- type of exception.
62740266059SGregory Neil Shapiro **		ap -- varargs.
62840266059SGregory Neil Shapiro **
62940266059SGregory Neil Shapiro **	Returns:
63040266059SGregory Neil Shapiro **		none.
63140266059SGregory Neil Shapiro */
63240266059SGregory Neil Shapiro 
633323f6dcbSGregory Neil Shapiro void SM_DEAD_D
63440266059SGregory Neil Shapiro #if SM_VA_STD
sm_exc_raisenew_x(const SM_EXC_TYPE_T * etype,...)63540266059SGregory Neil Shapiro sm_exc_raisenew_x(
63640266059SGregory Neil Shapiro 	const SM_EXC_TYPE_T *etype,
63740266059SGregory Neil Shapiro 	...)
63840266059SGregory Neil Shapiro #else
63940266059SGregory Neil Shapiro sm_exc_raisenew_x(etype, va_alist)
64040266059SGregory Neil Shapiro 	const SM_EXC_TYPE_T *etype;
64140266059SGregory Neil Shapiro 	va_dcl
64240266059SGregory Neil Shapiro #endif
64340266059SGregory Neil Shapiro {
64440266059SGregory Neil Shapiro 	SM_EXC_T *exc;
64540266059SGregory Neil Shapiro 	SM_VA_LOCAL_DECL
64640266059SGregory Neil Shapiro 
64740266059SGregory Neil Shapiro 	SM_VA_START(ap, etype);
64840266059SGregory Neil Shapiro 	exc = sm_exc_vnew_x(etype, ap);
64940266059SGregory Neil Shapiro 	SM_VA_END(ap);
65040266059SGregory Neil Shapiro 	sm_exc_raise_x(exc);
65140266059SGregory Neil Shapiro }
652