Google
 

Trailing-Edge - PDP-10 Archives - decuslib20-10 - decus/20-184/filcmp.mac
There are no other files named filcmp.mac in the archive.
; Add a TYPE command (starting at) PAGE,STRING -  use DCCOC to display
; warn user if NEWer file is not newer than OLDer file
; give warning if byte size is not the same for both files
; have UPDATE check to see if updates have already been added

	TITLE	FILCMP - File compare program
	SUBTTL	EDIT HISTORY
	SEARCH	MONSYM,MACSYM,MLIB
	INTERN	DIE,SETTAB
	.REQUES	LIB:MLIB
	.DIRECT	FLBLST	;only list first line of multiline text
	SALL		;make neat listings

	VWHO==^o2	;2 indicates an edit at customer site
	VMAJOR==^o1	;MAJOR version number
	VMINOR==^o0	;MINOR ver.- reset to zero when major changes
	VEDIT==^o3	;EDIT number - never reset to zero
	VERSION==<VWHO>B2+<VMAJOR>B11+<VMINOR>B17+VEDIT


;WHO	DATE	   Edit	MODIFICATIONS
;===	=========  ====	================================================
;DLW	15-Oct-84   00	-genesis
;ALF	27-MAY-85   01	-allow user to ignore spaces, tabs and new lines
;			 when performing the comparision.
;DLW	28-Jun-85   02	-have /IGNORE ignore nulls as well
;ALF	24-Oct-85   03	-fix problem with /VERIFY in UPDATE


	SUBTTL	DEFINITIONS

	OP=P1	;byte pointer to pmap area for OLDer file
	OB=P2	;number of bytes left in pmap area after OP
	NP=P3	;byte pointer to pmap area for NEWer file
	NB=P4	;number of bytes left in pmap area after NP

;flags used in register "F"
;	Bits "1B30 to 1B35" are reserved for flags used in MLIB
	F%VT==1B29	;1=controlling terminal is a VT100
	F%SES==1B28	;1=output is to a VT100 like device
	F%QUIK==1B27	;1=quick switch selected
	F%UPDF==1B26	;1=write output file in UPDATE format
	F%ABRT==1B25	;1=a routine had to be aborted due to errors
	F%VRFY==1B24	;1=verify the change to be made is unique
	F%TTY==1B23	;1=output is going to the terminal
	F%BOFM==1B22	;1=BOFM marker was found in text region
	F%EOFM==1B21	;1=EOFM marker was found in text region
	F%IGNO==1B20	;1=ingore spaces, tabs, nulls, ^L, multiple blank lines
	F%LSTK==1B19	;1=FINDL will use LSTK to get pointer to start of lines
;;	F%____==1B18	;1=
	F%WILD==1B17	;1=wild file specs given for files to process
	F%APND==1B16	;1=append data to the OUTput file
	F%OPEN==1B15	;1=output file is open
	F%EXE==1B14	;1=input files are in binary format

DEFINE SENDES (AA) <
	SPTR	T2,<AA>
	CALL	SNDES
>
DEFINE RVIDON < CALL	.RVIDO>
DEFINE CATOFF < CALL	.CATOF>
DEFINE OSOUT% <
	SOUT%
	 JERR	(?,,PC,DIE)>
	SUBTTL	CORRUPTIBLE DATA AREA

	CMD.DA (<FILCMP>,<FILCMP>>,60)	;set up command data area
	ENDCMD==:GETCMD##	;no special exit routine after each command

	VARBEG==.	;start of variable area zeroed for warm restart
	CMD.ZV		;assemble COMND variables to be zeroed
	VAREND==.-1	;end of variable area zeroed for warm restart


FDA:!	PHASE	0	;define structure of File Data Area.

JFN:!	0	;JFN of file
FFP:!	0	;fork handle,,first page of pmap area
FLP:!	0	;flags,,# pages to map in (normally length of pmap area)
FPG:!	0	;page of file to start pamping at
PGL:!	0	;length of file in pages
BPP:!	0	;# number of bytes/page
BSZ:!	0	;right half of byte pointer,,byte size of file
BLN:!	0	;byte length of file
BLF:!	0	;# unused bytes on last page of file
PFP:!	0	;pointer file page (used to hold FPG when PTR is used. It will
		; allows one to return to a previous point in a file)
