// // CONV-LIST.VDM Ch. Ziemski 28.04.2002 // 15.05.2002 // // Creates a list of files from a directory listing (DIR) and merges it with a (possibly) // already existing definition file. // // This definition file is like a table with one line for every file. // Each line describes one file with category, title, short description and filename. // // If there are files in the directory but not in the definition file they will be appended there. // If there are files in the definition file but not (longer) really existing in the // directory they will be marked as "inactive" in the definition file. // // Editing the definition file by adding more descriptions or change existing ones and // restarting the macro will sort the entries again by category and reformat it to line up // the columns. // So you can maintain the definition file part for part and you will have a table of // contents this way. // // // The key assignments are: // // F11 : Open the file the cursor is on (line) into another VEDIT window (and close it again) // Shift-F11 : add the appropriate column deviders in the current line // Ctrl-F11 : like Shift-F11, but jump a line down then for following additions // // F12 : convert it again to HTML // Shift-F12 : restart the macro (reload file list, resort and reformat, convert to HTML) // // // // As second feature the macro converts the definition file into a HTML document consisting // of a bunch of tables - one table for each category. // // // Here is an example: // -------------------- // // *TITLE: Example list of files // * // *INTRO: This is an example definition file for CONV-LIST.VDM // *INTRO: // * // * filename category title short description // *--------------------|-------------------------|-----------------------------------------|-------------------- // *** // COMPDIR.VDM | 01 - enhanced originals | | // Checkparen.vdm | 02 - Programming | Parenthesis checker | // chkgotos.vdm | 02 - Programming | Goto checker | checks gotos // adel2.vdm | 02 - Programming | Auto backspace | Auto backspace like in Borland IDE's // book2adr.vdm | Converter | Converts Opera's (?) bookmarks to HTML | // // // This little definition file would be converted into a HTML page with the defined title line // and with a short introduction text. // Then there will follow 3 tables for the three categories. // // // I've developed this macro to maintain a part of my website, where I'm listing many of // VEDIT macros. (As side effect I'm forced to document the macros a bit more... :-) ) // // // The site is still under construction but if you want to have a look (to see this macro at work): // // http://home.t-online.de/home/Christian.Ziemski/vedit // // // // Since it's a work in progress this documentation is to be improved.... // // //---------------------------------------------------------------------------------------- // // ToDo: HTML-Tags aus txt-datei erzeugen, z.B. für Umlaute, <, > ... // chdir/Get_Filename bug!? => Ted // sometimes: "Reentering visual mode" bei Ctrl-F4 // more doku and translate all into english // Code optimizing // //---------------------------------------------------------------------------------------- // // // #88 Cur_Line (for F12) // #89 Cur_Col (for F12) // #90 size of $100 (this macro itself), for restarting with F12 // #91 buffer num for DIR // #92 buffer num for definition file // #93 flag, whether a "|" was found // #94 flag for 1st run of HTML-generation (for group/category switch) // #95 ASCII value of character for inactive entries // #96 BufNum of last loaded file +1000 (for F11) // #97 longest filename + 2 // #98 flag for loaded html file // #99 temp // // #103 temp // #104 temp // #105 temp // #106 temp // // $88 #95 as text // $89 "[no category]" // $90 name of definition file // $91 HTML-Header // $92 HTML-table-header // $93 HTML-table-row // $94 HTML-table-end // $95 HTML-footer // $96 header lines of the definition file (beginning with leading *) // $97 title string (from header line of definition file beginning with *TITLE:) // $98 HTML-intro (from header lines of definition file beginning with *INTRO:) // $99 last category, used for group switch // // $103 temp // $104 temp // $105 temp // $106 temp // //---------------------------------------------------------------------------------------- #98=0 // 1 = let the html-file loaded after generating it // 0 = if you want to have it closed #90=Reg_Size(100) // to check if $100 is unchanged (for F-keys) #95=247 // ASCII value of character for inactive entries Out_Reg(88) // put it as string into $88 Type_Char(#95) Out_Reg(CLEAR) Reg_Set(89, "[no category]") Reg_Set(90, "!contents") // name of the definition file //-------------------------------------- // Set the HTML-templates; used for the HTML generation later // // The key fields @HTMLTITLE@, @INTRO@, @CATEGORY@, @TITLE@, @DESCRIPTION@, @NAME@ // and @DATE@ will be replaced with the current values. // Reg_Set(91, ` @HTMLTITLE@ @INTRO@ `) Reg_Set(92, ' ') Reg_Set(93, ' ') Reg_Set(94, '
@CATEGORY@ Description Macro name
@TITLE@ @DESCRIPTION@ @NAME@

