mirror of
https://github.com/vishapoberon/compiler.git
synced 2026-10-09 23:17:23 +00:00
adding ulm unix file and terminal streams.
This commit is contained in:
parent
fcf59d5d93
commit
9701249ad2
12 changed files with 1163 additions and 11 deletions
2
make.cmd
2
make.cmd
|
|
@ -391,7 +391,9 @@ cd %BUILDDIR%\%MODEL%
|
|||
%ROOTDIR%\%OBECOMP% -Ffs -O%MODEL% ../../../src/library/ulm/ulmResources.Mod || exit /b
|
||||
%ROOTDIR%\%OBECOMP% -Ffs -O%MODEL% ../../../src/library/ulm/ulmForwarders.Mod || exit /b
|
||||
%ROOTDIR%\%OBECOMP% -Ffs -O%MODEL% ../../../src/library/ulm/ulmRelatedEvents.Mod || exit /b
|
||||
%ROOTDIR%\%OBECOMP% -Ffs -O%MODEL% ../../../src/library/ulm/ulmStreamsHost.Mod || exit /b
|
||||
%ROOTDIR%\%OBECOMP% -Ffs -O%MODEL% ../../../src/library/ulm/ulmStreams.Mod || exit /b
|
||||
%ROOTDIR%\%OBECOMP% -Ffs -O%MODEL% ../../../src/library/ulm/ulmTerminals.Mod || exit /b
|
||||
%ROOTDIR%\%OBECOMP% -Ffs -O%MODEL% ../../../src/library/ulm/ulmStrings.Mod || exit /b
|
||||
%ROOTDIR%\%OBECOMP% -Ffs -O%MODEL% ../../../src/library/ulm/ulmSysTypes.Mod || exit /b
|
||||
%ROOTDIR%\%OBECOMP% -Ffs -O%MODEL% ../../../src/library/ulm/ulmTexts.Mod || exit /b
|
||||
|
|
|
|||
|
|
@ -82,6 +82,21 @@ MODULE ulmSysIO;
|
|||
Protection* = Types.Int32;
|
||||
Whence* = Types.Int32;
|
||||
|
||||
PROCEDURE StdIn*() : File;
|
||||
BEGIN
|
||||
RETURN Platform.StdIn
|
||||
END StdIn;
|
||||
|
||||
PROCEDURE StdOut*() : File;
|
||||
BEGIN
|
||||
RETURN Platform.StdOut
|
||||
END StdOut;
|
||||
|
||||
PROCEDURE StdErr*() : File;
|
||||
BEGIN
|
||||
RETURN Platform.StdErr
|
||||
END StdErr;
|
||||
|
||||
PROCEDURE OpenCreat*(VAR fd: File;
|
||||
filename: ARRAY OF CHAR; options: Types.Set;
|
||||
protection: Protection;
|
||||
|
|
@ -179,8 +194,8 @@ MODULE ulmSysIO;
|
|||
BEGIN
|
||||
interrupted := FALSE;
|
||||
LOOP
|
||||
error := Platform.Write(fd, buf, cnt);
|
||||
IF error = 0 THEN RETURN cnt (* todo: Upfate Platform.Write to return actual length written. *)
|
||||
error := Platform.WriteCount(fd, buf, cnt, byteswritten);
|
||||
IF error = 0 THEN RETURN byteswritten
|
||||
ELSE
|
||||
IF Platform.Interrupted(error) THEN
|
||||
interrupted := TRUE;
|
||||
|
|
@ -211,6 +226,29 @@ MODULE ulmSysIO;
|
|||
END;
|
||||
END Seek;
|
||||
|
||||
PROCEDURE Tell*(fd: File; VAR offset: Count;
|
||||
errors: RelatedEvents.Object) : BOOLEAN;
|
||||
VAR error: Platform.ErrorCode;
|
||||
BEGIN
|
||||
error := Platform.Tell(fd, offset);
|
||||
IF error = 0 THEN RETURN TRUE
|
||||
ELSE
|
||||
SysErrors.Raise(errors, error, Sys.lseek, "");
|
||||
RETURN FALSE
|
||||
END
|
||||
END Tell;
|
||||
|
||||
PROCEDURE Isatty*(fd: File) : BOOLEAN;
|
||||
BEGIN
|
||||
RETURN Platform.IsConsole(fd)
|
||||
END Isatty;
|
||||
|
||||
PROCEDURE Seekable*(fd: File) : BOOLEAN;
|
||||
VAR offset: Count;
|
||||
BEGIN
|
||||
RETURN Platform.Tell(fd, offset) = 0
|
||||
END Seekable;
|
||||
|
||||
(*
|
||||
|
||||
PROCEDURE Tell*(fd: File; VAR offset: Count;
|
||||
|
|
|
|||
309
src/library/ulm/ulmTerminals.Mod
Normal file
309
src/library/ulm/ulmTerminals.Mod
Normal file
|
|
@ -0,0 +1,309 @@
|
|||
(* Basic terminal abstraction from Ulm's Oberon Library. *)
|
||||
MODULE ulmTerminals;
|
||||
|
||||
IMPORT Events := ulmEvents, Objects := ulmObjects, Priorities := ulmPriorities,
|
||||
RelatedEvents := ulmRelatedEvents, Services := ulmServices,
|
||||
Streams := ulmStreams;
|
||||
|
||||
CONST
|
||||
autoleftmargin* = 0;
|
||||
autorightmargin* = 1;
|
||||
overstrikes* = 2;
|
||||
safelastcolumn* = 3;
|
||||
|
||||
cannotSetEcho* = 0;
|
||||
cannotSetTermMode* = 1;
|
||||
cannotSetCursor* = 2;
|
||||
cannotMoveCursor* = 3;
|
||||
cannotSetAppearance* = 4;
|
||||
cannotScroll* = 5;
|
||||
cannotSetScrollRegion* = 6;
|
||||
cannotClearScreen* = 7;
|
||||
invalidDirection* = 8;
|
||||
invalidRegion* = 9;
|
||||
invalidPosition* = 10;
|
||||
notSupported* = 11;
|
||||
errorcodes* = 12;
|
||||
|
||||
on* = 0;
|
||||
off* = 1;
|
||||
raw* = 0;
|
||||
cooked* = 1;
|
||||
forward* = 0;
|
||||
reverse* = 1;
|
||||
visible* = 0;
|
||||
invisible* = 1;
|
||||
|
||||
setEcho* = 0;
|
||||
setTermMode* = 1;
|
||||
setCursor* = 2;
|
||||
moveCursor* = 3;
|
||||
setAppearance* = 4;
|
||||
scroll* = 5;
|
||||
setScrollRegion* = 6;
|
||||
clearScreen* = 7;
|
||||
|
||||
TYPE
|
||||
CapabilitySet* = SET;
|
||||
EchoMode* = SHORTINT;
|
||||
TermMode* = SHORTINT;
|
||||
Direction* = SHORTINT;
|
||||
Shape* = SHORTINT;
|
||||
Stream* = POINTER TO StreamRec;
|
||||
|
||||
WindowChangeEvent* = POINTER TO WindowChangeEventRec;
|
||||
WindowChangeEventRec* = RECORD (Events.EventRec)
|
||||
stream*: Streams.Stream;
|
||||
newlines*, newcolumns*: INTEGER
|
||||
END;
|
||||
|
||||
InterruptEvent* = POINTER TO InterruptEventRec;
|
||||
InterruptEventRec* = RECORD (Events.EventRec)
|
||||
stream*: Streams.Stream
|
||||
END;
|
||||
|
||||
QuitEvent* = POINTER TO QuitEventRec;
|
||||
QuitEventRec* = RECORD (Events.EventRec)
|
||||
stream*: Streams.Stream
|
||||
END;
|
||||
|
||||
HangupEvent* = POINTER TO HangupEventRec;
|
||||
HangupEventRec* = RECORD (Events.EventRec)
|
||||
stream*: Streams.Stream
|
||||
END;
|
||||
|
||||
ErrorEvent* = POINTER TO ErrorEventRec;
|
||||
ErrorEventRec* = RECORD (Events.EventRec)
|
||||
errorcode*: SHORTINT
|
||||
END;
|
||||
|
||||
SetTermModeProc* = PROCEDURE(s: Streams.Stream; mode: TermMode);
|
||||
SetEchoProc* = PROCEDURE(s: Streams.Stream; mode: EchoMode);
|
||||
SetCursorProc* = PROCEDURE(s: Streams.Stream; line, column: INTEGER);
|
||||
MoveCursorProc* = PROCEDURE(s: Streams.Stream; fromline, fromcolumn,
|
||||
toline, tocolumn: INTEGER);
|
||||
SetAppearanceProc* = PROCEDURE(s: Streams.Stream; shape: Shape);
|
||||
ScrollProc* = PROCEDURE(s: Streams.Stream; dir: Direction);
|
||||
SetScrollRegionProc* = PROCEDURE(s: Streams.Stream; top, bottom: INTEGER);
|
||||
ClearProc* = PROCEDURE(s: Streams.Stream);
|
||||
|
||||
Interface* = POINTER TO InterfaceRec;
|
||||
InterfaceRec* = RECORD (Objects.ObjectRec)
|
||||
setEcho*: SetEchoProc;
|
||||
setTermMode*: SetTermModeProc;
|
||||
setCursor*: SetCursorProc;
|
||||
moveCursor*: MoveCursorProc;
|
||||
setAppearance*: SetAppearanceProc;
|
||||
scroll*: ScrollProc;
|
||||
setScrollRegion*: SetScrollRegionProc;
|
||||
clearScreen*: ClearProc
|
||||
END;
|
||||
|
||||
Status* = RECORD (Objects.ObjectRec)
|
||||
lines*, columns*: INTEGER;
|
||||
scrtop*, scrbottom*: INTEGER;
|
||||
echo*: EchoMode;
|
||||
mode*: TermMode;
|
||||
characteristics*: SET;
|
||||
scrollDirections*: SET;
|
||||
cursorShape*: Shape
|
||||
END;
|
||||
|
||||
StreamRec* = RECORD (Streams.StreamRec)
|
||||
status: Status;
|
||||
caps: CapabilitySet;
|
||||
interface: Interface
|
||||
END;
|
||||
|
||||
VAR
|
||||
console*: Streams.Stream;
|
||||
windowchanged*, interrupt*, quit*, hangup*: Events.EventType;
|
||||
error*: Events.EventType;
|
||||
errormsg*: ARRAY errorcodes OF Events.Message;
|
||||
terminaltype: Services.Type;
|
||||
|
||||
PROCEDURE Error(object: RelatedEvents.Object; errorcode: SHORTINT);
|
||||
VAR event: ErrorEvent;
|
||||
BEGIN
|
||||
NEW(event);
|
||||
event.type := error;
|
||||
event.message := errormsg[errorcode];
|
||||
event.errorcode := errorcode;
|
||||
RelatedEvents.Raise(object, event)
|
||||
END Error;
|
||||
|
||||
PROCEDURE Init*(s: Stream; status: Status; caps: CapabilitySet;
|
||||
interface: Interface);
|
||||
BEGIN
|
||||
ASSERT(interface # NIL);
|
||||
s.status := status;
|
||||
s.caps := caps;
|
||||
s.interface := interface
|
||||
END Init;
|
||||
|
||||
PROCEDURE SetScreenSize(event: Events.Event);
|
||||
BEGIN
|
||||
WITH event: WindowChangeEvent DO
|
||||
event.stream(Stream).status.lines := event.newlines;
|
||||
event.stream(Stream).status.columns := event.newcolumns
|
||||
END
|
||||
END SetScreenSize;
|
||||
|
||||
PROCEDURE ClearScreen*(s: Streams.Stream);
|
||||
BEGIN
|
||||
WITH s: Stream DO
|
||||
IF clearScreen IN s.caps THEN s.interface.clearScreen(s)
|
||||
ELSE Error(s, cannotClearScreen)
|
||||
END
|
||||
END
|
||||
END ClearScreen;
|
||||
|
||||
PROCEDURE Echo*(s: Streams.Stream; mode: EchoMode);
|
||||
BEGIN
|
||||
WITH s: Stream DO
|
||||
IF s.status.echo # mode THEN
|
||||
IF setEcho IN s.caps THEN
|
||||
s.interface.setEcho(s, mode);
|
||||
s.status.echo := mode
|
||||
ELSE
|
||||
Error(s, cannotSetEcho)
|
||||
END
|
||||
END
|
||||
END
|
||||
END Echo;
|
||||
|
||||
PROCEDURE SetTermMode*(s: Streams.Stream; mode: TermMode);
|
||||
BEGIN
|
||||
WITH s: Stream DO
|
||||
IF s.status.mode # mode THEN
|
||||
IF setTermMode IN s.caps THEN
|
||||
s.interface.setTermMode(s, mode);
|
||||
s.status.mode := mode
|
||||
ELSE
|
||||
Error(s, cannotSetTermMode)
|
||||
END
|
||||
END
|
||||
END
|
||||
END SetTermMode;
|
||||
|
||||
PROCEDURE SetCursor*(s: Streams.Stream; line, column: INTEGER);
|
||||
BEGIN
|
||||
WITH s: Stream DO
|
||||
IF (line >= 0) & (line < s.status.lines) &
|
||||
(column >= 0) & (column < s.status.columns) THEN
|
||||
IF setCursor IN s.caps THEN s.interface.setCursor(s, line, column)
|
||||
ELSE Error(s, cannotSetCursor)
|
||||
END
|
||||
ELSE
|
||||
Error(s, invalidPosition)
|
||||
END
|
||||
END
|
||||
END SetCursor;
|
||||
|
||||
PROCEDURE MoveCursor*(s: Streams.Stream; fromline, fromcolumn,
|
||||
toline, tocolumn: INTEGER);
|
||||
BEGIN
|
||||
WITH s: Stream DO
|
||||
IF (fromline >= 0) & (fromline < s.status.lines) &
|
||||
(toline >= 0) & (toline < s.status.lines) &
|
||||
(fromcolumn >= 0) & (fromcolumn <= s.status.columns) &
|
||||
(tocolumn >= 0) & (tocolumn < s.status.columns) THEN
|
||||
IF moveCursor IN s.caps THEN
|
||||
s.interface.moveCursor(s, fromline, fromcolumn, toline, tocolumn)
|
||||
ELSIF setCursor IN s.caps THEN
|
||||
s.interface.setCursor(s, toline, tocolumn)
|
||||
ELSE
|
||||
Error(s, cannotMoveCursor)
|
||||
END
|
||||
ELSE
|
||||
Error(s, invalidPosition)
|
||||
END
|
||||
END
|
||||
END MoveCursor;
|
||||
|
||||
PROCEDURE CursorAppearance*(s: Streams.Stream; shape: Shape);
|
||||
BEGIN
|
||||
WITH s: Stream DO
|
||||
IF shape # s.status.cursorShape THEN
|
||||
IF setAppearance IN s.caps THEN
|
||||
s.interface.setAppearance(s, shape);
|
||||
s.status.cursorShape := shape
|
||||
ELSE
|
||||
Error(s, cannotSetAppearance)
|
||||
END
|
||||
END
|
||||
END
|
||||
END CursorAppearance;
|
||||
|
||||
PROCEDURE Scroll*(s: Streams.Stream; dir: Direction);
|
||||
BEGIN
|
||||
WITH s: Stream DO
|
||||
IF dir IN s.status.scrollDirections THEN
|
||||
IF scroll IN s.caps THEN s.interface.scroll(s, dir)
|
||||
ELSE Error(s, cannotScroll)
|
||||
END
|
||||
ELSE
|
||||
Error(s, invalidDirection)
|
||||
END
|
||||
END
|
||||
END Scroll;
|
||||
|
||||
PROCEDURE SetScrollRegion*(s: Streams.Stream; top, bottom: INTEGER);
|
||||
BEGIN
|
||||
WITH s: Stream DO
|
||||
IF (top >= 0) & (top < s.status.lines) &
|
||||
(bottom >= 0) & (bottom < s.status.lines) & (top < bottom) THEN
|
||||
IF setScrollRegion IN s.caps THEN
|
||||
s.interface.setScrollRegion(s, top, bottom);
|
||||
s.status.scrtop := top;
|
||||
s.status.scrbottom := bottom;
|
||||
IF setCursor IN s.caps THEN SetCursor(s, bottom, 0) END
|
||||
ELSE
|
||||
Error(s, cannotSetScrollRegion)
|
||||
END
|
||||
ELSE
|
||||
Error(s, invalidRegion)
|
||||
END
|
||||
END
|
||||
END SetScrollRegion;
|
||||
|
||||
PROCEDURE Capabilities*(s: Streams.Stream): CapabilitySet;
|
||||
BEGIN
|
||||
WITH s: Stream DO RETURN s.caps END
|
||||
END Capabilities;
|
||||
|
||||
PROCEDURE GetStatus*(s: Streams.Stream; VAR status: Status);
|
||||
BEGIN
|
||||
WITH s: Stream DO status := s.status END
|
||||
END GetStatus;
|
||||
|
||||
BEGIN
|
||||
Services.CreateType(terminaltype, "Terminals.Stream", "Streams.Stream");
|
||||
|
||||
Events.Define(error);
|
||||
Events.SetPriority(error, Priorities.liberrors);
|
||||
errormsg[cannotSetEcho] := "cannot change echo mode";
|
||||
errormsg[cannotSetTermMode] := "cannot change terminal mode";
|
||||
errormsg[cannotSetCursor] := "cannot set cursor";
|
||||
errormsg[cannotMoveCursor] := "cannot move cursor";
|
||||
errormsg[cannotSetAppearance] := "cannot change appearance of cursor";
|
||||
errormsg[cannotScroll] := "terminal cannot scroll";
|
||||
errormsg[cannotSetScrollRegion] := "scroll regions not supported by terminal";
|
||||
errormsg[cannotClearScreen] := "cannot clear screen";
|
||||
errormsg[invalidDirection] := "direction not valid";
|
||||
errormsg[invalidPosition] := "cursor coordinates not valid";
|
||||
errormsg[invalidRegion] := "parameters for scroll region not valid";
|
||||
errormsg[notSupported] :=
|
||||
"module Terminals not supported by underlying stream implementation";
|
||||
|
||||
Events.Define(windowchanged);
|
||||
Events.Define(interrupt);
|
||||
Events.Define(quit);
|
||||
Events.Define(hangup);
|
||||
Events.SetPriority(windowchanged, Priorities.interrupts);
|
||||
Events.SetPriority(interrupt, Priorities.interrupts);
|
||||
Events.SetPriority(quit, Priorities.interrupts);
|
||||
Events.SetPriority(hangup, Priorities.interrupts);
|
||||
Events.Handler(windowchanged, SetScreenSize);
|
||||
console := NIL
|
||||
END ulmTerminals.
|
||||
235
src/library/ulm/ulmUnixFiles.Mod
Normal file
235
src/library/ulm/ulmUnixFiles.Mod
Normal file
|
|
@ -0,0 +1,235 @@
|
|||
(* Platform-backed port of Ulm's UnixFiles stream module. *)
|
||||
MODULE ulmUnixFiles;
|
||||
|
||||
IMPORT Events := ulmEvents, Priorities := ulmPriorities,
|
||||
RelatedEvents := ulmRelatedEvents, Services := ulmServices,
|
||||
Streams := ulmStreams, Sys := ulmSys, SysErrors := ulmSysErrors,
|
||||
SysIO := ulmSysIO, SysStat := ulmSysStat, SysTypes := ulmSysTypes,
|
||||
Platform, SYSTEM;
|
||||
|
||||
CONST
|
||||
illegalMode* = 0;
|
||||
invalidFd* = 1;
|
||||
errorcodes* = 2;
|
||||
|
||||
read* = 0;
|
||||
write* = 1;
|
||||
rdwr* = 2;
|
||||
create* = 4;
|
||||
condcreate* = 8;
|
||||
|
||||
TYPE
|
||||
ErrorCode* = SHORTINT;
|
||||
ErrorEvent* = POINTER TO ErrorEventRec;
|
||||
ErrorEventRec* = RECORD (Events.EventRec)
|
||||
errorcode*: ErrorCode
|
||||
END;
|
||||
|
||||
Mode* = SHORTINT;
|
||||
Stream* = POINTER TO StreamRec;
|
||||
StreamRec* = RECORD (Streams.StreamRec)
|
||||
file*: SysTypes.File;
|
||||
interrupted*: BOOLEAN;
|
||||
retry*: BOOLEAN
|
||||
END;
|
||||
|
||||
VAR
|
||||
error*: Events.EventType;
|
||||
errormsg*: ARRAY errorcodes OF Events.Message;
|
||||
interface: Streams.Interface;
|
||||
type: Services.Type;
|
||||
|
||||
PROCEDURE Error(errors: RelatedEvents.Object; errorcode: ErrorCode);
|
||||
VAR event: ErrorEvent;
|
||||
BEGIN
|
||||
NEW(event);
|
||||
event.type := error;
|
||||
event.message := errormsg[errorcode];
|
||||
event.errorcode := errorcode;
|
||||
RelatedEvents.Raise(errors, event)
|
||||
END Error;
|
||||
|
||||
PROCEDURE ReadBuf(s: Streams.Stream; address: SysTypes.Address;
|
||||
count: SysTypes.Count): SysTypes.Count;
|
||||
BEGIN
|
||||
WITH s: Stream DO
|
||||
RETURN SysIO.Read(s.file, address, count, s, s.retry, s.interrupted)
|
||||
END
|
||||
END ReadBuf;
|
||||
|
||||
PROCEDURE WriteBuf(s: Streams.Stream; address: SysTypes.Address;
|
||||
count: SysTypes.Count): SysTypes.Count;
|
||||
BEGIN
|
||||
WITH s: Stream DO
|
||||
RETURN SysIO.Write(s.file, address, count, s, s.retry, s.interrupted)
|
||||
END
|
||||
END WriteBuf;
|
||||
|
||||
PROCEDURE ReadByte(s: Streams.Stream; VAR byte: Streams.Byte): BOOLEAN;
|
||||
VAR count: SysTypes.Count;
|
||||
BEGIN
|
||||
WITH s: Stream DO
|
||||
count := SysIO.Read(s.file, SYSTEM.ADR(byte), 1, s,
|
||||
s.retry, s.interrupted);
|
||||
RETURN count = 1
|
||||
END
|
||||
END ReadByte;
|
||||
|
||||
PROCEDURE WriteByte(s: Streams.Stream; byte: Streams.Byte): BOOLEAN;
|
||||
VAR count: SysTypes.Count;
|
||||
BEGIN
|
||||
WITH s: Stream DO
|
||||
count := SysIO.Write(s.file, SYSTEM.ADR(byte), 1, s,
|
||||
s.retry, s.interrupted);
|
||||
RETURN count = 1
|
||||
END
|
||||
END WriteByte;
|
||||
|
||||
PROCEDURE Seek(s: Streams.Stream; offset: SysTypes.Count;
|
||||
whence: Streams.Whence): BOOLEAN;
|
||||
BEGIN
|
||||
WITH s: Stream DO
|
||||
RETURN SysIO.Seek(s.file, offset, whence, s)
|
||||
END
|
||||
END Seek;
|
||||
|
||||
PROCEDURE Tell(s: Streams.Stream; VAR offset: SysTypes.Count): BOOLEAN;
|
||||
BEGIN
|
||||
WITH s: Stream DO
|
||||
RETURN SysIO.Tell(s.file, offset, s)
|
||||
END
|
||||
END Tell;
|
||||
|
||||
PROCEDURE Close(s: Streams.Stream): BOOLEAN;
|
||||
BEGIN
|
||||
WITH s: Stream DO
|
||||
RETURN SysIO.Close(s.file, s, FALSE, s.interrupted)
|
||||
END
|
||||
END Close;
|
||||
|
||||
PROCEDURE OpenFd*(VAR s: Streams.Stream; fd: SysTypes.File;
|
||||
mode: Mode; bufmode: Streams.BufMode;
|
||||
errors: RelatedEvents.Object): BOOLEAN;
|
||||
VAR caps: Streams.CapabilitySet; newfile: Stream; stat: SysStat.StatRec;
|
||||
BEGIN
|
||||
IF ~SysStat.Fstat(fd, stat, errors) THEN
|
||||
Error(errors, invalidFd);
|
||||
RETURN FALSE
|
||||
END;
|
||||
|
||||
caps := {Streams.addrio, Streams.close};
|
||||
CASE mode OF
|
||||
| read: INCL(caps, Streams.read)
|
||||
| write: INCL(caps, Streams.write)
|
||||
| rdwr: caps := caps + {Streams.read, Streams.write}
|
||||
ELSE
|
||||
Error(errors, illegalMode);
|
||||
RETURN FALSE
|
||||
END;
|
||||
|
||||
IF SysIO.Seekable(fd) THEN
|
||||
caps := caps + {Streams.seek, Streams.tell, Streams.holes}
|
||||
END;
|
||||
|
||||
NEW(newfile);
|
||||
Services.Init(newfile, type);
|
||||
Streams.Init(newfile, interface, caps, bufmode);
|
||||
newfile.file := fd;
|
||||
newfile.interrupted := FALSE;
|
||||
newfile.retry := TRUE;
|
||||
RelatedEvents.QueueEvents(newfile);
|
||||
s := newfile;
|
||||
RETURN TRUE
|
||||
END OpenFd;
|
||||
|
||||
PROCEDURE Open*(VAR s: Streams.Stream; filename: ARRAY OF CHAR;
|
||||
mode: Mode; bufmode: Streams.BufMode;
|
||||
errors: RelatedEvents.Object): BOOLEAN;
|
||||
CONST accessMask = 4;
|
||||
VAR accessMode, openMode: Mode; access, creation: INTEGER;
|
||||
fd: SysTypes.File; interrupted: BOOLEAN; hostError: Platform.ErrorCode;
|
||||
BEGIN
|
||||
accessMode := mode MOD accessMask;
|
||||
openMode := mode - accessMode;
|
||||
|
||||
CASE accessMode OF
|
||||
| read: access := Platform.ReadOnly
|
||||
| write: access := Platform.WriteOnly
|
||||
| rdwr: access := Platform.ReadWrite
|
||||
ELSE
|
||||
Error(errors, illegalMode);
|
||||
RETURN FALSE
|
||||
END;
|
||||
|
||||
CASE openMode OF
|
||||
| 0: creation := Platform.OpenExisting
|
||||
| create: creation := Platform.CreateAlways
|
||||
| condcreate: creation := Platform.OpenAlways
|
||||
ELSE
|
||||
Error(errors, illegalMode);
|
||||
RETURN FALSE
|
||||
END;
|
||||
|
||||
filename[LEN(filename)-1] := 0X;
|
||||
REPEAT
|
||||
hostError := Platform.OpenFile(filename, access, creation, fd)
|
||||
UNTIL (hostError = 0) OR ~Platform.Interrupted(hostError);
|
||||
IF hostError # 0 THEN
|
||||
SysErrors.Raise(errors, hostError, Sys.open, filename);
|
||||
RETURN FALSE
|
||||
END;
|
||||
|
||||
IF OpenFd(s, fd, accessMode, bufmode, errors) THEN
|
||||
RETURN TRUE
|
||||
END;
|
||||
IF ~SysIO.Close(fd, errors, FALSE, interrupted) THEN END;
|
||||
RETURN FALSE
|
||||
END Open;
|
||||
|
||||
PROCEDURE InitInterface;
|
||||
BEGIN
|
||||
NEW(interface);
|
||||
interface.addrread := ReadBuf;
|
||||
interface.addrwrite := WriteBuf;
|
||||
interface.read := ReadByte;
|
||||
interface.write := WriteByte;
|
||||
interface.seek := Seek;
|
||||
interface.tell := Tell;
|
||||
interface.close := Close
|
||||
END InitInterface;
|
||||
|
||||
PROCEDURE InitStandardStreams;
|
||||
PROCEDURE Connect(VAR stream: Streams.Stream; fd: SysTypes.File; mode: Mode);
|
||||
VAR bufmode: Streams.BufMode;
|
||||
BEGIN
|
||||
IF fd = SysIO.StdErr() THEN
|
||||
bufmode := Streams.nobuf
|
||||
ELSIF SysIO.Isatty(fd) THEN
|
||||
bufmode := Streams.linebuf
|
||||
ELSIF fd = SysIO.StdOut() THEN
|
||||
(* ULM process termination is not wired into VOC program shutdown. *)
|
||||
bufmode := Streams.nobuf
|
||||
ELSE
|
||||
bufmode := Streams.onebuf
|
||||
END;
|
||||
IF ~OpenFd(stream, fd, mode, bufmode, NIL) THEN END
|
||||
END Connect;
|
||||
BEGIN
|
||||
Connect(Streams.stdin, SysIO.StdIn(), rdwr);
|
||||
Connect(Streams.stdout, SysIO.StdOut(), rdwr);
|
||||
Connect(Streams.stderr, SysIO.StdErr(), write);
|
||||
IF (Streams.GetBufMode(Streams.stdin) = Streams.linebuf) &
|
||||
(Streams.GetBufMode(Streams.stdout) = Streams.linebuf) THEN
|
||||
Streams.Tie(Streams.stdin, Streams.stdout)
|
||||
END
|
||||
END InitStandardStreams;
|
||||
|
||||
BEGIN
|
||||
errormsg[illegalMode] := "illegal opening mode";
|
||||
errormsg[invalidFd] := "invalid file descriptor";
|
||||
Events.Define(error);
|
||||
Events.SetPriority(error, Priorities.liberrors);
|
||||
InitInterface;
|
||||
Services.CreateType(type, "UnixFiles.Stream", "Streams.Stream");
|
||||
InitStandardStreams
|
||||
END ulmUnixFiles.
|
||||
327
src/library/ulm/ulmUnixTerminals.Mod
Normal file
327
src/library/ulm/ulmUnixTerminals.Mod
Normal file
|
|
@ -0,0 +1,327 @@
|
|||
(* Synchronous Unix terminal streams backed by Platform termios operations. *)
|
||||
MODULE ulmUnixTerminals;
|
||||
|
||||
IMPORT Heap, SYSTEM, Forwarders := ulmForwarders,
|
||||
RelatedEvents := ulmRelatedEvents, Services := ulmServices,
|
||||
Streams := ulmStreams, Sys := ulmSys, SysErrors := ulmSysErrors,
|
||||
Terminals := ulmTerminals, UnixFiles := ulmUnixFiles, Platform;
|
||||
|
||||
TYPE
|
||||
Stream = POINTER TO StreamRec;
|
||||
StreamRec = RECORD (Terminals.StreamRec)
|
||||
instream, outstream: Streams.Stream;
|
||||
inputState, outputState: Platform.TerminalState;
|
||||
inputHandle, outputHandle: Platform.FileHandle;
|
||||
owned: Streams.Stream
|
||||
END;
|
||||
|
||||
VAR
|
||||
streamType: Services.Type;
|
||||
streamInterface: Streams.Interface;
|
||||
terminalInterface: Terminals.Interface;
|
||||
|
||||
PROCEDURE RaiseHostError(s: Streams.Stream; error: Platform.ErrorCode);
|
||||
BEGIN
|
||||
IF error # 0 THEN SysErrors.Raise(s, error, Sys.ioctl, "") END
|
||||
END RaiseHostError;
|
||||
|
||||
PROCEDURE ReadByte(s: Streams.Stream; VAR byte: Streams.Byte): BOOLEAN;
|
||||
BEGIN
|
||||
WITH s: Stream DO RETURN Streams.ReadByte(s.instream, byte) END
|
||||
END ReadByte;
|
||||
|
||||
PROCEDURE WriteByte(s: Streams.Stream; byte: Streams.Byte): BOOLEAN;
|
||||
BEGIN
|
||||
WITH s: Stream DO RETURN Streams.WriteByte(s.outstream, byte) END
|
||||
END WriteByte;
|
||||
|
||||
PROCEDURE Flush(s: Streams.Stream): BOOLEAN;
|
||||
BEGIN
|
||||
WITH s: Stream DO RETURN Streams.Flush(s.outstream) END
|
||||
END Flush;
|
||||
|
||||
PROCEDURE RestoreState(handle: Platform.FileHandle;
|
||||
VAR state: Platform.TerminalState): Platform.ErrorCode;
|
||||
VAR error: Platform.ErrorCode;
|
||||
BEGIN
|
||||
IF state = 0 THEN RETURN 0 END;
|
||||
REPEAT
|
||||
error := Platform.RestoreTerminalState(handle, state)
|
||||
UNTIL (error = 0) OR ~Platform.Interrupted(error);
|
||||
IF error = 0 THEN Platform.ReleaseTerminalState(state) END;
|
||||
RETURN error
|
||||
END RestoreState;
|
||||
|
||||
PROCEDURE Restore(s: Stream; report: BOOLEAN): BOOLEAN;
|
||||
VAR ok: BOOLEAN; error: Platform.ErrorCode;
|
||||
BEGIN
|
||||
ok := TRUE;
|
||||
error := RestoreState(s.outputHandle, s.outputState);
|
||||
IF error # 0 THEN
|
||||
IF report THEN RaiseHostError(s, error) END;
|
||||
ok := FALSE
|
||||
END;
|
||||
error := RestoreState(s.inputHandle, s.inputState);
|
||||
IF error # 0 THEN
|
||||
IF report THEN RaiseHostError(s, error) END;
|
||||
ok := FALSE
|
||||
END;
|
||||
RETURN ok
|
||||
END Restore;
|
||||
|
||||
PROCEDURE Close(s: Streams.Stream): BOOLEAN;
|
||||
VAR ok: BOOLEAN;
|
||||
BEGIN
|
||||
WITH s: Stream DO
|
||||
ok := Restore(s, TRUE);
|
||||
IF ok & (s.owned # NIL) THEN
|
||||
ok := Streams.Close(s.owned);
|
||||
IF ok THEN s.owned := NIL END
|
||||
END;
|
||||
RETURN ok
|
||||
END
|
||||
END Close;
|
||||
|
||||
PROCEDURE Finalize(object: SYSTEM.PTR);
|
||||
VAR s: Stream;
|
||||
BEGIN
|
||||
s := SYSTEM.VAL(Stream, object);
|
||||
IF ~Restore(s, FALSE) THEN
|
||||
Platform.ReleaseTerminalState(s.outputState);
|
||||
Platform.ReleaseTerminalState(s.inputState)
|
||||
END;
|
||||
IF s.owned # NIL THEN
|
||||
IF Streams.Close(s.owned) THEN END;
|
||||
s.owned := NIL
|
||||
END
|
||||
END Finalize;
|
||||
|
||||
PROCEDURE SetEcho(s: Streams.Stream; mode: Terminals.EchoMode);
|
||||
VAR error: Platform.ErrorCode;
|
||||
BEGIN
|
||||
WITH s: Stream DO
|
||||
IF s.inputState # 0 THEN
|
||||
REPEAT
|
||||
error := Platform.SetTerminalEcho(s.inputHandle, mode = Terminals.on)
|
||||
UNTIL (error = 0) OR ~Platform.Interrupted(error);
|
||||
RaiseHostError(s, error)
|
||||
END
|
||||
END
|
||||
END SetEcho;
|
||||
|
||||
PROCEDURE SetTermMode(s: Streams.Stream; mode: Terminals.TermMode);
|
||||
VAR error: Platform.ErrorCode; raw: BOOLEAN;
|
||||
BEGIN
|
||||
WITH s: Stream DO
|
||||
raw := mode = Terminals.raw;
|
||||
IF s.inputState # 0 THEN
|
||||
REPEAT
|
||||
error := Platform.SetTerminalInputMode(s.inputHandle, raw)
|
||||
UNTIL (error = 0) OR ~Platform.Interrupted(error);
|
||||
RaiseHostError(s, error)
|
||||
END;
|
||||
IF s.outputState # 0 THEN
|
||||
IF ~Streams.Flush(s.outstream) THEN END;
|
||||
REPEAT
|
||||
error := Platform.SetTerminalOutputMode(s.outputHandle, raw)
|
||||
UNTIL (error = 0) OR ~Platform.Interrupted(error);
|
||||
RaiseHostError(s, error)
|
||||
END
|
||||
END
|
||||
END SetTermMode;
|
||||
|
||||
PROCEDURE Open*(VAR s: Streams.Stream; instream, outstream: Streams.Stream;
|
||||
tiname: ARRAY OF CHAR;
|
||||
errors: RelatedEvents.Object): BOOLEAN;
|
||||
VAR newterm: Stream; status: Terminals.Status;
|
||||
caps: Terminals.CapabilitySet; streamCaps: Streams.CapabilitySet;
|
||||
hostError: Platform.ErrorCode;
|
||||
BEGIN
|
||||
IF (instream = NIL) OR (outstream = NIL) THEN RETURN FALSE END;
|
||||
streamCaps := Streams.Capabilities(instream);
|
||||
IF ~(Streams.read IN streamCaps) THEN RETURN FALSE END;
|
||||
streamCaps := Streams.Capabilities(outstream);
|
||||
IF ~(Streams.write IN streamCaps) THEN RETURN FALSE END;
|
||||
|
||||
NEW(newterm);
|
||||
newterm.instream := instream;
|
||||
newterm.outstream := outstream;
|
||||
newterm.inputState := 0;
|
||||
newterm.outputState := 0;
|
||||
newterm.owned := NIL;
|
||||
caps := {};
|
||||
|
||||
IF instream IS UnixFiles.Stream THEN
|
||||
newterm.inputHandle := instream(UnixFiles.Stream).file;
|
||||
IF Platform.IsConsole(newterm.inputHandle) THEN
|
||||
hostError := Platform.CaptureTerminalState(newterm.inputHandle,
|
||||
newterm.inputState);
|
||||
IF hostError # 0 THEN
|
||||
SysErrors.Raise(errors, hostError, Sys.ioctl, "");
|
||||
RETURN FALSE
|
||||
END;
|
||||
INCL(caps, Terminals.setEcho);
|
||||
INCL(caps, Terminals.setTermMode)
|
||||
END
|
||||
END;
|
||||
|
||||
IF outstream IS UnixFiles.Stream THEN
|
||||
newterm.outputHandle := outstream(UnixFiles.Stream).file;
|
||||
IF Platform.IsConsole(newterm.outputHandle) THEN
|
||||
hostError := Platform.CaptureTerminalState(newterm.outputHandle,
|
||||
newterm.outputState);
|
||||
IF hostError # 0 THEN
|
||||
IF newterm.inputState # 0 THEN
|
||||
Platform.ReleaseTerminalState(newterm.inputState)
|
||||
END;
|
||||
SysErrors.Raise(errors, hostError, Sys.ioctl, "");
|
||||
RETURN FALSE
|
||||
END;
|
||||
INCL(caps, Terminals.setTermMode)
|
||||
END
|
||||
END;
|
||||
|
||||
IF caps = {} THEN RETURN FALSE END;
|
||||
|
||||
IF newterm.inputState # 0 THEN
|
||||
hostError := Platform.SetTerminalEcho(newterm.inputHandle, TRUE);
|
||||
IF hostError = 0 THEN
|
||||
hostError := Platform.SetTerminalInputMode(newterm.inputHandle, FALSE)
|
||||
END
|
||||
ELSE
|
||||
hostError := 0
|
||||
END;
|
||||
IF (hostError = 0) & (newterm.outputState # 0) THEN
|
||||
IF Streams.Flush(outstream) THEN
|
||||
hostError := Platform.SetTerminalOutputMode(newterm.outputHandle, FALSE)
|
||||
ELSE
|
||||
IF ~Restore(newterm, FALSE) THEN END;
|
||||
RETURN FALSE
|
||||
END
|
||||
END;
|
||||
IF hostError # 0 THEN
|
||||
IF ~Restore(newterm, FALSE) THEN END;
|
||||
SysErrors.Raise(errors, hostError, Sys.ioctl, "");
|
||||
RETURN FALSE
|
||||
END;
|
||||
|
||||
status.lines := 24;
|
||||
status.columns := 80;
|
||||
IF newterm.outputState # 0 THEN
|
||||
hostError := Platform.GetTerminalSize(newterm.outputHandle,
|
||||
status.lines, status.columns)
|
||||
ELSE
|
||||
hostError := Platform.GetTerminalSize(newterm.inputHandle,
|
||||
status.lines, status.columns)
|
||||
END;
|
||||
IF (hostError # 0) OR (status.lines <= 0) OR (status.columns <= 0) THEN
|
||||
status.lines := 24;
|
||||
status.columns := 80
|
||||
END;
|
||||
status.scrtop := 0;
|
||||
status.scrbottom := status.lines - 1;
|
||||
status.echo := Terminals.on;
|
||||
status.mode := Terminals.cooked;
|
||||
status.characteristics := {};
|
||||
status.scrollDirections := {};
|
||||
status.cursorShape := Terminals.visible;
|
||||
|
||||
Services.Init(newterm, streamType);
|
||||
Streams.Init(newterm, streamInterface,
|
||||
{Streams.read, Streams.write, Streams.flush, Streams.close},
|
||||
Streams.nobuf);
|
||||
Terminals.Init(newterm, status, caps, terminalInterface);
|
||||
RelatedEvents.QueueEvents(newterm);
|
||||
RelatedEvents.Forward(instream, newterm);
|
||||
Forwarders.Forward(newterm, outstream);
|
||||
Heap.RegisterFinalizer(newterm, Finalize);
|
||||
s := newterm;
|
||||
RETURN TRUE
|
||||
END Open;
|
||||
|
||||
PROCEDURE OpenByName*(VAR s: Streams.Stream; devicename, tiname: ARRAY OF CHAR;
|
||||
errors: RelatedEvents.Object): BOOLEAN;
|
||||
VAR name: ARRAY 1024 OF CHAR; file: Streams.Stream;
|
||||
BEGIN
|
||||
IF devicename[0] = 0X THEN COPY("/dev/tty", name)
|
||||
ELSE COPY(devicename, name)
|
||||
END;
|
||||
IF ~UnixFiles.Open(file, name, UnixFiles.rdwr, Streams.nobuf, errors) THEN
|
||||
RETURN FALSE
|
||||
END;
|
||||
IF Open(s, file, file, tiname, errors) THEN
|
||||
s(Stream).owned := file;
|
||||
RETURN TRUE
|
||||
END;
|
||||
IF ~Streams.Close(file) THEN END;
|
||||
RETURN FALSE
|
||||
END OpenByName;
|
||||
|
||||
PROCEDURE InitInterfaces;
|
||||
BEGIN
|
||||
NEW(streamInterface);
|
||||
streamInterface.read := ReadByte;
|
||||
streamInterface.write := WriteByte;
|
||||
streamInterface.flush := Flush;
|
||||
streamInterface.close := Close;
|
||||
|
||||
NEW(terminalInterface);
|
||||
terminalInterface.setEcho := SetEcho;
|
||||
terminalInterface.setTermMode := SetTermMode
|
||||
END InitInterfaces;
|
||||
|
||||
PROCEDURE OpenConsole;
|
||||
VAR console, oldout, file: Streams.Stream;
|
||||
name, tiname: ARRAY 16 OF CHAR; handle: Platform.FileHandle;
|
||||
hostError: Platform.ErrorCode; opened: BOOLEAN;
|
||||
BEGIN
|
||||
oldout := Streams.stdout;
|
||||
opened := FALSE;
|
||||
IF (Streams.stdin IS UnixFiles.Stream) &
|
||||
(Streams.stdout IS UnixFiles.Stream) THEN
|
||||
IF Platform.IsConsole(Streams.stdin(UnixFiles.Stream).file) &
|
||||
Platform.IsConsole(Streams.stdout(UnixFiles.Stream).file) &
|
||||
Platform.SameTerminal(Streams.stdin(UnixFiles.Stream).file,
|
||||
Streams.stdout(UnixFiles.Stream).file) THEN
|
||||
opened := Open(console, Streams.stdin, Streams.stdout, "",
|
||||
RelatedEvents.null)
|
||||
END
|
||||
END;
|
||||
|
||||
IF ~opened THEN
|
||||
COPY("/dev/tty", name);
|
||||
REPEAT
|
||||
hostError := Platform.OpenFile(name, Platform.ReadWrite,
|
||||
Platform.OpenExisting, handle)
|
||||
UNTIL (hostError = 0) OR ~Platform.Interrupted(hostError);
|
||||
IF hostError = 0 THEN
|
||||
IF UnixFiles.OpenFd(file, handle, UnixFiles.rdwr, Streams.nobuf,
|
||||
RelatedEvents.null) THEN
|
||||
IF Open(console, file, file, "", RelatedEvents.null) THEN
|
||||
console(Stream).owned := file;
|
||||
opened := TRUE
|
||||
ELSE
|
||||
IF ~Streams.Close(file) THEN END
|
||||
END
|
||||
ELSE
|
||||
IF Platform.Close(handle) # 0 THEN END
|
||||
END
|
||||
END
|
||||
END;
|
||||
|
||||
IF opened THEN
|
||||
Terminals.console := console;
|
||||
IF oldout IS UnixFiles.Stream THEN
|
||||
IF Platform.SameTerminal(oldout(UnixFiles.Stream).file,
|
||||
console(Stream).outputHandle) THEN
|
||||
Streams.stdout := console
|
||||
END
|
||||
END
|
||||
END
|
||||
END OpenConsole;
|
||||
|
||||
BEGIN
|
||||
Services.CreateType(streamType, "UnixTerminals.Stream", "Terminals.Stream");
|
||||
InitInterfaces;
|
||||
OpenConsole
|
||||
END ulmUnixTerminals.
|
||||
|
|
@ -6,11 +6,19 @@ CONST
|
|||
StdOut- = 1;
|
||||
StdErr- = 2;
|
||||
|
||||
ReadOnly* = 0;
|
||||
WriteOnly* = 1;
|
||||
ReadWrite* = 2;
|
||||
OpenExisting* = 0;
|
||||
CreateAlways* = 1;
|
||||
OpenAlways* = 2;
|
||||
|
||||
TYPE
|
||||
SignalHandler = PROCEDURE(signal: SYSTEM.INT32);
|
||||
|
||||
ErrorCode* = INTEGER;
|
||||
FileHandle* = LONGINT;
|
||||
TerminalState* = SYSTEM.ADDRESS;
|
||||
|
||||
FileIdentity* = RECORD
|
||||
volume: LONGINT; (* dev on Unix filesystems, volume serial number on NTFS *)
|
||||
|
|
@ -42,6 +50,8 @@ PROCEDURE -Aincludesysstat '#include <sys/stat.h>';
|
|||
PROCEDURE -Aincludefcntl '#include <fcntl.h>';
|
||||
PROCEDURE -Aincludeerrno '#include <errno.h>';
|
||||
PROCEDURE -Aincludeutime '#include <utime.h>';
|
||||
PROCEDURE -Aincludetermios '#include <termios.h>';
|
||||
PROCEDURE -Aincludesysioctl '#include <sys/ioctl.h>';
|
||||
PROCEDURE -Astdlib '#include <stdlib.h>';
|
||||
PROCEDURE -Astdio '#include <stdio.h>';
|
||||
PROCEDURE -Aerrno '#include <errno.h>';
|
||||
|
|
@ -229,6 +239,8 @@ PROCEDURE Error*(): ErrorCode; BEGIN RETURN err() END Error;
|
|||
PROCEDURE -openrw (n: ARRAY OF CHAR): INTEGER "open((char*)n, O_RDWR)";
|
||||
PROCEDURE -openro (n: ARRAY OF CHAR): INTEGER "open((char*)n, O_RDONLY)";
|
||||
PROCEDURE -opennew(n: ARRAY OF CHAR): INTEGER "open((char*)n, O_CREAT | O_TRUNC | O_RDWR, 0664)";
|
||||
PROCEDURE -openfile(n: ARRAY OF CHAR; access, creation: INTEGER): LONGINT
|
||||
"open((char*)n, (access == 0 ? O_RDONLY : access == 1 ? O_WRONLY : O_RDWR) | (creation == 1 ? O_CREAT | O_TRUNC : creation == 2 ? O_CREAT : 0), 0666)";
|
||||
|
||||
(* File APIs *)
|
||||
|
||||
|
|
@ -253,6 +265,14 @@ BEGIN
|
|||
IF (fd < 0) THEN RETURN err() ELSE h := fd; RETURN 0 END;
|
||||
END New;
|
||||
|
||||
PROCEDURE OpenFile*(VAR n: ARRAY OF CHAR; access, creation: INTEGER;
|
||||
VAR h: FileHandle): ErrorCode;
|
||||
VAR fd: LONGINT;
|
||||
BEGIN
|
||||
fd := openfile(n, access, creation);
|
||||
IF fd < 0 THEN RETURN err() ELSE h := fd; RETURN 0 END
|
||||
END OpenFile;
|
||||
|
||||
|
||||
|
||||
PROCEDURE -closefile(fd: LONGINT): INTEGER "close(fd)";
|
||||
|
|
@ -295,6 +315,86 @@ PROCEDURE -isatty(fd: LONGINT): INTEGER "isatty(fd)";
|
|||
PROCEDURE IsConsole*(h: FileHandle): BOOLEAN;
|
||||
BEGIN RETURN isatty(h) # 0 END IsConsole;
|
||||
|
||||
PROCEDURE -captureTerminalState(h: FileHandle;
|
||||
VAR state: TerminalState): INTEGER
|
||||
"({ struct termios *p = malloc(sizeof(*p)); int rc = 0; if (p == 0) { errno = ENOMEM; rc = -1; } else if (tcgetattr(h, p) < 0) { free(p); rc = -1; } else *state = (ADDRESS)p; rc; })";
|
||||
|
||||
PROCEDURE CaptureTerminalState*(h: FileHandle;
|
||||
VAR state: TerminalState): ErrorCode;
|
||||
BEGIN
|
||||
state := 0;
|
||||
IF captureTerminalState(h, state) < 0 THEN RETURN err() ELSE RETURN 0 END
|
||||
END CaptureTerminalState;
|
||||
|
||||
PROCEDURE -restoreTerminalState(h: FileHandle; state: TerminalState): INTEGER
|
||||
"tcsetattr(h, TCSANOW, (struct termios*)(ADDRESS)state)";
|
||||
|
||||
PROCEDURE RestoreTerminalState*(h: FileHandle;
|
||||
state: TerminalState): ErrorCode;
|
||||
BEGIN
|
||||
IF restoreTerminalState(h, state) < 0 THEN RETURN err() ELSE RETURN 0 END
|
||||
END RestoreTerminalState;
|
||||
|
||||
PROCEDURE -releaseTerminalState(state: TerminalState)
|
||||
"free((void*)(ADDRESS)state)";
|
||||
|
||||
PROCEDURE ReleaseTerminalState*(VAR state: TerminalState);
|
||||
BEGIN
|
||||
IF state # 0 THEN releaseTerminalState(state); state := 0 END
|
||||
END ReleaseTerminalState;
|
||||
|
||||
PROCEDURE -setTerminalEcho(h: FileHandle; enabled: INTEGER): INTEGER
|
||||
"({ struct termios t; int rc = tcgetattr(h, &t); if (rc == 0) { if (enabled) t.c_lflag |= ECHO; else t.c_lflag &= ~ECHO; rc = tcsetattr(h, TCSANOW, &t); } rc; })";
|
||||
|
||||
PROCEDURE SetTerminalEcho*(h: FileHandle; enabled: BOOLEAN): ErrorCode;
|
||||
VAR value: INTEGER;
|
||||
BEGIN
|
||||
IF enabled THEN value := 1 ELSE value := 0 END;
|
||||
IF setTerminalEcho(h, value) < 0 THEN RETURN err() ELSE RETURN 0 END
|
||||
END SetTerminalEcho;
|
||||
|
||||
PROCEDURE -setTerminalInputRaw(h: FileHandle): INTEGER
|
||||
"({ struct termios t; int rc = tcgetattr(h, &t); if (rc == 0) { t.c_iflag &= ~ICRNL; t.c_lflag &= ~ICANON; t.c_cc[VMIN] = 1; t.c_cc[VTIME] = 0; rc = tcsetattr(h, TCSANOW, &t); } rc; })";
|
||||
|
||||
PROCEDURE -setTerminalInputCooked(h: FileHandle): INTEGER
|
||||
"({ struct termios t; int rc = tcgetattr(h, &t); if (rc == 0) { t.c_iflag |= ICRNL; t.c_lflag |= ICANON; t.c_cc[VEOF] = 4; t.c_cc[VEOL] = '\n'; rc = tcsetattr(h, TCSANOW, &t); } rc; })";
|
||||
|
||||
PROCEDURE SetTerminalInputMode*(h: FileHandle; raw: BOOLEAN): ErrorCode;
|
||||
VAR result: INTEGER;
|
||||
BEGIN
|
||||
IF raw THEN result := setTerminalInputRaw(h)
|
||||
ELSE result := setTerminalInputCooked(h)
|
||||
END;
|
||||
IF result < 0 THEN RETURN err() ELSE RETURN 0 END
|
||||
END SetTerminalInputMode;
|
||||
|
||||
PROCEDURE -setTerminalOutputMode(h: FileHandle; raw: INTEGER): INTEGER
|
||||
"({ struct termios t; int rc = tcgetattr(h, &t); if (rc == 0) { if (raw) t.c_oflag &= ~(OPOST | OCRNL | ONLCR); else t.c_oflag |= OPOST | OCRNL | ONLCR; rc = tcsetattr(h, TCSANOW, &t); } rc; })";
|
||||
|
||||
PROCEDURE SetTerminalOutputMode*(h: FileHandle; raw: BOOLEAN): ErrorCode;
|
||||
VAR value: INTEGER;
|
||||
BEGIN
|
||||
IF raw THEN value := 1 ELSE value := 0 END;
|
||||
IF setTerminalOutputMode(h, value) < 0 THEN RETURN err() ELSE RETURN 0 END
|
||||
END SetTerminalOutputMode;
|
||||
|
||||
PROCEDURE -getTerminalSize(h: FileHandle; VAR lines, columns: INTEGER): INTEGER
|
||||
"({ struct winsize ws; int rc = ioctl(h, TIOCGWINSZ, &ws); if (rc == 0) { *lines = ws.ws_row; *columns = ws.ws_col; } rc; })";
|
||||
|
||||
PROCEDURE GetTerminalSize*(h: FileHandle;
|
||||
VAR lines, columns: INTEGER): ErrorCode;
|
||||
BEGIN
|
||||
IF getTerminalSize(h, lines, columns) < 0 THEN RETURN err() ELSE RETURN 0 END
|
||||
END GetTerminalSize;
|
||||
|
||||
PROCEDURE -sameTerminal(first, second: FileHandle): INTEGER
|
||||
"({ struct stat a, b; int same = 0; if (fstat(first, &a) == 0 && fstat(second, &b) == 0) same = S_ISCHR(a.st_mode) && S_ISCHR(b.st_mode) && a.st_rdev == b.st_rdev; same; })";
|
||||
|
||||
PROCEDURE SameTerminal*(first, second: FileHandle): BOOLEAN;
|
||||
BEGIN
|
||||
RETURN sameTerminal(first, second) # 0
|
||||
END SameTerminal;
|
||||
|
||||
|
||||
|
||||
PROCEDURE -fstat(fd: LONGINT): INTEGER "fstat(fd, &s)";
|
||||
|
|
@ -375,11 +475,22 @@ END ReadBuf;
|
|||
PROCEDURE -writefile(fd: LONGINT; p: SYSTEM.ADDRESS; l: LONGINT): SYSTEM.ADDRESS
|
||||
"write(fd, (void*)(ADDRESS)(p), l)";
|
||||
|
||||
PROCEDURE Write*(h: FileHandle; p: SYSTEM.ADDRESS; l: LONGINT): ErrorCode;
|
||||
PROCEDURE -writecount(value: SYSTEM.ADDRESS): LONGINT "(LONGINT)value";
|
||||
|
||||
PROCEDURE WriteCount*(h: FileHandle; p: SYSTEM.ADDRESS; l: LONGINT;
|
||||
VAR n: LONGINT): ErrorCode;
|
||||
VAR written: SYSTEM.ADDRESS;
|
||||
BEGIN
|
||||
written := writefile(h, p, l);
|
||||
IF written < 0 THEN RETURN err() ELSE RETURN 0 END
|
||||
IF written < 0 THEN n := 0; RETURN err() END;
|
||||
n := writecount(written);
|
||||
RETURN 0
|
||||
END WriteCount;
|
||||
|
||||
PROCEDURE Write*(h: FileHandle; p: SYSTEM.ADDRESS; l: LONGINT): ErrorCode;
|
||||
VAR written: LONGINT;
|
||||
BEGIN
|
||||
RETURN WriteCount(h, p, l, written)
|
||||
END Write;
|
||||
|
||||
|
||||
|
|
@ -393,7 +504,7 @@ END Sync;
|
|||
|
||||
|
||||
|
||||
PROCEDURE -lseek(fd: LONGINT; o: LONGINT; w: INTEGER): INTEGER "lseek(fd, o, w)";
|
||||
PROCEDURE -lseek(fd: LONGINT; o: LONGINT; w: INTEGER): LONGINT "lseek(fd, o, w)";
|
||||
PROCEDURE -seekset(): INTEGER "SEEK_SET";
|
||||
PROCEDURE -seekcur(): INTEGER "SEEK_CUR";
|
||||
PROCEDURE -seekend(): INTEGER "SEEK_END";
|
||||
|
|
@ -403,6 +514,12 @@ BEGIN
|
|||
IF lseek(h, offset, whence) < 0 THEN RETURN err() ELSE RETURN 0 END
|
||||
END Seek;
|
||||
|
||||
PROCEDURE Tell*(h: FileHandle; VAR offset: LONGINT): ErrorCode;
|
||||
BEGIN
|
||||
offset := lseek(h, 0, seekcur());
|
||||
IF offset < 0 THEN offset := 0; RETURN err() ELSE RETURN 0 END
|
||||
END Tell;
|
||||
|
||||
|
||||
|
||||
PROCEDURE -ftruncate(fd: LONGINT; l: LONGINT): INTEGER "ftruncate(fd, l)";
|
||||
|
|
|
|||
|
|
@ -412,10 +412,18 @@ END ReadBuf;
|
|||
PROCEDURE -writefile(fd: FileHandle; p: SYSTEM.ADDRESS; l: LONGINT; VAR n: SYSTEM.INT32): INTEGER
|
||||
"(INTEGER)WriteFile((HANDLE)fd, (void*)(p), (DWORD)l, (DWORD*)n, 0)";
|
||||
|
||||
PROCEDURE Write*(h: FileHandle; p: SYSTEM.ADDRESS; l: LONGINT): ErrorCode;
|
||||
VAR n: SYSTEM.INT32;
|
||||
PROCEDURE WriteCount*(h: FileHandle; p: SYSTEM.ADDRESS; l: LONGINT;
|
||||
VAR n: LONGINT): ErrorCode;
|
||||
VAR result: INTEGER; written: SYSTEM.INT32;
|
||||
BEGIN
|
||||
IF writefile(h, p, l, n) = 0 THEN RETURN err() ELSE RETURN 0 END
|
||||
result := writefile(h, p, l, written);
|
||||
IF result = 0 THEN n := 0; RETURN err() ELSE n := written; RETURN 0 END
|
||||
END WriteCount;
|
||||
|
||||
PROCEDURE Write*(h: FileHandle; p: SYSTEM.ADDRESS; l: LONGINT): ErrorCode;
|
||||
VAR n: LONGINT;
|
||||
BEGIN
|
||||
RETURN WriteCount(h, p, l, n)
|
||||
END Write;
|
||||
|
||||
|
||||
|
|
@ -444,12 +452,18 @@ BEGIN
|
|||
IF rc = 0 THEN RETURN err() ELSE RETURN 0 END
|
||||
END Seek;
|
||||
|
||||
|
||||
|
||||
PROCEDURE -setEndOfFile(h: FileHandle): INTEGER "(INTEGER)SetEndOfFile((HANDLE)h)";
|
||||
PROCEDURE -getFilePos(h: FileHandle; VAR r: LONGINT; VAR rc: INTEGER)
|
||||
"LARGE_INTEGER liz = {0}; *rc = (INTEGER)SetFilePointerEx((HANDLE)h, liz, &li, FILE_CURRENT); *r = (LONGINT)li.QuadPart";
|
||||
|
||||
PROCEDURE Tell*(h: FileHandle; VAR offset: LONGINT): ErrorCode;
|
||||
VAR rc: INTEGER;
|
||||
BEGIN
|
||||
largeInteger;
|
||||
getFilePos(h, offset, rc);
|
||||
IF rc = 0 THEN offset := 0; RETURN err() ELSE RETURN 0 END
|
||||
END Tell;
|
||||
|
||||
PROCEDURE Truncate*(h: FileHandle; limit: LONGINT): ErrorCode;
|
||||
VAR rc: INTEGER; oldpos: LONGINT;
|
||||
BEGIN
|
||||
|
|
@ -516,7 +530,7 @@ END EnableVT100;
|
|||
|
||||
PROCEDURE IsConsole*(h: FileHandle): BOOLEAN;
|
||||
VAR mode: SYSTEM.INT32;
|
||||
BEGIN RETURN GetConsoleMode(StdOut, mode)
|
||||
BEGIN RETURN GetConsoleMode(h, mode)
|
||||
END IsConsole;
|
||||
|
||||
|
||||
|
|
|
|||
27
src/test/ulm/readme.md
Normal file
27
src/test/ulm/readme.md
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
# ULM Unix smoke tests
|
||||
|
||||
Build the tests with an installed VOC compiler:
|
||||
|
||||
```sh
|
||||
voc -M testUnixFiles.Mod
|
||||
voc -M testUnixTerminals.Mod
|
||||
voc -M testUnixTerminalExit.Mod
|
||||
```
|
||||
|
||||
Run the file and explicit terminal-close tests normally from this directory:
|
||||
|
||||
```sh
|
||||
./testUnixFiles
|
||||
./testUnixTerminals
|
||||
```
|
||||
|
||||
The terminal tests require standard input and output to refer to the same
|
||||
terminal. To verify restoration during program shutdown:
|
||||
|
||||
```sh
|
||||
before=$(stty -g)
|
||||
./testUnixTerminalExit
|
||||
test "$before" = "$(stty -g)"
|
||||
```
|
||||
|
||||
`testUnixFiles` leaves `ulmUnixFiles.test` in the current directory.
|
||||
45
src/test/ulm/testUnixFiles.Mod
Normal file
45
src/test/ulm/testUnixFiles.Mod
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
MODULE testUnixFiles;
|
||||
|
||||
IMPORT Streams := ulmStreams, UnixFiles := ulmUnixFiles, Write := ulmWrite,
|
||||
SYSTEM;
|
||||
|
||||
VAR
|
||||
stream: Streams.Stream;
|
||||
filename: ARRAY 64 OF CHAR;
|
||||
written, received: ARRAY 5 OF CHAR;
|
||||
byte: Streams.Byte;
|
||||
position: Streams.Count;
|
||||
|
||||
BEGIN
|
||||
ASSERT(Streams.stdin IS UnixFiles.Stream);
|
||||
ASSERT(Streams.stdout IS UnixFiles.Stream);
|
||||
ASSERT(Streams.stderr IS UnixFiles.Stream);
|
||||
|
||||
COPY("ulmUnixFiles.test", filename);
|
||||
COPY("test", written);
|
||||
ASSERT(UnixFiles.Open(stream, filename, UnixFiles.rdwr + UnixFiles.create,
|
||||
Streams.onebuf, NIL));
|
||||
ASSERT(Streams.WritePart(stream, written, 0, 4));
|
||||
Streams.GetPos(stream, position);
|
||||
ASSERT(position = 4);
|
||||
ASSERT(Streams.Close(stream));
|
||||
|
||||
COPY("ulmUnixFiles.test", filename);
|
||||
ASSERT(UnixFiles.Open(stream, filename,
|
||||
UnixFiles.read + UnixFiles.condcreate,
|
||||
Streams.onebuf, NIL));
|
||||
ASSERT(Streams.ReadPart(stream, received, 0, 4));
|
||||
ASSERT((received[0] = "t") & (received[1] = "e") &
|
||||
(received[2] = "s") & (received[3] = "t"));
|
||||
ASSERT(Streams.Close(stream));
|
||||
|
||||
COPY("ulmUnixFiles.test", filename);
|
||||
ASSERT(UnixFiles.Open(stream, filename, UnixFiles.read,
|
||||
Streams.nobuf, NIL));
|
||||
ASSERT(Streams.ReadByte(stream, byte));
|
||||
ASSERT(SYSTEM.VAL(CHAR, byte) = "t");
|
||||
ASSERT(Streams.Close(stream));
|
||||
|
||||
Write.String("ulmUnixFiles ok");
|
||||
Write.Ln
|
||||
END testUnixFiles.
|
||||
9
src/test/ulm/testUnixTerminalExit.Mod
Normal file
9
src/test/ulm/testUnixTerminalExit.Mod
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
MODULE testUnixTerminalExit;
|
||||
|
||||
IMPORT Terminals := ulmTerminals, UnixTerminals := ulmUnixTerminals;
|
||||
|
||||
BEGIN
|
||||
ASSERT(Terminals.console # NIL);
|
||||
Terminals.Echo(Terminals.console, Terminals.off);
|
||||
Terminals.SetTermMode(Terminals.console, Terminals.raw)
|
||||
END testUnixTerminalExit.
|
||||
26
src/test/ulm/testUnixTerminals.Mod
Normal file
26
src/test/ulm/testUnixTerminals.Mod
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
MODULE testUnixTerminals;
|
||||
|
||||
IMPORT Streams := ulmStreams, Terminals := ulmTerminals,
|
||||
UnixTerminals := ulmUnixTerminals, Write := ulmWrite;
|
||||
|
||||
VAR status: Terminals.Status; caps: Terminals.CapabilitySet;
|
||||
|
||||
BEGIN
|
||||
ASSERT(Terminals.console # NIL);
|
||||
ASSERT(Streams.stdout = Terminals.console);
|
||||
caps := Terminals.Capabilities(Terminals.console);
|
||||
ASSERT(Terminals.setEcho IN caps);
|
||||
ASSERT(Terminals.setTermMode IN caps);
|
||||
|
||||
Terminals.GetStatus(Terminals.console, status);
|
||||
ASSERT((status.lines > 0) & (status.columns > 0));
|
||||
|
||||
Terminals.Echo(Terminals.console, Terminals.off);
|
||||
Terminals.Echo(Terminals.console, Terminals.on);
|
||||
Terminals.SetTermMode(Terminals.console, Terminals.raw);
|
||||
Terminals.SetTermMode(Terminals.console, Terminals.cooked);
|
||||
|
||||
Write.String("ulmUnixTerminals ok");
|
||||
Write.Ln;
|
||||
ASSERT(Streams.Close(Terminals.console))
|
||||
END testUnixTerminals.
|
||||
|
|
@ -316,6 +316,7 @@ ulm:
|
|||
cd $(BUILDDIR)/$(MODEL); "$(ROOTDIR)/$(OBECOMP)" -Fs -O$(MODEL) ../../../src/library/ulm/ulmRelatedEvents.Mod
|
||||
cd $(BUILDDIR)/$(MODEL); "$(ROOTDIR)/$(OBECOMP)" -Fs -O$(MODEL) ../../../src/library/ulm/ulmStreamsHost.Mod
|
||||
cd $(BUILDDIR)/$(MODEL); "$(ROOTDIR)/$(OBECOMP)" -Fs -O$(MODEL) ../../../src/library/ulm/ulmStreams.Mod
|
||||
cd $(BUILDDIR)/$(MODEL); "$(ROOTDIR)/$(OBECOMP)" -Fs -O$(MODEL) ../../../src/library/ulm/ulmTerminals.Mod
|
||||
cd $(BUILDDIR)/$(MODEL); "$(ROOTDIR)/$(OBECOMP)" -Fs -O$(MODEL) ../../../src/library/ulm/ulmStrings.Mod
|
||||
cd $(BUILDDIR)/$(MODEL); "$(ROOTDIR)/$(OBECOMP)" -Fs -O$(MODEL) ../../../src/library/ulm/ulmSysTypes.Mod
|
||||
cd $(BUILDDIR)/$(MODEL); "$(ROOTDIR)/$(OBECOMP)" -Fs -O$(MODEL) ../../../src/library/ulm/ulmTexts.Mod
|
||||
|
|
@ -337,6 +338,8 @@ ulm:
|
|||
cd $(BUILDDIR)/$(MODEL); "$(ROOTDIR)/$(OBECOMP)" -Fs -O$(MODEL) ../../../src/library/ulm/ulmConstStrings.Mod
|
||||
cd $(BUILDDIR)/$(MODEL); "$(ROOTDIR)/$(OBECOMP)" -Fs -O$(MODEL) ../../../src/library/ulm/ulmPlotters.Mod
|
||||
cd $(BUILDDIR)/$(MODEL); "$(ROOTDIR)/$(OBECOMP)" -Fs -O$(MODEL) ../../../src/library/ulm/ulmSysIO.Mod
|
||||
if [ "$(PLATFORM)" = "unix" ]; then cd $(BUILDDIR)/$(MODEL) && "$(ROOTDIR)/$(OBECOMP)" -Fs -O$(MODEL) ../../../src/library/ulm/ulmUnixFiles.Mod; fi
|
||||
if [ "$(PLATFORM)" = "unix" ]; then cd $(BUILDDIR)/$(MODEL) && "$(ROOTDIR)/$(OBECOMP)" -Fs -O$(MODEL) ../../../src/library/ulm/ulmUnixTerminals.Mod; fi
|
||||
cd $(BUILDDIR)/$(MODEL); "$(ROOTDIR)/$(OBECOMP)" -Fs -O$(MODEL) ../../../src/library/ulm/ulmLoader.Mod
|
||||
cd $(BUILDDIR)/$(MODEL); "$(ROOTDIR)/$(OBECOMP)" -Fs -O$(MODEL) ../../../src/library/ulm/ulmNetIO.Mod
|
||||
cd $(BUILDDIR)/$(MODEL); "$(ROOTDIR)/$(OBECOMP)" -Fs -O$(MODEL) ../../../src/library/ulm/ulmPersistentObjects.Mod
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue