<> <> <> <> <> DIRECTORY FS: TYPE USING [GetInfo, Error, nullOpenFile, Open, OpenFile, Read], IO: TYPE USING [GetInt, int, PutFR, RIS], PL: TYPE USING[RErr], Process: TYPE USING [Detach], PString: TYPE USING [Stream], Rope: TYPE USING [Fetch, Flatten, Length, MakeRope, MapType, ROPE, Substr], SafeStorage: TYPE USING [EstablishFinalization, FinalizationQueue, FQNext, NewFQ], VM: TYPE USING [AddressForPageNumber, Allocate, Free, Interval, MakeReadOnly]; PStringImpl: CEDAR PROGRAM IMPORTS FS, IO, PL, Process, Rope, SafeStorage, VM EXPORTS PString = { ROPE: TYPE = Rope.ROPE; Stream: TYPE = PString.Stream; FileHandle: TYPE = REF FileRecord; FileRecord: PRIVATE TYPE = RECORD[ len: INT, s: VM.Interval, p: LONG POINTER TO PACKED ARRAY [0..0) OF CHAR]; fq: SafeStorage.FinalizationQueue; BoundsError: ERROR = CODE; minCoerceSize: INT = 100; OpenFiles: LIST OF FileHandle _ LIST[NIL]; MTSt: ROPE = ""; CopyStream: PUBLIC PROC[n: Stream] RETURNS[Stream] = {RETURN[n]}; ConvertStream: PUBLIC PROC[s: Stream] RETURNS [ans: ROPE] = { IF s.node=NIL THEN RETURN [MTSt]; RETURN[s.node.Substr[s.place]]}; Empty: PUBLIC PROC[n: ROPE] RETURNS [BOOL] = {RETURN[n.Length=0]}; EmptyS: PUBLIC PROC[n: Stream] RETURNS [BOOL] = {RETURN[n.node=NIL]}; MyFetch: PROC[data: REF, index: INT] RETURNS[CHAR] = TRUSTED { fh: FileHandle _ NARROW[data]; IF index >= fh.len THEN ERROR BoundsError; RETURN[(fh.p+(index/2))[index MOD 2]]}; Finalizer: PROC = { DO fh: FileHandle = NARROW -- LOOPHOLE -- [SafeStorage.FQNext[fq]]; Close[fh]; ENDLOOP}; Item: PUBLIC PROC[s: Stream] RETURNS[c: CHAR, ns: Stream] = { IF s.node=NIL THEN RETURN[0C, s]; ns _ [place: s.place+1, node: s.node]; c _ s.node.Fetch[s.place]; IF ns.place=s.node.Length THEN ns.node _ NIL; }; MakeInt: PUBLIC PROC[n: ROPE] RETURNS[cnt: INT] = { RETURN[IO.RIS[n].GetInt[]]}; MakeNUM: PUBLIC PROC[i: INT] RETURNS [ROPE] = { RETURN[IO.PutFR[,IO.int[i]]]}; MyMap: Rope.MapType = TRUSTED { fh: FileHandle _ NARROW[base]; IF start>fh.len THEN ERROR BoundsError; FOR i: INT IN [start..MIN[fh.len, start+len]) DO IF action[(fh.p+(i/2))[i MOD 2]] THEN RETURN[TRUE]; ENDLOOP; RETURN[FALSE]}; NewStream: PUBLIC PROC[n: ROPE] RETURNS[s: Stream] = { IF Empty[n] THEN n _ NIL; RETURN[[place: 0, node: n]] }; Open: PROC [name: ROPE] RETURNS [fh: FileHandle, len: INT] = { file: FS.OpenFile _ FS.nullOpenFile; file _ FS.Open[name: name, lock: $read ! FS.Error--[error: FS.ErrorDesc]-- => IF error.code=$unknownFile THEN CONTINUE]; IF file = FS.nullOpenFile THEN RETURN[NIL,0]; fh _ NEW[FileRecord]; fh.s _ VM.Allocate[file.GetInfo[].pages]; TRUSTED { fh.p _ VM.AddressForPageNumber[fh.s.page]; file.Read[from: 0, nPages: fh.s.count, to: fh.p]}; (fh.s).MakeReadOnly[]; len _ file.GetInfo[].bytes; OpenFiles _ CONS[fh, OpenFiles]; }; Close: PROC [f: FileHandle] = {TRUSTED{(f.s).Free[]}; FOR t: LIST OF FileHandle _ OpenFiles, t.rest UNTIL t=NIL DO IF t.first=f THEN t^ _ t.rest^ ENDLOOP}; StringToFile: PUBLIC PROC[name: ROPE] RETURNS[ans:ROPE] = { fi: FileHandle; len: INT; IF name = NIL THEN PL.RErr["File name must be non-null string"]; IF name.Length >= 100 THEN PL.RErr["File name too long (>100)"]; ans _ NIL; [fi, len] _ Open[name]; IF fi=NIL THEN ans _ NIL ELSE {fi.len _ len; ans _ Rope.MakeRope[fi, len, MyFetch, MyMap]; IF ans.Length < minCoerceSize THEN ans _ ans.Flatten[]}; }; Sub: PUBLIC PROC[n: ROPE, inx: INT] RETURNS[CHAR] = {RETURN[n.Fetch[inx]]}; SubStream: PUBLIC PROC[s:Stream,i: INT] RETURNS [CHAR] = { RETURN[IF s.node = NIL THEN '\000 ELSE Sub[s.node,s.place+i]]; }; SubString: PUBLIC PROC[str: ROPE, ii, jj: INT] RETURNS [ans: ROPE] = {RETURN[str.Substr[ii, jj-ii]]}; SubStringStream: PUBLIC PROC[s: Stream,i,j: INT] RETURNS [ROPE] = { RETURN[IF s.node = NIL THEN MTSt ELSE SubString[s.node,s.place+i, s.place+j]]; }; fq _ SafeStorage.NewFQ[]; SafeStorage.EstablishFinalization[CODE[FileRecord], 1, fq]; TRUSTED{Process.Detach[FORK Finalizer]} }.