/* BVI  -  Binary Visual Editor
 *
 * 1996-02-28  V 1.0.0
 * 1999-01-27  V 1.1.0
 * 1999-04-22  V 1.1.1 
 * 1999-07-01  V 1.2.0 beta
 * 1999-10-22  V 1.2.0 final
 * 2000-05-10  V 1.3.0 alpha
 * 2000-10-24  V 1.3.0 final
 * 2002-01-03  V 1.3.1
 * 2004-01-04  V 1.3.2
 * 2006-04-04  V 1.3.3
 * 2013-08-23  V 1.4.0alpha
 * 2014-10-07  V 1.4.0
 * 2019-10-12  V 1.4.1
 * 2023-03-06  V 1.4.2
 *
 * NOTE: Edit this file with tabstop=4 !
 *
 * Copyright 1996-2023 by Gerhard Buergmann
 * gerhard@puon.at
 *
 * 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 3, 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.
 *
 * See file COPYING for information on distribution conditions.
 */

#include <sys/types.h>
#include <unistd.h>

#include "bvi.h"
#include "set.h"

#ifdef HAVE_LOCALE_H
#	include <locale.h>
#endif


char	*copyright  = "(C) GPL 1996-2023 by Gerhard Buergmann";

jmp_buf	env;        /* context for `longjmp' function   */

int		loc;
int		maxx, maxy, x, xx, y;
int		screen, status, statsize;
off_t	size;
PTR		mem = NULL;
PTR		curpos;
PTR		maxpos;
PTR		pagepos;
PTR		spos;
char	*name = NULL;
char	*shell;
char	string[MAXCMD+1];
char	cmdstr[MAXCMD+1] = "";
FILE	*Ausgabe_Datei;
int		edits = 0;
int		AnzAdd, Anzahl, Anzahl3;
off_t	filesize, memsize, undosize;


long	precount = -1;

int		block_flag = 0;


off_t	block_begin, block_end, block_size;


char	**files;		/* list of input files */
int		numfiles;		/* number of input files */
int		curfile;		/* number of the current file */

int		arrnum = 0;
char	numarr[MAXCMD+1];		/* string for collecting number */
char	rep_buf[BUFFER];

PTR		current;
PTR		last_motion;
PTR		current_start;
PTR		undo_start;
off_t	undo_count;
off_t	yanked = 0L;
char	*yank_buf = NULL;
char	*undo_buf = NULL;
char	*fname_buf = NULL;
PTR		markbuf[26];

char	addr_form[15];

char	*nobytes = "No bytes@in the buffer";

static	char	progname[8];
static	char	line[MAXCMD+1];
static	int		mark;
static	int		wrstat = 1;


void
usage()
{

	fprintf(stderr, "Usage: %s [-R] [-c cmd | +cmd] [-f script]\n\
	   [-s skip] [-e end] [-n length] file ...\n\
	   file offset/size: 10k, 20m, 1g, 0x1000 hex, 0200 octal\n", progname);

	exit(1);
}


