xref: /titanic_50/usr/src/lib/libast/common/misc/error.c (revision 677fd05c3b05c78948501f6ffdced37dab9368fe)
1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 1985-2009 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