/* 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.
 */

#include <stdio.h>
#include <curses.h>
#include <string.h>
#include "main.h"
#if !defined(Amiga) && !defined(OS9) && !defined(FreeBSD)
#	include <malloc.h>
#endif
#include "screen.h"
#include "edit.h"

#ifdef NetBSD
#	include <sys/ttycom.h>
#endif

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

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

char *NONWORD = ",<.>/?;:'`~[{]}=+-\\|!@#$%^&*() \001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032";

#define MARKS   64
extern	int	generic_search();

STATIC	void	light_string();
STATIC	int	eget_key();

#define KSHISTMAX 256
static	char	kshist[KSHISTMAX];
static	int 	kshistsize = 0;

#define KSFULLHISTMAX 1024
static	char	ksfullhist[KSFULLHISTMAX];
static	int 	ksfullhistsize = 0;

#define KSBUFMAX 1024
char	ksbuf[KSBUFMAX];
int 	ksbufsize = 0;


int edit_mem_base(addr, base)
int addr;
int base;
{
	uchar	*origbuf;
	int	ch = ' ';
	int	cur = addr;
	int	row = 0;
	int	col = 0;
	int	icur;
	int	mrow, mcol;
	int	value;

	stopping = 0;

#ifdef Amiga
	leaveok(main_window, FALSE);
#endif
	xmove(status_window,  0, 20);
	xaddstr(status_window, "hex editing..");
	xclrtoeol(status_window);
	xrefresh(status_window);

	origbuf = (uchar *) malloc(buf_size);
	if (origbuf == NULL)
		error_exit("edit_mem_base: malloc failed");

	memcpy(origbuf, buffer, buf_size);

	mcol = 2 * cpline - 1;
	mrow = buf_size / cpline - 1;

	col = 2 * (addr % cpline);
	row = addr / cpline;

	/* stop on ^M ^J ^D or ^C */
	while ((ch != 13) && (ch != 10) && (ch != 4) && !stopping) {
	    cur = row * cpline + col / 2;
#ifdef Amiga
	    xstandout(main_window);
	    light_string(cur, 0, 1);
#endif
	    icur = cur ^ swapbytes;

	    xmove(main_window, icur / cpline, 10 * addis +
		  (icur % cpline) * 2 + ((icur % cpline) >> 2) + (col & 1));
	    xrefresh(main_window);
	    ch = get_key();
#ifdef Amiga
	    light_string(cur, 0, 1);
#endif
	    switch (ch) {
		case 8:		/* ^H  back */
		case 127:
		case 'h':
		case 2: 	/* ^B */
#ifdef KEY_LEFT
		case KEY_LEFT:
#endif
		    if (col)
			col--;
		    else if (row) {
			row--;
			col = mcol;
		    }
		    break;
		case 'j':	/* down one row */
#ifdef KEY_DOWN
		case KEY_DOWN:
#endif
		    if (row < mrow)
			row++;
		    break;
		case 'k':	/* up one row */
#ifdef KEY_UP
		case KEY_UP:
#endif
		    if (row > 0)
			row--;
		    break;
		case 'l':	/* right one column */
		case 6: 	/* ^F */
/*		case ' ':    commented out so paste of xxxx xxxx works */
#ifdef KEY_RIGHT
		case KEY_RIGHT:
#endif
		    if (col < mcol)
			col++;
		    else {
			if (row < mrow) {
			    row++;
			    col = 0;
			}
		    }
		    break;
		case '^':	/* back to start */
		    col = 2 * (addr % cpline);
		    row = addr / cpline;
		    break;
#ifndef EMACS_FEEL
		case 24:	/* ^X  quit */
#endif
		case 3:		/* ^C */
		case 'q':
		case 'Q':
		    memcpy(buffer, origbuf, buf_size);
		    ch = 4;
		    break;
		case '?':
		case 'H':
#ifdef KEY_HELP
		case KEY_HELP:
#endif
		    help_me(1);
		    xtouchwin(main_window);
		    xrefresh(main_window);
		    break;
		default:	/* guess it is hex */
		    value = -1;
		    if ((ch >= '0') && (ch <= '9'))
			value = ch - '0';
		    else if ((ch >= 'a') && (ch <= 'f'))
			value = ch - 'a' + 10;
		    else if ((ch >= 'A') && (ch <= 'F')) {
			value = ch - 'A' + 10;
			ch += 'a' - 'A';
		    } else
			break;

		    cur = row * cpline + col / 2;
		    if (col & 1)		/* odd position */
		        buffer[cur ^ SWAPBYTES] =
		    	(buffer[cur ^ SWAPBYTES] & 0xf0) | value;
		    else
		        buffer[cur ^ SWAPBYTES] =
		    	(buffer[cur ^ SWAPBYTES] & 0x0f) | (value << 4);

		    /* update in character area */
		    icur = cur ^ Swapbytes;
		    xmove(main_window, icur / cpline,
		          10 * addis + wpline * 9 + (icur % cpline));
#ifdef CHAR_COLOR
		    wattron(main_window, CHAR_COLOR);
#endif
		    xaddch_filt(buffer[cur ^ SWAPBYTES]);

		    /* update in word area */
		    icur = cur ^ swapbytes;
		    xmove(main_window, icur / cpline, 10 * addis +
		          (icur % cpline) * 2 + ((icur % cpline) >> 2) +
		          (col & 1));
#ifdef WORD_COLOR
		    wattron(main_window, WORD_COLOR);
#endif
		    xaddch(main_window, ch);

		    if (col < mcol)
		        col++;
		    else if (row < mrow) {
		        row++;
		        col = 0;
		    }
#ifdef NO_COLOR
		    wattrset(main_window, 0);
#endif
	    }
	}
#ifdef Amiga
	leaveok(main_window, TRUE);
#endif
	xmove(status_window, 0, 20);
	xclrtoeol(status_window);
	xrefresh(status_window);
	return(0);
}



