#ifdef SHELL
cc -Wall -O2 -o ned ned.c -lcurses -ltermcap
exit
#endif
/* ned.c 	A simple four-function text editor
 *
 * ned is a simple text editor for those who can't be bothered with the
 * finickiness of vi and don't like waiting for emacs to drag its dripping
 * elephantine carcass into core just for the sake of making a quick edit
 * to a file.
 *
 * ned was originally written for MS-DOS because I needed a simple editor
 * that would work from a session CTTY'd down a COM port.  It can still
 * fulfill that role.
 *
 * Usage: ned [options] filename
 *
 * See usage() for options list.
 *
 * Simple commands:
 *	PF1, Ctrl/V		Function shift key (SHIFT below)
 *	Up, Down, Left, Right	Movement keys
 *	Ctrl/A			Beginning of line
 *	Ctrl/E			End of line
 *	PrevScreen, Ctrl/P	Up a screen
 *	NextScreen, Ctrl/N	Down a screen
 *	Ctrl/D			Delete character to right
 *	DEL, Ctrl/H		Delete character to left
 *	Ctrl/K			Delete to end of line
 *	Ctrl/U			Delete to beginning of line
 *	Select, Ctrl/B		Mark start of selection
 *	Remove, Ctrl/W		Cut from mark to cursor
 *	InsertHere, Ctrl/Y	Paste cut text
 *	Ctrl/J			Justify line
 *	Find, Ctrl/F		Find text
 *	Ctrl/G			Go to specific line number
 *	Ctrl/^			Insert control character
 *	Ctrl/L			Refresh screen
 *	Ctrl/C			Quit without saving
 *	Ctrl/X			Quit with save
 *
 * Shifted commands, key PF1 or Ctrl/V then command:
 *	PrevScreen, Ctrl/P	Go to top of file
 *	NextScreen, Ctrl/N	Go to bottom of file
 *	Find, Ctrl/F		Find text backwards
 *	i, I			Include file
 *	w, W			Save file under new name
 *	r, R			Replace found text with contents of cut buffer
 *	b, B			Go to new buffer
 *	n, N			Go to next buffer
 *	l, L			Load a file
 *
 * Above keys are DEC LK201/401 names; PC-101/104 equivalents are:
 *	PrevScreen	PageUp
 *	NextScreen	PageDn
 *	Find		Home
 *	Select		End
 *	Remove		Delete
 *	InsertHere	Insert
 *	DEL		Backspace
 *	PF1		NumLock or F1, depending on mapping (F1 on DOS)
 *
 * Current version tested by me under FreeBSD 4 & 5, Linux, MS-DOS (Turbo C
 * V2.0).  Also seen working under Solaris 2.6 and HPUX 10.20.  Older version
 * tested with Digital Unix & NetBSD; should work with most Unices.
 *
 * Compiling under Unix:
 *	cc -o ned ned.c -lcurses -ltermcap
 *	or just sh ned.c
 *
 * Compiling under DOS (Turbo C v2.0; later versions should be similar --
 * switches are just to turn off some overly paranoid warnings, to use
 * the large memory model, and enable emulation of curses functions)
 *	tcc -DDOS -ml -w-pia -w-par ned
 *
 * Note: Under DOS requires an ANSI display, ie an ANSI console driver
 *	 or an ANSI terminal (eg VT100) if using ned from a serial port.
 *	 (The ANSI emulation required is very basic: ESC [ <row> ; <col> H
 *	 to move, ESC [ K to clear to EOL, ESC [ 7 m and ESC [ m to start
 *	 and end standout mode.)  A faster ANSI driver than ANSI.SYS can
 *	 make a big difference to ned's usability.
 *
 *	 Curses is used under Unix to provide terminal independence.
 *
 * Author:
 *	Don Stokes
 *	Daedalus Consulting Services
 *	Email: don@daedalus.co.nz
 *
 * Modifications (since v0.7):
 *	8/12/98/dcs	0.8u
 *		Added horizontal panning
 *		Justify line fixed to work on last line of file, also
 *		move cursor to end of line.
 *	3/1/99/dcs	0.8u1
 *		Ned can now edit binary files
 *		Command line changed, use switches
 *		Added text/binary mode, do the Right Thing under DOS or Unix
 *		Added auto-wrap
 *		Can start with cursor on arbitrary row or column, negative
 *		row/col count from ends (-1 = end, -2, one from end etc).
 *		Added buffers
 *	13/2/99/dcs	0.8u2
 *		Use cbreak mode instead of raw.
 *	6/6/99/dcs	0.8u3
 *		'+n' equivalent to '-r n' (vi compatible)
 *		Reinstated line overflow marker
 *		Bugfixes in multibuffer stuff
 *		Made cbreak mode more raw (but without disabling ^Z & XON/XOFF)
 *	12/1/00/dcs	0.8d3
 *		Compiles with TC2 under DOS once again.  Couple of bugs fixed.
 *		DOS F1 works as PF1.
 *	12/4/00/dcs	0.8u4
 *		Added pipes in file write and include operations.  Also
 *		-s (secure) switch to disable pipes.
 *	3/8/00/dcs	0.8u5
 *		Added journalling.  Journal written to <file>.jou, records
 *		inserts and deletes into file.  See journal() for codes.
 *		Lock journal file to ensure only one writer.
 *		-j option disables journal but still checks for existing
 *		journal file to permit recovery
 *		Fixed auto-wrap.
 *		Added -n to disable .bak files.
 *	6/10/00/dcs	0.8u6
 *		Changed PageUp from Ctrl/U to Ctrl/P, Ctrl/U is now DELBOL
 *		and deletes to the beginning of the line or the previous
 *		newline if already at BOL.
 *		Treat Linux console F1-F4 as PF1-PF4
 *		Treat NetBSD console F1-F4 as PF1-PF4
 *		Fix QUOTE function
 *	15/3/01/dcs	0.8u7
 *		Add checkpoint (-C) facility
 *	10/4/04/dcs	0.8u8
 *		Fix ANSI sequence parsing so <esc>[n;nA is parsed correctly
 *		(Xterm generates <esc>[1;2A and <esc>[1;5A for shift-up-arrow
 *		and control-up-arrow respectively, which was getting annoying.)
 *		Provide support for FreeBSD console key sequences.
 *	11/6/07/dcs	0.8u8g
 *		License change to GPL
 *	21/10/10/dcs	0.8u9
 *		Fix journaling of line justify function.
 *		Fix screen resize processing
 */

/* Copyright 1996-2010 Don Stokes.
 *
 * 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 2
 * of the License, 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., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA.
 */

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
/* #define DOS */ /* Uncomment for MSDOS (or use -DDOS) */

#define VERSION "ned v0.8u9"

#define MINBUF 4096
#define MAXBUF 80

struct buffer {
	struct buffer *next;
	char *name;
	char *jfile;
	FILE *journal;
	unsigned char *buffer;
	unsigned int cursor, bufsize, cutpoint, bufalloc, lastjpos;
	int selactive:1, readonly:1, autowrap:1, modified:1,
		isfile:1, isuser:1, ismain:1, backedup:1;
};

struct buffer *bufferlist = 0, *buf_curr = 0;
struct buffer *buf_main = 0, *buf_cut, *buf_find, *buf_temp, *buf_keys;