int
main(argc, argv)
	int argc;
	char *argv[];
{
	int		ch;
	int		lflag;
	long	count;
	int		n = 1;
	int		script = -1;
	off_t	inaddr;
	char	*poi;



#ifdef HAVE_LOCALE_H
	setlocale(LC_ALL, "");
#endif
	poi = strrchr(argv[0], DELIM);

	if (poi) strncpy(progname, ++poi, 7);
		else strncpy(progname, argv[0], 7);
	strtok(progname, ".");

	if (!strcasecmp(progname, "bview")) {
		params[P_RO].flags |= P_CHANGED;
		P(P_RO) = TRUE;
	} else if (!strcasecmp(progname, "bvedit")) {
		/* This should be the beginners version */
	}

	if (isatty(fileno(stdin)) == 0) {
		// Guckes
		fprintf(stderr, "Input is not from a terminal\n");
    	exit(1);
	}


	while (n < argc) {
		switch (argv[n][0]) {
		case '-':
			if (argv[n][1] == 'R') {
				params[P_RO].flags |= P_CHANGED;
				P(P_RO) = TRUE;
			} else if (argv[n][1] == 'c') {
				if (argv[n + 1] == NULL) {
					usage();
				} else {
					strcpy(cmdstr, argv[++n]);
				}
			} else if (argv[n][1] == 'f') {
				if (argv[n + 1] == NULL || argv[n + 1][0] == '-') {
					usage();
				} else {
					script = ++n;
				}

			} else if (argv[n][1] == 's') {
				if (argv[n + 1] == NULL || argv[n + 1][0] == '-') {
					usage();
				} else {
					block_begin = calc_size(argv[++n]);
					block_flag |= BLOCK_BEGIN;
				}
			} else if (argv[n][1] == 'e') {
				if (argv[n + 1] == NULL || argv[n + 1][0] == '-') {
					usage();
				} else {
					block_end = calc_size(argv[++n]);
					block_flag |= BLOCK_END;
				}
			} else if (argv[n][1] == 'n') {
				if (argv[n + 1] == NULL || argv[n + 1][0] == '-') {
					usage();
				} else {
					block_size = calc_size(argv[++n]);
					block_flag |= BLOCK_LEN;
				}
			} else if (argv[n][1] == 'w') {
				if (argv[n][2] == '\0') {
					usage();
				} else {
					params[P_LI].flags |= P_CHANGED;
					P(P_LI) = atoi(argv[n] + 2);
				}
			} else usage();
			n++;
			break;
		case '+':			/* +cmd */
			if (argv[n][1] == '\0') {
				strcpy(cmdstr, "$");
			} else {
				strcpy(cmdstr, &argv[n][1]);
			}
			n++;
			break;
		default:			/* must be a file name */
			name = strdup(argv[n]);
			files = &(argv[n]);
			numfiles = argc - n;
			n = argc;
			break;
		}
	}
	/* TODO default block_size - end of file up to max 64 KB with warning */
	switch (block_flag) {
	case BLOCK_BEGIN:
		/* Acc. to SF-Error 3036881 we should use the whole rest of the file */
		/*
		block_size = 1024;
		block_end = block_begin + block_size - 1;
		*/
		break;
	case BLOCK_END:
		block_begin = 0;
		block_size = block_end - block_begin + 1;
		break;
	case BLOCK_BEGIN|BLOCK_END:
		block_size = block_end - block_begin + 1;
		break;
	case BLOCK_LEN:
		block_begin = 0;
		block_end = block_begin + block_size - 1;
		break;
	case BLOCK_BEGIN|BLOCK_LEN:
		block_end = block_begin + block_size - 1;
		break;
	case BLOCK_END|BLOCK_LEN:
		block_begin = block_end + 1 - block_size;
		break;
	case BLOCK_BEGIN|BLOCK_END|BLOCK_LEN:
		if (block_end - block_begin != block_size + 1) {
			fprintf(stderr, "Ambigous block data\n");
			exit(1);
		}
		break;
	}
	if (block_flag && !numfiles) {
		fprintf(stderr, "Cannot read a range of a nonexisting file\n");
		exit(1);
	}
	if (numfiles > 1) fprintf(stderr, "%d files to edit\n", numfiles);
	curfile = 0;

	/****** Initialisation of curses ******/
	initscr();
	attrset(A_NORMAL);

	maxy = LINES;
	if (params[P_LI].flags & P_CHANGED) maxy = P(P_LI);
	P(P_SS) = maxy / 2;
	P(P_LI) = maxy;
	maxy--;
	keypad(stdscr, TRUE);
	scrollok(stdscr, TRUE);
	nonl();
	cbreak();
	noecho();

    /* address column width */
    /*  default is 8 + 2 blanks  */
    /* if block_begin has 8 hex digits or more */
    /* reserve 1 hex digit more than required  */
    char tmp[sizeof(block_begin) * 2 + 3];
    AnzAdd = sprintf(tmp, "%llX", (long long unsigned)block_begin) + 1;
    if (AnzAdd < 8)
        AnzAdd = 8;
    if (AnzAdd > sizeof(block_begin) * 2)
        AnzAdd = sizeof(block_begin) * 2;
    sprintf(addr_form,  "%%0%dllX  ", AnzAdd);
    AnzAdd = sprintf(tmp, addr_form, block_begin);

	Anzahl = ((COLS - AnzAdd - 1) / 16) * 4;
	P(P_CM) = Anzahl;
	maxx = Anzahl * 4 + AnzAdd + 1;
	Anzahl3 = Anzahl * 3;
	statsize = 35;
	status = Anzahl3 + Anzahl - statsize;
	screen = Anzahl * (maxy - 1);

	signal(SIGINT, SIG_IGN);
	filesize = load(name);

	bvi_init(argv[0]);
	params[P_TT].svalue = terminal;
	if (block_flag && (P(P_MM) == TRUE)) {
		P(P_MM) = FALSE;
		params[P_TT].flags |= P_CHANGED;
	}
	if (script > -1)
		read_rc(argv[script]);
	if (*cmdstr != '\0')
		docmdline(cmdstr);
	msg(fname_buf);
	
	/* main loop */
	do {
		setjmp(env);
		current = (PTR)(pagepos + y * Anzahl + xpos());
		if (wrstat) statpos();
		wrstat = 1;
		setcur();
		ch = vgetc();
		while (ch >= '0' && ch <= '9') {
			if (arrnum < MAXCMD) numarr[arrnum++] = ch;
			ch = vgetc();
		}
		numarr[arrnum] = '\0';
		if (arrnum != 0) precount = strtoll(numarr, (char **)NULL, 10);
			else precount = -1;
		lflag = arrnum = 0;

		switch (ch) {
		case '^':	x = AnzAdd;
					loc = HEX;
					break;
		/*
		case '0':	x = AnzAdd + Anzahl3;
					loc = ASCII;
					break;
		*/
		case '$':	x = AnzAdd - 1 + Anzahl3 + Anzahl;
					loc = ASCII;
					break;
		case '\t':  toggle();
					break;
		case '~':	if (precount < 1) precount = 1;
					sprintf(rep_buf, "%ld~", precount);
					do_tilde((off_t)precount);
					lflag++;
					break;
		case KEY_HOME:
		case 'H':	if (precount > 0) {
						y = --precount;
						if (y > maxy - 1) {
							scrolldown(y - maxy + 1);
							y = maxy - 1; }
					} else y = 0;
					if (loc == HEX) x = AnzAdd;
						else	x = AnzAdd + Anzahl3;
					break;
		case 'M':	y = maxy / 2;
					if ((PTR)(pagepos + screen) > maxpos)
						y = (int)(maxpos - pagepos) / Anzahl / 2;
					if (loc == HEX) x = AnzAdd;
						else	x = AnzAdd + Anzahl3;
					break;
		case KEY_LL:
		case 'L':   if (precount < 1) precount = 1;
					n = maxy - 1;
					if ((PTR)((pagepos + screen)) > maxpos)
						n = (int)(maxpos - pagepos) / Anzahl;
					if (precount < n) y = n + 1 - precount;
					if (loc == HEX) x = AnzAdd;
						else	x = AnzAdd + Anzahl3;
					break;
		case BVICTRL('H'):
		case ASCII_DEL:
		case KEY_BACKSPACE:
		case KEY_LEFT:
		case 'h':	do {
						if (x > (AnzAdd + 2) && x < (Anzahl3 + AnzAdd + 1))
								x -= 3;
							else
								if (x > (Anzahl3 + AnzAdd - 2))  x--;
					} while (--precount > 0);
					if (x < AnzAdd + Anzahl3) loc = HEX;
						else loc = ASCII;
					break;
		case ' ':
		case KEY_RIGHT:
		case 'l':	do {
						/*
						if (x < (Anzahl3 + 6))  x += 3;
						*/
						if (x < (Anzahl3 + AnzAdd - 2))  x += 3;
							else if (x > (Anzahl3 + 3)
								&& x < (Anzahl3 + AnzAdd - 1 + Anzahl))
									x++;
					} while (--precount > 0);
					if (x < AnzAdd + Anzahl3) loc = HEX;
						else loc = ASCII;
					break;
		case '-':
		case KEY_UP :
		case 'k':	do {
						if (y > 0) y--;
							else scrollup(1);
					} while(--precount > 0);
					break;
		case '+':
		case CR:	if (loc == HEX) x = AnzAdd;
						else		x = AnzAdd + Anzahl3;
		case 'j':
		case BVICTRL('J'):
		case BVICTRL('N'):
		case KEY_DOWN:
					do {
						if ((PTR)((pagepos + (y + 1) * Anzahl)) > maxpos) break;
						if (y < (maxy - 1))	y++;
						else scrolldown(1);
					} while(--precount > 0);
					break;
		case '|':   if (precount < 1) break;
					if (loc == ASCII) x = AnzAdd - 1 + Anzahl3 + precount;
						else x = 5 + 3 * precount;
					if (x > AnzAdd - 1 + Anzahl3 + Anzahl) {
						x = AnzAdd - 1 + Anzahl3 + Anzahl;
						loc = ASCII; }
					break;
		case ':' :	clearstr();
					addch(ch);
					refresh();
					getcmdstr(cmdstr, 1);
					if (strlen(cmdstr))
						docmdline(cmdstr);
					break;
		case BVICTRL('B'):
		case KEY_PPAGE: 	/**** Previous Page ****/
					if (mem <= (PTR)(pagepos - screen))	pagepos -= screen;
						else	pagepos = mem;
					repaint();
					break;
		case BVICTRL('D'):
					if (precount > 1) P(P_SS) = precount;
					scrolldown(P(P_SS));
					break;
		case BVICTRL('U'):
					if (precount > 1) P(P_SS) = precount;
					scrollup(P(P_SS));
					break;
		case BVICTRL('E'):
					if (y > 0) y--;
					scrolldown(1);
					break;
		case BVICTRL('F'):
		case KEY_NPAGE: 	/**** Next Page *****/
					if (maxpos >= (PTR)(pagepos + screen))  {
						pagepos += screen;
						current += screen;
						if (current - mem >= filesize) {
							current = mem + filesize;
							setpage((PTR)(mem + filesize - 1L));
						}
						repaint();
					}
					break;
		case BVICTRL('G'):
					fileinfo(name);
					wrstat = 0;
					break;
		case BVICTRL('L'):   	/*** REDRAW SCREEN ***/
					new_screen();
					break;
		case BVICTRL('Y'):
					if (y < maxy - 1) y++;
					scrollup(1);
					break;
		case 'A':   smsg("APPEND MODE");
					current = (PTR)(mem + filesize - 1L);
					setpage(current++);
					cur_forw(0);
					setcur();
					undosize = filesize;
					undo_count = edit(ch);
					break;
		case 'B':
		case 'b':	setpage(backsearch(current, ch));
					break;
		case 'e':	setpage(end_word(current));
					break;
		case ',':	do_ft(-1, 0);
					break;
		case ';':	do_ft(0, 0);
					break;
		case 'F':	
		case 'f':	
		case 't':	
		case 'T':	do_ft(ch, 0);
					break;
		case 'G':   last_motion = current;
					if (precount > -1)	{
						if ((precount < P(P_OF)) ||
								(precount - P(P_OF)) > (filesize - 1L)) {
							beep();
						} else { 
							setpage((PTR)(mem + precount - P(P_OF)));
						}
					} else { 
						setpage((PTR)(mem + filesize - 1L));
					}
					break;
		case 'g':	last_motion = current;
					clearstr();
					outmsg("Goto Hex Address:");
					refresh();
					getcmdstr(cmdstr, 18);
					clearstr();
					if (cmdstr[0] == '^') {
						inaddr = P(P_OF);
					} else if (cmdstr[0] == '$') {
						inaddr = filesize + P(P_OF) - 1L;
					} else {
						off_t ltmp;
						sscanf(cmdstr, "%llx", (long long unsigned *)&ltmp);
						inaddr = ltmp;
					}
					if (inaddr < P(P_OF)) break;
					inaddr -= P(P_OF);
					if (inaddr < filesize)  {
						setpage(mem + inaddr);
					} else {
						if (filesize == 0L) break;
						sprintf(string, "Max. address of current file : %06llX", (long long unsigned)(filesize - 1L + P(P_OF)));
						emsg(string);
					}
					break;
		case '?':
		case '/':	/**** Search String ****/
		case '#':
		case '\\':  clearstr();
					addch(ch);
					refresh();
					if (getcmdstr(line, 1)) break;
					last_motion = current;
					hl_spat = P(P_HL);
					searching(ch, line, current, maxpos - 1, P(P_WS));
					if (hl_spat) {
						repaint();
					}
					break;
		case ESC:		/* un-highlight */
					hl_spat = FALSE;
					repaint();
					break;
		case 'n': 		/**** Search Next ****/
		case 'N':   last_motion = current;
					hl_spat = P(P_HL);
					searching(ch, "", current, maxpos - 1, P(P_WS));
					if (hl_spat) {
						repaint();
					}
					break;
		case 'm':	do_mark(vgetc(), current);
					break;
		case '\'':
		case '`':   if ((ch == '`' && loc == ASCII) ||
									(ch == '\'' && loc == HEX))
						toggle();
					mark = vgetc();
					if (mark == '`' || mark == '\'') {
						setpage(last_motion);
						last_motion = current;
					} else {
						if (mark < 'a' || mark > 'z') {
							beep(); break;
						} else if (markbuf[mark - 'a'] == NULL) {
								beep(); break;
						}
						setpage(markbuf[mark - 'a']);
					}
					break;
		case 'D':	if (precount < 1) precount = 1;
					sprintf(rep_buf, "%ldD", precount);
					trunc_cur();
					break;
		case 'o':	/* overwrite: this is an overwriting put */
					if (precount < 1) precount = 1;
					sprintf(rep_buf, "%ldo", precount);
					do_over(current, yanked, yank_buf);
					break;
		case 'P':
					if (precount < 1) precount = 1;
					if ((undo_count = alloc_buf(yanked, &undo_buf)) == 0L)
						break;
					sprintf(rep_buf, "%ldP", precount);
					if (do_append((off_t)yanked, yank_buf)) break;
					/* we save it not for undo but for the dot command
					memcpy(undo_buf, yank_buf, yanked);
					*/
					repaint();
					break;
		case 'r':
		case 'R':   if (filesize == 0L) break;
					if (precount < 1) precount = 1;
					sprintf(rep_buf, "%ld%c", precount, ch);
					undo_count = edit(ch);
					lflag++;
					break;
		case 'u':	do_undo();
					break;
		case 'W':
		case 'w':	// loc = ASCII;
					setpage(wordsearch(current, ch));
					break;
		case 'y':	count = range(ch);
					if (count > 0) {
/*
  sprintf(string, "$%ld$", (long)yanked);
  msg(string);
  vgetc();
*/
						if ((yanked = alloc_buf((off_t)count, &yank_buf)) == 0L) {
							break;
						}
						memcpy(yank_buf, current, yanked);
					} else if (count < 0) {
						if ((yanked = alloc_buf(-(off_t)count, &yank_buf)) == 0L) {
							break;
						}
						memcpy(yank_buf, current, yanked);
					} else {
						break;
					}
/*
					sprintf(string, "%ld bytes yanked", labs(count));
					msg(string);
*/
					break;
		case 'z':	do_z(vgetc());
					break;
		case 'Z':   if (vgetc() == 'Z') do_exit();
						else beep();
					break;
		case '.':
					if (!strlen(rep_buf)) {
						beep();
					} else {
						stuffin(rep_buf);
					}
					break;
		default :
			if P(P_MM) {
				if (precount < 1) precount = 1;
				switch (ch) {
				case 'I':
					sprintf(rep_buf, "%ldI", precount);
					current = mem;
					setpage(mem);
					repaint();
					undo_count = edit('i');
					lflag++;
					break;
/* undo does not work correctly !!! */
				case 's':
					sprintf(rep_buf, "%lds", precount);
					if (do_delete((off_t)precount, current)) break;
					precount = 1;
					undo_count = edit('i');
					lflag++;
					break;
				case 'a':
					if (cur_forw(1)) break;
					current++;
				case 'i':
					sprintf(rep_buf, "%ld%c", precount, ch);
					undo_count = edit(ch);
					lflag++;
					break;
				case 'p':
					sprintf(rep_buf, "%ldp", precount);
					do_put(current, yanked, yank_buf);
					break;
				case 'c':
				case 'd':
					count = range(ch);
					if (count > 0)
						do_delete((off_t)count, current);
					else if (count < 0)
						do_back(-(off_t)count, current);
					if (ch == 'c') {
						precount = 1;
						undo_count = edit('i');
						lflag++;
					}
					break;
				case 'x':
					sprintf(rep_buf, "%ldx", precount);
					do_delete((off_t)precount, current);
					break;
				case 'X':
					sprintf(rep_buf, "%ldX", precount);
					do_back((off_t)precount, current);
					break;
				default:
					flushinp();
					beep();
				}
			} else {
				switch (ch) {
				case 'x':
					if (precount < 1) precount = 1;
					if ((off_t)precount + current == maxpos) {
						sprintf(rep_buf, "%ldx", precount);
						do_delete((off_t)precount, current);
					} else {
						movebyte();
						flushinp();
						beep();
					}
					break;
				case 'd':
				case 'i':
				case 'I':
				case 'p':
				case 'X':
					movebyte();
				default:
					flushinp();
					beep();
				}
			}
		}
		if (lflag) lineout();
	} while (1);
}


