rsalz@bbn.com (Rich Salz) (02/11/88)
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@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@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@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@uunet.uu.net.