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 <[5m> ;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,<[7m> ;turn reverse video on
TRNA
.CATOF: SPTR T2,<[0m> ;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