/* fv is a binary file editor written by Chris Hooper (cdh@mtu.edu)
 *	on 6-2-91 and has gone through many revisions since then.
 *      Much inspiration was given by Bill Moore, the first real user!
 *
 *   Author:  CHRISTOPHER D HOOPER
 *
 *   fv source Copyright (c) 1992 - 1998  Chris Hooper
 *
 *   Modification and redistribution is strictly prohibited.
 *   Sale of this software above media cost is prohibited.
 *
 *   Except for above two restrictions, this software may be used for
 *       any purpose, commercial or private.
 *
 *   Disclaimer:  This product is fit for no use, foreign or domestic.
 *                Use implies knowledge you intend to destroy something.
 */

#define VERSION "@(#)fv  2.20  19-Jan-98  cdh"

#ifdef OS9
_asm("_sysedit: equ 220");
#endif

#include <stdio.h>
#include <curses.h>
#include <string.h>
#if !defined(_OS9000) && !defined(_OSK)
#	include <sys/types.h>
#endif
#include "main.h"
#if !defined(Amiga) && !defined(OS9) && !defined(FreeBSD)
#	include <malloc.h>
#	include <unistd.h>
#	include <ctype.h>
#endif
#ifdef Linux
#	include	<sys/ioctl.h>
#endif
#include <signal.h>
#include "edit.h"
#include "screen.h"
#include "io.h"

#ifdef Dynix
#	include <sys/types.h>
#endif

#if defined(NetBSD) || defined(Linux) || defined(__FreeBSD__)
#	define _cury cury
#	define _curx curx
#endif

#ifdef OS9
#	define _cury w_wrow
#	define _curx w_wcol
char *strdup(char *str);
#endif

#ifdef LINT
#	include "lint.h"
#endif

#define WDISMAX		4
#define KBUFMAX  	1024
#define HIGH_BUF_SIZE	65536

uchar   *buffer;                /* character pointer into display buffer */
ulong   *ibuffer;               /* integer pointer into display buffer */
char    *filename[256];         /* name of files currently being edited */
char    kbuffer[KBUFMAX];       /* storage area for "automatic" keystrokes */
char    out_string[512];        /* output buffering scratch space */ 
char    *version         = VERSION; /* program version string */
int     SWAPBYTES        = 0;   /* both display-only displacement */
int     Swapbytes        = 0;   /* character cursor displacement */
int     Zmode            = 1;   /* cycle for display character for nil */
int     addis            = 0;   /* 1=address display mode on */
int     alignment        = 1;   /* start out with single character {1,2,4,8} */
int     buf_size         = 0;   /* size of display buffer */
int     chdis            = 1;   /* 1=character display mode on */
int     cpline           = 0;   /* number of characters (bytes) per line */
int     divisor          = 0;   /* address display divisor/modulus for bottom */
int     divisor2         = 0;   /* address display divisor/modulus for left */
int     divmod           = 0;   /* toggle (divide or modulus) for bottom */
int     divmod2          = 0;   /* toggle (divide or modulus) for left */
int     filenames        = 0;   /* number of files in buffer */
int     filenum          = 0;   /* number of file currently being edited */
int     force_ronly      = 0;   /* 1=force file read-only */
int     force_write      = 0;   /* 1=force file write mode */
int     global_direction = 0;   /* direction of last search (o=forward) */
int     global_num       = 0;   /* number of bytes last searched against */
int     global_special[MAXLEN]; /* wildcard positions for search */
int     global_type      = 0;   /* type of last search (hex or character) */
int     kbuffer_size     = 0;   /* number of buffered keystrokes */
int     max_buf_size     = 0;   /* maximum size of display buffer */
int     mode             = STRING; /* display/search mode (words or string) */
int     noscreen         = 0;   /* turn off all screen stuff */
int     ronly            = 0;   /* 1=file is read-only */
int     prompting        = 0;   /* flag for interrupt handler of search */
int     swapbytes        = 0;   /* word cursor displacement */
int     swapmode         = 0;   /* cycle for which of above three is active */
int     view_address     = 0;   /* file address at top left corner of screen */
int     wdis             = 1;   /* 1=word display mode on */
int     wdismode         = 0;   /* word display mode, 0=hex, 1=dec, etc */
int     wpline           = 0;   /* number of (4 byte) words per line */
int     wref             = 0;   /* refresh window because of motion or change */
int     zmode            = 1;   /* cycle for display chara of non-printable */
int     daddr            = -1;  /* current buffered address */
int     dirty            = 0;   /* buffer has dirty data */
uchar   global_search[MAXLEN];  /* last search data */
ulong   cursor_address   = 0;   /* current cursor position in file */
ulong   last_viewed      = 0;   /* last address relocated from */
ulong   fend             = 0;   /* size of file in bytes */

extern	ulong inbuf_size;

void sigint();
#ifndef AMIGA
void sigquit();
#ifndef HPUX
void sigwinch();
#endif
#endif

static void print_usage();
static void kbuffer_append();
static int  parse_string();

extern char *getenv();


