[comp.sources.unix] v14i063: Jove, an emacs variant, version 4.9, Part07/21

rsalz@bbn.com (Rich Salz) (04/26/88)

Submitted-by: Jonathan Payne <jpayne@cs.rochester.edu>
Posting-number: Volume 14, Issue 63
Archive-name: jove4.9/part07

#! /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 archive 7 (of 21)."
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f './c.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'./c.c'\"
else
echo shar: Extracting \"'./c.c'\" \(14160 characters\)
sed "s/^X//" >'./c.c' <<'END_OF_FILE'
X/***************************************************************************
X * This program is Copyright (C) 1986, 1987, 1988 by Jonathan Payne.  JOVE *
X * is provided to you without charge, and with no warranty.  You may give  *
X * away copies of JOVE, including sources, provided that this notice is    *
X * included in all the files.                                              *
X ***************************************************************************/
X
X/* Contains commands for C mode.  Paren matching routines are in here. */
X
X#include "jove.h"
X#include "re.h"
X#include "ctype.h"
X
X#ifdef MAC
X#	undef private
X#	define private
X#endif
X
X#ifdef	LINT_ARGS
private int
X	backslashed(char *, int);
private void	
X	do_expr(int, int),
X	FindMatch(int),
X	parse_cmt_fmt(char *),
X	strip_c(char *, char *);
X#else
private int
X	backslashed();
private void	
X	do_expr(),
X	FindMatch(),
X	parse_cmt_fmt(),
X	strip_c();
X#endif	/* LINT_ARGS */
X
X#ifdef MAC
X#	undef private
X#	define private static
X#endif
X
X
private int
backslashed(lp, cpos)
register char	*lp;
register int	cpos;
X{
X	register int	cnt = 0;
X
X	while (cpos > 0 && lp[--cpos] == '\\')
X		cnt += 1;
X	return (cnt % 2);
X}
X
private char	*p_types = "(){}[]";
private int	mp_kind;
X#define MP_OKAY		0
X#define MP_MISMATCH	1
X#define MP_UNBALANCED	2
X
void
mp_error()
X{
X	switch (mp_kind) {
X	case MP_MISMATCH:
X		message("[Mismatched parentheses]");
X		break;
X
X	case MP_UNBALANCED:
X		message("[Unbalanced parenthesis]");
X		break;
X
X	case MP_OKAY:
X	default:
X		return;
X	}
X	rbell();
X}
X
X/* Search from the current position for the paren that matches p_type.
X   Search in the direction dir.  If can_mismatch is YES then it is okay
X   to have mismatched parens.  If stop_early is YES then when an open
X   paren is found at the beginning of a line, it is assumed that there
X   is no point in backing up further.  This is so when you hit tab or
X   LineFeed outside, in-between procedure/function definitions, it won't
X   sit there searching all the way to the beginning of the file for a
X   match that doesn't exist.  {forward,backward}-s-expression are the
X   only ones that insist on getting the "true" story. */
X
Bufpos *
m_paren(p_type, dir, can_mismatch, can_stop)
char	p_type;
register int	dir;
X{
X	static Bufpos	ret;
X	Bufpos	savedot,
X		*sp;
X	char	re_buf[100],
X		*re_alts[NALTS];
X	int	count = 0;
X	register char	*lp,
X			c;
X	char	p_match,
X		re_str[128],
X		*cp,
X		quote_c = 0;
X	register int	c_char;
X	int	in_comment = -1,
X		stopped = NO;
X
X	sprintf(re_str, "[(){}[\\]%s]", (MajorMode(CMODE)) ? "/\"'" : "\"");
X	REcompile(re_str, 1, re_buf, re_alts);
X	if (cp = index(p_types, p_type))
X		p_match = cp[dir];
X	else
X		complain("[Cannot match %c's]", p_type);
X	DOTsave(&savedot);
X
X	/* To make things a little faster I avoid copying lines into
X	   linebuf by setting curline and curchar by hand.  Warning:
X	   this is slightly to very risky.  When I did this there were
X	   lots of problems with procedures that expect the contents of
X	   curline to be in linebuf. */
X	while (count >= 0) {
X		sp = docompiled(dir, re_buf, re_alts);
X		if (sp == 0)
X			break;
X		lp = lbptr(sp->p_line);
X
X		if (sp->p_line != curline)
X			/* let's assume that strings do NOT go over line
X			   bounderies (for now don't check for wrapping
X 			   strings) */
X			quote_c = 0;
X		curline = sp->p_line;
X		curchar = sp->p_char;	/* here's where I cheat */
X		c_char = curchar;
X		if (dir == FORWARD)
X			c_char -= 1;
X		if (backslashed(lp, c_char))
X			continue;
X		c = lp[c_char];
X		/* check if this is a comment (if we're not inside quotes) */
X		if (quote_c == 0 && c == '/') {
X			int	new_ic;
X
X			if ((c_char != 0) && lp[c_char - 1] == '*') {
X				new_ic = (dir == FORWARD) ? NO : YES;
X				if (new_ic == NO && in_comment == -1) {
X					count = 0;
X					quote_c = 0;
X				}
X			} else if (lp[c_char + 1] == '*') {
X				new_ic = (dir == FORWARD) ? YES : NO;
X				if (new_ic == NO && in_comment == -1) {
X					count = 0;
X					quote_c = 0;
X				}
X			}
X			in_comment = new_ic;
X		}
X		if (in_comment == YES)
X			continue;
X		if (c == '"' || c == '\'') {
X			if (quote_c == c)
X				quote_c = 0;
X			else if (quote_c == 0)
X				quote_c = c;
X		}
X		if (quote_c != 0)
X			continue;
X		if (isopenp(c)) {
X			count += dir;
X			if (c_char == 0 && can_stop == YES && count >= 0) {
X				stopped = YES;
X				break;
X			}
X		} else if (isclosep(c))
X			count -= dir;
X	}
X
X	ret.p_line = curline;
X	ret.p_char = curchar;
X
X	curline = savedot.p_line;
X	curchar = savedot.p_char;	/* here's where I undo it */
X
X	if (count >= 0)
X		mp_kind = MP_UNBALANCED;
X	else if (c != p_match)
X		mp_kind = MP_MISMATCH;
X	else
X		mp_kind = MP_OKAY;
X
X	/* If we stopped (which means we were allowed to stop) and there
X	   was an error, we clear the error so no error message is printed.
X	   An error should be printed ONLY when we are sure about the fact,
X	   namely we didn't stop prematurely HOPING that it was the right
X	   answer. */
X	if (stopped && mp_kind != MP_OKAY) {
X		mp_kind = MP_OKAY;
X		return 0;
X	}
X	if (mp_kind == MP_OKAY || (mp_kind == MP_MISMATCH && can_mismatch == YES))
X		return &ret;
X	return 0;
X}
X
private void
do_expr(dir, skip_words)
register int	dir;
X{
X	register char	c,
X			syntax = (dir == FORWARD) ? _Op : _Cl;
X
X	if (dir == BACKWARD)
X		b_char(1);
X	c = linebuf[curchar];
X	for (;;) {
X		if (!skip_words && ismword(c)) {
X		    WITH_TABLE(curbuf->b_major)
X		    if(dir == FORWARD) f_word(1);
X		    	else b_word(1);	
X		    END_TABLE();
X		    break;
X		} else if (has_syntax(c, syntax)) {
X			FindMatch(dir);
X			break;
X		}
X		f_char(dir);
X		if (eobp() || bobp())
X			return;
X		c = linebuf[curchar];
X	}
X}
X
void
FSexpr()
X{
X	register int	num = arg_value();
X
X	if (num < 0) {
X		set_arg_value(-num);
X		BSexpr();
X	}
X	while (--num >= 0)
X		do_expr(FORWARD, NO);
X}
X
void
FList()
X{
X	register int	num = arg_value();
X
X	if (num < 0) {
X		set_arg_value(-num);
X		BList();
X	}
X	while (--num >= 0)
X		do_expr(FORWARD, YES);
X}
X
void
BSexpr()
X{
X	register int	num = arg_value();
X
X	if (num < 0) {
X		negate_arg_value();
X		FSexpr();
X	}
X	while (--num >= 0)
X		do_expr(BACKWARD, NO);
X}
X
void
BList()
X{
X	register int	num = arg_value();
X
X	if (num < 0) {
X		negate_arg_value();
X		FList();
X	}
X	while (--num >= 0)
X		do_expr(BACKWARD, YES);
X}
X
void
BUpList()
X{
X	Bufpos	*mp;
X	char	c = (MajorMode(CMODE) ? '}' : ')');
X
X	mp = m_paren(c, BACKWARD, NO, YES);
X	if (mp == 0)
X		mp_error();
X	else
X		SetDot(mp);
X}
X
void
FDownList()
X{
X	Bufpos	*sp;
X	char	*sstr = (MajorMode(CMODE) ? "[{([\\])}]" : "[()]"),
X		*lp;
X
X	sp = dosearch(sstr, FORWARD, YES);
X	if (sp != 0)
X		lp = lcontents(sp->p_line);
X	if (sp == 0 || has_syntax(lp[sp->p_char - 1], _Cl))
X		complain("[No contained expression]");
X	SetDot(sp);
X}
X
X/* Move to the matching brace or paren depending on the current position
X   in the buffer. */
X
private void
FindMatch(dir)
X{
X	register Bufpos	*bp;
X	register char	c = linebuf[curchar];
X
X	if ((index(p_types, c) == 0) ||
X	    (backslashed(linebuf, curchar)))
X		complain((char *) 0);
X	if (dir == FORWARD)
X		f_char(1);
X	bp = m_paren(c, dir, YES, NO);
X	if (dir == FORWARD)
X		b_char(1);
X	if (bp != 0)
X		SetDot(bp);
X	mp_error();	/* if there is an error the user wants to
X			   know about it */
X}
X
Bufpos *
c_indent(incrmt)
X{
X	Bufpos	*bp;
X	int	indent = 0;
X
X	if (bp = m_paren('}', BACKWARD, NO, YES)) {
X		Bufpos	save;
X
X		DOTsave(&save);
X		SetDot(bp);
X		ToIndent();
X		indent = calc_pos(linebuf, curchar);
X		SetDot(&save);
X	}
X	if (incrmt) {
X		if (indent == 0)
X			incrmt = tabstop;
X		else
X			incrmt = (tabstop - (indent%tabstop));
X	}
X	n_indent(indent + incrmt);
X	return bp;
X}
X
X#ifdef CMT_FMT
X
char	CmtFmt[80] = "/*%n%! * %c%!%n */";
X
void
Comment()
X{
X	FillComment(CmtFmt);
X}
X
X/* Strip leading and trailing white space.  Skip over any imbedded '\r's. */
X
private void
strip_c(from, to)
char	*from,
X	*to;
X{
X	register char	*fr_p = from,
X			*to_p = to,
X			c;
X
X	while (c = *fr_p) {
X		if (c == ' ' || c == '\t' || c == '\r')
X			fr_p += 1;
X		else
X			break;
X	}
X	while (c = *fr_p) {
X		if (c != '\r')
X			*to_p++ = c;
X		fr_p += 1;
X	}
X	while (--to_p >= to)
X		if (*to_p != ' ' && *to_p != '\t')
X			break;
X	*++to_p = '\0';
X}
X
private char	open_c[20],	/* the open comment format string */
X		open_pat[20],	/* the search pattern for open comment */
X		l_header[20],	/* the prefix for each comment line */
X		l_trailer[20],	/* the suffix ... */
X		close_c[20],
X		close_pat[20];
X
private char	*comment_body[] = {
X 	open_c,
X	l_header,
X	l_trailer,
X	close_c
X};
X					
private int	nlflags;
X
X/* Fill in the data structures above from the format string.  Don't return
X   if there's trouble. */
X
private void
parse_cmt_fmt(str)
char	*str;
X{
X	register char	*fmtp = str;
X	register char	**c_body = comment_body,
X			*body_p = *c_body;
X	int	c,
X	 	newlines = 1;
X
X	/* pick apart the comment string */
X	while (c = *fmtp++) {
X		if (c != '%') {
X			*body_p++ = c;
X			continue;
X		}
X		switch(c = *fmtp++) {
X		case 'n':
X			if (newlines == 2 || newlines == 3)
X				complain("%n not allowed in line header or trailer: %s",
X				  fmtp - 2);
X			nlflags += newlines;
X			*body_p++ = '\r';
X			break;
X		case 't':
X			*body_p++ = '\t';
X			break;
X		case '%':
X			*body_p++ = '%';
X			break;
X		case '!':
X		case 'c':
X			newlines += 1;
X			*body_p++ = '\0';
X			body_p = *++c_body;
X			break;
X		default:
X			complain("[Unknown comment escape: %%%c]", c);
X			/* VARARGS */
X			break;
X		}
X	}
X	*body_p = '\0';
X	/* make search patterns */
X	strip_c(open_c, open_pat);
X	strip_c(close_c, close_pat);
X}
X
X#define NL_IN_OPEN_C  ((nlflags % 4) == 1)
X#define NL_IN_CLOSE_C (nlflags >= 4)
X
void
FillComment(format)
char	*format;
X{
X	int	saveRMargin,
X		indent_pos,
X		close_at_dot = NO,
X		slen,
X		header_len,
X		trailer_len;
X	register char	*cp;
X	static char	inside_err[] = "[Must be between %s and %s to re-format]";
X	Bufpos	open_c_pt,
X		close_c_pt,
X		tmp_bp,
X		*match_o,
X		*match_c;
X	Mark	*entry_mark,
X		*open_c_mark,
X		*savedot;
X
X	parse_cmt_fmt(format);
X	/* figure out if we're "inside" a comment */
X 	if ((match_o = dosearch(open_pat, BACKWARD, 0)) == 0)
X		/* VARARGS */
X		complain("No opening %s to match to.", open_pat);
X	open_c_pt = *match_o;
X	if ((match_c = dosearch(close_pat, BACKWARD, NO)) != 0 &&
X	    inorder(open_c_pt.p_line, open_c_pt.p_char,
X		    match_c->p_line, match_c->p_char))
X	  	complain(inside_err, open_pat, close_pat);
X	if ((match_o = dosearch(open_pat, FORWARD, NO)) != 0) {
X		tmp_bp = *match_o;
X		match_o = &tmp_bp;
X	} 
X	if ((match_c = dosearch(close_pat, FORWARD, 0)) != (Bufpos *) 0)
X		close_c_pt = *match_c;
X
X	/* Here's where we figure out whether to format from dot or from
X	   the close comment.  Note that we've already searched backwards to
X	   find the open comment symbol for the comment we are formatting.
X	   The open symbol mentioned below refers to the possible existence
X	   of the next comment.  There are 5 cases:
X		1) no open or close symbol		==> dot
X		2) open, but no close symbol		==> dot
X		3) close, but no open			==> close
X		4) open, close are inorder		==> dot
X		5) open, close are not inorder		==> close */
X
X
X	if (match_o == (Bufpos *) 0) {
X		if (match_c == (Bufpos *) 0)
X			close_at_dot = YES;
X	} else if (match_c == (Bufpos *) 0)
X		close_at_dot = YES;
X	else if (inorder(match_o->p_line, match_o->p_char,
X		 match_c->p_line, match_c->p_char))
X		close_at_dot = YES;
X	if (close_at_dot) {
X		close_c_pt.p_line = curline;
X		close_c_pt.p_char = curchar;
X	} else {
X		SetDot(match_c);
X	}
X	SetDot(&open_c_pt);
X	open_c_mark = MakeMark(curline, curchar, M_FLOATER);
X	indent_pos = calc_pos(linebuf, curchar);
X	/* search for a close comment; delete it if it exits */
X	SetDot(&close_c_pt);
X	if (close_at_dot == 0) {
X		slen = strlen(close_pat);
X		while (slen--)
X			del_char(BACKWARD, 1);
X	}
X	entry_mark = MakeMark(curline, curchar, M_FLOATER);
X	ToMark(open_c_mark);
X	/* always separate the comment body from anything preceeding it */
X	LineInsert(1);
X	DelWtSpace();
X	Bol();
X	for (cp = open_c; *cp; cp++) {
X		if (*cp == '\r') {
X			if (!eolp())
X				LineInsert(1);
X			else
X				line_move(FORWARD, 1, NO);
X		} else if (*cp == ' ' || *cp == '\t') {
X			if (linebuf[curchar] != *cp)
X				insert_c(*cp, 1);
X		} else
X			/* Since we matched the open comment string on this
X			   line, we don't need to worry about crossing line
X			   boundaries. */
X			curchar += 1;
X	}
X	savedot = MakeMark(curline, curchar, M_FLOATER);
X
X	/* We need to strip the line header pattern of leading white space
X	   since we need to match the line after all of its leading
X	   whitespace is gone. */
X	for (cp = l_header; *cp && (isspace(*cp)); cp++)
X		;
X	header_len = strlen(cp);
X	trailer_len = strlen(l_trailer);
X
X	/* Strip each comment line of the open and close comment strings
X	   before reformatting it. */
X
X	do {
X		Bol();
X		DelWtSpace();
X		if (header_len && !strncmp(linebuf, cp, header_len))
X			del_char(FORWARD, header_len);
X		if (trailer_len) {
X			Eol();
X			if ((curchar > trailer_len) &&
X			    (!strncmp(&linebuf[curchar - trailer_len],
X				      l_trailer, trailer_len)))
X				del_char(BACKWARD, trailer_len);
X		}
X		if (curline->l_next != 0)
X			line_move(FORWARD, 1, NO);
X		else
X			break;
X	} while (curline != entry_mark->m_line->l_next);
X
X	do_set_mark(savedot->m_line, savedot->m_char);
X	ToMark(entry_mark);
X	saveRMargin = RMargin;
X	RMargin = saveRMargin - strlen(l_header) -
X		  strlen(l_trailer) - indent_pos + 2;
X	do_rfill(NO);
X	RMargin = saveRMargin;
X	/* get back to the start of the comment */
X	PopMark(); 
X	do {
X		if (curline == open_c_mark->m_line->l_next) {
X			;
X		} else {
X			n_indent(indent_pos);
X			ins_str(l_header, NO);
X		}
X		Eol();
X		if (!NL_IN_CLOSE_C && (curline == entry_mark->m_line))
X			;
X		else
X			ins_str(l_trailer, NO);
X		if (curline->l_next != 0)
X			line_move(FORWARD, 1, NO);
X		else 
X			break;
X	} while (curline != entry_mark->m_line->l_next);
X	/* handle the close comment symbol */
X	if (curline == entry_mark->m_line->l_next) {
X		line_move(BACKWARD, 1, NO);
X		Eol();
X	}
X	DelWtSpace();
X	/* if the addition of the close symbol would cause the line to be
X	   too long, put the close symbol on the next line. */
X	if (!(NL_IN_CLOSE_C) &&
X	  strlen(close_c) + calc_pos(linebuf, curchar) > RMargin) {
X		LineInsert(1);
X		n_indent(indent_pos);
X	}
X	for (cp = close_c; *cp; cp++) {
X		if (*cp == '\r') {
X			LineInsert(1);
X			n_indent(indent_pos);
X		} else
X			insert_c(*cp, 1);
X	}
X	ToMark(open_c_mark);
X	Eol();
X	del_char(FORWARD, 1);
X}
X
X#endif /* CMT_FMT */
X
END_OF_FILE
if test 14160 -ne `wc -c <'./c.c'`; then
    echo shar: \"'./c.c'\" unpacked with wrong size!