unsigned char *buffer = 0;
unsigned int cursor,scrtop,bufsize,cutpoint,bufalloc,autoclear;
char *showmessage = 0;
char scrbuf[256];
int row,col,actualcol,leftmargin;
int ccol;
int refstate;
unsigned int refpos;
int selactive;
char *filename;
int modified = 0;
int wrapwidth = 72;
int autowrap = 0;
int readonly = 0;
char *inputprompt = 0;
int controlc = 0;
int nojournal = 0;
int nobackup = 0;
int checkpoint = 0;
time_t lastcp = 0;

#define TRACE(m)

#define REFEOL 1
#define REFEOS 2
#define REFSCR 4
#define REFSTA 8

#define UPARR 256
#define DOWN 257
#define LEFT 258
#define RIGHT 259
#define END 5		/* Ctrl/E */
#define HOME 1		/* Ctrl/A */
#define PGUP 16		/* Ctrl/P */
#define PGDOWN 14	/* Ctrl/N */
#define DEL 127		/* DEL */
#define BKSP 8		/* Backspace */
#define DELF 4		/* Ctrl/D */
#define DELEOL 11	/* Ctrl/K */
#define DELBOL 21	/* Ctrl/U */
#define TAB 9		/* Tab */
#define CRET 13		/* CR */
#define LFEED 10	/* LF (bloody unix!) */
#define REFR 12		/* Ctrl/L */
#define EXIT 24		/* Ctrl/X */
#define SELECT 2	/* Ctrl/B */
#define CUT 23		/* Ctrl/W */
#define PASTE 25	/* Ctrl/Y */
#define FIND 6		/* Ctrl/F */
#define ABORT 3		/* Ctrl/C */
#define QUOTE 30	/* Ctrl/^ */
#define SHIFT 22	/* Ctrl/V */
#define GOTO 7		/* Ctrl/G */

/*
Hackery to emulate what little of curses this thing uses on a DOS box without
a curses library, and to ignore unixisms
*/
#ifdef DOS
#include <dos.h>

int textmode = 1;

void
move(int row, int col) {
	printf("\033[%d;%dH", row+1,col+1);
}
void
clrtoeol(void) {
	printf("\033[K");
}
void
standout(void) {
	printf("\033[7m");
}
void
standend(void) {
	printf("\033[m");
}
int scrlines = 0;
int
getlines(void) {
	union REGS r;
	struct SREGS s;
	unsigned char vsb[64];
	if(scrlines) return scrlines;
	r.h.ah = 0x1b;
	r.x.di = FP_OFF(vsb);
	r.x.bx = 0;
	s.es = FP_SEG(vsb);
	int86x(0x10, &r, &r, &s);
	scrlines = vsb[34];
	if(scrlines == 25) scrlines = 24;
	return scrlines;
}
static char popen_cmd[1024] = "";
static int popen_ret = 0;
FILE *					/* note: popen()/pclose() can run */
popen(char *cmd, char *rw) {		/* only one command at a time     */
	FILE *f = 0;
	if(*rw == 'w') {
		f = fopen("C:\\popen.tmp", "w");
		sprintf(popen_cmd, "%s <C:\\popen.tmp", cmd);
	} else if(*rw == 'r') {
		sprintf(popen_cmd, "%s >C:\\popen.tmp", cmd);
		popen_ret = system(popen_cmd);
		f = fopen("C:\\popen.tmp", "r");
		*popen_cmd = 0;
	}
	return f;
}
int
pclose(FILE *f) {
	fclose(f);
	if(*popen_cmd) popen_ret = system(popen_cmd);
	unlink("C:\\popen.tmp");
	return popen_ret;
}

#define addstr(s) fputs(s, stdout)
#define addch(c) putchar(c)
#define touchwin(s)
#define readch getch
void refresh(void) {}
void setupscreen(void) {}
void restorescreen(void) {}
#define LINES getlines()
#define COLS 80
#define chmod(f,m)
#define chown(f,o,g)
#define lstat(f,b) (0)
#define signal(s,a)
#define FOPEN_READ "rb"
#define FOPEN_RDWR "rb+"
#define FOPEN_WRITE "wb"

#else

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <signal.h>
#include <string.h>
#include <curses.h>
#include <termios.h>
#include <unistd.h>

struct stat statbuf;
int textmode = 0;

#define FOPEN_READ "r"
#define FOPEN_RDWR "r+"
#define FOPEN_WRITE "w"

struct termios tio, stio;
int killios[] = { VQUIT, VINTR,
#ifndef _POSIX_SOURCE
		 VDSUSP, VLNEXT, VDISCARD, VSTATUS,
#endif
		 -1};

void
setupscreen(void) {
	int i;

	tcgetattr(0, &tio);
	stio = tio;
	for(i = 0; killios[i] >= 0; i++) tio.c_cc[killios[i]] = _POSIX_VDISABLE;
	tcsetattr(0, TCSADRAIN, &tio);
	initscr();
	noecho();
	cbreak();
	nonl();
	refresh();
}

void
restorescreen(void) {
	echo();
	nocbreak();
	nl();
	endwin();
	tcsetattr(0, TCSADRAIN, &stio);
}

#endif /* DOS */

int secure = 0;

unsigned int
findbol(unsigned int c) {
	TRACE("findbol")
	for(; c > 0 && buffer[c-1] != '\n'; c--);
	return c;
}

int
findpos(unsigned int pos) {
	unsigned int c;
	int r, rr;
	int lines, cols;

	TRACE("findpos")

	if(scrtop >= bufsize) scrtop = bufsize;		/* Paranoia */

	lines = LINES;
	cols = COLS;
	if(inputprompt) {
		lines = 1;
		cols = COLS - strlen(inputprompt) - 1;
	}

	r = 0;
	if(scrtop > pos) {
		rr = 0;
		for(c = pos; c < scrtop; c++) if(buffer[c] == '\n') rr--;
		scrtop = findbol(pos);
		row = 0;
		col = 0;
		for(c = scrtop; c < pos; c++) {
			col++;
			if(buffer[c] == TAB) for(; (col % 8) != 0; col++);
			else if(buffer[c] < 32 || buffer[c] == 255 ||
				(buffer[c] > 126 && buffer[c] < 160)) col++;
		}
	} else {
		col = 0;
		rr = 0;
		for(c = scrtop; c < bufsize && c != pos; c++) {
			col++;
			if(buffer[c] == '\n') {
				r++;
				col = 0;
			} else if(buffer[c] == TAB) {
				for(; (col % 8) != 0; col++);
			} else if(buffer[c] < ' ' || buffer[c] == 255 ||
				  (buffer[c] > 126 && buffer[c] < 160)) col++;
		}
		row = r;
		if(r >= lines-1) {
			c = scrtop;
			scrtop = findbol(pos);
			for(; c < bufsize && c != pos; c++) {
				if(buffer[c] == '\n') {
					r--;
					if(r < lines-1) {
						rr = r;
						scrtop = c + 1;
						break;
					}
				}
			}
			rr = r;
		}
	}
	actualcol = col;
	col -= leftmargin;
	if(col < 0) col = 0;
	if(col >= cols) col = cols-1;
	return rr;
}