') Reg_Set(95, 'Last modified: @DATE@

') //-------------------------------------------------------- Key_Add(`F11`, `[VISUAL EXIT] if( Reg_Size(100)==#90) { CALL(100, "LOADFILE") }`, OK) Key_Add(`Shft-F11`, `[VISUAL EXIT] if((Reg_Size(100)==#90)&&(Buf_Num==#92)){ CALL(100, "FILLPIPES") }`, OK) Key_Add(`Ctrl-F11`, `[VISUAL EXIT] if((Reg_Size(100)==#90)&&(Buf_Num==#92)){ CALL(100, "FILLPIPES") Line(1, NOERR) }`, OK) Key_Add('F12', '[VISUAL EXIT] if((Reg_Size(100)==#90)&&(Buf_Num==#92)){ #88=Cur_Line #89=Cur_Col CALL(100, "CONV2HTML") Goto_Line(#88) Goto_Col(#89) }', OK) Key_Add('Shft-F12', '[VISUAL EXIT] if((Reg_Size(100)==#90)&&(Buf_Num==#92)){ #88=Cur_Line #89=Cur_Col CALL(100) Goto_Line(#88) Goto_Col(#89) }', OK) //======================================================================================== // if no definition file found in the current dir: let the user choose a directory Chdir(PATH_ONLY) if(! File_Exist("|@(90).txt")){ #103=Dialog_Input_1(104,"`CONV-LIST.VDM: Info`, `There is no definition file in the current directory. \nPlease choose a directory in the following dialog box \nby 'opening' a file in the desired directory; \nor [Cancel].`,`[&Ok]`,`[&Cancel]`", APP+CENTER,0,0) if((#103==0) || (#103==2)){ return } #103=Buf_Num if(Get_Filename(103,"|(CUR_DIR)\*.*")==0){ // bug? pfad wird nicht immer beachtet!?!?! return // o.k.??? } Buf_Switch(Buf_Free(EXTRA)) Reg_Ins(103) Search("\",REVERSE+NOERR) Reg_Copy_Block(104, 0, Cur_Pos) Buf_Quit(OK) Chdir(@104) } // ----- // get the list of files in the directory Buf_Switch(#91=Buf_Free) Out_Ins() Dir("*.*",NOMSG) Out_Ins(CLEAR) #91=Buf_Num Replace("|W|>","",BEGIN+ALL+NOERR) // delete trailing whitespace Replace("|<..|N","",BEGIN+ALL+NOERR) // delete entry of parent directory Replace("|<|*\|N","",BEGIN+ALL+NOERR) // delete entry of subdirectories Replace("|<|@(90)|Y|N", "", BEGIN+ALL+NOERR) // delete entries of definition file itself Replace("|<|*.bak|N","",BEGIN+ALL+NOERR) // delete entries of *.bak files File_Save_As("|(VEDIT_TEMP)\conv-list.tmp", OK+NOMSG) // ----- // open the definition file #99=File_Check("|@(90).txt") if (#99 == -1){ Buf_Switch(#92=Buf_Free) File_Open("|@(90).txt") }else{ Buf_Switch(#99) #92=Buf_Num } // remove (and save) the header lines, some formatting BoF Reg_Empty(96) while(Match("|<*")==0){ if(Match("|<**")==0){ // um das Ende des Headers zu finden, falls die erste Datei auskommentiert ist Reg_Copy(96, 1, APPEND) Del_Line(1) break } Reg_Copy(96, 1, APPEND) Del_Line(1) } Replace("|W|>","",BEGIN+ALL+NOERR) // delete trailing whitespace Replace("|<|X","",BEGIN+ALL+NOERR) // delete empty lines Replace("|<|@(90)|Y|N", "", BEGIN+ALL+NOERR) // delete entries of definition file itself BoF // ----- // check all files in the directory if they are in the definition file too Buf_Switch(#91) BoF while(! At_EoF){ Reg_Copy_Block(103, Cur_Pos, EoL_Pos) // current filename Buf_Switch(#92) Search("|<|@(103)", BEGIN+NOERR) // is the file as active entry in definition file? if(!EM){ // if yes Char(Chars_Matched) if(Search("|<|@(103)", BEGIN+ALL+NOERR) > 1){ // search for active doublettes Win_Clear() Message("\nAttention: There are some files with more than one active entry!\n") Message("----------------------------------------------------------------\n\n") } while(Search("|<|@(103)", BEGIN+ALL+NOERR) > 1){ // search for active doublettes Message(">> ") Reg_Type(103) Message(` << has more than one active entry. Double is set to inactive.\n`) BoL Ins_Char(#95) Ins_Text(" [2x] ") } }else{ // if not found as active entry Search("|<|@(88)|[|W]|@(103)", BEGIN+NOERR) // check if it's an inactive entry if(!EM){ // if yes Del_Char(1) // switch from inactive to active state Replace("|<|W", "", NOERR) }else{ // if no Search("|<*|*|@(103)", BEGIN+NOERR) // check if it's an commented out entry if(EM){ // if no Search("|<|@(88)|[|W]*|*|@(103)", BEGIN+NOERR) // check if it's an inactive commented out entry if(EM){ // if absolutely not there => append it there EoF Reg_Ins(103) Ins_Newline(1) } } } } Buf_Switch(#91) Line(1, NOERR) } BoF // ----- // check all files in the definition file if they are existing in file list; if not mark the entry as inaktive Buf_Switch(#92) BoF while(! At_EoF){ #103=Cur_Pos if(Cur_Char != #95){ // if not an inactive entry line Search("|{||,|N}", NOERR) Search("|!|W", REVERSE+ADVANCE+NOERR) if(EM || At_BoL){ // if no filename in that line (maybe a faulty leading "|") BoL Ins_Char(#95) // mark line as inactive Ins_Text(" ") }else{ Reg_Copy_Block(103, #103, Cur_Pos) // current filename Buf_Switch(#91) Search("|@(103)", BEGIN+NOERR) // search this one in directory list Buf_Switch(#92) if(EM){ // if file not existing in directory list BoL Ins_Char(#95) // mark it as inactive Ins_Text(" ") } } } Line(1, NOERR) } BoF // ----- // reformat and sort the definition file Buf_Switch(#92) BoF // delete all leading and trailing spaces in every column Replace("\s+\|\s+", " \| ", BEGIN+ALL+NOERR+REGEXP) // insert a space around the pipes where necessary (should be optimzed!) Replace("{[^\s]}\|", "\1 \|", BEGIN+ALL+NOERR+REGEXP) Replace("\|{[^\s]}", "\| \1", BEGIN+ALL+NOERR+REGEXP) // search the longest filename => in #97 (+ 2) BoF #97=0 // longest name #93=0 // flag if at least one "|" found while(! At_EoF){ Search_Block("|", Cur_Pos, EoL_Pos, NOERR) if(!EM){ Search("|!|W", REVERSE+ADVANCE) #93=1 // at least one | found }else{ EoL } if((Cur_Col + 2) > #97){ #97=Cur_Col + 2 } Line(1, NOERR) } // line up the trailing infos (if any) BoF while(! At_EoF){ Search_Block("|", Cur_Pos, EoL_Pos, NOERR) if(!EM){ if(Cur_Col < #97){ Ins_Indent(#97) }else{ Del_Char(#97-Cur_Col) } } Line(1, NOERR) } BoF if(#93){ // wenn mindestens 1 | vorhanden // Alle Zeilen mit | nach oben Sort_Merge("#97,#97+2",0,File_Size, REVERSE) EoF Search("||", REVERSE) Line(1) // diese dann nach category und name sortieren Sort_Merge("#97,#97+20;1,#97",0,Cur_Pos) // schön wäre hier noch die Möglichkeit, leere category ans Ende zu sortieren. Aber SPACE < ABC ... } // Rest dann nach name sortieren if(! At_EoF){ // to avoid crash Sort_Merge("1,#97",Cur_Pos, File_Size) } // more beautifying (the 3rd and 4th column) for(#105=2; #105<4; #105++){ #104=0 BoF while(! At_EoF){ #103=Search_Block("|", Cur_Pos, EoL_Pos, ALL+NOERR+ERRBREAK) if(#103 >= #105){ BoL Search_Block("|", Cur_Pos, EoL_Pos, COUNT, #105) // auf den 2. bzw 3. "|" if(Cur_Col > #104){ #104=Cur_Col } }else{ EoL if(Cur_Col > #104){ #104=Cur_Col + 1 } } Line(1, NOERR) } BoF while(! At_EoF){ #103=Search_Block("|", Cur_Pos, EoL_Pos, ALL+NOERR+ERRBREAK) if(#103 >= #105){ BoL Search_Block("|", Cur_Pos, EoL_Pos, COUNT, #105) // auf den 2. bzw. 3. "|" if(Cur_Col < #104){ Ins_Char(32, COUNT, #104-Cur_Col) } } Line(1, NOERR) } } // get all inactive enties and put them to the end of file BoF Reg_Empty(104) while(! At_EoF){ Search("|<|@(88) ", NOERR) // search inactive entries if(EM){ break }else{ Reg_Copy(104, 1, APPEND) Del_Line(1) } } EoF Reg_Ins(104) // insert header again BoF Reg_Ins(96) File_Save(NOMSG) // --------------------------------------------------------- :CONV2HTML: // generating the HTML code Buf_Switch(#91) Buf_Quit(OK) #99=File_Check("|@(90).html") // is the html-file already loaded (from a previous run or so)? if (#99 == -1){ // if not: Buf_Switch(#91) // use the used buffer #91 for that File_Save_As("|@(90).html",OK+NOMSG) // #98=0 }else{ Buf_Switch(#99) // if yes: #98=1 #91=Buf_Num // use it, but clear all lines BoF Del_Line(ALL) } // check header of definition file Buf_Switch(#92) Replace("|<|X","",BEGIN+ALL+NOERR) // delete empty lines BoF Reg_Empty(97) // HTML-title Reg_Empty(98) // introduction lines while(Match("|<*")==0){ if(Match("|<*TITLE:")==0){ Char(Chars_Matched) Reg_Copy_Block(97, Cur_Pos, EoL_Pos) } if(Match("|<*INTRO:")==0){ Char(Chars_Matched) Reg_Copy(98,1, APPEND) Reg_Set(98, "
", APPEND) } Line(1, NOERR) } if(Reg_Size(97)==0){ Reg_Set(97, "no title") } // build HTML Buf_Switch(#91) BoF Reg_Ins(91, BEGIN) // HTML-header Replace("@HTMLTITLE@", @97, CASE+NOERR) Replace("@INTRO@", "", CASE+NOERR) if(!EM){ Reg_Ins(98) } EoF Reg_Empty(99) // last category Buf_Switch(#92) #94=1 // flag for 1st run while(! At_EoF){ // Format der Datei: // // * header or comments // name | category | title | short description // ÷ inactive entries BoL if((Cur_Char == #95) || (Cur_Char == '*')){ // if comment or inactive entry Line(1, NOERR) Continue } Reg_Empty(103) // filename Reg_Empty(104) // category Reg_Empty(105) // title Reg_Empty(106) // descr #103=Cur_Pos Search_Block("|", Cur_Pos, EoL_Pos, NOERR+ADVANCE) if(! EM){ // wenn Feld(er) mit | gefunden #103=Cur_Pos Char(-1) Search("|!|W", REVERSE+ADVANCE) Reg_Copy_Block(103, BoL_Pos, Cur_Pos) // name Goto_Pos(#103) Search_Block("|", Cur_Pos, EoL_Pos, NOERR+ADVANCE) if(!EM){ // hinter category ist noch mehr ... Reg_Copy_Block(104, #103, Cur_Pos-2) // save category #103=Cur_Pos Search_Block("|", Cur_Pos, EoL_Pos, NOERR+ADVANCE) if(!EM){ // hinter title ist noch mehr ... Reg_Copy_Block(105, #103, Cur_Pos-2) // save title Reg_Copy_Block(106, Cur_Pos, EoL_Pos) }else{ // nur noch titel Reg_Copy_Block(105, #103, EoL_Pos) } }else{ // nur category gefunden Reg_Copy_Block(104, #103, EoL_Pos) } }else{ // nur name gefunden Reg_Copy_Block(103, #103, EoL_Pos) } // remove leading and trailing spaces from category, title and description Buf_Switch(Buf_Free(EXTRA)) Reg_Ins(104) Replace("|<|W","",BEGIN+ALL+NOERR) Replace("|W|>","",BEGIN+ALL+NOERR) Reg_Copy_Block(104, 0, File_Size, DELETE) Reg_Ins(105) Replace("|<|W","",BEGIN+ALL+NOERR) Replace("|W|>","",BEGIN+ALL+NOERR) Reg_Copy_Block(105, 0, File_Size, DELETE) Reg_Ins(106) Replace("|<|W","",BEGIN+ALL+NOERR) Replace("|W|>","",BEGIN+ALL+NOERR) Reg_Copy_Block(106, 0, File_Size, DELETE) Buf_Quit(OK) Buf_Switch(#92) // switch back if(Reg_Size(104)==0){ Reg_Set(104, @89) // category = "[no category]" } if(Reg_Size(105)==0){ Reg_Set(105, "...") // title } if(Reg_Size(106)==0){ Reg_Set(106, "...") // description } // insert the results in the other buffer Buf_Switch(#91) if(Reg_Compare(99, @(104)) != 0){ // if category has changed if(#94==1){ // not on the very first run #94++ }else{ Reg_Ins(94) // insert HTML-table-end } Reg_Set(99, @104) // remember this, the last used category // create new table with header Reg_Ins(92, BEGIN) // HTML table header template Replace("@CATEGORY@", @99, CASE) EoF } Reg_Ins(93, BEGIN) // HTML table row template Replace("@TITLE@", @105, CASE) Replace("@DESCRIPTION@", @106, CASE) Replace("@NAME@", @103, ALL+CASE) EoF Buf_Switch(#92) Line(1, NOERR) } Buf_Switch(#91) EoF if(#94 <= 2){ // Reg_Ins(94) // insert HTML-table-end } Out_Reg(103) // get the current date Date(NOMSG+NOCR) Out_Reg(CLEAR) Reg_Ins(95, BEGIN) // HTML-footer Replace("@DATE@", @103, CASE) // with "last modification" note File_Save(NOMSG) BoF if(#98==0){ // if html-file wasn't loaded before the run (and not set to remain loaded) Buf_Quit(OK) // close it now } Buf_Switch(#92) BoF return //========================================================================================= // Key-Assignment for F11: If in definition file: Load the file where the cursor is on :LOADFILE: if(Buf_Num==#92){ BoL if((Cur_Char == #95) || (Cur_Char == '*') || At_EoL){ return } Search("|{||,|N}", NOERR) if(EM){ return } Search("|! ", REVERSE+ADVANCE) Reg_Copy_Block(103, BoL_Pos, Cur_Pos) File_Open("|@(103)") #96=Buf_Num+1000 }else{ if(Buf_Num == (#96-1000)){ File_Close(CONFIRM+NOMSG) Buf_Quit() Buf_Switch(#92) } } return //--------------------------- // Key-Assignment for Shift-F11/ Ctrl-F11: Fill line with appropriate |'s :FILLPIPES: BoL if(At_EoF){return} #103=Cur_Pos Search("|", REVERSE+NOERR) if(EM){return} Goto_Pos(#103) Replace_Block("|" ," ", Cur_Pos, EoL_Pos, ALL+NOERR) BoL Search("|", REVERSE+NOERR) BoL repeat(ALL){ Search_Block("|", Cur_Pos, EoL_Pos, NOERR+ERRBREAK) #104=Cur_Pos #105=Cur_Col Goto_Pos(#103) EoL Ins_Indent(#105) Goto_Col(#105) if(At_EoL){Ins_Text("|")}else{Ins_Text("|", OVERWRITE)} Goto_Pos(#104+1) } Goto_Pos(#103) EoL if(Cur_Char(-1)=='|'){Ins_Text(" ")} BoL Search("|", ADVANCE) Char(1) return //=======================================================================================