[comp.sources.misc] v03i033: mg 2a part 9 of 15

BLARSON@ECLA.USC.EDU (Bob Larson) (05/26/88)

comp.sources.misc: Volume 3, Issue 33
Submitted-By: "Bob Larson" <BLARSON@ECLA.USC.EDU>
Archive-Name: mg2a/Part9

#	This is a shell archive.
#	Remove everything above and including the cut line.
#	Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar:    Shell Archiver
#	Run the following text with /bin/sh to create:
#	match.c
#	dir.c
#	re_search.c
#	sys/bsd/README
#	sys/bsd/Makefile
#	sys/bsd/spawn.c
#	sys/bsd/sysdef.h
#	sys/bsd/ttyio.c
#	sys/sysv/Makefile
#	sys/sysv/fileio.c
#	sys/sysv/spawn.c
#	sys/sysv/sysdef.h
#	sys/sysv/ttyio.c
# This archive created: Thu May 19 00:10:20 1988
# By:	blarson
if test -d sys
then true
else mkdir sys
fi
if test -d sys/bsd
then true
else mkdir sys/bsd
fi
if test -d sys/sysv
then true
else mkdir sys/sysv
fi
cat << \SHAR_EOF > match.c
/*
 * Name:	MicroEMACS
 *		Limited parenthesis matching routines
 *
 * The hacks in this file implement automatic matching
 * of (), [], {}, and other characters.	 It would be
 * better to have a full-blown syntax table, but there's
 * enough overhead in the editor as it is.
 *
 * Since I often edit Scribe code, I've made it possible to
 * blink arbitrary characters -- just bind delimiter characters
 * to "blink-matching-paren-hack"
 */
#include	"def.h"
#include	"key.h"

static int	balance();
static VOID	displaymatch();

/* Balance table. When balance() encounters a character
 * that is to be matched, it first searches this table
 * for a balancing left-side character.	 If the character
 * is not in the table, the character is balanced by itself.
 * This is to allow delimiters in Scribe documents to be matched.
 */

static struct balance {
	char left, right;
} bal[] = {
	{ '(', ')' },
	{ '[', ']' },
	{ '{', '}' },
	{ '<', '>' },
	{ '\0','\0'}
};

/*
 * Self-insert character, then show matching character,
 * if any.  Bound to "blink-matching-paren-command".
 */

showmatch(f, n)
{
	register int  i, s;

	if (f & FFRAND) return FALSE;
	for (i = 0; i < n; i++) {
		if ((s = selfinsert(FFRAND, 1)) != TRUE)
			return s;
		if (balance() != TRUE) /* unbalanced -- warn user */
			ttbeep();
	}
	return TRUE;
}

/*
 * Search for and display a matching character.
 *
 * This routine does the real work of searching backward
 * for a balancing character.  If such a balancing character
 * is found, it uses displaymatch() to display the match.
 */

static balance()
{
	register LINE	*clp;
	register int	cbo;
	int	c;
	int	i;
	int	rbal, lbal;
	int	depth;

	rbal = key.k_chars[key.k_count-1];

	/* See if there is a matching character -- default to the same */

	lbal = rbal;
	for (i = 0; bal[i].right != '\0'; i++)
		if (bal[i].right == rbal) {
			lbal = bal[i].left;
			break;
		}

	/* Move behind the inserted character.	We are always guaranteed    */
	/* that there is at least one character on the line, since one was  */
	/* just self-inserted by blinkparen.				    */

	clp = curwp->w_dotp;
	cbo = curwp->w_doto - 1;

	depth = 0;			/* init nesting depth		*/

	for (;;) {
		if (cbo == 0) {			/* beginning of line	*/
			clp = lback(clp);
			if (clp == curbp->b_linep)
				return (FALSE);
			cbo = llength(clp)+1;
		}
		if (--cbo == llength(clp))	/* end of line		*/
			c = '\n';
		else
			c = lgetc(clp,cbo);	/* somewhere in middle	*/

		/* Check for a matching character.  If still in a nested */
		/* level, pop out of it and continue search.  This check */
		/* is done before the nesting check so single-character	 */
		/* matches will work too.				 */
		if (c == lbal) {
			if (depth == 0) {
				displaymatch(clp,cbo);
				return (TRUE);
			}
			else
				depth--;
		}
		/* Check for another level of nesting.	*/
		if (c == rbal)
			depth++;
	}
	/*NOTREACHED*/
}


/*
 * Display matching character.
 * Matching characters that are not in the current window
 * are displayed in the echo line. If in the current
 * window, move dot to the matching character,
 * sit there a while, then move back.
 */

static VOID displaymatch(clp, cbo)
register LINE *clp;
register int  cbo;
{
	register LINE	*tlp;
	register int	tbo;
	register int	cp;
	register int	bufo;
	register int	c;
	int		inwindow;
	char		buf[NLINE];

	/* Figure out if matching char is in current window by	*/
	/* searching from the top of the window to dot.		*/

	inwindow = FALSE;
	for (tlp = curwp->w_linep; tlp != lforw(curwp->w_dotp); tlp = lforw(tlp))
		if (tlp == clp)
			inwindow = TRUE;

	if (inwindow == TRUE) {
		tlp = curwp->w_dotp;	/* save current position */
		tbo = curwp->w_doto;

		curwp->w_dotp  = clp;	/* move to new position */
		curwp->w_doto  = cbo;
		curwp->w_flag |= WFMOVE;

		update();		/* show match */
		sleep(1);		/* wait a bit */

		curwp->w_dotp	= tlp;	/* return to old position */
		curwp->w_doto	= tbo;
		curwp->w_flag  |= WFMOVE;
		update();
	}
	else {	/* match not in this window so display line in echo area */
		bufo = 0;
		for (cp = 0; cp < llength(clp); cp++) { /* expand tabs	*/
			c = lgetc(clp,cp);
			if (c != '\t'
#ifdef	NOTAB
				|| (curbp->b_flag & BFNOTAB)
#endif
				) if(ISCTRL(c)) {
				    buf[bufo++] = '^';
				    buf[bufo++] = CCHR(c);
				} else buf[bufo++] = c;
			else
				do {
					buf[bufo++] = ' ';
				} while (bufo & 7);
		}
		buf[bufo++] = '\0';
		ewprintf("Matches %s",buf);
	}
}
SHAR_EOF
cat << \SHAR_EOF > dir.c
/*
 * Name:	MG 2a
 *		Directory management functions
 * Created:	Ron Flax (ron@vsedev.vse.com)
 *		Modified for MG 2a by Mic Kaczmarczik 03-Aug-1987
 */

#include "def.h"

#ifndef NO_DIR
#ifndef	getwd			/* may be a #define */
char	*getwd();
#endif
char	*wdir;
static char cwd[NFILEN];

/*
 * Initialize anything the directory management routines need
 */
dirinit()
{
	if (!(wdir = getwd(cwd)))
		panic("Can't get current directory!");
}

/*
 * Change current working directory
 */
/*ARGSUSED*/
changedir(f, n)
{
	register int s;
	char bufc[NPAT];

	if ((s=ereply("Change default directory: ", bufc, NPAT)) != TRUE)
		return(s);
	if (bufc[0] == '\0')
		(VOID) strcpy(bufc, wdir);
	if (chdir(bufc) == -1) {
		ewprintf("Can't change dir to %s", bufc);
		return(FALSE);
	} else {
		if (!(wdir = getwd(cwd)))
			panic("Can't get current directory!");
		ewprintf("Current directory is now %s", wdir);
		return(TRUE);
	}
}

/*
 * Show current directory
 */
/*ARGSUSED*/
showcwdir(f, n)
{
	ewprintf("Current directory: %s", wdir);
	return(TRUE);
}
#endif
SHAR_EOF
cat << \SHAR_EOF > re_search.c
/*
 *		regular expression search commands for
 *			   MicroGnuEmacs
 *
 * This file contains functions to implement several of gnuemacs'
 * regular expression functions for MicroGnuEmacs.  Several of
 * the routines below are just minor rearrangements of the MicroGnuEmacs
 * non-regular expression search functions.  Hence some of them date back
 * in essential structure to the original MicroEMACS; others are modifications
 * of Rich Ellison's code.  I, Peter Newton, wrote about half from scratch.
 *
 * Although I have nothing to do with the GNU project, these functions
 * require the GNU project's regular expression package (files regex.c and
 * regex.h).  Hence, this file comes under the same copyright notice
 * as the GNU project's code.  As far as I know, the rest of MicroGnuEmacs
 * need not since it may be used independently of any GNU project code.	 In
 * any case, I certainly do not warrant either the correctness or utility
 * of this code.  The GNU project copyright notice follows.  Don't you
 * wish they would make it a bit shorter!
 */