void
setref(int state) {
	unsigned int c;

	TRACE("setref")
	if(state == REFSCR || state == REFSTA || state == (REFSCR|REFSTA)) {
		refstate |= state;
		return;
	}
	if(state == REFEOS) {
		if((refstate & REFEOS) && cursor > refpos) return;
		refstate |= REFEOS;
		if((refstate & REFEOL) && cursor > refpos) return;
		refpos = cursor;
	} else if(state == REFEOL) {
		if(refstate & REFEOS) {
			if(cursor < refpos) refpos = cursor;
			return;
		}
		if(refstate & REFEOL) {
			if(cursor < refpos) {
				for(c = cursor; c < refpos; c++) {
					if(buffer[c] == '\n') {
						state |= REFEOS;
						refpos = cursor;
						return;
					}
				}
			} else if(cursor > refpos) {
				for(c = refpos; c < cursor; c++) {
					if(buffer[c] == '\n') {
						refstate |= REFEOS;
						return;
					}
				}
			}
		}
		refpos = cursor;
		refstate |= REFEOL;
	}
}

void
message(char *msg, int sts) {
	showmessage = msg;
	setref(REFSTA);
}

void
refrscr(void) {
	int rstate;
	unsigned int c;
	int i,r, co, cos, oc, modc;
	unsigned char outbuf[32];
	int lines, cols, top, left;

	lines = LINES-1;
	cols = COLS;
	top = left = 0;

	if(inputprompt) {
		lines = 1;
		top = LINES-1;
		left = strlen(inputprompt);
		cols = COLS - left - 1;
		if(refstate | REFSTA) refstate = REFSCR;
		showmessage = 0;
	}

	TRACE("refrscr")
	if(refstate & REFSTA || showmessage) {
		move(lines,0);
		standout();
		modc = ' ';
		if(modified) modc = '*';
		if(readonly) modc = '!';
		if(showmessage) if(!showmessage[0]) showmessage = 0;
		if(showmessage) {
			if(showmessage != scrbuf) {
				strncpy(scrbuf, showmessage, 255);
				scrbuf[255] = 0;
			}
			if((i = strlen(scrbuf)) < 39) {
				for(; i < 40; i++) scrbuf[i] = ' ';
				scrbuf[39] = modc;
				strncpy(&scrbuf[40], filename, 200);
			}
			showmessage = "";
		} else sprintf(scrbuf, "%-10s%-29s%c%s",
			VERSION, " (Ctrl/X to exit and save)", modc, filename);
		for(i = strlen(scrbuf); i < 255 && i < cols-1; i++)
			scrbuf[i] = ' ';
		if(i > cols-1) i = cols-1;
		scrbuf[i] = 0;
		addstr(scrbuf);
		standend();
	}

	rstate = refstate & (REFSCR|REFEOS|REFEOL);
	if(findpos(cursor)) rstate = REFSCR;
	if(leftmargin && actualcol < cols && ccol < cols) {
		for(c = cursor; c < bufsize && buffer[c] != '\n'; c++) ;
		if((c - cursor) + actualcol < cols) {
			leftmargin = 0;
			rstate = REFSCR;
		}
	}
	while(actualcol >= leftmargin + cols) {
		rstate = REFSCR;
		leftmargin += 8;
	}
	while(actualcol < leftmargin) {
		rstate = REFSCR;
		leftmargin -= 8;
	}
	col = actualcol - leftmargin;

	if(!rstate) {
		findpos(cursor);
		goto done;
	}
	if(rstate & REFSCR) refpos = scrtop;
	if(findpos(refpos)) {
		rstate = REFSCR;
		refpos = scrtop;
		row = col = actualcol = 0;
	}

	if(inputprompt) {
		standout();
		move(top, 0);
		addstr(inputprompt);
	}
	r = row;
	co = cos = actualcol;
	move(row+top, col+left);

	for(c = refpos; r < lines && c < bufsize; c++) {
		cos = co;
		co++;
		if(buffer[c] == '\n') {
			if(cos < cols+leftmargin) {
				if(inputprompt)
					for(; co<=cols; co++) addch(' ');
				else clrtoeol();
			}
			if(rstate == REFEOL) {
				findpos(cursor);
				refstate = 0;
				goto done;
			}
			r++;
			move(r+top,left);
			co = 0;
			continue;
		}

		oc = 0;
		if(buffer[c] == TAB) {
			outbuf[oc++] = ' ';
			for(; co % 8; co++) outbuf[oc++] = ' ';
		} else if(buffer[c] < 32) {
			outbuf[oc++] = '^';
			outbuf[oc++] = buffer[c] + '@';
			co++;
		} else if(buffer[c] == 127) {
			outbuf[oc++] = '^';
			outbuf[oc++] = '?';
			co++;
		} else if(buffer[c] >= 128 && buffer[c] < 160) {
			outbuf[oc++] = '&';
			outbuf[oc++] = buffer[c]-128 + '@';
			co++;
		} else if(buffer[c] == 255) {
			outbuf[oc++] = '&';
			outbuf[oc++] = '?';
			co++;
		} else outbuf[oc++] = buffer[c];

		for(i = 0; i < oc; i++) {
			if(cos+i >= leftmargin && cos+i < leftmargin+cols) {
				if(outbuf[i] == 160 && !inputprompt) {
					standout();
					addch(' ');
					standend();
				}
				else addch(outbuf[i]);
			}
		}
		if(oc+cos > leftmargin+cols) {
			move(r+top, left+cols-1);
			standout();
			addch('+');
			standend();
		}
		cos += oc;
	}
	if(r < lines) {
		if(inputprompt) for(; co < leftmargin+cols; co++) addch(' ');
		else if(co < leftmargin+cols) {
			clrtoeol();
			if(c == bufsize) {
				standout();
				for(i=0; i < 5 && co+i < leftmargin+cols; i++)
					addch("[eof]"[i]);
				standend();
			}
		}
	}
	while(++r < lines) {
		move(r+top, left);
		clrtoeol();
	}
	if(inputprompt) standend();
	findpos(cursor);
	refstate = 0;

done:	move(row+top, col+left);
}

static int needredraw = 0;
void
redraw_handler(int sig) {
	needredraw = 1;
}

void
redraw(int k) {
	TRACE("redraw")
	restorescreen();
	setupscreen();
	setref(REFSCR);
	setref(REFSTA);
	refrscr();
	touchwin(stdscr);
	move(row,col);
	refresh();
}

void
controlctrap() {
	controlc = 1;
}

unsigned char *
setbufsize(unsigned int newsize) {
	unsigned char *b;
	TRACE("setbufsize")
	if(!buffer) {
		buffer = (unsigned char *)malloc(MAXBUF+1);
		bufalloc = MAXBUF;
	}
	if(newsize+2 > bufalloc) {
		if(!(b = (unsigned char *)realloc(buffer, newsize+MAXBUF+1))) {
			message("Insufficient memory", 1);
			return 0;
		}
		buffer = b;
		bufalloc = newsize+MAXBUF;
	}
	bufsize = newsize;
	return buffer;
}

unsigned int
findeol(unsigned int c) {
	TRACE("findeol")
	for(; c < bufsize && buffer[c] != '\n'; c++);
	return c;
}

int
left(void) {
	TRACE("left")
	if(!cursor) {
		message("At top of file",1);
		return 0;
	}
	cursor--;
	ccol = 0;
	return 1;
}