int edit_mem_string(addr)
int addr;
{
	uchar	*origbuf;
	uchar	*prevstr;
	uchar	*prevstr2;
	uchar	*prevstrtemp;
	int	strsize;
	int	strpsize;
	int	cur;
	int	index;
	uchar	temp;
	uchar	temp2;
	int	old_alignment;
	int	emode = 1;   /* esc mode */
	int	amode = 0;   /* append mode */
	int	rollover = 1;
	int	looptime = 1;
	int	ch = '\0';
	int	ch2;
	int	fixed_x_offset;
	int	icur;

	stopping = 0;

	ksfullhistsize = 0;
	old_alignment = alignment;
	cur = addr;
	for (cur = addr; ((cur < buf_size - 1) && buffer[cur ^ SWAPBYTES]); cur++);
	strsize = cur - addr;
	strpsize = strsize + 32;
	origbuf = (uchar *) malloc(strpsize);
	prevstr = (uchar *) malloc(strpsize);
	prevstr2 = (uchar *) malloc(strpsize);
	if ((origbuf == NULL) ||
	    (prevstr == NULL) ||
	    (prevstr2 == NULL))
		error_exit("edit_mem_string: malloc failed");
	memcpy(origbuf, buffer + addr, strpsize);
	memcpy(prevstr, buffer + addr, strpsize);
	memcpy(prevstr2, buffer + addr, strpsize);


	xmove(status_window,  0, 20);
	if (mode)
	    xaddstr(status_window, "string editing..");
	else
	    xaddstr(status_window, "hex editing..");
	xclrtoeol(status_window);
	xrefresh(status_window);

	fixed_x_offset = addis * 10 + wpline * 9 * wdis;

	cur = addr;
	do {			/* ^D - ends editing session */

	    light_string(addr, 1, strsize);
	    light_string(addr + strsize, 0, strpsize - strsize);

	    icur = cur ^ Swapbytes;
#ifdef Amiga
	    light_string(cur, 2, 1);
#endif
	    xmove(main_window, icur / cpline, fixed_x_offset + icur % cpline);

	    xrefresh(main_window);

	    ch = eget_key();

	    if (emode)
		if (strchr("~aAcCdDiIrRsx", ch))	/* edit cmds */
		    if (looptime > 1) {
			sprintf(kshist, "%d%c", looptime, ch);
			kshistsize = strlen(kshist);
		    } else {
			kshist[0] = ch;
			kshistsize = 1;
		    }
		else if (strchr("?/.0^befG$\010\177hjkl uUtTw\012\015\030q\004\033", ch))
		    kshistsize--;			/* motion cmd */

	    switch (ch) {	/* FOLLOWING WORK IN BOTH EDIT OR INSERT MODE */
#ifndef EMACS_FEEL
		case 24:	/* ^X  abort editing - must precede ^D */
		    memcpy(buffer + addr, origbuf, strpsize);
#endif
		case 4:		/* ^D - done editing */
		case 10:	/* ^R */
		case 13:	/* ^M */
		    ch = 4;	/* ^D */
		    break;
		case ESC:	/* 27  return to command mode */
		    if (!emode && amode)
			if (cur > addr)
			    cur--;
		    looptime = 1;
		    rollover = 1;
		    emode = 1;
		    break;
		case 8:		/* ^H delete character to left (or crsr left) */
		case 127:	/* ^? */
		    if (looptime < 1)
			looptime = 1;
		    if (emode) {
			if (cur > addr)
			    cur--;
			break;
		    }
		    memcpy(prevstr, buffer + addr, strpsize);
		    if (cur > addr) {
			cur--;
			for (index = cur; index <= addr + strsize - looptime;
			     index++)
			    buffer[index ^ SWAPBYTES] =
				buffer[(index + looptime) ^ SWAPBYTES];
			for (; index <= addr + strsize; index++)
			    buffer[index ^ SWAPBYTES] = '\0';
		    }
		    break;
		default:
		    if (emode)	/* EDIT MODE COMMAND */
			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;
			    else
				rollover = 1;

			    switch (ch) {
#ifdef DEBUG
			    case '?':
				printf("___%.*s___", kshistsize, kshist);
				break;
			    case '/':
				printf("___%.*s___", ksbufsize, ksbuf);
				break;
#endif
			    case '.':
				while ((looptime--) &&
				       (ksbufsize + kshistsize < 1023))
				    for (index = kshistsize; index;)
					ksbuf[ksbufsize++] = kshist[--index];
				break;
			    case '~':
				while (looptime--) {
				    if (buffer[cur ^ SWAPBYTES] & 64)
					buffer[cur ^ SWAPBYTES] ^= 32;
				    if (cur + 1 < addr + strpsize)
					cur++;
				}
				break;
			    case '0':	/* beginning of line */
				cur = addr;
				break;
			    case '^':	/* beginning of text */
				cur = addr;
				while ((cur == ' ') || (cur == '\t'))
					cur++;
				if (cur > addr + strpsize)
					cur = addr;
				break;
			    case 'A':	/* append at end - must precede 'a' */
				cur = addr + strsize - 1;
			    case 'a':	/* append after cursor */
				memcpy(prevstr, buffer + addr, strpsize);
				amode = 1;
				emode = 0;
				if (cur + 1 < addr + strpsize)
					cur++;
				break;
			    case 'b':	/* back a single word */
				while (looptime--) {
				    index = cur - 1;
				    for (; index >= addr; index--)
					if (!strchr(NONWORD, buffer[index ^ SWAPBYTES]))
					    break;
				    for (; index >= addr; index--)
					if (strchr(NONWORD, buffer[index ^ SWAPBYTES]))
					    break;
					else
					    cur = index;
				}
				break;
			    case 'c':	/* change (range) - must precede 'd' */
				emode = 0;
			    case 'd':	/* delete (range) */
				memcpy(prevstr, buffer + addr, strpsize);
				ch2 = eget_key();

				if (ch2 == 'w') {	/* word */
				    while (looptime--) {
					if (strchr(NONWORD, buffer[cur ^ SWAPBYTES]))
					    index = cur + 1;
					else
					for (index = cur + 1;
					     index < addr + strsize; index++)
					    if (strchr(NONWORD, buffer[index ^ SWAPBYTES]))
						break;
				        temp = index - cur;
				        if ((buffer[index ^ SWAPBYTES] == ' ') &&
					    (ch == 'd'))
					    temp++;
				        for (index = cur; index <= (int)
					     (addr + strsize - temp); index++)
					    buffer[index ^ SWAPBYTES] = buffer[(index + temp) ^ SWAPBYTES];
				        for (; index <= addr + strsize; index++)
					    buffer[index ^ SWAPBYTES] = '\0';
				    }
				} else if (ch2 == 't') {  /* to character */
				    ch2 = eget_key();
				    while (looptime--)
				    for (index = cur + 1;
					 index < addr + strsize; index++)
					if (buffer[index ^ SWAPBYTES] == ch2) {
					    temp = index - cur;
					    for (index = cur; index <= (int)(addr + strsize - temp); index++)
						buffer[index ^ SWAPBYTES] = buffer[(index + temp) ^ SWAPBYTES];
					    for (; index <= addr + strsize; index++)
						buffer[index ^ SWAPBYTES] = '\0';
					    break;
					}
				} else if (ch2 == 'f') {  /* including char. */
				    ch2 = eget_key();
				    while (looptime--)
					for (index = cur + 1;
					     index < addr + strsize; index++)
					if (buffer[index ^ SWAPBYTES] == ch2) {
					    temp = index - cur + 1;
					    for (index = cur; index <= (addr +
					               strsize - temp); index++)
						buffer[index ^ SWAPBYTES] =
					                   buffer[(index + temp) ^ SWAPBYTES];
					    for (; index <= addr + strsize;
					                                index++)
						buffer[index ^ SWAPBYTES] = '\0';
					    break;
					}
				} else if ((ch2 == '0') ||  /* start of line */
				           (ch2 == '^')) {
				    if (cur != addr) {
				        temp = strsize - cur + addr;
				        memcpy(buffer + addr,
				               buffer + cur, temp);
				        memset(buffer + addr + temp,
				               0, cur - addr);
				        cur = addr;
				    }
				} else if (ch2 == '$')	/* rest of line */
					goto delete_line;
				break;
			    case 'C':	/* change line - must precede 'D' */
				emode = 0;
				amode = 1;
			    case 'D':	/* delete line - must follow 'C' */
				delete_line:
				memcpy(prevstr, buffer + addr, strpsize);
				for (index = cur; index <= addr + strsize; index++)
				    buffer[index ^ SWAPBYTES] = '\0';
				break;
			    case 'e':	/* go to end of current word */
				while (looptime--) {
				    index = cur + 1;
				    for (; index < addr + strpsize; index++)
					if (!strchr(NONWORD, buffer[index ^ SWAPBYTES]))
					    break;
				    for (; index < addr + strpsize; index++)
					if (strchr(NONWORD, buffer[index ^ SWAPBYTES]))
					    break;
					else
					    cur = index;
				}
				break;
			    case 'f':	/* go to specific character */
				ch2 = eget_key();
				while (looptime--)
				    for (index = cur + 1;
					 index < addr + strsize; index++)
					if (buffer[index ^ SWAPBYTES] == ch2) {
						cur = index;
						break;
					}
				break;
			    case 'I':	/* insert at start - must precede 'i' */
				cur = addr;
			    case 'i':	/* insert at cursor position */
				memcpy(prevstr, buffer + addr, strpsize);
				emode = 0;
				break;
			    case 'G':	/* go to end of string */
			    case '$':
				cur = addr + strsize - 1;
				break;
			    case 8:	/* ^H - go left one character */
			    case 127:	/* ^? */
			    case 'h':
			    case 2: 	/* ^B */
#ifdef KEY_LEFT
			    case KEY_LEFT:
#endif
				while (looptime--)
				    if (cur > addr)
					cur--;
				break;
			    case 'j':	/* go down one row */
#ifdef KEY_DOWN
			    case KEY_DOWN:
#endif
				while (looptime--)
				    if (cur + cpline< addr + strpsize)
					cur += cpline;
				break;
			    case 'k':	/* go up one row */
#ifdef KEY_UP
			    case KEY_UP:
#endif
				while (looptime--)
				    if (cur - cpline >= addr)
					cur -= cpline;
				break;
			    case 'l':	/* go right one character */
			    case ' ':
			    case 6: 	/* ^F */
#ifdef KEY_RIGHT
			    case KEY_RIGHT:
#endif
				while (looptime--)
				    if (cur + 1 < addr + strpsize)
					cur++;
				break;
			    case 'q':	/* terminate edit - no change */
			    case 3:	/* ^C */
#ifndef EMACS_FEEL
			    case 24:	/* ^X */
#endif
				memcpy(buffer + addr, origbuf, strpsize);
				ch = '\004';
				break;
			    case 'r':	/* replace character */
				memcpy(prevstr, buffer + addr, strpsize);
				while (looptime--)
				    if (cur < addr + strpsize) {
					ch2 = eget_key();
					buffer[cur ^ SWAPBYTES] = ch2;
				        light_string(addr, 1, strsize);
				        light_string(addr + strsize, 0,
						     strpsize - strsize);
					cur++;
				        icur = cur ^ Swapbytes;
				        xmove(main_window, icur / cpline,
					      fixed_x_offset + icur % cpline);
				        xrefresh(main_window);
				    }
				break;
			    case 'R':	/* overstrike mode */
				memcpy(prevstr, buffer + addr, strpsize);
				ch2 = eget_key();
				while (ch2 != ESC) {
				    if ((ch2 == 8) || (ch2 == 127)) {
					if (cur > addr)
					    cur--;
				    } else {
					if (cur < addr + strpsize) {
					    buffer[cur ^ SWAPBYTES] = ch2;
					} else
					    break;
					cur++;
				    }
				    light_string(addr, 1, strsize);
				    light_string(addr + strsize, 0,
						 strpsize - strsize);
				    icur = cur ^ Swapbytes;
				    xmove(main_window, icur / cpline,
					  fixed_x_offset + icur % cpline);
				    xrefresh(main_window);

				    ch2 = eget_key();
				}
				break;
			    case 's':	/* substitute string for character */
				memcpy(prevstr, buffer + addr, strpsize);
				for (index = cur; index <= addr+strsize-looptime; index++)
				    buffer[index ^ SWAPBYTES] = buffer[(index + looptime) ^ SWAPBYTES];
				for (; index <= addr + strsize; index++)
				    buffer[index ^ SWAPBYTES] = '\0';
				emode = 0;
				break;
			    case 't':	/* move forward just before character */
			    case 'T':
				ch2 = eget_key();
				while (looptime--)
				    for (index = cur + 1;
					 index < addr + strsize; index++)
					if (buffer[index ^ SWAPBYTES] == ch2) {
						cur = index - 1;
						break;
					}
				break;
			    case 'u':	/* undo last edit */
				memcpy(prevstr2, buffer + addr, strpsize);
				memcpy(buffer + addr, prevstr, strpsize);
				prevstrtemp = prevstr;
				prevstr = prevstr2;
				prevstr2 = prevstrtemp;
				break;
			    case 'U':	/* Undo all edits */
				memcpy(prevstr, buffer + addr, strpsize);
				memcpy(buffer + addr, origbuf, strpsize);
				break;
			    case 'w':	/* move forward to next word */
				while (looptime--)
				    for (index = cur + 1; index < addr + strpsize;index++)
					if (strchr(NONWORD, buffer[index ^ SWAPBYTES])) {
					    for (index++; index < addr + strpsize;index++)
						if (!strchr(NONWORD, buffer[index ^ SWAPBYTES])) {
						    cur = index;
						    break;
						}
					    break;
					}
				break;
			    case 'x':	/* delete character */
				memcpy(prevstr, buffer + addr, strpsize);
				for (index = cur; index <= addr+strsize-looptime; index++)
				    buffer[index ^ SWAPBYTES] = buffer[(index + looptime) ^ SWAPBYTES];
				for (; index <= addr + strsize; index++)
				    buffer[index ^ SWAPBYTES] = '\0';
				break;
			    case '?':	/* help me */
#ifdef KEY_HELP
			    case KEY_HELP:
#endif
				help_me(2);
				xtouchwin(main_window);
				xrefresh(main_window);
				break;
			    }
			}
		    else {   /* INSERT MODE (CHARACTER) */
		        if (ch == 22)  /* ^V - insert verbose next character */
			    temp = eget_key();
			else
			    temp = ch;
			for (index = cur; index < addr + strsize; index++) {
			    temp2 = buffer[index ^ SWAPBYTES];
			    buffer[index ^ SWAPBYTES] = temp;
			    temp = temp2;
			}
			if ((index == cur) && (index < addr + strpsize)) {
			    buffer[index ^ SWAPBYTES] = temp;  /* already at end of buffer, add more */
			}
			if (cur + 1 < addr + strpsize)
				cur++;
		    }
	    }
	} while ((ch != 4) && !stopping);	/* ^D or ^C - end editing session */
	light_string(addr, 0, strsize);

	alignment = old_alignment;
	xmove(status_window,  0, 20);
	xclrtoeol(status_window);
	xrefresh(status_window);

	free(origbuf);
	free(prevstr);

	return(0);
}