/*
GNU Emacs copying permission notice Copyright (C) 1985 Richard M. Stallman
     Verbatim copies of this document, including its copyright notice,
     may be distributed by anyone in any manner.
     Distribution with modifications is not permitted.

GNU Emacs is distributed in the hope that it will be useful,
but without any warranty.  No author or distributor
accepts responsibility to anyone for the consequences of using it
or for whether it serves any particular purpose or works at all,
unless he says so in writing.

Everyone is granted permission to copy, modify and redistribute
GNU Emacs under the following conditions:

   Permission is granted to anyone to make or distribute verbatim copies
   of GNU Emacs source code as received, in any medium, provided that all
   copyright notices and permission and nonwarranty notices are preserved,
   and that the distributor grants the recipient permission
   for further redistribution as permitted by this document,
   and gives him and points out to him an exact copy of this document
   to inform him of his rights.

   Permission is granted to distribute modified versions
   of GNU Emacs source code, or of portions of it,
   under the above conditions, provided also that all
   changed files carry prominent notices stating who last changed them
   and that all the GNU-Emacs-derived material, including everything
   packaged together with it and not independently usable, is
   distributed under the conditions stated in this document.

   Permission is granted to distribute GNU Emacs in
   compiled or executable form under the same conditions applying
   for source code, provided that either
    A.	it is accompanied by the corresponding machine-readable
      source code, or
    B.	it is accompanied by a written offer, with no time limit,
      to give anyone a machine-readable copy of the corresponding
      source code in return for reimbursement of the cost of distribution.
      This written offer must permit verbatim duplication by anyone.
    C.	it is distributed by someone who received only the
      executable form, and is accompanied by a copy of the
      written offer of source code which he received along with it.

In other words, you are welcome to use, share and improve GNU Emacs
You are forbidden to forbid anyone else to use, share and improve
what you give them.   Help stamp out software-hoarding!
*/

#ifdef	REGEX
#include	"def.h"
#include	"macro.h"

#define SRCH_BEGIN	(0)			/* Search sub-codes.	*/
#define SRCH_FORW	(-1)
#define SRCH_BACK	(-2)
#define SRCH_NOPR	(-3)
#define SRCH_ACCM	(-4)
#define SRCH_MARK	(-5)

char	re_pat[NPAT];			/* Regex pattern		*/
int	re_srch_lastdir = SRCH_NOPR;	 /* Last search flags. */
int	casefoldsearch = TRUE;		 /* Does search ignore case ? */

/* Indexed by a character, gives the upper case equivalent of the character */

static char upcase[0400] =
  { 000, 001, 002, 003, 004, 005, 006, 007,
    010, 011, 012, 013, 014, 015, 016, 017,
    020, 021, 022, 023, 024, 025, 026, 027,
    030, 031, 032, 033, 034, 035, 036, 037,
    040, 041, 042, 043, 044, 045, 046, 047,
    050, 051, 052, 053, 054, 055, 056, 057,
    060, 061, 062, 063, 064, 065, 066, 067,
    070, 071, 072, 073, 074, 075, 076, 077,
    0100, 0101, 0102, 0103, 0104, 0105, 0106, 0107,
    0110, 0111, 0112, 0113, 0114, 0115, 0116, 0117,
    0120, 0121, 0122, 0123, 0124, 0125, 0126, 0127,
    0130, 0131, 0132, 0133, 0134, 0135, 0136, 0137,
    0140, 0101, 0102, 0103, 0104, 0105, 0106, 0107,
    0110, 0111, 0112, 0113, 0114, 0115, 0116, 0117,
    0120, 0121, 0122, 0123, 0124, 0125, 0126, 0127,
    0130, 0131, 0132, 0173, 0174, 0175, 0176, 0177,
    0200, 0201, 0202, 0203, 0204, 0205, 0206, 0207,
    0210, 0211, 0212, 0213, 0214, 0215, 0216, 0217,
    0220, 0221, 0222, 0223, 0224, 0225, 0226, 0227,
    0230, 0231, 0232, 0233, 0234, 0235, 0236, 0237,
    0240, 0241, 0242, 0243, 0244, 0245, 0246, 0247,
    0250, 0251, 0252, 0253, 0254, 0255, 0256, 0257,
    0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267,
    0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277,
    0300, 0301, 0302, 0303, 0304, 0305, 0306, 0307,
    0310, 0311, 0312, 0313, 0314, 0315, 0316, 0317,
    0320, 0321, 0322, 0323, 0324, 0325, 0326, 0327,
    0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337,
    0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347,
    0350, 0351, 0352, 0353, 0354, 0355, 0356, 0357,
    0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367,
    0370, 0371, 0372, 0373, 0374, 0375, 0376, 0377
  };

/*
 * Search forward.
 * Get a search string from the user, and search for it,
 * starting at ".". If found, "." gets moved to just after the
 * matched characters, and display does all the hard stuff.
 * If not found, it just prints a message.
 */
/*ARGSUSED*/
re_forwsearch(f, n) {
	register int	s;

	if ((s=re_readpattern("RE Search")) != TRUE)
		return (s);
	if (re_forwsrch() == FALSE) {
		ewprintf("Search failed: \"%s\"", re_pat);
		return (FALSE);
	}
	re_srch_lastdir = SRCH_FORW;
	return (TRUE);
}

/*
 * Reverse search.
 * Get a search string from the	 user, and search, starting at "."
 * and proceeding toward the front of the buffer. If found "." is left
 * pointing at the first character of the pattern [the last character that
 * was matched].
 */
/*ARGSUSED*/
re_backsearch(f, n) {
	register int	s;

	if ((s=re_readpattern("RE Search backward")) != TRUE)
		return (s);
	if (re_backsrch() == FALSE) {
		ewprintf("Search failed: \"%s\"", re_pat);
		return (FALSE);
	}
	re_srch_lastdir = SRCH_BACK;
	return (TRUE);
}



/*
 * Search again, using the same search string
 * and direction as the last search command. The direction
 * has been saved in "srch_lastdir", so you know which way
 * to go.
 */
/*ARGSUSED*/
/*  This code has problems-- some incompatibility(?) with
    extend.c causes match to fail when it should not.
 */
re_searchagain(f, n) {

  if (re_srch_lastdir == SRCH_NOPR) {
    ewprintf("No last search");
    return (FALSE);
  }

  if (re_srch_lastdir == SRCH_FORW) {
    if (re_forwsrch() == FALSE) {
      ewprintf("Search failed: \"%s\"", re_pat);
      return (FALSE);
    }
    return (TRUE);
  }
  if (re_srch_lastdir == SRCH_BACK) {
    if (re_backsrch() == FALSE) {
      ewprintf("Search failed: \"%s\"", re_pat);
      return (FALSE);
    }
    return (TRUE);
  }
}


#include "regex.h"
#define BYTEWIDTH 8

/* Compiled regex goes here-- changed only when new pattern read */
static struct re_pattern_buffer re_buff;
static char fastmap[(1 << BYTEWIDTH)];

/* regs holds boundaries of matched text */
static struct re_registers regs;

/*
 * Re-Query Replace.
 *	Replace strings selectively.  Does a search and replace operation.
 */
/*ARGSUSED*/
re_queryrepl(f, n) {
	register int	s;
	register int	rcnt = 0;	/* Replacements made so far	*/
	register int	plen;		/* length of found string	*/
	char		news[NPAT];	/* replacement string		*/

	/* Casefold check */
	if (!casefoldsearch) f = TRUE;

	if ((s=re_readpattern("RE Query replace")) != TRUE)
		return (s);
	if ((s=ereply("Query replace %s with: ",news, NPAT, re_pat)) == ABORT)
		return (s);
	if (s == FALSE)
		news[0] = '\0';
	ewprintf("Query replacing %s with %s:", re_pat, news);

	/*
	 * Search forward repeatedly, checking each time whether to insert
	 * or not.  The "!" case makes the check always true, so it gets put
	 * into a tighter loop for efficiency.
	 */

	while (re_forwsrch() == TRUE) {
	retry:
		update();
		switch (getkey(FALSE)) {
		case ' ':
			plen = regs.end[0] - regs.start[0];
			if (re_doreplace((RSIZE) plen, news, f) == FALSE)
				return (FALSE);
			rcnt++;
			break;

		case '.':
			plen = regs.end[0] - regs.start[0];
			if (re_doreplace((RSIZE) plen, news, f) == FALSE)
				return (FALSE);
			rcnt++;
			goto stopsearch;

		case CCHR('G'): /* ^G */
			(VOID) ctrlg(FFRAND, 0);
		case CCHR('['): /* ESC */
		case '`':
			goto stopsearch;

		case '!':
			do {
				plen = regs.end[0] - regs.start[0];
				if (re_doreplace((RSIZE) plen, news, f) == FALSE)
					return (FALSE);
				rcnt++;
			} while (re_forwsrch() == TRUE);
			goto stopsearch;

		case CCHR('?'):		/* To not replace */
			break;

		default:
ewprintf("<SP> replace, [.] rep-end, <DEL> don't, [!] repl rest <ESC> quit");
			goto retry;
		}
	}
stopsearch:
	curwp->w_flag |= WFHARD;
	update();
	if (!inmacro) {
		if (rcnt == 0)
			ewprintf("(No replacements done)");
		else if (rcnt == 1)
			ewprintf("(1 replacement done)");
		else
			ewprintf("(%d replacements done)", rcnt);
	}
	return TRUE;
}



