Files
harbour-core/harbour/extras/hbdoc/hbdoc.prg
Viktor Szakats 3f4cf28f68 2012-11-10 20:55 UTC+0100 Viktor Szakats (harbour syenar.net)
* src/rtl/hbgtcore.c
    * changed HB_GTI_COMPATBUFFER default to be Clipper
      compatible. INCOMPATIBLE compared to 3.2 versions,
      but restores the compatibility with previous ones
      and Clipper. Applications using Unicode, must now 
      manually change these buffers to be Unicode 
      compatible using:
         #include "hbgtinfo.ch"
         hb_gtInfo( HB_GTI_COMPATBUFFER, .F. )

  * include/assert.ch
  * include/hbclass.ch
  * contrib/rddads/ads.ch
  * contrib/xhb/hbdll.ch
  * contrib/xhb/hblog.ch
  * contrib/xhb/ttable.ch
    ! use 'END' instead of 'ENDIF' consistently
      in command translated code. (most of these
      is result of recent regression)

  * contrib/gtwvg/class.prg
  * contrib/gtwvg/combobox.prg
  * contrib/gtwvg/crt.prg
  * contrib/gtwvg/menubar.prg
  * contrib/gtwvg/statbar.prg
  * contrib/gtwvg/static.prg
  * contrib/gtwvg/syswnd.prg
  * contrib/gtwvg/tests/_modal.prg
  * contrib/gtwvg/wnd.prg
  * contrib/hbgd/tests/test_out.prg
  * contrib/hbmysql/tmysql.prg
  * contrib/xhb/decode.prg
  * contrib/xhb/xhbtedit.prg
  * extras/hbdoc/hbdoc.prg
  * extras/httpsrv/cgifunc.prg
  * extras/httpsrv/session.prg
    % deleted excessive parenthesis
    ! fixed some '- 1' to '-1' in older hbformatted
      code

  * extras/gtwvw/tests/_wvwmous.prg
  * extras/gtwvw/tests/wvt2wvw.ch
  * extras/hbvpdf/core.prg
  * extras/httpsrv/uhttpd.prg
  * extras/rddado/adordd.ch
  * include/hbsix.ch
    * minor cleanups
2012-11-10 20:04:37 +00:00

1160 lines
41 KiB
Plaintext

