Filewatcher File Search
FTP Search
  
Directory (beta)
  
Content Search (beta)
   
pkg://joe-2.9.6-2.src.rpm:223169/joe-2.9.6.tgz  info  downloads

joe-2.9.6/0040755003233700235640000000000007267604100011703 5ustar  xgracstudentjoe-2.9.6/b.c0100600003233700235640000012742307265144131012267 0ustar  xgracstudent/*
	Editor engine
	Copyright
		(C) 1992 Joseph H. Allen

	This file is part of JOE (Joe's Own Editor)
*/

#include <unistd.h>

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#ifndef __MSDOS__
#include <pwd.h>
#endif
#include <errno.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>

#include "config.h"
#include "blocks.h"
#include "undo.h"
#include "vs.h"
#include "va.h"
#include "utils.h"
#include "path.h"
#include "w.h"
#include "tty.h"
#include "scrn.h"
#include "main.h"
#include "bw.h"
#include "uerror.h"

#include "b.h"

char stdbuf[stdsiz];

extern int errno;

int error;
int force = 0;
VFILE *vmem;

char *msgs[] = {
	"Error writing file",
	"Error opening file",
	"Error seeking file",
	"Error reading file",
	"New File"
};

/* Get size of gap (amount of free space) */

#define GGAPSZ(hdr) ((hdr)->ehole-(hdr)->hole)

/* Get number of characters in gap buffer */

#define GSIZE(hdr) (SEGSIZ-GGAPSZ(hdr))

/* Set position of gap */

static void gstgap (H *hdr, char *ptr, int ofst) {
	if (ofst > hdr->hole)
		mfwrd (ptr + hdr->hole, ptr + hdr->ehole, ofst - hdr->hole),
			vchanged (ptr);
	else if (ofst < hdr->hole)
		mbkwd (ptr + hdr->ehole - (hdr->hole - ofst), ptr + ofst,
		       hdr->hole - ofst), vchanged (ptr);
	hdr->ehole = ofst + hdr->ehole - hdr->hole;
	hdr->hole = ofst;
}

/* Insert a block */

static void ginsm (H *hdr, char *ptr, int ofst, char *blk, int size) {
	if (ofst != hdr->hole)
		gstgap (hdr, ptr, ofst);
	mcpy (ptr + hdr->hole, blk, size);
	hdr->hole += size;
	vchanged (ptr);
}

/* Read block */

static void grmem (H *hdr, char *ptr, int ofst, char *blk, int size) {
	if (ofst < hdr->hole)
		if (size > hdr->hole - ofst)
			mcpy (blk, ptr + ofst, hdr->hole - ofst),
				mcpy (blk + hdr->hole - ofst,
				      ptr + hdr->ehole,
				      size - (hdr->hole - ofst));
		else
			mcpy (blk, ptr + ofst, size);
	else
		mcpy (blk, ptr + ofst + hdr->ehole - hdr->hole, size);
}

/* Header allocation */

static H nhdrs = { {&nhdrs, &nhdrs} };
static H ohdrs = { {&ohdrs, &ohdrs} };

static H *halloc (void) {
	H *h;
	if (qempty (H, link, &ohdrs))
	  {
		  h = (H *) alitem (&nhdrs, sizeof (H));
		  h->seg = my_valloc (vmem, (long) SEGSIZ);
	  }
	else
		h = deque (H, link, ohdrs.link.next);
	h->hole = 0;
	h->ehole = SEGSIZ;
	h->nlines = 0;
	izque (H, link, h);
	return h;
}

static void hfree (H *h) {
	enquef (H, link, &ohdrs, h);
}

static void hfreechn (H *h) {
	splicef (H, link, &ohdrs, h);
}

/* Pointer allocation */

static P frptrs = { {&frptrs, &frptrs} };

static P *palloc (void) {
	return alitem (&frptrs, sizeof (P));
}

static void pfree (P *p) {
	enquef (P, link, &frptrs, p);
}

/* Doubly linked list of buffers and free buffer structures */

static B bufs = { {&bufs, &bufs} };
static B frebufs = { {&frebufs, &frebufs} };

B *bnext (void) {
	B *b;
	do
	  {
		  b = bufs.link.prev;
		  deque (B, link, &bufs);
		  enqueb (B, link, b, &bufs);
	  }
	while (b->internal);
	return b;
}

B *bprev (void) {
	B *b;
	do
	  {
		  b = bufs.link.next;
		  deque (B, link, &bufs);
		  enquef (B, link, b, &bufs);
	  }
	while (b->internal);
	return b;
}

/* Make a buffer out of a chain */

static B *bmkchn (H *chn, B *prop, long amnt, long nlines) {
	B *b = alitem (&frebufs, sizeof (B));
	b->undo = undomk (b);
	if (prop)
		b->o = prop->o;
	else
		b->o = pdefault;
	mset (b->marks, 0, sizeof (b->marks));
	b->rdonly = 0;
	b->orphan = 0;
	b->oldcur = 0;
	b->oldtop = 0;
	b->backup = 1;
	b->internal = 1;
	b->changed = 0;
	b->count = 1;
	b->name = 0;
	b->er = -3;
	b->bof = palloc ();
	izque (P, link, b->bof);
	b->bof->end = 0;
	b->bof->b = b;
	b->bof->owner = 0;
	b->bof->hdr = chn;
	b->bof->ptr = vlock (vmem, b->bof->hdr->seg);
	b->bof->ofst = 0;
	b->bof->byte = 0;
	b->bof->line = 0;
	b->bof->col = 0;
	b->bof->xcol = 0;
	b->bof->valcol = 1;
	b->eof = pdup (b->bof);
	b->eof->end = 1;
	vunlock (b->eof->ptr);
	b->eof->hdr = chn->link.prev;
	b->eof->ptr = vlock (vmem, b->eof->hdr->seg);
	b->eof->ofst = GSIZE (b->eof->hdr);
	b->eof->byte = amnt;
	b->eof->line = nlines;
	b->eof->valcol = 0;
	enquef (B, link, &bufs, b);
	pcoalesce (b->bof);
	pcoalesce (b->eof);
	return b;
}

/* Create an empty buffer */

B *bmk (B *prop) {
	return bmkchn (halloc (), prop, 0L, 0L);
}

/* Eliminate a buffer */

extern B *errbuf;

void brm (B *b) {
	if (b && !--b->count)
	  {
		  if (b->changed)
			  abrerr (b->name);
		  if (b == errbuf)
			  errbuf = 0;
		  if (b->undo)
			  undorm (b->undo);
		  hfreechn (b->eof->hdr);
		  while (!qempty (P, link, b->bof))
			  prm (b->bof->link.next);
		  prm (b->bof);
		  if (b->name)
			  free (b->name);
		  demote (B, link, &frebufs, b);
	  }
}

P *poffline (P *p) {
	if (p->ptr)
	  {
		  vunlock (p->ptr);
		  p->ptr = 0;
	  }
	return p;
}

P *ponline (P *p) {
	if (!p->ptr)
		p->ptr = vlock (vmem, p->hdr->seg);
	return p;
}

B *boffline (B *b) {
	P *p = b->bof;
	do
		poffline (p);
	while ((p = p->link.next) != b->bof);
	return b;
}

B *bonline (B *b) {
	P *p = b->bof;
	do
		ponline (p);
	while ((p = p->link.next) != b->bof);
	return b;
}

P *pdup (P *p) {
	P *n = palloc ();
	n->end = 0;
	n->ptr = 0;
	n->owner = 0;
	enquef (P, link, p, n);
	return pset (n, p);
}

P *pdupown (P *p, P **o) {
	P *n = palloc ();
	n->end = 0;
	n->ptr = 0;
	n->owner = o;
	enquef (P, link, p, n);
	pset (n, p);
	if (*o)
		prm (*o);
	*o = n;
	return n;
}

void prm (P *p) {
	if (!p)
		return;
	if (p->owner)
		*p->owner = 0;
	if (p->ptr)
		vunlock (p->ptr);
	pfree (deque (P, link, p));
}

P *pset (P *n, P *p) {
	if (n != p)
	  {
		  n->b = p->b;
		  n->ofst = p->ofst;
		  n->hdr = p->hdr;
		  if (n->ptr)
			  vunlock (n->ptr);
		  if (p->ptr)
		    {
			    n->ptr = p->ptr;
			    vupcount (n->ptr);
		    }
		  else
			  n->ptr = vlock (vmem, n->hdr->seg);
		  n->byte = p->byte;
		  n->line = p->line;
		  n->col = p->col;
		  n->valcol = p->valcol;
	  }
	return n;
}

P *p_goto_bof (P *p) {
	return pset (p, p->b->bof);
}

P *p_goto_eof (P *p) {
	return pset (p, p->b->eof);
}

int pisbof (P *p) {
	return p->hdr == p->b->bof->hdr && !p->ofst;
}

int piseof (P *p) {
	return p->ofst == GSIZE (p->hdr);
}

int piseol (P *p) {
	int c;
	if (piseof (p))
		return 1;
	c = brc (p);
	if (c == '\n')
		return 1;
	if (p->b->o.crlf)
		if (c == '\r')
		  {
			  P *q = pdup (p);
			  pfwrd (q, 1L);
			  if (pgetc (q) == '\n')
			    {
				    prm (q);
				    return 1;
			    }
			  else
				  prm (q);
		  }
	return 0;
}

int pisbol (P *p) {
	char c;
	if (pisbof (p))
		return 1;
	c = prgetc (p);
	pgetc (p);
	return c == '\n';
}

int pisbow (P *p) {
	P *q = pdup (p);
	int c = brc (p);
	int d = prgetc (q);
	prm (q);
	if (isalnum_ (c) && !isalnum_ (d))
		return 1;
	else
		return 0;
}

int piseow (P *p) {
	P *q = pdup (p);
	int d = brc (q);
	int c = prgetc (q);
	prm (q);
	if (isalnum_ (c) && !isalnum_ (d))
		return 1;
	else
		return 0;
}

int pisblank (P *p) {
	P *q = pdup (p);

	p_goto_bol (q);
	while (isblank (brc (q))) {
		pgetc (q);
	}
	if (piseol (q)) {
		prm (q);
		return 1;
	} else {
		prm (q);
		return 0;
	}
}

long pisindent (P *p) {
	P *q = pdup (p);
	long col;
	p_goto_bol (q);
	while (isblank (brc (q)))
		pgetc (q);
	col = q->col;
	prm (q);
	return col;
}

int pnext (P *p) {
	if (p->hdr == p->b->eof->hdr)
	  {
		  p->ofst = GSIZE (p->hdr);
		  return 0;
	  }
	p->hdr = p->hdr->link.next;
	p->ofst = 0;
	vunlock (p->ptr);
	p->ptr = vlock (vmem, p->hdr->seg);
	return 1;
}

int pprev (P *p) {
	if (p->hdr == p->b->bof->hdr)
	  {
		  p->ofst = 0;
		  return 0;
	  }
	p->hdr = p->hdr->link.prev;
	p->ofst = GSIZE (p->hdr);
	vunlock (p->ptr);
	p->ptr = vlock (vmem, p->hdr->seg);
	return 1;
}

int pgetc (P *p) {
	unsigned char c;

	if (p->ofst == GSIZE (p->hdr))
		return MAXINT;
	if (p->ofst >= p->hdr->hole)
		c = p->ptr[p->ofst + p->hdr->ehole - p->hdr->hole];
	else
		c = p->ptr[p->ofst];
	if (++p->ofst == GSIZE (p->hdr))
		pnext (p);
	++p->byte;

	if (c == '\n') {
		++(p->line);
		p->col = 0;
		p->valcol = 1;
	} else if (p->b->o.crlf && c == '\r') {
		if (brc (p) == '\n')
			return pgetc (p);
		else
			++p->col;
	} else {
		if (c == '\t')
			p->col += (p->b->o.tab) - (p->col) % (p->b->o.tab);
		else
			++(p->col);
	}
	return c;
}

P *pfwrd (P *p, long n) {
	if (!n)
		return p;
	p->valcol = 0;
	do
	  {
		  if (p->ofst == GSIZE (p->hdr))
			  do
			    {
				    if (!p->ofst)
					    p->byte += GSIZE (p->hdr), n -=
						    GSIZE (p->hdr), p->line +=
						    p->hdr->nlines;
				    if (!pnext (p))
					    return 0;
			    }
			  while (n > GSIZE (p->hdr));
		  if (p->ofst >= p->hdr->hole)
		    {
			    if (p->
				ptr[p->ofst + p->hdr->ehole - p->hdr->hole] ==
				'\n')
				    ++p->line;
		    }
		  else if (p->ptr[p->ofst] == '\n')
			  ++p->line;
		  ++p->byte;
		  ++p->ofst;
	  }
	while (--n);
	if (p->ofst == GSIZE (p->hdr))
		pnext (p);
	return p;
}

int prgetc1 (P *p) {
	unsigned char c;
	if (!p->ofst)
		if (!pprev (p))
			return MAXINT;
	--p->ofst;
	if (p->ofst >= p->hdr->hole)
		c = p->ptr[p->ofst + p->hdr->ehole - p->hdr->hole];
	else
		c = p->ptr[p->ofst];
	--p->byte;
	if (c == '\n')
		--p->line, p->valcol = 0;
	else
	  {
		  if (c == '\t')
			  p->valcol = 0;
		  --p->col;
	  }
	return c;
}

int prgetc (P *p) {
	int c = prgetc1 (p);
	if (p->b->o.crlf && c == '\n')
	  {
		  c = prgetc1 (p);
		  if (c == '\r')
			  return '\n';
		  if (c != MAXINT)
			  pgetc (p);
		  c = '\n';
	  }
	return c;
}

P *pbkwd (P *p, long n) {
	if (!n)
		return p;
	p->valcol = 0;
	do
	  {
		  if (!p->ofst)
			  do
			    {
				    if (p->ofst)
					    p->byte -= p->ofst, n -=
						    p->ofst, p->line -=
						    p->hdr->nlines;
				    if (!pprev (p))
					    return 0;
			    }
			  while (n > GSIZE (p->hdr));
		  --p->ofst;
		  --p->byte;
		  if (p->ofst >= p->hdr->hole)
		    {
			    if (p->
				ptr[p->ofst + p->hdr->ehole - p->hdr->hole] ==
				'\n')
				    --p->line;
		    }
		  else if (p->ptr[p->ofst] == '\n')
			  --p->line;
	  }
	while (--n);
	return p;
}

P *pgoto (P *p, long loc) {
	if (loc > p->byte)
		pfwrd (p, loc - p->byte);
	else if (loc < p->byte)
		pbkwd (p, p->byte - loc);
	return p;
}

P *pfcol (P *p) {
	H *hdr = p->hdr;
	int ofst = p->ofst;
	p_goto_bol (p);
	while (p->ofst != ofst || p->hdr != hdr)
		pgetc (p);
	return p;
}

P *p_goto_bol (P *p) {
	if (pprevl (p))
		pgetc (p);
	p->col = 0;
	p->valcol = 1;
	return p;
}

P *p_goto_eol (P *p) {
	if (p->b->o.crlf)
		while (!piseol (p))
			pgetc (p);
	else
		while (p->ofst != GSIZE (p->hdr))
		  {
			  unsigned char c;
			  if (p->ofst >= p->hdr->hole)
				  c =
					  p->ptr[p->ofst + p->hdr->ehole -
						 p->hdr->hole];
			  else
				  c = p->ptr[p->ofst];
			  if (c == '\n')
				  break;
			  else
			    {
				    ++p->byte;
				    ++p->ofst;
				    if (c == '\t')
					    p->col +=
						    p->b->o.tab -
						    p->col % p->b->o.tab;
				    else
					    ++p->col;
				    if (p->ofst == GSIZE (p->hdr))
					    pnext (p);
			    }
		  }
	return p;
}