/* Routine re_doreplace calls lreplace to make replacements needed by
 * re_query replace.  Its reason for existence is to deal with \1,
 * \2. etc.
 */

/* Maximum length of replacement string */
#define REPLEN 256

re_doreplace(plen, st, f)
     register RSIZE  plen;		     /* length to remove	     */
     char	     *st;		     /* replacement string	     */
     int	     f;			     /* case hack disable	     */
{
  int s;
  int num, k;
  register int j;
  int more, state;
  LINE *clp;
  char repstr[REPLEN];

  clp = curwp->w_dotp;
  more = TRUE;
  j = 0;
  state = 0;

  /* The following FSA parses the replacement string */
  while (more) {
    switch (state) {

    case 0: if (*st == '\\') {
	      st++;
	      state = 1;
	    }
	    else if (*st == '\0')
	      more = FALSE;
	    else {
	      repstr[j] = *st;
	      j++; if (j >= REPLEN) return(FALSE);
	      st++;
	    }
	    break;
    case 1: if (*st >= '0' && *st <= '9') {
	      num = *st - '0';
	      st++;
	      state = 2;
	    }
	    else if (*st == '\0')
	      more = FALSE;
	    else {
	      repstr[j] = *st;
	      j++; if (j >= REPLEN) return(FALSE);
	      st++;
	      state = 0;
	    }
	    break;
    case 2: if (*st >= '0' && *st <= '9') {
	      num = 10*num + *st - '0';
	      st++;
	    }
	    else {
	      if (num >= RE_NREGS) return(FALSE);
	      k = regs.end[num] - regs.start[num];
	      if (j+k >= REPLEN) return(FALSE);
	      bcopy(&(clp->l_text[regs.start[num]]), &repstr[j], k);
	      j += k;
	      if (*st == '\0')
		more = FALSE;
	      if (*st == '\\') {
		st++;
		state = 1;
	      }
	      else {
		repstr[j] = *st;
		j++; if (j >= REPLEN) return(FALSE);
		st++;
		state = 0;
	      }
	    }
	    break;
	  } /* end case */
  } /* end while */

  repstr[j] = '\0';

  s = lreplace(plen, repstr, f);

  return(s);
}



/*
 * This routine does the real work of a
 * forward search. The pattern is sitting in the external
 * variable "pat". If found, dot is updated, the window system
 * is notified of the change, and TRUE is returned. If the
 * string isn't found, FALSE is returned.
 */
re_forwsrch() {

  register LINE *clp;
  register int tbo;
  int ntries;
  int i, plen;

  clp = curwp->w_dotp;
  tbo = curwp->w_doto;

  if (tbo == clp->l_used)
    /* Don't start matching off end of line-- must
     * move to beginning of next line, unless at end
     */
    if (clp != curbp->b_linep) {
      clp = lforw(clp);
      tbo = 0;
    }


  /* Note this loop does not process the last line, but this editor
     always makes the last line empty so this is good.
   */

  while (clp != (curbp->b_linep)) {

     ntries = llength(clp) - tbo;
     i = re_search (&re_buff, ltext(clp), llength(clp), tbo, ntries, &regs);

     if (i == -1) {
       clp = lforw(clp);
       tbo = 0;
     }
     else {
       curwp->w_doto = regs.end[0];
       curwp->w_dotp = clp;
       curwp->w_flag |= WFMOVE;
       return (TRUE);
     }

   }

  return(FALSE);

}


/*
 * This routine does the real work of a
 * backward search. The pattern is sitting in the external
 * variable "re_pat". If found, dot is updated, the window system
 * is notified of the change, and TRUE is returned. If the
 * string isn't found, FALSE is returned.
 */
re_backsrch() {

  register LINE *clp;
  register int tbo;
  int ntries;
  int i, startpos;
char m[1];

  clp = curwp->w_dotp;
  tbo = curwp->w_doto;

  /* Start search one position to the left of dot */
  tbo = tbo - 1;
  if (tbo < 0) {
    /* must move up one line */
    clp = lback(clp);
    tbo = llength(clp);
  }

  /* Note this loop does not process the last line, but this editor
     always makes the last line empty so this is good.
   */

  while (clp != (curbp->b_linep)) {

     ntries = tbo;
     i = re_search (&re_buff, ltext(clp), llength(clp), tbo, -ntries, &regs);

     if (i == -1) {
       clp = lback(clp);
       tbo = llength(clp);
     }
     else {
       curwp->w_doto = regs.start[0];
       curwp->w_dotp = clp;
       curwp->w_flag |= WFMOVE;
       return (TRUE);
     }

   }

  return(FALSE);

}


/*
 * Read a pattern.
 * Stash it in the external variable "re_pat". The "pat" is
 * not updated if the user types in an empty line. If the user typed
 * an empty line, and there is no old pattern, it is an error.
 * Display the old pattern, in the style of Jeff Lomicka. There is
 * some do-it-yourself control expansion.
 */
re_readpattern(prompt) char *prompt; {
	register int s;
	char tpat[NPAT];
	char *message;

	if (re_pat[0] == '\0') s = ereply("%s: ", tpat, NPAT, prompt);
	else s = ereply("%s: (default %s) ", tpat, NPAT, prompt, re_pat);

	if (s == TRUE) {
	  /* New pattern given */
	  (VOID) strcpy(re_pat, tpat);
	  re_buff.allocated = 40;
	  re_buff.buffer = (char *) malloc (re_buff.allocated);
	  re_buff.fastmap = fastmap;
	  if (casefoldsearch)
	    re_buff.translate = upcase;
	  else
	    re_buff.translate = '\0';
	  message = re_compile_pattern (re_pat, strlen(re_pat), &re_buff);
	  if (message != '\0') {
	    ewprintf("Regex Error: %s", message);
	    re_pat[0] = '\0';
	    return(FALSE);
	  }
	  re_compile_fastmap (&re_buff);
	}
	else if (s==FALSE && re_pat[0]!='\0')
	  /* Just using old pattern */
	  s = TRUE;
	return (s);
}



/* Cause case to not matter in searches.  This is the default.	If
 * called with argument cause case to matter.
 */
setcasefold(f, n) {

  if (f & FFARG) {
    casefoldsearch = FALSE;
    ewprintf("Case-fold-search unset");
  }
  else {
    casefoldsearch = TRUE;
    ewprintf("Case-fold-search set");
  }

  /* Invalidate the regular expression pattern since I'm too lazy
   * to recompile it.
   */

  re_pat[0] = '\0';

  return(TRUE);

} /* end setcasefold */


/* Delete all lines after dot that contain a string matching regex
 */
delmatchlines(f, n) {
  int s;

  if ((s=re_readpattern("Flush lines (containing match for regexp)")) != TRUE)
    return (s);

  s = killmatches(TRUE);

  return(s);
}



/* Delete all lines after dot that don't contain a string matching regex
 */
delnonmatchlines(f, n) {
  int s;


  if ((s=re_readpattern("Keep lines (containing match for regexp)")) != TRUE)
    return (s);

  s = killmatches(FALSE);

  return(s);
}



/* This function does the work of deleting matching lines */
killmatches(cond)
   int cond;
{
  int s, i;
  int count = 0;
  LINE	*clp;

  clp = curwp->w_dotp;
  if (curwp->w_doto == llength(clp))
    /* Consider dot on next line */
    clp = lforw(clp);

  while (clp != (curbp->b_linep)) {

     /* see if line matches */
     i = re_search (&re_buff, ltext(clp), llength(clp), 0, llength(clp),
		    &regs);
     /* Delete line when appropriate */
     if ((cond == FALSE && i == -1) || (cond == TRUE && i != -1)) {
       curwp->w_doto = 0;
       curwp->w_dotp = clp;
       count++;
       s = ldelete(llength(clp)+1, KNONE);
       clp = curwp->w_dotp;
       curwp->w_flag |= WFMOVE;
       if (s == FALSE) return(FALSE);
     }
     else
       clp = lforw(clp);
   }

  ewprintf("%d line(s) deleted", count);
  if (count > 0) curwp->w_flag |= WFMOVE;

  return(TRUE);
}


petersfunc(f, n) {

  int s;
  LINE	*clp;
  char c;

  curwp->w_doto = 0;
  s = ldelete(llength(curwp->w_dotp)+1, KNONE);
  curwp->w_flag |= WFMOVE;
  return(s);

}


/* Count lines matching regex
 */
cntmatchlines(f, n) {
  int s;

  if ((s=re_readpattern("Count lines (matching regexp)")) != TRUE)
    return (s);

  s = countmatches(TRUE);

  return(s);
}



/* Count lines that fail to match regex
 */
cntnonmatchlines(f, n) {
  int s;


  if ((s=re_readpattern("Count lines (not matching regexp)")) != TRUE)
    return (s);

  s = countmatches(FALSE);

  return(s);
}