int
right(void) {
	TRACE("right")
	if(cursor == bufsize) {
		message("At bottom of file",1);
		return 0;
	}
	cursor++;
	ccol = 0;
	return 1;
}

unsigned int
newcol(unsigned int c) {
	unsigned int d;
	int i;

	TRACE("newcol")
	d = findbol(cursor);
	if(!ccol) {
		for(ccol = 0; d < cursor; d++) {
			if(buffer[d] == TAB) while(++ccol % 8);
			else if(buffer[d] < ' ' || buffer[d] == 127) ccol += 2;
			else ccol++;
		}
	}
	for(i = 0; i < ccol; c++) {
		if(buffer[c] == '\n' || c == bufsize) return c;
		if(buffer[c] == TAB) while(++i % 8);
		else if(buffer[c] < ' ' || buffer[c] == 255 ||
			(buffer[c] >= 127 && buffer[c] < 160)) i += 2;
		else		     i++;
	}
	if(i != ccol) c--;
	return c;
}

int
up(void) {
	unsigned int c;

	TRACE("up")
	c = findbol(cursor);
	if(!c) {
		message("At top of file",1);
		return 0;
	}
	cursor = newcol(findbol(c-1));
	return 1;
}

int
down(void) {
	unsigned int c;

	TRACE("down")
	c = findeol(cursor);
	if(c++ == bufsize) {
		message("At bottom of file",1);
		return 0;
	}
	cursor = newcol(c);
	return 1;
}

void
startselect(void) {
	TRACE("startselect")
	cutpoint = cursor;
	selactive = 1;
	message("Mark set",0);
}

#define JOU_MOVN 0	/* Move to absolute address 			*/
#define JOU_MOVF 1	/* Move one char forwards 			*/
#define JOU_MOVB 2 	/* Move one char backwards 			*/
#define JOU_DELN 3	/* Delete nnnn characters			*/
#define JOU_DELF 4	/* Delete one character forwards		*/
#define JOU_DELB 5	/* Delete one character backwards		*/
#define JOU_INST 8	/* Quote following char, > JOU_INST = literal	*/
int
journal(struct buffer *buf, unsigned int c, unsigned int dellen,
		unsigned int inslen, unsigned char *ptr) {

	int i;
	unsigned int lastpos;
	FILE *jnl;

	if(!buf) buf = buf_curr;
	jnl = buf->journal;
	lastpos = buf->lastjpos;
	if(buf->jfile && !jnl) {
		buf->journal = jnl = fopen(buf->jfile, "w");
		if(!jnl) {
			message("Could not open journal file", 0);
			free(buf->jfile);
			buf->jfile = 0;
			return 1;
		}
#ifndef DOS
		if(flock(fileno(jnl), LOCK_EX|LOCK_NB)) {
			message("File aready being modified", 0);
			readonly = 1;
			fclose(jnl);
			free(buf->jfile);
			buf->jfile = 0;
			buf->journal = 0;
			return 0;
		}
#endif
		fwrite(&(buf->bufsize), 1, sizeof(buf->bufsize), jnl);
	}
	if(!jnl) return 1;

	if(!inslen && dellen && dellen < 5 && c + dellen == lastpos) {
		while(dellen--) fputc(JOU_DELB, jnl);
	} else {
		if(lastpos > c && lastpos - c < 5) {
			for(i = lastpos - c; i; i--)
				fputc(JOU_MOVB, jnl);
		} else if(lastpos < c && c - lastpos < 5) {
			for(i = c - lastpos; i; i--)
				fputc(JOU_MOVF, jnl);
		} else if(lastpos != c) {
			fputc(JOU_MOVN, jnl);
			fwrite(&c, 1, sizeof(c), jnl);
		}
		if(dellen) {
			if(dellen < 5) {
				while(dellen--) fputc(JOU_DELF, jnl);
			} else {
				fputc(JOU_DELN, jnl);
				fwrite(&dellen, 1, sizeof(dellen), jnl);
			}
		}
		while(inslen--) {
			if(*ptr <= JOU_INST) fputc(JOU_INST, jnl);
			fputc(*ptr++, jnl);
			c++;
		}
	}
	buf->lastjpos = c;
	fflush(jnl);
	return 1;
}

void
delete(unsigned int n) {
	unsigned int c;

	TRACE("delete")
	if(readonly) return;
	setref(REFEOL);
	if(n > (bufsize - cursor)) n = bufsize - cursor;
	if(!journal(0, cursor, n, 0, 0)) return;
	if(!lastcp) lastcp = time(0);
	for(c = cursor; c<cursor+n; c++) if(buffer[c] == '\n') setref(REFEOS);
	memmove(&buffer[cursor], &buffer[cursor+n], bufsize - (cursor+n));
	bufsize -= n;
	if(cutpoint > cursor) cutpoint -= n;
	ccol = 0;
	if(!modified) setref(REFSTA);
	modified = 1;
}

void
insert(unsigned char *k, unsigned int l) {
	unsigned int c, bl, oldbufsize;

	TRACE("insert")

	if(readonly) return;
	if(!journal(0, cursor, 0, l, k)) return;
	if(!lastcp) lastcp = time(0);
	if(autoclear && !cursor) {
		setref(REFEOS);
		delete(bufsize);
		autoclear = 0;
	} else setref(REFEOL);
	oldbufsize = bufsize;
	if(!setbufsize(bufsize+l)) return;
	if(!l) return;
	if(autowrap && l == 1 && *k > ' ' &&
			cursor - (bl = findbol(cursor)) >= wrapwidth) {
		while(bl < cursor && buffer[bl] <= ' ') bl++;
		for(c = cursor - 1; c > bl && buffer[c] != '\t'; c--)
							if(buffer[c] == ' ') {
			buffer[c] = '\n';
			journal(0, c, 1, 1, (unsigned char *)"\n");
			setref(REFEOS);
			refpos = c;
			break;
		}
	}
	memmove(&buffer[cursor + l], &buffer[cursor], oldbufsize - cursor);
	if(cutpoint > cursor) cutpoint += l;
	for(c = 0; c < l; c++) {
		buffer[cursor++] = k[c];
		if(k[c] == '\n') setref(REFEOS);
	}
	ccol = 0;
	if(!modified) setref(REFSTA);
	modified = 1;
}

void
recover(FILE *jnl) {
	int c;
	unsigned char insbuf[1024];
	unsigned int inslen, dellen, nnnn;

	inslen = 0;
	dellen = 0;
	c = 0;
	for(;;) {
		c = fgetc(jnl);
		if(inslen && (c < JOU_INST || c == EOF)) {
			insert(insbuf, inslen);
			inslen = 0;
		}
		if(dellen && c != JOU_DELN && c != JOU_DELF && c != JOU_DELB) {
			delete(dellen);
			dellen = 0;
		}
		switch(c) {
		case EOF:	return;
		case JOU_MOVN:
			fread(&nnnn, 1, sizeof(nnnn), jnl);
			cursor = nnnn;
			break;
		case JOU_MOVF:	right();		break;
		case JOU_MOVB:	left();			break;
		case JOU_DELN:
			fread(&nnnn, 1, sizeof(nnnn), jnl);
			dellen += nnnn;
			break;
		case JOU_DELF:	dellen++;		break;
		case JOU_DELB:	dellen++; left();	break;
		case JOU_INST:
			c = fgetc(jnl);
			if(c == EOF) return;	/* Paranoia */
			/* FALL THROUGH */
		default:
			if(inslen == 1024) {
				insert(insbuf, inslen);
				inslen = 0;
			}
			insbuf[inslen++] = c;
			break;
		}
	}
}

