Oberon/ETH Oberon/Dialer.Mod

(* ETH Oberon, Copyright 1990-2005 Computer Systems Institute, ETH Zurich, CH-8092 Zurich. Desktops.OpenDoc http:&#47;/www.oberon.ethz.ch/ and follow the "license agreement" link. *) MODULE Dialer;	(** non-portable *)	(* 26.08.96 mg *) IMPORT Oberon, NetSystem, Strings, Texts, Input, V24, NetBase, Modules, TextFrames; CONST RI = 5; DCD = 6;	(* input *) Service = "dialup"; GrabPort = TRUE;	(* take over com port by force, if already in use *) VAR W: Texts.Writer; script: Oberon.Task; host: ARRAY 64 OF CHAR; waitStr: ARRAY 32 OF CHAR; waitPos: INTEGER; (* DoScript runs in the background.  waitPos  	    maintains the identity of an operation across iterations of the  	    Oberon system event loop. 	    waitPos = -2 while a pause is in progress; 	                  = -1 while interpretation of a script statement is pending 	                  and in progress; 	                  = 0 while a match to a specified string is pending; 	                  = 1..n-1 as characters are received and matched.  	                  2004-10-20, ple *) waitTime: LONGINT; err, open: BOOLEAN; S: Texts.Scanner; port: INTEGER; dev: NetBase.Device; PROCEDURE Delay (i: LONGINT); VAR t: LONGINT; BEGIN t := Input.Time; WHILE Input.Time - t < i DO END END Delay; PROCEDURE Hang(port: INTEGER); BEGIN V24.ClearMC(port, {V24.DTR}); Delay(Input.TimeUnit DIV 6); V24.SetMC(port, {V24.DTR}) END Hang; PROCEDURE InitComPort (port: INTEGER; baud: LONGINT); VAR ok: BOOLEAN; res: LONGINT; BEGIN ok := TRUE; V24.Start(port, baud, 8, V24.ParNo, 1, res); IF GrabPort & (res = 1) THEN V24.Stop(port); V24.Start(port, baud, 8, V24.ParNo, 1, res) END; ok := FALSE; IF res = 0 THEN ok := TRUE ELSIF res = 3 THEN Texts.WriteString(W,"Dialer: Baudrate not supported"); Texts.WriteLn(W) ELSIF res = 1 THEN Texts.WriteString(W,"Dialer: Port already in use"); Texts.WriteLn(W) ELSE Texts.WriteString(W,"Dialer: Init error "); Texts.WriteInt(W, res, 1); Texts.WriteLn(W) END; Texts.Append(Oberon.Log, W.buf); IF ~ok THEN err := TRUE END END InitComPort; PROCEDURE SendStr(str: ARRAY OF CHAR); VAR ch: CHAR; i: INTEGER;  res: LONGINT; BEGIN i := 0; WHILE str[i] # 0X DO ch := str[i]; IF ch = "~" THEN Delay(300) ELSE V24.Send(port, ch, res) END; INC(i) END; V24.Send(port, 0DX, res) END SendStr; PROCEDURE WaitStr(str: ARRAY OF CHAR; timeOut: LONGINT); BEGIN COPY(str, waitStr); waitPos := 0; waitTime := Input.Time + timeOut END WaitStr; PROCEDURE StartLine; BEGIN IF open THEN Texts.Write(W, "}"); open := FALSE END; Texts.WriteLn(W) END StartLine; PROCEDURE SendUser(use: BOOLEAN; VAR err: BOOLEAN); VAR user, passwd: ARRAY 64 OF CHAR; BEGIN user := ""; NetSystem.GetPassword(Service, host, user, passwd); IF (user # "") & (passwd # "") THEN StartLine; Texts.WriteString(W, "Sending "); IF use THEN Texts.WriteString(W, "USER ["); Texts.WriteString(W, user);  SendStr(user) ELSE Texts.WriteString(W, "PASSWORD [not printed"); SendStr(passwd) END; Texts.WriteString(W, "]"); Texts.Append(Oberon.Log, W.buf); Texts.Scan(S) ELSE StartLine; Texts.WriteString(W, "NetSystem.SetUser "); Texts.WriteString(W, Service); Texts.WriteString(W, ": @ ~ required"); err := TRUE END END SendUser; PROCEDURE Call(cmd: ARRAY OF CHAR); VAR F: TextFrames.Frame; par: Oberon.ParList;  T: Texts.Text; BEGIN IF Oberon.Par = NIL THEN par := NIL ELSE NEW(par); par^ := Oberon.Par^ END; NEW(T); Texts.Open(T, ""); Texts.WriteString(W, cmd); Texts.WriteLn(W); Texts.Append(T, W.buf); F := TextFrames.NewText(T, 0); TextFrames.Call(F, 0, FALSE); IF (par # NIL) & (Oberon.Par # NIL) THEN Oberon.Par^ := par^ END END Call; PROCEDURE DoScript(me: Oberon.Task); VAR timo, res: LONGINT; any: BOOLEAN;  ch: CHAR; BEGIN err := FALSE; (* Components of IF statement reordered so that the values of waitPos 		are in numerical order, 2004-10-27, ple *) IF waitPos = -2 THEN (* Pause in progress. *) IF (Input.Time - waitTime > 0) THEN Texts.WriteString(W, " Pause completed."); waitPos := -1 (* Another script statement pending. *) (* ELSE continue the pause. *) END ELSIF waitPos = -1 THEN	(* execute next script statement *) IF S.class = Texts.Name THEN IF S.s = "USER" THEN SendUser(TRUE, err) ELSIF S.s = "PASSWORD" THEN SendUser(FALSE, err) ELSIF S.s = "START" THEN StartLine; Texts.WriteString(W, "Enabling device");  Texts.Append(Oberon.Log, W.buf); IF (dev # NIL) & (dev.state = NetBase.pending) THEN dev.state := NetBase.open END; dev := NIL; Texts.Scan(S) ELSIF S.s = "CALL" THEN Texts.Scan(S); IF (S.class = Texts.String) OR (S.class = Texts.Name) THEN StartLine; Texts.WriteString(W, "Calling [");  Texts.WriteString(W, S.s); Texts.WriteString(W, "]"); Texts.Append(Oberon.Log, W.buf); Call(S.s); Texts.Scan(S) ELSE StartLine; Texts.WriteString(W, "Dialer: Command expected after CALL"); err := TRUE END ELSE StartLine; Texts.WriteString(W, "Dialer: Unknown keyword [");  Texts.WriteString(W, S.s); Texts.Write(W, "]"); err := TRUE END ELSIF S.class = Texts.String THEN StartLine; Texts.WriteString(W, "Sending [");  Texts.WriteString(W, S.s); Texts.WriteString(W, "]"); Texts.Append(Oberon.Log, W.buf); SendStr(S.s); Texts.Scan(S) ELSIF S.class = Texts.Int THEN (* Wait for string followning int. *) timo := S.i * Input.TimeUnit; Texts.Scan(S); IF S.class = Texts.String THEN StartLine; Texts.WriteString(W, "Waiting ");  Texts.WriteInt(W, timo DIV Input.TimeUnit, 1); Texts.WriteString(W, "s for [");  Texts.WriteString(W, S.s);  Texts.WriteString(W, "]."); IF S.s[0] = 0X THEN (* String is empty.  Wait the specified interval.  2004-10-28, ple. *) waitPos := -2; waitTime := Input.Time + timo ELSE (* Wait for non-empty string. *) Texts.WriteString(W, " {"); open := TRUE; WaitStr(S.s, timo); END; Texts.Append(Oberon.Log, W.buf); Texts.Scan(S) ELSE StartLine; Texts.WriteString(W, "Dialer: Wait string expected after integer"); err := TRUE END ELSIF S.class = Texts.Char THEN IF S.c = "}" THEN StartLine; Texts.WriteString(W, "End of script"); Texts.WriteLn(W); Texts.Append(Oberon.Log, W.buf); Oberon.Remove(script); script := NIL;  dev := NIL ELSE StartLine; Texts.WriteString(W, "Dialer: Unexpected character in script"); err := TRUE END ELSE HALT(99) END ELSIF waitPos >= 0 THEN	(* wait for response from modem *) any := FALSE; WHILE (waitPos >= 0) & (V24.Available(port) > 0) DO 			any := TRUE; V24.Receive(port, ch, res); IF ch = 0AX THEN (* skip *) ELSIF ch = 0DX THEN Texts.Write(W, "|") ELSIF (ch >= 20X) & (ch <= 7EX) THEN Texts.Write(W, ch) ELSE Texts.Write(W, CHR(147)) END; IF waitPos >= 0 THEN IF CAP(ch) = CAP(waitStr[waitPos]) THEN INC(waitPos); IF waitStr[waitPos] = 0X THEN waitPos := -1 END	(* matched! *) ELSE waitPos := 0	(* restart *) END END END; IF any THEN Texts.Append(Oberon.Log, W.buf) END; IF (waitPos >= 0) & (Input.Time - waitTime > 0) THEN StartLine; Texts.WriteString(W, "Dialer: Timed out"); err := TRUE END ELSE (* waitPos has an unintended value. 2004-10-28, ple. *) StartLine; Texts.WriteString(W, "DoScript: Unintended value of waitPos."); StartLine; Texts.WriteString(W, "waitPos = "); Texts.WriteInt(W, waitPos, 1); Texts.Append(Oberon.Log, W.buf); HALT(99) END; IF err THEN Oberon.Remove(script); script := NIL;  dev := NIL; Hang(port); Texts.WriteLn(W); Texts.WriteString(W, "Dialer: Script aborted"); Texts.WriteLn(W); Texts.Append(Oberon.Log, W.buf) END END DoScript; PROCEDURE GetDevice(device: ARRAY OF CHAR; VAR devnum: LONGINT); BEGIN Strings.Lower(device, device); IF device = "default" THEN devnum := 0 ELSIF Strings.Prefix("device", device) & (device[6] >= "0") & (device[6] <= "9") & (device[7] = 0X) THEN devnum := ORD(device[6])-ORD("0") ELSE devnum := -1 END END GetDevice; PROCEDURE Dial*;	(** config device {config.Host is used to find password, config.Init for port, config.Dial for script} *) VAR S0: Texts.Scanner; prefix, path: ARRAY 64 OF CHAR;  err: BOOLEAN;  devnum: LONGINT; BEGIN err := FALSE; Texts.OpenScanner(S0, Oberon.Par.text, Oberon.Par.pos); Texts.Scan(S0); IF S0.class = Texts.Name THEN COPY(S0.s, prefix); COPY(prefix, host); Texts.Scan(S0); IF S0.class = Texts.Name THEN GetDevice(S0.s, devnum); dev := NetBase.FindDevice(devnum); IF dev # NIL THEN IF dev.state = NetBase.open THEN dev.state := NetBase.pending; Texts.WriteString(W, "Warning: "); Texts.WriteString(W, S0.s); Texts.WriteString(W, " device is already open"); Texts.WriteLn(W); Texts.Append(Oberon.Log, W.buf) END; COPY(prefix, path); Strings.Append(path, ".Init"); Oberon.OpenScanner(S, path); IF S.class = Texts.Name THEN IF S.s = "COM1" THEN port := V24.COM1 ELSIF S.s = "COM2" THEN port := V24.COM2 ELSIF S.s = "COM3" THEN port := V24.COM3 ELSIF S.s = "COM4" THEN port := V24.COM4 ELSE HALT(99) END; Texts.Scan(S); IF S.class = Texts.Int THEN InitComPort(port, S.i); COPY(prefix, path); Strings.Append(path, ".Dial"); Oberon.OpenScanner(S, path); IF script # NIL THEN Oberon.Remove(script) ELSE NEW(script) END; waitPos := -1; script.safe := FALSE; script.time := 0; script.handle := DoScript; Texts.WriteString(W, "Dial script started"); open := FALSE; Texts.Append(Oberon.Log, W.buf); Oberon.Install(script) ELSE Texts.WriteString(W, "Init syntax error"); err := TRUE END ELSE Texts.WriteString(W, "Init syntax error"); err := TRUE END ELSE Texts.WriteString(W, S0.s); Texts.WriteString(W, " device not found"); err := TRUE END ELSE Texts.WriteString(W, "Dial syntax error"); err := TRUE END ELSE Texts.WriteString(W, "Dial syntax error"); err := TRUE END; IF err THEN Texts.WriteLn(W); Texts.Append(Oberon.Log, W.buf) END END Dial; PROCEDURE GetPort(VAR port: INTEGER; VAR devnum: LONGINT); VAR S, R: Texts.Scanner; i: LONGINT; BEGIN devnum := -1; Texts.OpenScanner(S, Oberon.Par.text, Oberon.Par.pos); Texts.Scan(S); IF S.class = Texts.Name THEN Strings.Append(S.s, ".Init"); Oberon.OpenScanner(R, S.s); IF R.s = "COM1" THEN port := V24.COM1 ELSIF R.s = "COM2" THEN port := V24.COM2 ELSIF R.s = "COM3" THEN port := V24.COM3 ELSIF R.s = "COM4" THEN port := V24.COM4 ELSE HALT(99) END; Texts.Scan(S); IF S.class = Texts.Name THEN GetDevice(S.s, devnum) END ELSE HALT(99) END END GetPort; PROCEDURE Hangup*; VAR port: INTEGER; devnum: LONGINT; BEGIN GetPort(port, devnum); IF script # NIL THEN Texts.WriteLn(W); Texts.WriteString(W, "Script aborted"); Texts.WriteLn(W); Texts.Append(Oberon.Log, W.buf); Oberon.Remove(script); script := NIL;  dev := NIL END; Hang(port) END Hangup; PROCEDURE State*; VAR port: INTEGER; devnum: LONGINT;  s: SET; BEGIN GetPort(port, devnum); V24.GetMC(port, s); IF DCD IN s THEN	(* carrier detected *) Texts.WriteString(W, "Modem on-line") ELSE Texts.WriteString(W, "Modem off-line") END; IF devnum # -1 THEN dev := NetBase.FindDevice(devnum); Texts.WriteString(W, ", device "); Texts.WriteInt(W, devnum, 1); IF dev = NIL THEN Texts.WriteString(W, " not installed") ELSE IF dev.state = NetBase.closed THEN Texts.WriteString(W, " closed") ELSIF dev.state = NetBase.open THEN Texts.WriteString(W, " open") ELSIF dev.state = NetBase.pending THEN Texts.WriteString(W, " link pending") ELSE Texts.WriteString(W, " in state"); Texts.WriteInt(W, dev.state, 1) END END END; Texts.WriteLn(W); Texts.Append(Oberon.Log, W.buf) END State; PROCEDURE Cleanup; BEGIN IF script # NIL THEN Oberon.Remove(script); script := NIL END END Cleanup; BEGIN Texts.OpenWriter(W); script := NIL; dev := NIL;  port := 1; Modules.InstallTermHandler(Cleanup) END Dialer. System.Free Dialer ~ Compiler.Compile SYS:Dialer.Mod \OSYS: ~ Compiler.Compile USR:Dialer.Mod \OSYS: ~ NetSystem.SetUser dialup:pmullerppp@ETHPPP ~ Dialer.Dial ETHPPP default Dialer.State ETHPPP default Dialer.Hangup ETHPPP