#!/bin/sh
# -*- tcl -*-
# the next line restarts using ish \
exec iwish "$0" -- "$@"

package require Iwidgets

option add *AutoDOC.home "" widgetDefault
option add *AutoDOC.format "HTML" widgetDefault
option add *AutoDOC.version "2.0" widgetDefault

option add *AutoDOC.makewhatis "/usr/lib/makewhatis" widgetDefault
option add *AutoDOC.ansicpp "gcc -E" widgetDefault
option add *AutoDOC.cppinput "file" widgetDefault

option add *AutoDOC.includepath "[file join [exec pwd] include]" widgetDefault
option add *AutoDOC.end "*.h" widgetDefault

option add *AutoDOC.htmldir "[file join [exec pwd] html]" widgetDefault
option add *AutoDOC.url "file:[file join [exec pwd] html]" widgetDefault
option add *AutoDOC.htmlbackground "0xffffff" widgetDefault

option add *AutoDOC.manpath "[file join [exec pwd] man]" widgetDefault
option add *AutoDOC.manual "C Library Functions" widgetDefault
option add *AutoDOC.mansection "3" widgetDefault
option add *AutoDOC.organization "IfI WWU Muenster" widgetDefault

class itk::AutoDOC {
    inherit itk::Widget
    
    constructor {args} {}

    itk_option define -home home Home ""
    itk_option define -version version Version "2.0"
    itk_option define -format format Format "HTML"
    
    itk_option define -makewhatis makeWhatis MakeWhatis "/usr/lib/makewhatis"

    itk_option define -ansicpp ansiCpp AnsiCPP "gcc -E"
    itk_option define -cppinput cppInput CppInput "file"

    itk_option define -includepath includePath IncludePath "[file join [exec pwd] include]"
    itk_option define -end end End "*.h"

    itk_option define -htmldir htmldir Htmldir "[file join [exec pwd] html]"
    itk_option define -url url Url "file:[file join [exec pwd] html]"
    itk_option define -htmlbackground htmlbackground Htmlbackground "0xffffff"

    itk_option define -manpath manpath Manpath "[file join [exec pwd] man]"
    itk_option define -manual manual Manual "C Library Functions"
    itk_option define -mansection mansection Mansection "3"
    itk_option define -organization organization Organization "IfI WWU Muenster"

    private variable children
    private variable classes
    private variable templates ""
    private variable packages ""
    private variable path ""

    private method getClassName {className}
    private method getBaseClasses {baseClassString}
    private method hasDefaultConstructor {class declarations}
    private method extractPublicFunctions {declarations}
    private method isClassAbstract {className declaration}
    private method updateDataStructures {name file baselist info {isTemplate 0}}
    private method scanpath {path}
    private method classfinder {searchPath}
    private method validSpec {}
    private method parseClassDesc {class filename}
    private method parseMethodBlocks {class filename}

    private variable MANKeyChars { "\-" }  ; # MAN reserved chars
    private variable MANDupChars { "\\-" } ; # Translation tags

    private method makeMAN {}
    private method manize {class}

    private variable body ""
    private variable allclasses ""

    private method makeHTML {}
    private method htmlize {class}
    private method htmlindex {}
    private method htmlizeRecursive {pack base out tab}
    private method htmlTags {}
    private method copyImages {}

    private variable skipblanks "(\t| )*"               ; # Leading blanks
    private variable ntag "^(\t| )*\$"                  ; # Empty line
    private variable eoc "^\};"                         ; # End of class tag
    private variable htag "^(\t| )*//\-"                ; # Headings tag
    private variable dtag "^(\t| )*//\\."               ; # Descriptions tag
    private variable ctag "^(\t| )*//\:"                ; # Code tag 
    private variable eoparam "^(\t| )*\\)"              ; # End-of-parameters tag
    private variable eodecla "^(\t| )*\}"               ; # End-of-deklaration tag
    private variable pritag "^(\t| )*private:"        ; # Private section tag
    private variable protag "^(\t| )*protected:"      ; # Protected section tag

    private variable HTMLKeyChars { "\&" "\<" "\>" }        ; # HTML reserved chars
    private variable HTMLDupChars { "\&amp" "\&lt" "\&gt" } ; # Translation tags

    public variable helpVar ""

    private variable _autorun ""

    private method _message {msg err}
    private method _scan {}
    private method _make {}
    private method _edit {}
    private method _index {}
    private method _about {}
    
    private method _menuHelp {name1 name2 op}
}

#
# AutoDOC HTML Dialog
#
class itk::AutoDOC::htmlDialog {
    inherit iwidgets::Dialog
    constructor {args} {}

    itk_option define -htmldir htmldir Htmldir ""
    itk_option define -url url Url ""
    itk_option define -htmlbackground htmlbackground Htmlbackground ""

    private method _saveOptions {}
}

#
# AutoDOC MAN Dialog
#
class itk::AutoDOC::manDialog {
    inherit iwidgets::Dialog
    constructor {args} {}

    itk_option define -manpath manpath Manpath ""
    itk_option define -manual manual Manual ""
    itk_option define -mansection mansection Mansection ""
    itk_option define -organization organization Organization ""

    private method _saveOptions {}
    private method _selectSection {}

}


# ------------------------------- #
# -        AutoDOC Methods      - #
# ------------------------------- #

