1 /*
2 * undo.c --
3 *
4 * Undo/redo module.
5 *
6 * The undo package records a series of invertible editing events
7 * in a log maintained in main memory.
8 *
9 * The current state may be rewound back toward the time the editing
10 * session began, and it may be replayed forward as well.
11 *
12 * *********************************************************************
13 * * Copyright (C) 1985, 1990 Regents of the University of California. *
14 * * Permission to use, copy, modify, and distribute this *
15 * * software and its documentation for any purpose and without *
16 * * fee is hereby granted, provided that the above copyright *
17 * * notice appear in all copies. The University of California *
18 * * makes no representations about the suitability of this *
19 * * software for any purpose. It is provided "as is" without *
20 * * express or implied warranty. Export of this software outside *
21 * * of the United States of America may require an export license. *
22 * *********************************************************************
23 */
24
25 #ifndef lint
26 static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/utils/undo.c,v 1.1.1.1 2008/02/03 20:43:50 tim Exp $";
27 #endif /* not lint */
28
29 #include <stdio.h>
30 #include <sys/types.h>
31
32 #include "utils/magic.h"
33 #include "utils/utils.h"
34 #include "utils/malloc.h"
35 #include "utils/undo.h"
36
37 /* ------------------------------------------------------------------------ */
38
39 /*
40 * CONFIGURATION INFORMATION
41 */
42
43 #define MAXUNDOCLIENTS 50 /* Maximum number of calls to UndoAddClient */
44
45 /*
46 * MAXCOMMANDS is the maximum number of delimited event sequences
47 * ("commands") retained in main memory. LOWCOMMANDS is a low-water
48 * mark for the number of commands in memory; whenever we free up
49 * commands, we do so until there are no more than LOWCOMMANDS in
50 * main memory.
51 *
52 * >>>>> In the current implementation, these two are the same <<<<<
53 */
54
55 #define MAXCOMMANDS 1000
56 #define LOWCOMMANDS 1000 /* Must be > 0 ! */
57
58 /* ------------------------------------------------------------------------ */
59
60 /*
61 * The following structure describes the basic information
62 * required by the undo package for each event it stores.
63 * This information is NOT intended to be visible to any of
64 * the clients of the undo package and is susceptible to being
65 * changed arbitrarily.
66 *
67 * To enforce the absolute ignorance of undo's clients, when
68 * we allocate an internalUndoEvent, we only give the client
69 * a pointer to the iue_client part, which is of a size determined
70 * by the client when it calls UndoNewEvent(). This pointer is
71 * what the client sees as an (UndoEvent *) (really a (char *)).
72 */
73
74 typedef struct ue
75 {
76 UndoType iue_type; /* Event type */
77 struct ue *iue_back; /* Previous event on list */
78 struct ue *iue_forw; /* Next event on list */
79 int iue_client; /* Client data area. This is merely a
80 * dummy placeholder; the actual size
81 * of one of these structures is
82 * determined at the time of
83 * UndoNewEvent().
84 */
85 } internalUndoEvent;
86
87 #define UT_DELIM (-1)
88
89 /*
90 * The following macro is used to compute the number of bytes we must
91 * allocate in order to give the user an UndoEvent capable of holding
92 * n bytes.
93 */
94
95 #define undoSize(n) (sizeof (struct ue) + (n) \
96 - sizeof (((struct ue *) 0)->iue_client))
97
98 /*
99 * Mapping between internal and external undo event pointers.
100 * When the undo package hands an (UndoEvent *) to a client, it is
101 * really a pointer to the iue_client part of the structure
102 * above.
103 */
104
105 #define CLIENTOFFSET ((int) &((internalUndoEvent) 0)->iue_client)
106 #define undoExport(p) ((UndoEvent *) (&(p)->iue_client))
107 #define undoImport(p) ((internalUndoEvent) (((char *) (p)) - CLIENTOFFSET))
108
109 /*
110 * The following table is used to record the information about clients
111 * of the undo package. The number of such clients is stored in
112 * undoNumClients.
113 */
114
115 typedef struct
116 {
117 char *uc_name; /* Name (for error messages) */
118 void (*uc_init)(); /* Called before playing log */
119 void (*uc_done)(); /* Called after playing log */
120 void (*uc_forw)(); /* Play event forward */
121 void (*uc_back)(); /* Play event backward */
122 } undoClient;
123
124 undoClient undoClientTable[MAXUNDOCLIENTS];
125 int undoNumClients = 0;
126
127 /*
128 * undoState is used by UndoNewEvent() to figure out the context from which
129 * it was called in order that it may link the newly allocated event
130 * in the appropriate way. Events are only linked in when the state
131 * is US_APPEND. The only reason for having two other states instead
132 * of one is for ease in debugging.
133 * UndoDisableCount counts the number of times UndoDisable() has been called.
134 */
135
136 #define US_APPEND 0 /* Normal state: appending to log */
137 #define US_FORWARD 1 /* Playing log forward */
138 #define US_BACKWARD 2 /* Playing log backward */
139
140 int undoState = US_APPEND;
141 global int UndoDisableCount = 0;
142
143 /*
144 * Log of events kept in main memory.
145 *
146 * undoLogHead Pointer to first entry stored in main memory.
147 * - NULL, indicating no events are in memory
148 * - a pointer to the first event of a command
149 * undoLogTail Pointer to last entry stored in main memory.
150 * - Undefined (if undoLogHead == NULL)
151 * - a pointer to a UT_DELIM event if
152 * undoNumRecentEvents == 0
153 * - a pointer to a non-UT_DELIM event if
154 * undoNumRecentEvents != 0
155 * undoLogCur Pointer to "current" event, ie, one after which
156 * next event will be added.
157 * - NULL if at beginning of event list
158 * - a pointer to a UT_DELIM event if
159 * undoNumRecentEvents == 0
160 * - a pointer to a non-UT_DELIM event if
161 * undoNumRecentEvents != 0
162 *
163 * undoNumRecentEvents
164 * Number of events written since last call to
165 * UndoNext().
166 * undoNumCommands
167 * Number of complete commands in main memory.
168 */
169
170 internalUndoEvent *undoLogCur;
171 internalUndoEvent *undoLogHead;
172 internalUndoEvent *undoLogTail;
173 int undoNumRecentEvents;
174 int undoNumCommands;
175
176 /*
177 * ============================================================================
178 *
179 * The following collection of procedures completely defines
180 * the interface the undo package presents to its clients.
181 *
182 * ============================================================================
183 */
184
185 extern internalUndoEvent *undoGetForw();
186 extern internalUndoEvent *undoGetBack();
187 extern void undoFreeHead();
188 extern void undoMemTruncate();
189
190 /*
191 * ----------------------------------------------------------------------------
192 *
193 * UndoInit --
194 *
195 * Initialize the undo package.
196 * Opens the given file as an undo log.
197 * One of several modes may be specified. In all cases, the log file
198 * is opened so that playback will commence from the beginning of the
199 * file.
200 *
201 * "r" The log file is opened for playback only; no new events
202 * may be played out to the file.
203 * This mode is intended for use in recovering from a crash
204 * without clobbering the crash log.
205 *
206 * "w" The log file is created afresh and is available to the
207 * undo package for use in logging events.
208 * This is the normal mode for starting a new session.
209 *
210 * "rw" The log file is opened for playback and logging.
211 * Any new events written before the entire log has played
212 * forward will cause the remaining events in the log to be
213 * truncated.
214 * This is the normal mode for crash recovery.
215 *
216 * Results:
217 * TRUE if the log file could be successfully created/opened, FALSE
218 * if an error was encountered. In the latter case, the external
219 * variable errno is set to the UNIX error encountered.
220 *
221 * Side effects:
222 * Initializes all the variables in the undo package.
223 *
224 * ----------------------------------------------------------------------------
225 */
226
227 bool
UndoInit(logFileName,mode)228 UndoInit(logFileName, mode)
229 char *logFileName; /* Name of log file. This may contain tilde
230 * abbreviations.
231 */
232 char *mode; /* Mode for opening. Must be "r", "rw", or "w" */
233 {
234 UndoDisableCount = 0;
235 undoLogTail = NULL;
236 undoLogCur = NULL;
237 undoNumRecentEvents = 0;
238 undoNumCommands = 0;
239
240 /*
241 * Deallocate any events stored in main memory
242 */
243
244 while (undoLogHead != (internalUndoEvent *) NULL)
245 {
246 freeMagic((char *) undoLogHead);
247 undoLogHead = undoLogHead->iue_forw;
248 }
249
250 return (TRUE);
251 }
252
253 /*
254 * ----------------------------------------------------------------------------
255 *
256 * UndoAddClient --
257 *
258 * Used by a client to make itself known to the undo package.
259 * The client must supply the following information:
260 *
261 * 1. A procedure to call before starting an undo or redo
262 * operation.
263 *
264 * 2. A procedure to call upon completion of an undo or
265 * redo operation.
266 *
267 * 3. A procedure to parse a line in the undo log file and
268 * return a filled-in UndoEvent (allocated, of course,
269 * by a call to UndoNewEvent()) that is the internal
270 * representation of the event externally represented
271 * by the supplied line of text from the log file.
272 *
273 * UndoEvent *
274 * readEvent(line)
275 * char *line;
276 * {
277 * }
278 *
279 * 4. A procedure to fill in a line (null-terminated with
280 * no newlines embedded or trailing) with the external
281 * representation of an event based on an UndoEvent
282 * supplied to it. This prodedure returns the number
283 * of bytes it stored in the line (which must be less
284 * than a constant, UNDOLINESIZE).
285 *
286 * int
287 * writeEvent(event, line)
288 * UndoEvent *event;
289 * char *line;
290 * {
291 * }
292 *
293 * If either readEvent or writeEvent are NULL, a default procedure
294 * is used which simply uses the same internal and external representation
295 * for the undo event (ie, a text string).
296 *
297 * 5. A procedure to play an event forward.
298 *
299 * void
300 * forwEvent(event)
301 * UndoEvent *event;
302 * {
303 * }
304 *
305 * 6. A procedure to play an event backward.
306 *
307 * void
308 * backEvent(event)
309 * UndoEvent *event;
310 * {
311 * }
312 *
313 * 7. A client name, for error messages. This is a character
314 * string.
315 *
316 * Results:
317 * Returns an UndoType which must be passed in future calls
318 * to UndoNewEvent(). If -1 is returned, this means that there
319 * are too many clients of the undo package.
320 *
321 * Side effects:
322 * Initializes local state in the undo package.
323 *
324 * ----------------------------------------------------------------------------
325 */
326
327 UndoType
328 UndoAddClient(init, done, readEvent, writeEvent, forwEvent, backEvent, name)
329 void (*init)();
330 void (*done)();
331 UndoEvent *(*readEvent)();
332 int (*writeEvent)();
333 void (*forwEvent)(), (*backEvent)();
334 char *name;
335 {
336 if (undoNumClients >= MAXUNDOCLIENTS)
337 return ((UndoType) -1);
338
339 undoClientTable[undoNumClients].uc_name = StrDup((char **) NULL, name);
340 undoClientTable[undoNumClients].uc_forw = forwEvent;
341 undoClientTable[undoNumClients].uc_back = backEvent;
342 undoClientTable[undoNumClients].uc_init = init;
343 undoClientTable[undoNumClients].uc_done = done;
344
345 return (undoNumClients++);
346 }
347
348 /*
349 * ----------------------------------------------------------------------------
350 *
351 * UndoFlush --
352 *
353 * Flush the current undo list.
354 *
355 * Results:
356 * None.
357 *
358 * Side effects:
359 * Deletes everything from the undo list.
360 *
361 * ----------------------------------------------------------------------------
362 */
363
364 void
UndoFlush()365 UndoFlush()
366 {
367 if (undoLogHead == (internalUndoEvent *) NULL)
368 return;
369
370 while (undoLogTail != undoLogHead)
371 {
372 freeMagic((char *) undoLogTail);
373 undoLogTail = undoLogTail->iue_back;
374 ASSERT(undoLogTail != (internalUndoEvent *) NULL, "UndoFlush");
375 }
376 freeMagic((char *) undoLogHead);
377
378 undoLogHead = undoLogTail = undoLogCur = (internalUndoEvent *) NULL;
379 undoNumCommands = 0;
380 undoNumRecentEvents = 0;
381 }
382
383 /*
384 * ----------------------------------------------------------------------------
385 *
386 * UndoDisable --
387 *
388 * Turn the undo package off.
389 * Future calls to UndoNewEvent() will return NULL, and future calls
390 * to UndoIsEnabled() will return FALSE, until the next call to
391 * UndoEnable();
392 *
393 * Results:
394 * None.
395 *
396 * Side effects:
397 * Disables undoing until the next call to UndoEnable().
398 *
399 * ----------------------------------------------------------------------------
400 */
401
402 void
UndoDisable()403 UndoDisable()
404 {
405 UndoDisableCount++;
406 }
407
408 /*
409 * ----------------------------------------------------------------------------
410 *
411 * UndoEnable --
412 *
413 * Turn the undo package on.
414 * Re-enables the undo package after a call to UndoDisable().
415 *
416 * Results:
417 * None.
418 *
419 * Side effects:
420 * Re-enables undoing.
421 *
422 * ----------------------------------------------------------------------------
423 */
424
425 void
UndoEnable()426 UndoEnable()
427 {
428 if (UndoDisableCount > 0)
429 UndoDisableCount--;
430 }
431
432 /*
433 * ----------------------------------------------------------------------------
434 *
435 * UndoNewEvent --
436 *
437 * Return a pointer to a new UndoEvent of the specified type and capable
438 * of holding size bytes of client data.
439 *
440 * Results:
441 * A pointer to a new UndoEvent.
442 *
443 * WARNING:
444 * The pointer to the new UndoEvent must not be retained past the
445 * next call to any of the routines in the undo package, as the
446 * event is liable to be reallocated.
447 *
448 * Side effects:
449 * Appends the event read to the undo list in the appropriate place,
450 * depending on the context from which it was called. If called by
451 * a client under normal circumstances, appends the event to the end
452 * of the undo log. If called by the readEvent() procedure during
453 * replaying of the undo log, places the event at whichever end of
454 * the log is appropriate to the operation (back/forw) being performed.
455 *
456 * ----------------------------------------------------------------------------
457 */
458
459 UndoEvent *
UndoNewEvent(clientType,size)460 UndoNewEvent(clientType, size)
461 UndoType clientType; /* Type of event to allocate */
462 unsigned int size; /* Number of bytes of client data to allocate */
463 {
464 internalUndoEvent *iup;
465 int usize;
466
467 if (UndoDisableCount > 0)
468 return ((UndoEvent *) NULL);
469
470 usize = undoSize(size);
471 iup = (internalUndoEvent *) mallocMagic((unsigned) usize);
472 ASSERT(clientType >= 0 && clientType < undoNumClients, "UndoNewEvent");
473 iup->iue_type = clientType;
474 if (undoState == US_APPEND)
475 {
476 /*
477 * Normal state:
478 * Append the new event after the event pointed to by
479 * undoLogCur.
480 */
481 iup->iue_forw = (internalUndoEvent *) NULL;
482 iup->iue_back = undoLogCur;
483 if (undoLogCur == (internalUndoEvent *) NULL)
484 {
485 if (undoLogHead != (internalUndoEvent *) NULL)
486 undoMemTruncate();
487 undoLogHead = undoLogCur = undoLogTail = iup;
488 }
489 else
490 {
491 if (undoLogCur->iue_forw != (internalUndoEvent *) NULL)
492 undoMemTruncate();
493 undoLogCur->iue_forw = iup;
494 undoLogCur = undoLogTail = iup;
495 }
496 undoNumRecentEvents++;
497 }
498
499 return (undoExport(iup));
500 }
501
502 /*
503 * ----------------------------------------------------------------------------
504 *
505 * UndoNext --
506 *
507 * Delimit a sequence of operations to the undo package with an event
508 * delimiter.
509 *
510 * Results:
511 * None.
512 *
513 * Side effects:
514 * Appends a marker to the undo list signifying the boundary of
515 * a unit of events. UndoBackward() and UndoForward() operate in
516 * terms of complete units of this sort.
517 *
518 * ----------------------------------------------------------------------------
519 */
520
521 void
UndoNext()522 UndoNext()
523 {
524 internalUndoEvent *iup;
525 int usize;
526
527 if (UndoDisableCount > 0 || undoNumRecentEvents == 0)
528 return;
529
530 undoNumRecentEvents = 0;
531 undoNumCommands++;
532 usize = undoSize(0);
533 iup = (internalUndoEvent *) mallocMagic((unsigned) usize);
534 iup->iue_type = UT_DELIM;
535 iup->iue_back = undoLogTail;
536 iup->iue_forw = (internalUndoEvent *) NULL;
537 if (undoLogTail != (internalUndoEvent *) NULL)
538 undoLogTail->iue_forw = iup;
539 undoLogCur = undoLogTail = iup;
540 if (undoNumCommands >= MAXCOMMANDS)
541 undoFreeHead();
542 }
543
544 /*
545 * ----------------------------------------------------------------------------
546 *
547 * UndoBackward --
548 *
549 * Play the undo log backward n events.
550 *
551 * Results:
552 * The number of events actually played backward. Normally, this
553 * will be equal to n unless we encounter the beginning of the log.
554 *
555 * Side effects:
556 * Applies the client backEvent() procedures to each event encountered
557 * in playing the log backward.
558 *
559 * ----------------------------------------------------------------------------
560 */
561
562 int
UndoBackward(n)563 UndoBackward(n)
564 int n; /* Number of events to unplay */
565 {
566 internalUndoEvent *iup;
567 int client, count;
568
569 #ifdef MAGIC_WRAPPER
570 /* This condition appears to happen just prior to a program */
571 /* crash, apparently due to a race condition which is */
572 /* difficult to reproduce. Tcl_DoOneEvent() inside the */
573 /* DRCContinuous routine is implicated. Can't find the */
574 /* error source, but refusing to execute the undo command */
575 /* appears to prevent it. */
576
577 if (UndoDisableCount > 0)
578 {
579 TxError("Attempted undo with undo disabled. . . abort function.\n");
580 return 0;
581 }
582 #endif
583
584 /* Call the initialization routines of all clients */
585 for (client = 0; client < undoNumClients; client++)
586 if (undoClientTable[client].uc_init)
587 (*undoClientTable[client].uc_init)();
588
589 iup = undoLogCur;
590 undoNumRecentEvents = 0;
591 UndoDisableCount++;
592 for (count = 0; (count < n) && (iup != NULL); count++)
593 {
594 do
595 {
596 if (iup->iue_type != UT_DELIM)
597 if (undoClientTable[iup->iue_type].uc_back != NULL)
598 (*undoClientTable[iup->iue_type].uc_back)(undoExport(iup));
599
600 /* fprintf(stderr, "Undo record: type=%d back=0x%x, forw=0x%x\n",
601 * iup->iue_type, iup->iue_back, iup->iue_forw);
602 * fprintf(stderr, " UndoDisableCount = %d, undoNumRecentEvents = %d\n",
603 * UndoDisableCount, undoNumRecentEvents);
604 * fflush(stderr);
605 */
606
607 iup = undoGetBack(iup);
608 }
609 while ((iup != (internalUndoEvent *) NULL) && (iup->iue_type != UT_DELIM));
610 }
611 UndoDisableCount--;
612
613 undoLogCur = iup;
614
615 /* fprintf(stderr, "UndoBackward: undoLogCur set to 0x%x\n", undoLogCur);
616 * fflush(stderr);
617 */
618
619 /* Call the termination routines of all clients */
620 for (client = 0; client < undoNumClients; client++)
621 if (undoClientTable[client].uc_done)
622 (*undoClientTable[client].uc_done)();
623 return (count);
624 }
625
626 /*
627 * ----------------------------------------------------------------------------
628 *
629 * UndoForward --
630 *
631 * Play the undo log forward n events.
632 *
633 * Results:
634 * The number of events actually played forward. Normally, this
635 * will be equal to n unless we encounter the end of the log.
636 *
637 * Side effects:
638 * Applies the client forwEvent() procedures to each event encountered
639 * in playing the log forward.
640 *
641 * ----------------------------------------------------------------------------
642 */
643
644 int
UndoForward(n)645 UndoForward(n)
646 int n; /* Number of events to replay */
647 {
648 internalUndoEvent *iup;
649 int count, client;
650
651 /* Call the initialization routines of all clients */
652 for (client = 0; client < undoNumClients; client++)
653 if (undoClientTable[client].uc_init)
654 (*undoClientTable[client].uc_init)();
655
656 count = 0;
657 iup = undoGetForw(undoLogCur);
658 if (iup == NULL) goto done;
659
660 undoNumRecentEvents = 0;
661 UndoDisableCount++;
662 for ( ; count < n; count++)
663 {
664 do
665 {
666 if (iup->iue_type != UT_DELIM)
667 if (undoClientTable[iup->iue_type].uc_forw != NULL)
668 (*undoClientTable[iup->iue_type].uc_forw)(undoExport(iup));
669 iup = undoGetForw(iup);
670 }
671 while (iup != (internalUndoEvent *) NULL && iup->iue_type != UT_DELIM);
672 if (iup == (internalUndoEvent *) NULL)
673 {
674 iup = undoLogTail;
675 break;
676 }
677 }
678 UndoDisableCount--;
679
680 undoLogCur = iup;
681
682 done:
683 /* Call the termination routines of all clients */
684 for (client = 0; client < undoNumClients; client++)
685 if (undoClientTable[client].uc_done)
686 (*undoClientTable[client].uc_done)();
687 return (count);
688 }
689
690 /*
691 * ============================================================================
692 *
693 * All of the remaining procedures in the file are invisible
694 * to the clients of the undo package and should not be used.
695 *
696 * ============================================================================
697 */
698
699
700 /*
701 * ----------------------------------------------------------------------------
702 *
703 * undoGetForw --
704 *
705 * Return a pointer to the next undo event in the list.
706 *
707 * Results:
708 * A pointer to an undo event.
709 *
710 * Side effects:
711 * None.
712 *
713 * Directly modifies:
714 * undoLogHead, undoLogTail
715 * undoNumCommands
716 * undoNumRecentEvents = 0
717 *
718 * Indirectly modifies:
719 * Nothing.
720 *
721 * ----------------------------------------------------------------------------
722 */
723
724 internalUndoEvent *
undoGetForw(iup)725 undoGetForw(iup)
726 internalUndoEvent *iup;
727 {
728 if (iup != (internalUndoEvent *) NULL)
729 {
730 /*
731 * Return the next event in memory if there is one.
732 */
733 if (iup->iue_forw != (internalUndoEvent *) NULL)
734 return (iup->iue_forw);
735 }
736 else
737 {
738 /*
739 * A NULL initial iup means to start at the very beginning of
740 * the main-memory undo list. If there is anything there, return
741 * it; otherwise return NULL.
742 */
743 if (undoLogHead != (internalUndoEvent *) NULL)
744 return (undoLogHead);
745 }
746
747 return ((internalUndoEvent *) NULL);
748 }
749
750 /*
751 * ----------------------------------------------------------------------------
752 *
753 * undoGetBack --
754 *
755 * Return a pointer to the previous undo event in the list.
756 *
757 * Results:
758 * A pointer to an undo event.
759 *
760 * Side effects:
761 * None.
762 *
763 * Directly modifies:
764 *
765 * Indirectly modifies:
766 *
767 * ----------------------------------------------------------------------------
768 */
769
770 internalUndoEvent *
undoGetBack(iup)771 undoGetBack(iup)
772 internalUndoEvent *iup;
773 {
774 if (iup == (internalUndoEvent *) NULL) return (iup);
775 if (iup->iue_back != (internalUndoEvent *) NULL) return (iup->iue_back);
776 return ((internalUndoEvent *) NULL);
777 }
778
779 /*
780 * ----------------------------------------------------------------------------
781 *
782 * undoFreeHead --
783 *
784 * Free up space by throwing away events from the front of the in-memory
785 * event list until the total number of in-memory commands falls below
786 * LOWCOMMANDS
787 *
788 * Results:
789 * None.
790 *
791 * Side effects:
792 * Deallocates events from the front of the in-memory event list.
793 * Updates undoLogHead, undoNumCommands.
794 * Guaranteed to leave undoLogHead pointing to the first event
795 * in a command (not of type UT_DELIM).
796 *
797 * WARNING:
798 * It is important that undoLogCur point beyond the region
799 * to be freed. Also, it is important that the in-core list
800 * be terminated by an UT_DELIM event.
801 *
802 * ----------------------------------------------------------------------------
803 */
804
805 void
undoFreeHead()806 undoFreeHead()
807 {
808 if (undoNumCommands <= LOWCOMMANDS)
809 return;
810
811 while (undoNumCommands > LOWCOMMANDS)
812 {
813 do
814 {
815 ASSERT(undoLogHead != undoLogCur, "undoFreeHead");
816 freeMagic((char *) undoLogHead);
817 undoLogHead = undoLogHead->iue_forw;
818 ASSERT(undoLogHead != (internalUndoEvent *) NULL, "undoFreeHead");
819 }
820 while (undoLogHead->iue_type != UT_DELIM);
821 undoNumCommands--;
822 }
823 freeMagic((char *) undoLogHead);
824 undoLogHead = undoLogHead->iue_forw;
825 undoLogHead->iue_back = (internalUndoEvent *) NULL;
826 }
827
828 /*
829 * ----------------------------------------------------------------------------
830 *
831 * undoMemTruncate --
832 *
833 * Delete events in memory which are later than the current event.
834 * NOTE: This expects to be called only when undoNumRecentEvents == 0.
835 *
836 * Results:
837 * None.
838 *
839 * Side effects:
840 * Truncates the event list so there are no events past undoLogCur.
841 *
842 * Updates undoLogTail.
843 *
844 * ----------------------------------------------------------------------------
845 */
846
847 void
undoMemTruncate()848 undoMemTruncate()
849 {
850 internalUndoEvent *up;
851
852 /*
853 * If there are events forward of the current event that
854 * will get overwritten by the new event, delete them from
855 * memory.
856 */
857
858 if (undoLogCur == (internalUndoEvent *) NULL)
859 {
860 /*
861 * Delete ALL events from memory
862 */
863 up = undoLogHead;
864 while (up != (internalUndoEvent *) NULL)
865 {
866 freeMagic((char *) up);
867 up = up->iue_forw;
868 }
869 undoLogTail = undoLogHead = (internalUndoEvent *) NULL;
870 undoNumCommands = 0;
871 }
872 else
873 {
874 ASSERT(undoLogCur->iue_type == UT_DELIM, "undoMemTruncate");
875 /*
876 * Delete only some of the events in main memory.
877 */
878 up = undoLogCur->iue_forw;
879 while (up != (internalUndoEvent *) NULL)
880 {
881 if (up->iue_type == UT_DELIM)
882 undoNumCommands--;
883 freeMagic((char *) up);
884 up = up->iue_forw;
885 }
886 undoLogCur->iue_forw = (internalUndoEvent *) NULL;
887 undoLogTail = undoLogCur;
888 }
889
890 }
891
892 /*
893 * ============================================================================
894 *
895 * DEBUGGING PROCEDURES
896 *
897 * ============================================================================
898 */
899
900 void
undoPrintEvent(iup)901 undoPrintEvent(iup)
902 internalUndoEvent *iup;
903 {
904 char *client_name;
905 if (iup->iue_type < 0)
906 client_name = "(delimiter)";
907 else
908 client_name = undoClientTable[iup->iue_type].uc_name;
909
910 (void) TxPrintf("0x%x: \t%s \tf=0x%x \tb=0x%x\n",
911 iup, client_name, iup->iue_forw, iup->iue_back);
912 }
913
914 /* Print events forward from "iup". If n is 0 or negative, print to */
915 /* the end of the stack. Otherwise, print the next n events. */
916
917 void
undoPrintForw(iup,n)918 undoPrintForw(iup, n)
919 internalUndoEvent *iup;
920 int n;
921 {
922 int i = 0;
923
924 (void) TxPrintf("head=0x%x\ttail=0x%x\tcur=0x%x\n",
925 undoLogHead, undoLogTail, undoLogCur);
926 if (iup == (internalUndoEvent *) NULL)
927 iup = undoLogHead;
928 while (iup != (internalUndoEvent *) NULL)
929 {
930 undoPrintEvent(iup);
931 iup = iup->iue_forw;
932 i++;
933 if (i == n) break;
934 }
935 }
936
937 /* Print events backward from "iup". If n is 0 or negative, print to */
938 /* the beginning of the stack. Otherwise, print the previous n events. */
939
940 void
undoPrintBack(iup,n)941 undoPrintBack(iup, n)
942 internalUndoEvent *iup;
943 int n;
944 {
945 int i = 0;
946
947 (void) TxPrintf("head=0x%x\ttail=0x%x\tcur=0x%x\n",
948 undoLogHead, undoLogTail, undoLogCur);
949 if (iup == (internalUndoEvent *) NULL)
950 iup = undoLogTail;
951 while (iup != (internalUndoEvent *) NULL)
952 {
953 undoPrintEvent(iup);
954 iup = iup->iue_back;
955 i++;
956 if (i == n) break;
957 }
958 }
959
960 /* Called from windUndoCmd() or windRedoCmd() with n either positive, */
961 /* or negative with 1 offset to differentiate between n = 0 (forward) */
962 /* and n = 0 (backward). */
963
964 void
UndoStackTrace(n)965 UndoStackTrace(n)
966 int n;
967 {
968 if (n < 0)
969 undoPrintBack(undoLogCur, -(n + 1));
970 else
971 undoPrintForw(undoLogCur, n);
972 }
973
974