home *** CD-ROM | disk | FTP | other *** search
/ PC World 2002 May / PCWorld_2002-05_cd.bin / Software / TemaCD / activetcltk / ActiveTcl8.3.4.1-8.win32-ix86.exe / ActiveTcl8.3.4.1-win32-ix86 / lib / tclX8.3 / tcl.tlib < prev    next >
Encoding:
Text File  |  2001-10-22  |  71.0 KB  |  2,245 lines

  1. #
  2. # arrayprocs.tcl --
  3. #
  4. # Extended Tcl array procedures.
  5. #------------------------------------------------------------------------------
  6. # Copyright 1992-1999 Karl Lehenbauer and Mark Diekhans.
  7. #
  8. # Permission to use, copy, modify, and distribute this software and its
  9. # documentation for any purpose and without fee is hereby granted, provided
  10. # that the above copyright notice appear in all copies.  Karl Lehenbauer and
  11. # Mark Diekhans make no representations about the suitability of this
  12. # software for any purpose.  It is provided "as is" without express or
  13. # implied warranty.
  14. #------------------------------------------------------------------------------
  15. # $Id: arrayprocs.tcl,v 8.3 1999/03/31 06:37:47 markd Exp $
  16. #------------------------------------------------------------------------------
  17. #
  18.  
  19. #@package: TclX-ArrayProcedures for_array_keys
  20.  
  21. proc for_array_keys {varName arrayName codeFragment} {
  22.     upvar $varName enumVar $arrayName enumArray
  23.  
  24.     if ![info exists enumArray] {
  25.     error "\"$arrayName\" isn't an array"
  26.     }
  27.  
  28.     set code 0
  29.     set result {}
  30.     set searchId [array startsearch enumArray]
  31.     while {[array anymore enumArray $searchId]} {
  32.     set enumVar [array nextelement enumArray $searchId]
  33.         set code [catch {uplevel 1 $codeFragment} result]
  34.         if {$code != 0 && $code != 4} break
  35.     }
  36.     array donesearch enumArray $searchId
  37.  
  38.     if {$code == 0 || $code == 3 || $code == 4} {
  39.         return $result
  40.     }
  41.     if {$code == 1} {
  42.         global errorCode errorInfo
  43.         return -code $code -errorcode $errorCode -errorinfo $errorInfo $result
  44.     }
  45.     return -code $code $result
  46. }
  47.  
  48.  
  49. #
  50. # compat --
  51. #
  52. # This file provides commands compatible with older versions of Extended Tcl.
  53. #------------------------------------------------------------------------------
  54. # Copyright 1992-1999 Karl Lehenbauer and Mark Diekhans.
  55. #
  56. # Permission to use, copy, modify, and distribute this software and its
  57. # documentation for any purpose and without fee is hereby granted, provided
  58. # that the above copyright notice appear in all copies.  Karl Lehenbauer and
  59. # Mark Diekhans make no representations about the suitability of this
  60. # software for any purpose.  It is provided "as is" without express or
  61. # implied warranty.
  62. #------------------------------------------------------------------------------
  63. # $Id: compat.tcl,v 8.6 1999/03/31 06:37:47 markd Exp $
  64. #------------------------------------------------------------------------------
  65. #
  66.  
  67. #@package: TclX-GenCompat assign_fields cexpand
  68.  
  69. proc assign_fields {list args} {
  70.     puts stderr {**** Your program is using an obsolete TclX proc, "assign_fields".}
  71.     puts stderr {**** Please use the command "lassign". Compatibility support will}
  72.     puts stderr {**** be removed in the next release.}
  73.  
  74.     proc assign_fields {list args} {
  75.         if [lempty $args] {
  76.             return
  77.         }
  78.         return [uplevel lassign [list $list] $args]
  79.     }
  80.     return [uplevel assign_fields [list $list] $args]
  81. }
  82.  
  83. # Added TclX 7.4a
  84. proc cexpand str {subst -nocommands -novariables $str}
  85.  
  86. #@package: TclX-ServerCompat server_open server_connect server_send \
  87.                              server_info server_cntl
  88.  
  89. # Added TclX 7.4a
  90.  
  91. proc server_open args {
  92.     set cmd server_connect
  93.  
  94.     set buffered 1
  95.     while {[string match -* [lindex $args 0]]} {
  96.         set opt [lvarpop args]
  97.         if [cequal $opt -buf] {
  98.             set buffered 1
  99.         } elseif  [cequal $opt -nobuf] {
  100.             set buffered 0
  101.         }
  102.         lappend cmd $opt
  103.     }
  104.     set handle [uplevel [concat $cmd $args]]
  105.     if $buffered {
  106.         lappend handle [dup $handle]
  107.     }
  108.     return $handle
  109. }
  110.  
  111. # Added TclX 7.5a
  112.  
  113. proc server_connect args {
  114.     set cmd socket
  115.  
  116.     set buffered 1
  117.     set twoids 0
  118.     while {[string match -* [lindex $args 0]]} {
  119.         switch -- [set opt [lvarpop args]] {
  120.             -buf {
  121.                 set buffered 1
  122.             }
  123.             -nobuf {
  124.                 set buffered 0
  125.             }
  126.             -myip {
  127.                 lappend cmd -myaddr [lvarpop args]
  128.             }
  129.             -myport {
  130.                 lappend cmd -myport [lvarpop args]
  131.             }
  132.             -twoids {
  133.                 set twoids 1
  134.             }
  135.             default {
  136.                 error "unknown option \"$opt\""
  137.             }
  138.         }
  139.     }
  140.     set handle [uplevel [concat $cmd $args]]
  141.     if !$buffered {
  142.         fconfigure $handle -buffering none 
  143.     }
  144.     if $twoids {
  145.         lappend handle [dup $handle]
  146.     }
  147.     return $handle
  148. }
  149.  
  150. proc server_send args {
  151.     set cmd puts
  152.  
  153.     while {[string match -* [lindex $args 0]]} {
  154.         switch -- [set opt [lvarpop args]] {
  155.             {-dontroute} {
  156.                 error "server_send if obsolete, -dontroute is not supported by the compatibility proc"
  157.             }
  158.             {-outofband} {
  159.                 error "server_send if obsolete, -outofband is not supported by the compatibility proc"
  160.             }
  161.         }
  162.         lappend cmd $opt
  163.     }
  164.     uplevel [concat $cmd $args]
  165.     flush [lindex $args 0]
  166. }
  167.  
  168. proc server_info args {
  169.     eval [concat host_info $args]
  170. }
  171.  
  172. proc server_cntl args {
  173.     eval [concat fcntl $args]
  174. }
  175.  
  176. #@package: TclX-ClockCompat fmtclock convertclock getclock
  177.  
  178. # Added TclX 7.5a
  179.  
  180. proc fmtclock {clockval {format {}} {zone {}}} {
  181.     lappend cmd clock format $clockval
  182.     if ![lempty $format] {
  183.         lappend cmd -format $format
  184.     }
  185.     if ![lempty $zone] {
  186.         lappend cmd -gmt 1
  187.     }
  188.     return [eval $cmd]
  189. }
  190.  
  191. # Added TclX 7.5a
  192.  
  193. proc convertclock {dateString {zone {}} {baseClock {}}} {
  194.     lappend cmd clock scan $dateString
  195.     if ![lempty $zone] {
  196.         lappend cmd -gmt 1
  197.     }
  198.     if ![lempty $baseClock] {
  199.         lappend cmd -base $baseClock
  200.     }
  201.     return [eval $cmd]
  202. }
  203.  
  204. # Added TclX 7.5a
  205.  
  206. proc getclock {} {
  207.     return [clock seconds]
  208. }
  209.  
  210. #@package: TclX-FileCompat mkdir rmdir unlink frename
  211.  
  212. # Added TclX 7.6.0
  213.  
  214. proc mkdir args {
  215.     set path 0
  216.     if {[llength $args] > 1} {
  217.         lvarpop args
  218.         set path 1
  219.     }
  220.     foreach dir [lindex $args 0] {
  221.         if {((!$path) && [file isdirectory $dir]) || \
  222.                 ([file exists $dir] && ![file isdirectory $dir])} {
  223.             error "creating directory \"$dir\" failed: file already exists" \
  224.                     {} {POSIX EEXIST {file already exists}}
  225.         }
  226.         file mkdir $dir
  227.     }
  228.     return
  229. }
  230.  
  231. # Added TclX 7.6.0
  232.  
  233. proc rmdir args {
  234.     set nocomplain 0
  235.     if {[llength $args] > 1} {
  236.         lvarpop args
  237.         set nocomplain 1
  238.         global errorInfo errorCode
  239.         set saveErrorInfo $errorInfo
  240.         set saveErrorCode $errorCode
  241.     }
  242.     foreach dir [lindex $args 0] {
  243.         if $nocomplain {
  244.             catch {file delete $dir}
  245.         } else {
  246.             if ![file exists $dir] {
  247.                 error "can't remove \"$dir\": no such file or directory" {} \
  248.                         {POSIX ENOENT {no such file or directory}}
  249.             }
  250.             if ![cequal [file type $dir] directory] {
  251.                 error "$dir: not a directory" {} \
  252.                         {POSIX ENOTDIR {not a directory}}
  253.             }
  254.             file delete $dir
  255.         }
  256.     }
  257.     if $nocomplain {
  258.         set errorInfo $saveErrorInfo 
  259.         set errorCode $saveErrorCode
  260.     }
  261.     return
  262. }
  263.  
  264. # Added TclX 7.6.0
  265.  
  266. proc unlink args {
  267.     set nocomplain 0
  268.     if {[llength $args] > 1} {
  269.         lvarpop args
  270.         set nocomplain 1
  271.         global errorInfo errorCode
  272.         set saveErrorInfo $errorInfo
  273.         set saveErrorCode $errorCode
  274.     }
  275.     foreach file [lindex $args 0] {
  276.         if {[file exists $file] && [cequal [file type $file] directory]} {
  277.             if !$nocomplain {
  278.                 error "$file: not owner" {} {POSIX EPERM {not owner}}
  279.             }
  280.         } elseif $nocomplain {
  281.             catch {file delete $file}
  282.         } else {
  283.             if {!([file exists $file] || \
  284.                     ([catch {file readlink $file}] == 0))} {
  285.                 error "can't remove \"$file\": no such file or directory" {} \
  286.                         {POSIX ENOENT {no such file or directory}}
  287.             }
  288.             file delete $file
  289.         }
  290.     }
  291.     if $nocomplain {
  292.         set errorInfo $saveErrorInfo 
  293.         set errorCode $saveErrorCode
  294.     }
  295.     return
  296. }
  297.  
  298. # Added TclX 7.6.0
  299.  
  300. proc frename {old new} {
  301.     if {[file isdirectory $new] && ![lempty [readdir $new]]} {
  302.         error "rename \"foo\" to \"baz\" failed: directory not empty" {} \
  303.                 POSIX ENOTEMPTY {directory not empty}
  304.     }
  305.     file rename -force $old $new
  306. }
  307.  
  308.  
  309. #@package: TclX-CopyFileCompat copyfile
  310.  
  311. # Added TclX 8.0.0
  312.  
  313. # copyfile ?-bytes num | \-maxbytes num? ?\-translate? fromFileId toFileId
  314.  
  315. proc copyfile args {
  316.     global errorInfo errorCode
  317.  
  318.     set copyMode NORMAL
  319.     set translate 0
  320.     while {[string match -* [lindex $args 0]]} {
  321.         set opt [lvarpop args]
  322.         switch -exact -- $opt {
  323.             -bytes {
  324.                 set copyMode BYTES
  325.                 if {[llength $args] == 0} {
  326.                     error "argument required for -bytes option"
  327.                 }
  328.                 set totalBytesToRead [lvarpop args]
  329.             }
  330.             -maxbytes {
  331.                 set copyMode MAX_BYTES
  332.                 if {[llength $args] == 0} {
  333.                     error "argument required for -maxbytes option"
  334.                 }
  335.                 set totalBytesToRead [lvarpop args]
  336.             }
  337.             -translate {
  338.                 set translate 1
  339.             }
  340.             default {
  341.                 error "invalid argument \"$opt\", expected \"-bytes\",\
  342.                         \"-maxbytes\", or \"-translate\""
  343.             }
  344.         }
  345.     }
  346.     if {[llength $args] != 2} {
  347.         error "wrong # args: copyfile ?-bytes num|-maxbytes num? ?-translate?\
  348.                 fromFileId toFileId"
  349.     }
  350.     lassign $args fromFileId toFileId
  351.  
  352.     if !$translate {
  353.         set fromOptions [list \
  354.                 [fconfigure $fromFileId -translation] \
  355.                 [fconfigure $fromFileId -eofchar]]
  356.         set toOptions [list \
  357.                 [fconfigure $toFileId -translation] \
  358.                 [fconfigure $toFileId -eofchar]]
  359.  
  360.         fconfigure $fromFileId -translation binary
  361.         fconfigure $fromFileId -eofchar {}
  362.         fconfigure $toFileId -translation binary
  363.         fconfigure $toFileId -eofchar {}
  364.     }
  365.  
  366.     set cmd [list fcopy $fromFileId $toFileId]
  367.     if ![cequal $copyMode NORMAL] {
  368.         lappend cmd -size $totalBytesToRead
  369.     }
  370.     
  371.     set stat [catch {eval $cmd} totalBytesRead]
  372.     if $stat {
  373.         set saveErrorResult $totalBytesRead
  374.         set saveErrorInfo $errorInfo
  375.         set saveErrorCode $errorCode
  376.     }
  377.  
  378.     if !$translate {
  379.         # Try to restore state, even if we have an error.
  380.         if [catch {
  381.             fconfigure $fromFileId -translation [lindex $fromOptions 0]
  382.             fconfigure $fromFileId -eofchar [lindex $fromOptions 1]
  383.             fconfigure $toFileId -translation [lindex $toOptions 0]
  384.             fconfigure $toFileId -eofchar [lindex $toOptions 1]
  385.         } errorResult] {
  386.             # If fcopy did not get an error, we process this one
  387.             if !$stat {
  388.                 set stat 1
  389.                 set saveErrorResult $errorResult
  390.                 set saveErrorInfo $errorInfo
  391.                 set saveErrorCode $errorCode
  392.             }
  393.         }
  394.     }
  395.  
  396.     if $stat {
  397.         error $saveErrorResult $saveErrorInfo $saveErrorCode
  398.     }
  399.  
  400.     if {[cequal $copyMode BYTES] && ($totalBytesToRead > 0) && \
  401.             ($totalBytesRead != $totalBytesToRead)} {
  402.         error "premature EOF, $totalBytesToRead bytes expected,\
  403.                 $totalBytesRead bytes actually read"
  404.     }
  405.     return $totalBytesRead
  406. }
  407. #
  408. # convlib.tcl --
  409. #
  410. #     Convert Ousterhout style tclIndex files and associated libraries to a
  411. # package library.
  412. #------------------------------------------------------------------------------
  413. # Copyright 1992-1999 Karl Lehenbauer and Mark Diekhans.
  414. #
  415. # Permission to use, copy, modify, and distribute this software and its
  416. # documentation for any purpose and without fee is hereby granted, provided
  417. # that the above copyright notice appear in all copies.  Karl Lehenbauer and
  418. # Mark Diekhans make no representations about the suitability of this
  419. # software for any purpose.  It is provided "as is" without express or
  420. # implied warranty.
  421. #------------------------------------------------------------------------------
  422. # $Id: convlib.tcl,v 8.7 1999/03/31 06:37:47 markd Exp $
  423. #------------------------------------------------------------------------------
  424. #
  425.  
  426. #@package: TclX-convertlib convert_lib
  427.  
  428. namespace eval TclX {
  429.  
  430.     #--------------------------------------------------------------------------
  431.     # ParseTclIndex
  432.     # Parse a tclIndex file, returning an array of file names with the list of
  433.     # procedures in each package. This is done by sourcing the file and then
  434.     # going through the local auto_index array that was created. Issues
  435.     # warnings for lines that can't be converted. 
  436.     #
  437.     # Returns 1 if all lines are converted, 0 if some failed.
  438.     #
  439.  
  440.     proc ParseTclIndex {tclIndex fileTblVar ignore} {
  441.         upvar $fileTblVar fileTbl
  442.         set allOK 1
  443.  
  444.         # Open and validate the file.
  445.  
  446.         set tclIndexFH [open $tclIndex r]
  447.         try_eval {
  448.             set hdr [gets $tclIndexFH]
  449.             if {!([cequal $hdr {# Tcl autoload index file, version 2.0}] ||
  450.                 [cequal $hdr == {# Tcl autoload index file, version 2.0 for [incr Tcl]}])} {
  451.                     error "can only convert version 2.0 Tcl auto-load files"
  452.                 }
  453.             set dir [file dirname $tclIndex]  ;# Expected by the script.
  454.             eval [read $tclIndexFH]
  455.         }  {} {
  456.             close $tclIndexFH
  457.         }
  458.         foreach procName [array names auto_index] {
  459.             if ![string match "source *" $auto_index($procName)] {
  460.                 puts stderr "WARNING: Can't convert load command for\
  461.                         \"$procName\": $auto_index($procName)"
  462.                 set allOK 0
  463.                 continue
  464.             }
  465.             set filePath [lindex $auto_index($procName) 1]
  466.             set fileName [file tail $filePath] 
  467.             if {[lsearch $ignore $fileName] >= 0} continue
  468.             
  469.             lappend fileTbl($filePath) $procName
  470.         }
  471.         if ![info exists fileTbl] {
  472.             error "no entries could be converted in $tclIndex"
  473.         }
  474.         return $allOK
  475.     }
  476. } ;# namespace TclX
  477.  
  478. #--------------------------------------------------------------------------
  479. # convert_lib:
  480. # Convert a tclIndex library to a .tlib. ignore any files in the ignore
  481. # list
  482.  
  483. proc convert_lib {tclIndex packageLib {ignore {}}} {
  484.     if {[file tail $tclIndex] != "tclIndex"} {
  485.         error "Tail file name must be `tclIndex': $tclIndex"}
  486.     if ![file readable $tclIndex] {
  487.         error "File not readable: $tclIndex"
  488.     }
  489.  
  490.     # Parse the file.
  491.  
  492.     set allOK [TclX::ParseTclIndex $tclIndex fileTbl $ignore]
  493.  
  494.     # Generate the .tlib package names with contain the directory and
  495.     # file name, less any extensions.
  496.  
  497.     if {[file extension $packageLib] != ".tlib"} {
  498.         append packageLib ".tlib"
  499.     }
  500.     set libFH [open $packageLib w]
  501.  
  502.     foreach srcFile [array names fileTbl] {
  503.         set pkgName [file tail [file dirname $srcFile]]/[file tail [file root $srcFile]]
  504.         set srcFH [open $srcFile r]
  505.         puts $libFH "#@package: $pkgName $fileTbl($srcFile)\n"
  506.         copyfile $srcFH $libFH
  507.         close $srcFH
  508.     }
  509.     close $libFH
  510.     buildpackageindex $packageLib
  511.     if !$allOK {
  512.         error "*** Not all entries converted, but library generated"
  513.     }
  514. }
  515.  
  516.  
  517. #
  518. # edprocs.tcl --
  519. #
  520. # Tools for Tcl developers. Procedures to save procs to a file and to edit
  521. # a proc in memory.
  522. #------------------------------------------------------------------------------
  523. # Copyright 1992-1999 Karl Lehenbauer and Mark Diekhans.
  524. #
  525. # Permission to use, copy, modify, and distribute this software and its
  526. # documentation for any purpose and without fee is hereby granted, provided
  527. # that the above copyright notice appear in all copies.  Karl Lehenbauer and
  528. # Mark Diekhans make no representations about the suitability of this
  529. # software for any purpose.  It is provided "as is" without express or
  530. # implied warranty.
  531. #------------------------------------------------------------------------------
  532. # $Id: edprocs.tcl,v 8.4 1999/03/31 06:37:47 markd Exp $
  533. #------------------------------------------------------------------------------
  534. #
  535.  
  536. #@package: TclX-developer_utils saveprocs edprocs
  537.  
  538. proc saveprocs {fileName args} {
  539.     set fp [open $fileName w]
  540.     try_eval {
  541.         puts $fp "# tcl procs saved on [fmtclock [getclock]]\n"
  542.         puts $fp [eval "showproc $args"]
  543.     } {} {
  544.         close $fp
  545.     }
  546. }
  547.  
  548. proc edprocs {args} {
  549.     global env
  550.  
  551.     set tmpFilename /tmp/tcldev.[id process]
  552.  
  553.     set fp [open $tmpFilename w]
  554.     try_eval {
  555.         puts $fp "\n# TEMP EDIT BUFFER -- YOUR CHANGES ARE FOR THIS SESSION ONLY\n"
  556.         puts $fp [eval "showproc $args"]
  557.     } {} {
  558.         close $fp
  559.     }
  560.  
  561.     if [info exists env(EDITOR)] {
  562.         set editor $env(EDITOR)
  563.     } else {
  564.     set editor vi
  565.     }
  566.  
  567.     set startMtime [file mtime $tmpFilename]
  568.     system "$editor $tmpFilename"
  569.  
  570.     if {[file mtime $tmpFilename] != $startMtime} {
  571.     source $tmpFilename
  572.     echo "Procedures were reloaded."
  573.     } else {
  574.     echo "No changes were made."
  575.     }
  576.     unlink $tmpFilename
  577.     return
  578. }
  579.  
  580.  
  581. #
  582. # eventloop.tcl --
  583. #
  584. # Eventloop procedure.
  585. #------------------------------------------------------------------------------
  586. # Copyright 1992-1999 Karl Lehenbauer and Mark Diekhans.
  587. #
  588. # Permission to use, copy, modify, and distribute this software and its
  589. # documentation for any purpose and without fee is hereby granted, provided
  590. # that the above copyright notice appear in all copies.  Karl Lehenbauer and
  591. # Mark Diekhans make no representations about the suitability of this
  592. # software for any purpose.  It is provided "as is" without express or
  593. # implied warranty.
  594. #------------------------------------------------------------------------------
  595. # $Id: events.tcl,v 8.3 1999/03/31 06:37:47 markd Exp $
  596. #------------------------------------------------------------------------------
  597. #
  598.  
  599. #@package: TclX-events mainloop
  600.  
  601. proc mainloop {} {
  602.     global tcl_interactive
  603.  
  604.     if {[info exists tcl_interactive] && $tcl_interactive} {
  605.         commandloop -async -interactive on -endcommand exit
  606.     }
  607.     set loopVar 0
  608.     catch {vwait loopVar}
  609.     exit
  610. }
  611.  
  612.  
  613. #
  614. # forfile.tcl --
  615. #
  616. # Proc to execute code on every line of a file.
  617. #------------------------------------------------------------------------------
  618. # Copyright 1992-1999 Karl Lehenbauer and Mark Diekhans.
  619. #
  620. # Permission to use, copy, modify, and distribute this software and its
  621. # documentation for any purpose and without fee is hereby granted, provided
  622. # that the above copyright notice appear in all copies.  Karl Lehenbauer and
  623. # Mark Diekhans make no representations about the suitability of this
  624. # software for any purpose.  It is provided "as is" without express or
  625. # implied warranty.
  626. #------------------------------------------------------------------------------
  627. # $Id: forfile.tcl,v 8.5 1999/03/31 06:37:48 markd Exp $
  628. #------------------------------------------------------------------------------
  629. #
  630.  
  631. #@package: TclX-forfile for_file
  632.  
  633. proc for_file {var filename cmd} {
  634.     upvar 1 $var line
  635.     set fp [open $filename r]
  636.     try_eval {
  637.         set code 0
  638.         set result {}
  639.         while {[gets $fp line] >= 0} {
  640.             set code [catch {uplevel 1 $cmd} result]
  641.             if {$code != 0 && $code != 4} break
  642.         }
  643.     } {} {
  644.         close $fp
  645.     }
  646.  
  647.     if {$code == 0 || $code == 3 || $code == 4} {
  648.         return $result
  649.     }
  650.     if {$code == 1} {
  651.         global errorCode errorInfo
  652.         return -code $code -errorcode $errorCode -errorinfo $errorInfo $result
  653.     }
  654.     return -code $code $result
  655. }
  656.  
  657.  
  658. #
  659. # globrecur.tcl --
  660. #
  661. #  Build or process a directory list recursively.
  662. #------------------------------------------------------------------------------
  663. # Copyright 1992-1999 Karl Lehenbauer and Mark Diekhans.
  664. #
  665. # Permission to use, copy, modify, and distribute this software and its
  666. # documentation for any purpose and without fee is hereby granted, provided
  667. # that the above copyright notice appear in all copies.  Karl Lehenbauer and
  668. # Mark Diekhans make no representations about the suitability of this
  669. # software for any purpose.  It is provided "as is" without express or
  670. # implied warranty.
  671. #------------------------------------------------------------------------------
  672. # $Id: globrecur.tcl,v 8.3 1999/03/31 06:37:48 markd Exp $
  673. #------------------------------------------------------------------------------
  674. #
  675.  
  676. #@package: TclX-globrecur recursive_glob
  677.  
  678. proc recursive_glob {dirlist globlist} {
  679.     set result {}
  680.     set recurse {}
  681.     foreach dir $dirlist {
  682.         if ![file isdirectory $dir] {
  683.             error "\"$dir\" is not a directory"
  684.         }
  685.         foreach pattern $globlist {
  686.             set result [concat $result \
  687.                     [glob -nocomplain -- [file join $dir $pattern]]]
  688.         }
  689.         foreach file [readdir $dir] {
  690.             set file [file join $dir $file]
  691.             if [file isdirectory $file] {
  692.                 set fileTail [file tail $file]
  693.                 if {!([cequal $fileTail .] || [cequal $fileTail ..])} {
  694.                     lappend recurse $file
  695.                 }
  696.             }
  697.         }
  698.     }
  699.     if ![lempty $recurse] {
  700.         set result [concat $result [recursive_glob $recurse $globlist]]
  701.     }
  702.     return $result
  703. }
  704.  
  705. #@package: TclX-forrecur for_recursive_glob
  706.  
  707. proc for_recursive_glob {var dirlist globlist cmd {depth 1}} {
  708.     upvar $depth $var myVar
  709.     set recurse {}
  710.     foreach dir $dirlist {
  711.         if ![file isdirectory $dir] {
  712.             error "\"$dir\" is not a directory"
  713.         }
  714.         set code 0
  715.         set result {}
  716.         foreach pattern $globlist {
  717.             foreach file [glob -nocomplain -- [file join $dir $pattern]] {
  718.                 set myVar $file
  719.                 set code [catch {uplevel $depth $cmd} result]
  720.                 if {$code != 0 && $code != 4} break
  721.             }
  722.             if {$code != 0 && $code != 4} break
  723.         }
  724.         if {$code != 0 && $code != 4} {
  725.             if {$code == 3} {
  726.                 return $result
  727.             }
  728.             if {$code == 1} {
  729.                 global errorCode errorInfo
  730.                 return -code $code -errorcode $errorCode \
  731.                         -errorinfo $errorInfo $result
  732.             }
  733.             return -code $code $result
  734.         }
  735.  
  736.         foreach file [readdir $dir] {
  737.             set file [file join $dir $file]
  738.             if [file isdirectory $file] {
  739.                 set fileTail [file tail $file]
  740.                 if {!([cequal $fileTail .] || [cequal $fileTail ..])} {
  741.                     lappend recurse $file
  742.                 }
  743.             }
  744.         }
  745.     }
  746.     if ![lempty $recurse] {
  747.         return [for_recursive_glob $var $recurse $globlist $cmd \
  748.                     [expr $depth + 1]]
  749.     }
  750.     return {}
  751. }
  752.  
  753.  
  754. #
  755. # help.tcl --
  756. #
  757. # Tcl help command. (see TclX manual)
  758. #------------------------------------------------------------------------------
  759. # Copyright 1992-1999 Karl Lehenbauer and Mark Diekhans.
  760. #
  761. # Permission to use, copy, modify, and distribute this software and its
  762. # documentation for any purpose and without fee is hereby granted, provided
  763. # that the above copyright notice appear in all copies.  Karl Lehenbauer and
  764. # Mark Diekhans make no representations about the suitability of this
  765. # software for any purpose.  It is provided "as is" without express or
  766. # implied warranty.
  767. #------------------------------------------------------------------------------
  768. # The help facility is based on a hierarchical tree of subjects (directories)
  769. # and help pages (files).  There is a virtual root to this tree. The root
  770. # being the merger of all "help" directories found along the $auto_path
  771. # variable.
  772. #------------------------------------------------------------------------------
  773. # $Id: help.tcl,v 8.9 1999/03/31 06:37:48 markd Exp $
  774. #------------------------------------------------------------------------------
  775. #
  776. # FIX: Convert this to use namespaces.
  777.  
  778. #@package: TclX-help help helpcd helppwd apropos
  779.  
  780. namespace eval TclXHelp {
  781.  
  782.     variable curSubject "/"
  783.  
  784.     #----------------------------------------------------------------------
  785.     # Return a list of help root directories.
  786.  
  787.     proc RootDirs {} {
  788.         global auto_path
  789.         set roots {}
  790.         foreach dir $auto_path {
  791.             if [file isdirectory $dir/help] {
  792.                 lappend roots $dir/help
  793.             }
  794.         }
  795.         return $roots
  796.     }
  797.  
  798.     #--------------------------------------------------------------------------
  799.     # Take a path name which might have "." and ".." elements and flatten them
  800.     # out.  Also removes trailing and adjacent "/", unless its the only
  801.     # character.
  802.  
  803.     proc FlattenPath pathName {
  804.         set newPath {}
  805.         foreach element [split $pathName /] {
  806.             if {"$element" == "." || [lempty $element]} continue
  807.  
  808.             if {"$element" == ".."} {
  809.                 if {[llength [join $newPath /]] == 0} {
  810.                     error "Help: name goes above subject directory root" {} \
  811.                         [list TCLXHELP NAMEABOVEROOT $pathName]
  812.                 }
  813.                 lvarpop newPath [expr [llength $newPath]-1]
  814.                 continue
  815.             }
  816.             lappend newPath $element
  817.         }
  818.         set newPath [join $newPath /]
  819.  
  820.         # Take care of the case where we started with something line "/" or "/."
  821.  
  822.         if {("$newPath" == "") && [string match "/*" $pathName]} {
  823.             set newPath "/"
  824.         }
  825.  
  826.         return $newPath
  827.     }
  828.  
  829.     #--------------------------------------------------------------------------
  830.     # Given a pathName relative to the virtual help root, convert it to a list
  831.     # of real file paths.  A list is returned because the path could be "/",
  832.     # returning a list of all roots. The list is returned in the same order of
  833.     # the auto_path variable. If path does not start with a "/", it is take as
  834.     # relative to the current help subject.  Note: The root directory part of
  835.     # the name is not flattened.  This lets other commands pick out the part
  836.     # relative to the one of the root directories.
  837.  
  838.     proc ConvertPath pathName {
  839.         variable curSubject
  840.  
  841.         if {![string match "/*" $pathName]} {
  842.             if [cequal $curSubject "/"] {
  843.                 set pathName "/$pathName"
  844.             } else {
  845.                 set pathName "$curSubject/$pathName"
  846.             }
  847.         }
  848.         set pathName [FlattenPath $pathName]
  849.  
  850.         # If the virtual root is specified, return a list of directories.
  851.  
  852.         if {$pathName == "/"} {
  853.             return [RootDirs]
  854.         }
  855.  
  856.         # Not the virtual root find the first match.
  857.  
  858.         foreach dir [RootDirs] {
  859.             if [file readable $dir/$pathName] {
  860.                 return [list $dir/$pathName]
  861.             }
  862.         }
  863.  
  864.     # Not found, try to find a file matching only the file tail,
  865.     # for example if --> <helpDir>/tcl/control/if.
  866.  
  867.     set fileTail [file tail $pathName]
  868.         foreach dir [RootDirs] {
  869.         set fileName [exec find $dir -name $fileTail | head -1]
  870.         if {$fileName != {}} {
  871.                 return [list $fileName]
  872.         }
  873.     }
  874.  
  875.         error "\"$pathName\" does not exist" {} \
  876.             [list TCLXHELP NOEXIST $pathName]
  877.     }
  878.  
  879.     #--------------------------------------------------------------------------
  880.     # Return the virtual root relative name of the file given its absolute
  881.     # path.  The root part of the path should not have been flattened, as we
  882.     # would not be able to match it.
  883.  
  884.     proc RelativePath pathName {
  885.         foreach dir [RootDirs] {
  886.             if {[csubstr $pathName 0 [clength $dir]] == $dir} {
  887.                 set name [csubstr $pathName [clength $dir] end]
  888.                 if {$name == ""} {set name /}
  889.                 return $name
  890.             }
  891.         }
  892.         if ![info exists found] {
  893.             error "problem translating \"$pathName\"" {} [list TCLXHELP INTERROR]
  894.         }
  895.     }
  896.  
  897.     #--------------------------------------------------------------------------
  898.     # Given a list of path names to subjects generated by ConvertPath, return
  899.     # the contents of the subjects.  Two lists are returned, subjects under
  900.     # that subject and a list of pages under the subject.  Both lists are
  901.     # returned sorted.  This merges all the roots into a virtual root.
  902.     # pathName is the string that was passed to ConvertPath and is used for
  903.     # error reporting.  *.brk files are not returned.
  904.  
  905.     proc ListSubject {pathName pathList subjectsVar pagesVar} {
  906.         upvar $subjectsVar subjects $pagesVar pages
  907.  
  908.         set subjects {}
  909.         set pages {}
  910.         set foundDir 0
  911.         foreach dir $pathList {
  912.             if ![file isdirectory $dir] continue
  913.             if [cequal [file tail $dir] CVS] continue
  914.             set foundDir 1
  915.             foreach file [glob -nocomplain $dir/*] {
  916.                 if {[lsearch {.brf .orig .diff .rej} [file extension $file]] \
  917.                         >= 0} continue
  918.                 if [file isdirectory $file] {
  919.                     lappend subjects [file tail $file]/
  920.                 } else {
  921.                     lappend pages [file tail $file]
  922.                 }
  923.             }
  924.         }
  925.         if !$foundDir {
  926.             if [cequal $pathName /] {
  927.                 global auto_path
  928.                 error "no \"help\" directories found on auto_path ($auto_path)" {} \
  929.                     [list TCLXHELP NOHELPDIRS]
  930.             } else {
  931.                 error "\"$pathName\" is not a subject" {} \
  932.                     [list TCLXHELP NOTSUBJECT $pathName]
  933.             }
  934.         }
  935.         set subjects [lsort $subjects]
  936.         set pages [lsort $pages]
  937.         return {}
  938.     }
  939.  
  940.     #--------------------------------------------------------------------------
  941.     # Display a line of output, pausing waiting for input before displaying if
  942.     # the screen size has been reached.  Return 1 if output is to continue,
  943.     # return 0 if no more should be outputed, indicated by input other than
  944.     # return.
  945.     #
  946.  
  947.     proc Display line {
  948.         variable lineCnt
  949.         if {$lineCnt >= 23} {
  950.             set lineCnt 0
  951.             puts -nonewline stdout ":"
  952.             flush stdout
  953.             gets stdin response
  954.             if {![lempty $response]} {
  955.                 return 0}
  956.         }
  957.         puts stdout $line
  958.         incr lineCnt
  959.     }
  960.  
  961.     #--------------------------------------------------------------------------
  962.     # Display a help page (file).
  963.  
  964.     proc DisplayPage filePath {
  965.  
  966.         set inFH [open $filePath r]
  967.         try_eval {
  968.             while {[gets $inFH fileBuf] >= 0} {
  969.                 if {![Display $fileBuf]} {
  970.                     break
  971.                 }
  972.             }
  973.         } {} {
  974.             close $inFH
  975.         }
  976.     }
  977.  
  978.     #--------------------------------------------------------------------------
  979.     # Display a list of file names in a column format. This use columns of 14 
  980.     # characters 3 blanks.
  981.  
  982.     proc DisplayColumns {nameList} {
  983.         set count 0
  984.         set outLine ""
  985.         foreach name $nameList {
  986.             if {$count == 0} {
  987.                 append outLine "   "}
  988.             append outLine $name
  989.             if {[incr count] < 4} {
  990.                 set padLen [expr 17-[clength $name]]
  991.                 if {$padLen < 3} {
  992.                    set padLen 3}
  993.                 append outLine [replicate " " $padLen]
  994.             } else {
  995.                if {![Display $outLine]} {
  996.                    return}
  997.                set outLine ""
  998.                set count 0
  999.             }
  1000.         }
  1001.         if {$count != 0} {
  1002.             Display [string trimright $outLine]}
  1003.         return
  1004.     }
  1005.  
  1006.  
  1007.     #--------------------------------------------------------------------------
  1008.     # Display help on help, the first occurance of a help page called "help" in
  1009.     # the help root.
  1010.  
  1011.     proc HelpOnHelp {} {
  1012.         set helpPage [lindex [ConvertPath /help] 0]
  1013.         if [lempty $helpPage] {
  1014.             error "No help page on help found" {} \
  1015.                 [list TCLXHELP NOHELPPAGE]
  1016.         }
  1017.         DisplayPage $helpPage
  1018.     }
  1019.  
  1020. };# namespace TclXHelp
  1021.  
  1022.  
  1023. #------------------------------------------------------------------------------
  1024. # Help command.
  1025.  
  1026. proc help {{what {}}} {
  1027.     variable ::TclXHelp::lineCnt 0
  1028.  
  1029.     # Special case "help help", so we can get it at any level.
  1030.  
  1031.     if {($what == "help") || ($what == "?")} {
  1032.         TclXHelp::HelpOnHelp
  1033.         return
  1034.     }
  1035.  
  1036.     set pathList [TclXHelp::ConvertPath $what]
  1037.     if [file isfile [lindex $pathList 0]] {
  1038.         TclXHelp::DisplayPage [lindex $pathList 0]
  1039.         return
  1040.     }
  1041.  
  1042.     TclXHelp::ListSubject $what $pathList subjects pages
  1043.     set relativeDir [TclXHelp::RelativePath [lindex $pathList 0]]
  1044.  
  1045.     if {[llength $subjects] != 0} {
  1046.         TclXHelp::Display "\nSubjects available in $relativeDir:"
  1047.         TclXHelp::DisplayColumns $subjects
  1048.     }
  1049.     if {[llength $pages] != 0} {
  1050.         TclXHelp::Display "\nHelp pages available in $relativeDir:"
  1051.         TclXHelp::DisplayColumns $pages
  1052.     }
  1053. }
  1054.  
  1055.  
  1056. #------------------------------------------------------------------------------
  1057. # helpcd command.  The name of the new current directory is assembled from the
  1058. # current directory and the argument.
  1059.  
  1060. proc helpcd {{dir /}} {
  1061.     variable ::TclXHelp::curSubject
  1062.  
  1063.     set pathName [lindex [TclXHelp::ConvertPath $dir] 0]
  1064.  
  1065.     if {![file isdirectory $pathName]} {
  1066.         error "\"$dir\" is not a subject" \
  1067.             [list TCLXHELP NOTSUBJECT $dir]
  1068.     }
  1069.  
  1070.     set ::TclXHelp::curSubject [TclXHelp::RelativePath $pathName]
  1071.     return
  1072. }
  1073.  
  1074. #------------------------------------------------------------------------------
  1075. # Helpcd main.
  1076.  
  1077. proc helppwd {} {
  1078.     variable ::TclXHelp::curSubject
  1079.     echo "Current help subject: $::TclXHelp::curSubject"
  1080. }
  1081.  
  1082. #------------------------------------------------------------------------------
  1083. # apropos command.  This search the 
  1084.  
  1085. proc apropos {regexp} {
  1086.     variable ::TclXHelp::lineCnt 0
  1087.     variable ::TclXHelp::curSubject
  1088.  
  1089.     set ch [scancontext create]
  1090.     scanmatch -nocase $ch $regexp {
  1091.         set path [lindex $matchInfo(line) 0]
  1092.         set desc [lrange $matchInfo(line) 1 end]
  1093.         if {![TclXHelp::Display [format "%s - %s" $path $desc]]} {
  1094.             set stop 1
  1095.             return}
  1096.     }
  1097.     set stop 0
  1098.     foreach dir [TclXHelp::RootDirs] {
  1099.         foreach brief [glob -nocomplain $dir/*.brf] {
  1100.             set briefFH [open $brief]
  1101.             try_eval {
  1102.                 scanfile $ch $briefFH
  1103.             } {} {
  1104.                 close $briefFH
  1105.             }
  1106.             if $stop break
  1107.         }
  1108.         if $stop break
  1109.     }
  1110.     scancontext delete $ch
  1111. }
  1112. #
  1113. # profrep  --
  1114. #
  1115. # Generate Tcl profiling reports.
  1116. #------------------------------------------------------------------------------
  1117. # Copyright 1992-1999 Karl Lehenbauer and Mark Diekhans.
  1118. #
  1119. # Permission to use, copy, modify, and distribute this software and its
  1120. # documentation for any purpose and without fee is hereby granted, provided
  1121. # that the above copyright notice appear in all copies.  Karl Lehenbauer and
  1122. # Mark Diekhans make no representations about the suitability of this
  1123. # software for any purpose.  It is provided "as is" without express or
  1124. # implied warranty.
  1125. #------------------------------------------------------------------------------
  1126. # $Id: profrep.tcl,v 8.5 1999/03/31 06:37:48 markd Exp $
  1127. #------------------------------------------------------------------------------
  1128. #
  1129.  
  1130. #@package: TclX-profrep profrep
  1131.  
  1132. namespace eval TclXProfRep {
  1133.  
  1134.     #
  1135.     # Convert the profile array from entries that have only the time spent in
  1136.     # the proc to the time spend in the proc and all it calls.
  1137.     #
  1138.     proc sum {inDataVar outDataVar} {
  1139.         upvar 1 $inDataVar inData $outDataVar outData
  1140.  
  1141.         foreach inStack [array names inData] {
  1142.             for {set idx 0} {![lempty [set part [lrange $inStack $idx end]]]} \
  1143.                     {incr idx} {
  1144.                 if ![info exists outData($part)] {
  1145.                     set outData($part) {0 0 0}
  1146.                 }
  1147.                 lassign $outData($part) count real cpu
  1148.                 if {$idx == 0} {
  1149.                     incr count [lindex $inData($inStack) 0]
  1150.                 }
  1151.                 incr real [lindex $inData($inStack) 1]
  1152.                 incr cpu [lindex $inData($inStack) 2]
  1153.                 set outData($part) [list $count $real $cpu]
  1154.             }
  1155.         }
  1156.     }
  1157.  
  1158.     #
  1159.     # Do sort comparison.  May only be called by sort, as it address its
  1160.     # local variables.
  1161.     #
  1162.     proc sortcmp {key1 key2} {
  1163.         upvar profData profData keyIndex keyIndex
  1164.  
  1165.         set val1 [lindex $profData($key1) $keyIndex]
  1166.         set val2 [lindex $profData($key2) $keyIndex]
  1167.  
  1168.         if {$val1 < $val2} {
  1169.             return -1
  1170.         }
  1171.         if {$val1 > $val2} {
  1172.             return 1
  1173.         }
  1174.         return 0
  1175.     }
  1176.  
  1177.     #
  1178.     # Generate a list, sorted in descending order by the specified key, contain
  1179.     # the indices into the summarized data.
  1180.     #
  1181.     proc sort {profDataVar sortKey} {
  1182.         upvar $profDataVar profData
  1183.  
  1184.         case $sortKey {
  1185.             {calls} {set keyIndex 0}
  1186.             {real}  {set keyIndex 1}
  1187.             {cpu}   {set keyIndex 2}
  1188.             default {
  1189.                 error "Expected a sort type of: `calls', `cpu' or ` real'"
  1190.             }
  1191.         }
  1192.  
  1193.         return [lsort -integer -decreasing -command sortcmp \
  1194.                 [array names profData]]
  1195.     }
  1196.  
  1197.     #
  1198.     # Print the sorted report
  1199.     #
  1200.     proc print {profDataVar sortedProcList outFile userTitle} {
  1201.         upvar $profDataVar profData
  1202.  
  1203.         set maxNameLen 0
  1204.         foreach procStack [array names profData] {
  1205.             foreach procName $procStack {
  1206.                 set maxNameLen [max $maxNameLen [clength $procName]]
  1207.             }
  1208.         }
  1209.  
  1210.         if {$outFile == ""} {
  1211.             set outFH stdout
  1212.         } else {
  1213.             set outFH [open $outFile w]
  1214.         }
  1215.  
  1216.         # Output a header.
  1217.  
  1218.         set stackTitle "Procedure Call Stack"
  1219.         set maxNameLen [max [expr $maxNameLen+6] [expr [clength $stackTitle]+4]]
  1220.         set hdr [format "%-${maxNameLen}s %10s %10s %10s" $stackTitle \
  1221.                         "Calls" "Real Time" "CPU Time"]
  1222.         if {$userTitle != ""} {
  1223.             puts $outFH [replicate - [clength $hdr]]
  1224.             puts $outFH $userTitle
  1225.         }
  1226.         puts $outFH [replicate - [clength $hdr]]
  1227.         puts $outFH $hdr
  1228.         puts $outFH [replicate - [clength $hdr]]
  1229.  
  1230.         # Output the data in sorted order.  Trim leading ::.
  1231.  
  1232.         foreach procStack $sortedProcList {
  1233.             set data $profData($procStack)
  1234.             set cmd [lvarpop procStack]
  1235.             regsub {^::} $cmd {} cmd
  1236.             puts $outFH [format "%-${maxNameLen}s %10d %10d %10d" \
  1237.                                 $cmd [lindex $data 0] [lindex $data 1] \
  1238.                                 [lindex $data 2]]
  1239.             foreach procName $procStack {
  1240.                 if {$procName == "<global>"} break
  1241.                 regsub {^::} $procName {} procName
  1242.                 puts $outFH "    $procName"
  1243.             }
  1244.         }
  1245.         if {$outFile != ""} {
  1246.             close $outFH
  1247.         }
  1248.     }
  1249.  
  1250. } ;# TclXProfRep
  1251.  
  1252. #------------------------------------------------------------------------------
  1253. # Generate a report from data collect from the profile command.
  1254. #   o profDataVar (I) - The name of the array containing the data from profile.
  1255. #   o sortKey (I) - Value to sort by. One of "calls", "cpu" or "real".
  1256. #   o outFile (I) - Name of file to write the report to.  If omitted, stdout
  1257. #     is assumed.
  1258. #   o userTitle (I) - Title line to add to output.
  1259.  
  1260. proc profrep {profDataVar sortKey {outFile {}} {userTitle {}}} {
  1261.     upvar $profDataVar profData
  1262.  
  1263.     TclXProfRep::sum profData sumProfData
  1264.     set sortedProcList [TclXProfRep::sort sumProfData $sortKey]
  1265.     TclXProfRep::print sumProfData $sortedProcList $outFile $userTitle
  1266. }
  1267.  
  1268.  
  1269. #
  1270. # pushd.tcl --
  1271. #
  1272. # C-shell style directory stack procs.
  1273. #
  1274. #------------------------------------------------------------------------------
  1275. # Copyright 1992-1999 Karl Lehenbauer and Mark Diekhans.
  1276. #
  1277. # Permission to use, copy, modify, and distribute this software and its
  1278. # documentation for any purpose and without fee is hereby granted, provided
  1279. # that the above copyright notice appear in all copies.  Karl Lehenbauer and
  1280. # Mark Diekhans make no representations about the suitability of this
  1281. # software for any purpose.  It is provided "as is" without express or
  1282. # implied warranty.
  1283. #------------------------------------------------------------------------------
  1284. # $Id: pushd.tcl,v 8.3 1999/03/31 06:37:48 markd Exp $
  1285. #------------------------------------------------------------------------------
  1286. #
  1287.  
  1288. #@package: TclX-directory_stack pushd popd dirs
  1289.  
  1290. global TCLXENV(dirPushList)
  1291.  
  1292. set TCLXENV(dirPushList) ""
  1293.  
  1294. proc pushd {{new ""}} {
  1295.     global TCLXENV
  1296.  
  1297.     set current [pwd]
  1298.     if {[clength $new] > 0} {
  1299.         set dirs [glob -nocomplain $new]
  1300.         set count [llength $dirs]
  1301.         if {$count == 0} {
  1302.             error "no such directory: $new"
  1303.         } elseif {$count != 1} {
  1304.             error "ambiguous directory: $new: [join $directories ", "]"
  1305.         }
  1306.         cd [lindex $dirs 0]
  1307.         lvarpush TCLXENV(dirPushList) $current
  1308.     } else {
  1309.         if [lempty $TCLXENV(dirPushList)] {
  1310.             error "directory stack empty"
  1311.         }
  1312.         cd [lindex $TCLXENV(dirPushList) 0]
  1313.         lvarpop TCLXENV(dirPushList)
  1314.         lvarpush TCLXENV(dirPushList) $current
  1315.     }
  1316.     return [pwd]
  1317. }
  1318.  
  1319. proc popd {} {
  1320.     global TCLXENV
  1321.  
  1322.     if [lempty $TCLXENV(dirPushList)] {
  1323.         error "directory stack empty"
  1324.     }
  1325.     cd [lvarpop TCLXENV(dirPushList)]
  1326.     return [pwd]
  1327. }
  1328.  
  1329. proc dirs {} { 
  1330.     global TCLXENV
  1331.     return [concat [list [pwd]] $TCLXENV(dirPushList)]
  1332. }
  1333.  
  1334.  
  1335. #
  1336. # setfuncs --
  1337. #
  1338. # Perform set functions on lists.  Also has a procedure for removing duplicate
  1339. # list entries.
  1340. #------------------------------------------------------------------------------
  1341. # Copyright 1992-1999 Karl Lehenbauer and Mark Diekhans.
  1342. #
  1343. # Permission to use, copy, modify, and distribute this software and its
  1344. # documentation for any purpose and without fee is hereby granted, provided
  1345. # that the above copyright notice appear in all copies.  Karl Lehenbauer and
  1346. # Mark Diekhans make no representations about the suitability of this
  1347. # software for any purpose.  It is provided "as is" without express or
  1348. # implied warranty.
  1349. #------------------------------------------------------------------------------
  1350. # $Id: setfuncs.tcl,v 8.4 1999/03/31 06:37:48 markd Exp $
  1351. #------------------------------------------------------------------------------
  1352. #
  1353.  
  1354. #@package: TclX-set_functions union intersect intersect3 lrmdups
  1355.  
  1356. #
  1357. # return the logical union of two lists, removing any duplicates
  1358. #
  1359. proc union {lista listb} {
  1360.     return [lrmdups [concat $lista $listb]]
  1361. }
  1362.  
  1363. #
  1364. # sort a list, returning the sorted version minus any duplicates
  1365. #
  1366. proc lrmdups list {
  1367.     if [lempty $list] {
  1368.         return {}
  1369.     }
  1370.     set list [lsort $list]
  1371.     set last [lvarpop list]
  1372.     lappend result $last
  1373.     foreach element $list {
  1374.     if ![cequal $last $element] {
  1375.         lappend result $element
  1376.         set last $element
  1377.     }
  1378.     }
  1379.     return $result
  1380. }
  1381.  
  1382. #
  1383. # intersect3 - perform the intersecting of two lists, returning a list
  1384. # containing three lists.  The first list is everything in the first
  1385. # list that wasn't in the second, the second list contains the intersection
  1386. # of the two lists, the third list contains everything in the second list
  1387. # that wasn't in the first.
  1388. #
  1389.  
  1390. proc intersect3 {list1 list2} {
  1391.     set la1(0) {} ; unset la1(0)
  1392.     set lai(0) {} ; unset lai(0)
  1393.     set la2(0) {} ; unset la2(0)
  1394.     foreach v $list1 {
  1395.         set la1($v) {}
  1396.     }
  1397.     foreach v $list2 {
  1398.         set la2($v) {}
  1399.     }
  1400.     foreach elem [concat $list1 $list2] {
  1401.         if {[info exists la1($elem)] && [info exists la2($elem)]} {
  1402.             unset la1($elem)
  1403.             unset la2($elem)
  1404.             set lai($elem) {}
  1405.         }
  1406.     }
  1407.     list [lsort [array names la1]] [lsort [array names lai]] \
  1408.          [lsort [array names la2]]
  1409. }
  1410.  
  1411. #
  1412. # intersect - perform an intersection of two lists, returning a list
  1413. # containing every element that was present in both lists
  1414. #
  1415. proc intersect {list1 list2} {
  1416.     set intersectList ""
  1417.  
  1418.     set list1 [lsort $list1]
  1419.     set list2 [lsort $list2]
  1420.  
  1421.     while {1} {
  1422.         if {[lempty $list1] || [lempty $list2]} break
  1423.  
  1424.         set compareResult [string compare [lindex $list1 0] [lindex $list2 0]]
  1425.  
  1426.         if {$compareResult < 0} {
  1427.             lvarpop list1
  1428.             continue
  1429.         }
  1430.  
  1431.         if {$compareResult > 0} {
  1432.             lvarpop list2
  1433.             continue
  1434.         }
  1435.  
  1436.         lappend intersectList [lvarpop list1]
  1437.         lvarpop list2
  1438.     }
  1439.     return $intersectList
  1440. }
  1441.  
  1442.  
  1443.  
  1444.  
  1445. #
  1446. # showproc.tcl --
  1447. #
  1448. # Display procedure headers and bodies.
  1449. #------------------------------------------------------------------------------
  1450. # Copyright 1992-1999 Karl Lehenbauer and Mark Diekhans.
  1451. #
  1452. # Permission to use, copy, modify, and distribute this software and its
  1453. # documentation for any purpose and without fee is hereby granted, provided
  1454. # that the above copyright notice appear in all copies.  Karl Lehenbauer and
  1455. # Mark Diekhans make no representations about the suitability of this
  1456. # software for any purpose.  It is provided "as is" without express or
  1457. # implied warranty.
  1458. #------------------------------------------------------------------------------
  1459. # $Id: showproc.tcl,v 8.3 1999/03/31 06:37:48 markd Exp $
  1460. #------------------------------------------------------------------------------
  1461. #
  1462.  
  1463. #@package: TclX-showproc showproc
  1464.  
  1465. proc showproc args {
  1466.     if [lempty $args] {
  1467.         set args [info procs]
  1468.     }
  1469.     set out {}
  1470.  
  1471.     foreach procname $args {
  1472.         if [lempty [info procs $procname]] {
  1473.             auto_load $procname
  1474.         }
  1475.         set arglist [info args $procname]
  1476.         set nargs {}
  1477.         while {[llength $arglist] > 0} {
  1478.             set varg [lvarpop arglist 0]
  1479.             if [info default $procname $varg defarg] {
  1480.                 lappend nargs [list $varg $defarg]
  1481.             } else {
  1482.                 lappend nargs $varg
  1483.             }
  1484.         }
  1485.         append out "proc $procname [list $nargs] \{[info body $procname]\}\n"
  1486.     }
  1487.     return $out
  1488. }
  1489.  
  1490.  
  1491. #
  1492. # string_file --
  1493. #
  1494. # Functions to read and write strings from a file that has not been opened.
  1495. #------------------------------------------------------------------------------
  1496. # Copyright 1992-1999 Karl Lehenbauer and Mark Diekhans.
  1497. #
  1498. # Permission to use, copy, modify, and distribute this software and its
  1499. # documentation for any purpose and without fee is hereby granted, provided
  1500. # that the above copyright notice appear in all copies.  Karl Lehenbauer and
  1501. # Mark Diekhans make no representations about the suitability of this
  1502. # software for any purpose.  It is provided "as is" without express or
  1503. # implied warranty.
  1504. #------------------------------------------------------------------------------
  1505. # $Id: stringfile.tcl,v 8.4 1999/03/31 06:37:48 markd Exp $
  1506. #------------------------------------------------------------------------------
  1507. #
  1508.  
  1509. #@package: TclX-stringfile_functions read_file write_file
  1510.  
  1511. proc read_file {fileName args} {
  1512.     if {$fileName == "-nonewline"} {
  1513.         set flag $fileName
  1514.         set fileName [lvarpop args]
  1515.     } else {
  1516.         set flag {}
  1517.     }
  1518.     set fp [open $fileName]
  1519.     try_eval {
  1520.         set result [eval read $flag $fp $args]
  1521.     } {} {
  1522.         close $fp
  1523.     }
  1524.     return $result
  1525.  
  1526. proc write_file {fileName args} {
  1527.     set fp [open $fileName w]
  1528.     try_eval {
  1529.         foreach string $args {
  1530.             puts $fp $string
  1531.         }
  1532.     } {} {
  1533.         close $fp
  1534.     }
  1535. }
  1536.  
  1537.  
  1538.  
  1539. #
  1540. # tcllib.tcl --
  1541. #
  1542. # Various command dealing with tlib package libraries.
  1543. #------------------------------------------------------------------------------
  1544. # Copyright 1992-1999 Karl Lehenbauer and Mark Diekhans.
  1545. #
  1546. # Permission to use, copy, modify, and distribute this software and its
  1547. # documentation for any purpose and without fee is hereby granted, provided
  1548. # that the above copyright notice appear in all copies.  Karl Lehenbauer and
  1549. # Mark Diekhans make no representations about the suitability of this
  1550. # software for any purpose.  It is provided "as is" without express or
  1551. # implied warranty.
  1552. #------------------------------------------------------------------------------
  1553. # Copyright (c) 1991-1994 The Regents of the University of California.
  1554. # All rights reserved.
  1555. #
  1556. # Permission is hereby granted, without written agreement and without
  1557. # license or royalty fees, to use, copy, modify, and distribute this
  1558. # software and its documentation for any purpose, provided that the
  1559. # above copyright notice and the following two paragraphs appear in
  1560. # all copies of this software.
  1561. #
  1562. # IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
  1563. # DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
  1564. # OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
  1565. # CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  1566. #
  1567. # THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
  1568. # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
  1569. # AND FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
  1570. # ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
  1571. # PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  1572. #------------------------------------------------------------------------------
  1573. # $Id: tcllib.tcl,v 8.3 1999/03/31 06:37:48 markd Exp $
  1574. #------------------------------------------------------------------------------
  1575. #
  1576.  
  1577. #@package: TclX-libraries searchpath auto_load_file
  1578.  
  1579. #------------------------------------------------------------------------------
  1580. # searchpath:
  1581. # Search a path list for a file. (catch is for bad ~user)
  1582. #
  1583. proc searchpath {pathlist file} {
  1584.     foreach dir $pathlist {
  1585.         if {$dir == ""} {set dir .}
  1586.         if {[catch {file exists $dir/$file} result] == 0 && $result}  {
  1587.             return $dir/$file
  1588.         }
  1589.     }
  1590.     return {}
  1591. }
  1592.  
  1593. #------------------------------------------------------------------------------
  1594. # auto_load_file:
  1595. # Search auto_path for a file and source it.
  1596. #
  1597. proc auto_load_file {name} {
  1598.     global auto_path errorCode
  1599.     if {[string first / $name] >= 0} {
  1600.         return  [uplevel 1 source $name]
  1601.     }
  1602.     set where [searchpath $auto_path $name]
  1603.     if [lempty $where] {
  1604.         error "couldn't find $name in any directory in auto_path"
  1605.     }
  1606.     uplevel 1 source $where
  1607. }
  1608.  
  1609. #@package: TclX-lib-list auto_packages auto_commands
  1610.  
  1611. #------------------------------------------------------------------------------
  1612. # auto_packages:
  1613. # List all of the loadable packages.  If -files is specified, the file paths
  1614. # of the packages is also returned.
  1615.  
  1616. proc auto_packages {{option {}}} {
  1617.     global auto_pkg_index
  1618.  
  1619.     auto_load  ;# Make sure all indexes are loaded.
  1620.     if ![info exists auto_pkg_index] {
  1621.         return {}
  1622.     }
  1623.     
  1624.     set packList [array names auto_pkg_index] 
  1625.     if [lempty $option] {
  1626.         return $packList
  1627.     }
  1628.  
  1629.     if {$option != "-files"} {
  1630.         error "Unknow option \"$option\", expected \"-files\""
  1631.     }
  1632.     set locList {}
  1633.     foreach pack $packList {
  1634.         lappend locList [list $pack [lindex $auto_pkg_index($pack) 0]]
  1635.     }
  1636.     return $locList
  1637. }
  1638.  
  1639. #------------------------------------------------------------------------------
  1640. # auto_commands:
  1641. # List all of the loadable commands.  If -loaders is specified, the commands
  1642. # that will be involked to load the commands is also returned.
  1643.  
  1644. proc auto_commands {{option {}}} {
  1645.     global auto_index
  1646.  
  1647.     auto_load  ;# Make sure all indexes are loaded.
  1648.     if ![info exists auto_index] {
  1649.         return {}
  1650.     }
  1651.     
  1652.     set cmdList [array names auto_index] 
  1653.     if [lempty $option] {
  1654.         return $cmdList
  1655.     }
  1656.  
  1657.     if {$option != "-loaders"} {
  1658.         error "Unknow option \"$option\", expected \"-loaders\""
  1659.     }
  1660.     set loadList {}
  1661.     foreach cmd $cmdList {
  1662.         lappend loadList [list $cmd $auto_index($cmd)]
  1663.     }
  1664.     return $loadList
  1665. }
  1666.  
  1667.  
  1668.  
  1669. #
  1670. # fmath.tcl --
  1671. #
  1672. #   Contains a package of procs that interface to the Tcl expr command built-in
  1673. # functions.  These procs provide compatibility with older versions of TclX and
  1674. # are also generally useful.
  1675. #------------------------------------------------------------------------------
  1676. # Copyright 1993-1999 Karl Lehenbauer and Mark Diekhans.
  1677. #
  1678. # Permission to use, copy, modify, and distribute this software and its
  1679. # documentation for any purpose and without fee is hereby granted, provided
  1680. # that the above copyright notice appear in all copies.  Karl Lehenbauer and
  1681. # Mark Diekhans make no representations about the suitability of this
  1682. # software for any purpose.  It is provided "as is" without express or
  1683. # implied warranty.
  1684. #------------------------------------------------------------------------------
  1685. # $Id: fmath.tcl,v 8.3 1999/03/31 06:37:47 markd Exp $
  1686. #------------------------------------------------------------------------------
  1687.  
  1688. #@package: TclX-fmath acos asin atan ceil cos cosh exp fabs floor log log10 \
  1689.            sin sinh sqrt tan tanh fmod pow atan2 abs double int round
  1690.  
  1691. proc acos  x {uplevel [list expr acos($x)]}
  1692. proc asin  x {uplevel [list expr asin($x)]}
  1693. proc atan  x {uplevel [list expr atan($x)]}
  1694. proc ceil  x {uplevel [list expr ceil($x)]}
  1695. proc cos   x {uplevel [list expr cos($x)]}
  1696. proc cosh  x {uplevel [list expr cosh($x)]}
  1697. proc exp   x {uplevel [list expr exp($x)]}
  1698. proc fabs  x {uplevel [list expr abs($x)]}
  1699. proc floor x {uplevel [list expr floor($x)]}
  1700. proc log   x {uplevel [list expr log($x)]}
  1701. proc log10 x {uplevel [list expr log10($x)]}
  1702. proc sin   x {uplevel [list expr sin($x)]}
  1703. proc sinh  x {uplevel [list expr sinh($x)]}
  1704. proc sqrt  x {uplevel [list expr sqrt($x)]}
  1705. proc tan   x {uplevel [list expr tan($x)]}
  1706. proc tanh  x {uplevel [list expr tanh($x)]}
  1707.  
  1708. proc fmod {x n} {uplevel [list expr fmod($x,$n)]}
  1709. proc pow {x n} {uplevel [list expr pow($x,$n)]}
  1710.  
  1711. # New functions that TclX did not provide in eariler versions.
  1712.  
  1713. proc atan2  x {uplevel [list expr atan2($x)]}
  1714. proc abs    x {uplevel [list expr abs($x)]}
  1715. proc double x {uplevel [list expr double($x)]}
  1716. proc int    x {uplevel [list expr int($x)]}
  1717. proc round  x {uplevel [list expr round($x)]}
  1718.  
  1719.  
  1720.  
  1721. #
  1722. # buildhelp.tcl --
  1723. #
  1724. # Program to extract help files from TCL manual pages or TCL script files.
  1725. # The help directories are built as a hierarchical tree of subjects and help
  1726. # files.  
  1727. #------------------------------------------------------------------------------
  1728. # Copyright 1992-1999 Karl Lehenbauer and Mark Diekhans.
  1729. #
  1730. # Permission to use, copy, modify, and distribute this software and its
  1731. # documentation for any purpose and without fee is hereby granted, provided
  1732. # that the above copyright notice appear in all copies.  Karl Lehenbauer and
  1733. # Mark Diekhans make no representations about the suitability of this
  1734. # software for any purpose.  It is provided "as is" without express or
  1735. # implied warranty.
  1736. #------------------------------------------------------------------------------
  1737. # $Id: buildhelp.tcl,v 8.3 1999/03/31 06:37:47 markd Exp $
  1738. #------------------------------------------------------------------------------
  1739. #
  1740. # For nroff man pages, the areas of text to extract are delimited with:
  1741. #
  1742. #     '\"@help: subjectdir/helpfile
  1743. #     '\"@endhelp
  1744. #
  1745. # start in column one. The text between these markers is extracted and stored
  1746. # in help/subjectdir/help.  The file must not exists, this is done to enforced 
  1747. # cleaning out the directories before help file generation is started, thus
  1748. # removing any stale files.  The extracted text is run through:
  1749. #
  1750. #     nroff -man|col -xb   {col -b on BSD derived systems}
  1751. #
  1752. # If there is other text to include in the helpfile, but not in the manual 
  1753. # page, the text, along with nroff formatting commands, may be included using:
  1754. #
  1755. #     '\"@:Other text to include in the help page.
  1756. #
  1757. # A entry in the brief file, used by apropos my be included by:
  1758. #
  1759. #     '\"@brief: Short, one line description
  1760. #
  1761. # These brief request must occur with in the bounds of a help section.
  1762. #
  1763. # If some header text, such as nroff macros, need to be preappended to the
  1764. # text streem before it is run through nroff, then that text can be bracketed
  1765. # with:
  1766. #
  1767. #     '\"@header
  1768. #     '\"@endheader
  1769. #
  1770. # If multiple header blocks are encountered, they will all be preappended.
  1771. #
  1772. # For TCL script files, which are indentified because they end in ".tcl",
  1773. # the text to be extracted is delimited by:
  1774. #
  1775. #    #@help: subjectdir/helpfile
  1776. #    #@endhelp
  1777. #
  1778. # And brief lines are in the form:
  1779. #
  1780. #     #@brief: Short, one line description
  1781. #
  1782. # The only processing done on text extracted from .tcl files it to replace
  1783. # the # in column one with a space.
  1784. #
  1785. #
  1786. #-----------------------------------------------------------------------------
  1787. # To generate help:
  1788. #
  1789. #   buildhelp helpDir brief.brf filelist
  1790. #
  1791. # o helpDir is the help tree root directory.  helpDir should  exists, but any
  1792. #   subdirectories that don't exists will be created.  helpDir should be
  1793. #   cleaned up before the start of manual page generation, as this program
  1794. #   will not overwrite existing files.
  1795. # o brief.brf  is the name of the brief file to create form the @brief entries.
  1796. #   It must have an extension of ".brf".  It will be created in helpDir.
  1797. # o filelist are the nroff manual pages, or .tcl, .tlib files to extract
  1798. #   the help files from. If the suffix is not .tcl or .tlib, a nroff manual
  1799. #   page is assumed.
  1800. #
  1801. #-----------------------------------------------------------------------------
  1802.  
  1803. #@package: TclX-buildhelp buildhelp
  1804.  
  1805. #-----------------------------------------------------------------------------
  1806. # Truncate a file name of a help file if the system does not support long
  1807. # file names.  If the name starts with `Tcl_', then this prefix is removed.
  1808. # If the name is then over 14 characters, it is truncated to 14 charactes
  1809. #  
  1810. proc TruncFileName {pathName} {
  1811.     global truncFileNames
  1812.  
  1813.     if {!$truncFileNames} {
  1814.         return $pathName}
  1815.     set fileName [file tail $pathName]
  1816.     if {"[crange $fileName 0 3]" == "Tcl_"} {
  1817.         set fileName [crange $fileName 4 end]}
  1818.     set fileName [crange $fileName 0 13]
  1819.     return "[file dirname $pathName]/$fileName"
  1820. }
  1821.  
  1822. #-----------------------------------------------------------------------------
  1823. # Proc to ensure that all directories for the specified file path exists,
  1824. # and if they don't create them.  Don't use -path so we can set the
  1825. # permissions.
  1826.  
  1827. proc EnsureDirs {filePath} {
  1828.     set dirPath [file dirname $filePath]
  1829.     if [file exists $dirPath] return
  1830.     foreach dir [split $dirPath /] {
  1831.         lappend dirList $dir
  1832.         set partPath [join $dirList /]
  1833.         if [file exists $partPath] continue
  1834.  
  1835.         mkdir $partPath
  1836.         chmod u=rwx,go=rx $partPath
  1837.     }
  1838. }
  1839.  
  1840. #-----------------------------------------------------------------------------
  1841. # Proc to set up scan context for use by FilterNroffManPage.
  1842. # This keeps the a two line cache of the previous two lines encountered
  1843. # and the blank lines that followed them.
  1844. #
  1845.  
  1846. proc CreateFilterNroffManPageContext {} {
  1847.     global filterNroffManPageContext
  1848.  
  1849.     set filterNroffManPageContext [scancontext create]
  1850.  
  1851.     # On finding a page header, drop the previous line (which is
  1852.     # the page footer). Also deleting the blank lines followin
  1853.     # the last line on the previous page.
  1854.  
  1855.     scanmatch $filterNroffManPageContext {@@@BUILDHELP@@@} {
  1856.         catch {unset prev2Blanks}
  1857.         catch {unset prev1Line}
  1858.         catch {unset prev1Blanks}
  1859.         set nukeBlanks {}
  1860.     }
  1861.  
  1862.     # Save blank lines
  1863.  
  1864.     scanmatch $filterNroffManPageContext {$^} {
  1865.         if ![info exists nukeBlanks] {
  1866.             append prev1Blanks \n
  1867.         }
  1868.     }
  1869.  
  1870.     # Non-blank line, save it.  Output the 2nd previous line if necessary.
  1871.  
  1872.     scanmatch $filterNroffManPageContext {
  1873.         catch {unset nukeBlanks}
  1874.         if [info exists prev2Line] {
  1875.             puts $outFH $prev2Line
  1876.             unset prev2Line
  1877.         }
  1878.         if [info exists prev2Blanks] {
  1879.             puts $outFH $prev2Blanks nonewline
  1880.             unset prev2Blanks
  1881.         }
  1882.         if [info exists prev1Line] {
  1883.             set prev2Line $prev1Line
  1884.         }
  1885.         set prev1Line $matchInfo(line)
  1886.         if [info exists prev1Blanks] {
  1887.             set prev2Blanks $prev1Blanks
  1888.             unset prev1Blanks
  1889.         }
  1890.     }
  1891. }
  1892.  
  1893. #-----------------------------------------------------------------------------
  1894. # Proc to filter a formatted manual page, removing the page headers and
  1895. # footers.  This relies on each manual page having a .TH macro in the form:
  1896. #   .TH @@@BUILDHELP@@@ n
  1897.  
  1898. proc FilterNroffManPage {inFH outFH} {
  1899.     global filterNroffManPageContext
  1900.  
  1901.     if ![info exists filterNroffManPageContext] {
  1902.         CreateFilterNroffManPageContext
  1903.     }
  1904.  
  1905.     scanfile $filterNroffManPageContext $inFH
  1906.  
  1907.     if [info exists prev2Line] {
  1908.         puts $outFH $prev2Line
  1909.     }
  1910. }
  1911.  
  1912. #-----------------------------------------------------------------------------
  1913. # Proc to set up scan context for use by ExtractNroffHeader
  1914. #
  1915.  
  1916. proc CreateExtractNroffHeaderContext {} {
  1917.     global extractNroffHeaderContext
  1918.  
  1919.     set extractNroffHeaderContext [scancontext create]
  1920.  
  1921.     scanmatch $extractNroffHeaderContext {'\\"@endheader[     ]*$} {
  1922.         break
  1923.     }
  1924.     scanmatch $extractNroffHeaderContext {'\\"@:} {
  1925.         append nroffHeader "[crange $matchInfo(line) 5 end]\n"
  1926.     }
  1927.     scanmatch $extractNroffHeaderContext {
  1928.         append nroffHeader "$matchInfo(line)\n"
  1929.     }
  1930. }
  1931.  
  1932. #-----------------------------------------------------------------------------
  1933. # Proc to extract nroff text to use as a header to all pass to nroff when
  1934. # processing a help file.
  1935. #    manPageFH - The file handle of the manual page.
  1936. #
  1937.  
  1938. proc ExtractNroffHeader {manPageFH} {
  1939.     global extractNroffHeaderContext nroffHeader
  1940.  
  1941.     if ![info exists extractNroffHeaderContext] {
  1942.         CreateExtractNroffHeaderContext
  1943.     }
  1944.     scanfile $extractNroffHeaderContext $manPageFH
  1945. }
  1946.  
  1947.  
  1948. #-----------------------------------------------------------------------------
  1949. # Proc to set up scan context for use by ExtractNroffHelp
  1950. #
  1951.  
  1952. proc CreateExtractNroffHelpContext {} {
  1953.     global extractNroffHelpContext
  1954.  
  1955.     set extractNroffHelpContext [scancontext create]
  1956.  
  1957.     scanmatch $extractNroffHelpContext {^'\\"@endhelp[     ]*$} {
  1958.         break
  1959.     }
  1960.  
  1961.     scanmatch $extractNroffHelpContext {^'\\"@brief:} {
  1962.         if $foundBrief {
  1963.             error {Duplicate "@brief:" entry}
  1964.         }
  1965.         set foundBrief 1
  1966.         puts $briefHelpFH "$helpName\t[csubstr $matchInfo(line) 11 end]"
  1967.         continue
  1968.     }
  1969.  
  1970.     scanmatch $extractNroffHelpContext {^'\\"@:} {
  1971.         puts $nroffFH  [csubstr $matchInfo(line) 5 end]
  1972.         continue
  1973.     }
  1974.     scanmatch $extractNroffHelpContext {^'\\"@help:} {
  1975.         error {"@help" found within another help section"}
  1976.     }
  1977.     scanmatch $extractNroffHelpContext {
  1978.         puts $nroffFH $matchInfo(line)
  1979.     }
  1980. }
  1981.  
  1982. #-----------------------------------------------------------------------------
  1983. # Proc to extract a nroff help file when it is located in the text.
  1984. #    manPageFH - The file handle of the manual page.
  1985. #    manLine - The '\"@help: line starting the data to extract.
  1986. #
  1987.  
  1988. proc ExtractNroffHelp {manPageFH manLine} {
  1989.     global helpDir nroffHeader briefHelpFH colArgs
  1990.     global extractNroffHelpContext
  1991.  
  1992.     if ![info exists extractNroffHelpContext] {
  1993.         CreateExtractNroffHelpContext
  1994.     }
  1995.  
  1996.     set helpName [string trim [csubstr $manLine 9 end]]
  1997.     set helpFile [TruncFileName "$helpDir/$helpName"]
  1998.     if [file exists $helpFile] {
  1999.         error "Help file already exists: $helpFile"
  2000.     }
  2001.     EnsureDirs $helpFile
  2002.  
  2003.     set tmpFile "[file dirname $helpFile]/tmp.[id process]"
  2004.  
  2005.     echo "    creating help file $helpName"
  2006.  
  2007.     set nroffFH [open "| nroff -man | col $colArgs > $tmpFile" w]
  2008.  
  2009.     puts $nroffFH {.TH @@@BUILDHELP@@@ 1}
  2010.  
  2011.     set foundBrief 0
  2012.     scanfile $extractNroffHelpContext $manPageFH
  2013.  
  2014.     # Close returns an error on if anything comes back on stderr, even if
  2015.     # its a warning.  Output errors and continue.
  2016.  
  2017.     set stat [catch {
  2018.         close $nroffFH
  2019.     } msg]
  2020.     if $stat {
  2021.         puts stderr "nroff: $msg"
  2022.     }
  2023.  
  2024.     set tmpFH [open $tmpFile r]
  2025.     set helpFH [open $helpFile w]
  2026.  
  2027.     FilterNroffManPage $tmpFH $helpFH
  2028.  
  2029.     close $tmpFH
  2030.     close $helpFH
  2031.  
  2032.     unlink $tmpFile
  2033.     chmod a-w,a+r $helpFile
  2034. }
  2035.  
  2036. #-----------------------------------------------------------------------------
  2037. # Proc to set up scan context for use by ExtractScriptHelp
  2038. #
  2039.  
  2040. proc CreateExtractScriptHelpContext {} {
  2041.     global extractScriptHelpContext
  2042.  
  2043.     set extractScriptHelpContext [scancontext create]
  2044.  
  2045.     scanmatch $extractScriptHelpContext {^#@endhelp[     ]*$} {
  2046.         break
  2047.     }
  2048.  
  2049.     scanmatch $extractScriptHelpContext {^#@brief:} {
  2050.         if $foundBrief {
  2051.             error {Duplicate "@brief" entry}
  2052.         }
  2053.         set foundBrief 1
  2054.         puts $briefHelpFH "$helpName\t[csubstr $matchInfo(line) 9 end]"
  2055.         continue
  2056.     }
  2057.  
  2058.     scanmatch $extractScriptHelpContext {^#@help:} {
  2059.         error {"@help" found within another help section"}
  2060.     }
  2061.  
  2062.     scanmatch $extractScriptHelpContext {^#$} {
  2063.         puts $helpFH ""
  2064.     }
  2065.  
  2066.     scanmatch $extractScriptHelpContext {
  2067.         if {[clength $matchInfo(line)] > 1} {
  2068.             puts $helpFH " [csubstr $matchInfo(line) 1 end]"
  2069.         } else {
  2070.             puts $helpFH $matchInfo(line)
  2071.         }
  2072.     }
  2073. }
  2074.  
  2075. #-----------------------------------------------------------------------------
  2076. # Proc to extract a tcl script help file when it is located in the text.
  2077. #    ScriptPageFH - The file handle of the .tcl file.
  2078. #    ScriptLine - The #@help: line starting the data to extract.
  2079. #
  2080.  
  2081. proc ExtractScriptHelp {scriptPageFH scriptLine} {
  2082.     global helpDir briefHelpFH
  2083.     global extractScriptHelpContext
  2084.  
  2085.     if ![info exists extractScriptHelpContext] {
  2086.         CreateExtractScriptHelpContext
  2087.     }
  2088.  
  2089.     set helpName [string trim [csubstr $scriptLine 7 end]]
  2090.     set helpFile "$helpDir/$helpName"
  2091.     if {[file exists $helpFile]} {
  2092.         error "Help file already exists: $helpFile"
  2093.     }
  2094.     EnsureDirs $helpFile
  2095.  
  2096.     echo "    creating help file $helpName"
  2097.  
  2098.     set helpFH [open $helpFile w]
  2099.  
  2100.     set foundBrief 0
  2101.     scanfile $extractScriptHelpContext $scriptPageFH
  2102.  
  2103.     close $helpFH
  2104.     chmod a-w,a+r $helpFile
  2105. }
  2106.  
  2107. #-----------------------------------------------------------------------------
  2108. # Proc to scan a nroff manual file looking for the start of a help text
  2109. # sections and extracting those sections.
  2110. #    pathName - Full path name of file to extract documentation from.
  2111. #
  2112.  
  2113. proc ProcessNroffFile {pathName} {
  2114.    global nroffScanCT scriptScanCT nroffHeader
  2115.  
  2116.    set fileName [file tail $pathName]
  2117.  
  2118.    set nroffHeader {}
  2119.    set manPageFH [open $pathName r]
  2120.    set matchInfo(fileName) [file tail $pathName]
  2121.  
  2122.    echo "    scanning $pathName"
  2123.  
  2124.    scanfile $nroffScanCT $manPageFH
  2125.  
  2126.    close $manPageFH
  2127. }
  2128.  
  2129. #-----------------------------------------------------------------------------
  2130. # Proc to scan a Tcl script file looking for the start of a
  2131. # help text sections and extracting those sections.
  2132. #    pathName - Full path name of file to extract documentation from.
  2133. #
  2134.  
  2135. proc ProcessTclScript {pathName} {
  2136.    global scriptScanCT nroffHeader
  2137.  
  2138.    set scriptFH [open "$pathName" r]
  2139.    set matchInfo(fileName) [file tail $pathName]
  2140.  
  2141.    echo "    scanning $pathName"
  2142.    scanfile $scriptScanCT $scriptFH
  2143.  
  2144.    close $scriptFH
  2145. }
  2146.  
  2147. #-----------------------------------------------------------------------------
  2148. # build: main procedure.  Generates help from specified files.
  2149. #    helpDirPath - Directory were the help files go.
  2150. #    briefFile - The name of the brief file to create.
  2151. #    sourceFiles - List of files to extract help files from.
  2152.  
  2153. proc buildhelp {helpDirPath briefFile sourceFiles} {
  2154.     global helpDir truncFileNames nroffScanCT
  2155.     global scriptScanCT briefHelpFH colArgs
  2156.  
  2157.     echo ""
  2158.     echo "Begin building help tree"
  2159.  
  2160.     # Determine version of col command to use (no -x on BSD)
  2161.     if {[system {col -bx </dev/null >/dev/null 2>&1}] != 0} {
  2162.         set colArgs {-b}
  2163.     } else {
  2164.         set colArgs {-bx}
  2165.     }
  2166.     set helpDir $helpDirPath
  2167.     if {![file exists $helpDir]} {
  2168.         mkdir $helpDir
  2169.     }
  2170.  
  2171.     if {![file isdirectory $helpDir]} {
  2172.         error [concat "$helpDir is not a directory or does not exist. "  
  2173.                       "This should be the help root directory"]
  2174.     }
  2175.         
  2176.     set status [catch {set tmpFH [open $helpDir/AVeryVeryBigFileName w]}]
  2177.     if {$status != 0} {
  2178.         set truncFileNames 1
  2179.     } else {
  2180.         close $tmpFH
  2181.         unlink $helpDir/AVeryVeryBigFileName
  2182.         set truncFileNames 0
  2183.     }
  2184.  
  2185.     set nroffScanCT [scancontext create]
  2186.  
  2187.     scanmatch $nroffScanCT {'\\"@help:} {
  2188.         ExtractNroffHelp $matchInfo(handle) $matchInfo(line)
  2189.         continue
  2190.     }
  2191.  
  2192.     scanmatch $nroffScanCT {^'\\"@header} {
  2193.         ExtractNroffHeader $matchInfo(handle)
  2194.         continue
  2195.     }
  2196.     scanmatch $nroffScanCT {^'\\"@endhelp} {
  2197.         error [concat {@endhelp" without corresponding "@help:"} \
  2198.                  ", offset = $matchInfo(offset)"]
  2199.     }
  2200.     scanmatch $nroffScanCT {^'\\"@brief} {
  2201.         error [concat {"@brief" without corresponding "@help:"} \
  2202.                  ", offset = $matchInfo(offset)"]
  2203.     }
  2204.  
  2205.     set scriptScanCT [scancontext create]
  2206.     scanmatch $scriptScanCT {^#@help:} {
  2207.         ExtractScriptHelp $matchInfo(handle) $matchInfo(line)
  2208.     }
  2209.  
  2210.     if {[file extension $briefFile] != ".brf"} {
  2211.         error "Brief file \"$briefFile\" must have an extension \".brf\""
  2212.     }
  2213.     if [file exists $helpDir/$briefFile] {
  2214.         error "Brief file \"$helpDir/$briefFile\" already exists"
  2215.     }
  2216.     set briefHelpFH [open "|sort > $helpDir/$briefFile" w]
  2217.  
  2218.     foreach manFile [glob $sourceFiles] {
  2219.         set ext [file extension $manFile]
  2220.         if {$ext == ".tcl" || $ext == ".tlib"} {
  2221.             set status [catch {ProcessTclScript $manFile} msg]
  2222.         } else {
  2223.             set status [catch {ProcessNroffFile $manFile} msg]
  2224.         }
  2225.         if {$status != 0} {
  2226.             global errorInfo errorCode
  2227.             error "Error extracting help from: $manFile" $errorInfo $errorCode
  2228.         }
  2229.     }
  2230.  
  2231.     close $briefHelpFH
  2232.     chmod a-w,a+r $helpDir/$briefFile
  2233.     echo "Completed extraction of help files"
  2234. }
  2235.  
  2236.  
  2237.  
  2238.