previous next top contents index framed top this page unframed
Redirecting terminal I/O within MAINSAIL is done through a general-purpose mechanism. The MAINSAIL editor uses this mechanism, and your program may use it also.
Redirecting terminal I/O is useful for programs running under a window manager but which may nonetheless occasionally interact with the user through line-oriented terminal I/O (e.g., because an error has occurred within MAINSAIL code and the system PROCEDURE errMsg has issued the Error response: prompt). You can arrange to pop up a window under your program's control where the terminal interaction takes place, instead of having the interaction take place in the window (e.g., a UNIX shell window) from which the program was originally invoked. Allowing the interaction to take place in the shell window is often inconvenient because your program's windows may have obscured the shell window, and the user may not be aware that your program has issued a prompt to which he or she is expected to respond.
23.1. $ttyCls and $ttyMod
To capture terminal I/O within MAINSAIL, you need to write a MODULE
of the CLASS $ttyCls ($ttyCls is predefined by MAINSAIL, so
do not redeclare it):
CLASS $ttyCls (
STRING
PROCEDURE $ttyRead (OPTIONAL POINTER($area)
area);
PROCEDURE $sttyWrite (REPEATABLE STRING s);
PROCEDURE $ttycWrite (REPEATABLE INTEGER ch);
LONG BITS $ttyAttributes;
# e.g., $canReclaimStrings or $usesEditWindow
);
and set the system variable $ttyMod to point to a data section for your MODULE. $ttyMod is declared as:
POINTER($ttyCls) $ttyMod; # system variable
It is possible for $ttyMod to be NULLPOINTER; MAINSAIL initially
comes up with $ttyMod equal to NULLPOINTER.
This means that terminal I/O should go through the direct terminal
I/O PROCEDUREs, as described in Section 23.3.
If $sttyWrite stores the STRINGs
passed to it in a data structure that
lasts after $sttyWrite returns (e.g., by storing written
STRINGs into an ARRAY
representing a transcript of the terminal
session), then you should use the
PROCEDURE $getInArea to ensure that the input
STRING is in $defaultArea.
For MODULEs where this is true, $ttyAttributes
should not have the
$canReclaimStrings bit set (see below).
For MODULEs where $sttyWrite never hangs on to
STRINGs passed to it,
the $canReclaimStrings bit should be set in $ttyAttributes.
If you do not set $canReclaimStrings, it is your responsibility to
make $sttyWrite
call $getInArea for any input STRING s that it holds on to.
For example, if you store s into an ARRAY myArray:
you must use $getInArea
because input STRINGs can have text in areas allocated
by programs; the programs can dispose the areas at any time, immediately
invalidating all STRINGs with text located in the areas.
23.2. How to Write a $ttyCls MODULE
There are not many restrictions on what a $ttyCls MODULE can do;
in particular, it is permissible for a $ttyCls MODULE to cause a
garbage collection.
However, you should make sure that the $ttyCls MODULE displays its
output in a place clearly visible to the user so that the user
understands that terminal interaction is taking place.
23.2.1. How to Write $ttyCls.$ttyRead
The $ttyRead interface PROCEDURE of a $ttyCls MODULE should read
and return the next line of input available from the redirected
terminal.
If area is non-Zero, you should put the text read into the specified
area if new text is generated.
You should not include the character or characters that terminated
the input line (eol characters) in the result STRING.
23.2.2. How to Write $ttyCls.$sttyWrite
and $ttyCls.$ttycWrite
$sttyWrite and $ttycWrite write a
STRING or character to the redirected
terminal.
$sttyWrite should not normally append any additional end-of-line
characters.
23.2.3. How to Set $ttyCls.$ttyAttributes
Two bits can be set in $ttyAttributes:
myArray[i] := $getInArea(s);
23.3. Direct Terminal I/O
It is sometimes useful to force I/O directly to the original terminal
with which MAINSAIL started up.
For example, if you have a bug in a $ttyCls MODULE to which you have made $ttyMod point, the MAINSAIL debugger is not much help, since it attempts to do I/O through the buggy MODULE. You can debug such a MODULE by adding calls to the direct terminal I/O PROCEDUREs below.
Another case where you may want to use direct terminal I/O is when you want to print out debugging information to the terminal, but because you are using low-level memory manipulation, your program is in a state where garbage collections must not occur. Since redirected terminal I/O may trigger collections, you should use the direct terminal I/O PROCEDUREs, which cannot trigger collections.
Yet another case is where you have written a MODULE that calls a $ttyCls MODULE directly, and you want to deal with the case where that $ttyCls MODULE's POINTER is NULLPOINTER by calling the direct terminal I/O PROCEDUREs, just like the system PROCEDUREs ttyRead, ttyWrite, and ttycWrite.
Aside from the cases above, your program is unlikely to use direct terminal I/O; it is a low-level feature that should not often be necessary.
The direct terminal I/O PROCEDUREs are:
PROCEDURE $directTtycWrite
(REPEATABLE INTEGER char);
STRING
PROCEDURE $directTtyRead
(OPTIONAL POINTER($area) strArea);
PROCEDURE $directTtyWrite
(REPEATABLE STRING s);
PROCEDURE $directTtyWrite
(REPEATABLE LONG INTEGER li);
PROCEDURE $directTtyWrite
(REPEATABLE LONG BITS lb);
$directTtycWrite and $directTtyRead behave exactly like ttycWrite and ttyRead, except that they cannot be redirected and they cannot trigger garbage collections.
The instances of
$directTtyWrite
behave exactly like the
corresponding instances of ttyWrite,
except that they cannot be redirected
and they cannot trigger garbage collections.
$directTtyWrite has not been implemented for as many data types as
ttyWrite; only the data types XIDAK thought most useful were
implemented.
23.4. Sample Use of Terminal I/O Redirection and Direct Terminal
I/O
Figure 23–2 shows a $ttyCls MODULE SCRIPTTTY that keeps a
record of all terminal interaction in a file;
it has an additional interface PROCEDURE shown in
Figure 23–1.
SCRIPTTTY talks through another $ttyCls MODULE, which may be
NULLPOINTER, in which case SCRIPTTTY uses the
direct terminal I/O PROCEDUREs.
Figure 23–3 shows a program that uses SCRIPTTTY to record terminal I/O to an inferior MAINSAIL executive.
Figure 23–1. Intmod with SCRIPTTTY Interface
| BEGIN "scriptTtyHdr" # Interface for $ttyCls module SCRIPTTTY. $DIRECTIVE "NOOUTPUT"; SAVEON; CLASS($ttyCls) scriptTtyCls ( # This module has an additional interface PROCEDURE, # setOutputTtyClsModule. # The parameter outTty is used as the $ttyCls module # through which I/O ultimately goes. # SCRIPTTTY just acts as an intermediate between the # program and outTty, capturing input and output. # It is an error to call any other interface PROCEDURE # in this module before setting outTty, although it # is permissible to set outTty to NULLPOINTER (meaning # use direct terminal I/O). PROCEDURE setOutputTtyClsModule (POINTER($ttyCls) outTty); ); MODULE(scriptTtyCls) scriptTty; END "scriptTtyHdr" |
Figure 23–2. SCRIPTTTY: $ttyCls MODULE That Maintains a Transcript
| BEGIN "scriptTty" # $ttyCls module that records all its terminal interaction # in a transcript file. RESTOREFROM "scriptTtyHdr"; BOOLEAN outTtySet; POINTER(textFile) f; # The transcript file POINTER($ttyCls) theOutTty; # target $ttyCls module PROCEDURE checkOutTty; IF NOT outTtySet THEN errMsg("scriptTty: outTty not set","",fatal); STRING PROCEDURE $ttyRead (OPTIONAL POINTER($area) area); BEGIN STRING s; checkOutTty; s := IF theOutTty THEN theOutTty.$ttyRead(area) EL $directTtyRead(area); write(f,s,eol); RETURN(s); END; PROCEDURE $sttyWrite (REPEATABLE STRING s); BEGIN checkOutTty; IF theOutTty THEN theOutTty.$sttyWrite(s) EL $directsTtyWrite(s); write(f,s); END; PROCEDURE $ttycWrite (REPEATABLE INTEGER ch); BEGIN checkOutTty; IF theOutTty THEN theOutTty.$ttycWrite(ch) EL $directTtycWrite(ch); cWrite(f,ch); END; PROCEDURE setOutputTtyClsModule (POINTER($ttyCls) outTty); BEGIN theOutTty := outTty; outTtySet := TRUE; $ttyAttributes := IF outTty THEN outTty.$ttyAttributes EL '0L; # Because each of our PROCEDUREs turns around and # calls the corresponding outTty PROCEDURE, our # characteristics are the same as outTty's. END; INITIAL PROCEDURE; open(f,"Transcript file: ",prompt!create!output); FINAL PROCEDURE; close(f); END "scriptTty" |
Figure 23–3. ST: Module That Uses SCRIPTTTY
| BEGIN "st" # scriptTty test RESTOREFROM "scriptTtyHdr"; INITIAL PROCEDURE; BEGIN POINTER(scriptTtyCls) stp; POINTER($ttyCls) oldTty; stp := new(scriptTty); # scriptTty will ask for transcript file name stp.setOutputTtyClsModule(oldTty := $ttyMod); # ultimate destination for terminal I/O is current # terminal write(logFile,eol & "*** PUSH ***" & eol & eol); # let user know we'll enter a subsidiary MAINSAIL # executive $ttyMod := stp; # capture all output with scriptTty $HANDLE $mainsailExec $WITHB IF $exceptionName = $abortProcedureExcpt THEN $ttyMod := oldTty; # restore before we lose control $raise END; $ttyMod := oldTty; # now restore the previous $ttyMod write(logFile,eol & "*** POP ***" & eol & eol); # let user know we're back from the subsidiary # MAINSAIL executive dispose(stp); # scriptTty now closes transcript file END; END "st" |