-- CSImpl.mesa
-- last edit by Schmidt, May 6, 1983 1:01 pm
-- last edit by Satterthwaite, April 28, 1983 4:46 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, Value],
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
AbortMyself: PUBLIC SIGNAL ~ CODE;
FileError: PUBLIC ERROR[error: CS.FileErrorType] ~ CODE;
-- startup
Init: PUBLIC PROC ~ {}; -- call to make sure module is started
-- rope utilities
RopeToString: PUBLIC UNSAFE PROC[to: LONG STRING, from: Rope.ROPE] ~ UNCHECKED {
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};
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[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[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]};
-- misc
Confirm: PUBLIC PROC[dch: CHAR, in, out: IO.Handle] RETURNS[CHAR] ~ {
ch: CHAR;
bs: IO.Handle ← IF in.backingStream = NIL THEN in ELSE in.backingStream;
DO {
ENABLE IO.Signal => {IF ec = Rubout THEN LOOP};
out.Put[IO.string["? "L]];
ch ← bs.GetChar;
IF ch = '\n THEN ch ← dch;
RETURN[Rope.Lower[ch]]};
ENDLOOP};
-- npages should not include the leader page; I'll add +1
NewFile: PUBLIC UNSAFE PROC[name: Rope.Text, access: File.Permissions, npages: CARDINAL]
RETURNS[cap: File.Capability] ~ UNCHECKED {
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 UNSAFE PROC[name: Rope.Text, access: File.Permissions]
RETURNS[Stream.Handle] ~ UNCHECKED {
RETURN[FileStream.Create[NewFile[name, access, 0]]]};
-- iostream utilities
SetPFCodes: PUBLIC PROC[h: IO.Handle] ~ {
h.SetPFCodeProc['a, PrintACode];
h.SetPFCodeProc['v, PrintVCode];
h.SetPFCodeProc['y, PrintYCode]};
PrintACode: IO.PFCodeProc ~ {
WITH val SELECT FROM
v: IO.Value.refAny => {
pts: CS.PTimeStamp ~ NARROW[v.value];
hex: PACKED ARRAY [0 .. 12) OF [0 .. 16) ~ LOOPHOLE[pts↑];
stream.PutChar['$];
FOR i: CARDINAL IN [0 .. 12) DO
stream.PutChar[IF hex[i] IN [0 .. 9] THEN ('0 + hex[i]) ELSE ('a + hex[i] - 10)];
ENDLOOP};
ENDCASE => ERROR};
PrintVCode: IO.PFCodeProc ~ {
WITH val SELECT FROM
v: IO.Value.refAny => {
pts: CS.PTimeStamp ~ NARROW[v.value];
stream.PutF["(%g#,%g#,%t)", IO.card[pts.net], IO.card[pts.host], IO.card[pts.time]]};
ENDCASE => ERROR};
PrintYCode: IO.PFCodeProc ~ {
WITH val SELECT FROM
v: IO.Value.cardinal => {
hr, min, sec: CARDINAL;
[min, sec] ← Inline.LongDivMod[v.value, 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[];
}.