void
initbuffer(struct buffer *buf, unsigned char *s, int l) {
	unsigned char *b;

	journal(buf, 0, buf->bufsize, l, s);
	buf->cursor    = 0;
	buf->cutpoint  = 0;
	buf->selactive = 0;
	buf->bufsize   = l;
	if(l) {
		if(buf->bufalloc <= l) {
			if(!(b = (unsigned char *)realloc(buffer, l+1))) {
				message("Insufficient memory", 1);
				return;
			}
			buf->buffer = b;
			buf->bufalloc = l+1;
		}
		memcpy(buf->buffer, s, l);
	}
}

void
copycut(struct buffer *tobuf) {
	unsigned int c, l;
	unsigned char *b;

	TRACE("cut")
	if(!selactive) {
		message("No mark set", 1);
		return;
	}
	if(buf_curr == tobuf) {
		message("Cut to current buffer", 1);
		return;
	}
	if(cursor > cutpoint) {
		c = cutpoint;
		cutpoint = cursor;
		cursor = c;
	}
	if(cutpoint > bufsize) cutpoint = bufsize;

	l = cutpoint - cursor;
	if(l >= tobuf->bufalloc) {
		if(tobuf->buffer) free(tobuf->buffer);
		if(!(b = (unsigned char *) malloc(l+2))) {
			message("Insufficent memory", 1);
			return;
		}
		tobuf->bufalloc = l+1;
		tobuf->buffer = b;
	}
	journal(tobuf, 0, tobuf->bufsize, l, &buffer[cursor]);
	if(!lastcp) lastcp = time(0);
	tobuf->bufsize = l;
	tobuf->cursor = bufsize;
	memcpy(tobuf->buffer, &buffer[cursor], l);
}

void
delcut(void) {
	unsigned int c;

	TRACE("cut")
	if(!selactive) {
		message("No mark set", 1);
		return;
	}
	if(cursor > cutpoint) {
		c = cutpoint;
		cutpoint = cursor;
		cursor = c;
	}
	if(cutpoint > bufsize) cutpoint = bufsize;
	delete(cutpoint - cursor);
	cutpoint = cursor;
	selactive = 0;
}

void
paste(struct buffer *frombuf) {
	TRACE("paste")
	if(buf_curr == frombuf) {
		message("Paste from current buffer", 1);
		return;
	}
	insert(frombuf->buffer, frombuf->bufsize);
}

unsigned int
find(unsigned char *f) {
	unsigned int c;
	int l;

	TRACE("find")
	if(!(*f)) return cursor;
	l = strlen((char *)f);

	for(c = cursor+1; c + l < bufsize; c++)
		if(!memcmp(f, &buffer[c], l)) return c; 

	message("Not found",1);
	return cursor;
}

unsigned int
findreverse(unsigned char *f) {
	unsigned int c;
	int l;

	TRACE("find")
	if(!(*f)) return cursor;
	l = strlen((char *)f);

	if(cursor) for(c = cursor-1;;) {
		if(!memcmp(f, &buffer[c], l)) return c;
		if(!c--) break;
	}

	message("Not found",1);
	return cursor;
}

#ifndef DOS
int
readch() {
	unsigned char c;
	int i;
	controlc = 0;
	i = read(0, &c, 1);
	if(controlc) return 3;
	if(i != 1) return -1;
	return c;
}
#endif

int
getkey(void) {
	int k, j;

	TRACE("getkey")
	refrscr();
	refresh();
	k = readch();
#ifdef DOS
	if(k == 0) {				/* PC keyboard */
		k = readch();
		switch(k) {
		case 'H': k = UPARR; break;	/* DOS up arrow */
		case 'P': k = DOWN; break;	/* DOS down arrow */
		case 'M': k = RIGHT; break;	/* DOS right arrow */
		case 'K': k = LEFT; break;	/* DOS left arrow */
		case 'I': k = PGUP; break;	/* DOS PageUp */
		case 'Q': k = PGDOWN; break;	/* DOS PageDown */
		case 'O': k = END; break;	/* DOS End */
		case 'G': k = HOME; break;	/* DOS Home */
		case 'S': k = CUT; break;	/* DOS Del */
		case 'R': k = PASTE; break;	/* DOS Insert */
		case ';': k = SHIFT; break;	/* DOS F1 */
		default:  k = 0;
		}
		return k;
	}
#else
	if(k == ERR) {
		if(needredraw) {
			needredraw = 0;
			return REFR;
		}
		exit(1);
	}
#endif
	if(k == 27) {
		k = readch();
		if(k == 'O') {			/* DEC application & PF keys */
			switch(readch()) {
			case 'A': k = UPARR;	break;	/* VT App up arrow */
			case 'B': k = DOWN;	break;	/* VT App down arrow */
			case 'C': k = RIGHT;	break;	/* VT App right arrow*/
			case 'D': k = LEFT;	break;	/* VT App left arrow */
			case 'P': k = SHIFT;	break;	/* VT100 PF1 */
			case 'Q': k = 0; 	break;	/* VT100 PF2 */
			case 'R': k = FIND;	break;	/* VT100 PF3 */
			case 'S': k = DELEOL;	break;	/* VT100 PF4 */
			default:  k = 0;
			}
		}
		else if(k == '[')	/* ANSI CSI */
			k = 155;		/* CSI */
		else {
			k = readch();
			if(k == '[') { 		/* Linux console Fkeys */
				switch(readch()) {
				case 'A': k = SHIFT;	break; /* Linux P1 */
				case 'Q': k = 0; 	break; /* Linux P2 */
				case 'R': k = FIND;	break; /* Linux P3 */
				case 'S': k = DELEOL;	break; /* Linux P4 */
				default:  k = 0;
				}
			}
			else	k = 0;
		}
	}

	if(k == 155) {				/* CSI */
		k = readch();
		j = 0;
		while(k >= '0' && k <= '9') {
			j = j * 10 + (k - '0');
			k = readch();
		}
		while((k >= '0' && k <= '9') || k == ';')
			k = readch();
		switch(k) {
		case '~': switch(j) {
			case  1: k = FIND;	break;	/* VT200 Find */
			case  2: k = PASTE;	break;	/* VT200 Paste */
			case  3: k = CUT;	break;	/* VT200 Copy */
			case  4: k = SELECT;	break;	/* VT200 Select */
			case  5: k = PGUP;	break;	/* VT200 Page Up */
			case  6: k = PGDOWN;	break;	/* VT200 Page Down */
			case 11: k = SHIFT;	break;	/* Xterm F1 */
			case 12: k = 0;		break;	/* Xterm F2 */
			case 13: k = FIND;	break;	/* Xterm F3 */
			case 14: k = DELEOL;	break;	/* Xterm F4 */
			default: k = 0;
			}
			break;
		case 'A': k = UPARR;	break;		/* VT100 up arrow */
		case 'B': k = DOWN;	break;		/* VT100 down arrow */
		case 'C': k = RIGHT;	break;		/* VT100 right arrow */
		case 'D': k = LEFT;	break;		/* VT100 left arrow */
		case 'F': k = SELECT;	break;		/* FreeBSD End */
		case 'G': k = PGDOWN;	break;		/* FreeBSD Page Down */
		case 'H': k = FIND;	break;		/* FreeBSD Home */
		case 'I': k = PGUP;	break;		/* FreeBSD Page Up */
		case 'L': k = PASTE;	break;		/* FreeBSD Insert */
		case 'M': k = SHIFT;	break;		/* FreeBSD PF1 */
		case 'N': k = 0; 	break;		/* FreeBSD PF2 */
		case 'O': k = FIND;	break;		/* FreeBSD PF3 */
		case 'P': k = DELEOL;	break;		/* FreeBSD PF4 */
		default:  k = 0;
		}
	}
	return k;
}

