home *** CD-ROM | disk | FTP | other *** search
- #
- # buildhelp.tcl --
- #
- # Program to extract help files from TCL manual pages or TCL script files.
- # The help directories are built as a hierarchical tree of subjects and help
- # files.
- #
- #------------------------------------------------------------------------------
- # Copyright 1992-1993 Karl Lehenbauer and Mark Diekhans.
- #
- # Permission to use, copy, modify, and distribute this software and its
- # documentation for any purpose and without fee is hereby granted, provided
- # that the above copyright notice appear in all copies. Karl Lehenbauer and
- # Mark Diekhans make no representations about the suitability of this
- # software for any purpose. It is provided "as is" without express or
- # implied warranty.
- #------------------------------------------------------------------------------
- # $Id: buildhelp.tcl,v 3.0 1993/11/19 07:00:46 markd Rel $
- #------------------------------------------------------------------------------
- #
- # For nroff man pages, the areas of text to extract are delimited with:
- #
- # '\"@help: subjectdir/helpfile
- # '\"@endhelp
- #
- # start in column one. The text between these markers is extracted and stored
- # in help/subjectdir/help. The file must not exists, this is done to enforced
- # cleaning out the directories before help file generation is started, thus
- # removing any stale files. The extracted text is run through:
- #
- # nroff -man|col -xb {col -b on BSD derived systems}
- #
- # If there is other text to include in the helpfile, but not in the manual
- # page, the text, along with nroff formatting commands, may be included using:
- #
- # '\"@:Other text to include in the help page.
- #
- # A entry in the brief file, used by apropos my be included by:
- #
- # '\"@brief: Short, one line description
- #
- # These brief request must occur with in the bounds of a help section.
- #
- # If some header text, such as nroff macros, need to be preappended to the
- # text streem before it is run through nroff, then that text can be bracketed
- # with:
- #
- # '\"@header
- # '\"@endheader
- #
- # If multiple header blocks are encountered, they will all be preappended.
- #
- # For TCL script files, which are indentified because they end in ".tcl",
- # the text to be extracted is delimited by:
- #
- # #@help: subjectdir/helpfile
- # #@endhelp
- #
- # And brief lines are in the form:
- #
- # #@brief: Short, one line description
- #
- # The only processing done on text extracted from .tcl files it to replace
- # the # in column one with a space.
- #
- #
- #-----------------------------------------------------------------------------
- #
- # To generate help:
- #
- # buildhelp helpDir brief.brf filelist
- #
- # o helpDir is the help tree root directory. helpDir should exists, but any
- # subdirectories that don't exists will be created. helpDir should be
- # cleaned up before the start of manual page generation, as this program
- # will not overwrite existing files.
- # o brief.brf is the name of the brief file to create form the @brief entries.
- # It must have an extension of ".brf". It will be created in helpDir.
- # o filelist are the nroff manual pages, or .tcl, .tlib files to extract
- # the help files from. If the suffix is not .tcl or .tlib, a nroff manual
- # page is assumed.
- #
- #-----------------------------------------------------------------------------
-
- #@package: TclX-buildhelp buildhelp
-
- #-----------------------------------------------------------------------------
- # Truncate a file name of a help file if the system does not support long
- # file names. If the name starts with `Tcl_', then this prefix is removed.
- # If the name is then over 14 characters, it is truncated to 14 charactes
- #
- proc TruncFileName {pathName} {
- global truncFileNames
-
- if {!$truncFileNames} {
- return $pathName}
- set fileName [file tail $pathName]
- if {"[crange $fileName 0 3]" == "Tcl_"} {
- set fileName [crange $fileName 4 end]}
- set fileName [crange $fileName 0 13]
- return "[file dirname $pathName]/$fileName"
- }
-
- #-----------------------------------------------------------------------------
- # Proc to ensure that all directories for the specified file path exists,
- # and if they don't create them. Don't use -path so we can set the
- # permissions.
-
- proc EnsureDirs {filePath} {
- set dirPath [file dirname $filePath]
- if [file exists $dirPath] return
- foreach dir [split $dirPath /] {
- lappend dirList $dir
- set partPath [join $dirList /]
- if [file exists $partPath] continue
-
- mkdir $partPath
- chmod u=rwx,go=rx $partPath
- }
- }
-
- #-----------------------------------------------------------------------------
- # Proc to set up scan context for use by FilterNroffManPage.
- # This keeps the a two line cache of the previous two lines encountered
- # and the blank lines that followed them.
- #
-
- proc CreateFilterNroffManPageContext {} {
- global filterNroffManPageContext
-
- set filterNroffManPageContext [scancontext create]
-
- # On finding a page header, drop the previous line (which is
- # the page footer). Also deleting the blank lines followin
- # the last line on the previous page.
-
- scanmatch $filterNroffManPageContext {@@@BUILDHELP@@@} {
- catch {unset prev2Blanks}
- catch {unset prev1Line}
- catch {unset prev1Blanks}
- set nukeBlanks {}
- }
-
- # Save blank lines
-
- scanmatch $filterNroffManPageContext {$^} {
- if ![info exists nukeBlanks] {
- append prev1Blanks \n
- }
- }
-
- # Non-blank line, save it. Output the 2nd previous line if necessary.
-
- scanmatch $filterNroffManPageContext {
- catch {unset nukeBlanks}
- if [info exists prev2Line] {
- puts $outFH $prev2Line
- unset prev2Line
- }
- if [info exists prev2Blanks] {
- puts $outFH $prev2Blanks nonewline
- unset prev2Blanks
- }
- if [info exists prev1Line] {
- set prev2Line $prev1Line
- }
- set prev1Line $matchInfo(line)
- if [info exists prev1Blanks] {
- set prev2Blanks $prev1Blanks
- unset prev1Blanks
- }
- }
- }
-
- #-----------------------------------------------------------------------------
- # Proc to filter a formatted manual page, removing the page headers and
- # footers. This relies on each manual page having a .TH macro in the form:
- # .TH @@@BUILDHELP@@@ n
-
- proc FilterNroffManPage {inFH outFH} {
- global filterNroffManPageContext
-
- if ![info exists filterNroffManPageContext] {
- CreateFilterNroffManPageContext
- }
-
- scanfile $filterNroffManPageContext $inFH
-
- if [info exists prev2Line] {
- puts $outFH $prev2Line
- }
- }
-
- #-----------------------------------------------------------------------------
- # Proc to set up scan context for use by ExtractNroffHeader
- #
-
- proc CreateExtractNroffHeaderContext {} {
- global extractNroffHeaderContext
-
- set extractNroffHeaderContext [scancontext create]
-
- scanmatch $extractNroffHeaderContext {'\\"@endheader[ ]*$} {
- break
- }
- scanmatch $extractNroffHeaderContext {'\\"@:} {
- append nroffHeader "[crange $matchInfo(line) 5 end]\n"
- }
- scanmatch $extractNroffHeaderContext {
- append nroffHeader "$matchInfo(line)\n"
- }
- }
-
- #-----------------------------------------------------------------------------
- # Proc to extract nroff text to use as a header to all pass to nroff when
- # processing a help file.
- # manPageFH - The file handle of the manual page.
- #
-
- proc ExtractNroffHeader {manPageFH} {
- global extractNroffHeaderContext nroffHeader
-
- if ![info exists extractNroffHeaderContext] {
- CreateExtractNroffHeaderContext
- }
- scanfile $extractNroffHeaderContext $manPageFH
- }
-
-
- #-----------------------------------------------------------------------------
- # Proc to set up scan context for use by ExtractNroffHelp
- #
-
- proc CreateExtractNroffHelpContext {} {
- global extractNroffHelpContext
-
- set extractNroffHelpContext [scancontext create]
-
- scanmatch $extractNroffHelpContext {^'\\"@endhelp[ ]*$} {
- break
- }
-
- scanmatch $extractNroffHelpContext {^'\\"@brief:} {
- if $foundBrief {
- error {Duplicate "@brief:" entry}
- }
- set foundBrief 1
- puts $briefHelpFH "$helpName\t[csubstr $matchInfo(line) 11 end]"
- continue
- }
-
- scanmatch $extractNroffHelpContext {^'\\"@:} {
- puts $nroffFH [csubstr $matchInfo(line) 5 end]
- continue
- }
- scanmatch $extractNroffHelpContext {^'\\"@help:} {
- error {"@help" found within another help section"}
- }
- scanmatch $extractNroffHelpContext {
- puts $nroffFH $matchInfo(line)
- }
- }
-
- #-----------------------------------------------------------------------------
- # Proc to extract a nroff help file when it is located in the text.
- # manPageFH - The file handle of the manual page.
- # manLine - The '\"@help: line starting the data to extract.
- #
-
- proc ExtractNroffHelp {manPageFH manLine} {
- global helpDir nroffHeader briefHelpFH colArgs
- global extractNroffHelpContext
-
- if ![info exists extractNroffHelpContext] {
- CreateExtractNroffHelpContext
- }
-
- set helpName [string trim [csubstr $manLine 9 end]]
- set helpFile [TruncFileName "$helpDir/$helpName"]
- if [file exists $helpFile] {
- error "Help file already exists: $helpFile"
- }
- EnsureDirs $helpFile
-
- set tmpFile "[file dirname $helpFile]/tmp.[id process]"
-
- echo " creating help file $helpName"
-
- set nroffFH [open "| nroff -man | col $colArgs > $tmpFile" w]
-
- puts $nroffFH {.TH @@@BUILDHELP@@@ 1}
-
- set foundBrief 0
- scanfile $extractNroffHelpContext $manPageFH
-
- # Close returns an error on if anything comes back on stderr, even if
- # its a warning. Output errors and continue.
-
- set stat [catch {
- close $nroffFH
- } msg]
- if $stat {
- puts stderr "nroff: $msg"
- }
-
- set tmpFH [open $tmpFile r]
- set helpFH [open $helpFile w]
-
- FilterNroffManPage $tmpFH $helpFH
-
- close $tmpFH
- close $helpFH
-
- unlink $tmpFile
- chmod a-w,a+r $helpFile
- }
-
- #-----------------------------------------------------------------------------
- # Proc to set up scan context for use by ExtractScriptHelp
- #
-
- proc CreateExtractScriptHelpContext {} {
- global extractScriptHelpContext
-
- set extractScriptHelpContext [scancontext create]
-
- scanmatch $extractScriptHelpContext {^#@endhelp[ ]*$} {
- break
- }
-
- scanmatch $extractScriptHelpContext {^#@brief:} {
- if $foundBrief {
- error {Duplicate "@brief" entry}
- }
- set foundBrief 1
- puts $briefHelpFH "$helpName\t[csubstr $matchInfo(line) 9 end]"
- continue
- }
-
- scanmatch $extractScriptHelpContext {^#@help:} {
- error {"@help" found within another help section"}
- }
- scanmatch $extractScriptHelpContext {
- if {[clength $matchInfo(line)] > 1} {
- puts $helpFH " [csubstr $matchInfo(line) 1 end]"
- } else {
- puts $helpFH $matchInfo(line)
- }
- }
- }
-
- #-----------------------------------------------------------------------------
- # Proc to extract a tcl script help file when it is located in the text.
- # ScriptPageFH - The file handle of the .tcl file.
- # ScriptLine - The #@help: line starting the data to extract.
- #
-
- proc ExtractScriptHelp {ScriptPageFH ScriptLine} {
- global helpDir briefHelpFH
- global extractScriptHelpContext
-
- if ![info exists extractScriptHelpContext] {
- CreateExtractScriptHelpContext
- }
-
- set helpName [string trim [csubstr $ScriptLine 7 end]]
- set helpFile "$helpDir/$helpName"
- if {[file exists $helpFile]} {
- error "Help file already exists: $helpFile"
- }
- EnsureDirs $helpFile
-
- echo " creating help file $helpName"
-
- set helpFH [open $helpFile w]
-
- set foundBrief 0
- scanfile $extractScriptHelpContext $manPageFH
-
- close $helpFH
- chmod a-w,a+r $helpFile
- }
-
- #-----------------------------------------------------------------------------
- # Proc to scan a nroff manual file looking for the start of a help text
- # sections and extracting those sections.
- # pathName - Full path name of file to extract documentation from.
- #
-
- proc ProcessNroffFile {pathName} {
- global nroffScanCT scriptScanCT nroffHeader
-
- set fileName [file tail $pathName]
-
- set nroffHeader {}
- set manPageFH [open $pathName r]
- set matchInfo(fileName) [file tail $pathName]
-
- echo " scanning $pathName"
-
- scanfile $nroffScanCT $manPageFH
-
- close $manPageFH
- }
-
- #-----------------------------------------------------------------------------
- # Proc to scan a Tcl script file looking for the start of a
- # help text sections and extracting those sections.
- # pathName - Full path name of file to extract documentation from.
- #
-
- proc ProcessTclScript {pathName} {
- global scriptScanCT nroffHeader
-
- set scriptFH [open "$pathName" r]
- set matchInfo(fileName) [file tail $pathName]
-
- echo " scanning $pathName"
- scanfile $scriptScanCT $scriptFH
-
- close $scriptFH
- }
-
- #-----------------------------------------------------------------------------
- # build: main procedure. Generates help from specified files.
- # helpDirPath - Directory were the help files go.
- # briefFile - The name of the brief file to create.
- # sourceFiles - List of files to extract help files from.
-
- proc buildhelp {helpDirPath briefFile sourceFiles} {
- global helpDir truncFileNames nroffScanCT
- global scriptScanCT briefHelpFH colArgs
-
- echo ""
- echo "Begin building help tree"
-
- # Determine version of col command to use (no -x on BSD)
- if {[system {col -bx </dev/null >/dev/null 2>&1}] != 0} {
- set colArgs {-b}
- } else {
- set colArgs {-bx}
- }
- set helpDir $helpDirPath
- if {![file exists $helpDir]} {
- mkdir $helpDir
- }
-
- if {![file isdirectory $helpDir]} {
- error [concat "$helpDir is not a directory or does not exist. "
- "This should be the help root directory"]
- }
-
- set status [catch {set tmpFH [open $helpDir/AVeryVeryBigFileName w]}]
- if {$status != 0} {
- set truncFileNames 1
- } else {
- close $tmpFH
- unlink $helpDir/AVeryVeryBigFileName
- set truncFileNames 0
- }
-
- set nroffScanCT [scancontext create]
-
- scanmatch $nroffScanCT {'\\"@help:} {
- ExtractNroffHelp $matchInfo(handle) $matchInfo(line)
- continue
- }
-
- scanmatch $nroffScanCT {^'\\"@header} {
- ExtractNroffHeader $matchInfo(handle)
- continue
- }
- scanmatch $nroffScanCT {^'\\"@endhelp} {
- error [concat {@endhelp" without corresponding "@help:"} \
- ", offset = $matchInfo(offset)"]
- }
- scanmatch $nroffScanCT {^'\\"@brief} {
- error [concat {"@brief" without corresponding "@help:"} \
- ", offset = $matchInfo(offset)"]
- }
-
- set scriptScanCT [scancontext create]
- scanmatch $scriptScanCT {^#@help:} {
- ExtractScriptHelp $matchInfo(handle) $matchInfo(line)
- }
-
- if {[file extension $briefFile] != ".brf"} {
- error "Brief file \"$briefFile\" must have an extension \".brf\""
- }
- if [file exists $helpDir/$briefFile] {
- error "Brief file \"$helpDir/$briefFile\" already exists"
- }
- set briefHelpFH [open "|sort > $helpDir/$briefFile" w]
-
- foreach manFile [glob $sourceFiles] {
- set ext [file extension $manFile]
- if {$ext == ".tcl" || $ext == ".tlib"} {
- set status [catch {ProcessTclScript $manFile} msg]
- } else {
- set status [catch {ProcessNroffFile $manFile} msg]
- }
- if {$status != 0} {
- global errorInfo errorCode
- error "Error extracting help from: $manFile" $errorInfo $errorCode
- }
- }
-
- close $briefHelpFH
- chmod a-w,a+r $helpDir/$briefFile
- echo "Completed extraction of help files"
- }
-
-