1 ############################################################
3 # Copyright (C) 1998 William F. Schelter #
4 # For distribution under GNU public License. See COPYING. #
6 # Time-stamp: "2024-03-25 20:06:43 villate" #
7 ############################################################
9 # catch {close $socket}
10 # source send-some.tcl ; openConnection $tohost $port $magic $program
12 # run-one.tcl octave 4448 billy1
13 # then from any machine do:
14 # can also open maxima at same time
15 # source send-some.tcl ; openConnection linux14 4448 billy1 octave
17 # sendOneWait octave 2+3
19 # If you specified -debug when starting the server then you can
20 # evaluate tcl commands in the process controlling 'program'
21 # eg: sendCommand octave "list 1 1"
23 #-----------------------------------------------------------------
25 # myVwait -- this is a replacement for vwait which is missing from
26 # the plugin tcl. It is 'supposed' to be the same but in fact if it
27 # is a fileevent handler that is supposed to do the setting, then the
28 # fileevent handler might indeed get called continuously because the
29 # file becomes readable, and myVwait which was checking a variable that
30 # the handler set, never gets a chance to return, since the handler
31 # is called again and again. So Remove the handler when it is invoked.
32 # Note this uses tracing of the variable or array, and may interfere
36 # Side Effects: waits till the variable is set if it was unset, or
37 # until its value is different.
39 #----------------------------------------------------------------
41 proc myVwait
{ var
} {
42 global _waiting maxima_priv
43 set tem
[split $var "(" ]
44 set variable [lindex $tem 0]
46 lappend maxima_priv
(myVwait
) $variable
48 if { [llength $tem ] > 1 } {
49 set index
[lindex [split [lindex $tem 1] ")" ] 0]
52 set action
"_myaction [list $index]"
53 trace variable $variable w
$action
56 while { [set _waiting
] } {
57 #puts "still waiting _waiting=$_waiting"
60 set maxima_priv
(myVwait
) [ ldelete
$variable $maxima_priv(myVwait
)]
61 trace vdelete
$variable w
$action
64 proc _myaction
{ ind name1 name2 op
} {
66 # puts "action $ind $name1 $name2 $op"
67 if { "$ind" == "$name2" } {
74 # proc myVwait { x args } {uplevel "#0" vwait $x }
75 if { "[info commands vwait]" == "vwait" } {
78 # Fix for Tcl 8.5: linking unreachable global variables used to be ignored
79 # in Tcl 8.4 but in 8.5 it raises an error. The catch command should
80 # restore the Tcl 8.4 behavior. (villate, 20080513)
82 lappend maxima_priv
(myVwait
) $x
84 set maxima_priv
(myVwait
) [ ldelete
$x $maxima_priv(myVwait
)]
88 proc omDoInterrupt
{ win
} {
89 foreach v
[ $win tag names
] {
90 if { [regexp "com:pdata\\((\[a-z_A-Z]*)," $v junk program
] } {
91 set var
[string range
$v 4 end
]
92 # puts "interrupt program=$program,$var"
93 after 10 uplevel "#0" set $var <interrupted
>
94 catch { sendInterrupt
$program }
100 proc omDoAbort
{ win
} {
101 foreach v
[ $win tag names
] {
102 set var
[string range
$v 4 end
]
103 if { [regexp "com:pdata\\((\[a-z_A-Z]*)," $v junk program
] } {
104 set prog
[programName
$program]
105 if { "[info command abort_$prog]" != "" } {
107 after 200 uplevel "#0" set $var <aborted
>
110 set var
[string range
$v 4 end
]
111 # rputs "interrupt program=$program,$var"
112 after 200 uplevel "#0" set $var <aborted
>
120 after $n "set Msleeping 0"
121 debugsend
"waiting Msleeping.."
123 debugsend
"..donewaiting Msleeping"
126 proc message { msg
} {
127 global maxima_priv _debugSend
128 if { $_debugSend } { puts "setting message=<$msg>" }
129 catch { set maxima_priv
(load_rate
) $msg }
132 proc sendOne
{ program com
} {
133 global pdata maxima_priv
134 incr pdata
($program,currentExpr
)
135 set socket $pdata($program,socket)
137 if { [eof $socket] } {
138 error [mc
"connection closed"]
140 # puts "sending $program ([lindex [fconfigure $socket -peername] 1])"
141 message [concat [mc
"sending"] "$program" [mc
"on"] "[lindex [fconfigure $socket -peername] 1]"]
142 debugsend
"sending.. {$com<$pdata($program,currentExpr)\|fayve>}"
143 set msg
"$com<$pdata($program,currentExpr)\|fayve>\n"
144 proxyPuts
$socket $msg
147 #-----------------------------------------------------------------
149 # sendOneDoCommand -- sends to PROGRAM the COMMAND and then
150 # when the result comes back it invokes the script CALLBACK with
151 # one argument appended: the global LOCATION where the result
152 # will be. [uplevel "#0" set $LOCATION] would retrieve it.
154 # Results: returns immediately the location that will be
157 # Side Effects: CALLBACK is invoked later by tracing the
160 #----------------------------------------------------------------
162 proc sendOneDoCommand
{program command callback
} {
165 if { ![assureProgram
$program 5000 2] } { return "cant connect"}
166 set ii
[expr {$pdata($program,currentExpr
) + 1}]
167 catch { unset pdata
($program,results
,$ii)}
168 trace variable pdata
($program,results
,$ii) w
\
169 [list invokeAndUntrace
$callback]
170 sendOne
$program $command
171 return pdata
($program,results
,$ii)
174 proc testit
{ program com
} {
175 sendOneDoCommand
$program $com "jimmy"
176 proc jimmy
{s
} { puts "<result is:[uplevel #0 set $s]>" ; flush stdout
}
179 proc invokeAndUntrace
{ callback name1 name2 op args
} {
180 #puts "callback:$callback $name1 $name2 $op, args=$args"
181 #puts "trace vdelete [set name1]($name2) w [list invokeAndUntrace $callback]"
182 trace vdelete
[set name1
]($name2) w
[list invokeAndUntrace
$callback]
183 lappend callback
[set name1
]($name2)
184 # puts "callback=$callback" ; flush stdout
185 if { [catch { eval $callback } errmsg
] } {
187 # report the error in the background
188 set com
[list error [concat [mc
"had error in"] "$callback:[string range $errmsg 0 300].."] $errorInfo]
193 proc sendOneWait
{ program com
} {
195 if { ![assureProgram
$program 5000 2] } { return "cant connect"}
196 set ii
[expr {$pdata($program,currentExpr
) + 1}]
197 catch { unset pdata
($program,results
,$ii)}
198 sendOne
$program $com
199 set i
$pdata($program,currentExpr
)
200 set socket $pdata($program,socket)
201 if { $ii != $i } { error "expected $ii got $i as expression number " }
202 debugsend
"waiting for pdata($program,results,$i)"
203 myVwait pdata
($program,results
,$i)
204 debugsend
"..done waiting for pdata($program,results,$i)"
205 return $pdata($program,results
,$i)
208 proc closeConnection
{ program
} {
211 set sock
$pdata($program,socket)
212 set pdata
(input
,$sock) ""
221 puts "at: [info level -1]"
222 if { [info level
]>2 } {puts " from:[info level -2 ]"}
226 proc openConnection
{ tohost port magic program
} {
229 set msg
"magic: $magic\n"
231 message [concat [mc
"connecting to"] "nmtp($port)://$tohost/$program"]
232 debugsend
"openConnection { $tohost $port $magic $program }"
234 while { [incr retries
-1] > 0 \
235 && [catch {set socket [openSocketAndSend
$tohost $port $msg 1]}] } {
236 debugsend retries
=$retries
239 if { $retries == 0 } { return 0}
240 message [concat [mc
"connected to"] "nmtp//$tohost:$port/$program"]
241 set pdata
($program,socket) $socket
242 set pdata
($program,currentExpr
) 0
243 set pdata
(input
,$socket) ""
244 catch { fconfigure $socket -blocking 0 }
245 fileevent $socket readable
"getResults $program $socket"
249 proc sendInterrupt
{ program
} {
250 global pdata interrupt_signal
251 set socket $pdata($program,socket)
252 maxStatus
[mc
"Sending socket interrupt"]
253 puts $socket $interrupt_signal
257 proc sendCommand
{ program c
} {
259 set socket $pdata($program,socket)
260 puts $socket "<command:$c>"
264 proc dumpInfo
{program
} {
265 sendCommand
$program dumpInfo
268 proc getResults
{ program
socket } {
269 # debugsend "enter:getResults"
270 global pdata next_command_available next_command results ii
271 if { [eof $socket] } {
273 debugsend
"closed $socket"
275 return "<$program exitted>"
278 if { "[string index $s 0]" != "" } {
279 set s
[append pdata
(input
,$socket) $s]
280 while { [set inds
[testForFayve
$s]] != "" } {
281 set input
$pdata(input
,$socket)
282 # set next_command_available 1
283 debugsend
"input=$input"
284 set gotback
[string range
$input 0 [expr {[lindex $inds 0] -1}]]
285 set index
[lindex $inds 2]
286 set pdata
($program,results
,$index) $gotback
287 if { [string first
"exitted>" $gotback] > 0 } {
292 debugsend
"gotback{$index:$gotback}"
294 [string range
$input [expr {1 + [lindex $inds 1]}] end
]
295 set pdata
(input
,$socket) $s
301 proc cleanPdata
{ program
} {
303 catch { close $pdata($program,socket) }
304 catch { unset pdata
($program,socket) }
305 catch { unset pdata
($program,preeval
) }
307 foreach v
[array names
$program,results
,*] {
313 proc currentTextWinWidth
{ } {
316 set t
[oget
[omPanel .
] textwin
]
317 set width
[expr {round
([winfo width
$t]*1.0 / [font measure
[$t cget
-font] 0]) - 12 }]
322 #-----------------------------------------------------------------
326 # Results: return 2 if the program was already open, and 1 if it is just
327 # now opened. 0 if cant open it.
329 # Side Effects: program is started.
331 #----------------------------------------------------------------
333 proc assureProgram
{ program timeout tries
} {
334 # puts "assure: program=$program"
336 set MathServer
{ some.server.example.org
4443 }
338 if { $tries <= 0 } { return 0}
340 if { [catch { set socket $pdata($program,socket) } ] \
341 ||
[catch { eof $socket}] \
343 ||
[catch { set s
[read $socket]; append pdata
(input
,$socket) $s }] } {
345 message [concat [mc
"connecting"] "[lindex $MathServer 0]"]
346 set msg
"OPEN [programName $program] MMTP/1.0\nLineLength: [currentTextWinWidth]\n\n\n"
347 if {[catch {openSocketAndSend
[lindex $MathServer 0] \
348 [lindex $MathServer 1] "$msg\n"} sock
] } {
349 error [concat [mc
"Can't connect to"] "$MathServer." [mc
"You can try another host by altering Base Program under the \"File\" menu."]]
352 set pdata
($program,currentExpr
) 0
353 fconfigure $sock -blocking 0
354 if { [eof $sock] } {return 0}
355 message [concat [mc
"connected to"] "[lindex $MathServer 0]"]
358 set pdata
(waiting
,$sock) 1
359 set script
"close $sock ; debugsend {after closing} ; set pdata(waiting,$sock) -1"
360 debugsend
"script=$script,timeout=$timeout"
361 set af
[after $timeout $script ]
362 debugsend
"after=$af"
364 debugsend
"waiting pdata(waiting,$sock)=$pdata(waiting,$sock)"
365 # puts "pdata=[array get pdata *$sock* ]"
366 fileevent $sock readable
"if { [eof $sock] } {set pdata(waiting,$sock) -2} else { set pdata(waiting,$sock) 0 ;} ;fileevent $sock readable {} "
367 set pdata
(waiting
,$sock) 1
368 debugsend
"waiting on pdata(waiting,$sock)"
369 myVwait pdata
(waiting
,$sock)
371 debugsend
"..done now pdata(waiting,$sock)=$pdata(waiting,$sock)"
372 if { $pdata(waiting
,$sock) < 0 } {
373 debugsend
"timed out,$pdata(waiting,$sock)"
377 if { "[string index $me 0]" == "" && [eof $sock] } {
378 debugsend
"nothing there"
382 debugsend
"result=<$result>"
383 if { [regexp "RUNNING (\[^ \]+) MMTP\[^\n\]*\nHost: (\[^\n ]+)\nPort: (\[0-9\]+)\nMagic: (\[^\n \]+)\n" \
384 $result junk prog tohost port magic
] } {
386 debugsend
"doing openConnection $tohost $port $magic $program"
388 return [openConnection
$tohost $port $magic $program]
391 } elseif
{ [eof $socket] } {
393 unset pdata
($program,socket)
394 return [assureProgram
$program $timeout [expr {$tries -1}]]
401 # name may look like "maxima#1.2"
402 proc programName
{ name
} {
403 set name
[file tail
$name]
404 return [lindex [split $name #] 0]
407 proc getMatch
{ s inds
} {
408 return [string range
$s [lindex $inds 0] [lindex $inds 1]]
411 proc testForFayve
{ input
} {
412 set EOFexpr
"|fayve>"
413 set ind
[string first
$EOFexpr $input]
414 if { $ind < 0 } { return "" } else {
415 regexp -indices {<([0-9]+)\|fayve
>} $input all first
416 set n
[getMatch
$input $first]
421 #### the following is correct but just a fair bit slower.. ####
422 ##### because of all the arguments to be parsed for the other..
423 proc statServer1
{server
{timeout
1000}} {
426 if { ![catch { set s
[eval socket $server]} ] } {
427 puts $s "STAT MMTP/1.0\n" ; flush $s
428 if { [readAllData
$s -tovar statServer
(data
) \
429 -mimeheader statServer
(header
) -timeout $timeout ] > 0 } {
430 set head
$statServer(header
)
431 # puts "data=<$statServer(data)>"
432 set res
$statServer(header
)\n\n$statServer(data
)
440 #-----------------------------------------------------------------
442 # needToDo -- Check if we have already done OPERATION for NAME into data
444 # Results: returns 0 if the data for name is not preloaded, and 1 otherwise
446 # Side Effects: adds NAME to those preloaded for PROGRAM if not there
448 #----------------------------------------------------------------
450 proc preeval
{ program name
} {
452 assureProgram
$program 5000 2
453 if { ![info exists pdata
($program,preeval
)] ||
\
454 [lsearch $pdata($program,preeval
) $name] < 0 } {
455 lappend pdata
($program,preeval
) $name
462 proc statServer
{server
{timeout
1000}} {
465 if { ![catch { set s
[eval socket $server]} ] } {
466 puts $s "STAT MMTP/1.0\n" ; flush $s
467 if { [readDataTilEof
$s data
$timeout ] } {
468 foreach v
{ jobs currentjobs
} {
469 if { [regexp "\n$v: (\[^\n]*)\n" $data junk val
] } {
478 proc isAlive1
{ s
} {
480 if { [catch { read $s } ] } {
481 set maxima_priv
(isalive
) -1
483 set maxima_priv
(isalive
) 1
488 proc isAlive
{ server
{timeout
1000} } {
491 if { [ catch { set s
[eval socket -async $server] } ] } { return -1 }
492 set maxima_priv
(isalive
) 0
493 fconfigure $s -blocking 0
494 fileevent $s writable
"isAlive1 $s"
495 set c1
"set maxima_priv(isalive) -2"
496 set after_id
[after $timeout $c1]
497 myVwait maxima_priv
(isalive
)
499 after cancel
$after_id
500 return $maxima_priv(isalive
)
503 proc debugsend
{ s
} {