P *pnextl (P *p) {
	char c;
	do
	  {
		  if (p->ofst == GSIZE (p->hdr))
			  do
			    {
				    p->byte += GSIZE (p->hdr) - p->ofst;
				    if (!pnext (p))
					    return 0;
			    }
			  while (!p->hdr->nlines);
		  if (p->ofst >= p->hdr->hole)
			  c = p->ptr[p->ofst + p->hdr->ehole - p->hdr->hole];
		  else
			  c = p->ptr[p->ofst];
		  ++p->byte;
		  ++p->ofst;
	  }
	while (c != '\n');
	++p->line;
	p->col = 0;
	p->valcol = 1;
	if (p->ofst == GSIZE (p->hdr))
		pnext (p);
	return p;
}

P *pprevl (P *p) {
	char c;
	p->valcol = 0;
	do
	  {
		  if (!p->ofst)
			  do
			    {
				    p->byte -= p->ofst;
				    if (!pprev (p))
					    return 0;
			    }
			  while (!p->hdr->nlines);
		  --p->ofst;
		  --p->byte;
		  if (p->ofst >= p->hdr->hole)
			  c = p->ptr[p->ofst + p->hdr->ehole - p->hdr->hole];
		  else
			  c = p->ptr[p->ofst];
	  }
	while (c != '\n');
	--p->line;
	if (p->b->o.crlf && c == '\n')
	  {
		  int k = prgetc1 (p);
		  if (k != '\r' && k != MAXINT)
			  pgetc (p);
	  }
	return p;
}

P *pline (P *p, long line) {
	if (line > p->b->eof->line)
	  {
		  pset (p, p->b->eof);
		  return p;
	  }
	if (line < labs (p->line - line))
		pset (p, p->b->bof);
	if (labs (p->b->eof->line - line) < labs (p->line - line))
		pset (p, p->b->eof);
	if (p->line == line)
	  {
		  p_goto_bol (p);
		  return p;
	  }
	while (line > p->line)
		pnextl (p);
	if (line < p->line)
	  {
		  while (line < p->line)
			  pprevl (p);
		  p_goto_bol (p);
	  }
	return p;
}

P *pcol (P *p, long goalcol) {
	p_goto_bol (p);
	do
	  {
		  unsigned char c;
		  int wid;
		  if (p->ofst == GSIZE (p->hdr))
			  break;
		  if (p->ofst >= p->hdr->hole)
			  c = p->ptr[p->ofst + p->hdr->ehole - p->hdr->hole];
		  else
			  c = p->ptr[p->ofst];
		  if (c == '\n')
			  break;
		  if (p->b->o.crlf && c == '\r' && piseol (p))
			  break;
		  if (c == '\t')
			  wid = p->b->o.tab - p->col % p->b->o.tab;
		  else
			  wid = 1;
		  if (p->col + wid > goalcol)
			  break;
		  if (++p->ofst == GSIZE (p->hdr))
			  pnext (p);
		  ++p->byte;
		  p->col += wid;
	  }
	while (p->col != goalcol);
	return p;
}

P *pcolwse (P *p, long goalcol) {
	int c;
	pcol (p, goalcol);
	do
		c = prgetc (p);
	while (c == ' ' || c == '\t');
	if (c != MAXINT)
		pgetc (p);
	return p;
}

P *pcoli (P *p, long goalcol) {
	p_goto_bol (p);
	while (p->col < goalcol)
	  {
		  unsigned char c;
		  if (p->ofst == GSIZE (p->hdr))
			  break;
		  if (p->ofst >= p->hdr->hole)
			  c = p->ptr[p->ofst + p->hdr->ehole - p->hdr->hole];
		  else
			  c = p->ptr[p->ofst];
		  if (c == '\n')
			  break;
#ifdef __MSDOS
		  if (c == '\r' && piseol (p))
			  break;
#endif
		  else if (c == '\t')
			  p->col += p->b->o.tab - p->col % p->b->o.tab;
		  else
			  ++p->col;
		  if (++p->ofst == GSIZE (p->hdr))
			  pnext (p);
		  ++p->byte;
	  }
	return p;
}

void pfill (P *p, long to, int usetabs) {
	piscol (p);
	if (usetabs)
		while (p->col < to)
			if (p->col + p->b->o.tab - p->col % p->b->o.tab <= to)
				binsc (p, '\t'), pgetc (p);
			else
				binsc (p, ' '), pgetc (p);
	else
		while (p->col < to)
			binsc (p, ' '), pgetc (p);
}

void pbackws (P *p) {
	int c;
	P *q = pdup (p);
	do
		c = prgetc (q);
	while (c == ' ' || c == '\t');
	if (c != MAXINT)
		pgetc (q);
	bdel (q, p);
	prm (q);
}

static char frgetc (P *p) {
	if (!p->ofst)
		pprev (p);
	--p->ofst;
	if (p->ofst >= p->hdr->hole)
		return p->ptr[p->ofst + p->hdr->ehole - p->hdr->hole];
	else
		return p->ptr[p->ofst];
}

static void ffwrd (P *p, int n) {
	while (n > GSIZE (p->hdr) - p->ofst)
	  {
		  n -= GSIZE (p->hdr) - p->ofst;
		  if (!pnext (p))
			  return;
	  }
	if ((p->ofst += n) == GSIZE (p->hdr))
		pnext (p);
}

static P *ffind (P *p, unsigned char *s, int len) {
	long amnt = p->b->eof->byte - p->byte;
	int x;
	unsigned char table[256], c;
	if (len > amnt)
		return 0;
	if (!len)
		return p;
	p->valcol = 0;
	mset (table, 255, 256);
	for (x = 0; x != len - 1; ++x)
		table[s[x]] = x;
	ffwrd (p, len);
	amnt -= len;
	x = len;
	do
		if ((c = frgetc (p)) != s[--x])
		  {
			  if (table[c] == 255)
				  ffwrd (p, len + 1), amnt -= x + 1;
			  else if (x <= table[c])
				  ffwrd (p, len - x + 1), --amnt;
			  else
				  ffwrd (p, len - table[c]), amnt -=
					  x - table[c];
			  if (amnt < 0)
				  return 0;
			  else
				  x = len;
		  }
	while (x);
	return p;
}

static P *fifind (P *p, unsigned char *s, int len) {
	long amnt = p->b->eof->byte - p->byte;
	int x;
	unsigned char table[256], c;
	if (len > amnt)
		return 0;
	if (!len)
		return p;
	p->valcol = 0;
	mset (table, 255, 256);
	for (x = 0; x != len - 1; ++x)
		table[s[x]] = x;
	ffwrd (p, len);
	amnt -= len;
	x = len;
	do
		if ((c = toupper (frgetc (p))) != s[--x])
		  {
			  if (table[c] == 255)
				  ffwrd (p, len + 1), amnt -= x + 1;
			  else if (x <= table[c])
				  ffwrd (p, len - x + 1), --amnt;
			  else
				  ffwrd (p, len - table[c]), amnt -=
					  x - table[c];
			  if (amnt < 0)
				  return 0;
			  else
				  x = len;
		  }
	while (x);
	return p;
}

static P *getto (P *p, P *q) {
	while (p->hdr != q->hdr || p->ofst != q->ofst)
	  {
		  if (p->ofst >= p->hdr->hole)
		    {
			    if (p->
				ptr[p->ofst + p->hdr->ehole - p->hdr->hole] ==
				'\n')
				    ++p->line;
		    }
		  else if (p->ptr[p->ofst] == '\n')
			  ++p->line;
		  ++p->byte;
		  ++p->ofst;
		  if (p->ofst == GSIZE (p->hdr))
			  pnext (p);
		  while (!p->ofst && p->hdr != q->hdr)
		    {
			    p->byte += GSIZE (p->hdr), p->line +=
				    p->hdr->nlines;
			    pnext (p);
		    }
	  }
	return p;
}

P *pfind (P *p, char *s, int len) {
	P *q = pdup (p);
	if (ffind (q, s, len))
	  {
		  getto (p, q);
		  prm (q);
		  return p;
	  }
	else
	  {
		  prm (q);
		  return 0;
	  }
}

P *pifind (P *p, char *s, int len) {
	P *q = pdup (p);
	if (fifind (q, s, len))
	  {
		  getto (p, q);
		  prm (q);
		  return p;
	  }
	else
	  {
		  prm (q);
		  return 0;
	  }
}

static void fbkwd (P *p, int n) {
	while (n > p->ofst)
	  {
		  n -= p->ofst;
		  if (!pprev (p))
			  return;
	  }
	if (p->ofst >= n)
		p->ofst -= n;
	else
		p->ofst = 0;
}

static int fpgetc (P *p) {
	char c;
	if (p->ofst == GSIZE (p->hdr))
		return MAXINT;
	if (p->ofst >= p->hdr->hole)
		c = p->ptr[p->ofst + p->hdr->ehole - p->hdr->hole];
	else
		c = p->ptr[p->ofst];
	if (++p->ofst == GSIZE (p->hdr))
		pnext (p);
	return c;
}

static P *frfind (P *p, unsigned char *s, int len) {
	long amnt = p->byte;
	int x;
	unsigned char table[256], c;
	if (len > p->b->eof->byte - p->byte)
	  {
		  x = len - (p->b->eof->byte - p->byte);
		  if (amnt < x)
			  return 0;
		  amnt -= x;
		  fbkwd (p, x);
	  }
	if (!len)
		return p;
	p->valcol = 0;
	mset (table, 255, 256);
	for (x = len; --x; table[s[x]] = len - x - 1);
	x = 0;
	do
		if ((c = fpgetc (p)) != s[x++])
		  {
			  if (table[c] == 255)
				  fbkwd (p, len + 1), amnt -= len - x + 1;
			  else if (len - table[c] <= x)
				  fbkwd (p, x + 1), --amnt;
			  else
				  fbkwd (p, len - table[c]), amnt -=
					  len - table[c] - x;
			  if (amnt < 0)
				  return 0;
			  else
				  x = 0;
		  }
	while (x != len);
	fbkwd (p, len);
	return p;
}

static P *frifind (P *p, unsigned char *s, int len) {
	long amnt = p->byte;
	int x;
	unsigned char table[256], c;
	if (len > p->b->eof->byte - p->byte)
	  {
		  x = len - (p->b->eof->byte - p->byte);
		  if (amnt < x)
			  return 0;
		  amnt -= x;
		  fbkwd (p, x);
	  }
	if (!len)
		return p;
	p->valcol = 0;
	mset (table, 255, 256);
	for (x = len; --x; table[s[x]] = len - x - 1);
	x = 0;
	do
		if ((c = toupper (fpgetc (p))) != s[x++])
		  {
			  if (table[c] == 255)
				  fbkwd (p, len + 1), amnt -= len - x + 1;
			  else if (len - table[c] <= x)
				  fbkwd (p, x + 1), --amnt;
			  else
				  fbkwd (p, len - table[c]), amnt -=
					  len - table[c] - x;
			  if (amnt < 0)
				  return 0;
			  else
				  x = 0;
		  }
	while (x != len);
	fbkwd (p, len);
	return p;
}

static P *rgetto (P *p, P *q) {
	while (p->hdr != q->hdr || p->ofst != q->ofst)
	  {
		  if (!p->ofst)
			  do
			    {
				    if (p->ofst)
					    p->byte -= p->ofst, p->line -=
						    p->hdr->nlines;
				    pprev (p);
			    }
			  while (p->hdr != q->hdr);
		  --p->ofst;
		  --p->byte;
		  if (p->ofst >= p->hdr->hole)
		    {
			    if (p->
				ptr[p->ofst + p->hdr->ehole - p->hdr->hole] ==
				'\n')
				    --p->line;
		    }
		  else if (p->ptr[p->ofst] == '\n')
			  --p->line;
	  }
	return p;
}

P *prfind (P *p, char *s, int len) {
	P *q = pdup (p);
	if (frfind (q, s, len))
	  {
		  rgetto (p, q);
		  prm (q);
		  return p;
	  }
	else
	  {
		  prm (q);
		  return 0;
	  }
}

P *prifind (P *p, char *s, int len) {
	P *q = pdup (p);
	if (frifind (q, s, len))
	  {
		  rgetto (p, q);
		  prm (q);
		  return p;
	  }
	else
	  {
		  prm (q);
		  return 0;
	  }
}

B *bcpy (P *from,P *to) {
	H anchor, *l;
	char *ptr;
	P *q;

	if (from->byte >= to->byte)
		return bmk (from->b);

	q = pdup (from);
	izque (H, link, &anchor);

	if (q->hdr == to->hdr)
	  {
		  l = halloc ();
		  ptr = vlock (vmem, l->seg);
		  if (q->ofst != q->hdr->hole)
			  gstgap (q->hdr, q->ptr, q->ofst);
		  l->nlines = mcnt (q->ptr + q->hdr->ehole, '\n', l->hole =
				    to->ofst - q->ofst);
		  mcpy (ptr, q->ptr + q->hdr->ehole, l->hole);
		  vchanged (ptr);
		  vunlock (ptr);
		  enqueb (H, link, &anchor, l);
	  }
	else
	  {
		  l = halloc ();
		  ptr = vlock (vmem, l->seg);
		  if (q->ofst != q->hdr->hole)
			  gstgap (q->hdr, q->ptr, q->ofst);
		  l->nlines = mcnt (q->ptr + q->hdr->ehole, '\n', l->hole =
				    SEGSIZ - q->hdr->ehole);
		  mcpy (ptr, q->ptr + q->hdr->ehole, l->hole);
		  vchanged (ptr);
		  vunlock (ptr);
		  enqueb (H, link, &anchor, l);
		  pnext (q);
		  while (q->hdr != to->hdr)
		    {
			    l = halloc ();
			    ptr = vlock (vmem, l->seg);
			    l->nlines = q->hdr->nlines;
			    mcpy (ptr, q->ptr, q->hdr->hole);
			    mcpy (ptr + q->hdr->hole, q->ptr + q->hdr->ehole,
				  SEGSIZ - q->hdr->ehole);
			    l->hole = GSIZE (q->hdr);
			    vchanged (ptr);
			    vunlock (ptr);
			    enqueb (H, link, &anchor, l);
			    pnext (q);
		    }
		  if (to->ofst)
		    {
			    l = halloc ();
			    ptr = vlock (vmem, l->seg);
			    if (to->ofst != to->hdr->hole)
				    gstgap (to->hdr, to->ptr, to->ofst);
			    l->nlines = mcnt (to->ptr, '\n', to->ofst);
			    mcpy (ptr, to->ptr, l->hole = to->ofst);
			    vchanged (ptr);
			    vunlock (ptr);
			    enqueb (H, link, &anchor, l);
		    }
	  }

	l = anchor.link.next;
	deque (H, link, &anchor);
	prm (q);

	return bmkchn (l, from->b, to->byte - from->byte,
		       to->line - from->line);
}

/* Coalesce small blocks into a single larger one */

void pcoalesce (P *p) {
	if (p->hdr != p->b->eof->hdr &&
	    GSIZE (p->hdr) + GSIZE (p->hdr->link.next) <= SEGSIZ - SEGSIZ / 4)
	  {
		  H *hdr = p->hdr->link.next;
		  char *ptr = vlock (vmem, hdr->seg);
		  int osize = GSIZE (p->hdr);
		  int size = GSIZE (hdr);
		  P *q;
		  gstgap (hdr, ptr, size);
		  ginsm (p->hdr, p->ptr, GSIZE (p->hdr), ptr, size);
		  p->hdr->nlines += hdr->nlines;
		  vunlock (ptr);
		  hfree (deque (H, link, hdr));
		  for (q = p->link.next; q != p; q = q->link.next)
			  if (q->hdr == hdr)
			    {
				    q->hdr = p->hdr;
				    if (q->ptr)
				      {
					      vunlock (q->ptr);
					      q->ptr =
						      vlock (vmem,
							     q->hdr->seg);
				      }
				    q->ofst += osize;
			    }
	  }
	if (p->hdr != p->b->bof->hdr &&
	    GSIZE (p->hdr) + GSIZE (p->hdr->link.prev) <= SEGSIZ - SEGSIZ / 4)
	  {
		  H *hdr = p->hdr->link.prev;
		  char *ptr = vlock (vmem, hdr->seg);
		  int size = GSIZE (hdr);
		  P *q;
		  gstgap (hdr, ptr, size);
		  ginsm (p->hdr, p->ptr, 0, ptr, size);
		  p->hdr->nlines += hdr->nlines;
		  vunlock (ptr);
		  hfree (deque (H, link, hdr));
		  p->ofst += size;
		  for (q = p->link.next; q != p; q = q->link.next)
			  if (q->hdr == hdr)
			    {
				    q->hdr = p->hdr;
				    if (q->ptr)
					    vunlock (q->ptr);
				    q->ptr = vlock (vmem, q->hdr->seg);
			    }
			  else if (q->hdr == p->hdr)
				  q->ofst += size;
	  }
}

