Google
 

Trailing-Edge - PDP-10 Archives - decuslib10-05 - 43,50337/23/safeio.sim
There is 1 other file named safeio.sim in the archive. Click here to see a list.
OPTIONS(/E/-A/-Q/-I/-D/C/P:"SAFEIO - System");

EXTERNAL REF (Infile) PROCEDURE findinfile;
EXTERNAL REF (Outfile) PROCEDURE findoutfile;
EXTERNAL TEXT PROCEDURE conc,upcase,frontstrip,rest,checkextension;
EXTERNAL CHARACTER PROCEDURE fetchar,findtrigger;
EXTERNAL LONG REAL PROCEDURE scanreal;
EXTERNAL INTEGER PROCEDURE checkreal,checkint,scanint,ilog;
EXTERNAL BOOLEAN PROCEDURE menu;
COMMENT --- CLASS SAFEIO --- Version 4.0
Date: 76-01-09
Author: Mats Ohlin
Swedish Research Institute of National Defence
FOA 1
Fack
S-104 50 STOCKHOLM 80
SWEDEN

The information in this document is subject to change without
notice. The institute assumes no responsibility for any errors that
may be present in this document. The described software is furnished
to the user for use on a SIMULA system. (SIMULA is a registered
trademark of the Norwegian Computing Center, Oslo, Norway).

Copyright 1975 by the Swedish Research Institute for National Defence.
Copying is allowed.
----------------------------------------------------------------------

SAFEIO is a SIMULA class which is designed to faciliate the
programming of conversational parts of SIMULA programs.
For more information, see SAFEIO.HLP and SAFEIO.DOC.
;