void
justify(void) {
	unsigned c, i;

	cursor = findeol(cursor);
	if(cursor >= bufsize) return;
	for(cursor--; cursor > 0 && (buffer[cursor] == ' ' ||
				     buffer[cursor]=='\t'); cursor--) delete(1);
	cursor++;
	c = findbol(cursor);
	if(c == cursor) {
		if(cursor < bufsize) cursor++;
		return;
	}
	ccol = 0;
	newcol(cursor);
	if(ccol < wrapwidth) {
		if(cursor >= bufsize) return;
		if(buffer[cursor+1] == '\n') {
			cursor++;
			return;
		}
		journal(0, cursor, 1, 1, (unsigned char *)" ");
		buffer[cursor++] = ' ';
		setref(REFSCR);
		for(i = 0; buffer[cursor+i] == ' ' || buffer[cursor+i] == '\t';
		    i++);
		if(i) delete(i);
		ccol = 0;
		cursor = findeol(cursor);
		newcol(cursor);
		if(!modified) setref(REFSTA);
		modified = 1;
	}

	if(ccol > wrapwidth) {
		ccol = wrapwidth;
		cursor = newcol(findbol(cursor));
		for(;; cursor--) {
			if(!cursor || buffer[cursor] == '\n') {
				down();
				break;
			}
			if(buffer[cursor] != ' ' && buffer[cursor] != '\t')
				continue;
			for(i = 0; cursor && (buffer[cursor] == ' ' ||
					 buffer[cursor] == '\t'); cursor--) i++;
			cursor++;
			if(i) delete(i);
			insert((unsigned char *)"\n", 1);
			if(!modified) setref(REFSTA);
			modified = 1;
			break;
		}
	} else if(cursor < bufsize) cursor++;

	for(i = 0; buffer[cursor+i] == ' ' || buffer[cursor+i] == '\t'; i++);
	if(i) delete(i);

	if(cursor >= bufsize || buffer[cursor] == '\n') return;

	c = findbol(cursor - 1);
	for(i = 0; buffer[c+i] == ' ' || buffer[c+i] == '\t'; i++);
	insert(&buffer[c], i);

	cursor = findeol(cursor);
}

struct buffer *
findbuffer(char *name) {
	struct buffer *buf;
	for(buf = bufferlist; buf; buf = buf->next)
		if(!strcmp(name, buf->name)) return buf;
	return 0;
}

struct buffer *
newbuffer(char *name, int user) {
	struct buffer *buf;

	if((buf = findbuffer(name))) return buf;

	buf = malloc(sizeof(struct buffer));
	if(!buf) {
		message("Insufficent memory", 1);
		return 0;
	}
	buf->name = malloc(strlen(name) + 1);
	if(!buf->name) {
		message("Insufficent memory", 1);
		return 0;
	}
	strcpy(buf->name, name);

	buf->buffer = (unsigned char *)malloc(MINBUF+1);
	buf->buffer[0] = 0;
	buf->bufalloc = MINBUF;

	buf->cursor    = 0;
	buf->bufsize   = 0;
	buf->cutpoint  = 0;
	buf->readonly  = 0;
	buf->autowrap  = 0;
	buf->modified  = 0;
	buf->selactive = 0;
	buf->isfile    = 0;
	buf->ismain    = 0;
	buf->backedup  = 0;
	buf->lastjpos  = 0;
	buf->journal   = 0;
	buf->jfile     = 0;
	buf->isuser    = user;
	buf->next      = bufferlist;
	bufferlist     = buf;
	return buf;
}

void
storebuffer() {
	if(!buf_curr) return;
	buf_curr->buffer    = buffer;
	buf_curr->cursor    = cursor;
	buf_curr->bufsize   = bufsize;
	buf_curr->cutpoint  = cutpoint;
	buf_curr->bufalloc  = bufalloc;
	buf_curr->selactive = selactive;
	buf_curr->readonly  = readonly;
	buf_curr->autowrap  = autowrap;
	buf_curr->modified  = modified;
}

void
loadbuffer(struct buffer *buf) {
	if(!buf) return;
	if(buf == buf_curr) return;
	buffer    = buf->buffer;
	cursor    = buf->cursor;
	bufsize   = buf->bufsize;
	cutpoint  = buf->cutpoint;
	bufalloc  = buf->bufalloc;
	selactive = buf->selactive;
	readonly  = buf->readonly;
	autowrap  = buf->autowrap;
	modified  = buf->modified;
	filename  = buf->name;
	buf_curr = buf;
	if(!inputprompt) setref(REFSCR|REFSTA);
}

struct buffer *
switchbuffer(struct buffer *buf) {
	if(!buf) return buf_curr;
	if(buf == buf_curr) return buf_curr;
	storebuffer();
	loadbuffer(buf);
	return buf;
}

void
deletebuffer(struct buffer *buf) {
	struct buffer *b;

	if(buf == buf_curr) return;
	if(bufferlist == buf) b = 0;
	for(b = bufferlist; b; b = b->next) if(b->next == buf) break;
	if(!b) return;	/* paranoia */

	if(b) b->next = buf->next;
	else  bufferlist = buf->next;
	if(buf->bufalloc) free(buf->buffer);
	free(buf->name);
	free(buf);
}

int doedit();

int
query(char *prompt, struct buffer *tobuf) {
	struct buffer *cbuf;
	int sscrtop, r;

	if(inputprompt) return 0;
	if(buf_curr == tobuf) {
		message("Cannot query to current buffer", 1);
		return 0;
	}
	cbuf = buf_curr;
	switchbuffer(tobuf);
	inputprompt = prompt;
	sscrtop = scrtop;
	scrtop = 0;
	cursor = 0;
	autoclear = 1;
	setref(REFSCR);
	r = doedit();
	autoclear = 0;
	inputprompt = 0;
	switchbuffer(cbuf);
	scrtop = sscrtop;
	setref(REFSTA);
	tobuf->buffer[tobuf->bufsize] = 0;
	return r;
}

int
insertfile(char *filename) {
	int i, k;
	FILE *f;
	unsigned char buf[1024];
	int piped;

	i = 0;

	piped = (*filename == '!' && !secure);
	if(piped) f = popen(&filename[1], "r");
	else      f = fopen(filename, FOPEN_READ);
	if(f) {
		while((k = getc(f)) != EOF) if(k) {
			if(textmode && !piped && (k == '\r' || k == 26))
				continue;
			buf[i++] = k;
			if(i == 1024) {
				insert(buf, 1024);
				i = 0;
			}
		}
		if(i) insert(buf, i);
		if(piped) pclose(f);
		else      fclose(f);
	} else {
		sprintf(scrbuf, "ERROR: could not read %s", filename);
		message(scrbuf,0);
		return 1;
	}
	return 0;
}

