/*
* Copyright (c) 2000-2002 Proofpoint, 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.
*
*/
/*
** exception handling
** For documentation, see exc.html
*/
#include <ctype.h>
#include <string.h>
#include <sm/errstring.h>
/*
** 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
{
const char *p, *s;
char format;
{
if (*p != '%')
{
continue;
}
++p;
if (*p == '\0')
{
break;
}
if (*p == '%')
{
continue;
}
format = '\0';
if (isalpha(*p))
{
format = *p++;
if (*p == '\0')
{
format);
break;
}
}
if (isdigit(*p))
{
size_t i = *p - '0';
if (i < n)
{
{
case 's':
case 'r':
if (s == NULL)
s = "(null)";
continue;
case 'i':
: "%d",
continue;
case 'l':
: "%ld",
continue;
case 'e':
stream);
continue;
}
}
}
if (format)
}
}
/*
** Standard exception types.
*/
/*
** SM_ETYPE_OS_PRINT -- Print OS related exception.
**
** Parameters:
** exc -- exception.
** stream -- file for output.
**
** Returns:
** none.
*/
static void
static void
{
if (sysargs)
else
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")
*/
{
"E:sm.os",
"isr",
NULL,
};
/*
** SmEtypeErr is a completely generic error which should only be
** used in applications and test programs. Libraries should use
** more specific exception codes.
*/
{
"E:sm.err",
"r",
"%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.
*/
static SM_EXC_T *
const SM_EXC_TYPE_T *etype;
{
/*
** 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 */
int volatile si = 0;
int i, argc;
{
/*
** Step 1. Allocate the exception structure.
** On failure, scan the varargs list and free all
** exception arguments.
*/
/*
** 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.
*/
for (i = 0; i < argc; ++i)
{
switch (etype->etype_argformat[i])
{
case 'i':
break;
case 'l':
break;
case 'e':
break;
case 's':
break;
case 'r':
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.
*/
{
{
case 's':
{
}
break;
case 'r':
{
}
break;
}
}
}
SM_EXCEPT(e, "*")
{
{
/*
** 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':
break;
case 'l':
break;
case 'e':
break;
case 's':
case 'r':
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':
break;
case 's':
case 'r':
if (i < si)
break;
}
}
}
sm_exc_raise_x(e);
}
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
const SM_EXC_TYPE_T *etype,
...)
#else /* SM_VA_STD */
const SM_EXC_TYPE_T *etype;
#endif /* SM_VA_STD */
{
return exc;
}
/*
** SM_EXC_FREE -- Destroy a reference to an exception object.
**
** Parameters:
** exc -- exception object.
**
** Returns:
** none.
*/
void
{
return;
if (exc->exc_refcount == 0)
return;
if (--exc->exc_refcount == 0)
{
int i, c;
++i)
{
switch (c)
{
case 's':
case 'r':
break;
case 'e':
break;
}
}
}
}
/*
** SM_EXC_MATCH -- Match exception category against a glob pattern.
**
** Parameters:
** exc -- exception.
** pattern -- glob pattern.
**
** Returns:
** true iff match.
*/
bool
const char *pattern;
{
return false;
}
/*
** SM_EXC_WRITE -- Write exception message to a stream (wo trailing newline).
**
** Parameters:
** exc -- exception.
** stream -- file for output.
**
** Returns:
** none.
*/
void
{
}
/*
** SM_EXC_PRINT -- Print exception message to a stream (with trailing newline).
**
** Parameters:
** exc -- exception.
** stream -- file for output.
**
** Returns:
** none.
*/
void
{
}
/*
**
** 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
{
SmExcHandler = NULL;
SmExcDefaultHandler = h;
}
/*
** SM_EXC_RAISE_X -- Raise an exception.
**
** Parameters:
** exc -- exception.
**
** Returns:
** doesn't.
*/
void SM_DEAD_D
{
if (SmExcHandler == NULL)
{
if (SmExcDefaultHandler != NULL)
{
/*
** 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;
(*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>.
*/
exit(255);
}
else
}
/*
** SM_EXC_RAISENEW_X -- shorthand for sm_exc_raise_x(sm_exc_new_x(...))
**
** Parameters:
** etype -- type of exception.
** ap -- varargs.
**
** Returns:
** none.
*/
void SM_DEAD_D
#if SM_VA_STD
const SM_EXC_TYPE_T *etype,
...)
#else
const SM_EXC_TYPE_T *etype;
#endif
{
}