Metropoli BBS
VIEWER: hexed.c MODE: TEXT (ASCII)
/*
 * Hexidecimal screen editor:
 * NOTE: Must be compiled in SMALL model!
 *
 *	Special Keys:
 *		PgDn		= Page forward
 *		PgUp		= Page backward
 *		CTRL-PgUp	= Goto start of file
 *		CTRL-PgDn	= Goto end of file
 *		Home		= Goto start of line
 *		End			= Goto end of line
 *		Ins			= Insert a byte at cursor location
 *		Del			= Delete a byte at cursor location
 *		F1			= Toggle between HEX/ASCII editing
 *		CTRL-Home	= Redraw screen
 *	F10/Keypad '+'	= Enter command
 *	 F9/Keypad '-'	= Re-execute last command
 *	Commands:
 *		G<xxxx>		= Goto address xxxx
 *		Q			= Quit editor
 *		QQ			= Quit even if unsaved changes
 *		W[file]		= Write file
 *		X[file]		= Exit and write file
 *		?xx ...		= Search for HEX byte pattern
 *		/text...	= Search for ASCII text
 *
 * Copyright 1983-1995 Dave Dunfield
 * All rights reserved
 *
 * Permission granted for personal (non-commercial) use only.
 *
 * Compile command: cc hexed -fops
 */
#include <stdio.h>
#include <video.h>
#include <file.h>

#define ALLOCATION	60001

	char cmdbuf[50], *cmdptr, searchbuf[50];
	/* Declare data buffer in two chunks, to prevent a negative size,
       which confuses MASM */
	unsigned char data[ALLOCATION/2], data1[ALLOCATION/2];

/*
 * Skip to the next non-blank character
 */
char skip()
{
	while(*cmdptr == ' ')
		++cmdptr;
	return *cmdptr;
}

main(argc, argv)
	unsigned argc;
	char *argv[];

