Put 'gbh3' back into the git repository.

[CONFIGURATION]
* BUG: Don't put main/tools in .gitignore.

[HELPER]
* BUG: Put 'gbh3' back into the git repository.
This commit is contained in:
gambas 2019-02-25 00:19:46 +01:00
parent aa66e9bcd5
commit 1f73946947
10 changed files with 854 additions and 1 deletions

2
.gitignore vendored
View file

@ -35,7 +35,7 @@ missing
*.mo
*.gambas
main/trunk_version.h
main/*/gb*3
main/gb*/gb*3
DISABLED*
.startup
.settings

View file

@ -0,0 +1,2 @@
[Desktop Entry]
Icon=./.icon.png

BIN
main/tools/gbh3/.icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

12
main/tools/gbh3/.project Normal file
View file

@ -0,0 +1,12 @@
# Gambas Project File 3.0
# Compiled with Gambas 3.6.90
Title=gbh3
Startup=MMain
Icon=icon.png
Version=3.6.90
VersionFile=1
Description="Extract help comments from C/C++ source files and format them into a .help file."
Authors="(C) 2014 Tobias Boege <tobias@gambas-buch.de>, GPLv2+"
TabSize=2
Language=en_GB
Packager=1

View file

@ -0,0 +1,447 @@
' Gambas module file
Private $sRoot As String
Private $sComponent As String
Private $bVerbose As Boolean
Private Sub PrintError(sErr As String)
Error File.Name(Args[0]); ": error: "; sErr
Quit 1
End
Private Sub PrintMessage(sMsg As String)
If Not $bVerbose Then Return
Error sMsg
End
Public Sub Main()
Dim iInd As Integer, aSources As New String[]
Dim sArg, sRec As String, aRec As String[]
Dim sPath As String
Dim hOut As File
Dim bOnlySources As Boolean
Dim sMsg As String
$sRoot = System.Path
For iInd = 1 To Args.Max
Select Case Args[iInd]
Case "-h", "--help"
Print File.Load("usage")
Quit
Case "-V", "--version"
Print Application.Version
Quit
Case "-L", "--license"
Print File.Load("license")
Quit
Case "-v", "--verbose"
$bVerbose = True
Case "-r", "--root"
$sRoot = Args[iInd + 1]
If Not $sRoot Then PrintError(Args[iInd] & " requires an argument")
Inc iInd
Case "-c", "--component"
$sComponent = Args[iInd + 1]
If Not $sComponent Then PrintError(Args[iInd] & " requires an argument")
Inc iInd
Case "--"
bOnlySources = True
Default
If Not bOnlySources Then
If Args[iInd] Begins "-" Then
PrintError("unknown option: " & Args[iInd])
Endif
Endif
aSources.Add(Args[iInd])
End Select
Next
If aSources.Count = 0 Then aSources.Add(".")
If $sComponent Then
sPath = $sRoot &/ "share/gambas" & System.Version &/ "info" &/ $sComponent & ".help"
PrintMessage("Output to " & sPath)
hOut = Open sPath For Create
Else
hOut = File.Out
Endif
For Each sArg In aSources
If Left(sArg) <> "/" Then
If sArg = "." Then
sArg = Application.Dir
Else
sArg = Application.Dir &/ sArg
Endif
Endif
If IsDir(sArg) Then
aRec = RDir(sArg, "*.{c,cc,cpp}")
For Each sRec In aRec
OneFile(sArg &/ sRec, hOut)
Next
Else
OneFile(sArg, hOut)
Endif
Next
If $sComponent Then
Close #hOut
If Stat(sPath).Size = 0 Then
PrintMessage("Removing void data file")
Try Kill sPath
Endif
Endif
Catch
sMsg = Error.Text & ": " & Error.Backtrace.Join(" ")
Output To Default
PrintError(sMsg)
End
Private Sub AddHelp(cHelp As Collection, sClass As String, sSymbol As String, sHelp As String)
Dim cCol As Collection
PrintMessage("AddHelp: " & sClass & "." & sSymbol)
cCol = cHelp[sClass]
If Not cCol Then
cCol = New Collection
cHelp[sClass] = cCol
Endif
cCol[sSymbol] = sHelp
End
Private Sub OneFile(sPath As String, hOut As File)
Dim hFile As File
Dim sLine As String
Dim aHelp As New String[]
Dim bInsideComments As Boolean
Dim sHelp As String
Dim iPos As Integer
Dim bInsideDesc As Boolean
Dim sMacro As String
Dim iPos2 As Integer
Dim aArg As String[]
Dim sClass As String
Dim sSymbol As String
Dim cHelp As Collection
Dim sDeclareClass As String
Dim cCol As Variant
Dim aClass As String[]
Dim sImpl As String
PrintMessage("Processing " & sPath & "...")
hFile = Open sPath For Input
cHelp = New Collection
For Each sLine In hFile.Lines
sLine = Trim(sLine)
If Not sLine Then Continue
'PrintMessage(sLine)
If sLine Begins "/// " Then
aHelp.Add(Mid$(sLine, 5))
Continue
Else If sLine = "/**G" Or If sLine = "/**" Then
bInsideComments = True
Continue
Else If sLine Begins "/** " Then
aHelp.Add(Mid$(sLine, 5))
bInsideComments = True
Continue
Else If bInsideComments Then
If RTrim(sLine) Ends "*/" Then
bInsideComments = False
Continue
Else
While Left(sLine) = "*"
sLine = Mid$(sLine, 2)
Wend
If Left(sLine) = " " Then sLine = Mid$(sLine, 2)
aHelp.Add(sLine)
Continue
Endif
Endif
If aHelp.Count Then
sHelp = Trim(aHelp[0])
If Len(sHelp) >= 3 And If sHelp Begins "[" And If sHelp Ends "]" Then
sHelp = Mid$(sHelp, 2, -1)
iPos = InStr(sHelp, ".")
If iPos = 0 Then
sClass = sHelp
sSymbol = ""
Else
sClass = Left$(sHelp, iPos - 1)
sSymbol = Mid$(sHelp, iPos + 1)
Endif
aHelp.Remove(0)
AddHelp(cHelp, sClass, sSymbol, aHelp.Join("\n"))
sHelp = ""
Else
sHelp = Trim(aHelp.Join("\n"))
Endif
aHelp.Clear
Endif
If sHelp Then
If sLine Begins "BEGIN_METHOD" Or If sLine Begins "BEGIN_PROPERTY" Then
iPos = InStr(sLine, "(")
If iPos = 0 Then Continue
sLine = Mid$(sLine, iPos + 1)
iPos = InStr(sLine, ",")
If iPos = 0 Then iPos = InStr(sLine, ")")
If iPos = 0 Then Continue
sLine = Left(sLine, iPos - 1)
AddHelp(cHelp, "@", sLine, sHelp)
sHelp = ""
Continue
Endif
Endif
If sLine Begins "GB_DECLARE" Then
bInsideDesc = True
Try sDeclareClass = Scan(sLine, "GB_DECLARE*(\"*\"*")[1]
If Error Then PrintError("Missing class name in GB_DECLARE macro: " & sLine)
Continue
Endif
If bInsideDesc Then
If sLine Begins "GB_END_DECLARE" Then
bInsideDesc = False
Continue
Endif
iPos = InStr(sLine, "(")
If iPos = 0 Then Continue
sMacro = Left(sLine, iPos - 1)
If sMacro Not Begins "GB_" Then Continue
If InStr(sMacro, "_CONSTANT") = 0 And If InStr(sMacro, "_PROPERTY") = 0 And If InStr(sMacro, "_METHOD") = 0 Then Continue
iPos2 = InStr(sLine, ")", iPos + 1)
If iPos2 = 0 Then Continue
aArg = Split(Mid$(sLine, iPos + 1, iPos2 - iPos - 1), ",", Chr$(34))
sSymbol = Trim(aArg[0])
Try sImpl = Trim(aArg[2])
If sHelp Then
AddHelp(cHelp, sDeclareClass, sSymbol, sHelp)
sHelp = ""
Else
If InStr(sMacro, "_CONSTANT") Then Continue
If InStr(sMacro, "_SELF") Then Continue
Try sHelp = cHelp["@"][sImpl]
If sHelp Then
AddHelp(cHelp, sDeclareClass, sSymbol, sHelp)
sHelp = ""
Endif
Endif
Endif
Next
Close #hFile
' Make class list
aClass = New String[]
For Each cCol In cHelp
If cHelp.Key = "@" Then Continue
aClass.Add(cHelp.Key)
Next
aClass.Sort(gb.IgnoreCase)
' Generate help
For Each sClass In aClass
cCol = cHelp[sClass]
If cCol.Count = 0 Then Continue
Print #hOut, "#"; sClass
For Each sHelp In cCol
Print #hOut, cCol.Key
Print #hOut, "'"; Split(sHelp, "\n").Join("\n'")
Next
Next
End
' Private Function Extract(hFile As File) As String[]
'
' Dim hNameFn As New RegExp, hNameInline As New RegExp
' Dim sLine As String, sInline As String
' Dim aRes As New String[]
' Dim bRecord As Boolean
'
' hNameFn.Compile("^BEGIN_.*\\(([^,)]+).*")
' hNameInline.Compile("/\\*\\*G (.+)$")
'
' For Each sLine In hFile.Lines
' If sLine Match "^[\\t ]*\\*?\\*/$" Then
' If sInline Then
' aRes.Add("G " & sInline)
' sInline = ""
' bRecord = False
' Endif
' Continue
' Endif
' hNameFn.Exec(sLine)
' If hNameFn.Offset <> -1 And If bRecord And If Not sInline Then
' aRes.Add(hNameFn[1].Text)
' bRecord = False
' Endif
'
' If bRecord Then aRes.Add(RegExp.Replace(sLine, "^[\\t ]*\\*", "'"))
'
' If sLine Match "^/\\*\\*G$" Then
' If bRecord Then aRes.Add("ERROR")
' sInline = ""
' bRecord = True
' Endif
' hNameInline.Exec(sLine)
' If hNameInline.Offset <> -1 Then
' If bRecord Then aRes.Add("ERROR")
' sInline = LTrim$(hNameInline[1].Text)
' bRecord = True
' Endif
' Next
' If bRecord Then aRes.Add("ERROR")
' Return aRes
' End
'
' Private Function Translate(aSource As String[], sPath As String) As String[]
'
' Dim sLine As String, aRes As New String[]
'
' For Each sLine In aSource
' If Not sLine Then Continue
' If sLine = "ERROR" Or If sLine Begins "'" Then
' aRes.Add(sLine)
' Continue
' Endif
' If sLine Begins "G " Then ' Syntax-2?
' aRes.Add(Right$(sLine, -2))
' Continue
' Endif
' ' Syntax-1
' aRes.Add(GetSyntax1(sLine, sPath))
' Next
' Return aRes
' End
'
' Private Function GetSyntax1(sFunc As String, sPath As String) As String
'
' Dim hClassName As New RegExp, hFunction As New RegExp
' Dim hFile As File, sLine, sClass As String
' Dim aRes As New String[]
'
' hClassName.Compile("GB_DECLARE\\(\\\"([^\\\"]+).*")
' hFunction.Compile("GB_[^(]+\\(\\\"([^\"]+)\\\".*" & sFunc & "\\W")
'
' hFile = Open sPath For Input
' sClass = "ERROR"
' For Each sLine In hFile.Lines
' If Not sLine Then Continue
' hClassName.Exec(sLine)
' If hClassName.Offset <> -1 Then sClass = hClassName[1].Text
' hFunction.Exec(sLine)
' If hFunction.Offset <> -1 Then
' If Not aRes.Count Then aRes.Add(sClass)
' aRes.Add(hFunction[1].Text)
' Endif
' Next
' Close #hFile
' Return aRes.Join(" ")
' End
'
' Public Sub MakeHelp(aSource As String[], hOut As File)
'
' Dim sLine, sCls, sSym As String
' Dim cHelp As New Collection, aCurrent As New String[]
' Dim cClass As Collection, aHelp, aSyn As String[]
' Dim iInd As Integer
'
' Output To hOut
' For Each sLine In aSource
' If Not sLine Then Continue
' If sLine Begins "'" Then
' aCurrent.Add(sLine)
' Else
' With Scan(sLine, "* *")
' If .Count = 0 Then ' <Class>
' sCls = sLine
' sSym = "#"
' Else If .Count = 2 Then ' <Class> <Symbol-and-Synonym-List>
' sCls = Trim$(.[0])
' sSym = Trim$(.[1])
' Endif
' If Not cHelp[sCls] Then cHelp[sCls] = New Collection
' cHelp[sCls][sSym] = aCurrent
' aCurrent = New String[]
' End With
' Endif
' Next
'
' For Each cClass In cHelp
' Print "#"; cHelp.Key
' aHelp = cClass["#"]
' If aHelp Then Print aHelp.Join("\n")
' For Each aHelp In cClass
' If cClass.Key = "#" Then Continue
' aSyn = Split(cClass.Key, " ")
' Print aSyn[0]
' If aHelp.Count Then Print aHelp.Join("\n")
' For iInd = 1 To aSyn.Max
' Print aSyn[iInd]
' Print "' A synonym for";; aSyn[0]; "."
' Next
' Next
' Next
' Output To Default
' End

