[comp.sys.atari.st] Uemail source

RDROYA01@ULKYVX.BITNET.UUCP (02/04/87)

#       This is a shell archive.
#       Remove everything above and including the cut line.
#       Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar: Shell Archiver
#       Run the following text with /bin/sh to create:
#       readme.mss
#       uemail.mss
# This archive created: Tue Feb  3 19:13:50 1987
cat << \SHAR_EOF > readme.mss
 
The following 11 files are the source code to uemail ST.  I have had a
number of requests for this source and decided to post it to the atari
group all at once.  I decided not to post it to net sources because the
program has been made so ST specific that I doubt it would have much appeal
for the mainframe crowd.  This editor is based on the original D. G. Conroy
version that I believe was written about 1978.  That version was 27.  This
program was ported to the ST from a CP/M-68K version I made a year ago.
The original has not been around for some years.  One of my instructors
found the source for me on an old disk he had in his office.  All this is a
round-about way of saying that the ancestry of this version is very
different from that of most of the current microemaxen.  I have tried to
add some GNU type commands and to make a few of the standard commands work
the way they do with Vax GNU, but this is not microGNU.  It does have an
aliasing feature and a command interface.  I use it as a shell, and never
see the desktop after startup.  I can even connect to remote systems via
the kermit section (which will be improved once my ordered copy of the
kermit protocol manual arrives).
 
I hope people find this code as educational as I have, and I would like to
thank Mr. Conroy for writing such interesting and useful C.  For better or
worse I taught myself that language with his program as guide.  Of course
there are others whose code appears in this program.  Most of them I do not
know, or know only through the NET.  Notable are Richard Stallman whose GNU
makes teaching writing on the computer much saner than it was before, and
Jwahar R. Bammi @ Case whose ST code has always been solid.
 
SHAR_EOF
cat << \SHAR_EOF > uemail.mss
 
                                   Uemail
 
Uemail is an emacs-like full-screen editor for the Atari-ST computer
equipped with a color or monochrome monitor.  It supports all of the major
commands offered by microEmacs version 30, plus some extensions that make
its interface closer to that used by GNU_EMACS. Although smaller than
1ST_WORD and many GEM-based programs, uemail is a large program by TOS
standards.  It was ported from a two megabyte 68K system and scaled down to
fit in the small memory of a 1040ST. It requires more than 128K to load.
To support shell commands, it returns 256K to the operating system at
startup.  On the 68K system the shell command required only 12K of code and
128K of operating space and did not require loading a command processor to
get at the operating system.  But since the ST has no command control
processor, you must load a shell program such as COMMAND.PRG or
PCOMMAND.PRG to get at the file system.  You may run any TOS based program
from within the editor, but programs that require a GEM interface will
crash the system.  You will have to exit the editor entirely to run GEM
programs as there is just not enough extra memory to run GEM programs.
 
Running uemail from within a shell program diminishes the available text
buffer space.  The return TPA command (ESC-@) reports the amount of space
used and the amount available.  Long source code files (900 or more lines)
are the biggest memory eaters.  If uemail runs directly from the desktop,
buffer space is rarely a problem even with a 360K ramdisk loaded, but
running this program and trying to edit large files on a 520ST may be a
problem if ramdisks or numerous desk accessories are in memory.  Files
consume approximately one and one-half times their physical size.  Text
files consume a little less, and because program code often has a number of
blank lines for which space must be allocated, it consumes somewhat more
memory in relation to its size.
 
The main reason for the size of the program is the added kermit utility not
usually found in a text editor, and the aliasing system for loading text
files and shell programs.  The disadvantage of tying these functions into
the program and using up memory space is outwayed by the advantage gained
by integration.  The ability to transfer text directly into and out of an
editor buffer saves time and programming effort.  The integrated program
also gives the programmer a relatively easy way to implement individual
protocol schemes to communicate with remote systems since all of the
readaux and sendaux functions are already defined.  If the kermit section
and the terminal section were to be separate from the editor, then loading
those free standing programs into memory would require a larger shell space
as each of those programs would be linked to its own copy of the run-time
library.  Finally, the additional features make the editor more of an
all-purpose tool, the type of program that can be loaded and serve as a
simple shell from which to carry out most tasks on the computer.
 
Uemail is designed to work in tandem with GNU_EMACS on a remote computer.
When the program is connected to the serial line, all of the function keys
and the number pad keys send the same commands as they do from within
uemail, and the defined keyboard macros (macros triggered by the accent
grave key) are also sent across the line. This allows you to set up special
function key definition files and macro definition files to use with a
remote emacs. Macro files can be loaded into memory from within an editor
buffer or while online using the terminal section.
 
In addition to the direct connect terminal section, uemail has a connect
mode which supports kermit protocol for 7-bit transfers between two
computers. This ability allows the editor to work with text from a remote
system and to transfer it back to that system when done.  For remotes that
do not support the kermit protocol there are log and transfer commands
which use the ST's flow control (settable from within the terminal mode) to
transfer text without dropping characters (provided that the remote
supports one of the ST's handshaking protocols).  Because the modem I have
does not support RTS/CTS, I have not been able to determine if it works as
well with an RTS/CTS protocol as it does with XON/XOFF.  Uemail sets the
baud rate while in connect mode.  At the beginning of the connect session,
the baud rate defaults to 1200 bps, but it can be reset to 300, 2400, 9600,
or 19200 bps.  When uemail exits from terminal mode, it saves the terminal
screen.  When it returns to terminal mode, it rewrites that screen placing
the cursor at its previous position.  You cannot set parity or stop bits
from within the terminal section, if anyone finds this to be a problem, let
me know, and I'll figure a way to get around it.  However, you can always
run a TOS-based modem program from within uemail that can set the additional
parameters.  The sample alias file shows how to define an xmodem program
that will run when the CTRL-C command is given `xmodem' for a program name.
 
Macro commands are a quick way to automate repetitive tasks, and they also
may be used to send strings, such as dial commands or logon scripts, to a
modem.  The limitations of the execute macro command (CTLX-E) in many
microemaxen are only one macro may be defined at a time, defining a new
macro kills the old one, macros cannot be saved across editing sessions,
and macros cannot be loaded from a disk file.  Uemail overcomes these
limitations while retaining the "flavor" of the original define and execute
macro command.  Any macro defined by the define macro command (CTLX-() may
be assigned to a key on the keyboard using the getmacro command (CTLX-`)
which will prompt for the key to bind.  The current keyboard macro is
copied into a safe place in memory, and a pointer to it is stored in a
macro table (a dynamically allocated structure).  From then on typing the
accent grave (`) key followed by the key that you assigned to that macro
will cause the appropriate macro to execute.  Macros can then be saved to
disk, using the `-CTRL-M command, for later loading, using the `-CTRL-L
command.  When the editor starts up, it attempts to load the file
UEMAIL.MCR default macro file from the default drive and path.  The
off-line program EXTMAC reads a macro file and writes a C-code equivalent
of each function to a file.  This external program, which can be run using
the shell command, replaces the old C-Mac buffer scheme in previous
versions and, unlike the earlier built-in function, will create C-code for
any defined macro.
 
Macro files may also be loaded from the terminal mode using the ALT-L
command.  Macros are used in the terminal mode the same way as they are in
an editor buffer (by typing a ` followed by the appropriate character).
Macros are of two basic types: character strings and command strings.
Character strings are words that have been typed to the screen while
defining a macro.  For example from within a buffer you can type the begin
macro command, type "ATDT5551234<CR>," followed by the end macro command
(CTLX-)), and the getmacro command (CTLX-`), binding this string to `5.
Then from the terminal section you can type `5 to send the string to your
modem. Command strings are macros which would normally cause uemail to
execute a series of commands (such as CTRL-A CTRL-O to go to the beginning
of the line and open a blank line above it).  Command strings make the most
sense when uemail is serving as a terminal connection to a remote Emacs
editor, allowing you to define a set of macros that behave the same way in
both editors.
 
As of version 33 of uemail ST, the cc command is an off-line program which
runs in the space uemail returns to the operating system.  The sample
start-up file (cc.ini) shows how to define an alias for CC.PRG which makes
the new interface transparent.  The CC.PRG will also run by itself to drive
the Alcyon compiler.  It accepts wildcards or multiple files on the command
line, can compiler or simply assemble a file.  (It goes directly to the
assembler for any file with an `.S' file type.)  It will link a group of
files if the cc.ini file has the `dolink=1' command in it.  It will compile
the files first if you supply a command line.  With no command line and the
dolink option on, the program will skip compilation and go directly to the
linker.  It envokes the linker by passing it a command file whose first
name is that defined in the cc.ini file as the `linkfile.'  If the linker
is successful, the program will attempt to relmod the resulting linker
output. To make all of this work properly you must define the parts of your
cc.ini program correctly.  the source directory is where the compiler,
assembler, and relmod expect to find their input files.  The temp directory
is where the assembler places its output.  The program expects the linkfile
to be on the source directory and assumes that the linkfile contains a
complete link68 script.  It also assumes that the linker output file will
have the same filename as the linkfile name, that the extension will be
68K, and that it will be located on the source directory.  Because the
link68 program cannot handle filenames longer than 13 letters, including
the path and drive, your filename plus source directory should be no more
than 10 characters long.
 
WARNING:  it is not safe to batter the CTRL-C key while CC.PRG is running in
the background as this will often send you back to the desktop (OK if you
have saved your files).  Instead if you find runaway errors while compiling,
type CTRL-S first and wait for the program to stop; then type one CTRL-C.
Future versions of uemail will incorpaorate a buffer in which to run all
shell commands.  That buffer will allow you to type a number of different
halt commands to stop runaway background programs.  It will also let you
keep the program output.  This will require operating system redirection and
all sorts of stack manipulation, so don't expect it any time soon, but a
version with this feature is currently running under CP/M-68K using the
Alcyon compiler.
 
One added ability the command buffer will give is being able to run SID
from within the editor and, with a split screen, to read the source for a
file currently being debugged.  The CP/M version uses the <ESC> key to
suspend the background process and return to the editing buffer.  That
allows the background program to be reentered at anytime without having it
reloaded.  Using SID this way to debug a file for which the symbols table
has remained intact, is very close to using the fancy symbolic debuggers on
other systems.  With two megs you can even load the assembly source code
produced by the compiler and go line by line through SID.  But these are
features not yet available for the ST version.
 
The aliasing feature mention above is implemented via the cc.ini file.  When
uemail starts up, it attempts to read the file from the current directory
and drive.  It attempts to read this file before any initialization takes
palce; therefore, you can use aliases (provided the cc.ini file is on the
correct drive and directory) on the startup line.  The cc.ini file (example
below) may contain any aliases you wish to use.  And new files may be loaded
from the disk (using CTLX CTRL-E) at any time.  The function key and number
pad definitions do not cause any additional space to be allocated.  But all
other aliases do use space (approximately 10 bytes plus the number of
characters in the alias string and the definition per each alias).
 
Aliases are freed from memory and new pointers are installed everytime the
alias function sees a matching alias name, it does not check to see whether
the aliases value is the same as the original, but simply reallocates space
and frees the old value.  This means that alias files need not contain
every value that is needed.  For example you might wish to redefine the
number pad or the function keys but not change the paths.  You can do this
by creating an alias file listing just those keys you wish to redefine and
loading that file with the CTLX-CTRL-E command.  The alias file also
controls the screen color with a color monitor; however the load alias
command issued from an editing buffer does not automatically update the
screen.  The new colors will be installed if you issue the set global modes
command and give it the word `color' as a mode name.  The colors will also
be installed when you return from a terminal session or from a background
(shell) operation.  The load command file command is also available from
the modem section using the ALT-E key.  In that case the colors are
installed immediately.  The following is a sample cc.ini file:
 
/* cc.ini file for cc.prg and uemail.prg
 * the first ten are used by the cc or link commands. the aliases must
 * be spelled as they are here and must be in lower case. change the
 * path names to match your set-up.
 */
/* these are for the cc.prg which also reads this file */
source=e:                       /* C, lnk, and 68K files        */
include=d:\stdlib.h\            /* headers                      */
temp=m:                         /* temporary files, o files, and final prg  */
symb=c:\bin\                    /* as68symb.dat                 */
bin=c:\bin\                     /* compilers, assembler, linker, and loader */
syslib=c:\bin\                  /* library directory            */
linkfile=xemacs                 /* filename of link command file*/
float=                          /* flag for floating point -f, -e or nil*/
dolink=1                        /* if 1, call linker after compiling file[s] */
delete=1                        /* delete I, S, 68K files? 1==YES       */
/* the next two are used by shell */
shell=c:\util\pcommand.prg      /* shell program        */
xmodem=c:\comm\bmodem.ttp       /* xmodem program       */
extmac=c:\uemail\extmac.ttp     /* extract macros       */
cc=c:\uemail\cc.prg             /* compiler driver      */
/* these are not used and if not needed, may be deleted */
home=c:\uemail\                 /* where it all began   */
root=c:\                        /* boot directory       */
assembly=d:\assembly.s\         /* assembly sources     */
etc=c:\util\                    /* utility programs     */
comm=c:\comm\                   /* communications       */
arc=c:\util\arc.ttp             /* arc cruncher         */
vt100=c:\comm\vt100.tos         /* small vt100 prog     */
/* set up colors the way I like. aliases and color names must be lower
 * case.  additional colors are grey, magenta, teal, green, yellow, and
 * white. cobalt is a deep blue. if colors are not aliased, you get
 * white on black as default.
 */
border=black                    /* color #0             */
desk=cobalt                     /* color #2             */
cursor=red                      /* color #1             */
letter=black                    /* color #3             */
/* function key bindings for uemail.ttp The F must be upper case */
F1=setmark                      /* sets the mark        */
F2=listbuffers                  /* lists current buffers        */
F3=filename                     /* change current filename      */
F4=writeregion                  /* write text between point and mark to file */
F5=fileinsert                   /* insert file at point         */
F6=fileread                     /* read file into current buffer*/
F7=filevisit                    /* visit (find) a file          */
F8=filewrite                    /* write out buffer to named file       */
F9=filesave                     /* save current buffer if changed       */
F10=quit                        /* save all changed buffers and exit    */
/* Number pad bindings for uemail.ttp. the N must be upper case as must
 * the word NENTER.
 */
N(=backword                     /* backward word        */
N)=forwword                     /* forward word         */
N/=grtw                         /* globally remove trailing whitespace */
N*=retversion                   /* return current version to message line */
N7=gotobol                      /* goto beginning of line */
N8=gotoeol                      /* goto end of line     */
N9=rettpa                       /* return usage to message line */
N-=pageback                     /* back by one text page (60 lines default) */
N4=backsent                     /* goto beginning of sentence */
N5=forwsent                     /* goto end of sentence */
N6=unkncom                      /* unknown              */
N+=pageforw                     /* forward by one text page */
N1=gotbop                       /* goto beginning of paragraph (blank line) */
N2=goteop                       /* goto end of paragraph (blank line) */
N3=unkncom                      /* unknown              */
N.=forwdel                      /* delete character under cursor */
NENTER=indent                   /* newline and indent   */
 
Each line must be formatted as above.  The word before the equal sign is
the `alias' name used by the program, and the line to the left of the equal
sign is the actual path name assigned to the aliased variable.  There can
be no embedded blanks or tabs in a name or alias, but comments may appear
on any line as long as there is at least one space or tab character between
the end of the path and the beginning of the comment.  No special comment
characters are necessary although using standard comment markers like those
above help to make the comments more readable.  The order of definitions is
unimportant, and lines that contain no `=' signs are ignored.
 
The aliasing mechanism attempts to find aliases for any command file you
type at the shell command (but not for the command line arguments).  Typing
`cc' after issuing the shell command (CTRL-C) and the filename at the
command line prompt invokes the compiler driver CC.PRG. It will also expand
`temp' to m:\ because it does not check to see is the aliased name makes
sense in context.  If the aliased name would not load, you will receive an
error message on the message line. You also can use the aliases the usual
way in the shell command.  If the first character of the program name is a
`~,' the function will attempt to expand the aliased path.  Thus
`~etc\arc.ttp' is the same as `arc' and `c:\util\arc.ttp.'
 
The set path command and all the file commands also use the alias table. If
at the command's prompt you type a line which begins with a `~', these
commands look up the word after the ~ and test it against the known
aliases.  If the word match matches a known alias, the string associated
with that alias is copied into the filename.  Thus if "temp=c:\files\temp"
is in the cc.ini file at startup, typing the read file command CTRL-X
CTRL-R and asking for the file ~temp\test.i will read in the file
c:\files\temp\test.i (note that the `\' character is necessary, and the `~'
must be the first character on the line).
 
Uemail also supports writers.  In addition to the useful macro function,
uemail provides sentence, paragraph, and page movement commands.  It
provides two punctuation movement commands, forward to next punctation and
backward to previous puntuation, useful for moving to the next comma or
quotation mark in a text.  Uemail extends the usual microemacs on-screen
editing features, adding an indent column with the ability to define an
indent prefix.  Unlike other versions of microemacs, uemail's fill
paragraph command leaves the cursor inside the paragraph in the same spot
where it was when the command was issued.  There are line-based commands for
formatting texts.  The flushleft, flushright, center, transpose words, and
globally delete trailing whitespace all deal with text appearance.  The
indent subsequent newline same as this lets you open a line underneath the
present line with the same indentation without having to be at the end of
the present line.  The drop line and indent command works similarly but
inserts the new line above the present line.  Both of these functions are
not only useful for formatting text, but also for indenting program code.
 
Uemail provides a rudimentary interface for a spelling checker.  If your
spelling checker uses the standard spell correction mark (0x93), the
program will search for that character in your file when you type the go
spell command (CTLX-S), placing your cursor at the beginning of the word to
correct. The program has a word count function which reports the average
size of words in a buffer.  This function reports the length of your buffer
in letters and characters.  It uses the letter total to determine averages.
This leads to a more accurate average than methods which divide the total
number of characters in a buffer by the total number of words.
 
Uemail can print buffers with page numbers, two header lines, and single or
double spacing.  The page size is settable, and the internal code
automatically sets up any Diablo/Xerox compatible printer for standard 12
pitch type.  The program can print while editing (as long as there is
keyboard activity), and if you try to exit while printing, uemail will ask
whether you want to exit for sure.  In order for the print buffers command
to work properly, be sure to define a temp directory in your cc.ini file.
 
Uemail allows you to set your colors at start up to any four of nine
possible colors (black, white, grey, teal, red, green, yellow, magenta, and
cobalt).  Each of the four parts of the screen is attached to a color.  You
may use up to four color, defining them with lines in your cc.ini file (see
example above).  When you read a new set of colors, you update the colors
on the screen by issuing the set global modes command (META-M) and choosing
color as the new mode.  If you are currently in the kermit mode, the colors
are automatically updated when you load the file.  The aliasing mechanism
allows you to write short color files that contain only the information
needed to set up the colors you want and to load them into memory as
needed.  One additonal command (CTLX-C) alllows you to switch your
background to any color currently defined.  The command is intelligent
enough to prevent the screen from "dissappearing" if you choose to make the
background the same as the letter color (background is the `desk').  For
example, if your desk is cobalt and your letters are black, issuing the
command and answering black for the new color will give you a black desk
with blue letters and a blue border.  A similar transposition occurs if you
wish the background to be the color of the cursor.
 
Unlike most microEmaxen, uemail follows the GNU_EMACS convention of
writing all `dirty' buffers when exitting.  If a normal exit is chosen
(CTLX-CTRL-C) and no argument is given, the program asks the user whether
individual modified files should be saved.  The quick exit command (CTRL-Z)
saves every modified buffer without asking.  The ST is prone to BUS and
address errors, uemail tries to protect the user from these by intercepting
the vectors associated with these exceptions. When the code signals an
error, the program gives you the opportunity to save your files before
restoring the system and exiting.  This can avoid a lot of frustration.
 
The following list of commands is arranged according to category.  The
column marked KEY shows the keystrokes bound to the command.  The column
marked COMMAND gives the internal name of the command that uemail uses.
And the DESCRIPTION gives a brief explanation of what the command does.  In
the chart, the ^ character in front of a letter means that letter is a
control character; ESC is the escape key, and ` is the accent grave.  Any
ESC sequence that uses a simple alphabetic key as the second argument is
reachable using the ALT key while typing the alpha key.  For example both
ESC-F and ALT-F move forward one word.  The difference between the two
options is that the ESC version requires typing two keys, while the ALT
version requires one "shifted" keystroke.  Some of the commands below react
differently depending upon whether the function is passed a numeric
argument.  Numeric arguments are used most often to cause commands to
repeat a set number of times.  There are two ways to signify an argument to
a function.  Each method requires typing a repeat command before typing the
function.  The two repeat commands are CTRL-U and the ESC key followed by
any digit or by the negative sign.  CTRL-U is called the universal prefix
and always defaults to four repeats.  Typing CTRL-U repeatedly multiplies
the repeat count.  The count echos to the message line.  Typing ESC
followed by a number does essentially the same thing but allows you to
choose the number directly.  For example: ESC-2 followed by CTRL-K deletes
two full lines forward, which is equivalent to deleting a line and the
trailing newline.  Some commands look for zero arguments to change their
normal actions.  For example: CTLX-D is the set path command; however, when
it is prefaced with ESC-0 or CTRL-U-0, it returns the current default drive
and path to the message line.  The showtime command is similar.  If it is
called normally, without an argument, or if it is called with a zero or
negative argument, it writes the current time to the message line.  If it
is called with a possitive argument less than 1000 (a special case), it
writes the time into the document at the current position.  The 1000 value
is used by the kermit section to write to the message line.  Since the
kermit section must use a different message writing scheme from that used
by the editor.
 
Commands for Uemail
===================
KEY (lower case OK)     COMMAND                 DESCRIPTION
--------------------------------------------------------------------------
MISCELLANEOUS UTILITIES
ESC-~           clearflag();            /* Clear changed buffer flag    */
^X-~ or ^C      shell();                /* run a child process or cc    */
^Q              quote();                /* Insert literal               */
^G              ctrlg();                /* Abort out of things          */
^X-^C           quit();                 /* Quit prompt to save files    */
^Z              quickexit();            /* low keystroke exit and save  */
^X-=            showcpos();             /* Show the cursor position     */
^X-W            wc();                   /* word and line count of buffer*/
^X-*            retversion();           /* version date                 */
^X-M            setmode();              /* set a mode for a buffer      */
^X-^T           showtime();             /* tell the time or insert in buffer */
ESC-M           sglmode();              /* set global mode              */
ESC-X           mdoncom();              /* do named command             */
ESC-@           rettpa();               /* show available memory and usage */
ESC-^N          enumerate();            /* start or incr. counter variable */
Lots-o-keys     unkncom();              /* unknown command BEEP         */
 
FILE OPERATIONS
^X-^E           commfill();             /* Read a cc.ini command file   */
^X-I            fileinsert();           /* Insert existing file at point*/
^X-^R           fileread();             /* Get a file, into existing buf*/
^X-^V           filevisit();            /* Get a file, new buffer       */
^X-^W           filewrite();            /* Write a file                 */
^X-^S           filesave();             /* Save current file            */
^X-^F           filename();             /* Change file name             */
^X-D            setpath();              /* set path if arg=0 get path   */
 
CURSOR MOVEMENT
^A              gotobol();              /* Move to start of line or prev*/
^E              gotoeol();              /* Move to end of line or next  */
^B              backchar();             /* Move backward by characters  */
^F              forwchar();             /* Move forward by characters   */
^P              backline();             /* Move backward by lines       */
^N              forwline();             /* Move forward by lines        */
ESC-V           backpage();             /* Move backward by screens     */
^V              forwpage();             /* Move forward by screens      */
ESC-B or ESC-^B backword();             /* Backup by words              */
ESC-F or ESC-^F forwword();             /* Advance by words             */
ESC-N           goteop();               /* goto end of paragraph        */
ESC-P           gotbop();               /* goto beginning of paragraph  */
^X-<            btopunct();             /* move backward to last punctuation */
^X->            ftopunct();             /* move forward to next punctuation  */
ESC-A           backsent();             /* backward to beginning of sentence */
ESC-E           forwsent();             /* forward to end of sentence   */
ESC-, or ESC-<  gotobob();              /* Move to start of buffer      */
ESC-. or ESC->  gotoeob();              /* Move to end of buffer        */
ESC-G           goline();               /* goto arg line in text        */
^@ or ESC-SP    setmark();              /* Set mark                     */
^X-^X           swapmark();             /* Swap "." and mark            */
 
DOCUMENT FORMATTING
^X-#            setpage();              /* set page size to arg length  */
^X-!            paginate();             /* insert '\f' every page       */
^X-+            pageforw();             /* move forward set page length */
^X-- (dash)     pageback();             /* move back set page length    */
^X-TAB          print();                /* print buffer with heading    */
^X-F            setfillcol();           /* Set fill column.             */
ESC-Q           fillpar();              /* fill paragraph to fill column*/
^X-.            setindcol();            /* Set indent column or prefix  */
^T              twiddle();              /* Twiddle characters           */
ESC-T           twaddle();              /* transpaose two words in place*/
TAB             tab();                  /* Insert tab                   */
RETURN          newline();              /* Insert CR-LF                 */
^J              indent();               /* Insert CR-LF, then indent    */
^O              openline();             /* Open up a blank line         */
^X-^O           deblank();              /* Delete blank lines           */
ESC-O           mdropln();              /* drop current line and move up*/
ESC-J           mindnl();               /* indent subsequent NL same as this */
ESC-\           mdelind();              /* delete beginning line indentation */
ESC-^R          mrflush();              /* flush right current line     */
ESC-^C          mcenter();              /* center current line          */
^X-\            grtw();                 /* remove trailing white space  */
ESC-^O          clowsp();               /* close up intervening white space  */
ESC-^P          tglcase();              /* toggle case of letter at point    */
 
BUFFERS AND WINDOWS
^X-^B           listbuffers();          /* Display list of buffers      */
^X-B            usebuffer();            /* Switch a window to a buffer  */
^X-K            killbuffer();           /* Make a buffer go away.       */
ESC-!           reposition();           /* Reposition window            */
^L              refresh();              /* Refresh the screen           */
^X-^N           mvdnwind();             /* Scroll window down           */
^X-^P           mvupwind();             /* Scroll window up             */
^X-N            nextwind();             /* Move to the next window      */
^X-P or ^X-O    prevwind();             /* Move to the previous window  */
^X-1            onlywind();             /* Make current window only one */
^X-2            splitwind();            /* Split current window         */
^X-Z            enlargewind();          /* Enlarge display window.      */
^X-^Z           shrinkwind();           /* Shrink window.               */
^X-C            paintbuffer();          /* change the backgound color   */
 
DELETION AND REGIONS
ESC-H           markpar();              /* mark paragraph sets mark     */
^D              forwdel();              /* Forward delete               */
DEL             backdel();              /* Backward delete              */
ESC-TAB         kill();                 /* Kill line forward            */
^Y              yank();                 /* Yank back from killbuffer.   */
ESC-U           upperword();            /* Upper case word.             */
ESC-L           lowerword();            /* Lower case word.             */
^X-^L           upperregion();          /* Upper case region.           */
^X-^U           lowerregion();          /* Lower case region.           */
ESC-C           capword();              /* Initial capitalize word.     */
ESC-D           delfword();             /* Delete forward word.         */
ESC-DEL         delbword();             /* Delete backward word.        */
^K              mdeleln();              /* delete entire line from beginning */
ESC-^K          mdelwln();              /* delete entire line including NL   */
ESC-K           killsent();             /* kill sentence forward sets mark */
^W              killregion();           /* Kill region.                 */
ESC-W           copyregion();           /* Copy region to kill buffer.  */
^X-R            writereg();             /* write defined region to file */
 
SEARCHING
^S              forwsearch();           /* Search forward               */
^R              backsearch();           /* Search backwards             */
ESC-R           replace();              /* Search and replace           */
`^R or ESC-^T   backisearch();          /* Incremental search back      */
`^S or ESC-^S   forwisearch();          /* Incremental search forward   */
^X-S            gospell();              /* Search for 0x93 (unix spell) */
 
Function Key, Cursor Pad, and Numeric pad bindings.  The keycode associated
with the primary EMACS binding for each of the rebound functions is sent
over the serial line.  This allows you to set up your bindings at the ST to
match those you normally use with the EMACS on a remote computer.
 
KEY             FUNCTION
===========================
F1              setmark()
F2              listbuffers()
F3              filename()
F4              writeregion()
F5              fileinsert()
F6              fileread()
F7              filevisit()
F8              filewrite()
F9              filesave()
F10             quickexit()
Number pad      (bound to functions except when CapsLock is on)
(               backword()
)               forwword()
/               grtw()
*               retversion()
7               gotobol()
8               gotoeol()
-               pageback()
4               backsent()
5               forwsent()
+               pageforw()
1               gotbop()        /* beginning of paragraph */
2               goteop()        /* end of paragraph */
0               Control-X prefix
               forwdel()
ENTER           indent()
[All of the other number pad keys are bound to unkncom() (the unknown
command function), but those can be rebound to something useful through a
command file.]
[The cursor pad cannot be rebound.  It would not be that difficult to add
this feature to the source.]
CURSORS         what they look like they should do
INSERT          openline()
UNDO            yank()
HELP            kermit()        showhelp() in terminal mode
CLR             refresh()
 
KERMIT (and subcommands)
^_ or HELP      kermit();               /* file transfer */
 ________________
| [The following subcommands are supported.  Only the first letter is
|  significant.]
|  send         send a file (in a uemail buffer) to remote kermit
|  receive      receive a file from remote
|  put          send a file in server mode
|  get          recieve in server mode (uemail prompts for remote name)
|  finish       finish remote kermit server mode
|  bye          finish server and logoff remote
|  transfer     send a uemail buffer to remote appending EOF to end
|  emacs        same as above but without EOF
|  log          copy remote session into a uemail buffer (`^\ turns it off)
|  connect      connect to the RS232 line in terminal mode
|  ^G           abort a kermit session (get, put, rec, send, fin, or bye)
|               Use this if one of the kermit commands appears to "lock up"
 ================
|  [Terminal mode commands.]
|       ALT-CTRL-B      send break
|       ALT-CTRL-C      run program or shell
|       ALT-B           set baud rate (prompts for value)
|       ALT-C           change background color
|       ALT-E           load alias file and update color
|       ALT-L           load macros file
|       ALT-O           turn off handshaking
|       ALT-R           use RTS/CTS handshaking
|       ALT-T           show time on mode line
|       ALT-X           use XON/XOFF handshaking
|       ALT-?           show help information
|       HELP            show help information
|       ALT-UNDO        exit terminal mode (return to current uemail buffer)
|       ALT-any-other   sends the character (i.e. NULL)
 ----------------
 
MACROS
======
^X-(            ctlxlp();               /* Begin macro                  */
^X-)            ctlxrp();               /* End macro                    */
^X-E            ctlxe();                /* Execute macro                */
 ________________
| The following commands let you bind a macro that was defined using
| ^X( to any keyboard key.  That macro can be executed using the accent
| grave (putmacro) command.  The macros can be saved to disk and reloaded
| when needed.
 ----------------
` (grave)       putmacro();             /* print line macros in text    */
^X`             getmacro();             /* bind current kbdm to key     */
[The following macros are predefined and cannot be rebound.]
`^G             abort
`^L             load a file of previously defined macros
`^M             write currently defined macros to file
`^R             incremental search back
`^S             incremental search forward
`^T             show time on mode line
``              insert an accent grave
SHAR_EOF
#       End of shell archive
exit 0
%NONAME-W-NOMSG, Message number 00000000

RDROYA01@ULKYVX.BITNET.UUCP (02/04/87)

#       This is a shell archive.
#       Remove everything above and including the cut line.
#       Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar: Shell Archiver
#       Run the following text with /bin/sh to create:
#       basic.c
#       buffer.c
#       region.c
# This archive created: Tue Feb  3 17:41:14 1987
cat << \SHAR_EOF > basic.c
/*
 * The routines in this file
 * move the cursor around on the screen.
 * They compute a new value for the cursor, then
 * adjust ".". The display code always updates the
 * cursor location, so only moves between lines,
 * or functions that adjust the top line in the window
 * and invalidate the framing, are hard.
 */
#include        <stdio.h>
#include        "ed.h"
 
/*
 * Move the cursor to the
 * beginning of the current line
 * or the beginning of the last
 * line if already at beginning.
 * Trivial.
 */
gotobol(f, n)
register int f, n;
{
        if(curwp->w_doto == 0)
                backline(f, n);
        curwp->w_doto  = 0;
        return (TRUE);
}
 
/*
 * Move the cursor backwards by
 * "n" characters. If "n" is less than
 * zero call "forwchar" to actually do the
 * move. Otherwise compute the new cursor
 * location. Error if you try and move
 * out of the buffer. Set the flag if the
 * line pointer for dot changes.
 */
backchar(f, n)
register int    f, n;
{
        register LINE   *lp;
 
        if (n < 0)
                return (forwchar(f, -n));
        while (n--) {
                if (curwp->w_doto == 0) {
                        if ((lp=lback(curwp->w_dotp)) == curbp->b_linep)
                                return (FALSE);
                        curwp->w_dotp  = lp;
                        curwp->w_doto  = llength(lp);
                        curwp->w_flag |= WFMOVE;
                } else
                        curwp->w_doto--;
        }
        return (TRUE);
}
 
/*
 * Move the cursor to the end
 * of the current line or the
 * end of the next line if
 * dot is already at the end.
 * Trivial.  No errors.
 */
gotoeol(f, n)
register int f, n;
{
        if (curwp->w_doto == llength(curwp->w_dotp))
                forwline(f, n);
        curwp->w_doto = llength(curwp->w_dotp);
        return (TRUE);
}
 
/*
 * Move the cursor forwwards by
 * "n" characters. If "n" is less than
 * zero call "backchar" to actually do the
 * move. Otherwise compute the new cursor
 * location, and move ".". Error if you
 * try and move off the end of the
 * buffer. Set the flag if the line pointer
 * for dot changes.
 */
forwchar(f, n)
register int    f, n;
{
        if (n < 0)
                return (backchar(f, -n));
        while (n--) {
                if (curwp->w_doto == llength(curwp->w_dotp)) {
                        if (curwp->w_dotp == curbp->b_linep)
                                return (FALSE);
                        curwp->w_dotp  = lforw(curwp->w_dotp);
                        curwp->w_doto  = 0;
                        curwp->w_flag |= WFMOVE;
                } else
                        curwp->w_doto++;
        }
        return (TRUE);
}
 
/*
 * Goto the beginning of the buffer.
 * Massive adjustment of dot. This is considered
 * to be hard motion; it really isn't if the original
 * value of dot is the same as the new value of dot.
 * Normally bound to "M-<".
 */
gotobob(f, n)
register int f, n;
{
        curwp->w_dotp  = lforw(curbp->b_linep);
        curwp->w_doto  = 0;
        curwp->w_flag |= WFHARD;
        return (TRUE);
}
 
/*
 * Move to the end of the buffer.
 * Dot is always put at the end of the
 * file (ZJ). The standard screen code does
 * most of the hard parts of update. Bound to
 * "M->".
 */
gotoeob(f, n)
register int f, n;
{
        curwp->w_dotp  = curbp->b_linep;
        curwp->w_doto  = 0;
        curwp->w_flag |= WFHARD;
        return (TRUE);
}
 
/*
 * Move forward by full lines.
 * If the number of lines to move is less
 * than zero, call the backward line function to
 * actually do it. The last command controls how
 * the goal column is set. Bound to "C-N". No
 * errors are possible.
 */
forwline(f, n)
register int f, n;
{
        register LINE   *dlp;
 
        if (n < 0)
                return (backline(f, -n));
        if ((lastflag&CFCPCN) == 0)             /* Reset goal if last   */
                curgoal = curcol;               /* not C-P or C-N       */
        thisflag |= CFCPCN;
        dlp = curwp->w_dotp;
        while (n-- && dlp!=curbp->b_linep)
                dlp = lforw(dlp);
        curwp->w_dotp  = dlp;
        curwp->w_doto  = getgoal(dlp);
        curwp->w_flag |= WFMOVE;
        return (TRUE);
}
 
/*
 * This function is like "forwline", but
 * goes backwards. The scheme is exactly the same.
 * Check for arguments that are less than zero and
 * call your alternate. Figure out the new line and
 * call "movedot" to perform the motion. No errors
 * are possible. Bound to "C-P".
 */
backline(f, n)
register int f, n;
{
        register LINE   *dlp;
 
        if (n < 0)
                return (forwline(f, -n));
        if ((lastflag&CFCPCN) == 0)             /* Reset goal if the    */
                curgoal = curcol;               /* last isn't C-P, C-N  */
        thisflag |= CFCPCN;
        dlp = curwp->w_dotp;
        while (n-- && lback(dlp)!=curbp->b_linep)
                dlp = lback(dlp);
        curwp->w_dotp  = dlp;
        curwp->w_doto  = getgoal(dlp);
        curwp->w_flag |= WFMOVE;
        return (TRUE);
}
 
/*
 * This routine, given a pointer to
 * a LINE, and the current cursor goal
 * column, return the best choice for the
 * offset. The offset is returned.
 * Used by "C-N" and "C-P".
 */
getgoal(dlp)
register LINE   *dlp;
{
        register int    c;
        register int    col;
        register int    newcol;
        register int    dbo;
 
        col = 0;
        dbo = 0;
        while (dbo != llength(dlp)) {
                c = lgetc(dlp, dbo);
                newcol = col;
                if (c == '\t')
                        newcol |= 0x07;
                else if (c<0x20 || c==0x7F)
                        ++newcol;
                ++newcol;
                if (newcol > curgoal)
                        break;
                col = newcol;
                ++dbo;
        }
        return (dbo);
}
 
/*
 * Scroll forward by a specified number
 * of lines, or by a full page if no argument.
 * Bound to "C-V". The "2" in the arithmetic on
 * the window size is the overlap; this value is
 * the default overlap value in ITS EMACS.
 * Because this zaps the top line in the display
 * window, we have to do a hard update.
 */
forwpage(f, n)
register int    f, n;
{
        register LINE   *lp;
 
        if (f == FALSE) {
                n = curwp->w_ntrows - 2;        /* Default scroll.      */
                if (n <= 0)                     /* Forget the overlap   */
                        n = 1;                  /* if tiny window.      */
        } else if (n < 0)
                return (backpage(f, -n));
#if     CVMVAS
        else                                    /* Convert from pages   */
                n *= curwp->w_ntrows;           /* to lines.            */
#endif
        lp = curwp->w_linep;
        while (n-- && lp!=curbp->b_linep)
                lp = lforw(lp);
        curwp->w_linep = lp;
        curwp->w_dotp  = lp;
        curwp->w_doto  = 0;
        curwp->w_flag |= WFHARD;
        return (TRUE);
}
 
/*
 * This command is like "forwpage",
 * but it goes backwards. The "2", like above,
 * is the overlap between the two windows. The
 * value is from the ITS EMACS manual. Bound
 * to "M-V". We do a hard update for exactly
 * the same reason.
 */
backpage(f, n)
register int    f, n;
{
        register LINE   *lp;
 
        if (f == FALSE) {
                n = curwp->w_ntrows - 2;        /* Default scroll.      */
                if (n <= 0)                     /* Don't blow up if the */
                        n = 1;                  /* window is tiny.      */
        } else if (n < 0)
                return (forwpage(f, -n));
#if     CVMVAS
        else                                    /* Convert from pages   */
                n *= curwp->w_ntrows;           /* to lines.            */
#endif
        lp = curwp->w_linep;
        while (n-- && lback(lp)!=curbp->b_linep)
                lp = lback(lp);
        curwp->w_linep = lp;
        curwp->w_dotp  = lp;
        curwp->w_doto  = 0;
        curwp->w_flag |= WFHARD;
        return (TRUE);
}
 
/*
 * Set the mark in the current window
 * to the value of "." in the window. No errors
 * are possible. Bound to "M-.".
 */
setmark(f, n)
register int f, n;
{
        curwp->w_markp = curwp->w_dotp;
        curwp->w_marko = curwp->w_doto;
        mlwrite("[Mark set]");
        return (TRUE);
}
 
/*
 * Swap the values of "." and "mark" in
 * the current window. This is pretty easy, bacause
 * all of the hard work gets done by the standard routine
 * that moves the mark about. The only possible error is
 * "no mark". Bound to "C-X C-X".
 */
swapmark(f, n)
register int f, n;
{
        register LINE   *odotp;
        register int    odoto;
 
        if (curwp->w_markp == NULL) {
                mlwrite("No mark in this window");
                return (FALSE);
        }
        odotp = curwp->w_dotp;
        odoto = curwp->w_doto;
        curwp->w_dotp  = curwp->w_markp;
        curwp->w_doto  = curwp->w_marko;
        curwp->w_markp = odotp;
        curwp->w_marko = odoto;
        curwp->w_flag |= WFMOVE;
        return (TRUE);
}
SHAR_EOF
cat << \SHAR_EOF > buffer.c
/*
 * Buffer management.
 * Some of the functions are internal,
 * and some are actually attached to user
 * keys. Like everyone else, they set hints
 * for the display system.
 */
#include        <stdio.h>
#include        "ed.h"
 
/* USEBUFFER eXtended command.  Prompt for buffer name.  Store it in
 * external pattern lastbuf.  Call selbuf() to make the change.  Bound
 * to CTLX-B.
 */
usebuffer(f, n)
register int f, n;
{
        register int    s;
 
        if ((s=readpattern("Use buffer [DEFAULT] ", &lastbuf)) != TRUE)
                return (s);
        return(selbuf(lastbuf));
}
 
/*
 * Attach a buffer to a window. The
 * values of dot and mark come from the buffer
 * if the use count is 0. Otherwise, they come
 * from some other window.
 */
selbuf(bufname)
register char *bufname;
{
        register BUFFER *bp;
        register WINDOW *wp;
 
        /* Find a buffer.  If the buffer does not exist, then ask the user
         * whether a new buffer should be created.
         */
 
        if ((bp=bfind(bufname, MAYBE, 0)) == NULL)
                return (FALSE);
        strcpy(lastbuf, curbp->b_bname);        /* set up for return    */
        if (--curbp->b_nwnd == 0) {             /* Last use.            */
                curbp->b_dotp  = curwp->w_dotp;
                curbp->b_doto  = curwp->w_doto;
                curbp->b_markp = curwp->w_markp;
                curbp->b_marko = curwp->w_marko;
        }
        curbp = bp;                             /* Switch.              */
        curwp->w_bufp  = bp;
        curwp->w_linep = bp->b_linep;           /* For macros, ignored. */
        curwp->w_flag |= WFMODE|WFFORCE|WFHARD; /* Quite nasty.         */
        if (bp->b_nwnd++ == 0) {                /* First use.           */
                curwp->w_dotp  = bp->b_dotp;
                curwp->w_doto  = bp->b_doto;
                curwp->w_markp = bp->b_markp;
                curwp->w_marko = bp->b_marko;
                return (TRUE);
        }
        wp = wheadp;                            /* Look for old.        */
        while (wp != NULL) {
                if (wp!=curwp && wp->w_bufp==bp) {
                        curwp->w_dotp  = wp->w_dotp;
                        curwp->w_doto  = wp->w_doto;
                        curwp->w_markp = wp->w_markp;
                        curwp->w_marko = wp->w_marko;
                        break;
                }
                wp = wp->w_wndp;
        }
        return (TRUE);
}
 
/* KILLBUFFER  eXtended command.  Prompt for buffer name.  Call delbuf()
 * to do the actual kill.  Bound to CTLX-K.
 */
killbuffer(f, n)
register int f, n;
{
        register int    s;
 
        if ((s=readpattern("Kill buffer [DEFAULT] ", &lastbuf)) != TRUE)
                return (s);
        return(delbuf(lastbuf));
}
 
/*
 * Dispose of a buffer, by name.
 * Look up bufname (don't get too
 * upset if it isn't there at all!). Get quite upset
 * if the buffer is being displayed. Clear the buffer (ask
 * if the buffer has been changed). Then free the header
 * line and the buffer header.
 */
delbuf(bufname)
register char *bufname;
{
        register BUFFER *bp;
        register BUFFER *bp1;
        register BUFFER *bp2;
        register int    s;
 
        if ((bp=bfind(bufname, FALSE, 0)) == NULL)      /* Easy if unknown.*/
                return (TRUE);
        strcpy(lastbuf, curbp->b_bname);
        if (bp->b_nwnd != 0) {                  /* Error if on screen.  */
                mlwrite("Buffer is being displayed");
                return (FALSE);
        }
        if ((s=bclear(bp)) != TRUE)             /* Blow text away.      */
                return (s);
        free((char *) bp->b_linep);             /* Release header line. */
        bp1 = NULL;                             /* Find the header.     */
        bp2 = bheadp;
        while (bp2 != bp) {
                bp1 = bp2;
                bp2 = bp2->b_bufp;
        }
        bp2 = bp2->b_bufp;                      /* Next one in chain.   */
        if (bp1 == NULL)                        /* Unlink it.           */
                bheadp = bp2;
        else
                bp1->b_bufp = bp2;
        free((char *) bp);                      /* Release buffer block */
        return (TRUE);
}
 
/*
 * List all of the active
 * buffers. First update the special
 * buffer that holds the list. Next make
 * sure at least 1 window is displaying the
 * buffer list, splitting the screen if this
 * is what it takes. Lastly, repaint all of
 * the windows that are displaying the
 * list. Bound to "C-X C-B".
 */
listbuffers(f, n)
register int f, n;
{
        register WINDOW *wp;
        register BUFFER *bp;
        register int    s;
 
        if (blistp == NULL)
                {
                blistp = bfind("[List]", TRUE, BFTEMP); /* Buffer list buffer*/
                if (blistp == NULL)
                        return(ABORT);
                }
        if ((s=makelist()) != TRUE)
                return (s);
        if (blistp->b_nwnd == 0) {              /* Not on screen yet.   */
                if ((wp=wpopup()) == NULL)
                        return (FALSE);
                bp = wp->w_bufp;
                if (--bp->b_nwnd == 0) {
                        bp->b_dotp  = wp->w_dotp;
                        bp->b_doto  = wp->w_doto;
                        bp->b_markp = wp->w_markp;
                        bp->b_marko = wp->w_marko;
                }
                wp->w_bufp  = blistp;
                ++blistp->b_nwnd;
        }
        wp = wheadp;
        while (wp != NULL) {
                if (wp->w_bufp == blistp) {
                        wp->w_linep = lforw(blistp->b_linep);
                        wp->w_dotp  = lforw(blistp->b_linep);
                        wp->w_doto  = 0;
                        wp->w_markp = NULL;
                        wp->w_marko = 0;
                        wp->w_flag |= WFMODE|WFHARD;
                }
                wp = wp->w_wndp;
        }
        return (TRUE);
}
 
/*
 * This routine rebuilds the
 * text in the special secret buffer
 * that holds the buffer list. It is called
 * by the list buffers command. Return TRUE
 * if everything works. Return FALSE if there
 * is an error (if there is no memory).
 */
makelist()
{
        register char   *cp1;
        register char   *cp2;
        register int    c;
        register BUFFER *bp;
        register LINE   *lp;
        register long   nbytes;
        register int    s;
        register int    type;
        char            b[6+1];
        char            line[128];
 
        blistp->b_flag &= ~BFCHG;               /* Don't complain!      */
        blistp->b_bmode |= BMNWRAP;
        if ((s=bclear(blistp)) != TRUE)         /* Blow old text away   */
                return (s);
        strcpy(blistp->b_fname, "");
        if (addline(blistp,"C   Size Buffer           File") == FALSE
        ||  addline(blistp,"-   ---- ------           ----") == FALSE)
                return (FALSE);
        bp = bheadp;                            /* For all buffers      */
        while (bp != NULL) {
                if ((bp->b_flag&BFTEMP) != 0) { /* Skip magic ones.     */
                        bp = bp->b_bufp;
                        continue;
                }
                cp1 = &line[0];                 /* Start at left edge   */
                if ((bp->b_flag&BFCHG) != 0)    /* "*" if changed       */
                        *cp1++ = '*';
                else
                        *cp1++ = ' ';
                *cp1++ = ' ';                   /* Gap.                 */
                nbytes = 0L;                    /* Count bytes in buf.  */
                lp = lforw(bp->b_linep);
                while (lp != bp->b_linep) {
                        nbytes += llength(lp)+1;
                        lp = lforw(lp);
                }
                ltoa(b, 6, nbytes);             /* 6 digit buffer size. */
                cp2 = &b[0];
                while ((c = *cp2++) != 0)
                        *cp1++ = c;
                *cp1++ = ' ';                   /* Gap.                 */
                cp2 = &bp->b_bname[0];          /* Buffer name          */
                while ((c = *cp2++) != 0)
                        *cp1++ = c;
                cp2 = &bp->b_fname[0];          /* File name            */
                if (*cp2 != 0) {
                        while (cp1 < &line[1+1+6+1+NBUFN+1])
                                *cp1++ = ' ';
                        while ((c = *cp2++) != 0) {
                                if (cp1 < &line[128-1])
                                        *cp1++ = c;
                        }
                }
                *cp1 = 0;                       /* Add to the buffer.   */
                if (addline(blistp, line) == FALSE)
                        return (FALSE);
                bp = bp->b_bufp;
        }
        return (TRUE);                          /* All done             */
}
 
ltoa(buf, width, num)
register char   buf[];
register int    width;
register long   num;
{
        buf[width] = 0;                         /* End of string.       */
        while (num >= 10L) {                    /* Conditional digits.  */
                buf[--width] = (int)(num%10L) + '0';
                num /= 10L;
        }
        buf[--width] = num + '0';               /* Always 1 digit.      */
        while (width != 0)                      /* Pad with blanks.     */
                buf[--width] = ' ';
}
 
/*
 * The argument "text" points to
 * a string. Append this line to the
 * buffer "bp" (one with BFTEMP set). Handcraft the EOL
 * on the end. Return TRUE if it worked and
 * FALSE if you ran out of room.
 */
addline(bp, text)
register BUFFER *bp;
register char *text;
{
        register LINE   *lp;
        register int    i;
        register int    ntext;
 
        ntext = strlen(text);
        if ((lp=lalloc(ntext)) == NULL)
                return (FALSE);
        for (i=0; i<ntext; ++i)
                lputc(lp, i, text[i]);
        bp->b_linep->l_bp->l_fp = lp;   /* Hook onto the end    */
        lp->l_bp = bp->b_linep->l_bp;
        bp->b_linep->l_bp = lp;
        lp->l_fp = bp->b_linep;
        if (bp->b_dotp == bp->b_linep)  /* If "." is at the end */
                bp->b_dotp = lp;                /* move it to new line  */
        return (TRUE);
}
 
/*
 * Look through the list of
 * buffers. Return TRUE if there
 * are any changed buffers. Buffers
 * that hold magic internal stuff are
 * not considered; who cares if the
 * list of buffer names is hacked.
 * Return FALSE if no buffers
 * have been changed.
 */
anycb()
{
        register BUFFER *bp;
 
        bp = bheadp;
        while (bp != NULL) {
                if ((bp->b_flag&BFTEMP)==0 && (bp->b_flag&BFCHG)!=0)
                        return (TRUE);
                bp = bp->b_bufp;
        }
        return (FALSE);
}
 
/*
 * Find a buffer, by name. Return a pointer
 * to the BUFFER structure associated with it. If
 * the named buffer is found, but is a TEMP buffer (like
 * the buffer list) conplain. If the buffer is not found
 * and the "cflag" is TRUE, create it. The "bflag" is
 * the settings for the flags in in buffer.
 */
BUFFER  *
bfind(bname, cflag, bflag)
register char   *bname;
register int cflag, bflag;
{
        register BUFFER *bp;
        register LINE   *lp;
        char    *index(),*ptr;
 
        bp = bheadp;
        while (bp != NULL)
                {
                if (strcmp(bname, bp->b_bname) == 0)
                        {
                        if ((bp->b_flag&BFTEMP) != 0)
                                if (cflag == FALSE)
                                        {
                                        mlwrite("Cannot select builtin buffer");
                                        return (FALSE);
                                        }
                        return (bp);
                        }
                bp = bp->b_bufp;
                }
        /* Buffer does not exist.  Prompt the user to see if a new
         * buffer should be created.
         */
        if (cflag == MAYBE) {
                if (mlyesno("Create new buffer") != TRUE)
                        return(FALSE);
                cflag = TRUE;   /* yes, create it */
                }
        if (cflag == TRUE) {
                if ((bp=(BUFFER *)malloc(sizeof(BUFFER))) == NULL)
                        return (NULL);
                if ((lp=lalloc(0)) == NULL) {
                        free((char *) bp);
                        return (NULL);
                }
                /* clear all mode flags */
                bp->b_bmode &=~BMWRAP;
                bp->b_bmode &=~BMNWRAP;
                bp->b_bmode &=~BMCMODE;
                if (ptr=index(bname,'.'))
                        {
                        if (strncmp(ptr, ".mss",4)==0)
                                bp->b_bmode |= BMWRAP;
                        else if (strcmp(ptr,".c")==0)
                                bp->b_bmode |= BMCMODE;
                        else if (strcmp(ptr,".h")==0)
                                bp->b_bmode |= BMCMODE;
                        else
                                bp->b_bmode |= glmode;
                        }
                else
                        bp->b_bmode |= glmode;
                bp->b_bufp  = bheadp;
                bheadp = bp;
                bp->b_dotp  = lp;
                bp->b_doto  = 0;
                bp->b_markp = lp;       /* All new buffers begin with mark */
                bp->b_marko = 0;
                bp->b_flag  = bflag;
                bp->b_nwnd  = 0;
                bp->b_linep = lp;
                strcpy(bp->b_fname, "");
                strcpy(bp->b_bname, bname);
                lp->l_fp = lp;
                lp->l_bp = lp;
        }
        return (bp);
}
 
/*
 * This routine blows away all of the text
 * in a buffer. If the buffer is marked as changed
 * then we ask if it is ok to blow it away; this is
 * to save the user the grief of losing text. The
 * window chain is nearly always wrong if this gets
 * called; the caller must arrange for the updates
 * that are required. Return TRUE if everything
 * looks good.
 */
bclear(bp)
register BUFFER *bp;
{
        register LINE   *lp;
        register int    s;
 
        if ((bp->b_flag&BFTEMP) == 0            /* Not scratch buffer.  */
        && (bp->b_flag&BFCHG) != 0              /* Something changed    */
        && (s=mlyesno("Discard changes")) != TRUE)
                return (s);
        bp->b_flag  &= ~BFCHG;                  /* Not changed          */
        while ((lp=lforw(bp->b_linep)) != bp->b_linep)
                lfree(lp);
        bp->b_dotp  = bp->b_linep;              /* Fix "."              */
        bp->b_doto  = 0;
        bp->b_markp = NULL;                     /* Invalidate "mark"    */
        bp->b_marko = 0;
        return (TRUE);
}
SHAR_EOF
cat << \SHAR_EOF > region.c
/*
 * The routines in this file
 * deal with the region, that magic space
 * between "." and mark. Some functions are
 * commands. Some functions are just for
 * internal use.
 */
#include        <stdio.h>
#include        "ed.h"
 
/*
 * Kill the region. Ask "getregion"
 * to figure out the bounds of the region.
 * Move "." to the start, and kill the characters.
 * Bound to "C-W".
 */
killregion(f, n)
register int f, n;
{
        register int    s;
        REGION          region;
 
        if ((s=getregion(&region)) != TRUE)
                return (s);
        if ((lastflag&CFKILL) == 0)             /* This is a kill type  */
                kdelete();                      /* command, so do magic */
        thisflag |= CFKILL;                     /* kill buffer stuff.   */
        curwp->w_dotp = region.r_linep;
        curwp->w_doto = region.r_offset;
        return (ldelete(region.r_size, TRUE));
}
 
/*
 * Copy all of the characters in the
 * region to the kill buffer. Don't move dot
 * at all. This is a bit like a kill region followed
 * by a yank. Bound to "M-W".
 */
copyregion(f, n)
register int f, n;
{
        register LINE   *linep;
        register int    loffs;
        register int    s;
        REGION          region;
 
        if ((s=getregion(&region)) != TRUE)
                return (s);
        if ((lastflag&CFKILL) == 0)             /* Kill type command.   */
                kdelete();
        thisflag |= CFKILL;
        linep = region.r_linep;                 /* Current line.        */
        loffs = region.r_offset;                /* Current offset.      */
        while (region.r_size--) {
                if (loffs == llength(linep)) {  /* End of line.         */
                        if ((s=kinsert('\n')) != TRUE)
                                return (s);
                        linep = lforw(linep);
                        loffs = 0;
                } else {                        /* Middle of line.      */
                        if ((s=kinsert(lgetc(linep, loffs))) != TRUE)
                                return (s);
                        ++loffs;
                }
        }
        return (TRUE);
}
 
/*
 * Lower case region. Zap all of the upper
 * case characters in the region to lower case. Use
 * the region code to set the limits. Scan the buffer,
 * doing the changes. Call "lchange" to ensure that
 * redisplay is done in all buffers. Bound to
 * "C-X C-L".
 */
lowerregion(f, n)
register int f, n;
{
        register LINE   *linep;
        register int    loffs;
        register int    c;
        register int    s;
        REGION          region;
 
        if ((s=getregion(&region)) != TRUE)
                return (s);
        lchange(WFHARD);
        linep = region.r_linep;
        loffs = region.r_offset;
        while (region.r_size--) {
                if (loffs == llength(linep)) {
                        linep = lforw(linep);
                        loffs = 0;
                } else {
                        c = lgetc(linep, loffs);
                        if (c>='A' && c<='Z')
                                lputc(linep, loffs, c+'a'-'A');
                        ++loffs;
                }
        }
        return (TRUE);
}
 
/*
 * Upper case region. Zap all of the lower
 * case characters in the region to upper case. Use
 * the region code to set the limits. Scan the buffer,
 * doing the changes. Call "lchange" to ensure that
 * redisplay is done in all buffers. Bound to
 * "C-X C-L".
 */
upperregion(f, n)
register int f, n;
{
        register LINE   *linep;
        register int    loffs;
        register int    c;
        register int    s;
        REGION          region;
 
        if ((s=getregion(&region)) != TRUE)
                return (s);
        lchange(WFHARD);
        linep = region.r_linep;
        loffs = region.r_offset;
        while (region.r_size--) {
                if (loffs == llength(linep)) {
                        linep = lforw(linep);
                        loffs = 0;
                } else {
                        c = lgetc(linep, loffs);
                        if (c>='a' && c<='z')
                                lputc(linep, loffs, c-'a'+'A');
                        ++loffs;
                }
        }
        return (TRUE);
}
 
/*
 * This routine figures out the
 * bounds of the region in the current window, and
 * fills in the fields of the "REGION" structure pointed
 * to by "rp". Because the dot and mark are usually very
 * close together, we scan outward from dot looking for
 * mark. This should save time. Return a standard code.
 * Callers of this routine should be prepared to get
 * an "ABORT" status; we might make this have the
 * conform thing later.
 */
getregion(rp)
register REGION *rp;
{
        register LINE   *flp;
        register LINE   *blp;
        register int    fsize;
        register int    bsize;
 
        if (curwp->w_markp == NULL) {
                mlwrite("No mark set in this window");
                return (FALSE);
        }
        if (curwp->w_dotp == curwp->w_markp) {
                rp->r_linep = curwp->w_dotp;
                if (curwp->w_doto < curwp->w_marko) {
                        rp->r_offset = curwp->w_doto;
                        rp->r_size = curwp->w_marko-curwp->w_doto;
                } else {
                        rp->r_offset = curwp->w_marko;
                        rp->r_size = curwp->w_doto-curwp->w_marko;
                }
                return (TRUE);
        }
        blp = curwp->w_dotp;
        bsize = curwp->w_doto;
        flp = curwp->w_dotp;
        fsize = llength(flp)-curwp->w_doto+1;
        while (flp!=curbp->b_linep || lback(blp)!=curbp->b_linep) {
                if (flp != curbp->b_linep) {
                        flp = lforw(flp);
                        if (flp == curwp->w_markp) {
                                rp->r_linep = curwp->w_dotp;
                                rp->r_offset = curwp->w_doto;
                                rp->r_size = fsize+curwp->w_marko;
                                return (TRUE);
                        }
                        fsize += llength(flp)+1;
                }
                if (lback(blp) != curbp->b_linep) {
                        blp = lback(blp);
                        bsize += llength(blp)+1;
                        if (blp == curwp->w_markp) {
                                rp->r_linep = blp;
                                rp->r_offset = curwp->w_marko;
                                rp->r_size = bsize - curwp->w_marko;
                                return (TRUE);
                        }
                }
        }
        mlwrite("Bug: lost mark");
        return (FALSE);
}
SHAR_EOF
#       End of shell archive
exit 0
%NONAME-W-NOMSG, Message number 00000000

RDROYA01@ULKYVX.BITNET.UUCP (02/05/87)

#       This is a shell archive.
#       Remove everything above and including the cut line.
#       Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar: Shell Archiver
#       Run the following text with /bin/sh to create:
#       file.c
#       fileio.c
#       print.c
# This archive created: Tue Feb  3 17:51:53 1987
cat << \SHAR_EOF > file.c
/*
 * The routines in this file
 * handle the reading and writing of
 * disk files. All of details about the
 * reading and writing of the disk are
 * in "fileio.c".
 */
#include        <stdio.h>
#include        "ed.h"
 
unsigned char   insflag = FALSE;        /* Flag for C mode in newline() */
 
/*
 * Read a file into the current
 * buffer. This is really easy; all you do it
 * find the name of the file, and call the standard
 * "read a file into the current buffer" code.
 * Bound to "C-X C-R".
 */
fileread(f, n)
register int f, n;
{
        register int    s;
        char            fname[NFILEN];
 
        if ((s=mlreply("Read file: ", fname, NFILEN)) != TRUE)
                return (s);
        return (readin(fname));
}
 
/*
 * Select a file for editing.
 * Look around to see if you can find the
 * file in another buffer; if you can find it
 * just switch to the buffer. If you cannot find
 * the file, create a new buffer, read in the
 * text, and switch to the new buffer.
 * Bound to C-X C-V.
 */
filevisit(f, n)
register int f, n;
{
        register BUFFER *bp;
        register WINDOW *wp;
        register LINE   *lp;
        register int    i;
        register int    s;
        char            bname[NBUFN];
        char            fname[NFILEN];
 
        if ((s=mlreply("Visit file: ", fname, NFILEN)) != TRUE)
                return (s);
        strcpy(lastbuf, curbp->b_bname);
        for (bp=bheadp; bp!=NULL; bp=bp->b_bufp) {
                if ((bp->b_flag&BFTEMP)==0 && strcmp(bp->b_fname, fname)==0) {
                        if (--curbp->b_nwnd == 0) {
                                curbp->b_dotp  = curwp->w_dotp;
                                curbp->b_doto  = curwp->w_doto;
                                curbp->b_markp = curwp->w_markp;
                                curbp->b_marko = curwp->w_marko;
                        }
                        curbp = bp;
                        curwp->w_bufp  = bp;
                        if (bp->b_nwnd++ == 0) {
                                curwp->w_dotp  = bp->b_dotp;
                                curwp->w_doto  = bp->b_doto;
                                curwp->w_markp = bp->b_markp;
                                curwp->w_marko = bp->b_marko;
                        } else {
                                wp = wheadp;
                                while (wp != NULL) {
                                        if (wp!=curwp && wp->w_bufp==bp) {
                                                curwp->w_dotp  = wp->w_dotp;
                                                curwp->w_doto  = wp->w_doto;
                                                curwp->w_markp = wp->w_markp;
                                                curwp->w_marko = wp->w_marko;
                                                break;
                                        }
                                        wp = wp->w_wndp;
                                }
                        }
                        lp = curwp->w_dotp;
                        i = curwp->w_ntrows/2;
                        while (i-- && lback(lp)!=curbp->b_linep)
                                lp = lback(lp);
                        curwp->w_linep = lp;
                        curwp->w_flag |= WFMODE|WFHARD;
                        mlwrite("[Old buffer]");
                        return (TRUE);
                }
        }
        makename(bname, fname);                 /* New buffer name.     */
        while ((bp=bfind(bname, FALSE, 0)) != NULL) {
                s = mlreply("Buffer name: ", bname, NBUFN);
                if (s == ABORT)                 /* ^G to just quit      */
                        return (s);
                if (s == FALSE) {               /* CR to clobber it     */
                        makename(bname, fname);
                        break;
                }
        }
        if (bp==NULL && (bp=bfind(bname, TRUE, 0))==NULL) {
                mlwrite("Cannot create buffer");
                return (FALSE);
        }
        if (--curbp->b_nwnd == 0) {             /* Undisplay.           */
                curbp->b_dotp = curwp->w_dotp;
                curbp->b_doto = curwp->w_doto;
                curbp->b_markp = curwp->w_markp;
                curbp->b_marko = curwp->w_marko;
        }
        curbp = bp;                             /* Switch to it.        */
        curwp->w_bufp = bp;
        curbp->b_nwnd++;
        return (readin(fname));                 /* Read it in.          */
}
 
/*
 * Read file "fname" into the current
 * buffer, blowing away any text found there. Called
 * by both the read and visit commands. Return the final
 * status of the read. Also called by the mainline,
 * to read in a file specified on the command line as
 * an argument.
 */
readin(fname)
char    fname[];
{
        register LINE   *lp1;
        register LINE   *lp2;
        register int    i;
        register WINDOW *wp;
        register BUFFER *bp;
        register int    s;
        register int    nbytes;
        register int    nline;
        char            line[NLINE];
 
        bp = curbp;                             /* Cheap.               */
        if ((s=bclear(bp)) != TRUE)             /* Might be old.        */
                return (s);
        bp->b_flag &= ~(BFTEMP|BFCHG);
        if (fname[0] == '~')                    /* an alias request */
                if ((s=parsefn(fname))==FALSE)
                        s = FIOFNF;
        strcpy(bp->b_fname, fname);
        if ((s=ffropen(fname)) == FIOERR)       /* Hard file open.      */
                goto out;
        if (s == FIOFNF) {                      /* File not found.      */
                mlwrite("[New file]");
                goto out;
        }
        mlwrite("[Reading %s]",fname);
        nline = 0;
        while ((s=ffgetline(line, NLINE)) == FIOSUC) {
                nbytes = strlen(line);
                if ((lp1=lalloc(nbytes)) == NULL) {
                        s = FIOERR;             /* Keep message on the  */
                        break;                  /* display.             */
                }
                lp2 = lback(curbp->b_linep);
                lp2->l_fp = lp1;
                lp1->l_fp = curbp->b_linep;
                lp1->l_bp = lp2;
                curbp->b_linep->l_bp = lp1;
                for (i=0; i<nbytes; ++i)
                        lputc(lp1, i, line[i]);
                ++nline;
        }
        ffclose();                              /* Ignore errors.       */
        if (s == FIOEOF) {                      /* Don't zap message!   */
                if (nline == 1)
                        mlwrite("[Read %d line]",nline);
                else
                        mlwrite("[Read %d lines]", nline);
        }
out:
        for (wp=wheadp; wp!=NULL; wp=wp->w_wndp) {
                if (wp->w_bufp == curbp) {
                        wp->w_linep = lforw(curbp->b_linep);
                        wp->w_dotp  = lforw(curbp->b_linep);
                        wp->w_doto  = 0;
                        wp->w_markp = curwp->w_dotp;
                        wp->w_marko = 0;
                        wp->w_flag |= WFMODE|WFHARD;
                }
        }
        if (s == FIOERR)                        /* False if error.      */
                return (FALSE);
        return (TRUE);
}
 
/*
 * Take a file name, and from it
 * fabricate a buffer name. This routine knows
 * about the syntax of file names on the target system.
 * I suppose that this information could be put in
 * a better place than a line of code.
 */
makename(bname, fname)
char    bname[];
char    fname[];
{
        register char   *cp1;
        register char   *cp2;
 
        if (fname[0] == '~')
                parsefn(fname);
        cp1 = &fname[0];
        while (*cp1 != 0)
                ++cp1;
#if     VMS
        while (cp1!=&fname[0] && cp1[-1]!=':' && cp1[-1]!=']')
                --cp1;
#endif
#if     CPM
        while (cp1!=&fname[0] && cp1[-1]!=':')
                --cp1;
#endif
#if     ST
        while (cp1!=&fname[0] && cp1[-1]!=':' && cp1[-1]!='\\')
                --cp1;
#endif
#if     V7
        while (cp1!=&fname[0] && cp1[-1]!='/')
                --cp1;
#endif
        cp2 = &bname[0];
        while (cp2!=&bname[NBUFN-1] && *cp1!=0 && *cp1!=';')
                *cp2++ = *cp1++;
        *cp2 = 0;
}
 
/*
 * Ask for a file name, and write the
 * contents of the current buffer to that file.
 * Update the remembered file name and clear the
 * buffer changed flag. This handling of file names
 * is different from the earlier versions, and
 * is more compatable with Gosling EMACS than
 * with ITS EMACS. Bound to "C-X C-W".
 */
filewrite(f, n)
register int f, n;
{
        register int    s;
        char            fname[NFILEN];
 
        if ((s=mlreply("Write file: ", fname, NFILEN)) != TRUE)
                return (s);
        if ((s=writeout(fname)) == TRUE) {
                strcpy(curbp->b_fname, fname);
                curbp->b_flag &= ~BFCHG;
                upmode();                       /* Update mode lines.   */
        }
        return (s);
}
 
/*
 * Save the contents of the current
 * buffer in its associatd file. No nothing
 * if nothing has changed (this may be a bug, not a
 * feature). Error if there is no remembered file
 * name for the buffer. Bound to "C-X C-S". May
 * get called by "C-Z".
 */
filesave(f, n)
register int f, n;
{
        register int    s;
 
        if ((curbp->b_flag&BFCHG) == 0)         /* Return, no changes.  */
                return (TRUE);
        if (curbp->b_fname[0] == 0) {           /* Must have a name.    */
                filename(f,n);                  /* prompt user for a name */
                if (curbp->b_fname[0] == 0)     /* still no name        */
                        return (FALSE);
        }
        if ((s=writeout(curbp->b_fname)) == TRUE) {
                curbp->b_flag &= ~BFCHG;
                upmode();                       /* Update mode lines.   */
 
        }
        return (s);
}
 
/*
 * This function performs the details of file
 * writing. Uses the file management routines in the
 * "fileio.c" package. The number of lines written is
 * displayed. Sadly, it looks inside a LINE; provide
 * a macro for this. Most of the grief is error
 * checking of some sort.
 */
writeout(fn)
char    fn[];
{
        register int    s;
        register LINE   *lp;
        register int    nline;
 
        if (fn[0] == '~')                       /* an alias request */
                if ((s=parsefn(fn))==FALSE)
                        return(FALSE);
        if ((s=ffwopen(fn)) != FIOSUC)          /* Open writes message. */
                return (FALSE);
        mlwrite("[Writing: %s]", fn);
        lp = lforw(curbp->b_linep);             /* First line.          */
        nline = 0;                              /* Number of lines.     */
        while (lp != curbp->b_linep) {
                if ((s=ffputline(&lp->l_text[0], llength(lp))) != FIOSUC)
                        break;
                ++nline;
                lp = lforw(lp);
        }
        if (s == FIOSUC) {                      /* No write error.      */
                s = ffclose();
                if (s == FIOSUC) {              /* No close error.      */
                        if (nline == 1)
                                mlwrite("[Wrote %d line]",nline);
                        else
                                mlwrite("[Wrote %d lines]", nline);
                }
        } else                                  /* Ignore close error   */
                ffclose();                      /* if a write error.    */
        if (s != FIOSUC)                        /* Some sort of error.  */
                return (FALSE);
        return (TRUE);
}
 
/*
 * The command allows the user
 * to modify the file name associated with
 * the current buffer. It is like the "f" command
 * in UNIX "ed". The operation is simple; just zap
 * the name in the BUFFER structure, and mark the windows
 * as needing an update. You can type a blank line at the
 * prompt if you wish.
 */
filename(f, n)
register int f, n;
{
        register int    s;
        char            fname[NFILEN];
 
        if ((s=mlreply("Name: ", fname, NFILEN)) == ABORT)
                return (s);
        if (fname[0] == '~')                    /* an alias request */
                s=parsefn(fname);
        if (s == FALSE)
                strcpy(curbp->b_fname, "");
        else
                strcpy(curbp->b_fname, fname);
        upmode();
        return (TRUE);
}
 
/* WRITEREG eXtended command  Write defined region to file.  Prompt
 * for filename.  Error if any sub-function returns failure. Bound to
 * CTLX-R.
 */
writereg(f, n)
register int f, n;
{
        register int    s;
        char            buf[NLINE];
        char            fname[NFILEN];
 
        n = 0;
        f = 0;
 
        if ((s=mlreply("Write region: ", fname, NFILEN)) == ABORT || s == FALSE)
                return(s);
        if (copyregion(NULL, NULL) != TRUE)
                return (FALSE);
        if ((s=ffwopen(fname)) != FIOSUC)
                return (s);
        while ((s=kremove(n++)) != EOF)
                {
                if (s != '\n' && f < NLINE)
                        buf[f++] = s;
                else
                        {
                        ffputline(buf, f);
                        f=0;
                        }
                }
        if ((s=ffclose()) == FIOERR)
                return(s);
        else
                mlwrite("[Region written to %s]",fname);
        return(TRUE);
}
 
/* FILEINSERT  eXtended command insert existing file at point.  All of the
 * necessary updating is done by the usual insert commands.  Bound to ^X-I.
 */
 
fileinsert(f, n)
register int f, n;
{
        char fname[NFILEN];
        char line[NLINE];
        register int c;
        register int nline;
        register int omarko;
        register LINE *omarkp;
 
        nline = 0;
 
        if ((f=mlreply("Insert file: ", fname, NFILEN)) == ABORT || f == FALSE)
                return(f);
        if (fname[0] == '~')
                if ((f=parsefn(fname))==FALSE)
                        return(f);
        if (ffropen(fname) == FIOFNF)
                {
                mlwrite("File: %s not found", fname);
                return (FALSE);
                }
        insflag = TRUE;
        /* save current place in buffer */
        if ((omarkp=lback(curwp->w_dotp)) == curbp->b_linep)
                omarkp = NULL;
        omarko = curwp->w_doto;
        mlwrite("[Mark set]");
        /* the standard routines take care of update */
        while ((f=ffgetline(line, NLINE)) == FIOSUC)
                {
                n = 0;
                ++nline;
                while ((c=line[n++]) !=NULL)
                        if (linsert(1, c) == FALSE)
                                {
                                insflag = FALSE;
                                return (FALSE);
                                }
                if (newline(FALSE, 1) == FALSE)
                        {
                        insflag = FALSE;
                        return (FALSE);
                        }
                }
        ffclose();
        if (f == FIOEOF)
                {
                if (nline == 1)
                        mlwrite("[Inserted %d line]",nline);
                else
                        mlwrite("[Inserted %d lines]", nline);
                }
        insflag = FALSE;
        /* The following code is needed if we want to insert a file
         * the way GNU does (returning to the point of insertion
         * when done).  The problem is with files inserted when the
         * current point is at the end or beginning of the buffer.
         */
        if (omarkp == NULL)
                /* inserted at beginning */
                {
                curwp->w_markp = curwp->w_dotp;
                curwp->w_marko = curwp->w_doto;
                gotobob(FALSE,TRUE);
                curwp->w_doto = omarko;
                }
        else
                /* inserted at end or in the midst */
                {
                curwp->w_markp = omarkp;
                curwp->w_marko = omarko;
                swapmark(FALSE,TRUE);
                forwline(NULL,1);
                }
        if (f == FIOERR)
                return (FALSE);
        return (TRUE);
}
 
parsefn(fname)
char fname[];
{
        register char   *ptr;
        char            *index(), *alias();
        char            template[NFILEN];
 
        if ((ptr=index(fname,'\\'))!=(char *)NULL)
                {
                *ptr = '\0';    /* ptr++==file; fname==alias */
                if (alias(&template[0],&fname[1])==(char *)NULL)
                        return(FALSE);
                strcat(template,++ptr);
                strcpy(fname,template);
                }
        else
                return(FALSE);
        return(TRUE);
}
SHAR_EOF
cat << \SHAR_EOF > fileio.c
/*
 * The routines in this file
 * read and write ASCII files from the
 * disk. All of the knowledge about files
 * are here. A better message writing
 * scheme should be used.
 */
#include        <stdio.h>
#include        "ed.h"
 
FILE    *ffp;                   /* File pointer, all functions. */
 
/*
 * Open a file for reading.
 */
ffropen(fn)
char    *fn;
{
        if ((ffp=fopen(fn, "r")) == NULL)
                return (FIOFNF);
        return (FIOSUC);
}
 
/*
 * Open a file for writing.
 * Return TRUE if all is well, and
 * FALSE on error (cannot create).
 */
ffwopen(fn)
char    *fn;
{
#if     VMS
        register int    fd;
 
        if ((fd=creat(fn, 0666, "rfm=var", "rat=cr")) < 0
        || (ffp=fdopen(fd, "w")) == NULL) {
#else
        if ((ffp=fopen(fn, "w")) == NULL) {
#endif
                mlwrite("Cannot open file for writing");
                return (FIOERR);
        }
        return (FIOSUC);
}
 
/*
 * Close a file.
 * Should look at the status in all systems.
 */
ffclose()
{
#if     ALCYON
        if (fclose(ffp) != FALSE) {
                mlwrite("Error closing file");
                return(FIOERR);
        }
        return(FIOSUC);
#endif
        fclose(ffp);
        return (FIOSUC);
}
 
/*
 * Write a line to the already
 * opened file. The "buf" points to the
 * buffer, and the "nbuf" is its length, less
 * the free newline. Return the status.
 * Check only at the newline.
 */
ffputline(buf, nbuf)
register char   buf[];
register int nbuf;
{
        register int    i;
 
        for (i=0; i<nbuf; ++i)
                putc(buf[i]&0xFF, ffp);
        putc('\n', ffp);
        if (ferror(ffp) != FALSE) {
                mlwrite("Write I/O error");
                return (FIOERR);
        }
        return (FIOSUC);
}
 
/*
 * Read a line from a file,
 * and store the bytes in the supplied
 * buffer. The "nbuf" is the length of the
 * buffer. Complain about long lines and lines
 * at the end of the file that don't have a
 * newline present. Check for I/O errors
 * too. Return status.
 */
ffgetline(buf, nbuf)
register char   buf[];
register int nbuf;
{
        register int    c;
        register int    i;
 
        i = 0;
        while ((c=getc(ffp))!=EOF && c!='\n') {
                if (i >= nbuf-1) {
                        mlwrite("File has long line");
                        return (FIOERR);
                }
                buf[i++] = c;
        }
        if (c == EOF) {
                if (ferror(ffp) != FALSE) {
                        mlwrite("File read error");
                        return (FIOERR);
                }
                if (i != 0) {
                        mlwrite("File has funny line at EOF");
                        return (FIOERR);
                }
                return (FIOEOF);
        }
        buf[i] = 0;
        return (FIOSUC);
}
 
/* Function to handle printing file pageheaders.  Needs access to ffp,
 * so it's here rather than in print.c where it probably belongs.  Two
 * lines of top margin and three lines between header and text are wired in.
 */
 
prhdg(f, pagenum, rm)
int f;
int pagenum;
int rm;
{
        fflush(ffp);
        if (f == TRUE)
                fprintf(ffp, "\f\n\n%*s\n%*s\n%*d\n\n\n",
                        rm, &prnhdr[0], rm, &prndate[0],
                        rm, pagenum);
        else
                fprintf(ffp, "\n\n%*s\n%*s\n\n\n\n",
                        rm, &prnhdr[0], rm, &prndate[0]);
 
        return(TRUE);
}
 
SHAR_EOF
cat << \SHAR_EOF > print.c
/* Output is only to the printer.  We need to test for printer
 * connect before doing anything.  If no connect, return FALSE.
 * These functions should allow line height and header selection
 * via a menu.
 */
 
#include <stdio.h>
#include <osbind.h>
#include "ed.h"
#include "printer.h"
 
/* PRINT eXtended command  Handles the print buffer function.  Gets user
 * parameters, calls printer init, and reports errors.  Bound to ^X <TAB>.
 */
 
static  char prnfin[NFILEN];
 
print(f, n)
register int f;
register int n;
{
 
        register int s,i;
        register int lm, rm, cw;
        char buf[MYBUF];
        lm = 6;         /* for now use 6 char left margins */
        cw = 12;        /* for now use 12 char pitch */
        i  = 0;
 
        if (n > 1)
                pagelen = n;    /* pagelen is set by routine in page.c */
 
        /* Eventually change these mode line questions into a window */
        /* on the screen using the cursor keys to select options */
 
        if (isnprint || (PRNRDY == NULL))
                {
                mlwrite("Device LST: is being used");
                return(FALSE);
                }
        if((s = readpattern("What is your name? ", &prnhdr[0])) != TRUE)
                return(s);
        if((s = readpattern("What is today's date? ", &prndate[0])) != TRUE)
                return(s);
        if((s = mlyesno("Do you want double spacing? ")) == FALSE)
                spacing = 1;
        else
                spacing = 2;
 
        strcpy(prnfin,"~temp\\prn.fin");
        parsefn(prnfin);
        mlwrite("[Writing buffer file: %s page length = %d]", prnfin,pagelen);
 
        if(! tabsize)
                temptab = 8;
        else
                temptab = tabsize;
 
        rm = 80;
 
        printout(prnfin, rm);   /* Temp file for buffer */
        if((in = open(prnfin, 0)) == EOF)
                {
                mlwrite("OPEN failure on buffer file");
                return(FALSE);
                }
        setprn(cw, spacing, temptab, lm, rm); /* Initialize printer */
        isnprint = TRUE;                        /* We are printing */
        /* Initially fill printer buffer */
        if ((s = read(in, buf, MYBUF)) > NULL)
                while (s--)
                        {
                        if (buf[i] == '\n')
                                Cprnout('\r');
                        if (!(int)gemdos(0x5,buf[i++]))
                                {
                                mlwrite("Write error on LST:");
                                return(FIOERR);
                                }
                        }
                return(TRUE);
}
 
/* SETPRN Local function to set printer parameters.  Mostly uses macros from
 * printer.h
 */
 
static int
setprn(pitch, lheight, tabstop, lmarg, rmarg)
register int pitch, lheight, tabstop, lmarg, rmarg;
{
        register int i;
        pitch -= 2;
        lheight = (lheight * 5) + 4;    /* this sort of gives single and */
                                        /* double spacing @ 6 per inch */
        RESET;
        for(i=0;i<lmarg;i++)
                PRINT(SPACE);
        SET_LMARG;
        while(rmarg)
                {
                for(i=0;i<tabstop;i++,--rmarg)
                        PRINT(SPACE);
                SET_TAB;
                }
        PRINT(CR);
        SET_PITCH(pitch);
        SET_HEIGHT(lheight);
        return(TRUE);
}
 
/*
 * This function performs the details of file buffer
 * printing. Uses the file management routines in the
 * "fileio.c" package.
 */
 
static int
printout(fn, marg)
register char   *fn;
register int marg;
{
        register int    s;
        register LINE   *lp;
        register int    nline;
        register short f;
 
        f = FALSE;                              /* first time through */
 
        if ((s=ffwopen(fn)) != FIOSUC)          /* Open writes message. */
                return (FALSE);
        lp = lforw(curbp->b_linep);             /* First line.          */
        nline = 0;                              /* Number of lines.     */
        linecnt = 0;
        pagecnt = 1;
        while (lp != curbp->b_linep)
                {
                if (linecnt == 0)
                        {
                        if((s=prhdg(f, pagecnt++, marg)) != TRUE)
                                break;
                        f = TRUE;               /* Each new page gets \f */
                        linecnt += 9;
                        }
                if ((s=ffputline(&lp->l_text[0], llength(lp))) != FIOSUC)
                        break;
                ++nline;
                lp = lforw(lp);
                if(linecnt > pagelen - 3)
                        {
                        linecnt = 0;
                        continue;
                        }
                ++linecnt;
        }
        if (s == FIOSUC)                        /* No write error.      */
                s = ffclose();
        if (s != FIOSUC)                        /* Some sort of error.  */
                return (FALSE);
        return (TRUE);
}
 
/* This function reads the temp file written above and writes the file to
 * the list device.  It then deletes the temp file.
 */
 
prnbuf()
{
        register int stat;
        register int i;
        char buf[BUFSIZE];
        i = 0;
 
        if ((stat = read(in, buf, BUFSIZE)) > NULL)
                {
                while(stat--)
                        {
                        if (buf[i] == '\n')
                                Cprnout('\r');
                        if (!(int)gemdos(0x5,buf[i++]))
                                {
                                mlwrite("Write error on LST:");
                                return(FIOERR);
                                }
                        }
                return(TRUE);
                }
        isnprint = FALSE;
        close(in);
        unlink(prnfin);
        NEWPAGE;
        mlwrite("[Printing completed]");
        update();
        return(TRUE);
}
SHAR_EOF
#       End of shell archive
exit 0
%NONAME-W-NOMSG, Message number 00000000

RDROYA01@ULKYVX.BITNET.UUCP (02/05/87)

#       This is a shell archive.
#       Remove everything above and including the cut line.
#       Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar: Shell Archiver
#       Run the following text with /bin/sh to create:
#       ed.h
#       errno.h
#       kermit.h
#       keycode.h
#       printer.h
#       shell.h
# This archive created: Tue Feb  3 18:00:56 1987
cat << \SHAR_EOF > ed.h
/*
 * This file is the general header file for
 * all parts of the MicroEMACS display editor. It contains
 * definitions used by everyone, and it contains the stuff
 * you have to edit to create a version of the editor for
 * a specific operating system and terminal.
 */
#define V7      0                       /* V7 UN*X or Coherent          */
#define VMS     0                       /* VAX/VMS                      */
#define CPM     0                       /* CP/M-86                      */
#define ST      1
#define ALCYON  1                       /* Alcyon C compiler            */
#define MSDOS   0                       /* MS-DOS                       */
 
#define ANSI    0
#define ADM3    0                       /* [Kaypro] Lear Siegler        */
#define VT52    0                       /* VT52 terminal (Zenith).      */
#define VT100   0                       /* Handle VT100 style keypad.   */
#define LK201   0                       /* Handle LK201 style keypad.   */
#define RAINBOW 0                       /* Use Rainbow fast video.      */
#define TERMCAP 0                       /* Use TERMCAP                  */
 
#define CVMVAS  1                       /* C-V, M-V arg. in screens.    */
 
#define NFILEN  80                      /* # of bytes, file name        */
#define NBUFN   16                      /* # of bytes, buffer name      */
#define NLINE   256                     /* # of bytes, line             */
#define NKBDM   256                     /* # of strokes, keyboard macro */
#define NPAT    80                      /* # of bytes, pattern          */
#define HUGE    1000                    /* Huge number                  */
 
#define AGRAVE  0x60                    /* M- prefix,   Grave (LK201)   */
#define METACH  0x1B                    /* M- prefix,   Control-[, ESC  */
#define CTMECH  0x1C                    /* C-M- prefix, Control-\       */
#define EXITCH  0x1D                    /* Exit level,  Control-]       */
#define CTRLCH  0x1E                    /* C- prefix,   Control-^       */
#define HELPCH  0x1F                    /* Help key,    Control-_       */
 
#define CTRL    0x0100                  /* Control flag, or'ed in       */
#define META    0x0200                  /* Meta flag, or'ed in          */
#define CTLX    0x0400                  /* ^X flag, or'ed in            */
#define SPEC    0x0800                  /* Special scancode keys        */
 
#define MAYBE   -1                      /* Conditional based on query   */
#define FALSE   0                       /* False, no, bad, etc.         */
#define TRUE    1                       /* True, yes, good, etc.        */
#define ABORT   2                       /* Death, ^G, abort, etc.       */
 
#define FIOSUC  0                       /* File I/O, success.           */
#define FIOFNF  1                       /* File I/O, file not found.    */
#define FIOEOF  2                       /* File I/O, end of file.       */
#define FIOERR  3                       /* File I/O, error.             */
 
#define CFCPCN  0x0001                  /* Last command was C-P, C-N    */
#define CFKILL  0x0002                  /* Last command was a kill      */
 
#define PRNRDY  gemdos(0x11)            /* LST: status for print buffer */
/*
 * There is a window structure allocated for
 * every active display window. The windows are kept in a
 * big list, in top to bottom screen order, with the listhead at
 * "wheadp". Each window contains its own values of dot and mark.
 * The flag field contains some bits that are set by commands
 * to guide redisplay; although this is a bit of a compromise in
 * terms of decoupling, the full blown redisplay is just too
 * expensive to run for every input character.
 */
typedef struct  WINDOW {
        struct  WINDOW *w_wndp;         /* Next window                  */
        struct  BUFFER *w_bufp;         /* Buffer displayed in window   */
        struct  LINE *w_linep;          /* Top line in the window       */
        struct  LINE *w_dotp;           /* Line containing "."          */
        short   w_doto;                 /* Byte offset for "."          */
        struct  LINE *w_markp;          /* Line containing "mark"       */
        short   w_marko;                /* Byte offset for "mark"       */
        char    w_toprow;               /* Origin 0 top row of window   */
        char    w_ntrows;               /* # of rows of text in window  */
        char    w_force;                /* If NZ, forcing row.          */
        char    w_flag;                 /* Flags.                       */
}       WINDOW;
 
#define WFFORCE 0x01                    /* Window needs forced reframe  */
#define WFMOVE  0x02                    /* Movement from line to line   */
#define WFEDIT  0x04                    /* Editing within a line        */
#define WFHARD  0x08                    /* Better to a full display     */
#define WFMODE  0x10                    /* Update mode line.            */
 
/*
 * Text is kept in buffers. A buffer header, described
 * below, exists for every buffer in the system. The buffers are
 * kept in a big list, so that commands that search for a buffer by
 * name can find the buffer header. There is a safe store for the
 * dot and mark in the header, but this is only valid if the buffer
 * is not being displayed (that is, if "b_nwnd" is 0). The text for
 * the buffer is kept in a circularly linked list of lines, with
 * a pointer to the header line in "b_linep".
 */
typedef struct  BUFFER {
        struct  BUFFER *b_bufp;         /* Link to next BUFFER          */
        struct  LINE *b_dotp;           /* Link to "." LINE structure   */
        short   b_doto;                 /* Offset of "." in above LINE  */
        struct  LINE *b_markp;          /* The same as the above two,   */
        short   b_marko;                /* but for the "mark"           */
        struct  LINE *b_linep;          /* Link to the header LINE      */
        char    b_nwnd;                 /* Count of windows on buffer   */
        char    b_flag;                 /* Flags                        */
        char    b_bmode;                /* This buffer's current modes  */
        char    b_fname[NFILEN];        /* File name                    */
        char    b_bname[NBUFN];         /* Buffer name                  */
}       BUFFER;
 
#define BFTEMP  0x01                    /* Internal temporary buffer    */
#define BFCHG   0x02                    /* Changed since last write     */
#define BMNWRAP 0x0001                  /* Buffer is in No-Wrap mode    */
#define BMWRAP  0x0002                  /* Buffer is in Wrap mode       */
#define BMCMODE 0x0004                  /* Buffer is in C-Mode          */
#define NUMMODES 3
 
/*
 * The starting position of a
 * region, and the size of the region in
 * characters, is kept in a region structure.
 * Used by the region commands.
 */
typedef struct  {
        struct  LINE *r_linep;          /* Origin LINE address.         */
        short   r_offset;               /* Origin LINE offset.          */
        short   r_size;                 /* Length in characters.        */
}       REGION;
 
/*
 * All text is kept in circularly linked
 * lists of "LINE" structures. These begin at the
 * header line (which is the blank line beyond the
 * end of the buffer). This line is pointed to by
 * the "BUFFER". Each line contains a the number of
 * bytes in the line (the "used" size), the size
 * of the text array, and the text. The end of line
 * is not stored as a byte; it's implied. Future
 * additions will include update hints, and a
 * list of marks into the line.
 */
typedef struct  LINE {
        struct  LINE *l_fp;             /* Link to the next line        */
        struct  LINE *l_bp;             /* Link to the previous line    */
        short   l_size;                 /* Allocated size               */
        short   l_used;                 /* Used size                    */
        char    l_text[];               /* A bunch of characters.       */
}       LINE;
 
#define lforw(lp)       ((lp)->l_fp)
#define lback(lp)       ((lp)->l_bp)
#define lgetc(lp, n)    ((lp)->l_text[(n)]&0xFF)
#define lputc(lp, n, c) ((lp)->l_text[(n)]=(c))
#define llength(lp)     ((lp)->l_used)
 
/*
 * The editor communicates with the display
 * using a high level interface. A "TERM" structure
 * holds useful variables, and indirect pointers to
 * routines that do useful operations. The low level get
 * and put routines are here too. This lets a terminal,
 * in addition to having non standard commands, have
 * funny get and put character code too. The calls
 * might get changed to "termp->t_field" style in
 * the future, to make it possible to run more than
 * one terminal type.
 */
typedef struct  {
        short   t_nrow;                 /* Number of rows.              */
        short   t_ncol;                 /* Number of columns.           */
        short   t_margin;               /* Min marg for extended lines  */
        short   t_scrsiz;               /* Size of scroll reg. " "      */
        int     (*t_open)();            /* Open terminal at the start.  */
        int     (*t_close)();           /* Close terminal at end.       */
        int     (*t_getchar)();         /* Get character from keyboard. */
        int     (*t_putchar)();         /* Put character to display.    */
        int     (*t_flush)();           /* Flush output buffers.        */
        int     (*t_move)();            /* Move the cursor, origin 0.   */
        int     (*t_eeol)();            /* Erase to end of line.        */
        int     (*t_eeop)();            /* Erase to end of page.        */
        int     (*t_beep)();            /* Beep.                        */
        int     (*t_rev)();             /* Set reverse video state      */
}       TERM;
 
extern  int     fillcol;                /* Fill column                  */
extern  int     indcol;                 /* Indent column                */
extern  int     pagelen;                /* Page size for print/paginate */
extern  int     currow;                 /* Cursor row                   */
extern  int     curcol;                 /* Cursor column                */
extern  int     thisflag;               /* Flags, this command          */
extern  int     lastflag;               /* Flags, last command          */
extern  int     curgoal;                /* Goal for C-P, C-N            */
extern  int     mpresf;                 /* Stuff in message line        */
extern  int     sgarbf;                 /* State of screen unknown      */
extern  int     glmode;                 /* Global mode value            */
extern  WINDOW  *curwp;                 /* Current window               */
extern  BUFFER  *curbp;                 /* Current buffer               */
extern  WINDOW  *wheadp;                /* Head of list of windows      */
extern  BUFFER  *bheadp;                /* Head of list of buffers      */
extern  BUFFER  *blistp;                /* Buffer for C-X C-B           */
extern  BUFFER  *bmacrp;                /* Buffer for compiled macros   */
extern  short   kbdm[];                 /* Holds keyboard macro data    */
extern  short   *kbdmip;                /* Input pointer for above      */
extern  short   *kbdmop;                /* Output pointer for above     */
extern  char    pat[];                  /* Search pattern               */
extern  char    rpat[];                 /* Replacement pattern          */
extern  char    prnhdr[];               /* Print header                 */
extern  char    prndate[];              /* Print date                   */
extern  char    lastbuf[];              /* Saved name of last used buf  */
extern  char    fillprefix[];           /* String to append filled lines*/
extern  char    *modes[];               /* The mode names               */
extern  TERM    term;                   /* Terminal information.        */
 
extern  BUFFER  *bfind();               /* Lookup a buffer by name      */
extern  WINDOW  *wpopup();              /* Pop up window creation       */
extern  LINE    *lalloc();              /* Allocate a line              */
 
#if ALCYON                              /* CPM-68K Alcyon C compiler    */
extern char     *malloc();
extern FILE     *fopen();
extern char     *strcpy(), *strncpy(), *alias();
#endif
#if ST
extern  long    gemdos();
extern  int     scancode;               /* most recent key scancode     */
extern  int     shiftstat;              /* most recent status of shift  */
#endif
SHAR_EOF
cat << \SHAR_EOF > errno.h
 
/*
 * errno.h - error codes
 */
 
#define EPERM   1
#define ENOENT  2
#define ESRCH   3
#define EINTR   4
#define EIO     5
#define ENXIO   6
#define E2BIG   7
#define ENOEXEC 8
#define EBADF   9
#define ECHILD  10
#define EAGAIN  11
#define ENOMEM  12
#define EACCES  13
#define EFAULT  14
#define ENOTBLK 15
#define EBUSY   16
#define EEXIST  17
#define EXDEV   18
#define ENODEV  19
#define ENOTDIR 20
#define EISDIR  21
#define EINVAL  22
#define ENFILE  23
#define EMFILE  24
#define ENOTTY  25
#define ETXTBSY 26
#define EFBIG   27
#define ENOSPC  28
#define ESPIPE  29
#define EROFS   30
#define EMLINK  31
#define EPIPE   32
 
/* math software */
#define EDOM    33
#define ERANGE  34
 
/* hereafter is available to CP/M specials */
#define ENODSPC 35
#define ERENAME 36
 
/****** end of errno.h ******/
 
 
SHAR_EOF
cat << \SHAR_EOF > kermit.h
/*
*       K e r m i t     File Transfer Utility
*       Header file for uEmail
*/
 
/* Symbol Definitions */
 
#define MAXPACKSIZ      94      /* Maximum packet size */
#define MYPACKSIZ       94      /* My packet size */
#define SOH             1       /* Start of header */
#define CLEAR           26      /* ^Z clear screen */
#define LF              10      /* ASCII Line Feed */
#define CR              13      /* ASCII Carriage Return */
#define SP              32      /* ASCII space */
#define DEL             127     /* Delete (rubout) */
#define ESCCHR          0x1c    /* Default escape character for CONNECT */
#define REMOTE          1
#define HOST            2
#define SHOWHELP        3
#define DEFLO           1       /* default == XON/XOFF */
#define NOFLO           0
#define XON             1
#define RTS             2
#define DEFBPS          1200
#define BORDER          0
#define CURSOR          1
#define STATUS          2
#define LETTER          3
#define BLACK           0
#define COBALT          7
#define RED             0x700
#define TEAL            0x55
#define DEFPAR          '\0'    /* Default no parity */
 
#define MAXTRY          10      /* Times to retry a packet */
#define MYQUOTE         '#'     /* Quote character I will use */
#define MYPAD           0       /* Number of padding characters I will need */
#define MYPCHAR         0       /* Padding character I need (0) */
#define DEFMAXL         80      /* Default packer size */
#define DEFTIME         5       /* Default timeout */
#define DEFPAD          0       /* Default pad characters */
#define DEFPADC         0       /* Default pad character */
#define DEFEOL          '\n'    /* Default EOL character */
#define DEFQUOTE        '#'     /* Default QUOTE character */
#define DEFQBIN         'N'     /* Default QBIN character */
 
#define QBIN            '&'     /* Character for binary quoting */
 
#define MYEOL           '\r'    /* End-Of-Line character I need */
 
#define MYTIME          10      /* Seconds after which I should be timed out */
#define MAXTIM          60      /* Maximum timeout interval */
#define MINTIM          2       /* Minumum timeout interval */
 
/* Macro Definitions */
 
/*
* tochar:       converts a control character to a printable one by adding a
*               space.
*
* unchar:       undoes tochar.
*
* ctl:          converts between control characters and printable characters by
*               toggling the control bit (ie. ^A becomes A and A becomes ^A).
*/
#define tochar(ch)      ((ch) + ' ')
#define unchar(ch)      ((ch) - ' ')
#define ctl(ch)         ((ch) ^ 64 )
 
 
/* Global Variables */
int     size,
        spsiz,          /* Maximum send packet size */
        pad,            /* How much padding to send */
        timint,         /* Timeout for foreign host on sends */
        n,              /* Packet number */
        np,             /* Packet count for display */
        numtry,         /* Times this packet retried */
        oldtry,         /* Times previous packet retried */
        parity,         /* o,e,s,m or 0 */
        logfile,        /* log session to buffer */
        qflag;          /* -1 if doing 8 bit quoting */
 
char    state,          /* Present state of the automaton */
        padchar,        /* Padding character to send */
        eol,            /* End-Of-Line character to send */
        quotech,        /* Quote character in incoming data */
        qbin,           /* character for binary quoting */
        *filnam,        /* Current file name */
        recpkt[MAXPACKSIZ],       /* Receive packet buffer */
        packet[MAXPACKSIZ];       /* Packet buffer */
 
extern  char deflow[];
extern  int flow, defbaud, bps;
extern
void    spack(),
        rpar();
 
extern
char    rinit(),
        rfile(),
        rdata(),
        rpack(),
        sinit(),
        sfile(),
        sdata(),
        seof(),
        sbreak();
SHAR_EOF
cat << \SHAR_EOF > keycode.h
/* return the ascii value of a scan coded key */
int keyscan[] = {
   /* ASCII */                          /* scan */
   0x00,                                /* keycode value = 0x00 */
   0x1b,                                /* 0x01 */
   0x31,                                /* 0x02 */
   0x32,                                /* 0x03 */
   0x33,                                /* 0x04 */
   0x34,                                /* 0x05 */
   0x35,                                /* 0x06 */
   0x36,                                /* 0x07 */
   0x37,                                /* 0x08 */
   0x38,                                /* 0x09 */
   0x39,                                /* 0x0A */
   0x30,                                /* 0x0B */
   0x2d,                                /* 0x0C */
   0x3d,                                /* 0x0D */
   0x08,                        /* 0x0E */
   0x09,                              /* 0x0F */
   0x51,                                /* 0x10 */
   0x57,                                /* 0x11 */
   0x45,                                /* 0x12 */
   0x52,                                /* 0x13 */
   0x54,                                /* 0x14 */
   0x59,                                /* 0x15 */
   0x55,                                /* 0x16 */
   0x49,                                /* 0x17 */
   0x4f,                                /* 0x18 */
   0x50,                                /* 0x19 */
   0x5b,                                /* 0x1A */
   0x5d,                                /* 0x1B */
   0x0d,                           /* 0x1C */
   0x00,                          /* 0x1D */
   0x41,                                /* 0x1E */
   0x53,                                /* 0x1F */
   0x44,                                /* 0x20 */
   0x46,                                /* 0x21 */
   0x47,                                /* 0x22 */
   0x48,                                /* 0x23 */
   0x4a,                                /* 0x24 */
   0x4b,                                /* 0x25 */
   0x4c,                                /* 0x26 */
   0x3b,                                /* 0x27 */
   0x27,                                /* 0x28 */
   0x60,                                /* 0x29 */
   0x00,                               /* 0x2A */
   0x5c,                               /* 0x2B */
   0x5a,                                /* 0x2C */
   0x58,                                /* 0x2D */
   0x43,                                /* 0x2E */
   0x56,                                /* 0x2F */
   0x42,                                /* 0x30 */
   0x4e,                                /* 0x31 */
   0x4d                                /* 0x32 */
};
SHAR_EOF
cat << \SHAR_EOF > printer.h
/* Printer defines for Brother Letter quality printers */
 
#define BUFSIZE 512     /* 4 * 128 */
#define MYBUF 1536      /* 3/4 of my print buffer */
#define ESC 0x1b
#define FORMFEED '\f'
#define TAB 0x09
#define SPACE  ' '
#define NEWLINE  '\n'
#define CR '\r'
#define RS 0x1e
#define US 0x1f
#define RN 0x50
 
extern int tabsize;
extern int isnprint;
static int in;          /* file descriptor for low level printer I/O */
static int spacing;
static int temptab;
static int linecnt;
static char *prnstr;
int pagecnt;
 
#define RESET Cprnout(ESC);Cprnout(CR);Cprnout(RN)
#define NEWPAGE Cprnout(FORMFEED);Cprnout(CR)
#define PRINT(arg) Cprnout((arg))
#define SET_LMARG Cprnout(ESC);Cprnout(0x39);\
        Cprnout(CR)
#define SET_TAB Cprnout(ESC);Cprnout(0x31)
#define SET_PITCH(arg) Cprnout(ESC);Cprnout(US);\
        Cprnout((arg))
#define SET_HEIGHT(arg) Cprnout(ESC);Cprnout(RS);\
        Cprnout((arg))
#define PRNOUT(arg1,arg2) Cprnout(ESC);Cprnout((arg1));\
        Cprnout((arg2))
SHAR_EOF
cat << \SHAR_EOF > shell.h
/* shell.h header for uemail files shell.c, emcc.c, and path.c */
 
 
#include <stdio.h>
#include <ctype.h>
#include <osbind.h>
#include "ed.h"
 
#define GEMLOAD         /* if prog was loaded from GEM give back 256K to OS */
#define MAXPATH 128
#define MAXINPUT 80
extern long adderr, buserr;     /* default GEMDOS error handlers */
extern int errexit();           /* my error handler */
 
extern char cline[MAXINPUT+1];          /* holds the command line and other thin
gs */
extern char pnam[MAXINPUT+1];           /* program name */
extern char source[MAXINPUT+1];         /* name of file for cc() */
extern char path[MAXPATH+1];            /* current path string */
extern int delete;                      /* on/off for above     */
extern int dolink;                      /* link a program       */
extern int temdrv;
extern int curdrv;
 
/* aliases are kept in dynamically allocated tables.  Space for each alias
 * and its associated value is allocated as needed.
 */
typedef struct  ALITAB {
        struct  ALITAB  *a_forw;        /* next table   */
        char            *alias;         /* alias        */
        char            *value;         /* actual value */
} ALITAB;
 
extern ALITAB *aheadp;                  /* structure header */
SHAR_EOF
#       End of shell archive
exit 0
%NONAME-W-NOMSG, Message number 00000000

RDROYA01@ULKYVX.BITNET.UUCP (02/05/87)

#       This is a shell archive.
#       Remove everything above and including the cut line.
#       Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar: Shell Archiver
#       Run the following text with /bin/sh to create:
#       kermit.c
#       kertrans.c
#       kerrec.c
#       kertty.c
# This archive created: Tue Feb  3 17:54:14 1987
cat << \SHAR_EOF > kermit.c
/* kermit.c  support routines and main entry point for Uemail file transfer
 * functions.  Kermit send, receive, get, put, finish, and bye supported.
 * Buffer logging and direct ASCII transfer also available.  All actions are
 * ASCII 7 bit files.  Transfers are made into and out of Uemail buffers.
 */
 
#include <stdio.h>
#include <osbind.h>
#include <ctype.h>
#include "ed.h"
#include "kermit.h"
 
 
int emacsfile;          /* if TRUE then \r become \n on ASCII transfers */
char getfiln[NFILEN];   /* name for remote file get or put */
char deflow[] = "XON/XOFF";
static unsigned char *tsr_ptr = (char *)0x00fffa2d; /* See St internals */
static long *hz_200 =  (long *)0x000004ba;
int *aaddress(); /* thanks Jwahar R. Bammi for this line A code */
int *aline_addr; /* Ptr to base of Aline variables */
int sav_row, sav_col;
int flow = DEFLO;       /* default flow control */
int defbaud = 7;
int bps = DEFBPS;
unsigned long *hpterm;  /* HOST */
 
/* KERMIT CTRL command prompt for action and switch to appropriate state.
 * Returns TRUE on success. !TRUE on failures.  Bound to ^_ and HELP on ST.
 */
kermit(flg, _n)
register int flg, _n;
{
        static char command[9]; /* command */
        register char key;
        register int owf;
 
        owf = curwp->w_flag;            /* save for redraw */
 
        if ((_n=mlreply("Kermit-ST> ",command,8)) != TRUE)
                return(_n==FALSE ? FALSE : ABORT);
 
        /* Initialize these values */
        /* Hope the first packet will get across OK */
 
        eol = CR;               /* EOL for outgoing packets */
        quotech = '#';          /* Standard control-quote char "#" */
        pad = 0;                /* No padding */
        padchar = '\0';         /* Use null if any padding wanted */
        timint = DEFTIME;       /* Default timeout */
        parity = DEFPAR;        /* Default to no parity */
        logfile = 0;            /* Capture flag */
        emacsfile = 0;          /* Trans to emacs (no EOF) */
        en8quote(FALSE);
 
        key = tolower(command[0]);
        switch (key)
                {
                case 'b':               /* B = Bye */
                        if(mlyesno("Logout remote")==TRUE)
                                if(gencmdsw('L')==FALSE)
                                        {
                                        mlwrite("Remote does not ACK BYE");
                                        (*term.t_beep)();
                                        return(FALSE);
                                        }
                        return(TRUE);   /* No update */
                case 'c':              /* C = Connect command */
                        connect();
                        break;
                case 'e':              /* E = transmit to GNU_EMACS command */
                        if(usebuffer(NULL,NULL) != TRUE)
                                return(FALSE);
                        gotobob(NULL, 1);
                        update();
                        mlwrite("[Transmitting %s to remote EMACS]",curbp->b_bna
me);
                        emacsfile = 1;
                        trans();
                        emacsfile = 0;
                        connect();
                        break;
                case 'f':               /* F = Finish */
                        if(gencmdsw('F')==FALSE)
                                {
                                mlwrite("Remote does not ACK FINISH");
                                (*term.t_beep)();
                                return(FALSE);
                                }
                        return(TRUE);   /* No update */
                case 'g':              /* G = Get command */
                        if((_n=mlreply("Remote filename: ",getfiln,NFILEN))!=TRU
E)
                                return(_n);
                        if(usebuffer(NULL,NULL) != TRUE)
                                return(FALSE);
                        gotoeob(NULL, 1);
                        update();
                        if((_n=getsw()) == FALSE)
                                {
                                (*term.t_beep)();
                                mlreply("Get failed. Type <CR>",command,2);
                                }
                        break;
                case 'l':       /* L = Log session */
                        if(usebuffer(NULL,NULL) != TRUE)
                                return(FALSE);
                        gotoeob(NULL, 1);
                        logfile = TRUE;
                        connect();
                        break;
                case 'r':              /* R = Receive command */
                        if(usebuffer(NULL,NULL) != TRUE)
                                return(FALSE);
                        gotoeob(NULL, 1);
                        update();
                        if((_n=recsw()) == FALSE)
                                {
                                (*term.t_beep)();
                                mlreply("Receive failed. Type <CR>",command,2);
                                }
                        break;
                case 'p':               /* P = Put command */
                        if((_n=mlreply("Remote filename: ",getfiln,NFILEN))!=TRU
E)
                                return(_n);
                case 's':               /* S = Send command */
                        if(usebuffer(NULL,NULL) != TRUE)
                                return(FALSE);
                        if (key == 's')
                                strcpy(getfiln,curbp->b_bname);
                        gotobob(NULL, 1);
                        update();
                        if((_n=sendsw()) == FALSE)
                                {
                                if (key == 's')
                                        mlwrite("Send failed");
                                else
                                        mlwrite("Put failed");
                                (*term.t_beep)();
                                }
                        if (key == 's')
                                connect();
                        break;
                case 't':              /* T = transmit command */
                        if(usebuffer(NULL,NULL) != TRUE)
                                return(FALSE);
                        gotobob(NULL, 1);
                        update();
                        mlwrite("[Transmitting: %s]",curbp->b_bname);
                        trans();
                        connect();
                        break;
                default:
                        mlwrite("Connect, Send, Receive, Get, Put, Finish, Bye\
, Log, Transfer");
 
                        return(FALSE);
                }
        /* Restore controlling tty's modes */
 
        owf |= WFHARD; owf |= WFMODE;
        curwp->w_flag |= owf;
        sgarbf = TRUE;  /* we know screen is garbage after connect */
        update();
        return(TRUE);
}
 
/*
*      c o n n e c t
*
* Establish a virtual terminal connection with the remote host, over an
* assigned tty line.
*/
 
connect()
{
        register int c;
 
        setterm(REMOTE);
        while (1)
                {
                if (Bconstat(2))
                        if(readcon() == FALSE)
                                break;
                if (Bconstat(1))
                        {
                        c=readaux();
                        ttputc(c);
                        if (logfile)
                                {
                                if (c=='\n')
                                        {
                                        lnewline();
                                        continue;
                                        }
                                if (c=='\r')
                                        continue;
                                linsert(1,c);
                                }
                        }
                }
        setterm(HOST);
        return(FALSE);
}
 
/* set up terminal's screen and allow it to be saved for later.
 */
setterm(f)
int f;
{
        static char scrbase[0x7d10];
        static unsigned char firsttime = TRUE;
        extern int deskcol;
 
        if (hpterm == NULL)
                hpterm = Physbase();    /* init TERM screen address */
        if (f == REMOTE)
                {
                Bconout(2,0x1b);        /* disable cursor */
                Bconout(2,'f');
                copy(scrbase,hpterm,0x7d00);
                if (firsttime)
                        {
                        aline_addr = aaddress();
                        Bconout(2,0x1b);        /* clear screen */
                        Bconout(2,'E');
                        showhelp();
                        sav_row = aline_addr[-13];/* save cursor position */
                        sav_col = aline_addr[-14];
                        Rsconf(defbaud,flow,-1,-1,-1,-1);
                        mtwrite("[%s active at %dbps]",deflow,bps);
                        firsttime = FALSE;
                        }
                (*term.t_move)(sav_row,sav_col);
                Bconout(2,0x1b);        /* enable cursor */
                Bconout(2,'e');
                return(TRUE);
                }
        else if (f == HOST)
                {
                sav_row = aline_addr[-13];      /* save cursor position */
                sav_col = aline_addr[-14];
                Bconout(2,0x1b);        /* disable cursor */
                Bconout(2,'f');
                copy(hpterm,scrbase,0x7d00);
                ttopen();               /* restore uemail screen */
                Bconout(2,0x1b);        /* enable cursor */
                Bconout(2,'e');
                return(TRUE);
                }
        else if (f == SHOWHELP)
                {
                sav_row = aline_addr[-13];      /* save cursor position */
                sav_col = aline_addr[-14];
                Bconout(2,0x1b);                /* disable cursor */
                Bconout(2,'f');
                copy(hpterm,scrbase,0x7d00);    /* save current screen */
                showhelp();                     /* write help message */
                while(ttgetc())                 /* wait for */
                        if (scancode == 0x62)   /* HELP key */
                                break;
                copy(scrbase,hpterm,0x7d00);    /* restore old screen */
                (*term.t_move)(sav_row,sav_col);
                Bconout(2,0x1b);                /* enable cursor */
                Bconout(2,'e');
                return(TRUE);
                }
}
 
/*
 * return the base address of the line A variables
 * Bammi @ Case
 */
int *
aaddress()
{
        asm("dc.w $A000");/* Line A trap - 0000 is init line-A  */
        /* d0 and a0 now contain the address
         * so we can just return and the result
         * will be valid
         */
}
 
/* a mini-mlwrite for the terminal mode */
/* VARARGS */
mtwrite(fmt,arg1,arg2,arg3,arg4)
register char *fmt;
register long arg1,arg2,arg3,arg4;
{
        static char buf[128];
 
        sprintf(buf,fmt,arg1,arg2,arg3,arg4);
        ttputc(0x1b);ttputc('j');       /* save cursor */
        (*term.t_move)(term.t_nrow,0);
        ttputc(0x1b);ttputc('l');       /* delete line */
        Cconws(buf);
        ttputc(0x1b);ttputc('k');       /* restore cursor */
        return(TRUE);
}
 
/* A modicum of help for the RS232 connection module */
int
showhelp()
{
        register int i;
        i = 5;
 
        Crawio(0x1b);
        Crawio('p');    /* rev video on */
        (*term.t_move)(i++,17);
        Cconws("                                     ");
        (*term.t_move)(i++,17);
        Cconws("   ALT-UNDO    return to Uemail      ");
        (*term.t_move)(i++,17);
        Cconws("   ALT-B       set the baud rate     ");
        (*term.t_move)(i++,17);
        Cconws("   ALT-C       change color          ");
        (*term.t_move)(i++,17);
        Cconws("   ALT-E       load an alias file    ");
        (*term.t_move)(i++,17);
        Cconws("   ALT-L       load a macro file     ");
        (*term.t_move)(i++,17);
        Cconws("   ALT-O       turn off flow control ");
        (*term.t_move)(i++,17);
        Cconws("   ALT-R       turn on RTS/CTS       ");
        (*term.t_move)(i++,17);
        Cconws("   ALT-X       turn on XON/XOFF      ");
        (*term.t_move)(i++,17);
        Cconws("   ALT-T       show the current time ");
        (*term.t_move)(i++,17);
        Cconws("   ALT-CTRL-B  send a break          ");
        (*term.t_move)(i++,17);
        Cconws("   ALT-CTRL-C  run a program         ");
        (*term.t_move)(i++,17);
        Cconws("   ALT-?       this help message     ");
        (*term.t_move)(i++,17);
        Cconws("   HELP        this help message     ");
        (*term.t_move)(i++,17);
        Cconws("                                     \r\n");
        Crawio(0x1b);
        Crawio('q');    /* rev video off */
        return(TRUE);
}
 
/* setbaud for RS232 (default is 1200) */
 
int
setbaud()
{
        register int c;
 
        mtwrite("Baud rate [a/b/c/d/e] a = 300, \
b = 1200, c = 2400, d = 9600, e = 19,200");
        c = ttgetc();
        switch(c)
                {
                case 'a':
                case 'A':
                        defbaud = 9;
                        bps = 300;
                        Rsconf(defbaud,flow,-1,-1,-1,-1);
                        mtwrite("[Baud = %dbps]",bps);
                        return(TRUE);
                case 'b':
                case 'B':
                        defbaud = 7;
                        bps = 1200;
                        Rsconf(defbaud,flow,-1,-1,-1,-1);
                        mtwrite("[Baud = %dbps]",bps);
                        return(TRUE);
                case 'c':
                case 'C':
                        defbaud = 4;
                        bps = 2400;
                        Rsconf(defbaud,flow,-1,-1,-1,-1);
                        mtwrite("[Baud = %dbps]",bps);
                        return(TRUE);
                case 'd':
                case 'D':
                        defbaud = 1;
                        bps = 9600;
                        Rsconf(defbaud,flow,-1,-1,-1,-1);
                        mtwrite("[Baud = %dbps]",bps);
                        return(TRUE);
                case 'e':
                case 'E':
                        defbaud = 0;
                        bps = 19200;
                        Rsconf(defbaud,flow,-1,-1,-1,-1);
                        mtwrite("[Baud = %dbps]",bps);
                        return(TRUE);
                default:
                        Rsconf(defbaud,flow,-1,-1,-1,-1);
                        mtwrite("[Baud = %dbps]",bps);
                        return(TRUE);
                }
}
 
/*
*      KERMIT utilities.
*/
 
/* generic command for BYE and FINish */
gencmdsw(cmd)
char cmd;
{
        int num, len;              /* Packet number, length */
        packet[0] = cmd;              /* Generic command */
        spack('G', 0, 1, packet);     /* Send Generic command  */
 
        switch(rpack(&len,&num,packet))  /* Get packet */
                {
                case 'Y':
                        return(TRUE);
                case 'E':
                        prerrpkt(packet);
                        return(FALSE);
                default:
                        return(FALSE);
                }
 
}
 
/*
*      b u f i l l
*
* Get a bufferful of data from the buffer that's being sent.
* Only control-quoting and 8-bit quoting is done;
* repeat count prefixes are not handled.
*/
 
bufill(buffer)
char buffer[];                    /* Buffer */
{
        register char t;                         /* Char read from file */
        register char *buffend;          /* End of buffer pointer */
        register char *buffp;              /* Pointer into buffer */
        register int unprintable;              /* Is a character printable ?*/
        short eolflag = FALSE;                  /* kludge for VAX */
 
        buffend = &buffer[spsiz-9];     /* set up end of buffer pointer */
        buffp=buffer;              /* and the current position */
 
        while(curwp->w_dotp != curbp->b_linep)
                {
                t = lgetc(curwp->w_dotp, curwp->w_doto);
                if (curwp->w_doto == llength(curwp->w_dotp))
                        {
                        t = eol;
                        eolflag = TRUE; /* kludge for VAX */
                        }
 
                unprintable = ((t<SP)||(t==DEL));
 
                /* Does this char require special handling? */
                if ((unprintable) || t==quotech)
                        {
                        *buffp++ = quotech;       /* Quote the character */
                        if (unprintable)
                                t = ctl(t);          /* and uncontrolify */
                        }
                *buffp++ = t;
                if (eolflag)
                        {
                        eolflag = FALSE;        /* be sure it gets \r\n pair */
                        *buffp++ = quotech;     /* this is for VAX/VMS */
                        *buffp++ = ctl(LF);
                        }
                forwchar(FALSE,1);
                /* Check length */
                if (buffp>=buffend)     /* this is how you avoid pointer */
                        return((int)buffp-(int)buffer); /* subtraction */
                }                       /* not (int)(buffp-buffer) */
        if (buffp == buffer)
                return(EOF);
        return((int)buffp-(int)buffer); /* Handle partial buffer */
}
 
/*
*      b u f e m p
*
* Put data from an incoming packet into the default buffer.
*/
 
bufemp(buffer,len)
char buffer[];                    /* Buffer */
int len;                                /* Length */
{
        register int i;          /* Counter */
        register char t;                /* Character holder */
        register int highbit;      /* place to hold quoted highbit */
 
        highbit = 0;
        for (i=0; i<len; i++)      /* Loop thru the data field */
                {
                t = buffer[i];            /* Get a character */
                if (t == MYQUOTE)              /* Control quote? */
                        {                              /* Yes */
                        t = buffer[++i];        /* Get the quoted character */
                        /* Low order bits match quote char? */
                        if (((t & 0177) != MYQUOTE) &&
                            (!qflag || ((t & 0x7F)!=qbin)))
                                t = ctl(t);     /* No, uncontrollify it */
                        }
                t |= highbit;   /* set top bit if needed */
                highbit=0;
                if (t==eol)
                        {
                        lnewline();
                        continue;
                        }
                if (t=='\n')
                        continue;
                linsert(1,t);
        }
}
 
int
readaux()
{
        int i;
 
        while(!Bconstat(1))     /* loop until line is ready or */
                if (i++ >= 32000)       /* time is up */
                        return(FALSE);
        return ((int)Bconin(1));        /* char not masked when read */
}
 
sauxstr(buf,len)
char *buf;
int len;
{
        for(;len>0;len--)
                sendaux(*buf++);
}
 
sendaux(c)
int c;
{
        while(!Bcostat(1))
                {
                if (Bconstat(2))
                        if (ttgetc()==0x07)
                                return(FALSE);
                }
        Bconout(1,c);   /* Send character */
}
 
/*
*      p r e r r p k t
*
* Print contents of error packet received from remote host.
*/
prerrpkt(msg)
char *msg;
{
        mlwrite("ABORT: %s",msg);
        return;
}
 
en8quote(t)
int t;
{
        qflag = t;
/* no binary files allowed */
}
 
/* tsr_ptr points to the 8 bit TSR register of the 68901
 * From Jwahar R. Bammi
 * send a break
 * Modifies Bit 3 in the TSR (reg 23) of the Mfp
 */
 
sendbrk()
{
        register long save_ssp;
        register long time;
 
        save_ssp = Super(0L);   /* Super Mode */
 
        /* set bit 3 of the TSR */
        *tsr_ptr |= (unsigned char)8;
 
        /* wait for 250 ms -- you can adjust the duration
         * 250 ms seems to be almost a universal figure for break
         * duration.
         */
        time = *hz_200 + 50;
        while(*hz_200 < time)
        /* wait */ ;
        /* reset bit 3 of the tsr */
        *tsr_ptr &= (unsigned char)~8;
 
        Super(save_ssp);        /* Back to user Mode */
}
 
/*
 *  f l u s h i n p u t
 *
 *  Dump all pending input to clear stacked up NACK's.
 */
flushinput()
{
        /* TOS Clear AUX receive buffer */
        while (Cauxis())
                Cauxin();
}
SHAR_EOF
cat << \SHAR_EOF > kertrans.c
/* kertrans.c transfer and send functions to support file sends through
 * ueMail.  All functions available only through kermit().
 */
 
#include <stdio.h>
#include <osbind.h>
#include "ed.h"
#include "kermit.h"
 
/* ASCII transmit.  Some delay ability.  Use for short files only or over
 * a direct link.
 */
extern int emacsfile;
extern char getfiln[NFILEN];
 
int
trans()
{
        register int c;
        register int ln = 0;
 
        while (curwp->w_dotp != curbp->b_linep)
                {
                c = lgetc(curwp->w_dotp, curwp->w_doto);
                if (curwp->w_doto == llength(curwp->w_dotp))
                        {
                        mlwrite("[Transmitting line: %d]",++ln);
                        if (!emacsfile)
                                c = '\r';
                        else
                                c = '\n';
                        }
                while (!(int)Bcostat(1))        /* check for line ready */
                        {
                        if (Bconstat(2))
                                if (ttgetc()==0x07)
                                        return(FALSE);
                        }
                sendaux(c);
                if (!forwchar(FALSE, 1))
                        return(FALSE);
                }
        if(!emacsfile)
                {
                while (!(int)Bcostat(1))
                        {
                        if (Bconstat(2))
                                if (ttgetc()==0x07)
                                        return(FALSE);
                        }
                sendaux(CLEAR);
                }
        return(ln);
}
 
/*
*      s e n d s w
*
* Sendsw is the state table switcher for sending files. It loops until
* either it finishes, or an error is encountered. The routines called
* by sendsw are responsible for changing the state.
*
*/
int
sendsw()
{
 
        state = 'S';        /* Send initiate is the start state */
        n = np = 0;               /* Initialize message number */
        numtry = 0;          /* Say no tries yet */
        while(TRUE)          /* Do this as long as necessary */
        {
                switch(state)
                {
                case 'S':
                        mlwrite("[Sending init packet]");
                        state = sinit();
                        break; /* Send-Init */
                case 'F':
                        state = sfile();
                        break; /* Send-File */
                case 'D':
                        state = sdata();
                        break; /* Send-Data */
                case 'Z':
                        state = seof();
                        break; /* Send-End-of-File */
                case 'B':
                        state = sbreak();
                        break; /* Send-Break */
                case 'C':
                        return (TRUE);    /* Complete */
                case 'A':
                        return (FALSE);  /* "Abort" */
                default:
                        return (FALSE);  /* Unknown, fail */
                }
        }
}
 
 
/*
*      s i n i t
*
* Send Initiate: send this host's parameters and get other side's back.
*/
 
char sinit()
{
        int num, len;              /* Packet number, length */
        /* If too many tries, give up */
        if (numtry++ > MAXTRY) return('A');
        len = spar(packet);                  /* Fill up init info packet */
 
 
        spack('S',n,len,packet);                /* Send an S packet */
        switch(rpack(&len,&num,recpkt)) /* What was the reply? */
        {
        case 'N':
                return(state);    /* NAK, try it again */
 
        case 'Y':                      /* ACK */
                if (n != num)      /* If wrong ACK, stay in S state */
                        return(state);  /* and try again */
                rpar(recpkt,len);       /* Get other side's init info */
 
                numtry = 0;          /* Reset try counter */
                n = (n+1)%64;      /* Bump packet count */
                return('F');        /* OK, switch state to F */
 
        case 'E':                      /* Error packet received */
                prerrpkt(recpkt);       /* Print it out and */
                return('A');        /* abort */
 
        case FALSE:
                return(state);    /* Receive failure, try again */
 
        default:
                return('A');        /* Anything else, just "abort" */
        }
}
 
 
/*
*      s f i l e
*
* Send File Header.
*/
 
char sfile()
{
        int num, len;              /* Packet number, length */
 
        /* If too many tries, give up */
        if (numtry++ > MAXTRY) return('A');
        len = strlen(getfiln);
        mlwrite("[Sending %s as %s]",curbp->b_bname,getfiln);
        spack('F',n,len,getfiln);            /* Send an F packet */
        switch(rpack(&len,&num,recpkt))  /* What was the reply? */
        {
        case 'N':                      /* NAK, just stay in this state, */
                /* unless it's NAK for next packet */
                num = (--num<0 ? 63:num);
                if (n != num)      /* which is just like an ACK for */
                        return(state);  /* this packet so fall thru to... */
 
        case 'Y':                      /* ACK */
                if (n != num) return(state); /* If wrong ACK, stay in F state */
                numtry = 0;          /* Reset try counter */
                n = (n+1)%64;      /* Bump packet count */
                size = bufill(packet);  /* Get first data from file */
                return('D');        /* Switch state to D */
 
        case 'E':                      /* Error packet received */
                prerrpkt(recpkt);       /* Print it out and */
                return('A');        /* abort */
 
        case FALSE:
                return(state);    /* Receive failure, stay in F state */
 
        default:
                return('A');        /* Something else, just "abort" */
        }
}
 
 
/*
*      s d a t a
*
* Send File Data
*/
 
char sdata()
{
        int num, len;              /* Packet number, length */
 
        if (numtry++ > MAXTRY) return('A'); /* If too many tries, give up */
 
        spack('D',n,(int)size,packet);         /* Send a D packet */
        switch(rpack(&len,&num,recpkt))  /* What was the reply? */
        {
        case 'N':                      /* NAK, just stay in this state, */
                /* unless it's NAK for next packet */
                num = (--num<0 ? 63:num);
                if (n != num)      /* which is just like an ACK for */
                        return(state);  /* this packet so fall thru to... */
 
        case 'Y':                      /* ACK */
                if (n != num) return(state); /* If wrong ACK, fail */
                numtry = 0;          /* Reset try counter */
                n = (n+1)%64;      /* Bump packet count */
                mlwrite("[Sending packet: %d]",++np);
                if ((size=bufill(packet))==EOF) /* Get data from file */
                        return('Z');    /* If EOF set state to that */
                return('D');        /* Got data, stay in state D */
 
        case 'E':                      /* Error packet received */
                prerrpkt(recpkt);       /* Print it out and */
                return('A');        /* abort */
 
        case FALSE:
                return(state);    /* Receive failure, stay in D */
 
        default:
                return('A');    /* Anything else, "abort" */
        }
}
 
 
/*
*      s e o f
*
* Send End-Of-File.
*/
 
char seof()
{
        int num, len;              /* Packet number, length */
        if (numtry++ > MAXTRY) return('A'); /* If too many tries, "abort" */
 
        spack('Z',n,0,packet);    /* Send a 'Z' packet */
        switch(rpack(&len,&num,recpkt))  /* What was the reply? */
        {
        case 'N':                      /* NAK, just stay in this state, */
                /* unless it's NAK for next packet, */
                num = (--num<0 ? 63:num);
                if (n != num)      /* which is just like an ACK for */
                        return(state);  /* this packet so fall thru to... */
 
        case 'Y':                      /* ACK */
                if (n != num) return(state); /* If wrong ACK, hold out */
                numtry = 0;                  /* Reset try counter */
                n = (n+1)%64;              /* and bump packet count */
                        return('B');    /* if not, break, EOT, all done */
 
        case 'E':                      /* Error packet received */
                prerrpkt(recpkt);       /* Print it out and */
                return('A');        /* abort */
 
        case FALSE:
                return(state);    /* Receive failure, stay in Z */
 
        default:
                return('A');    /* Something else, "abort" */
        }
}
 
 
/*
*      s b r e a k
*
* Send Break (EOT)
*/
 
char sbreak()
{
        int num, len;              /* Packet number, length */
        if (numtry++ > MAXTRY) return('A'); /* If too many tries "abort" */
 
        spack('B',n,0,packet);    /* Send a B packet */
        switch (rpack(&len,&num,recpkt))        /* What was the reply? */
        {
        case 'N':                      /* NAK, just stay in this state, */
                /* unless NAK for previous packet, */
                num = (--num<0 ? 63:num);
                if (n != num)      /* which is just like an ACK for */
                        return(state);  /* this packet so fall thru to... */
 
        case 'Y':                      /* ACK */
                if (n != num) return(state); /* If wrong ACK, fail */
                numtry = 0;          /* Reset try counter */
                n = (n+1)%64;      /* and bump packet count */
                return('C');        /* Switch state to Complete */
 
        case 'E':                      /* Error packet received */
                prerrpkt(recpkt);       /* Print it out and */
                return('A');        /* abort */
 
        case FALSE:
                return(state);    /* Receive failure, stay in B */
 
        default:
                return ('A');      /* Other, "abort" */
        }
}
SHAR_EOF
cat << \SHAR_EOF > kerrec.c
/* kerrec.c kermit protocol file receive support.
 */
 
#include <stdio.h>
#include <osbind.h>
#include "ed.h"
#include "kermit.h"
 
extern char getfiln[NFILEN];
 
/*
*      r e c s w
*
* This is the state table switcher for receiving files.
*/
 
recsw()
{
 
        state = 'R';                /* Receive-Init is the start state */
        n = np = 0;                       /* Initialize message number */
        numtry = 0;                  /* Say no tries yet */
 
        while(TRUE)
        {
        switch(state)              /* Do until done */
                {
                case 'R':
                        state = rinit();
                        mlwrite("[Awaiting remote init]");
                        break; /* Receive-Init */
                case 'F':
                        state = rfile();
                        break; /* Receive-File */
                case 'D':
                        state = rdata();
                        break; /* Receive-Data */
                case 'C':
                        return(TRUE);      /* Complete state */
                case 'A':
                        return(FALSE);    /* "Abort" state */
                }
        }
}
 
/*
 * astget.c server function GET
 * implemented by B. Nebel
 */
getsw()
{
        int result;
 
        flushinput();
        spack('R',0,strlen(getfiln),getfiln);
        result = recsw();
        return (result);
}
 
/*
*      r i n i t
*
* Receive Initialization
*/
 
char rinit()
{
        int len, num;              /* Packet length, number */
 
        if (numtry++ > MAXTRY) return('A');     /* If too many tries, "abort" */
 
        switch(rpack(&len,&num,packet))  /* Get a packet */
        {
        case 'S':                      /* Send-Init */
                rpar(packet,len);       /* Get the other side's init data */
                len = spar(packet);     /* Fill up packet with my init info */
                spack('Y',n,len,packet);        /* ACK with my parameters */
                oldtry = numtry;                /* Save old try count */
                numtry = 0;                  /* Start a new counter */
                n = (n+1)%64;      /* Bump packet number, mod 64 */
                return('F');        /* Enter File-Receive state */
 
        case 'E':                      /* Error packet received */
                prerrpkt(recpkt);       /* Print it out and */
                return('A');        /* abort */
 
        case FALSE:                  /* Didn't get packet */
                spack('N',n,0,NULLPTR);    /* Return a NAK */
                return(state);    /* Keep trying */
 
        default:
                return('A');        /* Some other packet type, "abort" */
        }
}
 
 
/*
*      r f i l e
*
* Receive File Header
*/
 
char rfile()
{
        int num, len;              /* Packet number, length */
 
        if (numtry++ > MAXTRY) return('A');     /* "abort" if too many tries */
 
        switch(rpack(&len,&num,packet))  /* Get a packet */
        {
        case 'S':                      /* Send-Init, maybe our ACK lost */
                /* If too many tries "abort" */
                if (oldtry++ > MAXTRY) return('A');
                if (num == ((n==0) ? 63:n-1)) /* Previous packet, mod 64? */
                {                              /* Yes, ACK it again with */
                        len = spar(packet);     /* our Send-Init parameters */
                        spack('Y',num,len,packet);
                        numtry = 0;          /* Reset try counter */
                        return(state);    /* Stay in this state */
                }
                else return('A');       /* Not previous packet, "abort" */
 
        case 'Z':                      /* End-Of-File */
                if (oldtry++ > MAXTRY) return('A');
                if (num == ((n==0) ? 63:n-1)) /* Previous packet, mod 64? */
                {                              /* Yes, ACK it again. */
                        spack('Y',num,0,NULLPTR);
                        numtry = 0;
                        return(state);    /* Stay in this state */
                }
                else return('A');       /* Not previous packet, "abort" */
 
        case 'F':                      /* File Header (just what we want) */
                /* The packet number must be right */
                if (num != n) return('A');
                mlwrite("[Receiving %s as %s]",packet,curbp->b_bname);
 
                spack('Y',n,0,NULLPTR);    /* Acknowledge the file header */
                oldtry = numtry;        /* Reset try counters */
                numtry = 0;
                n = (n+1)%64;      /* Bump packet number, mod 64 */
                return('D');        /* Switch to Data state */
 
        case 'B':                      /* Break transmission (EOT) */
                if (num != n) return ('A'); /* Need right packet number here */
                spack('Y',n,0,NULLPTR);     /* Say OK */
                return('C');        /* Go to complete state */
 
        case 'E':                      /* Error packet received */
                prerrpkt(recpkt);       /* Print it out and */
                return('A');        /* abort */
 
        case FALSE:                  /* Didn't get packet */
                spack('N',n,0,NULLPTR);    /* Return a NAK */
                return(state);    /* Keep trying */
 
        default:
                return ('A');      /* Some other packet, "abort" */
        }
}
 
 
/*
*      r d a t a
*
* Receive Data
*/
 
char rdata()
{
        int num, len;              /* Packet number, length */
        if (numtry++ > MAXTRY) return('A');     /* "abort" if too many tries */
 
        switch(rpack(&len,&num,packet))  /* Get packet */
        {
        case 'D':                      /* Got Data packet */
                if (num != n)      /* Right packet? */
                {                              /* No */
                        if (oldtry++ > MAXTRY)
                                return('A');    /* If too many tries, abort */
                        if (num == ((n==0) ? 63:n-1))
                        { /* Else check previous packet again? */
                                spack('Y',num,0,NULLPTR); /* Yes, re-ACK it */
                                numtry = 0;     /* Reset try counter */
                                return(state);  /* Don't write out data! */
                        }
                        else return('A');       /* sorry, wrong number */
                }
                /* Got data with right packet number */
                bufemp(packet,len);          /* Write the data to the file */
                spack('Y',n,0,NULLPTR);     /* Acknowledge the packet */
                oldtry = numtry;                /* Reset the try counters */
                numtry = 0;                  /* ... */
                mlwrite("[Receiving packet: %d]", ++np);
                n = (n+1)%64;      /* Bump packet number, mod 64 */
                return('D');        /* Remain in data state */
 
        case 'F':                      /* Got a File Header */
                if (oldtry++ > MAXTRY)
                        return('A');        /* If too many tries, "abort" */
                if (num == ((n==0) ? 63:n-1))   /* Else check packet number */
                {                              /* It was the previous one */
                        spack('Y',num,0,NULLPTR);  /* ACK it again */
                        numtry = 0;          /* Reset try counter */
                        return(state);    /* Stay in Data state */
                }
                else return('A');       /* Not previous packet, "abort" */
 
        case 'Z':                      /* End-Of-File */
                /* Must have right packet number */
                if (num != n) return('A');
                spack('Y',n,0,NULLPTR);     /* OK, ACK it. */
                n = (n+1)%64;      /* Bump packet number */
                return('F');        /* Go back to Receive File state */
 
        case 'E':                      /* Error packet received */
                prerrpkt(recpkt);              /* Print it out and */
                return('A');        /* abort */
 
        case FALSE:                  /* Didn't get packet */
                spack('N',n,0,NULLPTR);     /* Return a NAK */
                return(state);    /* Keep trying */
 
        default:
                return('A');        /* Some other packet, "abort" */
        }
}
 
/*
*      r p a c k
*
* Read a Packet
*/
 
char
rpack(len,num,data)
int *len, *num;                  /* Packet length, number */
char *data;                          /* Packet data */
{
        register int i, done;       /* Data character number, loop exit */
        register char t,         /* Current input character */
        cchksum,                /* Our (computed) checksum */
        rchksum,                        /* Checksum received from other host */
        type;              /* Packet type */
 
        do
        {
                if (Bconstat(2))
                        if (ttgetc() == 0x07) /* ^G abort */
                                {
                                mlwrite("User ABORT");
                                (*term.t_beep)();
                                return('A');
                                }
                if (!Cauxis())
                        continue;
                else
                        t = (char)readaux()& 0x7f;
        }while (t != SOH);                    /* Wait for packet header */
 
        done = FALSE;              /* Got SOH, init loop */
        while (!done)              /* Loop to get a packet */
        {
                if (Bconstat(2))
                        if (ttgetc() == 0x07) /* ^G abort */
                                {
                                mlwrite("User ABORT");
                                (*term.t_beep)();
                                return('A');
                                }
                t = (char)readaux();        /* Get character */
                if (t == SOH) continue;  /* Resynchronize if SOH */
                cchksum = t;                /* Start the checksum */
                *len = unchar(t)-3;          /* Character count */
 
                t = (char)readaux();        /* Get character */
                if (t == SOH) continue;  /* Resynchronize if SOH */
                cchksum += t;              /* Update checksum */
                *num = unchar(t);              /* Packet number */
 
                t = (char)readaux();        /* Get character */
                if (t == SOH) continue;  /* Resynchronize if SOH */
                cchksum += t;              /* Update checksum */
                type = t;                      /* Packet type */
 
                for (i=0; i<*len; i++)    /* The data itself, if any */
                {                              /* Loop for character count */
                        if (Bconstat(2))
                                if (ttgetc() == 0x07) /* ^G abort */
                                        {
                                        mlwrite("User ABORT");
                                        (*term.t_beep)();
                                        return('A');
                                        }
                        t = (char)readaux();    /* Get character */
                        if (t == SOH) continue; /* Resynch if SOH */
                        cchksum += t;      /* Update checksum */
                        data[i] = t;        /* Put it in the data buffer */
                }
                data[*len] = 0;          /* Mark the end of the data */
 
                t = (char)readaux();        /* Get last character (checksum) */
                rchksum = unchar(t);        /* Convert to numeric */
                t = (char)readaux();       /* get EOL character and toss it */
                if (t == SOH) continue;  /* Resynchronize if SOH */
                done = TRUE;                /* Got checksum, done */
        }
        /* Fold in bits 7,8 to compute */
        cchksum = (((cchksum&0300) >> 6)+cchksum)&077; /* final checksum */
 
        if (cchksum != rchksum) return((char)FALSE);
        return(type);              /* All OK, return packet type */
}
 
/*      r p a r
*
* Get the other host's send-init parameters
*
*/
 
void
rpar(data,len)
char data[];
int len;
{
        spsiz = DEFMAXL;        /* default packet size */
        timint = DEFTIME;
        pad = DEFPAD;
        padchar = DEFPADC;
        eol = DEFEOL;
        quotech = DEFQUOTE;
        qbin = DEFQBIN;
        switch (len){
        default:
        case 10:        /* attributes */
        case 9:         /* repeat count */
        case 8:         /* Check type */
        case 7:         /* 8 bit quoting */
                qbin = data[6];
                if (((qbin >='!') && (qbin <= '>'))
                    || ((qbin >= '`')&&(qbin <= '~')))
                     /*   qflag = TRUE; */ en8quote(TRUE);     /* jgc dec 25th.
1985 */
        case 6:         /* Incoming data quote character */
                quotech = data[5];
        case 5:         /* EOL character I must send */
                eol = unchar(data[4]);
        case 4:         /* Padding character I must send */
                padchar = ctl(data[3]);
        case 3:         /* Number of pads to send */
                pad = unchar(data[2]);
        case 2:         /* When I should time out */
                timint = unchar(data[1]);
                if ((timint > MAXTIM) || (timint < MINTIM)) timint = MYTIME;
        case 1:         /* Maximum send packet size */
                spsiz = unchar(data[0]);
        case 0:
                break;
        }
        if (qflag) {
                if (qbin == 'N') mlwrite("Can't do 8 bit quoting");
                else if (qbin == 'Y') qbin = QBIN;
        }
}
 
/*
*      s p a r
*
* Fill the data array with my send-init parameters
*
*/
 
int
spar(data)
char data[];
{
        data[0] = tochar(MYPACKSIZ);    /* Biggest packet I can receive */
        data[1] = tochar(MYTIME);       /* When I want to be timed out */
        data[2] = tochar(MYPAD);        /* How much padding I need */
        data[3] = ctl(MYPCHAR);  /* Padding character I want */
        data[4] = tochar(MYEOL);        /* End-Of-Line character I want */
        data[5] = MYQUOTE;            /* Control-Quote character I send */
        data[6] = qflag?QBIN:'Y';       /* Request 8 bit quoting */
        return (7);                  /* return number of parameters */
}
 
/*
*      s p a c k
*
* Send a Packet
*/
 
void
spack(type,num,len,data)
char type, *data;
int num, len;
{
        register int i;                   /* Character loop counter */
        char chksum, buffer[100];       /* Checksum, packet buffer */
        register char *bufp;        /* Buffer pointer */
 
 
        bufp = buffer;            /* Set up buffer pointer */
        for (i=1; i<=pad; i++) sendaux(padchar); /* Issue any padding */
 
        *bufp++ = SOH;            /* Packet marker, ASCII 1 (SOH) */
        *bufp++ = tochar(len+3);        /* Send the character count */
        chksum = tochar(len+3);  /* Initialize the checksum */
        *bufp++ = tochar(num);    /* Packet number */
        chksum += tochar(num);    /* Update checksum */
        *bufp++ = type;          /* Packet type */
        chksum += type;          /* Update checksum */
 
        for (i=0; i<len; i++)      /* Loop for all data characters */
        {
                *bufp++ = data[i];            /* Get a character */
                chksum += data[i];            /* Update checksum */
        }
        chksum = (((chksum&0300) >> 6)+chksum)&077; /* Compute final checksum */
        *bufp++ = tochar(chksum);       /* Put it in the packet */
        *bufp = eol;                /* Extra-packet line terminator */
        sauxstr( buffer,((int)bufp-(int)buffer+1)); /* Send the packet */
}
SHAR_EOF
cat << \SHAR_EOF > kertty.c
#include <stdio.h>
#include <ctype.h>
#include <osbind.h>
#include "ed.h"
#include "kermit.h"
 
#ifndef ESC
#define ESC 0x1b
#endif
#ifndef DEL
#define DEL 0x7f
#endif
 
extern  int *aline_addr; /* Ptr to base of Aline variables */
extern  int sav_row, sav_col;
 
typedef struct MACTAB {
        struct  MACTAB  *m_fmac;        /* next MACTAB  */
        short   *keymac;                /* pointer to the macro */
        char    key;                    /* the key      */
} MACTAB;
 
/* Do a direct read of the console. The present scheme does not always
 * work with a minimal NULL modem (i.e. only lines 1,2,&3 connected).
 */
readcon()
{
 
        register int c,i;
        extern MACTAB *mheadp, *mcralloc();
        register MACTAB *mt;
        char fname[16];
        extern char *getfname();
        extern short getktpcode();
 
        c=getkkey();    /* Read the keyboard and get mask */
        if (c == '`')   /* keyboard macro */
                {
                c = (*term.t_getchar)();
                if (c == '`')
                        {
                        sendaux('`');
                        return(TRUE);
                        }
                if (mheadp==NULL || mheadp->m_fmac == NULL)
                        {
                        mtwrite("No defineable macros assigned");
                        return(TRUE);
                        }
                if((mt=mcralloc(FALSE,c))!= (MACTAB *)NULL)
                        sendmacro(mt->keymac);
                }
        else if (shiftstatus == 12 && scancode == 0x30) /* CTRL-ALT-B */
                sendbrk();
        else if (shiftstatus == 12 && scancode == 0x2e) /* CTRL-ALT-C */
                {
                setterm(HOST);
                shell(FALSE,HUGE);
                setterm(REMOTE);
                }
        else if (shiftstatus == 17 || shiftstatus == 18) /* C-Lock + Shift */
                {
                i=tolower(c);
                sendaux(i);
                }
        else if (shiftstatus == 0 && scancode == 0x62) /* HELP Key */
                setterm(SHOWHELP);
        else if (shiftstatus == 8)      /* ALT key pressed */
                {
                switch(scancode)
                        {
                        case 0x12:      /* E */
                        /* save cursor position */
                                sav_row = aline_addr[-13];
                                sav_col = aline_addr[-14];
                                commfil(TRUE,TRUE);
                                ttopen();       /* update color */
                                (*term.t_move)(sav_row,sav_col);
                                return(TRUE);
                        case 0x61:      /* UNDO */
                                return(FALSE);
                        case 0x2e:      /* C */
                        /* save cursor position */
                                sav_row = aline_addr[-13];
                                sav_col = aline_addr[-14];
                                paintbuffer(TRUE,TRUE);
                                (*term.t_move)(sav_row,sav_col);
                                return(TRUE);
                        case 0x30:      /* B */
                                return(setbaud());
                        case 0x26:      /* L */
                                sav_row = aline_addr[-13];
                                sav_col = aline_addr[-14];
                                loadmac(TRUE);
                                (*term.t_move)(sav_row,sav_col);
                                return(TRUE);
                        case 0x18:      /* O */
                                /* Turn off flow control */
                                flow = NOFLO;
                                Rsconf(-1,flow,-1,-1,-1,-1);
                                mtwrite("[Handshaking disabled]");
                                return(TRUE);
                        case 0x13:      /* R */
                                flow = RTS;
                                Rsconf(-1,flow,-1,-1,-1,-1);
                                mtwrite("[RTS/CTS active]");
                                return(TRUE);
                        case 0x14:
                                showtime(TRUE,HUGE);
                                return(TRUE);
                        case 0x2d:      /* X */
                                flow = XON;
                                Rsconf(-1,flow,-1,-1,-1,-1);
                                mtwrite("[XON/XOFF active]");
                                return(TRUE);
                        case 0x35:      /* / ? */
                                setterm(SHOWHELP);
                                return(TRUE);
                        default:
                                sendaux(c);
                                return(TRUE);
                        }
                }
        else
                {
                if (c & SPEC)
                        if (getfname(c,fname)!=(char *)NULL)
                                c = (int)getktpcode(fname);
                if (c & META)
                        {
                        i = c & ~META;
                        sendaux(ESC);
                        c = i;
                        }
                if (c & CTLX)
                        {
                        i = c & ~CTLX;
                        sendaux(0x18);
                        c = i;
                        }
                if (c & CTRL)
                        {
                        i = c & ~CTRL;
                        c = i - '@';
                        }
                sendaux(c);
                }
        return(TRUE);
}
 
/* pratically identical to getkey in main.c but that one uses mlwrite
 * which would damage the terminal screen when it tried to update.
 */
getkkey()
{
        register int c;
        extern char keyscan[];
 
        c = (*term.t_getchar)();
#if     ST
        if (scancode == 0x70 && shiftstatus != 16)
                return (CTRL|'X');
#endif
        if (c == DEL)
                return(c);
        if (c == METACH) {                      /* Apply M- prefix      */
                mtwrite("Meta: ");
                c = (*term.t_getchar)();
                return (META | c);
        }
#if     ST
        /* use special keys or number pad (code >= 0x63) ?
         * 0x03 = scancode for ^@ (setmark).
         */
        if ((c == NULL && scancode != 0x03) || scancode >= 0x4a)
                if (scancode > 0x32)
                        {
                        /* if CapsLock and number pad, use real numbers */
                        if (shiftstatus == 16 && (scancode > 0x62 ||
                                scancode == 0x4a || scancode == 0x4e))
                                return(c);
                        else
                                return (SPEC | scancode);
                        }
                else    {
                        c = keyscan[scancode];
                        return (META | c);
                        }
#endif
        if (c>=0x00 && c<=0x1F)                 /* C0 control -> C-     */
                c = CTRL | (c+'@');
        return (c);
}
 
/* Allows keyboard macros to go through completely on a slow and noisy line */
 
sendmacro(buf)
register short *buf;
{
        register int c;
        char fname[16];
 
        while(*buf != (CTLX|')'))
                {
                while(!(int)Cauxos())   /* wait for line to clear */
                        ;
                if (*buf & SPEC)
                        if (getfname(*buf,fname)!=(char *)NULL)
                                *buf = (int)getktpcode(fname);
                if (*buf & META)
                        {
                        c = *buf & ~META;
                        sendaux(ESC);
                        *buf = c;
                        }
                if (*buf & CTLX)
                        {
                        c = *buf & ~CTLX;
                        sendaux(0x18);
                        *buf = c;
                        }
                if (*buf & CTRL)
                        {
                        c = *buf & ~CTRL;
                        *buf = c - '@';
                        }
                sendaux(*buf++);
                }
}
SHAR_EOF
#       End of shell archive
exit 0
%NONAME-W-NOMSG, Message number 00000000

RDROYA01@ULKYVX.BITNET.UUCP (02/05/87)

#       This is a shell archive.
#       Remove everything above and including the cut line.
#       Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar: Shell Archiver
#       Run the following text with /bin/sh to create:
#       display.c
# This archive created: Tue Feb  3 17:55:19 1987
cat << \SHAR_EOF > display.c
/*
 * The functions in this file
 * handle redisplay. There are two halves,
 * the ones that update the virtual display
 * screen, and the ones that make the physical
 * display screen the same as the virtual
 * display screen. These functions use hints
 * that are left in the windows by the
 * commands.
 */
#include        <stdio.h>
#include        <osbind.h>
#include        "ed.h"
 
#define WFDEBUG 0                       /* Window flag debug.           */
#define BELL    0x07
 
typedef struct  VIDEO {
        short   v_flag;                 /* Flags                        */
        char    v_text[];               /* Screen data.                 */
}       VIDEO;
 
#define VFCHG   0x0001                  /* Changed.                     */
#define VFEXT   0x0002                  /* extended (beyond column 80)  */
#define VFREV   0x0004                  /* reverse ?                    */
#define VFREQ   0x0008
int     sgarbf  = TRUE;                 /* TRUE if screen is garbage    */
int     mpresf  = FALSE;                /* TRUE if message in last line */
int     vtrow   = 0;                    /* Row location of SW cursor    */
int     vtcol   = 0;                    /* Column location of SW cursor */
int     ttrow   = HUGE;                 /* Row location of HW cursor    */
int     ttcol   = HUGE;                 /* Column location of HW cursor */
int     lbound  = 0;                    /* leftmost column of current ln*/
char    mlbuf[256];                     /* modeline buffer              */
VIDEO   **vscreen;                      /* Virtual screen.              */
VIDEO   **pscreen;                      /* Physical screen.             */
 
/*
 * Initialize the data structures used
 * by the display code. The edge vectors used
 * to access the screens are set up. The operating
 * system's terminal I/O channel is set up. All the
 * other things get initialized at compile time.
 * The original window has "WFCHG" set, so that it
 * will get completely redrawn on the first
 * call to "update".
 */
vtinit()
{
        register int    i;
        register VIDEO  *vp;
 
#if ST
        /* save current colors for later */
        savecolor();
#endif
        (*term.t_open)();
        vscreen = (VIDEO **) malloc(term.t_nrow*sizeof(VIDEO *));
        if (vscreen == NULL)
                abort();
        pscreen = (VIDEO **) malloc(term.t_nrow*sizeof(VIDEO *));
        if (pscreen == NULL)
                abort();
        for (i=0; i<term.t_nrow; ++i) {
                vp = (VIDEO *) malloc(sizeof(VIDEO)+term.t_ncol);
                if (vp == NULL)
                        abort();
                vp->v_flag = 0;
                vscreen[i] = vp;
                vp = (VIDEO *) malloc(sizeof(VIDEO)+term.t_ncol);
                if (vp == NULL)
                        abort();
                vp->v_flag = 0;
                pscreen[i] = vp;
        }
}
 
/*
 * Clean up the virtual terminal
 * system, in anticipation for a return to the
 * operating system. Move down to the last line and
 * clear it out (the next system prompt will be
 * written in the line). Shut down the channel
 * to the terminal.
 */
vttidy()
{
        mlerase();
        movecursor(term.t_nrow, 0);
        (*term.t_eeol)();
        (*term.t_close)();
}
 
/*
 * Set the virtual cursor to
 * the specified row and column on the
 * virtual screen. There is no checking for
 * nonsense values; this might be a good
 * idea during the early stages.
 */
vtmove(row, col)
register int row, col;
{
        vtrow = row;
        vtcol = col;
}
 
/*
 * Write a character to the
 * virtual screen. The virtual row and
 * column are updated. If the line is too
 * long put a "$" in the last column.
 * This routine only puts printing characters
 * into the virtual terminal buffers.
 * Only column overflow is checked.
 */
vtputc(c)
register int    c;
{
        register VIDEO  *vp;
 
        vp = vscreen[vtrow];
        if (vtcol >= term.t_ncol) {
                vtcol = (vtcol + 0x07) & ~0x07;
                vp->v_text[term.t_ncol-1] = '$';
        }
        else if (c == '\t') {
                do {
                        vtputc(' ');
                } while ((vtcol&0x07) != 0);
        } else if (c<0x20 || c==0x7F) {
                vtputc('^');
                vtputc(c ^ 0x40);
        } else
                vp->v_text[vtcol++] = c;
}
 
/*
 * put a character to the virtual screen in an extended line. If we are
 * not yet on left edge, don't print it yet. check for overflow on
 * the right margin.
 */
vtpute(c)
register int c;
{
        register VIDEO      *vp;
 
        vp = vscreen[vtrow];
        if (vtcol >= term.t_ncol) {
                vtcol = (vtcol + 0x07) & ~0x07;
                vp->v_text[term.t_ncol - 1] = '$';
        }
        else if (c == '\t') {
                do {
                        vtpute(' ');
                } while (((vtcol + lbound)&0x07) != 0);
        }
        else if (c < 0x20 || c == 0x7F) {
                vtpute('^');
                vtpute(c ^ 0x40);
        }
        else {
                if (vtcol >= 0)
                        vp->v_text[vtcol] = c;
                ++vtcol;
        }
}
 
/*
 * Erase from the end of the
 * software cursor to the end of the
 * line on which the software cursor is
 * located.
 */
vteeol()
{
        register VIDEO  *vp;
 
        vp = vscreen[vtrow];
        while (vtcol < term.t_ncol)
                vp->v_text[vtcol++] = ' ';
}
 
/*
 * Make sure that the display is
 * right. This is a three part process. First,
 * scan through all of the windows looking for dirty
 * ones. Check the framing, and refresh the screen.
 * Second, make sure that "currow" and "curcol" are
 * correct for the current window. Third, make the
 * virtual and physical screens the same.
 */
update()
{
        register LINE   *lp;
        register WINDOW *wp;
        register VIDEO  *vp1;
        register VIDEO  *vp2;
        register int    i;
        register int    j;
        register int    c;
 
#if     ST
        Bconout(2,0x1b);        /* hide cursor */
        Bconout(2,'f');
#endif
        for (i = 0; i < term.t_nrow; ++i)
                vscreen[i]->v_flag &= ~VFREQ;
#if     ST
        wp = wheadp;
        while (wp != NULL) {
                vscreen[wp->w_toprow+wp->w_ntrows]->v_flag |= VFREQ;
                wp = wp->w_wndp;
        }
#endif
        wp = wheadp;
        while (wp != NULL) {
                /* Look at any window with update flags set on.         */
                if (wp->w_flag != 0) {
                        /* If not force reframe, check the framing.     */
                        if ((wp->w_flag&WFFORCE) == 0) {
                                lp = wp->w_linep;
                                for (i=0; i<wp->w_ntrows; ++i) {
                                        if (lp == wp->w_dotp)
                                                goto out;
                                        if (lp == wp->w_bufp->b_linep)
                                                break;
                                        lp = lforw(lp);
                                }
                        }
                        /* Not acceptable, better compute a new value   */
                        /* for the line at the top of the window. Then  */
                        /* set the "WFHARD" flag to force full redraw.  */
                        i = wp->w_force;
                        if (i > 0) {
                                --i;
                                if (i >= wp->w_ntrows)
                                        i = wp->w_ntrows-1;
                        } else if (i < 0) {
                                i += wp->w_ntrows;
                                if (i < 0)
                                        i = 0;
                        } else
                                i = wp->w_ntrows/2;
                        lp = wp->w_dotp;
                        while (i!=0 && lback(lp)!=wp->w_bufp->b_linep) {
                                --i;
                                lp = lback(lp);
                        }
                        wp->w_linep = lp;
                        wp->w_flag |= WFHARD;   /* Force full.          */
                out:
                        /* Try to use reduced update. Mode line update  */
                        /* has its own special flag. The fast update is */
                        /* used if the only thing to do is within the   */
                        /* line editing.                                */
                        lp = wp->w_linep;
                        i  = wp->w_toprow;
                        if ((wp->w_flag&~WFMODE) == WFEDIT) {
                                while (lp != wp->w_dotp) {
                                        ++i;
                                        lp = lforw(lp);
                                }
                                vscreen[i]->v_flag |= VFCHG;
                                vtmove(i, 0);
                                for (j=0; j<llength(lp); ++j)
                                        vtputc(lgetc(lp, j));
                                vteeol();
                        } else if ((wp->w_flag&(WFEDIT|WFHARD)) != 0) {
                                while (i < wp->w_toprow+wp->w_ntrows) {
                                        vscreen[i]->v_flag |= VFCHG;
                                        vtmove(i, 0);
                                        if (lp != wp->w_bufp->b_linep) {
                                                for (j=0; j<llength(lp); ++j)
                                                        vtputc(lgetc(lp, j));
                                                lp = lforw(lp);
                                        }
                                        vteeol();
                                        ++i;
                                }
                        }
                        if ((wp->w_flag&WFMODE) != 0)
                                modeline(wp);
                        wp->w_flag  = 0;
                        wp->w_force = 0;
                }
/*
#if     WFDEBUG
                modeline(wp);
                wp->w_flag =  0;
                wp->w_force = 0;
#endif
*/
                wp = wp->w_wndp;
        }
        /* Always recompute the row and column number of the hardware   */
        /* cursor. This is the only update for simple moves.            */
        lp = curwp->w_linep;
        currow = curwp->w_toprow;
        while (lp != curwp->w_dotp) {
                ++currow;
                lp = lforw(lp);
        }
        curcol = 0;
        i = 0;
        while (i < curwp->w_doto) {
                c = lgetc(lp, i++);
                if (c == '\t')
                        curcol |= 0x07;
                else if (c<0x20 || c==0x7F)
                        ++curcol;
                ++curcol;
        }
        if (curcol >= term.t_ncol-1) {          /* Extended line.       */
                /* flag we are extended and changed */
                vscreen[currow]->v_flag |= VFEXT | VFCHG;
                updext();                       /* and output extended line */
        } else
                lbound = 0;                     /* not extended line */
        /* make sure no lines need to be de-extended because the cursor is
         * no longer on them
         */
        wp = wheadp;
        while (wp != NULL) {
                lp = wp->w_linep;
                i = wp->w_toprow;
                while (i < wp->w_toprow + wp->w_ntrows) {
                        if (vscreen[i]->v_flag & VFEXT) {
                                /* always flag extended lines as changed */
                                vscreen[i]->v_flag |= VFCHG;
                                if ((wp != curwp) || (lp != wp->w_dotp) ||
                                (curcol < term.t_ncol - 1)) {
                                        vtmove(i, 0);
                                        for (j = 0; j < llength(lp); ++j)
                                                vtputc(lgetc(lp, j));
                                        vteeol();
                                        /* this line no longer is extended */
                                        vscreen[i]->v_flag &= ~VFEXT;
                                }
                        }
                lp = lforw(lp);
                ++i;
                }
        /* and onward to the next window */
        wp = wp->w_wndp;
        }
        /* Special hacking if the screen is garbage. Clear the hardware */
        /* screen, and update your copy to agree with it. Set all the   */
        /* virtual screen change bits, to force a full update.          */
        if (sgarbf != FALSE) {
                for (i=0; i<term.t_nrow; ++i) {
                        vscreen[i]->v_flag |= VFCHG;
                        vp1 = pscreen[i];
                        for (j=0; j<term.t_ncol; ++j)
                                vp1->v_text[j] = ' ';
                }
                movecursor(0, 0);               /* Erase the screen.    */
                (*term.t_eeop)();
                sgarbf = FALSE;                 /* Erase-page clears    */
                mpresf = FALSE;                 /* the message area.    */
        }
        /* Make sure that the physical and virtual displays agree.      */
        /* Unlike before, the "updateline" code is only called with a   */
        /* line that has been updated for sure.                         */
        for (i=0; i<term.t_nrow; ++i) {
                vp1 = vscreen[i];
                j = vp1->v_flag;
                if (((j & VFCHG) != 0) || (((j & VFREV) == 0) !=
                        ((j & VFREQ) ==0)))
                        {
                        vp2 = pscreen[i];
                        updateline(i, &vp1->v_text[0], &vp2->v_text[0],
                        &vp1->v_flag);
                }
        }
        /* Finally, update the hardware cursor and flush out buffers.   */
        movecursor(currow, curcol-lbound);
        (*term.t_flush)();
#if     ST
        Bconout(2,0x1b);        /* enable cursor */
        Bconout(2,'e');
#endif
}
 
/*
 * updext: update the extended line which the cursor is currently
 * on at a column greater than the terminal width. The line
 * will be scrolled right or left to let the user see where
 * the cursor is
 */
updext()
{
        register int rcursor;   /* real cursor location */
        register LINE *lp;      /* pointer to current line */
        register int j;  /* index into line */
 
        /* calculate what column the real cursor will end up in */
        rcursor = ((curcol - term.t_ncol) % term.t_scrsiz) + term.t_margin;
        lbound = curcol - rcursor + 1;
        /* scan through the line outputing characters to the virtual screen */
        /* once we reach the left edge    */
        vtmove(currow, -lbound);        /* start scanning offscreen */
        lp = curwp->w_dotp;          /* line to output */
        for (j=0; j<llength(lp); ++j)   /* until the end-of-line */
                vtpute(lgetc(lp, j));
        /* truncate the virtual line */
        vteeol();
        /* and put a '$' in column 1 */
        vscreen[currow]->v_text[0] = '$';
}
 
/*
 * Update a single line. This
 * does not know how to use insert
 * or delete character sequences; we are
 * using VT52 functionality. Update the physical
 * row and column variables. It does try an
 * exploit erase to end of line. The RAINBOW version
 * of this routine uses fast video.
 */
updateline(row, vline, pline, flags)
int row;
char    vline[];
char    pline[];
short   *flags;
{
#if     RAINBOW
        register char   *cp1;
        register char   *cp2;
        register int    nch;
 
        cp1 = &vline[0];                        /* Use fast video.      */
        cp2 = &pline[0];
        putline(row+1, 1, cp1);
        nch = term.t_ncol;
        do {
                *cp2 = *cp1;
                ++cp2;
                ++cp1;
        } while (--nch);
#else
        register char   *cp1;
        register char   *cp2;
        register char   *cp3;
        register char   *cp4;
        register char   *cp5;
        register int    nbflag;
        int     rev;
        int     req;
 
        cp1 = &vline[0];                        /* Compute left match.  */
        cp2 = &pline[0];
#if     ST
        rev = *flags & VFREV;
        req = *flags & VFREQ;
        if (rev != req) {
                movecursor(row, 0);     /* Go to start of line. */
                if (req)
                        {
                        Crawio(0x1b);
                        Crawio('p');
                        }
                else
                        {
                        Crawio(0x1b);
                        Crawio('q');
                        }
/*              (*term.t_rev)(req != FALSE);*/
                /* scan through the line and dump it to the screen and
                 * the virtual screen array
                 */
                cp3 = &vline[term.t_ncol];
                while (cp1 < cp3) {
                        (*term.t_putchar)(*cp1);
                        ++ttcol;
                        *cp2++ = *cp1++;
                }
                Crawio(0x1b);
                Crawio('q');            /* turn on reverse video */
                /* update the needed flags */
                *flags &= ~VFCHG;
                if (req)
                        *flags |= VFREV;
                else
                        *flags &= ~VFREV;
                return(TRUE);
        }
#endif
        while (cp1!=&vline[term.t_ncol] && cp1[0]==cp2[0]) {
                ++cp1;
                ++cp2;
        }
        /* This can still happen, even though we only call this routine */
        /* on changed lines. A hard update is always done when a line   */
        /* splits, a massive change is done, or a buffer is displayed   */
        /* twice. This optimizes out most of the excess updating. A lot */
        /* of computes are used, but these tend to be hard operations   */
        /* that do a lot of update, so I don't really care.             */
        if (cp1 == &vline[term.t_ncol])         /* All equal.           */
                return;
        nbflag = FALSE;
        cp3 = &vline[term.t_ncol];              /* Compute right match. */
        cp4 = &pline[term.t_ncol];
        while (cp3[-1] == cp4[-1]) {
                --cp3;
                --cp4;
                if (cp3[0] != ' ')              /* Note if any nonblank */
                        nbflag = TRUE;          /* in right match.      */
        }
        cp5 = cp3;
        if (nbflag == FALSE) {                  /* Erase to EOL ?       */
                while (cp5!=cp1 && cp5[-1]==' ')
                        --cp5;
                if ( ((int)cp3-(int)cp5) <= 3)  /* Use only if erase is */
                        cp5 = cp3;              /* fewer characters.    */
        }
        movecursor(row, (int)cp1-(int)&vline[0]);/* Go to start of line.*/
        while (cp1 != cp5) {                    /* Ordinary.            */
                (*term.t_putchar)(*cp1);
                ++ttcol;
                *cp2++ = *cp1++;
        }
        if (cp5 != cp3) {                       /* Erase.               */
                (*term.t_eeol)();
                while (cp1 != cp3)
                        *cp2++ = *cp1++;
        }
        *flags &= ~VFCHG;
#endif
}
 
/*
 * Redisplay the mode line for
 * the window pointed to by the "wp".
 * This is the only routine that has any idea
 * of how the modeline is formatted. You can
 * change the modeline format by hacking at
 * this routine. Called by "update" any time
 * there is a dirty window.
 */
modeline(wp)
register WINDOW *wp;
{
        register char   *cp;
        register int    c;
        register int    i;
        register int    n;
        register BUFFER *bp;
        register int    lchar;
        char            tline[82];
 
        n = wp->w_toprow+wp->w_ntrows;          /* Location.            */
        vscreen[n]->v_flag |= VFCHG;            /* Redraw next time.    */
        vtmove(n,0);
        if (wp == curwp)
                lchar = '=';
        else
                lchar = '-';
 
        vtputc(lchar);
        bp = wp->w_bufp;
        if ((bp->b_flag&BFCHG) != 0)            /* "*" if changed.      */
                vtputc('*');
        else
                vtputc(lchar);
        n  = 2;
#if     ST
        strcpy(tline," Uemail ST 3.3 (");       /* Buffer name.         */
#else
        strcpy(tline, "Uemail 3.3 (");
#endif
        /* display the modes */
        if (wp->w_bufp->b_bmode&BMCMODE)
                strcat(tline, "C");
        else if (wp->w_bufp->b_bmode&BMWRAP)
                strcat(tline, "Wrap");
        else
                strcat(tline, "Fundamental");
        strcat(tline,") ");
 
        cp = &tline[0];
        while ((c = *cp++) != 0) {
                vtputc(c);
                ++n;
        }
        vtputc(lchar);
        vtputc(lchar);
        vtputc(' ');
        n += 3;
        cp = &bp->b_bname[0];
        while ((c = *cp++) != 0) {
                vtputc(c);
                ++n;
        }
        vtputc(' ');
        vtputc(lchar);
        vtputc(lchar);
        n += 3;
        if (bp->b_fname[0] != 0) {              /* File name.           */
                cp = " File: ";
                while ((c = *cp++) != 0) {
                        vtputc(c);
                        ++n;
                }
                cp = &bp->b_fname[0];
                while ((c = *cp++) != 0) {
                        vtputc(c);
                        ++n;
                }
                vtputc(' ');
                ++n;
        }
#if     WFDEBUG
        vtputc(lchar);
        vtputc((wp->w_flag&WFMODE)!=0  ? 'M' : '-');
        vtputc((wp->w_flag&WFHARD)!=0  ? 'H' : '-');
        vtputc((wp->w_flag&WFEDIT)!=0  ? 'E' : '-');
        vtputc((wp->w_flag&WFMOVE)!=0  ? 'V' : '-');
        vtputc((wp->w_flag&WFFORCE)!=0 ? 'F' : '-');
        n += 6;
#endif
        while (n < term.t_ncol) {               /* Pad to full width.   */
                vtputc(lchar);
                ++n;
        }
}
 
upmode()        /* update all the mode lines */
{
        register WINDOW *wp;
 
        wp = wheadp;
        while (wp != NULL) {
                wp->w_flag |= WFMODE;
                wp = wp->w_wndp;
        }
}
 
/*
 * Send a command to the terminal
 * to move the hardware cursor to row "row"
 * and column "col". The row and column arguments
 * are origin 0. Optimize out random calls.
 * Update "ttrow" and "ttcol".
 */
movecursor(row, col)
register int row, col;
{
        if (row!=ttrow || col!=ttcol) {
                ttrow = row;
                ttcol = col;
                (*term.t_move)(row, col);
        }
}
 
/*
 * Erase the message line.
 * This is a special routine because
 * the message line is not considered to be
 * part of the virtual screen. It always works
 * immediately; the terminal buffer is flushed
 * via a call to the flusher (not on ST).
 */
mlerase()
{
        movecursor(term.t_nrow, 0);
#if     ST
        Bconout(2,0x1b);        /* ST Kill entire line command */
        Bconout(2,'l');
#else
        (*term.t_eeol)();
        (*term.t_flush)();
#endif
        mpresf = FALSE;
}
 
/*
 * Ask a yes or no question in
 * the message line. Return either TRUE,
 * FALSE, or ABORT. The ABORT status is returned
 * if the user bumps out of the question with
 * a ^G. Used any time a confirmation is
 * required.
 */
mlyesno(prompt)
char    *prompt;
{
        register int    c;
        char            buf[64];
 
        for (;;) {
                strcpy(buf, prompt);
                strcat(buf, " [y/n]? ");
                mlwrite(buf);
                /* get the response */
                c = (*term.t_getchar)();
 
                if (c == BELL)    /* Bail out! */
                        return(ABORT);
 
                if (c=='y' || c=='Y')
                        return(TRUE);
 
                if (c=='n' || c=='N')
                        return(FALSE);
        }
}
 
/*
 * Write a prompt into the message
 * line, then read back a response. Keep
 * track of the physical position of the cursor.
 * If we are in a keyboard macro throw the prompt
 * away, and return the remembered response. This
 * lets macros run at full speed. The reply is
 * always terminated by a carriage return. Handle
 * erase, kill, and abort keys.
 */
mlreply(prompt, buf, nbuf)
char    *prompt;
char    *buf;
int nbuf;
{
        return(mlreplt(prompt,buf,nbuf,'\n'));
}
 
/* A more generalized prompt/reply function allowing the caller
 * to specify the proper terminator. If the terminator is not
 * a return ('\n') it will echo as "<NL>"
 */
mlreplt(prompt, buf, nbuf, eolchar)
char *prompt;
char *buf;
int nbuf;
char eolchar;
{
        register int    cpos;
        register int    i;
        register int    c;
 
        cpos = 0;
        if (kbdmop != NULL) {
                while ((c = *kbdmop++) != '\0')
                        buf[cpos++] = c;
                buf[cpos] = 0;
                if (buf[0] == 0)
                        return (FALSE);
                return (TRUE);
        }
#if     ST
        (*term.t_move)(term.t_nrow, 0); /* in case we're in terminal mode */
#endif
        mlwrite(prompt);
        for (;;) {
        /* get a character from the user. if it is a <ret>, change it
         * to a <NL>
         */
                c = (*term.t_getchar)();
                if (c == 0x0d)
                        c = '\n';
                if (c == eolchar) {
                        buf[cpos++] = 0;
                        if (kbdmip != NULL) {
                                if (kbdmip+cpos > &kbdm[NKBDM-3]) {
                                        ctrlg(FALSE, 0);
                                        (*term.t_flush)();
                                        return(ABORT);
                                }
                                for (i=0; i<cpos; ++i)
                                        *kbdmip++ = buf[i];
                        }
                        (*term.t_putchar)('\r');
                        ttcol = 0;
                        (*term.t_flush)();
                        if (buf[0] == 0)
                                return(FALSE);
                        return(TRUE);
                } else if (c == 0x07) { /* Bell, abort */
                        (*term.t_putchar)('^');
                        (*term.t_putchar)('G');
                        ttcol += 2;
                        ctrlg(FALSE, 0);
                        (*term.t_flush)();
                        return(ABORT);
                } else if (c == 0x7F || c == 0x08 || c == 0x02) {
                        /* rubout/erase/cursor left */
                        if (cpos != 0) {
                                (*term.t_putchar)('\b');
                                (*term.t_putchar)(' ');
                                (*term.t_putchar)('\b');
                                --ttcol;
                                if (buf[--cpos] < 0x20) {
                                        (*term.t_putchar)('\b');
                                        (*term.t_putchar)(' ');
                                        (*term.t_putchar)('\b');
                                        --ttcol;
                                }
                                if (buf[cpos] == '\n') {
                                        (*term.t_putchar)('\b');
                                        (*term.t_putchar)('\b');
                                        (*term.t_putchar)(' ');
                                        (*term.t_putchar)(' ');
                                        (*term.t_putchar)('\b');
                                        (*term.t_putchar)('\b');
                                        --ttcol;
                                        --ttcol;
                                }
                                (*term.t_flush)();
                        }
                } else if (c == 0x15) { /* C-U, kill */
                        while (cpos != 0) {
                                (*term.t_putchar)('\b');
                                (*term.t_putchar)(' ');
                                (*term.t_putchar)('\b');
                                --ttcol;
                                if (buf[--cpos] < 0x20) {
                                        (*term.t_putchar)('\b');
                                        (*term.t_putchar)(' ');
                                        (*term.t_putchar)('\b');
                                        --ttcol;
                                }
                        }
                        (*term.t_flush)();
                } else {
                        if (cpos < nbuf-1) {
                                buf[cpos++] = c;
                                if ((c < ' ') && (c != '\n')) {
                                        (*term.t_putchar)('^');
                                        ++ttcol;
                                        c ^= 0x40;
                                }
                                if (c != '\n')
                                        (*term.t_putchar)(c);
                                else {  /* put out <NL> for <ret> */
                                        (*term.t_putchar)('<');
                                        (*term.t_putchar)('N');
                                        (*term.t_putchar)('L');
                                        (*term.t_putchar)('>');
                                        ttcol += 3;
                                }
                        ++ttcol;
                        (*term.t_flush)();
                        }
                }
        }
}
 
/*
 * Write a message into the message
 * line. Keep track of the physical cursor
 * position. A small class of printf like format
 * items is handled. Assumes the stack grows
 * down; this assumption is made by the "++"
 * in the argument scan loop. Set the "message
 * line" flag TRUE.  For the ST this function
 * uses sprintf() to get at ULONG and FLOAT
 * arguments.  I couldn't get *VARARGS* to work
 * so I kludged with the 10 long args.  Is this
 * worth it?
 */
mlwrite(fmt, arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9,arg10)
char    *fmt;
#if     ST
long    arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9,arg10;
{
        /* Use the built-in functions on the ST.
         * This lets us get to unsigned longs and floats
         * but limits the number of arguments to 10.
         */
        mlerase();
        mpresf = TRUE;
        sprintf(mlbuf,fmt,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9,arg10);
        ttputc(0x1b); ttputc('f');      /* hide cursor */
        Cconws(mlbuf);
        (*term.t_eeol)();
        ttputc(0x1b); ttputc('e');      /* show cursor */
}
#else
int arg;
{
        register int    c;
        register char   *ap;
 
        movecursor(term.t_nrow, 0);
        ap = (char *) &arg;
        while ((c = *fmt++) != 0) {
                if (c != '%') {
                        (*term.t_putchar)(c);
                        ++ttcol;
                } else {
                        c = *fmt++;
                        switch (c) {
                        case 'd':
                                mlputi(*(int *)ap, 10);
                                ap += sizeof(int);
                                break;
 
                        case 'o':
                                mlputi(*(int *)ap,  8);
                                ap += sizeof(int);
                                break;
 
                        case 'x':
                                mlputi(*(int *)ap, 16);
                                ap += sizeof(int);
                                break;
 
                        case 'D':
                                mlputli(*(long *)ap, 10);
                                ap += sizeof(long);
                                break;
 
                        case 's':
                                mlputs(*(char **)ap);
                                ap += sizeof(char *);
                                break;
 
                        default:
                                (*term.t_putchar)(c);
                                ++ttcol;
                        }
                }
        }
        (*term.t_eeol)();
        (*term.t_flush)();
        mpresf = TRUE;
}
 
/*
 * Write out a string.
 * Update the physical cursor position.
 * This assumes that the characters in the
 * string all have width "1"; if this is
 * not the case things will get screwed up
 * a little.
 */
mlputs(s)
register char   *s;
{
        register int    c;
 
        while ((c = *s++) != 0) {
                (*term.t_putchar)(c);
                ++ttcol;
        }
}
 
/*
 * Write out an integer, in
 * the specified radix. Update the physical
 * cursor position. This will not handle any
 * negative numbers; maybe it should.
 */
mlputi(i, r)
int i, r;
{
        register int    q;
        static char hexdigits[] = "0123456789ABCDEF";
 
        if (i < 0) {
                i = -i;
                (*term.t_putchar)('-');
        }
        q = i/r;
        if (q != 0)
                mlputi(q, r);
        (*term.t_putchar)(hexdigits[i%r]);
        ++ttcol;
}
 
/*
 * do the same except as a long integer.
 */
mlputli(l, r)
long l;
int r;
{
        register long q;
 
        if (l < 0) {
                l = -l;
                (*term.t_putchar)('-');
        }
        q = l/r;
        if (q != 0)
                mlputli(q, r);
        (*term.t_putchar)((int)(l%r)+'0');
        ++ttcol;
}
#endif
SHAR_EOF
#       End of shell archive
exit 0
%NONAME-W-NOMSG, Message number 00000000

RDROYA01@ULKYVX.BITNET.UUCP (02/05/87)

#       This is a shell archive.
#       Remove everything above and including the cut line.
#       Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar: Shell Archiver
#       Run the following text with /bin/sh to create:
#       random.c
#       misc.c
#       macros.c
# This archive created: Tue Feb  3 17:52:51 1987
cat << \SHAR_EOF > random.c
/*
 * This file contains the
 * command processing functions for
 * a number of random commands. There is
 * no functional grouping here, for
 * sure.
 */
#include        <stdio.h>
#include        "ed.h"
 
int     tabsize;                        /* Tab size (0: use real tabs)  */
char    fillprefix[80];                 /* rather large but who knows   */
int     yankcflag = FALSE;
 
/*
 * Set fill column to n.  The fill column is global, but the individual
 * buffer can be filled or not depending on its mode.
 */
setfillcol(f, n)
register int f, n;
{
        register WINDOW *wp;
        register BUFFER *bp;
 
        fillcol = (n == 1) ? (getccol(FALSE) + 1) : n;
        n = fillcol;
        wp =  wheadp;
        bp =  curwp->w_bufp;
        if (n > 1)
                {
                bp->b_bmode |= BMWRAP;
                bp->b_bmode &= ~BMNWRAP;
                }
        else
                {
                bp->b_bmode |= BMNWRAP;
                bp->b_bmode &= ~BMWRAP;
                }
        upmode();
        mlwrite("Fill column is %d", n);
        return(TRUE);
}
 
/*
 * Set indent column to n or set fill prefix to string.
 */
setindcol(f, n)
register int f, n;
{
        short curdo;    /* current dot offset */
        register short tdo;     /* moveable dot offset */
        register short i;
        char tstring[80];       /* temporary store for prefix string */
        i = 0;
        tdo = 0;
        curdo = curwp->w_doto;
 
        if (n>1)        /* called with argument, so just set the indcol */
                {
                if (n >= 80)
                        n=79;
                indcol = n;
                /* copy the correct number of tabs and spaces into string */
                if ((tdo=indcol/8)!=0)
                        while (tdo--)
                                tstring[i++] = '\t';
                if ((tdo=indcol%8)!=0)
                        while (tdo--)
                                tstring[i++] =  ' ';
                tstring[i] = '\0';
                strncpy(fillprefix,tstring,79);
                mlwrite("Indent column is %d", indcol);
                return(TRUE);
                }
        indcol = getccol(FALSE) +1;
        /* now fabricate fill prefix string */
        do {
                curwp->w_doto = tdo;
                tstring[tdo] = lgetc(curwp->w_dotp,tdo);
        } while (++tdo < curdo && tdo < 80);
        tstring[curdo] = '\0';
        curwp->w_doto = curdo;
        strncpy(fillprefix,tstring,79);
        mlwrite("Fill prefix is '%s'",fillprefix);
        return(TRUE);
}
 
/*
 * Display the current position of the cursor,
 * in origin 1 X-Y coordinates, the character that is
 * under the cursor (in hex), and the fraction of the
 * text that is before the cursor. The displayed column
 * is not the current column, but the column that would
 * be used on an infinite width display. Normally this
 * is bound to "C-X =".
 */
showcpos(f,n)
int f, n;
{
        register LINE   *clp;
        register long   nch;
        register long   nbc;
        register long   nmc;
        register int    cbo;
        register int    cac;
        register int    page;
        register int    col;
        register int    i;
        register int    row;
        register long   tl1;
        float    ratio, tf1, tf2;
        char     buf[12], *ftoa();
 
        i = 0;
        clp = lforw(curbp->b_linep);            /* Grovel the data.     */
        cbo = 0;
        nch = 0;
        for (;;) {
                if (clp==curwp->w_markp && cbo==curwp->w_marko)
                        nmc = nch;
                if (clp==curwp->w_dotp && cbo==curwp->w_doto) {
                        nbc = nch;
                        row = i;
                        if (cbo == llength(clp))
                                cac = '\n';
                        else
                                cac = lgetc(clp, cbo);
                }
                if (cbo == llength(clp)) {
                        if (clp == curbp->b_linep)
                                break;
                        clp = lforw(clp);
                        ++i;
                        cbo = 0;
                } else
                        ++cbo;
                ++nch;
        }
        col = getccol(FALSE) + 1;               /* Get real column.     */
        if (nch != 0L)
                {
                tl1 = nbc * 100L;
                tf1 = (float)tl1;
                tf2 = (float)nch;
                ratio = tf1/tf2;
                }
        (void)ftoa(ratio,buf,4);
        buf[strlen(buf)-2] = '\0';      /* fool precision error */
        if (curwp->w_dotp == curbp->b_linep)
                row = i;
        page = row / pagelen + 1;
mlwrite("page:=%d col:=%d line:=%d CH:=0x%x mark:=%D point:=%D (%s%% of %D)",
                 page, col, row, cac, nmc, nbc, buf, nch);
        return (TRUE);
}
 
/*
 * Return current column.  Stop at first non-blank given TRUE argument.
 */
getccol(bflg)
int bflg;
{
        register int c, i, col;
        col = 0;
        for (i=0; i<curwp->w_doto; ++i) {
                c = lgetc(curwp->w_dotp, i);
                if (c!=' ' && c!='\t' && bflg)
                        break;
                if (c == '\t')
                        col |= 0x07;
                else if (c<0x20 || c==0x7F)
                        ++col;
                ++col;
        }
        return(col);
}
 
/*
 * Twiddle the two characters on either side of
 * dot. If dot is at the end of the line twiddle the
 * two characters before it. Return with an error if dot
 * is at the beginning of line; it seems to be a bit
 * pointless to make this work. This fixes up a very
 * common typo with a single stroke. Normally bound
 * to "C-T". This always works within a line, so
 * "WFEDIT" is good enough.
 */
twiddle(f, n)
register int f, n;
{
        register LINE   *dotp;
        register int    doto;
        register int    cl;
        register int    cr;
 
        dotp = curwp->w_dotp;
        doto = curwp->w_doto;
        if (doto==llength(dotp) && --doto<0)
                return (FALSE);
        cr = lgetc(dotp, doto);
        if (--doto < 0)
                return (FALSE);
        cl = lgetc(dotp, doto);
        lputc(dotp, doto+0, cr);
        lputc(dotp, doto+1, cl);
        lchange(WFEDIT);
        return (TRUE);
}
 
/*
 * Quote the next character, and
 * insert it into the buffer. All the characters
 * are taken literally, with the exception of the newline,
 * which always has its line splitting meaning. The character
 * is always read, even if it is inserted 0 times, for
 * regularity. Bound to "M-Q" (for me) and "C-Q" (for Rich,
 * and only on terminals that don't need XON-XOFF).
 */
quote(f, n)
register int f, n;
{
        register int    s;
        register int    c;
 
        c = (*term.t_getchar)();
        if (n < 0)
                return (FALSE);
        if (n == 0)
                return (TRUE);
        if (c == '\n') {
                do {
                        s = lnewline();
                } while (s==TRUE && --n);
                return (s);
        }
        return (linsert(n, c));
}
 
/*
 * Set tab size if given non-default argument (n <> 1).  Otherwise, insert a
 * tab into file.  If given argument, n, of zero, change to true tabs.
 * If n > 1, simulate tab stop every n-characters using spaces.
 * This has to be done in this slightly funny way because the
 * tab (in ASCII) has been turned into "C-I" (in 10
 * bit code) already. Bound to "C-I".
 */
tab(f, n)
register int f, n;
{
        if (n < 0)
                return (FALSE);
        if (n == 0 || n > 1) {
                tabsize = n;
                return(TRUE);
        }
        if (! tabsize)
                return(linsert(1, '\t'));
        return(linsert(tabsize - (getccol(FALSE) % tabsize), ' '));
}
 
/*
 * Open up some blank space. The basic plan
 * is to insert a bunch of newlines, and then back
 * up over them. Everything is done by the subcommand
 * processors. They even handle the looping. Normally
 * this is bound to "C-O".
 */
openline(f, n)
register int f, n;
{
        register int    i;
        register int    s;
 
        if (n < 0)
                return (FALSE);
        if (n == 0)
                return (TRUE);
        i = n;                                  /* Insert newlines.     */
        do {
                s = lnewline();
        } while (s==TRUE && --i);
        if (s == TRUE)                          /* Then back up overtop */
                s = backchar(f, n);             /* of them all.         */
        return (s);
}
 
/*
 * Insert a newline. Bound to "C-M".
 * If you are at the end of the line and the
 * next line is a blank line, just move into the
 * blank line. This makes "C-O" and "C-X C-O" work
 * nicely, and reduces the ammount of screen
 * update that has to be done. This would not be
 * as critical if screen update were a lot
 * more efficient.
 */
newline(f, n)
register int f, n;
{
        register LINE   *lp;
        register int    s;
        extern unsigned char insflag;
 
        if (n < 0)
                return (FALSE);
        if (yankcflag == TRUE || insflag == TRUE)
                goto around;
        if (n == 1 && (curbp->b_bmode&BMCMODE) != 0
                && curwp->w_dotp != curbp->b_linep)
                return(indent(TRUE,n));
        if (indcol > 0)
                return(indent(MAYBE,1));
around: while (n--) {
                lp = curwp->w_dotp;
                if (llength(lp) == curwp->w_doto
                && lp != curbp->b_linep
                && llength(lforw(lp)) == 0) {
                        if ((s=forwchar(FALSE, 1)) != TRUE)
                                return (s);
                } else if ((s=lnewline()) != TRUE)
                        return (s);
        }
        return (TRUE);
}
 
/*
 * Delete blank lines around dot.
 * What this command does depends if dot is
 * sitting on a blank line. If dot is sitting on a
 * blank line, this command deletes all the blank lines
 * above and below the current line. If it is sitting
 * on a non blank line then it deletes all of the
 * blank lines after the line. Normally this command
 * is bound to "C-X C-O". Any argument is ignored.
 */
deblank(f, n)
register int f, n;
{
        register LINE   *lp1;
        register LINE   *lp2;
        register int    nld;
 
        lp1 = curwp->w_dotp;
        while (llength(lp1)==0 && (lp2=lback(lp1))!=curbp->b_linep)
                lp1 = lp2;
        lp2 = lp1;
        nld = 0;
        while ((lp2=lforw(lp2))!=curbp->b_linep && llength(lp2)==0)
                ++nld;
        if (nld == 0)
                return (TRUE);
        curwp->w_dotp = lforw(lp1);
        curwp->w_doto = 0;
        return (ldelete(nld));
}
 
/*
 * Insert a newline, then enough
 * tabs and spaces to duplicate the indentation
 * of the previous line. Assumes tabs are every eight
 * characters. Quite simple. Figure out the indentation
 * of the current line. Insert a newline by calling
 * the standard routine. Insert the indentation by
 * inserting the right number of tabs and spaces.
 * Return TRUE if all ok. Return FALSE if one
 * of the subcomands failed. Normally bound
 * to "C-J".
 */
indent(f, n)
register int f, n;
{
        register int    nicol;
        register int    c;
        register int    i;
 
        if (n < 0)
                return (FALSE);
        if (f == MAYBE) /* This call is to fill a paragraph or wrap an */
                {       /* indented line */
                if (lnewline() == FALSE || putlin(fillprefix) == FALSE)
                        return(FALSE);
                return(TRUE);
                }
        while (n--) {
                nicol = 0;
                for (i=0; i<llength(curwp->w_dotp); ++i) {
                        c = lgetc(curwp->w_dotp, i);
                        if (c!=' ' && c!='\t')
                                break;
                        if (c == '\t')
                                nicol |= 0x07;
                        ++nicol;
                }
        if (lnewline() == FALSE
                || ((i=nicol/8)!=0 && linsert(i, '\t')==FALSE)
                || ((i=nicol%8)!=0 && linsert(i,  ' ')==FALSE))
                        return (FALSE);
        }
        return (TRUE);
}
 
/* MDROPLN : META command to drop the current line and indent the same
 * as the current line.  Bound to M-O.
 */
 
mdropln(f, n)
register int f, n;
{
        register int    nicol;
        register int    c;
        register int    i;
 
        if (n < 0)
                return (FALSE);
        curwp->w_doto = 0;
        while (n--) {
                nicol = 0;
                for (i=0; i<llength(curwp->w_dotp); ++i) {
                        c = lgetc(curwp->w_dotp, i);
                        if (c!=' ' && c!='\t')
                                break;
                        if (c == '\t')
                                nicol |= 0x07;
                        ++nicol;
                }
                if (openline(NULL, 1) == FALSE
                || ((i=nicol/8)!=0 && linsert(i, '\t')==FALSE)
                || ((i=nicol%8)!=0 && linsert(i,  ' ')==FALSE))
                        return (FALSE);
        }
        return (TRUE);
}
 
/*
 * Delete forward. This is real
 * easy, because the basic delete routine does
 * all of the work. Watches for negative arguments,
 * and does the right thing. If any argument is
 * present, it kills rather than deletes, to prevent
 * loss of text if typed with a big argument.
 * Normally bound to "C-D".
 */
forwdel(f, n)
register int f, n;
{
        if (n < 0)
                return (backdel(f, -n));
        if (f != FALSE) {                       /* Really a kill.       */
                if ((lastflag&CFKILL) == 0)
                        kdelete();
                thisflag |= CFKILL;
        }
        return (ldelete(n, f));
}
 
/*
 * Delete backwards. This is quite easy too,
 * because it's all done with other functions. Just
 * move the cursor back, and delete forwards.
 * Like delete forward, this actually does a kill
 * if presented with an argument. Bound to both
 * "RUBOUT" and "C-H".
 */
backdel(f, n)
register int f, n;
{
        register int    s;
 
        if (n < 0)
                return (forwdel(f, -n));
        if (f != FALSE) {                       /* Really a kill.       */
                if ((lastflag&CFKILL) == 0)
                        kdelete();
                thisflag |= CFKILL;
        }
        if ((s=backchar(f, n)) == TRUE)
                s = ldelete(n, f);
        return (s);
}
 
/*
 * Kill text. If called without an argument,
 * it kills from dot to the end of the line, unless it
 * is at the end of the line, when it kills the newline.
 * If called with an argument of 0, it kills from the
 * start of the line to dot. If called with a positive
 * argument, it kills from dot forward over that number
 * of newlines. If called with a negative argument it
 * kills backwards that number of newlines. Normally
 * bound to "C-K".
 */
kill(f, n)
register int f, n;
{
        register int    chunk;
        register LINE   *nextp;
 
        if ((lastflag&CFKILL) == 0)             /* Clear kill buffer if */
                kdelete();                      /* last wasn't a kill.  */
        thisflag |= CFKILL;
        if (f == FALSE) {
                chunk = llength(curwp->w_dotp)-curwp->w_doto;
                if (chunk == 0)
                        chunk = 1;
        } else if (n == 0) {
                chunk = curwp->w_doto;
                curwp->w_doto = 0;
        } else if (n > 0) {
                chunk = llength(curwp->w_dotp)-curwp->w_doto+1;
                nextp = lforw(curwp->w_dotp);
                while (--n) {
                        if (nextp == curbp->b_linep)
                                return (FALSE);
                        chunk += llength(nextp)+1;
                        nextp = lforw(nextp);
                }
        } else {
                mlwrite("neg kill");
                return (FALSE);
        }
        return (ldelete(chunk, TRUE));
}
 
/*
 * Yank text back from the kill buffer. This
 * is really easy. All of the work is done by the
 * standard insert routines. All you do is run the loop,
 * and check for errors. Bound to "C-Y". The blank
 * lines are inserted with a call to "newline"
 * instead of a call to "lnewline" so that the magic
 * stuff that happens when you type a carriage
 * return also happens when a carriage return is
 * yanked back from the kill buffer.
 */
yank(f, n)
register int f, n;
{
        register int    c;
        register int    i;
        extern   int    kused;
 
        if (n < 0)
                return (FALSE);
        if (curbp->b_bmode&BMCMODE)
                yankcflag = TRUE;       /* don't reindent yanked text */
        while (n--) {
                i = 0;
                while ((c=kremove(i)) >= 0) {
                        if (c == '\n') {
                                if (newline(FALSE, 1) == FALSE)
                                        return (FALSE);
                        } else {
                                if (linsert(1, c) == FALSE)
                                        return (FALSE);
                        }
                        ++i;
                }
        }
        yankcflag = FALSE;
        return (TRUE);
}
SHAR_EOF
cat << \SHAR_EOF > misc.c
/* misc.c  miscellaneous commands, some macros and mode commands */
 
#include <stdio.h>
#include <ctype.h>
#include <osbind.h>
#include "ed.h"
 
typedef short boolean;
static int count = 1;
char *modes[] = {"Fundamental","Wrap","C"};
 
/* MINDNL : META command Indent subsequent newline same as the present
 * one.  Bound to M-J.
 */
 
mindnl(f, n)
register int f, n;
{
        curwp->w_doto = llength(curwp->w_dotp);
        return(indent(f, n));
}
 
/* MDELELN : CTRL command Delete entire line from beginning. Bound to ^K.
 */
 
mdeleln(f, n)
register int f, n;
{
        curwp->w_doto = 0;
        return(kill(NULL, 1));
}
 
/* MDELWLN : META command Delete entire line including NL. Bound to M-^K.
 */
 
mdelwln(f, n)
register int f, n;
{
        curwp->w_doto = 0;
        return(kill(TRUE, 1));
}
 
/* MDELIND : META command Delete beginning indentation on line. Bound to M-\.
 */
 
mdelind(f, n)
register f, n;
{
        register int odo;
 
        f = llength(curwp->w_dotp);
        odo = curwp->w_doto;
 
        curwp->w_doto = 0;
        while ((n = lgetc(curwp->w_dotp, curwp->w_doto)) <= ' '
                && curwp->w_doto != llength(curwp->w_dotp))
                if (forwdel(NULL, 1) == FALSE)
                        return(FALSE);
        if ((n = llength(curwp->w_dotp)) == NULL)
                curwp->w_doto = n;
        else {
                if (odo == NULL)
                        curwp->w_doto = odo;
                else
                        curwp->w_doto = odo - (f - n);
        }
        lchange(WFEDIT);
        return(TRUE);
}
 
/* CLOWSP Meta Command  Close up white space from point forward.
 * Bound to M-^O.
 */
 
clowsp(f, n)
register int f, n;
{
        while ((n = lgetc(curwp->w_dotp, curwp->w_doto)) <= ' '
                || curwp->w_doto == llength(curwp->w_dotp))
                if(forwdel(NULL, 1) == FALSE)
                        return(FALSE);
        return(linsert(1, ' '));
}
 
/* MARKPAR : Meta Command Mark paragraph for moving or deleting.  Bound to
 * M-H.
 */
 
markpar(f, n)
register int f, n;
{
        goteop(NULL, n);
        setmark(NULL, 1);
        gotbop(NULL, n);
        return(TRUE);
}
 
/* KILLSENT : Meta command  Kills sentence forward.  Bound to M-K.
 */
 
killsent(f, n)
register int f, n;
{
        setmark(NULL,1);
        forwsent(f, n);
        return(killregion(NULL, 1));
 
}
 
/* TGLCASE : Meta command  Toggles case of letter at point.  Bound to M-^P.
 */
 
tglcase(f, n)
register int f, n;
{
        register int c;
 
        if (n < 0)
                return(FALSE);
        while(n--)
                {
                c = lgetc(curwp->w_dotp, curwp->w_doto);
                if(islower(c))
                        c = toupper(c);
                else
                        c = tolower(c);
 
                lputc(curwp->w_dotp, curwp->w_doto, c);
                lchange(WFEDIT);
                if(forwchar(FALSE, 1) == FALSE)
                        return(FALSE);
                }
        return(TRUE);
}
 
/* GRTW eXtended command  Globally removes trailing white space.  Bound to
 * ^X-\.
 */
 
grtw(f, n)
register int f, n;
{
 
        register int dlo, d;
        register LINE *dlp;
 
        dlp = curwp->w_dotp;
        dlo = curwp->w_doto;
 
        gotobob(NULL, 1);
 
        while((d = ltrw(FALSE, 1)) != EOF)
                forwline(NULL, 1);
 
        if (dlo > llength(dlp))
                dlo = llength(dlp);
 
        curwp->w_dotp = dlp;
        curwp->w_doto = dlo;
        curwp->w_flag |= WFHARD;
        return(TRUE);
}
 
/* LTRW internal function delete trailing white space on current line.
 * called by grtw() and fillpar().  Any arguments are ignored.  Returns
 * length of line or EOF at end of buffer.
 */
 
ltrw(f, n)
register int f, n;
{
        register int c, d;
 
        if(curwp->w_dotp == curbp->b_linep)
                return(EOF);
        d = llength(curwp->w_dotp);
        curwp->w_doto = d;
 
        if (d == NULL)
                return(NULL);
        while (--d >= 0)
                {
                backchar(FALSE, 1);
                c = lgetc(curwp->w_dotp, curwp->w_doto);
                if(c != ' ' && c != '\t')
                        break;
                if(ldelete(1, NULL) == FALSE)
                        return(EOF);
                }
        return(llength(curwp->w_dotp));
}
 
/* TWADDLE Meta command  More at twiddle.  Transpose two words on a line.
 * Bound to M-T.
 */
 
twaddle(f, n)
register int f, n;
{
        boolean flag = FALSE;
        boolean punct= FALSE;
        register int c;
        char word[20];
 
        n = 0;
 
        if(backchar(FALSE, 1) == FALSE)
                return (FALSE);
        c = lgetc(curwp->w_dotp, curwp->w_doto);
        if(ispunct(c))
                {
                punct = c;
                if (forwdel(NULL, 1) == FALSE)
                        return (FALSE);
                }
        else if (forwchar(FALSE, 1) == FALSE)
                        return (FALSE);
 
        c = lgetc(curwp->w_dotp, curwp->w_doto);
        if (isspace(c))
                flag = c;
        else if (ispunct(c))
                flag = c;
        else
                word[n++] = c;
        if (ldelete(1, NULL) == FALSE)
                return (FALSE);
 
        while(c = lgetc(curwp->w_dotp, curwp->w_doto))
                {
                if (curwp->w_doto == llength(curwp->w_dotp))
                        break;
                if (isalnum(c))
                        {
                        word[n++] = c;
                        if (forwdel(NULL, 1) == FALSE)
                                return (FALSE);
                        }
                else
                        break;
                }
        if(backword(NULL, 1) == FALSE)
                return (FALSE);
        c = 0;
        while(n--)
                if (linsert(1, word[c++]) == FALSE)
                        return (FALSE);
        if (punct)
                if (linsert(1, punct) == FALSE)
                        return (FALSE);
        if (flag)
                if (linsert(1, flag) == FALSE)
                        return (FALSE);
        return(TRUE);
}
 
/* MRFLUSH META Command right flush current line.  Bound to M-^R */
 
mrflush(f, n)
register int f, n;
{
        register short p;       /* where we are in line */
        register short l;       /* length of line */
        register short o;       /* offset for new location */
 
        p = curwp->w_doto;
        if((l = llength(curwp->w_dotp)) == NULL)
                return(FALSE);
        if (fillcol)
                f = fillcol;
        else    f = 80;
        if((o = f - l) < 0)
                return(FALSE);
        curwp->w_doto = 0;
        linsert(o, ' ');
        forwchar(NULL, p);
        return(TRUE);
}
 
/* MCENTER META Command center current line.  Bound to M-^C */
 
mcenter(f, n)
register int f, n;
{
        register short p;       /* where we are in line */
        register short l;       /* length of line */
        register short o;       /* offset for new location */
 
        p = curwp->w_doto;
        if((l = llength(curwp->w_dotp)) == NULL)
                return(FALSE);
        if (fillcol)
                f = fillcol;
        else    f = 80;
        if((o = (f - l)/2) < 0)
                return(FALSE);
        curwp->w_doto = 0;
        linsert(o, ' ');
        forwchar(NULL, p);
        return(TRUE);
}
 
/* SETMODE Extended Command.  Only one mode at a time.  Bound to ^X-M.
 */
 
setmode(f, n)
register int f, n;
{
        char mymode[12];
        register char *ptr;
        extern char *index();
        register BUFFER *bp;
        register WINDOW *wp;
 
        if((f=mlreply("New mode: ",mymode,12)) != TRUE)
                return(f);
        /* don't fail because of stray spaces */
        if ((ptr=index(mymode,' '))!=NULL)
                *ptr = '\0';
        else if ((ptr=index(mymode,'\t'))!=NULL)
                *ptr = '\0';
        n = strlen(mymode);
        /* adjust case */
        for (f=0;f<=n;f++)
                if (f == 0)
                        mymode[f] = toupper(mymode[f]);
                else
                        mymode[f] = tolower(mymode[f]);
        for (f=0;f<=NUMMODES-1;f++)
                {
                if (strncmp(modes[f],mymode,12)==0)
                        {
                        switch(f)
                                {
                                case 0:
                                        curbp->b_bmode &= ~BMWRAP;
                                        curbp->b_bmode &= ~BMCMODE;
                                        curbp->b_bmode |= BMNWRAP;
                                        break;
                                case 1:
                                        curbp->b_bmode &= ~BMNWRAP;
                                        curbp->b_bmode &= ~BMCMODE;
                                        curbp->b_bmode |= BMWRAP;
                                        break;
                                case 2:
                                        curbp->b_bmode &= ~BMNWRAP;
                                        curbp->b_bmode &= ~BMWRAP;
                                        curbp->b_bmode |= BMCMODE;
                                        break;
                                }
                        upmode();
                        return(TRUE);
                        }
                }
                mlwrite("No match");
                return(FALSE);
}
 
/* SGLMODE META-Command Set the flag glmode.  The flag is used only by
 * bfind and this command.  Call this after reading in a new color file
 * and answer "color" to the mode question to install new colors.
 */
 
sglmode(f, n)
int f, n;
{
        char mymode[12];
        register char *ptr;
        extern char *index();
        register BUFFER *bp;
        register WINDOW *wp;
 
        if((f=mlreply("New global mode: ",mymode,12)) != TRUE)
                return(f);
        /* don't fail because of stray spaces */
        if ((ptr=index(mymode,' '))!=NULL)
                *ptr = '\0';
        else if ((ptr=index(mymode,'\t'))!=NULL)
                *ptr = '\0';
        n = strlen(mymode);
        /* adjust case */
        for (f=0;f<=n;f++)
                if (f == 0)
                        mymode[f] = toupper(mymode[f]);
                else
                        mymode[f] = tolower(mymode[f]);
        if (strcmp(mymode,"Color")==NULL)
                {
                ttopen();
                return(TRUE);
                }
        for (f=0;f<=NUMMODES-1;f++)
                {
                if (strncmp(modes[f],mymode,12)==0)
                        {
                        switch(f)
                                {
                                case 0:
                                        glmode &= ~BMWRAP;
                                        glmode &= ~BMCMODE;
                                        glmode |= BMNWRAP;
                                        break;
                                case 1:
                                        glmode &= ~BMNWRAP;
                                        glmode &= ~BMCMODE;
                                        glmode |= BMWRAP;
                                        break;
                                case 2:
                                        glmode &= ~BMNWRAP;
                                        glmode &= ~BMWRAP;
                                        glmode |= BMCMODE;
                                        break;
                                }
                        return(TRUE);
                        }
                }
                mlwrite("No match");
                return(FALSE);
}
 
/* RETTPA META-Command returns current memory use and available buffer
 * to msgline.  Bound to M-@.
 */
 
rettpa(f, n)
int f, n;
#if ST
{
        register long low, u_buf;
        unsigned long tpa, stkadd;
        extern long progend;
        extern unsigned long getmem();
        extern long sbrk();
 
        low = sbrk(0);
        stkadd = getmem();
        u_buf = low - progend;
        tpa = stkadd - low;
        mlwrite("Used:%D bytes Heap address:%D Stack address:%lu TPA:%lu bytes",
        u_buf,low,stkadd,tpa);
        return(TRUE);
}
#else
{
        return(FALSE);
}
#endif
 
/* GOLINE  Meta command go to argument line in text.  Bound to M-G.
 */
 
goline(f, n)
register int f, n;
{
        return(gotobob(f, n) && forwline(f, n));
}
 
/* ENUMERATE  Meta Command  Based on value of n, initialize or increment
 * a counter variable.  If n < NULL, simply write the value on the mode
 * line.  Bound to M-CTRL-N.
 */
 
enumerate(f, n)
register int f, n;
{
        char num[5+1];
 
        if (n == -1)    /* request value of count */
                mlwrite("Counter = %d",count);
        else if (n == TRUE)     /* increment counter */
                count++;
        else if (n == NULL)
                {
                ltoa(num, 5, (long)count++); /* make it a string */
                putlin(num);    /* put count in doc */
                putlin(". ");           /* do some formatting */
                }
        else if (n > 1)                 /* n > 1.  Set counter to arg value */
                {
                count = n;
                enumerate(NULL,-1);
                }
        else if (n < -1)        /* Request to reset counter */
                count = 1;
        return(TRUE);
}
 
/* GOSPELL  eXtended command goto next spell mark to correct spelling
 * mistakes.  Bound to X-S.
 */
 
gospell(f, n)
register int f, n;
{
 
        while(TRUE)
                {
                if (curwp->w_dotp == curbp->b_linep)
                        {
                        mlwrite("No spell marks found");
                        return(FALSE);
                        }
                f = lgetc(curwp->w_dotp, curwp->w_doto);
                if (f == 0x93)
                        {
                        if (forwdel(NULL, TRUE) == FALSE)
                                return (FALSE);
                        mlwrite("Correction mark found");
                        return(TRUE);
                        }
                forwchar(NULL,TRUE);
                }
}
 
/* showtime print current time on mode line.  If n > 1 insert in document.
 */
showtime(f,n)
register int f, n;
{
        register int time;
        register int hours, minutes, seconds;
        char mortem[3]; /* am or pm */
        char timeline[20];
 
        time  = Tgettime();
        seconds = (time&31)*2;
        minutes = (time>>5)&63;
        hours   = (time>>11)&31;
        if (hours >= 12)
                strcpy(mortem, "pm");
        else
                strcpy(mortem, "am");
        if (hours > 12)
                hours-=12;
        if (hours == 0)
                hours = 12;
        sprintf(timeline,"Time: %d:%02d:%02d %s",hours,minutes,seconds,mortem);
        if (n <= 1)
                mlwrite(timeline);
        else if (n == HUGE)
                mtwrite(timeline);
        else
                putlin(timeline);
        return(TRUE);
}
 
/* MDONCOM  do named command.  Bound to M-X.
 */
mdoncom(f, n)
register int f, n;
{
        char fname[16];         /* function name */
        register char *ptr;
        extern char *index();
        extern short getktpcode();
        register short c;
 
        mlreply("Function: ",fname,15);
        /* don't fail because of stray spaces */
        if ((ptr=index(fname,' '))!=NULL)
                *ptr = '\0';
        else if ((ptr=index(fname,'\t'))!=NULL)
                *ptr = '\0';
        if ((c=getktpcode(fname))!=NULL)
                c = (short)execute(c,f,n);
        else
                {
                mlwrite("No match!");
                (*term.t_beep)();
                }
        return((int)c);
}
SHAR_EOF
cat << \SHAR_EOF > macros.c
/* macros.c  routines to bind keyboard macros to individual keys */
 
#include <stdio.h>
#include "ed.h"
 
#define FLEN 14
 
static FILE *mp;        /* file pointer for LOAD and SAVE */
static char macfile[NFILEN+1] = "uemail.mcr";
static short macstr[NKBDM];
 
typedef struct MACTAB {
        struct  MACTAB  *m_fmac;        /* next MACTAB  */
        short   *keymac;                /* pointer to the macro */
        char    key;                    /* the key      */
} MACTAB;
 
MACTAB  *mheadp = NULL; /* pointer to header structure */
 
/* GETMACRO  eXtended command to store a macro in the table above.
 * Copies the current keyboard macro to the appropriate array.
 * Bound to ^X-`.
 */
getmacro(f, n)
int f,n;
{
        register int c,i;
        short *maccpy();
        register MACTAB *mt;
        MACTAB *mcralloc();
 
        /* is there currently a macro being defined ? */
        if (kbdmip != NULL || kbdmop != NULL) {
                mlwrite("Not now");
                return (FALSE);
        }
        mlwrite("Key to bind to current macro: ");
        c = ttgetc();
        ttputc(c);
        if((mt=mcralloc(TRUE,c))==NULL)
                return(FALSE);
        i = maclen(kbdm);
        mt->key = c;                    /* install the key      */
        if ((mt->keymac=(short *)malloc((i+1)*sizeof(short)))==NULL)
                {
                mlwrite("Cannot allocate space for macro");
                return(FALSE);
                }
        maccpy(mt->keymac,kbdm);        /* copy the macro       */
        return(TRUE);
}
 
/* PUTMACRO  AGRAVE command finds macro associated with key returned
 * by ttgetc and executes the macro.
 */
 
putmacro(f, n)
register int f, n;
{
        register int c;
        short  *maccpy();
        register MACTAB *mt;
        MACTAB *mcralloc();
 
        c = ttgetc();   /* get next char */
        switch(c)
                {
                case 0x07:      /* abort */
                        return(ctrlg(FALSE,0));
                case 0x0c:      /* load a macro file */
                        return(loadmac(TRUE));
                case 0x0d:      /* "make" a macro file */
                        return(savemac());
                case 0x12:      /* incremental search back */
                        return(backisearch(f, n));
                case 0x13:      /* incremental search forward */
                        return(forwisearch(f, n));
                case 0x14:      /* get time */
                        return(showtime(f,n));
                case '`':
                        return(linsert(1,c));
                }
        if (kbdmip!=NULL || kbdmop!=NULL)
                {
                mlwrite("Not now");
                return (FALSE);
                }
        /* actually, there should be no way mheadp could != NULL and
         * its forward pointer == NULL, but it's best to check
         */
        if (mheadp==NULL || mheadp->m_fmac == NULL)
                {
                mlwrite("No defineable macros assigned");
                return(FALSE);
                }
        if((mt=mcralloc(FALSE,c))!= (MACTAB *)NULL)
                {
                /* save current macro to restore later */
                maccpy(macstr,kbdm);
                maccpy(kbdm,mt->keymac);
                c=ctlxe(f, n);          /* execute mac in keybrd.c */
                maccpy(kbdm,macstr);
                return(c);
                }
        (*term.t_beep)();
        return(FALSE);
}
 
/* LOADMAC load a macro file and install the macros into memory.
 * Bound to `^L.
 */
loadmac(f)
int f;
{
        short *maccpy();
        register int c,i;
        register short s;
        register MACTAB *mt;
        MACTAB *mcralloc();
 
        if (f==TRUE)    /* FALSE == Called from main */
                {
                if(readpattern("Load macros: ",macfile)==ABORT)
                        return(ABORT);
                }
        else
                strcpy(macfile,"uemail.mcr");
        if (macfile[0] == '~')
                parsefn(macfile);
        if ((mp=fopen(macfile,"r"))==NULL)
                {
                mlwrite("Cannot open %s",macfile);
                return(FALSE);
                }
        if ((s=getw(mp))!=(CTLX|'('))
                {
                mlwrite("Macro file format error: %s",macfile);
                fclose(mp);
                return(FALSE);
                }
        while((c=fgetc(mp))!=EOF)
                {
                i=0;
                mlwrite("[Installing macro for key %c]",c);
                if((mt=mcralloc(TRUE,c))==NULL)
                        {
                        fclose(mp);
                        return(FALSE);
                        }
                while ((s=getw(mp))!=(CTLX|')'))
                        {
                        if (feof(mp))
                                {
                                mlwrite("Read error on: %s",macfile);
                                break;
                                }
                        macstr[i++]=s;
                        }
                macstr[i] = CTLX|')';
                mt->key = c;            /* install the key      */
                if ((mt->keymac=(short *)malloc((i+1)*sizeof(short)))==NULL)
                        {
                        fclose(mp);
                        mlwrite("Cannot allocate space for macro");
                        return(FALSE);
                        }
                maccpy(mt->keymac,macstr); /* copy the macro    */
                }
        fclose(mp);
        return(TRUE);
}
 
/* SAVEMAC function to save current keyboard macros to a file.
 * Bound to `^M (makemac).
 */
savemac()
{
        short *maccpy();
        register int i;
        register short s;
        register MACTAB *mt;
 
        if(readpattern("Save macros: ",macfile)==ABORT)
                return(ABORT);
        if (macfile[0] == '~')
                parsefn(macfile);
        if ((mp=fopen(macfile,"w"))==NULL)
                {
                mlwrite("Cannot open %s",macfile);
                return(FALSE);
                }
        /* actually, there should be no way mheadp could != NULL and
         * its forward pointer == NULL, but it's best to check
         */
        if (mheadp==NULL || mheadp->m_fmac == NULL)
                {
                mlwrite("No defineable macros assigned");
                return(FALSE);
                }
        /* don't change the file format or the load function
         * will fail
         */
        mt=mheadp->m_fmac;
        putw(CTLX|'(',mp);
        while(mt != NULL)
                {
                i=0;
                mlwrite("[Saving key: %c]",mt->key);
                maccpy(macstr,mt->keymac);
                fputc(mt->key,mp);
                while(macstr[i]!=(CTLX|')'))
                        putw(macstr[i++],mp);
                putw(CTLX|')',mp);
                mt=mt->m_fmac;  /* next pointer */
                }
        fclose(mp);
        return(TRUE);
}
 
/* find an existing keyboard macro or get space for a new one.  Return
 * a pointer to the MACTAB structure so others can do with it what they
 * will.
 */
MACTAB *
mcralloc(f,c)
register int f,c;
{
        register MACTAB *mt;
 
        if (mheadp == NULL)
                {
                if ((mheadp=(MACTAB *)malloc(sizeof(MACTAB))) == NULL)
                        {
                        mlwrite("Cannot allocate %d bytes",sizeof(MACTAB));
                        return(FALSE);
                        }
                mheadp->m_fmac = (MACTAB *)NULL;
                }
        /* check to see if this one has already been allocated */
        mt=mheadp;
        while(mt->m_fmac != (MACTAB *)NULL)
                {
                if (mt->m_fmac->key == c)
                        {
                        if (f == TRUE)          /* reallocate old */
                                free(mt->m_fmac->keymac);
                        return(mt->m_fmac);
                        }
                mt=mt->m_fmac;  /* next pointer */
                }
        /* this was a get request. return if no macro found */
        if (f==FALSE)
                return(FALSE);
        /* otherwise must be a new request */
        if ((mt->m_fmac=(MACTAB *)malloc(sizeof(MACTAB)))==NULL)
                {
                mlwrite("Cannot allocate %d bytes",sizeof(MACTAB));
                return(FALSE);
                }
        mt->m_fmac->m_fmac = (MACTAB *)NULL;    /* new pointer */
        return(mt->m_fmac);
}
 
/* PUTLIN function to service putmacro() also used by enumerate and showtime */
 
putlin(lin)
register char *lin;
{
        while(*lin)
                linsert(1,*lin++);
}
 
/* get actual length of macro to allocate spacr */
maclen(mac)
register short *mac;
{
        register int i;
 
        i = 1;
        while(*mac++ != (CTLX|')'))
                ++i;
        return(i);
}
 
/* similar to strcpy but copies one keyboard macro to another returning
 * the address of the destination string.
 */
short *
maccpy(dest, sour)
register short *dest;
register short *sour;
{
        register short *dptr;
        register short s;
 
        dptr = dest;
 
        while ((s = *sour++) != (CTLX|')'))
                *dest++ = s;
        *dest = CTLX|')';
        return(dptr);
}
SHAR_EOF
#       End of shell archive
exit 0
%NONAME-W-NOMSG, Message number 00000000

RDROYA01@ULKYVX.BITNET.UUCP (02/05/87)

#       This is a shell archive.
#       Remove everything above and including the cut line.
#       Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar: Shell Archiver
#       Run the following text with /bin/sh to create:
#       main.c
#       keybrd.c
# This archive created: Tue Feb  3 19:12:55 1987
cat << \SHAR_EOF > main.c
/*
 * This program is in public domain; written by Dave G. Conroy.
 * This file contains the main driving routine for the Micro-
 * EMACS screen editor.  This version began as the original
 * small version by DGC.  It has been extensively expanded
 * using versions 35 (a hybrid) and 36 (a direct descendent).
 * This version includes a number of text functions that are
 * not found in others and a word wrap algorithm different from
 * 35 or 36.  It also contains a kermit module, real page movement,
 * printer support and a shell command.  It is sort of a cross
 * between ME and Perfect Writer version 1.03.
 */
 
#include <stdio.h>
#include <ctype.h>
#include <osbind.h>
#include "ed.h"
#if ST
#include "keycode.h"
#endif
 
#if     VMS
#include        <ssdef.h>
#define GOOD    (SS$_NORMAL)
#endif
 
#ifndef GOOD
#define GOOD    0
#endif
 
char *version = "v. 33 25-January-1987";
 
int     currow;                         /* Working cursor row           */
int     curcol;                         /* Working cursor column        */
int     fillcol = 76;                   /* Current fill column          */
int     indcol;                         /* Current indent column        */
int     thisflag;                       /* Flags, this command          */
int     lastflag;                       /* Flags, last command          */
int     curgoal;                        /* Goal column                  */
int     isnprint;                       /* Print buffer in use          */
int     glmode = BMNWRAP;               /* Begin in fundamental mode    */
BUFFER  *curbp;                         /* Current buffer               */
WINDOW  *curwp;                         /* Current window               */
BUFFER  *bheadp;                        /* BUFFER listhead              */
WINDOW  *wheadp;                        /* WINDOW listhead              */
BUFFER  *blistp = NULL;                 /* Buffer list BUFFER           */
BUFFER  *bmacrp = NULL;                 /* Compiled macros BUFFER       */
short   kbdm[NKBDM] = CTLX|')';         /* Macro                        */
short   *kbdmip;                        /* Input  for above             */
short   *kbdmop;                        /* Output for above             */
char    pat[NPAT];                      /* Pattern                      */
char    rpat[NPAT];                     /* Replacement pattern          */
char    prnhdr[NPAT];                   /* Print header                 */
char    prndate[NPAT];                  /* Print date                   */
char    lastbuf[NBUFN];                 /* Last buffer name             */
char    defpath[128];                   /* default path name            */
int     defdrive;                       /* default drive                */
long    buserr;                         /* Address of gemdos busserr    */
long    adderr;                         /* Address of gemdos address err*/
long    progend;                        /* end of program after init    */
 
main(argc, argv)
int argc;
char *argv[];
{
        register int    c;
        register int    f;
        register int    n;
        register int    mflag;
        register int    prncnt;
        register int    basec;
        extern   int    errexit();
        extern   long   sbrk();
 
#if     ALCYON
        fclose(stdin);                          /* reduce overhead for */
        fclose(stdout);                         /* unused file pointers */
        fclose(stderr);
#endif
        isnprint = FALSE;
        prncnt = 0;
 
        if (access("cc.ini",4) == NULL)         /* cc drive assignments */
                commfil(FALSE,HUGE);            /* and function key bind*/
        strcpy(lastbuf, "main");                /* Work out the name of */
        if (argc > 1)                           /* the default buffer.  */
                makename(lastbuf, argv[1]);
        edinit(lastbuf);                        /* Buffers, windows.    */
        vtinit();                               /* Displays.            */
        update();
#if     ST
        defdrive = Dgetdrv();
        Dgetpath(defpath,0);
        buserr = Setexc(2,-1L);
        adderr = Setexc(3,-1L);
        Setexc(2,&errexit);                     /* Clean up screen on err */
        Setexc(3,&errexit);
#endif
        if (access("uemail.mcr",4) == NULL)     /* default macro file   */
                {
                update();
                loadmac(FALSE);
                }
        progend = sbrk(0);
        if (argc > 1) {
                update();                       /* You have to update   */
                readin(argv[1]);                /* in case "[New file]" */
        }
        lastflag = 0;                           /* Fake last flags.     */
loop:
        update();                               /* Fix up the screen    */
 
        /* This is the print buffer code.  It's very rudimentary, but it
         * works as long as the buffer in the printer does not get filled.
         * On the initial call to the function print(), the printer is
         * sent one 1536 byte buffer of data and isnprint is set to
         * TRUE.  From then on the printer is sent 384 bytes of data after
         * an arbitrary number of keystrokes is entered if the printer
         * is ready.
         */
 
        if(isnprint)                            /* We are printing a file */
                if (prncnt++ > 16)              /* Keystroke count      */
                        if(PRNRDY)              /* Check LST: status */
                                {
                                prnbuf();       /* Send a buffer full */
                                prncnt = 0;
                                }
        f = getkey();
        if (shiftstatus == 17 || shiftstatus == 18)     /* CapsLock + Shift */
                c = tolower(f);
        else
                c = f;
        if (mpresf != FALSE) {
                mlerase();
                update();
        }
        f = FALSE;
        n = 1;
 
        /* do META-# processing if needed */
 
        basec = c & ~META;              /* strip meta char off if there */
        if ((c & META) && ((basec >= '0' && basec <= '9') || basec == '-')) {
                 f = TRUE;               /* there is a # arg */
                 n = 0;             /* start with a zero default */
                 mflag = 1;             /* current minus flag */
                 c = basec;             /* strip the META */
                 while ((c >= '0' && c <= '9') || (c == '-')) {
                          if (c == '-') {
                                   /* already hit a minus or digit? */
                                   if ((mflag == -1) || (n != 0))
                                            break;
                                   mflag = -1;
                          } else {
                                   n = n * 10 + (c - '0');
                          }
                          if ((n == 0) && (mflag == -1))  /* lonely - */
                                   mlwrite("Arg:");
                          else
                                   mlwrite("Arg: %d",n * mflag);
 
                          c = getkey();   /* get the next key */
                 }
                 n = n * mflag;  /* figure in the sign */
        }
 
        /* ^U expansion */
 
        if (c == (CTRL|'U')) {                  /* ^U, start argument   */
                f = TRUE;
                n = 4;                          /* with argument of 4 */
                mflag = 0;                      /* that can be discarded. */
                mlwrite("Arg: 4");
                while ((c=getkey()) >='0' && c<='9' || c==(CTRL|'U') || c=='-'){
                        if (c == (CTRL|'U'))
                                n = n*4;
                        /*
                         * If dash, and start of argument string, set arg.
                         * to -1.  Otherwise, insert it.
                         */
                        else if (c == '-') {
                                if (mflag)
                                        break;
                                n = 0;
                                mflag = -1;
                        }
                        /*
                         * If first digit entered, replace previous argument
                         * with digit and set sign.  Otherwise, append to arg.
                         */
                        else {
                                if (!mflag) {
                                        n = 0;
                                        mflag = 1;
                                }
                                n = 10*n + c - '0';
                        }
                        mlwrite("Arg: %d", (mflag >=0) ? n : (n ? -n : -1));
                }
                /*
                 * Make arguments preceded by a minus sign negative and change
                 * the special argument "^U -" to an effective "^U -1".
                 */
                if (mflag == -1) {
                        if (n == 0)
                                n++;
                        n = -n;
                }
        }
        if (c == (CTRL|'X')) {                  /* ^X is a prefix       */
                mlwrite("C-X: ");
                c = CTLX | getctl();
        }
        if (kbdmip != NULL) {                   /* Save macro strokes.  */
                if (c!=(CTLX|')') && kbdmip>&kbdm[NKBDM-6]) {
                        ctrlg(FALSE, 0);
                        goto loop;
                }
                if (f != FALSE) {
                        *kbdmip++ = (CTRL|'U');
                        *kbdmip++ = n;
                }
                *kbdmip++ = c;
        }
        execute(c, f, n);                       /* Do it.               */
        goto loop;
}
 
/*
 * Initialize all of the buffers
 * and windows. The buffer name is passed down as
 * an argument, because the main routine may have been
 * told to read in a file by default, and we want the
 * buffer name to be right.
 */
edinit(bname)
char    bname[];
{
        register BUFFER *bp;
        register WINDOW *wp;
 
        bp = bfind(bname, TRUE, 0);             /* First buffer         */
        wp = (WINDOW *) malloc(sizeof(WINDOW)); /* First window         */
        if (bp==NULL || wp==NULL)
                exit(1);
        curbp  = bp;                            /* Make this current    */
        wheadp = wp;
        curwp  = wp;
        wp->w_wndp  = NULL;                     /* Initialize window    */
        wp->w_bufp  = bp;
        bp->b_nwnd  = 1;                        /* Displayed.           */
        wp->w_linep = bp->b_linep;
        wp->w_dotp  = bp->b_linep;
        wp->w_doto  = 0;
        wp->w_markp = wp->w_dotp;
        wp->w_marko = 0;
        wp->w_toprow = 0;
        wp->w_ntrows = term.t_nrow-1;           /* "-1" for mode line.  */
        wp->w_force = 0;
        wp->w_flag  = WFMODE|WFHARD;            /* Full.                */
}
 
/*
 * Read in a key.
 * Do the standard keyboard preprocessing.
 * Convert the keys to the internal character set. On
 * the LK201, which lacks a reasonable ESC key, make the
 * grave accent a meta key too; this is a fairly common
 * customization around Digital. Also read and decode
 * the arrow keys, and other special keys. This is
 * done in Rainbow mode; does this work on all
 * the terminals with LK201 keyboards?
 */
getkey()
{
        register int    c;
#if     LK201
        register int    n;
loop:
        c = (*term.t_getchar)();
        if (c == AGRAVE) {                      /* Alternate M- prefix. */
                c = getctl();
                return (META | c);
        }
        if (c == METACH) {                      /* M-, or special key.  */
                c = (*term.t_getchar)();
                if (c == '[') {                 /* Arrows and extras.   */
                        c = (*term.t_getchar)();
                        if (c == 'A')
                                return (CTRL | 'P');
                        if (c == 'B')
                                return (CTRL | 'N');
                        if (c == 'C')
                                return (CTRL | 'F');
                        if (c == 'D')
                                return (CTRL | 'B');
                        if (c>='0' && c<='9') {
                                n = 0;
                                do {
                                        n = 10*n + c - '0';
                                        c = (*term.t_getchar)();
                                } while (c>='0' && c<='9');
                                if (c=='~' && n<=34 && (c=lkmap[n])!=0)
                                        return (c);
                        }
                        goto loop;
                }
                if (c == 'O') {
                        c = (*term.t_getchar)();
                        if (c == 'P')           /* PF1 => M-X (Future)  */
                                return (META | 'X');
                        if (c == 'Q')           /* PF2 => C-Q           */
                                return (CTRL | 'Q');
                        if (c == 'R')           /* PF3 => C-S           */
                                return (CTRL | 'S');
                        if (c == 'S')           /* PF4 => C-R           */
                                return (CTRL | 'R');
                        goto loop;
                }
                if (c>='a' && c<='z')           /* Force to upper       */
                        c -= 0x20;
                if (c>=0x00 && c<=0x1F)         /* C0 control -> C-     */
                        c = CTRL | (c+'@');
                return (META | c);
        }
#endif
#if     VT100
loop:
        c = (*term.t_getchar)();
        if (c == METACH) {                      /* Apply M- prefix      */
                c = (*term.t_getchar)();
                if (c == '[') {                 /* Arrow keys.          */
                        c = (*term.t_getchar)();
                        if (c == 'A')
                                return (CTRL | 'P');
                        if (c == 'B')
                                return (CTRL | 'N');
                        if (c == 'C')
                                return (CTRL | 'F');
                        if (c == 'D')
                                return (CTRL | 'B');
                        goto loop;
                }
                if (c == 'O') {
                        c = (*term.t_getchar)();
                        if (c == 'P')           /* PF1 => M-X (Future)  */
                                return (META | 'X');
                        if (c == 'Q')           /* PF2 => C-Q           */
                                return (CTRL | 'Q');
                        if (c == 'R')           /* PF3 => C-S           */
                                return (CTRL | 'S');
                        if (c == 'S')           /* PF4 => C-R           */
                                return (CTRL | 'R');
                        goto loop;
                }
                if (c>='a' && c<='z')           /* Force to upper       */
                        c -= 0x20;
                if (c>=0x00 && c<=0x1F)         /* C0 control -> C-     */
                        c = CTRL | (c+'@');
                return (META | c);
        }
#endif
        c = (*term.t_getchar)();
#if     ST
        if (scancode == 0x70 && shiftstatus != 16)
                return (CTRL|'X');
#endif
        if (c == METACH) {                      /* Apply M- prefix      */
                mlwrite("Meta: ");
                c = getctl();
                return (META | c);
        }
        if (c == CTRLCH) {                      /* Apply C- prefix      */
                c = getctl();
                return (CTRL | c);
        }
        if (c == CTMECH) {                      /* Apply C-M- prefix    */
                c = getctl();
                return (CTRL | META | c);
        }
#if     ST
        /* use special keys or number pad (code >= 0x63) ?
         * 0x03 = scancode for ^@ (setmark).
         */
        if ((c == NULL && scancode != 0x03) || scancode >= 0x4a)
                if (scancode > 0x32)
                        {
                        /* if CapsLock and number pad, use real numbers */
                        if (shiftstatus == 16 && (scancode > 0x62 ||
                                scancode == 0x4a || scancode == 0x4e))
                                return(c);
                        else
                                return (SPEC | scancode);
                        }
                else    {
                        c = keyscan[scancode];
                        return (META | c);
                        }
#endif
        if (c>=0x00 && c<=0x1F)                 /* C0 control -> C-     */
                c = CTRL | (c+'@');
        return (c);
}
 
/*
 * Get a key.
 * Apply control modifications
 * to the read key.
 */
getctl()
{
        register int    c;
 
        c = (*term.t_getchar)();
#if     ST
        if (scancode == 0x70 && shiftstatus != 16)
                return (SPEC|'p');
#endif
        if (c>='a' && c<='z')                   /* Force to upper       */
                c -= 0x20;
        if (c>=0x00 && c<=0x1F)                 /* C0 control -> C-     */
                c = CTRL | (c+'@');
        return (c);
}
 
#if     ST
/* ERREXIT  Since this toy insists on Bus errors for so many memory
 * accesses, we need a way to exit gracefully.
 */
errexit()
{
        if(mlyesno("FATAL: Buss error. Attempt to save files")!=TRUE)
                quit(TRUE,FALSE);
        quit(FALSE,FALSE);
        exit(-1);
}
#endif
 
/*
/*
 * Fancy quit command, as implemented
 * by Norm. If any buffer has changed
 * write that buffer out. Otherwise simply exit.
 */
quickexit(f, n)
int f, n;
{
        register BUFFER *bp;    /* for buffer scan */
 
        bp = bheadp;
        while (bp != NULL) {
                if ((bp->b_flag&BFCHG) != 0     /* Changed.             */
                && (bp->b_flag&BFTEMP) == 0){   /* Real.        */
                        curbp = bp;
                        mlwrite("[Saving %s]",bp->b_fname);
                                filesave(f, n);
                }
        bp = bp->b_bufp;                /* next buffer */
        }
        quit(f, n);                             /* conditionally quit   */
}
 
/*
 * Quit command. If an argument, always
 * quit. Otherwise confirm if a buffer has been
 * changed and not written out. Normally bound
 * to "C-X C-C".
 */
quit(f, n)
int f, n;
{
        register BUFFER *bp;    /* for buffer scan */
        register int    s;
 
        bp = bheadp;
        if (f != FALSE                          /* Argument forces it.  */
        || (anycb() == FALSE                    /* All buffers clean.   */
        && isnprint == FALSE)) {                /* Are we printing?     */
                vttidy();
#if     ST
                Dsetdrv(defdrive);
                Dsetpath(defpath);
                Setexc(2,buserr);
                Setexc(3,adderr);
#endif
                exit((n!=FALSE) ? GOOD: -1);
        }
        if (isnprint)
                if ((s=mlyesno("Abandon printing")) != TRUE)
                        return(s);
        while (bp != NULL) {
                if ((bp->b_flag&BFCHG) != 0     /* Changed.     */
                && (bp->b_flag&BFTEMP) == 0){   /* Real.        */
                        curbp = bp;
                        mlwrite("Save %s [y/n]?",bp->b_fname);
                        switch(ttgetc()) {
                                case 'Y':
                                case 'y':
                                        filesave(f, n);
                                        break;
                                case 0x07:
                                        return(ctrlg(f,n));
                                default:
                                        break;
                        }
                }
        bp = bp->b_bufp;                /* next buffer */
        }
        if (anycb())    /* check once more */
                if ((s=mlyesno("Abandon modified buffers")) != TRUE)
                        return(s);
        vttidy();
#if     ST
        Setexc(2,buserr);
        Setexc(3,adderr);
#endif
        exit((n!=FALSE) ? GOOD : -1);
}
 
/*
 * Begin a keyboard macro.
 * Error if not at the top level
 * in keyboard processing. Set up
 * variables and return.
 */
ctlxlp(f, n)
int f, n;
{
        if (kbdmip!=NULL || kbdmop!=NULL) {
                mlwrite("Not now");
                return (FALSE);
        }
        mlwrite("[Start macro]");
        kbdmip = &kbdm[0];
        return (TRUE);
}
 
/*
 * End keyboard macro. Check for
 * the same limit conditions as the
 * above routine. Set up the variables
 * and return to the caller.
 */
ctlxrp(f, n)
int f, n;
{
        if (kbdmip == NULL) {
                mlwrite("Not now");
                return (FALSE);
        }
        mlwrite("[End macro]");
        kbdmip = NULL;
        return (TRUE);
}
 
/*
 * Execute a macro.
 * The command argument is the
 * number of times to loop. Quit as
 * soon as a command gets an error.
 * Return TRUE if all ok, else
 * FALSE.
 */
ctlxe(f, n)
register int f, n;
{
        register int    c;
        register int    af;
        register int    an;
        register int    s;
 
        if (kbdmip!=NULL || kbdmop!=NULL) {
                mlwrite("Not now");
                return (FALSE);
        }
        if (n <= 0)
                return (TRUE);
        do {
                kbdmop = &kbdm[0];
                do {
                        af = FALSE;
                        an = 1;
                        if ((c = *kbdmop++) == (CTRL|'U')) {
                                af = TRUE;
                                an = *kbdmop++;
                                c  = *kbdmop++;
                        }
                        s = TRUE;
                } while (c!=(CTLX|')') && (s=execute(c, af, an))==TRUE);
                kbdmop = NULL;
        } while (s==TRUE && --n);
        return (s);
}
 
/*
 * Abort.
 * Beep the beeper.
 * Kill off any keyboard macro,
 * etc., that is in progress.
 * Sometimes called as a routine,
 * to do general aborting of
 * stuff.
 */
ctrlg(f, n)
int f, n;
{
        (*term.t_beep)();
        if (kbdmip != NULL) {
                kbdm[0] = (CTLX|')');
                kbdmip  = NULL;
        }
        mlwrite("Aborting");
        return (ABORT);
}
 
/* CLEARFLAG clears buffer change flag.  Bound to M-~
 */
clearflag(f, n)
register int f, n;
{
        curwp->w_bufp->b_flag &= ~BFCHG;
        upmode();
        return(TRUE);
}
 
/* UNKNCOM  Returns abort and beeps keyboard.  Bound to unused keys.
 */
unkncom(f, n)
register int f, n;
{
        mlwrite("Unbound command!");
        (*term.t_beep)();
        return(ABORT);
}
 
/* RETVERSION  Returns version string.  Bound to CTLX *.
 */
retversion(f, n)
register int f, n;
{
        return(mlwrite("Writer: D. G. Conroy Revised: R. D. Royar (%s)",
        version));
}
SHAR_EOF
cat << \SHAR_EOF > keybrd.c
/* KEYBRD.C this file contain the
 * keyboard processing code, for the
 * MicroEMACS screen editor.
 */
#include        <stdio.h>
#include        "ed.h"
 
#define FLEN    14                      /* Maximum length of function name */
 
typedef struct  {
        short   k_code;                 /* Key code                     */
        int     (*k_fp)();              /* Routine to handle it         */
        char    k_mfunc[FLEN];          /* function name                */
}       KEYTAB;
 
extern  int     clearflag();            /* Clear changed buffer flag    */
extern  int     ctrlg();                /* Abort out of things          */
extern  int     quit();                 /* Quit                         */
extern  int     ctlxlp();               /* Begin macro                  */
extern  int     ctlxrp();               /* End macro                    */
extern  int     ctlxe();                /* Execute macro                */
extern  int     fileinsert();           /* Insert existing file at point*/
extern  int     fileread();             /* Get a file, read only        */
extern  int     filevisit();            /* Get a file, read write       */
extern  int     filewrite();            /* Write a file                 */
extern  int     filesave();             /* Save current file            */
extern  int     filename();             /* Adjust file name             */
extern  int     getccol();              /* Get current column           */
extern  int     gotobol();              /* Move to start of line        */
extern  int     forwchar();             /* Move forward by characters   */
extern  int     gotoeol();              /* Move to end of line          */
extern  int     backchar();             /* Move backward by characters  */
extern  int     forwline();             /* Move forward by lines        */
extern  int     backline();             /* Move backward by lines       */
extern  int     forwpage();             /* Move forward by pages        */
extern  int     backpage();             /* Move backward by pages       */
extern  int     gotobob();              /* Move to start of buffer      */
extern  int     gotoeob();              /* Move to end of buffer        */
extern  int     setfillcol();           /* Set fill column.             */
extern  int     setindcol();            /* Set indent column            */
extern  int     setmark();              /* Set mark                     */
extern  int     swapmark();             /* Swap "." and mark            */
extern  int     forwsearch();           /* Search forward               */
extern  int     backsearch();           /* Search backward              */
extern  int     forwisearch();          /* I-search forward             */
extern  int     backisearch();          /* I-search backward            */
extern  int     replace();              /* Search and replace           */
extern  int     showcpos();             /* Show the cursor position     */
extern  int     nextwind();             /* Move to the next window      */
extern  int     prevwind();             /* Move to the previous window  */
extern  int     onlywind();             /* Make current window only one */
extern  int     splitwind();            /* Split current window         */
extern  int     mvdnwind();             /* Move window down             */
extern  int     mvupwind();             /* Move window up               */
extern  int     enlargewind();          /* Enlarge display window.      */
extern  int     shrinkwind();           /* Shrink window.               */
extern  int     listbuffers();          /* Display list of buffers      */
extern  int     paintbuffer();          /* Set background buffer color  */
extern  int     usebuffer();            /* Switch a window to a buffer  */
extern  int     killbuffer();           /* Make a buffer go away.       */
extern  int     reposition();           /* Reposition window            */
extern  int     refresh();              /* Refresh the screen           */
extern  int     twiddle();              /* Twiddle characters           */
extern  int     tab();                  /* Insert tab                   */
extern  int     newline();              /* Insert CR-LF                 */
extern  int     indent();               /* Insert CR-LF, then indent    */
extern  int     openline();             /* Open up a blank line         */
extern  int     deblank();              /* Delete blank lines           */
extern  int     quote();                /* Insert literal               */
extern  int     backword();             /* Backup by words              */
extern  int     forwword();             /* Advance by words             */
extern  int     forwdel();              /* Forward delete               */
extern  int     backdel();              /* Backward delete              */
extern  int     kill();                 /* Kill forward                 */
extern  int     yank();                 /* Yank back from killbuffer.   */
extern  int     upperword();            /* Upper case word.             */
extern  int     lowerword();            /* Lower case word.             */
extern  int     upperregion();          /* Upper case region.           */
extern  int     lowerregion();          /* Lower case region.           */
extern  int     capword();              /* Initial capitalize word.     */
extern  int     delfword();             /* Delete forward word.         */
extern  int     delbword();             /* Delete backward word.        */
extern  int     killregion();           /* Kill region.                 */
extern  int     copyregion();           /* Copy region to kill buffer.  */
extern  int     writereg();             /* write defined region to file */
extern  int     quickexit();            /* low keystroke style exit.    */
extern  int     goteop();               /* goto end of paragraph */
extern  int     gotbop();               /* goto beginning of paragraph */
extern  int     unkncom();              /* unknown command BEEP */
extern  int     ftopunct();             /* move forward to next punctuation */
extern  int     btopunct();             /* move backward to last punctuation */
extern  int     forwsent();             /* forward to end of sentence */
extern  int     backsent();             /* backward to beginning of sentence */
extern  int     fillpar();              /* fill paragraph to fill column */
extern  int     setpage();              /* set page size from arg */
extern  int     paginate();             /* insert '\f' every page */
extern  int     pageforw();             /* move forward set page length */
extern  int     pageback();             /* move back set page length */
extern  int     print();                /* print buffer with heading */
extern  int     wc();                   /* word and line count of buffer */
extern  int     retversion();           /* version date */
extern  int     kermit();               /* file transfer */
 
/* From here on the commands are from misc.c */
extern  int     mdropln();              /* drop current line and move up*/
extern  int     mindnl();               /* indent subsequent NL same as this */
extern  int     mdeleln();              /* delete entire line from beginning */
extern  int     mdelwln();              /* delete entire line including NL   */
extern  int     mdelind();              /* delete beginning line indentation */
extern  int     markpar();              /* mark paragraph sets mark */
extern  int     tglcase();              /* toggle case of letter at point */
extern  int     killsent();             /* kill sentence forward sets mark */
extern  int     grtw();                 /* remove trailing white space */
extern  int     twaddle();              /* transpaose two words in place */
extern  int     clowsp();               /* close up intervening white space */
extern  int     mrflush();              /* flush right current line */
extern  int     mcenter();              /* center current line */
extern  int     setmode();              /* set a mode for a buffer */
extern  int     sglmode();              /* set global mode      */
extern  int     rettpa();               /* show available memory and usage */
extern  int     showtime();             /* return current time  */
extern  int     goline();               /* goto line in text */
extern  int     enumerate();            /* start or incr. counter variable */
extern  int     gospell();              /* search forward for spell mark */
extern  int     mdoncom();              /* execute a named command */
 
/* functions in macros.c to allow 26 keyboard macros */
extern  int     putmacro();             /* print line macros in text    */
extern  int     getmacro();             /* copy cureent key-mac to key  */
 
/* shell commands */
extern  int     shell();                /* run one command or run cc    */
extern  int     commfil();              /* read alias file              */
extern  int     setpath();              /* get/set path and drive       */
 
/*
 * Command table.
 * This table  is *roughly* in ASCII
 * order, left to right across the characters
 * of the command. This expains the funny
 * location of the control-X commands.
 */
KEYTAB  keytab[] = {
        CTRL|'@',               &setmark,
        "setmark",
        CTRL|'A',               &gotobol,
        "gotobol",
        CTRL|'B',               &backchar,
        "backchar",
        CTRL|'C',               &shell,
        "shell",
        CTRL|'D',               &forwdel,
        "forwdel",
        CTRL|'E',               &gotoeol,
        "gotoeol",
        CTRL|'F',               &forwchar,
        "forwchar",
        CTRL|'G',               &ctrlg,
        "ctrlg",
        CTRL|'H',               &backchar,
        "backchar",
        CTRL|'I',               &tab,
        "tab",
        CTRL|'J',               &indent,
        "indent",
        CTRL|'K',               &mdeleln,
        "mdeleln",
        CTRL|'L',               &refresh,
        "refresh",
        CTRL|'M',               &newline,
        "newline",
        CTRL|'N',               &forwline,
        "forwline",
        CTRL|'O',               &openline,
        "openline",
        CTRL|'P',               &backline,
        "backline",
        CTRL|'Q',               &quote,
        "quote",
        CTRL|'R',               &backsearch,
        "backsearch",
        CTRL|'S',               &forwsearch,
        "forwsearch",
        CTRL|'T',               &twiddle,
        "twiddle",
        CTRL|'V',               &forwpage,
        "forwpage",
        CTRL|'W',               &killregion,
        "killregion",
        CTRL|'Y',               &yank,
        "yank",
        CTRL|'Z',               &quickexit,
        "quickexit",
        CTRL|'\\',              &unkncom,
        "unkncom",
        CTRL|'_',               &kermit,
        "kermit",
        CTRL|'^',               &unkncom,
        "unkncom",
        CTLX|CTRL|'A',          &unkncom,
        "unkncom",
        CTLX|CTRL|'B',          &listbuffers,
        "listbuffers",
        CTLX|CTRL|'C',          &quit,
        "quit",
        CTLX|CTRL|'D',          &unkncom,
        "unkncom",
        CTLX|CTRL|'E',          &commfil,
        "commfil",
        CTLX|CTRL|'F',          &filename,
        "filename",
        CTLX|CTRL|'G',          &ctrlg,
        "ctrlg",
        CTLX|CTRL|'H',          &unkncom,
        "unkncom",
        CTLX|CTRL|'I',          &print,
        "print",
        CTLX|CTRL|'J',          &unkncom,
        "unkncom",
        CTLX|CTRL|'K',          &unkncom,
        "unkncom",
        CTLX|CTRL|'L',          &lowerregion,
        "lowerregion",
        CTLX|CTRL|'M',          &unkncom,
        "unkncom",
        CTLX|CTRL|'N',          &mvdnwind,
        "mvdnwind",
        CTLX|CTRL|'O',          &deblank,
        "deblank",
        CTLX|CTRL|'P',          &mvupwind,
        "mvupwind",
        CTLX|CTRL|'Q',          &unkncom,
        "unkncom",
        CTLX|CTRL|'R',          &fileread,
        "fileread",
        CTLX|CTRL|'S',          &filesave,
        "filesave",
        CTLX|CTRL|'T',          &showtime,
        "showtime",
        CTLX|CTRL|'U',          &upperregion,
        "upperregion",
        CTLX|CTRL|'V',          &filevisit,
        "filevisit",
        CTLX|CTRL|'W',          &filewrite,
        "filewrite",
        CTLX|CTRL|'X',          &swapmark,
        "swapmark",
        CTLX|CTRL|'Y',          &unkncom,
        "unkncom",
        CTLX|CTRL|'Z',          &shrinkwind,
        "shrinkwind",
        CTLX|'!',               &paginate,
        "paginate",
        CTLX|'#',               &setpage,
        "setpage",
        CTLX|'+',               &pageforw,
        "pageforw",
        CTLX|'-',               &pageback,
        "pageback",
        CTLX|'.',               &setindcol,
        "setindcol",
        CTLX|'(',               &ctlxlp,
        "ctlxlp",
        CTLX|')',               &ctlxrp,
        "ctlxrp",
        CTLX|'*',               &retversion,
        "retversion",
        CTLX|'<',               &btopunct,
        "btopunct",
        CTLX|'=',               &showcpos,
        "showcpos",
        CTLX|'>',               &ftopunct,
        "ftopunct",
        CTLX|'0',               &unkncom,
        "unkncom",
        CTLX|'1',               &onlywind,
        "onlywind",
        CTLX|'2',               &splitwind,
        "splitwind",
        CTLX|'3',               &unkncom,
        "unkncom",
        CTLX|'4',               &unkncom,
        "unkncom",
        CTLX|'5',               &unkncom,
        "unkncom",
        CTLX|'6',               &unkncom,
        "unkncom",
        CTLX|'7',               &unkncom,
        "unkncom",
        CTLX|'8',               &unkncom,
        "unkncom",
        CTLX|'9',               &unkncom,
        "unkncom",
        CTLX|'A',               &unkncom,
        "unkncom",
        CTLX|'B',               &usebuffer,
        "usebuffer",
        CTLX|'C',               &paintbuffer,
        "paintbuffer",
        CTLX|'D',               &setpath,
        "setpath",
        CTLX|'E',               &ctlxe,
        "ctlxe",
        CTLX|'F',               &setfillcol,
        "setfillcol",
        CTLX|'H',               &unkncom,
        "unkncom",
        CTLX|'I',               &fileinsert,
        "fileinsert",
        CTLX|'J',               &unkncom,
        "unkncom",
        CTLX|'K',               &killbuffer,
        "killbuffer",
        CTLX|'L',               &unkncom,
        "unkncom",
        CTLX|'M',               &setmode,
        "setmode",
        CTLX|'N',               &nextwind,
        "nextwind",
        CTLX|'O',               &prevwind,
        "prevwind",
        CTLX|'P',               &prevwind,
        "prevwind",
        CTLX|'Q',               &unkncom,
        "unkncom",
        CTLX|'R',               &writereg,
        "writereg",
        CTLX|'S',               &gospell,
        "gospell",
        CTLX|'T',               &unkncom,
        "unkncom",
        CTLX|'U',               &unkncom,
        "unkncom",
        CTLX|'V',               &unkncom,
        "unkncom",
        CTLX|'W',               &wc,
        "wc",
        CTLX|'Y',               &unkncom,
        "unkncom",
        CTLX|'Z',               &enlargewind,
        "enlargewind",
        CTLX|'\\',              &grtw,
        "grtw",
        CTLX|'`',               &getmacro,
        "getmacro",
        CTLX|'~',               &shell,
        "shell",
        CTLX|SPEC|'p',          &swapmark,
        "swapmark",
        META|CTRL|'B',          &backword,
        "backword",
        META|CTRL|'C',          &mcenter,
        "mcenter",
        META|CTRL|'F',          &forwword,
        "forwword",
        META|CTRL|'G',          &ctrlg,
        "ctrlg",
        META|CTRL|'H',          &backword,
        "backword",
        META|CTRL|'I',          &kill,
        "kill",
        META|CTRL|'K',          &mdelwln,
        "mdelwln",
        META|CTRL|'M',          &unkncom,
        "unkncom",
        META|CTRL|'N',          &enumerate,
        "enumerate",
        META|CTRL|'O',          &clowsp,
        "clowsp",
        META|CTRL|'P',          &tglcase,
        "tglcase",
        META|CTRL|'R',          &mrflush,
        "mrflush",
        META|CTRL|'S',          &forwisearch,
        "forwisearch",
        META|CTRL|'T',          &backisearch,
        "backisearch",
        META|'!',               &reposition,
        "reposition",
        META|'.',               &gotoeob,
        "gotoeob",
        META|',',               &gotobob,
        "gotobob",
        META|'>',               &gotoeob,
        "gotoeob",
        META|'<',               &gotobob,
        "gotobob",
        META|' ',               &setmark,
        "setmark",
        META|'@',               &rettpa,
        "rettpa",
        META|'A',               &backsent,
        "backsent",
        META|'B',               &backword,
        "backword",
        META|'C',               &capword,
        "capword",
        META|'D',               &delfword,
        "delfword",
        META|'E',               &forwsent,
        "forwsent",
        META|'F',               &forwword,
        "forwword",
        META|'G',               &goline,
        "goline",
        META|'H',               &markpar,
        "markpar",
        META|'I',               &unkncom,
        "unkncom",
        META|'J',               &mindnl,
        "mindnl",
        META|'K',               &killsent,
        "killsent",
        META|'L',               &lowerword,
        "lowerword",
        META|'M',               &sglmode,
        "sglmode",
        META|'N',               &goteop,
        "goteop",
        META|'O',               &mdropln,
        "mdropln",
        META|'P',               &gotbop,
        "gotbop",
        META|'Q',               &fillpar,
        "fillpar",
        META|'R',               &replace,
        "replace",
        META|'S',               &unkncom,
        "unkncom",
        META|'T',               &twaddle,
        "twaddle",
        META|'U',               &upperword,
        "upperword",
        META|'V',               &backpage,
        "backpage",
        META|'W',               &copyregion,
        "copyregion",
        META|'X',               &mdoncom,
        "mdoncom",
        META|'Y',               &unkncom,
        "unkncom",
        META|'Z',               &unkncom,
        "unkncom",
        META|'\\',              &mdelind,
        "mdelind",
        META|'~',               &clearflag,
        "clearflag",
        META|0x7F,              &delbword,
        "delbword",
#if     ST
        SPEC|'H',               &backline,
        "backline",
        SPEC|'P',               &forwline,
        "forwline",
        SPEC|'K',               &backchar,
        "backchar",
        SPEC|'M',               &forwchar,
        "forwchar",
        SPEC|'b',               &kermit,
        "kermit",
        SPEC|'a',               &yank,
        "yank",
        SPEC|'R',               &openline,
        "openline",
        SPEC|'S',               &backdel,
        "backdel",
        SPEC|'G',               &refresh,
        "refresh",
        SPEC|'q',               &forwdel,
        "forwdel",
        SPEC|'r',               &indent,
        "indent",
        SPEC|'c',               &backword,
        "backword",
        SPEC|'d',               &forwword,
        "forwword",
        SPEC|'e',               &grtw,
        "grtw",
        SPEC|'f',               &retversion,
        "retversion",
        SPEC|'g',               &gotobol,
        "gotobol",
        SPEC|'h',               &gotoeol,
        "gotoeol",
        SPEC|'i',               &unkncom,
        "unkncom",
        SPEC|'J',               &pageback,
        "pageback",
        SPEC|'N',               &pageforw,
        "pageforw",
        SPEC|'j',               &backsent,
        "backsent",
        SPEC|'k',               &forwsent,
        "forwsent",
        SPEC|'l',               &unkncom,
        "unkncom",
        SPEC|'m',               &gotbop,
        "gotbop",
        SPEC|'n',               &goteop,
        "goteop",
        SPEC|'o',               &unkncom,
        "unkncom",
        SPEC|'D',               &quickexit,
        "quickexit",
        SPEC|'C',               &filesave,
        "filesave",
        SPEC|'B',               &filewrite,
        "filewrite",
        SPEC|'A',               &filevisit,
        "filevisit",
        SPEC|'@',               &fileread,
        "fileread",
        SPEC|'?',               &fileinsert,
        "fileinsert",
        SPEC|'>',               &writereg,
        "writereg",
        SPEC|'=',               &filename,
        "filename",
        SPEC|'<',               &listbuffers,
        "listbuffers",
        SPEC|';',               &setmark,
        "setmark",
#endif
        AGRAVE,                 &putmacro,
        "putmacro",
        0x7F,                   &backdel,
        "backdel"
};
 
#define NKEYTAB (sizeof(keytab)/sizeof(keytab[0]))
 
#if     LK201
/*
 * Mapping table for all of the funny
 * keys with the numeric parameters on the LK201.
 * Indexed by the code, which is between 0 (unused) and
 * 34 (F20). An entry of 0 means no mapping. The map
 * goes to command keys. If I had a "special" bit,
 * I could use the code in the escape sequence as a
 * key code, and return (for example) "do" as
 * SPECIAL + 29. Then the dispatch would be
 * done by the default keymap. This is probably a
 * better way to go.
 */
short   lkmap[] = {
        0,
        CTRL|'S',                       /* 1    Find                    */
        CTRL|'Y',                       /* 2    Insert here             */
        CTRL|'W',                       /* 3    Remove                  */
        CTRL|'@',                       /* 4    Select                  */
        META|'V',                       /* 5    Previous screen         */
        CTRL|'V',                       /* 6    Next screen             */
        0,
        0,
        0,
        0,                              /* 10   Compose                 */
        0,
        0,                              /* 12   Print screen            */
        0,
        0,                              /* 14   F4                      */
        0,
        0,
        0,                              /* 17   F6                      */
        0,                              /* 18   F7                      */
        0,                              /* 19   F8                      */
        0,                              /* 20   F9                      */
        0,                              /* 21   F10                     */
        0,
        0,
        0,
        0,
        0,                              /* 26   F14                     */
        0,
        0,                              /* 28   Help                    */
        CTLX|'E',                       /* 29   Do      C-X E           */
        0,
        CTLX|'P',                       /* 31   F17     C-X P           */
        CTLX|'N',                       /* 32   F18     C-X N           */
        CTLX|'Z',                       /* 33   F19     C-X Z           */
        CTLX|CTRL|'Z'                   /* 34   F20     C-X C-Z         */
};
#endif
 
/*
 * This is the general command execution
 * routine. It handles the fake binding of all the
 * keys to "self-insert". It also clears out the "thisflag"
 * word, and arranges to move it to the "lastflag", so that
 * the next command can look at it. Return the status of
 * command.
 */
execute(c, f, n)
register int c, f, n;
{
        register KEYTAB *ktp;
        register int    status;
 
        ktp = &keytab[0];                       /* Look in key table.   */
        while (ktp < &keytab[NKEYTAB]) {
                if (ktp->k_code == c) {
                        thisflag = 0;
                        status   = (*ktp->k_fp)(f, n);
                        lastflag = thisflag;
                        return (status);
                }
                ++ktp;
        }
 
        /*
         * If inwrap a space was typed, fill column is defined, the argument
         * is non-negative, and we are now past fill column, perform word wrap.
         */
        if ((curbp->b_bmode&BMWRAP) != 0 && c == ' ' && fillcol > 0
                && n>=0 && getccol(FALSE) > fillcol)
                {
                linsert(1, c);  /* we want the space */
                wrapword(FALSE, NULL);
                }
        if ((c>=0x20 && c<=0x7E)                /* Self inserting.      */
        ||  (c>=0xA0 && c<=0xFE)) {
                if (n <= 0) {                   /* Fenceposts.          */
                        lastflag = 0;
                        return (n<0 ? FALSE : TRUE);
                }
                thisflag = 0;                   /* For the future.      */
                status   = linsert(n, c);
                lastflag = thisflag;
                return (status);
        }
        lastflag = 0;                           /* Fake last flags.     */
        return (FALSE);
}
 
bindkey(code,name)
register short code;
register char *name;
{
        register KEYTAB *oktp,*nktp;
        int found = FALSE;
 
        nktp = &keytab[0];      /* bind to this key */
        oktp = &keytab[0];      /* the function to bind */
        /* look through to find keytab assoc with "name" */
        while (oktp < &keytab[NKEYTAB]) {
                if (strcmp(oktp->k_mfunc,name)==NULL) {
                        found = TRUE;
                        break;
                }
                ++oktp;
        }
        if (found)
                /* search for key */
                while (nktp < &keytab[NKEYTAB]) {
                        if (nktp->k_code == code) {
                                strcpy(nktp->k_mfunc,oktp->k_mfunc);
                                nktp->k_fp=oktp->k_fp;
                                return(TRUE);
                        }
                        ++nktp;
                }
        return(FALSE);
}
 
/* given a keycode value, return a pointer to the name of that function
 * and get the function's name into `name'.
 */
char *
getfname(kcode,name)
register short kcode;
char name[];
{
        register KEYTAB *ktp;
 
        ktp = &keytab[0];       /* the function to find */
        /* look through to find keytab assoc with "code" */
        while (ktp < &keytab[NKEYTAB]) {
                if (ktp->k_code == kcode)
                        {
                        strcpy(name,ktp->k_mfunc);
                        return(ktp->k_mfunc);
                        }
                ++ktp;
        }
        return((char *)NULL);   /* not found */
}
 
/* return the code associated with `name' */
short
getktpcode(name)
char name[];
{
        register KEYTAB *ktp;
 
        ktp = &keytab[0];       /* the function to find */
        /* look through to find keytab assoc with "name" */
        while (ktp < &keytab[NKEYTAB]) {
                if (strcmp(ktp->k_mfunc,name) == NULL)
                        return(ktp->k_code);
                ++ktp;
        }
        return(NULL);
}
SHAR_EOF
#       End of shell archive
exit 0
%NONAME-W-NOMSG, Message number 00000000

RDROYA01@ULKYVX.BITNET.UUCP (02/05/87)

#       This is a shell archive.
#       Remove everything above and including the cut line.
#       Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar:    Shell Archiver
#       Run the following text with /bin/sh to create:
#       termio.c
#       tty.c
#       ttgetc.s
# This archive created: Tue Feb  3 08:01:36 1987
cat << \SHAR_EOF > termio.c
/*
 * The functions in this file
 * negotiate with the operating system
 * for characters, and write characters in
 * a barely buffered fashion on the display.
 * All operating systems.
 */
#include        <stdio.h>
#include        <ctype.h>
#include        <osbind.h>
#include        "ed.h"
 
#if ST
#define BORDER  0
#define CURSOR  1
#define DESK    2
#define LETTER  3
#define BLACK   0
#define WHITE   0x777
#define COBALT  0x007
#define TEAL    0x055
#define GREEN   0x070
#define GREY    0x555
#define RED     0x700
#define MAGENTA 0x707
#define YELLOW  0x770
/* the first four are the saved values */
int     bordcol;        /* #0   */
int     curscol;        /* #1   */
int     deskcol;        /* #2   */
int     letcol;         /* #3   */
/* these are the internal values */
int     bcolor;         /* #0   */
int     ccolor;         /* #1   */
int     dcolor;         /* #2   */
int     lcolor;         /* #3   */
char    *colornames[4] = {"Magenta","Magenta","Magenta","Magenta"};
#endif
 
#if     VMS
#include        <stsdef.h>
#include        <ssdef.h>
#include        <descrip.h>
#include        <iodef.h>
#include        <ttdef.h>
 
#define NIBUF   128                     /* Input  buffer size           */
#define NOBUF   1024                    /* MM says bug buffers win!     */
#define EFN     0                       /* Event flag                   */
 
char    obuf[NOBUF];                    /* Output buffer                */
int     nobuf;                          /* # of bytes in above          */
char    ibuf[NIBUF];                    /* Input buffer                 */
int     nibuf;                          /* # of bytes in above          */
int     ibufi;                          /* Read index                   */
int     oldmode[2];                     /* Old TTY mode bits            */
int     newmode[2];                     /* New TTY mode bits            */
short   iochan;                         /* TTY I/O channel              */
#endif
 
#if     MSDOS
#include        <dos.h>
#endif
 
#if V7
#include        <sgtty.h>               /* for stty/gtty functions */
struct  sgttyb  ostate;                 /* saved tty state */
struct  sgttyb  nstate;                 /* values for editor mode */
#endif
 
/*
 * This function is called once
 * to set up the terminal device streams.
 * On VMS, it translates SYS$INPUT until it
 * finds the terminal, then assigns a channel to it
 * and sets it raw. On CPM it is a no-op.  On the ST
 * it is called to update the screen color and every
 * time we return from spawn or the term mode.
 */
ttopen()
{
#if     VMS
        struct  dsc$descriptor  idsc;
        struct  dsc$descriptor  odsc;
        char    oname[40];
        int     iosb[2];
        int     status;
 
        odsc.dsc$a_pointer = "SYS$INPUT";
        odsc.dsc$w_length  = strlen(odsc.dsc$a_pointer);
        odsc.dsc$b_dtype   = DSC$K_DTYPE_T;
        odsc.dsc$b_class   = DSC$K_CLASS_S;
        idsc.dsc$b_dtype   = DSC$K_DTYPE_T;
        idsc.dsc$b_class   = DSC$K_CLASS_S;
        do {
                idsc.dsc$a_pointer = odsc.dsc$a_pointer;
                idsc.dsc$w_length  = odsc.dsc$w_length;
                odsc.dsc$a_pointer = &oname[0];
                odsc.dsc$w_length  = sizeof(oname);
                status = LIB$SYS_TRNLOG(&idsc, &odsc.dsc$w_length, &odsc);
                if (status!=SS$_NORMAL && status!=SS$_NOTRAN)
                        exit(status);
                if (oname[0] == 0x1B) {
                        odsc.dsc$a_pointer += 4;
                        odsc.dsc$w_length  -= 4;
                }
        } while (status == SS$_NORMAL);
        status = SYS$ASSIGN(&odsc, &iochan, 0, 0);
        if (status != SS$_NORMAL)
                exit(status);
        status = SYS$QIOW(EFN, iochan, IO$_SENSEMODE, iosb, 0, 0,
                          oldmode, sizeof(oldmode), 0, 0, 0, 0);
        if (status!=SS$_NORMAL || (iosb[0]&0xFFFF)!=SS$_NORMAL)
                exit(status);
        newmode[0] = oldmode[0];
        newmode[1] = oldmode[1] | TT$M_PASSALL | TT$M_NOECHO;
        status = SYS$QIOW(EFN, iochan, IO$_SETMODE, iosb, 0, 0,
                          newmode, sizeof(newmode), 0, 0, 0, 0);
        if (status!=SS$_NORMAL || (iosb[0]&0xFFFF)!=SS$_NORMAL)
                exit(status);
#endif
#if     ST
        static char first = TRUE;
        char colornm[80];
        register int i;
        char temp[40];
 
        if (first)
                {
                i = 0;
                Crawio(0x1b);   /* clear and home */
                Crawio('E');
                bcolor = WHITE; /* border */
                strcpy(colorname[i++],"White");
                if(!alias(temp,"border"))
                        insalias("border","white");
                ccolor = WHITE; /* cursor */
                strcpy(colorname[i++],"White");
                if(!alias(temp,"cursor"))
                        insalias("cursor","white");
                dcolor = BLACK; /* screen (desk) */
                strcpy(colorname[i++],"Black");
                if(!alias(temp,"desk"))
                        insalias("desk","black");
                lcolor = WHITE; /* letters */
                strcpy(colorname[i++],"White");
                if(!alias(temp,"letter"))
                        insalias("letter","white");
                first = FALSE;
                }
        /* check for user preferences */
        i = 0;
        if (alias(colornm,"border"))
                {
                if (strcmp(colornm,"red")==NULL)
                        {
                        bcolor = RED;
                        strcpy(colornames[0],"Red");
                        }
                else if (strcmp(colornm,"black")==NULL)
                        {
                        bcolor = BLACK;
                        strcpy(colornames[0],"Black");
                        }
                else if (strcmp(colornm,"white")==NULL)
                        {
                        bcolor = WHITE;
                        strcpy(colornames[0],"White");
                        }
                else if (strcmp(colornm,"cobalt")==NULL)
                        {
                        bcolor = COBALT;
                        strcpy(colornames[0],"Cobalt");
                        }
                else if (strcmp(colornm,"green")==NULL)
                        {
                        bcolor = GREEN;
                        strcpy(colornames[0],"Green");
                        }
                else if (strcmp(colornm,"magenta")==NULL)
                        {
                        bcolor = MAGENTA;
                        strcpy(colornames[0],"Magenta");
                        }
                else if (strcmp(colornm,"yellow")==NULL)
                        {
                        bcolor = YELLOW;
                        strcpy(colornames[0],"Yellow");
                        }
                else if (strcmp(colornm,"grey")==NULL)
                        {
                        bcolor = GREY;
                        strcpy(colornames[0],"Grey");
                        }
                else if (strcmp(colornm,"teal")==NULL)
                        {
                        bcolor = TEAL;
                        strcpy(colornames[0],"Teal");
                        }
                }
        if (alias(colornm,"cursor"))
                {
                if (strcmp(colornm,"red")==NULL)
                        {
                        ccolor = RED;
                        strcpy(colornames[1],"Red");
                        }
                else if (strcmp(colornm,"black")==NULL)
                        {
                        ccolor = BLACK;
                        strcpy(colornames[1],"Black");
                        }
                else if (strcmp(colornm,"white")==NULL)
                        {
                        ccolor = WHITE;
                        strcpy(colornames[1],"White");
                        }
                else if (strcmp(colornm,"cobalt")==NULL)
                        {
                        ccolor = COBALT;
                        strcpy(colornames[1],"Cobalt");
                        }
                else if (strcmp(colornm,"green")==NULL)
                        {
                        ccolor = GREEN;
                        strcpy(colornames[1],"Green");
                        }
                else if (strcmp(colornm,"magenta")==NULL)
                        {
                        ccolor = MAGENTA;
                        strcpy(colornames[1],"Magenta");
                        }
                else if (strcmp(colornm,"yellow")==NULL)
                        {
                        ccolor = YELLOW;
                        strcpy(colornames[1],"Yellow");
                        }
                else if (strcmp(colornm,"grey")==NULL)
                        {
                        ccolor = GREY;
                        strcpy(colornames[1],"Grey");
                        }
                else if (strcmp(colornm,"teal")==NULL)
                        {
                        ccolor = TEAL;
                        strcpy(colornames[1],"Teal");
                        }
                }
        if (alias(colornm,"desk"))
                {
                if (strcmp(colornm,"red")==NULL)
                        {
                        dcolor = RED;
                        strcpy(colornames[2],"Red");
                        }
                else if (strcmp(colornm,"black")==NULL)
                        {
                        dcolor = BLACK;
                        strcpy(colornames[2],"Black");
                        }
                else if (strcmp(colornm,"white")==NULL)
                        {
                        dcolor = WHITE;
                        strcpy(colornames[2],"White");
                        }
                else if (strcmp(colornm,"cobalt")==NULL)
                        {
                        dcolor = COBALT;
                        strcpy(colornames[2],"Cobalt");
                        }
                else if (strcmp(colornm,"green")==NULL)
                        {
                        dcolor = GREEN;
                        strcpy(colornames[2],"Green");
                        }
                else if (strcmp(colornm,"magenta")==NULL)
                        {
                        dcolor = MAGENTA;
                        strcpy(colornames[2],"Magenta");
                        }
                else if (strcmp(colornm,"yellow")==NULL)
                        {
                        dcolor = YELLOW;
                        strcpy(colornames[2],"Yellow");
                        }
                else if (strcmp(colornm,"grey")==NULL)
                        {
                        dcolor = GREY;
                        strcpy(colornames[2],"Grey");
                        }
                else if (strcmp(colornm,"teal")==NULL)
                        {
                        dcolor = TEAL;
                        strcpy(colornames[2],"Teal");
                        }
                }
        if (alias(colornm,"letter"))
                {
                if (strcmp(colornm,"red")==NULL)
                        {
                        lcolor = RED;
                        strcpy(colornames[3],"Red");
                        }
                else if (strcmp(colornm,"black")==NULL)
                        {
                        lcolor = BLACK;
                        strcpy(colornames[3],"Black");
                        }
                else if (strcmp(colornm,"white")==NULL)
                        {
                        lcolor = WHITE;
                        strcpy(colornames[3],"White");
                        }
                else if (strcmp(colornm,"cobalt")==NULL)
                        {
                        lcolor = COBALT;
                        strcpy(colornames[3],"Cobalt");
                        }
                else if (strcmp(colornm,"green")==NULL)
                        {
                        lcolor = GREEN;
                        strcpy(colornames[3],"Green");
                        }
                else if (strcmp(colornm,"magenta")==NULL)
                        {
                        lcolor = MAGENTA;
                        strcpy(colornames[3],"Magenta");
                        }
                else if (strcmp(colornm,"yellow")==NULL)
                        {
                        lcolor = YELLOW;
                        strcpy(colornames[3],"Yellow");
                        }
                else if (strcmp(colornm,"grey")==NULL)
                        {
                        lcolor = GREY;
                        strcpy(colornames[3],"Grey");
                        }
                else if (strcmp(colornm,"teal")==NULL)
                        {
                        lcolor = TEAL;
                        strcpy(colornames[3],"Teal");
                        }
                }
        Setcolor(BORDER,bcolor);
        Setcolor(CURSOR,ccolor);
        Setcolor(DESK,dcolor);
        Setcolor(LETTER,lcolor);
        Crawio(0x1b);   /* No wrap mode */
        Crawio('w');
        Crawio(0x1b);   /* Turn on standout mode */
        Crawio('c');    /* background == dcolor # */
        Crawio('2');
        Crawio(0x1b);
        Crawio('b');    /* foreground == lcolor # */
        Crawio('3');
#endif
#if     MSDOS
#endif
#if     V7
        gtty(1, &ostate);                       /* save old state */
        gtty(1, &nstate);                       /* get base of new state */
        nstate.sg_flags |= RAW;
        nstate.sg_flags &= ~(ECHO|CRMOD);       /* no echo for now... */
        stty(1, &nstate);                       /* set mode */
#endif
}
 
/*
 * This function gets called just
 * before we go back home to the command interpreter.
 * On VMS it puts the terminal back in a reasonable state.
 * Another no-operation on ST.
 */
ttclose()
{
#if     VMS
        int     status;
        int     iosb[1];
 
        ttflush();
        status = SYS$QIOW(EFN, iochan, IO$_SETMODE, iosb, 0, 0,
                 oldmode, sizeof(oldmode), 0, 0, 0, 0);
        if (status!=SS$_NORMAL || (iosb[0]&0xFFFF)!=SS$_NORMAL)
                exit(status);
        status = SYS$DASSGN(iochan);
        if (status != SS$_NORMAL)
                exit(status);
#endif
#if     ST
        Crawio(0x1b);   /* turn off standout mode */
        Crawio('c');
        Crawio('0');
        Crawio(0x1b);
        Crawio('b');
        Crawio('3');
        Setcolor(BORDER,bordcol);
        Setcolor(CURSOR,curscol);
        Setcolor(DESK,deskcol); /* reset color the way I like it */
        Setcolor(LETTER,letcol);
#endif
#if     MSDOS
#endif
#if     V7
        stty(1, &ostate);
#endif
}
 
/*
 * Write a character to the display.
 * On VMS, terminal output is buffered, and
 * we just put the characters in the big array,
 * after cheching for overflow. On ST terminal I/O
 * unbuffered, so we just write the byte out.
 * Ditto on MS-DOS (use the very very raw console
 * output routine).
 */
#if ST  /* use assembly */
#else
ttputc(c)
register int c;
{
#if     VMS
        if (nobuf >= NOBUF)
                ttflush();
        obuf[nobuf++] = c;
#endif
#if     MSDOS
        dosb(CONDIO, c, 0);
#endif
#if     V7
        fputc(c, stdout);
#endif
}
#endif
/*
 * Flush terminal buffer. Does real work
 * where the terminal output is buffered up. A
 * no-operation on systems where byte at a time
 * terminal I/O is done.
 */
ttflush()
{
#if     VMS
        int     status;
        int     iosb[2];
 
        status = SS$_NORMAL;
        if (nobuf != 0) {
                status = SYS$QIOW(EFN, iochan, IO$_WRITELBLK|IO$M_NOFORMAT,
                         iosb, 0, 0, obuf, nobuf, 0, 0, 0, 0);
                if (status == SS$_NORMAL)
                        status = iosb[0] & 0xFFFF;
                nobuf = 0;
        }
        return (status);
#endif
#if     ST
#endif
#if     MSDOS
#endif
#if     V7
        fflush(stdout);
#endif
}
 
/*
 * Read a character from the terminal,
 * performing no editing and doing no echo at all.
 * More complex in VMS that almost anyplace else, which
 * figures. Very simple on ST, because the system can
 * do exactly what you want.
 */
#if     ST
/* ST needs to get scancode, so we do it in assembly */
#else
ttgetc()
{
#if     VMS
        int     status;
        int     iosb[2];
        int     term[2];
 
        while (ibufi >= nibuf) {
                ibufi = 0;
                term[0] = 0;
                term[1] = 0;
                status = SYS$QIOW(EFN, iochan, IO$_READLBLK|IO$M_TIMED,
                         iosb, 0, 0, ibuf, NIBUF, 0, term, 0, 0);
                if (status != SS$_NORMAL)
                        exit(status);
                status = iosb[0] & 0xFFFF;
                if (status!=SS$_NORMAL && status!=SS$_TIMEOUT)
                        exit(status);
                nibuf = (iosb[0]>>16) + (iosb[1]>>16);
                if (nibuf == 0) {
                        status = sys$qiow(EFN, iochan, IO$_READLBLK,
                                 iosb, 0, 0, ibuf, 1, 0, term, 0, 0);
                        if (status != SS$_NORMAL
                        || (status = (iosb[0]&0xFFFF)) != SS$_NORMAL)
                                exit(status);
                        nibuf = (iosb[0]>>16) + (iosb[1]>>16);
                }
        }
        return (ibuf[ibufi++] & 0xFF);          /* Allow multinational  */
#endif
#if     MSDOS
        return (dosb(CONRAW, 0, 0));
#endif
#if     V7
        return(fgetc(stdin));
#endif
}
#endif
#if ST
/* save current color values so they can be reinstalled on exit */
 
savecolor()
{
        bordcol=(int)Getcolor(BORDER);
        deskcol=(int)Getcolor(DESK);
        curscol=(int)Getcolor(CURSOR);
        letcol=(int)Getcolor(LETTER);
}
 
paintbuffer(f,n)
register int f, n;
{
        char mycolor[20];
        char clrtwo[20];
        register char *ptr;
        extern char *index();
 
        if((f=mlreply("New background color: ",mycolor,12)) != TRUE)
                return(f);
        /* don't fail because of stray spaces */
        if ((ptr=index(mycolor,' '))!=NULL)
                *ptr = '\0';
        else if ((ptr=index(mycolor,'\t'))!=NULL)
                *ptr = '\0';
        n = strlen(mycolor);
        /* adjust case */
        for (f=0;f<=n;f++)
                if (f == 0)
                        mycolor[f] = toupper(mycolor[f]);
                else
                        mycolor[f] = tolower(mycolor[f]);
        for (f=0;f<=LETTER;f++)
                {
                if (strcmp(colornames[f],mycolor)==0)
                        {
                        switch(f)
                                {
                                case BORDER:
                                        strcpy(mycolor,colornames[BORDER]);
                                        mycolor[0] = tolower(mycolor[0]);
                                        strcpy(clrtwo,colornames[DESK]);
                                        clrtwo[0] = tolower(clrtwo[0]);
                                        insalias("desk",mycolor);
                                        insalias("border",clrtwo);
                                        if (strcmp(colornames[BORDER],
                                            colornames[LETTER])==NULL)
                                                insalias("letter",clrtwo);
                                        break;
                                case CURSOR:
                                        strcpy(mycolor,colornames[CURSOR]);
                                        mycolor[0] = tolower(mycolor[0]);
                                        strcpy(clrtwo,colornames[DESK]);
                                        clrtwo[0] = tolower(clrtwo[0]);
                                        insalias("desk",mycolor);
                                        insalias("cursor",clrtwo);
                                        break;
                                case DESK:      /* no sense in this */
                                        break;
                                case LETTER:
                                        strcpy(mycolor,colornames[LETTER]);
                                        mycolor[0] = tolower(mycolor[0]);
                                        strcpy(clrtwo,colornames[DESK]);
                                        clrtwo[0] = tolower(clrtwo[0]);
                                        insalias("desk",mycolor);
                                        insalias("letter",clrtwo);
                                        break;
                                default:
                                        return(FALSE);
                                }
                        ttopen();
                        return(TRUE);
                        }
                }
        mlwrite("No match");
        return(FALSE);
}
 
#else
paintbuffer(f,n)
int f,n;
{
        mlwrite("Buy a color computer like an ST");
        return(FALSE);
}
#endif
SHAR_EOF
cat << \SHAR_EOF > tty.c
/*
 * The routines in this file
 * provide support for Atari ST terminals
 * over a serial line. The serial I/O services are
 * provided by routines in "osbind.h".
 */
#include        <stdio.h>
#include        "ed.h"
 
#if     ST
#include <osbind.h>
 
#define NROW    25                      /* Screen size.                 */
#define NCOL    80                      /* Edit if you want to.         */
#define MARGIN  8                       /* Size of minimum margin and   */
#define SCRSIZ  64                      /* Scroll size of extended lines*/
#define BIAS    0x20                    /* Origin 0 coordinate bias.    */
#define ESC     0x1B                    /* ESC character.               */
#define BEL     0x07                    /* ascii bell character         */
 
extern  int     ttopen();               /* Forward references.          */
extern  int     ttgetc();
extern  int     ttputc();
extern  int     ttflush();
extern  int     ttclose();
extern  int     stmove();
extern  int     steeol();
extern  int     steeop();
extern  int     stbeep();
extern  int     stopen();
extern  int     strev();
 
/*
 * Dispatch table. All the
 * hard fields just point into the
 * terminal I/O code.
 */
TERM    term    = {
        NROW-1,
        NCOL,
        MARGIN,
        SCRSIZ,
        &stopen,
        &ttclose,
        &ttgetc,
        &ttputc,
        &ttflush,
        &stmove,
        &steeol,
        &steeop,
        &stbeep,
        &strev
};
 
stmove(row, col)
{
        Bconout(2,ESC);
        Bconout(2,'Y');
        Bconout(2,row+BIAS);
        Bconout(2,col+BIAS);
}
 
steeol()
{
        Bconout(2,ESC);
        Bconout(2,'K');
}
 
steeop()
{
        Bconout(2,ESC);
        Bconout(2,'J');
}
 
stbeep()
{
#ifdef  BEL
        Bconout(2,BEL);
        ttflush();
#endif
}
 
strev(status)
int status;
{
        if (status)
                {
                Bconout(2,ESC);
                Bconout(2,'p');
                }
        else
                {
                Bconout(2,ESC);
                Bconout(2,'q');
                }
}
 
stopen()
{
#if     V7
        register char *cp;
        char *getenv();
 
        if ((cp = getenv("TERM")) == NULL) {
                puts("Shell variable TERM not defined!");
                exit(1);
        }
        if (strcmp(cp, "st") != 0 && strcmp(cp, "z19") != 0) {
                puts("Terminal type not 'st25' or 'vt52' !");
                exit(1);
        }
#endif
        ttopen();
}
#endif
 
SHAR_EOF
cat << \SHAR_EOF > ttgetc.s
                .globl  _ttgetc
                .globl  _ttputc
                .globl  _scancode
                .globl  _shiftstatus
                .text
_ttgetc:        link    A6,#0
                movem.l D6-D7,-(sp)
                move    #7,(sp)
                trap    #1
                move.l  D0,D7
                swap    d7
                move.w  d7,_scancode
                move.w  #$ffff,(sp)
                move.w  #$b,-(sp)
                trap    #13
                addq.l  #2,sp
                move.w  d0,_shiftstatus
                swap    d7
                move.l  d7,d0
                and.l   #$7f,D0
                tst.l   (sp)+
                movem.l (sp)+,D7-D7
                unlk    A6
                rts
_ttputc:        link    A6,#0
                movem.l d6-d7,-(sp)
                move    $8(A6),(sp)
                and     #127,(sp)
                move    #6,-(sp)
                trap    #1
                addq.l  #2,sp
                tst.l   (sp)+
                movem.l (sp)+,R7-R7
                unlk    A6
                rts
 
                .bss
_scancode:      ds.w    1
_shiftstatus:   ds.w    1
SHAR_EOF
#       End of shell archive
exit 0
%NONAME-W-NOMSG, Message number 00000000

RDROYA01@ULKYVX.BITNET.UUCP (02/05/87)

#       This is a shell archive.
#       Remove everything above and including the cut line.
#       Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar: Shell Archiver
#       Run the following text with /bin/sh to create:
#       shell.c
#       path.c
#       page.c
#       window.c
# This archive created: Tue Feb  3 18:08:00 1987
cat << \SHAR_EOF > shell.c
/* shell.c contains the routines to allow exec calls.  If there were a
 * small command interpreter somewhere, we could do a real spawn.
 * If you compile this to run from a shell, undefine GEMLOAD.  Since
 * shells eat memory, you can't give as much back to the OS.  If you
 * will be using this from GEM, give back 256K and you'll be able to
 * run just about any program from within the editor.
 */
 
#include "shell.h"
 
#ifdef  GEMLOAD
unsigned long _STKSIZ = -262144;        /* give back 256K to the OS */
#else
unsigned long _STKSIZ = -147456;        /* give back 144K to the OS */
#endif
 
char cline[MAXINPUT * 2];       /* holds the command line and other things */
char pnam[MAXINPUT+1];          /* program name */
char source[MAXINPUT+1];        /* name of file for aliasing */
char path[MAXPATH+1];           /* current path string  */
int delete = TRUE;              /* on/off for above     */
int dolink = 0;                 /* link a program       */
int temdrv;
int curdrv;
ALITAB *aheadp = NULL;          /* alias header */
 
shell(f, n)
register int f, n;
{
        extern char *alias(), *index(), *rindex();
        register char *ptr;
 
        if ((f=mlreply("!: ",cline,MAXINPUT))!=TRUE)
                return(f);
        if ((ptr=index(cline,' '))!=(char *)NULL)
                {
                *ptr = '\0';
                strcpy(pnam,cline);
                strcpy(&cline[1],++ptr);
                }
        else
                {
                strcpy(pnam,cline);
                strcpy(&cline[1]," ");
                }
        if (pnam[0] == '~')
                parsefn(pnam);
        if (alias(source,pnam)!=(char *)NULL)
                strcpy(pnam,source);
        /* check for file type (prg) and append if not there */
        if ((ptr=rindex(pnam,'\\'))!=(char *)NULL)      /* don't be fooled */
                {                                       /* by \foo.dir\bar */
                if (!index(ptr,'.'))
                        strcat(ptr,".prg");
                }
        else if (rindex(pnam,'.')==(char *)NULL)
                strcat(pnam,".prg");
        if (cline[1] == '~')
                parsefn(&cline[1]);
        while((ptr=index(&cline[1], '~'))!=(char *)NULL)
                if (!parsefn(ptr))
                        break;
        cline[0] = (char)strlen(&cline[1]);
        ttclose();      /* reset terminal characteristics */
        ttputc(0x1b); ttputc('E');
        Setexc(2,buserr);       /* use default exception handlers */
        Setexc(3,adderr);
        f = (int)Pexec(0,pnam,cline,0L);
        Setexc(2,&errexit);     /* reset local handlers */
        Setexc(3,&errexit);
        mtwrite("[Type a <CR>]");
        ttgetc();
        ttopen();       /* turn on uemail screen */
        if (f == FALSE && n == HUGE)    /* called from term */
                ;
        else
                {
                sgarbf = TRUE;  /* force full redraw */
                upmode();       /* set colors */
                update();       /* fix screen */
                }
        mlwrite("Return from %s = %d",pnam,f);
        return(TRUE);
}
 
/* read in the cc.ini file if it's on the default drive.  Called once at
 * editor startup and later by command.  Bound to CTRL-C.
 */
commfil(f,n)
register int f,n;
{
        FILE *fopen();
        register FILE *fp;
        char *fgets(),*index();
        char *ptr;
        static char line[MAXINPUT+1];
 
        /* for the call from main */
        if (f == FALSE && n == HUGE)
                {
                if ((fp=fopen("cc.ini","r")) == NULL)
                        {
                        Dgetpath(path,0);
                        mlwrite("Cannot open %s\\cc.ini",path);
                        return(FALSE);
                        }
                }
        else
                {
                if ((f=mlreply("Command file: ",line,MAXINPUT))!=TRUE)
                        return(f);
                if (line[0] == '~')
                        parsefn(line);
                if ((fp=fopen(line, "r"))==NULL)
                        {
                        mlwrite("Cannot open %s",line);
                        return(FALSE);
                        }
                }
        while (fgets(line,MAXINPUT,fp))
                {
                /* kill trailing whitespace */
                if ((ptr=index(line,'\n'))!=(char *)NULL)
                        *ptr = '\0';
                if ((ptr=index(line,' '))!=(char *)NULL)
                        *ptr = '\0';
                if ((ptr=index(line,'\t'))!=(char *)NULL)
                        *ptr = '\0';
                /* terminate alias and get address of path */
                if ((ptr=index(line,'='))!=(char *)NULL)
                        *ptr = '\0';
                else    /* improper line */
                        continue;
                ++ptr;  /* now equals &path[0] */
                /* test two cases that aren't easy in a switch */
                if (strcmp(line,"F10")==NULL)
                        {
                        bindkey((SPEC|'D'),ptr);
                        continue;
                        }
                if (strcmp(line,"NENTER")==NULL)
                        {
                        bindkey((SPEC|'r'),ptr);
                        continue;
                        }
                f = strlen(line);
                n = strlen(ptr);
                /* bind function keys */
                if (line[0] == 'F' && f == 2)
                        {
                        switch(line[1])
                                {
                                case '1':
                                        bindkey((SPEC|';'),ptr);
                                        break;
                                case '2':
                                        bindkey((SPEC|'<'),ptr);
                                        break;
                                case '3':
                                        bindkey((SPEC|'='),ptr);
                                        break;
                                case '4':
                                        bindkey((SPEC|'>'),ptr);
                                        break;
                                case '5':
                                        bindkey((SPEC|'?'),ptr);
                                        break;
                                case '6':
                                        bindkey((SPEC|'@'),ptr);
                                        break;
                                case '7':
                                        bindkey((SPEC|'A'),ptr);
                                        break;
                                case '8':
                                        bindkey((SPEC|'B'),ptr);
                                        break;
                                case '9':
                                        bindkey((SPEC|'C'),ptr);
                                        break;
                                default:
                                        break;
                                }
                        }
                /* or maybe a number pad key */
                else if (line[0] == 'N' && f == 2)
                        {
                        switch(line[1])
                                {
                                case '(':
                                        bindkey((SPEC|'c'),ptr);
                                        break;
                                case ')':
                                        bindkey((SPEC|'d'),ptr);
                                        break;
                                case '/':
                                        bindkey((SPEC|'e'),ptr);
                                        break;
                                case '*':
                                        bindkey((SPEC|'f'),ptr);
                                        break;
                                case '7':
                                        bindkey((SPEC|'g'),ptr);
                                        break;
                                case '8':
                                        bindkey((SPEC|'h'),ptr);
                                        break;
                                case '9':
                                        bindkey((SPEC|'i'),ptr);
                                        break;
                                case '-':
                                        bindkey((SPEC|'J'),ptr);
                                        break;
                                case '4':
                                        bindkey((SPEC|'j'),ptr);
                                        break;
                                case '5':
                                        bindkey((SPEC|'k'),ptr);
                                        break;
                                case '6':
                                        bindkey((SPEC|'l'),ptr);
                                        break;
                                case '+':
                                        bindkey((SPEC|'N'),ptr);
                                        break;
                                case '1':
                                        bindkey((SPEC|'m'),ptr);
                                        break;
                                case '2':
                                        bindkey((SPEC|'n'),ptr);
                                        break;
                                case '3':
                                        bindkey((SPEC|'o'),ptr);
                                        break;
                                case '.':
                                        bindkey((SPEC|'q'),ptr);
                                        break;
                                default:
                                        break;
                                }
                        }
                /* search for a match */
                /* check possible alias definitions */
                else if (insalias(line,ptr)==FALSE)
                                {
                                fclose(fp);
                                return(FALSE);
                                }
                else if (feof(fp))
                        break;
                }
        fclose(fp);
        return(TRUE);
}
 
insalias(al,pa)
register char *al;
register char *pa;
{
        register int f, n;
        register ALITAB *apt;
        register int found;
 
        found = FALSE;
 
        if (aheadp == NULL)
                {
                if ((aheadp=(ALITAB *)malloc(sizeof(ALITAB)))==NULL)
                        {
                        mlwrite("Cannot allocate %d bytes",sizeof(ALITAB));
                        return(FALSE);
                        }
                if ((aheadp->alias=malloc(5))==NULL)
                        {
                        mlwrite("Alias alloc failure");
                        return(FALSE);
                        }
                if ((aheadp->value=malloc(11))==NULL)
                        {
                        mlwrite("Alias alloc failure");
                        return(FALSE);
                        }
                strcpy(aheadp->alias,"home");
                strcpy(aheadp->value,"c:\\uemail\\");
                aheadp->a_forw = NULL;
                }
        n = strlen(pa);
        f = strlen(al);
        apt = aheadp;
        while (apt)
                {
                if (strcmp(apt->alias,al)==NULL)
                        {
                        free(apt->value);
                        if((apt->value=malloc(n+1))==NULL)
                                {
                                mlwrite("Alias alloc failure %s",pa);
                                return(FALSE);
                                }
                        strcpy(apt->value,pa);
                        found == TRUE;
                        }
                if (apt->a_forw)
                        apt=apt->a_forw;
                else
                        break;
                }
        if (found)
                return(TRUE);
        /* not already bound */
        if ((apt->a_forw=(ALITAB *)malloc(sizeof(ALITAB)))== NULL)
                {
                mlwrite("Cannot allocate %d bytes",sizeof(ALITAB));
                return(FALSE);
                }
        apt=apt->a_forw;
        apt->a_forw=NULL;
        if ((apt->alias=malloc(f+1))==NULL)
                {
                mlwrite("Alias alloc failure %s",al);
                return(FALSE);
                }
        if ((apt->value=malloc(n+1))==NULL)
                {
                mlwrite("Alias alloc failure %s",pa);
                return(FALSE);
                }
        strcpy(apt->alias,al);
        strcpy(apt->value,pa);
        return(TRUE);
}
SHAR_EOF
cat << \SHAR_EOF > path.c
/* PATH.C  commands that deal with paths are in here */
 
#include "shell.h"
 
/* SETPATH set the current path and default drive.  This is needed on the
 * ST for shell programs that expand wildcards incorrectly when given
 * a command line which requests a drive other than the default.  The
 * command also uses the aliases listed above.  Bound to CTLX-D.
 */
setpath(f, n)
int f, n;
{
        register char *ptr;
        extern char *index(),*alias();
        char template[MAXINPUT+1];
        char subdir[MAXINPUT+1];        /* handle multiple path */
 
        if (n == 0)
                {
                f = (int)Dgetdrv();
                Dgetpath(path,(f+1));
                mlwrite("Default drive: %c: Default path: %s",(f + 'A'),path);
                return(TRUE);
                }
        mlreply("New drive and path: ",template,MAXINPUT-1);
        if (template[0] == '~') /* use alias if possible */
                {
                if ((ptr=index(template,' '))!=(char *)NULL)
                        *ptr = '\0';
                if ((ptr=index(template,'\t'))!=(char *)NULL)
                        *ptr = '\0';
                if ((ptr=index(template,'\\'))!=(char *)NULL)
                        {
                        *ptr = '\0';
                        ++ptr;
                        strcpy(subdir,ptr);
                        n = 0;
                        }
                if (alias(&path[0],&template[1]) == (char *)NULL)
                        return(FALSE);
                if (n==0)
                        strcat(path,subdir);
                }
        else
                strcpy(path,template);
        if ((ptr=index(path,':'))!=(char *)NULL)
                {
                --ptr;
                f = (int)toupper(*ptr);
                if (f < 'A' || f > 'P')
                        {
                        mlwrite("Illegal drive specification %c:",f);
                        return(FALSE);
                        }
                f -= 'A';
                Dsetdrv(f);
                }
        Dsetpath(path);
        return(TRUE);
}
 
/* look up the string template in the known aliased names.  If it's a
 * known alias, copy the actual path into dirpath and return a pointer
 * to dirpath.  If it is not legit, complain and return NULL.  Called
 * by any command that looks at a file on disk.
 */
char *
alias(dirpath, template)
register char *dirpath;
register char *template;
{
        register ALITAB *apt;
 
        apt = aheadp;
        if (apt == NULL)
                {
                mlwrite("No aliases defined");
                return((char *)NULL);
                }
        while (apt)
                {
                if (strcmp(template,apt->alias)==NULL)
                        {
                        strncpy(dirpath,apt->value,MAXINPUT);
                        return(dirpath);
                        }
                apt=apt->a_forw;
                }
        return((char *)NULL);
}
SHAR_EOF
cat << \SHAR_EOF > page.c
#include <stdio.h>
#include "ed.h"
 
int pagelen = 60;       /* default page length */
 
/* PAGINATE : eXtended command  Set mark, goto beginning of file and move
 * beginning to end inserting formfeeds.  Bound to CTLX-!.
 */
 
paginate(f, n)
register int f, n;
{
        setmark(NULL, 1);
        gotobob(NULL, 1);
 
        while(pageforw(NULL, 1) != FALSE)
                {
                openline(NULL, 1);
                linsert(1 ,'\f');
                }
        return(swapmark(NULL, 1));
}
 
/* SETPAGE eXtended command  Set page length for paginate.  Or tell page
 * length already set.  Bound to CTLX-#.
 */
 
setpage(f ,n)
register int f, n;
{
 
        if (n > 1 && n < 100)
                pagelen = n;
 
        mlwrite ("Page length is %d", pagelen);
                return(TRUE);
}
 
/* PAGEFORW eXtended command  move forward by set page size.  Bound to
 * CTLX-+.
 */
 
pageforw(f, n)
register int f, n;
{
        register int c;
 
        c = 0;
 
        if (n < 0)
                return(pageback(f, -n));
        while(n)
                {
                if (curwp->w_dotp != curbp->b_linep)
                        {
                        forwline(NULL, 1);
                        ++c;
                        if (c == pagelen)
                                {
                                c = 0;
                                --n;
                                continue;
                                }
                        }
                else
                        return(FALSE);
                }
        return(TRUE);
}
 
/* PAGEBACK eXtended command  move forward by set page size.  Bound to
 * CTLX-+.
 */
 
pageback(f, n)
register int f, n;
{
        register int c;
 
        c = 0;
 
        if (n < 0)
                return(pageforw(f, -n));
        while(n)
                {
                if (lback(curwp->w_dotp) != curbp->b_linep)
                        {
                        backline(NULL, 1);
                        ++c;
                        if (c == pagelen)
                                {
                                c = 0;
                                --n;
                                continue;
                                }
                        }
                else
                        return(FALSE);
                }
        return(TRUE);
}
SHAR_EOF
cat << \SHAR_EOF > window.c
/*
 * Window management.
 * Some of the functions are internal,
 * and some are attached to keys that the
 * user actually types.
 */
#include        <stdio.h>
#include        "ed.h"
 
/*
 * Reposition dot in the current
 * window to line "n". If the argument is
 * positive, it is that line. If it is negative it
 * is that line from the bottom. If it is 0 the window
 * is centered (this is what the standard redisplay code
 * does). With no argument it defaults to 1. Bound to
 * M-!. Because of the default, it works like in
 * Gosling.
 */
reposition(f, n)
register int f, n;
{
        curwp->w_force = n;
        curwp->w_flag |= WFFORCE;
        return (TRUE);
}
 
/*
 * Refresh the screen. With no
 * argument, it just does the refresh. With an
 * argument it recenters "." in the current
 * window. Bound to "C-L".
 */
refresh(f, n)
register int f, n;
{
        if (f == FALSE)
                sgarbf = TRUE;
        else {
                curwp->w_force = 0;             /* Center dot.          */
                curwp->w_flag |= WFFORCE;
        }
        return (TRUE);
}
 
/*
 * The command make the next
 * window (next => down the screen)
 * the current window. There are no real
 * errors, although the command does
 * nothing if there is only 1 window on
 * the screen. Bound to "C-X C-N".
 */
nextwind(f, n)
register int f, n;
{
        register WINDOW *wp;
        register WINDOW *wp2;
 
        if ((wp=curwp->w_wndp) == NULL)
                wp = wheadp;
        curwp = wp;
        curbp = wp->w_bufp;
        upmode();
        return (TRUE);
}
 
/*
 * This command makes the previous
 * window (previous => up the screen) the
 * current window. There arn't any errors,
 * although the command does not do a lot
 * if there is 1 window.
 */
prevwind(f, n)
register int f, n;
{
        register WINDOW *wp1;
        register WINDOW *wp2;
 
        wp1 = wheadp;
        wp2 = curwp;
        if (wp1 == wp2)
                wp2 = NULL;
        while (wp1->w_wndp != wp2)
                wp1 = wp1->w_wndp;
        curwp = wp1;
        curbp = wp1->w_bufp;
        upmode();
        return (TRUE);
}
 
/*
 * This command moves the current
 * window down by "arg" lines. Recompute
 * the top line in the window. The move up and
 * move down code is almost completely the same;
 * most of the work has to do with reframing the
 * window, and picking a new dot. We share the
 * code by having "move down" just be an interface
 * to "move up". Magic. Bound to "C-X C-N".
 */
mvdnwind(f, n)
register int    f, n;
{
        return (mvupwind(f, -n));
}
 
/*
 * Move the current window up by "arg"
 * lines. Recompute the new top line of the window.
 * Look to see if "." is still on the screen. If it is,
 * you win. If it isn't, then move "." to center it
 * in the new framing of the window (this command does
 * not really move "."; it moves the frame). Bound
 * to "C-X C-P".
 */
mvupwind(f, n)
register int    f, n;
{
        register LINE   *lp;
        register int    i;
 
        lp = curwp->w_linep;
        if (n < 0) {
                while (n++ && lp!=curbp->b_linep)
                        lp = lforw(lp);
        } else {
                while (n-- && lback(lp)!=curbp->b_linep)
                        lp = lback(lp);
        }
        curwp->w_linep = lp;
        curwp->w_flag |= WFHARD;                /* Mode line is OK.     */
        for (i=0; i<curwp->w_ntrows; ++i) {
                if (lp == curwp->w_dotp)
                        return (TRUE);
                if (lp == curbp->b_linep)
                        break;
                lp = lforw(lp);
        }
        lp = curwp->w_linep;
        i  = curwp->w_ntrows/2;
        while (i-- && lp!=curbp->b_linep)
                lp = lforw(lp);
        curwp->w_dotp  = lp;
        curwp->w_doto  = 0;
        return (TRUE);
}
 
/*
 * This command makes the current
 * window the only window on the screen.
 * Bound to "C-X 1". Try to set the framing
 * so that "." does not have to move on
 * the display. Some care has to be taken
 * to keep the values of dot and mark
 * in the buffer structures right if the
 * distruction of a window makes a buffer
 * become undisplayed.
 */
onlywind(f, n)
register int f, n;
{
        register WINDOW *wp;
        register LINE   *lp;
        register int    i;
 
        while (wheadp != curwp) {
                wp = wheadp;
                wheadp = wp->w_wndp;
                if (--wp->w_bufp->b_nwnd == 0) {
                        wp->w_bufp->b_dotp  = wp->w_dotp;
                        wp->w_bufp->b_doto  = wp->w_doto;
                        wp->w_bufp->b_markp = wp->w_markp;
                        wp->w_bufp->b_marko = wp->w_marko;
                }
                free((char *) wp);
        }
        while (curwp->w_wndp != NULL) {
                wp = curwp->w_wndp;
                curwp->w_wndp = wp->w_wndp;
                if (--wp->w_bufp->b_nwnd == 0) {
                        wp->w_bufp->b_dotp  = wp->w_dotp;
                        wp->w_bufp->b_doto  = wp->w_doto;
                        wp->w_bufp->b_markp = wp->w_markp;
                        wp->w_bufp->b_marko = wp->w_marko;
                }
                free((char *) wp);
        }
        lp = curwp->w_linep;
        i  = curwp->w_toprow;
        while (i!=0 && lback(lp)!=curbp->b_linep) {
                --i;
                lp = lback(lp);
        }
        curwp->w_toprow = 0;
        curwp->w_ntrows = term.t_nrow-1;
        curwp->w_linep  = lp;
        curwp->w_flag  |= WFMODE|WFHARD;
        return (TRUE);
}
 
/*
 * Split the current window. A window
 * smaller than 3 lines cannot be split.
 * The only other error that is possible is
 * a "malloc" failure allocating the structure
 * for the new window. Bound to "C-X 2".
 */
splitwind(f, n)
register int f, n;
{
        register WINDOW *wp;
        register LINE   *lp;
        register int    ntru;
        register int    ntrl;
        register int    ntrd;
        register WINDOW *wp1;
        register WINDOW *wp2;
 
        if (curwp->w_ntrows < 3) {
                mlwrite("Cannot split a %d line window", curwp->w_ntrows);
                return (FALSE);
        }
        if ((wp = (WINDOW *) malloc(sizeof(WINDOW))) == NULL) {
                mlwrite("Cannot allocate WINDOW block");
                return (FALSE);
        }
        ++curbp->b_nwnd;                        /* Displayed twice.     */
        wp->w_bufp  = curbp;
        wp->w_dotp  = curwp->w_dotp;
        wp->w_doto  = curwp->w_doto;
        wp->w_markp = curwp->w_markp;
        wp->w_marko = curwp->w_marko;
        wp->w_flag  = 0;
        wp->w_force = 0;
        ntru = (curwp->w_ntrows-1) / 2;         /* Upper size           */
        ntrl = (curwp->w_ntrows-1) - ntru;      /* Lower size           */
        lp = curwp->w_linep;
        ntrd = 0;
        while (lp != curwp->w_dotp) {
                ++ntrd;
                lp = lforw(lp);
        }
        lp = curwp->w_linep;
        if (ntrd <= ntru) {                     /* Old is upper window. */
                if (ntrd == ntru)               /* Hit mode line.       */
                        lp = lforw(lp);
                curwp->w_ntrows = ntru;
                wp->w_wndp = curwp->w_wndp;
                curwp->w_wndp = wp;
                wp->w_toprow = curwp->w_toprow+ntru+1;
                wp->w_ntrows = ntrl;
        } else {                                /* Old is lower window  */
                wp1 = NULL;
                wp2 = wheadp;
                while (wp2 != curwp) {
                        wp1 = wp2;
                        wp2 = wp2->w_wndp;
                }
                if (wp1 == NULL)
                        wheadp = wp;
                else
                        wp1->w_wndp = wp;
                wp->w_wndp   = curwp;
                wp->w_toprow = curwp->w_toprow;
                wp->w_ntrows = ntru;
                ++ntru;                         /* Mode line.           */
                curwp->w_toprow += ntru;
                curwp->w_ntrows  = ntrl;
                while (ntru--)
                        lp = lforw(lp);
        }
        curwp->w_linep = lp;                    /* Adjust the top lines */
        wp->w_linep = lp;                       /* if necessary.        */
        curwp->w_flag |= WFMODE|WFHARD;
        wp->w_flag |= WFMODE|WFHARD;
        return (TRUE);
}
 
/*
 * Enlarge the current window.
 * Find the window that loses space. Make
 * sure it is big enough. If so, hack the window
 * descriptions, and ask redisplay to do all the
 * hard work. You don't just set "force reframe"
 * because dot would move. Bound to "C-X Z".
 */
enlargewind(f, n)
register int f, n;
{
        register WINDOW *adjwp;
        register LINE   *lp;
        register int    i;
 
        if (n < 0)
                return (shrinkwind(f, -n));
        if (wheadp->w_wndp == NULL) {
                mlwrite("Only one window");
                return (FALSE);
        }
        if ((adjwp=curwp->w_wndp) == NULL) {
                adjwp = wheadp;
                while (adjwp->w_wndp != curwp)
                        adjwp = adjwp->w_wndp;
        }
        if (adjwp->w_ntrows <= n) {
                mlwrite("Impossible change");
                return (FALSE);
        }
        if (curwp->w_wndp == adjwp) {           /* Shrink below.        */
                lp = adjwp->w_linep;
                for (i=0; i<n && lp!=adjwp->w_bufp->b_linep; ++i)
                        lp = lforw(lp);
                adjwp->w_linep  = lp;
                adjwp->w_toprow += n;
        } else {                                /* Shrink above.        */
                lp = curwp->w_linep;
                for (i=0; i<n && lback(lp)!=curbp->b_linep; ++i)
                        lp = lback(lp);
                curwp->w_linep  = lp;
                curwp->w_toprow -= n;
        }
        curwp->w_ntrows += n;
        adjwp->w_ntrows -= n;
        curwp->w_flag |= WFMODE|WFHARD;
        adjwp->w_flag |= WFMODE|WFHARD;
        return (TRUE);
}
 
/*
 * Shrink the current window.
 * Find the window that gains space. Hack at
 * the window descriptions. Ask the redisplay to
 * do all the hard work. Bound to "C-X C-Z".
 */
shrinkwind(f, n)
register int f, n;
{
        register WINDOW *adjwp;
        register LINE   *lp;
        register int    i;
 
        if (n < 0)
                return (enlargewind(f, -n));
        if (wheadp->w_wndp == NULL) {
                mlwrite("Only one window");
                return (FALSE);
        }
        if ((adjwp=curwp->w_wndp) == NULL) {
                adjwp = wheadp;
                while (adjwp->w_wndp != curwp)
                        adjwp = adjwp->w_wndp;
        }
        if (curwp->w_ntrows <= n) {
                mlwrite("Impossible change");
                return (FALSE);
        }
        if (curwp->w_wndp == adjwp) {           /* Grow below.          */
                lp = adjwp->w_linep;
                for (i=0; i<n && lback(lp)!=adjwp->w_bufp->b_linep; ++i)
                        lp = lback(lp);
                adjwp->w_linep  = lp;
                adjwp->w_toprow -= n;
        } else {                                /* Grow above.          */
                lp = curwp->w_linep;
                for (i=0; i<n && lp!=curbp->b_linep; ++i)
                        lp = lforw(lp);
                curwp->w_linep  = lp;
                curwp->w_toprow += n;
        }
        curwp->w_ntrows -= n;
        adjwp->w_ntrows += n;
        curwp->w_flag |= WFMODE|WFHARD;
        adjwp->w_flag |= WFMODE|WFHARD;
        return (TRUE);
}
 
/*
 * Pick a window for a pop-up.
 * Split the screen if there is only
 * one window. Pick the uppermost window that
 * isn't the current window. An LRU algorithm
 * might be better. Return a pointer, or
 * NULL on error.
 */
WINDOW  *
wpopup()
{
        register WINDOW *wp;
 
        if (wheadp->w_wndp == NULL              /* Only 1 window        */
        && splitwind(FALSE, 0) == FALSE)        /* and it won't split   */
                return (NULL);
        wp = wheadp;                            /* Find window to use   */
        while (wp!=NULL && wp==curwp)
                wp = wp->w_wndp;
        return (wp);
}
SHAR_EOF
#       End of shell archive
exit 0
%NONAME-W-NOMSG, Message number 00000000

RDROYA01@ULKYVX.BITNET.UUCP (02/05/87)

#       This is a shell archive.
#       Remove everything above and including the cut line.
#       Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar: Shell Archiver
#       Run the following text with /bin/sh to create:
#       cc.c
#       cc.ini
#       extmac.c
#       ueasm.s
#       xemacs.lnk
# This archive created: Tue Feb  3 18:04:08 1987
cat << \SHAR_EOF > cc.c
/* FILE:         cc.c
 * DATE:         27-Jan-1987
 *               Updated 07-01-1987 to allow wildcards and linking multiple
 *               files.
 *               Updated 27-01-1987 to allow assembling and/or compiling and
 *               to allow linking without compiling.
 * AUTHOR:       Robert Royar rdroya01@bitnet
 * SYSTEM:       Atari ST
 * COMPILER:     Alcyon v. 4.14
 * PURPOSE:      A simple minded compiler driver, no frills. (well, almost)
 * USAGE:        cc filename[s] (path must agree with cc.ini)
 *               the command line may contain wildcards as long as all
 *               source files are on the drive designated as the sdir in
 *               this file.  Using the link option will cause the linker to
 *               look for a command file on the source directory.  That link
 *               file should be named `foo.lnk' where `foo' is the name in
 *               the cc.ini file associated with the lnkfile value.  The
 *               link file should tell the linker to place the output on the
 *               source dir if the relocation program is to find the
 *               `foo.68k' file and successfully create a `foo.prg' file.
 *               Calling the program without a command line and with dolink=1
 *               will force it to try to link the file `linkfile.68k' and
 *               then call relmod.  Giving it a filename with an .s extent
 *               will skip the compiling and assemble the file.  Files with
 *               .s extents and .c extents may be mixed on a line.
 * DISTRIBUTION: Public Domain.  Do with it what you will.  Just leave
 *               the header intact.
 */
#include <stdio.h>
#include <osbind.h>
#include <ctype.h>
#include <errno.h>
 
unsigned long _STKSIZ = 16284;  /* keep 16K */
 
/* These are the defaults.  All of them can be over-ridden by values
 * in the cc.ini file if that file is on the default drive and directory.
 */
char sdir[81] = "e:\\"; /* where the source files are */
char include[81] = "d:\\stdlib.h\\"; /* include files */
char tdir[81] = "m:\\"; /* temporary files (all of them) */
char bin[81] = "c:\\bin\\";     /* compiler directory */
char symb[81] = "c:\\bin\\";    /* as68 symbols */
char floflag[12] = " ";         /* float ?      */
char delflag[12] = "1";         /* delete temporary files */
char lnkflag[12] = "0";         /* link file[s] if == "1" */
char warn[12] = " ";            /* make it -w to suppress warnings */
char syslib[81] = "c:\\bin\\";  /* drive to log for linker */
char lnk[81] = "out";           /* .68K and .lnk filename */
char fprg[81] = "out";          /* final output name */
char pname[81];
char command[81];
char source[81];
int temdrv;
char delfile[80];
int delete = 1;
int dolink = 0;
int doasm  = FALSE;
char path[128];
int exec;
int curdrv;
char author[30] = "Robert Royar";
 
main(argc,argv)
register int argc;
char *argv[];
{
        register char c, *ptr;
        register int argnum;
        char *index();
 
        argnum = 1;
        curdrv = (int)Dgetdrv(); /* 0 = A */
        Dgetpath(path,(1+curdrv)); /* 1 = A */
        if (access("cc.ini",4) != -1)
                if (!inivar())
                        exit(-1);
        if (argc == 1 && dolink == FALSE)
                {
                fprintf(stderr,"usage: %s filename[s]\n",argv[0]);
                exit(-1);
                }
        if (argc == 1)
                {
                exec = linkfil();
                goto out;
                }
        if (tdir[1] == ':')
                {
                c = (char)toupper(tdir[0]);
                temdrv = (int)(c - 'A');
                Dsetdrv(temdrv);        /* Log into temp drive */
                }
        if((exec=(int)Dsetpath(tdir))!=0)
                goto out;
        while(--argc)
                {
                strcpy(source,argv[argnum++]);
                if ((ptr=index(source,':'))!=NULL)
                        strcpy(source,++ptr);
                /* If the file has an .s extent, then assemble it.
                 * Check the source dir first.  If it's not there,
                 * check the temp dir.  Set the doasm flag to fit
                 * the outcome, or break if no file found.
                 */
                if ((ptr=index(source,'.'))!=NULL)
                        {
                        *ptr = '\0';    /* don't let the extension through */
                        /* delete any existing .o files */
                        sprintf(delfile,"%s%s.o",tdir,source);
                        if (access(delfile,4)==NULL)
                                unlink(delfile);
                        ++ptr;
                        if (*ptr != '\0')
                                {
                                *ptr = tolower(*ptr);
                                if (strcmp(ptr,"s")==NULL) /* assemb only */
                                        {
                                        sprintf(command,"%s%s.s",sdir,source);
                                        if ((exec=access(command,4)) == -1)
                                                {
                                                sprintf(command,"%s%s.s",
                                                       tdir,source);
                                                if ((exec=access(command,4))
                                                     == -1)
                                                        continue;
                                                else /* this is a tdir file */
                                                        doasm = FALSE;
                                                }
                                        else    /* this asm file is on sdir */
                                                doasm = TRUE;
                                        if ((exec=as())!=NULL)
                                                goto out;
                                        else
                                                continue;
                                        }
                                }
                        }
                /* delete any existing .o files */
                sprintf(delfile,"%s%s.o",tdir,source);
                if (access(delfile,4)==NULL)
                        unlink(delfile);
                doasm = FALSE;
                sprintf(command,"%s%s.c",sdir,source);
                if ((exec=access(command,4)) == -1)
                        break;
                if ((exec=cc())!=NULL)
                        break;
                }
        if (dolink && (exec == NULL))
                exec = linkfil();
out:    Dsetdrv(curdrv);
        Dsetpath(path);
        exit(exec);
}
 
cc()
{
        fprintf(stderr,"\nCompiling : %s%s.c\n",sdir,source);
        sprintf(&command[1],"-i %s %s%s.c %s%s.i ",
                include,sdir,source,tdir,source);
        command[0] = (char)strlen(&command[1]);
        sprintf(pname,"%sCP68.PRG",bin);
        if((exec=(int)Pexec(0,pname,command,0L))!=0)
                return(exec);
        sprintf(&command[1],"%s%s.i %s%s.1 %s%s.2 %s%s.3 %s %s",
                tdir,source,tdir,source,tdir,source,tdir,source,floflag,warn);
        command[0] = (char)strlen(&command[1]);
        sprintf(pname,"%sC068.PRG",bin);
        if((exec=(int)Pexec(0,pname,command,0L))!=0)
                return(exec);
        if (delete)
                {
                sprintf(command,"%s%s.i",tdir,source);
                unlink(command);
                }
        sprintf(&command[1],"%s%s.1 %s%s.2 %s%s.s",
        tdir,source,tdir,source,tdir,source);
        command[0] = (char)strlen(&command[1]);
        sprintf(pname,"%sC168.PRG",bin);
        if((exec=(int)Pexec(0,pname,command,0L))!=0)
                return(exec);
        sprintf(command,"%s%s.1",tdir,source);
        unlink(command);
        sprintf(command,"%s%s.2",tdir,source);
        unlink(command);
        exec = as();
        return(exec);
}
 
as()
{
        if (!doasm)
                {
                fprintf(stderr,"\nAssembling : %s%s.s\n",tdir,source);
                sprintf(&command[1],"-l -u -s %s %s%s.s",bin,tdir,source);
                }
        else
                {
                fprintf(stderr,"\nAssembling : %s%s.s\n",sdir,source);
                sprintf(&command[1],"-l -u -s %s %s%s.s",bin,sdir,source);
                }
        command[0] = (char)strlen(&command[1]);
        sprintf(pname,"%sAS68.PRG",bin);
        if((exec=(int)Pexec(0,pname,command,0L))!=0)
                return(exec);
        sprintf(command,"%s%s.o",tdir,source);
        if ((exec=access(command,4))==NULL)
                if (delete)
                        {
                        sprintf(command,"%s%s.s",tdir,source);
                        unlink(command);
                        }
        return(exec);
}
 
linkfil()
{
        register char c;
 
        if (syslib[1] == ':')
                {
                c = (char)toupper(syslib[0]);
                temdrv = (int) (c - 'A');
                Dsetdrv(temdrv);
                }
        else
                Dsetdrv(curdrv);
        Dsetpath(syslib);
        /* save disk write errors */
        sprintf(delfile,"%s%s.68k",sdir,lnk);
        if (access(delfile,4)==NULL)
                unlink(delfile);
        sprintf(delfile,"%s%s.prg",tdir,lnk);
        if (access(delfile,4)==NULL)
                unlink(delfile);
        sprintf(source,"%s%s",sdir,lnk);
        sprintf(pname,"%s%s",bin,"link68.ttp");
        sprintf(&command[1],"[co[%s.lnk]]",source);
        command[0] = (char)strlen(&command[1]);
        if ((exec=Pexec(0,pname,command,0L))!=0)
                return(exec);
        sprintf(source,"%s%s.68K",sdir,lnk);
        if ((exec=access(source,4))!=NULL)
                return(exec);
        sprintf(pname,"%s%s",tdir,lnk);
        sprintf(&command[1],"%s %s",source,pname);
        sprintf(pname,"%s%s",bin,"relmod.ttp");
        command[0] = (char)strlen(&command[1]);
        if ((exec=Pexec(0,pname,command,0L))!=0)
                return(exec);
        if ((exec=access(delfile,4))==NULL)
                if (delete)
                        unlink(source);
        return(exec);
}
 
inivar()
{
        FILE *freopen();
        char *gets(), *index();
        register char *ptr;
        static char line[81];
 
        if (freopen("cc.ini","r",stdin) != stdin)
                {
                fprintf(stderr,"cc: cannot open %scc.ini\n",path);
                return(0);
                }
        while (gets(line))
                {
                if ((ptr=index(line,' '))!=(char *)NULL)
                        *ptr = '\0';
                if ((ptr=index(line,'\t'))!=(char *)NULL)
                        *ptr = '\0';
                if ((ptr=index(line,'='))!=(char *)NULL)
                        *ptr = '\0';
                else
                        continue;
                ++ptr;
                if (strcmp(line,"source")==NULL)
                        strncpy(sdir,ptr,80);
                else if (strcmp(line,"include")==NULL)
                        strncpy(include,ptr,80);
                else if (strcmp(line,"temp")==NULL)
                        strncpy(tdir,ptr,80);
                else if (strcmp(line,"bin")==NULL)
                        strncpy(bin,ptr,80);
                else if (strcmp(line,"symb")==NULL)
                        strncpy(symb,ptr,80);
                else if (strcmp(line,"syslib")==NULL)
                        strncpy(syslib,ptr,80);
                else if (strcmp(line,"linkfile")==NULL)
                        strncpy(lnk,ptr,80);
                else if (strcmp(line,"float")==NULL)
                        strncpy(floflag,ptr,3);
                else if (strcmp(line,"warn")==NULL)
                        strncpy(warn,ptr,3);
                else if (strcmp(line,"delete")==NULL)
                        {
                        strncpy(delflag,ptr,2);
                        if (strncmp(delflag,"0",1)==NULL)
                                delete = FALSE;
                        }
                else if (strcmp(line,"dolink")==NULL)
                        {
                        strncpy(lnkflag,ptr,2);
                        if (strncmp(lnkflag,"1",1)==NULL)
                                dolink = TRUE;
                        }
                else if (feof(stdin))
                        break;
                }
        return(1);
}
SHAR_EOF
cat << \SHAR_EOF > cc.ini
/* cc.ini file for cc.prg and uemail.prg
 * the first ten are used by the cc or link commands. the aliases must
 * be spelled as they are here and must be in lower case. change the
 * path names to match your set-up.
 */
source=e:\                      /* C, lnk, and 68K files        */
include=d:\stdlib.h\            /* headers      */
temp=m:\                        /* temporary files, o files, and final prg  */
symb=c:\bin\                    /* as68symb.dat */
bin=c:\bin\                     /* compilers, assembler, linker, and loader */
syslib=c:\bin\                  /* library directory    */
linkfile=xemacs                 /* filename of link command file        */
float=                          /* flag for floating point -f, -e or nil*/
warn=
dolink=0                        /* if 1, call linker after compiling file[s] */
delete=1                        /* delete I, S, 68K files? 1==YES       */
/* the next two are used by shell */
shell=c:\util\pcommand.prg      /* shell program        */
xmodem=c:\comm\bmodem.ttp       /* xmodem program       */
cc=c:\bin\cc.prg
extmac=c:\uemail\extmac.prg
/* these are not used and if not needed, may be deleted */
home=c:\uemail\                 /* where it all began   */
root=c:\                        /* boot directory       */
assembly=d:\assembly.s\         /* assembly sources     */
etc=c:\util\                    /* utility programs     */
comm=c:\comm\                   /* communications       */
arc=c:\util\arc.ttp             /* arc cruncher         */
vt100=c:\comm\vt100.tos         /* small vt100 prog     */
/* set up colors the way I like. aliases and color names must be lower
 * case.  additional colors are grey, magenta, teal, green, yellow, and
 * white. cobalt is a deep blue. if colors are not aliased, you get
 * white on black as default.
 */
border=black                    /* color #0             */
cursor=red                      /* color #1             */
desk=cobalt                     /* color #2             */
letter=black                    /* color #3             */
/* function key bindings for uemail.ttp The F must be upper case */
F1=setmark                      /* sets the mark */
F2=listbuffers                  /* lists current buffers */
F3=filename                     /* change current filename */
F4=writeregion                  /* write text between point and mark to file */
F5=fileinsert                   /* insert file at point */
F6=fileread                     /* read file into current buffer */
F7=filevisit                    /* visit (find) a file */
F8=filewrite                    /* write out buffer to named file */
F9=filesave                     /* save current buffer if changed */
F10=quickexit                   /* save all changed buffers and exit */
/* Number pad bindings for uemail.ttp. the N must be upper case as must
 * the word NENTER.
 */
N(=backword                     /* backward word */
N)=forwword                     /* forward word */
N/=grtw                         /* globally remove trailing whitespace */
N*=retversion                   /* return current version to message line */
N7=gotobol                      /* goto beginning of line */
N8=gotoeol                      /* goto end of line */
N9=rettpa                       /* return usage to message line */
N-=pageback                     /* back by one text page (60 lines default) */
N4=backsent                     /* goto beginning of sentence */
N5=forwsent                     /* goto end of sentence */
N6=unkncom                      /* unknown */
N+=pageforw                     /* forward by one text page */
N1=gotbop                       /* goto beginning of paragraph (blank line) */
N2=goteop                       /* goto end of paragraph (blank line) */
N3=unkncom                      /* unknown */
N.=forwdel                      /* delete character under cursor */
NENTER=indent                   /* newline and indent */
SHAR_EOF
cat << \SHAR_EOF > extmac.c
/* EXTMAC.C support program for uemail.ttp.  Reads macro files created
 * in uemail format and creates C source prototypes.
 */
 
#include <stdio.h>
 
#define NFILEN  80                      /* # of bytes, file name        */
#define NBUFN   16                      /* # of bytes, buffer name      */
#define NLINE   256                     /* # of bytes, line             */
#define NKBDM   256                     /* # of strokes, keyboard macro */
#define NPAT    80                      /* # of bytes, pattern          */
#define HUGE    1000                    /* Huge number                  */
#define CTRL    0x0100                  /* Control flag, or'ed in       */
#define META    0x0200                  /* Meta flag, or'ed in          */
#define CTLX    0x0400                  /* ^X flag, or'ed in            */
#define SPEC    0x0800                  /* Special scancode keys        */
#define FLEN    15
 
short *kbdmop;
short kbdm[NKBDM];
FILE  *bmacrp, *fopen();
 
unsigned long _STKSIZ = 32 * 1024;
typedef struct KEYTAB {
        short   k_code;
        char    k_mfunc[FLEN];
} KEYTAB;
/*
 * Command table.
 * This table  is *roughly* in ASCII
 * order, left to right across the characters
 * of the command. This expains the funny
 * location of the control-X commands.
 */
KEYTAB  keytab[] = {
        CTRL|'@',       "setmark",
        CTRL|'A',       "gotobol",
        CTRL|'B',       "backchar",
        CTRL|'C',       "shell",
        CTRL|'D',       "forwdel",
        CTRL|'E',       "gotoeol",
        CTRL|'F',       "forwchar",
        CTRL|'G',       "ctrlg",
        CTRL|'H',       "backchar",
        CTRL|'I',       "tab",
        CTRL|'J',       "indent",
        CTRL|'K',       "mdeleln",
        CTRL|'L',       "refresh",
        CTRL|'M',       "newline",
        CTRL|'N',       "forwline",
        CTRL|'O',       "openline",
        CTRL|'P',       "backline",
        CTRL|'Q',       "quote",
        CTRL|'R',       "backsearch",
        CTRL|'S',       "forwsearch",
        CTRL|'T',       "twiddle",
        CTRL|'V',       "forwpage",
        CTRL|'W',       "killregion",
        CTRL|'Y',       "yank",
        CTRL|'Z',       "quickexit",
        CTRL|'\\',      "unkncom",
        CTRL|'_',       "kermit",
        CTRL|'^',       "unkncom",
        CTLX|CTRL|'A',  "unkncom",
        CTLX|CTRL|'B',  "listbuffers",
        CTLX|CTRL|'C',  "quit",
        CTLX|CTRL|'D',  "unkncom",
        CTLX|CTRL|'E',  "commfil",
        CTLX|CTRL|'F',  "filename",
        CTLX|CTRL|'G',  "ctrlg",
        CTLX|CTRL|'H',  "unkncom",
        CTLX|CTRL|'I',  "print",
        CTLX|CTRL|'J',  "unkncom",
        CTLX|CTRL|'K',  "unkncom",
        CTLX|CTRL|'L',  "lowerregion",
        CTLX|CTRL|'M',  "unkncom",
        CTLX|CTRL|'N',  "mvdnwind",
        CTLX|CTRL|'O',  "deblank",
        CTLX|CTRL|'P',  "mvupwind",
        CTLX|CTRL|'Q',  "unkncom",
        CTLX|CTRL|'R',  "fileread",
        CTLX|CTRL|'S',  "filesave",
        CTLX|CTRL|'T',  "showtime",
        CTLX|CTRL|'U',  "upperregion",
        CTLX|CTRL|'V',  "filevisit",
        CTLX|CTRL|'W',  "filewrite",
        CTLX|CTRL|'X',  "swapmark",
        CTLX|CTRL|'Y',  "unkncom",
        CTLX|CTRL|'Z',  "shrinkwind",
        CTLX|'!',       "paginate",
        CTLX|'#',       "setpage",
        CTLX|'+',       "pageforw",
        CTLX|'-',       "pageback",
        CTLX|'.',       "setindcol",
        CTLX|'(',       "ctlxlp",
        CTLX|')',       "ctlxrp",
        CTLX|'*',       "retversion",
        CTLX|'<',       "btopunct",
        CTLX|'=',       "showcpos",
        CTLX|'>',       "ftopunct",
        CTLX|'0',       "unkncom",
        CTLX|'1',       "onlywind",
        CTLX|'2',       "splitwind",
        CTLX|'3',       "unkncom",
        CTLX|'4',       "unkncom",
        CTLX|'5',       "unkncom",
        CTLX|'6',       "unkncom",
        CTLX|'7',       "unkncom",
        CTLX|'8',       "unkncom",
        CTLX|'9',       "unkncom",
        CTLX|'A',       "unkncom",
        CTLX|'B',       "usebuffer",
        CTLX|'C',       "paintbuffer",
        CTLX|'D',       "setpath",
        CTLX|'E',       "ctlxe",
        CTLX|'F',       "setfillcol",
        CTLX|'H',       "unkncom",
        CTLX|'I',       "fileinsert",
        CTLX|'J',       "unkncom",
        CTLX|'K',       "killbuffer",
        CTLX|'L',       "unkncom",
        CTLX|'M',       "setmode",
        CTLX|'N',       "nextwind",
        CTLX|'O',       "prevwind",
        CTLX|'P',       "prevwind",
        CTLX|'Q',       "unkncom",
        CTLX|'R',       "writereg",
        CTLX|'S',       "gospell",
        CTLX|'T',       "unkncom",
        CTLX|'U',       "unkncom",
        CTLX|'V',       "unkncom",
        CTLX|'W',       "wc",
        CTLX|'Y',       "unkncom",
        CTLX|'Z',       "enlargewind",
        CTLX|'\\',      "grtw",
        CTLX|'`',       "getmacro",
        CTLX|'~',       "shell",
        CTLX|SPEC|'p',  "swapmark",
        META|CTRL|'B',  "backword",
        META|CTRL|'C',  "mcenter",
        META|CTRL|'F',  "forwword",
        META|CTRL|'G',  "ctrlg",
        META|CTRL|'H',  "backword",
        META|CTRL|'I',  "kill",
        META|CTRL|'K',  "mdelwln",
        META|CTRL|'M',  "unkncom",
        META|CTRL|'N',  "enumerate",
        META|CTRL|'O',  "clowsp",
        META|CTRL|'P',  "tglcase",
        META|CTRL|'R',  "mrflush",
        META|CTRL|'S',  "forwisearch",
        META|CTRL|'T',  "backisearch",
        META|'!',       "reposition",
        META|'.',       "gotoeob",
        META|',',       "gotobob",
        META|'>',       "gotoeob",
        META|'<',       "gotobob",
        META|' ',       "setmark",
        META|'@',       "rettpa",
        META|'A',       "backsent",
        META|'B',       "backword",
        META|'C',       "capword",
        META|'D',       "delfword",
        META|'E',       "forwsent",
        META|'F',       "forwword",
        META|'G',       "goline",
        META|'H',       "markpar",
        META|'I',       "unkncom",
        META|'J',       "mindnl",
        META|'K',       "killsent",
        META|'L',       "lowerword",
        META|'M',       "sglmode",
        META|'N',       "goteop",
        META|'O',       "mdropln",
        META|'P',       "gotbop",
        META|'Q',       "fillpar",
        META|'R',       "replace",
        META|'S',       "unkncom",
        META|'T',       "twaddle",
        META|'U',       "upperword",
        META|'V',       "backpage",
        META|'W',       "copyregion",
        META|'X',       "mdoncom",
        META|'Y',       "unkncom",
        META|'Z',       "unkncom",
        META|'\\',      "mdelind",
        META|'~',       "clearflag",
        META|0x7F,      "delbword",
        SPEC|'H',       "backline",
        SPEC|'P',       "forwline",
        SPEC|'K',       "backchar",
        SPEC|'M',       "forwchar",
        SPEC|'b',       "kermit",
        SPEC|'a',       "yank",
        SPEC|'R',       "openline",
        SPEC|'S',       "backdel",
        SPEC|'G',       "refresh",
        SPEC|'q',       "forwdel",
        SPEC|'r',       "indent",
        SPEC|'c',       "backword",
        SPEC|'d',       "forwword",
        SPEC|'e',       "grtw",
        SPEC|'f',       "retversion",
        SPEC|'g',       "gotobol",
        SPEC|'h',       "gotoeol",
        SPEC|'i',       "unkncom",
        SPEC|'J',       "pageback",
        SPEC|'N',       "pageforw",
        SPEC|'j',       "backsent",
        SPEC|'k',       "forwsent",
        SPEC|'l',       "unkncom",
        SPEC|'m',       "gotbop",
        SPEC|'n',       "goteop",
        SPEC|'o',       "unkncom",
        SPEC|'D',       "quickexit",
        SPEC|'C',       "filesave",
        SPEC|'B',       "filewrite",
        SPEC|'A',       "filevisit",
        SPEC|'@',       "fileread",
        SPEC|'?',       "fileinsert",
        SPEC|'>',       "writereg",
        SPEC|'=',       "filename",
        SPEC|'<',       "listbuffers",
        SPEC|';',       "setmark",
        0x60,           "putmacro",
        0x7F,           "backdel"
};
 
#define NKEYTAB (sizeof(keytab)/sizeof(keytab[0]))
 
main(argc,argv)
int argc;
char *argv[];
{
        register int err;
 
        if (argc < 3)
                {
                fprintf(stderr,"usage: %s infile outfile\n",argv[0]);
                exit(-1);
                }
        if ((bmacrp=fopen(argv[2], "w"))==NULL)
                {
                err = perror("open failure");
                exit(err);
                }
        err=loadmac(argv[1]);
        fclose(bmacrp);
        exit(err);
}
 
/* LOADMAC read a uemail macro file and produce a prototype C source file.
 */
loadmac(macfile)
char macfile[];
{
        register int d,i;
        register short s;
        register FILE   *mp;
        extern void extrct_macro();
 
        if ((mp=fopen(macfile,"r"))==NULL)
                {
                fprintf(stderr,"Cannot open %s",macfile);
                return(-1);
                }
        if ((s=getw(mp))!=(CTLX|'('))
                {
                fprintf(stderr,"Macro file format error: %s",macfile);
                fclose(mp);
                return(-1);
                }
        while((d=fgetc(mp))!=EOF)
                {
                i=0;
                while ((s=getw(mp))!=(CTLX|')'))
                        {
                        if (feof(mp))
                                {
                                fprintf(stderr,"Read error on: %s",macfile);
                                fclose(mp);
                                return(-11);    /* read error */
                                }
                        kbdm[i++]=s;
                        }
                kbdm[i] = CTLX|')';
                extrct_macro(d);
                }
        fclose(mp);
        return(NULL);
}
 
/* Look at the current keyboard macro.  Send each bitcode to writmacro()
 * so it can look up the function calling sequence in the KEYTAB.  These
 * functions allow you to record keyboard macros in C source code for
 * later compilation.
 */
void
extrct_macro(d)
{
        register int    c;
        register int    an;
        register int    s;
 
        fprintf(bmacrp,"%c(f,n)\n",d);
        fputs("int f,n;\n",bmacrp);     /* default uEmail parameters */
        fputs("{\n",bmacrp);            /* begin definition */
        fputs("        if (n < 0)\n",bmacrp);
        fputs("                return(0);\n",bmacrp);
        fputs("        while(n--)\n",bmacrp);
        fputs("                {\n",bmacrp);
        kbdmop = &kbdm[0];
        do
                {
                an = 1;
                if ((c = *kbdmop++) == (CTRL|'U'))
                        {
                        an = *kbdmop++;
                        c  = *kbdmop++;
                        }
                s = TRUE;
                } while (c!=(CTLX|')') && (s=writmacro(c,an))==TRUE);
        kbdmop = NULL;
        fputs("                }\n",bmacrp);    /* end the while statement */
        fputs("        return(1);\n",bmacrp);
        fputs("}\n\n",bmacrp);
        return;
}
 
/* Look up function name based on bitcode found in extrct_macro().  No errors
 * because a running macro that uses one of the "get-string" routines would
 * return an error and extrct_macro() would abort.  Watch when using with
 * functions that use mlreply() or readpattern() that the read-in text does
 * not return FALSE.
 */
 
writmacro(c,n)
register int c,n;
{
 
        char funam[FLEN];
        register int f;
 
        if (n != 1)
                f = TRUE;
        else
                f = FALSE;
        if(getfname((short)c,funam)!=FALSE)
                fprintf(bmacrp,"                if(!%s(%d,%d))\n",funam,f,n);
        else
                fprintf(bmacrp,"                if(!linsert(%d,'%c'))\n",n,c);
        fputs("                        return(0);\n",bmacrp);
        return(TRUE);   /* bad keycode, but we want it anyway */
}
 
/* given a keycode value, return TRUE
 * and get the function's name into `name'.
 */
getfname(kcode,name)
register short kcode;
char name[];
{
        register KEYTAB *ktp;
 
        ktp = &keytab[0];       /* the function to find */
        /* look through to find keytab assoc with "code" */
        while (ktp < &keytab[NKEYTAB]) {
                if (ktp->k_code == kcode)
                        {
                        strcpy(name,ktp->k_mfunc);
                        return(TRUE);
                        }
                ++ktp;
        }
        return(FALSE);  /* not found */
}
SHAR_EOF
cat << \SHAR_EOF > ueasm.s
*************************************************************************
* Add all of this to your gemstart.s file.  You will need the latest
* version.  It is copyrighted, or I would include it also.  You need
* the stack variable to allow shell commands.  The __BDOS function is
* a bug fix.  The copy function handles screens in the terminal section.
* The getmem function is for rettpa.  I include gemdos-xbios in my
* startup so that link68 does not have to load osbind.o.
*************************************************************************
*
* STACK variable summary:
*       -1=keep all
*        0=keep MINSTACK bytes
*        1=keep 1/4 of free memory
*        2=keep 2/4
*        3=keep 3/4
*        4=use _STKSIZ: keep (if >0) or give up (if <0) _STKSIZ bytes.
*    other=keep that many bytes (positive) or give back that many (negative)
 
STACK=4                 * CHANGE THIS VARIABLE TO CHOOSE A MEMORY MODEL
*
* Surprise, the trap #2 vector is largely a do nothing routine.
* Redirecting the basic calls may help solve a few problems.
*
        .globl ___BDOS
 
___BDOS:
        link    A6,#-4
        cmpi.w  #9,$8(A6)
        beq     togem
        cmpi.w  #2,$8(A6)
        beq     togem
        cmpi.w  #1,$8(A6)
        beq     togem
        cmpi.w  #26,$8(A6)
        bne     bdos
togem:
        move.l  $a(A6),(sp)     * value
        move.w  $8(A6),-(sp)    * function
        bsr     _gemdos
        addq.l  #2,sp
        bra     out
bdos:
        move.l  $a(A6),d1       * value
        move.w  $8(A6),d0       * function
        trap    #2              * Enter BDOS
        cmpa.l  __break,sp      * Check for stack ovf
        bcs     __sovf          * overflow! print msg and abort
 
out:    unlk    A6              * no error; return
        rts                     * Back to caller
 
*
*       GEMDOS, BIOS, and XBIOS calls
*
        .globl  _gemdos
        .globl  _bios
        .globl  _xbios
_gemdos:
        move.l  (sp)+,biosret
        trap    #1
        move.l  biosret,-(sp)
        rts
*
_bios:  move.l  (sp)+,biosret
        trap    #13
        move.l  biosret,-(sp)
        rts
*
_xbios: move.l  (sp)+,biosret
        trap    #14
        move.l  biosret,-(sp)
        rts
*       from _Programming the 68000_ by Steve Williams
*       copy(src,dst,length);
*       char    *src;
*       char    *dst;
*       int     length;
*
        .globl  _copy
_copy:  move.l  4(a7),a0
        move.l  8(a7),a1
        move.w  12(a7),d0
        sub.w   #1,d0
loop:   move.b  (a0)+,(a1)+
        dbra    d0,loop
        rts
*
*       getmem function to return present top of memory
*
        .globl  _getmem
_getmem:
        move.l  a7,d0   * real simple and it works
        rts
        .bss
        .even
biosret:        .ds.l   1
 
SHAR_EOF
cat << \SHAR_EOF > xemacs.lnk
[tem[m:]]e:xemacs.68k=gemsnew,
m:main,
m:basic,
m:buffer,
m:file,
m:fileio,
m:line,
m:random,
m:misc,
m:region,
m:search,
m:word,
m:wc,
m:kermit,
m:kertrans,
m:kerrec,
m:kertty,
m:page,
m:print,
m:window,
m:display,
m:termio,
m:ttgetc,
m:tty,
m:shell,
m:path,
m:macros,
m:keybrd,
gemlib[in[_nobinary],in[_nofilesz],in[_nottyin],in[_maxfiles],in[_nowildc]],
libm
SHAR_EOF
#       End of shell archive
exit 0
%NONAME-W-NOMSG, Message number 00000000

RDROYA01@ULKYVX.BITNET.UUCP (02/06/87)

#       This is a shell archive.
#       Remove everything above and including the cut line.
#       Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar: Shell Archiver
#       Run the following text with /bin/sh to create:
#       line.c
#       word.c
#       search.c
#       wc.c
# This archive created: Tue Feb  3 17:57:11 1987
cat << \SHAR_EOF > line.c
/*
 * The functions in this file
 * are a general set of line management
 * utilities. They are the only routines that
 * touch the text. They also touch the buffer
 * and window structures, to make sure that the
 * necessary updating gets done. There are routines
 * in this file that handle the kill buffer too.
 * It isn't here for any good reason.
 *
 * Note that this code only updates the dot and
 * mark values in the window list. Since all the code
 * acts on the current window, the buffer that we
 * are editing must be being displayed, which means
 * that "b_nwnd" is non zero, which means that the
 * dot and mark values in the buffer headers are
 * nonsense.
 */
#include        <stdio.h>
#include        "ed.h"
 
#define NBLOCK  16                      /* Line block chunk size        */
#define KBLOCK  256                     /* Kill buffer block size       */
 
char    *kbufp  = NULL;                 /* Kill buffer data             */
int     kused   = 0;                    /* # of bytes used in KB        */
int     ksize   = 0;                    /* # of bytes allocated in KB   */
 
/*
 * This routine allocates a block
 * of memory large enough to hold a LINE
 * containing "used" characters. The block is
 * always rounded up a bit. Return a pointer
 * to the new block, or NULL if there isn't
 * any memory left. Print a message in the
 * message line if no space.
 */
LINE    *
lalloc(used)
register int    used;
{
        register LINE   *lp;
        register int    size;
 
        size = (used+NBLOCK-1) & ~(NBLOCK-1);
        if (size == 0)                          /* Assume that an empty */
                size = NBLOCK;                  /* line is for type-in. */
        if ((lp = (LINE *) malloc(sizeof(LINE)+size)) == NULL) {
                mlwrite("Cannot allocate %d bytes", size);
                return (NULL);
        }
        lp->l_size = size;
        lp->l_used = used;
        return (lp);
}
 
/*
 * Delete line "lp". Fix all of the
 * links that might point at it (they are
 * moved to offset 0 of the next line.
 * Unlink the line from whatever buffer it
 * might be in. Release the memory. The
 * buffers are updated too; the magic conditions
 * described in the above comments don't hold
 * here.
 */
lfree(lp)
register LINE   *lp;
{
        register BUFFER *bp;
        register WINDOW *wp;
 
        wp = wheadp;
        while (wp != NULL) {
                if (wp->w_linep == lp)
                        wp->w_linep = lp->l_fp;
                if (wp->w_dotp  == lp) {
                        wp->w_dotp  = lp->l_fp;
                        wp->w_doto  = 0;
                }
                if (wp->w_markp == lp) {
                        wp->w_markp = lp->l_fp;
                        wp->w_marko = 0;
                }
                wp = wp->w_wndp;
        }
        bp = bheadp;
        while (bp != NULL) {
                if (bp->b_nwnd == 0) {
                        if (bp->b_dotp  == lp) {
                                bp->b_dotp = lp->l_fp;
                                bp->b_doto = 0;
                        }
                        if (bp->b_markp == lp) {
                                bp->b_markp = lp->l_fp;
                                bp->b_marko = 0;
                        }
                }
                bp = bp->b_bufp;
        }
        lp->l_bp->l_fp = lp->l_fp;
        lp->l_fp->l_bp = lp->l_bp;
        free((char *) lp);
}
 
/*
 * This routine gets called when
 * a character is changed in place in the
 * current buffer. It updates all of the required
 * flags in the buffer and window system. The flag
 * used is passed as an argument; if the buffer is being
 * displayed in more than 1 window we change EDIT to
 * HARD. Set MODE if the mode line needs to be
 * updated (the "*" has to be set).
 */
lchange(flag)
register int    flag;
{
        register WINDOW *wp;
 
        if (curbp->b_nwnd != 1)                 /* Ensure hard.         */
                flag = WFHARD;
        if ((curbp->b_flag&BFCHG) == 0) {       /* First change, so     */
                flag |= WFMODE;                 /* update mode lines.   */
                curbp->b_flag |= BFCHG;
        }
        wp = wheadp;
        while (wp != NULL) {
                if (wp->w_bufp == curbp)
                        wp->w_flag |= flag;
                wp = wp->w_wndp;
        }
}
 
/*
 * Insert "n" copies of the character "c"
 * at the current location of dot. In the easy case
 * all that happens is the text is stored in the line.
 * In the hard case, the line has to be reallocated.
 * When the window list is updated, take special
 * care; I screwed it up once. You always update dot
 * in the current window. You update mark, and a
 * dot in another window, if it is greater than
 * the place where you did the insert. Return TRUE
 * if all is well, and FALSE on errors.
 */
linsert(n, c)
register int n, c;
{
        register char   *cp1;
        register char   *cp2;
        register LINE   *lp1;
        register LINE   *lp2;
        register LINE   *lp3;
        register int    doto;
        register int    i;
        register WINDOW *wp;
 
        lchange(WFEDIT);
        lp1 = curwp->w_dotp;                    /* Current line         */
        if (lp1 == curbp->b_linep) {            /* At the end: special  */
                if (curwp->w_doto != 0) {
                        mlwrite("bug: linsert");
                        return (FALSE);
                }
                if ((lp2=lalloc(n)) == NULL)    /* Allocate new line    */
                        return (FALSE);
                lp3 = lp1->l_bp;                /* Previous line        */
                lp3->l_fp = lp2;                /* Link in              */
                lp2->l_fp = lp1;
                lp1->l_bp = lp2;
                lp2->l_bp = lp3;
                for (i=0; i<n; ++i)
                        lp2->l_text[i] = c;
                curwp->w_dotp = lp2;
                curwp->w_doto = n;
                return (TRUE);
        }
        doto = curwp->w_doto;                   /* Save for later.      */
        if (lp1->l_used+n > lp1->l_size) {      /* Hard: reallocate     */
                if ((lp2=lalloc(lp1->l_used+n)) == NULL)
                        return (FALSE);
                cp1 = &lp1->l_text[0];
                cp2 = &lp2->l_text[0];
                while (cp1 != &lp1->l_text[doto])
                        *cp2++ = *cp1++;
                cp2 += n;
                while (cp1 != &lp1->l_text[lp1->l_used])
                        *cp2++ = *cp1++;
                lp1->l_bp->l_fp = lp2;
                lp2->l_fp = lp1->l_fp;
                lp1->l_fp->l_bp = lp2;
                lp2->l_bp = lp1->l_bp;
                free((char *) lp1);
        } else {                                /* Easy: in place       */
                lp2 = lp1;                      /* Pretend new line     */
                lp2->l_used += n;
                cp2 = &lp1->l_text[lp1->l_used];
                cp1 = cp2-n;
                while (cp1 != &lp1->l_text[doto])
                        *--cp2 = *--cp1;
        }
        for (i=0; i<n; ++i)                     /* Add the characters   */
                lp2->l_text[doto+i] = c;
        wp = wheadp;                            /* Update windows       */
        while (wp != NULL) {
                if (wp->w_linep == lp1)
                        wp->w_linep = lp2;
                if (wp->w_dotp == lp1) {
                        wp->w_dotp = lp2;
                        if (wp==curwp || wp->w_doto>doto)
                                wp->w_doto += n;
                }
                if (wp->w_markp == lp1) {
                        wp->w_markp = lp2;
                        if (wp->w_marko > doto)
                                wp->w_marko += n;
                }
                wp = wp->w_wndp;
        }
        return (TRUE);
}
 
/*
 * Insert a newline into the buffer
 * at the current location of dot in the current
 * window. The funny ass-backwards way it does things
 * is not a botch; it just makes the last line in
 * the file not a special case. Return TRUE if everything
 * works out and FALSE on error (memory allocation
 * failure). The update of dot and mark is a bit
 * easier then in the above case, because the split
 * forces more updating.
 */
lnewline()
{
        register char   *cp1;
        register char   *cp2;
        register LINE   *lp1;
        register LINE   *lp2;
        register int    doto;
        register WINDOW *wp;
 
        lchange(WFHARD);
        lp1  = curwp->w_dotp;                   /* Get the address and  */
        doto = curwp->w_doto;                   /* offset of "."        */
        if ((lp2=lalloc(doto)) == NULL)         /* New first half line  */
                return (FALSE);
        cp1 = &lp1->l_text[0];                  /* Shuffle text around  */
        cp2 = &lp2->l_text[0];
        while (cp1 != &lp1->l_text[doto])
                *cp2++ = *cp1++;
        cp2 = &lp1->l_text[0];
        while (cp1 != &lp1->l_text[lp1->l_used])
                *cp2++ = *cp1++;
        lp1->l_used -= doto;
        lp2->l_bp = lp1->l_bp;
        lp1->l_bp = lp2;
        lp2->l_bp->l_fp = lp2;
        lp2->l_fp = lp1;
        wp = wheadp;                            /* Windows              */
        while (wp != NULL) {
                if (wp->w_linep == lp1)
                        wp->w_linep = lp2;
                if (wp->w_dotp == lp1) {
                        if (wp->w_doto < doto)
                                wp->w_dotp = lp2;
                        else
                                wp->w_doto -= doto;
                }
                if (wp->w_markp == lp1) {
                        if (wp->w_marko < doto)
                                wp->w_markp = lp2;
                        else
                                wp->w_marko -= doto;
                }
                wp = wp->w_wndp;
        }
        return (TRUE);
}
 
/*
 * This function deletes "n" bytes,
 * starting at dot. It understands how do deal
 * with end of lines, etc. It returns TRUE if all
 * of the characters were deleted, and FALSE if
 * they were not (because dot ran into the end of
 * the buffer. The "kflag" is TRUE if the text
 * should be put in the kill buffer.
 */
ldelete(n, kflag)
register int n, kflag;
{
        register char   *cp1;
        register char   *cp2;
        register LINE   *dotp;
        register int    doto;
        register int    chunk;
        register WINDOW *wp;
 
        while (n != 0) {
                dotp = curwp->w_dotp;
                doto = curwp->w_doto;
                if (dotp == curbp->b_linep)     /* Hit end of buffer.   */
                        return (FALSE);
                chunk = dotp->l_used-doto;      /* Size of chunk.       */
                if (chunk > n)
                        chunk = n;
                if (chunk == 0) {               /* End of line, merge.  */
                        lchange(WFHARD);
                        if (ldelnewline() == FALSE
                        || (kflag!=FALSE && kinsert('\n')==FALSE))
                                return (FALSE);
                        --n;
                        continue;
                }
                lchange(WFEDIT);
                cp1 = &dotp->l_text[doto];      /* Scrunch text.        */
                cp2 = cp1 + chunk;
                if (kflag != FALSE) {           /* Kill?                */
                        while (cp1 != cp2) {
                                if (kinsert(*cp1) == FALSE)
                                        return (FALSE);
                                ++cp1;
                        }
                        cp1 = &dotp->l_text[doto];
                }
                while (cp2 != &dotp->l_text[dotp->l_used])
                        *cp1++ = *cp2++;
                dotp->l_used -= chunk;
                wp = wheadp;                    /* Fix windows          */
                while (wp != NULL) {
                        if (wp->w_dotp==dotp && wp->w_doto>=doto) {
                                wp->w_doto -= chunk;
                                if (wp->w_doto < doto)
                                        wp->w_doto = doto;
                        }
                        if (wp->w_markp==dotp && wp->w_marko>=doto) {
                                wp->w_marko -= chunk;
                                if (wp->w_marko < doto)
                                        wp->w_marko = doto;
                        }
                        wp = wp->w_wndp;
                }
                n -= chunk;
        }
        return (TRUE);
}
 
/*
 * Delete a newline. Join the current line
 * with the next line. If the next line is the magic
 * header line always return TRUE; merging the last line
 * with the header line can be thought of as always being a
 * successful operation, even if nothing is done, and this makes
 * the kill buffer work "right". Easy cases can be done by
 * shuffling data around. Hard cases require that lines be moved
 * about in memory. Return FALSE on error and TRUE if all
 * looks ok. Called by "ldelete" only.
 */
ldelnewline()
{
        register char   *cp1;
        register char   *cp2;
        register LINE   *lp1;
        register LINE   *lp2;
        register LINE   *lp3;
        register WINDOW *wp;
 
        lp1 = curwp->w_dotp;
        lp2 = lp1->l_fp;
        if (lp2 == curbp->b_linep) {            /* At the buffer end.   */
                if (lp1->l_used == 0)           /* Blank line.          */
                        lfree(lp1);
                return (TRUE);
        }
        if (lp2->l_used <= lp1->l_size-lp1->l_used) {
                cp1 = &lp1->l_text[lp1->l_used];
                cp2 = &lp2->l_text[0];
                while (cp2 != &lp2->l_text[lp2->l_used])
                        *cp1++ = *cp2++;
                wp = wheadp;
                while (wp != NULL) {
                        if (wp->w_linep == lp2)
                                wp->w_linep = lp1;
                        if (wp->w_dotp == lp2) {
                                wp->w_dotp  = lp1;
                                wp->w_doto += lp1->l_used;
                        }
                        if (wp->w_markp == lp2) {
                                wp->w_markp  = lp1;
                                wp->w_marko += lp1->l_used;
                        }
                        wp = wp->w_wndp;
                }
                lp1->l_used += lp2->l_used;
                lp1->l_fp = lp2->l_fp;
                lp2->l_fp->l_bp = lp1;
                free((char *) lp2);
                return (TRUE);
        }
        if ((lp3=lalloc(lp1->l_used+lp2->l_used)) == NULL)
                return (FALSE);
        cp1 = &lp1->l_text[0];
        cp2 = &lp3->l_text[0];
        while (cp1 != &lp1->l_text[lp1->l_used])
                *cp2++ = *cp1++;
        cp1 = &lp2->l_text[0];
        while (cp1 != &lp2->l_text[lp2->l_used])
                *cp2++ = *cp1++;
        lp1->l_bp->l_fp = lp3;
        lp3->l_fp = lp2->l_fp;
        lp2->l_fp->l_bp = lp3;
        lp3->l_bp = lp1->l_bp;
        wp = wheadp;
        while (wp != NULL) {
                if (wp->w_linep==lp1 || wp->w_linep==lp2)
                        wp->w_linep = lp3;
                if (wp->w_dotp == lp1)
                        wp->w_dotp  = lp3;
                else if (wp->w_dotp == lp2) {
                        wp->w_dotp  = lp3;
                        wp->w_doto += lp1->l_used;
                }
                if (wp->w_markp == lp1)
                        wp->w_markp  = lp3;
                else if (wp->w_markp == lp2) {
                        wp->w_markp  = lp3;
                        wp->w_marko += lp1->l_used;
                }
                wp = wp->w_wndp;
        }
        free((char *) lp1);
        free((char *) lp2);
        return (TRUE);
}
 
/*
 * Delete all of the text
 * saved in the kill buffer. Called by commands
 * when a new kill context is being created. The kill
 * buffer array is released, just in case the buffer has
 * grown to immense size. No errors.
 */
kdelete()
{
        if (kbufp != NULL) {
                free((char *) kbufp);
                kbufp = NULL;
                kused = 0;
                ksize = 0;
        }
}
 
/*
 * Insert a character to the kill buffer,
 * enlarging the buffer if there isn't any room. Always
 * grow the buffer in chunks, on the assumption that if you
 * put something in the kill buffer you are going to put
 * more stuff there too later. Return TRUE if all is
 * well, and FALSE on errors.
 */
kinsert(c)
register int c;
{
        register char   *nbufp;
        register int    i;
 
        if (kused == ksize) {
                if ((nbufp =  malloc(ksize+KBLOCK)) == NULL)
                        return (FALSE);
                for (i=0; i<ksize; ++i)
                        nbufp[i] = kbufp[i];
                if (kbufp != NULL)
                        free((char *) kbufp);
                kbufp  = nbufp;
                ksize += KBLOCK;
        }
        kbufp[kused++] = c;
        return (TRUE);
}
 
/*
 * This function gets characters from
 * the kill buffer. If the character index "n" is
 * off the end, it returns "-1". This lets the caller
 * just scan along until it gets a "-1" back.
 */
kremove(n)
register int n;
{
        if (n >= kused)
                return (-1);
        else
                return (kbufp[n] & 0xFF);
}
fp = NULL;
                kused = 0;
                ksize = 0;
        }
}
 
SHAR_EOF
cat << \SHAR_EOF > word.c
/*
 * The routines in this file
 * implement commands that work word at
 * a time. There are all sorts of word mode
 * commands. If I do any sentence and/or paragraph
 * mode commands, they are likely to be put in
 * this file.  Added R.D.R Feb. 1986.
 */
#include        <stdio.h>
#include        <ctype.h>
#include        "ed.h"
 
 
/* Word wrap or fill wrap depending on f flag value.
 * Back-over whatever precedes the point on the current line and
 * stop on the first word-break or the beginning of the line.
 * If we reach the beginning of the line, jump back to the end of the
 * word and start a new line.  Otherwise, break the line at the
 * word-break, eat it, and jump back to the end of the word.
 * Returns TRUE on success, FALSE on errors.
 */
wrapword(f, n)
register int f, n;
{
        int oldp;
        oldp = curwp->w_dotp;
 
        if (! backwword(NULL, 1)) /* punctuation marks */
                return(FALSE);
        if (oldp != curwp->w_dotp && curwp->w_doto)
                {
                if (! backdel(NULL, 1))
                        return(FALSE);
                if (! newline(TRUE, 1))
                        return(FALSE);
                }
        if(f)
                return(forwwword(NULL, 1));
        return(forwwword(NULL, 1) && forwchar(NULL, 1) && backdel(NULL, 1));
}
 
/* FILLPAR Meta command  Fill paragraph to specified fill column and indent
 * column.  Bound to M-Q.
 */
 
fillpar(f, n)
register int f, n;
{
        register int s;
        register short omo;
        register LINE *omp;
 
        if (n > 1)
                setfillcol(f, n);
 
        if(fillcol == 0)
                {
                mlwrite("Fill column not set");
                (*term.t_beep)();
                return(FALSE);
                }
        omp = curwp->w_markp;
        omo = curwp->w_marko;
        curwp->w_markp = curwp->w_dotp;
        curwp->w_marko = curwp->w_doto;
 
        gotbop(FALSE, 1);
        forwchar(FALSE, 1);
        while((n = ltrw(FALSE, 1)) != EOF && n != NULL)
                forwline(NULL, 1);
        gotbop(FALSE, 1);
        forwchar(FALSE, 1);
        while (TRUE)
                {
                if (llength(curwp->w_dotp) == NULL)
                        break;
                if (curwp->w_dotp == curbp->b_linep)
                        break;
                if (getccol(FALSE) > fillcol)
                        {
                        if (wrapword(TRUE, NULL) == FALSE)
                                break;
                        continue;
                        }
                if (curwp->w_doto ==  llength(curwp->w_dotp)
                        && getccol(FALSE) <= fillcol)
                        {
                        if (forwchar(FALSE, 1) == FALSE)
                                break;
                        if (curwp->w_dotp == curbp->b_linep)
                                break;  /* @ EOB */
                        if (llength(curwp->w_dotp) == NULL)
                                break;  /* @ EOP */
                        if (backchar(FALSE, 1) == FALSE)
                                break;
                        if (clowsp(FALSE, NULL) == FALSE)
                                break;
                        continue;
                        }
                if (forwchar(FALSE, 1) == FALSE)
                        break;
                }
        curwp->w_dotp = curwp->w_markp;
        curwp->w_doto = curwp->w_marko;
        curwp->w_markp = omp;
        curwp->w_marko = omo;
        curwp->w_flag |= WFHARD;
        curgoal = getccol(FALSE);
        return(TRUE);
}
 
/*
 * PRIVATE VERSION OF INWORD() FOR WRAPWORD(), FORWWORD(), AND BACKWWORD()
 * Return FALSE if the character at dot
 * is a space character or above 0x7e.
 * Otherwise return TRUE.  Any printing
 * character below <DEL> that is not
 * a space character may appear inside a
 * word to be wrapped.  Using a special version
 * of inword() allows us to keep the usual meaning
 * of word for regular movement.
 */
static
inwword()
{
        register int    c;
 
        if (curwp->w_doto == llength(curwp->w_dotp))
                return (FALSE);
        c = lgetc(curwp->w_dotp, curwp->w_doto);
        if (isspace(c) || c> '~')
                return (FALSE);
        return (TRUE);
}
 
/*
 * PRIVATE VERSION OF BACKWORD() FOR WRAPWORD() AND FORWWWORD()
 * Move the cursor backward by
 * "n" words. All of the details of motion
 * are performed by the "backchar" and "forwchar"
 * routines. Error if you try to move beyond
 * the buffers.
 */
static
backwword(f, n)
register int f, n;
{
        if (backchar(FALSE, 1) == FALSE)
                return (FALSE);
        while (inwword() == FALSE)
                {
                if (backchar(FALSE, 1) == FALSE)
                        return (FALSE);
                }
        while (inwword() != FALSE)
                {
                if (backchar(FALSE, 1) == FALSE)
                        return (FALSE);
                }
        return (forwchar(FALSE, 1));
}
 
/* PRIVATE VERSION OF FORWWORD() FOR WRAPWORD() AND BACKWWORD()
 * Move the cursor forward by
 * the specified number of words. All of the
 * motion is done by "forwchar". Error if you
 * try and move beyond the buffer's end.
 */
static
forwwword(f, n)
register int f, n;
{
        while (inwword() == FALSE)
                {
                if (forwchar(FALSE, 1) == FALSE)
                        return (FALSE);
                }
        while (inwword() != FALSE)
                {
                if (forwchar(FALSE, 1) == FALSE)
                        return (FALSE);
                }
        return (TRUE);
}
 
/*
 * Move the cursor forward by
 * the specified number of words. All of the
 * motion is done by "forwchar". Error if you
 * try and move beyond the buffer's end.
 */
forwword(f, n)
register int f, n;
{
        if (n < 0)
                return (backword(f, -n));
        while (n--) {
                while (inword() == FALSE) {
                        if (forwchar(FALSE, 1) == FALSE)
                                return (FALSE);
                }
                while (inword() != FALSE) {
                        if (forwchar(FALSE, 1) == FALSE)
                                return (FALSE);
                }
        }
        return (TRUE);
}
 
/*
 * Move the cursor backward by
 * "n" words. All of the details of motion
 * are performed by the "backchar" and "forwchar"
 * routines. Error if you try to move beyond
 * the buffers.
 */
backword(f, n)
register int f, n;
{
        if (n < 0)
                return (forwword(f, -n));
        if (backchar(FALSE, 1) == FALSE)
                return (FALSE);
        while (n--) {
                while (inword() == FALSE) {
                        if (backchar(FALSE, 1) == FALSE)
                                return (FALSE);
                }
                while (inword() != FALSE) {
                        if (backchar(FALSE, 1) == FALSE)
                                return (FALSE);
                }
        }
        return (forwchar(FALSE, 1));
}
 
/*
 * Move the cursor forward by
 * the specified number of words. As you move,
 * convert any characters to upper case. Error
 * if you try and move beyond the end of the
 * buffer. Bound to "M-U".
 */
upperword(f, n)
register int f, n;
{
        register int    c;
 
        if (n < 0)
                return (FALSE);
        while (n--) {
                while (inword() == FALSE) {
                        if (forwchar(FALSE, 1) == FALSE)
                                return (FALSE);
                }
                while (inword() != FALSE) {
                        c = lgetc(curwp->w_dotp, curwp->w_doto);
                        if (islower(c)) {
                                c = toupper(c);
                                lputc(curwp->w_dotp, curwp->w_doto, c);
                                lchange(WFHARD);
                        }
                        if (forwchar(FALSE, 1) == FALSE)
                                return (FALSE);
                }
        }
        return (TRUE);
}
 
/*
 * Move the cursor forward by
 * the specified number of words. As you move
 * convert characters to lower case. Error if you
 * try and move over the end of the buffer.
 * Bound to "M-L".
 */
lowerword(f, n)
register int f, n;
{
        register int    c;
 
        if (n < 0)
                return (FALSE);
        while (n--) {
                while (inword() == FALSE) {
                        if (forwchar(FALSE, 1) == FALSE)
                                return (FALSE);
                }
                while (inword() != FALSE) {
                        c = lgetc(curwp->w_dotp, curwp->w_doto);
                        if (isupper(c)) {
                                c = tolower(c);
                                lputc(curwp->w_dotp, curwp->w_doto, c);
                                lchange(WFHARD);
                        }
                        if (forwchar(FALSE, 1) == FALSE)
                                return (FALSE);
                }
        }
        return (TRUE);
}
 
/*
 * Move the cursor forward by
 * the specified number of words. As you move
 * convert the first character of the word to upper
 * case, and subsequent characters to lower case. Error
 * if you try and move past the end of the buffer.
 * Bound to "M-C".
 */
capword(f, n)
register int f, n;
{
        register int    c;
 
        if (n < 0)
                return (FALSE);
        while (n--) {
                while (inword() == FALSE) {
                        if (forwchar(FALSE, 1) == FALSE)
                                return (FALSE);
                }
                if (inword() != FALSE) {
                        c = lgetc(curwp->w_dotp, curwp->w_doto);
                        if (islower(c)) {
                                c = toupper(c);
                                lputc(curwp->w_dotp, curwp->w_doto, c);
                                lchange(WFHARD);
                        }
                        if (forwchar(FALSE, 1) == FALSE)
                                return (FALSE);
                        while (inword() != FALSE) {
                                c = lgetc(curwp->w_dotp, curwp->w_doto);
                                if (isupper(c)) {
                                        c = tolower(c);
                                        lputc(curwp->w_dotp, curwp->w_doto, c);
                                        lchange(WFHARD);
                                }
                                if (forwchar(FALSE, 1) == FALSE)
                                        return (FALSE);
                        }
                }
        }
        return (TRUE);
}
 
/*
 * Kill forward by "n" words.
 * Remember the location of dot. Move forward
 * by the right number of words. Put dot back where
 * it was and issue the kill command for the
 * right number of characters. Bound to "M-D".
 */
delfword(f, n)
register int f, n;
{
        register int    size;
        register LINE   *dotp;
        register int    doto;
 
        if (n < 0)
                return (FALSE);
        dotp = curwp->w_dotp;
        doto = curwp->w_doto;
        size = 0;
        while (n--) {
                while (inword() == FALSE) {
                        if (forwchar(FALSE, 1) == FALSE)
                                return (FALSE);
                        ++size;
                }
                while (inword() != FALSE) {
                        if (forwchar(FALSE, 1) == FALSE)
                                return (FALSE);
                        ++size;
                }
        }
        curwp->w_dotp = dotp;
        curwp->w_doto = doto;
        return (ldelete(size, TRUE));
}
 
/*
 * Kill backwards by "n" words.
 * Move backwards by the desired number of
 * words, counting the characters. When dot is
 * finally moved to its resting place, fire off
 * the kill command. Bound to "M-Rubout" and
 * to "M-Backspace".
 */
delbword(f, n)
register int f, n;
{
        register int    size;
 
        if (n < 0)
                return (FALSE);
        if (backchar(FALSE, 1) == FALSE)
                return (FALSE);
        size = 0;
        while (n--) {
                while (inword() == FALSE) {
                        if (backchar(FALSE, 1) == FALSE)
                                return (FALSE);
                        ++size;
                }
                while (inword() != FALSE) {
                        if (backchar(FALSE, 1) == FALSE)
                                return (FALSE);
                        ++size;
                }
        }
        if (forwchar(FALSE, 1) == FALSE)
                return (FALSE);
        return (ldelete(size, TRUE));
}
 
/*
 * Return TRUE if the character at dot
 * is a character that is considered to be
 * part of a word. The word character list is hard
 * coded. Should be setable.
 */
inword()
{
        register int    c;
 
        if (curwp->w_doto == llength(curwp->w_dotp))
                return (FALSE);
        c = lgetc(curwp->w_dotp, curwp->w_doto);
        if (isalnum(c))
                return (TRUE);
        if (c=='$' || c=='_')   /* For identifiers */
                return (TRUE);
        return (FALSE);
}
 
/* FTOPUNCT : eXtended Command  Move forward to next punctuation mark.  Bound
 * to CTLX - >.
 */
 
ftopunct(f, n)
register int f, n;
{
        register int c;
 
        do      {
                if(forwchar(FALSE, 1) == FALSE)
                        return(FALSE);
                c = lgetc(curwp->w_dotp, curwp->w_doto);
                }
                while (! ispunct(c) || isspace(c))
                        ;
        return(TRUE);
}
 
/* BTOPUNCT : eXtended Command  Move backward to last punctuation mark.  Bound
 * to CTLX - <.
 */
 
btopunct(f, n)
register int f, n;
{
        register int c;
 
        do      {
                if(backchar(FALSE, 1) == FALSE)
                        return(FALSE);
                c = lgetc(curwp->w_dotp, curwp->w_doto);
                }
                while (! ispunct(c) || isspace(c))
                        ;
        return(TRUE);
}
 
/* FORWSENT : Meta Command  Move forward to end punctuation mark.
 * A sentence is defined by a full stop followed by white space.
 * Bound to M-E.
 */
 
forwsent(f, n)
register int f, n;
{
        register int c;
 
        if( n < 0)
                return(backsent(f, -n));
loop:   while(n--)
        {
                while(forwchar(FALSE, 1) != FALSE)
                        {
                        c = lgetc(curwp->w_dotp, curwp->w_doto);
                        if(llength(curwp->w_dotp) == NULL)
                                goto loop;
                        if(c=='.' ||  c=='!' || c=='?')
                                {
                                if (forwchar(FALSE, 1) == FALSE)
                                        return(FALSE);
                                c = lgetc(curwp->w_dotp, curwp->w_doto);
                                if (c <= ' ' || c == '\"'
                                    || curwp->w_doto == llength(curwp->w_dotp))
                                        goto loop;
                                }
                        }
                return(FALSE);  /* EOF or no sentence terminator found */
        }
        return(TRUE);
}
 
/* BACKSENT : Meta Command  Move backward to last terminal punctuation mark.
 * A sentence is defined by a full stop followed by white space.  This
 * function has trouble with some sentences at ends of lines.  Bound to
 * M-A.
 */
 
backsent(f, n)
register int f, n;
{
        register int c;
        register int d;
 
        if (n < 0)
                return (forwsent(f, -n));
 
        d = 'a';        /* d must begin as an alpha */
loop:   while (n--)
                {
                while(backchar(FALSE, 1) != FALSE)
                        {
                        if(llength(curwp->w_dotp) == NULL)
                                goto loop;
                        c = lgetc(curwp->w_dotp, curwp->w_doto);
                        if(c=='.' ||  c=='?' || c=='!')
                                if (d <= ' ' || d == '\"'
                                    || curwp->w_doto+1 == llength(curwp->w_dotp)
)
                                        {
                                        forwchar(FALSE, 1);
                                        goto loop;
                                        }
                        d = c;  /* d becomes the last fetched char */
                        }
                return(FALSE);  /* BOF or no sentence terminator found */
                }
        return(TRUE);
}
 
/* GOTEOP : META command Goto end of paragraph.  Each paragraph is a
 * block of text bordered by blanklines.  Bound to M-N.
 */
 
goteop(f, n)
register f, n;
{
        if ( n < 0)
                return(gotbop(f, -n));
        while (n)
                {
                if (curwp->w_dotp != curbp->b_linep)
                        forwline(NULL, 1);
                else
                        return(FALSE);
                if (llength(curwp->w_dotp) == NULL)
                        --n;
                }
        return(TRUE);
}
 
/* GOTBOP : META command Goto beginning of paragraph.  Each paragraph is
 * a block of text bordered by blanklines.  Bound to M-P.
 */
 
gotbop(f, n)
register f, n;
{
        if ( n < 0)
                return(goteop(f, -n));
        while (n)
                {
                if (lback(curwp->w_dotp) != curbp->b_linep)
                        backline(NULL, 1);
                else
                        return(FALSE);
                if (llength(curwp->w_dotp) == NULL)
                        --n;
                }
        return(TRUE);
}
SHAR_EOF
cat << \SHAR_EOF > search.c
/*
 * The functions in this file
 * implement commands that search in the
 * forward and backward directions. There are
 * no special characters in the search strings.
 * Probably should have a regular expression
 * search, or something like that.
 */
#include        <stdio.h>
#include        <ctype.h>
#include        "ed.h"
 
 
#define CCHR(x)  ((x)-'@')
 
#define SRCH_BEGIN      (0)                  /* Search sub-codes.    */
#define SRCH_FORW       (-1)
#define SRCH_BACK       (-2)
#define SRCH_PREV       (-3)
#define SRCH_NEXT       (-4)
#define SRCH_NOPR       (-5)
#define SRCH_ACCM       (-6)
#define NSRCH   128                     /* Undoable search commands.    */
 
typedef struct  {
        int     s_code;
        LINE    *s_dotp;
        int     s_doto;
}       SRCHCOM;
 
static  SRCHCOM cmds[NSRCH];
static  int     cip;
 
int     srch_lastdir = SRCH_NOPR;              /* Last search flags.   */
 
/*
 * Search forward.
 * Get a search string from the user, and search for it,
 * starting at ".". If found, "." gets moved to just after the
 * matched characters, and display does all the hard stuff.
 * If not found, it just prints a message.
 */
forwsearch(f, n)
register int f, n;
{
 
        if ((f=readpattern("Search", &pat)) != TRUE)
                return (f);
        if (forwsrch() == FALSE) {
                mlwrite("Not found");
                return (FALSE);
        }
        return (TRUE);
}
 
/*
 * Reverse search.
 * Get a search string from the  user, and search, starting at "."
 * and proceeding toward the front of the buffer. If found "." is left
 * pointing at the first character of the pattern [the last character that
 * was matched].
 */
backsearch(f, n)
register int f, n;
{
 
        if ((f=readpattern("Reverse search", &pat)) != TRUE)
                return (f);
        if (backsrch() == FALSE) {
                mlwrite("Not found");
                return (FALSE);
        }
        return (TRUE);
}
 
 
/*
 * This routine does the real work of a
 * forward search. The pattern is sitting in the external
 * variable "pat". If found, dot is updated, the window system
 * is notified of the change, and TRUE is returned. If the
 * string isn't found, FALSE is returned.
 */
forwsrch()
{
        register LINE   *clp;
        register int    cbo;
        register LINE   *tlp;
        register int    tbo;
        register char   *pp;
        register int    c;
 
        clp = curwp->w_dotp;
        cbo = curwp->w_doto;
        while (clp != curbp->b_linep) {
                if (cbo == llength(clp)) {
                        clp = lforw(clp);
                        cbo = 0;
                        c = '\n';
                } else
                        c = lgetc(clp, cbo++);
                if (eq(c, pat[0]) != FALSE) {
                        tlp = clp;
                        tbo = cbo;
                        pp  = &pat[1];
                        while (*pp != 0) {
                                if (tlp == curbp->b_linep)
                                        goto fail;
                                if (tbo == llength(tlp)) {
                                        tlp = lforw(tlp);
                                        if (tlp == curbp->b_linep)
                                                goto fail;
                                        tbo = 0;
                                        c = '\n';
                                } else
                                        c = lgetc(tlp, tbo++);
                                if (eq(c, *pp++) == FALSE)
                                        goto fail;
                        }
                        curwp->w_dotp  = tlp;
                        curwp->w_doto  = tbo;
                        curwp->w_flag |= WFMOVE;
                        return (TRUE);
                }
        fail:   ;
        }
        return (FALSE);
}
 
/*
 * This routine does the real work of a
 * backward search. The pattern is sitting in the external
 * variable "pat". If found, dot is updated, the window system
 * is notified of the change, and TRUE is returned. If the
 * string isn't found, FALSE is returned.
 */
backsrch()
{
        register LINE   *clp;
        register int    cbo;
        register LINE   *tlp;
        register int    tbo;
        register int    c;
        register char   *epp;
        register char   *pp;
 
        for (epp = &pat[0]; epp[1] != 0; ++epp)
                ;
        clp = curwp->w_dotp;
        cbo = curwp->w_doto;
        for (;;) {
                if (cbo == 0) {
                        clp = lback(clp);
                        if (clp == curbp->b_linep)
                                return (FALSE);
                        cbo = llength(clp)+1;
                }
                if (--cbo == llength(clp))
                        c = '\n';
                else
                        c = lgetc(clp,cbo);
                if (eq(c, *epp) != FALSE) {
                        tlp = clp;
                        tbo = cbo;
                        pp  = epp;
                        while (pp != &pat[0]) {
                                if (tbo == 0) {
                                        tlp = lback(tlp);
                                        if (tlp == curbp->b_linep)
                                                goto fail;
                                        tbo = llength(tlp)+1;
                                }
                                if (--tbo == llength(tlp))
                                        c = '\n';
                                else
                                        c = lgetc(tlp,tbo);
                                if (eq(c, *--pp) == FALSE)
                                        goto fail;
                        }
                        curwp->w_dotp  = tlp;
                        curwp->w_doto  = tbo;
                        curwp->w_flag |= WFMOVE;
                        return (TRUE);
                }
        fail:   ;
        }
}
 
/*
 * Compare two characters.
 * The "bc" comes from the buffer.
 * It has it's case folded out. The
 * "pc" is from the pattern.
 */
eq(bc, pc)
register int    bc;
register int    pc;
{
        if (bc>='a' && bc<='z')
                bc -= 0x20;
        if (pc>='a' && pc<='z')
                pc -= 0x20;
        if (bc == pc)
                return (TRUE);
        return (FALSE);
}
 
/*
 * Read a pattern.
 * Stash it in an external
 * variable. The calling function
 * passes the address of inpat. The "pat" is
 * not updated if the user types in
 * an empty line. If the user typed
 * an empty line, and there is no
 * old pattern, it is an error.
 * Display the old pattern, in the
 * style of Jeff Lomicka. There is
 * some do-it-yourself control
 * expansion.
 */
readpattern(prompt, inpat)
char    *prompt;
char    *inpat;
{
        register char   *cp1;
        register char   *cp2;
        register int    c;
        register int    s;
        char            tpat[NPAT+20];
 
        cp1 = &tpat[0];                         /* Copy prompt          */
        cp2 = prompt;
        while ((c = *cp2++) != '\0')
                *cp1++ = c;
        if (inpat[0] != '\0') {                 /* Old pattern          */
                *cp1++ = ' ';
                *cp1++ = '[';
                cp2 = &inpat[0];
                while ((c = *cp2++) != 0) {
                        if (cp1 < &tpat[NPAT+20-6]) {   /* "??]: \0"    */
                                if (c<0x20 || c==0x7F) {
                                        *cp1++ = '^';
                                        c ^= 0x40;
                                } else if (c == '%')    /* Map "%" to   */
                                        *cp1++ = c;     /* "%%".        */
                                *cp1++ = c;
                        }
                }
                *cp1++ = ']';
        }
        *cp1++ = ':';                           /* Finish prompt        */
        *cp1++ = ' ';
        *cp1++ = '\0';
        s = mlreply(tpat, tpat, NPAT);          /* Read pattern         */
        if (s == TRUE)                          /* Specified            */
                strcpy(inpat, tpat);
        else if (s==FALSE && inpat[0]!=0)               /* CR, but old one */
                s = TRUE;
        return (s);
}
 
/*
 * Use incremental searching, initially in the forward direction.
 * isearch ignores any explicit arguments.
 */
forwisearch(f, n)
register int f, n;
{
        return (isearch(SRCH_FORW));
}
 
/*
 * Use incremental searching, initially in the reverse direction.
 * isearch ignores any explicit arguments.
 */
backisearch(f, n)
register int f, n;
{
        return (isearch(SRCH_BACK));
}
 
/*
 * Incremental Search.
 *      dir is used as the initial direction to search.
 *      ^N      find next occurance  (if first thing typed reuse old string).
 *      ^P      find prev occurance  (if first thing typed reuse old string).
 *      ^S      switch direction to forward, find next
 *      ^R      switch direction to reverse, find prev
 *      ^Q      quote next character (allows searching for ^N etc.)
 *      <ESC>   exit from Isearch.
 *      <DEL>   undoes last character typed. (tricky job to do this correctly).
 *      else    accumulate into search string
 */
isearch(dir)
int dir;
{
        register int    c;
        register LINE   *clp;
        register int    cbo;
        register int    success;
        int          pptr;
 
        for (cip=0; cip<NSRCH; cip++)
                cmds[cip].s_code = SRCH_NOPR;
        cip = 0;
        pptr = -1;
        clp = curwp->w_dotp;
        cbo = curwp->w_doto;
        is_lpush();
        is_cpush(SRCH_BEGIN);
        success = TRUE;
        is_prompt(dir, TRUE, success);
        for (;;) {
                update();
                switch (c = ttgetc()) {
                case CCHR('M'):
                case METACH:
                        srch_lastdir = dir;
                        mlwrite("[Done]");
                        return (TRUE);
 
                case CCHR('G'):
                        curwp->w_dotp = clp;
                        curwp->w_doto = cbo;
                        curwp->w_flag |= WFMOVE;
                        srch_lastdir = dir;
                        ctrlg(FALSE, 0);
                        return (FALSE);
 
                case CCHR('S'):
                case CCHR('F'):
                        if (dir == SRCH_BACK) {
                                dir = SRCH_FORW;
                                is_lpush();
                                is_cpush(SRCH_FORW);
                                success = TRUE;
                        }
                        /* Drop through to find next. */
                case CCHR('N'):
                        if (success==FALSE && dir==SRCH_FORW)
                                break;
                        is_lpush();
                        forwchar(FALSE, 1);
                        if (is_find(SRCH_NEXT) != FALSE) {
                                is_cpush(SRCH_NEXT);
                                pptr = strlen(pat);
                        } else {
                                backchar(FALSE, 1);
                                (*term.t_beep)();
                                success = FALSE;
                        }
                        is_prompt(dir, FALSE, success);
                        break;
 
                case CCHR('R'):
                case CCHR('B'):
                        if (dir == SRCH_FORW) {
                                dir = SRCH_BACK;
                                is_lpush();
                                is_cpush(SRCH_BACK);
                                success = TRUE;
                        }
                        /* Drop through to find previous. */
                case CCHR('P'):
                        if (success==FALSE && dir==SRCH_BACK)
                                break;
                        is_lpush();
                        backchar(FALSE, 1);
                        if (is_find(SRCH_PREV) != FALSE) {
                                is_cpush(SRCH_PREV);
                                pptr = strlen(pat);
                        } else {
                                forwchar(FALSE, 1);
                                (*term.t_beep)();
                                success = FALSE;
                        }
                        is_prompt(dir,FALSE,success);
                        break;
 
                case 0x7F:
                        if (is_undo(&pptr, &dir) != TRUE)
                                return (ABORT);
                        if (is_peek() != SRCH_ACCM)
                                success = TRUE;
                        is_prompt(dir, FALSE, success);
                        break;
 
                case CCHR('^'):
                case CCHR('Q'):
                        c = ttgetc();
                case CCHR('U'):
                case CCHR('X'):
                case CCHR('J'):
                        goto  addchar;
 
                default:
                        if (iscntrl(c) != FALSE) {
                                c += '@';
                                c |= CTRL;
                                success = execute(c, FALSE, 1);
                                curwp->w_flag |= WFMOVE;
                                return (success);
                        }
                addchar:
                        if (pptr == -1)
                                pptr = 0;
                        if (pptr == 0)
                                success = TRUE;
                        pat[pptr++] = c;
                        if (pptr == NPAT) {
                                mlwrite("Pattern too long");
                                ctrlg(FALSE, 0);
                                return (ABORT);
                        }
                        pat[pptr] = '\0';
                        is_lpush();
                        if (success != FALSE) {
                                if (is_find(dir) != FALSE)
                                        is_cpush(c);
                                else {
                                        success = FALSE;
                                        (*term.t_beep)();
                                        is_cpush(SRCH_ACCM);
                                }
                        } else
                                is_cpush(SRCH_ACCM);
                        is_prompt(dir, FALSE, success);
                }
        }
}
 
is_cpush(cmd)
register int    cmd;
{
        if (++cip >= NSRCH)
                cip = 0;
        cmds[cip].s_code = cmd;
}
 
is_lpush()
{
        register int    ctp;
 
        ctp = cip+1;
        if (ctp >= NSRCH)
                ctp = 0;
        cmds[ctp].s_code = SRCH_NOPR;
        cmds[ctp].s_doto = curwp->w_doto;
        cmds[ctp].s_dotp = curwp->w_dotp;
}
 
is_pop()
{
        if (cmds[cip].s_code != SRCH_NOPR) {
                curwp->w_doto  = cmds[cip].s_doto;
                curwp->w_dotp  = cmds[cip].s_dotp;
                curwp->w_flag |= WFMOVE;
                cmds[cip].s_code = SRCH_NOPR;
        }
        if (--cip <= 0)
                cip = NSRCH-1;
}
 
is_peek()
{
        if (cip == 0)
                return (cmds[NSRCH-1].s_code);
        else
                return (cmds[cip-1].s_code);
}
 
is_undo(pptr, dir)
register int    *pptr;
register int    *dir;
{
        switch (cmds[cip].s_code) {
        case SRCH_NOPR:
        case SRCH_BEGIN:
        case SRCH_NEXT:
        case SRCH_PREV:
                break;
 
        case SRCH_FORW:
                *dir = SRCH_BACK;
                break;
 
        case SRCH_BACK:
                *dir = SRCH_FORW;
                break;
 
        case SRCH_ACCM:
        default:
                *pptr -= 1;
                if (*pptr < 0)
                        *pptr = 0;
                pat[*pptr] = '\0';
                break;
        }
        is_pop();
        return (TRUE);
}
 
is_find(dir)
register int    dir;
{
        register int    plen;
 
        plen = strlen(pat);
        if (plen != 0) {
                if (dir==SRCH_FORW || dir==SRCH_NEXT) {
                        backchar(FALSE, plen);
                        if (forwsrch() == FALSE) {
                                forwchar(FALSE, plen);
                                return (FALSE);
                        }
                        return (TRUE);
                }
                if (dir==SRCH_BACK || dir==SRCH_PREV) {
                        forwchar(FALSE, plen);
                        if (backsrch() == FALSE) {
                                backchar(FALSE, plen);
                                return (FALSE);
                        }
                        return (TRUE);
                }
                mlwrite("bad call to is_find");
                ctrlg(FALSE, 0);
                return (FALSE);
        }
        return (FALSE);
}
 
/*
 * If called with "dir" not one of SRCH_FORW
 * or SRCH_BACK, this routine used to print an error
 * message. It also used to return TRUE or FALSE,
 * depending on if it liked the "dir". However, none
 * of the callers looked at the status, so I just
 * made the checking vanish.
 */
is_prompt(dir, flag, success)
{
        if (dir == SRCH_FORW) {
                if (success != FALSE)
                        is_dspl("i-search forward", flag);
                else
                        is_dspl("failing i-search forward", flag);
        } else if (dir == SRCH_BACK) {
                if (success != FALSE)
                        is_dspl("i-search backward", flag);
                else
                        is_dspl("failing i-search backward", flag);
        }
}
 
/*
 * Prompt writing routine for the incremental search.
 * The "prompt" is just a string. The "flag" determines
 * if a "[ ]" or ":" embelishment is used.
 */
is_dspl(prompt, flag)
char    *prompt;
{
        if (flag != FALSE)
                mlwrite("%s [%s]", prompt, pat);
        else
                mlwrite("%s: %s", prompt, pat);
}
 
/* META Command Search and replace in forward direction only.  Prompts
 * before replacement allows user to ABORT or continue.  Calling with an
 * argument prevents case distinctions.  Bound to M-R.
 */
 
replace(f, n)
register int f, n;
{
        register int    s;
        register int    kludge;         /* Watch for saved line move    */
        register LINE   *clp;           /* saved line pointer           */
        char         toprompt[81];      /* temporary string             */
        char         prompt[81];        /* final prompt                 */
        char         *perptr, *index(); /* remap '%' to '%%' in prompt  */
        int          cbo;               /* offset into the saved line   */
        int          rcnt = 0;          /* Replacements made so far     */
        int          plen;              /* length of found string       */
 
        strcpy(prompt,"Query Replace ");
        if ((s=readpattern(prompt, &pat)) != TRUE)
                return (s);
        strcat(prompt,pat);
        strcat(prompt," with");
        if ((perptr=index(prompt,'%'))!=NULL)
                {
                *perptr = '\0';                 /* terminate prompt     */
                strcpy(toprompt,"%%");          /* setup mapping chars  */
                strcat(toprompt,++perptr);      /* append end of prompt */
                strcat(prompt,toprompt);        /* append end to prompt */
                }
        if ((s=readpattern(prompt, &rpat)) == ABORT)
                return (s);
        if (s == FALSE)
                rpat[0] = '\0';
        plen = strlen(pat);
 
        /*
         * Search forward repeatedly, checking each time whether to insert
         * or not.  The "!" case makes the check always true, so it gets put
         * into a tighter loop for efficiency.
         *
         * If we change the line that is the remembered value of dot, then
         * it is possible for the remembered value to move.  This causes great
         * pain when trying to return to the non-existant line.
         *
         * possible fixes:
         * 1) put a single, relocated marker in the WINDOW structure, handled
         *    like mark.  The problem now becomes a what if two are needed...
         * 2) link markers into a list that gets updated (auto structures for
         *    the nodes)
         * 3) Expand the mark into a stack of marks and add pushmark, popmark.
         */
 
        clp = curwp->w_dotp;        /* save the return location     */
        cbo = curwp->w_doto;
        while (forwsrch() == TRUE) {
        retry:
                update();
                switch (ttgetc()) {
                case ' ':
                case ',':
                        kludge = (curwp->w_dotp == clp);
                        if (lreplace(plen, rpat, f) == FALSE)
                                return (FALSE);
                        rcnt++;
                        if (kludge != FALSE)
                                clp = curwp->w_dotp;
                        break;
 
                case '.':
                        kludge = (curwp->w_dotp == clp);
                        if (lreplace(plen, rpat, f) == FALSE)
                                return (FALSE);
                        rcnt++;
                        if (kludge != FALSE)
                                clp = curwp->w_dotp;
                        goto stopsearch;
 
                case 0x07:
                        ctrlg(FALSE, 0);
                        goto stopsearch;
 
                case '!':
                        do {
                                kludge = (curwp->w_dotp == clp);
                                if (lreplace(plen, rpat, f) == FALSE)
                                        return (FALSE);
                                rcnt++;
                                if (kludge != FALSE)
                                        clp = curwp->w_dotp;
                        } while (forwsrch() == TRUE);
                        goto stopsearch;
 
                case 'n':
                        break;
 
                default:
mlwrite("<SP>[,] replace, [.] rep-end, [n] don't, [!] repl rest [C-G] quit");
                        goto retry;
                }
        }
stopsearch:
        curwp->w_dotp = clp;
        curwp->w_doto = cbo;
        curwp->w_flag |= WFHARD;
        update();
        if (rcnt == 0)
                mlwrite("[No replacements done]");
        else if (rcnt == 1)
                mlwrite("[1 replacement done]");
        else
                mlwrite("[%d replacements done]", rcnt);
        return (TRUE);
}
 
/*
 * Replace plen characters before dot with argument string.
 * Control-J characters in st are interpreted as newlines.
 * There is a casehack disable flag (normally it likes to match
 * case of replacement to what was there).
 */
lreplace(plen, st, f)
register int    plen;              /* length to remove       */
char        *st;                    /* replacement string          */
int          f;               /* case hack disable          */
{
        register int    rlen;      /* replacement length           */
        register int    rtype;    /* capitalization            */
        register int    c;            /* used for random characters   */
        register int    doto;      /* offset into line       */
 
        /*
         * Find the capitalization of the word that was found.
         * f says use exact case of replacement string (same thing that
         * happens with lowercase found), so bypass check.
         */
        backchar(TRUE, plen);
        rtype = __l;
        c = lgetc(curwp->w_dotp, curwp->w_doto);
        if (isupper(c)!=FALSE  &&  f==FALSE) {
                rtype = __u|__l;
                if (curwp->w_doto+1 < llength(curwp->w_dotp)) {
                        c = lgetc(curwp->w_dotp, curwp->w_doto+1);
                        if (isupper(c) != FALSE) {
                                rtype = __u;
                        }
                }
        }
 
        /*
         * make the string lengths match (either pad the line
         * so that it will fit, or scrunch out the excess).
         * be careful with dot's offset.
         */
        rlen = strlen(st);
        doto = curwp->w_doto;
        if (plen > rlen)
                ldelete(plen-rlen, FALSE);
        else if (plen < rlen) {
                if (linsert(rlen-plen, ' ') == FALSE)
                        return (FALSE);
        }
        curwp->w_doto = doto;
 
        /*
         * do the replacement:  If was capital, then place first
         * char as if upper, and subsequent chars as if lower.
         * If inserting upper, check replacement for case.
         */
        while ((c = *st++&0xff) != '\0') {
                if ((rtype&__u)!=0  &&  islower(c)!=0)
                        c = toupper(c);
                if (rtype == (__u|__l))
                        rtype = __l;
                if (c == '\n') {
                        if (curwp->w_doto == llength(curwp->w_dotp))
                                forwchar(FALSE, 1);
                        else {
                                ldelete(1, FALSE);
                                lnewline();
                        }
                } else if (curwp->w_dotp == curbp->b_linep) {
                        linsert(1, c);
                } else if (curwp->w_doto == llength(curwp->w_dotp)) {
                        ldelete(1, FALSE);
                        linsert(1, c);
                } else
                        lputc(curwp->w_dotp, curwp->w_doto++, c);
        }
        lchange(WFHARD);
        return (TRUE);
}
 
SHAR_EOF
cat << \SHAR_EOF > wc.c
/* File: wc.c
 * This file originally appeared in "The C Programming Language" (c) 1978.
 * Modified to become part of MicroEMACS.  Includes line and word information.
 * Searches current buffer only.
 */
 
#include <stdio.h>
#include <ctype.h>
#include "ed.h"
 
wc(f, n)
register int f, n;
{
 
        static double lpw = (0.00);
        static double tp1,tp2;
        register long nc, na, nw;
        register LINE *dlp;
        char lpwbuf[6];
        short dlo, inword;
 
        /*
         * nc = # of characters
         * f = # lines
         * nw = # words
         * na = # alpha characters
         * inword = "Are we in a word?"
        */
 
        inword = NO;
        f = 00;
        na = nw = nc = 0L;
        dlp = curwp->w_dotp;
        dlo = curwp->w_doto;
 
        gotobob(NULL, 1);
 
        while (curwp->w_dotp != curbp->b_linep)
                {
                ++nc;
                n = lgetc(curwp->w_dotp, curwp->w_doto);
                if (curwp->w_doto == llength(curwp->w_dotp))
                        {
                        ++f;
                        inword = NO;
                        }
                if (isspace(n))
                        inword = NO;
                else    if (inword == NO)
                                {
                                inword = YES;
                                ++nw;
                                }
                if (isalpha(n))
                        ++na;
                forwchar(NULL, 1);
                }
        --nw;
        curwp->w_dotp = dlp;
        curwp->w_doto = dlo;
        curwp->w_flag |= WFHARD;
        if (nw > 0L)            /* avoid division by zero */
                {
                tp1 = (double)na;
                tp2 = (double)nw;
                lpw = (double)(tp1/tp2);
                }
        ftoa(lpw, lpwbuf, 2);
        n = mlwrite("Length:=%D Lines:=%d Words:=%D Letters:=%D \
Avg. word:=%s letters", nc, f, nw, na, lpwbuf);
        return(n);
}
SHAR_EOF
#       End of shell archive
exit 0
%NONAME-W-NOMSG, Message number 00000000

RDROYA01@ULKYVX.BITNET.UUCP (02/06/87)

#       This is a shell archive.
#       Remove everything above and including the cut line.
#       Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar: Shell Archiver
#       Run the following text with /bin/sh to create:
#       line.c
#       word.c
#       search.c
#       wc.c
# This archive created: Tue Feb  3 17:57:11 1987
cat << \SHAR_EOF > line.c
/*
 * The functions in this file
 * are a general set of line management
 * utilities. They are the only routines that
 * touch the text. They also touch the buffer
 * and window structures, to make sure that the
 * necessary updating gets done. There are routines
 * in this file that handle the kill buffer too.
 * It isn't here for any good reason.
 *
 * Note that this code only updates the dot and
 * mark values in the window list. Since all the code
 * acts on the current window, the buffer that we
 * are editing must be being displayed, which means
 * that "b_nwnd" is non zero, which means that the
 * dot and mark values in the buffer headers are
 * nonsense.
 */
#include        <stdio.h>
#include        "ed.h"
 
#define NBLOCK  16                      /* Line block chunk size        */
#define KBLOCK  256                     /* Kill buffer block size       */
 
char    *kbufp  = NULL;                 /* Kill buffer data             */
int     kused   = 0;                    /* # of bytes used in KB        */
int     ksize   = 0;                    /* # of bytes allocated in KB   */
 
/*
 * This routine allocates a block
 * of memory large enough to hold a LINE
 * containing "used" characters. The block is
 * always rounded up a bit. Return a pointer
 * to the new block, or NULL if there isn't
 * any memory left. Print a message in the
 * message line if no space.
 */
LINE    *
lalloc(used)
register int    used;
{
        register LINE   *lp;
        register int    size;
 
        size = (used+NBLOCK-1) & ~(NBLOCK-1);
        if (size == 0)                          /* Assume that an empty */
                size = NBLOCK;                  /* line is for type-in. */
        if ((lp = (LINE *) malloc(sizeof(LINE)+size)) == NULL) {
                mlwrite("Cannot allocate %d bytes", size);
                return (NULL);
        }
        lp->l_size = size;
        lp->l_used = used;
        return (lp);
}
 
/*
 * Delete line "lp". Fix all of the
 * links that might point at it (they are
 * moved to offset 0 of the next line.
 * Unlink the line from whatever buffer it
 * might be in. Release the memory. The
 * buffers are updated too; the magic conditions
 * described in the above comments don't hold
 * here.
 */
lfree(lp)
register LINE   *lp;
{
        register BUFFER *bp;
        register WINDOW *wp;
 
        wp = wheadp;
        while (wp != NULL) {
                if (wp->w_linep == lp)
                        wp->w_linep = lp->l_fp;
                if (wp->w_dotp  == lp) {
                        wp->w_dotp  = lp->l_fp;
                        wp->w_doto  = 0;
                }
                if (wp->w_markp == lp) {
                        wp->w_markp = lp->l_fp;
                        wp->w_marko = 0;
                }
                wp = wp->w_wndp;
        }
        bp = bheadp;
        while (bp != NULL) {
                if (bp->b_nwnd == 0) {
                        if (bp->b_dotp  == lp) {
                                bp->b_dotp = lp->l_fp;
                                bp->b_doto = 0;
                        }
                        if (bp->b_markp == lp) {
                                bp->b_markp = lp->l_fp;
                                bp->b_marko = 0;
                        }
                }
                bp = bp->b_bufp;
        }
        lp->l_bp->l_fp = lp->l_fp;
        lp->l_fp->l_bp = lp->l_bp;
        free((char *) lp);
}
 
/*
 * This routine gets called when
 * a character is changed in place in the
 * current buffer. It updates all of the required
 * flags in the buffer and window system. The flag
 * used is passed as an argument; if the buffer is being
 * displayed in more than 1 window we change EDIT to
 * HARD. Set MODE if the mode line needs to be
 * updated (the "*" has to be set).
 */
lchange(flag)
register int    flag;
{
        register WINDOW *wp;
 
        if (curbp->b_nwnd != 1)                 /* Ensure hard.         */
                flag = WFHARD;
        if ((curbp->b_flag&BFCHG) == 0) {       /* First change, so     */
                flag |= WFMODE;                 /* update mode lines.   */
                curbp->b_flag |= BFCHG;
        }
        wp = wheadp;
        while (wp != NULL) {
                if (wp->w_bufp == curbp)
                        wp->w_flag |= flag;
                wp = wp->w_wndp;
        }
}
 
/*
 * Insert "n" copies of the character "c"
 * at the current location of dot. In the easy case
 * all that happens is the text is stored in the line.
 * In the hard case, the line has to be reallocated.
 * When the window list is updated, take special
 * care; I screwed it up once. You always update dot
 * in the current window. You update mark, and a
 * dot in another window, if it is greater than
 * the place where you did the insert. Return TRUE
 * if all is well, and FALSE on errors.
 */
linsert(n, c)
register int n, c;
{
        register char   *cp1;
        register char   *cp2;
        register LINE   *lp1;
        register LINE   *lp2;
        register LINE   *lp3;
        register int    doto;
        register int    i;
        register WINDOW *wp;
 
        lchange(WFEDIT);
        lp1 = curwp->w_dotp;                    /* Current line         */
        if (lp1 == curbp->b_linep) {            /* At the end: special  */
                if (curwp->w_doto != 0) {
                        mlwrite("bug: linsert");
                        return (FALSE);
                }
                if ((lp2=lalloc(n)) == NULL)    /* Allocate new line    */
                        return (FALSE);
                lp3 = lp1->l_bp;                /* Previous line        */
                lp3->l_fp = lp2;                /* Link in              */
                lp2->l_fp = lp1;
                lp1->l_bp = lp2;
                lp2->l_bp = lp3;
                for (i=0; i<n; ++i)
                        lp2->l_text[i] = c;
                curwp->w_dotp = lp2;
                curwp->w_doto = n;
                return (TRUE);
        }
        doto = curwp->w_doto;                   /* Save for later.      */
        if (lp1->l_used+n > lp1->l_size) {      /* Hard: reallocate     */
                if ((lp2=lalloc(lp1->l_used+n)) == NULL)
                        return (FALSE);
                cp1 = &lp1->l_text[0];
                cp2 = &lp2->l_text[0];
                while (cp1 != &lp1->l_text[doto])
                        *cp2++ = *cp1++;
                cp2 += n;
                while (cp1 != &lp1->l_text[lp1->l_used])
                        *cp2++ = *cp1++;
                lp1->l_bp->l_fp = lp2;
                lp2->l_fp = lp1->l_fp;
                lp1->l_fp->l_bp = lp2;
                lp2->l_bp = lp1->l_bp;
                free((char *) lp1);
        } else {                                /* Easy: in place       */
                lp2 = lp1;                      /* Pretend new line     */
                lp2->l_used += n;
                cp2 = &lp1->l_text[lp1->l_used];
                cp1 = cp2-n;
                while (cp1 != &lp1->l_text[doto])
                        *--cp2 = *--cp1;
        }
        for (i=0; i<n; ++i)                     /* Add the characters   */
                lp2->l_text[doto+i] = c;
        wp = wheadp;                            /* Update windows       */
        while (wp != NULL) {
                if (wp->w_linep == lp1)
                        wp->w_linep = lp2;
                if (wp->w_dotp == lp1) {
                        wp->w_dotp = lp2;
                        if (wp==curwp || wp->w_doto>doto)
                                wp->w_doto += n;
                }
                if (wp->w_markp == lp1) {
                        wp->w_markp = lp2;
                        if (wp->w_marko > doto)
                                wp->w_marko += n;
                }
                wp = wp->w_wndp;
        }
        return (TRUE);
}
 
/*
 * Insert a newline into the buffer
 * at the current location of dot in the current
 * window. The funny ass-backwards way it does things
 * is not a botch; it just makes the last line in
 * the file not a special case. Return TRUE if everything
 * works out and FALSE on error (memory allocation
 * failure). The update of dot and mark is a bit
 * easier then in the above case, because the split
 * forces more updating.
 */
lnewline()
{
        register char   *cp1;
        register char   *cp2;
        register LINE   *lp1;
        register LINE   *lp2;
        register int    doto;
        register WINDOW *wp;
 
        lchange(WFHARD);
        lp1  = curwp->w_dotp;                   /* Get the address and  */
        doto = curwp->w_doto;                   /* offset of "."        */
        if ((lp2=lalloc(doto)) == NULL)         /* New first half line  */
                return (FALSE);
        cp1 = &lp1->l_text[0];                  /* Shuffle text around  */
        cp2 = &lp2->l_text[0];
        while (cp1 != &lp1->l_text[doto])
                *cp2++ = *cp1++;
        cp2 = &lp1->l_text[0];
        while (cp1 != &lp1->l_text[lp1->l_used])
                *cp2++ = *cp1++;
        lp1->l_used -= doto;
        lp2->l_bp = lp1->l_bp;
        lp1->l_bp = lp2;
        lp2->l_bp->l_fp = lp2;
        lp2->l_fp = lp1;
        wp = wheadp;                            /* Windows              */
        while (wp != NULL) {
                if (wp->w_linep == lp1)
                        wp->w_linep = lp2;
                if (wp->w_dotp == lp1) {
                        if (wp->w_doto < doto)
                                wp->w_dotp = lp2;
                        else
                                wp->w_doto -= doto;
                }
                if (wp->w_markp == lp1) {
                        if (wp->w_marko < doto)
                                wp->w_markp = lp2;
                        else
                                wp->w_marko -= doto;
                }
                wp = wp->w_wndp;
        }
        return (TRUE);
}
 
/*
 * This function deletes "n" bytes,
 * starting at dot. It understands how do deal
 * with end of lines, etc. It returns TRUE if all
 * of the characters were deleted, and FALSE if
 * they were not (because dot ran into the end of
 * the buffer. The "kflag" is TRUE if the text
 * should be put in the kill buffer.
 */
ldelete(n, kflag)
register int n, kflag;
{
        register char   *cp1;
        register char   *cp2;
        register LINE   *dotp;
        register int    doto;
        register int    chunk;
        register WINDOW *wp;
 
        while (n != 0) {
                dotp = curwp->w_dotp;
                doto = curwp->w_doto;
                if (dotp == curbp->b_linep)     /* Hit end of buffer.   */
                        return (FALSE);
                chunk = dotp->l_used-doto;      /* Size of chunk.       */
                if (chunk > n)
                        chunk = n;
                if (chunk == 0) {               /* End of line, merge.  */
                        lchange(WFHARD);
                        if (ldelnewline() == FALSE
                        || (kflag!=FALSE && kinsert('\n')==FALSE))
                                return (FALSE);
                        --n;
                        continue;
                }
                lchange(WFEDIT);
                cp1 = &dotp->l_text[doto];      /* Scrunch text.        */
                cp2 = cp1 + chunk;
                if (kflag != FALSE) {           /* Kill?                */
                        while (cp1 != cp2) {
                                if (kinsert(*cp1) == FALSE)
                                        return (FALSE);
                                ++cp1;
                        }
                        cp1 = &dotp->l_text[doto];
                }
                while (cp2 != &dotp->l_text[dotp->l_used])
                        *cp1++ = *cp2++;
                dotp->l_used -= chunk;
                wp = wheadp;                    /* Fix windows          */
                while (wp != NULL) {
                        if (wp->w_dotp==dotp && wp->w_doto>=doto) {
                                wp->w_doto -= chunk;
                                if (wp->w_doto < doto)
                                        wp->w_doto = doto;
                        }
                        if (wp->w_markp==dotp && wp->w_marko>=doto) {
                                wp->w_marko -= chunk;
                                if (wp->w_marko < doto)
                                        wp->w_marko = doto;
                        }
                        wp = wp->w_wndp;
                }
                n -= chunk;
        }
        return (TRUE);
}
 
/*
 * Delete a newline. Join the current line
 * with the next line. If the next line is the magic
 * header line always return TRUE; merging the last line
 * with the header line can be thought of as always being a
 * successful operation, even if nothing is done, and this makes
 * the kill buffer work "right". Easy cases can be done by
 * shuffling data around. Hard cases require that lines be moved
 * about in memory. Return FALSE on error and TRUE if all
 * looks ok. Called by "ldelete" only.
 */
ldelnewline()
{
        register char   *cp1;
        register char   *cp2;
        register LINE   *lp1;
        register LINE   *lp2;
        register LINE   *lp3;
        register WINDOW *wp;
 
        lp1 = curwp->w_dotp;
        lp2 = lp1->l_fp;
        if (lp2 == curbp->b_linep) {            /* At the buffer end.   */
                if (lp1->l_used == 0)           /* Blank line.          */
                        lfree(lp1);
                return (TRUE);
        }
        if (lp2->l_used <= lp1->l_size-lp1->l_used) {
                cp1 = &lp1->l_text[lp1->l_used];
                cp2 = &lp2->l_text[0];
                while (cp2 != &lp2->l_text[lp2->l_used])
                        *cp1++ = *cp2++;
                wp = wheadp;
                while (wp != NULL) {
                        if (wp->w_linep == lp2)
                                wp->w_linep = lp1;
                        if (wp->w_dotp == lp2) {
                                wp->w_dotp  = lp1;
                                wp->w_doto += lp1->l_used;
                        }
                        if (wp->w_markp == lp2) {
                                wp->w_markp  = lp1;
                                wp->w_marko += lp1->l_used;
                        }
                        wp = wp->w_wndp;
                }
                lp1->l_used += lp2->l_used;
                lp1->l_fp = lp2->l_fp;
                lp2->l_fp->l_bp = lp1;
                free((char *) lp2);
                return (TRUE);
        }
        if ((lp3=lalloc(lp1->l_used+lp2->l_used)) == NULL)
                return (FALSE);
        cp1 = &lp1->l_text[0];
        cp2 = &lp3->l_text[0];
        while (cp1 != &lp1->l_text[lp1->l_used])
                *cp2++ = *cp1++;
        cp1 = &lp2->l_text[0];
        while (cp1 != &lp2->l_text[lp2->l_used])
                *cp2++ = *cp1++;
        lp1->l_bp->l_fp = lp3;
        lp3->l_fp = lp2->l_fp;
        lp2->l_fp->l_bp = lp3;
        lp3->l_bp = lp1->l_bp;
        wp = wheadp;
        while (wp != NULL) {
                if (wp->w_linep==lp1 || wp->w_linep==lp2)
                        wp->w_linep = lp3;
                if (wp->w_dotp == lp1)
                        wp->w_dotp  = lp3;
                else if (wp->w_dotp == lp2) {
                        wp->w_dotp  = lp3;
                        wp->w_doto += lp1->l_used;
                }
                if (wp->w_markp == lp1)
                        wp->w_markp  = lp3;
                else if (wp->w_markp == lp2) {
                        wp->w_markp  = lp3;
                        wp->w_marko += lp1->l_used;
                }
                wp = wp->w_wndp;
        }
        free((char *) lp1);
        free((char *) lp2);
        return (TRUE);
}
 
/*
 * Delete all of the text
 * saved in the kill buffer. Called by commands
 * when a new kill context is being created. The kill
 * buffer array is released, just in case the buffer has
 * grown to immense size. No errors.
 */
kdelete()
{
        if (kbufp != NULL) {
                free((char *) kbufp);
                kbufp = NULL;
                kused = 0;
                ksize = 0;
        }
}
 
/*
 * Insert a character to the kill buffer,
 * enlarging the buffer if there isn't any room. Always
 * grow the buffer in chunks, on the assumption that if you
 * put something in the kill buffer you are going to put
 * more stuff there too later. Return TRUE if all is
 * well, and FALSE on errors.
 */
kinsert(c)
register int c;
{
        register char   *nbufp;
        register int    i;
 
        if (kused == ksize) {
                if ((nbufp =  malloc(ksize+KBLOCK)) == NULL)
                        return (FALSE);
                for (i=0; i<ksize; ++i)
                        nbufp[i] = kbufp[i];
                if (kbufp != NULL)
                        free((char *) kbufp);
                kbufp  = nbufp;
                ksize += KBLOCK;
        }
        kbufp[kused++] = c;
        return (TRUE);
}
 
/*
 * This function gets characters from
 * the kill buffer. If the character index "n" is
 * off the end, it returns "-1". This lets the caller
 * just scan along until it gets a "-1" back.
 */
kremove(n)
register int n;
{
        if (n >= kused)
                return (-1);
        else
                return (kbufp[n] & 0xFF);
}
SHAR_EOF
cat << \SHAR_EOF > word.c
/*
 * The routines in this file
 * implement commands that work word at
 * a time. There are all sorts of word mode
 * commands. If I do any sentence and/or paragraph
 * mode commands, they are likely to be put in
 * this file.  Added R.D.R Feb. 1986.
 */
#include        <stdio.h>
#include        <ctype.h>
#include        "ed.h"
 
 
/* Word wrap or fill wrap depending on f flag value.
 * Back-over whatever precedes the point on the current line and
 * stop on the first word-break or the beginning of the line.
 * If we reach the beginning of the line, jump back to the end of the
 * word and start a new line.  Otherwise, break the line at the
 * word-break, eat it, and jump back to the end of the word.
 * Returns TRUE on success, FALSE on errors.
 */
wrapword(f, n)
register int f, n;
{
        int oldp;
        oldp = curwp->w_dotp;
 
        if (! backwword(NULL, 1)) /* punctuation marks */
                return(FALSE);
        if (oldp != curwp->w_dotp && curwp->w_doto)
                {
                if (! backdel(NULL, 1))
                        return(FALSE);
                if (! newline(TRUE, 1))
                        return(FALSE);
                }
        if(f)
                return(forwwword(NULL, 1));
        return(forwwword(NULL, 1) && forwchar(NULL, 1) && backdel(NULL, 1));
}
 
/* FILLPAR Meta command  Fill paragraph to specified fill column and indent
 * column.  Bound to M-Q.
 */
 
fillpar(f, n)
register int f, n;
{
        register int s;
        register short omo;
        register LINE *omp;
 
        if (n > 1)
                setfillcol(f, n);
 
        if(fillcol == 0)
                {
                mlwrite("Fill column not set");
                (*term.t_beep)();
                return(FALSE);
                }
        omp = curwp->w_markp;
        omo = curwp->w_marko;
        curwp->w_markp = curwp->w_dotp;
        curwp->w_marko = curwp->w_doto;
 
        gotbop(FALSE, 1);
        forwchar(FALSE, 1);
        while((n = ltrw(FALSE, 1)) != EOF && n != NULL)
                forwline(NULL, 1);
        gotbop(FALSE, 1);
        forwchar(FALSE, 1);
        while (TRUE)
                {
                if (llength(curwp->w_dotp) == NULL)
                        break;
                if (curwp->w_dotp == curbp->b_linep)
                        break;
                if (getccol(FALSE) > fillcol)
                        {
                        if (wrapword(TRUE, NULL) == FALSE)
                                break;
                        continue;
                        }
                if (curwp->w_doto ==  llength(curwp->w_dotp)
                        && getccol(FALSE) <= fillcol)
                        {
                        if (forwchar(FALSE, 1) == FALSE)
                                break;
                        if (curwp->w_dotp == curbp->b_linep)
                                break;  /* @ EOB */
                        if (llength(curwp->w_dotp) == NULL)
                                break;  /* @ EOP */
                        if (backchar(FALSE, 1) == FALSE)
                                break;
                        if (clowsp(FALSE, NULL) == FALSE)
                                break;
                        continue;
                        }
                if (forwchar(FALSE, 1) == FALSE)
                        break;
                }
        curwp->w_dotp = curwp->w_markp;
        curwp->w_doto = curwp->w_marko;
        curwp->w_markp = omp;
        curwp->w_marko = omo;
        curwp->w_flag |= WFHARD;
        curgoal = getccol(FALSE);
        return(TRUE);
}
 
/*
 * PRIVATE VERSION OF INWORD() FOR WRAPWORD(), FORWWORD(), AND BACKWWORD()
 * Return FALSE if the character at dot
 * is a space character or above 0x7e.
 * Otherwise return TRUE.  Any printing
 * character below <DEL> that is not
 * a space character may appear inside a
 * word to be wrapped.  Using a special version
 * of inword() allows us to keep the usual meaning
 * of word for regular movement.
 */
static
inwword()
{
        register int    c;
 
        if (curwp->w_doto == llength(curwp->w_dotp))
                return (FALSE);
        c = lgetc(curwp->w_dotp, curwp->w_doto);
        if (isspace(c) || c> '~')
                return (FALSE);
        return (TRUE);
}
 
/*
 * PRIVATE VERSION OF BACKWORD() FOR WRAPWORD() AND FORWWWORD()
 * Move the cursor backward by
 * "n" words. All of the details of motion
 * are performed by the "backchar" and "forwchar"
 * routines. Error if you try to move beyond
 * the buffers.
 */
static
backwword(f, n)
register int f, n;
{
        if (backchar(FALSE, 1) == FALSE)
                return (FALSE);
        while (inwword() == FALSE)
                {
                if (backchar(FALSE, 1) == FALSE)
                        return (FALSE);
                }
        while (inwword() != FALSE)
                {
                if (backchar(FALSE, 1) == FALSE)
                        return (FALSE);
                }
        return (forwchar(FALSE, 1));
}
 
/* PRIVATE VERSION OF FORWWORD() FOR WRAPWORD() AND BACKWWORD()
 * Move the cursor forward by
 * the specified number of words. All of the
 * motion is done by "forwchar". Error if you
 * try and move beyond the buffer's end.
 */
static
forwwword(f, n)
register int f, n;
{
        while (inwword() == FALSE)
                {
                if (forwchar(FALSE, 1) == FALSE)
                        return (FALSE);
                }
        while (inwword() != FALSE)
                {
                if (forwchar(FALSE, 1) == FALSE)
                        return (FALSE);
                }
        return (TRUE);
}
 
/*
 * Move the cursor forward by
 * the specified number of words. All of the
 * motion is done by "forwchar". Error if you
 * try and move beyond the buffer's end.
 */
forwword(f, n)
register int f, n;
{
        if (n < 0)
                return (backword(f, -n));
        while (n--) {
                while (inword() == FALSE) {
                        if (forwchar(FALSE, 1) == FALSE)
                                return (FALSE);
                }
                while (inword() != FALSE) {
                        if (forwchar(FALSE, 1) == FALSE)
                                return (FALSE);
                }
        }
        return (TRUE);
}
 
/*
 * Move the cursor backward by
 * "n" words. All of the details of motion
 * are performed by the "backchar" and "forwchar"
 * routines. Error if you try to move beyond
 * the buffers.
 */
backword(f, n)
register int f, n;
{
        if (n < 0)
                return (forwword(f, -n));
        if (backchar(FALSE, 1) == FALSE)
                return (FALSE);
        while (n--) {
                while (inword() == FALSE) {
                        if (backchar(FALSE, 1) == FALSE)
                                return (FALSE);
                }
                while (inword() != FALSE) {
                        if (backchar(FALSE, 1) == FALSE)
                                return (FALSE);
                }
        }
        return (forwchar(FALSE, 1));
}
 
/*
 * Move the cursor forward by
 * the specified number of words. As you move,
 * convert any characters to upper case. Error
 * if you try and move beyond the end of the
 * buffer. Bound to "M-U".
 */
upperword(f, n)
register int f, n;
{
        register int    c;
 
        if (n < 0)
                return (FALSE);
        while (n--) {
                while (inword() == FALSE) {
                        if (forwchar(FALSE, 1) == FALSE)
                                return (FALSE);
                }
                while (inword() != FALSE) {
                        c = lgetc(curwp->w_dotp, curwp->w_doto);
                        if (islower(c)) {
                                c = toupper(c);
                                lputc(curwp->w_dotp, curwp->w_doto, c);
                                lchange(WFHARD);
                        }
                        if (forwchar(FALSE, 1) == FALSE)
                                return (FALSE);
                }
        }
        return (TRUE);
}
 
/*
 * Move the cursor forward by
 * the specified number of words. As you move
 * convert characters to lower case. Error if you
 * try and move over the end of the buffer.
 * Bound to "M-L".
 */
lowerword(f, n)
register int f, n;
{
        register int    c;
 
        if (n < 0)
                return (FALSE);
        while (n--) {
                while (inword() == FALSE) {
                        if (forwchar(FALSE, 1) == FALSE)
                                return (FALSE);
                }
                while (inword() != FALSE) {
                        c = lgetc(curwp->w_dotp, curwp->w_doto);
                        if (isupper(c)) {
                                c = tolower(c);
                                lputc(curwp->w_dotp, curwp->w_doto, c);
                                lchange(WFHARD);
                        }
                        if (forwchar(FALSE, 1) == FALSE)
                                return (FALSE);
                }
        }
        return (TRUE);
}
 
/*
 * Move the cursor forward by
 * the specified number of words. As you move
 * convert the first character of the word to upper
 * case, and subsequent characters to lower case. Error
 * if you try and move past the end of the buffer.
 * Bound to "M-C".
 */
capword(f, n)
register int f, n;
{
        register int    c;
 
        if (n < 0)
                return (FALSE);
        while (n--) {
                while (inword() == FALSE) {
                        if (forwchar(FALSE, 1) == FALSE)
                                return (FALSE);
                }
                if (inword() != FALSE) {
                        c = lgetc(curwp->w_dotp, curwp->w_doto);
                        if (islower(c)) {
                                c = toupper(c);
                                lputc(curwp->w_dotp, curwp->w_doto, c);
                                lchange(WFHARD);
                        }
                        if (forwchar(FALSE, 1) == FALSE)
                                return (FALSE);
                        while (inword() != FALSE) {
                                c = lgetc(curwp->w_dotp, curwp->w_doto);
                                if (isupper(c)) {
                                        c = tolower(c);
                                        lputc(curwp->w_dotp, curwp->w_doto, c);
                                        lchange(WFHARD);
                                }
                                if (forwchar(FALSE, 1) == FALSE)
                                        return (FALSE);
                        }
                }
        }
        return (TRUE);
}
 
/*
 * Kill forward by "n" words.
 * Remember the location of dot. Move forward
 * by the right number of words. Put dot back where
 * it was and issue the kill command for the
 * right number of characters. Bound to "M-D".
 */
delfword(f, n)
register int f, n;
{
        register int    size;
        register LINE   *dotp;
        register int    doto;
 
        if (n < 0)
                return (FALSE);
        dotp = curwp->w_dotp;
        doto = curwp->w_doto;
        size = 0;
        while (n--) {
                while (inword() == FALSE) {
                        if (forwchar(FALSE, 1) == FALSE)
                                return (FALSE);
                        ++size;
                }
                while (inword() != FALSE) {
                        if (forwchar(FALSE, 1) == FALSE)
                                return (FALSE);
                        ++size;
                }
        }
        curwp->w_dotp = dotp;
        curwp->w_doto = doto;
        return (ldelete(size, TRUE));
}
 
/*
 * Kill backwards by "n" words.
 * Move backwards by the desired number of
 * words, counting the characters. When dot is
 * finally moved to its resting place, fire off
 * the kill command. Bound to "M-Rubout" and
 * to "M-Backspace".
 */
delbword(f, n)
register int f, n;
{
        register int    size;
 
        if (n < 0)
                return (FALSE);
        if (backchar(FALSE, 1) == FALSE)
                return (FALSE);
        size = 0;
        while (n--) {
                while (inword() == FALSE) {
                        if (backchar(FALSE, 1) == FALSE)
                                return (FALSE);
                        ++size;
                }
                while (inword() != FALSE) {
                        if (backchar(FALSE, 1) == FALSE)
                                return (FALSE);
                        ++size;
                }
        }
        if (forwchar(FALSE, 1) == FALSE)
                return (FALSE);
        return (ldelete(size, TRUE));
}
 
/*
 * Return TRUE if the character at dot
 * is a character that is considered to be
 * part of a word. The word character list is hard
 * coded. Should be setable.
 */
inword()
{
        register int    c;
 
        if (curwp->w_doto == llength(curwp->w_dotp))
                return (FALSE);
        c = lgetc(curwp->w_dotp, curwp->w_doto);
        if (isalnum(c))
                return (TRUE);
        if (c=='$' || c=='_')   /* For identifiers */
                return (TRUE);
        return (FALSE);
}
 
/* FTOPUNCT : eXtended Command  Move forward to next punctuation mark.  Bound
 * to CTLX - >.
 */
 
ftopunct(f, n)
register int f, n;
{
        register int c;
 
        do      {
                if(forwchar(FALSE, 1) == FALSE)
                        return(FALSE);
                c = lgetc(curwp->w_dotp, curwp->w_doto);
                }
                while (! ispunct(c) || isspace(c))
                        ;
        return(TRUE);
}
 
/* BTOPUNCT : eXtended Command  Move backward to last punctuation mark.  Bound
 * to CTLX - <.
 */
 
btopunct(f, n)
register int f, n;
{
        register int c;
 
        do      {
                if(backchar(FALSE, 1) == FALSE)
                        return(FALSE);
                c = lgetc(curwp->w_dotp, curwp->w_doto);
                }
                while (! ispunct(c) || isspace(c))
                        ;
        return(TRUE);
}
 
/* FORWSENT : Meta Command  Move forward to end punctuation mark.
 * A sentence is defined by a full stop followed by white space.
 * Bound to M-E.
 */
 
forwsent(f, n)
register int f, n;
{
        register int c;
 
        if( n < 0)
                return(backsent(f, -n));
loop:   while(n--)
        {
                while(forwchar(FALSE, 1) != FALSE)
                        {
                        c = lgetc(curwp->w_dotp, curwp->w_doto);
                        if(llength(curwp->w_dotp) == NULL)
                                goto loop;
                        if(c=='.' ||  c=='!' || c=='?')
                                {
                                if (forwchar(FALSE, 1) == FALSE)
                                        return(FALSE);
                                c = lgetc(curwp->w_dotp, curwp->w_doto);
                                if (c <= ' ' || c == '\"'
                                    || curwp->w_doto == llength(curwp->w_dotp))
                                        goto loop;
                                }
                        }
                return(FALSE);  /* EOF or no sentence terminator found */
        }
        return(TRUE);
}
 
/* BACKSENT : Meta Command  Move backward to last terminal punctuation mark.
 * A sentence is defined by a full stop followed by white space.  This
 * function has trouble with some sentences at ends of lines.  Bound to
 * M-A.
 */
 
backsent(f, n)
register int f, n;
{
        register int c;
        register int d;
 
        if (n < 0)
                return (forwsent(f, -n));
 
        d = 'a';        /* d must begin as an alpha */
loop:   while (n--)
                {
                while(backchar(FALSE, 1) != FALSE)
                        {
                        if(llength(curwp->w_dotp) == NULL)
                                goto loop;
                        c = lgetc(curwp->w_dotp, curwp->w_doto);
                        if(c=='.' ||  c=='?' || c=='!')
                                if (d <= ' ' || d == '\"'
                                    || curwp->w_doto+1 == llength(curwp->w_dotp)
)
                                        {
                                        forwchar(FALSE, 1);
                                        goto loop;
                                        }
                        d = c;  /* d becomes the last fetched char */
                        }
                return(FALSE);  /* BOF or no sentence terminator found */
                }
        return(TRUE);
}
 
/* GOTEOP : META command Goto end of paragraph.  Each paragraph is a
 * block of text bordered by blanklines.  Bound to M-N.
 */
 
goteop(f, n)
register f, n;
{
        if ( n < 0)
                return(gotbop(f, -n));
        while (n)
                {
                if (curwp->w_dotp != curbp->b_linep)
                        forwline(NULL, 1);
                else
                        return(FALSE);
                if (llength(curwp->w_dotp) == NULL)
                        --n;
                }
        return(TRUE);
}
 
/* GOTBOP : META command Goto beginning of paragraph.  Each paragraph is
 * a block of text bordered by blanklines.  Bound to M-P.
 */
 
gotbop(f, n)
register f, n;
{
        if ( n < 0)
                return(goteop(f, -n));
        while (n)
                {
                if (lback(curwp->w_dotp) != curbp->b_linep)
                        backline(NULL, 1);
                else
                        return(FALSE);
                if (llength(curwp->w_dotp) == NULL)
                        --n;
                }
        return(TRUE);
}
SHAR_EOF
cat << \SHAR_EOF > search.c
/*
 * The functions in this file
 * implement commands that search in the
 * forward and backward directions. There are
 * no special characters in the search strings.
 * Probably should have a regular expression
 * search, or something like that.
 */
#include        <stdio.h>
#include        <ctype.h>
#include        "ed.h"
 
 
#define CCHR(x)  ((x)-'@')
 
#define SRCH_BEGIN      (0)                  /* Search sub-codes.    */
#define SRCH_FORW       (-1)
#define SRCH_BACK       (-2)
#define SRCH_PREV       (-3)
#define SRCH_NEXT       (-4)
#define SRCH_NOPR       (-5)
#define SRCH_ACCM       (-6)
#define NSRCH   128                     /* Undoable search commands.    */
 
typedef struct  {
        int     s_code;
        LINE    *s_dotp;
        int     s_doto;
}       SRCHCOM;
 
static  SRCHCOM cmds[NSRCH];
static  int     cip;
 
int     srch_lastdir = SRCH_NOPR;              /* Last search flags.   */
 
/*
 * Search forward.
 * Get a search string from the user, and search for it,
 * starting at ".". If found, "." gets moved to just after the
 * matched characters, and display does all the hard stuff.
 * If not found, it just prints a message.
 */
forwsearch(f, n)
register int f, n;
{
 
        if ((f=readpattern("Search", &pat)) != TRUE)
                return (f);
        if (forwsrch() == FALSE) {
                mlwrite("Not found");
                return (FALSE);
        }
        return (TRUE);
}
 
/*
 * Reverse search.
 * Get a search string from the  user, and search, starting at "."
 * and proceeding toward the front of the buffer. If found "." is left
 * pointing at the first character of the pattern [the last character that
 * was matched].
 */
backsearch(f, n)
register int f, n;
{
 
        if ((f=readpattern("Reverse search", &pat)) != TRUE)
                return (f);
        if (backsrch() == FALSE) {
                mlwrite("Not found");
                return (FALSE);
        }
        return (TRUE);
}
 
 
/*
 * This routine does the real work of a
 * forward search. The pattern is sitting in the external
 * variable "pat". If found, dot is updated, the window system
 * is notified of the change, and TRUE is returned. If the
 * string isn't found, FALSE is returned.
 */
forwsrch()
{
        register LINE   *clp;
        register int    cbo;
        register LINE   *tlp;
        register int    tbo;
        register char   *pp;
        register int    c;
 
        clp = curwp->w_dotp;
        cbo = curwp->w_doto;
        while (clp != curbp->b_linep) {
                if (cbo == llength(clp)) {
                        clp = lforw(clp);
                        cbo = 0;
                        c = '\n';
                } else
                        c = lgetc(clp, cbo++);
                if (eq(c, pat[0]) != FALSE) {
                        tlp = clp;
                        tbo = cbo;
                        pp  = &pat[1];
                        while (*pp != 0) {
                                if (tlp == curbp->b_linep)
                                        goto fail;
                                if (tbo == llength(tlp)) {
                                        tlp = lforw(tlp);
                                        if (tlp == curbp->b_linep)
                                                goto fail;
                                        tbo = 0;
                                        c = '\n';
                                } else
                                        c = lgetc(tlp, tbo++);
                                if (eq(c, *pp++) == FALSE)
                                        goto fail;
                        }
                        curwp->w_dotp  = tlp;
                        curwp->w_doto  = tbo;
                        curwp->w_flag |= WFMOVE;
                        return (TRUE);
                }
        fail:   ;
        }
        return (FALSE);
}
 
/*
 * This routine does the real work of a
 * backward search. The pattern is sitting in the external
 * variable "pat". If found, dot is updated, the window system
 * is notified of the change, and TRUE is returned. If the
 * string isn't found, FALSE is returned.
 */
backsrch()
{
        register LINE   *clp;
        register int    cbo;
        register LINE   *tlp;
        register int    tbo;
        register int    c;
        register char   *epp;
        register char   *pp;
 
        for (epp = &pat[0]; epp[1] != 0; ++epp)
                ;
        clp = curwp->w_dotp;
        cbo = curwp->w_doto;
        for (;;) {
                if (cbo == 0) {
                        clp = lback(clp);
                        if (clp == curbp->b_linep)
                                return (FALSE);
                        cbo = llength(clp)+1;
                }
                if (--cbo == llength(clp))
                        c = '\n';
                else
                        c = lgetc(clp,cbo);
                if (eq(c, *epp) != FALSE) {
                        tlp = clp;
                        tbo = cbo;
                        pp  = epp;
                        while (pp != &pat[0]) {
                                if (tbo == 0) {
                                        tlp = lback(tlp);
                                        if (tlp == curbp->b_linep)
                                                goto fail;
                                        tbo = llength(tlp)+1;
                                }
                                if (--tbo == llength(tlp))
                                        c = '\n';
                                else
                                        c = lgetc(tlp,tbo);
                                if (eq(c, *--pp) == FALSE)
                                        goto fail;
                        }
                        curwp->w_dotp  = tlp;
                        curwp->w_doto  = tbo;
                        curwp->w_flag |= WFMOVE;
                        return (TRUE);
                }
        fail:   ;
        }
}
 
/*
 * Compare two characters.
 * The "bc" comes from the buffer.
 * It has it's case folded out. The
 * "pc" is from the pattern.
 */
eq(bc, pc)
register int    bc;
register int    pc;
{
        if (bc>='a' && bc<='z')
                bc -= 0x20;
        if (pc>='a' && pc<='z')
                pc -= 0x20;
        if (bc == pc)
                return (TRUE);
        return (FALSE);
}
 
/*
 * Read a pattern.
 * Stash it in an external
 * variable. The calling function
 * passes the address of inpat. The "pat" is
 * not updated if the user types in
 * an empty line. If the user typed
 * an empty line, and there is no
 * old pattern, it is an error.
 * Display the old pattern, in the
 * style of Jeff Lomicka. There is
 * some do-it-yourself control
 * expansion.
 */
readpattern(prompt, inpat)
char    *prompt;
char    *inpat;
{
        register char   *cp1;
        register char   *cp2;
        register int    c;
        register int    s;
        char            tpat[NPAT+20];
 
        cp1 = &tpat[0];                         /* Copy prompt          */
        cp2 = prompt;
        while ((c = *cp2++) != '\0')
                *cp1++ = c;
        if (inpat[0] != '\0') {                 /* Old pattern          */
                *cp1++ = ' ';
                *cp1++ = '[';
                cp2 = &inpat[0];
                while ((c = *cp2++) != 0) {
                        if (cp1 < &tpat[NPAT+20-6]) {   /* "??]: \0"    */
                                if (c<0x20 || c==0x7F) {
                                        *cp1++ = '^';
                                        c ^= 0x40;
                                } else if (c == '%')    /* Map "%" to   */
                                        *cp1++ = c;     /* "%%".        */
                                *cp1++ = c;
                        }
                }
                *cp1++ = ']';
        }
        *cp1++ = ':';                           /* Finish prompt        */
        *cp1++ = ' ';
        *cp1++ = '\0';
        s = mlreply(tpat, tpat, NPAT);          /* Read pattern         */
        if (s == TRUE)                          /* Specified            */
                strcpy(inpat, tpat);
        else if (s==FALSE && inpat[0]!=0)               /* CR, but old one */
                s = TRUE;
        return (s);
}
 
/*
 * Use incremental searching, initially in the forward direction.
 * isearch ignores any explicit arguments.
 */
forwisearch(f, n)
register int f, n;
{
        return (isearch(SRCH_FORW));
}
 
/*
 * Use incremental searching, initially in the reverse direction.
 * isearch ignores any explicit arguments.
 */
backisearch(f, n)
register int f, n;
{
        return (isearch(SRCH_BACK));
}
 
/*
 * Incremental Search.
 *      dir is used as the initial direction to search.
 *      ^N      find next occurance  (if first thing typed reuse old string).
 *      ^P      find prev occurance  (if first thing typed reuse old string).
 *      ^S      switch direction to forward, find next
 *      ^R      switch direction to reverse, find prev
 *      ^Q      quote next character (allows searching for ^N etc.)
 *      <ESC>   exit from Isearch.
 *      <DEL>   undoes last character typed. (tricky job to do this correctly).
 *      else    accumulate into search string
 */
isearch(dir)
int dir;
{
        register int    c;
        register LINE   *clp;
        register int    cbo;
        register int    success;
        int          pptr;
 
        for (cip=0; cip<NSRCH; cip++)
                cmds[cip].s_code = SRCH_NOPR;
        cip = 0;
        pptr = -1;
        clp = curwp->w_dotp;
        cbo = curwp->w_doto;
        is_lpush();
        is_cpush(SRCH_BEGIN);
        success = TRUE;
        is_prompt(dir, TRUE, success);
        for (;;) {
                update();
                switch (c = ttgetc()) {
                case CCHR('M'):
                case METACH:
                        srch_lastdir = dir;
                        mlwrite("[Done]");
                        return (TRUE);
 
                case CCHR('G'):
                        curwp->w_dotp = clp;
                        curwp->w_doto = cbo;
                        curwp->w_flag |= WFMOVE;
                        srch_lastdir = dir;
                        ctrlg(FALSE, 0);
                        return (FALSE);
 
                case CCHR('S'):
                case CCHR('F'):
                        if (dir == SRCH_BACK) {
                                dir = SRCH_FORW;
                                is_lpush();
                                is_cpush(SRCH_FORW);
                                success = TRUE;
                        }
                        /* Drop through to find next. */
                case CCHR('N'):
                        if (success==FALSE && dir==SRCH_FORW)
                                break;
                        is_lpush();
                        forwchar(FALSE, 1);
                        if (is_find(SRCH_NEXT) != FALSE) {
                                is_cpush(SRCH_NEXT);
                                pptr = strlen(pat);
                        } else {
                                backchar(FALSE, 1);
                                (*term.t_beep)();
                                success = FALSE;
                        }
                        is_prompt(dir, FALSE, success);
                        break;
 
                case CCHR('R'):
                case CCHR('B'):
                        if (dir == SRCH_FORW) {
                                dir = SRCH_BACK;
                                is_lpush();
                                is_cpush(SRCH_BACK);
                                success = TRUE;
                        }
                        /* Drop through to find previous. */
                case CCHR('P'):
                        if (success==FALSE && dir==SRCH_BACK)
                                break;
                        is_lpush();
                        backchar(FALSE, 1);
                        if (is_find(SRCH_PREV) != FALSE) {
                                is_cpush(SRCH_PREV);
                                pptr = strlen(pat);
                        } else {
                                forwchar(FALSE, 1);
                                (*term.t_beep)();
                                success = FALSE;
                        }
                        is_prompt(dir,FALSE,success);
                        break;
 
                case 0x7F:
                        if (is_undo(&pptr, &dir) != TRUE)
                                return (ABORT);
                        if (is_peek() != SRCH_ACCM)
                                success = TRUE;
                        is_prompt(dir, FALSE, success);
                        break;
 
                case CCHR('^'):
                case CCHR('Q'):
                        c = ttgetc();
                case CCHR('U'):
                case CCHR('X'):
                case CCHR('J'):
                        goto  addchar;
 
                default:
                        if (iscntrl(c) != FALSE) {
                                c += '@';
                                c |= CTRL;
                                success = execute(c, FALSE, 1);
                                curwp->w_flag |= WFMOVE;
                                return (success);
                        }
                addchar:
                        if (pptr == -1)
                                pptr = 0;
                        if (pptr == 0)
                                success = TRUE;
                        pat[pptr++] = c;
                        if (pptr == NPAT) {
                                mlwrite("Pattern too long");
                                ctrlg(FALSE, 0);
                                return (ABORT);
                        }
                        pat[pptr] = '\0';
                        is_lpush();
                        if (success != FALSE) {
                                if (is_find(dir) != FALSE)
                                        is_cpush(c);
                                else {
                                        success = FALSE;
                                        (*term.t_beep)();
                                        is_cpush(SRCH_ACCM);
                                }
                        } else
                                is_cpush(SRCH_ACCM);
                        is_prompt(dir, FALSE, success);
                }
        }
}
 
is_cpush(cmd)
register int    cmd;
{
        if (++cip >= NSRCH)
                cip = 0;
        cmds[cip].s_code = cmd;
}
 
is_lpush()
{
        register int    ctp;
 
        ctp = cip+1;
        if (ctp >= NSRCH)
                ctp = 0;
        cmds[ctp].s_code = SRCH_NOPR;
        cmds[ctp].s_doto = curwp->w_doto;
        cmds[ctp].s_dotp = curwp->w_dotp;
}
 
is_pop()
{
        if (cmds[cip].s_code != SRCH_NOPR) {
                curwp->w_doto  = cmds[cip].s_doto;
                curwp->w_dotp  = cmds[cip].s_dotp;
                curwp->w_flag |= WFMOVE;
                cmds[cip].s_code = SRCH_NOPR;
        }
        if (--cip <= 0)
                cip = NSRCH-1;
}
 
is_peek()
{
        if (cip == 0)
                return (cmds[NSRCH-1].s_code);
        else
                return (cmds[cip-1].s_code);
}
 
is_undo(pptr, dir)
register int    *pptr;
register int    *dir;
{
        switch (cmds[cip].s_code) {
        case SRCH_NOPR:
        case SRCH_BEGIN:
        case SRCH_NEXT:
        case SRCH_PREV:
                break;
 
        case SRCH_FORW:
                *dir = SRCH_BACK;
                break;
 
        case SRCH_BACK:
                *dir = SRCH_FORW;
                break;
 
        case SRCH_ACCM:
        default:
                *pptr -= 1;
                if (*pptr < 0)
                        *pptr = 0;
                pat[*pptr] = '\0';
                break;
        }
        is_pop();
        return (TRUE);
}
 
is_find(dir)
register int    dir;
{
        register int    plen;
 
        plen = strlen(pat);
        if (plen != 0) {
                if (dir==SRCH_FORW || dir==SRCH_NEXT) {
                        backchar(FALSE, plen);
                        if (forwsrch() == FALSE) {
                                forwchar(FALSE, plen);
                                return (FALSE);
                        }
                        return (TRUE);
                }
                if (dir==SRCH_BACK || dir==SRCH_PREV) {
                        forwchar(FALSE, plen);
                        if (backsrch() == FALSE) {
                                backchar(FALSE, plen);
                                return (FALSE);
                        }
                        return (TRUE);
                }
                mlwrite("bad call to is_find");
                ctrlg(FALSE, 0);
                return (FALSE);
        }
        return (FALSE);
}
 
/*
 * If called with "dir" not one of SRCH_FORW
 * or SRCH_BACK, this routine used to print an error
 * message. It also used to return TRUE or FALSE,
 * depending on if it liked the "dir". However, none
 * of the callers looked at the status, so I just
 * made the checking vanish.
 */
is_prompt(dir, flag, success)
{
        if (dir == SRCH_FORW) {
                if (success != FALSE)
                        is_dspl("i-search forward", flag);
                else
                        is_dspl("failing i-search forward", flag);
        } else if (dir == SRCH_BACK) {
                if (success != FALSE)
                        is_dspl("i-search backward", flag);
                else
                        is_dspl("failing i-search backward", flag);
        }
}
 
/*
 * Prompt writing routine for the incremental search.
 * The "prompt" is just a string. The "flag" determines
 * if a "[ ]" or ":" embelishment is used.
 */
is_dspl(prompt, flag)
char    *prompt;
{
        if (flag != FALSE)
                mlwrite("%s [%s]", prompt, pat);
        else
                mlwrite("%s: %s", prompt, pat);
}
 
/* META Command Search and replace in forward direction only.  Prompts
 * before replacement allows user to ABORT or continue.  Calling with an
 * argument prevents case distinctions.  Bound to M-R.
 */
 
replace(f, n)
register int f, n;
{
        register int    s;
        register int    kludge;         /* Watch for saved line move    */
        register LINE   *clp;           /* saved line pointer           */
        char         toprompt[81];      /* temporary string             */
        char         prompt[81];        /* final prompt                 */
        char         *perptr, *index(); /* remap '%' to '%%' in prompt  */
        int          cbo;               /* offset into the saved line   */
        int          rcnt = 0;          /* Replacements made so far     */
        int          plen;              /* length of found string       */
 
        strcpy(prompt,"Query Replace ");
        if ((s=readpattern(prompt, &pat)) != TRUE)
                return (s);
        strcat(prompt,pat);
        strcat(prompt," with");
        if ((perptr=index(prompt,'%'))!=NULL)
                {
                *perptr = '\0';                 /* terminate prompt     */
                strcpy(toprompt,"%%");          /* setup mapping chars  */
                strcat(toprompt,++perptr);      /* append end of prompt */
                strcat(prompt,toprompt);        /* append end to prompt */
                }
        if ((s=readpattern(prompt, &rpat)) == ABORT)
                return (s);
        if (s == FALSE)
                rpat[0] = '\0';
        plen = strlen(pat);
 
        /*
         * Search forward repeatedly, checking each time whether to insert
         * or not.  The "!" case makes the check always true, so it gets put
         * into a tighter loop for efficiency.
         *
         * If we change the line that is the remembered value of dot, then
         * it is possible for the remembered value to move.  This causes great
         * pain when trying to return to the non-existant line.
         *
         * possible fixes:
         * 1) put a single, relocated marker in the WINDOW structure, handled
         *    like mark.  The problem now becomes a what if two are needed...
         * 2) link markers into a list that gets updated (auto structures for
         *    the nodes)
         * 3) Expand the mark into a stack of marks and add pushmark, popmark.
         */
 
        clp = curwp->w_dotp;        /* save the return location     */
        cbo = curwp->w_doto;
        while (forwsrch() == TRUE) {
        retry:
                update();
                switch (ttgetc()) {
                case ' ':
                case ',':
                        kludge = (curwp->w_dotp == clp);
                        if (lreplace(plen, rpat, f) == FALSE)
                                return (FALSE);
                        rcnt++;
                        if (kludge != FALSE)
                                clp = curwp->w_dotp;
                        break;
 
                case '.':
                        kludge = (curwp->w_dotp == clp);
                        if (lreplace(plen, rpat, f) == FALSE)
                                return (FALSE);
                        rcnt++;
                        if (kludge != FALSE)
                                clp = curwp->w_dotp;
                        goto stopsearch;
 
                case 0x07:
                        ctrlg(FALSE, 0);
                        goto stopsearch;
 
                case '!':
                        do {
                                kludge = (curwp->w_dotp == clp);
                                if (lreplace(plen, rpat, f) == FALSE)
                                        return (FALSE);
                                rcnt++;
                                if (kludge != FALSE)
                                        clp = curwp->w_dotp;
                        } while (forwsrch() == TRUE);
                        goto stopsearch;
 
                case 'n':
                        break;
 
                default:
mlwrite("<SP>[,] replace, [.] rep-end, [n] don't, [!] repl rest [C-G] quit");
                        goto retry;
                }
        }
stopsearch:
        curwp->w_dotp = clp;
        curwp->w_doto = cbo;
        curwp->w_flag |= WFHARD;
        update();
        if (rcnt == 0)
                mlwrite("[No replacements done]");
        else if (rcnt == 1)
                mlwrite("[1 replacement done]");
        else
                mlwrite("[%d replacements done]", rcnt);
        return (TRUE);
}
 
/*
 * Replace plen characters before dot with argument string.
 * Control-J characters in st are interpreted as newlines.
 * There is a casehack disable flag (normally it likes to match
 * case of replacement to what was there).
 */
lreplace(plen, st, f)
register int    plen;              /* length to remove       */
char        *st;                    /* replacement string          */
int          f;               /* case hack disable          */
{
        register int    rlen;      /* replacement length           */
        register int    rtype;    /* capitalization            */
        register int    c;            /* used for random characters   */
        register int    doto;      /* offset into line       */
 
        /*
         * Find the capitalization of the word that was found.
         * f says use exact case of replacement string (same thing that
         * happens with lowercase found), so bypass check.
         */
        backchar(TRUE, plen);
        rtype = __l;
        c = lgetc(curwp->w_dotp, curwp->w_doto);
        if (isupper(c)!=FALSE  &&  f==FALSE) {
                rtype = __u|__l;
                if (curwp->w_doto+1 < llength(curwp->w_dotp)) {
                        c = lgetc(curwp->w_dotp, curwp->w_doto+1);
                        if (isupper(c) != FALSE) {
                                rtype = __u;
                        }
                }
        }
 
        /*
         * make the string lengths match (either pad the line
         * so that it will fit, or scrunch out the excess).
         * be careful with dot's offset.
         */
        rlen = strlen(st);
        doto = curwp->w_doto;
        if (plen > rlen)
                ldelete(plen-rlen, FALSE);
        else if (plen < rlen) {
                if (linsert(rlen-plen, ' ') == FALSE)
                        return (FALSE);
        }
        curwp->w_doto = doto;
 
        /*
         * do the replacement:  If was capital, then place first
         * char as if upper, and subsequent chars as if lower.
         * If inserting upper, check replacement for case.
         */
        while ((c = *st++&0xff) != '\0') {
                if ((rtype&__u)!=0  &&  islower(c)!=0)
                        c = toupper(c);
                if (rtype == (__u|__l))
                        rtype = __l;
                if (c == '\n') {
                        if (curwp->w_doto == llength(curwp->w_dotp))
                                forwchar(FALSE, 1);
                        else {
                                ldelete(1, FALSE);
                                lnewline();
                        }
                } else if (curwp->w_dotp == curbp->b_linep) {
                        linsert(1, c);
                } else if (curwp->w_doto == llength(curwp->w_dotp)) {
                        ldelete(1, FALSE);
                        linsert(1, c);
                } else
                        lputc(curwp->w_dotp, curwp->w_doto++, c);
        }
        lchange(WFHARD);
        return (TRUE);
}
 
SHAR_EOF
cat << \SHAR_EOF > wc.c
/* File: wc.c
 * This file originally appeared in "The C Programming Language" (c) 1978.
 * Modified to become part of MicroEMACS.  Includes line and word information.
 * Searches current buffer only.
 */
 
#include <stdio.h>
#include <ctype.h>
#include "ed.h"
 
wc(f, n)
register int f, n;
{
 
        static double lpw = (0.00);
        static double tp1,tp2;
        register long nc, na, nw;
        register LINE *dlp;
        char lpwbuf[6];
        short dlo, inword;
 
        /*
         * nc = # of characters
         * f = # lines
         * nw = # words
         * na = # alpha characters
         * inword = "Are we in a word?"
        */
 
        inword = NO;
        f = 00;
        na = nw = nc = 0L;
        dlp = curwp->w_dotp;
        dlo = curwp->w_doto;
 
        gotobob(NULL, 1);
 
        while (curwp->w_dotp != curbp->b_linep)
                {
                ++nc;
                n = lgetc(curwp->w_dotp, curwp->w_doto);
                if (curwp->w_doto == llength(curwp->w_dotp))
                        {
                        ++f;
                        inword = NO;
                        }
                if (isspace(n))
                        inword = NO;
                else    if (inword == NO)
                                {
                                inword = YES;
                                ++nw;
                                }
                if (isalpha(n))
                        ++na;
                forwchar(NULL, 1);
                }
        --nw;
        curwp->w_dotp = dlp;
        curwp->w_doto = dlo;
        curwp->w_flag |= WFHARD;
        if (nw > 0L)            /* avoid division by zero */
                {
                tp1 = (double)na;
                tp2 = (double)nw;
                lpw = (double)(tp1/tp2);
                }
        ftoa(lpw, lpwbuf, 2);
        n = mlwrite("Length:=%D Lines:=%d Words:=%D Letters:=%D \
Avg. word:=%s letters", nc, f, nw, na, lpwbuf);
        return(n);
}
SHAR_EOF
#       End of shell archive
exit 0
%NONAME-W-NOMSG, Message number 00000000

RDROYA01@ULKYVX.BITNET.UUCP (02/06/87)

 
I just wanted to tell everyone who is receiving or has received the
source to uemail that the numbering scheme ("Uemail source (X of 12)")
was inaccurate, but all of the files went through as far as I can
tell.  However, you may have received two versions of the main.c (one
in posting 2 and another in posting 7).  That was a mistake on my part
in running the batch mailer on this end.  Also you may have received
two versions of parts 3 and 4.  Those are two versions of four
different files despite what the subject line says, again caused by my
ineptness with the batch mailer.  To make it easier for everyone to
check what you have I enclose the lnk file:
 
[tem[m:]]e:xemacs.68k=gemsnew,
m:main,
m:basic,
m:buffer,
m:file,
m:fileio,
m:line,
m:random,
m:misc,
m:region,
m:search,
m:word,
m:wc,
m:kermit,
m:kertrans,
m:kerrec,
m:kertty,
m:page,
m:print,
m:window,
m:display,
m:termio,
m:ttgetc,
m:tty,
m:shell,
m:path,
m:macros,
m:keybrd,
gemlib[in[_nobinary],in[_nofilesz],in[_nottyin],in[_maxfiles],in[_nowildc]],
libm
 
I hope all of these files have come through unscathed, but I know that
BITNET wrapped some of the code.  If you get errors in compilation,
check the file for wrapped strings.  Also, you will get one warning in
word.c about short assigned to pointer.  Ignore that warning if you
have Alcyon.  Fixing the code defeats the word wrap, and since I
didn't write that code, I'm not that sure how to fix it properly.  The
program runs fine with that warning and word wrap does too.
 
When you compile this source DO NOT compile with the -f floating
point.  On v. 4.14 that option produces code that c168 cannot understand
(probably because Alcyon has case sensitive command options and TOS
upcases everything), and on the earlier Alcyon version it produces code
identical to the -e option.  You will have to link with libm, not libf
because the program uses floating point.  Also the code will not
compile on a compiler that does not support unsigned char or unsigned
long.  The break function (kermit.c) requires unsigned char, and the
rettpa function (misc.c) uses unsigned long.  I have not used
structure assignment or passing in the code.
 
You will also need the new gemstart file that Allan Pratt wrote, the
one that uses mshrink.  You will need to set the STACK equate to 4.
Shell.c define _STKSIZ as 256K.  You can reduce that, but you won't be
able to run the compiler or linker (safely) while in the editor if you
do.  On CP/M-68K you can batch the whole compile/link process and run
it in a 128K buffer while inside the editor without any problems, but
on the ST you'll crash the system if you try this with the same
compiler and linker.
 
Hope you find some use for this software,
 
Robert Royar
Department of English
University of Louisville
Louisville, KY 40208
 
BITNET: RDROYA01@ULKYVX.BITNET
ARPA:   rdroya01%ulkyvx.bitnet@wiscvm.arpa
CIS:    72347,2767