summaryrefslogtreecommitdiffstats
path: root/contrib/sendmail/libsm/exc.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/sendmail/libsm/exc.c')
-rw-r--r--contrib/sendmail/libsm/exc.c669
1 files changed, 669 insertions, 0 deletions
diff --git a/contrib/sendmail/libsm/exc.c b/contrib/sendmail/libsm/exc.c
new file mode 100644
index 0000000..9cc1c6f
--- /dev/null
+++ b/contrib/sendmail/libsm/exc.c
@@ -0,0 +1,669 @@
+/*
+ * Copyright (c) 2000-2002 Sendmail, Inc. and its suppliers.
+ * All rights reserved.
+ *
+ * By using this file, you agree to the terms and conditions set
+ * forth in the LICENSE file which can be found at the top level of
+ * the sendmail distribution.
+ *
+ */
+
+#include <sm/gen.h>
+SM_RCSID("@(#)$Id: exc.c,v 1.47 2002/01/09 18:51:43 ca Exp $")
+
+/*
+** exception handling
+** For documentation, see exc.html
+*/
+
+#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";
+
+/*
+** SM_ETYPE_PRINTF -- printf for exception types.
+**
+** Parameters:
+** exc -- exception.
+** stream -- file for output.
+**
+** Returns:
+** none.
+*/
+
+/*
+** A simple formatted print function that can be used as the print function
+** by most exception types. It prints the printcontext string, interpreting
+** occurrences of %0 through %9 as references to the argument vector.
+** If exception argument 3 is an int or long, then %3 will print the
+** argument in decimal, and %o3 or %x3 will print it in octal or hex.
+*/
+
+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);
+ }
+}
+
+/*
+** Standard exception types.
+*/
+
+/*
+** SM_ETYPE_OS_PRINT -- Print OS related exception.
+**
+** Parameters:
+** exc -- exception.
+** stream -- file for output.
+**
+** Returns:
+** none.
+*/
+
+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));
+}
+
+/*
+** SmEtypeOs represents the failure of a Unix system call.
+** The three arguments are:
+** int errno (eg, ENOENT)
+** char *syscall (eg, "open")
+** char *sysargs (eg, NULL or "/etc/mail/sendmail.cf")
+*/
+
+const SM_EXC_TYPE_T SmEtypeOs =
+{
+ SmExcTypeMagic,
+ "E:sm.os",
+ "isr",
+ sm_etype_os_print,
+ NULL,
+};
+
+/*
+** SmEtypeErr is a completely generic error which should only be
+** used in applications and test programs. Libraries should use
+** more specific exception codes.
+*/
+
+const SM_EXC_TYPE_T SmEtypeErr =
+{
+ SmExcTypeMagic,
+ "E:sm.err",
+ "r",
+ sm_etype_printf,
+ "%0",
+};
+
+/*
+** SM_EXC_VNEW_X -- Construct a new exception object.
+**
+** Parameters:
+** etype -- type of exception.
+** ap -- varargs.
+**
+** Returns:
+** pointer to exception object.
+*/
+
+/*
+** This is an auxiliary function called by sm_exc_new_x and sm_exc_raisenew_x.
+**
+** If an exception is raised, then to avoid a storage leak, we must:
+** (a) Free all storage we have allocated.
+** (b) Free all exception arguments in the varargs list.
+** Getting this right is tricky.
+**
+** To see why (b) is required, consider the code fragment
+** SM_EXCEPT(exc, "*")
+** sm_exc_raisenew_x(&MyEtype, exc);
+** SM_END_TRY
+** In the normal case, sm_exc_raisenew_x will allocate and raise a new
+** exception E that owns exc. When E is eventually freed, exc is also freed.
+** In the exceptional case, sm_exc_raisenew_x must free exc before raising
+** an out-of-memory exception so that exc is not leaked.
+*/
+
+SM_EXC_T *
+sm_exc_vnew_x(etype, ap)
+ const SM_EXC_TYPE_T *etype;
+ va_list SM_NONVOLATILE ap;
+{
+ /*
+ ** All variables that are modified in the SM_TRY clause and
+ ** referenced in the SM_EXCEPT clause must be declared volatile.
+ */
+
+ /* NOTE: Type of si, i, and argc *must* match */
+ 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
+ {
+ /*
+ ** Step 1. Allocate the exception structure.
+ ** On failure, scan the varargs list and free all
+ ** exception arguments.
+ */
+
+ exc = sm_malloc_x(sizeof(SM_EXC_T));
+ exc->sm_magic = SmExcMagic;
+ exc->exc_refcount = 1;
+ exc->exc_type = etype;
+ exc->exc_argv = NULL;
+
+ /*
+ ** Step 2. Allocate the argument vector.
+ ** On failure, free exc, scan the varargs list and free all
+ ** exception arguments. On success, scan the varargs list,
+ ** and copy the arguments into argv.
+ */
+
+ 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]);
+ }
+ }
+
+ /*
+ ** Step 3. Scan argv, and allocate space for all
+ ** string arguments. si is the number of elements
+ ** of argv that have been processed so far.
+ ** On failure, free exc, argv, all the exception arguments
+ ** and all of the strings that have been copied.
+ */
+
+ 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)
+ {
+ /*
+ ** Failure in step 1 or step 2.
+ ** Scan ap and free all exception arguments.
+ */
+
+ 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
+ {
+ /*
+ ** Failure in step 3. Scan argv and free
+ ** all exception arguments and all string
+ ** arguments that have been duplicated.
+ ** Then free argv.
+ */
+
+ 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_NEW_X -- Construct a new exception object.
+**
+** Parameters:
+** etype -- type of exception.
+** ... -- varargs.
+**
+** Returns:
+** pointer to exception object.
+*/
+
+SM_EXC_T *
+#if SM_VA_STD
+sm_exc_new_x(
+ const SM_EXC_TYPE_T *etype,
+ ...)
+#else /* SM_VA_STD */
+sm_exc_new_x(etype, va_alist)
+ const SM_EXC_TYPE_T *etype;
+ va_dcl
+#endif /* SM_VA_STD */
+{
+ 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;
+}
+
+/*
+** SM_ADDREF -- Add a reference to an exception object.
+**
+** Parameters:
+** exc -- exception object.
+**
+** Returns:
+** exc itself.
+*/
+
+SM_EXC_T *
+sm_addref(exc)
+ SM_EXC_T *exc;
+{
+ SM_REQUIRE_ISA(exc, SmExcMagic);
+ if (exc->exc_refcount != 0)
+ ++exc->exc_refcount;
+ return exc;
+}
+
+/*
+** SM_EXC_FREE -- Destroy a reference to an exception object.
+**
+** Parameters:
+** exc -- exception object.
+**
+** Returns:
+** none.
+*/
+
+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);
+ }
+}
+
+/*
+** SM_EXC_MATCH -- Match exception category against a glob pattern.
+**
+** Parameters:
+** exc -- exception.
+** pattern -- glob pattern.
+**
+** Returns:
+** true iff match.
+*/
+
+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);
+}
+
+/*
+** SM_EXC_WRITE -- Write exception message to a stream (wo trailing newline).
+**
+** Parameters:
+** exc -- exception.
+** stream -- file for output.
+**
+** Returns:
+** none.
+*/
+
+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);
+}
+
+/*
+** SM_EXC_PRINT -- Print exception message to a stream (with trailing newline).
+**
+** Parameters:
+** exc -- exception.
+** stream -- file for output.
+**
+** Returns:
+** none.
+*/
+
+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;
+
+/*
+** SM_EXC_NEWTHREAD -- Initialize exception handling for new process/thread.
+**
+** Parameters:
+** h -- default exception handler.
+**
+** Returns:
+** none.
+*/
+
+/*
+** Initialize a new process or a new thread by clearing the
+** exception handler stack and optionally setting a default
+** exception handler function. Call this at the beginning of main,
+** or in a new process after calling fork, or in a new thread.
+**
+** This function is a luxury, not a necessity.
+** If h != NULL then you can get the same effect by
+** wrapping the body of main, or the body of a forked child
+** or a new thread in SM_TRY ... SM_EXCEPT(e,"*") h(e); SM_END_TRY.
+*/
+
+void
+sm_exc_newthread(h)
+ SM_EXC_DEFAULT_HANDLER_T h;
+{
+ SmExcHandler = NULL;
+ SmExcDefaultHandler = h;
+}
+
+/*
+** SM_EXC_RAISE_X -- Raise an exception.
+**
+** Parameters:
+** exc -- exception.
+**
+** Returns:
+** doesn't.
+*/
+
+void
+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;
+
+ /*
+ ** If defined, the default handler is expected
+ ** to terminate the current thread of execution
+ ** using exit() or pthread_exit().
+ ** If it instead returns normally, then we fall
+ ** through to the default case below. If it
+ ** raises an exception, then sm_exc_raise_x is
+ ** re-entered and, because we set SmExcDefaultHandler
+ ** to NULL before invoking h, we will again
+ ** end up in the default case below.
+ */
+
+ h = SmExcDefaultHandler;
+ SmExcDefaultHandler = NULL;
+ (*h)(exc);
+ }
+
+ /*
+ ** No exception handler, so print the error and exit.
+ ** To override this behaviour on a program wide basis,
+ ** call sm_exc_newthread or put an exception handler in main().
+ **
+ ** XXX TODO: map the exception category to an exit code
+ ** XXX from <sysexits.h>.
+ */
+
+ 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);
+}
+
+/*
+** SM_EXC_RAISENEW_X -- shorthand for sm_exc_raise_x(sm_exc_new_x(...))
+**
+** Parameters:
+** etype -- type of exception.
+** ap -- varargs.
+**
+** Returns:
+** none.
+*/
+
+void
+#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);
+}
OpenPOWER on IntegriCloud