-- 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[];
}.