off_t
calc_size(arg)
	char	*arg;
{
	off_t	val;
	extern int errno;
	char	*poi;

	errno = 0;
	val = strtoll(arg, &poi, 0);
	if (val < 0) {
		fprintf(stderr, "negative begin/size/end not allowed\n");
		usage();
	}
	if (poi == arg || errno != 0) {
		/* cygwin gdb displays errno incorrectly as 0 */
		fprintf(stderr, "invalid begin/size/end (hex nr 0x#, octal 0#)\n");
		usage();
	}
	switch (*poi) {
	case 'k':
	case 'K':	val *= 1024;
			 	break;
	case 'm':
	case 'M':	val *= 1048576;
			 	break;
	case 'g':
	case 'G':   val *= 1024*1024*1024LL;
				break;
	case '\0':	break;
	default:	usage();
	}
	return (off_t)val;
}


void
trunc_cur()
{
	undosize = filesize;
	undo_count = (off_t)(maxpos - current);
	undo_start = current;
	filesize = pagepos - mem + y * Anzahl + xpos();
	maxpos = (PTR)(mem + filesize);
	if (filesize == 0L) {
		emsg(nobytes);
	} else	cur_back();
	edits = U_TRUNC;
	repaint();
}


int
do_append(count, buf)
	off_t	count;
	char	*buf;
{
	if (filesize + count > memsize) {
		if (enlarge(count + 100L)) return 1;
	}
	memcpy(mem + filesize, buf, count);
	undo_start = mem + filesize - 1L;
	setpage(undo_start + count);
	edits = U_APPEND;
	undosize = filesize;
	filesize += count;
	maxpos += count;
	return 0;
}


