UNDO.TXT for UnDo Version 2  (RJH LastRevision 8 August 2008)
UNDO.ZIP or XYWEB120.ZIP contains all the requisite files. If you
haven't already, please install XYWWWEB.U2 v120!

The underlying thinking behind the UnDo|ReDo program:

You can't reliably capture keystrokes in XyWrite's XPL memory buffer,
and you can't reverse the logic of XyWrite commands with XPL (consider,
e.g., the SORT command).  Even if you could do all this, you'd swiftly
be OOM (Out-Of-Memory).  In short, it can't be a "smart" Undo  la
MSWord, which un-does individual keystrokes.

The only reliable method is file backups.  Here are two schemes:  1)
Timed, and 2) Keystroke-Generated, backups.  Choose either method.  You
canNOT use both methods concurrently -- pick one or the other.  Windows
NT5+ ONLY for the Timed method ("NT5+" means 2000, XP, Server (not
tested), or Vista); 9x and NT5+ can both be used with the Keystroke-
Generated method.  NT4 will NOT work.

What UnDo Does:
UnDo performs classic "UnDo" and "ReDo" operations on texts.
ReDo simply reverses (cancels) the last UnDo.
Using the "Timed" method:
  UnDo restores the text to its state before periods of time that you
  establish -- for example, the first UnDo command might restore its
  state 10 seconds ago, the second 20 seconds ago, the third 30 seconds
  ago, etc.
Using the "Keystroke-Generated" method:
  UnDo restores the text to its state before particular actions
  such as block deletes (RemoveDefined command) or block inserts (CoPy
  command).  You establish which actions create an UnDo restore point.

To enable UnDos and ReDos, UnDo.exe makes backups whenever the file in
the current window changes.  UnDo.exe compares, byte for byte, its last-
made backup with the current content of a window.  If they are
different, UnDo.exe stores a new backup; otherwise, if the current
content hasn't changed since the last backup, then no new backup is
made.  UnDo stores backups to the "Depth" (max number of backups)
specified in REG variable "UnDoDepth".  When the maximum Depth is
reached, UnDo continually saves the newest backup and deletes the oldest
backup (e.g. if UnDoDepth=20 then backup #21 is created and backup #1 is

The Timed and Keystroke-Generated methods are described individually

Stack Users Only:
If (only if) you use U2's Stack facility, the Stack is automatically
saved to disk every 5 minutes using the Timed method, or once every 20
backups using the Keystroke-Generated method, for pre-loading via
STARTUP.INT at the onset of your next editing session (in order to
resume with your most recent Stack of commands).  REG variable
"Stored_Stack" (required) contains the d:\path\filename used to save the
Stack.  See HELP STACK for instructions about pre-loading the
Stack in XyWrite sessions.

Required external programs -- DOWNLOAD UnDo.ZIP to obtain them:
UnDo.exe - locate in Editor's directory
KillNB.exe - ditto
MyPID.exe - ditto

Get the latest (16 June 2007) XY4.DLG at XyWWWeb:
Make sure you LOAD XY4.DLG in STARTUP.INT!  The very FIRST frame in your
DLG file **MUST** be:
Check to make sure that frame is present!  Inspect carefully.

Kmd.exe is also required - locate kmd.exe in the DOS PATH (e.g.
C:\WINDOWS\system32); in a properly-configured XyWrite system, Editor's
directory should also be in the DOS PATH, and therefore Editor's dir is
also a suitable location for kmd.exe.  If you don't already have
kmd.exe, download it from XyWWWeb:

UnDo.exe does all the work (KillNB just kills UnDo.exe if you want to
stop making Timed backups -- no other purpose in the context of UnDo --
although KillNB is quite a powerful program in its own right:  at a DOS
prompt, command "KillNB /?" for a summary of functions).

UnDo requires a dedicated subdirectory of Editor's dir, \UD (e.g.
C:\XY4\UD), which contains the backups.  Frame UNDO.INT creates this
subdirectory automatically if it does not yet exist -- you do NOT need
to create it.

You'll require PLENTY of unused disk space for backups!  Theoretically,
you can retain max=998 backups of *each* of max=100 million different
source files.

