// // DIR-FDOUBLE-W.VDM Christian Ziemski 09.03.2004 // 18.03.2004 // // Note: This version uses an extra window for the online help and // will be no longer developed: // The new version with a DI_1() help is the way I'll go: DIR-FDOUBLE.VDM // // // // Only for WINNT and above yet (due to DIR format) // (And only tested with German Windows yet.) // // //----- Online documentation (via Shift-F1) --------------------------------------- // // // DIR-FDOUBLE Helper tool for finding double files (C) Ch.Ziemski 3/2004 // =========================================================================== // // It lists all files with double filenames found in a directory tree. // Data shown: date, time, size and path. // // Provides these commands via key assignment to work with the files: // // F12 - open file at cursor line // - close the file again // // Shft-F12 - open files with same name in and around cursor line // - closes all same named files again // // Ctrl-F12 compare files with same name in and around cursor line // (And marks them with A..J to show diffs) // // Ctrl-F10 - toggle DELETE flag of file in current cursor line // - toggle DELETE flag of current file in list // Ctrl-Shft-F10 ... and advance one line // // Alt-F10 delete non double filenames (req. by-name sorted list) // // F11 sort list by filename // Shft-F11 sort list by path // Ctrl-F11 sort list by DELETE flag (at EoF) // Alt-F11 sort list by size // // // //---------------------------------------------------------------------------------- // // To do: // // - support more Windows versions // - input parameter for drive/path as alternative for DI_1() // - more comfort and tools // - Tools menu additional to the key assignments // - UNC pathnames? how to check Cur_Col then? // - Win_Split()/Win_Create() error handling // //-------------------------------------------------------------------------- // // #90 reg num of running macro // #91 file list loaded into a free buffer // #92 win_width // #93 win_height // #94 flag for formatting empty lines in filename-sort // // #103 // #104 // #105 // // @103 // @104 // @105 // @106 //-------------------------------------------------------------------------- if ( ! Is_Windows) { Statline_Message("Sorry, this macro requires VEDIT for Windows.") return } #94=1 // insert an empty line after each filname-block in name sort Key_Add("Shft-F1" , "[VISUAL EXIT] Call(#90, 'HELP')",OK) Key_Add("F11" , "[VISUAL EXIT] Call(#90, 'SORTBYNAME') if (#94){Call(#90, 'INSERTDIVIDER')}", OK) Key_Add("Shft-F11", "[VISUAL EXIT] Call(#90, 'SORTBYPATH')", OK) Key_Add("Ctrl-F11", "[VISUAL EXIT] Call(#90, 'SORTBYFLAG')", OK) Key_Add("Alt-F11" , "[VISUAL EXIT] Call(#90, 'SORTBYSIZE')", OK) Key_Add("F12" , "[VISUAL EXIT] Call(#90, 'OPENFILE')", OK) Key_Add("Shft-F12", "[VISUAL EXIT] Call(#90, 'OPENFILES')", OK) Key_Add("Ctrl-F12", "[VISUAL EXIT] Call(#90, 'COMPAREFILES')", OK) Key_Add("Ctrl-F10" , "[VISUAL EXIT] #104=0 Call(#90, 'TOGGLEDEL')", OK) Key_Add("Ctrl-Shft-F10", "[VISUAL EXIT] #104=1 Call(#90, 'TOGGLEDEL')", OK) Key_Add("Alt-F10" , "[VISUAL EXIT] Call(#90, 'DELNONDOUBLE')", OK) #103=File_Check("|(VEDIT_TEMP)/dir.out") if (#103 != -1) { Buf_Switch(#103) Buf_Quit(OK) Update } #90 = Macro_Num //#90 = reg# of running macro Reg_Set(103, HOME) // default directory name Call("GETDIR") Config( D_H_CR_LINE, "Highlight cursor line (1=Full, 2=Partial)", 1 ) if (File_Size > 0) { Buf_Switch(Buf_Free) } #91=Buf_Num #92=Win_Width #93=Win_Height Reg_Set(104, "|(VEDIT_TEMP)/dir.out") // save into this target file Message("getting directory listing...", STATLINE) Call("SHELLOUT") // Shell out with "dir" command to create DIR.OUT Message("processing directory listing...", STATLINE) Buf_Switch(#91) File_Open("|(VEDIT_TEMP)/dir.out",NOEVENT) Update Call("MAKEDIRLIST") // Clean up "dir" output Update Call("LINEUP") // format the list with equally sized file names Update Call("SORTBYNAME") // sort list according file names Update Call("DELNONDOUBLE") // delete lines with single files Update if (#94) { Call("INSERTDIVIDER") } return ///////////////////////////////////////////////////////////////////////////// // // SUBROUTINES // //--------------------------------------------------------------------------- // // LINEUP // :LINEUP: #103=0 BoF while (! At_EOF) { Search("::", NOERR+ERRBREAK) #103 = Max(#103, Cur_Col) Line(1, NOERR) } BoF while (! At_EOF) { Search("::", NOERR+ERRBREAK) Del_Char(2) Ins_Char(32, COUNT, #103-Cur_Col+4) } return // -------------------------------------------------------------------------- // // DELNONDOUBLE // :DELNONDOUBLE: // delete lines with non double filenames in a sorted text BoF Search(" |A:\", NOERR) if (EM) { return } BB(CLEAR) #103=Cur_Col-42 // a character before the date column BoF RCB(104, BoL_Pos, BoL_Pos+#103) Line(1, NOERR) #104=1 repeat (ALL) { if (Match(@104)==0) { #104++ } else { if (#104==1) { Del_Line(-1) } else { #104=1 } RCB(104, BoL_Pos, BoL_Pos+#103) } Line(1, NOERR+ERRBREAK) } BoF return // -------------------------------------------------------------------------- // // // SHELLOUT - Shell out, using directory name in @103 and target filename in @104 // 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(105,'dir "') Reg_Set(105,@103, APPEND) Reg_Set(105,'"', APPEND) } else { Reg_Set(105,'dir ') Reg_Set(105,@103, APPEND) } Reg_Set(105,' /s', APPEND) // need the "/s" option Reg_Set(105,' /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(105,' "', APPEND) } Reg_Set(105, @104, APPEND) if (Is_Longfilename) { //Need double-quotes around filespec Reg_Set(105,'"', 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! // if (Is_WinNT) { Sys('"|@(105)"',DOS+SIMPLE+OK+SUPPRESS) } else { Sys('|@(105)',DOS+SIMPLE+OK+SUPPRESS) } return() // -------------------------------------------------------------------------- // // MAKEDIRLIST - removes: - header and footer // - subdirectory entries // from DIR output (in English and German Windows) // :MAKEDIRLIST: BOF() Search("|{directory|wof,Verzeichnis|wvon}|W",ADVANCE+NOERR) BOL() Del_Line(-ALL) //Delete Header info Replace("^.*.*\N", "", REGEXP+BEGIN+ALL+CASE+NOERR) 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 do the subdirs // BOF() //loop through every directory block Search("|{directory|Wof,Verzeichnis|Wvon}|W",ADVANCE+NOERR) while (!Error_Match) { Reg_Copy_Block(17,Cur_Pos, EoL_Pos) //complete pathname BOL() Del_Line(1) //delete info line if (At_EOF) { Break } while (Match("|[|W]|{directory|Wof|W,verzeichnis|Wvon|W}") != 0) { Goto_Col(40) // ? Reg_Copy_Block(18, Cur_Pos, EoL_Pos) Ins_Text(" ") Reg_Ins(17) Ins_Text("\") BoL Reg_Ins(18) Ins_Text(" :: ") Line(1,NOERR+ERRBREAK) if (At_EoF) { break } } Char(Chars_Matched) if (At_EOF) { Break } } return // -------------------------------------------------------------------------- // // OPENFILE - opens the file in the current cursor line // (if in buffer with file list; // else close current file and switch to list) :OPENFILE: if (Buf_Num == #91) { Save_Pos() Search_Block(" |A:\", BoL_Pos, EoL_Pos, NOERR) if (EM) { Restore_Pos() return } Reg_Copy_Block(106, Cur_Pos+1, EoL_Pos) Restore_Pos() File_Open("|@(106)") } else { Buf_Quit(DELETE) Buf_Switch(#91) // Buf_Switch(#91, ATTACH) // Win_Switch(Win_Next(BUFFER)) // ??? // #77=Win_Num // Win_Move(Win_Num,0,0,#92, #93) // geht so nicht!? Falsches Window wird vergrössert. // Win_Move(#91,0,0,#92, #93) // unsauber } return // -------------------------------------------------------------------------- // // OPENFILES - opens the files with the same filename like the file in the // current cursor line (list must be sorted by filename). // (if in buffer with file list; // else try to close current file and all with that name // and then switch to list) // :OPENFILES: if (Buf_Num == #91) { Save_Pos() Search_Block(" |A:\", BoL_Pos, EoL_Pos, NOERR) if (EM) { Restore_Pos() return } Char(-42) #103=Cur_Col // a character before the date column Search("|!|W", REVERSE+ADVANCE) Reg_Copy_Block(105, BoL_Pos, Cur_Pos) // filename Line(-1, NOERR) if (! EM) { while (Match(@105)==0) { Line(-1, NOERR+ERRBREAK) } if (! At_BoF) { Line(1) } } #104=-1 // # of first new opened buffer Block_Begin(Cur_Pos) while (Match(@105)==0) { Goto_Col(#103+43) Reg_Copy_Block(106, Cur_Pos, EoL_Pos) if (File_Check(@106) == -1) { #105=Buf_Free if (#104==-1) { //Win_Split(#105, Win_Lines-10, BOTTOM) Win_Move(Win_Num, Win_X_Org, Win_Y_Org, Win_Width, 10*Font_Height) Win_Create(#105, Win_Y_Org+10*Font_Height, Win_X_Org, #93-10*Font_Height, #92, PIXEL) } else { Win_Split(#105, 0, RIGHT) // problematic if not enough screen width } Buf_Switch(#105) } File_Open("|@(106)") if (#104==-1) { #104=Buf_Num } Win_Attach(#105) Win_Switch(#105) Update // why necessary? Buf_Switch(#91) Line(1, NOERR+ERRBREAK) } Block_End(Cur_Pos) Restore_Pos() Set_Visual_Line(0) Buf_Switch(#104) } else { Reg_Set(103, FILENAME) if (Reg_Size(103)>0) { #103=1 do { Buf_Switch(#103) if (#103 != #91) { if (Buf_Status(#103) > -1) { Reg_Set(104, FILENAME) if (Reg_Compare(103, @(104)) == 0) { Buf_Quit(DELETE) // incl. delete attached windows } } } #104=#103 #103 = Buf_Next } while(#103 > #104) } Buf_Switch(#91) //Win_Move(Win_Num,0,0,(Screen_Cols+1)*Font_Width, (Screen_Lines+1)*Font_Height) Win_Move(Win_Num,0,0,#92, #93) } return // -------------------------------------------------------------------------- // // SORTBYNAME - sort the list by filename // :SORTBYNAME: if (Buf_Num == #91) { Replace("|<|[|W]|N", "", BEGIN+ALL+NOERR) // remove empty lines BoF Search(" |A:\", NOERR) if (EM) { return } BB(CLEAR) #103=Cur_Col-42 // a character before the date column Sort_Merge("1,#103", 0, FileSize) BoF } return // -------------------------------------------------------------------------- // // SORTBYPATH - sort the list by pathname // :SORTBYPATH: if (Buf_Num == #91) { Replace("|<|[|W]|N", "", BEGIN+ALL+NOERR) // remove empty lines Message("Processing... (About 30 sec / Megabyte) -- Press to abort ",STATLINE) #104=0 Begin_Of_File() while (! AT_EOF) { EOL() #104=Max(#104, Cur_Col) Line(1,ERRBREAK) } BoF Search(" |A:\", NOERR) if (EM) { return } BB(CLEAR) Sort_Merge("Cur_Col+1,#104", 0, FileSize) BoF } return // -------------------------------------------------------------------------- // // SORTBYSIZE - sort the list by filesize // :SORTBYSIZE: if (Buf_Num == #91) { Replace("|<|[|W]|N", "", BEGIN+ALL+NOERR) // remove empty lines BoF Search(" |A:\", NOERR) if (EM) { return } BB(CLEAR) Search("|!|W", REVERSE) #104=Cur_Col+1 Search("|W", REVERSE) Search("|!|W", REVERSE) #103=Cur_Col+1 Sort_Merge("#103,#104", 0, FileSize) BoF } return // -------------------------------------------------------------------------- // // SORTBYFLAG - sort the list by flag and pathname // :SORTBYFLAG: if (Buf_Num == #91) { Replace("|<|[|W]|N", "", BEGIN+ALL+NOERR) // remove empty lines Message("Processing... (About 30 sec / Megabyte) -- Press to abort ",STATLINE) #104=0 Begin_Of_File() while (! AT_EOF) { EOL() #104=Max(#104, Cur_Col) Line(1,ERRBREAK) } BoF Search(" |A:\", NOERR) if (EM) { return } BB(CLEAR) Sort_Merge("Cur_Col-1,#104", 0, FileSize) EoF } return // -------------------------------------------------------------------------- // // INSERTDIVIDER - inserts an empty line after each filename-group // :INSERTDIVIDER: Replace("|<|[|W]|N", "", BEGIN+ALL+NOERR) // remove empty lines BoF Search_Block(" |A:\", BoL_Pos, EoL_Pos, NOERR) if (EM) { return } Char(-42) #103=Cur_Col // a character before the date column while (! At_EoF) { Search("|!|W", REVERSE+ADVANCE) Reg_Copy_Block(105, BoL_Pos, Cur_Pos) // filename BoL while (Match("|@(105) ") == 0) { Line(1, NOERR+ERRBREAK) } Ins_Newline(1) Goto_Col(#103) } BoF return // -------------------------------------------------------------------------- // // TOGGLEDEL - toggle the DELETE-Flag // if in list: toggle file in cursor line // if in open file: toggle that file in list // :TOGGLEDEL: if (Buf_Num == #91) { Save_Pos() BoL Search(" |A:\", NOERR) if (EM) { Restore_Pos() return } Char(-1) if (Match("#") == 0) { Ins_Char(32, OVERWRITE) } else { Ins_Char(35, OVERWRITE) } Restore_Pos() if(#104==1) { Do_Visual("\CD\") } } else { #103=Buf_Num Reg_Set(103, PATHNAME) Buf_Switch(#91) Search(@103, BEGIN+NOERR) if (EM) { Buf_Switch(#103) return } Char(-2) if (Match("#") == 0) { Ins_Char(32, OVERWRITE) } else { Ins_Char(35, OVERWRITE) } Buf_Switch(#103) } return // -------------------------------------------------------------------------- // // GETDIR - user input dialog for the directory to be checked // // to do: more than 10 files :GETDIR: Reg_Set(104, "Please enter the directory to be checked (including it's subdirectories)") repeat (ALL) { //LOOP until valid directory #103=Dialog_Input_1(103,"`Double File Checker`, `|@(104) `, `??`,`[&OK]`, `[&Help]`, `[&Cancel]`", @103,APP+CENTER,0,0) if ((#103 == 0) || (#103 > 2)) { Break_Out(EXTRA) } if (#103 == 2) { Call("HELP") Continue } Reg_Set(104,"Directory invalid or doesn't exist. Try again.") if (Reg_Size(103) < 2) { continue } if (File_Exist(@103,NOERR) != 0) { Break } } return() // -------------------------------------------------------------------------- // // COMPAREFILES // :COMPAREFILES: if (Buf_Num == #91) { Save_Pos() Search_Block(" |A:\", BoL_Pos, EoL_Pos, NOERR) if (EM) { Restore_Pos() return } Char(-42) #103=Cur_Col // a character before the date column Search("|!|W", REVERSE+ADVANCE) Reg_Copy_Block(105, BoL_Pos, Cur_Pos) // filename Line(-1, NOERR) if (! EM) { while (Match(@105)==0) { Line(-1, NOERR+ERRBREAK) } if (! At_BoF) { Line(1) } } #97=70 // number of same files + 69 while (Match(@105)==0) { Goto_Col(#103+43) Reg_Copy_Block(106, Cur_Pos, EoL_Pos) File_Open("|@(106)", NOEVENT) #@97=Buf_Num // save buffer numbers of opened files in #70-#79 // to do: mark already open files with +1000 or so Buf_Switch(#91) Line(1, NOERR+ERRBREAK) #97++ if (#97 > 79) { // only 10 files yet break } } #97-- // max reg. if (#97 == 70) { // if only one file Restore_Pos() return } for (#104=80; #104<=(#97+10); #104++) { #@104=#104-80 // set #80-#89 to 0..9 } #98=70 #99=71 repeat (ALL) { repeat (ALL) { #95=#@98 #96=#@99 // .#98 #99 Buf_Switch(#95) BoF #106=File_Size Buf_Switch(#96) BoF if (#106 == File_Size) { // if same size if (Compare(#95+BUFFER, CASE) == 0) { // if at least one character matched #106=0 if (!At_EoF) { // not all identical #106=1 } } else { #106=1 } } else { #106=1 } if (#106 == 0) { // if identical #104=#99+10 #105=#98+10 #@104=#@105 // set to 0..9 from 1.st field } #99++ // 2nd pointer++ if (#99 > #97) { #99=#98+2 break } } #98++ // 1st pointer++ if (#98 == #97) { break } } for (#104=70; #104<=#97; #104++) { // mark in list and close all compared files // to do: let opened files open Buf_Switch(#@104) Reg_Set(104, PATHNAME) Buf_Switch(#91) Search(@104, BEGIN) Char(-3) #105=#104+10 Ins_Char(#@105+65, OVERWRITE) Buf_Switch(#@104) Buf_Quit(OK) } Restore_Pos() Buf_Switch(#91) } return // -------------------------------------------------------------------------- // HELP - Show some help instructions (the inline doco from top of the macro) :HELP: Config(CM_E_UNDO,0) Num_Push(103,107) // save used registers Reg_Push(103,106) #107=Config( D_H_CR_LINE, "Highlight cursor line (1=Full, 2=Partial)", 0 ) Win_Clear Key_Purge() #103=Buf_Num Buf_Switch(Buf_Free(EXTRA)) Reg_Ins(Macro_Num) BoF Search("//|[|W]|H3CDOC>", NOERR+ADVANCE) // the leading "<" is masked out intentionally! if(EM){ Message("\n\n No inline help found.") }else{ Line(1) Del_Line(-ALL) Search("//|[|W]|H3C/DOC>", NOERR) if(!EM){ BoL Del_Block(Cur_Pos, File_Size) } Replace("^/+", BEGIN+ALL+REGEXP+NOERR) // remove leading comment characters BoF while(! At_EoF){ // shorten lines longer than window width (not perfect, but ...) Goto_Col(Win_Cols) Del_Block(Cur_Pos, EoL_Pos) Line(1, NOERR+ERRBREAK) } EoF #106=Cur_Line - 1 // number of lines to display #105=Win_Lines - 1 // number of lines displayable in the window BoF repeat(ALL){ Win_Clear Type(#105, NORESTORE) if(Cur_Line >= #106){ break } #104=Get_Key("\nPress any key to continue or to quit ...", STATLINE+RAW) if (#104 == 27) { break } } } Update Buf_Quit(OK) Buf_Switch(#103) Visual_Macro(NOMSG) if(#104 != 27){ Get_Key("\nPress any key to close help...", STATLINE+RAW) } Update Config( D_H_CR_LINE, "Highlight cursor line (1=Full, 2=Partial)", #107 ) Reg_Pop(103,106) // restore used registers Num_Pop(103,107) return