/*T bpatch - A binary file patch/dump utility */
/*S Introduction */
/*F bpatch ***********************************************************
* bpatch
* by Garry M Johnson - 09/17/84
* (C) Copyright 1984, 1985
*
* Change History:
*
* 11/29/87 - PORT TO 80286 SYSTEM (MICROPORT SYSV/AT, 2.2)
* 
* -> sorted out nonportable int -ne char *  and int -ne long (ever-
* annoying problems for the 286); the changes should not have broken 
* the code for 32-bit int -eq char *  machines; indeed, some of the 
* changes fix apparently broken code
* -> calls to outstr() now pass only a string address; where values are to 
* be displayed, they are entered into the string with a prior 
* sprintf(); 
* -> calls to errmsg() handled as for outstr(), except there is a second 
* parameter, "sleep_time", to specify the length of a sleep after message 
* display; this is *not* implemented; call of sleep dumps core -- signal 
* handling problem??
* -> after "windowed" is set, forced all exit()'s into passing first 
* through reset() and likewise everywhere follow reset() with immediate 
* exit()
* -> in the segment following "not interactive - keep dumping ...",
* repaired broken code that controlled flow for the -d and -D options;
* allow DEL interrupt of continuous dump of long file, in order to 
* quit or go to next file
* -> in reset(), ioctl'd terminal to be as at entry
* -> in arrow(), added vi-like cursor control
* -> cleaned up code for formats where specification did not agree with
* declared type for variable; same for function parameter lists; checked 
* casts and conversions to/from long; miscellaneous general cleanup 
* of the code
* -> cleaned up the display, but lots left to be done
* 
* 
* REMAINING MINOR BUGS AND INFELICITIES AND WISH LIST --
* 
* -> spawn a properly working interactive shell; probably just need 
* a fork and a full reset() under "!" option
* -> easy repeat of string search to find next occurrence; highlight 
* string positions in the display; put edit cursor at start of string
* -> in debug mode, allow backspace over command entered, to cancel it
* -> errmsg() display not nice, if have several messages output in
* sequence; no pause in errmsg() (sleep not working?)
* -> column under pointer not always erased (local clrtoeol problem ??)
* -> irregular passing of pointers to record and related arrays
* -> most of the external vars should be local to main()
* -> signal handling?
* -> file closing?
* 
* COMMENTS --
* 
* -> thanks to Steven List and Garry Johnson for a very useful program;
* it's a nice way for one who is lazy to avoid a lot of compilation 
* when what is wanted is a small change in data; the code is easy to 
* read -- again compliments to the authors;
* -> these modifications are hacks of the previous authors' code; the 
* modified program has not been hardened, although I tried to check 
* its operation under limit conditions; please be careful in using it
* -> like the authors, I would appreciate receiving a copy of any 
* improvements
* 
* John Rupley, Dept. Biochemistry, Univ. Arizona, Tucson, AZ 85721
* {ihnp4 | hao!noao}!arizona!rupley!local or rupley!local@megaron.arizona.edu
*
******************************************************************************
*
*  03/05/86 - added further terminal independence
*	      added use of ioctl (see main and mstdin)
*	      added -D versus -d command line option
*	      added use of standard getopt
*	      cleaned up code, eliminated function "ikf"
*	      added original versions of ezlib functions, such as
*		icc, setterm, cm, mstdin, erase, length, move
*	      added ^R, ^Q, ^N, and ^P commands
*	      added ^F, !, and ^X (-X) commands
*			  changed name to "bpatch"
*			  added direct address command (g)
*	      added ASCII search capability
*  07/07/86 - converted to use curses
*	      modified direct addressing to use suffixes
*	      updated HELP function
*
*   Steven List @ Benetics Corporation, Mt. View, CA
*   {cdp,engfocus,idi,oliveb,plx,tolerant}!bene!luke!itkin
*********************************************************************/
/*E*/
/*S includes, globals, and defines */
/*Page Eject*/
#include	<curses.h>
#include <fcntl.h>
#include <signal.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>

static int pbrk = 0;
struct stat sb;

void icc ();
void copyrec ();
void schwapp ();

/* added declarations, mostly for cleanliness */
extern long lseek();
extern long atol();
extern char *strncpy();
extern void exit();
extern unsigned sleep();	/* signal problems ??? if sleep called */
extern void perror();

/* set up for calls to outstr and errmsg, etc, */
/* by use of sprintf to fill outbuf */
char outbuf[512];

	/* ------------------------------------------------------------ */
	/* Some defines added by the moderator to get it work on 4.2	 */
	/* ------------------------------------------------------------ */
#ifdef	MOD_HAX
    /* Fifos?  We ain't got no steenkin' fifos. */
/*	#define S_IFIFO		123450	*/
    /* "Spelling differences." */
#define beep()		fprintf (stderr, "\007")
/*	#define cbreak()	crmode()	*/
    /* Our curses doesn't translate keypad keys to single characters.	*/
#define keypad(a, b)		/*	null	*/	/* orig defines */
#define KEY_UP		'\0'	/*	'^'	*/	/* by MOD_HAX	*/
#define KEY_DOWN	'\0'	/*	'v'	*/	/* not portable	*/
#define KEY_LEFT	'\0'	/*	'<'	*/
#define KEY_RIGHT	'\0'	/*	'>'	*/
#define KEY_HOME	'\0'	/*	'@'	*/
#endif	/* MOD_HAX */

/* vi-like cursor control = vi keys, as control characters */
/* this is a hack, and I hope does not interfere with keypad() */
#define VI_UP		('K' - 0x40)
#define VI_DOWN		('J' - 0x40)
#define VI_LEFT		('H' - 0x40)
#define VI_RIGHT	('L' - 0x40)
#define VI_HOME		('G' - 0x40)

#define SLEEP_TIME	(unsigned )0	/* a small pause after errmsg */
					/* does not work -- signals?? */
	/* ------------------------------------------------------------ */
	/* Some convenient defines					 */
	/* ------------------------------------------------------------ */

#define DEL '\177'
#define HEX 1
#define ALPHA 0

	/* ------------------------------------------------------------ */
	/* general purpose identification and control variables	 	*/
	/* ------------------------------------------------------------ */

