v13i035: Binary file editor

Rich Salz rsalz at bbn.com
Fri Feb 12 00:42:49 AEST 1988


Submitted-by: Johan Vromans <mcvax!mh.nl!jv>
Posting-number: Volume 13, Issue 35
Archive-name: zap

[  This is a simple little editor for binary-type files.  It works under
   Unix, VMS, and MS-DOS.  I'm not sure what "no military use means";
   don't zap the enemy, I guess.  --r$ ]


Features include -

 - looking at the file by byte, word or longword,
 - display contents in octal, hex, decimal and ascii,
 - searching for bytes/words/longwords
 - verification of changes
 - buffered update with optional checksum
 - runs on Unix, VAX/VMS and MS-DOS

Zap mimics a program called SIPP (Save Image Patch Program), which
was supplied by Digital Equipment Corp. with the RT-11 operating system.

Distribution free as long as you give credit to the original author.
Military use and explicit resale prohibited.

Johan Vromans                              | jv at mh.nl via European backbone
Multihouse N.V., Gouda, the Netherlands    | uucp: ..{uunet!}mcvax!mh.nl!jv
"It is better to light a candle than to curse the darkness"

#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create:
#	Read.Me
#	zap.c
#	zap.1
#	Makefile
#	mkzap.com
#	mkzap.bat
export PATH; PATH=/bin:/usr/bin:$PATH
if test -f 'Read.Me'
then
	echo shar: "will not over-write existing file 'Read.Me'"
else
cat << \SHAR_EOF > 'Read.Me'
This is zap, a binary file inspector/patcher.

Written by Johan Vromans at Multihouse Research, Gouda, the Netherlands.
Copyright 1987 Johan Vromans.
Distribution free as long as you give credit to the original author.
Military use and explicit resale prohibited.

Features include -

 - looking at the file by byte, word or longword,
 - display contents in octal, hex, decimal and ascii,
 - searching for bytes/words/longwords
 - verification of changes
 - buffered update with optional checksum
 - runs on Unix, VAX/VMS and MS-DOS

Zap mimics a program called SIPP (Save Image Patch Program), which
was supplied by Digital Equipment Corp. with the RT-11 operating system.

----------------

Building zap

Zap is pretty portable, mainly because it does not use difficult things.
No shell escapes, no tty rubbish, only standard IO.

The only thing which is important to zap is whether your system
has byte ordering like a VAX or not.
When it has its bytes ordered VAX-wise, you may compile zap with -DSWAB=1,
otherwise use -DSWAB=0. If you are not sure: don't specify -DSWAB and zap 
will find out itself.

This distribution contains:

	Read.Me		this file
	zap.c		C program source
	zap.1		nroff -man manual page
	Makefile	makefile for unix
	mkzap.com	compile for VAX/VMS
	mkzap.bat	compile for MS-DOS

----------------
Special notes for Unix:

 - the program has been tested on System V and BSD.

 - on swabbing and non-swabbing machines.

Special notes for VAX/VMS:

 - the program has been tested with VAX-C (not VAX11C).

 - always compile with /DEFINE=(SWAB=2) (use the mkzap.com file).

 - performs well, but probably limited to stream-lf files.

Special notes for MS-DOS:

 - the program has been tested with Microsoft C V4 using small model.

 - always compile with -DLINT_ARGS.

----------------

Suggestions and enhancements are welcome!

Johan Vromans, Multihouse Research
Usenet: jv at mh.nl via european backbone mcvax.

Disclaimer: Usage of this program is always at your own risk.
SHAR_EOF
if test 1992 -ne "`wc -c < 'Read.Me'`"
then
	echo shar: "error transmitting 'Read.Me'" '(should have been 1992 characters)'
fi
fi
if test -f 'zap.c'
then
	echo shar: "will not over-write existing file 'zap.c'"
else
cat << \SHAR_EOF > 'zap.c'
/* zap.c - program to inspect/patch binary files */

static char SCCS_id[] = "@(#)@ zap	1.9	zap.c";

static char cprght[] = "\
@(#) Written by Johan Vromans at Multihouse Research, Gouda, the Netherlands.\n\
@(#) Copyright 1987 Johan Vromans.\n\
@(#) Distribution free as long as you give credit to the original author.\n\
@(#) Military use and explicit resale prohibited.\n\
@(#) Usage of this program is always at your own risk.";

#include <stdio.h>
#include <ctype.h>
#include <signal.h>

#ifndef TRUE
#  define TRUE	1
#  define FALSE	0
#endif

/* define SWAB=1 for byte swapping machines, such as vax and pdp-11 */
/* otherwise, define it to 0 */
/* if unknown, don't define it (or set it to 2) - zap will find out */
/* when known, it is up to the C compiler to optimize unneeded code */

#ifndef SWAB
/* SWAB not defined - use info for machines we know */
# ifdef vax			/* DEC VAX */
#  define SWAB 1
# endif
# ifdef pdp11			/* DEC PDP-11 */
#  define SWAB 1
# endif
# ifdef hp9000s200		/* Hewlett-Packard HP9000/200 (M68xxx) */
#   define SWAB 0
# endif
# ifdef hp9000s500		/* Hewlett-Packard HP9000/500 (FocusII) */
#   define SWAB 0
# endif
# ifdef M_I86			/* Intel 86 family */
#   define SWAB	1 
# endif
#endif

#ifdef SWAB
# if SWAB > 1		/* explicitly unknown */
#  undef SWAB
# endif
#endif

#ifndef SWAB
int	swab = FALSE;		/* use dynamic method */
#else
#  define swab	SWAB		/* leave it to the compiler to eliminate */
#endif

/* About swabbing - 
 *
 *	Representation of data
 *
 *				swabbing	non-swabbing
 *	type	    numeric	character	character
 *	byte	    0x61	'a'		'a'
 *	word	    0x6162	'ba'		'ab'
 *	longword    0x61626364	'dcba'		'abcd'
 */

#ifdef MSDOS
# ifdef LINT_ARGS

/* function defs as generated by MS-C V4.0 */

/*global*/  int main (int, char**);
/*global*/  int decod (char*, long*);
/*global*/  unsigned int gv_file (long);
/*global*/  int locate (long);
/*global*/  int enter (long, int);
/*global*/  int get_value (long);
/*global*/  int put_value (long, long);
/*global*/  int ptv_file (long, char);
/*global*/  int push_loc (long);
/*global*/  long pop_loc (void);
/*global*/  int quit_search (void);
/*global*/  int search (void);
/*global*/  int verify (void);
/*global*/  int gt_line (char*, char*, long, long, char, char*);
/*global*/  int gt_val (char*, long*);
/*global*/  char* pr_val (long, int);
/*global*/  int zap (char*);
/*global*/  int cant (char*);
/*global*/  int remark (char*, long);
/*global*/  int error (char*);
/*global*/  int swabcheck (void);

# endif
#endif

long	lseek ();
char	*strcpy ();
char	*calloc ();
char	*realloc ();
void	exit();
#define	V_printf	(void) printf
#define	V_fprintf	(void) fprintf
#define	V_sprintf	(void) sprintf
#ifdef lint
void clearerr ();
#endif

char	*my_name    = "zap";	/* identification */
char	*usage      = "usage: zap [-cdrsvw] file";

/* option flags */

int	f_check   = FALSE;	/* request checksum */
int	f_sum     = FALSE;	/* print checksum */
int     f_write   = FALSE;	/* read-write */
int	f_silent  = FALSE;	/* silent */
int	f_batch   = FALSE;	/* running batch mode */
int	f_verbose = FALSE;	/* give more info */

/* main routine */

main (argc, argv)
int	argc;		/* # arguments + 1 */
char	*argv[];	/* argument pointers */

  {
    int		file_cnt;	/* number of files processed */
    char	*arg_ptr;	/* argument pointer */
    char	c;		/* current option character */

    swabcheck ();		/* verify or establish swabbing mode */

    /* ignore first argument (program name) */

    argc--;
    argv++;

    f_batch = !isatty (0);
    file_cnt = 0;		/* haven't seen one yet */

    while (argc-- > 0)		/* through arguments */
      {
        /* fetch a pointer to the current argument, and
         * increase argv
         */

        arg_ptr = *argv;
        argv++;

        if (*arg_ptr == '-')		/* must be an option */
          {
            while (c = *++arg_ptr)	/* get option character */
              switch (c)
                {

              case  'C' :
              case  'c' :
                          f_check = TRUE;	/* request checksum */
                          break;

              case  'D' :
              case  'd' :
                          f_sum = TRUE;		/* print checksum */
                          break;

              case  'R' :
              case  'r' :
                          f_write = FALSE;	/* read-only */
                          break;

              case  'S' :
              case  's' :
                          f_silent = TRUE;	/* a little more quiet */
                          break;

              case  'V' :
              case  'v' :
			  V_printf ("zap version 1.9\n");
#ifndef SWAB
	      		  if (!f_verbose)
			    remark ("you may recompile with \"-DSWAB=%ld\"",
			      (long)swab);
#endif
                          f_verbose = TRUE;	/* a little less quiet */
                          break;
              case  'W' :
              case  'w' :
                          f_write = TRUE;	/* allow write access */
                          break;

              default   : error (usage);
                          break;
                }

            /* this ends the option processing */
          }
        else
          {
            /* it must be a file specification */

            file_cnt++;		/* now we've seen one */

    	    zap (arg_ptr);

            /* this ends the file processing */
          }

        /* this ends the argument processing */
      }

    /* if there were no filespecs, give error */

    if (!file_cnt)
      error (usage);

    /* that's it */

#ifdef vaxc
    return (1);
#else
    return (0);
#endif
  }

/* current type values. note - value is also size of type */

int	cur_type;
#define	BYTE	1
#define WORD	2
#define LWORD	4

char dp_type [] = " \\/ |";

/* current display mode */

int cur_printmode;
#define OCTAL	0
#define DECIMAL	1
#define HEX	2
#define ASCII	3

char *defffmt[]	= { "0%05lo", "%6ld", "x%05lx", "0%05lo" };
char *deffmt[]	= { "0%lo", "%ld", "x%lx", "0%lo" };

#define BYTEVAL(x)	((x) & 0xff)

/* current file */

FILE	*zf;

/* get (decimal, hex or octal) value from input line */
/* a zero return value means : ok */

int decod (buf, lp)
char	*buf;
long	*lp;
  {
    long	num;
    char	*cp;
    int dooct = FALSE;
    int dohex = FALSE;
    int doasc = FALSE;

    num = 0;
    cp = buf;
    if (*cp == ';')		/* select mode */
      {
	cp++;
        if (*cp == 'x' || *cp == 'X')
	    dohex = TRUE;
	else
	if (*cp == 'o' || *cp == 'O')
	    dooct = TRUE;
	else
	if (*cp == 'd' || *cp == 'D')
	  ;
	else
	if (*cp == 'a' || *cp == 'A')
	    doasc = TRUE;
	else
	  V_printf ("input error");
	cp++;
      }
    else
      {
	while (*cp == '0')
	  {
	    dooct = TRUE;
	    cp++;
	  }
	if (*cp == 'x' || *cp == 'X')
	  {
	    cp++;
	    dohex = TRUE;
	  }
      }

    if (dohex)
      {
	while (isxdigit (*cp))
	  {
	    num = num * 16 
		  + (isdigit (*cp) 
		     ? *cp - '0' 
		     : (*cp | 0x20) - 'a' + 10);
	    cp++;
	  }    
      }
    else
    if (dooct)
      {
	while (isdigit (*cp) && *cp < '8')
	  {
	    num = num * 8 + *cp - '0';
	    cp++;
	  }    
      }
    else
    if (doasc)
      {
	int i;
	for (i = 0; i < cur_type && *cp; i++)
	  {
	    if (swab)
		num += ((long)(*cp++)) << (i << 3);
	    else
		num = (num << 8) + *cp++;
	  }
      }
    else
      {
	while (isdigit (*cp))
	  {
	    num = num * 10 + *cp - '0';
	    cp++;
	  }    
      }

    *lp = num;
    if (!*cp)
      return (0);
    if (*cp == '^')
      return (-1);	/* special return value for zap */
    else
      return (1);
  }

/* retrieve byte from file */

unsigned gv_file (addr)
long	addr;
  {
    long	l;

    if (fseek (zf, addr, 0))
      remark ("cannot position to %ld", addr);

    (void) clearerr (zf);
    l = fgetc (zf);

    if (l == EOF)
      remark (ferror(zf) ? "cannot read at %ld" : "read beyond eof", addr);

    return (BYTEVAL(l));
  }

#define BUF_INC	512
int	tbl_max = BUF_INC;

struct ntry
  {
    long	addr;
    char	val;
    char	old;
  }
    *tbl,		/* value table */
    *tbl_cur,		/* last referenced entry in table */
    *tbl_free,		/* next free entry in table */
    *tbl_ptr;		/* work pointer into table */

int locate (adr)
long	adr;
  {
    /* lookup address in table. return tbl_cur at correct entry
     * or next higher */

    if (tbl_cur >= tbl && tbl_cur < tbl_free && tbl_cur->addr == adr)
      /* just looked up */
      return (TRUE);

    for (tbl_cur = tbl; tbl_cur != tbl_free; tbl_cur++)
      {
    	if (tbl_cur->addr > adr)
    	  break;
    	if (tbl_cur->addr == adr)
    	  return (TRUE);
      }
    return (FALSE);
  }

enter (addr, val)
long	addr;
int	val;
  {
    char	old;

    /* lookup address */
    if (locate (addr))
      {
    	/* store value, if different from file value */
    	if (val != tbl_cur->old)
    	  {
            tbl_cur->val = val;
     	    return;
          }
    	/* else delete entry from table */
        for (tbl_ptr=tbl_cur; tbl_ptr < tbl_free-1; tbl_ptr++)
          tbl_ptr[0] = tbl_ptr[1];
    	tbl_free--;
    	return;
      }

    /* if not found, tbl_cur points at next higher address entry */
    /* insert new entry at appropriate position */

    old = gv_file (addr);
    if (val == old)		/* no-op if new == old */
      return;

    /* check for space in table, otherwise extend it */
    if (tbl_free == &tbl[tbl_max])
      {
    	tbl_max += BUF_INC;
    	if ((tbl = (struct ntry*) realloc ((char*) tbl, (unsigned) tbl_max * sizeof (*tbl))) == NULL)
      	  error ("table overflow");
      }

    for (tbl_ptr=tbl_free-1; tbl_ptr >= tbl_cur; tbl_ptr--)
      tbl_ptr[1] = tbl_ptr[0];

    tbl_cur->addr = addr;
    tbl_cur->val = val;
    tbl_cur->old = old;

    tbl_free++;
  }

/* retrieve value from table */

int get_value (addr)
long	addr;
  {
    int		val;

    if (locate (addr))
      val = tbl_cur->val;
    else
      val = gv_file (addr);

    return (val);
  }

/* put byte into table */

#define put_byte	enter

/* put value into table */

put_value (addr, val)
long	addr;
long	val;
  {
    int		i;

    for (i=0; i<cur_type; i++)
      {
	register long temp = addr + ((swab) ? i : (cur_type-i-1));
    	put_byte (temp, (int)BYTEVAL(val));
    	val >>= 8;
      }
  }

ptv_file (addr, val)
long	addr;
char	val;
  {
    char 	c;

    c = val;

    if (fseek (zf, addr, 0))
      remark ("cannot position to %ld", addr);

    (void) clearerr (zf);
    (void) fputc (c, zf);
    if (ferror(zf) || feof(zf))
      remark ("cannot write at %ld", addr);
  }

char	buf [132];
char	*pr_val();

#define PREV_MAX 256		/* size of previous goto table	*/
long	prevs [PREV_MAX];	/* previous goto table		*/
int	prevcnt;		/* next free index in previous table */

push_loc (loc)
long loc;
  {
    int i;
    if (prevcnt == PREV_MAX)
      {
	for (i=0; i<prevcnt; i++)
	  prevs[i] = prevs[i+1];
	prevcnt--;
      }
    prevs[prevcnt++] = loc;
  }

long pop_loc ()
  {
    if (prevcnt > 0)
      return (prevs[--prevcnt]);
    return (0);
  }

long	last_value;	/* last printed value		*/
long	sstart;		/* search starting value	*/
long	ennd;		/* search ending value		*/
long	interrupted;	/* search was terminated	*/
int	diddots;	/* dots were displayed		*/

int	quit_search ()
  {
    interrupted = sstart;
    sstart = ennd;
  }

foundit (addr)
long addr;
  {
    if (diddots)
      V_printf ("\n");
    V_printf ("Found at ");
    V_printf (defffmt[cur_printmode], addr);
    V_printf ("\n");
    diddots = FALSE;
    push_loc (addr);
  }

/* search value in file */

search ()
  {
    int		bt;		/* first byte thereof	*/
    long	first;
    union {
      long ll;
      char ss[4];
    } uu;

    if (!gt_val ("Search for ? ", &uu.ll))
      return;

    if (!gt_val ("Start at   ? ", &sstart))
      return;

    if (!gt_val ("Stop at    ? ", &ennd))
      return;

    /* temporary using first to hold EOF value */
    first = lseek (fileno(zf), 0l, 2);
    if (ennd == 0)
      {
	if (f_verbose)
	  {
	    V_fprintf (stderr, "EOF at ");
	    V_fprintf (stderr, deffmt[cur_printmode], first);
	    V_fprintf (stderr, "\n");
	  }
	ennd = first - cur_type + 1;
      }

    if (sstart > ennd)
      {
	remark ("start > end", 0L);
	return;
      }

    if (ennd > first)
      {
	if (f_verbose)
	  remark ("end > EOF, truncated", 0L);
	ennd = first;
      }
    /* end of using first to hold EOF value */

#ifndef SEARCH_ACTUAL
    if (fseek (zf, sstart, 0))
      {
	V_fprintf (stderr, "cannot position to ");
	V_fprintf (stderr, deffmt[cur_printmode], sstart);
	V_fprintf (stderr, "\n");
	return;
      }
#endif

    (void) signal (SIGINT, quit_search);

    /* shift to align */
    if (!swab)
      {
	if (cur_type == BYTE)
	  uu.ss[0] = uu.ss[3];
	else
	if (cur_type == WORD)
	  {
	    uu.ss[0] = uu.ss[2];
	    uu.ss[1] = uu.ss[3];
	  }
      }
    bt = BYTEVAL(uu.ss[0]);

    first = sstart;
    diddots = interrupted = FALSE;

    while (sstart < ennd)
      {

	/* print a dot for every 1K processed */
	if (!f_silent && (((first - sstart) & 0x3ff) == 0) && sstart > first)
	  {
	    V_printf (".");
	    (void) fflush (stdout);
	    diddots = TRUE;
	  }

#ifdef SEARCH_ACTUAL

	/* searching the actual values (very slow) */

    	if (get_value (sstart) == bt)
    	  {
    	    if (
    		( cur_type == BYTE	/* looking for byte is easy */
    		)
    	     || ( cur_type == WORD	/* word needs another byte */
                   && (get_value (sstart+1L) == BYTEVAL(uu.ss[1]))
                )
    	     || ( cur_type == LWORD 	/* lword needs three other bytes */
    		   && (get_value (sstart+1L) == BYTEVAL(uu.ss[1]))
    		   && (get_value (sstart+2L) == BYTEVAL(uu.ss[2]))
    		   && (get_value (sstart+3L) == BYTEVAL(uu.ss[3]))
                )
    	       )
	       foundit (sstart);
    	  }
	start++;

#else

	/* searching the old contents of the file */

    	if (fgetc (zf) == bt)
    	  {
	    int chr;
	    if (cur_type == BYTE)	/* looking for byte is easy */
	      foundit (sstart);
	    else
	      {
		chr = fgetc (zf);
		if (chr == BYTEVAL(uu.ss[1]))
		  {
		    if (cur_type == WORD)
		      {
			foundit (sstart);
			ungetc (chr, zf);
		      }
		    else
		      {
			chr = fgetc (zf);
			if (chr == BYTEVAL(uu.ss[2]))
			  {
			    chr = fgetc (zf);
			    if (chr == BYTEVAL(uu.ss[3]))
			      foundit (sstart);
			  }
			fseek (zf, sstart+1L, 0);
		      }
		  }
		else
		  ungetc (chr, zf);
	      }
	  }
    	sstart++;
	if (ferror (zf) || feof (zf))
	  quit_search ();

#endif

      }				/* while (sstart < ennd) */

    if (diddots)
      V_printf ("\n");
    (void) signal (SIGINT, SIG_DFL);
    if (!f_batch && interrupted)
      {
	V_printf ("Interrupted at ");
	V_printf (defffmt[cur_printmode], sstart);
	V_printf ("\n");
      }
  }

/* print verification list */

verify ()
  {
    long	addr = 0;

    /* display all modifications entered until now. display in portions
     * of cur_printmode. align to lower cur_type boundary
     */
    for (tbl_ptr = tbl; tbl_ptr != tbl_free; tbl_ptr++)
    if (tbl_ptr->addr >= addr)
      {
    	addr = tbl_ptr->addr & ~(cur_type-1);
        V_printf ("vfy: ");
        V_printf (defffmt[cur_printmode], addr);
        V_printf ("%c %-7s => ", dp_type[cur_type], pr_val (addr, FALSE));
        V_printf ("%-7s\n", pr_val (addr, TRUE));
    	addr += cur_type;
      }
  }

int gt_line (dst, prompt, arg1, arg2, arg3, arg4)
char	*dst;
char	*prompt;
long	arg1;
long	arg2;
char	arg3;
char	*arg4;
  {
    if (prompt != NULL && !f_silent)
      V_printf (prompt, arg1, arg2, arg3, arg4);
    (void) fflush (stdout);
    if (!gets (dst))
      {
	if (f_batch && !f_silent)
	  V_printf ("[eof]\n");
#ifndef vaxc
	(void) putchar ('\n');
#endif
	return (NULL);
      }
    if (f_batch && !f_silent)
      V_printf ("%s\n", dst);
    if (dst[0] == '^' && dst[1] == 'Z' && dst[2] == '\0')
      return (FALSE);
    else
      return (TRUE);
  }

int gt_val (prompt, l)
char	*prompt;
long	*l;
  {
    *l = 0l;
    while (gt_line (buf, prompt, 0L, 0L, '\0', NULL))
      {
    	if (!decod (buf, l))
    	  return (TRUE);
      }
    return (FALSE);
  }
      
/* display value, using current settings (result is in static area) */

char *pr_val (addr, cur)
long	addr;
int	cur;		/* 1 = use current, 0 = use previous */
  {
    static char dst [64];
    char	*cp;
    long	val;
    int		i;
#   define getbyte(addr)  BYTEVAL((cur) ? get_value (addr) : gv_file (addr))

    last_value = 0;
    if (cur_printmode == ASCII)
      {
    	cp = dst;
    	for (i=0; i<cur_type; i++)
    	  {
	    val = getbyte (addr);
            addr++;
	    if (val >= ' ' && val < 0177 && val != '\\')
	      *cp++ = val;
	    else
	      {
		*cp++ = '\\';
		switch ((int)BYTEVAL(val))
		  {
		case '\b':	*cp++ = 'b';
				break;
		case '\n':	*cp++ = 'n';
				break;
		case '\t':	*cp++ = 't';
				break;
		case '\f':	*cp++ = 'f';
				break;
		case '\r':	*cp++ = 'r';
				break;
		case '\\':	*cp++ = '\\';
				break;
		default:	V_sprintf (cp, "%o", val);
				while (*cp) cp++;
				break;
		  }
	      }
	    *cp++ = ' ';
	  }
	*cp = '\0';
      }
    else
      {
	val = 0l;
	switch (cur_type)
	  {
	case BYTE:
            val = getbyte (addr);
	    break;
        case WORD:
	    if (swab) {
	      val =              getbyte (addr+1L);
	      val = (val << 8) | getbyte (addr  );
	    }
	    else {
	      val =              getbyte (addr  );
	      val = (val << 8) | getbyte (addr+1L);
	    }
	    break;
	case LWORD:
	    if (swab) {
	      val =              getbyte (addr+3L);
	      val = (val << 8) | getbyte (addr+2L);
	      val = (val << 8) | getbyte (addr+1L);
	      val = (val << 8) | getbyte (addr  );
	    }
	    else {
	      val =              getbyte (addr  );
	      val = (val << 8) | getbyte (addr+1L);
	      val = (val << 8) | getbyte (addr+2L);
	      val = (val << 8) | getbyte (addr+3L);
	    }
            break;
	  }
	if ((last_value = val) != 0 || cur_printmode != OCTAL)
	  V_sprintf (dst, deffmt[cur_printmode], val);
	else
	  (void) strcpy (dst, "0");
      }

    return (dst);
  }

zap (fname)
char	*fname;

  {
    long	base;			/* base of patching sequence	*/
    long	offset;			/* offset from base		*/
    long	val;			/* holding variable for values	*/
    int		i;			/* scratch			*/
    char	chr;			/* scratch			*/
    int		check;			/* checksum value		*/
    int		need_head;		/* header toggle		*/
    int		checkwrite = TRUE;	/* check for write access	*/
    int		goon = TRUE;		/* until ^Y is used */
    static char *fmt [] = {
            	"0%05lo  0%05lo%c %-7s ",
            	"%6ld  %6ld%c %-7s ",
            	"x%05ld  x%05lx%c %-7s ",
            	"0%05lo  0%05lo%c %-7s " };

    /* open file */

#ifdef MSDOS
    if ((zf = fopen (fname, (f_write) ? "rb+" : "rb")) == NULL)
#else
    if ((zf = fopen (fname, (f_write) ? "r+" : "r")) == NULL)
#endif
      cant (fname);

    /* set defaults and allocate table */

    cur_type = BYTE;
    cur_printmode = OCTAL;
    if (!tbl)
      tbl = (struct ntry*) calloc ((unsigned)tbl_max, sizeof (struct ntry));
    if (!tbl)
      error ("no room for table");
    tbl_cur = tbl_free = tbl;
    prevcnt = 0;		/* reset previous location table */

    /* loop 1 : loop on Base values */

    while (goon && gt_val ("Base ?    ", &base))
      {
    	/* loop 2 : loop on offset values */

    	while (goon && gt_val ("Offset ?  ", &offset))
    	  {
    	    need_head = TRUE;

    	    /* loop 3 : loop on patch commands */

    	    while (goon)
    	      {
    	        if (need_head && !f_silent)
    	          V_printf ("Base    Offset  Value   New\n");
    		need_head = FALSE;

            	if (!gt_line (buf, fmt[cur_printmode], base, offset,
    			      dp_type[cur_type], pr_val (base+offset, TRUE)))
    	          break;

                switch (buf[0])
                  {
    		case '\0':
    			/* close current, advance and open new location */
               		offset += cur_type;
    			break;
    		case '/':
    			/* re-open current using new type */
    			cur_type = WORD;
    			break;
                case '\\':
    			/* re-open current using new type */
    			cur_type = BYTE;
    			break;
    		case '|':
    			/* re-open current using new type */
    			cur_type = LWORD;
    			break;
    		case '^':
			if (buf[1] == '\0')
			  {
			    /* close current, backup and open new location */
			    offset -= cur_type;
			    break;
			  }
			if (buf[1] != 'Y' || buf[2] != '\0')
			  break;
			/* FALL THROUGH */
		case '\031':	/* ^Y */
			goon = FALSE;
			break;
                case '>':
                        /* goto new location */
                        if (!decod (&buf[1], &val))
                          {
			    push_loc (base+offset);
                            offset = buf[1] ? val : last_value;
                          }
                        break;
                case '<':
                        /* goto location */
                        if (buf[1] == '\0' && prevcnt > 0)
			  offset = pop_loc () - base;
                        break;
    		case ';':
    			/* change current display mode ... */
                        chr = buf[1];
    			if (isupper (chr))
                          chr = tolower (chr);
    			if (chr == 'o')
                          cur_printmode = OCTAL;
    			else
    			if (chr == 'd')
                          cur_printmode = DECIMAL;
    			else
    			if (chr == 'x')
                          cur_printmode = HEX;
    			else
    			/* ... or store ascii bytes ... */
    			if (chr == 'a')
                          {
                            cur_printmode = ASCII;
                            for (i=2; chr=buf[i]; i++)
                              {
				if (checkwrite && !f_write)
				  {
				    need_head = TRUE;
				    checkwrite = FALSE;
				    remark ("no write access", 0L);
				  }
                                put_byte (base+offset, chr);
                                offset++;
                              }
                          }
    			else
    			/* ... or print modifications ... */
    			if (chr == 'v')
    			  {
                            verify ();
                            need_head = TRUE;
                          }
                        else
    			/* ... or search values */
                        if (chr == 's')
                          {
                            search ();
                            need_head = TRUE;
                          }
 
    			break;
                default:
    			if ((i = decod (buf, &val)) <= 0)
    			  {
			    if (checkwrite && !f_write)
			      {
				need_head = TRUE;
				checkwrite = FALSE;
				remark ("no write access", 0L);
			      }
    			    put_value (base+offset, val);
    			    if (!i)
    			      offset += cur_type;
    			    else
                              offset -= cur_type;
    			  }
                  }
    		/* loop on patch commands */
              }
    	    /* loop on offset values */
          }
        /* loop on base values */
      }

    /* compute checksum, if requested */

    if (f_check || f_sum)
      {
        check = 0;
        for (tbl_cur = tbl; tbl_cur != tbl_free; tbl_cur++)
          check ^= (BYTEVAL(tbl_cur->val) | ((tbl_cur->old << 8) & 0xff00));
        if (f_sum)
	  {
	    V_printf ("Checksum = ");
	    V_printf (deffmt[cur_printmode], check);
	    V_printf ("\n");
	  }
      }

    /* apply patches, after checksum verification */

    tbl_cur = tbl;

    if (f_write)
      {
    	/* verify checksum */

        if (f_check)
    	  while (gt_val ("Checksum ? ", &val))
            if (val == check || f_batch)
              break;

    	if (!(f_check && val != check))
          for (tbl_cur = tbl; tbl_cur != tbl_free; tbl_cur++)
       	    ptv_file (tbl_cur->addr, tbl_cur->val);
      }
    
    if (tbl_cur != tbl_free)
      error ("no modifications made");

    if (!f_silent && f_write && tbl == tbl_free)
      remark ("no modifications requested", 0L);

    /* close file and exit */

    (void) fclose (zf);
  }

cant (s)
char *s;
  {
    V_fprintf (stderr, "%s: cannot open %s\n", my_name, s);
    exit (1);
  }

remark (s, a)
char *s;
long a;
  {
    V_fprintf (stderr, "%s: ", my_name);
    V_fprintf (stderr, s, a);
    V_fprintf (stderr, "\n");
  }

error (s)
char *s;
  {
    V_fprintf (stderr, "%s: %s\n", my_name, s);
    exit (1);
  }

swabcheck ()
  {
    union {
      short s;
      char a[2];
    } u;
    u.s = 0x1357;
#ifdef SWAB
#if SWAB
    if (!(u.a[0] == 0x57 && u.a[1] == 0x13))
      error ("please recompile with \"-DSWAB=0\"");
#else
    if (!(u.a[0] == 0x13 && u.a[1] == 0x57))
      error ("please recompile with \"-DSWAB=1\"");
#endif
#else
    swab = (u.a[0] == 0x57 && u.a[1] == 0x13);
#endif
#ifdef lint
    SCCS_id[0] = cprght[0] = '\0';
#endif
  }
SHAR_EOF
if test 24693 -ne "`wc -c < 'zap.c'`"
then
	echo shar: "error transmitting 'zap.c'" '(should have been 24693 characters)'
fi
fi
if test -f 'zap.1'
then
	echo shar: "will not over-write existing file 'zap.1'"
else
cat << \SHAR_EOF > 'zap.1'
.TH ZAP 1
.ad b
.SH NAME
zap \- binary inspect or modify files
.PP
.SH SYNOPSIS
.PP
.B zap
.\" single character options
.RB [ \-cdrsvw ]
.\" options with a value
.\" name arguments
file
.SH VERSION INFO
.PP
@(#)@ ZAP 1.9 - 87/11/04
.br
Written by Johan Vromans at Multihouse Research, Gouda, the Netherlands.
.br
Copyright 1987 Johan Vromans.
.br
Distribution free as long as you give credit to the original author.
.br
.Military use and explicit resale prohibited.
.br
Usage of this program is always at your own risk.
.SH DESCRIPTION
.PP
.I Zap\^
can be used to inspect and/or modify files. It regards the file as a
sequence of bytes, words (2 bytes) or longwords (4 bytes). Changes are
buffered, and only applied to the file upon normal completion. Only
real changes are considered a modification, e.g. a change of 1 to 1 is
a no-operation, and a change of 1 to 2 and then again to 1 discards
the modification.
.PP
Input may be redirected from a file for batch-mode patching.
.PP
.I Zap\^
regards locations in a file to be an 
.I offset
to a 
.IR base .
After invokation, 
.I zap
asks for the base value. When end-of-file is issued, the program
terminates. After the base value has been entered, 
.I zap
asks for the offset value. End-of-file makes it go back to the "Base"
prompt. After the offset value is entered,
.I zap
displays 
the offset, base and contents of the current location, and
waits for commands to execute. 
Typing end-of-file to the command prompt makes
.I zap
go back to the "Offset" question.
.PP
.I Zap
operates in one of three modes: byte, word or longword. Words and
longwords need not be aligned. The current mode is identified by a 
"\e" for byte, "/" for word and "|" for longword mode.
.br
The contents of a location are displayed in one of four formats: octal,
decimal, hexadecimal or ascii. See the commands how to change this format.
In ascii format, some interpretation is made to show special control
characters.
.PP
An example of 
.IR zap 's
output (user input in bold):
.sp
.in +.5i
.nf
Base ?    \fB0100\fP
Offset ?  \fB0200\fP
Base    Offset  Value   New
000100  000200\e 0130    \fB/\fP
000100  000200/ 054117  
000100  000202/ 045200  
000100  000204/ 063004  \fB^Z\fP
Offset ?  \fB^Z\fP
Base ?    \fB^Z\fP
.in
.fi
.PP
Each command is terminated by a <newline>. Valid commands are:
.PP
.I
Changing mode
.sp
.TP 7
.B \e
change to byte mode.
.TP
.B /
change to word (2-byte) mode.
.TP
.B |
change to longword (4-byte) mode.
.PP
.I
Changing display format
.TP 7
.B ;a
change display format to ascii.
In ascii format, the base and offset values are displayed in octal.
.TP
.B ;d
change display format to decimal.
.TP
.B ;o
change display format to octal.
.TP
.B ;x
change display format to hexadecimal.
.PP
.I
Moving around
.TP 7
<empty>
an empty line advances to the next location.
.TP
.B ^
a caret backs up to the previous location.
.TP
.BI > nnn
moves 
.I offset
to the specified location. If
.I nnn
is omited, the contents of the current location are used.
The current location is saved in the location table.
Up to 256 saved locations can be restored in a last-in
first-out manner. 
.TP
.B <
move back to the most recently saved location.
.PP
.I
Modifying contents
.TP 7
.I nnn
the contents of the current location are set to 
.IR nnn .
This can be an octal, decimal or a hexadecimal number in the form
.I nnn
(decimal),
.BI 0 nnn
(octal),
.BI 0x nnn
(hex).
The offset is advanced to the next location.
.TP
.IB nnn ^
the contents of the current location are set to 
.I nnn
as described above.
The offset is backed up to the previous location.
.TP
.BI ;a abc
change display format to ascii, and store the ascii characters
.I abc
starting from the current location. Any number of caracters can be
stored this way. The current offset is advanced to the next location.
.PP
.I
Miscellaneous commands
.TP 7
.B ;v
verify - prints a list of pending modifications. 
.TP
.B ;s
search - asks for a search value and boundaries, and then searches
for the specfied value. The locations where it is found are printed, and
also stored in the location table.
.br
The search applies to the file contents only, any pending modifications are
ignored during the search.
.br
The argument to the search can be supplied numerically (decimal, octal or
hex), or in the format
.BI ;a pqr
which causes it to be interpreted as an ascii search argument. The number
of characters allowed depends on the current mode of operation: 1 for byte
mode, 2 for word mode, and 4 for longword mode.
.br
A search can be interrupted using the terminal interrupt signal.
Note that the current mode controls the search. If zap is in byte mode, the
search is for a byte, and so on. When searching for words or longwords,
word boundaries are ignored.
.br
During the search, a "." is displayed for each 1024 bytes processed. This
can be suppressed with the 
.B \-s
command line option.
.TP
.B ^Y
(caret-uppercase-Z or control/Y) terminates the 
.I zap
loop without asking for new offset/base values.
.TP
.B ^Z
(caret-uppercase-Z) can be used as end-of-file signal. It's main use is in
batch files.
.PP
The following options may be given (in any order)
before the file-name argument:
.PP
.TP 7
.B \-c
calculates a 16-bit checksum involving all modifications. The order in
which the modifications are made is not important. 
.I Zap
requests a checksum value to be entered upon completion, and
requires this value to match the checksum. If they differ, no
modifications are made.
.TP
.B \-d
calculates the checksum and print its value upon completion.
.TP
.B \-r
access the 
.I file
for inspection only. This is the default.
.TP
.B \-s
work silently. No prompts and remarks are displayed. This can be used
for batch-like processing, if input has been re-directed from a file.
.TP
.B \-v
Supply informational messages.
.TP
.B \-w
access the
.I file
in a mode which allows modification.
.SH DIAGNOSTICS
.TP 7
no write access
a modification is made, and the
.B \-w
option was not supplied. This message is showed only once.
.TP
no modifications made
the file has not been modified, either because the
.B \-w
option was missing, or the requested checksum did not match.
This situation is considered an error.
.TP
no modifications requested
the file was accessed using the
.B \-w
option, but no changes were pending. This is an informational message.
.TP
input error
an invalid input format was supplied to a numeric prompt. The question is
repeated.
.TP
start > end
(warning) the end value for a search exceeded the starting position. The
search is not executed.
.TP
EOF > end, truncated
(warning) the end value for a search exceeded the end-of-file. The end-of-file value
is used.
.TP
you may recompile with "-DSWAB=\fIX\fP"
(informational)
.I zap\^
found out that your system swabs bytes (\fIX\fP = 1) or not (\fX\fP = 0).
You may use this in a subsequent compilation.
.TP
please recompile with "-DSWAB=\fIX\fP"
(fatal)
.I zap\^
found out that your system swabs bytes (\fIX\fP = 1) or not (\fX\fP = 0),
but the opposite was specified during compilation. You will have to
recompile with the correct value.
.PP
messages from reading/writing/positioning the file.
.SH BUGS
.PP
Usage of this program is at your own risk. Use 
.IR cmp (1)
to verify any changes.
.SH AUTHORS
.PP
Johan Vromans - Multihouse Research.
.sp
USENET: jv at mh.nl via european backbone mcvax.
.PP
There may be some resemblance with an RT-11 program called SIPP.
SHAR_EOF
if test 7418 -ne "`wc -c < 'zap.1'`"
then
	echo shar: "error transmitting 'zap.1'" '(should have been 7418 characters)'
fi
fi
if test -f 'Makefile'
then
	echo shar: "will not over-write existing file 'Makefile'"
else
cat << \SHAR_EOF > 'Makefile'
# Makefile for zap

# Define SWAB to 1 if your machine swabs bytes, like a vax, otherwise use
# the value 0.
# A number of systems types are already known to zap, like vax, pdp11
# and intel processors.
# If you do this wrong, zap will abort with an appropriate message.
# Define SWAB=2 if you want to force zap to find out itself, running
# 'zap -i' will give you advise.

#SWAB	=		# unknown (uses built-ins if possible, or find out)
#SWAB	= -DSWAB=0	# normal system
#SWAB	= -DSWAB=1	# vax
#SWAB	= -DSWAB=2	# unknown (overrides built-ins)

CFLAGS	= -O -s

zap:	zap.c
	$(CC) $(CFLAGS) $(SWAB) zap.c -o zap

# change these for your site

DESTDIR	= /usr/local/bin
DSTOWN	= bin
DSTGRP	= bin
MANDIR	= /usr/local/man/man1

install: zap
	cp zap $(DESTDIR)/zap
	chmod 0755 $(DESTDIR)/zap
	chgrp $(DSTGRP) $(DESTDIR)/zap
	chown $(DSTOWN) $(DESTDIR)/zap
	cp zap.1 $(MANDIR)/zap.1
	chmod 0644 $(MANDIR)/zap.1
	chgrp $(DSTGRP) $(MANDIR)/zap.1
	chown $(DSTOWN) $(MANDIR)/zap.1

SOURCES	= Read.Me zap.c zap.1 Makefile mkzap.com mkzap.bat
SHAR	= zap.shar

zap.shar:
	shar -c $(SOURCES) > $(SHAR)

clean:
	rm -f $(SOURCES) $(SHAR) zap zap.o a.out core
SHAR_EOF
if test 1137 -ne "`wc -c < 'Makefile'`"
then
	echo shar: "error transmitting 'Makefile'" '(should have been 1137 characters)'
fi
fi
if test -f 'mkzap.com'
then
	echo shar: "will not over-write existing file 'mkzap.com'"
else
cat << \SHAR_EOF > 'mkzap.com'
$! compile & link zap for VAX/VMS
$!
$ cc /define=(SWAB=2) zap.c
$ link zap,sys$library:vaxcrtl/lib
$!
$! invoke by symbol
$!
$ zap :== $'f$environment("default")'zap.exe
$!
SHAR_EOF
if test 174 -ne "`wc -c < 'mkzap.com'`"
then
	echo shar: "error transmitting 'mkzap.com'" '(should have been 174 characters)'
fi
fi
if test -f 'mkzap.bat'
then
	echo shar: "will not over-write existing file 'mkzap.bat'"
else
cat << \SHAR_EOF > 'mkzap.bat'
rem compile & link zap for ms-dos

cl -DLINT_ARGS zap.c
SHAR_EOF
if test 56 -ne "`wc -c < 'mkzap.bat'`"
then
	echo shar: "error transmitting 'mkzap.bat'" '(should have been 56 characters)'
fi
fi
exit 0
#	End of shell archive
exit
-- 
For comp.sources.unix stuff, mail to sources at uunet.uu.net.



More information about the Comp.sources.unix mailing list