char *
newfilename(char *name, char *ext) {
	char *s;
	int c, i;
	c = strlen(name);
#ifdef DOS
	for(i = 0; name[i]; i++) {
		if(name[i] == '/' || name [i] == '\\') c = strlen(name);
		if(name[i] == '.') c = i;
	}
#endif
	i = strlen(ext) + 1;
	s = malloc(c + i);
	memcpy(s, name, c);
	memcpy(&s[c], ext, i);
	return s;
}

int
writefile(char *fna) {
	FILE *f;
	char *backupfile;
	int notnew, bytes;
	unsigned int i;
	int piped;
	int saving;

	piped = (!secure && *fna == '!');
	saving = !strcmp(fna, filename);
	notnew = 1;
	if(saving && buf_curr->backedup) notnew = 0;
	if(!strncmp(fna, "/tmp/crontab.", 13)) notnew = 0;
						/* effing Vixie! */
	if(notnew && !piped && !nobackup) {
		if((f = fopen(fna, "r"))) {
			fclose(f);
				backupfile = newfilename(fna, ".bak");
			unlink(backupfile);
			if(strcmp(fna, backupfile)) {
				if(lstat(fna, &statbuf) ||
				   rename(fna, backupfile)) {
					move(LINES-1, 0);
					addstr("ERROR: could not make backup ");
					addstr(backupfile);
					free(backupfile);
					return 0;
				}
				buf_curr->backedup = 1;
			} else notnew = 0;
			free(backupfile);
		} else notnew = 0;
	}

	if(piped) f = popen(&fna[1], "w");
	else      f = fopen(fna, FOPEN_WRITE);

	if(!f) {
		move(LINES-1, 0);
		sprintf(scrbuf, "ERROR: could not write %s", fna);
		message(scrbuf,0);
		return 0;
	}
	bytes = 0;
	for(i = 0; i < bufsize; i++) {
#ifdef DOS
		if(textmode && buffer[i] == '\n') {
			fputc('\r', f);
			bytes++;
		}
#endif
		fputc(buffer[i], f);
		bytes++;
	}
	if(piped) pclose(f);
	else      fclose(f);

	if(notnew && !nobackup) {
		chmod(fna, statbuf.st_mode);
		chown(fna, statbuf.st_uid, statbuf.st_gid);
	}

	move(LINES-1,0);
	clrtoeol();
	sprintf(scrbuf,"%s %u bytes", fna, bytes);
	message(scrbuf, 0);

	if(saving) {
		modified = 0;
		if(buf_curr->journal) {
			fclose(buf_curr->journal);
			buf_curr->journal = 0;
			unlink(buf_curr->jfile);
		}
		lastcp = 0;
	}
	return 1;
}

void
abortedit(int i) {
	struct buffer *buf;

	storebuffer();
	for(buf = bufferlist; buf; buf = buf->next) {
		if(buf->isfile && buf->modified) {
			initbuffer(buf_temp, (unsigned char *)"No", 2);
			if(!query("Some files not saved, quit anyway? ",
				buf_temp)) goto oops;
			if(*buf_temp->buffer != 'y' && *buf_temp->buffer != 'Y')
				goto oops;
			break;
		}
	}
	for(buf = bufferlist; buf; buf = buf->next) if(buf->journal) {
		fclose(buf->journal);
		unlink(buf->jfile);
	}
	refresh();
	restorescreen();
	putchar('\n');
	exit(i);

oops:	switchbuffer(buf);
}

void
loadfile(char *name, int ronly) {
	struct buffer *buf;
	FILE *f;
	int k;
	unsigned int siz;
	char *jfilename;

	storebuffer();

	filename = name;
	if((buf = findbuffer(name))) {
		if(buf->isfile) {
			switchbuffer(buf);
			return;
		} else deletebuffer(buf);
	}

	switchbuffer(newbuffer(filename, 1));
	buf_curr->isfile = 1;

	buffer = 0;
	setbufsize(0);
	readonly = ronly;
	buf_curr->jfile = jfilename = 0;

	if(!readonly)
		jfilename = newfilename(name, ".jou");

	if(!(f = fopen(filename, FOPEN_RDWR))) {
		if(!(f = fopen(filename, FOPEN_READ))) {
			message("Could not open file", 1);
		} else {
			if(!readonly) message("File read-only", 0);
			readonly = 1;
			free(jfilename);
			jfilename = 0;
		}
	}

	if(f) {
		while((k = getc(f)) != EOF) {
			if(textmode && (k == '\r' || k == 26)) continue;
			buffer[bufsize] = k;
			if(!setbufsize(bufsize+1)) {
				message("File too large", 1);
				ronly = 1;
				break;
			}
		}
		fclose(f);
	}

	buf_curr->journal = 0;
	if(jfilename) if((f = fopen(jfilename, "r+"))) {
		fread(&siz, 1, sizeof(siz), f);
#ifndef DOS
		if(flock(fileno(f), LOCK_EX|LOCK_NB)) {
			message("File aready being modified", 0);
			readonly = 1;
			fclose(f);
			free(jfilename);
			jfilename = 0;
		}
#endif
		if(siz == bufsize && jfilename) {
			refrscr();
			initbuffer(buf_temp, (unsigned char *)"Yes", 3);
			if(query("Journal file found.  Recover? ", buf_temp)) {
				if(*buf_temp->buffer != 'n' &&
				   *buf_temp->buffer != 'N') {
					recover(f);
					buf_curr->journal = f;
					buf_curr->lastjpos = cursor;
				}
			}
			setref(REFSCR|REFSTA);
		} else if(jfilename) message("Warning: journal ignored",0);
		if(!buf_curr->journal && jfilename) {
			fclose(f);
			unlink(jfilename);
		}
	}
	if(nojournal && jfilename) {
		if(buf_curr->journal) {
			fclose(buf_curr->journal);
			buf_curr->journal = 0;
			unlink(jfilename);
		}
		free(jfilename);
		jfilename = 0;
	}
	buf_curr->jfile   = jfilename;
	buf_curr->bufsize = bufsize;
}