/* This function does the work of counting matching lines */
countmatches(cond)
   int cond;
{
  int s, i;
  int count = 0;
  LINE	*clp;

  clp = curwp->w_dotp;
  if (curwp->w_doto == llength(clp))
    /* Consider dot on next line */
    clp = lforw(clp);

  while (clp != (curbp->b_linep)) {

     /* see if line matches */
     i = re_search (&re_buff, ltext(clp), llength(clp), 0, llength(clp),
		    &regs);
     /*	 Count line when appropriate */
     if ((cond == FALSE && i == -1) || (cond == TRUE && i != -1)) count++;
     clp = lforw(clp);
   }

  if (cond)
     ewprintf("Number of lines matching: %d", count);
  else
     ewprintf("Number of lines not matching: %d", count);

  return(TRUE);
}
#endif
SHAR_EOF
cat << \SHAR_EOF > sys/bsd/README
Not much here; contributions welcome.

Two important things to note:

1) The editor supports the notion that "~" is your home directory.

2) Startup files are either ~/.mg, or (optionaly) a system default.
Only one is used.

SHAR_EOF
cat << \SHAR_EOF > sys/bsd/Makefile
# Makefile for Mg 2a

SYS	= bsd
LIBS	= -ltermcap
# CDEFS gets defines, and gets passed to lint. CFLAGS gets flags, and doesn't
# get passed to lint.
#
# (Common) compile-time options:
#
#	DO_METAKEY	-- if bit 7 is set for a key, treat like a META key
#	STARTUP		-- look for and handle initialization file
#	SYMBLINK	-- follow symbolic links the same as the BSD kernel
#	XKEYS		-- use termcap function key definitions. Warning -
#				XKEYS and bsmap mode do _not_ get along.
#	BACKUP		-- enable "make-backup-files"
#	PREFIXREGION	-- enable function "prefix-region"
#	REGEX		-- create regular expression functions
#
#CDEFS	=  -DDO_METAKEY
CDEFS	=  -DDO_METAKEY -DPREFIXREGION
CFLAGS	= -g $(CDEFS)

# Objects which only depend on the "standard" includes
OBJS	= basic.o dir.o dired.o file.o line.o match.o paragraph.o \
	  random.o region.o search.o version.o window.o word.o

# Those with unique requirements
IND	= buffer.o display.o echo.o extend.o help.o kbd.o keymap.o \
	  macro.o main.o modes.o regex.o re_search.o

# System dependent objects
OOBJS = cinfo.o spawn.o ttyio.o tty.o ttykbd.o

OBJ = $(OBJS) $(IND) $(OOBJS) fileio.o

OSRCS	= cinfo.c fileio.c spawn.c ttyio.c tty.c ttykbd.c
SRCS	= basic.c dir.c dired.c file.c line.c match.c paragraph.c \
	  random.c region.c search.c version.c window.c word.c \
	  buffer.c display.c echo.c extend.c help.c kbd.c keymap.c \
	  macro.c main.c modes.c regex.c re_search.c

OINCS =	ttydef.h sysdef.h chrdef.h
INCS =	def.h

mg:	$(OBJ)
	cc $(CFLAGS) -o mg $(OBJ) $(LIBS)

# strip mg once you're satisfied it'll run -- makes it much smaller
strip:
	strip mg

lint: $(SRCS) $(OSRCS) $(INCS) $(OINCS)
	lint -ahbz $(CDEFS) $(SRCS) $(OSRCS)

$(OBJ):		$(INCS) $(OINCS)


dir.r search.o:	$(INCS) $(OINCS)

regex.o re_search.o:	$(INCS) $(OINCS) regex.h

kbd.o:	$(INCS) $(OINCS) macro.h kbd.h key.h

macro.o main.o:	$(INCS) $(OINCS) macro.h

buffer.o display.o keymap.o help.o modes.o dired.o fileio.o: \
	$(INCS) $(OINCS) kbd.h

extend.o:	$(INCS) $(OINCS) kbd.h macro.h key.h

help.o:	$(INCS) $(OINCS) kbd.h key.h macro.h

echo.o:	$(INCS) $(OINCS) key.h macro.h

$(OOBJS):	$(INCS) $(OINCS)

sysdef.h:	sys/$(SYS)/sysdef.h	# Update links, if needed.
	rm -f sysdef.h
	ln sys/$(SYS)/sysdef.h .

ttydef.h:	sys/default/ttydef.h
	rm -f ttydef.h
	ln sys/default/ttydef.h .

chrdef.h:	sys/default/chrdef.h
	rm -f chrdef.h
	ln sys/default/chrdef.h .

fileio.c:	sys/$(SYS)/fileio.c
	rm -f fileio.c
	ln sys/$(SYS)/fileio.c .

spawn.c:	sys/$(SYS)/spawn.c
	rm -f spawn.c
	ln sys/$(SYS)/spawn.c .

tty.c:		sys/default/tty.c
	rm -f tty.c
	ln sys/default/tty.c .

ttyio.c:	sys/$(SYS)/ttyio.c
	rm -f ttyio.c
	ln sys/$(SYS)/ttyio.c .

ttykbd.c:	sys/default/ttykbd.c
	rm -f ttykbd.c
	ln sys/default/ttykbd.c .

cinfo.c:	sys/default/cinfo.c
	rm -f cinfo.c
	ln sys/default/cinfo.c .

port: $(SRCS) $(INCS)
	rm -f port
	tar cfb port 1 $?

clean:;	rm -f $(OBJ) $(OSRCS) $(OINCS)

SHAR_EOF
cat << \SHAR_EOF > sys/bsd/spawn.c
/*
 * Spawn. New version, which
 * interracts with the job control stuff
 * in the 4.X BSD C shell.
 * Last edit:  Wed Aug 27 11:16:07 PDT 1986
 * By:	       rtech!daveb, to use stop for ksh.
 */
#include	"def.h"

#include	<sgtty.h>
#include	<signal.h>
#include	<sys/wait.h>

char	*shellp = NULL;			/* Saved "SHELL" name.		*/

extern	struct	sgttyb	oldtty;		/* There really should be a	*/
extern	struct	sgttyb	newtty;		/* nicer way of doing this, so	*/
extern	struct	sgttyb	oldtchars;	/* spawn does not need to know	*/
extern	struct	sgttyb	newtchars;	/* about the insides of the	*/
extern	struct	sgttyb	oldltchars;	/* terminal I/O code.		*/
extern	struct	sgttyb	newltchars;

extern	char	*getenv();

/*
 * This code does a one of 2 different
 * things, depending on what version of the shell
 * you are using. If you are using the C shell, which
 * implies that you are using job control, then MicroEMACS
 * moves the cursor to a nice place and sends itself a
 * stop signal. If you are using the Bourne shell it runs
 * a subshell using fork/exec. Bound to "C-C", and used
 * as a subcommand by "C-Z".
 *
 * Daveb -- changed sense of test so that we only spawn if you
 *	    are explicitly using /bin/sh.  This makes it stop
 *	    work with the ksh.
 */
/*ARGSUSED*/
spawncli(f, n) {
	register int	pid, wpid, (*oqsig)(), (*oisig)(), omask;
	union wait	status;

	if (shellp == NULL) {
		shellp = getenv("SHELL");
		if (shellp == NULL)
			shellp = getenv("shell");
		if (shellp == NULL)
			shellp = "/bin/sh";	/* Safer.		*/
	}
	ttcolor(CTEXT);
	ttnowindow();
	if (strcmp(shellp, "/bin/csh") == 0) {
		if (epresf != FALSE) {
			ttmove(nrow-1, 0);
			tteeol();
			epresf = FALSE;
		}				/* Csh types a "\n"	*/
		ttmove(nrow-2, 0);		/* before "Stopped".	*/
	} else {
		ttmove(nrow-1, 0);
		if (epresf != FALSE) {
			tteeol();
			epresf = FALSE;
		}
	}
	if (ttcooked() == FALSE)
		return (FALSE);
	if (strcmp(shellp, "/bin/sh") != 0) {	/* C shell, ksh		*/
		omask = sigsetmask(0);
		(void) kill(0, SIGTSTP);
		(void) sigsetmask(omask);
	} else {				/* Bourne shell.	*/
		oqsig = signal(SIGQUIT, SIG_IGN);
		oisig = signal(SIGINT,	SIG_IGN);
		if ((pid=fork()) < 0) {
			(void) signal(SIGQUIT, oqsig);
			(void) signal(SIGINT,  oisig);
			ewprintf("Failed to create process");
			return (FALSE);
		}
		if (pid == 0) {
			execl(shellp, "sh", "-i", NULL);
			_exit(0);		/* Should do better!	*/
		}
		while ((wpid=wait(&status))>=0 && wpid!=pid)
			;
		(void) signal(SIGQUIT, oqsig);
		(void) signal(SIGINT,  oisig);
	}
	sgarbf = TRUE;				/* Force repaint.	*/
	return ttraw();
}
SHAR_EOF
cat << \SHAR_EOF > sys/bsd/sysdef.h
/*
 *		BSD unix based systems (sunos, ultrix)
 */
#include <stdio.h>

#define	KBLOCK	8192			/* Kill grow.			*/
#define	GOOD	0			/* Good exit status.		*/
#define	SYMBLINK	1		/* Handle symbolic links	*/
#define	MAXPATH	256			/* Maximum length of path for chdir */

