MAINSAIL Language Manual, Chapter 23

previous   next   top   contents   index   framed top   this page unframed


23. Intercepting Terminal I/O

MAINSAIL terminal I/O can be redirected in two ways: from the operating system, and from within MAINSAIL. It is possible to redirect MAINSAIL terminal I/O at the operating system level only on operating systems that support it (e.g., by using the < and > operators to the UNIX shell).

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.

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.

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.

23.2.3. How to Set $ttyCls.$ttyAttributes

Two bits can be set in $ttyAttributes:

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($areastrArea);

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($ttyClsscriptTtyCls (

    # 
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 outTtycapturing input and output.
    # 
It is an error to call any other interface PROCEDURE
    # 
in this module before setting outTtyalthough it
    # 
is permissible to set outTty to NULLPOINTER (meaning
    # 
use direct terminal I/O).

    
PROCEDURE setOutputTtyClsModule
        (
POINTER($ttyClsoutTty);

);

MODULE(scriptTtyClsscriptTty;

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(textFilef; # The transcript file
POINTER($ttyClstheOutTty; # target $ttyCls module

PROCEDURE checkOutTty;
IF NOT outTtySet THEN
    
errMsg("scriptTtyoutTty not set","",fatal);



STRING PROCEDURE $ttyRead (OPTIONAL POINTER($areaarea);
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($ttyClsoutTty);
BEGIN
theOutTty := outTtyoutTtySet := TRUE;
$ttyAttributes :=
        
IF outTty THEN outTty.$ttyAttributes
        
EL '0L;
    # 
Because each of our PROCEDUREs turns around and
    # 
calls the corresponding outTty PROCEDUREour
    # 
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(scriptTtyClsstp;
POINTER($ttyClsoldTty;

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"


previous   next   top   contents   index   framed top   this page unframed

MAINSAIL Language Manual, Chapter 23