[comp.os.minix] Part 3

jeff@questar.QUESTAR.MN.ORG (Jeff Holmes) (12/16/88)

	Part 3 of Sources for Stevie didn't make it to our
	site, could someone please email it to me.

				Thanks,
					Jeff
-- 
Jeff Holmes	                  DOMAIN: jeff@questar.mn.org 
Questar Data Systems                UUCP: amdahl!bungia!questar!jeff

darrylo@hpsrli.HP.COM (Darryl Okahata) (12/20/88)

In comp.os.minix, jeff@questar.QUESTAR.MN.ORG (Jeff Holmes) writes:

> 	Part 3 of Sources for Stevie didn't make it to our
> 	site, could someone please email it to me.

     They also didn't make it here.  Could someone please Email me a copy
or repost them?  Thanks.

     -- Darryl Okahata
	UUCP: {hpcea!, hpfcla!} hpnmd!darrylo
	Internet: darrylo%hpnmd@hpcea.HP.COM
	CIS: 75206,3074

Disclaimer: the above is the author's personal opinion and is not the
opinion or policy of his employer or of the little green men that
have been following him all day.

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

Some people in the USA didn't get stevie part 3 of 5
Here is it again:


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

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


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