typedef int	RSIZE;			/* Type for file/region sizes	*/
typedef short	KCHAR;			/* Type for internal keystrokes	*/

/*
 * Macros used by the buffer name making code.
 * Start at the end of the file name, scan to the left
 * until BDC1 (or BDC2, if defined) is reached. The buffer
 * name starts just to the right of that location, and
 * stops at end of string (or at the next BDC3 character,
 * if defined). BDC2 and BDC3 are mainly for VMS.
 */
#define	BDC1	'/'			/* Buffer names.		*/

#define MALLOCROUND(m)	(m+=7,m&=~7)	/* round up to 8 byte boundry	*/

#define	fncmp		strcmp		/* file name comparison		*/
#define	unlinkdir(fn)	rmdir(fn)	/* unlink directory		*/
char *getenv();
#define	gettermtype()	getenv("TERM")	/* determine terminal type	*/
SHAR_EOF
cat << \SHAR_EOF > sys/bsd/ttyio.c
/*
 *		Ultrix-32 and Unix terminal I/O.
 * The functions in this file
 * negotiate with the operating system for
 * keyboard characters, and write characters to
 * the display in a barely buffered fashion.
 */
#include	"def.h"

#include	<sgtty.h>

#define NOBUF	512			/* Output buffer size.		*/

char	obuf[NOBUF];			/* Output buffer.		*/
int	nobuf;
struct	sgttyb	oldtty;			/* V6/V7 stty data.		*/
struct	sgttyb	newtty;
struct	tchars	oldtchars;		/* V7 editing.			*/
struct	tchars	newtchars;
struct	ltchars oldltchars;		/* 4.2 BSD editing.		*/
struct	ltchars newltchars;
#ifdef	TIOCGWINSZ
struct	winsize winsize;		/* 4.3 BSD window sizing	*/
#endif
int	nrow;				/* Terminal size, rows.		*/
int	ncol;				/* Terminal size, columns.	*/

/*
 * This function gets called once, to set up
 * the terminal channel. On Ultrix is's tricky, since
 * we want flow control, but we don't want any characters
 * stolen to send signals. Use CBREAK mode, and set all
 * characters but start and stop to 0xFF.
 */
ttopen() {
	register char *tv_stype;
	char *getenv(), *tgetstr(), tcbuf[1024], err_str[72];
	char *sprintf();

/* do this the REAL way */
	if ((tv_stype = getenv("TERM")) == NULL)
	{
		puts("Environment variable TERM not defined!");
		exit(1);
	}

	if((tgetent(tcbuf, tv_stype)) != 1)
	{
		(void) sprintf(err_str, "Unknown terminal type %s!", tv_stype);
		puts(err_str);
		exit(1);
	}
	if (ttraw() == FALSE)
		panic("aborting due to terminal initialize failure");
}

/*
 * This function sets the terminal to RAW mode, as defined for the current
 * shell.  This is called both by ttopen() above and by spawncli() to
 * get the current terminal settings and then change them to what
 * mg expects.	Thus, stty changes done while spawncli() is in effect
 * will be reflected in mg.
 */
ttraw() {
	extern short ospeed;

	if (ioctl(0, TIOCGETP, (char *) &oldtty) < 0) {
		ewprintf("ttopen can't get sgtty");
		return(FALSE);
	}
	newtty.sg_ospeed = ospeed = oldtty.sg_ospeed;
	newtty.sg_ispeed = oldtty.sg_ispeed;
	newtty.sg_erase	 = oldtty.sg_erase;
	newtty.sg_kill	 = oldtty.sg_kill;
	newtty.sg_flags	 = oldtty.sg_flags;
	newtty.sg_flags &= ~(ECHO|CRMOD);	/* Kill echo, CR=>NL.	*/
#ifdef FLOWCONTROL
	newtty.sg_flags |= CBREAK;		/* Half-cooked mode.	*/
#else
	newtty.sg_flags |= RAW|ANYP;		/* raw mode for 8 bit path.*/
#endif
	if (ioctl(0, TIOCSETP, (char *) &newtty) < 0) {
		ewprintf("ttopen can't set sgtty");
		return(FALSE);
	}
	if (ioctl(0, TIOCGETC, (char *) &oldtchars) < 0) {
		ewprintf("ttopen can't get chars");
		return(FALSE);
	}
	newtchars.t_intrc  = 0xFF;		/* Interrupt.		*/
	newtchars.t_quitc  = 0xFF;		/* Quit.		*/
#if FLOWCONTROL
	newtchars.t_startc = 0x11;		/* ^Q, for terminal.	*/
	newtchars.t_stopc  = 0x13;		/* ^S, for terminal.	*/
#else
	newtchars.t_startc = 0xFF;		/* ^Q, for terminal.	*/
	newtchars.t_stopc  = 0xFF;		/* ^S, for terminal.	*/
#endif
	newtchars.t_eofc   = 0xFF;
	newtchars.t_brkc   = 0xFF;
	if (ioctl(0, TIOCSETC, (char *) &newtchars) < 0) {
		ewprintf("ttraw can't set chars");
		return(FALSE);
	}
	if (ioctl(0, TIOCGLTC, (char *) &oldltchars) < 0) {
		panic("ttraw can't get ltchars");
		return(FALSE);
	}
	newltchars.t_suspc  = 0xFF;		/* Suspend #1.		*/
	newltchars.t_dsuspc = 0xFF;		/* Suspend #2.		*/
	newltchars.t_rprntc = 0xFF;
	newltchars.t_flushc = 0xFF;		/* Output flush.	*/
	newltchars.t_werasc = 0xFF;
	newltchars.t_lnextc = 0xFF;		/* Literal next.	*/
	if (ioctl(0, TIOCSLTC, (char *) &newltchars) < 0) {
		ewprintf("ttraw can't set ltchars");
		return(FALSE);
	}
	setttysize() ;
	return(TRUE);
}

/*
 * This function gets called just
 * before we go back home to the shell. Put all of
 * the terminal parameters back.
 *    Under UN*X this just calls ttcooked(), but the ttclose() hook is in
 * because vttidy() in display.c expects it for portability reasons.
 */
ttclose() {
	if (ttcooked() == FALSE)
		panic("");		/* ttcooked() already printf'd */
}

/*
 * This function restores all terminal settings to their default values,
 * in anticipation of exiting or suspending the editor.
 */

ttcooked() {
	ttflush();
	if (ioctl(0, TIOCSLTC, (char *) &oldltchars) < 0) {
		ewprintf("ttclose can't set ltchars");
		return(FALSE);
	}
	if (ioctl(0, TIOCSETC, (char *) &oldtchars) < 0) {
		ewprintf("ttclose can't set chars");
		return(FALSE);
	}
	if (ioctl(0, TIOCSETP, (char *) &oldtty) < 0) {
		ewprintf("ttclose can't set sgtty");
		return(FALSE);
	}
	return(TRUE);
}

/*
 * Write character to the display.
 * Characters are buffered up, to make things
 * a little bit more efficient.
 */
ttputc(c)
int c;
{
	if (nobuf >= NOBUF)
		ttflush();
	obuf[nobuf++] = c;
}

/*
 * Flush output.
 */
ttflush() {
	if (nobuf != 0) {
		if (write(1, obuf, nobuf) != nobuf)
			panic("ttflush write failed");
		nobuf = 0;
	}
}

/*
 * Read character from terminal.
 * All 8 bits are returned, so that you can use
 * a multi-national terminal.
 */
ttgetc() {
	char	buf[1];

	while (read(0, &buf[0], 1) != 1)
		;
	return (buf[0] & 0xFF);
}
/*
 * set the tty size. Functionized for 43BSD.
 */
setttysize() {

#ifdef	TIOCGWINSZ
	if (ioctl(0, TIOCGWINSZ, (char *) &winsize) == 0) {
		nrow = winsize . ws_row;
		ncol = winsize . ws_col;
	} else nrow = 0;
	if(nrow<=0 || ncol<=0)
#endif
	if ((nrow=tgetnum ("li")) <= 0
	|| (ncol=tgetnum ("co")) <= 0) {
		nrow = 24;
		ncol = 80;
	}
	if (nrow > NROW)			/* Don't crash if the	*/
		nrow = NROW;			/* termcap entry is	*/
	if (ncol > NCOL)			/* too big.		*/
		ncol = NCOL;
}

/*
 * typeahead returns TRUE if there are characters available to be read
 * in.
 */
typeahead() {
	int	x;

	return((ioctl(0, FIONREAD, (char *) &x) < 0) ? 0 : x);
}

/*
 * panic - just exit, as quickly as we can.
 */
panic(s) char *s; {
	(void) fputs("panic: ", stderr);
	(void) fputs(s, stderr);
	(void) fputc('\n', stderr);
	(void) fflush(stderr);
	abort();		/* To leave a core image. */
}
#ifndef NO_DPROMPT
#include <sys/time.h>
/*
 * A program to return TRUE if we wait for 2 seconds without anything
 * happening, else return FALSE.  Cribbed from mod.sources xmodem.
 */