body itk::AutoDOC::constructor {args} {

    global format

    if {[regsub {\-h[elp]} $args "" args]||[regsub {\-\?} $args "" args]} {
        puts "Usage: autodoc \[options\]\n"
	puts "-h -home <path>\n"
	puts "-a -auto"
        puts "-f -format HTML | MAN\n"
	puts "-c -ansicpp <CPP>"
	puts "-n -cppinput file | stdin"
	puts "-i -includepath <path>\n"
	puts "-e -end <suffix>"
        puts "-d -htmldir <path>"
        puts "-u -url <URL>"
        puts "-b -htmlbackground \$rrggbb | <image-file>\n"
        puts "-p -manpath <path>"
        puts "-m -manual <name>"
        puts "-s -mansection (1-8) | n"
        puts "-o -organization <name>\n"
	puts "-w -makewhatis <MAKEWHATIS>"
        exit
    }

    regsub {\-h } $args "-home " args
    regsub {\-f } $args "-format " args
    regsub {\-c } $args "-ansicpp " args
    regsub {\-n } $args "-cppinputfile " args
    regsub {\-i } $args "-includepath " args
    regsub {\-e } $args "-end " args
    regsub {\-d } $args "-htmldir " args
    regsub {\-u } $args "-url " args
    regsub {\-b } $args "-htmlbackground " args
    regsub {\-p } $args "-manpath " args
    regsub {\-m } $args "-manual " args
    regsub {\-s } $args "-mansection " args
    regsub {\-o } $args "-organization " args
    regsub {\-w } $args "-makewhatis " args
    regsub {\-a } $args "-auto " args

    if {[regsub {\-auto} $args "" args]} {
	set _autorun 1
    } {
	set _autorun 0
    }

    #
    # Include Path Dialog
    #
    itk_component add pathDialog {
	dialog $itk_interior.dlgPATH -modality "application"
    } {
    }
    set pd $itk_component(pathDialog)
    $pd hide Apply
    $pd hide Help
    
    itk_component add includePath {
	entryfield [$itk_component(pathDialog) childsite].path \
	    -labeltext "Include Path:" \
	    -width 40
    } {
    }
    set ip $itk_component(includePath)
    pack $ip -fill x -expand 1

    $pd buttonconfigure OK -command "\
	$itk_interior config -includepath \[$ip get\];
	$pd deactivate 1"

    #
    # HTML Dialog
    #
    itk_component add htmlDialog {
	htmlDialog $itk_interior.htmlDialog \
	    -title "HTML Preferences"
    } {
	keep -htmldir -url -htmlbackground
    }

    #
    # MAN Dialog
    #
    itk_component add manDialog {
	manDialog $itk_interior.manDialog \
	    -title "MAN Preferences"
    } {
	keep -manpath -manual -mansection -organization
    }

    #
    # About Dialog
    #
    itk_component add aboutDialog {
	dialog $itk_interior.dlgAbout \
	    -title "About AutoDOC"
    } {
    }
    set ad $itk_component(aboutDialog)
    $ad hide Apply
    $ad hide Cancel
    $ad hide Help
    $ad buttonconfigure OK -text "Dismiss"
    itk_component add aboutLogo {
        label [$itk_component(aboutDialog) childsite].logo \
	    -relief raised -border 3 -bg darkgrey
    } {
    }
    grid $itk_component(aboutLogo)
    itk_component add aboutText {
	label [$itk_component(aboutDialog) childsite].label
    } {
    }
    grid $itk_component(aboutText)
	    
    #
    # Feedback Shell
    #
    itk_component add progress {
	shell $itk_interior.piShell \
	    -title "Progress Indicator"
    } {
    }
    itk_component add feedback {
	feedback $itk_component(progress).feedback \
	    -labeltext "" -labelpos n -barcolor #adf
    } {
    }
    pack $itk_component(feedback) -fill x -padx 5 -pady 5

    #
    # AutoDOC Menubar
    #
    itk_component add menubar {
	menubar $itk_interior.mb -helpvariable [scope helpVar] -menubuttons {
	    menubutton file -text Project -menu {
		options -tearoff false
		command scan -label "Scan ..." \
		    -helpstr "Scan an include directory" \
		    -command "[code $this _scan]"
		command make -label "Make ..." -state disabled \
		    -helpstr "Start document generation" \
		    -command "[code $this _make]"
		separator sep1
		command exit -label Exit -command {exit} \
		    -helpstr "Exit AutoDOC"
	    }
	    menubutton edit -text Edit -menu {
		options -tearoff false
		command classes -label "Class Lists" -state disabled \
		    -helpstr "Edit the generated class lists" \
		    -command "[code $this _edit]"
	    }
	    menubutton formats -text Formats -menu {
		options -tearoff false -selectcolor blue
		radiobutton docHTML -label "HTML" \
		    -command "[list [code $this config -format HTML]]" \
		    -variable ::format -value "HTML" \
		    -helpstr "Select HTML output format"
		radiobutton docMAN -label "MAN" \
		    -command "[list [code $this config -format MAN]]" \
		    -variable ::format -value "MAN" \
		    -helpstr "Select MAN output format"
		separator sep2
		command prefs -label "Preferences" \
			-helpstr "Change format preferences" \
			-command "[list catch [list [component htmlDialog] activate]]"
	    }
	    menubutton help -text "Help" -menu {
		options -tearoff false
		command index -label "Index" -state disabled \
		    -helpstr "Show help index" \
		    -command "[code $this _index]"
		command about -label "About" \
		    -helpstr "About AutoDOC" \
		    -command "[code $this _about]"
	    }
	}
    } {
    }
    pack  $itk_component(menubar) -anchor nw -fill x

    #
    # AutoDOC LOGO 
    #
    itk_component add logoframe {
	frame $itk_interior.lf -border 2 -relief sunken -bg white
    } {
    }
    pack $itk_component(logoframe) \
	-fill both -expand 1 -padx 3 -pady 3

    itk_component add logo {
	label $itk_component(logoframe).logo \
	    -borderwidth 0
    } {
    }
    grid $itk_component(logo)


    #
    # Status Field
    #
    itk_component add statusframe {
	frame $itk_interior.sf -relief groove -border 2
    } {
    }
    pack $itk_component(statusframe) -fill x -padx 3 -pady 3
    itk_component add status {
	label $itk_component(statusframe).status -justify left
    } {
    }
    pack $itk_component(status) -fill x -side left -anchor w

    eval itk_initialize $args

    trace variable [scope helpVar] w [code $this _menuHelp]

    $itk_component(pathDialog) config -title "AutoDOC [cget -version] - Include Path" 
    $itk_component(includePath) insert end [cget -includepath]	

    image create photo logo -file "[file join [cget -home] lib AutoDOC.gif]"
    $itk_component(logo) config -image logo

    $itk_component(aboutLogo) config \
        -image logo
    $itk_component(aboutText) config \
	-text "\nJoerg Reiberg & Juergen Doellner\n\n(c)1997 IfI WWU Muenster"

    if {$_autorun} {

	frame .f -bg white
	pack .f -fill both -expand 1

	label .f.l1 -image logo -borderwidth 0
	grid .f.l1

	label .f.l2 \
	    -bg white \
	    -text "Joerg Reiberg & Juergen Doellner\n\n(c)1997 IfI WWU Muenster\nGermany."
	grid .f.l2

	after 2000 "
	    [code $this _scan]
	    [code $this _make]
	    exit
	"
    }
}

#
# Home Option
#
configbody itk::AutoDOC::home {
    global env
    if {$itk_option(-home)==""} {
	if {[info exists env(AUTODOC_HOME)]} {
	    set itk_option(-home) $env(AUTODOC_HOME)
	} {
	    set itk_option(-home) "[exec pwd]"
	}
    }
}

#
# Version Option
#
configbody itk::AutoDOC::version {
    wm title . "AutoDOC $itk_option(-version)"
}

#
# Format Option
# 
configbody itk::AutoDOC::format {
    global format
    set mb [component menubar]
    set pb [$mb path *prefs]
    if {$itk_option(-format)=="HTML"} {    
	$mb menuconfigure $pb \
	    -command "[list catch [list $itk_component(htmlDialog) activate]]"
	if {$format!="HTML"} {
	    $mb invoke [$mb path *docHTML]
	} 
    }
    if {$itk_option(-format)=="MAN"} {
	$mb menuconfigure $pb \
	    -command "[list catch [list $itk_component(manDialog) activate]]"
	if {$format!="MAN"} {
	    $mb invoke [$mb path *docMAN]
	}
    }
}


# ------------------------------- #
# -        Menu Callbacks       - #
# ------------------------------- #

#
# Scan Callback
#
body itk::AutoDOC::_scan {} {

    set mb [component menubar]
    set fb [component feedback]
    set pg [component progress]

    if {!$_autorun} {
	$itk_component(pathDialog) center $itk_interior
	set run [$itk_component(pathDialog) activate]
    } {
	set run 1
    }

    if {$run} {

	_message "Parsing C++ includes ..."
	$mb menuconfigure [$mb path *make] -state "disabled"
	$mb menuconfigure [$mb path *scan] -state "disabled"
	$fb reset
	$fb config -labeltext {}
	$pg center $itk_interior
	$pg activate

	# Run Classfinder
	if {[catch {classfinder [cget -includepath]} errorMsg]} {
	    _message "$errorMsg" err
	} {
	    # Valiadate specification file
	    _message "Validating specification ..."
	    if {[catch {validSpec} errorMsg]} {
		_message "$errorMsg" err	
		$pg deactivate
	    }
	    _message "Finished scanning classes."
	    $mb menuconfigure [$mb path *make] -state normal
	}

	$mb menuconfigure [$mb path *scan] -state normal
	$pg deactivate
    }
}

