//
// 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