{
	int cursor;
	unsigned i, j, k, pos, size;
	unsigned char c, chr, mode, modified;
	HANDLE fh;

	cursor = pos = mode = modified = 0;
	if(argc<2)
		abort("\nUse: hexed <file>\n\nCopyright 1983-1995 Dave Dunfield\nAll rights reserved.\n");

	
	if(!(fh=open(argv[1], F_READ)))
		abort("HEXED: Cannot open input file\n");

	if((size = read(data, ALLOCATION, fh)) >= ALLOCATION)
		abort("HEXED: File to large\n");

	close(fh);

	vopen();

draw:		/* draw entire screen */
	vgotoxy(0,0);
	for(i=0; i<30; ++i) vputc('-');
	V_ATTR = REVERSE;
	putstr(" Hex-Ed Version 1.3 ");
	V_ATTR = NORMAL;
	for(i=0; i<30; ++i) vputc('-');
	vgotoxy(0,2);
	vprintf("File: %s",argv[1]);
	vgotoxy(59,2);
	vprintf("Size: %u, ($%04x)",size, size);

redraw:		/* re-draw text area of screen */
	for(i=0; i < 16; ++i) {		/* for each line */
		vgotoxy(0, i+4);
		if(size <= (k=pos+(i*16)))
			break;
		vprintf("%04x  ",k=pos+(i*16));
		for(j = k; j < (k+16) ; ++j) {
			if(j<size)
				vprintf(" %02x", data[j]);
			else {
				vcleol();
				break; } }
		vgotoxy(63,i+4);
		for(j = k; (j<(k+16)) && (j<size); ++j)
			putprint(data[j]); }
	vcleos();

prompt:
	if(mode)
		vgotoxy(63 + cursor%16, 4 + cursor/16);
	else
		vgotoxy(7+3*(cursor%16), 4+cursor/16);
	switch(chr = vgetc()) {
		case _KUA :		/* backup a line (16 bytes) */
			if((cursor = cursor - 16) < 0) {
				cursor += 16;
		case _KPU :		/* backup a page (256 bytes) */
			if(pos > 255) {
				pos -= 256;
				goto redraw; }
			else if(pos) {
				pos = 0;
				goto redraw; } }
			break;
		case _KDA :		/* advance a line (16 bytes) */
			if((cursor + pos + 16) >= size)
				break;
			if(cursor <= (255-16)) {
				cursor += 16;
				break; }
		case _KPD :		/* advance a page (256 bytes) */
			if((pos+256) < size)
				pos += 256;
			if((pos + cursor) > size)
				cursor = size - pos - 1 ;
			goto redraw;
		case _KLA :		/* backup one byte */
			if(cursor)
				--cursor;
			else if(pos) {
				--pos;
				goto redraw; }
			break;
		case _KRA :		/* advance one byte */
			if(size <= (pos + ++cursor)) --cursor;
			else if(255 < cursor) {
				--cursor;
				if(pos < (size - 255)) ++pos;
				goto redraw; }
			break;
		case _CPD :		/* goto end of file */
			if(size > 255) {
				cursor = 255;
				pos = size - 256; }
			else {
				cursor = size-1;
				pos = 0; }
			goto redraw;
		case _CPU :		/* goto start of file */
			cursor = pos = 0;
			goto redraw;
		case _KEN :		/* advance to end of field */
			cursor = (cursor & 0xff0) + 15;
			if(size <= cursor+pos)
				cursor = size - pos - 1;
			break;
		case _KHO :		/* backup to beginning of field */
			cursor = cursor & 0xff0;
			break;
		case _K1 :		/* swap editing field */
			mode = ~mode;
			break;
		case _CHO :		/* re-draw the screen */
			vclscr();
			goto draw;
		case _KDL:		/* Delete a byte */
			i = pos + cursor;
			while(i < size)
				data[i] = data[++i];
			if((pos + cursor) >= --size) {
				if(cursor)
					--cursor;
				else if(pos)
					--pos; }
			modified = -1;
			goto redraw;
		case _KIN:		/* Insert a byte */
			i = pos + cursor;
			if(size < ALLOCATION) {
				chr = data[i];
				while(i < size) {
					c = data[++i];
					data[i] = chr;
					chr = c; }
				++size; }
			modified = -1;
			goto redraw;
		case _K10 :		/* execute line mode command */
		case _KKP :
			vgotoxy(0,22);
			vcleos();
			vputs("Command: ");
			i = 0;
			do {
				if((chr = vgetc()) == _KBS) {
					if (i) {
						putstr("\010 \010");
						--i; } }
				else if(chr < 128)
					vputc(cmdbuf[i++] = chr); }
			while((chr != _KKP) && (chr != _K10));
			cmdbuf[i] = 0;
		case _K9 :		/* repeat last command */
		case _KKM :
			vgotoxy(0, 22);
			V_ATTR = REVERSE;
			putstr(cmdbuf);
			V_ATTR = NORMAL;
			vcleos();
			cmdptr = cmdbuf;
			switch(chr = toupper(skip())) {
				case 'G' :		/* goto address */
					++cmdptr;
					skip();
					i = 0;
					if((i = gethex()) == -1)
						break;
					cursor = 0;
					pos = (i<size) ? i : size;
					goto redraw;
				case '/' :		/* Search for ASCII */
					i = 0;
					while(*++cmdptr)
						searchbuf[i++] = *cmdptr;
					if(!i)
						break;;
					goto do_search;
				case '?' :		/* search for HEX */
					++cmdptr;
					skip();
					i = 0;
					do
						searchbuf[i++] = j = gethex();
					while((j != -1) && skip());
					if(j == -1)
						break;
		do_search:	j = pos + cursor + 1;
					while(!compare(searchbuf, data+j, i)) {
						if(++j >= size) {
							error("Not found");
							break; } }
					if(j < size) {
						if((cursor = j - pos) & 0xff00) {
							pos = j;
							cursor = 0;
							goto redraw; } }
					break;
				case 'X' :		/* Exit and write file */
				case 'W' :		/* write file */
					++cmdptr;
					if(!skip())
						cmdptr = argv[1];
					if(fh=open(cmdptr, F_WRITE)) {
						write(data, size, fh);
						modified = 0;
						close(fh); }
					else {
						error("Can't open output file");
						break; }
					if(chr != 'X')
						break;
				case 'Q' :		/* quit (Exit) */
					if(modified && (toupper(*(cmdptr+1)) != 'Q'))
						error("Unsaved changes, 'qq' to quit anyway");
					else {
						vclscr();
						exit(0); }
					break;
				case 0 :		/* null command, do nothing */
					break;
				default:
					error("Unknown command"); }
			vgotoxy(0,22);
			vcleol();
			break;
		default:
			k = data[pos + cursor];
			if(mode) {
				data[pos+cursor] = k = chr;
				chr = modified = -1; }
			else {
				if(ishex(chr = toupper(chr))) {
					vputc(chr);
					if(chr > '9') chr -= 7;
					i = (chr-'0')*16;
					vputc('-');
					vputc('\b');
					if(ishex(chr=toupper(vgetc()))) {
						if(chr > '9') chr -= 7;
						data[pos+cursor] = k =(chr-'0') + i;
						chr = modified = -1; } } }
				vgotoxy(7+3*(cursor%16), 4+cursor/16);
				vprintf("%02x ",k);
				vgotoxy(63 + cursor%16, 4 + cursor/16);
				putprint(k);
				if(chr == -1) {
					if(size <= (pos + ++cursor)) --cursor;
					else if(cursor > 255) {
						if(pos < (size-256)) {
							pos += 256;
							cursor = 0; }
						else 
							pos = size-(pos + cursor);
					goto redraw; } } }
	goto prompt;
}

/*
 * Test for a valid hexidecimal digit
 */
ishex(chr)
	char chr;
{
	return ((chr >= '0') && (chr <= '9')) || ((chr >= 'A') && (chr <= 'F'));
}

/*
 * Get a hex value from the input line
 */
gethex()
{
	register unsigned i;
	register char chr, flag;

	flag = i = 0;
	while(ishex(chr = toupper(*cmdptr))) {
		++cmdptr;
		flag = -1;
		if(chr > '9')
			chr -= 7;
		i = (i*16) + (chr - '0'); }
	if(!flag) {
		error("Invalid HEX digit");
		return -1; }
	return i;
}

/*
 * Display a character in printable form.
 * Non-printable characters display as '.'
 */
putprint(chr)
	char chr;
{
	vputc(((chr >= ' ') && (chr < 0x7f)) ? chr : '.');
}

/*
 * Display an error message on the bottom line of the screen
 */
error(string)
	char *string;
{	vgotoxy(0,23);
	V_ATTR = REVERSE;
	putstr(string);
	V_ATTR = NORMAL;
	vcleos();
	vputc(7);
}

/*
 * Compare two strings for the given length
 */
compare(str1, str2, len)
	char *str1, *str2;
	int len;
{
	while(len--) {
		if(*str1++ != *str2++)
			return 0; }
	return 1;
}

/*
 * Write a string to the output device
 */
putstr(ptr)
	char *ptr;
{
	register char c;

	while(c = *ptr++)
		vputc(c);
}
[ RETURN TO DIRECTORY ]