1 /* -*- tab-width: 4 -*-
2  *
3  * Electric(tm) VLSI Design System
4  *
5  * File: dbchange.c
6  * Database change manager
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 "edialogs.h"
35 #include "sim.h"
36 #include "usr.h"
37 
38 static INTBIG       db_batchtally;				/* number of batches in list */
39 static INTBIG       db_maximumbatches;			/* limit of number of batches */
40 static INTBIG       db_batchnumber;				/* batch number counter */
41 static CHANGEBATCH *db_currentbatch;			/* current batch of changes */
42 static CHANGEBATCH *db_headbatch;				/* most recent change in the list */
43 static CHANGEBATCH *db_tailbatch;				/* oldest change in the list */
44 static CHANGEBATCH *db_donebatch;				/* last done batch in the list */
45 static CHANGEBATCH *db_changebatchfree;			/* head of free change batches */
46 static CHANGE      *db_changefree;				/* head of free change modules */
47 static CHANGECELL  *db_changecellfree;			/* head of free change cell modules */
48 static TOOL        *db_currenttool;				/* current tool being given a slice */
49        BOOLEAN      db_donextchangequietly = FALSE;	/* true to do next change quietly */
50        BOOLEAN      db_dochangesquietly = FALSE;/* true to do changes quietly */
51        UINTBIG      db_changetimestamp = 0;		/* timestamp for changes to database */
52        INTBIG       db_broadcasting = 0;		/* nonzero if broadcasting */
53 
54 /* prototypes for local routines */
55 static CHANGE      *db_allocchange(void);
56 static void         db_freechange(CHANGE*);
57 static CHANGECELL  *db_allocchangecell(void);
58 static CHANGEBATCH *db_allocchangebatch(void);
59 static void         db_freechangebatch(CHANGEBATCH*);
60 static void         db_killbatch(CHANGEBATCH*);
61 static CHANGEBATCH *db_preparenewbatch(void);
62 static void         db_erasebatch(CHANGEBATCH*);
63 static void         db_freechangecell(CHANGECELL*);
64 static void         db_loadhistorylist(INTBIG, void*);
65 static void         db_broadcastchange(CHANGE*, BOOLEAN, BOOLEAN);
66 static BOOLEAN      db_reversechange(CHANGE*);
67 static CHAR        *db_describechange(INTBIG, INTBIG, INTBIG, INTBIG, INTBIG, INTBIG, INTBIG, INTBIG);
68 static BOOLEAN      db_majorvariable(INTBIG type, INTBIG key);
69 static void         db_setdirty(INTBIG change, INTBIG obj, INTBIG a1, INTBIG a2, INTBIG a3, INTBIG a4);
70 
71 /*
72  * Routine to free all memory associated with this module.
73  */
db_freechangememory(void)74 void db_freechangememory(void)
75 {
76 	REGISTER CHANGE *c;
77 	REGISTER CHANGECELL *cf;
78 	REGISTER CHANGEBATCH *b;
79 
80 	while (db_changefree != NOCHANGE)
81 	{
82 		c = db_changefree;
83 		db_changefree = db_changefree->nextchange;
84 		efree((CHAR *)c);
85 	}
86 	while (db_changecellfree != NOCHANGECELL)
87 	{
88 		cf = db_changecellfree;
89 		db_changecellfree = db_changecellfree->nextchangecell;
90 		efree((CHAR *)cf);
91 	}
92 	while (db_changebatchfree != NOCHANGEBATCH)
93 	{
94 		b = db_changebatchfree;
95 		db_changebatchfree = db_changebatchfree->nextchangebatch;
96 		efree((CHAR *)b);
97 	}
98 }
99 
100 /************************* CHANGE CONTROL *************************/
101 
102 /*
103  * Routine to initialize the change control system.
104  */
db_initializechanges(void)105 void db_initializechanges(void)
106 {
107 	/* initialize change lists */
108 	db_changefree = NOCHANGE;
109 	db_changecellfree = NOCHANGECELL;
110 	db_changebatchfree = NOCHANGEBATCH;
111 
112 	/* initialize changes */
113 	db_batchtally = 0;
114 	db_batchnumber = 1;
115 	db_maximumbatches = maxi(el_maxtools + el_maxtools/2 + 1, 20);
116 	db_currentbatch = db_headbatch = db_tailbatch = db_donebatch = NOCHANGEBATCH;
117 }
118 
119 /*
120  * Routine to record and broadcast a change.  The change is to object "obj" and the type of
121  * change is in "change".  The arguments to the change depend on the type, and are in "a1"
122  * through "a6".  Returns NOCHANGE upon error
123  */
db_change(INTBIG obj,INTBIG change,INTBIG a1,INTBIG a2,INTBIG a3,INTBIG a4,INTBIG a5,INTBIG a6)124 CHANGE *db_change(INTBIG obj, INTBIG change, INTBIG a1, INTBIG a2, INTBIG a3, INTBIG a4,
125 	INTBIG a5, INTBIG a6)
126 {
127 	REGISTER BOOLEAN firstchange;
128 	REGISTER CHANGE *c;
129 	static CHAR *broadcasttype[] = {M_("nodeinstnew"), M_("nodeinstkill"), M_("nodeinstmod"),
130 		M_("arcinstnew"), M_("arcinstkill"), M_("arcinstmod"), M_("portprotonew"),
131 		M_("portprotokill"), M_("portprotomod"), M_("nodeprotonew"), M_("nodeprotokill"),
132 		M_("nodeprotomod"), M_("objectstart"), M_("objectend"), M_("objectnew"), M_("objectkill"),
133 		M_("variablenew"), M_("variablekill"), M_("variablemod"), M_("variableinsert"),
134 		M_("variabledelete"), M_("descriptmod")};
135 
136 	/* code cannot be called by multiple procesors: uses globals */
137 	NOT_REENTRANT;
138 
139 	if (db_broadcasting != 0)
140 	{
141 		ttyputerr(_("Recieved this change during broadcast of type %s:"),
142 			TRANSLATE(broadcasttype[db_broadcasting-1]));
143 		ttyputmsg(x_("%s"), db_describechange(change, obj, a1, a2, a3, a4, a5, a6));
144 	}
145 
146 	/* determine what needs to be marked as changed */
147 	db_setdirty(change, obj, a1, a2, a3, a4);
148 
149 	/* get change module */
150 	c = db_allocchange();
151 	if (c == NOCHANGE)
152 	{
153 		ttyputnomemory();
154 		return(NOCHANGE);
155 	}
156 
157 	/* insert new change module into linked list */
158 	firstchange = FALSE;
159 	if (db_currentbatch == NOCHANGEBATCH)
160 	{
161 		db_currentbatch = db_preparenewbatch();
162 		if (db_currentbatch == NOCHANGEBATCH)
163 		{
164 			ttyputnomemory();
165 			return(NOCHANGE);
166 		}
167 		firstchange = TRUE;
168 		db_currentbatch->changehead = c;
169 		c->prevchange = NOCHANGE;
170 	} else
171 	{
172 		c->prevchange = db_currentbatch->changetail;
173 		db_currentbatch->changetail->nextchange = c;
174 	}
175 	db_currentbatch->changetail = c;
176 	c->nextchange = NOCHANGE;
177 
178 	/* save the change information */
179 	c->changetype = change;     c->entryaddr = obj;
180 	c->p1 = a1;   c->p2 = a2;   c->p3 = a3;
181 	c->p4 = a4;   c->p5 = a5;   c->p6 = a6;
182 
183 	/* broadcast the change */
184 	db_broadcastchange(c, firstchange, FALSE);
185 	return(c);
186 }
187 
db_setdirty(INTBIG change,INTBIG obj,INTBIG a1,INTBIG a2,INTBIG a3,INTBIG a4)188 void db_setdirty(INTBIG change, INTBIG obj, INTBIG a1, INTBIG a2, INTBIG a3, INTBIG a4)
189 {
190 	REGISTER INTBIG i, major;
191 	REGISTER PORTPROTO *pp;
192 	REGISTER NODEPROTO *np;
193 	REGISTER NODEINST *ni;
194 	REGISTER ARCINST *ai;
195 	REGISTER LIBRARY *lib;
196 
197 	np = NONODEPROTO;
198 	lib = NOLIBRARY;
199 	major = 0;
200 	switch (change)
201 	{
202 		case NODEINSTNEW:
203 		case NODEINSTKILL:
204 		case NODEINSTMOD:
205 			ni = (NODEINST *)obj;
206 			np = ni->parent;
207 			lib = np->lib;
208 			major = 1;
209 			break;
210 		case ARCINSTNEW:
211 		case ARCINSTKILL:
212 		case ARCINSTMOD:
213 			ai = (ARCINST *)obj;
214 			np = ai->parent;
215 			lib = np->lib;
216 			major = 1;
217 			break;
218 		case PORTPROTONEW:
219 		case PORTPROTOKILL:
220 		case PORTPROTOMOD:
221 			pp = (PORTPROTO *)obj;
222 			np = pp->parent;
223 			lib = np->lib;
224 			major = 1;
225 			break;
226 		case NODEPROTONEW:
227 			np = (NODEPROTO *)obj;
228 			lib = np->lib;
229 			major = 1;
230 			break;
231 		case NODEPROTOKILL:
232 			major = 1;
233 			/* FALLTHROUGH */
234 		case NODEPROTOMOD:
235 			lib = ((NODEPROTO *)obj)->lib;
236 			break;
237 		case OBJECTNEW:
238 		case OBJECTKILL:
239 			np = db_whichnodeproto(obj, a1);
240 			lib = whichlibrary(obj, a1);
241 			break;
242 		case VARIABLENEW:
243 		case VARIABLEMOD:
244 		case VARIABLEINS:
245 		case VARIABLEDEL:
246 			if ((a3&VDONTSAVE) != 0) { lib = NOLIBRARY;   np = NONODEPROTO; } else
247 			{
248 				np = db_whichnodeproto(obj, a1);
249 				lib = whichlibrary(obj, a1);
250 
251 				/* special cases that make the change "major" */
252 				if (db_majorvariable(a1, a2)) major = 1;
253 			}
254 			break;
255 		case VARIABLEKILL:
256 			if ((a4&VDONTSAVE) != 0) { lib = NOLIBRARY;   np = NONODEPROTO; } else
257 			{
258 				np = db_whichnodeproto(obj, a1);
259 				lib = whichlibrary(obj, a1);
260 
261 				/* special cases that make the change "major" */
262 				if (db_majorvariable(a1, a2)) major = 1;
263 			}
264 			break;
265 		case DESCRIPTMOD:
266 			if ((a3&VDONTSAVE) != 0) { lib = NOLIBRARY;   np = NONODEPROTO; } else
267 			{
268 				np = db_whichnodeproto(obj, a1);
269 				lib = whichlibrary(obj, a1);
270 			}
271 			break;
272 	}
273 
274 	/* set "changed" and "dirty" bits */
275 	if (np != NONODEPROTO)
276 	{
277 		if (major != 0) np->revisiondate = (UINTBIG)getcurrenttime();
278 		np->adirty = 0;
279 		for(i=0; i<el_maxtools; i++)
280 			if ((el_tools[i].toolstate & TOOLON) == 0) np->adirty |= 1 << i;
281 	}
282 	if (lib != NOLIBRARY)
283 	{
284 		if (major != 0) lib->userbits |= LIBCHANGEDMAJOR; else
285 			lib->userbits |= LIBCHANGEDMINOR;
286 	}
287 }
288 
db_majorvariable(INTBIG type,INTBIG key)289 BOOLEAN db_majorvariable(INTBIG type, INTBIG key)
290 {
291 	switch (type)
292 	{
293 		case VNODEPROTO:
294 			/* when changing text in text-only cell */
295 			if (key == el_cell_message_key) return(TRUE);
296 			break;
297 		case VNODEINST:
298 			/* when changing node name */
299 			if (key == el_node_name_key || key == sim_weaknodekey) return(TRUE);
300 			break;
301 		case VARCINST:
302 			/* when changing arc name */
303 			if (key == el_arc_name_key) return(TRUE);
304 			break;
305 	}
306 	return(FALSE);
307 }
308 
309 /*
310  * Routine to request that the next change be made "quietly" (i.e. no constraints,
311  * change control, or broadcast).
312  */
nextchangequiet(void)313 void nextchangequiet(void)
314 {
315 	db_donextchangequietly = TRUE;
316 }
317 
318 /*
319  * Routine to set the nature of subsequent changes to be "quiet".  TRUE
320  * means no constraints, change control, or broadcast are done.
321  */
changesquiet(BOOLEAN quiet)322 void changesquiet(BOOLEAN quiet)
323 {
324 	db_dochangesquietly = quiet;
325 }
326 
327 /*
328  * Routine to broadcast change "c" to all tools that are on.  If "firstchange" is nonzero,
329  * this is the first change of a batch, and a "startbatch" change will also be broadcast.
330  * "undoredo" is true if this is an undo/redo batch.
331  */
db_broadcastchange(CHANGE * c,BOOLEAN firstchange,BOOLEAN undoredo)332 void db_broadcastchange(CHANGE *c, BOOLEAN firstchange, BOOLEAN undoredo)
333 {
334 	REGISTER INTBIG i, oldbroadcasting;
335 	UINTBIG descript[TEXTDESCRIPTSIZE];
336 
337 	/* code cannot be called by multiple procesors: uses globals */
338 	NOT_REENTRANT;
339 
340 	/* start the batch if this is the first change */
341 	oldbroadcasting = db_broadcasting;
342 	db_broadcasting = c->changetype+1;
343 	if (firstchange)
344 	{
345 		/* broadcast a start-batch on the first change */
346 		for(i=0; i<el_maxtools; i++)
347 			if ((el_tools[i].toolstate & TOOLON) != 0 && el_tools[i].startbatch != 0)
348 				(*el_tools[i].startbatch)(db_currentbatch->tool, undoredo);
349 	}
350 	switch (c->changetype)
351 	{
352 		case NODEINSTNEW:
353 			for(i=0; i<el_maxtools; i++)
354 				if ((el_tools[i].toolstate & TOOLON) != 0 && el_tools[i].newobject != 0)
355 					(*el_tools[i].newobject)(c->entryaddr, VNODEINST);
356 			break;
357 		case NODEINSTKILL:
358 			for(i=0; i<el_maxtools; i++)
359 				if ((el_tools[i].toolstate & TOOLON) != 0 && el_tools[i].killobject != 0)
360 					(*el_tools[i].killobject)(c->entryaddr, VNODEINST);
361 			break;
362 		case NODEINSTMOD:
363 			for(i=0; i<el_maxtools; i++)
364 				if ((el_tools[i].toolstate & TOOLON) != 0 && el_tools[i].modifynodeinst != 0)
365 					(*el_tools[i].modifynodeinst)((NODEINST *)c->entryaddr, c->p1, c->p2,
366 						c->p3, c->p4, c->p5, c->p6);
367 			break;
368 		case ARCINSTNEW:
369 			for(i=0; i<el_maxtools; i++)
370 				if ((el_tools[i].toolstate & TOOLON) != 0 && el_tools[i].newobject != 0)
371 					(*el_tools[i].newobject)(c->entryaddr, VARCINST);
372 			break;
373 		case ARCINSTKILL:
374 			for(i=0; i<el_maxtools; i++)
375 				if ((el_tools[i].toolstate & TOOLON) != 0 && el_tools[i].killobject != 0)
376 					(*el_tools[i].killobject)(c->entryaddr, VARCINST);
377 			break;
378 		case ARCINSTMOD:
379 			for(i=0; i<el_maxtools; i++)
380 				if ((el_tools[i].toolstate & TOOLON) != 0 && el_tools[i].modifyarcinst != 0)
381 					(*el_tools[i].modifyarcinst)((ARCINST *)c->entryaddr, c->p1, c->p2,
382 						c->p3, c->p4, c->p5, c->p6);
383 			break;
384 		case PORTPROTONEW:
385 			for(i=0; i<el_maxtools; i++)
386 				if ((el_tools[i].toolstate & TOOLON) != 0 && el_tools[i].newobject != 0)
387 					(*el_tools[i].newobject)(c->entryaddr, VPORTPROTO);
388 			break;
389 		case PORTPROTOKILL:
390 			for(i=0; i<el_maxtools; i++)
391 				if ((el_tools[i].toolstate & TOOLON) != 0 && el_tools[i].killobject != 0)
392 					(*el_tools[i].killobject)(c->entryaddr, VPORTPROTO);
393 			break;
394 		case PORTPROTOMOD:
395 			for(i=0; i<el_maxtools; i++)
396 				if ((el_tools[i].toolstate & TOOLON) != 0 && el_tools[i].modifyportproto != 0)
397 					(*el_tools[i].modifyportproto)((PORTPROTO *)c->entryaddr, (NODEINST *)c->p1,
398 						(PORTPROTO *)c->p2);
399 			break;
400 		case NODEPROTONEW:
401 			for(i=0; i<el_maxtools; i++)
402 				if ((el_tools[i].toolstate & TOOLON) != 0 && el_tools[i].newobject != 0)
403 					(*el_tools[i].newobject)(c->entryaddr, VNODEPROTO);
404 			break;
405 		case NODEPROTOKILL:
406 			for(i=0; i<el_maxtools; i++)
407 				if ((el_tools[i].toolstate & TOOLON) != 0 && el_tools[i].killobject != 0)
408 					(*el_tools[i].killobject)(c->entryaddr, VNODEPROTO);
409 			break;
410 		case NODEPROTOMOD:
411 			for(i=0; i<el_maxtools; i++)
412 				if ((el_tools[i].toolstate & TOOLON) != 0 && el_tools[i].modifynodeproto != 0)
413 					(*el_tools[i].modifynodeproto)((NODEPROTO *)c->entryaddr);
414 			break;
415 		case OBJECTSTART:
416 			for(i=0; i<el_maxtools; i++)
417 				if ((el_tools[i].toolstate & TOOLON) != 0 && el_tools[i].startobjectchange != 0)
418 					(*el_tools[i].startobjectchange)(c->entryaddr, c->p1);
419 			break;
420 		case OBJECTEND:
421 			for(i=0; i<el_maxtools; i++)
422 				if ((el_tools[i].toolstate & TOOLON) != 0 && el_tools[i].endobjectchange != 0)
423 					(*el_tools[i].endobjectchange)(c->entryaddr, c->p1);
424 			break;
425 		case OBJECTNEW:
426 			for(i=0; i<el_maxtools; i++)
427 				if ((el_tools[i].toolstate & TOOLON) != 0 && el_tools[i].newobject != 0)
428 					(*el_tools[i].newobject)(c->entryaddr, c->p1);
429 			break;
430 		case OBJECTKILL:
431 			for(i=0; i<el_maxtools; i++)
432 				if ((el_tools[i].toolstate & TOOLON) != 0 && el_tools[i].killobject != 0)
433 					(*el_tools[i].killobject)(c->entryaddr, c->p1);
434 			break;
435 		case VARIABLENEW:
436 			for(i=0; i<el_maxtools; i++)
437 				if ((el_tools[i].toolstate & TOOLON) != 0 && el_tools[i].newvariable != 0)
438 					(*el_tools[i].newvariable)(c->entryaddr, c->p1, c->p2, c->p3);
439 			break;
440 		case VARIABLEKILL:
441 			descript[0] = c->p5;
442 			descript[1] = c->p6;
443 			for(i=0; i<el_maxtools; i++)
444 				if ((el_tools[i].toolstate & TOOLON) != 0 && el_tools[i].killvariable != 0)
445 					(*el_tools[i].killvariable)(c->entryaddr, c->p1, c->p2, c->p3, c->p4, descript);
446 			break;
447 		case VARIABLEMOD:
448 			for(i=0; i<el_maxtools; i++)
449 				if ((el_tools[i].toolstate & TOOLON) != 0 && el_tools[i].modifyvariable != 0)
450 					(*el_tools[i].modifyvariable)(c->entryaddr, c->p1, c->p2, c->p3, c->p4, c->p5);
451 			break;
452 		case VARIABLEINS:
453 			for(i=0; i<el_maxtools; i++)
454 				if ((el_tools[i].toolstate & TOOLON) != 0 && el_tools[i].insertvariable != 0)
455 					(*el_tools[i].insertvariable)(c->entryaddr, c->p1, c->p2, c->p3, c->p4);
456 			break;
457 		case VARIABLEDEL:
458 			for(i=0; i<el_maxtools; i++)
459 				if ((el_tools[i].toolstate & TOOLON) != 0 && el_tools[i].deletevariable != 0)
460 					(*el_tools[i].deletevariable)(c->entryaddr, c->p1, c->p2, c->p3, c->p4, c->p5);
461 			break;
462 		case DESCRIPTMOD:
463 			descript[0] = c->p4;
464 			descript[1] = c->p5;
465 			for(i=0; i<el_maxtools; i++)
466 				if ((el_tools[i].toolstate & TOOLON) != 0 && el_tools[i].modifydescript != 0)
467 					(*el_tools[i].modifydescript)(c->entryaddr, c->p1, c->p2, descript);
468 			break;
469 	}
470 	db_broadcasting = oldbroadcasting;
471 }
472 
473 /*
474  * Routine to terminate a batch of changes by broadcasting the endbatch
475  * and clearing the "change" words.
476  */
db_endbatch(void)477 void db_endbatch(void)
478 {
479 	REGISTER INTBIG i;
480 
481 	/* if no changes were recorded, stop */
482 	if (db_currentbatch == NOCHANGEBATCH) return;
483 
484 	/* changes made: apply final constraints to this batch of changes */
485 	(*el_curconstraint->solve)(NONODEPROTO);
486 
487 	for(i=0; i<el_maxtools; i++)
488 		if ((el_tools[i].toolstate & TOOLON) != 0 && el_tools[i].endbatch != 0)
489 			(*el_tools[i].endbatch)();
490 
491 	db_currentbatch = NOCHANGEBATCH;
492 }
493 
494 /************************* BATCH CONTROL *************************/
495 
496 /*
497  * Routine to allocate and initialize a new batch of changes.
498  * Returns NOCHANGEBATCH on error.
499  */
db_preparenewbatch(void)500 CHANGEBATCH *db_preparenewbatch(void)
501 {
502 	REGISTER CHANGEBATCH *killbatch, *thisbatch;
503 
504 	/* code cannot be called by multiple procesors: uses globals */
505 	NOT_REENTRANT;
506 
507 	/* first kill off any undone batches */
508 	noredoallowed();
509 
510 	/* allocate a new change batch */
511 	thisbatch = db_allocchangebatch();
512 	if (thisbatch == NOCHANGEBATCH) return(NOCHANGEBATCH);
513 	thisbatch->changehead = NOCHANGE;
514 	thisbatch->batchnumber = db_batchnumber++;
515 	thisbatch->done = TRUE;
516 	thisbatch->nextchangebatch = NOCHANGEBATCH;
517 
518 	/* put at head of list */
519 	if (db_headbatch == NOCHANGEBATCH)
520 	{
521 		thisbatch->lastchangebatch = NOCHANGEBATCH;
522 		db_headbatch = db_tailbatch = thisbatch;
523 	} else
524 	{
525 		thisbatch->lastchangebatch = db_headbatch;
526 		db_headbatch->nextchangebatch = thisbatch;
527 		db_headbatch = thisbatch;
528 	}
529 	db_donebatch = thisbatch;
530 
531 	/* kill last batch if list is full */
532 	db_batchtally++;
533 	if (db_batchtally > db_maximumbatches)
534 	{
535 		killbatch = db_tailbatch;
536 		db_tailbatch = db_tailbatch->nextchangebatch;
537 		db_tailbatch->lastchangebatch = NOCHANGEBATCH;
538 		db_killbatch(killbatch);
539 	}
540 
541 	/* miscellaneous initialization */
542 	thisbatch->firstchangecell = NOCHANGECELL;
543 	thisbatch->tool = db_currenttool;
544 	thisbatch->activity = 0;
545 	return(thisbatch);
546 }
547 
548 /*
549  * Routine to completely delete change batch "batch".
550  */
db_killbatch(CHANGEBATCH * batch)551 void db_killbatch(CHANGEBATCH *batch)
552 {
553 	db_erasebatch(batch);
554 	db_batchtally--;
555 	db_freechangebatch(batch);
556 }
557 
558 /*
559  * routine to erase the contents of change batch "batch"
560  */
db_erasebatch(CHANGEBATCH * batch)561 void db_erasebatch(CHANGEBATCH *batch)
562 {
563 	REGISTER CHANGE *c, *nextc;
564 	REGISTER CHANGECELL *cc, *nextcc;
565 	REGISTER NODEINST *ni;
566 	REGISTER ARCINST *ai;
567 	REGISTER NODEPROTO *np;
568 	REGISTER PORTPROTO *pp;
569 	REGISTER WINDOWPART *w;
570 	REGISTER EDITOR *e;
571 
572 	/* erase the change information */
573 	for(c = batch->changehead; c != NOCHANGE; c = nextc)
574 	{
575 		nextc = c->nextchange;
576 		if (c->changetype == NODEINSTKILL)
577 		{
578 			/* now free the nodeinst */
579 			ni = (NODEINST *)c->entryaddr;
580 			freegeom(ni->geom);
581 			freenodeinst(ni);
582 		} else if (c->changetype == ARCINSTKILL)
583 		{
584 			ai = (ARCINST *)c->entryaddr;
585 			freeportarcinst(ai->end[0].portarcinst);
586 			freeportarcinst(ai->end[1].portarcinst);
587 			freegeom(ai->geom);
588 			freearcinst(ai);
589 		} else if (c->changetype == PORTPROTOKILL)
590 		{
591 			pp = (PORTPROTO *)c->entryaddr;
592 			efree(pp->protoname);
593 			freeportproto(pp);
594 		} else if (c->changetype == NODEPROTOKILL)
595 		{
596 			np = (NODEPROTO *)c->entryaddr;
597 			freenodeproto(np);
598 		} else if (c->changetype == VARIABLEKILL)
599 		{
600 			db_freevar(c->p3, c->p4);
601 		} else if (c->changetype == OBJECTKILL)
602 		{
603 			switch (c->p1&VTYPE)
604 			{
605 				case VVIEW:
606 					freeview((VIEW *)c->entryaddr);
607 					break;
608 				case VWINDOWPART:
609 					w = (WINDOWPART *)c->entryaddr;
610 					if ((w->state&WINDOWTYPE) == TEXTWINDOW || (w->state&WINDOWTYPE) == POPTEXTWINDOW)
611 					{
612 						e = w->editor;
613 						if ((w->state&WINDOWTYPE) == POPTEXTWINDOW)
614 							screenrestorebox(e->savedbox, -1);
615 						us_freeeditor(e);
616 					}
617 					freewindowpart(w);
618 					break;
619 			}
620 		} else if (c->changetype == VARIABLEDEL ||
621 			c->changetype == VARIABLEMOD)
622 		{
623 			/* free the array entry if it is allocated */
624 			if ((c->p3&(VCODE1|VCODE2)) != 0 || (c->p3&VTYPE) == VSTRING)
625 			{
626 				efree((CHAR *)c->p5);
627 			}
628 		}
629 		db_freechange(c);
630 	}
631 	batch->changehead = batch->changetail = NOCHANGE;
632 
633 	/* erase the change cell information */
634 	for(cc = batch->firstchangecell; cc != NOCHANGECELL; cc = nextcc)
635 	{
636 		nextcc = cc->nextchangecell;
637 		db_freechangecell(cc);
638 	}
639 	batch->firstchangecell = NOCHANGECELL;
640 
641 	/* erase the activity information */
642 	if (batch->activity != 0) efree(batch->activity);
643 	batch->activity = 0;
644 }
645 
646 /*
647  * Routine to prevent undo by deleting all batches.
648  */
noundoallowed(void)649 void noundoallowed(void)
650 {
651 	REGISTER CHANGEBATCH *batch, *prevbatch;
652 
653 	/* properly terminate the current batch */
654 	db_endbatch();
655 
656 	/* kill them all */
657 	for(batch = db_headbatch; batch != NOCHANGEBATCH; batch = prevbatch)
658 	{
659 		prevbatch = batch->lastchangebatch;
660 		db_killbatch(batch);
661 	}
662 
663 	/* clear pointers */
664 	db_currentbatch = NOCHANGEBATCH;
665 	db_headbatch = NOCHANGEBATCH;
666 	db_tailbatch = NOCHANGEBATCH;
667 	db_donebatch = NOCHANGEBATCH;
668 }
669 
670 /*
671  * Routine to prevent redo by deleting all undone batches.
672  */
noredoallowed(void)673 void noredoallowed(void)
674 {
675 	REGISTER CHANGEBATCH *killbatch;
676 
677 	/* code cannot be called by multiple procesors: uses globals */
678 	NOT_REENTRANT;
679 
680 	while (db_headbatch != NOCHANGEBATCH && db_headbatch != db_donebatch)
681 	{
682 		killbatch = db_headbatch;
683 		db_headbatch = db_headbatch->lastchangebatch;
684 		if (db_headbatch != NOCHANGEBATCH) db_headbatch->nextchangebatch = NOCHANGEBATCH;
685 		db_killbatch(killbatch);
686 	}
687 }
688 
689 /*
690  * routine to set the size of the history list to "newsize".  The size of
691  * the list is returned.  If "newsize" is not positive, the size is not
692  * changed, just returned.
693  */
historylistsize(INTBIG newsize)694 INTBIG historylistsize(INTBIG newsize)
695 {
696 	REGISTER CHANGEBATCH *batch;
697 	REGISTER INTBIG oldsize;
698 
699 	if (newsize <= 0) return(db_maximumbatches);
700 
701 	oldsize = db_maximumbatches;
702 	db_maximumbatches = newsize;
703 	while (db_batchtally > db_maximumbatches)
704 	{
705 		batch = db_tailbatch;
706 		db_tailbatch = db_tailbatch->nextchangebatch;
707 		db_tailbatch->lastchangebatch = NOCHANGEBATCH;
708 		db_killbatch(batch);
709 	}
710 	return(oldsize);
711 }
712 
713 /*
714  * routine to set the tool for the current (or next) batch of changes
715  */
db_setcurrenttool(TOOL * tool)716 void db_setcurrenttool(TOOL *tool)
717 {
718 	db_currenttool = tool;
719 }
720 
721 /*
722  * routine to get the current batch of changes
723  */
db_getcurrentbatch(void)724 CHANGEBATCH *db_getcurrentbatch(void)
725 {
726 	return(db_currentbatch);
727 }
728 
729 /*
730  * routine to get the identifying number for the current batch of changes
731  */
getcurrentbatchnumber(void)732 INTBIG getcurrentbatchnumber(void)
733 {
734 	if (db_currentbatch == NOCHANGEBATCH) return(-1);
735 	return(db_currentbatch->batchnumber);
736 }
737 
738 /*
739  * routine to set the activity message for the current batch of changes
740  */
setactivity(CHAR * message)741 void setactivity(CHAR *message)
742 {
743 	if (db_currentbatch == NOCHANGEBATCH) return;		/* should save for later !!! */
744 	if (db_currentbatch->activity != 0) efree(db_currentbatch->activity);
745 	(void)allocstring(&db_currentbatch->activity, message, db_cluster);
746 }
747 
748 /************************* UNDO/REDO *************************/
749 
750 /*
751  * Routine to return the nature of the next possible undo/redo batch.
752  * If "undo" is true, examine the next undo batch, otherwise the next redo batch.
753  * Returns zero if there is no batch available.
754  * Returns positive if there is a batch with major changes in it.
755  * Returns negative if there is a batch with minor changes in it.
756  */
undonature(BOOLEAN undo)757 INTBIG undonature(BOOLEAN undo)
758 {
759 	REGISTER CHANGEBATCH *batch;
760 	REGISTER CHANGE *c;
761 	REGISTER INTBIG retval;
762 
763 	if (undo)
764 	{
765 		/* examine the next batch to be undone */
766 		if (db_donebatch == NOCHANGEBATCH) return(0);
767 		batch = db_donebatch;
768 	} else
769 	{
770 		/* examine the next batch to be redone */
771 		if (db_donebatch == NOCHANGEBATCH) return(0);
772 		batch = db_donebatch->nextchangebatch;
773 		if (batch == NOCHANGEBATCH) return(0);
774 	}
775 
776 	/* determine the nature of the batch */
777 	retval = -1;
778 	for(c = batch->changetail; c != NOCHANGE; c = c->prevchange)
779 	{
780 		switch (c->changetype)
781 		{
782 			case NODEINSTNEW:
783 			case NODEINSTKILL:
784 			case NODEINSTMOD:
785 			case ARCINSTNEW:
786 			case ARCINSTKILL:
787 			case ARCINSTMOD:
788 			case PORTPROTONEW:
789 			case PORTPROTOKILL:
790 			case PORTPROTOMOD:
791 			case NODEPROTONEW:
792 			case NODEPROTOKILL:
793 			case NODEPROTOMOD:
794 			case DESCRIPTMOD:
795 				retval = 1;
796 				break;
797 			case VARIABLENEW:
798 				if ((c->p3&VCREF) != 0) retval = 1;
799 				break;
800 		}
801 	}
802 	return(retval);
803 }
804 
805 /*
806  * routine to undo backward through the list of change batches.  If there is no batch to
807  * undo, the routine returns zero.  If there is a batch, it is undone and the
808  * routine returns the batch number (a positive value) if the batch involved changes to
809  * nodes/arcs/ports/cells or the negative batch number if the batch only involves minor
810  * changes (variables, etc.).  The tool that produced the batch is returned in "tool".
811  */
undoabatch(TOOL ** tool)812 INTBIG undoabatch(TOOL **tool)
813 {
814 	REGISTER CHANGEBATCH *batch, *savebatch;
815 	REGISTER CHANGE *c;
816 	REGISTER INTBIG i, retval;
817 	REGISTER BOOLEAN firstchange;
818 
819 	/* get the most recent batch of changes */
820 	if (db_donebatch == NOCHANGEBATCH) return(0);
821 	batch = db_donebatch;
822 	db_donebatch = db_donebatch->lastchangebatch;
823 	*tool = batch->tool;
824 
825 	/* look through the changes in this batch */
826 	firstchange = TRUE;
827 	retval = -batch->batchnumber;
828 	savebatch = db_currentbatch;
829 	for(c = batch->changetail; c != NOCHANGE; c = c->prevchange)
830 	{
831 		/* reverse the change */
832 		if (db_reversechange(c))
833 			retval = batch->batchnumber;
834 
835 		/* now broadcast this change */
836 		db_currentbatch = batch;
837 		db_broadcastchange(c, firstchange, TRUE);
838 		firstchange = FALSE;
839 	}
840 
841 	/* broadcast the end-batch */
842 	for(i=0; i<el_maxtools; i++)
843 		if ((el_tools[i].toolstate & TOOLON) != 0 && el_tools[i].endbatch != 0)
844 			(*el_tools[i].endbatch)();
845 	db_currentbatch = savebatch;
846 
847 	/* mark that this batch is undone */
848 	batch->done = FALSE;
849 	return(retval);
850 }
851 
852 /*
853  * routine to redo forward through the list of change batches.  If there is no batch to
854  * redo, the routine returns zero.  If there is a batch, it is redone and the
855  * routine returns the batch number (a positive value) if the batch involved changes to
856  * nodes/arcs/ports/cells or the negative batch number if the batch only involves minor
857  * changes (variables, etc.).  The tool that produced the batch is returned in "tool".
858  */
redoabatch(TOOL ** tool)859 INTBIG redoabatch(TOOL **tool)
860 {
861 	REGISTER CHANGEBATCH *batch, *savebatch;
862 	REGISTER CHANGE *c;
863 	REGISTER BOOLEAN firstchange;
864 	REGISTER INTBIG i, retval;
865 
866 	/* get the most recent batch of changes */
867 	if (db_donebatch == NOCHANGEBATCH)
868 	{
869 		if (db_tailbatch == NOCHANGEBATCH) return(0);
870 		batch = db_tailbatch;
871 	} else
872 	{
873 		batch = db_donebatch->nextchangebatch;
874 		if (batch == NOCHANGEBATCH) return(0);
875 	}
876 	db_donebatch = batch;
877 	*tool = batch->tool;
878 
879 	/* look through the changes in this batch */
880 	firstchange = TRUE;
881 	retval = -batch->batchnumber;
882 	savebatch = db_currentbatch;
883 	for(c = batch->changehead; c != NOCHANGE; c = c->nextchange)
884 	{
885 		/* reverse the change */
886 		if (db_reversechange(c))
887 			retval = batch->batchnumber;
888 
889 		/* now broadcast this change */
890 		db_currentbatch = batch;
891 		db_broadcastchange(c, firstchange, TRUE);
892 		firstchange = FALSE;
893 	}
894 
895 	/* broadcast the end-batch */
896 	for(i=0; i<el_maxtools; i++)
897 		if ((el_tools[i].toolstate & TOOLON) != 0 && el_tools[i].endbatch != 0)
898 			(*el_tools[i].endbatch)();
899 	db_currentbatch = savebatch;
900 
901 	/* mark that this batch is redone */
902 	batch->done = TRUE;
903 	return(retval);
904 }
905 
906 /*
907  * Routine to undo the effects of change "c".
908  * Returns true if the change is important.
909  */
db_reversechange(CHANGE * c)910 BOOLEAN db_reversechange(CHANGE *c)
911 {
912 	REGISTER PORTPROTO *pp, *oldsubpp;
913 	REGISTER NODEPROTO *np;
914 	REGISTER NODEINST *ni, *oldsubni;
915 	REGISTER ARCINST *ai;
916 	REGISTER VARIABLE *var;
917 	REGISTER VIEW *v, *lastv, *view;
918 	REGISTER WINDOWPART *w;
919 	REGISTER CHAR *attname;
920 	CHAR *storage;
921 	INTBIG oldval;
922 	REGISTER INTSML oldshort;
923 
924 	/* determine what needs to be marked as changed */
925 	db_setdirty(c->changetype, c->entryaddr, c->p1, c->p2, c->p3, c->p4);
926 
927 	switch (c->changetype)
928 	{
929 		case NODEINSTNEW:
930 			ni = (NODEINST *)c->entryaddr;
931 			db_retractnodeinst(ni);
932 			c->changetype = NODEINSTKILL;
933 			return(TRUE);
934 		case NODEINSTKILL:
935 			ni = (NODEINST *)c->entryaddr;
936 			db_enternodeinst(ni);
937 			c->changetype = NODEINSTNEW;
938 			return(TRUE);
939 		case NODEINSTMOD:
940 			ni = (NODEINST *)c->entryaddr;
941 			oldval = ni->lowx;       ni->lowx = c->p1;       c->p1 = oldval;
942 			oldval = ni->lowy;       ni->lowy = c->p2;       c->p2 = oldval;
943 			oldval = ni->highx;      ni->highx = c->p3;      c->p3 = oldval;
944 			oldval = ni->highy;      ni->highy = c->p4;      c->p4 = oldval;
945 			oldshort = ni->rotation;   ni->rotation = (INTSML)c->p5;   c->p5 = oldshort;
946 			oldshort = ni->transpose;  ni->transpose = (INTSML)c->p6;  c->p6 = oldshort;
947 			/* don't need to call "updategeom()" because it is done by "OBJECTSTART" */
948 			return(TRUE);
949 		case ARCINSTNEW:
950 			ai = (ARCINST *)c->entryaddr;
951 			db_retractarcinst(ai);
952 			c->changetype = ARCINSTKILL;
953 			return(TRUE);
954 		case ARCINSTKILL:
955 			ai = (ARCINST *)c->entryaddr;
956 			db_addportarcinst(ai->end[0].nodeinst, ai->end[0].portarcinst);
957 			db_addportarcinst(ai->end[1].nodeinst, ai->end[1].portarcinst);
958 			db_enterarcinst(ai);
959 			c->changetype = ARCINSTNEW;
960 			return(TRUE);
961 		case ARCINSTMOD:
962 			ai = (ARCINST *)c->entryaddr;
963 			oldval = ai->end[0].xpos;  ai->end[0].xpos = c->p1;   c->p1 = oldval;
964 			oldval = ai->end[0].ypos;  ai->end[0].ypos = c->p2;   c->p2 = oldval;
965 			oldval = ai->end[1].xpos;  ai->end[1].xpos = c->p3;   c->p3 = oldval;
966 			oldval = ai->end[1].ypos;  ai->end[1].ypos = c->p4;   c->p4 = oldval;
967 			oldval = ai->width;        ai->width = c->p5;         c->p5 = oldval;
968 			oldval = ai->length;       ai->length = c->p6;        c->p6 = oldval;
969 			determineangle(ai);
970 			(void)setshrinkvalue(ai, TRUE);
971 			/* don't need to call "updategeom()" because it is done by "OBJECTSTART" */
972 			return(TRUE);
973 		case PORTPROTONEW:
974 			pp = (PORTPROTO *)c->entryaddr;
975 			db_retractportproto(pp);
976 			c->changetype = PORTPROTOKILL;
977 			return(TRUE);
978 		case PORTPROTOKILL:
979 			pp = (PORTPROTO *)c->entryaddr;
980 			db_enterportproto(pp);
981 			c->changetype = PORTPROTONEW;
982 			return(TRUE);
983 		case PORTPROTOMOD:
984 			pp = (PORTPROTO *)c->entryaddr;
985 			oldsubni = pp->subnodeinst;
986 			oldsubpp = pp->subportproto;
987 			db_changeport(pp, (NODEINST *)c->p1, (PORTPROTO *)c->p2);
988 			c->p1 = (INTBIG)oldsubni;   c->p2 = (INTBIG)oldsubpp;
989 			return(TRUE);
990 		case NODEPROTONEW:
991 			np = (NODEPROTO *)c->entryaddr;
992 			db_retractnodeproto(np);
993 			c->changetype = NODEPROTOKILL;
994 			return(TRUE);
995 		case NODEPROTOKILL:
996 			np = (NODEPROTO *)c->entryaddr;
997 			db_insertnodeproto(np);
998 			c->changetype = NODEPROTONEW;
999 			return(TRUE);
1000 		case NODEPROTOMOD:
1001 			np = (NODEPROTO *)c->entryaddr;
1002 			oldval = np->lowx;    np->lowx = c->p1;    c->p1 = oldval;
1003 			oldval = np->highx;   np->highx = c->p2;   c->p2 = oldval;
1004 			oldval = np->lowy;    np->lowy = c->p3;    c->p3 = oldval;
1005 			oldval = np->highy;   np->highy = c->p4;   c->p4 = oldval;
1006 			return(TRUE);
1007 		case OBJECTSTART:
1008 			c->changetype = OBJECTEND;
1009 			if (c->p1 == VNODEINST)
1010 			{
1011 				ni = (NODEINST *)c->entryaddr;
1012 				updategeom(ni->geom, ni->parent);
1013 			} else if (c->p1 == VARCINST)
1014 			{
1015 				ai = (ARCINST *)c->entryaddr;
1016 				updategeom(ai->geom, ai->parent);
1017 			}
1018 			break;
1019 		case OBJECTEND:
1020 			c->changetype = OBJECTSTART;
1021 			break;
1022 		case OBJECTNEW:
1023 			/* args: addr, type */
1024 			switch (c->p1&VTYPE)
1025 			{
1026 				case VVIEW:
1027 					/* find the view */
1028 					view = (VIEW *)c->entryaddr;
1029 					lastv = NOVIEW;
1030 					for(v = el_views; v != NOVIEW; v = v->nextview)
1031 					{
1032 						if (v == view) break;
1033 						lastv = v;
1034 					}
1035 					if (v != NOVIEW)
1036 					{
1037 						/* delete the view */
1038 						if (lastv == NOVIEW) el_views = v->nextview; else
1039 							lastv->nextview = v->nextview;
1040 					}
1041 					break;
1042 				case VWINDOWPART:
1043 					w = (WINDOWPART *)c->entryaddr;
1044 					db_retractwindowpart(w);
1045 					break;
1046 			}
1047 			c->changetype = OBJECTKILL;
1048 			break;
1049 		case OBJECTKILL:
1050 			/* args: addr, type */
1051 			switch (c->p1&VTYPE)
1052 			{
1053 				case VVIEW:
1054 					view = (VIEW *)c->entryaddr;
1055 					view->nextview = el_views;
1056 					el_views = view;
1057 					break;
1058 				case VWINDOWPART:
1059 					w = (WINDOWPART *)c->entryaddr;
1060 					(void)db_enterwindowpart(w);
1061 					break;
1062 			}
1063 			c->changetype = OBJECTNEW;
1064 			break;
1065 		case VARIABLENEW:
1066 			/* args: addr, type, key, newtype, newdescript[0], newdescript[1] */
1067 			if ((c->p3&VCREF) != 0)
1068 			{
1069 				/* change to fixed attribute */
1070 				attname = changedvariablename(c->p1, c->p2, c->p3);
1071 				var = getvalnoeval(c->entryaddr, c->p1, c->p3 & (VTYPE|VISARRAY), attname);
1072 				if (var == NOVARIABLE)
1073 				{
1074 					ttyputmsg(_("Warning: Could not find attribute %s on object %s"),
1075 						attname, describeobject(c->entryaddr, c->p1));
1076 					break;
1077 				}
1078 				c->p3 = var->addr;
1079 				c->p4 = var->type;
1080 				c->p5 = var->textdescript[0];
1081 				c->p6 = var->textdescript[1];
1082 				c->changetype = VARIABLEKILL;
1083 				if (c->p1 == VNODEINST || c->p1 == VARCINST) return(TRUE);
1084 				if (c->p1 == VPORTPROTO)
1085 				{
1086 					if (estrcmp(attname, x_("textdescript")) == 0 ||
1087 						estrcmp(attname, x_("userbits")) == 0 ||
1088 						estrcmp(attname, x_("protoname")) == 0) return(TRUE);
1089 				}
1090 				break;
1091 			}
1092 
1093 			/* change to variable attribute: get current value */
1094 			var = getvalkeynoeval(c->entryaddr, c->p1, c->p3&(VTYPE|VISARRAY), c->p2);
1095 			if (var == NOVARIABLE)
1096 			{
1097 				ttyputmsg(_("Warning: Could not find attribute %s on object %s"),
1098 					makename(c->p2), describeobject(c->entryaddr, c->p1));
1099 				break;
1100 			}
1101 			c->p3 = var->addr;
1102 			c->p4 = var->type;
1103 			c->p5 = var->textdescript[0];
1104 			c->p6 = var->textdescript[1];
1105 			c->changetype = VARIABLEKILL;
1106 			var->type = VINTEGER;		/* fake it out so no memory is deallocated */
1107 			nextchangequiet();
1108 			(void)delvalkey(c->entryaddr, c->p1, c->p2);
1109 			if (c->p1 == VNODEINST || c->p1 == VARCINST) return(TRUE);
1110 			break;
1111 		case VARIABLEKILL:
1112 			/* args: addr, type, key, oldaddr, oldtype, olddescript */
1113 			if ((c->p4&VCREF) != 0)
1114 			{
1115 				attname = changedvariablename(c->p1, c->p2, c->p4);
1116 				nextchangequiet();
1117 				var = setval(c->entryaddr, c->p1, attname, c->p3, c->p4);
1118 				if (var == NOVARIABLE)
1119 				{
1120 					ttyputmsg(_("Warning: Could not set attribute %s on object %s"),
1121 						attname, describeobject(c->entryaddr, c->p1));
1122 					break;
1123 				}
1124 			} else
1125 			{
1126 				nextchangequiet();
1127 				var = setvalkey(c->entryaddr, c->p1, c->p2, c->p3, c->p4);
1128 				if (var == NOVARIABLE)
1129 				{
1130 					ttyputmsg(_("Warning: Could not set attribute %s on object %s"),
1131 						makename(c->p2), describeobject(c->entryaddr, c->p1));
1132 					break;
1133 				}
1134 				var->textdescript[0] = c->p5;
1135 				var->textdescript[1] = c->p6;
1136 			}
1137 			db_freevar(c->p3, c->p4);
1138 			c->p3 = c->p4;
1139 			c->changetype = VARIABLENEW;
1140 			if (c->p1 == VNODEINST || c->p1 == VARCINST) return(TRUE);
1141 			break;
1142 		case VARIABLEMOD:
1143 			/* args: addr, type, key, vartype, aindex, oldvalue */
1144 			if ((c->p3&VCREF) != 0)
1145 			{
1146 				attname = changedvariablename(c->p1, c->p2, c->p3);
1147 				if (getind(c->entryaddr, c->p1, attname, c->p4, &oldval))
1148 				{
1149 					ttyputmsg(_("Warning: Could not find index %ld of attribute %s on object %s"),
1150 						c->p4, attname, describeobject(c->entryaddr, c->p1));
1151 					break;
1152 				}
1153 				nextchangequiet();
1154 				(void)setind(c->entryaddr, c->p1, attname, c->p4, c->p5);
1155 				c->p5 = oldval;
1156 				if (c->p1 == VPORTPROTO)
1157 				{
1158 					if (estrcmp(attname, x_("textdescript")) == 0) return(TRUE);
1159 				}
1160 			} else
1161 			{
1162 				if (getindkey(c->entryaddr, c->p1, c->p2, c->p4, &oldval))
1163 				{
1164 					ttyputmsg(_("Warning: Could not find index %ld of attribute %s on object %s"),
1165 						c->p4, makename(c->p2), describeobject(c->entryaddr, c->p1));
1166 					break;
1167 				}
1168 				if ((c->p3&(VCODE1|VCODE2)) != 0 || (c->p3&VTYPE) == VSTRING)
1169 				{
1170 					/* because this change is done quietly, the memory is freed and must be saved */
1171 					(void)allocstring(&storage, (CHAR *)oldval, db_cluster);
1172 					oldval = (INTBIG)storage;
1173 				}
1174 				nextchangequiet();
1175 				(void)setindkey(c->entryaddr, c->p1, c->p2, c->p4, c->p5);
1176 				c->p5 = oldval;
1177 			}
1178 			if (c->p1 == VNODEINST || c->p1 == VARCINST) return(TRUE);
1179 			break;
1180 		case VARIABLEINS:
1181 			/* args: addr, type, key, vartype, aindex */
1182 			if (getindkey(c->entryaddr, c->p1, c->p2, c->p4, &oldval))
1183 			{
1184 				ttyputmsg(_("Warning: Could not find index %ld of attribute %s on object %s"),
1185 					c->p4, makename(c->p2), describeobject(c->entryaddr, c->p1));
1186 				break;
1187 			}
1188 			nextchangequiet();
1189 			(void)delindkey(c->entryaddr, c->p1, c->p2, c->p4);
1190 			c->changetype = VARIABLEDEL;
1191 			c->p5 = oldval;
1192 			if (c->p1 == VNODEINST || c->p1 == VARCINST) return(TRUE);
1193 			break;
1194 		case VARIABLEDEL:
1195 			/* args: addr, type, key, vartype, aindex, oldvalue */
1196 			nextchangequiet();
1197 			(void)insindkey(c->entryaddr, c->p1, c->p2, c->p4, c->p5);
1198 			c->changetype = VARIABLEINS;
1199 			if (c->p1 == VNODEINST || c->p1 == VARCINST) return(TRUE);
1200 			break;
1201 		case DESCRIPTMOD:
1202 			var = getvalkeynoeval(c->entryaddr, c->p1, -1, c->p2);
1203 			if (var == NOVARIABLE) break;
1204 			oldval = var->textdescript[0];   var->textdescript[0] = c->p4;   c->p4 = oldval;
1205 			oldval = var->textdescript[1];   var->textdescript[1] = c->p5;   c->p5 = oldval;
1206 			return(TRUE);
1207 	}
1208 	return(FALSE);
1209 }
1210 
1211 /************************* CHANGE CELLS *************************/
1212 
1213 /*
1214  * routine to add cell "cell" to the list of cells that are being
1215  * changed in this batch.
1216  */
db_setchangecell(NODEPROTO * cell)1217 void db_setchangecell(NODEPROTO *cell)
1218 {
1219 	REGISTER CHANGECELL *cc;
1220 
1221 	if (db_currentbatch == NOCHANGEBATCH) return;
1222 	if (db_currentbatch->firstchangecell == NOCHANGECELL ||
1223 		db_currentbatch->firstchangecell->changecell != cell)
1224 	{
1225 		cc = db_allocchangecell();
1226 		cc->nextchangecell = db_currentbatch->firstchangecell;
1227 		db_currentbatch->firstchangecell = cc;
1228 		cc->changecell = cell;
1229 		cc->forcedlook = FALSE;
1230 	}
1231 }
1232 
db_removechangecell(NODEPROTO * np)1233 void db_removechangecell(NODEPROTO *np)
1234 {
1235 	REGISTER CHANGECELL *cc, *lastcc, *nextcc;
1236 
1237 	if (db_currentbatch == NOCHANGEBATCH) return;
1238 	lastcc = NOCHANGECELL;
1239 	for(cc = db_currentbatch->firstchangecell; cc != NOCHANGECELL; cc = nextcc)
1240 	{
1241 		nextcc = cc->nextchangecell;
1242 		if (cc->changecell == np)
1243 		{
1244 			if (lastcc == NOCHANGECELL) db_currentbatch->firstchangecell = nextcc; else
1245 				lastcc->nextchangecell = nextcc;
1246 			db_freechangecell(cc);
1247 			return;
1248 		}
1249 		lastcc = cc;
1250 	}
1251 }
1252 
1253 /*
1254  * Routine to ensure that cell "np" is given a hierarchical analysis by the
1255  * constraint system.
1256  */
db_forcehierarchicalanalysis(NODEPROTO * np)1257 void db_forcehierarchicalanalysis(NODEPROTO *np)
1258 {
1259 	REGISTER CHANGECELL *cc;
1260 
1261 	if (db_currentbatch == NOCHANGEBATCH) return;
1262 	for(cc = db_currentbatch->firstchangecell; cc != NOCHANGECELL; cc = cc->nextchangecell)
1263 		if (cc->changecell == np)
1264 	{
1265 		cc->forcedlook = TRUE;
1266 		return;
1267 	}
1268 
1269 	/* if not in the list, create the entry and try again */
1270 	db_setchangecell(np);
1271 	db_forcehierarchicalanalysis(np);
1272 }
1273 
1274 /************************* USER INTERFACE *************************/
1275 
1276 /*
1277  * routine to display history list entry "which" (if "which" is negative,
1278  * display the entire list
1279  */
showhistorylist(INTBIG which)1280 void showhistorylist(INTBIG which)
1281 {
1282 	REGISTER INTBIG node, arcinst, portproto, nproto, object, variable;
1283 	REGISTER INTBIG lobatch, hibatch;
1284 	REGISTER CHANGEBATCH *batchreport;
1285 	REGISTER CHANGE *c;
1286 	CHAR line[50];
1287 	REGISTER void *infstr;
1288 
1289 	/* specific display of a batch */
1290 	if (which >= 0)
1291 	{
1292 		lobatch = hibatch = db_tailbatch->batchnumber;
1293 		for(batchreport = db_tailbatch; batchreport != NOCHANGEBATCH;
1294 			batchreport = batchreport->nextchangebatch)
1295 		{
1296 			if (batchreport->batchnumber < lobatch) lobatch = batchreport->batchnumber;
1297 			if (batchreport->batchnumber > hibatch) hibatch = batchreport->batchnumber;
1298 			if (batchreport->batchnumber != which) continue;
1299 			infstr = initinfstr();
1300 			formatinfstr(infstr, M_("Batch %ld from %s tool"), batchreport->batchnumber,
1301 				batchreport->tool->toolname);
1302 			if (batchreport->activity != 0)
1303 				formatinfstr(infstr, M_(", caused by '%s'"), batchreport->activity);
1304 			addstringtoinfstr(infstr, M_(" is:"));
1305 			ttyputmsg(x_("%s"), returninfstr(infstr));
1306 
1307 			for(c = batchreport->changehead; c != NOCHANGE; c = c->nextchange)
1308 				ttyputmsg(x_("%s"), db_describechange(c->changetype, c->entryaddr, c->p1, c->p2,
1309 					c->p3, c->p4, c->p5, c->p6));
1310 			return;
1311 		}
1312 		ttyputmsg(M_("Batch %ld is not in the list (range is %ld to %ld)"), which, lobatch, hibatch);
1313 		return;
1314 	}
1315 
1316 	/* display the change batches */
1317 	ttyputmsg(M_("%ld batches (limit is %ld):"), db_batchtally-1, db_maximumbatches);
1318 	for(batchreport = db_tailbatch; batchreport != NOCHANGEBATCH;
1319 		batchreport = batchreport->nextchangebatch)
1320 	{
1321 		node = arcinst = portproto = nproto = object = variable = 0;
1322 		for(c = batchreport->changehead; c != NOCHANGE; c = c->nextchange)
1323 			switch (c->changetype)
1324 		{
1325 				case NODEINSTNEW:
1326 				case NODEINSTKILL:
1327 				case NODEINSTMOD:
1328 					node++;
1329 					break;
1330 				case ARCINSTNEW:
1331 				case ARCINSTKILL:
1332 				case ARCINSTMOD:
1333 					arcinst++;
1334 					break;
1335 				case PORTPROTONEW:
1336 				case PORTPROTOKILL:
1337 				case PORTPROTOMOD:
1338 					portproto++;
1339 					break;
1340 				case NODEPROTONEW:
1341 				case NODEPROTOKILL:
1342 				case NODEPROTOMOD:
1343 					nproto++;
1344 					break;
1345 				case OBJECTNEW:
1346 				case OBJECTKILL:
1347 					object++;
1348 					break;
1349 				case VARIABLENEW:
1350 				case VARIABLEKILL:
1351 				case VARIABLEMOD:
1352 				case VARIABLEINS:
1353 				case VARIABLEDEL:
1354 					variable++;
1355 					break;
1356 				case DESCRIPTMOD:
1357 					if ((VARIABLE *)c->p1 != NOVARIABLE) variable++; else
1358 						if ((PORTPROTO *)c->p3 != NOPORTPROTO) portproto++; else
1359 							node++;
1360 					break;
1361 		}
1362 
1363 		infstr = initinfstr();
1364 		(void)esnprintf(line, 50, x_("%ld {"), batchreport->batchnumber);
1365 		addstringtoinfstr(infstr, line);
1366 		if (batchreport->activity != 0) addstringtoinfstr(infstr, batchreport->activity);
1367 		addstringtoinfstr(infstr, M_("} affects"));
1368 
1369 		if (node != 0)
1370 		{
1371 			(void)esnprintf(line, 50, x_(" %ld %s"), node, makeplural(M_("node"), node));
1372 			addstringtoinfstr(infstr, line);
1373 		}
1374 		if (arcinst != 0)
1375 		{
1376 			(void)esnprintf(line, 50, x_(" %ld %s"), arcinst, makeplural(M_("arc"), arcinst));
1377 			addstringtoinfstr(infstr, line);
1378 		}
1379 		if (portproto != 0)
1380 		{
1381 			(void)esnprintf(line, 50, x_(" %ld %s"), portproto, makeplural(M_("port"), portproto));
1382 			addstringtoinfstr(infstr, line);
1383 		}
1384 		if (nproto != 0)
1385 		{
1386 			(void)esnprintf(line, 50, x_(" %ld %s"), nproto, makeplural(M_("cell"), nproto));
1387 			addstringtoinfstr(infstr, line);
1388 		}
1389 		if (object != 0)
1390 		{
1391 			(void)esnprintf(line, 50, x_(" %ld %s"), object, makeplural(M_("object"), object));
1392 			addstringtoinfstr(infstr, line);
1393 		}
1394 		if (variable != 0)
1395 		{
1396 			(void)esnprintf(line, 50, x_(" %ld %s"), variable, makeplural(M_("variable"), variable));
1397 			addstringtoinfstr(infstr, line);
1398 		}
1399 		ttyputmsg(x_("%s"), returninfstr(infstr));
1400 	}
1401 }
1402 
db_describechange(INTBIG changetype,INTBIG entryaddr,INTBIG p1,INTBIG p2,INTBIG p3,INTBIG p4,INTBIG p5,INTBIG p6)1403 CHAR *db_describechange(INTBIG changetype, INTBIG entryaddr, INTBIG p1, INTBIG p2,
1404 	INTBIG p3, INTBIG p4, INTBIG p5, INTBIG p6)
1405 {
1406 	REGISTER NODEINST *ni;
1407 	REGISTER ARCINST *ai;
1408 	REGISTER PORTPROTO *pp;
1409 	REGISTER NODEPROTO *np;
1410 	REGISTER INTBIG lambda;
1411 	VARIABLE myvar;
1412 	REGISTER void *infstr;
1413 
1414 	switch (changetype)
1415 	{
1416 		case NODEINSTNEW:
1417 			ni = (NODEINST *)entryaddr;
1418 			infstr = initinfstr();
1419 			formatinfstr(infstr, M_(" Node '%s' created in cell %s"), describenodeinst(ni),
1420 				describenodeproto(ni->parent));
1421 			return(returninfstr(infstr));
1422 		case NODEINSTKILL:
1423 			ni = (NODEINST *)entryaddr;
1424 			infstr = initinfstr();
1425 			formatinfstr(infstr, M_(" Node '%s' killed in cell %s"), describenodeinst(ni),
1426 				describenodeproto(ni->parent));
1427 			return(returninfstr(infstr));
1428 		case NODEINSTMOD:
1429 			ni = (NODEINST *)entryaddr;
1430 			lambda = lambdaofnode(ni);
1431 			infstr = initinfstr();
1432 			formatinfstr(infstr, M_(" Node '%s' changed in cell %s [was %sx%s, center (%s,%s), rotated %ld"),
1433 				describenodeinst(ni), describenodeproto(ni->parent), latoa(p3-p1, lambda),
1434 				latoa(p4-p2, lambda), latoa((p1+p3)/2, lambda), latoa((p2+p4)/2, lambda), p5);
1435 			if (p6 != 0) addstringtoinfstr(infstr, M_(", transposed"));
1436 			addstringtoinfstr(infstr, x_("]"));
1437 			return(returninfstr(infstr));
1438 		case ARCINSTNEW:
1439 			ai = (ARCINST *)entryaddr;
1440 			infstr = initinfstr();
1441 			formatinfstr(infstr, M_(" Arc '%s' created in cell %s"), describearcinst(ai),
1442 				describenodeproto(ai->parent));
1443 			return(returninfstr(infstr));
1444 		case ARCINSTKILL:
1445 			ai = (ARCINST *)entryaddr;
1446 			infstr = initinfstr();
1447 			formatinfstr(infstr, M_(" Arc '%s' killed in cell %s"), describearcinst(ai),
1448 				describenodeproto(ai->parent));
1449 			return(returninfstr(infstr));
1450 		case ARCINSTMOD:
1451 			ai = (ARCINST *)entryaddr;
1452 			lambda = lambdaofarc(ai);
1453 			infstr = initinfstr();
1454 			formatinfstr(infstr, M_(" Arc '%s' modified in cell %s [was %s wide %s long from (%s,%s) to (%s,%s)]"),
1455 				describearcinst(ai), describenodeproto(ai->parent), latoa(p5, lambda), latoa(p6, lambda),
1456 					latoa(p1, lambda), latoa(p2, lambda), latoa(p3, lambda), latoa(p4, lambda));
1457 			return(returninfstr(infstr));
1458 		case PORTPROTONEW:
1459 			pp = (PORTPROTO *)entryaddr;
1460 			infstr = initinfstr();
1461 			formatinfstr(infstr, M_(" Port '%s' created in cell %s"), pp->protoname,
1462 				describenodeproto(pp->parent));
1463 			return(returninfstr(infstr));
1464 		case PORTPROTOKILL:
1465 			pp = (PORTPROTO *)entryaddr;
1466 			infstr = initinfstr();
1467 			formatinfstr(infstr, M_(" Port '%s' killed in cell %s"), pp->protoname,
1468 				describenodeproto(pp->parent));
1469 			return(returninfstr(infstr));
1470 		case PORTPROTOMOD:
1471 			pp = (PORTPROTO *)entryaddr;
1472 			infstr = initinfstr();
1473 			formatinfstr(infstr, M_(" Port '%s' moved in cell %s [was on node %s, subport %s]"),
1474 				pp->protoname, describenodeproto(pp->parent), describenodeinst((NODEINST *)p1),
1475 				((PORTPROTO *)p2)->protoname);
1476 			return(returninfstr(infstr));
1477 		case NODEPROTONEW:
1478 			np = (NODEPROTO *)entryaddr;
1479 			infstr = initinfstr();
1480 			formatinfstr(infstr, M_(" Cell '%s' created"), describenodeproto(np));
1481 			return(returninfstr(infstr));
1482 		case NODEPROTOKILL:
1483 			np = (NODEPROTO *)entryaddr;
1484 			infstr = initinfstr();
1485 			formatinfstr(infstr, M_(" Cell %s' killed"), describenodeproto(np));
1486 			return(returninfstr(infstr));
1487 		case NODEPROTOMOD:
1488 			np = (NODEPROTO *)entryaddr;
1489 			lambda = lambdaofcell(np);
1490 			infstr = initinfstr();
1491 			formatinfstr(infstr, M_(" Cell '%s' modified [was from %s<=X<=%s %s<=Y<=%s]"),
1492 				describenodeproto(np), latoa(p1, lambda), latoa(p2, lambda),
1493 					latoa(p3, lambda), latoa(p4, lambda));
1494 			return(returninfstr(infstr));
1495 		case OBJECTSTART:
1496 			/* args: addr, type */
1497 			infstr = initinfstr();
1498 			formatinfstr(infstr, M_(" Start change to object %s"), describeobject(entryaddr, p1));
1499 			return(returninfstr(infstr));
1500 		case OBJECTEND:
1501 			/* args: addr, type */
1502 			infstr = initinfstr();
1503 			formatinfstr(infstr, M_(" End change to object %s"), describeobject(entryaddr, p1));
1504 			return(returninfstr(infstr));
1505 		case OBJECTNEW:
1506 			/* args: addr, type */
1507 			infstr = initinfstr();
1508 			formatinfstr(infstr, M_(" Object %s created"), describeobject(entryaddr, p1));
1509 			return(returninfstr(infstr));
1510 		case OBJECTKILL:
1511 			/* args: addr, type */
1512 			infstr = initinfstr();
1513 			formatinfstr(infstr, M_(" Object %s deleted"), describeobject(entryaddr, p1));
1514 			return(returninfstr(infstr));
1515 		case VARIABLENEW:
1516 			/* args: addr, type, key, newtype */
1517 			infstr = initinfstr();
1518 			formatinfstr(infstr, M_(" Variable '%s' created on %s"), changedvariablename(p1, p2, p3),
1519 				describeobject(entryaddr, p1));
1520 			return(returninfstr(infstr));
1521 		case VARIABLEKILL:
1522 			/* args: addr, type, key, oldaddr, oldtype, olddescript[0], olddescript[1] */
1523 			infstr = initinfstr();
1524 			myvar.key = p2;  myvar.type = p4;  myvar.addr = p3;
1525 			formatinfstr(infstr, M_(" Variable '%s' killed on %s [was %s]"), changedvariablename(p1, p2, p4),
1526 				describeobject(entryaddr, p1), describevariable(&myvar, -1, -1));
1527 			return(returninfstr(infstr));
1528 		case VARIABLEMOD:
1529 			/* args: addr, type, key, vartype, aindex, oldvalue */
1530 			infstr = initinfstr();
1531 			formatinfstr(infstr, M_(" Variable '%s[%ld]' changed on %s"), changedvariablename(p1, p2, p3), p4,
1532 				describeobject(entryaddr, p1));
1533 			if ((p3&VCREF) == 0)
1534 			{
1535 				myvar.key = p2;  myvar.type = p3 & ~VISARRAY;
1536 				myvar.addr = p5;
1537 				formatinfstr(infstr, M_(" [was '%s']"), describevariable(&myvar, -1, -1));
1538 			}
1539 			return(returninfstr(infstr));
1540 		case VARIABLEINS:
1541 			/* args: addr, type, key, vartype, aindex */
1542 			infstr = initinfstr();
1543 			formatinfstr(infstr, M_(" Variable '%s[%ld]' inserted on %s"), changedvariablename(p1, p2, p3), p4,
1544 				describeobject(entryaddr, p1));
1545 			return(returninfstr(infstr));
1546 		case VARIABLEDEL:
1547 			/* args: addr, type, key, vartype, aindex, oldvalue */
1548 			infstr = initinfstr();
1549 			formatinfstr(infstr, M_(" Variable '%s[%ld]' deleted on %s"), changedvariablename(p1, p2, p3), p4,
1550 				describeobject(entryaddr, p1));
1551 			return(returninfstr(infstr));
1552 		case DESCRIPTMOD:
1553 			infstr = initinfstr();
1554 			formatinfstr(infstr, M_(" Text descriptor on variable %s changed [was 0%lo/0%lo]"),
1555 				makename(p2), p4, p5);
1556 			return(returninfstr(infstr));
1557 	}
1558 	return(x_("?"));
1559 }
1560 
1561 /* Undo dialog */
1562 static DIALOGITEM db_undodialogitems[] =
1563 {
1564  /*  1 */ {0, {468,608,492,680}, BUTTON, N_("OK")},
1565  /*  2 */ {0, {32,8,455,690}, SCROLL, x_("")},
1566  /*  3 */ {0, {468,16,492,88}, BUTTON, N_("Undo")},
1567  /*  4 */ {0, {8,8,24,241}, MESSAGE, N_("These are the recent changes:")},
1568  /*  5 */ {0, {8,344,24,456}, CHECK, N_("Show details")},
1569  /*  6 */ {0, {468,108,492,180}, BUTTON, N_("Redo")}
1570 };
1571 static DIALOG db_undodialog = {{50,75,555,775}, N_("Change History"), 0, 6, db_undodialogitems, 0, 0};
1572 
1573 /* special items in the "undo" dialog: */
1574 #define DUND_CHGLIST   2		/* Change list (scroll) */
1575 #define DUND_UNDO      3		/* Undo (button) */
1576 #define DUND_DETAILS   5		/* Show detail (check box) */
1577 #define DUND_REDO      6		/* Redo (button) */
1578 
db_undodlog(void)1579 void db_undodlog(void)
1580 {
1581 	INTBIG itemHit, details;
1582 	TOOL *tool;
1583 	REGISTER void *dia;
1584 
1585 	/* display the undo dialog box */
1586 	dia = DiaInitDialog(&db_undodialog);
1587 	if (dia == 0) return;
1588 	DiaInitTextDialog(dia, DUND_CHGLIST, DiaNullDlogList, DiaNullDlogItem, DiaNullDlogDone,
1589 		-1, SCHORIZBAR|SCSMALLFONT);
1590 	details = 0;
1591 	db_loadhistorylist(details, dia);
1592 
1593 	/* default number of changes to undo */
1594 	DiaSelectLine(dia, DUND_CHGLIST, 1000);
1595 
1596 	/* loop until done */
1597 	for(;;)
1598 	{
1599 		itemHit = DiaNextHit(dia);
1600 		if (itemHit == OK) break;
1601 		if (itemHit == DUND_DETAILS)
1602 		{
1603 			details = 1 - details;
1604 			DiaSetControl(dia, DUND_DETAILS, details);
1605 			db_loadhistorylist(details, dia);
1606 			continue;
1607 		}
1608 		if (itemHit == DUND_UNDO)
1609 		{
1610 			(void)undoabatch(&tool);
1611 			db_loadhistorylist(details, dia);
1612 			continue;
1613 		}
1614 		if (itemHit == DUND_REDO)
1615 		{
1616 			(void)redoabatch(&tool);
1617 			db_loadhistorylist(details, dia);
1618 			continue;
1619 		}
1620 	}
1621 
1622 	DiaDoneDialog(dia);
1623 }
1624 
1625 /*
1626  * helper routines for undo dialog
1627  */
db_loadhistorylist(INTBIG details,void * dia)1628 void db_loadhistorylist(INTBIG details, void *dia)
1629 {
1630 	REGISTER INTBIG node, arcinst, portproto, nproto, object, variable, lineno, curline;
1631 	REGISTER CHANGE *c;
1632 	CHAR line[50];
1633 	CHANGEBATCH *batch;
1634 	REGISTER void *infstr;
1635 
1636 	DiaLoadTextDialog(dia, DUND_CHGLIST, DiaNullDlogList, DiaNullDlogItem, DiaNullDlogDone, -1);
1637 	lineno = 0;
1638 	curline = -1;
1639 	for(batch = db_tailbatch; batch != NOCHANGEBATCH; batch = batch->nextchangebatch)
1640 	{
1641 		node = arcinst = portproto = nproto = object = variable = 0;
1642 		for(c = batch->changehead; c != NOCHANGE; c = c->nextchange)
1643 			switch (c->changetype)
1644 		{
1645 				case NODEINSTNEW:
1646 				case NODEINSTKILL:
1647 				case NODEINSTMOD:
1648 					node++;
1649 					break;
1650 				case ARCINSTNEW:
1651 				case ARCINSTKILL:
1652 				case ARCINSTMOD:
1653 					arcinst++;
1654 					break;
1655 				case PORTPROTONEW:
1656 				case PORTPROTOKILL:
1657 				case PORTPROTOMOD:
1658 					portproto++;
1659 					break;
1660 				case NODEPROTONEW:
1661 				case NODEPROTOKILL:
1662 				case NODEPROTOMOD:
1663 					nproto++;
1664 					break;
1665 				case OBJECTNEW:
1666 				case OBJECTKILL:
1667 					object++;
1668 					break;
1669 				case VARIABLENEW:
1670 				case VARIABLEKILL:
1671 				case VARIABLEMOD:
1672 				case VARIABLEINS:
1673 				case VARIABLEDEL:
1674 				case DESCRIPTMOD:
1675 					variable++;
1676 					break;
1677 		}
1678 
1679 		infstr = initinfstr();
1680 		if (details != 0) addstringtoinfstr(infstr, M_("***** BATCH "));
1681 		(void)esnprintf(line, 50, x_("%ld {"), batch->batchnumber);
1682 		addstringtoinfstr(infstr, line);
1683 		if (batch->activity != 0) addstringtoinfstr(infstr, batch->activity);
1684 		addstringtoinfstr(infstr, M_("} affects"));
1685 
1686 		if (node != 0)
1687 		{
1688 			(void)esnprintf(line, 50, x_(" %ld %s"), node, makeplural(M_("node"), node));
1689 			addstringtoinfstr(infstr, line);
1690 		}
1691 		if (arcinst != 0)
1692 		{
1693 			(void)esnprintf(line, 50, x_(" %ld %s"), arcinst, makeplural(M_("arc"), arcinst));
1694 			addstringtoinfstr(infstr, line);
1695 		}
1696 		if (portproto != 0)
1697 		{
1698 			(void)esnprintf(line, 50, x_(" %ld %s"), portproto, makeplural(M_("port"), portproto));
1699 			addstringtoinfstr(infstr, line);
1700 		}
1701 		if (nproto != 0)
1702 		{
1703 			(void)esnprintf(line, 50, x_(" %ld %s"), nproto, makeplural(M_("cell"), nproto));
1704 			addstringtoinfstr(infstr, line);
1705 		}
1706 		if (object != 0)
1707 		{
1708 			(void)esnprintf(line, 50, x_(" %ld %s"), object, makeplural(M_("object"), object));
1709 			addstringtoinfstr(infstr, line);
1710 		}
1711 		if (variable != 0)
1712 		{
1713 			(void)esnprintf(line, 50, x_(" %ld %s"), variable, makeplural(M_("variable"), variable));
1714 			addstringtoinfstr(infstr, line);
1715 		}
1716 		DiaStuffLine(dia, DUND_CHGLIST, returninfstr(infstr));
1717 		if (batch == db_donebatch) curline = lineno;
1718 		lineno++;
1719 
1720 		if (details != 0)
1721 		{
1722 			for(c = batch->changehead; c != NOCHANGE; c = c->nextchange)
1723 			{
1724 				DiaStuffLine(dia, DUND_CHGLIST, db_describechange(c->changetype, c->entryaddr, c->p1, c->p2,
1725 					c->p3, c->p4, c->p5, c->p6));
1726 				lineno++;
1727 			}
1728 		}
1729 	}
1730 	DiaSelectLine(dia, DUND_CHGLIST, curline);
1731 }
1732 
1733 /************************* MEMORY ALLOCATION *************************/
1734 
1735 /*
1736  * routine to allocate a change module from the pool (if any) or memory
1737  * the routine returns NOCHANGE if allocation fails.
1738  */
db_allocchange(void)1739 CHANGE *db_allocchange(void)
1740 {
1741 	REGISTER CHANGE *c;
1742 
1743 	/* code cannot be called by multiple procesors: uses globals */
1744 	NOT_REENTRANT;
1745 
1746 	if (db_changefree == NOCHANGE)
1747 	{
1748 		c = (CHANGE *)emalloc((sizeof (CHANGE)), db_cluster);
1749 		if (c == 0) return(NOCHANGE);
1750 		return(c);
1751 	}
1752 
1753 	/* take change module from free list */
1754 	c = db_changefree;
1755 	db_changefree = db_changefree->nextchange;
1756 	return(c);
1757 }
1758 
1759 /*
1760  * routine to free a change module
1761  */
db_freechange(CHANGE * c)1762 void db_freechange(CHANGE *c)
1763 {
1764 	c->nextchange = db_changefree;
1765 	db_changefree = c;
1766 }
1767 
1768 /*
1769  * routine to allocate a changecell module from the pool (if any) or memory
1770  * the routine returns NOCHANGECELL if allocation fails.
1771  */
db_allocchangecell(void)1772 CHANGECELL *db_allocchangecell(void)
1773 {
1774 	REGISTER CHANGECELL *c;
1775 
1776 	if (db_changecellfree == NOCHANGECELL)
1777 	{
1778 		c = (CHANGECELL *)emalloc((sizeof (CHANGECELL)), db_cluster);
1779 		if (c == 0) return(NOCHANGECELL);
1780 		return(c);
1781 	}
1782 
1783 	/* take change module from free list */
1784 	c = db_changecellfree;
1785 	db_changecellfree = db_changecellfree->nextchangecell;
1786 	return(c);
1787 }
1788 
1789 /*
1790  * routine to free a changecell module
1791  */
db_freechangecell(CHANGECELL * c)1792 void db_freechangecell(CHANGECELL *c)
1793 {
1794 	c->nextchangecell = db_changecellfree;
1795 	db_changecellfree = c;
1796 }
1797 
1798 /*
1799  * routine to allocate a change batch from the pool (if any) or memory
1800  * the routine returns NOCHANGEBATCH if allocation fails.
1801  */
db_allocchangebatch(void)1802 CHANGEBATCH *db_allocchangebatch(void)
1803 {
1804 	REGISTER CHANGEBATCH *b;
1805 
1806 	/* code cannot be called by multiple procesors: uses globals */
1807 	NOT_REENTRANT;
1808 
1809 	if (db_changebatchfree == NOCHANGEBATCH)
1810 	{
1811 		/* no free change batches...allocate one */
1812 		b = (CHANGEBATCH *)emalloc((sizeof (CHANGEBATCH)), db_cluster);
1813 		if (b == 0) return(NOCHANGEBATCH);
1814 	} else
1815 	{
1816 		/* take batch from free list */
1817 		b = db_changebatchfree;
1818 		db_changebatchfree = db_changebatchfree->nextchangebatch;
1819 	}
1820 	return(b);
1821 }
1822 
1823 /*
1824  * routine to free a change batch
1825  */
db_freechangebatch(CHANGEBATCH * b)1826 void db_freechangebatch(CHANGEBATCH *b)
1827 {
1828 	b->nextchangebatch = db_changebatchfree;
1829 	db_changebatchfree = b;
1830 }
1831