DIRECTORY ConvertUnsafe: TYPE USING [AppendRope], Directory: TYPE USING [Error, GetProps, Handle, Lookup], File: TYPE USING [Capability, GetSize, nullCapability, read], IO: TYPE USING [Close, CreateInputStreamFromRope, GetInt, int, PutFToRope, ROPE], PL: TYPE USING[PBug, RErr], Process: TYPE USING [Detach], PString: TYPE USING [Stream], Rope: TYPE USING [Fetch, Flatten, Length, MakeRope, MapType, ROPE, Substr], RTTypesBasic: TYPE USING [EstablishFinalization, FinalizationQueue, FQNext, NewFQ], Space: TYPE USING [Create, Delete, Handle, LongPointer, MakeReadOnly, Map, virtualMemory]; PStringImpl: CEDAR PROGRAM IMPORTS ConvertUnsafe, Directory, File, IO, PL, Process, Rope, RTTypesBasic, Space EXPORTS PString SHARES PString = { -- OPEN Rope, IO; Stream: TYPE = PString.Stream; FileHandle: TYPE = REF FileRecord; FileRecord: PRIVATE TYPE = RECORD[len: INT, s: Space.Handle, p: LONG POINTER TO PACKED ARRAY [0..0) OF CHARACTER]; fq: RTTypesBasic.FinalizationQueue; BoundsError: ERROR = CODE; MinCoerceSize: LONG INTEGER = 100; OpenFiles: LIST OF FileHandle _ LIST[NIL]; MTSt: ROPE = ""; Cardinal: PUBLIC PROC[in: LONG INTEGER] RETURNS [CARDINAL] = { IF in<0 OR in > 177777B THEN PL.PBug["integer too big"]; RETURN[in]; }; Close: PROC [f: FileHandle] = {TRUSTED{Space.Delete[f.s]}; FOR t: LIST OF FileHandle _ OpenFiles, t.rest UNTIL t=NIL DO IF t.first=f THEN t^ _ t.rest^ ENDLOOP}; 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[Substr[s.node, s.place]]}; Empty: PUBLIC PROC[n: ROPE] RETURNS [BOOLEAN] = {RETURN[Rope.Length[n]=0]}; EmptyS: PUBLIC PROC[n: Stream] RETURNS [BOOLEAN] = {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 -- [RTTypesBasic.FQNext[fq]]; Close[fh]; ENDLOOP}; Item: PUBLIC PROC[s: Stream] RETURNS[c: CHARACTER, ns: Stream] = { IF s.node=NIL THEN RETURN[0C, s]; ns _ [place: s.place+1, node: s.node]; c _ Fetch[s.node, s.place]; IF ns.place=Length[s.node] THEN ns.node _ NIL; }; MakeInteger: PUBLIC PROC[n: ROPE] RETURNS[cnt: INT] = { RETURN[GetInt[IO.CreateInputStreamFromRope[n]]] }; MakeNUM: PUBLIC PROC[i: INT] RETURNS [ROPE] = {RETURN[PutFToRope[,int[i]]]}; MyMap: 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 [rname: ROPE] RETURNS [fh: FileHandle, len: LONG INTEGER] = TRUSTED{cap: File.Capability _ File.nullCapability; name: STRING = [100]; name.length _ 0; ConvertUnsafe.AppendRope[name, rname]; cap _ Directory.Lookup[fileName: name, permissions: File.read ! Directory.Error--[type]-- => IF type=fileNotFound THEN CONTINUE]; IF cap = File.nullCapability THEN RETURN[NIL,0]; fh _ NEW[FileRecord]; {t: INT = File.GetSize[cap]; fh.s _ Space.Create[Cardinal[t-1], Space.virtualMemory]; fh.p _ Space.LongPointer[fh.s]; Space.Map[fh.s, [file: cap, base: 1]]; Space.MakeReadOnly[fh.s]; [,,,len,] _ Directory.GetProps[cap, name]; OpenFiles _ CONS[fh, OpenFiles]; }}; StringToFile: PUBLIC PROC[name: ROPE] RETURNS[ans:ROPE] = { fi: FileHandle; len: LONG INTEGER; IF name = NIL THEN PL.RErr["File name must be non-null string"]; IF Length[name] >= 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 _ MakeRope[fi, len, MyFetch, MyMap]; IF Length[ans] < MinCoerceSize THEN ans _ Rope.Flatten[ans]}; }; Sub: PUBLIC PROC[n: ROPE, inx: LONG INTEGER] RETURNS[CHARACTER] = {RETURN[Fetch[n, inx]]}; SubStream: PUBLIC PROC[s:Stream,i: LONG INTEGER] RETURNS [CHARACTER] = { RETURN[IF s.node = NIL THEN 0C ELSE Sub[s.node,s.place+i]]; }; SubString: PUBLIC PROC[str: ROPE, ii, jj: LONG INTEGER] RETURNS [ans: ROPE] = {RETURN[Substr[str, ii, jj-ii]]}; SubStringStream: PUBLIC PROC[s: Stream,i,j: LONG INTEGER] RETURNS [ROPE] = { RETURN[IF s.node = NIL THEN MTSt ELSE SubString[s.node,s.place+i, s.place+j]]; }; fq _ RTTypesBasic.NewFQ[]; RTTypesBasic.EstablishFinalization[CODE[FileRecord], 1, fq]; TRUSTED{Process.Detach[FORK Finalizer]} }.