#ifdef Amiga
int main(argc, argv)
#else
void main(argc, argv)
#endif
int argc;
char *argv[];
{
	int	index = 0;
	char	*ptr;

	while (*version != ' ')
	    version++;

	if ((ptr = getenv("FVINIT")) != NULL)
	    kbuffer_append(ptr);

	for (index = 1; index < argc; index++)
	    if (*(ptr = argv[index]) == '-')
		for (ptr++; *ptr != '\0'; ptr++)
		    switch (*ptr) {
#ifdef EDIT_MEMORY
			case 'a':	/* access memory */
			    index++;
			    if ((index < argc) && isdigit(*argv[index])) {
			        out_string[0] = '<';
			        strcpy(out_string + 1, argv[index]);
			        strcat(out_string, ">");
				filename[filenames++] = strdup(out_string);
			    } else {
				fprintf(stderr, "No address for -a given\n");
				print_usage(argv[0], 0);
				exit(1);
			    }
			    break;
#endif
			case 'A':
			    for (index = 0; index < 24; index++)
#ifdef LITTLE_ENDIAN
			        putchar(((char *) file_sz)[index ^ 3] ^ 64);
#else
			        putchar(((char *) file_sz)[index] ^ 64);
#endif
			    break;
			case 'h':
			case '?':
			case '/':
			    print_usage(argv[0], 0);
			    exit(0);
			case 'k':	/* buffer keystrokes */
			    index++;
			    if (index < argc)
				kbuffer_append(argv[index]);
			    else {
				fprintf(stderr, "No command for -k given\n");
				print_usage(argv[0], 0);
				exit(1);
			    }
			    break;
			case 'r':	/* force read-only */
			    force_ronly = !force_ronly;
			    break;
			case 's':	/* turn off screen */
			    noscreen = !noscreen;
			    break;
			case 'v':	/* version */
			    print_usage(argv[0], 1);
			    printf("%s\n", version);
			    exit(0);
			    break;
			case 'w':	/* force write mode */
			    force_write = !force_write;
			    break;
			default: 
			    fprintf(stderr, "Unknown parameter -%s\n", ptr);
			    print_usage(argv[0], 0);
			    exit(1);
		    }
	    else
		filename[filenames++] = ptr;


	if (filenames == 0) {
	    fprintf(stderr, "No filename specified!  ");
	    print_usage(argv[0], 0);
	    exit(1);
	}

	for (index = 0; index < filenames; index++)
	    if (open_file(index))
	        break;

	if (index < filenames)
	    filenum = index;
	else
	    exit(1);


	signal(SIGINT, sigint);
#if !defined(AMIGA) && !defined(Win32)
	signal(SIGQUIT, sigint);
#if !defined(OS9) && !defined(HPUX)
	if (!noscreen)
	    signal(SIGWINCH, sigwinch);
#endif /* !OS9 */
#endif /* !Amiga */

	assign_curses();
	init();

	if (noscreen)
		assign_dummy();

	if (fend < HIGH_BUF_SIZE)
	    max_buf_size = fend;

	if (max_buf_size < HIGH_BUF_SIZE)
	    max_buf_size = HIGH_BUF_SIZE;

	ibuffer = (ulong *) malloc(max_buf_size + MAXLEN);
	if (ibuffer == NULL)
	    error_exit("main: malloc failed");

	buffer = (uchar *) ibuffer;

	if (!kbuffer_size)
	    status_filename();
	else
	    wref = 1;

	refresh_buffer(1);

	if (!noscreen)
	    show_addr(view_address);

	edit_file();
	close_file();

	de_init();
	exit(0);
}

void error_exit(str)
char *str;
{
	de_init();
	if (str)
		fprintf(stderr, str);
	exit(1);
}

ulong file_sz[] =
{0x03081209,0x13140f10,0x08051260,0x046e6008,0x0f0f1005,0x124a4040};

/************************************************************************/
/*									*/
/*			   Editor state machine				*/
/*									*/
/************************************************************************/

