[comp.sources.misc] v13i077: File Modifier

penneyj@servio.UUCP (D. Jason Penney) (07/03/90)

Posting-number: Volume 13, Issue 77
Submitted-by: penneyj@servio.UUCP (D. Jason Penney)
Archive-name: fm-2.0/part01

Enclosed is a major rewrite to fm, which appeared recently in comp.sources.misc.

[Attention, moderator:  this rewrite was done as a collaboration with the
original author, and appears with his blessing!]

Not only have a few minor bugs been patched, but it now runs on BSD Unix boxes
(such as SunOS), and it has even been ported to MS-DOS, where it requires
pc-curses, available at your nearest archive site.

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 1 (of 1)."
# Contents:  MANIFEST Makefile Readme fm.1 fm.c patchlevel.h vms.hc
# Wrapped by penneyj@denim on Wed Jun 27 19:22:52 1990
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'MANIFEST' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'MANIFEST'\"
else
echo shar: Extracting \"'MANIFEST'\" \(331 characters\)
sed "s/^X//" >'MANIFEST' <<'END_OF_FILE'
X   File Name		Archive #	Description
X-----------------------------------------------------------
X MANIFEST                   1	This shipping list
X Makefile                   1	
X Readme                     1	
X fm.1                       1	
X fm.c                       1	
X patchlevel.h               1	
X vms.hc                     1	
END_OF_FILE
if test 331 -ne `wc -c <'MANIFEST'`; then
    echo shar: \"'MANIFEST'\" unpacked with wrong size!
fi
# end of 'MANIFEST'
fi
if test -f 'Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Makefile'\"
else
echo shar: Extracting \"'Makefile'\" \(2271 characters\)
sed "s/^X//" >'Makefile' <<'END_OF_FILE'
X#		fm   --   file modifier
X#             Copyright (c) Tony Field 1990
X#
X
X#	fm works on the following systems:
X#	Xenix 2.3.2 and AT&T Unix 3.2.2.
X#	SunOS 4.0.3, SPARCstation-1 (others are probably trivial)
X#	IBM XT, MS-DOS 3.1, Microsoft C 5.0
X#	DECstation-2100/3100, Ultrix
X#	Sony News workstation, NEWS-OS (BSD 4.2)
X#
X#	other systems have not been tried....
X#
X#	select the compiler/linker options for your system
X#	If you are using a unix-type of system, make fm with:
X#
X#		make unix
X#
X#---------------------------------------------
X# AT&T Unix 3.2.2 - terminfo curses
X# LDFLAGS= -lcurses
X# CCFLAGS= -O -DSYSV_CURSES
X
X#---------------------------------------------
X# Xenix setup:  depending on how the development package was installed,
X#               you may choose to include or remove -DM_TERMINFO
X# 		and -DM_TERMCAP from the following....
X
X#---------------------------------------------
X# Xenix 2.3.2 - terminfo curses
X# LDFLAGS = -ltinfo
X# CCFLAGS = -O -DSYSV_CURSES -DM_TERMINFO
X
X#---------------------------------------------
X# SunOs or Xenix 2.3.2 - termcap curses
XO = .o
XE = 
XLDFLAGS = -lcurses -ltermcap
XCCFLAGS = -O
X
X#CCFLAGS = -g
X
X#---------------------------------------------
X# MS-DOS with Microsoft C 5.0 and pc-curses, available from uunet
X# Note 1: you will have to change the link step below as well as
X#         the flags in this section.
X# Note 2: Huge model is definitely overkill, but I have no patience
X#         with 8086 memory models...
X#CCFLAGS	= /AH /FPi /J /W3 /I. /Ox /Gs
X##CCFLAGS	= /AH /FPi /J /W3 /I. /Zi
X#LD = link
X## Make sure the memory model of pc-curses agrees with the one chosen above!
X#LDFLAGS = lcurses.lib/stack:50000
X##LDFLAGS = /CO /stack:50000
X
X#---------------------------------------------
X# Other unix systems:  the following may/not work for other unix systems
X#		       using termcap curses:
X# LDFLAGS = -lcurses
X# CCFLAGS = -O
X
XSOURCES = Readme Makefile fm.1 fm.c patchlevel.h vms.hc
X
XOBJECTS=fm$(O)
X
Xall:	fm$(E)
X
Xfm$(O):	fm.c
X
Xfm$(E):	fm$(O)
X# for Unix:
X	cc fm$(O) -o fm$(E) $(LDFLAGS)
X# for DOS
X#	link fm$(O),fm$(E),fm/map,$(LDFLAGS)
X
Xunix:
X	$(CC) $(CCFLAGS) fm.c $(LDFLAGS) -o fm
X
Xlint:
X	lint $(LINTFLAGS) fm.c $(LIBS) >fm.lint
X
Xshar:
X	makekit -oMANIFEST -nfm -s64k $(SOURCES)
X
Xtar:
X	tar -cvAf fm.tar $(SOURCES)
END_OF_FILE
if test 2271 -ne `wc -c <'Makefile'`; then
    echo shar: \"'Makefile'\" unpacked with wrong size!
fi
# end of 'Makefile'
fi
if test -f 'Readme' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Readme'\"
else
echo shar: Extracting \"'Readme'\" \(5768 characters\)
sed "s/^X//" >'Readme' <<'END_OF_FILE'
X                                 FM 2.0
X		      Copyright (c) Tony Field 1990
X
XThis is a major revision of "fm", a binary file modifier.
X
XFM is a curses-based hex/ascii file editor that allows binary file
Xor device editing.  Screen display format is similar to that of "hd".
X
XThe file/device may be edited in either hex or ascii modes.  Limited
Xfile searching (simple strings - not regular expressions) is allowed.
X
XAs well as some minor bug fixes, fm has now been extensively ported to other
Xsystems.  It has been known to run on the following platforms:
X
XAT&T unix 3.2.2 and Xenix 2.3.2   (System V / 386)
XSunOS 4.0.3 (Sun-3, Sun-4, Sun-386i)
XUltrix 3.1 (DECstation 3100/2100)
XNews-OS (Sony workstation, OS is a variant of BSD 4.2)
XMS-DOS (requires Microsoft C 5.0 and pc-curses, the public-domain
X        curses package)
X
XFM was originally written by Tony Field (uunet!ajfcal!tony).  This release
Xis from D. Jason Penney (penney@slc.com), with Tony's blessing and cooperation.
X
XRemember, you get what you pay for.  The authors are not liable for any bugs
Xor consequential damages arising from the use of this software.  However, they
Xmay fix bugs if they are asked nicely.
X
X-------------------------------------------------------------------------------
X                        *** NO WARRANTY ***
X
XBECAUSE THIS PROGRAM IS LICENSED FREE OF CHARGE, THE AUTHORS PROVIDE ABSOLUTELY
XNO WARRANTY, TO THE EXTENT PERMITTED BY APPLICABLE STATE LAW.  EXCEPT WHEN
XOTHERWISE STATED IN WRITING, THE AUTHORS AND/OR OTHER PARTIES PROVIDE THIS
XPROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED,
XINCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
XFITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND
XPERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE,
XYOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
X
XIN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW WILL THE AUTHORS AND/OR ANY
XOTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THIS PROGRAM BE LIABLE TO YOU
XFOR DAMAGES, INCLUDING ANY LOST PROFITS, LOST MONIES, OR OTHER SPECIAL,
XINCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE
X(INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE
XOR LOSSES SUSTAINED BY THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE
XWITH ANY OTHER PROGRAMS) THIS PROGRAM, EVEN IF YOU HAVE BEEN ADVISED OF THE
XPOSSIBILITY OF SUCH DAMAGES, OR FOR ANY CLAIM BY ANY OTHER PARTY.
X
X-------------------------------------------------------------------------------
X		   *** GENERAL PUBLIC LICENSE TO COPY ***
X
X1. You may copy and distribute verbatim copies of this program as you receive
Xit, in any medium, provided that you conspicuously and appropriately publish on
Xeach copy a valid copyright notice,
X
X		      "Copyright (c) Tony Field 1990"
X
Xand include following the copyright notice a verbatim copy of the above
Xdisclaimer of warranty and of this License.  You may charge a distribution fee
Xfor the physical act of transferring a copy.
X
X2. You may modify your copy or copies of this source file or any portion of it,
Xand copy and distribute such modifications under the terms of Paragraph 1 above,
Xprovided that you also do the following:
X
X    a) cause the modified files to carry prominent notices stating that you
X       changed the files and the date of any change, and
X
X    b) cause the whole of any work that you distribute or publish, that in
X       whole or in part contains or is a derivative of this program or any part
X       thereof, to be licensed at no charge to all third parties on terms
X       identical to those contained in this License Agreement (except that you
X       may choose to grant more extensive warranty protection to third parties,
X       at your option).
X
X    c) You may charge a distribution fee for the physical act of transferring a
X       copy, and you may at your option offer warranty protection in exchange
X       for a fee.
X
X3. You may copy and distribute this program or any portion of it in compiled,
Xexecutable or object code form under the terms of Paragraphs 1 and 2 above
Xprovided that you do the following:
X
X    a) cause each such copy to be accompanied by the corresponding
X       machine-readable source code, which must be distributed under the terms
X       of Paragraphs 1 and 2 above, or
X
X    b) cause each such copy to be accompanied by a written offer, with no time
X       limit, to give any third party free (except possibly for a nominal
X       shipping charge) a machine readable copy of the corresponding source
X       code, to be distributed under the terms of Paragraphs 1 and 2 above, or
X
X    c) in the case of a recipient of this program in compiled, executable or
X       object code form (without the corresponding source code), you shall 
X       cause copies you distribute to be accompanied by a copy of the written 
X       offer of source code which you received along with the copy you received.
X
X4. You may not copy, sublicense, distribute or transfer this program except as
Xexpressly provided under this License Agreement.  Any attempt otherwise to copy,
Xsublicense, distribute or transfer this program is void and your rights to use
Xthe program under this License agreement shall be automatically terminated.
XHowever, parties who have received computer software programs from you with 
Xthis License Agreement will not have their licenses terminated so long as such
Xparties remain in full compliance.
X
X-------------------------------------------------------------------------------
XIn other words, you are welcome to use, share and improve this program.  You 
Xare forbiden to forbid anyone else to use, share and improve what you 
Xgive them. Help stamp out software-hoarding!
END_OF_FILE
if test 5768 -ne `wc -c <'Readme'`; then
    echo shar: \"'Readme'\" unpacked with wrong size!