/* Delete the text between two pointers from a buffer and return it in a new
 * buffer.
 *
 * This routine calls these functions:
 *  gstgap	- to position gaps
 *  halloc	- to allocate new header/segment pairs
 *  vlock	- virtual memory routines
 *  vunlock
 *  vchanged
 *  vupcount
 *  mcpy	- to copy deleted text
 *  mcnt	- to count NLs
 *  snip	- queue routines
 *  enqueb
 *  splicef
 *  scrdel	- to tell screen update to scroll when NLs are deleted
 *  bmkchn	- to make a buffer out of a chain
 */

/* This is only to be used for bdel() */
static B *bcut (P *from, P *to) {
	H *h,			/* The deleted text */
	 *i;
	char *ptr;
	P *p;
	long nlines;		/* No. EOLs to delete */
	long amnt;		/* No. bytes to delete */
	int toamnt;		/* Amount to delete from segment in 'to' */
	int bofmove = 0;	/* Set if bof got deleted */

	if (!(amnt = to->byte - from->byte))
		return 0;	/* ...nothing to delete */

	nlines = to->line - from->line;

	if (from->hdr == to->hdr)
	  {			/* Delete is within a single segment */
		  /* Move gap to deletion point */
		  if (from->ofst != from->hdr->hole)
			  gstgap (from->hdr, from->ptr, from->ofst);

		  /* Store the deleted text */
		  h = halloc ();
		  ptr = vlock (vmem, h->seg);
		  mcpy (ptr, from->ptr + from->hdr->ehole, (int) amnt);
		  h->hole = amnt;
		  h->nlines = nlines;
		  vchanged (ptr);
		  vunlock (ptr);

		  /* Delete */
		  from->hdr->ehole += amnt;
		  from->hdr->nlines -= nlines;

		  toamnt = amnt;
	  }
	else
	  {			/* Delete crosses segments */
		  H *a;
		  if (toamnt = to->ofst)
		    {
			    /* Delete beginning of to */
			    /* Move gap to deletion point */
			    /* To could be deleted if it's at the end of the file */
			    if (to->ofst != to->hdr->hole)
				    gstgap (to->hdr, to->ptr, to->ofst);

			    /* Save deleted text */
			    i = halloc ();
			    ptr = vlock (vmem, i->seg);
			    mcpy (ptr, to->ptr, to->hdr->hole);
			    i->hole = to->hdr->hole;
			    i->nlines = mcnt (to->ptr, '\n', to->hdr->hole);
			    vchanged (ptr);
			    vunlock (ptr);

			    /* Delete */
			    to->hdr->nlines -= i->nlines;
			    to->hdr->hole = 0;
		    }
		  else
			  i = 0;

		  /* Delete end of from */
		  if (!from->ofst)
		    {
			    /* ..unless from needs to be deleted too */
			    a = from->hdr->link.prev, h = 0;
			    if (a == from->b->eof->hdr)
				    bofmove = 1;
		    }
		  else
		    {
			    a = from->hdr;
			    /* Move gap to deletion point */
			    if (from->ofst != from->hdr->hole)
				    gstgap (from->hdr, from->ptr, from->ofst);

			    /* Save deleted text */
			    h = halloc ();
			    ptr = vlock (vmem, h->seg);
			    mcpy (ptr, from->ptr + from->hdr->ehole,
				  SEGSIZ - from->hdr->ehole);
			    h->hole = SEGSIZ - from->hdr->ehole;
			    h->nlines = mcnt (ptr, '\n', h->hole);
			    vchanged (ptr);
			    vunlock (ptr);

			    /* Delete */
			    from->hdr->nlines -= h->nlines;
			    from->hdr->ehole = SEGSIZ;
		    }

		  /* Make from point to header/segment of to */
		  from->hdr = to->hdr;
		  vunlock (from->ptr);
		  from->ptr = to->ptr;
		  vupcount (to->ptr);
		  from->ofst = 0;

		  /* Delete headers/segments between a and to->hdr (if there are any) */
		  if (a->link.next != to->hdr)
			  if (!h)
			    {
				    h =
					    snip (H, link, a->link.next,
						  to->hdr->link.prev);
				    if (i)
					    enqueb (H, link, h, i);
			    }
			  else
			    {
				    splicef (H, link, h,
					     snip (H, link, a->link.next,
						   to->hdr->link.prev));
				    if (i)
					    enqueb (H, link, h, i);
			    }
		  else if (!h)
			  h = i;
		  else if (i)
			  enqueb (H, link, h, i);
	  }

	/* If to is empty, then it must have been at the end of the file.  If
	   the file did not become empty, delete to */
	if (!GSIZE (to->hdr) && from->byte)
	  {
		  H *ph = from->hdr->link.prev;
		  hfree (deque (H, link, from->hdr));
		  vunlock (from->ptr);
		  from->hdr = ph;
		  from->ptr = vlock (vmem, from->hdr->seg);
		  from->ofst = GSIZE (ph);
		  vunlock (from->b->eof->ptr);
		  from->b->eof->ptr = from->ptr;
		  vupcount (from->ptr);
		  from->b->eof->hdr = from->hdr;
		  from->b->eof->ofst = from->ofst;
	  }

	/* The deletion is now done */

	/* Scroll if necessary */

	if (bofmove)
		pset (from->b->bof, from);
	if (nlines && !pisbol (from))
	  {
		  scrdel (from->b, from->line, nlines, 1);
/* too many arguments so I remove last one
		  delerr (from->b->name, from->line, nlines, 0);
*/
		  delerr (from->b->name, from->line, nlines);
	        }
	else
	  {
		  scrdel (from->b, from->line, nlines, 0);
/* too many arguments so I remove last one
		  delerr (from->b->name, from->line, nlines, 1);
*/
		delerr (from->b->name, from->line, nlines);

	     }

	/* Fix pointers */

	for (p = from->link.next; p != from; p = p->link.next)
		if (p->line == from->line && p->byte > from->byte)
			p->valcol = 0;
	for (p = from->link.next; p != from; p = p->link.next)
		if (p->byte >= from->byte)
			if (p->byte <= from->byte + amnt) {
				if (p->ptr) {
					pset (p, from);
				} else {
					poffline (pset (p, from));
				}
			} else {
				  if (p->hdr == to->hdr)
					  p->ofst -= toamnt;
				  p->byte -= amnt;
				  p->line -= nlines;
			}

	pcoalesce (from);

	/* Make buffer out of deleted text and return it */

	return bmkchn (h, from->b, amnt, nlines);
}

void bdel (P *from, P *to) {
	if (to->byte - from->byte)
	  {
		  B *b = bcut (from, to);
		  if (from->b->undo)
			  undodel (from->b->undo, from->byte, b);
		  else
			  brm (b);
		  from->b->changed = 1;
	  }
}

/* Split a block at p's ofst */
/* p is placed in the new block such that it points to the same text but with
 * p->ofst==0
 */

static void bsplit (P *p) {
	if (p->ofst)
	  {
		  H *hdr;
		  char *ptr;
		  P *pp;

		  hdr = halloc ();
		  ptr = vlock (vmem, hdr->seg);

		  if (p->ofst != p->hdr->hole)
			  gstgap (p->hdr, p->ptr, p->ofst);
		  mcpy (ptr, p->ptr + p->hdr->ehole, SEGSIZ - p->hdr->ehole);
		  hdr->hole = SEGSIZ - p->hdr->ehole;
		  hdr->nlines = mcnt (ptr, '\n', hdr->hole);
		  p->hdr->nlines -= hdr->nlines;
		  vchanged (ptr);
		  p->hdr->ehole = SEGSIZ;

		  enquef (H, link, p->hdr, hdr);

		  vunlock (p->ptr);

		  for (pp = p->link.next; pp != p; pp = pp->link.next)
			  if (pp->hdr == p->hdr && pp->ofst >= p->ofst)
			    {
				    pp->hdr = hdr;
				    if (pp->ptr)
				      {
					      vunlock (pp->ptr);
					      pp->ptr = ptr;
					      vupcount (ptr);
				      }
				    pp->ofst -= p->ofst;
			    }

		  p->ptr = ptr;
		  p->hdr = hdr;
		  p->ofst = 0;
	  }
}

/* Make a chain out of a block of memory */
/* The block must not be empty */

static H *bldchn (char *blk, int size, long *nlines) {
	H anchor, *l;
	*nlines = 0;
	izque (H, link, &anchor);
	do
	  {
		  char *ptr;
		  int amnt;
		  ptr = vlock (vmem, (l = halloc ())->seg);
		  if (size > SEGSIZ)
			  amnt = SEGSIZ;
		  else
			  amnt = size;
		  mcpy (ptr, blk, amnt);
		  l->hole = amnt;
		  l->ehole = SEGSIZ;
		  (*nlines) += (l->nlines = mcnt (ptr, '\n', amnt));
		  vchanged (ptr);
		  vunlock (ptr);
		  enqueb (H, link, &anchor, l);
		  blk += amnt;
		  size -= amnt;
	  }
	while (size);
	l = anchor.link.next;
	deque (H, link, &anchor);
	return l;
}

/* Insert a chain into a buffer */
/* This does not update pointers */

static void inschn (P *p, H *a) {
	if (!p->b->eof->byte)
	  {			/* P's buffer is empty: replace the empty segment in p with a */
		  hfree (p->hdr);
		  p->hdr = a;
		  vunlock (p->ptr);
		  p->ptr = vlock (vmem, a->seg);
		  pset (p->b->bof, p);

		  p->b->eof->hdr = a->link.prev;
		  vunlock (p->b->eof->ptr);
		  p->b->eof->ptr = vlock (vmem, p->b->eof->hdr->seg);
		  p->b->eof->ofst = GSIZE (p->b->eof->hdr);
	  }
	else if (piseof (p))
	  {			/* We're at the end of the file: append a to the file */
		  p->b->eof->hdr = a->link.prev;
		  spliceb (H, link, p->b->bof->hdr, a);
		  vunlock (p->b->eof->ptr);
		  p->b->eof->ptr = vlock (vmem, p->b->eof->hdr->seg);
		  p->b->eof->ofst = GSIZE (p->b->eof->hdr);
		  p->hdr = a;
		  vunlock (p->ptr);
		  p->ptr = vlock (vmem, p->hdr->seg);
		  p->ofst = 0;
	  }
	else if (pisbof (p))
	  {			/* We're at the beginning of the file: insert chain and set bof pointer */
		  p->hdr = spliceb (H, link, p->hdr, a);
		  vunlock (p->ptr);
		  p->ptr = vlock (vmem, a->seg);
		  pset (p->b->bof, p);
	  }
	else
	  {			/* We're in the middle of the file: split and insert */
		  bsplit (p);
		  p->hdr = spliceb (H, link, p->hdr, a);
		  vunlock (p->ptr);
		  p->ptr = vlock (vmem, a->seg);
	  }
}

static void fixupins (P *p, long amnt, long nlines, H *hdr, int hdramnt) {
	P *pp;
	if (nlines && !pisbol (p))
		scrins (p->b, p->line, nlines, 1);
	else
		scrins (p->b, p->line, nlines, 0);
/* too few arguments so I added one but because I don't know is last 
	argument so first try is 0 :)
	inserr (p->b->name, p->line, nlines);
*/
	inserr (p->b->name, p->line, nlines, 0);

	for (pp = p->link.next; pp != p; pp = pp->link.next)
		if (pp->line == p->line &&
		    (pp->byte > p->byte || pp->end && pp->byte == p->byte))
			pp->valcol = 0;
	for (pp = p->link.next; pp != p; pp = pp->link.next)
		if (pp->byte == p->byte && !pp->end)
			if (pp->ptr)
				pset (pp, p);
			else
				poffline (pset (pp, p));
		else if (pp->byte > p->byte || pp->end && pp->byte == p->byte)
		  {
			  pp->byte += amnt;
			  pp->line += nlines;
			  if (pp->hdr == hdr)
				  pp->ofst += hdramnt;
		  }
	if (p->b->undo)
		undoins (p->b->undo, p, amnt);
	p->b->changed = 1;
}

/* Insert a buffer at pointer position */
/* The buffer goes away */

P *binsb (P *p, B *b) {
	if (b->eof->byte)
	  {
		  P *q = pdup (p);
		  inschn (q, b->bof->hdr);
		  b->eof->hdr = halloc ();
		  fixupins (q, b->eof->byte, b->eof->line, NULL, 0);
		  pcoalesce (q);
		  prm (q);
	  }
	brm (b);
	return p;
}

P *binsm (P *p, char *blk, int amnt) {
	long nlines;
	H *h = 0;
	int hdramnt;
	P *q;
	if (!amnt)
		return p;
	q = pdup (p);
	if (amnt <= GGAPSZ (q->hdr))
	  {
		  h = q->hdr;
		  hdramnt = amnt;
		  ginsm (q->hdr, q->ptr, q->ofst, blk, amnt);
		  q->hdr->nlines += (nlines = mcnt (blk, '\n', amnt));
	  }
	else if (!q->ofst && q->hdr != q->b->bof->hdr
		 && amnt <= GGAPSZ (q->hdr->link.prev))
	  {
		  pprev (q);
		  ginsm (q->hdr, q->ptr, q->ofst, blk, amnt);
		  q->hdr->nlines += (nlines = mcnt (blk, '\n', amnt));
	  }
	else
	  {
		  H *a = bldchn (blk, amnt, &nlines);
		  inschn (q, a);
	  }
	fixupins (q, (long) amnt, nlines, h, hdramnt);
	pcoalesce (q);
	prm (q);
	return p;
}

P *binsc (p, c)
     P *p;
     char c;
{
	if (p->b->o.crlf && c == '\n')
		return binsm (p, "\r\n", 2);
	else
		return binsm (p, &c, 1);
}

P *
binss (p, s)
     P *p;
     char *s;
{
	return binsm (p, s, strlen (s));
}

/* Read 'size' bytes from file or stream.  Stops and returns amnt. read
 * when requested size has been read or when end of file condition occurs.
 * Returns with -2 in error for read error or 0 in error for success.
 */

static int
bkread (fi, buff, size)
     char *buff;
{
	int a, b;
	if (!size)
	  {
		  error = 0;
		  return 0;
	  }
	for (a = b = 0;
	     (a < size) && ((b = jread (fi, buff + a, size - a)) > 0);
	     a += b);
	if (b < 0)
		error = -2;
	else
		error = 0;
	return a;
}

/* Read up to 'max' bytes from a file into a buffer */
/* Returns with 0 in error or -2 in error for read error */

B *
bread (fi, max)
     long max;
{
	H anchor, *l;
	long lines = 0, total = 0;
	int amnt;
	char *seg;
	izque (H, link, &anchor);
	error = 0;
	while (seg = vlock (vmem, (l = halloc ())->seg),
	       !error
	       && (amnt =
		   bkread (fi, seg, max >= SEGSIZ ? SEGSIZ : (int) max)))
	  {
		  total += amnt;
		  max -= amnt;
		  l->hole = amnt;
		  lines += (l->nlines = mcnt (seg, '\n', amnt));
		  vchanged (seg);
		  vunlock (seg);
		  enqueb (H, link, &anchor, l);
	  }
	hfree (l);
	vunlock (seg);
	if (!total)
		return bmk (NULL);
	l = anchor.link.next;
	deque (H, link, &anchor);
	return bmkchn (l, NULL, total, lines);
}

