Module doku
/*

<doku>
<created>23/02/2004 14:07:48</created>
<changed>23/02/04 sk</changed>
<author>Thomas Friebel</author>
<about>
Dieses Modul ist weniger für die direkte Wiederverwendung in anderen Modulen gedacht, als vielmehr als Entwicklungswerkzeug, um Module mit Hilfe ihrer Quellcode-Kommentare zu dokumentieren.
Alle Kommentare, die sich zwischen den Tags &lt;DOKU&gt; und &lt;/DOKU&gt; befinden werden in ein HTML-Dokument geschrieben, welches den Dateinamen des Moduls (inkl. der Endung ".mod") und der Erweiterung ".htm" bekommt.
Der Aufruf auf Kommandozeilenebene sieht exemplarisch wie folgt aus:
<code>
/tdbengine/tdbengine doku.prg -cwin -smeinmodul.mod -ddoku
</code>
Wurde kein "-d Paramter" als Zielverzeichnis übergeben, so wird ein Unterverzeichnis ./doku/ angelegt und als Zielpfad verwendet.

Das Ergebnis ist eine Datei namens meinmodul.mod.htm
Das Doku-Modul unterscheidet bisher 2 Vorkommensarten der &lt;Doku&gt;-Tags, nämlich
a.) Innerhalb eines PROCEDURe-Rumpfes und
b.) ausserhalb irgendwelcher Prozeduren.

Im 1. Fall werden alle Doku-Abschnitte erkannt und der Procedure zugeordnet ausgegeben.
</about>
<todo>
Um grössere Dokumentationsblöcke nicht mitten im Quellcode platzieren zu müssen ist noch eine Art Doku-Verweis einzubauen.
An der Stelle, an der im HTML-Dokument ein Text erscheinen soll ist im Quellcode nur noch ein "Link" auf einen am Modul-Ende stehenden Block einzufügen. Das soll die Übersichtlichkeit im Source wahren.
</todo>
</doku>
*/


Var    cVerStr             : String = "0.2"
Var    STDOUT                : Integer = 0
var false                : Byte = NEIN
var true                : Byte = JA
var cEOL_Win            : String = ^M+^J
var cEOL_NIX            : String = ^J
var cEOL                : String = cEOL_NIX

Procedure trim(c : String) : String
    return rtrim(ltrim(c))
endproc

Procedure ConOut(cText : String)
    Write(STDOUT, cText)
EndProc

Procedure ConOutLn(cText : String)
    WriteLn(STDOUT, cText)
EndProc


Procedure displayHelp
/*<doku>
<created>30/04/02 11:19:56</created><changed>30/04/02 11:19:56</changed><author>TF</author>
<about>Gibt die Kommandozeilen-Hilfe zum Modul aus</about>
<todo>Für HTML-Ausgabe einrichten</todo>
</doku>*/
    ConOutLn("")
    ConOutLn("Hilfe")
    ConOutLn("----------------------------------------------- ")
    ConOutLn("Aufruf: {/bin/tdbengine} doku.prg [-?|--help] [-c{eol}] [-s|--source]{matchpattern} [-d|--destination]{destination}")
    ConOutLn("")
    ConOutLn("eol        : Art des Zeilenumbruchs im Modul (eol = 'win' - Chr(13)+Chr(10), eol = 'unix' - Chr(13))")
    ConOutLn("matchpattern    : Dateifilter (z.B. *.mod oder doku.mod, etc.)")
    ConOutLn("destination    : Name des Zielverzeichnis (wenn leer, dann './doku/'). Wird ggfs. angelegt")
    ConOutLn("")
    ConOutLn("Beispiele:")
    ConOutLn("tdbengine doku.prg -cwin -sadressen.mod -ddoku")
    ConOutLn("tdbengine doku.prg --source*.mod")
    ConOutLn("tdbengine doku.prg -s*.mod")
    ConOutLn("")
    ConOutLn("Bei Fragen, Kritik oder Wünschen: http://www.tdbengine.org")
    ConOutLn("Dieses Modul kann sowohl gewerblich als auch privat nach Belieben eingesetzt, modifiziert und weitergegeben werden.")
    ConOutLn("Die Firma tdb Software Service GmbH gewährt keinerlei Garantie auf die Funktionalität des Programms.")
    ConOutLn("")