fi
# end of 'Readme'
fi
if test -f 'fm.1' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'fm.1'\"
else
echo shar: Extracting \"'fm.1'\" \(2721 characters\)
sed "s/^X//" >'fm.1' <<'END_OF_FILE'
X.\" Copyright (c) Tony Field 1990
X.TH FM 1 ""
X.SH NAME
Xfm \- curses-based hex file modifier/editor
X
X.SH SYNOPSIS
X.nf
X
Xfm file
X
X   where    file = file to be examined/modified
X.fi
X
X.SH DESCRIPTION
X
XThe fm utility allows direct examination and modification of files - be they 
Xtext, binary, or device files.  The modifications may be performed either in
Xhex or ascii mode.  
XThe user will be interrogated before changes are written back to the file.
XThe file is immediately updated with the changes if the response to the "apply
Xchanges" question is a 'y'.
X
XThe screen presentation is similar to the output format of hd.
X
XText searching may be specifed as ascii or hex strings.  Regular expression
Xsearches are not provided.
X
XA file may be examined with (for example):
X
X.nf
X      fm my.file
X.fi
X
XA device may be examined with (for example):
X
X.nf
X      fm /dev/fd096ds15
X.fi
X
XIf the file/device cannot be opened in read/write mode, then fm will default
Xinto a read-only mode.
X
XThe screen operation will use the cursor pad if available (i.e., if your termcap
Xdefines these operation and curses supports KEY_UP, KEY_DOWN, KEY_LEFT and
XKEY_RIGHT).  Control key sequences are provided to select various fm options.
X
XIf your termcap describes functions keys and if curses supports KEY_NPAGE,
XKEY_PPAGE, KEY_F(0) through KEY_F(10), the options may be selected with
Xappropriate function keys.
X
XText searches may be performed on ascii or hex strings.
XThe form of text search is determined by whether you are currently
Xpointing into the ascii or hex portion of the file.
X
XWhen searching for a hex string, the pattern is represented as normal
Xcharacters with or without blank separation:
X
X.nf
X     6b 36 fe 69a3
X.fi
X
XA search string may be repeated if the empty string is provided for the 
Xsearch string.
X
XThe current screen contents (with an optional comment) may be printed to a file
Xwith the "\^^P" command. If the specified print file does not exist it is
Xcreated; if it does exist, the screen print image is appended to the end of
Xthe file.
X
XHere are the keyboard bindings for fm commands:
X
X.nf
X    ^F / PgDn    (Forward) next 256 byte block from file
X    ^B / PgUp    (Back) previous 256 byte block from file
X
X    ^J / <down>  cursor down
X    ^K / <up>    cursor up
X    ^H / <left>  cursor left
X    ^M / <right> cursor right
X
X    ^A / F1      toggle selection of ascii/hex editing
X    ^X / F2      search for string
X    ^U / F3      Undo current screen changes
X    ^P / F4      Print screen to file
X    ^G / F5      Goto byte location in file
X    ^D / F6      Done (quit)
X
X    ^L           redraw screen
X.fi
X
X.SH SEE ALSO
Xhd,termcap,curses
X
X.SH AUTHORS
XD. Jason Penney	   (penney@slc.com)
XTony Field.        (uunet!ajfcal!tony)
END_OF_FILE
if test 2721 -ne `wc -c <'fm.1'`; then
    echo shar: \"'fm.1'\" unpacked with wrong size!