Backups in \UD bear filenames (e.g. "6.001") unrelated to the source
file.  Do NOT try to CAll or edit files in the \UD subdirectory!  Ignore
\UD altogether.  \UD is off-limits to users.

The only situation in which backup "confusion" can occur is if different
UNTITLED texts are opened *successively* in the same window number: 
repeated UnDos might eventually display an old abandoned text (but ReDo
will immediately restore the current UNTITLED text).  This could happen
because it is impossible for UnDo to distinguish between a changed
UNTITLED text and a wholly different UNTITLED text.

and unZIP all files in Editor's directory.

Contents of UnDo.ZIP:
  UNDO.FRM (UnDo programming to be inserted in U2)
  UNDO.REG (*sample* REG stanza)
  UNDO.TXT (this text file)

Get the latest (16 June 2007) XY4.DLG at XyWWWeb:
Make sure you LOAD XY4.DLG in STARTUP.INT!  The very first frame in your
DLG file MUST be:

Add a new stanza "[UnDo]" to XyWWWeb.REG, per different instructions for
each Method, below.  See the sample UNDO.REG in UnDo.ZIP.

U2 Frames
Install 10 new frames and 4 revised frames in XyWWWeb.U2 (CoPy from file
UNDO.FRM into U2, then LOADHELP -- or DeFine the entirety of
file UNDO.FRM, then command ADD2U2).


1)  $M
$M is the gateway for external programs that communicate with XyWrite. 
Frame $M contains a menu of programs that can be run by XyWrite.  When
function is poked by an external program, XyWrite looks in file
XYMENU in Editor's directory to determine which MenuNumber (which
program) in the $M menu to run.  File XYMENU is a reuseable file written
by the external program (here, UnDo.exe); XYMENU contains the
MenuNumber, i.e. the program to run.

2)  R?:*
R?:* traps and discards any function that is "poked" by an external
program (like UnDo.exe) during an AERCAF or AERKAF (ReadCharacter)
operation.  For future use.

UNDO.INT provides INiTialization.  You MUST run UNDO.INT *or* RUD (see
below) in STARTUP.INT:  edit STARTUP and enter a separate line in
STARTUP as follows, *after* XyWWWeb.U2 is LOADed, e.g.:
You can also launch UNDO.INT manually, on the CMline, to start
UnDo for the *first* time in a session.  UNDO.INT creates subdir \UD if
it doesn't already exist, or erases any existing files in \UD

RUD restarts UnDo under the Timed method after UnDo is turned off with
KUD (described below, frame 8; KUD="Kill UnDo", RUD="Restart UnDo"). 
RUD does NOT erase existing files in \UD; you can resume UnDo-ing and
ReDo-ing where you left off.  KUD+RUD is mainly useful for suspending
UnDo when it might interfere with a running XPL program.

The main difference between UNDO.INT and RUD is that UNDO.INT wipes the
slate clean -- deletes all files in directory \UD.  If you want those
files to survive from session to session, then put in STARTUP.INT frame
RUD instead of frame UNDO.INT.

If you use Stack, then run frame STACK.INT in STARTUP.INT *before* you
run frame UNDO.INT.

4)  TUD
Creates Timed UnDo backups.  You NEVER issue this command manually! 
UnDo.exe manages it, hands-off.

5)  $K
Creates Keystroke-Triggered UnDo backups.

Writes the appropriate backup over the current file, then RECAlls it. 
When you want to UnDo or ReDo, issue "UnDo" or "ReDo"
on the CMline (could also assign to a key).

Note Well!  UnDo and ReDo remember their position in the backup queue. 
You can UnDo to the full "UnDoDepth" (number of levels) of backups and
then ReDo up to the most recent version.  BUT, if you edit (in ANY
respect change, even without SAving) any document displayed as a result
of an UnDo or ReDo, then that signifies *acceptance* by you of that
backup as your *current document*, and THEREFORE the capacity to ReDo
(revert to a more recent backup than the current document) is LOST!
You have 10 backups (because REG variable "UnDoDepth=10").  Number 1 is
the oldest, number 10 is the most recent (the current document).  You
UnDo three times, displaying #9, then #8, then #7.  Then you ReDo once,
displaying #8.  You edit #8.  Backups 9 and 10 are LOST!  You can still
UnDo to #7 and earlier, but you cannot ReDo above #8.