#
# Edit Callback
#
body itk::AutoDOC::_edit {} {
}

#
# Make Callback
#
body itk::AutoDOC::_make {} {

    set mb [component menubar]
    set fb [component feedback]
    set pg [component progress]

    # Disable some menus
    [$mb component formats] config -state disabled
    $mb menuconfigure [$mb path *scan] -state disabled
    $mb menuconfigure [$mb path *make] -state disabled

    # Show progress shell
    $fb reset
    $fb config -labeltext ""
    $pg center $itk_interior
    $pg activate

    # Start 
    if {[catch {make[cget -format]} errorMsg]} {
	_message "$errorMsg" err
    } {
	_message "Finished documentation."
    }

    # Reset menus' states
    [$mb component formats] config -state normal
    $mb menuconfigure [$mb path *scan] -state normal
    $mb menuconfigure [$mb path *make] -state normal

    # Hide progress shell
    $pg deactivate
    ::update
}

#
# Help Index Callback
#
body itk::AutoDOC::_index {} {
    if [catch {exec netscape -remote "openURL(http://www.uni-muenster.de/informatik/u/reiberg/autodoc/index.html)" &}] {
	exec netscape http://www.uni-muenster.de/informatik/u/reiberg/autodoc/index.html &
    }
}

#
# HelpAbout Callback
#
body itk::AutoDOC::_about {} {
    $itk_component(aboutDialog) center $itk_interior
    catch {$itk_component(aboutDialog) activate}
}

#
# Report Help on Menu in Status Field
#
body itk::AutoDOC::_menuHelp {name1 name2 op} {
    [.autodoc component status] config -text [set $name1]
}

#
# 1) When not in "autorun" mode, write message to "status" entry
# 2) When in "autorun" mode, write messgae to "stdout" and exit on error.
#
body itk::AutoDOC::_message {msg {err {}}} {
    if {$_autorun} {
	puts "$msg"
	flush stdout
	if {$err!={}} {
	    exit
	}
    } {
	[component status] config -text "$msg"
	::update
    }
}


# ------------------------------- #
# -     Classfinder Methods     - #
# ------------------------------- #

#
# extract class name from a string
#
body itk::AutoDOC::getClassName {className} {
    set className [split $className "\n"]
    while {[string trim [set name [lindex $className end]]] == {}} {
	set className [lreplace $className end end]
    }
    regsub {^class } $name "" name
    set name [string trim $name]

    return $name
}

#
# extract names of base classes from a comma-separated string
#
body itk::AutoDOC::getBaseClasses {baseClassString} {
    set tmp ""
    regsub {^:} $baseClassString "" tmp
    set tmp [split $tmp ","]
    
    set baseclasses {}
    foreach base $tmp {
        regsub -all {(public|protected|private|virtual)} $base "" base
        set base [string trim $base]    
        if { $base != "" } {
            lappend baseclasses [string trim $base]
        }
    }
    return [string trim $baseclasses]
}

#
# return true if a class has a default constructor
#
body itk::AutoDOC::hasDefaultConstructor {class declarations} {
    foreach decl [split $declarations ";"] {
        set decl [string trim $decl]

        if { [regexp "$class ?\\\(\\\)" $decl] } {
            return 1
        }
    }
    return 0
}

#
# find the public member functions of a class
#
body itk::AutoDOC::extractPublicFunctions {declarations} {
    set publicMode 0
    set publicFunctions {}

    # foreach expression
    foreach decl [split $declarations ";"] {
        set decl [string trim $decl]

        # remember access permission (public, protected, private)
        while { [regexp {^[^:]*(private|protected|public) ?: ?(.*)} $decl\
                 tmp type decl] } {
                     if { $type == "public" } {
                         set publicMode 1
                     } else {
                         set publicMode 0
                     }
                 }
        
        # find functions:
        # declarations ending with a closing parenthesis, and
        # NOT beeing a declaration of a friend function or 
        # a pointer to function.
        if { $publicMode && [regexp {(.*\)) ?(const)?$} $decl tmp decl] &&\
            ![regexp "^friend" $decl] &&\
            ![regexp {^[^\(]*\( ?\*} $decl] } {
	    lappend publicFunctions [string trim $decl]
        }        
    }
    return $publicFunctions
}

#
# encode the body of a class declaration into a list
#
body itk::AutoDOC::isClassAbstract {className declaration} {
    set abstractClass [regexp {\) ?= ?0 ?;} $declaration]
    set noPublicConstructor 1

    if {!$abstractClass} {
	foreach func [extractPublicFunctions $declaration] {
	    if { [regexp "^(inline *)?$className ?\\\((.*)\\\)$" $func tmp argList] } {
                set noPublicConstructor 0
	    }
	}
    } else {
	return 1
    }

    # if there is no public constructor and no (protected) default 
    # constructor declared, the class is not abstract, because the
    # C++ compiler will generate a public default constructor.

    if {$noPublicConstructor && 
	![hasDefaultConstructor $className $declaration] } {
	return 0
    }    
    return $noPublicConstructor
}

body itk::AutoDOC::updateDataStructures {name file baselist info {isTemplate 0}} {

    set realBaseList {}
    foreach base $baselist {
	regsub {<.*>} $base "" realBase
	if $isTemplate {
	    set base $realBase
	}
	lappend realBaseList $base
	if {[lsearch [array names children] $realBase] >= 0} {
	    lappend children($realBase) $name
	} else {
	    set children($realBase) $name
	}
    }
    set tmp [split $file "/"]
    if {[llength $tmp] > 1} {
	set package [lindex $tmp 0]
    } else {
	set package ""
    }
    if {[lsearch $packages $package] < 0} {
	lappend packages $package
    }
	
    regsub "^$package/" $file "" file
    set flags {}
    if $info {
	lappend flags "abstract"
    }
    if $isTemplate {
	lappend flags "template"
    }
    
    if {$package == {}} {
	set package "\{\}"
    }
    set classes($name) "$package $file \{$realBaseList\} \{$flags\}"
}

body itk::AutoDOC::scanpath {path} {
    set files ""
    set LS [exec ls $path]
    foreach f $LS {
	set F [file join $path $f]
	if {[regexp {(.h$)|(.hh$)|(.H$)} $f] && [file isfile $F] && [file readable $F]} {
	    append files " $F"
	}
	if {[file isdirectory $F] && [file executable $F]} {
	    append files [scanpath $F]
	}
    }
    return $files
}

