/* * $Id$ */ /* * Harbour Project source code: * Document generator * * Copyright 2009 April White * www - http://harbour-project.org * * Portions of this project are based on hbdoc * Copyright 1999-2003 Luiz Rafael Culik * * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this software; see the file COPYING. If not, write to * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307 USA (or visit the web site http://www.gnu.org/). * * As a special exception, the Harbour Project gives permission for * additional uses of the text contained in its release of Harbour. * * The exception is that, if you link the Harbour libraries with other * files to produce an executable, this does not by itself cause the * resulting executable to be covered by the GNU General Public License. * Your use of that executable is in no way restricted on account of * linking the Harbour library code into it. * * This exception does not however invalidate any other reasons why * the executable file might be covered by the GNU General Public License. * * This exception applies only to the code released by the Harbour * Project under the name Harbour. If you copy code from other * Harbour Project or Free Software Foundation releases into a copy of * Harbour, as the General Public License permits, the exception does * not apply to the code that you add in this way. To avoid misleading * anyone as to the status of such modified files, you must delete * this exception notice from them. * * If you write modifications of your own for Harbour, it is your choice * whether to permit this exception to apply to your modifications. * If you do not wish that, delete this exception notice. * */ /* todo - handle preformatted text / code / etc - include links back to index - include jumps to 'top' - 'coverage' to have links to corresponding file - 'Filename' must return the same file name all of the time for the same source - one method to retrieve, one to add? - key-value pair [hash table?] todo - treat '' / as an non-conformance condition ntf: this may be okay for EXAMPLES and TESTS but this is also used within other sections, much like todo - look for embedded 'fixed' these files have *..\..\doc\en\cmdline.txt txt\lineutility.txt ..\..\doc\en\dbstrux.txt txt\dbstrux.txt *..\..\doc\en\file.txt txt\file.txt ..\..\doc\en\input.txt txt\input.txt ..\..\doc\en\lang.txt txt\lang.txt ..\..\doc\en\menu.txt txt\menu.txt ..\..\doc\en\objfunc.txt txt\objfunc.txt ..\..\doc\en\rdddb.txt txt\rdddb.txt ..\..\doc\en\sayget.txt txt\sayget.txt ..\..\doc\en\set.txt txt\set.txt ..\..\doc\en\setmode.txt txt\setmode.txt ..\..\doc\en\string.txt txt\string.txt ..\..\doc\en\var.txt txt\var.txt done - recognize and accept ; see macro.txt output esp. hb_setmacro done - list 'compliance' and 'platforms' within help done - list 'category' and 'subcategory' types on help screen done - load into memory (class and) method template done - minimize these to the barest done - build a list of 'categories' and validate against; see what 'classdoc' uses done - validate sources against these templates */ #include "directry.ch" #include "fileio.ch" #include "hbdoc.ch" ANNOUNCE HB_GTSYS REQUEST HB_GT_CGI_DEFAULT #define BASE_DIR ".." + hb_ps() + ".." + hb_ps() STATIC s_aExclusions := { "class_tp.txt", "hdr_tpl.txt" } MEMVAR p_hsSwitches PROCEDURE Main( ... ) LOCAL aArgs := hb_AParams() LOCAL idx, idx2, idx3, idx4 LOCAL arg LOCAL cArgName LOCAL cFormat LOCAL oDocument, oIndex LOCAL aContent /* Setup input CP of the translation */ hb_cdpSelect( "UTF8EX" ) /* Configure terminal and OS codepage */ hb_SetTermCP( hb_cdpTerm() ) Set( _SET_OSCODEPAGE, hb_cdpOS() ) init_Templates() PUBLIC p_hsSwitches := {; /* configuration settings, values, etc */ ; "basedir" => BASE_DIR, ; "doc" => .T., ; "source" => .F., ; "contribs" => .T., ; "format" => {}, ; "output" => "category", ; "include-doc-source" => .F., ; "include-doc-version" => .F., ; "immediate-errors" => .F., ; /* internal settings, values, etc */ ; "DELIMITER" => "$", ; "format-list" => { "text", "ascii", "html", "html2", "xml", "rtf", "hpc", "ngi", "os2", "chm", "ch2", "pdf", "trf", "doc", "dbf", "all" }, ; "hbextern.ch" => {}, ; "in hbextern" => {}, ; "not in hbextern" => {}, ; "" => NIL } // remove formats that have not been implemented yet FOR idx := Len( p_hsSwitches[ "format-list" ] ) TO 1 STEP -1 IF p_hsSwitches[ "format-list" ][ idx ] == "all" ELSEIF ! hb_IsFunction( "Generate" + p_hsSwitches[ "format-list" ][ idx ] ) hb_ADel( p_hsSwitches[ "format-list" ], idx, .T. ) ENDIF NEXT IF Len( aArgs ) == 0 .OR. aArgs[ 1 ] == "-?" .OR. aArgs[ 1 ] == "/?" .OR. aArgs[ 1 ] == "--help" ShowHelp( , aArgs ) RETURN ENDIF FOR EACH arg IN aArgs IF ! Empty(arg) IF ( idx := At( "=", arg ) ) == 0 cArgName := arg arg := "" ELSE cArgName := Left( arg, idx - 1 ) arg := SubStr( arg, idx + 1 ) ENDIF DO CASE CASE cArgName == "-source" ; p_hsSwitches[ "basedir" ] := arg + iif( Right( arg, 1 ) == hb_ps(), "", hb_ps() ) CASE cArgName == "-format" IF arg == "" .OR. hb_AScan( p_hsSwitches[ "format-list" ], arg, , , .T. ) == 0 ShowHelp( "Unknown format option '" + arg + "'" ) RETURN ELSE IF arg == "all" p_hsSwitches[ "format" ] := p_hsSwitches[ "format-list" ] ELSE AAdd( p_hsSwitches[ "format" ], arg ) ENDIF END CASE cArgName == "-output-single" ; p_hsSwitches[ "output" ] := "single" CASE cArgName == "-output-category" ; p_hsSwitches[ "output" ] := "category" CASE cArgName == "-output-entry" ; p_hsSwitches[ "output" ] := "entry" CASE cArgName == "-include-doc-source" ; p_hsSwitches[ "include-doc-source" ] := .T. CASE cArgName == "-include-doc-version" ; p_hsSwitches[ "include-doc-version" ] := .T. OTHERWISE IF hb_AScan( p_hsSwitches[ "format-list" ], SubStr( cArgName, 2 ), , , .T. ) > 0 IF SubStr( cArgName, 2 ) == "all" p_hsSwitches[ "format" ] := p_hsSwitches[ "format-list" ] ELSE AAdd( p_hsSwitches[ "format" ], SubStr( cArgName, 2 ) ) ENDIF ELSE ShowHelp( "Unknown option:" + cArgName + iif( Len(arg) > 0, "=" + arg, "") ) RETURN ENDIF ENDCASE ENDIF NEXT // load hbextern.ch FileEval( p_hsSwitches[ "basedir" ] + "include" + hb_ps() + "hbextern.ch", ; {| c | iif( Left( c, Len( "EXTERNAL " ) ) == "EXTERNAL ", ; AAdd( p_hsSwitches[ "hbextern.ch" ], SubStr( c, Len( "EXTERNAL " ) + 1 ) ), ; ) } ) ASort( p_hsSwitches[ "hbextern.ch" ] ) aContent := {} AEval( ; {; p_hsSwitches[ "basedir" ] + "doc", ; p_hsSwitches[ "basedir" ] + "doc" + hb_ps() + "en", ; iif( p_hsSwitches[ "source" ], p_hsSwitches[ "basedir" ] + "source", NIL ), ; iif( p_hsSwitches[ "contribs" ], p_hsSwitches[ "basedir" ] + "contrib", NIL ), ; }, ; {| c | iif( ! Empty( c ), ProcessFolder( c, @aContent ), ) } ) OutStd( hb_ntos( Len( aContent ) ) + " items found" + hb_eol() ) OutStd( hb_eol() ) ASort( aContent, , , {| oL, oR | ; hb_ntos( oL:CategoryIndex( oL:Category ) ) + " " + hb_ntos( oL:SubcategoryIndex( oL:Category, oL:Subcategory ) ) + Chr(1) + oL:Name + " " ; <= ; hb_ntos( oR:CategoryIndex( oR:Category ) ) + " " + hb_ntos( oR:SubcategoryIndex( oR:Category, oR:Subcategory ) ) + Chr(1) + oR:Name + " " ; } ) // TODO: what is this for? it is sorting the category sub-arrays and removing empty (?) sub-arrays, but why? FOR idx := 1 TO Len( p_aCategories ) IF ! Empty( p_aCategories[ idx ] ) IF Len( p_aCategories[ idx ] ) == 4 // category, list of subcategory, list of entries, handle FOR idx2 := Len( p_aCategories[ idx ][ 3 ] ) TO 1 STEP -1 IF HB_ISARRAY( p_aCategories[ idx ][ 3 ][ idx2 ] ) ASort( p_aCategories[ idx ][ 3 ][ idx2 ], , , ; {| oL, oR | ; hb_ntos( oL:CategoryIndex( oL:Category ) ) + " " + hb_ntos( oL:SubcategoryIndex( oL:Category, oL:Subcategory ) ) + " " + oL:Name ; <= ; hb_ntos( oR:CategoryIndex( oR:Category ) ) + " " + hb_ntos( oR:SubcategoryIndex( oR:Category, oR:Subcategory ) ) + " " + oR:Name ; } ) ELSE ASize( ADel( p_aCategories[ idx ][ 2 ], idx2 ), Len( p_aCategories[ idx ][ 2 ] ) - 1 ) ASize( ADel( p_aCategories[ idx ][ 3 ], idx2 ), Len( p_aCategories[ idx ][ 3 ] ) - 1 ) ENDIF NEXT ELSE OutStd( "Index", idx, " is not length 4 but rather", Len( p_aCategories[ idx ] ), hb_eol() ) ENDIF ENDIF NEXT IF Len( p_hsSwitches[ "format" ] ) == 0 p_hsSwitches[ "format" ] := { "text" } ENDIF FOR idx2 := 1 TO Len( p_hsSwitches[ "format" ] ) cFormat := p_hsSwitches[ "format" ][ idx2 ] IF !( cFormat == "all" ) OutStd( "Output as " + cFormat + hb_eol() ) DO CASE CASE p_hsSwitches[ "output" ] == "single" oDocument := &("Generate" + cFormat + "()"):NewDocument( cFormat, "harbour", "Harbour Reference Guide" ) FOR idx := 1 TO Len( aContent ) IF Right( aContent[ idx ]:sourcefile_, Len( "1stread.txt" ) ) == "1stread.txt" oDocument:AddEntry( aContent[ idx ] ) idx := Len( aContent ) ENDIF NEXT FOR idx := 1 TO Len( aContent ) IF Right( aContent[ idx ]:sourcefile_, Len( "1stread.txt" ) ) == "1stread.txt" ELSE oDocument:AddEntry( aContent[ idx ] ) ENDIF NEXT oDocument:Generate() oDocument := NIL CASE p_hsSwitches[ "output" ] == "category" oIndex := &("Generate" + cFormat + "()"):NewIndex( cFormat, "harbour", "Harbour Reference Guide" ) FOR idx := 1 TO Len( aContent ) IF Right( aContent[ idx ]:sourcefile_, Len( "1stread.txt" ) ) == "1stread.txt" IF oIndex != NIL oIndex:AddEntry( aContent[ idx ] ) ENDIF idx := Len( aContent ) ENDIF NEXT FOR idx3 := 1 TO Len( p_aCategories ) IF ! Empty( p_aCategories[ idx3 ] ) p_aCategories[ idx3 ][ 4 ] := Filename( p_aCategories[ idx3 ][ 1 ] ) //~ oIndex:BeginSection( p_aCategories[ idx3 ][ 1 ], p_aCategories[ idx3 ][ 4 ] ) //~ oIndex:EndSection( p_aCategories[ idx3 ][ 1 ], p_aCategories[ idx3 ][ 4 ] ) ENDIF NEXT FOR idx3 := 1 TO Len( p_aCategories ) IF ! Empty( p_aCategories[ idx3 ] ) oDocument := &("Generate" + cFormat + "()"):NewDocument( cFormat, p_aCategories[ idx3 ][ 4 ], "Harbour Reference Guide - " + p_aCategories[ idx3 ][ 1 ] ) IF oIndex != NIL oIndex:BeginSection( p_aCategories[ idx3 ][ 1 ], oDocument:cFilename ) ENDIF oDocument:BeginSection( p_aCategories[ idx3 ][ 1 ], oDocument:cFilename ) FOR idx := 1 TO Len( p_aCategories[ idx3 ][ 3 ] ) IF ! Empty( p_aCategories[ idx3 ][ 3 ][ idx ] ) ASort( p_aCategories[ idx3 ][ 3 ][ idx ], , , {| oL, oR | oL:Name <= oR:Name } ) IF Len( p_aCategories[ idx3 ][ 2 ][ idx ] ) > 1 .OR. Len( p_aCategories[ idx3 ][ 2 ][ idx ] ) > 0 IF oIndex != NIL oIndex:BeginSection( p_aCategories[ idx3 ][ 2 ][ idx ], oDocument:cFilename ) ENDIF oDocument:BeginSection( p_aCategories[ idx3 ][ 2 ][ idx ], oDocument:cFilename ) ENDIF FOR idx4 := 1 TO Len( p_aCategories[ idx3 ][ 3 ][ idx ] ) IF ! Empty( p_aCategories[ idx3 ][ 3 ][ idx ][ idx4 ] ) IF Right( p_aCategories[ idx3 ][ 3 ][ idx ][ idx4 ]:sourcefile_, Len( "1stread.txt" ) ) == "1stread.txt" ELSE IF oIndex != NIL oIndex:AddReference( p_aCategories[ idx3 ][ 3 ][ idx ][ idx4 ] ) ENDIF oDocument:AddEntry( p_aCategories[ idx3 ][ 3 ][ idx ][ idx4 ] ) IF oIndex != NIL oDocument:AddReference( "Index", oIndex:cFilename ) // this kind of works; the reference is outputed but it is not what I meant oDocument:AddReference( p_aCategories[ idx3 ][ 1 ], oIndex:cFilename, p_aCategories[ idx3 ][ 4 ] ) ENDIF ENDIF ENDIF NEXT IF Len( p_aCategories[ idx3 ][ 2 ][ idx ] ) > 1 .OR. Len( p_aCategories[ idx3 ][ 2 ][ idx ] ) > 0 IF oIndex != NIL oIndex:EndSection( p_aCategories[ idx3 ][ 2 ][ idx ], oDocument:cFilename ) ENDIF oDocument:EndSection( p_aCategories[ idx3 ][ 2 ][ idx ], oDocument:cFilename ) ENDIF ENDIF NEXT IF oIndex != NIL oIndex:EndSection( p_aCategories[ idx3 ][ 1 ], oDocument:cFilename ) ENDIF oDocument:EndSection( p_aCategories[ idx3 ][ 1 ], oDocument:cFilename ) oDocument:Generate() ENDIF NEXT CASE p_hsSwitches[ "output" ] == "entry" FOR idx := 1 TO Len( aContent ) oDocument := &("Generate" + cFormat + "()"):NewDocument( cFormat, aContent[ idx ]:filename, "Harbour Reference Guide" ) IF oIndex != NIL oIndex:AddEntry( aContent[ idx ] ) ENDIF oDocument:AddEntry( aContent[ idx ] ) oDocument:Generate() NEXT ENDCASE oDocument := NIL IF oIndex != NIL oIndex:Generate() oIndex := NIL ENDIF ENDIF NEXT OutStd( hb_eol() ) RETURN STATIC PROCEDURE ProcessFolder( cFolder, aContent ) // this is a recursive procedure LOCAL aFiles LOCAL nLen LOCAL idx LOCAL cExt //~ OutStd( ">>> " + cFolder + hb_eol() ) cFolder += hb_ps() aFiles := Directory( cFolder + hb_osFileMask(), "D" ) IF ( nLen := Len( aFiles ) ) > 0 FOR idx := 1 TO nLen IF aFiles[ idx ][F_ATTR ] == "D" IF !( aFiles[ idx ][ F_NAME ] == "." ) .AND. ; !( aFiles[ idx ][ F_NAME ] == ".." ) IF ( p_hsSwitches[ "source" ] .OR. p_hsSwitches[ "contribs" ] ) /* .AND. ; hb_AScan( s_aSkipDirs, {| d | Lower( d ) == Lower( aFiles[ idx ][ F_NAME ] ) } ) == 0 */ ProcessFolder( cFolder + aFiles[ idx ][ F_NAME ], @aContent ) ENDIF ENDIF ELSEIF hb_AScan( s_aExclusions, {| f | Lower( f ) == Lower( aFiles[ idx ][ F_NAME ] ) } ) == 0 hb_FNameSplit( aFiles[ idx ][ F_NAME ], , , @cExt ) IF Lower( cExt ) == ".txt" IF ! ProcessFile( cFolder + aFiles[ idx ][ F_NAME ], @aContent ) EXIT ENDIF ENDIF ENDIF NEXT ENDIF RETURN STATIC FUNCTION ProcessFile( cFile, aContent ) LOCAL aHandle := { 0, 0 } // file handle and position LOCAL cSectionName LOCAL cVersion LOCAL o LOCAL nOldContentLen := Len( aContent ) IF ( aHandle[ 1 ] := FOpen( cFile ) ) == F_ERROR OutErr( "error: could not open " + cFile + ", " + hb_ntos( Abs( aHandle[ 1 ] ) ) + hb_eol() ) RETURN .F. ENDIF IF ! FReadLn( @aHandle, "" ) // assume first line is ID comment prefix //~ FClose( aHandle[ 1 ] ) //~ RETURN .F. ENDIF IF ! FReadLn( @aHandle, @cVersion ) // assume second line is ID //~ FClose( aHandle[ 1 ] ) //~ RETURN .F. ENDIF o := Entry():New( "Template" ) DO WHILE FReadSection( aHandle, @cSectionName, , o ) IF o:IsField( @cSectionName, TPL_START ) o := Entry():New( "Template" ) ProcessBlock( aHandle, @aContent, cFile, cSectionName, @cVersion, @o ) ENDIF ENDDO FClose( aHandle[ 1 ] ) IF ( Len( aContent ) - nOldContentLen ) > 0 OutStd( "> " + cFile + " (" + hb_ntos( Len( aContent ) - nOldContentLen ) + " items)" + hb_eol() ) ENDIF RETURN .T. STATIC PROCEDURE ProcessBlock( aHandle, aContent, cFile, cType, cVersion, o ) LOCAL cSectionName LOCAL cSection LOCAL lAccepted := .T. LOCAL cSource LOCAL idxCategory := -1 LOCAL idxSubCategory := -1 LOCAL cSourceFile cSourceFile := StrTran( ".." + hb_ps() + cFile /* SubStr( cFile, Len( p_hsSwitches[ "basedir" ] + hb_ps() ) ) */, iif( hb_ps() == "\", "/", "\" ), hb_ps() ) o:type_ := cType o:sourcefile_ := cSourceFile o:sourcefileversion_ := cVersion o:Template := "?TEMPLATE?" o:Name := "?NAME?" DO WHILE FReadSection( aHandle, @cSectionName, @cSection, @o ) DO CASE CASE cSectionName == "TEMPLATE" IF o:IsTemplate( cSection ) o:SetTemplate( cSection ) ELSE AddErrorCondition( cFile, "Unknown TEMPLATE '" + cSection + "'" ) // + "' (line " + hb_ntos( aHandle[ 2 ] ) + ")" // exclude link number, it reports tonnes of entries lAccepted := .F. EXIT ENDIF OTHERWISE IF Len( cSectionName ) == 0 ELSEIF o:IsField( cSectionName ) DO CASE CASE o:IsField( cSectionName, TPL_START ) AddErrorCondition( cFile, "Encountered another section '" + cSection, aHandle[ 2 ] ) lAccepted := .F. EXIT CASE o:IsField( cSectionName, TPL_END ) EXIT CASE ! Empty( o:&cSectionName ) AddErrorCondition( cFile, "Duplicate " + cSectionName, aHandle[ 2 ] ) lAccepted := .F. CASE cSectionName == "CATEGORY" IF ( idxCategory := hb_AScan( p_aCategories, {| c | ! Empty( c ) .AND. ( iif( HB_ISCHAR( c ), Lower( c ) == Lower( cSection ), Lower( c[ 1 ] ) == Lower( cSection ) ) ) } ) ) == 0 AddErrorCondition( cFile, "Unknown CATEGORY '" + cSection + "' for template '" + o:Template, aHandle[ 2 ] ) lAccepted := .F. ENDIF CASE cSectionName == "SUBCATEGORY" .AND. o:IsField( "SUBCATEGORY" ) IF idxCategory <= 0 .OR. o:Category == "" AddErrorCondition( cFile, "SUBCATEGORY '" + cSection + "' defined before CATEGORY", aHandle[ 2 ] ) lAccepted := .F. ELSEIF ( idxSubCategory := hb_AScan( p_aCategories[ idxCategory ][ 2 ], {| c | ! ( c == NIL ) .AND. ( iif( HB_ISCHAR( c ), Lower( c ) == Lower( cSection ), Lower( c[ 1 ] ) == Lower( cSection ) ) ) } ) ) == 0 AddErrorCondition( cFile, "Unknown SUBCATEGORY '" + p_aCategories[ idxCategory ][ 1 ] + "-" + cSection, aHandle[ 2 ] ) lAccepted := .F. ENDIF CASE o:IsField( "RETURNS" ) .AND. cSectionName == "RETURNS" .AND. ( ; Empty( cSection ) .OR. ; Lower( cSection ) == "nil" .OR. ; Lower( cSection ) == "none" .OR. ; Lower( cSection ) == "none." ) AddErrorCondition( cFile, "'" + o:Name + "' is identified as template " + o:Template + " but has no RETURNS value (" + cSection + ")", aHandle[ 2 ] - 1 ) lAccepted := .F. CASE ! o:IsConstraint( cSectionName, cSection ) cSource := cSectionName + " is '" + iif( Len( cSection ) <= 20, cSection, Left( StrTran( cSection, hb_eol() ), 20 ) + "..." ) + "', should be one of: " //~ cSource := hb_HKeyAt( hsTemplate, idx ) + " should be one of: " AEval( &( "p_a" + cSectionName ), {| c, n | cSource += iif( n == 1, "", "," ) + c } ) AddErrorCondition( cFile, cSource, aHandle[ 2 ] - 1 ) OTHERWISE ENDCASE IF lAccepted o:&cSectionName := Decode( cSectionName, , cSection ) ENDIF ELSE AddErrorCondition( cFile, "Using template '" + o:Template + "' encountered an unexpected section '" + cSectionName, aHandle[ 2 ] ) lAccepted := .F. ENDIF ENDCASE ENDDO IF lAccepted lAccepted := o:IsComplete( @cSource ) IF ! lAccepted AddErrorCondition( cFile, "Missing sections: '" + cSource + "'", aHandle[ 2 ] ) lAccepted := .F. ENDIF ENDIF IF ! lAccepted ELSEIF o:Template == "Function" .AND. ( ; Empty( o:Returns ) .OR. ; Lower( o:Returns ) == "nil" .OR. ; Lower( o:Returns ) == "none" .OR. ; Lower( o:Returns ) == "none." ) AddErrorCondition( cFile, "'" + o:Name + "' is identified as template " + o:Template + " but has no RETURNS value (" + o:Returns + ")", aHandle[ 2 ] ) //~ lAccepted := .F. ELSE IF ! ( ; /* Lower( hsBlock[ "CATEGORY" ] ) == "document" .OR. */ ; /* ! ( hsBlock[ "SUBCODE" ] == "" ) .OR. */ ; .F. ) cSectionName := Parse( Upper( o:Name ), "(" ) IF hb_AScan( p_hsSwitches[ "hbextern.ch" ], cSectionName, , , .T. ) > 0 AAdd( p_hsSwitches[ "in hbextern" ], cSectionName ) ELSE AAdd( p_hsSwitches[ "not in hbextern" ], cSectionName + "; " + cFile ) ENDIF //~ OutStd( " > " + cSectionName + hb_eol() ) ENDIF IF p_hsSwitches[ "include-doc-source" ] o:Files += hb_eol() + o:sourcefile_ + iif( p_hsSwitches[ "include-doc-version" ], " (" + o:sourcefileversion_ + ")", "" ) ENDIF o:filename := Filename( o:Name ) AAdd( aContent, o ) IF idxSubCategory == -1 .AND. ( ! o:IsField( "SUBCATEGORY" ) .OR. ! o:IsRequired( "SUBCATEGORY" ) ) //.AND. idxSubCategory == -1 idxSubCategory := o:SubcategoryIndex( o:Category, "" ) IF idxSubCategory == -1 idxSubCategory := 1 ENDIF ENDIF IF ! HB_ISARRAY( p_aCategories[ idxCategory ][ 3 ][ idxSubCategory ] ) p_aCategories[ idxCategory ][ 3 ][ idxSubCategory ] := {} ENDIF AAdd( p_aCategories[ idxCategory ][ 3 ][ idxSubCategory ], o ) ENDIF RETURN STATIC FUNCTION FReadSection( aHandle, cSectionName, cSection, o ) LOCAL nPosition LOCAL cBuffer LOCAL nLastIndent LOCAL lPreformatted := .F. LOCAL lLastPreformatted cSectionName := cSection := "" IF FReadLn( @aHandle, @cSectionName ) cSectionName := LTrim( SubStr( cSectionName, 3 ) ) IF Left( cSectionName, 1 ) == p_hsSwitches[ "DELIMITER" ] .AND. ; Right( cSectionName, 1 ) == p_hsSwitches[ "DELIMITER" ] cSectionName := SubStr( cSectionName, 1 + Len( p_hsSwitches[ "DELIMITER" ] ), Len( cSectionName ) - ( 2 * Len( p_hsSwitches[ "DELIMITER" ] ) ) ) IF o:IsField( cSectionName ) lLastPreformatted := lPreformatted := o:IsPreformatted( cSectionName ) nLastIndent := -1 DO WHILE ( nPosition := FSeek( aHandle[ 1 ], 0, FS_RELATIVE ) ), FReadLn( @aHandle, @cBuffer ) // TOFIX: this assumes that every line starts with " *" cBuffer := RTrim( SubStr( cBuffer, 3 ) ) IF Left( LTrim( cBuffer ), 1 ) == p_hsSwitches[ "DELIMITER" ] ; .AND. Right( cBuffer, 1 ) == p_hsSwitches[ "DELIMITER" ] FSeek( aHandle[ 1 ], nPosition, FS_SET ) aHandle[ 2 ]-- // decrement the line number when rewinding the file Exit ELSEIF Len( AllTrim( cBuffer ) ) == 0 IF !( Right( cSection, Len( hb_eol() ) ) == hb_eol() ) cSection += hb_eol() ENDIF nLastIndent := -1 ELSEIF AllTrim( cBuffer ) == "
" IF !( Right( cSection, Len( hb_eol() ) ) == hb_eol() ) .OR. lPreformatted cSection += hb_eol() ENDIF cSection += "
" //+ hb_eol() lLastPreformatted := lPreformatted lPreformatted := .T. ELSEIF AllTrim( cBuffer ) == "
" IF !( Right( cSection, Len( hb_eol() ) ) == hb_eol() ) .OR. lPreformatted cSection += hb_eol() ENDIF cSection += "" + hb_eol() lPreformatted := lLastPreformatted ELSEIF nLastIndent != ( Len( cBuffer ) - Len( LTrim( cBuffer ) ) ) .OR. lPreformatted .OR. Right( cBuffer, Len( "" ) ) == "" IF Right( cBuffer, Len( "" ) ) == "" cBuffer := Left( cBuffer, Len( cBuffer ) - Len( "" ) ) ENDIF nLastIndent := ( Len( cBuffer ) - Len( LTrim( cBuffer ) ) ) IF !( Right( cSection, Len( hb_eol() ) ) == hb_eol() ) cSection += hb_eol() ENDIF cSection += iif( lPreformatted, cBuffer, AllTrim( cBuffer ) ) ELSE cSection += " " + AllTrim( cBuffer ) ENDIF ENDDO ENDIF ENDIF ELSE RETURN .F. ENDIF DO WHILE Left( cSection, Len( hb_eol() ) ) == hb_eol() cSection := SubStr( cSection, Len( hb_eol() ) + 1 ) ENDDO DO WHILE Right( cSection, Len( hb_eol() ) ) == hb_eol() cSection := Left( cSection, Len( cSection ) - Len( hb_eol() ) ) ENDDO IF lPreformatted .AND. Lower( Right( cSection, Len( "" ) ) ) == "" cSection := Left( cSection, Len( cSection ) - Len( "" ) ) DO WHILE Right( cSection, Len( hb_eol() ) ) == hb_eol() cSection := Left( cSection, Len( cSection ) - Len( hb_eol() ) ) ENDDO ENDIF RETURN .T. STATIC PROCEDURE FileEval( acFile, bBlock, nMaxLine ) LOCAL aHandle := { 0, 0 } LOCAL cBuffer LOCAL lCloseFile := .F. LOCAL xResult hb_default( @nMaxLine, 256 ) IF HB_ISSTRING( acFile ) lCloseFile := .T. IF ( aHandle[ 1 ] := FOpen( acFile ) ) == F_ERROR RETURN ENDIF ELSEIF HB_ISNUMERIC( acFile ) aHandle[ 1 ] := acFile ELSE aHandle := acFile ENDIF DO WHILE FReadLn( @aHandle, @cBuffer, nMaxLine ) xResult := Eval( bBlock, cBuffer ) IF xResult != NIL .AND. HB_ISLOGICAL( xResult ) .AND. ! xResult EXIT ENDIF ENDDO IF lCloseFile FClose( aHandle ) ENDIF RETURN STATIC FUNCTION FReadLn( aHandle, cBuffer, nMaxLine ) STATIC s_aEOL := { Chr( 13 ) + Chr( 10 ), Chr( 10 ), Chr( 13 ) } LOCAL cLine, nSavePos, nEol, nNumRead, nLenEol, idx hb_default( @nMaxLine, 256 ) cBuffer := "" nSavePos := FSeek( aHandle[ 1 ], 0, FS_RELATIVE ) cLine := Space( nMaxLine ) nNumRead := FRead( aHandle[ 1 ], @cLine, hb_BLen( cLine ) ) cLine := hb_BLeft( cLine, nNumRead ) nEol := 0 FOR idx := 1 To Len( s_aEOL ) IF ( nEol := At( s_aEOL[ idx ], cLine ) ) > 0 nLenEol := hb_BLen( s_aEOL[ idx ] ) - 1 Exit ENDIF NEXT IF nEol == 0 cBuffer := cLine ELSE cBuffer := Left( cLine, nEol - 1 ) FSeek( aHandle[ 1 ], nSavePos + hb_BLen( cBuffer ) + 1 + nLenEol, FS_SET ) ENDIF aHandle[ 2 ]++ RETURN nNumRead != 0 FUNCTION Decode( cType, hsBlock, cKey ) LOCAL cCode LOCAL cResult LOCAL idx IF cKey != NIL .AND. hsBlock != NIL .AND. hb_HHasKey( hsBlock, cKey ) cCode := hsBlock[ cKey ] ELSE cCode := cKey ENDIF DO CASE CASE cType == "STATUS" IF "," $ cCode .AND. hb_AScan( p_aStatus, Parse( cCode, "," ) ) > 0 cResult := "" DO WHILE Len( cCode ) > 0 cResult += hb_eol() + Decode( cType, hsBlock, Parse( @cCode, "," ) ) ENDDO RETURN SubStr( cResult, Len( hb_eol() ) + 1 ) ENDIF IF ( idx := hb_AScan( p_aStatus, {| a | a[ 1 ] == cCode } ) ) > 0 RETURN p_aStatus[ idx ][ 2 ] ELSEIF Len( cCode ) > 1 RETURN cCode ELSEIF Len( cCode ) > 0 RETURN "Unknown 'STATUS' code: '" + cCode + "'" ELSE RETURN ATail( p_aStatus )[ 2 ] ENDIF CASE cType == "PLATFORMS" IF "," $ cCode .AND. hb_AScan( p_aPlatforms, Parse( cCode, "," ) ) > 0 cResult := "" DO WHILE Len( cCode ) > 0 cResult += hb_eol() + Decode( cType, hsBlock, Parse( @cCode, "," ) ) ENDDO RETURN SubStr( cResult, Len( hb_eol() ) + 1 ) ENDIF IF ( idx := hb_AScan( p_aPlatforms, {| a | a[ 1 ] == cCode } ) ) > 0 RETURN p_aPlatforms[ idx ][ 2 ] ELSE RETURN "Unknown 'PLATFORMS' code: '" + cCode + "'" ENDIF CASE cType == "COMPLIANCE" IF "," $ cCode .AND. hb_AScan( p_aCompliance, Parse( cCode, "," ) ) > 0 cResult := "" DO WHILE Len( cCode ) > 0 cResult += hb_eol() + Decode( cType, hsBlock, Parse( @cCode, "," ) ) ENDDO RETURN SubStr( cResult, Len( hb_eol() ) + 1 ) ENDIF IF ( idx := hb_AScan( p_aCompliance, {| a | a[ 1 ] == cCode } ) ) > 0 RETURN p_aCompliance[ idx ][ 2 ] ELSE RETURN "Unknown 'COMPLIANCE' code: '" + cCode + "'" ENDIF DO CASE CASE Empty( cCode ) ; RETURN cCode CASE cCode == "C" ; RETURN "This is CA-Cl*pper v5.2 compliant" CASE cCode == "C(array)" ; RETURN "This is CA-Cl*pper v5.2 compliant except that arrays in Harbour can have an unlimited number of elements" CASE cCode == "C(menu)" ; RETURN "This is CA-Cl*pper v5.2 compliant except that menus (internally arrays) in Harbour can have an unlimited number of elements" CASE cCode == "C52U" ; RETURN "This is an undocumented CA-Cl*pper v5.2 function and is only visible if source was compiled with the HB_CLP_UNDOC flag" CASE cCode == "C52S" ; RETURN "? verbage: This is an CA-Cl*pper v5.2 compliant and is only visible if source was compiled with the HB_CLP_STRICT flag" CASE cCode == "C53" ; RETURN "This is CA-Cl*pper v5.3 compliant and is only visible if source was compiled with the HB_COMPAT_C53 flag" CASE cCode == "H" ; RETURN "This is Harbour specific" CASE cCode == "NA" ; RETURN "Not applicable" OTHERWISE ; RETURN "Unknown 'COMPLIANCE' code: '" + cCode + "'" ENDCASE CASE cType == "NAME" IF hsBlock == NIL RETURN cCode ELSEIF ! hb_HHasKey( hsBlock, "RETURNS" ) RETURN hsBlock[ "NAME" ] ELSEIF Empty( hsBlock[ "RETURNS" ] ) .OR. ; Lower( hsBlock[ "RETURNS" ] ) == "nil" .OR. ; Lower( hsBlock[ "RETURNS" ] ) == "none" .OR. ; Lower( hsBlock[ "RETURNS" ] ) == "none." hsBlock[ "RETURNS" ] := "" DO CASE CASE Lower( hsBlock[ "CATEGORY" ] ) == "document" RETURN hsBlock[ "NAME" ] OTHERWISE IF Lower( hsBlock[ "TEMPLATE" ] ) == "function" .OR. Lower( hsBlock[ "TEMPLATE" ] ) == "procedure" RETURN "Procedure " + hsBlock[ "NAME" ] ELSE RETURN LTrim( hsBlock[ "SUBCATEGORY" ] + " " ) + hsBlock[ "CATEGORY" ] + " " + hsBlock[ "NAME" ] ENDIF ENDCASE ELSE DO CASE CASE ! Empty( hsBlock[ "NAME" ] ) RETURN "Function " + hsBlock[ "NAME" ] OTHERWISE RETURN "Unknown 'CATEGORY': " + hsBlock[ "CATEGORY" ] ENDCASE ENDIF ENDCASE RETURN /* cType + "=" + */cCode PROCEDURE ShowSubHelp( xLine, nMode, nIndent, n ) LOCAL cIndent := Space( nIndent ) IF xLine != NIL DO CASE CASE HB_ISNUMERIC( xLine ) nMode := xLine CASE HB_ISBLOCK( xLine ) Eval( xLine ) CASE HB_ISARRAY( xLine ) IF nMode == 2 OutStd( cIndent + Space( 2 ) ) ENDIF AEval( xLine, {| x, n | ShowSubHelp( x, @nMode, nIndent + 2, n ) } ) IF nMode == 2 OutStd( hb_eol() ) ENDIF OTHERWISE DO CASE CASE nMode == 1 ; OutStd( cIndent + xLine ) ; OutStd( hb_eol() ) CASE nMode == 2 ; OutStd( iif( n > 1, ", ", "") + xLine ) OTHERWISE ; OutStd( "(" + hb_ntos( nMode ) + ") " ) ; OutStd( xLine ) ; OutStd( hb_eol() ) ENDCASE ENDCASE ENDIF RETURN STATIC FUNCTION HBRawVersion() RETURN StrTran( Version(), "Harbour " ) PROCEDURE ShowHelp( cExtraMessage, aArgs ) LOCAL nMode := 1 #define OnOrOff( b ) iif( b, "excluded", "included" ) #define YesOrNo( b ) iif( b, "yes", "no" ) #define IsDefault( b ) iif( b, "; default", "" ) LOCAL aHelp DO CASE CASE Empty( aArgs ) .OR. Len( aArgs ) <= 1 .OR. Empty( aArgs[ 1 ] ) aHelp := { ; cExtraMessage, ; "Harbour Document Compiler (hbdoc) " + HBRawVersion(), ; "Copyright (c) 1999-2010, http://harbour-project.org/", ; "", ; "Syntax:", ; "", ; { "hbdoc [options]" }, ; "", ; "Options:", ; { ; "-? or --help // this screen", ; "-?