PTR:!	0	;byte pointer to pmap area
BAP:!	0	;# bytes after PTR left in the pmap area
TOB:!	0	;total number of bytes in pmap area
EOF:!	0	;end of file flag (If > 0 then last page of file is NOT mapped
		; into the pmap area (EOF= the # pages of file left after
		; last page in pmap area). If <= 0 then last page of file is
		; currently mapped into the pmap area (EOF= the offset
		; that when added to RH of FLP will give the page in memory
		; that last file page is on)
LPFP:!	0	;pointer file page for lines in LPTR (see PFP)
LBYG:!	0	;number of bytes in the line group being searched for
	0	;stack pointer - points to top of LSTK stack
	DEPHASE
	FDALN==.-FDA	;length of File data area
	.ORG FDA	;reclaim space used for FDA definitons
IFN <FLP-FFP-1+FFP-JFN-1>,<PRINTX ?JFN,FFP,FLP must be in order for DMOVE>
IFN <BAP-PTR-1+PTR-PFP-1>,<PRINTX ?PFP,PTR,BAP must be in order for DMOVE>

	;initialize part of FDA for the OLDer file
OLD:	0				;JFN
	.FHSLF,,OLDFPG			;FFP
	PM%CNT!PM%RD!PM%PLD+OLDPLN	;FLP
	BLOCK	FDALN-<.-OLD>

	;initialize part of FDA for the NEWer file
NEW:	0				;JFN
	.FHSLF,,NEWFPG			;FFP
	PM%CNT!PM%RD!PM%PLD+NEWPLN	;FLP
	BLOCK	FDALN-<.-NEW>

	;initialize part of FDA for the OUTput file (only need JFN)
OUT:	0				;JFN
	MAXL4M==^D64	;max possible number of lines required for a match
			; (the only requirement is that it must be < LSTKLN)
			; (I chose 64 because I like that number)
	DEFL4M==^D3	;default number of lines required for a match
L4M:	DEFL4M		;number of lines required for a match
DL4M:	DEFL4M		;default number of lines required for a match

FMAFDA:	0	;holds FDA's for FMATCH
FMAPTR:	0	;holds ptr
	0	;holds bytes after ptr
FMALCT:	0	;holds count of number of lines processed or searched
	DEFL2S==^D1000
L2S:	DEFL2S		;# lines to search for a match before giving up
DL2S:	DEFL2S		;default # lines to search for a match before giving up
DSWI.S:	0		;default switches to set
DSWIF==F%QUIK!F%UPDF!F%APND!F%IGNO!F%EXE	;default switches to clear

CHGCNT:	0	;for COMPARE - the # of differences found between the files
		;for UPDATE - the actual number of changes processed or made
CHGNUM:	0	;the current change being processed
MCCOC:	BLOCK 2	;holds modified CCOC words for terminal...
		;  ...will cause monitor to send actual code for <esc>
OCCOC:	BLOCK 2	;holds original CCOC words for terminal
DCCOC:	252525,,553125	;will cause all control characters to be displayed...
	252525,,652400	; ...as a 2 character code execpt ^I,^J,^M and $

FILCNT:	0	;holds the number of files processed
FSPEC:	BLOCK ^d<80/5>	;holds file specs temorarly for various things
OLDFS:	BLOCK ^d<80/5>	;holds non-wild file specs for OLDer file
OLDFSW:	BLOCK ^d<80/5>	;holds wild file specs for OLDer file
NEWFSW:	BLOCK ^d<80/5>	;holds wild file specs for NEWer file
	SUBTTL	Software Interrupt Data

PI.CNT:	0	;holds # times ^A routine has been called
CAOLOC:	BLOCK 2	;holds file location of OLDer file (ptr + page)
CANLOC:	BLOCK 2	;holds file location of NEWer file (ptr + page)
PI.ABT:	0	;holds address of return routine for ^E,^X abort
PI.P:	0	;holds stack for interrupt routines

PI.ACS:	BLOCK CX+1	;holds AC's (F to CX) for ^A interrupts

	LALL
	P.LVT	;assemble LEVTAB data for software interrupt processing
	SALL

CHNTAB::DCW (3,CTRLA,.CACH,.CALV)	;0  ^A interrupts
	DCW (3,CTRLE,.CECH,.CELV)	;1  ^E interrupts
	DCW (3,CTRLX,.CXCH,.CXLV)	;2  ^X interrupts
	0				;3  free
	0				;4  free
	0				;5  free

	0				;6  arithmetic overflow
	0				;7  arithmetic floating pt overflow
	0				;8  reserved for DEC
	0				;9  PANIC - pushdown list overflow
	0				;10 end of file condition
	0				;11 PANIC - data error file condition
	0				;12 PANIC - disk full or quota exceeded
	0				;13 reserved for DEC
	0				;14 reserved for DEC
	0				;15 PANIC - illegal instruction
	0				;16 PANIC - illegal memory read
	0				;17 PANIC - illegal memory write
	0				;18 reserved for DEC
	0				;19 inferior process termination
	0				;20 PANIC - system resources exhausted
	0				;21 reserved for DEC
	0				;22 nonexistent page reference

	REPEAT ^D13,<0>			;23-35	free

ONCHNL:: $ONCHN
	PURGE $ONCHN
	SUBTTL	PMAPing Area

;-----------------------------------------------------------------------------
.ORG	100K		;start area for PMAPing at this address

	PMBLN==100	;length of PMAP buffers for NEWer and OLDer files
;-----
	OLDFPG==._^D-9		;first page of OLDer file pmap area
	OLDPLN==PMBLN		;normal length of pmap area (must be at least 2)
	BLOCK	<OLDPLN*1K>
	IFN <^D36-<^L<._^D27>>>,<PRINTX ?Data not on page boundary>
;-----
	NEWFPG==._^D-9		;first page of NEWer file pmap area
	NEWPLN==PMBLN		;normal length of pmap area (must be at least 2)
	BLOCK	<NEWPLN*1K>
	IFN <^D36-<^L<._^D27>>>,<PRINTX ?Data not on page boundary>
;-----
		LSTKLN==100*1K
LSTK:	BLOCK	LSTKLN
	; the LSTK holds the number of bytes in a line for a line in the NEWer
	; and OLDer files:
	;	#-bytes-in-line-for-NEW-file,,#-bytes-in-line-for-OLD-file
	; This stack is used by the FMATCH and FINDL routines to speed up the
	; searching for where the two files start to match up again. The first
	; word of LSTK is not used and may be trashed under certain conditions
	; by the FMAINI routine.

	IFL <377777-LSTKLN>,<PRINTX ?LSTKLN must be less than 400K>
	IFL <LSTKLN-MAXL4M-1>,<PRINTX ?LSTKLN must be bigger than MAXL4M>
;-----
.ORG		;reset relocation counter back to previous value
;-----------------------------------------------------------------------------
	SUBTTL	NON-CORRUPTIBLE DATA AREA

CSTRBP:	POINT 7,CSTR
CSTR:	ASCIZ\>>>>>>>>>>>Change \
CSTRBC==^D19	;number of bytes in CSTR
FSTRBP:	POINT 7,FSTR
FSTR:	ASCIZ\>>>>>File \
FSTRBC==^D11	;number of bytes in FSTR
TSTR:	ASCIZ\total \
TSTRBC==^D6	;number of bytes in TSTR
TSTRFB=="t"	;first byte of TSTR
BOFMBP:	POINT 7,BOFM
BOFMBC==^D9	;number of bytes in BOFM
BOFM:	ASCIZ\<<bof>>
\
EOFMBP:	POINT 7,EOFM
EOFMBC==^D9	;number of bytes in EOFM
EOFM:	ASCIZ\<<eof>>
\
;=============================================================================
;These tables lists all the commands this program can process. When adding
;new entries make sure they are added in alphabetical order

CMDTAB:	CMDTLN,,CMDTLN			;actual,,maximum number of entries
	TBL (COMPARE)
	TBL (EXIT)
	TBL (HELP,,.HELP##)
	TBL (INFORMATION)
	TBL (QUIT,CM%INV,.QUIT##)	;same as EXIT at the top command level
;	TBL (REMOVE)
	TBL (SET,,.SET##)
	TBL (TAKE,,.TAKE##)
	TBL (UPDATE)
	CMDTLN==<.-CMDTAB>-1

;=============================================================================
;Table for the SET command

SETTAB:	SETTLN,,SETTLN			;actual,,max length of table
	TBL (ECHO,,.SECHO##)
	TBL (EXE,,.SEXE)
	TBL (IGNORE,,.SIGNO)
	TBL (LINES,,.SL4M)
	TBL (MAX-LINES,,.SL2S)
	TBL (NO,,.SNO##)
	TBL (QUICK,,.SQUIC)
	TBL (UPDATE-FORMAT,,.SUPDF)
	SETTLN==<.-SETTAB>-1

CMPSWI:	FLDBK. .CMSWI,,CMPSTB,,,,CONFRM##	;FDB for COMPARE switches
CMPSTB:	CMPSTL,,CMPSTL			;actual,,maximum number of entries
	TBL (EXE)
	TBL (IGNORE,,.IGNO)
	TBL (LINES:,,.L4M)
	TBL (MAX-LINES:,,.L2S)
	TBL (NO:,,.SWNO)
	TBL (QUICK)
	TBL (UPDATE-FORMAT,,.UPDF)
	CMPSTL==<.-CMPSTB>-1

UPDSWI:	FLDBK. .CMSWI,,UPDSTB,,,,CONFRM##	;FDB for UPDATE switches
UPDSTB:	UPDSTL,,UPDSTL			;actual,,maximum number of entries
	TBL (VERIFY,,0)
	UPDSTL==<.-UPDSTB>-1

NOTAB:	NOTLN,,NOTLN			;actual,,max length of table
	TBL (EXE,,.NOEXE)
	TBL (IGNORE,,.NOIGN)
	TBL (MAX-LINES,,.NOMAX)
	TBL (QUICK,,.NOQU)
	TBL (UPDATE-FORMAT,,.NOUP)
	NOTLN==<.-NOTAB>-1

YNTAB:	YNTLN,,YNTLN			;actual,,maximum number of entries
	TBL (NO,,0)
	TBL (YES,,1)
	YNTLN==<.-YNTAB>-1
	SUBTTL	MAIN PROGRAM
					;start of entry vector
ENTVEC:	JRST	START			;"@START" address
	JRST	START			;"@REENTER" address
	VERSION				;version number (must be 3rd word)
	EVLEN==.-ENTVEC			;get length of entry vector


START:	RESET%				;initialize the world
	SETZ	F,			;initialize flag  register
	MOVE	P,[IOWD PDLEN,PDL]	;initialize stack register
	CALL	ERESET##		;say program has encountered no errors
	SKIPN	STWARM			;is this a warm start?
	 IFSKP.				;no, go to ENDIF.
				;this code is only executed for warm restarts
	ZERO	(VARBEG,VAREND)		;reinitialize memory
	SETZM	NEW+JFN
	SETZM	OLD+JFN
	SETZM	OUT+JFN
	CMD.WM				;assemble warm restart code for COMND
ENDIF.
	MOVEI	T1,.CTTRM		;get the current CCOC words for...
	RFCOC%				;  ... the terminal
	DMOVEM	T2,OCCOC 		;save the original CCOC words
	TRZ	T3,3B19			;clear the bits for <esc> and make the
	TRO	T3,2B19			;monitor send the actual caracter code
	DMOVEM	T2,MCCOC 		;save the modified CCOC words
	GTTYP%				;get terminal type
	 JERR (?,,PC)
;;	CAIN	T2,.TT100		;is it a VT100 ?
;;	 TXO	F,F%VT			;yes
	CKTTY	(F%VT)			;check whether it is VT100 type
	CALL	ENAPSI##		;enable the interrupt system

	CALL	RCNINP##		;set up to read commands from RESCAN
;	SKIPN	STWARM			;is this a warm start
;	 CALL	TAKINI##		;no, setup to get commands from INI file
	SETOM	STWARM			;next time though its a warm start

	MOVEI	T1,DIE			;exit routine for this command level
	HRROI	T2,TOPCLP		;prompt string for this command level
	CALL	BEGCML##		;set up this command level
	RELJFN	<NEW+JFN,OLD+JFN>	;release any left over JFN's
	SKIPE	T1,OUT+JFN		;any JFN's around from last command?
	 CAIN	T1,.PRIOU		;ignore .PRIOU
	  TRNA				;nothing to release
	   RLJFN%			;release it (JFN already in T1)
	    JERR (%,,PC)
	SETZM	OUT+JFN			;zero so I don't do it again
	PARSE	(CMDBLK,<.CMKEY,,CMDTAB,<A command,>>)
	HRRZ	T2,(T2)			;get address of command server
	JRST	(T2)			;dispatch to it
	SUBTTL	Server for COMPARE command
;=============================================================================
.COMPA:	TXZ	F,F%WILD!F%APND		;initialize flags
	MOVX	T4,GJ%OLD!GJ%IFG	;parse an existing file...
	MOVEM	T4,GTJBLK+.GJGEN	; ...allow wildcards

	NOISE	(older file)
	PARSE	(,<.CMFIL,CM%SDH,,<file specs of older file>>)
	MOVEM	T2,OLD+JFN		;save JFN
	TXNE	T2,GJ%DEV!GJ%DIR!GJ%NAM!GJ%EXT!GJ%VER	;wildcards found?
	 TXO	F,F%WILD		;yes, set flag

	NOISE	(against newer file)
	CALL	SETDNE			;set up the default name + extension
	PARSE	(,<.CMFIL,CM%SDH,,<file specs of newer file>>)
	MOVEM	T2,NEW+JFN		;save JFN
	TXNE	T2,GJ%DEV!GJ%DIR!GJ%NAM!GJ%EXT!GJ%VER	;wildcards found?
	 TXO	F,F%WILD		;yes, set flag

	NOISE	(outputing differences to)
	MOVX	T4,GJ%FOU		;parse a new file generation
; program can't handle a wild output file yet
;	TXNE	F,F%WILD		;wildcards used for other files?
;	 TXO	T4,GJ%IFG		;yes, allow user to use wildcards here
	MOVEM	T4,GTJBLK+.GJGEN
	SETZM	GTJBLK+.GJNAM		;no defalut name
	HRROI	T4,[ASCIZ/CMP/]		;use this default file extension
	MOVEM	T4,GTJBLK+.GJEXT
	MOVEI	T4,.PRIOU		;default output device
	MOVEM	T4,OUT+JFN
	CALL	SWINI			;init switches to their default values
	PARSE	(,<.CMFIL,,,,,,CMPSWI>)
	TLZ	T3,-1			;get function discriptor block parsed
	CAIN	T3,CONFRM##		;user confirmed command?
	 JRST	COMPA7			;yes
	CAIN	T3,CMPSWI		;a switch?
	 JRST	COMPA4			;yes, go process it
	MOVEM	T2,OUT+JFN		;no, must have parsed the output file
COMPA3:	PARSE	(,,CMPSWI)		;parse a switch
	TLZ	T3,-1			;get function discriptor block parsed
	CAIN	T3,CONFRM##		;confirm command?
	 JRST	COMPA7			;yes
COMPA4:	HRRZ	T2,(T2)			;get address of command server
	CALL	(T2)			;call server for switch
	JRST	COMPA3			;parse next field

COMPA7:	CALL	DOECHO##		;echo the command if necessary
	MOVEI	T1,CMPFIL		;assume non-wild file specs given?
	TXNE	F,F%WILD		;were wild file specs given?
	 MOVEI	T1,WLDCMP		;yes
	CALL	@T1			;call routine to compare the files
	JRST	ENDCMD			;go get another command
;=============================================================================
;Routine to compare one ascii file with another
;	CALL CMPFIL
;ACCEPTS: no registers need to be initialized
;RETURNS: +1 always
;Trashes - most registers

CMPFIL:	HRRZ	T1,OUT+JFN		;get JFN of output device
	CALL	CKDEV			;check output device
	CATOFF				;make sure graphic attributes off

	CALL	FOPEN			;open the required files
	MOVEI	T1,CMPXIT		;exit routine for ^E,^X abort
	CALL	ATIAEX			;activate for interrupts

	MOVEI	T1,.WLJFN		;compare JFN's
	HRRZ	T2,OLD+JFN
	HRRZ	T3,NEW+JFN
	WILD%
	JUMPE	T1,[TMSGL <	*** Files are identical ***
>
		JRST	CMPXIT]	;done

	SETZM	CHGCNT			;initialize difference count
	TXZ	F,F%NO!F%ABRT!F%LSTK	;initialize flags

CMPLUP:	JUMPG	OB,CMPLU1		;jump if still bytes in OLD pmap buffer
	MOVEI	CX,OLD
	SKIPG	EOF(CX)			;end of file?
	 JRST	CMPEOF			;yes
	CALL	NXTPMP			;map in some more pages
	DMOVEM	T2,OP			;save byte ptr + byte count
CMPLU1:
	JUMPG	NB,CMPLU2		;jump if still bytes in NEW pmap buffer
	MOVEI	CX,NEW
	SKIPG	EOF(CX)			;end of file?
	 JRST	CMPEOF			;yes
	CALL	NXTPMP			;map in some more pages
	DMOVEM	T2,NP			;save byte ptr + byte count
CMPLU2:
	MOVE	T1,OP			;get byte ptr of OLDer file
	DMOVE	T2,NP			;get byte ptr + byte count of NEWer file
	CAMLE	T3,OB			;are length of strings equal?
	 MOVE	T3,OB			;no, get the lower one
	MOVEM	T3,T4			;save byte count I used
	TXNE	F,F%IGNO		;ignore spaces, tabs, nulls & newlines?
	 IFSKP.				;yes,
	CALL	CMPSTR##		;compare the strings
	 TXO	F,F%NO			;strings weren't identical
	SUB	T4,T3			;calc # bytes compared
	SUB	OB,T4			;calc # bytes left in buffer for OLD
	SUB	NB,T4			;calc # bytes left in buffer for NEW
ELSE.
	MOVE	P5,OB
	MOVE	P6,NB
	CALL	SUPCMP			;routine to handle /IGNORE
	 TXO	F,F%NO			;strings weren't identical
	MOVE	OB,P5			;calc # bytes left in buffer for OLD
	MOVE	NB,P6			;calc # bytes left in buffer for NEW
ENDIF.
	MOVEM	T1,OP			;save updated ptr for OLD file
	MOVEM	T2,NP			;save updated ptr for NEW file
	TXZN	F,F%NO			;were strings identical?
	 JRST	CMPLUP			;yes, continue the comparison

; found a difference has been found between the two files. Search back to find
; the beginning of the lines for each file
	TXNE	F,F%QUIK		;quick mode?
	 JRST	CMPQIK			;yes
	MOVEI	CX,OLD
	DMOVE	T1,OP			;get ptr + bytes left in pmap area
	CALL	FBACKJ			;search backward for a ^J
	DMOVEM	T1,OP			;save ptr + bytes left in pmap area

	MOVEI	CX,NEW
	DMOVE	T1,NP			;get ptr + bytes left in pmap area
	CALL	FBACKJ			;search backward for a ^J
	DMOVEM	T1,NP			;save ptr + bytes left in pmap area

; now see if I can find where the files start to match up again

	CALL	FMATCH			;find where files start matching again
	 IFSKP.				;no luck
	CALL	OUTDIF			;output the differences
	JRST	CMPLUP			;loop back to find the next difference
ENDIF.

	JUMPL	T4,CMPEND		;quit if L2S limit was exceeded
	SKIPG	CHGCNT			;is this the first difference?
	 JRST	[MOVEI	T1,OLD
		CALL	CKBOF		;am I at beginning of file?
		 JRST	.+1		;no
		MOVEI	T1,NEW
		CALL	CKBOF		;am I at beginning of file?
		 JRST	.+1		;no
		TMSGL <%	***** Files are TOTALLY different from beginning to end *****
>
		TXO	F,F%ABRT	;don't bother writing the output file
		JRST	CMPXIT]
	JRST	CMPEO5			;output differences to the EOF

; program gets here when I've reached the end-of-file of the NEW and/or OLD
; file when executing code in CMPLUP. If not EOF for both files then all data
; of the longer file is different
CMPEOF:	TXNE	F,F%IGNO
	 CALL EATEOF
	SKIPG	OLD+EOF
	 SKIPLE	OB
	  JRST	CMPEO3			;not at the end of the OLDer file
	SKIPG	NEW+EOF
	 SKIPLE	NB
	  JRST	CMPEO3			;not at the end of the NEWer file
	JRST	CMPEND			;I'm at the EOF for both files...
					; ...so nothing to output
CMPEO3:	TXNE	F,F%QUIK		;quick mode?
	 JRST	CMPQIK			;yes
	DMOVEM	OP,OLD+PTR		;save ptr + bytes left for 1st file
	MOVE	T1,OLD+FPG		;get current file page starting buffer
	MOVEM	T1,OLD+PFP		;save current ptr's file page
	DMOVEM	NP,NEW+PTR		;save ptr + bytes left for 2nd file
	MOVE	T1,NEW+FPG		;get current file page starting buffer
	MOVEM	T1,NEW+PFP		;save current ptr's file page

; program jumps here when I've reached the EOF of one of the files but there
; is still data left in the other file - so report the differences
CMPEO5:	SETOB	Q2,Q1			;initialize line group length
	CALL	OUTDIF			;output the differences
	JRST	CMPEND			;done

; gets here when /QUICK mode was set and files are different
CMPQIK:	TMSGL <%	files are different
>
	JRST	CMPXIT

CMPEND:	SKIPE	T2,CHGCNT		;any differences found?
	 IFSKP.				;yes
	TMSGL <	*** No differences found ***
>
ELSE.
	HRRZ	T1,OUT+JFN
	RVIDON				;turn reverse video on
	MOVE	T2,CSTRBP		;get byte pointer to CHANGE string
	TXNN	F,F%UPDF		;use update format?
	 IBP	T2			;no, skip first byte of string
	SETZ	T3,
	OSOUT%
	HRROI	T2,TSTR
	OSOUT%
	NUMOUT (CHGCNT,,-)
	SPTR	T2,<
>
	SETZ	T3,
	OSOUT%
	CATOFF				;turn reverse video off
	TMSGL <%	>
	NUMOUT (CHGCNT)
	TMSG < difference>
	MOVEI	T1,"s"
	CAIE	T2,1			;more that 1 difference?
	 PBOUT%				;yes, get tense right
	TMSG < found
>
ENDIF.
CMPXIT:	CALL	DTIAEX			;deactivate for ^A,^E interrupts
	CALLRET	FCLOSE			;close all files and return to caller
;===============================================================================
;this routine will compare two strings and ignore all the tabs, spaces, nulls
;and multiple blank lines
;
;	CALL SUPCMP
;
;Accept:T1-Byte ptr for the 1st string.
;	T2-Byte ptr for the 2nd string.
;	P5-Number of bytes availiable in the 1st string.
;	P6-Number of bytes availiable in the 2nd string.
;
;Return:T1-Updated byte ptr for 1st string.
;	T2-Updated byte ptr for 2nd string.
;	P5-Updated byte count for 1st string.
;	P6-Updated byte count for 2nd string.
;
;Trash:	T3
;
SUPCMP:	DMOVEM	Q1,1(P)			;save ACs
	MOVEM	T4,3(P)
	ADJSP	P,3			;adjust P for later subroutine calls

;use the shortest length in comparision
SUPLEN:	MOVE	T3,P5			;assume P5 < P6
	CAMLE	P5,P6			;is P5 < P6?
	 MOVE	T3,P6			;no, use P6 instead

	MOVE	T4,T3			;get # of bytes to compare from T3

SUPLOP:	JUMPLE	T3,SUPEQ		;quit when no more bytes to compare
	CALL	CMPSTR##		;compare further
	 JRST	SUPNE			;find a mismatch

	SUB	P5,T4			;subtract # of byte compared
	SUB	P6,T4			;subtract # of byte compared
;this is the exit when two strings are found to be the same.
SUPEQ: 	AOS	-3(P)			;set up return+1 when two strings are matched

SUPEXT:	ADJSP	P,-3			;adjust P for ACs' restoration
	DMOVE	Q1,1(P)			;restore ACs
	MOVE	T4,3(P)
	RET				;retrun to caller


;To check whether the mismatch is caused by a tab, a space or new line (CR,LF),
;I must set both byte ptr back 1 position and set the byte counts.
SUPNE:	SUB	T4,T3			;get # of bytes which has compared from
					;previous comparision
	SUB	P5,T4			;subtract # of byte compared
	SUB	P6,T4			;subtract # of byte compared
	ADDI	P5,1			;adjust byte cnt of string 1
	ADDI	P6,1			;adjust byte cnt of string 2
	SETOB	Q1,Q2			;set Q1 & Q2 to -1
	ADJBP	Q1,T1			;adjust byte ptr back 1 byte for string1
	MOVE	T1,Q1			;save the adjusted byte ptr
	ADJBP	Q2,T2			;adjust byte ptr back 1 byte for string2
	MOVE	T2,Q2			;save the adjusted byte ptr

; get rid of all the leading spaces, tabs, nulls, and multiple blank lines
; which are causing the mismatch

	EXCH	P5,T2			;set up AC to call EATST
	CALL	EATST
	EXCH	P5,T2			;restore ACs

	EXCH	T2,P6			;set up ACs to work on string2
	EXCH	T1,P6
	CALL	EATST
	EXCH	T1,P6			;restore ACs
	EXCH	T2,P6

	LDB	Q1,T1			;get byte pointed by ptr1 in string1
	LDB	Q2,T2			;get byte pointed by ptr2 in string2
	CAME	Q1,Q2			;are they equal?
	 JRST	SUPEXT			;no, found a mismatch and exit routine

	JUMPLE	P5,SUPEQ		;quit when end of string1
	JUMPLE	P6,SUPEQ		;quit when end of string2
	JRST	SUPLEN			;go compare further
;===============================================================================
;This routine adjusts the byte ptr up to the point where the first
;valid character is encountered (valid characters are characterers other than
;space, tab, null, ^L, ^J, ^M)
;
;	CALL EATST
;
;Accept:T1-Byte ptr which points to the string for process.
;	T2-Number of bytes availiable in the string after the byte ptr.
;
;Return:T1-Updated byte ptr
;	T2-Updated byte count
;	T3-# of bytes ignored.
;	T4-Byte ptr to last ^J encountered, zero => no ^J encountered
;	Q1-Bytes left after ptr in T4
;
;Trash:	Q2
;
EATST:	SETZB	T4,Q1			;initialize counts
	MOVEM	T2,T3			;save byte count

EATST3:	JUMPLE	T2,EATST9		;quit when count reaches zero
	SUBI	T2,1			;adjust # bytes left in string
	ILDB	Q2,T1			;get the next byte
	CAIE	Q2," "			;is it a SPACE?
	 CAIN	Q2,"	"		;is it a TAB?
	  JRST	EATST3			;yes, loop back to process next byte
	CAIE	Q2,.CTRLM		;is it a carriage return?
	 CAIN	Q2,.CTRLL		;is it a ^L (new page)
	  JRST	EATST3			;yes, loop back to process next byte
	JUMPE	Q2,EATST3		;[02] ignore nulls
	CAIE	Q2,.CTRLJ		;is it a line feed?
	 JRST	EATST8			;no, found a non-separator
	DMOVEM	T1,T4			;save ptr to ^J + bytes after it
	JRST	EATST3			;loop back to process next byte

EATST8:	SUBI	T3,1			;correct count of bytes ignored
EATST9:	SUB	T3,T2			;calc # bytes ignored
	RET				;return to caller
;=============================================================================
;This routine will ignore all the leading tabs, spaces, nulls, ^L, and
;multiple blank lines before report the difference which exist at the end of
;the file.
;
;	CALL EATEOF
;
;Accept:No register
;
;Return:OP-Updated byte ptr for older file.
;	OB-Updated byte count for older file.
;	NP-Updated byte ptr for newer file.
;	NB-Updated byte count for newer file.
;
;TRASH:	NO register
;
EATEOF:	DMOVEM	T1,1(P)			;save the ACs
	DMOVEM	T3,3(P)
	DMOVEM	Q1,5(P)
	ADJSP	P,6			;adjust stack ptr before to do any call
	JUMPL	OB,EATNEW		;is there any byte left in older file?
	MOVE 	T1,OP			;load information into ACs
	MOVE	T2,OB
	CALL	EATST
	SUB	OB,T3
	MOVE	OP,T1
	SETO	T1,			;yes, set up AC to adjust byte ptr back
	ADJBP	T1,OP			;up one byte
	MOVE	OP,T1

EATNEW:	JUMPL	NB,EATFIN		;is there any byte left in newer file?
       	MOVE	T1,NP			;set up ACs to work on string2
	MOVE	T2,NB
	CALL	EATST			;call routine to eleminate leading tabs
					;spaces and new lines in string2.
	SUB	NB,T3
	MOVE	NP,T1			;restore ACs
	SETO	T1,			;yes, set up AC to adjust byte ptr back
	ADJBP	T1,NP			;up one byte
	MOVE	NP,T1

EATFIN:	ADJSP	P,-6			;adjust stack ptr
	DMOVE	T1,1(P)			;restore ACs
	DMOVE	T3,3(P)
	DMOVE	Q1,5(P)
	RET				;return to caller
;===============================================================================
;This routine will position the byte ptr to next non blank line to print.
;If the string is composed of multiple blank lines, this routine will return
;the ptr at the beginning of the last blank line.
;
;	CALL EALIN
;
;Accept:T2-byte ptr to file which is going to be searched.
;	T3-# of bytes after the byte ptr.
;
;Return:T2-Updated byte ptr
;	T3-Updated byte count
;
;Trashes: No register.
EATLIN:	DMOVEM	T1,1(P)			;save ACs
	DMOVEM	T3,3(P)
	DMOVEM	Q1,5(P)
	ADJSP	P,6			;adjust stack ptr
	MOVE	T1,T2			;set up ACs for EATST
	MOVE	T2,T3
	CALL	EATST			;ignore leading multiple new lines
	ADJSP	P,-6			;adjust stack ptr, before restore ACs
	JUMPN	T4,EATUP		;if T4 points at a ^J, go to EATUP.
	DMOVE	T1,1(P)			;restore original values for ACs
	DMOVE	T3,3(P)
	DMOVE	Q1,5(P)
	RET				;returns to caller

EATUP:	IFE.	T2			;is EATST eats up the whole line group?
	MOVNI	T2,2			;set up ACs to perform adjustment
	MOVE	T3,T2			;
	ADJBP	T2,T4			;adjust T2 to point to last blank line
	SUB	T3,Q1			;calculate number of byte after T2
ELSE.
				; we have eliminate the leading blank line(s)
	MOVE 	T2,T4			;point to the rest of the line.
	MOVE	T3,Q1			;get number of byte after last ^J
ENDIF.
	MOVE	T1,1(P)			;restore the rest of the ACs
	MOVE	T4,4(P)
	DMOVE	Q1,5(P)
	RET				;return to caller
;=============================================================================
;Routine to pmap a file into memory.
;	CALL PMP	;map starting at file page FPG into pmap area
;	CALL NXTPMP	;map in next group of file pages into pmap area
;ACCEPTS:
;	CX - address of the FDA (File Data Area)
;RETURNS:
;    +1 always with:
;	T1 - trashed
;	T2 - byte pointer to start of pmap area
;	T3 - # of bytes of file in the pmap area
;	EOF(CX) -updated
;	TOB(CX) -updated
;Trashes T1

NXTPMP:	HRRZ	T1,FLP(CX)		;get length of pmap buffer
	ADDM	T1,FPG(CX)		;add current page at start of buffer...
					; ...so next pmap will start here
PMP:	DMOVE	T2,FFP(CX)		;initialize words for pmap
	HRRZ	T1,T3			;# pages to map in
	ADD	T1,FPG(CX)		;add file page to start at
	SUB	T1,PGL(CX)		;sub length of file in pages
	MOVNM	T1,EOF(CX)		;save end of file flag
	SKIPLE	T1			;enough left to fill pmap area?
	 SUB	T3,T1			;no, map in only what we need to
	HRL	T1,JFN(CX)		;get JFN,,0
	HRR	T1,FPG(CX)		;JFN,,page of file to start mapping at
	PMAP%
	 JERR (?,,PC,DIE)
	TLZ	T3,-1			;get # pages I mapped in
	IMUL	T3,BPP(CX)		;calc # of bytes of file in pmap area
	SKIPG	EOF(CX)			;am I at the end of file?
	 SUB	T3,BLF(CX)		;yes, sub # of free bytes on last page
	MOVEM	T3,TOB(CX)		;save total bytes in pmap area
	LSH	T2,9			;convert page number to address
	HLL	T2,BSZ(CX)		;make pointer to start of pmap area
	RET				;return to caller
;=============================================================================
;Routine to search backward for a ^J. If the search takes me to the beginning
;of the pmap buffer area then I'll bring in the previous page of the file and
;continue searching. If this routine reaches the beginning of the file then
;it will stop
;	CALL FBACKJ
;ACCEPTS:
;	T1 - byte pointer
;	T2 - number of bytes left after pointer in pmap buffer
;	CX - address of the FDA (File Data Area)
;	TOB - total # bytes in pmap buffer
;RETURNS:
;	+1 - always with T1,T2 updated
;Trashes none

FBACKJ:	MOVN	T2,T2			;negate # bytes in pmap area after ptr
	ADD	T2,TOB(CX)		;calc # bytes in pmap area before ptr
	CALL	BACKJ			;search backward for a ^J
	 JRST	FBACK5			;didn't find one - check it out
FBACK3:	MOVN	T2,T2			;negate # bytes in pmap area before ptr
	ADD	T2,TOB(CX)		;calc # bytes in pmap area after ptr
	RET

; gets here when BACKJ failed to find ^J in pmap area. At this point T2 must
; be zero and I must be a beginning of pmap buffer for the file. If it's not
; the first page of the file then map in a previous page of the file and then
; loop back to keep on looking for a ^J

FBACK5:	SKIPG	FPG(CX)			;am I on the first page of the file
	 JRST	FBACK3			;yes, don't back up any more
	PUSH	P,T3			;save a register
	SOS	FPG(CX)			;back up one page of the file
	CALL	PMP			;do it
	ADDI	T2,1K			;increment ptr returned by PMP by 1 page
	SUB	T3,BPP(CX)		; ... and decrement bytes left by 1 page
	DMOVE	T1,T2
	POP	P,T3			;restore register
	JRST	FBACKJ			;loop back and continue looking
;=============================================================================
;Routine to search backward for a ^J. If the byte pointer supplied is already
;pointing to a ^J it is ignored and a search will be for a previous one.
;(NOTE: be careful when modifying this routine because you should keep the
; loop as short as possible and must not try to load the byte pointed to by
; the "base byte pointer" because it may not exist)
;	CALL BACKJ
;ACCEPTS:
;	T1 - byte pointer
;	T2 - number of bytes I can back up (include byte T1 points to in count)
;RETURNS:
;	+1 - reached backup limit before I found a ^J. T1 will be pointing to
;		the byte before the last byte processed, T2 will be zero.
;	+2 - with T1 pointing to the ^J and T2 updated
;Trashes none

BACKJ:	JUMPLE	T2,BACKJ9		;quit if T2 already zero
	DMOVEM	T3,1(P)			;save T3,T4
	MOVN	T3,T2
	ADJBP	T3,T1			;make base byte pointer
	SOJE	T2,BACKJ7		;ignore byte T1 is now pointing to
BACKJ2:	MOVE	T1,T2			;set up for ADJBP to gets last byte
	ADJBP	T1,T3			;back pointer up another byte
	LDB	T4,T1			;get byte
	CAIE	T4,.CTRLJ		;is it a <lf>
	 SOJG	T2,BACKJ2		;no, back up one more byte and try again
	JUMPE	T2,BACKJ7		;jump if I ran out of bytes
	AOSA	(P)			;found a ^J - set +2 return and skip...
					; ...because T1 is pointing to the ^J
BACKJ7:	 MOVE	T1,T3			;use base byte pointer
	DMOVE	T3,1(P)			;restore T3,T4
BACKJ9:	RET
;=============================================================================
;Routine to do a pmap (if necessary) so that the place in the file pointed
;to by PTR, BAP and PFP will be in the pmap buffer
;	CALL PTRPMP
;ACCEPTS:
;	CX - address of the FDA (File Data Area)
;RETURNS:
;	+1 - with PTR, BAP, PFP and pmap area for file updated
;Trashes T1-T3

PTRPMP:	MOVE	T1,PFP(CX)		;get pointer file page
	CAMN	T1,FPG(CX)		;is this page still starting buffer?
	 RET				;yes, I'm done
	DMOVE	T2,PTR(CX)		;get ptr + byte count
	CALL	CKPTR			;is this area of file in pmap buffer?
	 IFSKP.				;no, I'll have to map it in
	MOVEM	T1,PFP(CX)		;yes, save update pointer file page
	DMOVEM	T2,PTR(CX)		;save updated ptr + bytes after ptr
	RET				;done
ENDIF.

; if I have to remap the file buffer area I might as well remap it so that the
; page the PTR points to is the 1st page of the pmap buffer area because if
; the PTR points to a page near the end of the buffer area then another
; remap would have to be done again shortly - this logic would save me from
; having to do that 2nd remap so soon.

	HRRZ	T3,FLP(CX)		;get length of pmap area
	ADD	T3,PFP(CX)		;add ptr's file page
	SUB	T3,PGL(CX)		;calc # file pages to be after buffer
	MOVEM	T1,FPG(CX)		;have PMP start with this page
	JUMPGE	T3,PMP			;if next pmap will bring last page...
			; ...of file into buffer then don't bother adjusting

; do page adjusments so PTR will point to first page in pmap buffer
	PUSH	P,T4			;save register
	MOVE	T2,PTR(CX)		;get byte ptr
	IBP	T2			;adjust to point to actual byte...
					; ...incase ptr on page boundry
	TLZ	T2,-1			;isolate address of ptr
	LSH	T2,-9			;get page ptr is on
	HRRZ	T3,FFP(CX)		;get page # of start of pmap buffer
	SUB	T2,T3			;calc page offset
	MOVEM	T2,T4			;save offset
	ADD	T2,PFP(CX)		;calc page to start pmaping at
	JUMPL	T2,[	CALL	CKBOF	;check for beginning of file
			 JRST	[TMSGL <?Attempt made to backup beyond beginning of file>
				JRST DIE]
			SETZB	T2,T4	;PTR(CX) must refer to nonexistant...
			JRST	.+1]	; ...right before beginning of file
	MOVEM	T2,PFP(CX)		;save this new pointer file page
	MOVEM	T2,FPG(CX)		;have next PMP start with this page

	MOVN	T1,T4			;get offset
	LSH	T1,9			;put page # at proper place
	ADDM	T1,PTR(CX)		;adjust ptr

	HRRZ	T1,FLP(CX)		;get length of pmap area
	SUB	T4,T1			;calc # pages from PTR to end of buffer
	IMUL	T4,BPP(CX)		;convert pages to bytes
	ADD	T4,BAP(CX)		;calc # bytes from PTR to start of page
	CALL	PMP			;get some more data
	ADD	T4,T3			;calc # bytes from ptr to end of buffer
	MOVEM	T4,BAP(CX)		;save it
	POP	P,T4			;restore register
	RET
;=============================================================================
;Routine to check to see if a file relative byte pointer is in the pmap buffer
;for the file. If it is the file page number, the byte ptr, and the bytes
;of file after ptr will be adjusted accordingly
;	CALL CKPTR
;ACCEPTS:
;	T1 - pointer file page (see PFP)
;	T2 - byte pointer (see PTR)
;	T3 - bytes in pmap buffer after ptr (see BAP)
;	CX - address of the FDA (File Data Area)
;RETURNS:
;	+1 - data being pointed to is NOT in pmap buffer area
;	+2 - data is in pmap buffer area. T1,T2,T3 will be updated
;Trashes none

CKPTR:	AOS	(P)			;assume +2 return
	CAMN	T1,FPG(CX)		;am I on the same page?
	 RET				;yes, return+2
	DMOVEM	T4,1(P)			;save some registers
	MOVE	Q1,T2			;get byte pointer
	IBP	Q1			;adjust to point to actual byte...
					; ...incase ptr on page boundry
	TLZ	Q1,-1			;isolate address of ptr
	LSH	Q1,-9			;isolate page # from the address
	HRRZ	T4,FFP(CX)		;get first page of pmap buffer
	SUB	Q1,T4			;calc page offset of ptr
	ADD	Q1,T1			;calc actual file page ptr is on
	HRRZ	T4,FLP(CX)		;get # pages in pmap buffer
	ADD	T4,FPG(CX)		;calc last file page in buffer
	CAML	Q1,FPG(CX)		;if file page the ptr is on...
	 CAML	Q1,T4			; ...is not in buffer then...
	  JRST	CKPTR9			; ...return +1

; file data being pointed to by T1-T3 is in pmap buffer so readjust ptr in T2
	MOVEM	T1,T4
	SUB	T4,FPG(CX)		;calc offset for pointer
	LSH	T4,9			;make it a page offset
	ADD	T2,T4			;adjust the supplied byte pointer

; and also readjust # bytes after ptr which is in T3
	HRRZ	T4,FLP(CX)		;get # pages in pmap buffer
	ADD	T1,T4			;calc file page after buffer
	CAML	T1,PGL(CX)		;was EOF in buffer?
	 ADD	T3,BLF(CX)		;yes, adjust # bytes
	IDIV	T3,BPP(CX)		;calc # bytes from ptr to end of page
	SUB	T4,BPP(CX)		;calc -(#bytes from page start to ptr)
	SUB	Q1,FPG(CX)		;calc # pages into buffer new ptr is on
	IMUL	Q1,BPP(CX)		;calc # bytes this represents
	MOVN	T3,Q1
	ADD	T3,TOB(CX)		;calc # bytes after page ptr is on
	ADD	T3,T4			;calc # bytes after ptr

; and also readjust pointer file page in T1
	MOVE	T1,FPG(CX)		;set T1 to be consistent with T2,T3
	TRNA				;take +2 return
CKPTR9:	 SOS	(P)			;set +1 return
	DMOVE	T4,1(P)			;restore registers
	RET
;=============================================================================
;Routine to try to find where the OLDer and NEWer files match up again.
;The logic is as follows: Get the next L4M lines from the OLDer file and
;see if this line group matches the first FMALCT lines from the NEWer file
;If no match then get the next L4M lines from the NEWer file and see if
;it matches the first FMALCT lines of the OLDer file. If no match then increase
;the value of FMALCT and do the whole process again.
;	CALL FMATCH
;ACCEPTS:
;	OP - pointer to beginning of line for OLDer file
;	OB - bytes left in pmap area after pointer for OLDer file
;	NP - pointer to beginning of line for NEWer file
;	NB - bytes left in pmap area after pointer for NEWer file
;	L4M - # lines required for a match
;	L2S - maximum # lines to search for a match before giving up
;RETURNS:
;	+1 - files did not match up again because L2S limit reached (T4 will
;		be < zero) --OR-- because EOF of both files were reached
;	+2 - a match was found
;
;		PTR, BAP, PFP - will be updated for both files and will contain
;			the ptr data supplied when FMATCH was callled
;      		Q1 contains the byte length of matching line for NEW area
;      		Q2 contains the byte length of matching line for OLD area
;
;		OP,OB,NP,NB	-updated ptr and # bytes after pointer. They
;			 will point to the end of the matching line group
;			 for each file
;
;Trashes T1-P6

FMATCH:	DMOVEM	OP,PTR+OLD		;save ptr + bytes left for OLDer file
	MOVE	Q2,FPG+OLD		;get current file page starting buffer
	MOVEM	Q2,PFP+OLD		;save current ptr's file page
	SETZM	LBYG+OLD		;initialize # bytes in line group

	DMOVEM	NP,PTR+NEW		;save ptr + bytes left for NEWer file
	MOVE	Q2,FPG+NEW		;get current file page starting buffer
	MOVEM	Q2,PFP+NEW		;save current ptr's file page
	SETZM	LBYG+NEW		;initialize # bytes in line group

	MOVEI	Q2,1
	MOVEM	Q2,FMALCT		;initialize # of lines to search
	MOVEI	CX,NEW
	MOVEM	CX,FMAFDA		;save FDA for file
	MOVEI	CX,OLD
	TXO	F,F%LSTK		;initialize flags

FMATC3:	CALL	FMAFIL			;try to find next line from OLD in NEW
	 JRST	FMATC8			;found where file match up again
	JUMPL	T4,FMATC9		;jump if L2S limit exceeded
	JUMPG	Q2,FMATC6		;jump if not EOF of OLDer file
	SKIPG	LBYG+NEW		;EOF of NEWer file as well?
	 JRST	FMATC9			;yes, quit
FMATC6:	AOS	FMALCT			;increase lines to search
	EXCH	CX,FMAFDA		;switch FDA from OLDer to NEWer file
	EXCH	OP,NP			;exchange byte ptrs and...
	EXCH	OB,NB			;  ...byte counts for call to FMAFIL
	CALL	FMAFIL			;try to find next line from NEW in OLD
	 TXO	F,F%NO			;found it
	EXCH	CX,FMAFDA		;switch FDA from NEWer to OLDer file
	EXCH	OP,NP			;exchange byte ptrs and...
	EXCH	OB,NB			;  ...byte counts so I'm back to normal
	TXZE	F,F%NO			;did I find where files match up again?
	 JRST	FMATC8			;yes
	JUMPL	T4,FMATC9		;jump if L2S limit exceeded
	JUMPG	Q2,FMATC3		;jump if not EOF of NEWer file
	SKIPG	LBYG+OLD		;EOF of OLDer file as well?
	 JRST	FMATC9			;yes, quit
	JRST	FMATC3			;loop back to try some more

FMATC8:	AOS	(P)			;set +2 return
FMATC9:	TXZ	F,F%LSTK		;initialize flags
	RET
;=============================================================================
;Routine to get a line from one file (pointed to by CX) and search for it
;in another file (pointed to by FMAFDA). The search region is given by the
;line count in FMALCT
;	CALL FMAFIL
;ACCEPTS: -see FMATCH. This routine was designed to be called only by FMATCH
;RETURNS:
;	+1 - found where files match up again
;	+2 - couldn't find where files match up again. If Q2 = -1 then
;		reason is because EOF(CX) was reached. If T4 = -1 then reason
;		is L2S count was exceeded
FMAFIL:	DMOVE	Q2,LBYG(CX)		;get # bytes in line group...
					; ...and stack pointer in Q3
	JUMPE	Q2,FMAINI		;do initialization if necessary...
FMAFI0:					; ...and return to here

; this loop will get a line group from the 1st file. Each time it is executed
; it will move the line group down by one line. This line group will then be
; looked for in the 2nd file

FMAFI1:	AOBJP	Q3,LSTKOF		;increment LSTK pointer
	MOVN	Q1,L4M			;get # of lines required for a match
	ADD	Q1,Q3			;adjust to beginning of last line group
	CAIN	CX,OLD
	 HRRZ	Q1,LSTK(Q1)		;get # bytes in this line of OLDer file
	CAIN	CX,NEW
	 HLRZ	Q1,LSTK(Q1)		;get # bytes in this line of NEWer file
	SUB	Q2,Q1			;remove count from total bytes
	MOVN	Q1,Q2
	ADJBP	Q1,OP			;make ptr to start of next line group

	MOVE	T1,FPG(CX)		;get current file page starting buffer
	CAME	T1,LPFP(CX)		;has it changed?
	 CALL	FMAPMP			;yes, need to readjust some ptrs

	CALL	MSCANJ			;look for next ^J

	EXCH	CX,FMAFDA		;switch around FDA's
	DMOVEM	NP,FMAPTR		;save old ptr + bytes incase FINDL fails
	CALL	PTRPMP			;go back to beginning of difference
	DMOVE	NP,PTR(CX)		;start searching from here
	MOVE	T4,FMALCT		;initialize # lines to search
	CAML	T4,L2S			;have I exceeded the limit
	 CALL	L2SEXC			;yes, see if user wants to continue
	  CALL	FINDL			;search for line group in other file
	   TXO	F,F%NO			;couldn't find it
	EXCH	CX,FMAFDA		;switch around FDA's
	TXZN	F,F%NO			;did I find where files match up again?
	 RET				;found it, return +1
	DMOVE	NP,FMAPTR		;restore previous ptr + bytes
; checking T4 will tell me whether or not FINDL quit because FMALCT was
; exhausted or EOF was reached. If EOF was reached then there is no need to
; return to the calling routine to switch files around since there are no
; more lines in the other file that need to be processed.
	JUMPLE	T4,FMAFI8		;jump if EOF of file NOT reached
	AOS	FMALCT			;increase lines to search
	JRST	FMAFI1			;loop back to process more lines

FMAFI8:	DMOVEM	Q2,LBYG(CX)		;save # bytes in line group + stack ptr
FMAFI9:	AOS	(P)			;set +2 return
	RET
;=============================================================================
;Routine to search for the next ^J in the update file
;	CALL MSCANJ
;ACCEPTS:
;	Q1 - byte pointer to beginning of line group
;	Q3 - pointer to next free location in LSTK
;	OP,OB - ptr + bytes left after ptr to end of line group
;RETURNS: +1 unless EOF reached
;
;Trashes:T1-T4
MSCANJ:	DMOVE	T1,OP			;get ptr + bytes left after ptr
	MOVE	T3,T2			;save initial length of string
MSCAN0:	TXNN	F,F%IGNO		;is user wants /IGNORE
	 JRST	MSCAN1			;no, go scan for next ^J
	CALL	EATJ			;set the ptr at 1st non-blank line
	 JRST	MSCAN1			;ptr is already at the proper position

; gets here when the line I was on was blank (only spaces,tabs,nulls,^L's). So
; I will add the number of bytes skiped over to the previous line in LSTK. If
; this is the first time MSCANJ was called (from FMAINI) then the first word
; of LSTK will be trashed - thats ok because it's not used anyway
	SUB	T3,T2			;get # of bytes moved forward
	CAIE	CX,OLD			;are we looking at the OLD file?
	 HRLZ	T3,T3			;no, set data at the correct position
	ADDM	T3,LSTK-1(Q3)		;add the data to the previous LSTK
	MOVE	T3,T2			;move the new byte cnt to T3

MSCAN1:	CALL	SCANJ			;look for next ^J
	 JRST	MSCAN4			;search failed - check out why
MSCAN2:	DMOVEM	T1,OP			;save ptr + bytes left after ptr
	SUB	T3,T2			;calc number of bytes in line
	ADD	Q2,T3			;calc # bytes in line group
	CAIN	CX,OLD
	 HRRM	T3,LSTK(Q3)		;save # bytes in this line of OLDer file
	CAIN	CX,NEW
	 HRLM	T3,LSTK(Q3)		;save # bytes in this line of NEWer file
	RET

; gets here when SCANJ couldn't find a ^J in the file. It failed because
; either it reached the EOF or end of data in pmap area for file.
MSCAN4:	SKIPLE	EOF(CX)			;end of file?
	 IFSKP.				;no
	CALL	MSCAN2			;yes, save the data collected
	ADJSP	P,-1			;remove call to MSCANJ from stack
	SETO	Q2,			;set flag to indicate EOF reached
	JRST	FMAFI8			;quit
ENDIF.
	PUSH	P,T3			;save # bytes searched so far
	MOVE	T1,Q1			;get ptr to start of current line group
	CALL	SU4PMP			;setup for next call to PMP
	ADDM	T1,Q1			;adjust byte pointer
	MOVE	T1,FPG(CX)		;get current file page starting buffer
	MOVEM	T1,LPFP(CX)		;save current ptr's file page
	EXCH	OP,NP			;exchange byte ptrs and...
	EXCH	OB,NB			;  ...byte counts for call to SUPMP5
	CALL	SUPMP5			;setup for PMP and get some more data
	EXCH	OP,NP			;exchange byte ptrs and...
	EXCH	OB,NB			;  ...byte counts so I'm back to normal
	POP	P,T3			;get # bytes searched so far
	ADD	T3,T2			;calc new value
	JRST	MSCAN0			;continue search for ^J


;===============================================================================
;This routine will position the byte ptr to next ^J and skips all leading
;tabs, spaces, ^Ls, LFs and multiple blank lines (CR-LF).
;
;	CALL EATJ
;
;Accept:T1-byte ptr to file which is going to be searched.
;	T2-# of bytes after the byte ptr.
;
;Return:T1-Updated byte ptr.
;	T2-Updated byte count
;
;	+1-the line is a non-blank line, or cannot find next ^J.
;	+2-a new ^J is found.
;
;Trashes: No register.
EATJ:	DMOVEM	T1,1(P)			;save ACs
	DMOVEM	T3,3(P)
	DMOVEM	Q1,5(P)
	ADJSP	P,6			;adjust stack ptr
	CALL	EATST			;ignore spaces, tabs, etc.
	ADJSP	P,-6			;adjust stack ptr, before restore ACs
	JUMPE	T4,EATJNO		;does T4 point to a ^J?
	MOVE	T1,T4			;yes save new values in ACs
	MOVE	T2,Q1
	AOSA	(P)			;set retun +2
EATJNO:	 DMOVE	T1,1(P)			;no, restore original values for ACs
	DMOVE	T3,3(P)
	DMOVE	Q1,5(P)
	RET				;returns to caller
;=============================================================================
;Routine to adjust pointers and remap the file buffer (if necessary so as to
;return to a previous location in the file
;	CALL	FMAPMP
;ACCEPTS:
;	Q1 - byte pointer to start of line group
;	LPFP	- pointer file page for pointer in Q1
;RETURNS: +1 always

FMAPMP:	PUSH	P,PFP(CX)		;save current ptr's file page
	PUSH	P,PTR(CX)		;save current ptr
	PUSH	P,BAP(CX)		;save current bytes after ptr
	MOVE	T1,LPFP(CX)		;get line stack ptr file page
	MOVEM	T1,PFP(CX)
	MOVEM	Q1,PTR(CX)		;use pointer to start of line group
	CALL	PTRPMP			;go to this location in the file
	POP	P,BAP(CX)		;restore current bytes after ptr
	POP	P,PTR(CX)		;restore current ptr
	POP	P,PFP(CX)		;restore current ptr's file page
	MOVE	T1,LPFP(CX)		;get line stack ptr file page
	DMOVE	T2,OP			;get ptr to end of line region
	CALL	CKPTR			;is end of line region in pmap buffer?
	 JRST	FMADJ9			;no
	DMOVEM	T2,OP			;save ptr + byte count (incase changed)
	MOVEM	T1,LPFP(CX)		;save current ptr's file page
	RET

; since end of line group is not presently in pmap buffer but start of line
; group is I must remap so that all of line region is in the pmap buffer
FMADJ9:	MOVE	T1,LPFP(CX)		;get line stack ptr file page
	MOVEM	T1,FPG(CX)		;make next pmap start with this page
	CALLRET	PMP			;get some more data
;=============================================================================
;Routine to do the initialization necessary for FMAFIL routine
;	JRST FMAINI
;ACCEPTS: no registers
;RETURNS: to FMAFI0 always

FMAINI:	CALL	PTRPMP			;make sure I'm at beginning of change
	DMOVE	OP,PTR(CX)		;get ptr + bytes in case they changed
	MOVE	T1,FPG(CX)		;get current file page starting buffer
	MOVEM	T1,LPFP(CX)		;save current ptr's file page

; now initialize LSTK stack

	MOVN	Q3,L4M			;set up for AOBJN instruction
	LSH	Q3,^D18
	ADDI	Q3,1			;set Q3 to 1
	MOVE	Q1,OP			;initialize for MSCANJ
	SETZ	Q2,			;initialize line byte counts
FMAIN1:	CALL	MSCANJ			;look for next ^J
	AOBJN	Q3,FMAIN1		;loop until stack initialized

	MOVE	T1,L4M			;reinitialize ptr for LSTK
	SUBI	T1,LSTKLN
	SUBI	Q3,1
	HRL	Q3,T1
	JRST	FMAFI0			;return to main FMAFIL routine
;=============================================================================
;Routine searches for a line (or lines) in a file.
;	CALL FINDL
;	CALL FINDLI	;same as FINDL but initializes PFP,PTR,BAP
;	CALL FINDCK	;same as FINDLI but checks for BOFM and EOFM markers
;ACCEPTS:
;	T4 - maximum number of lines to search before giving up
;	Q1 - byte pointer to start of line
;	Q2 - # bytes in line
;	NP - pointer to where in pmap buffer to start searching
;	NB - bytes left in pmap area after pointer
;	CX - address of the FDA (File Data Area)
;RETURNS:
;	+1 - line could not be found. Either EOF reached (T4 will be > 0) or
;		maximum lines to search was reached (T4 will be zero).
;	+2 - found the line
;	     Q1 - # bytes in line group of NEWer file
;	     Q2 - # bytes in line group of OLDer file
;
;Trashes T1-T3 & Q1

FINDCK:	CALL	FIBEOF			;check for BOFM and EOFM markers
	 JRST	FINDL			;PTR, BAP, PFP already intialized
FINDLI:	DMOVEM	NP,PTR(CX)		;save ptr + bytes left
	MOVE	T1,FPG(CX)		;get current file page starting buffer
	MOVEM	T1,PFP(CX)		;save current ptr's file page

FINDL:	CAMG	Q2,NB			;enough bytes left in file?
	 IFSKP.				;yes
	CALL	SUPMPE			;setup for PMP and get some more data
	 RET				;end of file, so match is impossible
ENDIF.
	MOVE	T1,NP			;get ptr to start of line in file
	DMOVE	T2,Q1			;get ptr to start of line + length
	TXNE	F,F%IGNO
	 IFSKP.
	CALL	CMPSTR##		;see if line(s) match
	 TRNA				;no match yet - try next line
	  JRST	FINDL9			;found where file match up again
ELSE.
	MOVE	P5,NB
	MOVE	P6,Q2
	CALL	SUPCMP
	 TRNA				;no match yet - try next line
	  JRST	FINDL9			;found where file match up again

; no match found so get another line from the file and try that one
ENDIF.
	SOJE	T4,FINDL8		;quit when max # lines to search reached
	CALL	FSCANJ			;get next line from file
	JRST	FINDL			;keep on searching

; program gets here when I've found the line in the file
FINDL9:	MOVEM	T1,NP			;save ptr to end of match
	TXNE	F,F%IGNO
	 IFSKP.				;yes
	SUB	NB,Q2			;adjust # bytes left after pointer
	MOVE	Q1,Q2			;move # bytes left after pointer
ELSE.
	SUB	NB,P5			;calculate # of byte in region of common
	MOVE	Q1,NB			;save it
	MOVE	NB,P5			;store the updated byte count in NB
	CAIE	CX,NEW			;does CX points to the NEWer file?
	 EXCH	Q1,Q2			;no, switch around byte counts
ENDIF.
	AOS	(P)			;set +2 return
FINDL8:	RET
;=============================================================================
;Routine to get another line from the CX file.
;	CALL FSCANJ
;ACCEPTS:
;	NP,NB - containing ptr + bytes left after ptr
;RETURNS: +1 always unless EOF with T1-T2 as left by SCANJ

FSCANJ:	TXNN	F,F%LSTK		;is LSTK initialized?
	 JRST	FSCAN5			;no, use SCANJ to find next line

	MOVE	T1,FMALCT		;calc index for LSTK
	SUB	T1,T4
;;	SUBI	T1,1
;;; due to other changes in the program I no longer need to test of this
;;;	HRRZ	T2,LBYG+1(CX)		;get location of top of LSTK stack
;;;	CAMLE	T1,T2			;if past the top of LSTK stack...
;;;	 JRST	FSCAN5			; ...then can't use stack
	CAIN	CX,OLD
	 HRRZ	T1,LSTK(T1)		;get # bytes in this line of OLDer file
	CAIN	CX,NEW
	 HLRZ	T1,LSTK(T1)		;get # bytes in this line of NEWer file
	CAMG	T1,NB			;enough bytes left in pmap buffer?
	 JRST	FSCAN3			;yes
	PUSH	P,T1			;save adjustment
	CALL	SUPMPE			;setup for PMP and get some more data
	 JRST	FSCAN7			;end of file encountered - so quit
	POP	P,T1			;restore adjustment
FSCAN3:	MOVN	T2,T1
	ADD	T2,NB			;adjust # bytes after ptr
	ADJBP	T1,NP			;adjust ptr to start of next line
	DMOVEM	T1,NP			;save ptr + bytes left
	RET

; program jumps here to look for next line using SCANJ routine
FSCAN5:	DMOVE	T1,NP			;get ptr + bytes left after ptr
FSCAN6:	CALL	SCANJ			;look for next ^J
	 IFSKP.				;search failed - check out why
	DMOVEM	T1,NP			;save ptr + bytes left
	RET
ENDIF.
	CALL	SUPMPE			;setup for PMP and get some more data
	 JRST	FSCAN8			;end of file encountered
	JRST	FSCAN6			;continue search for ^J
FSCAN7:	ADJSP	P,-1			;remove T1 saved on stack
FSCAN8:	ADJSP	P,-1			;remove call to FSCANJ from stack
	RET				;have FINDL routine return +1
;===========================================================================
;Routine to look for the next ^J <line-feed> character
;	CALL SCANJ
;ACCEPTS:
;	T1 - byte pointer to string
;	T2 - length of string
;RETURNS:
;	+1 - no ^J found (T2 will be zero)
;	+2 - ^J found (T2 will be decremented)
;Trashes no registers

SCANJ:	DMOVEM	T1+2,1(P)		;save T1+2,T1+3
	DMOVEM	T1+4,3(P)		;save T1+4,T1+5
	EXCH	T1,T2			;put byte ptr and length in proper place
	MOVEI	T1+3,1			;write one byte if source runs out first
	MOVEI	T1+4,T1+4		;write nothing here if source runs out
	SETZB	T1+2,T1+5		;initialize for local byte pointers
	EXTEND	T1,[MOVST JTBL]		;look for the next ^J
	 AOS	(P)			;^J found so set +2 return
	EXCH	T1,T2			;put byte ptr and length in proper place
	DMOVE	T1+2,1(P)		;restore T1+2,T1+3
	DMOVE	T1+4,3(P)		;restore T1+4,T1+5
	RET

;-----------------------------------------------------------------------------
	MVSTLN==^o200/2		;length of table for MOVST instruction
DEFINE	MAKTBL,<
;;	macro to generate the translation table for the MOVST instruction
;;	it will generate a table to cause the MOVST instruction to halt on
;;	line-feed (^J)
	XLIST
	$CHAR==0
	REPEAT MVSTLN,<
		$WORD==0		;;initialize the word
		IFE <$CHAR-.CTRLJ>,<$WORD==<1B2>>	;;break on ^J
		EXP	$WORD		;;assemble the word
		$CHAR==$CHAR+2		;;go on to next 2 bytes
	>
	LIST
	PURGE $WORD,$CHAR	;;purge temporary symbols
>;end of MAKTBL

JTBL:	MAKTBL			;^J break table
;=============================================================================
;Routine to set up to for and do a call to PMP. The location of the file
;pointed to by NP will not be mapped out when the next PMP is done. Normally
;this routine is called when you've reached the end of the pmap area and
;want to get some more data but don't want to lose the current line you are
;working on (pointed to by NP).
;	CALL SUPMP
;	CALL SUPMPE	-same as SUPMP but will return +2 if NOT end-of-file
;	CALL SUPMP5	-alternate entry point
;ACCEPTS:
;	NP,NB - pointer and bytes after pointer in pmap area
;	CX - address of the FDA (File Data Area)
;RETURNS:
;	+1 with NP,NB adjusted so they will point to the data they did before
;		T1,T2 will be the ptr and bytes after pointer that use to
;		refer to the byte at the end of the the pmap buffer.

SUPMPE:	SKIPG	EOF(CX)			;end of file?
	 RET				;yes, return +1
	AOS	(P)			;no, set +2 return
SUPMP:	MOVE	T1,NP			;no, get ptr to start of line
	CALL	SU4PMP			;setup for next call to PMP
	ADDM	T1,NP			;adjust ptr
SUPMP5:	PUSH	P,T2			;save page adjusment
	CALL	PMP			;get some more data
	POP	P,T1			;restore page adjustment
	LSH	T1,9			;make offset adjustment for byte ptrs
	ADD	T1,T2			;adjust ptr returned by PMP
	MOVE	T2,1(P)			;get page adjustment
	IMUL	T2,BPP(CX)		;calc byte adjustment
	SUBM	T3,T2			;adjust # bytes returned by PMP
	ADDM	T2,NB			;calc # bytes left after ptr
	RET
;-----------------------------------------------------------------------------
;Routine to set up to for another call to PMP. It will adjust FPG(CX) so that
;next call to PMP will map in the file page pointed to by T1 but this page
;will be the first page in the pmap area and T1 will be adjusted so that
;it will refer to the location in the file it pointed to previously
;	CALL SU4PMP
;ACCEPTS:
;	T1 - byte ptr (refers to byte before actual byte you want)
;	CX - address of the FDA (File Data Area)
;RETURNS:
;	+1 - FPG(CX) ajusted so ptr supplied in T1 will be on 1st page of
;		pmap buffer. T1 will contain the ptr adjusment necessary to
;		readjust byte pts and T2 will contain a page adjustment
;		which you may need to use after PMP is called
;Trashes none

SU4PMP:	IBP	T1			;adjust to point to actual byte...
					; ...incase ptr on page boundry
	HRRZ	T2,T1			;get ptr to use
	LSH	T2,-9			;get page ptr is on
	SUB	T2,FFP(CX)		;calc page offset
	HRRES	T2			;remove triva from LH
	JUMPLE	T2,[TMSGL <?Can't fit region in pmap buffer. Perhaps lines too long or buffer too small or not an ASCII file>
		JRST	DIE]
	ADDM	T2,FPG(CX)		;add offset to current file page...
					; ...and save it for next call to PMP
	MOVNS	T1,T2			;save page offset
	LSH	T1,9			;make offset adjustment for byte ptrs
	ADD	T2,FLP(CX)		;calc page adjustment
	HRRES	T2			;remove triva from LH
	RET
;=============================================================================
;Routine to check for the BOFM or EOFM markers in the text region pointed
;to by Q1,Q2
;	CALL FIBEOF
;ACCEPTS:
;	(same as FINDLI)
;RETURNS:
;	+1 - only difference between this and +2 return is that PTR, BAP and PFP
;		have been already initialized
;	+2 - the BOFM or EOFM was found - F%BOFM, or F%EOFM will be set
;		and T4,Q1,Q2,NP,NB maybe adjusted
;Trashes T1-T3

FIBEOF:	AOS	(P)			;set +2 return
	SKIPG	CHGCNT			;is this the 1st change to be made?
	 JRST	FIBEO5			;yes, check for BOFM
FIBEO1:	CALL	CKEOFM			;check for EOFM marker in text region
	 RET				;it's not there
	TXO	F,F%EOFM		;set flag, EOFM marker found
	HRRZ	T2,FLP(CX)		;get length of pmap area
	CAMLE	T2,PGL(CX)		;is file smaller than pmap area
	 MOVE	T2,PGL(CX)		;yes, use smaller value
	MOVN	T1,T2			;negate it
	ADD	T1,PGL(CX)		;calc page to start pmaping at
	IMUL	T2,BPP(CX)		;calc # bytes in pmap area
	SUB	T2,BLF(CX)		;sub # free bytes on the last page
	SUB	T2,Q2			;sub length of text region
	HRRZ	T4,FFP(CX)		;get first page of pmap area
	LSH	T4,9			;convert page number to address
	HLL	T4,BSZ(CX)		;make pointer to start of pmap area
	ADJBP	T2,T4			;adjust pointer to last byte of file
	MOVE	T3,Q2			;get # bytes left in file after ptr
	MOVEM	T1,PFP(CX)		;save pointer file page
	DMOVEM	T2,PTR(CX)		;save ptr + bytes after pointer
	PUSH	P,FPG(CX)		;save current file page I'm on
	CALL	PTRPMP			;map last page of file into pmap area
	POP	P,PFP(CX)		;initialize ptr's file page
	EXCH	NP,PTR(CX)		;get ptr
	EXCH	NB,BAP(CX)		;get bytes after ptr
	SOS	(P)			;set +1 return

FIBEO9:	MOVEI	T4,1			;perform FINDL loop once
	RET
;-----------------------------------------------------------------------------
;Routine to check for BOFM marker in the text region
FIBEO5:	CALL	CKBOFM			;check for BOFM marker in text region
	 JRST	FIBEO1			;it's not there - so check for EOFM...
					; ...in case only one change in file
; don't neet to check for beginning of file because this code is only executed
; if CHGCNT is zero. When CHGCNT is zero then I must be at beginning of file
;	CALL	CKBOF			;at beginning of file?
;	 JRST	[TMSGL <?Found <<bof>> but I'm not at beginning of file!!>
;		JRST DIE]
	TXO	F,F%BOFM		;set flag, BOFM marker found
	JRST	FIBEO9			;found it
;=============================================================================
;Routines to check for BOFM or EOFM marker in a text region
;	CALL CKBOFM
;	CALL CKEOFM
;ACCEPTS:
;	Q1 - byte pointer to text region
;	Q2 - byte length of text region
;RETURN:
;	+1 - marker not found in text region
;	+2 - marker found. Q1 and Q2 will be adjusted so as to exclude the
;		marker
;Trashes T1-T3

CKEOFM:	MOVE	T1,Q2			;get length of text region
	SUBI	T1,EOFMBC		;sub length of EOFM
	JUMPL	T1,CKEOF1		;jump if EOFM not there
	ADJBP	T1,Q1			;point to start of EOFM (if it exists)
	MOVE	T2,EOFMBP		;get byte pointer to EOFM
	MOVEI	T3,EOFMBC		;get # bytes in EOFM
	CALL	CMPSTR##		;no, look for EOFM
CKEOF1:	 RET				;EOFM is NOT in text region
	SUBI	Q2,EOFMBC		;ignore EOFM in text region
	AOS	(P)			;set +2 return
	RET

;-----------------------------------------------------------------------------
CKBOFM:	MOVE	T1,Q1			;get ptr to start of text region
	MOVE	T2,BOFMBP		;get byte pointer to BOFM
	MOVEI	T3,BOFMBC		;get # bytes in BOFM
	CAMG	T3,Q2			;is text region shorter than BOFM ?
	 CALL	CMPSTR##		;no, look for BOFM
	  RET				;BOFM is not in text region
	MOVEM	T1,Q1			;save ptr to byte after BOFM
	SUBI	Q2,BOFMBC		;calc # bytes in text region after BOFM
	AOS	(P)			;set +2 return
	RET
;===========================================================================
;Routine to output the difference between the two files.
;	CALL OUTDIF
;ACCEPTS:
;	PFP,PTR,BAP - refer the beginning of the first line of the files that
;			don't match
;	FPG,OP,OB - refer to the OLDer file and point to the end of the line
;			group where the files started matching up again
;	FPG,NP,NB - refer to the NEWer file and point to the end of the line
;			group where the files started matching up again
;	Q2 - length of matching line group in bytes. If < zero then
;		all data from PTR to the end-of-file will be output
;RETURNS:
;	+1 - always (NOTE: the FPG,OP,OB for the OLDer file and FPG,NP,NB
;		for the NEWer file may have been adjusted by CKPTR but they
;		will still point to the same actual position in the file)
;Trashes: T1-Q1

OUTDIF:	SKIPG	CHGCNT			;is this the first difference?
	 CALL	OUTHDR			;yes, output header stuff
	HRRZ	T1,OUT+JFN
	RVIDON				;turn reverse video on
	MOVE	T2,CSTRBP		;get byte pointer to CHANGE string
	TXNN	F,F%UPDF		;use update format?
	 IBP	T2			;no, skip first byte of string
	SETZ	T3,
	OSOUT%
	AOS	T2,CHGCNT		;get and increment difference count
	NUMOUT (,,-)
	SPTR	T2,<
>
	SETZ	T3,
	OSOUT%
	MOVE	T2,FSTRBP		;get byte pointer to FILE string
	TXNN	F,F%UPDF		;use update format?
	 IBP	T2			;no, skip first byte of string
	OSOUT%
	SPTR	T2,<1
>
	OSOUT%
	CATOFF				;turn reverse video off

	PUSH	P,Q1
	MOVEI	CX,OLD
	DMOVE	T1,OP			;get ptr + bytes after ptr
	CALL	ODIF			;output the region of difference
	DMOVEM	T1,OP			;save ptr + bytes incase it was changed

	HRRZ	T1,OUT+JFN
	RVIDON				;turn reverse video on
	MOVE	T2,FSTRBP		;get byte pointer to FILE string
	TXNN	F,F%UPDF		;use update format?
	 IBP	T2			;no, skip first byte of string
	SETZ	T3,
	OSOUT%
	SPTR	T2,<2
>
	OSOUT%
	CATOFF				;turn reverse video off

	POP	P,Q2			;get Q1 into Q2
	MOVEI	CX,NEW
	DMOVE	T1,NP			;get ptr + bytes after ptr
	CALL	ODIF			;output the region of difference
	DMOVEM	T1,NP			;save ptr + bytes incase it was changed

	RET
;=============================================================================
;Routine to output the differences between two files plus one line in common.
;If the F%UPDF flag is set then a few more lines of text will be output either
;before and after the region of difference.
;	CALL ODIF
;ACCEPTS: (see OTXT)
;RETURNS: +1 - always
;Trashes T3-Q1

ODIF:	TXNN	F,F%UPDF		;writing output file in /UPDATE-FORMAT?
	 JRST	ODIF7			;no
	JUMPG	Q2,ODIF7		;jump if NOT outputing to EOF

; if the output file is to be written in /UPDATE-FORMAT and I'm outputing
; to EOF then I must place a few lines before the difference in the output file
; so the UPDATE procedure will know where to put the update
	PUSH	P,T1			;save registers
	PUSH	P,T2
	CALL	PTRPMP			;set pmap buffer to start of text
	DMOVE	T1,PTR(CX)		;get ptr + bytes
	MOVE	T3,L4M			;get # lines required for a match
	CALL	FBACKJ			;back up a line...
	SOJG	T3,.-1			; ...loop for required # of lines
	DMOVEM	T1,PTR(CX)		;save updated ptr + bytes
	MOVE	T1,FPG(CX)		;get file page starting buffer
	MOVEM	T1,PFP(CX)		;save ptr's file page
	POP	P,T2			;restore registers
	POP	P,T1

ODIF7:	SKIPN	PFP(CX)			;am I at beginning of file?
	 CALL	OBOFM			;could be, check it out
	CALL	OTXT			;output the region of difference
	JUMPL	Q2,OEOFM		;if EOF then output EOF marker
	DMOVEM	T1,1(P)			;save registers
	ADJSP	P,2			;adjust stack ptr for later calls
	MOVN	T2,Q2
	ADJBP	T2,T1			;back up ptr to start of text
	HRRZ	T1,OUT+JFN
	MOVE	T3,Q2			;# bytes in line group
	SKIPN	T3			;skip if no bytes to output
	 IFSKP.
	TXNN	F,F%UPDF		;is user in update mode?
	 CALL	EATLIN			;no, print the first non-blank line
	TXNE	F,F%UPDF		;writing output file in /UPDATE-FORMAT?
	 MOVN	T3,T3			;yes, write all of matching line group
	MOVEI	T4,.CTRLJ		;output up to next ^J
	OSOUT%
ENDIF.
	ADJSP	P,-2			;adjust stack ptr to restore ACs
	DMOVE	T1,1(P)			;restore registers
	RET
;=============================================================================
;Routine to output the beginning-of-file marker to the output file
;	CALL OBOFM
;ACCEPTS: no registers need to be initialized
;RETURNS: +1 - always
;Trashes T3,T4

OBOFM:	CALL	CKBOF			;at beginning of file?
	 RET				;no, don't write marker
	HRROI	T4,BOFM			;get beginning-of-file marker
	CALLRET	OMRKER			;output marker

;-----------------------------------------------------------------------------
;Routine to check to see if PTR is pointing to beginning of the file or
;beginning of pmap buffer
;	CALL CKBOF	-checks to see if ptr at beginning of file
;	CALL CKBOB	-checks to see if ptr at beginning of pmap buffer
;ACCEPTS:
;	CX - address of the FDA (File Data Area)
;RETURNS:
;	+1 - PTR is NOT pointing to beginning of file/buffer
;	+2 - PTR is pointing to beginning of the file/buffer
;Trashes T3

CKBOF:	SKIPE	PFP(CX)			;at beginning of file?
	 RET				;no, return +1
CKBOB:	MOVE	T3,FFP(CX)		;get first page of pmap area
	LSH	T3,9			;convert page number to address
	HLL	T3,BSZ(CX)		;make pointer to start of pmap area
	CAMN	T3,PTR(CX)		;am I at the beginning of the file?
	 IFSKP.				;yes
	MOVEM	T3,1(P)			;save ptr
	SETZ	T3,
	ADJBP	T3,1(P)			;make it a real ptr
	CAME	T3,PTR(CX)		;am I at the beginning of the file?
	 RET				;no
ENDIF.
	AOS	(P)			;set +2 return
	RET

;=============================================================================
;Routine to output the end-of-file marker to the output file
;	CALL OEOFM
;ACCEPTS: no registers need to be initialized
;RETURNS: +1 - always
;Trashes T3,T4

OEOFM:	HRROI	T4,EOFM			;get end-of-file marker
OMRKER:	PUSH	P,T1			;save registers
	PUSH	P,T2
	HRRZ	T1,OUT+JFN
	SENDES <>			;turn blinking on
	MOVE	T2,T4			;get marker to output
	SETZ	T3,
	OSOUT%
	CATOFF				;turn blinking off
	POP	P,T2			;restore registers
	POP	P,T1
	RET
;=============================================================================
;Routine to output the header information for file differences
;	CALL OUTHDR
;ACCEPTS: no registers need to be initialized
;RETURNS: +1 -always
;Trashes none

OUTHDR:	DMOVEM	T1,1(P)			;save needed AC's
	DMOVEM	T3,3(P)
	ADJSP	P,4			;adjust stack for later CALLs

	HRRZ	T1,OUT+JFN
	SETZ	T3,

	TXNN	F,F%TTY			;skip if output to the terminal
	 TXNN	F,F%APND		;appending to the output file?
	  JRST	OUTHD1			;no
	SPTR	T2,<
>
	OSOUT%

OUTHD1:	SPTR	T2,< File 1: >
	OSOUT%
	FILSTR (OLD+JFN,<JS%TMP+JS%SPC>,-)
	SETZ	T3,
	SPTR	T2,<
 File 2: >
	OSOUT%
	FILSTR (NEW+JFN,<JS%TMP+JS%SPC>,-)
	SETZ	T3,
	SPTR	T2,<

>
	OSOUT%

	ADJSP	P,-4			;adjust stack pointer
	DMOVE	T1,1(P)			;restore registers
	DMOVE	T3,3(P)
	RET
;=============================================================================
;Routine to output the region of text to the output file.
;	CALL OTXT
;ACCEPTS:
;	FPG - pointer file page for T1,T2
;	T1 - byte pointer to end of region of text to output
;	T2 - bytes left in pmap buffer after pointer in T1
;	Q2 - number of bytes at end of region to ignore - this data will not
;		be output. If less than 0 then all the data from PFP,PTR,BAP
;		to the end-of-file will be output.
;	CX - address of the FDA (File Data Area)
;	PFP,PTR,BAP - refer the beginning of the region of text to output
;RETURNS:
;	+1 - always (NOTE: T1,T2 may be adjusted by CKPTR but they
;		will still point to the same actual position in the file)
;Trashes T3-Q1

OTXT:	JUMPL	Q2,OTXEOF		;jump to output from PTR to EOF
	TXNE	F,F%TTY			;output going to TTY?
	 CALL	SETDCC			;yes, set CCOC words for display
	DMOVEM	OP,1(P)			;save registers
	DMOVEM	NP,3(P)
	ADJSP	P,4			;adjust because I will do a CALL later
	MOVN	OP,Q2
	ADJBP	OP,T1			;back up ptr to start of text to ignore
	ADD	T2,Q2			;calc # bytes left
	MOVEM	T2,OB			;save it
	MOVE	Q1,FPG(CX)		;get pointer file page for ptr in OP
	CALL	PTRPMP			;set pmap buffer to start of text
	DMOVE	NP,PTR(CX)		;get ptr + bytes to output

	SETZ	T4,			;initialize flag

OTXT3:	MOVE	T1,Q1			;get ptr's file page
	DMOVE	T2,OP			;get ptr + byte count
	CALL	CKPTR			;is this area of file in pmap buffer?
	 IFSKP.				;no, not yet
	SETO	T4,			;set flag
	DMOVEM	T2,OP			;save ptr + bytes after ptr
	SUB	NB,OB			;calc bytes to output
ENDIF.
	HRRZ	T1,OUT+JFN		;get output device
	DMOVE	T2,NP			;get ptr + bytes to output
	SKIPN	T3			;skip if no bytes to output
	 IFSKP.
	CALL	EATLIN
	MOVN	T3,T3			;write a string this long
	OSOUT%
ENDIF.
	JUMPL	T4,OTXT5		;quit when flag set
	CALL	NXTPMP			;not done yet - get some more data
	DMOVEM	T2,NP			;save ptr + bytes after ptr
	JRST	OTXT3			;loop back to output difference

OTXT5:	MOVE	T1,Q2
	ADJBP	T1,OP			;advance ptr to end of text
	MOVE	T2,OB
	SUB	T2,Q2			;calc # bytes left

	ADJSP	P,-4			;adjust stack pointer
	DMOVE	OP,1(P)			;restore registers
	DMOVE	NP,3(P)
	TXNE	F,F%TTY			;output going to TTY?
	 CALL	SETOCC			;yes, reset original  CCOC words
	RET

;=============================================================================
;routine to output from PTR to the end-of-file
;	CALL	OTXEOF
;ACCEPTS: (see OTXT)
;RETURNS: (see OTXT)
;Trashes: T3

OTXEOF:	TXNE	F,F%TTY			;output going to TTY?
	 CALL	SETDCC			;yes, set CCOC words for display
	PUSH	P,T1			;save registers
	PUSH	P,T2
	CALL	PTRPMP			;set pmap buffer to start of difference
	DMOVE	T2,PTR(CX)		;get ptr + bytes to output
OTXEO1:	HRRZ	T1,OUT+JFN		;get output device
	MOVN	T3,T3			;write a string this long
	SKIPE	T3			;skip if no bytes to output
	 OSOUT%
	SKIPG	EOF(CX)			;at end of file?
	 IFSKP.				;yes
	CALL	NXTPMP			;no, get some more data
	JRST	OTXEO1			;loop back and write some more
ENDIF.
	POP	P,T2			;restore registers
	POP	P,T1
	TXNE	F,F%TTY			;output going to TTY?
	 CALL	SETOCC			;yes, reset original  CCOC words
	RET
;===========================================================================
;Routine to write the output file and replace one region of text with another
;	CALL OUTRPL
;ACCEPTS:
;	PFP,PTR,BAP - refer the beginning of text that needs to be written
;		to the output file
;	FPG,OP,OB - refer to the OLDer file and point to the end of the text
;		region that is to be replaced by the one in the NEWer file
;	Q2 - length of text region in the OLDer file that will be replaced
;		by the text region of the NEWer file
;	FPG,NP,NB - refer to the NEWer file and point to the end of the text
;		region that is to replace the one in the OLDer file
;	Q3 - length of text region in the NEWer file that will replace the
;		text region of the OLDer file
;RETURNS:
;	+1 - always (NOTE: the FPG,OP,OB for the OLDer file and FPG,NP,NB
;		for the NEWer file may have been adjusted by CKPTR but they
;		will still point to the same actual position in the file)
;Trashes: T1-Q2

OUTRPL:	MOVEI	CX,OLD
	DMOVE	T1,OP			;get ptr + bytes after ptr
	CALL	OTXT			;output upto the replacement text
	DMOVEM	T1,OP			;save ptr + bytes incase it was changed

	HRRZ	T1,OUT+JFN		;if output is to the terminal then...
	RVIDON				; ...turn reverse video on

	MOVEI	CX,NEW
	SETZ	Q2,			;output all of replacement text
	TXNE	F,F%BOFM!F%EOFM		;was BOFM or EOFM found in the change?
	 CALL	IBEOFM			;yes, ignore BOFM or EOFM for output
	DMOVE	T1,NP			;get ptr + bytes after ptr
	CALL	OTXT			;output the replacement text
	DMOVEM	T1,NP			;save ptr + bytes incase it was changed

	HRRZ	T1,OUT+JFN		;if output is to the terminal then...
	CATOFF				; ...turn reverse video off

	AOS	CHGCNT			;increment # changes made to file
	RET

;-----------------------------------------------------------------------------
;Routine to adjust the pointers so BOFM or EOFM markers are not written out
;to the output file.

IBEOFM:	DMOVE	Q1,PTR(CX)		;get ptr + bytes to start of text
	SUB	Q2,NB			;calc length of replacement text
	TXZN	F,F%BOFM		;was BOFM found earlier?
	 IFSKP.				;no, must have been EOFM
	CALL	CKBOFM			;check for BOFM in replacement text
	 CALLRET [HRROI	T1,BOFM
		CALLRET	IBEOFE]
;	TXZ	F,F%BOFM		;clear BOFM flag
	ADD	Q2,NB			;calc # bytes after ptr
	DMOVEM	Q1,PTR(CX)		;save new ptr + bytes
	SETZ	Q2,			;output all of replacement text
	RET
ENDIF.
	CALL	CKEOFM			;check for EOFM in replacement text
	 CALLRET [HRROI	T1,EOFM
		CALLRET	IBEOFE]
	ADD	Q2,NB			;calc # bytes after ptr
	MOVN	Q2,Q2			;negate it
	ADD	Q2,BAP(CX)		;ignore EOFM at end of text
	RET

;-----------------------------------------------------------------------------
;Routine to output error message if both change and replacement text don't
;contain the BOFM or EOFM
IBEOFE:	PUSH	P,T1			;save marker
	TMSGL <%The text for ">
	HRROI	T1,FSTR
	PSOUT%
	TMSG <2" in change #>
	NUMOUT (CHGNUM)
	TMSG < should also contain >
	POP	P,T1			;get marker
	PSOUT%
	SETZ	Q2,			;output all of replacement text
	RET
;=============================================================================
;Routine to send an escape sequence. The position
;of the terminals pointer is adjusted so that the monitor won't count
;the esc-sequence being set and try to put in a <crlf> where is shouldn't
;(I assume the esc-sequence will not be displayed by the terminal).
;	CALL SNDES
;ACCEPTS:
;	T1 - output JFN
;	T2 - esc-sequence string to send
;RETURNS: +1 always
;Trashes T2,T3

.RVIDO:	SPTR	T2,<>		;turn reverse video on
	TRNA
.CATOF:	 SPTR	T2,<>		;turn all character attributes off
SNDES:	TXNN	F,F%SES			;send escape sequence?
	 RET				;no
	DMOVEM	T4,1(P)			;save registers
	MOVEM	T2,T4			;save escape sequence to send
	DMOVE	T2,MCCOC		;set the modified CCOC words
	SFCOC%				;modify CCOC
	RFPOS%				;get current line#,,column#
	MOVEM	T2,Q1			;save it
	TRZ	T2,-1			;reset column# incase near end of line
	SFPOS%				;set current line#,,column#
	MOVE	T2,T4			;get escape sequence to send
	SETZ	T3,
	SOUT%
	 JERR (?,,PC,DIE)
	MOVE	T2,Q1
	SFPOS%				;set current line#,,column#
	DMOVE	T2,OCCOC		;set the original CCOC words
	SFCOC%
	DMOVE	T4,1(P)			;restore registers
	RET
;=============================================================================
;Routine to set the CCOC words.
;	CALL SETDCC	;set CCOC in DCCOC
;	CALL SETOCC	;set CCOC in OCCOC
;ACCEPTS: no registers need to be initialized
;RETURNS: +1 always
;Trashes none

SETOCC:	DMOVEM	T2,2(P)			;save registers
	DMOVE	T2,OCCOC		;get original CCOC words
	CALLRET	SETDC1
SETDCC:	DMOVEM	T2,2(P)			;save registers
	DMOVE	T2,DCCOC		;get "display" CCOC words
SETDC1:	MOVEM	T1,1(P)			;save register
	HRRZ	T1,OUT+JFN		;get output device
	SFCOC%
	MOVE	T1,1(P)			;restore registers
	DMOVE	T2,2(P)
	RET
	SUBTTL	Server for UPDATE command
;=============================================================================
.UPDAT:	HRLZI	T4,(GJ%OLD)		;parse existing file
	MOVEM	T4,GTJBLK+.GJGEN
	NOISE	(source file)
	PARSE	(,<.CMFIL,CM%SDH,,<file specs of file you want updated>>)
	MOVEM	T2,OLD+JFN		;save JFN

	NOISE	(using changes in)
	PUSH	P,T1			;save register
	HRROI	T1,FSPEC		;default file name is here
	MOVEM	T1,GTJBLK+.GJNAM
	FILSTR (OLD+JFN,<FLD(.JSAOF,JS%NAM)>,-)
	POP	P,T1			;restore register
	HRROI	T4,[ASCIZ/CMP/]		;use this default file extension
	MOVEM	T4,GTJBLK+.GJEXT
	PARSE	(,<.CMFIL,CM%SDH,,<file specs of file with changes>>)
	MOVEM	T2,NEW+JFN		;save JFN

	NOISE	(giving)
	HRLZI	T4,(GJ%FOU)		;parse a new file generation
	MOVEM	T4,GTJBLK+.GJGEN
	CALL	SETDNE			;set up the default name + extension
	TXZ	F,F%VRFY!F%WILD!F%LSTK!F%IGNO	;initialize flags
	PARSE	(,<.CMFIL,,,,,,UPDSWI>)
UPDAT5:	TLZ	T3,-1			;get function discriptor block parsed
	CAIN	T3,CONFRM##		;user confirmed command?
	 JRST	UPDAT7			;yes
	CAIE	T3,UPDSWI		;a switch?
	 IFSKP.				;no
	TXO	F,F%VRFY		;yes, set it
	CONFIRM
	JRST	UPDAT8
ENDIF.
	HRRZM	T2,OUT+JFN		;no, must have parsed the output file
	PARSE	(,,UPDSWI)		;parse a switch
	JRST	UPDAT5

UPDAT7:	CALL	DOECHO##		;echo the command if necessary
UPDAT8:	CALL	UPDFIL			;update the file
	JRST	ENDCMD			;go get another command
;=============================================================================
;Routine to update a ascii file using the changes in a update file
;	CALL UPDFIL
;ACCEPTS: no registers need to be initialized
;RETURNS: +1 always
;Trashes - most registers

UPDFIL:	HRRZ	T1,OUT+JFN		;get JFN of output device
	CALL	CKDEV			;check output device
	CATOFF				;make sure graphic attributes off

	CALL	FOPEN			;open the required files
	MOVEI	T1,UPDXIT		;exit routine for ^E,^X abort
	CALL	ATIAEX			;activate for interrupts
	TXZ	F,F%ABRT!F%BOFM!F%EOFM	;initialize flags
	SETZM	CHGCNT			;initialize change count
	SETZM	CHGNUM			;initialize change number

UPDBEG:	MOVEI	CX,NEW			;initialize FDA to file with changes
	MOVE	Q1,CSTRBP		;byte pointer to CHANGE string
	MOVEI	Q2,CSTRBC		;# bytes in CHANGE string
	SETZ	T4,			;search until EOF
	CALL	FINDLI			;search for next change
	 JRST	UPDNOD			;couldn't find change delimiter
UPDLUP:	DMOVE	T1,NP			;get updated ptr + bytes left in buffer
	CALL	USCANJ			;look for next ^J
	MOVE	T3,NP			;get ptr
	ILDB	T3,T3			;get byte after CHANGE string
	CAIN	T3,TSTRFB		;is this the end of all changes?
	 JRST	UPDEOF			;yes, quit
	TXNE	F,F%EOFM		;was EOFM marker found in last change?
	 JRST	[TMSGL <?The end of file marker ">
		HRROI	T1,EOFM
		PSOUT%
		TMSG <" was found in change #>
		NUMOUT (CHGNUM)
		TMSG <
 but this is NOT the last change in the file. Will assume that is should be
>
		JRST	UPDEOF]
	EXCH	T1,NP			;save updated ptr...
	MOVEM	T2,NB			; ...and bytes left
	CALL	D2B##			;get change number
	AOS	CHGNUM			;increment change number
	CAME	T2,CHGNUM		;is it right?
	 JRST	[PUSH	P,T2		;save number
		TMSGL <%Was expecting change #>
		NUMOUT (CHGNUM)
		TMSG < but found change #>
		POP	P,T2		;restore number
		NUMOUT
		TMSG < instead
>
		CAMG	T2,CHGNUM	;is new change number bigger that last
		 JRST [	TXO	F,F%ABRT	;no
			TMSG <?Aborting update.....
>
			JRST	UPDXIT]
		MOVEM	T2,CHGNUM	;attempt to recover
		TMSG <	will contiune processing updates from here....
>
		JRST .+1]

	MOVE	Q1,FSTRBP		;byte pointer to FILE string
	MOVEI	Q2,FSTRBC		;# bytes in FILE string
	SETZ	T4,			;search until EOF
	CALL	FINDLI			;search for start of "File 1" change
	 JRST	UPDNOD			;couldn't find change delimiter
	DMOVE	T1,NP			;get updated ptr + bytes left in buffer
	CALL	USCANJ			;look for next ^J
	ILDB	T3,NP			;get file number
	CAIE	T3,"1"			;is this it?
	 JRST	UPDNOF			;no
	DMOVEM	T1,NP			;save ptr+bytes for start of change
	MOVE	Q1,FSTRBP		;byte pointer to FILE string
	MOVEI	Q2,FSTRBC		;# bytes in FILE string
	SETZ	T4,			;search until EOF
	CALL	FINDLI			;search for end of "File 1" change
	 JRST	UPDNOD			;couldn't find change delimiter
	DMOVE	T1,NP			;get updated ptr + bytes left in buffer
	CALL	USCANJ			;look for next ^J
	ILDB	T3,NP			;get file number
	CAIE	T3,"2"			;is this it?
	 JRST	UPDNOF			;no
	DMOVEM	T1,NP			;save ptr+bytes for start of replacement
	CALL	FBACKJ			;search backward for a ^J
	DMOVE	Q1,PTR(CX)		;get ptr+bytes for start of change
	SUB	Q2,T2			;calc # bytes in change
	JUMPLE	Q2,UPDNOB		;no bytes - should not happen
	MOVEI	CX,OLD
	EXCH	OP,NP			;exchange byte ptrs and...
	EXCH	OB,NB			;  ...byte counts to search OLDer file
	SETZ	T4,			;search until EOF
	CALL	FINDCK			;search for where to put change
	 JRST	[TMSGL <?Couldn't find change #>

		NUMOUT (CHGNUM)
;		TMSG < in >
;		FILSTR (JFN(CX))
		TMSG <	(this update will NOT be made)
>
		CALL	PTRPMP		;go back to where I was
		DMOVE	OP,NP		;restore ptr + bytes back to normal
		DMOVE	NP,PTR(CX)	;get ptr + bytes left in buffer
		JRST	UPDBEG]		;ignore this update
	TXNN	F,F%BOFM!F%EOFM		;verify NOT needed if BOFM or EOFM found
	 TXNN	F,F%VRFY		;verify the change is unique?
	  IFSKP.			;no
	PUSH	P,PFP(CX)		;save file location data
	PUSH	P,PTR(CX)
	PUSH	P,BAP(CX)
	CALL	FINDLI			;see if string occurs more than once
	 TRNA				;didn't find another occurance
	  JRST	[TMSGL <?Change #>
		NUMOUT (CHGNUM)
		TMSG < is not unique. Will make change to 1st occurance ONLY!!
>
		JRST .+1]
	CALL	PTRPMP			;go back to where I was before FINDLI
	DMOVE	NP,PTR(CX)		;restore ptr + bytes after ptr
	POP	P,BAP(CX)		;restore file location data
	POP	P,PTR(CX)
	POP	P,PFP(CX)
ENDIF.
	MOVEI	CX,NEW
	EXCH	OP,NP			;exchange byte ptrs and...
	EXCH	OB,NB			;  ...byte counts so I'm back to normal
	PUSH	P,Q2			;save length of change text

	MOVE	Q1,CSTRBP		;byte pointer to CHANGE string
	MOVEI	Q2,CSTRBC		;# bytes in CHANGE string
	SETZ	T4,			;search until EOF
	CALL	FINDLI			;search for next change
	 JRST	UPDNOD			;couldn't find change delimiter
	POP	P,Q2			;restore length of change text
	PUSH	P,NP			;save ptr to end of CHANGE string...
	PUSH	P,NB			; ...and bytes after ptr
	DMOVE	T1,NP			;get ptr + bytes left in buffer
	CALL	FBACKJ			;search backward for a ^J
	DMOVEM	T1,NP			;save ptr + bytes left in buffer
	MOVE	Q3,BAP(CX)		;get bytes after start of replacement
	SUB	Q3,NB			;calc # bytes in replacement text
	JUMPLE	Q3,UPDNOB		;no bytes - should not happen
	CALL	OUTRPL			;write output file using replacement
	POP	P,NB			;restore ptr to end of CHANGE string...
	POP	P,NP			; ...and bytes after ptr
	JRST	UPDLUP			;loop back to make next update
;=============================================================================
;Routine to search for the next ^J in the update file
;	CALL USCANJ
;ACCEPTS:
;	NP,NB - containing ptr + bytes left after ptr
;RETURNS: +1 always unless EOF with T1-T2 as left by SCANJ

USCANJ:	DMOVE	T1,NP			;get ptr + bytes left after ptr
USCAN1:	CALL	SCANJ			;look for next ^J
	 TRNA				;search failed - check out why
	  RET
	CALL	SUPMPE			;setup for PMP and get some more data
	 JRST [	TMSGL <?Premature end of file for >
		FILSTR (NEW+JFN)
		ADJSP	P,-1		;remove CALL to USCANJ from stack
		TXO	F,F%ABRT
		JRST	UPDXIT]		;abort update
	JRST	USCAN1			;continue SCANJ
;=============================================================================
;Program jumps here when the end of the update file is reached
UPDEOF:	MOVEI	CX,OLD
	MOVE	Q2,FPG(CX)		;get file page starting buffer
	MOVEM	Q2,PFP(CX)		;init ptr's file page
	DMOVEM	OP,PTR(CX)		;init ptr + bytes after ptr
	SETO	Q2,			;write anything left in the source...
	CALL	OTXT			; ... file to the output file

	SPTR T1,< >
	PSOUTL
	NUMOUT (CHGCNT)
	TMSG < change>
	MOVEI	T1,"s"
	CAIE	T2,1			;more that 1 change?
	 PBOUT%				;yes, get tense right
	TMSG < made using >
	FILSTR (NEW+JFN)
	TMSG <
>
	MOVEI	T1,TSTRBC		;get length of TSTR
	ADJBP	T1,NP			;skip over it
	CALL	D2B##			;get total number of changes
	CAME	T2,CHGCNT		;is it same as # of changes I made?
	 JRST	[TMSG <%	...but update file said there should have been >
		NUMOUT
		TMSG < changes!!
>
		JRST	.+1]

;-----------------------------------------------------------------------------
UPDXIT:	CALL	DTIAEX			;deactivate for ^A,^E interrupts
	CALLRET	FCLOSE			;close all files and return to caller
;	SUBTTL	Error handlers for UPDATE
;-----------------------------------------------------------------------------
UPDNOB:	TMSGL <?Null replacement string found for change #>
	NUMOUT (CHGNUM)
	TXO	F,F%ABRT
	JRST	UPDXIT

;-----------------------------------------------------------------------------
;program jumps here when either CSTR or FSTR couldn't be found. This strings
;are used to delimit the changes and replacements to be made to the file so it
;is important that they be in the file
;	JRST UPDNOD
;ACCEPTS:
;	Q1 - either CSTR or FSTR
;RETURNS: never

UPDNOD:	TMSGL <?Couldn't find string ">
	MOVE	T1,Q1			;get CPTR or FPTR
	PSOUT%
	TMSG <" to delimit changes
	Problem with file or file was not written in /UPDATE-FORMAT>
	TXO	F,F%ABRT	;abort any updates done so far
	JRST	UPDXIT

;-----------------------------------------------------------------------------
;program jumps here when byte after FSTR was not "1" or "2" or was not what I
; was expecting
;	JRST UPDNOF
;ACCEPTS:
;	T3 - byte found after FSTR
;RETURNS: never

UPDNOF:	TMSGL <?Found the byte ">
	MOVE	T1,T3
	PBOUT%
	TMSG <" after ">
	HRROI	T1,FSTR
	PSOUT%
	TMSG <" for change #>
	NUMOUT (CHGNUM)
	TMSG <	This is not what I was expecting
>
	TXO	F,F%ABRT	;abort any updates done so far
	JRST	UPDXIT
	SUBTTL	Miscellaneous Subroutines
;=============================================================================
;This routine will set the default file name and file extension in the
;GTJBLK to the same as those of the OLDer file
;	CALL SETDNE
;ACCEPTS:
;	OLD+JFN - JFN of OLDer file
;RETURNS: +1 always
;Trashes T2-T3

SETDNE:	PUSH	P,T1			;save register
	HRROI	T1,FSPEC		;default file name is here
	MOVEM	T1,GTJBLK+.GJNAM
	MOVE	T2,OLD+JFN		;get flags,,JFN
	FILSTR (-,<FLD(.JSAOF,JS%NAM)>,-)
	IBP	T1			;preserve null at end of string
	MOVEM	T1,GTJBLK+.GJEXT	;defalut file extension will be here
	FILSTR (-,<FLD(.JSAOF,JS%TYP)>,-)
	POP	P,T1			;restore register
	RET

;=============================================================================
;This routine will initialize command switches to their default values.
;	CALL SWINI
;ACCEPTS: nothing
;RETURNS: +1 always
;Trashes T4

SWINI:	TXZ	F,DSWIF			;clear these default flags
	IOR	F,DSWI.S		;set the ones that should be set
	MOVE	T4,DL4M			;get default lines for a match
	MOVEM	T4,L4M			;initialize it
	MOVE	T4,DL2S			;get default lines to search
	MOVEM	T4,L2S			;initialize it
	RET
;=============================================================================
;Routine to compare files when the files have wildcards in their file specs
;	CALL WLDCMP
;ACCEPTS: no registers need to be initialized
;RETURNS: +1 always

WLDCMP:	TXO	F,F%APND		;insure output file is appended to
	HRROI	T1,OLDFSW
	MOVE	T2,OLD+JFN
	CALL	BLDFS			;put wild file specs here
	HRROI	T1,NEWFSW
	MOVE	T2,NEW+JFN
	CALL	BLDFS			;put wild file specs here
	RELJFN NEW+JFN			;don't need JFN anymore
	SETZM	FILCNT			;initialize the file count

WLDCM1:	HRROI	T1,OLDFS
	HRRZ	T2,OLD+JFN
	CALL	BLDFS			;put non-wild file specs here
	MOVE	P1,[POINT 7,OLDFSW]
	MOVE	P2,[POINT 7,OLDFS]
	MOVE	P3,[POINT 7,NEWFSW]
	MOVE	P4,[POINT 7,FSPEC]
	CALL	BWILD			;build the NEW DEV:<directory> fields
	CALL	BWILD			;build the NEW file-name field
	CALL	BWILD			;build the NEW file-type field
	CALL	BWILD8			;build the NEW file-generation field
	MOVX	T1,GJ%SHT!GJ%IFG!GJ%OLD	;get JFN for NEWer file
	HRROI	T2,FSPEC
	GTJFN%
	 JRST	[TMSGL <?Can't get >	;couldn't get JFN for some reason
		HRROI	T1,FSPEC
		PSOUT%
		CALL	ERSTRI##	;output last error message
		JRST	WLDCM8]
	MOVEM	T1,NEW+JFN		;save indexable file handle

WLDCM3:	TMSGL < Comparing >
	FILSTR (OLD+JFN)
	TMSG < with >
	FILSTR (NEW+JFN)
;	TXNE	F,F%TTY			;output going to the terminal?
;	 JRST	WLDCM6			;yes
;	MOVE	T2,OUT+JFN
;	TXNN	T2,GJ%DEV!GJ%DIR!GJ%NAM!GJ%EXT!GJ%VER	;wildcards found?
;	 JRST	WLDCM6			;no
;	TMSG < outputing to >
;	**** must add code to built output file here ***
;WLDCM6:
	TMSG <
>
	CALL	CMPFIL			;compare the files
	AOS	FILCNT			;increment file count
WLDCM8:	MOVEI	CX,NEW
	CALL	NEXTF			;get next file in group
	 TRNA				;no more file in wildcard group
	  JRST	WLDCM3			;go process next file
	MOVEI	CX,OLD
	CALL	NEXTF			;get next file in group
	 TRNA				;no more file in wildcard group
	  JRST	WLDCM1			;go process next file

	TMSGL < A total of >
	NUMOUT (FILCNT)
	TMSG < comparisons were done
>
	TXZ	F,F%WILD		;let OCLOSE close the file
	CALLRET	OCLOSE			;close the output file
;=============================================================================
;Routine to execute the GNJFN% to get the next file in the wildcard group
;	CALL NEXTF
;ACCEPTS:
;	CX - address of the FDA (File Data Area)
;RETURNS:
;	+1 - no more files in wildcard group. JFN will be released and JFN(CX)
;		will be set to zero
;	+2 - JFN will now refer to next file in wildcard group
;Trashes T1

NEXTF:	SKIPN	T1,JFN(CX)		;get indexable file handle
	 RET				;JFN released so no more files
	GNJFN%				;get next JFN in wildcard group
	 IFSKP.				;GNJFN jsys failed
	AOS	(P)			;set +2 return
	RET
ENDIF.
	CAIE	T1,GNJFX1		;GNJFN failed because no more JFN's?
	 ERR (?,,PC,DIE)		;no, some other error
	SETZM	JFN(CX)			;JFN is released (last GNJFN% did it)
	CALLRET	ERESET			;reset last error

;=============================================================================
;Routine to build a the file string required by BWILD, each field will be
;separated by a null
;	CALL BLDFS
;ACCEPTS:
;	T1 - byte pointer to where file string is to go
;	T2 - JFN or indexable file handle (flags,,JFN)
;RETURNS: +1 always
;Trashes T1-T3

BLDFS:	FILSTR (-,<JS%DEV!JS%DIR!JS%PAF>,-)
	IBP	T1			;keep null at end
	FILSTR (-,<JS%NAM!JS%PAF>,-)
	IBP	T1			;keep null at end
	FILSTR (-,<JS%TYP!JS%PAF>,-)
	IBP	T1			;keep null at end
	FILSTR (-,<JS%GEN!JS%PAF>,-)
	RET
;=============================================================================
;Routine to build a new non-wild string from a wild and non-wild string and
;a 2nd wild string. The 1st non-wild string must be a valid element in the
;wildcard set of the 1st wild string - no checking will be done to insure this.
;	CALL BWILD
;	CALL BWILD8	-write bytes directly from 2nd wild string to non-wild
;ACCEPTS:
;	P1 - byte pointer to 1st wild string
;	P2 - byte pointer to 1st non-wild string
;	P3 - byte pointer to 2nd wild string
;	P4 - byte pointer to where to place 2nd non-wild string
;RETURNS:
;	+1 with P1-P4 updated to end of strings. P4 is adjusted so that
;		next IDPB will overwrite termining null
;Trashes T1-T4

BWILD:
BWILD1:	ILDB	T1,P1			;get a byte from 1st wild string
	ILDB	T2,P2			;get a byte from 1st non-wild string
	JUMPE	T1,BWILD8		;end of 1st wild string found
	CAMN	T1,T2
	 JRST	BWILD1			;loop until difference found
	SETO	T2,
	ADJBP	T2,P2			;back up ptr to 1st non-wild string
	MOVEM	T2,P2			;save it
;don't need to check because I left that up to the user who called me
;	CAIE	T1,"*"
;	 CAIN	T1,"%"
;	  TRNA
;	   JRST [TMSGL <?1st non-wild string is not in subset of wild string>
;		JRST	ENDCMD]

BWILD2:	ILDB	T3,P3			;get a byte from 2nd wild string
	JUMPE	T3,BWILD6		;end of 2nd wild string found
	CAIE	T3,"*"
	 CAIN	T3,"%"
	  IFSKP.			;wildcard found
	IDPB	T3,P4			;build new string
	JRST	BWILD2			;loop back for more
ENDIF.
	CAME	T1,T3			;wildcards should be same for both
	 JRST	[TMSGL <?BWILD: Illegal use of wildcards>
		JRST	ENDCMD]
	CAIE	T1,"%"
	 IFSKP.
	ILDB	T2,P2			;get byte from 1st non-wild string
	IDPB	T2,P4			;build 2nd non-wild string
	JRST	BWILD1			;loop back to process more wildcards
ENDIF.

; find the text region that corresponds to the wildcard to the 2nd non-wild
; string
	MOVE	T3,P1			;save ptr to 1st wildcard string
	SETZ	T4,
BWILD4:	ILDB	T1,P1			;get byte from 1st wild string
	JUMPE	T1,BWILD7		;quit when end of 1st wild string
	CAIE	T1,"*"
	 CAIN	T1,"%"
	  TRNA				;found another wildcard
	   AOJA	T4,BWILD4		;keep on searching
	JUMPE	T4,BWILD2		;2 wildcards are next to each other
	MOVEM	T3,P1			;restore ptr to 1st wildcard string
BWILD5:	DMOVE	T1,P1			;get ptr to wild + non-wildstring
	MOVE	T3,T4			;get length of common string
	CALL	CMPSTR##		;look for common string
	 TRNA				;not found yet
	  IFSKP.			;found it
	ILDB	T2,P2			;get byte from 1st non-wild string
	IDPB	T2,P4			;build 2nd non-wild string
	JRST	BWILD5			;keep on looking for a match
ENDIF.
	DMOVEM	T1,P1			;save updated pointers
	JRST	BWILD1			;loop back to process more wildcards

; execution gets here when end of 2nd wild string found. So make sure pointers
; to 1st wild and non-wild string are updated (point to the end of string)
BWILD6:	ILDB	T1,P1			;advance to end of 1st wild string
	JUMPN	T1,.-1			;loop until null is reached
	ILDB	T2,P2			;advance to end of 1st non-wild string
	JUMPN	T2,.-1			;loop until null is reached
	JRST	BWILD9			;quit

; execution gets here when end of 1st wild string found when looking for
; the string that the wildcard matches. When this happens (usually about 90%
; of the time) I don't have to do the "expensive" CMPSTR loop because I have
;the information to shortcut the process.
BWILD7:	SETZ	T3,			;initialize count
	MOVE	T2,P2			;get ptr to 1st non-wild string
	ILDB	T1,P2			;get byte from 1st non-wild string
	SKIPE	T1
	 AOJA	T3,.-2			;loop until end of string
	SUB	T3,T4			;calc # bytes corresponding to "*"
	JUMPE	T3,BWILD8		;no bytes correspond to "*"
	ILDB	T1,T2			;get byte from 1st non-wild string
	IDPB	T1,P4			;add it to 2nd non-wild string
	SOJG	T3,.-2			;loop until done

; execution gets here when end of 1st wild string found. So write any left
; over bytes in 2nd wild string to the 2nd non-wild string I'm building
BWILD8:	ILDB	T3,P3			;get byte from 1st non-wild string
BWILD9:	IDPB	T3,P4			;build 2nd non-wild string
	JUMPN	T3,BWILD8		;loop for all bytes in 2nd non-wild
	SETO	T2,
	ADJBP	T2,P4			;back up ptr past terminating null
	MOVEM	T2,P4			;save it
	RET
;=============================================================================
;This routine will check the output device to determine whether it's a
;terminal and set the appropiate flags
;	CALL CKDEV
;ACCEPTS:
;	T1 - JFN of output device
;RETURNS
;	+1 - always with F%SES set appropriately and T1 will contain the
;		output designator
;Trashes T2-T3

CKDEV:	TXZ	F,F%SES!F%TTY		;initialize flags
	DVCHR%				;get characteristics of the device
	 JERR (?,,PC)
	TLZ	T2,777000		;remove unwanted info
	HLRZ	T2,T2			;get device type
	CAIN	T2,.DVTTY		;is it a terminal
	 TXO	F,F%TTY			;yes, set flag
	TXNE	F,F%VT			;is program running from a VT100?
	 TXNN	F,F%TTY			; ...and output going to the terminal?
	  TRNA				;no
	   TXO	F,F%SES			;yes, set flag to send escape sequences
	RET
;=============================================================================
;Routine to open all required files
;	CALL FOPEN
;ACCEPTS: no registers need to be initialized
;RETURNS: +1 always
;Trashes T1-T3

FOPEN:	MOVEI	CX,OLD
	CALL	FOPEN7			;open file and map some pages in
	DMOVEM	T2,OP			;save byte ptr + byte count

	MOVEI	CX,NEW
	CALL	FOPEN7			;open file and map some pages in
	DMOVEM	T2,NP			;save byte ptr + byte count

	HRRZ	T1,OUT+JFN
	CAIE	T1,.PRIOU		;output going here?
	 TXOE	F,F%OPEN		; or is it already open?
	  RET				;yes, I'm done
	MOVX	T2,<FLD(7,OF%BSZ)>+OF%WR	;open the file, ascii write
;	TXNE	F,F%APND		;append to the output file?
;	 TXO	T2,OF%APP!OF%RD		;yes, open for append
	OPENF%
	 JERR (?,,PC,DIE)
	RET

;-----------------------------------------------------------------------------
;Routine to open a ascii file for input and map the pages of the file into
;memory
;	CALL FOPEN7
;ACCEPTS:
;	CX - address of the FDA (File Data Area)
;RETURNS: +1 always with T2,T3 as returned by PMP

FOPEN7:	MOVEI	T1,7			;assume it is a 7bit (ascii) file
	TXNE	F,F%EXE			;is user want to compare EXE file?
	 MOVEI	T1,36			;yes, force it to use 36bit
	CALL	FDAINI			;init file data area for OLD file
	HRRZ	T1,JFN(CX)
	MOVX	T2,<FLD(7,OF%BSZ)>+OF%RD	;Open the file, ascii read
	OPENF%
	 JERR (?,,PC,DIE)
	CALL	CKHOLE			;check file for holes
	 JRST	DIE			;can't handle files with holes
	CALLRET	PMP			;map the file into memory
;=============================================================================
;Routine to initialize the FDA (File Data Area) for a file.
;	CALL FDAINI
;ACCEPTS:
;	T1 - byte size to force file to if necessary
;	CX - address of the FDA (File Data Area)
;	JFN(CX) - JFN of file
;RETURNS:
;	+1 -always. with FDA updated
;Trashes T1-T2

FDAINI:	DMOVEM	T3,1(P)			;save needed AC's
	DMOVEM	Q1,3(P)
	ADJSP	P,4			;adjust because I may do a CALL later

	HRRZM	T1,BSZ(CX)		;save byte size
	MOVEI	T2,^D36			;# bits/word
	IDIV	T2,T1			;calc # bytes/word
	IMULI	T2,1K			;calc # bytes/page
	MOVEM	T2,BPP(CX)		;save it

	HRRZ	T1,JFN(CX)		;get JFN
	MOVE	T2,[2,,.FBBYV]		;get number of pages and bytes in file
	MOVEI	T3,Q1			;start saving info here
	GTFDB%
	 JERR (?,,PC,DIE)
	LDB	T1,[POINT 6,Q1,11]	;get byte size
	JUMPE	T1,[TMSGL <?Byte size of file is "0" - can't process it>
		JRST	DIE]
	MOVEI	T2,^D36			;# bits/word
	IDIV	T2,T1			;calc # bytes/word
	MOVE	T1,Q2			;get # bytes in file
	IDIV	T1,T2			;calc # words in file
	DMOVEM	T1,T3			;save # words + remainder
	SKIPE	T2			;any partial words?
	 ADDI	T1,1			;yes, bump up # words in file
	IDIVI	T1,1K			;calc # pages for this many words
	SKIPE	T2			;any partial pages?
	 ADDI	T1,1			;yes, bump up # pages in file
	MOVEM	T1,PGL(CX)		;save length of file in pages
	MOVEM	T1,EOF(CX)		;initialize EOF flag
	HRRZ	T1,Q1			;get actual # pages in file
	CAME	T1,PGL(CX)		;does it match actual pages?
	 JRST [	CAMG	T1,PGL(CX)	;is actual bigger?
		 JRST [	TMSGL <?Actual page count is less than that calculated using byte count>
			JRST	DIE]
		TMSGL <%Actual page count is greater than that calculated by using byte count
	Data after logical end of file will be ignored...
>
		JRST .+1]
	LDB	T1,[POINT 6,Q1,11]	;get byte size
	CAMN	T1,BSZ(CX)		;is byte size same as one to force?
	 IFSKP.				;yes, don't need to force byte size

					;T3= # words in file, T4=remainder bytes
	MOVEI	T2,^D36			;# bits/word
	MOVEM	T3,Q2			;save # words in file
	IDIV	T2,BSZ(CX)		;calc # bytes/word for new byte size
	IMUL	Q2,T2			;calc # bytes for new byte size
	IMUL	T1,T4			;calc # bits remaining in last word
	IDIV	T1,BSZ(CX)		;calc # bytes this is
	SKIPE	T2			;any remainder bits?
	 ADDI	T1,1			;yes, bump up # bytes in file
	ADD	Q2,T1			;calc new byte count
ENDIF.
	MOVEM	Q2,BLN(CX)		;save length of file in bytes
	MOVE	T1,BPP(CX)		;get number of bytes/page
	IMUL	T1,PGL(CX)		;calc # bytes if all page full
	SUB	T1,Q2			;calc # bytes free on last page
	MOVEM	T1,BLF(CX)		;save it
	SETZM	FPG(CX)			;init first page to start pmaping at
	HRLZ	T1,BSZ(CX)		;get byte size
	LSH	T1,6			;put byte size in "S" field for byte ptr
	TLO	T1,440000		;initialize "P" field for byte ptr
	HLLM	T1,BSZ(CX)		;save it
	SETZM	PTR(CX)			;init pointer for ^A interrupts

	ADJSP	P,-4			;adjust stack pointer
	DMOVE	T3,1(P)			;restore needed AC's
	DMOVE	Q1,3(P)
	RET				;return to caller
;=============================================================================
;Routine to check file for holes
;	CALL CKHOLE
;ACCEPTS:
;	CX - address of the FDA (File Data Area)
;RETURNS:
;	+1 - file has holes
;	+2 - file does NOT have holes
;Trashes T1-T2

CKHOLE:	AOS	(P)			;set +2 return
	HRRZ	T1,JFN(CX)		;make 0,,JFN (start at page 0)
	FFFFP%				;find first free page
	 JERR (?,,PC,DIE)
					;T1 = JFN,,page-number-first-free-page
	TLZ	T1,-1			;get page # of first free page
	CAML	T1,PGL(CX)		;does file have holes?
	 RET				;no
	TMSGL <?File >
	FILSTR JFN(CX)
	TMSG < has holes
>

	SOS	(P)			;set +1 return
	RET
;=============================================================================
;Routine to close all required files
;	CALL FCLOSE
;	CALL OCLOSE	-close the output file
;ACCEPTS: no registers need to be initialized
;RETURNS: +1 always
;Trashes T1-T3

FCLOSE:	MOVEI	CX,NEW
	CALL	UNMAPC			;unmap and close it
	MOVEI	CX,OLD
	CALL	UNMAPC			;unmap and close it

OCLOSE:	SKIPE	T1,OUT+JFN		;if JFN already released
	 CAIN	T1,.PRIOU		; ...or output to here?
	  RET				;then, I'm done

	TXNN	F,F%TTY			;output to a terminal?
	 TXNN	F,F%ABRT		; ...or NOT aborting?
	  JRST	FCLOS5			;then don't display abort message
	TMSGL	<?Aborting output to >
	FILSTR (OUT+JFN)
FCLOS5:	TXNE	F,F%WILD		;processing wildcard group?
	 RET				;yes, don't close output file
	MOVEI	CX,OUT
	CALLRET	FCLOS7			;close the output file

;=============================================================================
;routine to unmap the pmap buffer area for a file and close it
;	CALL UNMAPC
;	CALL FCLOS7	-just close file
;ACCEPTS:
;	CX - address of the FDA (File Data Area)
;RETURNS: +1 always
;Trashes T1-T2

UNMAPC:	SKIPN	JFN(CX)			;is there a JFN
	 RET				;no, nothing to do
	SETO	T1,			;unmap pmap buffer from memory
	DMOVE	T2,FFP(CX)		;fork,,first page of pmap area
					;T3 = number of pages to unmap
	PMAP%
	 JERR (?,,PC,DIE)
FCLOS7:	HRRZ	T1,JFN(CX)
	TXNE	F,F%WILD		;am I processing a wildcard group?
	 TXO	T1,CO%NRJ		;yes, don't release JFN
	CAIN	CX,OUT			;closing output file?
	 TXNN	F,F%ABRT		;aborting output?
	  TRNA				;no, don't set CZ%ABT flag
	   TXO	T1,CZ%ABT		;yes, abort any changes made
	CLOSF%				;close the file
	 JERR (?,,PC)
	TXNN	T1,CO%NRJ		;was JFN released?
	 SETZM	JFN(CX)			;yes, say JFN is released
	CAIN	CX,OUT			;closing output file?
	 TXZ	F,F%OPEN		;yes, say it's closed
	RET
	SUBTTL	Software Interrupt Routines
;=============================================================================
;Routine to activate ^A, ^E and ^X interrupts
;	CALL ATIAEX
;ACCEPTS:
;	T1 - address of command routine
;RETURNS: +1 always
;Trashes T1

ATIAEX:	SETOM	PI.CNT			;set flag
	SETZM	CAOLOC			;initialize file location for OLDer file
	SETZM	CANLOC			;initialize file location for NEWer file
	MOVEM	T1,PI.ABT		;save exit routine for abort
	MOVEM	P,PI.P			;save stack for interrupt
	MOVE	T1,[.TICCA,,.CACH]	;activate to intercept ^A
	ATI%
	 JERR (?,,PC)
	MOVE	T1,[.TICCE,,.CECH]	;activate to intercept ^E
	ATI%
	 JERR (?,,PC)
	MOVE	T1,[.TICCX,,.CXCH]	;activate to intercept ^X
	ATI%
	 JERR (?,,PC)
	RET

;=============================================================================
;Routine to deactivate ^A, ^E and ^X interrupts
;	CALL DTIAEX
;ACCEPTS: none
;RETURNS: +1 always
;Trashes T1

DTIAEX:	MOVEI	T1,.TICCA		;deassign ^A
	DTI%
	 JERR (?,,PC)
	MOVEI	T1,.TICCE		;deassign ^E
	DTI%
	 JERR (?,,PC)
	MOVEI	T1,.TICCX		;deassign ^X
	DTI%
	 JERR (?,,PC)
	RET
;=============================================================================
;Routine to hande ^A interrupts. I must set up a new stack so the CALLs I
;make won't destroy data that may be temporarly saved past the top of stack -
;eg. by the DMOVEM _,_(P). Since this program spends most of it time executing
;the MOVST instruction (in SCANJ) or CMPSN (in CMPSTR), I can test for this and
;use this information to report where in the file the program is currently
;working.

CTRLA:	IP.SAVE				;save F to P

; save the AC's in PI.ACS because I may need them later if I interrupted
; an EXTEND instruction
	MOVEM	CX,PI.ACS+CX		;save BLT register
	MOVEI	CX,PI.ACS
	BLT	CX,PI.ACS+CX-1		;save registers F to CX-1
	MOVE	CX,PI.ACS+CX		;restore BLT register

	MOVE	CX,PI.ABT		;get command being processed
	AOSE	PI.CNT			;this the first time I've been called?
	 JRST	CTRLA3			;no, don't need to display file names
	SKIPE	TAKJFN			;is TAKE in progress?
	 TXNE	F,F%ECHO		;yes, am I echoing the commands?
	  JRST	CTRLA3			;yes, don't need to display file names
	SPTR	T1,< Comparing >
	CAIE	CX,CMPXIT		;COMPARE command?
	 SPTR	T1,< Updating >		;no
	PSOUTL
	FILSTR OLD+JFN
	TMSG < with >
	FILSTR NEW+JFN
CTRLA3:	SPTR	T1,<  >
	PSOUTL
	NUMOUT	CHGCNT
	TMSG < change>
	PSOUT%
	MOVEI	T1,"s"
	CAIE	T2,1			;more that 1 ?
	 PBOUT%				;yes, get tense right
	SPTR	T1,< found>
	CAIE	CX,CMPXIT		;COMPARE command?
	 SPTR	T1,< made>		;no
	PSOUT%

	DMOVE	P1,CAOLOC		;get previous ptr + page for OLDer file
	DMOVE	P3,CANLOC		;get previous ptr + page for NEWer file
	DMOVE	Q1,@LEVTAB-1+.CALV	;get flags + PC of next instruction
	HLRZ	T1,(Q2)			;get LH of interrupted instruction
	TRZ	T1,000777		;isolate op code
	CAIE	T1,(EXTEND)		;extended instruction?
	 JRST	CTRLA4			;no,
	HLRZ	T1,@(Q2)		;get left half of what EXTEND refers to
	TRZ	T1,000777		;isolate op code
	CAIE	T1,(MOVST)		;is it the move string instruction?
	 CAIN	T1,(CMPSN)		;is it the compare string instruction?
	  TRNA				;yes
	   JRST	CTRLA4			;no,
	LDB	Q2,[POINT 3,(Q2),12]	;get accumulator used
	MOVE	T1,PI.ACS+1(Q2)		;get 1st byte ptr from EXTEND
	CALL	CKLOC			;check which file it refers to
	MOVE	T1,PI.ACS+4(Q2)		;get 2nd byte ptr from EXTEND
	CALL	CKLOC			;check which file it refers to
	JRST	CTRLA7			;output file location data

; see if program is in FMATCH routine. If it is then the data in NP and
; OP can be trusted and used for outputing the file location data
CTRLA4:	TXNN	F,F%LSTK		;is program in FMATCH routine?
	 JRST	CTRLA7			;no,
	MOVE	T1,PI.ACS+OP		;get 1st byte ptr
	CALL	CKLOC			;check which file it refers to
	MOVE	T1,PI.ACS+NP		;get 2nd byte ptr
	CALL	CKLOC			;check which file it refers to

CTRLA7:	MOVEI	CX,OLD
	DMOVE	T1,P1			;get file location
	CALL	OUTLOC			;output file location
	DMOVEM	T1,CAOLOC		;save previous ptr + page for OLDer file
	MOVEI	CX,NEW
	DMOVE	T1,P3			;get file location
	CALL	OUTLOC			;output file location
	DMOVEM	T1,CANLOC		;save previous ptr + page for NEWer file
	TMSG <
>
	RET				;dismiss interrupt
;-----------------------------------------------------------------------------
;Routine to determine if a byte pointer points to the pmap area for
;the OLDer or NEWer file.
;	CALL CKLOC
;ACCEPTS:
;	T1 - byte pointer
;RETURNS: +1 always with P1,P2 updated if pointer points to OLDer file and P3,P4
;		updated if pointer points to NEWer file
;Trashes T1-T4

CKLOC:	MOVEI	CX,OLD
	CALL	CKLOC5			;see if pointer belongs to this file
	 JRST	CKLOC3			;yes
	MOVEI	CX,NEW
	CALL	CKLOC5			;see if pointer belongs to this file
	 DMOVEM	T1,P3			;yes, save info here
	TRNA
CKLOC3:	 DMOVEM	T1,P1			;yes, save info here
	RET

CKLOC5:	MOVE	T2,T1			;get byte pointer
	IBP	T2			;adjust to point to actual byte...
					; ...incase ptr on page boundry
	TLZ	T2,-1			;isolate address of ptr
	LSH	T2,-9			;get page ptr is on
	HRRZ	T3,FFP(CX)		;get start of pmap buffer
	SUB	T2,T3			;calc page offset
	JUMPL	T2,CKLOC9		;jump if ptr not in pmap area
	HRRZ	T3,FLP(CX)		;get length of pmap buffer
	CAML	T2,T3			;is ptr in pmap buffer?
CKLOC9:	 AOSA	(P)			;no, set +2 return
	  ADD	T2,FPG(CX)		;yes, add offset to page starting buffer
	RET

;-----------------------------------------------------------------------------
;Routine to output the approximate location of the file the program is currently
;working on
;	CALL OUTLOC
;ACCEPTS:
;	T1 - byte pointer to pmap area of the file
;	T2 - actual page of file the pointer refer to
;	CX - address of the FDA (File Data Area)
;RETURNS: +1 always
;Trashes T3-Q2

OUTLOC:	JUMPN	T1,OUTLO1		;jump if ptr is initialized
	SKIPN	T1,PTR(CX)		;get ptr
	 RET				;ptr not initialized yet
	IBP	T1			;adjust to point to actual byte...
					; ...incase ptr on page boundry
	HRRZ	T2,T1			;isolate address of ptr
	LSH	T2,-9			;get page ptr is on
	SUB	T2,FFP(CX)		;calc page offset
	HRRES	T2			;remove triva from LH
	ADD	T2,PFP(CX)		;add offset to ptr's file page

OUTLO1:	DMOVEM	T1,Q1			;save registers
	TMSG <	File>
	SPTR	T1,<1: >
	CAIE	CX,OLD			;working on the OLDer file?
	 SPTR	T1,<2: >		;no
	PSOUT%
	TMSG <page >
	NUMOUT	(-)			;output it
	IMULI	T2,^D100		;calc % of file I'm on...
	IDIV	T2,PGL(CX)		; ...
	MOVEM	T2,T4			;save % of file for later
	TMSG <.>
	HRRZ	T2,Q1			;get byte pointer
	TRZ	T2,777000		;isolate word on page
	IDIVI	T2,^D51			;calc approx. fraction of page I'm on
	NUMOUT (-)
	TMSG <, >
	NUMOUT (T4)			;output % of file I'm on
	TMSG <%>
	DMOVE	T1,Q1			;restore T1,T2
	RET
;=============================================================================
;Routines to handle ^E or ^X interrupts
CTRLX:	TMSGL <^X
>
	JRST	CTRLEX			;rejoin common code

CTRLE:	TMSGL <^E
>
	TXZ	F,F%WILD		;if I'm processing wild file specs...
					; ...then don't process anymore files

CTRLEX:	HRRZ	T1,OUT+JFN		;if output is to the terminal then...
	CATOFF				; ...make sure graphic attributes off
					; ...and also make sure OCCOC are set
	CAIN	CX,CMPXIT		;aborting UPDATE command?
	 TXO	F,F%ABRT		;yes, set abort flag
	IFN <.CXLV-.CELV>,<PX ?LEVEL for ^E and ^X must be the same>
	MOVE	T2,@LEVTAB-1+.CXLV	;get PC flags
	TXO	T2,PC%USR		;abort JSYS if I was executing one
	MOVE	T3,PI.ABT		;get address of exit routine
	DMOVEM	T2,@LEVTAB-1+.CXLV	;save new PC and flags
	MOVE	P,PI.P			;restore P
	ADJSP	P,-1			;remove call to ATIAEX
	DEBRK%				;dismiss interrupt
	SUBTTL	Switch Servers
;-----------------------------------------------------------------------------
;Server for the switch /NO:
.SWNO:	PARSE	(,<.CMKEY,,NOTAB>)
	HRRZ	T2,(T2)			;get address of service routine
	 JRST	(T2)			;	....dispatch to the handler
;------------------------------------------------------------------------------
;Server for SET LINES: number
.SL4M:	TXNE	F,F%NO			;user want NO lines?
	 IFSKP.
	NOISE	<to>
	CALL	.L4M
	CONFIRM
	MOVE	T2,L4M
	MOVEM	T2,DL4M
ELSE.
	TMSGL	<?Cannot use NO for this command>
ENDIF.
	JRST	ENDCMD

;-------------------------------------------------------------------------------
;Server for /LINES: switch

.L4M:	PARSE	(,<.CMNUM,CM%SDH,^D10,<a number from 1 to 64
 This is the number of lines which have to be common between the two
 files before before they are considered to match up again>,\DEFL4M>)
	CAIG	T2,MAXL4M
	 CAIGE	T2,1
	  JRST	[TMSGL <?/LINES: must be beween 1 and 64>
		JRST	ENDCMD]
	MOVEM	T2,L4M			;save number parsed
; routine to check the lines for match with lines to search to see if ok
CKL4M:	CAMLE	T2,L2S
	 JRST	[TMSGL <?/LINES: can't be bigger than /MAX-LINES:>
		JRST	ENDCMD]
	RET
;-----------------------------------------------------------------------------
;Server for SET MAX-LINES
.SL2S:	TXNE	F,F%NO			;user want NO ?
	 IFSKP.
	NOISE	<to>
	CALL	.L2S
	CONFIRM
ELSE.
	CONFIRM
	CALL	.NOMAX
ENDIF.
	MOVE	T2,L2S
	MOVEM	T2,DL2S
	JRST	ENDCMD

;-------------------------------------------------------------------------------
;Server for /MAX-LINES: switch

.L2S:	PARSE	(,<.CMNUM,CM%SDH,^D10,<a number from 0 to +INF
 This is the maximum number of lines the the program will search forward in
 order to find where the files start matching up again. If this limit is
 exceeded then the program will stop and ask whether or not to continue. If
 you use zero then the MAX-LINES limit will be canceled>,1000>)
	JUMPL	T2,[TMSGL <?/MAX-LINES: can't be less than zero>
		JRST	ENDCMD]
	SKIPG	T2			;if number is zero or less then...
.NOMAX:	 MOVE	T2,[377777,,777777]	; ...use maximum positive number
	MOVEM	T2,L2S			;save number parsed
	MOVE	T2,L4M
	CALLRET	CKL4M			;check L2S against L4M

;-----------------------------------------------------------------------------
;Server for SET QUICK
.SQUIC:	CONFIRM
	MOVE	T2,DSWI.S		;load default switches in T2
	IOR	F,T2			;set these switchs
	TXNN	F,F%NO			;did user enter "NO"?
	 IFSKP.				;no
	TXZ	F,F%QUIK		;yes, set QUICK off
	TXZ	T2,F%QUIK
ELSE.
	CALL	.QUICK			;check out if OK to set it
	TXO	T2,F%QUIK
ENDIF.
	MOVEM	T2,DSWI.S		;save default switches
	JRST	ENDCMD			;return

;-------------------------------------------------------------------------------
;Server for /QUICK switch
.QUICK:	TXNE	F,F%UPDF		;is /UPDATE-FORMAT also set?
	 JRST	QU.ERR			;yes
	TXO	F,F%QUIK		;set the switch
	RET				;no
QU.ERR:	TMSGL <?/UPDATE-FORMAT and /QUICK switches can't both be set>
	JRST	ENDCMD

;-------------------------------------------------------------------------------
;Server for /NO:QUICK switch
.NOQU:	TXZ	F,F%QUIK		;yes, set QUICK off
	RET

;-----------------------------------------------------------------------------
;Server for SET UPDATE
.SUPDF:	CONFIRM
	MOVE	T2,DSWI.S		;load default switches in T2
	IOR	F,T2			;set these switchs
	TXNN	F,F%NO			;did user enter "NO"?
	 IFSKP.				;no
	TXZ	F,F%UPDF		;yes, set UPDATE-FORMAT off
	TXZ	T2,F%UPDF
ELSE.
	CALL	.UPDF			;no, call .UPDF to set switch
	TXO	T2,F%UPDF
ENDIF.
	MOVEM	T2,DSWI.S		;save the default switches
	JRST	ENDCMD			;return

;-------------------------------------------------------------------------------
;Server for /UPDATE-FORMAT switch
.UPDF:	TXNE	F,F%QUIK		;is /UPDATE-FORMAT also set?
	  JRST	QU.ERR			;yes
	TXNE	F,F%EXE			;is /EXE also set?
	  JRST	UP.ERR			;yes
	TXO	F,F%UPDF		;set the switch
	RET				;return
UP.ERR:	TMSGL <?/UPDATE-FORMAT and /EXE switches can't both be set>
	JRST	ENDCMD

;-------------------------------------------------------------------------------
;Server for /NO:UPDATE switch
.NOUP:	TXZ	F,F%UPDF		;turn off the /UPDATE-FORMAT
	RET

;-------------------------------------------------------------------------------
;Server for SET IGNORE
;
.SIGNO:	NOISE	(<spaces, tabs, nulls, ^L's & multiple blank lines>)
	CONFIRM
	MOVE	T2,DSWI.S		;load default switches in T2
	IOR	F,T2			;set these switchs
	TXNN	F,F%NO			;did user enter "NO"?
	 IFSKP.				;no
	TXZ	F,F%IGNO		;yes, do not ignore!
	TXZ	T2,F%IGNO		;set swith in T2
ELSE.
	CALL	.IGNO			;no, go set ignore!
	TXO	T2,F%IGNO
ENDIF.
	MOVEM	T2,DSWI.S		;both default and F are the same
	JRST	ENDCMD			;go get another command

;-------------------------------------------------------------------------------
;Server for /IGNORE  switch
.IGNO:	TXNE	F,F%EXE			;is /EXE also set?
	 JRST	IG.ERR			;yes
	TXO	F,F%IGNO		;set the switch
	RET				;return
IG.ERR:	TMSGL <?/IGNORE and /EXE switches can't both be set>
	JRST	ENDCMD

;-------------------------------------------------------------------------------
;Server for /NO:IGNORE switch
.NOIGN:	TXZ	F,F%IGNO		;set F%IGNO to off
	RET				;return to caller
;-------------------------------------------------------------------------------
;Server for SET EXE
.SEXE:	NOISE	(<binary format>)
	CONFIRM
	MOVE	T2,DSWI.S		;load default switches in T2
	IOR	F,T2			;set these switchs
	TXNN	F,F%NO			;did user enter "NO"?
	 IFSKP.				;no
	CALL	.NOEXE			;yes, turn it off!
	TXZ	T2,F%EXE
ELSE.
	CALL	.EXE			;no, user want EXE to be on
	TXO	T2,F%EXE
ENDIF.
	MOVEM	T2,DSWI.S		;save the default switches
	JRST	ENDCMD			;go get another command

;-------------------------------------------------------------------------------
;Server for /EXE  switch
;input files are in binary format
.EXE:	TXO	F,F%EXE			;turn on the EXE switch
	TXZ	F,F%IGNO		;turn off ignore spaces, tab function
	TXZ	F,F%UPDF		;turn off update mode
	TXO	F,F%QUIK		;turn on quick mode
	RET				;return to caller

;-------------------------------------------------------------------------------
;Server for /NO:EXE switch
.NOEXE:	TXZ	F,F%EXE			;turn off the EXE switch
	TXZ	F,F%QUIK		;turn off the QUICK switch
	RET				;return to caller
	SUBTTL	Server for EXIT command
;=============================================================================
C.EXIT

	SUBTTL	Server for INFORMATION command
;=============================================================================
C.INFO <
	TMSG < Default switch settings: /LINES:>
	NUMOUT	(DL4M)
	TMSG < /MAX-LINES:>
	MOVE	T2,DL2S
	CAME	T2,[377777,,777777]
	 IFSKP.
	TMSG <+INF>
ELSE.
	NUMOUT (-)
ENDIF.
	MOVE	T2,DSWI.S		;get default switches set
	SPTR	T1,< /QUICK>
	TXNE	T2,F%QUIK
	 PSOUT%
	SPTR	T1,< /UPDATE-FORMAT>
	TXNE	T2,F%UPDF
	 PSOUT%
	SPTR	T1,< /EXE>
	TXNE	T2,F%EXE
	 PSOUT%
	SPTR	T1,< /IGNORE>
	TXNE	T2,F%IGNO
	 PSOUT%
	TMSG <
>
>;end of C.INFO
;=============================================================================
;Routine to handle when L2S in exceeded in FMAFIL routine.
;	CALL L2SEXC
;ACCEPTS:
;	T4 - FMALCT
;RETURNS:
;	+1 - user wants to continue search (a new, higher, /MAX-LINES: is set)
;	+2 - user does NOT want to continue. T4 will be set to -1.
;Trashes T1-T3

WHILWO:	TMSGL <?While working on change #>
	MOVE	T2,CHGCNT
	ADDI	T2,1
	NUMOUT	(-)
	TMSG < couldn't find where the files match up again
 even after searching ahead for >
	RET

L2SEXC:	CALL	WHILWO			;output "While working on..." message
	NUMOUT (L2S)			;output max lines to search
		TMSG < lines...

>
;	MOVEI	T1,DIE			;there is no exit routine
	SPTR	T2,<Do you want to continue? >	;prompt string for command level
	CALL	BEGCML##		;set up this command level
	PARSE	(,<.CMKEY,CM%SDH,YNTAB,<Answer YES or NO>>)
	HRRZ	T4,(T2)			;get answer parsed
	CONFIRM
	CALL	RMVCML##		;remove last command level
	JUMPE	T4,L2SEX8		;jump if answer was NO
;	MOVEI	T1,DIE			;there is no exit routine
	SPTR	T2,<New value for /MAX-LINES:>
	CALL	BEGCML##		;set up this command level
	CALL	.L2S			;get number of lines to search
	CONFIRM
	MOVE	T4,FMALCT		;reset T4
	CAML	T4,L2S			;is it bigger than last value?
	 IFSKP.				;no, display error message
	CALL	RMVCML##		;yes, remove last command level
	RET				;done
ENDIF.
	TMSGL <?Previous value for /MAX-LINES: was >
	NUMOUT (FMALCT)
	TMSG <. New value must be bigger
>
	JRST	ENDCMD			;ask user again

L2SEX8:	SETO	T4,			;set flag - don't continue search
	AOS	(P)			;set +2 return
	RET

;=============================================================================
;Routine to handle LSTK overflow. AOBJP will jump here when LSTK is full

LSTKOF:	CALL	WHILWO			;output "While working on..." message
	MOVEI	T2,LSTKLN		;get length of LSTK
	NUMOUT (-)			;output # lines searched
	TMSG < lines...
 Files can't be compared because of LSTK overflow (The internal value of LSTKLN
 may have to be increased and the the program reassembled)>
	JRST	DIE

LITPOL:	XLIST	;so user can identify literal pool when running DDT
	LIT	;put literals here
	LIST

	END <EVLEN,,ENTVEC>	;set length and start of entry vector