8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / cmd / sendmail / libsm / exc.c
blob78fb1c0a9899ac4d51c814fbcc018d606fca3583
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 #pragma ident "%Z%%M% %I% %E% SMI"
13 #include <sm/gen.h>
14 SM_RCSID("@(#)$Id: exc.c,v 1.49 2006/12/19 19:28:09 ca Exp $")
17 ** exception handling
18 ** For documentation, see exc.html
21 #include <ctype.h>
22 #include <string.h>
24 #include <sm/errstring.h>
25 #include <sm/exc.h>
26 #include <sm/heap.h>
27 #include <sm/string.h>
28 #include <sm/varargs.h>
29 #include <sm/io.h>
31 const char SmExcMagic[] = "sm_exc";
32 const char SmExcTypeMagic[] = "sm_exc_type";
35 ** SM_ETYPE_PRINTF -- printf for exception types.
37 ** Parameters:
38 ** exc -- exception.
39 ** stream -- file for output.
41 ** Returns:
42 ** none.
46 ** A simple formatted print function that can be used as the print function
47 ** by most exception types. It prints the printcontext string, interpreting
48 ** occurrences of %0 through %9 as references to the argument vector.
49 ** If exception argument 3 is an int or long, then %3 will print the
50 ** argument in decimal, and %o3 or %x3 will print it in octal or hex.
53 void
54 sm_etype_printf(exc, stream)
55 SM_EXC_T *exc;
56 SM_FILE_T *stream;
58 size_t n = strlen(exc->exc_type->etype_argformat);
59 const char *p, *s;
60 char format;
62 for (p = exc->exc_type->etype_printcontext; *p != '\0'; ++p)
64 if (*p != '%')
66 (void) sm_io_putc(stream, SM_TIME_DEFAULT, *p);
67 continue;
69 ++p;
70 if (*p == '\0')
72 (void) sm_io_putc(stream, SM_TIME_DEFAULT, '%');
73 break;
75 if (*p == '%')
77 (void) sm_io_putc(stream, SM_TIME_DEFAULT, '%');
78 continue;
80 format = '\0';
81 if (isalpha(*p))
83 format = *p++;
84 if (*p == '\0')
86 (void) sm_io_putc(stream, SM_TIME_DEFAULT, '%');
87 (void) sm_io_putc(stream, SM_TIME_DEFAULT,
88 format);
89 break;
92 if (isdigit(*p))
94 size_t i = *p - '0';
95 if (i < n)
97 switch (exc->exc_type->etype_argformat[i])
99 case 's':
100 case 'r':
101 s = exc->exc_argv[i].v_str;
102 if (s == NULL)
103 s = "(null)";
104 sm_io_fputs(stream, SM_TIME_DEFAULT, s);
105 continue;
106 case 'i':
107 sm_io_fprintf(stream,
108 SM_TIME_DEFAULT,
109 format == 'o' ? "%o"
110 : format == 'x' ? "%x"
111 : "%d",
112 exc->exc_argv[i].v_int);
113 continue;
114 case 'l':
115 sm_io_fprintf(stream,
116 SM_TIME_DEFAULT,
117 format == 'o' ? "%lo"
118 : format == 'x' ? "%lx"
119 : "%ld",
120 exc->exc_argv[i].v_long);
121 continue;
122 case 'e':
123 sm_exc_write(exc->exc_argv[i].v_exc,
124 stream);
125 continue;
129 (void) sm_io_putc(stream, SM_TIME_DEFAULT, '%');
130 if (format)
131 (void) sm_io_putc(stream, SM_TIME_DEFAULT, format);
132 (void) sm_io_putc(stream, SM_TIME_DEFAULT, *p);
137 ** Standard exception types.
141 ** SM_ETYPE_OS_PRINT -- Print OS related exception.
143 ** Parameters:
144 ** exc -- exception.
145 ** stream -- file for output.
147 ** Returns:
148 ** none.
151 static void
152 sm_etype_os_print __P((
153 SM_EXC_T *exc,
154 SM_FILE_T *stream));
156 static void
157 sm_etype_os_print(exc, stream)
158 SM_EXC_T *exc;
159 SM_FILE_T *stream;
161 int err = exc->exc_argv[0].v_int;
162 char *syscall = exc->exc_argv[1].v_str;
163 char *sysargs = exc->exc_argv[2].v_str;
165 if (sysargs)
166 sm_io_fprintf(stream, SM_TIME_DEFAULT, "%s: %s failed: %s",
167 sysargs, syscall, sm_errstring(err));
168 else
169 sm_io_fprintf(stream, SM_TIME_DEFAULT, "%s failed: %s", syscall,
170 sm_errstring(err));
174 ** SmEtypeOs represents the failure of a Unix system call.
175 ** The three arguments are:
176 ** int errno (eg, ENOENT)
177 ** char *syscall (eg, "open")
178 ** char *sysargs (eg, NULL or "/etc/mail/sendmail.cf")
181 const SM_EXC_TYPE_T SmEtypeOs =
183 SmExcTypeMagic,
184 "E:sm.os",
185 "isr",
186 sm_etype_os_print,
187 NULL,
191 ** SmEtypeErr is a completely generic error which should only be
192 ** used in applications and test programs. Libraries should use
193 ** more specific exception codes.
196 const SM_EXC_TYPE_T SmEtypeErr =
198 SmExcTypeMagic,
199 "E:sm.err",
200 "r",
201 sm_etype_printf,
202 "%0",
206 ** SM_EXC_VNEW_X -- Construct a new exception object.
208 ** Parameters:
209 ** etype -- type of exception.
210 ** ap -- varargs.
212 ** Returns:
213 ** pointer to exception object.
217 ** This is an auxiliary function called by sm_exc_new_x and sm_exc_raisenew_x.
219 ** If an exception is raised, then to avoid a storage leak, we must:
220 ** (a) Free all storage we have allocated.
221 ** (b) Free all exception arguments in the varargs list.
222 ** Getting this right is tricky.
224 ** To see why (b) is required, consider the code fragment
225 ** SM_EXCEPT(exc, "*")
226 ** sm_exc_raisenew_x(&MyEtype, exc);
227 ** SM_END_TRY
228 ** In the normal case, sm_exc_raisenew_x will allocate and raise a new
229 ** exception E that owns exc. When E is eventually freed, exc is also freed.
230 ** In the exceptional case, sm_exc_raisenew_x must free exc before raising
231 ** an out-of-memory exception so that exc is not leaked.
234 static SM_EXC_T *sm_exc_vnew_x __P((const SM_EXC_TYPE_T *, va_list SM_NONVOLATILE));
236 static SM_EXC_T *
237 sm_exc_vnew_x(etype, ap)
238 const SM_EXC_TYPE_T *etype;
239 va_list SM_NONVOLATILE ap;
242 ** All variables that are modified in the SM_TRY clause and
243 ** referenced in the SM_EXCEPT clause must be declared volatile.
246 /* NOTE: Type of si, i, and argc *must* match */
247 SM_EXC_T * volatile exc = NULL;
248 int volatile si = 0;
249 SM_VAL_T * volatile argv = NULL;
250 int i, argc;
252 SM_REQUIRE_ISA(etype, SmExcTypeMagic);
253 argc = strlen(etype->etype_argformat);
254 SM_TRY
257 ** Step 1. Allocate the exception structure.
258 ** On failure, scan the varargs list and free all
259 ** exception arguments.
262 exc = sm_malloc_x(sizeof(SM_EXC_T));
263 exc->sm_magic = SmExcMagic;
264 exc->exc_refcount = 1;
265 exc->exc_type = etype;
266 exc->exc_argv = NULL;
269 ** Step 2. Allocate the argument vector.
270 ** On failure, free exc, scan the varargs list and free all
271 ** exception arguments. On success, scan the varargs list,
272 ** and copy the arguments into argv.
275 argv = sm_malloc_x(argc * sizeof(SM_VAL_T));
276 exc->exc_argv = argv;
277 for (i = 0; i < argc; ++i)
279 switch (etype->etype_argformat[i])
281 case 'i':
282 argv[i].v_int = SM_VA_ARG(ap, int);
283 break;
284 case 'l':
285 argv[i].v_long = SM_VA_ARG(ap, long);
286 break;
287 case 'e':
288 argv[i].v_exc = SM_VA_ARG(ap, SM_EXC_T*);
289 break;
290 case 's':
291 argv[i].v_str = SM_VA_ARG(ap, char*);
292 break;
293 case 'r':
294 SM_REQUIRE(etype->etype_argformat[i+1] == '\0');
295 argv[i].v_str = SM_VA_ARG(ap, char*);
296 break;
297 default:
298 sm_abort("sm_exc_vnew_x: bad argformat '%c'",
299 etype->etype_argformat[i]);
304 ** Step 3. Scan argv, and allocate space for all
305 ** string arguments. si is the number of elements
306 ** of argv that have been processed so far.
307 ** On failure, free exc, argv, all the exception arguments
308 ** and all of the strings that have been copied.
311 for (si = 0; si < argc; ++si)
313 switch (etype->etype_argformat[si])
315 case 's':
317 char *str = argv[si].v_str;
318 if (str != NULL)
319 argv[si].v_str = sm_strdup_x(str);
321 break;
322 case 'r':
324 char *fmt = argv[si].v_str;
325 if (fmt != NULL)
326 argv[si].v_str = sm_vstringf_x(fmt, ap);
328 break;
332 SM_EXCEPT(e, "*")
334 if (exc == NULL || argv == NULL)
337 ** Failure in step 1 or step 2.
338 ** Scan ap and free all exception arguments.
341 for (i = 0; i < argc; ++i)
343 switch (etype->etype_argformat[i])
345 case 'i':
346 (void) SM_VA_ARG(ap, int);
347 break;
348 case 'l':
349 (void) SM_VA_ARG(ap, long);
350 break;
351 case 'e':
352 sm_exc_free(SM_VA_ARG(ap, SM_EXC_T*));
353 break;
354 case 's':
355 case 'r':
356 (void) SM_VA_ARG(ap, char*);
357 break;
361 else
364 ** Failure in step 3. Scan argv and free
365 ** all exception arguments and all string
366 ** arguments that have been duplicated.
367 ** Then free argv.
370 for (i = 0; i < argc; ++i)
372 switch (etype->etype_argformat[i])
374 case 'e':
375 sm_exc_free(argv[i].v_exc);
376 break;
377 case 's':
378 case 'r':
379 if (i < si)
380 sm_free(argv[i].v_str);
381 break;
384 sm_free(argv);
386 sm_free(exc);
387 sm_exc_raise_x(e);
389 SM_END_TRY
391 return exc;
395 ** SM_EXC_NEW_X -- Construct a new exception object.
397 ** Parameters:
398 ** etype -- type of exception.
399 ** ... -- varargs.
401 ** Returns:
402 ** pointer to exception object.
405 SM_EXC_T *
406 #if SM_VA_STD
407 sm_exc_new_x(
408 const SM_EXC_TYPE_T *etype,
409 ...)
410 #else /* SM_VA_STD */
411 sm_exc_new_x(etype, va_alist)
412 const SM_EXC_TYPE_T *etype;
413 va_dcl
414 #endif /* SM_VA_STD */
416 SM_EXC_T *exc;
417 SM_VA_LOCAL_DECL
419 SM_VA_START(ap, etype);
420 exc = sm_exc_vnew_x(etype, ap);
421 SM_VA_END(ap);
422 return exc;
426 ** SM_EXC_FREE -- Destroy a reference to an exception object.
428 ** Parameters:
429 ** exc -- exception object.
431 ** Returns:
432 ** none.
435 void
436 sm_exc_free(exc)
437 SM_EXC_T *exc;
439 if (exc == NULL)
440 return;
441 SM_REQUIRE(exc->sm_magic == SmExcMagic);
442 if (exc->exc_refcount == 0)
443 return;
444 if (--exc->exc_refcount == 0)
446 int i, c;
448 for (i = 0; (c = exc->exc_type->etype_argformat[i]) != '\0';
449 ++i)
451 switch (c)
453 case 's':
454 case 'r':
455 sm_free(exc->exc_argv[i].v_str);
456 break;
457 case 'e':
458 sm_exc_free(exc->exc_argv[i].v_exc);
459 break;
462 exc->sm_magic = NULL;
463 sm_free(exc->exc_argv);
464 sm_free(exc);
469 ** SM_EXC_MATCH -- Match exception category against a glob pattern.
471 ** Parameters:
472 ** exc -- exception.
473 ** pattern -- glob pattern.
475 ** Returns:
476 ** true iff match.
479 bool
480 sm_exc_match(exc, pattern)
481 SM_EXC_T *exc;
482 const char *pattern;
484 if (exc == NULL)
485 return false;
486 SM_REQUIRE(exc->sm_magic == SmExcMagic);
487 return sm_match(exc->exc_type->etype_category, pattern);
491 ** SM_EXC_WRITE -- Write exception message to a stream (wo trailing newline).
493 ** Parameters:
494 ** exc -- exception.
495 ** stream -- file for output.
497 ** Returns:
498 ** none.
501 void
502 sm_exc_write(exc, stream)
503 SM_EXC_T *exc;
504 SM_FILE_T *stream;
506 SM_REQUIRE_ISA(exc, SmExcMagic);
507 exc->exc_type->etype_print(exc, stream);
511 ** SM_EXC_PRINT -- Print exception message to a stream (with trailing newline).
513 ** Parameters:
514 ** exc -- exception.
515 ** stream -- file for output.
517 ** Returns:
518 ** none.
521 void
522 sm_exc_print(exc, stream)
523 SM_EXC_T *exc;
524 SM_FILE_T *stream;
526 SM_REQUIRE_ISA(exc, SmExcMagic);
527 exc->exc_type->etype_print(exc, stream);
528 (void) sm_io_putc(stream, SM_TIME_DEFAULT, '\n');
531 SM_EXC_HANDLER_T *SmExcHandler = NULL;
532 static SM_EXC_DEFAULT_HANDLER_T SmExcDefaultHandler = NULL;
535 ** SM_EXC_NEWTHREAD -- Initialize exception handling for new process/thread.
537 ** Parameters:
538 ** h -- default exception handler.
540 ** Returns:
541 ** none.
545 ** Initialize a new process or a new thread by clearing the
546 ** exception handler stack and optionally setting a default
547 ** exception handler function. Call this at the beginning of main,
548 ** or in a new process after calling fork, or in a new thread.
550 ** This function is a luxury, not a necessity.
551 ** If h != NULL then you can get the same effect by
552 ** wrapping the body of main, or the body of a forked child
553 ** or a new thread in SM_TRY ... SM_EXCEPT(e,"*") h(e); SM_END_TRY.
556 void
557 sm_exc_newthread(h)
558 SM_EXC_DEFAULT_HANDLER_T h;
560 SmExcHandler = NULL;
561 SmExcDefaultHandler = h;
565 ** SM_EXC_RAISE_X -- Raise an exception.
567 ** Parameters:
568 ** exc -- exception.
570 ** Returns:
571 ** doesn't.
574 void SM_DEAD_D
575 sm_exc_raise_x(exc)
576 SM_EXC_T *exc;
578 SM_REQUIRE_ISA(exc, SmExcMagic);
580 if (SmExcHandler == NULL)
582 if (SmExcDefaultHandler != NULL)
584 SM_EXC_DEFAULT_HANDLER_T h;
587 ** If defined, the default handler is expected
588 ** to terminate the current thread of execution
589 ** using exit() or pthread_exit().
590 ** If it instead returns normally, then we fall
591 ** through to the default case below. If it
592 ** raises an exception, then sm_exc_raise_x is
593 ** re-entered and, because we set SmExcDefaultHandler
594 ** to NULL before invoking h, we will again
595 ** end up in the default case below.
598 h = SmExcDefaultHandler;
599 SmExcDefaultHandler = NULL;
600 (*h)(exc);
604 ** No exception handler, so print the error and exit.
605 ** To override this behaviour on a program wide basis,
606 ** call sm_exc_newthread or put an exception handler in main().
608 ** XXX TODO: map the exception category to an exit code
609 ** XXX from <sysexits.h>.
612 sm_exc_print(exc, smioerr);
613 exit(255);
616 if (SmExcHandler->eh_value == NULL)
617 SmExcHandler->eh_value = exc;
618 else
619 sm_exc_free(exc);
621 sm_longjmp_nosig(SmExcHandler->eh_context, 1);
625 ** SM_EXC_RAISENEW_X -- shorthand for sm_exc_raise_x(sm_exc_new_x(...))
627 ** Parameters:
628 ** etype -- type of exception.
629 ** ap -- varargs.
631 ** Returns:
632 ** none.
635 void SM_DEAD_D
636 #if SM_VA_STD
637 sm_exc_raisenew_x(
638 const SM_EXC_TYPE_T *etype,
639 ...)
640 #else
641 sm_exc_raisenew_x(etype, va_alist)
642 const SM_EXC_TYPE_T *etype;
643 va_dcl
644 #endif
646 SM_EXC_T *exc;
647 SM_VA_LOCAL_DECL
649 SM_VA_START(ap, etype);
650 exc = sm_exc_vnew_x(etype, ap);
651 SM_VA_END(ap);
652 sm_exc_raise_x(exc);