home *** CD-ROM | disk | FTP | other *** search
/ Complete Linux / Complete Linux.iso / docs / devel / tcl / tclx7_31.z / tclx7_31 / tcldev / tclX7.3a-p1 / tclsrc / buildhelp.tcl < prev    next >
Encoding:
Text File  |  1993-11-19  |  15.5 KB  |  513 lines

  1. #
  2. # buildhelp.tcl --
  3. #
  4. # Program to extract help files from TCL manual pages or TCL script files.
  5. # The help directories are built as a hierarchical tree of subjects and help
  6. # files.  
  7. #------------------------------------------------------------------------------
  8. # Copyright 1992-1993 Karl Lehenbauer and Mark Diekhans.
  9. #
  10. # Permission to use, copy, modify, and distribute this software and its
  11. # documentation for any purpose and without fee is hereby granted, provided
  12. # that the above copyright notice appear in all copies.  Karl Lehenbauer and
  13. # Mark Diekhans make no representations about the suitability of this
  14. # software for any purpose.  It is provided "as is" without express or
  15. # implied warranty.
  16. #------------------------------------------------------------------------------
  17. # $Id: buildhelp.tcl,v 3.0 1993/11/19 07:00:46 markd Rel $
  18. #------------------------------------------------------------------------------
  19. #
  20. # For nroff man pages, the areas of text to extract are delimited with:
  21. #
  22. #     '\"@help: subjectdir/helpfile
  23. #     '\"@endhelp
  24. #
  25. # start in column one. The text between these markers is extracted and stored
  26. # in help/subjectdir/help.  The file must not exists, this is done to enforced 
  27. # cleaning out the directories before help file generation is started, thus
  28. # removing any stale files.  The extracted text is run through:
  29. #
  30. #     nroff -man|col -xb   {col -b on BSD derived systems}
  31. #
  32. # If there is other text to include in the helpfile, but not in the manual 
  33. # page, the text, along with nroff formatting commands, may be included using:
  34. #
  35. #     '\"@:Other text to include in the help page.
  36. #
  37. # A entry in the brief file, used by apropos my be included by:
  38. #
  39. #     '\"@brief: Short, one line description
  40. #
  41. # These brief request must occur with in the bounds of a help section.
  42. #
  43. # If some header text, such as nroff macros, need to be preappended to the
  44. # text streem before it is run through nroff, then that text can be bracketed
  45. # with:
  46. #
  47. #     '\"@header
  48. #     '\"@endheader
  49. #
  50. # If multiple header blocks are encountered, they will all be preappended.
  51. #
  52. # For TCL script files, which are indentified because they end in ".tcl",
  53. # the text to be extracted is delimited by:
  54. #
  55. #    #@help: subjectdir/helpfile
  56. #    #@endhelp
  57. #
  58. # And brief lines are in the form:
  59. #
  60. #     #@brief: Short, one line description
  61. #
  62. # The only processing done on text extracted from .tcl files it to replace
  63. # the # in column one with a space.
  64. #
  65. #
  66. #-----------------------------------------------------------------------------
  67. # To generate help:
  68. #
  69. #   buildhelp helpDir brief.brf filelist
  70. #
  71. # o helpDir is the help tree root directory.  helpDir should  exists, but any
  72. #   subdirectories that don't exists will be created.  helpDir should be
  73. #   cleaned up before the start of manual page generation, as this program
  74. #   will not overwrite existing files.
  75. # o brief.brf  is the name of the brief file to create form the @brief entries.
  76. #   It must have an extension of ".brf".  It will be created in helpDir.
  77. # o filelist are the nroff manual pages, or .tcl, .tlib files to extract
  78. #   the help files from. If the suffix is not .tcl or .tlib, a nroff manual
  79. #   page is assumed.
  80. #
  81. #-----------------------------------------------------------------------------
  82.  
  83. #@package: TclX-buildhelp buildhelp
  84.  
  85. #-----------------------------------------------------------------------------
  86. # Truncate a file name of a help file if the system does not support long
  87. # file names.  If the name starts with `Tcl_', then this prefix is removed.
  88. # If the name is then over 14 characters, it is truncated to 14 charactes
  89. #  
  90. proc TruncFileName {pathName} {
  91.     global truncFileNames
  92.  
  93.     if {!$truncFileNames} {
  94.         return $pathName}
  95.     set fileName [file tail $pathName]
  96.     if {"[crange $fileName 0 3]" == "Tcl_"} {
  97.         set fileName [crange $fileName 4 end]}
  98.     set fileName [crange $fileName 0 13]
  99.     return "[file dirname $pathName]/$fileName"
  100. }
  101.  
  102. #-----------------------------------------------------------------------------
  103. # Proc to ensure that all directories for the specified file path exists,
  104. # and if they don't create them.  Don't use -path so we can set the
  105. # permissions.
  106.  
  107. proc EnsureDirs {filePath} {
  108.     set dirPath [file dirname $filePath]
  109.     if [file exists $dirPath] return
  110.     foreach dir [split $dirPath /] {
  111.         lappend dirList $dir
  112.         set partPath [join $dirList /]
  113.         if [file exists $partPath] continue
  114.  
  115.         mkdir $partPath
  116.         chmod u=rwx,go=rx $partPath
  117.     }
  118. }
  119.  
  120. #-----------------------------------------------------------------------------
  121. # Proc to set up scan context for use by FilterNroffManPage.
  122. # This keeps the a two line cache of the previous two lines encountered
  123. # and the blank lines that followed them.
  124. #
  125.  
  126. proc CreateFilterNroffManPageContext {} {
  127.     global filterNroffManPageContext
  128.  
  129.     set filterNroffManPageContext [scancontext create]
  130.  
  131.     # On finding a page header, drop the previous line (which is
  132.     # the page footer). Also deleting the blank lines followin
  133.     # the last line on the previous page.
  134.  
  135.     scanmatch $filterNroffManPageContext {@@@BUILDHELP@@@} {
  136.         catch {unset prev2Blanks}
  137.         catch {unset prev1Line}
  138.         catch {unset prev1Blanks}
  139.         set nukeBlanks {}
  140.     }
  141.  
  142.     # Save blank lines
  143.  
  144.     scanmatch $filterNroffManPageContext {$^} {
  145.         if ![info exists nukeBlanks] {
  146.             append prev1Blanks \n
  147.         }
  148.     }
  149.  
  150.     # Non-blank line, save it.  Output the 2nd previous line if necessary.
  151.  
  152.     scanmatch $filterNroffManPageContext {
  153.         catch {unset nukeBlanks}
  154.         if [info exists prev2Line] {
  155.             puts $outFH $prev2Line
  156.             unset prev2Line
  157.         }
  158.         if [info exists prev2Blanks] {
  159.             puts $outFH $prev2Blanks nonewline
  160.             unset prev2Blanks
  161.         }
  162.         if [info exists prev1Line] {
  163.             set prev2Line $prev1Line
  164.         }
  165.         set prev1Line $matchInfo(line)
  166.         if [info exists prev1Blanks] {
  167.             set prev2Blanks $prev1Blanks
  168.             unset prev1Blanks
  169.         }
  170.     }
  171. }
  172.  
  173. #-----------------------------------------------------------------------------
  174. # Proc to filter a formatted manual page, removing the page headers and
  175. # footers.  This relies on each manual page having a .TH macro in the form:
  176. #   .TH @@@BUILDHELP@@@ n
  177.  
  178. proc FilterNroffManPage {inFH outFH} {
  179.     global filterNroffManPageContext
  180.  
  181.     if ![info exists filterNroffManPageContext] {
  182.         CreateFilterNroffManPageContext
  183.     }
  184.  
  185.     scanfile $filterNroffManPageContext $inFH
  186.  
  187.     if [info exists prev2Line] {
  188.         puts $outFH $prev2Line
  189.     }
  190. }
  191.  
  192. #-----------------------------------------------------------------------------
  193. # Proc to set up scan context for use by ExtractNroffHeader
  194. #
  195.  
  196. proc CreateExtractNroffHeaderContext {} {
  197.     global extractNroffHeaderContext
  198.  
  199.     set extractNroffHeaderContext [scancontext create]
  200.  
  201.     scanmatch $extractNroffHeaderContext {'\\"@endheader[     ]*$} {
  202.         break
  203.     }
  204.     scanmatch $extractNroffHeaderContext {'\\"@:} {
  205.         append nroffHeader "[crange $matchInfo(line) 5 end]\n"
  206.     }
  207.     scanmatch $extractNroffHeaderContext {
  208.         append nroffHeader "$matchInfo(line)\n"
  209.     }
  210. }
  211.  
  212. #-----------------------------------------------------------------------------
  213. # Proc to extract nroff text to use as a header to all pass to nroff when
  214. # processing a help file.
  215. #    manPageFH - The file handle of the manual page.
  216. #
  217.  
  218. proc ExtractNroffHeader {manPageFH} {
  219.     global extractNroffHeaderContext nroffHeader
  220.  
  221.     if ![info exists extractNroffHeaderContext] {
  222.         CreateExtractNroffHeaderContext
  223.     }
  224.     scanfile $extractNroffHeaderContext $manPageFH
  225. }
  226.  
  227.  
  228. #-----------------------------------------------------------------------------
  229. # Proc to set up scan context for use by ExtractNroffHelp
  230. #
  231.  
  232. proc CreateExtractNroffHelpContext {} {
  233.     global extractNroffHelpContext
  234.  
  235.     set extractNroffHelpContext [scancontext create]
  236.  
  237.     scanmatch $extractNroffHelpContext {^'\\"@endhelp[     ]*$} {
  238.         break
  239.     }
  240.  
  241.     scanmatch $extractNroffHelpContext {^'\\"@brief:} {
  242.         if $foundBrief {
  243.             error {Duplicate "@brief:" entry}
  244.         }
  245.         set foundBrief 1
  246.         puts $briefHelpFH "$helpName\t[csubstr $matchInfo(line) 11 end]"
  247.         continue
  248.     }
  249.  
  250.     scanmatch $extractNroffHelpContext {^'\\"@:} {
  251.         puts $nroffFH  [csubstr $matchInfo(line) 5 end]
  252.         continue
  253.     }
  254.     scanmatch $extractNroffHelpContext {^'\\"@help:} {
  255.         error {"@help" found within another help section"}
  256.     }
  257.     scanmatch $extractNroffHelpContext {
  258.         puts $nroffFH $matchInfo(line)
  259.     }
  260. }
  261.  
  262. #-----------------------------------------------------------------------------
  263. # Proc to extract a nroff help file when it is located in the text.
  264. #    manPageFH - The file handle of the manual page.
  265. #    manLine - The '\"@help: line starting the data to extract.
  266. #
  267.  
  268. proc ExtractNroffHelp {manPageFH manLine} {
  269.     global helpDir nroffHeader briefHelpFH colArgs
  270.     global extractNroffHelpContext
  271.  
  272.     if ![info exists extractNroffHelpContext] {
  273.         CreateExtractNroffHelpContext
  274.     }
  275.  
  276.     set helpName [string trim [csubstr $manLine 9 end]]
  277.     set helpFile [TruncFileName "$helpDir/$helpName"]
  278.     if [file exists $helpFile] {
  279.         error "Help file already exists: $helpFile"
  280.     }
  281.     EnsureDirs $helpFile
  282.  
  283.     set tmpFile "[file dirname $helpFile]/tmp.[id process]"
  284.  
  285.     echo "    creating help file $helpName"
  286.  
  287.     set nroffFH [open "| nroff -man | col $colArgs > $tmpFile" w]
  288.  
  289.     puts $nroffFH {.TH @@@BUILDHELP@@@ 1}
  290.  
  291.     set foundBrief 0
  292.     scanfile $extractNroffHelpContext $manPageFH
  293.  
  294.     # Close returns an error on if anything comes back on stderr, even if
  295.     # its a warning.  Output errors and continue.
  296.  
  297.     set stat [catch {
  298.         close $nroffFH
  299.     } msg]
  300.     if $stat {
  301.         puts stderr "nroff: $msg"
  302.     }
  303.  
  304.     set tmpFH [open $tmpFile r]
  305.     set helpFH [open $helpFile w]
  306.  
  307.     FilterNroffManPage $tmpFH $helpFH
  308.  
  309.     close $tmpFH
  310.     close $helpFH
  311.  
  312.     unlink $tmpFile
  313.     chmod a-w,a+r $helpFile
  314. }
  315.  
  316. #-----------------------------------------------------------------------------
  317. # Proc to set up scan context for use by ExtractScriptHelp
  318. #
  319.  
  320. proc CreateExtractScriptHelpContext {} {
  321.     global extractScriptHelpContext
  322.  
  323.     set extractScriptHelpContext [scancontext create]
  324.  
  325.     scanmatch $extractScriptHelpContext {^#@endhelp[     ]*$} {
  326.         break
  327.     }
  328.  
  329.     scanmatch $extractScriptHelpContext {^#@brief:} {
  330.         if $foundBrief {
  331.             error {Duplicate "@brief" entry}
  332.         }
  333.         set foundBrief 1
  334.         puts $briefHelpFH "$helpName\t[csubstr $matchInfo(line) 9 end]"
  335.         continue
  336.     }
  337.  
  338.     scanmatch $extractScriptHelpContext {^#@help:} {
  339.         error {"@help" found within another help section"}
  340.     }
  341.     scanmatch $extractScriptHelpContext {
  342.         if {[clength $matchInfo(line)] > 1} {
  343.             puts $helpFH " [csubstr $matchInfo(line) 1 end]"
  344.         } else {
  345.             puts $helpFH $matchInfo(line)
  346.         }
  347.     }
  348. }
  349.  
  350. #-----------------------------------------------------------------------------
  351. # Proc to extract a tcl script help file when it is located in the text.
  352. #    ScriptPageFH - The file handle of the .tcl file.
  353. #    ScriptLine - The #@help: line starting the data to extract.
  354. #
  355.  
  356. proc ExtractScriptHelp {ScriptPageFH ScriptLine} {
  357.     global helpDir briefHelpFH
  358.     global extractScriptHelpContext
  359.  
  360.     if ![info exists extractScriptHelpContext] {
  361.         CreateExtractScriptHelpContext
  362.     }
  363.  
  364.     set helpName [string trim [csubstr $ScriptLine 7 end]]
  365.     set helpFile "$helpDir/$helpName"
  366.     if {[file exists $helpFile]} {
  367.         error "Help file already exists: $helpFile"
  368.     }
  369.     EnsureDirs $helpFile
  370.  
  371.     echo "    creating help file $helpName"
  372.  
  373.     set helpFH [open $helpFile w]
  374.  
  375.     set foundBrief 0
  376.     scanfile $extractScriptHelpContext $manPageFH
  377.  
  378.     close $helpFH
  379.     chmod a-w,a+r $helpFile
  380. }
  381.  
  382. #-----------------------------------------------------------------------------
  383. # Proc to scan a nroff manual file looking for the start of a help text
  384. # sections and extracting those sections.
  385. #    pathName - Full path name of file to extract documentation from.
  386. #
  387.  
  388. proc ProcessNroffFile {pathName} {
  389.    global nroffScanCT scriptScanCT nroffHeader
  390.  
  391.    set fileName [file tail $pathName]
  392.  
  393.    set nroffHeader {}
  394.    set manPageFH [open $pathName r]
  395.    set matchInfo(fileName) [file tail $pathName]
  396.  
  397.    echo "    scanning $pathName"
  398.  
  399.    scanfile $nroffScanCT $manPageFH
  400.  
  401.    close $manPageFH
  402. }
  403.  
  404. #-----------------------------------------------------------------------------
  405. # Proc to scan a Tcl script file looking for the start of a
  406. # help text sections and extracting those sections.
  407. #    pathName - Full path name of file to extract documentation from.
  408. #
  409.  
  410. proc ProcessTclScript {pathName} {
  411.    global scriptScanCT nroffHeader
  412.  
  413.    set scriptFH [open "$pathName" r]
  414.    set matchInfo(fileName) [file tail $pathName]
  415.  
  416.    echo "    scanning $pathName"
  417.    scanfile $scriptScanCT $scriptFH
  418.  
  419.    close $scriptFH
  420. }
  421.  
  422. #-----------------------------------------------------------------------------
  423. # build: main procedure.  Generates help from specified files.
  424. #    helpDirPath - Directory were the help files go.
  425. #    briefFile - The name of the brief file to create.
  426. #    sourceFiles - List of files to extract help files from.
  427.  
  428. proc buildhelp {helpDirPath briefFile sourceFiles} {
  429.     global helpDir truncFileNames nroffScanCT
  430.     global scriptScanCT briefHelpFH colArgs
  431.  
  432.     echo ""
  433.     echo "Begin building help tree"
  434.  
  435.     # Determine version of col command to use (no -x on BSD)
  436.     if {[system {col -bx </dev/null >/dev/null 2>&1}] != 0} {
  437.         set colArgs {-b}
  438.     } else {
  439.         set colArgs {-bx}
  440.     }
  441.     set helpDir $helpDirPath
  442.     if {![file exists $helpDir]} {
  443.         mkdir $helpDir
  444.     }
  445.  
  446.     if {![file isdirectory $helpDir]} {
  447.         error [concat "$helpDir is not a directory or does not exist. "  
  448.                       "This should be the help root directory"]
  449.     }
  450.         
  451.     set status [catch {set tmpFH [open $helpDir/AVeryVeryBigFileName w]}]
  452.     if {$status != 0} {
  453.         set truncFileNames 1
  454.     } else {
  455.         close $tmpFH
  456.         unlink $helpDir/AVeryVeryBigFileName
  457.         set truncFileNames 0
  458.     }
  459.  
  460.     set nroffScanCT [scancontext create]
  461.  
  462.     scanmatch $nroffScanCT {'\\"@help:} {
  463.         ExtractNroffHelp $matchInfo(handle) $matchInfo(line)
  464.         continue
  465.     }
  466.  
  467.     scanmatch $nroffScanCT {^'\\"@header} {
  468.         ExtractNroffHeader $matchInfo(handle)
  469.         continue
  470.     }
  471.     scanmatch $nroffScanCT {^'\\"@endhelp} {
  472.         error [concat {@endhelp" without corresponding "@help:"} \
  473.                  ", offset = $matchInfo(offset)"]
  474.     }
  475.     scanmatch $nroffScanCT {^'\\"@brief} {
  476.         error [concat {"@brief" without corresponding "@help:"} \
  477.                  ", offset = $matchInfo(offset)"]
  478.     }
  479.  
  480.     set scriptScanCT [scancontext create]
  481.     scanmatch $scriptScanCT {^#@help:} {
  482.         ExtractScriptHelp $matchInfo(handle) $matchInfo(line)
  483.     }
  484.  
  485.     if {[file extension $briefFile] != ".brf"} {
  486.         error "Brief file \"$briefFile\" must have an extension \".brf\""
  487.     }
  488.     if [file exists $helpDir/$briefFile] {
  489.         error "Brief file \"$helpDir/$briefFile\" already exists"
  490.     }
  491.     set briefHelpFH [open "|sort > $helpDir/$briefFile" w]
  492.  
  493.     foreach manFile [glob $sourceFiles] {
  494.         set ext [file extension $manFile]
  495.         if {$ext == ".tcl" || $ext == ".tlib"} {
  496.             set status [catch {ProcessTclScript $manFile} msg]
  497.         } else {
  498.             set status [catch {ProcessNroffFile $manFile} msg]
  499.         }
  500.         if {$status != 0} {
  501.             global errorInfo errorCode
  502.             error "Error extracting help from: $manFile" $errorInfo $errorCode
  503.         }
  504.     }
  505.  
  506.     close $briefHelpFH
  507.     chmod a-w,a+r $helpDir/$briefFile
  508.     echo "Completed extraction of help files"
  509. }
  510.  
  511.