char	filename[64];		/* current file being examined		*/
char	record[16][16];		/* record (page) buffer			*/
char	unch_rec[16][16];	/* record before any changes		*/
int 	zp;			/* current input character		*/

int		block = 0;	/* block size if -b in command		*/
int		block_spec;	/* true if file is block special	*/
int		bytes = 0;	/* number of bytes from last read	*/
int		char_spec;	/* true if file is char special		*/
int		debug = 0;	/* true if debug is turned on		*/
int		dir_spec;	/* true if file is directory		*/
int		dump = 0;	/* nonzero if dump instead of change	*/
int		Dump = 0;	/* store of value with -D option	*/
int		ebcdic = 0;	/* true if -e option			*/
int		fifo_spec;	/* true if file is fifo			*/
int		honly = 0;	/* true if dump is to be hex only	*/
int		mod = 0;	/* true if record has been modified	*/
int		pause_opt = 0;	/* true if -p option			*/
int		rawfile = 0;	/* true if file is c/b/p		*/
int		reclen = 0;	/* record length, if -r			*/
int		recno = 0;	/* current record (page) number		*/
int		stay = 0;	/* true if no position change 		*/
int		swab_opt = 0;	/* true if byte swapping is on		*/
int		windowed = 0;	/* true if windowing - not dump		*/

long	position = 0;		/* byte address in file			*/

WINDOW *hexwin = NULL;
WINDOW *alphawin = NULL;
WINDOW *errwin = NULL;

#ifdef	MOD_HAX
#else	/* use original code... */
	/* plus some more for restoring terminal function */
	struct termio asis, aswas;
#endif	/* MOD_HAX */

