Vile 08/17 - vi feel-alike (multi-window)

Paul Fox pgf at cayman.COM
Sat Jun 8 08:09:42 AEST 1991


#!/bin/sh
# this is vileshar.08 (part 8 of Vile)
# do not concatenate these parts, unpack them in order with /bin/sh
# file input.c continued
#
if test ! -r _shar_seq_.tmp; then
	echo 'Please unpack part 1 first!'
	exit 1
fi
(read Scheck
 if test "$Scheck" != 8; then
	echo Please unpack part "$Scheck" next!
	exit 1
 else
	exit 0
 fi
) < _shar_seq_.tmp || exit 1
echo 'x - continuing file input.c'
sed 's/^X//' << 'SHAR_EOF' >> 'input.c' &&
X * macro throw the prompt away, and return the remembered response. This
X * lets macros run at full speed. The reply is always terminated by a carriage
X * return. Handle erase, kill, and abort keys.
X */
X
mlreply(prompt, buf, bufn)
char *prompt;
char *buf;
{
X	return kbd_string(prompt, buf, bufn, '\n',EXPAND);
}
X
/* as above, but don't expand special punctuation, like #, %, ~, etc. */
mlreply_no_exp(prompt, buf, bufn)
char *prompt;
char *buf;
{
X	return kbd_string(prompt, buf, bufn, '\n',NO_EXPAND);
}
X
/*	kcod2key:	translate 10-bit keycode to single key value */
/* probably defined as a macro in estruct.h */
#ifndef kcod2key
kcod2key(c)
int c;
{
X	return c & 0x7f;
}
#endif
X
X
/* the numbered buffer names increment each time they are referenced */
incr_dot_kregnum()
{
X	if (dotcmdmode == PLAY) {
X		if (isdigit(*dotcmdptr) && *dotcmdptr < '9')
X			(*dotcmdptr)++;
X	}
}
X
int tungotc = -1;
X
tungetc(c)
{
X	tungotc = c;
}
X
tpeekc()
{
X	return tungotc;
}
X
/*	tgetc:	Get a key from the terminal driver, resolve any keyboard
X		macro action					*/
int 
tgetc()
{
X	int c;	/* fetched character */
X
X	if (dotcmdmode == PLAY) {
X		
X		if (interrupted) {
X			dotcmdmode = STOP;
X			return (kcod2key(abortc));
X		}
X
X		/* if there is some left... */
X		if (dotcmdptr < dotcmdend)
X			return(*dotcmdptr++);
X
X		/* at the end of last repitition? */
X		if (--dotcmdrep < 1) {
X			dotcmdmode = RECORD;
X			tmpcmdptr = &tmpcmdm[0];
X			tmpcmdend = tmpcmdptr;
#if	VISMAC == 0
X			/* force a screen update after all is done */
X			update(FALSE);
#endif
X		} else {
X
X			/* reset the macro to the begining for the next rep */
X			dotcmdptr = &dotcmdm[0];
X			return((int)*dotcmdptr++);
X		}
X	} else if (kbdmode == PLAY) {
X	/* if we are playing a keyboard macro back, */
X
X		/* if there is some left... */
X		if (kbdptr < kbdend)
X			return((int)*kbdptr++);
X
X		/* at the end of last repitition? */
X		if (--kbdrep < 1) {
X			kbdmode = STOP;
#if	VISMAC == 0
X			/* force a screen update after all is done */
X			update(FALSE);
#endif
X		} else {
X
X			/* reset the macro to the begining for the next rep */
X			kbdptr = &kbdm[0];
X			return((int)*kbdptr++);
X		}
X	}
X
X
X	if (tungotc >= 0) {
X		c = tungotc;
X		tungotc = -1;
X	} else { /* fetch a character from the terminal driver */
X		interrupted = 0;
X		c = TTgetc();
X		if (c == -1 || c == kcod2key(intrc)) {
X			c = kcod2key(abortc);
X			return lastkey = c;
X		}
X	}
X
X	/* save it if we need to */
X	if (dotcmdmode == RECORD) {
X		*tmpcmdptr++ = c;
X		tmpcmdend = tmpcmdptr;
X
X		/* don't overrun the buffer  */
X		/* (we're recording, so must be using tmp) */
X		if (tmpcmdptr == &tmpcmdm[NKBDM - 1]) {
X			dotcmdmode = STOP;
X			TTbeep();
X		}
X	}
X
X	/* save it if we need to */
X	if (kbdmode == RECORD) {
X		*kbdptr++ = c;
X		kbdend = kbdptr;
X
X		/* don't overrun the buffer */
X		if (kbdptr == &kbdm[NKBDM - 1]) {
X			kbdmode = STOP;
X			TTbeep();
X		}
X	}
X
X	/* and finally give the char back */
X	/* record it for $lastkey */
X	return(lastkey = c);
}
X
/*	KBD_KEY:	Get one keystroke. The only prefix legal here
X			is the SPEC prefix.  */
kbd_key()
{
X	int    c;
X
X	/* get a keystroke */
X        c = tgetc();
X
#if	MSDOS | ST520
X	if (c == 0) {			/* Apply SPEC prefix	*/
X	        c = tgetc();
X		if (insertmode) continue;
X		return(last1key = SPEC | c);
X	}
#endif
X
#if	AMIGA
X	/* apply SPEC prefix */
X	if ((unsigned)c == 155) {
X		int	d;
X		c = tgetc();
X
X		/* first try to see if it is a cursor key */
X		if ((c >= 'A' && c <= 'D') || c == 'S' || c == 'T') {
X			if (insertmode) continue;
X			return(last1key = SPEC | c);
X		}
X
X		/* next, a 2 char sequence */
X		d = tgetc();
X		if (d == '~') {
X			if (insertmode) continue;
X			return(last1key = SPEC | c);
X		}
X
X		/* decode a 3 char sequence */
X		c = d + ' ';
X		/* if a shifted function key, eat the tilde */
X		if (d >= '0' && d <= '9')
X			d = tgetc();
X		if (insertmode) continue;
X		return(last1key = SPEC | c);
X	}
#endif
X
#if  WANGPC
X	if (c == 0x1F) {	/* Apply SPEC prefix    */
X	        c = tgetc();
X		if (insertmode) continue;
X		return(last1key = SPEC | c);
X	}
#endif
X
X        return (last1key = c);
}
X
/*	KBD_SEQ:	Get a command sequence (multiple keystrokes) from 
X		the keyboard.
X		Process all applicable prefix keys.
X		Set lastcmd for commands which want stuttering.
*/
kbd_seq()
{
X	int c;		/* fetched keystroke */
X
X	/* get initial character */
X	c = kbd_key();
X
X	/* process CTLA prefix */
X	if (c == cntl_a) {
X		c = kbd_key();
#if BEFORE
X	        if (islower(c))		/* Force to upper */
X        	        c = toupper(c);
#endif
X		return (lastcmd = CTLA | c);
X	}
X
X	/* process CTLX prefix */
X	if (c == cntl_x) {
X		c = kbd_key();
X		return (lastcmd = CTLX | c);
X	}
X
X	/* otherwise, just return it */
X	return (lastcmd = c);
}
X
screen_string(buf,bufn,inclchartype)
char *buf;
{
X	register int i = 0;
X	register int s = TRUE;
X
X	setmark();
X	while (s == TRUE && i < bufn && 
X			curwp->w_doto != llength(curwp->w_dotp)) {
X		buf[i] = lgetc(curwp->w_dotp, curwp->w_doto);
X		if (!istype(inclchartype, buf[i]))
X			break;
X		s = forwchar(FALSE, 1);
X		i++;
X	}
X	buf[i] = '\0';
X	swapmark();
X
X	return buf[0] != '\0';
}
X
/*	A more generalized prompt/reply function allowing the caller
X	to specify a terminator other than '\n'.  Both are accepted.
X	Assumes the buffer already contains a valid (possibly
X	null) string to use as the default response.
*/
kbd_string(prompt, buf, bufn, eolchar, expand)
char *prompt;
char *buf;
int eolchar;
{
X	register int cpos;	/* current character position in string */
X	register int c;
X	register int quotef;	/* are we quoting the next char? */
X	int firstchar = TRUE;
X
X	if (clexec)
X		return nextarg(buf);
X
X	lineinput = TRUE;
X
X	cpos = 0;
X	quotef = FALSE;
X
X
X	/* prompt the user for the input string */
X	mlwrite(prompt);
X	/* put out the default response, which comes in already in the
X		buffer */
X	while((c = buf[cpos]) != '\0' && cpos < bufn-1) {
X		if (!isprint(c)) {
X			if (disinp)
X				TTputc('^');
X			++ttcol;
X			c = toalpha(c);
X		}
X
X		if (disinp)
X			TTputc(c);
X		++ttcol;
X		++cpos;
X	}
X	TTflush();
X
X	for (;;) {
X		/* get a character from the user */
X		c = kbd_key();
X
X		/* If it is a <ret>, change it to a <NL> */
X		if (c == '\r' && quotef == FALSE)
X			c = '\n';
X
X		/* if they hit the line terminate, wrap it up */
X		/* don't allow newlines in the string -- they cause real
X			problems, especially when searching for patterns
X			containing them -pgf */
X		/* never a newline, and only eolchar if ^V or \ precedes it */
X		if (c == '\n' || (c == eolchar && 
X			quotef == FALSE && cpos > 0 && buf[cpos-1] != '\\'))
X		{
X			if (buf[cpos] != '\0')
X				buf[cpos++] = 0;
X
X			lineinput = FALSE;
X			/* if buffer is empty, return FALSE */
X			if (buf[0] == 0)
X				return(FALSE);
X
X			return(TRUE);
X		}
X
#if     NeWS    /* make sure cursor is where we think it is before output */
X		TTmove(ttrow,ttcol) ;
#endif
X		/* change from command form back to character form */
X		c = kcod2key(c);
X		
X		if (c == kcod2key(abortc) && quotef == FALSE) {
X			buf[cpos] = 0;
X			esc(FALSE, 0);
X			TTflush();
X			lineinput = FALSE;
X			return ABORT;
X		} else if (isbackspace(c) && quotef==FALSE) {
X			/* rubout/erase */
X			if (cpos != 0) {
X				outstring("\b \b");
X				--ttcol;
X
X				if (!isprint(buf[--cpos])) {
X					outstring("\b \b");
X					--ttcol;
X				}
X
X				TTflush();
X			} else {
X				buf[0] = 0;
X				mlerase();
X				lineinput = FALSE;
X				return FALSE;
X			}
X
X		} else if (c == kcod2key(killc) && quotef == FALSE) {
X			/* C-U, kill */
X		killit:
X			while (cpos != 0) {
X				outstring("\b \b");
X				--ttcol;
X
X				if (!isprint(buf[--cpos])) {
X					outstring("\b \b");
X					--ttcol;
X				}
X			}
X			TTflush();
X
X		} else if (expand == EXPAND) {
X			/* we prefer to expand to filenames, but a buffer name
X				will do */
X			char *cp = NULL;
X			char *hist_lookup();
X			if (firstchar == TRUE) {
X				tungetc(c);
X				goto killit;
X			}
X			switch(c) {
X			case '%':
X				cp = curbp->b_fname;
X				if (!*cp || isspace(*cp))
X					cp = curbp->b_bname;
X				break;
X			case '#':
X				cp = hist_lookup(1);  /* returns buffer name */
X				if (cp) {
X					/* but we want a file */
X					BUFFER *bp;
X					bp = bfind(cp,NO_CREAT,0);
X					cp = bp->b_fname;
X					if (!*cp || isspace(*cp)) {
X						/* oh well, use the buffer */
X						cp = bp->b_bname;
X					}
X				}
X				break;
X			/* case tocntrl('W'): */
X			case ':':
X				{
X				char str[80];
X			 	if (screen_string(str, 80, _path))
X					cp = str;
X				break;
X				}
X			default:
X				goto trymore;
X			}
X			
X			if (!cp) {
X				TTbeep();
X				continue;
X			}
X			while (cpos < bufn-1 && (c = *cp++)) {
X				buf[cpos++] = c;
X				if (!isprint(c)) {
X					outstring("^");
X					++ttcol;
X					c = toalpha(c);
X				}
X				if (disinp)
X					TTputc(c);
X				++ttcol;
X			}
X			TTflush();
X		} else {
X		trymore:
X			if (c == kcod2key(quotec) && quotef == FALSE) {
X				quotef = TRUE;
X			} else {
X				quotef = FALSE;
X				if (firstchar == TRUE) {
X					tungetc(c);
X					goto killit;
X				}
X				if (cpos < bufn-1) {
X					buf[cpos++] = c;
X
X					if (!isprint(c)) {
X						outstring("^");
X						++ttcol;
X						c = toalpha(c);
X					}
X
X					if (disinp)
X						TTputc(c);
X
X					++ttcol;
X					TTflush();
X				}
X			}
X		}
X		firstchar = FALSE;
X	}
}
X
outstring(s)	/* output a string of input characters */
char *s;	/* string to output */
{
X	if (disinp)
X		while (*s)
X			TTputc(*s++);
}
X
ostring(s)	/* output a string of output characters */
char *s;	/* string to output */
{
X	if (discmd)
X		while (*s)
X			TTputc(*s++);
}
SHAR_EOF
echo 'File input.c is complete' &&
chmod 0444 input.c ||
echo 'restore of input.c failed'
Wc_c="`wc -c < 'input.c'`"
test 10225 -eq "$Wc_c" ||
	echo 'input.c: original size 10225, current size' "$Wc_c"
