[comp.sources.misc] v08i003: stevie 3.69 - part 1 of 8

allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc) (08/20/89)

Posting-number: Volume 8, Issue 3
Submitted-by: tony@cs.utexas.edu@wldrdg.UUCP (Tony Andrews)
Archive-name: stevie3.68/part01

[STevie is a vi-clone, originally developed for the Atari ST but since ported
to MS-DOS and Minix (PC and ST versions), and probably to a few other systems
as well.  ++bsa]

#! /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
#	alloc.c
#	ascii.h
#	cmdline.c
#	ctags.c
#	edit.c
#	env.h
#	fileio.c
# This archive created: Sun Aug 13 11:45:54 1989
export PATH; PATH=/bin:$PATH
echo shar: extracting "'README'" '(1787 characters)'
if test -f 'README'
then
	echo shar: will not over-write existing file "'README'"
else
sed 's/^X//' << \HE_HATES_THESE_CANS > 'README'
XSTEVIE Source Release - 3.68
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, OS/2, DOS, and Minix-ST as well.
XPorts done by others, but not directly supported by this release, include
XMinix-PC, Amiga, and a Data General system of some sort.
X
XThe good news about stevie is that it is extremely portable. It supports
Xsystems with virtually all combinations of integer size, pointer size,
Xand byte order. The only major issue remaining, for some environments,
Xis that the file is kept in memory. For most environments, this isn't a
Xproblem, but for Minix-PC it is particularly annoying.
X
XThe files included in this release are:
X
XREADME
X	This file.
X
Xstevie.mm
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
Xos2.mk
Xunix.mk
Xdos.mk
Xtos.mk
Xminix.mk
X	Makefiles for OS/2, UNIX, DOS, and the Atari ST (TOS and Minix).
X
Xos2.c
Xunix.c
Xdos.c
Xtos.c
Xminix.c
Xterm.h
Xenv.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 ops.c ops.h
Xparam.c param.h ptrfunc.c regexp.c regexp.h regmagic.h regsub.c
Xscreen.c search.c stevie.h term.c 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. Edit the file 'env.h' to set the system defines as needed.
X
X	2. Check the makefile for your system, and modify as needed.
X
X	3. Compile.
X
XGood Luck...
X
XTony Andrews
X8/6/89
HE_HATES_THESE_CANS
if test 1787 -ne "`wc -c < 'README'`"
then
	echo shar: error transmitting "'README'" '(should have been 1787 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'alloc.c'" '(4487 characters)'
if test -f 'alloc.c'
then
	echo shar: will not over-write existing file "'alloc.c'"
else
sed 's/^X//' << \HE_HATES_THESE_CANS > 'alloc.c'
X/* $Header: /nw/tony/src/stevie/src/RCS/alloc.c,v 1.5 89/08/06 09:49:22 tony Exp $
X *
X * Various allocation routines and routines returning information about
X * allocated objects.
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'+1 characters. We add one to nchars here to allow for
X * null termination because all the callers would just do it otherwise.
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+1));	/* the line is empty */
X	l->s[0] = NUL;
X	l->size = nchars + 1;
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(0)) == NULL) {
X		fprintf(stderr,"Unable to allocate file memory!\n");
X		exit(1);
X	}
X	if ((Filetop->linep = newline(0)) == NULL) {
X		fprintf(stderr,"Unable to allocate file memory!\n");
X		exit(1);
X	}
X	if ((Fileend->linep = newline(0)) == 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	register 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((char *)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}
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}
HE_HATES_THESE_CANS
if test 4487 -ne "`wc -c < 'alloc.c'`"
then
	echo shar: error transmitting "'alloc.c'" '(should have been 4487 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'ascii.h'" '(282 characters)'
if test -f 'ascii.h'
then
	echo shar: will not over-write existing file "'ascii.h'"
