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