int edit_file()
{
    int ch = ' ';	/* input character */
    int looptime = 1;	/* default number of times to execute operation */
    int rollover = 1;	/* if non-zero digit entered previously */
    int cref = 0;	/* refresh display because of cursor motion */
    int temp;		/* temp calculation */
    int temp2;		/* temp calculation */
    int temp3;		/* temp calculation */
    int addr;
    int addr2;
    int running = 1;	/* 0=terminating */

    update_cursor(ON);
    if (!kbuffer_size)
        xrefresh(main_window);
    status_mode();

    while (running) {
	ch = get_key();
	if (((ch >= '1') && (ch <= '9')) ||
	    ((ch == '0') && (rollover == 0))) {
	    if (rollover)
		looptime = ch - '0';
	    else
		looptime = looptime * 10 + ch - '0';
	    rollover = 0;
	} else {
	    if (rollover)
		looptime = 1;

	    while (looptime-- > 0)
		switch (ch) {
	            case 'a':	/* toggle address display */
	        	wref	= 1;
	        	addis = !addis;

	        	wpline = (COLS - addis * 10) /
	        		 (9 * wdis + chdis * 4);
			cpline = wpline << 2;
	        	buf_size = (LINES - 1) * cpline;

	        	if ((cursor_address - view_address) >= buf_size) {
	        	    cursor_address  = view_address + buf_size - 1;
			    ALIGN(cursor_address);
	        	}

	        	xmove(main_window, 0, 0);
	        	xclrtobot(main_window);
	        	break;
	            case 'A':	/* append bis cursor von mark to a file */
			looptime = 0;
	        	write_mark(cursor_address, 1);
	        	wref = 1;
	        	break;
	            case 'b':
#ifndef EMACS_FEEL
	            case 2:	/* ^B  back one page */
#endif
#ifdef KEY_SR
		    case KEY_SR:
#endif
	        	last_viewed   = view_address;
	        	view_address -= buf_size;
			VALIGN(view_address);
	        	if (view_address < 0)
	        	    view_address = 0;
	        	if (cursor_address >= view_address + inbuf_size) {
			    cursor_address -= buf_size;
			    ALIGN(cursor_address);
	        	}
	        	wref	= 1;
	        	break;
	            case 'c':	/* toggle character display */
	        	wref	= 1;
	        	chdis	= !chdis;
	        	temp	= wpline;
	        	if (chdis == 0)
	        		wdis = 1;

	        	wpline = (COLS - addis * 10) /
	        		 (9 * wdis + chdis * 4);
			cpline = wpline << 2;
	        	buf_size = (LINES - 1) * cpline;

	        	if (cursor_address >= view_address + inbuf_size) {
	        	    cursor_address  = view_address + buf_size - 1;
			    ALIGN(cursor_address);
	        	}
	        	xmove(main_window, 0, 0);
	        	xclrtobot(main_window);
	        	break;
		    /* ^C taken for SIGINT and 'q' below */
	            case 'C':		/* copy bis cursor von mark to file */
			looptime = 0;
	        	write_mark(cursor_address, 0);
	        	wref = 1;
	        	break;
	            case 4:	/* ^D  down one half page */
	            case 'd':
	        	if (view_address < fend - buf_size / 2) {
	        	    last_viewed   = view_address;
	        	    view_address += buf_size / 2;
	        	    VALIGN(view_address);
	        	    wref	      = 1;
	        	    if (cursor_address < view_address) {
	        	    	cursor_address += buf_size / 2;
				ALIGN(cursor_address);
			    }
	        	}
	        	break;
	            case 'D':	/* delete physical area */
	        	break;
	            case 'f':
#ifndef EMACS_FEEL
	            case 6:	/* ^F  forward one page */
#endif
#ifdef KEY_SF
		    case KEY_SF:
#endif
	        	if (view_address < fend - buf_size) {
	        	    last_viewed   = view_address;
	        	    view_address += buf_size;
			    VALIGN(view_address);
	        	    wref	      = 1;
	        	    if (cursor_address < view_address) {
				cursor_address += buf_size;
				ALIGN(cursor_address);
			    }
	        	}
	        	break;
	            case 'F':	/* select new file to edit */
#ifdef KEY_OPEN
		    case KEY_OPEN:
#endif
			temp = select_file();
			if (temp != filenum) {
			    if (save_changes(1))
				break;
			    close_file();
			    view_address = 0;
			    cursor_address = 0;
			    daddr = -1;
			    wref = 1;
			    xmove(status_window, 0, 0);
			    xclrtoeol(status_window);
			    if (open_file(temp))
			        filenum = temp;
			    else if (open_file(filenum) == 0) {
			        sprintf(out_string, "cannot reopen %s!\n",
			       	        filename[filenum]);
			        error_exit(out_string);
			    }
			} else {
			    xmove(status_window, 0, 0);
			    xclrtoeol(status_window);
			}
	        	status_range(view_address, view_address + buf_size);
			status_filename();
			status_mode();
	        	break;
	            case 'g':	/* go (jump) to address */
			looptime = 0;
	        	temp = get_jump(view_address);
			if (temp == -1) {
	        	    status_mode();
			    break;
			}
			temp <<= divisor;
			ALIGN(temp);
			extend_end(temp);
	        	if ((temp >= view_address) &&
	        	    (temp <= view_address + inbuf_size)) {
	        		update_cursor(OFF);
	        		cref            = 1;
	        		cursor_address  = temp;
	        	} else if ((view_address != temp) &&
	        		   (temp >= 0) && (temp <= fend)) {
	        		wref          = 1;
	        		last_viewed    = view_address;
	        		view_address   = temp;
	        		cursor_address = temp;
	        	}
	        	status_mode();
	        	break;
		    case 'G':	/* go to bottom of file */
			/* eventually let user enter address below and this
			   would then let them Go to that address? */
	        	update_cursor(OFF);

			if (rollover == 0) {	/* jump to addr */
			    cursor_address = ALIGNED(looptime + 1);
			    if (cursor_address > fend - 1) {
				wref = 1;
				cursor_address = ALIGNED(fend - 1);
			    }
			    if ((cursor_address < view_address) ||
			        (cursor_address > ALIGNED(view_address +
			                                  buf_size - 1))) {
				wref = 1;
				view_address = ALIGNED((cursor_address -
				                        buf_size / 2) & ~3);
			    } else
				cref = 1;
			} else {		/* jump to end of file */
			    if (cursor_address == ALIGNED(fend - 1))
				fend = file_end();
			    cursor_address = ALIGNED(fend - 1);
			    temp = fend - buf_size + alignment - 1;
			    ALIGN(temp);
			    if (view_address < temp) {
				wref        = 1;
				last_viewed  = view_address;
				view_address = temp;
			    } else
				cref = 1;
			}

			looptime = 0;
	        	break;
	            case 7:	/* ^G  display current filename + cursor addr */
	        	status_filename();
	        	status_mode();
	        	break;
	            case 'h':
#ifdef EMACS_FEEL
	            case 2:	/* ^B  back one page */
#endif
#ifdef KEY_LEFT
		    case KEY_LEFT:
#endif
	        	update_cursor(OFF);
	        	cref = 1;

	        	if (cursor_address >= alignment)
	        	    cursor_address -= alignment;
	        	else if (cursor_address > 0)
			    cursor_address = 0;

	        	if (cursor_address < view_address) {
	        	    wref         = 1;
	        	    last_viewed   = view_address;
	        	    view_address -= buf_size / 2;
			    VALIGN(view_address);
	        	    if (view_address < 0)
				view_address = 0;
		        }
	        	break;
	            case 'H':	/* move display (not cursor) left */
	        	last_viewed   = view_address;
	        	view_address += alignment;
#ifdef ALIGN_VIEW
	        	if (view_address > fend - alignment) {
	        		view_address  = fend - 1;
				ALIGN(view_address);
	        	}
#endif
	        	wref = 1;
	        	if (cursor_address < view_address)
	        		cursor_address = view_address;
	        	break;
	            case 8:	/* ^H  Give simple help */
#ifdef KEY_HELP
		    case KEY_HELP:
#endif
	        	looptime = 0;
	        	help_me(0);
	        	xtouchwin(main_window);
	        	xrefresh(main_window);
	        	break;
	            case 'I':	/* insert physical area */
	        	break;
	            case 9:	/* ^I  toggle hex/string edit mode */
	        	mode = !mode;
	        	cref = 1;
	        	status_mode();
	        	break;
	            case 'j':	/* move cursor down one line */
	            case 14:	/* ^N - for Emacs users */
#ifdef KEY_DOWN
		    case KEY_DOWN:
#endif
	        	update_cursor(OFF);
	        	cref = 1;
	        	if (cursor_address < (fend - cpline)) {
	        	    cursor_address += cpline;
	        	    if (cursor_address >= view_address + buf_size) {
	        		last_viewed   = view_address;
	        		view_address += buf_size / 2;
#ifdef ALIGN_VIEW
				ALIGN(view_address);
#endif
	        		wref         = 1;
	        		if (view_address > fend) {
	        			view_address = fend - 1;
#ifdef ALIGN_VIEW
					ALIGN(view_address);
#endif
	        		}
	        	    }
	        	}
	        	break;
	            case 'J':	/* move display (not cursor) down */
	        	update_cursor(OFF);
	        	if (view_address > 0) {
	        	    last_viewed = view_address;
	        	    view_address -= cpline;
	        	    if (view_address < 0)
	        		view_address = 0;
	        	    if (cursor_address >= view_address + inbuf_size + cpline)
	        		cursor_address -= cpline;
	        	}
	        	wref  = 1;
	        	break;
	            case 'k':	/* move cursor up one line */
	            case 16:	/* ^P - for Emacs users */
#ifdef KEY_UP
		    case KEY_UP:
#endif
	        	update_cursor(OFF);
	        	cref = 1;

	        	if (cursor_address >= cpline)
	        	    cursor_address -= cpline;

	        	if (cursor_address < view_address) {
	        	    wref         = 1;
	        	    last_viewed   = view_address;
	        	    view_address -= buf_size / 2;
#ifdef ALIGN_VIEW
			    ALIGN(view_address);
#endif
	        	    if (view_address < 0)
				view_address = 0;
	        	}
	        	break;
	            case 'K':	/* move display (not cursor) up */
	        	update_cursor(OFF);
	        	if (view_address < fend - cpline) {
	        		last_viewed   = view_address;
	        		view_address += cpline;
	        		if (cursor_address < view_address)
	        			cursor_address += cpline;
	        	}
	        	wref  = 1;
	        	break;
	            case 'l':	/* move cursor right */
#ifdef EMACS_FEEL
	            case 6:	/* ^F */
#endif
#ifdef KEY_RIGHT
		    case KEY_RIGHT:
#endif
	        	update_cursor(OFF);
	        	cref = 1;
	        	if (cursor_address < (fend - alignment)) {
	        	    cursor_address += alignment;
	        	    if (cursor_address >= (view_address + buf_size)) {
	        		wref         = 1;
	        		last_viewed   = view_address;
	        		view_address += buf_size / 2;
#ifdef ALIGN_VIEW
				ALIGN(view_address);
#endif
	        		if (view_address > fend)
	        			view_address = fend - 1;
	        	    }
	        	}
	        	break;
	            case 'L':		/* move display (not cursor) right */
	        	last_viewed   = view_address;
	        	view_address -= alignment;
	        	if (view_address < 0)
	        		view_address = 0;
	        	wref = 1;
	        	if (cursor_address >= view_address + inbuf_size + alignment) {
	        		cursor_address = view_address + inbuf_size - 1;
				ALIGN(cursor_address);
	        	}
	        	break;
	            case 12:		/* ^L  redraw screen */
#ifdef KEY_REFRESH
	            case KEY_REFRESH:
#endif
#ifdef KEY_CLEAR
	            case KEY_CLEAR:
#endif
			looptime = 0;
	        	wref = 1;
	        	daddr = -1;
			if (noscreen)
			    break;

	        	xclear(stdscr);
			xmove(stdscr, 0, 0);
			xclrtobot(stdscr);
	        	xtouchwin(main_window);
	        	xtouchwin(status_window);

	        	xrefresh(stdscr);

	        	break;
	            case 'm':	/* set mark */
#ifdef KEY_MARK
		    case KEY_MARK:
#endif
	        	set_mark(cursor_address);
	        	break;
	            case 'M':	/* move to middle of screen */
	        	update_cursor(OFF);
			cursor_address  = view_address + buf_size / 2;
			ALIGN(cursor_address);
			cref = 1;
	        	break;
	            case '.':	/* repeat last edit command - must precede ^M */
#ifdef KEY_REDO
		    case KEY_REDO:
#endif
			if (load_ksbuf_from_hist())
	            case 13:	/* ^M  start edit mode */
	            case 10:	/* ^J  start edit mode */
			looptime = 0;
	        	addr  = cursor_address - view_address;
	        	addr2 = addr >> 2;
	        	temp  = ibuffer[addr2];
	        	temp2 = ibuffer[addr2 + 1];
	        	temp3 = ibuffer[addr2 + 2];
			update_cursor(OFF);
#if !defined(Dynix) && !defined(Linux) && !defined(FreeBSD) && !defined(HPUX)
			if (!noscreen)
				raw();
#endif
	        	prompting = 1;
	        	if (mode == STRING)
	        		edit_mem_string(addr);
	        	else
	        		edit_mem_base(addr, wdismode);
	        	prompting = 0;
#if !defined(Dynix) && !defined(Linux) && !defined(FreeBSD) && !defined(HPUX)
			if (!noscreen) {
				noraw();
#	ifdef Solaris2
				cbreak();
#	endif
			}
#endif
	        	status_mode();
	        	if ((temp  != ibuffer[addr2]) ||
	        	    (temp2 != ibuffer[addr2 + 1]) ||
	        	    (temp3 != ibuffer[addr2 + 2]))
	        		dirty = 1;
	        	wref = 1;
	        	break;
	            case 'N':	/* switch search direction and re-search */
#ifdef KEY_PREVIOUS
		    case KEY_PREVIOUS:
#endif
	        	global_direction = !global_direction;
	            case 'n':	/* search again for same thing */
#ifdef KEY_NEXT
		    case KEY_NEXT:
#endif
	        	if (save_changes(1))
			    break;
	        	wref = 1;
	        	update_cursor(OFF);
	        	prompting = 1;
	        	cursor_address = generic_search(cursor_address);
			ALIGN(cursor_address);
	        	prompting = 0;
	        	if ((cursor_address < view_address) ||
	        	    (cursor_address >= view_address + inbuf_size)) {
	        		last_viewed = view_address;
	        		view_address = cursor_address;
	        		status_mode();
	        	}
	        	status_mode();
	        	daddr = -1;
	        	break;
	            case 'o':	/* rotate display character for non-printable */
	        	if (save_changes(1))
			    break;
	        	wref = 1;
	        	zmode++;
	        	zmode = zmode % 12;
	        	init_ascii();
	        	break;
	            case 'O':	/* rotate display character for nil */
	        	if (save_changes(1))
			    break;
	        	wref = 1;
	        	Zmode++;
	        	Zmode = Zmode % 12;
	        	init_ascii();
	        	break;
		    /* q and Q taken for exit below */
	            case 'r':	/* reread/redisplay file */
	        	daddr = -1;
	        	wref = 1;
	        	break;
	            case 'R':	/* change Read-only mode */
			ronly = !ronly;
			status_mode();
	        	break;
	            case 's':	/* cycle byte swapping off, word, long, quad */
			swapbytes |= Swapbytes | SWAPBYTES;
			swapbytes += swapbytes + 1;
			if (swapbytes == 15)
			    swapbytes = 0;
			if (swapmode == 1) {
				Swapbytes = swapbytes;
				swapbytes = 0;
			} else if (swapmode == 2) {
				SWAPBYTES = swapbytes;
				swapbytes = 0;
			}
	        	wref = 1;
	        	status_mode();
	        	break;
	            case 'S':	/* toggle word or character display swapping */
			swapmode = (swapmode + 1) % 3;
			switch (swapmode) {
			    case 0:
				swapbytes = SWAPBYTES;
				Swapbytes = 0;
				SWAPBYTES = 0;
				break;
			    case 1:
				Swapbytes = swapbytes;
				swapbytes = 0;
				SWAPBYTES = 0;
				break;
			    case 2:
				SWAPBYTES = Swapbytes;
				swapbytes = 0;
				Swapbytes = 0;
				break;
			}
	        	wref = 1;
	        	status_mode();
	        	break;
	            case 't':	/* toggle hex or decimal word display */
			if (wdismode != 0)
				wdismode = -1;
	            case 'T':	/* cycle word display hex,dec,float,double */
	        	wdismode = (wdismode + 1) % WDISMAX;
	        	switch (wdismode) {
	        	    case 1:
	        	    case 2:
	        		if (alignment < 4)
	        		    alignment = 4;
	        		break;
	        	    case 3:
				alignment = 8;
	        	}
			ALIGN(cursor_address);
			VALIGN(view_address);
	        	wref = 1;
	        	status_mode();
	        	break;
	            case 21:	/* ^U  up one half page */
	            case 'u':
	        	if (view_address > 0) {
	        	    wref	      = 1;
	        	    last_viewed   = view_address;
	        	    view_address -= buf_size / 2;
			    VALIGN(view_address);
	        	    if (view_address < 0)
	        	    	view_address = 0;
	        	    if (cursor_address >= view_address + inbuf_size) {
/*	        	        cursor_address  = view_address + inbuf_size - 1; */
	        	        cursor_address -= buf_size / 2;
			        ALIGN(cursor_address);
	        	    }
	        	}
	        	break;
	            case 'w':	/* toggle word display */
	        	wref = 1;
	        	wdis = !wdis;
	        	if (wdis == 0)
	        		chdis = 1;

	        	wpline = (COLS - addis * 10) / (9 * wdis + chdis * 4);
			cpline = wpline << 2;
	        	buf_size = (LINES - 1) * cpline;

	        	if (cursor_address >= view_address + buf_size) {
	        		cursor_address  = view_address + buf_size - 1;
	        		ALIGN(cursor_address);
	        	}

	        	xmove(main_window, 0, 0);
	        	xclrtobot(main_window);
	        	break;
	            case 23:	/* ^W  write changes */
	            case 'W':
#ifdef KEY_SAVE
		    case KEY_SAVE:
#endif
			looptime = 0;
	        	if (save_changes(1))
			    break;
	        	wref = 1;
	        	break;
#ifndef EMACS_FEEL
	            case 24:	/* ^X   exit program */
#endif
	            case 'q':
	            case 'x':
#ifdef KEY_EXIT
		    case KEY_EXIT:
#endif
			looptime = 0;
	        	if (save_changes(1))
			    break;
	        	running = 0;
	        	break;
	            case 3:	/* ^C  exit immediately - no save */
	            case 'Q':
#ifdef KEY_SEXIT
		    case KEY_SEXIT:
#endif
			dirty = 0;
			looptime = 0;
	        	running = 0;
	        	break;
	            case 'z':	/* relocate display, keeping cursor on addr */
			ch = get_key();
			switch (ch) {
			    case '.':	/* center current line */
				temp = (cursor_address - buf_size / 2);
				ALIGN(temp);
				if (temp < 0)
				    temp = 0;
				break;
			    case '-':	/* line to bottom of screen */
				temp = cursor_address + cpline - buf_size;
				ALIGN(temp);
				if (temp < 0)
				    temp = 0;
				break;
			    case '+':	/* bottom line to top of screen */
				temp  = view_address + buf_size - cpline;
				if (temp >= fend)
					temp = fend - 1;
				ALIGN(temp);
				if (cursor_address < temp)
				    cursor_address = temp;
				break;
			    case 10:	/* ^J  line to top of screen */
			    case 13:	/* ^M */
				temp = cursor_address;
				break;
			    default:	/* unknown, stay there */
				temp = view_address;
				break;
			}
			if (view_address != temp) {
			    last_viewed = view_address;
			    view_address = temp;
			    wref = 1;
			}
	        	break;
	            case 'Z':	/* save/exit if second Z received */
			ch = get_key();
			if (ch == 'Z') {
			    looptime = 0;
			    if (save_changes(0))
				break;
			    running = 0;
			}
	        	break;
#ifdef ZERO_START
	            case '0':	/* jump to start of file */
	        	update_cursor(OFF);
	        	if (view_address > 0) {
	        	    last_viewed  = view_address;
	        	    view_address = 0;
	        	    wref        = 1;
	        	    if (cursor_address >= view_address + inbuf_size) {
	        	        cursor_address  = view_address + inbuf_size - 1;
				ALIGN(cursor_address);
	        	    }
	        	} else {
	        	    cursor_address = 0;
	        	    cref	= 1;
	        	}
	        	break;
#endif
	            case '/':	/* search forward */
#ifdef KEY_FIND
		    case KEY_FIND:
#endif
			global_direction = -1;
	            case '?':	/* search backward */
#ifdef KEY_SFIND
		    case KEY_SFIND:
#endif
			global_direction++;

	        	if (save_changes(1))
			    break;
	        	wref = 1;
	        	update_cursor(OFF);

			prompting = 1;

/*			global_direction = (ch == '?') ? 1 : 0; */
#if !defined(Dynix) && !defined(Linux) && !defined(FreeBSD) && !defined(HPUX)
			if (!noscreen)
				raw();
#endif
	        	if (mode == STRING)
	        	    cursor_address = find_str(cursor_address);
	        	else
	        	    cursor_address = find_hex(cursor_address);

			prompting = 0;
#if !defined(Dynix) && !defined(Linux) && !defined(FreeBSD) && !defined(HPUX)
			if (!noscreen) {
				noraw();
#	if defined(Solaris2)
				cbreak();
#	endif
			}
#endif

	        	ALIGN(cursor_address);

	        	if ((cursor_address < view_address) ||
	        	    (cursor_address >= view_address + inbuf_size)) {
	        		last_viewed = view_address;
	        		view_address = cursor_address;
	        	}
	        	status_mode();
			wref = 1;
	        	daddr = -1;
			if (looptime)
				ch = 'n';
	        	break;
	            case '^':	/* go to beginning of current display row */
#ifndef ZERO_START
	            case '0':
#endif
#ifdef KEY_SLEFT
		    case KEY_SLEFT:
#endif
			update_cursor(OFF);
			cursor_address = view_address +
					 ((cursor_address - view_address) /
					  cpline) * cpline;
			ALIGN(cursor_address);
			cref = 1;
	        	break;
	            case '$':	/* go to end of current display row */
#ifdef KEY_SRIGHT
		    case KEY_SRIGHT:
#endif
			update_cursor(OFF);
			cursor_address = view_address +
					 ((cursor_address - view_address) /
					  cpline + 1) * cpline - 1;
			ALIGN(cursor_address);
			cref = 1;
	        	break;
	            case '+':	/* increase number of columns in display */
	            case '=':
	        	wref = 1;
	        	wpline++;
	        	if (wpline > (COLS - addis * 10)/(9 * wdis + chdis * 4))
	        	    wpline--;
			else
			    cpline += 4;
	        	buf_size = (LINES - 1) * cpline;

	        	break;
	            case '-':	/* decrease number of columns in display */
	            case '_':
	        	wref = 1;
	        	wpline--;
	        	if (wpline < 1)
	        	    wpline++;
			else
			    cpline -= 4;
			cpline = wpline << 2;
	        	buf_size = (LINES - 1) * cpline;

	        	if (cursor_address >= view_address + buf_size) {
	        	    cursor_address  = view_address + buf_size - 1;
	        	    ALIGN(cursor_address);
	        	}

	        	xmove(main_window, 0, 0);
	        	xclrtobot(main_window);
	        	break;
	            case '\'':	/* jump to address of mark */
	            case '`':
			looptime = 0;
	        	if ((temp = get_mark(-1)) != -1) {
			    ALIGN(temp);
			    last_viewed = cursor_address;
	        	    if ((temp > view_address + inbuf_size - 1) ||
			        (temp < view_address)) {
	        		view_address   = temp;
	        		cursor_address = temp;
	        		wref          = 1;
	        	    } else {
	        		update_cursor(OFF);
	        		cursor_address = temp;
	        		cref           = 1;
	        	    }
			}
	        	break;
	            case ')':	/* cursor to bottom right corner of display */
#ifdef KEY_END
		    case KEY_END:
#endif
	        	update_cursor(OFF);
	        	cursor_address  = view_address + inbuf_size - 1;
	        	ALIGN(cursor_address);
	        	cref = 1;
	        	break;
	            case '(':	/* cursor to top left corner of display */
#ifdef KEY_HOME
		    case KEY_HOME:
#endif
	        	update_cursor(OFF);
	        	cursor_address = view_address;
	        	cref = 1;
	        	break;
	            case '<':	/* increase alignment (cursor size) */
	        	update_cursor(OFF);
	        	alignment <<= 1;
	        	if (alignment > 8)
	        		alignment = 1;
	        	status_mode();
	        	cref = 1;
			ALIGN(cursor_address);
			if (cursor_address < view_address)
			    cursor_address += alignment;
#ifdef ALIGN_VIEW
			if (view_address & (alignment - 1)) {
			    ALIGN(view_address);
			    wref = 1;
			}
#endif
	        	break;
	            case '>':	/* decrease alignment (cursor size) */
	        	update_cursor(OFF);
	        	alignment >>= 1;
	        	if (alignment == 0)
	        		alignment = 8;
	        	status_mode();
	        	cref = 1;
			ALIGN(cursor_address);
#ifdef ALIGN_VIEW
			if (view_address & (alignment - 1)) {
			    ALIGN(view_address);
			    wref = 1;
			}
#endif
	        	break;
	            case '[':	/* double address display divisor -- bottom */
	        	divisor++;
	        	if (divisor > 29)
	        		divisor = 29;
	        	status_range(view_address, view_address + buf_size);
	        	break;
	            case ']':	/* halve address display divisor -- bottom */
	        	divisor--;
	        	if (divisor < 0)
	        		divisor = 0;
	        	status_range(view_address, view_address + buf_size);
	        	break;
	            case '{':	/* double address display divisor - left */
	        	divisor2++;
	        	if (divisor2 > 29)
	        		divisor2 = 29;
	        	status_range(view_address, view_address + buf_size);
	        	wref = 1;
	        	break;
	            case '}':	/* halve address display divisor - left */
	        	divisor2--;
	        	if (divisor2 < 0)
	        		divisor2 = 0;
	        	status_range(view_address, view_address + buf_size);
	        	wref = 1;
	        	break;
	            case '"':	/* toggle div or mod for bottom address disp */
	        	divmod = !divmod;
	        	status_range(view_address, view_address + buf_size);
	        	break;
	            case '~':	/* toggle div or mod for left address disp */
	        	divmod2 = !divmod2;
	        	wref = 1;
	        	break;
#ifdef COLON_MODE
		    case ':':	/* enter colon command mode prompt */
			temp = colon_mode();
			if (temp == -1)
			    running = 0;
			else {
			    if (temp == 1) {
				wref = 1;
			        status_filename();
			    }
			    status_range(view_address, view_address + buf_size);
			    status_mode();
			}
	        	break;
#endif
	        }
	if (kbuffer_size) {
	    if (wref) {
		refresh_buffer(1);
		if (cursor_address < view_address)
		    cursor_address = view_address;
		else if (cursor_address >= view_address + buf_size) {
		     cursor_address  = view_address + buf_size - 1;
		     ALIGN(cursor_address);
		}
	    }
	} else
	    if (wref | cref) {
	        if (wref) {
		    if (refresh_buffer(0)) {
	                view_address = last_viewed;
			refresh_buffer(1);
		    }
		    if (cursor_address < view_address)
		        cursor_address = view_address;
		    else if (cursor_address >= view_address + buf_size) {
	                cursor_address  = view_address + buf_size - 1;
	                ALIGN(cursor_address);
	            }
		    if (!noscreen)
	                show_addr(view_address);
	        }
		
		if (!noscreen) {
		    update_cursor(ON);
#ifndef Win32
#ifdef AMIGA
	            temp  = main_window->_maxy - 1;
	            temp2 = main_window->_maxx - 1;
#else
	            temp  = main_window->_cury;
	            temp2 = main_window->_curx - 1;
#endif
	            xmove(main_window, temp, temp2);
	            ch = winch(main_window);
	            xaddch(main_window, ch);
#endif

	            xmove(main_window, temp, temp2);
		    xrefresh(main_window);
		    if (wref)
		        xrefresh(main_window);
		}
		wref = 0;
		cref = 0;
	    }
	    rollover = 1;
	}
    }

    save_changes(1);

    if (!noscreen) {
	update_cursor(OFF);
	xrefresh(main_window);
	xstandend(main_window);

	xmove(status_window, 0, COLS - 10);
	xclrtoeol(status_window);
    }
    return(running);
}


