17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * Copyright (c) 2000-2002 Sendmail, Inc. and its suppliers. 37c478bd9Sstevel@tonic-gate * All rights reserved. 47c478bd9Sstevel@tonic-gate * 57c478bd9Sstevel@tonic-gate * By using this file, you agree to the terms and conditions set 67c478bd9Sstevel@tonic-gate * forth in the LICENSE file which can be found at the top level of 77c478bd9Sstevel@tonic-gate * the sendmail distribution. 87c478bd9Sstevel@tonic-gate * 97c478bd9Sstevel@tonic-gate */ 107c478bd9Sstevel@tonic-gate 117c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 127c478bd9Sstevel@tonic-gate 137c478bd9Sstevel@tonic-gate #include <sm/gen.h> 14*058561cbSjbeck SM_RCSID("@(#)$Id: exc.c,v 1.49 2006/12/19 19:28:09 ca Exp $") 157c478bd9Sstevel@tonic-gate 167c478bd9Sstevel@tonic-gate /* 177c478bd9Sstevel@tonic-gate ** exception handling 187c478bd9Sstevel@tonic-gate ** For documentation, see exc.html 197c478bd9Sstevel@tonic-gate */ 207c478bd9Sstevel@tonic-gate 217c478bd9Sstevel@tonic-gate #include <ctype.h> 227c478bd9Sstevel@tonic-gate #include <string.h> 237c478bd9Sstevel@tonic-gate 247c478bd9Sstevel@tonic-gate #include <sm/errstring.h> 257c478bd9Sstevel@tonic-gate #include <sm/exc.h> 267c478bd9Sstevel@tonic-gate #include <sm/heap.h> 277c478bd9Sstevel@tonic-gate #include <sm/string.h> 287c478bd9Sstevel@tonic-gate #include <sm/varargs.h> 297c478bd9Sstevel@tonic-gate #include <sm/io.h> 307c478bd9Sstevel@tonic-gate 317c478bd9Sstevel@tonic-gate const char SmExcMagic[] = "sm_exc"; 327c478bd9Sstevel@tonic-gate const char SmExcTypeMagic[] = "sm_exc_type"; 337c478bd9Sstevel@tonic-gate 347c478bd9Sstevel@tonic-gate /* 357c478bd9Sstevel@tonic-gate ** SM_ETYPE_PRINTF -- printf for exception types. 367c478bd9Sstevel@tonic-gate ** 377c478bd9Sstevel@tonic-gate ** Parameters: 387c478bd9Sstevel@tonic-gate ** exc -- exception. 397c478bd9Sstevel@tonic-gate ** stream -- file for output. 407c478bd9Sstevel@tonic-gate ** 417c478bd9Sstevel@tonic-gate ** Returns: 427c478bd9Sstevel@tonic-gate ** none. 437c478bd9Sstevel@tonic-gate */ 447c478bd9Sstevel@tonic-gate 457c478bd9Sstevel@tonic-gate /* 467c478bd9Sstevel@tonic-gate ** A simple formatted print function that can be used as the print function 477c478bd9Sstevel@tonic-gate ** by most exception types. It prints the printcontext string, interpreting 487c478bd9Sstevel@tonic-gate ** occurrences of %0 through %9 as references to the argument vector. 497c478bd9Sstevel@tonic-gate ** If exception argument 3 is an int or long, then %3 will print the 507c478bd9Sstevel@tonic-gate ** argument in decimal, and %o3 or %x3 will print it in octal or hex. 517c478bd9Sstevel@tonic-gate */ 527c478bd9Sstevel@tonic-gate 537c478bd9Sstevel@tonic-gate void 547c478bd9Sstevel@tonic-gate sm_etype_printf(exc, stream) 557c478bd9Sstevel@tonic-gate SM_EXC_T *exc; 567c478bd9Sstevel@tonic-gate SM_FILE_T *stream; 577c478bd9Sstevel@tonic-gate { 587c478bd9Sstevel@tonic-gate size_t n = strlen(exc->exc_type->etype_argformat); 597c478bd9Sstevel@tonic-gate const char *p, *s; 607c478bd9Sstevel@tonic-gate char format; 617c478bd9Sstevel@tonic-gate 627c478bd9Sstevel@tonic-gate for (p = exc->exc_type->etype_printcontext; *p != '\0'; ++p) 637c478bd9Sstevel@tonic-gate { 647c478bd9Sstevel@tonic-gate if (*p != '%') 657c478bd9Sstevel@tonic-gate { 667c478bd9Sstevel@tonic-gate (void) sm_io_putc(stream, SM_TIME_DEFAULT, *p); 677c478bd9Sstevel@tonic-gate continue; 687c478bd9Sstevel@tonic-gate } 697c478bd9Sstevel@tonic-gate ++p; 707c478bd9Sstevel@tonic-gate if (*p == '\0') 717c478bd9Sstevel@tonic-gate { 727c478bd9Sstevel@tonic-gate (void) sm_io_putc(stream, SM_TIME_DEFAULT, '%'); 737c478bd9Sstevel@tonic-gate break; 747c478bd9Sstevel@tonic-gate } 757c478bd9Sstevel@tonic-gate if (*p == '%') 767c478bd9Sstevel@tonic-gate { 777c478bd9Sstevel@tonic-gate (void) sm_io_putc(stream, SM_TIME_DEFAULT, '%'); 787c478bd9Sstevel@tonic-gate continue; 797c478bd9Sstevel@tonic-gate } 807c478bd9Sstevel@tonic-gate format = '\0'; 817c478bd9Sstevel@tonic-gate if (isalpha(*p)) 827c478bd9Sstevel@tonic-gate { 837c478bd9Sstevel@tonic-gate format = *p++; 847c478bd9Sstevel@tonic-gate if (*p == '\0') 857c478bd9Sstevel@tonic-gate { 867c478bd9Sstevel@tonic-gate (void) sm_io_putc(stream, SM_TIME_DEFAULT, '%'); 877c478bd9Sstevel@tonic-gate (void) sm_io_putc(stream, SM_TIME_DEFAULT, 887c478bd9Sstevel@tonic-gate format); 897c478bd9Sstevel@tonic-gate break; 907c478bd9Sstevel@tonic-gate } 917c478bd9Sstevel@tonic-gate } 927c478bd9Sstevel@tonic-gate if (isdigit(*p)) 937c478bd9Sstevel@tonic-gate { 947c478bd9Sstevel@tonic-gate size_t i = *p - '0'; 957c478bd9Sstevel@tonic-gate if (i < n) 967c478bd9Sstevel@tonic-gate { 977c478bd9Sstevel@tonic-gate switch (exc->exc_type->etype_argformat[i]) 987c478bd9Sstevel@tonic-gate { 997c478bd9Sstevel@tonic-gate case 's': 1007c478bd9Sstevel@tonic-gate case 'r': 1017c478bd9Sstevel@tonic-gate s = exc->exc_argv[i].v_str; 1027c478bd9Sstevel@tonic-gate if (s == NULL) 1037c478bd9Sstevel@tonic-gate s = "(null)"; 1047c478bd9Sstevel@tonic-gate sm_io_fputs(stream, SM_TIME_DEFAULT, s); 1057c478bd9Sstevel@tonic-gate continue; 1067c478bd9Sstevel@tonic-gate case 'i': 1077c478bd9Sstevel@tonic-gate sm_io_fprintf(stream, 1087c478bd9Sstevel@tonic-gate SM_TIME_DEFAULT, 1097c478bd9Sstevel@tonic-gate format == 'o' ? "%o" 1107c478bd9Sstevel@tonic-gate : format == 'x' ? "%x" 1117c478bd9Sstevel@tonic-gate : "%d", 1127c478bd9Sstevel@tonic-gate exc->exc_argv[i].v_int); 1137c478bd9Sstevel@tonic-gate continue; 1147c478bd9Sstevel@tonic-gate case 'l': 1157c478bd9Sstevel@tonic-gate sm_io_fprintf(stream, 1167c478bd9Sstevel@tonic-gate SM_TIME_DEFAULT, 1177c478bd9Sstevel@tonic-gate format == 'o' ? "%lo" 1187c478bd9Sstevel@tonic-gate : format == 'x' ? "%lx" 1197c478bd9Sstevel@tonic-gate : "%ld", 1207c478bd9Sstevel@tonic-gate exc->exc_argv[i].v_long); 1217c478bd9Sstevel@tonic-gate continue; 1227c478bd9Sstevel@tonic-gate case 'e': 1237c478bd9Sstevel@tonic-gate sm_exc_write(exc->exc_argv[i].v_exc, 1247c478bd9Sstevel@tonic-gate stream); 1257c478bd9Sstevel@tonic-gate continue; 1267c478bd9Sstevel@tonic-gate } 1277c478bd9Sstevel@tonic-gate } 1287c478bd9Sstevel@tonic-gate } 1297c478bd9Sstevel@tonic-gate (void) sm_io_putc(stream, SM_TIME_DEFAULT, '%'); 1307c478bd9Sstevel@tonic-gate if (format) 1317c478bd9Sstevel@tonic-gate (void) sm_io_putc(stream, SM_TIME_DEFAULT, format); 1327c478bd9Sstevel@tonic-gate (void) sm_io_putc(stream, SM_TIME_DEFAULT, *p); 1337c478bd9Sstevel@tonic-gate } 1347c478bd9Sstevel@tonic-gate } 1357c478bd9Sstevel@tonic-gate 1367c478bd9Sstevel@tonic-gate /* 1377c478bd9Sstevel@tonic-gate ** Standard exception types. 1387c478bd9Sstevel@tonic-gate */ 1397c478bd9Sstevel@tonic-gate 1407c478bd9Sstevel@tonic-gate /* 1417c478bd9Sstevel@tonic-gate ** SM_ETYPE_OS_PRINT -- Print OS related exception. 1427c478bd9Sstevel@tonic-gate ** 1437c478bd9Sstevel@tonic-gate ** Parameters: 1447c478bd9Sstevel@tonic-gate ** exc -- exception. 1457c478bd9Sstevel@tonic-gate ** stream -- file for output. 1467c478bd9Sstevel@tonic-gate ** 1477c478bd9Sstevel@tonic-gate ** Returns: 1487c478bd9Sstevel@tonic-gate ** none. 1497c478bd9Sstevel@tonic-gate */ 1507c478bd9Sstevel@tonic-gate 1517c478bd9Sstevel@tonic-gate static void 1527c478bd9Sstevel@tonic-gate sm_etype_os_print __P(( 1537c478bd9Sstevel@tonic-gate SM_EXC_T *exc, 1547c478bd9Sstevel@tonic-gate SM_FILE_T *stream)); 1557c478bd9Sstevel@tonic-gate 1567c478bd9Sstevel@tonic-gate static void 1577c478bd9Sstevel@tonic-gate sm_etype_os_print(exc, stream) 1587c478bd9Sstevel@tonic-gate SM_EXC_T *exc; 1597c478bd9Sstevel@tonic-gate SM_FILE_T *stream; 1607c478bd9Sstevel@tonic-gate { 1617c478bd9Sstevel@tonic-gate int err = exc->exc_argv[0].v_int; 1627c478bd9Sstevel@tonic-gate char *syscall = exc->exc_argv[1].v_str; 1637c478bd9Sstevel@tonic-gate char *sysargs = exc->exc_argv[2].v_str; 1647c478bd9Sstevel@tonic-gate 1657c478bd9Sstevel@tonic-gate if (sysargs) 1667c478bd9Sstevel@tonic-gate sm_io_fprintf(stream, SM_TIME_DEFAULT, "%s: %s failed: %s", 1677c478bd9Sstevel@tonic-gate sysargs, syscall, sm_errstring(err)); 1687c478bd9Sstevel@tonic-gate else 1697c478bd9Sstevel@tonic-gate sm_io_fprintf(stream, SM_TIME_DEFAULT, "%s failed: %s", syscall, 1707c478bd9Sstevel@tonic-gate sm_errstring(err)); 1717c478bd9Sstevel@tonic-gate } 1727c478bd9Sstevel@tonic-gate 1737c478bd9Sstevel@tonic-gate /* 1747c478bd9Sstevel@tonic-gate ** SmEtypeOs represents the failure of a Unix system call. 1757c478bd9Sstevel@tonic-gate ** The three arguments are: 1767c478bd9Sstevel@tonic-gate ** int errno (eg, ENOENT) 1777c478bd9Sstevel@tonic-gate ** char *syscall (eg, "open") 1787c478bd9Sstevel@tonic-gate ** char *sysargs (eg, NULL or "/etc/mail/sendmail.cf") 1797c478bd9Sstevel@tonic-gate */ 1807c478bd9Sstevel@tonic-gate 1817c478bd9Sstevel@tonic-gate const SM_EXC_TYPE_T SmEtypeOs = 1827c478bd9Sstevel@tonic-gate { 1837c478bd9Sstevel@tonic-gate SmExcTypeMagic, 1847c478bd9Sstevel@tonic-gate "E:sm.os", 1857c478bd9Sstevel@tonic-gate "isr", 1867c478bd9Sstevel@tonic-gate sm_etype_os_print, 1877c478bd9Sstevel@tonic-gate NULL, 1887c478bd9Sstevel@tonic-gate }; 1897c478bd9Sstevel@tonic-gate 1907c478bd9Sstevel@tonic-gate /* 1917c478bd9Sstevel@tonic-gate ** SmEtypeErr is a completely generic error which should only be 1927c478bd9Sstevel@tonic-gate ** used in applications and test programs. Libraries should use 1937c478bd9Sstevel@tonic-gate ** more specific exception codes. 1947c478bd9Sstevel@tonic-gate */ 1957c478bd9Sstevel@tonic-gate 1967c478bd9Sstevel@tonic-gate const SM_EXC_TYPE_T SmEtypeErr = 1977c478bd9Sstevel@tonic-gate { 1987c478bd9Sstevel@tonic-gate SmExcTypeMagic, 1997c478bd9Sstevel@tonic-gate "E:sm.err", 2007c478bd9Sstevel@tonic-gate "r", 2017c478bd9Sstevel@tonic-gate sm_etype_printf, 2027c478bd9Sstevel@tonic-gate "%0", 2037c478bd9Sstevel@tonic-gate }; 2047c478bd9Sstevel@tonic-gate 2057c478bd9Sstevel@tonic-gate /* 2067c478bd9Sstevel@tonic-gate ** SM_EXC_VNEW_X -- Construct a new exception object. 2077c478bd9Sstevel@tonic-gate ** 2087c478bd9Sstevel@tonic-gate ** Parameters: 2097c478bd9Sstevel@tonic-gate ** etype -- type of exception. 2107c478bd9Sstevel@tonic-gate ** ap -- varargs. 2117c478bd9Sstevel@tonic-gate ** 2127c478bd9Sstevel@tonic-gate ** Returns: 2137c478bd9Sstevel@tonic-gate ** pointer to exception object. 2147c478bd9Sstevel@tonic-gate */ 2157c478bd9Sstevel@tonic-gate 2167c478bd9Sstevel@tonic-gate /* 2177c478bd9Sstevel@tonic-gate ** This is an auxiliary function called by sm_exc_new_x and sm_exc_raisenew_x. 2187c478bd9Sstevel@tonic-gate ** 2197c478bd9Sstevel@tonic-gate ** If an exception is raised, then to avoid a storage leak, we must: 2207c478bd9Sstevel@tonic-gate ** (a) Free all storage we have allocated. 2217c478bd9Sstevel@tonic-gate ** (b) Free all exception arguments in the varargs list. 2227c478bd9Sstevel@tonic-gate ** Getting this right is tricky. 2237c478bd9Sstevel@tonic-gate ** 2247c478bd9Sstevel@tonic-gate ** To see why (b) is required, consider the code fragment 2257c478bd9Sstevel@tonic-gate ** SM_EXCEPT(exc, "*") 2267c478bd9Sstevel@tonic-gate ** sm_exc_raisenew_x(&MyEtype, exc); 2277c478bd9Sstevel@tonic-gate ** SM_END_TRY 2287c478bd9Sstevel@tonic-gate ** In the normal case, sm_exc_raisenew_x will allocate and raise a new 2297c478bd9Sstevel@tonic-gate ** exception E that owns exc. When E is eventually freed, exc is also freed. 2307c478bd9Sstevel@tonic-gate ** In the exceptional case, sm_exc_raisenew_x must free exc before raising 2317c478bd9Sstevel@tonic-gate ** an out-of-memory exception so that exc is not leaked. 2327c478bd9Sstevel@tonic-gate */ 2337c478bd9Sstevel@tonic-gate 234*058561cbSjbeck static SM_EXC_T *sm_exc_vnew_x __P((const SM_EXC_TYPE_T *, va_list SM_NONVOLATILE)); 235*058561cbSjbeck 236*058561cbSjbeck static SM_EXC_T * 2377c478bd9Sstevel@tonic-gate sm_exc_vnew_x(etype, ap) 2387c478bd9Sstevel@tonic-gate const SM_EXC_TYPE_T *etype; 2397c478bd9Sstevel@tonic-gate va_list SM_NONVOLATILE ap; 2407c478bd9Sstevel@tonic-gate { 2417c478bd9Sstevel@tonic-gate /* 2427c478bd9Sstevel@tonic-gate ** All variables that are modified in the SM_TRY clause and 2437c478bd9Sstevel@tonic-gate ** referenced in the SM_EXCEPT clause must be declared volatile. 2447c478bd9Sstevel@tonic-gate */ 2457c478bd9Sstevel@tonic-gate 2467c478bd9Sstevel@tonic-gate /* NOTE: Type of si, i, and argc *must* match */ 2477c478bd9Sstevel@tonic-gate SM_EXC_T * volatile exc = NULL; 2487c478bd9Sstevel@tonic-gate int volatile si = 0; 2497c478bd9Sstevel@tonic-gate SM_VAL_T * volatile argv = NULL; 2507c478bd9Sstevel@tonic-gate int i, argc; 2517c478bd9Sstevel@tonic-gate 2527c478bd9Sstevel@tonic-gate SM_REQUIRE_ISA(etype, SmExcTypeMagic); 2537c478bd9Sstevel@tonic-gate argc = strlen(etype->etype_argformat); 2547c478bd9Sstevel@tonic-gate SM_TRY 2557c478bd9Sstevel@tonic-gate { 2567c478bd9Sstevel@tonic-gate /* 2577c478bd9Sstevel@tonic-gate ** Step 1. Allocate the exception structure. 2587c478bd9Sstevel@tonic-gate ** On failure, scan the varargs list and free all 2597c478bd9Sstevel@tonic-gate ** exception arguments. 2607c478bd9Sstevel@tonic-gate */ 2617c478bd9Sstevel@tonic-gate 2627c478bd9Sstevel@tonic-gate exc = sm_malloc_x(sizeof(SM_EXC_T)); 2637c478bd9Sstevel@tonic-gate exc->sm_magic = SmExcMagic; 2647c478bd9Sstevel@tonic-gate exc->exc_refcount = 1; 2657c478bd9Sstevel@tonic-gate exc->exc_type = etype; 2667c478bd9Sstevel@tonic-gate exc->exc_argv = NULL; 2677c478bd9Sstevel@tonic-gate 2687c478bd9Sstevel@tonic-gate /* 2697c478bd9Sstevel@tonic-gate ** Step 2. Allocate the argument vector. 2707c478bd9Sstevel@tonic-gate ** On failure, free exc, scan the varargs list and free all 2717c478bd9Sstevel@tonic-gate ** exception arguments. On success, scan the varargs list, 2727c478bd9Sstevel@tonic-gate ** and copy the arguments into argv. 2737c478bd9Sstevel@tonic-gate */ 2747c478bd9Sstevel@tonic-gate 2757c478bd9Sstevel@tonic-gate argv = sm_malloc_x(argc * sizeof(SM_VAL_T)); 2767c478bd9Sstevel@tonic-gate exc->exc_argv = argv; 2777c478bd9Sstevel@tonic-gate for (i = 0; i < argc; ++i) 2787c478bd9Sstevel@tonic-gate { 2797c478bd9Sstevel@tonic-gate switch (etype->etype_argformat[i]) 2807c478bd9Sstevel@tonic-gate { 2817c478bd9Sstevel@tonic-gate case 'i': 2827c478bd9Sstevel@tonic-gate argv[i].v_int = SM_VA_ARG(ap, int); 2837c478bd9Sstevel@tonic-gate break; 2847c478bd9Sstevel@tonic-gate case 'l': 2857c478bd9Sstevel@tonic-gate argv[i].v_long = SM_VA_ARG(ap, long); 2867c478bd9Sstevel@tonic-gate break; 2877c478bd9Sstevel@tonic-gate case 'e': 2887c478bd9Sstevel@tonic-gate argv[i].v_exc = SM_VA_ARG(ap, SM_EXC_T*); 2897c478bd9Sstevel@tonic-gate break; 2907c478bd9Sstevel@tonic-gate case 's': 2917c478bd9Sstevel@tonic-gate argv[i].v_str = SM_VA_ARG(ap, char*); 2927c478bd9Sstevel@tonic-gate break; 2937c478bd9Sstevel@tonic-gate case 'r': 2947c478bd9Sstevel@tonic-gate SM_REQUIRE(etype->etype_argformat[i+1] == '\0'); 2957c478bd9Sstevel@tonic-gate argv[i].v_str = SM_VA_ARG(ap, char*); 2967c478bd9Sstevel@tonic-gate break; 2977c478bd9Sstevel@tonic-gate default: 2987c478bd9Sstevel@tonic-gate sm_abort("sm_exc_vnew_x: bad argformat '%c'", 2997c478bd9Sstevel@tonic-gate etype->etype_argformat[i]); 3007c478bd9Sstevel@tonic-gate } 3017c478bd9Sstevel@tonic-gate } 3027c478bd9Sstevel@tonic-gate 3037c478bd9Sstevel@tonic-gate /* 3047c478bd9Sstevel@tonic-gate ** Step 3. Scan argv, and allocate space for all 3057c478bd9Sstevel@tonic-gate ** string arguments. si is the number of elements 3067c478bd9Sstevel@tonic-gate ** of argv that have been processed so far. 3077c478bd9Sstevel@tonic-gate ** On failure, free exc, argv, all the exception arguments 3087c478bd9Sstevel@tonic-gate ** and all of the strings that have been copied. 3097c478bd9Sstevel@tonic-gate */ 3107c478bd9Sstevel@tonic-gate 3117c478bd9Sstevel@tonic-gate for (si = 0; si < argc; ++si) 3127c478bd9Sstevel@tonic-gate { 3137c478bd9Sstevel@tonic-gate switch (etype->etype_argformat[si]) 3147c478bd9Sstevel@tonic-gate { 3157c478bd9Sstevel@tonic-gate case 's': 3167c478bd9Sstevel@tonic-gate { 3177c478bd9Sstevel@tonic-gate char *str = argv[si].v_str; 3187c478bd9Sstevel@tonic-gate if (str != NULL) 3197c478bd9Sstevel@tonic-gate argv[si].v_str = sm_strdup_x(str); 3207c478bd9Sstevel@tonic-gate } 3217c478bd9Sstevel@tonic-gate break; 3227c478bd9Sstevel@tonic-gate case 'r': 3237c478bd9Sstevel@tonic-gate { 3247c478bd9Sstevel@tonic-gate char *fmt = argv[si].v_str; 3257c478bd9Sstevel@tonic-gate if (fmt != NULL) 3267c478bd9Sstevel@tonic-gate argv[si].v_str = sm_vstringf_x(fmt, ap); 3277c478bd9Sstevel@tonic-gate } 3287c478bd9Sstevel@tonic-gate break; 3297c478bd9Sstevel@tonic-gate } 3307c478bd9Sstevel@tonic-gate } 3317c478bd9Sstevel@tonic-gate } 3327c478bd9Sstevel@tonic-gate SM_EXCEPT(e, "*") 3337c478bd9Sstevel@tonic-gate { 3347c478bd9Sstevel@tonic-gate if (exc == NULL || argv == NULL) 3357c478bd9Sstevel@tonic-gate { 3367c478bd9Sstevel@tonic-gate /* 3377c478bd9Sstevel@tonic-gate ** Failure in step 1 or step 2. 3387c478bd9Sstevel@tonic-gate ** Scan ap and free all exception arguments. 3397c478bd9Sstevel@tonic-gate */ 3407c478bd9Sstevel@tonic-gate 3417c478bd9Sstevel@tonic-gate for (i = 0; i < argc; ++i) 3427c478bd9Sstevel@tonic-gate { 3437c478bd9Sstevel@tonic-gate switch (etype->etype_argformat[i]) 3447c478bd9Sstevel@tonic-gate { 3457c478bd9Sstevel@tonic-gate case 'i': 3467c478bd9Sstevel@tonic-gate (void) SM_VA_ARG(ap, int); 3477c478bd9Sstevel@tonic-gate break; 3487c478bd9Sstevel@tonic-gate case 'l': 3497c478bd9Sstevel@tonic-gate (void) SM_VA_ARG(ap, long); 3507c478bd9Sstevel@tonic-gate break; 3517c478bd9Sstevel@tonic-gate case 'e': 3527c478bd9Sstevel@tonic-gate sm_exc_free(SM_VA_ARG(ap, SM_EXC_T*)); 3537c478bd9Sstevel@tonic-gate break; 3547c478bd9Sstevel@tonic-gate case 's': 3557c478bd9Sstevel@tonic-gate case 'r': 3567c478bd9Sstevel@tonic-gate (void) SM_VA_ARG(ap, char*); 3577c478bd9Sstevel@tonic-gate break; 3587c478bd9Sstevel@tonic-gate } 3597c478bd9Sstevel@tonic-gate } 3607c478bd9Sstevel@tonic-gate } 3617c478bd9Sstevel@tonic-gate else 3627c478bd9Sstevel@tonic-gate { 3637c478bd9Sstevel@tonic-gate /* 3647c478bd9Sstevel@tonic-gate ** Failure in step 3. Scan argv and free 3657c478bd9Sstevel@tonic-gate ** all exception arguments and all string 3667c478bd9Sstevel@tonic-gate ** arguments that have been duplicated. 3677c478bd9Sstevel@tonic-gate ** Then free argv. 3687c478bd9Sstevel@tonic-gate */ 3697c478bd9Sstevel@tonic-gate 3707c478bd9Sstevel@tonic-gate for (i = 0; i < argc; ++i) 3717c478bd9Sstevel@tonic-gate { 3727c478bd9Sstevel@tonic-gate switch (etype->etype_argformat[i]) 3737c478bd9Sstevel@tonic-gate { 3747c478bd9Sstevel@tonic-gate case 'e': 3757c478bd9Sstevel@tonic-gate sm_exc_free(argv[i].v_exc); 3767c478bd9Sstevel@tonic-gate break; 3777c478bd9Sstevel@tonic-gate case 's': 3787c478bd9Sstevel@tonic-gate case 'r': 3797c478bd9Sstevel@tonic-gate if (i < si) 3807c478bd9Sstevel@tonic-gate sm_free(argv[i].v_str); 3817c478bd9Sstevel@tonic-gate break; 3827c478bd9Sstevel@tonic-gate } 3837c478bd9Sstevel@tonic-gate } 3847c478bd9Sstevel@tonic-gate sm_free(argv); 3857c478bd9Sstevel@tonic-gate } 3867c478bd9Sstevel@tonic-gate sm_free(exc); 3877c478bd9Sstevel@tonic-gate sm_exc_raise_x(e); 3887c478bd9Sstevel@tonic-gate } 3897c478bd9Sstevel@tonic-gate SM_END_TRY 3907c478bd9Sstevel@tonic-gate 3917c478bd9Sstevel@tonic-gate return exc; 3927c478bd9Sstevel@tonic-gate } 3937c478bd9Sstevel@tonic-gate 3947c478bd9Sstevel@tonic-gate /* 3957c478bd9Sstevel@tonic-gate ** SM_EXC_NEW_X -- Construct a new exception object. 3967c478bd9Sstevel@tonic-gate ** 3977c478bd9Sstevel@tonic-gate ** Parameters: 3987c478bd9Sstevel@tonic-gate ** etype -- type of exception. 3997c478bd9Sstevel@tonic-gate ** ... -- varargs. 4007c478bd9Sstevel@tonic-gate ** 4017c478bd9Sstevel@tonic-gate ** Returns: 4027c478bd9Sstevel@tonic-gate ** pointer to exception object. 4037c478bd9Sstevel@tonic-gate */ 4047c478bd9Sstevel@tonic-gate 4057c478bd9Sstevel@tonic-gate SM_EXC_T * 4067c478bd9Sstevel@tonic-gate #if SM_VA_STD 4077c478bd9Sstevel@tonic-gate sm_exc_new_x( 4087c478bd9Sstevel@tonic-gate const SM_EXC_TYPE_T *etype, 4097c478bd9Sstevel@tonic-gate ...) 4107c478bd9Sstevel@tonic-gate #else /* SM_VA_STD */ 4117c478bd9Sstevel@tonic-gate sm_exc_new_x(etype, va_alist) 4127c478bd9Sstevel@tonic-gate const SM_EXC_TYPE_T *etype; 4137c478bd9Sstevel@tonic-gate va_dcl 4147c478bd9Sstevel@tonic-gate #endif /* SM_VA_STD */ 4157c478bd9Sstevel@tonic-gate { 4167c478bd9Sstevel@tonic-gate SM_EXC_T *exc; 4177c478bd9Sstevel@tonic-gate SM_VA_LOCAL_DECL 4187c478bd9Sstevel@tonic-gate 4197c478bd9Sstevel@tonic-gate SM_VA_START(ap, etype); 4207c478bd9Sstevel@tonic-gate exc = sm_exc_vnew_x(etype, ap); 4217c478bd9Sstevel@tonic-gate SM_VA_END(ap); 4227c478bd9Sstevel@tonic-gate return exc; 4237c478bd9Sstevel@tonic-gate } 4247c478bd9Sstevel@tonic-gate 4257c478bd9Sstevel@tonic-gate /* 4267c478bd9Sstevel@tonic-gate ** SM_EXC_FREE -- Destroy a reference to an exception object. 4277c478bd9Sstevel@tonic-gate ** 4287c478bd9Sstevel@tonic-gate ** Parameters: 4297c478bd9Sstevel@tonic-gate ** exc -- exception object. 4307c478bd9Sstevel@tonic-gate ** 4317c478bd9Sstevel@tonic-gate ** Returns: 4327c478bd9Sstevel@tonic-gate ** none. 4337c478bd9Sstevel@tonic-gate */ 4347c478bd9Sstevel@tonic-gate 4357c478bd9Sstevel@tonic-gate void 4367c478bd9Sstevel@tonic-gate sm_exc_free(exc) 4377c478bd9Sstevel@tonic-gate SM_EXC_T *exc; 4387c478bd9Sstevel@tonic-gate { 4397c478bd9Sstevel@tonic-gate if (exc == NULL) 4407c478bd9Sstevel@tonic-gate return; 4417c478bd9Sstevel@tonic-gate SM_REQUIRE(exc->sm_magic == SmExcMagic); 4427c478bd9Sstevel@tonic-gate if (exc->exc_refcount == 0) 4437c478bd9Sstevel@tonic-gate return; 4447c478bd9Sstevel@tonic-gate if (--exc->exc_refcount == 0) 4457c478bd9Sstevel@tonic-gate { 4467c478bd9Sstevel@tonic-gate int i, c; 4477c478bd9Sstevel@tonic-gate 4487c478bd9Sstevel@tonic-gate for (i = 0; (c = exc->exc_type->etype_argformat[i]) != '\0'; 4497c478bd9Sstevel@tonic-gate ++i) 4507c478bd9Sstevel@tonic-gate { 4517c478bd9Sstevel@tonic-gate switch (c) 4527c478bd9Sstevel@tonic-gate { 4537c478bd9Sstevel@tonic-gate case 's': 4547c478bd9Sstevel@tonic-gate case 'r': 4557c478bd9Sstevel@tonic-gate sm_free(exc->exc_argv[i].v_str); 4567c478bd9Sstevel@tonic-gate break; 4577c478bd9Sstevel@tonic-gate case 'e': 4587c478bd9Sstevel@tonic-gate sm_exc_free(exc->exc_argv[i].v_exc); 4597c478bd9Sstevel@tonic-gate break; 4607c478bd9Sstevel@tonic-gate } 4617c478bd9Sstevel@tonic-gate } 4627c478bd9Sstevel@tonic-gate exc->sm_magic = NULL; 4637c478bd9Sstevel@tonic-gate sm_free(exc->exc_argv); 4647c478bd9Sstevel@tonic-gate sm_free(exc); 4657c478bd9Sstevel@tonic-gate } 4667c478bd9Sstevel@tonic-gate } 4677c478bd9Sstevel@tonic-gate 4687c478bd9Sstevel@tonic-gate /* 4697c478bd9Sstevel@tonic-gate ** SM_EXC_MATCH -- Match exception category against a glob pattern. 4707c478bd9Sstevel@tonic-gate ** 4717c478bd9Sstevel@tonic-gate ** Parameters: 4727c478bd9Sstevel@tonic-gate ** exc -- exception. 4737c478bd9Sstevel@tonic-gate ** pattern -- glob pattern. 4747c478bd9Sstevel@tonic-gate ** 4757c478bd9Sstevel@tonic-gate ** Returns: 4767c478bd9Sstevel@tonic-gate ** true iff match. 4777c478bd9Sstevel@tonic-gate */ 4787c478bd9Sstevel@tonic-gate 4797c478bd9Sstevel@tonic-gate bool 4807c478bd9Sstevel@tonic-gate sm_exc_match(exc, pattern) 4817c478bd9Sstevel@tonic-gate SM_EXC_T *exc; 4827c478bd9Sstevel@tonic-gate const char *pattern; 4837c478bd9Sstevel@tonic-gate { 4847c478bd9Sstevel@tonic-gate if (exc == NULL) 4857c478bd9Sstevel@tonic-gate return false; 4867c478bd9Sstevel@tonic-gate SM_REQUIRE(exc->sm_magic == SmExcMagic); 4877c478bd9Sstevel@tonic-gate return sm_match(exc->exc_type->etype_category, pattern); 4887c478bd9Sstevel@tonic-gate } 4897c478bd9Sstevel@tonic-gate 4907c478bd9Sstevel@tonic-gate /* 4917c478bd9Sstevel@tonic-gate ** SM_EXC_WRITE -- Write exception message to a stream (wo trailing newline). 4927c478bd9Sstevel@tonic-gate ** 4937c478bd9Sstevel@tonic-gate ** Parameters: 4947c478bd9Sstevel@tonic-gate ** exc -- exception. 4957c478bd9Sstevel@tonic-gate ** stream -- file for output. 4967c478bd9Sstevel@tonic-gate ** 4977c478bd9Sstevel@tonic-gate ** Returns: 4987c478bd9Sstevel@tonic-gate ** none. 4997c478bd9Sstevel@tonic-gate */ 5007c478bd9Sstevel@tonic-gate 5017c478bd9Sstevel@tonic-gate void 5027c478bd9Sstevel@tonic-gate sm_exc_write(exc, stream) 5037c478bd9Sstevel@tonic-gate SM_EXC_T *exc; 5047c478bd9Sstevel@tonic-gate SM_FILE_T *stream; 5057c478bd9Sstevel@tonic-gate { 5067c478bd9Sstevel@tonic-gate SM_REQUIRE_ISA(exc, SmExcMagic); 5077c478bd9Sstevel@tonic-gate exc->exc_type->etype_print(exc, stream); 5087c478bd9Sstevel@tonic-gate } 5097c478bd9Sstevel@tonic-gate 5107c478bd9Sstevel@tonic-gate /* 5117c478bd9Sstevel@tonic-gate ** SM_EXC_PRINT -- Print exception message to a stream (with trailing newline). 5127c478bd9Sstevel@tonic-gate ** 5137c478bd9Sstevel@tonic-gate ** Parameters: 5147c478bd9Sstevel@tonic-gate ** exc -- exception. 5157c478bd9Sstevel@tonic-gate ** stream -- file for output. 5167c478bd9Sstevel@tonic-gate ** 5177c478bd9Sstevel@tonic-gate ** Returns: 5187c478bd9Sstevel@tonic-gate ** none. 5197c478bd9Sstevel@tonic-gate */ 5207c478bd9Sstevel@tonic-gate 5217c478bd9Sstevel@tonic-gate void 5227c478bd9Sstevel@tonic-gate sm_exc_print(exc, stream) 5237c478bd9Sstevel@tonic-gate SM_EXC_T *exc; 5247c478bd9Sstevel@tonic-gate SM_FILE_T *stream; 5257c478bd9Sstevel@tonic-gate { 5267c478bd9Sstevel@tonic-gate SM_REQUIRE_ISA(exc, SmExcMagic); 5277c478bd9Sstevel@tonic-gate exc->exc_type->etype_print(exc, stream); 5287c478bd9Sstevel@tonic-gate (void) sm_io_putc(stream, SM_TIME_DEFAULT, '\n'); 5297c478bd9Sstevel@tonic-gate } 5307c478bd9Sstevel@tonic-gate 5317c478bd9Sstevel@tonic-gate SM_EXC_HANDLER_T *SmExcHandler = NULL; 5327c478bd9Sstevel@tonic-gate static SM_EXC_DEFAULT_HANDLER_T SmExcDefaultHandler = NULL; 5337c478bd9Sstevel@tonic-gate 5347c478bd9Sstevel@tonic-gate /* 5357c478bd9Sstevel@tonic-gate ** SM_EXC_NEWTHREAD -- Initialize exception handling for new process/thread. 5367c478bd9Sstevel@tonic-gate ** 5377c478bd9Sstevel@tonic-gate ** Parameters: 5387c478bd9Sstevel@tonic-gate ** h -- default exception handler. 5397c478bd9Sstevel@tonic-gate ** 5407c478bd9Sstevel@tonic-gate ** Returns: 5417c478bd9Sstevel@tonic-gate ** none. 5427c478bd9Sstevel@tonic-gate */ 5437c478bd9Sstevel@tonic-gate 5447c478bd9Sstevel@tonic-gate /* 5457c478bd9Sstevel@tonic-gate ** Initialize a new process or a new thread by clearing the 5467c478bd9Sstevel@tonic-gate ** exception handler stack and optionally setting a default 5477c478bd9Sstevel@tonic-gate ** exception handler function. Call this at the beginning of main, 5487c478bd9Sstevel@tonic-gate ** or in a new process after calling fork, or in a new thread. 5497c478bd9Sstevel@tonic-gate ** 5507c478bd9Sstevel@tonic-gate ** This function is a luxury, not a necessity. 5517c478bd9Sstevel@tonic-gate ** If h != NULL then you can get the same effect by 5527c478bd9Sstevel@tonic-gate ** wrapping the body of main, or the body of a forked child 5537c478bd9Sstevel@tonic-gate ** or a new thread in SM_TRY ... SM_EXCEPT(e,"*") h(e); SM_END_TRY. 5547c478bd9Sstevel@tonic-gate */ 5557c478bd9Sstevel@tonic-gate 5567c478bd9Sstevel@tonic-gate void 5577c478bd9Sstevel@tonic-gate sm_exc_newthread(h) 5587c478bd9Sstevel@tonic-gate SM_EXC_DEFAULT_HANDLER_T h; 5597c478bd9Sstevel@tonic-gate { 5607c478bd9Sstevel@tonic-gate SmExcHandler = NULL; 5617c478bd9Sstevel@tonic-gate SmExcDefaultHandler = h; 5627c478bd9Sstevel@tonic-gate } 5637c478bd9Sstevel@tonic-gate 5647c478bd9Sstevel@tonic-gate /* 5657c478bd9Sstevel@tonic-gate ** SM_EXC_RAISE_X -- Raise an exception. 5667c478bd9Sstevel@tonic-gate ** 5677c478bd9Sstevel@tonic-gate ** Parameters: 5687c478bd9Sstevel@tonic-gate ** exc -- exception. 5697c478bd9Sstevel@tonic-gate ** 5707c478bd9Sstevel@tonic-gate ** Returns: 5717c478bd9Sstevel@tonic-gate ** doesn't. 5727c478bd9Sstevel@tonic-gate */ 5737c478bd9Sstevel@tonic-gate 5747c478bd9Sstevel@tonic-gate void SM_DEAD_D 5757c478bd9Sstevel@tonic-gate sm_exc_raise_x(exc) 5767c478bd9Sstevel@tonic-gate SM_EXC_T *exc; 5777c478bd9Sstevel@tonic-gate { 5787c478bd9Sstevel@tonic-gate SM_REQUIRE_ISA(exc, SmExcMagic); 5797c478bd9Sstevel@tonic-gate 5807c478bd9Sstevel@tonic-gate if (SmExcHandler == NULL) 5817c478bd9Sstevel@tonic-gate { 5827c478bd9Sstevel@tonic-gate if (SmExcDefaultHandler != NULL) 5837c478bd9Sstevel@tonic-gate { 5847c478bd9Sstevel@tonic-gate SM_EXC_DEFAULT_HANDLER_T h; 5857c478bd9Sstevel@tonic-gate 5867c478bd9Sstevel@tonic-gate /* 5877c478bd9Sstevel@tonic-gate ** If defined, the default handler is expected 5887c478bd9Sstevel@tonic-gate ** to terminate the current thread of execution 5897c478bd9Sstevel@tonic-gate ** using exit() or pthread_exit(). 5907c478bd9Sstevel@tonic-gate ** If it instead returns normally, then we fall 5917c478bd9Sstevel@tonic-gate ** through to the default case below. If it 5927c478bd9Sstevel@tonic-gate ** raises an exception, then sm_exc_raise_x is 5937c478bd9Sstevel@tonic-gate ** re-entered and, because we set SmExcDefaultHandler 5947c478bd9Sstevel@tonic-gate ** to NULL before invoking h, we will again 5957c478bd9Sstevel@tonic-gate ** end up in the default case below. 5967c478bd9Sstevel@tonic-gate */ 5977c478bd9Sstevel@tonic-gate 5987c478bd9Sstevel@tonic-gate h = SmExcDefaultHandler; 5997c478bd9Sstevel@tonic-gate SmExcDefaultHandler = NULL; 6007c478bd9Sstevel@tonic-gate (*h)(exc); 6017c478bd9Sstevel@tonic-gate } 6027c478bd9Sstevel@tonic-gate 6037c478bd9Sstevel@tonic-gate /* 6047c478bd9Sstevel@tonic-gate ** No exception handler, so print the error and exit. 6057c478bd9Sstevel@tonic-gate ** To override this behaviour on a program wide basis, 6067c478bd9Sstevel@tonic-gate ** call sm_exc_newthread or put an exception handler in main(). 6077c478bd9Sstevel@tonic-gate ** 6087c478bd9Sstevel@tonic-gate ** XXX TODO: map the exception category to an exit code 6097c478bd9Sstevel@tonic-gate ** XXX from <sysexits.h>. 6107c478bd9Sstevel@tonic-gate */ 6117c478bd9Sstevel@tonic-gate 6127c478bd9Sstevel@tonic-gate sm_exc_print(exc, smioerr); 6137c478bd9Sstevel@tonic-gate exit(255); 6147c478bd9Sstevel@tonic-gate } 6157c478bd9Sstevel@tonic-gate 6167c478bd9Sstevel@tonic-gate if (SmExcHandler->eh_value == NULL) 6177c478bd9Sstevel@tonic-gate SmExcHandler->eh_value = exc; 6187c478bd9Sstevel@tonic-gate else 6197c478bd9Sstevel@tonic-gate sm_exc_free(exc); 6207c478bd9Sstevel@tonic-gate 6217c478bd9Sstevel@tonic-gate sm_longjmp_nosig(SmExcHandler->eh_context, 1); 6227c478bd9Sstevel@tonic-gate } 6237c478bd9Sstevel@tonic-gate 6247c478bd9Sstevel@tonic-gate /* 6257c478bd9Sstevel@tonic-gate ** SM_EXC_RAISENEW_X -- shorthand for sm_exc_raise_x(sm_exc_new_x(...)) 6267c478bd9Sstevel@tonic-gate ** 6277c478bd9Sstevel@tonic-gate ** Parameters: 6287c478bd9Sstevel@tonic-gate ** etype -- type of exception. 6297c478bd9Sstevel@tonic-gate ** ap -- varargs. 6307c478bd9Sstevel@tonic-gate ** 6317c478bd9Sstevel@tonic-gate ** Returns: 6327c478bd9Sstevel@tonic-gate ** none. 6337c478bd9Sstevel@tonic-gate */ 6347c478bd9Sstevel@tonic-gate 6357c478bd9Sstevel@tonic-gate void SM_DEAD_D 6367c478bd9Sstevel@tonic-gate #if SM_VA_STD 6377c478bd9Sstevel@tonic-gate sm_exc_raisenew_x( 6387c478bd9Sstevel@tonic-gate const SM_EXC_TYPE_T *etype, 6397c478bd9Sstevel@tonic-gate ...) 6407c478bd9Sstevel@tonic-gate #else 6417c478bd9Sstevel@tonic-gate sm_exc_raisenew_x(etype, va_alist) 6427c478bd9Sstevel@tonic-gate const SM_EXC_TYPE_T *etype; 6437c478bd9Sstevel@tonic-gate va_dcl 6447c478bd9Sstevel@tonic-gate #endif 6457c478bd9Sstevel@tonic-gate { 6467c478bd9Sstevel@tonic-gate SM_EXC_T *exc; 6477c478bd9Sstevel@tonic-gate SM_VA_LOCAL_DECL 6487c478bd9Sstevel@tonic-gate 6497c478bd9Sstevel@tonic-gate SM_VA_START(ap, etype); 6507c478bd9Sstevel@tonic-gate exc = sm_exc_vnew_x(etype, ap); 6517c478bd9Sstevel@tonic-gate SM_VA_END(ap); 6527c478bd9Sstevel@tonic-gate sm_exc_raise_x(exc); 6537c478bd9Sstevel@tonic-gate } 654