View file

@ -0,0 +1,256 @@
' Gambas module file
' ' Gambas module file
'
' Private $sRoot As String
' Private $sComponent As String
' Private $bVerbose As Boolean
'
' Private Sub PrintError(sErr As String)
'
' Error File.Name(Args[0]); ": error: "; sErr
' Quit 1
'
' End
'
' Private Sub PrintMessage(sMsg As String)
'
' If Not $bVerbose Then Return
' Error sMsg
'
' End
'
' Public Sub Main()
'
' Dim iInd As Integer, aSources As New String[]
' Dim sArg, sRec As String, aRec As String[]
' Dim sPath As String
' Dim hOut As File
' Dim bOnlySources As Boolean
' Dim sMsg As String
'
' $sRoot = System.Path
'
' For iInd = 1 To Args.Max
' Select Case Args[iInd]
' Case "-h", "--help"
' Print File.Load("usage")
' Quit
' Case "-V", "--version"
' Print Application.Version
' Quit
' Case "-L", "--license"
' Print File.Load("license")
' Quit
' Case "-v", "--verbose"
' $bVerbose = True
' Case "-r", "--root"
' $sRoot = Args[iInd + 1]
' If Not $sRoot Then PrintError(Args[iInd] & " requires an argument")
' Inc iInd
' Case "-c", "--component"
' $sComponent = Args[iInd + 1]
' If Not $sComponent Then PrintError(Args[iInd] & " requires an argument")
' Inc iInd
' Case "--"
' bOnlySources = True
' Default
' If Not bOnlySources Then
' If Args[iInd] Begins "-" Then
' PrintError("unknown option: " & Args[iInd])
' Endif
' Endif
' aSources.Add(Args[iInd])
' End Select
' Next
'
' If aSources.Count = 0 Then aSources.Add(".")
'
' If $sComponent Then
' sPath = $sRoot &/ "share/gambas" & System.Version &/ "info" &/ $sComponent & ".help"
' PrintMessage("Output to " & sPath)
' hOut = Open sPath For Create
' Else
' hOut = File.Out
' Endif
'
' For Each sArg In aSources
' If Left(sArg) <> "/" Then
' If sArg = "." Then
' sArg = Application.Dir
' Else
' sArg = Application.Dir &/ sArg
' Endif
' Endif
' If IsDir(sArg) Then
' aRec = RDir(sArg, "*.{c,cc,cpp}")
' For Each sRec In aRec
' OneFile(sArg &/ sRec, hOut)
' Next
' Else
' OneFile(sArg, hOut)
' Endif
' Next
'
' If $sComponent Then
' Close #hOut
' If Stat(sPath).Size = 0 Then
' PrintMessage("Removing void data file")
' Try Kill sPath
' Endif
' Endif
'
' Catch
'
' sMsg = Error.Text & ": " & Error.Backtrace.Join(" ")
' Output To Default
' PrintError(sMsg)
'
' End
'
' Private Sub OneFile(sPath As String, hOut As File)
'
' Dim hFile As File
'
' PrintMessage("Processing " & sPath & "...")
' hFile = Open sPath For Input
'
' MakeHelp(Translate(Extract(hFile), sPath), hOut)
'
' Close #hFile
'
' End
'
' Private Function Extract(hFile As File) As String[]
'
' Dim hNameFn As New RegExp, hNameInline As New RegExp
' Dim sLine As String, sInline As String
' Dim aRes As New String[]
' Dim bRecord As Boolean
'
' hNameFn.Compile("^BEGIN_.*\\(([^,)]+).*")
' hNameInline.Compile("/\\*\\*G (.+)$")
'
' For Each sLine In hFile.Lines
' If sLine Match "^[\\t ]*\\*?\\*/$" Then
' If sInline Then
' aRes.Add("G " & sInline)
' sInline = ""
' bRecord = False
' Endif
' Continue
' Endif
' hNameFn.Exec(sLine)
' If hNameFn.Offset <> -1 And If bRecord And If Not sInline Then
' aRes.Add(hNameFn[1].Text)
' bRecord = False
' Endif
'
' If bRecord Then aRes.Add(RegExp.Replace(sLine, "^[\\t ]*\\*", "'"))
'
' If sLine Match "^/\\*\\*G$" Then
' If bRecord Then aRes.Add("ERROR")
' sInline = ""
' bRecord = True
' Endif
' hNameInline.Exec(sLine)
' If hNameInline.Offset <> -1 Then
' If bRecord Then aRes.Add("ERROR")
' sInline = LTrim$(hNameInline[1].Text)
' bRecord = True
' Endif
' Next
' If bRecord Then aRes.Add("ERROR")
' Return aRes
' End
'
' Private Function Translate(aSource As String[], sPath As String) As String[]
'
' Dim sLine As String, aRes As New String[]
'
' For Each sLine In aSource
' If Not sLine Then Continue
' If sLine = "ERROR" Or If sLine Begins "'" Then
' aRes.Add(sLine)
' Continue
' Endif
' If sLine Begins "G " Then ' Syntax-2?
' aRes.Add(Right$(sLine, -2))
' Continue
' Endif
' ' Syntax-1
' aRes.Add(GetSyntax1(sLine, sPath))
' Next
' Return aRes
' End
'
' Private Function GetSyntax1(sFunc As String, sPath As String) As String
'
' Dim hClassName As New RegExp, hFunction As New RegExp
' Dim hFile As File, sLine, sClass As String
' Dim aRes As New String[]
'
' hClassName.Compile("GB_DECLARE\\(\\\"([^\\\"]+).*")
' hFunction.Compile("GB_[^(]+\\(\\\"([^\"]+)\\\".*" & sFunc & "\\W")
'
' hFile = Open sPath For Input
' sClass = "ERROR"
' For Each sLine In hFile.Lines
' If Not sLine Then Continue
' hClassName.Exec(sLine)
' If hClassName.Offset <> -1 Then sClass = hClassName[1].Text
' hFunction.Exec(sLine)
' If hFunction.Offset <> -1 Then
' If Not aRes.Count Then aRes.Add(sClass)
' aRes.Add(hFunction[1].Text)
' Endif
' Next
' Close #hFile
' Return aRes.Join(" ")
' End
'
' Public Sub MakeHelp(aSource As String[], hOut As File)
'
' Dim sLine, sCls, sSym As String
' Dim cHelp As New Collection, aCurrent As New String[]
' Dim cClass As Collection, aHelp, aSyn As String[]
' Dim iInd As Integer
'
' Output To hOut
' For Each sLine In aSource
' If Not sLine Then Continue
' If sLine Begins "'" Then
' aCurrent.Add(sLine)
' Else
' With Scan(sLine, "* *")
' If .Count = 0 Then ' <Class>
' sCls = sLine
' sSym = "#"
' Else If .Count = 2 Then ' <Class> <Symbol-and-Synonym-List>
' sCls = Trim$(.[0])
' sSym = Trim$(.[1])
' Endif
' If Not cHelp[sCls] Then cHelp[sCls] = New Collection
' cHelp[sCls][sSym] = aCurrent
' aCurrent = New String[]
' End With
' Endif
' Next
'
' For Each cClass In cHelp
' Print "#"; cHelp.Key
' aHelp = cClass["#"]
' If aHelp Then Print aHelp.Join("\n")
' For Each aHelp In cClass
' If cClass.Key = "#" Then Continue
' aSyn = Split(cClass.Key, " ")
' Print aSyn[0]
' If aHelp.Count Then Print aHelp.Join("\n")
' For iInd = 1 To aSyn.Max
' Print aSyn[iInd]
' Print "' A synonym for";; aSyn[0]; "."
' Next
' Next
' Next
' Output To Default
' End

