1 /*********************************************************************** 2 * * 3 * This software is part of the ast package * 4 * Copyright (c) 1985-2008 AT&T Intellectual Property * 5 * and is licensed under the * 6 * Common Public License, Version 1.0 * 7 * by AT&T Intellectual Property * 8 * * 9 * A copy of the License is available at * 10 * http://www.opensource.org/licenses/cpl1.0.txt * 11 * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * 12 * * 13 * Information and Software Systems Research * 14 * AT&T Research * 15 * Florham Park NJ * 16 * * 17 * Glenn Fowler <gsf@research.att.com> * 18 * David Korn <dgk@research.att.com> * 19 * Phong Vo <kpv@research.att.com> * 20 * * 21 ***********************************************************************/ 22 #pragma prototyped 23 /* 24 * Glenn Fowler 25 * AT&T Research 26 * 27 * error and message formatter 28 * 29 * level is the error level 30 * level >= error_info.core!=0 dumps core 31 * level >= ERROR_FATAL calls error_info.exit 32 * level < 0 is for debug tracing 33 * 34 * NOTE: id && ERROR_NOID && !ERROR_USAGE implies format=id for errmsg() 35 */ 36 37 #include "lclib.h" 38 39 #include <ctype.h> 40 #include <ccode.h> 41 #include <namval.h> 42 #include <sig.h> 43 #include <stk.h> 44 #include <times.h> 45 #include <regex.h> 46 47 /* 48 * 2007-03-19 move error_info from _error_info_ to (*_error_infop_) 49 * to allow future Error_info_t growth 50 * by 2009 _error_info_ can be static 51 */ 52 53 #if _BLD_ast && defined(__EXPORT__) 54 #define extern extern __EXPORT__ 55 #endif 56 57 extern Error_info_t _error_info_; 58 59 Error_info_t _error_info_ = 60 { 61 2, exit, write, 62 0,0,0,0,0,0,0,0, 63 0, /* version */ 64 0, /* auxilliary */ 65 0,0,0,0,0,0,0, /* top of old context stack */ 66 0,0,0,0,0,0,0, /* old empty context */ 67 0, /* time */ 68 translate, 69 0 /* catalog */ 70 }; 71 72 #undef extern 73 74 __EXTERN__(Error_info_t, _error_info_); 75 76 __EXTERN__(Error_info_t*, _error_infop_); 77 78 Error_info_t* _error_infop_ = &_error_info_; 79 80 /* 81 * these should probably be in error_info 82 */ 83 84 static struct State_s 85 { 86 char* prefix; 87 Sfio_t* tty; 88 unsigned long count; 89 int breakpoint; 90 regex_t* match; 91 } error_state; 92 93 #undef ERROR_CATALOG 94 #define ERROR_CATALOG (ERROR_LIBRARY<<1) 95 96 #define OPT_BREAK 1 97 #define OPT_CATALOG 2 98 #define OPT_CORE 3 99 #define OPT_COUNT 4 100 #define OPT_FD 5 101 #define OPT_LIBRARY 6 102 #define OPT_MASK 7 103 #define OPT_MATCH 8 104 #define OPT_PREFIX 9 105 #define OPT_SYSTEM 10 106 #define OPT_TIME 11 107 #define OPT_TRACE 12 108 109 static const Namval_t options[] = 110 { 111 "break", OPT_BREAK, 112 "catalog", OPT_CATALOG, 113 "core", OPT_CORE, 114 "count", OPT_COUNT, 115 "debug", OPT_TRACE, 116 "fd", OPT_FD, 117 "library", OPT_LIBRARY, 118 "mask", OPT_MASK, 119 "match", OPT_MATCH, 120 "prefix", OPT_PREFIX, 121 "system", OPT_SYSTEM, 122 "time", OPT_TIME, 123 "trace", OPT_TRACE, 124 0, 0 125 }; 126 127 /* 128 * called by stropt() to set options 129 */ 130 131 static int 132 setopt(void* a, const void* p, register int n, register const char* v) 133 { 134 NoP(a); 135 if (p) 136 switch (((Namval_t*)p)->value) 137 { 138 case OPT_BREAK: 139 case OPT_CORE: 140 if (n) 141 switch (*v) 142 { 143 case 'e': 144 case 'E': 145 error_state.breakpoint = ERROR_ERROR; 146 break; 147 case 'f': 148 case 'F': 149 error_state.breakpoint = ERROR_FATAL; 150 break; 151 case 'p': 152 case 'P': 153 error_state.breakpoint = ERROR_PANIC; 154 break; 155 default: 156 error_state.breakpoint = strtol(v, NiL, 0); 157 break; 158 } 159 else 160 error_state.breakpoint = 0; 161 if (((Namval_t*)p)->value == OPT_CORE) 162 error_info.core = error_state.breakpoint; 163 break; 164 case OPT_CATALOG: 165 if (n) 166 error_info.set |= ERROR_CATALOG; 167 else 168 error_info.clear |= ERROR_CATALOG; 169 break; 170 case OPT_COUNT: 171 if (n) 172 error_state.count = strtol(v, NiL, 0); 173 else 174 error_state.count = 0; 175 break; 176 case OPT_FD: 177 error_info.fd = n ? strtol(v, NiL, 0) : -1; 178 break; 179 case OPT_LIBRARY: 180 if (n) 181 error_info.set |= ERROR_LIBRARY; 182 else 183 error_info.clear |= ERROR_LIBRARY; 184 break; 185 case OPT_MASK: 186 if (n) 187 error_info.mask = strtol(v, NiL, 0); 188 else 189 error_info.mask = 0; 190 break; 191 case OPT_MATCH: 192 if (error_state.match) 193 regfree(error_state.match); 194 if (n) 195 { 196 if ((error_state.match || (error_state.match = newof(0, regex_t, 1, 0))) && regcomp(error_state.match, v, REG_EXTENDED|REG_LENIENT)) 197 { 198 free(error_state.match); 199 error_state.match = 0; 200 } 201 } 202 else if (error_state.match) 203 { 204 free(error_state.match); 205 error_state.match = 0; 206 } 207 break; 208 case OPT_PREFIX: 209 if (n) 210 error_state.prefix = strdup(v); 211 else if (error_state.prefix) 212 { 213 free(error_state.prefix); 214 error_state.prefix = 0; 215 } 216 break; 217 case OPT_SYSTEM: 218 if (n) 219 error_info.set |= ERROR_SYSTEM; 220 else 221 error_info.clear |= ERROR_SYSTEM; 222 break; 223 case OPT_TIME: 224 error_info.time = n ? 1 : 0; 225 break; 226 case OPT_TRACE: 227 if (n) 228 error_info.trace = -strtol(v, NiL, 0); 229 else 230 error_info.trace = 0; 231 break; 232 } 233 return 0; 234 } 235 236 /* 237 * print a name with optional delimiter, converting unprintable chars 238 */ 239 240 static void 241 print(register Sfio_t* sp, register char* name, char* delim) 242 { 243 if (mbwide()) 244 sfputr(sp, name, -1); 245 else 246 { 247 #if CC_NATIVE != CC_ASCII 248 register int c; 249 register unsigned char* n2a; 250 register unsigned char* a2n; 251 register int aa; 252 register int as; 253 254 n2a = ccmap(CC_NATIVE, CC_ASCII); 255 a2n = ccmap(CC_ASCII, CC_NATIVE); 256 aa = n2a['A']; 257 as = n2a[' ']; 258 while (c = *name++) 259 { 260 c = n2a[c]; 261 if (c & 0200) 262 { 263 c &= 0177; 264 sfputc(sp, '?'); 265 } 266 if (c < as) 267 { 268 c += aa - 1; 269 sfputc(sp, '^'); 270 } 271 c = a2n[c]; 272 sfputc(sp, c); 273 } 274 #else 275 register int c; 276 277 while (c = *name++) 278 { 279 if (c & 0200) 280 { 281 c &= 0177; 282 sfputc(sp, '?'); 283 } 284 if (c < ' ') 285 { 286 c += 'A' - 1; 287 sfputc(sp, '^'); 288 } 289 sfputc(sp, c); 290 } 291 #endif 292 } 293 if (delim) 294 sfputr(sp, delim, -1); 295 } 296 297 /* 298 * print error context FIFO stack 299 */ 300 301 #define CONTEXT(f,p) (((f)&ERROR_PUSH)?((Error_context_t*)&(p)->context->context):((Error_context_t*)(p))) 302 303 static void 304 context(register Sfio_t* sp, register Error_context_t* cp) 305 { 306 if (cp->context) 307 context(sp, CONTEXT(cp->flags, cp->context)); 308 if (!(cp->flags & ERROR_SILENT)) 309 { 310 if (cp->id) 311 print(sp, cp->id, NiL); 312 if (cp->line > ((cp->flags & ERROR_INTERACTIVE) != 0)) 313 { 314 if (cp->file) 315 sfprintf(sp, ": \"%s\", %s %d", cp->file, ERROR_translate(NiL, NiL, ast.id, "line"), cp->line); 316 else 317 sfprintf(sp, "[%d]", cp->line); 318 } 319 sfputr(sp, ": ", -1); 320 } 321 } 322 323 /* 324 * debugging breakpoint 325 */ 326 327 extern void 328 error_break(void) 329 { 330 char* s; 331 332 if (error_state.tty || (error_state.tty = sfopen(NiL, "/dev/tty", "r+"))) 333 { 334 sfprintf(error_state.tty, "error breakpoint: "); 335 if (s = sfgetr(error_state.tty, '\n', 1)) 336 { 337 if (streq(s, "q") || streq(s, "quit")) 338 exit(0); 339 stropt(s, options, sizeof(*options), setopt, NiL); 340 } 341 } 342 } 343 344 void 345 error(int level, ...) 346 { 347 va_list ap; 348 349 va_start(ap, level); 350 errorv(NiL, level, ap); 351 va_end(ap); 352 } 353 354 void 355 errorv(const char* id, int level, va_list ap) 356 { 357 register int n; 358 int fd; 359 int flags; 360 char* s; 361 char* t; 362 char* format; 363 char* library; 364 const char* catalog; 365 366 int line; 367 char* file; 368 369 #if !_PACKAGE_astsa 370 unsigned long d; 371 struct tms us; 372 #endif 373 374 if (!error_info.init) 375 { 376 error_info.init = 1; 377 stropt(getenv("ERROR_OPTIONS"), options, sizeof(*options), setopt, NiL); 378 } 379 if (level > 0) 380 { 381 flags = level & ~ERROR_LEVEL; 382 level &= ERROR_LEVEL; 383 } 384 else 385 flags = 0; 386 if ((flags & (ERROR_USAGE|ERROR_NOID)) == ERROR_NOID) 387 { 388 format = (char*)id; 389 id = 0; 390 } 391 else 392 format = 0; 393 if (id) 394 { 395 catalog = (char*)id; 396 if (!*catalog || *catalog == ':') 397 { 398 catalog = 0; 399 library = 0; 400 } 401 else if ((library = strchr(catalog, ':')) && !*++library) 402 library = 0; 403 } 404 else 405 { 406 catalog = 0; 407 library = 0; 408 } 409 if (catalog) 410 id = 0; 411 else 412 { 413 id = (const char*)error_info.id; 414 catalog = error_info.catalog; 415 } 416 if (level < error_info.trace || (flags & ERROR_LIBRARY) && !(((error_info.set | error_info.flags) ^ error_info.clear) & ERROR_LIBRARY) || level < 0 && error_info.mask && !(error_info.mask & (1<<(-level - 1)))) 417 { 418 if (level >= ERROR_FATAL) 419 (*error_info.exit)(level - 1); 420 return; 421 } 422 if (error_info.trace < 0) 423 flags |= ERROR_LIBRARY|ERROR_SYSTEM; 424 flags |= error_info.set | error_info.flags; 425 flags &= ~error_info.clear; 426 if (!library) 427 flags &= ~ERROR_LIBRARY; 428 fd = (flags & ERROR_OUTPUT) ? va_arg(ap, int) : error_info.fd; 429 if (error_info.write) 430 { 431 long off; 432 char* bas; 433 434 bas = stkptr(stkstd, 0); 435 if (off = stktell(stkstd)) 436 stkfreeze(stkstd, 0); 437 file = error_info.id; 438 if (error_state.prefix) 439 sfprintf(stkstd, "%s: ", error_state.prefix); 440 if (flags & ERROR_USAGE) 441 { 442 if (flags & ERROR_NOID) 443 sfprintf(stkstd, " "); 444 else 445 sfprintf(stkstd, "%s: ", ERROR_translate(NiL, NiL, ast.id, "Usage")); 446 if (file || opt_info.argv && (file = opt_info.argv[0])) 447 print(stkstd, file, " "); 448 } 449 else 450 { 451 if (level && !(flags & ERROR_NOID)) 452 { 453 if (error_info.context && level > 0) 454 context(stkstd, CONTEXT(error_info.flags, error_info.context)); 455 if (file) 456 print(stkstd, file, (flags & ERROR_LIBRARY) ? " " : ": "); 457 if (flags & (ERROR_CATALOG|ERROR_LIBRARY)) 458 { 459 sfprintf(stkstd, "["); 460 if (flags & ERROR_CATALOG) 461 sfprintf(stkstd, "%s %s%s", 462 catalog ? catalog : ERROR_translate(NiL, NiL, ast.id, "DEFAULT"), 463 ERROR_translate(NiL, NiL, ast.id, "catalog"), 464 (flags & ERROR_LIBRARY) ? ", " : ""); 465 if (flags & ERROR_LIBRARY) 466 sfprintf(stkstd, "%s %s", 467 library, 468 ERROR_translate(NiL, NiL, ast.id, "library")); 469 sfprintf(stkstd, "]: "); 470 } 471 } 472 if (level > 0 && error_info.line > ((flags & ERROR_INTERACTIVE) != 0)) 473 { 474 if (error_info.file && *error_info.file) 475 sfprintf(stkstd, "\"%s\", ", error_info.file); 476 sfprintf(stkstd, "%s %d: ", ERROR_translate(NiL, NiL, ast.id, "line"), error_info.line); 477 } 478 } 479 #if !_PACKAGE_astsa 480 if (error_info.time) 481 { 482 if ((d = times(&us)) < error_info.time || error_info.time == 1) 483 error_info.time = d; 484 sfprintf(stkstd, " %05lu.%05lu.%05lu ", d - error_info.time, (unsigned long)us.tms_utime, (unsigned long)us.tms_stime); 485 } 486 #endif 487 switch (level) 488 { 489 case 0: 490 flags &= ~ERROR_SYSTEM; 491 break; 492 case ERROR_WARNING: 493 sfprintf(stkstd, "%s: ", ERROR_translate(NiL, NiL, ast.id, "warning")); 494 break; 495 case ERROR_PANIC: 496 sfprintf(stkstd, "%s: ", ERROR_translate(NiL, NiL, ast.id, "panic")); 497 break; 498 default: 499 if (level < 0) 500 { 501 s = ERROR_translate(NiL, NiL, ast.id, "debug"); 502 if (error_info.trace < -1) 503 sfprintf(stkstd, "%s%d:%s", s, level, level > -10 ? " " : ""); 504 else 505 sfprintf(stkstd, "%s: ", s); 506 for (n = 0; n < error_info.indent; n++) 507 { 508 sfputc(stkstd, ' '); 509 sfputc(stkstd, ' '); 510 } 511 } 512 break; 513 } 514 if (flags & ERROR_SOURCE) 515 { 516 /* 517 * source ([version], file, line) message 518 */ 519 520 file = va_arg(ap, char*); 521 line = va_arg(ap, int); 522 s = ERROR_translate(NiL, NiL, ast.id, "line"); 523 if (error_info.version) 524 sfprintf(stkstd, "(%s: \"%s\", %s %d) ", error_info.version, file, s, line); 525 else 526 sfprintf(stkstd, "(\"%s\", %s %d) ", file, s, line); 527 } 528 if (format || (format = va_arg(ap, char*))) 529 { 530 if (!(flags & ERROR_USAGE)) 531 format = ERROR_translate(NiL, id, catalog, format); 532 sfvprintf(stkstd, format, ap); 533 } 534 if (!(flags & ERROR_PROMPT)) 535 { 536 /* 537 * level&ERROR_OUTPUT on return means message 538 * already output 539 */ 540 541 if ((flags & ERROR_SYSTEM) && errno && errno != error_info.last_errno) 542 { 543 sfprintf(stkstd, " [%s]", fmterror(errno)); 544 if (error_info.set & ERROR_SYSTEM) 545 errno = 0; 546 error_info.last_errno = (level >= 0) ? 0 : errno; 547 } 548 if (error_info.auxilliary && level >= 0) 549 level = (*error_info.auxilliary)(stkstd, level, flags); 550 sfputc(stkstd, '\n'); 551 } 552 if (level > 0) 553 { 554 if ((level & ~ERROR_OUTPUT) > 1) 555 error_info.errors++; 556 else 557 error_info.warnings++; 558 } 559 if (level < 0 || !(level & ERROR_OUTPUT)) 560 { 561 n = stktell(stkstd); 562 s = stkptr(stkstd, 0); 563 if (t = memchr(s, '\f', n)) 564 { 565 n -= ++t - s; 566 s = t; 567 } 568 #if HUH_19980401 /* nasty problems if sfgetr() is in effect! */ 569 sfsync(sfstdin); 570 #endif 571 sfsync(sfstdout); 572 sfsync(sfstderr); 573 if (fd == sffileno(sfstderr) && error_info.write == write) 574 { 575 sfwrite(sfstderr, s, n); 576 sfsync(sfstderr); 577 } 578 else 579 (*error_info.write)(fd, s, n); 580 } 581 else 582 { 583 s = 0; 584 level &= ERROR_LEVEL; 585 } 586 stkset(stkstd, bas, off); 587 } 588 else 589 s = 0; 590 if (level >= error_state.breakpoint && error_state.breakpoint && (!error_state.match || !regexec(error_state.match, s ? s : format, 0, NiL, 0)) && (!error_state.count || !--error_state.count)) 591 { 592 if (error_info.core) 593 { 594 #ifndef SIGABRT 595 #ifdef SIGQUIT 596 #define SIGABRT SIGQUIT 597 #else 598 #ifdef SIGIOT 599 #define SIGABRT SIGIOT 600 #endif 601 #endif 602 #endif 603 #ifdef SIGABRT 604 signal(SIGABRT, SIG_DFL); 605 kill(getpid(), SIGABRT); 606 pause(); 607 #else 608 abort(); 609 #endif 610 } 611 else 612 error_break(); 613 } 614 if (level >= ERROR_FATAL) 615 (*error_info.exit)(level - ERROR_FATAL + 1); 616 } 617 618 /* 619 * error_info context control 620 */ 621 622 static Error_info_t* freecontext; 623 624 Error_info_t* 625 errorctx(Error_info_t* p, int op, int flags) 626 { 627 if (op & ERROR_POP) 628 { 629 if (!(_error_infop_ = p->context)) 630 _error_infop_ = &_error_info_; 631 if (op & ERROR_FREE) 632 { 633 p->context = freecontext; 634 freecontext = p; 635 } 636 p = _error_infop_; 637 } 638 else 639 { 640 if (!p) 641 { 642 if (p = freecontext) 643 freecontext = freecontext->context; 644 else if (!(p = newof(0, Error_info_t, 1, 0))) 645 return 0; 646 *p = *_error_infop_; 647 p->errors = p->flags = p->line = p->warnings = 0; 648 p->catalog = p->file = 0; 649 } 650 if (op & ERROR_PUSH) 651 { 652 p->flags = flags; 653 p->context = _error_infop_; 654 _error_infop_ = p; 655 } 656 p->flags |= ERROR_PUSH; 657 } 658 return p; 659 } 660