1 /***********************************************************************
2 * *
3 * This software is part of the ast package *
4 * Copyright (c) 1985-2010 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
setopt(void * a,const void * p,register int n,register const char * v)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
print(register Sfio_t * sp,register char * name,char * delim)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
context(register Sfio_t * sp,register Error_context_t * cp)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
error_break(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
error(int level,...)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
errorv(const char * id,int level,va_list ap)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*
errorctx(Error_info_t * p,int op,int flags)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