# ============= isearch.c ==============
echo 'x - extracting isearch.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'isearch.c' &&
/* vile note, 6/1/91, pgf -- I haven't tried this code in a long time */
/*
X * The functions in this file implement commands that perform incremental
X * searches in the forward and backward directions.  This "ISearch" command
X * is intended to emulate the same command from the original EMACS 
X * implementation (ITS).  Contains references to routines internal to
X * SEARCH.C.
X *
X * REVISION HISTORY:
X *
X *	D. R. Banks 9-May-86
X *	- added ITS EMACSlike ISearch
X *
X *	John M. Gamble 5-Oct-86
X *	- Made iterative search use search.c's scanner() routine.
X *	  This allowed the elimination of bakscan().
X *	- Put isearch constants into estruct.h
X *	- Eliminated the passing of 'status' to scanmore() and
X *	  checknext(), since there were no circumstances where
X *	  it ever equalled FALSE.
X */
X
X
#include        <stdio.h>
#include	"estruct.h"
#include        "edef.h"
X
#if	ISRCH
X
extern int thescanner();		/* Handy search routine */
extern int eq();			/* Compare chars, match case */
X
/* A couple of "own" variables for re-eat */
X
int	(*saved_get_char)();		/* Get character routine */
int	eaten_char = -1;		/* Re-eaten char */
X
/* A couple more "own" variables for the command string */
X
int	cmd_buff[CMDBUFLEN];		/* Save the command args here */
int	cmd_offset;			/* Current offset into command buff */
int	cmd_reexecute = -1;		/* > 0 if re-executing command */
X
X
/*
X * Subroutine to do incremental reverse search.  It actually uses the
X * same code as the normal incremental search, as both can go both ways.
X */
X 
int risearch(f, n)
{
X    LINE *curline;			/* Current line on entry	      */
X    int  curoff;			/* Current offset on entry	      */
X
X    /* remember the initial . on entry: */
X
X    curline = curwp->w_dotp;		/* Save the current line pointer      */
X    curoff  = curwp->w_doto;		/* Save the current offset	      */
X
X    /* Make sure the search doesn't match where we already are:		      */
X
X    backchar(TRUE, 1);			/* Back up a character		      */
X
#if	NeWS
X    newsimmediateon() ;
#endif
X
X    if (!(isearch(f, -n)))		/* Call ISearch backwards	      */
X    {					/* If error in search:		      */
X	curwp->w_dotp = curline;	/* Reset the line pointer	      */
X	curwp->w_doto = curoff;		/*  and the offset to original value  */
X	curwp->w_flag |= WFMOVE;	/* Say we've moved		      */
X	update(FALSE);			/* And force an update		      */
X	mlwrite ("[search failed]");	/* Say we died			      */
X	TTbeep();
X    } else
X	mlerase ();			/* If happy, just erase the cmd line  */
X
#if	NeWS
X    newsimmediateoff() ;
#endif
}
X
/* Again, but for the forward direction */
X
int fisearch(f, n)
{
X    LINE *curline;			/* Current line on entry	      */
X    int  curoff;			/* Current offset on entry	      */
X
X    /* remember the initial . on entry: */
X
X    curline = curwp->w_dotp;		/* Save the current line pointer      */
X    curoff  = curwp->w_doto;		/* Save the current offset	      */
X
X    /* do the search */
X
#if	NeWS
X    newsimmediateon() ;
#endif
X
X    if (!(isearch(f, n)))		/* Call ISearch forwards	      */
X    {					/* If error in search:		      */
X	curwp->w_dotp = curline;	/* Reset the line pointer	      */
X	curwp->w_doto = curoff;		/*  and the offset to original value  */
X	curwp->w_flag |= WFMOVE;	/* Say we've moved		      */
X	update(FALSE);			/* And force an update		      */
X	mlwrite ("[search failed]");	/* Say we died			      */
X	TTbeep();
X    } else
X	mlerase ();			/* If happy, just erase the cmd line  */
X
#if	NeWS
X    newsimmediateoff() ;
#endif
}
X
/*
X * Subroutine to do an incremental search.  In general, this works similarly
X * to the older micro-emacs search function, except that the search happens
X * as each character is typed, with the screen and cursor updated with each
X * new search character.
X *
X * While searching forward, each successive character will leave the cursor
X * at the end of the entire matched string.  Typing a Control-S or Control-X
X * will cause the next occurrence of the string to be searched for (where the
X * next occurrence does NOT overlap the current occurrence).  A Control-R will
X * change to a backwards search, META will terminate the search and Control-G
X * will abort the search.  Rubout will back up to the previous match of the
X * string, or if the starting point is reached first, it will delete the
X * last character from the search string.
X *
X * While searching backward, each successive character will leave the cursor
X * at the beginning of the matched string.  Typing a Control-R will search
X * backward for the next occurrence of the string.  Control-S or Control-X
X * will revert the search to the forward direction.  In general, the reverse
X * incremental search is just like the forward incremental search inverted.
X *
X * In all cases, if the search fails, the user will be feeped, and the search
X * will stall until the pattern string is edited back into something that
X * exists (or until the search is aborted).
X */
X 
isearch(f, n)
{
X    int			status;		/* Search status */
X    int			col;		/* prompt column */
X    register int	cpos;		/* character number in search string  */
X    register int	c;		/* current input character */
X    char		pat_save[NPAT];	/* Saved copy of the old pattern str  */
X    LINE		*curline;	/* Current line on entry	      */
X    int			curoff;		/* Current offset on entry	      */
X    int			init_direction;	/* The initial search direction	      */
X
X    /* Initialize starting conditions */
X
X    cmd_reexecute = -1;		/* We're not re-executing (yet?)      */
X    cmd_offset = 0;			/* Start at the beginning of the buff */
X    cmd_buff[0] = '\0';		/* Init the command buffer	      */
X    strncpy (pat_save, pat, NPAT);	/* Save the old pattern string	      */
X    curline = curwp->w_dotp;		/* Save the current line pointer      */
X    curoff  = curwp->w_doto;		/* Save the current offset	      */
X    init_direction = n;			/* Save the initial search direction  */
X    setboundry(FALSE,NULL,0,0);		/* keep thescanner() finite */
X
X    /* This is a good place to start a re-execution: */
X
start_over:
X
X    /* ask the user for the text of a pattern */
X    col = promptpattern("ISearch: ");		/* Prompt, remember the col   */
X
X    cpos = 0;					/* Start afresh		      */
X    status = TRUE;				/* Assume everything's cool   */
X
X    /*
X       Get the first character in the pattern.  If we get an initial Control-S
X       or Control-R, re-use the old search string and find the first occurrence
X     */
X
X    c = kcod2key(get_char());			/* Get the first character    */
X    if ((c == IS_FORWARD) ||
X        (c == IS_REVERSE))			/* Reuse old search string?   */
X    {
X    	for (cpos = 0; pat[cpos] != 0; cpos++)	/* Yup, find the length	      */
X    	    col = echochar(pat[cpos],col);	/*  and re-echo the string    */
X	if (c == IS_REVERSE) {			/* forward search?	      */
X	    n = -1;				/* No, search in reverse      */
X	    backchar (TRUE, 1);			/* Be defensive about EOB     */
X	} else
X	    n = 1;				/* Yes, search forward	      */
X	status = scanmore(pat, n);		/* Do the search	      */
X	c = kcod2key(get_char());		/* Get another character      */
X    }
X
X    /* Top of the per character loop */
X        	
X    for (;;)					/* ISearch per character loop */
X    {
X	/* Check for special characters first: */
X	/* Most cases here change the search */
X
X	if (c == kcod2key(abortc))			/* Want to quit searching?    */
X	    return (TRUE);			/* Quit searching now	      */
X
X	if (isbackspace(c))
X		 c = '\b';
X
X	if (c == kcod2key(quotec))			/* quote character?	      */
X	    c = kcod2key(get_char());		/* Get the next char	      */
X
X	switch (c)				/* dispatch on the input char */
X	{
X	  case IS_REVERSE:			/* If backward search	      */
X	  case IS_FORWARD:			/* If forward search	      */
X	    if (c == IS_REVERSE)		/* If reverse search	      */
X		n = -1;				/* Set the reverse direction  */
X	    else				/* Otherwise, 		      */
X		n = 1;				/*  go forward		      */
X	    status = scanmore(pat, n);		/* Start the search again     */
X	    c = kcod2key(get_char());		/* Get the next char	      */
X	    continue;				/* Go continue with the search*/
X
X	  case '\r':				/* Carriage return	      */
X	    c = '\n';				/* Make it a new line	      */
X	    break;				/* Make sure we use it	      */
X
X	  case '\t':				/* Generically allowed	      */
X	  case '\n':				/*  controlled characters     */
X	    break;				/* Make sure we use it	      */
X
X	  case '\b':				/*  or if a Rubout:	      */
X	    if (cmd_offset <= 1)		/* Anything to delete?	      */
X		return (TRUE);			/* No, just exit	      */
X	    --cmd_offset;			/* Back up over the Rubout    */
X	    cmd_buff[--cmd_offset] = '\0';	/* Yes, delete last char   */
X	    curwp->w_dotp = curline;		/* Reset the line pointer     */
X	    curwp->w_doto = curoff;		/*  and the offset	      */
X	    n = init_direction;			/* Reset the search direction */
X	    strncpy (pat, pat_save, NPAT);	/* Restore the old search str */
X	    cmd_reexecute = 0;			/* Start the whole mess over  */
X	    goto start_over;			/* Let it take care of itself */
X
X	  /* Presumably a quasi-normal character comes here */
X
X	  default:				/* All other chars    	      */
X	    if (c < ' ')			/* Is it printable?	      */
X	    {					/* Nope.		      */
X		reeat (c);			/* Re-eat the char	      */
X		return (TRUE);			/* And return the last status */
X	    }
X	}  /* Switch */
X
X	/* I guess we got something to search for, so search for it	      */
X
X	pat[cpos++] = c;			/* put the char in the buffer */
X	if (cpos >= NPAT)			/* too many chars in string?  */
X	{					/* Yup.  Complain about it    */
X	    mlwrite("? Search string too long");
X	    return(TRUE);			/* Return an error	      */
X	}
X	pat[cpos] = 0;				/* null terminate the buffer  */
X	col = echochar(c,col);			/* Echo the character	      */
X	if (!status) {				/* If we lost last time	      */
X	    TTbeep();				/* Feep again		      */
X	    TTflush();				/* see that the feep feeps    */
X	} else					/* Otherwise, we must have won*/
X	    if (!(status = checknext(c, pat, n))) /* See if match	      */
X		status = scanmore(pat, n);	/*  or find the next match    */
X	c = kcod2key(get_char());		/* Get the next char	      */
X    } /* for {;;} */
}
X
/*
X * Trivial routine to insure that the next character in the search string is
X * still true to whatever we're pointing to in the buffer.  This routine will
X * not attempt to move the "point" if the match fails, although it will 
X * implicitly move the "point" if we're forward searching, and find a match,
X * since that's the way forward isearch works.
X *
X * If the compare fails, we return FALSE and assume the caller will call
X * scanmore or something.
X */
X
int checknext (chr, patrn, dir)	/* Check next character in search string */
char	chr;			/* Next char to look for		 */
char	*patrn;			/* The entire search string (incl chr)   */
int	dir;			/* Search direction			 */
{
X    register LINE *curline;		/* current line during scan	      */
X    register int curoff;		/* position within current line	      */
X    register int buffchar;		/* character at current position      */
X    int status;				/* how well things go		      */
X
X
X    /* setup the local scan pointer to current "." */
X
X    curline = curwp->w_dotp;		/* Get the current line structure     */
X    curoff  = curwp->w_doto;		/* Get the offset within that line    */
X
X    if (dir > 0)			/* If searching forward		      */
X    {
X    	if (curoff == llength(curline)) /* If at end of line		      */
X    	{
X	    curline = lforw(curline);	/* Skip to the next line	      */
X	    if (curline == curbp->b_linep)
X		return (FALSE);		/* Abort if at end of buffer	      */
X	    curoff = 0;			/* Start at the beginning of the line */
X	    buffchar = '\n';		/* And say the next char is NL	      */
X	} else
X	    buffchar = lgetc(curline, curoff++); /* Get the next char	      */
X	if (status = eq(buffchar, chr))	/* Is it what we're looking for?      */
X	{
X	    curwp->w_dotp = curline;	/* Yes, set the buffer's point	      */
X	    curwp->w_doto = curoff;	/*  to the matched character	      */
X	    curwp->w_flag |= WFMOVE;	/* Say that we've moved		      */
X	}
X	return (status);		/* And return the status	      */
X    } else				/* Else, if reverse search:	      */
X	return (match_pat (patrn));	/* See if we're in the right place    */
}
X
/*
X * This hack will search for the next occurrence of <pat> in the buffer, either
X * forward or backward.  It is called with the status of the prior search
X * attempt, so that it knows not to bother if it didn't work last time.  If
X * we can't find any more matches, "point" is left where it was before.  If
X * we do find a match, "point" will be at the end of the matched string for
X * forward searches and at the beginning of the matched string for reverse
X * searches.
X */
X 
int scanmore(patrn, dir)	/* search forward or back for a pattern	      */
char	*patrn;			/* string to scan for			      */
int	dir;			/* direction to search			      */
{
X	int	sts;			/* search status		      */
X
X    	if (dir < 0)				/* reverse search?	      */
X    	{
X		rvstrcpy(tap, patrn);		/* Put reversed string in tap */
X		sts = thescanner(tap, REVERSE, PTBEG, FALSE);
X	}
X	else 	/* Nope. Go forward   */
X		sts = thescanner(patrn, FORWARD, PTEND, FALSE);
X
X	if (!sts)
X	{
X		TTbeep();		/* Feep if search fails       */
X		TTflush();		/* see that the feep feeps    */
X	}
X
X	return(sts);				/* else, don't even try	      */
}
X
/*
X * The following is a worker subroutine used by the reverse search.  It
X * compares the pattern string with the characters at "." for equality. If
X * any characters mismatch, it will return FALSE.
X *
X * This isn't used for forward searches, because forward searches leave "."
X * at the end of the search string (instead of in front), so all that needs to
X * be done is match the last char input.
X */
X
int match_pat (patrn)	/* See if the pattern string matches string at "."   */
char	*patrn;		/* String to match to buffer			     */
{
X    register int  i;			/* Generic loop index/offset	      */
X    register int buffchar;		/* character at current position      */
X    register LINE *curline;		/* current line during scan	      */
X    register int curoff;		/* position within current line	      */
X
X    /* setup the local scan pointer to current "." */
X
X    curline = curwp->w_dotp;		/* Get the current line structure     */
X    curoff  = curwp->w_doto;		/* Get the offset within that line    */
X
X    /* top of per character compare loop: */
X
X    for (i = 0; i < strlen(patrn); i++)	/* Loop for all characters in patrn   */
X    {
X    	if (curoff == llength(curline)) /* If at end of line		      */
X    	{
X	    curline = lforw(curline);	/* Skip to the next line	      */
X	    curoff = 0;			/* Start at the beginning of the line */
X	    if (curline == curbp->b_linep)
X		return (FALSE);		/* Abort if at end of buffer	      */
X	    buffchar = '\n';		/* And say the next char is NL	      */
X	} else
X	    buffchar = lgetc(curline, curoff++); /* Get the next char	      */
X	if (!eq(buffchar, patrn[i]))	/* Is it what we're looking for?      */
X	    return (FALSE);		/* Nope, just punt it then	      */
X    }
X    return (TRUE);			/* Everything matched? Let's celebrate*/
}
X
/* Routine to prompt for I-Search string. */
X
int promptpattern(prompt)
char *prompt;
{
X    char tpat[NPAT+20];
X
X    strcpy(tpat, prompt);		/* copy prompt to output string */
X    strcat(tpat, " [");			/* build new prompt string */
X    expandp(pat, &tpat[strlen(tpat)], NPAT/2);	/* add old pattern */
X    strcat(tpat, "]: ");
X
X    /* check to see if we are executing a command line */
X    if (!clexec) {
X	mlwrite(tpat);
X    }
X    return(strlen(tpat));
}
X
/* routine to echo i-search characters */
X
int echochar(c,col)
int	c;	/* character to be echoed */
int	col;	/* column to be echoed in */
{
X    movecursor(term.t_nrow,col);		/* Position the cursor	      */
X    if (!isprint(c)) {	 		/* control char			*/
X	TTputc('^');		/* Yes, output prefix	      */
X	TTputc(toalpha(c));		/* Make it "^X"		      */
X	col++;			/* Count this char	      */
X    } else {
X	TTputc(c);			/* Otherwise, output raw char */
X    }
X    TTflush();				/* Flush the output	      */
X    return(++col);			/* return the new column no   */
}
X
/*
X * Routine to get the next character from the input stream.  If we're reading
X * from the real terminal, force a screen update before we get the char. 
X * Otherwise, we must be re-executing the command string, so just return the
X * next character.
X */
X
int get_char ()
{
X    int	c;				/* A place to get a character	      */
X
X    /* See if we're re-executing: */
X
X    if (cmd_reexecute >= 0)		/* Is there an offset?		      */
X	if ((c = cmd_buff[cmd_reexecute++]) != 0)
X	    return (c);			/* Yes, return any character	      */
X
X    /* We're not re-executing (or aren't any more).  Try for a real char      */
X
X    cmd_reexecute = -1;		/* Say we're in real mode again	      */
X    update(FALSE);			/* Pretty up the screen		      */
X    if (cmd_offset >= CMDBUFLEN-1)	/* If we're getting too big ...	      */
X    {
X	mlwrite ("? command too long");	/* Complain loudly and bitterly	      */
X	return (abortc);			/* And force a quit		      */
X    }
X    c = kbd_key();		/* Get the next character	      */
X    cmd_buff[cmd_offset++] = c; /* Save the char for next time        */
X    cmd_buff[cmd_offset] = '\0';/* And terminate the buffer	      */
X    return (c);				/* Return the character		      */
}
X
/*
X * Hacky routine to re-eat a character.  This will save the character to be
X * re-eaten by redirecting the input call to a routine here.  Hack, etc.
X */
X
/* Come here on the next term.t_getchar call: */
X
int uneat()
{
X    int c;
X
X    term.t_getchar = saved_get_char;	/* restore the routine address	      */
X    c = eaten_char;			/* Get the re-eaten char	      */
X    eaten_char = -1;			/* Clear the old char		      */
X    return(c);				/* and return the last char	      */
}
X
int reeat(c)
int	c;
{
X    if (eaten_char != -1)		/* If we've already been here	      */
X	return/*(NULL)*/;		/* Don't do it again		      */
X    eaten_char = c;			/* Else, save the char for later      */
X    saved_get_char = term.t_getchar;	/* Save the char get routine	      */
X    term.t_getchar = uneat;		/* Replace it with ours		      */
}
#else
isearch()
{
}
#endif
SHAR_EOF
chmod 0444 isearch.c ||
echo 'restore of isearch.c failed'
Wc_c="`wc -c < 'isearch.c'`"
test 18075 -eq "$Wc_c" ||
	echo 'isearch.c: original size 18075, current size' "$Wc_c"
