1*7c478bd9Sstevel@tonic-gate /* 2*7c478bd9Sstevel@tonic-gate * Copyright (c) 2000-2002 Sendmail, Inc. and its suppliers. 3*7c478bd9Sstevel@tonic-gate * All rights reserved. 4*7c478bd9Sstevel@tonic-gate * 5*7c478bd9Sstevel@tonic-gate * By using this file, you agree to the terms and conditions set 6*7c478bd9Sstevel@tonic-gate * forth in the LICENSE file which can be found at the top level of 7*7c478bd9Sstevel@tonic-gate * the sendmail distribution. 8*7c478bd9Sstevel@tonic-gate * 9*7c478bd9Sstevel@tonic-gate */ 10*7c478bd9Sstevel@tonic-gate 11*7c478bd9Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 12*7c478bd9Sstevel@tonic-gate 13*7c478bd9Sstevel@tonic-gate #include <sm/gen.h> 14*7c478bd9Sstevel@tonic-gate SM_RCSID("@(#)$Id: exc.c,v 1.47.2.1 2003/12/05 22:44:17 ca Exp $") 15*7c478bd9Sstevel@tonic-gate 16*7c478bd9Sstevel@tonic-gate /* 17*7c478bd9Sstevel@tonic-gate ** exception handling 18*7c478bd9Sstevel@tonic-gate ** For documentation, see exc.html 19*7c478bd9Sstevel@tonic-gate */ 20*7c478bd9Sstevel@tonic-gate 21*7c478bd9Sstevel@tonic-gate #include <ctype.h> 22*7c478bd9Sstevel@tonic-gate #include <string.h> 23*7c478bd9Sstevel@tonic-gate 24*7c478bd9Sstevel@tonic-gate #include <sm/errstring.h> 25*7c478bd9Sstevel@tonic-gate #include <sm/exc.h> 26*7c478bd9Sstevel@tonic-gate #include <sm/heap.h> 27*7c478bd9Sstevel@tonic-gate #include <sm/string.h> 28*7c478bd9Sstevel@tonic-gate #include <sm/varargs.h> 29*7c478bd9Sstevel@tonic-gate #include <sm/io.h> 30*7c478bd9Sstevel@tonic-gate 31*7c478bd9Sstevel@tonic-gate const char SmExcMagic[] = "sm_exc"; 32*7c478bd9Sstevel@tonic-gate const char SmExcTypeMagic[] = "sm_exc_type"; 33*7c478bd9Sstevel@tonic-gate 34*7c478bd9Sstevel@tonic-gate /* 35*7c478bd9Sstevel@tonic-gate ** SM_ETYPE_PRINTF -- printf for exception types. 36*7c478bd9Sstevel@tonic-gate ** 37*7c478bd9Sstevel@tonic-gate ** Parameters: 38*7c478bd9Sstevel@tonic-gate ** exc -- exception. 39*7c478bd9Sstevel@tonic-gate ** stream -- file for output. 40*7c478bd9Sstevel@tonic-gate ** 41*7c478bd9Sstevel@tonic-gate ** Returns: 42*7c478bd9Sstevel@tonic-gate ** none. 43*7c478bd9Sstevel@tonic-gate */ 44*7c478bd9Sstevel@tonic-gate 45*7c478bd9Sstevel@tonic-gate /* 46*7c478bd9Sstevel@tonic-gate ** A simple formatted print function that can be used as the print function 47*7c478bd9Sstevel@tonic-gate ** by most exception types. It prints the printcontext string, interpreting 48*7c478bd9Sstevel@tonic-gate ** occurrences of %0 through %9 as references to the argument vector. 49*7c478bd9Sstevel@tonic-gate ** If exception argument 3 is an int or long, then %3 will print the 50*7c478bd9Sstevel@tonic-gate ** argument in decimal, and %o3 or %x3 will print it in octal or hex. 51*7c478bd9Sstevel@tonic-gate */ 52*7c478bd9Sstevel@tonic-gate 53*7c478bd9Sstevel@tonic-gate void 54*7c478bd9Sstevel@tonic-gate sm_etype_printf(exc, stream) 55*7c478bd9Sstevel@tonic-gate SM_EXC_T *exc; 56*7c478bd9Sstevel@tonic-gate SM_FILE_T *stream; 57*7c478bd9Sstevel@tonic-gate { 58*7c478bd9Sstevel@tonic-gate size_t n = strlen(exc->exc_type->etype_argformat); 59*7c478bd9Sstevel@tonic-gate const char *p, *s; 60*7c478bd9Sstevel@tonic-gate char format; 61*7c478bd9Sstevel@tonic-gate 62*7c478bd9Sstevel@tonic-gate for (p = exc->exc_type->etype_printcontext; *p != '\0'; ++p) 63*7c478bd9Sstevel@tonic-gate { 64*7c478bd9Sstevel@tonic-gate if (*p != '%') 65*7c478bd9Sstevel@tonic-gate { 66*7c478bd9Sstevel@tonic-gate (void) sm_io_putc(stream, SM_TIME_DEFAULT, *p); 67*7c478bd9Sstevel@tonic-gate continue; 68*7c478bd9Sstevel@tonic-gate } 69*7c478bd9Sstevel@tonic-gate ++p; 70*7c478bd9Sstevel@tonic-gate if (*p == '\0') 71*7c478bd9Sstevel@tonic-gate { 72*7c478bd9Sstevel@tonic-gate (void) sm_io_putc(stream, SM_TIME_DEFAULT, '%'); 73*7c478bd9Sstevel@tonic-gate break; 74*7c478bd9Sstevel@tonic-gate } 75*7c478bd9Sstevel@tonic-gate if (*p == '%') 76*7c478bd9Sstevel@tonic-gate { 77*7c478bd9Sstevel@tonic-gate (void) sm_io_putc(stream, SM_TIME_DEFAULT, '%'); 78*7c478bd9Sstevel@tonic-gate continue; 79*7c478bd9Sstevel@tonic-gate } 80*7c478bd9Sstevel@tonic-gate format = '\0'; 81*7c478bd9Sstevel@tonic-gate if (isalpha(*p)) 82*7c478bd9Sstevel@tonic-gate { 83*7c478bd9Sstevel@tonic-gate format = *p++; 84*7c478bd9Sstevel@tonic-gate if (*p == '\0') 85*7c478bd9Sstevel@tonic-gate { 86*7c478bd9Sstevel@tonic-gate (void) sm_io_putc(stream, SM_TIME_DEFAULT, '%'); 87*7c478bd9Sstevel@tonic-gate (void) sm_io_putc(stream, SM_TIME_DEFAULT, 88*7c478bd9Sstevel@tonic-gate format); 89*7c478bd9Sstevel@tonic-gate break; 90*7c478bd9Sstevel@tonic-gate } 91*7c478bd9Sstevel@tonic-gate } 92*7c478bd9Sstevel@tonic-gate if (isdigit(*p)) 93*7c478bd9Sstevel@tonic-gate { 94*7c478bd9Sstevel@tonic-gate size_t i = *p - '0'; 95*7c478bd9Sstevel@tonic-gate if (i < n) 96*7c478bd9Sstevel@tonic-gate { 97*7c478bd9Sstevel@tonic-gate switch (exc->exc_type->etype_argformat[i]) 98*7c478bd9Sstevel@tonic-gate { 99*7c478bd9Sstevel@tonic-gate case 's': 100*7c478bd9Sstevel@tonic-gate case 'r': 101*7c478bd9Sstevel@tonic-gate s = exc->exc_argv[i].v_str; 102*7c478bd9Sstevel@tonic-gate if (s == NULL) 103*7c478bd9Sstevel@tonic-gate s = "(null)"; 104*7c478bd9Sstevel@tonic-gate sm_io_fputs(stream, SM_TIME_DEFAULT, s); 105*7c478bd9Sstevel@tonic-gate continue; 106*7c478bd9Sstevel@tonic-gate case 'i': 107*7c478bd9Sstevel@tonic-gate sm_io_fprintf(stream, 108*7c478bd9Sstevel@tonic-gate SM_TIME_DEFAULT, 109*7c478bd9Sstevel@tonic-gate format == 'o' ? "%o" 110*7c478bd9Sstevel@tonic-gate : format == 'x' ? "%x" 111*7c478bd9Sstevel@tonic-gate : "%d", 112*7c478bd9Sstevel@tonic-gate exc->exc_argv[i].v_int); 113*7c478bd9Sstevel@tonic-gate continue; 114*7c478bd9Sstevel@tonic-gate case 'l': 115*7c478bd9Sstevel@tonic-gate sm_io_fprintf(stream, 116*7c478bd9Sstevel@tonic-gate SM_TIME_DEFAULT, 117*7c478bd9Sstevel@tonic-gate format == 'o' ? "%lo" 118*7c478bd9Sstevel@tonic-gate : format == 'x' ? "%lx" 119*7c478bd9Sstevel@tonic-gate : "%ld", 120*7c478bd9Sstevel@tonic-gate exc->exc_argv[i].v_long); 121*7c478bd9Sstevel@tonic-gate continue; 122*7c478bd9Sstevel@tonic-gate case 'e': 123*7c478bd9Sstevel@tonic-gate sm_exc_write(exc->exc_argv[i].v_exc, 124*7c478bd9Sstevel@tonic-gate stream); 125*7c478bd9Sstevel@tonic-gate continue; 126*7c478bd9Sstevel@tonic-gate } 127*7c478bd9Sstevel@tonic-gate } 128*7c478bd9Sstevel@tonic-gate } 129*7c478bd9Sstevel@tonic-gate (void) sm_io_putc(stream, SM_TIME_DEFAULT, '%'); 130*7c478bd9Sstevel@tonic-gate if (format) 131*7c478bd9Sstevel@tonic-gate (void) sm_io_putc(stream, SM_TIME_DEFAULT, format); 132*7c478bd9Sstevel@tonic-gate (void) sm_io_putc(stream, SM_TIME_DEFAULT, *p); 133*7c478bd9Sstevel@tonic-gate } 134*7c478bd9Sstevel@tonic-gate } 135*7c478bd9Sstevel@tonic-gate 136*7c478bd9Sstevel@tonic-gate /* 137*7c478bd9Sstevel@tonic-gate ** Standard exception types. 138*7c478bd9Sstevel@tonic-gate */ 139*7c478bd9Sstevel@tonic-gate 140*7c478bd9Sstevel@tonic-gate /* 141*7c478bd9Sstevel@tonic-gate ** SM_ETYPE_OS_PRINT -- Print OS related exception. 142*7c478bd9Sstevel@tonic-gate ** 143*7c478bd9Sstevel@tonic-gate ** Parameters: 144*7c478bd9Sstevel@tonic-gate ** exc -- exception. 145*7c478bd9Sstevel@tonic-gate ** stream -- file for output. 146*7c478bd9Sstevel@tonic-gate ** 147*7c478bd9Sstevel@tonic-gate ** Returns: 148*7c478bd9Sstevel@tonic-gate ** none. 149*7c478bd9Sstevel@tonic-gate */ 150*7c478bd9Sstevel@tonic-gate 151*7c478bd9Sstevel@tonic-gate static void 152*7c478bd9Sstevel@tonic-gate sm_etype_os_print __P(( 153*7c478bd9Sstevel@tonic-gate SM_EXC_T *exc, 154*7c478bd9Sstevel@tonic-gate SM_FILE_T *stream)); 155*7c478bd9Sstevel@tonic-gate 156*7c478bd9Sstevel@tonic-gate static void 157*7c478bd9Sstevel@tonic-gate sm_etype_os_print(exc, stream) 158*7c478bd9Sstevel@tonic-gate SM_EXC_T *exc; 159*7c478bd9Sstevel@tonic-gate SM_FILE_T *stream; 160*7c478bd9Sstevel@tonic-gate { 161*7c478bd9Sstevel@tonic-gate int err = exc->exc_argv[0].v_int; 162*7c478bd9Sstevel@tonic-gate char *syscall = exc->exc_argv[1].v_str; 163*7c478bd9Sstevel@tonic-gate char *sysargs = exc->exc_argv[2].v_str; 164*7c478bd9Sstevel@tonic-gate 165*7c478bd9Sstevel@tonic-gate if (sysargs) 166*7c478bd9Sstevel@tonic-gate sm_io_fprintf(stream, SM_TIME_DEFAULT, "%s: %s failed: %s", 167*7c478bd9Sstevel@tonic-gate sysargs, syscall, sm_errstring(err)); 168*7c478bd9Sstevel@tonic-gate else 169*7c478bd9Sstevel@tonic-gate sm_io_fprintf(stream, SM_TIME_DEFAULT, "%s failed: %s", syscall, 170*7c478bd9Sstevel@tonic-gate sm_errstring(err)); 171*7c478bd9Sstevel@tonic-gate } 172*7c478bd9Sstevel@tonic-gate 173*7c478bd9Sstevel@tonic-gate /* 174*7c478bd9Sstevel@tonic-gate ** SmEtypeOs represents the failure of a Unix system call. 175*7c478bd9Sstevel@tonic-gate ** The three arguments are: 176*7c478bd9Sstevel@tonic-gate ** int errno (eg, ENOENT) 177*7c478bd9Sstevel@tonic-gate ** char *syscall (eg, "open") 178*7c478bd9Sstevel@tonic-gate ** char *sysargs (eg, NULL or "/etc/mail/sendmail.cf") 179*7c478bd9Sstevel@tonic-gate */ 180*7c478bd9Sstevel@tonic-gate 181*7c478bd9Sstevel@tonic-gate const SM_EXC_TYPE_T SmEtypeOs = 182*7c478bd9Sstevel@tonic-gate { 183*7c478bd9Sstevel@tonic-gate SmExcTypeMagic, 184*7c478bd9Sstevel@tonic-gate "E:sm.os", 185*7c478bd9Sstevel@tonic-gate "isr", 186*7c478bd9Sstevel@tonic-gate sm_etype_os_print, 187*7c478bd9Sstevel@tonic-gate NULL, 188*7c478bd9Sstevel@tonic-gate }; 189*7c478bd9Sstevel@tonic-gate 190*7c478bd9Sstevel@tonic-gate /* 191*7c478bd9Sstevel@tonic-gate ** SmEtypeErr is a completely generic error which should only be 192*7c478bd9Sstevel@tonic-gate ** used in applications and test programs. Libraries should use 193*7c478bd9Sstevel@tonic-gate ** more specific exception codes. 194*7c478bd9Sstevel@tonic-gate */ 195*7c478bd9Sstevel@tonic-gate 196*7c478bd9Sstevel@tonic-gate const SM_EXC_TYPE_T SmEtypeErr = 197*7c478bd9Sstevel@tonic-gate { 198*7c478bd9Sstevel@tonic-gate SmExcTypeMagic, 199*7c478bd9Sstevel@tonic-gate "E:sm.err", 200*7c478bd9Sstevel@tonic-gate "r", 201*7c478bd9Sstevel@tonic-gate sm_etype_printf, 202*7c478bd9Sstevel@tonic-gate "%0", 203*7c478bd9Sstevel@tonic-gate }; 204*7c478bd9Sstevel@tonic-gate 205*7c478bd9Sstevel@tonic-gate /* 206*7c478bd9Sstevel@tonic-gate ** SM_EXC_VNEW_X -- Construct a new exception object. 207*7c478bd9Sstevel@tonic-gate ** 208*7c478bd9Sstevel@tonic-gate ** Parameters: 209*7c478bd9Sstevel@tonic-gate ** etype -- type of exception. 210*7c478bd9Sstevel@tonic-gate ** ap -- varargs. 211*7c478bd9Sstevel@tonic-gate ** 212*7c478bd9Sstevel@tonic-gate ** Returns: 213*7c478bd9Sstevel@tonic-gate ** pointer to exception object. 214*7c478bd9Sstevel@tonic-gate */ 215*7c478bd9Sstevel@tonic-gate 216*7c478bd9Sstevel@tonic-gate /* 217*7c478bd9Sstevel@tonic-gate ** This is an auxiliary function called by sm_exc_new_x and sm_exc_raisenew_x. 218*7c478bd9Sstevel@tonic-gate ** 219*7c478bd9Sstevel@tonic-gate ** If an exception is raised, then to avoid a storage leak, we must: 220*7c478bd9Sstevel@tonic-gate ** (a) Free all storage we have allocated. 221*7c478bd9Sstevel@tonic-gate ** (b) Free all exception arguments in the varargs list. 222*7c478bd9Sstevel@tonic-gate ** Getting this right is tricky. 223*7c478bd9Sstevel@tonic-gate ** 224*7c478bd9Sstevel@tonic-gate ** To see why (b) is required, consider the code fragment 225*7c478bd9Sstevel@tonic-gate ** SM_EXCEPT(exc, "*") 226*7c478bd9Sstevel@tonic-gate ** sm_exc_raisenew_x(&MyEtype, exc); 227*7c478bd9Sstevel@tonic-gate ** SM_END_TRY 228*7c478bd9Sstevel@tonic-gate ** In the normal case, sm_exc_raisenew_x will allocate and raise a new 229*7c478bd9Sstevel@tonic-gate ** exception E that owns exc. When E is eventually freed, exc is also freed. 230*7c478bd9Sstevel@tonic-gate ** In the exceptional case, sm_exc_raisenew_x must free exc before raising 231*7c478bd9Sstevel@tonic-gate ** an out-of-memory exception so that exc is not leaked. 232*7c478bd9Sstevel@tonic-gate */ 233*7c478bd9Sstevel@tonic-gate 234*7c478bd9Sstevel@tonic-gate SM_EXC_T * 235*7c478bd9Sstevel@tonic-gate sm_exc_vnew_x(etype, ap) 236*7c478bd9Sstevel@tonic-gate const SM_EXC_TYPE_T *etype; 237*7c478bd9Sstevel@tonic-gate va_list SM_NONVOLATILE ap; 238*7c478bd9Sstevel@tonic-gate { 239*7c478bd9Sstevel@tonic-gate /* 240*7c478bd9Sstevel@tonic-gate ** All variables that are modified in the SM_TRY clause and 241*7c478bd9Sstevel@tonic-gate ** referenced in the SM_EXCEPT clause must be declared volatile. 242*7c478bd9Sstevel@tonic-gate */ 243*7c478bd9Sstevel@tonic-gate 244*7c478bd9Sstevel@tonic-gate /* NOTE: Type of si, i, and argc *must* match */ 245*7c478bd9Sstevel@tonic-gate SM_EXC_T * volatile exc = NULL; 246*7c478bd9Sstevel@tonic-gate int volatile si = 0; 247*7c478bd9Sstevel@tonic-gate SM_VAL_T * volatile argv = NULL; 248*7c478bd9Sstevel@tonic-gate int i, argc; 249*7c478bd9Sstevel@tonic-gate 250*7c478bd9Sstevel@tonic-gate SM_REQUIRE_ISA(etype, SmExcTypeMagic); 251*7c478bd9Sstevel@tonic-gate argc = strlen(etype->etype_argformat); 252*7c478bd9Sstevel@tonic-gate SM_TRY 253*7c478bd9Sstevel@tonic-gate { 254*7c478bd9Sstevel@tonic-gate /* 255*7c478bd9Sstevel@tonic-gate ** Step 1. Allocate the exception structure. 256*7c478bd9Sstevel@tonic-gate ** On failure, scan the varargs list and free all 257*7c478bd9Sstevel@tonic-gate ** exception arguments. 258*7c478bd9Sstevel@tonic-gate */ 259*7c478bd9Sstevel@tonic-gate 260*7c478bd9Sstevel@tonic-gate exc = sm_malloc_x(sizeof(SM_EXC_T)); 261*7c478bd9Sstevel@tonic-gate exc->sm_magic = SmExcMagic; 262*7c478bd9Sstevel@tonic-gate exc->exc_refcount = 1; 263*7c478bd9Sstevel@tonic-gate exc->exc_type = etype; 264*7c478bd9Sstevel@tonic-gate exc->exc_argv = NULL; 265*7c478bd9Sstevel@tonic-gate 266*7c478bd9Sstevel@tonic-gate /* 267*7c478bd9Sstevel@tonic-gate ** Step 2. Allocate the argument vector. 268*7c478bd9Sstevel@tonic-gate ** On failure, free exc, scan the varargs list and free all 269*7c478bd9Sstevel@tonic-gate ** exception arguments. On success, scan the varargs list, 270*7c478bd9Sstevel@tonic-gate ** and copy the arguments into argv. 271*7c478bd9Sstevel@tonic-gate */ 272*7c478bd9Sstevel@tonic-gate 273*7c478bd9Sstevel@tonic-gate argv = sm_malloc_x(argc * sizeof(SM_VAL_T)); 274*7c478bd9Sstevel@tonic-gate exc->exc_argv = argv; 275*7c478bd9Sstevel@tonic-gate for (i = 0; i < argc; ++i) 276*7c478bd9Sstevel@tonic-gate { 277*7c478bd9Sstevel@tonic-gate switch (etype->etype_argformat[i]) 278*7c478bd9Sstevel@tonic-gate { 279*7c478bd9Sstevel@tonic-gate case 'i': 280*7c478bd9Sstevel@tonic-gate argv[i].v_int = SM_VA_ARG(ap, int); 281*7c478bd9Sstevel@tonic-gate break; 282*7c478bd9Sstevel@tonic-gate case 'l': 283*7c478bd9Sstevel@tonic-gate argv[i].v_long = SM_VA_ARG(ap, long); 284*7c478bd9Sstevel@tonic-gate break; 285*7c478bd9Sstevel@tonic-gate case 'e': 286*7c478bd9Sstevel@tonic-gate argv[i].v_exc = SM_VA_ARG(ap, SM_EXC_T*); 287*7c478bd9Sstevel@tonic-gate break; 288*7c478bd9Sstevel@tonic-gate case 's': 289*7c478bd9Sstevel@tonic-gate argv[i].v_str = SM_VA_ARG(ap, char*); 290*7c478bd9Sstevel@tonic-gate break; 291*7c478bd9Sstevel@tonic-gate case 'r': 292*7c478bd9Sstevel@tonic-gate SM_REQUIRE(etype->etype_argformat[i+1] == '\0'); 293*7c478bd9Sstevel@tonic-gate argv[i].v_str = SM_VA_ARG(ap, char*); 294*7c478bd9Sstevel@tonic-gate break; 295*7c478bd9Sstevel@tonic-gate default: 296*7c478bd9Sstevel@tonic-gate sm_abort("sm_exc_vnew_x: bad argformat '%c'", 297*7c478bd9Sstevel@tonic-gate etype->etype_argformat[i]); 298*7c478bd9Sstevel@tonic-gate } 299*7c478bd9Sstevel@tonic-gate } 300*7c478bd9Sstevel@tonic-gate 301*7c478bd9Sstevel@tonic-gate /* 302*7c478bd9Sstevel@tonic-gate ** Step 3. Scan argv, and allocate space for all 303*7c478bd9Sstevel@tonic-gate ** string arguments. si is the number of elements 304*7c478bd9Sstevel@tonic-gate ** of argv that have been processed so far. 305*7c478bd9Sstevel@tonic-gate ** On failure, free exc, argv, all the exception arguments 306*7c478bd9Sstevel@tonic-gate ** and all of the strings that have been copied. 307*7c478bd9Sstevel@tonic-gate */ 308*7c478bd9Sstevel@tonic-gate 309*7c478bd9Sstevel@tonic-gate for (si = 0; si < argc; ++si) 310*7c478bd9Sstevel@tonic-gate { 311*7c478bd9Sstevel@tonic-gate switch (etype->etype_argformat[si]) 312*7c478bd9Sstevel@tonic-gate { 313*7c478bd9Sstevel@tonic-gate case 's': 314*7c478bd9Sstevel@tonic-gate { 315*7c478bd9Sstevel@tonic-gate char *str = argv[si].v_str; 316*7c478bd9Sstevel@tonic-gate if (str != NULL) 317*7c478bd9Sstevel@tonic-gate argv[si].v_str = sm_strdup_x(str); 318*7c478bd9Sstevel@tonic-gate } 319*7c478bd9Sstevel@tonic-gate break; 320*7c478bd9Sstevel@tonic-gate case 'r': 321*7c478bd9Sstevel@tonic-gate { 322*7c478bd9Sstevel@tonic-gate char *fmt = argv[si].v_str; 323*7c478bd9Sstevel@tonic-gate if (fmt != NULL) 324*7c478bd9Sstevel@tonic-gate argv[si].v_str = sm_vstringf_x(fmt, ap); 325*7c478bd9Sstevel@tonic-gate } 326*7c478bd9Sstevel@tonic-gate break; 327*7c478bd9Sstevel@tonic-gate } 328*7c478bd9Sstevel@tonic-gate } 329*7c478bd9Sstevel@tonic-gate } 330*7c478bd9Sstevel@tonic-gate SM_EXCEPT(e, "*") 331*7c478bd9Sstevel@tonic-gate { 332*7c478bd9Sstevel@tonic-gate if (exc == NULL || argv == NULL) 333*7c478bd9Sstevel@tonic-gate { 334*7c478bd9Sstevel@tonic-gate /* 335*7c478bd9Sstevel@tonic-gate ** Failure in step 1 or step 2. 336*7c478bd9Sstevel@tonic-gate ** Scan ap and free all exception arguments. 337*7c478bd9Sstevel@tonic-gate */ 338*7c478bd9Sstevel@tonic-gate 339*7c478bd9Sstevel@tonic-gate for (i = 0; i < argc; ++i) 340*7c478bd9Sstevel@tonic-gate { 341*7c478bd9Sstevel@tonic-gate switch (etype->etype_argformat[i]) 342*7c478bd9Sstevel@tonic-gate { 343*7c478bd9Sstevel@tonic-gate case 'i': 344*7c478bd9Sstevel@tonic-gate (void) SM_VA_ARG(ap, int); 345*7c478bd9Sstevel@tonic-gate break; 346*7c478bd9Sstevel@tonic-gate case 'l': 347*7c478bd9Sstevel@tonic-gate (void) SM_VA_ARG(ap, long); 348*7c478bd9Sstevel@tonic-gate break; 349*7c478bd9Sstevel@tonic-gate case 'e': 350*7c478bd9Sstevel@tonic-gate sm_exc_free(SM_VA_ARG(ap, SM_EXC_T*)); 351*7c478bd9Sstevel@tonic-gate break; 352*7c478bd9Sstevel@tonic-gate case 's': 353*7c478bd9Sstevel@tonic-gate case 'r': 354*7c478bd9Sstevel@tonic-gate (void) SM_VA_ARG(ap, char*); 355*7c478bd9Sstevel@tonic-gate break; 356*7c478bd9Sstevel@tonic-gate } 357*7c478bd9Sstevel@tonic-gate } 358*7c478bd9Sstevel@tonic-gate } 359*7c478bd9Sstevel@tonic-gate else 360*7c478bd9Sstevel@tonic-gate { 361*7c478bd9Sstevel@tonic-gate /* 362*7c478bd9Sstevel@tonic-gate ** Failure in step 3. Scan argv and free 363*7c478bd9Sstevel@tonic-gate ** all exception arguments and all string 364*7c478bd9Sstevel@tonic-gate ** arguments that have been duplicated. 365*7c478bd9Sstevel@tonic-gate ** Then free argv. 366*7c478bd9Sstevel@tonic-gate */ 367*7c478bd9Sstevel@tonic-gate 368*7c478bd9Sstevel@tonic-gate for (i = 0; i < argc; ++i) 369*7c478bd9Sstevel@tonic-gate { 370*7c478bd9Sstevel@tonic-gate switch (etype->etype_argformat[i]) 371*7c478bd9Sstevel@tonic-gate { 372*7c478bd9Sstevel@tonic-gate case 'e': 373*7c478bd9Sstevel@tonic-gate sm_exc_free(argv[i].v_exc); 374*7c478bd9Sstevel@tonic-gate break; 375*7c478bd9Sstevel@tonic-gate case 's': 376*7c478bd9Sstevel@tonic-gate case 'r': 377*7c478bd9Sstevel@tonic-gate if (i < si) 378*7c478bd9Sstevel@tonic-gate sm_free(argv[i].v_str); 379*7c478bd9Sstevel@tonic-gate break; 380*7c478bd9Sstevel@tonic-gate } 381*7c478bd9Sstevel@tonic-gate } 382*7c478bd9Sstevel@tonic-gate sm_free(argv); 383*7c478bd9Sstevel@tonic-gate } 384*7c478bd9Sstevel@tonic-gate sm_free(exc); 385*7c478bd9Sstevel@tonic-gate sm_exc_raise_x(e); 386*7c478bd9Sstevel@tonic-gate } 387*7c478bd9Sstevel@tonic-gate SM_END_TRY 388*7c478bd9Sstevel@tonic-gate 389*7c478bd9Sstevel@tonic-gate return exc; 390*7c478bd9Sstevel@tonic-gate } 391*7c478bd9Sstevel@tonic-gate 392*7c478bd9Sstevel@tonic-gate /* 393*7c478bd9Sstevel@tonic-gate ** SM_EXC_NEW_X -- Construct a new exception object. 394*7c478bd9Sstevel@tonic-gate ** 395*7c478bd9Sstevel@tonic-gate ** Parameters: 396*7c478bd9Sstevel@tonic-gate ** etype -- type of exception. 397*7c478bd9Sstevel@tonic-gate ** ... -- varargs. 398*7c478bd9Sstevel@tonic-gate ** 399*7c478bd9Sstevel@tonic-gate ** Returns: 400*7c478bd9Sstevel@tonic-gate ** pointer to exception object. 401*7c478bd9Sstevel@tonic-gate */ 402*7c478bd9Sstevel@tonic-gate 403*7c478bd9Sstevel@tonic-gate SM_EXC_T * 404*7c478bd9Sstevel@tonic-gate #if SM_VA_STD 405*7c478bd9Sstevel@tonic-gate sm_exc_new_x( 406*7c478bd9Sstevel@tonic-gate const SM_EXC_TYPE_T *etype, 407*7c478bd9Sstevel@tonic-gate ...) 408*7c478bd9Sstevel@tonic-gate #else /* SM_VA_STD */ 409*7c478bd9Sstevel@tonic-gate sm_exc_new_x(etype, va_alist) 410*7c478bd9Sstevel@tonic-gate const SM_EXC_TYPE_T *etype; 411*7c478bd9Sstevel@tonic-gate va_dcl 412*7c478bd9Sstevel@tonic-gate #endif /* SM_VA_STD */ 413*7c478bd9Sstevel@tonic-gate { 414*7c478bd9Sstevel@tonic-gate SM_EXC_T *exc; 415*7c478bd9Sstevel@tonic-gate SM_VA_LOCAL_DECL 416*7c478bd9Sstevel@tonic-gate 417*7c478bd9Sstevel@tonic-gate SM_VA_START(ap, etype); 418*7c478bd9Sstevel@tonic-gate exc = sm_exc_vnew_x(etype, ap); 419*7c478bd9Sstevel@tonic-gate SM_VA_END(ap); 420*7c478bd9Sstevel@tonic-gate return exc; 421*7c478bd9Sstevel@tonic-gate } 422*7c478bd9Sstevel@tonic-gate 423*7c478bd9Sstevel@tonic-gate /* 424*7c478bd9Sstevel@tonic-gate ** SM_ADDREF -- Add a reference to an exception object. 425*7c478bd9Sstevel@tonic-gate ** 426*7c478bd9Sstevel@tonic-gate ** Parameters: 427*7c478bd9Sstevel@tonic-gate ** exc -- exception object. 428*7c478bd9Sstevel@tonic-gate ** 429*7c478bd9Sstevel@tonic-gate ** Returns: 430*7c478bd9Sstevel@tonic-gate ** exc itself. 431*7c478bd9Sstevel@tonic-gate */ 432*7c478bd9Sstevel@tonic-gate 433*7c478bd9Sstevel@tonic-gate SM_EXC_T * 434*7c478bd9Sstevel@tonic-gate sm_addref(exc) 435*7c478bd9Sstevel@tonic-gate SM_EXC_T *exc; 436*7c478bd9Sstevel@tonic-gate { 437*7c478bd9Sstevel@tonic-gate SM_REQUIRE_ISA(exc, SmExcMagic); 438*7c478bd9Sstevel@tonic-gate if (exc->exc_refcount != 0) 439*7c478bd9Sstevel@tonic-gate ++exc->exc_refcount; 440*7c478bd9Sstevel@tonic-gate return exc; 441*7c478bd9Sstevel@tonic-gate } 442*7c478bd9Sstevel@tonic-gate 443*7c478bd9Sstevel@tonic-gate /* 444*7c478bd9Sstevel@tonic-gate ** SM_EXC_FREE -- Destroy a reference to an exception object. 445*7c478bd9Sstevel@tonic-gate ** 446*7c478bd9Sstevel@tonic-gate ** Parameters: 447*7c478bd9Sstevel@tonic-gate ** exc -- exception object. 448*7c478bd9Sstevel@tonic-gate ** 449*7c478bd9Sstevel@tonic-gate ** Returns: 450*7c478bd9Sstevel@tonic-gate ** none. 451*7c478bd9Sstevel@tonic-gate */ 452*7c478bd9Sstevel@tonic-gate 453*7c478bd9Sstevel@tonic-gate void 454*7c478bd9Sstevel@tonic-gate sm_exc_free(exc) 455*7c478bd9Sstevel@tonic-gate SM_EXC_T *exc; 456*7c478bd9Sstevel@tonic-gate { 457*7c478bd9Sstevel@tonic-gate if (exc == NULL) 458*7c478bd9Sstevel@tonic-gate return; 459*7c478bd9Sstevel@tonic-gate SM_REQUIRE(exc->sm_magic == SmExcMagic); 460*7c478bd9Sstevel@tonic-gate if (exc->exc_refcount == 0) 461*7c478bd9Sstevel@tonic-gate return; 462*7c478bd9Sstevel@tonic-gate if (--exc->exc_refcount == 0) 463*7c478bd9Sstevel@tonic-gate { 464*7c478bd9Sstevel@tonic-gate int i, c; 465*7c478bd9Sstevel@tonic-gate 466*7c478bd9Sstevel@tonic-gate for (i = 0; (c = exc->exc_type->etype_argformat[i]) != '\0'; 467*7c478bd9Sstevel@tonic-gate ++i) 468*7c478bd9Sstevel@tonic-gate { 469*7c478bd9Sstevel@tonic-gate switch (c) 470*7c478bd9Sstevel@tonic-gate { 471*7c478bd9Sstevel@tonic-gate case 's': 472*7c478bd9Sstevel@tonic-gate case 'r': 473*7c478bd9Sstevel@tonic-gate sm_free(exc->exc_argv[i].v_str); 474*7c478bd9Sstevel@tonic-gate break; 475*7c478bd9Sstevel@tonic-gate case 'e': 476*7c478bd9Sstevel@tonic-gate sm_exc_free(exc->exc_argv[i].v_exc); 477*7c478bd9Sstevel@tonic-gate break; 478*7c478bd9Sstevel@tonic-gate } 479*7c478bd9Sstevel@tonic-gate } 480*7c478bd9Sstevel@tonic-gate exc->sm_magic = NULL; 481*7c478bd9Sstevel@tonic-gate sm_free(exc->exc_argv); 482*7c478bd9Sstevel@tonic-gate sm_free(exc); 483*7c478bd9Sstevel@tonic-gate } 484*7c478bd9Sstevel@tonic-gate } 485*7c478bd9Sstevel@tonic-gate 486*7c478bd9Sstevel@tonic-gate /* 487*7c478bd9Sstevel@tonic-gate ** SM_EXC_MATCH -- Match exception category against a glob pattern. 488*7c478bd9Sstevel@tonic-gate ** 489*7c478bd9Sstevel@tonic-gate ** Parameters: 490*7c478bd9Sstevel@tonic-gate ** exc -- exception. 491*7c478bd9Sstevel@tonic-gate ** pattern -- glob pattern. 492*7c478bd9Sstevel@tonic-gate ** 493*7c478bd9Sstevel@tonic-gate ** Returns: 494*7c478bd9Sstevel@tonic-gate ** true iff match. 495*7c478bd9Sstevel@tonic-gate */ 496*7c478bd9Sstevel@tonic-gate 497*7c478bd9Sstevel@tonic-gate bool 498*7c478bd9Sstevel@tonic-gate sm_exc_match(exc, pattern) 499*7c478bd9Sstevel@tonic-gate SM_EXC_T *exc; 500*7c478bd9Sstevel@tonic-gate const char *pattern; 501*7c478bd9Sstevel@tonic-gate { 502*7c478bd9Sstevel@tonic-gate if (exc == NULL) 503*7c478bd9Sstevel@tonic-gate return false; 504*7c478bd9Sstevel@tonic-gate SM_REQUIRE(exc->sm_magic == SmExcMagic); 505*7c478bd9Sstevel@tonic-gate return sm_match(exc->exc_type->etype_category, pattern); 506*7c478bd9Sstevel@tonic-gate } 507*7c478bd9Sstevel@tonic-gate 508*7c478bd9Sstevel@tonic-gate /* 509*7c478bd9Sstevel@tonic-gate ** SM_EXC_WRITE -- Write exception message to a stream (wo trailing newline). 510*7c478bd9Sstevel@tonic-gate ** 511*7c478bd9Sstevel@tonic-gate ** Parameters: 512*7c478bd9Sstevel@tonic-gate ** exc -- exception. 513*7c478bd9Sstevel@tonic-gate ** stream -- file for output. 514*7c478bd9Sstevel@tonic-gate ** 515*7c478bd9Sstevel@tonic-gate ** Returns: 516*7c478bd9Sstevel@tonic-gate ** none. 517*7c478bd9Sstevel@tonic-gate */ 518*7c478bd9Sstevel@tonic-gate 519*7c478bd9Sstevel@tonic-gate void 520*7c478bd9Sstevel@tonic-gate sm_exc_write(exc, stream) 521*7c478bd9Sstevel@tonic-gate SM_EXC_T *exc; 522*7c478bd9Sstevel@tonic-gate SM_FILE_T *stream; 523*7c478bd9Sstevel@tonic-gate { 524*7c478bd9Sstevel@tonic-gate SM_REQUIRE_ISA(exc, SmExcMagic); 525*7c478bd9Sstevel@tonic-gate exc->exc_type->etype_print(exc, stream); 526*7c478bd9Sstevel@tonic-gate } 527*7c478bd9Sstevel@tonic-gate 528*7c478bd9Sstevel@tonic-gate /* 529*7c478bd9Sstevel@tonic-gate ** SM_EXC_PRINT -- Print exception message to a stream (with trailing newline). 530*7c478bd9Sstevel@tonic-gate ** 531*7c478bd9Sstevel@tonic-gate ** Parameters: 532*7c478bd9Sstevel@tonic-gate ** exc -- exception. 533*7c478bd9Sstevel@tonic-gate ** stream -- file for output. 534*7c478bd9Sstevel@tonic-gate ** 535*7c478bd9Sstevel@tonic-gate ** Returns: 536*7c478bd9Sstevel@tonic-gate ** none. 537*7c478bd9Sstevel@tonic-gate */ 538*7c478bd9Sstevel@tonic-gate 539*7c478bd9Sstevel@tonic-gate void 540*7c478bd9Sstevel@tonic-gate sm_exc_print(exc, stream) 541*7c478bd9Sstevel@tonic-gate SM_EXC_T *exc; 542*7c478bd9Sstevel@tonic-gate SM_FILE_T *stream; 543*7c478bd9Sstevel@tonic-gate { 544*7c478bd9Sstevel@tonic-gate SM_REQUIRE_ISA(exc, SmExcMagic); 545*7c478bd9Sstevel@tonic-gate exc->exc_type->etype_print(exc, stream); 546*7c478bd9Sstevel@tonic-gate (void) sm_io_putc(stream, SM_TIME_DEFAULT, '\n'); 547*7c478bd9Sstevel@tonic-gate } 548*7c478bd9Sstevel@tonic-gate 549*7c478bd9Sstevel@tonic-gate SM_EXC_HANDLER_T *SmExcHandler = NULL; 550*7c478bd9Sstevel@tonic-gate static SM_EXC_DEFAULT_HANDLER_T SmExcDefaultHandler = NULL; 551*7c478bd9Sstevel@tonic-gate 552*7c478bd9Sstevel@tonic-gate /* 553*7c478bd9Sstevel@tonic-gate ** SM_EXC_NEWTHREAD -- Initialize exception handling for new process/thread. 554*7c478bd9Sstevel@tonic-gate ** 555*7c478bd9Sstevel@tonic-gate ** Parameters: 556*7c478bd9Sstevel@tonic-gate ** h -- default exception handler. 557*7c478bd9Sstevel@tonic-gate ** 558*7c478bd9Sstevel@tonic-gate ** Returns: 559*7c478bd9Sstevel@tonic-gate ** none. 560*7c478bd9Sstevel@tonic-gate */ 561*7c478bd9Sstevel@tonic-gate 562*7c478bd9Sstevel@tonic-gate /* 563*7c478bd9Sstevel@tonic-gate ** Initialize a new process or a new thread by clearing the 564*7c478bd9Sstevel@tonic-gate ** exception handler stack and optionally setting a default 565*7c478bd9Sstevel@tonic-gate ** exception handler function. Call this at the beginning of main, 566*7c478bd9Sstevel@tonic-gate ** or in a new process after calling fork, or in a new thread. 567*7c478bd9Sstevel@tonic-gate ** 568*7c478bd9Sstevel@tonic-gate ** This function is a luxury, not a necessity. 569*7c478bd9Sstevel@tonic-gate ** If h != NULL then you can get the same effect by 570*7c478bd9Sstevel@tonic-gate ** wrapping the body of main, or the body of a forked child 571*7c478bd9Sstevel@tonic-gate ** or a new thread in SM_TRY ... SM_EXCEPT(e,"*") h(e); SM_END_TRY. 572*7c478bd9Sstevel@tonic-gate */ 573*7c478bd9Sstevel@tonic-gate 574*7c478bd9Sstevel@tonic-gate void 575*7c478bd9Sstevel@tonic-gate sm_exc_newthread(h) 576*7c478bd9Sstevel@tonic-gate SM_EXC_DEFAULT_HANDLER_T h; 577*7c478bd9Sstevel@tonic-gate { 578*7c478bd9Sstevel@tonic-gate SmExcHandler = NULL; 579*7c478bd9Sstevel@tonic-gate SmExcDefaultHandler = h; 580*7c478bd9Sstevel@tonic-gate } 581*7c478bd9Sstevel@tonic-gate 582*7c478bd9Sstevel@tonic-gate /* 583*7c478bd9Sstevel@tonic-gate ** SM_EXC_RAISE_X -- Raise an exception. 584*7c478bd9Sstevel@tonic-gate ** 585*7c478bd9Sstevel@tonic-gate ** Parameters: 586*7c478bd9Sstevel@tonic-gate ** exc -- exception. 587*7c478bd9Sstevel@tonic-gate ** 588*7c478bd9Sstevel@tonic-gate ** Returns: 589*7c478bd9Sstevel@tonic-gate ** doesn't. 590*7c478bd9Sstevel@tonic-gate */ 591*7c478bd9Sstevel@tonic-gate 592*7c478bd9Sstevel@tonic-gate void SM_DEAD_D 593*7c478bd9Sstevel@tonic-gate sm_exc_raise_x(exc) 594*7c478bd9Sstevel@tonic-gate SM_EXC_T *exc; 595*7c478bd9Sstevel@tonic-gate { 596*7c478bd9Sstevel@tonic-gate SM_REQUIRE_ISA(exc, SmExcMagic); 597*7c478bd9Sstevel@tonic-gate 598*7c478bd9Sstevel@tonic-gate if (SmExcHandler == NULL) 599*7c478bd9Sstevel@tonic-gate { 600*7c478bd9Sstevel@tonic-gate if (SmExcDefaultHandler != NULL) 601*7c478bd9Sstevel@tonic-gate { 602*7c478bd9Sstevel@tonic-gate SM_EXC_DEFAULT_HANDLER_T h; 603*7c478bd9Sstevel@tonic-gate 604*7c478bd9Sstevel@tonic-gate /* 605*7c478bd9Sstevel@tonic-gate ** If defined, the default handler is expected 606*7c478bd9Sstevel@tonic-gate ** to terminate the current thread of execution 607*7c478bd9Sstevel@tonic-gate ** using exit() or pthread_exit(). 608*7c478bd9Sstevel@tonic-gate ** If it instead returns normally, then we fall 609*7c478bd9Sstevel@tonic-gate ** through to the default case below. If it 610*7c478bd9Sstevel@tonic-gate ** raises an exception, then sm_exc_raise_x is 611*7c478bd9Sstevel@tonic-gate ** re-entered and, because we set SmExcDefaultHandler 612*7c478bd9Sstevel@tonic-gate ** to NULL before invoking h, we will again 613*7c478bd9Sstevel@tonic-gate ** end up in the default case below. 614*7c478bd9Sstevel@tonic-gate */ 615*7c478bd9Sstevel@tonic-gate 616*7c478bd9Sstevel@tonic-gate h = SmExcDefaultHandler; 617*7c478bd9Sstevel@tonic-gate SmExcDefaultHandler = NULL; 618*7c478bd9Sstevel@tonic-gate (*h)(exc); 619*7c478bd9Sstevel@tonic-gate } 620*7c478bd9Sstevel@tonic-gate 621*7c478bd9Sstevel@tonic-gate /* 622*7c478bd9Sstevel@tonic-gate ** No exception handler, so print the error and exit. 623*7c478bd9Sstevel@tonic-gate ** To override this behaviour on a program wide basis, 624*7c478bd9Sstevel@tonic-gate ** call sm_exc_newthread or put an exception handler in main(). 625*7c478bd9Sstevel@tonic-gate ** 626*7c478bd9Sstevel@tonic-gate ** XXX TODO: map the exception category to an exit code 627*7c478bd9Sstevel@tonic-gate ** XXX from <sysexits.h>. 628*7c478bd9Sstevel@tonic-gate */ 629*7c478bd9Sstevel@tonic-gate 630*7c478bd9Sstevel@tonic-gate sm_exc_print(exc, smioerr); 631*7c478bd9Sstevel@tonic-gate exit(255); 632*7c478bd9Sstevel@tonic-gate } 633*7c478bd9Sstevel@tonic-gate 634*7c478bd9Sstevel@tonic-gate if (SmExcHandler->eh_value == NULL) 635*7c478bd9Sstevel@tonic-gate SmExcHandler->eh_value = exc; 636*7c478bd9Sstevel@tonic-gate else 637*7c478bd9Sstevel@tonic-gate sm_exc_free(exc); 638*7c478bd9Sstevel@tonic-gate 639*7c478bd9Sstevel@tonic-gate sm_longjmp_nosig(SmExcHandler->eh_context, 1); 640*7c478bd9Sstevel@tonic-gate } 641*7c478bd9Sstevel@tonic-gate 642*7c478bd9Sstevel@tonic-gate /* 643*7c478bd9Sstevel@tonic-gate ** SM_EXC_RAISENEW_X -- shorthand for sm_exc_raise_x(sm_exc_new_x(...)) 644*7c478bd9Sstevel@tonic-gate ** 645*7c478bd9Sstevel@tonic-gate ** Parameters: 646*7c478bd9Sstevel@tonic-gate ** etype -- type of exception. 647*7c478bd9Sstevel@tonic-gate ** ap -- varargs. 648*7c478bd9Sstevel@tonic-gate ** 649*7c478bd9Sstevel@tonic-gate ** Returns: 650*7c478bd9Sstevel@tonic-gate ** none. 651*7c478bd9Sstevel@tonic-gate */ 652*7c478bd9Sstevel@tonic-gate 653*7c478bd9Sstevel@tonic-gate void SM_DEAD_D 654*7c478bd9Sstevel@tonic-gate #if SM_VA_STD 655*7c478bd9Sstevel@tonic-gate sm_exc_raisenew_x( 656*7c478bd9Sstevel@tonic-gate const SM_EXC_TYPE_T *etype, 657*7c478bd9Sstevel@tonic-gate ...) 658*7c478bd9Sstevel@tonic-gate #else 659*7c478bd9Sstevel@tonic-gate sm_exc_raisenew_x(etype, va_alist) 660*7c478bd9Sstevel@tonic-gate const SM_EXC_TYPE_T *etype; 661*7c478bd9Sstevel@tonic-gate va_dcl 662*7c478bd9Sstevel@tonic-gate #endif 663*7c478bd9Sstevel@tonic-gate { 664*7c478bd9Sstevel@tonic-gate SM_EXC_T *exc; 665*7c478bd9Sstevel@tonic-gate SM_VA_LOCAL_DECL 666*7c478bd9Sstevel@tonic-gate 667*7c478bd9Sstevel@tonic-gate SM_VA_START(ap, etype); 668*7c478bd9Sstevel@tonic-gate exc = sm_exc_vnew_x(etype, ap); 669*7c478bd9Sstevel@tonic-gate SM_VA_END(ap); 670*7c478bd9Sstevel@tonic-gate sm_exc_raise_x(exc); 671*7c478bd9Sstevel@tonic-gate } 672