int ttwait() {
	int readfd;
	struct timeval tmout;

	tmout.tv_sec = 2;
	tmout.tv_usec = 0;

	readfd = 1;

	if ((select(1, &readfd, (int *)0, (int *)0, &tmout)) == 0)
		return(TRUE);
	return(FALSE);
}
#endif
SHAR_EOF
cat << \SHAR_EOF > sys/sysv/Makefile
# Makefile for MicroEMACS.
# Is there a better way to do the rebuilds, other than using
# the links?

SYS	= sysv
LIBS	= -lcurses
# CDEFS gets defines, and gets passed to lint. CFLAGS gets flags, and doesn't
# get passed to lint.
#
# (Common) compile-time options:
#
#	DO_METAKEY	-- if bit 7 is set for a key, treat like a META key
#	STARTUP		-- look for and handle initialization file
#	XKEYS		-- use termcap function key definitions. Warning -
#				XKEYS and bsmap mode do _not_ get along.
#	BACKUP		-- enable "make-backup-files"
#	PREFIXREGION	-- enable function "prefix-region"
#	REGEX		-- create regular expression functions
#
#CDEFS	=  -DDO_METAKEY
CDEFS	=  -DDO_METAKEY -DPREFIXREGION
CFLAGS	= -g $(CDEFS)

# Objects which only depend on the "standard" includes
OBJS	= basic.o dir.o dired.o file.o line.o match.o paragraph.o \
	  random.o region.o search.o version.o window.o word.o

# Those with unique requirements
IND	= buffer.o display.o echo.o extend.o help.o kbd.o keymap.o \
	  macro.o main.o modes.o regex.o re_search.o

# System dependent objects
OOBJS = cinfo.o spawn.o ttyio.o tty.o ttykbd.o

OBJ = $(OBJS) $(IND) $(OOBJS) fileio.o

OSRCS	= cinfo.c fileio.c spawn.c ttyio.c tty.c ttykbd.c
SRCS	= basic.c dir.c dired.c file.c line.c match.c paragraph.c \
	  random.c region.c search.c version.c window.c word.c \
	  buffer.c display.c echo.c extend.c help.c kbd.c keymap.c \
	  macro.c main.c modes.c regex.c re_search.c

OINCS =	ttydef.h sysdef.h chrdef.h
INCS =	def.h

mg:	$(OBJ)
	cc $(CFLAGS) -o mg $(OBJ) $(LIBS)

# strip mg once you're satisfied it'll run -- makes it much smaller
strip:
	strip mg

lint: $(SRCS) $(OSRCS) $(INCS) $(OINCS)
	lint -ahbz $(CDEFS) $(SRCS) $(OSRCS)

$(OBJ):		$(INCS) $(OINCS)


dir.r search.o:	$(INCS) $(OINCS)

regex.o re_search.o:	$(INCS) $(OINCS) regex.h

kbd.o:	$(INCS) $(OINCS) macro.h kbd.h key.h

macro.o main.o:	$(INCS) $(OINCS) macro.h

buffer.o display.o keymap.o help.o modes.o dired.o fileio.o: \
	$(INCS) $(OINCS) kbd.h

extend.o:	$(INCS) $(OINCS) kbd.h macro.h key.h

help.o:	$(INCS) $(OINCS) kbd.h key.h macro.h

echo.o:	$(INCS) $(OINCS) key.h macro.h

$(OOBJS):	$(INCS) $(OINCS)

sysdef.h:	sys/$(SYS)/sysdef.h	# Update links, if needed.
	rm -f sysdef.h
	ln sys/$(SYS)/sysdef.h .

ttydef.h:	sys/default/ttydef.h
	rm -f ttydef.h
	ln sys/default/ttydef.h .

chrdef.h:	sys/default/chrdef.h
	rm -f chrdef.h
	ln sys/default/chrdef.h .

fileio.c:	sys/$(SYS)/fileio.c
	rm -f fileio.c
	ln sys/$(SYS)/fileio.c .

spawn.c:	sys/$(SYS)/spawn.c
	rm -f spawn.c
	ln sys/$(SYS)/spawn.c .

tty.c:		sys/default/tty.c
	rm -f tty.c
	ln sys/default/tty.c .

ttyio.c:	sys/$(SYS)/ttyio.c
	rm -f ttyio.c
	ln sys/$(SYS)/ttyio.c .

ttykbd.c:	sys/default/ttykbd.c
	rm -f ttykbd.c
	ln sys/default/ttykbd.c .

cinfo.c:	sys/default/cinfo.c
	rm -f cinfo.c
	ln sys/default/cinfo.c .

port: $(SRCS) $(INCS)
	rm -f port
	tar cfb port 1 $?

clean:;	rm -f $(OBJ) $(OSRCS) $(OINCS)
SHAR_EOF
cat << \SHAR_EOF > sys/sysv/fileio.c
/*
 *		sys V fileio.c
 */
#include	"def.h"

static	FILE	*ffp;
extern	char	*getenv();
char	*adjustname();

#include <sys/types.h>

/*
 * Open a file for reading.
 */
ffropen(fn) char *fn; {
	if ((ffp=fopen(fn, "r")) == NULL)
		return (FIOFNF);
	return (FIOSUC);
}

/*
 * Open a file for writing.
 * Return TRUE if all is well, and
 * FALSE on error (cannot create).
 */
ffwopen(fn) char *fn; {
	if ((ffp=fopen(fn, "w")) == NULL) {
		ewprintf("Cannot open file for writing");
		return (FIOERR);
	}
	return (FIOSUC);
}

/*
 * Close a file.
 * Should look at the status.
 */
ffclose() {
	(VOID) fclose(ffp);
	return (FIOSUC);
}

/*
 * Write a buffer to the already
 * opened file. bp points to the
 * buffer. Return the status.
 * Check only at the newline and
 * end of buffer.
 */
ffputbuf(bp)
BUFFER *bp;
{
    register char *cp;
    register char *cpend;
    register LINE *lp;
    register LINE *lpend;

    lpend = bp->b_linep;
    lp = lforw(lpend);
    do {
	cp = &ltext(lp)[0];		/* begining of line	*/
	cpend = &cp[llength(lp)];	/* end of line		*/
	while(cp != cpend) {
	    putc(*cp, ffp);
	    cp++;	/* putc may evalualte arguments more than once */
	}
	lp = lforw(lp);
	if(lp == lpend) break;		/* no implied newline on last line */
	putc('\n', ffp);
    } while(!ferror(ffp));
    if(ferror(ffp)) {
	ewprintf("Write I/O error");
	return FIOERR;
    }
    return FIOSUC;
}

/*
 * Read a line from a file, and store the bytes
 * in the supplied buffer. Stop on end of file or end of
 * line.  When FIOEOF is returned, there is a valid line
 * of data without the normally implied \n.
 */
ffgetline(buf, nbuf, nbytes)
register char	*buf;
register int	nbuf;
register int	*nbytes;
{
	register int	c;
	register int	i;

	i = 0;
	while((c = getc(ffp))!=EOF && c!='\n') {
		buf[i++] = c;
		if (i >= nbuf) return FIOLONG;
	}
	if (c == EOF  && ferror(ffp) != FALSE) {
		ewprintf("File read error");
		return FIOERR;
	}
	*nbytes = i;
	return c==EOF ? FIOEOF : FIOSUC;
}

#ifndef NO_BACKUP
/*
 * Rename the file "fname" into a backup
 * copy. On Unix the backup has the same name as the
 * original file, with a "~" on the end; this seems to
 * be newest of the new-speak. The error handling is
 * all in "file.c". The "unlink" is perhaps not the
 * right thing here; I don't care that much as
 * I don't enable backups myself.
 */
fbackupfile(fn) char *fn; {
	register char	*nname;
	char		*malloc();

	if ((nname=malloc((unsigned)(strlen(fn)+1+1))) == NULL) {
		ewprintf("Can't get %d bytes", strlen(fn) + 1);
		return (ABORT);
	}
	(void) strcpy(nname, fn);
	(void) strcat(nname, "~");
	(void) unlink(nname);			/* Ignore errors.	*/
	if (rename(fn, nname) < 0) {
		free(nname);
		return (FALSE);
	}
	free(nname);
	return (TRUE);
}
#endif

/*
 * The string "fn" is a file name.
 * Perform any required appending of directory name or case adjustments.
 * If NO_DIR is not defined, the same file should be refered to even if the
 * working directory changes.
 */
#ifdef SYMBLINK
#include <sys/types.h>
#include <sys/stat.h>
#ifndef MAXLINK
#define MAXLINK 8		/* maximum symbolic links to follow */
#endif
#endif
#include <pwd.h>
#ifndef NO_DIR
extern char *wdir;
#endif