body itk::AutoDOC::classfinder {searchPath} {

    set files {}
    set fullfiles {}
    set macros ""
    set outFileName stdout
    set packages {}
    
    #
    # Get the files 
    #
    foreach p $searchPath {
	if {![file isdirectory $p]} {
	    error "Include path \"$p\" not found"
	} {
	    foreach file [scanpath $p] {
		if {![regexp {/\.} $file]} { 
		    lappend fullfiles $file
		    regsub $p $file "" file
		    lappend files [string trim $file "/"]
		}
	    }
	}
    }

    puts "Files: $files"

    [component feedback] config -steps [llength $files] -barcolor "#0020ff"
    [component feedback] step
    ::update

    #
    # Process the files
    #
    foreach file $fullfiles {
	set inFile [open $file r]
	set code [read $inFile]
	close $inFile
	set file [lindex $files 0]
	set files [lreplace $files 0 0]

	# Update Window
	[component feedback] config -labeltext "$file"
	[component feedback] step
	::update

	# remove preprocessor directives
	regsub -all "#include" $code "//" code
	regsub -all "#define" $code "//" code
	regsub -all "#undef" $code "//" code
	regsub -all "#if" $code "//" code
	regsub -all "#elif" $code "//" code
	regsub -all "#else" $code "//" code
	regsub -all "#ifdef" $code "//" code
	regsub -all "#ifndef" $code "//" code
	regsub -all "#endif" $code "//" code
	regsub -all "\t" $code " " code

	if {[string match [cget -cppinput] stdin]} {
	    # start preprocessor to remove comments
	    set code [eval exec "[cget -ansicpp]" $macros << \$code]
	} else {
	    exec cat > g_tmp.h << $code
	    set code [eval exec "[cget -ansicpp]" $macros g_tmp.h]
	    exec rm -f g_tmp.h
	}
	regsub -all "(^|\n)( |\t)*\n" $code "\n" code
	regsub -all "\ *\n" $code "\n" code
	regsub -all "\t|\n" $code " " code

	# flatten block hierarchy
	set regex "^\(.*\{\[^\{\}\]*\)\{\[^\{\}\]*\}\(.*\)\$"
	while { [regsub -all $regex $code {\1;\2} code] } {}
	regsub -all {;;} $code ";" code
	regsub -all { ;} $code ";" code

	# extract typedefs for template classes
	while { [regexp {typedef ([^;\{]+(\{[^\}]*\})?)*;} $code typeDef] } {
	    regsub {typedef ([^;\{]+(\{[^\}]*\})?)*; ?} $code "" code
	}
    
	set blocks [split $code "\}"]

	foreach block $blocks {

	    # ignore friends
	    regsub -all {([^a-zA-Z0-9_])friend [^;]*;} $block {\1} block
	    
	    # ignore class forward declarations
	    regsub -all {[ ]?(template ?<[^>]*> )?class [a-zA-Z0-9_]*[ ]?;}\
		$block "" block
	    
	    set regex \
		"template ?<(\[^>\]*)>( )?(\n)?class (\[^:\]*)(\[^\{\]*)\{(.*)\$"
	    if { [regexp $regex $block \
		      tmp param dummy dummy className baseClasses declaration ] } {
		
		if {[llength $className] > 1} continue
		
		set name [getClassName $className]
		set base [getBaseClasses $baseClasses]
		set info [isClassAbstract $name $declaration]
		
		updateDataStructures $name $file $base $info 1

	    }
	    
	    set regex "class (\[^:\]*)(\[^\{\]*)\{(.*)\$"
	    if { [regexp $regex $block\
		      tmp className baseClasses declaration ] } {
		
		if {[llength $className] > 1} continue
		
		set name [getClassName $className]
		set base [getBaseClasses $baseClasses]
		set info [isClassAbstract $name $declaration]
		
		updateDataStructures $name $file $base $info

	    }
	}
    }

    set path "$searchPath"

    set baseclassList [array names children]
    foreach class [lsort [array names classes]] {
	if {[lsearch $baseclassList $class] >= 0} {
	    set childlist $children($class)
	} else {
	    set childlist {}
	}
	set classes($class) [linsert $classes($class) 3 $childlist]
    }
}


# ------------------------------- #
# -     Classparser Methods     - #
# ------------------------------- #

#
# Validate sourced specifications
#
body itk::AutoDOC::validSpec {} {

    # Check for valid class array
    if [catch {array names classes}] {
	error "\"class\" array is not defined"
    }

    # Check for valid include directories
    if {$path=={}} {
	error "\"path\" is not defined"
    }
    foreach i $path {
	if ![file isdirectory $i] {
	    error "No directory $i"
	}
    }

    # Check for packages
    if {$packages=={}} {
	error "\"packages\" is not defined"
    }

    # Configure progress indicator
    set n [llength [array names classes]]
    [component feedback] configure -steps $n
    [component feedback] reset
    ::update
    return {}
}

#
# Search "$class" in file named "$filename" and parse it's description 
#
body itk::AutoDOC::parseClassDesc {class filename} {

    set desc ""
    if [file exists $filename] {
	set inp [open $filename r]
	set line [gets $inp]
	while {![regexp "${skipblanks}class $class\(:| \)" $line] \
             &&![eof $inp]} {
	    set line [gets $inp]
	}
	set line [gets $inp]
	if {![eof $inp]} {
	    while {[regexp $dtag $line]&&![eof $inp]} {
		regsub $dtag $line "" line
		foreach key $[cget -format]KeyChars dup $[cget -format]DupChars {
		    regsub -all $key $line \\$dup line
		}
		lappend desc $line
		set line [gets $inp]
	    }
	}
	close $inp
    }
    return $desc
}

