DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - download
Index: ┃ T d

⟦2e4b81746⟧ TextFile

    Length: 27523 (0x6b83)
    Types: TextFile
    Names: »docmd1.c«

Derivation

└─⟦a0efdde77⟧ Bits:30001252 EUUGD11 Tape, 1987 Spring Conference Helsinki
    └─ ⟦this⟧ »EUUGD11/euug-87hel/sec1/se/docmd1.c« 

TextFile

#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 ();
}