/* Parse file name.
 *
 * Removes ',xxx,yyy' from end of name and puts their value into skip and amnt
 * Replaces ~user/ with directory of given user
 * Replaces ~/ with $HOME
 *
 * Returns new variable length string.
 */

char *
parsens (s, skip, amnt)
     char *s;
     long *skip, *amnt;
{
	char *n = vsncpy (NULL, 0, sz (s));
	int x;
	*skip = 0;
	*amnt = MAXLONG;
	for (x = sLEN (n) - 1;
	     x > 0 && (n[x] >= '0' && n[x] <= '9' || n[x] == 'x'
		       || n[x] == 'X'); --x);
	if (n[x] == ',')
	  {
		  n[x] = 0;
		  if (n[x + 1] == 'x' || n[x + 1] == 'X')
			  sscanf (n + x + 2, "%lx", skip);
		  else if (n[x + 1] == '0'
			   && (n[x + 2] == 'x' || n[x + 2] == 'X'))
			  sscanf (n + x + 3, "%lx", skip);
		  else if (n[x + 1] == '0')
			  sscanf (n + x + 1, "%lo", skip);
		  else
			  sscanf (n + x + 1, "%ld", skip);
		  for (--x;
		       x > 0 && (n[x] >= '0' && n[x] <= '9' || n[x] == 'x'
				 || n[x] == 'X'); --x);
		  if (n[x] == ',')
		    {
			    n[x] = 0;
			    *amnt = *skip;
			    if (n[x + 1] == 'x' || n[x + 1] == 'X')
				    sscanf (n + x + 2, "%lx", skip);
			    else if (n[x + 1] == '0'
				     && (n[x + 2] == 'x' || n[x + 2] == 'X'))
				    sscanf (n + x + 3, "%lx", skip);
			    else if (n[x + 1] == '0')
				    sscanf (n + x + 1, "%lo", skip);
			    else
				    sscanf (n + x + 1, "%ld", skip);
		    }
	  }
#ifndef __MSDOS__
	if (n[0] == '~') {
		for (x = 1; n[x] && n[x] != '/'; ++x);
		if (n[x] == '/') {
			if (x == 1) {
				char *z;
				s = getenv ("HOME");
				z = vsncpy (NULL, 0, sz (s));
				z = vsncpy (z, sLEN (z), sz (n + x));
				vsrm (n);
				n = z;
			} else {
				struct passwd *passwd;
				n[x] = 0;
				passwd = getpwnam (n + 1);
				n[x] = '/';
				if (passwd) {
					char *z = vsncpy (NULL, 0, sz (passwd->pw_dir));
					z = vsncpy (z, sLEN (z), sz (n + x));
					vsrm (n);
					n = z;
				}
			}
		}
	}
#endif
	return n;
}

/* Load file into new buffer and return the new buffer */
/* Returns with error set to 0 for success,
 * -1 for new file (file doesn't exist)
 * -2 for read error
 * -3 for seek error
 * -4 for open error
 */

B *
bload (s)
     char *s;
{
	char buffer[SEGSIZ];
	FILE *fi;
	B *b;
	long skip, amnt;
	char *n;
	int nowrite = 0;

	if (!s || !s[0])
	  {
		  error = -1;
		  b = bmk (NULL);
		  setopt (&b->o, "");
		  b->rdonly = b->o.readonly;
		  b->er = error;
		  return b;
	  }

	n = parsens (s, &skip, &amnt);

	/* Open file or stream */
	ossep (n);
#ifndef __MSDOS__
	if (n[0] == '!')
	  {
		  nescape (maint->t);
		  ttclsn ();
		  fi = popen (n + 1, "r");
	  }
	else
#endif
	if (!strcmp (n, "-"))
		fi = stdin;
	else
	  {
		  fi = fopen (n, "r+");
		  if (!fi)
			  nowrite = 1;
		  else
			  fclose (fi);
		  fi = fopen (n, "r");
		  if (!fi)
			  nowrite = 0;
	  }
	joesep (n);

	/* Abort if couldn't open */
	if (!fi)
	  {
		  if (errno == ENOENT)
			  error = -1;
		  else
			  error = -4;
		  b = bmk (NULL);
		  setopt (&b->o, n);
		  b->rdonly = b->o.readonly;
		  goto opnerr;
	  }

	/* Skip data if we need to */
	if (skip && lseek (fileno (fi), skip, 0) < 0)
	  {
		  int r;
		  while (skip > SEGSIZ)
		    {
			    r = bkread (fileno (fi), buffer, SEGSIZ);
			    if (r != SEGSIZ || error)
			      {
				      error = -3;
				      goto err;
			      }
			    skip -= SEGSIZ;
		    }
		  skip -= bkread (fileno (fi), buffer, (int) skip);
		  if (skip || error)
		    {
			    error = -3;
			    goto err;
		    }
	  }

	/* Read from stream into new buffer */
	b = bread (fileno (fi), amnt);
	setopt (&b->o, n);
	b->rdonly = b->o.readonly;

	/* Close stream */
      err:;
#ifndef __MSDOS__
	if (s[0] == '!')
		pclose (fi);
	else
#endif
	if (strcmp (n, "-"))
		fclose (fi);

      opnerr:;
	if (s[0] == '!')
		ttopnn (), nreturn (maint->t);

	/* Set name */
	b->name = joesep (strdup (s));

	/* Set flags */
	if (error || s[0] == '!' || skip || amnt != MAXLONG)
		b->backup = 1, b->changed = 0;
	else if (!strcmp (n, "-"))
		b->backup = 1, b->changed = 1;
	else
		b->backup = 0, b->changed = 0;
	if (nowrite)
		b->rdonly = b->o.readonly = 1;

	/* Eliminate parsed name */
	vsrm (n);

	b->er = error;
	return b;
}

/* Find already loaded buffer or load file into new buffer */

B *
bfind (s)
     char *s;
{
	B *b;
	if (!s || !s[0])
	  {
		  error = -1;
		  b = bmk (NULL);
		  setopt (&b->o, "");
		  b->rdonly = b->o.readonly;
		  b->internal = 0;
		  b->er = error;
		  return b;
	  }
	for (b = bufs.link.next; b != &bufs; b = b->link.next)
		if (b->name && !strcmp (s, b->name))
		  {
			  if (!b->orphan)
				  ++b->count;
			  else
				  b->orphan = 0;
			  error = 0;
			  b->internal = 0;
			  return b;
		  }
	b = bload (s);
	b->internal = 0;
	return b;
}

char **
getbufs ()
{
	char **s = vamk (16);
	B *b;
	for (b = bufs.link.next; b != &bufs; b = b->link.next)
		if (b->name)
			s = vaadd (s, vsncpy (NULL, 0, sz (b->name)));
	return s;
}

/* Find an orphaned buffer */

B *
borphan ()
{
	B *b;
	for (b = bufs.link.next; b != &bufs; b = b->link.next)
		if (b->orphan)
		  {
			  b->orphan = 0;
			  return b;
		  }
	return 0;
}

/* Write 'size' bytes from file beginning at 'p' to open file 'fd'.
 * Returns error.
 * error is set to -5 for write error or 0 for success.
 * Don't attempt to write past the end of the file
 */

int
bsavefd (p, fd, size)
     P *p;
     long size;
{
	P *np = pdup (p);
	int amnt;
	while (size > (amnt = GSIZE (np->hdr) - np->ofst))
	  {
		  if (np->ofst < np->hdr->hole)
		    {
			    if (jwrite
				(fd, np->ptr + np->ofst,
				 np->hdr->hole - np->ofst) < 0)
				    goto err;
			    if (jwrite
				(fd, np->ptr + np->hdr->ehole,
				 SEGSIZ - np->hdr->ehole) < 0)
				    goto err;
		    }
		  else
			  if (jwrite
			      (fd, np->ptr + np->ofst + GGAPSZ (np->hdr),
			       amnt) < 0)
			  goto err;
		  size -= amnt;
		  pnext (np);
	  }
	if (size)
		if (np->ofst < np->hdr->hole) {
			if (size > np->hdr->hole - np->ofst) {
				if (jwrite
				      (fd, np->ptr + np->ofst,
				       np->hdr->hole - np->ofst) < 0)
					  goto err;
				if (jwrite
				      (fd, np->ptr + np->hdr->ehole,
				       (int) size - np->hdr->hole +
				       np->ofst) < 0)
					  goto err;
			} else {
				if (jwrite
				     (fd, np->ptr + np->ofst,
				       (int) size) < 0)
					  goto err;
			}
		} else {
			if (jwrite
			    (fd, np->ptr + np->ofst + GGAPSZ (np->hdr),
			     (int) size) < 0)
			goto err;
		}
	prm (np);
	return error = 0;
      err:;
	prm (np);
	return error = 5;
}

/* Save 'size' bytes beginning at 'p' in file 's' */

int
bsave (p, s, size)
     P *p;
     char *s;
     long size;
{
	FILE *f;
	long skip, amnt;

	s = parsens (s, &skip, &amnt);

	if (amnt < size)
		size = amnt;

	ossep (s);
#ifndef __MSDOS__
	if (s[0] == '!')
	  {
		  nescape (maint->t);
		  ttclsn ();
		  f = popen (s + 1, "w");
	  }
	else
#endif
	if (s[0] == '>' && s[1] == '>')
		f = fopen (s + 2, "a");
	else if (!strcmp (s, "-"))
	  {
		  nescape (maint->t);
		  ttclsn ();
		  f = stdout;
	  }
	else if (skip || amnt != MAXLONG)
		f = fopen (s, "r+");
	else
		f = fopen (s, "w");
	joesep (s);

	if (!f)
	  {
		  error = -4;
		  goto opnerr;
	  }
	fflush (f);

	if (skip && lseek (fileno (f), skip, 0) < 0)
	  {
		  error = -3;
		  goto err;
	  }

	bsavefd (p, fileno (f), size);

	if (!error && force && size && !skip && amnt == MAXLONG)
	  {
		  P *q = pdup (p);
		  char nl = '\n';
		  pfwrd (q, size - 1);
		  if (brc (q) != '\n' && jwrite (fileno (f), &nl, 1) < 0)
			  error = -5;
		  prm (q);
	  }

      err:;
#ifndef __MSDOS__
	if (s[0] == '!')
		pclose (f);
	else
#endif
	if (strcmp (s, "-"))
		fclose (f);
	else
		fflush (f);

      opnerr:;
	if (s[0] == '!' || !strcmp (s, "-"))
		ttopnn (), nreturn (maint->t);
	return error;
}

int
brc (p)
     P *p;
{
	if (p->ofst == GSIZE (p->hdr))
		return MAXINT;
	if (p->ofst >= p->hdr->hole)
		return p->ptr[p->ofst + p->hdr->ehole - p->hdr->hole];
	else
		return p->ptr[p->ofst];
}

char *
brmem (p, blk, size)
     P *p;
     char *blk;
     int size;
{
	char *bk = blk;
	P *np;
	int amnt;
	np = pdup (p);
	while (size > (amnt = GSIZE (np->hdr) - np->ofst))
	  {
		  grmem (np->hdr, np->ptr, np->ofst, bk, amnt);
		  bk += amnt;
		  size -= amnt;
		  pnext (np);
	  }
	if (size)
		grmem (np->hdr, np->ptr, np->ofst, bk, size);
	prm (np);
	return blk;
}

char *
brs (p, size)
     P *p;
     int size;
{
	char *s = (char *) malloc (size + 1);
	s[size] = 0;
	return brmem (p, s, size);
}

char *
brvs (p, size)
     P *p;
     int size;
{
	char *s = vstrunc (NULL, size);
	return brmem (p, s, size);
}

/* Save edit buffers when editor dies */

extern char *ctime ();

void
ttsig (sig)
{
	long tim = time (0);
	B *b;
	FILE *f;
	int tmpfd;
	struct stat sbuf;

	if ((tmpfd = open ("DEADJOE", O_RDWR | O_EXCL | O_CREAT, 0600)) < 0)
	  {
		  if (lstat ("DEADJOE", &sbuf) < 0)
			  _exit (-1);
		  if (!S_ISREG (sbuf.st_mode) || sbuf.st_uid != geteuid ())
			  _exit (-1);
		  /*
		     A race condition still exists between the lstat() and the open()
		     systemcall, which leads to a possible denial-of-service attack
		     by setting the file access mode to 600 for every file the
		     user executing joe has permissions to.
		     This can't be fixed w/o breacking the behavior of the orig. joe!
		   */
		  if ((tmpfd = open ("DEADJOE", O_RDWR | O_APPEND)) < 0)
			  _exit (-1);
		  if (fchmod (tmpfd, S_IRUSR | S_IWUSR) < 0)
			  _exit (-1);
	  }
	if ((f = fdopen (tmpfd, "a")) == NULL)
		_exit (-1);

	fprintf (f, "\n*** Modified files in JOE when it aborted on %s",
		 ctime (&tim));
	if (sig)
		fprintf (f, "*** JOE was aborted by signal %d\n", sig);
	else
		fprintf (f,
			 "*** JOE was aborted because the terminal closed\n");
	fflush (f);
	for (b = bufs.link.next; b != &bufs; b = b->link.next)
		if (b->changed)
		  {
			  if (b->name)
				  fprintf (f, "\n*** File \'%s\'\n", b->name);
			  else
				  fprintf (f, "\n*** File \'(Unnamed)\'\n");
			  fflush (f);
			  bsavefd (b->bof, fileno (f), b->eof->byte);
		  }
	if (sig)
		ttclsn ();
	_exit (1);
}
joe-2.9.6/b.h0100600003233700235640000000726507265143612012300 0ustar  xgracstudent#ifndef _Ib
#define _Ib 1

#include "config.h"
#include "queue.h"
#include "rc.h"
#include "vfile.h"

#define stdsiz 8192
/* 31744 */
extern char stdbuf[stdsiz];

typedef struct buffer B;
typedef struct point P;
typedef struct header H;

struct header {
	LINK (H) link;			/* ??? */
	long seg;			/* ??? */
	int hole;			/* ??? */
	int ehole;			/* ??? */
	int nlines;			/* ??? */
};

struct point {
	LINK (P) link;			/* ??? */
		
	B *b;				/* ??? */
	int ofst;			/* ??? */
	char *ptr;			/* ??? */
	H *hdr;				/* ??? */

	long byte;			/* ??? */
	long line;			/* ??? */
	long col;			/* ??? */
	long xcol;			/* ??? */
	int valcol;			/* ??? */
	int end;			/* ??? */

	P **owner;			/* ??? */
};

struct buffer {
	LINK (B) link;
	P *bof;
	P *eof;
	char *name;
	int orphan;
	int count;
	int changed;
	int backup;
	void *undo;
	P *marks[10];		/* Bookmarks */
	OPTIONS o;		/* Options */
	P *oldcur;		/* Last cursor position before orphaning */
	P *oldtop;		/* Last top screen position before orphaning */
	int rdonly;		/* Set for read-only */
	int internal;		/* Set for internal buffers */
	int er;			/* Error code when file was loaded */
};

extern int force;		/* Set to have final '\n' added to file */
extern int tabwidth;		/* Default tab width */

extern VFILE *vmem;		/* Virtual memory file used for buffer system */

extern char *msgs[];

B *bmk ();
void brm ();

B *bfind ();

P *pdup ();
P *pdupown ();
P *poffline ();
P *ponline ();
B *bonline ();
B *boffline ();

void prm ();
P *pset ();

P *p_goto_bof (P *p);		/* move cursor to begging of file */
P *p_goto_eof (P *p);		/* move cursor to end of file */
P *p_goto_bol (P *p);		/* move cursor to begging of line */
P *p_goto_eol (P *p);		/* move cursor to end of line */

int pisbof ();
int piseof ();
int piseol ();
int pisbol ();
int pisbow ();
int piseow ();

#define piscol(p) ((p)->valcol?(p)->col:(pfcol(p),(p)->col))

int pisblank ();

long pisindent ();

int pnext ();
int pprev ();

int pgetc ();

P *pfwrd ();

