mirror of
https://github.com/RTimothyEdwards/magic.git
synced 2026-08-31 18:25:50 +02:00
Added a ARG_UNUSED macro in utils/magic.h, which produces a simple cast to void to silence the warnings. The reasons this is used as opposed to an attribute is that most compilers should support this. Besides this, the macro can be easily searched for in the future to potentially remove (where possible) the unused parameters.
981 lines
26 KiB
C
981 lines
26 KiB
C
/*
|
|
* undo.c --
|
|
*
|
|
* Undo/redo module.
|
|
*
|
|
* The undo package records a series of invertible editing events
|
|
* in a log maintained in main memory.
|
|
*
|
|
* The current state may be rewound back toward the time the editing
|
|
* session began, and it may be replayed forward as well.
|
|
*
|
|
* *********************************************************************
|
|
* * Copyright (C) 1985, 1990 Regents of the University of California. *
|
|
* * Permission to use, copy, modify, and distribute this *
|
|
* * software and its documentation for any purpose and without *
|
|
* * fee is hereby granted, provided that the above copyright *
|
|
* * notice appear in all copies. The University of California *
|
|
* * makes no representations about the suitability of this *
|
|
* * software for any purpose. It is provided "as is" without *
|
|
* * express or implied warranty. Export of this software outside *
|
|
* * of the United States of America may require an export license. *
|
|
* *********************************************************************
|
|
*/
|
|
|
|
#ifndef lint
|
|
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 $";
|
|
#endif /* not lint */
|
|
|
|
#include <stdio.h>
|
|
#include <sys/types.h>
|
|
|
|
#include "utils/magic.h"
|
|
#include "utils/utils.h"
|
|
#include "utils/malloc.h"
|
|
#include "utils/undo.h"
|
|
|
|
/* C99 compat */
|
|
#include "textio/textio.h"
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
|
|
/*
|
|
* CONFIGURATION INFORMATION
|
|
*/
|
|
|
|
#define MAXUNDOCLIENTS 50 /* Maximum number of calls to UndoAddClient */
|
|
|
|
/*
|
|
* MAXCOMMANDS is the maximum number of delimited event sequences
|
|
* ("commands") retained in main memory. LOWCOMMANDS is a low-water
|
|
* mark for the number of commands in memory; whenever we free up
|
|
* commands, we do so until there are no more than LOWCOMMANDS in
|
|
* main memory.
|
|
*
|
|
* >>>>> In the current implementation, these two are the same <<<<<
|
|
*/
|
|
|
|
#define MAXCOMMANDS 1000
|
|
#define LOWCOMMANDS 1000 /* Must be > 0 ! */
|
|
|
|
/* ------------------------------------------------------------------------ */
|
|
|
|
/*
|
|
* The following structure describes the basic information
|
|
* required by the undo package for each event it stores.
|
|
* This information is NOT intended to be visible to any of
|
|
* the clients of the undo package and is susceptible to being
|
|
* changed arbitrarily.
|
|
*
|
|
* To enforce the absolute ignorance of undo's clients, when
|
|
* we allocate an internalUndoEvent, we only give the client
|
|
* a pointer to the iue_client part, which is of a size determined
|
|
* by the client when it calls UndoNewEvent(). This pointer is
|
|
* what the client sees as an (UndoEvent *) (really a (char *)).
|
|
*/
|
|
|
|
typedef struct ue
|
|
{
|
|
UndoType iue_type; /* Event type */
|
|
struct ue *iue_back; /* Previous event on list */
|
|
struct ue *iue_forw; /* Next event on list */
|
|
int iue_client; /* Client data area. This is merely a
|
|
* dummy placeholder; the actual size
|
|
* of one of these structures is
|
|
* determined at the time of
|
|
* UndoNewEvent().
|
|
*/
|
|
} internalUndoEvent;
|
|
|
|
#define UT_DELIM (-1)
|
|
|
|
/*
|
|
* The following macro is used to compute the number of bytes we must
|
|
* allocate in order to give the user an UndoEvent capable of holding
|
|
* n bytes.
|
|
*/
|
|
|
|
#define undoSize(n) (sizeof (struct ue) + (n) \
|
|
- sizeof (((struct ue *) 0)->iue_client))
|
|
|
|
/*
|
|
* Mapping between internal and external undo event pointers.
|
|
* When the undo package hands an (UndoEvent *) to a client, it is
|
|
* really a pointer to the iue_client part of the structure
|
|
* above.
|
|
*/
|
|
|
|
#define CLIENTOFFSET ((int) &((internalUndoEvent) 0)->iue_client)
|
|
#define undoExport(p) ((UndoEvent *) (&(p)->iue_client))
|
|
#define undoImport(p) ((internalUndoEvent) (((char *) (p)) - CLIENTOFFSET))
|
|
|
|
/*
|
|
* The following table is used to record the information about clients
|
|
* of the undo package. The number of such clients is stored in
|
|
* undoNumClients.
|
|
*/
|
|
|
|
typedef struct
|
|
{
|
|
char *uc_name; /* Name (for error messages) */
|
|
void (*uc_init)(); /* Called before playing log */
|
|
void (*uc_done)(); /* Called after playing log */
|
|
void (*uc_forw)(); /* Play event forward */
|
|
void (*uc_back)(); /* Play event backward */
|
|
} undoClient;
|
|
|
|
undoClient undoClientTable[MAXUNDOCLIENTS];
|
|
int undoNumClients = 0;
|
|
|
|
/*
|
|
* undoState is used by UndoNewEvent() to figure out the context from which
|
|
* it was called in order that it may link the newly allocated event
|
|
* in the appropriate way. Events are only linked in when the state
|
|
* is US_APPEND. The only reason for having two other states instead
|
|
* of one is for ease in debugging.
|
|
* UndoDisableCount counts the number of times UndoDisable() has been called.
|
|
*/
|
|
|
|
#define US_APPEND 0 /* Normal state: appending to log */
|
|
#define US_FORWARD 1 /* Playing log forward */
|
|
#define US_BACKWARD 2 /* Playing log backward */
|
|
|
|
int undoState = US_APPEND;
|
|
global int UndoDisableCount = 0;
|
|
|
|
/*
|
|
* Log of events kept in main memory.
|
|
*
|
|
* undoLogHead Pointer to first entry stored in main memory.
|
|
* - NULL, indicating no events are in memory
|
|
* - a pointer to the first event of a command
|
|
* undoLogTail Pointer to last entry stored in main memory.
|
|
* - Undefined (if undoLogHead == NULL)
|
|
* - a pointer to a UT_DELIM event if
|
|
* undoNumRecentEvents == 0
|
|
* - a pointer to a non-UT_DELIM event if
|
|
* undoNumRecentEvents != 0
|
|
* undoLogCur Pointer to "current" event, ie, one after which
|
|
* next event will be added.
|
|
* - NULL if at beginning of event list
|
|
* - a pointer to a UT_DELIM event if
|
|
* undoNumRecentEvents == 0
|
|
* - a pointer to a non-UT_DELIM event if
|
|
* undoNumRecentEvents != 0
|
|
*
|
|
* undoNumRecentEvents
|
|
* Number of events written since last call to
|
|
* UndoNext().
|
|
* undoNumCommands
|
|
* Number of complete commands in main memory.
|
|
*/
|
|
|
|
internalUndoEvent *undoLogCur;
|
|
internalUndoEvent *undoLogHead;
|
|
internalUndoEvent *undoLogTail;
|
|
int undoNumRecentEvents;
|
|
int undoNumCommands;
|
|
|
|
/*
|
|
* ============================================================================
|
|
*
|
|
* The following collection of procedures completely defines
|
|
* the interface the undo package presents to its clients.
|
|
*
|
|
* ============================================================================
|
|
*/
|
|
|
|
extern internalUndoEvent *undoGetForw();
|
|
extern internalUndoEvent *undoGetBack();
|
|
extern void undoFreeHead();
|
|
extern void undoMemTruncate();
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* UndoInit --
|
|
*
|
|
* Initialize the undo package.
|
|
* Opens the given file as an undo log.
|
|
* One of several modes may be specified. In all cases, the log file
|
|
* is opened so that playback will commence from the beginning of the
|
|
* file.
|
|
*
|
|
* "r" The log file is opened for playback only; no new events
|
|
* may be played out to the file.
|
|
* This mode is intended for use in recovering from a crash
|
|
* without clobbering the crash log.
|
|
*
|
|
* "w" The log file is created afresh and is available to the
|
|
* undo package for use in logging events.
|
|
* This is the normal mode for starting a new session.
|
|
*
|
|
* "rw" The log file is opened for playback and logging.
|
|
* Any new events written before the entire log has played
|
|
* forward will cause the remaining events in the log to be
|
|
* truncated.
|
|
* This is the normal mode for crash recovery.
|
|
*
|
|
* Results:
|
|
* TRUE if the log file could be successfully created/opened, FALSE
|
|
* if an error was encountered. In the latter case, the external
|
|
* variable errno is set to the UNIX error encountered.
|
|
*
|
|
* Side effects:
|
|
* Initializes all the variables in the undo package.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
bool
|
|
UndoInit(logFileName, mode)
|
|
char *logFileName; /* Name of log file. This may contain tilde
|
|
* abbreviations.
|
|
*/
|
|
char *mode; /* Mode for opening. Must be "r", "rw", or "w" */
|
|
{
|
|
ARG_UNUSED(logFileName);
|
|
ARG_UNUSED(mode);
|
|
UndoDisableCount = 0;
|
|
undoLogTail = NULL;
|
|
undoLogCur = NULL;
|
|
undoNumRecentEvents = 0;
|
|
undoNumCommands = 0;
|
|
|
|
/*
|
|
* Deallocate any events stored in main memory
|
|
*/
|
|
|
|
while (undoLogHead != (internalUndoEvent *) NULL)
|
|
{
|
|
freeMagic((char *) undoLogHead);
|
|
undoLogHead = undoLogHead->iue_forw;
|
|
}
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* UndoAddClient --
|
|
*
|
|
* Used by a client to make itself known to the undo package.
|
|
* The client must supply the following information:
|
|
*
|
|
* 1. A procedure to call before starting an undo or redo
|
|
* operation.
|
|
*
|
|
* 2. A procedure to call upon completion of an undo or
|
|
* redo operation.
|
|
*
|
|
* 3. A procedure to parse a line in the undo log file and
|
|
* return a filled-in UndoEvent (allocated, of course,
|
|
* by a call to UndoNewEvent()) that is the internal
|
|
* representation of the event externally represented
|
|
* by the supplied line of text from the log file.
|
|
*
|
|
* UndoEvent *
|
|
* readEvent(line)
|
|
* char *line;
|
|
* {
|
|
* }
|
|
*
|
|
* 4. A procedure to fill in a line (null-terminated with
|
|
* no newlines embedded or trailing) with the external
|
|
* representation of an event based on an UndoEvent
|
|
* supplied to it. This prodedure returns the number
|
|
* of bytes it stored in the line (which must be less
|
|
* than a constant, UNDOLINESIZE).
|
|
*
|
|
* int
|
|
* writeEvent(event, line)
|
|
* UndoEvent *event;
|
|
* char *line;
|
|
* {
|
|
* }
|
|
*
|
|
* If either readEvent or writeEvent are NULL, a default procedure
|
|
* is used which simply uses the same internal and external representation
|
|
* for the undo event (ie, a text string).
|
|
*
|
|
* 5. A procedure to play an event forward.
|
|
*
|
|
* void
|
|
* forwEvent(event)
|
|
* UndoEvent *event;
|
|
* {
|
|
* }
|
|
*
|
|
* 6. A procedure to play an event backward.
|
|
*
|
|
* void
|
|
* backEvent(event)
|
|
* UndoEvent *event;
|
|
* {
|
|
* }
|
|
*
|
|
* 7. A client name, for error messages. This is a character
|
|
* string.
|
|
*
|
|
* Results:
|
|
* Returns an UndoType which must be passed in future calls
|
|
* to UndoNewEvent(). If -1 is returned, this means that there
|
|
* are too many clients of the undo package.
|
|
*
|
|
* Side effects:
|
|
* Initializes local state in the undo package.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
UndoType
|
|
UndoAddClient(init, done, readEvent, writeEvent, forwEvent, backEvent, name)
|
|
void (*init)();
|
|
void (*done)();
|
|
UndoEvent *(*readEvent)();
|
|
int (*writeEvent)();
|
|
void (*forwEvent)(), (*backEvent)();
|
|
char *name;
|
|
{
|
|
ARG_UNUSED(readEvent);
|
|
ARG_UNUSED(writeEvent);
|
|
if (undoNumClients >= MAXUNDOCLIENTS)
|
|
return ((UndoType) -1);
|
|
|
|
undoClientTable[undoNumClients].uc_name = StrDup((char **) NULL, name);
|
|
undoClientTable[undoNumClients].uc_forw = forwEvent;
|
|
undoClientTable[undoNumClients].uc_back = backEvent;
|
|
undoClientTable[undoNumClients].uc_init = init;
|
|
undoClientTable[undoNumClients].uc_done = done;
|
|
|
|
return (undoNumClients++);
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* UndoFlush --
|
|
*
|
|
* Flush the current undo list.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* Deletes everything from the undo list.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
UndoFlush()
|
|
{
|
|
if (undoLogHead == (internalUndoEvent *) NULL)
|
|
return;
|
|
|
|
while (undoLogTail != undoLogHead)
|
|
{
|
|
freeMagic((char *) undoLogTail);
|
|
undoLogTail = undoLogTail->iue_back;
|
|
ASSERT(undoLogTail != (internalUndoEvent *) NULL, "UndoFlush");
|
|
}
|
|
freeMagic((char *) undoLogHead);
|
|
|
|
undoLogHead = undoLogTail = undoLogCur = (internalUndoEvent *) NULL;
|
|
undoNumCommands = 0;
|
|
undoNumRecentEvents = 0;
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* UndoDisable --
|
|
*
|
|
* Turn the undo package off.
|
|
* Future calls to UndoNewEvent() will return NULL, and future calls
|
|
* to UndoIsEnabled() will return FALSE, until the next call to
|
|
* UndoEnable();
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* Disables undoing until the next call to UndoEnable().
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
UndoDisable()
|
|
{
|
|
UndoDisableCount++;
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* UndoEnable --
|
|
*
|
|
* Turn the undo package on.
|
|
* Re-enables the undo package after a call to UndoDisable().
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* Re-enables undoing.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
UndoEnable()
|
|
{
|
|
if (UndoDisableCount > 0)
|
|
UndoDisableCount--;
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* UndoNewEvent --
|
|
*
|
|
* Return a pointer to a new UndoEvent of the specified type and capable
|
|
* of holding size bytes of client data.
|
|
*
|
|
* Results:
|
|
* A pointer to a new UndoEvent.
|
|
*
|
|
* WARNING:
|
|
* The pointer to the new UndoEvent must not be retained past the
|
|
* next call to any of the routines in the undo package, as the
|
|
* event is liable to be reallocated.
|
|
*
|
|
* Side effects:
|
|
* Appends the event read to the undo list in the appropriate place,
|
|
* depending on the context from which it was called. If called by
|
|
* a client under normal circumstances, appends the event to the end
|
|
* of the undo log. If called by the readEvent() procedure during
|
|
* replaying of the undo log, places the event at whichever end of
|
|
* the log is appropriate to the operation (back/forw) being performed.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
UndoEvent *
|
|
UndoNewEvent(clientType, size)
|
|
UndoType clientType; /* Type of event to allocate */
|
|
unsigned int size; /* Number of bytes of client data to allocate */
|
|
{
|
|
internalUndoEvent *iup;
|
|
int usize;
|
|
|
|
if (UndoDisableCount > 0)
|
|
return ((UndoEvent *) NULL);
|
|
|
|
usize = undoSize(size);
|
|
iup = (internalUndoEvent *) mallocMagic((unsigned) usize);
|
|
ASSERT(clientType >= 0 && clientType < undoNumClients, "UndoNewEvent");
|
|
iup->iue_type = clientType;
|
|
if (undoState == US_APPEND)
|
|
{
|
|
/*
|
|
* Normal state:
|
|
* Append the new event after the event pointed to by
|
|
* undoLogCur.
|
|
*/
|
|
iup->iue_forw = (internalUndoEvent *) NULL;
|
|
iup->iue_back = undoLogCur;
|
|
if (undoLogCur == (internalUndoEvent *) NULL)
|
|
{
|
|
if (undoLogHead != (internalUndoEvent *) NULL)
|
|
undoMemTruncate();
|
|
undoLogHead = undoLogCur = undoLogTail = iup;
|
|
}
|
|
else
|
|
{
|
|
if (undoLogCur->iue_forw != (internalUndoEvent *) NULL)
|
|
undoMemTruncate();
|
|
undoLogCur->iue_forw = iup;
|
|
undoLogCur = undoLogTail = iup;
|
|
}
|
|
undoNumRecentEvents++;
|
|
}
|
|
|
|
return (undoExport(iup));
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* UndoNext --
|
|
*
|
|
* Delimit a sequence of operations to the undo package with an event
|
|
* delimiter.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* Appends a marker to the undo list signifying the boundary of
|
|
* a unit of events. UndoBackward() and UndoForward() operate in
|
|
* terms of complete units of this sort.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
UndoNext()
|
|
{
|
|
internalUndoEvent *iup;
|
|
int usize;
|
|
|
|
if (UndoDisableCount > 0 || undoNumRecentEvents == 0)
|
|
return;
|
|
|
|
undoNumRecentEvents = 0;
|
|
undoNumCommands++;
|
|
usize = undoSize(0);
|
|
iup = (internalUndoEvent *) mallocMagic((unsigned) usize);
|
|
iup->iue_type = UT_DELIM;
|
|
iup->iue_back = undoLogTail;
|
|
iup->iue_forw = (internalUndoEvent *) NULL;
|
|
if (undoLogTail != (internalUndoEvent *) NULL)
|
|
undoLogTail->iue_forw = iup;
|
|
undoLogCur = undoLogTail = iup;
|
|
if (undoNumCommands >= MAXCOMMANDS)
|
|
undoFreeHead();
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* UndoBackward --
|
|
*
|
|
* Play the undo log backward n events.
|
|
*
|
|
* Results:
|
|
* The number of events actually played backward. Normally, this
|
|
* will be equal to n unless we encounter the beginning of the log.
|
|
*
|
|
* Side effects:
|
|
* Applies the client backEvent() procedures to each event encountered
|
|
* in playing the log backward.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
int
|
|
UndoBackward(n)
|
|
int n; /* Number of events to unplay */
|
|
{
|
|
internalUndoEvent *iup;
|
|
int client, count;
|
|
|
|
#ifdef MAGIC_WRAPPER
|
|
/* This condition appears to happen just prior to a program */
|
|
/* crash, apparently due to a race condition which is */
|
|
/* difficult to reproduce. Tcl_DoOneEvent() inside the */
|
|
/* DRCContinuous routine is implicated. Can't find the */
|
|
/* error source, but refusing to execute the undo command */
|
|
/* appears to prevent it. */
|
|
|
|
if (UndoDisableCount > 0)
|
|
{
|
|
TxError("Attempted undo with undo disabled. . . abort function.\n");
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
/* Call the initialization routines of all clients */
|
|
for (client = 0; client < undoNumClients; client++)
|
|
if (undoClientTable[client].uc_init)
|
|
(*undoClientTable[client].uc_init)();
|
|
|
|
iup = undoLogCur;
|
|
undoNumRecentEvents = 0;
|
|
UndoDisableCount++;
|
|
for (count = 0; (count < n) && (iup != NULL); count++)
|
|
{
|
|
do
|
|
{
|
|
if (iup->iue_type != UT_DELIM)
|
|
if (undoClientTable[iup->iue_type].uc_back != NULL)
|
|
(*undoClientTable[iup->iue_type].uc_back)(undoExport(iup));
|
|
|
|
/* fprintf(stderr, "Undo record: type=%d back=0x%x, forw=0x%x\n",
|
|
* iup->iue_type, iup->iue_back, iup->iue_forw);
|
|
* fprintf(stderr, " UndoDisableCount = %d, undoNumRecentEvents = %d\n",
|
|
* UndoDisableCount, undoNumRecentEvents);
|
|
* fflush(stderr);
|
|
*/
|
|
|
|
iup = undoGetBack(iup);
|
|
}
|
|
while ((iup != (internalUndoEvent *) NULL) && (iup->iue_type != UT_DELIM));
|
|
}
|
|
UndoDisableCount--;
|
|
|
|
undoLogCur = iup;
|
|
|
|
/* fprintf(stderr, "UndoBackward: undoLogCur set to 0x%x\n", undoLogCur);
|
|
* fflush(stderr);
|
|
*/
|
|
|
|
/* Call the termination routines of all clients */
|
|
for (client = 0; client < undoNumClients; client++)
|
|
if (undoClientTable[client].uc_done)
|
|
(*undoClientTable[client].uc_done)();
|
|
return (count);
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* UndoForward --
|
|
*
|
|
* Play the undo log forward n events.
|
|
*
|
|
* Results:
|
|
* The number of events actually played forward. Normally, this
|
|
* will be equal to n unless we encounter the end of the log.
|
|
*
|
|
* Side effects:
|
|
* Applies the client forwEvent() procedures to each event encountered
|
|
* in playing the log forward.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
int
|
|
UndoForward(n)
|
|
int n; /* Number of events to replay */
|
|
{
|
|
internalUndoEvent *iup;
|
|
int count, client;
|
|
|
|
/* Call the initialization routines of all clients */
|
|
for (client = 0; client < undoNumClients; client++)
|
|
if (undoClientTable[client].uc_init)
|
|
(*undoClientTable[client].uc_init)();
|
|
|
|
count = 0;
|
|
iup = undoGetForw(undoLogCur);
|
|
if (iup == NULL) goto done;
|
|
|
|
undoNumRecentEvents = 0;
|
|
UndoDisableCount++;
|
|
for ( ; count < n; count++)
|
|
{
|
|
do
|
|
{
|
|
if (iup->iue_type != UT_DELIM)
|
|
if (undoClientTable[iup->iue_type].uc_forw != NULL)
|
|
(*undoClientTable[iup->iue_type].uc_forw)(undoExport(iup));
|
|
iup = undoGetForw(iup);
|
|
}
|
|
while (iup != (internalUndoEvent *) NULL && iup->iue_type != UT_DELIM);
|
|
if (iup == (internalUndoEvent *) NULL)
|
|
{
|
|
iup = undoLogTail;
|
|
break;
|
|
}
|
|
}
|
|
UndoDisableCount--;
|
|
|
|
undoLogCur = iup;
|
|
|
|
done:
|
|
/* Call the termination routines of all clients */
|
|
for (client = 0; client < undoNumClients; client++)
|
|
if (undoClientTable[client].uc_done)
|
|
(*undoClientTable[client].uc_done)();
|
|
return (count);
|
|
}
|
|
|
|
/*
|
|
* ============================================================================
|
|
*
|
|
* All of the remaining procedures in the file are invisible
|
|
* to the clients of the undo package and should not be used.
|
|
*
|
|
* ============================================================================
|
|
*/
|
|
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* undoGetForw --
|
|
*
|
|
* Return a pointer to the next undo event in the list.
|
|
*
|
|
* Results:
|
|
* A pointer to an undo event.
|
|
*
|
|
* Side effects:
|
|
* None.
|
|
*
|
|
* Directly modifies:
|
|
* undoLogHead, undoLogTail
|
|
* undoNumCommands
|
|
* undoNumRecentEvents = 0
|
|
*
|
|
* Indirectly modifies:
|
|
* Nothing.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
internalUndoEvent *
|
|
undoGetForw(iup)
|
|
internalUndoEvent *iup;
|
|
{
|
|
if (iup != (internalUndoEvent *) NULL)
|
|
{
|
|
/*
|
|
* Return the next event in memory if there is one.
|
|
*/
|
|
if (iup->iue_forw != (internalUndoEvent *) NULL)
|
|
return (iup->iue_forw);
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
* A NULL initial iup means to start at the very beginning of
|
|
* the main-memory undo list. If there is anything there, return
|
|
* it; otherwise return NULL.
|
|
*/
|
|
if (undoLogHead != (internalUndoEvent *) NULL)
|
|
return (undoLogHead);
|
|
}
|
|
|
|
return ((internalUndoEvent *) NULL);
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* undoGetBack --
|
|
*
|
|
* Return a pointer to the previous undo event in the list.
|
|
*
|
|
* Results:
|
|
* A pointer to an undo event.
|
|
*
|
|
* Side effects:
|
|
* None.
|
|
*
|
|
* Directly modifies:
|
|
*
|
|
* Indirectly modifies:
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
internalUndoEvent *
|
|
undoGetBack(iup)
|
|
internalUndoEvent *iup;
|
|
{
|
|
if (iup == (internalUndoEvent *) NULL) return (iup);
|
|
if (iup->iue_back != (internalUndoEvent *) NULL) return (iup->iue_back);
|
|
return ((internalUndoEvent *) NULL);
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* undoFreeHead --
|
|
*
|
|
* Free up space by throwing away events from the front of the in-memory
|
|
* event list until the total number of in-memory commands falls below
|
|
* LOWCOMMANDS
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* Deallocates events from the front of the in-memory event list.
|
|
* Updates undoLogHead, undoNumCommands.
|
|
* Guaranteed to leave undoLogHead pointing to the first event
|
|
* in a command (not of type UT_DELIM).
|
|
*
|
|
* WARNING:
|
|
* It is important that undoLogCur point beyond the region
|
|
* to be freed. Also, it is important that the in-core list
|
|
* be terminated by an UT_DELIM event.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
undoFreeHead()
|
|
{
|
|
if (undoNumCommands <= LOWCOMMANDS)
|
|
return;
|
|
|
|
while (undoNumCommands > LOWCOMMANDS)
|
|
{
|
|
do
|
|
{
|
|
ASSERT(undoLogHead != undoLogCur, "undoFreeHead");
|
|
freeMagic((char *) undoLogHead);
|
|
undoLogHead = undoLogHead->iue_forw;
|
|
ASSERT(undoLogHead != (internalUndoEvent *) NULL, "undoFreeHead");
|
|
}
|
|
while (undoLogHead->iue_type != UT_DELIM);
|
|
undoNumCommands--;
|
|
}
|
|
freeMagic((char *) undoLogHead);
|
|
undoLogHead = undoLogHead->iue_forw;
|
|
undoLogHead->iue_back = (internalUndoEvent *) NULL;
|
|
}
|
|
|
|
/*
|
|
* ----------------------------------------------------------------------------
|
|
*
|
|
* undoMemTruncate --
|
|
*
|
|
* Delete events in memory which are later than the current event.
|
|
* NOTE: This expects to be called only when undoNumRecentEvents == 0.
|
|
*
|
|
* Results:
|
|
* None.
|
|
*
|
|
* Side effects:
|
|
* Truncates the event list so there are no events past undoLogCur.
|
|
*
|
|
* Updates undoLogTail.
|
|
*
|
|
* ----------------------------------------------------------------------------
|
|
*/
|
|
|
|
void
|
|
undoMemTruncate()
|
|
{
|
|
internalUndoEvent *up;
|
|
|
|
/*
|
|
* If there are events forward of the current event that
|
|
* will get overwritten by the new event, delete them from
|
|
* memory.
|
|
*/
|
|
|
|
if (undoLogCur == (internalUndoEvent *) NULL)
|
|
{
|
|
/*
|
|
* Delete ALL events from memory
|
|
*/
|
|
up = undoLogHead;
|
|
while (up != (internalUndoEvent *) NULL)
|
|
{
|
|
freeMagic((char *) up);
|
|
up = up->iue_forw;
|
|
}
|
|
undoLogTail = undoLogHead = (internalUndoEvent *) NULL;
|
|
undoNumCommands = 0;
|
|
}
|
|
else
|
|
{
|
|
ASSERT(undoLogCur->iue_type == UT_DELIM, "undoMemTruncate");
|
|
/*
|
|
* Delete only some of the events in main memory.
|
|
*/
|
|
up = undoLogCur->iue_forw;
|
|
while (up != (internalUndoEvent *) NULL)
|
|
{
|
|
if (up->iue_type == UT_DELIM)
|
|
undoNumCommands--;
|
|
freeMagic((char *) up);
|
|
up = up->iue_forw;
|
|
}
|
|
undoLogCur->iue_forw = (internalUndoEvent *) NULL;
|
|
undoLogTail = undoLogCur;
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
* ============================================================================
|
|
*
|
|
* DEBUGGING PROCEDURES
|
|
*
|
|
* ============================================================================
|
|
*/
|
|
|
|
void
|
|
undoPrintEvent(iup)
|
|
internalUndoEvent *iup;
|
|
{
|
|
char *client_name;
|
|
if (iup->iue_type < 0)
|
|
client_name = "(delimiter)";
|
|
else
|
|
client_name = undoClientTable[iup->iue_type].uc_name;
|
|
|
|
(void) TxPrintf("%p: \t%s \tf=%p \tb=%p\n",
|
|
(void *)iup, client_name, (void *)iup->iue_forw, (void *)iup->iue_back);
|
|
}
|
|
|
|
/* Print events forward from "iup". If n is 0 or negative, print to */
|
|
/* the end of the stack. Otherwise, print the next n events. */
|
|
|
|
void
|
|
undoPrintForw(iup, n)
|
|
internalUndoEvent *iup;
|
|
int n;
|
|
{
|
|
int i = 0;
|
|
|
|
(void) TxPrintf("head=%p\ttail=%p\tcur=%p\n",
|
|
(void *)undoLogHead, (void *)undoLogTail, (void *)undoLogCur);
|
|
if (iup == (internalUndoEvent *) NULL)
|
|
iup = undoLogHead;
|
|
while (iup != (internalUndoEvent *) NULL)
|
|
{
|
|
undoPrintEvent(iup);
|
|
iup = iup->iue_forw;
|
|
i++;
|
|
if (i == n) break;
|
|
}
|
|
}
|
|
|
|
/* Print events backward from "iup". If n is 0 or negative, print to */
|
|
/* the beginning of the stack. Otherwise, print the previous n events. */
|
|
|
|
void
|
|
undoPrintBack(iup, n)
|
|
internalUndoEvent *iup;
|
|
int n;
|
|
{
|
|
int i = 0;
|
|
|
|
(void) TxPrintf("head=%p\ttail=%p\tcur=%p\n",
|
|
(void *)undoLogHead, (void *)undoLogTail, (void *)undoLogCur);
|
|
if (iup == (internalUndoEvent *) NULL)
|
|
iup = undoLogTail;
|
|
while (iup != (internalUndoEvent *) NULL)
|
|
{
|
|
undoPrintEvent(iup);
|
|
iup = iup->iue_back;
|
|
i++;
|
|
if (i == n) break;
|
|
}
|
|
}
|
|
|
|
/* Called from windUndoCmd() or windRedoCmd() with n either positive, */
|
|
/* or negative with 1 offset to differentiate between n = 0 (forward) */
|
|
/* and n = 0 (backward). */
|
|
|
|
void
|
|
UndoStackTrace(n)
|
|
int n;
|
|
{
|
|
if (n < 0)
|
|
undoPrintBack(undoLogCur, -(n + 1));
|
|
else
|
|
undoPrintForw(undoLogCur, n);
|
|
}
|
|
|