/*
* $Id$
*/
/*
* Harbour Project source code:
* Document generator
*
* Copyright 2009 April White <april users.sourceforge.net>
* www - http://harbour-project.org
*
* Portions of this project are based on hbdoc
* Copyright 1999-2003 Luiz Rafael Culik <culikr@uol.com.br>
*
* <TODO: list gen... methods used>
*
* 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 '<fixed>' / </fixed> as an non-conformance condition
ntf: this may be okay for EXAMPLES and TESTS but this is also used
within other sections, much like <table>
todo - look for embedded 'fixed'
these files have <tables>
*..\..\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 </par>; 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()
#define OnOrOff( b ) iif( b, "excluded", "included" )
#define YesOrNo( b ) iif( b, "yes", "no" )
#define IsDefault( b ) iif( b, "; default", "" )
STATIC sc_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" => {}, ;
"<eol>" => 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
ENDIF
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
hb_ADel( p_aCategories[ idx ][ 2 ], idx2, .T. )
hb_ADel( p_aCategories[ idx ][ 3 ], idx2, .T. )
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. AScan( s_aSkipDirs, {| d | Lower( d ) == Lower( aFiles[ idx ][ F_NAME ] ) } ) == 0 */
ProcessFolder( cFolder + aFiles[ idx ][ F_NAME ], @aContent )
ENDIF
ENDIF
ELSEIF AScan( sc_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 := 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 := 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 ) == "<table>"
IF !( Right( cSection, Len( hb_eol() ) ) == hb_eol() ) .OR. lPreformatted
cSection += hb_eol()
ENDIF
cSection += "<table>" // + hb_eol()
lLastPreformatted := lPreformatted
lPreformatted := .T.
ELSEIF AllTrim( cBuffer ) == "</table>"
IF !( Right( cSection, Len( hb_eol() ) ) == hb_eol() ) .OR. lPreformatted
cSection += hb_eol()
ENDIF
cSection += "</table>" + hb_eol()
lPreformatted := lLastPreformatted
ELSEIF nLastIndent != ( Len( cBuffer ) - Len( LTrim( cBuffer ) ) ) .OR. lPreformatted .OR. Right( cBuffer, Len( "</par>" ) ) == "</par>"
IF Right( cBuffer, Len( "</par>" ) ) == "</par>"
cBuffer := Left( cBuffer, Len( cBuffer ) - Len( "</par>" ) )
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( "</fixed>" ) ) ) == "</fixed>"
cSection := Left( cSection, Len( cSection ) - Len( "</fixed>" ) )
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, "," ), , , .T. ) > 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 := 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, "," ), , , .T. ) > 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 := 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, "," ), , , .T. ) > 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 := 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
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", ;
"-? <option> or --help <option> // help on <option>, <option> is one of:", ;
2, ;
{ "Categories", "Templates", "Compliance", "Platforms" }, ;
1, ;
"-[format=]<type> // output type, default is text, or one of:", ;
2, ;
p_hsSwitches[ "format-list" ], ;
1, ;
"-output-single // output is one file" + IsDefault( p_hsSwitches[ "output" ] == "single" ), ;
"-output-category // output is one file per category" + IsDefault( p_hsSwitches[ "output" ] == "category" ), ;
"-output-entry // output is one file per entry (function, command, etc)" + IsDefault( p_hsSwitches[ "output" ] == "entry" ), ;
"-source=<folder> // source folder, default is .." + hb_ps() + "..", ;
"-include-doc-source // output is to indicate the document source file name", ;
"-include-doc-version // output is to indicate the document source file version", ;
} ;
}
CASE aArgs[ 2 ] == "Categories"
aHelp := { ;
"Defined categories and sub-categories are:", ;
p_aCategories, ;
}
CASE aArgs[ 2 ] == "Templates"
aHelp := { ;
iif( Len( aArgs ) >= 3, aArgs[ 3 ] + " template is:", "Defined templates are:" ), ;
"", ;
{|| ShowTemplatesHelp( iif( Len( aArgs ) >= 3, aArgs[ 3 ], NIL ), p_hsSwitches[ "DELIMITER" ] ) } ;
}
CASE aArgs[ 2 ] == "Compliance"
aHelp := { ;
"Defined 'COMPLIANCE' are:", ;
"", ;
{|| ShowComplianceHelp() } ;
}
CASE aArgs[ 2 ] == "Platforms"
aHelp := { ;
"Defined 'PLATFORMS' are:", ;
"", ;
{|| ShowPlatformsHelp() } ;
}
OTHERWISE
ShowHelp( "Unknown help option" )
RETURN
ENDCASE
// using hbmk2 style
AEval( aHelp, {| x | ShowSubHelp( x, @nMode, 0 ) } )
RETURN
FUNCTION Parse( cVar, xDelimiter )
LOCAL cResult
LOCAL idx
IF HB_ISNUMERIC( xDelimiter )
cResult := Left( cVar, xDelimiter )
cVar := SubStr( cVar, xDelimiter + 1 )
ELSE
IF ( idx := At( xDelimiter, cVar ) ) == 0
cResult := cVar
cVar := ""
ELSE
cResult := Left( cVar, idx - 1 )
cVar := SubStr( cVar, idx + Len( xDelimiter ) )
ENDIF
ENDIF
RETURN cResult
FUNCTION Join( aVar, cDelimiter )
LOCAL cResult := ""
AEval( aVar, {| c, n | cResult += iif( n > 1, cDelimiter, "" ) + c } )
RETURN cResult
STATIC PROCEDURE AddErrorCondition( cFile, cMessage, nLine )
IF p_hsSwitches[ "immediate-errors" ]
OutStd( cFile + ":" + hb_ntos( nLine ) + ": " + cMessage + hb_eol() )
ENDIF
RETURN
FUNCTION Indent( cText, nLeftMargin, nWidth, lRaw )
LOCAL cResult := ""
LOCAL idx
LOCAL cLine
LOCAL aText
hb_default( @lRaw, .F. )
IF nWidth == 0 .OR. lRaw
aText := hb_ATokens( cText, hb_eol() )
idx := 99999
AEval( aText, {| c | iif( Empty( c ), , idx := Min( idx, Len( c ) - Len( LTrim( c ) ) ) ) } )
AEval( aText, {| c, n | aText[ n ] := Space( nLeftMargin ) + SubStr( c, idx + 1 ) } )
cResult := Join( aText, hb_eol() ) + hb_eol() + hb_eol()
ELSE
DO WHILE Len( cText ) > 0
cLine := Parse( @cText, hb_eol() )
IF cLine == "<table>"
lRaw := .T.
ELSEIF cLine == "</table>"
cResult += hb_eol()
lRaw := .F.
ELSEIF lRaw
cResult += Space( nLeftMargin ) + LTrim( cLine ) + hb_eol()
ELSE
DO WHILE Len( cLine ) > nWidth
idx := nWidth + 1
DO WHILE idx > 0
idx--
DO CASE
CASE At( SubStr( cLine, idx, 1 ), " ,;.!?" ) == 0
//
CASE Upper( SubStr( cLine, idx, 3 ) ) == ".T." .OR. Upper( SubStr( cLine, idx, 3 ) ) == ".F."
idx--
CASE Upper( SubStr( cLine, idx - 2, 3 ) ) == ".T." .OR. Upper( SubStr( cLine, idx - 1, 3 ) ) == ".F."
idx -= 3
CASE Upper( SubStr( cLine, idx, 5 ) ) == ".AND." .OR. Upper( SubStr( cLine, idx, 5 ) ) == ".NOT."
idx--
CASE Upper( SubStr( cLine, idx - 4, 5 ) ) == ".AND." .OR. Upper( SubStr( cLine, idx - 4, 5 ) ) == ".NOT."
idx -= 5
CASE Upper( SubStr( cLine, idx, 4 ) ) == ".OR."
idx--
CASE Upper( SubStr( cLine, idx - 3, 4 ) ) == ".OR."
idx -= 4
CASE Upper( SubStr( cLine, idx - 1, 4 ) ) == "i.e."
idx -= 2
CASE Upper( SubStr( cLine, idx - 3, 4 ) ) == "i.e."
idx -= 4
CASE Upper( SubStr( cLine, idx - 1, 4 ) ) == "e.g."
idx -= 2
CASE Upper( SubStr( cLine, idx - 3, 4 ) ) == "e.g."
idx -= 4
CASE Upper( SubStr( cLine, idx - 1, 2 ) ) == "*."
idx -= 2
OTHERWISE
EXIT
ENDCASE
ENDDO
IF idx <= 0
idx := nWidth
ENDIF
cResult += Space( nLeftMargin ) + Left( cLine, idx - iif( SubStr( cLine, idx, 1 ) == " ", 1, 0 ) ) + hb_eol()
cLine := LTrim( SubStr( cLine, idx + 1 ) )
ENDDO
IF Len( cLine ) > 0
cResult += Space( nLeftMargin ) + cLine + hb_eol()
ENDIF
cResult += hb_eol()
ENDIF
ENDDO
ENDIF
RETURN cResult
FUNCTION Filename( cFile, cFormat, nLength )
STATIC s_Files := {}
LOCAL cResult := ""
LOCAL idx
LOCAL char
hb_default( @cFormat, "alnum" )
#ifdef __PLATFORM__DOS
hb_default( @nLength, 8 )
#else
hb_default( @nLength, 0 )
#endif
DO CASE
CASE Lower( cFormat ) == "alnum"
FOR idx := 1 TO Len( cFile )
char := Lower( SubStr( cFile, idx, 1 ) )
IF "0" <= char .AND. char <= "9" .OR. "a" <= char .AND. char <= "z" .OR. char == "_"
cResult += char
IF nLength > 0 .AND. Len( cResult ) == nLength
EXIT
ENDIF
ENDIF
NEXT
OTHERWISE
cResult := cFile
ENDCASE
IF hb_AScan( s_Files, cResult, , , .T. ) == 0
AAdd( s_Files, cResult )
ELSE
#ifdef __PLATFORM__DOS
cResult := Left( cResult, Len( cResult ) - 3 )
#endif
idx := 0
DO WHILE hb_AScan( s_Files, cResult + PadL( hb_ntos( ++idx ), 3, "0" ), , , .T. ) > 0
ENDDO
cResult += PadL( hb_ntos( idx ), 3, "0" )
AAdd( s_Files, cResult )
ENDIF
RETURN cResult