int prgetc ();

P *pbkwd ();
P *pgoto ();

P *pfcol ();


P *pnextl ();

P *pprevl ();

P *pline ();

P *pcolwse ();
P *pcol ();
P *pcoli ();
void pbackws ();
void pfill ();

P *pfind ();
P *pifind ();
P *prfind ();
P *prifind ();

/* B *bcpy(P *from,P *to);
 * Copy text between from and to into a new buffer
 */
B *bcpy ();

void pcoalesce ();

void bdel ();

/* P *binsb(P *p,B *b);
 * Insert an entire buffer 'b' into another buffer at 'p'
 */
P *binsb ();

/* P *binsm(P *p,char *blk,int amnt);
 * Insert a block 'blk' of size 'amnt' into buffer at 'p'
 */
P *binsm ();

/* P *binsc(P *p,char c);
 * Insert character into buffer at P
 */
P *binsc ();

/* P *binss(P *p,char *s);
 * Insert zero terminated string into buffer at P
 */
P *binss ();

/* B *bload(char *s);
 * Load a file into a new buffer
 *
 * Returns with errno set to 0 for success,
 * -1 for new file (file doesn't exist)
 * -2 for read error
 * -3 for seek error
 * -4 for open error
 */
B *bread ();
B *bload ();
B *bfind ();
B *borphan ();

/* int bsave(P *p,char *s,long size);
 * Save 'size' bytes beginning at 'p' into file with name in 's'
 */
int bsavefd ();
int bsave ();

char *parsens ();

/* int brc(P *p);
 * Get character at pointer or return MAXINT if pointer is at end of buffer
 */
int brc ();

/* char *brmem(P *p,char *blk,int size);
 * Copy 'size' bytes from a buffer beginning at p into block 'blk'
 */
char *brmem ();

/* char *brs(P *p,int size);
 * Copy 'size' bytes from a buffer beginning at p into a zero-terminated
 * C-string in an malloc block.
 */
char *brs ();

/* char *brvs(P *p,int size);
 * Copy 'size' bytes from a buffer beginning at p into a variable length
 * string.
 */
char *brvs ();

B *bnext ();
B *bprev ();

#define error berror
extern int berror;

char **getbufs ();

#endif
joe-2.9.6/w.c0100644003233700235640000004300407262722314012316 0ustar  xgracstudent/*
	Window system
	Copyright (C) 1992 Joseph H. Allen

	This file is part of JOE (Joe's Own Editor)
*/

#include "config.h"
#include "b.h"
#include "scrn.h"
#include "queue.h"
#include "main.h"
#include "poshist.h"
#include "blocks.h"
#include "utils.h"
#include "w.h"

extern int dspasis;		/* Set to display chars above 127 as-is */
extern int staen;		/* 0 if top-most status line not displayed */

/* Count no. of main windows */

int countmain (SCREEN * t) {
	int nmain = 1;
	W *m = t->curwin->main;
	W *q;
	for (q = t->curwin->link.next; q != t->curwin; q = q->link.next)
		if (q->main != m)
		  {
			  ++nmain;
			  m = q->main;
		  }
	return nmain;
}

/* Redraw a window */

void wredraw (W *w) {
	msetI (w->t->t->updtab + w->y, 1, w->h);
}

/* Find first window in a group */

W *findtopw (W *w) {
	W *x;
	for (x = w; x->link.prev->main == w->main && x->link.prev != w;
	     x = x->link.prev);
	return x;
}

/* Determine height of a window.  Returns reqh if it is set, otherwise
 * used fixed or hh scaled to the current screen size */

int geth (W *w) {
	if (w->reqh)
		return w->reqh;
	else if (w->fixed)
		return w->fixed;
	else
		return (((long) w->t->h - w->t->wind) * w->hh) / 1000;
}

/* Set the height of a window */

void seth (W *w, int h) {
	long tmp;
	w->reqh = h;
	tmp = 1000L * h;
	w->hh =
		tmp / (w->t->h - w->t->wind) +
		(tmp % (w->t->h - w->t->wind) ? 1 : 0);
}

/* Determine height of a family of windows.  Uses 'reqh' if it's set */

int getgrouph (W *w) {
	W *x;
	int h;

	/* Find first window in family */
	x = findtopw (w);

	/* Add heights of all windows in family */
	for (w = x, h = geth (w);
	     w->link.next != x && w->link.next->main == x->main;
	     w = w->link.next, h += geth (w));

	return h;
}

/* Determine minimum height of a family */

int getminh (W *w) {
	W *x;
	int h;
	x = findtopw (w);
	for (w = x, h = (w->fixed ? w->fixed : 2);
	     w->link.next != x && w->link.next->main == x->main;
	     w = w->link.next, h += (w->fixed ? w->fixed : 2));

	return h;
}

/* Find last window in a group */

W *findbotw (W *w) {
	W *x;
	for (x = w; x->link.next->main == w->main && x->link.next != w;
	     x = x->link.next);
	return x;
}

/* Demote group of window to end of window list.  Returns true if top window
   was demoted */

int demotegroup (W *w) {
	W *top = findtopw (w);
	W *bot = findbotw (w);
	W *next;
	int flg = 0;
	for (w = top; w != bot; w = next)
	  {
		  next = w->link.next;
		  if (w == w->t->topwin)
			  flg = 1, w->t->topwin = next;
		  else
			  demote (W, link, w->t->topwin, w);
		  w->y = -1;
	  }
	if (w == w->t->topwin)
		flg = 1;
	else
		demote (W, link, w->t->topwin, w);
	w->y = -1;
	return flg;
}

/* Find last window on the screen */

W *lastw (SCREEN *t) {
	W *x;
	for (x = t->topwin; x->link.next != t->topwin && x->link.next->y >= 0;
	     x = x->link.next);
	return x;
}

/* Create a screen object */

SCREEN *scr;

SCREEN *screate (SCRN *scrn) {
	SCREEN *t = (SCREEN *) malloc (sizeof (SCREEN));
	t->t = scrn;
	t->w = scrn->co;
	t->h = scrn->li;
	t->topwin = 0;
	t->curwin = 0;
	t->wind = skiptop;
	scr = t;
	return t;
}

void sresize (SCREEN *t) {
	SCRN *scrn = t->t;
	W *w;
	t->w = scrn->co;
	t->h = scrn->li;
	if (t->h - t->wind < FITHEIGHT)
		t->wind = t->h - FITHEIGHT;
	if (t->wind < 0)
		t->wind = 0;
	w = t->topwin;
	do
		w->y = -1, w->w = t->w - 1;
	while (w = w->link.next, w != t->topwin);
	wfit (t);
	updall ();
}

void updall (void) {
	int y;
	for (y = 0; y != scr->h; ++y)
		scr->t->updtab[y] = 1;
}

void scrins (B *b, long l, long n, int flg) {
	W *w;
	if (w = scr->topwin)
		do
			if (w->y >= 0)
			  {
				  if (w->object && w->watom->ins)
					  w->watom->ins (w->object, b, l, n,
							 flg);
			  }
		while (w = w->link.next, w != scr->topwin);
}

void scrdel (B *b, long l, long n, int flg) {
	W *w;
	if (w = scr->topwin)
		do
			if (w->y >= 0)
			  {
				  if (w->object && w->watom->del)
					  w->watom->del (w->object, b, l, n,
							 flg);
			  }
		while (w = w->link.next, w != scr->topwin);
}

/* Fit as many windows on the screen as is possible beginning with the window
 * at topwin.  Give any extra space which couldn't be used to fit in another
 * window to the last text window on the screen.  This function guarentees
 * to fit on the window with the cursor in it (moves topwin to next group
 * of windows until window with cursor fits on screen).
 */

static int doabort ();
extern volatile int dostaupd;

void wfit (SCREEN *t) {
	int y;			/* Where next window goes */
	int left;		/* Lines left on screen */
	W *w;			/* Current window we're fitting */
	W *pw;			/* Main window of previous family */
	int req;		/* Amount this family needs */
	int adj;		/* Amount family needs to be adjusted */
	int flg = 0;		/* Set if cursor window was placed on screen */
	int ret;
	dostaupd = 1;

      tryagain:
	y = t->wind;
	left = t->h - y;
	pw = 0;

	w = t->topwin;
	do
	  {
		  w->ny = -1;
		  w->nh = geth (w);
	  }
	while ((w = w->link.next) != t->topwin);

	/* Fit a group of windows on the screen */
	w = t->topwin;
	do
	  {
		  req = getgrouph (w);
		  if (req > left)	/* If group is taller than lines left */
			  adj = req - left;	/* then family gets shorter */
		  else
			  adj = 0;

		  /* Fit a family of windows on the screen */
		  do
		    {
			    w->ny = y;	/* Set window's y position */
			    if (!w->win)
				    pw = w, w->nh -= adj;	/* Adjust main window of the group */
			    if (!w->win && w->nh < 2)
				    while (w->nh < 2)
					    w->nh +=
						    doabort (w->link.next,
							     &ret);
			    if (w == t->curwin)
				    flg = 1;	/* Set if we got window with cursor */
			    y += w->nh;
			    left -= w->nh;	/* Increment y value by height of window */
			    w = w->link.next;	/* Next window */
		    }
		  while (w != t->topwin && w->main == w->link.prev->main);
	  }
	while (w != t->topwin && left >= FITHEIGHT);

	/* We can't use extra space to fit a new family on, so give space to parent of
	 * previous family */
	pw->nh += left;

	/* Adjust that family's children which are below the parent */
	while ((pw = pw->link.next) != w)
		pw->ny += left;

	/* Make sure the cursor window got on the screen */
	if (!flg)
	  {
		  t->topwin = findbotw (t->topwin)->link.next;
		  goto tryagain;
	  }

	/* All of the windows are now on the screen.  Scroll the screen to reflect what
	 * happened
	 */
	w = t->topwin;
	do
		if (w->y >= 0 && w->ny >= 0)
			if (w->ny > w->y)
			  {
				  W *l = pw = w;
				  while (pw->link.next != t->topwin &&
					 (pw->link.next->y < 0
					  || pw->link.next->ny < 0
					  || pw->link.next->ny >
					  pw->link.next->y))
				    {
					    pw = pw->link.next;
					    if (pw->ny >= 0 && pw->y >= 0)
						    l = pw;
				    }
				  /* Scroll windows between l and w */
				loop1:
				  if (l->ny >= 0 && l->y >= 0)
					  nscrldn (t->t, l->y,
						   l->ny + uns_min (l->h,
								    l->nh),
						   l->ny - l->y);
				  if (w != l)
				    {
					    l = l->link.prev;
					    goto loop1;
				    }
				  w = pw->link.next;
			  }
			else if (w->ny < w->y)
			  {
				  W *l = pw = w;
				  while (pw->link.next != t->topwin &&
					 (pw->link.next->y < 0 ||
					  pw->link.next->ny < 0 ||
					  pw->link.next->ny <
					  pw->link.next->y))
				    {
					    pw = pw->link.next;
					    if (pw->ny >= 0 && pw->y >= 0)
						    l = pw;
				    }
				  /* Scroll windows between l and w */
				loop0:
				  if (w->ny >= 0 && w->y >= 0)
					  nscrlup (t->t, w->ny,
						   w->y + uns_min (w->h,
								   w->nh),
						   w->y - w->ny);
				  if (w != l)
				    {
					    w = w->link.next;
					    goto loop0;
				    }
				  w = pw->link.next;
			  }
			else
				w = w->link.next;
		else
			w = w->link.next;
	while (w != t->topwin);

	/* Update current height and position values */
	w = t->topwin;
	do
	  {
		  if (w->ny >= 0)
		    {
			    if (w->object)
			      {
				      if (w->watom->move)
					      w->watom->move (w->object, w->x,
							      w->ny);
				      if (w->watom->resize)
					      w->watom->resize (w->object,
								w->w, w->nh);
			      }
			    if (w->y == -1)
				    msetI (t->t->updtab + w->ny, 1, w->nh);
			    w->y = w->ny;
		    }
		  else
			  w->y = -1;
		  w->h = w->nh;
		  w->reqh = 0;
	  }
	while (w = w->link.next, w != t->topwin);
}

/* Goto next window */

int wnext (SCREEN *t) {
	if (t->curwin->link.next != t->curwin)
	  {
		  t->curwin = t->curwin->link.next;
		  if (t->curwin->y == -1)
			  wfit (t);
		  return 0;
	  }
	else
		return -1;
}

/* Goto previous window */

int wprev (SCREEN *t) {
	if (t->curwin->link.prev != t->curwin)
	  {
		  t->curwin = t->curwin->link.prev;
		  if (t->curwin->y == -1)
		    {
			    t->topwin = findtopw (t->curwin);
			    wfit (t);
		    }
		  return 0;
	  }
	else
		return -1;
}

/* Grow window */

int wgrow (W *w) {
	W *nextw;

	/* If we're the last window on the screen, shrink the previous window */
	if ((w->link.next == w->t->topwin || w->link.next->y == -1) &&
	    w != w->t->topwin)
		return wshrink (w->link.prev->main);

	/* Get to next variable size window */
	for (nextw = w->link.next; nextw->fixed && nextw != w->t->topwin;
	     nextw = nextw->link.next);

	/* Is it below us, on screen and big enough to take space from? */
	if (nextw == w->t->topwin || nextw->y == -1 || nextw->h <= FITHEIGHT)
		return -1;

	/* Increase this window's height */
	seth (w, w->h + 1);

	/* Decrease next window's height */
	seth (nextw, nextw->h - 1);

	/* Do it */
	wfit (w->t);

	return 0;
}

/* Shrink window */

int wshrink (W *w) {
	W *nextw;

	/* If we're the last window on the screen, grow the previous window */
	if ((w->link.next == w->t->topwin || w->link.next->y == -1) &&
	    w != w->t->topwin)
		return wgrow (w->link.prev->main);

	/* Is this window too small already? */
	if (w->h <= FITHEIGHT)
		return -1;

	/* Get to window below us */
	for (nextw = w->link.next;
	     nextw != w->t->topwin && nextw->fixed; nextw = nextw->link.next);
	if (nextw == w->t->topwin)
		return -1;

	/* Decrease the size of this window */
	seth (w, w->h - 1);

	/* Increase the size of next window */
	seth (nextw, nextw->h + 1);

	/* Do it */
	wfit (w->t);

	return 0;
}

/* Show all windows */

void wshowall (SCREEN *t) {
	int n = 0;
	int set;
	W *w;

	/* Count no. of main windows */
	w = t->topwin;
	do
		if (!w->win)
			++n;
	while (w = w->link.next, w != t->topwin);

	/* Compute size to set each window */
	if ((set = (t->h - t->wind) / n) < FITHEIGHT)
		set = FITHEIGHT;

	/* Set size of each variable size window */
	w = t->topwin;
	do
		if (!w->win)
		  {
			  int h = getminh (w);
			  if (h >= set)
				  seth (w, 2);
			  else
				  seth (w, set - (h - 2));
			  w->orgwin = 0;
		  }
	while (w = w->link.next, w != t->topwin);

	/* Do it */
	wfit (t);
}

void wspread (SCREEN *t) {
	int n = 0;
	W *w = t->topwin;
	do
		if (w->y >= 0 && !w->win)
			++n;
	while (w = w->link.next, w != t->topwin);
	if (!n)
	  {
		  wfit (t);
		  return;
	  }
	if ((t->h - t->wind) / n >= FITHEIGHT)
		n = (t->h - t->wind) / n;
	else
		n = FITHEIGHT;
	w = t->topwin;
	do
		if (!w->win)
		  {
			  int h = getminh (w);
			  if (h >= n)
				  seth (w, 2);
			  else
				  seth (w, n - (h - 2));
			  w->orgwin = 0;
		  }
	while (w = w->link.next, w != t->topwin);
	wfit (t);
}

/* Show just one family of windows */

void wshowone (W *w) {
	W *q = w->t->topwin;
	do
		if (!q->win)
		  {
			  seth (q, w->t->h - w->t->wind - (getminh (q) - 2));
			  q->orgwin = 0;
		  }
	while (q = q->link.next, q != w->t->topwin);
	wfit (w->t);
}