void
do_tilde(count)
	off_t	count;
{
	if (filesize == 0L) return;
	undo_start = current;
	if (current + count > maxpos) {
		beep();
		return;
	}
	if ((undo_count = alloc_buf(count, &undo_buf)) == 0L)
		return;
	memcpy(undo_buf, current, undo_count);
	while (count--) {
		if (isupper((int)(*current))) *current = tolower((int)(*current));
			else if (islower((int)(*current)))
				*current = toupper((int)(*current));
		current++;
		cur_forw(0);
	}
	edits = U_TILDE;
	setcur();
}


void
do_undo()
{
	off_t	n, tempsize;
	char	temp;
	PTR		set_cursor;
	PTR		s;
	PTR		d;

	if (undo_count == 0L) {
		emsg("Nothing to undo");
		return;
	}
	set_cursor = undo_start;
	switch (edits) {
		case U_EDIT:
		case U_TILDE:
			n = undo_count;
			s = undo_buf;
			d = undo_start;
			while (n--) {
				temp = *d;
				*d = *s;
				*s = temp;
				s++; d++;
			}
			break;
		case U_APPEND:
		case U_TRUNC:
			tempsize = filesize;
			filesize = undosize;
			undosize = tempsize;
			maxpos = (PTR)(mem + filesize);
			if (filesize)
				set_cursor = maxpos - 1L;
			else
				set_cursor = maxpos;
			break;
		case U_INSERT:
			filesize -= undo_count;
			maxpos -= undo_count;
			memcpy(undo_buf, undo_start, undo_count);
			memmove(undo_start, undo_start + undo_count,
				maxpos - undo_start);
			edits = U_DELETE;
			break;
		case U_BACK:
		case U_DELETE:
			filesize += undo_count;
			maxpos += undo_count;
			memmove(undo_start + undo_count, undo_start,
				maxpos - undo_start);
			memcpy(undo_start, undo_buf, undo_count);
			edits = U_INSERT;
			break;
		}
		setpage(set_cursor);
		if (edits == U_TRUNC && undosize > filesize) cur_back();
		repaint();
}