fi
# end of './c.c'
fi
if test -f './insert.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'./insert.c'\"
else
echo shar: Extracting \"'./insert.c'\" \(14465 characters\)
sed "s/^X//" >'./insert.c' <<'END_OF_FILE'
X/***************************************************************************
X * This program is Copyright (C) 1986, 1987, 1988 by Jonathan Payne.  JOVE *
X * is provided to you without charge, and with no warranty.  You may give  *
X * away copies of JOVE, including sources, provided that this notice is    *
X * included in all the files.                                              *
X ***************************************************************************/
X
X#include "jove.h"
X#include "ctype.h"
X#include "table.h"
X
X#ifdef MAC
X#	undef private
X#	define private
X#endif
X
X#ifdef	LINT_ARGS
private int
X	newchunk(void);
private void	
X	init_specials(void),
X	remfreelines(struct chunk *);
X#else
private int
X	newchunk();
private void	
X	init_specials(),
X	remfreelines();
X#endif	/* LINT_ARGS */
X
X#ifdef MAC
X#	undef private
X#	define private static
X#endif
X
X/* Make a newline after AFTER in buffer BUF, UNLESS after is 0,
X   in which case we insert the newline before after. */
X
Line *
listput(buf, after)
register Buffer	*buf;
register Line	*after;
X{
X	register Line	*newline = nbufline();
X
X	if (after == 0) {	/* Before the first line */
X		newline->l_next = buf->b_first;
X		newline->l_prev = 0;
X		buf->b_first = newline;
X	} else {
X		newline->l_prev = after;
X		newline->l_next = after->l_next;
X		after->l_next = newline;
X	}
X	if (newline->l_next)
X		newline->l_next->l_prev = newline;
X	else
X		if (buf)
X			buf->b_last = newline;
X	if (buf && buf->b_dot == 0)
X		buf->b_dot = newline;
X	return newline;
X}	
X
X/* Divide the current line and move the current line to the next one */
X
void
LineInsert(num)
register int	num;
X{
X	char	newline[LBSIZE];
X	register Line	*newdot,
X			*olddot;
X	int	oldchar;
X
X	olddot = curline;
X	oldchar = curchar;
X
X	newdot = curline;
X	while (--num >= 0) {
X		newdot = listput(curbuf, newdot);
X		SavLine(newdot, NullStr);
X	}
X
X	modify();
X	if (curchar != 0) {
X		strcpy(newline, &linebuf[curchar]);
X		linebuf[curchar] = '\0';	/* Shorten this line */
X		SavLine(curline, linebuf);
X		strcpy(linebuf, newline);
X	} else {	/* Redisplay optimization */
X		newdot->l_dline = curline->l_dline;
X		SavLine(curline, NullStr);
X	}
X
X	makedirty(curline);
X	curline = newdot;
X	curchar = 0;
X	makedirty(curline);
X	IFixMarks(olddot, oldchar, curline, curchar);
X}	
X
X/* Makes the indent of the current line == goal.  If the current indent
X   is greater than GOAL it deletes.  If more indent is needed, it uses
X   tabs and spaces to get to where it's going. */
X
void
n_indent(goal)
register int	goal;
X{
X	int	dotcol,
X		incrmt;
X
X	ToIndent();
X	dotcol = calc_pos(linebuf, curchar);
X	if (goal < dotcol) {
X		DelWtSpace();
X		dotcol = 0;
X	}
X
X	for (;;) {
X		incrmt = (tabstop - (dotcol % tabstop));
X		if (dotcol + incrmt > goal)
X			break;
X		insert_c('\t', 1);
X		dotcol += incrmt;
X	}
X	if (dotcol != goal)
X		insert_c(' ', (goal - dotcol));
X}
X
X#ifdef ABBREV
void
MaybeAbbrevExpand()
X{
X	if (MinorMode(Abbrev) && !ismword(LastKeyStruck) &&
X	    !bolp() && ismword(linebuf[curchar - 1]))
X		AbbrevExpand();
X}
X#endif
X
void
SelfInsert()
X{
X#ifdef ABBREV
X	MaybeAbbrevExpand();
X#endif
X	if (LastKeyStruck != CTL('J') && MinorMode(OverWrite)) {
X		register int	num,
X				i;
X
X		for (i = 0, num = arg_value(); i < num; i++) {
X			int	pos = calc_pos(linebuf, curchar);
X
X			if (!eolp()) {
X				if (linebuf[curchar] == '\t') {
X					if ((pos + 1) == ((pos + tabstop) - (pos % tabstop)))
X						del_char(FORWARD, 1);
X				} else
X					del_char(FORWARD, 1);
X			}
X			insert_c(LastKeyStruck, 1);
X		}
X	} else
X		Insert(LastKeyStruck);
X
X	if (MinorMode(Fill) && (curchar >= RMargin ||
X			       (calc_pos(linebuf, curchar) >= RMargin))) {
X		int margin;
X		Bufpos save;
X
X		if (MinorMode(Indent)) {
X			DOTsave(&save);
X			ToIndent();
X			margin = calc_pos(linebuf, curchar);
X			SetDot(&save);
X		} else
X			margin = LMargin;
X		DoJustify(curline, 0, curline,
X			  curchar + strlen(&linebuf[curchar]), 1, margin);
X	}
X}
X
void
Insert(c)
X{
X	if (c == CTL('J'))
X		LineInsert(arg_value());
X	else
X		insert_c(c, arg_value());
X}
X
X/* insert character C N times at point */
void
insert_c(c, n)
X{
X	if (n <= 0)
X		return;
X	modify();
X	makedirty(curline);
X	ins_c(c, linebuf, curchar, n, LBSIZE);
X	IFixMarks(curline, curchar, curline, curchar + n);
X	curchar += n;
X}	
X
X/* Tab in to the right place for C mode */
X
void
Tab()
X{
X#ifdef LISP
X	if (MajorMode(LISPMODE) && (bolp() || !eolp())) {
X		int	dotchar = curchar;
X		Mark	*m = 0;
X
X		ToIndent();
X		if (dotchar > curchar)
X			m = MakeMark(curline, dotchar, M_FLOATER);
X		(void) lisp_indent();
X		if (m) {
X			ToMark(m);
X			DelMark(m);
X		} else
X			ToIndent();
X		return;
X	}
X#endif
X	if (MajorMode(CMODE) && strlen(linebuf) == 0)
X		(void) c_indent(CIndIncrmt);
X	else
X		SelfInsert();
X}
X
void
QuotChar()
X{
X	int	c,
X		slow;
X
X	c = waitchar(&slow);
X	if (slow)
X		message(key_strokes);
X	if (c != CTL('@'))
X		Insert(c);
X}
X
X/* Insert the paren.  If in C mode and c is a '}' then insert the
X   '}' in the "right" place for C indentation; that is indented 
X   the same amount as the matching '{' is indented. */
X
int	PDelay = 5,	/* 1/2 a second */
X	CIndIncrmt = 8;
X
void
DoParen()
X{
X	Bufpos	*bp = (Bufpos *) -1;
X	int	nx,
X		c = LastKeyStruck;
X
X	if (!isclosep(c)) {
X		SelfInsert();
X		return;
X	}
X
X	if (MajorMode(CMODE) && c == '}' && blnkp(linebuf))
X		bp = c_indent(0);
X#ifdef LISP
X	if (MajorMode(LISPMODE) && c == ')' && blnkp(linebuf))
X		bp = lisp_indent();
X#endif
X	SelfInsert();
X#ifdef MAC
X	if (MinorMode(ShowMatch) && !in_macro()) {
X#else
X	if (MinorMode(ShowMatch) && !charp() && !in_macro()) {
X#endif
X		b_char(1);	/* Back onto the ')' */
X		if ((int) bp == -1)
X			bp = m_paren(c, BACKWARD, NO, YES);
X		f_char(1);
X		if (bp != 0) {
X			nx = in_window(curwind, bp->p_line);
X			if (nx != -1) {		/* is visible */
X				Bufpos	b;
X
X				DOTsave(&b);
X				SetDot(bp);
X				SitFor(PDelay);
X				SetDot(&b);
X			} else
X				s_mess("%s", lcontents(bp->p_line));
X		}
X		mp_error();	/* display error message */
X	}
X}
X
void
LineAI()
X{
X	DoNewline(TRUE);
X}
X
void
Newline()
X{
X	DoNewline(MinorMode(Indent));
X}	
X
void
DoNewline(indentp)
X{
X	Bufpos	save;
X	int	indent;
X
X	/* first we calculate the indent of the current line */
X	DOTsave(&save);
X	ToIndent();
X	indent = calc_pos(linebuf, curchar);
X	SetDot(&save);
X
X#ifdef ABBREV
X	MaybeAbbrevExpand();
X#endif
X#ifdef LISP
X	if (MajorMode(LISPMODE))
X		DelWtSpace();
X	else
X#endif
X	    if (indentp || blnkp(linebuf))
X		DelWtSpace();
X		
X	/* If there is more than 2 blank lines in a row then don't make
X	   a newline, just move down one. */
X	if (arg_value() == 1 && eolp() && TwoBlank())
X		SetLine(curline->l_next);
X	else
X		LineInsert(arg_value());
X
X	if (indentp)
X#ifdef LISP
X	    if (MajorMode(LISPMODE))
X		(void) lisp_indent();
X	    else
X#endif
X		n_indent((LMargin == 0) ? indent : LMargin);
X}
X
void
ins_str(str, ok_nl)
register char	*str;
X{
X	register char	c;
X	Bufpos	save;
X	int	llen;
X
X	if (*str == 0)
X		return;		/* ain't nothing to insert! */
X	DOTsave(&save);
X	llen = strlen(linebuf);
X	while (c = *str++) {
X		if (c == '\n' || (ok_nl && llen >= LBSIZE - 2)) {
X			IFixMarks(save.p_line, save.p_char, curline, curchar);
X			modify();
X			makedirty(curline);
X			LineInsert(1);
X			DOTsave(&save);
X			llen = strlen(linebuf);
X		}
X		if (c != '\n') {
X			ins_c(c, linebuf, curchar++, 1, LBSIZE);
X			llen += 1;
X		}
X	}
X	IFixMarks(save.p_line, save.p_char, curline, curchar);
X	modify();
X	makedirty(curline);
X}
X
void
open_lines(n)
X{
X	Bufpos	dot;
X
X	DOTsave(&dot);
X	LineInsert(n);	/* Open the lines... */
X	SetDot(&dot);
X}
X
void
OpenLine()
X{
X	open_lines(arg_value());
X}
X
X/* Take the region FLINE/FCHAR to TLINE/TCHAR and insert it at
X   ATLINE/ATCHAR in WHATBUF. */
X
Bufpos *
DoYank(fline, fchar, tline, tchar, atline, atchar, whatbuf)
Line	*fline,
X	*tline,
X	*atline;
Buffer	*whatbuf;
X{
X	register Line	*newline;
X	static Bufpos	bp;
X	char	save[LBSIZE],
X		buf[LBSIZE];
X	Line	*startline = atline;
X	int	startchar = atchar;
X
X	lsave();
X	if (whatbuf)
X		modify();
X	(void) ltobuf(atline, genbuf);
X	strcpy(save, &genbuf[atchar]);
X
X	(void) ltobuf(fline, buf);
X	if (fline == tline)
X		buf[tchar] = '\0';
X
X	linecopy(genbuf, atchar, &buf[fchar]);
X	atline->l_dline = putline(genbuf);
X	makedirty(atline);
X
X	fline = fline->l_next;
X	while (fline != tline->l_next) {
X		newline = listput(whatbuf, atline);
X		newline->l_dline = fline->l_dline;
X		makedirty(newline);
X		fline = fline->l_next;
X		atline = newline;
X		atchar = 0;
X	}
X
X	getline(atline->l_dline, genbuf);
X	atchar += tchar;
X	linecopy(genbuf, atchar, save);
X	atline->l_dline = putline(genbuf);
X	makedirty(atline);
X	IFixMarks(startline, startchar, atline, atchar);
X	bp.p_line = atline;
X	bp.p_char = atchar;
X	this_cmd = YANKCMD;
X	getDOT();			/* Whatever used to be in linebuf */
X	return &bp;
X}
X
void
YankPop()
X{
X	Line	*line,
X		*last;
X	Mark	*mp = CurMark();
X	Bufpos	*dot;
X	int	dir = -1;	/* Direction to rotate the ring */
X
X	if (last_cmd != YANKCMD)
X		complain("Yank something first!");
X
X	lfreelist(reg_delete(mp->m_line, mp->m_char, curline, curchar));
X
X	/* Now must find a recently killed region. */
X
X	if (arg_value() < 0)
X		dir = 1;
X
X	killptr += dir;
X	for (;;) {
X		if (killptr < 0)
X			killptr = NUMKILLS - 1;
X		else if (killptr >= NUMKILLS)
X			killptr = 0;
X		if (killbuf[killptr])
X			break;
X		killptr += dir;
X	}
X
X	this_cmd = YANKCMD;
X
X	line = killbuf[killptr];
X	last = lastline(line);
X	dot = DoYank(line, 0, last, length(last), curline, curchar, curbuf);
X	MarkSet(CurMark(), curline, curchar);
X	SetDot(dot);
X}
X
X/* This is an attempt to reduce the amount of memory taken up by each line.
X   Without this each malloc of a line uses sizeof (line) + sizeof(HEADER)
X   where line is 3 words and HEADER is 1 word.
X   This is going to allocate memory in chucks of CHUNKSIZE * sizeof (line)
X   and divide each chuck into lineS.  A line is free in a chunk when its
X   line->l_dline == 0, so freeline sets dline to 0. */
X
X#define CHUNKSIZE	300
X
struct chunk {
X	int	c_nlines;	/* Number of lines in this chunk (so they
X				   don't all have to be CHUNKSIZE long). */
X	Line	*c_block;	/* Chunk of memory */
X	struct chunk	*c_nextfree;	/* Next chunk of lines */
X};
X
private struct chunk	*fchunk = 0;
private Line	*ffline = 0;	/* First free line */
X
void
freeline(line)
register Line	*line;
X{
X	line->l_dline = 0;
X	line->l_next = ffline;
X	if (ffline)
X		ffline->l_prev = line;
X	line->l_prev = 0;
X	ffline = line;
X}
X
void
lfreelist(first)
register Line	*first;
X{
X	if (first)
X		lfreereg(first, lastline(first));
X}
X
X/* Append region from line1 to line2 onto the free list of lines */
X
void
lfreereg(line1, line2)
register Line	*line1,
X		*line2;
X{
X	register Line	*next,
X			*last = line2->l_next;
X
X	while (line1 != last) {
X		next = line1->l_next;
X		freeline(line1);
X		line1 = next;
X	}
X}
X
private int
newchunk()
X{
X	register Line	*newline;
X	register int	i;
X	struct chunk	*f;
X	int	nlines = CHUNKSIZE;
X
X	f = (struct chunk *) emalloc(sizeof (struct chunk));
X	if (f == 0)
X		return 0;
X
X	if ((f->c_block = (Line *) malloc((unsigned) (sizeof (Line) * nlines))) == 0) {
X		while (nlines > 0) {
X			f->c_block = (Line *) malloc((unsigned) (sizeof (Line) * nlines));
X			if (f->c_block != 0)
X				break;
X			nlines /= 2;
X		}
X	}
X
X	if (nlines <= 0)
X		return 0;
X
X	f->c_nlines = nlines;
X	for (i = 0, newline = f->c_block; i < nlines; newline++, i++)
X		freeline(newline);
X	f->c_nextfree = fchunk;
X	fchunk = f;
X	return 1;
X}
X
X/* New BUFfer LINE */
X
Line *
nbufline()
X{
X	register Line	*newline;
X
X	if (ffline == 0)	/* No free list */
X		if (newchunk() == 0)
X			complain("[Out of lines] ");
X	newline = ffline;
X	ffline = ffline->l_next;
X	if (ffline)
X		ffline->l_prev = 0;
X	return newline;
X}
X
X/* Remove the free lines, in chunk c, from the free list because they are
X   no longer free. */
X
private void
remfreelines(c)
register struct chunk	*c;
X{
X	register Line	*lp;
X	register int	i;
X
X	for (lp = c->c_block, i = 0; i < c->c_nlines; i++, lp++) {
X		if (lp->l_prev)
X			lp->l_prev->l_next = lp->l_next;
X		else
X			ffline = lp->l_next;
X		if (lp->l_next)
X			lp->l_next->l_prev = lp->l_prev;
X	}
X}
X
X/* This is used to garbage collect the chunks of lines when malloc fails
X   and we are NOT looking for a new buffer line.  This goes through each
X   chunk, and if every line in a given chunk is not allocated, the entire
X   chunk is `free'd by "free()". */
X
void
GCchunks()
X{
X	register struct chunk	*cp;
X	struct chunk	*prev = 0,
X			*next = 0;
X	register int	i;
X	register Line	*newline;
X
X 	for (cp = fchunk; cp != 0; cp = next) {
X		for (i = 0, newline = cp->c_block; i < cp->c_nlines; newline++, i++)
X			if (newline->l_dline != 0)
X				break;
X
X 		next = cp->c_nextfree;
X
X		if (i == cp->c_nlines) {		/* Unlink it!!! */
X			if (prev)
X				prev->c_nextfree = cp->c_nextfree;
X			else
X				fchunk = cp->c_nextfree;
X			remfreelines(cp);
X			free((char *) cp->c_block);
X			free((char *) cp);
X		} else
X			prev = cp;
X	}
X}
X
X#ifdef LISP
X
X/* Grind S-Expr */
X
void
GSexpr()
X{
X	Bufpos	dot,
X		end;
X
X	if (linebuf[curchar] != '(')
X		complain((char *) 0);
X	DOTsave(&dot);
X	FSexpr();
X	DOTsave(&end);
X	SetDot(&dot);
X	for (;;) {
X		if (curline == end.p_line)
X			break;
X		line_move(FORWARD, 1, NO);
X		if (!blnkp(linebuf))
X			(void) lisp_indent();
X	}
X	SetDot(&dot);
X}
X
X/* lisp_indent() indents a new line in Lisp Mode, according to where
X   the matching close-paren would go if we typed that (sort of). */
X
private Table	*specials = NIL;
X
private void
init_specials()
X{
X	static char *words[] = {
X		"case",
X		"def",
X		"dolist",
X		"fluid-let",
X		"lambda",
X		"let",
X		"lexpr",
X		"macro",
X		"named-l",	/* named-let and named-lambda */
X		"nlambda",
X		"prog",
X		"selectq",
X		0
X	};
X	char	**wordp = words;
X
X	specials = make_table();
X	while (*wordp)
X		add_word(*wordp++, specials);
X}
X
void
AddSpecial()
X{
X	char	*word;
X
X	word = ask((char *) 0, ProcFmt);
X	if (specials == NIL)
X		init_specials();
X	add_word(copystr(word), specials);
X}
X
Bufpos *
lisp_indent()
X{
X	Bufpos	*bp,
X		savedot;
X	int	goal;
X
X	bp = m_paren(')', BACKWARD, NO, YES);
X
X	if (bp == 0)
X		return 0;
X
X	/* We want to end up
X	 
X	 	(atom atom atom ...
X	 	      ^ here.
X	 */
X
X	DOTsave(&savedot);
X	SetDot(bp);
X	f_char(1);
X	if (linebuf[curchar] != '(') {
X		register Word	*wp;
X
X		if (specials == NIL)
X			init_specials();
X		for (wp = table_top(specials); wp != NIL; wp = next_word(wp))
X			if (casencmp(word_text(wp), &linebuf[curchar], word_length(wp)) == 0)
X				break;
X		if (wp == NIL) {	/* not special */
X			int	c_char = curchar;
X
X			WITH_TABLE(curbuf->b_major)
X				f_word(1);
X			END_TABLE();
X			if (LookingAt("[ \t]*;\\|[ \t]*$", linebuf, curchar))
X				curchar = c_char;
X			else while (linebuf[curchar] == ' ')
X				curchar += 1;
X		} else
X			curchar += 1;
X	}
X	goal = calc_pos(linebuf, curchar);
X	SetDot(&savedot);
X	n_indent(goal);
X
X	return bp;
X}
X#endif /* LISP */
END_OF_FILE
if test 14465 -ne `wc -c <'./insert.c'`; then
    echo shar: \"'./insert.c'\" unpacked with wrong size!
fi
# end of './insert.c'
fi
if test -f './recover.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'./recover.c'\"
else
echo shar: Extracting \"'./recover.c'\" \(14306 characters\)
sed "s/^X//" >'./recover.c' <<'END_OF_FILE'
X/***************************************************************************
X * This program is Copyright (C) 1986, 1987, 1988 by Jonathan Payne.  JOVE *
X * is provided to you without charge, and with no warranty.  You may give  *
X * away copies of JOVE, including sources, provided that this notice is    *
X * included in all the files.                                              *
X ***************************************************************************/
X
X/* Recovers JOVE files after a system/editor crash.
X   Usage: recover [-d directory] [-syscrash]
X   The -syscrash option is specified in /etc/rc and what it does is
X   move all the jove tmp files from TMP_DIR to REC_DIR.
X
X   The -d option lets you specify the directory to search for tmp files when
X   the default isn't the right one.
X
X   Look in Makefile to change the default directories. */
X
X#include <stdio.h>	/* Do stdio first so it doesn't override OUR
X			   definitions. */
X#undef EOF
X#undef BUFSIZ
X#undef putchar
X#undef getchar
X
X#define STDIO
X
X#include "jove.h"
X#include "temp.h"
X#include "rec.h"
X#include <signal.h>
X#include <sys/file.h>
X#include <sys/stat.h>
X#include <sys/dir.h>
X
X#ifndef L_SET
X#	define L_SET	0
X#	define L_INCR	1
X#endif
X
char	blk_buf[BUFSIZ];
int	nleft;
FILE	*ptrs_fp;
int	data_fd;
struct rec_head	Header;
char	datafile[40],
X	pntrfile[40];
long	Nchars,
X	Nlines;
char	tty[] = "/dev/tty";
int	UserID,
X	Verbose = 0;
char	*Directory = 0;		/* the directory we're looking in */
X
struct file_pair {
X	char	*file_data,
X		*file_rec;
X#define INSPECTED	01
X	int	file_flags;
X	struct file_pair	*file_next;
X} *First = 0,
X  *Last = 0;
X
struct rec_entry	*buflist[100] = {0};
X
X#ifndef BSD4_2
X
typedef struct {
X	int	d_fd;		/* File descriptor for this directory */
X} DIR;
X
DIR *
opendir(dir)
char	*dir;
X{
X	DIR	*dp = (DIR *) malloc(sizeof *dp);
X
X	if ((dp->d_fd = open(dir, 0)) == -1)
X		return NULL;
X	return dp;
X}
X
closedir(dp)
DIR	*dp;
X{
X	(void) close(dp->d_fd);
X	free(dp);
X}
X
struct direct *
readdir(dp)
DIR	*dp;
X{
X	static struct direct	dir;
X
X	do
X		if (read(dp->d_fd, &dir, sizeof dir) != sizeof dir)
X			return NULL;
X#if defined(elxsi) && defined(SYSV)
X	/*
X	 * Elxsi has a BSD4.2 implementation which may or may not use
X	 * `twisted inodes' ...  Anyone able to check?
X	 */
X	while (*(unsigned short *)&dir.d_ino == 0);
X#else
X	while (dir.d_ino == 0);
X#endif
X
X	return &dir;
X}
X
X#endif /* BSD4_2 */
X
X/* Get a line at `tl' in the tmp file into `buf' which should be LBSIZE
X   long. */
X
getline(tl, buf)
disk_line	tl;
char	*buf;
X{
X	register char	*bp,
X			*lp;
X	register int	nl;
X	char	*getblock();
X
X	lp = buf;
X	bp = getblock(tl >> 1);
X	nl = nleft;
X	tl = blk_round(tl);
X
X	while (*lp++ = *bp++) {
X		if (--nl == 0) {
X			tl = forward_block(tl);
X			bp = getblock(tl >> 1);
X			nl = nleft;
X		}
X	}
X}
X
char *
getblock(atl)
disk_line	atl;
X{
X	int	bno,
X		off;
X	static int	curblock = -1;
X
X	bno = da_to_bno(atl);
X	off = da_to_off(atl);
X	nleft = BUFSIZ - off;
X
X	if (bno != curblock) {
X		lseek(data_fd, (long) bno * BUFSIZ, L_SET);
X		read(data_fd, blk_buf, BUFSIZ);
X		curblock = bno;
X	}
X	return blk_buf + off;
X}
X
char *
copystr(s)
char	*s;
X{
X	char	*str;
X
X	str = malloc(strlen(s) + 1);
X	strcpy(str, s);
X
X	return str;
X}
X
X/* Scandir returns the number of entries or -1 if the directory cannoot
X   be opened or malloc fails. */
X
scandir(dir, nmptr, qualify, sorter)
char	*dir;
struct direct	***nmptr;
int	(*qualify)();
struct direct	*(*sorter)();
X{
X	DIR	*dirp;
X	struct direct	*entry,
X			**ourarray;
X	int	nalloc = 10,
X		nentries = 0;
X
X	if ((dirp = opendir(dir)) == NULL)
X		return -1;
X	ourarray = (struct direct **) malloc(nalloc * sizeof (struct direct *));
X	while ((entry = readdir(dirp)) != NULL) {
X		if (qualify != 0 && (*qualify)(entry) == 0)
X			continue;
X		if (nentries == nalloc) {
X			ourarray = (struct direct **) realloc(ourarray, (nalloc += 10) * sizeof (struct direct));
X			if (ourarray == NULL)
X				return -1;
X		}
X		ourarray[nentries] = (struct direct *) malloc(sizeof *entry);
X		*ourarray[nentries] = *entry;
X		nentries += 1;
X	}
X	closedir(dirp);
X	if (nentries != nalloc)
X		ourarray = (struct direct **) realloc(ourarray,
X					(nentries * sizeof (struct direct)));
X	if (sorter != 0)
X		qsort(ourarray, nentries, sizeof (struct direct **), sorter);
X	*nmptr = ourarray;
X
X	return nentries;
X}
X
alphacomp(a, b)
struct direct	**a,
X		**b;
X{
X	return strcmp((*a)->d_name, (*b)->d_name);
X}
X
char	*CurDir;
X
X/* Scan the DIRNAME directory for jove tmp files, and make a linked list
X   out of them. */
X
get_files(dirname)
char	*dirname;
X{
X	int	add_name();
X	struct direct	**nmptr;
X
X	CurDir = dirname;
X	scandir(dirname, &nmptr, add_name, (int (*)())0);
X}
X
add_name(dp)
struct direct	*dp;
X{
X	char	dfile[128],
X		rfile[128];
X	struct file_pair	*fp;
X	struct rec_head		header;
X	int	fd;
X
X	if (strncmp(dp->d_name, "jrec", 4) != 0)
X		return 0;
X	/* If we get here, we found a "recover" tmp file, so now
X	   we look for the corresponding "data" tmp file.  First,
X	   though, we check to see whether there is anything in
X	   the "recover" file.  If it's 0 length, there's no point
X	   in saving its name. */
X	(void) sprintf(rfile, "%s/%s", CurDir, dp->d_name);
X	(void) sprintf(dfile, "%s/jove%s", CurDir, dp->d_name + 4);
X	if ((fd = open(rfile, 0)) != -1) {
X		if ((read(fd, (char *) &header, sizeof header) != sizeof header)) {
X			close(fd);
X		    	return 0;
X		} else
X			close(fd);
X	}
X	if (access(dfile, 0) != 0) {
X		fprintf(stderr, "recover: can't find the data file for %s/%s\n", Directory, dp->d_name);
X		fprintf(stderr, "so deleting...\n");
X		(void) unlink(rfile);
X		(void) unlink(dfile);
X		return 0;
X	}
X	/* If we get here, we've found both files, so we put them
X	   in the list. */
X	fp = (struct file_pair *) malloc (sizeof *fp);
X	if ((char *) fp == 0) {
X		fprintf(stderr, "recover: cannot malloc for file_pair.\n");
X		exit(-1);
X	}
X	fp->file_data = copystr(dfile);
X	fp->file_rec = copystr(rfile);
X	fp->file_flags = 0;
X	fp->file_next = First;
X	First = fp;
X
X	return 1;
X}
X
options()
X{
X	printf("Options are:\n");
X	printf("	?		list options.\n");
X	printf("	get		get a buffer to a file.\n");
X	printf("	list		list known buffers.\n");
X	printf("	print		print a buffer to terminal.\n");
X	printf("	quit		quit and delete jove tmp files.\n");
X	printf("	restore		restore all buffers.\n");
X}
X
X/* Returns a legitimate buffer # */
X
struct rec_entry **
getsrc()
X{
X	char	name[128];
X	int	number;
X
X	for (;;) {
X		tellme("Which buffer ('?' for list)? ", name);
X		if (name[0] == '?')
X			list();
X		else if (name[0] == '\0')
X			return 0;
X		else if ((number = atoi(name)) > 0 && number <= Header.Nbuffers)
X			return &buflist[number];
X		else {
X			int	i;
X
X			for (i = 1; i <= Header.Nbuffers; i++)
X				if (strcmp(buflist[i]->r_bname, name) == 0)
X					return &buflist[i];
X			printf("%s: unknown buffer.\n", name);
X		}
X	}
X}
X
X/* Get a destination file name. */
X
static char *
getdest()
X{
X	static char	filebuf[256];
X
X	tellme("Output file: ", filebuf);
X	if (filebuf[0] == '\0')
X		return 0;
X	return filebuf;
X}
X
X#include "ctype.h"
X
char *
readword(buf)
char	*buf;
X{
X	int	c;
X	char	*bp = buf;
X
X	while (index(" \t\n", c = getchar()))
X		;
X
X	do {
X		if (index(" \t\n", c))
X			break;
X		*bp++ = c;
X	} while ((c = getchar()) != EOF);
X	*bp = 0;
X
X	return buf;
X}
X
tellme(quest, answer)
char	*quest,
X	*answer;
X{
X	if (stdin->_cnt <= 0) {
X		printf("%s", quest);
X		fflush(stdout);
X	}
X	readword(answer);
X}
X
X/* Print the specified file to strandard output. */
X
jmp_buf	int_env;
X
catch()
X{
X	longjmp(int_env, 1);
X}
X
restore()
X{
X	register int	i;
X	char	tofile[100],
X		answer[30];
X	int	nrecovered = 0;
X
X	for (i = 1; i <= Header.Nbuffers; i++) {
X		(void) sprintf(tofile, "#%s", buflist[i]->r_bname);
tryagain:
X		printf("Restoring %s to %s, okay?", buflist[i]->r_bname,
X						     tofile);
X		tellme(" ", answer);
X		switch (answer[0]) {
X		case 'y':
X			break;
X
X		case 'n':
X			continue;
X
X		default:
X			tellme("What file should I use instead? ", tofile);
X			goto tryagain;
X		}
X		get(&buflist[i], tofile);
X		nrecovered += 1;
X	}
X	printf("Recovered %d buffers.\n", nrecovered);
X}
X
get(src, dest)
struct rec_entry	**src;
char	*dest;
X{
X	FILE	*outfile;
X
X	if (src == 0 || dest == 0)
X		return;
X	(void) signal(SIGINT, catch);
X	if (setjmp(int_env) == 0) {
X		if ((outfile = fopen(dest, "w")) == NULL) {
X			printf("recover: cannot create %s.\n", dest);
X			return;
X		}
X		if (dest != tty)
X			printf("\"%s\"", dest);
X		dump_file(src - buflist, outfile);
X	} else
X		printf("\nAborted!\n");
X	fclose(outfile);
X	if (dest != tty)
X		printf(" %ld lines, %ld characters.\n", Nlines, Nchars);
X	(void) signal(SIGINT, SIG_DFL);
X}
X
char **
scanvec(args, str)
register char	**args,
X		*str;
X{
X	while (*args) {
X		if (strcmp(*args, str) == 0)
X			return args;
X		args += 1;
X	}
X	return 0;
X}
X
read_rec(recptr)
struct rec_entry	*recptr;
X{
X	if (fread((char *) recptr, sizeof *recptr, 1, ptrs_fp) != 1)
X		fprintf(stderr, "recover: cannot read record.\n");
X}
X
seekto(which)
X{
X	struct rec_entry	rec;
X	long	offset;
X	int	i;
X
X	offset = sizeof (Header) + (Header.Nbuffers * sizeof (rec));
X	for (i = 1; i < which; i++)
X		offset += buflist[i]->r_nlines * sizeof (disk_line);
X	fseek(ptrs_fp, offset, L_SET);
X}
X
makblist()
X{
X	int	i;
X
X	fseek(ptrs_fp, (long) sizeof (Header), L_SET);
X	for (i = 1; i <= Header.Nbuffers; i++) {
X		if (buflist[i] == 0)
X			buflist[i] = (struct rec_entry *) malloc (sizeof (struct rec_entry));
X		read_rec(buflist[i]);
X	}
X	while (buflist[i]) {
X		free((char *) buflist[i]);
X		buflist[i] = 0;
X		i += 1;
X	}
X}
X
disk_line
getaddr(fp)
register FILE	*fp;
X{
X	register int	nchars = sizeof (disk_line);
X	disk_line	addr;
X	register char	*cp = (char *) &addr;
X
X	while (--nchars >= 0)
X		*cp++ = getc(fp);
X
X	return addr;
X}
X
dump_file(which, out)
FILE	*out;
X{
X	register int	nlines;
X	register disk_line	daddr;
X	char	buf[BUFSIZ];
X
X	seekto(which);
X	nlines = buflist[which]->r_nlines;
X	Nchars = Nlines = 0L;
X	while (--nlines >= 0) {
X		daddr = getaddr(ptrs_fp);
X		getline(daddr, buf);
X		Nlines += 1;
X		Nchars += 1 + strlen(buf);
X		fputs(buf, out);
X		if (nlines > 0)
X			fputc('\n', out);
X	}
X	if (out != stdout)
X		fclose(out);
X}
X
X/* List all the buffers. */
X
list()
X{
X	int	i;
X
X	for (i = 1; i <= Header.Nbuffers; i++)
X		printf("%d) buffer %s  \"%s\" (%d lines)\n", i,
X			buflist[i]->r_bname,
X			buflist[i]->r_fname,
X			buflist[i]->r_nlines);
X}
X
doit(fp)
struct file_pair	*fp;
X{
X	char	answer[30];
X	char	*datafile = fp->file_data,
X		*pntrfile = fp->file_rec;
X
X	ptrs_fp = fopen(pntrfile, "r");
X	if (ptrs_fp == NULL) {
X		if (Verbose)
X			fprintf(stderr, "recover: cannot read rec file (%s).\n", pntrfile);
X		return 0;
X	}
X	fread((char *) &Header, sizeof Header, 1, ptrs_fp);
X	if (Header.Uid != UserID)
X		return 0;
X
X	/* Don't ask about JOVE's that are still running ... */
X#ifdef KILL0
X	if (kill(Header.Pid, 0) == 0)
X		return 0;
X#endif /* KILL0 */
X
X	if (Header.Nbuffers == 0) {
X		printf("There are no modified buffers in %s; should I delete the tmp file?", pntrfile);
X		ask_del(" ", fp);
X		return 1;
X	}
X		
X	if (Header.Nbuffers < 0) {
X		fprintf(stderr, "recover: %s doesn't look like a jove file.\n", pntrfile);
X		ask_del("Should I delete it? ", fp);
X		return 1;	/* We'll, we sort of found something. */
X	}
X	printf("Found %d buffer%s last updated: %s",
X		Header.Nbuffers,
X		Header.Nbuffers != 1 ? "s" : "",
X		ctime(&Header.UpdTime));
X	data_fd = open(datafile, 0);
X	if (data_fd == -1) {
X		fprintf(stderr, "recover: but I can't read the data file (%s).\n", datafile);
X		ask_del("Should I delete the tmp files? ", fp);
X		return 1;
X	}
X	makblist();
X	list();
X
X	for (;;) {
X		tellme("(Type '?' for options): ", answer);
X		switch (answer[0]) {
X		case '\0':
X			continue;
X
X		case '?':
X			options();
X			break;
X
X		case 'l':
X			list();
X			break;
X
X		case 'p':
X			get(getsrc(), tty);
X			break;
X
X		case 'q':
X			ask_del("Shall I delete the tmp files? ", fp);
X			return 1;
X
X		case 'g':
X		    {	/* So it asks for src first. */
X		    	char	*dest;
X		    	struct rec_entry	**src;
X
X		    	if ((src = getsrc()) == 0)
X		    		break;
X		    	dest = getdest();
X			get(src, dest);
X			break;
X		    }
X
X		case 'r':
X			restore();
X			break;
X
X		default:
X			printf("I don't know how to \"%s\"!\n", answer);
X			break;
X		}
X	}
X}
X
ask_del(prompt, fp)
char	*prompt;
struct file_pair	*fp;
X{
X	char	yorn[20];
X
X	tellme(prompt, yorn);
X	if (yorn[0] == 'y')
X		del_files(fp);
X}
X
del_files(fp)
struct file_pair	*fp;
X{
X	(void) unlink(fp->file_data);
X	(void) unlink(fp->file_rec);
X}
X
X#ifdef notdef
savetmps()
X{
X	struct file_pair	*fp;
X	int	status,
X		pid;
X
X	if (strcmp(TMP_DIR, REC_DIR) == 0)
X		return;		/* Files are moved to the same place. */
X	get_files(TMP_DIR);
X	for (fp = First; fp != 0; fp = fp->file_next) {
X		switch (pid = fork()) {
X		case -1:
X			fprintf(stderr, "recover: can't fork\n!");
X			exit(-1);
X
X		case 0:
X			execl("/bin/cp", "cp", fp->file_data, fp->file_rec, 
X				  REC_DIR, (char *)0);
X			fprintf(stderr, "recover: cannot execl /bin/cp.\n");
X			exit(-1);
X
X		default:
X			while (wait(&status) != pid)
X				;
X			if (status != 0)
X				fprintf(stderr, "recover: non-zero status (%d) returned from copy.\n", status);
X		}
X	}
X}
X#endif
X
lookup(dir)
char	*dir;
X{
X	struct file_pair	*fp;
X	struct rec_head		header;
X	char	yorn[20];
X	int	nfound = 0,
X		this_one;
X
X	printf("Checking %s ...\n", dir);
X	Directory = dir;
X	get_files(dir);
X	for (fp = First; fp != 0; fp = fp->file_next) {
X		nfound += doit(fp);
X		if (ptrs_fp)
X			(void) fclose(ptrs_fp);
X		if (data_fd > 0)
X			(void) close(data_fd);
X	}
X	return nfound;
X}
X
main(argc, argv)
int	argc;
char	*argv[];
X{
X	int	nfound;
X	char	**argvp;
X
X	UserID = getuid();
X
X	if (scanvec(argv, "-help")) {
X		printf("recover: usage: recover [-d directory]\n");
X		printf("Use \"recover\" after JOVE has died for some\n");
X		printf("unknown reason.\n\n");
X/*		printf("Use \"recover -syscrash\" when the system is in the process\n");
X		printf("of rebooting.  This is done automatically at reboot time\n");
X		printf("and so most of you don't have to worry about that.\n\n");
X */
X		printf("Use \"recover -d directory\" when the tmp files are store\n");
X		printf("in DIRECTORY instead of the default one (/tmp).\n");
X		exit(0);
X	}
X	if (scanvec(argv, "-v"))
X		Verbose = YES;
X/*	if (scanvec(argv, "-syscrash")) {
X		printf("Recovering jove files ... ");
X		savetmps();
X		printf("Done.\n");
X		exit(0);
X	} */
X	if (argvp = scanvec(argv, "-uid"))
X		UserID = atoi(argvp[1]);
X	if (argvp = scanvec(argv, "-d"))
X		nfound = lookup(argvp[1]);
X	else
X		nfound = lookup(TmpFilePath);
X	if (nfound == 0)
X		printf("There's nothing to recover.\n");
X}
END_OF_FILE
if test 14306 -ne `wc -c <'./recover.c'`; then
    echo shar: \"'./recover.c'\" unpacked with wrong size!
fi
# end of './recover.c'
fi
echo shar: End of archive 7 \(of 21\).
cp /dev/null ark7isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 21 archives.
    rm -f ark[1-9]isdone ark[1-9][0-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
-- 
Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.