1 /* -*- tab-width: 4 -*-
2  *
3  * Electric(tm) VLSI Design System
4  *
5  * File: dberror.c
6  * Database error handler
7  * Written by: Steven M. Rubin, Static Free Software
8  *
9  * Copyright (c) 2000 Static Free Software.
10  *
11  * Electric(tm) is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * Electric(tm) is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with Electric(tm); see the file COPYING.  If not, write to
23  * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
24  * Boston, Mass 02111-1307, USA.
25  *
26  * Static Free Software
27  * 4119 Alpine Road
28  * Portola Valley, California 94028
29  * info@staticfreesoft.com
30  */
31 
32 #include "global.h"
33 #include "database.h"
34 #include "usr.h"
35 
36 #define	NEWERRORS 1		/* uncomment to remove newly added code */
37 
38 INTBIG  db_lasterror;			/* last error message */
39 BOOLEAN db_printerrors;			/* flag for printing internal errors */
40 
41 static CHAR *db_errortype[] =
42 {
43 	N_("no error"),								/* DBNOERROR */
44 	N_("no memory"),							/* DBNOMEM */
45 	N_("bad transposition"),					/* DBBADTRANS */
46 	N_("bad rotation"),							/* DBBADROT */
47 	N_("bad prototype"),						/* DBBADPROTO */
48 	N_("bad parent"),							/* DBBADPARENT */
49 	N_("invalid instance"),						/* DBBADINST */
50 	N_("invalid name"),							/* DBBADNAME */
51 	N_("bad width"),							/* DBBADWIDTH */
52 	N_("bad end A node/port"),					/* DBBADENDAN */
53 	N_("bad end B node/port"),					/* DBBADENDBN */
54 	N_("bad end A connection"),					/* DBBADENDAC */
55 	N_("bad end B connection"),					/* DBBADENDBC */
56 	N_("bad end A position"),					/* DBBADENDAP */
57 	N_("bad end B position"),					/* DBBADENDBP */
58 	N_("bad new width"),						/* DBBADNEWWID */
59 	N_("bad cell"), 							/* DBBADCELL */
60 	N_("bad library"),							/* DBBADLIB */
61 	N_("bad size"),								/* DBBADSIZE */
62 	N_("bad object type"),						/* DBBADOBJECT */
63 	N_("bad sub port"),							/* DBBADSUBPORT */
64 	N_("still has arcs"),						/* DBHASARCS */
65 	N_("still has ports"),						/* DBHASPORTS */
66 	N_("cell has instances"),					/* DBHASINSTANCES */
67 	N_("recursive call"),						/* DBRECURSIVE */
68 	N_("arc not in port"),						/* DBNOTINPORT */
69 	N_("conflicts with primitive"),				/* DBCONFLICT */
70 	N_("port mismatch"),						/* DBPORTMM */
71 	N_("duplicate name"),						/* DBDUPLICATE */
72 	N_("primitive prototype"),					/* DBPRIMITIVE */
73 	N_("bad transformation matrix"),			/* DBBADTMAT */
74 	N_("variable does not exist"),				/* DBNOVAR */
75 	N_("variable cannot be changed"),			/* DBVARFIXED */
76 	N_("variable cannot be displayable array"),	/* DBVARARRDIS */
77 	N_("this is the last technology"),			/* DBLASTECH */
78 	N_("technology is still in use"),			/* DBTECINUSE */
79 	N_("sliding not allowed")					/* DBNOSLIDING */
80 };
81 
82 /*
83  * database error routine codes
84  */
85 static CHAR *db_routines[] =
86 {
87 	x_("addstringtoinfstr"),	/*  1 */
88 	x_("addtechnology"),		/*  2 */
89 	x_("addtoinfstr"),			/*  3 */
90 	x_("allocarcinst"),			/*  4 */
91 	x_("allocgeom"),			/*  5 */
92 	x_("alloclibrary"),			/*  6 */
93 	x_("allocnodeinst"),		/*  7 */
94 	x_("allocnodeproto"),		/*  8 */
95 	x_("allocpolygon"),			/*  9 */
96 	x_("allocportarcinst"),		/* 10 */
97 	x_("allocportexpinst"),		/* 11 */
98 	x_("allocportproto"),		/* 12 */
99 	x_("allocstring"),			/* 13 */
100 	x_("alloctechnology"),		/* 14 */
101 	x_("allocview"),			/* 15 */
102 	x_("copynodeproto"),		/* 16 */
103 	x_("delind"),				/* 17 */
104 	x_("delindkey"),			/* 18 */
105 	x_("delvalkey"),			/* 19 */
106 	x_("describevariable"),		/* 20 */
107 	x_("extendpolygon"),		/* 21 */
108 	x_("initinfstr"),			/* 22 */
109 	x_("initobjlist"),			/* 23 */
110 	x_("insind"),				/* 24 */
111 	x_("insindkey"),			/* 25 */
112 	x_("killarcinst"),			/* 26 */
113 	x_("killlibrary"),			/* 27 */
114 	x_("killnodeinst"),			/* 28 */
115 	x_("killnodeproto"),		/* 29 */
116 	x_("killportproto"),		/* 30 */
117 	x_("killtechnology"),		/* 31 */
118 	x_("makekey"),				/* 32 */
119 	x_("modifyarcinst"),		/* 33 */
120 	x_("moveportproto"),		/* 34 */
121 	x_("newarcinst"),			/* 35 */
122 	x_("newlibrary"),			/* 36 */
123 	x_("newnodeinst"),			/* 37 */
124 	x_("newnodeproto"),			/* 38 */
125 	x_("newportproto"),			/* 39 */
126 	x_("newview"),				/* 40 */
127 	x_("replacearcinst"),		/* 41 */
128 	x_("replacenodeinst"),		/* 42 */
129 	x_("returninfstr"),			/* 43 */
130 	x_("setind"),				/* 44 */
131 	x_("setindkey"),			/* 45 */
132 	x_("setval"),				/* 46 */
133 	x_("setvalkey"),			/* 47 */
134 	x_("transmult"),			/* 48 */
135 	x_("xform"),				/* 49 */
136 };
137 
138 /*
139  * routine to report a database error
140  */
db_error(INTBIG code)141 INTBIG db_error(INTBIG code)
142 {
143 	db_lasterror = code;
144 	if (db_printerrors) telldatabaseerror();
145 	return(-1);
146 }
147 
148 /*
149  * routine to print an error message
150  */
telldatabaseerror(void)151 void telldatabaseerror(void)
152 {
153 	REGISTER INTBIG routine, error;
154 
155 	routine = (db_lasterror >> 16) - 1;
156 	error = db_lasterror & 0xFFFF;
157 	if (error == DBNOERROR) ttyputmsg(_("No error")); else
158 		ttyputmsg(x_("%s: %s"), db_routines[routine], TRANSLATE(db_errortype[error]));
159 }
160 
161 /******************** ERROR REPORTING ********************/
162 
163 /*
164  * These are the routines to log errors:
165  *   initerrorlogging(CHAR *s)                   initialize for subsystem "s"
166  *   e = logerror(CHAR *msg, NODEPROTO *f, k)    log message "msg" in cell "f", sort key "k"
167  *   addgeomtoerror(void *e, GEOM *g, BOOLEAN s, INTBIG l, NODEINST **p)
168  *                                               add geom "g" to error "e" (show if "s" nonzero)
169  *   addexporttoerror(void *e, PORTPROTO *p, BOOLEAN s)  add export "pp" to error "e"
170  *   addlinetoerror(void *e, x1, y1, x2, y2)     add line to error "e"
171  *   addpolytoerror(void *e, POLYGON *p)         add polygon to error "e"
172  *   addpointtoerror(void *e, x, y)              add point to error "e"
173  *
174  * To report errors, call:
175  *   termerrorlogging(explain)                   complete error accumulation
176  *   sorterrors()                                sort errors by key
177  *   n = numerrors()                             returns number of errors
178  *   s = reportnexterror(show, &g1, &g2)         report next error
179  *   s = reportpreverror()                       report previous error
180  *
181  * To obtain errors internally, call:
182  *   e = getnexterror(e, msg)                    gets error after "e" (0 is first error)
183  *   msg = describeerror(e)                      return description of error "e"
184  *   reporterror(e)                              reports error "e"
185  *   t = getnumerrorgeom(e)                      get number of geoms on error "e"
186  *   eg = geterrorgeom(e, i)                     get geom "i" on error "e"
187  *   msg = describeerrorgeom(eg)                 return description of geom "eg"
188  *   showerrorgeom(eg)                           highlight error geom "eg"
189  *   adviseofchanges(routine)                    call "routine" when errors change
190  */
191 
192 typedef enum {ERRORTYPEGEOM, ERRORTYPEEXPORT, ERRORTYPELINE, ERRORTYPEPOINT} ERRORHIGHLIGHTTYPE;
193 
194 #define NOERRORHIGHLIGHT ((ERRORHIGHLIGHT *)-1)
195 
196 typedef struct
197 {
198 	ERRORHIGHLIGHTTYPE type;
199 	GEOM              *geom;
200 	PORTPROTO         *pp;
201 	BOOLEAN            showgeom;
202 	INTBIG             x1, y1;
203 	INTBIG             x2, y2;
204 	INTBIG             pathlen;
205 	NODEINST         **path;
206 } ERRORHIGHLIGHT;
207 
208 
209 #define NOERRORLIST ((ERRORLIST *)-1)
210 
211 typedef struct iErrorList
212 {
213 	CHAR              *message;
214 	NODEPROTO         *cell;
215 	INTBIG             sortkey;
216 	INTBIG             index;
217 	INTBIG             numhighlights;
218 	ERRORHIGHLIGHT   **highlights;
219 	struct iErrorList *preverrorlist;
220 	struct iErrorList *nexterrorlist;
221 } ERRORLIST;
222 
223 static ERRORLIST *db_errorlistfree = NOERRORLIST;
224 static ERRORLIST *db_firsterrorlist = NOERRORLIST;
225 static ERRORLIST *db_curerrorlist = NOERRORLIST;
226 static ERRORLIST *db_nexterrorlist = NOERRORLIST;
227 static ERRORLIST *db_preverrorlist = NOERRORLIST;
228 
229 static CHAR       db_errorsystem[100] = {0};
230 static INTBIG     db_numerrors;
231 static INTBIG     db_truenumerrors;
232 static INTBIG     db_maxerrors = 0;
233 static void      *db_errorreportmutex = 0;			/* mutex for error reporting modules */
234 static void     (*db_errorchangedroutine)(void) = 0;
235 
236 static BOOLEAN db_addtoerrorlist(ERRORLIST *el, ERRORHIGHLIGHT *eh);
237 static int     db_errorlistascending(const void *e1, const void *e2);
238 static CHAR   *db_reportcurrenterror(INTBIG showhigh, GEOM **g1, GEOM **g2);
239 static void    db_clearerrorlist(void);
240 #ifdef	NEWERRORS
241 static BOOLEAN db_validatecell(NODEPROTO *cell);
242 #endif
243 
244 /*
245  * Routine to free memory associated with this module.
246  */
db_freeerrormemory(void)247 void db_freeerrormemory(void)
248 {
249 	REGISTER ERRORLIST *el;
250 
251 	/* ensure that the current error list is freed */
252 	db_clearerrorlist();
253 
254 	/* now purge the free list */
255 	while (db_errorlistfree != NOERRORLIST)
256 	{
257 		el = db_errorlistfree;
258 		db_errorlistfree = el->nexterrorlist;
259 		efree((CHAR *)el);
260 	}
261 }
262 
263 /*
264  * Routine to free all previously stored errors and initialize the system.
265  * The errors are described by "system" and up to two cells "cell1" and
266  * "cell2" (may be NONODEPROTO).
267  */
initerrorlogging(CHAR * system)268 void initerrorlogging(CHAR *system)
269 {
270 	REGISTER VARIABLE *var;
271 
272 	/* code cannot be called by multiple procesors: uses globals */
273 	NOT_REENTRANT;
274 
275 	db_clearerrorlist();
276 	db_curerrorlist = NOERRORLIST;
277 	db_nexterrorlist = NOERRORLIST;
278 	db_preverrorlist = NOERRORLIST;
279 	estrncpy(db_errorsystem, system, 100);
280 	db_numerrors = db_truenumerrors = 0;
281 	var = getval((INTBIG)us_tool, VTOOL, VINTEGER, x_("USER_maximum_errors"));
282 	if (var == NOVARIABLE) db_maxerrors = 0; else
283 		db_maxerrors = var->addr;
284 	if (ensurevalidmutex(&db_errorreportmutex, TRUE)) return;
285 }
286 
287 /*
288  * Routine to free all previously stored errors.
289  */
db_clearerrorlist(void)290 void db_clearerrorlist(void)
291 {
292 	REGISTER ERRORLIST *el;
293 	REGISTER ERRORHIGHLIGHT *eh;
294 	REGISTER INTBIG i;
295 
296 	/* code cannot be called by multiple procesors: uses globals */
297 	NOT_REENTRANT;
298 
299 	while (db_firsterrorlist != NOERRORLIST)
300 	{
301 		/* get the next error on the list */
302 		el = db_firsterrorlist;
303 		db_firsterrorlist = el->nexterrorlist;
304 
305 		/* clear memory in that module */
306 		efree(el->message);
307 		for(i=0; i<el->numhighlights; i++)
308 		{
309 			eh = el->highlights[i];
310 			if (eh->pathlen > 0) efree((CHAR *)eh->path);
311 			efree((CHAR *)eh);
312 		}
313 		if (el->numhighlights > 0) efree((CHAR *)el->highlights);
314 
315 		/* place the module on the free list */
316 		el->nexterrorlist = db_errorlistfree;
317 		db_errorlistfree = el;
318 	}
319 }
320 
321 /*
322  * Routine to create an error message with the text "message" applying to cell "cell".
323  * Returns a pointer to the message (0 on error) which can be used to add highlights.
324  */
logerror(CHAR * message,NODEPROTO * cell,INTBIG sortkey)325 void *logerror(CHAR *message, NODEPROTO *cell, INTBIG sortkey)
326 {
327 	REGISTER ERRORLIST *el;
328 
329 	/* if too many errors, don't save it */
330 	db_numerrors++;
331 	if (db_maxerrors > 0 && db_numerrors > db_maxerrors)
332 	{
333 		if (db_numerrors == db_maxerrors+1)
334 			ttyputmsg(_("WARNING: more than %ld errors found, ignoring the rest"),
335 				db_maxerrors);
336 		return(0);
337 	}
338 
339 	/* grab an error from the global free list (if any) */
340 	if (db_multiprocessing) emutexlock(db_errorreportmutex);
341 	el = NOERRORLIST;
342 	if (db_errorlistfree != NOERRORLIST)
343 	{
344 		el = db_errorlistfree;
345 		db_errorlistfree = el->nexterrorlist;
346 	}
347 	if (db_multiprocessing) emutexunlock(db_errorreportmutex);
348 
349 	/* if nothing on the list, allocate one */
350 	if (el == NOERRORLIST)
351 	{
352 		el = (ERRORLIST *)emalloc(sizeof (ERRORLIST), db_cluster);
353 		if (el == 0) return(0);
354 	}
355 
356 	/* store information about the error */
357 	if (allocstring(&el->message, message, db_cluster)) return(0);
358 	el->cell = cell;
359 	el->sortkey = sortkey;
360 	el->numhighlights = 0;
361 
362 	/* link the error into the global list */
363 	if (db_multiprocessing) emutexlock(db_errorreportmutex);
364 	el->preverrorlist = NOERRORLIST;
365 	el->nexterrorlist = db_firsterrorlist;
366 	if (db_firsterrorlist != NOERRORLIST) db_firsterrorlist->preverrorlist = el;
367 	db_firsterrorlist = el;
368 	if (db_multiprocessing) emutexunlock(db_errorreportmutex);
369 
370 	db_truenumerrors++;
371 	return((void *)el);
372 }
373 
374 /*
375  * Routine to add "geom" to the error in "errorlist".  Also adds a
376  * hierarchical traversal path "path" (which is "pathlen" long).
377  */
addgeomtoerror(void * errorlist,GEOM * geom,BOOLEAN showit,INTBIG pathlen,NODEINST ** path)378 void addgeomtoerror(void *errorlist, GEOM *geom, BOOLEAN showit, INTBIG pathlen, NODEINST **path)
379 {
380 	REGISTER ERRORLIST *el;
381 	REGISTER ERRORHIGHLIGHT *eh;
382 	REGISTER INTBIG i;
383 
384 	if (errorlist == 0) return;
385 	el = (ERRORLIST *)errorlist;
386 	eh = (ERRORHIGHLIGHT *)emalloc(sizeof (ERRORHIGHLIGHT), db_cluster);
387 	if (eh == 0) return;
388 	eh->type = ERRORTYPEGEOM;
389 	eh->geom = geom;
390 	eh->showgeom = showit;
391 	eh->pathlen = pathlen;
392 	if (pathlen > 0)
393 	{
394 		eh->path = (NODEINST **)emalloc(pathlen * (sizeof (NODEINST *)), db_cluster);
395 		if (eh->path == 0) return;
396 		for(i=0; i<pathlen; i++)
397 			eh->path[i] = path[i];
398 	}
399 	(void)db_addtoerrorlist(el, eh);
400 }
401 
402 /*
403  * Routine to add "pp" to the error in "errorlist".
404  */
addexporttoerror(void * errorlist,PORTPROTO * pp,BOOLEAN showit)405 void addexporttoerror(void *errorlist, PORTPROTO *pp, BOOLEAN showit)
406 {
407 	REGISTER ERRORLIST *el;
408 	REGISTER ERRORHIGHLIGHT *eh;
409 
410 	if (errorlist == 0) return;
411 	el = (ERRORLIST *)errorlist;
412 	eh = (ERRORHIGHLIGHT *)emalloc(sizeof (ERRORHIGHLIGHT), db_cluster);
413 	if (eh == 0) return;
414 	eh->type = ERRORTYPEEXPORT;
415 	eh->pp = pp;
416 	eh->showgeom = showit;
417 	eh->pathlen = 0;
418 	(void)db_addtoerrorlist(el, eh);
419 }
420 
421 /*
422  * Routine to add line (x1,y1)=>(x2,y2) to the error in "errorlist".
423  */
addlinetoerror(void * errorlist,INTBIG x1,INTBIG y1,INTBIG x2,INTBIG y2)424 void addlinetoerror(void *errorlist, INTBIG x1, INTBIG y1, INTBIG x2, INTBIG y2)
425 {
426 	REGISTER ERRORLIST *el;
427 	REGISTER ERRORHIGHLIGHT *eh;
428 
429 	if (errorlist == 0) return;
430 	el = (ERRORLIST *)errorlist;
431 	eh = (ERRORHIGHLIGHT *)emalloc(sizeof (ERRORHIGHLIGHT), db_cluster);
432 	if (eh == 0) return;
433 	eh->type = ERRORTYPELINE;
434 	eh->x1 = x1;
435 	eh->y1 = y1;
436 	eh->x2 = x2;
437 	eh->y2 = y2;
438 	eh->pathlen = 0;
439 	(void)db_addtoerrorlist(el, eh);
440 }
441 
442 /*
443  * Routine to add polygon "poly" to the error in "errorlist".
444  */
addpolytoerror(void * errorlist,POLYGON * poly)445 void addpolytoerror(void *errorlist, POLYGON *poly)
446 {
447 	REGISTER INTBIG i, prev;
448 	INTBIG lx, hx, ly, hy;
449 
450 	if (errorlist == 0) return;
451 	if (isbox(poly, &lx, &hx, &ly, &hy))
452 	{
453 		(void)addlinetoerror(errorlist, lx, ly, lx, hy);
454 		(void)addlinetoerror(errorlist, lx, hy, hx, hy);
455 		(void)addlinetoerror(errorlist, hx, hy, hx, ly) ;
456 		(void)addlinetoerror(errorlist, hx, ly, lx, ly);;
457 	} else
458 	{
459 		for(i=0; i<poly->count; i++)
460 		{
461 			if (i == 0) prev = poly->count-1; else prev = i-1;
462 			(void)addlinetoerror(errorlist, poly->xv[prev], poly->yv[prev],
463 				poly->xv[i], poly->yv[i]);
464 		}
465 	}
466 }
467 
468 /*
469  * Routine to add point (x,y) to the error in "errorlist".
470  */
addpointtoerror(void * errorlist,INTBIG x,INTBIG y)471 void addpointtoerror(void *errorlist, INTBIG x, INTBIG y)
472 {
473 	REGISTER ERRORLIST *el;
474 	REGISTER ERRORHIGHLIGHT *eh;
475 
476 	if (errorlist == 0) return;
477 	el = (ERRORLIST *)errorlist;
478 	eh = (ERRORHIGHLIGHT *)emalloc(sizeof (ERRORHIGHLIGHT), db_cluster);
479 	if (eh == 0) return;
480 	eh->type = ERRORTYPEPOINT;
481 	eh->x1 = x;
482 	eh->y1 = y;
483 	eh->pathlen = 0;
484 	(void)db_addtoerrorlist(el, eh);
485 }
486 
487 /*
488  * Routine called when all errors are logged.  Initializes pointers for replay of errors.
489  */
termerrorlogging(BOOLEAN explain)490 void termerrorlogging(BOOLEAN explain)
491 {
492 	REGISTER INTBIG errs;
493 	REGISTER ERRORLIST *el;
494 
495 	/* code cannot be called by multiple procesors: uses globals */
496 	NOT_REENTRANT;
497 
498 	db_curerrorlist = NOERRORLIST;
499 	db_nexterrorlist = db_firsterrorlist;
500 	db_preverrorlist = NOERRORLIST;
501 
502 	/* enumerate the errors */
503 	errs = 0;
504 	for(el = db_firsterrorlist; el != NOERRORLIST; el = el->nexterrorlist)
505 		el->index = ++errs;
506 
507 	if (db_errorchangedroutine != 0)
508 		(*db_errorchangedroutine)();
509 
510 	if (errs > 0 && explain)
511 	{
512 		ttyputmsg(_("To review errors, type:"));
513 		ttyputmsg(_(" >  Show the next error"));
514 		ttyputmsg(_(" <  Show the previous error"));
515 	}
516 }
517 
518 /*
519  * Routine to sort the errors by their "key" (a value provided to "logerror()").
520  * Obviously, this should be called after all errors have been reported.
521  */
sorterrors(void)522 void sorterrors(void)
523 {
524 	REGISTER ERRORLIST *el, **sortedlist;
525 	REGISTER INTBIG i;
526 
527 	if (db_truenumerrors == 0) return;
528 
529 	/* code cannot be called by multiple procesors: uses globals */
530 	NOT_REENTRANT;
531 
532 	sortedlist = (ERRORLIST **)emalloc(db_truenumerrors * (sizeof (ERRORLIST *)), db_cluster);
533 	if (sortedlist == 0) return;
534 	i = 0;
535 	for(el = db_firsterrorlist; el != NOERRORLIST; el = el->nexterrorlist)
536 		sortedlist[i++] = el;
537 	esort(sortedlist, db_truenumerrors, sizeof (ERRORLIST *), db_errorlistascending);
538 	for(i=0; i<db_truenumerrors; i++)
539 	{
540 		el = sortedlist[i];
541 		el->index = i+1;
542 		if (i == 0) el->preverrorlist = NOERRORLIST; else
543 			el->preverrorlist = sortedlist[i-1];
544 		if (i < db_truenumerrors-1) el->nexterrorlist = sortedlist[i+1]; else
545 			el->nexterrorlist = NOERRORLIST;
546 	}
547 	db_firsterrorlist = sortedlist[0];
548 	efree((CHAR *)sortedlist);
549 }
550 
551 /*
552  * Routine to return the number of logged errors.
553  */
numerrors(void)554 INTBIG numerrors(void)
555 {
556 	return(db_numerrors);
557 }
558 
559 /*
560  * Routine to advance to the next error and report it.
561  */
reportnexterror(INTBIG showhigh,GEOM ** g1,GEOM ** g2)562 CHAR *reportnexterror(INTBIG showhigh, GEOM **g1, GEOM **g2)
563 {
564 	if (db_nexterrorlist != NOERRORLIST)
565 	{
566 		db_curerrorlist = db_nexterrorlist;
567 		db_nexterrorlist = db_curerrorlist->nexterrorlist;
568 		db_preverrorlist = db_curerrorlist->preverrorlist;
569 	} else
570 	{
571 		/* at end: go to start of list */
572 		db_curerrorlist = db_firsterrorlist;
573 		if (db_curerrorlist == NOERRORLIST) db_nexterrorlist = NOERRORLIST; else
574 			db_nexterrorlist = db_curerrorlist->nexterrorlist;
575 		db_preverrorlist = NOERRORLIST;
576 	}
577 	return(db_reportcurrenterror(showhigh, g1, g2));
578 }
579 
580 /*
581  * Routine to back up to the previous error and report it.
582  */
reportpreverror(void)583 CHAR *reportpreverror(void)
584 {
585 	REGISTER ERRORLIST *el;
586 
587 	if (db_preverrorlist != NOERRORLIST)
588 	{
589 		db_curerrorlist = db_preverrorlist;
590 		db_nexterrorlist = db_curerrorlist->nexterrorlist;
591 		db_preverrorlist = db_curerrorlist->preverrorlist;
592 	} else
593 	{
594 		/* at start: go to end of list */
595 		db_preverrorlist = db_curerrorlist = db_nexterrorlist = NOERRORLIST;
596 		for(el = db_firsterrorlist; el != NOERRORLIST; el = el->nexterrorlist)
597 		{
598 			db_preverrorlist = db_curerrorlist;
599 			db_curerrorlist = el;
600 		}
601 	}
602 	return(db_reportcurrenterror(1, 0, 0));
603 }
604 
605 /*
606  * Routine to return a list of errors.  On the first call, set "e" to zero.
607  * Returns the next in the list (zero when done).
608  */
getnexterror(void * elv)609 void *getnexterror(void *elv)
610 {
611 	REGISTER ERRORLIST *el;
612 
613 	if (elv == 0) el = db_firsterrorlist; else
614 	{
615 		el = (ERRORLIST *)elv;
616 		if (el == NOERRORLIST) return(0);
617 		el = el->nexterrorlist;
618 	}
619 	if (el == NOERRORLIST) return(0);
620 	return((void *)el);
621 }
622 
623 /*
624  * Routine to return the number of objects associated with error "e".  Only
625  * returns "geom" objects
626  */
getnumerrorgeom(void * elv)627 INTBIG getnumerrorgeom(void *elv)
628 {
629 	REGISTER ERRORLIST *el;
630 	REGISTER ERRORHIGHLIGHT *eh;
631 	REGISTER INTBIG i, total;
632 
633 	if (elv == 0) return(0);
634 	el = (ERRORLIST *)elv;
635 	total = 0;
636 	for(i=0; i<el->numhighlights; i++)
637 	{
638 		eh = el->highlights[i];
639 		if (eh->type == ERRORTYPEGEOM) total++;
640 	}
641 	return(total);
642 }
643 
644 /*
645  * Routine to return the next object in a list of objects on an error.  The previous
646  * object is "eg".  Returns zero at the end of the list.
647  */
geterrorgeom(void * elv,INTBIG index)648 void *geterrorgeom(void *elv, INTBIG index)
649 {
650 	REGISTER ERRORLIST *el;
651 	REGISTER ERRORHIGHLIGHT *eh;
652 	REGISTER INTBIG i, total;
653 
654 	if (elv == 0) return(0);
655 	el = (ERRORLIST *)elv;
656 	total = 0;
657 	for(i=0; i<el->numhighlights; i++)
658 	{
659 		eh = el->highlights[i];
660 		if (eh->type != ERRORTYPEGEOM) continue;
661 		if (total == index) break;
662 		total++;
663 	}
664 	if (i >= el->numhighlights) return(0);
665 	return(eh);
666 }
667 
668 /*
669  * Routine to describe an object on an error.  The object is "ehv".
670  */
describeerrorgeom(void * ehv)671 CHAR *describeerrorgeom(void *ehv)
672 {
673 	REGISTER ERRORHIGHLIGHT *eh;
674 	REGISTER GEOM *geom;
675 	REGISTER void *infstr;
676 	REGISTER INTBIG i;
677 	REGISTER NODEINST *ni;
678 
679 	if (ehv == 0) return(x_(""));
680 	eh = (ERRORHIGHLIGHT *)ehv;
681 	geom = eh->geom;
682 	infstr = initinfstr();
683 	if (geom->entryisnode) formatinfstr(infstr, _("Node %s"), describenodeinst(geom->entryaddr.ni)); else
684 		formatinfstr(infstr, _("Arc %s"), describearcinst(geom->entryaddr.ai));
685 	for(i=0; i<eh->pathlen; i++)
686 	{
687 		ni = eh->path[i];
688 		formatinfstr(infstr, _(" in %s:%s:%s"), ni->parent->lib->libname,
689 			nldescribenodeproto(ni->parent), describenodeinst(ni));
690 	}
691 	return(returninfstr(infstr));
692 }
693 
694 /*
695  * Routine to highlight an object on an error.  The object is "ehv".
696  */
showerrorgeom(void * ehv)697 void showerrorgeom(void *ehv)
698 {
699 	REGISTER ERRORHIGHLIGHT *eh;
700 	REGISTER GEOM *geom;
701 	INTBIG lx, hx, ly, hy;
702 	REGISTER INTBIG i;
703 	REGISTER WINDOWPART *w;
704 	REGISTER NODEPROTO *np;
705 
706 	if (ehv == 0) return;
707 	eh = (ERRORHIGHLIGHT *)ehv;
708 	geom = eh->geom;
709 	np = geomparent(geom);
710 	for(w = el_topwindowpart; w != NOWINDOWPART; w = w->nextwindowpart)
711 		if (w->curnodeproto == np) break;
712 	if (w == NOWINDOWPART)
713 	{
714 		el_curwindowpart = us_wantnewwindow(0);
715 		us_fullview(np, &lx, &hx, &ly, &hy);
716 		us_switchtocell(np, lx, hx, ly, hy, NONODEINST, NOPORTPROTO, FALSE, FALSE, FALSE);
717 	}
718 	(void)asktool(us_tool, x_("show-object"), (INTBIG)geom);
719 
720 	for(i=eh->pathlen-1; i>=0; i--)
721 	{
722 		sethierarchicalparent(np, eh->path[i], NOWINDOWPART, 0, 0);
723 		np = eh->path[i]->parent;
724 	}
725 }
726 
727 /*
728  * Routine to describe error "elv".
729  */
describeerror(void * elv)730 CHAR *describeerror(void *elv)
731 {
732 	REGISTER ERRORLIST *el;
733 
734 	if (elv == 0) return(x_(""));
735 	el = (ERRORLIST *)elv;
736 	return(el->message);
737 }
738 
739 /*
740  * Routine to highlight and report error "elv".
741  */
reporterror(void * elv)742 void reporterror(void *elv)
743 {
744 	REGISTER ERRORLIST *el;
745 
746 	if (elv == 0) return;
747 	el = (ERRORLIST *)elv;
748 	db_curerrorlist = el;
749 	db_nexterrorlist = db_curerrorlist->nexterrorlist;
750 	db_preverrorlist = db_curerrorlist->preverrorlist;
751 	(void)db_reportcurrenterror(1, 0, 0);
752 }
753 
754 /*
755  * Routine to request that "routine" be called whenever any changes are made to the list of
756  * errors.
757  */
adviseofchanges(void (* routine)(void))758 void adviseofchanges(void (*routine)(void))
759 {
760 	db_errorchangedroutine = routine;
761 }
762 
763 /******************** ERROR REPORTING SUPPORT ROUTINES ********************/
764 
765 /*
766  * Internal routine to add detail to an error.  The error is "el" and the detail is "eh".
767  * Returns TRUE on error.
768  */
db_addtoerrorlist(ERRORLIST * el,ERRORHIGHLIGHT * eh)769 BOOLEAN db_addtoerrorlist(ERRORLIST *el, ERRORHIGHLIGHT *eh)
770 {
771 	REGISTER ERRORHIGHLIGHT **neweh;
772 	REGISTER INTBIG i;
773 
774 	neweh = (ERRORHIGHLIGHT **)emalloc((sizeof (ERRORHIGHLIGHT *)) * (el->numhighlights+1),
775 		db_cluster);
776 	if (neweh == 0) return(TRUE);
777 	for(i=0; i<el->numhighlights; i++)
778 		neweh[i] = el->highlights[i];
779 	neweh[el->numhighlights] = eh;
780 	if (el->numhighlights > 0) efree((CHAR *)el->highlights);
781 	el->highlights = neweh;
782 	el->numhighlights++;
783 	return(FALSE);
784 }
785 
786 /*
787  * Helper routine for "sorterrors()" to sort errors by their key
788  */
db_errorlistascending(const void * e1,const void * e2)789 int db_errorlistascending(const void *e1, const void *e2)
790 {
791 	REGISTER ERRORLIST *el1, *el2;
792 
793 	el1 = *((ERRORLIST **)e1);
794 	el2 = *((ERRORLIST **)e2);
795 	return(el1->sortkey - el2->sortkey);
796 }
797 
798 /*
799  * Routine to return the error message associated with the current error.
800  * Highlights associated graphics if "showhigh" is nonzero.  Fills "g1" and "g2"
801  * with associated geometry modules (if nonzero).
802  */
803 #define MAXCELLS 20
804 
db_reportcurrenterror(INTBIG showhigh,GEOM ** g1,GEOM ** g2)805 CHAR *db_reportcurrenterror(INTBIG showhigh, GEOM **g1, GEOM **g2)
806 {
807 	REGISTER ERRORLIST *el;
808 	REGISTER NODEPROTO *cell;
809 	REGISTER PORTPROTO *pp;
810 	NODEPROTO *celllist[MAXCELLS];
811 	REGISTER INTBIG i, j, consize, numcells, havegeoms, newwindows, count, hierpathcount;
812 	REGISTER NODEINST **hierpath, *ni;
813 	REGISTER ARCINST *ai;
814 	INTBIG lx, hx, ly, hy;
815 	REGISTER ERRORHIGHLIGHT *eh;
816 	REGISTER GEOM *geom1, *geom2;
817 	REGISTER WINDOWPART *w;
818 	WINDOWPART *neww[4];
819 	REGISTER void *infstr;
820 
821 	el = db_curerrorlist;
822 	if (el == NOERRORLIST)
823 	{
824 		infstr = initinfstr();
825 		formatinfstr(infstr, _("No %s errors"), db_errorsystem);
826 		return(returninfstr(infstr));
827 	}
828 
829 	/* turn off highlighting */
830 	if (showhigh != 0)
831 	{
832 		(void)asktool(us_tool, x_("clear"));
833 
834 		/* validate the cell (it may have been deleted) */
835 		cell = el->cell;
836 #ifdef	NEWERRORS
837 		if (cell != NONODEPROTO)
838 		{
839 			if (!db_validatecell(cell))
840 			{
841 				infstr = initinfstr();
842 				formatinfstr(infstr, _("%s error %ld of %ld (but cell is deleted): %s"), db_errorsystem,
843 					el->index, db_numerrors, el->message);
844 				return(returninfstr(infstr));
845 			}
846 		}
847 #else
848 		{
849 			REGISTER NODEPROTO *np;
850 			REGISTER LIBRARY *lib;
851 			for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
852 			{
853 				for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
854 					if (np == cell) break;
855 				if (np != NONODEPROTO) break;
856 			}
857 			if (lib == NOLIBRARY)
858 			{
859 				infstr = initinfstr();
860 				formatinfstr(infstr, _("%s error %ld of %ld (but cell is deleted): %s"), db_errorsystem,
861 					el->index, db_numerrors, el->message);
862 				return(returninfstr(infstr));
863 			}
864 		}
865 #endif
866 
867 		/* first figure out which cells need to be displayed */
868 		numcells = 0;
869 		for(i=0; i<el->numhighlights; i++)
870 		{
871 			eh = el->highlights[i];
872 			hierpathcount = 0;
873 			cell = el->cell;
874 #ifdef	NEWERRORS
875 			if (eh->showgeom && eh->type == ERRORTYPEGEOM && cell == NONODEPROTO)
876 			{
877 				cell = geomparent(eh->geom);
878 				if (cell != NONODEPROTO && !db_validatecell(cell))
879 					cell = NONODEPROTO;
880 			}
881 #endif
882 			switch (eh->type)
883 			{
884 				case ERRORTYPEGEOM:
885 					if (!eh->showgeom) cell = NONODEPROTO; else
886 #ifdef	NEWERRORS
887 						if (cell != NONODEPROTO)
888 #endif
889 					{
890 						/* validate the geometry */
891 						for(ai = cell->firstarcinst; ai != NOARCINST; ai = ai->nextarcinst)
892 							if (ai->geom == eh->geom) break;
893 						if (ai == NOARCINST)
894 						{
895 							for(ni = cell->firstnodeinst; ni != NONODEINST; ni = ni->nextnodeinst)
896 								if (ni->geom == eh->geom) break;
897 							if (ni == NONODEINST)
898 							{
899 								/* geometry pointer is not valid */
900 								eh->showgeom = FALSE;
901 								cell = NONODEPROTO;
902 							}
903 						}
904 					}
905 					if (eh->showgeom)
906 					{
907 						cell = geomparent(eh->geom);
908 						if (eh->pathlen > 0)
909 						{
910 							hierpathcount = eh->pathlen;
911 							hierpath = eh->path;
912 						}
913 					}
914 					break;
915 				case ERRORTYPEEXPORT:
916 					if (!eh->showgeom) cell = NONODEPROTO; else
917 #ifdef	NEWERRORS
918 						if (cell != NONODEPROTO)
919 #endif
920 					{
921 						/* validate the export */
922 						for(pp = cell->firstportproto; pp != NOPORTPROTO; pp = pp->nextportproto)
923 							if (pp == eh->pp) break;
924 						if (pp == NOPORTPROTO)
925 						{
926 							eh->showgeom = FALSE;
927 							cell = NONODEPROTO;
928 						} else cell = eh->pp->parent;
929 					}
930 					break;
931 				case ERRORTYPELINE:
932 				case ERRORTYPEPOINT:
933 					break;
934 			}
935 			if (cell == NONODEPROTO) continue;
936 			for(j=0; j<numcells; j++)
937 				if (celllist[j] == cell) break;
938 			if (j < numcells) continue;
939 			if (numcells >= MAXCELLS) break;
940 			celllist[numcells] = cell;
941 			numcells++;
942 		}
943 
944 		/* be sure that all requested cells are shown */
945 		newwindows = 0;
946 		for(i=0; i<numcells; i++)
947 		{
948 			/* see if the cell is already being displayed */
949 			for(w = el_topwindowpart; w != NOWINDOWPART; w = w->nextwindowpart)
950 				if (w->curnodeproto == celllist[i]) break;
951 			if (w != NOWINDOWPART)
952 			{
953 				/* already displayed: mark this cell done */
954 				bringwindowtofront(w->frame);
955 				celllist[i] = NONODEPROTO;
956 				continue;
957 			}
958 
959 			/* keep a count of the number of new windows needed */
960 			newwindows++;
961 		}
962 		while (newwindows > 0)
963 		{
964 			neww[0] = us_wantnewwindow(0);
965 			newwindows--;
966 			if (newwindows > 0)
967 			{
968 				el_curwindowpart = neww[0];
969 				neww[1] = us_splitcurrentwindow(2, FALSE, &neww[0], 50);
970 				newwindows--;
971 			}
972 			if (newwindows > 0)
973 			{
974 				el_curwindowpart = neww[0];
975 				neww[2] = us_splitcurrentwindow(1, FALSE, &neww[0], 50);
976 				newwindows--;
977 			}
978 			if (newwindows > 0)
979 			{
980 				el_curwindowpart = neww[1];
981 				neww[3] = us_splitcurrentwindow(1, FALSE, &neww[1], 50);
982 				newwindows--;
983 			}
984 			count = 0;
985 			for(i=0; i<numcells; i++)
986 			{
987 				if (celllist[i] == NONODEPROTO) continue;
988 				el_curwindowpart = neww[count++];
989 				us_fullview(celllist[i], &lx, &hx, &ly, &hy);
990 				us_switchtocell(celllist[i], lx, hx, ly, hy, NONODEINST, NOPORTPROTO, FALSE, FALSE, FALSE);
991 				celllist[i] = NONODEPROTO;
992 				if (count >= 4) break;
993 			}
994 		}
995 	}
996 
997 	/* first show the geometry associated with this error */
998 	geom1 = geom2 = NOGEOM;
999 	havegeoms = 0;
1000 	for(i=0; i<el->numhighlights; i++)
1001 	{
1002 		eh = el->highlights[i];
1003 		if (showhigh == 0 || !eh->showgeom) continue;
1004 		switch (eh->type)
1005 		{
1006 			case ERRORTYPEGEOM:
1007 				if (geom1 == NOGEOM) geom1 = eh->geom; else
1008 					if (geom2 == NOGEOM) geom2 = eh->geom;
1009 
1010 				/* include this geometry module in list to show */
1011 				if (havegeoms == 0) infstr = initinfstr(); else
1012 					addtoinfstr(infstr, '\n');
1013 				havegeoms++;
1014 				formatinfstr(infstr, x_("CELL=%s FROM=0%lo;-1;0"),
1015 					describenodeproto(geomparent(eh->geom)), (INTBIG)eh->geom);
1016 				break;
1017 			case ERRORTYPEEXPORT:
1018 				if (havegeoms == 0) infstr = initinfstr(); else
1019 					addtoinfstr(infstr, '\n');
1020 				havegeoms++;
1021 				formatinfstr(infstr, x_("CELL=%s TEXT=0%lo;0%lo;-"),
1022 					describenodeproto(eh->pp->parent), (INTBIG)eh->pp->subnodeinst->geom,
1023 						(INTBIG)eh->pp);
1024 				break;
1025 			case ERRORTYPELINE:
1026 			case ERRORTYPEPOINT:
1027 				break;
1028 		}
1029 
1030 		/* set the hierarchical path */
1031 		if (eh->type == ERRORTYPEGEOM && eh->showgeom && eh->pathlen > 0)
1032 		{
1033 			cell = geomparent(eh->geom);
1034 			for(w=el_topwindowpart; w != NOWINDOWPART; w = w->nextwindowpart)
1035 				if (w->curnodeproto == cell) break;
1036 			if (w != NOWINDOWPART)
1037 			{
1038 				for(j=eh->pathlen-1; j>=0; j--)
1039 				{
1040 					sethierarchicalparent(cell, eh->path[j], w, 0, 0);
1041 					cell = eh->path[j]->parent;
1042 				}
1043 			}
1044 		}
1045 	}
1046 
1047 	if (havegeoms != 0)
1048 		(void)asktool(us_tool, x_("show-multiple"), (INTBIG)returninfstr(infstr));
1049 
1050 	/* now show the lines and points associated with this error */
1051 	for(i=0; i<el->numhighlights; i++)
1052 	{
1053 		eh = el->highlights[i];
1054 		switch (eh->type)
1055 		{
1056 			case ERRORTYPELINE:
1057 				if (showhigh != 0)
1058 					(void)asktool(us_tool, x_("show-line"), eh->x1, eh->y1, eh->x2, eh->y2,
1059 						el->cell);
1060 				break;
1061 			case ERRORTYPEPOINT:
1062 				if (showhigh != 0)
1063 				{
1064 					consize = lambdaofcell(el->cell) * 5;
1065 					(void)asktool(us_tool, x_("show-line"), eh->x1-consize, eh->y1-consize,
1066 						eh->x1+consize, eh->y1+consize, el->cell);
1067 					(void)asktool(us_tool, x_("show-line"), eh->x1-consize, eh->y1+consize,
1068 						eh->x1+consize, eh->y1-consize, el->cell);
1069 				}
1070 				break;
1071 			case ERRORTYPEGEOM:
1072 			case ERRORTYPEEXPORT:
1073 				break;
1074 		}
1075 	}
1076 
1077 	/* return geometry if requested */
1078 	if (g1 != 0) *g1 = geom1;
1079 	if (g2 != 0) *g2 = geom2;
1080 
1081 	/* return the error message */
1082 	infstr = initinfstr();
1083 	formatinfstr(infstr, _("%s error %ld of %ld: %s"), db_errorsystem,
1084 		el->index, db_numerrors, el->message);
1085 	return(returninfstr(infstr));
1086 }
1087 
1088 #ifdef NEWERRORS
db_validatecell(NODEPROTO * cell)1089 BOOLEAN db_validatecell(NODEPROTO *cell)
1090 {
1091 	REGISTER LIBRARY *lib;
1092 	REGISTER NODEPROTO *np;
1093 
1094 	for(lib = el_curlib; lib != NOLIBRARY; lib = lib->nextlibrary)
1095 	{
1096 		for(np = lib->firstnodeproto; np != NONODEPROTO; np = np->nextnodeproto)
1097 			if (np == cell) return(TRUE);
1098 	}
1099 	return(FALSE);
1100 }
1101 #endif
1102