8.9 Standard Channel I/O in ISO Modula-2

As has already been observed, in ISO standard Modula-2, the modules SWholeIO, SRealIO, SLongIO, and STextIO operate on the standard channels. These are defined in the Module StdChans:

DEFINITION MODULE StdChans;
 
IMPORT IOChan;
 
TYPE
  ChanId = IOChan.ChanId;
    (* Values of this type are used to identify channels *)
 
  (* The following functions return the standard channel values.
     These channels cannot be closed. *)

PROCEDURE StdInChan (): ChanId;
  (* Returns the identity of the implementation-defined standard source for program input. *)
 
PROCEDURE StdOutChan (): ChanId;
  (* Returns the identity of the implementation-defined standard source for program output. *)
 
PROCEDURE StdErrChan (): ChanId;
  (* Returns the identity of the implementation-defined standard destination for program error messages. *)
 
PROCEDURE NullChan (): ChanId;
  (* Returns the identity of a channel open to the null device. *)
 
  (* The following functions return the default channel values *)
 
PROCEDURE InChan (): ChanId;
  (* Returns the identity of the current default input channel. *)
 
PROCEDURE OutChan (): ChanId;
  (* Returns the identity of the current default output channel. *)
 
PROCEDURE ErrChan (): ChanId;
  (* Returns the identity of the current default error message channel. *)
 
 (* The following procedures allow for redirection of default channels *)
 
PROCEDURE SetInChan (cid: ChanId);
  (* Sets the current default input channel to that identified by cid. *)
 
PROCEDURE SetOutChan (cid: ChanId);
  (* Sets the current default output channel to that identified by cid. *)
 
PROCEDURE SetErrChan (cid: ChanId);
  (* Sets the current default error channel to that identified by cid. *)
 
END StdChans.

Observe that there is a third channel besides that for input and output. The error channel may be the same as the standard output, or it may be a file that logs errors. This channel is available to the programmer as well:

IF Error
  THEN
    TextIO.WriteString (StdChans.ErrChan(), theMessage)
  END;

It is also easy to see how the non-standard module RedirStdIO that was employed earlier in the text can be written on top of this standard one. It is only necessary to open new channels through StreamFile and then employ the procedures of StdChans to redirect the current default channel. The definition, and an implementation for the Macintosh operating system follow. Of course, the specific details that relate to the manner in which the file name is obtained before being passed to be opened will differ on other operating systems.

  
 DEFINITION MODULE RedirStdIO;

(* =========================================
  Definition and Implementation © 1993-1997
                by R. Sutcliffe
        Trinity Western University
7600 Glover Rd., Langley, BC Canada V3A 6H4
         e-mail: rsutc@twu.ca
    Last modification date 1997 07 02
=========================================== *)

IMPORT ChanConsts;

TYPE
  OpenResults = ChanConsts.OpenResults;
	
PROCEDURE OpenResult () : OpenResults;
(* returns the result of the last attempt to open a file for redirection *)

PROCEDURE OpenOutput;
(* engages the user in a dialog to obtain a file for redirection of standard Output and attempts to open the file so obtained *)

PROCEDURE OpenOutputFile (VAR fileName: ARRAY OF CHAR);
(* opens the file specified by fileName for redirection of output.  If the name supplied is the empty string or the file could not be opened, control passes to OpenOutput and the filename eventually used is returned in the parameter. *)

PROCEDURE CloseOutput;
(* returns the standard output channel to the default value *)

PROCEDURE OpenInput;
(* engages the user in a dialog to obtain a file for redirection of standard Input and attempts to open the file so obtained *)

PROCEDURE OpenInputFile (VAR fileName: ARRAY OF CHAR);
(* Opens the file specified by fileName for redirection of input.  If the name supplied is the empty string or is not found, control passes to OpenInput and the filename eventually used is returned in the parameter. *)

PROCEDURE CloseInput;
(* returns the standard input channel to the default value *)

END RedirStdIO.

IMPLEMENTATION MODULE RedirStdIO;

(* =========================================
  Definition and Implementation © 1993-1997
                by R. Sutcliffe
        Trinity Western University
7600 Glover Rd., Langley, BC Canada V3A 6H4
         e-mail: rsutc@twu.ca
    Last modification date 1999 07 22
=========================================== *)

(* First cut was October 1993
1994 05 21 modified to p1 version 
  change some type imports
  use put get File rather than File0
  change string handling accordingly 
  change to proper name "Strings" 
  1996 08 08 removed extraneous local var from OpenOutput
  1997 07 08 added OpenOutputFile and OpenInputFile *)

(* Mac Specific *)
FROM StandardFile IMPORT
  SFReply, SFTypeList, SFPutFile, SFGetFile;
FROM MacTypes IMPORT
  Str255, Str63, Point;
FROM Strings IMPORT
  Assign;
FROM StdChans IMPORT
  SetOutChan, StdOutChan, SetInChan, StdInChan;
IMPORT ChanConsts;
FROM ChanConsts IMPORT
  read, write, old; 
FROM  StreamFile IMPORT
  ChanId, Open, Close;
FROM SYSTEM IMPORT
  TOSTR255, FROMSTR255;
FROM MacString IMPORT
  MacToModString;
TYPE
  ModStr255 = ARRAY [0..255] OF CHAR;
  
VAR
  out, in : ChanId ;
  count : CARDINAL;
  lastOpenResult : OpenResults;
  inRedir, outRedir : BOOLEAN;

(*--------------------------------------------------------------------------------*)

PROCEDURE InternalOpenOutput (VAR fileName : ARRAY OF CHAR);
(* engages the user in a dialog to obtain a file for redirection of standard Output and attempts to open the file so obtained *)