fi
# end of 'fm.1'
fi
if test -f 'fm.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'fm.c'\"
else
echo shar: Extracting \"'fm.c'\" \(35764 characters\)
sed "s/^X//" >'fm.c' <<'END_OF_FILE'
X/**********************************************************************
X				f m . c
X		      Copyright (c) Tony Field 1990
X
XPlease examine the associated Readme file for details on the lack of
Xwarranty and explicit licensing information.
X
XNo responsibility is taken for any errors on inaccuracies inherent either to
Xthe comments or the code of this program, but, if reported to me, an attempt
Xwill be made to fix them.
X
X  Authors:  Tony Field      (uunet!ajfcal!tony)
X            D. Jason Penney (penneyj@slc.com)
X
X------------------------------
X  - curses-based binary file modifier
X  - default is BSD curses.
X  - compile with -DSYSV_CURSES for ATT termcap curses.
X  - seems to work on files, devices, and directories(read only)
X  - developed for AT&T Unix 3.2.2 and  Xenix 2.3.2  (System V/386)
X
X  - Major hacks by D. Jason Penney (penneyj@slc.com) to run under SunOS,
X    Ultrix, MS-DOS, NEWS-OS, and VAX/VMS, June 1990.  
X
XSome warnings for people attempting to port this code to other hosts:
X
X1. VAX/VMS curses doesn't have mvwprintw().  Use wmove() followed by wprintw()
X   instead.  I considered implementing mvwprintw() within this module, but
X   that would just open up another (varargs) portability problem...
X
X2. BSD 4.2 systems (such as Sony NEWS) do not have #elif preprocessor directive.
X   Use nested #if/#else/#endif constructs instead.
X
X3. Most curses implementations don't have beep().  It is trivially implemented
X   here.  Enable the implementation if you need it.
X
X4. A number of characters do NOT come through well on some hosts, raw mode
X   notwithstanding:  ^S/^Q are often used for flow-control (even if stdin
X   is in raw mode, the user might have flow control enabled in some
X   intermediate machine).  ^C/^Y should be avoided because they are often
X   used for panic (SIGINT) handling.  ^T is a VMS meta-character
X   that is not usually available to curses.  ^Z is a favorite command in
X   BSD unix for job control, and is hard-wired to EOF in VAX/VMS.  If you
X   decide to add commands or change the bindings of existing commands, please
X   consider the portability impact of your changes.
X**********************************************************************
X
XPatchlevels
X===========
XPatchlevel 1:	May-1990, by Tony Field
X	  1. clean up cursor motion while editing hex side of screen.
X	  2. KEY_UNDO removed for !SYS_TERMCAP compilation
X	  3. fixed file print on last 16 bytes of file
X
XPatchlevel 2:  June, 1990 by D. Jason Penney
X	  1. adjust code for portability
X	  2. detect zero-length files and exit appropriately
X	  3. fix boundary condition editing last byte of file
X
X*/
X
X/*============================================
X  System Declarations
X============================================*/
X
X#include <stdio.h>
X
X#if defined(VMS)
X#include <file.h>
X#include <stat.h>
X#else
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <fcntl.h>
X#endif
X
X#include <ctype.h>
X#include <curses.h>
X#include <errno.h>
X#include <signal.h>
X#include <string.h>
X
X/* Use ANSI C if available... */
X#if defined(MSDOS)
X#define FLG_PROTOTYPE TRUE
X#include <dos.h>
X#endif
X#if defined(VMS)
X#define FLG_PROTOTYPE TRUE
X#endif
X
X#if defined(MSDOS) /* prototypes for curses calls */
Xint	 addch(char ch);	/* put char in stdscr */
Xvoid	 attroff(int attr);	/* clear attribute(a) stdscr */
Xvoid	 attron(int attr);	/* add attribute(s) stdscr */
Xvoid	 beep(void);		/* sound bell */
Xvoid	 clear(void);		/* clear stdscr */
Xint	 clrtobot(void);	/* clear end of stdscr */
Xint	 clrtoeol(void);	/* clear end of line in stdscr */
Xvoid	 echo(void);		/* set terminal echo mode */
Xint	 endwin(void);		/* cleanup and finitialization */
Xvoid	 erase(void);		/* erase stdscr */
Xint	 initscr(void);		/* curses initialization */
Xvoid	 keypad(WINDOW *scr, int enabled);
X				/* marks a window for keypad usage */
Xint	 move(int row, int col);/* move cursor in stdscr */
Xint	 mvaddstr(int row, int col, char msg[]);
X				/* move & put string in stdscr */
Xint	 mvwgetch(WINDOW *scr, int row, int col);
X				/* move & get char to a window */
Xint	 mvprintw(int row, int col, char pat[], ...);
X				/* move & print string in stdscr */
Xint	 mvwaddstr(WINDOW *scr, int row, int col, char msg[]);
X				/* move & put string in stdscr */
Xint	 mvwprintw(WINDOW *scr,int row, int col, char pat[], ...);
X				/* move & print string in a window */
Xvoid	 nl(void);			/* set terminal cr-crlf map mode */
Xvoid	 noecho(void);		/* unset terminal echo mode */
Xvoid	 nonl(void);		/* unset terminal cr-crlf map mode */
Xvoid	 noraw(void);		/* unset raw terminal mode */
Xvoid	 raw(void);		/* set raw terminal mode */
Xvoid	 refresh(void);		/* refresh stdscr */
Xint	 resetty(void);		/* restore terminal I/O modes */
Xint	 savetty(void);		/* save terminal I/O modes */
Xint	 waddch(WINDOW *scr, char ch);
X				/* put char in a window */
Xint	 waddstr(WINDOW *scr, char *buf);
X				/* put string in a window */
Xvoid	 wattrset(WINDOW *scr, int attr);
X				/* set window char attributes */
Xint	 wclrtobot(WINDOW *scr);/* clear end of a window */
Xint	 wclrtoeol(WINDOW *scr);/* clear end of line in a window */
Xint	 wgetch(WINDOW *scr);	/* get char to a window */
Xint	 wgetstr(WINDOW *scr, char *buf);
X				/* get string to window and buffer */
Xint	 wmove(WINDOW *scr, int row, int col);	/* move cursor in a window */
Xint	 wprintw(WINDOW *scr, char *pat,...);	/* print string in a window */
Xvoid	 wrefresh(WINDOW *scr);	/* refresh screen */
X#endif
X
X/* Prototypes for System calls: */
X
X#if defined(FLG_PROTOTYPE)
Xint close(int fp);
X#else
Xint close();
X#endif
X
X#if defined(FLG_PROTOTYPE)
Xvoid exit(int code);
X#else
Xvoid exit();
X#endif
X
X#if defined(FLG_PROTOTYPE)
Xint lseek(int fp, long pos, int mode);
X#else
Xint lseek();
X#endif
X
X/* Classic irony that most important syscall is also most host dependent! */
X#if defined(FLG_PROTOTYPE)
X#if defined(MSDOS)
Xint open(char name[], int mode, int pmode);
X#else
X#if defined(VMS)
Xint open(char name[], int mode,...);
X#else
Xint open(char name[], int mode);
X#endif
X#endif
X#else
Xint open();
X#endif
X
X#if defined(FLG_PROTOTYPE)
Xint read(int fp, char *buf, int len);
X#else
Xint read();
X#endif
X
X#if defined(FLG_PROTOTYPE)
Xint write(int fp, char *buf, int len);
X#else
Xint write();
X#endif
X
Xextern int errno;
X
X/*============================================
X  Local Includes, Defines, and Types
X============================================*/
X
X#include "patchlevel.h"
X
X#if !defined(FALSE)
X#define FALSE 0
X#define TRUE 1
X#endif
Xtypedef unsigned char BoolType;
X
X/* Define your variant of curses.  If you have compilation or link problems,
X   you probably should reverse (undef/define) this flag.
X*/
X#if !defined(SYSV_CURSES)  &&  !defined(M_TERMCAP)
X#if !defined(sun) && !defined(ultrix) && !defined(sony_news) && !defined(MSDOS)
X#define SYSV_CURSES TRUE
X#endif
X#endif
X
X#define AS_CTRL(x)      ((x) - '@')
X
X#define BSIZE	256		/*	size of window onto file */
X#define MAX_RETURNED_FROM_DISK 1000 /* buffer size limit */
X#define	FIRSTL	3		/* 	first screen line */
X#define FIRSTH	6		/*	first hex column */
X#define FIRSTA	58		/*	first alpha column */
X#define PROMPTCOL 22            /*      col for prompts */
X#define STAT1   22              /*      first status line */
X#define STAT2   23              /*      second status line */
X
Xtypedef enum {asciiMode, hexMode} ViewModeEType;
X
X/* Key Bindings: */
X#define CMD_ASC		AS_CTRL('A')	/*	ascii side */
X#define	CMD_BAK		AS_CTRL('B')	/* 	previous page */
X#define	CMD_DON		AS_CTRL('D')	/*	done/quit */
X#define	CMD_FWD		AS_CTRL('F')	/*	next page */
X#define	CMD_JMP		AS_CTRL('G')	/*	goto address */
X#define CMD_LEFT        AS_CTRL('H')
X#define CMD_DOWN        AS_CTRL('J')
X#define CMD_UP          AS_CTRL('K')
X#define CMD_REDRAW      AS_CTRL('L')
X#define CMD_PRI		AS_CTRL('P')	/*	print to file */
X#define	CMD_SEARCH	AS_CTRL('N')	/*	search in current view mode */
X#define CMD_UND		AS_CTRL('U')	/*	undo current changes */
X
X/*============================================
X  Local Variables
X============================================*/
X
Xstatic char	binToHex[] = "0123456789abcdef";
Xstatic int		byte_pos; /*	current byte editing in buffer */
Xstatic BoolType	maybe_dirty;	  /*	user may have changed data */
Xstatic long	max_fpos;	  /*	end of file byte */
Xstatic ViewModeEType vmode = hexMode; /* remember hex/ascii side of screen */
X
Xstatic FILE	*printFile = NULL;/*	print file */
Xstatic BoolType writeOk;
Xstatic char	srcName[200];
X
X/*============================================
X  Local forwards and macros
X============================================*/
X
X#define IS_VALID(x) ((x) >= ' ' && (x) < 127)
X#define T_TO_A(x) (IS_VALID(x) ? (x) : '.')
X#define EXIT_SUCCESS exit(0)
X#define EXIT_ERR     exit(1)
X
X#if defined(VMS)
X#define EXTRA_REFRESH  wrefresh(stdscr)
X#define CANONICAL { echo(); noraw(); nocrmode();}
X#define NONCANONICAL { noecho(); crmode();}
X#else
X#if defined(SYSV_CURSES)
X#define EXTRA_REFRESH
X#define CANONICAL { echo(); }
X#define NONCANONICAL { noecho(); }
X#else /* default */
X#define EXTRA_REFRESH  wrefresh(stdscr)
X#define CANONICAL { echo(); noraw(); nl(); }
X#define NONCANONICAL { noecho(); raw(); nonl(); }
X#endif
X#endif
X
X/* The point behind the following is to try to make standout() give
X   us reverse video as opposed to bold, which doesn't stand out (heh, heh)
X   very well on a lot of displays...
X*/
X#if defined(SYSV_CURSES)
X#if defined(VMS)
X#define STANDOUT standout()
X#define STANDEND standend()
X#else
X#define STANDOUT wattrset(stdscr, A_STANDOUT)
X#define STANDEND wattrset(stdscr, 0)
X#endif
X#else
X#if defined(MSDOS)
X#define STANDOUT attron(A_REVERSE)
X#define STANDEND attroff(A_REVERSE)
X#else /* default */
X#define STANDOUT standout()
X#define STANDEND standend()
X#endif
X#endif
X
X/* Local forwards: */
X
X#if defined(FLG_PROTOTYPE)
Xstatic void cleanup(void);
X#else
Xstatic void cleanup();
X#endif
X
X#if defined(FLG_PROTOTYPE)
Xstatic int hexval(int c);
X#else
Xstatic int hexval();
X#endif
X
X#if defined(FLG_PROTOTYPE)
Xstatic void reflect_change(int byte, ViewModeEType vmode, char *wbuf);
X#else
Xstatic void reflect_change();
X#endif
X
X#if defined(FLG_PROTOTYPE)
Xstatic void ttox(char x[], int c);
X#else
Xstatic void ttox();
X#endif
X
X#if defined(FLG_PROTOTYPE)
Xstatic void whereis(int pos, ViewModeEType which, int *x, int *y);
X#else
Xstatic void whereis();
X#endif
X
X/*============================================
X  Host-dependent Implementations
X============================================*/
X
X/* Special routines for file access, host-dependent */
X#if defined(VMS)
X#include "vms.hc" /* see comment in vms.hc for explanation of purpose */
X#else
X/* Unix, MS-DOS Implementations */
X
X/* =======================================================================
X * Name - doLseek
X *
X * Purpose - Abstraction in front of lseek() -- checks for errors
X *
X * Arguments:  fp -- file we are seeking into
X *	       ptr -- position to which we wish to move
X *             n -- "mode" of move, as in lseek(2).  This is really just noise.
X *
X * Returns     function return -- 
X *
X *========================================================================
X */
X#if defined(FLG_PROTOTYPE)
Xstatic void doLseek(int fp, long ptr, int n)
X#else
Xstatic void doLseek(fp, ptr, n)
Xint fp;
Xlong ptr;
Xint n;
X#endif
X{
Xif (lseek(fp, ptr, n) == -1) {
X  cleanup();
X  perror("lseek() error");
X  EXIT_ERR;
X  }
X}
X
X/* =======================================================================
X * Name - doRead
X *
X * Purpose - Abstraction for file read
X *
X * Arguments:  fp -- file from which to read
X *	       fpos -- location from which to read in file
X *	       loc -- address to place result
X *             maxsize -- max number of bytes that may be read into loc
X *
X * Returns     function return -- -1 on error, otherwise success
X *
X *========================================================================
X */
Xstatic int doRead(fp, fpos, loc, maxsize)
Xint fp;
Xlong fpos;
Xchar *loc;
Xint maxsize;
X{
Xint nn;
X
XdoLseek(fp, fpos, 0);	/*	position to current byte */
Xnn = read(fp, loc, maxsize);
Xif (nn == -1) {
X  if (errno != ERANGE) {
X    cleanup();
X    perror("error in read()");
X    EXIT_ERR;
X    }
X  }
Xreturn nn;
X}
X
X/* =======================================================================
X * Name - doWrite
X *
X * Purpose - Abstraction to perform write on file
X *
X * Arguments:  fp -- file to which to write
X *             fpos -- position at which to write
X *             loc -- address of bytes to be written
X *             nbytes -- number of bytes to be written
X *
X * Returns     function return -- -1L on error, otherwise success
X *
X *========================================================================
X */
Xstatic int doWrite(fp, fpos, loc, nbytes)
Xint fp;
Xlong fpos;
Xchar *loc;
Xint nbytes;
X{
Xint result;
X
XdoLseek(fp, fpos, 0);
Xresult = write(fp, loc, nbytes);
Xif (result == -1) {
X  cleanup();
X  perror("error in write()");
X  EXIT_ERR;
X  }
Xreturn result;
X}
X
X/* =======================================================================
X * Name - doOpen
X *
X * Purpose - Abstraction to open file
X *
X * Arguments:  name -- null-terminated name of file to open
X *             readOnly -- FALSE if you are allowed to write to file
X *
X * Returns     function return -- -1L on error, otherwise success
X *
X *========================================================================
X */
Xstatic int doOpen(name, readOnly)
Xchar name[];
XBoolType readOnly;
X{
Xint fp;
X
Xif (readOnly) {
X#if defined(MSDOS)
X    fp = open(name, (int)(O_RDONLY | O_BINARY), 0);
X#else
X    fp = open(name, O_RDONLY);
X#endif
X  }
Xelse {
X#if defined(MSDOS)
X  fp = open(name, (int)(O_RDWR | O_BINARY), 0);
X#else
X  fp = open(name, O_RDWR);
X#endif
X  }
Xreturn fp;
X}
X
X/* =======================================================================
X * Name - doClose
X *
X * Purpose - Abstraction to close file (when done)
X *
X * Arguments:  fp -- file to be closed
X *
X * Returns     function return -- 
X *
X *========================================================================
X */
Xstatic void doClose(fp)
Xint fp;
X{
Xclose(fp);
X}
X#endif
X
X/*============================================
X  Implementations of Routines
X============================================*/
X
X/* =======================================================================
X * Name - cleanup
X *
X * Purpose - Restore display and keyboard to sane (cooked) mode
X *
X * Arguments:  None
X *
X * Returns     function return -- 
X *
X *========================================================================
X */
X#if defined(FLG_PROTOTYPE)
Xstatic void cleanup(void)
X#else
Xstatic void cleanup()
X#endif
X{
X  if (printFile != NULL)
X    fclose(printFile);
X  wmove(stdscr, LINES - 1, 0);
X  wclrtobot(stdscr);
X  refresh();
X  CANONICAL;
X  endwin();
X}
X
X/* =======================================================================
X * Name - freakout
X *
X * Purpose - Signal handler to trap signal and return control to shell
X *
X * Arguments:  None (whatever signal handler passes, we ignore it)
X *
X * Returns     function return -- 
X *
X *========================================================================
X */
X#if defined(FLG_PROTOTYPE)
Xstatic void freakout(void)
X#else
Xstatic void freakout()
X#endif
X{
X  cleanup();
X  EXIT_ERR;
X}
X
X/* =======================================================================
X * Name - beep
X *
X * Purpose - Ring terminal bell
X *
X * Arguments:  None
X *
X * Returns     function return -- 
X *
X * This is missing from many implementations of curses.  Note that we
X * don't use curses to write the character, because at best it would write
X * a character on the screen, and at worst it will become confused about
X * the cursor position since the bell doesn't display.
X *
X *========================================================================
X */
X#if !defined(SYSV_CURSES) || defined(VMS)
X#if defined(FLG_PROTOTYPE)
Xstatic void beep(void)
X#else
Xstatic void beep()
X#endif
X{
Xfprintf(stderr, "\007");
Xfflush(stderr);
X}
X#endif
X
X/* =======================================================================
X * Name - display
X *
X * Purpose - display a 256 byte block of file with hex on left and alpha
X *	     on right side.
X *
X * Arguments:  fpos -- current position in the file
X *	       wbuf -- working data buffer
X *	       nbytes -- size of wbuf
X *
X * Returns     function return -- 
X *
X *========================================================================
X */
X#if defined(FLG_PROTOTYPE)
Xstatic void display(long fpos, char wbuf[], int nbytes)
X#else
Xstatic void display(fpos, wbuf, nbytes)
Xlong	fpos;
Xchar	wbuf[];
Xint	nbytes;
X#endif
X{
X  int		i, j, k, m;
X  char	hbuf[2]; /*	working buf to convert byte to hex */
X
X  erase();
X  wmove(stdscr, 0, 0);
X  wprintw(stdscr, "File: %s    (0x%lx bytes) %s", srcName, max_fpos,
X      writeOk ? "" : "(read only)");
X  wmove(stdscr, 1, 0);
X  wprintw(stdscr, "Byte: 0x%-8lx ", fpos);
X
X  for (i = 0, j = FIRSTL;  i < nbytes;  i += 16, j++) {
X    /*display hex side  */
X    wmove(stdscr, j, 0);
X    wprintw(stdscr, "%02x", i);
X    wmove(stdscr, j, FIRSTH);
X    for (k = 0, m = i; k < 48  &&  m < nbytes;  k += 3, m++) {
X      ttox(hbuf, wbuf[m]);
X      waddch(stdscr, hbuf[0]);
X      waddch(stdscr, hbuf[1]);
X      waddch(stdscr, ' ');
X      }
X
X    /*	display ascii side	*/
X    wmove(stdscr, j, FIRSTA);
X    for (k = 0, m = i;  k < 16 &&  m < nbytes;  k++, m++)
X      waddch(stdscr, T_TO_A(wbuf[m]));
X    }
X  
X  /*	bottom annotation	*/
X  for (i = 0;  i < 16;  i++) {
X    wmove(stdscr, j + 1, i * 3 + FIRSTH);
X    waddch(stdscr, binToHex[i]);
X    }
X  wmove(stdscr, j + 1, FIRSTA);
X  waddstr(stdscr, "0123456789abcdef");
X  wmove(stdscr, STAT1, 0);
X#if defined(KEY_F)
X  waddstr(stdscr, 
X" ^F/pgdn=fwd  ^A/f1=ascii/hex ^H=left  ^K=up  ^N/f2=srch ^U/f3=undo ^D/f6=done"
X      );
X  wmove(stdscr, STAT2, 0);
X  waddstr(stdscr,
X" ^B/pgup=back ^D/f6=done      ^M=right ^J=dwn ^P/f4=prnt ^G/f5=goto ^L=redraw"
X      );
X#else
X  waddstr(stdscr, 
X      " ^F/pgdn=fwd  ^A=ascii/hex ^H=left  ^K=up  ^N=srch ^U=undo ^D=done");
X  wmove(stdscr, STAT2, 0);
X  waddstr(stdscr,
X      " ^B/pgup=back ^D=done      ^M=right ^J=dwn ^P=prnt ^G=goto ^L=redraw");
X#endif
X  wrefresh(stdscr);
X}
X
X/* =======================================================================
X * Name - printit
X *
X * Purpose - display a 256 byte block of file with hex on left and alpha
X *	     on right side to a print file.
X *
X * Arguments:  comment -- commentnote
X *	       fpos -- position within file
X *	       wbuf -- working data buffer
X *	       nbytes -- size of wbuf
X *
X * Returns     function return -- 
X *
X *========================================================================
X */
X#if defined(FLG_PROTOTYPE)
Xstatic void printit(char *comment, long fpos, char wbuf[], int nbytes)
X#else
Xstatic void printit(comment, fpos, wbuf, nbytes)
Xchar	*comment;
Xlong	fpos;
Xchar	wbuf[];
Xint	nbytes;
X#endif
X{
X  int		i, j, k, m;
X  char	hbuf[2]; /*	working buf to convert byte to hex */
X
X  fprintf(printFile, "File: %s    (%lx bytes) %s\nByte: %lx      %s\n", 
X      srcName, max_fpos, writeOk ? "" : "(read only)", fpos, comment);
X
X  for (i = 0, j = FIRSTL;  i < nbytes;  i += 16, j++) {
X    /*	display hex side  */
X    fprintf(printFile, "%02x    ", i);
X    for (k = 0, m = i; k < 48  &&  m < nbytes;  k += 3, m++) {
X      ttox(hbuf, wbuf[m]);
X      fputc(hbuf[0], printFile);
X      fputc(hbuf[1], printFile);
X      fputc(' ', printFile);
X      }
X
X    for (; (m % 16 != 0);  m++)
X      fprintf(printFile, "   ");
X
X    /*	display ascii side	*/
X    fprintf(printFile, "   ");
X    for (k = 0, m = i;  k < 16 &&  m < nbytes;  k++, m++)
X      fputc(T_TO_A(wbuf[m]), printFile);
X
X    fprintf(printFile, "\n");
X    }
X  /*	bottom annotation	*/
X  fprintf(printFile, "      ");	
X  for (i = 0;  i < 16;  i++)
X    fprintf(printFile, "%c  ", binToHex[i]);
X  fprintf(printFile, "   0123456789abcdef\n\n");
X}
X
X/* =======================================================================
X * Name - edit
X *
X * Purpose - allow user to modify the 256 byte block of text.
X *
X * Arguments:  wbuf -- working buffer
X *	       nbytes -- size of wbuf
X *	       fpos -- current position in file
X *
X * Returns     function return -- keyboard command if not handled locally
X *
X *========================================================================
X */
X#if defined(FLG_PROTOTYPE)
Xstatic int edit(char *wbuf, int nbytes, long fpos)
X#else
Xstatic int edit(wbuf, nbytes, fpos)
Xchar	*wbuf;
Xint	nbytes;
Xlong	fpos;
X#endif
X{
X  int	key;	/*	user entered this key	*/
X  int		x, y;	/*	screen coords.	*/
X  int		hexa, hexb;	
X  BoolType	reflected; /*	was a change really done */
X  BoolType      have_key;
X  
X  have_key = FALSE;
X  while (TRUE) {
X    if (byte_pos >= nbytes)		
X      byte_pos = nbytes - 1;
X    if (byte_pos < 0)
X      byte_pos = 0;
X	  
X    wmove(stdscr, 1, 6);
X    wprintw(stdscr, "0x%-8lx",  fpos + byte_pos);
X    whereis(byte_pos, vmode, &x, &y);
X#if !defined(SYSV_CURSES)
X    wmove(stdscr, y, x);
X#endif
X    EXTRA_REFRESH;
X    if (!have_key)
X      key = mvwgetch(stdscr, y, x);
X    have_key = FALSE;
X
X    switch (key) { /*	any user keys or function keys	*/
X      case CMD_FWD:
X#if defined(KEY_NPAGE)
X      case KEY_NPAGE:
X#endif
X	return CMD_FWD;
X	break;
X      
X      case CMD_BAK:
X#if defined(KEY_PPAGE)
X      case KEY_PPAGE:
X#endif
X	return CMD_BAK;
X	break;
X
X      case CMD_UP:
X#if defined(KEY_UP)
X      case KEY_UP:
X#endif
X	if (byte_pos - 16 >= 0)
X          byte_pos -= 16;
X        else
X          beep();
X	break;
X
X      case CMD_DOWN:
X#if defined(KEY_DOWN)
X      case KEY_DOWN:
X#endif
X	if (byte_pos + 16 < nbytes)
X          byte_pos += 16;
X        else
X          beep();
X	break;
X
X      case CMD_LEFT:
X#if defined(KEY_LEFT)
X      case KEY_LEFT:
X#endif
X	if (byte_pos - 1 >= 0)
X          byte_pos -= 1;
X        else
X          beep();
X	break;
X
X      case '\r':
X#if defined(KEY_RIGHT)
X      case KEY_RIGHT:
X#endif
X	if (byte_pos + 1 < nbytes)
X          byte_pos += 1;
X        else
X          beep();
X	break;
X
X      case CMD_ASC:
X#if defined(KEY_F)
X      case KEY_F(1):
X#endif
X        if (vmode == hexMode)
X	  vmode = asciiMode;
X        else
X	  vmode = hexMode;
X	break;
X
X      case CMD_SEARCH:
X#if defined(KEY_F)
X      case KEY_F(2):
X#endif
X	return CMD_SEARCH;
X	break;
X
X      case CMD_UND:
X#if defined(KEY_F)
X      case KEY_F(3):
X#endif
X#if defined(KEY_UNDO)
X      case KEY_UNDO:
X#endif
X	return CMD_UND;
X	break;
X
X      case CMD_REDRAW:
X        erase();
X        clear();
X        break;
X
X      case CMD_PRI:
X#if defined(KEY_F)
X      case KEY_F(4):
X#endif
X	return CMD_PRI;
X	break;
X
X      case CMD_JMP:
X#if defined(KEY_F)
X      case KEY_F(5):
X#endif
X	return CMD_JMP;
X	break;
X
X      case CMD_DON:
X#if defined(KEY_F)
X      case KEY_F(6):
X#endif
X	return CMD_DON;
X	break;
X
X      default: 
X	if (!writeOk)
X	  break;
X	if (vmode == asciiMode  &&  !IS_VALID(key))
X	{	beep();
X		break;
X	}
X	if (vmode == hexMode    &&  !isxdigit(key))
X	{	beep();
X		break;
X	}
X
X	/* user typed a key.   If in the ascii side, allow the overstrike of 
X           the ascii value If in the hex side, ensure user types valid hex 
X           digits.
X			      
X	   reflect changes on both sides of the display.
X	*/
X	reflected = FALSE;
X	if (vmode == asciiMode) {
X            STANDOUT;
X	    waddch(stdscr, (char)key);
X	    maybe_dirty = TRUE;
X	    wbuf[byte_pos] = (char)key;
X	    reflect_change(byte_pos, hexMode, wbuf);
X	    reflected = TRUE;
X	  }
X	else {
X          hexa = hexb = 0;
X          STANDOUT;			/* 1st nibble	*/
X	  waddch(stdscr, (char)key);
X          EXTRA_REFRESH;
X	  hexa = key;
X	  maybe_dirty = TRUE;
X	  key = wgetch(stdscr);	/* 2nd nibble	*/
X	  if (isxdigit(key)) {
X            waddch(stdscr, (char)key);
X	    hexb = key;
X	    }
X	  reflected = TRUE;
X	  if (hexa != 0) {
X            wbuf[byte_pos] &= 0x0f;
X	    wbuf[byte_pos] |= hexval(hexa) << 4;
X	    }
X	  if (hexb != 0) {
X            wbuf[byte_pos] &= 0x0f0;
X	    wbuf[byte_pos] |= hexval(hexb);
X	    }
X	  if (hexa != 0 || hexb != 0)
X	    reflect_change(byte_pos, asciiMode, wbuf);
X	  }
X
X	if ((key < 0  || key > 256) && key != CMD_LEFT
X#if defined(KEY_LEFT)
X	    && key != KEY_LEFT
X#endif
X	    ) /* function key ?? */
X	  have_key = TRUE;
X	else
X	if (byte_pos < BSIZE - 1  &&  (reflected || key == ' ')
X            && key != CMD_LEFT
X#if defined(KEY_LEFT)
X	    && key != KEY_LEFT
X#endif
X	    )
X	  byte_pos++;
X
X        STANDEND;
X	if (key == CMD_UND)
X	  return CMD_UND;
X#if defined(KEY_F)
X        if (key == KEY_F(5))
X	  return CMD_UND;
X#endif
X	break;
X      } /* switch */
X    } /* while */
X}
X
X/* =======================================================================
X * Name - reflect_change
X *
X * Purpose - ensure the byte is placed on the screen in the appropriate panel.
X *
X * Arguments:  position in wbuf that has changed
X *	       vmode -- HEX versus ASCII side
X *	       wbuf -- buffer with change
X *
X * Returns     function return -- 
X *
X *========================================================================
X */
X#if defined(FLG_PROTOTYPE)
Xstatic void reflect_change(int byte, ViewModeEType vmode, char *wbuf)
X#else
Xstatic void reflect_change(byte, vmode, wbuf)
Xint	byte;
XViewModeEType vmode;
Xchar	*wbuf;
X#endif
X{
X  int		x,y;
X  char	hbuf[2];
X
X  whereis(byte, vmode, &x, &y);
X  wmove(stdscr, y, x);
X  if (vmode == asciiMode)
X    waddch(stdscr, T_TO_A(wbuf[byte]));
X  else {
X    ttox(hbuf, wbuf[byte]);
X    waddch(stdscr, hbuf[0]);
X    waddch(stdscr, hbuf[1]);
X    }
X  EXTRA_REFRESH;
X}
X
X/* =======================================================================
X * Name - hexval
X *
X * Purpose - convert an ascii character into a hex nibble.
X *
X * Arguments:  c -- the char
X *
X * Returns     function return -- its equivalent as an int
X *
X *========================================================================
X */
X#if defined(FLG_PROTOTYPE)
Xstatic int hexval(int c)
X#else
Xstatic int hexval(c)
Xint	c; /*	0123456789abcdef expected */
X#endif
X{
X  if (c >= '0'  &&  c <= '9')
X    return c - '0';
X  return (tolower(c) - 'a' + 10) & 0x0f;
X}
X
X/* =======================================================================
X * Name - whereis
X *
X * Purpose - given the relative byte position of a byte within the 256 byte
X *	     buffer, compute the screen coordinates to be used.
X *
X * Arguments:  pos -- relative byte position in wbuf
X *             which -- HEX versus ASCII side of display
X *
X * Returns     function return -- 
X *	       *x -- column of byte on display
X *	       *y -- row of byte on display
X *
X *========================================================================
X */
X#if defined(FLG_PROTOTYPE)
Xstatic void whereis(int pos, ViewModeEType which, int *x, int *y)
X#else
Xstatic void whereis(pos, which, x, y)
Xint	pos;	/*	relative byte position in wbuf	*/
XViewModeEType	which;	/*	HEX or ASC side of display	*/
Xint	*x, *y;	/*	returned x,y coordinate on display	*/
X#endif
X{
X  if (which == asciiMode) {
X    *x = pos % 16 + FIRSTA;
X    *y = pos / 16 + FIRSTL;
X    }
X  else {
X    *x = (pos % 16) * 3 + FIRSTH;
X    *y = pos / 16 + FIRSTL;
X    }
X}
X
X/* =======================================================================
X * Name - ttox
X *
X * Purpose - for a given character, return the 2 character hex equivalent.
X *
X * Arguments:  c -- character to convert
X *             x -- location of result
X *
X * Returns     function return -- 
X *	       x[0], x[1] -- the two nibbles
X *
X *========================================================================
X */
X#if defined(FLG_PROTOTYPE)
Xstatic void ttox(char x[], int c)
X#else
Xstatic void ttox(x, c)
Xchar x[];
Xint c;
X#endif
X{
X  x[1] = binToHex[c & 0x0f];
X  x[0] = binToHex[(c >> 4) & 0x0f];
X}
X
X/* =======================================================================
X * Name - search
X *
X * Purpose - search the file for a string of bytes.  return the byte pointer
X *	     if found.  If not found, return -1
X *
X * Arguments:  fp -- file descriptor of file to search
X *	       s -- string to search for
X *             n -- length of s
X *             here -- current byte position
X *
X * Returns     function return -- position of file that matches string, -1L
X *                                otherwise
X *
X *========================================================================
X */
X#if defined(FLG_PROTOTYPE)
Xstatic long search(int fp, char s[], int n, long here)
X#else
Xstatic long search(fp, s, n, here)
Xint	fp;
Xchar	s[];
Xint	n;
Xlong	here;
X#endif
X{
X  char	buf[MAX_RETURNED_FROM_DISK];
X  int		nn, i, j;
X  long	ptr;
X
X  ptr = here;
X
X  while (TRUE) {
X    nn = doRead(fp, ptr, buf, sizeof buf);
X    if (nn < n)
X      break;
X    for (i = 0;  i < nn - n + 1;  i++) {
X      for (j = 0;  j < n;  j++)	/*	test string match 	*/
X	if (s[j] != buf[i + j])
X	  break;
X	if (j == n) {
X	  return ptr + i; /*	valid match found */
X	  }
X      }
X    ptr += (sizeof buf) + (long)(1 - n); /*	with n-1 overlap */
X    }
X  return -1L; /* no match found	*/
X}
X
X/* =======================================================================
X * Name - cvthex
X *
X * Purpose - convert a string of hex 'characters' into bytes.
X *	     attempt to allow blanks or not as user sees fit.
X *
X * Arguments:  s -- null-terminated input string, e.g. c524fe
X *             sx -- address in which to place converted string
X *
X * Returns     function return -- size of resulting string
X *	       *sx -- contains converted bytes
X *
X *========================================================================
X */
X#if defined(FLG_PROTOTYPE)
Xstatic int cvthex(char s[], char *sx)
X#else
Xstatic int cvthex(s, sx)
Xchar *s; /*	input string eg:  c524fe */
Xchar	*sx; /*	returned packed bytes. */
X#endif
X{
X  int		n;
X  int		 c1, c2;
X
X  n = 0;
X  while (*s != '\0') {
X    if (*s == ' ') {
X      s++;
X      continue;
X      }
X    c1 = hexval(*s++);
X    if (*s == ' ')
X      sx[n++] = (char)c1;
X    else {
X      c2 = hexval(*s++);
X      sx[n++] = (char)((c1 << 4) | c2);
X      }
X    }
X  return n;
X}
X
X/* =======================================================================
X * Name - usage
X *
X * Purpose - Print short blurb
X *
X * Arguments:  None
X *
X * Returns     function return -- 
X *
X *========================================================================
X */
X#if defined(FLG_PROTOTYPE)
Xstatic void usage(char name[])
X#else
Xstatic void usage(name)
Xchar name[];
X#endif
X{
X  printf("File Modifier 2.0 (binary file editor), patch level %d\n",
X      PATCHLEVEL);
X  printf("Usage: %s filename\n", name);
X}
X
X/* =======================================================================
X * Name - main
X *
X * Purpose - where it all starts
X *
X * Arguments:  argv, argc noise
X *
X * Returns     function return -- status code to operating system (not reached)
X *
X *========================================================================
X */
X#if defined(FLG_PROTOTYPE)
Xint main(int argc, char *argv[])
X#else
Xint main(argc, argv)
Xint		argc;
Xchar	*argv[];
X#endif
X{
X  int		fp;
X  char	fileBuf[BSIZE];		/*	file i/o buffer	*/
X  char	scratchBuf[BSIZE];	/*	work buffer copy of fileBuf*/
X  long	fpos, fsearch;		/*	current file position	*/
X  int		nbytes;		/*	number of bytes read */
X  int		key, yesno;
X  char	resp[100];		/*	user response */
X  char	stext[100];		/*	last ascii search string */
X  char	shex[100];		/*	last hex search string */
X  char	hexstring[50];
X  int		nn;
X  struct stat statbuf;
X		  
X  if (argc == 1) {
X    usage(argv[0]);
X    EXIT_SUCCESS;
X    }
X
X  signal(SIGTERM, freakout);
X  initscr();
X#if defined(MSDOS)
X{ /* make cursor easier to see */
Xunion REGS inRegs, outRegs;
X
XinRegs.h.ah = 1; /* Set Cursor Type */
XinRegs.h.ch = 0;
XinRegs.h.cl = 13;
Xint86(0x10 /* VIDEO_IO*/, &inRegs, &outRegs);
X}
X#endif
X  /*	determine size - for directories or regular files.  */
X  strcpy(srcName, argv[1]);
X  if (stat(srcName, &statbuf) == -1) {
X    cleanup();
X    perror("stat() failure");
X    EXIT_ERR;
X    }
X  if ((statbuf.st_mode & S_IFMT) == S_IFDIR
X      ||  (statbuf.st_mode & S_IFMT) == S_IFREG)
X    max_fpos = (long) statbuf.st_size;
X  else
X    max_fpos = 0x7fffffffL;	/*	infinity??? */
X
X  if (max_fpos <= 0) { /* what's the point? */
X    cleanup();
X    fprintf(stderr, "file %s is zero-length!\n", srcName);
X    EXIT_ERR;
X    }
X
X  fp = doOpen(srcName, FALSE);
X  if (fp == -1) {
X    fp = doOpen(srcName, TRUE);
X    if (fp == -1) {
X      cleanup();
X      perror("file is unavailable");
X      EXIT_ERR;
X      }
X    writeOk = FALSE;	/*	read only */
X    }
X  else
X    writeOk = TRUE;	/*	read/write	*/
X
X  clear();
X  NONCANONICAL;
X
X#if (defined(SYSV_CURSES) || defined(M_XENIX)) && !defined(VMS)
X  keypad(stdscr, TRUE);
X#endif
X  key = CMD_REDRAW;
X  fpos = 0;
X  byte_pos = 0;
X  stext[0] = '\0';
X  shex[0] = '\0';
X
X  while (key != CMD_DON) {
X    switch (key) {
X      case CMD_FWD:	/*	next screen load */
X	if (fpos + BSIZE < max_fpos)
X	  fpos += BSIZE;
X        else
X          beep();
X	break;
X
X      case CMD_BAK:	/*	previous screen load	*/
X	fpos -= BSIZE;
X	if (fpos < 0L) {
X	  fpos = 0L;
X	  byte_pos = 0;
X          beep();
X	  }
X	break;
X
X      case CMD_JMP:	/*	goto byte address */
X        CANONICAL;
X	mvwaddstr(stdscr, 1, PROMPTCOL, "jump address = ");
X	wclrtoeol(stdscr);
X        EXTRA_REFRESH;
X	wgetstr(stdscr, resp);
X	sscanf(resp, "%lx", &fpos);
X	if (fpos < 0)
X	  fpos = 0;
X	else
X	if (fpos > max_fpos)
X	  fpos = max_fpos;
X	byte_pos = (int)(fpos & 0x0ff);
X	fpos &= ~((long) BSIZE - 1);
X	NONCANONICAL;
X	break;
X
X      case CMD_SEARCH:	/*	search ascii text */
X	CANONICAL;
X        if (vmode == asciiMode) {
X	  if (*stext != '\0') {
X	    wmove(stdscr, 2, PROMPTCOL);
X	    wprintw(stdscr, "( / = use '%s')", stext);
X            }
X	  mvwaddstr(stdscr, 1, PROMPTCOL, "ascii search = ");
X          }
X        else {
X	  if (*shex != '\0') {
X	    wmove(stdscr, 2, PROMPTCOL);
X	    wprintw(stdscr, "( / = use '%s')", shex);
X	    }
X	  mvwaddstr(stdscr, 1, PROMPTCOL, "hex search = ");
X          }
X	wclrtoeol(stdscr);
X        EXTRA_REFRESH;
X	wgetstr(stdscr, resp);
X	NONCANONICAL;
X        if (vmode == asciiMode) {
X	  if (resp[0] == '\0')
X	    strcpy(resp, stext);
X          else
X	    strcpy(stext, resp);
X	  fsearch = search(fp, resp, strlen(resp), fpos + byte_pos + 1);
X          }
X        else {
X	  if (resp[0] == '\0')
X	    strcpy(resp, shex);
X          else
X	    strcpy(shex, resp);
X	  nn = cvthex(resp, hexstring);
X	  fsearch = search(fp, hexstring, nn, fpos + byte_pos + 1);
X          }
X	if (fsearch != -1) {
X	  fpos = fsearch;
X	  byte_pos = (int)(fpos & 0x0ff);
X	  fpos &= ~((long) BSIZE - 1);
X	  }
X	break;
X
X      case CMD_REDRAW:
X        erase();
X        clear();
X        break;
X
X      case CMD_PRI:
X	CANONICAL;
X	if (printFile == NULL) {
X	  mvwaddstr(stdscr, 1, PROMPTCOL, "print file = ");
X	  wclrtoeol(stdscr);
X          EXTRA_REFRESH;
X	  wgetstr(stdscr, resp);
X	  if (resp[0] == '\0') {
X	    NONCANONICAL;
X	    break;
X	    }
X	  printFile = fopen(resp, "a+");
X	  if (printFile == NULL) { /* display err */
X            char message[200];
X
X            sprintf(message, "fopen() error, errno = %d", errno);
X	    mvwaddstr(stdscr, 1, PROMPTCOL, message);
X	    wclrtoeol(stdscr);
X	    NONCANONICAL;
X	    break;
X	    }
X	  }
X
X	mvwaddstr(stdscr, 1, PROMPTCOL, "print comment = ");
X	wclrtoeol(stdscr);
X        EXTRA_REFRESH;
X	wgetstr(stdscr, resp);
X	NONCANONICAL;
X	printit(resp, fpos + byte_pos, scratchBuf, nbytes);
X	mvwaddstr(stdscr, 1, PROMPTCOL, "printed");
X	wclrtoeol(stdscr);
X	break;		
X
X      case CMD_UND:	/*	undo current changes */
X	break;
X		    
X      default: ;
X      }
X
X    /*	get a 256 byte block of file.  copy to a working buffer (scratchBuf) */
X    
X    nbytes = doRead(fp, fpos, fileBuf, BSIZE);
X    memcpy(scratchBuf, fileBuf, nbytes);
X    maybe_dirty = FALSE;
X    display(fpos, scratchBuf, nbytes);
X
X    /*	allow user to modify file */
X    
X    key = edit(scratchBuf, nbytes, fpos);
X    if (key == CMD_UND)
X      continue;
X    if (!writeOk || !maybe_dirty || memcmp(fileBuf, scratchBuf, BSIZE) == 0)
X      continue;
X
X    echo();
X    mvwaddstr(stdscr, 1, PROMPTCOL, "apply changes (y/n): ");
X    EXTRA_REFRESH;
X    yesno = wgetch(stdscr);
X    noecho();
X    if (yesno == 'Y' || yesno == 'y')
X      doWrite(fp, fpos, scratchBuf, nbytes);
X    else
X      memcpy(scratchBuf, fileBuf, nbytes);		/* in case PRInt */
X    } /* while */
X  EXTRA_REFRESH;
X  doClose(fp);
X  cleanup();
X  EXIT_SUCCESS;
X
Xreturn 0; /* for lint */
X}
END_OF_FILE
if test 35764 -ne `wc -c <'fm.c'`; then
    echo shar: \"'fm.c'\" unpacked with wrong size!
