v08i001: Georgia Tech 'se' Screen Editor

sources-request at mirror.UUCP sources-request at mirror.UUCP
Tue Jan 27 07:15:12 AEST 1987


Submitted by: emoryu1!arnold (Arnold D. Robbins)
Mod.sources: Volume 8, Issue 1
Archive-name: se/Part01


Here is the second release of the Georgia Tech Screen Editor, 'se'.
There were enough changes that a whole new posting is warranted.

Major Changes:
	All Georgia Tech specific stuff removed.
	It understands window size changes on 4.3BSD and ATT Unix PC/3B1
	Support for the shared library on the ATT Unix PC/3B1
	Considerable source code reorganization in certain files.

Enjoy,

Arnold Robbins

#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	README
#	ascii.h
#	constdefs.h
#	docmd1.c
#	docmd2.c
export PATH; PATH=/bin:$PATH
echo shar: extracting "'README'" '(6131 characters)'
if test -f 'README'
then
	echo shar: will not over-write existing file "'README'"
else
cat << \SHAR_EOF > 'README'
# 
# $Header: README,v 1.3 86/07/17 17:52:23 arnold Exp $
# 
# $Log:	README,v $
# Revision 1.3  86/07/17  17:52:23  arnold
# Fixed some spelling mistakes.
# 
# Revision 1.2  86/07/17  17:19:06  arnold
# Removed GT specific stuff, added discussion of window sensitivities.
# 
# Revision 1.1  86/05/06  13:34:09  osadr
# Initial revision
# 
# 
# 
README:

This directory contains the source files for the Unix version of the Georgia
Tech Screen Editor "Se".  It has three subdirectories which contain things
that se needs. Here is a rundown of the various files.

Files containing documentation are:

README		-- this file.
se.m4		-- nroff manual page for se (has to be munged to create se.1).
scriptse.1	-- nroff manual page for scriptse.

The header files are:

ascii.h		-- definition of ASCII mnemonics and control characters.
extern.h	-- external data definitions for the screen editor.
se.h		-- global #define's for the screen editor.
constdefs.h	-- global constants, also used by files in subdirectories.

The C source files are:

main.c		-- main program and declaration of globals, initialization.
edit.c		-- main command loop to get and execute commands, file handling.
docmd1.c	-- command decoder and functions for most commands.
docmd2.c	-- functions for the rest of the commands.
misc.c		-- miscellanious functions.
scratch.c	-- scratch file manipulating functions.
screen.c	-- routines to keep track of the screen contents.
term.c		-- routines for changing the terminal.

The subdirectories are:

libchangetty	-- routines to change the terminal driver back and forth.
pat		-- pattern matching routines.
se_h		-- contains help scripts for all commands in se.

Miscellanious files:

where		-- shell file to determine System V (R 1 or 2), 4.1, or 4.2 BSD.
m4munge		-- manipulate output of where for m4 for se man page
makefile	-- the makefile for make(1).
print2		-- inode used by make for printing only changed stuff.

scriptse.c	-- quick and dirty C program to make scripts for se.
scriptse.1	-- manual page for same.

Executable files:

se		-- executable version of the screen editor.
scriptse	-- the executable version of the script maker.


Conditional Compilation flags:

	The flag HARD_TERMS, if added to the CFLAGS macro in the makefile,
will remove the terminal-independent code which uses termlib, and put back
the original, terminal-types-hardwired-into-the-program code.  The only
reason to do this is if se has to run on a system without the termlib package.
Using termlib, se is considerably smaller, as well as more flexible.

	The flag OLD_SCRATCH, if added to the CFLAGS macro in the makefile,
will cause se to use the original, linked-list method for keeping track of
lines in the buffer.  This method is faster for rearranging lines, but
considerably slower for simply looking up lines.  Currently, se uses the
method given in Software Tools in Pascal, which keeps the lines in order in
an array.  It is slower at rearranging, but as fast as possible for finding
lines in the buffer.  This version also takes less data and code space.

	The flag OLD_GLOB, if added to the CFLAGS macro in the makefile,
will keep se from special casing commands whose effect is to reverse the
order of the lines in the buffer.  The special casing code can save an
*incredible* amount of time for this pathological case, so it is best to leave
things alone.  This only applies to the Software Tools in Pascal style
line handling.

	The flag LOG_USAGE, if added to the CFLAGS macro in the makefile,
will cause se to write usage "statistics" to a log file, consisting of the
login name and time and date when an individual used se. The logfile path
name in the log() routine in edit.c should be changed to use a system
accounting file somewhere.  It currently creates a file in /usr/tmp.

	The 'where' command creates a file called 'flags', which the makefile
cats inside ``s.  These define and/or undefine the flags USG for System V,
S5R2 if for Release 2, BSD for 4.1 and 4.2, and BSD4_2 for 4.2 specific code.

Miscellanious:

	Code which is dependent on the Berkeley job control stuff is also
conditionally compiled in, so that on systems without it, it won't get in
the way.

	Code has been added which should allow se to come up under USG Unix
5.0 (System V).  If S5R2 is defined, se will use the terminfo package.
Otherwise, it assumes Release 1, and that the BSD termlib package has
been ported and is available.

	There is code in term.c to test if se is running on a system with
windows or not. It currently understands the windows on an ATT Unix PC (or 3B1)
and the windowing ioctl's in BRL Unix, which should be identical to those
in 4.3 BSD.

Comments:

	It is a big piece of software. But, if you 1) read both Software Tools