else
sed 's/^X//' << \HE_HATES_THESE_CANS > 'ascii.h'
X/* $Header: /nw/tony/src/stevie/src/RCS/ascii.h,v 1.2 89/03/11 22:42:03 tony Exp $
X *
X * Definitions of various common control characters
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)
HE_HATES_THESE_CANS
if test 282 -ne "`wc -c < 'ascii.h'`"
then
	echo shar: error transmitting "'ascii.h'" '(should have been 282 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'cmdline.c'" '(13370 characters)'
if test -f 'cmdline.c'
then
	echo shar: will not over-write existing file "'cmdline.c'"
else
sed 's/^X//' << \HE_HATES_THESE_CANS > 'cmdline.c'
X/* $Header: /nw/tony/src/stevie/src/RCS/cmdline.c,v 1.20 89/08/13 11:41:23 tony Exp $
X *
X * Routines to parse and execute "command line" commands, such as searches
X * or colon commands.
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#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 * getcmdln() - read a command line from the terminal
X *
X * Reads a command line started by typing '/', '?', '!', or ':'. Returns a
X * pointer to the string that was read. For searches, an optional trailing
X * '/' or '?' is removed.
X */
Xchar *
Xgetcmdln(firstc)
Xchar	firstc;
X{
X	static	char	buff[CMDSZ];
X	register char	*p = buff;
X	register int	c;
X	register char	*q;
X
X	gotocmd(TRUE, firstc);
X
X	/* collect the command string, handling '\b' and @ */
X	do {
X		switch (c = vgetc()) {
X
X		default:		/* a normal character */
X			outchar(c);
X			*p++ = c;
X			break;
X
X		case BS:
X			if (p > buff) {
X				/*
X				 * this is gross, but it relies
X				 * only on 'gotocmd'
X				 */
X				p--;
X				gotocmd(TRUE, firstc);
X				for (q = buff; q < p ;q++)
X					outchar(*q);
X			} else {
X				msg("");
X				return NULL;		/* back to cmd mode */
X			}
X			break;
X
X		case '@':			/* line kill */
X			p = buff;
X			gotocmd(TRUE, firstc);
X			break;
X
X		case NL:			/* done reading the line */
X		case CR:
X			break;
X		}
X	} while (c != NL && c != CR);
X
X	*p = '\0';
X
X	if (firstc == '/' || firstc == '?') {	/* did we do a search? */
X		/*
X		 * Look for a terminating '/' or '?'. This will be the first
X		 * one that isn't quoted. Truncate the search string there.
X		 */
X		for (p = buff; *p ;) {
X			if (*p == firstc) {	/* we're done */
X				*p = '\0';
X				break;
X			} else if (*p == '\\')	/* next char quoted */
X				p += 2;
X			else
X				p++;		/* normal char */
X		}
X	}
X	return buff;
X}
X
X/*
X * docmdln() - handle a colon command
X *
X * Handles a colon command received interactively by getcmdln() or from
X * the environment variable "EXINIT" (or eventually .virc).
X */
Xvoid
Xdocmdln(cmdline)
Xchar	*cmdline;
X{
X	char	buff[CMDSZ];
X	char	cmdbuf[CMDSZ];
X	char	argbuf[CMDSZ];
X	char	*cmd, *arg;
X	register char	*p;
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	 */
X	LPTR	l_pos, u_pos;
X
X
X	/*
X	 * Clear the range variables.
X	 */
X	l_pos.linep = (struct line *) NULL;
X	u_pos.linep = (struct line *) NULL;
X
X	if (cmdline == NULL)
X		return;
X
X	if (strlen(cmdline) > CMDSZ-2) {
X		msg("Error: command line too long");
X		return;
X	}
X	strcpy(buff, cmdline);
X
X	/* skip any initial white space */
X	for (cmd = buff; *cmd != NUL && isspace(*cmd) ;cmd++)
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, &l_pos, &u_pos);
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
X				getout();
X		}
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		(void) doecmd(arg, cmd[1] == '!');
X		return;
X	}
X	/*
X	 * The command ":e#" gets expanded to something like ":efile", so
X	 * detect that case here.
X	 */
X	if (*cmd == 'e' && arg == NULL) {
X		if (cmd[1] == '!')
X			(void) doecmd(&cmd[2], TRUE);
X		else
X			(void) doecmd(&cmd[1], FALSE);
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);
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		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
X		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, lower, upper)
Xregister char	**cp;
XLPTR	*lower, *upper;
X{
X	register LPTR	*l;
X	register char	*p;
X
X	if ((l = get_line(cp)) == NULL)
X		return;
X
X	*lower = *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		*upper = *lower;
X		return;
X	}
X
X	*cp = ++p;
X
X	if ((l = get_line(cp)) == NULL) {
X		*upper = *lower;
X		return;
X	}
X
X	*upper = *l;
X}
X
Xstatic LPTR *
Xget_line(cp)
Xchar	**cp;
X{
X	static	LPTR	pos;
X	LPTR	*lp;
X	register char	*p, c;
X	register 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	emsg("Unrecognized command");
X}
X
X#define	LSIZE	256	/* 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];		/* line buffer */
X	char	pbuf[LSIZE];		/* search pattern buffer */
X	register char	*fname, *str;
X	register char	*p;
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
X			/*
X			 * Scan through the search string. If we see a magic
X			 * char, we have to quote it. This lets us use "real"
X			 * implementations of ctags.
X			 */
X			p = pbuf;
X			*p++ = *str++;		/* copy the '/' or '?' */
X			*p++ = *str++;		/* copy the '^' */
X
X			for (; *str != NUL ;str++) {
X				if (*str == '\\') {
X					*p++ = *str++;
X					*p++ = *str;
X				} else if (strchr("/?", *str) != NULL) {
X					if (str[1] != '\n') {
X						*p++ = '\\';
X						*p++ = *str;
X					} else
X						*p++ = *str;
X				} else if (strchr("^()*.", *str) != NULL) {
X					*p++ = '\\';
X					*p++ = *str;
X				} else
X					*p++ = *str;
X			}
X			*p = NUL;
X
X			/*
X			 * This looks out of order, but by calling stuffin()
X			 * before doecmd() we keep an extra screen update
X			 * from occuring. This stuffins() have no effect
X			 * until we get back to the main loop, anyway.
X			 */
X			stuffin(pbuf);		/* str has \n at end */
X			stuffin("\007");	/* CTRL('g') */
X
X			if (doecmd(fname, force)) {
X				fclose(tp);
X				return;
X			} else
X				stuffin(NULL);	/* clear the input */
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		if (altfile) {
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	}
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
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,a10,a11,a12,a13,a14,a15,a16)
Xchar	*s;
Xint	a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16;
X{
X	char	sbuf[80];
X
X	sprintf(sbuf, s,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16);
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	register char	c;
X
X	if (got_int)
X		outstr("Interrupt: ");
X
X	outstr("Press RETURN to continue");
X	do {
X		c = vgetc();
X	} while (c != CR && c != NL && c != ' ' && c != ':');
X
X	if (c == ':') {
X		outchar(NL);
X		docmdln(getcmdln(c));
X	} else
X		screenclear();
X
X	updatescreen();
X}
HE_HATES_THESE_CANS
if test 13370 -ne "`wc -c < 'cmdline.c'`"
then
	echo shar: error transmitting "'cmdline.c'" '(should have been 13370 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'ctags.c'" '(2893 characters)'