static void kbuffer_append(str)
register char *str;
{
	char ch;
	register char *ptr;
	int len;

	len = parse_string(str);

	if (kbuffer_size + len > KBUFMAX) {
		de_init();
		fprintf(stderr, "key buffer overflow\n");
		exit(1);
	}

	ptr = kbuffer + kbuffer_size + len;
	kbuffer_size += len;

	while ((ch = *str++) != '\0')
	    *--ptr = ch;
}

int get_key()
{
	if (kbuffer_size)
		return(kbuffer[--kbuffer_size]);

#ifdef OS9
	{
		int ch = xgetch(main_window) & 255;

		if (ch == 0xff)
			return(ESC);
		else
			return(ch);
	}
#else
	return(xgetch(main_window));
#endif

}


void unget_key(ch)
char ch;
{
	kbuffer[kbuffer_size++] = ch;
}

#define LINE(text) fprintf(stderr, text, len, "")


static void print_usage(progname, just_name)
char *progname;
int just_name;
{
	int len;
	char *program;

	for (program = progname; *program != '\0'; program++);
	for (--program; program > progname; program--)
	    if ((*program == '/') || (*program == '\\')) {
		program++;
		break;
	    }

	if (just_name) {
	    printf("%s", program);
	    return;
	}

	len = strlen(program);

	fprintf(stderr, "Usage is:\n%s [ -hrsvw ] %s%sfilename [ filename(s) ... ]\n",
	        program,
#ifdef EDIT_MEMORY
	        "[ -a RANGE ] ",
#else
			"",
#endif
			"[ -k STRING ] ");

#ifdef EDIT_MEMORY
	LINE("%*s -a = edit memory at ADDRESS[-ENDADDR or :SIZE]\n");
#endif
	LINE("%*s -h = display this help information\n");
	LINE("%*s -k = specify initial keystrokes to perform\n");
	LINE("%*s -r = force read only mode\n");
	LINE("%*s -s = perform editing with no screen\n");
	LINE("%*s -v = print version\n");
	LINE("%*s -w = force read / write mode\n");
	fprintf(stderr, "\n  Environment FVINIT specifies startup keystrokes.\n");

	fprintf(stderr, "\n%s\n       ", version);
#if defined(Amiga)
	fprintf(stderr, " Amiga");
#elif defined(Win32)
	fprintf(stderr, " Win32");
#elif defined(Dynix)
	fprintf(stderr, " Dynix");
#elif defined(Linux)
	fprintf(stderr, " Linux");
#elif defined(NetBSD)
	fprintf(stderr, " NetBSD");
#elif defined(FreeBSD)
	fprintf(stderr, " FreeBSD");
#elif defined(HPUX)
	fprintf(stderr, " HPUX");
#elif defined(OS9)
#	if defined _OS9000
	fprintf(stderr, " OS-9000");
#	else
	fprintf(stderr, " OS-9");
#	endif
#elif defined(SunOS)
	fprintf(stderr, " SunOS");
#elif defined(unix)
	fprintf(stderr, " UNIX");
#else
	fprintf(stderr, " Unknown host");
#endif

#if defined(STRICT_VI)
	fprintf(stderr, " Strict_VI");
#endif

#if defined(ZERO_START)
	fprintf(stderr, " Zero_Start");
#endif

#if defined(EMACS_FEEL)
	fprintf(stderr, " Emacs_Feel");
#endif

#if defined(ALIGN_VIEW)
	fprintf(stderr, " Align_View");
#endif

#if defined(SHOW_EXTASCII)
	fprintf(stderr, " Show_ExtASCII");
#endif

#if defined(EDIT_MEMORY)
	fprintf(stderr, " Edit_Memory");
#endif

#if defined(COLON_MODE)
	fprintf(stderr, " Colon_Mode");
#endif

#if defined(HUGE_BUFFER)
	fprintf(stderr, " Huge_Buffer");
#endif

#if defined(COLOR_CURSES)
	fprintf(stderr, " Color_Curses");
#endif

#if defined(DEBUG)
	fprintf(stderr, " Debug");
#endif
	fprintf(stderr, "\n");

	exit(1);
}