/*S main - control all the work from here */
/*H main *************************************************************
*
*			     main
*
*	set up the globals, initilize the state, and process the file
*
*********************************************************************/
/*E*/
main (argc, argv)
int argc;
char *argv[];
{
	extern WINDOW *subwin ();
	extern WINDOW *newwin ();

	register char	*cp;		/* general purpose char ptr	*/
	extern   char	*gets ();	/* get string from stdin	*/
		  char	m = '\017';	/* mask for hex edit		*/
		  char	response[512];	/* general purpose buffer	*/
		  int 	z;		/* character read in		*/
	 
	int		breakp ();	/* signal trapping function	*/
	int		c;		/* current screen column	*/
	int		change = 0;	/* true if cmd line option toggled */
	int		fid;		/* file descriptor 		*/
	int		firstfile;	/* arg # of first file in cmd line */
	int		h;		/* temp for hex edit		*/
	int		i;		/* general purpose loop index	*/
	int		j;		/* general purpose loop index	*/
	int		r;		/* current screen row		*/
	int		hexc;		/* current cursor column in hexwin */

	long	byteaddr;		/* planned byte address for 'G'	*/
	long	size;			/* file size in bytes		*/
	long	status;			/* EOF if at end of file or error */

	extern int optind;		/* getopt index into argv	*/
	extern char *optarg;		/* getopt pointer to opt arg	*/

	extern long getnum ();
	extern char *instr ();		/* get a string from the cmd line */
	extern int reset ();		/* exit function - reset terminal */

	/* ------------------------------------------------------------ */
	/* set up signal handling					 */
	/* ------------------------------------------------------------ */

	if (!dump) signal (SIGINT, breakp);

	signal (SIGTERM, reset);

	/* ------------------------------------------------------------ */
	/* process command line arguments				 */
	/* ------------------------------------------------------------ */

	while ((i = getopt (argc, argv, "r:dD:b:pxXse")) != EOF)
	{
		switch (i)
		{
			case	'b':	/* blocking			*/
				block = atoi (optarg);
				if (block < 1 || block > 10240)
				{
					fprintf (stderr,
						"invalid block size: %d\n", block);
					exit (1);
				}
				break;
			case	'd':	/* straight dump - no limit	*/
				dump = -1;
				break;
			case	'D':	/* dump - page count spec	*/
				dump = atoi (optarg);
				Dump = dump;
				break;
			case	'e':	/* file is ebcdic		*/
				ebcdic = 1;
				break;
			case	'p':	/* pause between pages - dump	*/
				pause_opt = 1;
				break;
			case	'r':	/* record length for dump	*/
				reclen = atoi (optarg);
				break;
			case	's':	/* byte swapping required	*/
				swab_opt = 1;
				break;
			case	'x':	/* hex dump only		*/
				honly = 1;
				break;
			case	'X':
				debug = 1;
				break;
			default:	/* uhoh				*/
				fprintf (stderr,
"usage: bpatch [ -b blocksz ] [ -d<ump> ] [ -D pagecnt ] [ -e<bcdic> ]\n");
				fprintf (stderr,
"	       [ -p<ause> ] [ -r reclen ] [ -s<wap bytes> ] [ -x<only> ]\n");
				exit (1);
		}
	}

	/* ------------------------------------------------------------ */
	/* check for valid combinations of options			 */
	/* ------------------------------------------------------------ */

	if ((honly || block || reclen || pause_opt ) && !dump)
	{
		fprintf (stderr, "-x|-b|-r|-p requires -d or -D\n");
		exit (2);
	}

	/* ------------------------------------------------------------ */
	/* At least one file name must be specified on the cmd line	 */
	/* ------------------------------------------------------------ */

	if (optind == argc)
	{
		fprintf (stderr, "no file name(s) specified\n");
		exit (2);
	}

	/* ------------------------------------------------------------ */
	/* set up the screen, if this is an interactive session	 	*/
	/* ------------------------------------------------------------ */

	if (!dump)
	{
		windowed = 1;
		initscr ();
		nonl ();
		noecho ();
		cbreak ();
		keypad (stdscr, TRUE);
		hexwin = subwin (stdscr, 16, 48, 4, 4);
		keypad (hexwin, TRUE);
		alphawin = subwin (stdscr, 16, 16, 4, 57);
		keypad (alphawin, TRUE);
		errwin = subwin (stdscr, 1, 80, 23, 0);

#ifdef	MOD_HAX
		/* This is not exactly what the original code does,
		   but it's good enough.  -r$ */
		raw();
#else	/* use original code... */
		ioctl (0, TCGETA, &asis);
		aswas = asis;	/* save termio stuff for later restore */
		asis.c_cc[VINTR] = '\0';
		asis.c_iflag &= ~IXON;
		asis.c_iflag &= ~IXOFF;
		asis.c_iflag &= ~IXANY;
		ioctl (0, TCSETA, &asis);
#endif	/* MOD_HAX */
	}

	/* ------------------------------------------------------------ */
	/* save the first file's index for backing up later		 */
	/* ------------------------------------------------------------ */

	firstfile = optind;

	/* ------------------------------------------------------------ */
	/* open the first file 					 	*/
	/* ------------------------------------------------------------ */

	for (fid = -1; fid < 0 && optind < argc;)
	{
		fid = ckfile (argv[optind], &size);
		if (fid < 0) optind++;
	}
	if (fid < 0)
	{
		fprintf (stderr, "could not handle the file list\n");
		reset (0);
		exit (2);
	}

	strncpy (filename, argv[optind], sizeof filename);

	if (block != 0)
	{
		size = -1;
	}

	recno = 0;
	stay = 0;
	mod = 0;
	status = 0;

	/* ------------------------------------------------------------ */
	/* Until the user exits...					 */
	/* ------------------------------------------------------------ */

	if (!dump) clear ();

	while (status != EOF)
	{
	/* ------------------------------------------------------------ */
	/* change of location - read and display			 */
	/* ------------------------------------------------------------ */
		if (stay == 0)
		{
			position = lseek (fid, ((long )recno) * 256, 0);

			if ((bytes = bread (fid, record, 256, block)) < 0)
			{
				sprintf(outbuf, "error on reading file %s", filename);
				errmsg (outbuf, SLEEP_TIME);
				status = EOF;
				continue;
			}
			if (bytes > 0)
			{
				if (swab_opt) schwapp (record, 256);

				copyrec (record, unch_rec, sizeof record);

				if (!dump) clear ();
				show (bytes, record, filename, size, recno,
						position, m,reclen, dump, ebcdic, swab_opt,
						block, honly);
			}
			mod = 0;
		}
	/* ------------------------------------------------------------ */
	/* not interactive - keep dumping or open next file		 */
	/* ------------------------------------------------------------ */
		/* major changes in control of flow */
		/* first records of multiple-files now dump */
		/* core is not dumped at end of filelist */
		/* can break with DEL into a long dump */
		if (dump)
		{
			if (dump > 0) dump--;
			if ((dump < 0 && bytes == 0) || dump == 0)
			{
				if (Dump) dump = Dump;
				if (optind == argc) status = EOF;
				else
				{
					close (fid);
					fid = -1;
					for (optind++; fid < 0 && optind < argc;)
					{
						fid = ckfile (argv[optind], &size);
						if (fid < 0) optind++;
					}
					if (fid < 0)
					{
						status = EOF;
						continue;
					}

					strncpy (filename, argv[optind], sizeof filename);

					if (block != 0)
					{
						size = -1;
					}
					recno = 0;
					stay = 0;
					status = lseek (fid, (long)0, 0);
				}
			}
			else
			{
				++recno;
			}
	/* ------------------------------------------------------------ */
	/* if pause, beep and wait					 */
	/* ------------------------------------------------------------ */
			/* settings to bypass show() */
			/* and go to read of next file */
			/* if type DEL during file display */
			if (status != EOF && (pause_opt || pbrk))
			{
				if (pbrk)
				{
					bytes = 0;
					dump = -1;
					stay = 1;
				}
				pbrk = 0;
				fprintf (stderr, "\007");
				gets (response);

				if (pbrk) status = EOF;
			}

			continue;
		}
	/* ------------------------------------------------------------ */
	/* if we got here, this is an interactive session		 */
	/* ------------------------------------------------------------ */
		stay = 0;
	/* ------------------------------------------------------------ */
	/* get the user's command					 */
	/* ------------------------------------------------------------ */
		response[0] = EOF;
		mvaddstr (22, 0, "> ");
		clrtoeol ();
		refresh ();
		zp = getch ();

		if (debug && !dump)
		{
			/* cheap cleanup of errwin */
			errmsg ("", SLEEP_TIME);
			if (isascii (zp) && isprint (zp))
			{
				sprintf (outbuf, "command entered is %c", (char )zp);
				errmsg (outbuf, 0);
			}
			else
			{
				sprintf (outbuf, "command entered is ^%c (%#x)", (char )zp + '@', zp);
				errmsg (outbuf, 0);
			}
			getch ();
		}

		/* assume normal ascii character sequence		*/
		if ((zp >= 'a') && (zp <= 'z'))
			zp = zp - 0x20;
	/* ------------------------------------------------------------ */
	/* here we go - what does the user want?			 */
	/* ------------------------------------------------------------ */
		refresh ();
		errmsg ("", SLEEP_TIME);
		switch (zp)
		{ 
			case	'!':	/* shell escape	*/
				echo ();
				move (23,0);
				clrtoeol ();
				addstr ("shell command: ");
				refresh ();
				getstr (response);
				clear ();
				nl ();
				system (response);
				noecho ();
				nonl ();
				move (23,0);
				standout ();
				addstr (" <Press any key> ");
				standend ();
				clrtoeol ();
				refresh ();
				getch ();
				break;

			case	'?':	/* HELP	*/
				clear ();
				dbg_msg ("Help");
				dohelp ();
				break;

			case	'/':	/* search for a string */
				stay = 1;
				if (mod)
				{
					errmsg ("No write since last change", SLEEP_TIME);
				}
				else search (fid);
				break;

			case '-':	/* toggle options */
				zp = getch ();
				stay = 1;
				change = 0;
				switch (zp)
				{
					case 'a': /* ascii */
						if (ebcdic)
						{
							dbg_msg ("toggle to ascii");
							change = 1;
						}
						ebcdic = 0;
						break;

					case 'e': /* ebcdic */
						if (ebcdic == 0)
						{
							dbg_msg ("toggle to ebcdic");
							change = 1;
						}
						ebcdic = 1;
						break;

					case 's': /* swab */
						dbg_msg ("toggle byte swap");
						change = 1;
						schwapp (record, 256);
						swab_opt = !swab_opt;
						break;
				}
				if (change)
				{
					clear ();
					show (bytes, record, filename, size,
							recno, position, m,reclen, dump,
							ebcdic, swab_opt, block, honly);
				}

				break;

			case '\022':	/* redraw screen (^R) */
				clear ();
				show (bytes, record, filename, size, recno,
					  position, m, reclen, dump, ebcdic,
					  swab_opt, block, honly);
				stay = 1;
				break;
			
			case	'\030':	/* toggle debug (^X) */
				debug = !debug;
				stay = 1;
				break;

			case	'\006':	/* new file (^F) */
				close (fid);
				fid = ckfile (cp = instr (), &size);
				if (fid < 0)
				{
					fid = ckfile (filename, &size);
				}
				else
				{
					strncpy (filename, cp, sizeof filename);
					stay = 0;
					recno = 0;
				}
				break;

			case	'\016':	/* next file (^N) */
				if (mod)
				{
					errmsg ("No write since last change", SLEEP_TIME);
					stay = 1;
				}
				else if (optind == (argc - 1))
				{
					errmsg ("No more files", SLEEP_TIME);
					stay = 1;
				}
				else
				{
					close (fid);
					for (fid = -1, optind++; fid < 0 && optind < argc;)
					{
						fid = ckfile (argv[optind], &size);
						if (fid < 0) optind++;
					}
					if (fid < 0)
					{
						errmsg ("could not handle the file list", SLEEP_TIME);
						reset (0);
						exit (0);
					}
					strncpy (filename, argv[optind], sizeof filename);
					stay = 0;
					recno = 0;
				}
				break;

			case	'\020':	/* prev file (^P) */
				if (mod)
				{
					errmsg ("No write since last change", SLEEP_TIME);
					stay = 1;
				}
				else if (optind == firstfile)
				{
					errmsg ("No previous file", SLEEP_TIME);
					stay = 1;
				}
				else
				{
					close (fid);
					for (fid = -1, optind--; fid < 0 && optind >= firstfile;)
					{
						fid = ckfile (argv[optind], &size);
						if (fid < 0) optind--;
					}
					if (fid < 0)
					{
						errmsg ("could not handle the file list", SLEEP_TIME);
						reset (0);
						exit (0);
					}
					strncpy (filename, argv[optind], sizeof filename);
					stay = 0;
					recno = 0;
				}
				break;

			case	'\021':	/* quit absolutely (^Q)	*/
				status = EOF;
				break;

			case DEL:	/* quit with check */
			case 'Q': 	/* quit */
				if (mod)
				{
					errmsg ("No write since last change!", SLEEP_TIME);
					stay = 1;
				}
				else
				{
					status = EOF;
				}
				break;

			case '\\': 	/* back up 1 record */
				if (mod)
				{
					errmsg ("No write since last change", SLEEP_TIME);
					stay = 1;
				}
				else
				{
					if (recno > 0)
					{
						--recno;
						stay = 0;

						status = lseek (fid, ((long )recno) * 256, 0);
						if (status < 0)
						{
							move (22, 0);
							clrtoeol ();
							perror (filename);
							errmsg ("error positioning in file", SLEEP_TIME);
							beep ();
							++recno;
							stay = 1;
						}
					}
					else
					{
						errmsg ("No previous records", SLEEP_TIME);
						beep ();
						stay = 1;
					}
				}
				break; 

			case 'F': 	/* go to first record */
				if (mod)
				{
					errmsg ("No write since last change", SLEEP_TIME);
					stay = 1;
				}
				else
				{
					status = lseek (fid, (long)0, 0);
					recno = 0;
				}
				break;

			case 'L': 	/* go to last record */
				if (mod)
				{
					errmsg ("No write since last change", SLEEP_TIME);
					stay = 1;
				}
				else
				{
					position = lseek (fid, (long)0, 2);
					recno = position / 256;
					j = position % 256;
					if (j == 0) --recno;
					status = lseek (fid, ((long )recno)*256, 0);
				}
				break;

			case 'U':	/* undo changes	*/
				stay = 1;
				mod = 0;
				copyrec (unch_rec, record, sizeof record);
				clear ();
				show (bytes, record, filename, size, recno,
						position, m,reclen, dump, ebcdic, swab_opt,
						block, honly);
				break;

			case 'R': 	/* re-read record */
				status = lseek (fid, ((long )recno)*256, 0);
				break;

			case '0': 	/* go to some address */
			case '1':
			case '2':
			case '3':
			case '4':
			case '5':
			case '6':
			case '7':
			case '8':
			case '9':
				if (mod)
				{
					errmsg ("No write since last change", SLEEP_TIME);
					stay = 1;
				}
				else
				{
					byteaddr = getnum (zp, FALSE);
					stay = 1;
					sprintf (outbuf, "Position to byte %ld", byteaddr);
					errmsg (outbuf, 0);
					if (!rawfile && byteaddr > size)
					{
						errmsg ("Address outside file", SLEEP_TIME);
						beep ();
					}
					else if ((int )(byteaddr / 256) != recno)
					{
						recno = byteaddr / 256;
						status = lseek (fid, ((long )recno)*256, 0);
						stay = 0;
					}
				}
				break;

			case 'A': 	/* alpha modify */
				stay = 1;
				r = c = 0;
				dbg_msg ("edit ascii");
				if (bytes == 0) break;
				touchwin (stdscr);
				refresh ();
				wmove (alphawin, r, c);
				touchwin (alphawin);
				wrefresh (alphawin);

				while ((z = wgetch (alphawin)) != DEL)
				{
					if (!arrow (z, &r, &c))
					{
						if (isascii (z))
						{
							if (isprint (z)) waddch (alphawin, z);
							else		waddch (alphawin, '.');

							if (ebcdic) icc (&z, 1,"AE");

							record[r][c] = z;
							mod = 1;

							hexc = c * 3;
							wmove (hexwin, r, hexc);
							if (record[r][c] < '\0')
							{
								wprintw (hexwin, "%x%x", (record[r][c] >> 4) & m, record[r][c] & m);
							}
							else
							{
								wprintw (hexwin, "%02x", record[r][c]);
							}
							wrefresh (hexwin);

						}
						else
						{
							beep ();
						}

						if (c == 15)
						{
							if (r == 15) beep ();
							else
							{
								c = 0;
								++r;
							}
						}
						else
						{
							c++;
						}
					}
					if (r * 16 + c >= bytes)
					{
						beep ();
						r = (bytes - 1) / 16;
						c = (bytes - 1) % 16;
					}

					wmove (alphawin, r, c);
					wrefresh (alphawin);
				}

				break;

			case 'H': 	/* hex modify */
				dbg_msg ("edit hex");
				stay = 1;
				r = c = hexc = 0;
				if (bytes == 0) break;
				touchwin (stdscr);
				refresh ();
				wmove (hexwin, r, hexc);
				touchwin (hexwin);
				wrefresh (hexwin);

				while ((z = wgetch (hexwin)) != DEL)
				{
					if (!arrow (z, &r, &c))
					{
						hexc = c * 3;
						if ((z >= 'a') && (z <= 'z'))
							z = z - 0x20;
						if (!(((z >= '0') && (z <= '9')) || ((z >= 'A') && (z <= 'F'))))
						{
							beep ();
						}
						else
						{
							if (z >= 'A')
								waddch (hexwin, tolower (z));
							else
								waddch (hexwin, z);
							wrefresh (hexwin);

							if (z > '9') z -= 7;

							h = (z & m) << 4;

							while (2)
							{
								z = EOF;
								z = getch ();
								if (z == EOF)
								{
									pbrk = 0;
									h = -1;
									break;
								}
								if ((z >= 'a') && (z <= 'z'))
									z = z - 0x20;
								if (!(((z >= '0') && (z <= '9')) || ((z >= 'A') && (z <= 'F'))))
								{
									beep ();
								}
								else
								{
									if (z >= 'A')
 										waddch (hexwin, tolower (z));
 									else
 										waddch (hexwin, z);
									wrefresh (hexwin);
									if (z > '9') z -= 7;

									h |= z & m;
									break;
								}
							}

							if (h < 0)
							{
								wmove (hexwin, r, hexc);
								if (record[r][c] < '\0')
								{
									wprintw (hexwin, "%x%x",
										(record[r][c] >> 4) & m,
										record[r][c] & m);
								}
								else
								{
									wprintw (hexwin, "%02x", record[r][c]);
								}
								wrefresh (hexwin);
								break;
							}

							record[r][c] = z = h;
							mod = 1;

							if (ebcdic) icc (&z, 1,"EA");

							wmove (alphawin, r, c);
							if (isascii (z) && isprint (z))
								waddch (alphawin, z);
							else waddch (alphawin, '.');
							wrefresh (alphawin);

							if (c == 15)
							{
								if (r == 15) beep ();
								else
								{
									c = 0;
									++r;
								}
							}
							else
							{
								c++;
							}
						}
					}

					if (r * 16 + c >= bytes)
					{
						beep ();
						r = (bytes - 1) / 16;
						c = (bytes - 1) % 16;
					}

					hexc = c * 3;
					wmove (hexwin, r, hexc);
					wrefresh (hexwin);
				}
				break;

			case 'W': 	/* write record */
				stay = 1;
				status = lseek (fid, position, 0);
				if (status != position)
				{
					move (22, 0);
					clrtoeol ();
					perror (filename);
					errmsg ("error positioning in file", SLEEP_TIME);
					beep ();
				}
				if (swab_opt) schwapp (record, 256);
				if (write (fid, record, bytes) != bytes)
				{
					errmsg ("error writing to file", SLEEP_TIME);
					reset (0);
					exit (0);
				}
				if (swab_opt) schwapp (record, 256);
				mod = 0;
				errmsg ("Record written", SLEEP_TIME);
				break;

			case	'\n':	/* newline - next page	*/
			case	'\r':
				if (mod)
				{
					errmsg ("No write since last change", SLEEP_TIME);
					stay = 1;
				}
				else
				{
					++recno;
					if (!rawfile && (((long )recno) * 256) >= size)
					{
						recno--;
						beep ();
						errmsg ("No more records in file", SLEEP_TIME);
						stay = 1;
					}
					else stay = 0;
				}
				break;

			default:
				if (isascii (zp) && isprint (zp))
				{
					sprintf (outbuf, "Unknown command: %d", zp);
					errmsg (outbuf, 0);
				}
				else
				{
					sprintf (outbuf, "Unknown command: %d", zp + '@');
					errmsg (outbuf, 0);
				}
				beep ();
				stay = 1;
				break;
		} 	/* end switch zp */
		refresh ();
	}

	reset (0);
	if (fid > 0) return (close (fid));
	else return (0);
}
/*S show - display a record on the terminal */
/*H show */
/*E*/
/*checked typing of parameters as declared in fucntion */
/* versus declarations at call */
show (bytes, record, filename, size, recno, position,
	  m,reclen, dump, ebcdic, swab_opt, block, honly)