int
doedit() {
	int k, i, j;
	struct buffer *buf;
	unsigned char ch;
	int cpnow = 0;

	for(;;) {
		if(checkpoint && buf_curr->ismain && lastcp &&
					(cpnow || time(0) > lastcp + 15)) {
			writefile(filename);
			lastcp = 0;
		}
		cpnow = 0;
		k = getkey();
		switch(k) {
		case LEFT:
			left();
			break;
		case UPARR:
			up();
			cpnow = 1;
			break;
		case DOWN:
			down();
			cpnow = 1;
			break;
		case RIGHT:
			right();
			break;
		case PGUP:
			for(i = 0; i < LINES-1; i++) up();
			cpnow = 1;
			break;
		case PGDOWN:
			for(i = 0; i < LINES-1; i++) down();
			cpnow = 1;
			break;
		case HOME:
			cursor = findbol(cursor);
			ccol = 0;
			break;
		case END:
			cursor = findeol(cursor);
			ccol = 0;
			break;
		case FIND:
			if(inputprompt) return 1;
			if(query("Find: ", buf_find)) {
				cursor = find(buf_find->buffer);
				ccol = 0;
			}
			break;
		case GOTO:
			initbuffer(buf_temp, 0, 0);
			if(query("Goto: ", buf_temp))  {
				if((j = atoi((char *)buf_temp->buffer))) {
					cursor = 0;
					for(i = 1; i < j; i++) down();
				}
			}
			break;
		case SELECT:
			startselect();
			break;
		case CUT:
			copycut(buf_cut);
			delcut();
			break;
		case PASTE:
			paste(buf_cut);
			cpnow = 1;
			break;
		case DELF:
			delete(1);
			break;
		case DELEOL:
			if(buffer[cursor] == '\n') delete(1);
			else {
				startselect();
				cursor = findeol(cursor);
				copycut(buf_cut);
				delcut();
			}
			break;
		case DELBOL:
			if(!left()) break;
			if(buffer[cursor] == '\n') delete(1);
			else {
				right();
				startselect();
				cursor = findbol(cursor);
				copycut(buf_cut);
				delcut();
			}
			break;
		case BKSP:
		case DEL:
			if(left()) delete(1);
			break;
		case LFEED:
			justify();
			break;
		case CRET:
			if(inputprompt) return 1;
			insert((unsigned char *)"\n", 1);
			cpnow = 1;
			break;
		case EXIT:
			if(inputprompt) return 0;
			for(buf=bufferlist; buf; buf=buf->next) if(buf->ismain){
				switchbuffer(buf);
				if(!writefile(filename)) break;
				move(LINES-1,0);
				clrtoeol();
				addstr(scrbuf);
			}
			if(!buf) abortedit(0);
			break;
		case ABORT:
			if(inputprompt) return 0;
			move(LINES-1,0);
			abortedit(0);
			break;
		case REFR:
			redraw(0);
			cpnow = 1;
			break;
		case QUOTE:
			k = readch();
			if(k >= 0 && k < 256) {
				ch = k;
				insert(&ch, 1);
			}
			break;
		case SHIFT:
			switch(getkey()) {
			case PGUP:
				cursor = 0;
				break;
			case PGDOWN:
				cursor = bufsize;
				break;
			case FIND:
				if(query("Reverse Find: ", buf_find)) {
					cursor = findreverse(buf_find->buffer);
					ccol = 0;
				}
				break;
			case 'i':
			case 'I':
				initbuffer(buf_temp, 0, 0);
				if(query("Insert: ", buf_temp))
					if(buf_temp->bufsize)
						insertfile(
						     (char *)buf_temp->buffer);
				break;
			case 'W':
			case 'w':
				initbuffer(buf_temp, 0, 0);
				if(query("Write to: ", buf_temp))
					if(buf_temp->bufsize)
						writefile(
						     (char *)buf_temp->buffer);
				break;
			case 'r':
			case 'R':
				delete(buf_find->bufsize);
				paste(buf_cut);
				cursor = find(buf_find->buffer);
				break;
			case 'b':
			case 'B':
				initbuffer(buf_temp, 0, 0);
				if(query("Buffer: ", buf_temp))
					if(buf_temp->bufsize)
						switchbuffer(newbuffer(
						 (char *)buf_temp->buffer, 1));
				break;
			case 'n':
			case 'N':
				buf = buf_curr;
				do {
					buf = buf->next;
					if(!buf) buf = bufferlist;
				} while(!buf->isuser);
				switchbuffer(buf);
				break;
			case 'l':
			case 'L':
				initbuffer(buf_temp, 0, 0);
				if(query("Load: ", buf_temp))
					if(buf_temp->bufsize)
						loadfile(
						  (char *)buf_temp->buffer, 0);
				break;
			}
			break;
		default:
			if((k >= ' ' && k < DEL) ||
			   (k >= 160 && k < 255) || k == TAB) {
				ch = k;
				insert(&ch,1);
			}
		}
		autoclear = 0;
	}
}

void
usage() {
        fputs(VERSION
"\nUsage: ned [options] <filename> ..."
"\nOptions:"
"\n        -b              Binary mode (default under Un*x)"
"\n        -c col          Start on column <col>"
"\n        -j              Disable journalling"
"\n        -n              Disable backup file"
"\n        -r row          Start on row <row>"
"\n        -R              Read-only"
"\n        -s              Secure mode (pipes disabled)"
"\n        -t              Text mode (default under DOS)"
"\n        -w              Enable auto-wrap"
"\n        -W cols         Wrap after <cols> columns"
"\n        -C              Write the file out as it is being edited"
"\n",              stderr);
	exit(1);
}

int
main(int argc, char **argv) {
	int i;
	char ch, *s;
	int startrow, startcol, ronly, wrapping;
#ifndef DOS
	struct sigaction sa;
#endif

	ronly = startrow = startcol = wrapping = 0;
	for(i = 1; i < argc; i++) {
		if(*argv[i] == '+') {
			startrow = atoi(&argv[i][1]);
			continue;
		}
		if(*argv[i] != '-') break;
		ch = argv[i][1];
		s = 0;
                if(strchr("rcW", ch)) {
			if(argv[i][2]) s = &argv[i][2];
			else {
				if(++i == argc) usage();
				s = argv[i];
			}
		}
		switch(ch) {
		case 't':	textmode = 1;			break;
		case 'b':	textmode = 0;			break;
		case 'r':	startrow = atoi(s);		break;
		case 'c':	startcol = atoi(s);		break;
		case 'w':	wrapping = 1;			break;
		case 'W':	wrapwidth = atoi(s);		break;
		case 'R':	ronly = 1;			break;
		case 's':	secure = 1;			break;
		case 'j':	nojournal = 1;			break;
		case 'n':	nobackup = 1;			break;
		case 'C':	checkpoint = nojournal = 1;	break;
		default: 	usage();
		}
	}
	if(i >= argc) usage();

	setupscreen();
#ifndef DOS
	memset(&sa, 0, sizeof(sa));
	sa.sa_handler = redraw_handler;
	sigaction(SIGWINCH, &sa, 0);
#endif

	leftmargin = 0;
	ccol = 0;
	cutpoint = 0; selactive = 0;
	scrtop = cursor = 0;
	refstate = REFSCR|REFSTA;
	modified = 0;
	autoclear = 0;

	buf_cut  = newbuffer("cut",  0);
	buf_find = newbuffer("find", 0);
	buf_temp = newbuffer("temp", 0);
	buf_keys = newbuffer("keys", 0);

	for(; i < argc; i++) {
		loadfile(argv[i], ronly);
		if(!buf_main) buf_main = buf_curr;
		buf_curr->ismain = 1;
	}
	switchbuffer(buf_main);
	autowrap = wrapping;

	if(startrow < 0) {
		cursor = bufsize;
		for(i = startrow+1; i; i++) up();
	} else if(startrow || startcol) {
		cursor = 0;
		for(i = 1; i < startrow; i++) down();
	}
	if(startcol < 0) {
		cursor = findeol(cursor);
		for(i = startcol+1; i; i++)
			if(!--cursor || buffer[cursor-1] == '\n') break;
	} else if(startcol > 0) {
		ccol = startcol;
		cursor = newcol(cursor);
	}

	doedit();
	return 0;
}