if test -f 'ctags.c'
then
	echo shar: will not over-write existing file "'ctags.c'"
else
sed 's/^X//' << \HE_HATES_THESE_CANS > 'ctags.c'
X/*
X * ctags - first cut at a UNIX ctags re-implementation
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
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, *strchr();
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		lbuf[strlen(lbuf)-1] = '\0';	/* bag the newline */
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 = strchr(lbuf,')'))!=NULL))) {
X				*++cp = '\0';
X				fprintf(tp, "%s\t%s\t/^%s$/\n", func,fn,lbuf);
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';
X			fprintf(tp, "%s\t%s\t/^%s/\n", func, fn, lbuf);
X		}
X	}
X	fclose(fp);
X}
HE_HATES_THESE_CANS
if test 2893 -ne "`wc -c < 'ctags.c'`"
then
	echo shar: error transmitting "'ctags.c'" '(should have been 2893 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'edit.c'" '(6488 characters)'
if test -f 'edit.c'
then
	echo shar: will not over-write existing file "'edit.c'"
else
sed 's/^X//' << \HE_HATES_THESE_CANS > 'edit.c'
X/* $Header: /nw/tony/src/stevie/src/RCS/edit.c,v 1.11 89/08/02 19:57:12 tony Exp $
X *
X * The main edit loop as well as some other simple cursor movement routines.
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	extern	bool_t	need_redraw;
X	int	c;
X	register 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	updatescreen();
X
X	for ( ;; ) {
X
X	/* Figure out where the cursor is based on Curschar. */
X	cursupdate();
X
X	if (need_redraw && !anyinput()) {
X		updatescreen();
X		need_redraw = FALSE;
X	}
X
X	if (!anyinput())
X		windgoto(Cursrow,Curscol);
X
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
Xvoid
Xinsertchar(c)
Xint	c;
X{
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	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)
Xint	n;
X{
X	LPTR	p, *np;
X	register 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)
Xint	n;
X{
X	LPTR	p, *np;
X	register 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}
HE_HATES_THESE_CANS
if test 6488 -ne "`wc -c < 'edit.c'`"
then
	echo shar: error transmitting "'edit.c'" '(should have been 6488 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'env.h'" '(2323 characters)'
