class: TerminalSession
authorClaus Gittinger <cg@exept.de>
Fri, 16 Aug 2013 17:03:36 +0200
changeset 3087 21384929202f
parent 3086 a6a426ef9950
child 3088 52f83256a664
class: TerminalSession class definition added:7 methods changed: #checkForPrompt:count: #readAnyAvailableData
TerminalSession.st
--- a/TerminalSession.st	Fri Aug 16 16:32:49 2013 +0200
+++ b/TerminalSession.st	Fri Aug 16 17:03:36 2013 +0200
@@ -4,7 +4,8 @@
 	instanceVariableNames:'inStream outStream errStream readerProcess shellPid shellCommand
 		shellDirectory readerDelay pluggableCheckBeforeReadAction
 		pluggableProcessInputAction execFDArray stxToStdinPipe
-		stdOutToStxPipe pty ptyName terminatedAction'
+		stdOutToStxPipe pty ptyName terminatedAction collectedOutput
+		expectedPrompt promptAction'
 	classVariableNames:'Debug'
 	poolDictionaries:''
 	category:'Views-TerminalViews'
@@ -350,6 +351,10 @@
 
 !TerminalSession methodsFor:'misc'!
 
+collectedOutput
+    ^ collectedOutput
+!
+
 defineWindowSizeLines:numberOfLines columns:numberOfColumns
     | delta prevNumCols prevNumLines|
 
@@ -385,6 +390,65 @@
     "Modified: / 5.5.1999 / 19:45:09 / cg"
 !
 
+onPrompt:aString do:aBlock
+    "remember what to do, when a prompt arrives;
+     notice: will only start checking for prompt, when startCollectingOutput
+     has been called."
+
+    promptAction := aBlock.
+    expectedPrompt := aString.
+!
+
+outputFromAction:aBlock prompt:prompt timeout:seconds
+    "evaluate aBlock and wait for the prompt.
+     return gdb output as string collection"
+
+    |sema output|
+
+    sema := Semaphore new.
+    self startCollectingOutput.
+    self onPrompt:prompt do:[:strings | output := strings. sema signal. ].
+
+    aBlock value.
+
+    (sema waitWithTimeout:seconds) isNil ifTrue:[
+        self information:'Error: command timeout.'.
+        self stopCollectingOutput.
+        self onPrompt:nil do:nil.
+        AbortOperationRequest raise.
+    ].
+    output first isEmpty ifTrue:[
+        "/ self halt.
+        output := output copyFrom:2
+    ].
+    ^ output
+!
+
+outputFromCommand:aCommand prompt:prompt timeout:seconds
+    "return a command's output as string collection"
+
+    |output firstLine|
+
+    output := self 
+                outputFromAction:[ self sendLine:aCommand ]
+                prompt:prompt timeout:seconds.
+
+    "/ the first line of output is the echo
+    firstLine := output first withoutLeadingSeparators.
+    firstLine ~= aCommand ifTrue:[
+        OperatingSystem isMSWINDOWSlike ifFalse:[
+            "/ sigh - it is sometimes truncated (to be investigated)
+            (aCommand startsWith:firstLine) ifTrue:[
+                self halt.
+                ^ output.
+            ].
+            self halt.
+        ].
+        ^ output.
+    ].
+    ^ output copyFrom:2
+!
+
 sendInterruptSignal
     "send an INT-signal to the shell (UNIX only)"
 
@@ -401,10 +465,52 @@
     shellPid notNil ifTrue:[
         OperatingSystem sendSignal:(OperatingSystem sigKILL) to:shellPid negated.
     ]
+!
+
+startCollectingOutput
+    collectedOutput := '' writeStream.
+!
+
+stopCollectingOutput
+    collectedOutput := nil.
 ! !
 
 !TerminalSession methodsFor:'reader process'!
 
+collectOutputAndCheckForPrompt:buffer count:n
+    |string collectedString collectedLines i lastLine|
+
+    collectedOutput isNil ifTrue:[^ self].
+
+    string := buffer copyTo:n.
+    collectedOutput nextPutAll:string.
+
+    promptAction notNil ifTrue:[
+        collectedString := collectedOutput contents.
+
+        i := collectedString lastIndexOf:Character lf.
+        i ~= 0 ifTrue:[
+            lastLine := (collectedString copyFrom:i+1) withoutTrailingSeparators.
+            ((lastLine endsWith:expectedPrompt) 
+            or:[ (lastLine startsWith:expectedPrompt) ]) ifTrue:[
+                "/ ('found prompt; call ',promptAction printString) printCR.
+
+                "/ perform the promptaction
+                collectedLines := collectedString asStringCollection 
+                                    collect:[:each | 
+                                        (each endsWith:String crlf) 
+                                                ifTrue:[ each copyButLast:2 ]
+                                                ifFalse:[
+                                                    (each endsWith:Character return) 
+                                                        ifTrue:[ each copyButLast:1 ]
+                                                        ifFalse:[ each ]]].
+                collectedLines removeLast.  "/ the prompt itself
+                promptAction value: collectedLines.
+            ]
+        ].
+    ].
+!
+
 readAnyAvailableData
     "read data from the stream,
      and sends me #processInput:n: events if something arrived.
@@ -422,6 +528,9 @@
     ] do:[
         n := outStream nextAvailableBytes:bufferSize into:buffer startingAt:1.
         n > 0 ifTrue:[
+            collectedOutput notNil ifTrue:[
+                self collectOutputAndCheckForPrompt:buffer count:n
+            ].
             pluggableProcessInputAction notNil ifTrue:[
                 pluggableProcessInputAction value:buffer value:n.
             ]
@@ -511,11 +620,11 @@
 !TerminalSession class methodsFor:'documentation'!
 
 version
-    ^ '$Header: /cvs/stx/stx/libbasic2/TerminalSession.st,v 1.8 2013-08-16 12:39:29 cg Exp $'
+    ^ '$Header: /cvs/stx/stx/libbasic2/TerminalSession.st,v 1.9 2013-08-16 15:03:36 cg Exp $'
 !
 
 version_CVS
-    ^ '$Header: /cvs/stx/stx/libbasic2/TerminalSession.st,v 1.8 2013-08-16 12:39:29 cg Exp $'
+    ^ '$Header: /cvs/stx/stx/libbasic2/TerminalSession.st,v 1.9 2013-08-16 15:03:36 cg Exp $'
 ! !