// // FILEVERS.VDM Ch.Ziemski 24.10.2002 // 31.10.2002 // 03.11.2002 compare feature added // parameter 'C' changed to 'S' // some enhancements // 06.11.2002 Cleanup feature added (beta!) // new dialog design // 23.11.2002 fix: didn't save config // 03.12.2002 fix: didn't load *big* old file versions // 26.10.2004 fix: missing double quotes in File_Open() // // File-Generation (File-Version-System) // // - Save current file as new version (name generated automatically) // - Load old version of the currently loaded file // - Delete old versions of the file // - Compare current file with an old version // - Customize the behavior via dialog // // // Usage: // // Have a file opened in VEDIT and call the macro // via {Misc, Load/Exec Macro} // or // via an entry in the {User} or {Tools} menu // // If called via {User} or {Tools} menu you can control it's behavior: // // Call_File(100, "FILEVERS.VDM") starts interactive with dialog // #120='S' Call_File(100, "FILEVERS.VDM") (S)aves a new file version silently // #120='G' Call_File(100, "FILEVERS.VDM") (G)ets an older version back // // // Some additional options can be modified in the "Customize" dialog. // //-------------------- // // Register usage: // // $90 name of the versions directory // $91 FILENAME current filename without path, incl. extension // $92 PATHONLY " " path only // $93 FILE_ONLY " " without extension // $94 EXT_ONLY " " extension // $95 storage for new filename // $96 new version number as string // $97 // $98 name of this macro // // #90 flag for different types of version-number/timestamp, see below // #91 flag for starting behavior, see above // #92 T-Reg# containing this macro // #93 temp. buffer # // #94 temp. buffer # // #95 temp. buffer # //------------------------------------------------------------------------- // - The macro returns without action if the current buffer has no filename. // - Due to the used Dialog_Input_1() command the macro only runs with the // Windows version of VEDIT. // - The compare feature uses a temporary copy of the current file and opens // both files in browse mode. So there are no direct modifications to the // file possible. (It's not a bug - it's a feature.) // //------------------------------------------------------------------------- // //>>CONFIG-BEGIN---------- Attention: Avoid trailing spaces here! --- // // // -- Name of this macro itself. // -- Please change it manually here if you rename the macro file. // // MYNAME: FILEVERS.VDM // // // -- The following two options can be changed interactively in the dialog // // -- Name of the versions-directory (subdirectory). // -- If necessary it is created in the current directory. // -- // -- If you want to have the version files saved in the // -- current directory, leave this entry empty. // // VERSIONDIR: !versions // // // -- Type of the version-stamp // // -- "1" to use version# filename.ext__nnn // -- "2" to use version# filename__nnn.ext // -- "3" to use timestamp filename.ext__yyyy-mm-dd_hh-mm-ss // // VERSIONTYPE: 1 // // //>>CONFIG-END------------------------------------------------------------- if( ! Is_Win32_Version){ Get_Key("Sorry, but this macro only runs with the Win32-version of VEDIT!") return } Reg_Push(90,98) // save the used registers Num_Push(90,95) #92 = Macro_Num // T-Reg containing this macro #91=#120 // save the parameter from call: interactive run or not #120=0 // reset the parameter Call("READCONFIG") Reg_Set(91, FILENAME) // the currently loaded file Reg_Set(92, PATH_ONLY) Reg_Set(93, FILE_ONLY) Reg_Set(94, EXT_ONLY) if(Reg_Size(91)==0){ // no action if the buffer has no filename Goto CLEANUP+EXIT } Call("CHECKDIR") // check whether the versions directory exists or create it if(Return_Value != 0){ // if it couldn't be created Goto CLEANUP+EXIT } Call("BUILDVERSION") // build a new filename with incremented version if(Return_Value != 0){ // if not possible Goto CLEANUP+EXIT } if(#91 == 'S'){ // if macro was called "not interactively" to save a version silently Block_Save_As("|@(92)/|@(90)/|@(121)", 0, File_Size) // save with timestamp or version# into version directory Statline_Message("Version created: |@(96)") Goto CLEANUP+EXIT } if(#91 == 'G'){ // if macro was called "not interactively" to get an old version back Call("GETOLDVERSION") Goto CLEANUP+EXIT } //------- otherwise: the dialog -------- while(1==1){ // "endless" loop until user cancels it Reg_Set(121, @95) // get new filename from save place #104=Dialog_Input_1(121,^`|@(98) - File Version Generator`, `.g.lThe current file can now be versionized as`, `??`, `.l[&Save]`, `.\.h.l Saves file in: "|@(92)\|@(90)"`, `.v.l[&Get old]`, `.h.l Restores one of the previously saved versions`, `.l[Com&pare]`, `.h.l Compares the current with an old version`, `.l[&Delete old]`, `.h.l Deletes an old version`, `.l[Cleanup]`, `.h.l Removes doubles found in old versions`, `.c---------------------------------------------------------------------------------------------------`, `.l[Customi&ze]`, `.h.l Lets you customize the macro`, `.v.r[&Cancel]`^, APP+CENTER+SET,0,0) if(#104==0 || #104==7){ Goto CLEANUP+EXIT } if(#104==1){ Block_Save_As("|@(92)/|@(90)/|@(121)", 0, File_Size) // save with timestamp or version# into version directory break } if(#104==2){ // get old version Call("GETOLDVERSION") if(Return_Value == 0){ break } } if(#104==3){ // compare with old version Call("COMPAREVER") } if(#104==4){ // delete old version Call("DELOLDVER") Call("BUILDVERSION") } if(#104==5){ // remove doubles Call("REMOVEDOUBLES") Call("BUILDVERSION") } if(#104==6){ // customize #106=1 while(#106 > 0){ // loop until error-free Call("CUSTOMIZE") if(Return_Value == 0){ Call("BUILDVERSION") if(Return_Value == 0){ #106=0 }else{ #106=9 } } } if(#106==9){ // if error building a version // o.k.??? break } } } //---------- :CLEANUP+EXIT: Num_Pop(90,95) // restore the used registers Reg_Pop(90,98) return //======================= end of main program ========================= // ------------------- here are the subroutines -------------------- :BUILDVERSION: #103=Buf_Num Buf_Switch(Buf_Free(EXTRA)) if(#90==3){ // ----- use timestamp filename.ext__yyyy-mm-dd_hh-mm-ss Reg_Ins(91) // check whether filename already has a timestamp Search("__|D|D|D|D-|D|D-|D|D_|D|D.|D|D.|D|D", BEGIN+NOERR) if(!EM){ #106=Dialog_Input_1(106,"`Error!`, `The file already is a versionized one!`, `It's not possible to do that again.`, `[&Ok]`",APP+CENTER,0,0) Buf_Quit(OK) Buf_Switch(#103) return(1) } Buf_Empty(OK) Out_Ins() // create current timestamp Date(NOMSG+NOCR) Ins_Text("_") Time(NOMSG+NOCR) Out_Ins(CLEAR) BoF Char(5) Del_Char(1) // delete divider Reg_Copy_Block(105, CP, CP+4, DELETE) // get year BoF Ins_Text("__") Reg_Ins(105) // year in front Ins_Text("-") Replace(":",".",BEGIN+ALL) Reg_Copy_Block(105, 0, 21) // date and time in format: __yyyy-mm-dd_hh.mm.ss Reg_Copy_Block(96, 13, 21) // only the time Reg_Set(96, "...", INSERT) Reg_Set(121, @91) Reg_Set(121, @105, APPEND) // filename+timestamp }else{ if(#90==1){ // ----- use version# filename.ext__nnn Reg_Ins(91) // check whether filename already has a timestamp Search("__|D|D|D", BEGIN+NOERR) if(!EM){ #106=Dialog_Input_1(106,"`Error!`, `The file already is a versionized one!`, `It's not possible to do that again.`, `[&Ok]`",APP+CENTER,0,0) Buf_Quit(OK) Buf_Switch(#103) return(1) } Buf_Empty(OK) Out_Ins() // create current version# #105=Dir("|@(92)/|@(90)/|@(91)__*", NOMSG+SUPPRESS+NOERR) Out_Ins(CLEAR) if(#105 > 0){ BoF Replace("|@(91)__","", BEGIN+ALL+NOERR) EoF Line(-1) #105=Num_Eval(SUPPRESS) } Buf_Empty(OK) #105++ Num_Ins(#105, FILL+NOCR) BoF Reg_Copy_Block(96, 2, 5) // save number as string Ins_Text("__", OVERWRITE) // ignore the first two digits and insert divider Reg_Set(121, @91) Reg_Copy_Block(121, 0, CP+5 , APPEND) // filename+version# }else{ if(#90==2){ // ----- use version# filename_nnn.ext Reg_Ins(93) // check whether filename already has a timestamp Search("__|D|D|D", BEGIN+NOERR) if(!EM){ #106=Dialog_Input_1(106,"`Error!`, `The file already is a versionized one!`, `It's not possible to do that again.`, `[&Ok]`",APP+CENTER,0,0) Buf_Quit(OK) Buf_Switch(#103) return(1) } Buf_Empty(OK) Out_Ins() // create current version# #105=Dir("|@(92)/|@(90)/|@(93)__???.|@(94)", NOMSG+SUPPRESS+NOERR) Out_Ins(CLEAR) if(#105 > 0){ BoF Replace("|@(93)__","", BEGIN+ALL+NOERR) Replace(".|@(94)","", BEGIN+ALL+NOERR) EoF Line(-1) #105=Num_Eval(SUPPRESS) } Buf_Empty(OK) #105++ Num_Ins(#105, FILL+NOCR) BoF Reg_Copy_Block(96, 2, 5) // save number as string Ins_Text("__", OVERWRITE) // ignore the first two digits and insert divider Reg_Set(121, @93) Reg_Copy_Block(121, 0, CP+5 , APPEND) // filename+version#+extension if(Reg_Size(94)>0){ // if there is an extension: Reg_Set(121, ".", APPEND) // append it again Reg_Set(121, @94, APPEND) } } } } Reg_Set(95, @121) // save new name for future use Buf_Quit(OK) Buf_Switch(#103) return(0) //------------------------------------------------------------------------ :GETOLDVERSION: ChDir(@92, NOMSG) if(#90 == 2){ #106 = Get_Filename(121,"|@(90)/|@(93)__*.|@(94)", FORCE) }else{ #106 = Get_Filename(121,"|@(90)/|@(91)__*", FORCE) } if (#106 == 0) { return(1) } if(Is_Altered){ #106=Dialog_Input_1(106,"`Attention!`, `Current buffer is modified but not saved!`, `Should it be overwritten by the selected file`, `.\|@(121)`, `[&Yes]`,`[&No]`",APP+CENTER,0,0) if(#106 != 1){ return(1) } } Buf_Empty(OK) File_Save_As("|@(91)", OK+NOMSG) Ins_File('|@(121)') BoF File_Save(NOMSG) Return(0) // -------------------------------------------------------------------------- :DELOLDVER: ChDir(@92, NOMSG) if(#90==2){ #106 = Get_Filename(121,"|@(90)/|@(93)__*.|@(94)",FORCE) }else{ #106 = Get_Filename(121,"|@(90)/|@(91)__*",FORCE) } if (#106 == 0) { return } #106=Dialog_Input_1(106,"`Attention!`, `Do you really want to delete the file`, `.\|@(121)`, `[&Yes]`,`[&No]`",APP+CENTER,0,0) if(#106 == 1){ File_Delete("|@(121)", OK) } return // -------------------------------------------------------------------------- :REMOVEDOUBLES: #106=Dialog_Input_1(106,"`Info!`, `This feature is in beta-status!`, `Do you really want to execute it?`, `[&Yes]`, `[&No]`",APP+CENTER,0,0) if(#106 != 1){ return } ChDir("|@(92)\|@(90)", NOMSG) #103=Buf_Num #93=Buf_Switch(Buf_Free(EXTRA)) // buffer for list of files Out_Ins() if(#90 == 2){ #104=Dir("|@(93)__???|@(94)", NOMSG+SUPPRESS+NOERR) }else{ #104=Dir("|@(91)__*", NOMSG+SUPPRESS+NOERR) } Out_Ins(CLEAR) #94=Buf_Switch(Buf_Free(EXTRA)) // buffer for 1st file to compare Ins_Text(" ") // to set it "in use" #95=Buf_Free(EXTRA) // buffer for 2nd file Buf_Switch(#93) BoF if(#104 > 1){ #105=1 // line 1 while(! At_EoF){ Buf_Switch(#93) Goto_Line(#105) Reg_Copy_Block(104, CP, EoL_Pos) // Get 1st filename while(! At_EoF){ // check all following lines/files Line(1, NOERR+ERRBREAK) if(At_EoF){ break } Reg_Copy_Block(105, CP, EoL_Pos) // Get 2nd filename Buf_Switch(#94) Buf_Empty(OK) //File_Open(@104, FORCE) // doesn't work due to known Compare()-Bug Ins_File("|@(104)") // but workaround only works for small files!!!!! BoF Buf_Switch(#95) //File_Open(@105, FORCE) // doesn't work due to known Compare()-Bug Ins_File("|@(105)") BoF // Compare current buffer with the other one Compare(#94+BUFFER) if(Return_Value == 0){ // if identical #106=Dialog_Input_1(106,"`Attention!`, `File |@(104) is the reference.`, `File |@(105) is identical to the reference.`, `Should the identical file |@(105) be deleted?`, `[&Yes]`,`[&No]`",APP+CENTER,0,0) if(#106 == 1){ File_Delete("|@(105)", OK) Buf_Switch(#93) Del_Line(1) // oder hier die Zeile verändern (deaktivieren) Line(-1, NOERR) // und später diese Liste anzeigen zum Löschen? Buf_Switch(#95) } } Buf_Empty(OK) Buf_Switch(#94) Buf_Empty(OK) Buf_Switch(#93) } #105++ // counter for next line for 1st filename if(#105 >= #104){ break } BoF } } Buf_Switch(#94) Buf_Quit(OK) Buf_Switch(#94) Buf_Quit(OK) Buf_Switch(#93) Buf_Quit(OK) Buf_Switch(#103) return // -------------------------------------------------------------------------- :COMPAREVER: ChDir(@92, NOMSG) // get a filename of a saved version if(#90==2){ #106 = Get_Filename(121,"|@(90)/|@(93)__*.|@(94)",FORCE) }else{ #106 = Get_Filename(121,"|@(90)/|@(91)__*",FORCE) } if (#106 == 0) { return } // if(Buf_Num == 1 && Buf_Total == 1){ // if there is only ONE file open (the current file) // BoF // execute COMPARE in this instance of VEDIT // Buf_Switch(2) // File_Open(@121, BROWSE) // N_Option(1) // // Call("CLEANUP+EXIT") // as workaround ... but not working too, see below // Call_File(101, "COMPARE.VDM") // File_Open("|@(91\|@(92)") // Win_Delete("$") // Buf_Switch(2) // Unfortunately COMPARE.VDM doesn't "return" but "Break_Out(EXTRA" // Buf_Quit(OK) // So these command are not executed :-(( // Buf_Switch(1) // And it's not possible to cleanup !!! // // }else{ // Otherwise: // // open a second instance of VEDIT to do the compare // // and use a temp. copy of the current file for that. Block_Save_As("|(VEDITTEMP)/|@(91)-(current)", 0, File_Size, OK) // save current file temporary Reg_Set(103, 'Win_Move(APP, ') //move the new VEDIT some pixels down and right Num_Str(App_X_Org+20, 103, APPEND+LEFT+NOCR) //to make it obvious that it's a second instance Reg_Set(103, ', ', APPEND) Num_Str(App_Y_Org+20, 103, APPEND+LEFT+NOCR) Reg_Set(103, ', APPW, APPH) File_Open("', APPEND) Reg_Set(103, VEDITTEMP, APPEND) Reg_Set(103, '\', APPEND) Reg_Set(103, @91, APPEND) Reg_Set(103, '-(current)", BROWSE) File_Open("', APPEND) Reg_Set(103, @121, APPEND) Reg_Set(103, '", BROWSE) Buf_Switch(1) N_Option(1) Chain_File(100,"compare.vdm")', APPEND) Reg_Save(103, "|(VEDITTEMP)\FILEVERS$.VDM", OK) Sys("|(VEDIT_EXE) -w -x |(VEDITTEMP)\FILEVERS$.VDM ",NOWAIT+NOMSG) // } return() //-------------------------------------------------------------------------- :CUSTOMIZE: #103=#90 Reg_Set(103, @90) #104=Dialog_Input_1(103,"`Customization of |@(98)`, `Here you are able to change the behavior of the macro.`, `The changes are stored in the macro itself for future use.`, `.g() use version# filename.ext__nnn`, `() use version# filename__nnn.ext`, `() use timestamp filename.ext__yyyy-mm-dd_hh-mm-ss`, `??Subdirectory name for version files:`, `[&OK]`,`[&Cancel]`",SET+APP+CENTER,0,0) if(#104 == 1){ #90 = #103 Reg_Set(90, @103) Call("CHECKDIR") if(Return_Value != 0){ return(1) } Call("WRITECONFIG") } return(0) // -------------------------------------------------------------------- :CHECKDIR: if(File_Exist("|@(92)/|@(90)")){ // does the versions directory exist? if((File_Attrib("|@(92)/|@(90)") & 16) == 0){ // yes, but if not a directory return with error #106=Dialog_Input_1(106,"`Fatal Error!`, `.gThe subdirectory '|@(90)' couldn't be created!`, `There is a file existing with the very same name.`, `Solution:`, `Delete that file or use another directory name.`, `[&Ok]`",APP+CENTER,0,0) return(1) } }else{ // if not exist: create it File_MkDir("|@(92)/|@(90)") } return(0) // -------------------------------------------------------------------- :READCONFIG: #103=Buf_Num Buf_Switch(Buf_Free(EXTRA)) Reg_Ins(#92) // insert running macro Reg_Empty(90) Reg_Empty(98) if (Search("| 0){ // if macro source file found Reg_Set(105, "\", APPEND) // complete the path with filename Reg_Set(105, @98, APPEND) #103=Buf_Num #104=File_Check('"|@(105)"') if(#104 != -1){ // if is already open in a buffer show warning message #106=Dialog_Input_1(106,"`Attention!`, `.gThe new configuration is active for this macro call but wasn't saved because the source file`, `of the running macro (|@(98)) seems to be already loaded.`, ` `, `Should the loaded macro been modified to reflect the changes?`, `\(You have to save the file manually then to make the changes active for the next macro calls.)`, `[&Yes]`, `[&No]`",APP+CENTER,0,0) if(#106 != 1){ return } Buf_Switch(#104) Save_Pos() }else{ // else open it in temp. buffer Buf_Switch(Buf_Free(EXTRA)) File_Open('"|@(105)"', FORCE) } if (Search("|