otcldoc
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:26k
- #!/bin/sh
- # the exec restarts using tclsh which in turn ignores
- # the command because of this backslash:
- exec tclsh "$0" "$@"
- #
- # otcldoc - a simple script for translating otcl classes into
- # an html hyperlinked document. Inspired by javadoc.
- # Basic algorithm: for a given set of input files, find all Class
- # definitions. Put the text in the comment immediately preceding the
- # class def in the "description" section of the doc.
- # Parse the Class definition to locate superclasses and at
- # that to the "superclasses" section. Locate each method
- # labeled as instproc, public, or private, and enter desriptions
- # in the "Public/Private Methods" sections of the html doc.
- #
- # TO-DO:
- #
- # Add a cross-links for methods as Elan did for classes
- # (in format_line)
- #
- # Separate public and private methods into different sections
- # of the web page. [DONE]
- #
- # Add a tags section to the comment so we can include
- # "see also" refs, author info, etc.
- #
- # Do more interesting html formatting of code, e.g., highlight
- # class names and add hyperlinks.
- #
- # Add tags for C++ methods so they will not only be documented
- # in the OTcl module, but they will also appear in the html doc
- # in a special section.
- #
- # Have a separate pass that determines all of children used by
- # a class so we can build the class interdependecnies into the
- # doc (i.e., we should encompss more than just superclass relationships)
- # [DONE]
- #
- # Add hyperlinks from method description to actual OTcl proc.
- # [DONE]
- #
- # Create a top-level index.
- # [DONE - rough cut]
- #
- # Add keyword or comment to set up inherits-from/overrides relationships.
- #
- # Parsing of the Class definition should not assume it all fits on a single line.
- # (WidgetClass definitions, for example, often span multiple lines.) [DONE]
- #
- # Parse WidgetClass definitions, presenting available attributes and their defaults in html.
- # i.e. Process "-configspec"/"-config" and "-default" similar to the way "-superclass" is handled.
- #
- set app [pwd]
- set outputDir /var/tmp/doc-[file tail [pwd]]
- set indexFileName index.html
- set classString Class
- exec mkdir -p $outputDir
- set outputChannel stdout
- set nlines 0
- proc emit s {
- global outputChannel
- puts $outputChannel $s
- }
- proc emit_nonewline s {
- global outputChannel
- puts -nonewline $outputChannel $s
- }
- #
- # Mape an OTcl class name to an html file name
- #
- proc class_to_file c {
- set f ""
- # map slashes to dashes
- set sep ""
- foreach s [split $c /] {
- set f "$f$sep$s"
- set sep -
- }
- return $f.html
- }
- #
- # Arrange for output to go to the appropriate output file.
- #
- proc set_class c {
- global outputChannel outputDir
- if { $outputChannel != "stdout" } {
- close $outputChannel
- }
- set outputChannel [open $outputDir/[class_to_file $c] w]
- }
- #
- # Arrange for output to go to the appropriate output file.
- #
- proc set_generic_class {} {
- global outputChannel outputDir
- if { $outputChannel != "stdout" } {
- close $outputChannel
- }
- set outputChannel [open $outputDir/[class_to_file "Generic"] w]
- }
- #
- # Return a list of the otcl class definitions
- # found in the file. Set up a table (called "lineOfClass")
- # to map class name to line number and a table (called "methods")
- # that maps a class name to a list of line numbers that
- # correspond to each method of that class.
- #
- proc find_tcl_classes {} {
- global line nlines lineOfClass public_methods private_methods proc_methods generic_proc_methods classString
- set classes ""
- set n 0
- while { $n < $nlines } {
- set s [split $line($n)]
- if { [lindex $s 0] == $classString } {
- set cname [lindex $s 1]
- if { $cname != "instproc" && $cname != "proc" &&
- $cname != "private" && $cname != "public" } {
- set classes "$classes $cname"
- set lineOfClass($cname) $n
- }
- } elseif { "[lindex $s 0]" == "proc" } {
- lappend generic_proc_methods $n
- # if second word is one of the following, make sure the first word is not a standard tcl/tk command
- # (This is to avoid the situations where "public", "proc", etc. are used as variables sent as argument to a command)
- } elseif { [lsearch -exact [info commands {[a-z]*}] [lindex $s 0]] == -1 } {
- if { "[lindex $s 1]" == "instproc" ||
- "[lindex $s 1]" == "public" } {
- set cname [lindex $s 0]
- lappend public_methods($cname) $n
- } elseif { "[lindex $s 1]" == "private" } {
- set cname [lindex $s 0]
- lappend private_methods($cname) $n
- } elseif { "[lindex $s 1]" == "proc" } {
- set cname [lindex $s 0]
- lappend proc_methods($cname) $n
- }
- }
- incr n
- }
- return $classes
- }
- proc class_anchor_name lineno {
- global line
- set s $line($lineno)
- return [lindex $s 1]
- }
- proc method_anchor_name lineno {
- global line
- set s $line($lineno)
- return [lindex $s 0]::[lindex $s 2]
- }
- proc generic_method_anchor_name lineno {
- global line
- set s $line($lineno)
- return [lindex $s 1]
- }
- #
- # Read the important lines from input file into the "lines" array.
- # While we're reading in the file, dump the code to correponsding
- # output html file and insert anchors for each Class and instproc
- #
- proc read_tcl_file fname {
- global line nlines outputDir file classString
- set outFile $outputDir/$fname.html
- exec mkdir -p [file dirname $outFile]
- set out [open $outFile w]
- puts $out "<html><pre>"
- set cont 0
- set contClassDefn 0
- set f [open $fname r]
- while 1 {
- set rawline [gets $f]
- if [eof $f] {
- break
- }
- set s $rawline
-
- # Since we are outputting in an html <pre> section,
- # change '<' to '<' so that '<' is not interpreted
- # to be the start of a specially interpreted element,
- # such as '<s>' which means strike-through.
- regsub -all {<} $rawline {<} rawline
- set linekReset 0
- if $cont {
- set c [continuation $s]
- if { $c != "" } {
- set s $c
- } else {
- set cont 0
- }
- set k [expr $nlines - 1]
- set linekReset 1
- set oldlinek $line($k)
- set line($k) "$line($k) $s"
- }
- # For Class definitions, read in the entire body of the definition even if it spans multiple lines.
- # To do this, we count open and closed braces.
- # The motivation for this was to be able to process the "-configuration" option of a Class definition
- # in order to display the default settings for an Object.
- if { $contClassDefn } {
- if { [is_tcl_comment $s] == 1} {
- continue
- }
- set c [continuation $s]
- if { $c != "" } {
- set s $c
- set cont 1
- }
- set c [classDefnContinuation $s]
- global unclosedbraces
- if { $unclosedbraces > 0 } {
- set s $c
- } else {
- set contClassDefn 0
- }
- if { $linekReset == 1} {
- set line($k) $oldlinek
- } else {
- set k [expr $nlines - 1]
- }
- while { $line($k) == "" } {
- incr k -1
- }
- set line($k) "$line($k) $s"
- }
- #
- # Strip off trailing open brace if necessary
- # (escape rules in split are too difficult otherwise)
- #
- set stringb4trim $s
- set s [string trim $s]
- if { [string last { $s] == [expr [string length $s] - 1] } {
- set s [string range $s 0 [expr [string length $s] - 2]]
- }
-
- set words [split $s]
- #
- # Preserve all class and method definitions in addition
- # to comments and blank lines. (We need the blank
- # lines to server as delimeters in the logic used later.)
- #
- if { [is_tcl_comment $s] || [lindex $words 0] == $classString ||
- [lindex $words 1] == "instproc" ||
- [lindex $words 1] == "public" ||
- [lindex $words 1] == "private" ||
- [lindex $words 1] == "proc" ||
- [lindex $words 0] == "proc" ||
- $words == "" } {
- set c [continuation $s]
- if { $c != "" } {
- set s $c
- set cont 1
- }
- #if s has configuration, read til the brace afterwards is closed into a var which i will process later
- if {[lindex $words 0] == $classString &&
- [lindex $words 1] != "instproc" &&
- [lindex $words 1] != "public" &&
- [lindex $words 1] != "private" &&
- [lindex $words 1] != "proc" &&
- ![is_tcl_comment $s] } {
- set c [classDefnContinuation $stringb4trim]
- global unclosedbraces
- if { $unclosedbraces > 0 } {
- set s $c
- set contClassDefn 1
- }
- }
- set line($nlines) $s
- set file($nlines) $fname
- if { [lindex $words 0] == $classString } {
- set anc [class_anchor_name $nlines]
- puts $out "</pre><a name=$anc><pre>"
- } elseif { [lindex $words 0] == "proc" } {
- set anc [generic_method_anchor_name $nlines]
- puts $out "</pre><a name=$anc><pre>"
- # if second word is one of the following, make sure the first word is not a standard tcl/tk command
- # (This is to avoid the situations where "public", "proc", etc. are used as variables sent as argument to a command)
- } elseif { [lsearch -exact [info commands {[a-z]*}] [lindex $words 0]] == -1 } {
- if { [lindex $words 1] == "instproc" ||
- [lindex $words 1] == "public" ||
- [lindex $words 1] == "private" ||
- [lindex $words 1] == "proc" } {
- set anc [method_anchor_name $nlines]
- puts $out "</pre><a name=$anc><pre>"
- }
- }
- incr nlines
- }
- puts $out $rawline
- }
- puts $out "</pre></html>"
- close $out
- close $f
- }
- #
- # Like read_tcl_file, but for C++. Doesn't bother
- # generating C++ html files.
- #
- proc read_cpp_file fname {
- global line nlines outputDir file tcl_class classString
- set outFile $outputDir/$fname.html
- exec mkdir -p [file dirname $outFile]
- set out [open $outFile w]
- puts $out "<html><pre>"
- set f [open $fname r]
- while 1 {
- set rawline [gets $f]
- if [eof $f] {
- break
- }
- set s $rawline
- set words [split $s]
- #
- # Collect up all the TclClass names so we can
- # warn of classes that don't have structured
- # comments.
- #
- foreach w $words {
- if [string match TclClass(*) $w] {
- set s [string first " $w]
- set e [string last " $w]
- if { $s < 0 } {
- continue
- }
- incr s
- incr e -1
- set c [string range $w $s $e]
- #
- set tcl_class($c) 1
- break
- }
- #XXX skip C++ methods for now.
- }
- #
- # Preserve all comments since all interesting OTcl
- # methods are defined inside C comments.
- #
- if { [is_c_comment $s] } {
- #
- # convert to tcl comment for consistency with
- # other code
- #
- set ss [convert_comment $s]
- set words [split $ss]
- set line($nlines) $ss
- set file($nlines) $fname
- if { [lindex $words 1] == "<otcl>" } {
- set ss [strip_otcl_tag $ss]
- set line($nlines) [string trimleft $ss]
- if { [lindex $words 2] == $classString } {
- set anc [class_anchor_name $nlines]
- puts $out "</pre><a name=$anc><pre>"
- } elseif { [lindex $words 3] == "instproc" ||
- [lindex $words 3] == "public" ||
- [lindex $words 3] == "private" ||
- [lindex $words 3] == "proc" } {
- set anc [method_anchor_name $nlines]
- puts $out "</pre><a name=$anc><pre>"
- } elseif { [lindex $words 2] == "proc" } {
- set anc [generic_method_anchor_name $nlines]
- puts $out "</pre><a name=$anc><pre>"
- }
- }
- incr nlines
- }
- puts $out $rawline
- }
- puts $out "</pre></html>"
- close $out
- close $f
- }
- proc convert_comment s {
- set s [string trimleft $s]
- if { [string index $s 0] == "*" } {
- if { [string index $s 1] == "/" } {
- #
- # terminate the comment block with a special
- # marker (#.) so we can detect it later
- #
- return "#. [string range $s 2 end]"
- }
- return "#[string range $s 1 end]"
- }
- #
- # must be a "/*"
- #
- return "#[string range $s 2 end]"
- }
- proc strip_otcl_tag s {
- set k [string first <otcl> $s]
- return [string range $s [expr $k + 6] end]
- }
- proc is_tcl_comment s {
- # if { [string index $s 0] == "#" } {
- # return 1
- # }
- if { [regexp "^[ t]*#" $s] } {
- return 1
- }
- return 0
- }
- #
- # Return true iff the string s begins with "/*", "*", ignoring
- # leading whitespace. This is not necessarily a C comment, but is
- # good enough for our purposes here.
- #
- proc is_c_comment s {
- set s [string trimleft $s]
- if { [string index $s 0] == "*" || [string range $s 0 1] == "/*" } {
- return 1
- }
- return 0
- }
- #
- # return null if the string does not end in backslash
- # otherwise, return the string less the backslash char
- #
- proc continuation s {
- set k [expr [string length $s] - 1]
- if { [string index $s $k] == "\" } {
- incr k -1
- return [string range $s 0 $k]
- }
- return ""
- }
- #
- # return null if the string closes all the open braces
- # otherwise, return the string (NOTE that if $s is "", that is what will be returned)
- #
- proc classDefnContinuation s {
- global unclosedbraces
-
- if { [is_tcl_comment $s] } {
- return " "
- }
- if {![info exists unclosedbraces]} {
- set unclosedbraces 0
- }
- regsub -all {[^{]} $s {} openers
- regsub -all {[^}]} $s {} closers
- set openbraces [string length $openers]
- set closebraces [string length $closers]
- set ucb [expr $unclosedbraces + $openbraces - $closebraces]
- set unclosedbraces $ucb
- if { $unclosedbraces > 0 } {
- return $s
- } else {
- return ""
- }
- }
- proc strip s {
- set n 1
- if { [string index $s $n] == " " } {
- set n 2
- }
- return [string range $s $n end]
- }
- proc get_comment_block lineNo {
- global file
- if { [file extension $file($lineNo)] == ".tcl" } {
- return [get_tcl_comment_block $lineNo]
- } else {
- return [get_cpp_comment_block $lineNo]
- }
- }
- proc format_line l {
- global lineOfClass
- set l [strip $l]
- # set up class cross references
- set q ""
- foreach w [split $l] {
- # trim punctuation.
- set c [string trim $w ".;:?,(){}[]!"]
- if [info exists lineOfClass($c)] {
- set o "<a href=[class_to_file $c]>$w</a>"
- } else {
- set o $w
- }
- set q "$q $o"
- }
- return $q
- }
- #
- # Return the comment block immediately above line number $lineNo.
- # Removes leading comment characters (but does not strip whitespace
- # since we probably want to preserve tabs). But it does strip
- # a single space char if present. Assume $lineNo < $nlines.
- #
- proc get_tcl_comment_block lineNo {
- global line nlines
- set n $lineNo
- incr n -1
- #
- # First skip over whitespace
- #
- while { $n > 0 } {
- set s $line($n)
- if [is_tcl_comment $s] {
- break
- }
- set w [string trim $s]
- if { $w != "" } {
- # ran into non-comment, non-whitespace
- break
- }
- incr n -1
- }
- #
- # Now skip up past the comment body
- #
- while { $n > 0 } {
- set s $line($n)
- if ![is_tcl_comment $s] {
- break
- }
- incr n -1
- }
- #
- # gather up the text
- #
- set blk ""
- while 1 {
- incr n
- if { $n >= $lineNo } {
- break
- }
- set blk "$blkn[format_line $line($n)]"
- }
- return $blk
- }
- #
- # Just like get_tcl_comment_block but for C++.
- # Note that the C++ comment has been converted to tcl
- # syntax --- the only difference is the comment is
- # below the method rather than above it.
- #
- proc get_cpp_comment_block lineNo {
- global line nlines
- set n $lineNo
- #
- # gather up the text
- # Note that we terminated the comment block
- # with "#." when we converted from C to tcl.
- #
- set blk ""
- while 1 {
- incr n
- set s $line($n)
- if { $n >= $nlines || [string first "#." $s] >= 0 ||
- ![is_tcl_comment $s] } {
- break
- }
- set blk "$blkn[format_line $line($n)]"
- }
- return $blk
- }
- proc inList { list item } {
- return [expr [lsearch $list $item] >= 0]
- }
- proc add_class_edge { parent child } {
- global subclass
- if ![inList subclass($parent) $child] {
- lappend subclass($parent) $child
- }
- }
- # XXX not sure if this will do the right thing
- proc copy_class { dst src } {
- global lineOfClass
- set lineOfClass($dst) $lineOfClass($src)
- }
- #
- # compute the superclass relationships
- #
- proc arrange_class_hierarchy classes {
- global superclass
- foreach c $classes {
- set sc [get_superclasses $c]
- set superclass($c) $sc
- foreach super $sc {
- add_class_edge $super $c
- }
- }
- }
- #
- # Return true iff we encountered the class definition
- # of the class $c.
- #
- proc class_known c {
- global lineOfClass
- return [info exists lineOfClass($c)]
- }
- #
- # Return the superclasses of a given class $c. We no longer assume that the
- # entire "-superclass" directive is on the same line as the Class keyword.
- #
- proc get_superclasses c {
- global line lineOfClass
- set s $line($lineOfClass($c))
- if {[regexp {-superclass[ ]*(.*)} $s match allAfterDashSuperClass]} {
- return [lindex $allAfterDashSuperClass 0]
- } else {
- return ""
- }
- }
- proc is_otcl_class c {
- global file lineOfClass
- return [string match [file extension $file($lineOfClass($c))] ".tcl"]
- }
- proc is_otcl_method lineno {
- global file
- return [string match [file extension $file($lineno)] ".tcl"]
- }
- proc emit_generic_header {} {
- global file lineOfClass private_methods public_methods proc_methods generic_proc_methods
- #XXX should dump comment that otcldoc autogen'd this
- #XXX should use paramters for link color, font etc
- if [info exists generic_proc_methods] {
- foreach l $generic_proc_methods {
- set files($file($l)) 1
- }
- }
- set flist [array names files]
- emit "
- <HTML>n
- <HEAD>n
- <TITLE>Object</TITLE>n
- </HEAD>n
- <BODY BGCOLOR=#FFFFFF TEXT=#000000 LINK=#0000FF>n
- <h1> <i>MASH</i> Proc Methods Not Installed on a Particular Object</h1>n
- <h2>Files</h2>
- "
- foreach fname $flist {
- set flink $fname.html
- emit "
- <ul><a href=$flink>n
- <pre>$fname</pre></a></ul>n
- "
- }
- }
- proc emit_header c {
- global file lineOfClass private_methods public_methods proc_methods
- #XXX should dump comment that otcldoc autogen'd this
- #XXX should use paramters for link color, font etc
- if [info exists public_methods($c)] {
- foreach l $public_methods($c) {
- set files($file($l)) 1
- }
- }
- if [info exists private_methods($c)] {
- foreach l $private_methods($c) {
- set files($file($l)) 1
- }
- }
- if [info exists proc_methods($c)] {
- foreach l $proc_methods($c) {
- set files($file($l)) 1
- }
- }
- set flist [array names files]
- if { $flist == "" } {
- set flist $file($lineOfClass($c))
- }
- set tcl 0
- set cpp 0
- foreach f $flist {
- if [string match [file extension $f] ".tcl"] {
- set tcl 1
- } else {
- set cpp 1
- }
- }
- global classtype classfiles
- if { $tcl } {
- if { $cpp } {
- set cn "Split C++/OTcl Class"
- set classtype($c) split
- } else {
- set cn "OTcl Class"
- set classtype($c) otcl
- }
- } else {
- set cn "C++ Class"
- set classtype($c) c++
- }
- set classfiles($c) $flist
- emit "
- <HTML>n
- <HEAD>n
- <TITLE>$c Object</TITLE>n
- </HEAD>n
- <BODY BGCOLOR=#FFFFFF TEXT=#000000 LINK=#0000FF>n
- <h1>$cn <u>$c</u></h1>n
- <h2>Files</h2>
- "
- foreach fname $flist {
- set flink $fname.html
- emit "
- <ul><a href=$flink>n
- <pre>$fname</pre></a></ul>n
- "
- }
- }
- proc emit_trailer {} {
- #XXX
- emit "</HTML>"
- }
- proc class_link c {
- set link [class_to_file $c]
- return "<A HREF="$link">$c</A>"
- }
- proc emit_class_list clist {
- emit "<UL>"
- set sep ""
- foreach sc $clist {
- emit_nonewline "$sep[class_link $sc]"
- set sep ",nt"
- }
- emit "</UL>"
- }
- proc emit_superclasses c {
- set scl [get_superclasses $c]
- if { $scl != "" } {
- emit "<p>n<h2>Superclasses</h2>n"
- emit_class_list $scl
- }
- }
- proc emit_subclasses c {
- global subclass
- if ![info exists subclass($c)] {
- return
- }
- emit "<p>n<h2>Subclasses</h2>n"
- emit_class_list $subclass($c)
- }
- proc emit_syntax c {
- global public_methods private_methods line file
- emit "<h2>Syntax</h2>"
- if ![info exists public_methods($c)] {
- #XXX no methods (in particular, no constructor)
- emit "<UL><B>$c::init</B><BR></UL>"
- return
- }
- #
- # find the constructor
- #
- foreach lineno $public_methods($c) {
- set s $line($lineno)
- set proc [lindex $s 2]
- if { $proc == "init" } {
- set args [lindex $s 3]
- set link $file($lineno).html#[method_anchor_name $lineno]
- emit "<UL><a href=$link>$c::init</a> <I>$args</I><br>"
- set desc [get_comment_block $lineno]
- if { $desc != "" } {
- emit $desc
- emit "<BR>"
- }
- emit "</UL><BR>"
- return
- }
- }
- if [info exists private_methods($c)] {
- foreach lineno $private_methods($c) {
- set s $line($lineno)
- set proc [lindex $s 2]
- if { $proc == "init" } {
- emit
- "<ul>Abstract base class. Constructor is private.</ul>"
- return
- }
- }
- }
- emit "<ul>No constructor.</ul>"
- }
- #
- # Lists each default configuration for this Class in a two-column format,
- # the first column displaying the option-name, the second column displaying the default value.
- #
- proc emit_default_configuration c {
- global lineOfClass line
- # put everything from the Class defn after "-configuration " into the variable "allAfterDashConfig"
- if {[regexp {-configuration[ ]*({.*)} $line($lineOfClass($c)) match allAfterDashConfig]} {
- # put the item immediately after "-configuration " into a list variable
- set configuration_list [lindex $allAfterDashConfig 0]
- if { [is_even [llength $configuration_list]] } {
- # convert the list to an array of default values, indexed by the option-names
- array set configuration_array $configuration_list
- # output a heading
- emit "
- <h2>Default Configuration</h2>n
- "
- emit "
- <ul><b>Use the <i>add_option</i> method provided by the <i>Configuration</i> object to override these default settings:</b></ul>n
- "
- emit "<ul><table>"
- # output the option-name & default-value pairs in a two-column format
- foreach {key value} [array get configuration_array] {
- emit "<tr><td> $key </td><td> $value </td></tr>"
- }
- emit "</table></ul>"
- } else {
- puts stderr "Warning: every default configuration option must have a value."
- puts stderr "Class $c has an uneven list of default configuration options: $configuration_list"
- }
- }
- }
- proc is_even { num } {
- if { [expr { $num % 2 }] == 0 } {
- return 1
- } else {
- return 0
- }
- }
- proc emit_description c {
- global lineOfClass
- emit "
- <h2>Description</h2>n
- "
- emit [get_comment_block $lineOfClass($c)]
- }
- proc emit_generic_methods { title } {
- global generic_proc_methods line file
- if ![info exists generic_proc_methods] {
- #XXX no methods
- return
- }
- emit "<H2>$title</H2>n<UL>n"
-
- foreach lineno [set generic_proc_methods] {
- set s $line($lineno)
- set proc [lindex $s 1]
- set args [lindex $s 2]
- if [is_otcl_method $lineno] {
- set type "(OTcl)"
- } else {
- set type "(C++)"
- }
- set link $file($lineno).html#[generic_method_anchor_name $lineno]
- emit "<LI><a href=$link>$proc</a> <I>$args</I>$type<br>"
- set desc [get_comment_block $lineno]
- if { $desc != "" } {
- emit $desc
- emit "<br>"
- }
- emit "<br>"
- }
- emit "</UL>"
- }
- proc emit_methods { c scope title } {
- global $scope_methods line file
- if ![info exists $scope_methods($c)] {
- #XXX no methods
- return
- }
- emit "<H2>$title</H2>n<UL>n"
-
- foreach lineno [set $scope_methods($c)] {
- set s $line($lineno)
- set proc [lindex $s 2]
- set args [lindex $s 3]
- if [is_otcl_method $lineno] {
- set type "(OTcl)"
- } else {
- set type "(C++)"
- }
- set link $file($lineno).html#[method_anchor_name $lineno]
- emit "<LI><a href=$link>$proc</a> <I>$args</I>$type<br>"
- set desc [get_comment_block $lineno]
- if { $desc != "" } {
- emit $desc
- emit "<br>"
- }
- emit "<br>"
- }
- emit "</UL>"
- }
- proc emit_class_tree c {
- global mark subclass classtype
- if [info exists mark($c)] {
- return
- }
- # If the next line is uncommented, objects that inherit from multiple parents will only be output beneath the
- # parent that appears first in the tree.
- # set mark($c) 1
- emit "<li> [class_link $c]"
- switch $classtype($c) {
- split { emit "(OTcl/C++)" }
- otcl { emit "(OTcl)" }
- c++ { emit "(C++)" }
- }
- if [info exists subclass($c)] {
- foreach s [lsort $subclass($c)] {
- emit "<ul>"
- emit_class_tree $s
- emit "</ul>"
- }
- }
- }
- #
- # Return true iff the class $c has no parents that
- # have been encountered in the scan of all input files.
- #
- proc is_root_class c {
- global superclass
- foreach p $superclass($c) {
- if [class_known $p] {
- return 0
- }
- }
- return 1
- }
- proc emit_index classes {
- emit "<html>"
- global indexHeaderFile
- if { [info exists indexHeaderFile] &&
- [file readable $indexHeaderFile] } {
- emit [exec cat $indexHeaderFile]
- emit "<hr size=2 noshade>"
- }
- set link [class_to_file "Generic"]
- emit "<br><A HREF="$link">General Tcl procs</A> (<i>i.e.</i> proc methods not installed on a particular Object)"
- #
- # Go through all the classes. For each root class, dump
- # the class hierarchy below. A root class is a class with
- # no parent that will be encountered in the scan.
- #
- foreach c [lsort $classes] {
- if [is_root_class $c] {
- emit "<ul>"
- emit_class_tree $c
- emit "</ul>"
- }
- }
- emit "</html>"
- }
- proc emit_timestamp {} {
- emit "<HR WIDTH='100%'> <CITE>Updated [exec date].</CITE>"
- }
- proc emit_generic_doc {} {
- emit_generic_header
- emit_generic_methods "Procs"
- emit_trailer
- emit_timestamp
- }
- proc emit_doc c {
- emit_header $c
- emit_description $c
- emit_superclasses $c
- emit_subclasses $c
- emit_syntax $c
- emit_default_configuration $c
- emit_methods $c public "Public Methods"
- emit_methods $c private "Private Methods"
- emit_methods $c proc "Proc Methods"
- emit_trailer
- emit_timestamp
- }
- proc is_arg argv {
- if { $argv != "" } {
- return [string match -* [lindex $argv 0]]
- }
- return 0
- }
- proc fatal s {
- puts stderr "otcldoc: $s"
- exit 1
- }
- proc warn s {
- puts stderr "otcldoc: warning - $s"
- }
- proc usage {} {
- puts stderr "usage: otcldoc [-h index-header-file] [ -d output-dir ] [ -i index-file-name ] [ -c class-string ] files ..."
- exit 1
- }
- proc parse_args argv {
- while 1 {
- if ![is_arg $argv] {
- break
- }
- set arg [lindex $argv 0]
- set argv [lrange $argv 1 end]
- set val [lindex $argv 0]
- if { $arg == "-d" } {
- global outputDir
- set outputDir $val
- set argv [lrange $argv 1 end]
- continue
- }
- if { $arg == "-h" } {
- global indexHeaderFile
- set indexHeaderFile $val
- set argv [lrange $argv 1 end]
- continue
- }
- if { $arg == "-i" } {
- global indexFileName
- set indexFileName $val
- set argv [lrange $argv 1 end]
- continue
- }
- if { $arg == "-c" } {
- global classString
- set classString $val
- set argv [lrange $argv 1 end]
- continue
- }
- fatal "unknown command option at '$arg'"
- }
- return $argv
- }
- if { $argv == "" } {
- usage
- }
- set argv [parse_args $argv]
- foreach f $argv {
- if { [file extension $f] == ".tcl" } {
- read_tcl_file $f
- } elseif { [file extension $f] == ".cc" } {
- read_cpp_file $f
- } else {
- fatal "$f: unknown file extension"
- }
- }
- set classes [find_tcl_classes]
- arrange_class_hierarchy $classes
- set_generic_class
- emit_generic_doc
- foreach c $classes {
- set_class $c
- emit_doc $c
- }
- close $outputChannel
- set outputChannel [open $outputDir/$indexFileName w]
- emit_index $classes
- emit_timestamp
- close $outputChannel
- foreach c [array names tcl_class] {
- if ![inList $classes $c] {
- warn "no doc for C++ class $c"
- }
- }