(* BSD 2-Clause License Copyright (c) 2018, 2019, Anton Krotov All rights reserved. *) MODULE TEXTDRV; IMPORT FILES, C := COLLECTIONS; CONST CR = 0DX; LF = 0AX; CHUNK = 1024 * 256; TYPE TEXT* = POINTER TO RECORD (C.ITEM) chunk: ARRAY CHUNK OF BYTE; pos, size: INTEGER; file: FILES.FILE; utf8: BOOLEAN; CR: BOOLEAN; line*, col*: INTEGER; eof*: BOOLEAN; eol*: BOOLEAN; peak*: CHAR END; VAR texts: C.COLLECTION; PROCEDURE load (text: TEXT); BEGIN IF ~text.eof THEN text.size := FILES.read(text.file, text.chunk, LEN(text.chunk)); text.pos := 0; IF text.size = 0 THEN text.eof := TRUE; text.chunk[0] := 0 END; text.peak := CHR(text.chunk[0]) END END load; PROCEDURE next* (text: TEXT); VAR c: CHAR; BEGIN IF text.pos < text.size - 1 THEN INC(text.pos); text.peak := CHR(text.chunk[text.pos]) ELSE load(text) END; IF ~text.eof THEN c := text.peak; IF c = CR THEN INC(text.line); text.col := 0; text.eol := TRUE; text.CR := TRUE ELSIF c = LF THEN IF ~text.CR THEN INC(text.line); text.col := 0; text.eol := TRUE ELSE text.eol := FALSE END; text.CR := FALSE ELSE text.eol := FALSE; IF text.utf8 THEN IF (c < 80X) OR (c > 0BFX) THEN INC(text.col) END ELSE INC(text.col) END; text.CR := FALSE END END END next; PROCEDURE init (text: TEXT); BEGIN IF (text.pos = 0) & (text.size >= 3) THEN IF (text.chunk[0] = 0EFH) & (text.chunk[1] = 0BBH) & (text.chunk[2] = 0BFH) THEN text.pos := 3; text.utf8 := TRUE END END; IF text.size = 0 THEN text.chunk[0] := 0; text.size := 1; text.eof := FALSE END; text.line := 1; text.col := 1; text.peak := CHR(text.chunk[text.pos]) END init; PROCEDURE close* (VAR text: TEXT); BEGIN IF text # NIL THEN IF text.file # NIL THEN FILES.close(text.file) END; C.push(texts, text); text := NIL END END close; PROCEDURE open* (name: ARRAY OF CHAR): TEXT; VAR text: TEXT; citem: C.ITEM; BEGIN citem := C.pop(texts); IF citem = NIL THEN NEW(text) ELSE text := citem(TEXT) END; IF text # NIL THEN text.chunk[0] := 0; text.pos := 0; text.size := 0; text.utf8 := FALSE; text.CR := FALSE; text.line := 1; text.col := 1; text.eof := FALSE; text.eol := FALSE; text.peak := 0X; text.file := FILES.open(name); IF text.file # NIL THEN load(text); init(text) ELSE close(text) END END RETURN text END open; BEGIN texts := C.create() END TEXTDRV.