MS-DOS/v2.0/source/FC.ASM

1684 lines
64 KiB
NASM
Raw Normal View History

1983-08-13 01:53:34 +01:00
title File Compare Routine for MSDOS 2.0
;-----------------------------------------------------------------------;
; Revision History: ;
; ;
; V1.0 Rev. 0 10/27/82 M.A.Ulloa ;
; ;
; Rev. 1 10/28/82 M.A.Ulloa ;
; Changed switch names and added binary compare using the ;
; -b switch. ;
; ;
; Rev. 1 11/4/82 A.R. Reynolds ;
; Messages in separate module ;
; Also added header for MSVER ;
; ;
; Rev. 2 11/29/82 M.A. Ulloa ;
; Corrected sysntex problem with references to [base...] ;
; ;
; Rev. 3 01/03/83 M.A. Ulloa ;
; Stack is right size now. ;
; ;
;-----------------------------------------------------------------------;
FALSE equ 0
TRUE equ 0ffh
buf_size equ 4096 ;buffer size
;-----------------------------------------------------------------------;
; Description ;
; ;
; FC [-# -b -w -c] <file1> <file2> ;
; ;
; Options: ;
; ;
; -# were # is a number from 1 to 9, how many lines have to ;
; before the end of an area of difference ends. ;
; ;
; -b will force a binary comparation of both files. ;
; ;
; -w will cause all spaces and tabs to be compressed to a single ;
; space before comparing. All leading and trailing spaces and/or tabs ;
; in a line are ignored. ;
; ;
; -c will cause FC to ignore the case of the letters. ;
; ;
; Algorithm for text compare: (The one for binary comp. is trivial) ;
; ;
; The files are read into two separate buffers and the ;
; comparation starts. If two lines are found to be different in the ;
; two buffers, say line i of buffer A and line j of buffer B differ. ;
; The program will try to match line i with line j+1, then with line ;
; j+2 and so on, if the end of buffer is reached the program will ;
; recompact the buffer and try to read more lines into the buffer, if ;
; no more lines can be read because either the buffer is full, or the ;
; end of file was reached, then it will revert and try to match line ;
; j of buffer B to line i+1, i+2 and so on of buffer A. If an end of ;
; buffer is found, it tries to refill it as before. If no matches are ;
; found, then it will try to match line i+1 of buffer A to line j+1, ;
; j+2, j+3, .... of buffer B, if still no matches are found, it reverts ;
; again and tries to match line j+1 of buffer B with lines i+2, i+3,... ;
; of buffer A. And so on till a match is found. ;
; ;
; Once a match is found it continues chcking pairs of lines till ;
; the specified number are matched (option #, 3 by default), and then ;
; it prints the differing area in both files, each followed by the ;
; first line matched. ;
; ;
; If no match is found (the difference is bigger than the buffer) ;
; a "files different" message is printed. ;
; ;
; If one of the files finishes before another the remaining ;
; portion of the file (plus any ongoing difference) is printed out. ;
; ;
;-----------------------------------------------------------------------;
subttl Debug Macros
page
m_debug macro str
local a,b
jmp short b
a db str,0dh,0ah,"$"
b: pushf
push dx
mov dx,offset code:a
push ds
push cs
pop ds
push ax
mov ah,9h
int 21h
pop ax
pop ds
pop dx
popf
endm
m_bname macro
local a0,a1,a2,b1,b2
jmp short a0
b1 db "------ buffer 1",0dh,0ah,"$"
b2 db "------ buffer 2",0dh,0ah,"$"
a0: pushf
push dx
cmp bx,offset dg:buf1
je a1
mov dx,offset code:b2
jmp short a2
a1: mov dx,offset code:b1
a2: push ds
push cs
pop ds
push ax
mov ah,9h
int 21h
pop ax
pop ds
pop dx
popf
endm
page
.SALL
.XLIST
include dossym.asm
.LIST
subttl General Definitions
page
CR equ 0dh
LF equ 0ah
;-----------------------------------------------------------------------;
; Offsets to buffer structure
; For text comparations:
fname equ 0 ;file name ptr
fname_len equ 2 ;file name length
handle equ 4 ;handle
curr equ 6 ;current line ptr
lst_curr equ 8 ;last current line ptr
fst_sinc equ 10 ;first line towards a sinc ptr
fst_nosinc equ 12 ;first line out of sinc ptr
dat_end equ 14 ;ptr to last char of the buffer
buf_end equ 16 ;pointer to the end of the buffer
buf equ 18 ;pointer to the buffer
; For binary comparations:
by_read equ 6 ;bytes read into buffer
;-----------------------------------------------------------------------;
code segment word
code ends
const segment public word
const ends
data segment word
data ends
dg group code,const,data
subttl Constants Area
page
const segment public word
make db "MAUlloa/Microsoft/V10"
rev db "2"
;----- CAREFULL WITH PRESERVING THE ORDER OF THE TABLE -----
opt_tbl equ $ ;option table
flg_b db FALSE
flg_c db FALSE
flg_s db FALSE
flg_w db FALSE
;-----------------------------------------------------------
ib_first1 db FALSE ;flags used when comparing lines
ib_first2 db FALSE ; while in ignore white mode.
m_num dw 3 ;lines that have to match before
; reporting a match
mtch_cntr dw 0 ;matches towards a sinc
mode db FALSE ;If false then trying to match a line
; from buf1 to lines in buf2. If true
; then viceversa.
sinc db TRUE ;Sinc flag, start IN SINC
bend db 0 ;binary end of file flag, 0= none yet,
; 1= file 1 ended, 2= file 2 ended
base dd 0 ;base address of files for binary
; comparations
bhead_flg db false ;true if heading for binary comp.
; has been printed already.
;-----------------------------------------------------------
bp_buf equ $ ;binary compare difference template
bp_buf1 db 8 dup(' ') ;file address
db 3 dup(' ')
bp_buf2 db 2 dup(' ') ;byte of file 1
db 3 dup(' ')
bp_buf3 db 2 dup(' ') ;byte of file 1
db CR,LF
bp_buf_len equ $ - bp_buf ;length of template
;-----------------------------------------------------------
EXTRN vers_err:byte,opt_err:byte,opt_e:byte,crlf:byte,opt_err_len:byte
EXTRN bhead_len:byte
EXTRN found_err_pre:byte,found_err_pre_len:byte
EXTRN found_err_post:byte,found_err_post_len:byte
EXTRN read_err_pre:byte,read_err_pre_len:byte
EXTRN read_err_post:byte,read_err_post_len:byte
EXTRN file_err:byte,file_err_len:byte
EXTRN bf1ne:byte,bf1ne_len:byte,bf2ne:byte,bf2ne_len:byte,bhead:byte
EXTRN int_err:byte,int_err_len:byte,dif_err:byte,dif_err_len:byte
EXTRN args_err:byte,args_err_len:byte,fname_sep:byte,fname_sep_len:byte
EXTRN diff_sep:byte,diff_sep_len:byte
const ends
subttl Data Area
page
data segment word
com_buf db 128 dup(?) ;command line buffer
;----- Buffer structures
buf1 dw 11 dup(?)
buf2 dw 11 dup(?)
; two extra for guard in case of need to insert a CR,LF pair
b1 db buf_size dup(?)
end_b1 db 2 dup(?)
b2 db buf_size dup(?)
end_b2 db 2 dup(?)
data ends
subttl MAIN Routine
page
code segment
assume cs:dg,ds:nothing,es:nothing,ss:stack
start:
jmp short FCSTRT
;-----------------------------------------------------------------------;
; Check version number
HEADER DB "Vers 1.00"
FCSTRT:
;Code to print header
; PUSH DS
; push cs
; pop ds
; MOV DX,OFFSET DG:HEADER
; mov ah,std_con_string_output
; int 21h
; POP DS
mov ah,get_version
int 21h
cmp al,2
jge vers_ok
mov dx,offset dg:vers_err
mov ah,std_con_string_output
int 21h
push es ;bad vers, exit a la 1.x
xor ax,ax
push ax
badvex proc far
ret
badvex endp
vers_ok:
push cs
pop es
assume es:dg
;-----------------------------------------------------------------------;
; Copy command line
mov si,80h ;command line address
cld
lodsb ;get char count
mov cl,al
xor ch,ch
inc cx ;include the CR
mov di,offset dg:com_buf
cld
rep movsb
push cs
pop ds
assume ds:dg
;-----------------------------------------------------------------------;
; Initialize buffer structures
mov bx,offset dg:buf1
mov word ptr [bx].buf,offset dg:b1
mov word ptr [bx].buf_end,offset dg:end_b1
mov bx,offset dg:buf2
mov word ptr [bx].buf,offset dg:b2
mov word ptr [bx].buf_end,offset dg:end_b2
;-----------------------------------------------------------------------;
; Process options
mov ah,char_oper
mov al,0
int 21h ;get switch character
mov si,offset dg:com_buf
cont_opt:
call kill_bl
jc bad_args ;arguments missing
cmp al,dl ;switch character?
jne get_file ;no, process file names
cld
lodsb ;get option
call make_caps ;capitalize option
mov bx,offset dg:opt_tbl
cmp al,'B'
je b_opt
cmp al,'C'
je c_opt
cmp al,'S'
je s_opt
cmp al,'W'
je w_opt
cmp al,'1' ;a number option?
jb bad_opt
cmp al,'9'
ja bad_opt
and al,0fh ;a number option, convert to binary
xor ah,ah ;zero high nibble
mov [m_num],ax
jmp short cont_opt
bad_opt: ;a bad option:
push dx ; save switch character
mov [opt_e],al ; option in error
mov dx,offset dg:opt_err
mov cl,opt_err_len
call prt_err ; print error message
pop dx
jmp short cont_opt ; process rest of options
b_opt:
mov di,0
jmp short opt_dispatch
c_opt:
mov di,1
jmp short opt_dispatch
s_opt:
mov di,2
jmp short opt_dispatch
w_opt:
mov di,3
opt_dispatch:
mov byte ptr dg:[bx+di],TRUE ;set the corresponding flag
jmp short cont_opt
bad_args:
mov dx,offset dg:args_err
mov cl,args_err_len
jmp an_err
;-----------------------------------------------------------------------;
; Get the file names
get_file:
dec si ;adjust pointer
call find_nonb ;find first non blank in com. buffer
jc bad_args ;file (or files) missing
mov byte ptr [di],0 ;nul terminate
mov dx,si ;pointer to file name
mov bx,offset dg:buf1
mov word ptr [bx].fname,dx ;save pointer to file name
mov word ptr [bx].fname_len,cx ;file name length
mov ah,open
mov al,0 ;open for reading
int 21h
jc bad_file
mov word ptr [bx].handle,ax ;save the handle
mov si,di
inc si ;point past the nul
call kill_bl ;find other file name
jc bad_args ;a CR found: file name missing
dec si ;adjust pointer
call find_nonb
mov byte ptr [di],0 ;nul terminate the file name
mov dx,si
mov bx,offset dg:buf2
mov word ptr [bx].fname,dx ;save pointer to file name
mov word ptr [bx].fname_len,cx ;file name length
mov ah,open
mov al,0 ;open for reading
int 21h
jc bad_file
mov word ptr [bx].handle,ax ;save the handle
jmp short go_compare
bad_file:
cmp ax,error_file_not_found
je sj01
mov dx,offset dg:int_err
mov cl,int_err_len
jmp short an_err
sj01:
push cx ;save file name length
mov dx,offset dg:found_err_pre
mov cl,found_err_pre_len
call prt_err
pop cx
mov dx,si ;pointer to file name length
call prt_err
mov dx,offset dg:found_err_post
mov cl,found_err_post_len
an_err:
call prt_err
mov al,-1 ;return an error code
mov ah,exit
int 21h
;-----------------------------------------------------------------------;
; CHECK COMPARE MODE
go_compare:
cmp [flg_b],true ;do we do a binary comparation?
je bin_compare
jmp txt_compare
subttl Binary Compare Routine
page
;-----------------------------------------------------------------------;
; COMPARE BUFFERS IN BINARY MODE
bin_compare:
;----- Fill in the buffers
mov bx,offset dg:buf1 ;pointer to buffer structure
mov dx,word ptr[bx].buf ;pointer to buffer
mov si,dx ;save for latter comparation
call read_dat ;read into buffer
jc bad_datj ;an error
mov word ptr[bx].by_read,AX ;save ammount read
push ax ;save for now
mov bx,offset dg:buf2 ;pointer to buffer structure
mov dx,word ptr[bx].buf ;pointer to buffer
mov di,dx ;save for comparation
call read_dat ;read into buffer
bad_datj: jc bad_dat ;an error
mov word ptr[bx].by_read,AX ;save ammount read
pop cx ;restore byte count of buffer1
cmp ax,cx ;compare byte counts
ja morein_b2
jb morein_b1
or ax,ax ;the same ammount, is it 0?
jne go_bcomp ;no,compare
jmp go_quit ;yes, all done....
morein_b2:
mov [bend],1 ;file 1 ended
jmp short go_bcomp
morein_b1:
mov [bend],2 ;file 2 ended
mov cx,ax
;----- Compare data in buffers
go_bcomp:
mov ax,word ptr [base] ;load base addrs. to AX,BX pair
mov bx,word ptr [base+2]
add bx,cx ;add to base num. of bytes to
adc ax,0 ; compare.
mov word ptr [base],ax ;save total
mov word ptr [base+2],bx
next_bcomp:
cld
jcxz end_check
repz cmpsb ;compare both buffers
jz end_check ;all bytes match
push cx ;save count so far
push ax
push bx
inc cx
sub bx,cx ;get file address of bytes that
sbb ax,0 ; are different.
call prt_bdif ;print difference
pop bx
pop ax
pop cx ;restore on-going comparation count
jmp short next_bcomp
bnot_yet:
jmp bin_compare
end_check:
cmp [bend],0 ;have any file ended yet?
je bnot_yet ;no, read in more data
cmp [bend],1 ;yes, was it file 1?
je bf1_ended ;yes, data left in file 2
mov dx,offset dg:bf1ne
mov cl,bf1ne_len
jmp short bend_mes
bf1_ended:
mov dx,offset dg:bf2ne
mov cl,bf2ne_len
bend_mes:
xor ch,ch
call prout
jmp go_quit
subttl Text Compare Routine
page
;-----------------------------------------------------------------------;
; Fill in the buffers
bad_dat:
mov dx,offset dg:file_err
mov cl,file_err_len
jmp an_err
txt_compare:
mov bx,offset dg:buf1
mov dx,word ptr [bx].buf
mov word ptr [bx].fst_nosinc,dx
mov word ptr [bx].curr,dx
call fill_buffer
jc bad_dat
mov bx,offset dg:buf2
mov dx,word ptr [bx].buf
mov word ptr [bx].fst_nosinc,dx
mov word ptr [bx].curr,dx
call fill_buffer
jc bad_dat
;-----------------------------------------------------------------------;
; COMPARE BUFFERS IN TEXT MODE
another_line:
call go_match ;try to match both current lines
jc sj02 ;a match
jmp no_match ;no match, continue....
sj02:
cmp byte ptr[sinc],true ;are we in SINC?
je sj04
mov ax,[mtch_cntr]
or ax,ax ;first line of a possible SINC?
jnz sj03
mov bx,offset dg:buf1
mov word ptr [bx].fst_sinc,si ;yes, save curr line buffer 1
mov bx,offset dg:buf2
mov word ptr [bx].fst_sinc,di ;save curr line buffer 2
sj03:
inc ax ;increment match counter
mov [mtch_cntr],ax ;save number of matches
cmp m_num,ax ;enough lines matched for a SINC?
jne sj04 ;not yet, match some more
mov [sinc],true ;yes, flag we are now in sinc
call print_diff ;print mismatched lines
;-----------------------------------------------------------------------;
; Advance current line pointer in both buffers
sj04:
mov bx,offset dg:buf1
call adv_b
jnc sj05
jmp no_more1
sj05:
mov word ptr[bx].curr,si
mov bx,offset dg:buf2
call adv_b
jnc sj051
jmp no_more2
sj051:
mov word ptr[bx].curr,si
jmp another_line ;continue matching
;-----------------------------------------------------------------------;
; Process a mismatch
no_match:
cmp [sinc],true ;are we in SINC?
jne sj06
mov [sinc],false ;not any more....
mov bx,offset dg:buf1
mov word ptr [bx].fst_nosinc,si ;save current lines
mov word ptr [bx].lst_curr,si
mov bx,offset dg:buf2
mov word ptr [bx].fst_nosinc,di
mov word ptr [bx].lst_curr,di
sj06:
mov [mtch_cntr],0 ;reset match counter
cmp [mode],true
je sj09
;----- MODE A -----
mov bx,offset dg:buf2
call adv_b ;get next line in buffer (or file)
jc sj08 ;no more lines in buffer
sj07:
mov word ptr [bx].curr,si
jmp another_line
sj08:
mov [mode],true ;change mode
mov si,word ptr [bx].lst_curr
mov word ptr [bx].curr,si
mov bx,offset dg:buf1
mov si,word ptr [bx].lst_curr
mov word ptr [bx].curr,si
call adv_b ;get next line
jc no_more1 ;no more lines fit in buffer 1
mov word ptr [bx].lst_curr,si
jmp short sj10
;----- MODE B -----
sj09:
mov bx,offset dg:buf1
call adv_b ;get next line in buffer (or file)
jc sj11 ;no more lines in buffer
sj10:
mov word ptr [bx].curr,si
jmp another_line
sj11:
mov [mode],false
mov si,word ptr [bx].lst_curr
mov word ptr [bx].curr,si
mov bx,offset dg:buf2
mov si,word ptr [bx].lst_curr
mov word ptr [bx].curr,si
call adv_b ;get next line
jc no_more2 ;no more lines fit in buffer 2
mov word ptr [bx].lst_curr,si
jmp sj07
;-----------------------------------------------------------------------;
; Process end of files
no_more1:
cmp ax,0 ;end of file reached?
jz xj1
jmp dif_files ;no, difference was too big
xj1:
cmp [sinc],true ;file1 ended, are we in SINC?
je xj3
jmp no_sinc
xj3:
mov bx,offset dg:buf2
call adv_b ;advance current line in buf2
jnc xj5
jmp go_quit ;file2 ended too, terminate prog.
xj5:
;----- File 1 ended but NOT file 2
mov bx,offset dg:buf1
call print_head
mov bx,offset dg:buf2
call print_head
call print_all ;print the rest of file2
jmp go_quit
no_more2:
cmp ax,0 ;end of file reached?
jz xj2
jmp dif_files ;no, difference was too big
xj2:
cmp [sinc],true ;file1 ended, are we in SINC?
je xj4
jmp no_sinc
xj4:
mov bx,offset dg:buf1
call adv_b ;advance current line in buf2
jnc xj6
jmp go_quit ;file2 ended too, terminate prog.
xj6:
;----- File 2 ended but NOT file 1
mov bx,offset dg:buf1
call print_head
call print_all ;print the rest of file1
mov bx,offset dg:buf2
call print_head
jmp go_quit
no_sinc:
mov bx,offset dg:buf1
call print_head
call print_all
mov bx,offset dg:buf2
call print_head
call print_all
jmp go_quit
dif_files:
mov dx,offset dg:dif_err
mov cl,dif_err_len
jmp an_err
go_quit:
mov al,0
mov ah,exit
int 21h
subttl Subroutines: make caps
page
;-----------------------------------------------------------------------;
; CAPIALIZES THE CHARACTER IN AL ;
; ;
; entry: ;
; AL has the character to Capitalize ;
; ;
; exit: ;
; AL has the capitalized character ;
; ;
; Called from MAIN and go_match ;
;-----------------------------------------------------------------------;
make_caps:
cmp al,'a'
jb sa1
cmp al,'z'
jg sa1
and al,0dfh
sa1: ret
subttl Subroutines: kill_bl
page
;-----------------------------------------------------------------------;
; Get rid of blanks in command line. ;
; ;
; entry: ;
; SI points to the first character on the line to scan. ;
; ;
; exit: ;
; SI points to the next char after the first non-blank ;
; char found. ;
; Carry Set if a CR found ;
; ;
; modifies: ;
; SI and AX ;
; ;
; Called from MAIN ;
;-----------------------------------------------------------------------;
kill_bl:
cld ;increment
sb1: lodsb ;get rid of blanks
cmp al,' '
je sb1
cmp al,9
je sb1
cmp al,CR
clc ;assume not a CR
jne sb2
stc ;a CR found, set carry
sb2: ret
subttl Subroutines: find_nonb
page
;-----------------------------------------------------------------------;
; Find the first non-blank in a line ;
; ;
; entry: ;
; SI points to the line buffer ;
; ;
; exit: ;
; DI pointer to the first blank found (incl. CR) ;
; CX character count of non-blanks ;
; Carry Set if a CR was found ;
; ;
; modifies: ;
; AX ;
; ;
; Called from MAIN ;
;-----------------------------------------------------------------------;
find_nonb:
push si ;save pointer
xor cx,cx ;zero character count
cld
sc1:
lodsb
cmp al,' '
je sc2
cmp al,9
je sc2
cmp al,CR
je sc2
inc cx ;inc character count
jmp short sc1
sc2:
dec si
mov di,si
pop si
cmp al,CR
jne sc3
stc
ret
sc3:
clc
ret
subttl Subroutines: prt_bdif
page
;-----------------------------------------------------------------------;
; Print a binary difference ;
; ;
; entry: ;
; AX,BX file address of diference ;
; SI pointer to one past byte in buffer1 ;
; DI pointer to one past byte in buffer2 ;
; ;
; modifies: ;
; AX, DX and CX ;
; ;
; called from bin_compare ;
;-----------------------------------------------------------------------;
prt_bdif:
cmp [bhead_flg],true ;have we peinted head yet?
je bhead_ok
mov [bhead_flg],true ;no, set flag
push ax ;print heading
mov dx,offset dg:bhead
mov cl,bhead_len
xor ch,ch
call prout
pop ax
bhead_ok:
mov dx,di ;conver file address
mov di,offset dg:bp_buf1
push ax
mov al,ah
call bin2hex
pop ax
call bin2hex
mov al,bh
call bin2hex
mov al,bl
call bin2hex
mov di,offset dg:bp_buf2 ;convert byte from file 1
mov al, byte ptr[si-1]
call bin2hex
mov di,offset dg:bp_buf3 ;convert byte from file 2
push si
mov si,dx
mov al, byte ptr[si-1]
pop si
call bin2hex
mov di,dx ;print result
mov dx,offset dg:bp_buf
mov cx,bp_buf_len
call prout
ret
subttl Subroutines: bin2hex
page
;-----------------------------------------------------------------------;
; Binary to ASCII hex conversion ;
; ;
; entry: ;
; AL byte to convert ;
; DI pointer to were the two result ASCII bytes should go ;
; ;
; exit: ;
; DI points to one past were the last result byte whent ;
; ;
; modifies: ;
; AH and CL ;
; ;
; Called from prt_bdif ;
;-----------------------------------------------------------------------;
bin2hex:
mov cl,4
ror ax,cl ;get the high nibble
and al,0fh ;mask of high nible
call pt_hex
rol ax,cl ;get the low nibble
and al,0fh ;mask....
pt_hex:
cmp al,0ah ;is it past an A ?
jae pasta
add al,30h
jmp short put_hex
pasta:
add al,37h
put_hex:
stosb ;place in buffer
ret
subttl Subroutines: go_match
page
;-----------------------------------------------------------------------;
; Match current lines ;
; ;
; exit: ;
; Carry set if the match reset otherwise ;
; SI Current line of buff1 ;
; DI Current line of buff2 ;
; ;
; ;
; modifies: ;
; AX,BX,CX,DX and BP ;
; ;
; Called from txt_compare ;
;-----------------------------------------------------------------------;
go_match:
mov bx,offset dg:buf1
mov si,word ptr[bx].curr
push si
mov bp,si ;save line pointer
call find_eol
mov dx,cx ;save length of line
mov bx,offset dg:buf2
mov si,word ptr[bx].curr
push si
mov di,si
call find_eol
cmp cx,dx ;compare lengths
jne sd1 ;they do not match
mov si,bp ;restore line pointer
jcxz sd4 ;both length = 0, they match
push cx ;save the length
cld
repz cmpsb ;compare strings
pop cx ;restore the length
jz sd4 ;they match
sd1:
cmp [flg_w],true ;do we ignore multiple whites?
je ib_compare ;yes, go compare
cmp [flg_c],true ;do we ignore case differences?
je ic_compare ;yes, go compare
sd3:
clc ;they don't match
jmp short sd5
sd4:
stc
sd5:
pop di ;curr2
pop si ;curr1
ret
page
;-----------------------------------------------------------------------;
; Compare ignoring case differences.
ic_compare:
pop di ;get pointer to lines
pop si
push si ;re-save pointers
push di
sd8:
mov al,byte ptr [si] ;get next char. of first line
call make_caps
mov bl,al ;save capitalized char
mov al,byte ptr [di] ;get next chra. of second line
call make_caps
cmp al,bl
jne sd3 ;they do not match....
inc si ;advance pointers
inc di
loop sd8 ;loop for the line lengths
jmp short sd4 ;they match
page
;-----------------------------------------------------------------------;
; Compare compressing whites and ignoring case differences if
; desired too.
ib_compare:
mov [ib_first1],true ;we start by the first char in the
mov [ib_first2],true ; in the lines.
pop di ;get pointer to lines
pop si
push si ;re-save pointers
push di
sd9:
mov al,byte ptr [si] ;get next char. of first line
call isa_white ;is it a white?
jnc sd12 ;no, compare....
sd10:
mov al,byte ptr [si+1] ;peek to next,
call isa_white ; it is a white too?
jnc sd11
inc si ; yes,
jmp short sd10 ; compress all whites to a blank
sd11:
cmp [ib_first1],true ;is this the first char. of the line?
jne sd111 ;no, it stays a white
inc si ;ignore the white
jmp short sd12
sd111:
cmp al,CR ;is this the last char. of the line
jne sd112 ;no, it stays a white
inc si ;yes, ignore the whites
jmp short sd12
sd112:
mov al,' ' ;no more whites found
sd12:
cmp [ib_first1],true ;is this the first char. of the line?
jne sd121 ;no, continue
mov [ib_first1],false ;yes, reset the flag
sd121:
cmp [flg_c],true ;do we ignore case?
jne sd122 ;no,....
call make_caps
sd122:
mov bl,al ;save char
mov al,byte ptr [di] ;get next chra. of second line
call isa_white
jnc sd15
sd13:
mov al,byte ptr [di+1] ;peek to next as before
call isa_white
jnc sd14
inc di
jmp short sd13
sd14:
cmp [ib_first2],true ;is this the first char. of the line?
jne sd141 ;no, it stays a white
inc di ;ignore the white
jmp short sd15
sd141:
cmp al,CR ;is this the last char. of the line
jne sd142 ;no, it stays a white
inc si ;yes, ignore the whites
jmp short sd15
sd142:
mov al,' '
sd15:
cmp [ib_first2],true ;is this the first char. of the line?
jne sd151 ;no, continue
mov [ib_first2],false ;yes, reset the flag
sd151:
cmp [flg_c],true ;do we ignore case?
jne sd152 ;no,....
call make_caps
sd152:
cmp al,bl
je sd153
jmp sd3 ;they do not match....
sd153:
cmp al,CR ;have we reached the end?
jne sd154 ;no, continue....
jmp sd4 ;yes, they match
sd154:
inc si ;no, advance pointers
inc di
jmp sd9 ;loop for the line lengths
isa_white:
cmp al,' ' ;is it a space?
je sdx1
cmp al,09h ;is it a tab?
je sdx1
clc ;if not a white return with carry clear
ret
sdx1:
stc ;is a white return with carry set
ret
page
;-----------------------------------------------------------------------;
find_eol:
xor cx,cx ;zero count
cld
sd6:
lodsb
cmp al,CR
je sd7
inc cx
jmp short sd6
sd7:
ret
subttl Subroutines: adv_b
page
;-----------------------------------------------------------------------;
; Get the next line in the buffer ;
; ;
; It will attempt to get the next current line from the buffer ;
; if it fails, it will force a refill, and if some data is read in ;
; then it will return the next current line. ;
; ;
; entry: ;
; BX pointer to buffer structure ;
; ;
; exit: ;
; SI pointer to next line (if any) ;
; Carry set if no more lines available. If carry set then: ;
; AX End Code: 0 = end of file reached ;
; 1 = no room in buffer for a line ;
; ;
; modifies: ;
; CX,DX and DI ;
; ;
; Called from txt_compare ;
;-----------------------------------------------------------------------;
adv_b:
call get_nextl
jc se1
ret
se1:
call refill
jnc se0
ret
se0:
call get_nextl
ret
subttl Subroutines: get_nextl
page
;-----------------------------------------------------------------------;
; Returns the next line in a buffer ;
; (next from current or next from pointer) ;
; ;
; entry: ;
; BX pointer to buffer structure ;
; (SI pointer to line, if calling get_next) ;
; ;
; exit: ;
; SI pointer to next line ;
; Carry set if no more lines available ;
; ;
; modifies: ;
; DI and CX ;
; ;
; Called from adv_b and print_diff (in the case of get_next) ;
;-----------------------------------------------------------------------;
get_nextl:
mov si,word ptr [bx].curr
get_next:
mov cx,word ptr [bx].dat_end
sub cx,si
mov di,si
mov al,LF
cld
repnz scasb
mov si,di ;pointer to next line
jnz se2 ;not found
clc
ret
se2:
inc si ;point past the LF
stc
ret
subttl Subroutines: refill
page
;-----------------------------------------------------------------------;
; Refill a buffer ;
; ;
; It will refill a buffer with data from the corresponding ;
; file. It will first recompact the buffer to make room for the new ;
; data. If in SINC then it will move the current line to the top of ;
; the buffer, and read the data from the end of this line till the ;
; end of the buffer. ;
; If NOT in SINC then it will recompact the buffer by moving ;
; all lines between the first to go out of SINC till the current line ;
; to the top of the buffer, and then reading data after the current ;
; line. ;
; When recompacting the buffer it relocates all pointers to ;
; point to the new locations of the respective lines. ;
; Some of the pointers may be pointing to meaningless locations ;
; before the relocation, and consecuently they will be pointing to ;
; even less meaningfull locations after relocation. ;
; After reading the data it normalizes the buffer to make sure ;
; that no partially full lines are present at the end of the buffer. If ;
; after recompacting and reading some character it is found that the ;
; characters read do not constitute a full line, then it will return ;
; with an error code. It will also return with an error code if it ;
; attempts to read past the end of file. ;
; ;
; entry: ;
; BX pointer to buffer structure ;
; ;
; exit: ;
; Carry set if no chars read into the buffer. If carry set then: ;
; AX End Code: 0 = end of file reached ;
; 1 = no room in the buffer for a line ;
; ;
; modifies: ;
; CX,DX,SI and DI ;
; ;
; Called from adv_b ;
;-----------------------------------------------------------------------;
refill:
;----- Calculate ammount to move & pointer relocation factor.
cmp [sinc],true
jne sf1
mov si,word ptr [bx].curr
jmp short sf2
sf1:
mov si,word ptr [bx].fst_nosinc
sf2:
mov di,word ptr [bx].buf
mov cx,word ptr [bx].dat_end
mov dx,si ;calculate pointer relocation factor
sub dx,di ;DX = factor
jz sf3 ;no room in buffer
sub cx,si ;calculate ammount of data to move
inc cx ;CX = ammount
;----- Move data
cld ;auto decrement
rep movsb
;----- Relocate pointers
sub word ptr [bx].curr,dx
sub word ptr [bx].lst_curr,dx
sub word ptr [bx].fst_sinc,dx
sub word ptr [bx].fst_nosinc,dx
sub word ptr [bx].dat_end,dx
sf3:
mov dx,word ptr [bx].dat_end
inc dx ;empty part starts here
;----- fill the buffer
call fill_buffer
ret
subttl Subroutines: fill_buffer
page
;-----------------------------------------------------------------------;
; Fill the data buffers ;
; ;
; It will fill the buffer from the pointer to the end of buffer ;
; and normalize the buffer. ;
; ;
; entry: ;
; BX pointer to buffer structure ;
; DX pointer to buffer (or part of buffer) ;
; ;
; exit: ;
; Carry set if no chars read into the buffer. If carry set then: ;
; AX End Code: 0 = end of file reached ;
; 1 = no room in the buffer for a line ;
; ;
; modifies: ;
; AX,CX,DX and DI ;
; ;
; Called from txt_compare and refill ;
;-----------------------------------------------------------------------;
fill_buffer:
push bx
call read_dat ;get data
jc bad_read
or ax,ax ;zero chars read?
jz rd_past_eof
call nor_buf
mov di,cx ;save normalized char. count
mov bp,dx ;save data end for now
;----- seek for old partial line
or ax,ax ;is the seek value = 0 ?
jz sg1 ;yes, do not seek
mov dx,ax
neg dx
mov cx,-1
mov al,1 ;seek from current position
mov ah,lseek
int 21h
jc bad_read ;error mesage (BX already in stack)
sg1:
mov cx,di ;restore normalized char count.
or cx,cx ;char count = 0 due to normalization?
jz no_room
pop bx
mov word ptr [bx].dat_end,bp
clc
ret
bad_read:
mov dx,offset dg:read_err_pre
mov cl,read_err_pre_len
call prt_err ;print error message
pop bx
mov dx,word ptr[bx].fname
mov cx,word ptr[bx].fname_len
call prt_err ;print file name
mov dx,offset dg:read_err_post
mov cl,read_err_post_len
jmp an_err
no_room:
mov ax,1
jmp short sg2
rd_past_eof:
xor ax,ax
sg2:
pop bx
stc
ret
subttl Subroutines: read_dat
page
;-----------------------------------------------------------------------;
; ;
; entry: ;
; DX pointer to data area (buffer or part of buffer) ;
; ;
; exit: ;
; AX character count or error code (from DOS read) ;
; Carry set if error condition ;
; ;
; modifies: ;
; BX and CX ;
; ;
; Called from fill_buffer, print_all and bin_compare ;
;-----------------------------------------------------------------------;
read_dat:
mov cx,word ptr [bx].buf_end
mov bx,word ptr [bx].handle
sub cx,dx ;ammount to read to buff1
mov ah,read
int 21h
ret
subttl Subroutines: nor_buf
page
;-----------------------------------------------------------------------;
; Normalize buffers so they do not have partially full ;
; lines at the end. If character count is less than the buffer size ;
; then it checks that the last line is terminated by a CR,LF pair. ;
; If it is not it inserts a CR,LF at the end. It returns a seek value ;
; for the buffer corresponding to the number of characters in the ;
; incomplete line at the end of the buffer (if any). This can be used ;
; to start reading from the beggining of the incomplete line on next ;
; time the buffer is loaded. ;
; ;
; ENTRY: ;
; DX buffer pointer ;
; AX character count read ;
; CX character count requested ;
; ;
; EXIT: ;
; DX pointer to last char in buffer (normalized) ;
; CX character count (normalized) ;
; AX seek value ;
; ;
; MODIFIES: ;
; DI ;
; ;
; Called from fill_buffer ;
;-----------------------------------------------------------------------;
nor_buf:
mov di,dx
add di,ax
dec di ;points to last char in buffer
cmp ax,cx ;were all chars. requested read?
je sm7 ;yes, buffer full
cmp byte ptr[di],1ah ;terminated with a ^Z ?
jne sm1
dec di ;point to previous character
dec ax ;decrement character count
sm1: cmp byte ptr[di],lf ;is last char a LF?
je sm6
cmp byte ptr[di],cr ;is it a CR then?
je sm5
add ax,2 ;two more chars in buffer
inc di
sm2: mov byte ptr[di],cr
sm3: inc di
mov byte ptr[di],lf
sm4: mov cx,ax ;new character count
mov dx,di ;pointer to last char
xor ax,ax ;seek = 0
ret
sm5:
inc ax ;one more char in buffer
jmp short sm3
sm6:
cmp byte ptr[di-1],cr ;is previous char a CR?
je sm4
inc ax ;no, one more char in buffer
jmp short sm2
sm7:
push ax ;save char count
mov cx,ax
mov al,LF
std
repnz scasb ;search for last LF
pop ax ;restore char count
jnz bad_line ;none found, line too big
inc di ;point to last LF
mov dx,di
inc cx ;ammount of chars in buffer
sub ax,cx ;seek value
ret
bad_line: ;full line not possible, return
mov dx,di ; with AX=count, CX=0 and DX=
ret ; old last char in buffer pointer.
subttl Subroutines: print_diff
page
;-----------------------------------------------------------------------;
; print the difference between buffers ;
; ;
; It will print the mismatched lines. First it prints a heading ;
; with the first file name, then the lines that differ from file 1, ;
; then a heading with the second file name, and then the lines that ;
; differ in file 2 . ;
; The lines that differ are considered to start from fst_nosinc ;
; till fst_sinc. ;
; ;
; Called from txt_compare ;
;-----------------------------------------------------------------------;
print_diff:
mov bx,offset dg:buf1
call print_head ;print heading for file 1
mov dx,word ptr [bx].fst_nosinc
mov si,word ptr [bx].fst_sinc
call get_next ;get pointer to next line
mov cx,si
sub cx,dx ;get character count
call prout
mov bx,offset dg:buf2
call print_head ;print heading for file 1
mov dx,word ptr [bx].fst_nosinc
mov si,word ptr [bx].fst_sinc
call get_next ;get pointer to next line
mov cx,si
sub cx,dx ;get character count
call prout
mov dx,offset dg:diff_sep
mov cl,diff_sep_len
xor ch,ch
call prout ;print difference separator
ret
subttl Subroutines: print_head
page
;-----------------------------------------------------------------------;
; Print heading for difference ;
; ;
; entry: ;
; BX pointer to buffer structure ;
; ;
; modifies: ;
; AX,CX and DX ;
; ;
; Called from txt_compare and print_diff ;
;-----------------------------------------------------------------------;
print_head:
mov dx,offset dg:fname_sep
mov cl,fname_sep_len
xor ch,ch
call prout
mov dx,word ptr [bx].fname
mov cx,word ptr [bx].fname_len
call prout
mov dx,offset dg:CRLF
mov cx,2
call prout
ret
subttl Subroutines: print_all
page
;-----------------------------------------------------------------------;
; Print the rest of a file ;
; ;
; If in SINC it will print the file from the fst_nosinc line ;
; till the end of the file. If NOT in SINC then it will print from ;
; the current line of the buffer to the end of the file. ;
; ;
; entry: ;
; BX pointer to buffer structure ;
; ;
; modifies: ;
; AX,CX and DX ;
; ;
; Called from txt_compare ;
;-----------------------------------------------------------------------;
print_all:
cmp [sinc],true ;are we in SINC?
jne so1
mov dx,word ptr [bx].curr
jmp short so2
so1:
mov dx,word ptr [bx].fst_nosinc
so2:
mov cx,word ptr [bx].dat_end
inc cx
prt_again:
sub cx,dx ;ammount of data to write
call prout ;write it out
;----- Read more data to the buffer
push bx ;save pointer to buffer struct
mov dx,word ptr [bx].buf
call read_dat
jnc so3
jmp bad_read ;print error (BX in stack)
so3:
or ax,ax ;zero chars read?
jne so4
pop bx ;all done writting
ret
so4:
pop bx
mov cx,word ptr [bx].buf_end
jmp short prt_again ;print next buffer full
subttl Subroutines: prout and prt_err
page
;-----------------------------------------------------------------------;
; ;
;-----------------------------------------------------------------------;
prout:
push bx
mov bx,stdout
mov ah,write
int 21h
pop bx
ret
;-----------------------------------------------------------------------;
; ;
;-----------------------------------------------------------------------;
prt_err:
push bx
xor ch,ch
jcxz retpbx
mov bx,stderr
mov ah,write
int 21h
retpbx:
pop bx
ret
code ends
page
stack segment stack
dw 128 dup(?)
stack ends
end start