void
do_over(loc, n, buf)
	PTR		loc;
	off_t	n;
	PTR		buf;
{
	if (n < 1L) {
		emsg(nobytes);
		return;
	}
	if (loc + n > maxpos) {
		beep();
		return;
	}
	if ((undo_count = alloc_buf(n, &undo_buf)) == 0L)
		return;
	undo_start = loc;
	memcpy(undo_buf, loc, n);
	memcpy(loc, buf, n);
	edits = U_EDIT;
	setpage(loc + n - 1);
	repaint();
}


void
do_put(loc, n, buf)
	PTR		loc;
	off_t	n;
	PTR		buf;
{
	if (n < 1L) {
		emsg(nobytes);
		return;
	}
	if (loc >= maxpos) {
		beep();
		return;
	}
	if (filesize + n > memsize) {
		if (enlarge(n + 1024)) return;
	}
	if ((undo_count = alloc_buf(n, &undo_buf)) == 0L)
		return;
	undo_start = loc + 1;
	edits = U_INSERT;
	filesize += n;
	maxpos += n;
	memmove(undo_start + n, undo_start, maxpos - loc);
	memcpy(undo_start, buf, n);
	setpage(loc + n);
	repaint();
}


/* argument sig not used, because only SIGINT will be catched */
void
jmpproc(sig)
	int	sig;
{
	if (P(P_EB)) beep();
	repaint();
	clearstr();
    signal(SIGINT, SIG_IGN);
    longjmp(env, 0);
}