Experiment with *DUMMY DOCUMENTS* to learn how UnDo and ReDo work!

7)  CUD[/NV][ d:\path\filename|UNTITLD{WindowNumber}]
Purge ("C"lear) the UnDo backups for the current file.  /NV optionally
purges *all* files in \UD, creating an empty directory.  Add a fully-
qualified filename argument, to purge backups of a file which is not the
current file.  UNTITLD# files may also be purged.  Tip:  REad UNDOIDX in
Editor's directory to learn which files are being tracked (indexed) by

8)  KUD
Kills UnDo.exe (stops a running UnDo.exe) when using the Timed method. 
You might want to Kill UnDo temporarily in order to run a long program
that accepts keyboard input, with which Timed UnDo's poked keystrokes
might interfere.  Afterward, Restart (resume) UnDo with the RUD command
(described above, frame 3).

SHORTSTK is a new frame that writes the current Stack to disk.  SHORTSTK
produces the same result as the old STACK frame, but saves the Stack
without using an empty window or any Save/Get memory.  As with STACK,
the file produced by SHORTSTK may simply be RUN as a freestanding
program at any time (e.g. "RUN MY.STK"), to replace the current (or
empty) Stack with the stored Stack.

10)  MYPID
MyPID.EXE performs a special service that XyWrite has long needed.  It
writes to disk, in file MyPID.DAT in Editor's directory, three pieces of
essential Windows system information about the current XyWrite session: 
its ProcessID ("PID"), the handle ("HWND") to the XyWrite window on the
Desktop, and XyWrite's "Window Title".  U2 frame MYPID reads the first
of these values; the PID is used by frame FINITO and KillNB.exe to
unambiguously close XyWrite.

Run MyPID.EXE in STARTUP.INT:  edit STARTUP and enter a separate line in
STARTUP as follows, e.g.:
do/nv/x/z C:\XY4\MyPID.EXE;*;

FINITO is an old frame, which you put on a key to QUIT XyWrite.  I
modified it to kill Timed method UnDo.exe before you Quit XyWrite.
If you don't use FINITO, then DO SOMETHING ELSE to kill "Timed"
UnDo.exe, e.g. issue KUD manually, or assign this to a key:
N.B.:  You do NOT want "Timed" UnDo.exe to continue to run after you
Quit XyWrite!

These three old frames are slightly rewritten to utilize a faster method
of retrieving the special "?xx" Save/Gets, such as "?WI", the Window

Description of the "Timed" method:
Change the "KEYS=" spec in the ";DEFINITIONS" section of your KBD file
from, e.g. "KEYS=105", to "KEYS=115".  Assign frame $M to key 115 in
*every* TABLE in your KBD file (write "115=$M"); if you map $M to key
#115 in each "TABLE" of your KBD file, any shifting keys that happen to
be depressed by you will also generate $M.  This way, UnDo never
interferes with your keystrokes.

Every X seconds, UnDo pokes key 115 into the keyboard buffer of the
process that is currently running in the foreground.  Due to a bit of
stunning good fortune, XyWrite is the ONLY application on your computer
that listens for and recognizes key 115!  Every other application
ignores it.  Poking that keystroke into the keyboard buffer every X
seconds tells XyWrite to EXECUTE the backup procedure ($M) assigned to
the keystroke, just as if you had fingered it manually!

Key 115 does not even exist in reality (except on gigantic compositor
keyboards with 140 keys or so), so you can't possibly be deprived of its

Note Regarding Key 115:
Key 115 lies outside the range of most XyWrite keyboard files -- which
makes it especially suitable for UnDo, because it does not conflict with
any other keystrokes in your KBD file, yet it is recognized by XyWrite
(tested:  Win2000, WinXP, Vista without UAC).  To enable it, you must
change the "KEYS=" spec in the ";DEFINITIONS" section of your KBD file
from, e.g., "KEYS=105" to "KEYS=115".  Then simply add a "115=$M" at the
*end* of *every* "TABLE" in the keyboard, and re-LOAD your KBD file.  No
intermediate key assignments are required between your old last key, e.g
105, and key 115 -- in other words, the end of a TABLE might look like