#
# Search "$class" in file named "$filename" and parse it's method blocks
#
body itk::AutoDOC::parseMethodBlocks {class filename} {

    set blocks ""
    set head ""
    set methods ""
    set desc ""

    if [file exists $filename] {
	set inp [open $filename r]
	set line [gets $inp]

	# Skip comment-,include- and class stuff
	while {(![regexp "${skipblanks}class $class\(:| \)" $line])&&(![eof $inp])} {
	    set line [gets $inp]
	}

	set line [gets $inp]

	if {[eof $inp]} {
	    lappend blocks [list $head $methods $desc]
	    close $inp
	    return $blocks
	}

	# Skip class description
	while {[regexp $dtag $line]&&![eof $inp]} {
	    set line [gets $inp]
	}

	# Read all method blocks
	while {![eof $inp]&&![regexp $eoc $line]} {

	    set head ""
	    set methods ""
	    set desc ""
		    
	    # Skip uncommented stuff
	    while {![regexp $htag $line]&&![regexp $eoc $line]&&![eof $inp]} {
		set line [gets $inp]
	    }	    
		
	    # Leave if class is uncommented
	    if {[eof $inp]||[regexp $eoc $line]} {
		lappend blocks [list $head $methods $desc]
		close $inp
		return $blocks
	    }

	    # Read heading
	    if {[cget -format]=="HTML"} {
		set indent "<dt>"
		set head "<dl>"
	    } {
		set indent ""
		set head ""
	    }
	    while {[regexp $htag $line]&&![eof $inp]} {
		regsub $htag $line "" line
		foreach key $[cget -format]KeyChars dup $[cget -format]DupChars {
		    regsub -all $key $line \\$dup line
		}
		lappend head "${indent}${line}"
		set line [gets $inp]
	    }
	    
	    # Read methods
	    if {[eof $inp]||[regexp $eoc $line]} {
		lappend blocks [list $head $methods $desc]
		close $inp
		return $blocks
	    }

	    while {![regexp $dtag $line]&&![regexp $htag $line] \
		       &&![regexp $ctag $line]&&![regexp $ntag $line] \
		       &&![regexp $pritag $line]&&![regexp $protag $line] \
		       &&![regexp $eoc $line]&&![eof $inp]} {
		regsub "^$skipblanks" $line "" line
		foreach key $[cget -format]KeyChars dup $[cget -format]DupChars {
		    regsub -all $key $line \\$dup line
		}
		if {([regexp {\($} $line])||([regexp {\{$} $line]) } {
		    lappend methods "$indent$line"
		    if {[cget -format]=="HTML"} {
			set indent "<dd>"
		    } {
			set indent ""
		    }
		} elseif {[regexp $eoparam $line]||[regexp $eodecla $line]} {
		    if {[cget -format]=="HTML"} {
			set indent "<dt>"
		    } {
			set indent ""
		    }
		    lappend methods "${indent}${line}"
		} {
		    lappend methods "${indent}${line}"
		}
		set line [gets $inp]
	    }
		    
	    if {[eof $inp]||[regexp $eoc $line]} {
		lappend blocks [list $head $methods $desc]
		close $inp
		return $blocks
	    }

	    # Read description
	    while {([regexp $dtag $line]||[regexp $ctag $line])&&![eof $inp]} {
		foreach key $[cget -format]KeyChars dup $[cget -format]DupChars {
		    regsub -all $key $line \\$dup line
		}
		if {[regexp $dtag $line]} {
		    set codeIndent 0
		    if {[cget -format]=="HTML"} {
			set prefix "<i>"
			set postfix "</i>"
		    } {
			set prefix ""
			set postfix ""
		    }
		    regsub $dtag $line "" line
		} {
		    regsub $ctag $line "" line
		    if {[cget -format]=="HTML"} {
			set codeIndent 0
			while {[regsub "^\ " $line "" line]} {
			    incr codeIndent 10
			}
			set prefix "<code><spacer type=horizontal size=$codeIndent>"
			set postfix "</spacer></code>"
		    } {
			set prefix ""
			set postfix ""
		    }
		}
		lappend desc ${prefix}${line}${postfix}
		set line [gets $inp]
	    }

	    lappend blocks [list $head $methods $desc]
	}
        close $inp
    }
    return $blocks
}


# ------------------------------- #
# -       HTMLizer Methods      - #
# ------------------------------- #

#
# HTMLizing Sequence
#
body itk::AutoDOC::makeHTML {} {
     
    # Configure background tag
    set bg [cget -htmlbackground]
    if {$bg!={}} {
	if [regsub {^0x} $bg "" bg] {
	    set body "<body bgcolor=$bg>"
	} {
	    set body "<body background=\"$bg\">"
	}
    }

    # Search resp. make HTML directory
    if ![file exists [cget -htmldir]] {
	exec mkdir -p [cget -htmldir]
    } {
	if ![file isdirectory [cget -htmldir]] {
	    [component status] config -text "A file [cget -htmldir] already exists."
	    ::update
	    return
	}
    }

    # Check AutoDOC Home
    if {![file exists [cget -home]]||![file isdirectory [cget -home]]} {
	[component status] config -text "AutoDOC_HOME: [cget -home] doesn't exist."
	::update
	return
    }

    # HTMLize all classes
    [component status] config -text "Writing HTML files ..."
    ::update
    foreach c [array names classes] {
	htmlize $c
    }

    # Create index files
    [component status] config -text "Writing HTML indices ..."
    ::update
    htmlindex

    # Copy image files
    [component status] config -text "Copying image files ..."
    ::update
    copyImages
}

#
# Make HTML description of "$class"
# Output written to ${htmldir}/${package}/${class}.html
#
body itk::AutoDOC::htmlize {class} {

    [component feedback] configure -labeltext $class

    if {[lsearch [array names classes] $class]<0} {
	return
    }
    set specs $classes($class)
    set package [lindex $specs 0]

    set incFile [lindex $specs 1]
    set htmlFile "${class}.html"

    set workDir [file join [cget -htmldir] $package]
    set fdir [file join $workDir [file dirname $htmlFile]]
    if {![file exists $fdir]} {
	exec mkdir -p $fdir
    }

    set baseClass [lindex $specs 2]
    set directChild [lindex $specs 3]
    set flags [lindex $specs 4]

    set hf [file join ${workDir} ${htmlFile}]
    if [file exists $hf] {
	set inctime [file mtime [file join $path $package $incFile]]
	set htmtime [file mtime $hf]
	if {$inctime < $htmtime} {
	    [component feedback] step    
	    ::update
	    return
	}
    }

    set out [open $hf w]

    puts $out "<html>\n"
    puts $out "<!--"
    puts $out " Creator: AutoDOC [cget -version]"
    puts $out " Date:    [exec date]"
    puts $out "-->\n"
    puts $out "<head>"
    puts $out "<title>"
    puts $out "   [string toupper $package] - ${class}"
    puts $out "</title>"
    puts $out "</head>\n"
    puts $out "$body\n"
    puts $out "<h1 align=center> $class </h1>\n"
    puts $out "<base target=left>"

    # Write hints
    set hints [string tolower [lindex $classes($class) 4] ]
    if {$hints!={}} {
	puts $out "<p align=center>"
	puts $out "( <i>${hints}</i> )"
    }

    # Write package link
    puts $out "<p align=center>"
    puts $out "Package: <a href=\"[cget -url]/${package}/index.html\">" nonewline
    puts $out "[string toupper ${package}] </a><br>"
    puts $out "Include: $incFile\n"
    puts $out "<base target=right>"

    # Write class Description
    set classDesc [parseClassDesc $class "${path}/${package}/$incFile"]
    if {$classDesc!={}} {
	puts $out "<p align=center>"
	puts $out "<i>"
	foreach d $classDesc {
	    puts $out "  ${d} <br>"
	}
	puts $out "</i>\n"
	puts $out "\n<p>\n"
    }
    puts $out "<p>"
    puts $out "<hr>"
    puts $out "<p>"

    # Write method blocks
    set methodBlocks [parseMethodBlocks $class "${path}/${package}/$incFile"]
    if {$methodBlocks!={}} {
	set undocumented 1
	puts $out "<b> Methods: </b>"
	puts $out "<dl>"
	foreach block $methodBlocks {
	    set heads [lindex $block 0]
	    set methods [lindex $block 1]
	    set descs [lindex $block 2]
	    if {($heads!={})&&($methods!={})} {
		puts $out "  <dd>"
		puts $out "    <b>"
		foreach h $heads {
		    puts $out "      $h"
		}
		puts $out "    </b><br>"
		foreach m $methods {
		    puts $out "    $m <br>"
		}
		foreach d $descs {
		    puts $out "      <dd> $d"
		}
		puts $out "    </dl>"
		puts $out "    <br>"
		set undocumented 0
	    }
	}
	if {$undocumented} {
	    puts $out "<dt><b> undocumented </b>\n"
	}
	puts $out "</dl>\n"
    }
    
    # Write base class links
    puts $out "<p>"
    if {$baseClass!={}} {
	puts $out "<b> Base Classes: </b>"
	puts $out "<dl>"
	set allBaseClasses ""
	while {$baseClass!={}} {
	    set otherBaseClass ""
	    foreach b $baseClass {
		regsub -all "\<.*\>" $b "" b
		if {[lsearch [array names classes] $b]>=0} {
		    if {[lsearch $allBaseClasses $b]==-1} {
			lappend allBaseClasses $b
			set bspec $classes($b)
			set bpack [lindex $bspec 0]
			set bhtml "${b}.html"
			puts $out "<dd>"
			puts $out "  <a href=\"[cget -url]/${bpack}/${bhtml}\">"
			puts $out "   ${b}"
			puts $out "  </a>"
			if {[info exists classes($b)]} {
			    set newBases [lindex $classes($b) 2]
			} {
			    set newBases {}
			}
			if {$newBases!={}} {
			    lappend otherBaseClass $newBases
			}
		    }
		}
	    }
	    set baseClass $otherBaseClass
	}
	puts $out "</dl>"
    }

    # Write footer
    puts $out "<p>"
    puts $out "<hr>"
    puts $out "<font size=-1><i>"
    puts $out "AutoDOC [cget -version]: "
    puts $out "Created on [exec hostname], [exec date]" 
    puts $out "</i></font>"
    puts $out "</body>"
    puts $out "</html>"
    close $out

    # Update feedback
    [component feedback] step
    ::update
}

#
# Create main index file and index files for all packages
#
body itk::AutoDOC::htmlindex {} {

    # Reset Feedback
    [component feedback] config -labeltext "MAIN Index"
    [component feedback] reset

    # Write global index
    set out [open "[file join [cget -htmldir] index.html]" w]
    puts $out "<html>\n"
    puts $out "<!--"
    puts $out " Creator: AutoDOC [cget -version]"
    puts $out " Date:    [exec date]"
    puts $out "-->\n"
    puts $out "<head>"
    puts $out "<title>"
    puts $out "   AutoDOC - Index"
    puts $out "</title>"
    puts $out "</head>\n"
    puts $out "<frameset rows=\"100,*\">"
    puts $out "  <frame name=\"top\" src=\"packages.html\" scrolling=\"No\">"
    puts $out "  <frameset cols=\"35%,65%\">"
    puts $out "    <frame name=\"left\" src=\"empty.html\" scrolling=\"Yes\">"
    puts $out "    <frame name=\"right\" src=\"credits.html\" scrolling=\"Yes\" >"
    puts $out "  </frameset>"
    puts $out "<noframes>\n"
    puts $out "$body\n"
    puts $out "<h1 align=center>"
    puts $out " AutoDOC [cget -version]"
    puts $out "</h1>\n"
    puts $out "<p align=center>"
    puts $out " HTML files created with"
    puts $out " <a href=\"http://www.uni-muenster.de/informatik/u/reiberg/autodoc\"> "
    puts $out "   AutoDOC [cget -version] "
    puts $out " </a> "
    puts $out "<br>"
    puts $out " require a HTML3.2 and FRAMES capable browser.<br><br>\n"
    puts $out " You should download one of the following\n" 
    puts $out " \[<a href=\"http://home.netscape.com\"> Netscape 3.0 </a>\]"
    puts $out " \[<a href=\"http://www.microsoft.com\"> Internet Explorer </a>\]\n"
    puts $out "</noframes>"
    puts $out "</frameset>"
    puts $out "</body>"
    puts $out "</html>"
    close $out

    # Write empty page
    set out [open "[file join [cget -htmldir] empty.html]" w]
    puts $out "<html>"
    puts $out "$body\n"
    puts $out "</body>"
    puts $out "</html>"
    close $out

    # Write credits page
    set out [open "[file join [cget -htmldir] credits.html]" w]
    puts $out "<html>"
    puts $out "$body\n"
    puts $out "<p align=center>"
    puts $out "<a href=\"http://www.uni-muenster.de/informatik/u/reiberg/autodoc\">"
    puts $out " <img src=\".gfx/AutoDOC.gif\" border=0>"
    puts $out "</a>"
    puts $out "<p align=center>"
    puts $out "Automatic documentation system for C++ headers"
    puts $out "<p align=center>"
    puts $out "<a href=\"http://www.tcltk.com/itcl\">"
    puts $out " <img src=\".gfx/itclins.gif\" border=0>"
    puts $out "</a>"
    puts $out "<p align=center>"
    puts $out "<i> <b>Authors</b><br>  "
    puts $out " <a href=\"http://www.uni-muenster.de/informatik/u/reiberg\"> J.Reiberg</a> &"
    puts $out " <a href=\"http://www.uni-muenster.de/informatik/u/dollner\"> J.D&ouml;llner</a> <br>"
    puts $out " &copy;1996 by "
    puts $out " <a href=\"http://wwwmath.uni-muenster.de/math/inst/info/\"> "
    puts $out " Institut f&uuml;r Informatik </a>,"
    puts $out " <a href=\"http://www.uni-muenster.de\"> "
    puts $out " Universit&auml;t M&uuml;nster</a></i>"
    puts $out "</body>"
    puts $out "</html>"
    close $out
    
    # Write packages index
    set piout [open "[file join [cget -htmldir] packages.html]" w]
    puts $piout "<html>"
    puts $piout "<!--"
    puts $piout " Creator: AutoDOC [cget -version]"
    puts $piout " Date:    [exec date]"
    puts $piout "-->\n"
    puts $piout "<head>"
    puts $piout "<title> AutoDOC - Package-Index </title>"
    puts $piout "</head>\n"
    puts $piout "<base target=left>"
    puts $piout "$body"
    puts $piout "<p align=center>"
    puts $piout "<img src=\".gfx/Packages.gif\"><br>"

    foreach p $packages {

	# Make packagename uppercase
	set pname [string toupper $p]

	# Show package name in progress indicator
	[component feedback] config -labeltext "${pname} Index"

	# Write entry to package index
	set hname $p
	puts $piout " \[<a href=\"[cget -url]/${hname}/index.html\">$pname</a>\]"

	# Write index of package
	set ipout [open "[file join [cget -htmldir] ${p} index.html]" w]
	puts $ipout "<html>"
	puts $ipout "<!--"
	puts $ipout " Creator: AutoDOC [cget -version]"
	puts $ipout " Date:    [exec date]"
	puts $ipout "-->\n"
	puts $ipout "<head>"
	puts $ipout "<title> AutoDOC - ${pname}-Index </title>"
	puts $ipout "</head>\n"
	puts $ipout "$body\n"

	# Collect base classes of package
	set allclasses ""
	set bases ""
        foreach c [array names classes] {
            set isbase 1
	    if {[string compare [lindex $classes($c) 0] $p]==0} {
		foreach ap [lindex $classes($c) 2] {
		    regsub -all "\<.*\>" $ap "" ap
		    if {[lsearch [array names classes] $ap]>=0} {
			if {[string compare [lindex $classes($ap) 0] $p]==0} {
			    set isbase 0
			    break
			}
		    }
		}
		if {$isbase==1} {
		    if {[lsearch $bases $c]<0} {
			lappend bases $c
			lappend allclasses $c
		    }
		}
            }
        }
        set bases [lsort $bases]
 
	# Write Hierarchie and ABC List only if there are classes.
	# Well, an empty package is quite unuseful, but nevertheless ...
	if {$bases!={}} {

	    # Make jump tag
	    puts $ipout "<a name=\"HIE\">"
	    puts $ipout "<h2 align=center> ${pname} </h2>"
	    puts $ipout "<base target=left>"
	    puts $ipout "Goto <a href=\"#ABC\"> ABC List </a>\n"
	    puts $ipout "<base target=right>"

	    puts $ipout "<p>"

	    # Write hierarchie
	    puts $ipout "<b> Hierarchie: </b>"
	    puts $ipout "<p>"
	    foreach b $bases {
		# Write child classes
		htmlizeRecursive $p $b $ipout 0
		# Update feedback
		[component feedback] step
		::update
	    }

	    puts $ipout "<p>"
	    puts $ipout "<hr>"
	    puts $ipout "<p>"

	    # Make jump tag
	    puts $ipout "<base target=left>"
	    puts $ipout "<a name=\"ABC\">"
	    puts $ipout "Goto <a href=\"#HIE\"> Hierarchie </a> <p>"
	    puts $ipout "<base target=right>"
	    puts $ipout "<b> ABC List: </b>"
	    puts $ipout "<p>"
	    
	    # Sort all collected classes for ABC List
	    set allclasses [lsort $allclasses]

	    # Write ABC List
	    foreach c $allclasses {
		set hfn "$c.html"
		puts $ipout "<a href=\"[cget -url]/${p}/${hfn}\"> ${c} </a><br>"
	    }

	}
	close $ipout
    }
    
    puts $piout "\n</body>"
    puts $piout "</html>"
    close $piout

}

#
# Write hyperlinks to class "$base" of package "$pack"
# to file "$out", and do it recursively for all subclasses
# Use $tab for indentation of HTML code (not for layout !) 
# to make HTML code a bit more readable ;-)
#
body itk::AutoDOC::htmlizeRecursive {pack base out tab} {

    # Make "$tab" spaces for indentation
    set indent ""
    for {set i 0} {$i<$tab} {incr i} {
	set indent "$indent " 
    }

    # Make name for HTML file
    set hfn "${base}.html"

    # Get all direct subclasses
    set ch [lindex $classes($base) 3]

    # If there are subclasses ...
    if {$ch!={}} {
	# ... check whether they are in the same package ...
	set onlyOtherPackages 1
	foreach c $ch {
	    if {[lsearch [array names classes] $c]>=0} {
		if {[string compare [lindex $classes($c) 0] $pack]==0} {
		    set onlyOtherPackages 0
		}
	    }
	}
	# ... because index should be for one package only.
	if {!$onlyOtherPackages} {
	    puts $out "${indent}<dl>"
	    puts $out "<a href=\"[cget -url]/${pack}/${hfn}\"> ${base} </a>"
	    foreach c $ch {
		if {[string compare [lindex $classes($c) 0] $pack]==0} {
		    puts $out "${indent} <dd>" nonewline
		    htmlizeRecursive $pack $c $out [expr $tab + 1]
		    if {[lsearch $allclasses $c]==-1} {
			lappend allclasses $c
		    }
		}
	    }
	    puts $out "${indent}</dl>"
	}
    } { # If there are no subclasses ...
	# ... write as list-item if not toplevel
	if {$tab>0} {
	    puts $out "${indent}<dd>" nonewline
	    puts $out "<a href=\"[cget -url]/${pack}/${hfn}\"> ${base} </a>"
	} { # ... write normally with linebreak if toplevel 
	    puts $out "<a href=\"[cget -url]/${pack}/${hfn}\"> ${base} </a><br>"
	}
    }

    # Update progress indicator
    [component feedback] step
    ::update
}

#
# Copy image files for HTML layout
#
body itk::AutoDOC::copyImages {} {

    set gd [file join [cget -htmldir] .gfx]
    if {[file exists $gd]} {
	if {![file isdirectory $gd]} {
	    [component status] config -text "A file $gd already exists."
	    ::update
	    return
	}
    } {
	exec mkdir -p $gd
    }
    catch {exec cp [file join [cget -home] lib "AutoDOC.gif"] $gd}
    catch {exec cp [file join [cget -home] lib "Packages.gif"] $gd}
    catch {exec cp [file join [cget -home] lib "itclins.gif"] $gd}
}


# ------------------------------- #
# -       MANizer Methods       - #
# ------------------------------- #

#
# MANizing Sequence
#
body itk::AutoDOC::makeMAN {} {
     
    # Search resp. make MAN directory
    set mandir [file join [cget -manpath] man[cget -mansection]]
    if ![file exists $mandir] {
	exec mkdir -p $mandir
    } {
	if ![file isdirectory $mandir] {
	    error "A file $mandir already exists."
	}
    }

    # MANize all classes
    [component status] config -text "Writing MAN files ..."
    ::update
    foreach c [array names classes] {
	manize "$c"
    }

    # Make whatis database file
    [component status] config -text "Creating \"whatis\" database ..."
    ::update
    if {[catch [exec [cget -makewhatis] [cget -manpath] ]]} {
	[component status] config -text "Creating \"whatis\" database ... failed."	
    } {
	[component status] config -text "Creating \"whatis\" database ... ok."	
    }
    ::update
}

#
# Make troff format MAN description of "$class"
# Output written to ${manpath}/man${mansection}/${class}.1
#
body itk::AutoDOC::manize {class} {

    [component feedback] configure -labeltext $class
    ::update

    if {[lsearch [array names classes] $class]<0} {
	return
    }

    set specs $classes($class)
    set package [lindex $specs 0]

    set incFile [lindex $specs 1]
    set manFile "${class}.[cget -mansection]"

    set workDir [file join [cget -manpath] man[cget -mansection]]

    set baseClass [lindex $specs 2]
    set directChild [lindex $specs 3]
    set flags [lindex $specs 4]

    set mf [file join ${workDir} ${manFile}]
    if [file exists $mf] {
	set inctime [file mtime [file join ${path} ${package} $incFile]]
	set mantime [file mtime $mf]
	if {$inctime < $mantime} {
	    [component feedback] step
	    return
	}
    }

    set classDesc [parseClassDesc $class "${path}/${package}/$incFile"]
    set out [open $mf w]

    # Write Header
    puts $out ".\\\" Creator: AutoDOC $[cget -version]"
    puts $out ".TH ${class} " nonewline
    puts $out "\"[cget -mansection]\" " nonewline
    puts $out "\"[exec date +%m.%d.%y]\" " nonewline
    puts $out "\"[cget -organization]\" " nonewline
    puts $out "\"[cget -manual]\""

    # Write NAME
    puts $out ".SH NAME"
    puts $out "${class} " nonewline
    set hints [string tolower [lindex $classes($class) 4] ]
    set desc [lindex $classDesc 0]
    if {$hints!={}} {
	puts $out "\\- ($hints) $desc"
    } {
	if {$desc!={}} {
	    puts $out "\\- ${desc}"	
	}
    }

    # Write DESCRIPTION
    if {$classDesc!={}} {
	puts $out ".SH DESCRIPTION"
	foreach d $classDesc {
	    regsub "^$skipblanks" $d "" d
	    regsub {\-} $d "\\-" d
	    puts $out "$d " nonewline
	}
	puts $out ""
    }

    # Write PACKAGE
    if {$package!={}} {
	puts $out ".SH PACKAGE"
	puts $out "${package}"
    }

    # Write HERITAGE
    if {$baseClass!={}} {
	puts $out ".SH HERITAGE"
	set allBaseClasses ""
	while {$baseClass!={}} {
	    set otherBaseClass ""
	    foreach b $baseClass {
		regsub -all "\<.*\>" $b "" b
		if {[lsearch $allBaseClasses $b]==-1} {
		    lappend allBaseClasses $b
		    puts $out ".BR $b \([cget -mansection]\)"
		    if {[info exists classes($b)]} {
			set newBases [lindex $classes($b) 2]
		    } {
			set newBases {}
		    }
		    if {$newBases!={}} {
			lappend otherBaseClass $newBases
		    }
		}
	    }
	    set baseClass $otherBaseClass
	}
	puts $out ""
    }


    # Write SYNOPSIS
    puts $out ".SH SYNOPSIS"
    puts $out "\#include <[file join ${package} ${incFile}]>"
    set methodBlocks [parseMethodBlocks $class "${path}/${package}/$incFile"]
    if {$methodBlocks!={}} {
	foreach block $methodBlocks {
	    puts $out ".sp"
	    set methods [lindex $block 1]
	    set descs [lindex $block 2]
	    if {$methods!={}} {
		set indent 0
		set switchIndent 0
		foreach m $methods {
		    if {[regexp {\([\ ]*$} $m]} {
			set switchIndent 1
		    }
		    if {$indent&&[regexp {\)([\ ]*const)?[\ ]*;[\ ]*$} $m]} {
			set switchIndent 1
		    }
		    if {$indent} {
			if {$switchIndent} {
			    puts $out ".br\n.B \"$m\"\n.br"
			    set indent 0
			    set switchIndent 0
			} {
			    puts $out "   $m"
			}
		    } {
			puts $out ".B \"$m\"\n.br"
			if {$switchIndent} {
			    set indent 1
			    set switchIndent 0
			}
		    }
		}
		if {$descs!={}} {
		    puts $out ".LP\n.RS\n.PD 0\n.PP"
		    foreach d $descs {
			regsub "^$skipblanks" $d "" d
			regsub {\-} $d "\\-" d
			puts $out "$d " nonewline
		    }
		    puts $out "\n.RE\n.PD\n.LP"
		}
	    }
	}
    }
    
    # Write BUGS
    # puts $out ".SH BUGS"
    # puts $out "Not known."

    #Write AUTHOR
    #puts $out ".SH AUTHOR"
    #puts $out ".B ${class}"
    #puts $out "was written by ${author}\(${authoremail}\).

    close $out

    # Update feedback
    [component feedback] step
}


# ------------------------------- #
# -      htmlDialog Methods     - #
# ------------------------------- #

body itk::AutoDOC::htmlDialog::constructor {args} {

    hide Cancel
    hide Apply
    hide Help
    buttonconfigure OK -text "Dismiss" \
    	-command [code $this _saveOptions]
    
    itk_component add dir {
	entryfield [childsite].dir \
	    -labelpos nw -labeltext "HTML base directory" \
	    -width 40
    } {
    }
    $itk_component(dir) config \
	-command "$itk_interior config -htmldir [$itk_component(dir) get]"
    pack $itk_component(dir) -fill x

    itk_component add url {
	entryfield [childsite].url \
	    -labelpos nw -labeltext "URL base"
    } {
    }
    $itk_component(url) config \
	-command "$itk_interior config -url [$itk_component(url) get]"
    pack $itk_component(url) -fill x

    itk_component add back {
	entryfield [childsite].bg \
	    -labelpos nw -labeltext "Background"
    } {
    }
    $itk_component(back) config \
	-command "$itk_interior config -htmlbackground [$itk_component(back) get]"
    pack $itk_component(back) -fill x

    eval itk_initialize $args
}

body itk::AutoDOC::htmlDialog::_saveOptions {} {
    set ad [winfo parent [component hull]]
    $ad config -htmldir [$itk_component(dir) get]
    $ad config -url [$itk_component(url) get]
    $ad config -htmlbackground [$itk_component(back) get]
    deactivate
}

configbody itk::AutoDOC::htmlDialog::htmldir {
    [component dir] delete 0 end
    [component dir] insert end $itk_option(-htmldir)
}

configbody itk::AutoDOC::htmlDialog::url {
    [component url] delete 0 end
    [component url] insert end $itk_option(-url)
}

configbody itk::AutoDOC::htmlDialog::htmlbackground {
    [component back] delete 0 end
    [component back] insert end $itk_option(-htmlbackground)
}


# ------------------------------- #
# -       manDialog Methods     - #
# ------------------------------- #

body itk::AutoDOC::manDialog::constructor {args} {

    hide Cancel
    hide Apply
    hide Help
    buttonconfigure OK -text "Dismiss" \
	-command [code $this _saveOptions]
    
    itk_component add path {
	entryfield [childsite].path \
	    -labelpos nw -labeltext "MAN path" \
	    -width 40
    } {
    }
    $itk_component(path) config \
	-command "$itk_interior config -manpath [$itk_component(path) get]"
    pack $itk_component(path) -fill x

    itk_component add manual {
	entryfield [childsite].manual \
	    -labelpos nw -labeltext "Manual name"
    } {
    }
    $itk_component(manual) config \
	-command "$itk_interior config -manual [$itk_component(manual) get]"
    pack $itk_component(manual) -fill x

    itk_component add section {
	combobox [childsite].section \
	    -labelpos nw -labeltext "Manual Section" \
	    -selectioncommand [code $this _selectSection] \
	    -editable 0
    } {
    }
    $itk_component(section) insert list 1 "(1) User Commands"
    $itk_component(section) insert list 2 "(2) System Calls"
    $itk_component(section) insert list 3 "(3) Subroutines"
    $itk_component(section) insert list 4 "(4) Devices"
    $itk_component(section) insert list 5 "(5) File Formats"
    $itk_component(section) insert list 6 "(6) Games"
    $itk_component(section) insert list 7 "(7) Miscellaneous"
    $itk_component(section) insert list 8 "(8) System Admin."
    $itk_component(section) insert list 9 "(n) New"
    [$itk_component(section) component entry] config -state normal
    pack $itk_component(section) -fill x

    itk_component add organization {
	entryfield [childsite].organization \
	    -labelpos nw -labeltext "Organization"
    } {
    }
    $itk_component(organization) config \
	-command "$itk_interior config -organization [$itk_component(organization) get]"
    pack $itk_component(organization) -fill x    

    eval itk_initialize $args

}

body itk::AutoDOC::manDialog::_saveOptions {} {
    set ad [winfo parent [component hull]]
    $ad config -manpath [$itk_component(path) get]
    $ad config -manual [$itk_component(manual) get]
    $ad config -organization [$itk_component(organization) get]
    deactivate
}

body itk::AutoDOC::manDialog::_selectSection {} {
    set combo $itk_component(section)
    set sect [$combo curselection]
    config -mansection "RADIO[expr $sect+1]"
}

configbody itk::AutoDOC::manDialog::manpath {
    [component path] delete 0 end
    [component path] insert end $itk_option(-manpath)
}

configbody itk::AutoDOC::manDialog::manual {
    [component manual] delete 0 end
    [component manual] insert end $itk_option(-manual)
}

configbody itk::AutoDOC::manDialog::mansection {
    set combo [component section]
    if {![regsub {RADIO} $itk_option(-mansection) {} itk_option(-mansection)]} {
        regsub {^n$} $itk_option(-mansection) 9 itk_option(-mansection)
        set S $itk_option(-mansection)
        set S [lindex $S 0]
        set S [expr $S - 1]
        if {[regexp {^[0-8]$} $S]} {
 	    $combo selection clear 0 end
	    $combo selection set $S $S
	    $combo clear entry
	    set Sec [$combo get $S]
	    $combo insert entry 1 $Sec
        }
    } {
	regsub {9} $itk_option(-mansection) n itk_option(-mansection)
    }
}

configbody itk::AutoDOC::manDialog::organization {
    [component organization] delete 0 end
    [component organization] insert end $itk_option(-organization)
}


# ------------------------------- #
# -      Start Application      - #
# ------------------------------- #

set format "HTML"
eval AutoDOC .autodoc $argv
if {![regexp {\-a[uto]} $argv]} {
    pack .autodoc -expand 1 -fill both
}