/* Create a window */

W *wcreate (SCREEN *t, WATOM *watom, W *where, W *target, W *original, int height, char *huh, int *notify) {
	W *new;

	if (height < 1)
		return 0;

	/* Create the window */
	new = (W *) malloc (sizeof (W));
	new->notify = notify;
	new->t = t;
	new->w = t->w - 1;
	seth (new, height);
	new->h = new->reqh;
	new->y = -1;
	new->ny = 0;
	new->nh = 0;
	new->x = 0;
	new->huh = huh;
	new->orgwin = original;
	new->watom = watom;
	new->object = 0;
	new->msgb = 0;
	new->msgt = 0;
	/* Set window's target and family */
	if (new->win = target)
	  {			/* A subwindow */
		  new->main = target->main;
		  new->fixed = height;
	  }
	else
	  {			/* A parent window */
		  new->main = new;
		  new->fixed = 0;
	  }

	/* Get space for window */
	if (original)
		if (original->h - height <= 2)
		  {
			  /* Not enough space for window */
			  free (new);
			  return 0;
		  }
		else
			seth (original, original->h - height);

	/* Create new keyboard handler for window */
	if (watom->context)
		new->kbd = mkkbd (getcontext (watom->context));
	else
		new->kbd = 0;

	/* Put window on the screen */
	if (where)
		enquef (W, link, where, new);
	else
	  {
		  if (t->topwin)
			  enqueb (W, link, t->topwin, new);
		  else
			  izque (W, link, new), t->curwin = t->topwin = new;
	  }

	return new;
}

/* Abort group of windows */

static int doabort (W *w, int *ret) {
	int amnt = geth (w);
	W *z;
	w->y = -2;
	if (w->t->topwin == w)
		w->t->topwin = w->link.next;
      loop:
	z = w->t->topwin;
	do
	  {
		  if (z->orgwin == w)
			  z->orgwin = 0;
		  if ((z->win == w || z->main == w) && z->y != -2)
		    {
			    amnt += doabort (z, ret);
			    goto loop;
		    }
	  }
	while (z = z->link.next, z != w->t->topwin);
	if (w->orgwin)
		seth (w->orgwin, geth (w->orgwin) + geth (w));
	if (w->t->curwin == w)
		if (w->t->curwin->win)
			w->t->curwin = w->t->curwin->win;
		else if (w->orgwin)
			w->t->curwin = w->orgwin;
		else
			w->t->curwin = w->link.next;
	if (qempty (W, link, w))
	  {
		  leave = 1;
		  amnt = 0;
	  }
	deque (W, link, w);
	if (w->watom->abort && w->object)
	  {
		  *ret = w->watom->abort (w->object, MAXINT);
		  if (w->notify)
			  *w->notify = -1;
	  }
	else
	  {
		  *ret = -1;
		  if (w->notify)
			  *w->notify = 1;
	  }
	rmkbd (w->kbd);
	free (w);
	windie (w);
	return amnt;
}

/* Abort a window and its children */

int wabort (W *w) {
	SCREEN *t = w->t;
	int ret;
	if (w != w->main)
	  {
		  doabort (w, &ret);
		  if (!leave)
			  wfit (t);
	  }
	else
	  {
		  doabort (w, &ret);
		  if (!leave)
		    {
			    if (lastw (t)->link.next != t->topwin)
				    wfit (t);
			    else
				    wspread (t);
		    }
	  }
	return ret;
}

/* Generate text with formatting escape sequences */

void genfmt (SCRN *t, int x, int y, int ofst, char *s, int flg) {
	int *scrn = t->scrn + y * t->co + x;
	int atr = 0;
	int col = 0;
	int c;
	while (c = *s++)
		if (c == '\\')
			switch (c = *s++)
			  {
			  case 'u':
			  case 'U':
				  atr ^= UNDERLINE;
				  break;

			  case 'i':
			  case 'I':
				  atr ^= INVERSE;
				  break;

			  case 'b':
			  case 'B':
				  atr ^= BOLD;
				  break;

			  case 'd':
			  case 'D':
				  atr ^= DIM;
				  break;

			  case 'f':
			  case 'F':
				  atr ^= BLINK;
				  break;

			  case 0:
				  --s;
				  break;

			  default:
				  if (col++ >= ofst)
				    {
					    outatr (t, scrn, x, y, c, atr);
					    ++scrn;
					    ++x;
				    }
				  break;
			  }
		else if (col++ >= ofst)
		  {
			  if (c == '\t')
				  c = ' ';
			  outatr (t, scrn, x, y, c, atr);
			  ++scrn;
			  ++x;
		  }
	if (flg)
		eraeol (t, x, y);
}

/* Generate text: no formatting */

void gentxt (SCRN * t, int x, int y, int ofst, char *s, int len, int flg) {
	int *scrn = t->scrn + y * t->co + x;
	int col;
	int c;
	int a;
	for (col = 0; col != len; ++col)
		if (col >= ofst)
		  {
			  c = (unsigned) s[col];
			  if (c == '\t')
				  c = ' ';
			  xlat (a, c);
			  outatr (t, scrn, x, y, c, a);
			  ++scrn;
			  ++x;
		  }
	if (flg)
		eraeol (t, x, y);
}

/* Determine column width of string with format codes */

int fmtlen (char *s) {
	int col = 0;
	while (*s)
	  {
		  if (*s == '\\')
			  switch (*++s)
			    {
			    case 'u':
			    case 'i':
			    case 'd':
			    case 'f':
			    case 'b':
			    case 'U':
			    case 'I':
			    case 'D':
			    case 'F':
			    case 'B':
				    ++s;
				    goto cont;

			    case 0:
				    --s;
			    }
		  ++col;
		  ++s;
		cont:;
	  }
	return col;
}

/* Return offset within format string which corresponds to a particular
   column */

int fmtpos (char *s, int goal) {
	char *org = s;
	int col = 0;
	while (*s && col != goal)
	  {
		  if (*s == '\\')
			  switch (*++s)
			    {
			    case 'u':
			    case 'i':
			    case 'd':
			    case 'f':
			    case 'b':
			    case 'U':
			    case 'I':
			    case 'D':
			    case 'F':
			    case 'B':
				    ++s;
				    goto cont;

			    case 0:
				    --s;
			    }
		  ++col;
		  ++s;
		cont:;
	  }
	return s - org + goal - col;
}

/* Display a message and skip the next key */

static void mdisp (SCRN *t, int y, char *s) {
	int ofst;
	int len;
	len = fmtlen (s);
	if (len <= (t->co - 1))
		ofst = 0;
	else
		ofst = len - (t->co - 1);
	genfmt (t, 0, y, ofst, s, 1);
	t->updtab[y] = 1;
}

void msgout (W *w) {
	SCRN *t = w->t->t;
	if (w->msgb)
	  {
		  mdisp (t, w->y + w->h - 1, w->msgb);
		  w->msgb = 0;
	  }
	if (w->msgt)
	  {
		  mdisp (t, w->y + ((w->h > 1 && (w->y || !staen)) ? 1 : 0),
			 w->msgt);
		  w->msgt = 0;
	  }
}

/* Set temporary message */

char msgbuf[MSGBUFSIZE];

void msgnw (BASE *w, char *s) {
	w->parent->msgb = s;
}

void msgnwt (BASE *w, char *s) {
	w->parent->msgt = s;
}

int urtn (BASE *b, int k) {
	if (b->parent->watom->rtn)
		return b->parent->watom->rtn (b, k);
	else
		return -1;
}

int utype (BASE *b, int k) {
	if (b->parent->watom->type)
		return b->parent->watom->type (b, k);
	else
		return -1;
}

/* Window user commands */

int uprevw (BASE *bw) {
	return wprev (bw->parent->t);
}

int unextw (BASE *bw) {
	return wnext (bw->parent->t);
}

int ugroww (BASE *bw) {
	return wgrow (bw->parent);
}

int ushrnk (BASE *bw) {
	return wshrink (bw->parent);
}

int uexpld (BASE *bw) {
	if (bw->parent->t->h - bw->parent->t->wind == getgrouph (bw->parent))
		wshowall (bw->parent->t);
	else
		wshowone (bw->parent);
	return 0;
}

int uretyp (BASE *bw) {
	nredraw (bw->parent->t->t);
	return 0;
}
joe-2.9.6/w.h0100600003233700235640000001352007264414442012315 0ustar  xgracstudent/*
	Window management
	Copyright (C) 1992 Joseph H. Allen

	This file is part of JOE (Joe's Own Editor)
*/

#ifndef _Iw
#define _Iw 1

#include "config.h"
#include "queue.h"
#include "scrn.h"
#include "kbd.h"

typedef struct watom WATOM;
typedef struct screen SCREEN;
typedef struct window W;
typedef struct base BASE;

struct watom {
	char *context;		/* Context name */
	void (*disp) ();	/* Display window */
	void (*follow) ();	/* Called to have window follow cursor */
	int (*abort) ();	/* Common user functions */
	int (*rtn) ();
	int (*type) ();
	void (*resize) ();	/* Called when window changed size */
	void (*move) ();	/* Called when window moved */
	void (*ins) ();		/* Called on line insertions */
	void (*del) ();		/* Called on line deletions */
	int what;		/* Type of this thing */
};

struct screen {
	SCRN *t;		/* Screen data on this screen is output to */

	int wind;		/* Number of help lines on this screen */

	W *topwin;		/* Top-most window showing on screen */
	W *curwin;		/* Window cursor is in */

	int w, h;		/* Width and height of this screen */
};

struct window {
	LINK (W) link;		/* Linked list of windows in order they
				   appear on the screen */

	SCREEN *t;		/* Screen this thing is on */

	int x, y, w, h;		/* Position and size of window */
	/* Currently, x=0, w=width of screen. */
	/* y== -1 if window is not on screen */

	int ny, nh;		/* Temporary values for wfit */

	int reqh;		/* Requested new height or 0 for same */
	/* This is an argument for wfit */

	int fixed;		/* If this is zero, use 'hh'.  If not, this
				   is a fixed size window and this variable
				   gives its height */

	int hh;			/* Height window would be on a screen with
				   1000 lines.  When the screen size changes
				   this is used to calculate the window's
				   real height */

	W *win;			/* Window this one operates on */
	W *main;		/* Main window of this family */
	W *orgwin;		/* Window where space from this window came */
	int curx, cury;		/* Cursor position within window */
	KBD *kbd;		/* Keyboard handler for this window */
	WATOM *watom;		/* The type of this window */
	void *object;		/* Object which inherits this */

	char *msgt;		/* Message at top of window */

	char *msgb;		/* Message at bottom of window */

	char *huh;		/* Name of window for context sensitive hlp */

	int *notify;		/* Address of kill notification flag */
};

/* Anything which goes in window.object must start like this: */

struct base {
	W *parent;
};

/* Minimum text window height */
#define FITHEIGHT 4

/***************/
/* Subroutines */
/***************/

/* int getgrouph(W *);
 * Get height of a family of windows
 */
int getgrouph ();

/* W *findtopw(W *);
 * Find first (top-most) window of a family
 */
W *findtopw ();

/* W *findbotw(W *);
 * Find last (bottom-most) window a family
 */
W *findbotw ();

int demotegroup ();

/* W *lastw(SCREEN *t);
 * Find last window on screen
 */
W *lastw ();

/* Determine number of main windows
 */
int countmain ();

/* void wfit(SCREEN *t);
 *
 * Fit all of the windows onto the screen
 */
void wfit ();

/*****************/
/* Main routines */
/*****************/

/* SCREEN *screate(SCRN *);
 *
 * Create a screen
 */
SCREEN *screate ();

/* void sresize(SCREEN *t);
 * Screen size changed
 */
void sresize ();

/* void chsize(SCREEN *t,int mul,int div)
 * Resize windows: each window is multiplied by the fraction mul/div
 */
void chsize ();

/* W *wcreate(SCREEN *t,WATOM *watom,W *where,W *target,W *original,int height);
 *
 * Try to create a window
 *
 * 't'		Is the screen the window is placed on
 * 'watom'	Type of new window
 * 'where'	The window is placed after this window, or if 'where'==0, the
 *		window is placed on the end of the screen
 * 'target'	The window operates on this window.  The window becomes a
 *		member of 'target's family or starts a new family if
 *		'target'==0.
 * 'original'	Attempt to get 'height' from this window.  When the window is
 *              aborted, the space gets returned to 'original' if it still
 *		exists.  If 'original'==0, the window will force other
 *		windows to go off of the screen.
 * 'height'	The height of the window
 *
 * Returns the new window or returns 0 if there was not enough space to
 * create the window and maintain family integrity.
 */
W *wcreate ();

/* int wabort(W *w);
 *
 * Kill a window and it's children
 */
int wabort ();

/* int wnext(SCREEN *);
 *
 * Switch to next window
 */
int wnext ();

/* int wprev(SCREEN *);
 *
 * Switch to previous window
 */
int wprev ();

/* int wgrow(W *);
 *
 * increase size of window.  Return 0 for success, -1 for fail.
 */
int wgrow ();

/* int wshrink(W *);
 *
 * Decrease size of window.  Returns 0 for success, -1 for fail.
 */
int wshrink ();

/* void wshowone(W *);
 *
 * Show only one window on the screen
 */
void wshowone ();

/* void wshowall(SCREEN *);
 *
 * Show all windows on the screen, including the given one
 */
void wshowall ();

/* void wredraw(W *);
 *
 * Force complete redraw of window
 */
void wredraw ();

/* void updall()
 *
 * Redraw all windows
 */
void updall ();

void genfmt ();
void gentxt ();
int fmtlen ();
int fmtpos ();

/* void msgnw[t](W *w,char *text);
 * Display a message which will be eliminated on the next keypress.
 * msgnw displays message on bottom line of window
 * msgnwt displays message on top line of window
 */
void msgnw ();
void msgnwt ();

#define MSGBUFSIZE 80
extern char msgbuf[MSGBUFSIZE];	/* Message composition buffer for msgnw/msgnwt */

void msgout ();			/* Output msgnw/msgnwt messages */

/* Common user functions */

int urtn ();			/* User hit return */
int utype ();			/* User types a character */
int uretyp ();			/* Refresh the screen */
int ugroww ();			/* Grow current window */
int uexpld ();			/* Explode current window or show all windows */
int ushrnk ();			/* Shrink current window */
int unextw ();			/* Goto next window */
int uprevw ();			/* Goto previous window */

void scrdel ();
void scrins ();

#endif
joe-2.9.6/INFO0100600003233700235640000001502607262717537012370 0ustar  xgracstudent

                             Joe's Own Editor 2.9.x

                  A Free ASCII-Text Screen Editor for UNIX



	JOE is the professional freeware ASCII text screen editor for UNIX. 
It makes full use of the power and versatility of UNIX, but lacks the steep
learning curve and basic nonsense you have to deal with in every other UNIX
editor. JOE has the feel of most IBM PC text editors: The key-sequences are
reminiscent of WordStar and Turbo-C.  JOE is much more powerful than those
editors, however.  JOE has all of the features a UNIX user should expect:
full use of termcap/terminfo, excellent screen update optimizations (JOE is
fully useable at 2400 baud), simple installation, and all of the
UNIX-integration features of VI.

	JOE's initialization file determines much of JOE's personality and
the name of the initialization file is simply the name of the editor
executable followed by "rc".  JOE comes with four "rc" files in addition to
the basic "joerc", which allow it to emulate these editors:

	JPICO	- An enhanced version of the Pine mailer system's PICO
		  editor.

	JSTAR	- A complete immitation of WordStar including many "JOE"
		  extensions.

	RJOE	- A restricted version of JOE which allowed you to edit
		  only the files specified on the command line.

	JMACS	- A GNU-EMACS immitation which is about one order of
		  magnitude smaller than real GNU-EMACS.

Features:

	JOE has a well thought-out user-interface with great attention to