if test -f 'env.h'
then
	echo shar: will not over-write existing file "'env.h'"
else
sed 's/^X//' << \HE_HATES_THESE_CANS > 'env.h'
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 * 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#ifdef	ATARI
X
X#ifdef	MINIX
X#define	STRCSPN
X#endif
X
X#endif
X
X/*
X * The following defines control the inclusion of "optional" features. As
X * the code size of the editor grows, it will probably be useful to be able
X * to tailor the editor to get the features you most want in environments
X * with code size limits.
X *
X * TILDEOP
X *	Normally the '~' command works on a single character. This define
X *	turns on code that allows it to work like an operator. This is
X *	then enabled at runtime with the "tildeop" parameter.
X *
X * HELP
X *	If defined, a series of help screens may be views with the ":help"
X *	command. This eats a fair amount of data space.
X *
X * TERMCAP
X *	Where termcap support is provided, it is generally optional. If
X *	not enabled, you generally get hard-coded escape sequences for
X *	some "reasonable" terminal. In Minix, this means the console. For
X *	UNIX, this means an ANSI standard terminal. See the file "term.h"
X *	for details about specific environments.
X *
X */
X#define	TILDEOP		/* enable tilde to be an operator */
X#define	HELP		/* enable help command */
X#define	TERMCAP		/* enable termcap support */
HE_HATES_THESE_CANS
if test 2323 -ne "`wc -c < 'env.h'`"
then
	echo shar: error transmitting "'env.h'" '(should have been 2323 characters)'
fi
fi # end of overwriting check
echo shar: extracting "'fileio.c'" '(5409 characters)'
if test -f 'fileio.c'
then
	echo shar: will not over-write existing file "'fileio.c'"
else
sed 's/^X//' << \HE_HATES_THESE_CANS > 'fileio.c'
X/* $Header: /nw/tony/src/stevie/src/RCS/fileio.c,v 1.12 89/08/06 09:50:01 tony Exp $
X *
X * Basic file I/O routines.
X */
X
X#include "stevie.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
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	register LINE	*curr;
X	char	buff[MAXLINE], buf2[80];
X	register int	i, c;
X	register 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	filemess("");
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		/*
X		 * Abort if we get an interrupt, but finished reading the
X		 * current line first.
X		 */
X		if (got_int && i == 0)
X			break;
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			LINE	*lp;
X
X			if (c != NL)
X				toolong = TRUE;
X
X			buff[i] = '\0';
X			if ((lp = newline(strlen(buff))) == NULL)
X				exit(1);
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((char *)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 (got_int) {
X		smsg("\"%s\" Interrupt", fname);
X		got_int = FALSE;
X		return FALSE;		/* an interrupt isn't really an error */
X	}
X
X	if (toolong) {
X		smsg("\"%s\" Line too long", fname);
X		return FALSE;
X	}
X
X	sprintf(buff, "\"%s\" %s%d line%s, %ld character%s",
X		fname,
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;
X	register char	*s;
X	register long	nchars;
X	register int	lines;
X	register LPTR	*p;
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	rename(fname, backup);
X
X
X	f = P(P_CR) ? fopen(fixname(fname), "w") : fopenb(fixname(fname), "w");
X
X	if (f == NULL) {
X		emsg("Can't open file for writing!");
X		free(backup);
X		return FALSE;
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\" %d line%s, %ld character%s", fname,
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}
HE_HATES_THESE_CANS
if test 5409 -ne "`wc -c < 'fileio.c'`"
then
	echo shar: error transmitting "'fileio.c'" '(should have been 5409 characters)'
fi
fi # end of overwriting check
#	End of shell archive
exit 0
--