# ============= line.c ==============
echo 'x - extracting line.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'line.c' &&
/*
X * The functions in this file are a general set of line management utilities.
X * They are the only routines that touch the text. They also touch the buffer
X * and window structures, to make sure that the necessary updating gets done.
X * There are routines in this file that handle the kill register too. It isn't
X * here for any good reason.
X *
X * Note that this code only updates the dot and mark values in the window list.
X * Since all the code acts on the current window, the buffer that we are
X * editing must be being displayed, which means that "b_nwnd" is non zero,
X * which means that the dot and mark values in the buffer headers are nonsense.
X */
X
#include	<stdio.h>
#include	"estruct.h"
#include	"edef.h"
X
#define roundup(n) ((n+NBLOCK-1) & ~(NBLOCK-1))
/*
X * This routine allocates a block of memory large enough to hold a LINE
X * containing "used" characters. The block is always rounded up a bit. Return
X * a pointer to the new block, or NULL if there isn't any memory left. Print a
X * message in the message line if no space.
X */
LINE *
lalloc(used)
register int	used;
{
X	register LINE	*lp;
X	register int	size;
X
X	/* lalloc(-1) is used by undo for placeholders */
X	if (used < 0)  {
X		size = 0;
X	} else {
X		size = roundup(used);
X		if (size == 0)			/* Assume that an empty */
X			size = NBLOCK;		/* line is for type-in. */
X	}
X	/* malloc 4 less, because struct LINE is 4 too big */
X	if ((lp = (LINE *) malloc(sizeof(LINE))) == NULL) {
X		mlwrite("[OUT OF MEMORY]");
X		return NULL;
X	}
X	if ((lp->l_text = malloc(size)) == NULL) {
X		mlwrite("[OUT OF MEMORY]");
X		free((char *)lp);
X		return NULL;
X	}
X	lp->l_size = size;
X	lp->l_used = used;
X	lsetclear(lp);
X	lp->l_nxtundo = NULL;
X	return (lp);
}
X
lfree(lp)
register LINE *lp;
{
X	if (lp->l_text)
X		free(lp->l_text);
X	free((char *)lp);
}
X
/*
X * Delete line "lp". Fix all of the links that might point at it (they are
X * moved to offset 0 of the next line. Unlink the line from whatever buffer it
X * might be in. The buffers are updated too; the magic
X * conditions described in the above comments don't hold here.
X * Memory is not released, so line can be saved in undo stacks.
X */
lremove(bp,lp)
register BUFFER *bp;
register LINE	*lp;
{
X	register WINDOW *wp;
X
X	wp = wheadp;
X	while (wp != NULL) {
X		if (wp->w_linep == lp)
X			wp->w_linep = lp->l_fp;
X		if (wp->w_dotp	== lp) {
X			wp->w_dotp  = lp->l_fp;
X			wp->w_doto  = 0;
X		}
X		if (wp->w_mkp == lp) {
X			wp->w_mkp = lp->l_fp;
X			wp->w_mko = 0;
X		}
#if 0
X		if (wp->w_ldmkp == lp) {
X			wp->w_ldmkp = lp->l_fp;
X			wp->w_ldmko = 0;
X		}
#endif
X		wp = wp->w_wndp;
X	}
X	if (bp->b_nwnd == 0) {
X		if (bp->b_dotp	== lp) {
X			bp->b_dotp = lp->l_fp;
X			bp->b_doto = 0;
X		}
X		if (bp->b_markp == lp) {
X			bp->b_markp = lp->l_fp;
X			bp->b_marko = 0;
X		}
#if 0
X		if (bp->b_ldmkp == lp) {
X			bp->b_ldmkp = lp->l_fp;
X			bp->b_ldmko = 0;
X		}
#endif
X	}
#if 0
X	if (bp->b_nmmarks != NULL) { /* fix the named marks */
X		int i;
X		struct MARK *mp;
X		for (i = 0; i < 26; i++) {
X			mp = &(bp->b_nmmarks[i]);
X			if (mp->markp == lp) {
X				mp->markp = lp->l_fp;
X				mp->marko = 0;
X			}
X		}
X	}
#endif
X	lp->l_bp->l_fp = lp->l_fp;
X	lp->l_fp->l_bp = lp->l_bp;
}
X
/*
X * This routine gets called when a character is changed in place in the current
X * buffer. It updates all of the required flags in the buffer and window
X * system. The flag used is passed as an argument; if the buffer is being
X * displayed in more than 1 window we change EDIT to HARD. Set MODE if the
X * mode line needs to be updated (the "*" has to be set).
X */
lchange(flag)
register int	flag;
{
X	register WINDOW *wp;
X
X	if (curbp->b_nwnd != 1) { 		/* Ensure hard. 	*/
X		flag |= WFHARD;
X	}
X	if ((curbp->b_flag&BFCHG) == 0) {	/* First change, so	*/
X		flag |= WFMODE; 		/* update mode lines.	*/
X		curbp->b_flag |= BFCHG;
X	}
X	wp = wheadp;
X	while (wp != NULL) {
X		if (wp->w_bufp == curbp)
X			wp->w_flag |= flag;
X		wp = wp->w_wndp;
X	}
}
X
insspace(f, n)	/* insert spaces forward into text */
int f, n;	/* default flag and numeric argument */
{
X	linsert(n, ' ');
X	backchar(f, n);
}
X
/*
X * Insert "n" copies of the character "c" at the current location of dot. In
X * the easy case all that happens is the text is stored in the line. In the
X * hard case, the line has to be reallocated. When the window list is updated,
X * take special care; I screwed it up once. You always update dot in the
X * current window. You update mark, and a dot in another window, if it is
X * greater than the place where you did the insert. Return TRUE if all is
X * well, and FALSE on errors.
X */
linsert(n, c)
{
X	register char	*cp1;
X	register char	*cp2;
X	register LINE	*lp1;
X	register LINE	*lp2;
X	register LINE	*lp3;
X	register int	doto;
X	register int	i;
X	register WINDOW *wp;
X	register char	*ntext;
X	int nsize;
X
X	lchange(WFEDIT);
X	lp1 = curwp->w_dotp;			/* Current line 	*/
X	if (lp1 == curbp->b_linep) {		/* At the end: special	*/
X		if (curwp->w_doto != 0) {
X			mlwrite("bug: linsert");
X			return (FALSE);
X		}
X		if ((lp2=lalloc(n)) == NULL)	/* Allocate new line	*/
X			return (FALSE);
X		copy_for_undo(lp1->l_bp); /* don't want preundodot to point
X					   *	at a new line if this is the
X					   *	first change */
X		lp3 = lp1->l_bp;		/* Previous line	*/
X		lp3->l_fp = lp2;		/* Link in		*/
X		lp2->l_fp = lp1;
X		lp1->l_bp = lp2;
X		lp2->l_bp = lp3;
X		for (i=0; i<n; ++i)
X			lp2->l_text[i] = c;
X		curwp->w_dotp = lp2;
X		curwp->w_doto = n;
X		tag_for_undo(lp2);
X		return (TRUE);
X	}
X	doto = curwp->w_doto;			/* Save for later.	*/
X	if (lp1->l_used+n > lp1->l_size) {	/* Hard: reallocate	*/
X		copy_for_undo(lp1);
X		/* first, create the new image */
X		if ((ntext=malloc(nsize = roundup(lp1->l_used+n))) == NULL)
X			return (FALSE);
X		memcpy(&ntext[0],      &lp1->l_text[0],    doto);
X		memset(&ntext[doto],   c, n);
X		memcpy(&ntext[doto+n], &lp1->l_text[doto], lp1->l_used-doto );
X		free((char *)lp1->l_text);
X		lp1->l_text = ntext;
X		lp1->l_size = nsize;
X		lp1->l_used += n;
X	} else {				/* Easy: in place	*/
X		copy_for_undo(lp1);
X		/* don't used memcpy:  overlapping regions.... */
X		lp1->l_used += n;
X		cp2 = &lp1->l_text[lp1->l_used];
X		cp1 = cp2-n;
X		while (cp1 != &lp1->l_text[doto])
X			*--cp2 = *--cp1;
X		for (i=0; i<n; ++i)		/* Add the characters	*/
X			lp1->l_text[doto+i] = c;
X	}
X	wp = wheadp;				/* Update windows	*/
X	while (wp != NULL) {
X		if (wp->w_dotp == lp1) {
X			if (wp==curwp || wp->w_doto>doto)
X				wp->w_doto += n;
X		}
X		if (wp->w_mkp == lp1) {
X			if (wp->w_mko > doto)
X				wp->w_mko += n;
X		}
X		if (wp->w_ldmkp == lp1) {
X			if (wp->w_ldmko > doto)
X				wp->w_ldmko += n;
X		}
X		wp = wp->w_wndp;
X	}
X	if (curbp->b_nmmarks != NULL) { /* fix the named marks */
X		struct MARK *mp;
X		for (i = 0; i < 26; i++) {
X			mp = &(curbp->b_nmmarks[i]);
X			if (mp->markp == lp1) {
X				if (mp->marko > doto)
X					mp->marko += n;
X			}
X		}
X	}
X	return (TRUE);
}
X
/*
X * Insert a newline into the buffer at the current location of dot in the
X * current window. The funny ass-backwards way it does things is not a botch;
X * it just makes the last line in the file not a special case. Return TRUE if
X * everything works out and FALSE on error (memory allocation failure). The
X * update of dot and mark is a bit easier then in the above case, because the
X * split forces more updating.
X */
lnewline()
{
X	register char	*cp1;
X	register char	*cp2;
X	register LINE	*lp1;
X	register LINE	*lp2;
X	register int	doto;
X	register WINDOW *wp;
X
X	lchange(WFHARD|WFINS);
X	lp1  = curwp->w_dotp;			/* Get the address and	*/
X	doto = curwp->w_doto;			/* offset of "."	*/
X	if (lp1 != curbp->b_linep)
X		copy_for_undo(lp1);
X	if ((lp2=lalloc(doto)) == NULL) 	/* New first half line	*/
X		return (FALSE);
X	cp1 = &lp1->l_text[0];			/* Shuffle text around	*/
X	cp2 = &lp2->l_text[0];
X	while (cp1 != &lp1->l_text[doto])
X		*cp2++ = *cp1++;
X	cp2 = &lp1->l_text[0];
X	while (cp1 != &lp1->l_text[lp1->l_used])
X		*cp2++ = *cp1++;
X	lp1->l_used -= doto;
X	/* put lp2 in above lp1 */
X	lp2->l_bp = lp1->l_bp;
X	lp1->l_bp = lp2;
X	lp2->l_bp->l_fp = lp2;
X	lp2->l_fp = lp1;
X	tag_for_undo(lp2);
X	dumpuline(lp1);
X	wp = wheadp;				/* Windows		*/
X	while (wp != NULL) {
X		if (wp->w_linep == lp1)
X			wp->w_linep = lp2;
X		if (wp->w_dotp == lp1) {
X			if (wp->w_doto < doto)
X				wp->w_dotp = lp2;
X			else
X				wp->w_doto -= doto;
X		}
X		if (wp->w_mkp == lp1) {
X			if (wp->w_mko < doto)
X				wp->w_mkp = lp2;
X			else
X				wp->w_mko -= doto;
X		}
X		if (wp->w_ldmkp == lp1) {
X			if (wp->w_ldmko < doto)
X				wp->w_ldmkp = lp2;
X			else
X				wp->w_ldmko -= doto;
X		}
X		wp = wp->w_wndp;
X	}
X	if (curbp->b_nmmarks != NULL) { /* fix the named marks */
X		int i;
X		struct MARK *mp;
X		for (i = 0; i < 26; i++) {
X			mp = &(curbp->b_nmmarks[i]);
X			if (mp->markp == lp1) {
X				if (mp->marko < doto)
X					mp->markp = lp2;
X				else
X					mp->marko -= doto;
X			}
X		}
X	}
X	return (TRUE);
}
X
/*
X * This function deletes "n" bytes, starting at dot. It understands how do deal
X * with end of lines, etc. It returns TRUE if all of the characters were
X * deleted, and FALSE if they were not (because dot ran into the end of the
X * buffer. The "kflag" is TRUE if the text should be put in the kill buffer.
X */
ldelete(n, kflag)
long n; 	/* # of chars to delete */
int kflag;	/* put killed text in kill buffer flag */
{
X	register char	*cp1;
X	register char	*cp2;
X	register LINE	*dotp;
X	register LINE	*nlp;
X	register int	doto;
X	register int	chunk;
X	register WINDOW *wp;
X	register int i,s;
X
X	while (n != 0) {
X		dotp = curwp->w_dotp;
X		doto = curwp->w_doto;
X		if (dotp == curbp->b_linep)	/* Hit end of buffer.	*/
X			return (FALSE);
X		chunk = dotp->l_used-doto;	/* Size of chunk.	*/
X		if (chunk > (int)n)
X			chunk = (int)n;
X		if (chunk == 0) {		/* End of line, merge.	*/
X			lchange(WFHARD|WFKILLS);
X			/* first take out any whole lines below this one */
X			nlp = lforw(dotp);
X			while (nlp != curbp->b_linep && llength(nlp)+1 < n) {
X				if (kflag) {
X					s = kinsert('\n');
X					for (i = 0; i < llength(nlp) && 
X								s == TRUE; i++)
X						s = kinsert(lgetc(nlp,i));
X					if (s != TRUE)
X						return(FALSE);
X				}
X				lremove(curbp,nlp);
X				toss_to_undo(nlp);
X				n -= llength(nlp)+1;
X				nlp = lforw(dotp);
X			}
X			if ((s = ldelnewline()) != TRUE)
X				return (s);
X			if (kflag && (s = kinsert('\n')) != TRUE)
X				return (s);
X			--n;
X			continue;
X		}
X		lchange(WFEDIT);
X		copy_for_undo(dotp);
X		cp1 = &dotp->l_text[doto];	/* Scrunch text.	*/
X		cp2 = cp1 + chunk;
X		if (kflag) {		/* Kill?		*/
X			while (cp1 != cp2) {
X				if ((s = kinsert(*cp1)) != TRUE)
X					return (s);
X				++cp1;
X			}
X			cp1 = &dotp->l_text[doto];
X		}
X		while (cp2 != &dotp->l_text[dotp->l_used])
X			*cp1++ = *cp2++;
X		dotp->l_used -= chunk;
X		wp = wheadp;			/* Fix windows		*/
X		while (wp != NULL) {
X			if (wp->w_dotp==dotp && wp->w_doto > doto) {
X				wp->w_doto -= chunk;
X				if (wp->w_doto < doto)
X					wp->w_doto = doto;
X			}
X			if (wp->w_mkp==dotp && wp->w_mko > doto) {
X				wp->w_mko -= chunk;
X				if (wp->w_mko < doto)
X					wp->w_mko = doto;
X			}
X			if (wp->w_ldmkp==dotp && wp->w_ldmko > doto) {
X				wp->w_ldmko -= chunk;
X				if (wp->w_ldmko < doto)
X					wp->w_ldmko = doto;
X			}
X			wp = wp->w_wndp;
X		}
X		if (curbp->b_nmmarks != NULL) { /* fix the named marks */
X			struct MARK *mp;
X			for (i = 0; i < 26; i++) {
X				mp = &(curbp->b_nmmarks[i]);
X				if (mp->markp==dotp && mp->marko > doto) {
X					mp->marko -= chunk;
X					if (mp->marko < doto)
X						mp->marko = doto;
X				}
X			}
X		}
X		n -= chunk;
X	}
X	return (TRUE);
}
X
/* getctext:	grab and return a string with the text of
X		the current line
*/
X
char *getctext()
X
{
X	register LINE *lp;	/* line to copy */
X	register int size;	/* length of line to return */
X	register char *sp;	/* string pointer into line */
X	register char *dp;	/* string pointer into returned line */
X	char rline[NSTRING];	/* line to return */
X
X	/* find the contents of the current line and its length */
X	lp = curwp->w_dotp;
X	sp = lp->l_text;
X	size = lp->l_used;
X	if (size >= NSTRING)
X		size = NSTRING - 1;
X
X	/* copy it across */
X	dp = rline;
X	while (size--)
X		*dp++ = *sp++;
X	*dp = 0;
X	return(rline);
}
X
#if ! SMALLER
/* putctext:	replace the current line with the passed in text	*/
X
putctext(iline)
char *iline;	/* contents of new line */
{
X	register int status;
X
X	/* delete the current line */
X	curwp->w_doto = 0;	/* starting at the beginning of the line */
X	if ((status = deltoeol(TRUE, 1)) != TRUE)
X		return(status);
X
X	/* insert the new line */
X	while (*iline) {
X		if (*iline == '\n') {
X			if (lnewline() != TRUE)
X				return(FALSE);
X		} else {
X			if (linsert(1, *iline) != TRUE)
X				return(FALSE);
X		}
X		++iline;
X	}
X	status = lnewline();
X	backline(TRUE, 1);
X	return(status);
}
#endif
X
/*
X * Delete a newline. Join the current line with the next line. If the next line
X * is the magic header line always return TRUE; merging the last line with the
X * header line can be thought of as always being a successful operation, even
X * if nothing is done, and this makes the kill buffer work "right". Easy cases
X * can be done by shuffling data around. Hard cases require that lines be moved
X * about in memory. Return FALSE on error and TRUE if all looks ok. Called by
X * "ldelete" only.
X */
ldelnewline()
{
X	register char	*cp1;
X	register char	*cp2;
X	register LINE	*lp1;
X	register LINE	*lp2;
X	register LINE	*lp3;
X	register WINDOW *wp;
X
X	lp1 = curwp->w_dotp;
X	/* if the current line is empty, remove it */
X	if (lp1->l_used == 0) {		/* Blank line.		*/
X		lremove(curbp,lp1);
X		toss_to_undo(lp1);
X		return (TRUE);
X	}
X	lp2 = lp1->l_fp;
X	/* if the next line is empty, that's "currline\n\n", so we
X		remove the second \n by deleting the next line */
X	/* but never delete the newline on the last non-empty line */
X	if (lp2 == curbp->b_linep)
X		return (TRUE);
X	else if (lp2->l_used == 0) {
X		/* next line blank? */
X		lremove(curbp,lp2);
X		toss_to_undo(lp2);
X		return (TRUE);
X	}
X	/* no room in line above, make room */
X	if (lp2->l_used > lp1->l_size-lp1->l_used) {
X		char *ntext;
X		int nsize;
X		copy_for_undo(lp1);
X		/* first, create the new image */
X		if ((ntext=malloc(nsize = roundup(lp1->l_used + lp2->l_used)))
X								 == NULL)
X			return (FALSE);
X		memcpy(&ntext[0],      &lp1->l_text[0],    lp1->l_used);
X		free((char *)lp1->l_text);
X		lp1->l_text = ntext;
X	}
X	cp1 = &lp1->l_text[lp1->l_used];
X	cp2 = &lp2->l_text[0];
X	while (cp2 != &lp2->l_text[lp2->l_used])
X		*cp1++ = *cp2++;
X	/* check all windows for references to the deleted line */
X	wp = wheadp;
X	while (wp != NULL) {
X		if (wp->w_linep == lp2)
X			wp->w_linep = lp1;
X		if (wp->w_dotp == lp2) {
X			wp->w_dotp  = lp1;
X			wp->w_doto += lp1->l_used;
X		}
X		if (wp->w_mkp == lp2) {
X			wp->w_mkp  = lp1;
X			wp->w_mko += lp1->l_used;
X		}
X		if (wp->w_ldmkp == lp2) {
X			wp->w_ldmkp  = lp1;
X			wp->w_ldmko += lp1->l_used;
X		}
X		wp = wp->w_wndp;
X	}
X	if (curbp->b_nmmarks != NULL) { /* fix the named marks */
X		int i;
X		struct MARK *mp;
X		for (i = 0; i < 26; i++) {
X			mp = &(curbp->b_nmmarks[i]);
X			if (mp->markp == lp2) {
X				mp->markp  = lp1;
X				mp->marko += lp1->l_used;
X			}
X		}
X	}
X	lp1->l_used += lp2->l_used;
X	lp1->l_fp = lp2->l_fp;
X	lp2->l_fp->l_bp = lp1;
X	dumpuline(lp1);
X	toss_to_undo(lp2);
X	return (TRUE);
}
X
/*
X * Delete all of the text saved in the kill buffer. Called by commands when a
X * new kill context is being created. The kill buffer array is released, just
X * in case the buffer has grown to immense size. No errors.
X */
kdelete()
{
X
X	if ((kregflag & KAPPEND) != 0)
X		kregflag = KAPPEND;
X	else
X		kregflag = KNEEDCLEAN;
X
}
X
/*
X * Insert a character to the kill buffer, allocating new chunks as needed.
X * Return TRUE if all is well, and FALSE on errors.
X */
X
kinsert(c)
int c;		/* character to insert in the kill buffer */
{
X	KILL *nchunk;	/* ptr to newly malloced chunk */
X	KILLREG *kbp = &kbs[ukb];
X
X	if ((kregflag & KNEEDCLEAN) && kbs[ukb].kbufh != NULL) {
X		KILL *kp;	/* ptr to scan kill buffer chunk list */
X
X		/* first, delete all the chunks */
X		kbs[ukb].kbufp = kbs[ukb].kbufh;
X		while (kbs[ukb].kbufp != NULL) {
X			kp = kbs[ukb].kbufp->d_next;
X			free((char *)(kbs[ukb].kbufp));
X			kbs[ukb].kbufp = kp;
X		}
X
X		/* and reset all the kill buffer pointers */
X		kbs[ukb].kbufh = kbs[ukb].kbufp = NULL;
X		kbs[ukb].kused = KBLOCK; 	        
X
X	}
X	kregflag &= ~KNEEDCLEAN;
X	kbs[ukb].kbflag = kregflag;
X
X	/* check to see if we need a new chunk */
X	if (kbp->kused >= KBLOCK || kbp->kbufh == NULL) {
X		if ((nchunk = (KILL *)malloc(sizeof(KILL))) == NULL)
X			return(FALSE);
X		if (kbp->kbufh == NULL)	/* set head ptr if first time */
X			kbp->kbufh = nchunk;
X		/* point the current to this new one */
X		if (kbp->kbufp != NULL)
X			kbp->kbufp->d_next = nchunk;
X		kbp->kbufp = nchunk;
X		kbp->kbufp->d_next = NULL;
X		kbp->kused = 0;
X	}
X
X	/* and now insert the character */
X	kbp->kbufp->d_chunk[kbp->kused++] = c;
X	return(TRUE);
}
X
/* select one of the named registers for use with the following command */
/*  this could actually be handled as a command prefix, in kbdseq(), much
X	the way ^X-cmd and META-cmd are done, except that we need to be
X	able to accept any of
X		 3"adw	"a3dw	"ad3w
X	to delete 3 words into register a.  So this routine gives us an
X	easy way to handle the second case.  (The third case is handled in
X	operators(), the first in main())
*/
usekreg(f,n)
{
X	int c, status;
X
X	/* take care of incrementing the buffer number, if we're replaying
X		a command via 'dot' */
X	incr_dot_kregnum();
X
X	c = kbd_key();
X
X	if (isdigit(c))
X		ukb = c - '0';
X	else if (islower(c))
X		ukb = c - 'a' + 10;  /* named buffs are in 10 through 36 */
X	else if (isupper(c)) {
X		ukb = c - 'A' + 10;
X		kregflag |= KAPPEND;
X	} else {
X		TTbeep();
X		return (FALSE);
X	}
X
X	/* get the next command from the keyboard */
X	c = kbd_seq();
X
X	/* allow second chance for entering counts */
X	if (f == FALSE) {
X		do_num_proc(&c,&f,&n);
X		do_rept_arg_proc(&c,&f,&n);
X	}
X
X	/* and execute the command */
X	status = execute(kcod2fnc(c), f, n);
X
X	ukb = 0;
X	kregflag = 0;
X
X	return(status);
X	
}
X
/* buffers 0 through 9 are circulated automatically for full-line deletes */
/* we re-use one of them until the KLINES flag is on, then we advance */
/* to the next */
kregcirculate(killing)
{
X	static lastkb; /* index of the real "0 */
X
X	if (ukb >= 10) /* then the user specified a lettered buffer */
X		return;
X
X	/* we only allow killing into the real "0 */
X	/* ignore any other buffer spec */
X	if (killing) {
X		if ((kbs[lastkb].kbflag & KLINES) && 
X			! (kbs[lastkb].kbflag & KYANK)) {
X			if (--lastkb < 0) lastkb = 9;
X			kbs[lastkb].kbflag = 0;
X		}
X		ukb = lastkb;
X	} else {
X		/* let 0 pass unmolested -- it is the default */
X		if (ukb == 0) {
X			ukb = lastkb; 
X		} else {
X		/* for the others, if the current "0 has lines in it, it
X		    must be `"1', else "1 is `"1'.  get it? */
X			if (kbs[lastkb].kbflag & KLINES)
X				ukb = (lastkb + ukb - 1) % 10;
X			else
X				ukb = (lastkb + ukb) % 10;
X		}
X	}
X	
}
X
putbefore(f,n)
{
X	return doput(f,n,FALSE,FALSE);
}
X
putafter(f,n)
{
X	return doput(f,n,TRUE,FALSE);
}
X
lineputbefore(f,n)
{
X	return doput(f,n,FALSE,TRUE);
}
X
lineputafter(f,n)
{
X	return doput(f,n,TRUE,TRUE);
}
X
X
doput(f,n,after,putlines)
{
X	int s, oukb, lining;
X	
X	if (!f)
X		n = 1;
X		
X	oukb = ukb;
X	kregcirculate(FALSE);
X	if (kbs[ukb].kbufh == NULL) {
X		if (ukb != 0)
X			mlwrite("Nothing in register %c", 
X				(oukb<10)? oukb+'0' : oukb-10+'a');
X		TTbeep();
X		return(FALSE);
X	}
X	lining = (putlines == TRUE || (kbs[ukb].kbflag & KLINES));
X	if (lining) {
X		if (after && curwp->w_dotp != curbp->b_linep)
X			curwp->w_dotp = lforw(curwp->w_dotp);
X		curwp->w_doto = 0;
X	} else {
X		if (after && curwp->w_doto != llength(curwp->w_dotp))
X			forwchar(TRUE,1);
X	}
X	setmark();
X	s = put(n,lining);
X	if (s == TRUE)
X		swapmark();
X	if (curwp->w_dotp == curbp->b_linep)
X		curwp->w_dotp = lback(curwp->w_dotp);
X	if (lining)
X		firstnonwhite(FALSE,0);
X	ukb = 0;
X	return (s);
}
X
/*
X * Put text back from the kill register.
X */
put(n,aslines)
{
X	register int	c;
X	register int	i;
X	int wasnl, suppressnl;
X	register char	*sp;	/* pointer into string to insert */
X	KILL *kp;		/* pointer into kill register */
X	
X	if (n < 0)
X		return FALSE;
X		
X	/* make sure there is something to put */
X	if (kbs[ukb].kbufh == NULL)
X		return TRUE;		/* not an error, just nothing */
X
X	suppressnl = FALSE;
X	wasnl = FALSE;
X
X	/* for each time.... */
X	while (n--) {
X		kp = kbs[ukb].kbufh;
X		while (kp != NULL) {
X			if (kp->d_next == NULL)
X				i = kbs[ukb].kused;
X			else
X				i = KBLOCK;
X			sp = kp->d_chunk;
X			while (i--) {
X				if ((c = *sp++) == '\n') {
X					if (lnewline() != TRUE)
X						return FALSE;
X					wasnl = TRUE;
X				} else {
X					if (curwp->w_dotp == curbp->b_linep)
X						suppressnl = TRUE;
X					if (linsert(1, c) != TRUE)
X						return FALSE;
X					wasnl = FALSE;
X				}
X			}
X			kp = kp->d_next;
X		}
X		if (wasnl) {
X			if (suppressnl) {
X				if (ldelnewline() != TRUE)
X					return FALSE;
X			}
X		} else {
X			if (aslines && !suppressnl) {
X				if (lnewline() != TRUE)
X					return FALSE;
X			}
X		}
X	}
X        curwp->w_flag |= WFHARD;
X	return (TRUE);
}
SHAR_EOF
chmod 0444 line.c ||
echo 'restore of line.c failed'
Wc_c="`wc -c < 'line.c'`"
test 20714 -eq "$Wc_c" ||
	echo 'line.c: original size 20714, current size' "$Wc_c"
