|
|
1.1 ! root 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: #------------------------------------------------------------------------------ ! 9: # Copyright 1992 Karl Lehenbauer and Mark Diekhans. ! 10: # ! 11: # Permission to use, copy, modify, and distribute this software and its ! 12: # documentation for any purpose and without fee is hereby granted, provided ! 13: # that the above copyright notice appear in all copies. Karl Lehenbauer and ! 14: # Mark Diekhans make no representations about the suitability of this ! 15: # software for any purpose. It is provided "as is" without express or ! 16: # implied warranty. ! 17: #------------------------------------------------------------------------------ ! 18: # $Id: buildhelp.tcl,v 2.1 1992/10/25 17:07:40 markd Exp $ ! 19: #------------------------------------------------------------------------------ ! 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: # For TCL script files, which are indentified because they end in ".tcl", ! 54: # the text to be extracted is delimited by: ! 55: # ! 56: # #@help: subjectdir/helpfile ! 57: # #@endhelp ! 58: # ! 59: # And brief lines are in the form: ! 60: # ! 61: # #@brief: Short, one line description ! 62: # ! 63: # The only processing done on text extracted from .tcl files it to replace ! 64: # the # in column one with a space. ! 65: # ! 66: # ! 67: #----------------------------------------------------------------------------- ! 68: # ! 69: # To run this program: ! 70: # ! 71: # tcl buildhelp.tcl [-m mergeTree] -b brief.brf helpDir file-1 file-2 ... ! 72: # ! 73: # o -m mergeTree is a tree of help code, plus a brief file to merge with the ! 74: # help files that are to be extracted. This will become part of the new ! 75: # help tree. Used to merge in the documentation from UCB Tcl. ! 76: # o -b specified the name of the brief file to create form the @brief entries. ! 77: # It must have an extension of ".brf". ! 78: # o helpDir is the help tree root directory. helpDir should exists, but any ! 79: # subdirectories that don't exists will be created. helpDir should be ! 80: # cleaned up before the start of manual page generation, as this program ! 81: # will not overwrite existing files. ! 82: # o file-n are the nroff manual pages (.man) or .tcl or .tlib files to extract ! 83: # the help files from. ! 84: # ! 85: #----------------------------------------------------------------------------- ! 86: ! 87: #----------------------------------------------------------------------------- ! 88: # Truncate a file name of a help file if the system does not support long ! 89: # file names. If the name starts with `Tcl_', then this prefix is removed. ! 90: # If the name is then over 14 characters, it is truncated to 14 charactes ! 91: # ! 92: proc TruncFileName {pathName} { ! 93: global G_truncFileNames ! 94: ! 95: if {!$G_truncFileNames} { ! 96: return $pathName} ! 97: set fileName [file tail $pathName] ! 98: if {"[crange $fileName 0 3]" == "Tcl_"} { ! 99: set fileName [crange $fileName 4 end]} ! 100: set fileName [crange $fileName 0 13] ! 101: return "[file dirname $pathName]/$fileName" ! 102: } ! 103: ! 104: #----------------------------------------------------------------------------- ! 105: # Proc to ensure that all directories for the specified file path exists, ! 106: # and if they don't create them. Don't use -path so we can set the ! 107: # permissions. ! 108: ! 109: proc EnsureDirs {filePath} { ! 110: set dirPath [file dirname $filePath] ! 111: if [file exists $dirPath] return ! 112: foreach dir [split $dirPath /] { ! 113: lappend dirList $dir ! 114: set partPath [join $dirList /] ! 115: if [file exists $partPath] continue ! 116: ! 117: mkdir $partPath ! 118: chmod u=rwx,go=rx $partPath ! 119: } ! 120: } ! 121: ! 122: ! 123: #----------------------------------------------------------------------------- ! 124: # ! 125: # Proc to extract nroff text to use as a header to all pass to nroff when ! 126: # processing a help file. ! 127: # manPageFH - The file handle of the manual page. ! 128: # ! 129: ! 130: proc ExtractNroffHeader {manPageFH} { ! 131: global nroffHeader ! 132: while {[gets $manPageFH manLine] >= 0} { ! 133: if {[string first "'@endheader" $manLine] == 0} { ! 134: break; ! 135: } ! 136: if {[string first "'@:" $manLine] == 0} { ! 137: set manLine [csubstr manLine 3 end] ! 138: } ! 139: append nroffHeader "$manLine\n" ! 140: } ! 141: } ! 142: ! 143: #----------------------------------------------------------------------------- ! 144: # ! 145: # Proc to extract a nroff help file when it is located in the text. ! 146: # manPageFH - The file handle of the manual page. ! 147: # manLine - The '@help: line starting the data to extract. ! 148: # ! 149: ! 150: proc ExtractNroffHelp {manPageFH manLine} { ! 151: global G_helpDir nroffHeader G_briefHelpFH G_colArgs ! 152: ! 153: set helpName [string trim [csubstr $manLine 7 end]] ! 154: set helpFile [TruncFileName "$G_helpDir/$helpName"] ! 155: if {[file exists $helpFile]} { ! 156: error "Help file already exists: $helpFile"} ! 157: EnsureDirs $helpFile ! 158: set helpFH [open "| nroff -man | col $G_colArgs > $helpFile" w] ! 159: echo " creating help file $helpName" ! 160: ! 161: # Nroff commands from .TH macro to get the formatting right. The `\n' ! 162: # are newline separators to output, the `\\n' become `\n' in the text. ! 163: ! 164: puts $helpFH ".ad b\n.PD\n.nrIN \\n()Mu\n.nr)R 0\n.nr)I \\n()Mu" ! 165: puts $helpFH ".nr)R 0\n.\}E\n.DT\n.na\n.nh" ! 166: puts $helpFH $nroffHeader ! 167: set foundBrief 0 ! 168: while {[gets $manPageFH manLine] >= 0} { ! 169: if {[string first "'@endhelp" $manLine] == 0} { ! 170: break; ! 171: } ! 172: if {[string first "'@brief:" $manLine] == 0} { ! 173: if $foundBrief { ! 174: error {Duplicate "'@brief" entry"} ! 175: } ! 176: set foundBrief 1 ! 177: puts $G_briefHelpFH "$helpName\t[csubstr $manLine 8 end]" ! 178: continue; ! 179: } ! 180: if {[string first "'@:" $manLine] == 0} { ! 181: set manLine [csubstr $manLine 3 end] ! 182: } ! 183: if {[string first "'@help" $manLine] == 0} { ! 184: error {"'@help" found within another help section"} ! 185: } ! 186: puts $helpFH $manLine ! 187: } ! 188: close $helpFH ! 189: chmod a-w,a+r $helpFile ! 190: } ! 191: ! 192: #----------------------------------------------------------------------------- ! 193: # ! 194: # Proc to extract a tcl script help file when it is located in the text. ! 195: # ScriptPageFH - The file handle of the .tcl file. ! 196: # ScriptLine - The #@help: line starting the data to extract. ! 197: # ! 198: ! 199: proc ExtractScriptHelp {ScriptPageFH ScriptLine} { ! 200: global G_helpDir G_briefHelpFH ! 201: set helpName [string trim [csubstr $ScriptLine 7 end]] ! 202: set helpFile "$G_helpDir/$helpName" ! 203: if {[file exists $helpFile]} { ! 204: error "Help file already exists: $helpFile"} ! 205: EnsureDirs $helpFile ! 206: set helpFH [open $helpFile w] ! 207: echo " creating help file $helpName" ! 208: set foundBrief 0 ! 209: while {[gets $ScriptPageFH ScriptLine] >= 0} { ! 210: if {[string first "#@endhelp" $ScriptLine] == 0} { ! 211: break; ! 212: } ! 213: if {[string first "#@brief:" $ScriptLine] == 0} { ! 214: if $foundBrief { ! 215: error {Duplicate "#@brief" entry"} ! 216: } ! 217: set foundBrief 1 ! 218: puts $G_briefHelpFH "$helpName\t[csubstr $ScriptLine 8 end]" ! 219: continue; ! 220: } ! 221: if {[string first "#@help" $ScriptLine] == 0} { ! 222: error {"#@help" found within another help section"} ! 223: } ! 224: if {[clength $ScriptLine] > 1} { ! 225: set ScriptLine " [csubstr $ScriptLine 1 end]" ! 226: } else { ! 227: set ScriptLine "" ! 228: } ! 229: puts $helpFH $ScriptLine ! 230: } ! 231: close $helpFH ! 232: chmod a-w,a+r $helpFile ! 233: } ! 234: ! 235: #----------------------------------------------------------------------------- ! 236: # ! 237: # Proc to scan a nroff manual file looking for the start of a help text ! 238: # sections and extracting those sections. ! 239: # pathName - Full path name of file to extract documentation from. ! 240: # ! 241: ! 242: proc ProcessNroffFile {pathName} { ! 243: global G_nroffScanCT G_scriptScanCT nroffHeader ! 244: ! 245: set fileName [file tail $pathName] ! 246: ! 247: set nroffHeader {} ! 248: set manPageFH [open $pathName r] ! 249: echo " scanning $pathName" ! 250: set matchInfo(fileName) [file tail $pathName] ! 251: scanfile $G_nroffScanCT $manPageFH ! 252: close $manPageFH ! 253: } ! 254: ! 255: #----------------------------------------------------------------------------- ! 256: # ! 257: # Proc to scan a Tcl script file looking for the start of a ! 258: # help text sections and extracting those sections. ! 259: # pathName - Full path name of file to extract documentation from. ! 260: # ! 261: ! 262: proc ProcessTclScript {pathName} { ! 263: global G_scriptScanCT nroffHeader ! 264: ! 265: set scriptFH [open "$pathName" r] ! 266: ! 267: echo " scanning $pathName" ! 268: set matchInfo(fileName) [file tail $pathName] ! 269: scanfile $G_scriptScanCT $scriptFH ! 270: close $scriptFH ! 271: } ! 272: ! 273: #----------------------------------------------------------------------------- ! 274: # Proc to copy the help merge tree, excluding the brief file and RCS files ! 275: # ! 276: ! 277: proc CopyMergeTree {helpDirPath mergeTree} { ! 278: if {"[cindex $helpDirPath 0]" != "/"} { ! 279: set helpDirPath "[pwd]/$helpDirPath" ! 280: } ! 281: set oldDir [pwd] ! 282: cd $mergeTree ! 283: ! 284: set curHelpDir "." ! 285: ! 286: for_recursive_glob mergeFile {.} { ! 287: if [string match "*/RCS/*" $mergeFile] continue ! 288: ! 289: set helpFile "$helpDirPath/$mergeFile" ! 290: if [file isdirectory $mergeFile] continue ! 291: ! 292: if {[file exists $helpFile]} { ! 293: error "Help file already exists: $helpFile"} ! 294: EnsureDirs $helpFile ! 295: set inFH [open $mergeFile r] ! 296: set outFH [open $helpFile w] ! 297: copyfile $inFH $outFH ! 298: close $outFH ! 299: close $inFH ! 300: chmod a-w,a+r $helpFile ! 301: } ! 302: cd $oldDir ! 303: } ! 304: ! 305: #----------------------------------------------------------------------------- ! 306: # GenerateHelp: main procedure. Generates help from specified files. ! 307: # helpDirPath - Directory were the help files go. ! 308: # mergeTree - Help file tree to merge with the extracted help files. ! 309: # briefFile - The name of the brief file to create. ! 310: # sourceFiles - List of files to extract help files from. ! 311: ! 312: proc GenerateHelp {helpDirPath briefFile mergeTree sourceFiles} { ! 313: global G_helpDir G_truncFileNames G_nroffScanCT ! 314: global G_scriptScanCT G_briefHelpFH G_colArgs ! 315: ! 316: echo "" ! 317: echo "Begin building help tree" ! 318: ! 319: # Determine version of col command to use (no -x on BSD) ! 320: if {[system {col -bx </dev/null >/dev/null 2>&1}] != 0} { ! 321: set G_colArgs {-b} ! 322: } else { ! 323: set G_colArgs {-bx} ! 324: } ! 325: set G_helpDir [glob $helpDirPath] ! 326: ! 327: if {![file isdirectory $G_helpDir]} { ! 328: error [concat "$G_helpDir is not a directory or does not exist. " ! 329: "This should be the help root directory"] ! 330: } ! 331: ! 332: set status [catch {set tmpFH [open $G_helpDir/AVeryVeryBigFileName w]}] ! 333: if {$status != 0} { ! 334: set G_truncFileNames 1 ! 335: } else { ! 336: close $tmpFH ! 337: unlink $G_helpDir/AVeryVeryBigFileName ! 338: set G_truncFileNames 0 ! 339: } ! 340: ! 341: set G_nroffScanCT [scancontext create] ! 342: ! 343: scanmatch $G_nroffScanCT "^'@help:" { ! 344: ExtractNroffHelp $matchInfo(handle) $matchInfo(line) ! 345: continue ! 346: } ! 347: ! 348: scanmatch $G_nroffScanCT "^'@header" { ! 349: ExtractNroffHeader $matchInfo(handle) ! 350: continue ! 351: } ! 352: scanmatch $G_nroffScanCT "^'@endhelp" { ! 353: error [concat {"'@endhelp" without corresponding "'@help:"} \ ! 354: ", offset = $matchInfo(offset)"] ! 355: } ! 356: scanmatch $G_nroffScanCT "^'@brief" { ! 357: error [concat {"'@brief" without corresponding "'@help:"} ! 358: ", offset = $matchInfo(offset)"] ! 359: } ! 360: ! 361: set G_scriptScanCT [scancontext create] ! 362: scanmatch $G_scriptScanCT "^#@help:" { ! 363: ExtractScriptHelp $matchInfo(handle) $matchInfo(line) ! 364: } ! 365: ! 366: if ![lempty $mergeTree] { ! 367: echo " Merging tree: $mergeTree" ! 368: CopyMergeTree $helpDirPath $mergeTree ! 369: } ! 370: ! 371: ! 372: if {[file extension $briefFile] != ".brf"} { ! 373: puts stderr "Brief file \"$briefFile\" must have an extension \".brf\"" ! 374: exit 1 ! 375: } ! 376: if [file exists $G_helpDir/$briefFile] { ! 377: puts stderr "Brief file \"$G_helpDir/$briefFile\" already exists" ! 378: exit 1 ! 379: } ! 380: set G_briefHelpFH [open "|sort > $G_helpDir/$briefFile" w] ! 381: ! 382: foreach manFile $sourceFiles { ! 383: set manFile [glob $manFile] ! 384: set ext [file extension $manFile] ! 385: if {"$ext" == ".man"} { ! 386: set status [catch {ProcessNroffFile $manFile} msg] ! 387: } else { ! 388: set status [catch {ProcessTclScript $manFile} msg] ! 389: } ! 390: if {$status != 0} { ! 391: echo "Error extracting help from: $manFile" ! 392: echo $msg ! 393: global errorInfo interactiveSession ! 394: if {!$interactiveSession} { ! 395: echo $errorInfo ! 396: exit 1 ! 397: } ! 398: } ! 399: } ! 400: ! 401: close $G_briefHelpFH ! 402: chmod a-w,a+r $G_helpDir/$briefFile ! 403: echo "*** completed extraction of all help files" ! 404: } ! 405: ! 406: #----------------------------------------------------------------------------- ! 407: # Print a usage message and exit the program ! 408: proc Usage {} { ! 409: puts stderr {Wrong args: [-m mergetree] -b briefFile helpdir manfile1 [manfile2..]} ! 410: exit 1 ! 411: } ! 412: ! 413: #----------------------------------------------------------------------------- ! 414: # Main program body, decides if help is interactive or batch. ! 415: ! 416: if {$interactiveSession} { ! 417: echo "To extract help, use the command:" ! 418: echo {GenerateHelp helpdir -m mergetree file-1 file-2 ...} ! 419: } else { ! 420: set mergeTree {} ! 421: set briefFile {} ! 422: while {[string match "-*" [lindex $argv 0]]} { ! 423: set flag [lvarpop argv 0] ! 424: case $flag in { ! 425: "-m" {set mergeTree [lvarpop argv]} ! 426: "-b" {set briefFile [lvarpop argv]} ! 427: default Usage ! 428: } ! 429: } ! 430: if {[llength $argv] < 2} { ! 431: Usage ! 432: } ! 433: if [lempty $briefFile] { ! 434: puts stderr {must specify -b argument} ! 435: Usage ! 436: } ! 437: GenerateHelp [lindex $argv 0] $briefFile $mergeTree [lrange $argv 1 end] ! 438: ! 439: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.