EndProc

Procedure getUses(fDoc : Integer; cFileTarget : String)
/*<doku>
<created>30/04/02 11:20:50</created><changed>03/05/02 11:20:50 / TF</changed><author>TF</author>
<parameters><li>fDoc : File-Handle des zu erzeugenden HTML-Files </li></parameters>
<about>
Ermittelt alle mit &#85;ses/&#73;nclude eingebundenen Module und listet diese im Kopfbereich der Doku als Links zu deren Dokus auf.
Es wird dabei nicht auf die Existenz entsprechender Dokus geprüft.
</about>
<keywords>eingebundene Module, ermitteln, verlinken</keywords>
</doku>*/
    Var nLastPos     : Integer = 0;
    Var nNewPos     : Integer = 0;
    Var cModul        :    String

    If RAMText_Find("ramtext:modul","us"+"es",nLastPos,1) > 0 or RAMText_Find("ramtext:modul","incl"+"ude",nLastPos,1) > 0
        WriteLn(fDoc, OEMToANSI("benötigte Module:<br>"))
    End
    While nLastPos := RAMText_Find("ramtext:modul","us"+"es ",nLastPos,1) > 0
        nNewPos := RAMText_Find("ramtext:modul",cEOL, nLastPos)
        cModul := RAMText_Part("ramtext:modul",nLastPos, nNewPos - nLastPos)
        cModul := Lower(cModul)
        cModul := Exchange(cModul,"us"+"es ","")
        cModul := Exchange(cModul," ","")
        cModul := Exchange(cModul,";","")
        If IsFile(cModul) > 0
            cModul := "<a href='"+cFileTarget+cModul+".htm'>"+cModul+"</a>"
        End
        WriteLn(fDoc, cModul +"<br>")
        nLastPos := nNewPos
    End
    nLastPos := 1
    While nLastPos := RAMText_Find("ramtext:modul","incl"+"ude ",nLastPos,1) > 0
        nNewPos := RAMText_Find("ramtext:modul",cEOL, nLastPos)
        cModul := RAMText_Part("ramtext:modul",nLastPos, nNewPos - nLastPos)
        cModul := Lower(cModul)
        cModul := Exchange(cModul,"incl"+"ude ","")
        cModul := Exchange(cModul," ","")
        cModul := Exchange(cModul,";","")
        If IsFile(cModul) > 0
            cModul := "<a href='"+cFileTarget+cModul+".htm'>"+cModul+"</a>"
        End
        WriteLn(fDoc, cModul +"<br>")
        nLastPos := nNewPos
    End

EndProc


Procedure DokuInProc(nBegin, nEnd : Integer; Var aProcPos : Integer[,]; iCount : Integer ) : Integer

    Var i : Integer = 0
    Var nResult : Integer = 0

    While i++ <= iCount do
        If nBegin > aProcPos[i,1] and nEnd < aProcPos[i,2]
            If aProcPos[i,0] = 0
                nResult := i
                aProcPos[i,0] := i
                i := iCount +1
            Else
                nResult := -i
            End
        End
    End
    Return nResult
EndProc

