Import sendmail 8.13.7
[dragonfly.git] / contrib / sendmail-8.13.7 / libsm / exc.c
blob26ad020ba7536a1a9d14e36ac8f3bfa0c3bc9be8
1 /*
2 * Copyright (c) 2000-2002 Sendmail, Inc. and its suppliers.
3 * All rights reserved.
5 * By using this file, you agree to the terms and conditions set
6 * forth in the LICENSE file which can be found at the top level of
7 * the sendmail distribution.
9 */
11 #include <sm/gen.h>
12 SM_RCSID("@(#)$Id: exc.c,v 1.48 2003/12/05 22:45:24 ca Exp $")
15 ** exception handling
16 ** For documentation, see exc.html
19 #include <ctype.h>
20 #include <string.h>
22 #include <sm/errstring.h>
23 #include <sm/exc.h>
24 #include <sm/heap.h>
25 #include <sm/string.h>
26 #include <sm/varargs.h>
27 #include <sm/io.h>
29 const char SmExcMagic[] = "sm_exc";
30 const char SmExcTypeMagic[] = "sm_exc_type";
33 ** SM_ETYPE_PRINTF -- printf for exception types.
35 ** Parameters:
36 ** exc -- exception.
37 ** stream -- file for output.
39 ** Returns:
40 ** none.
44 ** A simple formatted print function that can be used as the print function
45 ** by most exception types. It prints the printcontext string, interpreting
46 ** occurrences of %0 through %9 as references to the argument vector.
47 ** If exception argument 3 is an int or long, then %3 will print the
48 ** argument in decimal, and %o3 or %x3 will print it in octal or hex.
51 void
52 sm_etype_printf(exc, stream)
53 SM_EXC_T *exc;
54 SM_FILE_T *stream;
56 size_t n = strlen(exc->exc_type->etype_argformat);
57 const char *p, *s;
58 char format;
60 for (p = exc->exc_type->etype_printcontext; *p != '\0'; ++p)
62 if (*p != '%')
64 (void) sm_io_putc(stream, SM_TIME_DEFAULT, *p);
65 continue;
67 ++p;
68 if (*p == '\0')
70 (void) sm_io_putc(stream, SM_TIME_DEFAULT, '%');
71 break;
73 if (*p == '%')
75 (void) sm_io_putc(stream, SM_TIME_DEFAULT, '%');
76 continue;
78 format = '\0';
79 if (isalpha(*p))
81 format = *p++;
82 if (*p == '\0')
84 (void) sm_io_putc(stream, SM_TIME_DEFAULT, '%');
85 (void) sm_io_putc(stream, SM_TIME_DEFAULT,
86 format);
87 break;
90 if (isdigit(*p))
92 size_t i = *p - '0';
93 if (i < n)
95 switch (exc->exc_type->etype_argformat[i])
97 case 's':
98 case 'r':
99 s = exc->exc_argv[i].v_str;
100 if (s == NULL)
101 s = "(null)";
102 sm_io_fputs(stream, SM_TIME_DEFAULT, s);
103 continue;
104 case 'i':
105 sm_io_fprintf(stream,
106 SM_TIME_DEFAULT,
107 format == 'o' ? "%o"
108 : format == 'x' ? "%x"
109 : "%d",
110 exc->exc_argv[i].v_int);
111 continue;
112 case 'l':
113 sm_io_fprintf(stream,
114 SM_TIME_DEFAULT,
115 format == 'o' ? "%lo"
116 : format == 'x' ? "%lx"
117 : "%ld",
118 exc->exc_argv[i].v_long);
119 continue;
120 case 'e':
121 sm_exc_write(exc->exc_argv[i].v_exc,
122 stream);
123 continue;
127 (void) sm_io_putc(stream, SM_TIME_DEFAULT, '%');
128 if (format)
129 (void) sm_io_putc(stream, SM_TIME_DEFAULT, format);
130 (void) sm_io_putc(stream, SM_TIME_DEFAULT, *p);
135 ** Standard exception types.
139 ** SM_ETYPE_OS_PRINT -- Print OS related exception.
141 ** Parameters:
142 ** exc -- exception.
143 ** stream -- file for output.
145 ** Returns:
146 ** none.
149 static void
150 sm_etype_os_print __P((
151 SM_EXC_T *exc,
152 SM_FILE_T *stream));
154 static void
155 sm_etype_os_print(exc, stream)
156 SM_EXC_T *exc;
157 SM_FILE_T *stream;
159 int err = exc->exc_argv[0].v_int;
160 char *syscall = exc->exc_argv[1].v_str;
161 char *sysargs = exc->exc_argv[2].v_str;
163 if (sysargs)
164 sm_io_fprintf(stream, SM_TIME_DEFAULT, "%s: %s failed: %s",
165 sysargs, syscall, sm_errstring(err));
166 else
167 sm_io_fprintf(stream, SM_TIME_DEFAULT, "%s failed: %s", syscall,
168 sm_errstring(err));
172 ** SmEtypeOs represents the failure of a Unix system call.
173 ** The three arguments are:
174 ** int errno (eg, ENOENT)
175 ** char *syscall (eg, "open")
176 ** char *sysargs (eg, NULL or "/etc/mail/sendmail.cf")
179 const SM_EXC_TYPE_T SmEtypeOs =
181 SmExcTypeMagic,
182 "E:sm.os",
183 "isr",
184 sm_etype_os_print,
185 NULL,
189 ** SmEtypeErr is a completely generic error which should only be
190 ** used in applications and test programs. Libraries should use
191 ** more specific exception codes.
194 const SM_EXC_TYPE_T SmEtypeErr =
196 SmExcTypeMagic,
197 "E:sm.err",
198 "r",
199 sm_etype_printf,
200 "%0",
204 ** SM_EXC_VNEW_X -- Construct a new exception object.
206 ** Parameters:
207 ** etype -- type of exception.
208 ** ap -- varargs.
210 ** Returns:
211 ** pointer to exception object.
215 ** This is an auxiliary function called by sm_exc_new_x and sm_exc_raisenew_x.
217 ** If an exception is raised, then to avoid a storage leak, we must:
218 ** (a) Free all storage we have allocated.
219 ** (b) Free all exception arguments in the varargs list.
220 ** Getting this right is tricky.
222 ** To see why (b) is required, consider the code fragment
223 ** SM_EXCEPT(exc, "*")
224 ** sm_exc_raisenew_x(&MyEtype, exc);
225 ** SM_END_TRY
226 ** In the normal case, sm_exc_raisenew_x will allocate and raise a new
227 ** exception E that owns exc. When E is eventually freed, exc is also freed.
228 ** In the exceptional case, sm_exc_raisenew_x must free exc before raising
229 ** an out-of-memory exception so that exc is not leaked.
232 SM_EXC_T *
233 sm_exc_vnew_x(etype, ap)
234 const SM_EXC_TYPE_T *etype;
235 va_list SM_NONVOLATILE ap;
238 ** All variables that are modified in the SM_TRY clause and
239 ** referenced in the SM_EXCEPT clause must be declared volatile.
242 /* NOTE: Type of si, i, and argc *must* match */
243 SM_EXC_T * volatile exc = NULL;
244 int volatile si = 0;
245 SM_VAL_T * volatile argv = NULL;
246 int i, argc;
248 SM_REQUIRE_ISA(etype, SmExcTypeMagic);
249 argc = strlen(etype->etype_argformat);
250 SM_TRY
253 ** Step 1. Allocate the exception structure.
254 ** On failure, scan the varargs list and free all
255 ** exception arguments.
258 exc = sm_malloc_x(sizeof(SM_EXC_T));
259 exc->sm_magic = SmExcMagic;
260 exc->exc_refcount = 1;
261 exc->exc_type = etype;
262 exc->exc_argv = NULL;
265 ** Step 2. Allocate the argument vector.
266 ** On failure, free exc, scan the varargs list and free all
267 ** exception arguments. On success, scan the varargs list,
268 ** and copy the arguments into argv.
271 argv = sm_malloc_x(argc * sizeof(SM_VAL_T));
272 exc->exc_argv = argv;
273 for (i = 0; i < argc; ++i)
275 switch (etype->etype_argformat[i])
277 case 'i':
278 argv[i].v_int = SM_VA_ARG(ap, int);
279 break;
280 case 'l':
281 argv[i].v_long = SM_VA_ARG(ap, long);
282 break;
283 case 'e':
284 argv[i].v_exc = SM_VA_ARG(ap, SM_EXC_T*);
285 break;
286 case 's':
287 argv[i].v_str = SM_VA_ARG(ap, char*);
288 break;
289 case 'r':
290 SM_REQUIRE(etype->etype_argformat[i+1] == '\0');
291 argv[i].v_str = SM_VA_ARG(ap, char*);
292 break;
293 default:
294 sm_abort("sm_exc_vnew_x: bad argformat '%c'",
295 etype->etype_argformat[i]);
300 ** Step 3. Scan argv, and allocate space for all
301 ** string arguments. si is the number of elements
302 ** of argv that have been processed so far.
303 ** On failure, free exc, argv, all the exception arguments
304 ** and all of the strings that have been copied.
307 for (si = 0; si < argc; ++si)
309 switch (etype->etype_argformat[si])
311 case 's':
313 char *str = argv[si].v_str;
314 if (str != NULL)
315 argv[si].v_str = sm_strdup_x(str);
317 break;
318 case 'r':
320 char *fmt = argv[si].v_str;
321 if (fmt != NULL)
322 argv[si].v_str = sm_vstringf_x(fmt, ap);
324 break;
328 SM_EXCEPT(e, "*")
330 if (exc == NULL || argv == NULL)
333 ** Failure in step 1 or step 2.
334 ** Scan ap and free all exception arguments.
337 for (i = 0; i < argc; ++i)
339 switch (etype->etype_argformat[i])
341 case 'i':
342 (void) SM_VA_ARG(ap, int);
343 break;
344 case 'l':
345 (void) SM_VA_ARG(ap, long);
346 break;
347 case 'e':
348 sm_exc_free(SM_VA_ARG(ap, SM_EXC_T*));
349 break;
350 case 's':
351 case 'r':
352 (void) SM_VA_ARG(ap, char*);
353 break;
357 else
360 ** Failure in step 3. Scan argv and free
361 ** all exception arguments and all string
362 ** arguments that have been duplicated.
363 ** Then free argv.
366 for (i = 0; i < argc; ++i)
368 switch (etype->etype_argformat[i])
370 case 'e':
371 sm_exc_free(argv[i].v_exc);
372 break;
373 case 's':
374 case 'r':
375 if (i < si)
376 sm_free(argv[i].v_str);
377 break;
380 sm_free(argv);
382 sm_free(exc);
383 sm_exc_raise_x(e);
385 SM_END_TRY
387 return exc;
391 ** SM_EXC_NEW_X -- Construct a new exception object.
393 ** Parameters:
394 ** etype -- type of exception.
395 ** ... -- varargs.
397 ** Returns:
398 ** pointer to exception object.
401 SM_EXC_T *
402 #if SM_VA_STD
403 sm_exc_new_x(
404 const SM_EXC_TYPE_T *etype,
405 ...)
406 #else /* SM_VA_STD */
407 sm_exc_new_x(etype, va_alist)
408 const SM_EXC_TYPE_T *etype;
409 va_dcl
410 #endif /* SM_VA_STD */
412 SM_EXC_T *exc;
413 SM_VA_LOCAL_DECL
415 SM_VA_START(ap, etype);
416 exc = sm_exc_vnew_x(etype, ap);
417 SM_VA_END(ap);
418 return exc;
422 ** SM_ADDREF -- Add a reference to an exception object.
424 ** Parameters:
425 ** exc -- exception object.
427 ** Returns:
428 ** exc itself.
431 SM_EXC_T *
432 sm_addref(exc)
433 SM_EXC_T *exc;
435 SM_REQUIRE_ISA(exc, SmExcMagic);
436 if (exc->exc_refcount != 0)
437 ++exc->exc_refcount;
438 return exc;
442 ** SM_EXC_FREE -- Destroy a reference to an exception object.
444 ** Parameters:
445 ** exc -- exception object.
447 ** Returns:
448 ** none.
451 void
452 sm_exc_free(exc)
453 SM_EXC_T *exc;
455 if (exc == NULL)
456 return;
457 SM_REQUIRE(exc->sm_magic == SmExcMagic);
458 if (exc->exc_refcount == 0)
459 return;
460 if (--exc->exc_refcount == 0)
462 int i, c;
464 for (i = 0; (c = exc->exc_type->etype_argformat[i]) != '\0';
465 ++i)
467 switch (c)
469 case 's':
470 case 'r':
471 sm_free(exc->exc_argv[i].v_str);
472 break;
473 case 'e':
474 sm_exc_free(exc->exc_argv[i].v_exc);
475 break;
478 exc->sm_magic = NULL;
479 sm_free(exc->exc_argv);
480 sm_free(exc);
485 ** SM_EXC_MATCH -- Match exception category against a glob pattern.
487 ** Parameters:
488 ** exc -- exception.
489 ** pattern -- glob pattern.
491 ** Returns:
492 ** true iff match.
495 bool
496 sm_exc_match(exc, pattern)
497 SM_EXC_T *exc;
498 const char *pattern;
500 if (exc == NULL)
501 return false;
502 SM_REQUIRE(exc->sm_magic == SmExcMagic);
503 return sm_match(exc->exc_type->etype_category, pattern);
507 ** SM_EXC_WRITE -- Write exception message to a stream (wo trailing newline).
509 ** Parameters:
510 ** exc -- exception.
511 ** stream -- file for output.
513 ** Returns:
514 ** none.
517 void
518 sm_exc_write(exc, stream)
519 SM_EXC_T *exc;
520 SM_FILE_T *stream;
522 SM_REQUIRE_ISA(exc, SmExcMagic);
523 exc->exc_type->etype_print(exc, stream);
527 ** SM_EXC_PRINT -- Print exception message to a stream (with trailing newline).
529 ** Parameters:
530 ** exc -- exception.
531 ** stream -- file for output.
533 ** Returns:
534 ** none.
537 void
538 sm_exc_print(exc, stream)
539 SM_EXC_T *exc;
540 SM_FILE_T *stream;
542 SM_REQUIRE_ISA(exc, SmExcMagic);
543 exc->exc_type->etype_print(exc, stream);
544 (void) sm_io_putc(stream, SM_TIME_DEFAULT, '\n');
547 SM_EXC_HANDLER_T *SmExcHandler = NULL;
548 static SM_EXC_DEFAULT_HANDLER_T SmExcDefaultHandler = NULL;
551 ** SM_EXC_NEWTHREAD -- Initialize exception handling for new process/thread.
553 ** Parameters:
554 ** h -- default exception handler.
556 ** Returns:
557 ** none.
561 ** Initialize a new process or a new thread by clearing the
562 ** exception handler stack and optionally setting a default
563 ** exception handler function. Call this at the beginning of main,
564 ** or in a new process after calling fork, or in a new thread.
566 ** This function is a luxury, not a necessity.
567 ** If h != NULL then you can get the same effect by
568 ** wrapping the body of main, or the body of a forked child
569 ** or a new thread in SM_TRY ... SM_EXCEPT(e,"*") h(e); SM_END_TRY.
572 void
573 sm_exc_newthread(h)
574 SM_EXC_DEFAULT_HANDLER_T h;
576 SmExcHandler = NULL;
577 SmExcDefaultHandler = h;
581 ** SM_EXC_RAISE_X -- Raise an exception.
583 ** Parameters:
584 ** exc -- exception.
586 ** Returns:
587 ** doesn't.
590 void SM_DEAD_D
591 sm_exc_raise_x(exc)
592 SM_EXC_T *exc;
594 SM_REQUIRE_ISA(exc, SmExcMagic);
596 if (SmExcHandler == NULL)
598 if (SmExcDefaultHandler != NULL)
600 SM_EXC_DEFAULT_HANDLER_T h;
603 ** If defined, the default handler is expected
604 ** to terminate the current thread of execution
605 ** using exit() or pthread_exit().
606 ** If it instead returns normally, then we fall
607 ** through to the default case below. If it
608 ** raises an exception, then sm_exc_raise_x is
609 ** re-entered and, because we set SmExcDefaultHandler
610 ** to NULL before invoking h, we will again
611 ** end up in the default case below.
614 h = SmExcDefaultHandler;
615 SmExcDefaultHandler = NULL;
616 (*h)(exc);
620 ** No exception handler, so print the error and exit.
621 ** To override this behaviour on a program wide basis,
622 ** call sm_exc_newthread or put an exception handler in main().
624 ** XXX TODO: map the exception category to an exit code
625 ** XXX from <sysexits.h>.
628 sm_exc_print(exc, smioerr);
629 exit(255);
632 if (SmExcHandler->eh_value == NULL)
633 SmExcHandler->eh_value = exc;
634 else
635 sm_exc_free(exc);
637 sm_longjmp_nosig(SmExcHandler->eh_context, 1);
641 ** SM_EXC_RAISENEW_X -- shorthand for sm_exc_raise_x(sm_exc_new_x(...))
643 ** Parameters:
644 ** etype -- type of exception.
645 ** ap -- varargs.
647 ** Returns:
648 ** none.
651 void SM_DEAD_D
652 #if SM_VA_STD
653 sm_exc_raisenew_x(
654 const SM_EXC_TYPE_T *etype,
655 ...)
656 #else
657 sm_exc_raisenew_x(etype, va_alist)
658 const SM_EXC_TYPE_T *etype;
659 va_dcl
660 #endif
662 SM_EXC_T *exc;
663 SM_VA_LOCAL_DECL
665 SM_VA_START(ap, etype);
666 exc = sm_exc_vnew_x(etype, ap);
667 SM_VA_END(ap);
668 sm_exc_raise_x(exc);