detail.  The Page Up and Page Down functions do not move the cursor relative
to the edges of the screen.  Left and Right arrow keys work at the beginning
and ends of lines.  The cursor can move past the ends of lines without
jumping, but also without inserting or deleting extra spaces at the ends of
lines. Control characters and characters above 127 can be displayed and
entered- even ^Q and ^S.  The cursor's row and column number can be
displayed in the status line.

	The key layout is made to reduce terminal incompatibility nonsense. 
^Q and ^S are not used and both ^H and DEL are mapped to backspace.  Case
does not matter in key sequences- ^K E, ^K e, and ^K ^E are each mapped to
the same function.  The arrow keys and PageUp, PageDown, Home, End, Insert
and Delete keypad keys are read from the termcap entry and are assigned to
the proper functions.  A simple initialization file, similar to Semware's
Q-EDIT, allows key-bindings, simple macros and help windows to be defined.

	JOE has full termcap/terminfo support and will work on any terminal. 
JOE has the best screen update optimization algorithm available.  It uses
VT100-style scrolling regions the way they are supposed to be used (I.E.,
without building insert and delete line functions out of them) and has a
powerful line shifting (insert/delete character) algorithm which works even
if text goes past the ends of lines.  JOE has deferred screen update to
handle typeahead and uses the baud rate reported by 'stty' to ensure that
deferral is not bypassed by tty buffering.

	JOE has multiple windows and lacks the confusing notion of a named
buffers.  You just have files and windows.  When there are more windows than
can fit on the screen, the Goto-Next-Window function scrolls through them. 
The same file can have multiple windows opened on it.

	JOE has VI-style unix integration.  You can filter a highlighted
block through a UNIX command.  Also, each place in joe which accepts a file
name (including the command line) will also accept:

		!command		to pipe into or out of a command
		>>filename		to append to a file
		filename,start,size	to edit a portion of a file/device
		-			to use stdin or stdout

	File names on the command line may be preceeded by +nnn to start
editing at a specified line.

	JOE has shell windows.  You can run a shell in a window and any
output from commands run in the shell gets stored in a buffer.

	JOE has an orthogonal event-driven design.  Each prompt is actually
a normal edit buffer containing a history of all of the responses entered
for that prompt.  You can use all of the normal edit commands to create file
names and search strings.  You can use the up arrow key (or search backwards
and any other appropriate edit command) to go back through the history of
previous responses.  Prompts are reentrant- meaning that edit commands which
require prompts can still be used inside of prompts.

	JOE has TAB-completion and file selection menus.  If you hit tab in
a file name prompt, the name is either completed or a menu of possible
matches appears.

	JOE stores edit files in a doubly linked list of gap buffers which
can spill into a temporary file.  You can edit files of any size up to the
amount of free disk space and there are no line-length restrictions.  Since
the buffering system is block-based, JOE will incur only a minimum of
swapping on heavily loaded systems.

	When you ask for help, one of six small help reference cards appears
on the screen and remains while you continue to use the editor.  Here is the
first help card:

