[comp.os.minix] Sources of new version of Stevie

rtregn@immd3.informatik.uni-erlangen.de (Robert Regn) (12/15/88)

Many people asked me for the stevie sources.
Here they are (in 5 messages, the first contains the doc).
I send the full sources and not the diffs to 3.10 (some people suggest this)
because the diffs are more than 100k and not everybody will have 3.10.

Special answers for
- Helge Oldach: There is no 8Bit-character support. A modified regexp package
	is included.
- Dan Lancian: I have not seen the version posted to the atari group.
- martin leisner: I don't know any well-known anonymous ftpable place
	ask Tony Andrews (uunet!isis!onecom!wldrdg!tony)
- Jonathan C. Broome: Supporting large files (as the real vi) is very good.
	Please post diffs when you has added it to the new version !

stevie.doc contains 262 control chars - they were probably removed

------------------------------------------ cut ---------------------------
#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  ctags.doc porting.doc source.doc stevie.doc stevie.mm
# Wrapped by rtregn@faui32 on Wed Dec 14 16:56:15 1988
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'ctags.doc' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'ctags.doc'\"
else
echo shar: Extracting \"'ctags.doc'\" \(994 characters\)
sed "s/^X//" >'ctags.doc' <<'END_OF_FILE'
X
Xctags - first cut at a UNIX ctags re-implementation
X
X
XThis is a public domain rewrite of the standard UNIX ctags command.
XIt is a simplified version written primarily for use with the 'stevie'
Xeditor. The command line syntax is:
X
X	ctags [file ...]
X
XCtags scans the all files given on the command line. If no files are
Xgiven, the standard input is scanned for a list of file names.
X
XFunction declarations and macros are supported. However, only simple
Xforms of each are recognized. Functions must be of the following form:
X
Xtype
Xfname(...)
X
Xwhere "fname" is the name of the function and must come at the beginning
Xof a line. This is the form I always use, so the limitation doesn't
Xbother me.
X
XMacros (with or without parameters) of the following form are also detected:
X
X"#" [white space] "define" [white space] NAME
X
XThe white space between the "#" and "define" is optional.
X
X
XOther Limitations and Changes
X
XNo sorting or detection of duplicate functions is done.
X
X 
XTony Andrews
XAugust 1987
END_OF_FILE
if test 994 -ne `wc -c <'ctags.doc'`; then
    echo shar: \"'ctags.doc'\" unpacked with wrong size!
fi
# end of 'ctags.doc'
fi
if test -f 'porting.doc' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'porting.doc'\"
else
echo shar: Extracting \"'porting.doc'\" \(2372 characters\)
sed "s/^X//" >'porting.doc' <<'END_OF_FILE'
X
X		 Release Notes for STEVIE - Version 3.15
X
X		   Atari ST Editor for VI Enthusiasts
X
X			        Porting
X
X
X			      Tony Andrews
X
X			 	 3/27/88
X
X
X	Porting the editor is a relatively simple task. Most of the
Xcode is pretty machine-independent. For each environment, there is
Xa file of routines that perform various low-level operations that
Xtend to vary a lot from one machine to another. Another file contains
Xthe escape sequences to be used for each machine.
X
X	The machine-dependent files currently used are:
X
Xtos.c:	Atari ST - ifdef for either Megamax or Alcyon
X
Xunix.c:	UNIX System V
X
Xos2.c:	Microsoft OS/2
X
X
X	Each of these files are around 150 lines long and deal with
Xlow-level issues like character I/O to the terminal, terminal
Xinitialization, cursor addressing, and so on. There are different
Xtradeoffs to be made depending on the environment. For example, the
XUNIX version buffers terminal output because of the relatively high
Xoverhead of system calls. A quick look at the files will make it clear
Xwhat needs to be done in a new environment.
X
X	Terminal escape sequences are in the file "term.h". These are
Xdefined statically, for the time being. There is some discussion in
Xterm.h regarding which sequences are optional and which are not. The
Xeditor is somewhat flexible in dealing with a lack of terminal
Xcapabilities.
X
X	Because not all C compilers support command line macro definitions,
Xthe #define's for system-specific macros are placed in the file 'env.h'.
XIf you port to a new system, add another line there to define the macro you
Xchoose for your port.
X
X	The basic process for doing a new port is:
X
X	1. Come up with a macro name to use when ifdef'ing your system-
X	   specific changes. Add a line to 'env.h' to define the macro
X	   name you've chosen.
X
X	2. Look at unix.c, tos.c, and os2.c and copy the one that comes
X	   closest to working on your system. Then modify your new file
X	   as needed.
X
X	3. Look at term.h and edit the file appropriately adding a new
X	   set of escape sequence definitions for your system.
X
X	4. If you haven't already, get a copy of Henry Spencer's regular
X	   expression library and compile it. This has been very simple
X	   every time I've done it.
X
X	5. Compiling and debug the editor.
X
X
X	In most cases it should really be that simple. I've done two
Xports (UNIX and OS/2) and both were completed in just a couple of hours.
END_OF_FILE
if test 2372 -ne `wc -c <'porting.doc'`; then
    echo shar: \"'porting.doc'\" unpacked with wrong size!
fi
# end of 'porting.doc'
fi
if test -f 'source.doc' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'source.doc'\"
else
echo shar: Extracting \"'source.doc'\" \(4609 characters\)
sed "s/^X//" >'source.doc' <<'END_OF_FILE'
X
X		 Release Notes for STEVIE - Version 3.15
X
X			      Source Notes
X
X			      Tony Andrews
X
X			 	 3/27/88
X
X
XOverview
X--------
X
X	This file provides a brief description of the source code for
XStevie. The data structures are described later as well. For information
Xspecific to porting the editor, see the file 'porting.doc'. This document
Xis more relevant to people who want to hack on the editor apart from doing
Xa simple port.
X
X	Most of this document was written some time ago so a lot of the
Xdiscussion centers on problems related to the Atari ST environment and
Xcompilers. Most of this can be ignored for other systems.
X
XThings You Need
X---------------
X
X	Stevie has been compiled with both the Alcyon (4.14A) and the
XMegamax C compilers. For the posted binary, Megamax was used because
Xit's less buggy and provides a reasonable malloc(). Ports to other
Xcompilers should be pretty simple. The current ifdef's for ALCYON and
XMEGAMAX should point to the potential trouble areas. (See 'porting.doc'
Xfor more information.)
X
X	The search code depends on Henry Spencer's regular expression
Xcode. I used a version I got from the net recently (as part of a 'grep'
Xposting) and had absolutely no trouble compiling it on the ST. Thanks,
XHenry!
X
X	The file 'getenv.c' contains a getenv routine that may or may
Xnot be needed with your compiler. My version works with Alcyon and
XMegamax, under either the Beckemeyer or Gulam shells.
X
X	Lastly, you need a decent malloc. Lots of stuff in stevie is
Xallocated dynamically. The malloc with Alcyon is problematic because
Xit allocates from the heap between the end of data and the top of stack.
XIf you make the stack big enough to edit large files, you wind up
Xwasting space when working with small files. Mallocs that get their memory
Xfrom GEMDOG (in fairly large chunks) are much better.
X
X
XCruft
X-----
X
X	Some artifacts from Tim Thompson's original version remain. In
Xsome cases, code has been re-written, with the original left in place
Xbut ifdef'd out. This will all be cleaned up eventually, but for now
Xit's sometimes useful to see how things used to work.
X
X
XData Structures
X---------------
X
X	A brief discussion of the evolution of the data structures will
Xdo much to clarify the code, and explain some of the strangeness you may
Xsee.
X
X	In the original version, the file was maintained in memory as a
Xsimple contiguous buffer. References to positions in the file were simply
Xcharacter pointers. Due to the obvious performance problems inherent in
Xthis approach, I made the following changes.
X
X	The file is now represented by a doubly linked list of 'line'
Xstructures defined as follows:
X
Xstruct	line {
X	struct	line	*prev, *next;	/* previous and next lines */
X	char	*s;			/* text for this line */
X	int	size;			/* actual size of space at 's' */
X	unsigned long	num;		/* line "number" */
X};
X
XThe members of the line structure are described more completely here:
X
Xprev	- pointer to the structure for the prior line, or NULL for the
X	  first line of the file
X
Xnext	- like 'prev' but points to the next line
X
Xs	- points to the contents of the line (null terminated)
X
Xsize	- contains the size of the chunk of space pointed to by s. This
X	  is used so we know when we can add text to a line without getting
X	  more space. When we DO need more space, we always get a little
X	  extra so we don't make so many calls to malloc.
X
Xnum	- This is a pseudo line number that makes it easy to compare
X	  positions within the file. Otherwise, we'd have to traverse
X	  all the links to tell which line came first.
X
X
X	Since character pointers served to mark file positions in the
Xoriginal, a similar data object was needed for the new data structures.
XThis purpose is served by the 'lptr' structure which is defined as:
X
Xstruct	lptr {
X	struct	line	*linep;		/* line we're referencing */
X	int	index;			/* position within that line */
X};
X
X
XThe member 'linep' points to the 'line' structure for the line containing
Xthe location of interest. The integer 'index' is the offset into the line
Xdata (member 's') of the character to be referenced.
X
XThe following typedef's are more commonly used:
X
Xtypedef	struct line	LINE;
Xtypedef	struct lptr	LPTR;
X
XMany operations that were trivial with character pointers had to be
Ximplemented by functions to manipulate LPTR's. Most of these are in the
Xfile 'ptrfunc.c'. There you'll find functions to increment, decrement,
Xand compare LPTR's.
X
XThis was the biggest change to the editor. Fortunately, I made this
Xchange very early on, while I was still doing the work on a UNIX system.
XUsing 'sdb' made it much easier to debug this code than if I had done it
Xon the ST.
X
END_OF_FILE
if test 4609 -ne `wc -c <'source.doc'`; then
    echo shar: \"'source.doc'\" unpacked with wrong size!
fi
# end of 'source.doc'
fi
if test -f 'stevie.doc' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'stevie.doc'\"
else
echo shar: Extracting \"'stevie.doc'\" \(28858 characters\)
sed "s/^X//" >'stevie.doc' <<'END_OF_FILE'
X
X
X
X                      STEVIE - An Aspiring VI Clone
X
X                          User Reference - 3.44
X
X                               Tony Andrews
X
X
X
X       1.  _O_v_e_r_v_i_e_w
X
X       STEVIE is an editor designed to mimic the interface  of  the
X       UNIX  editor  'vi'.  The name (ST Editor for VI Enthusiasts)
X       comes from the fact that the editor was  first  written  for
X       the  Atari  ST. The current version has been ported to UNIX,
X       Minix, MS-DOS, and OS/2, but I've left the name  intact  for
X       now.
X
X       This program is the result of many late  nights  of  hacking
X       over  the  last year or so. The first version was written by
X       Tim Thompson and posted to USENET. From  there,  I  reworked
X       the  data structures completely, added LOTS of features, and
X       generally improved the overall performance in the process.
X
X       I've labelled STEVIE an 'aspiring' vi clone as a warning  to
X       those  who  may expect too much. On the whole, the editor is
X       pretty complete. Almost all of the visual mode commands  are
X       supported.  I've tried very hard to capture the 'feel' of vi
X       by getting  the  little  things  right.  Making  lines  wrap
X       correctly,  supporting  true operators, and even getting the
X       cursor to land on the right place for tabs are  all  a  real
X       pain, but really help make the editor 'feel' right.
X
X       STEVIE may be freely distributed.  The  source  isn't  copy-
X       righted  or  restricted  in any way. If you pass the program
X       along, please include all the documentation and, if  practi-
X       cal, the source as well. I'm not fanatical about this, but I
X       tried to make STEVIE fairly portable and that doesn't do any
X       good if the source isn't available.
X
X       The remainder of this document describes  the  operation  of
X       the  editor.   This  is  intended  as  a reference for users
X       already familiar with the real vi editor.
X
X
X       2.  _S_t_a_r_t_i_n_g__t_h_e__E_d_i_t_o_r
X
X       The following command line forms are supported:
X
X       stevie [file ...]   Edit the specified file(s)
X
X       stevie -t tag       Start at the location of the given tag
X
X       stevie + file       Edit file starting at end
X
X
X
X
X
X                                  - 1 -
X
X
X
X
X
X
X
X       STEVIE                                        User Reference
X
X
X
X       stevie +n file      Edit file starting a line number 'n'
X
X       stevie +/pat file   Edit file starting at pattern 'pat'
X
X       If multiple files are given on the command line  (using  the
X       first  form),  the  ":n" command goes to the next file, ":N"
X       goes backward in the list, and ":rew" can be used to  rewind
X       back to the start of the file list.
X
X
X       3.  _S_e_t__C_o_m_m_a_n_d__O_p_t_i_o_n_s
X
X       The ":set" command works as usual to  set  parameters.  Each
X       parameter  has  a  long  and  an abbreviated name, either of
X       which may be used. Boolean parameters are set as in:
X
X            set showmatch
X
X       or cleared by:
X
X            set noshowmatch
X
X       Numeric parameters are set as in:
X
X            set scroll=5
X
X       Several parameters may be set with a single command:
X
X            set novb sm report=1
X
X       To see the status of all parameters use ":set  all".  Typing
X       ":set"  with  no  arguments  will show only those parameters
X       that have been changed.   The  supported  parameters,  their
X       names,  abbreviations,  defaults, and descriptions are shown
X       below:
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X                                  - 2 -
X
X
X
X
X
X
X
X       STEVIE                                        User Reference
X
X
X
X       autoindent  Short: ai, Default: noai, Type: Boolean
X                   When in insert mode, start new lines at the same
X                   column  as  the  prior  line. Unlike vi, you can
X                   backspace over the indentation.
X
X       backup      Short: bk, Default: nobk, Type: Boolean
X                   Leave a backup on file writes.
X
X       errorbells  Short: eb, Default: noeb, Type: Boolean
X                   Ring bell when error messages are shown.
X
X       ignorecase  Short: ic, Default: noic, Type: Boolean
X                   Ignore case in string searches.
X
X       lines       Short: lines, Default: lines=25, Type: Numeric
X                   Number of physical  lines  on  the  screen.  The
X                   default  value  actually  depends  on  the  host
X                   machine, but is generally 25.
X
X       list        Short: list, Default: nolist, Type: Boolean
X                   Show tabs and newlines graphically.
X
X       number      Short: nu, Default: nonu, Type: Boolean
X                   Display lines on  the  screen  with  their  line
X                   numbers.
X
X       report      Short: report, Default: report=5, Type: Numeric
X                   Minimum number of lines to report operations on.
X
X       return      Short: cr, Default: cr, Type: Boolean
X                   End lines with cr-lf when writing files.
X
X       scroll      Short: scroll, Default: scroll=12, Type: Numeric
X                   Number of lines to scroll for ^D & ^U.
X
X       showmatch   Short: sm, Default: nosm, Type: Boolean
X                   When a ), }, or ] is typed, show the matching (,
X                   {,  or [ if it's on the current screen by moving
X                   the cursor there briefly.
X
X       showmode    Short: mo, Default: nomo, Type: Boolean
X                   Show on status line when in insert mode.
X
X       tabstop     Short: ts, Default: ts=8, Type: Numeric
X                   Number of spaces in a tab.
X
X       wrapscan    Short: ws, Default: ws, Type: Boolean
X                   String searches wrap  around  the  ends  of  the
X                   file.
X
X
X
X
X
X                                  - 3 -
X
X
X
X
X
X
X
X       STEVIE                                        User Reference
X
X
X
X       vbell       Short: vb, Default: vb, Type: Boolean
X                   Use a visual bell, if possible. (novb for  audi-
X                   ble bell)
X
X       The EXINIT environment variable can be used  to  modify  the
X       default values on startup as in:
X
X            setenv EXINIT="set sm ts=4"
X
X       The 'backup' parameter, if set, causes the editor to  retain
X       a  backup of any files that are written. During file writes,
X       a backup is always kept for safety until the write  is  com-
X       pleted.  At  that  point,  the 'backup' parameter determines
X       whether the backup file is deleted.
X
X       In environments (e.g. OS/2 or TOS) where lines are  normally
X       terminated  by CR-LF, the 'return' parameter allows files to
X       be written with only a LF terminator (if  the  parameter  is
X       cleared).  This parameter is ignored on UNIX systems.
X
X       The 'lines' parameter tells the editor how many lines  there
X       are  on  the  screen.  This is useful on systems like the ST
X       (or OS/2 machines with an EGA adapter) where various  screen
X       resolutions  may  be  used.  By using the 'lines' parameter,
X       different screen sizes can be easily handled.
X
X
X       4.  _C_o_l_o_n__C_o_m_m_a_n_d_s
X
X       Several of the normal 'vi' colon commands are  supported  by
X       STEVIE.   Some  commands  may  be  preceded  by a line range
X       specification.  For commands that accept a range  of  lines,
X       the following address forms are supported:
X
X            addr
X            addr + number
X            addr - number
X
X       where 'addr' may be one of the following:
X
X            a line number
X            a mark (as in 'a or 'b)
X            '.' (the current line)
X            '$' (the last line)
X
X       An address range of "%" is accepted as  an  abbreviation  of
X       "1,$".
X
X
X
X
X
X
X
X                                  - 4 -
X
X
X
X
X
X
X
X       STEVIE                                        User Reference
X
X
X
X       4.1  _T_h_e__G_l_o_b_a_l__C_o_m_m_a_n_d
X
X       A limited form of the global command is supported, accepting
X       the following command form:
X
X            g/pattern/X
X
X       where X may be either 'd' or 'p' to delete  or  print  lines
X       that  match  the  given  pattern.  If a line range is given,
X       only those lines are checked for a match with  the  pattern.
X       If no range is given, all lines are checked.
X
X       If  the  trailing  command  character  is  omitted,  'p'  is
X       assumed.  In this case, the trailing slash is also optional.
X       The current version of the editor does not support the  undo
X       operation  following  the  deletion of lines with the global
X       command.
X
X       4.2  _T_h_e__S_u_b_s_t_i_t_u_t_e__C_o_m_m_a_n_d
X
X       The substitute command provides  a  powerful  mechanism  for
X       making  more complex substitutions than can be done directly
X       from visual mode.  The general form of the command is:
X
X            s/pattern/replacement/g
X
X       Each line in the given range (or the  current  line,  if  no
X       range  was  given)  is scanned for the given regular expres-
X       sion.  When found, the string that matched  the  pattern  is
X       replaced with the given replacement string.  If the replace-
X       ment  string  is  null,  each  matching  pattern  string  is
X       deleted.
X
X       The trailing 'g' is optional and, if present, indicates that
X       multiple  occurrences  of  'pattern' on a line should all be
X       replaced.
X
X       Some special sequences are  recognized  in  the  replacement
X       string.  The  ampersand  character is replaced by the entire
X       pattern that was matched.  For example, the  following  com-
X       mand  could be used to put all occurrences of 'foo' or 'bar'
X       within double quotes:
X
X            1,$s/foo|bar/"&"/g
X
X       The special sequence "\n" where 'n' is a digit from 1 to  9,
X       is  replaced  by  the  string  the matched the corresponding
X       parenthesized expression in the pattern. The following  com-
X       mand could be used to swap the first two parameters in calls
X       to the C function "foo":
X
X
X
X
X                                  - 5 -
X
X
X
X
X
X
X
X       STEVIE                                        User Reference
X
X
X
X            1,$s/foo\(([^,]*),([^,]*),/foo(\2,\1,/g
X
X       Like the global command, substitutions can't be undone  with
X       this version of the editor.
X
X       4.3  _F_i_l_e__M_a_n_i_p_u_l_a_t_i_o_n__C_o_m_m_a_n_d_s
X
X       The following table shows the  supported  file  manipulation
X       commands  as  well  as  some other 'ex' commands that aren't
X       described elsewhere:
X
X       :w              write the current file
X       :wq             write and quit
X       :x              write (if necessary) and quit
X       ZZ              same as ":x"
X
X       :e file         edit the named file
X       :e!             re-edit the current file, discarding changes
X       :e #            edit the alternate file
X
X       :w file         write the buffer to the named file
X       :x,yw file      write lines x through y to the named file
X       :r file         read the named file into the buffer
X
X       :n              edit the next file
X       :N              edit the previous file
X       :rew            rewind the file list
X
X       :f              show the current file name
X       :f name         change the current file name
X       :x=             show the line number of address 'x'
X
X       :ta tag         go to the named tag
X       ^]              like ":ta" using the current word as the tag
X
X       :help           display a command summary
X       :ve             show the version number
X
X       :sh             run an interactive shell
X       :!cmd           run a command
X
X       The ":help" command can also be invoked with the <HELP>  key
X       on  the  Atari  ST. This actually displays a pretty complete
X       summary of the real vi with unsupported  features  indicated
X       appropriately.
X
X       The commands above work pretty much like they  do  in  'vi'.
X       Most  of  the commands support a '!' suffix (if appropriate)
X       to discard any pending changes.
X
X
X
X
X
X                                  - 6 -
X
X
X
X
X
X
X
X       STEVIE                                        User Reference
X
X
X
X       5.  _S_t_r_i_n_g__S_e_a_r_c_h_e_s
X
X       String searches are supported, as in vi, accepting the usual
X       regular  expression  syntax.  This was done using a modified
X       form of Henry Spencer's regular expression library. I  added
X       code outside the library to support the '\<' and '\>' exten-
X       sions. This actually turned out to be pretty easy,  although
X       there may be some glitches in the way I did it.  The parame-
X       ter "ignorecase" can be set to ignore  case  in  all  string
X       searches.
X
X
X       6.  _O_p_e_r_a_t_o_r_s
X
X       The vi operators (d, c, y, !, <, and >) work as true  opera-
X       tors.  The  only exception is that the change operator works
X       only for character-oriented changes (like cw or c%) and  not
X       for line-oriented changes (like cL or c3j).
X
X
X       7.  _T_a_g_s
X
X       Tags are implemented and a fairly simple version of  'ctags'
X       is  supplied  with  the editor. The current version of ctags
X       will find functions and macros  following  a  specific  (but
X       common) form.  See 'ctags.doc' for a complete discussion.
X
X
X       8.  _S_y_s_t_e_m_-_S_p_e_c_i_f_i_c__C_o_m_m_e_n_t_s
X
X       The following sections provide additional relevant  informa-
X       tion for the systems to which STEVIE has been ported.
X
X       8.1  _A_t_a_r_i__S_T
X
X       8.1.1  _T_O_S  The editor has been tested in all three  resolu-
X       tions,  although  low  and  high  res.  are less tested than
X       medium. The 50-line high res. mode can be  used  by  setting
X       the  'lines' parameter to 50. Alternatively, the environment
X       variable 'LINES' can be set. The editor doesn't actively set
X       the  number  of  lines on the screen. It just operates using
X       the number of lines it was told.
X
X       The arrow keys, as well as the <INSERT>, <HELP>, and  <UNDO>
X       keys are all mapped appropriately.
X
X       8.1.2  _M_i_n_i_x  The editor  is  pretty  much  the  same  under
X       Minix, but many of the keyboard mappings aren't supported.
X
X
X
X
X
X
X                                  - 7 -
X
X
X
X
X
X
X
X       STEVIE                                        User Reference
X
X
X
X       8.2  _U_N_I_X
X
X       The editor has been ported to UNIX System  V  release  3  as
X       well as 4.2 BSD.  This was done mainly to get some profiling
X       data so I haven't put much effort into doing the  UNIX  ver-
X       sion  right. It's hard-coded for ansi-style escape sequences
X       and doesn't use the termcap/terminfo routines at all.
X
X       8.3  _O_S_/_2
X
X       This port was done because the editor that  comes  with  the
X       OS/2 developer's kit really stinks. Make sure 'ansi' mode is
X       on (using the 'ansi'  command).   The  OS/2  console  driver
X       doesn't  support  insert/delete line, so STEVIE bypasses the
X       driver and makes  the  appropriate  system  calls  directly.
X       This  is  all done in the system-specific part of the editor
X       so the kludge is at least localized.
X
X       The arrow keys, page up/down and home/end all do what  you'd
X       expect. The function keys are hard-coded to some useful mac-
X       ros until I can get true support for macros into the editor.
X       The current mappings are:
X
X            F1      :p <RETURN>
X            F2      :n <RETURN>
X            F3      :e # <RETURN>
X            F4      :rew <RETURN>
X            F5      [[
X            F6      ]]
X            F7      <<
X            F8      >>
X            F9      :x <RETURN>
X            F10     :help <RETURN>
X
X            S-F1    :p! <RETURN>
X            S-F2    :n! <RETURN>
X
X       8.4  _M_S_D_O_S
X
X       STEVIE has been ported to MSDOS 3.3 using  the  Microsoft  C
X       compiler,  version  5.1.  The keyboard mappings are the same
X       as for OS/2.  The only problem with the PC version  is  that
X       the inefficiency of the screen update code becomes painfully
X       apparent on slower machines.
X
X
X
X
X
X
X
X
X
X
X                                  - 8 -
X
X
X
X
X
X
X
X       STEVIE                                        User Reference
X
X
X
X       9.  _M_i_s_s_i_n_g__F_e_a_t_u_r_e_s
X
X         1.  Counts  aren't  yet  supported  everywhere  that  they
X             should be.
X
X         2.  Macros with support for function keys.
X
X         3.  More "set" options.
X
X         4.  Many others...
X
X
X       10.  _K_n_o_w_n__B_u_g_s__a_n_d__P_r_o_b_l_e_m_s
X
X         1.  The change operator is only half-way  implemented.  It
X             works for character motions but not line motions. This
X             isn't so bad since most change operations are  charac-
X             ter oriented anyway.
X
X         2.  The yank buffer uses statically allocated  memory,  so
X             large  yanks  will  fail.  If  a  delete spans an area
X             larger than the yank buffer, the program asks for con-
X             firmation  before  proceeding.  That  way, if you were
X             moving text, you don't get screwed by the limited yank
X             buffer.  You  just  have  to  move smaller chunks at a
X             time. All the  internal  buffers  (yank,  redo,  etc.)
X             need  to  be  reworked to allocate memory dynamically.
X             The 'undo' buffer is now dynamically allocated, so any
X             change can be undone.
X
X         3.  If you stay in insert mode for a long time, the insert
X             buffer  can overflow.  The editor will print a message
X             and dump you back into command mode.
X
X         4.  The current version of the  substitute  command  (i.e.
X             ":s/foo/bar") can't be undone.
X
X         5.  Several other less bothersome glitches...
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X                                  - 9 -
X
X
X
X
X
X
X
X       STEVIE                                        User Reference
X
X
X
X       11.  _C_o_n_c_l_u_s_i_o_n
X
X       The editor has reached a pretty stable state,  and  performs
X       well  on  the  systems  I  use  it on, so I'm pretty much in
X       maintenance mode now.  There's still plenty to be done;  the
X       screen  update  code  is  still  pretty  inefficient and the
X       yank/put code is still primitive.  But  after  more  than  a
X       year  of  hacking,  I'm  ready to work on new projects.  I'm
X       still interested in bug reports, and I do still  add  a  new
X       feature  from  time  to  time, but the rate of change is way
X       down now.
X
X       I'd like to thank Tim Thompson for writing the original ver-
X       sion  of  the  editor.  His  program was well structured and
X       quite readable. Thanks for giving me a  good  base  to  work
X       with.
X
X       If you're reading this file, but didn't get the source  code
X       for STEVIE, it can be had by sending a disk with return pos-
X       tage to the address given below. I can write disks  for  the
X       Atari  ST (SS or DS) or MSDOS (360K or 1.2M). Please be sure
X       to include the return postage. I don't intend to make  money
X       from this program, but I don't want to lose any either.
X
X       I'm not planning to try to coordinate the various  ports  of
X       STEVIE  that  may  occur. I just don't have the time. But if
X       you do port it, I'd be interested in hearing about it.
X
X            Tony Andrews            UUCP: onecom!wldrdg!tony
X            5902E Gunbarrel Ave.
X            Boulder, CO 80301
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X                                  - 10 -
X
X
X
X
X
X
X
X       STEVIE                                        User Reference
X
X
X
X       _C_h_a_r_a_c_t_e_r__F_u_n_c_t_i_o_n__S_u_m_m_a_r_y
X
X       The following list describes the meaning of  each  character
X       that's  used  by  the  editor. In some cases characters have
X       meaning in both command  and  insert  mode;  these  are  all
X       described.
X
X
X       ^@      The null character. Not used in any mode. This char-
X               acter may not be present in the file, as is the case
X               with vi.
X
X       ^B      Backward one screen.
X
X       ^D      Scroll the window down one half screen.
X
X       ^E      Scroll the screen up one line.
X
X       ^F      Forward one screen.
X
X       ^G      Same as ":f" command. Displays file information.
X
X       ^H      (Backspace) Moves cursor left one space  in  command
X               mode.   In  insert  mode,  erases the last character
X               typed.
X
X       ^J      Move the cursor down one line.
X
X       ^L      Clear and redraw the screen.
X
X       ^M      (Carriage return) Move to the first non-white  char-
X               acter  in  the next line. In insert mode, a carriage
X               return opens a new line for input.
X
X       ^N      Move the cursor down a line.
X
X       ^P      Move the cursor up a line.
X
X       ^U      Scroll the window up one half screen.
X
X       ^Y      Scroll the screen down one line.
X
X       ^[      Escape cancels a pending command  in  command  mode,
X               and is used to terminate insert mode.
X
X       ^]      Moves to the tag whose name is given by the word  in
X               which the cursor resides.
X
X       ^`      Same as ":e #" if supported (system-dependent).
X
X
X
X
X
X                                  - 11 -
X
X
X
X
X
X
X
X       STEVIE                                        User Reference
X
X
X
X       SPACE   Move the cursor right on column.
X
X       !       The filter operator always operates on  a  range  of
X               lines,  passing the lines as input to a program, and
X               replacing them with the output of the  program.  The
X               shorthand  command  "!!"  can  be  used  to filter a
X               number of lines (specified by  a  preceding  count).
X               The  command  "!"  is  replaced  by the last command
X               used, so "!!!<RETURN>"  runs  the  given  number  of
X               lines through the last specified command.
X
X       $       Move to the end of the current line.
X
X       %       If the cursor rests on a paren '()', brace '{}',  or
X               bracket '[]', move to the matching one.
X
X       '       Used to move the cursor to a previously marked posi-
X               tion,  as in 'a or 'b. The cursor moves to the start
X               of the marked line. The special mark  ''  refers  to
X               the "previous context".
X
X       +       Same as carriage return, in command mode.
X
X       ,       Reverse of the last t, T, f, or F command.
X
X       -       Move to the first non-white character in the  previ-
X               ous line.
X
X       .       Repeat the last edit command.
X
X       /       Start of a forward  string  search  command.  String
X               searches may be optionally terminated with a closing
X               slash. To search for a slash use '\/' in the  search
X               string.
X
X       0       Move to the start of the  current  line.  Also  used
X               within counts.
X
X       1-9     Used to add 'count' prefixes to commands.
X
X       :       Prefix character for "ex" commands.
X
X       ;       Repeat last t, T, f, or F command.
X
X       <       The 'left shift' operator.
X
X       >       The 'right shift' operator.
X
X       ?       Same as '/', but search backward.
X
X
X
X
X
X                                  - 12 -
X
X
X
X
X
X
X
X       STEVIE                                        User Reference
X
X
X
X       A       Append at the end of the current line.
X
X       B       Backward one blank-delimited word.
X
X       C       Change the rest of the current line.
X
X       D       Delete the rest of the current line.
X
X       E       End of the end of a blank-delimited word.
X
X       F       Find a character backward on the current line.
X
X       G       Go to  the  given  line  number  (end  of  file,  by
X               default).
X
X       H       Move to the first non-white char. on the top  screen
X               line.
X
X       I       Insert before  the  first  non-white  char.  on  the
X               current line.
X
X       J       Join two lines.
X
X       L       Move to the first  non-white  char.  on  the  bottom
X               screen line.
X
X       M       Move to the first  non-white  char.  on  the  middle
X               screen line.
X
X       N       Reverse the last string search.
X
X       O       Open a new line above the current  line,  and  start
X               inserting.
X
X       P       Put the yank/delete buffer before the current cursor
X               position.
X
X       R       Replace characters until an  "escape"  character  is
X               received.   Similar  to  insert  mode,  but replaces
X               instead of inserting.  Typing a newline  in  replace
X               mode  is  the  same as in insert mode, but replacing
X               continues on the new line.
X
X       T       Reverse search 'upto' the given character.
X
X       U       Restore the current line to  its  state  before  you
X               started changing it.
X
X       W       Move forward one blank-delimited word.
X
X
X
X
X
X                                  - 13 -
X
X
X
X
X
X
X
X       STEVIE                                        User Reference
X
X
X
X       X       Delete one character before the cursor.
X
X       Y       Yank the current line. Same as 'yy'.
X
X       ZZ      Exit from the editor, saving changes if necessary.
X
X       [[      Move backward one C function.
X
X       ]]      Move forward one C function.
X
X       ^       Move to the first non-white on the current line.
X
X       `       Move to the given mark, as with '.  The  distinction
X               between the two commands is important when used with
X               operators. I support the  difference  correctly.  If
X               you  don't know what I'm talking about, don't worry,
X               it won't matter to you.
X
X       a       Append text after the cursor.
X
X       b       Back one word.
X
X       c       The change operator.
X
X       d       The delete operator.
X
X       e       Move to the end of a word.
X
X       f       Find a character on the current line.
X
X       h       Move left one column.
X
X       i       Insert text before the cursor.
X
X       j       Move down one line.
X
X       k       Move up one line.
X
X       l       Move right one column.
X
X       m       Set a mark at the current position (e.g. ma or mb).
X
X       n       Repeat the last string search.
X
X       o       Open a new line and start inserting text.
X
X       p       Put the yank/delete buffer after the cursor.
X
X       r       Replace a character.
X
X
X
X
X
X                                  - 14 -
X
X
X
X
X
X
X
X       STEVIE                                        User Reference
X
X
X
X       s       Replace characters.
X
X       t       Move forward  'upto'  the  given  character  on  the
X               current line.
X
X       u       Undo the last edit.
X
X       w       Move forward one word.
X
X       x       Delete the character under the cursor.
X
X       y       The yank operator.
X
X       z       Redraw the screen with the current line at  the  top
X               (zRETURN), the middle (z.), or the bottom (z-).
X
X       |       Move to the column given by the preceding count.
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X                                  - 15 -
X
X
X
X
X
X
X
X
X
X
X
X                           STEVIE - User Guide
X
X                                 CONTENTS
X
X
X        1.  Overview...........................................   1
X
X        2.  Starting the Editor................................   1
X
X        3.  Set Command Options................................   2
X
X        4.  Colon Commands.....................................   4
X            4.1  The Global Command............................   5
X            4.2  The Substitute Command........................   5
X            4.3  File Manipulation Commands....................   6
X
X        5.  String Searches....................................   7
X
X        6.  Operators..........................................   7
X
X        7.  Tags...............................................   7
X
X        8.  System-Specific Comments...........................   7
X            8.1  Atari ST......................................   7
X            8.2  UNIX..........................................   8
X            8.3  OS/2..........................................   8
X            8.4  MSDOS.........................................   8
X
X        9.  Missing Features...................................   9
X
X       10.  Known Bugs and Problems............................   9
X
X       11.  Conclusion.........................................  10
X
X       Character Function Summary..............................  11
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X
X                                  - i -
X
X
X
X
END_OF_FILE
echo shar: 262 control characters may be missing from \"'stevie.doc'\"
if test 28858 -ne `wc -c <'stevie.doc'`; then
    echo shar: \"'stevie.doc'\" unpacked with wrong size!
fi
# end of 'stevie.doc'
fi
if test -f 'stevie.mm' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'stevie.mm'\"
else
echo shar: Extracting \"'stevie.mm'\" \(20348 characters\)
sed "s/^X//" >'stevie.mm' <<'END_OF_FILE'
X.\" $Header: stevie.mm,v 3.44 88/11/04 15:03:24 tony Exp $
X.\"
X.\" Documentation for STEVIE. Process with nroff using the mm macros.
X.\"
X.nr Hu 1
X.SA 1
X.TL
XSTEVIE - An Aspiring VI Clone
X.sp
XUser Reference - 3.44
X.AU "Tony Andrews"
X.AF ""
X.MT 4
X.PH "'STEVIE''User Reference'"
X.PF "''- \\\\nP -''"
X.H 1 "Overview"
XSTEVIE is an editor designed to mimic the interface of the UNIX
Xeditor 'vi'. The name (ST Editor for VI Enthusiasts) comes from the fact that
Xthe editor was first written for the Atari ST. The current version has
Xbeen ported to UNIX, Minix, MS-DOS, and OS/2, but I've left
Xthe name intact for now.
X.P
XThis program is the result of many late nights of hacking over the last
Xyear or so. The first version was written by Tim Thompson and posted
Xto USENET. From there, I reworked the data structures completely, added
XLOTS of features, and generally improved the overall performance in the
Xprocess.
X.P
XI've labelled STEVIE an 'aspiring' vi clone as a warning to those who
Xmay expect too much. On the whole, the editor is pretty complete. Almost
Xall of the visual mode commands are supported. I've tried very hard to
Xcapture the 'feel' of vi by getting the little things right. Making lines
Xwrap correctly, supporting true operators, and even getting the cursor to
Xland on the right place for tabs are all a real pain, but really help make
Xthe editor 'feel' right.
X.P
XSTEVIE may be freely distributed. The source isn't copyrighted or
Xrestricted in any way. If you pass the program along, please include all
Xthe documentation and, if practical, the source as well. I'm not fanatical
Xabout this, but I tried to make STEVIE fairly portable and that doesn't
Xdo any good if the source isn't available.
X.P
XThe remainder of this document describes the operation of the editor.
XThis is intended as a reference for users already familiar with the real
Xvi editor.
X.H 1 "Starting the Editor"
XThe following command line forms are supported:
X.VL 20
X.LI "stevie [file ...]"
XEdit the specified file(s)
X.LI "stevie -t tag"
XStart at the location of the given tag
X.LI "stevie + file"
XEdit file starting at end
X.LI "stevie +n file"
XEdit file starting a line number 'n'
X.LI "stevie +/pat file"
XEdit file starting at pattern 'pat'
X.LE
X.P
XIf multiple files are given on the command line (using the first form),
Xthe ":n" command goes to the next file, ":N" goes backward in the list,
Xand ":rew" can be used to rewind back to the start of the file list.
X.H 1 "Set Command Options"
XThe ":set" command works as usual to set parameters. Each parameter has
Xa long and an abbreviated name, either of which may be used. Boolean
Xparameters are set as in:
X.sp
X.ti +5
Xset showmatch
X.sp
Xor cleared by:
X.sp
X.ti +5
Xset noshowmatch
X.sp
XNumeric parameters are set as in:
X.sp
X.ti +5
Xset scroll=5
X.sp
XSeveral parameters may be set with a single command:
X.sp
X.ti +5
Xset novb sm report=1
X.P
XTo see the status of all parameters use ":set all". Typing ":set" with
Xno arguments will show only those parameters that have been changed.
XThe supported parameters, their names, abbreviations, defaults,
Xand descriptions are shown below:
X.SK
X.VL 12
X.LI autoindent
XShort: ai, Default: noai, Type: Boolean
X.br
XWhen in insert mode, start new lines at the same column as the prior
Xline. Unlike vi, you can backspace over the indentation.
X.LI backup
XShort: bk, Default: nobk, Type: Boolean
X.br
XLeave a backup on file writes.
X.LI errorbells
XShort: eb, Default: noeb, Type: Boolean
X.br
XRing bell when error messages are shown.
X.LI ignorecase
XShort: ic, Default: noic, Type: Boolean
X.br
XIgnore case in string searches.
X.LI lines
XShort: lines, Default: lines=25, Type: Numeric
X.br
XNumber of physical lines on the screen. The default value actually
Xdepends on the host machine, but is generally 25.
X.LI list
XShort: list, Default: nolist, Type: Boolean
X.br
XShow tabs and newlines graphically.
X.LI number
XShort: nu, Default: nonu, Type: Boolean
X.br
XDisplay lines on the screen with their line numbers.
X.LI report
XShort: report, Default: report=5, Type: Numeric
X.br
XMinimum number of lines to report operations on.
X.LI return
XShort: cr, Default: cr, Type: Boolean
X.br
XEnd lines with cr-lf when writing files.
X.LI scroll
XShort: scroll, Default: scroll=12, Type: Numeric
X.br
XNumber of lines to scroll for ^D & ^U.
X.LI showmatch
XShort: sm, Default: nosm, Type: Boolean
X.br
XWhen a ), }, or ] is typed, show the matching (, {, or [ if
Xit's on the current screen by moving the cursor there briefly.
X.LI showmode
XShort: mo, Default: nomo, Type: Boolean
X.br
XShow on status line when in insert mode.
X.LI tabstop
XShort: ts, Default: ts=8, Type: Numeric
X.br
XNumber of spaces in a tab.
X.LI wrapscan
XShort: ws, Default: ws, Type: Boolean
X.br
XString searches wrap around the ends of the file.
X.LI vbell
XShort: vb, Default: vb, Type: Boolean
X.br
XUse a visual bell, if possible. (novb for audible bell)
X.LE
X.P
XThe EXINIT environment variable can be used to modify the default values
Xon startup as in:
X.sp
X.ti +5
Xsetenv EXINIT="set sm ts=4"
X.P
XThe 'backup' parameter, if set, causes the editor to retain a backup of any
Xfiles that are written. During file writes, a backup is always kept for
Xsafety until the write is completed. At that point, the 'backup' parameter
Xdetermines whether the backup file is deleted.
X.P
XIn environments (e.g. OS/2 or TOS) where lines are normally terminated by
XCR-LF, the 'return' parameter allows files to be written with only a LF
Xterminator (if the parameter is cleared).
XThis parameter is ignored on UNIX systems.
X.P
XThe 'lines' parameter tells the editor how many lines there are on the screen.
XThis is useful on systems like the ST (or OS/2 machines with an EGA adapter)
Xwhere various screen resolutions may be
Xused. By using the 'lines' parameter, different screen sizes can be easily
Xhandled.
X.H 1 "Colon Commands"
XSeveral of the normal 'vi' colon commands are supported by STEVIE.
XSome commands may be preceded by a
Xline range specification.
XFor commands that accept a range of lines,
Xthe following address forms are supported:
X.DS 1
Xaddr
Xaddr + number
Xaddr - number
X.DE
Xwhere 'addr' may be one of the following:
X.DS 1
Xa line number
Xa mark (as in 'a or 'b)
X\'.' (the current line)
X\'$' (the last line)
X.DE
X.P
XAn address range of "%" is accepted as an abbreviation of "1,$".
X.H 2 "The Global Command"
XA limited form of the global command is supported, accepting the
Xfollowing command form:
X.DS 1
Xg/pattern/X
X.DE
Xwhere X may be either 'd' or 'p' to delete or print lines that match
Xthe given pattern.
XIf a line range is given, only those lines are checked for a match
Xwith the pattern.
XIf no range is given, all lines are checked.
X.P
XIf the trailing command character is omitted, 'p' is assumed.
XIn this case, the trailing slash is also optional.
XThe current version of the editor does not support the undo operation
Xfollowing the deletion of lines with the global command.
X.H 2 "The Substitute Command"
XThe substitute command provides a powerful mechanism for making more
Xcomplex substitutions than can be done directly from visual mode.
XThe general form of the command is:
X.DS 1
Xs/pattern/replacement/g
X.DE
XEach line in the given range (or the current line, if no range was
Xgiven) is scanned for the given regular expression.
XWhen found, the string that matched the pattern is replaced with
Xthe given replacement string.
XIf the replacement string is null, each matching pattern string is
Xdeleted.
X.P
XThe trailing 'g' is optional and, if present, indicates that multiple
Xoccurrences of 'pattern' on a line should all be replaced.
X.P
XSome special sequences are recognized in the replacement string. The
Xampersand character is replaced by the entire pattern that was matched.
XFor example, the following command could be used to put all occurrences
Xof 'foo' or 'bar' within double quotes:
X.DS 1
X1,$s/foo|bar/"&"/g
X.DE
X.P
XThe special sequence "\\n" where 'n' is a digit from 1 to 9, is replaced
Xby the string the matched the corresponding parenthesized expression in
Xthe pattern. The following command could be used to swap the first two
Xparameters in calls to the C function "foo":
X.DS 1
X1,$s/foo\\\\(([^,]*),([^,]*),/foo(\\\\2,\\\\1,/g
X.DE
X.P
XLike the global command, substitutions can't be undone with this
Xversion of the editor.
X.H 2 "File Manipulation Commands"
XThe following table shows the supported file manipulation commands as
Xwell as some other 'ex' commands that aren't described elsewhere:
X.DS
X:w		write the current file
X:wq		write and quit
X:x		write (if necessary) and quit
XZZ		same as ":x"
X
X:e file		edit the named file
X:e!		re-edit the current file, discarding changes
X:e #		edit the alternate file
X
X:w file		write the buffer to the named file
X:x,yw file	write lines x through y to the named file
X:r file		read the named file into the buffer
X
X:n		edit the next file
X:N		edit the previous file
X:rew		rewind the file list
X
X:f		show the current file name
X:f name		change the current file name
X:x=		show the line number of address 'x'
X
X:ta tag		go to the named tag
X^]		like ":ta" using the current word as the tag
X
X:help		display a command summary
X:ve		show the version number
X
X:sh		run an interactive shell
X:!cmd		run a command
X.DE
X.P
XThe ":help" command can also be invoked with the <HELP> key on the Atari
XST. This actually displays a pretty complete summary of the real vi with
Xunsupported features indicated appropriately.
X.P
XThe commands above work pretty much like they do in 'vi'. Most of the
Xcommands support a '!' suffix (if appropriate) to discard any pending
Xchanges.
X.H 1 "String Searches"
XString searches are supported, as in vi, accepting the usual regular
Xexpression syntax. This was done using a modified form of
XHenry Spencer's regular expression
Xlibrary. I added code outside the library to support
Xthe '\\<' and '\\>' extensions. This actually turned out to be pretty easy,
Xalthough there may be some glitches in the way I did it.
XThe parameter "ignorecase" can be set to ignore case in all string searches.
X.H 1 "Operators"
XThe vi operators (d, c, y, !, <, and >) work as true operators. The only
Xexception is that the change operator works only for character-oriented
Xchanges (like cw or c%) and not for line-oriented changes (like cL or c3j).
X.H 1 "Tags"
XTags are implemented and a fairly simple version of 'ctags' is supplied
Xwith the editor. The current version of ctags will find functions and
Xmacros following a specific (but common) form.  See 'ctags.doc' for a
Xcomplete discussion.
X.H 1 "System-Specific Comments"
XThe following sections provide additional relevant information for the
Xsystems to which STEVIE has been ported.
X.H 2 "Atari ST"
X.H 3 "TOS"
XThe editor has been tested in all three resolutions, although low and
Xhigh res. are less tested than medium. The 50-line high res. mode can
Xbe used by setting the 'lines' parameter to 50. Alternatively, the
Xenvironment variable 'LINES' can be set. The editor doesn't actively
Xset the number of lines on the screen. It just operates using the number
Xof lines it was told.
X.P
XThe arrow keys, as well as the <INSERT>, <HELP>, and <UNDO> keys are
Xall mapped appropriately.
X.H 3 "Minix"
XThe editor is pretty much the same under Minix, but many of the
Xkeyboard mappings aren't supported.
X.H 2 "UNIX"
XThe editor has been ported to UNIX System V release 3 as well as 4.2 BSD.
XThis was done
Xmainly to get some profiling data so I haven't put much effort into
Xdoing the UNIX version right. It's hard-coded for ansi-style escape
Xsequences and doesn't use the termcap/terminfo routines at all.
X.H 2 "OS/2"
XThis port was done because the editor that comes with the OS/2 developer's
Xkit really stinks. Make sure 'ansi' mode is on (using the 'ansi' command).
XThe OS/2 console driver doesn't support insert/delete line, so STEVIE
Xbypasses the driver and makes the appropriate system calls directly.
XThis is all done in the system-specific part of the editor so the kludge
Xis at least localized.
X.P
XThe arrow keys, page up/down and home/end all do what
Xyou'd expect. The function keys are hard-coded to some useful macros until
XI can get true support for macros into the editor. The current mappings
Xare:
X.DS 1
XF1	:p <RETURN>
XF2	:n <RETURN>
XF3	:e # <RETURN>
XF4	:rew <RETURN>
XF5	[[
XF6	]]
XF7	<<
XF8	>>
XF9	:x <RETURN>
XF10	:help <RETURN>
X
XS-F1	:p! <RETURN>
XS-F2	:n! <RETURN>
X.DE
X.H 2 "MSDOS"
XSTEVIE has been ported to MSDOS 3.3 using the Microsoft
XC compiler, version 5.1.
XThe keyboard mappings are the same as for OS/2.
XThe only problem with the PC version is that the inefficiency of
Xthe screen update code becomes painfully apparent on slower machines.
X.H 1 "Missing Features"
X.AL
X.LI
XCounts aren't yet supported everywhere that they should be.
X.LI
XMacros with support for function keys.
X.LI
XMore "set" options.
X.LI
XMany others...
X.LE
X.H 1 "Known Bugs and Problems"
X.AL
X.LI
XThe change operator is only half-way implemented. It works for character
Xmotions but not line motions. This isn't so bad since most change
Xoperations are character oriented anyway.
X.LI
XThe yank buffer uses statically allocated memory, so large yanks
Xwill fail. If a delete spans an area larger than the yank buffer,
Xthe program asks
Xfor confirmation before proceeding. That way, if you were moving text,
Xyou don't get screwed by the limited yank buffer. You just have to move
Xsmaller chunks at a time. All the internal buffers (yank, redo, etc.)
Xneed to be reworked to allocate memory dynamically. The 'undo' buffer
Xis now dynamically allocated, so any change can be undone.
X.LI
XIf you stay in insert mode for a long time, the insert buffer can overflow.
XThe editor will print a message and dump you back into command mode.
X.LI
XThe current version of the substitute command (i.e. ":s/foo/bar") can't
Xbe undone.
X.LI
XSeveral other less bothersome glitches...
X.LE
X.SK
X.H 1 "Conclusion"
XThe editor has reached a pretty stable state, and performs well on
Xthe systems I use it on, so I'm pretty much in maintenance mode now.
XThere's still plenty to be done; the screen update code is still pretty
Xinefficient and the yank/put code is still primitive.
XBut after more than a year of hacking, I'm ready to work on new projects.
XI'm still interested in bug reports, and I do still add a new feature
Xfrom time to time, but the rate of change is way down now.
X.P
XI'd like to thank Tim Thompson for writing the original version of the
Xeditor. His program was well structured and quite readable. Thanks for
Xgiving me a good base to work with.
X.P
XIf you're reading this file, but didn't get the source code for STEVIE,
Xit can be had by sending a disk with return postage to the address given
Xbelow. I can write disks for the Atari ST (SS or DS) or MSDOS (360K or
X1.2M). Please be sure to include the return postage. I don't intend to
Xmake money from this program, but I don't want to lose any either.
X.P
XI'm not planning to try to coordinate the various ports of STEVIE that
Xmay occur. I just don't have the time. But if you do port it, I'd be
Xinterested in hearing about it.
X.DS 1
XTony Andrews		UUCP: onecom!wldrdg!tony
X5902E Gunbarrel Ave.
XBoulder, CO 80301
X.DE
X.SK
X.HU "Character Function Summary"
XThe following list describes the meaning of each character that's used
Xby the editor. In some cases characters have meaning in both command and
Xinsert mode; these are all described.
X.SP 2
X.VL 8
X.LI ^@
XThe null character. Not used in any mode. This character may not
Xbe present in the file, as is the case with vi.
X.LI ^B
XBackward one screen.
X.LI ^D
XScroll the window down one half screen.
X.LI ^E
XScroll the screen up one line.
X.LI ^F
XForward one screen.
X.LI ^G
XSame as ":f" command. Displays file information.
X.LI ^H
X(Backspace) Moves cursor left one space in command mode.
XIn insert mode, erases the last character typed.
X.LI ^J
XMove the cursor down one line.
X.LI ^L
XClear and redraw the screen.
X.LI ^M
X(Carriage return) Move to the first non-white character
Xin the next line. In insert mode, a carriage return opens a new
Xline for input.
X.LI ^N
XMove the cursor down a line.
X.LI ^P
XMove the cursor up a line.
X.LI ^U
XScroll the window up one half screen.
X.LI ^Y
XScroll the screen down one line.
X.LI ^[
XEscape cancels a pending command in command mode, and is used to
Xterminate insert mode.
X.LI ^]
XMoves to the tag whose name is given by the word in which the cursor
Xresides.
X.LI ^`
XSame as ":e #" if supported (system-dependent).
X.LI SPACE
XMove the cursor right on column.
X.LI !
XThe filter operator always operates on a range of lines, passing the
Xlines as input to a program, and replacing them with the output of the
Xprogram. The shorthand command "!!" can be used to filter a number of
Xlines (specified by a preceding count). The command "!" is replaced
Xby the last command used, so "!!!<RETURN>" runs the given number of
Xlines through the last specified command.
X.LI $
XMove to the end of the current line.
X.LI %
XIf the cursor rests on a paren '()', brace '{}', or bracket '[]',
Xmove to the matching one.
X.LI \'
XUsed to move the cursor to a previously marked position, as
Xin 'a or 'b. The cursor moves to the start of the marked line. The
Xspecial mark '' refers to the "previous context".
X.LI +
XSame as carriage return, in command mode.
X.LI ,
XReverse of the last t, T, f, or F command.
X.LI -
XMove to the first non-white character in the previous line.
X.LI .
XRepeat the last edit command.
X.LI /
XStart of a forward string search command. String searches may be
Xoptionally terminated with a closing slash. To search for a slash
Xuse '\\/' in the search string.
X.LI 0
XMove to the start of the current line. Also used within counts.
X.LI 1-9
XUsed to add 'count' prefixes to commands.
X.LI :
XPrefix character for "ex" commands.
X.LI ;
XRepeat last t, T, f, or F command.
X.LI <
XThe 'left shift' operator.
X.LI >
XThe 'right shift' operator.
X.LI ?
XSame as '/', but search backward.
X.LI A
XAppend at the end of the current line.
X.LI B
XBackward one blank-delimited word.
X.LI C
XChange the rest of the current line.
X.LI D
XDelete the rest of the current line.
X.LI E
XEnd of the end of a blank-delimited word.
X.LI F
XFind a character backward on the current line.
X.LI G
XGo to the given line number (end of file, by default).
X.LI H
XMove to the first non-white char. on the top screen line.
X.LI I
XInsert before the first non-white char. on the current line.
X.LI J
XJoin two lines.
X.LI L
XMove to the first non-white char. on the bottom screen line.
X.LI M
XMove to the first non-white char. on the middle screen line.
X.LI N
XReverse the last string search.
X.LI O
XOpen a new line above the current line, and start inserting.
X.LI P
XPut the yank/delete buffer before the current cursor position.
X.LI R
XReplace characters until an "escape" character is received.
XSimilar to insert mode, but replaces instead of inserting.
XTyping a newline in replace mode is the same as in insert mode,
Xbut replacing continues on the new line.
X.LI T
XReverse search 'upto' the given character.
X.LI U
XRestore the current line to its state before you started changing it.
X.LI W
XMove forward one blank-delimited word.
X.LI X
XDelete one character before the cursor.
X.LI Y
XYank the current line. Same as 'yy'.
X.LI ZZ
XExit from the editor, saving changes if necessary.
X.LI [[
XMove backward one C function.
X.LI ]]
XMove forward one C function.
X.LI ^
XMove to the first non-white on the current line.
X.LI `
XMove to the given mark, as with '. The distinction between the two
Xcommands is important when used with operators. I support the
Xdifference correctly. If you don't know what I'm talking about,
Xdon't worry, it won't matter to you.
X.LI a
XAppend text after the cursor.
X.LI b
XBack one word.
X.LI c
XThe change operator.
X.LI d
XThe delete operator.
X.LI e
XMove to the end of a word.
X.LI f
XFind a character on the current line.
X.LI h
XMove left one column.
X.LI i
XInsert text before the cursor.
X.LI j
XMove down one line.
X.LI k
XMove up one line.
X.LI l
XMove right one column.
X.LI m
XSet a mark at the current position (e.g. ma or mb).
X.LI n
XRepeat the last string search.
X.LI o
XOpen a new line and start inserting text.
X.LI p
XPut the yank/delete buffer after the cursor.
X.LI r
XReplace a character.
X.LI s
XReplace characters.
X.LI t
XMove forward 'upto' the given character on the current line.
X.LI u
XUndo the last edit.
X.LI w
XMove forward one word.
X.LI x
XDelete the character under the cursor.
X.LI y
XThe yank operator.
X.LI z
XRedraw the screen with the current line at the top (zRETURN),
Xthe middle (z.), or the bottom (z-).
X.LI |
XMove to the column given by the preceding count.
X.LE
X.de TX
X.ce
XSTEVIE - User Guide
X.sp
X..
X.TC
END_OF_FILE
if test 20348 -ne `wc -c <'stevie.mm'`; then
    echo shar: \"'stevie.mm'\" unpacked with wrong size!
fi
# end of 'stevie.mm'
fi
echo shar: End of shell archive.
exit 0


Robert Regn\
rtregn@faui32.uucp\
rtregn@immd3.informatik.uni-erlangen.de

rtregn@immd3.informatik.uni-erlangen.de (Robert Regn) (12/15/88)

Files not used for Minix are renamed from *.c to *_c

Robert Regn\
rtregn@faui32.uucp\
rtregn@immd3.informatik.uni-erlangen.de

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  alloc.c ascii.h cmdline.c ctags.c dos_c edit.c env.h
#   fileio.c help.c hexchars.c keymap.h linefunc.c
# Wrapped by rtregn@faui32 on Wed Dec 14 17:04:57 1988
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'alloc.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'alloc.c'\"
else
echo shar: Extracting \"'alloc.c'\" \(4435 characters\)
sed "s/^X//" >'alloc.c' <<'END_OF_FILE'
Xstatic	char	RCSid[] =
X"$Header: alloc.c,v 1.2 88/08/26 08:44:18 tony Exp $";
X
X/*
X * Various allocation routines and routines returning information about
X * allocated objects.
X *
X * $Log:	alloc.c,v $
X * Revision 1.2  88/08/26  08:44:18  tony
X * Misc. changes to make lint happy.
X * 
X * Revision 1.1  88/03/20  21:06:06  tony
X * Initial revision
X * 
X *
X */
X
X#include "stevie.h"
X
Xchar *
Xalloc(size)
Xunsigned size;
X{
X	char *p;		/* pointer to new storage space */
X
X	p = malloc(size);
X	if ( p == (char *)NULL ) {	/* if there is no more room... */
X		emsg("alloc() is unable to find memory!");
X	}
X	return(p);
X}
X
Xchar *
Xstrsave(string)
Xchar *string;
X{
X	return(strcpy(alloc((unsigned)(strlen(string)+1)),string));
X}
X
Xvoid
Xscreenalloc()
X{
X	/*
X	 * If we're changing the size of the screen, free the old arrays
X	 */
X	if (Realscreen != NULL)
X		free(Realscreen);
X	if (Nextscreen != NULL)
X		free(Nextscreen);
X
X	Realscreen = malloc((unsigned)(Rows*Columns));
X	Nextscreen = malloc((unsigned)(Rows*Columns));
X}
X
X/*
X * Allocate and initialize a new line structure with room for
X * 'nchars' characters.
X */
XLINE *
Xnewline(nchars)
Xint	nchars;
X{
X	register LINE	*l;
X
X	if ((l = (LINE *) alloc(sizeof(LINE))) == NULL)
X		return (LINE *) NULL;
X
X	l->s = alloc((unsigned) nchars);	/* the line is empty */
X	l->s[0] = NUL;
X	l->size = nchars;
X
X	l->prev = (LINE *) NULL;	/* should be initialized by caller */
X	l->next = (LINE *) NULL;
X
X	return l;
X}
X
X/*
X * filealloc() - construct an initial empty file buffer
X */
Xvoid
Xfilealloc()
X{
X	if ((Filemem->linep = newline(1)) == NULL) {
X		fprintf(stderr,"Unable to allocate file memory!\n");
X		exit(1);
X	}
X	if ((Filetop->linep = newline(1)) == NULL) {
X		fprintf(stderr,"Unable to allocate file memory!\n");
X		exit(1);
X	}
X	if ((Fileend->linep = newline(1)) == NULL) {
X		fprintf(stderr,"Unable to allocate file memory!\n");
X		exit(1);
X	}
X	Filemem->index = 0;
X	Filetop->index = 0;
X	Fileend->index = 0;
X
X	Filetop->linep->next = Filemem->linep;	/* connect Filetop to Filemem */
X	Filemem->linep->prev = Filetop->linep;
X
X	Filemem->linep->next = Fileend->linep;	/* connect Filemem to Fileend */
X	Fileend->linep->prev = Filemem->linep;
X
X	*Curschar = *Filemem;
X	*Topchar  = *Filemem;
X
X	Filemem->linep->num = 0;
X	Fileend->linep->num = 0xffff;
X
X	clrall();		/* clear all marks */
X	u_clear();		/* clear the undo buffer */
X}
X
X/*
X * freeall() - free the current buffer
X *
X * Free all lines in the current buffer.
X */
Xvoid
Xfreeall()
X{
X	LINE	*lp, *xlp;
X
X	for (lp = Filetop->linep; lp != NULL ;lp = xlp) {
X		if (lp->s != NULL)
X			free(lp->s);
X		xlp = lp->next;
X		free(lp);
X	}
X
X	Curschar->linep = NULL;		/* clear pointers */
X	Filetop->linep = NULL;
X	Filemem->linep = NULL;
X	Fileend->linep = NULL;
X
X	u_clear();
X}
X
X/*
X * bufempty() - return TRUE if the buffer is empty
X */
Xbool_t
Xbufempty()
X{
X	return (buf1line() && Filemem->linep->s[0] == NUL);
X}
X
X/*
X * buf1line() - return TRUE if there is only one line
X */
Xbool_t
Xbuf1line()
X{
X	return (Filemem->linep->next == Fileend->linep);
X}
X
X/*
X * lineempty() - return TRUE if the current line is empty
X */
Xbool_t
Xlineempty()
X{
X	return (Curschar->linep->s[0] == NUL);
X}
X
X/*
X * endofline() - return TRUE if the given position is at end of line
X *
X * This routine will probably never be called with a position resting
X * on the NUL byte, but handle it correctly in case it happens.
X */
Xbool_t
Xendofline(p)
Xregister LPTR	*p;
X{
X	return (p->linep->s[p->index] == NUL || p->linep->s[p->index+1] == NUL);
X}
X/*
X * canincrease(n) - returns TRUE if the current line can be increased 'n' bytes
X *
X * This routine returns immediately if the requested space is available.
X * If not, it attempts to allocate the space and adjust the data structures
X * accordingly. If everything fails it returns FALSE.
X */
Xbool_t
Xcanincrease(n)
Xregister int	n;
X{
X	register int	nsize;
X	register char	*s;		/* pointer to new space */
X
X	nsize = strlen(Curschar->linep->s) + 1 + n;	/* size required */
X
X	if (nsize <= Curschar->linep->size)
X		return TRUE;
X
X	/*
X	 * Need to allocate more space for the string. Allow some extra
X	 * space on the assumption that we may need it soon. This avoids
X	 * excessive numbers of calls to malloc while entering new text.
X	 */
X	if ((s = alloc((unsigned) (nsize + SLOP))) == NULL) {
X		emsg("Can't add anything, file is too big!");
X		State = NORMAL;
X		return FALSE;
X	}
X
X	Curschar->linep->size = nsize + SLOP;
X	strcpy(s, Curschar->linep->s);
X	free(Curschar->linep->s);
X	Curschar->linep->s = s;
X	
X	return TRUE;
X}
END_OF_FILE
if test 4435 -ne `wc -c <'alloc.c'`; then
    echo shar: \"'alloc.c'\" unpacked with wrong size!
fi
# end of 'alloc.c'
fi
if test -f 'ascii.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'ascii.h'\"
else
echo shar: Extracting \"'ascii.h'\" \(350 characters\)
sed "s/^X//" >'ascii.h' <<'END_OF_FILE'
X/*
X * Definitions of various common control characters
X *
X * $Header: ascii.h,v 1.1 88/03/20 21:03:24 tony Exp $
X *
X * $Log:	ascii.h,v $
X * Revision 1.1  88/03/20  21:03:24  tony
X * Initial revision
X * 
X *
X */
X
X#define	NUL	'\0'
X#define	BS	'\010'
X#define	TAB	'\011'
X#define	NL	'\012'
X#define	CR	'\015'
X#define	ESC	'\033'
X
X#define	CTRL(x)	((x) & 0x1f)
END_OF_FILE
if test 350 -ne `wc -c <'ascii.h'`; then
    echo shar: \"'ascii.h'\" unpacked with wrong size!
fi
# end of 'ascii.h'
fi
if test -f 'cmdline.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'cmdline.c'\"
else
echo shar: Extracting \"'cmdline.c'\" \(14535 characters\)
sed "s/^X//" >'cmdline.c' <<'END_OF_FILE'
Xstatic	char	RCSid[] =
X"$Header: cmdline.c,v 1.9 88/11/10 08:58:04 tony Exp $";
X
X/*
X * Routines to parse and execute "command line" commands, such as searches
X * or colon commands.
X *
X * further   modifications by:  Robert Regn	   rtregn@faui32.uucp
X *
X * $Log:	cmdline.c,v $
X * Revision 1.9  88/11/10  08:58:04  tony
X * Added support for modelines when a new file edit is started.
X * 
X * Revision 1.8  88/11/01  21:31:56  tony
X * Added a call to flushbuf() in msg() to make sure messages get out
X * when they should.
X * 
X * Revision 1.7  88/10/27  08:21:38  tony
X * Removed support for Megamax. Added support for ":sh" and ":!", and
X * removed doshell() since it needs to be system-dependent. Changed
X * ":p" to ":N" to avoid name conflict with the "print" command. The
X * address spec "%" is translated to "1,$". The "=" command now works
X * to print the line number of the given address.
X * 
X * Revision 1.6  88/08/26  08:44:48  tony
X * Misc. changes to make lint happy.
X * 
X * Revision 1.5  88/06/26  14:48:20  tony
X * Added a call to doglob() to handle the new ":g" command.
X * 
X * Revision 1.4  88/06/25  21:43:19  tony
X * Fixed a problem in the processing of colon commands that caused
X * substitutions of patterns containing white space to fail.
X * 
X * Revision 1.3  88/04/24  21:35:09  tony
X * Added a new colon command "s" (substitute), which is performed by the
X * routine dosub() in search.c.
X * 
X * Revision 1.2  88/03/23  21:25:10  tony
X * Fixed a bug involving backspacing out of a colon or search command.
X * The fix is a hack until that part of the code can be cleaned up more.
X * 
X * Revision 1.1  88/03/20  21:06:19  tony
X * Initial revision
X * 
X *
X */
X
X#include "stevie.h"
X
Xstatic	char	*altfile = NULL;	/* alternate file */
Xstatic	int	altline;		/* line # in alternate file */
X
Xstatic	char	*nowrtmsg = "No write since last change (use ! to override)";
Xstatic	char	*nooutfile = "No output file";
Xstatic	char	*morefiles = "more files to edit";
X
Xextern	char	**files;		/* used for "n" and "rew" */
Xextern	int	numfiles, curfile;
X
X/*
X * The next two variables contain the bounds of any range given in a
X * command. If no range was given, both contain null line pointers.
X * If only a single line was given, u_pos will contain a null line
X * pointer.
X */
Xstatic	LPTR	l_pos, u_pos;
X
Xstatic	bool_t	interactive;	/* TRUE if we're reading a real command line */
X
X#define	CMDSZ	100		/* size of the command buffer */
X
Xstatic	bool_t	doecmd();
Xstatic	void	badcmd(), get_range();
Xstatic	LPTR	*get_line();
X
X/*
X * readcmdline() - accept a command line starting with ':', '/', or '?'
X *
X * readcmdline() accepts and processes colon commands and searches. If
X * 'cmdline' is null, the command line is read here. Otherwise, cmdline
X * points to a complete command line that should be used. This is used
X * in main() to handle initialization commands in the environment variable
X * "EXINIT".
X */
Xvoid
Xreadcmdline(firstc, cmdline)
Xint	firstc;		/* either ':', '/', or '?' */
Xchar	*cmdline;	/* optional command string */
X{
X	int c;
X	char buff[CMDSZ];
X	char cmdbuf[CMDSZ];
X	char argbuf[CMDSZ];
X	char *p, *q, *cmd, *arg;
X
X	/*
X	 * Clear the range variables.
X	 */
X	l_pos.linep = (struct line *) NULL;
X	u_pos.linep = (struct line *) NULL;
X
X	interactive = (cmdline == NULL);
X
X	if (interactive)
X		gotocmd(TRUE, firstc);
X	p = buff;
X	if ( firstc != ':' )
X		*p++ = firstc;
X
X	if (interactive) {
X		/* collect the command string, handling '\b' and @ */
X		for ( ; ; ) {
X			c = vgetc();
X			if ( c=='\n'||c=='\r'||c==EOF )
X				break;
X			if ( c=='\b' ) {
X				if ( p > buff + (firstc != ':') ) {
X					p--;
X					/* this is gross, but it relies
X					 * only on 'gotocmd'
X					 */
X					gotocmd(TRUE, firstc==':'?':':0);
X					for ( q=buff; q<p; q++ )
X						outchar(*q);
X				} else {
X					msg("");
X					return;		/* back to cmd mode */
X				}
X				continue;
X			}
X			if ( c=='@' ) {
X				p = buff;
X				gotocmd(TRUE, firstc);
X				continue;
X			}
X			outchar(c);
X			*p++ = c;
X		}
X		*p = '\0';
X	} else {
X		if (strlen(cmdline) > CMDSZ-2)	/* should really do something */
X			return;			/* better here... */
X		strcpy(p, cmdline);
X	}
X
X	/* skip any initial white space */
X	for ( cmd = buff; *cmd != NUL && isspace(*cmd); cmd++ )
X		;
X
X	/* search commands */
X	c = *cmd;
X	if ( c == '/' || c == '?' ) {
X		cmd++;
X		if ( *cmd == c || *cmd == NUL ) {
X			/*
X			 * The command was just '/' or '?' or possibly
X			 * '//' or '??'. Search in the requested direction
X			 * for the last search string used. The NULL
X			 * parameter to dosearch() tells it to do this.
X			 */
X			dosearch((c == '/') ? FORWARD : BACKWARD, NULL);
X			return;
X		}
X		/* If there is a matching '/' or '?' at the end, toss it */
X		p = strchr(cmd, NUL);
X		if ( *(p-1) == c && *(p-2) != '\\' )
X			*(p-1) = NUL;
X		dosearch((c == '/') ? FORWARD : BACKWARD, cmd);
X		return;
X	}
X
X	if (*cmd == '%') {		/* change '%' to "1,$" */
X		strcpy(cmdbuf, "1,$");	/* kind of gross... */
X		strcat(cmdbuf, cmd+1);
X		strcpy(cmd, cmdbuf);
X	}
X
X	while ( (p=strchr(cmd, '%')) != NULL && *(p-1) != '\\') {
X					/* change '%' to Filename */
X		if (Filename == NULL) {
X			emsg("No filename");
X			return;
X		}
X		*p= NUL;
X		strcpy (cmdbuf, cmd);
X		strcat (cmdbuf, Filename);
X		strcat (cmdbuf, p+1);
X		strcpy(cmd, cmdbuf);
X		msg(cmd);			/*repeat */
X	}
X
X	while ( (p=strchr(cmd, '#')) != NULL && *(p-1) != '\\') {
X					/* change '#' to Altname */
X		if (altfile == NULL) {
X			emsg("No alternate file");
X			return;
X		}
X		*p= NUL;
X		strcpy (cmdbuf, cmd);
X		strcat (cmdbuf, altfile);
X		strcat (cmdbuf, p+1);
X		strcpy(cmd, cmdbuf);
X		msg(cmd);			/*repeat */
X	}
X
X	/*
X	 * Parse a range, if present (and update the cmd pointer).
X	 */
X	get_range(&cmd);
X
X	if (l_pos.linep != NULL) {
X		if (LINEOF(&l_pos) > LINEOF(&u_pos)) {
X			emsg("Invalid range");
X			return;
X		}
X	}
X
X	strcpy(cmdbuf, cmd);	/* save the unmodified command */
X
X	/* isolate the command and find any argument */
X	for ( p=cmd; *p != NUL && ! isspace(*p); p++ )
X		;
X	if ( *p == NUL )
X		arg = NULL;
X	else {
X		*p = NUL;
X		for (p++; *p != NUL && isspace(*p) ;p++)
X			;
X		if (*p == NUL)
X			arg = NULL;
X		else {
X			strcpy(argbuf, p);
X			arg = argbuf;
X		}
X	}
X	if ( strcmp(cmd,"q!")==0 )
X		getout();
X	if ( strcmp(cmd,"q")==0 ) {
X		if ( Changed )
X			emsg(nowrtmsg);
X		else
X		if ( (curfile + 1) < numfiles )
X			emsg (morefiles);
X		else	getout();
X		return;
X	}
X	if ( strcmp(cmd,"w")==0 ) {
X		if ( arg == NULL ) {
X			if (Filename != NULL) {
X				writeit(Filename, &l_pos, &u_pos);
X			} else
X				emsg(nooutfile);
X		}
X		else
X			writeit(arg, &l_pos, &u_pos);
X		return;
X	}
X	if ( strcmp(cmd,"wq")==0 ) {
X		if (Filename != NULL) {
X			if (writeit(Filename, (LPTR *)NULL, (LPTR *)NULL))
X				getout();
X		} else
X			emsg(nooutfile);
X		return;
X	}
X	if ( strcmp(cmd, "x") == 0 ) {
X		doxit();
X		return;
X	}
X
X	if ( strcmp(cmd,"f")==0 && arg==NULL ) {
X		fileinfo();
X		return;
X	}
X	if ( *cmd == 'n' ) {
X		if ( (curfile + 1) < numfiles ) {
X			/*
X			 * stuff ":e[!] FILE\n"
X			 */
X			stuffin(":e");
X			if (cmd[1] == '!')
X				stuffin("!");
X			stuffin(" ");
X			stuffin(files[++curfile]);
X			stuffin("\n");
X		} else
X			emsg("No more files!");
X		return;
X	}
X	if ( *cmd == 'N' ) {
X		if ( curfile > 0 ) {
X			/*
X			 * stuff ":e[!] FILE\n"
X			 */
X			stuffin(":e");
X			if (cmd[1] == '!')
X				stuffin("!");
X			stuffin(" ");
X			stuffin(files[--curfile]);
X			stuffin("\n");
X		} else
X			emsg("No more files!");
X		return;
X	}
X	if ( strncmp(cmd, "rew", 3) == 0) {
X		if (numfiles <= 1)		/* nothing to rewind */
X			return;
X		curfile = 0;
X		/*
X		 * stuff ":e[!] FILE\n"
X		 */
X		stuffin(":e");
X		if (cmd[3] == '!')
X			stuffin("!");
X		stuffin(" ");
X		stuffin(files[0]);
X		stuffin("\n");
X		return;
X	}
X	if ( strcmp(cmd,"e") == 0 || strcmp(cmd,"e!") == 0 ) {
X		doecmd(arg, cmd[1] == '!');
X		return;
X	}
X	if ( strcmp(cmd,"f") == 0 ) {
X		Filename = strsave(arg);
X		filemess("");
X		return;
X	}
X	if ( strcmp(cmd,"r") == 0 ) {
X		if ( arg == NULL ) {
X			badcmd();
X			return;
X		}
X		if (readfile(arg, Curschar, 1)) {
X			emsg("Can't open file");
X			return;
X		}
X		updatescreen();
X		CHANGED;
X		return;
X	}
X	if ( strcmp(cmd,"=")==0 ) {
X		smsg("%d", cntllines(Filemem, &l_pos));
X		return;
X	}
X	if ( strncmp(cmd,"ta", 2) == 0 ) {
X		dotag(arg, cmd[2] == '!');
X		return;
X	}
X	if ( strncmp(cmd,"set", 2)==0 ) {
X		doset(arg, interactive);
X		return;
X	}
X	if ( strcmp(cmd,"help")==0 ) {
X		if (help()) {
X			screenclear();
X			updatescreen();
X		}
X		return;
X	}
X	if ( strncmp(cmd, "ve", 2) == 0) {
X		extern	char	*Version;
X
X		msg(Version);
X		return;
X	}
X	if ( strcmp(cmd, "sh") == 0) {
X		doshell(NULL);
X		return;
X	}
X	if ( *cmd == '!' ) {
X		doshell(cmdbuf+1);
X		return;
X	}
X	if ( strncmp(cmd, "s/", 2)==0 ) {
X		dosub(&l_pos, &u_pos, cmdbuf+1);
X		return;
X	}
X	if (strncmp(cmd, "g/", 2) == 0) {
X		doglob(&l_pos, &u_pos, cmdbuf+1);
X		return;
X	}
X	/*
X	 * If we got a line, but no command, then go to the line.
X	 */
X	if (*cmd == NUL && l_pos.linep != NULL) {
X		*Curschar = l_pos;
X		cursupdate();
X		return;
X	}
X
X	badcmd();
X}
X
X
Xdoxit()
X{
X	if (Changed) {
X		if (Filename != NULL) {
X			if (!writeit(Filename, (LPTR *)NULL, (LPTR *)NULL))
X				return;
X		} else {
X			emsg(nooutfile);
X			return;
X		}
X	}
X	if ( (curfile + 1) < numfiles )
X		emsg (morefiles);
X	else	getout();
X}
X
X/*
X * get_range - parse a range specifier
X *
X * Ranges are of the form:
X *
X * addr[,addr]
X *
X * where 'addr' is:
X *
X * $  [+- NUM]
X * 'x [+- NUM]	(where x denotes a currently defined mark)
X * .  [+- NUM]
X * NUM
X *
X * The pointer *cp is updated to point to the first character following
X * the range spec. If an initial address is found, but no second, the
X * upper bound is equal to the lower.
X */
Xstatic void
Xget_range(cp)
Xchar	**cp;
X{
X	LPTR	*l;
X	char	*p;
X
X	if ((l = get_line(cp)) == NULL)
X		return;
X
X	l_pos = *l;
X
X	for (p = *cp; *p != NUL && isspace(*p) ;p++)
X		;
X
X	*cp = p;
X
X	if (*p != ',') {		/* is there another line spec ? */
X		u_pos = l_pos;
X		return;
X	}
X
X	*cp = ++p;
X
X	if ((l = get_line(cp)) == NULL) {
X		u_pos = l_pos;
X		return;
X	}
X
X	u_pos = *l;
X}
X
Xstatic LPTR *
Xget_line(cp)
Xchar	**cp;
X{
X	static	LPTR	pos;
X	LPTR	*lp;
X	char	*p, c;
X	int	lnum;
X
X	pos.index = 0;		/* shouldn't matter... check back later */
X
X	p = *cp;
X	/*
X	 * Determine the basic form, if present.
X	 */
X	switch (c = *p++) {
X
X	case '$':
X		pos.linep = Fileend->linep->prev;
X		break;
X
X	case '.':
X		pos.linep = Curschar->linep;
X		break;
X
X	case '\'':
X		if ((lp = getmark(*p++)) == NULL) {
X			emsg("Unknown mark");
X			return (LPTR *) NULL;
X		}
X		pos = *lp;
X		break;
X
X	case '0': case '1': case '2': case '3': case '4':
X	case '5': case '6': case '7': case '8': case '9':
X		for (lnum = c - '0'; isdigit(*p) ;p++)
X			lnum = (lnum * 10) + (*p - '0');
X
X		pos = *gotoline(lnum);
X		break;
X
X	default:
X		return (LPTR *) NULL;
X	}
X
X	while (*p != NUL && isspace(*p))
X		p++;
X
X	if (*p == '-' || *p == '+') {
X		bool_t	neg = (*p++ == '-');
X
X		for (lnum = 0; isdigit(*p) ;p++)
X			lnum = (lnum * 10) + (*p - '0');
X
X		if (neg)
X			lnum = -lnum;
X
X		pos = *gotoline( cntllines(Filemem, &pos) + lnum );
X	}
X
X	*cp = p;
X	return &pos;
X}
X
Xstatic void
Xbadcmd()
X{
X	if (interactive)
X		emsg("Unrecognized command");
X}
X
X#define	LSIZE	512	/* max. size of a line in the tags file */
X
X/*
X * dotag(tag, force) - goto tag
X */
Xvoid
Xdotag(tag, force)
Xchar	*tag;
Xbool_t	force;
X{
X	FILE	*tp, *fopen();
X	char	lbuf[LSIZE];
X	char	*fname, *str;
X
X	if ((tp = fopen("tags", "r")) == NULL) {
X		emsg("Can't open tags file");
X		return;
X	}
X
X	while (fgets(lbuf, LSIZE, tp) != NULL) {
X	
X		if ((fname = strchr(lbuf, TAB)) == NULL) {
X			emsg("Format error in tags file");
X			return;
X		}
X		*fname++ = '\0';
X		if ((str = strchr(fname, TAB)) == NULL) {
X			emsg("Format error in tags file");
X			return;
X		}
X		*str++ = '\0';
X
X		if (strcmp(lbuf, tag) == 0) {
X			if (doecmd(fname, force)) {
X				stuffin(str);		/* str has \n at end */
X				stuffin("\007");	/* CTRL('G') */
X				fclose(tp);
X				return;
X			}
X		}
X	}
X	emsg("tag not found");
X	fclose(tp);
X}
X
Xstatic	bool_t
Xdoecmd(arg, force)
Xchar	*arg;
Xbool_t	force;
X{
X	int	line = 1;		/* line # to go to in new file */
X
X	if (!force && Changed) {
X		emsg(nowrtmsg);
X		if (altfile)
X			free(altfile);
X		altfile = strsave(arg);
X		return FALSE;
X	}
X	if ( arg != NULL ) {
X		/*
X		 * First detect a ":e" on the current file. This is mainly
X		 * for ":ta" commands where the destination is within the
X		 * current file.
X		 */
X		if (Filename != NULL && strcmp(arg, Filename) == 0) {
X			if (!Changed || (Changed && !force))
X				return TRUE;
X		}
X#ifdef OLDSUB
X		if (strcmp(arg, "#") == 0) {	/* alternate */
X			char	*s = Filename;
X
X			if (altfile == NULL) {
X				emsg("No alternate file");
X				return FALSE;
X			}
X			Filename = altfile;
X			altfile  = s;
X			line = altline;
X			altline = cntllines(Filemem, Curschar);
X		} else
X		  if (strchr(arg, '%')) {
X			char *s, *buf=alloc(strlen(arg)+strlen(Filename));
X			if (Filename == NULL) {
X				emsg("No filename");
X				return FALSE;
X			}
X			s = strchr(arg, '%');
X			*s = 0;
X			strcpy (buf, arg);
X			strcat (buf, Filename);
X			strcat (buf, s+1);
X			if (altfile)		/* I'm not shure if it is ok */
X				free(altfile);
X			altfile = Filename;
X			altline = cntllines(Filemem, Curschar);
X			Filename = buf;
X		} else {
X#endif
X			if (altfile)		/* I'm not shure if it is ok */
X				{
X				if (strcmp (arg, altfile) == 0)
X					line = altline;
X				free(altfile);
X				}
X			altfile = Filename;
X			altline = cntllines(Filemem, Curschar);
X			Filename = strsave(arg);
X#ifdef OLDSUB
X		}
X#endif
X	}
X	if (Filename == NULL) {
X		emsg("No filename");
X		return FALSE;
X	}
X
X	/* clear mem and read file */
X	freeall();
X	filealloc();
X	UNCHANGED;
X
X	if (readfile(Filename, Filemem, 0))
X		filemess("[New File]");
X	*Topchar = *Curschar;
X	if (line != 1) {
X		stuffnum(line);
X		stuffin("G");
X	}
X	do_mlines();
X	setpcmark();
X	updatescreen();
X	return TRUE;
X}
X
Xvoid
Xgotocmd(clr, firstc)
Xbool_t  clr;
Xchar	firstc;
X{
X	windgoto(Rows-1,0);
X	if ( clr )
X		outstr(T_EL);		/* clear the bottom line */
X	if ( firstc )
X		outchar(firstc);
X}
X
X/*
X * msg(s) - displays the string 's' on the status line
X */
Xvoid
Xmsg(s)
Xchar *s;
X{
X	gotocmd(TRUE, 0);
X	outstr(s);
X	flushbuf();
X}
X
X/*VARARGS1*/
Xvoid
Xsmsg(s, a1, a2, a3, a4, a5, a6, a7, a8, a9)
Xchar	*s;
Xint	a1, a2, a3, a4, a5, a6, a7, a8, a9;
X{
X	char	sbuf[80];
X
X	sprintf(sbuf, s, a1, a2, a3, a4, a5, a6, a7, a8, a9);
X	msg(sbuf);
X}
X
X/*
X * emsg() - display an error message
X *
X * Rings the bell, if appropriate, and calls message() to do the real work
X */
Xvoid
Xemsg(s)
Xchar	*s;
X{
X	if (P(P_EB))
X		beep();
X	msg(s);
X}
X
Xvoid
Xwait_return()
X{
X	char	c;
X
X	outstr("Press RETURN to continue");
X	do {
X		c = vgetc();
X	} while (c != '\r' && c != '\n' && c != ' ' && c != ':');
X
X	if ( c == ':') {				/* this can vi too  */
X		outstr("\n");
X		readcmdline(c, NULL);
X	}
X	else
X		screenclear();
X	updatescreen();
X}
END_OF_FILE
if test 14535 -ne `wc -c <'cmdline.c'`; then
    echo shar: \"'cmdline.c'\" unpacked with wrong size!
fi
# end of 'cmdline.c'
fi
if test -f 'ctags.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'ctags.c'\"
else
echo shar: Extracting \"'ctags.c'\" \(3077 characters\)
sed "s/^X//" >'ctags.c' <<'END_OF_FILE'
Xstatic	char	RCSid[] =
X"$Header: ctags.c,v 1.1 88/03/20 21:06:51 tony Exp $";
X
X/*
X * ctags - first cut at a UNIX ctags re-implementation
X *
X * $Log:	ctags.c,v $
X * Revision 1.1  88/03/20  21:06:51  tony
X * Initial revision
X * 
X *
X */
X
X/*
X * Caveats:
X *
X * Only simple function declarations are recognized, as in:
X *
X * type
X * fname(...)
X *
X * where "fname" is the name of the function and must come at the beginning
X * of a line. This is the form I always use, so the limitation doesn't
X * bother me.
X *
X * Macros (with or without parameters) of the following form are also detected:
X *
X * "#" [white space] "define" [white space] NAME
X *
X * No sorting or detection of duplicate functions is done.
X *
X * If there are no arguments, a list of filenames to be searched is read
X * from the standard input. Otherwise, all arguments are assumed to be
X * file names.
X *
X * Tony Andrews
X * August 1987
X */
X
X#include <stdio.h>
X#include <ctype.h>
X#include <string.h>
X
X#if 0
X#define	index	strchr
X#endif
X
Xint	ac;
Xchar	**av;
X
Xmain(argc, argv)
Xint	argc;
Xchar	*argv[];
X{
X	char	*fname, *nextfile();
X	FILE	*tp, *fopen();
X
X	ac = argc;
X	av = argv;
X
X	if ((tp = fopen("tags", "w")) == NULL) {
X		fprintf(stderr, "Can't create tags file\n");
X		exit(1);
X	}
X
X	while ((fname = nextfile()) != NULL)
X		dofile(fname, tp);
X
X	fclose(tp);
X	exit(0);
X}
X
Xchar *
Xnextfile()	/* returns ptr to next file to be searched, null at end */
X{
X	static	char	buf[128];
X	static	int	ap = 1;
X	char	*gets();
X
X	if (ac <= 1) {		/* read from stdin */
X		if (feof(stdin))
X			return (char *) NULL;
X		return (gets(buf));
X	} else {
X		if (ap < ac)
X			return av[ap++];
X		else
X			return (char *) NULL;
X	}
X}
X
X#define	LSIZE	512	/* max. size of a line */
X
X#define	BEGID(c)	(isalpha(c) || (c) == '_')
X#define	MIDID(c)	(isalpha(c) || isdigit(c) || (c) == '_')
X
Xdofile(fn, tp)
Xchar	*fn;
XFILE	*tp;
X{
X	FILE	*fp, *fopen();
X	char	*cp, *index();
X	char	lbuf[LSIZE];
X	char	func[LSIZE];
X	int	i, j;
X
X	if ((fp = fopen(fn, "r")) == NULL) {
X		fprintf(stderr, "Can't open file '%s' - skipping\n", fn);
X		return;
X	}
X
X	while (fgets(lbuf, LSIZE, fp) != NULL) {
X
X		if (BEGID(lbuf[0])) {		/* could be a function */
X			for (i=0; MIDID(lbuf[i]) ;i++)	/* grab the name */
X				func[i] = lbuf[i];
X	
X			func[i] = '\0';		/* null terminate the name */
X	
X			/*
X			 * We've skipped to the end of what may be a function
X			 * name. Check to see if the next char is a paren,
X			 * and make sure the closing paren is here too.
X			 */
X			if (lbuf[i]=='(' && (((cp = index(lbuf, ')'))!=NULL))) {
X				*++cp = '\0';
X				fprintf(tp, "%s\t%s\t/^%s\\(/\n", func,fn,func);
X			}
X
X		} else if (lbuf[0] == '#') {	/* could be a define */
X			for (i=1; isspace(lbuf[i]) ;i++)
X				;
X			if (strncmp(&lbuf[i], "define", 6) != 0)
X				continue;
X
X			i += 6;			/* skip "define" */
X
X			for (; isspace(lbuf[i]) ;i++)
X				;
X
X			if (!BEGID(lbuf[i]))
X				continue;
X
X			for (j=0; MIDID(lbuf[i]) ;i++, j++)
X				func[j] = lbuf[i];
X
X			func[j] = '\0';		/* null terminate the name */
X			lbuf[i] = '\0';		/* null terminate the line */
X			fprintf(tp, "%s\t%s\t/^%s/\n", func, fn, lbuf);
X		}
X	}
X	fclose(fp);
X}
END_OF_FILE
if test 3077 -ne `wc -c <'ctags.c'`; then
    echo shar: \"'ctags.c'\" unpacked with wrong size!
fi
# end of 'ctags.c'
fi
if test -f 'dos_c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'dos_c'\"
else
echo shar: Extracting \"'dos_c'\" \(5670 characters\)
sed "s/^X//" >'dos_c' <<'END_OF_FILE'
Xstatic	char	RCSid[] =
X"$Header: dos.c,v 1.4 88/10/28 14:40:15 tony Exp $";
X
X/*
X * DOS System-dependent routines.
X *
X * System-specific code for MS-DOS. This has been tested with
X * MSDOS 3.3 on an AT. Also, the console driver "nansi.sys" is
X * required.
X *
X * $Log:	dos.c,v $
X * Revision 1.4  88/10/28  14:40:15  tony
X * Added doshell() to support ":sh" and ":!".
X * 
X * Revision 1.3  88/10/06  10:13:30  tony
X * Added fixname() routine which trims the base and extension parts of
X * a file name to 8 and 3 characters, and returns a pointer to the
X * resulting string. This makes it easier to deal with UNIX-style names
X * on stupid systems.
X * 
X * Revision 1.2  88/08/26  13:39:42  tony
X * Added routines to get and set the switch character, and modified the
X * init and terminate code to set the switch character to '/' during
X * execution. This seems to be needed to make the system() routine happy.
X * 
X * Revision 1.1  88/05/03  14:22:16  tony
X * Initial revision
X * 
X *
X */
X
X#include "stevie.h"
X#include <stdio.h>
X#include <dos.h>
X
Xstatic	char	getswitch();
Xstatic	void	setswitch();
X
X/*
X * inchar() - get a character from the keyboard
X */
Xint
Xinchar()
X{
X	int	c;
X
X	for (;;beep()) {	/* loop until we get a valid character */
X
X		flushbuf();	/* flush any pending output */
X
X		switch (c = getch()) {
X		case 0x1e:
X			return K_CGRAVE;
X		case 0:				/* special key */
X			if (State != NORMAL) {
X				c = getch();	/* throw away next char */
X				continue;	/* and loop for another char */
X			}
X			switch (c = getch()) {
X			case 0x50:
X				return K_DARROW;
X			case 0x48:
X				return K_UARROW;
X			case 0x4b:
X				return K_LARROW;
X			case 0x4d:
X				return K_RARROW;
X			case 0x52:
X				return K_INSERT;
X			case 0x47:
X				stuffin("1G");
X				return -1;
X			case 0x4f:
X				stuffin("G");
X				return -1;
X			case 0x51:
X				stuffin(mkstr(CTRL('F')));
X				return -1;
X			case 0x49:
X				stuffin(mkstr(CTRL('B')));
X				return -1;
X			/*
X			 * Hard-code some useful function key macros.
X			 */
X			case 0x3b: /* F1 */
X				stuffin(":p\n");
X				return -1;
X			case 0x54: /* SF1 */
X				stuffin(":p!\n");
X				return -1;
X			case 0x3c: /* F2 */
X				stuffin(":n\n");
X				return -1;
X			case 0x55: /* SF2 */
X				stuffin(":n!\n");
X				return -1;
X			case 0x3d: /* F3 */
X				stuffin(":e #\n");
X				return -1;
X			case 0x3e: /* F4 */
X				stuffin(":rew\n");
X				return -1;
X			case 0x57: /* SF4 */
X				stuffin(":rew!\n");
X				return -1;
X			case 0x3f: /* F5 */
X				stuffin("[[");
X				return -1;
X			case 0x40: /* F6 */
X				stuffin("]]");
X				return -1;
X			case 0x41: /* F7 */
X				stuffin("<<");
X				return -1;
X			case 0x42: /* F8 */
X				stuffin(">>");
X				return -1;
X			case 0x43: /* F9 */
X				stuffin(":x\n");
X				return -1;
X			case 0x44: /* F10 */
X				stuffin(":help\n");
X				return -1;
X			default:
X				break;
X			}
X			break;
X
X		default:
X			return c;
X		}
X	}
X}
X
X#define	BSIZE	2048
Xstatic	char	outbuf[BSIZE];
Xstatic	int	bpos = 0;
X
Xvoid
Xflushbuf()
X{
X	if (bpos != 0)
X		write(1, outbuf, bpos);
X	bpos = 0;
X}
X
X/*
X * Macro to output a character. Used within this file for speed.
X */
X#define	outone(c)	outbuf[bpos++] = c; if (bpos >= BSIZE) flushbuf()
X
X/*
X * Function version for use outside this file.
X */
Xvoid
Xoutchar(c)
Xregister char	c;
X{
X	outbuf[bpos++] = c;
X	if (bpos >= BSIZE)
X		flushbuf();
X}
X
X/*
X * outstr(s) - write a string to the console
X */
Xvoid
Xoutstr(s)
Xregister char	*s;
X{
X	while (*s) {
X		outone(*s++);
X	}
X}
X
Xvoid
Xbeep()
X{
X	outone('\007');
X}
X
Xsleep(n)
Xint	n;
X{
X	/*
X	 * Should do something reasonable here.
X	 */
X}
X
Xvoid
Xdelay()
X{
X	long	l;
X
X	flushbuf();
X	/*
X	 * Should do something better here...
X	 */
X	for (l=0; l < 5000 ;l++)
X		;
X}
X
Xstatic	char	schar;		/* save original switch character */
X
Xvoid
Xwindinit()
X{
X	Columns = 80;
X	P(P_LI) = Rows = 25;
X	schar = getswitch();
X	setswitch('/');
X}
X
Xvoid
Xwindexit(r)
Xint r;
X{
X	flushbuf();
X	setswitch(schar);
X	exit(r);
X}
X
Xvoid
Xwindgoto(r, c)
Xregister int	r, c;
X{
X	r += 1;
X	c += 1;
X
X	/*
X	 * Check for overflow once, to save time.
X	 */
X	if (bpos + 8 >= BSIZE)
X		flushbuf();
X
X	outbuf[bpos++] = '\033';
X	outbuf[bpos++] = '[';
X	if (r >= 10)
X		outbuf[bpos++] = r/10 + '0';
X	outbuf[bpos++] = r%10 + '0';
X	outbuf[bpos++] = ';';
X	if (c >= 10)
X		outbuf[bpos++] = c/10 + '0';
X	outbuf[bpos++] = c%10 + '0';
X	outbuf[bpos++] = 'H';
X}
X
XFILE *
Xfopenb(fname, mode)
Xchar	*fname;
Xchar	*mode;
X{
X	FILE	*fopen();
X	char	modestr[16];
X
X	sprintf(modestr, "%sb", mode);
X	return fopen(fname, modestr);
X}
X
Xstatic	char
Xgetswitch()
X{
X	union	REGS	inregs, outregs;
X
X	inregs.h.ah = 0x37;
X	inregs.h.al = 0;
X
X	intdos(&inregs, &outregs);
X
X	return outregs.h.dl;
X}
X
Xstatic	void
Xsetswitch(c)
Xchar	c;
X{
X	union	REGS	inregs, outregs;
X
X	inregs.h.ah = 0x37;
X	inregs.h.al = 1;
X	inregs.h.dl = c;
X
X	intdos(&inregs, &outregs);
X}
X
X#define	PSIZE	128
X
X/*
X * fixname(s) - fix up a dos name
X *
X * Takes a name like:
X *
X *	\x\y\z\base.ext
X *
X * and trims 'base' to 8 characters, and 'ext' to 3.
X */
Xchar *
Xfixname(s)
Xchar	*s;
X{
X	char	*strchr(), *strrchr();
X	static	char	f[PSIZE];
X	char	base[32];
X	char	ext[32];
X	char	*p;
X	int	i;
X
X	strcpy(f, s);
X
X	for (i=0; i < PSIZE ;i++)
X		if (f[i] == '/')
X			f[i] = '\\';
X
X	/*
X	 * Split the name into directory, base, extension.
X	 */
X	if ((p = strrchr(f, '\\')) != NULL) {
X		strcpy(base, p+1);
X		p[1] = '\0';
X	} else {
X		strcpy(base, f);
X		f[0] = '\0';
X	}
X
X	if ((p = strchr(base, '.')) != NULL) {
X		strcpy(ext, p+1);
X		*p = '\0';
X	} else
X		ext[0] = '\0';
X
X	/*
X	 * Trim the base name if necessary.
X	 */
X	if (strlen(base) > 8)
X		base[8] = '\0';
X	
X	if (strlen(ext) > 3)
X		ext[3] = '\0';
X
X	/*
X	 * Paste it all back together
X	 */
X	strcat(f, base);
X	strcat(f, ".");
X	strcat(f, ext);
X
X	return f;
X}
X
Xvoid
Xdoshell(cmd)
Xchar	*cmd;
X{
X	if (cmd == NULL)
X		cmd = "command.com";
X
X	system(cmd);
X	wait_return();
X}
END_OF_FILE
if test 5670 -ne `wc -c <'dos_c'`; then
    echo shar: \"'dos_c'\" unpacked with wrong size!
fi
# end of 'dos_c'
fi
if test -f 'edit.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'edit.c'\"
else
echo shar: Extracting \"'edit.c'\" \(7989 characters\)
sed "s/^X//" >'edit.c' <<'END_OF_FILE'
Xstatic	char	RCSid[] =
X"$Header: edit.c,v 1.7 88/11/10 13:29:15 tony Exp $";
X
X/*
X * The main edit loop as well as some other simple cursor movement routines.
X *
X * $Log:	edit.c,v $
X * Revision 1.7  88/11/10  13:29:15  tony
X * Added a call to do_mlines() to check for mode lines at initialization.
X * 
X * Revision 1.6  88/10/06  10:11:50  tony
X * Fixed a bug in scrolldown() involving the special case of the cursor
X * being on the last line, and the line being scrolled onto the screen
X * being long. The special case code needs to deal with physical lines,
X * not logical.
X * 
X * Revision 1.5  88/08/30  20:35:53  tony
X * After much prodding from Mark, I finally added support for replace mode.
X * 
X * Revision 1.4  88/08/26  08:44:55  tony
X * Misc. changes to make lint happy.
X * 
X * Revision 1.3  88/05/01  20:08:47  tony
X * Fixed some problems with the new auto-indent feature.
X * 
X * Revision 1.2  88/04/30  19:59:43  tony
X * Added support for auto-indent option.
X * 
X * Revision 1.1  88/03/20  21:07:10  tony
X * Initial revision
X * 
X *
X */
X
X#include "stevie.h"
X
X/*
X * This flag is used to make auto-indent work right on lines where only
X * a <RETURN> or <ESC> is typed. It is set when an auto-indent is done,
X * and reset when any other editting is done on the line. If an <ESC>
X * or <RETURN> is received, and did_ai is TRUE, the line is truncated.
X */
Xbool_t	did_ai = FALSE;
X
Xvoid
Xedit()
X{
X	int c;
X	char *p, *q;
X
X	Prenum = 0;
X
X	/* position the display and the cursor at the top of the file. */
X	*Topchar = *Filemem;
X	*Curschar = *Filemem;
X	Cursrow = Curscol = 0;
X
X	do_mlines();		/* check for mode lines before starting */
X
X	for ( ;; ) {
X
X	/* Figure out where the cursor is based on Curschar. */
X	cursupdate();
X
X	windgoto(Cursrow,Curscol);
X
X	c = vgetc();
X
X	if (State == NORMAL) {
X
X		/* We're in the normal (non-insert) mode. */
X
X		/* Pick up any leading digits and compute 'Prenum' */
X		if ( (Prenum>0 && isdigit(c)) || (isdigit(c) && c!='0') ){
X			Prenum = Prenum*10 + (c-'0');
X			continue;
X		}
X		/* execute the command */
X		normal(c);
X		Prenum = 0;
X
X	} else {
X
X		/*
X		 * Insert or Replace mode.
X		 */
X		switch (c) {
X
X		case ESC:	/* an escape ends input mode */
X
X			/*
X			 * If we just did an auto-indent, truncate the
X			 * line, and put the cursor back.
X			 */
X			if (did_ai) {
X				Curschar->linep->s[0] = NUL;
X				Curschar->index = 0;
X				did_ai = FALSE;
X			}
X
X			set_want_col = TRUE;
X
X			/* Don't end up on a '\n' if you can help it. */
X			if (gchar(Curschar) == NUL && Curschar->index != 0)
X				dec(Curschar);
X
X			/*
X			 * The cursor should end up on the last inserted
X			 * character. This is an attempt to match the real
X			 * 'vi', but it may not be quite right yet.
X			 */
X			if (Curschar->index != 0 && !endofline(Curschar))
X				dec(Curschar);
X
X			State = NORMAL;
X			msg("");
X
X			/* construct the Redo buffer */
X			p=Redobuff;
X			q=Insbuff;
X			while ( q < Insptr )
X				*p++ = *q++;
X			*p++ = ESC;
X			*p = NUL;
X			updatescreen();
X			break;
X
X		case CTRL('D'):
X			/*
X			 * Control-D is treated as a backspace in insert
X			 * mode to make auto-indent easier. This isn't
X			 * completely compatible with vi, but it's a lot
X			 * easier than doing it exactly right, and the
X			 * difference isn't very noticeable.
X			 */
X		case BS:
X			/* can't backup past starting point */
X			if (Curschar->linep == Insstart->linep &&
X			    Curschar->index <= Insstart->index) {
X				beep();
X				break;
X			}
X
X			/* can't backup to a previous line */
X			if (Curschar->linep != Insstart->linep &&
X			    Curschar->index <= 0) {
X				beep();
X				break;
X			}
X
X			did_ai = FALSE;
X			dec(Curschar);
X			if (State == INSERT)
X				delchar(TRUE);
X			/*
X			 * It's a little strange to put backspaces into
X			 * the redo buffer, but it makes auto-indent a
X			 * lot easier to deal with.
X			 */
X			*Insptr++ = BS;
X			Ninsert++;
X			cursupdate();
X			updateline();
X			break;
X
X		case CR:
X		case NL:
X			*Insptr++ = NL;
X			Ninsert++;
X			opencmd(FORWARD, TRUE);		/* open a new line */
X			break;
X
X		default:
X			did_ai = FALSE;
X			insertchar(c);
X			break;
X		}
X	}
X	}
X}
X
X/*
X * Special characters in this context are those that need processing other
X * than the simple insertion that can be performed here. This includes ESC
X * which terminates the insert, and CR/NL which need special processing to
X * open up a new line. This routine tries to optimize insertions performed
X * by the "redo" command, so it needs to know when it should stop and defer
X * processing to the "normal" mechanism.
X */
X#define	ISSPECIAL(c)	((c) == NL || (c) == CR || (c) == ESC)
X
Xvoid
Xinsertchar(c)
Xint c;
X{
X#if 0
X	char *p;
X
X	if ( ! anyinput() ) {
X#endif
X		inschar(c);
X		*Insptr++ = c;
X		Ninsert++;
X		/*
X		 * The following kludge avoids overflowing the statically
X		 * allocated insert buffer. Just dump the user back into
X		 * command mode, and print a message.
X		 */
X		if (Insptr+10 >= &Insbuff[1024]) {
X			stuffin(mkstr(ESC));
X			emsg("No buffer space - returning to command mode");
X			sleep(2);
X		}
X#if 0
X	}
X	else {
X		/* If there's any pending input, grab it all at once. */
X		p = Insptr;
X		*Insptr++ = c;
X		Ninsert++;
X		for (c = vpeekc(); !ISSPECIAL(c) ;c = vpeekc()) {
X			c = vgetc();
X			*Insptr++ = c;
X			Ninsert++;
X		}
X		*Insptr = '\0';
X		insstr(p);
X	}
X#endif
X	updateline();
X}
X
Xvoid
Xgetout()
X{
X	windgoto(Rows-1,0);
X	putchar('\r');
X	putchar('\n');
X	windexit(0);
X}
X
Xvoid
Xscrolldown(nlines)
Xint nlines;
X{
X	register LPTR	*p;
X	register int	done = 0;	/* total # of physical lines done */
X
X	/* Scroll up 'nlines' lines. */
X	while (nlines--) {
X		if ((p = prevline(Topchar)) == NULL)
X			break;
X		done += plines(p);
X		*Topchar = *p;
X		/*
X		 * If the cursor is on the bottom line, we need to
X		 * make sure it gets moved up the appropriate number
X		 * of lines so it stays on the screen.
X		 */
X		if (Curschar->linep == Botchar->linep->prev) {
X			int	i = 0;
X			while (i < done) {
X				i += plines(Curschar);
X				*Curschar = *prevline(Curschar);
X			}
X		}
X	}
X	s_ins(0, done);
X}
X
Xvoid
Xscrollup(nlines)
Xint nlines;
X{
X	register LPTR	*p;
X	register int	done = 0;	/* total # of physical lines done */
X	register int	pl;		/* # of plines for the current line */
X
X	/* Scroll down 'nlines' lines. */
X	while (nlines--) {
X		pl = plines(Topchar);
X		if ((p = nextline(Topchar)) == NULL)
X			break;
X		done += pl;
X		if (Curschar->linep == Topchar->linep)
X			*Curschar = *p;
X		*Topchar = *p;
X
X	}
X	s_del(0, done);
X}
X
X/*
X * oneright
X * oneleft
X * onedown
X * oneup
X *
X * Move one char {right,left,down,up}.  Return TRUE when
X * sucessful, FALSE when we hit a boundary (of a line, or the file).
X */
X
Xbool_t
Xoneright()
X{
X	set_want_col = TRUE;
X
X	switch (inc(Curschar)) {
X
X	case 0:
X		return TRUE;
X
X	case 1:
X		dec(Curschar);		/* crossed a line, so back up */
X		/* fall through */
X	case -1:
X		return FALSE;
X	}
X	/*NOTREACHED*/
X}
X
Xbool_t
Xoneleft()
X{
X	set_want_col = TRUE;
X
X	switch (dec(Curschar)) {
X
X	case 0:
X		return TRUE;
X
X	case 1:
X		inc(Curschar);		/* crossed a line, so back up */
X		/* fall through */
X	case -1:
X		return FALSE;
X	}
X	/*NOTREACHED*/
X}
X
Xvoid
Xbeginline(flag)
Xbool_t	flag;
X{
X	while ( oneleft() )
X		;
X	if (flag) {
X		while (isspace(gchar(Curschar)) && oneright())
X			;
X	}
X	set_want_col = TRUE;
X}
X
Xbool_t
Xoneup(n)
X{
X	LPTR p, *np;
X	int k;
X
X	p = *Curschar;
X	for ( k=0; k<n; k++ ) {
X		/* Look for the previous line */
X		if ( (np=prevline(&p)) == NULL ) {
X			/* If we've at least backed up a little .. */
X			if ( k > 0 )
X				break;	/* to update the cursor, etc. */
X			else
X				return FALSE;
X		}
X		p = *np;
X	}
X	*Curschar = p;
X	/* This makes sure Topchar gets updated so the complete line */
X	/* is one the screen. */
X	cursupdate();
X	/* try to advance to the column we want to be at */
X	*Curschar = *coladvance(&p, Curswant);
X	return TRUE;
X}
X
Xbool_t
Xonedown(n)
X{
X	LPTR p, *np;
X	int k;
X
X	p = *Curschar;
X	for ( k=0; k<n; k++ ) {
X		/* Look for the next line */
X		if ( (np=nextline(&p)) == NULL ) {
X			if ( k > 0 )
X				break;
X			else
X				return FALSE;
X		}
X		p = *np;
X	}
X	/* try to advance to the column we want to be at */
X	*Curschar = *coladvance(&p, Curswant);
X	return TRUE;
X}
END_OF_FILE
if test 7989 -ne `wc -c <'edit.c'`; then
    echo shar: \"'edit.c'\" unpacked with wrong size!
fi
# end of 'edit.c'
fi
if test -f 'env.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'env.h'\"
else
echo shar: Extracting \"'env.h'\" \(1686 characters\)
sed "s/^X//" >'env.h' <<'END_OF_FILE'
X/*
X * The defines in this file establish the environment we're compiling
X * in. Set these appropriately before compiling the editor.
X */
X
X/*
X * One (and only 1) of the following defines should be uncommented.
X * Most of the code is pretty machine-independent. Machine dependent
X * code goes in a file like tos.c or unix.c. The only other place
X * where machine dependent code goes is term.h for escape sequences.
X */
X
X/* #define	ATARI			/* For the Atari ST */
X/* #define	UNIX			/* System V or BSD */
X/* #define	OS2			/* Microsoft OS/2 1.1 */
X/* #define	DOS			/* MSDOS 3.3 (on AT) */
X
X/*
X * If UNIX is defined above, then BSD may be defined.
X */
X#ifdef	UNIX
X/* #define	BSD			/* Berkeley UNIX */
X#endif
X
X/*
X * If ATARI is defined, MINIX may be defined. Otherwise, the editor
X * is set up to compile using the Sozobon C compiler under TOS.
X */
X#ifdef	ATARI
X#define	MINIX			/* Minix for the Atari ST */
X#endif
X
X/*
X * If HELP is defined, the :help command shows a vi command summary.
X * Help costs on the PC minix about 900 Bytes text and 5000 Bytes data !!
X */
X/* #define	HELP			/* enable help command */
X
X/*
X * Use of termcap is optional in some environments. If available, it
X * is enabled by the following define. Check the appropriate system-
X * dependent source file to see if termcap is supported.
X */
X/* #define	TERMCAP			/* enable termcap support */
X
X/*
X * The yank buffer is still static, but its size can be specified
X * here to override the default of 4K.
X */
X/* #define	YBSIZE	8192		/* yank buffer size */
X
X/*
X * STRCSPN should be defined if the target system doesn't have the
X * routine strcspn() available. See regexp.c for details.
X */
X
X
X#ifdef	MINIX
X#define	STRCSPN
X#endif
X
END_OF_FILE
if test 1686 -ne `wc -c <'env.h'`; then
    echo shar: \"'env.h'\" unpacked with wrong size!
fi
# end of 'env.h'
fi
if test -f 'fileio.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'fileio.c'\"
else
echo shar: Extracting \"'fileio.c'\" \(7044 characters\)
sed "s/^X//" >'fileio.c' <<'END_OF_FILE'
Xstatic	char	RCSid[] =
X"$Header: fileio.c,v 1.7 88/10/27 08:15:27 tony Exp $";
X
X/*
X * Basic file I/O routines.
X *
X * further   modifications by:  Robert Regn	   rtregn@faui32.uucp
X *
X * $Log:	fileio.c,v $
X * Revision 1.7  88/10/27  08:15:27  tony
X * Removed support for Megamax, and added a call to flushbuf() for
X * versions that buffer output.
X * 
X * Revision 1.6  88/10/06  10:10:45  tony
X * File names passed to fopen() are now processed by fixname() to do
X * system-dependent hacks on the name (mainly for DOS & OS/2).
X * 
X * Revision 1.5  88/08/26  08:45:00  tony
X * Misc. changes to make lint happy.
X * 
X * Revision 1.4  88/06/10  13:43:19  tony
X * Fixed a bug involving writing out files with long pathnames. A small
X * fixed size buffer was being used. The space for the backup file name
X * is now allocated dynamically.
X * 
X * Revision 1.3  88/05/02  21:37:12  tony
X * Reworked the readfile() routine to better deal with null characters,
X * non-ascii characters, long lines, and incomplete last lines. The status
X * messages now match the real vi correctly for all cases.
X * 
X * Revision 1.2  88/03/21  16:46:01  tony
X * Changed the line counter in renum() from 'int' to 'long' to correspond
X * to the actual type of the pseudo line number in the LINE structure.
X * 
X * Revision 1.1  88/03/20  21:07:19  tony
X * Initial revision
X * 
X *
X */
X
X#include "stevie.h"
X#include <sys/stat.h>
X
Xvoid
Xfilemess(s)
Xchar *s;
X{
X	smsg("\"%s\" %s", (Filename == NULL) ? "" : Filename, s);
X	flushbuf();
X}
X
Xvoid
Xrenum()
X{
X	LPTR	*p;
X	unsigned long l = 0;
X
X	for (p = Filemem; p != NULL ;p = nextline(p), l += LINEINC)
X		p->linep->num = l;
X
X	Fileend->linep->num = 0xffffffff;
X}
X
X#define	MAXLINE	256	/* maximum size of a line */
X
Xstatic char rdonly=0;
X
Xbool_t
Xreadfile(fname,fromp,nochangename)
Xchar	*fname;
XLPTR	*fromp;
Xbool_t	nochangename;	/* if TRUE, don't change the Filename */
X{
X	FILE	*f, *fopen();
X	LINE	*curr;
X	char	buff[MAXLINE], buf2[80];
X	int	i, c;
X	long	nchars = 0;
X	int	linecnt = 0;
X	bool_t	wasempty = bufempty();
X	int	nonascii = 0;		/* count garbage characters */
X	int	nulls = 0;		/* count nulls */
X	bool_t	incomplete = FALSE;	/* was the last line incomplete? */
X	bool_t	toolong = FALSE;	/* a line was too long */
X
X	curr = fromp->linep;
X
X	if ( ! nochangename )
X		Filename = strsave(fname);
X
X	if ( (f=fopen(fixname(fname),"r")) == NULL )
X		return TRUE;
X
X	rdonly = (access(fname, 2) != 0);
X	filemess( rdonly ? "[Read only]" : "");
X
X	i = 0;
X	do {
X		c = getc(f);
X
X		if (c == EOF) {
X			if (i == 0)	/* normal loop termination */
X				break;
X
X			/*
X			 * If we get EOF in the middle of a line, note the
X			 * fact and complete the line ourselves.
X			 */
X			incomplete = TRUE;
X			c = NL;
X		}
X
X		if (c >= 0x80) {
X			c -= 0x80;
X			nonascii++;
X		}
X
X		/*
X		 * If we reached the end of the line, OR we ran out of
X		 * space for it, then process the complete line.
X		 */
X		if (c == NL || i == (MAXLINE-1)) {
X			int	len;
X			LINE	*lp;
X
X			if (c != NL)
X				toolong = TRUE;
X
X			buff[i] = '\0';
X			len = strlen(buff) + 1;
X			if ((lp = newline(len)) == NULL)
X				{msg("Buffer overflow - File is uncomplete !!\n\r");
X				break;
X				}
X
X			strcpy(lp->s, buff);
X
X			curr->next->prev = lp;	/* new line to next one */
X			lp->next = curr->next;
X
X			curr->next = lp;	/* new line to prior one */
X			lp->prev = curr;
X
X			curr = lp;		/* new line becomes current */
X			i = 0;
X			linecnt++;
X
X		} else if (c == NUL)
X			nulls++;		/* count and ignore nulls */
X		else {
X			buff[i++] = c;		/* normal character */
X		}
X
X		nchars++;
X
X	} while (!incomplete && !toolong);
X
X	fclose(f);
X
X	/*
X	 * If the buffer was empty when we started, we have to go back
X	 * and remove the "dummy" line at Filemem and patch up the ptrs.
X	 */
X	if (wasempty && nchars > 0 /* !!! */) {
X		LINE	*dummy = Filemem->linep;	/* dummy line ptr */
X
X		free(dummy->s);				/* free string space */
X		Filemem->linep = Filemem->linep->next;
X		free(dummy);				/* free LINE struct */
X		Filemem->linep->prev = Filetop->linep;
X		Filetop->linep->next = Filemem->linep;
X
X		Curschar->linep = Filemem->linep;
X		Topchar->linep  = Filemem->linep;
X	}
X
X	renum();
X
X	if (toolong) {
X		smsg("\"%s\" Line too long", fname);
X		return FALSE;
X	}
X
X	sprintf(buff, "\"%s\" %s %s%d line%s, %ld character%s",
X		fname,
X		rdonly ? "[Read only]" : "",
X		incomplete ? "[Incomplete last line] " : "",
X		linecnt, (linecnt > 1) ? "s" : "",
X		nchars, (nchars > 1) ? "s" : "");
X
X	buf2[0] = NUL;
X
X	if (nonascii || nulls) {
X		if (nonascii) {
X			if (nulls)
X				sprintf(buf2, " (%d null, %d non-ASCII)",
X					nulls, nonascii);
X			else
X				sprintf(buf2, " (%d non-ASCII)", nonascii);
X		} else
X			sprintf(buf2, " (%d null)", nulls);
X	}
X	strcat(buff, buf2);
X	msg(buff);
X
X	return FALSE;
X}
X
X
X/*
X * writeit - write to file 'fname' lines 'start' through 'end'
X *
X * If either 'start' or 'end' contain null line pointers, the default
X * is to use the start or end of the file respectively.
X */
Xbool_t
Xwriteit(fname, start, end)
Xchar	*fname;
XLPTR	*start, *end;
X{
X	FILE	*f, *fopen();
X	FILE	*fopenb();		/* open in binary mode, where needed */
X	char	*backup, *s;
X	long	nchars;
X	int	lines;
X	LPTR	*p;
X	struct stat sbuf;
X	char newfile=0;
X  
X	if (stat(fname, &sbuf)!= 0)	/* save mode of file for creating */
X		newfile = 1;
X  
X  	/*
X
X	smsg("\"%s\"", fname);
X
X	/*
X	 * Form the backup file name - change foo.* to foo.bak
X	 */
X	backup = alloc((unsigned) (strlen(fname) + 5));
X	strcpy(backup, fname);
X	for (s = backup; *s && *s != '.' ;s++)
X		;
X	*s = NUL;
X	strcat(backup, ".bak");
X
X	/*
X	 * Delete any existing backup and move the current version
X	 * to the backup. For safety, we don't remove the backup
X	 * until the write has finished successfully. And if the
X	 * 'backup' option is set, leave it around.
X	 */
X	/* skipping rename avoids overwriting  R/O files by creating a new */
X	/* also save link structure 			R. Regn*/
X
X	if (!rdonly && sbuf.st_nlink == 1)
X		rename(fname, backup);
X
X
X	f = P(P_CR) ? fopen(fixname(fname), "w") : fopenb(fixname(fname), "w");
X
X	if ( f == NULL ) {
X		if (rdonly)
X			emsg("File is Read only");
X		else 	emsg("Permission denied");
X		free(backup);
X		return FALSE;
X	}
X
X	if (!rdonly && !newfile)	/* restore mode (and owner) properly */
X		{chmod (fname, sbuf.st_mode&04777);
X		 chown (fname, sbuf.st_uid, sbuf.st_gid);
X		}
X
X	/*
X	 * If we were given a bound, start there. Otherwise just
X	 * start at the beginning of the file.
X	 */
X	if (start == NULL || start->linep == NULL)
X		p = Filemem;
X	else
X		p = start;
X
X	lines = nchars = 0;
X	do {
X		fprintf(f, "%s\n", p->linep->s);
X		nchars += strlen(p->linep->s) + 1;
X		lines++;
X
X		/*
X		 * If we were given an upper bound, and we just did that
X		 * line, then bag it now.
X		 */
X		if (end != NULL && end->linep != NULL) {
X			if (end->linep == p->linep)
X				break;
X		}
X
X	} while ((p = nextline(p)) != NULL);
X
X	fclose(f);
X	smsg("\"%s\" %s %d line%s, %ld character%s", fname,
X		newfile ? "[New File]" : "",
X		lines, (lines > 1) ? "s" : "",
X		nchars, (nchars > 1) ? "s" : "");
X
X	UNCHANGED;
X
X	/*
X	 * Remove the backup unless they want it left around
X	 */
X	if (!P(P_BK))
X		remove(backup);
X
X	free(backup);
X
X	return TRUE;
X}
END_OF_FILE
if test 7044 -ne `wc -c <'fileio.c'`; then
    echo shar: \"'fileio.c'\" unpacked with wrong size!
fi
# end of 'fileio.c'
fi
if test -f 'help.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'help.c'\"
else
echo shar: Extracting \"'help.c'\" \(9234 characters\)
sed "s/^X//" >'help.c' <<'END_OF_FILE'
Xstatic	char	RCSid[] =
X"$Header: help.c,v 1.7 88/10/28 13:59:18 tony Exp $";
X
X/*
X * Routine to display a command summary.
X *
X * $Log:	help.c,v $
X * Revision 1.7  88/10/28  13:59:18  tony
X * Minor changes to the screen.
X * 
X * Revision 1.6  88/10/27  07:55:13  tony
X * Rearranged the screens to fit more of the colon commands.
X * 
X * Revision 1.5  88/08/30  20:36:46  tony
X * After much prodding from Mark, I finally added support for replace mode.
X * 
X * Revision 1.4  88/08/26  13:41:56  tony
X * Added a mention of the '!' operator as that is now supported.
X * 
X * Revision 1.3  88/08/26  08:45:05  tony
X * Misc. changes to make lint happy.
X * 
X * Revision 1.2  88/07/09  20:37:46  tony
X * Changed the help screen containing 'U' to remove the "not yet" notation.
X * 
X * Revision 1.1  88/03/20  21:07:26  tony
X * Initial revision
X * 
X *
X */
X
X#include "stevie.h"
X
Xextern	char	*Version;
X
Xstatic	int	helprow;
X
X#ifdef	HELP
X
Xstatic	void	longline();
X
Xbool_t
Xhelp()
X{
X
X/***********************************************************************
X * First Screen:   Positioning within file, Adjusting the Screen
X ***********************************************************************/
X
X	outstr(T_ED);
X	windgoto(helprow = 0, 0);
X
Xlongline("\
X   Positioning within file\n\
X   =======================\n\
X      ^F             Forward screenfull             Developed by:\n\
X      ^B             Backward screenfull                Tony Andrews\n");
Xlongline("\
X      ^D             scroll down half screen\n\
X      ^U             scroll up half screen          Based on a program by:\n");
Xlongline("\
X      G              Goto line (end default)            Tim Thompson\n\
X      ]]             next function\n\
X      [[             previous function\n\
X      /re            next occurence of regular expression 're'\n");
Xlongline("\
X      ?re            prior occurence of regular expression 're'\n\
X      n              repeat last / or ?\n\
X      N              reverse last / or ?\n\
X      %              find matching (, ), {, }, [, or ]\n");
Xlongline("\
X\n\
X   Adjusting the screen\n\
X   ====================\n\
X      ^L             Redraw the screen\n\
X      ^E             scroll window down 1 line\n\
X      ^Y             scroll window up 1 line\n");
Xlongline("\
X      z<RETURN>      redraw, current line at top\n\
X      z-             ... at bottom\n\
X      z.             ... at center\n");
X
X	windgoto(0, 52);
X	longline(Version);
X
X	windgoto(helprow = Rows-2, 47);
X	longline("<Press space bar to continue>\n");
X	windgoto(helprow = Rows-1, 47);
X	longline("<Any other key will quit>");
X
X	if ( vgetc() != ' ' )
X		return TRUE;
X
X/***********************************************************************
X * Second Screen:   Character positioning
X ***********************************************************************/
X
X	outstr(T_ED);
X	windgoto(helprow = 0, 0);
X
Xlongline("\
X   Character Positioning\n\
X   =====================\n\
X      ^              first non-white\n\
X      0              beginning of line\n\
X      $              end of line\n\
X      h              backward\n");
Xlongline("\
X      l              forward\n\
X      ^H             same as h\n\
X      space          same as l\n\
X      fx             find 'x' forward\n");
Xlongline("\
X      Fx             find 'x' backward\n\
X      tx             upto 'x' forward\n\
X      Tx             upto 'x' backward\n\
X      ;              Repeat last f, F, t, or T\n");
Xlongline("\
X      ,              inverse of ;\n\
X      |              to specified column\n\
X      %              find matching (, ), {, }, [, or ]\n");
X
X	windgoto(helprow = Rows-2, 47);
X	longline("<Press space bar to continue>\n");
X	windgoto(helprow = Rows-1, 47);
X	longline("<Any other key will quit>");
X
X	if ( vgetc() != ' ' )
X		return TRUE;
X
X/***********************************************************************
X * Third Screen:   Line Positioning, Marking and Returning
X ***********************************************************************/
X
X	outstr(T_ED);
X	windgoto(helprow = 0, 0);
X
Xlongline("\
X    Line Positioning\n\
X    ================\n\
X    H           home window line\n\
X    L           last window line\n\
X    M           middle window line\n");
Xlongline("\
X    +           next line, at first non-white\n\
X    -           previous line, at first non-white\n\
X    CR          return, same as +\n\
X    j           next line, same column\n\
X    k           previous line, same column\n");
X
Xlongline("\
X\n\
X    Marking and Returning\n\
X    =====================\n\
X    ``          previous context\n\
X    ''          ... at first non-white in line\n");
Xlongline("\
X    mx          mark position with letter 'x'\n\
X    `x          to mark 'x'\n\
X    'x          ... at first non-white in line\n");
X
Xlongline("\n\
X    Undo  &  Redo\n\
X    =============\n\
X    u           undo last change\n\
X    U           restore current line\n\
X    .           repeat last change\n");
X
X	windgoto(helprow = Rows-2, 47);
X	longline("<Press space bar to continue>\n");
X	windgoto(helprow = Rows-1, 47);
X	longline("<Any other key will quit>");
X
X	if ( vgetc() != ' ' )
X		return TRUE;
X/***********************************************************************
X * Fourth Screen:   Insert & Replace, 
X ***********************************************************************/
X
X	outstr(T_ED);
X	windgoto(helprow = 0, 0);
X
Xlongline("\
X    Insert and Replace\n\
X    ==================\n\
X    a           append after cursor\n\
X    i           insert before cursor\n\
X    A           append at end of line\n\
X    I           insert before first non-blank\n");
Xlongline("\
X    o           open line below\n\
X    O           open line above\n\
X    rx          replace single char with 'x'\n\
X    R           replace characters\n");
X
Xlongline("\
X\n\
X    Words, sentences, paragraphs\n\
X    ============================\n\
X    w           word forward\n\
X    b           back word\n\
X    e           end of word\n\
X    )           to next sentence (not yet)\n\
X    }           to next paragraph (not yet)\n");
Xlongline("\
X    (           back sentence (not yet)\n\
X    {           back paragraph (not yet)\n\
X    W           blank delimited word\n\
X    B           back W\n\
X    E           to end of W\n");
X
X	windgoto(helprow = Rows-2, 47);
X	longline("<Press space bar to continue>\n");
X	windgoto(helprow = Rows-1, 47);
X	longline("<Any other key will quit>");
X
X	if ( vgetc() != ' ' )
X		return TRUE;
X
X/***********************************************************************
X * Fifth Screen:   Misc. operations, 
X ***********************************************************************/
X
X	outstr(T_ED);
X	windgoto(helprow = 0, 0);
X
Xlongline("\
X    Miscellaneous Commands\n\
X    ======================\n");
Xlongline("\
X    :w          write back changes\n\
X    :wq         write and quit\n\
X    :x          write if modified, and quit\n\
X    :q          quit\n\
X    :q!         quit, discard changes\n\
X    :e name     edit file 'name'\n");
Xlongline("\
X    :e!         reedit, discard changes\n\
X    :e #        edit alternate file\n\
X    :w name     write file 'name'\n");
Xlongline("\
X    :n          edit next file in arglist\n\
X    :N          edit prior file in arglist\n\
X    :n args     specify new arglist (not yet)\n\
X    :rew        rewind arglist\n\
X    :f          show current file and lines\n");
Xlongline("\
X    :f file     change current file name\n\
X    :g/pat/p|d  global command (print or delete only)\n\
X    :s/p1/p2/   text substitution (trailing 'g' optional)\n\
X");
Xlongline("\
X    :ta tag     to tag file entry 'tag'\n\
X    ^]          :ta, current word is tag\n\
X    :sh         run an interactive shell\n\
X    :!cmd       execute a shell command\n\
X");
X
X	windgoto(helprow = Rows-2, 47);
X	longline("<Press space bar to continue>\n");
X	windgoto(helprow = Rows-1, 47);
X	longline("<Any other key will quit>");
X
X	if ( vgetc() != ' ' )
X		return TRUE;
X
X/***********************************************************************
X * Sixth Screen:   Operators, Misc. operations, Yank & Put
X ***********************************************************************/
X
X	outstr(T_ED);
X	windgoto(helprow = 0, 0);
X
Xlongline("\
X    Operators (double to affect lines)\n\
X    ==================================\n\
X    d           delete\n\
X    c           change\n");
Xlongline("\
X    <           left shift\n\
X    >           right shift\n\
X    y           yank to buffer\n\
X    !           filter lines\n");
X
Xlongline("\n\
X    Miscellaneous operations\n\
X    ========================\n\
X    C           change rest of line\n\
X    D           delete rest of line\n\
X    s           substitute chars\n");
Xlongline("\
X    S           substitute lines (not yet)\n\
X    J           join lines\n\
X    x           delete characters\n\
X    X           ... before cursor\n");
X
Xlongline("\n\
X    Yank and Put\n\
X    ============\n\
X    p           put back text\n\
X    P           put before\n\
X    Y           yank lines");
X
X	windgoto(helprow = Rows-1, 47);
X	longline("<Press any key>");
X
X	vgetc();
X
X	return TRUE;
X}
X
Xstatic void
Xlongline(p)
Xchar *p;
X{
X	char *s;
X
X	for ( s = p; *s ;s++ ) {
X		if ( *s == '\n' )
X			windgoto(++helprow, 0);
X		else
X			outchar(*s);
X	}
X}
X#else
X
Xbool_t
Xhelp()
X{
X	msg("Sorry, help not configured");
X	return FALSE;
X}
X#endif
END_OF_FILE
if test 9234 -ne `wc -c <'help.c'`; then
    echo shar: \"'help.c'\" unpacked with wrong size!
fi
# end of 'help.c'
fi
if test -f 'hexchars.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'hexchars.c'\"
else
echo shar: Extracting \"'hexchars.c'\" \(3413 characters\)
sed "s/^X//" >'hexchars.c' <<'END_OF_FILE'
Xstatic	char	RCSid[] =
X"$Header: hexchars.c,v 1.3 88/08/26 08:45:10 tony Exp $";
X
X/*
X * Contains information concerning the representation of characters for
X * visual output by the editor.
X *
X * $Log:	hexchars.c,v $
X * Revision 1.3  88/08/26  08:45:10  tony
X * Misc. changes to make lint happy.
X * 
X * Revision 1.2  88/05/03  14:38:43  tony
X * Fixed the representation for the ascii character DELETE to be the
X * same as vi.
X * 
X * Revision 1.1  88/03/20  21:07:42  tony
X * Initial revision
X * 
X *
X */
X
X#include "stevie.h"
X
X/*
X * This file shows how to display characters on the screen. This is
X * approach is something of an overkill. It's a remnant from the
X * original code that isn't worth messing with for now. TABS are
X * special-cased depending on the value of the "list" parameter.
X */
X
Xstruct charinfo chars[] = {
X	/* 000 */	1, NULL,
X	/* 001 */	2, "^A",
X	/* 002 */	2, "^B",
X	/* 003 */	2, "^C",
X	/* 004 */	2, "^D",
X	/* 005 */	2, "^E",
X	/* 006 */	2, "^F",
X	/* 007 */	2, "^G",
X	/* 010 */	2, "^H",
X	/* 011 */	2, "^I",
X	/* 012 */	7, "[ERROR]",	/* shouldn't occur */
X	/* 013 */	2, "^K",
X	/* 014 */	2, "^L",
X	/* 015 */	2, "^M",
X	/* 016 */	2, "^N",
X	/* 017 */	2, "^O",
X	/* 020 */	2, "^P",
X	/* 021 */	2, "^Q",
X	/* 022 */	2, "^R",
X	/* 023 */	2, "^S",
X	/* 024 */	2, "^T",
X	/* 025 */	2, "^U",
X	/* 026 */	2, "^V",
X	/* 027 */	2, "^W",
X	/* 030 */	2, "^X",
X	/* 031 */	2, "^Y",
X	/* 032 */	2, "^Z",
X	/* 033 */	2, "^[",
X	/* 034 */	2, "^\\",
X	/* 035 */	2, "^]",
X	/* 036 */	2, "^^",
X	/* 037 */	2, "^_",
X	/* 040 */	1, NULL,
X	/* 041 */	1, NULL,
X	/* 042 */	1, NULL,
X	/* 043 */	1, NULL,
X	/* 044 */	1, NULL,
X	/* 045 */	1, NULL,
X	/* 046 */	1, NULL,
X	/* 047 */	1, NULL,
X	/* 050 */	1, NULL,
X	/* 051 */	1, NULL,
X	/* 052 */	1, NULL,
X	/* 053 */	1, NULL,
X	/* 054 */	1, NULL,
X	/* 055 */	1, NULL,
X	/* 056 */	1, NULL,
X	/* 057 */	1, NULL,
X	/* 060 */	1, NULL,
X	/* 061 */	1, NULL,
X	/* 062 */	1, NULL,
X	/* 063 */	1, NULL,
X	/* 064 */	1, NULL,
X	/* 065 */	1, NULL,
X	/* 066 */	1, NULL,
X	/* 067 */	1, NULL,
X	/* 070 */	1, NULL,
X	/* 071 */	1, NULL,
X	/* 072 */	1, NULL,
X	/* 073 */	1, NULL,
X	/* 074 */	1, NULL,
X	/* 075 */	1, NULL,
X	/* 076 */	1, NULL,
X	/* 077 */	1, NULL,
X	/* 100 */	1, NULL,
X	/* 101 */	1, NULL,
X	/* 102 */	1, NULL,
X	/* 103 */	1, NULL,
X	/* 104 */	1, NULL,
X	/* 105 */	1, NULL,
X	/* 106 */	1, NULL,
X	/* 107 */	1, NULL,
X	/* 110 */	1, NULL,
X	/* 111 */	1, NULL,
X	/* 112 */	1, NULL,
X	/* 113 */	1, NULL,
X	/* 114 */	1, NULL,
X	/* 115 */	1, NULL,
X	/* 116 */	1, NULL,
X	/* 117 */	1, NULL,
X	/* 120 */	1, NULL,
X	/* 121 */	1, NULL,
X	/* 122 */	1, NULL,
X	/* 123 */	1, NULL,
X	/* 124 */	1, NULL,
X	/* 125 */	1, NULL,
X	/* 126 */	1, NULL,
X	/* 127 */	1, NULL,
X	/* 130 */	1, NULL,
X	/* 131 */	1, NULL,
X	/* 132 */	1, NULL,
X	/* 133 */	1, NULL,
X	/* 134 */	1, NULL,
X	/* 135 */	1, NULL,
X	/* 136 */	1, NULL,
X	/* 137 */	1, NULL,
X	/* 140 */	1, NULL,
X	/* 141 */	1, NULL,
X	/* 142 */	1, NULL,
X	/* 143 */	1, NULL,
X	/* 144 */	1, NULL,
X	/* 145 */	1, NULL,
X	/* 146 */	1, NULL,
X	/* 147 */	1, NULL,
X	/* 150 */	1, NULL,
X	/* 151 */	1, NULL,
X	/* 152 */	1, NULL,
X	/* 153 */	1, NULL,
X	/* 154 */	1, NULL,
X	/* 155 */	1, NULL,
X	/* 156 */	1, NULL,
X	/* 157 */	1, NULL,
X	/* 160 */	1, NULL,
X	/* 161 */	1, NULL,
X	/* 162 */	1, NULL,
X	/* 163 */	1, NULL,
X	/* 164 */	1, NULL,
X	/* 165 */	1, NULL,
X	/* 166 */	1, NULL,
X	/* 167 */	1, NULL,
X	/* 170 */	1, NULL,
X	/* 171 */	1, NULL,
X	/* 172 */	1, NULL,
X	/* 173 */	1, NULL,
X	/* 174 */	1, NULL,
X	/* 175 */	1, NULL,
X	/* 176 */	1, NULL,
X	/* 177 */	2, "^?",
X};
END_OF_FILE
if test 3413 -ne `wc -c <'hexchars.c'`; then
    echo shar: \"'hexchars.c'\" unpacked with wrong size!
fi
# end of 'hexchars.c'
fi
if test -f 'keymap.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'keymap.h'\"
else
echo shar: Extracting \"'keymap.h'\" \(1076 characters\)
sed "s/^X//" >'keymap.h' <<'END_OF_FILE'
X/*
X * $Header: keymap.h,v 1.1 88/03/20 21:03:51 tony Exp $
X *
X * Keycode definitions for special keys
X *
X * On systems that have any of these keys, the routine 'inchar' in the
X * machine-dependent code should return one of the codes here.
X *
X * $Log:	keymap.h,v $
X * Revision 1.1  88/03/20  21:03:51  tony
X * Initial revision
X * 
X *
X */
X
X#define	K_HELP		0x80
X#define	K_UNDO		0x81
X#define	K_INSERT	0x82
X#define	K_HOME		0x83
X#define	K_UARROW	0x84
X#define	K_DARROW	0x85
X#define	K_LARROW	0x86
X#define	K_RARROW	0x87
X#ifdef ATARI
X#define	K_CGRAVE	0x88	/* control grave accent */
X#else
X#define	K_CGRAVE	036	/* control grave accent */
X#endif
X
X#define	K_F1		0x91	/* function keys */
X#define	K_F2		0x92
X#define	K_F3		0x93
X#define	K_F4		0x94
X#define	K_F5		0x95
X#define	K_F6		0x96
X#define	K_F7		0x97
X#define	K_F8		0x98
X#define	K_F9		0x99
X#define	K_F10		0x9a
X
X#define	K_SF1		0xa1	/* shifted function keys */
X#define	K_SF2		0xa2
X#define	K_SF3		0xa3
X#define	K_SF4		0xa4
X#define	K_SF5		0xa5
X#define	K_SF6		0xa6
X#define	K_SF7		0xa7
X#define	K_SF8		0xa8
X#define	K_SF9		0xa9
X#define	K_SF10		0xaa
END_OF_FILE
if test 1076 -ne `wc -c <'keymap.h'`; then
    echo shar: \"'keymap.h'\" unpacked with wrong size!
fi
# end of 'keymap.h'
fi
if test -f 'linefunc.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'linefunc.c'\"
else
echo shar: Extracting \"'linefunc.c'\" \(1656 characters\)
sed "s/^X//" >'linefunc.c' <<'END_OF_FILE'
Xstatic	char	RCSid[] =
X"$Header: linefunc.c,v 1.1 88/03/20 21:08:07 tony Exp $";
X
X/*
X * Basic line-oriented motions.
X *
X * $Log:	linefunc.c,v $
X * Revision 1.1  88/03/20  21:08:07  tony
X * Initial revision
X * 
X *
X */
X
X#include "stevie.h"
X
X/*
X * nextline(curr)
X *
X * Return a pointer to the beginning of the next line after the one
X * referenced by 'curr'. Return NULL if there is no next line (at EOF).
X */
X
XLPTR *
Xnextline(curr)
XLPTR *curr;
X{
X	static	LPTR	next;
X
X	if (curr->linep->next != Fileend->linep) {
X		next.index = 0;
X		next.linep = curr->linep->next;
X		return &next;
X	}
X	return (LPTR *) NULL;
X}
X
X/*
X * prevline(curr)
X *
X * Return a pointer to the beginning of the line before the one
X * referenced by 'curr'. Return NULL if there is no prior line.
X */
X
XLPTR *
Xprevline(curr)
XLPTR *curr;
X{
X	static	LPTR	prev;
X
X	if (curr->linep->prev != Filetop->linep) {
X		prev.index = 0;
X		prev.linep = curr->linep->prev;
X		return &prev;
X	}
X	return (LPTR *) NULL;
X}
X
X/*
X * coladvance(p,col)
X *
X * Try to advance to the specified column, starting at p.
X */
X
XLPTR *
Xcoladvance(p, col)
XLPTR	*p;
Xint	col;
X{
X	static	LPTR	lp;
X	int	c, in;
X
X	lp.linep = p->linep;
X	lp.index = p->index;
X
X	/* If we're on a blank ('\n' only) line, we can't do anything */
X	if (lp.linep->s[lp.index] == '\0')
X		return &lp;
X	/* try to advance to the specified column */
X	for ( c=0; col-- > 0; c++ ) {
X		/* Count a tab for what it's worth (if list mode not on) */
X		if ( gchar(&lp) == TAB && !P(P_LS) ) {
X			in = ((P(P_TS)-1) - c%P(P_TS));
X			col -= in;
X			c += in;
X		}
X		/* Don't go past the end of */
X		/* the file or the line. */
X		if (inc(&lp)) {
X			dec(&lp);
X			break;
X		}
X	}
X	return &lp;
X}
END_OF_FILE
if test 1656 -ne `wc -c <'linefunc.c'`; then
    echo shar: \"'linefunc.c'\" unpacked with wrong size!
fi
# end of 'linefunc.c'
fi
echo shar: End of shell archive.
exit 0

rtregn@immd3.informatik.uni-erlangen.de (Robert Regn) (12/15/88)

Found 1 control char in "'makefile'"
makefile.min was the original makefile for ST Minix

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  main.c makefile makefile.dos makefile.min makefile.os2
#   makefile.tos makefile.usg mark.c minix.c misccmds.c normal.c os2_c
# Wrapped by rtregn@faui32 on Wed Dec 14 17:08:00 1988
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'main.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'main.c'\"
else
echo shar: Extracting \"'main.c'\" \(7937 characters\)
sed "s/^X//" >'main.c' <<'END_OF_FILE'
Xstatic	char	RCSid[] =
X"$Header: main.c,v 1.5 88/11/10 13:28:38 tony Exp $";
X
X/*
X * The main routine and routines to deal with the input buffer.
X *
X * $Log:	main.c,v $
X * Revision 1.5  88/11/10  13:28:38  tony
X * Moved the call to do_mlines() to edit() so that it gets called after
X * the various position pointers have been initialized.
X * 
X * Revision 1.4  88/11/10  08:58:40  tony
X * Added code to scan for modelines in the file currently being edited.
X * 
X * Revision 1.3  88/11/01  21:32:29  tony
X * Changed the RBSIZE macro (and comment) since it is no longer tied to
X * the yank buffer size.
X * 
X * Revision 1.2  88/08/26  08:45:14  tony
X * Misc. changes to make lint happy.
X * 
X * Revision 1.1  88/03/20  21:08:18  tony
X * Initial revision
X * 
X *
X */
X
X#include "stevie.h"
X
Xint Rows;		/* Number of Rows and Columns */
Xint Columns;		/* in the current window. */
X
Xchar *Realscreen = NULL;	/* What's currently on the screen, a single */
X				/* array of size Rows*Columns. */
Xchar *Nextscreen = NULL;	/* What's to be put on the screen. */
X
Xchar *Filename = NULL;	/* Current file name */
X
XLPTR *Filemem;		/* Pointer to the first line of the file */
X
XLPTR *Filetop;		/* Line 'above' the start of the file */
X
XLPTR *Fileend;		/* Pointer to the end of the file in Filemem. */
X			/* (It points to the byte AFTER the last byte.) */
X
XLPTR *Topchar;		/* Pointer to the byte in Filemem which is */
X			/* in the upper left corner of the screen. */
X
XLPTR *Botchar;		/* Pointer to the byte in Filemem which is */
X			/* just off the bottom of the screen. */
X
XLPTR *Curschar;		/* Pointer to byte in Filemem at which the */
X			/* cursor is currently placed. */
X
Xint Cursrow, Curscol;	/* Current position of cursor */
X
Xint Cursvcol;		/* Current virtual column, the column number of */
X			/* the file's actual line, as opposed to the */
X			/* column number we're at on the screen.  This */
X			/* makes a difference on lines that span more */
X			/* than one screen line. */
X
Xint Curswant = 0;	/* The column we'd like to be at. This is used */
X			/* try to stay in the same column through up/down */
X			/* cursor motions. */
X
Xbool_t set_want_col;	/* If set, then update Curswant the next time */
X			/* through cursupdate() to the current virtual */
X			/* column. */
X
Xint State = NORMAL;	/* This is the current state of the command */
X			/* interpreter. */
X
Xint Prenum = 0;		/* The (optional) number before a command. */
X
XLPTR *Insstart;		/* This is where the latest insert/append */
X			/* mode started. */
X
Xbool_t Changed = 0;	/* Set to 1 if something in the file has been */
X			/* changed and not written out. */
X
Xchar Redobuff[1024];	/* Each command should stuff characters into this */
X			/* buffer that will re-execute itself. */
X
Xchar Insbuff[1024];	/* Each insertion gets stuffed into this buffer. */
X
Xint Ninsert = 0;	/* Number of characters in the current insertion. */
Xchar *Insptr = NULL;
X
Xchar **files;		/* list of input files */
Xint  numfiles;		/* number of input files */
Xint  curfile;		/* number of the current file */
X
Xstatic void
Xusage()
X{
X	fprintf(stderr, "usage: stevie [file ...]\n");
X	fprintf(stderr, "       stevie -t tag\n");
X	fprintf(stderr, "       stevie +[num] file\n");
X	fprintf(stderr, "       stevie +/pat  file\n");
X	exit(1);
X}
X
Xmain(argc,argv)
Xint	argc;
Xchar	*argv[];
X{
X	char	*initstr, *getenv();	/* init string from the environment */
X	char	*tag = NULL;		/* tag from command line */
X	char	*pat = NULL;		/* pattern from command line */
X	int	line = -1;		/* line number from command line */
X
X	/*
X	 * Process the command line arguments.
X	 */
X	if (argc > 1) {
X		switch (argv[1][0]) {
X		
X		case '-':			/* -t tag */
X			if (argv[1][1] != 't')
X				usage();
X
X			if (argv[2] == NULL)
X				usage();
X
X			Filename = NULL;
X			tag = argv[2];
X			numfiles = 1;
X			break;
X
X		case '+':			/* +n or +/pat */
X			if (argv[1][1] == '/') {
X				if (argv[2] == NULL)
X					usage();
X				Filename = strsave(argv[2]);
X				pat = &(argv[1][1]);
X				numfiles = 1;
X
X			} else if (isdigit(argv[1][1]) || argv[1][1] == NUL) {
X				if (argv[2] == NULL)
X					usage();
X				Filename = strsave(argv[2]);
X				numfiles = 1;
X
X				line = (isdigit(argv[1][1])) ?
X					atoi(&(argv[1][1])) : 0;
X			} else
X				usage();
X
X			break;
X
X		default:			/* must be a file name */
X			Filename = strsave(argv[1]);
X			files = &(argv[1]);
X			numfiles = argc - 1;
X			break;
X		}
X	} else {
X		Filename = NULL;
X		numfiles = 1;
X	}
X	curfile = 0;
X
X	if (numfiles > 1)
X		fprintf(stderr, "%d files to edit\n", numfiles);
X
X	windinit();
X
X	/*
X	 * Allocate LPTR structures for all the various position pointers
X	 */
X	if ((Filemem = (LPTR *) malloc(sizeof(LPTR))) == NULL ||
X	    (Filetop = (LPTR *) malloc(sizeof(LPTR))) == NULL ||
X	    (Fileend = (LPTR *) malloc(sizeof(LPTR))) == NULL ||
X	    (Topchar = (LPTR *) malloc(sizeof(LPTR))) == NULL ||
X	    (Botchar = (LPTR *) malloc(sizeof(LPTR))) == NULL ||
X	    (Curschar = (LPTR *) malloc(sizeof(LPTR))) == NULL ||
X	    (Insstart = (LPTR *) malloc(sizeof(LPTR))) == NULL ) {
X		fprintf(stderr, "Can't allocate data structures\n");
X		windexit(0);
X	}
X
X	screenalloc();
X	filealloc();		/* Initialize Filemem, Filetop, and Fileend */
X
X	screenclear();
X
X	if ((initstr = getenv("EXINIT")) != NULL) {
X		char *lp, buf[128];
X
X		if ((lp = getenv("LINES")) != NULL) {
X			sprintf(buf, "%s lines=%s", initstr, lp);
X			readcmdline(':', buf);
X		} else
X			readcmdline(':', initstr);
X	}
X
X	if (Filename != NULL) {
X		if (readfile(Filename, Filemem, FALSE))
X			filemess("[New File]");
X	} else
X		msg("Empty Buffer");
X
X	setpcmark();
X
X	updatescreen();
X	
X	if (tag) {
X		stuffin(":ta ");
X		stuffin(tag);
X		stuffin("\n");
X
X	} else if (pat) {
X		stuffin(pat);
X		stuffin("\n");
X
X	} else if (line >= 0) {
X		if (line > 0)
X			stuffnum(line);
X		stuffin("G");
X	}
X
X	edit();
X
X	windexit(0);
X
X	return 1;		/* shouldn't be reached */
X}
X
X#define	RBSIZE	1024
Xstatic char getcbuff[RBSIZE];
Xstatic char *getcnext = NULL;
X
Xvoid
Xstuffin(s)
Xchar *s;
X{
X	if ( getcnext == NULL ) {
X		strcpy(getcbuff,s);
X		getcnext = getcbuff;
X	} else
X		strcat(getcbuff,s);
X}
X
Xvoid
Xstuffnum(n)
Xint	n;
X{
X	char	buf[32];
X
X	sprintf(buf, "%d", n);
X	stuffin(buf);
X}
X
X/*VARARGS1*/
Xvoid
Xaddtobuff(s,c1,c2,c3,c4,c5,c6)
Xchar *s;
Xchar c1, c2, c3, c4, c5, c6;
X{
X	char *p = s;
X	if ( (*p++ = c1) == NUL )
X		return;
X	if ( (*p++ = c2) == NUL )
X		return;
X	if ( (*p++ = c3) == NUL )
X		return;
X	if ( (*p++ = c4) == NUL )
X		return;
X	if ( (*p++ = c5) == NUL )
X		return;
X	if ( (*p++ = c6) == NUL )
X		return;
X}
X
Xint
Xvgetc()
X{
X	int	c;
X
X	/*
X	 * inchar() may map special keys by using stuffin(). If it does
X	 * so, it returns -1 so we know to loop here to get a real char.
X	 */
X	do {
X		if ( getcnext != NULL ) {
X			int nextc = *getcnext++;
X			if ( *getcnext == NUL ) {
X				*getcbuff = NUL;
X				getcnext = NULL;
X			}
X			return(nextc);
X		}
X		c = inchar();
X	} while (c == -1);
X
X	return c;
X}
X
X#if 0
Xint
Xvpeekc()
X{
X	if ( getcnext != NULL )
X		return(*getcnext);
X	return(-1);
X}
X#endif
X
X/*
X * anyinput
X *
X * Return non-zero if input is pending.
X */
X
Xbool_t
Xanyinput()
X{
X	return (getcnext != NULL);
X}
X
X/*
X * do_mlines() - process mode lines for the current file
X *
X * Returns immediately if the "ml" parameter isn't set.
X */
X#define	NMLINES	5	/* no. of lines at start/end to check for modelines */
X
Xvoid
Xdo_mlines()
X{
X	void	chk_mline();
X	int	i;
X	LPTR	*p;
X
X	if (!P(P_ML))
X		return;
X
X	p = Filemem;
X	for (i=0; i < NMLINES ;i++) {
X		chk_mline(p->linep->s);
X		if ((p = nextline(p)) == NULL)
X			break;
X	}
X
X	if ((p = prevline(Fileend)) == NULL)
X		return;
X
X	for (i=0; i < NMLINES ;i++) {
X		chk_mline(p->linep->s);
X		if ((p = prevline(p)) == NULL)
X			break;
X	}
X}
X
X/*
X * chk_mline() - check a single line for a mode string
X */
Xstatic void
Xchk_mline(s)
Xregister char	*s;
X{
X	register char	*cs;		/* local copy of any modeline found */
X	register char	*e;
X
X	for (; *s != NUL ;s++) {
X		if (strncmp(s, "vi:", 3) == 0 || strncmp(s, "ex:", 3) == 0) {
X			cs = strsave(s+3);
X			if ((e = strchr(cs, ':')) != NULL) {
X				*e = NUL;
X				readcmdline(':', cs);
X			}
X			free(cs);
X		}
X	}
X}
END_OF_FILE
if test 7937 -ne `wc -c <'main.c'`; then
    echo shar: \"'main.c'\" unpacked with wrong size!
fi
# end of 'main.c'
fi
if test -f 'makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'makefile'\"
else
echo shar: Extracting \"'makefile'\" \(491 characters\)
sed "s/^X//" >'makefile' <<'END_OF_FILE'
X#
X# Makefile for PC Minix
X#
X
XLDFLAGS= -i -T/usr/tmp
XCFLAGS=  -DMINIX  -O
X
X
XMACH=   minix.s
X
XOBJ=    main.s edit.s linefunc.s normal.s cmdline.s hexchars.s \
X        misccmds.s help.s ptrfunc.s search.s alloc.s mark.s \
X        regexp.s regsub.s \
X        screen.s fileio.s param.s undo.s version.s
X
X
X
Xall : stevie
X
Xstevie : $(OBJ) $(MACH)
X	$(CC) $(LDFLAGS) $(OBJ) $(MACH) -o stevie
X	@echo  stevie fertig
X
Xctags:	ctags.c
X	$(CC) -o ctags ctags.c
X	chmem =4096 ctags
Xclean :
X	rm $(OBJ) $(MACH)
END_OF_FILE
echo shar: 1 control character may be missing from \"'makefile'\"
if test 491 -ne `wc -c <'makefile'`; then
    echo shar: \"'makefile'\" unpacked with wrong size!
fi
# end of 'makefile'
fi
if test -f 'makefile.dos' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'makefile.dos'\"
else
echo shar: Extracting \"'makefile.dos'\" \(1487 characters\)
sed "s/^X//" >'makefile.dos' <<'END_OF_FILE'
X#
X# Makefile for DOS
X#
X# This makefile is set up for Microsoft C 5.1
X#
X
X#
X# Compact model lets us edit large files, but keep small model code
X#
XMODEL= /AC
XCFLAGS = $(MODEL)
X
XMACH=	dos.obj
X
XOBJ=	main.obj edit.obj linefunc.obj normal.obj cmdline.obj hexchars.obj \
X	misccmds.obj help.obj ptrfunc.obj search.obj alloc.obj mark.obj \
X	regexp.obj regsub.obj \
X	screen.obj fileio.obj param.obj undo.obj version.obj $(MACH)
X
Xall: stevie.exe
X
Xmain.obj:	main.c
X	cl -c $(CFLAGS) main.c
X
Xalloc.obj : alloc.c
X	cl -c $(CFLAGS) alloc.c
X
Xedit.obj : edit.c
X	cl -c $(CFLAGS) edit.c
X
Xlinefunc.obj : linefunc.c
X	cl -c $(CFLAGS) linefunc.c
X
Xnormal.obj : normal.c
X	cl -c $(CFLAGS) normal.c
X
Xcmdline.obj : cmdline.c
X	cl -c $(CFLAGS) cmdline.c
X
Xhexchars.obj : hexchars.c
X	cl -c $(CFLAGS) hexchars.c
X
Xmisccmds.obj : misccmds.c
X	cl -c $(CFLAGS) misccmds.c
X
Xhelp.obj : help.c
X	cl -c $(CFLAGS) help.c
X
Xptrfunc.obj : ptrfunc.c
X	cl -c $(CFLAGS) ptrfunc.c
X
Xsearch.obj : search.c
X	cl -c $(CFLAGS) search.c
X
Xmark.obj : mark.c
X	cl -c $(CFLAGS) mark.c
X
Xscreen.obj : screen.c
X	cl -c $(CFLAGS) screen.c
X
Xfileio.obj : fileio.c
X	cl -c $(CFLAGS) fileio.c
X
Xparam.obj : param.c
X	cl -c $(CFLAGS) param.c
X
Xdos.obj : dos.c
X	cl -c $(CFLAGS) dos.c
X
Xregexp.obj : regexp.c
X	cl -c $(CFLAGS) regexp.c
X
Xregsub.obj : regsub.c
X	cl -c $(CFLAGS) regsub.c
X
Xundo.obj : undo.c
X	cl -c $(CFLAGS) undo.c
X
Xversion.obj : version.c
X	cl -c $(CFLAGS) version.c
X
Xstevie.exe : $(OBJ)
X	cl $(MODEL) *.obj c:\lib\setargv.obj -o stevie.exe /F 6000 -link /NOE
END_OF_FILE
if test 1487 -ne `wc -c <'makefile.dos'`; then
    echo shar: \"'makefile.dos'\" unpacked with wrong size!
fi
# end of 'makefile.dos'
fi
if test -f 'makefile.min' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'makefile.min'\"
else
echo shar: Extracting \"'makefile.min'\" \(393 characters\)
sed "s/^X//" >'makefile.min' <<'END_OF_FILE'
X#
X# Makefile for Atari ST Minix
X#
X
XLDFLAGS= -T.
XCFLAGS= -O -T.
X
XMACH=	minix.o
X
XOBJ=	main.o edit.o linefunc.o normal.o cmdline.o hexchars.o \
X	misccmds.o help.o ptrfunc.o search.o alloc.o mark.o \
X	regexp.o regsub.o \
X	screen.o fileio.o param.o undo.o version.o
X
Xall : stevie
X
Xstevie : $(OBJ) $(MACH)
X	$(CC) $(LDFLAGS) $(OBJ) $(MACH) -o stevie
X	chmem =150000 stevie
X
Xclean :
X	rm $(OBJ) $(MACH)
END_OF_FILE
if test 393 -ne `wc -c <'makefile.min'`; then
    echo shar: \"'makefile.min'\" unpacked with wrong size!
fi
# end of 'makefile.min'
fi
if test -f 'makefile.os2' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'makefile.os2'\"
else
echo shar: Extracting \"'makefile.os2'\" \(1451 characters\)
sed "s/^X//" >'makefile.os2' <<'END_OF_FILE'
X#
X# Makefile for OS/2
X#
X# The make command with OS/2 is really stupid.
X#
X
X#
X# Compact model lets us edit large files, but keep small model code
X#
XMODEL= /AC
XCFLAGS = $(MODEL)
X
XMACH=	os2.obj
X
XOBJ=	main.obj edit.obj linefunc.obj normal.obj cmdline.obj hexchars.obj \
X	misccmds.obj help.obj ptrfunc.obj search.obj alloc.obj mark.obj \
X	screen.obj fileio.obj param.obj undo.obj version.obj $(MACH)
X
Xmain.obj:	main.c
X	cl -c $(CFLAGS) main.c
X
Xalloc.obj : alloc.c
X	cl -c $(CFLAGS) alloc.c
X
Xedit.obj : edit.c
X	cl -c $(CFLAGS) edit.c
X
Xlinefunc.obj : linefunc.c
X	cl -c $(CFLAGS) linefunc.c
X
Xnormal.obj : normal.c
X	cl -c $(CFLAGS) normal.c
X
Xcmdline.obj : cmdline.c
X	cl -c $(CFLAGS) cmdline.c
X
Xhexchars.obj : hexchars.c
X	cl -c $(CFLAGS) hexchars.c
X
Xmisccmds.obj : misccmds.c
X	cl -c $(CFLAGS) misccmds.c
X
Xhelp.obj : help.c
X	cl -c $(CFLAGS) help.c
X
Xptrfunc.obj : ptrfunc.c
X	cl -c $(CFLAGS) ptrfunc.c
X
Xsearch.obj : search.c
X	cl -c $(CFLAGS) search.c
X
Xmark.obj : mark.c
X	cl -c $(CFLAGS) mark.c
X
Xscreen.obj : screen.c
X	cl -c $(CFLAGS) screen.c
X
Xfileio.obj : fileio.c
X	cl -c $(CFLAGS) fileio.c
X
Xparam.obj : param.c
X	cl -c $(CFLAGS) param.c
X
Xregexp.obj : regexp.c
X	cl -c $(CFLAGS) regexp.c
X
Xregsub.obj : regsub.c
X	cl -c $(CFLAGS) regsub.c
X
Xos2.obj : os2.c
X	cl -c $(CFLAGS) os2.c
X
Xundo.obj : undo.c
X	cl -c $(CFLAGS) undo.c
X
Xversion.obj : version.c
X	cl -c $(CFLAGS) version.c
X
Xstevie.exe : $(OBJ)
X	cl $(MODEL) *.obj \pmsdk\lib\setargv.obj -o stevie.exe /F 6000 -link /NOE
END_OF_FILE
if test 1451 -ne `wc -c <'makefile.os2'`; then
    echo shar: \"'makefile.os2'\" unpacked with wrong size!
fi
# end of 'makefile.os2'
fi
if test -f 'makefile.tos' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'makefile.tos'\"
else
echo shar: Extracting \"'makefile.tos'\" \(418 characters\)
sed "s/^X//" >'makefile.tos' <<'END_OF_FILE'
X#
X# Makefile for the Atari ST - Sozobon C Compiler
X#
X
XCFLAGS = -O
X
X.c.o:
X	$(CC) -c $(CFLAGS) $<
X	ar rv vi.lib $*.o
X
XMACH =	tos.o
X
XOBJ =	main.o edit.o linefunc.o normal.o cmdline.o hexchars.o \
X	misccmds.o help.o ptrfunc.o search.o alloc.o mark.o \
X	regexp.o regsub.o \
X	screen.o fileio.o param.o undo.o version.o $(MACH)
X
Xall : stevie.ttp
X
Xstevie.ttp : $(OBJ)
X	$(CC) vi.lib -o stevie.ttp
X
Xclean :
X	$(RM) $(OBJ) vi.lib
END_OF_FILE
if test 418 -ne `wc -c <'makefile.tos'`; then
    echo shar: \"'makefile.tos'\" unpacked with wrong size!
fi
# end of 'makefile.tos'
fi
if test -f 'makefile.usg' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'makefile.usg'\"
else
echo shar: Extracting \"'makefile.usg'\" \(499 characters\)
sed "s/^X//" >'makefile.usg' <<'END_OF_FILE'
X#
X# Makefile for UNIX (System V)
X#
X
XLDFLAGS=
XCFLAGS= -g
X
XMACH=	unix.o
X
XOBJ=	main.o edit.o linefunc.o normal.o cmdline.o hexchars.o \
X	misccmds.o help.o ptrfunc.o search.o alloc.o mark.o \
X	regexp.o regsub.o \
X	screen.o fileio.o param.o undo.o version.o term.o
X
Xall : stevie stevie.doc
X
Xstevie : $(OBJ) $(MACH)
X	$(CC) $(LDFLAGS) $(OBJ) $(MACH) -o stevie -lcurses
X
Xlint :
X	lint $(OBJ:.o=.c) $(MACH:.o=.c)
X
Xstevie.doc : stevie.mm
X	nroff -rB1 -Tlp -mm stevie.mm > stevie.doc
X
Xclean :
X	rm $(OBJ) $(MACH)
END_OF_FILE
if test 499 -ne `wc -c <'makefile.usg'`; then
    echo shar: \"'makefile.usg'\" unpacked with wrong size!
fi
# end of 'makefile.usg'
fi
if test -f 'mark.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'mark.c'\"
else
echo shar: Extracting \"'mark.c'\" \(2315 characters\)
sed "s/^X//" >'mark.c' <<'END_OF_FILE'
Xstatic	char	RCSid[] =
X"$Header: mark.c,v 1.2 88/10/27 07:54:45 tony Exp $";
X
X/*
X * Routines to save and retrieve marks.
X *
X * $Log:	mark.c,v $
X * Revision 1.2  88/10/27  07:54:45  tony
X * Removed support for Megamax.
X * 
X * Revision 1.1  88/03/20  21:08:32  tony
X * Initial revision
X * 
X *
X */
X
X#include "stevie.h"
X
X#define	NMARKS	10		/* max. # of marks that can be saved */
X
Xstruct	mark {
X	char	name;
X	LPTR	pos;
X};
X
Xstatic	struct	mark	mlist[NMARKS];
Xstatic	struct	mark	pcmark;		/* previous context mark */
Xstatic	bool_t	pcvalid = FALSE;	/* true if pcmark is valid */
X
X/*
X * setmark(c) - set mark 'c' at current cursor position
X *
X * Returns TRUE on success, FALSE if no room for mark or bad name given.
X */
Xbool_t
Xsetmark(c)
Xchar	c;
X{
X	int	i;
X
X	if (!isalpha(c))
X		return FALSE;
X
X	/*
X	 * If there is already a mark of this name, then just use the
X	 * existing mark entry.
X	 */
X	for (i=0; i < NMARKS ;i++) {
X		if (mlist[i].name == c) {
X			mlist[i].pos = *Curschar;
X			return TRUE;
X		}
X	}
X
X	/*
X	 * There wasn't a mark of the given name, so find a free slot
X	 */
X	for (i=0; i < NMARKS ;i++) {
X		if (mlist[i].name == NUL) {	/* got a free one */
X			mlist[i].name = c;
X			mlist[i].pos = *Curschar;
X			return TRUE;
X		}
X	}
X	return FALSE;
X}
X
X/*
X * setpcmark() - set the previous context mark to the current position
X */
Xvoid
Xsetpcmark()
X{
X	pcmark.pos = *Curschar;
X	pcvalid = TRUE;
X}
X
X/*
X * getmark(c) - find mark for char 'c'
X *
X * Return pointer to LPTR or NULL if no such mark.
X */
XLPTR *
Xgetmark(c)
Xchar	c;
X{
X	register int	i;
X
X	if (c == '\'' || c == '`')	/* previous context mark */
X		return pcvalid ? &(pcmark.pos) : (LPTR *) NULL;
X
X	for (i=0; i < NMARKS ;i++) {
X		if (mlist[i].name == c)
X			return &(mlist[i].pos);
X	}
X	return (LPTR *) NULL;
X}
X
X/*
X * clrall() - clear all marks
X *
X * Used mainly when trashing the entire buffer during ":e" type commands
X */
Xvoid
Xclrall()
X{
X	register int	i;
X
X	for (i=0; i < NMARKS ;i++)
X		mlist[i].name = NUL;
X	pcvalid = FALSE;
X}
X
X/*
X * clrmark(line) - clear any marks for 'line'
X *
X * Used any time a line is deleted so we don't have marks pointing to
X * non-existent lines.
X */
Xvoid
Xclrmark(line)
XLINE	*line;
X{
X	register int	i;
X
X	for (i=0; i < NMARKS ;i++) {
X		if (mlist[i].pos.linep == line)
X			mlist[i].name = NUL;
X	}
X	if (pcvalid && (pcmark.pos.linep == line))
X		pcvalid = FALSE;
X}
END_OF_FILE
if test 2315 -ne `wc -c <'mark.c'`; then
    echo shar: \"'mark.c'\" unpacked with wrong size!
fi
# end of 'mark.c'
fi
if test -f 'minix.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'minix.c'\"
else
echo shar: Extracting \"'minix.c'\" \(3974 characters\)
sed "s/^X//" >'minix.c' <<'END_OF_FILE'
Xstatic	char	RCSid[] =
X"$Header: minix.c,v 1.3 88/10/29 14:07:36 tony Exp $";
X
X/*
X * System-dependent routines for Minix-ST
X *
X * modifications by:  Robert Regn	   rtregn@faui32.uucp
X *
X * $Log:	minix.c,v $
X * Revision 1.3  88/10/29  14:07:36  tony
X * Added optional support for termcap.
X * 
X * Revision 1.2  88/10/27  08:20:12  tony
X * Added doshell() (same as the unix version), and a no-op version of fixname().
X * 
X * Revision 1.1  88/10/25  20:14:00  tony
X * Initial revision
X * 
X *
X */
X
X#include "stevie.h"
X#include <sgtty.h>
X
X/*
X * inchar() - get a character from the keyboard
X */
Xint
Xinchar()
X{
X	char	c[4];
X	short n;
X
X	flushbuf();		/* flush any pending output */
X
X	while ( (n=read(0, c, 3)) < 1)
X		;
X	if (n > 1) {
X		if (n == 3 && c[0] == 033 && c[1] == '[')  /*  cursor button */
X			switch (c[2]) {
X				case 'A':	return K_UARROW;
X				case 'B':	return K_DARROW;
X				case 'C':	return K_RARROW;
X				case 'D':	return K_LARROW;
X				default: /* .. stuffin */ ;
X			}
X		c[n] = NUL;
X		stuffin ( c+1);
X		}
X
X	return *c;
X}
X
X#define	BSIZE	2048
Xstatic	char	outbuf[BSIZE];
Xstatic	int	bpos = 0;
X
Xvoid
Xflushbuf()
X{
X	if (bpos != 0)
X		write(1, outbuf, bpos);
X	bpos = 0;
X}
X
X/*
X * Macro to output a character. Used within this file for speed.
X */
X#define	outone(c)	outbuf[bpos++] = c; if (bpos >= BSIZE) flushbuf()
X
X/*
X * Function version for use outside this file.
X */
Xvoid
Xoutchar(c)
Xregister char	c;
X{
X	outbuf[bpos++] = c;
X	if (bpos >= BSIZE)
X		flushbuf();
X}
X
Xvoid
Xoutstr(s)
Xregister char	*s;
X{
X	while (*s) {
X		outone(*s++);
X	}
X}
X
Xvoid
Xbeep()
X{
X	outone('\007');
X}
X
X/*
X * remove(file) - remove a file
X */
Xvoid
Xremove(file)
Xchar *file;
X{
X	unlink(file);
X}
X
X/*
X * rename(of, nf) - rename existing file 'of' to 'nf'
X */
Xvoid
Xrename(of, nf)
Xchar	*of, *nf;
X{
X	unlink(nf);
X	link(of, nf);
X	unlink(of);
X}
X
Xvoid
Xdelay()
X{
X	/* not implemented */
X}
X
Xstatic	struct	sgttyb	ostate;
X
X/*
X * Go into cbreak mode
X */
Xvoid
Xset_tty()
X{
X	struct	sgttyb	nstate;
X
X	 ioctl(0, TIOCGETP, &ostate);
X	 nstate = ostate;
X	 nstate.sg_flags &= ~(XTABS|ECHO);
X	 nstate.sg_flags |= CBREAK;
X	 ioctl(0, TIOCSETP, &nstate);
X}
X
X/*
X * Restore original terminal modes
X */
Xvoid
Xreset_tty()
X{
X	ioctl(0, TIOCSETP, &ostate);
X}
X
Xvoid
Xwindinit()
X{
X#ifdef	TERMCAP
X	if (t_init() != 1) {
X		fprintf(stderr, "unknown terminal type\n");
X		exit(1);
X	}
X#else
X
X	char	*term, *getenv();
X	if ((term = getenv("TERM")) == NULL || strcmp(term, "minix") != 0) {
X		fprintf(stderr, "Invalid terminal type '%s'\n", term);
X		exit(1);
X	}
X	Columns = 80;
X	P(P_LI) = Rows = 25;
X#endif
X
X	set_tty();
X}
X
Xvoid
Xwindexit(r)
Xint r;
X{
X	flushbuf();
X	reset_tty();
X	exit(r);
X}
X
X#define	outone(c)	outbuf[bpos++] = c; if (bpos >= BSIZE) flushbuf()
X
Xvoid
Xwindgoto(r, c)
Xregister int	r, c;
X{
X#ifdef	TERMCAP
X	char	*tgoto();
X#else
X	r += 1;
X	c += 1;
X#endif
X
X	/*
X	 * Check for overflow once, to save time.
X	 */
X	if (bpos + 8 >= BSIZE)
X		flushbuf();
X
X#ifdef	TERMCAP
X	outstr(tgoto(T_CM, c, r));
X#else
X	outbuf[bpos++] = '\033';
X	outbuf[bpos++] = '[';
X	if (r >= 10)
X		outbuf[bpos++] = r/10 + '0';
X	outbuf[bpos++] = r%10 + '0';
X	outbuf[bpos++] = ';';
X	if (c >= 10)
X		outbuf[bpos++] = c/10 + '0';
X	outbuf[bpos++] = c%10 + '0';
X	outbuf[bpos++] = 'H';
X#endif
X}
X
XFILE *
Xfopenb(fname, mode)
Xchar	*fname;
Xchar	*mode;
X{
X	return fopen(fname, mode);
X}
X
Xchar *
Xstrchr(s, c)
Xchar	*s;
Xchar	c;
X{
X	char *index();
X
X	return index(s, c);
X}
X
Xchar *
Xfixname(s)
Xchar	*s;
X{
X	return s;
X}
X
X/*
X * doshell() - run a command or an interactive shell
X */
Xvoid
Xdoshell(cmd)
Xchar	*cmd;
X{
X	char	*cp, *getenv();
X	char	cline[128];
X
X	outstr("\r\n");
X	flushbuf();
X	reset_tty();
X
X	if (cmd == NULL 	/* don't say  sh sh  */
X	 || *cmd == NUL ) {	/* handle :!<return> */
X		if ((cmd = getenv("SHELL")) == NULL)
X			cmd = "/bin/sh";
X		switch (fork() ) {
X			case	0:	
X					execl(cmd, "sh", "-i", 0);
X					emsg("exec failed - ");
X					exit(1);
X
X			case	-1:	emsg("fork failed - ");
X					break;
X
X			default:	wait(0);
X		}
X
X	}
X
X	else	if (system(cmd) == -1)
X		outstr("execution of command failed - ");
X	set_tty();
X
X	wait_return();
X}
END_OF_FILE
if test 3974 -ne `wc -c <'minix.c'`; then
    echo shar: \"'minix.c'\" unpacked with wrong size!
fi
# end of 'minix.c'
fi
if test -f 'misccmds.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'misccmds.c'\"
else
echo shar: Extracting \"'misccmds.c'\" \(11025 characters\)
sed "s/^X//" >'misccmds.c' <<'END_OF_FILE'
Xstatic	char	RCSid[] =
X"$Header: misccmds.c,v 1.9 88/09/06 06:50:42 tony Exp $";
X
X/*
X * Various routines to perform specific editing operations or return
X * useful information about the file.
X *
X * $Log:	misccmds.c,v $
X * Revision 1.9  88/09/06  06:50:42  tony
X * Fixed a bug with shifts that was introduced when replace mode was added.
X * 
X * Revision 1.8  88/08/30  20:36:51  tony
X * After much prodding from Mark, I finally added support for replace mode.
X * 
X * Revision 1.7  88/08/26  08:45:18  tony
X * Misc. changes to make lint happy.
X * 
X * Revision 1.6  88/06/26  14:50:19  tony
X * Added a new parameter to delline() to control whether screen updates
X * may be performed while deleting the line(s).
X * 
X * Revision 1.5  88/05/02  07:34:46  tony
X * Fixed a bug in the last change to plines(). The column counter wasn't
X * being initialized to zero, so plines() returned garbage.
X * 
X * Revision 1.4  88/05/01  20:09:13  tony
X * Modified plines() to support the new "number" parameter.
X * 
X * Revision 1.3  88/04/30  20:00:04  tony
X * Added code to openfwd() and openbwd() to support auto-indent.
X * 
X * Revision 1.2  88/04/23  20:39:21  tony
X * Reworked openfwd() to avoid a problem when opening a line at the end of
X * the file when on the last line of the screen. Opening a line now inserts
X * or scrolls appropriately and deals with opens correctly when the cursor
X * is on a long line.
X * 
X * Revision 1.1  88/03/20  21:08:41  tony
X * Initial revision
X * 
X *
X */
X
X#include "stevie.h"
X
Xstatic	void	openfwd(), openbwd();
X
Xextern	bool_t	did_ai;
X
X/*
X * opencmd
X *
X * Add a blank line above or below the current line.
X */
X
Xvoid
Xopencmd(dir, can_ai)
Xint	dir;
Xint	can_ai;			/* if true, consider auto-indent */
X{
X	if (dir == FORWARD)
X		openfwd(can_ai);
X	else
X		openbwd(can_ai);
X}
X
Xstatic void
Xopenfwd(can_ai)
Xint	can_ai;
X{
X	LINE	*l;
X	LPTR	*next;
X	char	*s;		/* string to be moved to new line, if any */
X	int	newindex = 0;	/* index of the cursor on the new line */
X
X	/*
X	 * If we're in insert mode, we need to move the remainder of the
X	 * current line onto the new line. Otherwise the new line is left
X	 * blank.
X	 */
X	if (State == INSERT || State == REPLACE)
X		s = &Curschar->linep->s[Curschar->index];
X	else
X		s = "";
X
X	if ((next = nextline(Curschar)) == NULL)	/* open on last line */
X		next = Fileend;
X
X	/*
X	 * By asking for as much space as the prior line had we make sure
X	 * that we'll have enough space for any auto-indenting.
X	 */
X	if ((l = newline(strlen(Curschar->linep->s) + SLOP)) == NULL)
X		return;
X
X	if (*s != NUL)
X		strcpy(l->s, s);		/* copy string to new line */
X
X	else if (can_ai && P(P_AI) && !anyinput()) {
X		char	*p;
X
X		/*
X		 * Copy prior line, and truncate after white space
X		 */
X		strcpy(l->s, Curschar->linep->s);
X
X		for (p = l->s; *p == ' ' || *p == TAB ;p++)
X			;
X		*p = NUL;
X		newindex = p - l->s;
X
X		/*
X		 * If we just did an auto-indent, then we didn't type
X		 * anything on the prior line, and it should be truncated.
X		 */
X		if (did_ai)
X			Curschar->linep->s[0] = NUL;
X
X		did_ai = TRUE;
X	}
X
X	/* truncate current line at cursor */
X	if (State == INSERT || State == REPLACE)
X		*s = NUL;
X			
X
X	Curschar->linep->next = l;	/* link neighbors to new line */
X	next->linep->prev = l;
X
X	l->prev = Curschar->linep;	/* link new line to neighbors */
X	l->next = next->linep;
X
X	if (next == Fileend)			/* new line at end */
X		l->num = Curschar->linep->num + LINEINC;
X
X	else if ((l->prev->num) + 1 == l->next->num)	/* no gap, renumber */
X		renum();
X
X	else {					/* stick it in the middle */
X		unsigned long	lnum;
X		lnum = ((long)l->prev->num + (long)l->next->num) / 2;
X		l->num = lnum;
X	}
X
X	/*
X	 * Get the cursor to the start of the line, so that 'Cursrow'
X	 * gets set to the right physical line number for the stuff
X	 * that follows...
X	 */
X	Curschar->index = 0;
X	cursupdate();
X
X	/*
X	 * If we're doing an open on the last logical line, then
X	 * go ahead and scroll the screen up. Otherwise, just insert
X	 * a blank line at the right place. We use calls to plines()
X	 * in case the cursor is resting on a long line.
X	 */
X	if (Cursrow + plines(Curschar) == (Rows - 1))
X		scrollup(1);
X	else
X		s_ins(Cursrow+plines(Curschar), 1);
X
X	*Curschar = *nextline(Curschar);	/* cursor moves down */
X	Curschar->index = newindex;
X
X	updatescreen();		/* because Botchar is now invalid... */
X
X	cursupdate();		/* update Cursrow before insert */
X}
X
Xstatic void
Xopenbwd(can_ai)
Xint	can_ai;
X{
X	LINE	*l;
X	LINE	*prev;
X	int	newindex = 0;
X
X	prev = Curschar->linep->prev;
X
X	if ((l = newline(strlen(Curschar->linep->s) + SLOP)) == NULL)
X		return;
X
X	Curschar->linep->prev = l;	/* link neighbors to new line */
X	prev->next = l;
X
X	l->next = Curschar->linep;	/* link new line to neighbors */
X	l->prev = prev;
X
X	if (can_ai && P(P_AI) && !anyinput()) {
X		char	*p;
X
X		/*
X		 * Copy current line, and truncate after white space
X		 */
X		strcpy(l->s, Curschar->linep->s);
X
X		for (p = l->s; *p == ' ' || *p == TAB ;p++)
X			;
X		*p = NUL;
X		newindex = p - l->s;
X
X		did_ai = TRUE;
X	}
X
X	Curschar->linep = Curschar->linep->prev;
X	Curschar->index = newindex;
X
X	if (prev == Filetop->linep)		/* new start of file */
X		Filemem->linep = l;
X
X	renum();	/* keep it simple - we don't do this often */
X
X	cursupdate();			/* update Cursrow before insert */
X	if (Cursrow != 0)
X		s_ins(Cursrow, 1);		/* insert a physical line */
X
X	updatescreen();
X}
X
Xint
Xcntllines(pbegin,pend)
XLPTR *pbegin, *pend;
X{
X	LINE *lp;
X	int lnum = 1;
X
X	for (lp = pbegin->linep; lp != pend->linep ;lp = lp->next)
X		lnum++;
X
X	return(lnum);
X}
X
X/*
X * plines(p) - return the number of physical screen lines taken by line 'p'
X */
Xint
Xplines(p)
XLPTR	*p;
X{
X	register int	col = 0;
X	register char	*s;
X
X	s = p->linep->s;
X
X	if (*s == NUL)		/* empty line */
X		return 1;
X
X	for (; *s != NUL ;s++) {
X		if ( *s == TAB && !P(P_LS))
X			col += P(P_TS) - (col % P(P_TS));
X		else
X			col += chars[(unsigned)(*s & 0xff)].ch_size;
X	}
X
X	/*
X	 * If list mode is on, then the '$' at the end of
X	 * the line takes up one extra column.
X	 */
X	if (P(P_LS))
X		col += 1;
X	/*
X	 * If 'number' mode is on, add another 8.
X	 */
X	if (P(P_NU))
X		col += 8;
X
X	return ((col + (Columns-1)) / Columns);
X}
X
Xvoid
Xfileinfo()
X{
X	long	l1, l2;
X	char	buf[80];
X
X	if (bufempty()) {
X		l1 = 0;
X		l2 = 1;	/* sonst div by zero */
X	}
X	else {
X		l1 = cntllines(Filemem, Curschar);
X		l2 = cntllines(Filemem, Fileend) - 1;
X	}
X
X	l1 = cntllines(Filemem, Curschar);
X	l2 = cntllines(Filemem, Fileend) - 1;
X	sprintf(buf, "\"%s\"%s line %ld of %ld -- %ld %% --",
X		(Filename != NULL) ? Filename : "No File",
X		Changed ? " [Modified]" : "",
X		l1, l2, (l1 * 100)/l2);
X	msg(buf);
X}
X
X/*
X * gotoline(n) - return a pointer to line 'n'
X *
X * Returns a pointer to the last line of the file if n is zero, or
X * beyond the end of the file.
X */
XLPTR *
Xgotoline(n)
Xint n;
X{
X	static	LPTR	l;
X
X	l.index = 0;
X
X	if ( n == 0 )
X		l = *prevline(Fileend);
X	else {
X		LPTR	*p;
X
X		for (l = *Filemem; --n > 0 ;l = *p)
X			if ((p = nextline(&l)) == NULL)
X				break;
X	}
X	return &l;
X}
X
Xvoid
Xinschar(c)
Xint	c;
X{
X	register char	*p, *pend;
X
X	/* make room for the new char. */
X	if ( ! canincrease(1) )
X		return;
X
X	if (State != REPLACE) {
X		p = &Curschar->linep->s[strlen(Curschar->linep->s) + 1];
X		pend = &Curschar->linep->s[Curschar->index];
X
X		for (; p > pend ;p--)
X			*p = *(p-1);
X
X		*p = c;
X
X	} else {	/* replace mode */
X		/*
X		 * Once we reach the end of the line, we are effectively
X		 * inserting new text, so make sure the string terminator
X		 * stays out there.
X		 */
X		if (gchar(Curschar) == NUL)
X			Curschar->linep->s[Curschar->index+1] = NUL;
X		pchar(Curschar, c);
X	}
X
X	/*
X	 * If we're in insert mode and showmatch mode is set, then
X	 * check for right parens and braces. If there isn't a match,
X	 * then beep. If there is a match AND it's on the screen, then
X	 * flash to it briefly. If it isn't on the screen, don't do anything.
X	 */
X	if (P(P_SM) && State == INSERT && (c == ')' || c == '}' || c == ']')) {
X		LPTR	*lpos, csave;
X
X		if ((lpos = showmatch()) == NULL)	/* no match, so beep */
X			beep();
X		else if (LINEOF(lpos) >= LINEOF(Topchar)) {
X			updatescreen();		/* show the new char first */
X			csave = *Curschar;
X			*Curschar = *lpos;	/* move to matching char */
X			cursupdate();
X			windgoto(Cursrow, Curscol);
X			delay();		/* brief pause */
X			*Curschar = csave;	/* restore cursor position */
X			cursupdate();
X		}
X	}
X
X	inc(Curschar);
X	CHANGED;
X}
X
X#if 0
Xvoid
Xinsstr(s)
Xregister char *s;
X{
X	register char *p, *endp;
X	register int k, n = strlen(s);
X
X	/* Move everything in the file over to make */
X	/* room for the new string. */
X	if (!canincrease(n))
X		return;
X
X	endp = &Curschar->linep->s[Curschar->index];
X	p = Curschar->linep->s + strlen(Curschar->linep->s) + 1 + n;
X
X	for (; p>endp ;p--)
X		*p = *(p-n);
X
X	p = &Curschar->linep->s[Curschar->index];
X	for ( k=0; k<n; k++ ) {
X		*p++ = *s++;
X		inc(Curschar);
X	}
X	CHANGED;
X}
X#endif
X
Xbool_t
Xdelchar(fixpos)
Xbool_t	fixpos;		/* if TRUE, fix the cursor position when done */
X{
X	register int i;
X
X	/* Check for degenerate case; there's nothing in the file. */
X	if (bufempty())
X		return FALSE;
X
X	if (lineempty())	/* can't do anything */
X		return FALSE;
X
X	/* Delete the char. at Curschar by shifting everything */
X	/* in the line down. */
X	for ( i=Curschar->index+1; i < Curschar->linep->size ;i++)
X		Curschar->linep->s[i-1] = Curschar->linep->s[i];
X
X	/* If we just took off the last character of a non-blank line, */
X	/* we don't want to end up positioned at the newline. */
X	if (fixpos) {
X		if (gchar(Curschar)==NUL && Curschar->index>0 && State!=INSERT)
X			Curschar->index--;
X	}
X	CHANGED;
X
X	return TRUE;
X}
X
X
Xvoid
Xdelline(nlines, can_update)
Xint	nlines;
Xbool_t	can_update;
X{
X	register LINE *p, *q;
X	int	doscreen;		/* if true, update the screen */
X
X	doscreen = can_update;
X	/*
X	 * There's no point in keeping the screen updated if we're
X	 * deleting more than a screen's worth of lines.
X	 */
X	if (nlines > (Rows - 1) && can_update) {
X		doscreen = FALSE;
X		s_del(Cursrow, Rows-1);	/* flaky way to clear rest of screen */
X	}
X
X	while ( nlines-- > 0 ) {
X
X		if (bufempty())			/* nothing to delete */
X			break;
X
X		if (buf1line()) {		/* just clear the line */
X			Curschar->linep->s[0] = NUL;
X			Curschar->index = 0;
X			break;
X		}
X
X		p = Curschar->linep->prev;
X		q = Curschar->linep->next;
X
X		if (p == Filetop->linep) {	/* first line of file so... */
X			Filemem->linep = q;	/* adjust start of file */
X			Topchar->linep = q;	/* and screen */
X		}
X		p->next = q;
X		q->prev = p;
X
X		clrmark(Curschar->linep);	/* clear marks for the line */
X
X		/*
X		 * Delete the correct number of physical lines on the screen
X		 */
X		if (doscreen)
X			s_del(Cursrow, plines(Curschar));
X
X		/*
X		 * If deleting the top line on the screen, adjust Topchar
X		 */
X		if (Topchar->linep == Curschar->linep)
X			Topchar->linep = q;
X
X		free(Curschar->linep->s);
X		free(Curschar->linep);
X
X		Curschar->linep = q;
X		Curschar->index = 0;		/* is this right? */
X		CHANGED;
X
X		/* If we delete the last line in the file, back up */
X		if ( Curschar->linep == Fileend->linep) {
X			Curschar->linep = Curschar->linep->prev;
X			/* and don't try to delete any more lines */
X			break;
X		}
X	}
X}
END_OF_FILE
if test 11025 -ne `wc -c <'misccmds.c'`; then
    echo shar: \"'misccmds.c'\" unpacked with wrong size!
fi
# end of 'misccmds.c'
fi
if test -f 'normal.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'normal.c'\"
else
echo shar: Extracting \"'normal.c'\" \(28721 characters\)
sed "s/^X//" >'normal.c' <<'END_OF_FILE'
Xstatic	char	RCSid[] =
X"$Header: normal.c,v 1.12 88/11/01 21:32:55 tony Exp $";
X
X/*
X * Contains the main routine for processing characters in
X * command mode as well as routines for handling the operators.
X *
X * $Log:	normal.c,v $
X * Revision 1.12  88/11/01  21:32:55  tony
X * Improved the 'put' code. It now modifies the buffer directly instead
X * of stuffing things into the input buffer. This is MUCH faster. The
X * yank buffer is still statically allocated, but this can be easily
X * changed now.
X * 
X * Revision 1.11  88/10/27  08:13:39  tony
X * Made the replace command more robust.
X * 
X * Revision 1.10  88/09/16  08:37:05  tony
X * No longer beeps when repeated searches fail.
X * 
X * Revision 1.9  88/08/30  20:36:57  tony
X * After much prodding from Mark, I finally added support for replace mode.
X * 
X * Revision 1.8  88/08/26  13:46:05  tony
X * Added support for the '!' (filter) operator.
X * 
X * Revision 1.7  88/08/26  08:45:24  tony
X * Misc. changes to make lint happy.
X * 
X * Revision 1.6  88/07/09  20:38:20  tony
X * Added support code for the 'U' command.
X * 
X * Revision 1.5  88/06/28  07:51:09  tony
X * Fixed a bug involving redo's of the '~' command. The redo would just
X * repeat the replacement last performed instead of switching the case of
X * the current character.
X * 
X * Revision 1.4  88/06/26  14:49:45  tony
X * Modified calls to delline() for the addition of a new parameter.
X * 
X * Revision 1.3  88/05/04  08:28:09  tony
X * Fixed a minor bug with the 'G' command. It now goes to the first
X * non-white character on the destination line.
X * 
X * Revision 1.2  88/04/29  14:47:21  tony
X * Fixed up several motion commands to clear any pending operator if the
X * motion command failed. This fixes several bugs where commands like
X * "dtx" would fail, but still perform the indicated operation.
X * 
X * Revision 1.1  88/03/20  21:09:09  tony
X * Initial revision
X * 
X *
X */
X
X#include "stevie.h"
X
Xstatic	void	doshift(), dodelete(), doput(), dochange(), dofilter();
Xstatic	void	tabinout(), startinsert();
Xstatic	bool_t	dojoin(), doyank();
X
X/*
X * Macro evaluates true if char 'c' is a valid identifier character
X */
X#define	IDCHAR(c)	(isalpha(c) || isdigit(c) || (c) == '_')
X
X/*
X * Operators
X */
X#define	NOP	0		/* no pending operation */
X#define	DELETE	1
X#define	YANK	2
X#define	CHANGE	3
X#define	LSHIFT	4
X#define	RSHIFT	5
X#define	FILTER	6
X
X#define	CLEAROP	(operator = NOP)	/* clear any pending operator */
X
Xstatic	int	operator = NOP;		/* current pending operator */
X
X/*
X * When a cursor motion command is made, it is marked as being a character
X * or line oriented motion. Then, if an operator is in effect, the operation
X * becomes character or line oriented accordingly.
X *
X * Character motions are marked as being inclusive or not. Most char.
X * motions are inclusive, but some (e.g. 'w') are not.
X *
X * Generally speaking, every command in normal() should either clear any
X * pending operator (with CLEAROP), or set the motion type variable.
X */
X
X/*
X * Motion types
X */
X#define	MBAD	(-1)		/* 'bad' motion type marks unusable yank buf */
X#define	MCHAR	0
X#define	MLINE	1
X
Xstatic	int	mtype;			/* type of the current cursor motion */
Xstatic	bool_t	mincl;			/* true if char motion is inclusive */
X
Xstatic	LPTR	startop;		/* cursor pos. at start of operator */
X
X/*
X * Operators can have counts either before the operator, or between the
X * operator and the following cursor motion as in:
X *
X *	d3w or 3dw
X *
X * If a count is given before the operator, it is saved in opnum. If
X * normal() is called with a pending operator, the count in opnum (if
X * present) overrides any count that came later.
X */
Xstatic	int	opnum = 0;
X
X
X#define	DEFAULT1(x)	(((x) == 0) ? 1 : (x))
X
X/*
X * normal
X *
X * Execute a command in normal mode.
X */
X
Xvoid
Xnormal(c)
Xint c;
X{
X	int n;
X	bool_t flag = FALSE;
X	int type = 0;		/* used in some operations to modify type */
X	int dir = FORWARD;	/* search direction */
X	int nchar = NUL;
X	bool_t finish_op;
X
X	/*
X	 * If there is an operator pending, then the command we take
X	 * this time will terminate it. Finish_op tells us to finish
X	 * the operation before returning this time (unless the operation
X	 * was cancelled.
X	 */
X	finish_op = (operator != NOP);
X
X	/*
X	 * If we're in the middle of an operator AND we had a count before
X	 * the operator, then that count overrides the current value of
X	 * Prenum. What this means effectively, is that commands like
X	 * "3dw" get turned into "d3w" which makes things fall into place
X	 * pretty neatly.
X	 */
X	if (finish_op) {
X		if (opnum != 0)
X			Prenum = opnum;
X	} else
X		opnum = 0;
X
X	u_lcheck();	/* clear the "line undo" buffer if we've moved */
X
X	switch(c & 0xff){
X
X	case K_HELP:
X		CLEAROP;
X		if (help()) {
X			screenclear();
X			updatescreen();
X		}
X		break;
X
X	case CTRL('L'):
X		CLEAROP;
X		screenclear();
X		updatescreen();
X		break;
X
X	case CTRL('D'):
X		CLEAROP;
X		if (Prenum)
X			P(P_SS) = (Prenum > Rows-1) ? Rows-1 : Prenum;
X		scrollup(P(P_SS));
X		onedown(P(P_SS));
X		updatescreen();
X		break;
X
X	case CTRL('U'):
X		CLEAROP;
X		if (Prenum)
X			P(P_SS) = (Prenum > Rows-1) ? Rows-1 : Prenum;
X		scrolldown(P(P_SS));
X		oneup(P(P_SS));
X		updatescreen();
X		break;
X
X	/*
X	 * ^F and ^B are neat hacks, but don't take counts. This is very
X	 * code-efficient, and does the right thing. I'll fix it later
X	 * to take a count. The old code took a count, but didn't do the
X	 * right thing in other respects (e.g. leaving some context).
X	 */
X	case CTRL('F'):
X#if 1
X		screenclear();
X		stuffin("Lz\nM");
X#else
X		/*
X		 * Old code
X		 */
X		CLEAROP;
X		n = DEFAULT1(Prenum);
X		if ( ! onedown(Rows * n) )
X			beep();
X		cursupdate();
X#endif
X		break;
X
X	case CTRL('B'):
X#if 1
X		screenclear();
X		stuffin("Hz-M");
X#else
X		/*
X		 * Old code
X		 */
X		CLEAROP;
X		n = DEFAULT1(Prenum);
X		if ( ! oneup(Rows * n) )
X			beep();
X		cursupdate();
X#endif
X		break;
X
X	case CTRL('E'):
X		CLEAROP;
X		scrollup(DEFAULT1(Prenum));
X		updatescreen();
X		break;
X
X	case CTRL('Y'):
X		CLEAROP;
X		scrolldown(DEFAULT1(Prenum));
X		updatescreen();
X		break;
X
X	case 'z':
X		CLEAROP;
X		switch (vgetc()) {
X		case NL:		/* put Curschar at top of screen */
X		case CR:
X			*Topchar = *Curschar;
X			Topchar->index = 0;
X			updatescreen();
X			break;
X
X		case '.':		/* put Curschar in middle of screen */
X			n = Rows/2;
X			goto dozcmd;
X
X		case '-':		/* put Curschar at bottom of screen */
X			n = Rows-1;
X			/* fall through */
X
X		dozcmd:
X			{
X				register LPTR	*lp = Curschar;
X				register int	l = 0;
X
X				while ((l < n) && (lp != NULL)) {
X					l += plines(lp);
X					*Topchar = *lp;
X					lp = prevline(lp);
X				}
X			}
X			Topchar->index = 0;
X			updatescreen();
X			break;
X
X		default:
X			beep();
X		}
X		break;
X
X	case CTRL('G'):
X		CLEAROP;
X		fileinfo();
X		break;
X
X	case 'G':
X		mtype = MLINE;
X		*Curschar = *gotoline(Prenum);
X		beginline(TRUE);
X		break;
X
X	case 'H':
X		mtype = MLINE;
X		*Curschar = *Topchar;
X		for (n = Prenum; n && onedown(1) ;n--)
X			;
X		beginline(TRUE);
X		break;
X
X	case 'M':
X		mtype = MLINE;
X		*Curschar = *Topchar;
X		for (n = 0; n < Rows/2 && onedown(1) ;n++)
X			;
X		beginline(TRUE);
X		break;
X
X	case 'L':
X		mtype = MLINE;
X		*Curschar = *prevline(Botchar);
X		for (n = Prenum; n && oneup(1) ;n--)
X			;
X		beginline(TRUE);
X		break;
X
X	case 'l':
X	case K_RARROW:
X	case ' ':
X		mtype = MCHAR;
X		mincl = FALSE;
X		n = DEFAULT1(Prenum);
X		while (n--) {
X			if ( ! oneright() )
X				beep();
X		}
X		set_want_col = TRUE;
X		break;
X
X	case 'h':
X	case K_LARROW:
X	case CTRL('H'):
X		mtype = MCHAR;
X		mincl = FALSE;
X		n = DEFAULT1(Prenum);
X		while (n--) {
X			if ( ! oneleft() )
X				beep();
X		}
X		set_want_col = TRUE;
X		break;
X
X	case '-':
X		flag = TRUE;
X		/* fall through */
X
X	case 'k':
X	case K_UARROW:
X	case CTRL('P'):
X		mtype = MLINE;
X		if ( ! oneup(DEFAULT1(Prenum)) )
X			beep();
X		if (flag)
X			beginline(TRUE);
X		break;
X
X	case '+':
X	case CR:
X	case NL:
X		flag = TRUE;
X		/* fall through */
X
X	case 'j':
X	case K_DARROW:
X	case CTRL('N'):
X		mtype = MLINE;
X		if ( ! onedown(DEFAULT1(Prenum)) )
X			beep();
X		if (flag)
X			beginline(TRUE);
X		break;
X
X	/*
X	 * This is a strange motion command that helps make operators
X	 * more logical. It is actually implemented, but not documented
X	 * in the real 'vi'. This motion command actually refers to "the
X	 * current line". Commands like "dd" and "yy" are really an alternate
X	 * form of "d_" and "y_". It does accept a count, so "d3_" works to
X	 * delete 3 lines.
X	 */
X	case '_':
X	lineop:
X		mtype = MLINE;
X		onedown(DEFAULT1(Prenum)-1);
X		break;
X
X	case '|':
X		mtype = MCHAR;
X		mincl = TRUE;
X		beginline(FALSE);
X		if (Prenum > 0)
X			*Curschar = *coladvance(Curschar, Prenum-1);
X		Curswant = Prenum - 1;
X		break;
X		
X	case CTRL(']'):			/* :ta to current identifier */
X		CLEAROP;
X		{
X			char	ch;
X			LPTR	save;
X
X			save = *Curschar;
X			/*
X			 * First back up to start of identifier. This
X			 * doesn't match the real vi but I like it a
X			 * little better and it shouldn't bother anyone.
X			 */
X			ch = gchar(Curschar);
X			while (IDCHAR(ch)) {
X				if (!oneleft())
X					break;
X				ch = gchar(Curschar);
X			}
X			if (!IDCHAR(c))
X				oneright();
X
X			stuffin(":ta ");
X			/*
X			 * Now grab the chars in the identifier
X			 */
X			ch = gchar(Curschar);
X			while (IDCHAR(ch)) {
X				stuffin(mkstr(ch));
X				if (!oneright())
X					break;
X				ch = gchar(Curschar);
X			}
X			stuffin("\n");
X
X			*Curschar = save;	/* restore, in case of error */
X		}
X		break;
X
X	case '%':
X		mtype = MCHAR;
X		mincl = TRUE;
X		{
X			LPTR	*pos;
X
X			if ((pos = showmatch()) == NULL) {
X				beep();
X				CLEAROP;
X			} else {
X				setpcmark();
X				*Curschar = *pos;
X				set_want_col = TRUE;
X			}
X		}
X		break;
X		
X	/*
X	 * Word Motions
X	 */
X
X	case 'B':
X		type = 1;
X		/* fall through */
X
X	case 'b':
X		mtype = MCHAR;
X		mincl = FALSE;
X		set_want_col = TRUE;
X		for (n = DEFAULT1(Prenum); n > 0 ;n--) {
X			LPTR	*pos;
X
X			if ((pos = bck_word(Curschar, type)) == NULL) {
X				beep();
X				CLEAROP;
X				break;
X			} else
X				*Curschar = *pos;
X		}
X		break;
X
X	case 'W':
X		type = 1;
X		/* fall through */
X
X	case 'w':
X		/*
X		 * This is a little strange. To match what the real vi
X		 * does, we effectively map 'cw' to 'ce', and 'cW' to 'cE'.
X		 * This seems impolite at first, but it's really more
X		 * what we mean when we say 'cw'.
X		 */
X		if (operator == CHANGE)
X			goto doecmd;
X
X		mtype = MCHAR;
X		mincl = FALSE;
X		set_want_col = TRUE;
X		for (n = DEFAULT1(Prenum); n > 0 ;n--) {
X			LPTR	*pos;
X
X			if ((pos = fwd_word(Curschar, type)) == NULL) {
X				beep();
X				CLEAROP;
X				break;
X			} else
X				*Curschar = *pos;
X		}
X		break;
X
X	case 'E':
X		type = 1;
X		/* fall through */
X
X	case 'e':
X	doecmd:
X		mtype = MCHAR;
X		mincl = TRUE;
X		set_want_col = TRUE;
X		for (n = DEFAULT1(Prenum); n > 0 ;n--) {
X			LPTR	*pos;
X
X			if ((pos = end_word(Curschar, type)) == NULL) {
X				beep();
X				CLEAROP;
X				break;
X			} else
X				*Curschar = *pos;
X		}
X		break;
X
X	case '$':
X		mtype = MCHAR;
X		mincl = TRUE;
X		while ( oneright() )
X			;
X		Curswant = 999;		/* so we stay at the end */
X		break;
X
X	case '^':
X		flag = TRUE;
X		/* fall through */
X
X	case '0':
X		mtype = MCHAR;
X		mincl = TRUE;
X		beginline(flag);
X		break;
X
X	case 'x':
X		CLEAROP;
X		if (lineempty())	/* can't do it on a blank line */
X			beep();
X		if (Prenum)
X			stuffnum(Prenum);
X		stuffin("d.");
X		break;
X
X	case 'X':
X		CLEAROP;
X		if (!oneleft())
X			beep();
X		else {
X			addtobuff(Redobuff, 'X', NUL);
X			u_saveline();
X			delchar(TRUE);
X			updateline();
X		}
X		break;
X
X	case 'A':
X		set_want_col = TRUE;
X		while (oneright())
X			;
X		/* fall through */
X
X	case 'a':
X		CLEAROP;
X		/* Works just like an 'i'nsert on the next character. */
X		if (!lineempty())
X			inc(Curschar);
X		u_saveline();
X		startinsert(mkstr(c), FALSE);
X		break;
X
X	case 'I':
X		beginline(TRUE);
X		/* fall through */
X
X	case 'i':
X	case K_INSERT:
X		CLEAROP;
X		u_saveline();
X		startinsert(mkstr(c), FALSE);
X		break;
X
X	case 'o':
X		CLEAROP;
X		u_save(Curschar->linep, Curschar->linep->next);
X		opencmd(FORWARD, TRUE);
X		startinsert("o", TRUE);
X		break;
X
X	case 'O':
X		CLEAROP;
X		u_save(Curschar->linep->prev, Curschar->linep);
X		opencmd(BACKWARD, TRUE);
X		startinsert("O", TRUE);
X		break;
X
X	case 'R':
X		CLEAROP;
X		u_saveline();
X		startinsert("R", FALSE);
X		break;
X
X	case 'd':
X		if (operator == DELETE)		/* handle 'dd' */
X			goto lineop;
X		if (Prenum != 0)
X			opnum = Prenum;
X		startop = *Curschar;
X		operator = DELETE;
X		break;
X
X	case '!':
X		if (operator == FILTER)		/* handle '!!' */
X			goto lineop;
X		if (Prenum != 0)
X			opnum = Prenum;
X		startop = *Curschar;
X		operator = FILTER;
X		break;
X
X	/*
X	 * Some convenient abbreviations...
X	 */
X
X	case 'D':
X		stuffin("d$");
X		break;
X
X	case 'Y':
X		if (Prenum)
X			stuffnum(Prenum);
X		stuffin("yy");
X		break;
X
X	case 'C':
X		stuffin("c$");
X		break;
X
X	case 'c':
X		if (operator == CHANGE) {	/* handle 'cc' */
X			CLEAROP;
X			stuffin("0c$");
X			break;
X		}
X		if (Prenum != 0)
X			opnum = Prenum;
X		startop = *Curschar;
X		operator = CHANGE;
X		break;
X
X	case 'y':
X		if (operator == YANK)		/* handle 'yy' */
X			goto lineop;
X		if (Prenum != 0)
X			opnum = Prenum;
X		startop = *Curschar;
X		operator = YANK;
X		break;
X
X	case 'p':
X		doput(FORWARD);
X		break;
X
X	case 'P':
X		doput(BACKWARD);
X		break;
X
X	case '>':
X		if (operator == RSHIFT)		/* handle >> */
X			goto lineop;
X		if (Prenum != 0)
X			opnum = Prenum;
X		startop = *Curschar;
X		operator = RSHIFT;
X		break;
X
X	case '<':
X		if (operator == LSHIFT)		/* handle << */
X			goto lineop;
X		if (Prenum != 0)
X			opnum = Prenum;
X		startop = *Curschar;	/* save current position */
X		operator = LSHIFT;
X		break;
X
X	case 's':				/* substitute characters */
X		if (Prenum)
X			stuffnum(Prenum);
X		stuffin("c.");
X		break;
X
X	case '?':
X	case '/':
X	case ':':
X		CLEAROP;
X		readcmdline(c, NULL);
X		break;
X
X	case 'n':
X		mtype = MCHAR;
X		mincl = FALSE;
X		set_want_col = TRUE;
X		if (!repsearch(0))
X			CLEAROP;
X		break;
X
X	case 'N':
X		mtype = MCHAR;
X		mincl = FALSE;
X		set_want_col = TRUE;
X		if (!repsearch(1))
X			CLEAROP;
X		break;
X
X	/*
X	 * Character searches
X	 */
X	case 'T':
X		dir = BACKWARD;
X		/* fall through */
X
X	case 't':
X		type = 1;
X		goto docsearch;
X
X	case 'F':
X		dir = BACKWARD;
X		/* fall through */
X
X	case 'f':
X	docsearch:
X		mtype = MCHAR;
X		mincl = TRUE;
X		set_want_col = TRUE;
X		if ((nchar = vgetc()) == ESC)	/* search char */
X			break;
X		if (!searchc(nchar, dir, type)) {
X			CLEAROP;
X			beep();
X		}
X		break;
X
X	case ',':
X		flag = 1;
X		/* fall through */
X
X	case ';':
X		mtype = MCHAR;
X		mincl = TRUE;
X		set_want_col = TRUE;
X		if (!crepsearch(flag)) {
X			CLEAROP;
X			beep();
X		}
X		break;
X
X	/*
X	 * Function searches
X	 */
X
X	case '[':
X		dir = BACKWARD;
X		/* fall through */
X
X	case ']':
X		mtype = MLINE;
X		set_want_col = TRUE;
X		if (vgetc() != c) {
X			beep();
X			CLEAROP;
X			break;
X		}
X
X		if (!findfunc(dir)) {
X			beep();
X			CLEAROP;
X		}
X		break;
X
X	/*
X	 * Marks
X	 */
X
X	case 'm':
X		CLEAROP;
X		if (!setmark(vgetc()))
X			beep();
X		break;
X
X	case '\'':
X		flag = TRUE;
X		/* fall through */
X
X	case '`':
X		{
X			LPTR	mtmp, *mark = getmark(vgetc());
X
X			if (mark == NULL) {
X				beep();
X				CLEAROP;
X			} else {
X				mtmp = *mark;
X				setpcmark();
X				*Curschar = mtmp;
X				if (flag)
X					beginline(TRUE);
X			}
X			mtype = flag ? MLINE : MCHAR;
X			mincl = TRUE;		/* ignored if not MCHAR */
X			set_want_col = TRUE;
X		}
X		break;
X
X	case 'r':
X		CLEAROP;
X		if (lineempty()) {	/* Nothing to replace */
X			beep();
X			break;
X		}
X		if ((nchar = vgetc()) == ESC)
X			break;
X
X		if ((nchar & 0x80) || nchar == CR || nchar == NL) {
X			beep();
X			break;
X		}
X		u_saveline();
X
X		/* Change current character. */
X		pchar(Curschar, nchar);
X
X		/* Save stuff necessary to redo it */
X		addtobuff(Redobuff, 'r', nchar, NULL);
X
X		CHANGED;
X		updateline();
X		break;
X
X	case '~':		/* swap case */
X		CLEAROP;
X		if (lineempty()) {
X			beep();
X			break;
X		}
X		c = gchar(Curschar);
X
X		if (isalpha(c)) {
X			if (islower(c))
X				c = toupper(c);
X			else
X				c = tolower(c);
X		}
X		u_saveline();
X
X		pchar(Curschar, c);		/* Change current character. */
X		oneright();
X
X		addtobuff(Redobuff, '~', NULL);
X
X		CHANGED;
X		updateline();
X
X		break;
X
X	case 'J':
X		CLEAROP;
X
X		u_save(Curschar->linep->prev, Curschar->linep->next->next);
X
X		if (!dojoin())
X			beep();
X
X		addtobuff(Redobuff,'J',NULL);
X		updatescreen();
X		break;
X
X	case K_CGRAVE:			/* shorthand command */
X		CLEAROP;
X		stuffin(":e #\n");
X		break;
X
X	case 'Z':			/* write, if changed, and exit */
X		if (vgetc() != 'Z') {
X			beep();
X			break;
X		}
X
X		doxit();
X		break;
X
X	case '.':
X		/*
X		 * If a delete is in effect, we let '.' help out the same
X		 * way that '_' helps for some line operations. It's like
X		 * an 'l', but subtracts one from the count and is inclusive.
X		 */
X		if (operator == DELETE || operator == CHANGE) {
X			if (Prenum != 0) {
X				n = DEFAULT1(Prenum) - 1;
X				while (n--)
X					if (! oneright())
X						break;
X			}
X			mtype = MCHAR;
X			mincl = TRUE;
X		} else {			/* a normal 'redo' */
X			CLEAROP;
X			stuffin(Redobuff);
X		}
X		break;
X
X	case 'u':
X	case K_UNDO:
X		CLEAROP;
X		u_undo();
X		break;
X
X	case 'U':
X		CLEAROP;
X		u_lundo();
X		break;
X
X	default:
X		CLEAROP;
X		beep();
X		break;
X	}
X
X	/*
X	 * If an operation is pending, handle it...
X	 */
X	if (finish_op) {		/* we just finished an operator */
X		if (operator == NOP)	/* ... but it was cancelled */
X			return;
X
X		switch (operator) {
X
X		case LSHIFT:
X		case RSHIFT:
X			doshift(operator, c, nchar, Prenum);
X			break;
X
X		case DELETE:
X			dodelete(c, nchar, Prenum);
X			break;
X
X		case YANK:
X			doyank();	/* no redo on yank... */
X			break;
X
X		case CHANGE:
X			dochange(c, nchar, Prenum);
X			break;
X
X		case FILTER:
X			dofilter(c, nchar, Prenum);
X			break;
X
X		default:
X			beep();
X		}
X		operator = NOP;
X	}
X}
X
X/*
X * doshift - handle a shift operation
X */
Xstatic void
Xdoshift(op, c1, c2, num)
Xint	op;
Xchar	c1, c2;
Xint	num;
X{
X	LPTR	top, bot;
X	int	nlines;
X	char	opchar;
X
X	top = startop;
X	bot = *Curschar;
X
X	if (lt(&bot, &top))
X		pswap(&top, &bot);
X
X	u_save(top.linep->prev, bot.linep->next);
X
X	nlines = cntllines(&top, &bot);
X	*Curschar = top;
X	tabinout((op == LSHIFT), nlines);
X
X	/* construct Redo buff */
X	opchar = (op == LSHIFT) ? '<' : '>';
X	if (num != 0)
X		sprintf(Redobuff, "%c%d%c%c", opchar, num, c1, c2);
X	else
X		sprintf(Redobuff, "%c%c%c", opchar, c1, c2);
X
X	/*
X	 * The cursor position afterward is the prior of the two positions.
X	 */
X	*Curschar = top;
X
X	/*
X	 * If we were on the last char of a line that got shifted left,
X	 * then move left one so we aren't beyond the end of the line
X	 */
X	if (gchar(Curschar) == NUL && Curschar->index > 0)
X		Curschar->index--;
X
X	updatescreen();
X
X	if (nlines > P(P_RP))
X		smsg("%d lines %ced", nlines, opchar);
X}
X
X/*
X * dodelete - handle a delete operation
X */
Xstatic void
Xdodelete(c1, c2, num)
Xchar	c1, c2;
Xint	num;
X{
X	LPTR	top, bot;
X	int	nlines;
X	int	n;
X
X	/*
X	 * Do a yank of whatever we're about to delete. If there's too much
X	 * stuff to fit in the yank buffer, then get a confirmation before
X	 * doing the delete. This is crude, but simple. And it avoids doing
X	 * a delete of something we can't put back if we want.
X	 */
X	if (!doyank()) {
X		msg("yank buffer exceeded: press <y> to confirm");
X		if (vgetc() != 'y') {
X			msg("delete aborted");
X			*Curschar = startop;
X			return;
X		}
X	}
X
X	top = startop;
X	bot = *Curschar;
X
X	if (lt(&bot, &top))
X		pswap(&top, &bot);
X
X	u_save(top.linep->prev, bot.linep->next);
X
X	nlines = cntllines(&top, &bot);
X	*Curschar = top;
X	cursupdate();
X
X	if (mtype == MLINE) {
X		delline(nlines, TRUE);
X	} else {
X		if (!mincl && bot.index != 0)
X			dec(&bot);
X
X		if (top.linep == bot.linep) {		/* del. within line */
X			n = bot.index - top.index + 1;
X			while (n--)
X				if (!delchar(TRUE))
X					break;
X		} else {				/* del. between lines */
X			n = Curschar->index;
X			while (Curschar->index >= n)
X				if (!delchar(TRUE))
X					break;
X
X			top = *Curschar;
X			*Curschar = *nextline(Curschar);
X			delline(nlines-2, TRUE);
X			Curschar->index = 0;
X			n = bot.index + 1;
X			while (n--)
X				if (!delchar(TRUE))
X					break;
X			*Curschar = top;
X			dojoin();
X		}
X	}
X
X	/* construct Redo buff */
X	if (num != 0)
X		sprintf(Redobuff, "d%d%c%c", num, c1, c2);
X	else
X		sprintf(Redobuff, "d%c%c", c1, c2);
X
X	if (mtype == MCHAR && nlines == 1)
X		updateline();
X	else
X		updatescreen();
X
X	if (nlines > P(P_RP))
X		smsg("%d fewer lines", nlines);
X}
X
X/*
X * dofilter - handle a filter operation
X */
X
X#define	ITMP	"/tmp/viXXXXXX"
X#define	OTMP	"/tmp/voXXXXXX"
X
Xstatic	char	itmp[15];
Xstatic	char	otmp[15];
X
X
X/*
X * dofilter - filter lines through a command given by the user
X *
X * We use temp files and the system() routine here. This would normally
X * be done using pipes on a UNIX machine, but this is more portable to
X * the machines we usually run on. The system() routine needs to be able
X * to deal with redirection somehow, and should handle things like looking
X * at the PATH env. variable, and adding reasonable extensions to the
X * command name given by the user. All reasonable versions of system()
X * do this.
X */
Xstatic void
Xdofilter(c1, c2, num)
Xchar	c1, c2;
Xint	num;
X{
X	char	*mktemp();
X	static	char	*lastcmd = NULL;	/* the last thing we did */
X	char	buff[80];		/* buffer for prompting */
X	char	*p, *q;
X	char	c;
X	char	cmdln[128];		/* filtering command line */
X	LPTR	top, bot;
X	int	nlines;
X	int	n;
X
X	top = startop;
X	bot = *Curschar;
X
X	/*
X	 * Finish prompting for the filtering command...
X	 */
X	gotocmd(TRUE, '!');
X	p = buff;
X
X	/* collect the command string, handling '\b' and @ */
X	for (;;) {
X		c = vgetc();
X		if (c==NL || c==CR || c==EOF)
X			break;
X		if (c == BS) {
X			if (p > buff) {
X				p--;
X				/* this is gross, but it relies
X				 * only on 'gotocmd'
X				 */
X				gotocmd(TRUE, '!');
X				for (q=buff; q < p ; q++)
X					outchar(*q);
X			} else {
X				msg("");
X				return;		/* operator aborted */
X			}
X			continue;
X		}
X		if (c == '@') {
X			p = buff;
X			gotocmd(TRUE, '!');
X			continue;
X		}
X		outchar(c);
X		*p++ = c;
X	}
X	*p = '\0';
X
X	if (buff[0] == '!') {		/* use the 'last' command */
X		if (lastcmd == NULL) {
X			emsg("No previous command");
X			return;
X		}
X		strcpy(buff, lastcmd);
X	}
X
X	/*
X	 * Remember the current command
X	 */
X	if (lastcmd != NULL)
X		free(lastcmd);
X	lastcmd = strsave(buff);
X
X	if (lt(&bot, &top))
X		pswap(&top, &bot);
X
X	u_save(top.linep->prev, bot.linep->next);
X
X	nlines = cntllines(&top, &bot);
X	*Curschar = top;
X	cursupdate();
X
X	/*
X	 * 1. Form temp file names
X	 * 2. Write the lines to a temp file
X	 * 3. Run the filter command on the temp file
X	 * 4. Read the output of the command into the buffer
X	 * 5. Delete the original lines to be filtered
X	 * 6. Remove the temp files
X	 */
X
X	strcpy(itmp, ITMP);
X	strcpy(otmp, OTMP);
X
X	if (mktemp(itmp) == NULL || mktemp(otmp) == NULL) {
X		emsg("Can't get temp file names");
X		return;
X	}
X
X	if (!writeit(itmp, &top, &bot)) {
X		emsg("Can't create input temp file");
X		return;
X	}
X
X	sprintf(cmdln, "%s <%s >%s", buff, itmp, otmp);
X
X	if (system(cmdln) != 0) {
X		emsg("Filter command failed");
X		remove(ITMP);
X		return;
X	}
X
X	if (readfile(otmp, &bot, TRUE)) {
X		emsg("Can't read filter output");
X		return;
X	}
X
X	delline(nlines, TRUE);
X
X	remove(itmp);
X	remove(otmp);
X
X	/* construct Redo buff */
X	if (num != 0)
X		sprintf(Redobuff, "d%d%c%c", num, c1, c2);
X	else
X		sprintf(Redobuff, "d%c%c", c1, c2);
X
X	updatescreen();
X
X	if (nlines > P(P_RP))
X		smsg("%d lines filtered", nlines);
X}
X
X/*
X * dochange - handle a change operation
X */
Xstatic void
Xdochange(c1, c2, num)
Xchar	c1, c2;
Xint	num;
X{
X	char	sbuf[16];
X	bool_t	doappend;	/* true if we should do append, not insert */
X
X	doappend = endofline( (lt(Curschar, &startop)) ? &startop: Curschar);
X
X	if (mtype == MLINE) {
X		msg("multi-line changes not yet supported");
X		return;
X	}
X
X	dodelete(c1, c2, num);
X
X	if (num)
X		sprintf(sbuf, "c%d%c%c", num, c1, c2);
X	else
X		sprintf(sbuf, "c%c%c", c1, c2);
X
X	if (doappend && !lineempty())
X		inc(Curschar);
X
X	startinsert(sbuf, FALSE);
X}
X
X#ifndef	YBSIZE
X#define	YBSIZE	4096
X#endif
X
Xstatic	char	ybuf[YBSIZE];
Xstatic	int	ybtype = MBAD;
X
Xstatic bool_t
Xdoyank()
X{
X	LPTR	top, bot;
X	char	*yptr = ybuf;
X	char	*ybend = &ybuf[YBSIZE-1];
X	int	nlines;
X
X	top = startop;
X	bot = *Curschar;
X
X	if (lt(&bot, &top))
X		pswap(&top, &bot);
X
X	nlines = cntllines(&top, &bot);
X
X	ybtype = mtype;			/* set the yank buffer type */
X
X	if (mtype == MLINE) {
X		top.index = 0;
X		bot.index = strlen(bot.linep->s);
X		/*
X		 * The following statement checks for the special case of
X		 * yanking a blank line at the beginning of the file. If
X		 * not handled right, we yank an extra char (a newline).
X		 */
X		if (dec(&bot) == -1) {
X			ybuf[0] = NUL;
X			if (operator == YANK)
X				*Curschar = startop;
X			return TRUE;
X		}
X	} else {
X		if (!mincl) {
X			if (bot.index)
X				bot.index--;
X		}
X	}
X
X	for (; ltoreq(&top, &bot) ;inc(&top)) {
X		*yptr = (gchar(&top) != NUL) ? gchar(&top) : NL;
X		if (++yptr >= ybend) {
X			msg("yank too big for buffer");
X			ybtype = MBAD;
X			return FALSE;
X		}
X	}
X
X	*yptr = NUL;
X
X	if (operator == YANK) {	/* restore Curschar if really doing yank */
X		*Curschar = startop;
X
X		if (nlines > P(P_RP))
X			smsg("%d lines yanked", nlines);
X	}
X
X	return TRUE;
X}
X
X/*
X * inslines(lp, dir, buf)
X *
X * Inserts lines in the file from the given buffer. Lines are inserted
X * before or after "lp" according to the given direction flag. Newlines
X * in the buffer result in multiple lines being inserted. The cursor
X * is left on the first of the inserted lines.
X */
Xstatic void
Xinslines(lp, dir, buf)
XLINE	*lp;
Xint	dir;
Xchar	*buf;
X{
X	register char	*cp = buf;
X	register int	len;
X	char	*ep;
X	LINE	*l, *nc = NULL;
X	LPTR	sc;
X
X	if (dir == BACKWARD)
X		lp = lp->prev;
X
X	do {
X		if ((ep = strchr(cp, NL)) == NULL)
X			len = strlen(cp);
X		else
X			len = ep - cp;
X
X		l = newline(len+1);
X		strncpy(l->s, cp, len);
X		l->s[len] = NUL;
X
X		l->next = lp->next;
X		l->prev = lp;
X		lp->next->prev = l;
X		lp->next = l;
X
X		if (nc == NULL)
X			nc = l;
X
X		lp = lp->next;
X
X		cp = ep + 1;
X	} while (ep != NULL);
X
X	if (dir == BACKWARD)	/* fix the top line in case we were there */
X		Filemem->linep = Filetop->linep->next;
X
X	renum();
X
X	updatescreen();
X	Curschar->linep = nc;
X	Curschar->index = 0;
X}
X
X/*
X * doput(dir)
X *
X * Put the yank buffer at the current location, using the direction given
X * by 'dir'.
X */
Xstatic void
Xdoput(dir)
Xint	dir;
X{
X	if (ybtype == MBAD) {
X		beep();
X		return;
X	}
X	
X	u_saveline();
X
X	if (ybtype == MLINE)
X		inslines(Curschar->linep, dir, ybuf);
X	else {
X		/*
X		 * If we did a character-oriented yank, and the buffer
X		 * contains multiple lines, the situation is more complex.
X		 * For the moment, we punt, and pretend the user did a
X		 * line-oriented yank. This doesn't actually happen that
X		 * often.
X		 */
X		if (strchr(ybuf, NL) != NULL)
X			inslines(Curschar->linep, dir, ybuf);
X		else {
X			char	*s;
X			int	len;
X
X			len = strlen(Curschar->linep->s) + strlen(ybuf) + 1;
X			s = alloc(len);
X			strcpy(s, Curschar->linep->s);
X			if (dir == FORWARD)
X				Curschar->index++;
X			strcpy(s + Curschar->index, ybuf);
X			strcat(s, &Curschar->linep->s[Curschar->index]);
X			free(Curschar->linep->s);
X			Curschar->linep->s = s;
X			Curschar->linep->size = len;
X			updateline();
X		}
X	}
X
X	CHANGED;
X}
X
X/*
X * tabinout(inout,num)
X *
X * If inout==0, add a tab to the begining of the next num lines.
X * If inout==1, delete a tab from the beginning of the next num lines.
X */
Xstatic void
Xtabinout(inout, num)
Xint	inout;
Xint	num;
X{
X	int	ntodo = num;
X	LPTR	*p;
X
X	beginline(FALSE);
X	while ( ntodo-- > 0 ) {
X		beginline(FALSE);
X		if ( inout == 0 )
X			inschar(TAB);
X		else {
X			if ( gchar(Curschar) == TAB )
X				delchar(TRUE);
X		}
X		if ( ntodo > 0 ) {
X			if ( (p=nextline(Curschar)) != NULL )
X				*Curschar = *p;
X			else
X				break;
X		}
X	}
X}
X
Xstatic void
Xstartinsert(initstr, startln)
Xchar *initstr;
Xint	startln;	/* if set, insert point really at start of line */
X{
X	char *p, c;
X
X	*Insstart = *Curschar;
X	if (startln)
X		Insstart->index = 0;
X	Ninsert = 0;
X	Insptr = Insbuff;
X	for (p=initstr; (c=(*p++))!='\0'; )
X		*Insptr++ = c;
X
X	if (*initstr == 'R')
X		State = REPLACE;
X	else
X		State = INSERT;
X
X	if (P(P_MO))
X		msg((State == INSERT) ? "Insert Mode" : "Replace Mode");
X}
X
Xstatic bool_t
Xdojoin()
X{
X	int	scol;		/* save cursor column */
X	int	size;		/* size of the joined line */
X
X	if (nextline(Curschar) == NULL)		/* on last line */
X		return FALSE;
X
X	if (!canincrease(size = strlen(Curschar->linep->next->s)))
X		return FALSE;
X
X	while (oneright())			/* to end of line */
X		;
X
X	strcat(Curschar->linep->s, Curschar->linep->next->s);
X
X	/*
X	 * Delete the following line. To do this we move the cursor
X	 * there briefly, and then move it back. Don't back up if the
X	 * delete made us the last line.
X	 */
X	Curschar->linep = Curschar->linep->next;
X	scol = Curschar->index;
X
X	if (nextline(Curschar) != NULL) {
X		delline(1, TRUE);
X		Curschar->linep = Curschar->linep->prev;
X	} else
X		delline(1, TRUE);
X
X	Curschar->index = scol;
X
X	oneright();		/* go to first char. of joined line */
X
X	if (size != 0) {
X		/*
X		 * Delete leading white space on the joined line
X		 * and insert a single space.
X		 */
X		while (gchar(Curschar) == ' ' || gchar(Curschar) == TAB)
X			delchar(TRUE);
X		inschar(' ');
X	}
X
X	return TRUE;
X}
X
Xchar *
Xmkstr(c)
Xchar	c;
X{
X	static	char	s[2];
X
X	s[0] = c;
X	s[1] = NUL;
X
X	return s;
X}
END_OF_FILE
if test 28721 -ne `wc -c <'normal.c'`; then
    echo shar: \"'normal.c'\" unpacked with wrong size!
fi
# end of 'normal.c'
fi
if test -f 'os2_c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'os2_c'\"
else
echo shar: Extracting \"'os2_c'\" \(5296 characters\)
sed "s/^X//" >'os2_c' <<'END_OF_FILE'
Xstatic	char	RCSid[] =
X"$Header: os2.c,v 1.4 88/10/28 14:40:33 tony Exp $";
X
X/*
X * OS/2 System-dependent routines.
X *
X * $Log:	os2.c,v $
X * Revision 1.4  88/10/28  14:40:33  tony
X * Added doshell() to support ":sh" and ":!".
X * 
X * Revision 1.3  88/10/06  10:14:22  tony
X * Added fixname() routine which trims the base and extension parts of
X * a file name to 8 and 3 characters, and returns a pointer to the
X * resulting string. This makes it easier to deal with UNIX-style names
X * on stupid systems.
X * 
X * Revision 1.2  88/04/25  16:50:19  tony
X * Minor changes for OS/2 version 1.1; also fixed up the RCS header.
X * 
X * Revision 1.1  88/03/21  12:04:23  tony
X * Initial revision
X * 
X *
X */
X
X#define	INCL_BASE
X#include <os2.h>
X#include "stevie.h"
X
X/*
X * inchar() - get a character from the keyboard
X */
Xint
Xinchar()
X{
X	int	c;
X
X	for (;;beep()) {	/* loop until we get a valid character */
X
X		flushbuf();	/* flush any pending output */
X
X		switch (c = getch()) {
X		case 0x1e:
X			return K_CGRAVE;
X		case 0:				/* special key */
X			if (State != NORMAL) {
X				c = getch();	/* throw away next char */
X				continue;	/* and loop for another char */
X			}
X			switch (c = getch()) {
X			case 0x50:
X				return K_DARROW;
X			case 0x48:
X				return K_UARROW;
X			case 0x4b:
X				return K_LARROW;
X			case 0x4d:
X				return K_RARROW;
X			case 0x52:
X				return K_INSERT;
X			case 0x47:
X				stuffin("1G");
X				return -1;
X			case 0x4f:
X				stuffin("G");
X				return -1;
X			case 0x51:
X				stuffin(mkstr(CTRL('F')));
X				return -1;
X			case 0x49:
X				stuffin(mkstr(CTRL('B')));
X				return -1;
X			/*
X			 * Hard-code some useful function key macros.
X			 */
X			case 0x3b: /* F1 */
X				stuffin(":p\n");
X				return -1;
X			case 0x54: /* SF1 */
X				stuffin(":p!\n");
X				return -1;
X			case 0x3c: /* F2 */
X				stuffin(":n\n");
X				return -1;
X			case 0x55: /* SF2 */
X				stuffin(":n!\n");
X				return -1;
X			case 0x3d: /* F3 */
X				stuffin(":e #\n");
X				return -1;
X			case 0x3e: /* F4 */
X				stuffin(":rew\n");
X				return -1;
X			case 0x57: /* SF4 */
X				stuffin(":rew!\n");
X				return -1;
X			case 0x3f: /* F5 */
X				stuffin("[[");
X				return -1;
X			case 0x40: /* F6 */
X				stuffin("]]");
X				return -1;
X			case 0x41: /* F7 */
X				stuffin("<<");
X				return -1;
X			case 0x42: /* F8 */
X				stuffin(">>");
X				return -1;
X			case 0x43: /* F9 */
X				stuffin(":x\n");
X				return -1;
X			case 0x44: /* F10 */
X				stuffin(":help\n");
X				return -1;
X			default:
X				break;
X			}
X			break;
X
X		default:
X			return c;
X		}
X	}
X}
X
X#define	BSIZE	2048
Xstatic	char	outbuf[BSIZE];
Xstatic	int	bpos = 0;
X
Xvoid
Xflushbuf()
X{
X	if (bpos != 0)
X		write(1, outbuf, bpos);
X	bpos = 0;
X}
X
X/*
X * Macro to output a character. Used within this file for speed.
X */
X#define	outone(c)	outbuf[bpos++] = c; if (bpos >= BSIZE) flushbuf()
X
X/*
X * Function version for use outside this file.
X */
Xvoid
Xoutchar(c)
Xregister char	c;
X{
X	outbuf[bpos++] = c;
X	if (bpos >= BSIZE)
X		flushbuf();
X}
X
Xstatic	char	cell[2] = { 0, 7 };
X
X/*
X * outstr(s) - write a string to the console
X *
X * We implement insert/delete line escape sequences here. This is kind
X * of a kludge, but at least it's localized to a single point.
X */
Xvoid
Xoutstr(s)
Xregister char	*s;
X{
X	if (strcmp(s, T_DL) == 0) {		/* delete line */
X		int	r, c;
X
X		flushbuf();
X		VioGetCurPos(&r, &c, 0);
X		VioScrollUp(r, 0, 100, 100, 1, cell, 0);
X		return;
X	}
X	if (strcmp(s, T_IL) == 0) {		/* insert line */
X		int	r, c;
X
X		flushbuf();
X		VioGetCurPos(&r, &c, 0);
X		VioScrollDn(r, 0, 100, 100, 1, cell, 0);
X		return;
X	}
X
X	while (*s) {
X		outone(*s++);
X	}
X}
X
Xvoid
Xbeep()
X{
X	outone('\007');
X}
X
Xsleep(n)
Xint	n;
X{
X	DosSleep(1000L * n);
X}
X
Xvoid
Xdelay()
X{
X	flushbuf();
X	DosSleep(300L);
X}
X
Xvoid
Xwindinit()
X{
X	Columns = 80;
X	P(P_LI) = Rows = 25;
X}
X
Xvoid
Xwindexit(r)
Xint r;
X{
X	flushbuf();
X	exit(r);
X}
X
Xvoid
Xwindgoto(r, c)
Xregister int	r, c;
X{
X	r += 1;
X	c += 1;
X
X	/*
X	 * Check for overflow once, to save time.
X	 */
X	if (bpos + 8 >= BSIZE)
X		flushbuf();
X
X	outbuf[bpos++] = '\033';
X	outbuf[bpos++] = '[';
X	if (r >= 10)
X		outbuf[bpos++] = r/10 + '0';
X	outbuf[bpos++] = r%10 + '0';
X	outbuf[bpos++] = ';';
X	if (c >= 10)
X		outbuf[bpos++] = c/10 + '0';
X	outbuf[bpos++] = c%10 + '0';
X	outbuf[bpos++] = 'H';
X}
X
XFILE *
Xfopenb(fname, mode)
Xchar	*fname;
Xchar	*mode;
X{
X	FILE	*fopen();
X	char	modestr[16];
X
X	sprintf(modestr, "%sb", mode);
X	return fopen(fname, modestr);
X}
X
X#define	PSIZE	128
X
X/*
X * fixname(s) - fix up a dos name
X *
X * Takes a name like:
X *
X *	\x\y\z\base.ext
X *
X * and trims 'base' to 8 characters, and 'ext' to 3.
X */
Xchar *
Xfixname(s)
Xchar	*s;
X{
X	char	*strchr(), *strrchr();
X	static	char	f[PSIZE];
X	char	base[32];
X	char	ext[32];
X	char	*p;
X	int	i;
X
X	strcpy(f, s);
X
X	for (i=0; i < PSIZE ;i++)
X		if (f[i] == '/')
X			f[i] = '\\';
X
X	/*
X	 * Split the name into directory, base, extension.
X	 */
X	if ((p = strrchr(f, '\\')) != NULL) {
X		strcpy(base, p+1);
X		p[1] = '\0';
X	} else {
X		strcpy(base, f);
X		f[0] = '\0';
X	}
X
X	if ((p = strchr(base, '.')) != NULL) {
X		strcpy(ext, p+1);
X		*p = '\0';
X	} else
X		ext[0] = '\0';
X
X	/*
X	 * Trim the base name if necessary.
X	 */
X	if (strlen(base) > 8)
X		base[8] = '\0';
X	
X	if (strlen(ext) > 3)
X		ext[3] = '\0';
X
X	/*
X	 * Paste it all back together
X	 */
X	strcat(f, base);
X	strcat(f, ".");
X	strcat(f, ext);
X
X	return f;
X}
X
Xvoid
Xdoshell(cmd)
Xchar	*cmd;
X{
X	if (cmd == NULL)
X		cmd = "cmd.exe";
X
X	system(cmd);
X	wait_return();
X}
END_OF_FILE
if test 5296 -ne `wc -c <'os2_c'`; then
    echo shar: \"'os2_c'\" unpacked with wrong size!
fi
# end of 'os2_c'
fi
echo shar: End of shell archive.
exit 0

rtregn@immd3.informatik.uni-erlangen.de (Robert Regn) (12/15/88)

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  param.c param.h ptrfunc.c readme regexp.c regexp.h
#   regmagic.h regsub.c screen.c
# Wrapped by rtregn@faui32 on Wed Dec 14 17:11:22 1988
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'param.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'param.c'\"
else
echo shar: Extracting \"'param.c'\" \(4470 characters\)
sed "s/^X//" >'param.c' <<'END_OF_FILE'
Xstatic	char	RCSid[] =
X"$Header: param.c,v 1.6 88/11/10 08:57:18 tony Exp $";
X
X/*
X * Code to handle user-settable parameters. This is all pretty much table-
X * driven. To add a new parameter, put it in the params array, and add a
X * macro for it in param.h. If it's a numeric parameter, add any necessary
X * bounds checks to doset(). String parameters aren't currently supported.
X *
X * $Log:	param.c,v $
X * Revision 1.6  88/11/10  08:57:18  tony
X * Added a new parameter, modelines ("ml"), to support the modeline
X * feature.
X * 
X * Revision 1.5  88/08/26  08:45:34  tony
X * Misc. changes to make lint happy.
X * 
X * Revision 1.4  88/05/01  20:08:12  tony
X * Added the new boolean parameter "number" to turn on line numbering.
X * 
X * Revision 1.3  88/04/30  20:00:29  tony
X * Added the parameter "autoindent".
X * 
X * Revision 1.2  88/04/28  08:06:11  tony
X * Added a new parameter "ignorecase" which can be set to ignore case
X * distinctions in string searches.
X * 
X * Revision 1.1  88/03/20  21:09:40  tony
X * Initial revision
X * 
X *
X */
X
X#include "stevie.h"
X
Xstruct	param	params[] = {
X
X	{ "tabstop",	"ts",		8,	P_NUM },
X	{ "scroll",	"scroll",	12,	P_NUM },
X	{ "report",	"report",	5,	P_NUM },
X	{ "lines",	"lines",	25,	P_NUM },
X
X	{ "vbell",	"vb",		TRUE,	P_BOOL },
X	{ "showmatch",	"sm",		FALSE,	P_BOOL },
X	{ "wrapscan",	"ws",		TRUE,	P_BOOL },
X	{ "errorbells",	"eb",		FALSE,	P_BOOL },
X	{ "showmode",	"mo",		FALSE,	P_BOOL },
X	{ "backup",	"bk",		FALSE,	P_BOOL },
X	{ "return",	"cr",		TRUE,	P_BOOL },
X	{ "list",	"list",		FALSE,	P_BOOL },
X	{ "ignorecase",	"ic",		FALSE,	P_BOOL },
X	{ "autoindent",	"ai",		FALSE,	P_BOOL },
X	{ "number",	"nu",		FALSE,	P_BOOL },
X	{ "modelines",	"ml",		FALSE,	P_BOOL },
X	{ "",		"",		0,	0, }		/* end marker */
X
X};
X
Xstatic	void	showparms();
X
Xvoid
Xdoset(arg, inter)
Xchar	*arg;		/* parameter string */
Xbool_t	inter;		/* TRUE if called interactively */
X{
X	int	i;
X	char	*s;
X	bool_t	did_lines = FALSE;
X
X	bool_t	state = TRUE;		/* new state of boolean parms. */
X
X	if (arg == NULL) {
X		showparms(FALSE);
X		return;
X	}
X	if (strncmp(arg, "all", 3) == 0) {
X		showparms(TRUE);
X		return;
X	}
X	if (strncmp(arg, "no", 2) == 0) {
X		state = FALSE;
X		arg += 2;
X	}
X
X	for (i=0; params[i].fullname[0] != NUL ;i++) {
X		s = params[i].fullname;
X		if (strncmp(arg, s, strlen(s)) == 0)	/* matched full name */
X			break;
X		s = params[i].shortname;
X		if (strncmp(arg, s, strlen(s)) == 0)	/* matched short name */
X			break;
X	}
X
X	if (params[i].fullname[0] != NUL) {	/* found a match */
X		if (params[i].flags & P_NUM) {
X			did_lines = (i == P_LI);
X			if (inter && (arg[strlen(s)] != '=' || state == FALSE))
X				emsg("Invalid set of numeric parameter");
X			else {
X				params[i].value = atoi(arg+strlen(s)+1);
X				params[i].flags |= P_CHANGED;
X			}
X		} else /* boolean */ {
X			if (inter && (arg[strlen(s)] == '='))
X				emsg("Invalid set of boolean parameter");
X			else {
X				params[i].value = state;
X				params[i].flags |= P_CHANGED;
X			}
X		}
X	} else {
X		if (inter)
X			emsg("Unrecognized 'set' option");
X	}
X
X	/*
X	 * Update the screen in case we changed something like "tabstop"
X	 * or "list" that will change its appearance.
X	 */
X	if (inter)
X		updatescreen();
X
X	if (did_lines) {
X		Rows = P(P_LI);
X		screenalloc();		/* allocate new screen buffers */
X		screenclear();
X		updatescreen();
X	}
X	/*
X	 * Check the bounds for numeric parameters here
X	 */
X	if (P(P_TS) <= 0 || P(P_TS) > 32) {
X		if (inter)
X			emsg("Invalid tab size specified");
X		P(P_TS) = 8;
X		return;
X	}
X
X	if (P(P_SS) <= 0 || P(P_SS) > Rows) {
X		if (inter)
X			emsg("Invalid scroll size specified");
X		P(P_SS) = 12;
X		return;
X	}
X
X	/*
X	 * Check for another argument, and call doset() recursively, if
X	 * found. If any argument results in an error, no further
X	 * parameters are processed.
X	 */
X	while (*arg != ' ' && *arg != '\t') {	/* skip to next white space */
X		if (*arg == NUL)
X			return;			/* end of parameter list */
X		arg++;
X	}
X	while (*arg == ' ' || *arg == '\t')	/* skip to next non-white */
X		arg++;
X
X	if (*arg)
X		doset(arg, inter);	/* recurse on next parameter */
X}
X
Xstatic	void
Xshowparms(all)
Xbool_t	all;	/* show ALL parameters */
X{
X	struct	param	*p;
X	char	buf[64];
X
X	gotocmd(TRUE, 0);
X	outstr("Parameters:\r\n");
X
X	for (p = &params[0]; p->fullname[0] != NUL ;p++) {
X		if (!all && ((p->flags & P_CHANGED) == 0))
X			continue;
X		if (p->flags & P_BOOL)
X			sprintf(buf, "\t%s%s\r\n",
X				(p->value ? "" : "no"), p->fullname);
X		else
X			sprintf(buf, "\t%s=%d\r\n", p->fullname, p->value);
X
X		outstr(buf);
X	}
X	wait_return();
X}
END_OF_FILE
if test 4470 -ne `wc -c <'param.c'`; then
    echo shar: \"'param.c'\" unpacked with wrong size!
fi
# end of 'param.c'
fi
if test -f 'param.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'param.h'\"
else
echo shar: Extracting \"'param.h'\" \(1897 characters\)
sed "s/^X//" >'param.h' <<'END_OF_FILE'
X/*
X * $Header: param.h,v 1.5 88/11/10 08:57:56 tony Exp $
X *
X * Settable parameters
X *
X * $Log:	param.h,v $
X * Revision 1.5  88/11/10  08:57:56  tony
X * Added a new parameter, modelines ("ml"), to support the modeline
X * feature.
X * 
X * Revision 1.4  88/05/01  20:08:29  tony
X * Added the new boolean parameter "number" to turn on line numbering.
X * 
X * Revision 1.3  88/04/30  20:00:42  tony
X * Added the parameter "autoindent".
X * 
X * Revision 1.2  88/04/28  08:06:48  tony
X * Added a new parameter "ignorecase" which can be set to ignore case
X * distinctions in string searches.
X * 
X * Revision 1.1  88/03/20  21:04:50  tony
X * Initial revision
X * 
X *
X */
X
Xstruct	param {
X	char	*fullname;	/* full parameter name */
X	char	*shortname;	/* permissible abbreviation */
X	int	value;		/* parameter value */
X	int	flags;
X};
X
Xextern	struct	param	params[];
X
X/*
X * Flags
X */
X#define	P_BOOL		0x01	/* the parameter is boolean */
X#define	P_NUM		0x02	/* the parameter is numeric */
X#define	P_CHANGED	0x04	/* the parameter has been changed */
X
X/*
X * The following are the indices in the params array for each parameter
X */
X
X/*
X * Numeric parameters
X */
X#define	P_TS		0	/* tab size */
X#define	P_SS		1	/* scroll size */
X#define	P_RP		2	/* report */
X#define	P_LI		3	/* lines */
X
X/*
X * Boolean parameters
X */
X#define	P_VB		4	/* visual bell */
X#define	P_SM		5	/* showmatch */
X#define	P_WS		6	/* wrap scan */
X#define	P_EB		7	/* error bells */
X#define	P_MO		8	/* show mode */
X#define	P_BK		9	/* make backups when writing out files */
X#define	P_CR		10	/* use cr-lf to terminate lines on writes */
X#define	P_LS		11	/* show tabs and newlines graphically */
X#define	P_IC		12	/* ignore case in searches */
X#define	P_AI		13	/* auto-indent */
X#define	P_NU		14	/* number lines on the screen */
X#define	P_ML		15	/* enables mode-lines processing */
X
X/*
X * Macro to get the value of a parameter
X */
X#define	P(n)	(params[n].value)
END_OF_FILE
if test 1897 -ne `wc -c <'param.h'`; then
    echo shar: \"'param.h'\" unpacked with wrong size!
fi
# end of 'param.h'
fi
if test -f 'ptrfunc.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'ptrfunc.c'\"
else
echo shar: Extracting \"'ptrfunc.c'\" \(3242 characters\)
sed "s/^X//" >'ptrfunc.c' <<'END_OF_FILE'
Xstatic	char	RCSid[] =
X"$Header: ptrfunc.c,v 1.4 88/10/27 08:14:11 tony Exp $";
X
X/*
X * The routines in this file attempt to imitate many of the operations
X * that used to be performed on simple character pointers and are now
X * performed on LPTR's. This makes it easier to modify other sections
X * of the code. Think of an LPTR as representing a position in the file.
X * Positions can be incremented, decremented, compared, etc. through
X * the functions implemented here.
X *
X * $Log:	ptrfunc.c,v $
X * Revision 1.4  88/10/27  08:14:11  tony
X * Made some of the pointer operations less sensitive to the possibility
X * of null pointers.
X * 
X * Revision 1.3  88/08/26  08:45:40  tony
X * Misc. changes to make lint happy.
X * 
X * Revision 1.2  88/04/23  20:36:45  tony
X * Fixed a bug in dec() that caused problems when decrementing at the start
X * of the file. dec() now sticks at the start of the file. This caused
X * problems when reverse searching from the first character of the file.
X * 
X * Revision 1.1  88/03/20  21:09:55  tony
X * Initial revision
X * 
X *
X */
X
X#include "stevie.h"
X
X/*
X * inc(p)
X *
X * Increment the line pointer 'p' crossing line boundaries as necessary.
X * Return 1 when crossing a line, -1 when at end of file, 0 otherwise.
X */
Xint
Xinc(lp)
Xregister LPTR	*lp;
X{
X	register char *p;
X
X	if (lp && lp->linep)
X		p = &(lp->linep->s[lp->index]);
X	else
X		return -1;
X
X	if (*p != NUL) {			/* still within line */
X		lp->index++;
X		return ((p[1] != NUL) ? 0 : 1);
X	}
X
X	if (lp->linep->next != Fileend->linep) {  /* there is a next line */
X		lp->index = 0;
X		lp->linep = lp->linep->next;
X		return 1;
X	}
X
X	return -1;
X}
X
X/*
X * dec(p)
X *
X * Decrement the line pointer 'p' crossing line boundaries as necessary.
X * Return 1 when crossing a line, -1 when at start of file, 0 otherwise.
X */
Xint
Xdec(lp)
Xregister LPTR	*lp;
X{
X	if (lp->index > 0) {			/* still within line */
X		lp->index--;
X		return 0;
X	}
X
X	if (lp->linep->prev != Filetop->linep) { /* there is a prior line */
X		lp->linep = lp->linep->prev;
X		lp->index = strlen(lp->linep->s);
X		return 1;
X	}
X
X	lp->index = 0;				/* stick at first char */
X	return -1;				/* at start of file */
X}
X
X/*
X * gchar(lp) - get the character at position "lp"
X */
Xint
Xgchar(lp)
Xregister LPTR	*lp;
X{
X	if (lp && lp->linep)
X		return (lp->linep->s[lp->index]);
X	else
X		return 0;
X}
X
X/*
X * pchar(lp, c) - put character 'c' at position 'lp'
X */
Xvoid
Xpchar(lp, c)
Xregister LPTR	*lp;
Xchar	c;
X{
X	lp->linep->s[lp->index] = c;
X}
X
X/*
X * pswap(a, b) - swap two position pointers
X */
Xvoid
Xpswap(a, b)
Xregister LPTR	*a, *b;
X{
X	LPTR	tmp;
X
X	tmp = *a;
X	*a  = *b;
X	*b  = tmp;
X}
X
X/*
X * Position comparisons
X */
X
Xbool_t
Xlt(a, b)
Xregister LPTR	*a, *b;
X{
X	register int an, bn;
X
X	an = LINEOF(a);
X	bn = LINEOF(b);
X
X	if (an != bn)
X		return (an < bn);
X	else
X		return (a->index < b->index);
X}
X
X#if 0
Xbool_t
Xgt(a, b)
XLPTR	*a, *b;
X{
X	register int an, bn;
X
X	an = LINEOF(a);
X	bn = LINEOF(b);
X
X	if (an != bn)
X		return (an > bn);
X	else
X		return (a->index > b->index);
X}
X#endif
X
Xbool_t
Xequal(a, b)
Xregister LPTR	*a, *b;
X{
X	return (a->linep == b->linep && a->index == b->index);
X}
X
Xbool_t
Xltoreq(a, b)
Xregister LPTR	*a, *b;
X{
X	return (lt(a, b) || equal(a, b));
X}
X
X#if 0
Xbool_t
Xgtoreq(a, b)
XLPTR	*a, *b;
X{
X	return (gt(a, b) || equal(a, b));
X}
X#endif
END_OF_FILE
if test 3242 -ne `wc -c <'ptrfunc.c'`; then
    echo shar: \"'ptrfunc.c'\" unpacked with wrong size!
fi
# end of 'ptrfunc.c'
fi
if test -f 'readme' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'readme'\"
else
echo shar: Extracting \"'readme'\" \(1667 characters\)
sed "s/^X//" >'readme' <<'END_OF_FILE'
XSTEVIE Source Release - 3.26
X
XThis is a source release of the STEVIE editor, a public domain clone
Xof the UNIX editor 'vi'. The program was originally developed for the
XAtari ST, but has been ported to UNIX and OS/2 as well.
X
XParts of the editor are still badly in need of improvement, but it's
Xreached a pretty usable state overall. I'm not going to have much time
Xto work on it over the next couple of months, so I'll just release it
Xas is. The 'undo' facility is in pretty good shape, but yanks and puts
Xstill need some work. The current code there is rather limited and slow.
X
XThe files included in this release are:
X
Xreadme
X	This file.
X
Xstevie.doc
X	Reference manual for STEVIE. Assumes familiarity with vi.
X
Xsource.doc
X	Quick overview of the major data structures used.
X
Xporting.doc
X	Tips for porting STEVIE to other systems.
X
Xmakefile.os2
Xmakefile.usg
Xmakefile.tos
X	Makefiles for OS/2, UNIX System V, and the Atari ST respectively.
X
Xos2.c
Xunix.c
Xtos.c
Xenv.h
Xterm.h
X	System-dependent code for the same.
X
Xalloc.c ascii.h cmdline.c edit.c fileio.c help.c hexchars.c
Xkeymap.h linefunc.c main.c mark.c misccmds.c normal.c param.c
Xparam.h ptrfunc.c screen.c search.c stevie.h undo.c version.c
X
X	C source and header files for STEVIE.
X
Xctags.doc
X	Documentation for the accompanying program 'ctags'.
X
Xctags.c
X	Source code for the program 'ctags'.
X
X
XTo compile STEVIE for one of the provided systems:
X
X	1. Compile the regular expression library and install as
X	   appropriate for your system.
X
X	2. Edit the file 'env.h' to set the system defines as needed.
X
X	3. Check the makefile for your system, and modify as needed.
X
X	4. Compile.
X
XGood Luck...
X
XTony Andrews
X3/27/88
X
END_OF_FILE
if test 1667 -ne `wc -c <'readme'`; then
    echo shar: \"'readme'\" unpacked with wrong size!
fi
# end of 'readme'
fi
if test -f 'regexp.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'regexp.c'\"
else
echo shar: Extracting \"'regexp.c'\" \(29650 characters\)
sed "s/^X//" >'regexp.c' <<'END_OF_FILE'
Xstatic	char	RCSid[] =
X"$Header: regexp.c,v 1.3 88/10/27 08:12:32 tony Exp $";
X
X/*
X * NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE
X *
X * This is NOT the original regular expression code as written by
X * Henry Spencer. This code has been modified specifically for use
X * with the STEVIE editor, and should not be used apart from compiling
X * STEVIE. If you want a good regular expression library, get the
X * original code. The copyright notice that follows is from the
X * original.
X *
X * NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE
X *
X *
X * regcomp and regexec -- regsub and regerror are elsewhere
X *
X *	Copyright (c) 1986 by University of Toronto.
X *	Written by Henry Spencer.  Not derived from licensed software.
X *
X *	Permission is granted to anyone to use this software for any
X *	purpose on any computer system, and to redistribute it freely,
X *	subject to the following restrictions:
X *
X *	1. The author is not responsible for the consequences of use of
X *		this software, no matter how awful, even if they arise
X *		from defects in it.
X *
X *	2. The origin of this software must not be misrepresented, either
X *		by explicit claim or by omission.
X *
X *	3. Altered versions must be plainly marked as such, and must not
X *		be misrepresented as being the original software.
X *
X * Beware that some of this code is subtly aware of the way operator
X * precedence is structured in regular expressions.  Serious changes in
X * regular-expression syntax might require a total rethink.
X *
X * $Log:	regexp.c,v $
X * Revision 1.3  88/10/27  08:12:32  tony
X * Removed support for Megamax.
X * 
X * Revision 1.2  88/04/28  08:09:45  tony
X * First modification of the regexp library. Added an external variable
X * 'reg_ic' which can be set to indicate that case should be ignored.
X * Added a new parameter to regexec() to indicate that the given string
X * comes from the beginning of a line and is thus eligible to match
X * 'beginning-of-line'.
X * 
X */
X
X#include "env.h"
X
X#include <stdio.h>
X#include "regexp.h"
X#include "regmagic.h"
X
X/*
X * The "internal use only" fields in regexp.h are present to pass info from
X * compile to execute that permits the execute phase to run lots faster on
X * simple cases.  They are:
X *
X * regstart	char that must begin a match; '\0' if none obvious
X * reganch	is the match anchored (at beginning-of-line only)?
X * regmust	string (pointer into program) that match must include, or NULL
X * regmlen	length of regmust string
X *
X * Regstart and reganch permit very fast decisions on suitable starting points
X * for a match, cutting down the work a lot.  Regmust permits fast rejection
X * of lines that cannot possibly match.  The regmust tests are costly enough
X * that regcomp() supplies a regmust only if the r.e. contains something
X * potentially expensive (at present, the only such thing detected is * or +
X * at the start of the r.e., which can involve a lot of backup).  Regmlen is
X * supplied because the test in regexec() needs it and regcomp() is computing
X * it anyway.
X */
X
X/*
X * Structure for regexp "program".  This is essentially a linear encoding
X * of a nondeterministic finite-state machine (aka syntax charts or
X * "railroad normal form" in parsing technology).  Each node is an opcode
X * plus a "next" pointer, possibly plus an operand.  "Next" pointers of
X * all nodes except BRANCH implement concatenation; a "next" pointer with
X * a BRANCH on both ends of it is connecting two alternatives.  (Here we
X * have one of the subtle syntax dependencies:  an individual BRANCH (as
X * opposed to a collection of them) is never concatenated with anything
X * because of operator precedence.)  The operand of some types of node is
X * a literal string; for others, it is a node leading into a sub-FSM.  In
X * particular, the operand of a BRANCH node is the first node of the branch.
X * (NB this is *not* a tree structure:  the tail of the branch connects
X * to the thing following the set of BRANCHes.)  The opcodes are:
X */
X
X/* definition	number	opnd?	meaning */
X#define	END	0	/* no	End of program. */
X#define	BOL	1	/* no	Match "" at beginning of line. */
X#define	EOL	2	/* no	Match "" at end of line. */
X#define	ANY	3	/* no	Match any one character. */
X#define	ANYOF	4	/* str	Match any character in this string. */
X#define	ANYBUT	5	/* str	Match any character not in this string. */
X#define	BRANCH	6	/* node	Match this alternative, or the next... */
X#define	BACK	7	/* no	Match "", "next" ptr points backward. */
X#define	EXACTLY	8	/* str	Match this string. */
X#define	NOTHING	9	/* no	Match empty string. */
X#define	STAR	10	/* node	Match this (simple) thing 0 or more times. */
X#define	PLUS	11	/* node	Match this (simple) thing 1 or more times. */
X#define	OPEN	20	/* no	Mark this point in input as start of #n. */
X			/*	OPEN+1 is number 1, etc. */
X#define	CLOSE	30	/* no	Analogous to OPEN. */
X
X/*
X * Opcode notes:
X *
X * BRANCH	The set of branches constituting a single choice are hooked
X *		together with their "next" pointers, since precedence prevents
X *		anything being concatenated to any individual branch.  The
X *		"next" pointer of the last BRANCH in a choice points to the
X *		thing following the whole choice.  This is also where the
X *		final "next" pointer of each individual branch points; each
X *		branch starts with the operand node of a BRANCH node.
X *
X * BACK		Normal "next" pointers all implicitly point forward; BACK
X *		exists to make loop structures possible.
X *
X * STAR,PLUS	'?', and complex '*' and '+', are implemented as circular
X *		BRANCH structures using BACK.  Simple cases (one character
X *		per match) are implemented with STAR and PLUS for speed
X *		and to minimize recursive plunges.
X *
X * OPEN,CLOSE	...are numbered at compile time.
X */
X
X/*
X * A node is one char of opcode followed by two chars of "next" pointer.
X * "Next" pointers are stored as two 8-bit pieces, high order first.  The
X * value is a positive offset from the opcode of the node containing it.
X * An operand, if any, simply follows the node.  (Note that much of the
X * code generation knows about this implicit relationship.)
X *
X * Using two bytes for the "next" pointer is vast overkill for most things,
X * but allows patterns to get big without disasters.
X */
X#define	OP(p)	(*(p))
X#define	NEXT(p)	(((*((p)+1)&0377)<<8) + (*((p)+2)&0377))
X#define	OPERAND(p)	((p) + 3)
X
X/*
X * See regmagic.h for one further detail of program structure.
X */
X
X
X/*
X * Utility definitions.
X */
X#ifndef CHARBITS
X#define	UCHARAT(p)	((int)*(unsigned char *)(p))
X#else
X#define	UCHARAT(p)	((int)*(p)&CHARBITS)
X#endif
X
X#define	FAIL(m)	{ regerror(m); return(NULL); }
X#define	ISMULT(c)	((c) == '*' || (c) == '+' || (c) == '?')
X#define	META	"^$.[()|?+*\\"
X
X/*
X * Flags to be passed up and down.
X */
X#define	HASWIDTH	01	/* Known never to match null string. */
X#define	SIMPLE		02	/* Simple enough to be STAR/PLUS operand. */
X#define	SPSTART		04	/* Starts with * or +. */
X#define	WORST		0	/* Worst case. */
X
X#ifndef	ORIGINAL
X/*
X * The following supports the ability to ignore case in searches.
X */
X
X#include <ctype.h>
X
Xint reg_ic = 0;			/* set by callers to ignore case */
X
X/*
X * mkup - convert to upper case IF we're doing caseless compares
X */
X#define	mkup(c)		((islower(c) && reg_ic) ? toupper(c) : (c))
X
X#endif
X
X/*
X * Global work variables for regcomp().
X */
Xstatic char *regparse;		/* Input-scan pointer. */
Xstatic int regnpar;		/* () count. */
Xstatic char regdummy;
Xstatic char *regcode;		/* Code-emit pointer; &regdummy = don't. */
Xstatic long regsize;		/* Code size. */
X
X/*
X * Forward declarations for regcomp()'s friends.
X */
X#ifndef STATIC
X#define	STATIC	static
X#endif
XSTATIC char *reg();
XSTATIC char *regbranch();
XSTATIC char *regpiece();
XSTATIC char *regatom();
XSTATIC char *regnode();
XSTATIC char *regnext();
XSTATIC void regc();
XSTATIC void reginsert();
XSTATIC void regtail();
XSTATIC void regoptail();
X#ifdef STRCSPN
XSTATIC int strcspn();
X#endif
X
X/*
X - regcomp - compile a regular expression into internal code
X *
X * We can't allocate space until we know how big the compiled form will be,
X * but we can't compile it (and thus know how big it is) until we've got a
X * place to put the code.  So we cheat:  we compile it twice, once with code
X * generation turned off and size counting turned on, and once "for real".
X * This also means that we don't allocate space until we are sure that the
X * thing really will compile successfully, and we never have to move the
X * code and thus invalidate pointers into it.  (Note that it has to be in
X * one piece because free() must be able to free it all.)
X *
X * Beware that the optimization-preparation code in here knows about some
X * of the structure of the compiled regexp.
X */
Xregexp *
Xregcomp(exp)
Xchar *exp;
X{
X	register regexp *r;
X	register char *scan;
X	register char *longest;
X	register int len;
X	int flags;
X	extern char *malloc();
X
X	if (exp == NULL)
X		FAIL("NULL argument");
X
X	/* First pass: determine size, legality. */
X	regparse = exp;
X	regnpar = 1;
X	regsize = 0L;
X	regcode = &regdummy;
X	regc(MAGIC);
X	if (reg(0, &flags) == NULL)
X		return(NULL);
X
X	/* Small enough for pointer-storage convention? */
X	if (regsize >= 32767L)		/* Probably could be 65535L. */
X		FAIL("regexp too big");
X
X	/* Allocate space. */
X	r = (regexp *)malloc(sizeof(regexp) + (unsigned)regsize);
X	if (r == NULL)
X		FAIL("out of space");
X
X	/* Second pass: emit code. */
X	regparse = exp;
X	regnpar = 1;
X	regcode = r->program;
X	regc(MAGIC);
X	if (reg(0, &flags) == NULL)
X		return(NULL);
X
X	/* Dig out information for optimizations. */
X	r->regstart = '\0';	/* Worst-case defaults. */
X	r->reganch = 0;
X	r->regmust = NULL;
X	r->regmlen = 0;
X	scan = r->program+1;			/* First BRANCH. */
X	if (OP(regnext(scan)) == END) {		/* Only one top-level choice. */
X		scan = OPERAND(scan);
X
X		/* Starting-point info. */
X		if (OP(scan) == EXACTLY)
X			r->regstart = *OPERAND(scan);
X		else if (OP(scan) == BOL)
X			r->reganch++;
X
X		/*
X		 * If there's something expensive in the r.e., find the
X		 * longest literal string that must appear and make it the
X		 * regmust.  Resolve ties in favor of later strings, since
X		 * the regstart check works with the beginning of the r.e.
X		 * and avoiding duplication strengthens checking.  Not a
X		 * strong reason, but sufficient in the absence of others.
X		 */
X		if (flags&SPSTART) {
X			longest = NULL;
X			len = 0;
X			for (; scan != NULL; scan = regnext(scan))
X				if (OP(scan) == EXACTLY && strlen(OPERAND(scan)) >= len) {
X					longest = OPERAND(scan);
X					len = strlen(OPERAND(scan));
X				}
X			r->regmust = longest;
X			r->regmlen = len;
X		}
X	}
X
X	return(r);
X}
X
X/*
X - reg - regular expression, i.e. main body or parenthesized thing
X *
X * Caller must absorb opening parenthesis.
X *
X * Combining parenthesis handling with the base level of regular expression
X * is a trifle forced, but the need to tie the tails of the branches to what
X * follows makes it hard to avoid.
X */
Xstatic char *
Xreg(paren, flagp)
Xint paren;			/* Parenthesized? */
Xint *flagp;
X{
X	register char *ret;
X	register char *br;
X	register char *ender;
X	register int parno;
X	int flags;
X
X	*flagp = HASWIDTH;	/* Tentatively. */
X
X	/* Make an OPEN node, if parenthesized. */
X	if (paren) {
X		if (regnpar >= NSUBEXP)
X			FAIL("too many ()");
X		parno = regnpar;
X		regnpar++;
X		ret = regnode(OPEN+parno);
X	} else
X		ret = NULL;
X
X	/* Pick up the branches, linking them together. */
X	br = regbranch(&flags);
X	if (br == NULL)
X		return(NULL);
X	if (ret != NULL)
X		regtail(ret, br);	/* OPEN -> first. */
X	else
X		ret = br;
X	if (!(flags&HASWIDTH))
X		*flagp &= ~HASWIDTH;
X	*flagp |= flags&SPSTART;
X	while (*regparse == '|') {
X		regparse++;
X		br = regbranch(&flags);
X		if (br == NULL)
X			return(NULL);
X		regtail(ret, br);	/* BRANCH -> BRANCH. */
X		if (!(flags&HASWIDTH))
X			*flagp &= ~HASWIDTH;
X		*flagp |= flags&SPSTART;
X	}
X
X	/* Make a closing node, and hook it on the end. */
X	ender = regnode((paren) ? CLOSE+parno : END);	
X	regtail(ret, ender);
X
X	/* Hook the tails of the branches to the closing node. */
X	for (br = ret; br != NULL; br = regnext(br))
X		regoptail(br, ender);
X
X	/* Check for proper termination. */
X	if (paren && *regparse++ != ')') {
X		FAIL("unmatched ()");
X	} else if (!paren && *regparse != '\0') {
X		if (*regparse == ')') {
X			FAIL("unmatched ()");
X		} else
X			FAIL("junk on end");	/* "Can't happen". */
X		/* NOTREACHED */
X	}
X
X	return(ret);
X}
X
X/*
X - regbranch - one alternative of an | operator
X *
X * Implements the concatenation operator.
X */
Xstatic char *
Xregbranch(flagp)
Xint *flagp;
X{
X	register char *ret;
X	register char *chain;
X	register char *latest;
X	int flags;
X
X	*flagp = WORST;		/* Tentatively. */
X
X	ret = regnode(BRANCH);
X	chain = NULL;
X	while (*regparse != '\0' && *regparse != '|' && *regparse != ')') {
X		latest = regpiece(&flags);
X		if (latest == NULL)
X			return(NULL);
X		*flagp |= flags&HASWIDTH;
X		if (chain == NULL)	/* First piece. */
X			*flagp |= flags&SPSTART;
X		else
X			regtail(chain, latest);
X		chain = latest;
X	}
X	if (chain == NULL)	/* Loop ran zero times. */
X		(void) regnode(NOTHING);
X
X	return(ret);
X}
X
X/*
X - regpiece - something followed by possible [*+?]
X *
X * Note that the branching code sequences used for ? and the general cases
X * of * and + are somewhat optimized:  they use the same NOTHING node as
X * both the endmarker for their branch list and the body of the last branch.
X * It might seem that this node could be dispensed with entirely, but the
X * endmarker role is not redundant.
X */
Xstatic char *
Xregpiece(flagp)
Xint *flagp;
X{
X	register char *ret;
X	register char op;
X	register char *next;
X	int flags;
X
X	ret = regatom(&flags);
X	if (ret == NULL)
X		return(NULL);
X
X	op = *regparse;
X	if (!ISMULT(op)) {
X		*flagp = flags;
X		return(ret);
X	}
X
X	if (!(flags&HASWIDTH) && op != '?')
X		FAIL("*+ operand could be empty");
X	*flagp = (op != '+') ? (WORST|SPSTART) : (WORST|HASWIDTH);
X
X	if (op == '*' && (flags&SIMPLE))
X		reginsert(STAR, ret);
X	else if (op == '*') {
X		/* Emit x* as (x&|), where & means "self". */
X		reginsert(BRANCH, ret);			/* Either x */
X		regoptail(ret, regnode(BACK));		/* and loop */
X		regoptail(ret, ret);			/* back */
X		regtail(ret, regnode(BRANCH));		/* or */
X		regtail(ret, regnode(NOTHING));		/* null. */
X	} else if (op == '+' && (flags&SIMPLE))
X		reginsert(PLUS, ret);
X	else if (op == '+') {
X		/* Emit x+ as x(&|), where & means "self". */
X		next = regnode(BRANCH);			/* Either */
X		regtail(ret, next);
X		regtail(regnode(BACK), ret);		/* loop back */
X		regtail(next, regnode(BRANCH));		/* or */
X		regtail(ret, regnode(NOTHING));		/* null. */
X	} else if (op == '?') {
X		/* Emit x? as (x|) */
X		reginsert(BRANCH, ret);			/* Either x */
X		regtail(ret, regnode(BRANCH));		/* or */
X		next = regnode(NOTHING);		/* null. */
X		regtail(ret, next);
X		regoptail(ret, next);
X	}
X	regparse++;
X	if (ISMULT(*regparse))
X		FAIL("nested *?+");
X
X	return(ret);
X}
X
X/*
X - regatom - the lowest level
X *
X * Optimization:  gobbles an entire sequence of ordinary characters so that
X * it can turn them into a single node, which is smaller to store and
X * faster to run.  Backslashed characters are exceptions, each becoming a
X * separate node; the code is simpler that way and it's not worth fixing.
X */
Xstatic char *
Xregatom(flagp)
Xint *flagp;
X{
X	register char *ret;
X	int flags;
X
X	*flagp = WORST;		/* Tentatively. */
X
X	switch (*regparse++) {
X	case '^':
X		ret = regnode(BOL);
X		break;
X	case '$':
X		ret = regnode(EOL);
X		break;
X	case '.':
X		ret = regnode(ANY);
X		*flagp |= HASWIDTH|SIMPLE;
X		break;
X	case '[': {
X			register int class;
X			register int classend;
X
X			if (*regparse == '^') {	/* Complement of range. */
X				ret = regnode(ANYBUT);
X				regparse++;
X			} else
X				ret = regnode(ANYOF);
X			if (*regparse == ']' || *regparse == '-')
X				regc(*regparse++);
X			while (*regparse != '\0' && *regparse != ']') {
X				if (*regparse == '-') {
X					regparse++;
X					if (*regparse == ']' || *regparse == '\0')
X						regc('-');
X					else {
X						class = UCHARAT(regparse-2)+1;
X						classend = UCHARAT(regparse);
X						if (class > classend+1)
X							FAIL("invalid [] range");
X						for (; class <= classend; class++)
X							regc(class);
X						regparse++;
X					}
X				} else
X					regc(*regparse++);
X			}
X			regc('\0');
X			if (*regparse != ']')
X				FAIL("unmatched []");
X			regparse++;
X			*flagp |= HASWIDTH|SIMPLE;
X		}
X		break;
X	case '(':
X		ret = reg(1, &flags);
X		if (ret == NULL)
X			return(NULL);
X		*flagp |= flags&(HASWIDTH|SPSTART);
X		break;
X	case '\0':
X	case '|':
X	case ')':
X		FAIL("internal urp");	/* Supposed to be caught earlier. */
X		break;
X	case '?':
X	case '+':
X	case '*':
X		FAIL("?+* follows nothing");
X		break;
X	case '\\':
X		if (*regparse == '\0')
X			FAIL("trailing \\");
X		ret = regnode(EXACTLY);
X		regc(*regparse++);
X		regc('\0');
X		*flagp |= HASWIDTH|SIMPLE;
X		break;
X	default: {
X			register int len;
X			register char ender;
X
X			regparse--;
X			len = strcspn(regparse, META);
X			if (len <= 0)
X				FAIL("internal disaster");
X			ender = *(regparse+len);
X			if (len > 1 && ISMULT(ender))
X				len--;		/* Back off clear of ?+* operand. */
X			*flagp |= HASWIDTH;
X			if (len == 1)
X				*flagp |= SIMPLE;
X			ret = regnode(EXACTLY);
X			while (len > 0) {
X				regc(*regparse++);
X				len--;
X			}
X			regc('\0');
X		}
X		break;
X	}
X
X	return(ret);
X}
X
X/*
X - regnode - emit a node
X */
Xstatic char *			/* Location. */
Xregnode(op)
Xchar op;
X{
X	register char *ret;
X	register char *ptr;
X
X	ret = regcode;
X	if (ret == &regdummy) {
X		regsize += 3;
X		return(ret);
X	}
X
X	ptr = ret;
X	*ptr++ = op;
X	*ptr++ = '\0';		/* Null "next" pointer. */
X	*ptr++ = '\0';
X	regcode = ptr;
X
X	return(ret);
X}
X
X/*
X - regc - emit (if appropriate) a byte of code
X */
Xstatic void
Xregc(b)
Xchar b;
X{
X	if (regcode != &regdummy)
X		*regcode++ = b;
X	else
X		regsize++;
X}
X
X/*
X - reginsert - insert an operator in front of already-emitted operand
X *
X * Means relocating the operand.
X */
Xstatic void
Xreginsert(op, opnd)
Xchar op;
Xchar *opnd;
X{
X	register char *src;
X	register char *dst;
X	register char *place;
X
X	if (regcode == &regdummy) {
X		regsize += 3;
X		return;
X	}
X
X	src = regcode;
X	regcode += 3;
X	dst = regcode;
X	while (src > opnd)
X		*--dst = *--src;
X
X	place = opnd;		/* Op node, where operand used to be. */
X	*place++ = op;
X	*place++ = '\0';
X	*place++ = '\0';
X}
X
X/*
X - regtail - set the next-pointer at the end of a node chain
X */
Xstatic void
Xregtail(p, val)
Xchar *p;
Xchar *val;
X{
X	register char *scan;
X	register char *temp;
X	register int offset;
X
X	if (p == &regdummy)
X		return;
X
X	/* Find last node. */
X	scan = p;
X	for (;;) {
X		temp = regnext(scan);
X		if (temp == NULL)
X			break;
X		scan = temp;
X	}
X
X	if (OP(scan) == BACK)
X		offset = scan - val;
X	else
X		offset = val - scan;
X	*(scan+1) = (offset>>8)&0377;
X	*(scan+2) = offset&0377;
X}
X
X/*
X - regoptail - regtail on operand of first argument; nop if operandless
X */
Xstatic void
Xregoptail(p, val)
Xchar *p;
Xchar *val;
X{
X	/* "Operandless" and "op != BRANCH" are synonymous in practice. */
X	if (p == NULL || p == &regdummy || OP(p) != BRANCH)
X		return;
X	regtail(OPERAND(p), val);
X}
X
X/*
X * regexec and friends
X */
X
X/*
X * Global work variables for regexec().
X */
Xstatic char *reginput;		/* String-input pointer. */
Xstatic char *regbol;		/* Beginning of input, for ^ check. */
Xstatic char **regstartp;	/* Pointer to startp array. */
Xstatic char **regendp;		/* Ditto for endp. */
X
X/*
X * Forwards.
X */
XSTATIC int regtry();
XSTATIC int regmatch();
XSTATIC int regrepeat();
X
X#ifdef DEBUG
Xint regnarrate = 0;
Xvoid regdump();
XSTATIC char *regprop();
X#endif
X
X/*
X - regexec - match a regexp against a string
X */
Xint
Xregexec(prog, string, at_bol)
Xregister regexp *prog;
Xregister char *string;
Xint at_bol;
X{
X	register char *s;
X	extern char *cstrchr();
X
X	/* Be paranoid... */
X	if (prog == NULL || string == NULL) {
X		regerror("NULL parameter");
X		return(0);
X	}
X
X	/* Check validity of program. */
X	if (UCHARAT(prog->program) != MAGIC) {
X		regerror("corrupted program");
X		return(0);
X	}
X
X	/* If there is a "must appear" string, look for it. */
X	if (prog->regmust != NULL) {
X		s = string;
X		while ((s = cstrchr(s, prog->regmust[0])) != NULL) {
X			if (cstrncmp(s, prog->regmust, prog->regmlen) == 0)
X				break;	/* Found it. */
X			s++;
X		}
X		if (s == NULL)	/* Not present. */
X			return(0);
X	}
X
X	/* Mark beginning of line for ^ . */
X	if (at_bol)
X		regbol = string;	/* is possible to match bol */
X	else
X		regbol = NULL;		/* we aren't there, so don't match it */
X
X	/* Simplest case:  anchored match need be tried only once. */
X	if (prog->reganch)
X		return(regtry(prog, string));
X
X	/* Messy cases:  unanchored match. */
X	s = string;
X	if (prog->regstart != '\0')
X		/* We know what char it must start with. */
X		while ((s = cstrchr(s, prog->regstart)) != NULL) {
X			if (regtry(prog, s))
X				return(1);
X			s++;
X		}
X	else
X		/* We don't -- general case. */
X		do {
X			if (regtry(prog, s))
X				return(1);
X		} while (*s++ != '\0');
X
X	/* Failure. */
X	return(0);
X}
X
X/*
X - regtry - try match at specific point
X */
Xstatic int			/* 0 failure, 1 success */
Xregtry(prog, string)
Xregexp *prog;
Xchar *string;
X{
X	register int i;
X	register char **sp;
X	register char **ep;
X
X	reginput = string;
X	regstartp = prog->startp;
X	regendp = prog->endp;
X
X	sp = prog->startp;
X	ep = prog->endp;
X	for (i = NSUBEXP; i > 0; i--) {
X		*sp++ = NULL;
X		*ep++ = NULL;
X	}
X	if (regmatch(prog->program + 1)) {
X		prog->startp[0] = string;
X		prog->endp[0] = reginput;
X		return(1);
X	} else
X		return(0);
X}
X
X/*
X - regmatch - main matching routine
X *
X * Conceptually the strategy is simple:  check to see whether the current
X * node matches, call self recursively to see whether the rest matches,
X * and then act accordingly.  In practice we make some effort to avoid
X * recursion, in particular by going through "ordinary" nodes (that don't
X * need to know whether the rest of the match failed) by a loop instead of
X * by recursion.
X */
Xstatic int			/* 0 failure, 1 success */
Xregmatch(prog)
Xchar *prog;
X{
X	register char *scan;	/* Current node. */
X	char *next;		/* Next node. */
X	extern char *strchr();
X
X	scan = prog;
X#ifdef DEBUG
X	if (scan != NULL && regnarrate)
X		fprintf(stderr, "%s(\n", regprop(scan));
X#endif
X	while (scan != NULL) {
X#ifdef DEBUG
X		if (regnarrate)
X			fprintf(stderr, "%s...\n", regprop(scan));
X#endif
X		next = regnext(scan);
X
X		switch (OP(scan)) {
X		case BOL:
X			if (reginput != regbol)
X				return(0);
X			break;
X		case EOL:
X			if (*reginput != '\0')
X				return(0);
X			break;
X		case ANY:
X			if (*reginput == '\0')
X				return(0);
X			reginput++;
X			break;
X		case EXACTLY: {
X				register int len;
X				register char *opnd;
X
X				opnd = OPERAND(scan);
X				/* Inline the first character, for speed. */
X				if (mkup(*opnd) != mkup(*reginput))
X					return(0);
X				len = strlen(opnd);
X				if (len > 1 && cstrncmp(opnd,reginput,len) != 0)
X					return(0);
X				reginput += len;
X			}
X			break;
X		case ANYOF:
X 			if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) == NULL)
X				return(0);
X			reginput++;
X			break;
X		case ANYBUT:
X 			if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) != NULL)
X				return(0);
X			reginput++;
X			break;
X		case NOTHING:
X			break;
X		case BACK:
X			break;
X		case OPEN+1:
X		case OPEN+2:
X		case OPEN+3:
X		case OPEN+4:
X		case OPEN+5:
X		case OPEN+6:
X		case OPEN+7:
X		case OPEN+8:
X		case OPEN+9: {
X				register int no;
X				register char *save;
X
X				no = OP(scan) - OPEN;
X				save = reginput;
X
X				if (regmatch(next)) {
X					/*
X					 * Don't set startp if some later
X					 * invocation of the same parentheses
X					 * already has.
X					 */
X					if (regstartp[no] == NULL)
X						regstartp[no] = save;
X					return(1);
X				} else
X					return(0);
X			}
X			break;
X		case CLOSE+1:
X		case CLOSE+2:
X		case CLOSE+3:
X		case CLOSE+4:
X		case CLOSE+5:
X		case CLOSE+6:
X		case CLOSE+7:
X		case CLOSE+8:
X		case CLOSE+9: {
X				register int no;
X				register char *save;
X
X				no = OP(scan) - CLOSE;
X				save = reginput;
X
X				if (regmatch(next)) {
X					/*
X					 * Don't set endp if some later
X					 * invocation of the same parentheses
X					 * already has.
X					 */
X					if (regendp[no] == NULL)
X						regendp[no] = save;
X					return(1);
X				} else
X					return(0);
X			}
X			break;
X		case BRANCH: {
X				register char *save;
X
X				if (OP(next) != BRANCH)		/* No choice. */
X					next = OPERAND(scan);	/* Avoid recursion. */
X				else {
X					do {
X						save = reginput;
X						if (regmatch(OPERAND(scan)))
X							return(1);
X						reginput = save;
X						scan = regnext(scan);
X					} while (scan != NULL && OP(scan) == BRANCH);
X					return(0);
X					/* NOTREACHED */
X				}
X			}
X			break;
X		case STAR:
X		case PLUS: {
X				register char nextch;
X				register int no;
X				register char *save;
X				register int min;
X
X				/*
X				 * Lookahead to avoid useless match attempts
X				 * when we know what character comes next.
X				 */
X				nextch = '\0';
X				if (OP(next) == EXACTLY)
X					nextch = *OPERAND(next);
X				min = (OP(scan) == STAR) ? 0 : 1;
X				save = reginput;
X				no = regrepeat(OPERAND(scan));
X				while (no >= min) {
X					/* If it could work, try it. */
X					if (nextch == '\0' || *reginput == nextch)
X						if (regmatch(next))
X							return(1);
X					/* Couldn't or didn't -- back up. */
X					no--;
X					reginput = save + no;
X				}
X				return(0);
X			}
X			break;
X		case END:
X			return(1);	/* Success! */
X			break;
X		default:
X			regerror("memory corruption");
X			return(0);
X			break;
X		}
X
X		scan = next;
X	}
X
X	/*
X	 * We get here only if there's trouble -- normally "case END" is
X	 * the terminating point.
X	 */
X	regerror("corrupted pointers");
X	return(0);
X}
X
X/*
X - regrepeat - repeatedly match something simple, report how many
X */
Xstatic int
Xregrepeat(p)
Xchar *p;
X{
X	register int count = 0;
X	register char *scan;
X	register char *opnd;
X
X	scan = reginput;
X	opnd = OPERAND(p);
X	switch (OP(p)) {
X	case ANY:
X		count = strlen(scan);
X		scan += count;
X		break;
X	case EXACTLY:
X		while (mkup(*opnd) == mkup(*scan)) {
X			count++;
X			scan++;
X		}
X		break;
X	case ANYOF:
X		while (*scan != '\0' && strchr(opnd, *scan) != NULL) {
X			count++;
X			scan++;
X		}
X		break;
X	case ANYBUT:
X		while (*scan != '\0' && strchr(opnd, *scan) == NULL) {
X			count++;
X			scan++;
X		}
X		break;
X	default:		/* Oh dear.  Called inappropriately. */
X		regerror("internal foulup");
X		count = 0;	/* Best compromise. */
X		break;
X	}
X	reginput = scan;
X
X	return(count);
X}
X
X/*
X - regnext - dig the "next" pointer out of a node
X */
Xstatic char *
Xregnext(p)
Xregister char *p;
X{
X	register int offset;
X
X	if (p == &regdummy)
X		return(NULL);
X
X	offset = NEXT(p);
X	if (offset == 0)
X		return(NULL);
X
X	if (OP(p) == BACK)
X		return(p-offset);
X	else
X		return(p+offset);
X}
X
X#ifdef DEBUG
X
XSTATIC char *regprop();
X
X/*
X - regdump - dump a regexp onto stdout in vaguely comprehensible form
X */
Xvoid
Xregdump(r)
Xregexp *r;
X{
X	register char *s;
X	register char op = EXACTLY;	/* Arbitrary non-END op. */
X	register char *next;
X	extern char *strchr();
X
X
X	s = r->program + 1;
X	while (op != END) {	/* While that wasn't END last time... */
X		op = OP(s);
X		printf("%2d%s", s-r->program, regprop(s));	/* Where, what. */
X		next = regnext(s);
X		if (next == NULL)		/* Next ptr. */
X			printf("(0)");
X		else 
X			printf("(%d)", (s-r->program)+(next-s));
X		s += 3;
X		if (op == ANYOF || op == ANYBUT || op == EXACTLY) {
X			/* Literal string, where present. */
X			while (*s != '\0') {
X				putchar(*s);
X				s++;
X			}
X			s++;
X		}
X		putchar('\n');
X	}
X
X	/* Header fields of interest. */
X	if (r->regstart != '\0')
X		printf("start `%c' ", r->regstart);
X	if (r->reganch)
X		printf("anchored ");
X	if (r->regmust != NULL)
X		printf("must have \"%s\"", r->regmust);
X	printf("\n");
X}
X
X/*
X - regprop - printable representation of opcode
X */
Xstatic char *
Xregprop(op)
Xchar *op;
X{
X	register char *p;
X	static char buf[50];
X
X	(void) strcpy(buf, ":");
X
X	switch (OP(op)) {
X	case BOL:
X		p = "BOL";
X		break;
X	case EOL:
X		p = "EOL";
X		break;
X	case ANY:
X		p = "ANY";
X		break;
X	case ANYOF:
X		p = "ANYOF";
X		break;
X	case ANYBUT:
X		p = "ANYBUT";
X		break;
X	case BRANCH:
X		p = "BRANCH";
X		break;
X	case EXACTLY:
X		p = "EXACTLY";
X		break;
X	case NOTHING:
X		p = "NOTHING";
X		break;
X	case BACK:
X		p = "BACK";
X		break;
X	case END:
X		p = "END";
X		break;
X	case OPEN+1:
X	case OPEN+2:
X	case OPEN+3:
X	case OPEN+4:
X	case OPEN+5:
X	case OPEN+6:
X	case OPEN+7:
X	case OPEN+8:
X	case OPEN+9:
X		sprintf(buf+strlen(buf), "OPEN%d", OP(op)-OPEN);
X		p = NULL;
X		break;
X	case CLOSE+1:
X	case CLOSE+2:
X	case CLOSE+3:
X	case CLOSE+4:
X	case CLOSE+5:
X	case CLOSE+6:
X	case CLOSE+7:
X	case CLOSE+8:
X	case CLOSE+9:
X		sprintf(buf+strlen(buf), "CLOSE%d", OP(op)-CLOSE);
X		p = NULL;
X		break;
X	case STAR:
X		p = "STAR";
X		break;
X	case PLUS:
X		p = "PLUS";
X		break;
X	default:
X		regerror("corrupted opcode");
X		break;
X	}
X	if (p != NULL)
X		(void) strcat(buf, p);
X	return(buf);
X}
X#endif
X
X/*
X * The following is provided for those people who do not have strcspn() in
X * their C libraries.  They should get off their butts and do something
X * about it; at least one public-domain implementation of those (highly
X * useful) string routines has been published on Usenet.
X */
X#ifdef STRCSPN
X/*
X * strcspn - find length of initial segment of s1 consisting entirely
X * of characters not from s2
X */
X
Xstatic int
Xstrcspn(s1, s2)
Xchar *s1;
Xchar *s2;
X{
X	register char *scan1;
X	register char *scan2;
X	register int count;
X
X	count = 0;
X	for (scan1 = s1; *scan1 != '\0'; scan1++) {
X		for (scan2 = s2; *scan2 != '\0';)	/* ++ moved down. */
X			if (*scan1 == *scan2++)
X				return(count);
X		count++;
X	}
X	return(count);
X}
X#endif
X
Xint
Xcstrncmp(s1, s2, n)
Xchar	*s1, *s2;
Xint	n;
X{
X	char	*p, *S1, *S2, *strsave();
X	int	rval;
X
X	if (!reg_ic)
X		return (strncmp(s1, s2, n));
X
X	S1 = strsave(s1);
X	S2 = strsave(s2);
X
X	for (p = S1; *p ;p++)
X		if (islower(*p))
X			*p = toupper(*p);
X
X	for (p = S2; *p ;p++)
X		if (islower(*p))
X			*p = toupper(*p);
X
X	rval = strncmp(S1, S2, n);
X
X	free(S1);
X	free(S2);
X
X	return rval;
X}
X
Xchar *
Xcstrchr(s, c)
Xchar	*s;
Xchar	c;
X{
X	char	*p;
X
X	for (p = s; *p ;p++) {
X		if (mkup(*p) == mkup(c))
X			return p;
X	}
X	return NULL;
X}
END_OF_FILE
if test 29650 -ne `wc -c <'regexp.c'`; then
    echo shar: \"'regexp.c'\" unpacked with wrong size!
fi
# end of 'regexp.c'
fi
if test -f 'regexp.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'regexp.h'\"
else
echo shar: Extracting \"'regexp.h'\" \(1167 characters\)
sed "s/^X//" >'regexp.h' <<'END_OF_FILE'
X/*
X * NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE
X *
X * This is NOT the original regular expression code as written by
X * Henry Spencer. This code has been modified specifically for use
X * with the STEVIE editor, and should not be used apart from compiling
X * STEVIE. If you want a good regular expression library, get the
X * original code. The copyright notice that follows is from the
X * original.
X *
X * NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE
X *
X * Definitions etc. for regexp(3) routines.
X *
X * Caveat:  this is V8 regexp(3) [actually, a reimplementation thereof],
X * not the System V one.
X */
X#define NSUBEXP  10
Xtypedef struct regexp {
X	char *startp[NSUBEXP];
X	char *endp[NSUBEXP];
X	char regstart;		/* Internal use only. */
X	char reganch;		/* Internal use only. */
X	char *regmust;		/* Internal use only. */
X	int regmlen;		/* Internal use only. */
X	char program[1];	/* Unwarranted chumminess with compiler. */
X} regexp;
X
Xextern regexp *regcomp();
Xextern int regexec();
Xextern void regsub();
Xextern void regerror();
X
X#ifndef	ORIGINAL
Xextern int reg_ic;		/* set non-zero to ignore case in searches */
X#endif
END_OF_FILE
if test 1167 -ne `wc -c <'regexp.h'`; then
    echo shar: \"'regexp.h'\" unpacked with wrong size!
fi
# end of 'regexp.h'
fi
if test -f 'regmagic.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'regmagic.h'\"
else
echo shar: Extracting \"'regmagic.h'\" \(655 characters\)
sed "s/^X//" >'regmagic.h' <<'END_OF_FILE'
X/*
X * NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE
X *
X * This is NOT the original regular expression code as written by
X * Henry Spencer. This code has been modified specifically for use
X * with the STEVIE editor, and should not be used apart from compiling
X * STEVIE. If you want a good regular expression library, get the
X * original code. The copyright notice that follows is from the
X * original.
X *
X * NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE
X *
X * The first byte of the regexp internal "program" is actually this magic
X * number; the start node begins in the second byte.
X */
X#define	MAGIC	0234
END_OF_FILE
if test 655 -ne `wc -c <'regmagic.h'`; then
    echo shar: \"'regmagic.h'\" unpacked with wrong size!
fi
# end of 'regmagic.h'
fi
if test -f 'regsub.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'regsub.c'\"
else
echo shar: Extracting \"'regsub.c'\" \(2999 characters\)
sed "s/^X//" >'regsub.c' <<'END_OF_FILE'
Xstatic	char	RCSid[] =
X"$Header: regsub.c,v 1.3 88/10/27 08:13:03 tony Exp $";
X
X/*
X * NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE
X *
X * This is NOT the original regular expression code as written by
X * Henry Spencer. This code has been modified specifically for use
X * with the STEVIE editor, and should not be used apart from compiling
X * STEVIE. If you want a good regular expression library, get the
X * original code. The copyright notice that follows is from the
X * original.
X *
X * NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE
X *
X * regsub
X *
X *	Copyright (c) 1986 by University of Toronto.
X *	Written by Henry Spencer.  Not derived from licensed software.
X *
X *	Permission is granted to anyone to use this software for any
X *	purpose on any computer system, and to redistribute it freely,
X *	subject to the following restrictions:
X *
X *	1. The author is not responsible for the consequences of use of
X *		this software, no matter how awful, even if they arise
X *		from defects in it.
X *
X *	2. The origin of this software must not be misrepresented, either
X *		by explicit claim or by omission.
X *
X *	3. Altered versions must be plainly marked as such, and must not
X *		be misrepresented as being the original software.
X *
X * $Log:	regsub.c,v $
X * Revision 1.3  88/10/27  08:13:03  tony
X * Removed support for Megamax.
X * 
X * Revision 1.2  88/04/28  08:11:25  tony
X * First modification of the regexp library. Added an external variable
X * 'reg_ic' which can be set to indicate that case should be ignored.
X * Added a new parameter to regexec() to indicate that the given string
X * comes from the beginning of a line and is thus eligible to match
X * 'beginning-of-line'.
X * 
X */
X
X#include <stdio.h>
X#include "regexp.h"
X#include "regmagic.h"
X
X#ifndef CHARBITS
X#define	UCHARAT(p)	((int)*(unsigned char *)(p))
X#else
X#define	UCHARAT(p)	((int)*(p)&CHARBITS)
X#endif
X
X/*
X - regsub - perform substitutions after a regexp match
X */
Xvoid
Xregsub(prog, source, dest)
Xregexp *prog;
Xchar *source;
Xchar *dest;
X{
X	register char *src;
X	register char *dst;
X	register char c;
X	register int no;
X	register int len;
X	extern char *strncpy();
X
X	if (prog == NULL || source == NULL || dest == NULL) {
X		regerror("NULL parm to regsub");
X		return;
X	}
X	if (UCHARAT(prog->program) != MAGIC) {
X		regerror("damaged regexp fed to regsub");
X		return;
X	}
X
X	src = source;
X	dst = dest;
X	while ((c = *src++) != '\0') {
X		if (c == '&')
X			no = 0;
X		else if (c == '\\' && '0' <= *src && *src <= '9')
X			no = *src++ - '0';
X		else
X			no = -1;
X 		if (no < 0) {	/* Ordinary character. */
X 			if (c == '\\' && (*src == '\\' || *src == '&'))
X 				c = *src++;
X 			*dst++ = c;
X 		} else if (prog->startp[no] != NULL && prog->endp[no] != NULL) {
X			len = prog->endp[no] - prog->startp[no];
X			(void) strncpy(dst, prog->startp[no], len);
X			dst += len;
X			if (len != 0 && *(dst-1) == '\0') {	/* strncpy hit NUL. */
X				regerror("damaged match string");
X				return;
X			}
X		}
X	}
X	*dst++ = '\0';
X}
END_OF_FILE
if test 2999 -ne `wc -c <'regsub.c'`; then
    echo shar: \"'regsub.c'\" unpacked with wrong size!
fi
# end of 'regsub.c'
fi
if test -f 'screen.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'screen.c'\"
else
echo shar: Extracting \"'screen.c'\" \(16090 characters\)
sed "s/^X//" >'screen.c' <<'END_OF_FILE'
Xstatic	char	RCSid[] =
X"$Header: screen.c,v 1.4 88/08/26 08:45:43 tony Exp $";
X
X/*
X * Routines to manipulate the screen representations.
X *
X * $Log:	screen.c,v $
X * Revision 1.4  88/08/26  08:45:43  tony
X * Misc. changes to make lint happy.
X * 
X * Revision 1.3  88/06/26  14:50:59  tony
X * Added a new routine prt_line() to just dump a line of text, handling
X * control characters, etc. appropriately.
X * 
X * Revision 1.2  88/05/01  20:09:39  tony
X * Added support for  the new "number" parameter.
X * 
X * Revision 1.1  88/03/20  21:10:21  tony
X * Initial revision
X * 
X *
X */
X
X#include "stevie.h"
X
X/*
X * The following variable is set (in filetonext) to the number of physical
X * lines taken by the line the cursor is on. We use this to avoid extra
X * calls to plines(). The optimized routines lfiletonext() and lnexttoscreen()
X * make sure that the size of the cursor line hasn't changed. If so, lines
X * below the cursor will move up or down and we need to call the routines
X * filetonext() and nexttoscreen() to examine the entire screen.
X */
Xstatic	int	Cline_size;	/* size (in rows) of the cursor line */
Xstatic	int	Cline_row;	/* starting row of the cursor line */
X
Xstatic	char	*mkline();	/* calculate line string for "number" mode */
X
X/*
X * filetonext()
X *
X * Based on the current value of Topchar, transfer a screenfull of
X * stuff from Filemem to Nextscreen, and update Botchar.
X */
X
Xstatic void
Xfiletonext()
X{
X	register int row, col;
X	register char *screenp = Nextscreen;
X	LPTR memp;
X	LPTR save;			/* save pos. in case line won't fit */
X	register char *endscreen;
X	register char *nextrow;
X	char extra[16];
X	int nextra = 0;
X	register int c;
X	int n;
X	bool_t	done;		/* if TRUE, we hit the end of the file */
X	bool_t	didline;	/* if TRUE, we finished the last line */
X	int srow;		/* starting row of the current line */
X	int	lno;		/* number of the line we're doing */
X	int	coff;		/* column offset */
X
X	coff = P(P_NU) ? 8 : 0;
X
X	save = memp = *Topchar;
X
X	if (P(P_NU))
X		lno = cntllines(Filemem, Topchar);
X
X	/* The number of rows shown is Rows-1. */
X	/* The last line is the status/command line. */
X	endscreen = &screenp[(Rows-1)*Columns];
X
X	done = didline = FALSE;
X	srow = row = col = 0;
X	/*
X	 * We go one past the end of the screen so we can find out if the
X	 * last line fit on the screen or not.
X	 */
X	while ( screenp <= endscreen && !done) {
X
X
X		if (P(P_NU) && col == 0 && memp.index == 0) {
X			strcpy(extra, mkline(lno++));
X			nextra = 8;
X		}
X
X		/* Get the next character to put on the screen. */
X
X		/* The 'extra' array contains the extra stuff that is */
X		/* inserted to represent special characters (tabs, and */
X		/* other non-printable stuff.  The order in the 'extra' */
X		/* array is reversed. */
X
X		if ( nextra > 0 )
X			c = extra[--nextra];
X		else {
X			c = (unsigned)(0xff & gchar(&memp));
X			if (inc(&memp) == -1)
X				done = 1;
X			/* when getting a character from the file, we */
X			/* may have to turn it into something else on */
X			/* the way to putting it into 'Nextscreen'. */
X			if ( c == TAB && !P(P_LS) ) {
X				strcpy(extra,"        ");
X				/* tab amount depends on current column */
X				nextra = ((P(P_TS)-1) - (col - coff)%P(P_TS));
X				c = ' ';
X			}
X			else if ( c == NUL && P(P_LS) ) {
X				extra[0] = NUL;
X				nextra = 1;
X				c = '$';
X			} else if ( (n = chars[c].ch_size) > 1 ) {
X				char *p;
X				nextra = 0;
X				p = chars[c].ch_str;
X				/* copy 'ch-str'ing into 'extra' in reverse */
X				while ( n > 1 )
X					extra[nextra++] = p[--n];
X				c = p[0];
X			}
X		}
X
X		if (screenp == endscreen) {
X			/*
X			 * We're one past the end of the screen. If the
X			 * current character is null, then we really did
X			 * finish, so set didline = TRUE. In either case,
X			 * break out because we're done.
X			 */
X			dec(&memp);
X			if (memp.index != 0 && c == NUL) {
X				didline = TRUE;
X				inc(&memp);
X			}
X			break;
X		}
X
X		if ( c == NUL ) {
X			srow = ++row;
X			/*
X			 * Save this position in case the next line won't
X			 * fit on the screen completely.
X			 */
X			save = memp;
X			/* get pointer to start of next row */
X			nextrow = &Nextscreen[row*Columns];
X			/* blank out the rest of this row */
X			while ( screenp != nextrow )
X				*screenp++ = ' ';
X			col = 0;
X			continue;
X		}
X		if ( col >= Columns ) {
X			row++;
X			col = 0;
X		}
X		/* store the character in Nextscreen */
X		*screenp++ = c;
X		col++;
X	}
X	/*
X	 * If we didn't hit the end of the file, and we didn't finish
X	 * the last line we were working on, then the line didn't fit.
X	 */
X	if (!done && !didline) {
X		/*
X		 * Clear the rest of the screen and mark the unused lines.
X		 */
X		screenp = &Nextscreen[srow * Columns];
X		while (screenp < endscreen)
X			*screenp++ = ' ';
X		for (; srow < (Rows-1) ;srow++)
X			Nextscreen[srow * Columns] = '@';
X		*Botchar = save;
X		return;
X	}
X	/* make sure the rest of the screen is blank */
X	while ( screenp < endscreen )
X		*screenp++ = ' ';
X	/* put '~'s on rows that aren't part of the file. */
X	if ( col != 0 )
X		row++;
X	while ( row < Rows ) {
X		Nextscreen[row*Columns] = '~';
X		row++;
X	}
X	if (done)	/* we hit the end of the file */
X		*Botchar = *Fileend;
X	else
X		*Botchar = memp;	/* FIX - prev? */
X}
X
X/*
X * nexttoscreen
X *
X * Transfer the contents of Nextscreen to the screen, using Realscreen
X * to avoid unnecessary output.
X */
Xstatic void
Xnexttoscreen()
X{
X	register char *np = Nextscreen;
X	register char *rp = Realscreen;
X	register char *endscreen;
X	register int row = 0, col = 0;
X	int gorow = -1, gocol = -1;
X
X	endscreen = &np[(Rows-1)*Columns];
X
X	outstr(T_CI);		/* disable cursor */
X
X	for ( ; np < endscreen ; np++,rp++ ) {
X		/* If desired screen (contents of Nextscreen) does not */
X		/* match what's really there, put it there. */
X		if ( *np != *rp ) {
X			/* if we are positioned at the right place, */
X			/* we don't have to use windgoto(). */
X			if (gocol != col || gorow != row) {
X				/*
X				 * If we're just off by one, don't send
X				 * an entire esc. seq. (this happens a lot!)
X				 */
X				if (gorow == row && gocol+1 == col) {
X					outchar(*(np-1));
X					gocol++;
X				} else
X					windgoto(gorow=row,gocol=col);
X			}
X			outchar(*rp = *np);
X			gocol++;
X		}
X		if ( ++col >= Columns ) {
X			col = 0;
X			row++;
X		}
X	}
X	outstr(T_CV);		/* enable cursor again */
X}
X
X/*
X * lfiletonext() - like filetonext() but only for cursor line
X *
X * Returns true if the size of the cursor line (in rows) hasn't changed.
X * This determines whether or not we need to call filetonext() to examine
X * the entire screen for changes.
X */
Xstatic bool_t
Xlfiletonext()
X{
X	register int row, col;
X	register char *screenp;
X	LPTR memp;
X	register char *nextrow;
X	char extra[16];
X	int nextra = 0;
X	register int c;
X	int n;
X	bool_t eof;
X	int	lno;		/* number of the line we're doing */
X	int	coff;		/* column offset */
X
X	coff = P(P_NU) ? 8 : 0;
X
X	/*
X	 * This should be done more efficiently.
X	 */
X	if (P(P_NU))
X		lno = cntllines(Filemem, Curschar);
X
X	screenp = Nextscreen + (Cline_row * Columns);
X
X	memp = *Curschar;
X	memp.index = 0;
X
X	eof = FALSE;
X	col = 0;
X	row = Cline_row;
X
X	while (!eof) {
X
X		if (P(P_NU) && col == 0 && memp.index == 0) {
X			strcpy(extra, mkline(lno));
X			nextra = 8;
X		}
X
X		/* Get the next character to put on the screen. */
X
X		/* The 'extra' array contains the extra stuff that is */
X		/* inserted to represent special characters (tabs, and */
X		/* other non-printable stuff.  The order in the 'extra' */
X		/* array is reversed. */
X
X		if ( nextra > 0 )
X			c = extra[--nextra];
X		else {
X			c = (unsigned)(0xff & gchar(&memp));
X			if (inc(&memp) == -1)
X				eof = TRUE;
X			/* when getting a character from the file, we */
X			/* may have to turn it into something else on */
X			/* the way to putting it into 'Nextscreen'. */
X			if ( c == TAB && !P(P_LS) ) {
X				strcpy(extra,"        ");
X				/* tab amount depends on current column */
X				nextra = ((P(P_TS)-1) - (col - coff)%P(P_TS));
X				c = ' ';
X			} else if ( c == NUL && P(P_LS) ) {
X				extra[0] = NUL;
X				nextra = 1;
X				c = '$';
X			} else if ( c != NUL && (n=chars[c].ch_size) > 1 ) {
X				char *p;
X				nextra = 0;
X				p = chars[c].ch_str;
X				/* copy 'ch-str'ing into 'extra' in reverse */
X				while ( n > 1 )
X					extra[nextra++] = p[--n];
X				c = p[0];
X			}
X		}
X
X		if ( c == NUL ) {
X			row++;
X			/* get pointer to start of next row */
X			nextrow = &Nextscreen[row*Columns];
X			/* blank out the rest of this row */
X			while ( screenp != nextrow )
X				*screenp++ = ' ';
X			col = 0;
X			break;
X		}
X
X		if ( col >= Columns ) {
X			row++;
X			col = 0;
X		}
X		/* store the character in Nextscreen */
X		*screenp++ = c;
X		col++;
X	}
X	return ((row - Cline_row) == Cline_size);
X}
X
X/*
X * lnexttoscreen
X *
X * Like nexttoscreen() but only for the cursor line.
X */
Xstatic void
Xlnexttoscreen()
X{
X	register char *np = Nextscreen + (Cline_row * Columns);
X	register char *rp = Realscreen + (Cline_row * Columns);
X	register char *endline;
X	register int row, col;
X	int gorow = -1, gocol = -1;
X
X	endline = np + (Cline_size * Columns);
X
X	row = Cline_row;
X	col = 0;
X
X	outstr(T_CI);		/* disable cursor */
X
X	for ( ; np < endline ; np++,rp++ ) {
X		/* If desired screen (contents of Nextscreen) does not */
X		/* match what's really there, put it there. */
X		if ( *np != *rp ) {
X			/* if we are positioned at the right place, */
X			/* we don't have to use windgoto(). */
X			if (gocol != col || gorow != row) {
X				/*
X				 * If we're just off by one, don't send
X				 * an entire esc. seq. (this happens a lot!)
X				 */
X				if (gorow == row && gocol+1 == col) {
X					outchar(*(np-1));
X					gocol++;
X				} else
X					windgoto(gorow=row,gocol=col);
X			}
X			outchar(*rp = *np);
X			gocol++;
X		}
X		if ( ++col >= Columns ) {
X			col = 0;
X			row++;
X		}
X	}
X	outstr(T_CV);		/* enable cursor again */
X}
X
Xstatic char *
Xmkline(n)
Xint	n;
X{
X	static	char	lbuf[9];
X	int	i = 2;
X
X	strcpy(lbuf, "        ");
X
X	lbuf[i++] = (n % 10) + '0';
X	n /= 10;
X	if (n != 0) {
X		lbuf[i++] = (n % 10) + '0';
X		n /= 10;
X	}
X	if (n != 0) {
X		lbuf[i++] = (n % 10) + '0';
X		n /= 10;
X	}
X	if (n != 0) {
X		lbuf[i++] = (n % 10) + '0';
X		n /= 10;
X	}
X	if (n != 0) {
X		lbuf[i++] = (n % 10) + '0';
X		n /= 10;
X	}
X	return lbuf;
X}
X
X/*
X * updateline() - update the line the cursor is on
X *
X * Updateline() is called after changes that only affect the line that
X * the cursor is on. This improves performance tremendously for normal
X * insert mode operation. The only thing we have to watch for is when
X * the cursor line grows or shrinks around a row boundary. This means
X * we have to repaint other parts of the screen appropriately. If
X * lfiletonext() returns FALSE, the size of the cursor line (in rows)
X * has changed and we have to call updatescreen() to do a complete job.
X */
Xvoid
Xupdateline()
X{
X	if (!lfiletonext())
X		updatescreen();		/* bag it, do the whole screen */
X	else
X		lnexttoscreen();
X}
X
Xvoid
Xupdatescreen()
X{
X	filetonext();
X	nexttoscreen();
X}
X
X/*
X * prt_line() - print the given line
X */
Xvoid
Xprt_line(s)
Xchar	*s;
X{
X	register int	si = 0;
X	register int	c;
X	register int	col = 0;
X
X	char	extra[16];
X	int	nextra = 0;
X	int	n;
X
X	for (;;) {
X
X		if ( nextra > 0 )
X			c = extra[--nextra];
X		else {
X			c = s[si++];
X			if ( c == TAB && !P(P_LS) ) {
X				strcpy(extra, "        ");
X				/* tab amount depends on current column */
X				nextra = (P(P_TS) - 1) - col%P(P_TS);
X				c = ' ';
X			} else if ( c == NUL && P(P_LS) ) {
X				extra[0] = NUL;
X				nextra = 1;
X				c = '$';
X			} else if ( c != NUL && (n=chars[c].ch_size) > 1 ) {
X				char	*p;
X
X				nextra = 0;
X				p = chars[c].ch_str;
X				/* copy 'ch-str'ing into 'extra' in reverse */
X				while ( n > 1 )
X					extra[nextra++] = p[--n];
X				c = p[0];
X			}
X		}
X
X		if ( c == NUL )
X			break;
X
X		outchar(c);
X		col++;
X	}
X}
X
Xvoid
Xscreenclear()
X{
X	register char	*rp, *np;
X	register char	*end;
X
X	outstr(T_ED);		/* clear the display */
X
X	rp  = Realscreen;
X	end = Realscreen + Rows * Columns;
X	np  = Nextscreen;
X
X	/* blank out the stored screens */
X	while (rp != end)
X		*rp++ = *np++ = ' ';
X}
X
Xvoid
Xcursupdate()
X{
X	LPTR *p;
X	int icnt, c, nlines;
X	int i;
X	int didinc;
X
X	if (bufempty()) {		/* special case - file is empty */
X		*Topchar  = *Filemem;
X		*Curschar = *Filemem;
X	} else if ( LINEOF(Curschar) < LINEOF(Topchar) ) {
X		nlines = cntllines(Curschar,Topchar);
X		/* if the cursor is above the top of */
X		/* the screen, put it at the top of the screen.. */
X		*Topchar = *Curschar;
X		Topchar->index = 0;
X		/* ... and, if we weren't very close to begin with, */
X		/* we scroll so that the line is close to the middle. */
X		if ( nlines > Rows/3 ) {
X			for (i=0, p = Topchar; i < Rows/3 ;i++, *Topchar = *p)
X				if ((p = prevline(p)) == NULL)
X					break;
X		} else
X			s_ins(0, nlines-1);
X		updatescreen();
X	}
X	else if (LINEOF(Curschar) >= LINEOF(Botchar)) {
X		nlines = cntllines(Botchar,Curschar);
X		/* If the cursor is off the bottom of the screen, */
X		/* put it at the top of the screen.. */
X		/* ... and back up */
X		if ( nlines > Rows/3 ) {
X			p = Curschar;
X			for (i=0; i < (2*Rows)/3 ;i++)
X				if ((p = prevline(p)) == NULL)
X					break;
X			*Topchar = *p;
X		} else {
X			scrollup(nlines);
X		}
X		updatescreen();
X	}
X
X	Cursrow = Curscol = Cursvcol = 0;
X	for ( p=Topchar; p->linep != Curschar->linep ;p = nextline(p) )
X		Cursrow += plines(p);
X
X	Cline_row = Cursrow;
X	Cline_size = plines(p);
X
X	if (P(P_NU))
X		Curscol = 8;
X
X	for (i=0; i <= Curschar->index ;i++) {
X		c = Curschar->linep->s[i];
X		/* A tab gets expanded, depending on the current column */
X		if ( c == TAB && !P(P_LS) )
X			icnt = P(P_TS) - (Cursvcol % P(P_TS));
X		else
X			icnt = chars[(unsigned)(c & 0xff)].ch_size;
X		Curscol += icnt;
X		Cursvcol += icnt;
X		if ( Curscol >= Columns ) {
X			Curscol -= Columns;
X			Cursrow++;
X			didinc = TRUE;
X		}
X		else
X			didinc = FALSE;
X	}
X	if (didinc)
X		Cursrow--;
X
X	if (c == TAB && State == NORMAL && !P(P_LS)) {
X		Curscol--;
X		Cursvcol--;
X	} else {
X		Curscol -= icnt;
X		Cursvcol -= icnt;
X	}
X	if (Curscol < 0)
X		Curscol += Columns;
X
X	if (set_want_col) {
X		Curswant = Cursvcol;
X		set_want_col = FALSE;
X	}
X}
X
X/*
X * The rest of the routines in this file perform screen manipulations.
X * The given operation is performed physically on the screen. The
X * corresponding change is also made to the internal screen image.
X * In this way, the editor anticipates the effect of editing changes
X * on the appearance of the screen. That way, when we call screenupdate
X * a complete redraw isn't usually necessary. Another advantage is that
X * we can keep adding code to anticipate screen changes, and in the
X * meantime, everything still works.
X */
X
X/*
X * s_ins(row, nlines) - insert 'nlines' lines at 'row'
X */
Xvoid
Xs_ins(row, nlines)
Xint	row;
Xint	nlines;
X{
X	register char	*s, *d;		/* src & dest for block copy */
X	register char	*e;		/* end point for copy */
X	register int	i;
X
X	if (T_IL[0] == NUL)		/* can't do it */
X		return;
X
X	/*
X	 * It "looks" better if we do all the inserts at once
X	 */
X	outstr(T_SC);			/* save position */
X
X	for (i=0; i < nlines ;i++) {
X		windgoto(row, 0);
X		outstr(T_IL);
X	}
X
X	windgoto(Rows-1, 0);	/* delete any garbage that may have */
X	outstr(T_EL);		/* been shifted to the bottom line */
X	outstr(T_RC);		/* restore the cursor position */
X
X	/*
X	 * Now do a block move to update the internal screen image
X	 */
X	d = Realscreen + (Columns * (Rows - 1)) - 1;
X	s = d - (Columns * nlines);
X	e = Realscreen + (Columns * row);
X
X	while (s >= e)
X		*d-- = *s--;
X
X	/*
X	 * Clear the inserted lines
X	 */
X	s = Realscreen + (row * Columns);
X	e = s + (nlines * Columns);
X	while (s < e)
X		*s++ = ' ';
X}
X
X/*
X * s_del(row, nlines) - delete 'nlines' lines at 'row'
X */
Xvoid
Xs_del(row, nlines)
Xint	row;
Xint	nlines;
X{
X	register char	*s, *d, *e;
X	register int	i;
X
X	if (T_DL[0] == NUL)			/* can't do it */
X		return;
X
X	/* delete the lines */
X	outstr(T_SC);				/* save position */
X	for (i=0; i < nlines ;i++) {
X		windgoto(row, 0);
X		outstr(T_DL);			/* delete a line */
X		if (i == 0) {
X			windgoto(Rows-2, 0);	/* delete any garbage that */
X			outstr(T_EL);		/* was on the status line */
X		}
X	}
X	outstr(T_RC);				/* restore position */
X
X	/*
X	 * do a block move to update the internal image
X	 */
X	d = Realscreen + (row * Columns);
X	s = d + (nlines * Columns);
X	e = Realscreen + ((Rows - 1) * Columns);
X
X	while (s < e)
X		*d++ = *s++;
X
X	while (d < e)		/* clear the lines at the bottom */
X		*d++ = ' ';
X}
END_OF_FILE
if test 16090 -ne `wc -c <'screen.c'`; then
    echo shar: \"'screen.c'\" unpacked with wrong size!
fi
# end of 'screen.c'
fi
echo shar: End of shell archive.
exit 0

rtregn@immd3.informatik.uni-erlangen.de (Robert Regn) (12/15/88)

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  search.c stevie.h tags term.c term.h undo.c unix.c
#   version.c tos_c
# Wrapped by rtregn@faui32 on Wed Dec 14 17:19:38 1988
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'search.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'search.c'\"
else
echo shar: Extracting \"'search.c'\" \(19131 characters\)
sed "s/^X//" >'search.c' <<'END_OF_FILE'
Xstatic	char	RCSid[] =
X"$Header: search.c,v 1.10 88/10/27 08:11:21 tony Exp $";
X
X/*
X * This file contains various searching-related routines. These fall into
X * three groups: string searches (for /, ?, n, and N), character searches
X * within a single line (for f, F, t, T, etc), and "other" kinds of searches
X * like the '%' command, and 'word' searches.
X *
X * $Log:	search.c,v $
X * Revision 1.10  88/10/27  08:11:21  tony
X * Removed support for Megamax, and added code to print the search
X * string on repeated searches.
X * 
X * Revision 1.9  88/08/31  20:49:04  tony
X * Made another fix in search.c related to repeated searches.
X * 
X * Revision 1.8  88/08/25  15:12:06  tony
X * Fixed a bug where the cursor didn't land on the right place after
X * "beginning-of-word" searches if the word was preceded by the start
X * of the line and a single character.
X * 
X * Revision 1.7  88/08/23  12:52:08  tony
X * Fixed a bug in ssearch() where repeated searches ('n' or 'N') resulted
X * in dynamic memory being referenced after it was freed.
X * 
X * Revision 1.6  88/06/26  14:48:51  tony
X * Added a new routine, doglob(), to implement the ":g" command.
X * 
X * Revision 1.5  88/05/02  21:34:34  tony
X * Fixed a bug in my last modification that caused reverse repeated
X * searches to save the wrong direction away for future use.
X * 
X * Revision 1.4  88/04/29  14:46:06  tony
X * Changed repsearch() and dosearch() to return a boolean indicating
X * whether the search was successful.
X * 
X * Revision 1.3  88/04/28  08:18:14  tony
X * Added support for the "ignorecase" parameter. Also changed the search
X * routines to use the modified regexec() interface. Changed dosub() to
X * allow multiple substitutions per line.
X * 
X * Revision 1.2  88/04/24  21:36:19  tony
X * Added support for global substitution with the routine dosub(). The hard
X * work is really done by regsub() in the regexp library. Only a single
X * substitution per line is supported, for the moment.
X * 
X * Revision 1.1  88/03/20  21:10:30  tony
X * Initial revision
X * 
X *
X */
X
X#include "stevie.h"
X#include "regexp.h"	/* Henry Spencer's (modified) reg. exp. routines */
X
X/*
X * String searches
X *
X * The actual searches are done using Henry Spencer's regular expression
X * library.
X */
X
X#define	BEGWORD	"([^a-zA-Z0-9_]|^)"	/* replaces "\<" in search strings */
X#define	ENDWORD	"([^a-zA-Z0-9_]|$)"	/* likewise replaces "\>" */
X
X#define	BEGCHAR(c)	(islower(c) || isupper(c) || isdigit(c) || ((c) == '_'))
X
Xbool_t	begword;	/* does the search include a 'begin word' match */
X
X/*
X * mapstring(s) - map special backslash sequences
X */
Xstatic char *
Xmapstring(s)
Xregister char	*s;
X{
X	static	char	ns[80];
X	char	*p;
X
X	begword = FALSE;
X
X	for (p = ns; *s ;s++) {
X		if (*s != '\\') {	/* not an escape */
X			*p++ = *s;
X			continue;
X		}
X		switch (*++s) {
X		case '/':
X			*p++ = '/';
X			break;
X
X		case '<':
X			strcpy(p, BEGWORD);
X			p += strlen(BEGWORD);
X			begword = TRUE;
X			break;
X
X		case '>':
X			strcpy(p, ENDWORD);
X			p += strlen(ENDWORD);
X			break;
X
X		default:
X			*p++ = '\\';
X			*p++ = *s;
X			break;
X		}
X	}
X	*p++ = NUL;
X
X	return ns;
X}
X
Xstatic char *laststr = NULL;
Xstatic int lastsdir;
X
Xstatic LPTR *
Xssearch(dir,str)
Xint	dir;	/* FORWARD or BACKWARD */
Xchar	*str;
X{
X	LPTR *bcksearch(), *fwdsearch();
X	LPTR	*pos;
X	char	*old_ls = laststr;
X
X	reg_ic = P(P_IC);	/* tell the regexp routines how to search */
X
X	laststr = strsave(str);
X	lastsdir = dir;
X
X	if ( old_ls != NULL )
X		free(old_ls);
X
X	if ( dir == BACKWARD ) {
X		smsg("?%s", laststr);
X		pos = bcksearch(mapstring(laststr));
X	} else {
X		smsg("/%s", laststr);
X		pos = fwdsearch(mapstring(laststr));
X	}
X
X	/*
X	 * This is kind of a kludge, but its needed to make
X	 * 'beginning of word' searches land on the right place.
X	 */
X	if (pos != NULL && begword) {
X		if (pos->index != 0 || !BEGCHAR(pos->linep->s[0]))
X			pos->index += 1;
X	}
X	return pos;
X}
X
Xbool_t
Xdosearch(dir,str)
Xint dir;
Xchar *str;
X{
X	LPTR *p;
X
X	if (str == NULL)
X		str = laststr;
X
X	if ((p = ssearch(dir,str)) == NULL) {
X		msg("Pattern not found");
X		return FALSE;
X	} else {
X		LPTR savep;
X
X		cursupdate();
X		/* if we're backing up, we make sure the line we're on */
X		/* is on the screen. */
X		setpcmark();
X		*Curschar = savep = *p;
X		cursupdate();
X
X		return TRUE;
X	}
X}
X
X#define	OTHERDIR(x)	(((x) == FORWARD) ? BACKWARD : FORWARD)
X
Xbool_t
Xrepsearch(flag)
Xint	flag;
X{
X	int	dir = lastsdir;
X	bool_t	found;
X
X	if ( laststr == NULL ) {
X		beep();
X		return FALSE;
X	}
X
X	found = dosearch(flag ? OTHERDIR(lastsdir) : lastsdir, laststr);
X
X	/*
X	 * We have to save and restore 'lastsdir' because it gets munged
X	 * by ssearch() and winds up saving the wrong direction from here
X	 * if 'flag' is true.
X	 */
X	lastsdir = dir;
X
X	return found;
X}
X
X/*
X * regerror - called by regexp routines when errors are detected.
X */
Xvoid
Xregerror(s)
Xchar	*s;
X{
X	emsg(s);
X}
X
Xstatic LPTR *
Xfwdsearch(str)
Xregister char *str;
X{
X	static LPTR infile;
X	register LPTR *p;
X	regexp *prog;
X
X	register char *s;
X	register int i;
X
X	if ((prog = regcomp(str)) == NULL) {
X		emsg("Invalid search string");
X		return NULL;
X	}
X
X	p = Curschar;
X	i = Curschar->index + 1;
X	do {
X		s = p->linep->s + i;
X
X		if (regexec(prog, s, i == 0)) {		/* got a match */
X			infile.linep = p->linep;
X			infile.index = (int) (prog->startp[0] - p->linep->s);
X			free(prog);
X			return (&infile);
X		}
X		i = 0;
X	} while ((p = nextline(p)) != NULL);
X
X	/*
X	 * If wrapscan isn't set, then don't scan from the beginning
X	 * of the file. Just return failure here.
X	 */
X	if (!P(P_WS)) {
X		free(prog);
X		return NULL;
X	}
X
X	/* search from the beginning of the file to Curschar */
X	for (p = Filemem; p != NULL ;p = nextline(p)) {
X		s = p->linep->s;
X
X		if (regexec(prog, s, TRUE)) {		/* got a match */
X			infile.linep = p->linep;
X			infile.index = (int) (prog->startp[0] - s);
X			free(prog);
X			return (&infile);
X		}
X
X		if (p->linep == Curschar->linep)
X			break;
X	}
X
X	free(prog);
X	return(NULL);
X}
X
Xstatic LPTR *
Xbcksearch(str)
Xchar *str;
X{
X	static LPTR infile;
X	register LPTR *p;
X	regexp	*prog;
X	register char *s;
X	register int i;
X	register char	*match;
X
X	/* make sure str isn't empty */
X	if (str == NULL || *str == NUL)
X		return NULL;
X
X	if ((prog = regcomp(str)) == NULL) {
X		emsg("Invalid search string");
X		return NULL;
X	}
X
X	p = Curschar;
X	dec(p);
X
X	if (begword)		/* so we don't get stuck on one match */
X		dec(p);
X
X	i = p->index;
X
X	do {
X		s = p->linep->s;
X
X		if (regexec(prog, s, TRUE)) {	/* match somewhere on line */
X
X			/*
X			 * Now, if there are multiple matches on this line,
X			 * we have to get the last one. Or the last one
X			 * before the cursor, if we're on that line.
X			 */
X			match = prog->startp[0];
X
X			while (regexec(prog, prog->endp[0], FALSE)) {
X				if ((i >= 0) && ((prog->startp[0] - s) > i))
X					break;
X				match = prog->startp[0];
X			}
X
X			if ((i >= 0) && ((match - s) > i)) {
X				i = -1;
X				continue;
X			}
X
X			infile.linep = p->linep;
X			infile.index = (int) (match - s);
X			free(prog);
X			return (&infile);
X		}
X		i = -1;
X
X	} while ((p = prevline(p)) != NULL);
X
X	/*
X	 * If wrapscan isn't set, bag the search now
X	 */
X	if (!P(P_WS)) {
X		free(prog);
X		return NULL;
X	}
X
X	/* search backward from the end of the file */
X	p = prevline(Fileend);
X	do {
X		s = p->linep->s;
X
X		if (regexec(prog, s, TRUE)) {	/* match somewhere on line */
X
X			/*
X			 * Now, if there are multiple matches on this line,
X			 * we have to get the last one.
X			 */
X			match = prog->startp[0];
X
X			while (regexec(prog, prog->endp[0], FALSE))
X				match = prog->startp[0];
X
X			infile.linep = p->linep;
X			infile.index = (int) (match - s);
X			free(prog);
X			return (&infile);
X		}
X
X		if (p->linep == Curschar->linep)
X			break;
X
X	} while ((p = prevline(p)) != NULL);
X
X	free(prog);
X	return NULL;
X}
X
X/*
X * dosub(lp, up, cmd)
X *
X * Perform a substitution from line 'lp' to line 'up' using the
X * command pointed to by 'cmd' which should be of the form:
X *
X * /pattern/substitution/g
X *
X * The trailing 'g' is optional and, if present, indicates that multiple
X * substitutions should be performed on each line, if applicable.
X * The usual escapes are supported as described in the regexp docs.
X */
Xvoid
Xdosub(lp, up, cmd)
XLPTR	*lp, *up;
Xchar	*cmd;
X{
X	LINE	*cp;
X	char	*pat, *sub;
X	regexp	*prog;
X	int	nsubs;
X	bool_t	do_all;		/* do multiple substitutions per line */
X
X	/*
X	 * If no range was given, do the current line. If only one line
X	 * was given, just do that one.
X	 */
X	if (lp->linep == NULL)
X		*up = *lp = *Curschar;
X	else {
X		if (up->linep == NULL)
X			*up = *lp;
X	}
X
X	pat = ++cmd;		/* skip the initial '/' */
X
X	while (*cmd) {
X		if (cmd[0] == '/' && cmd[-1] != '\\') {
X			*cmd++ = NUL;
X			break;
X		}
X		cmd++;
X	}
X
X	if (*pat == NUL) {
X		emsg("NULL pattern specified");
X		return;
X	}
X
X	sub = cmd;
X
X	do_all = FALSE;
X
X	while (*cmd) {
X		if (cmd[0] == '/' && cmd[-1] != '\\') {
X			do_all = (cmd[1] == 'g');
X			*cmd++ = NUL;
X			break;
X		}
X		cmd++;
X	}
X
X	reg_ic = P(P_IC);	/* set "ignore case" flag appropriately */
X
X	if ((prog = regcomp(pat)) == NULL) {
X		emsg("Invalid search string");
X		return;
X	}
X
X	nsubs = 0;
X
X	for (cp = lp->linep; cp != NULL ;cp = cp->next) {
X		if (regexec(prog, cp->s, TRUE)) { /* a match on this line */
X			char	*ns, *sns, *p;
X
X			/*
X			 * Get some space for a temporary buffer
X			 * to do the substitution into.
X			 */
X			sns = ns = alloc(2048);
X			*sns = NUL;
X
X			p = cp->s;
X
X			do {
X				for (ns = sns; *ns ;ns++)
X					;
X				/*
X				 * copy up to the part that matched
X				 */
X				while (p < prog->startp[0])
X					*ns++ = *p++;
X
X				regsub(prog, sub, ns);
X
X				/*
X				 * continue searching after the match
X				 */
X				p = prog->endp[0];
X
X			} while (regexec(prog, p, FALSE) && do_all);
X
X			for (ns = sns; *ns ;ns++)
X				;
X
X			/*
X			 * copy the rest of the line, that didn't match
X			 */
X			while (*p)
X				*ns++ = *p++;
X
X			*ns = NUL;
X
X			free(cp->s);		/* free the original line */
X			cp->s = strsave(sns);	/* and save the modified str */
X			cp->size = strlen(cp->s) + 1;
X			free(sns);		/* free the temp buffer */
X			nsubs++;
X			CHANGED;
X		}
X		if (cp == up->linep)
X			break;
X	}
X
X	if (nsubs) {
X		updatescreen();
X		if (nsubs >= P(P_RP))
X			smsg("%d substitution%c", nsubs, (nsubs>1) ? 's' : ' ');
X	} else
X		msg("No match");
X
X	free(prog);
X}
X
X/*
X * doglob(cmd)
X *
X * Execute a global command of the form:
X *
X * g/pattern/X
X *
X * where 'x' is a command character, currently one of the following:
X *
X * d	Delete all matching lines
X * p	Print all matching lines
X *
X * The command character (as well as the trailing slash) is optional, and
X * is assumed to be 'p' if missing.
X */
Xvoid
Xdoglob(lp, up, cmd)
XLPTR	*lp, *up;
Xchar	*cmd;
X{
X	LINE	*cp;
X	char	*pat;
X	regexp	*prog;
X	int	ndone;
X	char	cmdchar = NUL;	/* what to do with matching lines */
X
X	/*
X	 * If no range was given, do every line. If only one line
X	 * was given, just do that one.
X	 */
X	if (lp->linep == NULL) {
X		*lp = *Filemem;
X		*up = *Fileend;
X	} else {
X		if (up->linep == NULL)
X			*up = *lp;
X	}
X
X	pat = ++cmd;		/* skip the initial '/' */
X
X	while (*cmd) {
X		if (cmd[0] == '/' && cmd[-1] != '\\') {
X			cmdchar = cmd[1];
X			*cmd++ = NUL;
X			break;
X		}
X		cmd++;
X	}
X	if (cmdchar == NUL)
X		cmdchar = 'p';
X
X	reg_ic = P(P_IC);	/* set "ignore case" flag appropriately */
X
X	if (cmdchar != 'd' && cmdchar != 'p') {
X		emsg("Invalid command character");
X		return;
X	}
X
X	if ((prog = regcomp(pat)) == NULL) {
X		emsg("Invalid search string");
X		return;
X	}
X
X	msg("");
X	ndone = 0;
X
X	for (cp = lp->linep; cp != NULL ;cp = cp->next) {
X		if (regexec(prog, cp->s, TRUE)) { /* a match on this line */
X			switch (cmdchar) {
X
X			case 'd':		/* delete the line */
X				if (Curschar->linep != cp) {
X					LPTR	savep;
X
X					savep = *Curschar;
X					Curschar->linep = cp;
X					Curschar->index = 0;
X					delline(1, FALSE);
X					*Curschar = savep;
X				} else
X					delline(1, FALSE);
X				break;
X
X			case 'p':		/* print the line */
X				prt_line(cp->s);
X				outstr("\r\n");
X				break;
X			}
X			ndone++;
X		}
X		if (cp == up->linep)
X			break;
X	}
X
X	if (ndone) {
X		switch (cmdchar) {
X
X		case 'd':
X			updatescreen();
X			if (ndone >= P(P_RP))
X				smsg("%d fewer line%c", ndone,
X					(ndone > 1) ? 's' : ' ');
X			break;
X
X		case 'p':
X			wait_return();
X			break;
X		}
X	} else
X		msg("No match");
X
X	free(prog);
X}
X
X/*
X * Character Searches
X */
X
Xstatic char lastc = NUL;	/* last character searched for */
Xstatic int  lastcdir;		/* last direction of character search */
Xstatic int  lastctype;		/* last type of search ("find" or "to") */
X
X/*
X * searchc(c, dir, type)
X *
X * Search for character 'c', in direction 'dir'. If type is 0, move to
X * the position of the character, otherwise move to just before the char.
X */
Xbool_t
Xsearchc(c, dir, type)
Xchar	c;
Xint	dir;
Xint	type;
X{
X	LPTR	save;
X
X	save = *Curschar;	/* save position in case we fail */
X	lastc = c;
X	lastcdir = dir;
X	lastctype = type;
X
X	/*
X	 * On 'to' searches, skip one to start with so we can repeat
X	 * searches in the same direction and have it work right.
X	 */
X	if (type)
X		(dir == FORWARD) ? oneright() : oneleft();
X
X	while ( (dir == FORWARD) ? oneright() : oneleft() ) {
X		if (gchar(Curschar) == c) {
X			if (type)
X				(dir == FORWARD) ? oneleft() : oneright();
X			return TRUE;
X		}
X	}
X	*Curschar = save;
X	return FALSE;
X}
X
Xbool_t
Xcrepsearch(flag)
Xint	flag;
X{
X	int	dir = lastcdir;
X	int	rval;
X
X	if (lastc == NUL)
X		return FALSE;
X
X	rval = searchc(lastc, flag ? OTHERDIR(lastcdir) : lastcdir, lastctype);
X
X	lastcdir = dir;		/* restore dir., since it may have changed */
X
X	return rval;
X}
X
X/*
X * "Other" Searches
X */
X
X/*
X * showmatch - move the cursor to the matching paren or brace
X */
XLPTR *
Xshowmatch()
X{
X	static	LPTR	pos;
X	int	(*move)(), inc(), dec();
X	char	initc = gchar(Curschar);	/* initial char */
X	char	findc;				/* terminating char */
X	char	c;
X	int	count = 0;
X
X	pos = *Curschar;		/* set starting point */
X
X	switch (initc) {
X
X	case '(':
X		findc = ')';
X		move = inc;
X		break;
X	case ')':
X		findc = '(';
X		move = dec;
X		break;
X	case '{':
X		findc = '}';
X		move = inc;
X		break;
X	case '}':
X		findc = '{';
X		move = dec;
X		break;
X	case '[':
X		findc = ']';
X		move = inc;
X		break;
X	case ']':
X		findc = '[';
X		move = dec;
X		break;
X	default:
X		return (LPTR *) NULL;
X	}
X
X	while ((*move)(&pos) != -1) {		/* until end of file */
X		c = gchar(&pos);
X		if (c == initc)
X			count++;
X		else if (c == findc) {
X			if (count == 0)
X				return &pos;
X			count--;
X		}
X	}
X	return (LPTR *) NULL;			/* never found it */
X}
X
X/*
X * findfunc(dir) - Find the next function in direction 'dir'
X *
X * Return TRUE if a function was found.
X */
Xbool_t
Xfindfunc(dir)
Xint	dir;
X{
X	LPTR	*curr;
X
X	curr = Curschar;
X
X	do {
X		curr = (dir == FORWARD) ? nextline(curr) : prevline(curr);
X
X		if (curr != NULL && curr->linep->s[0] == '{') {
X			setpcmark();
X			*Curschar = *curr;
X			return TRUE;
X		}
X	} while (curr != NULL);
X
X	return FALSE;
X}
X
X/*
X * The following routines do the word searches performed by the
X * 'w', 'W', 'b', 'B', 'e', and 'E' commands.
X */
X
X/*
X * To perform these searches, characters are placed into one of three
X * classes, and transitions between classes determine word boundaries.
X *
X * The classes are:
X *
X * 0 - white space
X * 1 - letters, digits, and underscore
X * 2 - everything else
X */
X
Xstatic	int	stype;		/* type of the word motion being performed */
X
X#define	C0(c)	(((c) == ' ') || ((c) == '\t') || ((c) == NUL))
X#define	C1(c)	(isalpha(c) || isdigit(c) || ((c) == '_'))
X
X/*
X * cls(c) - returns the class of character 'c'
X *
X * The 'type' of the current search modifies the classes of characters
X * if a 'W', 'B', or 'E' motion is being done. In this case, chars. from
X * class 2 are reported as class 1 since only white space boundaries are
X * of interest.
X */
Xstatic	int
Xcls(c)
Xchar	c;
X{
X	if (C0(c))
X		return 0;
X
X	if (C1(c))
X		return 1;
X
X	/*
X	 * If stype is non-zero, report these as class 1.
X	 */
X	return (stype == 0) ? 2 : 1;
X}
X
X
X/*
X * fwd_word(pos, type) - move forward one word
X *
X * Returns the resulting position, or NULL if EOF was reached.
X */
XLPTR *
Xfwd_word(p, type)
XLPTR	*p;
Xint	type;
X{
X	static	LPTR	pos;
X	int	sclass = cls(gchar(p));		/* starting class */
X
X	pos = *p;
X
X	stype = type;
X
X	/*
X	 * We always move at least one character.
X	 */
X	if (inc(&pos) == -1)
X		return NULL;
X
X	if (sclass != 0) {
X		while (cls(gchar(&pos)) == sclass) {
X			if (inc(&pos) == -1)
X				return NULL;
X		}
X		/*
X		 * If we went from 1 -> 2 or 2 -> 1, return here.
X		 */
X		if (cls(gchar(&pos)) != 0)
X			return &pos;
X	}
X
X	/* We're in white space; go to next non-white */
X
X	while (cls(gchar(&pos)) == 0) {
X		/*
X		 * We'll stop if we land on a blank line
X		 */
X		if (pos.index == 0 && pos.linep->s[0] == NUL)
X			break;
X
X		if (inc(&pos) == -1)
X			return NULL;
X	}
X
X	return &pos;
X}
X
X/*
X * bck_word(pos, type) - move backward one word
X *
X * Returns the resulting position, or NULL if EOF was reached.
X */
XLPTR *
Xbck_word(p, type)
XLPTR	*p;
Xint	type;
X{
X	static	LPTR	pos;
X	int	sclass = cls(gchar(p));		/* starting class */
X
X	pos = *p;
X
X	stype = type;
X
X	if (dec(&pos) == -1)
X		return NULL;
X
X	/*
X	 * If we're in the middle of a word, we just have to
X	 * back up to the start of it.
X	 */
X	if (cls(gchar(&pos)) == sclass && sclass != 0) {
X		/*
X		 * Move backward to start of the current word
X		 */
X		while (cls(gchar(&pos)) == sclass) {
X			if (dec(&pos) == -1)
X				return NULL;
X		}
X		inc(&pos);			/* overshot - forward one */
X		return &pos;
X	}
X
X	/*
X	 * We were at the start of a word. Go back to the start
X	 * of the prior word.
X	 */
X
X	while (cls(gchar(&pos)) == 0) {		/* skip any white space */
X		/*
X		 * We'll stop if we land on a blank line
X		 */
X		if (pos.index == 0 && pos.linep->s[0] == NUL)
X			return &pos;
X
X		if (dec(&pos) == -1)
X			return NULL;
X	}
X
X	sclass = cls(gchar(&pos));
X
X	/*
X	 * Move backward to start of this word.
X	 */
X	while (cls(gchar(&pos)) == sclass) {
X		if (dec(&pos) == -1)
X			return NULL;
X	}
X	inc(&pos);			/* overshot - forward one */
X
X	return &pos;
X}
X
X/*
X * end_word(pos, type) - move to the end of the word
X *
X * There is an apparent bug in the 'e' motion of the real vi. At least
X * on the System V Release 3 version for the 80386. Unlike 'b' and 'w',
X * the 'e' motion crosses blank lines. When the real vi crosses a blank
X * line in an 'e' motion, the cursor is placed on the FIRST character
X * of the next non-blank line. The 'E' command, however, works correctly.
X * Since this appears to be a bug, I have not duplicated it here.
X *
X * Returns the resulting position, or NULL if EOF was reached.
X */
XLPTR *
Xend_word(p, type)
XLPTR	*p;
Xint	type;
X{
X	static	LPTR	pos;
X	int	sclass = cls(gchar(p));		/* starting class */
X
X	pos = *p;
X
X	stype = type;
X
X	if (inc(&pos) == -1)
X		return NULL;
X
X	/*
X	 * If we're in the middle of a word, we just have to
X	 * move to the end of it.
X	 */
X	if (cls(gchar(&pos)) == sclass && sclass != 0) {
X		/*
X		 * Move forward to end of the current word
X		 */
X		while (cls(gchar(&pos)) == sclass) {
X			if (inc(&pos) == -1)
X				return NULL;
X		}
X		dec(&pos);			/* overshot - forward one */
X		return &pos;
X	}
X
X	/*
X	 * We were at the end of a word. Go to the end
X	 * of the next word.
X	 */
X
X	while (cls(gchar(&pos)) == 0) {		/* skip any white space */
X		if (inc(&pos) == -1)
X			return NULL;
X	}
X
X	sclass = cls(gchar(&pos));
X
X	/*
X	 * Move forward to end of this word.
X	 */
X	while (cls(gchar(&pos)) == sclass) {
X		if (inc(&pos) == -1)
X			return NULL;
X	}
X	dec(&pos);			/* overshot - forward one */
X
X	return &pos;
X}
END_OF_FILE
if test 19131 -ne `wc -c <'search.c'`; then
    echo shar: \"'search.c'\" unpacked with wrong size!
fi
# end of 'search.c'
fi
if test -f 'stevie.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'stevie.h'\"
else
echo shar: Extracting \"'stevie.h'\" \(5840 characters\)
sed "s/^X//" >'stevie.h' <<'END_OF_FILE'
X/*
X * $Header: stevie.h,v 1.15 88/11/10 08:59:10 tony Exp $
X *
X * Main header file included by all source files.
X *
X * $Log:	stevie.h,v $
X * Revision 1.15  88/11/10  08:59:10  tony
X * Added a declaration for the routine do_mlines() in main.c that checks
X * for mode lines in a file.
X * 
X * Revision 1.14  88/10/28  13:59:36  tony
X * Added missing endif.
X * 
X * Revision 1.13  88/10/27  08:23:47  tony
X *  Also added declarations for the new system-
X * dependent routines flushbuf() and doshell().
X * 
X * Revision 1.12  88/10/06  10:13:08  tony
X * Added a declaration for fixname().
X * 
X * Revision 1.11  88/08/30  20:37:10  tony
X * After much prodding from Mark, I finally added support for replace mode.
X * 
X * Revision 1.10  88/08/26  08:45:51  tony
X * Misc. changes to make lint happy.
X * 
X * Revision 1.9  88/07/09  20:38:47  tony
X * Added declarations for new routines in undo.c supporting the 'U' command.
X * 
X * Revision 1.8  88/06/26  14:52:33  tony
X * Added missing declaration for prt_line() in screen.c
X * 
X * Revision 1.7  88/06/26  14:49:21  tony
X * Added a declaration for the new routine, doglog(), in search.c.
X * 
X * Revision 1.6  88/06/20  14:52:13  tony
X * Merged in changes for BSD Unix sent in by Michael Lichter.
X * 
X * Revision 1.5  88/05/03  14:39:15  tony
X * Minor change for DOS support.
X * 
X * Revision 1.4  88/04/29  14:46:39  tony
X * Changed the declarations of repsearch() and dosearch() from void to bool_t.
X * 
X * Revision 1.3  88/04/24  21:35:56  tony
X * Added a declaration for the new routine dosub() in search.c.
X * 
X * Revision 1.2  88/03/21  16:46:57  tony
X * Moved the environmental defines out to a file named "env.h" which is
X * not maintained with RCS.
X * 
X * Revision 1.1  88/03/20  21:05:17  tony
X * Initial revision
X * 
X *
X */
X
X#include "env.h"	/* defines to establish the compile-time environment */
X
X#include <stdio.h>
X#include <ctype.h>
X
X#ifdef	BSD
X
X#include <strings.h>
X#define strchr index
X
X#else
X
X#ifdef	MINIX
X
Xextern	char	*strchr();
Xextern	char	*strrchr();
Xextern	char	*strcpy();
Xextern	char	*strcat();
Xextern	int	strlen();
X
X#else
X#include <string.h>
X#endif
X
X#endif
X
X#include "ascii.h"
X#include "keymap.h"
X#include "param.h"
X#include "term.h"
X
Xextern	char	*strchr();
X
X#define NORMAL 0
X#define CMDLINE 1
X#define INSERT 2
X#define REPLACE 3
X#define FORWARD 4
X#define BACKWARD 5
X
X/*
X * Boolean type definition and constants
X */
Xtypedef	short	bool_t;
X
X#ifndef	TRUE
X#define	FALSE	(0)
X#define	TRUE	(1)
X#endif
X
X/*
X * SLOP is the amount of extra space we get for text on a line during
X * editing operations that need more space. This keeps us from calling
X * malloc every time we get a character during insert mode. No extra
X * space is allocated when the file is initially read.
X */
X#define	SLOP	10
X
X/*
X * LINEINC is the gap we leave between the artificial line numbers. This
X * helps to avoid renumbering all the lines every time a new line is
X * inserted.
X */
X#define	LINEINC	10
X
X#define CHANGED		Changed=TRUE
X#define UNCHANGED	Changed=FALSE
X
Xstruct	line {
X	struct	line	*prev, *next;	/* previous and next lines */
X	char	*s;			/* text for this line */
X	int	size;			/* actual size of space at 's' */
X	unsigned long	num;		/* line "number" */
X};
X
X#define	LINEOF(x)	((x)->linep->num)
X
Xstruct	lptr {
X	struct	line	*linep;		/* line we're referencing */
X	int	index;			/* position within that line */
X};
X
Xtypedef	struct line	LINE;
Xtypedef	struct lptr	LPTR;
X
Xstruct charinfo {
X	char ch_size;
X	char *ch_str;
X};
X
Xextern struct charinfo chars[];
X
Xextern int State;
Xextern int Rows;
Xextern int Columns;
Xextern char *Realscreen;
Xextern char *Nextscreen;
Xextern char *Filename;
Xextern LPTR *Filemem;
Xextern LPTR *Filetop;
Xextern LPTR *Fileend;
Xextern LPTR *Topchar;
Xextern LPTR *Botchar;
Xextern LPTR *Curschar;
Xextern LPTR *Insstart;
Xextern int Cursrow, Curscol, Cursvcol, Curswant;
Xextern bool_t set_want_col;
Xextern int Prenum;
Xextern bool_t Changed;
Xextern char Redobuff[], Insbuff[];
Xextern char *Insptr;
Xextern int Ninsert;
X
Xextern char *malloc(), *strcpy();
X
X/*
X * alloc.c
X */
Xchar	*alloc(), *strsave();
Xvoid	screenalloc(), filealloc(), freeall();
XLINE	*newline();
Xbool_t	bufempty(), buf1line(), lineempty(), endofline(), canincrease();
X
X/*
X * cmdline.c
X */
Xvoid	readcmdline(), dotag(), msg(), emsg(), smsg(), gotocmd(), wait_return();
X
X/*
X * edit.c
X */
Xvoid	edit(), insertchar(), getout(), scrollup(), scrolldown(), beginline();
Xbool_t	oneright(), oneleft(), oneup(), onedown();
X
X/*
X * fileio.c
X */
Xvoid	filemess(), renum();
Xbool_t	readfile(), writeit();
X
X/*
X * help.c
X */
Xbool_t	help();
X
X/*
X * linefunc.c
X */
XLPTR	*nextline(), *prevline(), *coladvance();
X
X/*
X * main.c
X */
Xvoid	stuffin(), stuffnum(), addtobuff();
Xvoid	do_mlines();
Xint	vgetc();
Xbool_t	anyinput();
X
X/*
X * mark.c
X */
Xvoid	setpcmark(), clrall(), clrmark();
Xbool_t	setmark();
XLPTR	*getmark();
X
X/*
X * misccmds.c
X */
Xvoid	opencmd(), fileinfo(), inschar(), delline();
Xbool_t	delchar();
Xint	cntllines(), plines();
XLPTR	*gotoline();
X
X/*
X * normal.c
X */
Xvoid	normal();
Xchar	*mkstr();
X
X/*
X * param.c
X */
Xvoid	doset();
X
X/*
X * ptrfunc.c
X */
Xint	inc(), dec();
Xint	gchar();
Xvoid	pchar(), pswap();
Xbool_t	lt(), equal(), ltoreq();
X#if 0
X/* not currently used */
Xbool_t	gtoreq(), gt();
X#endif
X
X/*
X * screen.c
X */
Xvoid	updatescreen(), updateline();
Xvoid	screenclear(), cursupdate();
Xvoid	s_ins(), s_del();
Xvoid	prt_line();
X
X/*
X * search.c
X */
Xvoid	dosub(), doglob();
Xbool_t	searchc(), crepsearch(), findfunc(), dosearch(), repsearch();
XLPTR	*showmatch();
XLPTR	*fwd_word(), *bck_word(), *end_word();
X
X/*
X * undo.c
X */
Xvoid	u_save(), u_saveline(), u_clear();
Xvoid	u_lcheck(), u_lundo();
Xvoid	u_undo();
X
X/*
X * Machine-dependent routines.
X */
Xint	inchar();
Xvoid	flushbuf();
Xvoid	outchar(), outstr(), beep();
Xchar	*fixname();
X#ifndef	OS2
X#ifndef	DOS
Xvoid	remove(), rename();
X#endif
X#endif
Xvoid	windinit(), windexit(), windgoto();
Xvoid	delay();
Xvoid	doshell();
END_OF_FILE
if test 5840 -ne `wc -c <'stevie.h'`; then
    echo shar: \"'stevie.h'\" unpacked with wrong size!
fi
# end of 'stevie.h'
fi
if test -f 'tags' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'tags'\"
else
echo shar: Extracting \"'tags'\" \(7248 characters\)
sed "s/^X//" >'tags' <<'END_OF_FILE'
XBEGCHAR	search.c	/^#define	BEGCHAR(c)	(islower(c) || isupper(c) || is/
XBEGID	ctags.c	/^#define	BEGID(c)	(isalpha(c) || (c) == '_')$/
XC0	search.c	/^#define	C0(c)	(((c) == ' ') || ((c) == '\\t') || ((/
XC1	search.c	/^#define	C1(c)	(isalpha(c) || isdigit(c) || ((c) ==/
XDEFAULT1	normal.c	/^#define	DEFAULT1(x)	(((x) == 0) ? 1 : (x))$/
XFAIL	regexp.c	/^#define	FAIL(m)	{ regerror(m); return(NULL); }$/
XIDCHAR	normal.c	/^#define	IDCHAR(c)	(isalpha(c) || isdigit(c) || (c)/
XISMULT	regexp.c	/^#define	ISMULT(c)	((c) == '*' || (c) == '+' || (c)/
XISSPECIAL	edit.c	/^#define	ISSPECIAL(c)	((c) == NL || (c) == CR || (c/
XMIDID	ctags.c	/^#define	MIDID(c)	(isalpha(c) || isdigit(c) || (c) /
XMctags	ctags.c	/^main(argc, argv)$/
XMmain	main.c	/^main(argc,argv)$/
XNEXT	regexp.c	/^#define	NEXT(p)	(((*((p)+1)&0377)<<8) + (*((p)+2)&/
XOP	regexp.c	/^#define	OP(p)	(*(p))$/
XOPERAND	regexp.c	/^#define	OPERAND(p)	((p) + 3)$/
XOTHERDIR	search.c	/^#define	OTHERDIR(x)	(((x) == FORWARD) ? BACKWARD :/
XUCHARAT	regexp.c	/^#define	UCHARAT(p)	((int)*(unsigned char *)(p))$/
Xaddtobuff	main.c	/^addtobuff(s,c1,c2,c3,c4,c5,c6)$/
Xalloc	alloc.c	/^alloc(size)$/
Xanyinput	main.c	/^anyinput()$/
Xbadcmd	cmdline.c	/^badcmd()$/
Xbck_word	search.c	/^bck_word(p, type)$/
Xbcksearch	search.c	/^bcksearch(str)$/
Xbeep	dos.c	/^beep()$/
Xbeginline	edit.c	/^beginline(flag)$/
Xbuf1line	alloc.c	/^buf1line()$/
Xbufempty	alloc.c	/^bufempty()$/
Xcanincrease	alloc.c	/^canincrease(n)$/
Xchk_mline	main.c	/^chk_mline(s)$/
Xclrall	mark.c	/^clrall()$/
Xclrmark	mark.c	/^clrmark(line)$/
Xcls	search.c	/^cls(c)$/
Xcntllines	misccmds.c	/^cntllines(pbegin,pend)$/
Xcoladvance	linefunc.c	/^coladvance(p, col)$/
Xcopyline	undo.c	/^copyline(l)$/
Xcrepsearch	search.c	/^crepsearch(flag)$/
Xcstrchr	regexp.c	/^cstrchr(s, c)$/
Xcstrncmp	regexp.c	/^cstrncmp(s1, s2, n)$/
Xcursupdate	screen.c	/^cursupdate()$/
Xdec	ptrfunc.c	/^dec(lp)$/
Xdelay	dos.c	/^delay()$/
Xdelchar	misccmds.c	/^delchar(fixpos)$/
Xdelline	misccmds.c	/^delline(nlines, can_update)$/
Xdo_mlines	main.c	/^do_mlines()$/
Xdochange	normal.c	/^dochange(c1, c2, num)$/
Xdodelete	normal.c	/^dodelete(c1, c2, num)$/
Xdoecmd	cmdline.c	/^doecmd(arg, force)$/
Xdofile	ctags.c	/^dofile(fn, tp)$/
Xdofilter	normal.c	/^dofilter(c1, c2, num)$/
Xdoglob	search.c	/^doglob(lp, up, cmd)$/
Xdojoin	normal.c	/^dojoin()$/
Xdoput	normal.c	/^doput(dir)$/
Xdosearch	search.c	/^dosearch(dir,str)$/
Xdoset	param.c	/^doset(arg, inter)$/
Xdoshell	dos.c	/^doshell(cmd)$/
Xdoshift	normal.c	/^doshift(op, c1, c2, num)$/
Xdosub	search.c	/^dosub(lp, up, cmd)$/
Xdotag	cmdline.c	/^dotag(tag, force)$/
Xdoyank	normal.c	/^doyank()$/
Xedit	edit.c	/^edit()$/
Xemsg	cmdline.c	/^emsg(s)$/
Xend_word	search.c	/^end_word(p, type)$/
Xendofline	alloc.c	/^endofline(p)$/
Xequal	ptrfunc.c	/^equal(a, b)$/
Xfilealloc	alloc.c	/^filealloc()$/
Xfileinfo	misccmds.c	/^fileinfo()$/
Xfilemess	fileio.c	/^filemess(s)$/
Xfiletonext	screen.c	/^filetonext()$/
Xfindfunc	search.c	/^findfunc(dir)$/
Xfixname	dos.c	/^fixname(s)$/
Xflushbuf	dos.c	/^flushbuf()$/
Xfopenb	dos.c	/^fopenb(fname, mode)$/
Xfreeall	alloc.c	/^freeall()$/
Xfwd_word	search.c	/^fwd_word(p, type)$/
Xfwdsearch	search.c	/^fwdsearch(str)$/
Xgchar	ptrfunc.c	/^gchar(lp)$/
Xget_line	cmdline.c	/^get_line(cp)$/
Xget_range	cmdline.c	/^get_range(cp)$/
Xgetmark	mark.c	/^getmark(c)$/
Xgetout	edit.c	/^getout()$/
Xgetswitch	dos.c	/^getswitch()$/
Xgotocmd	cmdline.c	/^gotocmd(clr, firstc)$/
Xgotoline	misccmds.c	/^gotoline(n)$/
Xgt	ptrfunc.c	/^gt(a, b)$/
Xgtoreq	ptrfunc.c	/^gtoreq(a, b)$/
Xhelp	help.c	/^help()$/
Xinc	ptrfunc.c	/^inc(lp)$/
Xinchar	dos.c	/^inchar()$/
Xinschar	misccmds.c	/^inschar(c)$/
Xinsertchar	edit.c	/^insertchar(c)$/
Xinslines	normal.c	/^inslines(lp, dir, buf)$/
Xinsstr	misccmds.c	/^insstr(s)$/
Xlfiletonext	screen.c	/^lfiletonext()$/
Xlineempty	alloc.c	/^lineempty()$/
Xlnexttoscreen	screen.c	/^lnexttoscreen()$/
Xlongline	help.c	/^longline(p)$/
Xlt	ptrfunc.c	/^lt(a, b)$/
Xltoreq	ptrfunc.c	/^ltoreq(a, b)$/
Xmapstring	search.c	/^mapstring(s)$/
Xmkline	screen.c	/^mkline(n)$/
Xmkstr	normal.c	/^mkstr(c)$/
Xmkup	regexp.c	/^#define	mkup(c)		((islower(c) && reg_ic) ? toupper/
Xmsg	cmdline.c	/^msg(s)$/
Xnewline	alloc.c	/^newline(nchars)$/
Xnextfile	ctags.c	/^nextfile()	\/* returns ptr to next file to be searc/
Xnextline	linefunc.c	/^nextline(curr)$/
Xnexttoscreen	screen.c	/^nexttoscreen()$/
Xnormal	normal.c	/^normal(c)$/
Xonedown	edit.c	/^onedown(n)$/
Xoneleft	edit.c	/^oneleft()$/
Xoneright	edit.c	/^oneright()$/
Xoneup	edit.c	/^oneup(n)$/
Xopenbwd	misccmds.c	/^openbwd(can_ai)$/
Xopencmd	misccmds.c	/^opencmd(dir, can_ai)$/
Xopenfwd	misccmds.c	/^openfwd(can_ai)$/
Xoutchar	dos.c	/^outchar(c)$/
Xoutone	dos.c	/^#define	outone(c)	outbuf[bpos++] = c; if (bpos >= /
Xoutstr	dos.c	/^outstr(s)$/
Xpchar	ptrfunc.c	/^pchar(lp, c)$/
Xplines	misccmds.c	/^plines(p)$/
Xprevline	linefunc.c	/^prevline(curr)$/
Xprt_line	screen.c	/^prt_line(s)$/
Xpswap	ptrfunc.c	/^pswap(a, b)$/
Xreadcmdline	cmdline.c	/^readcmdline(firstc, cmdline)$/
Xreadfile	fileio.c	/^readfile(fname,fromp,nochangename)$/
Xreg	regexp.c	/^reg(paren, flagp)$/
Xregatom	regexp.c	/^regatom(flagp)$/
Xregbranch	regexp.c	/^regbranch(flagp)$/
Xregc	regexp.c	/^regc(b)$/
Xregcomp	regexp.c	/^regcomp(exp)$/
Xregdump	regexp.c	/^regdump(r)$/
Xregerror	search.c	/^regerror(s)$/
Xregexec	regexp.c	/^regexec(prog, string, at_bol)$/
Xreginsert	regexp.c	/^reginsert(op, opnd)$/
Xregmatch	regexp.c	/^regmatch(prog)$/
Xregnext	regexp.c	/^regnext(p)$/
Xregnode	regexp.c	/^regnode(op)$/
Xregoptail	regexp.c	/^regoptail(p, val)$/
Xregpiece	regexp.c	/^regpiece(flagp)$/
Xregprop	regexp.c	/^regprop(op)$/
Xregrepeat	regexp.c	/^regrepeat(p)$/
Xregsub	regsub.c	/^regsub(prog, source, dest)$/
Xregtail	regexp.c	/^regtail(p, val)$/
Xregtry	regexp.c	/^regtry(prog, string)$/
Xremove	minix.c	/^remove(file)$/
Xrename	minix.c	/^rename(of, nf)$/
Xrenum	fileio.c	/^renum()$/
Xrepsearch	search.c	/^repsearch(flag)$/
Xreset_tty	minix.c	/^reset_tty()$/
Xs_del	screen.c	/^s_del(row, nlines)$/
Xs_ins	screen.c	/^s_ins(row, nlines)$/
Xscreenalloc	alloc.c	/^screenalloc()$/
Xscreenclear	screen.c	/^screenclear()$/
Xscrolldown	edit.c	/^scrolldown(nlines)$/
Xscrollup	edit.c	/^scrollup(nlines)$/
Xsearchc	search.c	/^searchc(c, dir, type)$/
Xset_tty	minix.c	/^set_tty()$/
Xsetmark	mark.c	/^setmark(c)$/
Xsetpcmark	mark.c	/^setpcmark()$/
Xsetswitch	dos.c	/^setswitch(c)$/
Xshowmatch	search.c	/^showmatch()$/
Xshowparms	param.c	/^showparms(all)$/
Xsleep	dos.c	/^sleep(n)$/
Xsmsg	cmdline.c	/^smsg(s, a1, a2, a3, a4, a5, a6, a7, a8, a9)$/
Xssearch	search.c	/^ssearch(dir,str)$/
Xstartinsert	normal.c	/^startinsert(initstr, startln)$/
Xstrchr	minix.c	/^strchr(s, c)$/
Xstrcspn	regexp.c	/^strcspn(s1, s2)$/
Xstrsave	alloc.c	/^strsave(string)$/
Xstuffin	main.c	/^stuffin(s)$/
Xstuffnum	main.c	/^stuffnum(n)$/
Xt_init	term.c	/^t_init()$/
Xtabinout	normal.c	/^tabinout(inout, num)$/
Xu_clear	undo.c	/^u_clear()$/
Xu_lcheck	undo.c	/^u_lcheck()$/
Xu_lfree	undo.c	/^u_lfree()$/
Xu_lsave	undo.c	/^u_lsave(l, u)$/
Xu_lundo	undo.c	/^u_lundo()$/
Xu_save	undo.c	/^u_save(l, u)$/
Xu_saveline	undo.c	/^u_saveline()$/
Xu_undo	undo.c	/^u_undo()$/
Xupdateline	screen.c	/^updateline()$/
Xupdatescreen	screen.c	/^updatescreen()$/
Xusage	main.c	/^usage()$/
Xvgetc	main.c	/^vgetc()$/
Xvpeekc	main.c	/^vpeekc()$/
Xwait_return	cmdline.c	/^wait_return()$/
Xwindexit	dos.c	/^windexit(r)$/
Xwindgoto	dos.c	/^windgoto(r, c)$/
Xwindinit	dos.c	/^windinit()$/
Xwriteit	fileio.c	/^writeit(fname, start, end)$/
END_OF_FILE
if test 7248 -ne `wc -c <'tags'`; then
    echo shar: \"'tags'\" unpacked with wrong size!
fi
# end of 'tags'
fi
if test -f 'term.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'term.c'\"
else
echo shar: Extracting \"'term.c'\" \(1878 characters\)
sed "s/^X//" >'term.c' <<'END_OF_FILE'
Xstatic	char	RCSid[] =
X"$Header: term.c,v 1.2 88/10/31 13:10:32 tony Exp $";
X
X/*
X * Termcap initialization (optional).
X *
X * $Log:	term.c,v $
X * Revision 1.2  88/10/31  13:10:32  tony
X * Removed text after "#endif" that some compilers complain about.
X * 
X * Revision 1.1  88/10/29  14:05:27  tony
X * Initial revision
X * 
X */
X
X#include <stdio.h>
X#include "stevie.h"
X
X#ifdef	TERMCAP
X
Xstatic	char	buf[1024];	/* termcap entry read here */
Xstatic	char	cap[256];	/* capability strings go in here */
X
Xchar	*T_EL;		/* erase the entire current line */
Xchar	*T_IL;		/* insert one line */
Xchar	*T_DL;		/* delete one line */
Xchar	*T_SC;		/* save the cursor position */
Xchar	*T_ED;		/* erase display (may optionally home cursor) */
Xchar	*T_RC;		/* restore the cursor position */
Xchar	*T_CI;		/* invisible cursor (very optional) */
Xchar	*T_CV;		/* visible cursor (very optional) */
X
Xchar	*T_CM;		/* cursor motion string */
X
Xextern	int	tgetent(), tgetnum();
Xextern	char	*tgetstr();
Xextern	char	*getenv();
X
Xint
Xt_init()
X{
X	char	*term;
X	int	n;
X	char	*cp = cap;
X
X	if ((term = getenv("TERM")) == NULL)
X		return 0;
X
X	if (tgetent(buf, term) != 1)
X		return 0;
X
X	if ((n = tgetnum("li")) == -1)
X		return 0;
X	else
X		P(P_LI) = Rows = n;
X
X	if ((n = tgetnum("co")) == -1)
X		return 0;
X	else
X		Columns = n;
X
X	/*
X	 * Get mandatory capability strings.
X	 */
X	if ((T_CM = tgetstr("cm", &cp)) == NULL)
X		return 0;
X
X	if ((T_EL = tgetstr("ce", &cp)) == NULL)
X		return 0;
X
X	if ((T_IL = tgetstr("al", &cp)) == NULL)
X		return 0;
X
X	if ((T_DL = tgetstr("dl", &cp)) == NULL)
X		return 0;
X
X	if ((T_ED = tgetstr("cl", &cp)) == NULL)
X		return 0;
X
X	if ((T_SC = tgetstr("sc", &cp)) == NULL)
X		return 0;
X
X	if ((T_RC = tgetstr("rc", &cp)) == NULL)
X		return 0;
X
X	/*
X	 * Optional capabilities.
X	 */
X	if ((T_CI = tgetstr("vi", &cp)) == NULL)
X		T_CI = "";
X
X	if ((T_CV = tgetstr("ve", &cp)) == NULL)
X		T_CV = "";
X
X	return 1;
X}
X
X#endif
END_OF_FILE
if test 1878 -ne `wc -c <'term.c'`; then
    echo shar: \"'term.c'\" unpacked with wrong size!
fi
# end of 'term.c'
fi
if test -f 'term.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'term.h'\"
else
echo shar: Extracting \"'term.h'\" \(5757 characters\)
sed "s/^X//" >'term.h' <<'END_OF_FILE'
X/*
X * $Header: term.h,v 1.5 88/10/31 13:10:56 tony Exp $
X *
X * System-dependent escape sequence definitions.
X *
X * $Log:	term.h,v $
X * Revision 1.5  88/10/31  13:10:56  tony
X * Removed text after "#endif" that some compilers complain about.
X * 
X * Revision 1.4  88/10/29  14:06:41  tony
X * Added support for termcap, if TERMCAP is defined. If not defined, the
X * escape sequences are still hard-coded here.
X * 
X * Revision 1.3  88/10/27  07:54:01  tony
X * Added escape sequences for Minix-ST.
X * 
X * Revision 1.2  88/05/03  14:39:27  tony
X * Added escape sequences for DOS.
X * 
X * Revision 1.1  88/03/21  12:04:50  tony
X * Initial revision
X * 
X *
X */
X
X#ifdef	TERMCAP
X
Xextern char *T_EL;		/* erase the entire current line */
Xextern char *T_IL;		/* insert one line */
Xextern char *T_DL;		/* delete one line */
Xextern char *T_SC;		/* save the cursor position */
Xextern char *T_ED;		/* erase display (may optionally home cursor) */
Xextern char *T_RC;		/* restore the cursor position */
Xextern char *T_CI;		/* invisible cursor (very optional) */
Xextern char *T_CV;		/* visible cursor (very optional) */
X
Xextern char *T_CM;		/* cursor motion string */
X
X#else
X
X/*
X * This file contains the machine dependent escape sequences that
X * the editor needs to perform various operations. Some of the sequences
X * here are optional. Anything not available should be indicated by
X * a null string. In the case of insert/delete line sequences, the
X * editor checks the capability and works around the deficiency, if
X * necessary.
X *
X * Currently, insert/delete line sequences are used for screen scrolling.
X * There are lots of terminals that have 'index' and 'reverse index'
X * capabilities, but no line insert/delete. For this reason, the editor
X * routines s_ins() and s_del() should be modified to use 'index'
X * sequences when the line to be inserted or deleted line zero.
X */
X
X/*
X * The macro names here correspond (more or less) to the actual ANSI names
X */
X
X#ifdef	MINIX
X
X#define	T_EL	"\033[K"	/* erase the entire current line */
X#define	T_IL	"\033[L"	/* insert one line */
X#define	T_DL	"\033[M"	/* delete one line */
X#define	T_SC	"\0337"		/* save the cursor position */
X#define	T_ED	"\033[H\033[J"	/* erase display (may optionally home cursor) */
X#define	T_RC	"\0338"		/* restore the cursor position */
X#define	T_CI	""		/* invisible cursor (very optional) */
X#define	T_CV	""		/* visible cursor (very optional) */
X
X#endif
X
X#ifdef	ATARI
X#ifdef	MINIX
X
X#define	T_EL	"\033[2K"	/* erase the entire current line */
X#define	T_IL	"\033[L"	/* insert one line */
X#define	T_DL	"\033[M"	/* delete one line */
X#define	T_SC	"\0337"		/* save the cursor position */
X#define	T_ED	"\033[2J"	/* erase display (may optionally home cursor) */
X#define	T_RC	"\0338"		/* restore the cursor position */
X#define	T_CI	""		/* invisible cursor (very optional) */
X#define	T_CV	""		/* visible cursor (very optional) */
X
X#else
X
X
X#define	T_EL	"\033l"		/* erase the entire current line */
X#define	T_IL	"\033L"		/* insert one line */
X#define	T_DL	"\033M"		/* delete one line */
X#define	T_SC	"\033j"		/* save the cursor position */
X#define	T_ED	"\033E"		/* erase display (may optionally home cursor) */
X#define	T_RC	"\033k"		/* restore the cursor position */
X#define	T_CI	"\033f"		/* invisible cursor (very optional) */
X#define	T_CV	"\033e"		/* visible cursor (very optional) */
X
X#endif
X#endif
X
X#ifdef	UNIX
X/*
X * The UNIX sequences are hard-wired for ansi-like terminals. I should
X * really use termcap/terminfo, but the UNIX port was done for profiling,
X * not for actual use, so it wasn't worth the effort.
X */
X#define	T_EL	"\033[2K"	/* erase the entire current line */
X#define	T_IL	"\033[L"	/* insert one line */
X#define	T_DL	"\033[M"	/* delete one line */
X#define	T_ED	"\033[2J"	/* erase display (may optionally home cursor) */
X#define	T_SC	"\0337"		/* save the cursor position */
X#define	T_RC	"\0338"		/* restore the cursor position */
X#define	T_CI	""		/* invisible cursor (very optional) */
X#define	T_CV	""		/* visible cursor (very optional) */
X#endif
X
X#ifdef	OS2
X/*
X * The OS/2 ansi console driver is pretty deficient. No insert or delete line
X * sequences. The erase line sequence only erases from the cursor to the end
X * of the line. For our purposes that works out okay, since the only time
X * T_EL is used is when the cursor is in column 0.
X *
X * The insert/delete line sequences marked here are actually implemented in
X * the file os2.c using direct OS/2 system calls. This makes the capability
X * available for the rest of the editor via appropriate escape sequences
X * passed to outstr().
X */
X#define	T_EL	"\033[K"	/* erase the entire current line */
X#define	T_IL	"\033[L"	/* insert one line - fake (see os2.c) */
X#define	T_DL	"\033[M"	/* delete one line - fake (see os2.c) */
X#define	T_ED	"\033[2J"	/* erase display (may optionally home cursor) */
X#define	T_SC	"\033[s"	/* save the cursor position */
X#define	T_RC	"\033[u"	/* restore the cursor position */
X#define	T_CI	""		/* invisible cursor (very optional) */
X#define	T_CV	""		/* visible cursor (very optional) */
X#endif
X
X#ifdef	DOS
X/*
X * DOS sequences
X *
X * Some of the following sequences require the use of the "nansi.sys"
X * console driver. The standard "ansi.sys" driver doesn't support
X * sequences for insert/delete line.
X */
X#define	T_EL	"\033[K"	/* erase the entire current line */
X#define	T_IL	"\033[L"	/* insert line (requires nansi.sys driver) */
X#define	T_DL	"\033[M"	/* delete line (requires nansi.sys driver) */
X#define	T_ED	"\033[2J"	/* erase display (may optionally home cursor) */
X#define	T_SC	"\033[s"	/* save the cursor position */
X#define	T_RC	"\033[u"	/* restore the cursor position */
X#define	T_CI	""		/* invisible cursor (very optional) */
X#define	T_CV	""		/* visible cursor (very optional) */
X#endif
X
X#endif
END_OF_FILE
if test 5757 -ne `wc -c <'term.h'`; then
    echo shar: \"'term.h'\" unpacked with wrong size!
fi
# end of 'term.h'
fi
if test -f 'undo.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'undo.c'\"
else
echo shar: Extracting \"'undo.c'\" \(7253 characters\)
sed "s/^X//" >'undo.c' <<'END_OF_FILE'
Xstatic	char	RCSid[] =
X"$Header: undo.c,v 1.4 88/08/26 08:45:56 tony Exp $";
X
X/*
X * Undo facility
X *
X * The routines in this file comprise a general undo facility for use
X * throughout the rest of the editor. The routine u_save() is called
X * before each edit operation to save the current contents of the lines
X * to be editted. Later, u_undo() can be called to return those lines
X * to their original state. The routine u_clear() should be called
X * whenever a new file is going to be editted to clear the undo buffer.
X *
X * $Log:	undo.c,v $
X * Revision 1.4  88/08/26  08:45:56  tony
X * Misc. changes to make lint happy.
X * 
X * Revision 1.3  88/08/17  07:38:14  tony
X * Made u_save() more robust by checking for null parameters. It's up to
X * the caller to notice the problem before it causes trouble elsewhere.
X * 
X * Revision 1.2  88/07/09  20:39:11  tony
X * Added routines to implement the 'U' command.
X * 
X * Revision 1.1  88/03/20  21:10:53  tony
X * Initial revision
X * 
X */
X
X#include "stevie.h"
X
X/*
X * The next two variables mark the boundaries of the changed section
X * of the file. Lines BETWEEN the lower and upper bounds are changed
X * and originally contained the lines pointed to by u_lines. To undo
X * the last change, insert the lines in u_lines between the lower and
X * upper bounds.
X */
Xstatic	LINE	*u_lbound = NULL; /* line just prior to first changed line */
Xstatic	LINE	*u_ubound = NULL; /* line just after the last changed line */
X
Xstatic	LINE	*u_lline  = NULL; /* bounds of the saved lines */
Xstatic	LINE	*u_uline  = NULL;
X
Xstatic	int	u_col;
Xstatic	bool_t	u_valid = FALSE;  /* is the undo buffer valid */
X
X/*
X * Local forward declarations
X */
Xstatic	LINE	*copyline();
Xstatic	void	u_lsave();
Xstatic	void	u_lfree();
X
X/*
X * u_save(l, u) - save the current contents of part of the file
X *
X * The lines between 'l' and 'u' are about to be changed. This routine
X * saves their current contents into the undo buffer. The range l to u
X * is not inclusive because when we do an open, for example, there aren't
X * any lines in between. If no lines are to be saved, then l->next == u.
X */
Xvoid
Xu_save(l, u)
XLINE	*l, *u;
X{
X	LINE	*nl;			/* copy of the current line */
X
X	/*
X	 * If l or u is null, there's an error. We don't return an
X	 * indication to the caller. They should find the problem
X	 * while trying to perform whatever edit is being requested
X	 * (e.g. a join on the last line).
X	 */
X	if (l == NULL || u == NULL)
X		return;
X
X	u_clear();			/* clear the buffer, first */
X
X	u_lsave(l, u);		/* save to the "line undo" buffer, if needed */
X
X	u_lbound = l;
X	u_ubound = u;
X
X	if (l->next != u) {		/* there are lines in the middle */
X		l = l->next;
X		u = u->prev;
X
X		u_lline = nl = copyline(l);	/* copy the first line */
X		while (l != u) {
X			nl->next = copyline(l->next);
X			nl->next->prev = nl;
X			l = l->next;
X			nl = nl->next;
X		}
X		u_uline = nl;
X	} else
X		u_lline = u_uline = NULL;
X
X	u_valid = TRUE;
X	u_col = Cursvcol;
X}
X
X/*
X * u_saveline() - save the current line in the undo buffer
X */
Xvoid
Xu_saveline()
X{
X	u_save(Curschar->linep->prev, Curschar->linep->next);
X}
X
X/*
X * u_undo() - effect an 'undo' operation
X *
X * The last edit is undone by restoring the modified section of the file
X * to its original state. The lines we're going to trash are copied to
X * the undo buffer so that even an 'undo' can be undone. Rings the bell
X * if the undo buffer is empty.
X */
Xvoid
Xu_undo()
X{
X	LINE	*tl, *tu;
X
X	if (!u_valid) {
X		beep();
X		return;
X	}
X
X	/*
X	 * Get the first line of the thing we're undoing on the screen.
X	 */
X	Curschar->linep = u_lbound->next;
X	Curschar->index = 0;			/* for now */
X	if (Curschar->linep == Fileend->linep)
X		Curschar->linep = Curschar->linep->prev;
X	cursupdate();
X
X	/*
X	 * Save pointers to what's in the file now.
X	 */
X	if (u_lbound->next != u_ubound) {	/* there are lines to get */
X		tl = u_lbound->next;
X		tu = u_ubound->prev;
X		tl->prev = NULL;
X		tu->next = NULL;
X	} else
X		tl = tu = NULL;			/* no lines between bounds */
X
X	/*
X	 * Link the undo buffer into the right place in the file.
X	 */
X	if (u_lline != NULL) {		/* there are lines in the undo buf */
X
X		/*
X		 * If the top line of the screen is being undone, we need to
X		 * fix up Topchar to point to the new line that will be there.
X		 */
X		if (u_lbound->next == Topchar->linep)
X			Topchar->linep = u_lline;
X
X		u_lbound->next = u_lline;
X		u_lline->prev  = u_lbound;
X		u_ubound->prev = u_uline;
X		u_uline->next  = u_ubound;
X	} else {			/* no lines... link the bounds */
X		if (u_lbound->next == Topchar->linep)
X			Topchar->linep = u_ubound;
X		if (u_lbound == Filetop->linep)
X			Topchar->linep = u_ubound;
X			
X		u_lbound->next = u_ubound;
X		u_ubound->prev = u_lbound;
X	}
X
X	/*
X	 * If we swapped the top line, patch up Filemem appropriately.
X	 */
X	if (u_lbound == Filetop->linep)
X		Filemem->linep = Filetop->linep->next;
X
X	/*
X	 * Now save the old stuff in the undo buffer.
X	 */
X	u_lline = tl;
X	u_uline = tu;
X
X	renum();		/* have to renumber everything */
X
X	/*
X	 * Put the cursor on the first line of the 'undo' region.
X	 */
X	Curschar->linep = u_lbound->next;
X	Curschar->index = 0;
X	if (Curschar->linep == Fileend->linep)
X		Curschar->linep = Curschar->linep->prev;
X	*Curschar = *coladvance(Curschar, u_col);
X	cursupdate();
X	updatescreen();		/* now show the change */
X
X	u_lfree();		/* clear the "line undo" buffer */
X}
X
X/*
X * u_clear() - clear the undo buffer
X *
X * This routine is called to clear the undo buffer at times when the
X * pointers are about to become invalid, such as when a new file is
X * about to be editted.
X */
Xvoid
Xu_clear()
X{
X	LINE	*l, *nextl;
X
X	if (!u_valid)		/* nothing to do */
X		return;
X
X	for (l = u_lline; l != NULL ;l = nextl) {
X		nextl = l->next;
X		free(l->s);
X		free(l);
X	}
X
X	u_lbound = u_ubound = u_lline = u_uline = NULL;
X	u_valid = FALSE;
X}
X
X/*
X * The following functions and data implement the "line undo" feature
X * performed by the 'U' command.
X */
X
Xstatic	LINE	*u_line;		/* pointer to the line we last saved */
Xstatic	LINE	*u_lcopy = NULL;	/* local copy of the original line */
X
X/*
X * u_lfree() - free the line save buffer
X */
Xstatic	void
Xu_lfree()
X{
X	if (u_lcopy != NULL) {
X		free(u_lcopy->s);
X		free(u_lcopy);
X		u_lcopy = NULL;
X	}
X	u_line = NULL;
X}
X
X/*
X * u_lsave() - save the current line if necessary
X */
Xstatic	void
Xu_lsave(l, u)
XLINE	*l, *u;
X{
X
X	if (l->next != u->prev) {	/* not changing exactly one line */
X		u_lfree();
X		return;
X	}
X
X	if (l->next == u_line)		/* more edits on the same line */
X		return;
X
X	u_lfree();
X	u_line = l->next;
X	u_lcopy = copyline(l->next);
X}
X
X/*
X * u_lundo() - undo the current line (the 'U' command)
X */
Xvoid
Xu_lundo()
X{
X	if (u_lcopy != NULL) {
X		free(Curschar->linep->s);
X		Curschar->linep->s = u_lcopy->s;
X		Curschar->linep->size = u_lcopy->size;
X		free(u_lcopy);
X	} else
X		beep();
X	Curschar->index = 0;
X
X	cursupdate();
X	updatescreen();		/* now show the change */
X
X	u_lcopy = NULL;	/* can't undo this kind of undo */
X	u_line = NULL;
X}
X
X/*
X * u_lcheck() - clear the "line undo" buffer if we've moved to a new line
X */
Xvoid
Xu_lcheck()
X{
X	if (Curschar->linep != u_line)
X		u_lfree();
X}
X
X/*
X * copyline(l) - copy the given line, and return a pointer to the copy
X */
Xstatic LINE *
Xcopyline(l)
XLINE	*l;
X{
X	LINE	*nl;		/* the new line */
X
X	nl = newline(strlen(l->s) + 1);
X	strcpy(nl->s, l->s);
X
X	return nl;
X}
END_OF_FILE
if test 7253 -ne `wc -c <'undo.c'`; then
    echo shar: \"'undo.c'\" unpacked with wrong size!
fi
# end of 'undo.c'
fi
if test -f 'unix.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'unix.c'\"
else
echo shar: Extracting \"'unix.c'\" \(3697 characters\)
sed "s/^X//" >'unix.c' <<'END_OF_FILE'
Xstatic	char	RCSid[] =
X"$Header: unix.c,v 1.5 88/10/31 13:11:03 tony Exp $";
X
X/*
X * System-dependent routines for UNIX System V or Berkeley.
X *
X * $Log:	unix.c,v $
X * Revision 1.5  88/10/31  13:11:03  tony
X * Added code (optional) to support the use of termcap.
X * 
X * Revision 1.4  88/10/27  08:16:52  tony
X * Added doshell() to support ":sh" and ":!".
X * 
X * Revision 1.3  88/10/06  10:14:36  tony
X * Added fixname() routine, which does nothing under UNIX.
X * 
X * Revision 1.2  88/06/20  14:51:36  tony
X * Merged in changes for BSD Unix sent in by Michael Lichter.
X * 
X * Revision 1.1  88/03/20  21:11:02  tony
X * Initial revision
X * 
X *
X */
X
X#include "stevie.h"
X#ifdef BSD
X#include <sgtty.h>
X#else
X#include <termio.h>
X#endif
X
X/*
X * inchar() - get a character from the keyboard
X */
Xint
Xinchar()
X{
X	char	c;
X
X	flushbuf();		/* flush any pending output */
X
X	do {
X		while (read(0, &c, 1) != 1)
X			;
X	} while (c == NUL);
X
X	return c;
X}
X
X#define	BSIZE	2048
Xstatic	char	outbuf[BSIZE];
Xstatic	int	bpos = 0;
X
Xvoid
Xflushbuf()
X{
X	if (bpos != 0)
X		write(1, outbuf, bpos);
X	bpos = 0;
X}
X
X/*
X * Macro to output a character. Used within this file for speed.
X */
X#define	outone(c)	outbuf[bpos++] = c; if (bpos >= BSIZE) flushbuf()
X
X/*
X * Function version for use outside this file.
X */
Xvoid
Xoutchar(c)
Xregister char	c;
X{
X	outbuf[bpos++] = c;
X	if (bpos >= BSIZE)
X		flushbuf();
X}
X
Xvoid
Xoutstr(s)
Xregister char	*s;
X{
X	while (*s) {
X		outone(*s++);
X	}
X}
X
Xvoid
Xbeep()
X{
X	outone('\007');
X}
X
X/*
X * remove(file) - remove a file
X */
Xvoid
Xremove(file)
Xchar *file;
X{
X	unlink(file);
X}
X
X/*
X * rename(of, nf) - rename existing file 'of' to 'nf'
X */
Xvoid
Xrename(of, nf)
Xchar	*of, *nf;
X{
X	unlink(nf);
X	link(of, nf);
X	unlink(of);
X}
X
Xvoid
Xdelay()
X{
X	/* not implemented */
X}
X
X#ifdef BSD
Xstatic	struct	sgttyb	ostate;
X#else
Xstatic	struct	termio	ostate;
X#endif
X
X/*
X * Go into cbreak mode
X */
Xvoid
Xset_tty()
X{
X#ifdef BSD
X	struct	sgttyb	nstate;
X
X	ioctl(0, TIOCGETP, &ostate);
X	nstate = ostate;
X	nstate.sg_flags &= ~(XTABS|CRMOD|ECHO);
X	nstate.sg_flags |= CBREAK;
X	ioctl(0, TIOCSETN, &nstate);
X#else
X	struct	termio	nstate;
X
X	ioctl(0, TCGETA, &ostate);
X	nstate = ostate;
X	nstate.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL);
X	nstate.c_cc[VMIN] = 1;
X	nstate.c_cc[VTIME] = 0;
X	ioctl(0, TCSETAW, &nstate);
X#endif
X}
X
X/*
X * Restore original terminal modes
X */
Xvoid
Xreset_tty()
X{
X#ifdef BSD
X	ioctl(0, TIOCSETP, &ostate);
X#else
X	ioctl(0, TCSETAW, &ostate);
X#endif
X}
X
Xvoid
Xwindinit()
X{
X#ifdef	TERMCAP
X	if (t_init() != 1) {
X		fprintf(stderr, "unknown terminal type\n");
X		exit(1);
X	}
X#else
X	Columns = 80;
X	P(P_LI) = Rows = 24;
X#endif
X
X	set_tty();
X}
X
Xvoid
Xwindexit(r)
Xint r;
X{
X	reset_tty();
X	exit(r);
X}
X
X#define	outone(c)	outbuf[bpos++] = c; if (bpos >= BSIZE) flushbuf()
X
Xvoid
Xwindgoto(r, c)
Xregister int	r, c;
X{
X#ifdef	TERMCAP
X	char	*tgoto();
X#else
X	r += 1;
X	c += 1;
X#endif
X
X	/*
X	 * Check for overflow once, to save time.
X	 */
X	if (bpos + 8 >= BSIZE)
X		flushbuf();
X
X#ifdef	TERMCAP
X	outstr(tgoto(T_CM, c, r));
X#else
X	outbuf[bpos++] = '\033';
X	outbuf[bpos++] = '[';
X	if (r >= 10)
X		outbuf[bpos++] = r/10 + '0';
X	outbuf[bpos++] = r%10 + '0';
X	outbuf[bpos++] = ';';
X	if (c >= 10)
X		outbuf[bpos++] = c/10 + '0';
X	outbuf[bpos++] = c%10 + '0';
X	outbuf[bpos++] = 'H';
X#endif
X}
X
XFILE *
Xfopenb(fname, mode)
Xchar	*fname;
Xchar	*mode;
X{
X	return fopen(fname, mode);
X}
X
Xchar *
Xfixname(s)
Xchar	*s;
X{
X	return s;
X}
X
X/*
X * doshell() - run a command or an interactive shell
X */
Xvoid
Xdoshell(cmd)
Xchar	*cmd;
X{
X	char	*cp, *getenv();
X	char	cline[128];
X
X	outstr("\r\n");
X	flushbuf();
X
X	if (cmd == NULL) {
X		if ((cmd = getenv("SHELL")) == NULL)
X			cmd = "/bin/sh";
X		sprintf(cline, "%s -i", cmd);
X		cmd = cline;
X	}
X
X	reset_tty();
X	system(cmd);
X	set_tty();
X
X	wait_return();
X}
END_OF_FILE
if test 3697 -ne `wc -c <'unix.c'`; then
    echo shar: \"'unix.c'\" unpacked with wrong size!
fi
# end of 'unix.c'
fi
if test -f 'version.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'version.c'\"
else
echo shar: Extracting \"'version.c'\" \(7218 characters\)
sed "s/^X//" >'version.c' <<'END_OF_FILE'
Xstatic	char	RCSid[] =
X"$Header: version.c,v 3.45 88/11/10 09:00:06 tony Exp $";
X
X/*
X * Contains the declaration of the global version number variable.
X *
X * changes by Robert Regn:
X * Tue Nov 29 12:12:10 MET 1988
X * Better handling of Read/only files
X * preserving link structure and modes
X * recognizes % in cmdline - better algorithm for # also
X * edit an empty file is now possible
X * restore tty if the file to edit is too big
X * if writeit in :w fails, :q is not further possible
X *
X * $Log:	version.c,v $
X * Revision 3.45  88/11/10  09:00:06  tony
X * Added support for mode lines. Strings like "vi:stuff:" or "ex:stuff:"
X * occurring in the first or last 5 lines of a file cause the editor to
X * pretend that "stuff" was types as a colon command. This examination
X * is done only if the parameter "modelines" (or "ml") is set. This is
X * not enabled, by default, because of the security implications involved.
X * 
X * Revision 3.44  88/11/01  21:34:11  tony
X * Fixed a couple of minor points for Minix, and improved the speed of
X * the 'put' command dramatically.
X * 
X * Revision 3.43  88/10/31  13:11:33  tony
X * Added optional support for termcap. Initialization is done in term.c
X * and also affects the system-dependent files. To enable termcap in those
X * environments that support it, define the symbol "TERMCAP" in env.h
X * 
X * Revision 3.42  88/10/27  18:30:19  tony
X * Removed support for Megamax. Added '%' as an alias for '1,$'. Made the
X * 'r' command more robust. Now prints the string on repeated searches.
X * The ':=" command now works. Some pointer operations are now safer.
X * The ":!" and ":sh" now work correctly. Re-organized the help screens
X * a little.
X * 
X * Revision 3.41  88/10/06  10:15:00  tony
X * Fixed a bug involving ^Y that occurs when the cursor is on the last
X * line, and the line above the screen is long. Also hacked up fileio.c
X * to pass pathnames off to fixname() for system-dependent processing.
X * Used under DOS & OS/2 to trim parts of the name appropriately.
X * 
X * Revision 3.40  88/09/16  08:37:36  tony
X * No longer beeps when repeated searches fail.
X * 
X * Revision 3.39  88/09/06  06:51:07  tony
X * Fixed a bug with shifts that was introduced when replace mode was added.
X * 
X * Revision 3.38  88/08/31  20:48:28  tony
X * Made another fix in search.c related to repeated searches.
X * 
X * Revision 3.37  88/08/30  20:37:16  tony
X * After much prodding from Mark, I finally added support for replace mode.
X * 
X * Revision 3.36  88/08/26  13:46:34  tony
X * Added support for the '!' (filter) operator.
X * 
X * Revision 3.35  88/08/26  08:46:01  tony
X * Misc. changes to make lint happy.
X * 
X * Revision 3.34  88/08/25  15:13:36  tony
X * Fixed a bug where the cursor didn't land on the right place after
X * "beginning-of-word" searches if the word was preceded by the start
X * of the line and a single character.
X * 
X * Revision 3.33  88/08/23  12:53:08  tony
X * Fixed a bug in ssearch() where repeated searches ('n' or 'N') resulted
X * in dynamic memory being referenced after it was freed.
X * 
X * Revision 3.32  88/08/17  07:37:07  tony
X * Fixed a general problem in u_save() by checking both parameters for
X * null values. The specific symptom was that a join on the last line of
X * the file would crash the editor.
X * 
X * Revision 3.31  88/07/09  20:39:38  tony
X * Implemented the "line undo" command (i.e. 'U').
X * 
X * Revision 3.30  88/06/28  07:54:22  tony
X * Fixed a bug involving redo's of the '~' command. The redo would just
X * repeat the replacement last performed instead of switching the case of
X * the current character.
X * 
X * Revision 3.29  88/06/26  14:53:19  tony
X * Added support for a simple form of the "global" command. It supports
X * commands of the form "g/pat/d" or "g/pat/p", to delete or print lines
X * that match the given pattern. A range spec may be used to limit the
X * lines to be searched.
X * 
X * Revision 3.28  88/06/25  21:44:22  tony
X * Fixed a problem in the processing of colon commands that caused
X * substitutions of patterns containing white space to fail.
X * 
X * Revision 3.27  88/06/20  14:52:21  tony
X * Merged in changes for BSD Unix sent in by Michael Lichter.
X * 
X * Revision 3.26  88/06/10  13:44:06  tony
X * Fixed a bug involving writing out files with long pathnames. A small
X * fixed size buffer was being used. The space for the backup file name
X * is now allocated dynamically.
X * 
X * Revision 3.25  88/05/04  08:29:02  tony
X * Fixed a minor incompatibility with vi involving the 'G' command. Also
X * changed the RCS version number of version.c to match the actual version
X * of the editor.
X * 
X * Revision 1.12  88/05/03  14:39:52  tony
X * Changed the screen representation of the ascii character DELETE to be
X * compatible with vi. Also merged in support for DOS.
X * 
X * Revision 1.11  88/05/02  21:38:21  tony
X * The code that reads files now handles boundary/error conditions much
X * better, and generates status/error messages that are compatible with
X * the real vi. Also fixed a bug in repeated reverse searches that got
X * inserted in the recent changes to search.c.
X * 
X * Revision 1.10  88/05/02  07:35:41  tony
X * Fixed a bug in the routine plines() that was introduced during changes
X * made for the last version.
X * 
X * Revision 1.9  88/05/01  20:10:19  tony
X * Fixed some problems with auto-indent, and added support for the "number"
X * parameter.
X * 
X * Revision 1.8  88/04/30  20:00:49  tony
X * Added support for the auto-indent feature.
X * 
X * Revision 1.7  88/04/29  14:50:11  tony
X * Fixed a class of bugs involving commands like "ct)" where the cursor
X * motion part of the operator can fail. If the motion failed, the operator
X * was continued, with the cursor position unchanged. Cases like this were
X * modified to abort the operation if the motion fails.
X * 
X * Revision 1.6  88/04/28  08:19:35  tony
X * Modified Henry Spencer's regular expression library to support new
X * features that couldn't be done easily with the existing interface.
X * This code is now a direct part of the editor source code. The editor
X * now supports the "ignorecase" parameter, and multiple substitutions
X * per line, as in "1,$s/foo/bar/g".
X * 
X * Revision 1.5  88/04/24  21:38:00  tony
X * Added preliminary support for the substitute command. Full range specs.
X * are supported, but only a single substitution is allowed on each line.
X * 
X * Revision 1.4  88/04/23  20:41:01  tony
X * Worked around a problem with adding lines to the end of the buffer when
X * the cursor is at the bottom of the screen (in misccmds.c). Also fixed a
X * bug that caused reverse searches from the start of the file to bomb.
X * 
X * Revision 1.3  88/03/24  08:57:00  tony
X * Fixed a bug in cmdline() that had to do with backspacing out of colon
X * commands or searches. Searches were okay, but colon commands backed out
X * one backspace too early.
X * 
X * Revision 1.2  88/03/21  16:47:55  tony
X * Fixed a bug in renum() causing problems with large files (>6400 lines).
X * Also moved system-specific defines out of stevie.h and into a new file
X * named env.h. This keeps volatile information outside the scope of RCS.
X * 
X * Revision 1.1  88/03/20  21:00:39  tony
X * Initial revision
X * 
X */
X
Xchar	*Version = "STEVIE - Version 3.45";
END_OF_FILE
if test 7218 -ne `wc -c <'version.c'`; then
    echo shar: \"'version.c'\" unpacked with wrong size!
fi
# end of 'version.c'
fi
if test -f 'tos_c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'tos_c'\"
else
echo shar: Extracting \"'tos_c'\" \(7273 characters\)
sed "s/^X//" >'tos_c' <<'END_OF_FILE'
Xstatic	char	RCSid[] =
X"$Header: tos.c,v 1.3 88/10/27 08:18:07 tony Exp $";
X
X/*
X * System-dependent routines for the Atari ST.
X *
X * $Log:	tos.c,v $
X * Revision 1.3  88/10/27  08:18:07  tony
X * Added a null flushbuf() for compatibility with versions that buffer
X * output. Added fixname() to do appropriate name truncation. Added
X * a quick doshell() that supports commands, but not interactive shells.
X * Removed support for Megamax.
X * 
X * Revision 1.2  88/09/23  19:35:44  tony
X * Added mktemp() for use with the '!' operator. Also added support for
X * the Sozobon C compiler.
X * 
X * Revision 1.1  88/03/20  21:10:42  tony
X * Initial revision
X * 
X *
X */
X
X#include "stevie.h"
X
X#include <osbind.h>
X
X/*
X * The following buffer is used to work around a bug in TOS. It appears that
X * unread console input can cause a crash, but only if console output is
X * going on. The solution is to always grab any unread input before putting
X * out a character. The following buffer holds any characters read in this
X * fashion. The problem can be easily produced because STEVIE can't yet keep
X * up with the normal auto-repeat rate in insert mode.
X */
X#define	IBUFSZ	128
X
Xstatic long inbuf[IBUFSZ];	/* buffer for unread input */
Xstatic long *inptr = inbuf;	/* where to put next character */
X
X/*
X * inchar() - get a character from the keyboard
X *
X * Certain special keys are mapped to values above 0x80. These
X * mappings are defined in keymap.h. If the key has a non-zero
X * ascii value, it is simply returned. Otherwise it may be a
X * special key we want to map.
X *
X * The ST has a bug involving keyboard input that seems to occur
X * when typing quickly, especially typing capital letters. Sometimes
X * a value of 0x02540000 is read. This doesn't correspond to anything
X * on the keyboard, according to my documentation. My solution is to
X * loop when any unknown key is seen. Normally, the bell is rung to
X * indicate the error. If the "bug" value is seen, we ignore it completely.
X */
Xint
Xinchar()
X{
X	for (;;) {
X		long c, *p;
X
X		/*
X		 * Get the next input character, either from the input
X		 * buffer or directly from TOS.
X		 */
X		if (inptr != inbuf) {	/* input in the buffer, use it */
X			c = inbuf[0];
X			/*
X			 * Shift everything else in the buffer down. This
X			 * would be cleaner if we used a circular buffer,
X			 * but it really isn't worth it.
X			 */
X			inptr--;
X			for (p = inbuf; p < inptr ;p++)
X				*p = *(p+1);
X		} else
X			c = Crawcin();
X	
X		if ((c & 0xff) != 0)
X			return ((int) c);
X	
X		switch ((int) (c >> 16) & 0xff) {
X	
X		case 0x62: return K_HELP;
X		case 0x61: return K_UNDO;
X		case 0x52: return K_INSERT;
X		case 0x47: return K_HOME;
X		case 0x48: return K_UARROW;
X		case 0x50: return K_DARROW;
X		case 0x4b: return K_LARROW;
X		case 0x4d: return K_RARROW;
X		case 0x29: return K_CGRAVE;	/* control grave accent */
X		
X		/*
X		 * Occurs due to a bug in TOS.
X		 */
X		case 0x54:
X			break;
X		/*
X		 * Add the function keys here later if we put in support
X		 * for macros.
X		 */
X	
X		default:
X			beep();
X			break;
X	
X		}
X	}
X}
X
X/*
X * get_inchars - snarf away any pending console input
X *
X * If the buffer overflows, we discard what's left and ring the bell.
X */
Xstatic void
Xget_inchars()
X{
X	while (Cconis()) {
X		if (inptr >= &inbuf[IBUFSZ]) {	/* no room in buffer? */
X			Crawcin();		/* discard the input */
X			beep();			/* and sound the alarm */
X		} else
X			*inptr++ = Crawcin();
X	}
X}
X
Xvoid
Xoutchar(c)
Xchar	c;
X{
X	get_inchars();
X	Cconout(c);
X}
X
Xvoid
Xoutstr(s)
Xregister char	*s;
X{
X	get_inchars();
X	Cconws(s);
X}
X
X/*
X * flushbuf() - a no-op for TOS
X */
Xvoid
Xflushbuf()
X{
X}
X
X#define	BGND	0
X#define	TEXT	3
X
X/*
X * vbeep() - visual bell
X */
Xstatic void
Xvbeep()
X{
X	int	text, bgnd;		/* text and background colors */
X	long	l;
X
X	text = Setcolor(TEXT, -1);
X	bgnd = Setcolor(BGND, -1);
X
X	Setcolor(TEXT, bgnd);		/* swap colors */
X	Setcolor(BGND, text);
X
X	for (l=0; l < 5000 ;l++)	/* short pause */
X		;
X
X	Setcolor(TEXT, text);		/* restore colors */
X	Setcolor(BGND, bgnd);
X}
X
Xvoid
Xbeep()
X{
X	if (P(P_VB))
X		vbeep();
X	else
X		outchar('\007');
X}
X
X/*
X * remove(file) - remove a file
X */
Xvoid
Xremove(file)
Xchar *file;
X{
X	Fdelete(file);
X}
X
X/*
X * rename(of, nf) - rename existing file 'of' to 'nf'
X */
Xvoid
Xrename(of, nf)
Xchar	*of, *nf;
X{
X	Fdelete(nf);		/* if 'nf' exists, remove it */
X	Frename(0, of, nf);
X}
X
Xvoid
Xwindinit()
X{
X	if (Getrez() == 0)
X		Columns = 40;		/* low resolution */
X	else
X		Columns = 80;		/* medium or high */
X
X	P(P_LI) = Rows = 25;
X
X	Cursconf(1,NULL);
X}
X
Xvoid
Xwindexit(r)
Xint r;
X{
X	exit(r);
X}
X
Xvoid
Xwindgoto(r, c)
Xint	r, c;
X{
X	outstr("\033Y");
X	outchar(r + 040);
X	outchar(c + 040);
X}
X
X/*
X * System calls or library routines missing in TOS.
X */
X
Xvoid
Xsleep(n)
Xint n;
X{
X	int k;
X
X	k = Tgettime();
X	while ( Tgettime() <= k+n )
X		;
X}
X
Xvoid
Xdelay()
X{
X	long	n;
X
X	for (n = 0; n < 8000 ;n++)
X		;
X}
X
Xint
Xsystem(cmd)
Xchar	*cmd;
X{
X	char	arg[1];
X
X	arg[0] = (char) 0;	/* no arguments passed to the shell */
X
X	if (Pexec(0, cmd, arg, 0L) < 0)
X		return -1;
X	else
X		return 0;
X}
X
X#ifdef	SOZOBON
X
XFILE *
Xfopenb(fname, mode)
Xchar	*fname;
Xchar	*mode;
X{
X	char	modestr[10];
X
X	sprintf(modestr, "%sb", mode);
X
X	return fopen(fname, modestr);
X}
X
X#endif
X
X#ifndef	SOZOBON
X/*
X * getenv() - get a string from the environment
X *
X * Both Alcyon and Megamax are missing getenv(). This routine works for
X * both compilers and with the Beckemeyer and Gulam shells. With gulam,
X * the env_style variable should be set to either "mw" or "gu".
X */
Xchar *
Xgetenv(name)
Xchar *name;
X{
X	extern long _base;
X	char *envp, *p;
X
X	envp = *((char **) (_base + 0x2c));
X
X	for (; *envp ;envp += strlen(envp)+1) {
X		if (strncmp(envp, name, strlen(name)) == 0) {
X			p = envp + strlen(name);
X			if (*p++ == '=')
X				return p;
X		}
X	}
X	return (char *) 0;
X}
X#endif
X
X/*
X * mktemp() - quick hack since there isn't one here
X */
Xchar *
Xmktemp(name)
Xchar	*name;
X{
X	int	num;		/* pasted into the string to make it unique */
X	char	cbuf[7];
X	char	*s;		/* where the X's start in name */
X	int	fd;
X
X	if ((s = strchr(name, 'X')) == NULL)	/* needs to be an X */
X		return (char *) NULL;
X
X	if (strlen(s) != 6)			/* should be 6 X's */
X		return (char *) NULL;
X
X	for (num = 0; num < 1000 ;num++) {
X		sprintf(cbuf, "%06d", num);
X		strcpy(s, cbuf);
X		if ((fd = open(name, 0)) < 0)
X			return name;
X		close(fd);
X	}
X	return (char *) NULL;
X}
X
Xvoid
Xdoshell(cmd)
Xchar	*cmd;
X{
X	if (cmd == NULL) {
X		emsg("Sorry, no interactive shell");
X		return;
X	}
X	system(cmd);
X	wait_return();
X}
X
X#define	PSIZE	128
X
X/*
X * fixname(s) - fix up a dos name
X *
X * Takes a name like:
X *
X *	\x\y\z\base.ext
X *
X * and trims 'base' to 8 characters, and 'ext' to 3.
X */
Xchar *
Xfixname(s)
Xchar	*s;
X{
X	char	*strchr(), *strrchr();
X	static	char	f[PSIZE];
X	char	base[32];
X	char	ext[32];
X	char	*p;
X	int	i;
X
X	strcpy(f, s);
X
X	for (i=0; i < PSIZE ;i++)
X		if (f[i] == '/')
X			f[i] = '\\';
X
X	/*
X	 * Split the name into directory, base, extension.
X	 */
X	if ((p = strrchr(f, '\\')) != NULL) {
X		strcpy(base, p+1);
X		p[1] = '\0';
X	} else {
X		strcpy(base, f);
X		f[0] = '\0';
X	}
X
X	if ((p = strchr(base, '.')) != NULL) {
X		strcpy(ext, p+1);
X		*p = '\0';
X	} else
X		ext[0] = '\0';
X
X	/*
X	 * Trim the base name if necessary.
X	 */
X	if (strlen(base) > 8)
X		base[8] = '\0';
X	
X	if (strlen(ext) > 3)
X		ext[3] = '\0';
X
X	/*
X	 * Paste it all back together
X	 */
X	strcat(f, base);
X	strcat(f, ".");
X	strcat(f, ext);
X
X	return f;
X}
END_OF_FILE
if test 7273 -ne `wc -c <'tos_c'`; then
    echo shar: \"'tos_c'\" unpacked with wrong size!
fi
# end of 'tos_c'
fi
echo shar: End of shell archive.
exit 0