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