CURSOR           GO TO            BLOCK      DELETE   MISC         EXIT
^B left ^F right ^U  prev. screen ^KB begin  ^D char. ^KJ reformat ^KX save
^P up   ^N down  ^V  next screen  ^KK end    ^Y line  ^T  options  ^C  abort
^Z previous word ^A  beg. of line ^KM move   ^W >word ^@  insert   ^KZ shell
^X next word     ^E  end of line  ^KC copy   ^O word< ^R  retype   FILE
SEARCH           ^KU top of file  ^KW file   ^J >line SPELL        ^KE new
^KF find text    ^KV end of file  ^KY delete ^_ undo  ^[N word     ^KR insert
^L  find next    ^KL to line No.  ^K/ filter ^^ redo  ^[L file     ^KD save

	JOE has a powerful set of editing commands suitable for editing both
text files and programs:

		- search and replace system, including powerful regular 
		  expressions (including matching of balanced C expressions).

		- tags file search

		- paragraph format

		- undo and redo

		- position history allows you to get back to previous
		  editing contexts and allows you to quickly flip between
		  editing contexts

		- multiple keyboard macros

		- block move/copy/delete/filter

		- rectangle (columnar) mode

		- overtype/insert modes

		- indent/unindent

		- goto matching ( [ {

		- auto-indent mode

	Plus many options can be set:

		- can have EMACS-style cursor re-centering on scrolls

		- characters between 128-255 can be shown as-is for
		  non-english character sets

		- Final newline can be forced on end of file

		- Can start with a help screen on

		- Left/Right margin settings

		- Tab width

		- Indentation step and fill character

Any questions, problems, suggestions send to Marek 'Marx' Grac 
				(xgrac@fi.muni.cz)

joe-2.9.6/LIST0100600003233700235640000001150705703421055012371 0ustar  xgracstudentJoe commands grouped by function

Background programs
-------------------
bknd		Run a shell in a window
killproc	Kill program in current window
run		Run a unix command in a window

Blocks
------
blkcpy		Copy marked block to cursor
blkdel		Delete marked block
blkmove		Move marked block to cursor
blksave		Save marked block into a file
copy		Copy block to kill-ring
drop		Set markb.  If it was already set, eliminate it.
filt		Filter block or file through a unix command
markb		Set beginning of block mark
markk		Set end of block mark
markl		Mark current line
nmark		Eliminate markb and markk
picokill	Delete line or block
pop		Restore markb and markk values from stack
psh		Push markb and markk values onto a stack
swap		Switch cursor with markb
tomarkb		Move cursor to markb
tomarkbk	Move cursor to markb or markk
tomarkk		Move cursor to markk
yank		Insert top of kill ring
yankpop		Scroll through kill ring
yapp		Append next kill to top of kill ring

Buffers
-------
bufed		Buffer menu
edit		Load file into window
nbuf		Load next buffer into current window
pbuf		Load previous buffer into current window

Cursor Motion
-------------
bof		Move cursor to beginning of file
bol		Move cursor to beginning of line
bop		Move to beginning of a paragraph
bos		Move to beginning of screen
bkwdc		Search backwards for a character
byte		Move cursor to specific byte offset into the file.
col		Move cursor to specific column number.
dnarw		Move cursor down one line
eof		Move cursor to end of file
eol		Move cursor to end of line
eop		Move cursor to end of paragraph
fwrdc		Search forward for matching character
gomark		Move cursor to a bookmark
line		Move cursor to specified line
ltarw		Move cursor left
nedge		Move cursor to next edge
nextpos		Move cursor to next position in cursor position history
nextword	Move cursor to end of next word
pedge		Move cursor to previous edge
prevpos		Move cursor to previous position in cursor position history
prevword	Move cursor to beginning of previous word
rtarw		Move cursor right
setmark		Set a bookmark
tomatch		Move cursor to matching delimiter
tos		Move cursor to top of screen
uparw		Move cursor up

Deletion
--------
backs		Backspace
backw		Backspace a word
delbol		Delete to beginning of line
delch		Delete character under cursor
deleol		Delete to end of line
dellin		Delete entire line
delw		Delete word to right

Error parsing
-------------
nxterr		Goto next parsed error
parserr		Parse errors in current file
prverr		Go to previous parsed error

Exit
----
abort		Abort current buffer/window.  Prompt if it is changed.
abortbuf	Abort current buffer/window if it is not changed
ask		Prompt to save current file
exsave		Save file and exit
lose		Prompt to lose changes to current file

Files
-----
save		Save file
insf		Insert a file

Formatting
----------
center		Center line
fmtblk		Format all paragraphs in a block
format		Format current paragraph
lindent		Indent to the left
rindent		Indent to the right

Help
----
help		Turn help on or off
hnext		Switch to next help screen
hprev		Switch to previous help screen

Inserting
---------
ctrl		Type next key
insc		Insert a space 
open		Insert newline
quote		Insert a control character
quote8		Insert a meta character
rtn		Return key
type		Insert typed character

Macros
------
macros		Insert keyboard macros into current file
play		Execute a macro
query		Macro query
record		Record a macro
stop		Stop recording macro

Menu
----
backsmenu	Undo in file completion menu
bofmenu		Move to beginning of menu
bolmenu		Move to beginning of line in a menu
dnarwmenu	Move down one line in a menu
eolmenu		Move cursor to end of line in a menu
eofmenu		Move cursor to end of menu
ltarwmenu	Move cursor left in a menu
rtarwmenu	Move cursor right in menu
uparwmenu	Move cursor up in menu

Misc
----
execmd		Execute a joe command
math		Calculator
mode		Mode prompt
msg		Display a message
notmod		Clear the modified flag
retype		Refresh screen
shell		Suspend process or execute a sub-shell
stat		Display cursor position
tag		Tags file search
txt		Insert text

Prompts
-------
complete	Complete a file-name in a prompt

Repeat
------
arg		Prompt for repeat argument
uarg		Universal argument

Scrolling
---------
crawll		Pan screen left
crawlr		Pan screen right
dnslide		Scroll screen down 1 line
pgdn		Scroll screen down
pgup		Scroll screen up
upslide		Scroll up one line

Search and replace
------------------
ffirst		Find text
fnext		Repeat previous search
isrch		Incremental search forward
qrepl		Search and replace
rfirst		Search backwards for text
rsrch		Reverse incremental search

Windows
-------
explode		Display one window or display all windows
dupw		Duplicate current window
groww		Increase size of window
nextw		Move cursor to next window
prevw		Go to previous window
shrinkw		Shrink window
splitw		Split window into two
tw0		Eliminate this window
tw1		Show only one window

Undo
----
redo		Re-execute the latest undone change
undo		Undo last change
joe-2.9.6/NEWS0100600003233700235640000001455107265402263012401 0ustar  xgracstudentOverview of Changes in JOE 2.9.6 (13 Apr 2001)

- JOE can be compiled on Windows platform again
- BUGFIX:: problem with ${sysconfdir}/joerc was solved
- BUGFIX:: security patch for sprintf
- BUGFIX:: partially solved problem on Solaris with SegFault
- BUGFIX:: patch joe-2.8-security (slightly changed)
- BUGFIX:: patch joe-2.8-port
- BUGFIX:: patch joe-2.8-mips
- BUGFIX:: patch joe-2.8-vsmk
- BUGFIX:: patch joe2.8-time
- *rc files where moved from $(prefix)/lib to $(prefix)/etc or $sysconfdir
- Makefile.in (and Makefile) was rewritten
   - special prefix for package (more in Makefile.in)
   - use of system independent 'mkinstalldirs'
   - rc files are not rewritten
- TEST FEATURE:: added autoconf support
		program can be installed by ./configure; make; make install
- BUGFIX (v2.9.4):: go to previous word problem solved
- JOE can be compiled without locale.h again
- BUGFIX:: patch joe2.8-axphack.patch
- BUGFIX:: patch joe2.8-resize2.patch
- BUGFIX:: fixed problem with :include in rc files
- BUGFIX (v2.9.5):: portability problem with is_blank on nonGNU systems

Overview of Changes in JOE 2.9.5 (28 Mar 2001)

- new BUG:: can't be compiled on non-GNU systems (is_blank()) fixed in v2.9.6
- BUGFIX:: Fixed problem with resizing. 
- SECURITY:: .[joe|rjoe|jpico|..]rc in actual directory is ignored
             because in this file can be defined which program run.

Overview of Changes in JOE 2.9.4 (27 Mar 2001)

- new BUG:: go to previous word; goes one character before this word 
            fixed in v2.9.6
- FEATURE:: locale (LC_CTYPE) is accepted when skipping/deleting/... words	

Overview of Changes in JOE 2.9  (22 Mar 2001)

- version 2.8 with patches from RedHat/Suse

Overview of Changes in JOE 2.8

- Fixed problem with TERMPATH string
- Added stupid two-letter names to termcap file
- Improved jmacs help and command set
- Improved README file

Overview of Changes in JOE 2.7

- putenv() was not portable
- utime was not portable
- special utime handling for NeXT
- forgot to \\ the \s in the default termcap entry
- changed some key defaults in jpicorc
- add IXOFF in termio/termios list
- left margin limit was incorrect
- allow '.' and '/' in file names for error parsing
- Needed ptem.h and stream.h for SCO_UNIX window size structure (?)
- wordwrap no longer propogates indent prefix
- paragraph format was broken for tab indented paragraphs
- pipe through shell now goes through stderr too
- added '-crlf' option
- looks for termcap file in JOERC/termcap

Overview of Changes in JOE 2.6

- Fixed stupid bug in termcap.c which prevented terminfo from working
- ESC h was missing from jpicorc
- Changes suggested by Dan Nelson:
   - backup files now attempt to have same permissions and times as original
   - Stat command now ands chars with 255 (don't know why this worked on my system
     without this...)
   - Maybe change shell invocation name- have to check this more

Overview of Changes in JOE 2.5

- No longer use ^[ O, ^[ A, ^[ B, ^[ C, or ^[ D for anything because they
  interfere with arrow keys.
- Couldn't create new files because of bug in readonly setting
- fwrdc/bkwdc were crashing the editor except when called from wordstar
- 'tr' command was not called in a portable way in jmacs
- 'tr' was causing problems with the spell-check macros as well
- filter region was not working: had to add 'query' in ^[ | macro
- Changed incremental search so that whatever key the command is bound to
  can be used to repeat the search (used to only be able to use ^S)

Overview of Changes in JOE 2.4

- Closing message was incorrect when exit macros (macros where the last
  command is abortbuf) were used.
- SuperPico rc file added
- Write block now writes the entire file if no block is set
- Added pico kill function for pico emulation
  (tried to do this with 'psh,markk,blkdel' where blkdel deletes lines if
   no block is set, but it didn't group the deletes right in the yank
   buffer)
- Filter block would leave the marks set
- Fixed ^@ in joe mode
- Fixed help screen glitches in wordstar mode
- If joe can't make a backup file it now prompts for you to save anyway
- Eliminated IDLEOUT compile option.  Now is the user gives - on the
  command line, joe uses /dev/tty.
- Added %o %O %a %A %X and %R status line messages
- Starts out in read only mode if loaded file can not be written to
- If joe can't find the termcap/terminfo entry, it instead uses the default
- termcap routines are now included even if you use terminfo.  If your
  terminal can't be found in the terminfo database, it will look in
  the termcap database too.
- The JOETERM environment variable can be used to bypass the TERM
  environment variable setting.

Overview of Changes in JOE 2.3

- Search & Replace bugs fixed
   - replace would mess up the end mark of a marked block
   - a search would trash ^KB if only ^KB was set
   - regex problem with \*
- Was using TCSANOW in posix driver.  Should have been using TCSADRAIN
- Format paragraph now correctly formats quoted news articles
- Attempted fix for SCO
- Fix for coherent
- Fix for old IRIX/SGI
- Fixed bug where if you used search & replace in a macro, and exiting the
  search & replace loop with right arrow key, then when you played the macro
  you got an extra ^[ in the input queue
- Added file hooks
- Added function to insert current keyboard macro into current file
- Added range checks to numeric option settings
- Restricted joe rc file added
- Added ':def' feature for rc files

Overview of Changes in JOE 2.2

- First attempt at MS-DOS version of joe
   - Direct screen write
   - Modifications for dos file/drive names
   - Use TEMP variable to figure out where to store temporary file
   - Smaller virtual memory page size
   - Backslashes in file name problem
   - CR before an LF looks like an LF
- Backward search ignore-case was not working
- Scalable window height was not working fully
- Spaces in file-names gave a problem with backup file creation
- TILDE option is not available in all versions of BSD
- Allow : as seperate for termcap filename list
- Next error / Prev. error was not tracking right
- tabs not displayed right in nxterr/prverr messages
- Block moves where the cursor was to the right of the block was broken

Overview of Changes in JOE 2.1

- rc file wasn't giving correct error messages for missing options
- the '-nobackups' options was mispelled: '- nobackups'
- editor was crashing because of problem in undo
- update bypass in utype has a problem when wordwrapping and scrolling
joe-2.9.6/TODO0100600003233700235640000000327707264422264012377 0ustar  xgracstudentFEATURES
=-=-=-=-
	-=- syntax higlighting
	-=- gettext (possibility of translate messages from joe)

	from joe2.8
	-=- picture mode
	-=- brief emulation? EDT emulation?
	-=- save editor state
	-=- CR-LF mode for joe editing DOS files in linux?
	-=- xedit and folding stuff
		- with seperate valid-line database?
		- display update algorithm will break
	-=- hexadecimal edit mode
	-=- fixed record length edit mode
	-=- verticle windows
		- difficult to get scrolling to work right

PROGRAM REDeSIGN:
=-=-=-=-=-=-=-=-=-=
	-=- why to use list of help screens and array of help screens
		when later is only array used ?
	-=- prepare sources to use gettext
	-=- add prototypes for functions
	-=- clean sources
	-=- add documentation

PORTABILITY
=-=-=-=-=-=
	-=- WARNING: MSDOS compatibility can be broken by my changes
		I try to use ANSI/ISO C but sometimes 
		I have to use POSIX extentsions so if you want to 
		have a DOS version, plz mail me your patches
	-=- MSDOS
		- how to give memory back during shell escapes?
	-=- make identifier length significant in first 8 (6?) places 
		for old C compilers


BuGS
=-=-
Emacs emulation improvements:
	- Rectangle mode for emacs yank system
	- Overtype mode works for yank
	- Query save loop? for kill-emacs and compile
	- Emacs reload from file function? ^X ^V 
		(it just works out if ^X ^V kills all instances of the buffer)
	- Bufed should display unnamed buffers
	- ESC args should work differently in emacs
	- nextword,nextword,prevword sequence 
		doesn't work for marking next successive word at ends of lines
	- exiting options and i-search with ESC is not pretty
		(it isn't in emacs either)
	- Allow either direct screen access or normal tty support 
		for DOS version.

joe-2.9.6/COPYING0100600003233700235640000003031005644713463012732 0ustar  xgracstudent
		    GNU GENERAL PUBLIC LICENSE
		     Version 1, February 1989

 Copyright (C) 1989 Free Software Foundation, Inc.
                    675 Mass Ave, Cambridge, MA 02139, USA
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

			    Preamble

  The license agreements of most software companies try to keep users
at the mercy of those companies.  By contrast, our General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users.  The
General Public License applies to the Free Software Foundation's
software and to any other program whose authors commit to using it.
You can use it for your programs, too.

  When we speak of free software, we are referring to freedom, not
price.  Specifically, the General Public License is designed to make
sure that you have the freedom to give away or sell copies of free
software, that you receive source code or can get it if you want it,
that you can change the software or use pieces of it in new free
programs; and that you know you can do these things.

  To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.

  For example, if you distribute copies of a such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have.  You must make sure that they, too, receive or can get the
source code.  And you must tell them their rights.

  We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.

  Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software.  If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.

  The precise terms and conditions for copying, distribution and
modification follow.

		    GNU GENERAL PUBLIC LICENSE
   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

  0. This License Agreement applies to any program or other work which
contains a notice placed by the copyright holder saying it may be
distributed under the terms of this General Public License.  The
"Program", below, refers to any such program or work, and a "work based
on the Program" means either the Program or any work containing the
Program or a portion of it, either verbatim or with modifications.  Each
licensee is addressed as "you".

  1. You may copy and distribute verbatim copies of the Program's source
code as you receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice and
disclaimer of warranty; keep intact all the notices that refer to this
General Public License and to the absence of any warranty; and give any
other recipients of the Program a copy of this General Public License
along with the Program.  You may charge a fee for the physical act of
transferring a copy.

  2. You may modify your copy or copies of the Program or any portion of
it, and copy and distribute such modifications under the terms of Paragraph
1 above, provided that you also do the following:

    a) cause the modified files to carry prominent notices stating that
    you changed the files and the date of any change; and

    b) cause the whole of any work that you distribute or publish, that
    in whole or in part contains the Program or any part thereof, either
    with or without modifications, to be licensed at no charge to all
    third parties under the terms of this General Public License (except
    that you may choose to grant warranty protection to some or all
    third parties, at your option).

    c) If the modified program normally reads commands interactively when
    run, you must cause it, when started running for such interactive use
    in the simplest and most usual way, to print or display an
    announcement including an appropriate copyright notice and a notice
    that there is no warranty (or else, saying that you provide a
    warranty) and that users may redistribute the program under these
    conditions, and telling the user how to view a copy of this General
    Public License.

    d) You may charge a fee for the physical act of transferring a
    copy, and you may at your option offer warranty protection in
    exchange for a fee.

Mere aggregation of another independent work with the Program (or its
derivative) on a volume of a storage or distribution medium does not bring
the other work under the scope of these terms.

  3. You may copy and distribute the Program (or a portion or derivative of
it, under Paragraph 2) in object code or executable form under the terms of
Paragraphs 1 and 2 above provided that you also do one of the following:

    a) accompany it with the complete corresponding machine-readable
    source code, which must be distributed under the terms of
    Paragraphs 1 and 2 above; or,

    b) accompany it with a written offer, valid for at least three
    years, to give any third party free (except for a nominal charge
    for the cost of distribution) a complete machine-readable copy of the
    corresponding source code, to be distributed under the terms of
    Paragraphs 1 and 2 above; or,

    c) accompany it with the information you received as to where the
    corresponding source code may be obtained.  (This alternative is
    allowed only for noncommercial distribution and only if you
    received the program in object code or executable form alone.)

Source code for a work means the preferred form of the work for making
modifications to it.  For an executable file, complete source code means
all the source code for all modules it contains; but, as a special
exception, it need not include source code for modules which are standard
libraries that accompany the operating system on which the executable
file runs, or for standard header files or definitions files that
accompany that operating system.

  4. You may not copy, modify, sublicense, distribute or transfer the
Program except as expressly provided under this General Public License.
Any attempt otherwise to copy, modify, sublicense, distribute or transfer
the Program is void, and will automatically terminate your rights to use
the Program under this License.  However, parties who have received
copies, or rights to use copies, from you under this General Public
License will not have their licenses terminated so long as such parties
remain in full compliance.

  5. By copying, distributing or modifying the Program (or any work based
on the Program) you indicate your acceptance of this license to do so,
and all its terms and conditions.

  6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the original
licensor to copy, distribute or modify the Program subject to these
terms and conditions.  You may not impose any further restrictions on the
recipients' exercise of the rights granted herein.

  7. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time.  Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.

Each version is given a distinguishing version number.  If the Program
specifies a version number of the license which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation.  If the Program does not specify a version number of
the license, you may choose any version ever published by the Free Software
Foundation.

  8. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission.  For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this.  Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.

			    NO WARRANTY

  9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.

  10. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.

		     END OF TERMS AND CONDITIONS

	Appendix: How to Apply These Terms to Your New Programs

  If you develop a new program, and you want it to be of the greatest
possible use to humanity, the best way to achieve this is to make it
free software which everyone can redistribute and change under these
terms.

  To do so, attach the following notices to the program.  It is safest to
attach them to the start of each source file to most effectively convey
the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.

    <one line to give the program's name and a brief idea of what it does.>
    Copyright (C) 19yy  <name of author>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 1, or (at your option)
    any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

Also add information on how to contact you by electronic and paper mail.

If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:

    Gnomovision version 69, Copyright (C) 19xx name of author
    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
    This is free software, and you are welcome to redistribute it
    under certain conditions; type `show c' for details.

The hypothetical commands `show w' and `show c' should show the
appropriate parts of the General Public License.  Of course, the
commands you use may be called something other than `show w' and `show
c'; they could even be mouse-clicks or menu items--whatever suits your
program.

You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary.  Here a sample; alter the names:

  Yoyodyne, Inc., hereby disclaims all copyright interest in the
  program `Gnomovision' (a program to direct compilers to make passes
  at assemblers) written by James Hacker.

  <signature of Ty Coon>, 1 April 1989
  Ty Coon, President of Vice

That's all there is to it!
joe-2.9.6/bw.c0100600003233700235640000004674007265143252012463 0ustar  xgracstudent/*
    Edit buffer window generation
    Copyright (C) 1992 Joseph H. Allen

    This file is part of JOE (Joe's Own Editor)
*/

#include <string.h>
#include <stdio.h>
#include "config.h"
#include "tty.h"
#include "vfile.h"
#include "termcap.h"
#include "kbd.h"
#include "b.h"
#include "scrn.h"
#include "w.h"
#include "ublock.h"
#include "utils.h"
#include "blocks.h"
#include "bw.h"

/* Display modes */
int dspasis = 0;
int marking = 0;
extern int square;
extern int staen;

P *getto (P *p, P *cur, P *top, long line) {
	long dist = MAXLONG;
	long d;
	P *best;

	if (!p) {
		if (d = (line - cur->line >= 0 ? line - cur->line : cur->line - line), d < dist) {
			dist = d, best = cur;
		}
		if (d = (line - top->line >= 0 ? line - top->line : top->line - line), d < dist) { 
			dist = d, best = top;
		}
		p = pdup (best);
		p_goto_bol (p);
	}
	while (line > p->line) {
		if (!pnextl (p)) {
			break;
		}
	}
	if (line < p->line) {
		while (line < p->line) {
			pprevl (p);
		}
		p_goto_bol (p);
	}
	return p;
}

/* Scroll window to follow cursor */

int mid = 0;

void bwfllw (BW *w) {
	P *newtop;

	if (w->cursor->line < w->top->line)
	  {
		  newtop = pdup (w->cursor);
		  p_goto_bol (newtop);
		  if (mid)
			  if (newtop->line >= w->h / 2)
				  pline (newtop, newtop->line - w->h / 2);
			  else
				  pset (newtop, newtop->b->bof);
		  if (w->top->line - newtop->line < w->h)
			  nscrldn (w->t->t, w->y, w->y + w->h,
				   (int) (w->top->line - newtop->line));
		  else
			  msetI (w->t->t->updtab + w->y, 1, w->h);
		  pset (w->top, newtop);
		  prm (newtop);
	  }
	else if (w->cursor->line >= w->top->line + w->h)
	  {
		  newtop = pdup (w->top);
		  if (mid)
			  newtop =
				  getto (NULL, w->cursor, w->top,
					 w->cursor->line - w->h / 2);
		  else
			  newtop =
				  getto (NULL, w->cursor, w->top,
					 w->cursor->line - (w->h - 1));
		  if (newtop->line - w->top->line < w->h)
			  nscrlup (w->t->t,
				   w->y,
				   w->y + w->h,
				   (int) (newtop->line - w->top->line));
		  else
			  msetI (w->t->t->updtab + w->y, 1, w->h);
		  pset (w->top, newtop);
		  prm (newtop);
	  }

/* Adjust column */
	if (w->cursor->xcol < w->offset)
	  {
		  w->offset = w->cursor->xcol;
		  msetI (w->t->t->updtab + w->y, 1, w->h);
	  }
	else if (w->cursor->xcol >= w->offset + w->w)
	  {
		  w->offset = w->cursor->xcol - (w->w - 1);
		  msetI (w->t->t->updtab + w->y, 1, w->h);
	  }
}

/* Scroll a buffer window after an insert occured.  'flg' is set to 1 if
 * the first line was split
 */

void
bwins (w, l, n, flg)
     BW *w;
     long l, n;
     int flg;
{
	if (l + flg + n < w->top->line + w->h && l + flg >= w->top->line
	    && l + flg <= w->b->eof->line)
	  {
		  if (flg)
			  w->t->t->sary[w->y + l - w->top->line] =
				  w->t->t->li;
		  nscrldn (w->t->t, (int) (w->y + l + flg - w->top->line),
			   w->y + w->h, (int) n);
	  }
	if (l < w->top->line + w->h && l >= w->top->line)
		if (n >= w->h - (l - w->top->line))
			msetI (w->t->t->updtab + w->y + l - w->top->line, 1,
			       w->h - (int) (l - w->top->line));
		else
			msetI (w->t->t->updtab + w->y + l - w->top->line, 1,
			       (int) n + 1);
}

/* Scroll current windows after a delete */

void
bwdel (w, l, n, flg)
     BW *w;
     long l, n;
     int flg;
{
/* Update the line where the delete began */
	if (l < w->top->line + w->h && l >= w->top->line)
		w->t->t->updtab[w->y + l - w->top->line] = 1;

/* Update the line where the delete ended */
	if (l + n < w->top->line + w->h && l + n >= w->top->line)
		w->t->t->updtab[w->y + l + n - w->top->line] = 1;

	if (l < w->top->line + w->h &&
	    (l + n >= w->top->line + w->h ||
	     l + n == w->b->eof->line
	     && w->b->eof->line >= w->top->line + w->h))
		if (l >= w->top->line)
			/* Update window from l to end */
			msetI (w->t->t->updtab + w->y + l - w->top->line, 1,
			       w->h - (int) (l - w->top->line));
		else
			/* Update entire window */
			msetI (w->t->t->updtab + w->y, 1, w->h);
	else if (l < w->top->line + w->h && l + n == w->b->eof->line &&
		 w->b->eof->line < w->top->line + w->h)
		if (l >= w->top->line)
			/* Update window from l to end of file */
			msetI (w->t->t->updtab + w->y + l - w->top->line, 1,
			       (int) n);
		else
			/* Update from beginning of window to end of file */
			msetI (w->t->t->updtab + w->y, 1,
			       (int) (w->b->eof->line - w->top->line));
	else if (l + n < w->top->line + w->h && l + n > w->top->line
		 && l + n < w->b->eof->line)
		if (l + flg >= w->top->line)
			nscrlup (w->t->t,
				 (int) (w->y + l + flg - w->top->line),
				 w->y + w->h, (int) n);
		else
			nscrlup (w->t->t, w->y, w->y + w->h,
				 (int) (l + n - w->top->line));
}

/* Update a single line */

static int
lgen (t, y, screen, x, w, p, scr, from, to)
     SCRN *t;
     int y;
     int *screen;		/* Screen line address */
     int w;			/* Window */
     P *p;			/* Buffer pointer */
     long scr;			/* Starting column to display */
     long from, to;		/* Range for marked block */
{
	int ox = x;
	int done = 1;
	long col = 0;
	long byte = p->byte;
	char *bp;		/* Buffer pointer, 0 if not set */
	int amnt;		/* Amount left in this segment of the buffer */
	int c, ta, c1;
	unsigned char bc;

/* Initialize bp and amnt from p */
	if (p->ofst >= p->hdr->hole)
	  {
		  bp = p->ptr + p->hdr->ehole + p->ofst - p->hdr->hole;
		  amnt = SEGSIZ - p->hdr->ehole - (p->ofst - p->hdr->hole);
	  }
	else
	  {
		  bp = p->ptr + p->ofst;
		  amnt = p->hdr->hole - p->ofst;
	  }

	if (col == scr)
		goto loop;
      lp:			/* Display next character */
	if (amnt)
		do
		  {
			  bc = *bp++;
			  if (p->b->o.crlf && bc == '\r')
			    {
				    ++byte;
				    if (!--amnt)
				      {
					    pppl:
					      if (bp == p->ptr + SEGSIZ)
						{
							if (pnext (p))
							  {
								  bp = p->ptr;
								  amnt =
									  p->
									  hdr->
									  hole;
							  }
							else
								goto nnnl;
						}
					      else
						{
							bp =
								p->ptr +
								p->hdr->ehole;
							amnt =
								SEGSIZ -
								p->hdr->ehole;
							if (!amnt)
								goto pppl;
						}
				      }
				    if (*bp == '\n')
				      {
					      ++bp;
					      ++byte;
					      ++amnt;
					      goto eobl;
				      }
				  nnnl:
				    --byte; ++amnt;
			    }
			  if (square)
				  if (bc == '\t')
				    {
					    long tcol =
						    col + p->b->o.tab -
						    col % p->b->o.tab;
					    if (tcol > from && tcol <= to)
						    c1 = INVERSE;
					    else
						    c1 = 0;
				    }
				  else if (col >= from && col < to)
					  c1 = INVERSE;
				  else
					  c1 = 0;
			  else if (byte >= from && byte <