// COMPARE.VDM - VEDIT file comparison macro // // Originally by: Thomas C. Burt, Greenview Data, Inc. // Enhanced by: Christian Ziemski // 07-Aug-2001 by CZ Fix on initial(empty) mask for Get_Filename() under W2k // Little modification on the Point&Shoot directory // more info output there // default directory for second file // Help on Tools menu re-added // [2] (exit macro and leave files loaded) leaves the Tools menu loaded too // Last change: 25-Oct-2001 by CZ Alert on removed // "Quit macro" added to Tools menu // (this way the focus is set correctly if run from COMPDIR!) // it's a shortcut to: ESC 5 4 // Line(0,LOCAL) // by mail from Ted, 27.03.2002 // // Requires: VEDIT 5.21 or later. // // Usage: Select {MISC, More macros, COMPARE}. // // From OS: vpw -x compare filename.1 filename.2 // // -OR- vpw -n 1 -x compare filename.1 filename.2 // // The 2nd form suppresses the confirmation prompts. // // Note: The comparision is either case sensitive or insensitive // depending upon the setting of {SEARCH, Config, Default // case-sensitive option}. // //////////////// // // Text Register Usage // // 102 Traps [VISUAL ESCAPE] and other macro-abort conditions // 103 Active filename // 104 Template filename // 105 Temporary // // Numeric Register Usage (0 - 27 are saved and restored) // // #0 Temp for user input // #1-4 Locally used variables // #5 T-Reg ID # for current macro // #6 Line number in active buffer of current discrepancy // #7 Line number in active buffer where user adjusted the cursor // #9 Line number in template buffer // #10 Flag - files are still match // #11 Return value from last Compare() in COMPARE // #18 Save state of Visual_Macro // #20 Save original Undo setting // #21 Save original "-MORE-" mode // #22 Whether windows are being used: T,S,N for T/B, S-S, None // #23 Set to "CASE" to perform case sensitive comparison // #24 Inform Macro-X to not scroll last line off top of window // #25 Flag to determine Break-out operation // #26 Temp // #27 Line number for aligning the two windows // // #100 Temp for user input // #101 Flag - compare existing files in buffers 1 and 2 // //************************************************************************ // // Save keyboard layout and {TOOLS} menu. // Then remove any key assignments to the {TOOL} menu. // Key_Cfg_Push() // Save keyboard layout (restored on exit) Reg_Push(123,123,SET) // Save the old Tools menu (restored on exit) // #103=Buf_Num Buf_Switch(Buf_Free) Out_Ins() Key_List() Out_Ins(CLEAR) BOF while (! At_EOF) { Search("\[MENU\]t",REGEXP+NOERR+ERRBREAK) // search the Tools menu entries Reg_Copy_Block(103,Cur_Pos,EOL_Pos) // get the key Key_Delete(@103,REVERSE+NOERR) // and delete it from key table Line(1, NOERR) } Buf_Quit(OK) Buf_Switch(#103) // // Replace {TOOLS} menu with useful functions for comparing files. // *** Delete one of the compared files from disk // *** Replace one fo the compare files with the other file // Reg_Set(123,'1 Help DI1(1,"`Tools Help`, `The {Tools} menu contains some commands to help synchronize files and clean up the disk.\n\nYou can\n - delete one or both files currently worked on from disk\n or\n - overwrite (=update) one file with the other.`") 128 &Quit macro Call(#5,"Q-SAVE") SI() Qally 128 *** Attention: Dangerous commands! *** U 128 Delete _first_ file from disk #103=BN BS(1) RS(103,PATHNAME) if(Reg_Size(103)==0){BS(#103) return} BQ(OK) BS(1) U BS(2) U FDEL(@103, OK) 0 Delete _second_ file from disk #103=BN BS(2) RS(103,PATHNAME) if(Reg_Size(103)==0){BS(#103) return} BQ(OK) BS(2) U BS(1) U FDEL(@103, OK) 128 Copy first file --> second file #103=BN BS(1) RS(103,PATHNAME) if(Reg_Size(103)==0){BS(#103) return} FS(NOMSG) BS(2) RS(104,PATHNAME) if(Reg_Size(104)==0){BS(#103) return} BQ(OK) U FCP(@103,@104, OK) BS(2) FO(@104) U 0 Copy to first file <-- second file #103=BN BS(2) RS(103,PATHNAME) if(Reg_Size(103)==0){BS(#103) return} FS(NOMSG) BS(1) RS(104,PATHNAME) if(Reg_Size(104)==0){BS(#103) return} BQ(OK) U FCP(@103,@104, OK) BS(1) FO(@104) U ') Config_String(TOOL_MENU,"&Tools") Key_Add("Alt-T",'[MENU]T',OK) Win_Clear() Message(" ************************************ * * * COMPARE.VDM - 07/23/01 * * File Comparison Macro * * * ************************************ ",TAB8) // // Check if buffer #1 and/or #2 are in use // #101 = FALSE // Clear flag - don't compare existing files #102 = 0 // Clear buffer count flag Buf_Switch(1) // Check main buffer if (Is_Open_Write) { #102 = 1 } // If file open, set flag else { Buf_Empty(OK) } // Else empty it Buf_Switch(2) // Check buffer #2 if (Is_Open_Write) { #102 = #102 | 2 } // If file open, set flag else { Buf_Empty(OK) } // Else empty it if (#102==3) { // If buffers 1 and 2 in use... if (N_Option==1) { // If "-n" invocation option... #101 = TRUE // } else { Alert() Message("\nBuffers 1 and 2 are in use. You can save, abandon or compare them.\n") Message("[S]ave files; [A]bandon files; [C]ompare files; [E]xit (cancel) macro : ") while ((#100 = Get_Key()&0xFFDF)!='S' && #100!='A' && #100!='C' && #100!='E' && #100!='X') { Alert } // Get valid input "S", "A", "C", "E" or "X" Char_Dump(#100) if (#100=='E' || #100=='X') { if (Visual_Macro) { Break_Out(EXTRA) } else { Break_Out } } if (#100=='S') { Buf_Switch(1) File_Close(NOMSG) // Save the files Buf_Switch(2) File_Close(NOMSG) } if (#100=='A') { Buf_Switch(1) Buf_Empty(OK) // Abandon the files Buf_Switch(2) Buf_Empty(OK) } if (#100=='C') { #101 = TRUE } // Set flag - compare existing files } } else { if (#102>0) { // If buffers 1 or 2 in use... Alert() Message("\nBuffers 1 or 2 (but not both) are in use. You can save or abandon them.\n") Message("[S]ave file; [A]bandon file; [C]ancel macro : ") while ((#100 = Get_Key()&0xFFDF)!='S' && #100!='A' && #100!='C') { Alert } // Get valid input "S" or "A" or "C" Char_Dump(#100) if (#100=='C') { if (Visual_Macro) { Break_Out(EXTRA) } else { Break_Out } } if (#100=='S') { Buf_Switch(1) File_Close(NOMSG) Buf_Switch(2) File_Close(NOMSG) } else { Buf_Switch(1) Buf_Empty(OK) Buf_Switch(2) Buf_Empty(OK) } } } // // Setup the "locked-in" macro in T-Reg 102. // Reg_Set(102,` Buf_Switch(1,LOCAL) if (Win_Status('$')>=0) { Win_Switch('$') } // Be sure not on status line else { Win_Switch(1) } if (#24==0) { Type_Newline() } // Scroll window unless suppressed #24 = 0 // Flag - OK to scroll #101 = FALSE // Flag - don't compare existing files //Alert() // CZ: that is annoying ... if (#25<2) { if (#25==0) { //If comparison not running Message("BREAKOUT! Please select from the following options:") } else { Num_Type(Cur_Line-1,LEFT+NOCR) Message(' lines from "active file" examined.') } Message(" [1] Select files to compare [3] Exit macro, return to VEDIT [2] Exit macro, (leave files loaded) [4] Exit macro, return to OS Enter Option: ") while ((#0 = Get_Key())<'1' || #0>'4') { Alert() } //Get valid reply Type_Char(#0) if (#0=='1') { Type_Newline() Chain(#5,"STARTUP") } if (#0=='2') { Reg_Lock_Macro(CLEAR) Num_Pop(0,27) Reg_Set(103,@123) // CZ remember Tools-Menu Reg_Pop(123,123) // Restore previous {Tools} menu Key_Cfg_Pop() // Restore previous keyboard layout Reg_Set(123,@103) // Now the stack is clean and we can restore the {Tools} menu // to our own one for usage after the macro has exited Break_Out(Extra) } if (#0=='3') { Escape_Mode(-1) Call(#5,"Q-SAVE") // Check if need to save files if (#22!='N') { Screen_Init() } Buf_Switch(2,LOCAL) Buf_Quit(OK) Config(U_E_UNDO,#20) // Restore Undo setting Config(S_E_MORE,#21) // Restore -MORE- setting if (#18) { Visual_Macro(SET+NOMSG) } //Return to Visual Mode; no Pause Reg_Lock_Macro(CLEAR) // Disable locked-in macro Reg_Empty(#5,EXTRA) // Empty main macro Num_Pop(0,27) Reg_Pop(123,123) // Restore previous {TOOLS} menu Key_Cfg_Pop() // Restore previous keyboard layout Reg_Empty(Macro_Num,EXTRA) // Empty this macro and return Return // Never gets here, but be safe } if (#0=='4') { Call(#5,"Q-SAVE") SI() Qally } // SI() prevents focus problems } if (#25 != 3) { Message("\nFILE COMPARISON INTERRUPTED! Please select from following options:\n") } else { Message(" - Select:\n") #25 = 2 } Message(" [1] Examine active file [3] Resume, no realignment [2] Examine template file [4] Realign template & resume [5] Stop. Get exit options menu Enter Option: ") while ((#0 = Get_Key())<'1' || #0>'5') { Alert() } //Get valid reply Type_Char(#0) if (#0=='1') { Buf_Switch(1,LOCAL) Call(#5,"SHOW") #7 = Cur_Line Call(#5,"REALIGN") Chain(#5,"DO-COMP") } if (#0=='2') { Buf_Switch(2,LOCAL) Call(#5,"SHOW") Chain(#5,"DO-COMP") } if (#0=='3') { Chain(#5,"DO-COMP") } if (#0=='4') { Call(#5,"REALIGN") Chain(#5,"DO-COMP") } if (#0=='5') { Type_Newline(2) #25=1 Break_Out } `) // // ************ Initial Macro Continued ********************************* // Buf_Switch(1) // Back to buffer 1 Num_Push(0,27) // Save variables #0-#27 #20 = Config(U_E_UNDO,2) // Enable full Undo #21 = Config(S_E_MORE,0) // Disable -MORE- pause; save old value #22 = 0 // Prompt for window type //#22 = 'S' // Or, force side-by-side windows as default #18 = Visual_Macro // Save state of Visual_Macro #5 = Macro_Num // #5 = currently executing T-Reg #24 = 0 #25 = 0 // Flag that comparison not running Reg_Lock_Macro(102) // Macro 102 traps all escape attempts Visual_Macro(CLEAR) // Don't want "Press any key..." message Key_Add("F12","[VISUAL EXIT] if(Buf_Num==1){Buf_Switch(2)}else{Buf_Switch(1)}",INSERT+OK) // Define as window toggle Key_Add("Alt-Cursor-up",'[VISUAL EXIT]#103=Buf_Num BS(1) DoV("\CU\") BS(2) DoV("\CU\") BS(#103)',INSERT+OK) // Define as scroll both windows up Key_Add("Alt-Cursor-down", '[VISUAL EXIT]#103=Buf_Num BS(1) DoV("\CD\") BS(2) DoV("\CD\") BS(#103)',INSERT+OK) // Define as scroll both windows down Win_Vert(Win_Lines-3) // Determine whether/what kind of windows to use Win_EOS() // Ensure rest of window is cleared if (N_Option!=1 && #22==0) { // If not "-n" invocation option... Message("Do you want to use windows to view/edit the active & template files? [T]op/Bottom; [S]ide-by-Side; [N]o windows; [E]xit VEDIT; [C]ancel macro: ") while ((#22 = Get_Key()&0xFFDF)!='T' && #22!='S' && #22!='N' && #22!='E' && #22!='X' && #22!='C') { Alert } // Get valid input "T","S","N","E","X" or "C" Char_Dump(#22) } else { #22 = 'S' // If "-n" option, use side-by-side windows } if (#22=='E' || #22=='X') { Exit() Break_Out } if (#22=='C') { Config(U_E_UNDO,#20) // Restore Undo setting Config(S_E_MORE,#21) // Restore -MORE- setting Win_Clear() if (#18) { Visual_Macro(SET+NOMSG) } // Return to Visual Mode; no Pause Reg_Lock_Macro(CLEAR) // Disable locked-in macro Num_Pop(0,27) Reg_Empty(102) Reg_Pop(123,123) // Restore previous {TOOLS} menu Key_Cfg_Pop() // Restore previous keyboard layout Reg_Empty(Macro_Num,EXTRA) // Empty this macro and return Return // Never gets here, but be safe } if (#22=='S' || #22=='T') { if (Win_Status(1) == -1) { // If window #1 doesn't yet exist... Win_Create(1,0,0,0,0) // Create it to prevent hang under WinNT } Screen_Init() if (Win_Border>=2) { Win_Reserved('$',6,BOTTOM+NOBORDER) } else { Win_Reserved('$',6,BOTTOM) } if (#22=='S') { Win_Split(2,Win_Cols/2,RIGHT) } else { Win_Split(2,Win_Lines/2-1,BOTTOM) } Win_Switch('$') } // // "STARTUP" - Queries the user for file names and opens the files. // :STARTUP: #25 = 0 // Flag that comparison not running if (#101==FALSE) { // Unless comparing existing files ... Call("Q-SAVE") // Check if need to save (old) active file Buf_Switch(1,LOCAL) if (Win_Vert > 1) { Type_Newline() } Message(`Default directory is: `) Chdir("",NOMSG) repeat (ALL) { // LOOP until valid filename Get_Input(103,"\n(Just press for file dialog box.)\nEnter the name of the active (first) file: ",NOCR) if (Reg_Size(103)==0) { Reg_Set(103,".") } // CZ changed "*" to "." to make it working under W2k too Get_Filename(103,@103) if (File_Exist(@103)) { Break } Message("File not found. Try again.\n") } Buf_Switch(XBUF1) Buf_Empty(OK) Reg_Ins(103) Call("CHECK") // Check for .BAK filename Buf_Switch(1,LOCAL) // File_Open("|@(103)") File_Open("|@(103)", CHGDIR) // CZ: CHGDIR added to change the default dir // to that one of the first file. // IMHO that's more convenient. Message(" ") // CZ: show complete filename lined up PATHNAME // if (#22!='N') { Update() } repeat (ALL) { // LOOP until valid filename Get_Input(104,"Enter the name of the template (second) file: ",NOCR) if (Reg_Size(104)==2) { // If possible "d:"... Out_Reg(105) Reg_Type_Block(104,1,2) Out_Reg(CLEAR) if (Reg_Compare(105,":")==0) { Reg_Set(104,@103,APPEND) } } if (Reg_Size(104)==0) { Reg_Set(104,".") } // CZ changed "*" to "." to make it working under W2k too Get_Filename(104,@104) if (File_Exist(@104)) { Break } Message("File not found. Try again.\n\n") } Buf_Switch(XBUF1) Buf_Empty(OK) Reg_Ins(104) Call("CHECK") // Check for .BAK filename Buf_Switch(2,LOCAL) File_Open("|@(104)") } else { Buf_Switch(1,LOCAL) Update() Buf_Switch(2,LOCAL) Update() } if (#22!='N') { Update() } #10 = 0 // #10==0 => files match // // "DO-COMP" - Supervises the file comparison and reports the results. // :DO-COMP: #25 = 2 // Flag - comparison now running Reg_Lock_Macro(102) // Macro 102 traps all escape attempts Call("COMPARE") // Do the comparison Call("RESULT") // Report results // Set #25 to desired break-out processing Break_Out // Let "locked-in" macro handle options // // "Q-SAVE" - Checks if changes to file(s) are to be saved. // :Q-SAVE: Buf_Switch(1,LOCAL) if (Is_Altered==0) { // Just empty buffer if file not altered Buf_Empty(OK) } else { Message("\nSave changes to active file? [Y]es [N]o ") while ((#0 = Get_Key()&0xFFDF)!='Y' && #0!='N') { Alert } //Get valid input "Y" or "N" Char_Dump(#0) if (#0=='Y') { File_Close() } else { Buf_Empty(OK) } } Buf_Switch(2,LOCAL) if (Is_Altered==0) { // Just empty buffer if file not altered Buf_Empty(OK) } else { Message("\nSave changes to template file? [Y]es [N]o ") while ((#0 = Get_Key()&0xFFDF)!='Y' && #0!='N') { Alert } //Get valid input "Y" or "N" Char_Dump(#0) if (#0=='Y') { File_Close() } else { Buf_Empty(OK) } } Return // // "SHOW" - Shows where there is a mismatch. It tries to avoid problems // with cursor positioning when entering visual mode. If there are less // than 24 lines to the end of the buffer, 24 lines are appended. This // should eliminate auto buffering. // Note: is assigned the function [Visual Escape] for easily // accessing the Dispatcher; it is restored when COMPARE quits. // :SHOW: #24 = 0 #26 = Cur_Pos #17 = Cur_Line End_Of_File(LOCAL) if (Cur_Line-#17<24) { File_Read(24) } Goto_Pos(#26) Set_Visual_Line(#27) //Align the windows with differences Escape_Mode('V'+'S'*256) Win_Switch('$') Win_Clear() Message('The cursor(s) show where the files are different. Position the cursor in buffer (window) #1 where the files are again identical for at least 24 characters. Then press [VISUAL EXIT] (<\>) and COMPARE will re-align and continue the comparison. If necessary, you can also move the cursor in buffer #2. Press to toggle between the two buffers/files.') Visual() Return // // "CHECK" - Gives error if ".BAK" file or file not found. // :CHECK: if (Config(F_E_BACKUP) && Search(/.BAK/,BEGIN+NOERR)) { Alert() Message('\nCannot compare files with extension ".BAK" when\n{CONFIG, File handling, Backup files} is enabled.\nRename the file and try again.') Get_Key(" Press any key to continue...") Break_Out } Return // // "COMPARE" - Does the actual file comparison between buffers 1 & 2. // A full auto-buffered read has been done on buffer $1. // Set #23 to CASE if a case sensitive comparison is desired. // Return: #11 = return value from Compare() // :COMPARE: if (Config(SR_CASE_OPT)) { #23 = CASE } else { #23 = 0 } Message("\nComparing ...\n") repeat (ALL) { // LOOP until break out or Return Buf_Switch(1,LOCAL) // Ensure the main buffer is selected #11=Compare(2+BUFFER,#23) // Compare main buffer with pattern buffer 2 // Case sensitive if #23 == CASE if (At_EOF) { Return } // End of active file implies process done Buf_Switch(2) // Otherwise check for end of pattern buffer if (At_EOF) { Return } // Also done if at end of pattern file // // Show Mismatch // #10 = -1 // Clear files-match flag if (Win_Status(2)==2) { Buf_Switch(2,LOCAL) Update() // Force template window to update } #27 = Win_Vert // #27 = line # in this window Buf_Switch(1,LOCAL) // Reselect main edit buffer #6 = Cur_Line // Save current line number Set_Visual_Line(#27) // Align the windows with differences Call("SHOW") // Enter visual mode at discrepancy if (Buf_Num==2) { Goto DO-COMP } // Continue comparison if coming from template #7 = Cur_Line // Else get adjusted line number Call("REALIGN") // Realign active file with template file // (cursor at start of realignment string) // (Aborts with error message if unsuccessful) } // Repeat Return // // "REALIGN" - Realigns the template file with the active file. // // 1) A realignment string is created in Extra_Buffer_1 from the 24 // characters following the current edit position in the main buffer. // 2) The template/pattern buffer 2 is made the current edit buffer. // 3) If the user had moved the active cursor back towards the beginning of // the active file, then the template cursor is likewise backed up. // 6) A search is made for an occurrence of the realignment string. // 7) When the realignment string is matched, the template cursor is // reset to the start of the matching string and control is returned. // 8) If there is no match, various recovery attempts are made. // 9) If there is still no match, a message is printed and the comparison // is aborted. :REALIGN: if (Config(SR_CASE_OPT)) { #23 = CASE } else { #23 = 0 } Buf_Switch(1,LOCAL) if (At_EOF ) { Goto EOT } // Just in case #2 = 1 repeat (ALL) { // // 1) // Reg_Copy_Block(105,Cur_Pos,Cur_Pos+24) // Copy 24 chars from main buffer into @105 Buf_Switch(XBUF1) Buf_Empty(OK) Reg_Ins(105) // Copy the chars to Extra_Buffer_1 Replace("||","||||",BEGIN+ALL+NOERR) // Replicate each wild card char BOF() Reg_Copy(105,ALL) // Copy back to @105 // // 2) // Buf_Switch(2,LOCAL) // Activate template/pattern buffer 2 // // 3) // if (#7<#6) { Line(#7-#6,NOERR) } // Backup cursor if active cursor backed up #9 = Cur_Line #3 = Cur_Pos // Save current position Begin_Of_File(LOCAL) if (#9-Cur_Line>24) { // Write out excess processed text Goto_Pos(#3) Line(0,LOCAL) // by mail from Ted, 27.03.2002 File_Write(0) } End_Of_File(LOCAL) if ((Cur_Line-#9)<60) { // Want at least 60 additional lines File_Read(60-(Cur_Line-#9)) } Goto_Pos(#3) // Restore position // // 6) - Align pattern file by finding @105 in #2. // 7) - If successful, return. // if (Search(@105,LOCAL+NOERR+#23)!=0) { Return } // // 8) - Try other matching attempts. // Char(-24) // Else back up 1 pattern length and retry if (Search(@105,LOCAL+NOERR+#23)!=0) { Return } Goto_Pos(#3) // Restore position Update() // Redraw screen // // Still unsuccessful. Allow user to reposition Active File cursor. // if (#2>1) { Break } #2++ Win_Clear() #0 = Get_Key('\nUnable to re-align template file. Please select another position. Press any key to continue...',RAW) Buf_Switch(1,LOCAL) Call("SHOW") } // // That didn't work either. Ask whether alright to start global search. // End_Of_File(LOCAL) File_Read(100) // Go to end of buffer and read more text if (At_EOB) { Goto EOT } // If E-O-B, print message & quit // Win_Clear() #0 = Get_Key('\nStill unable to re-align files. OK to start searching to the end of the template file? (Press "Y" if so, any other key if no): ',RAW) & 0xFFDF if (#0>31 && #0<127) { Type_Char(#0) } if (#0!='Y') { Break_Out } Begin_Of_File(LOCAL) if (Search(@105,ADVANCE+NOERR+#23)!=0) { Return } // // Unalignable. 9) // :EOT: // Premature end of template file Goto_Pos(#3) Win_Clear() Message("\nEnd of template file reached. Unable to align active and template files.") if (Win_Status('$')<0) { Type_Newline() } else { #24 = 1 } Break_Out Return // // "RESULT" - Reports results of the file comparison. // Enter: #10==0 if 1st compare reached EOF of file(s). // #11 = return value from Compare() // :RESULT: Buf_Switch(2,LOCAL) #2 = At_EOF // Set #2 = @EOF for template file. Buf_Switch(1,LOCAL) #1 = At_EOF // Set #1 = @EOF for active file. #24 = 1 #25 = 3 //Flag to continue comparison if (!#1 ) { Message("\nEnd of template reached; active file is longer.") Return } if (!#2 ) { Message("\nEnd of active file reached; template file is longer.") Return } #25 = 1 // Flag - Comparison done if (#10!=0) { Message("\nThe remainder matches.\n") Return } Message("\nFiles match completely.\n") Return