int bytes;
char record[16][16];
char *filename;
long size;
int recno;
long position;
char m;
int reclen;
int dump;
int ebcdic;
int swab_opt;
int block;
int honly;
{
	int		i;
	int		j;
	char	s;
	char	temp[16];
	char	*look = NULL;

	int		row = 0;
	int		col = 0;

	if (dump)
		printf ("\n\n");
	if (!dump)
		move (0, 0);
	sprintf (outbuf, "FILE: %s ", filename);
	outstr (outbuf);
	if (block_spec) outstr ("(block special)");
	else if (char_spec) outstr ("(character special)");
	else if (fifo_spec) outstr ("(fifo (named pipe))");
	else if (dir_spec)
	{
		sprintf (outbuf, "(directory - %ld)", size);
		outstr (outbuf);
	}
	else 
	{
		sprintf (outbuf, "(%ld)", size);
		outstr (outbuf);
	}
	if (ebcdic) outstr (" - EBCDIC");
	else outstr (" - ASCII");
	if (swab_opt) outstr (" - SWAP");
	if (block) 
	{
		sprintf (outbuf, " - BLOCK (%d)", block);
		outstr (outbuf);
	}
	if (reclen) 
	{
		sprintf (outbuf, " - RECORD (%d)", reclen);
		outstr (outbuf);
	}
	if (!dump)
	{
		clrtoeol ();
		move (1,0);
		printw ("PAGE: %d (%ld)", recno, position);
		clrtoeol ();
		row = 2;
	}
	else
	{
		printf ("\nPAGE: %d (%ld)\n", recno, position);
	}

	if (honly)
	{
		look = (char *) record;
		for (j=0;j<256;++j)
		{
			if (*look++ != '\0')
			{
				look = NULL;
				break;
			}
		}
	}

	if (!dump) move (row, col);

	outstr ("    x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xa xb xc xd xe xf");
	outstr ("	0123456789abcdef");

	if (!dump)
	{
		row += 2;
		move (row, col);
	}
	else
	{
		printf ("\n\n");
	}
	for (i=0; i<=bytes/16; ++i)
	{
		if (honly && look != NULL)
		{
			i = 16;
			continue;
		}
 		if (i*16+1 > bytes)
 		{
			if (!dump) move (++row, 0);
 			continue;
 		}

		sprintf (outbuf, "%02x: ", i);
		outstr (outbuf);

		for (j=0; j<16; ++j)
		{
			if (i*16+j < bytes)
			{
				if (record[i][j] < '\0')
				{
					sprintf (outbuf, "%x%x ",
						(record[i][j] >> 4) & m, record[i][j] & m);
					outstr (outbuf);
				}
				else
				{
					sprintf (outbuf, "%02x ", record[i][j]);
					outstr (outbuf);
				}
				s = ' ';
				if (reclen > 0 && (position+i*16+j+1)%reclen == 0)
					s = ':';
				if (block > 0 && (position+i*16+j+1)%block == 0)
				{
					if (s == ' ')
						s = '/';
					else
						s = '%';
				}
				if (s != ' ')
				{
					sprintf (outbuf, "\b%c", s);
					outstr (outbuf);
				}
			}
			else
			{
				outstr ("   "); /* set at 3 spaces */
			}
		}

		outstr ("	 ");	/* set at tab + 1 space */

		copyrec (record[i], temp, 16);

		if (ebcdic) icc (temp, 16, "EA");

		for (j = 0; j < 16 && i*16+j < bytes; ++j)
		{
			if (temp[j] < ' ') outch ('.');
			else
			{
				sprintf (outbuf, "%c", temp[j]);
				outstr (outbuf);
			}
		}

		if (!dump)
		{
			move (++row, col);
		}
		else
		{
			printf ("\n");
		}
	}

	/* moved debug output so it would display + other related changes */
	if (!dump) clrtobot ();
	if (debug && !dump)
	{
		getyx (stdscr, row, col);
		move (23,0);
		printw ("show: %d|%ld|%s|%ld|%d|%ld|%#x|%d|%d|%d|%d|%d|%d",
			bytes, record, filename, size, recno, position,
			m, reclen, dump, ebcdic, swab_opt, block, honly);
		move (row, col);
	}
	if (!dump) 
	{
		touchwin (stdscr);
		refresh ();
	}
	
	return;
}
/*S breakp - set pbrk on interrupt */
/*H breakp */
/*E*/
int breakp (i)
int i;
{
	int s;
	extern int pbrk;
	s = (int) signal (SIGINT, breakp);
	pbrk = i;
}
/*S bread - buffered read */
/*H bread */
/*E*/
int bread (fid, record, want, block)
int fid, want, block;
char *record;
{
	int i, j, k;
	int what, bytes, orig;
	static char buffer[10240];
	static int left, xarrow;
	static int flag = 1;

	if (flag)
	{
		left = 0;
		xarrow = 0;
		flag = 0;
	}

	if (block == 0)
		return (read (fid, record, want));

	if (block & 1) ++block;

	orig = what = want;
	while (1)
	{
		if (left < want)
		{
			if (left)
			{
				copyrec (&buffer[xarrow], record, left);
				record += left;
				want -= left;
			}

			xarrow = 0;
			left = 0;

			if ((bytes = read (fid, buffer, block)) < 0)
			{
				what = bytes;
				break;
			}

			if (bytes == 0)
			{
				what = orig - want;
				break;
			}

			left = bytes;
		}
		else
		{
			copyrec (&buffer[xarrow], record, want);
			xarrow += want;
			left -= want;
			break;
		}
	}

	return (what);
}
/*S schwapp - swap bytes in place */
/*H schwapp */
/*E*/
void
schwapp (ptr, nch)
register char *ptr;
register int nch;
{
	register int i;
	register char c;
	register char *ptra = ptr + 1;

	if (nch & 1) --nch;

	for (i = 0; i < nch; i += 2, ptr += 2, ptra += 2)
	{
		c = *ptr;
		*ptr = *ptra;
		*ptra = c;
	}
	return;
}
/*S copyrec - transfer bytes from f to t for nbytes bytes */
/*H copyrec */
/*E*/
void
copyrec (f, t, nbytes)
register char *f;
register char *t;
register int nbytes;
{
	register int i;

	for (i = 0; i < nbytes; i++, f++, t++) *t = *f;

	return;
}
/*S ebcdic codes corresponding to ascii - translation table */
/*Page Eject*/
char xebcdic_codes[] = {
0, 0x1, 0x2, 0x3, 0x37, 0x2d, 0x2e, 0x2f, 0x16,
0x5, 0x25, 0xb, 0xc, 0xd, 0xe, 0xf, 0x10, 0x11,
0x12, 0x13, 0x3c, 0x3d, 0x32, 0x26, 0x18, 0x19, 0x3f,
0x27, 0x1c, 0x1d, 0x1e, 0x1f, 0x40, 0x5a, 0x7f, 0x7b,
0x5b, 0x6c, 0x50, 0x7d, 0x4d, 0x5d, 0x5c, 0x4e, 0x6b,
0x60, 0x4b, 0x61, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5,
0xf6, 0xf7, 0xf8, 0xf9, 0x7a, 0x5e, 0x4c, 0x7e, 0x6e,
0x6f, 0x7c, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
0xc8, 0xc9, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
0xd8, 0xd9, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8,
0xe9, 0xad, 0xe0, 0xbd, 0x9a, 0x6d, 0x79, 0x81, 0x82,
0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x91, 0x92,
0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0xa2, 0xa3,
0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xc0, 0x4f, 0xd0,
0x5f, 0x7 };
/*S icc - internal code conversion */
/*H icc */
/*E*/
void
icc (buf, nch, type)
register char *buf;
register int nch;
char *type;
{
	register int i;
	register int j;

	if (!strcmp (type, "AE"))
	{
		for (i = 0; i < nch; i++, buf++)
		{
			*buf = xebcdic_codes[*buf];
		}
	}
	else if (!strcmp (type, "EA"))
	{
		for (i = 0; i < nch; i++, buf++)
		{
			for (j = 0; j < 128; j++)
			{
				if (*buf == xebcdic_codes[j])
				{
					*buf = j;
					break;
				}
			}
		}
	}

	return;
}
/*S ckfile - check on existence, accessibility, and type of file */
/*H ckfile */
/*E*/
ckfile (filename, sizep)
char	*filename;
long	*sizep;
{
	register int fid = 0;

	if (access (filename, 0) < 0)
	{
		sprintf (outbuf, "file not found (%s)", filename);
		errmsg (outbuf, SLEEP_TIME);
		fid = -1;
	}

	if (block || pause_opt || dump)
	{
		fid = open (filename, O_RDONLY, 0);
	}
	else
	{
		fid = open (filename, O_RDWR, 0);
	}

	if (fid < 0)
	{
		sprintf (outbuf, "error opening file %s", filename);
		errmsg (outbuf, SLEEP_TIME);
		perror (filename);
		fid = -1;
	}
	else
	{
		sprintf (outbuf, "File %s opened successfully ", filename);
		errmsg (outbuf, 0);
		if (fstat (fid, &sb) == -1)
		{
			fprintf (stderr, "Can't stat\n");
			perror (filename);
			fid = -1;
		}
		else
		{
			block_spec = (sb.st_mode & S_IFMT) == S_IFBLK;
			char_spec = (sb.st_mode & S_IFMT) == S_IFCHR;
			fifo_spec = (sb.st_mode & S_IFMT) == S_IFIFO;
			dir_spec = (sb.st_mode & S_IFMT) == S_IFDIR;
			rawfile = block_spec || char_spec || fifo_spec;

			if (rawfile) *sizep = -1;
			else
			{
				if (sb.st_size == 0)
				{
					fprintf (stderr,
						"file %s is empty (zero bytes)\n",
						filename);
					fid = -1;
				}
				*sizep = sb.st_size;
			}
		}
	}

	return fid;
}
/*S dohelp - display help text */
/*H dohelp */
/*E*/
dohelp ()
{
	static char *helptxt[] = {
	"?    - display this help text",	"h   - edit hexadecimal portion",
	"!    - execute command in the shell",	"a   - edit ascii portion",
	"q,DEL - quit the program",		"DEL - exit edit->command mode",
	"^q   - quit without writing changes",	NULL,
	"-x   - toggle command line option",	"f   - display first page of file",
	"       ({a|e}->{e|a}, s->!s)",		"l   - display last page of file",
	"<cr> - display next page",		NULL,
	"\\    - display previous page",	"nnn - direct addressing",
	"/    - search for ASCII string", 	NULL,
	"^f   - select named file",		"r   - reread the current page",
	"^n   - select next file",		"u   - undo all changes to page",
	"^p   - select previous file",		"w   - write out changed page",
	"^r   - redraw the screen",		NULL,
	"^x   - turn on debug",			NULL,
	"----------------------------------------------------------------", NULL,
	"direct addressing: nnnS, where nnn = some number, and", NULL,
	"			S = type suffix", "b = block (512)",
	NULL, "k = kilobyte (1024)",
	NULL, "l = long word (4)",
	NULL, "p = page (256)",
	NULL, "w = word (2)",
	NULL, "<cr> = byte",
	};

	static int nmsg = sizeof helptxt / sizeof (char *);
	register int row = 0;
	register int i;

	register WINDOW *helpwin;
	extern WINDOW *newwin ();

	helpwin = newwin (LINES, COLS, 0, 0);
	wclear (helpwin);

	wrefresh (helpwin);

	wmove (helpwin, 0, 1);
	waddstr (helpwin,
"---------------------------------- HELP ----------------------------------");

	for (row = 1, i = 0; i < nmsg; i+=2)
	{
		if (helptxt[i])
		{
			wmove (helpwin, row, 1);
			waddstr (helpwin, helptxt[i]);
		}
		if (i+1 <= nmsg && helptxt[i+1])
		{
			wmove (helpwin, row, 41);
			waddstr (helpwin, helptxt[i+1]);
		}
		row++;
	}

	wmove (helpwin, 23, 0);
	wstandout (helpwin);
	waddstr (helpwin, " <Press any key> ");
	wstandend (helpwin);
	wclrtoeol (helpwin);
	wrefresh (helpwin);
	wgetch (helpwin);
	/*
	wclear (helpwin);
	wmove (helpwin, 0, 0);
	wrefresh (helpwin);
	*/
	delwin (helpwin);

	return;
}
/*S reset - reset terminal to original state */
/*H reset */
/*E*/
reset (sig)
int sig;
{
	if (windowed)
	{
		move (23, 0);
		refresh ();
#ifndef MOD_HAX
		ioctl (0, TCSETA, &aswas);
#endif
		endwin ();
	}
	fprintf (stderr, "\n");
	if (sig)
	{
		fprintf (stderr, "killed with signal %d\n", sig);
		exit (sig);
	}
	return (0);
}
/*S arrow - determine if current character is a cursor control key */
/*H arrow */
/*E*/
arrow (k, r, c)
register int k;
register int *r;
register int *c;
{
	register ret = 1;

	/* watch out for conflict of VI_* amd KEY_* definitions */
	if (k == KEY_UP || k == VI_UP)
	{
		if (*r == 0) beep ();
		else (*r)--;
	}
	else if (k == KEY_DOWN || k == VI_DOWN)
	{
		if (*r == 15) beep ();
		else (*r)++;
	}
	else if (k == KEY_LEFT || k == VI_LEFT)
	{
		if (*c == 0)
		{
			if (*r == 0) beep ();
			else
			{
				*c = 15;
				(*r)--;
			}
		}
		else (*c)--;
	}
	else if (k == KEY_RIGHT || k == VI_RIGHT)
	{
		if (*c == 15)
		{
			if (*r == 15) beep ();
			else
			{
				*c = 0;
				(*r)++;
			}
		}
		else (*c)++;
	}
	else if (k == KEY_HOME || k == VI_HOME)
	{
		*r = *c = 0;
	}
	else
	{
		ret = 0;
	}

	return ret;
}
/*S dbg_msg - print a debug message */
/*H dbg_msg */
/*E*/
dbg_msg (msg)
register char *msg;
{
	if (debug && !dump)
	{
		errmsg (msg, SLEEP_TIME);
	}

	return;
}
/*S instr - get a character string from the terminal */
/*H instr */
/*E*/
char *
instr ()
{
	static char buf[512];

	register int c;
	register char *p = buf;
	register int col = 0;

	move (22, 0);
	clrtoeol ();
	refresh ();

	while ((c = getch ()) != '\r')
	{
		if (isascii (c) && isprint (c))
		{
			move (22, col);
			addch (c);
			*p++ = c;
			col++;
		}
		else if (c == '\b')
		{
			p--;
			col--;
			move (22, col);
			addch (' ');
			move (22, col);
		}
		refresh ();
	}

	refresh ();

	*p = '\0';

	return buf;
}
/*S getnum - retrieve a number from the terminal */
/*H getnum */
/*E*/
long
getnum (frst_char, hex)
register int frst_char;
register int hex;
{
	static char buf[64];

	register int c;
	register char *p = buf;
	register int col = 0;

	register long retval = 0;

	move (22, 0);
	clrtoeol ();
	if (frst_char)
	{
		addch (frst_char);
		*p++ = frst_char;
		col++;
		refresh ();
	}

	while ((c = getch()) != '\r')
	{
		if (isascii (c))
		{
			if ((hex && isxdigit (c)) || isdigit (c))
			{
				move (22, col);
				addch (c);
				*p++ = c;
				col++;
			}
			else if (c == '\b')
			{
				p--;
				col--;
				move (22, col);
				addch (' ');
				move (22, col);
			}
			else
			{
				break;	/* some character typing the value */
			}
			refresh ();
		}
	}

	*p = '\0';

	retval = atol (buf);

	mvprintw (22, 0, "%ld", retval);
	switch (c)
	{
		case	'b':	/* block - 512 bytes			*/
			retval *= 512;
			break;
		case	'k':	/* 1024 bytes				*/
			retval *= 1024;
			break;
		case	'l':	/* long word - 4 bytes			*/
			retval *= 4;
			break;
		case	'p':	/* page - 256 bytes			*/
			retval *= 256;
			break;
		case	'w':	/* word - 2 bytes			*/
			retval *= 2;
			break;
		case	'\r':	/* just clear it for display		*/
			c = '\0';
			break;
	}

	printw ("%c -> %ld byte offset", c, retval);
	clrtoeol ();

	refresh ();

	return retval;
}
/*S search - look for an ascii string in the file */
/*H search */
/*E*/
search (fid)
register int fid;
{
	long	curpos = position;
	long	currec = recno;

	char	lrecord[sizeof record + 1];

	register int i;
	register int matched = 0;
	register int srch_len;

	register char *cp = instr ();
	register char *rp;

	int row, col;

	srch_len = strlen (cp);
	copyrec (record, lrecord, sizeof record);
	lrecord[256] = '\0';

	pbrk = 0;

	wmove (errwin, 0, 0);
	wstandout (errwin);
	wmove (errwin, 0, 40);
	wstandend (errwin);
	wmove (errwin, 0, 1);
	waddstr (errwin, "..searching record ");
	getyx (errwin, row, col);

	do
	{
		mvwprintw (errwin, row, col, "%ld", currec);
		touchwin (errwin);
		wrefresh (errwin);

		for (i = 0, rp = lrecord, matched = 0;
			 !matched && i < 256;
			 rp++, i++)
		{
			if (*cp == *rp && !strncmp (cp, rp, srch_len))
			{
				matched = 1;
				break;
			}
		}

		if (!matched)
		{
			bytes = bread (fid, lrecord, 256, block);
			currec++;
			lrecord[256] = '\0';
		}
	}	while (!pbrk && bytes && !matched);

	if (matched)
	{
		recno = currec;
		stay = 0;
		copyrec (record, unch_rec, sizeof record);
		werase (errwin);
		touchwin (errwin);
		wrefresh (errwin);
	}
	else
	{
		sprintf (outbuf, "String %s not found", cp);
		errmsg (outbuf, SLEEP_TIME);
		stay = 1;
	}

	return;
}
/* simplified call to errmsg(), by using sprintf to load format */
/* sleep_time is not implemented and awaits cleaning up of the signals (?) */
errmsg (fmt, sleep_time)
unsigned sleep_time;
char *fmt;
{
	int y, x;

	if (windowed)
	{
		getyx (stdscr, y, x);
		wmove (errwin, 0, 0);
		wstandout (errwin);
		wprintw (errwin, fmt);
		wstandend (errwin);
		wclrtoeol (errwin);
		touchwin (errwin);
		wrefresh (errwin);
		move (y, x);
	}
	else
	{
		fprintf (stderr, fmt);
		fprintf (stderr, "\n");
	}
	/* signal problem ?? if sleep called
	if (sleep_time)
		sleep (sleep_time);
	*/
	return;
}
/* simplified call to outstr(), by using sprintf to load format */
outstr (fmt)
char *fmt;
{
	if (dump) printf (fmt);
	else printw (fmt);

	return;
}
outch (ch)
register char ch;
{
	if (dump) putchar (ch);
	else addch (ch);

	return;
}