void sigint()			/* catch ^C */
{
	if (prompting) {
		stopping = 1;
#if defined(Solaris2) || defined(OS9)
		signal(SIGINT, (void (*)(int)) sigint);
#endif
#ifdef OS9
		get_key();
#endif
		return;
	}

	de_init();
	exit(0);
}

#if !defined(AMIGA) && !defined(Win32)
void sigquit()			/* catch ^\ */
{
	de_init();
	exit(0);
}

#if !defined(OS9) && !defined(HPUX)
void sigwinch()			/* catch window size changes */
{
	struct winsize buf;

	if (noscreen)
		return;

	de_init();

	ioctl(1, TIOCGWINSZ, &buf);
	COLS  = buf.ws_col;
	LINES = buf.ws_row;

	init();

	status_filename();
	last_viewed = view_address;

	refresh_buffer(0);
	show_addr(view_address);
	if (cursor_address >= view_address + buf_size) {
		cursor_address  = view_address + inbuf_size - 1;
		ALIGN(cursor_address);
	}
	update_cursor(ON);
	xrefresh(main_window);
	status_mode();
#ifdef Solaris2
	signal(SIGWINCH, (void (*)(int)) sigwinch);
#endif
}
#endif  /* !OS9 */
#endif	/* !AMIGA */


