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