Procedure ParseBlock
/*<doku>
<todo></todo>
<created>30/04/02 11:22:36</created><changed>30/04/02 11:22:36</changed><author>TF</author>
<about>Ersetzt alle Doku-Tags im Doku-Block durch entsprechende HTML-Konstrukte.
</doku>*/
    Var nPosBegin, nPosEnd, nPosCur : Integer
    Var nDelBeg, nDelEnd            : Integer
    Var cNode                        :    String
    Var aNodes                        :    String[]
    Var aPlaceholder                :    String[]
    Var i                            :    Integer
    Var fBase,fBlock, fClip                :    Integer
    Var fHeader                        :    Integer
    Var cLine                        :    String

    InitArray(aNodes[10]);
    InitArray(aPlaceholder[10]);
    aNodes[0] := "created"; aPlaceholder[0] := OEMToANSI("<span class='created'><b>Angelegt:</b> #created#</span>");
    aNodes[1] := "changed"; aPlaceholder[1] := OEMToANSI("<span class='changed'><b>Geändert:</b> #changed#</span>");
    aNodes[2] := "name"; aPlaceholder[2] := OEMToANSI("<br><div class='name'>Name: #name#</div>");
    aNodes[3] := "parameters"; aPlaceholder[3] := OEMToANSI("<br><span class='parameters'>Parameter: <ul>#parameters#</ul></span>");
    aNodes[4] := "return"; aPlaceholder[4] := OEMToANSI("<br><span class='return'>Rückgabewert:<br>#return#</span>"); //sk
    aNodes[5] := "about";    aPlaceholder[5] := OEMToANSI("<br><span class='about'><b>Beschreibung:</b><br><br>#about#</span>");
    aNodes[6] := "todo";    aPlaceholder[6] := OEMToANSI("<br><div class='todo'><b>TODO:</b><br> #todo#</div>");
    aNodes[7] := "attention";    aPlaceholder[7] := OEMToANSI("<br><div class='attention'><b>ACHTUNG:</b><br>#attention#</div>");
    aNodes[8] := "author";     aPlaceholder[8] := OEMToANSI("<br><div class='author'><b>Autor:</b> #author#</div>");
    aNodes[9] := "keywords";     aPlaceholder[9] := OEMToANSI("<br><div class='keywords'><b>Keywords:</b> #keywords#</div>");
    ramtext("ramtext:blockheader",8192)

    fBase := Rewrite("ramtext:blockheader")
        writeln(fBase, '<div class="blockheader">')
        writeln(fBase, '#created##changed#')
        writeln(fBase, '#author#')
        writeln(fBase, '#attention#')
        writeln(fBase, '#about#')
        writeln(fBase, '#todo#')
        writeln(fBase, '#parameters#')
        writeln(fBase, '#return#')
        writeln(fBase, '#keywords#')
        writeln(fBase, '#plain#')
        writeln(fBase, '</div>')
    close(fBase)


    i:=0
    nPosCur := 0
    While cNode := aNodes[i] # "" do
        nDelBeg := 0
        nDelEnd := 0
        If nPosBegin := RAMText_Find("ramtext:block","<"+cNode+">",1,1) > 0
            nDelBeg := nPosBegin
            nPosBegin := nPosBegin + Length(cNode) +2
            If nPosEnd := RAMText_Find("ramtext:block","</"+cNode+">",nPosBegin,1) > 0
                If nPosEnd > nPosBegin
                    RAMText_Copy("ramtext:block",nPosBegin, nPosEnd - nPosBegin)
                    RAMText_Subst("ramtext:blockheader","#"+cNode+"#",aPlaceholder[i])
                    fClip := Reset("ramtext:~clip")
                    While not EOT(fClip)
                        RAMText_Subst("ramtext:blockheader","#"+cNode+"#",ReadLn(fClip)+"<br>#"+cNode+"#")
                    End
                    Close(fClip)
                End
                RAMText_Subst("ramtext:blockheader","#"+cNode+"#","")
            End
            nPosCur :=    nPosEnd    + Length(cNode) +3
            nDelEnd := nPosCur
            If nDelBeg > 0 and (nDelBeg < nDelEnd)
                RAMText_Delete("ramtext:block",nDelBeg, nDelEnd - nDelBeg)
            End
        End
        i++
        RAMText_Subst("ramtext:blockheader","#"+cNode+"#","")
    End
    RAMText_Subst("ramtext:blockheader","#plain#","<br><div class='plain'>#plain#</div>")
    fBlock := Reset("ramtext:block")
        While not EOT(fBlock)
            If cLine := ReadLn(fBlock) # ""
                RAMText_Subst("ramtext:blockheader","#plain#",cLine+"<br>#plain#")
            End
        End
    Close(fBlock)
    RAMText_Subst("ramtext:blockheader","#plain#","")
    CopyFile( "ramtext:blockheader","ramtext:block")
EndProc


Procedure isParam(c : String) : String
    var i     : byte
    var cP    : String
    i:=1
    while cP := ParamStr(i++) do
        if cP[1,Length(c)] = c then
            return cP
        end
    end
    return ""