VAR
  result : OpenResults;
  defFileName, thePrompt : Str255;
  p : Point;
  sfreply : SFReply;
  mTempStr : ModStr255;
  gottaAsk : BOOLEAN;
  
BEGIN
  gottaAsk:=TRUE;
  IF fileName[0] <> "" (* file name non-empty? *)
    THEN (* go try it *)
      Open (out, fileName, write+old, result);
      gottaAsk:= NOT (result = ChanConsts.opened)
    END; (* if *)
  IF gottaAsk (* true if filename was empty or not found *)
    THEN
      thePrompt := TOSTR255 ("Output File?"); (* get dialog box prompt ready *)
      defFileName := TOSTR255 ("output"); (* and default file name *)
      SFPutFile (p, thePrompt, defFileName, NIL,sfreply); (* and go try with dialog box *)
      IF sfreply.good  (* got it OK *)
        THEN (* convert filename returned to Modula string *)
          MacToModString (sfreply.fName, mTempStr);
          Assign (mTempStr,fileName);
          Open (out, mTempStr, write+old, result);
        END;
    END;
  lastOpenResult := result;
  IF result = ChanConsts.opened
    THEN
      SetOutChan (out);
      outRedir := TRUE;
    ELSE
      fileName[0]:= "";
    END;  (* if didn't work, nothing is done *)
END InternalOpenOutput;

(*--------------------------------------------------------------------------------*)

PROCEDURE OpenResult () : OpenResults;
(* returns the result of the last attempt to open a file for redirection *)

BEGIN
  RETURN lastOpenResult;
END OpenResult;

(*--------------------------------------------------------------------------------*)

PROCEDURE OpenOutput;
(* engages the user in a dialog to obtain a file for redirection of standard Output and attempts to open the file so obtained *)
  
VAR
  DummyFile : ARRAY [0..1] OF CHAR;

BEGIN
  DummyFile[0]:= "";
  InternalOpenOutput (DummyFile);
END OpenOutput;

(*--------------------------------------------------------------------------------*)

PROCEDURE OpenOutputFile (VAR fileName : ARRAY OF CHAR);
(* opens the file specified by FileName for redirection of output.  If the name supplied is the empty string or the file could not be opened, control passes to OpenOutput and the filename eventually used is returned in the parameter. *)
  
BEGIN
  InternalOpenOutput (fileName);
END OpenOutputFile;

(*--------------------------------------------------------------------------------*)

PROCEDURE CloseOutput;
(* returns the standard output channel to the default value *)

BEGIN
  IF outRedir
    THEN
      Close (out);
      SetOutChan (StdOutChan () );
      outRedir := FALSE;
    END;
END CloseOutput;

(*--------------------------------------------------------------------------------*)

PROCEDURE InternalOpenInput (VAR fileName : ARRAY OF CHAR);
(* This procedure is where all the work gets done.  Engages the user in a dialog to obtain a file for redirection of standard Output and attempts to open the file so obtained *)

VAR
  result : OpenResults;
  p : Point;
  sfreply : SFReply;
  mTempStr : ModStr255;
  gottaAsk : BOOLEAN;
  SFt: SFTypeList;
  
BEGIN
  gottaAsk:=TRUE;
  IF fileName[0] <> ""  (* file name non-empty? *)
    THEN  (* go try it *)
      Open (in, fileName, old+read, result);
      gottaAsk:= NOT (result = ChanConsts.opened)
    END; (* if *)
  IF gottaAsk  (* true if filename was empty or not found *)
    THEN
      SFGetFile (p, "", NIL, -1, SFt, NIL, sfreply);  (* go try with dialog box *)
      IF sfreply.good (* got it OK *)
        THEN (* convert filename returned to Modula string *)
          MacToModString (sfreply.fName, mTempStr);
          Assign (mTempStr,fileName);
          Open (in, mTempStr, old+read, result);
        END;
    END;
  lastOpenResult := result; (* from one or the other *)
  IF result = ChanConsts.opened
    THEN
      SetInChan(in);
      inRedir := TRUE;
    ELSE
      fileName[0]:= "";
    END;  (* if didn't work, nothing is done *)
END InternalOpenInput;

(*--------------------------------------------------------------------------------*)

PROCEDURE OpenInput;
(* engages the user in a dialog to obtain a file for redirection of standard Input and attempts to open the file so obtained *)
  
VAR DummyFile : ARRAY [0..1] OF CHAR;

BEGIN
  DummyFile[0]:= "";
  InternalOpenInput (DummyFile);
END OpenInput;

(*--------------------------------------------------------------------------------*)

PROCEDURE OpenInputFile (VAR fileName : ARRAY OF CHAR);
(* This is the procedure that does all the work. Opens the file specified by fileName for redirection of input.  If the name supplied is the empty string or is not found, control passes to OpenInput and the filename eventually used is returned in the parameter. *)
  
BEGIN
  InternalOpenInput (fileName);
END OpenInputFile;

(*--------------------------------------------------------------------------------*)

PROCEDURE CloseInput;
(* returns the standard input channel to the default value *)

BEGIN
  IF inRedir
    THEN
      Close (in);
      SetInChan (StdInChan () );
      inRedir := FALSE;
    END;
END CloseInput;

BEGIN (* main program *)
  lastOpenResult := ChanConsts.otherProblem;
  inRedir := FALSE;
  outRedir := FALSE;
END RedirStdIO.

In this system, the dialog boxes that are standard to the Macintosh operating system are presented to the user so that folders can be navigated in the usual way to obtain the file name. In a text based operating system, these sections would have to be replaced with a dialog to the interactive terminal where the user types in a file name.


Contents