PAGE 60,132 TITLE CFcomp - CHKfile file compare utility ; SUBTTL General program description and use of common storage ;-----------------------------------------------------------------------------; ; CFCOMP - Compare output files created by CHKfile ; ;-----------------------------------------------------------------------------; ; CFCOMP 1.0 þ PCDATA TOOLKIT Copyright (c) 1990 Ziff Communications Co. ; ; PC Magazine þ Wolfgang Stiller ; ; ; ;-----------------------------------------------------------------------------; ; ; Purpose: ; ; CFCOMP does a high speed compare of redirected output (report) files ; ; captured from CHKfile. CFCOMP then displays which files have been ; ; changed, deleted or added between the creation of the OLD and NEW ; ; report files. ; ; ----------------------------------------------------------------------------; ;Format: ; ; ; ;CFCOMP OLDfile NEWfile [/C] [/O] [/P] ; ; ; ; OLDfile and NEWfile are CHKFILE.COM created report files. ; ; "/C" Changes only - reports and checks only for changed files ; ; and ignores additions or deletions from file list. ; ; "/O" Only use check fields and file size for comparison. DOS time ; ; and date stamps are not used in comparing. ; ; "/P" Pauses after each page of output if non-matches are detected.; ; ----------------------------------------------------------------------------; ;Remarks: ; ; CFCOMP utilizes a high speed compare algorithm tailored specifically ; ; to comparing data produced by CHKfile. It will work even if the ; ; directories have been sorted in a different order. The order of the ; ; files in the NEW and OLD files is completely independent. If the order ; ; is the same, (which is usually the case) CFCOMP runs the fastest. If ; ; both files were created using the /T option on CHKfile, then CFCOMP ; ; will report whether the totals match. If the totals match, yet CFCOMP ; ; reports additions and deletions, this usually indicates that files ; ; were effectively renamed. CFCOMP writes to DOS standard output, so ; ; that its output may be redirected to a file. "CFCOMP A B >OUT" will ; ; compare old file A and new file B with any differences reported on ; ; file OUT. ; ; ; ; If the OLDfile is not found, CFCOMP will report this fact and then ; ; continue executing, treating OLDfile as if it were an empty file. ; ; ; ; The individual report (input) files may not be larger than 65,536 ; ; bytes. This size allows up to 1260 file entries in each report file. ; ; To compare this many entries in each file, CFCOMP requires that at ; ; least 133,000 bytes of memory are free. CFCOMP will automatically ; ; adjust its memory allocation to use whatever memory is available. For ; ; each 1000 less bytes of free memory, 10 fewer file entries can be ; ; supported. ; ; ; ; All serious error conditions (such as a corrupted or invalid report ; ; file) will result in an error message accompanied by a beep, a prompt ; ; and a wait for a key press. ; ; ; ; CFCOMP will return the following DOS ERRORLEVELs (decimal): ; ; 128 - indicates control card syntax error ; ; 20 - indicates file open or other I/O error on one of the files ; ; 16 - indicates file(s) of invalid type for CFCOMP or file too large ; ; (64K or larger is too large - this is more than 1260 lines) ; ; 12 - indicates file(s) have been changed ; ; 8 - indicates file(s) added and/or deleted but no files changed ; ; 4 - indicates file(s) have probably been renamed rather than actually ; ; changed or deleted, since the "TOTAL==>" lines match but the files ; ; themselves did not. ; ; 0 - files match exactly ; ; -------------------------------------------------------------------------- ; ;Sample output: ; ; ; ;CFCOMP 1.0 [(c) 1989 W. Stiller] comparing in directory: \ASM\EXAMPLES ; ; File Name + Check Check File Update Update ; ; Extension: Val1: Val2: Size: Date: Time: ; ; ---------- ---- ---- ----- ------ ------ ; ;Chgd: OLD:SDOC.BAK BC37 2709 61A6 02/09/89 00:07:54 ; ; NEW:SDOC.BAK A6B4 A64F 6769 02/11/89 23:55:02 ; ;Chgd: OLD:SDOC BC37 2709 61A6 02/09/89 00:13:48 ; ; NEW:SDOC BC37 2709 61A6 02/12/89 00:07:54 ; ;Deleted-->OUT 1E4A 4FD5 65 02/12/89 00:10:30 ; ;NEW Fil-->NEWOUT.TXT 1E4A 4FD5 65 02/12/89 00:10:30 ; ;NEW Fil-->TESTDIR Dir. 02/11/89 00:07:48 ; ;File totals unequal ; ; ; ;Notes: CFCOMP displays which directory it is in, when doing the compare. ; ; In this case, it is in directory \ASM\EXAMPLES. Files SDOC and ; ; SDOC.BAK have both changed. Even though file OUT has apparently ; ; been deleted and file NEWOUT.TXT has been added, it is obvious ; ; that file OUT has merely been renamed since the check values and ; ; file sizes match. TESTDIR is the name of a directory which has ; ; been added. ; ; ----------------------------------------------------------------------------; ;---------------------------------------------------------------; ; Constants: ; ;---------------------------------------------------------------; BOX EQU 254 ;Small box character code CR EQU 0Dh LF EQU 0Ah CRLF EQU 0A0Dh ;Carriage return line feed. DSP_Key_len EQU 12 ;Length of the key part of DSP record DSP_Rec_len EQU 51 ;Length of the DSP (Display) record CSEG SEGMENT ASSUME CS:CSEG, DS:CSEG, ES:Nothing, SS:CSEG SUBTTL Main program ;******************************************************************************; ;** Main program begins here -CFCOMP- **; ;******************************************************************************; ORG 100H ; THIS IS A COM TYPE PROGRAM CFCOMP: CALL Parse_parms_Open_Files ;Parse cmdline paramters + open files MOV BX,offset Buffer_Area+200 ;locate stack down in prog storage MOV SP,BX ;Stack of 200 bytes CALL Allocate_Memory ;Allocate memory for file buffers ;and release other unused memory. ;ES will point to start of alloc mem CALL Read_and_Validate_Files ;Validate that both files are ;readable and of right type to read ;DS is now addressing DATA SEG (DS=CS) XOR BX,BX ;Zero highest error level variable(BL) MOV BH,Page_mode ;Load line counter + pagemode flag ; =FFh for no paging CALL Compare_Files ;Compare OLD file with NEW file and ; determine changed or deleted recs CALL Scan_for_Additions ;Look for records left on NEW file CALL Compare_totals ;Check if total===> lines match CMP BH,0FFh ;See if page mode is off (= FFh) JE Normal_termination ; IF turned off, skip page control OR BL,BL ;See if changes detected (BL not = 0) JE Normal_termination ; If no changes, then terminate CALL Page_Wait ; otherwise pause for user to read ; the display. Normal_termination: MOV AL,BL ;Load error level for termination MOV AH,4Ch ; terminate INT 21h ;---------------------------------------------------------------------------; ; Compare files - will compare OLD and NEW file (51 char DISPLAY records) ; ;---------------------------------------------------------------------------; ; Register conventions: ; ; DS=Segment register for OLD SEG ES=NEW report file segment ; ; SI=offset of current OLD rec DI=offset of current NEW rec ; ; CX=number of OLD recs left to proc. BP=offset of last NEW rec ; ; BL=highest code for DOS errorlevel BH=line counter (for /P option) ; ;---------------------------------------------------------------------------; ; Entry: ; ; Both files have been read into buffers and validated ; ; DS is segment register for DATA(=CS); ES is segment regst for NEW file. ; ; BP is offset 1st rec on NEW file ; ; Exit: ; ; All matched NEW file entries have 1st byte of filename zeroed. ; ; Reports have been written to Std output, on discrepancies so far. ; ; BL = 12 if changes, =8 if added/deleted files, and =0 for no changes ; ;---------------------------------------------------------------------------; Compare_early_exit: RET Compare_Files: ;<==== Enter here MOV DI,BP ;1st record address of NEW file MOV SI,OLD_File_Start ;1st record address of OLD file MOV CX,OLD_Rec_count ;number of records on OLD file JCXZ Compare_early_exit ;If no files in OLD directory: EXIT MOV BP,NEW_End_Of_File ;Offset after last rec on NEW file MOV AX,ES ;NEW file SEGment SUB AX,File_Size_PARAs ;Back down from NEW file to OLD file MOV DS,AX ;DS = SEG reg for OLD file SUB DI,DSP_Rec_Len ;Point to -1 record on NEW file SUB SI,DSP_Rec_Len ;Point to record -1 on OLD file Compare_Next: ;Main compare loop - Get next NEW+OLD ADD DI,DSP_Rec_Len ;Advance to next NEW record Check_Next_OLD_REC: ;Secondary compare loop: next OLD rec ADD SI,DSP_rec_len ;Advance to next OLD rec CMP DI,BP ;Compare offset of this NEW rec + EOF JB Compare_Keys ; If we are not past EOF on this rec CALL Display_Deleted_MSG ; Else remaining old recs are deletd LOOP Check_Next_OLD_REC ;Continue reporting deleted old recs RET ;All done with main file compare Compare_Keys: ; Compare File keys (1st 12 characters of each record) MOV DX,CX ;Save a copy of CX (# of old recs) MOV CX,DSP_Key_Len/2 ;Compare 12 character key (6 words) PUSH DI ;Save current NEW rec loc PUSH SI ;Save current OLD rec loc REPE CMPSW ;Do compare JNE Find_Matching_NEW_Rec ;if not =, Scan NEW file for a match ; Now compare the actual records (keys already match): ; ***Warning*** the length field of following instr may be patched by /O PATCH1: MOV CX,18 ;Check remainder of record (18 words) INC DI ;Skip checking next blank separator INC SI ;Skip checking next blank separator REPE CMPSW ;Do compare POP SI ;Point back to start of this OLD rec POP DI ;Point back to start of this NEW rec JE Good_compare ;If the records match CALL Display_changed_MSG ;Report detection of changed record Good_compare: ; Now zero out the 1st byte of the matched NEW file record: XOR AX,AX ;Zeros to store on key of record STOSB ;Zero 1st byte DEC DI ;Point back to beginning of NEW rec MOV CX,DX ;Restore OLD file recs remaining count LOOP Compare_Next ;Go check next record on OLD file RET ;All done with COMPARE_FILE ;-------------------------------------------; ;Search NEW file records for a matching key ; ;-------------------------------------------; Find_Matching_NEW_Rec: ;Search NEW file to match OLD file rec POP SI ;Restore pointer to current record POP DI ;Restore pointer to current record SUB SP,2 ;Leave current NEW rec pointer on stak Continue_NEW_file_Search: ADD DI,DSP_Rec_Len ;Next record on NEW file ; Are we at end of file on NEW file ? CMP DI,BP ;Compare current NEW rec with EOF JB Compare_Keys_NEW_Rec ;IF not EOF, go compare this key ; We are at END Of File (EOF) on NEW file on this search, so report deleted rec CALL Display_Deleted_MSG ;Report that rec from OLD file deleted POP DI ;Point to last CURRENT rec on NEW file MOV CX,DX ;Restore OLD file recs remaining count LOOP Check_Next_OLD_rec ;Go check next record on OLD file RET ;All done with COMPARE_FILE Compare_Keys_NEW_Rec: ; (1st 12 characters of each record) MOV CX,DSP_Key_Len/2 ;Compare 12 character key (6 words) PUSH DI ;Save current NEW rec loc PUSH SI ;Save current OLD rec loc REPE CMPSW ;Do compare JE Match_rest_of_NEW_Rec ;If keys =, check the records POP SI ;Point back to start of this OLD rec POP DI ;Point back to start of this NEW rec JMP SHORT Continue_NEW_File_Search ;Keek searching through NEW rec Match_rest_of_NEW_Rec: ;NEW file scan has found matching keys ; Now compare the actual records (keys already match): ; ***Warning*** the length field of following instr may be patched by /O PATCH2: MOV CX,18 ;Check remainder of record (18 words) INC DI ;Skip checking next blank separator INC SI ;Skip checking next blank separator REPE CMPSW ;Do compare POP SI ;Point back to start of this OLD rec POP DI ;Point back to start of this NEW rec JE Found_matching_NEW_rec ;If the records match CALL Display_changed_MSG ;Report detection of changed record Found_matching_NEW_rec: ; Now zero out 1st byte of the matched NEW file record: XOR AX,AX ;Zeros to store on key of record STOSB ;Store zero byte at start of record POP DI ;point to last current rec on NEW file MOV CX,DX ;Restore OLD file recs remaining count LOOP Check_Next_OLD_rec ;Go check next record on OLD file RET ;All done with COMPARE_FILE ;---------------------------------------------------------------------------; ; Scan for Additions: ; ; Scan through the NEW file and look for records which have not had the ; ; 1st byte zeroed. These recs represent added files. We will put out a ; ; message recording this fact. ; ; ENTRY: ES is segment register for NEW file ; ;---------------------------------------------------------------------------; Scan_for_Additions: MOV AX,CS ;Used to access normal DATA segment MOV DS,AX ;DS=CS (normal DATA segment) CMP Changes_only,'Y' ;Does user want only changes? JNE Scan_continue ; If not execute rest of procedure Scan_early_Exit: RET ; Otherwise, pack up and go home Scan_continue: MOV CX,NEW_Rec_Count ;Get # of 51 char recs on NEW file JCXZ Scan_Early_Exit ;If no NEW records to scan MOV DI,NEW_File_Start ;1st record on NEW file SUB DI,DSP_Rec_Len ;Point to record # -1 MOV AX,ES ;Use both segment regs for NEW file MOV DS,AX Check_for_Next_Addition: ADD DI,DSP_Rec_Len ;Look at next NEW file record CMP BYTE PTR [DI],0 ;Is this a zeroed record? JE Continue_Zero_Check ; If zeroed, keep checking CALL Display_Added_MSG ; Else record file addition Continue_Zero_Check: LOOP Check_For_Next_Addition RET ;---------------------------------------------------------------------------; ; COMPARE TOTALS - If both OLD and NEW files have total lines check that ; ; they match. If they match, yet files were changed then put out a message; ; to that effect and return ERRORLEVEL 4 at termination (BL reg). ; ; ENTRY: ES is segment register for NEW file ; ; EXIT: DS is back to original Data Segment (=CS) ; ; BL = 4 if records have changed but both totals match. ; ;---------------------------------------------------------------------------; Compare_totals: ;Check if total===> lines match MOV AX,CS ;Set DS back to datasegment MOV DS,AX MOV SI,OLD_Tot_Loc ;Point to location of totals OR SI,SI ;Check if totals line non-exist (SI=0) JNE Continue_totals_1 ; If it exists, chk other totals line RET ; ELSE pack up and go home Continue_totals_1: MOV DI,NEW_Tot_Loc ;Point to location of totals OR DI,DI ;Check if totals line non-exist (DI=0) JNE Continue_totals_2 ; If it exists, compare the totals RET ; ELSE pack up and go home Continue_totals_2: CMP BH,0FFh ;See if page mode is off (= FFh) JE Continue_totals_3 ; IF turned off, skip page control MOV AL,1 ; Else Set lines to be output to 2 CALL Page_Control ; + check if its time to pause Continue_totals_3: MOV AX,ES ;NEW file segment SUB AX,File_Size_PARAs ;Backup 64K MOV DS,AX ;DS is OLD file segment register again MOV CX,9 ;Compare 9 characters REPE CMPSB ;Compare MOV AX,CS ;Set DS back to datasegment MOV DS,AX MOV BP,BX ;Save highest errorlevel + line ct MOV BX,1 ;Handle for std output device JE Totals_Match MOV DX, offset Tot_nomatch_MSG MOV CX,21 ;Length of msg is 21 chars MOV AH,40h ;DOS Write func INT 21h ;Tell user that totals don't match MOV BX,BP ;Restore saved BX (ERRLVL + LINEct) RET Totals_match: MOV DX, offset Tot_match_MSG MOV CX,19 ;Length of msg MOV AH,40h ;DOS Write func INT 21h ;Tell user file totals are equal MOV BX,BP ;Restore saved BX (ERRLVL + LINEct) OR BL,BL ;Check if any file chgs,dels or adds ; (IE, BL not = 0) JE Totals_Return ; If no changes then we are done... MOV BL,4 ; ELSE, indicate probable rename Totals_Return: RET ;----------------------------------------------------------------------------; ; Display CHANGED Message: ; ; ; ;ENTRY: DS:SI points to OLD version of report record to format + display. ; ; ES:DI points to NEW version of report record to format + display. ; ;EXIT: ; ; Displays a message to standard output device informing user that a change; ; has occured between the OLD and NEW files. IF /P was selected then ; ; we check line count and pause every 24 lines. ; ;----------------------------------------------------------------------------; Display_changed_MSG: ;Display message announcing changed record PUSH BP PUSH CX PUSH DX PUSH DS ;Save (OLD) file segment MOV AX,CS ;Used to access normal DATA segment MOV DS,AX ;DS=CS (normal DATA segment) OR BL,BL ;Check if highest error level (BL) = 0 JNZ Check_CHG_Page_mode ; If not 0, then header msg alrdy out CALL Display_Header_MSG ; Else Display the header message Check_CHG_Page_mode: CMP BH,0FFh ;See if page mode is off (= FFh) JE Display_changed_cont ; IF turned off, skip page control MOV AL,2 ; Else Set lines to be output to 2 CALL Page_Control ; + check if its time to pause Display_changed_cont: MOV BP,BX ;Save BX in BP MOV DX, offset CHGD_MSG_1 ;First part of changed messaged MOV CX,10 ;Length of msg is 10 chars MOV AH,40h ;DOS Write func MOV BX,1 ;BX=1 = Handle for std output device INT 21h ;Write beginning of OLD file chngd msg POP DS ;Use file segment again (OLD file) SUB SP,2 ;Leave DS on stack MOV DX,SI ;Point to OLD record MOV CX,DSP_Rec_Len ;Length of display record MOV AH,40h ;DOS Write func INT 21h ;Write actual OLD record out MOV AX,CS ;Used to access normal DATA segment MOV DS,AX ;Back to normal data segment again MOV DX, offset CHGD_MSG_2 ;First part of changed messaged MOV CX,10 ;Length of msg is 10 chars MOV AH,40h INT 21h ;Write beginning of OLD file chngd msg MOV AX,ES ;Use file segment again (NEW file) MOV DS,AX MOV DX,DI ;Point to NEW record MOV CX,DSP_Rec_Len ;Length of display record MOV AH,40h INT 21h ;Write actual OLD record out MOV BX,BP ;Restore Saved version of BX POP DS ;Restore addressability to OLD file POP DX POP CX POP BP MOV BL,12 ;Indicate a record has changed RET ;---------------------------------------------------------------------------; ; Display DELETED Message ; ; ; ; Entry: DS:SI must point to 51 chr DSP_REC (display record) to be displayed; ; ; ; Displays a message to standard output device informing user that a file ; ; has been deleted from the OLD file list. IF /P was selected then ; ; we check line count and pause every 24 lines. ; ;---------------------------------------------------------------------------; Display_Deleted_MSG: ;Display message announcing deleted records PUSH BP PUSH CX ;Save used registers PUSH DX PUSH DS ;Save (OLD) file segment MOV AX,CS ;Used to access normal DATA segment MOV DS,AX ;DS=CS (normal DATA segment) CMP Changes_only,'Y' ;Does user want only changes? JNE Display_Deleted_continue ; If not continue POP DS ; ELSE restore DS and return JMP Display_Deleted_Exit2 ; to caller Display_Deleted_continue: OR BL,BL ;Check if highest error level (BL) = 0 JNZ Check_DEL_Page_mode ; If not 0, then header msg alrdy out CALL Display_Header_MSG ; Else Display the header message Check_DEL_Page_mode: CMP BH,0FFh ;See if page mode is off (= FFh) JE Display_deleted_continue2 ; IF turned off, skip page control MOV AL,1 ; Else Set lines to be output to 2 CALL Page_Control ; + check if its time to pause Display_deleted_continue2: MOV BP,BX ;Save BX MOV DX, offset DELETED_MSG ;First part of deleted message MOV CX,10 ;Length of msg is 10 chars MOV AH,40h ;DOS Write func MOV BX,1 ;Handle for std output device INT 21h ;Write beginning of OLD file deltd msg POP DS ;Use file segment again (OLD file) MOV DX,SI ;Point to OLD record MOV CX,DSP_Rec_Len ;Length of each display record MOV AH,40h ;DOS Write func INT 21h ;Write actual OLD record out Display_Deleted_Exit: MOV BX,BP ;Restore saved version of BX CMP BL,8 ;Check if Delete or chg already hapnd JAE Display_Deleted_Exit2 ; Do not change if already set MOV BL,8 ;Flag that Deleted record detected Display_Deleted_Exit2: POP DX POP CX POP BP RET ;---------------------------------------------------------------------------; ;Display ADDED Message -Indicate that a file has been added since OLD reprt; ; ; ;ENTRY: ES:DI points to NEW version of report record to format + display. ; ;EXIT: ; ; Prints a message to standard output device informing user that a file ; ; has been added to the NEW file list. IF /P was selected then ; ; we check line count and pause every 24 lines. ; ;---------------------------------------------------------------------------; Display_Added_MSG: ;Display message announcing additional files PUSH BP PUSH CX ;Save used registers MOV AX,CS ;Used to access normal DATA segment MOV DS,AX ;DS=CS (normal DATA segment) OR BL,BL ;Check if highest error level (BL) = 0 JNZ Check_ADD_Page_mode ; If not 0, then header msg alrdy out CALL Display_Header_MSG ; Else Display the header message Check_ADD_Page_mode: CMP BH,0FFh ;See if page mode is off (= FFh) JE Display_Added_continue ; IF turned off, skip page control MOV AL,1 ; Else Set lines to be output to 2 CALL Page_Control ; + check if its time to pause Display_Added_continue: MOV BP,BX ;Save BX MOV DX, offset Added_MSG ;First part of deleted message MOV CX,10 ;Length of msg is 10 chars MOV AH,40h ;DOS Write func MOV BX,1 ;Handle for std output device INT 21h ;Write beginning of OLD file added msg MOV AX,ES ;Use file segment again (NEW file) MOV DS,AX ;DS=ES (both are seg reg for NEW file) MOV DX,DI ;Point to rec on NEW file (Added rec) MOV CX,DSP_Rec_Len ;Length of display record MOV AH,40h ;DOS Write func INT 21h ;Write actual OLD record out MOV BX,BP ;Restore saved version of BX POP CX POP BP CMP BL,8 ;Chk if Del, add or chg already hapnd JAE AD_or_CHG_happened ; Do not change if already set MOV BL,8 ;Flag that Deleted record detected AD_or_CHG_happened: RET ;---------------------------------------------------------------------------; ; Allocate Memory ; ; ; ; ENTRY: BX contains starting offset of buffer area. ; ; (Note, this area contains initialization code and data which will ; ; be overlaid once we start reading into the buffers.) ; ; ; ; Release memory used by initialization routine and allocate 2000h pages ; ; (128k) memory for use as file buffers. If this fails get as much as ; ; possible. Divide memory allocated into 2 buffers - 1 for each file. ; ; Leaves ES as segment pointer to first file segment (OLD file) ; ;---------------------------------------------------------------------------; Allocate_Memory: ; Now determine how many paragraphs (16 bytes) program plus stack needs: ADD BX,15 ;Round up to nearest PARA MOV CL,4 SHR BX,CL ;Divide bytes of storage by 16 MOV AH,4Ah ;Dealloc all but needed (BX) paras INT 21h MOV BX,2000h ;Request 128K for input buffers MOV AH,48h ;DOS request mem function INT 21h JNC Mem_OK ;If memory is available MOV DX,offset Mem_loss_Msg ;Display msg informing lack of mem MOV AH,09H ;DOS display string function INT 21H ; Attempt to allocate what little memory is available and use that MOV AX,BX ;Paragraphs of memory free SHR AX,1 ;Divide by two: Space for each file MOV File_Size_PARAs,AX ;Each file's size in pargraphs MOV CL,4 ;Prepare to shift left 4 bits (*16) SHL AX,CL ;Mult by 16 = number of bytes per file MOV File_Size_Bytes,AX ;Space for each file in bytes MOV AH,48h ;DOS alloc mem func:BX = para avail INT 21h ;Get what memory we can get JNC Mem_OK ;If Alloc worked 2nd time around ; Fatal memory error: MOV DX,offset Mem_ERR_Msg ;Else: give message and give up MOV AH,09H ;DOS display string function INT 21H CALL Page_Wait ;Beep and force user to hit a key MOV AX,4C14h ; terminate with 20 error level INT 21h Mem_OK: MOV ES,AX ;ES points to start of allcoated block RET ;---------------------------------------------------------------------------; ; Read and Validate Files ; ; 1) Read both files into file buffers. ; ; 2) Validate that files are of correct type ; ; 3) Determine offset of first record and total number of records ; ; check if a total line exists on each file ; ; 4) Calc offset of beginning of last record for the NEW file ; ; ; ; On Entry ES must point to OLD_FILE segment ; ; ; ; On Exit ES will point to NEW_FILE segment and DS will DATA SEG (=CS) ; ;---------------------------------------------------------------------------; Read_and_Validate_Files: ; Read "OLD" file (first of two files specified) MOV BX,OLD_File_Handle ;Get handle for the first file PUSH OLD_Filename_end ;Save end of Filename on stack PUSH OLD_Filename_Loc ;Save start of filename for error msgs MOV AX,ES ;ES points to beginning of file SEG MOV DS,AX ;DS=ES = seg reg for OLD file CALL Read_File ;Do actual read of file + error chking CALL Validate_File ;Check file and locate 1st + last recs ;DS=CS after return from Validate_File ADD SP,4 ;Remove filename end + loc from stack MOV OLD_File_Start,BP ;Save 1st record address MOV OLD_Rec_count,CX ;Save number of records MOV OLD_Tot_Loc,BX ;Save location of totals (if existing) ; Read "NEW" file (2nd of two files specified) MOV BX,NEW_File_Handle ;Get handle for the first file PUSH NEW_Filename_end ;Save end of Filename on stack PUSH NEW_Filename_Loc ;Save start of filename for error msgs MOV AX,ES ;DS points to beginning of file SEG ADD AX,File_Size_PARAs ;point DS to NEW file segment MOV DS,AX ;DS is segment register for NEW file CALL Read_File ;Do actual read of file + error chking MOV AX,DS ;Validate_file needs MOV ES,AX ; ES for file seg CALL Validate_File ;Check file and locate 1st + last recs ;DS = CS after return from Validate_Fi ADD SP,4 ;Remove filename end + loc from stack MOV NEW_File_Start,BP ;Save 1st record address MOV NEW_Rec_count,CX ;Save number of records MOV NEW_Tot_Loc,BX ;Save location of totals (if existing) MOV New_End_Of_File,DI ;Save start of last rec on NEW file RET ;---------------------------------------------------------------------------; ; READ FILE - will read the file specified by the following parameters ; ; BX contains file handle, Stack contains end and start of filespec ; ; DS contains segment to read file into (file buffer is at offset zero) ; ; On EXIT: File will be read into file buffer pointed to by DS and closed. ; ; CX will contain number of characters read in from the file. ; ;---------------------------------------------------------------------------; Read_file: CMP CS:Missing_Old_File,'Y' ;Are we attempting to read from a ; Non existant OLD file? JNE Normal_File_Read ; If not, do normal file read XOR CX,CX ; Else, indicate file is empty MOV CS:Missing_Old_File,0 ; Turn off missing file switch RET ; All done for missing file Normal_File_Read: XOR DX,DX ;DX=0 = start of file buffer MOV SI,DX ;SI is for BUFFER reads later MOV CX,CS:File_Size_Bytes ;MAX # of bytes to read (64k-1) MOV AH,3FH ;Setup to read from file INT 21H ;Call DOS to do actual read JC Read_error ;Quit on any error or EOF OR AX,AX ;Check if ax=0 no records read JZ File_size_error ;If no records, close this file.. CMP AX,CX ;See if max number of chars read JE File_size_error ;If we have compltly filled buffer MOV CX,AX ;Save total # of chars read MOV AH,3Eh ;Prepare to close the file INT 21H ;Let DOS close file RET ;---------------------------------------------------------------------------; ; READ ERROR - report read error message - call with: ; ; Stack contains the END and (LOC) start of the filespec for error msgs ; ;---------------------------------------------------------------------------; Read_error: ;Report error reading a file MOV AX,CS MOV DS,AX ;Restore datasegment addresability MOV DX, offset Read_Err_MSG ;indicate read failed MOV CX,15 ;Length of msg is 15 chars MOV SI,4C14h ;DOS term with error level 20 JMP Report_file_errors SUBTTL General Purpose subroutines ;---------------------------------------------------------------------------; ; FILE SIZE ERROR - Report on an error - with file have too many records ; ; Stack contains the END and (LOC) start of the filespec for error msgs ; ;---------------------------------------------------------------------------; File_size_error: ;File has more than 64k records MOV AX,CS MOV DS,AX ;Restore datasegment addresability MOV DX, offset size_Err_MSG ;indicate record has an invalid size MOV CX,20 ;Length of msg is 20 chars MOV SI,4C10h ;DOS term with error level 16 (dec) JMP Report_file_errors ;---------------------------------------------------------------------------; ; FILE TYPE ERROR - Report on an error - with file being invalid type ; ; Stack contains the END and (LOC) start of the filespec for error msgs ; ;---------------------------------------------------------------------------; File_type_error: ;Report this is wrong type of file ADD SP,2 ;Remove CX from the stack; this way: ; filespec_end + loc are stack top MOV AX,CS MOV DS,AX ;Restore datasegment addresability MOV DX, offset type_Err_MSG ;indicate invalid record type MOV CX,20 ;Length of msg is 20 chars MOV SI,4C10h ;DOS term with error level 16 (dec) JMP Report_file_errors ;---------------------------------------------------------------------------; ; VALIDATE FILE - will examine file and locate 1st and last records in file ; ; On Entry: ; ; Stack contains the END and (LOC) start of the filespec for error msgs ; ; ES contains File buffer SEGMENT ; ; CX contains number of characters read in the buffer ; ; On EXIT: (if file is of valid type:) ; ; BP will be beginning of 1st record on file (offset) ; ; CX will be count of number of records on file ; ; BX will be = offset of CHKSUM field on final total=====> record ; ; DI will be offset of last char in last 51 char record on the file (EOF) ; ; DS will be code/data segment register rather than File segment ; ;---------------------------------------------------------------------------; Validate_File: MOV AX,CS ;XFER OLD data SEG(=CS) MOV DS,AX ; back into DS CMP CX,0 ;Is this a pretend file (empty) ? JNE Its_a_Real_File ;If not, do actual validation XOR DI,DI ;Set EOF offset to empty XOR BX,BX ;Indicate for totals record RET ;All done Its_a_Real_File: PUSH CX ;Save copy of offset of EOF XOR DI,DI ;Start at beginning of buffer MOV AL,254 ;Search for teltale of CHKfile (box) REPNE SCASB ;Search until match JNZ File_type_error ;If no match found MOV AL,'W' ;Search for teltale of CHKfile REPNE SCASB ;Search until match MOV BX,CX ;Save copy of CX MOV CX,4 ;Compare 8 characters MOV SI,offset Check_String REPE CMPSW ;File should match exactly JNZ File_type_error ;If mismatch found MOV CX,BX ;Restore CX to SUB CX,8 ; reflect chars left MOV AL,'-' ;Search for a REPNE SCASB ; minus sign MOV AL,LF ;Search for a REPNE SCASB ; linefeed character JNZ File_type_error ;If no match found MOV BP,DI ;Save this location (1st record) ; BP now points to the first 51 char display record on this file ; Now check for existance of TOTALS==> record and locate beginning of last rec XOR DX,DX ;Zero upper part of dividend (DX:AX) MOV AX,CX ;# of chars after start of 1st record MOV BX,DSP_Rec_Len ;Prepare to divide by record len (51) DIV BX ;Divide chars from 1st rec by rec len ; After divide: # of 51 char recs is in AX; # of chars in last rec in DX MOV CX,AX ;# of 51 character records on file XOR BX,BX ; Initially indicate no totals line OR DX,DX ;Check if, no total line (no last rec) JZ No_totals_line ;Skip totals line checking CMP DX,24 ;Totals line should be 24 characters JE Tot_line_exists ; If length is correct for tot line JMP File_type_error ; If length of final line is wrong Tot_line_exists: POP DI ;Get address of EOF (PUSHed CX) SUB DI,10 ;first char in CHKSUM field of TOT rec MOV BX,DI ; Return this location to caller SUB DI,13 ;Point DI past end of last 51 chr rec RET No_totals_line: POP DI ;Get address of EOF (PUSHed CX) ;DI= offset past last 51 char record RET ;End of VALIDATE_FILE routine PAGE ;******************************************************************************; ;** General purpose subroutines follow **; ;******************************************************************************; ;---------------------------------------------------------------------------; ; Report File errors - General purpose error presenter used by ; ; the specific error subroutines such as file_size_error + read_error. ; ; Stack contains the END and (LOC) start of the filespec which had error. ; ; DX = offset to error msg, CX has length of message ; ; SI = contains DOS terminate code with specific ERRORLEVEL in lower part ; ;---------------------------------------------------------------------------; Report_File_errors: ;General purpose error display routine ;DOS term func + errlvl must be in SI MOV AX,CS ;Point ES back MOV ES,AX ; to normal data segment MOV AH,40h ;DOS Write func MOV BX,1 ;Handle for std output device INT 21h ;Write beginning of error message ADD SP,2 ;Remove return address from stack POP BP ;Get starting offset of filespec POP DI ;1 character after end of filespec MOV AX,CRLF ;Terminate file name with CR,LF STOSW SUB DI,BP ;Calc filename length + 2 for CRLF MOV CX,DI MOV DX,BP ;Start of file name to output MOV AH,40h ;DOS Write func INT 21H CALL Page_Wait ;Beep and force user to hit a key MOV AX,SI ;SI contains 4Ch with error lvl INT 21h ;---------------------------------------------------------------------------; ; Page_control: (and *PAGE WAIT* alternate entry point) ; ; Called only if user specified /P option (BH will be not = to FFh). ; ; Page control will increment the line counter and pause every 24 lines ; ; giving the user a prompt to -Hit any KEY - ; ;ENTRY: ; ; AL contains number of lines waiting to be displayed. ; ; BH contains line count for this page already. ; ; DS points to normal data segment (=CS) ; ;EXIT: ; ; BH contains updated line count which is reset if page wait happened ; ; ; ;PAGE WAIT - simply puts out -HIT ANY KEY- message and waits for user ; ; to hit any key. ; ;---------------------------------------------------------------------------; Page_control: ADD BH,AL ;Increment line counter CMP BH,24 ;Are we over one page of output? JA Page_Wait ; If over 1 page then do page wait RET Page_Wait: ;Alternate entry point (here) MOV BH,AL ;Reset the line counter PUSH BP ;Save all corrupted registers PUSH BX PUSH CX PUSH DI PUSH DX PUSH SI ; Produce a beep to alert the user: (use BIOS TTY func to write an ASCII BELL) MOV AX,0E07H ;BIOS func (0Eh) to write (07H) beep XOR BH,BH ;Select page zero for output INT 10H ;BIOS video function (0Eh=write char) ;Find out what attribute is being used for display MOV AH,08h ;read attrib + char function INT 10h ;Call BIOS PUSH AX ;Save AH=attribute byte ;Find out what line the cursor is on MOV AH,03h ;Read cursor position function INT 10h ;BIOS video services PUSH DX ;DH contains row (line #) Save it! ; Position cursor to current line + column 28: (TO BIOS row 27) MOV AH,02 ;BIOS int 10h set cursor position func XOR BH,BH ;Set page to zero ;DH contains current row MOV DL,1Bh ;Set cusor current row and col 27 INT 10h ;BIOS video services ; Put -Hit any key- message out with inverse video attribute type on ; XOR BH,BH ;Set page to zero (BH is still 0) MOV BL,0F0h ;Inverse video attribute MOV CX,1 ;Character count MOV SI,offset Hit_Key_Msg ;The hit-any-key message Display_next_video_char: MOV AH,09H ;BIOS int 10h write attrib + char func LODSB ;Get next character for output PUSH SI ;Save SI (int 10h may corrupt it) INT 10h ;Put character and attribute out INC DX ;Advance cursor position MOV AH,02 ;Adv cursor function INT 10h ; advance the cursor (BIOS) POP SI ;Restore saved SI CMP SI,offset Hit_key_Msg_end ;are we at end of message? JB Display_next_video_char ; If not get next char for display ; Else, wait for key press by user ; Wait for user to hit any key XOR AX,AX INT 16h ;Wait for user to hit a key ; Erase HIT ANY KEY message POP DX ;DH=current line number POP BX ;BH=user's screen attribute MOV AH,06h ;INIT window function XOR AL,AL ;Zero AL to clear window MOV CH,DH ;Current row (y coor upr lft) MOV CL,00 ;Start in first char position MOV DL,79 ;Last char pos - blank entire line INT 10h ;Blank out line ; Position cursor to start of blanked line MOV AH,02 ;BIOS int 10h set cursor position func XOR DL,DL ;DH=cur line, DL=0: first char pos XOR BX,BX ;Use video page zero INT 10h ;BIOS video services POP SI ;Restore all corrupted registers POP DX POP DI POP CX POP BX POP BP RET ;Return to caller ;---------------------------------------------------------------------------; ; Display HEADER MSG - displays column headers the first time CFCOMP decides; ; it needs to display a changed record. ; ;ENTRY: ; ; DS points to normal data segment (=CS) ; ;EXIT: ; ; DX,CX,AX are corrupted. ; ;---------------------------------------------------------------------------; Display_Header_MSG: PUSH BX ;Save BX (error level in BL) MOV BX,1 ;Write to STD output device (=1) MOV DX, offset Header_MSG ;beginning loc of directory string MOV CX,179 ;179 chars in header message MOV AH,40h ;DOS write INT 21h POP BX RET SUBTTL Definition of Data structures PAGE ;******************************************************************************; ;** Definition of Data areas follow **; ;******************************************************************************; File_Size_PARAs DW 1000h ;Size of each REP file in paragraphs File_Size_Bytes DW 0FFFFh ;Size of each REP file in bytes OLD_Filename_Loc DW 0 ;offset of filespec for OLD file OLD_Filename_end DW 0 ;end of filespec for OLD file OLD_File_Handle DW 0 ;File handle for OLD file OLD_File_Start DW 0 ;Offset of 1st record on file OLD_Rec_count DW 0 ;Number of 51 character recs on file OLD_Tot_Loc DW 0 ;Offset of CHKSUM on totals records NEW_Filename_Loc DW 0 ;offset of filespec for NEW file NEW_Filename_end DW 0 ;end of filespec for NEW file NEW_File_Handle DW 0 ;File handle for NEW file NEW_File_Start DW 0 ;Offset of 1st record on file NEW_Rec_count DW 0 ;Number of 51 character recs on file NEW_Tot_Loc DW 0 ;Offset of CHKSUM on totals records New_End_Of_File DW 0 ;Offset beginning of last record Changes_Only DB 0 ;="Y" if user wants only changes Missing_Old_File DB 0 ;="Y" if OLD report file not found Page_Mode DB 0FFh ;=00h means stop after each page Check_String DB 'olfgang ' Mem_Err_Msg DB 'Error in MEM ALLOC' CRLF_Msg DB CR,LF,'$' Mem_loss_Msg DB 'Memory lack limits file size' DB CR,LF,'$' Read_Err_MSG DB 'Error reading: ' Type_Err_MSG DB 'Wrong type of file: ' Size_Err_MSG DB 'File size invalid: ' CHGD_MSG_1 DB 'Chgd: OLD:' CHGD_MSG_2 DB ' NEW:' Deleted_MSG DB 'Deleted-->' Added_MSG DB 'NEW Fil-->' Tot_match_MSG DB 'File totals match',CR,LF Tot_nomatch_MSG DB 'File totals unequal',CR,LF Hit_Key_MSG DB '-Hit any key-' Hit_Key_MSG_end EQU $ Header_MSG DB ' File Name + Check Check File Update Update' DB CR,LF DB ' Extension: Val1: Val2: Size: Date: Time:' DB CR,LF DB ' ---------- ---- ---- ----- ------ ------' DB CR,LF SUBTTL INIT data + code (also input BUFFERs + stack) PAGE ;******************************************************************************; ;** Definition of file buffer Data areas and code follow: **; ;** All the following storage will be overlaid when records are read in **; ;******************************************************************************; EVEN Buffer_area label byte ;All storage + code following is in ; the input file buffer. ; ----------------------------------------------------------------------------; ; Initialization code - parse parms + put out msgs and open both files ; ; ----------------------------------------------------------------------------; Parse_Parms_Open_Files: ;Parse input parameters + displ header MOV SI,80H ;Parameter area in PSP MOV CL,[SI] ;Get # of chars in input parm XOR CH,CH ;Clear upper byte of char count INC SI ;point to first char ;---------------------------------------------------------------------------; ; Conventions for command line parsing: ; ; SI points to next char to be checked in the parm field at DS:80 ; ; CX is count of characters left to be scanned ; ; BP points to start of current processed filespec. ; ;---------------------------------------------------------------------------; Call Parse_Filespec ;exract 1st filespec from parm area MOV OLD_Filename_Loc,BP ;Store location of file name MOV OLD_Filename_end,DI ;Store char loc after end of filespec ; Adjust CX to reflect actual characters left to be scanned JCXZ Skip_decrement ;Don't decrement CX if already = 0 DEC CX Skip_decrement: Call Parse_Filespec ;exract 2nd filespec from parm area MOV NEW_Filename_Loc,BP ;Store location of 2nd file name MOV NEW_Filename_end,DI ;Store char loc after end of filespec CALL Parse_parms ;Process any "/" parms CALL Put_Out_Initial_MSGs ;Display header and start messages MOV DX,OLD_Filename_Loc ;Open the first ("OLD") file MOV AX,3D00H ;DOS open file (handle) for read cmnd INT 21H ;invoke DOS JNC Continue_Open ;If no errors continue processing ; If open fails for OLD file, treat it as if file were empty - keep going MOV Missing_Old_File,'Y' ;Else, Indicate old file was gone MOV DI,OLD_Filename_end CALL File_Open_Error ;Put out file open error message MOV DX, OFFSET Empty_OLD_MSG ;Tell user we will pretend file is MT MOV AH,09H ;DOS display string function INT 21H Continue_Open: MOV OLD_File_handle,AX ;Save DOS file handle MOV DX,NEW_Filename_Loc ;Open the 2nd (AKA "NEW") file MOV AX,3D00H ;DOS open file (handle) for read cmnd INT 21H ;invoke DOS JNC Open_done ;If no errors continue processing MOV DI,NEW_Filename_end CALL File_Open_Error ;Put out file open error message CALL Page_Wait ;Force user to acknoledge error MOV AX,4C14h ; terminate with 20 error level INT 21h Open_done: MOV NEW_File_handle,AX ;Save DOS file handle RET ;---------------------------------------------------------------------------; ; Parse filespec: ; ; Input: ; ; SI points to next char to be checked in the parm field at DS:80 ; ; CX is count of characters left to be scanned ; ; ; ; Returns: ; ; BP points to start of filespec ; ; DI points to byte after last char in filespec ; ; Filespec is zero terminated in the parameter area ; ;---------------------------------------------------------------------------; Parse_Filespec: ;Extract and zero terminate filename OR CL,CL ;Check for 0 chars (NO INPUT) JZ ERR_EXIT ;If no parms put out error msg DEL_SPACES: LODSB ;Get byte at DS:SI and inc SI CMP AL,' ' ;Is it a space? JNE Set_File_name ;If not, we should have a file name.. LOOP DEL_SPACES ;Cont checking until last char ERR_EXIT: MOV DX, OFFSET No_File_Msg ;Prepare error message MOV AH,09H ;DOS display string function INT 21H JMP SHORT Give_Syntax_and_Quit ;Give correct syntax and terminate ;--------------------------------------------; ; Parse file spec and zero byte terminate it ; ;--------------------------------------------; Set_File_Name: DEC SI ;point back to 1ST letter of filespec MOV BP,SI ;Save a copy of filespec start Scan_To_File_Spec_End: ; start scanning the file specification and transfer into output field LODSB ;Get next char of file spec CMP AL,' ' ;check valid separator character JBE file_spec_end_found CMP AL,'/' ;check for valid separator JE file_spec_end_found CMP AL,',' ;check for valid separator JE file_spec_end_found LOOP Scan_To_File_Spec_End INC SI ;Adjust SI if no separator char found File_Spec_End_Found: ; SI is pointing 2 characters past end of filespec at this time MOV DI,SI DEC DI ;DI points to 1st char after filespec MOV BYTE PTR [DI],00 ;zero terminate the filespec: ASCIIZ RET ;----------------------------------------------------------------; ; Parse Parms: parse /P and /C parameters ; ; Input: SI must point to next character to process ; ; CX contains # of chars left in paramter area ; ;----------------------------------------------------------------; Parse_Parms: Check_parm_chars_left: ;Check if enough chars left for a parm CMP CX,01 ;Check if we out of chars to scan JA Parm_Scan ; If Not, continue checking RET ; If no more chars, we are done Parm_Scan: ;Check for presence of a /_ parm CMP AL,'/' ;check for "/" parm character JE Parm_found CMP AL,' ' ;check for blanks JNE Unrecog_parm ;If other than blank its illegal... LODSB ;Keep checking next character LOOP Parm_Scan RET ;finished (parsing parms) Parm_Found: ;Check if parm is valid DEC CX ;Adjust chars remaining counter JCXZ Unrecog_parm ;IF no chars left then parm is invalid LODSB ;Get next char DEC CX ;Adjust chars remaining counter AND AL,5Fh ;Capitalize char CMP AL,'P' ;Is it the "Totals wanted" parm? JE P_parm ;T parameter detected CMP AL,'C' ;Is it alternate Check Sum parm? JE C_parm ;C parameter detected.. CMP AL,'O' ;Is it "Only chk field compare parm"? JE O_parm ;C parameter detected.. Unrecog_parm: ; an illegal paramter: MOV DX, offset Bad_Parm_MSG ;indicate illegal parm was found MOV AH,09H ;DOS display string function INT 21H Give_Syntax_and_Quit: CALL Page_Wait ;Force user to acknoledge error MOV DX, offset Syntax_MSG ;Give user the correct syntax MOV AH,09H ;DOS display string function INT 21H MOV AX,4C80h ; terminate with 128 error level INT 21h P_parm: MOV Page_Mode,03h ;Indicate user wants page mode ;Std out already has 3 line header ;Originally =FFh to turn page mode off LODSB ;Keep checking next character JMP SHORT Check_Parm_chars_left ;Check for additional parms C_parm: MOV Changes_Only,'Y' ;User wants only changes (no add/del) LODSB ;Keep checking next character JMP SHORT Check_Parm_chars_left ;Check for additional parms O_parm: ;"Only" check file size + check fields ; IF the /O option is selected, we will patch the length field of two ; MOV CX,19 instructions in the COMPARE_FILES subroutine labeled PATCH1 and ; PATCH2. The modified routine will then only compare 9 words (the CHECK fields ; + file size ) rather than the entire remaining record. MOV BYTE PTR[PATCH1+1],09 ;Patch length field of MOV CX, instr MOV BYTE PTR[PATCH2+1],09 ;Patch length field of MOV CX, instr LODSB ;Keep checking next character JMP SHORT Check_Parm_chars_left ;Check for additional parms ;----------------------------------------------------------------; ; File Open Error - put out file open error message + terminate; ; Input: DX must point to start of filespec ; ; DI must point to end of filespec ; ;----------------------------------------------------------------; File_Open_Error: PUSH DX ;Save filename MOV DX, offset Open_Err_MSG ;indicate open failed MOV CX,23 ;Length of msg is 23 chars MOV AH,40h ;DOS Write func MOV BX,1 ;Handle for std output device INT 21h ;Write beginning of open error message POP DX ;restore name of file loc to DX SUB DI,DX ;Calc length of filename + CR,LF MOV CX,DI MOV AH,40h ;DOS Write func INT 21h MOV DX,OFFSET CRLF_Msg ;Put out Carriage return + line-feed MOV AH,09h INT 21h RET Put_Out_Initial_MSGs: ;Display header and start messages MOV DX, offset Start_MSG ;beginning of start message MOV CX,SM_End-Start_MSG ;# of chars in start message MOV AH,40h ;DOS Write func MOV BX,1 ;Handle for std output device INT 21h ;Write Start message MOV SI,offset Start_Dir ;Place to store current directory XOR DL,DL ;Zero DL in order to use default drive MOV AH,47h ;Get current directory (path) func INT 21h CLD ;Scan in forward direction MOV DI,offset Start_Dir ;Scan dir strng to determine length XOR AX,AX ;Scan for zero termination of dir MOV CX,64 ;Scan up to 64 chars of directory REPNE SCASB ;Find 1st zero byte MOV AX,CRLF STOSW ;Terminate dir string with CR LF MOV DX, offset Start_Dir ;beginning loc of directory string SUB DI,SI ;Calc length of directory string MOV CX,DI ;Length reg for DOS write function MOV AH,40h ;DOS Write func INT 21h ;Write Dir string to finish start msg RET ; --------------------------------------------------; ; Initialization DATA STORAGE ; ; --------------------------------------------------; Start_MSG DB CR,LF,"CFCOMP 1.0 ",BOX," PCDATA TOOLKIT (c) 1990" DB " Ziff Communications Co.",CR,LF DB "PC Magazine ",BOX," Wolfgang Stiller - In directory: \" SM_End LABEL BYTE ;End of the Start message Start_Dir DB 66 DUP (0) Open_ERR_Msg DB 'CFCOMP unable to open: ' Empty_Old_Msg DB 'OLDfile assumed empty - execution continues.',CR,LF,'$' Bad_Parm_MSG DB 'Unrecognized parameter detected.',CR,LF,LF,'$' NO_FILE_Msg DB 'You must specify at least OLD and NEW file names to compare.' DB CR,LF,'$' Syntax_Msg DB "CFCOMP 1.0 ",BOX," PCDATA TOOLKIT Copyright (c) 1990" DB " Ziff Communications Co.",CR,LF DB "PC Magazine ",BOX," Wolfgang Stiller",CR,LF DB CR,LF,'CFCOMP does a high speed compare of the compressed' DB ' report files produced',CR,LF DB 'by CHKfile. It displays all changes between the OLD and ' DB 'NEW report files.',CR,LF,LF DB 'Syntax is: CFCOMP OLDfile NEWfile [/C] [/O] [/P]' DB CR,LF,LF DB ' OLDfile and NEWfile are files created by CHKFILE.COM.' DB CR,LF,LF DB ' "/C" Display only changed files not additions or ' DB 'deletions.',CR,LF DB ' "/O" Only use check fields and file size in comparing ' DB 'files.' DB CR,LF DB ' DOS time and Date stamps are not used for compare.' DB CR,LF DB ' "/P" Pause between pages if changes found.' DB CR,LF,'$' CSEG EndS END CFCOMP