home *** CD-ROM | disk | FTP | other *** search
/ Internet File Formats / InternetFileFormatsCD.bin / text / latex / mac / alpha60.hqx / Tcl / SystemCode / MacPerl.tcl < prev    next >
Encoding:
Text File  |  1995-07-16  |  49.4 KB  |  1,706 lines

  1. #############################################################################
  2. # MacPerl.tcl
  3. # -----------
  4. #
  5. # This is a set of routines that allow Alpha to act as a front end for the
  6. # standalone MacPerl application and that allow Perl scripts to be used as 
  7. # text filters in Alpha.  These functions are accessed through a special 
  8. # MacPerl menu.
  9. #
  10. # The features of this package are explained in the file "MacPerl Help",
  11. # accessible from the Help menu.
  12. #
  13. #############################################################################
  14. #
  15. # If you don't already have MacPerl, it's available by anonymous ftp from
  16. # the umich site
  17. #
  18. #   mac.archive.umich.edu    [141.211.165.34]    mac/development/languages
  19. #
  20. # and its mirrors.  Also, MacPerl's home site is 
  21. #
  22. #   ftp.switch.ch            [130.59.1.40]        software/mac/src/mpw_c
  23. #
  24. # MacPerl was written (ported to the Mac) by 
  25. #        Matthias Neeracher <neeri@iis.ee.ethz.ch> , and
  26. #        Tim Endres <time@ice.com>.
  27. #
  28. #############################################################################
  29. # Author: W. Thomas Pollard <pollard@chem.columbia.edu>
  30. #
  31. # Version History:
  32. #
  33. # 2.3  7/95  -  Minor tweaks and code rearrangement.
  34. # 2.2  6/95  -  Text filters act only on current line if "Apply to Buffer" is
  35. #                  false and no text has been selected.
  36. #               Bug fix in error-marking for scripts sent as AppleEvent params.
  37. #               Cmd-dbl-clicking a function call jumps to function, if
  38. #                  defined in the same file.
  39. # 2.1  6/95  -  Cmd-dbl-clicking a 'require'd filename opens the file.
  40. # 2.0  6/95  -  Minor bug fixes (incl. keyword decapitalization)
  41. #               Alpha 6.0b17 compatibility updates.
  42. #               Text Filters folder is settable from the App Paths menu now.
  43. # 1.9  5/95  -  Cmd-dbl-clicking Perl keywords and special variables displays
  44. #                  the man page info.
  45. # 1.81 4/95  -  one very minor Alpha compatibility update (winInfo->getWinInfo).
  46. # 1.8  4/95  -  Menu reorganized somewhat.
  47. #               Text Filters folder can now be anywhere.
  48. #               "ApplyToBuffer" flag ignored if text has been selected.
  49. #               Bug fixes.
  50. # 1.7  1/95  -  Updated to take advantage of MacPerl 4.1.4 AppleEvent features:
  51. #                1) Text filters use 'batch' doScript (.: STDOUT file obsolete)
  52. #                2) Filter scripts sent as doScript params (.: SCRIPT file obsolete)
  53. #                3) "Save As Droplet" and "Save as Runtime" commands added.
  54. #               Errors generated in 'require'd files are now displayed correctly
  55. # 1.6 10/94  -  "UseDebugger" flag added (forces scripts to run under debugger).
  56. #               Key bindings added for some menu commands.
  57. #               "perlDoScript{,2,3}" procs consolidated into a single proc.
  58. #               "saveAndRun" option added.
  59. #               Command-line args now parsed into units more correctly, in
  60. #                   particular, quoted file names aren't broken up.
  61. #               "Close Output Window" added to "Tell MacPerl" menu.
  62. #               Updated for Alpha 5.98 to load when menu is inserted.
  63. #               The error messages window is now recycled.
  64. #               "perlRecycleOutput" recycles output window.
  65. #               Minor bug fixes.
  66. # 1.5  9/94  -  MacPerl menu rearranged somewhat.
  67. #               Explicit "Get Output Window" command added to menu.
  68. #               Reading "#!" line for args is incompatible w/ standard,
  69. #                   so it's been dropped.
  70. #               Only scan the first 40 output lines for error messages (faster)
  71. #                "wrapFilterScript" no longer opens STDIN
  72. #               Text filters may now use command-line args
  73. #               STDIN for text filters passed as explicit cmd-line arg 
  74. # 1.4  9/94  -  The "#!" line of every script is read for command-line args,
  75. #                    which are passed explicitly to MacPerl with the script.
  76. #                "PromptForArgs" menu flag added.
  77. #                "perlCmdlineArgs" modeVar holds default command-line args.
  78. #                Scripts are sent using custom "perlDoScript2" proc, which
  79. #                    allows passing of explicit command-line args.
  80. # 1.3  9/94  -  When any script generates a compilation error, the file 
  81. #                    containing the script is brought up with the offending 
  82. #                    line highlighted; all error output is also written to
  83. #                    a "Perl Error Messages" window.
  84. #                'repeatLastFilter' runs again the last text-filter script used.
  85. #                'perlLastFilter' modeVar holds pathname of last filter.
  86. #                Menu flags now mirrored as modeVars, so they can be saved and
  87. #                    restored between sessions.
  88. #                Minor bug fixes.
  89. # 1.2  8/94  -  'retrieveOutput' and 'autoSwitch' flags added.
  90. #                'openInMacperl' added.
  91. #                MacPerl output window now closed before new scripts are sent.
  92. #                Filters now abort if there are compilation errors, and
  93. #                MacPerl diagnostic output retrieved and displayed in Alpha.
  94. # 1.1  8/94  -  'quitMacperl' added.
  95. #               perl-mode file-marking updated for Alpha 5.90
  96. #               Simplified installation via 'loadMacperl'(Pete Keleher). 
  97. # 1.0  7/94  -  perl-mode setup updated for Alpha 5.85:
  98. #                    keyword colorization supported
  99. #                    custom file-marking added
  100. #               #! lines in filter scripts now handled correctly 
  101. #               Workarounds installed for AppleEvent bug in MacPerl 4.1.3
  102. # 0.9  3/94  -  perl-mode stuff added, and
  103. #               highlighted 'Perl commands' file (man page) prepared
  104. #               minor bug fixes, too
  105. # 0.8  3/94  -  flags are now check-marked
  106. # 0.7  3/94  -  nested Text Filters folder now supported
  107. #               menu format modified somewhat
  108. # 0.6  3/94  -  'applyToBuffer' flag added
  109. #               scripts in Alpha buffers can now be used as filters 
  110. # 0.5  2/94  -  'filters', 'open special' submenu added
  111. #               'overwrite' flag added
  112. # 0.2  1/94  -  menu support added (Martijn Koster <m.koster@nexor.co.uk>)
  113. #               'execute selection', 'execute buffer' commands added
  114. # 0.1  9/93  -  text filter functionality created
  115. #                  
  116. ##############################################################################
  117. #
  118. proc dummyPerl {} {
  119. }
  120.  
  121. #############################################################################
  122. #  Default settings for the Perl menu flags  
  123. #
  124. set perlDefault(perlUseDebug) 0
  125. set perlDefault(perlGetOutput) 1
  126. set perlDefault(perlAutoSwitch) 1
  127. set perlDefault(perlOverwrite) 0
  128. set perlDefault(perlUsebuffer) 1
  129. set perlDefault(perlPromptArgs) 0
  130. set perlDefault(perlRecycleOutput) 0
  131. set perlDefault(perlPrevScript) {*startup*}
  132. set perlDefault(perlCmdlineArgs) {}
  133.  
  134. if {![info exists perlFilterPath]} {
  135.     set perlFilterPath "$HOME:Tcl:UserCode:Text Filters:"
  136. }
  137.  
  138. foreach var [array names perlDefault] {
  139.     if (![info exists PerlmodeVars($var)]) { 
  140.         set $var $perlDefault($var) 
  141.     } else {
  142.         set $var $PerlmodeVars($var) 
  143.     }
  144. }
  145. unset perlDefault
  146.  
  147. ##############################################################################
  148. # Make duplicate copies of these variables as modeVars, taking responsibility
  149. # for keeping the two sets consistent (argh!)
  150. #
  151. # (Maybe it's OK now to let them _just_ be modeVars, and not also ordinary
  152. # variables?)
  153. #
  154.  
  155. newModeVar Perl perlUseDebug $perlUseDebug 1
  156. newModeVar Perl perlGetOutput $perlGetOutput 1
  157. newModeVar Perl perlAutoSwitch $perlAutoSwitch 1
  158. newModeVar Perl perlOverwrite $perlOverwrite 1
  159. newModeVar Perl perlUsebuffer $perlUsebuffer 1
  160. newModeVar Perl perlPromptArgs $perlPromptArgs 1
  161. newModeVar Perl perlRecycleOutput $perlRecycleOutput 1
  162.  
  163. newModeVar Perl perlLastFilter $perlPrevScript 0
  164. newModeVar Perl perlCmdlineArgs $perlCmdlineArgs 0
  165.  
  166. ##############################################################################
  167. # Other Perl-mode variable definitions
  168. #
  169. newModeVar Perl elecRBrace    {0} 1
  170. newModeVar Perl elecLBrace    {1} 1
  171. newModeVar Perl electricSemi    {0} 1
  172. newModeVar Perl electricTab    {1} 1
  173. newModeVar Perl wordBreak        {(\$)?[a-zA-Z0-9_]+} 0
  174. newModeVar Perl prefixString    {# } 0
  175. newModeVar Perl wordWrap        {0} 1
  176. newModeVar Perl funcExpr        {^sub *([+-a-zA-Z0-9]+)} 0
  177. newModeVar Perl wordBreakPreface        {[^a-zA-Z0-9_\$]} 0
  178. newModeVar Perl optionIsMeta    {1} 1
  179. newModeVar Perl autoMark    1    1
  180.  
  181. ##############################################################################
  182. # Miscellaneous definitions
  183. #
  184. set perlErrorWindow {* Perl Error Messages *}
  185. set perlOutputWindow {* Perl Output *}
  186.  
  187. set perlFilterMenu "textFilters"
  188.  
  189.  
  190. ##############################################################################
  191. # Colorization setup
  192. #
  193. # Keywords are separated here according to their location in "Perl Commands",
  194. # for the convenience of the cmd-double-click mechanism.
  195. #
  196. # Expression words are described in the "Compound Statements" section
  197. #
  198. set perlExprWords {  
  199. else elsif for foreach if return unless until while eq ne cmp lt gt le ge
  200. }
  201.  
  202. # Special variables are described in their own section (and are not 
  203. # individually marked, so we have to search for them.)
  204. #
  205. # This group can safely be colorized...
  206. #
  207. set perlNameWords {
  208. @_ $_ $.  $/ $, $" $\\ $\# $% $= $- $~ $^ $| $$ $? $& $` $' $+ $* 
  209. $0 $1 $2 $3 $4 $5 $6 $7 $8 $9 $[ $] $; $! $@ $< $> $( $) $:
  210. }
  211.  
  212. #... while this group is forced lower-case by the current colorization scheme
  213. #
  214. set perlSpecialVars [concat $perlNameWords {
  215. $^D $^F $^I $^P $^T $^W $^X 
  216. $ARGV @ARGV @INC %INC @INC %ENV $SIG $ENV %SIG
  217. }]
  218.  
  219. # Perl operators and functions are indexed via the Marks menu
  220. #
  221. set perlKeyWords {
  222.     accept alarm atan2 bind binmode caller chdir chmod chop chown chroot 
  223.     close closedir connect continue cos crypt dbmclose dbmopen defined 
  224.     delete die do dump each   eof eval exec exit exp fcntl fileno 
  225.     flock  fork getc getlogin getpeername getpgrp getppid 
  226.     getpriority getgrnam gethostbyname getnetbyname getprotobyname getpwuid 
  227.     getgrgid getservbyname gethostbyaddr getnetbyaddr getprotobynumber 
  228.     getservbyport getpwent getgrent gethostent getnetent getprotoent 
  229.     getservent setpwent setgrent sethostent setnetent setprotoent setservent 
  230.     endpwent endgrent endhostent endnetent endprotoent endservent 
  231.     getsockname getsockopt gmtime goto grep hex  index int ioctl join keys 
  232.     kill last  length link listen local localtime log lstat lstat mkdir 
  233.     msgctl msgget msgsnd msgrcv next oct open opendir ord pack pipe pop 
  234.     print  printf  push q qq qx rand read readdir readlink recv redo
  235.      rename require reset  reverse rewinddir rindex rindex rmdir 
  236.     scalar seek seekdir select semctl semget semop send setpgrp setpriority 
  237.     setsockopt shift shmctl shmget shmread shmwrite shutdown sin sleep 
  238.     socket socketpair sort splice split sprintf sqrt srand stat study sub 
  239.     substr symlink syscall sysread system syswrite tell telldir time times 
  240.     tr truncate umask undef  unlink unpack unshift  utime values 
  241.     vec wait waitpid wantarray warn  write 
  242. }
  243. set perlWords [concat $perlKeyWords $perlNameWords $perlExprWords]
  244. regModeKeywords -e {#} -c red -k blue Perl $perlWords
  245. unset perlWords
  246.  
  247. #############################################################################
  248. #  Return paths to standard files, based on the path to MacPerl:
  249. #
  250. proc macperlFolder {} {
  251.    global macperlPath
  252.    regexp {(.*):([^:]*)} $macperlPath pathname dirname filename
  253.    return ${dirname}:
  254. }
  255.  
  256. proc stdinPath {} {
  257.    return [macperlFolder]STDIN
  258. }
  259.  
  260. proc scriptPath {} {
  261.    return [macperlFolder]SCRIPT
  262. }
  263.  
  264. #############################################################################
  265. # Define the dummy proc that will be called when the perl menu
  266. # is first inserted into the menubar
  267. #
  268. proc perlMenu {} {}
  269.  
  270. #############################################################################
  271. #  Build the perl menu
  272. #            
  273. set perlMenu "Ñ132"
  274. set perlOptsMenu "generalOptions"
  275. set filtOptsMenu "filterOptions"
  276.  
  277. menu -n $perlMenu [ concat {
  278.         "/'<Umacperl"
  279.         {menu -m -n "tellMacperl..." -p perlTellProc {
  280.            "/O<UOpen This File"
  281.            "Save As Droplet"
  282.            "Save As Runtime"
  283.            "Save As CGI"
  284.             "(-"
  285.            "Get Output Window"
  286.            "Close Output Window"
  287.            "Quit"
  288.            }
  289.         } 
  290.         "(-"
  291.         "runTheSelection"
  292.         "/R<UrunTheBuffer"
  293.         "/R<B<OsaveAndRun"
  294.         "runAFile"
  295.         "(-"
  296.     } [list [list menu -n $perlFilterMenu {}]] {
  297.        "selectBufferAsFilter"
  298.        "selectFileAsFilter"
  299.        "/F<UrepeatLastFilter"
  300.        "(-" 
  301.     } [list [list menu -n $perlOptsMenu {}]] {
  302.     } [list [list menu -n $filtOptsMenu {}]] {
  303.     } ]
  304.  
  305. enableMenuItem $perlMenu perlDebugWindow 0
  306. enableMenuItem "tellMacperl..." "Save As CGI" 0
  307.  
  308. if {$perlPrevScript == {} || $perlPrevScript == {*startup*}} {
  309.     enableMenuItem $perlMenu repeatLastFilter 0
  310. }
  311.  
  312. # General Perl-menu options menu
  313. #
  314. menu -n $perlOptsMenu {
  315.     "retrieveOutput"
  316.     "autoSwitch"
  317.     "promptForArgs"
  318.     "useDebugger"
  319.     }    
  320. markMenuItem $perlOptsMenu useDebugger $perlUseDebug
  321. markMenuItem $perlOptsMenu retrieveOutput $perlGetOutput
  322. markMenuItem $perlOptsMenu autoSwitch $perlAutoSwitch
  323. markMenuItem $perlOptsMenu promptForArgs $perlPromptArgs
  324.  
  325. # Text Filter options menu
  326. #
  327. menu -n $filtOptsMenu {
  328.     "applyToBuffer"
  329.     "overwriteSelection"
  330.     "(-"
  331.     "textFiltersFolder"
  332.     "rebuildFilterMenu"
  333.     }    
  334. markMenuItem $filtOptsMenu overwriteSelection $perlOverwrite
  335. markMenuItem $filtOptsMenu applyToBuffer $perlUsebuffer
  336.  
  337. # if ([info exists macperlPath]) {
  338. #     rebuildPerlMenu
  339. # }
  340.  
  341. #############################################################################
  342. #  Build a submenu of "preattached" Perl filters using the names of the 
  343. #  scripts in the Text Filters directory.  Called whenever Text Filters
  344. # folder is reassigned.
  345. #
  346. proc rebuildFilterMenu {{args}} {
  347.     global perlFilters perlFilterMenu perlFilterPath
  348.     global $perlFilterMenu
  349.     
  350.     eval [buildSubMenu $perlFilterPath $perlFilterMenu textFiltersProc perlFilters]
  351. }
  352.  
  353. rebuildFilterMenu
  354.  
  355. #############################################################################
  356. # Use variable tracing to keep global vars and modeVars consistent.
  357. #
  358. trace variable PerlmodeVars(perlUseDebug) w shadowPerl
  359. trace variable PerlmodeVars(perlOverwrite) w shadowPerl
  360. trace variable PerlmodeVars(perlUsebuffer) w shadowPerl
  361. trace variable PerlmodeVars(perlGetOutput) w shadowPerl
  362. trace variable PerlmodeVars(perlAutoSwitch) w shadowPerl
  363. trace variable PerlmodeVars(perlPromptArgs) w shadowPerl
  364. trace variable PerlmodeVars(perlLastFilter) w shadowPerl
  365. trace variable PerlmodeVars(perlCmdlineArgs) w shadowPerl
  366. trace variable PerlmodeVars(perlRecycleOutput) w shadowPerl
  367.  
  368. # perlFilterPath is now just a regular variable, set from the App Paths submenu
  369. trace variable perlFilterPath w rebuildFilterMenu
  370.  
  371. # ShadowPerl sets the global vars when the mode vars are modified and
  372. # keeps the menu checkmarked correctly.
  373. #
  374. proc shadowPerl {name1 name2 op} {
  375.     global perlMenu perlOptsMenu filtOptsMenu
  376.     global perlOverwrite perlUsebuffer perlGetOutput perlAutoSwitch
  377.     global perlPromptArgs perlPrevScript perlCmdlineArgs perlUseDebug
  378.     global PerlmodeVars
  379.     if {$name1 == "PerlmodeVars" && $op == "w"} {
  380.         switch $name2 {
  381.             "perlUseDebug"    {
  382.                 set perlUseDebug $PerlmodeVars(perlUseDebug)
  383.                 markMenuItem $perlOptsMenu useDebugger $perlUseDebug
  384.              }
  385.             "perlOverwrite"    {
  386.                 set perlOverwrite $PerlmodeVars(perlOverwrite)
  387.                 markMenuItem $filtOptsMenu overwriteSelection $perlOverwrite
  388.              }
  389.             "perlUsebuffer"    {
  390.                 set perlUsebuffer $PerlmodeVars(perlUsebuffer)
  391.                 markMenuItem $filtOptsMenu applyToBuffer $perlUsebuffer
  392.              }
  393.             "perlGetOutput"    {
  394.                 set perlGetOutput $PerlmodeVars(perlGetOutput)
  395.                 markMenuItem $perlOptsMenu retrieveOutput $perlGetOutput 
  396.             }
  397.             "perlAutoSwitch" {    
  398.                 set perlAutoSwitch $PerlmodeVars(perlAutoSwitch)
  399.                 markMenuItem $perlOptsMenu autoSwitch $perlAutoSwitch 
  400.             }
  401.             "perlPromptArgs" {    
  402.                 set perlPromptArgs $PerlmodeVars(perlPromptArgs)
  403.                 markMenuItem $perlOptsMenu promptForArgs $perlPromptArgs 
  404.             }
  405.             "perlCmdlineArgs" {    
  406.                 set perlCmdlineArgs $PerlmodeVars(perlCmdlineArgs)
  407.             }
  408.             "perlRecycleOutput" {    
  409.                 set perlRecycleOutput $PerlmodeVars(perlRecycleOutput)
  410.             }
  411.             "perlLastFilter" {    
  412.                 # Don't allow perlPrevScript to be changed from the flags menu
  413.                 if {$perlPrevScript == "*startup*"} {
  414.                     set perlPrevScript $PerlmodeVars(perlLastFilter)
  415.                     enableMenuItem $perlMenu repeatLastFilter 1
  416.                 } else {
  417.                     set PerlmodeVars(perlLastFilter) $perlPrevScript 
  418.                 }
  419.             }
  420.             default {
  421.                 return
  422.             }
  423.         }
  424.     }
  425. }
  426.  
  427. #############################################################################
  428. # Menu commands
  429. #############################################################################
  430.  
  431. ############################################################################
  432. # Toggle the perl menu flags
  433. #
  434. proc retrieveOutput {} {
  435.     global perlMenu PerlmodeVars perlGetOutput modifiedModeVars
  436.     lappend modifiedModeVars [list perlGetOutput PerlmodeVars]
  437.     if {$perlGetOutput} then {
  438.         set PerlmodeVars(perlGetOutput) 0
  439.     } else {
  440.         set PerlmodeVars(perlGetOutput) 1
  441.     }
  442. }
  443.  
  444. proc useDebugger {} {
  445.     global perlMenu PerlmodeVars perlUseDebug modifiedModeVars
  446.     lappend modifiedModeVars [list  perlUseDebug PerlmodeVars]
  447.     if {$perlUseDebug} then {
  448.         set PerlmodeVars(perlUseDebug) 0
  449.     } else {
  450.         set PerlmodeVars(perlUseDebug) 1
  451.     }
  452. }
  453.  
  454. proc autoSwitch {} {
  455.     global perlMenu PerlmodeVars perlAutoSwitch modifiedModeVars
  456.     lappend modifiedModeVars [list  perlAutoSwitch PerlmodeVars]
  457.     if {$perlAutoSwitch} then {
  458.         set PerlmodeVars(perlAutoSwitch) 0
  459.     } else {
  460.         set PerlmodeVars(perlAutoSwitch) 1
  461.     }
  462. }
  463.  
  464. proc overwriteSelection {} {
  465.     global perlMenu perlOverwrite PerlmodeVars modifiedModeVars
  466.     lappend modifiedModeVars [list  perlOverwrite PerlmodeVars]
  467.     if {$perlOverwrite} then {
  468.         set PerlmodeVars(perlOverwrite) 0
  469.     } else {
  470.         set PerlmodeVars(perlOverwrite) 1
  471.     }
  472. }
  473.  
  474. proc applyToBuffer {} {
  475.     global perlMenu perlUsebuffer PerlmodeVars modifiedModeVars
  476.     lappend modifiedModeVars [list  perlUsebuffer PerlmodeVars]
  477.     if {$perlUsebuffer} then {
  478.            set PerlmodeVars(perlUsebuffer) 0
  479.     } else {
  480.            set PerlmodeVars(perlUsebuffer) 1
  481.     }
  482. }
  483.  
  484. proc promptForArgs {} {
  485.     global perlMenu perlPromptArgs PerlmodeVars modifiedModeVars
  486.     lappend modifiedModeVars [list perlPromptArgs PerlmodeVars]
  487.     if {$perlPromptArgs} then {
  488.            set PerlmodeVars(perlPromptArgs) 0
  489.     } else {
  490.            set PerlmodeVars(perlPromptArgs) 1
  491.     }
  492. }
  493.  
  494. proc textFiltersFolder {} {
  495.     global perlMenu perlFilterPath PerlmodeVars modifiedModeVars pathComments
  496.     
  497.     pathProc {} $pathComments(perlFilterPath)
  498. }
  499.  
  500. #############################################################################
  501. # Switch to MacPerl:
  502. proc macperl {} {
  503.     global macperlPath
  504.     set name [checkRunning MacPerl McPL macperlPath 0]
  505.     if {[string length $name]} {
  506.         switchTo "MacPerl"
  507.     } else {
  508.         alertnote "Couldn't run MacPerl"
  509.     }
  510. }
  511.  
  512. #############################################################################
  513. # Interact with MacPerl in some other way besides executing a script
  514. #
  515. proc perlTellProc {menu name} {
  516.     switch -exact $name {
  517.     "Open This File"        openInMacperl
  518.     
  519.     "Save As Droplet"        saveThruMacperl "droplet"
  520.     
  521.     "Save As Runtime"        saveThruMacperl "runtime"
  522.     
  523.     "Save As CGI"            saveThruMacperl "cgi"
  524.     
  525.     "Save As CGI-not"        saveThruMacperl "cgi-not"
  526.     
  527.     "Get Output Window"        openPerlOutput
  528.     
  529.     "Close Output Window"    { sendCloseWinName MacPerl MacPerl
  530.                               sendCloseWinName MacPerl "Perl Debug" }
  531.                             
  532.     "Quit"                    quitMacperl
  533.     }
  534. }
  535.  
  536. #############################################################################
  537. # Open the current file under MacPerl.  This used to useful for saving files 
  538. # as droplets or runtime scripts.  Maybe it's still useful for something...?
  539. #
  540. proc openInMacperl {} {
  541.     global macperlPath
  542.     set name [checkRunning MacPerl McPL macperlPath 0]
  543.     if {![string length $name]} {
  544.         alertnote "Couldn't run MacPerl"
  545.     }
  546.  
  547.     if {[winDirty]} {
  548.         case [askyesno -c "Save '[lindex [winNames] 0]'?"] in {
  549.             "yes" {save}
  550.             "no" {}
  551.             "cancel" {return}
  552.         }
  553.     }
  554.     switchTo $name
  555.     sendOpenEvent -n $name [lindex [winNames -f] 0]
  556. }
  557.  
  558. #############################################################################
  559. # Save the script in the current window as a MacPerl droplet or 
  560. # runtime script.  
  561. #
  562. proc saveThruMacperl {type} {
  563.     global macperlPath ALPHA
  564.     set name [checkRunning MacPerl McPL macperlPath 0]
  565.     if {![string length $name]} {
  566.         alertnote "Couldn't run MacPerl"
  567.     }
  568.     
  569.     getWinInfo arr
  570.     if {$arr(dirty) == 1} {
  571.         case [askyesno -c "Save '[lindex [winNames] 0]' source file also?"] in {
  572.             "yes" {save}
  573.             "no" {}
  574.             "cancel" {return}
  575.         }
  576.     }
  577.  
  578.     set destfile [AEFilename [putfile {Save droplet as} [lindex [winNames] 0]]]
  579.  
  580.     set script [curlyq [getText 0 [maxPos]]]
  581.  
  582.     if {$type == "droplet"} {
  583.         set saveType "SCPT"
  584.     } elseif {$type == "runtime"} {
  585.         set saveType "MrP7"
  586. #     } elseif {$type == "cgi"} {
  587. #         set saveType "WWW╜"
  588. #     } elseif {$type == "cgi-not"} {
  589. #         set saveType "WWWO"
  590.     } elseif {$type == "text"} {
  591.         set saveType "TEXT"
  592.     }
  593.     
  594.     set err [catch {eval "AEBuild -t 36000 -r \"$name\"" core save {----} [list $script] {dest:} [list $destfile] {fltp:} $saveType } reply ]
  595.  
  596. #     perlDisplayReply $reply
  597. #     message "AEBuild error code $err in saveThruMacperl"
  598.     
  599. # The following lines could be used to tell MacPerl to take the script file 
  600. # from an existing disk file and then re-save it in the desired form.
  601. #
  602. #    set srcfile "\[ [AEFilename [lindex [winNames -f] 0]] \]"
  603. #    set reply [eval "AEBuild -t 36000 -r \"$name\"" core save {----} [list $srcfile] {dest:} [list $destfile] {fltp:} $saveType ]
  604. #
  605. }
  606.  
  607. #############################################################################
  608. # Quit a running MacPerl app:
  609. proc quitMacperl {} {
  610.     foreach proc [processes] {
  611.         set sig [lindex $proc 1]
  612.         if {$sig == "McPL"} {
  613.             sendQuitEvent [lindex $proc 0]
  614.             # switchTo is necessary to keep MacPerl from blinking
  615.             switchTo [lindex $proc 0]    
  616.         }
  617.     }
  618. }
  619.  
  620. #############################################################################
  621. # Run the selection as a MacPerl script:
  622. # (No special arrangements are made to provide input or capture the output)
  623. proc runTheSelection {} {
  624.     global scriptFile scriptStart
  625.     set scriptFile [lindex [winNames -f] 0]
  626.     set scriptStart [lindex [posToRowCol [getPos]] 0]
  627.     perlExecuteScript [getSelect]
  628. }
  629.  
  630. proc runTheBuffer {} {
  631.     global scriptFile scriptStart
  632.     set scriptFile [lindex [winNames -f] 0]
  633.     set scriptStart 1
  634.     perlExecuteScript [getText 0 [maxPos]]
  635. }
  636.  
  637. proc runAFile {} {
  638.     global scriptFile scriptStart
  639.     if {! [catch {getfile "Select a Perl script"} path]} {
  640.         set scriptFile $path
  641.         set scriptStart 1
  642.         perlExecuteFile $path
  643.     }
  644. }
  645.  
  646. proc saveAndRun {} {
  647.     global scriptFile scriptStart
  648.     save
  649.     set path [lindex [winNames -f] 0]   
  650.     set scriptFile $path
  651.     set scriptStart 1
  652.     perlExecuteFile $path
  653. }
  654.  
  655. #############################################################################
  656. # Run a preattached Perl text-filter script selected from the menu:
  657. #
  658. proc textFiltersProc {menu name} {
  659.     global perlFilters scriptFile scriptStart
  660.     
  661.     perlFileAsFilter $perlFilters($menu:$name)
  662. }
  663.  
  664. #############################################################################
  665. # Reuse the previous (buffer or file) filter:
  666. #
  667. proc repeatLastFilter {} {
  668.     global scriptFile scriptStart perlPrevScript perlMenu PerlmodeVars
  669.     if {$perlPrevScript != {}} {
  670.         set stype [lindex $perlPrevScript 0]
  671.         set name [lindex $perlPrevScript 1]
  672.         if {$stype == "file"} {
  673.             perlFileAsFilter $name
  674.         } elseif {$stype == "buffer"} {
  675.             perlBufferAsFilter $name
  676.         } else {
  677.             message "Bogus filter name : \"$perlPrevScript\""
  678.             set perlPrevScript {}
  679.             set PerlmodeVars(perlLastFilter) $perlPrevScript 
  680.             enableMenuItem $perlMenu repeatLastFilter 0
  681.         }
  682.     }
  683. }
  684.  
  685. #############################################################################
  686. # Ask for a file containing a Perl script to use as a filter:
  687. #
  688. proc selectFileAsFilter {} {
  689.     global scriptFile scriptStart perlPrevScript
  690.     if {! [catch {getfile "Select a MacPerl script"} path]} {
  691.         perlFileAsFilter $path
  692.     }
  693. }
  694.  
  695. #############################################################################
  696. # Ask for an Alpha buffer containing a Perl script to use as a filter:
  697. #
  698. proc selectBufferAsFilter {} {
  699.     global scriptFile scriptStart perlPrevScript
  700.     
  701.     set windows [winNames]
  702.     set current [lindex $windows 0]
  703.     if {[llength $windows] > 1} {
  704.         set name [listpick [lsort $windows]]
  705.         if {[string length $name]} {
  706.             # get the full name of the chosen window
  707.             set wname [lindex [winNames -f] [lsearch -exact $windows $name]]
  708.             perlBufferAsFilter $wname
  709.            }
  710.     }
  711. }
  712.  
  713. #############################################################################
  714. # Open a file from the MacPerl application folder - used by "Open Special"
  715. #
  716. proc perlOpenFile {menu name} {
  717.     set filename [macperlFolder]$name
  718.     if {[file exists $filename]} {
  719.         edit $filename
  720.     } else {
  721.         alertnote "That file doesn't exist yet"
  722.     }
  723. }
  724.  
  725. #############################################################################
  726. # Support procs
  727. #############################################################################
  728.  
  729. #############################################################################
  730. # Prompt the user to enter a string containing command-line args.
  731. #
  732. proc getCmdlineArgs {} {
  733.     global PerlmodeVars
  734.     set oldargs $PerlmodeVars(perlCmdlineArgs)
  735.     if {![catch {prompt "Command-line arguments (if any):" $oldargs} args]} {
  736.         set PerlmodeVars(perlCmdlineArgs) $args
  737.     } else {
  738.         error "getCmdlineArgs: User cancelled"
  739.     }
  740.     return $args
  741. }
  742.  
  743. #############################################################################
  744. # Tell MacPerl to run a script file:
  745. #
  746. proc perlExecuteFile {path {args {}} {flags {}}} {
  747.     global ALPHA macperlPath
  748.     global perlGetOutput perlAutoSwitch perlPromptArgs perlUseDebug
  749.     global scriptFile scriptStart filterHeadLen
  750.     
  751.     if {[string length $path]} {
  752.         set name [checkRunning MacPerl McPL macperlPath 0]
  753.         if {[string length $name]} {
  754.                 
  755.             set ok [regexp {(.*):([^:]*)} $path pathname dirname filename]
  756.             if {!$ok} {    set name $wname    }
  757.  
  758.             if {$path != [scriptPath]} {    
  759.                 set filterHeadLen 0    
  760.             }
  761.             
  762.             if {$perlUseDebug} {
  763.                 append flags "debug"
  764.             }
  765.             if {$perlPromptArgs} { 
  766.                 append args " [getCmdlineArgs]"
  767.             }
  768.             
  769.             sendCloseWinName MacPerl MacPerl
  770.             sendCloseWinName MacPerl "Perl Debug"
  771.             if {$perlAutoSwitch || $perlUseDebug} then {
  772.                 switchTo $name
  773.             } else {
  774.                 message "Running file \"$filename\" as Perl script"
  775.                 watchCursor
  776.             }
  777.             
  778.             perlDoScript "MacPerl" $path $args {} $flags
  779.             
  780. # (not sure which choice is better...)
  781. #            if {!$perlAutoSwitch} then {switchTo $ALPHA}
  782.             switchTo $ALPHA
  783. #
  784.             if {![getMacPerlError]} {
  785.                 if {$perlGetOutput} then {openPerlOutput}
  786.             }
  787.         } else {
  788.             alertnote "Couldn't run MacPerl"
  789.         }
  790.     } else {
  791.         alertnote "No file specified to execute"
  792.     }
  793. }
  794.  
  795. #############################################################################
  796. # Run a MacPerl script, passed explicitly as a string:
  797. #
  798. # If no "#!/bin/perl" line already exists, one is preprended to the script
  799. # by wrapSelectScript, which also sets $filterHeadLen for use by 
  800. # getMacPerlError.
  801. proc perlExecuteScript {script {args ""} {flags {}} } {
  802.     global macperlPath perlGetOutput perlAutoSwitch perlPromptArgs
  803.     global scriptFile scriptStart filterHeadLen perlUseDebug ALPHA
  804.     
  805.     if {$script != ""} {
  806.         set script [wrapSelectScript $script]
  807.         incr scriptStart -$filterHeadLen
  808.         
  809.         set ok [regexp {(.*):([^:]*)} $scriptFile pathname dirname filename]
  810.         if {!$ok} {    set name $wname    }
  811.  
  812. #        perlExecuteFile [scriptPath] $args $flags
  813.  
  814.         set name [checkRunning MacPerl McPL macperlPath 0]
  815.         if {[string length $name]} {
  816.         
  817.             if {$perlUseDebug} {
  818.                 append flags "debug"
  819.             }
  820.             if {$perlPromptArgs} { 
  821.                 append args " [getCmdlineArgs]"
  822.             }
  823.             
  824.             sendCloseWinName MacPerl MacPerl
  825.             sendCloseWinName MacPerl "Perl Debug"
  826.             if {$perlAutoSwitch || $perlUseDebug} then {
  827.                 switchTo $name
  828.             } else {
  829.                 message "Running buffer \"$filename\" as Perl script"
  830.                 watchCursor
  831.             }
  832.             
  833.             perlDoScript "MacPerl" $script $args {} $flags
  834.             
  835.             switchTo $ALPHA
  836.  
  837.             if {![getMacPerlError]} {
  838.                 if {$perlGetOutput} then {openPerlOutput}
  839.             }
  840.         }
  841.         
  842.     } else {
  843.             alertnote "Can't run an empty script"
  844.     }
  845. }
  846.  
  847. #############################################################################
  848. # Prepare the contents of a disk file for use as a text-filter script. 
  849. # (calls perlTextFilter to actually run the script)
  850. proc perlFileAsFilter {path} {
  851.     global scriptFile scriptStart perlPrevScript perlMenu PerlmodeVars
  852.     
  853.     regexp {(.*):([^:]*)} $path pathname dirname name
  854.     
  855.     if {![catch {readFile $path} coreScript]} {
  856.         set script [wrapFilterScript $coreScript]
  857.         set scriptFile $path
  858.         set scriptStart 1
  859.         set perlPrevScript [list "file" $path]
  860.         set PerlmodeVars(perlLastFilter) $perlPrevScript 
  861.         enableMenuItem $perlMenu repeatLastFilter 1
  862.         message "Running file \"$name\" as text filter ..."
  863.         
  864.         perlTextFilter $script
  865.     } else {
  866.         set perlPrevScript {}
  867.         set PerlmodeVars(perlLastFilter) $perlPrevScript 
  868.         enableMenuItem $perlMenu repeatLastFilter 0
  869.         
  870.         alertnote "Couldn't read the script file : $path"
  871.         return
  872.     }
  873. }
  874.  
  875. #############################################################################
  876. # Prepare the contents of a text window for use as a text-filter script. 
  877. # (calls perlTextFilter to actually run the script)
  878. proc perlBufferAsFilter {wname} {
  879.     global scriptFile scriptStart perlPrevScript perlMenu PerlmodeVars
  880.  
  881.     set ok [regexp {(.*):([^:]*)} $wname pathname dirname name]
  882.     if {!$ok} {    set name $wname    }
  883.     
  884.     if {[lsearch [winNames -f] $wname] >= 0} {
  885.         set coreScript [getText -w $wname 0 [maxPos -w $wname]]
  886.         
  887.         # Does it have any text in it?
  888.         if {[string length $coreScript]} {
  889.             set scriptFile $wname
  890.             set scriptStart 1
  891.             set script [wrapFilterScript $coreScript]
  892.             set perlPrevScript [list "buffer" $wname]
  893.             set PerlmodeVars(perlLastFilter) $perlPrevScript 
  894.             enableMenuItem $perlMenu repeatLastFilter 1
  895.             message "Running buffer \"$name\" as text filter ..."
  896.             
  897.             perlTextFilter $script
  898.         }
  899.     } else {
  900.         set perlPrevScript {}
  901.         set PerlmodeVars(perlLastFilter) $perlPrevScript 
  902.         enableMenuItem $perlMenu repeatLastFilter 0
  903.  
  904.         alertnote "Couldn't find buffer : $name"
  905.     }
  906. }
  907.  
  908. #############################################################################
  909. # Run a Perl script as a command-line text filter, arranging for a text
  910. # buffer to be attached as standard input.  The calling routine should already
  911. # have processed the script with wrapFilterScript.  This routine actually
  912. # send the script and takes care of writing the input and reading the output 
  913. # files.
  914. proc perlTextFilter {script {args {}} {flags {}}} {
  915.     global macperlPath perlOverwrite perlUsebuffer perlPromptArgs
  916.     global filterHeadLen scriptFile scriptStart perlUseDebug ALPHA
  917.     global perlOutputWindow perlRecycleOutput
  918.  
  919.     set name [checkRunning MacPerl McPL macperlPath 0]
  920.     if {![string length $name]} {
  921.         alertnote "Couldn't run MacPerl"
  922.         error "Couldn't run MacPerl"
  923.     }
  924.     writeStdin
  925.  
  926.     if {$perlUseDebug} {
  927.         append flags "debug"
  928.     }
  929.     if {$perlPromptArgs} { 
  930.         append args " [getCmdlineArgs]"
  931.     }
  932.     
  933.     sendCloseWinName MacPerl MacPerl
  934.     sendCloseWinName MacPerl "Perl Debug"
  935.     
  936.     if {$perlUseDebug} then {
  937.         switchTo $name
  938.         perlDoScript "MacPerl" [scriptPath] $args [list [stdinPath]] $flags
  939.         set err [getMacPerlError]
  940.  
  941.     } else {
  942.         watchCursor
  943.         set reply [perlDoScriptBatch "MacPerl" [scriptPath] $args [list [stdinPath]]]
  944.         set err [getBatchError $reply]
  945.     }
  946.     
  947.     switchTo $ALPHA
  948.     
  949.     if {$err == 0} {
  950.         if {$perlUseDebug} {
  951.             set outp [sendGetText MacPerl MacPerl]
  952.         } else {
  953. #            set outp [parseReplyOutp $reply]
  954.             set outp [parseReplyResult $reply]
  955.         }
  956.         pasteFilterResult $outp
  957.     }
  958. }
  959.  
  960.  
  961. #############################################################################
  962. # Check the MacPerl output window for error messages.
  963. #
  964. proc getMacPerlError {} {
  965.     
  966.     set diag [getPerlDiag 40]
  967.     set srcs [parseDiagSrcs $diag]
  968.     set errf [parseDiagErrf $diag]
  969.     set mesg [parseDiagMesg $diag]
  970.  
  971.     if {[string length $errf]} {
  972.         showPerlDiag $diag [string length $diag] $mesg $errf $srcs
  973.         gotoPerlError $errf $srcs $mesg
  974.         return 1
  975.         
  976.     } else {
  977.         return 0
  978.     }
  979. }
  980.  
  981. #############################################################################
  982. # Check the MacPerl batch reply for error messages.
  983. #
  984. proc getBatchError {reply} {
  985.     global perlErrorWindow
  986.     
  987.     set fatalError 0
  988.     set diag [parseReplyDiag $reply]
  989.     set errf [parseDiagErrf  $diag ]
  990.     set srcs [parseReplySrcs $reply]
  991.     set mesg [parseDiagMesg  $diag ]
  992.     set errn [parseReplyErrn $reply]
  993.  
  994.     if {$errn} {        
  995.         showPerlDiag $diag $errn $mesg $errf $srcs
  996.         gotoPerlError $errf $srcs $mesg
  997.         set fatalError 1
  998.         
  999.     } elseif {[string length $diag] > 0} {
  1000.         showPerlDiag $diag $errn $mesg $errf $srcs
  1001.     }
  1002.     
  1003.     return $fatalError
  1004. }
  1005.  
  1006. #############################################################################
  1007. # Display the Perl diagnostic output in its own window.
  1008. #
  1009. proc showPerlDiag {diag {errn 1} {mesg {}} {errf {}} {srcs {}}} {
  1010.         global perlErrorWindow    
  1011.         
  1012.         set currWin [lindex [winNames] 0]
  1013.         if {[lsearch [winNames] $perlErrorWindow] >= 0} {
  1014.             bringToFront $perlErrorWindow
  1015.             setWinInfo read-only 0
  1016.             deleteText 0 [maxPos] 
  1017.             insertText $diag
  1018.         } else {
  1019.             new -n $perlErrorWindow 
  1020.              insertText $diag
  1021.         }
  1022.         
  1023.         goto 0
  1024.         catch {shrinkWindow 1}
  1025.         setWinInfo dirty 0
  1026.         setWinInfo read-only 1
  1027.         bringToFront $currWin
  1028. }
  1029.  
  1030. #############################################################################
  1031. # Bring up a window containing the bug-ridden Perl code and highlight the
  1032. # line at which the error was found.
  1033. #
  1034. proc gotoPerlError {errf srcs {mesg {}}} {
  1035.     global scriptFile scriptStart filterHeadLen
  1036.  
  1037.     if {$errf == [scriptPath] || $errf == "<AppleEvent>"} {
  1038.         set errf $scriptFile
  1039.         # Convert it to the line number in the original file
  1040.         set srcs [expr $srcs + $scriptStart - $filterHeadLen - 1]
  1041.     }
  1042.     # ... and leave an informative error message
  1043.     #
  1044.     if {[string length $mesg]} {
  1045.         set mesg "$mesg at Line $srcs"            
  1046.     } else {
  1047.         set mesg "MacPerl flagged an error at Line $srcs"    
  1048.     }
  1049.     
  1050.     # Bring up the script file and highlight the flagged line
  1051.     #
  1052.     catch {gotoFileLine $errf $srcs $mesg} fname    
  1053. }
  1054.  
  1055. #############################################################################
  1056. # Read the first block of lines (up to a maximum number) from the MacPerl
  1057. # output window.
  1058. #
  1059. proc getPerlDiag {maxlines} {
  1060.     set pat0 {^[ \t]*$}
  1061.  
  1062.     set lines {}    
  1063.  
  1064.     # read first $maxlines of output to the MacPerl window
  1065.     # (faster, but assumes error message won't appear at 
  1066.     # the end of a lot of output).
  1067.     #
  1068.     set nlines [sendCountLines MacPerl MacPerl]
  1069.     set nlines [expr ($nlines > $maxlines)?$maxlines:$nlines]
  1070.     if {$nlines > 0} {
  1071.         set output [sendGetText MacPerl MacPerl 1 $nlines]
  1072.         
  1073.         foreach line [split $output "\r"] {
  1074.             if  {[regexp $pat0 $line mtch]} {
  1075.                 break
  1076.             } else {
  1077.                 append lines "$line\n"
  1078.             }
  1079.         }
  1080.     }
  1081.     return $lines
  1082. }
  1083.  
  1084. #############################################################################
  1085. # Extract various items out of the MacPerl diagnostic output
  1086. #
  1087.  
  1088. # Name of the file in which the error was found
  1089. #
  1090. proc parseDiagErrf {diag}    {
  1091.     if {![regexp {File '([^']+)'; Line} $diag allofit errf]} { 
  1092.         set errf {}
  1093.     }
  1094.     return $errf
  1095. }
  1096.  
  1097. # The line number on which the error was found
  1098. #
  1099. proc parseDiagSrcs {diag}    {
  1100.     if {![regexp {File '[^']+'; Line ([0-9]+)} $diag allofit srcs]} { 
  1101.         set srcs 0 
  1102.     }
  1103.     return $srcs
  1104. }
  1105.  
  1106. # The error message associated with error
  1107. #
  1108. proc parseDiagMesg {diag} {
  1109.     set pat1 {^#(.*)$}
  1110.     set pat2 {File '([^']+)'; Line ([0-9]+)}
  1111.     
  1112.     set errMessage {}
  1113.     set errFound 0
  1114.     
  1115.     foreach line [split $diag "\n"] {
  1116.         if {[regexp $pat2 $line mtch num]} {
  1117.             set errFound 1
  1118.         } elseif {[regexp $pat1 $line mtch err]} {
  1119.             if {$errFound == 0} {
  1120.                 set errMessage $err
  1121.             }
  1122.         }
  1123.     }
  1124.     return $errMessage
  1125. }
  1126.  
  1127. #############################################################################
  1128. # Extract various return parameters out of a MacPerl DoScript reply
  1129. #
  1130.  
  1131. # Result from batch script
  1132. #
  1133. proc parseReplyResult {reply} {
  1134.     if {![regexp {'?\-\-\-\-'?:╥([^╙]*)╙} $reply allofit result]} { 
  1135.         set result {}
  1136.     }
  1137.     return $result
  1138. }
  1139.  
  1140. # Standard output of batch script
  1141. #
  1142. proc parseReplyOutp {reply} {
  1143.     if {![regexp {OUTP:╥([^╙]*)╙} $reply allofit outp]} { 
  1144.         set outp {}
  1145.     }
  1146.     return $outp
  1147. }
  1148.  
  1149. # Diagnostic output of the batch script
  1150. #
  1151. proc parseReplyDiag {reply}    {
  1152.     if {[regexp {diag:╥([^╙]*)╙} $reply allofit diag]}  {
  1153.     } else { 
  1154.         set diag {}
  1155.     }
  1156.     return $diag
  1157. }
  1158.  
  1159. # File alias of the script file in which the error was found
  1160. #
  1161. proc parseReplyErob {reply}    {
  1162.     if {![regexp {erob:alis\(╟(.*)╚\)} $reply allofit erob]} {
  1163.         set erob {} 
  1164.     }
  1165.     return $erob
  1166. }
  1167.  
  1168. # First line flagged in error
  1169. #
  1170. proc parseReplySrcs {reply}    {
  1171.     if {![regexp {erng:{srcs:([0-9]+)[^\}]*}} $reply allofit srcs]} { 
  1172.         set srcs 0 
  1173.     }
  1174.     return $srcs
  1175. }
  1176.  
  1177. # Last line flagged in error
  1178. #
  1179. proc parseReplySrce {reply}    {
  1180.     if {![regexp {erng:{[^\}]*srce:([0-9]+)}} $reply allofit srce]} { 
  1181.         set srce 0
  1182.     }
  1183.     return $srce
  1184. }
  1185.  
  1186. # Error number
  1187. #
  1188. proc parseReplyErrn {reply}    {
  1189.     if {![regexp {errn:([0-9]+)} $reply allofit errn]} {
  1190.         set errn 0
  1191.     }
  1192.     return $errn
  1193. }
  1194.  
  1195. #############################################################################
  1196. #  Take a Perl script and add commands to take the file STDIN as standard
  1197. #  input and STDOUT as standard output.  This allows scripts written as
  1198. #  Unix command-line filters to be used in the (non-MPW) Mac environment as
  1199. #  text filters.
  1200. #
  1201. #  If there's already a #! line in the script, then the new commands
  1202. #  are added after that line.  If there was no #! line in the first place,
  1203. #  one is added, in case MacPerl is set up to require it (can't hurt...) 
  1204. #
  1205. #  $filterHeadLen counts the number of lines we add to the top of the
  1206. #  original script, so that we can allow for it in interpreting error
  1207. #  messages issued by MacPerl.
  1208. #
  1209. #  *** As of MacPerl 4.1.4, this business is pretty much obsolete ***
  1210. #
  1211. proc wrapFilterScript {coreScript} {
  1212.     global filterHeadLen
  1213.  
  1214.     if {[regexp -indices {(#![     !-~]*)} $coreScript allofit cmdln]} {
  1215.         set endPos [lindex $cmdln 1]
  1216.         set filterHead [string range $coreScript 0 [expr $endPos+1]]
  1217.         set coreScript [string range $coreScript [expr $endPos+2] end]
  1218.         set filterHeadLen 0
  1219.     } else {
  1220.         set filterHead "#!/bin/perl\n"
  1221.         set filterHeadLen 1
  1222.     }
  1223.         
  1224.     set script $filterHead
  1225.     append script $coreScript
  1226.     
  1227.     # for debugging purposes, save the script on disk
  1228.     #
  1229.     writeScript $script
  1230.     return $script
  1231. }        
  1232.  
  1233. #############################################################################
  1234. #  Add a #!/bin/perl line to the script if it doesn't contain one already.
  1235. #  (MacPerl puts up dialog if this line is missing when it expects it,
  1236. #  hanging the DoScript and leaving us stuck.)
  1237. #
  1238. proc wrapSelectScript {coreScript} {
  1239.     global filterHeadLen
  1240.  
  1241.     if {![regexp -indices {(#![     !-~]*)} $coreScript allofit cmdln]} {
  1242.         set script "#!/bin/perl\n"
  1243.         append script $coreScript
  1244.         set filterHeadLen 1
  1245.     } else {
  1246.         set script $coreScript
  1247.         set filterHeadLen 0
  1248.     }
  1249.     
  1250.     # for debugging purposes, save the script on disk
  1251.     #
  1252.     writeScript $script
  1253.     return $script
  1254. }        
  1255.  
  1256. #############################################################################
  1257. #  Paste result of the filter operation in place of the input text, or in
  1258. #  a new window (depending on the flag $perlOverwrite
  1259. #
  1260. proc pasteFilterResult {text} {
  1261.     global perlOverwrite perlRecycleOutput perlOutputWindow
  1262.     global perlUsebuffer 
  1263.     
  1264.     if {!$perlOverwrite} {
  1265.         if {$perlRecycleOutput && 
  1266.             [lsearch [winNames] $perlOutputWindow] >= 0} {                
  1267.             bringToFront $perlOutputWindow
  1268.         } else {
  1269.             new -n $perlOutputWindow
  1270.         }
  1271.     }
  1272.     
  1273.     if {$perlUsebuffer || $perlRecycleOutput} {
  1274.         set from 0
  1275.         set to [maxPos]
  1276.     } else {
  1277.         set from [getPos] 
  1278.         set to [selEnd]
  1279.     }    
  1280.     replaceText $from $to $text
  1281.     
  1282.     if {!$perlOverwrite || $perlUseBuffer} {
  1283.         catch {shrinkWindow 1}
  1284.         goto 0
  1285.     } else {
  1286.         catch shrinkWindow
  1287.         goto $from
  1288.     }
  1289.     if {!$perlOverwrite} { setWinInfo dirty 0 }
  1290. }    
  1291.  
  1292. #############################################################################
  1293. #  Extend the current selection to encompass complete lines.  If the 
  1294. #  'applyToBuffer' flag is checked, then the entire buffer is selected.
  1295. #
  1296. proc completeSelection {} {
  1297.     global perlUsebuffer filterInput
  1298.     set filterInput "buffer \"[lindex [winNames] 0]\""
  1299.     if {$perlUsebuffer} {
  1300.         set start 0
  1301.         set end [maxPos]
  1302.     } else {
  1303.         set start [lineStart [getPos]]
  1304.         set end [nextLineStart [expr [selEnd]-1]]
  1305.         if {$end == $start} { set end [nextLineStart [selEnd]] }
  1306.         
  1307.         set startLine [lindex [posToRowCol $start] 0]
  1308.         set endLine [expr [lindex [posToRowCol $end] 0] - 1]
  1309.         if {$endLine > $startLine+1} {
  1310.             set filterInput "lines $startLine to $endLine of $filterInput"
  1311.         } else {
  1312.             set filterInput "line $startLine of $filterInput"
  1313.         }
  1314.    }
  1315.     return [list $start $end]
  1316. }
  1317.  
  1318. #############################################################################
  1319. #  writeStdin: Extend the selection, as appropriate, and write it to the 
  1320. #     STDIN file in the MacPerl directory.
  1321. #
  1322. #  writeScript: Write the SCRIPT file in the MacPerl directory.  MacPerl will
  1323. #     read the script from this file. 
  1324. #
  1325. proc writeStdin {} {
  1326.     set res [completeSelection]
  1327.     set tmpfid [open [stdinPath] "w+"]
  1328.     puts $tmpfid [eval getText $res]
  1329.     close $tmpfid
  1330. }
  1331.  
  1332. # This is unnecessary now, but maybe it'll still useful to save the script
  1333. # file for debugging.
  1334. #
  1335. proc writeScript {script} {
  1336.     set tmpfid [open [scriptPath] "w+"]
  1337.     puts $tmpfid $script 
  1338.     close $tmpfid
  1339. }
  1340.  
  1341. #############################################################################
  1342. # Read the MacPerl output window and load the contents, if any, into
  1343. # a new Alpha window. 
  1344. #
  1345. proc openPerlOutput {} {
  1346.     global perlRecycleOutput perlOutputWindow
  1347.     
  1348.     set output [sendGetText MacPerl MacPerl]
  1349.     if {[string length $output]} {
  1350.         if {$perlRecycleOutput && 
  1351.             [lsearch [winNames] $perlOutputWindow] >= 0} {
  1352.             
  1353.             bringToFront $perlOutputWindow
  1354.             replaceText 0 [maxPos] $output
  1355.         } else {
  1356.             new -n $perlOutputWindow
  1357.             insertText $output
  1358.         }
  1359.         catch {shrinkWindow 1}
  1360.         setWinInfo dirty 0
  1361.         goto 0
  1362.     }
  1363. }
  1364.  
  1365. #############################################################################
  1366. # translate special DoScript flags into flags string $usrf
  1367. #
  1368. proc perlScriptFlags {{flags {}}} {
  1369.      set usrf {}
  1370.  
  1371.     if {[lsearch -exact $flags "extract"] >= 0} {
  1372.         append usrf { "EXTR" 'true'}
  1373.     } elseif {[lsearch -exact $flags "noextract"] >= 0} {
  1374.         append usrf { "EXTR" 'fals'}
  1375.     }        
  1376.     if {[lsearch -exact $flags "debug"] >= 0} {
  1377.         append usrf { "DEBG" 'true'}
  1378.     } elseif {[lsearch -exact $flags "nodebug"] >= 0} {
  1379.         append usrf { "DEBG" 'fals'}
  1380.     }        
  1381.  
  1382.     if {[lsearch -exact $flags "local"] >= 0} {
  1383.         append usrf { "MODE" 'LOCL'}
  1384.     } elseif {[lsearch -exact $flags "batch"] >= 0} {
  1385.         append usrf { "MODE" 'BATC'}
  1386.     } elseif {[lsearch -exact $flags "remote"] >= 0} {
  1387.         append usrf { "MODE" 'RCTL'}
  1388.     }        
  1389.     return $usrf
  1390.  
  1391. proc perlScriptArgs {{args {}} {fileargs {}}} {
  1392.     set nargs 0
  1393.     set argv {}
  1394.     
  1395.     foreach item [parseWords $args] {
  1396.         set item [string trim $item]
  1397.         if {[string length $item]} {
  1398.             append argv ", [curlyq $item]"
  1399.             incr nargs
  1400.         }
  1401.     }
  1402.     foreach filename $fileargs {
  1403.         set item [string trim $filename]
  1404.         if {[string length $item]} {
  1405.             append argv ", [curlyq $item]"
  1406.             incr nargs
  1407.         }
  1408.     }
  1409.     return $argv
  1410. }
  1411.  
  1412. #############################################################################
  1413. # General Apple Event routines
  1414. # (most of these have been moved to SystemCode:appleEvents.tcl)
  1415. #
  1416. # DoScript for MacPerl 4.1.3
  1417. # (runs in "Local" mode under v4.1.4+)
  1418. #
  1419. proc perlDoScript {appname script {args {}} {fileargs {}} {flags {}} } {
  1420.     # form list of quoted "command-line" args
  1421.     #
  1422.     if {$script != ""} {
  1423.         set argv "\[[curlyq [string trim $script]]"
  1424. #         foreach item [split [join $args " "] " "] {
  1425. #}
  1426.         append argv [perlScriptArgs $args $fileargs]
  1427.         append argv "]"
  1428.         
  1429.         set usrf [perlScriptFlags $flags]
  1430.         set reply [eval "AEBuild -t 36000 -r \"$appname\" misc dosc $usrf \"----\" [list $argv] "]
  1431.     #    alertnote $reply
  1432.     }
  1433. }
  1434.  
  1435. # DoScript for MacPerl 4.1.4+
  1436. #
  1437. proc perlDoScriptBatch {appname script {args {}} {fileargs {}}} {
  1438.     
  1439.     # form list of quoted "command-line" args
  1440.     #
  1441.     if {$script != ""} {
  1442.         set argv "\[[curlyq [string trim $script]]"
  1443.         append argv [perlScriptArgs $args $fileargs ] 
  1444.         append argv "]"
  1445.                 
  1446.         set reply [eval "AEBuild -t 36000 -r \"$appname\" misc dosc MODE BATC \"----\" [list $argv]"]
  1447.         
  1448. #         perlDisplayReply $reply
  1449.  
  1450.     } else {
  1451.         set reply {}
  1452.     }
  1453.     return $reply
  1454. }
  1455.  
  1456. # For debugging 
  1457. #
  1458. proc perlDisplayReply {reply} {
  1459.     set currWin [lindex [winNames] 0]
  1460.     new -n {*** DoScript Reply **} 
  1461.     insertText $reply
  1462.         
  1463.     goto 0
  1464.     catch {shrinkWindow 1}
  1465.     setWinInfo dirty 0
  1466.     setWinInfo read-only 1
  1467.     bringToFront $currWin
  1468. }
  1469.  
  1470. # DoScript to launch interactive debugger (for MacPerl 4.1.4+)
  1471. #
  1472. proc perlDoScriptDebug {appname script {args {}} {fileargs {}}} {
  1473.     
  1474.     # form list of quoted "command-line" args
  1475.     #
  1476.     if {$script != ""} {
  1477.         set argv "\[[curlyq [string trim $script]]"
  1478.         append argv [perlScriptArgs "$args debug" $fileargs ] 
  1479.         append argv "]"
  1480.                 
  1481.         set reply [eval "AEBuild -t 36000 -r \"$appname\" misc dosc MODE RCTL \"----\" [list $argv]"]
  1482.  
  1483.         new -n {** DoScriptDebug Reply **} 
  1484.         insertText $reply
  1485.             
  1486.         goto 0
  1487.         catch {shrinkWindow 1}
  1488.         setWinInfo dirty 0
  1489.         setWinInfo read-only 1
  1490.  
  1491.  
  1492.     } else {
  1493.         set reply {}
  1494.     }
  1495.     return $reply
  1496. }
  1497.  
  1498. ##############################################################################
  1499. # Automatic indexing of Perl subs
  1500. #
  1501. proc PerlMarkFile {} {
  1502.     set end [maxPos]
  1503.     set pos 0
  1504.     set l {}
  1505.     while {![catch {search -f 1 -r 1 -m 0 -i 0 {^sub} $pos} res]} {
  1506.         set start [lindex $res 0]
  1507.         set end [nextLineStart $start]
  1508.         set text [lindex [getText $start $end] 1]
  1509.         set pos $end
  1510.         set inds($text) [lineStart [expr $start - 1]]
  1511.     }
  1512.  
  1513.     if {[info exists inds]} {
  1514.         foreach f [lsort [array names inds]] {
  1515.             set next [nextLineStart $inds($f)]
  1516.             setNamedMark $f $inds($f) $next $next
  1517.         }
  1518.     }
  1519. }
  1520.  
  1521. ##############################################################################
  1522. # Cmd-double-click support for Perl mode. 
  1523. proc PerlDblClick {from to} {
  1524.     global HOME perlKeyWords perlSpecialVars perlExprWords
  1525.     global perlSearchPath
  1526.     
  1527.     set pc  [lookAt [expr $from - 1]]
  1528.     set ppc [lookAt [expr $from - 2]]
  1529.     set tc  [lookAt $to]
  1530.     
  1531.     # Extend selection to include special characters
  1532.     #
  1533.     if {$pc == {$}} { 
  1534.         if {$from == $to} { incr to }
  1535.         incr from -1
  1536.         if {$tc == {^}} { incr to }
  1537.         
  1538.     } elseif {$pc == {^} && $ppc == {$}} {
  1539.         incr from -2
  1540.         
  1541.     } elseif {$pc == {%} || $pc == {@}} {
  1542.         incr from -1
  1543.     }
  1544.     
  1545.     # Return if there's no selected text
  1546.     if {$to > $from} {
  1547.         select $from $to
  1548.         set text [getSelect]
  1549.         set qtext [quoteExpr $text]
  1550.     } else {
  1551.         return
  1552.     }
  1553.  
  1554.     set perlSearchPath {}
  1555.     
  1556.     # Function call
  1557.     if {$pc == "&"} {
  1558.          if {![catch {search -f 1 -r 1 -m 0 -s "sub *$qtext *\{" 0} mtch]} {
  1559.              pushMark
  1560.              eval select $mtch
  1561.              message "Use Ctl-. to return to original position"
  1562.          } else {
  1563.              message {Sub definition not found}
  1564.          }
  1565.  
  1566.     # Look up keywords in the man page by their file marks
  1567.     } elseif {[lsearch -exact $perlKeyWords $text] >= 0} {
  1568.         editMark "$HOME:Help:Perl Commands" $text
  1569.  
  1570.     # Special vars aren't marked, so search for their definitions
  1571.     } elseif {[lsearch -exact $perlSpecialVars $text] >= 0} {
  1572.         if {[lsearch -exact [winNames] "Perl Commands"] >= 0} {
  1573.             bringToFront "Perl Commands"
  1574.         } else {
  1575.             edit "$HOME:Help:Perl Commands"
  1576.         }
  1577.         if {![catch {search -f 0 -r 0 -m 0 -i 0 -s "     $text  " [maxPos]} mtch]} {
  1578.             goto [lindex $mtch 0]
  1579.         }
  1580.  
  1581.     # Flow control statements don't have separate entries
  1582.     } elseif {[lsearch -exact $perlExprWords $text] >= 0} {
  1583.         editMark "$HOME:Help:Perl Commands" "Compound statements"
  1584.     
  1585.     # If user clicked the arg of a 'require' command, open the file
  1586.     } elseif {![catch {perlFindRequire $from $to} filename]} {
  1587.         openPerlFile $filename
  1588.  
  1589.     # Other
  1590.     } else {
  1591.         select $from $to
  1592.         message {Command-double-click on keywords, special vars, and req'd filenames}
  1593.     }
  1594.  
  1595. }
  1596.  
  1597. # Open a 'require'd Perl file.
  1598. proc perlFindRequire {from {to 0}} {
  1599.     set reqPat {^[     ]*require[     ]*(\"[^\"]+\"|\'[^\']+\'|[^     ]+)}
  1600.     if {$to == 0} { set to $from }
  1601.     set beg [lineStart $from]
  1602.     set end [nextLineStart $to]
  1603.     set words [parseWords [getText $beg $end]]
  1604.     if {[string tolower [lindex $words 0]] != "require"} {
  1605.         error "Not a require statement"
  1606.     }
  1607.     set root [string trim [lindex $words 1] {'"}]
  1608.     return $root
  1609. }
  1610.  
  1611. proc inlineRequires {} {
  1612.     global lastMatchingLines
  1613.     
  1614.     set reqPat {^[     ]*require[     ]*(\"[^\"]+\"|\'[^\']+\'|[^     ]+)}
  1615.     set pos 0
  1616.     while {![catch {search -s -f 1 -r 1 -m 0 -i 1 $reqPat $pos} mtch]} {
  1617.          [lindex [posToRowCol [lindex $mtch 0]] 0]] 
  1618.         set name [string [eval getText $mtch]
  1619.         set pos [lindex $mtch 1]
  1620.         incr matches
  1621.     }
  1622. }
  1623.  
  1624. # Open a Perl source file. 
  1625. #
  1626. proc openPerlFile {file {extensions {""}}} {
  1627.     global perlSearchPath
  1628.     # Determine absolute file specification
  1629.     # Ignore $extensions if $file already has an extension
  1630.     if {[string length [file extension $file]] == 0} {
  1631.         set extensions {""}
  1632.     }
  1633.     foreach ext $extensions {
  1634.         set filename [absolutePath $file$ext]
  1635.         if {![catch {openFileQuietly $filename}]} {
  1636.             message $filename
  1637.             return 
  1638.         }
  1639.     }
  1640.     if {[llength $perlSearchPath] == 0} { buildPerlSearchPath }
  1641.     foreach folder $perlSearchPath {
  1642.         foreach ext $extensions {
  1643.             set filename "$folder$file$ext"
  1644.             if {![catch {openFileQuietly $filename}]} {
  1645.                 message $filename
  1646.                 return     
  1647.             }
  1648.         }
  1649.     }
  1650.     beep
  1651.     message "can't find Perl source file \"$file\""
  1652. }
  1653.  
  1654. # Return a list of folders in which to search for Perl library files, 
  1655. # including the lib folder in the Perl application directory and the
  1656. # $perlLib folder (if it exists) .  
  1657. # The current folder is not included in the list.
  1658. #
  1659. # (The $perlLib folder is assigned from the AppPaths submenu.)
  1660. #
  1661. proc buildPerlSearchPath {} {
  1662.     global perlLib macperlPath perlSearchPath
  1663.     message "building Perl search path..."
  1664.     set folders {}
  1665.     
  1666.     # The local lib folder:
  1667.     if {[info exists perlLib] && [string length $perlLib] > 0} { 
  1668.         set folders [concat $folders [list $perlLib]]
  1669.         # Search subfolders one level deep:
  1670.         set folders [concat $folders [listSubfolders $perlLib 1]]
  1671.     }
  1672.  
  1673.     # Any "*lib*" folders in the MacPerl application folder:
  1674.     if {[info exists macperlPath] && [string length $macperlPath] > 0} { 
  1675.         set appDir [file dirname $macperlPath]
  1676.         set folders [concat $folders [list $appDir]]
  1677.         # Bug:  'glob' is case sensitive!
  1678.         foreach folder [glob "$appDir:*\[Ll\]ib*"] {
  1679.             set folders [concat $folders [list $folder]]
  1680.             # Search subfolders one level deep:
  1681.             set folders [concat $folders [listSubfolders $folder 1]]
  1682.         }
  1683.     }
  1684.  
  1685.     # Make sure each folder ends with a colon
  1686.     set perlSearchPath {}
  1687.     foreach folder $folders {
  1688.         set folder "[string trimright $folder {:}]:"
  1689.         set perlSearchPath [concat $perlSearchPath [list $folder]]
  1690.     }
  1691. }
  1692.  
  1693. ###########################################################################
  1694.  
  1695.  
  1696.