char *adjustname(fn)
register char *fn;
{
    register char *cp;
    static char fnb[NFILEN];
    struct passwd *pwent;
#ifdef	SYMBLINK
    struct stat statbuf;
    int i, j;
    char linkbuf[NFILEN];
#endif

    switch(*fn) {
    	case '/':
	    cp = fnb;
	    *cp++ = *fn++;
	    break;
	case '~':
	    fn++;
	    if(*fn == '/' || *fn == '\0') {
		(VOID) strcpy(fnb, getenv("HOME"));
		cp = fnb + strlen(fnb);
	    	if(*fn) fn++;
		break;
	    } else {
		cp = fnb;
		while(*fn && *fn != '/') *cp++ = *fn++;
		*cp = '\0';
		if((pwent = getpwnam(fnb)) != NULL) {
		    (VOID) strcpy(fnb, pwent->pw_dir);
		    cp = fnb + strlen(fnb);
		    break;
		} else {
		    fn -= strlen(fnb) + 1;
		    /* can't find ~user, continue to default case */
		}
	    }
	default:
#ifndef	NODIR
	    strcpy(fnb, wdir);
	    cp = fnb + strlen(fnb);
	    break;
#else
	    return fn;				/* punt */
#endif
    }
    if(cp != fnb && cp[-1] != '/') *cp++ = '/';
    while(*fn) {
    	switch(*fn) {
	    case '.':
		switch(fn[1]) {
	            case '\0':
		    	*--cp = '\0';
		    	return fnb;
	    	    case '/':
	    	    	fn += 2;
		    	continue;
		    case '.':
		    	if(fn[2]=='/' || fn[2] == '\0') {
#ifdef SYMBLINK
			    cp[-1] = '\0';
			    for(j = MAXLINK; j-- && 
			    	    lstat(fnb, &statbuf) != -1 && 
			    	    (statbuf.st_mode&S_IFMT) == S_IFLNK &&
			    	    (i = readlink(fnb, linkbuf, sizeof linkbuf))
				    != -1 ;) {
				if(linkbuf[0] != '/') {
				    --cp;
				    while(cp > fnb && *--cp != '/') {}
				    ++cp;
				    (VOID) strncpy(cp, linkbuf, i);
				    cp += i;
				} else {
				    (VOID) strncpy(fnb, linkbuf, i);
				    cp = fnb + i;
				}
				if(cp[-1]!='/') *cp++ = '\0';
				else cp[-1] = '\0';
			    }
			    cp[-1] = '/';
#endif
			    --cp;
			    while(cp > fnb && *--cp != '/') {}
			    ++cp;
			    if(fn[2]=='\0') {
			        *--cp = '\0';
			        return fnb;
			    }
		            fn += 3;
		            continue;
		        }
		        break;
		    default:
		    	break;
	        }
		break;
	    case '/':
	    	fn++;
	    	continue;
	    default:
	    	break;
	}
	while(*fn && (*cp++ = *fn++) != '/') {}
    }
    if(cp[-1]=='/') --cp;
    *cp = '\0';
    return fnb;
}

#ifndef NO_STARTUP
#include <sys/file.h>
#define F_OK 04			/* for stupid Sys V		*/

/*
 * Find a startup file for the user and return its name. As a service
 * to other pieces of code that may want to find a startup file (like
 * the terminal driver in particular), accepts a suffix to be appended
 * to the startup file name.
 */
char *
startupfile(suffix)
char *suffix;
{
	register char	*file;
	static char	home[NFILEN];
	char		*getenv();

	if ((file = getenv("HOME")) == NULL) goto notfound;
	if (strlen(file)+7 >= NFILEN - 1) goto notfound;
	(VOID) strcpy(home, file);
	(VOID) strcat(home, "/.mg");
	if (suffix != NULL) {
		(VOID) strcat(home, "-");
		(VOID) strcat(home, suffix);
	}
	if (access(home, F_OK) == 0) return home;

notfound:
#ifdef	STARTUPFILE
	file = STARTUPFILE;
	if (suffix != NULL) {
		(VOID) strcpy(home, file);
		(VOID) strcat(home, "-");
		(VOID) strcat(home, suffix);
		file = home;
	}
	if (access(file, F_OK ) == 0) return file;
#endif

	return NULL;
}
#endif

#ifndef NO_DIRED
#include "kbd.h"

copy(frname, toname)
char *frname, *toname;
{
    int pid;
    int status;

    if(pid = fork()) {
	if(pid == -1)	return	-1;
	execl("/bin/cp", "cp", frname, toname, (char *)NULL);
	_exit(1);	/* shouldn't happen */
    }
    while(wait(&status) != pid)
	;
    return status == 0;
}

BUFFER *dired_(dirname)
char *dirname;
{
    register BUFFER *bp;
    char line[256];
    BUFFER *findbuffer();
    FILE *dirpipe;
    FILE *popen();
    char *strncpy();

    if((dirname = adjustname(dirname)) == NULL) {
	ewprintf("Bad directory name");
	return NULL;
    }
    if((bp = findbuffer(dirname)) == NULL) {
	ewprintf("Could not create buffer");
	return NULL;
    }
    if(bclear(bp) != TRUE) return FALSE;
    (VOID) strcpy(line, "ls -al ");
    (VOID) strcpy(&line[7], dirname);
    if((dirpipe = popen(line, "r")) == NULL) {
	ewprintf("Problem opening pipe to ls");
	return NULL;
    }
    line[0] = line[1] = ' ';
    while(fgets(&line[2], 254, dirpipe) != NULL) {
	line[strlen(line) - 1] = '\0';		/* remove ^J	*/
	(VOID) addline(bp, line);
    }
    if(pclose(dirpipe) == -1) {
	ewprintf("Problem closing pipe to ls");
	return NULL;
    }
    bp->b_dotp = lforw(bp->b_linep);		/* go to first line */
    (VOID) strncpy(bp->b_fname, dirname, NFILEN);
    if((bp->b_modes[0] = name_mode("dired")) == NULL) {
	bp->b_modes[0] = &map_table[0];
	ewprintf("Could not find mode dired");
	return NULL;
    }
    bp->b_nmodes = 0;
    return bp;
}

d_makename(lp, fn)
register LINE *lp;
register char *fn;
{
    register char *cp;

    if(llength(lp) <= 56) return ABORT;
    (VOID) strcpy(fn, curbp->b_fname);
    cp = fn + strlen(fn);
    bcopy(&lp->l_text[56], cp, llength(lp) - 56);
    cp[llength(lp) - 56] = '\0';
    return lgetc(lp, 2) == 'd';
}

/*
 * I, a System V novice, could only figure out how to do unlinkdir()
 * and rename() as exec's of the appropriate functions.  So sue me.
 * --Stephen Walton, December 1987
 */

unlinkdir(f)
char *f;
{
	int status, pid, wpid;

	if ((pid = fork()) == 0)
		execl("/bin/rmdir", "rmdir", f, (char *)NULL);
	else if (pid > 0)
		while ((wpid = wait(&status)) && wpid != pid)
			;
	else
		return FALSE;
	return status == 0;
}

rename(f1, f2)
char *f1, *f2;
{

	int status, pid, wpid;

	if ((pid = fork()) == 0)
		execl("/bin/mv", "mv", f1, f2, (char *)NULL);
	else if (pid > 0)
		while ((wpid = wait(&status)) && wpid != pid)
			;
	else
		return FALSE;
	return status == 0;
}
#endif NO_DIRED
SHAR_EOF
cat << \SHAR_EOF > sys/sysv/spawn.c
/*
 * Name:	MicroGnuEmacs
 *		Spawn CLI for System V.
 *
 * Spawn for System V.
 */
#include	"def.h"

#include	<signal.h>

char	*shellp	= NULL;			/* Saved "SHELL" program.	*/
char	*shname = NULL;			/* Saved shell name		*/

extern	char	*getenv();

/*
 * On System V, we no gots job control, so always run
 * a subshell using fork/exec. Bound to "C-C", and used
 * as a subcommand by "C-Z". (daveb)
 *
 * Returns 0 if the shell executed OK, something else if
 * we couldn't start shell or it exited badly.
 */
spawncli(f, n)
{
	extern char	*strrchr();
	register int	pid;
	register int	wpid;
	register int	(*oqsig)();
	register int	(*oisig)();
	int		status;
	int		errp = FALSE;

	if (shellp == NULL) {
		shellp = getenv("SHELL");
		if (shellp == NULL)
			shellp = getenv("shell");
		if (shellp == NULL)
			shellp = "/bin/sh";	/* Safer.		*/
		shname = strrchr( shellp, '/' ); 
		shname = shname ? shname++ : shellp;
		
	}
	ttcolor(CTEXT);
	ttnowindow();
	ttmove(nrow-1, 0);
	if (epresf != FALSE) {
		tteeol();
		epresf = FALSE;
	}
	ttclose();
	sgarbf = TRUE;				/* Force repaint.	*/
	oqsig = signal(SIGQUIT, SIG_IGN);
	oisig = signal(SIGINT,  SIG_IGN);
	if ((pid=fork()) == 0) {
		(void) signal(SIGINT, oisig);
		(void) signal(SIGQUIT, oqsig);
		execlp(shellp, shname, "-i", (char *)NULL);
		_exit(1);			/* Should do better!	*/
	}
	else if (pid > 0) {
		while ((wpid=wait(&status))>=0 && wpid!=pid)
			;
	}
	else errp = TRUE;

	signal(SIGINT,  oisig);
	signal(SIGQUIT, oqsig);
	ttopen();
	if(errp)
		ewprintf("Failed to create process");

	return ( errp | status );
}
SHAR_EOF
cat << \SHAR_EOF > sys/sysv/sysdef.h
/*
 *		System V system header file
 */
