otcldoc
上传用户:rrhhcc
上传日期:2015-12-11
资源大小:54129k
文件大小:26k
源码类别:

通讯编程

开发平台:

Visual C++

  1. #!/bin/sh
  2. # the exec restarts using tclsh which in turn ignores
  3. # the command because of this backslash: 
  4. exec tclsh "$0" "$@"
  5. #
  6. # otcldoc - a simple script for translating otcl classes into
  7. # an html hyperlinked document.  Inspired by javadoc.
  8. # Basic algorithm: for a given set of input files, find all Class
  9. # definitions.  Put the text in the comment immediately preceding the
  10. # class def in the "description" section of the doc.
  11. # Parse the Class definition to locate superclasses and at
  12. # that to the "superclasses" section.  Locate each method
  13. # labeled as instproc, public, or private, and enter desriptions
  14. # in the "Public/Private Methods" sections of the html doc.
  15. #
  16. # TO-DO:
  17. #
  18. #   Add a cross-links for methods as Elan did for classes
  19. #   (in format_line)
  20. #
  21. #   Separate public and private methods into different sections
  22. #   of the web page. [DONE]
  23. #
  24. #   Add a tags section to the comment so we can include
  25. #   "see also" refs, author info, etc.
  26. #
  27. #   Do more interesting html formatting of code, e.g., highlight
  28. #   class names and add hyperlinks.
  29. #
  30. #   Add tags for C++ methods so they will not only be documented
  31. #   in the OTcl module, but they will also appear in the html doc
  32. #   in a special section.
  33. #
  34. #   Have a separate pass that determines all of children used by
  35. #   a class so we can build the class interdependecnies into the 
  36. #   doc (i.e., we should encompss more than just superclass relationships)
  37. # [DONE]
  38. #
  39. #   Add hyperlinks from method description to actual OTcl proc.
  40. # [DONE]
  41. #
  42. #   Create a top-level index.
  43. # [DONE - rough cut]
  44. #
  45. #   Add keyword or comment to set up inherits-from/overrides relationships.
  46. #
  47. #   Parsing of the Class definition should not assume it all fits on a single line.
  48. #   (WidgetClass definitions, for example, often span multiple lines.)  [DONE]
  49. #
  50. #   Parse WidgetClass definitions, presenting available attributes and their defaults in html. 
  51. #   i.e. Process "-configspec"/"-config" and "-default" similar to the way "-superclass" is handled.    
  52. #
  53. set app [pwd]
  54. set outputDir /var/tmp/doc-[file tail [pwd]]
  55. set indexFileName index.html
  56. set classString Class
  57. exec mkdir -p $outputDir
  58. set outputChannel stdout
  59. set nlines 0
  60. proc emit s {
  61. global outputChannel
  62. puts $outputChannel $s
  63. }
  64. proc emit_nonewline s {
  65. global outputChannel
  66. puts -nonewline $outputChannel $s
  67. }
  68. #
  69. # Mape an OTcl class name to an html file name
  70. #
  71. proc class_to_file c {
  72. set f ""
  73. # map slashes to dashes
  74. set sep ""
  75. foreach s [split $c /] {
  76. set f "$f$sep$s"
  77. set sep -
  78. }
  79. return $f.html
  80. }
  81. #
  82. # Arrange for output to go to the appropriate output file.
  83. proc set_class c {
  84. global outputChannel outputDir
  85. if { $outputChannel != "stdout" } {
  86. close $outputChannel
  87. }
  88. set outputChannel [open $outputDir/[class_to_file $c] w]
  89. }
  90. #
  91. # Arrange for output to go to the appropriate output file.
  92. proc set_generic_class {} {
  93. global outputChannel outputDir
  94. if { $outputChannel != "stdout" } {
  95. close $outputChannel
  96. }
  97. set outputChannel [open $outputDir/[class_to_file "Generic"] w]
  98. }
  99. #
  100. # Return a list of the otcl class definitions
  101. # found in the file.  Set up a table (called "lineOfClass")
  102. # to map class name to line number and a table (called "methods")
  103. # that maps a class name to a list of line numbers that
  104. # correspond to each method of that class.
  105. #
  106. proc find_tcl_classes {} {
  107. global line nlines lineOfClass public_methods private_methods proc_methods generic_proc_methods classString 
  108. set classes ""
  109. set n 0
  110. while { $n < $nlines } {
  111. set s [split $line($n)]
  112. if { [lindex $s 0]  == $classString } {
  113. set cname [lindex $s 1]
  114. if { $cname != "instproc" && $cname != "proc" &&
  115.     $cname != "private" && $cname != "public" } {
  116. set classes "$classes $cname"
  117. set lineOfClass($cname) $n
  118. }
  119. } elseif { "[lindex $s 0]"  == "proc" } {
  120. lappend generic_proc_methods $n
  121. # if second word is one of the following, make sure the first word is not a standard tcl/tk command
  122. # (This is to avoid the situations where "public", "proc", etc. are used as variables sent as argument to a command)
  123. } elseif { [lsearch -exact [info commands {[a-z]*}] [lindex $s 0]] == -1 } {
  124. if { "[lindex $s 1]" == "instproc" ||
  125.      "[lindex $s 1]" == "public" } {
  126. set cname [lindex $s 0]
  127. lappend public_methods($cname) $n
  128. } elseif { "[lindex $s 1]" == "private" } {
  129. set cname [lindex $s 0]
  130. lappend private_methods($cname) $n
  131. } elseif { "[lindex $s 1]" == "proc" } {
  132. set cname [lindex $s 0]
  133. lappend proc_methods($cname) $n
  134. }
  135. }
  136. incr n
  137. }
  138. return $classes
  139. }
  140. proc class_anchor_name lineno {
  141. global line
  142. set s $line($lineno)
  143. return [lindex $s 1]
  144. }
  145. proc method_anchor_name lineno {
  146. global line
  147. set s $line($lineno)
  148. return [lindex $s 0]::[lindex $s 2]
  149. }
  150. proc generic_method_anchor_name lineno {
  151. global line
  152. set s $line($lineno)
  153. return [lindex $s 1]
  154. }
  155. #
  156. # Read the important lines from input file into the "lines" array.
  157. # While we're reading in the file, dump the code to correponsding
  158. # output html file and insert anchors for each Class and instproc
  159. #
  160. proc read_tcl_file fname {
  161. global line nlines outputDir file classString
  162. set outFile $outputDir/$fname.html
  163. exec mkdir -p [file dirname $outFile]
  164. set out [open $outFile w]
  165. puts $out "<html><pre>"
  166. set cont 0
  167. set contClassDefn 0
  168. set f [open $fname r]
  169. while 1 {
  170. set rawline [gets $f]
  171. if [eof $f] {
  172. break
  173. }
  174. set s $rawline
  175. # Since we are outputting in an html <pre> section, 
  176. # change '<' to '&#60;' so that '<' is not interpreted
  177. # to be the start of a specially interpreted element,
  178. # such as '<s>' which means strike-through.
  179. regsub -all {<} $rawline {&#60;} rawline
  180. set linekReset 0
  181. if $cont {
  182. set c [continuation $s]
  183. if { $c != "" } {
  184. set s $c
  185. } else {
  186. set cont 0
  187. }
  188. set k [expr $nlines - 1]
  189. set linekReset 1
  190. set oldlinek $line($k)
  191. set line($k) "$line($k) $s"
  192. # For Class definitions, read in the entire body of the definition even if it spans multiple lines.
  193. # To do this, we count open and closed braces.
  194. # The motivation for this was to be able to process the "-configuration" option of a Class definition
  195. # in order to display the default settings for an Object.
  196. if { $contClassDefn } {
  197. if { [is_tcl_comment $s] == 1} {
  198.     continue
  199. }
  200. set c [continuation $s]
  201. if { $c != "" } {
  202. set s $c
  203. set cont 1
  204. }
  205. set c [classDefnContinuation $s]
  206. global unclosedbraces
  207. if { $unclosedbraces > 0 } {
  208.     set s $c
  209. } else {
  210.     set contClassDefn 0
  211. }
  212. if { $linekReset == 1} {
  213.     set line($k) $oldlinek
  214. } else {
  215.     set k [expr $nlines - 1]
  216. }
  217. while { $line($k) == "" } {
  218.     incr k -1
  219. }
  220. set line($k) "$line($k) $s"
  221. }
  222. #
  223. # Strip off trailing open brace if necessary
  224. # (escape rules in split are too difficult otherwise)
  225. #
  226. set stringb4trim $s
  227. set s [string trim $s]
  228. if { [string last { $s] == [expr [string length $s] - 1] } {
  229. set s [string range $s 0 [expr [string length $s] - 2]]
  230. }
  231. set words [split $s]
  232. #
  233. # Preserve all class and method definitions in addition
  234. # to comments and blank lines.  (We need the blank
  235. # lines to server as delimeters in the logic used later.)
  236. #
  237. if { [is_tcl_comment $s] || [lindex $words 0]  == $classString ||
  238. [lindex $words 1] == "instproc" ||
  239. [lindex $words 1] == "public" ||
  240. [lindex $words 1] == "private" ||
  241. [lindex $words 1] == "proc" ||
  242. [lindex $words 0] == "proc" ||
  243. $words == "" } {
  244. set c [continuation $s]
  245. if { $c != "" } {
  246. set s $c
  247. set cont 1
  248. }
  249. #if s has configuration, read til the brace afterwards is closed into a var which i will process later
  250. if {[lindex $words 0] == $classString && 
  251.     [lindex $words 1] != "instproc" &&
  252.     [lindex $words 1] != "public" &&
  253.     [lindex $words 1] != "private" &&
  254.     [lindex $words 1] != "proc" &&
  255.     ![is_tcl_comment $s] } {
  256. set c [classDefnContinuation $stringb4trim]
  257. global unclosedbraces
  258. if { $unclosedbraces > 0 } {
  259.     set s $c
  260.     set contClassDefn 1
  261. }
  262. }
  263. set line($nlines) $s
  264. set file($nlines) $fname
  265. if { [lindex $words 0]  == $classString } {
  266. set anc [class_anchor_name $nlines]
  267. puts $out "</pre><a name=$anc><pre>"
  268. } elseif { [lindex $words 0] == "proc" } {
  269. set anc [generic_method_anchor_name $nlines]
  270. puts $out "</pre><a name=$anc><pre>"
  271. # if second word is one of the following, make sure the first word is not a standard tcl/tk command
  272. # (This is to avoid the situations where "public", "proc", etc. are used as variables sent as argument to a command)
  273. } elseif { [lsearch -exact [info commands {[a-z]*}] [lindex $words 0]] == -1 } {
  274. if { [lindex $words 1] == "instproc" ||
  275. [lindex $words 1] == "public" ||
  276. [lindex $words 1] == "private" ||
  277. [lindex $words 1] == "proc" } {
  278. set anc [method_anchor_name $nlines]
  279. puts $out "</pre><a name=$anc><pre>"
  280. }
  281. }
  282. incr nlines
  283. }
  284. puts $out $rawline
  285. }
  286. puts $out "</pre></html>"
  287. close $out
  288. close $f
  289. }
  290. #
  291. # Like read_tcl_file, but for C++.  Doesn't bother
  292. # generating C++ html files.
  293. #
  294. proc read_cpp_file fname {
  295. global line nlines outputDir file tcl_class classString
  296. set outFile $outputDir/$fname.html
  297. exec mkdir -p [file dirname $outFile]
  298. set out [open $outFile w]
  299. puts $out "<html><pre>"
  300. set f [open $fname r]
  301. while 1 {
  302. set rawline [gets $f]
  303. if [eof $f] {
  304. break
  305. }
  306. set s $rawline
  307. set words [split $s]
  308. #
  309. # Collect up all the TclClass names so we can
  310. # warn of classes that don't have structured
  311. # comments.
  312. #
  313. foreach w $words {
  314. if [string match TclClass(*) $w] {
  315. set s [string first " $w]
  316. set e [string last " $w]
  317. if { $s < 0 } {
  318. continue
  319. }
  320. incr s
  321. incr e -1
  322. set c [string range $w $s $e]
  323. set tcl_class($c) 1
  324. break
  325. }
  326. #XXX skip C++ methods for now.
  327. }
  328. #
  329. # Preserve all comments since all interesting OTcl
  330. # methods are defined inside C comments.
  331. #
  332. if { [is_c_comment $s] } {
  333. #
  334. # convert to tcl comment for consistency with
  335. # other code
  336. #
  337. set ss [convert_comment $s]
  338. set words [split $ss]
  339. set line($nlines) $ss
  340. set file($nlines) $fname
  341. if { [lindex $words 1] == "<otcl>" } {
  342. set ss [strip_otcl_tag $ss]
  343. set line($nlines) [string trimleft $ss]
  344. if { [lindex $words 2] == $classString } {
  345. set anc [class_anchor_name $nlines]
  346. puts $out "</pre><a name=$anc><pre>"
  347. } elseif { [lindex $words 3] == "instproc" ||
  348. [lindex $words 3] == "public" ||
  349. [lindex $words 3] == "private" ||
  350. [lindex $words 3] == "proc" } {
  351. set anc [method_anchor_name $nlines]
  352. puts $out "</pre><a name=$anc><pre>"
  353. } elseif { [lindex $words 2] == "proc" } {
  354. set anc [generic_method_anchor_name $nlines]
  355. puts $out "</pre><a name=$anc><pre>"
  356. }
  357. }
  358. incr nlines
  359. }
  360. puts $out $rawline
  361. }
  362. puts $out "</pre></html>"
  363. close $out
  364. close $f
  365. }
  366. proc convert_comment s {
  367. set s [string trimleft $s]
  368. if { [string index $s 0] == "*" } {
  369. if { [string index $s 1] == "/" } {
  370. #
  371. # terminate the comment block with a special
  372. # marker (#.) so we can detect it later
  373. #
  374. return "#. [string range $s 2 end]"
  375. }
  376. return "#[string range $s 1 end]"
  377. }
  378. #
  379. # must be a "/*"
  380. #
  381. return "#[string range $s 2 end]"
  382. }
  383. proc strip_otcl_tag s {
  384. set k [string first <otcl> $s]
  385. return [string range $s [expr $k + 6] end]
  386. }
  387. proc is_tcl_comment s {
  388. # if { [string index $s 0] == "#" } {
  389. # return 1
  390. # }
  391. if { [regexp "^[ t]*#" $s] } {
  392. return 1
  393. }
  394. return 0
  395. }
  396. #
  397. # Return true iff the string s begins with "/*", "*", ignoring
  398. # leading whitespace.  This is not necessarily a C comment, but is
  399. # good enough for our purposes here.
  400. #
  401. proc is_c_comment s {
  402. set s [string trimleft $s]
  403. if { [string index $s 0] == "*" || [string range $s 0 1] == "/*" } {
  404. return 1
  405. }
  406. return 0
  407. }
  408. #
  409. # return null if the string does not end in backslash
  410. # otherwise, return the string less the backslash char
  411. #
  412. proc continuation s {
  413. set k [expr [string length $s] - 1]
  414. if { [string index $s $k] == "\" } {
  415. incr k -1
  416. return [string range $s 0 $k]
  417. }
  418. return ""
  419. }
  420. #
  421. # return null if the string closes all the open braces
  422. # otherwise, return the string  (NOTE that if $s is "", that is what will be returned)
  423. #
  424. proc classDefnContinuation s {
  425.     global unclosedbraces
  426.     
  427.     if { [is_tcl_comment $s] } {
  428. return " "
  429.     }
  430.     if {![info exists unclosedbraces]} {
  431. set unclosedbraces 0
  432.     }
  433.     regsub -all {[^{]} $s {} openers
  434.     regsub -all {[^}]} $s {} closers
  435.     set openbraces [string length $openers]
  436.     set closebraces [string length $closers]
  437.     set ucb [expr $unclosedbraces + $openbraces - $closebraces]
  438.     set unclosedbraces $ucb
  439.     if { $unclosedbraces > 0 } {
  440. return $s
  441.     } else {
  442. return ""
  443.     }
  444. }
  445. proc strip s {
  446. set n 1
  447. if { [string index $s $n] == " " } {
  448. set n 2
  449. }
  450. return [string range $s $n end]
  451. }
  452. proc get_comment_block lineNo {
  453. global file
  454. if { [file extension $file($lineNo)] == ".tcl" } {
  455. return [get_tcl_comment_block $lineNo]
  456. } else {
  457. return [get_cpp_comment_block $lineNo]
  458. }
  459. }
  460. proc format_line l {
  461. global lineOfClass
  462. set l [strip $l]
  463. # set up class cross references
  464. set q ""
  465. foreach w [split $l] {
  466. # trim punctuation.
  467. set c [string trim $w ".;:?,(){}[]!"]
  468. if [info exists lineOfClass($c)] {
  469. set o "<a href=[class_to_file $c]>$w</a>"
  470. } else {
  471. set o $w
  472. }
  473. set q "$q $o"
  474. }
  475. return $q
  476. }
  477. #
  478. # Return the comment block immediately above line number $lineNo.
  479. # Removes leading comment characters (but does not strip whitespace
  480. # since we probably want to preserve tabs).  But it does strip
  481. # a single space char if present.  Assume $lineNo < $nlines.
  482. #
  483. proc get_tcl_comment_block lineNo {
  484. global line nlines
  485. set n $lineNo
  486. incr n -1
  487. #
  488. # First skip over whitespace
  489. #
  490. while { $n > 0 } {
  491. set s $line($n)
  492. if [is_tcl_comment $s] {
  493. break
  494. }
  495. set w [string trim $s]
  496. if { $w != "" } {
  497. # ran into non-comment, non-whitespace
  498. break
  499. }
  500. incr n -1
  501. }
  502. #
  503. # Now skip up past the comment body
  504. #
  505. while { $n > 0 } {
  506. set s $line($n)
  507. if ![is_tcl_comment $s] {
  508. break
  509. }
  510. incr n -1
  511. }
  512. #
  513. # gather up the text
  514. #
  515. set blk ""
  516. while 1 {
  517. incr n
  518. if { $n >= $lineNo } {
  519. break
  520. }
  521. set blk "$blkn[format_line $line($n)]"
  522. }
  523. return $blk
  524. }
  525. #
  526. # Just like get_tcl_comment_block but for C++.
  527. # Note that the C++ comment has been converted to tcl
  528. # syntax --- the only difference is the comment is
  529. # below the method rather than above it.
  530. #
  531. proc get_cpp_comment_block lineNo {
  532. global line nlines
  533. set n $lineNo
  534. #
  535. # gather up the text
  536. # Note that we terminated the comment block 
  537. # with "#." when we converted from C to tcl.
  538. #
  539. set blk ""
  540. while 1 {
  541. incr n
  542. set s $line($n)
  543. if { $n >= $nlines || [string first "#." $s] >= 0 || 
  544. ![is_tcl_comment $s] } {
  545. break
  546. }
  547. set blk "$blkn[format_line $line($n)]"
  548. }
  549. return $blk
  550. }
  551. proc inList { list item } {
  552. return [expr [lsearch $list $item] >= 0]
  553. }
  554. proc add_class_edge { parent child } {
  555. global subclass
  556. if ![inList subclass($parent) $child] {
  557. lappend subclass($parent) $child
  558. }
  559. }
  560. # XXX not sure if this will do the right thing
  561. proc copy_class { dst src } {
  562. global lineOfClass
  563. set lineOfClass($dst) $lineOfClass($src)
  564. }
  565. #
  566. # compute the superclass relationships
  567. #
  568. proc arrange_class_hierarchy classes {
  569. global superclass
  570. foreach c $classes {
  571. set sc [get_superclasses $c]
  572. set superclass($c) $sc
  573. foreach super $sc {
  574. add_class_edge $super $c
  575. }
  576. }
  577. }
  578. #
  579. # Return true iff we encountered the class definition
  580. # of the class $c.
  581. #
  582. proc class_known c {
  583. global lineOfClass
  584. return [info exists lineOfClass($c)]
  585. }
  586. #
  587. # Return the superclasses of a given class $c.  We no longer assume that the
  588. # entire "-superclass" directive is on the same line as the Class keyword.
  589. #
  590. proc get_superclasses c {
  591. global line lineOfClass
  592. set s $line($lineOfClass($c))
  593. if {[regexp {-superclass[ ]*(.*)} $s match allAfterDashSuperClass]} {
  594.     return [lindex $allAfterDashSuperClass 0]
  595. } else {
  596. return ""
  597. }
  598. }
  599. proc is_otcl_class c {
  600. global file lineOfClass
  601. return [string match [file extension $file($lineOfClass($c))] ".tcl"]
  602. }
  603. proc is_otcl_method lineno {
  604. global file
  605. return [string match [file extension $file($lineno)] ".tcl"]
  606. }
  607. proc emit_generic_header {} {
  608. global file lineOfClass private_methods public_methods proc_methods generic_proc_methods
  609. #XXX should dump comment that otcldoc autogen'd this
  610. #XXX should use paramters for link color, font etc
  611. if [info exists generic_proc_methods] {
  612. foreach l $generic_proc_methods {
  613. set files($file($l)) 1
  614. }
  615. }
  616. set flist [array names files]
  617. emit "
  618. <HTML>n
  619. <HEAD>n
  620. <TITLE>Object</TITLE>n
  621. </HEAD>n
  622. <BODY BGCOLOR=#FFFFFF TEXT=#000000 LINK=#0000FF>n
  623. <h1> <i>MASH</i> Proc Methods Not Installed on a Particular Object</h1>n
  624. <h2>Files</h2>
  625. "
  626. foreach fname $flist {
  627. set flink $fname.html
  628. emit "
  629. <ul><a href=$flink>n
  630. <pre>$fname</pre></a></ul>n
  631. "
  632. }
  633. }
  634. proc emit_header c {
  635. global file lineOfClass private_methods public_methods proc_methods 
  636. #XXX should dump comment that otcldoc autogen'd this
  637. #XXX should use paramters for link color, font etc
  638. if [info exists public_methods($c)] {
  639. foreach l $public_methods($c) {
  640. set files($file($l)) 1
  641. }
  642. }
  643. if [info exists private_methods($c)] {
  644. foreach l $private_methods($c) {
  645. set files($file($l)) 1
  646. }
  647. }
  648. if [info exists proc_methods($c)] {
  649. foreach l $proc_methods($c) {
  650. set files($file($l)) 1
  651. }
  652. }
  653. set flist [array names files]
  654. if { $flist == "" } {
  655. set flist $file($lineOfClass($c))
  656. }
  657. set tcl 0
  658. set cpp 0
  659. foreach f $flist {
  660. if [string match [file extension $f] ".tcl"] {
  661. set tcl 1
  662. } else {
  663. set cpp 1
  664. }
  665. }
  666. global classtype classfiles
  667. if { $tcl } {
  668. if { $cpp } {
  669. set cn "Split C++/OTcl Class"
  670. set classtype($c) split
  671. } else {
  672. set cn "OTcl Class"
  673. set classtype($c) otcl
  674. }
  675. } else {
  676. set cn "C++ Class"
  677. set classtype($c) c++
  678. }
  679. set classfiles($c) $flist
  680. emit "
  681. <HTML>n
  682. <HEAD>n
  683. <TITLE>$c Object</TITLE>n
  684. </HEAD>n
  685. <BODY BGCOLOR=#FFFFFF TEXT=#000000 LINK=#0000FF>n
  686. <h1>$cn <u>$c</u></h1>n
  687. <h2>Files</h2>
  688. "
  689. foreach fname $flist {
  690. set flink $fname.html
  691. emit "
  692. <ul><a href=$flink>n
  693. <pre>$fname</pre></a></ul>n
  694. "
  695. }
  696. }
  697. proc emit_trailer {} {
  698. #XXX
  699. emit "</HTML>"
  700. }
  701. proc class_link c {
  702. set link [class_to_file $c]
  703. return "<A HREF="$link">$c</A>"
  704. }
  705. proc emit_class_list clist {
  706. emit "<UL>"
  707. set sep ""
  708. foreach sc $clist {
  709. emit_nonewline "$sep[class_link $sc]"
  710. set sep ",nt"
  711. }
  712. emit "</UL>"
  713. }
  714. proc emit_superclasses c {
  715. set scl [get_superclasses $c]
  716. if { $scl != "" } {
  717. emit "<p>n<h2>Superclasses</h2>n"
  718. emit_class_list $scl
  719. }
  720. }
  721. proc emit_subclasses c {
  722. global subclass
  723. if ![info exists subclass($c)] {
  724. return
  725. }
  726. emit "<p>n<h2>Subclasses</h2>n"
  727. emit_class_list $subclass($c)
  728. }
  729. proc emit_syntax c {
  730. global public_methods private_methods line file
  731. emit "<h2>Syntax</h2>"
  732. if ![info exists public_methods($c)] {
  733. #XXX no methods (in particular, no constructor)
  734. emit "<UL><B>$c::init</B><BR></UL>"
  735. return
  736. }
  737. #
  738. # find the constructor
  739. #
  740. foreach lineno $public_methods($c) {
  741. set s $line($lineno)
  742. set proc [lindex $s 2]
  743. if { $proc == "init" } {
  744. set args [lindex $s 3]
  745. set link $file($lineno).html#[method_anchor_name $lineno]
  746. emit "<UL><a href=$link>$c::init</a> <I>$args</I><br>"
  747. set desc [get_comment_block $lineno]
  748. if { $desc != "" } {
  749. emit $desc
  750. emit "<BR>"
  751. }
  752. emit "</UL><BR>"
  753. return
  754. }
  755. }
  756. if [info exists private_methods($c)] {
  757. foreach lineno $private_methods($c) {
  758. set s $line($lineno)
  759. set proc [lindex $s 2]
  760. if { $proc == "init" } {
  761. emit 
  762. "<ul>Abstract base class.  Constructor is private.</ul>"
  763. return
  764. }
  765. }
  766. }
  767. emit "<ul>No constructor.</ul>"
  768. }
  769. #
  770. # Lists each default configuration for this Class in a two-column format,
  771. # the first column displaying the option-name, the second column displaying the default value.
  772. #
  773. proc emit_default_configuration c {
  774. global lineOfClass line
  775. # put everything from the Class defn after "-configuration " into the variable "allAfterDashConfig"
  776. if {[regexp {-configuration[ ]*({.*)} $line($lineOfClass($c)) match allAfterDashConfig]} {
  777.     # put the item immediately after "-configuration " into a list variable 
  778.     set configuration_list [lindex $allAfterDashConfig 0]
  779.     if { [is_even [llength $configuration_list]] } {
  780. # convert the list to an array of default values, indexed by the option-names
  781. array set configuration_array $configuration_list
  782. # output a heading
  783. emit "
  784. <h2>Default Configuration</h2>n
  785. "
  786. emit "
  787. <ul><b>Use the <i>add_option</i> method provided by the <i>Configuration</i> object to override these default settings:</b></ul>n
  788. "
  789. emit "<ul><table>"
  790. # output the option-name & default-value pairs in a two-column format
  791. foreach {key value} [array get configuration_array] {
  792.     emit "<tr><td> $key </td><td> $value </td></tr>"
  793. }
  794. emit "</table></ul>"
  795.     } else {
  796. puts stderr "Warning: every default configuration option must have a value."
  797. puts stderr "Class $c has an uneven list of default configuration options: $configuration_list"
  798.     }
  799. }
  800. }
  801. proc is_even { num } {
  802.     if { [expr { $num % 2 }] == 0 } {
  803. return 1
  804.     } else {
  805. return 0
  806.     }
  807. }
  808. proc emit_description c {
  809. global lineOfClass
  810. emit "
  811. <h2>Description</h2>n
  812. "
  813. emit [get_comment_block $lineOfClass($c)]
  814. }
  815. proc emit_generic_methods { title } {
  816. global generic_proc_methods line file
  817. if ![info exists generic_proc_methods] {
  818. #XXX no methods
  819. return
  820. }
  821. emit "<H2>$title</H2>n<UL>n"
  822. foreach lineno [set generic_proc_methods] {
  823. set s $line($lineno)
  824. set proc [lindex $s 1]
  825. set args [lindex $s 2]
  826. if [is_otcl_method $lineno] {
  827. set type "(OTcl)"
  828. } else {
  829. set type "(C++)"
  830. }
  831. set link $file($lineno).html#[generic_method_anchor_name $lineno]
  832. emit "<LI><a href=$link>$proc</a> <I>$args</I>$type<br>"
  833. set desc [get_comment_block $lineno]
  834. if { $desc != "" } {
  835. emit $desc
  836. emit "<br>"
  837. }
  838. emit "<br>"
  839. }
  840. emit "</UL>"
  841. }
  842. proc emit_methods { c scope title } {
  843. global $scope_methods line file
  844. if ![info exists $scope_methods($c)] {
  845. #XXX no methods
  846. return
  847. }
  848. emit "<H2>$title</H2>n<UL>n"
  849. foreach lineno [set $scope_methods($c)] {
  850. set s $line($lineno)
  851. set proc [lindex $s 2]
  852. set args [lindex $s 3]
  853. if [is_otcl_method $lineno] {
  854. set type "(OTcl)"
  855. } else {
  856. set type "(C++)"
  857. }
  858. set link $file($lineno).html#[method_anchor_name $lineno]
  859. emit "<LI><a href=$link>$proc</a> <I>$args</I>$type<br>"
  860. set desc [get_comment_block $lineno]
  861. if { $desc != "" } {
  862. emit $desc
  863. emit "<br>"
  864. }
  865. emit "<br>"
  866. }
  867. emit "</UL>"
  868. }
  869. proc emit_class_tree c {
  870. global mark subclass classtype
  871. if [info exists mark($c)] {
  872. return
  873. }
  874. # If the next line is uncommented, objects that inherit from multiple parents will only be output beneath the
  875. # parent that appears first in the tree.
  876. # set mark($c) 1
  877. emit "<li> [class_link $c]"
  878. switch $classtype($c) {
  879. split { emit "(OTcl/C++)" }
  880. otcl { emit "(OTcl)" }
  881. c++ { emit "(C++)" }
  882. }
  883. if [info exists subclass($c)] {
  884. foreach s [lsort $subclass($c)] {
  885. emit "<ul>"
  886. emit_class_tree $s
  887. emit "</ul>"
  888. }
  889. }
  890. }
  891. #
  892. # Return true iff the class $c has no parents that
  893. # have been encountered in the scan of all input files.
  894. #
  895. proc is_root_class c {
  896. global superclass
  897. foreach p $superclass($c)  {
  898. if [class_known $p] {
  899. return 0
  900. }
  901. }
  902. return 1
  903. }
  904. proc emit_index classes {
  905. emit "<html>"
  906. global indexHeaderFile
  907. if { [info exists indexHeaderFile] &&
  908. [file readable $indexHeaderFile] } {
  909. emit [exec cat $indexHeaderFile]
  910. emit "<hr size=2 noshade>"
  911. }
  912. set link [class_to_file "Generic"]
  913. emit "<br><A HREF="$link">General Tcl procs</A> (<i>i.e.</i> proc methods not installed on a particular Object)" 
  914. #
  915. # Go through all the classes.  For each root class, dump
  916. # the class hierarchy below.  A root class is a class with
  917. # no parent that will be encountered in the scan.
  918. #
  919. foreach c [lsort $classes] {
  920. if [is_root_class $c] {
  921. emit "<ul>"
  922. emit_class_tree $c
  923. emit "</ul>"
  924. }
  925. }
  926. emit "</html>"
  927. }
  928. proc emit_timestamp {} {
  929.     emit "<HR WIDTH='100%'> <CITE>Updated [exec date].</CITE>"
  930. }
  931. proc emit_generic_doc {} {
  932. emit_generic_header 
  933. emit_generic_methods "Procs"
  934. emit_trailer 
  935. emit_timestamp
  936. }
  937. proc emit_doc c {
  938. emit_header $c
  939. emit_description $c
  940. emit_superclasses $c
  941. emit_subclasses $c
  942. emit_syntax $c
  943. emit_default_configuration $c
  944. emit_methods $c public "Public Methods"
  945. emit_methods $c private "Private Methods"
  946. emit_methods $c proc "Proc Methods"
  947. emit_trailer 
  948. emit_timestamp
  949. }
  950. proc is_arg argv {
  951. if { $argv != "" } {
  952. return [string match -* [lindex $argv 0]]
  953. }
  954. return 0
  955. }
  956. proc fatal s {
  957. puts stderr "otcldoc: $s"
  958. exit 1
  959. }
  960. proc warn s {
  961. puts stderr "otcldoc: warning - $s"
  962. }
  963. proc usage {} {
  964. puts stderr "usage: otcldoc [-h index-header-file] [ -d output-dir ] [ -i index-file-name ] [ -c class-string ] files ..."
  965. exit 1
  966. }
  967. proc parse_args argv {
  968. while 1 {
  969. if ![is_arg $argv] {
  970. break
  971. }
  972. set arg [lindex $argv 0]
  973. set argv [lrange $argv 1 end]
  974. set val [lindex $argv 0]
  975. if { $arg == "-d" } {
  976. global outputDir
  977. set outputDir $val
  978. set argv [lrange $argv 1 end]
  979. continue
  980. }
  981. if { $arg == "-h" } {
  982. global indexHeaderFile
  983. set indexHeaderFile $val
  984. set argv [lrange $argv 1 end]
  985. continue
  986. }
  987. if { $arg == "-i" } {
  988. global indexFileName
  989. set indexFileName $val
  990. set argv [lrange $argv 1 end]
  991. continue
  992. }
  993. if { $arg == "-c" } {
  994. global classString
  995. set classString $val
  996. set argv [lrange $argv 1 end]
  997. continue
  998. }
  999. fatal "unknown command option at '$arg'"
  1000. }
  1001. return $argv
  1002. }
  1003. if { $argv == "" } {
  1004. usage
  1005. }
  1006. set argv [parse_args $argv]
  1007. foreach f $argv {
  1008. if { [file extension $f] == ".tcl" } {
  1009. read_tcl_file $f
  1010. } elseif { [file extension $f] == ".cc" } {
  1011. read_cpp_file $f
  1012. } else {
  1013. fatal "$f: unknown file extension"
  1014. }
  1015. }
  1016. set classes [find_tcl_classes]
  1017. arrange_class_hierarchy $classes
  1018. set_generic_class 
  1019. emit_generic_doc
  1020. foreach c $classes {
  1021. set_class $c
  1022. emit_doc $c
  1023. }
  1024. close $outputChannel
  1025. set outputChannel [open $outputDir/$indexFileName w]
  1026. emit_index  $classes
  1027. emit_timestamp
  1028. close $outputChannel
  1029. foreach c [array names tcl_class] {
  1030. if ![inList $classes $c] {
  1031. warn "no doc for C++ class $c"
  1032. }
  1033. }