#ifndef isoctal
#define isoctal(x) ((x >= '0') && (x <= '7'))
#endif

static int parse_string(str)
char *str;
{
	char ch;
	register char *nstr;
	register char *ostr;

	ostr = str;
	nstr = str;

	while ((ch = *ostr++) != '\0')
	    if (ch != '\\')
	    	*nstr++ = ch;	
	    else
		if (isoctal(*ostr)) {
		    ch = (*ostr++ - '0');
		    if (isoctal(*ostr)) {
			ch <<= 3;
			ch += (*ostr++ - '0');
			if (isoctal(*ostr)) {
			    ch <<= 3;
			    ch += (*ostr++ - '0');
			}
		    }
		    *nstr++ = ch;
		} else
		    switch ((ch = *ostr++)) {
			case '\0':
			    ostr--;
			    break;
			case 't':
			    *nstr++ = '\t';
			    break;
			case 'n':
			    *nstr++ = '\n';
			    break;
			case 'r':
			    *nstr++ = '\r';
			    break;
			case 'b':
			    *nstr++ = '\b';
			    break;
			default:
			    *nstr++ = ch;
			    break;
		    }

	*nstr = '\0';

	return(nstr - str);
}

#ifdef OS9
char *strdup(char *str)
{
    int   len;
    char *newstr;
 
    len = strlen(str) + 1;
    if ((newstr = (char *) malloc(len)) == NULL)
		return(NULL);
 
    return(memcpy(newstr, str, len));
}
#endif