CLASS safeio(savefilename,language);   VALUE savefilename,language;
TEXT savefilename,language;
VIRTUAL: PROCEDURE special;   LABEL eof;
BEGIN



    PROCEDURE printint(i);   INTEGER i;
    COMMENT Printint prints the integer i without leading spaces
    on Sysout in Putfrac(i,0) format. ;
    BEGIN
	Outtext(fracput(i))
    END of printint;
    	
    PROCEDURE printreal(x);   REAL x;
    COMMENT Printreal prints the value of the real variable x
    without leading spaces.
    If Abs(x) is in the range (E-4,E8) the fixed point format will
    be used so that 8 significant digits are typed out. Else the
    Putreal format with 8 significant digits will be used. ;
    BEGIN   Outtext(realput(x));  END of printreal;

    	
    TEXT PROCEDURE fracput(i);   INTEGER i;
    COMMENT Fracput returns a text containing
    the value of the integer i without leading spaces
    in Putfrac(i,0) format. ;
    BEGIN   u.Putfrac(i,0);
	fracput:- Copy(frontstrip(u))
    END of fracput;

    TEXT PROCEDURE intput(i);   INTEGER i;
    COMMENT Intput returns a text containing
    the value of the integer i without leading spaces. ;
    BEGIN    u.Putint(i);
	intput:- Copy(frontstrip(u))
    END of intput;

    TEXT PROCEDURE realput(x);   REAL x;
    BEGIN
	IF x = 0 THEN u.Putfix(x,0) ELSE
	IF Abs(x) >= &8 THEN u.Putreal(x,8) ELSE
	IF Abs(x) >= &-4 THEN u.Putfix(x,8-ilog(x)) ELSE
	u.Putreal(x,8);
	realput:- Copy(frontstrip(u))
    END of realput;

    PROCEDURE outline(t);   VALUE t;   TEXT t;
    BEGIN
	WHILE t.Length > Length DO
	BEGIN   Outtext(t.Sub(1,Length));
	    t:- t.Sub(Length+1,t.Length-Length)
	END loop;
	Outtext(t);   Outimage;
    END of outline;

    BOOLEAN PROCEDURE irange(test,low,high);   INTEGER test,low,high;
    irange:= low <= test AND test <= high;

    BOOLEAN PROCEDURE range(test,low,high);   REAL test,low,high;
    range:= low <= test AND test <= high;

    TEXT PROCEDURE outofrange(low,high);   REAL low,high;
    outofrange:- conc(message[83],realput(low),
    ",",realput(high),"].");

    TEXT PROCEDURE outofirange(low,high);   INTEGER low,high;
    outofirange:- conc(message[83],intput(low),
    ",",intput(high),"].");

    BOOLEAN PROCEDURE commandhelp(table,n);   TEXT ARRAY table;   INTEGER n;
    BEGIN  INTEGER i;
	Outtext(message[84]);   Outimage;
	FOR i:= 1 STEP 1 UNTIL n DO
	BEGIN   Outtext(table[i]);
		IF Pos < Length//2 THEN Setpos(Length//2)
		ELSE Outimage;
	END;
	Outimage
    END of commandhelp;

    TEXT PROCEDURE commandmessage(index);   INTEGER index;
    commandmessage:-
    IF index = 0 THEN message[85] ELSE message[86];

    PROCEDURE special;
    ! The programmer may specify his own procedure special (with exactly
    ! that name and no parameter. Since it is virtual the local procedure
    ! will be called when the user types in '!%'.
    ! Note that the programmer may implement code for analysing the rest
    ! of the currentfile.image following the '!%'.
    ! Note also that the special procedure may call other procedures
    ! which in turn may have parameters.
    ! This declaration has the sole purpose of avoiding run time error
    ! ("No virtual match") if the programmer hasn't declared his
    ! own special procedure. ;
    BEGIN   Outtext(message[1]);   Outimage   END;

    PROCEDURE cmdclose;
    ! This procedure closes all open input SAFEIO files.
    ! If no input file is used a message is printed. ;
    IF currentitem.file == Sysin THEN
    BEGIN   Outtext(message[2]);   Outimage   END ELSE
    WHILE currentitem.file =/= Sysin DO currentitem.down;

    PROCEDURE recordclose;
    ! This procedure will close the recording (log) file.
    ! If no recordfile is open no action is taken. ;
    BEGIN
	IF recordfile =/= NONE THEN
	BEGIN
	    IF trace THEN
	    BEGIN   Outtext(message[3]);   Outtext(recordname);
		Outchar(']');   Outimage;
	    END trace;
	    recordfile.Setpos(1);
	    recordfile.Close;   recordfile:- NONE
	END;

    END of recordclose;

    PROCEDURE recordappend;
    ! This procedure closes the current recording file and opens it
    ! again in append mode. The programmer may insert class to recordappend
    ! whenever he wants this kind of checkpoint. The user may call the
    ! procedure by typing in '!+'. ;
    IF recordfile == NONE THEN
    BEGIN   Outtext(message[4]);
	Outimage
    END ELSE
    BEGIN   recordfile.Setpos(1);   recordfile.Close;
	recordfile:- NEW Outfile(conc(message[5],recordname,message[6]));
	recordfile.Open(record_buffer);
	IF trace THEN
	BEGIN   Outtext(message[7]);   Outtext(recordname);
	    Outtext(message[8]);   Outimage
	END trace
    END of recordappend;

    CLASS fileitem(file,filename,wait);   VALUE filename;   TEXT filename;
    ! Class fileitem describes the elements in the input file stack.
    ! Since input file calls ('!<' and '!_') may be nested we need this
    ! class. The currentitem (REF (fileitem) )  always points at the top
    ! of the stack. Usually the currentfile (REF (Infile) ) points at
    ! currentitem.file. However when an illegal or unvalid input has been
    ! read the currentfile will temporarly be switched to Sysin.
    ! The filename is used to remember the filenames of the input files.
    ! The wait attribute flags the wait/nowait state of the input
    ! operations. ;
    REF (Infile) file;   BOOLEAN wait;
    BEGIN   REF (fileitem) p,s;

	PROCEDURE up(x);   REF (fileitem) x;
	! The procedure up will add a new input file to the stack.
	! The new file will be opened. ;
	BEGIN   s:- x;   x.p:- THIS fileitem;
	    IF trace THEN
	    BEGIN
		Outtext(message[9]);
		Outchar(IF x.wait THEN cmdchar ELSE cmdnowaitchar);
		Outtext(x.filename);   Outchar(']');   Outimage;
	    END trace;
	    currentitem:- x;   x.file.Open(cmd_buffer);
	    waitforsysin:= x.wait
	END of up;

	PROCEDURE down;
	! This procedure removes the top element of the
	! stack if not equal to Sysin (when a message will be issued). ;
	IF file == Sysin THEN
	BEGIN   Outtext(message[10]);   Outimage;
	    GO TO eof
	END ELSE
	BEGIN  file.Close;   cmdcount:= cmdcount - 1;
	    IF trace THEN
	    BEGIN   Outimage;
		Outtext(message[11]);   Outtext(filename);
		Outtext(message[12]);
		Outchar(IF p.wait THEN cmdchar ELSE cmdnowaitchar);
		Outtext(p.filename);   Outchar('(');
		printint(cmdcount);   Outtext(")]");   Outimage;
	    END trace ELSE
	    IF p.file == Sysin THEN
	    BEGIN   Outtext(message[13]);   Outimage   END;
	    currentfile:- p.file;   currentitem:- p;   waitforsysin:= p.wait;
	    p.s:- NONE;   p:- NONE;
	END OF DOWN;

    END OF FILEITEM;

    BOOLEAN PROCEDURE nohelp;   outline(message[14]);
    ! The nohelp procedure issues a message that no special help
    ! information is available. The programmer is however encouraged to
    ! define his specific help procedures when using
    ! the request procedure. ;

    BOOLEAN PROCEDURE help(message);   NAME message;   TEXT message;
    ! This procedure will have the side effect of displaying the
    ! text MESSAGE on Sysout.;
    IF message.Length <= Length THEN
    BEGIN   Outtext(message);   Outimage   END ELSE
    BEGIN   TEXT t;   INTEGER i;
	t:- Copy(message);
	WHILE t.Length > Length DO
	BEGIN
	    FOR i:= Length STEP -1 UNTIL 2 DO
	    IF fetchar(t,i) = ' ' THEN GO TO blankfound;
	    i:= Length;
	    blankfound:   Outtext(t.Sub(1,i));
	    t:- t.Sub(i+1,t.Length-i);
	END loop;
	Outtext(t);   Outimage
    END of help;

    OPTIONS(/P);
    BOOLEAN PROCEDURE intinput(result,valid);
    ! This procedure checks that the rest of the currentfile.image
    ! contain exactly one integer item (and nothing more).
    ! If so the syntaxok will be flagged true (so that the errormessage in
    ! request may be printed) and the intinput will return the value of
    ! the dynamically evaluated parameter valid (which usually is a boolean
    ! expression). Otherwise a message will be issued and the syntaxok will
    ! will be flagged false. ;
    NAME result,valid;   INTEGER result;   BOOLEAN valid;
    BEGIN   INTEGER p,x;

	p:= currentfile.Pos;
	x:= scanint(currentfile.Image);
	IF currentfile.Pos > p AND rest(currentfile.Image).Strip == NOTEXT THEN
	BEGIN
	    result:= x;
	    syntaxok:= TRUE;
	    intinput:= IF checkvalidity THEN valid ELSE TRUE
	END ELSE
	BEGIN   Outtext(message[15]);
	    outline(currentfile.Image.Sub(p,currentfile.Length-p+1).Strip);
	    syntaxok:= FALSE
	END error

    END of intinput;

    BOOLEAN PROCEDURE realinput(result,valid);
    ! This procedure checks a real item. Otherwise as intinput. ;
    NAME result,valid;   REAL result;   BOOLEAN valid;
    BEGIN   INTEGER p;   REAL x;
	p:= currentfile.Pos;
	x:= scanreal(currentfile.Image);
	IF
	currentfile.Pos > p AND rest(currentfile.Image).Strip == NOTEXT
	THEN
	BEGIN   currentfile.Setpos(p);
	    result:= x;
	    syntaxok:= TRUE;
	    realinput:= IF checkvalidity THEN valid ELSE TRUE
	END ELSE
	BEGIN   syntaxok:= FALSE;
	    Outtext(message[16]);
	    outline(currentfile.Image.Sub(p,currentfile.Length-p+1).Strip)
	END error

    END of realinput;

    BOOLEAN PROCEDURE longrealinput(result,valid);
    ! This procedure checks a real item in double
    ! precision. The syntax checking does not differ form that in realinput,
    ! but the result parameter is long real so that long results may be
    ! returned. ;
    NAME result,valid;   LONG REAL result;   BOOLEAN valid;
    BEGIN   INTEGER p;   LONG REAL x;

	p:= currentfile.Pos;
	x:= scanreal(currentfile.Image);
	IF
	currentfile.Pos > p AND rest(currentfile.Image).Strip == NOTEXT
	THEN
	BEGIN   currentfile.Setpos(p);
	    result:= x;
	    syntaxok:= TRUE;
	    longrealinput:= IF checkvalidity THEN valid ELSE TRUE
	END ELSE
	BEGIN   syntaxok:= FALSE;
	    Outtext(message[17]);
	    outline(currentfile.Image.Sub(p,currentfile.Length-p+1).Strip)
	END error

    END of longrealinput;

    BOOLEAN PROCEDURE boolinput(result);   NAME result;   BOOLEAN result;
    ! The boolinput procedure has one parameter only. The validity check
    ! is of course unnecessary for boolean parameters.
    ! Accepted input depends on the content in the SAFEIO.language file.
    ! The input line may have lower case letters.
    ! In the English case it is YES, NO, TRUE OR FALSE.
    ! P} svenska g{ller JA, NEJ, SANN eller FALSK.;
    BEGIN   TEXT t;   CHARACTER c;
	t:- upcase(rest(currentfile.Image).Strip);
	IF t.Length = 1 THEN c:= t.Getchar;
	syntaxok:= TRUE;	! Allow errormessage to be issued.;
	GO TO
	IF c = 'Y' OR c = 'J' THEN l_true ELSE
	IF c = 'N' THEN l_false ELSE
	IF t = message[18] THEN l_false ELSE
	IF t = message[19] THEN l_true ELSE
	IF t = message[20] THEN l_true ELSE
	IF t = message[21] THEN l_false ELSE
	error;
	l_true:
	boolinput:= result:= TRUE;   GO TO exit;

	l_false:
	boolinput:= TRUE;   result:= FALSE;   GO TO exit;

	error:
	Outtext(message[22]);   outline(t);   syntaxok:= FALSE;

	exit:

    END of boolinput;

    BOOLEAN PROCEDURE textinput(result,valid);
    ! This procedure returns a copy of the stripped rest of the input line.
    ! The syntax is always considered correct.;
    NAME result,valid;   TEXT result;   BOOLEAN valid;
    BEGIN
	result:- Copy(rest(currentfile.Image).Strip);
	syntaxok:= TRUE;
	textinput:= IF checkvalidity THEN valid ELSE TRUE
    END of textinput;

    OPTIONS(/P);
    PROCEDURE request(prompt,default,inputok,errormessage,help);
    ! The request procedure has the following parameters:
    ! Prompt	is the prompting question, often ending with a
    !		prompting character as ':'.
    ! Default	is the default text value. If default action is to be
    !		prohibited, the nodefault variable should be used.
    ! Inputok	shall become true if the input is to be accepted,
    !		else false. Usually the actual parameter is a call to
    !		an ***input procedure.;
    ! Errormessage is a text that will be printed if inputok is
    !		is false and syntaxok is true (c.f. comment for intinput).
    ! Help	is a BOOLEAN parameter by NAME which will
    !		be evaluated when the user types a '?'.
    !;
    VALUE prompt;   NAME default,errormessage,inputok,help;
    TEXT prompt,default,errormessage;   BOOLEAN inputok,help;
    BEGIN   INTEGER p;   TEXT u;

	mainprompt:- prompt;   reqcount:= reqcount + 1;
	IF reqcount > 1 AND recordfile =/= NONE THEN
	BEGIN   Outtext(message[87]);   Outimage;   END warning;
	IF NOT inputsaved THEN currentfile.Setpos(0);

	inputsaved:= FALSE;   GO TO start;

	WHILE NOT inputok AND (IF syntaxok THEN NOT overrideflag ELSE TRUE) DO
	BEGIN   currentfile.Setpos(0);   currentfile:- Sysin;
	    IF syntaxok THEN
	    BEGIN   Outtext(errormessage);   Outimage   END;
	    GO TO mustprompt;

	    start:
	    IF displayprompt THEN   mustprompt:  Outtext(prompt);

	    IF displaydefault AND default =/= nodefault THEN
	    BEGIN   Outchar(defaultquote);   Outtext(default);
		Outchar(defaultquote);   Outchar(promptingchar);
	    END display default;

	    noprompt:
	    overrideflag:= FALSE;

	    IF Pos > 1 THEN
	    BEGIN
		IF Pos < margin THEN Setpos(margin);   Breakoutimage
	    END;

	    u:- rest(currentfile.Image);
	    IF u.Strip == NOTEXT THEN
	    BEGIN
		IF currentfile.Endfile THEN
		BEGIN   currentitem.down;   GO TO mustprompt  END;
		currentfile.Inimage;
		u:- currentfile.Image
	    END ELSE IF FALSE THEN  continue:  u:- rest(currentfile.Image);


	    ! Ignore lines ending with char 11(VT), 12(FF).;
	    FOR p:= IF u.Strip =/= NOTEXT THEN
	    Rank(u.Sub(u.Strip.Length,1).Getchar) ELSE 0
	    WHILE p = 11 OR p = 12 DO
	    BEGIN
		IF currentfile.Endfile THEN
		BEGIN   currentitem.down;   GO TO mustprompt  END;
		currentfile.Inimage;
		u:- currentfile.Image
	    END;

	    IF u.Strip == NOTEXT THEN
	    BEGIN
		IF default == nodefault THEN
		BEGIN   Outtext(message[23]);   Outimage;
		    currentfile:- Sysin;   GO TO mustprompt
		END no default allowed;

		! Note the implicit restriction on length
		! of the default text. ;
		u:= IF default.Length > u.Length THEN
		default.Sub(1,u.Length) ELSE  default;

	    END empty input ELSE
	    test: switchtest(mustprompt,noprompt,continue,help);

	    p:= currentfile.Pos;

	    ! If input from disk and displayinput is true then
	    ! print input value. ;
	    IF displayinput AND currentfile =/= Sysin THEN
	    BEGIN   Outtext(currentfile.Image.Sub(p,currentfile.Length-p+1).Strip);
		IF waitforsysin THEN
		BEGIN   Outtext(message[24]);   Breakoutimage  END  ELSE Outimage
	    END display input value;

	    ! Check Sysin actions: ;
	    IF waitforsysin AND currentfile =/= Sysin THEN
	    BEGIN  Inimage;
		currentfile.Setpos(p);
		IF Sysin.Image.Strip =/= NOTEXT THEN
		BEGIN   currentfile:- Sysin;
		    GO TO test
		END overriding cmd answer
	    END wait for sysin ok;

	END input ok loop;

	! Save in recordfile if not NONE. ;
	INSPECT recordfile DO
	BEGIN
	    ! May have been some Sysin overriding input since last time. ;
	    IF Pos > 1 THEN BEGIN   Image:= NOTEXT;   Setpos(1)  END;
	    Outchar(switchchar);   Outchar(switchchar);
	    Outint(prompt.Length+7,3);   Outchar(Char(9));
	    Outtext(prompt);
	    IF overrideflag THEN
	    BEGIN   Outchar(switchchar);   Outchar(overridechar)    END;
	    Outtext(currentfile.Image.Sub(p,currentfile.Length-p+1).Strip);
	END recording;

	! Restore currentfile.Image since REQUEST may have been
	! called recursively.;

	currentfile.Setpos(0);
	currentfile:- currentitem.file;
	! C.f. procedure switchtest. ;
	IF NOT inputsaved THEN currentfile.Setpos(0);
	INSPECT recordfile DO
	BEGIN   Outimage;
	    IF reqcount NE 1 THEN
	    BEGIN
		Outtext(message[88]);   Outint(reqcount,2);
		Outimage;
	    END warning;
	END inspect;

	reqcount:= reqcount - 1;

    END of request;

    PROCEDURE nooverride;   overridechar:= switchchar;
    ! A call of nooverride shortcircuits the '!&' override validity test
    ! facility. See procedure switchtest. ;

    OPTIONS(/P);
    PROCEDURE switchtest(mustprompt,noprompt,continue,helpvar); 
    ! This procedure takes care of all input lines starting with '!' or '?'. ;
    NAME helpvar;   LABEL mustprompt,noprompt,continue;    BOOLEAN helpvar;
    BEGIN   CHARACTER c;   INTEGER startpos;   BOOLEAN dummy;

	PROCEDURE toggle(switch_,string);   NAME switch_,string;
	! Change a switch value and tell the user. ;
	BOOLEAN switch_;   TEXT string;
	BEGIN   switch_:= NOT switch_;   Outtext(message[25]);
	    Outtext(string);   Outtext(message[26]);
	    Outtext(IF switch_ THEN message[27] ELSE message[28]);   Outimage;

	END of toggle;

	BOOLEAN PROCEDURE synchelp;
	! Printing information concerning syncronization question. ;
	BEGIN   Outtext(message[29]);
	    Outimage;   Outtext(message[30]);
	    Outimage;   Outtext(message[31]);
	    Outimage;   Outtext(message[32]);
	    Outchar(promptingchar);   Outtext(message[33]);
	    Outimage;
	END of synchelp;

	IF currentfile.Lastitem THEN
	BEGIN   currentitem.down;   GO TO exit   END of file;

	c:= currentfile.Inchar;

	! Call help if input line starts with ?. ;
	IF c = helpchar THEN
	BEGIN   IF helpvar THEN ;   GO TO exit  END;

	IF c = switchchar THEN
	BEGIN
	    c:= currentfile.Inchar;
	    IF c = ' ' THEN GO TO exit;
	    IF c = switchchar THEN
	    BEGIN   posfield:- currentfile.Image.Sub(3,3);
		! Reading the position where the answer starts. ;
		IF checkint(posfield) = 1 THEN
		BEGIN   startpos:= posfield.Getint;
		    IF startpos < 7 OR startpos > currentfile.Length THEN GO TO exit;
		    ! Compare input file question with current question. ;
		    IF (IF checkprompt AND currentfile =/= Sysin THEN
		    mainprompt NE currentfile.Image.Sub(7,startpos-7)
		    ELSE FALSE) THEN
		    BEGIN   REF (Outfile) savefile;   CHARACTER savechar;
			TEXT ARRAY table[1:3];   TEXT command;
			INTEGER action,oldcount,nskip;
			BOOLEAN savedisplay,saveprompt;
			PROCEDURE restore;
			BEGIN
			    switchchar:= savechar;   recordfile:- savefile;
			    displayinput:= savedisplay;   displayprompt:= saveprompt;
			END of restore;

			table[1]:- message[79];   table[2]:- message[80];
			table[3]:- message[81];
			Outtext(message[34]);
			Outimage;   Outtext(message[35]);
			Outtext(currentfile.Image.Sub(7,currentfile.Length-6).Strip);
			Outchar(']');   Outimage;
			currentfile:- Sysin;
			! Save possibly recording file and shortcircuit
			! the '!' facilities for this question. ;
			! Save also the displayinput value. ;
			! As well as the displayprompt value. ;
			! And the reqcount value. ;
			savefile:- recordfile;   recordfile:- NONE;
			savechar:= switchchar;   switchchar:= ' ';
			savedisplay:= displayinput;   displayinput:= TRUE;
			saveprompt:= displayprompt;   displayprompt:= TRUE;
			oldcount:= reqcount;   reqcount:= 0;
			request(message[36],nodefault,
			textinput(command,menu(command,action,table,3)),
			message[37],synchelp);
			IF action = 2 THEN
			BEGIN   currentfile:- Sysin;
			    request("How many records:","1",intinput(nskip,
			    nskip >=1),"? Must be >= 1.",
			    help("Enter number of records to be replaced."));
			END;
			FOR nskip:= nskip - 1 WHILE nskip > 0 DO
			BEGIN   IF currentfile.Endfile THEN
			    BEGIN   currentitem.down;   restore;
				   GO TO mustprompt
			    END;
				currentfile.Inimage;
			END loop;
			! .. and restore as before. ;
		restore;
			reqcount:= oldcount;
			! If action =  1  : use input file still (accept).
			! If action =  2  : replace input with Sysin
			! for this question.
			! If action =  3  : save input line for next question.;
			inputsaved:= action = 3;
			IF action >= 2 THEN
			BEGIN   currentfile:- Sysin;
			    currentitem.file.Setpos(action-2);
			    GO TO mustprompt
			END >= 2;
			! Still using startpos since action = 1 ;
		    END no syncronization;
		    currentfile.Setpos(startpos);
		    GO TO continue
		END ELSE GO TO exit;
	    END ELSE
	    IF c = recordchar THEN recordswitch ELSE
	    IF c = cmdchar OR c = cmdnowaitchar THEN cmdswitch(c,continue) ELSE
	    IF c = closechar THEN cmdclose ELSE
	    IF c = displaychar THEN toggle(displaydefault,message[38]) ELSE
	    IF c = tracechar THEN toggle(trace,message[39]) ELSE
	    IF c = promptswitchchar THEN toggle(displayprompt,message[40]) ELSE
	    IF c = helpchar THEN BEGIN   switchhelp;   GO TO exit  END ELSE
	    IF c = appendchar THEN recordappend ELSE
	    IF c = inputchar THEN
	    BEGIN   toggle(displayinputvalue,message[41]);
		displayprompt:= displaydefault:= displayinputvalue;
	    END ELSE
	    IF c = specialchar THEN special ELSE
	    IF c = overridechar THEN
	    BEGIN   overrideflag:= TRUE;   GO TO continue   END ELSE
	    IF c = commentchar THEN   outline(currentfile.Image.Strip) ELSE
	    BEGIN  exit:    currentfile.Setpos(0);   GO TO mustprompt   END;

	    currentfile.Setpos(0);
	    GO TO noprompt

	END c = switchchar ELSE currentfile.Setpos(currentfile.Pos-1);

    END of switchtest;

    OPTIONS(/P);
    PROCEDURE switchhelp;
    ! This procedure prints information on the SAFEIO ! commands. ;
    BEGIN   CHARACTER exclam;
	PROCEDURE charout(c,t);   NAME t;   TEXT t;   CHARACTER c;
	BEGIN   Outchar(exclam);   Outchar(c);   Outtext(message[42]);
	    Outtext(t);   Outimage
	END of charout;

	PROCEDURE switchout(c,t,sw);   NAME t;   CHARACTER c;   TEXT t;
	BOOLEAN sw;
	BEGIN   Outchar(exclam);   Outchar(c);   Outtext(message[43]);
	    Outtext(t);   Outtext(message[44]);
	    Outtext(IF sw THEN message[45] ELSE message[46]);    Outimage
	END switchout;

	Outtext(message[47]);   Outimage;   Eject(Line+1);
	charout(helpchar,message[48]);
	Eject(Line+1);   Outtext(message[49]);   Outchar(switchchar);
	Outimage;   Eject(Line+1);
	exclam:= switchchar;
	switchout(promptswitchchar,message[50],displayprompt);
	switchout(displaychar,message[51],displaydefault);
	switchout(inputchar,message[52],displayinputvalue);
	switchout(tracechar,message[53],trace);
	charout(appendchar,message[54]);
	charout(commentchar,message[55]);
	charout(specialchar,message[56]);
	! Will be printed only if not shortcircuited. ;
	IF overridechar NE switchchar THEN
	charout(overridechar,message[57]);
	Outchar(exclam);   Outchar(cmdchar);
	Outtext(message[58]);
	Outtext(defaultextension);   Outimage;
	charout(cmdnowaitchar,message[59]);
	charout(cmdchar,message[60]);
	charout(closechar,message[61]);
	Outchar(exclam);   Outchar(cmdnowaitchar);
	Outtext(message[62]);
	Outtext(defaultextension);   Outimage;
	Outchar(exclam);   Outchar(recordchar);
	Outtext(message[63]);
	Outtext(defaultextension);   Outimage;
	charout(recordchar,message[64]);
	charout(helpchar,message[65]);
	Eject(Line+1);
	Outtext(message[66]);
	Outchar(switchchar);   Outtext(message[67]);
	Outimage;   Eject(Line+1)


    END of switchhelp;

    OPTIONS(/P);
    PROCEDURE cmdswitch(c,continue);   CHARACTER c;   LABEL continue;
    ! This procedure takes care of !< and !_ commands. ;
    BEGIN   TEXT cmdname;
	cmdname:- rest(currentfile.Image).Strip;
	currentfile.Setpos(0);

	IF cmdname == NOTEXT THEN
	! No file name given. ;
	BEGIN
	    IF currentitem.file == Sysin THEN
	    BEGIN   Outtext(message[68]);
		Outimage;   currentfile:- currentitem.file
	    END ELSE
	    IF c = cmdnowaitchar THEN
	    BEGIN
		! Change to nowait input mode. ;
		BEGIN    waitforsysin:= FALSE;   currentfile.Setpos(0);
		    currentfile:- currentitem.file;   GO TO continue;
		END;
	    END ELSE
	    ! Close current input file:; currentitem.down
	END ELSE
	! File name was given. ;
	BEGIN
	    cmdname:- checkextension(cmdname,defaultextension);
	    IF cmdcount = maxcmdfiles THEN
	    BEGIN
		Outtext(message[82]);    Outimage
	    END ELSE
	    BEGIN   REF (Infile) x;
		cmdcount:= cmdcount + 1;
		x:- findinfile(cmdname);
		IF x == NONE THEN
		BEGIN   Outtext(message[89]);   Outtext(cmdname);
		    Outtext(message[91]);   Outimage
		END ELSE
		BEGIN
		    IF trace THEN
		    BEGIN   Outtext(message[69]);   Breakoutimage   END;
		    currentitem.up(NEW fileitem(x,cmdname,c = cmdchar));
		    currentfile:- currentitem.file
		END input ok;
	    END new cmd file;
	END new cmd file;

    END of cmdswitch;

    PROCEDURE recordswitch;
    ! This procedure takes care of the !> command. ;
    BEGIN   TEXT oldname;

	oldname:- recordname;
	recordname:- Copy(rest(currentfile.Image).Strip);
	currentfile.Setpos(0);

	IF recordname == NOTEXT THEN
	! No file name given. ;
	BEGIN
	    IF recordfile =/= NONE THEN
	    ! Close it. ;
	    BEGIN    recordfile.Setpos(1);
		recordfile.Close;   recordfile:- NONE;
		IF trace THEN
		BEGIN   Outtext(message[70]);   Outtext(oldname);
		    Outchar(']');   Outimage
		END trace
	    END active record file ELSE
	    ! No active file to close. ;
	    BEGIN   Outtext(message[71]);   Outimage   END no file
	END notext ELSE
	! File name was given. ;
	BEGIN
	    IF recordfile =/= NONE THEN
	    ! Already recording. ;
	    BEGIN   Outtext(message[72]);   Outtext(oldname);
		Outtext(message[73]);   Outimage;
		recordname:- oldname
	    END active record file ELSE
	    ! Open new recording file. ;
	    BEGIN
		recordname:- checkextension(recordname,defaultextension);
		recordfile:- findoutfile(conc(message[74],recordname));
		IF recordfile == NONE THEN
		BEGIN   Outtext(message[90]);
		    Outtext(recordname);   Outtext(message[91]);
		    Outimage
		END impossible ELSE
		BEGIN
		    recordfile.Open(record_buffer);
		    IF trace THEN
		    BEGIN   Outtext(message[75]);
			Outtext(recordname);   Outchar(']');
			Outimage
		    END trace
		END log ok
	    END was no active record file
	END file name given

    END of recordswitch;

    PROCEDURE closefiles;
    ! Close all open input and recording SAFEIO files. ;
    BEGIN
	recordclose;
	WHILE currentitem.file =/= Sysin DO currentitem.down;
    END of closefiles;

    PROCEDURE readmessages;
    ! Reads an input file containing SAFEIO messages.
    ! Currently two files are available: SAFEIO.ENG and SAFEIO.SWE
    ! for english and swedish texts respectively.
    ! If no such files exists on the user's area, the SYS: files
    ! will be used.
    ! Parameter "own.fra" will use the file "OWN.FRA".
    ! The parameter "own" will use a file OWN.ENG on your own disk
    ! area. SAFEIO("","") will create no log file and use the SAFEIO.ENG
    ! file on the SYS: area. ;
    BEGIN   REF (Infile) languagefile;   BOOLEAN sys_tried;
	INTEGER i;

	language:-
	frontstrip(language.Strip);
	IF language == NOTEXT THEN
	language:- Copy("SAFEIO.ENG");

	WHILE language.More DO
	IF language.Getchar = '.' THEN GO TO lookup;
	! Add default file name:;
	language.Setpos(1);
	WHILE language.More DO
	IF language.Getchar = ':' THEN
	GO TO colonfound;
	language:- conc("SAFEIO.",language);
	GO TO lookup;
	colonfound:
	language:- conc( language.Sub(1,language.Pos-1),
	    "SAFEIO.",rest(language));

	lookup:
	languagefile:- findinfile(language);
	INSPECT languagefile DO
	BEGIN   Open(Blanks(80));   Inimage;
	    i:= 0;
	    FOR i:= i + 1 WHILE NOT Endfile AND i <= 91 DO
	    BEGIN   message[i]:- Copy(Image.Sub(2,Image.Strip.Length-2));
		Inimage
	    END endfile loop;
	    Close
	END inspect OTHERWISE
	BEGIN
	    IF sys_tried THEN
	    BEGIN   Outtext("? Unknown language:");   Outtext(language);
		Outimage;   Outtext("ENGLISH used.");   Outimage;
		language:- Copy("sys:SAFEIO.ENG");
		GO TO lookup
	    END ELSE
	    BEGIN   sys_tried:= TRUE;
		WHILE language.More DO
		IF language.Getchar = '[' THEN
		BEGIN   language:- language.Sub(1,language.Pos-2);
		    GO TO out
		END;
		out:   language:- conc("SYS:",language);
		GO TO lookup
	    END sys trial
	END unsuccessfull lookup;

    END of readmessages;

    OPTIONS(/P);
    REF (Infile) currentfile;   REF (fileitem) currentitem;
    TEXT cmd_buffer,mainprompt,recordname,record_buffer,
    nodefault,defaultextension,posfield,u;
    TEXT ARRAY message[1:91];
    BOOLEAN trace,syntaxok,displayprompt,displayinput,displaydefault,
    inputsaved,checkprompt,overrideflag,waitforsysin,checkvalidity;
    REF (Outfile) recordfile;
    INTEGER margin,cmdcount,maxcmdfiles,reqcount;
    CHARACTER cmdchar,recordchar,displaychar,tracechar,promptswitchchar,
    switchchar,cmdnowaitchar,helpchar,inputchar,promptingchar,defaultquote,
    closechar,commentchar,appendchar,specialchar,overridechar;

    ! Length of images may be increased. ;
    u:- Blanks(20);
    maxcmdfiles:= 10;
    cmd_buffer:- Blanks(IF Length > 80 THEN Length ELSE 80);
    record_buffer:- Blanks(cmd_buffer.Length);

    readmessages;

    ! Set up initial values. ;

    nodefault:- message[76];    defaultextension:- message[77];
    checkprompt:= checkvalidity:=
    syntaxok:= displaydefault:= displayprompt:= displayinput:= trace:= TRUE;

    currentfile:- Sysin;
    currentitem:- NEW fileitem(currentfile,message[78],waitforsysin);

    ! May be changed to zero if no indentation of answers
    ! is wanted. Could also be increased if very long questions. ;
    margin:= 35;

    ! All these characters may be changed. However be
    ! carefull for clashes. See procedure switchtest about the
    ! testing order. Note the possibility to shortcircuit a facility
    ! by setting the corresponding character to ' '. ;

    cmdchar:= '<';
    recordchar:= '>';
    helpchar:= '?';
    displaychar:= '/';
    tracechar:= '[';
    promptswitchchar:= '*';
    inputchar:= '=';
    switchchar:= '!';
    cmdnowaitchar:= '_';
    defaultquote:= '/';
    promptingchar:= ':';
    specialchar:= '%';
    overridechar:= '&';
    appendchar:= '+';
    commentchar:= ';';
    closechar:= '^';

    ! Initializing recordfile from start. ;
    IF savefilename =/= NOTEXT THEN
    BEGIN   Sysin.Image:= savefilename;   Sysin.Setpos(1);
	recordswitch;   Sysin.Setpos(0);
    END;

    ! Eliminating page skipping on Sysout. ;
    INSPECT Sysout WHEN Printfile DO Linesperpage(-1);

    start: ;
    INNER;

    eof: ! Jumped here if End of file on Sysin:;

    closefiles;

END of safeio;