endproc

Procedure Main
/*<doku>
<created>30/04/02 11:25:02</created><changed>30/04/02 11:25:02</changed><author>TF</author>
<about>Die Hauptprozedur des Doku-Moduls. Hier werden die einzelnen Sourcen nach den &lt;DOKU&gt;-Tags gescannt und die HTML-Dokumente erstellt</about>
</doku>*/

    Var cFile,cDir        : String
    Var nPos,nPosEnde    :    Integer
    Var f                    :    Integer
    Var fDoc            :    Integer
    Var    cFileMatch         :    String
    Var cFileTarget        :    String
    Var bBlockOpen        :    Byte
    Var nPosProc        :    Integer
    Var nPosEndProc        :    Integer
    Var cProcedure         :    String
    Var    bWithinProc        :    Byte
    Var aProcedures        :    String[]
    Var aProcPos        :    Integer[,]
    Var    iProcedures        :    Byte
    Var    i                :    Byte
    Var nProc            :    Integer
    Var    cLine            :    String
    CGICloseBuffer

    ConOutLn("tdbengine doku.mod Version " + cVerStr)
    If isParam("-?") or isParam("--help") then
        displayHelp
        Return
    ElsIf cFileMatch := isParam("-s")[3,255] = "" and cFileMatch := isParam("--source")[9,255] = "" then
        ConOutLn("Keine Quell-Module angegeben!")
        Return
    End

    if isParam("-c")[3] like "w" or isParam("--charset")[10] like "w" then cEOL := cEOL_Win end

    If cFileTarget := isParam("-d")[3,255] = "" and cFileTarget := isParam("--destination")[14,255] = "" then
        cFileTarget := "./doku/"
    ElsIf cFileTarget[Length(cFileTarget),1] # "/"
        cFileTarget := cFileTarget +"/"
    End
    MakeDir(cFileTarget)
    ConOutLn("Such-Pattern            : "+cFileMatch)
    ConOutLn("Zielverzeichnis        : "+cFileTarget)
    ConOutLn("Starte Scan-Vorgang")

    cFile := trim(FirstDir(cFileMatch,""))
    //CGIWriteLn('Main.cFile='+cFile) //sk
    While cFile do
        ramtext("ramtext:modul",64000)
        cDir := trim(DirInfo(cFileMatch)[128,127])
        cFile := trim(cFile[1,63])
        fDoc := Rewrite("ramtext:docu")
        WriteLn(fDoc,"<html>")
        WriteLn(fDoc,"<head>")
        WriteLn(fDoc,"<title>")
        WriteLn(fDoc,cFile+" - Automatische Quellcode-Dokumentation (Stand: " + DateStr(Today) + " / " + TimeStr(Now)+")") //sk
        WriteLn(fDoc,"</title>")
        WriteLn(fDoc,'<link rel="stylesheet" type="text/css" href="dokustyle.css">')
        WriteLn(fDoc,"</head>")
        WriteLn(fDoc,"<body>")
        WriteLn(fDoc,"<style>")
        WriteLn(fDoc,".headline {margin:10px; color:#123456; font-family:verdana; font-size:16pt; font-weight:bolder;}")
        WriteLn(fDoc,".plain {position:relative; color:#123456; font-family:verdana; font-size:8pt;;}")
        WriteLn(fDoc,".author {color:#000000; font-family:verdana; font-size:8pt;;}")
        WriteLn(fDoc,".created {color:#000000; font-family:verdana; font-size:8pt;;}")
        WriteLn(fDoc,".about {color:#123456; font-family:verdana; font-size:9pt;}")
        WriteLn(fDoc,".parameters {color:#123456; font-family:verdana; font-size:9pt;}")
        WriteLn(fDoc,".return {color:#123456; font-family:verdana; font-size:9pt;}")
        WriteLn(fDoc,".todo {color:#123456; font-family:verdana; font-size:8pt;}")
        WriteLn(fDoc,".changed {color:#000000; font-family:verdana; font-size:8pt;;}")
        WriteLn(fDoc,".keywords {color:#999999; font-family:verdana; font-size:8pt; font-weight:bold;}")
        WriteLn(fDoc,".attention {border-style:solid; border-width:1; color:#ff0000; font-family:verdana; font-size:11pt; font-weight:bold;}")
        WriteLn(fDoc,".procedure {color:#123456; font-family:verdana; font-size:10pt; font-weight:bold;}")
        WriteLn(fDoc,"</style>")
        WriteLn(fDoc, "<small><a href='.'>Index</a></small>")
        WriteLn(fDoc, "<div class='headline'>Modul: "+ cFile +"</div>")

        CopyFile(trim(cDir+cFile),"ramtext:modul")
        getUses(fDoc,"")
        WriteLn(fDoc, "<hr>")
        WriteLn(fDoc,"Dokumentierte Prozeduren im Modul:<br>")
        WriteLn(fDoc,'<ul><code>#Procedures#</code></ul>') //sk

        ConOut(cDir+cFile+NTimes(" ",20 - Length(cFile)) + " : ")
        InitArray(aProcedures[99])

        iProcedures := 0
        i := 0
        nPos := 0
        nPosProc := 0
        bBlockOpen := false
        nPosEndProc := 0
        nPosEnde := 0
        bBlockOpen := false

        InitArray(aProcPos[99,2])


        While nPosProc := RAMText_Find("ramtext:modul","PROCEDURE ",nPosEndProc,1) > 0 do
            cProcedure := RAMText_Part("ramtext:modul",nPosProc,RAMText_Find("ramtext:modul",cEOL, nPosProc) - nPosProc)
            nPosEndProc := RAMText_Find("ramtext:modul","ENDPROC",nPosProc,1)

            aProcedures[iProcedures++] := cProcedure[11,250]
            aProcPos[iProcedures,0] := 0
            aProcPos[iProcedures,1] := nPosProc
            aProcPos[iProcedures,2] := nPosEndProc
        End

        While nPos := RAMText_Find("ramtext:modul","<DO"+"KU>",nPosEnde,1) > 0

                nPos := nPos + Length("<DO"+"KU>")
                nPosEnde := RAMText_Find("ramtext:modul","</DO"+"KU>",nPos,1)

                If nProc := DokuInProc(nPos, nPosEnde, aProcPos,iProcedures) > 0
                    bWithinProc := true
                    WriteLn(fDoc, "<a name='proc"+Str(nProc)+"'><br><div style='background-Color:#eeeeee;' class='procedure'><code>"+ aProcedures[nProc] +"</code></div><br>") //sk
                ElsIf nProc < 0
                    bWithinProc := true
                ElsIf nProc = 0
                    bWithinProc := false
                    WriteLn(fDoc, "<br><div style='position:relative;background-Color:#eeeeee; left:20px;'><b><small>HINWEIS</small></b>")
                End

                If bBlockOpen = true
                    bBlockOpen := false
                Else
                    bBlockOpen := true
                End
                If bBlockOpen && RAMText_Copy("ramtext:modul",nPos,nPosEnde - nPos) > 0
                    ConOut(".")
                    bBlockOpen := false
                    CopyFile("ramtext:~clip","ramtext:block")
                    ParseBlock

                    f := Reset("ramtext:block",1)
                    While not EOT(f)
                        Write(fDoc,Read(f))
                    End
                    If bWithinProc
                    Else
                        WriteLn(fDoc, "</div>")
                    End
                    Close(f)
                End
                nPosEnde := nPosEnde + Length("</do"+"ku>")
            End
        WriteLn(fDoc,"</body>")
        WriteLn(fDoc,"</html>")
        Close(fDoc)
        i:= 1
        While i <= iProcedures do
            If aProcPos[i,0] > 0
                RAMText_Subst("ramtext:docu","#Procedures#","<li><a href='#proc"+Str(i)+"'>"+aProcedures[i]+"</a></li>#Procedures#")
            End
            i++
        End
        RAMText_Subst("ramtext:docu","#Procedures#","")
        CopyFile("ramtext:docu", cFileTarget+cFile +".htm")
        ConOutLn(" OK")
        Close(fDoc)
        cFile := trim(NextDir[1,63])
    End

EndProc