// COMPDIR.VDM - Quickly compare all files in two directories to determine // which are different, unique or the same. // Implements {MISC, More Macros, COMPDIR}. // // Originally by: Thomas C. Burt, Greenview Data, Inc. // Updated by Ted Green to support Win2000. // Rewritten by Christian Ziemski to support Windows version, // long filenames, European "dir" format, optionally process // subdirectories. // Updated by George E. Christman to support Windows XP // Last change: 06-May-2003 by Tom Burt // Last change: 18-May-2004 by Christian Ziemski: Fix for compare in root (e.g. C:\) // Last change: 12-May-2006 by Christian Ziemski: Fix for problem with long paths in Sys() // // Requires: VEDIT 6.00 or later. // // From VEDIT: Select {MISC, More Macros, COMPDIR}. // // From OS: COMPDIR dir1 dir2 (Using the supplied COMPDIR.BAT file) // // -or- vpw -x compdir.vdm -u dir1 dir2 // // -or- vpw -x compdir.vdm (To be prompted for directories) // // -or- vpw -n1 -x compdir.vdm (To process sub-directories) // // -or- vpw -t -x compdir.vdm (Force full compare of all files) // // Description: Compares the files in the two specified directories. // One window displays the files which are different. // Two windows display the unique files in each directory. // If desired, the file lists can be saved as DIFFER.FIL, // UNIQUE.1, UNIQUE.2 and SAME.FIL; they are saved in the // VEDIT/TEMP directory. // // The directories entered by the user are saved into this macro. // // Options: Invocation option "-n1" processes subdirectories too. // // With "#77 = 1" below, the files DIFFER.FIL, SAME.FIL, UNIQUE.1, // UNIQUE.2 are saved in the VEDIT/TEMP directory (which is // created if necessary) instead of the current directory. // With "#77 = 2" below, the files are auto-saved into VEDIT/TEMP. // // With "#78 = 0" below, the last entered directories are not // saved and not used as the default. // // With "#79 = 0" below, .BAK files are not compared. // // With "#75 = 1" below, files are sorted by extension instead // of by filename. // // With "#74 = 1" below, a blank line is displayed between // the filename pairs. // ///////////////////////////////////////////////////////////////////////////// // // dir1: C:\vedit\macros\ // dir2: c:\vedit\user-mac // subd: no // ///////////////////////////////////////////////////////////////////////////// // // Register Usage: // // Buffer[#61] list of files that differ // Buffer[#62] DIR1.OUT; becomes list of files unique to dir1 // Buffer[#63] DIR2.OUT; becomes list of files unique to dir2 // Buffer[#64] list of files that are the same // Buffer[#65] file from dir1 // Buffer[#66] file from dir2, same name/size, compare against Buffer[#65] // // Register(10) - pathname for files listed in dir1 // Register(11) - pathname for files listed in dir2 // Register(12) - for DI1() // Register(13) - for DI1() // Register(14) - for DI1() // Register(15) - for DI1() and System() // Register(16) - for DI1() // Register(17) - work register // Register(18) - list of identical files // Register(19) - work register // Register(20) - target directory for temp files // Register(21) - target directory for saved files // ///////////////////////////////////////////////////////////////////////////// // // Init - 1) Save state // 2) Settings for user options // 3) Determine free buffers/T-Regs // 4) Setup as locked-in macro // if (#99==0x43444952) { Goto EXIT } //If macro already running ("CDIR") // Num_Push(12,12) //Save locally used numeric register Num_Push(60,99) //Save locally used numeric registers Reg_Push(10,21) //Save locally used text registers // // Set user's options. // #74 = 0 //Flag to add blank lines between filename pairs #75 = 0 //Flag to sort by filename //Change to "1" to sort by filename extension #12 = 0 //no subdir search/compare //Change to "1" to switch the default to "incl. subdirs" #77 = 1 //Flag to use current dir for saved files //Change to "1" to use (VEDIT_TEMP), e.g. c:\vedit\temp //Change to "2" to auto-save into (VEDIT_TEMP) #78 = 1 //Save the last directories for next time #79 = 1 //Compare all files, including .BAK files //Change to "0" to skip comparing .BAK files // #80 = Macro_Num //#80 = T-Reg containing this macro #81 = Reg_Lock_Macro //#81 = parent's locked-in macro (0=none) #82 = Buf_Num //#82 = original buffer #83 = Win_Num //#83 = original window # (maybe status line!) #84 = Config(F_E_F_MACRO,0) //#84 = original Config value // Disable file open/close macros #85 = Config(S_E_MORE,0) //#85 = original Config value; disable 'MORE' #86 = Visual_Macro //#86 = save state of Visual_Macro #87 = Config(F_AUTO_SAVE,0) //#87 = original Config value // Disable auto-save during this macro // if (!Is_Altered && File_Size==0) { #61 = Buf_Num //Use current buffer if empty } else { Buf_Switch(#61=Buf_Free) //#61 = buffer for DIFFER.FIL } Buf_Switch(#62=Buf_Free) //#62 = buffer for DIR1.OUT and UNIQUE.1 Buf_Switch(#63=Buf_Free) //#63 = buffer for DIR2.OUT and UNIQUE.2 Buf_Switch(#64=Buf_Free) //#64 = buffer for SAME.FIL Buf_Switch(#65=Buf_Free) //#65 = buffer to open file in dir1 Buf_Switch(#66=Buf_Free) //#66 = buffer to open file in dir2 // #99=0x43444952 //Macro now running ("CDIR") Reg_Lock_Macro(Macro_Num) //Enable Break-out macro Key_Add("F12","[VISUAL EXIT] Call(#80,'GETFILE')",INSERT+OK) ///////////////////////////////////////////////////////////////////////////// // // Create (VEDIT_TEMP) temp directory, if necessary. // if (#77) { if (File_Exist(VEDIT_TEMP)==0) { //Create vedit/temp dir if needed File_Mkdir(VEDIT_TEMP) } Reg_Set(20,VEDIT_TEMP) Reg_Set(21,@20) } else { Reg_Set(20,CUR_DIR) //Else, just use current directory Reg_Set(21,@20) } // // If running from a Batch file with "-u", get directories from CMD_LINE. // if (Is_Auto_Execution && Is_Option('u')) { if (N_Option == 1) { #12 = 1 } //If invoked with "-n1" option, // process subdirectories too Reg_Set(20,CUR_DIR) //Use current dir for DIRx.OUT files Buf_Switch(Buf_Free(EXTRA)) Ins_Text(CMD_LINE) //Get command line Ins_Char(' ') //Need trailing space Search("-u|W",BEGIN+ADVANCE+NOERR) //Look for special "-u" option if (Error_Match) { goto dialog } if (Cur_Char=='"') { //If dir specified in double-quotes... Char() Block_Begin(CP) Search(/"/,ADVANCE+NOERR) if (Error_Match) { goto dialog } Reg_Copy_Block(10,BB,CP-1) //Reg[10] = 1st dir name } else { Block_Begin(CP) Search("|W",NOERR) if (Error_Match) { goto dialog } Reg_Copy_Block(10,BB,CP) //Reg[10] = 1st dir name } Search("|!|S",NOERR) //Advance to the 2nd dir name if (Error_Match) { goto BATCH1 } if (Cur_Char=='"') { //If dir specified in double-quotes... Char() Block_Begin(CP) Search(/"/,ADVANCE+NOERR) if (Error_Match) { goto BATCH1 } Reg_Copy_Block(11,BB,CP-1) //Reg[11] = 2nd dir name } else { Block_Begin(CP) Search("|W",NOERR) if (Error_Match) { goto BATCH1 } Reg_Copy_Block(11,BB,CP) //Reg[11] = 2nd dir name } Buf_Switch(#61) goto SHELL // :BATCH1: Reg_Set(11,@10) //Use dir name as 2nd dir Reg_Set(10,CURDIR) //User current dir as 1st dir Buf_Switch(#61) goto SHELL } // // If running from a Batch file without "-u" and DIR1.OUT and DIR2.OUT exist, // ready to start comparison. // if (Is_Auto_Execution && !Is_Option('u') && File_Exist("dir1.out") && File_Exist("dir2.out") ) { if (N_Option == 1) { #12 = 1 } //If invoked with "-n1" option, // process subdirectories too Reg_Set(20,CUR_DIR) //Use current dir for DIRx.OUT files goto RUN } // // If not running from Batch file, prompt for the directories to compare. // The default is the last entries saved in COMPDIR.VDM itself. // :DIALOG: #69 = Buf_Free(EXTRA) Buf_Switch(#69,EXTRA) //Open compdir.vdm in an extra buffer // // Try to find the source file of this running macro so we can save the // user's selection back into it. If unable, just use the currently running // macro to obtain the user's previous selection for the default prompts. // #70 = 0 //Init flag that compdir.vdm was found if (File_Exist("compdir.vdm")) { File_Open("compdir.vdm",FORCE) #70 = 1 } else { if (File_Exist("|(USERMACRO)/compdir.vdm")) { File_Open("|(USERMACRO)/compdir.vdm",FORCE) #70 = 1 } else { if (File_Exist("|(MACRO)/compdir.vdm")) { File_Open("|(MACRO)/compdir.vdm",FORCE) #70 = 1 } else { Reg_Ins(#80) }}} // // Get the default dir1 and dir2 prompts into Reg(10) and Reg(11). // Reg_Empty(10) //In case "dir1:" not found Reg_Empty(11) if (Search("|. - or - Press ([VISUAL EXIT]) to exit macro and continue editing. - or - Exit VEDIT normally. ") Call("GETDIR") Reg_Set(11,@12) // // Possibly save the user's values into the compdir.vdm macro. // if (#78 && #70) { Buf_Switch(#69) if (Search("| to compare a pair of files at cursor position in top window") Win_Switch(1) Visual() //Show user the results // // Clean up and return. // :EXIT: File_Delete("|@(20)/dir1.out",OK+NOERR) File_Delete("|@(20)/dir2.out",OK+NOERR) File_Delete("|(VEDIT_TEMP)\CMPDIR$.VDM",OK+NOERR) Buf_Switch(#61) if (#61 != #82) { Buf_Quit(OK) } else { Buf_Empty(OK) } Buf_Switch(#62) Buf_Quit(OK) //Close/quit locally used buffers Buf_Switch(#63) Buf_Quit(OK) Buf_Switch(#64) Buf_Quit(OK) Buf_Switch(#65) Buf_Quit(OK) Buf_Switch(#66) Buf_Quit(OK) Buf_Switch(#69) Buf_Quit(OK) Buf_Switch(#82) //Switch back to original buffer Config(F_AUTO_SAVE,#87) //Restore Config() values Config(F_E_F_MACRO,#84) Config(S_E_MORE,#85) Reg_Lock_Macro(#81) //Restore original locked-in macro if (#86) { Visual_Macro(SET+NOMSG) } //Return to Visual Mode; no Pause Screen_Init(ATTACH) //Reset screen Key_Pop(1) //Remove "F12" assignment Reg_Pop(10,21) //Restore original text registers Num_Pop(60,99) //Restore original numeric registers Num_Pop(12,12) //Restore original numeric register Reg_Empty(Macro_Num,EXTRA) //Empty this T-Reg; return Return //Just in case // ///////////////////////////////////////////////////////////////////////////// // // SUBROUTINES // // // GETDIR - the user input dialog for the two directories to compare // :GETDIR: Reg_Empty(16) if (OS_TYPE==1) { repeat (ALL) { //LOOP until valid directory #89=Dialog_Input_1(12,"`Compare two directories`, `|@(16)|@(13)|@(17) `, `[] Include &subdirectories`, `??`,`[&OK]`,`[&Cancel]`", @12,APP+CENTER,0,0) if ((#89==0) || (#89==2)){ Goto Exit } Reg_Set(16,"Directory invalid or doesn't exist. Try again.\n\n") if (Reg_Size(12) < 2) { continue } if (File_Exist(@12,NOERR)) { Break } } } else { repeat (ALL) { //LOOP until valid directory Get_Input(12,@(13),@(12),NOCR) if (File_Exist(@12,NOERR)) { Break } Message("Directory not found. Try again.\n") } if ( Get_Key("subdirectories too? (y/n) ") == 'y') { #12 = 1 } } return() // // SHELLOUT - Shell out, using directory name in @12 and filename in @14. // With long filenames, need to shell out with command such as: // dir "filespec" /one // Else, shell out with command such as: // dir filespec /one // :SHELLOUT: if (Is_Longfilename) { //Need double-quotes around filespec Reg_Set(15,'dir "') Reg_Set(15,@12, APPEND) Reg_Set(15,'"', APPEND) } else { Reg_Set(15,'dir ') Reg_Set(15,@12, APPEND) } if (#12) { //If processing subdirectories... Reg_Set(15,' /s', APPEND) // need the "/s" option } // // Add desired sorting options // if (#75) { Reg_Set(15,' /oen >', APPEND) //Sort by extension and then name } else { Reg_Set(15,' /one >', APPEND) //Sort by name and then extension } // // Add redirection filename; long filename needs double-quotes // if (Is_Longfilename) { //Need double-quotes around filespec Reg_Set(15,' "', APPEND) } Reg_Set(15,@20, APPEND) Reg_Set(15,'\', APPEND) Reg_Set(15,@14, APPEND) if (Is_Longfilename) { //Need double-quotes around filespec Reg_Set(15,'"', APPEND) } // // Shell out with "dir" command to create a sorted list of files: // The syntax with single AND double quotes is necessary because of // long filenames (strange but true) ... // The difference between NT/2000/XP and W9x must be handled carefully! // // T-Reg 15 contains something like: // dir "d:\vedit\user-mac" /one > "C:\Vedit\temp\dir2.out" // or // dir "d:\vedit\user-mac" /one > "C:\DOKUME~1\CHRIST~1.W2\LOKALE~1\Temp\VeditTmp\dir2.out" // dependent on the TEMP path and VEDIT settings regarding temp files. // //if (Is_WinNT) { // Sys('"|@(15)"',DOS+SIMPLE+OK+SUPPRESS) //} else { // Sys('|@(15)',DOS+SIMPLE+OK+SUPPRESS) //} // // Unfortunately the above syntax with Sys("|@(15)...) fails on long strings (~100 characters). // This is an old DOS limitation. // // Fortunately the DOS box itself seems to have no such limit (tested with Win2000 so far). // // So the following commands will work even with long paths: // Reg_Save(15, "|(VEDIT_TEMP)\COMPDIR$.BAT", OK) Sys("|(VEDIT_TEMP)\COMPDIR$.BAT",DOS+SIMPLE+OK+SUPPRESS) File_Delete("|(VEDIT_TEMP)\COMPDIR$.BAT", OK+NOERR) return() // // MAKEDIRLIST - removes header and footer from DIR output // and: subdirectories // and: dir1.out, dir2.out, unique.1, unique.2, same.fil, differ.fil // and: optionally, all *.BAK files // :MAKEDIRLIST: BOF() Search("|{directory|wof,Verzeichnis|wvon}|W",ADVANCE+NOERR) BOL() Del_Line(-ALL) //Delete Header info repeat (ALL) { Search(//,ERRBREAK) //Delete subdirectories Del_Line(0); Del_Line(1) } BOF() repeat (ALL) { Search("dir|{1,2}|{.,|W}out",ERRBREAK) //Delete "dir?.out" Del_Line(0); Del_Line(1) } BOF() repeat (ALL) { Search("unique|{.,|W}|{1,2}",ERRBREAK) //Delete "unique.?" Del_Line(0); Del_Line(1) } BOF() repeat (ALL) { Search("same|{.,|W}fil",ERRBREAK) //Delete "same.fil" Del_Line(0); Del_Line(1) } BOF() repeat (ALL) { Search("differ|{.,|W}fil",ERRBREAK) //Delete "differ.fil" Del_Line(0); Del_Line(1) } if (#79==0) { //If skipping .BAK files... BOF() repeat (ALL) { Search("|{.,|W}BAK|{|N,|B}",ERRBREAK) Del_Line(0); Del_Line(1) } } BOF() repeat (ALL) { Search("|<|{Total Files Listed:,Dateien gesamt:}",ERRBREAK) Del_Line(0); Del_Line(1) } Replace("|< |Y|N","",BEGIN+ALL+NOERR) //Delete info lines beginning with spaces Replace("|<|N","", BEGIN+ALL+NOERR) //Delete empty lines // // Now make the list usable for subdirs too // BOF() //loop through every directory block Search("|{directory|Wof,Verzeichnis|Wvon}|W",NOERR) while (!Error_Match) { Goto_Col(#92+1) //past the root directory name of this search Reg_Copy_Block(17,Cur_Pos, EoL_Pos) //rest of pathname BOL() Del_Line(1) //delete info line while (Match("|[|W]|{directory|Wof|W,verzeichnis|Wvon|W}") != 0) { if (Reg_Size(17) > 0) { Reg_Ins(17) //if it was a subdir: add it in front Ins_Text("\") } Line(1,NOERR+ERRBREAK) if (At_EoF) { break } } if (At_EOF) { Break } } return // // FIXSIZES - removes all commas and dots from filesizes // (US and European format!) // :FIXSIZES: BOF() While(! At_EoF){ Search("|D|D|{.,/,-}|D|D|{.,/,-}|D|D|[|D|D]|W|D|[|D]:",ERRBREAK) // date and time Search("|!|W", REVERSE) //the end of the file size #97=CP Search("|W", REVERSE) //the begin of the file size #98=CP #99=Replace_Block("|{|,,.}","",#98,#97,ALL+NOERR) //remove all commas or dots GP(#98) if (#99 < 3) { //If less than 1,000,000,000... Del_Char(-(3-#99)) //remove extra leading spaces } Line(1) } Return() // // MAXFILENAME - Determine longest filename; filenames start at column 40. // (Only used for NT/2000/XP) // :MAXFILENAME: BOF() while (! AT_EOF) { EOL() if (Cur_Col>#103) { #103=Cur_Col } Line(1,ERRBREAK) } Return() // // FIXNT - Change DIR output from NT/2000/XP to have filename at the beginning. // From: 05/12/1999 04:35p 99,555,661,728 msdosio.asm // To: msdosio.asm 99,555,661,728 05/12/1999 04:35p // :FIXNT: BOF() if ( !(Is_WinNT || (Match("|D|D|{/,.}|D|D|{/,.}|D|D")==0)) ) { return } //Return if not WinNT or so formatted // // If any filenames longer than 40 character, only pad other names out to // 40 columns. Note: need "+40" because NT filename starts in column 40. // if (#12==0 && (#103 > 40+40)) { #103 = 40+40 } if (#103 < 13+40) { #103 = 13+40 } // // Pad filenames to length of longest name; minimum of 13, max of 40. // BOF() while (! At_EOF) { EOL() if (Cur_Col < #103+1) { Ins_Char(' ',COUNT,#103-Cur_Col+1) } Line(1,ERRBREAK) } // // Change WinXP "AM/PM" format to WinNT/2000 "a/p" format. // Replace("{[0-9]:[0-9][0-9]}\s[aA][mM]\s","\1a\s",BEGIN+REGEXP+ALL+NOERR) Replace("{[0-9]:[0-9][0-9]}\s[pP][mM]\s","\1p\s",BEGIN+REGEXP+ALL+NOERR) // // Change order of fields so that filename is at the beginning. // Remove 6 spaces preceding the filesize. // [0-9/.]+ - matches the date, e.g. 10/13/2002 or 13.10.2002 // [0-9:]+[ap ] - matches the time, e.g. 11:45a or 03:15p or 15:15 // (Note that Europeans don't use "a" or "p") // \s\s\s\s\s\s - there are six spaces between time and the max file-size // \s+[0-9,.]+ - matches the file-size, e.g. " 10,456,432" // Replace("{[0-9/.]+\s+[0-9:]+[ap ]}\s\s\s\s\s\s{\s+[0-9,.]+}\s{.+}$","\3\2 \1",BEGIN+REGEXP+ALL+NOERR) Return() // // GETFILE - Implements to compare the selected filenames. // Get the filename listed in the current line // and open a new VEDIT with those two files (in different // directories) and start COMPARE.VDM then. // :GETFILE: if (Buf_Num != #61) { //must be in the different files list Return } BOL() if (!Is_WinNT) { //getting filename (inclusive optional path) if (Cur_Pos == Eol_Pos) { return } Search_Block("\",BOL_Pos, EOL_Pos,BEGIN+REVERSE+ADVANCE+NOERR) if (Error_Match) { //no path there BOL() Search(" ") Reg_Copy(17,0) Match("|w",ADVANCE) if (Cur_Col==10) { Reg_Set(17,/./,APPEND) //Set Reg[17] = "fn.ext" Reg_Copy_Block(17,Cur_Pos,Cur_Pos+3,APPEND) } } else { //path there Search(" ") Reg_Copy(17,0) Match("|w",ADVANCE) //at ext or at filesize (no ext. existing) Set_Marker(0, Cur_Pos) Search(" ") if (Match(" ")==0) { //on ext Reg_Set(17,".",APPEND) //Set Reg[17] = "fn.ext" Reg_Copy_Block(17,Marker(0),Marker(0)+3,APPEND) } } } else { //NT and W2k have leading long filenames Search_Block("|D|D|{.,/,-}|D|D|{.,/,-}|D|D|[|D|D]|W|D|[|D]:",BOL_Pos,EOL_Pos,NOERR) //the date and time if (Error_Match) { Return } Search("|!|W", REVERSE) //the end of the file size Search("|W", REVERSE) //the begin of the file size Search("|!|W", REVERSE+ADVANCE) //the end of the file name Reg_Copy(17,0) //Reg[17] = fname ext } if (Reg_Size(17) > 0 ) { // // Workaround for the limitations of the command line // Out_Reg(19) if (OS_TYPE==1) { Message('Win_Move(APP, ') //move the new VEDIT some pixels down and right Num_Type(App_X_Org+20, LEFT+NOCR) //to make it obvious that it's a second instance Message(', ') Num_Type(App_Y_Org+20, LEFT+NOCR) Message(', APPW, APPH) ') } Message('File_Open("') //Open the two files to compare Reg_Type(10) Message('\') Reg_Type(17) Message('") File_Open("') Reg_Type(11) Message('\') Reg_Type(17) Message('") Buf_Switch(1) ') Message('N_Option(1) Chain_File(100,"|(MACRO)\\compare.vdm")') //Call the compare macro with disabled input dialog Out_Reg(CLEAR) Reg_Save(19,"|(VEDIT_TEMP)\CMPDIR$.VDM", OK) Sys("|(VEDIT_EXE) -w -x |(VEDIT_TEMP)\CMPDIR$.VDM ",NOWAIT+NOMSG) } return()