112
main/tools/gbh3/README Normal file
View file

@ -0,0 +1,112 @@
About gbh3
----------
gbh3 is used to extract Gambas documentation from C/C++ source files and
create .help files for a component from them. It is a single Gambas project
depending only on gb.pcre.
... and why?
------------
Components written in Gambas can already be documented in-code. This docu-
mentation is then stored inside their .info files. With a similar result
provided by gbh3 for C/C++ components, we can document all[*] Gambas in
its source files (at least my components will be) which makes it, IMHO,
easier to keep the documentation up-to-date. Also the help could be
displayed locally by the IDE or remotely once these .help files are imported
to the gambaswiki.org site.
Also, documentation can be bound to a specific source code version, so that
you get docs for the version of Gambas you are running, not only for the
development branch.
[*] I'm not sure about intrinsic functions, though. Should be feasible,
looking at gbx_class_info.c...
Great! How do I prepare my component?
-------------------------------------
The No. 0 rule is to BE CAREFUL! There are quite some rules to remember:
1. Documentation syntax.
You may write a Gambas documentation comment like
/**G
* Here goes the documentation.
**/
BEGIN_METHOD_VOID(ClassName_MethodName)
/* ... */
END_METHOD
or alternatively
/**G Class Symbol
* Documenting Class.Symbol
**/
Instead of the **/ at the end, you may also write */. Cool, huh?
The second syntax can also be used to document the class per se:
/**G Class
* Class documentation here
**/
The difference between both notations is that the first must immediately
precede a BEGIN_{PROPERTY,METHOD,METHOD_VOID) line that defines the symbol
to which the documentation refers. The second syntax documents the indicated
symbol -- no matter where the comment is.
This is intended to document GB_CONSTANTs or GB_PROPERTY_SELFs which don't
have a BEGIN_* line. It can neverthelss be used for any other symbol but
NEVER intermix both syntaxes on a single symbol.
The ClassName_MethodName part I call "(implementation) function name". The
particular function naming convention shown is not mandatory. The scripts
take the function name and look up the corresponding symbol in your
GB_DESC[].
2. Regular expressions.
The regular expressions used to filter these comments are somewhat strict.
You have to pass the spaces exactly as indicated in the line where your
comment text starts, i.e. <space>*<space>text. Also note that Class and
Symbol in the second syntax are case sensitive!
OTOH, I can't guarantee that the expressions will not consume junk that you
left behind where I didn't expect junk to be.
3. Strict structure.
DON'T try to confuse the parsers by not giving information it needs where
it expects it.
4. Synonyms.
The first match for a function name lookup in a GB_DESC[] is taken as the
"primary" implementation of that symbol. That means the help comment will
be attched to that symbol. All other symbols which call the same function
are considered synonyms and are *automatically* documented with the default
sentence: "This is a synonym for <primary-symbol>". DO NOT document them
separately using a syntax-2 help block!
5. Don't reuse method and property implementations (except for synonyms).
As the implementation function name is searched in the GB_DESC[]s in your
source file, you may not put the same function into different classes -- or
the parser may get confused.
Also, keep the GB_DESC[] in the same source file as the help comment.
Umm... I need an example
------------------------
gb.openssl and gb.data are partially documented that way. Digest.Hash() and
Digest._call() in gb.openssl show how to deal with synonyms. There the entry
for Hash precedes the entry for _call, thus Hash is the primary symbol and
_call a synonym.
To see a sample .help file (on stdout), you can do at the source tree root:
$ gbx3 app/src/gbh3 -- gb.openssl

BIN
main/tools/gbh3/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

6
main/tools/gbh3/license Normal file
View file

@ -0,0 +1,6 @@
(c) Tobias Boege, Benoît Minisini
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.

18
main/tools/gbh3/usage Normal file
View file

@ -0,0 +1,18 @@
Extract Gambas help comments from source files.
Usage: gbh3 [options] [<sources>]
Options:
-r --root <root> gives the Gambas installation directory
-c --component <component> generate help directly in a component '*.help' file
-V --version display version
-L --license display license
-h --help display this help
If <sources> contains directories, they are searched recursively for '*.c'
and '*.cpp' files. If <sources> is not specified, the current directory
is searched.
The extracted help is printed on the standard output, unless the '-c'
option is specified.