ð1Oberon10.Scn.FntÙ4)QS5MODULE Shell; (* ww 3 Oct 90, jt 19 Oct 91/30 Dec 92, mad 6 Jul 94 *) (* this source code is intended for system programmers only! it shows by example how to use Unix system calls from LINUX-Oberon how to extend the LINUX-Oberon garbage collector If anybody could find out how to avoid the error messages at Shell.Open and how to reliably kill the rlogin processes please send a mail to templ@inf.ethz.ch Check out where to find "rlogin" on your system. If it's not in the directory /usr/bin then you have to change the path which is assigned to the varibale "rlogin" inside the module body. *) IMPORT SYSTEM, Kernel, Unix, TextFrames, MenuViewers, Viewers, Display, Fonts, Texts, Oberon; CONST READ = 0; WRITE = 1; WNOHANG = 1; Left = 2; Middle = 1; Right = 0; Menu = "System.Close System.Copy System.Grow Shell.Clear Edit.Search Edit.Store "; TYPE Process = POINTER TO ProcessDesc; Frame = POINTER TO FrameDesc; Task = POINTER TO TaskDesc; ProcessDesc = RECORD readPipe, writePipe: Unix.PipeFd; text: Texts.Text; w: Texts.Writer; pid, textState, lastInp: LONGINT; lastCh: CHAR; dead: BOOLEAN; task: Task END; FrameDesc = RECORD (TextFrames.FrameDesc) p: Process; END; TaskDesc = RECORD (Oberon.TaskDesc) p: LONGINT (*Process*) END; String = POINTER TO StringDesc; StringDesc = ARRAY 80 OF CHAR; Para = ARRAY 4 OF String; VAR normFont, boldFont: Fonts.Font; root, res: LONGINT; waitpid: PROCEDURE (pid, statusp, options: LONGINT): LONGINT; rlogin: ARRAY 128 OF CHAR; PROCEDURE Write(p: Process; ch: CHAR); BEGIN res := Unix.Write(p.readPipe[WRITE], SYSTEM.ADR(ch), 1) END Write; PROCEDURE *Finalizer(obj: SYSTEM.PTR); VAR q: Process; BEGIN q:= SYSTEM.VAL(Process, obj); res := Unix.Close(q.readPipe[WRITE]); res := Unix.Close(q.writePipe[READ]); res := Unix.Kill(q.pid, 15); res := waitpid(q.pid, 0, WNOHANG); q.task.p := 0; EXCL(Kernel.readSet[q.writePipe[READ] DIV 32], q.writePipe[READ] MOD 32); END Finalizer; PROCEDURE* TaskHandle; VAR p: Process; t: Texts.Text; f: Display.Frame; m, n, l: LONGINT; ch: CHAR; carSet: BOOLEAN; buf: ARRAY 4096 OF CHAR; BEGIN p := SYSTEM.VAL(Process, Oberon.CurTask(Task).p); IF p # NIL THEN t := p.text; IF (p.writePipe[READ] MOD 32) IN Kernel.readySet[p.writePipe[READ] DIV 32] THEN n := Unix.Read(p.writePipe[READ], SYSTEM.ADR(buf), LEN(buf)); IF n > 0 THEN Texts.SetFont(p.w, normFont); m := 0; f := Oberon.FocusViewer; carSet := FALSE; IF (f # NIL) & (f.dsc # NIL) THEN f := f.dsc.next; IF (f # NIL) & (f IS TextFrames.Frame) THEN WITH f: TextFrames.Frame DO carSet := (f.hasCar) & (f.text = p.text) END END END; REPEAT ch := buf[m]; IF ch = 08X THEN IF p.textState = 4 THEN p.textState := 6 ELSE Texts.Append(t, p.w.buf); l := t.len; IF l > 0 THEN Texts.Delete(t, l-1, l) END; IF p.textState = 5 THEN p.textState := 6 END END ELSE IF p.textState = 4 THEN Texts.Write(p.w, "_"); p.textState := 0 END; CASE p.textState OF 0, 5, 6: IF ch = 1BX THEN p.textState := 1 ELSIF ch = "_" THEN IF m = n - 1 THEN p.textState := 5; Texts.Write(p.w, "_") ELSE p.textState := 4 END ELSE IF (ch >= " ") & (ch < 7FX) OR (ch = 09X) THEN IF p.textState = 6 THEN Texts.SetFont(p.w, boldFont); Texts.Write(p.w, ch); Texts.SetFont(p.w, normFont) ELSE Texts.Write(p.w, ch) END ELSIF (ch = 0DX) OR (ch = 0AX) THEN IF (p.lastCh # 0DX) OR (ch = 0DX) THEN Texts.WriteLn(p.w) END ELSIF ch = 08X THEN Texts.Append(t, p.w.buf); l := t.len; IF l > 0 THEN Texts.Delete(t, l-1, l) END ELSIF ch # 07X THEN Texts.Write(p.w, "<"); Texts.WriteInt(p.w, ORD(ch), 0); Texts.Write(p.w, ">") END; p.textState := 0 END | 1: IF ch = "[" THEN p.textState := 2 ELSIF ("0" <= ch) & (ch <= "~") THEN p.textState := 0 ELSE p.textState := 3 END | 2: IF ("@" <= ch) & (ch <= "~") THEN p.textState := 0 END | 3: IF ("0" <= ch) & (ch <= "~") THEN p.textState := 0 END END END; p.lastCh := ch; INC(m) UNTIL m = n; Texts.Append(t, p.w.buf); IF carSet THEN WITH f: TextFrames.Frame DO TextFrames.SetCaret(f, f.text.len) END END END END; IF waitpid(p.pid, 0, WNOHANG) = p.pid THEN p.dead := TRUE; Oberon.Remove(Oberon.CurTask); Texts.WriteLn(p.w); Texts.WriteString(p.w, "Terminated."); Texts.WriteLn(p.w); Texts.Append(p.text, p.w.buf) END ELSE (*process desc has been garbage collected*) Oberon.Remove(Oberon.CurTask) END END TaskHandle; PROCEDURE Neutralize(F: TextFrames.Frame); VAR M: Oberon.ControlMsg; BEGIN M.id := Oberon.neutralize; TextFrames.Handle(F, M) END Neutralize; PROCEDURE TfEdit(F: TextFrames.Frame; X, Y: INTEGER; keys: SET); VAR M: Oberon.InputMsg; BEGIN M.id := Oberon.track; M.X := X; M.Y := Y; M.keys := keys; TextFrames.Handle(F, M) END TfEdit; PROCEDURE SetCaret(f: Frame); VAR r: Texts.Reader; i, j: INTEGER; last, pos: LONGINT; ch: CHAR; list: ARRAY 256 OF LONGINT; BEGIN IF (f.hasCar) & (f.carloc.pos < f.text.len) THEN last := TextFrames.Pos(f, f.W, f.Y); TextFrames.RemoveCaret(f); IF last < f.text.len THEN list[0] := f.org; i := 1; j := 1; Texts.OpenReader(r, f.text, f.org); Texts.Read(r, ch); WHILE ~r.eot DO IF ch = 0DX THEN list[i] := Texts.Pos(r); IF list[i] < last THEN INC(j) END; i := (i + 1) MOD 256 END; Texts.Read(r, ch) END; IF i < j THEN pos := list[(256 + i - j) MOD 256] ELSE pos := list[(i - j) MOD 256] END; Oberon.RemoveMarks(f.X, f.Y, f.W, f.H); Neutralize(f); TextFrames.Show(f, pos) END; TextFrames.SetCaret(f, f.text.len) END END SetCaret; PROCEDURE Edit(f: Frame; x, y: INTEGER; keys: SET); VAR t: Texts.Text; r: Texts.Reader; cpm: Oberon.CopyOverMsg; keySum: SET; beg, end, time: LONGINT; ch: CHAR; BEGIN IF (f.X + TextFrames.barW <= x) & (x < f.X + f.W) & (f.Y <= y) & (y < f.Y + f.H) THEN IF Left IN keys THEN Oberon.PassFocus(MenuViewers.Ancestor); keySum := {}; TextFrames.TrackCaret(f, x, y, keySum); IF keySum = {Left, Middle} THEN Oberon.GetSelection(t, beg, end, time); IF time > 0 THEN Texts.OpenReader(r, t, beg); f.p.lastInp := f.text.len; IF end > t.len THEN end := t.len END ; WHILE Texts.Pos(r) < end DO Texts.Read(r, ch); Write(f.p, ch) END END ; SetCaret(f) ELSIF keySum = {Left, Right} THEN Oberon.GetSelection(t, beg, end, time); Texts.OpenReader(r, f.text, f.carloc.pos); Texts.Read(r, ch); IF ~r.eot & (time > 0) THEN Texts.ChangeLooks(t, beg, end, {0..2}, r.fnt, r.col, r.voff) END; TextFrames.RemoveCaret(f) END ELSIF Right IN keys THEN keySum := {}; TextFrames.TrackSelection(f, x, y, keySum); IF keySum = {Left, Right} THEN Oberon.PassFocus(MenuViewers.Ancestor); Oberon.GetSelection(t, beg, end, time); Texts.Delete(f.text, beg, end); IF f.p.lastInp >= beg THEN DEC(f.p.lastInp, end - beg) END ELSIF keySum = {Middle, Right} THEN Oberon.GetSelection(t, beg, end, time); cpm.text := t; cpm.beg := beg; cpm.end := end; Oberon.FocusViewer.handle(Oberon.FocusViewer, cpm) END ELSE TfEdit(f, x, y, keys) END ELSE TfEdit(f, x, y, keys) END END Edit; PROCEDURE* Handle(f: Display.Frame; VAR m: Display.FrameMsg); VAR cf: Frame; text: Texts.Text; r: Texts.Reader; last, cnt, pos: LONGINT; ch: CHAR; BEGIN WITH f: Frame DO text := f.text; IF f.p.dead THEN f.handle := TextFrames.Handle; TextFrames.Handle(f, m) ELSIF m IS Oberon.CopyOverMsg THEN WITH m: Oberon.CopyOverMsg DO IF f.hasCar THEN Texts.OpenReader(r, m.text, m.beg); f.p.lastInp := text.len; IF m.end > m.text.len THEN m.end := m.text.len END ; WHILE Texts.Pos(r) < m.end DO Texts.Read(r, ch); Write(f.p, ch) END ; SetCaret(f) END END ELSIF m IS Oberon.InputMsg THEN WITH m: Oberon.InputMsg DO IF m.id = Oberon.consume THEN IF f.hasCar THEN Write(f.p, m.ch); f.p.lastInp := text.len; SetCaret(f) END ELSIF m.id = Oberon.track THEN Edit(f, m.X, m.Y, m.keys) END END ELSIF m IS TextFrames.UpdateMsg THEN WITH m: TextFrames.UpdateMsg DO IF m.text = f.text THEN TextFrames.Handle(f, m); IF m.id = TextFrames.insert THEN last := TextFrames.Pos(f, f.W, f.Y); IF (last < text.len) & (last >= m.beg) THEN Texts.OpenReader(r, text, last); cnt := 0; REPEAT Texts.Read(r, ch); IF ch = 0DX THEN INC(cnt) END UNTIL r.eot; pos := f.org; Texts.OpenReader(r, text, pos); REPEAT Texts.Read(r, ch); IF ch = 0DX THEN DEC(cnt); pos := Texts.Pos(r) END UNTIL (cnt = 0) OR r.eot OR (Texts.Pos(r) = f.p.lastInp); IF pos # f.org THEN TextFrames.Show(f, pos) END END END END END ELSIF m IS Oberon.CopyMsg THEN NEW(cf); m(Oberon.CopyMsg).F := cf; TextFrames.Handle(f, m); cf.p := f.p; ELSE TextFrames.Handle(f, m) END END END Handle; PROCEDURE OpenScanner(VAR s: Texts.Scanner); VAR text: Texts.Text; beg, end, time: LONGINT; BEGIN Texts.OpenScanner(s, Oberon.Par.text, Oberon.Par.pos); Texts.Scan(s); IF (s.class = Texts.Char) & (s.c = "^") THEN Oberon.GetSelection(text, beg, end, time); IF time >= 0 THEN Texts.OpenScanner(s, text, beg); Texts.Scan(s) END END END OpenScanner; PROCEDURE Open*; VAR p: Process; para: Para; f: Frame; task: Task; v: Viewers.Viewer; s: Texts.Scanner; pid: LONGINT; x, y, i: INTEGER; ok: BOOLEAN; BEGIN OpenScanner(s); NEW(para[0]); IF s.class IN {Texts.Name, Texts.String} THEN COPY(s.s, para[0]^) ELSE para[0]^ := "localhost" END; para[1] := NIL; Texts.Scan(s); IF (s.class = Texts.Char) & (s.c = ":") THEN Texts.Scan(s); IF(s.class = Texts.Name) OR (s.class = Texts.String) THEN NEW(para[1]); COPY("-l", para[1]^); NEW(para[2]); COPY(s.s, para[2]^); para[3] := NIL END END; NEW(p); Texts.OpenWriter(p.w); Texts.SetFont(p.w, normFont); Kernel.RegisterObject(p, Finalizer); res := Unix.Pipe(p.readPipe); IF res >= 0 THEN res := Unix.Pipe(p.writePipe); IF res < 0 THEN res := Unix.Close(p.readPipe[READ]); res := Unix.Close(p.readPipe[WRITE]); res := -1 END END; IF res >= 0 THEN pid := Unix.Fork(); IF pid < 0 THEN (* fork error *) res := Unix.Close(p.readPipe[WRITE]); res := Unix.Close(p.readPipe[READ]); res := Unix.Close(p.writePipe[READ]); res := Unix.Close(p.writePipe[WRITE]); Texts.WriteString(p.w, "Fork error: "); Texts.WriteInt(p.w, pid, 0); Texts.WriteLn(p.w); Texts.Append(Oberon.Log, p.w.buf) ELSIF pid = 0 THEN res := Unix.Close(p.readPipe[WRITE]); res := Unix.Close(p.writePipe[READ]); res := Unix.Dup2(p.readPipe[READ], 0); res := Unix.Dup2(p.writePipe[WRITE], 1); res := Unix.Dup2(p.writePipe[WRITE], 2); res := Unix.Close(p.readPipe[READ]); res := Unix.Close(p.writePipe[WRITE]); res := Unix.Execv(SYSTEM.ADR(rlogin), SYSTEM.ADR(para)); res := Unix.Kill(Unix.Getpid(), 9) ELSE NEW(task); p.task := task; task.p := SYSTEM.VAL(LONGINT, p); task.handle := TaskHandle; task.safe := TRUE; task.time := -1; Oberon.Install(task); res := Unix.Close(p.readPipe[READ]); res := Unix.Close(p.writePipe[WRITE]); Oberon.AllocateUserViewer(Oberon.Par.vwr.X, x, y); NEW(f); TextFrames.Open(f, TextFrames.Text(""), 0); f.handle := Handle; f.p := p; p.pid := pid; p.text := f.text; p.dead := FALSE; p.textState := 0; p.lastCh := 0X; p.lastInp := 0; INCL(Kernel.readSet[p.writePipe[READ] DIV 32], p.writePipe[READ] MOD 32); v := MenuViewers.New(TextFrames.NewMenu(para[0]^, Menu), f, TextFrames.menuH, x, y) END ELSE Texts.WriteString(p.w, "Too many files opened."); Texts.WriteLn(p.w); Texts.Append(Oberon.Log, p.w.buf) END END Open; PROCEDURE Send*; VAR v: Viewers.Viewer; s: Texts.Scanner; p: Process; f: Frame; BEGIN v := Oberon.FocusViewer; IF (v.dsc # NIL) & (v.dsc.next # NIL) & (v.dsc.next IS Frame) THEN f := v.dsc.next(Frame); f.p.lastInp := f.text.len; IF f.hasCar THEN OpenScanner(s); p := f.p; WHILE ((s.class = Texts.Name) OR (s.class = Texts.String) OR (s.class = Texts.Int)) & ~p.dead DO IF s.class = Texts.Int THEN Write(p, CHR(s.i)) ELSIF s.class = Texts.Name THEN res := Unix.Write(p.readPipe[WRITE], SYSTEM.ADR(s.s), s.len) ELSE res := Unix.Write(p.readPipe[WRITE], SYSTEM.ADR(s.s), s.len-1) END; Texts.Scan(s) END; SetCaret(f) END END END Send; PROCEDURE Clear*; VAR f: Frame; t: Texts.Text; v: Viewers.Viewer; BEGIN v := Oberon.Par.vwr; IF (v.dsc # NIL) & (v.dsc.next # NIL) & (v.dsc.next IS Frame) THEN f := v.dsc.next(Frame); t := f.text; Texts.Delete(t, 0, t.len); f.p.lastInp := 0; Texts.OpenWriter(f.p.w) END END Clear; PROCEDURE SetFont*; VAR s: Texts.Scanner; name: ARRAY 32 OF CHAR; BEGIN OpenScanner(s); IF s.class = Texts.Name THEN COPY(s.s, name); Texts.Scan(s); IF (s.class = Texts.Char) & (s.c = "\") & (s.nextCh = "b") THEN boldFont := Fonts.This(name) ELSE normFont := Fonts.This(name) END END END SetFont; BEGIN normFont := Fonts.This("Courier10.Scn.Fnt"); boldFont := Fonts.This("Syntax10b.Scn.Fnt"); rlogin:= "/usr/bin/rlogin"; (* << you probably have to change this *) Kernel.dlsym(Kernel.libc, "waitpid", SYSTEM.VAL(LONGINT, waitpid)); END Shell.