-- CSImpl.Mesa, last edit February 13, 1983 7:07 pm -- replacement for SubrImpl in the Cedar world DIRECTORY CS: TYPE USING [FileErrorType, PTimeStamp, Read, ReadWrite, Write], DCSFileTypes: TYPE USING[tLeaderPage], Directory: TYPE USING[CreateFile, Error, ignore, Lookup, UpdateDates], Environment: TYPE USING[wordsPerPage], File: TYPE USING[Capability, Permissions, SetSize], FileStream: TYPE USING[Create], Inline: TYPE USING [DIVMOD, LongDivMod], IO: TYPE USING[card, GetChar, Handle, PFCodeProc, Put, PutChar, PutF, SetPFCodeProc, Signal, string], Rope: TYPE USING[Fetch, Length, Lower, ROPE, Text], SafeStorage: TYPE USING[NewZone], Stream: TYPE USING[Handle]; CSImpl: CEDAR PROGRAM IMPORTS Directory, File, FileStream, Inline, IO, Rope, SafeStorage EXPORTS CS = { -- variables z: PUBLIC ZONE ← NIL; -- signals FileError: PUBLIC ERROR[error: CS.FileErrorType] = CODE; -- startup Init: PUBLIC PROC = {}; -- call to make sure module is started -- rope utilities RopeToString: PUBLIC PROC[to: LONG STRING, from: Rope.ROPE] = TRUSTED { len: CARDINAL ← from.Length[]; IF to.maxlength > len THEN ERROR; FOR i: CARDINAL IN [0 .. len) DO to[i] ← from.Fetch[i]; ENDLOOP; to.length ← len; }; SetPFCodes: PUBLIC PROC[h: IO.Handle] = { h.SetPFCodeProc['a, PrintACode]; h.SetPFCodeProc['v, PrintVCode]; h.SetPFCodeProc['y, PrintYCode]; }; EndsIn: PUBLIC PROC[str: Rope.ROPE, suf: LONG STRING] RETURNS[BOOL] = TRUSTED { strLength: CARDINAL ← str.Length[]; sufLength: CARDINAL ← suf.length; FOR i: CARDINAL IN [0 .. strLength) DO { IF Rope.Lower[str.Fetch[i]] = Rope.Lower[suf[0]] THEN { FOR j: CARDINAL IN [1 ..sufLength) DO IF i+j >= strLength OR Rope.Lower[str.Fetch[i+j]] ~= Rope.Lower[suf[j]] THEN GOTO outer; ENDLOOP; IF i+sufLength < strLength THEN GOTO outer; RETURN[TRUE]; }; EXITS outer => NULL; }; ENDLOOP; RETURN[FALSE]; }; EqualRS: PUBLIC PROC[r: Rope.ROPE, s: LONG STRING] RETURNS[equal: BOOL] = TRUSTED { IF r.Length[] ~= s.length THEN RETURN[FALSE]; FOR i: CARDINAL IN [0 .. s.length) DO IF r.Fetch[i] ~= s[i] THEN RETURN[FALSE]; ENDLOOP; RETURN[TRUE]; }; EquivalentRS: PUBLIC PROC[r: Rope.ROPE, s: LONG STRING] RETURNS[equivalent: BOOL] = TRUSTED { IF r.Length[] ~= s.length THEN RETURN[FALSE]; FOR i: CARDINAL IN [0 .. s.length) DO IF Rope.Lower[r.Fetch[i]] ~= Rope.Lower[s[i]] THEN RETURN[FALSE]; ENDLOOP; RETURN[TRUE]; }; Confirm: PUBLIC PROC[dch: CHAR, in, out: IO.Handle] RETURNS[CHAR] = { ch: CHAR; DO { ENABLE IO.Signal => TRUSTED { IF ec = Rubout THEN LOOP}; out.Put[IO.string["? "L]]; ch ← in.GetChar[]; IF ch = '\n THEN ch ← dch; ch ← Rope.Lower[ch]; RETURN[ch]; }; ENDLOOP; }; -- npages should not include the leader page; I'll add +1 NewFile: PUBLIC PROC [name: Rope.Text, access: File.Permissions, npages: CARDINAL] RETURNS [cap: File.Capability] = TRUSTED { old: BOOL ← FALSE; IF access ~= CS.Read THEN cap ← Directory.CreateFile[LOOPHOLE[name], DCSFileTypes.tLeaderPage, npages + 1 ! Directory.Error => { IF type = fileAlreadyExists THEN old ← TRUE ELSE ERROR FileError[notFound]; CONTINUE }] ELSE old ← TRUE; IF old THEN cap ← Directory.Lookup[fileName: LOOPHOLE[name], permissions: Directory.ignore ! Directory.Error => ERROR FileError[notFound]]; cap ← Directory.UpdateDates[cap, access]; IF old AND npages > 0 AND (access = CS.Write OR access = CS.ReadWrite) THEN File.SetSize[cap, npages + 1]; }; NewStream: PUBLIC PROC [name: Rope.Text, access: File.Permissions] RETURNS [Stream.Handle] = TRUSTED { RETURN[FileStream.Create[NewFile[name, access, 0]]]; }; PrintACode: IO.PFCodeProc = TRUSTED { WITH v: val SELECT FROM refAny => { pts: CS.PTimeStamp ← NARROW[LOOPHOLE[v.value, REF ANY]]; hex: PACKED ARRAY [0 .. 12) OF [0 .. 16) ← LOOPHOLE[pts↑]; stream.PutChar['$]; FOR i: CARDINAL IN [0 .. 12) DO stream.PutChar['a + hex[i]]; ENDLOOP; }; ENDCASE => ERROR; }; PrintVCode: IO.PFCodeProc = TRUSTED { WITH v: val SELECT FROM refAny => { pts: CS.PTimeStamp ← NARROW[LOOPHOLE[v.value, REF ANY]]; stream.PutF["(%g#,%g#,%t)", IO.card[pts.net], IO.card[pts.host], IO.card[pts.time]]; }; ENDCASE => ERROR; }; PrintYCode: IO.PFCodeProc = TRUSTED { WITH v: val SELECT FROM cardinal => { time: LONG CARDINAL ← v.value; hr, min, sec: CARDINAL; [min, sec] ← Inline.LongDivMod[time, 60]; [hr, min] ← Inline.DIVMOD[min, 60]; IF hr > 0 THEN stream.PutF["%d:%02d:%02d", IO.card[hr], IO.card[min], IO.card[sec]] ELSE IF min > 0 THEN stream.PutF["%d:%02d", IO.card[min], IO.card[sec]] ELSE stream.PutF["%d", IO.card[sec]]; }; ENDCASE => ERROR; }; -- called when module is loaded OnceInit: PROC = { z ← SafeStorage.NewZone[initialSize: 40 * Environment.wordsPerPage]; }; -- start code OnceInit[]; }.