[comp.sources.misc] A File Patching Package

tanner@ki4pv.UUCP (07/03/87)

: "un-pack me with /bin/sh"
# This shar file contains a file patcher known around here as "fddt".
# The "Makefile" is incredibly simple.  The man page explains
# everything (hihi).  I hand-shar'ed the thing, as I don't have a
# "shar" prog running here.  It might unpack anyway.  Try it, after
# inspecting for trojan horse droppings.

echo	"unshar: x-fddt.1"
sed s/\^X-// > fddt.1 <<END_OF_FILE
X-.TH FDDT 1
X-.SH NAME
X-fddt \- display and patch files
X-.SH SYNOPSIS
X-.B fddt
X-.I "[-rw]"
X-.I "file"
X-.I "[-patch addr old1=new1 [old2=new2 ...] ]"
X-.SH DESCRIPTION
X-The
X-.I fddt
X-program allows viewing and patching of files, including special
X-files, program binaries, and binary data files.  This is especially
X-helpful on those systems where "adb" won't touch your target file,
X-or where "adb" isn't available.
X-.PP
X-If the
X-.I "-r"
X-argument is given, the file is opened for reading only.
X-If the
X-.I "-w"
X-argument is given, the file is opened for writing only.
X-The default is to open for both read and write.  It is advised that
X-you use the "-r" option on directories and "-w" option on line
X-printers.
X-.PP
X-If the
X-.I "-p"
X-option is not given, the program is interactive and reads commands
X-from the standard input.
X-All of the commands take one or two addresses, which are
X-offsets from the beginning of the file.
X-The addresses are expressed in hex.
X-The first address is the starting address,
X-and the second address is (if given) the ending address.
X-If the second address is omitted, EOF (actually a number greater than
X-the max unix filesize) is assumed.
X-If the first address is omitted, no work is done.
X-Addresses may be separated by a space, comma, or dash.
X-.\"
X-.SH "INTERACTIVE COMMANDS"
X-.IP "\fBd\fP"
X-Display data from the file, starting at the address
X-given.
X-Data are shown as hex numbers and as characters, a la \fIddt\fP.
X-.\"
X-.IP "\fBe\fP"
X-Enter data into the file, starting at the address
X-given.  It prompts with the address, and accepts bytes to be written
X-into the file.  The bytes are written upon receipt of a newline.
X-Bytes must be entered as one or two hex digits.  An EOF (normally
X-ctrl/D or ctrl/Z) will end input, as will an interrupt.
X-.\"
X-.IP "\fBf\fP"
X-Find data in the file, starting at the address
X-given and continuing to the end of file, interrupt, or the second
X-address if given.  It prompts for a series of bytes to be searched
X-for; they must be entered as one or two hex digits each.  One line of
X-bytes may be entered; the search starts upon receipt of the newline.
X-.\"
X-.IP "\fBq,x\fP"
X-Quit.  Also known as exit.
X-.SH "OPTIONS"
X-.IP "\fB-r\fP"
X-The file is opened for reading only.  The "e" command to enter data
X-will not work.  Non-interactive patching will not work.
X-.IP "\fB-w\fP"
X-The file is opened for writing only.  The "d" command to display
X-data and the "f" command to find data will not work.  If patching,
X-the old values must be given as "XX".
X-.IP "\fB-p\fP"
X-The file is to be patched non-interactively.
X-The next argument is the starting address for the patching,
X-and the following arguments are the old and new value substitutions.
X-If the old value given does not match the value found in the file, an
X-error is indicated; use "XX" if the old value is unknown or if the
X-file is open for writing only.
X-If the new value is given as "XX", no value is written.  Allows
X-skipping of bytes in the file.
X-.SH BUGS
X-This prog was originally written to fill a need under ms-dos, where
X-the local debugger won't patch executable files!
X-.PP
X-Most versions of unix won't let you patch a running program.
X-This means, of course, that the prog can't patch itself.
X-.PP
X-There should probably be a way to supress the lseek(2) attempts for
X-non-seekable files.  Displaying or patching your own tty can be
X-amusing.
X-.SH "SEE ALSO"
X-adb(1), debug(ms-dos)
X-.SH "AUTHOR"
X-This program and document were written by Tanner Andrews at
X-CompuData, Inc. in support of the Brother John\(rg project.
X-CompuData, by making them available to you,
X-neither gives up any rights nor assumes any responsibility
X-for damages caused by them.
END_OF_FILE

echo "unshar: x-Makefile"
sed s/\^X-// > Makefile <<END_OF_FILE
X-
X-# Makefile for "fddt"
X-
X-# set this as proper (use second one for xenix)
X-CFLAGS =  -O
X-#CFLAGS =   -O -K -Mm2
X-
X-fddt:	fddt.o
X-	$(CC) $(CFLAGS) -o fddt  /lib/cdate.o fddt.o
X-
END_OF_FILE

echo "unshar: x-fddt.c"
sed s/\^X-// > fddt.c <<END_OF_FILE
X-
X- /***************************************************************
X-  *								*
X-  *			disk file patch prog			*
X-  *								*
X-  ***************************************************************/
X-
X- /*	19-Nov-85  [ki4pv!tanner]				*/
X- /*	ed  20-Dec-85  [ki4pv!tanner] -- add find command	*/
X- /*	ed  03-Jan-86  [ki4pv!tanner] -- add exit command	*/
X- /*	ed  01-Jul-86  [ki4pv!tanner] -- add -p flag		*/
X- /*	ed  22-Oct-86  [ki4pv!tanner] -- turn off stdout buffer	*/
X-
X-
X-
X-#include  <stdio.h>
X-#include  <signal.h>
X-
X-#define	 SZ		0x0100		/* size of things */
X-
X- /*	this program allows someone to apply patches to files
X-  *	of almost any type.  it helps a lot if lseek() will work
X-  *	on the file, but it is not required if the file to be
X-  *	"patched" is a serial device which is really being fed
X-  *	a stream of bytes.
X-  *
X-  *	usage:
X-  *	  fddt [-rw] <file>
X-  *	or:
X-  *	  fddt [-w] <file> -p <addr> old1=new1 old2=new2 ...
X-  *	where
X-  *	  -r	read-only; patches may not be applied
X-  *	  -w	write-only; old contents may not be displayed
X-  *	  -p	"patch" mode, starts at addr and applies patch
X-  *
X-  *	once a file is selected for editing, the following commands
X-  *	will apply for interactive mode within the prog:
X-  *	 d	display data
X-  *	 e	enter new values
X-  *	 f	find data; will prompt for values
X-  *	 q,x	exit
X-  *	all commands take either one or two addresses (if no addr
X-  *	given, no work is done).  the first addr is a starting addr;
X-  *	the second, if given, is an ending addr.  default end is EOF.
X-  *	note that all addresses and data values are to be expressed
X-  *	in hex.  new values being entered are entered as 1-byte hex
X-  *	values.
X-  *
X-  *	patch mode is a way to non-interactively change values in the
X-  *	file being edited.  see the man page.
X-  *
X-  *	warning:  if your unix does not have rdchk(), you may either
X-  *	provide a substitute with ioctl() or eliminate the line of
X-  *	code using it with a return(0) and just use ^C to interrupt
X-  *	commands.  rdchk() appears new with SYS-3, replacing a V7
X-  *	ioctl() call.
X-  *
X-  *	note for cd_txt[] -- this is supplied by a little daemon
X-  *	which creates /lib/cdate.o containing today's date.  the
X-  *	daemon to do this is available from "ki4pv!tanner" upon
X-  *	application.  you could also eliminate the reference.
X-  *
X-  *	This prog and documentation (C) 1987, CompuData, Inc.
X-  *	Permission is granted to use it, so long as this
X-  *	copyright notice remains intact, and so long as you
X-  *	don't try to sell it or claim that you wrote it.
X-  *	By making this program available, CompuData neither gives
X-  *	up any rights in it, nor assumes any responsibility for
X-  *	any damages caused by it.
X-  */
X-
X-	 /*  important vars  */
X-
X-
X-static char
X-	tf_blt[] =	"FDDT   vers 1.2a  built %s\n\n",
X-
X-	tf_usg[] =	"? usage:  %s [-rw] <file> [-p <addr> old=new ...]\n",
X-
X-	tf_sup[] =	"%% %s suppressed\n",
X-	tx_rd[] =	"reading",
X-	tx_wr[] =	"writing",
X-
X-	tf_bad[] =	"? %s: bad %s: %s\n",
X-	tf_bdr[] =	"? bad range: %08lX > %08lX\n",
X-	tf_nos[] =	"? no search values\n",
X-	tf_sek[] =	"? can't seek: %08lX\n",
X-	tf_wre[] =	"? write error: file %s, off 0x%08X, n 0x%X\n",
X-
X-	tf_fnd[] =	"found @ 0x%08lX\n",
X-	tf_nfd[] =	"not found, end @ 0x%08lX\n",
X-	tf_8x[] =	"%08lX: ",
X-	tf_2x[] =	"%02X ",
X-
X-	tf_pat[] =	"patching 0x%08lX: ",
X-	tf_pr1[] =	"0x%02X",
X-	tx_prx[] =	" xx ",
X-	tf_per[] =	"read mismatch: exp 0x%02X, got 0x%02X\n",
X-	tx_pex[] =	"patch expr",
X-
X-	tx_int[] =	"** intr **\n";
X-
X-
X-unsigned int _fmode =	0x8000;		/* for ms-dos */
X-
X-
X- /* - - - - */
X-
X-short int
X-	stoppt,			/* "action stopped" flag */
X-	rw_sup,			/* -r or -w flag */
X-	patch,			/* set true if patching */
X-	ed_fd;			/* fd of file to be patched */
X-
X-char
X-	*prog_name,
X-	*inbuf,			/* user input buffer */
X-	*ed_fn,			/* name of file being patched */
X-	*ed_buf,		/* edit buffer */
X-	*match;			/* file match buffer */
X-
X-  /* - - - - - */
X-
X-extern char
X-	cd_txt[];		/* /lib/cdate.o, kept by daemon */
X-
X- /*  = = = = =  */
X-
X-short int
X-	do_hdr(),		/* things to do before any work */
X-	do_lev(),		/* evaluate a long addr */
X-	get_ln(),		/* get series of numbers */
X-	get_st(),		/* get "stopped" flag */
X-	pat_ev(),		/* eval expression for patch */
X-	sig_st();		/* signal handler */
X-
X-void
X-	do_opn(),		/* open the file */
X-	do_pat(),		/* patch the file */
X-	do_edt(),		/* edit the file */
X-	do_dmp(),		/* dump part of file */
X-	do_inp(),		/* input new values to file */
X-	do_src(),		/* find values in file */
X-	usage();
X-
X-  /* - - - - - */
X-
X-extern long int
X-	lseek();
X-
X-extern int
X-	chupper(),		/* $LOCLIB */
X-	dehex();		/* $LOCLIB */
X-	isprint(), iswhite(), isxdigit(),
X-	rdchk();		/* $SYSDEP */ /* pending input check */
X-
X-extern char
X-	*malloc(),
X-	*strbrk(),		/* $LOCLIB */
X-	*strchr();
X-
X-	 /*  main block of prog  */
X-
X-main(ac, av)
X-short int ac;
X-char	**av;
X-	{
X-	register char
X-		*p;
X-
X-
X-	fprintf(stderr, tf_blt, cd_txt);
X-
X-	prog_name = **av ? *av : "FDDT";
X-
X-	if  (ac < 2)
X-		usage();
X-
X-	if  (*(p = *++av) == '-')  {	/* have flag */
X-		if  (((rw_sup = chupper(*++p)) != 'R') &&
X-		     (rw_sup != 'W'))
X-			usage();
X-		++av;
X-		}
X-	else
X-		rw_sup = 0;
X-
X-	ed_fn = *av;			/* get working filename */
X-
X-	setbuf(stdout, (char *)0);
X-
X-	if  ((!(ed_buf = malloc(SZ))) ||
X-	     (!(match = malloc(2*SZ))) ||
X-	     (!(inbuf = malloc(SZ))))  {
X-		fprintf(stderr, "? %s: can't allocate\n", prog_name);
X-		exit(1);
X-		}
X-
X-	signal(SIGINT, sig_st);		/* catch ^C to abort command */
X-
X-	do_opn();			/* open the file */
X-
X-	if  (*++av)
X-		do_pat(av);		/* patch the file */
X-	else
X-		do_edt();		/* edit the file */
X-
X-	if  (close(ed_fd))		/* close the file */
X-		no_clos(ed_fn);
X-
X-	exit(0);
X-
X-	}
X-
X-
X-static void usage()
X-	{
X-
X-	fprintf(stderr, tf_usg, prog_name);
X-	exit(1);
X-
X-	}
X-
X-
X-	 /*  open file to be edited  */
X-
X-
X-void do_opn()
X-	{
X-	register short int
X-		mode;
X-
X-
X-	switch (rw_sup)  {		/* select file open mode */
X-		case 'R':
X-			mode = 0;
X-			break;
X-		case 'W':
X-			mode = 1;
X-			break;
X-		default:
X-			mode = 2;
X-		}
X-
X-	if  ((ed_fd = open(ed_fn, mode)) < 0)
X-		no_open(ed_fn);
X-
X-	}
X-
X-	 /*  patch the file  */
X-
X-void do_pat(ap)
X-char	**ap;
X-	{
X-	register char
X-		*p;
X-	short int
X-		old, new;
X-	long int
X-		p_addr,			/* patch addr */
X-		tmp;			/* tmp value from eval */
X-	unsigned char
X-		c;			/* in/out data item */
X-
X-
X-	if  ((*(p = *ap) != '-') ||
X-	     (chupper(*++p) != 'P'))
X-		usage();
X-
X-	if  (do_lev(*++ap, &p_addr))  {	/* eval addr */
X-		fprintf(stderr, tf_bad, prog_name, "addr", *ap);
X-		exit(1);
X-		}
X-
X-	if  (lseek(ed_fd, p_addr, 0) != p_addr)  {	/* just test */
X-		fprintf(stderr, tf_sek, p_addr);
X-		exit(2);
X-		}
X-
X-	for  ( ; p = *++ap ; ++p_addr )  {
X-		p += pat_ev(p, &old, "old");
X-		if  (*p++ != '=')  {
X-			fprintf(stderr, tf_bad, prog_name, tx_pex, *ap);
X-			exit(3);
X-			}
X-		p += pat_ev(p, &new, "new");
X-		if  (*p)  {
X-			fprintf(stderr, tf_bad, prog_name, tx_pex, *ap);
X-			exit(4);
X-			}
X-		printf(tf_pat, p_addr);
X-		if  (old >= 0)  {		/* match old */
X-			if  (rw_sup == 'R')  {
X-				fprintf(stderr, tf_sup, tx_rd);
X-				exit(5);
X-				}
X-			if  ((lseek(ed_fd, p_addr, 0) != p_addr) ||
X-			     (read(ed_fd, &c, 1) != 1) ||
X-			     (c != old))  {
X-				fprintf(stderr, tf_per, old, c);
X-				exit(6);
X-				}
X-			printf(tf_pr1, old);
X-			}
X-		else
X-			printf(tx_prx);
X-		printf(" -> ");
X-		if  (new >= 0)  {
X-			if  (rw_sup == 'W')  {
X-				fprintf(stderr, tf_sup, tx_wr);
X-				exit(7);
X-				}
X-			c = new;
X-			if  ((lseek(ed_fd, p_addr, 0) != p_addr) ||
X-			     (write(ed_fd, &c, 1) != 1))  {
X-				fprintf(stderr, tf_wre, ed_fn, p_addr, 1);
X-				exit(8);
X-				}
X-			printf(tf_pr1, new);
X-			}
X-		else
X-			printf(tx_prx);
X-		printf(" ok\n");
X-		}
X-
X-	}
X-
X-
X-	  /*  patch support: evaluate old/new value  */
X-
X-
X-static short int pat_ev(sp, dp, err)
X-char	*sp, *err;
X-short int *dp;			/* result goes here */
X-	{
X-	register char
X-		*q;
X-	long int
X-		tmp;
X-
X-
X-	if  (chupper(*sp) == 'X')  {
X-		*dp = -1;
X-		return(1);
X-		}
X-	if  (do_lev(sp, &tmp) || (tmp < 0) || (tmp > 0xFF))  {
X-		fprintf(stderr, tf_bad, prog_name, err, sp);
X-		exit(3);
X-		}
X-	*dp = tmp;
X-
X-	for  ( q=sp ; isxdigit(*q) ; ++q );
X-	return((short int)(q - sp));
X-
X-	}
X-
X-	 /*  edit the file (top-level interaction)  */
X-
X-
X-void do_edt()
X-	{
X-	register char
X-		*p;
X-	register short int
X-		nrd;
X-
X-
X-	while  (((nrd=read(0, inbuf, 100)) > 0) || get_st())  {
X-		if  (nrd < 0)		/* ^C caused I/O err */
X-			nrd = 0;
X-		*(inbuf+nrd) = 0;	/* null at end-of-input */
X-		if  (p = strchr(inbuf, '\n'))
X-			*p = 0;
X-		p = inbuf;
X-		while (iswhite(*p))
X-			++p;
X-		switch (chupper(*p))  {
X-			case 'F':
X-				do_src(p+1);
X-				break;
X-			case 'E':
X-				do_inp(p+1);
X-				break;
X-			case 'D':
X-				do_dmp(p+1);
X-				break;
X-			case 'Q':
X-			case 'X':
X-				return;
X-			case '?':
X-				printf("D-display  E-enter  F-find  Q-exit\n");
X-				break;
X-			default:
X-				printf("unknown: %c\n", *p);
X-			case 0:
X-				break;
X-			}
X-		}
X-
X-	}
X-
X-	 /*  dump values  */
X-
X-
X-void do_dmp(p)
X-char	*p;
X-	{
X-	register short int
X-		sz, nd;
X-	long int
X-		ed_adr,
X-		ed_cur,
X-		ed_end,
X-		lnend;
X-
X-
X-	if  (do_hdr('R', p, &ed_adr, &ed_end))	/* input bad */
X-		return;
X-
X-	ed_cur = ed_adr;		/* back to start */
X-
X-	do  {
X-		lnend = ed_cur - (ed_cur % 16) + 15;
X-		if  (lnend > ed_end)
X-			lnend = ed_end;
X-		sz = lnend - ed_cur + 1;
X-		if  ((nd = read(ed_fd, ed_buf, sz)) < sz)
X-			sz = nd;
X-		if  (sz < 1)  {
X-			printf("e-o-f\n");
X-			break;
X-			}
X-		printf(tf_8x, ed_cur);
X-		for  ( nd=0 ; nd<sz ; ++nd )  {
X-			printf(tf_2x, *(ed_buf+nd) & 0x00FF);
X-			if  (nd == 7)
X-				fputc(' ', stdout);
X-			}
X-		fputs("  ", stdout);
X-		for  ( nd=0 ; nd<sz ; ++nd )  {
X-			fputc(isprint(*(ed_buf+nd)) ? *(ed_buf+nd) : '.',
X-						stdout);
X-			if  (nd == 7)
X-				fputc(' ', stdout);
X-			}
X-		fputc('\n', stdout);
X-		}  while ((! get_st()) && ((ed_cur += sz) <= ed_end));
X-
X-	fputc('\n', stdout);
X-
X-	}
X-
X-	 /*  enter new values into file  */
X-
X-void do_inp(p)
X-register char	*p;
X-	{
X-	register short int
X-		n;
X-	long int
X-		ed_adr,
X-		ed_cur,
X-		ed_end;
X-
X-
X-	if  (do_hdr('W', p, &ed_adr, &ed_end))	/* command bad */
X-		return;
X-
X-	ed_cur = ed_adr;
X-	for ( ; (n = get_ln(ed_cur)) >= 0  ; )  {
X-		if  ((n > 0) && (write(ed_fd, ed_buf, n) < n))  {
X-			fprintf(stderr, tf_wre, ed_fn, ed_cur, n);
X-			break;
X-			}
X-		if  ((ed_cur += n) > ed_end)	/* gave exit point */
X-			break;
X-		}
X-
X-	}
X-
X-
X-	 /*  find values in file  */
X-
X-#define	 MATCH		(*(ed_buf+bufm) == *(match+bufo+bufm))
X-
X-void do_src(p)
X-char	*p;
X-	{
X-	register short int
X-		nsrc,				/* # to search for */
X-		nbuf,				/* # chars in buff */
X-		bufo,				/* buffer offset */
X-		bufm,				/* buffer # matched */
X-		nrd;
X-	long int
X-		ed_adr,
X-		ed_cur,
X-		ed_end,
X-		val;
X-
X-
X-	if  (do_hdr('R', p, &ed_adr, &ed_end))	/* input bad */
X-		return;
X-
X-	ed_cur = ed_adr;		/* back to start */
X-
X-	if  ((nsrc = get_ln(0L)) <= 0)  {
X-		printf(tf_nos);
X-		return;
X-		}
X-
X-	printf("searching...");
X-	nrd = nbuf = read(ed_fd, match, 2*SZ);
X-	for  ( bufo=0 ;; )  {
X-		for  ( bufm=0 ; (bufm < nsrc) && MATCH ; ++bufm );
X-		if  (bufm == nsrc)  {
X-			printf(tf_fnd, ed_cur);
X-			return;
X-			}
X-		++ed_cur;		/* start at next char */
X-		if  (++bufo > SZ)  {	/* must read more */
X-			move(match, match+SZ, SZ);
X-			bufo -= SZ;
X-			nbuf -= SZ;
X-			if  (get_st())	/* input, stop search */
X-				nrd = 0;
X-			if  (nrd && ((nrd=read(ed_fd, match+SZ, SZ)) > 0))
X-				nbuf += nrd;
X-			}
X-		if  ((bufo >= nbuf) || (ed_cur > ed_end))  {
X-			printf(tf_nfd, ed_cur);
X-			return;
X-			}
X-		}
X-
X-	}
X-
X-	 /*  common support routines  */
X-
X-
X- /*  handle the command understanding for both display and edit
X-  *  expects
X-  *	sup		'R', 'W' if reading, writing required
X-  *	p		user's input
X-  *	sad, ead	ptrs to display/edit start/end addr
X-  *  returns
X-  *	  0		good input
X-  *	 -1		didn't
X-  */
X-static short int do_hdr(sup, p, sad, ead)
X-char	sup, *p;
X-long int *sad, *ead;
X-	{
X-
X-	if  (rw_sup && (rw_sup != sup))  {	/* make sure it's OK */
X-		printf(tf_sup, (sup=='R') ? tx_rd : tx_wr);
X-		return;
X-		}
X-
X-	if  (do_lev(p, sad))			/* get starting addr */
X-		return(-1);
X-
X-	*ead = 0x40000000;			/* dflt end addr */
X-
X-	while (iswhite(*p))			/* skip white space */
X-		++p;
X-
X-	if  ((p = strbrk(p, "-, ")) && do_lev(++p, ead))  {
X-		printf(tf_bad, prog_name, "end", p);
X-		return(-1);
X-		}
X-
X-	if  (*ead < *sad)  {
X-		printf(tf_bdr, *sad, *ead);
X-		return(-1);
X-		}
X-
X-	if  (lseek(ed_fd, *sad, 0) != *sad)  {	/* do the seek */
X-		printf(tf_sek, *sad);
X-		return(-1);
X-		}
X-
X-	return(0);
X-
X-	}
X-
X-
X- /*  evaluate an address
X-  *  expects
X-  *	p	ptr to addr to scan
X-  *	adr	addr to receive value if we found number
X-  *  returns
X-  *	 0	worked
X-  *	-1	no addr found
X-  *  note
X-  *	if it works, *adr will be filled in.
X-  */
X-
X-static short int do_lev(p, adr)
X-char	*p;
X-long int *adr;
X-	{
X-	register long int
X-		val;
X-
X-
X-	while (iswhite(*p))		/* skip */
X-		++p;
X-
X-	if  (! isxdigit(*p))
X-		return(-1);
X-
X-	for  ( val=0 ; isxdigit(*p) ; ++p )
X-		val = (val << 4) + dehex(*p);
X-
X-	if  (*p && (! iswhite(*p)) && (*p != '=') && (*p != '-'))
X-		return(-1);
X-
X-	*adr = val;
X-	return(0);
X-
X-	}
X-
X-
X- /*  get a list of numbers into the edit buffer
X-  *  expects
X-  *	ed_cur	address to be printed in prompt
X-  *  returns
X-  *	 -1	trouble
X-  *	 n	# bytes specified on line by user
X-  *  fills in ed_buf with the values given
X-  */
X-
X-static short int get_ln(ed_cur)
X-long int ed_cur;
X-	{
X-	register char
X-		*p;
X-	register short int
X-		n;
X-	long int
X-		val;
X-
X-
X-	printf(tf_8x, ed_cur);			/* prompt */
X-	zero(inbuf, SZ);
X-	if  (read(0, p=inbuf, 200) < 1)  {	/* hit EOF */
X-		fputs(" ** OK\n", stdout);
X-		return(-1);
X-		}
X-	for  ( n=0 ; *p ; )  {			/* scan & convert */
X-		if  (do_lev(p, &val) ||
X-		    (val > 0x00FF) || (val < 0))  {
X-			printf("bad value: %s\n", p);
X-			n = 0;
X-			break;
X-			}
X-		*(ed_buf+(n++)) = val;
X-		while (isxdigit(*p))
X-			++p;
X-		while (isxdigit(*p))
X-			++p;
X-		if  (*p == ',')
X-			++p;
X-		while (iswhite(*p))
X-			++p;
X-		}
X-
X-	return(n);
X-
X-	}
X-
X-	 /*  user-attention handlers  */
X-
X- /*  user hits ^C, set flag to indicate same
X-  *  note:  under ms-dos, signal() only works for ^C
X-  *	but that's OK.  use MS-C; lattice doesn't have
X-  *	signal(2).
X-  */
X-static short int sig_st()
X-	{
X-
X-	signal(SIGINT, sig_st);		/* continued protection */
X-	printf(tx_int);			/* ack for abuser */
X-	stoppt = 1;			/* set flag */
X-
X-	}
X-
X-
X- /*  routine to see if user wants to stop command
X-  *  checks/resets "stoppt" flag set by ^C
X-  *  if machine has rdchk() uses that to see if pending input
X-  */
X- /* $SYSDEP */
X-static short int get_st()
X-	{
X-
X-	if  (stoppt)  {			/* hit ^C */
X-		stoppt = 0;		/* clear it */
X-		return(1);
X-		}
X-
X-	return(rdchk(0) > 0);		/* pending input */
X-
X-	}
X-
X-	 /*  local library routines  */
X-
X- /*  these routines normally live in a local library.  the names
X-  *  are actually left-overs from CP/M days.
X-  */
X-#ifndef	DONT_NEED
X- /*  convert a hex digit  */
X-int dehex(c)
X-int	c;
X-	{
X-	if  ((c >= '0') && (c <= '9'))
X-		return(c - '0');
X-	if  ((c >= 'A') && (c <= 'F'))
X-		return(c + 10 - 'A');
X-	if  ((c >= 'a') && (c <= 'f'))
X-		return(c + 10 - 'a');
X-	return(-1);
X-	}
X-
X- /*  find character in "breakset"  */
X-char *strbrk(s1, s2)
X-register char *s1, *s2;
X-	{
X-
X-	if  (s1 && s2)				/* have strings */
X-		for  ( ; *s1 ; ++s1 )		/* scan string */
X-			if  (strchr(s2, *s1))	/* match any? */
X-				return(s1);	/* yup, winner */
X-
X-	return((char *)0);
X-
X-	}
X-
X- /*  case conversion -- don't trust "toupper" or "tolower  */
X-int chupper(c)
X-int	c;
X-	{
X-	return(((c >= 'a') && (c <= 'z')) ? (c - ('a'-'A')) : c);
X-	}
X-
X-#endif
X-
END_OF_FILE