and Software Tools in Pascal (the chapters on editing and pattern matching, if
you don't want to read all of the books), and 2) take your time, you should be
able to understand, and eventually make changes to it, as necessary.

Authors:

	Se started out as the version of 'ed' that came with the book 'Software
Tools', by Kernighan and Plauger, which was written in Ratfor. On the Pr1me
computers at the School of Information and Computer Science at Georgia Tech,
Dan Forsyth, Perry Flinn, and Alan Akin added all the enhancements suggested
in the exercises in the book, and some more of their own. Jack Waugh made
extensive modifications to turn it into a screen editor; further work was done
by Dan Forsyth. All of this was in an improved Georgia Tech version of Ratfor.

	Later, Dan Forsyth, then (and now) at Medical Systems Development
Corporation, converted the Ratfor version into C, for Berkeley Unix (4.1 BSD).
At Georgia Tech, Arnold Robbins took the C version and added many new features
and improvements, the most important of which was termcap support and System V
support. The existing help screens were edited and completed at that time, as
well. This was completed in early 1985.

	Arnold Robbins is now at ...!gatech!emory!arnold, and will make every
reasonable attempt to answer any questions anyone may have about it, but in
no way promises to support or enhance 'se'.
SHAR_EOF
fi # end of overwriting check
echo shar: extracting "'ascii.h'" '(1389 characters)'
if test -f 'ascii.h'
then
	echo shar: will not over-write existing file "'ascii.h'"
else
cat << \SHAR_EOF > 'ascii.h'
/*
 * $Header: ascii.h,v 1.1 86/05/06 13:35:19 osadr Exp $
 */

/*
 * $Log:	ascii.h,v $
 * Revision 1.1  86/05/06  13:35:19  osadr
 * Initial revision
 * 
 * 
 */

/*
** ascii.h
**
** definitions of ASCII mnemonics and synonyms.
*/

#ifndef	_ASCII_H
#define	_ASCII_H
#define	NUL	'\0'
#define	SOH	'\001'
#define	STX	'\002'
#define	ETX	'\003'
#define	EOT	'\004'
#define	ENQ	'\005'
#define	ACK	'\006'
#define	BEL	'\007'
#define	BS	'\010'
#define	HT	'\011'
#define	LF	'\012'
#define	VT	'\013'
#define	FF	'\014'
#define	CR	'\015'
#define	SO	'\016'
#define	SI	'\017'
#define	DLE	'\020'
#define	DC1	'\021'
#define	DC2	'\022'
#define	DC3	'\023'
#define	DC4	'\024'
#define	NAK	'\025'
#define	SYN	'\026'
#define	ETB	'\027'
#define	CAN	'\030'
#define	EM	'\031'
#define	SUB	'\032'
#define	ESC	'\033'
#define	FS	'\034'
#define	GS	'\035'
#define	RS	'\036'
#define	US	'\037'
#define	SP	'\040'
#define	DEL	'\177'
#define	CTRL_A	SOH
#define	CTRL_B	STX
#define	CTRL_C	ETX
#define	CTRL_D	EOT
#define	CTRL_E	ENQ
#define	CTRL_F	ACK
#define	CTRL_G	BEL
#define	CTRL_H	BS
#define	CTRL_I	HT
#define	CTRL_J	LF
#define	CTRL_K	VT
#define	CTRL_L	FF
#define	CTRL_M	CR
#define	CTRL_N	SO
#define	CTRL_O	SI
#define	CTRL_P	DLE
#define	CTRL_Q	DC1
#define	CTRL_R	DC2
#define	CTRL_S	DC3
#define	CTRL_T	DC4
#define	CTRL_U	NAK
#define	CTRL_V	SYN
#define	CTRL_W	ETB
#define	CTRL_X	CAN
#define	CTRL_Y	EM
#define	CTRL_Z	SUB
#endif
SHAR_EOF
fi # end of overwriting check
echo shar: extracting "'constdefs.h'" '(325 characters)'
if test -f 'constdefs.h'
then
	echo shar: will not over-write existing file "'constdefs.h'"
else
cat << \SHAR_EOF > 'constdefs.h'
/*
 * $Header: constdefs.h,v 1.1 86/05/06 13:35:37 osadr Exp $
 */

/*
 * $Log:	constdefs.h,v $
 * Revision 1.1  86/05/06  13:35:37  osadr
 * Initial revision
 * 
 * 
 */

/*
** constdefs.h
**
** Standard macro definitions for se screen editor
*/

#define	EOS '\0'
#define	ERR (-3)
#define	OK (-2)
#define	NO 0
#define	YES 1
SHAR_EOF
fi # end of overwriting check
echo shar: extracting "'docmd1.c'" '(27523 characters)'
if test -f 'docmd1.c'
then
	echo shar: will not over-write existing file "'docmd1.c'"
else
cat << \SHAR_EOF > 'docmd1.c'
#ifndef lint
static char RCSid[] = "$Header: docmd1.c,v 1.3 86/07/17 17:19:37 arnold Exp $";
#endif

/*
 * $Log:	docmd1.c,v $
 * Revision 1.3  86/07/17  17:19:37  arnold
 * Some general code cleaning up.
 * 
 * Revision 1.2  86/07/11  15:10:20  osadr
 * Removed Georgia Tech specific items.
 * 
 * Revision 1.1  86/05/06  13:36:44  osadr
 * Initial revision
 * 
 * 
 */

/*
** docmd1.c
**
** main command processor.  routines for individual commands
*/

#include "se.h"
#include "extern.h"

/* static data definitions -- variables only needed in this file */
static char Tlpat[MAXPAT] = "";	/* saved character list for y/t command */
static char Tabstr[MAXLINE] = "";	/* string representation of tab stops */
static char Ddir = FORWARD;		/* delete direction */
static int Compress;			/* compress/expand tabs on read/write */

/* docmd --- handle all commands except globals */

int docmd (lin, i, glob, status)
char lin[];
int i, glob, *status;
{
	char file[MAXLINE], sub[MAXPAT];
	char kname;
	int gflag, line3, pflag, flag, fflag, junk, allbut, tflag;
	int append (), ckchar (), ckp (), ckupd (), copy ();
	int delete (), domark (), doopt (), doprnt (), doread ();
	int doshell ();
	int dotlit (), doundo (), dowrit (), getfn (), getkn ();
	int getone (), getrange (), getrhs (), getstr (), inject ();
	int join (), makset (), move (), nextln (), optpat ();
	int prevln (), substr (), draw_box ();
	char *expand_env ();


	*status = ERR;
	if (intrpt ())  /* catch a pending interrupt */
		return (*status);

	switch (lin[i]) {
	case APPENDCOM:
	case UCAPPENDCOM:
		if (lin[i + 1] == '\n' || lin[i + 1] == ':')
		{
			defalt (Curln, Curln);
			if (lin[i + 1] == '\n')
			{
				/* avoid updating with inline insertion */
				adjust_window (Line1, Line2);
				updscreen ();
			}
			*status = append (Line2, &lin[i + 1]);
		}
		break;

	case PRINTCUR:
		if (lin[i + 1] == '\n')
		{
			defalt (Curln, Curln);
			saynum (Line2);
			*status = OK;
		}
		break;

	case OVERLAYCOM:
	case UCOVERLAYCOM:
		defalt (Curln, Curln);
		if (lin[i + 1] == '\n')
			overlay (status);
		break;

	case CHANGE:
	case UCCHANGE:
		defalt (Curln, Curln);
		if (Line1 <= 0)
			Errcode = EORANGE;
		else if (lin[i + 1] == '\n' || lin[i + 1] == ':')
		{
			if (lin[i + 1] == '\n')
			{
				/* avoid updating with inline insertion */
				adjust_window (Line2, Line2);
				updscreen ();
			}
			First_affected = min (First_affected, Line1);
			if (lin[i + 1] == '\n')
				warn_deleted (Line1, Line2);
			*status = append (Line2, &lin[i + 1]);
			if (*status != ERR)
			{
				line3 = Curln;
				delete (Line1, Line2, status);
				Curln = line3 - (Line2 - Line1 + 1);
				/* adjust for deleted lines */
			}
		}
		break;

	case DELCOM:
	case UCDELCOM:
		if (ckp (lin, i + 1, &pflag, status) == OK)
		{
			defalt (Curln, Curln);
			if (delete (Line1, Line2, status) == OK
			    && Ddir == FORWARD
			    && nextln (Curln) != 0)
				Curln = nextln (Curln);
		}
		break;

	case INSERT:
	case UCINSERT:
		defalt (Curln, Curln);
		if (Line1 <= 0)
			Errcode = EORANGE;
		else if (lin[i + 1] == '\n' || lin[i + 1] == ':')
		{
			if (lin[i + 1] == '\n')
			{
				/* avoid updating with inline insertion */
				adjust_window (Line1, Line2);
				updscreen ();
			}
			*status = append (prevln (Line2), &lin[i + 1]);
		}
		break;

	case MOVECOM:
	case UCMOVECOM:
		i++;
		if (getone (lin, &i, &line3, status) == EOF)
			*status = ERR;
		if (*status == OK && ckp (lin, i, &pflag, status) == OK)
		{
			defalt (Curln, Curln);
			*status = move (line3);
		}
		break;

	case COPYCOM:
	case UCCOPYCOM:
		i++;
		if (getone (lin, &i, &line3, status) == EOF)
			*status = ERR;
		if (*status == OK && ckp (lin, i, &pflag, status) == OK)
		{
			defalt (Curln, Curln);
			*status = copy (line3);
		}
		break;

	case SUBSTITUTE:
	case UCSUBSTITUTE:
		i++;
		if (lin[i] == '\n')
		{
			/* turn "s\n" into "s//%/\n" */
			lin[i+0] = '/';
			lin[i+1] = '/';
			lin[i+2] = '%';
			lin[i+3] = '/';
			lin[i+4] = '\n';
			lin[i+5] = EOS;
			Peekc = SKIP_RIGHT;
		}
		else
		{
			/* try to handle "s/stuff\n" */
			int j, missing_delim;

			missing_delim = YES;
			for (j = i + 1; lin[j] != '\n'; j++)
				if (lin[j] == ESCAPE && lin[j+1] == lin[i])
					j++;	/* skip esc, loop continues */
				else if (lin[j] == lin[i])
				{
					missing_delim = NO;
					break;	/* for */
				}

			if (missing_delim)
			{
				for (; lin[j] != EOS; j++)
					;
				j--;		/* j now at newline */

				lin[j] = lin[i];	/* delim */
				lin[++j] = '\n';
				lin[++j] = EOS;
				Peekc = SKIP_RIGHT;
				/* rest of routines will continue to fix up */
			}
		}

		if (optpat (lin, &i) == OK
		    && getrhs (lin, &i, sub, &gflag) == OK
		    && ckp (lin, i + 1, &pflag, status) == OK)
		{
			defalt (Curln, Curln);
			*status = subst (sub, gflag, glob);
		}
		break;

	case TLITCOM:
	case UCTLITCOM:
		i++;
		if (lin[i] == '\n')
		{
			/* turn "y\n" into "y//%/\n" */
			lin[i+0] = '/';
			lin[i+1] = '/';
			lin[i+2] = '%';
			lin[i+3] = '/';
			lin[i+4] = '\n';
			lin[i+5] = EOS;
			Peekc = SKIP_RIGHT;
		}
		else
		{
			/* try to handle "y/stuff\n" */
			int j, missing_delim;

			missing_delim = YES;
			for (j = i + 1; lin[j] != '\n'; j++)
				if (lin[j] == ESCAPE && lin[j+1] == lin[i])
					j++;	/* skip esc, loop continues */
				else if (lin[j] == lin[i])
				{
					missing_delim = NO;
					break;	/* for */
				}

			if (missing_delim)
			{
				for (; lin[j] != EOS; j++)
					;
				j--;		/* j now at newline */

				lin[j] = lin[i];	/* delim */
				lin[++j] = '\n';
				lin[++j] = EOS;
				Peekc = SKIP_RIGHT;
				/* rest of routines will continue to fix up */
			}
		}

		if (getrange (lin, &i, Tlpat, MAXPAT, &allbut) == OK
		    && makset (lin, &i, sub, MAXPAT) == OK
		    && ckp (lin, i + 1, &pflag, status) == OK)
		{
			defalt (Curln, Curln);
			*status = dotlit (sub, allbut);
		}
		break;

	case JOINCOM:
	case UCJOINCOM:
		i++;
		if (getstr (lin, &i, sub, MAXPAT) == OK
		    && ckp (lin, i + 1, &pflag, status) == OK)
		{
			defalt (prevln (Curln), Curln);
			*status = join (sub);
		}
		break;

	case UNDOCOM:
	case UCUNDOCOM:
		i++;
		defalt (Curln, Curln);
		if (ckchar (UCDELCOM, DELCOM, lin, &i, &flag, status) == OK
		    && ckp (lin, i, &pflag, status) == OK)
			*status = doundo (flag, status);
		break;

	case ENTER:
	case UCENTER:
		i++;
		if (Nlines != 0)
			Errcode = EBADLNR;
		else if (ckupd (lin, &i, ENTER, status) == OK
		    && ckchar ('x', 'X', lin, &i, &tflag, status) == OK)
			if (getfn (lin, i - 1, file) == OK)
			{
				strcpy (Savfil, expand_env (file));
				mesg (Savfil, FILE_MSG);
				clrbuf ();
				mkbuf ();
				dfltsopt (file);
				*status = doread (0, file, tflag);
				First_affected = 0;
				Curln = min (1, Lastln);
				Buffer_changed = NO;
			}
			else
				*status = ERR;
		break;

	case PRINTFIL:
	case UCPRINTFIL:
		if (Nlines != 0)
			Errcode = EBADLNR;
		else if (getfn (lin, i, file) == OK)
		{
			strcpy (Savfil, expand_env (file));
			mesg (Savfil, FILE_MSG);
			*status = OK;
		}
		break;

	case READCOM:
	case UCREADCOM:
		i++;
		if (ckchar ('x', 'X', lin, &i, &tflag, status) == OK)
			if (getfn (lin, i - 1, file) == OK)
			{
				defalt (Curln, Curln);
				*status = doread (Line2, file, tflag);
			}
		break;

	case WRITECOM:
	case UCWRITECOM:
		i++;
		flag = NO;
		fflag = NO;
		junk = ckchar ('>', '+', lin, &i, &flag, &junk);
		if (flag == NO)
			junk = ckchar ('!', '!', lin, &i, &fflag, &junk);
		junk = ckchar ('x', 'X', lin, &i, &tflag, &junk);
		if (getfn (lin, i - 1, file) == OK)
		{
			defalt (1, Lastln);
			*status = dowrit (Line1, Line2, file, flag, fflag, tflag);
		}
		break;

	case PRINT:
	case UCPRINT:
		if (lin[i + 1] == '\n')
		{
			defalt (1, Topln);
			*status = doprnt (Line1, Line2);
		}
		break;

	case PAGECOM:
		defalt (1, min (Lastln, Botrow - Toprow + Topln));
		if (Line1 <= 0)
			Errcode = EORANGE;
		else if (lin[i + 1] == '\n')
		{
			Topln = Line2;
			Curln = Line2;
			First_affected = Line2;
			*status = OK;
		}
		break;

	case NAMECOM:
	case UCNAMECOM:
		i++;
		if (getkn (lin, &i, &kname, DEFAULTNAME) != ERR
		    && lin[i] == '\n')
			uniquely_name (kname, status);
		break;

	case MARKCOM:
	case UCMARKCOM:
		i++;
		if (getkn (lin, &i, &kname, DEFAULTNAME) != ERR
		    && lin[i] == '\n')
		{
			defalt (Curln, Curln);
			*status = domark (kname);
		}
		break;

	case '\n':
		line3 = nextln (Curln);
		defalt (line3, line3);
		*status = doprnt (Line2, Line2);
		break;

	case LOCATECMD:
	case UCLOCATECMD:
		if (lin[i+1] == '\n')
		{
			char *sysname ();

			remark (sysname ());
			*status = OK;
		}
		break;

	case OPTCOM:
	case UCOPTCOM:
		if (Nlines == 0)
			*status = doopt (lin, &i);
		else
			Errcode = EBADLNR;
		break;

	case QUIT:
	case UCQUIT:
		i++;
		if (Nlines != 0)
			Errcode = EBADLNR;
		else if (ckupd (lin, &i, QUIT, status) == OK)
			if (lin[i] == '\n')
				*status = EOF;
			else
				*status = ERR;
		break;

	case HELP:
	case UCHELP:
		i++;
		if (Nlines == 0)
			dohelp (lin, &i, status);
		else
			Errcode = EBADLNR;
		break;

	case MISCCOM:		/* miscellanious features */
	case UCMISCCOM:
		i++;
		switch (lin[i]) {
		case 'b':	/* draw box */
		case 'B':
			defalt (Curln, Curln);
			i++;
			*status = draw_box (lin, &i);
			break;

		default:
			Errcode = EWHATZAT;
			break;
		}
		break;

	case SHELLCOM:
		i++;
		defalt (Curln, Curln);
		*status = doshell (lin, &i);
		break;

	default:
		Errcode = EWHATZAT;	/* command not recognized */
		break;
	}

	if (*status == OK)
		Probation = NO;

	return (*status);
}


/* dohelp --- display documentation about editor */

dohelp (lin, i, status)
char lin[];
int *i, *status;
{
	char filename[MAXLINE];
	char swt_filename[MAXLINE];
	static char helpdir[] = "/usr/local/lib/se_h";	/* help scripts */
	int j;
	FILE *fp, *fopen ();

	SKIPBL (lin, *i);
	if (lin[*i] == NEWLINE)
		sprintf (filename, "%s/elp", helpdir);
	else
	{
		/* build filename from text after "h" */
		sprintf (filename, "%s/%s", helpdir, &lin[*i]);
		j = strlen (filename);
		filename[j-1] = EOS;	/* lop off newline */
	}

	/* map to lower case */
	for (j = 0; filename[j] != EOS; j++)
		if (isupper (filename[j]))
			filename[j] = tolower (filename[j]);

	*status = OK;
	if ((fp = fopen (filename, "r")) == NULL)
	{
		*status = ERR;
		Errcode = ENOHELP;
	}
	else
	{
#ifdef u3b2
		/* 3B2 seems to have problems with stdio and malloc... */
		char buf[BUFSIZ];
		setbuf (fp, buf);
#endif

		/* status is OK */
		display_message (fp);	/* display the help script */
		fclose (fp);
	}
}


/* doopt --- interpret option command */

int doopt (lin, i)
char lin[];
int *i;
{
	int temp, line, stat;
	char tempstr[4];
	int ret;
	int dosopt ();
	int ctoi ();

	(*i)++;
	ret = ERR;

	switch (lin[*i]) {

	case 'g':		/* substitutes in a global can(not) fail */
	case 'G':
		if (lin[*i + 1] == '\n')
		{
			ret = OK;
			Globals = ! Globals;	/* toggle */
			if (Globals == YES)
				remark ("failed global substitutes continue");
			else
				remark ("failed global substitutes stop");
		}
		break;

	case 'h':
	case 'H':		/* do/don't use hardware insert/delete */
		if (lin[*i + 1] == '\n')
		{
			ret = OK;
			No_hardware = ! No_hardware;
			if (No_hardware == YES)
				remark ("no line insert/delete");
			else
				remark ("line insert/delete");
		}
		break;

	case 'k':		/* tell user if buffer saved or not */
	case 'K':
		if (lin[*i + 1] == '\n')
		{
			ret = OK;
			if (Buffer_changed == YES)
				remark ("not saved");
			else
				remark ("saved");
		}
		break;


	case 'z':	/* suspend the editor process */
	case 'Z':
		if (lin[*i + 1] == '\n')
		{
			ret = OK;
#ifdef BSD
			if (Catching_stops)
			{
				if (Buffer_changed == YES)
					fprintf (stderr, "WARNING: buffer not saved\r\n");
				kill (getpid(), SIGTSTP);
				/* stop_hdlr() will do all the work for us */
			}
#else
			remark ("process suspension not available");
#endif
		}
		break;

	case 't':	/* set or display tab stops for expanding tabs */
	case 'T':
		++(*i);
		if (lin[*i] == '\n')
		{
			remark (Tabstr);
			ret = OK;
		}
		else
		{
			ret = settab (&lin[*i]);
			if (ret == OK)
				strcpy (Tabstr, &lin[*i]);
			else	/* defaults were set */
				strcpy (Tabstr, "+4");
		}
		break;

	case 'w':	/* set or display warning column */
	case 'W':
		++(*i);
		if (lin[*i] == '\n')
			ret = OK;
		else
		{
			temp = ctoi (lin, i);
			if (lin[*i] == '\n')
				if (temp > 0 && temp < MAXLINE - 3)
				{
					ret = OK;
					Warncol = temp;
				}
				else
					Errcode = ENONSENSE;
		}
		if (ret == OK)
			saynum (Warncol);
		break;

	case '-':	/* fix window in place on screen, or erase it */
		++(*i);
		if (getnum (lin, i, &line, &stat) == EOF)
		{
			mesg ("", HELP_MSG);
			if (Toprow > 0)
			{
				Topln = max (1, Topln - Toprow);
				Toprow = 0;
				First_affected = Topln;
			}
			ret = OK;
		}
		else if (stat != ERR && lin[*i] == '\n')
			if (Toprow + (line - Topln + 1) < Cmdrow)
			{
				Toprow += line - Topln + 1;
				Topln = line + 1;
				for (temp = 0; temp < Ncols; temp++)
					load ('-', Toprow - 1, temp);
				if (Topln > Lastln)
					adjust_window (1, Lastln);
				if (Curln < Topln)
					Curln = min (Topln, Lastln);
				ret = OK;
			}
			else
				Errcode = EORANGE;
		break;

	case 'a':	/* toggle absolute line numbering */
	case 'A':
		if (lin[*i + 1] == '\n')
		{
			Absnos = ! Absnos;
			ret = OK;
		}
		break;

	case 'c':	/* toggle case option */
	case 'C':
		if (lin[*i + 1] == '\n')
		{
			ret = OK;
			Invert_case = ! Invert_case;
			if (Rel_a == 'A')
			{
				Rel_a = 'a';
				Rel_z = 'z';
			}
			else
			{
				Rel_a = 'A';
				Rel_z = 'Z';
			}
		}

		mesg (Invert_case ? "CASE" : "", CASE_MSG);
		break;

	case 'd':	/* set or display placement of "." after a delete */
	case 'D':
		if (lin[*i + 1] == '\n')
		{
			if (Ddir == FORWARD)
				remark (">");
			else
				remark ("<");
			ret = OK;
		}
		else if (lin[*i + 2] != '\n')
			Errcode = EODLSSGTR;
		else if (lin[*i + 1] == '>')
		{
			ret = OK;
			Ddir = FORWARD;
		}
		else if (lin[*i + 1] == '<')
		{
			ret = OK;
			Ddir = BACKWARD;
		}
		else
			Errcode = EODLSSGTR;
		break;

	case 'v':	/* set or display overlay column */
	case 'V':
		++(*i);
		if (lin[*i] == '\n')
		{
			if (Overlay_col == 0)
				remark ("$");
			else
				saynum (Overlay_col);
			ret = OK;
		}
		else
		{
			if (lin[*i] == '$' && lin[*i + 1] == '\n')
			{
				Overlay_col = 0;
				ret = OK;
			}
			else
			{
				temp = ctoi (lin, i);
				if (lin[*i] == '\n')
				{
					Overlay_col = temp;
					ret = OK;
				}
				else
					Errcode = ENONSENSE;
			}
		}
		break;

	case 'u':	/* set or display character for unprintable chars */
	case 'U':
		if (lin[*i + 1] == '\n')
		{
			ret = OK;
			tempstr[0] = tempstr[2] = '"';
			tempstr[1] = Unprintable;
			tempstr[3] = EOS;
			remark (tempstr);
		}
		else if (lin[*i + 2] == '\n')
		{
			if (lin[*i + 1] < ' ' || lin[*i + 1] >= DEL)
				Errcode = ENONSENSE;
			else 
			{
				ret = OK;
				if (Unprintable != lin[*i + 1])
				{
					Unprintable = lin[*i + 1];
					First_affected = Topln;
				}
			}
		}
		break;

	case 'l':	/* set or display line number display option */
	case 'L':
		if (lin[*i+1] == '\n')
		{
			Nchoise = EOS;
			ret = OK;
		}
		else if (lin[*i + 2] == '\n' && 
		    (lin[*i + 1] == CURLINE || lin[*i + 1] == LASTLINE
		    || lin[*i + 1] == TOPLINE))
		{
			Nchoise = lin[*i + 1];
			ret = OK;
		}
		else if (lin[*i + 1] == 'm' || lin[*i + 1] == 'M')
		{
			/* set or display the left margin */
			(*i)++;
			if (lin[*i + 1] == '\n')
			{
				saynum (Firstcol + 1);
				ret = OK;
			}
			else 
			{
				(*i)++;
				temp = ctoi (lin, i);
				if (lin[*i] == '\n')
					if (temp > 0 && temp < MAXLINE)
					{
						First_affected = Topln;
						Firstcol = temp - 1;
						ret = OK;
					}
					else
						Errcode = ENONSENSE;
			}
		}
		break;

	case 'f':	/* fortran (ugh, yick, gross) options */
	case 'F':
		if (lin[*i + 1] == '\n')
			ret = dosopt ("f");
		break;

	case 's':	/* set source options */
	case 'S':
		ret = dosopt (&lin[*i + 1]);
		break;

	case 'i':	/* set or display indent option */
	case 'I':
		++(*i);
		if (lin[*i] == '\n')
			ret = OK;
		else if ((lin[*i] == 'a' || lin[*i] == 'A') && lin[*i + 1] == '\n')
		{
			Indent = 0;
			ret = OK;
		}
		else
		{
			temp = ctoi (lin, i);
			if (lin[*i] == '\n')
				if (temp > 0 && temp < MAXLINE - 3)
				{
					ret = OK;
					Indent = temp;
				}
				else
					Errcode = ENONSENSE;
		}
		if (ret == OK)
			if (Indent > 0)
				saynum (Indent);
			else
				remark ("auto");
		break;

	case 'm':	/* toggle mail notification */
	case 'M':
		if (lin[*i + 1] == '\n')
		{
			Notify = ! Notify;	/* toggle notification */
			remark (Notify ? "notify on" : "notify off");
			ret = OK;
		}
		break;

	case 'x':
	case 'X':	/* toggle tab compression */
		if (lin[*i + 1] == '\n')
		{
			ret = OK;
			Compress = ! Compress;
			mesg (Compress ? "XTABS" : "", COMPRESS_MSG);
		}
		break;

	case 'y':	/* encrypt files */
	case 'Y':
		if (lin[*i + 1] == '\n')
		{
		crypt_toggle:
			ret = OK;
			Crypting = ! Crypting;
			if (Crypting )
				do {
					getkey ();
					if (Key[0] == EOS)
						remark ("Empty keys are not allowed.\n");
				} while (Key[0] == EOS);
			else
				Key[0] = EOS;
		}
		else
		{
			register int j;

			ret = OK;
			(*i)++;		/* *i was the 'y' */
			while (isspace (lin[*i]) && lin[*i] != '\n')
				(*i)++;
			if (lin[*i] != '\n' && lin[*i] != EOS)
			{
				for (j = 0; lin[*i] != '\n' && lin[*i] != EOS;
				    j++, (*i)++)
					Key[j] = lin[*i];
				Key[j] = EOS;
				Crypting = YES;
			}
			else
				goto crypt_toggle;
		}
		mesg (Crypting ? "ENCRYPT" : "", CRYPT_MSG);
		break;

	default:
		Errcode = EOWHAT;

	}

	return (ret);
}


/* domark --- name lines line1 through line2 as kname */

int domark (kname)
char kname;
{
	int line;
	int ret;
	register LINEDESC *k;
	LINEDESC *getind ();

	if (Line1 <= 0)
	{
		Errcode = EORANGE;
		ret = ERR;
	}
	else
	{
		k = getind (Line1);
		for (line = Line1; line <= Line2; line++)
		{
			if (intrpt())
				return (ERR);
			k -> Markname = kname;
			k = NEXTLINE(k);
		}
		ret = OK;
	}
	return (ret);
}


/* doprnt --- set curln, locate window */

int doprnt (from, to)
int from, to;
{

	if (from <= 0)
	{
		Errcode = EORANGE;
		return (ERR);
	}

	adjust_window (from, to);
	Curln = to;
	return (OK);
}


/* doread --- read "file" after "line" */

int doread (line, file, tflag)
int line;
char *file;
int tflag;
{
	register int count, len, i;
	int ret;
	int strlen ();
	FILE *fd;
	FILE *fopen (), *crypt_open ();
	char lin1[MAXLINE], lin2[MAXLINE];
	char *fgets ();
	register LINEDESC *ptr;
	LINEDESC *sp_inject ();
	LINEDESC *getind ();
	char *expand_env ();

	file = expand_env (file);	/* expand $HOME, etc. */

	if (Savfil[0] == EOS)
	{
		strcpy (Savfil, file);
		mesg (Savfil, FILE_MSG);
	}

	if (Crypting)
		fd = crypt_open (file, "r");
	else
		fd = fopen (file, "r");

	if (fd == NULL)
	{
		ret = ERR;
		Errcode = ECANTREAD;
	}
	else
	{
		First_affected = min (First_affected, line + 1);
		ptr = getind (line);
		ret = OK;
#ifndef OLD_SCRATCH
		Curln = line;
#endif
		remark ("reading");
		for (count = 0; fgets (lin1, MAXLINE, fd) != NULL; count++)
		{
			if (intrpt ())
			{
				ret = ERR;
				break;
			}
			if (Compress == NO && tflag == NO)
				ptr = sp_inject (lin1, strlen (lin1), ptr);
			else
			{
				len = 0;
				for (i = 0; lin1[i] != EOS && len < MAXLINE - 1; i++)
					if (lin1[i] != '\t')
						lin2[len++] = lin1[i];
					else
						do
							lin2[len++] = ' ';
						while (len % 8 != 0 
						    && len < MAXLINE - 1);
				lin2[len] = EOS;
				if (len >= MAXLINE)
				{
					ret = ERR;
					Errcode = ETRUNC;
				}
				ptr = sp_inject (lin2, len, ptr);
			}
			if (ptr == NOMORE)
			{
				ret = ERR;
				break;
			}
		}
		if (Crypting)
			crypt_close (fd);
		else
			fclose (fd);
		saynum (count);
		Curln = line + count;
		svins (line, count);
	}

	return (ret);
}


/* dosopt --- set source language-related options */

int dosopt (lin)
char lin[];
{
	char lang[8];
	int i;
	int strbsr ();
	static struct {
		char *txt;
		int val;
	} ltxt[] = {    
		"",     1,
		"as",   2,
		"c",    3,
		"d",    1,
		"data", 1,
		"f",    4,
		"h",    3,
		"n",    1,
		"nr",   1,
		"nroff",1,
		"p",	3,
		"r",    3,
		"s",    2,
	};

	i = 0;
	getwrd (lin, &i, lang, 8);

	strmap (lang, 'a');

	i = strbsr ((char *)ltxt, sizeof (ltxt), sizeof (ltxt[0]), lang);
	if (i == EOF)
	{
		Errcode = ENOLANG;
		return (ERR);
	}

	/*
	 * these are all the same under Unix, so factor
	 * them out of the switch.
	 */

	Rel_a = 'A';
	Rel_z = 'Z';
	Invert_case = NO;
	Compress = NO;

	switch (ltxt[i].val) {
	case 1:
		Warncol = 74;
		strcpy (Tabstr, "+4");
		break;

	case 2:
		Warncol = 72;
		strcpy (Tabstr, "17+8");
		Compress = YES;		/* except this one */
		break;

	case 3:
		Warncol = 74;
		strcpy (Tabstr, "+8");
		break;

	case 4:
		Warncol = 72;
		strcpy (Tabstr, "7+3");
		break;
	}

	settab (Tabstr);
	mesg (Invert_case == YES ? "CASE" : "", CASE_MSG);
	mesg (Compress == YES ? "XTABS" : "", COMPRESS_MSG);

	return (OK);
}


/* dotlit --- transliterate characters */

int dotlit (sub, allbut)
char sub[];
int allbut;
{
	char new[MAXLINE];
	char kname;
	int collap, x, i, j, line, lastsub, status;
	int ret;
	LINEDESC *inx;
	LINEDESC *gettxt (), *getind ();

	ret = ERR;
	if (Line1 <= 0)
	{
		Errcode = EORANGE;
		return (ret);
	}

	if (First_affected > Line1)
		First_affected = Line1;

	lastsub = strlen (sub) - 1;
	if ((strlen (Tlpat)  - 1) > lastsub || allbut == YES)
		collap = YES;
	else
		collap = NO;

	for (line = Line1; line <= Line2; line++)
	{
		if (intrpt ())	/* check for interrupts */
			return (ERR);

		inx = gettxt (line);	/* get text of line into txt, return index */
		j = 0;
		for (i = 0; Txt[i] != EOS && Txt[i] != '\n'; i++)
		{
			x = xindex (Tlpat, Txt[i], allbut, lastsub);
			if (collap == YES && x >= lastsub && lastsub >= 0)	/* collapse */
			{
				new[j] = sub[lastsub];
				j++;
				for (i++; Txt[i] != EOS && Txt[i] != '\n'; i++)
				{
					x = xindex (Tlpat, Txt[i], allbut, lastsub);
					if (x < lastsub)
						break;
				}
			}
			if (Txt[i] == EOS || Txt[i] == '\n')
				break;
			if (x >= 0 && lastsub >= 0)	/* transliterate */
			{
				new[j] = sub[x];
				j++;
			}
			else if (x < 0)		/* copy */
			{
				new[j] = Txt[i];
				j++;
			}
			/* else
				delete */
		}

		if (Txt[i] == '\n')	/* add a newline, if necessary */
		{
			new[j] = '\n';
			j++;
		}
		new[j] = EOS;		/* add the EOS */

		kname = inx -> Markname;	/* save the markname */
		delete (line, line, &status);
		ret = inject (new);
		if (ret == ERR)
			break;
		inx = getind (Curln);
		inx -> Markname = kname;	/* set markname */
		ret = OK;
		Buffer_changed = YES;
	}

	return (ret);
}

/* doundo --- restore last set of lines deleted */

int doundo (dflg, status)
int dflg;
int *status;
{
	LINEDESC *l1, *l2, *k1, *k2;
	LINEDESC *getind ();
	int oldcnt;
	int nextln (), prevln ();

	*status = ERR;
	if (dflg == NO && Line1 <= 0)
		Errcode = EORANGE;
	else if (Limbo == NOMORE)
		Errcode = ENOLIMBO;
	else if (Line1 > Line2)
		Errcode = EBACKWARD;
	else if (Line2 > Lastln)
		Errcode = ELINE2;
	else
	{
		*status = OK;
		Curln = Line2;
#ifdef OLD_SCRATCH
		k1 = getind (Line2);
		k2 = getind (nextln (Line2));
		l1 = Limbo;
		l2 = l1 -> Prevline;
		relink (k1, l1, l2, k2);
		relink (l2, k2, k1, l1);
#else
		blkmove (Limbo - Buf, MAXBUF - 1, Line2);
#endif
		svins (Line2, Limcnt);
		oldcnt = Limcnt;
		Limcnt = 0;
		Limbo = NOMORE;
		Lastln += oldcnt;
		if (dflg == NO)
			delete (Line1, Line2, status);
		Curln += oldcnt;
		if (First_affected > Line1)
			First_affected = Line1;
	}

	return (*status);
}

/* dowrit --- write "from" through "to" into file */

int dowrit (from, to, file, aflag, fflag, tflag)
int from, to, aflag, fflag, tflag;
char *file;
{
	FILE *fd;
	FILE *fopen (), *crypt_open ();
	register int line, ret, i, j;
	int strcmp (), access ();
	char tabs[MAXLINE];
	register LINEDESC *k;
	LINEDESC *getind ();
	char *expand_env ();

	ret = ERR;
	if (from <= 0)
		Errcode = EORANGE;

	else
	{
		file = expand_env (file);	/* expand $HOME, etc. */

		if (aflag == YES)
		{
			if (Crypting)
				fd = crypt_open (file, "a");
			else
				fd = fopen (file, "a");
		}
		else if (strcmp (file, Savfil) == 0 || fflag == YES
		    || Probation == WRITECOM || access (file, 0) == -1)
		{
			if (Crypting)
				fd = crypt_open (file, "w");
			else
				fd = fopen (file, "w");
		}
		else
		{
			Errcode = EFEXISTS;
			Probation = WRITECOM;
			return (ret);
		}
		if (fd == NULL)
			Errcode = ECANTWRITE;
		else
		{
			ret = OK;
			remark ("writing");
			k = getind (from);
			for (line = from; line <= to; line++)
			{
				if (intrpt ())
					return (ERR);
				gtxt (k);
				if (Compress == NO && tflag == NO)
					fputs (Txt, fd);
				else
				{
					for (i = 0; Txt[i] == ' '; i++)
						;
					for (j = 0; j < i / 8; j++)
						tabs[j] = '\t';
					tabs[j] = EOS;
					fputs (tabs, fd);
					fputs (&Txt[j * 8], fd);
				}
				k = NEXTLINE(k);
			}
			if (Crypting)
				crypt_close (fd);
			else
				fclose (fd);
			sync ();	/* just in case the system crashes */
			saynum (line - from);
			if (from == 1 && line - 1 == Lastln)
				Buffer_changed = NO;
		}
	}
	return (ret);
}

/* expand_env --- expand environment variables in file names */

char *expand_env (file)
register char *file;
{
	register int i, j, k;		/* indices */
	char *getenv ();
	char var[MAXLINE];		/* variable name */
	char *val;			/* value of environment variable */
	static char buf[MAXLINE * 2];	/* expanded file name, static to not go away */


	i = j = k = 0;
	while (file[i] != EOS)
	{
		if (file[i] == ESCAPE)
		{
			if (file[i+1] == '$')
			{
				buf[j++] = file[++i];	/* the actual $ */
				i++;	/* for next time around the loop */
			}
			else
				buf[j++] = file[i++];	/* the \ */
		}
		else if (file[i] != '$')	/* normal char */
			buf[j++] = file[i++];
		else			/* environment var */
		{
			i++;	/* skip $ */
			k = 0;
			while (file[i] != '/' && file[i] != EOS)
				var[k++] = file[i++];	/* get var name */
			var[k] = EOS;

			if ((val = getenv (var)) != NULL)
				for (k = 0; val[k] != EOS; k++)
					buf[j++] = val[k];
					/* copy val into file name */
			else if (file[i] == '/')
				i++;	/* var not in enviroment; strip */
					/* extra slash */
		}
	}
	buf[j] = EOS;

	return (buf);
}

/* crypt_open -- run files through crypt */

FILE *crypt_open (file, mode)
char *file, *mode;
{
	char buf[MAXLINE];
	FILE *fp, *popen ();

	if (! Crypting)
		return (NULL);

	while (Key[0] == EOS)
	{
		getkey ();
		if (Key[0] == EOS)
			fprintf (stderr, "The key must be non-empty!\r\n");
	}

	switch (mode[0]) {
	case 'r':
		sprintf (buf, "crypt %s < %s", Key, file);
		fp = popen (buf, "r");
		return (fp);		/* caller checks for NULL or not */
		break;

	case 'w':
		sprintf (buf, "crypt %s > %s", Key, file);
		fp = popen (buf, "w");
		return (fp);		/* caller checks for NULL or not */
		break;

	case 'a':
		sprintf (buf, "crypt %s >> %s", Key, file);
		fp = popen (buf, "w");
		return (fp);		/* caller checks for NULL or not */
		break;
	
	default:
		return (NULL);
	}
}

crypt_close (fp)
FILE *fp;
{
	pclose (fp);
}

/* getkey -- get an encryption key from the user */

#define repeat		do
#define until(cond)	while(!(cond))

getkey ()
{
	char *getpass ();	/* get input w/out echoing on screen */

	clrscreen ();		/* does NOT wipe out Screen_image */
	tflush ();

	ttynormal ();

	repeat
	{
		strcpy (Key, getpass ("Enter encryption key: "));
		if (strcmp (Key, getpass ("Again: ")) != 0)
		{
			Key[0] = EOS;
			fprintf (stderr, "didn't work. try again.\n");
		}
		/* else
			all ok */
	} until (Key[0] != EOS);

	ttyedit ();

	restore_screen ();
}
SHAR_EOF
fi # end of overwriting check
echo shar: extracting "'docmd2.c'" '(18109 characters)'
if test -f 'docmd2.c'
then
	echo shar: will not over-write existing file "'docmd2.c'"
else
cat << \SHAR_EOF > 'docmd2.c'
#ifndef lint
static char RCSid[] = "$Header: docmd2.c,v 1.3 86/07/17 17:20:29 arnold Exp $";
#endif

/*
 * $Log:	docmd2.c,v $
 * Revision 1.3  86/07/17  17:20:29  arnold
 * Some general code cleaning up.
 * 
 * Revision 1.2  86/07/11  15:11:04  osadr
 * Removed Georgia Tech specific code.
 * 
 * Revision 1.1  86/05/06  13:36:57  osadr
 * Initial revision
 * 
 * 
 */

/*
** docmd2.c
**
** routines to actually execute commands
*/

#include "se.h"
#include "extern.h"


/* append --- append lines after "line" */

append (line, str)
int line;
char str[];
{
	char lin[MAXLINE];
	char term;
	int ret;
	int len, i, dpos, dotseen;
	int inject ();

	Curln = line;

	if (str[0] == ':')	/* text to be added is in the command line */
		ret = inject (&str[1]);
	else
	{
		Cmdrow = Toprow + (Curln - Topln) + 1;  /* 1 below Curln */
		lin[0] = EOS;
		if (Indent > 0 || line <= 0)
			len = max (0, Indent - 1);
		else /* do auto indent */
		{
			LINEDESC *k, *gettxt ();
			k = gettxt (line);
			for (len = 0; Txt[len] == ' '; len++)
				;
		}
		dpos = len;     /* position for terminating '.' */

		for (ret = NOSTATUS; ret == NOSTATUS; )
		{
			if (! hwinsdel())   /* do it the old, slow way */
			{
				if (Cmdrow > Botrow)
				{
					Cmdrow = Toprow + 1;
					cprow (Botrow, Toprow);
					adjust_window (Curln, Curln);
					if (First_affected > Topln)
						First_affected = Topln;
				}
				clrrow (Cmdrow);
				if (Cmdrow < Botrow)
					clrrow (Cmdrow + 1);
			}
			else	/* try to be smart about it */
			{
				if (Cmdrow > Botrow)
				{
					Cmdrow--;
					dellines (Toprow, 1);
					inslines (Cmdrow, 1);
					Topln++;
				}
				else
				{
					dellines (Botrow, 1);
					inslines (Cmdrow, 1);
				}
			}
			prompt ("apd>");
			do
				getcmd (lin, Firstcol, &len, &term);
			while (term == CURSOR_UP || term == CURSOR_DOWN
			    || term == CURSOR_SAME);

			dotseen = 0;
			if (lin[0] == '.' && lin[1] == '\n' && lin[2] == EOS)
				dotseen = 1;
			for (i = 0; i < dpos && lin[i] == ' '; i++)
				;
			if (i == dpos && lin[dpos] == '.' && lin[dpos + 1] == '\n'
			    && lin[dpos+2] == EOS)
				dotseen = 1;

			if (dotseen)
			{
				if (hwinsdel())
				{
					dellines (Cmdrow, 1);
					inslines (Botrow, 1);
				}
				ret = OK;
			}
			else if (inject (lin) == ERR)
				ret = ERR;
			else			/* inject occured */
				prompt ("");	/* erase prompt */
			Cmdrow++;
			if (term != FUNNY)
			{
				if (Indent > 0)
					len = Indent - 1;
				else /* do auto indent */
					for (len = 0; lin[len] == ' '; len++)
						;
				dpos = len;
				lin[0] = EOS;
			}
		}
		Cmdrow = Botrow + 1;
		if (hwinsdel())			/* since we take control */
		{				/* of the screen, we're sure */
			Sctop = Topln;		/* it's still OK */

			for (i = 0; i < Sclen; i++)
				Scline[i] = Sctop + i <= Lastln ? i : -1;
		}
	}
	if (Curln == 0 && Lastln > 0)   /* for 0a or 1i followed by "." */
		Curln = 1;
	if (First_affected > line)
		First_affected = line;

	tflush ();
	return (ret);
}

/* copy --- copy line1 through line2 after line3 */

int copy (line3)
int line3;
{
	register int i;
	int ret;
	register LINEDESC *ptr3, *after3, *k;
	LINEDESC *getind ();

	ret = ERR;

#ifdef OLD_SCRATCH
	ptr3 = getind (line3);
	after3 = ptr3 -> Nextline;
#endif

	if (Line1 <= 0)
		Errcode = EORANGE;
	else
	{
		ret = OK;
		Curln = line3;
		k = getind (Line1);
		for (i = Line1; i <= Line2; i++)
		{
			gtxt (k);
			if (inject (Txt) == ERR || intrpt ())
			{
				ret = ERR;
				break;
			}
#ifdef OLD_SCRATCH
			if (k == ptr3)		/* make sure we don't copy stuff */
				k = after3;	/* that's already been copied */
			else
				k = k -> Nextline;
#else
			if (Line1 < line3)
				k++;
			else
				k += 2;
			/*
			 * inject calls blkmove, which will shift the
			 * lines down one in the array, so we add two
			 * instead of one to get to the next line.
			 */
#endif
		}
		First_affected = min (First_affected, line3 + 1);
	}
	return (ret);
}


/* delete --- delete lines from through to */

int delete (from, to, status)
int from, to, *status;
{
	int nextln (), prevln ();
	LINEDESC *k1, *k2, *j1, *j2, *l1;
	LINEDESC *getind ();

	if (from <= 0)          /* can't delete line 0 */
	{
		*status = ERR;
		Errcode = EORANGE;
	}
	else
	{
		if (First_affected > from)
			First_affected = from;
#ifdef OLD_SCRATCH
		k1 = getind (prevln (from));
		j1 = k1 -> Nextline;
		j2 = getind (to);
		k2 = j2 -> Nextline;
		relink (k1, k2, k1, k2);        /* close chain around deletion */
#else
		blkmove (from, to, MAXBUF - 1);	/* stick at end of buffer */
#endif

		Lastln -= to - from + 1;        /* adjust number of last line */
		Curln = prevln (from);

#ifdef OLD_SCRATCH
		if (Limbo != NOMORE)            /* discard lines in limbo */
		{
			l1 = Limbo -> Prevline;
			Limbo -> Prevline = Free;
			Free = l1;
		}
#endif

		Lost_lines += Limcnt;
		Limcnt = to - from + 1;		/* number of lines "deleted" */

#ifdef OLD_SCRATCH
		Limbo = j1;     /* put what we just deleted in limbo */
		relink (j2, j1, j2, j1);        /* close the ring */
#else
		/* point at first deleted */
		Limbo = &Buf[MAXBUF - (to - from + 1)];
#endif
		*status = OK;
		svdel (from, to - from + 1);
		Buffer_changed = YES;
	}

	return (*status);
}


/* join --- join a group of lines into a single line */

int join (sub)
char sub[];
{
	char new[MAXLINE];
	register int l, line, sublen;
	int ret;
	int inject (), delete (), prevln (), strlen ();
	register LINEDESC *k;
	LINEDESC *getind ();

	ret = OK;
	if (Line1 <= 0)
	{
		Errcode = EORANGE;
		return (ERR);
	}

	sublen = strlen (sub) + 1;      /* length of separator & EOS */
	line = Line1;
	k = getind (line);
	gtxt (k);
	move_ (Txt, new, (int) k -> Lineleng);	/* move in first chunk */
	l = k -> Lineleng;

	for (line++; line <= Line2; line++)
	{
		if (intrpt ())
			return (ERR);
		if (new[l - 2] == '\n') /* zap the NEWLINE */
			l--;
		k = NEXTLINE(k);	/* get the next line */
		gtxt (k);
		if (l + sublen - 1 + k -> Lineleng - 1 > MAXLINE)	/* won't fit */
		{
			Errcode = E2LONG;
			return (ERR);
		}
		move_ (sub, &new[l - 1], sublen);	/* insert separator string */
		l += sublen - 1;
		move_ (Txt, &new[l - 1], (int) k -> Lineleng);	/* move next line */
		l += k -> Lineleng - 1;
	}
	Curln = Line2;          /* all this will replace line1 through line2 */
	ret = inject (new);	/* inject the new line */
	if (ret == OK)
		ret = delete (Line1, Line2, &ret);	/* delete old lines */
	Curln++;

	if (First_affected > Curln)
		First_affected = Curln;

	return (ret);
}


/* move --- move line1 through line2 after line3 */

int move (line3)
int line3;
{
	int nextln (), prevln ();
	LINEDESC *k0, *k1, *k2, *k3, *k4, *k5;
	LINEDESC *getind ();

	if (Line1 <= 0)
	{
		Errcode = EORANGE;
		return (ERR);
	}

	if (Line1 <= line3 && line3 <= Line2)
	{
		Errcode = EINSIDEOUT;
		return (ERR);
	}

#ifdef OLD_SCRATCH
	k0 = getind (prevln (Line1));
	k1 = k0 -> Nextline;
	k2 = getind (Line2);
	k3 = k2 -> Nextline;
	relink (k0, k3, k0, k3);
#else
	blkmove (Line1, Line2, line3);
#endif

	if (line3 > Line1)
	{
		Curln = line3;
#ifdef OLD_SCRATCH
		line3 -= Line2 - Line1 + 1;
#endif
	}
	else
		Curln = line3 + (Line2 - Line1 + 1);

#ifdef OLD_SCRATCH
	k4 = getind (line3);
	k5 = k4 -> Nextline;
	relink (k4, k1, k2, k5);
	relink (k2, k5, k4, k1);
#endif

	Buffer_changed = YES;
	First_affected = min (First_affected, min (Line1, line3));

	return (OK);
}

/* overlay --- let user edit lines directly */

overlay (status)
int *status;
{
	char savtxt[MAXLINE], term, kname;
	static char empty[] = "\n";
	int lng, vcol, lcurln, scurln;
	int inject (), nextln (), prevln (), strcmp ();
	LINEDESC *indx;
	LINEDESC *getind (), *gettxt ();

	*status = OK;
	if (Line1 == 0)
	{
		Curln = 0;
		*status = inject (empty);
		if (*status == ERR)
			return;
		First_affected = 1;
		Line1 = 1;
		Line2++;
	}

	for (lcurln = Line1; lcurln <= Line2; lcurln++)
	{
		Curln = lcurln;
		vcol = Overlay_col - 1;
		do {
			adjust_window (Curln, Curln);
			updscreen ();
			Cmdrow = Curln - Topln + Toprow;
			indx = gettxt (Curln);
			lng = indx -> Lineleng;
			if (Txt[lng - 2] == '\n')       /* clobber newline */
				lng--;
			if (vcol < 0)
				vcol = lng - 1;
			while (lng - 1 < vcol)
			{
				Txt[lng - 1] = ' ';
				lng++;
			}
			Txt[lng - 1] = '\n';
			Txt[lng] = EOS;
			move_ (Txt, savtxt, lng + 1);	/* make a copy of the line */
			getcmd (Txt, Firstcol, &vcol, &term);
			if (term == FUNNY)
			{
				if (First_affected > Curln)
					First_affected = Curln;
				Cmdrow = Botrow + 1;
				return;
			}
			if (strcmp (Txt, savtxt) != 0)  /* was line changed? */
			{
				kname = indx -> Markname;
				delete (Curln, Curln, status);
				scurln = Curln;
				if (*status == OK)
					*status = inject (Txt);
				if (*status == ERR)
				{
					Cmdrow = Botrow + 1;
					return;
				}
				indx = getind (nextln (scurln));
				indx -> Markname = kname;
			}
			else
			{           /* in case end-of-line is moved */
				if (First_affected > Curln)
					First_affected = Curln;
			}
			switch (term) {
			case CURSOR_UP:
				if (Curln > 1)
					Curln--;
				else
					Curln = Lastln;
				break;
			case CURSOR_DOWN:
				if (Curln < Lastln)
					Curln++;
				else
					Curln = min (1, Lastln);
				break;
			case CURSOR_SAME:
				vcol = 0;
				break;
			}
		} while (term == CURSOR_UP || 
		    term == CURSOR_DOWN ||
		    term == CURSOR_SAME);
	}
	Cmdrow = Botrow + 1;
	return;
}


/* subst --- substitute "sub" for occurrences of pattern */

int subst (sub, gflag, glob)
char sub[];
int gflag, glob;
{
	char new[MAXLINE], kname;
	register int line, m, k, lastm;
	int j, junk, status, subbed, ret;
	int tagbeg[10], tagend[10];
	int amatch (), addset (), inject ();
	register LINEDESC *inx;
	LINEDESC *gettxt (), *getind ();

	if (Globals && glob)
		ret = OK;
	else
		ret = ERR;

	if (Line1 <= 0)
	{
		Errcode = EORANGE;
		return (ERR);
	}

	/* the following code has been removed for your protection
	   index() occasionally grabs newlines out of the character class
	   counter in a pattern.  for example [0-9] doesn't work due to this

		if (index (Pat, '\n') != -1)    # never delete NEWLINE
		{
			Errcode = EBADPAT;
			return (ERR);
		}
	*/

	for (line = Line1; line <= Line2; line++)
	{
		if (intrpt ())
			break;
		j = 0;
		subbed = NO;
		inx = gettxt (line);
		lastm = -1;
		for (k = 0; Txt[k] != EOS; )
		{
			for (m = 1; m <= 9; m++)
			{
				tagbeg[m] = -1;
				tagend[m] = -1;
			}
			if (gflag == YES || subbed == NO)
				m = amatch (Txt, k, Pat, &tagbeg[1], &tagend[1]);
			else
				m = -1;
			if (m > -1 && lastm != m)       /* replace matched text */
			{
				subbed = YES;
				tagbeg[0] = k;
				tagend[0] = m;
				catsub (Txt, tagbeg, tagend, sub, new, &j, MAXLINE);
				lastm = m;
			}
			if (m == -1 || m == k)  /* no match or null match */
			{
				junk = addset (Txt[k], new, &j, MAXLINE);
				k++;
			}
			else
				k = m;	/* skip matched text */
		}
		if (subbed == YES)
		{
			if (addset (EOS, new, &j, MAXLINE) == NO)
			{
				ret = ERR;
				Errcode = E2LONG;
				break;
			}
			kname = inx -> Markname;
			delete (line, line, &status);	/* remembers dot */
			ret = inject (new);
			if (First_affected > Curln)
				First_affected = Curln;
			if (ret == ERR)
				break;
			inx = getind (Curln);
			inx -> Markname = kname;
			ret = OK;
			Buffer_changed = YES;
		}
		else	/* subbed == NO */
			Errcode = ENOMATCH;
	}

	return (ret);
}


/* uniquely_name --- mark-name line; make sure no other line has same name */

uniquely_name (kname, status)
char kname;
int *status;
{
	register int line;
	register LINEDESC *k;

	defalt (Curln, Curln);

	if (Line1 <= 0)
	{
		*status = ERR;
		Errcode = EORANGE;
		return;
	}

	*status = OK;
	line = 0;
	k = Line0;

	do {
		line++;
		k = NEXTLINE(k);
		if (line == Line2)
			k -> Markname = kname;
		else if (k -> Markname == kname)
			k -> Markname = DEFAULTNAME;
	} while (line < Lastln);

	return;
}


/* draw_box --- draw or erase a box at coordinates in command line */

int draw_box (lin, i)
char lin[];
int *i;
{
	register int left, right, col, len;
	int junk;
	int ctoi (), strcmp (), inject (), delete ();
	register LINEDESC *k;
	LINEDESC *getind (), *gettxt ();
	char text[MAXLINE];
	char kname, ch;

	left = ctoi (lin, i);
	if (left <= 0 || left > MAXLINE)
	{
		Errcode = EBADCOL;
		return (ERR);
	}

	if (lin[*i] == ',')
	{
		(*i)++;
		SKIPBL (lin, *i);
		right = ctoi (lin, i);
		if (right <= 0 || right >= MAXLINE || left > right)
		{
			Errcode = EBADCOL;
			return (ERR);
		}
	}
	else
		right = left;

	SKIPBL (lin, *i);
	if (lin[*i] == '\n')
		ch = ' ';
	else
		ch = lin[(*i)++];

	if (lin[*i] != '\n')
	{
		Errcode = EEGARB;
		return (ERR);
	}

	if (Line1 <= 0)
	{
		Errcode = EORANGE;
		return (ERR);
	}

	for (Curln = Line1; Curln <= Line2; Curln++)
	{
		k = gettxt (Curln);
		len = k -> Lineleng;
		move_ (Txt, text, len);

		if (text[len - 2] == '\n')
			col = len - 1;
		else
			col = len;
		while (col <= right)
		{
			text[col - 1] = ' ';
			col++;
		}
		text[col - 1] = '\n';
		text[col] = EOS;

		if (Curln == Line1 || Curln == Line2)
			for (col = left; col <= right; col++)
				text[col - 1] = ch;
		else
		{
			text[left - 1] = ch;
			text[right - 1] = ch;
		}

		if (strcmp (text, Txt) != 0)
		{
			kname = k -> Markname;
			if (delete (Curln, Curln, &junk) == ERR
			    || inject (text) == ERR)
				return (ERR);
			k = getind (Curln);
			k -> Markname = kname;
			Buffer_changed = YES;
		}
	}

	Curln = Line1;		/* move to top of box */
	if (First_affected > Curln)
		First_affected = Curln;
	adjust_window (Curln, Curln);
	updscreen ();

	return (OK);
}


/* dfltsopt --- set the 's' option to the extension on the file name */

dfltsopt (name)
char name[];
{
	int i;
	int strlen (), dosopt ();

	for (i = strlen (name) - 1; i >= 0; i--)
		if (name[i] == '.')
		{
			dosopt (&name[i + 1]);
			break;
		}
	if (i < 0)
		dosopt ("");
}



/* doshell --- escape to the Shell to run one or more Unix commands */

/*
** emulate vi: if running just a shell, redraw the screen as
** soon as the shell exits. if running a program, let the user
** redraw the screen when he/she is ready.
**
** also emulate USG Unix 5.0 ed: a ! as the first character is
** replaced by the previous shell command; an unescaped % is replaced
** by the saved file name. The expanded command is echoed.
*/

#ifdef BSD
#define DEFAULT_PATH	"/bin/csh"
#define DEF_SHELL	"csh"
#else
#define DEFAULT_PATH	"/bin/sh"
#define DEF_SHELL	"sh"
#endif

int doshell (lin, pi)
char lin[];
int *pi;
{
	int forkstatus, childstatus;
	int (*save_quit)(), (*save_int)();
	int int_hdlr ();
	int (*signal())();
	int i, auto_redraw;
	char *path, *name, *p, *getenv ();
	char new_command[MAXLINE];
	int j, k;
	static char sav_com[MAXLINE] = "";
	int expanded = NO;

	if (Nlines == 0)        /* use normal 'ed' behavior */
	{
		tflush ();	/* flush out the terminal output */
		position_cursor (Nrows - 1, 0);	/* bottom left corner */

		if ((p = getenv ("SHELL")) == NULL || strcmp (p, DEFAULT_PATH) == 0)
		{
			path = DEFAULT_PATH;
			name = DEF_SHELL;	/* default */
		}
#ifdef BSD
		/* on Berkeley systems, check the other shell */
		else if (strcmp (p, "/bin/sh") == 0)
		{
			path = "/bin/sh";
			name = "sh";
		}
#endif
		else
		{
			if (p[0] == '/')	/* full pathname there */
			{
				/* work backwards to find just name */
				path = p;
				i = strlen (p);
				while (p[i] != '/')
					i--;
				i++;		/* skip '/' */
				name = &p[i];
			}
			else
			{
				char buf[MAXLINE];

				sprintf (buf, "unknown shell, using %s",
					DEF_SHELL);
				remark (buf);
				path = DEFAULT_PATH;
				name = DEF_SHELL;
			}
		}

		auto_redraw = (lin[*pi] == '\n') ? YES : NO;

		/* build command, checking for leading !, and % anywhere */
		if (lin[*pi] == '!')
		{
			if (sav_com[0] != EOS)
			{
				for (j = 0; sav_com[j] != EOS; j++)
					new_command[j] = sav_com[j];
				if (new_command[j-1] == '\n')
					j--;
				(*pi)++;
				expanded = YES;
			}
			else
			{
				Errcode = ENOCMD;
				return (ERR);
			}
		}
		else
			j = 0;

		for (i = *pi; lin[i] != EOS; i++)
		{
			if (lin[i] == ESCAPE)
			{
				if (lin[i+1] != '%')
				{
					new_command[j++] = ESCAPE;
					new_command[j++] = lin[++i];
				}
				else
					new_command[j++] = lin[++i];
			}
			else if (lin[i] == '%')
			{
				for (k = 0; Savfil[k] != EOS; k++)
					new_command[j++] = Savfil[k];
				expanded = YES;
			}
			else
				new_command[j++] = lin[i];
		}

		if (new_command[j-1] == '\n')
			j--;
		new_command[j] = EOS;

		strcpy (sav_com, new_command);	/* save it */

		ttynormal ();
#ifndef HARD_TERMS
		t_exit ();
#endif
		write (1, "\n\n", 2);            /* clear out a line */

		forkstatus = fork();
		if (forkstatus == -1)   /* the fork failed */
		{
			ttyedit ();
#ifndef HARD_TERMS
			t_init ();
#endif
			Errcode = ECANTFORK;
			return ERR;
		}

		if (forkstatus == 0)    /* we're in the child process */
		{
			signal (SIGINT, SIG_DFL);
			signal (SIGQUIT, SIG_DFL);
#ifdef BSD
			if (strcmp (name, "sh") != 0)	/* not /bin/sh */
				signal (SIGTSTP, SIG_DFL);
			else
				signal (SIGTSTP, SIG_IGN);
#endif
			if (auto_redraw)	/* no params; run a shell */
			{
				execl (path, name, 0);
				_exit (RETERR);   /* exec failed, notify parent */
			}
			else
			{
				if (expanded)		/* echo it */
					printf ("%s\n", new_command);

				execl (path, name, "-c", new_command, 0);
				_exit (RETERR);
			}
		}

		/* we're in the parent process here */
		save_int = signal (SIGINT, SIG_IGN);        /* ignore interrupts */
		save_quit = signal (SIGQUIT, SIG_IGN);
		while (wait (&childstatus) != forkstatus)
			;
		save_int = signal (SIGINT, save_int);       /* catch interupts */
		save_quit = signal (SIGQUIT, save_quit);
		write (1, "\n\n", 2);    /* clear out some message space */
		Currow = Nrows - 1;
		Curcol = 0;
		if ((childstatus >> 8) != 0)
		{
			ttyedit ();
#ifndef HARD_TERMS
			t_init ();
#endif
			Errcode = ENOSHELL;
			return ERR;
		}

		/* a la vi: */
		if (! auto_redraw)
		{
			int c;

			printf ("type return to continue: ");
			while ((c = getchar()) != '\n' && c != EOF)
				;
		}

		ttyedit ();
#ifndef HARD_TERMS
		t_init ();
#endif
		restore_screen ();

		return OK;
	}

	else
		remark ("Not implemented yet");
	
	return OK;
}
SHAR_EOF
fi # end of overwriting check
#	End of shell archive
exit 0



More information about the Mod.sources mailing list