# ============= lock.c ==============
echo 'x - extracting lock.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'lock.c' &&
/*	LOCK:	File locking command routines for MicroEMACS
X		written by Daniel Lawrence
X								*/
X
#include <stdio.h>
#include "estruct.h"
#include "edef.h"
X
#if	FILOCK
#if	BSD
#include <sys/errno.h>
X
extern int sys_nerr;		/* number of system error messages defined */
extern char *sys_errlist[];	/* list of message texts */
extern int errno;		/* current error */
X
char *lname[NLOCKS];	/* names of all locked files */
int numlocks;		/* # of current locks active */
X
/* lockchk:	check a file for locking and add it to the list */
X
lockchk(fname)
X
char *fname;	/* file to check for a lock */
X
{
X	register int i;		/* loop indexes */
X	register int status;	/* return status */
X	char *undolock();
X
X	/* check to see if that file is already locked here */
X	if (numlocks > 0)
X		for (i=0; i < numlocks; ++i)
X			if (strcmp(fname, lname[i]) == 0)
X				return(TRUE);
X
X	/* if we have a full locking table, bitch and leave */
X	if (numlocks == NLOCKS) {
X		mlwrite("LOCK ERROR: Lock table full");
X		return(ABORT);
X	}
X
X	/* next, try to lock it */
X	status = lock(fname);
X	if (status == ABORT)	/* file is locked, no override */
X		return(ABORT);
X	if (status == FALSE)	/* locked, overriden, dont add to table */
X		return(TRUE);
X
X	/* we have now locked it, add it to our table */
X	lname[++numlocks - 1] = malloc(strlen(fname) + 1);
X	if (lname[numlocks - 1] == NULL) {	/* malloc failure */
X		undolock(fname);		/* free the lock */
X		mlwrite("Cannot lock, out of memory");
X		--numlocks;
X		return(ABORT);
X	}
X
X	/* everthing is cool, add it to the table */
X	strcpy(lname[numlocks-1], fname);
X	return(TRUE);
}
X
/*	lockrel:	release all the file locks so others may edit */
X
lockrel()
X
{
X	register int i;		/* loop index */
X	register int status;	/* status of locks */
X	register int s;		/* status of one unlock */
X
X	status = TRUE;
X	if (numlocks > 0)
X		for (i=0; i < numlocks; ++i) {
X			if ((s = unlock(lname[i])) != TRUE)
X				status = s;
X			free(lname[i]);
X		}
X	numlocks = 0;
X	return(status);
}
X
/* lock:	Check and lock a file from access by others
X		returns	TRUE = files was not locked and now is
X			FALSE = file was locked and overridden
X			ABORT = file was locked, abort command
*/
X
lock(fname)
X
char *fname;	/* file name to lock */
X
{
X	register char *locker;	/* lock error message */
X	register int status;	/* return status */
X	char msg[NSTRING];	/* message string */
X	char *dolock();
X
X	/* attempt to lock the file */
X	locker = dolock(fname);
X	if (locker == NULL)	/* we win */
X		return(TRUE);
X
X	/* file failed...abort */
X	if (strncmp(locker, "LOCK", 4) == 0) {
X		lckerror(locker);
X		return(ABORT);
X	}
X
X	/* someone else has it....override? */
X	strcpy(msg, "File in use by ");
X	strcat(msg, locker);
X	strcat(msg, ", overide?");
X	status = mlyesno(msg);		/* ask them */
X	if (status == TRUE)
X		return(FALSE);
X	else
X		return(ABORT);
}
X
/*	unlock:	Unlock a file
X		this only warns the user if it fails
X							*/
X
unlock(fname)
X
char *fname;	/* file to unlock */
X
{
X	register char *locker;	/* undolock return string */
X	char *undolock();
X
X	/* unclock and return */
X	locker = undolock(fname);
X	if (locker == NULL)
X		return(TRUE);
X
X	/* report the error and come back */
X	lckerror(locker);
X	return(FALSE);
}
X
lckerror(errstr)	/* report a lock error */
X
char *errstr;		/* lock error string to print out */
X
{
X	char obuf[NSTRING];	/* output buffer for error message */
X
X	strcpy(obuf, errstr);
X	strcat(obuf, " - ");
X	if (errno < sys_nerr)
X		strcat(obuf, sys_errlist[errno]);
X	else
X		strcat(obuf, "[can not get system error message]");
X	mlwrite(obuf);
}
#endif
#else
lckhello()	/* dummy function */
{
}
#endif
SHAR_EOF
chmod 0444 lock.c ||
echo 'restore of lock.c failed'
Wc_c="`wc -c < 'lock.c'`"
test 3557 -eq "$Wc_c" ||
	echo 'lock.c: original size 3557, current size' "$Wc_c"
# ============= main.c ==============
echo 'x - extracting main.c (Text)'
sed 's/^X//' << 'SHAR_EOF' > 'main.c' &&
/*
X *	This used to be MicroEMACS 3.9
X * 			written by Dave G. Conroy.
X *			substatially modified by Daniel M. Lawrence
X *
X *	(C)opyright 1987 by Daniel M. Lawrence
X *	MicroEMACS 3.9 can be copied and distributed freely for any
X *	non-commercial purposes. MicroEMACS 3.9 can only be incorporated
X *	into commercial software with the permission of the current author.
X *
X *	Turned into "VI Like Emacs", a.k.a. vile, by Paul Fox
SHAR_EOF
true || echo 'restore of main.c failed'
echo 'End of Vile part 8'
echo 'File main.c is continued in part 9'
echo 9 > _shar_seq_.tmp
exit 0
-- 
		paul fox, pgf at cayman.com, (617)494-1999
		Cayman Systems, 26 Landsdowne St., Cambridge, MA 02139



More information about the Alt.sources mailing list