fi
# end of 'fm.c'
fi
if test -f 'patchlevel.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'patchlevel.h'\"
else
echo shar: Extracting \"'patchlevel.h'\" \(21 characters\)
sed "s/^X//" >'patchlevel.h' <<'END_OF_FILE'
X#define PATCHLEVEL 2
END_OF_FILE
if test 21 -ne `wc -c <'patchlevel.h'`; then
    echo shar: \"'patchlevel.h'\" unpacked with wrong size!
fi
# end of 'patchlevel.h'
fi
if test -f 'vms.hc' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'vms.hc'\"
else
echo shar: Extracting \"'vms.hc'\" \(7232 characters\)
sed "s/^X//" >'vms.hc' <<'END_OF_FILE'
X/*************************************************************************
X                     vms.hc -- fm routines for VAX/VMS
X		      Copyright (c) Tony Field 1990
X
X  In principle this code should be maintained directly in-line in fm.c
X  However, there is a BIG gotcha:
X
X  1.  All VMS entry points and most types, fields, etc. contain the character
X   "$" (dollar sign).  This is a noble attempt to avoid the possibility of
X   user symbols conflicting with the predefined ones in the operating system.
X
X   Of course, all DEC-supported languages regard "$" as being yet another
X   admissible character in an identifier.
X
X  2.  ANSI C preprocessors are permitted to barf if they come across a
X   character that's not a legal character in C.  So even if the VMS code
X   is not used on the "foobar" architecture, it is possible that the mere
X   presence of its implementation in fm.c could prevent it from compiling.
X
XSO.  We put all this junk in a separate file so that ports to other hosts
Xwill not be influenced by the presence of a lot of "$"s in weird places.
X
XN.B. -- VMS DEBUG is not perturbed by include files containing C code, as
Xis dbx; thus there is no need to move this code back into fm.c if you need to
Xdebug this RMS junk.
X
X--D. Jason Penney, June 1990
X****************************************************************************/
X
X#include <fab.h>
X#include <rab.h>
X
Xtypedef void VmsAstFuncType(long arg);
X
Xlong SYS$CLOSE(struct FAB *fab, VmsAstFuncType *err, VmsAstFuncType *SUC);
Xlong SYS$CONNECT(struct RAB *rab, VmsAstFuncType *err, VmsAstFuncType *SUC);
Xlong SYS$OPEN(struct FAB *fab, VmsAstFuncType *err, VmsAstFuncType *SUC);
Xlong SYS$READ(struct RAB *aRab, VmsAstFuncType *err, VmsAstFuncType *SUC);
Xlong SYS$WRITE(struct RAB *aRab, VmsAstFuncType *err, VmsAstFuncType *SUC);
X
Xstatic struct FAB theFab;
Xstatic struct RAB theRab;
X
X/* =======================================================================
X * Name - doRead
X *
X * Purpose - Abstraction for file read
X *
X * Arguments:  fp -- file from which to read
X *	       fpos -- location from which to read in file
X *	       loc -- address to place result
X *             maxsize -- max number of bytes that may be read into loc
X *
X * Returns     function return -- -1 on error, otherwise success
X *
X *========================================================================
X */
Xstatic int doRead(int fp, long fpos, char *loc, int maxsize)
X{
Xlong retVal;
X
X/* reads are done at arbitrary position and length, so we have to
X   fake it at this level */
XtheRab.rab$l_bkt = 1 + fpos / 512; /* where on disk (one-based) */
Xif (fpos % 512 == 0) { /* aligned read, easy special case */
X  theRab.rab$w_usz = maxsize;	   /* how long */
X  theRab.rab$l_ubf = loc;	   /* where in memory */
X  theRab.rab$l_stv = 0;
X
X  retVal = SYS$READ(&theRab, NULL, NULL);
X  if (retVal % 2 != 1)
X    return -1;
X  /* VMS 5.1 doesn't set rab$l_stv, documentation notwithstanding */
X  if (theRab.rab$l_stv != 0 && (theRab.rab$l_stv % 2 != 1)
X    return -1;
X  }
Xelse { /* transfer required */
X  char aBlock[MAX_RETURNED_FROM_DISK + 512];
X
X  theRab.rab$w_usz = maxsize + 512; /* get extra block */
X  theRab.rab$l_ubf = &aBlock[0];
X  theRab.rab$l_stv = 0;
X
X  retVal = SYS$READ(&theRab, NULL, NULL);
X  if (retVal % 2 != 1)
X    return -1;
X  /* VMS 5.1 doesn't set rab$l_stv, documentation notwithstanding */
X  if (theRab.rab$l_stv != 0 && (theRab.rab$l_stv % 2 != 1)
X    return -1;
X  /* return part that we want */
X  memcpy(loc, &aBlock[fpos % 512], maxsize);
X  }
X
X/* return lesser of two lengths */
Xif (theRab.rab$w_rsz < maxsize)
X  return theRab.rab$w_rsz;
Xelse
X  return maxsize;
X}
X
X/* =======================================================================
X * Name - doWrite
X *
X * Purpose - Abstraction to perform write on file
X *
X * Arguments:  fp -- file to which to write
X *             fpos -- position at which to write
X *             loc -- address of bytes to be written
X *             nbytes -- number of bytes to be written
X *
X * Returns     function return -- -1L on error, otherwise success
X *
X *========================================================================
X */
Xstatic int doWrite(int fp, long fpos, char *loc, int nbytes)
X{
X/* writes are done in half-block chunks, so we have to normalize if
X   requested address is not on a block boundary */
Xchar scratch[512];
Xlong retVal;
X
X/* Get original block */
Xif (doRead(fp, fpos / 512 * 512, &scratch[0], 512) == -1)
X  return -1;
Xif (fpos % 512 == 0)
X  memcpy(&scratch[0], loc, 256);
Xelse
X  memcpy(&scratch[256], loc, 256);
XtheRab.rab$w_usz = 512;			/* how large */
XtheRab.rab$l_ubf = &scratch[0];		/* where in memory */
XtheRab.rab$l_bkt = 1 + fpos / 512;	/* where on disk (1-based) */
XtheRab.rab$l_stv = 0;
X
XretVal = SYS$WRITE(&theRab, NULL, NULL);
Xif (retVal % 2 != 1)
X  return -1;
X/* VMS 5.1 doesn't set rab$l_stv, documentation notwithstanding */
Xif (theRab.rab$l_stv != 0 && (theRab.rab$l_stv % 2 != 1)
X  return -1;
Xreturn theRab.rab$w_rsz;		/* return how many actually written */
X}
X
X/* =======================================================================
X * Name - doOpen
X *
X * Purpose - Abstraction to open file
X *
X * Arguments:  name -- null-terminated name of file to open
X *             readOnly -- FALSE if you are allowed to write to file
X *
X * Returns     function return -- -1L on error, otherwise success
X *
X *========================================================================
X */
Xstatic int doOpen(char name[], int /*BoolType*/ readOnly)
X/* Open the file using the RMS utilities */
X{
Xlong retStatus;
X
X/* zero all of the fields in the fab */
Xmemset( (char *)&theFab, 0, sizeof(theFab) );
X
X/* identify the Block as a fab */
XtheFab.fab$b_bid = FAB$C_BID;
XtheFab.fab$b_bln = FAB$C_BLN;
X
X/* fill in the fab fields needed for the open function */
X
XtheFab.fab$l_fna = name;
XtheFab.fab$b_fns = strlen(name);
XtheFab.fab$b_fac = FAB$M_BIO + FAB$M_GET; /* block i/o, read allowed */
Xif (!readOnly)
X  theFab.fab$b_fac |= FAB$M_PUT; /* write allowed */
XtheFab.fab$b_shr = FAB$M_NIL; /* no sharing, required for block i/o */
XtheFab.fab$w_mrs = 512; /* physical block size */
XtheFab.fab$b_org = FAB$C_SEQ;
XtheFab.fab$b_rfm = FAB$C_FIX;
X
XretStatus = SYS$OPEN(&theFab, (VmsAstFuncType *)(NULL), 
X    (VmsAstFuncType *)(NULL));
X
Xif (retStatus % 2 != 1) { /* error on open */
X  return -1;
X  }
X/* rab$l_stv not set by this call... */
X
X/* create record stream */
Xmemset(&theRab, 0, sizeof(struct RAB));
XtheRab.rab$b_bid = RAB$C_BID;
XtheRab.rab$b_bln = RAB$C_BLN;
XtheRab.rab$l_fab = &theFab;
XtheRab.rab$w_isi = 0L;
XtheRab.rab$l_rop = 0L;
XtheRab.rab$l_stv = 0;
XretStatus = SYS$CONNECT(&theRab, NULL, NULL);
X
Xif (retStatus % 2 != 1) { /* error on connect */
X  return -1;
X  }
X/* VMS 5.1 doesn't set rab$l_stv, documentation notwithstanding */
Xif (theRab.rab$l_stv != 0 && (theRab.rab$l_stv % 2 != 1)
X  return -1;
X
Xreturn 0; /* junk */
X}
X
X/* =======================================================================
X * Name - doClose
X *
X * Purpose - Abstraction to close file (when done)
X *
X * Arguments:  fp -- file to be closed
X *
X * Returns     function return -- 
X *
X *========================================================================
X */
Xstatic void doClose(int fp)
X{
Xint retStatus = SYS$CLOSE(&theFab, NULL, NULL);
X}
END_OF_FILE
if test 7232 -ne `wc -c <'vms.hc'`; then
    echo shar: \"'vms.hc'\" unpacked with wrong size!
fi
# end of 'vms.hc'
fi
echo shar: End of archive 1 \(of 1\).
cp /dev/null ark1isdone
MISSING=""
for I in 1 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have the archive.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0