STATIC void light_string(addr, hi, strsize)
int addr;
int hi;
int strsize;
{
	int cur;
	int icur;

	if (hi > 0)
	    xstandout(main_window);

	if (chdis) {
#if defined(CRSR_COLOR) || defined(WORD_COLOR) || defined(CHAR_COLOR)
	    if (!noscreen)
		if (hi == 2)
		    wattron(main_window, CRSR_COLOR);
		else if (hi)
		    wattron(main_window, WORD_COLOR);
		else
		    wattron(main_window, CHAR_COLOR);
#endif
	    cur = addr;
	    while ((cur < buf_size - 1) && ((cur - addr) < strsize)) {
		icur = cur ^ Swapbytes;
		xmove(main_window, icur / cpline,
		      addis * 10 + wpline * 9 * wdis +
		      (icur % cpline));
		xaddch_filt(buffer[cur ^ SWAPBYTES]);
		cur++;
	    }
	}

	if (wdis) {
#if defined(CRSR_COLOR) || defined(WORD_COLOR) || defined(CHAR_COLOR)
	    if (!noscreen)
		if (hi == 2)
		    wattron(main_window, CRSR_COLOR);
		else if (hi)
		    wattron(main_window, CHAR_COLOR);
		else
		    wattron(main_window, WORD_COLOR);
#endif
	    cur = addr;
	    while ((cur < buf_size - 1) && ((cur - addr) < strsize)) {
		icur = cur ^ swapbytes;
		xmove(main_window, icur / cpline,
		      addis * 10 + ((icur >> 2) % wpline) * 9 +
		      (icur & 3) * 2);
		xaddch(main_window, hex[buffer[cur ^ SWAPBYTES] >> 4]);
		xaddch(main_window, hex[buffer[cur ^ SWAPBYTES] & 15]);
		cur++;
	    }
	}

	if (hi > 0)
	    xstandend(main_window);

#ifdef NO_COLOR
	if (!noscreen)
	    wattrset(main_window, 0);
#endif
}


STATIC int eget_key()
{
    int ch;

    if (ksbufsize)
	ch = ksbuf[--ksbufsize];
    else
	ch = get_key();

    if (ch != '.')
	ksfullhist[ksfullhistsize++] = ch;
    kshist[kshistsize++] = ch;

    return(ch);
}

int load_ksbuf_from_hist()
{
    int index;

    if (kshistsize == 0)
	return(1);

    ksbufsize = 0;

#ifdef NOT
    /* following is not needed because buffer contains exit keystrokes */
    ksbuf[ksbufsize++] = 4;         /* ^D */
    ksbuf[ksbufsize++] = 27;        /* ESC */
#endif

    for (index = ksfullhistsize; index && (ksfullhistsize < KSBUFMAX - 1); )
	ksbuf[ksbufsize++] = ksfullhist[--index];

    return(0);
}