#include <stdio.h>

#define	KBLOCK	8192			/* Kill grow.			*/
#define	GOOD	0			/* Good exit status.		*/
#define	MAXPATH	256			/* Maximum length of path for chdir */

typedef long	RSIZE;			/* Type for file/region sizes	*/
typedef short	KCHAR;			/* Type for internal keystrokes	*/

/*
 * Macros used by the buffer name making code.
 * Start at the end of the file name, scan to the left
 * until BDC1 (or BDC2, if defined) is reached. The buffer
 * name starts just to the right of that location, and
 * stops at end of string (or at the next BDC3 character,
 * if defined). BDC2 and BDC3 are mainly for VMS.
 */
#define	BDC1	'/'			/* Buffer names.		*/

#define MALLOCROUND(m)	(m+=7,m&=~7)	/* round up to 8 byte boundry	*/

#define	fncmp		strcmp		/* file name comparison		*/
#define bcopy(s,d,n)	memcpy(d,s,n)	/* memory-to-memory copy	*/
char *getenv();
#define	gettermtype()	getenv("TERM")	/* determine terminal type	*/
char *getcwd();
#define getwd(cwd)	getcwd(cwd,NFILEN) /* get current working dir	*/
SHAR_EOF
cat << \SHAR_EOF > sys/sysv/ttyio.c
/*
 * Name:	MicroEMACS
 *		System V terminal I/O.
 * Version:	0
 * Last edit:	Tue Aug 26 23:57:57 PDT 1986
 * By:		gonzo!daveb
 *		{sun, amdahl, mtxinu}!rtech!gonzo!daveb
 *
 * The functions in this file
 * negotiate with the operating system for
 * keyboard characters, and write characters to
 * the display in a barely buffered fashion.
 *
 * This version goes along with tty/termcap/tty.c.
 * Terminal size is determined there, rather than here, and
 * this does not open the termcap file
 */
#include	"def.h"

#include	<sys/types.h>
#include	<fcntl.h>
#include	<termio.h>

#define	NOBUF	512			/* Output buffer size.		*/

char	obuf[NOBUF];			/* Output buffer.		*/
int	nobuf;				/* buffer count			*/

static struct termio	ot;		/* entry state of the terminal	*/
static struct termio	nt;		/* editor's terminal state	*/

static int ttyactivep = FALSE;		/* terminal in editor mode?	*/
static int ttysavedp = FALSE;		/* terminal state saved?	*/

int	nrow;				/* Terminal size, rows.		*/
int	ncol;				/* Terminal size, columns.	*/

/* These are used to implement typeahead on System V */

int kbdflgs;			/* saved keyboard fd flags	*/
int kbdpoll;			/* in O_NDELAY mode			*/
int kbdqp;			/* there is a char in kbdq	*/
char kbdq;			/* char we've already read	*/

/*
 * This function gets called once, to set up
 * the terminal channel.  This version turns off flow
 * control.  This may be wrong for your system, but no
 * good solution has really been found (daveb).
 */
ttopen()
{
	register char	*cp;
	extern char	*getenv();

	if (ttyactivep)
		return;

	if( !ttysavedp )
	{
		if (ioctl(0, TCGETA, &ot) < 0)
			abort();
		nt = ot;		/* save entry state		*/
		nt.c_cc[VMIN] = 1;	/* one character read is OK	*/
		nt.c_cc[VTIME] = 0;	/* Never time out.		*/
		nt.c_iflag |= IGNBRK;
		nt.c_iflag &= ~( ICRNL | INLCR | ISTRIP | IXON | IXOFF );
		nt.c_oflag &= ~OPOST;
		nt.c_cflag |= CS8;	/* allow 8th bit on input	*/
		nt.c_cflag &= ~PARENB;	/* Don't check parity		*/
		nt.c_lflag &= ~( ECHO | ICANON | ISIG );

		kbdpoll = (((kbdflgs = fcntl(0, F_GETFL, 0)) & O_NDELAY) != 0);
	
		ttysavedp = TRUE;
	}
	
	if (ioctl(0, TCSETAF, &nt) < 0)
		abort();

	/* This really belongs in tty/termcap... */

	if ((cp=getenv("TERMCAP")) == NULL
	|| (nrow=getvalue(cp, "li")) <= 0
	|| (ncol=getvalue(cp, "co")) <= 0) {
		nrow = 24;
		ncol = 80;
	}
	if (nrow > NROW)			/* Don't crash if the	*/
		nrow = NROW;			/* termcap entry is	*/
	if (ncol > NCOL)			/* too big.		*/
		ncol = NCOL;

	ttyactivep = TRUE;
}

/*
 * This routine scans a string, which is
 * actually the return value of a getenv call for the TERMCAP
 * variable, looking for numeric parameter "name". Return the value
 * if found. Return -1 if not there. Assume that "name" is 2
 * characters long. This limited use of the TERMCAP lets us find
 * out the size of a window on the X display.
 */
getvalue(cp, name)
register char	*cp;
register char	*name;
{
	for (;;) {
		while (*cp!=0 && *cp!=':')
			++cp;
		if (*cp++ == 0)			/* Not found.		*/
			return (-1);
		if (cp[0]==name[0] && cp[1]==name[1] && cp[2]=='#')
			return (atoi(cp+3));	/* Stops on ":".	*/
	}
}

/*
 * This function gets called just
 * before we go back home to the shell. Put all of
 * the terminal parameters back.
 */
ttclose()
{
	if(!ttysavedp || !ttyactivep)
		return;
	ttflush();
	if (ioctl(0, TCSETAF, &ot) < 0 || fcntl( 0, F_SETFL, kbdflgs ) < 0)
		abort();
	ttyactivep = FALSE;
}

/*
 * Write character to the display.
 * Characters are buffered up, to make things
 * a little bit more efficient.
 */
ttputc(c)
{
	if (nobuf >= NOBUF)
		ttflush();
	obuf[nobuf++] = c;
}

/*
 * Flush output.
 */
ttflush()
{
	if (nobuf != 0) {
		write(1, obuf, nobuf);
		nobuf = 0;
	}
}

/*
 * Read character from terminal.
 * All 8 bits are returned, so that you can use
 * a multi-national terminal.
 *
 * If keyboard 'queue' already has typeahead from a typeahead() call,
 * just return it.  Otherwise, make sure we are in blocking i/o mode
 * and read a character.
 */
ttgetc()
{
	if( kbdqp )
		kbdqp = FALSE;
	else
	{
		if( kbdpoll && fcntl( 0, F_SETFL, kbdflgs ) < 0 )
			abort();
		kbdpoll = FALSE;
		while (read(0, &kbdq, 1) != 1)
			;
	}
	return ( kbdq & 0xff );
}

/*
 * Return non-FALSE if typeahead is pending.
 *
 * If already got unread typeahead, do nothing.
 * Otherwise, set keyboard to O_NDELAY if not already, and try
 * a one character read.
 */
typeahead()
{
	if( !kbdqp )
	{
		if( !kbdpoll && fcntl( 0, F_SETFL, kbdflgs | O_NDELAY ) < 0 )
			abort();
		kbdpoll = TRUE;
		kbdqp = (1 == read( 0, &kbdq, 1 ));
	}
	return ( kbdqp );
}


/*
 * panic:  print error and die, leaving core file.
 * Don't know why this is needed (daveb).
 */
panic(s)
char *s;
{
	fprintf(stderr, "%s\r\n", s);
	abort();
}


/*
** This should check the size of the window, and reset if needed.
*/

setttysize()
{
#ifdef	TIOCGWINSZ
	if (ioctl(0, TIOCGWINSZ, (char *) &winsize) == 0) {
		nrow = winsize . ws_row;
		ncol = winsize . ws_col;
	} else
#endif
	if ((nrow=tgetnum ("li")) <= 0
	|| (ncol=tgetnum ("co")) <= 0) {
		nrow = 24;
		ncol = 80;
	}
	if (nrow > NROW)			/* Don't crash if the	*/
		nrow = NROW;			/* termcap entry is	*/
	if (ncol > NCOL)			/* too big.		*/
		ncol = NCOL;
}

#ifndef NO_DPROMPT
#include <signal.h>
#include <setjmp.h>

static jmp_buf tohere;

static alrm()
{
	longjmp(tohere, -1);
}

/*
 * Return TRUE if we wait without doing anything, else return FALSE.
 */

ttwait()
{
	int alrm();

	if (kbdqp)
		return FALSE;		/* already pending input	*/
	if (setjmp(tohere))
		return TRUE;		/* timeout on read if here	*/
	signal(SIGALRM, alrm); alarm(2);
	kbdqp = (1 == read(0, &kbdq, 1));
	alarm(0);
	return FALSE;			/* successful read if here	*/
}
#endif NO_DPROMPT
SHAR_EOF
#	End of shell archive
exit 0
-------