User Variables:
---- ---------
You establish, in REG Variable "UnDoFreq", the value of X seconds, i.e.
the frequency of backups:  10, 20, 30, 60, whatever (minimum=5 seconds). 
You also determine, in REG Variable "UnDoDepth", how many levels of
backup you want to maintain (Tip:  Do NOT use the full 998 maximum,
because UnDo will be RENaming ALL your backups EVERY time you make a new
one!  use a reasonable number, like 20 or 100 or 300).

When you initialize UnDo via frame UNDO.INT (or RUD), UnDo.exe is given
four pieces of information, derived from three REG Variables
(UnDoMethod, UnDoFreq, UnDoDepth) and XyWrite Save/Get 620.  UNDO.INT
passes an argument to UnDo.exe like "T 10 100 0", which means:
  T = Timed method
  10 = check every 10 seconds whether a backup is required
  100 = maintain 100 levels of backup
  0 = you use Stack (1=you do not use Stack).  This value is determined
automatically and internally, based on the value of Save/Get 620 when
UnDo is launched -- so *if* you use Stack, run frame STACK.INT in
STARTUP.INT *before* you run frame UNDO.INT (or RUD)!

New Stanza in REG ("Timed" method):
UnDoMethod=T   <==Timed method -- the single letter "t"
UnDoFreq=10    <==check document status every X seconds, e.g. 10 seconds
UnDoDepth=100  <==number of backups to maintain, e.g. 100

You can *test* that UnDo.exe is communicating correctly with XyWrite by
starting UnDo.exe and then running IDKEY.  Every X seconds
IDKEY should jump to your $M key, number 115.  Hit an ordinary alpha key
A-Z to move IDKEY off the $M key, then watch IDKEY pop back to your $M
key 115 again X seconds later...

If the current file hasn't changed since the last backup, then no backup
is made.  UnDo.exe _checks_ every X seconds, but if the file hasn't
changed, then nothing happens.

The Timed method will probably screw up in Vista if UAC (User Access
Control) is enabled; on my machine UAC is disabled, and everything
works.  UAC users will need to run Xy with "elevated privileges" (UAC is
a total PITA, in my opinion...)

The timed method is similar to Xy's native AOT method, which however
keeps only ONE level of backup depth, and operates only on whole
minutes: 1, 5, 50 (you can't specify, e.g., "d aot=.5" for every 30
seconds).  Pretty sad.

Description of the "Keystroke-Generated" method:
This is an UNtimed procedure (no poking into keyboard buffer).

The user decides what constitutes an "undo-able" action, and codes
KBDfile appropriately, inserting function $K (_not_ $M) before
keystrokes that do "big things":  copy, paste, delete block, copy block,
move block,  maybe, etc etc.  The user decides what actions
rise to the level of an UnDo.

New Stanza in REG ("Keystroke-Generated" method):
UnDoMethod=K  <==MUST be the single letter "k"
UnDoFreq=     <==leave this empty -- it is ignored
UnDoDepth=50  <==number of backups to maintain, e.g. 50

If you use Stack, run frame STACK.INT in STARTUP.INT *before* you run
frame UNDO.INT

The "Keystroke-Generated" method is very simple to implement, and works
like a charm.

What UnDo is NOT!
UnDo is not a general backup utility.  It doesn't supply permanent
backups.  It creates transient UnDo and ReDo capabilities, only.  Do NOT
call UnDo files (files in directory \UD) into windows manually -- "Hands
Off" of directory \UD, it's a no-go area for users!

Do NOT command UnDo or ReDo when *editing* LOADed and *indexed*
Helpfiles (U2, DLG)!  You WILL crash those files, and probably your
session too.

Reserved Filenames
======== =========
In Editor's directory:
Leave these files alone.  Don't CAll or edit them.  If you're insatiably
curious, you may REad them.