//
// 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, '
@CATEGORY@ |
Description |
Macro name |
')
Reg_Set(93, '
@TITLE@ |
@DESCRIPTION@ |
@NAME@ |
')
Reg_Set(94, '
')
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
//=======================================================================================