off_t
range(ch)
	int		ch;
{
	int		ch1;
	long	count;

	ch1 = vgetc();
	while (ch1 >= '0' && ch1 <= '9') {
		numarr[arrnum++] = ch1;
		ch1 = vgetc();
	}
	numarr[arrnum] = '\0';
	if (arrnum != 0) count = strtol(numarr, (char **)NULL, 10);
		else count = 1;
	arrnum = 0;
	sprintf(rep_buf, "%ld%c%s%c", precount, ch, numarr, ch1);
	switch (ch1) {
	case '/':	/**** Search String ****/
	case '\\':
		strcat(rep_buf, "\n");
		clearstr();
		addch(ch1);
		refresh();
		if (getcmdstr(line, 1)) break;
		end_addr = searching(ch1, line, current, maxpos - 1, FALSE);
		if (!end_addr) {
			beep();
			return 0;
		}
		return(end_addr - current);
	case '?':
	case '#':
		strcat(rep_buf, "\n");
		clearstr();
		addch(ch1);
		refresh();
		if (getcmdstr(line, 1)) break;
		start_addr = searching(ch1, line, current, maxpos - 1, FALSE);
		if (!start_addr) {
			beep();
			return 0;
		}
		return(start_addr - current);
	case 'f':
	case 't':
		precount = count;
		end_addr = do_ft(ch1, 1);
		if (!end_addr) {
			beep();
			return 0;
		}
		return (end_addr + 1 - current);
	case 'F':
	case 'T':
		precount = count;
		start_addr = do_ft(ch1, 1);
		if (!start_addr) {
			beep();
			return 0;
		}
		return (start_addr - current);
	case '$':
		trunc_cur();
		return 0;
	case 'G':
		if (count == -1) {
			trunc_cur();
			return 0;
		} else if ((count < P(P_OF)) || (count
				- (off_t)P(P_OF)) > (filesize - 1L)) {
			beep();
			return 0;
		} else { 
			if (mem + count < current) {
				return(mem + count - current);
			} else {
				return(count - (current - mem));
			}
		}
	case ' ':
		return precount;
	case '`':
	case '\'':
		mark = vgetc();
		if (mark < 'a' || mark > 'z') {
			beep();
			return 0;
		}
		end_addr = markbuf[mark - 'a'];
		if (end_addr == NULL) {
	        beep();
			return 0;
	    }
		if (end_addr < current) {
			return(end_addr - current);
		} else {
			return(end_addr - current + 1);
		}
	}
	beep();
	return 0;
}