home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 5 / Skunkware 5.iso / tls / tls074c.sunsparc.Z / tls074c.sunsparc / usr / lib / tcl / buildhlp.tcl next >
Encoding:
Text File  |  1995-07-20  |  15.2 KB  |  455 lines

  1. # CVS $Id: buildhlp.tcl,v 1.2 1994/04/28 16:39:09 shonagh Exp $
  2. #    @(#) buildhlp.tcl 25.1 92/07/31 
  3. #
  4. #    Copyright (C) 1992 The Santa Cruz Operation, Inc.
  5. #        All Rights Reserved.
  6. #    The information in this file is provided for the exclusive use of
  7. #    the licensees of The Santa Cruz Operation, Inc.  Such users have the
  8. #    right to use, modify, and incorporate this code into other products
  9. #    for purposes authorized by the license agreement provided they include
  10. #    this notice and the associated copyright notice with any such product.
  11. #    The information in this file is provided "AS IS" without warranty.
  12. #
  13. #-----------------------------------------------------------------------------
  14. #                                buildhelp.tcl
  15. #-----------------------------------------------------------------------------
  16. #
  17. # Program to extract help files from TCL manual pages or TCL script files.
  18. # The help directories are built as a hierarchical tree of subjects and help
  19. # files.  
  20. #
  21. # For nroff man pages, the areas of text to extract are delimited with:
  22. #
  23. #     '@help: subjectdir/helpfile
  24. #     '@endhelp
  25. #
  26. # start in column one. The text between these markers is extracted and stored
  27. # in help/subjectdir/help.  The file must not exists, this is done to enforced 
  28. # cleaning out the directories before help file generation is started, thus
  29. # removing any stale files.  The extracted text is run through:
  30. #
  31. #     nroff -man|col -xb   {col -b on BSD derived systems}
  32. #
  33. # If there is other text to include in the helpfile, but not in the manual 
  34. # page, the text, along with nroff formatting commands, may be included using:
  35. #
  36. #     '@:Other text to include in the help page.
  37. #
  38. # A entry in the brief file, used by apropos my be included by:
  39. #
  40. #     '@brief: Short, one line description
  41. #
  42. # These brief request must occur with in the bounds of a help section.
  43. #
  44. # If some header text, such as nroff macros, need to be preappended to the
  45. # text streem before it is run through nroff, then that text can be bracketed
  46. # with:
  47. #
  48. #     '@header
  49. #     '@endheader
  50. #
  51. # If multiple header blocks are encountered, they will all be preappended.
  52. #
  53. # A similar construct is used for manual page name index generation:
  54. #
  55. #      ;@index: subject1 subjectN
  56. #
  57. # This is used by `installTcl' to generate the name index files.  There should
  58. # be one per file, usuall right before the name line.  The subjects listed are
  59. # all of the procedures or commands to link to the manual page, usually the
  60. # same as on the .SH NAME line.
  61. #
  62. # For TCL script files, which are indentified because they end in ".tcl",
  63. # the text to be extracted is delimited by:
  64. #
  65. #    #@help: subjectdir/helpfile
  66. #    #@endhelp
  67. #
  68. # And brief lines are in the form:
  69. #
  70. #     #@brief: Short, one line description
  71. #
  72. # The only processing done on text extracted from .tcl files it to replace
  73. # the # in column one with a space.
  74. #
  75. #
  76. #-----------------------------------------------------------------------------
  77. # To run this program:
  78. #
  79. #   tcl buildhelp.tcl [-m mergeTree] [-i nameindex] helpDir file-1 file-2 ...
  80. #
  81. # o -m mergeTree is a tree of help code, plus a brief file to merge with the
  82. #   help files that are to be extracted.  This will become part of the new
  83. #   help tree.  Used to merge in the documentation from UCB Tcl.
  84. # o -i nameindex is an name index file to create from the '@index markers in
  85. #   the man files.
  86. # o helpDir is the help tree root directory.  helpDir should  exists, but any
  87. #   subdirectories that don't exists will be created.  helpDir should be
  88. #   cleaned up before the start of manual page generation, as this program
  89. #   will not overwrite existing files.
  90. # o file-n are the nroff manual pages (.man) or .tcl or .tlib files to extract
  91. #   the help files from.
  92. #-----------------------------------------------------------------------------
  93.  
  94. #-----------------------------------------------------------------------------
  95. # Truncate a file name of a help file if the system does not support long
  96. # file names.  If the name starts with `Tcl_', then this prefix is removed.
  97. # If the name is then over 14 characters, it is truncated to 14 charactes
  98. #  
  99. proc TruncFileName {pathName} {
  100.     global G_truncFileNames
  101.  
  102.     if {!$G_truncFileNames} {
  103.         return $pathName}
  104.     set fileName [file tail $pathName]
  105.     if {"[crange $fileName 0 3]" == "Tcl_"} {
  106.         set fileName [crange $fileName 4 end]}
  107.     set fileName [crange $fileName 0 13]
  108.     return "[file dirname $pathName]/$fileName"
  109. }
  110.  
  111. #-----------------------------------------------------------------------------
  112. # Proc to ensure that all directories for the specified file path exists,
  113. # and if they don't create them.
  114.  
  115. proc EnsureDirs {filePath} {
  116.     set dirPath [file dirname $filePath]
  117.     if {![file exists $dirPath]} {
  118.         mkdir -path $dirPath}
  119. }
  120.  
  121.  
  122. #-----------------------------------------------------------------------------
  123. #
  124. # Proc to extract nroff text to use as a header to all pass to nroff when
  125. # processing a help file.
  126. #    manPageFH - The file handle of the manual page.
  127. #
  128.  
  129. proc ExtractNroffHeader {manPageFH} {
  130.     global nroffHeader
  131.     while {[gets $manPageFH manLine] >= 0} {
  132.         if {[string first "'@endheader" $manLine] == 0} {
  133.             break;
  134.             }
  135.         if {[string first "'@:" $manLine] == 0} {
  136.             set manLine [csubstr manLine 3 end]
  137.             }
  138.         append nroffHeader "$manLine\n"
  139.         }
  140. }
  141.  
  142. #-----------------------------------------------------------------------------
  143. #
  144. # Proc to extract a nroff help file when it is located in the text.
  145. #    manPageFH - The file handle of the manual page.
  146. #    manLine - The '@help: line starting the data to extract.
  147. #
  148.  
  149. proc ExtractNroffHelp {manPageFH manLine} {
  150.     global G_helpDir nroffHeader G_briefHelpFH G_colArgs
  151.  
  152.     set helpName [string trim [csubstr $manLine 7 end]]
  153.     set helpFile [TruncFileName "$G_helpDir/$helpName"]
  154.     if {[file exists $helpFile]} {
  155.         error "Help file already exists: $helpFile"}
  156.     EnsureDirs $helpFile
  157.     set helpFH [open "| nroff -man | col $G_colArgs > $helpFile" w]
  158.     echo "    creating help file $helpName"
  159.  
  160.     # Nroff commands from .TH macro to get the formatting right.  The `\n'
  161.     # are newline separators to output, the `\\n' become `\n' in the text.
  162.         
  163.     puts $helpFH ".ad b\n.PD\n.nrIN \\n()Mu\n.nr)R 0\n.nr)I \\n()Mu"
  164.     puts $helpFH ".nr)R 0\n.\}E\n.DT\n.na\n.nh"
  165.     puts $helpFH $nroffHeader
  166.     set foundBrief 0
  167.     while {[gets $manPageFH manLine] >= 0} {
  168.         if {[string first "'@endhelp" $manLine] == 0} {
  169.             break;
  170.         }
  171.         if {[string first "'@brief:" $manLine] == 0} {
  172.             if $foundBrief {
  173.                 error {Duplicate "'@brief" entry"}
  174.             }
  175.             set foundBrief 1
  176.         puts $G_briefHelpFH "$helpName\t[csubstr $manLine 8 end]"
  177.             continue;
  178.         }
  179.         if {[string first "'@:" $manLine] == 0} {
  180.             set manLine [csubstr $manLine 3 end]
  181.         }
  182.         if {[string first "'@help" $manLine] == 0} {
  183.             error {"'@help" found within another help section"}
  184.         }
  185.         puts $helpFH $manLine
  186.         }
  187.     close $helpFH
  188.     chmod a-w,a+r $helpFile
  189. }
  190.  
  191. #-----------------------------------------------------------------------------
  192. #
  193. # Proc to extract a tcl script help file when it is located in the text.
  194. #    ScriptPageFH - The file handle of the .tcl file.
  195. #    ScriptLine - The #@help: line starting the data to extract.
  196. #
  197.  
  198. proc ExtractScriptHelp {ScriptPageFH ScriptLine} {
  199.     global G_helpDir G_briefHelpFH
  200.     set helpName [string trim [csubstr $ScriptLine 7 end]]
  201.     set helpFile "$G_helpDir/$helpName"
  202.     if {[file exists $helpFile]} {
  203.         error "Help file already exists: $helpFile"}
  204.     EnsureDirs $helpFile
  205.     set helpFH [open $helpFile w]
  206.     echo "    creating help file $helpName"
  207.     set foundBrief 0
  208.     while {[gets $ScriptPageFH ScriptLine] >= 0} {
  209.         if {[string first "#@endhelp" $ScriptLine] == 0} {
  210.             break;
  211.         }
  212.         if {[string first "#@brief:" $ScriptLine] == 0} {
  213.             if $foundBrief {
  214.                 error {Duplicate "#@brief" entry"}
  215.             }
  216.             set foundBrief 1
  217.         puts $G_briefHelpFH "$helpName\t[csubstr $ScriptLine 8 end]"
  218.             continue;
  219.         }
  220.         if {[string first "#@help" $ScriptLine] == 0} {
  221.             error {"#@help" found within another help section"}
  222.         }
  223.         if {[clength $ScriptLine] > 1} {
  224.             set ScriptLine " [csubstr $ScriptLine 1 end]"
  225.         } else {
  226.             set ScriptLine ""
  227.         }
  228.         puts $helpFH $ScriptLine
  229.         }
  230.     close $helpFH
  231.     chmod a-w,a+r $helpFile
  232. }
  233.  
  234. #-----------------------------------------------------------------------------
  235. #
  236. # Proc to scan a nroff manual file looking for the start of a help text
  237. # sections and extracting those sections.
  238. #    pathName - Full path name of file to extract documentation from.
  239. #
  240.  
  241. proc ProcessNroffFile {pathName} {
  242.    global G_nroffScanCT G_scriptScanCT nroffHeader
  243.  
  244.    set fileName [file tail $pathName]
  245.  
  246.    set nroffHeader {}
  247.    set manPageFH [open $pathName r]
  248.    echo "    scanning $pathName"
  249.    set matchInfo(fileName) [file tail $pathName]
  250.    scanfile $G_nroffScanCT $manPageFH
  251.    close $manPageFH
  252. }
  253.  
  254. #-----------------------------------------------------------------------------
  255. #
  256. # Proc to scan a Tcl script file looking for the start of a
  257. # help text sections and extracting those sections.
  258. #    pathName - Full path name of file to extract documentation from.
  259. #
  260.  
  261. proc ProcessTclScript {pathName} {
  262.    global G_scriptScanCT nroffHeader
  263.  
  264.    set scriptFH [open "$pathName" r]
  265.  
  266.    echo "    scanning $pathName"
  267.    set matchInfo(fileName) [file tail $pathName]
  268.    scanfile $G_scriptScanCT $scriptFH
  269.    close $scriptFH
  270. }
  271.  
  272. #-----------------------------------------------------------------------------
  273. # Proc to copy the help merge tree, excluding the brief file.
  274.  
  275. proc CopyMergeTree {helpDirPath mergeTree} {
  276.     if {"[cindex $helpDirPath 0]" != "/"} {
  277.         set helpDirPath "[pwd]/$helpDirPath"
  278.     }
  279.     set oldDir [pwd]
  280.     cd $mergeTree
  281.  
  282.     set curHelpDir "."
  283.  
  284.     for_recursive_glob mergeFile {.} {
  285.         if {"$mergeFile" == "./brief"} {
  286.             continue}
  287.             set helpFile "$helpDirPath/$mergeFile"
  288.         if {[file isdirectory $mergeFile]} {
  289.             if ![file exists $helpFile] {
  290.                 mkdir $helpFile
  291.                 chmod go-w,a+rx $helpFile
  292.             }
  293.         } else {
  294.             if {[file exists $helpFile]} {
  295.                 error "Help file already exists: $helpFile"}
  296.             set inFH [open $mergeFile r]
  297.             set outFH [open $helpFile w]
  298.             copyfile $inFH $outFH
  299.             close $outFH
  300.             close $inFH
  301.             chmod a-w,a+r $helpFile
  302.         }
  303.     }
  304.     cd $oldDir
  305. }
  306.  
  307. #-----------------------------------------------------------------------------
  308. # GenerateHelp: main procedure.  Generates help from specified files.
  309. #    helpDirPath - Directory were the help files go.
  310. #    mergeTree - Help file tree to merge with the extracted help files.
  311. #    manIndexFile - Manual page index file to build, or {} to not build one.
  312. #    sourceFiles - List of files to extract help files from.
  313.  
  314. proc GenerateHelp {helpDirPath mergeTree manIndexFile sourceFiles} {
  315.     global G_helpDir G_truncFileNames G_manIndexFH G_nroffScanCT
  316.     global G_scriptScanCT G_briefHelpFH G_colArgs
  317.  
  318.     echo ""
  319.     echo "Begin building help tree"
  320.  
  321.     # Determine version of col command to use (no -x on BSD)
  322.     if {[system {col -bx </dev/null >/dev/null 2>&1}] != 0} {
  323.         set G_colArgs {-b}
  324.     } else {
  325.         set G_colArgs {-bx}
  326.     }
  327.     set G_helpDir [glob $helpDirPath]
  328.  
  329.     if {![file isdirectory $G_helpDir]} {
  330.         error [concat "$G_helpDir is not a directory or does not exist. "  
  331.                       "This should be the help root directory"]
  332.     }
  333.         
  334.     set status [catch {set tmpFH [open xxx $G_helpDir/AVeryVeryBigFileName w]}]
  335.     if {$status != 0} {
  336.         set G_truncFileNames 1
  337.     } else {
  338.         close $tmpFH
  339.         unlink $G_helpDir/AVeryVeryBigFileName
  340.         set G_truncFileNames 0
  341.     }
  342.  
  343.     set G_manIndexFH {}
  344.     if {![lempty $manIndexFile]} {
  345.         set G_manIndexFH [open $manIndexFile w]
  346.     }
  347.  
  348.     set G_nroffScanCT [scancontext create]
  349.  
  350.     scanmatch $G_nroffScanCT "^'@help:" {
  351.         ExtractNroffHelp $matchInfo(handle) $matchInfo(line)
  352.         continue
  353.     }
  354.  
  355.     scanmatch $G_nroffScanCT "^'@header" {
  356.         ExtractNroffHeader $matchInfo(handle)
  357.         continue
  358.     }
  359.     scanmatch $G_nroffScanCT "^'@endhelp" {
  360.         error [concat {"'@endhelp" without corresponding "'@help:"} \
  361.                  ", offset = $matchInfo(offset)"]
  362.     }
  363.     scanmatch $G_nroffScanCT "^'@brief" {
  364.         error [concat {"'@brief" without corresponding "'@help:"}
  365.                  ", offset = $matchInfo(offset)"]
  366.     }
  367.  
  368.     scanmatch $G_nroffScanCT "^'@index:" {
  369.         global G_manIndexFH
  370.         if {[llength $matchInfo(line)] == 1} {
  371.             error "no subjects specified in \"'@index:\" line"}
  372.         if {![lempty $G_manIndexFH]} {
  373.             puts $G_manIndexFH [concat $matchInfo(fileName) \
  374.                                        [list [lrange $matchInfo(line) 1 end]]]
  375.         }
  376.     }
  377.  
  378.     set G_scriptScanCT [scancontext create]
  379.     scanmatch $G_scriptScanCT "^#@help:" {
  380.         ExtractScriptHelp $matchInfo(handle) $matchInfo(line)
  381.     }
  382.  
  383.     if ![lempty $mergeTree] {
  384.         echo "    Merging tree: $mergeTree"
  385.         CopyMergeTree $helpDirPath $mergeTree
  386.     }
  387.  
  388.     set G_briefHelpFH [open "|sort > $G_helpDir/brief" w]
  389.  
  390.     if {(![lempty $mergeTree]) && [file exists $mergeTree/brief]} {
  391.         echo "    Merging file: $mergeTree/brief"
  392.         set mergeBriefFH [open $mergeTree/brief r]
  393.         puts $G_briefHelpFH [read $mergeBriefFH]
  394.         close $mergeBriefFH
  395.     }
  396.     foreach manFile $sourceFiles {
  397.         set manFile [glob $manFile]
  398.         set ext [file extension $manFile]
  399.         if {"$ext" == ".man"} {
  400.             set status [catch {ProcessNroffFile $manFile} msg]
  401.         } else {
  402.             set status [catch {ProcessTclScript $manFile} msg]
  403.         }
  404.         if {$status != 0} {
  405.             echo "Error extracting help from: $manFile"
  406.             echo $msg
  407.             global errorInfo interactiveSession
  408.             if {!$interactiveSession} {
  409.                 echo $errorInfo
  410.                 exit 1
  411.             }
  412.         }
  413.     }
  414.  
  415.     close $G_briefHelpFH
  416.     chmod a-w,a+r $G_helpDir/brief
  417.     if {$G_manIndexFH != ""} {
  418.         close $G_manIndexFH
  419.     }
  420.     echo "*** completed extraction of all help files"
  421. }
  422.  
  423. #-----------------------------------------------------------------------------
  424. # Print a usage message and exit the program
  425. proc Usage {} {
  426.     echo {Wrong args: [-m mergetree] [-i index] helpdir manfile1 [manfile2..]}
  427.     exit 1
  428. }
  429.  
  430. #-----------------------------------------------------------------------------
  431. # Main program body, decides if help is interactive or batch.
  432.  
  433. if {$interactiveSession} {
  434.     echo "To extract help, use the command:"
  435.     echo "  GenerateHelp [-m mergetree] [-i nameindex] sourceFiles helpdir file-1 file-2 ..."
  436. } else {
  437.     set mergeTree {}
  438.     set manIndexFile {}
  439.     while {[string match "-*" [lindex $argv 0]]} {
  440.         set flag [lvarpop argv 0]
  441.         case $flag in {
  442.             "-m" {set mergeTree [lvarpop argv 0]}
  443.             "-i" {set manIndexFile [lvarpop argv 0]}
  444.             default Usage
  445.         }
  446.     }
  447.     if {[llength $argv] < 2} {
  448.         Usage
  449.     }
  450.     GenerateHelp [lindex $argv 0] $mergeTree $manIndexFile [lrange $argv 1 end]
  451.    
  452. }
  453.