<> <> <> DIRECTORY CWF: TYPE USING [SWF1, WF0, WF1, WF2, WFCR], DCSFileTypes: TYPE USING [tLeaderPage], Directory: TYPE USING [CreateFile, Error, GetProps, Handle, ignore, Lookup], Environment: TYPE USING [bytesPerPage, wordsPerPage], File: TYPE USING [Capability, SetSize], FileStream: TYPE USING [Create, SetLeaderPropertiesForCapability, SetLength], IFSFile: TYPE USING [AccessFailure, CantOpen, Close, Completer, Error, FileHandle, FSInstance, GetLength, GetTimes, Initialize, Login, Logout, Open, OpenOptions, Problem, SetCreation, SetLength, StartRead, StartWrite, UnableToLogin], Inline: TYPE USING [LowHalf], LeafSubr: TYPE USING [], LongString: TYPE USING [EquivalentString], Space: TYPE USING [CopyIn, CopyOut, Create, Delete, GetAttributes, Handle, Kill, LongPointer, Map, nullHandle, virtualMemory], Stream: TYPE USING [Delete, Handle], Subr: TYPE USING [AbortMyself, AllocateString, Any, CopyString, FileError, FreeString, GetNameandPassword, LongZone, Prefix, TTYProcs], UserCredentialsUnsafe: TYPE USING[GetUserCredentials]; LeafSubrImpl: MONITOR IMPORTS CWF, Directory, File, FileStream, IFSFile, Inline, LongString, Space, Stream, Subr, UserCredentialsUnsafe EXPORTS LeafSubr = { <> connseq: LONG POINTER TO ConnSeqRecord _ NIL; <> <> MAXCONNS: CARDINAL = 8; <> ConnSeqRecord: TYPE = RECORD[ user2: LONG STRING _ NIL, -- connect name password2: LONG STRING _ NIL, -- connect password size: CARDINAL _ 0, -- # of connections body: SEQUENCE maxsize: CARDINAL OF ConnTable ]; ConnTable: TYPE = RECORD[ fsinstance: IFSFile.FSInstance _ NIL, host: LONG STRING _ NIL ]; Open: PUBLIC PROC [host, sfn: LONG STRING, h: Subr.TTYProcs, openOptions: IFSFile.OpenOptions] RETURNS[fh: IFSFile.FileHandle] = { fsinstance: IFSFile.FSInstance _ Connect[host,h, NIL, NIL, NIL, NIL]; fh _ IFSFile.Open[fsinstance, sfn, openOptions ! IFSFile.CantOpen => SELECT reason FROM ok => NULL -- can't happen -- ; io, alreadyExists, other => ERROR IFSFile.CantOpen[reason]; accessDenied, accessConflict => { SetOtherPass[h, "access Denied or Conflict"L]; fsinstance _ ForceClosed[fsinstance]; fsinstance _ Connect[host, h, NIL, NIL, connseq.user2, connseq.password2]; RETRY; }; notFound, illegalFileName => ERROR Subr.FileError[notFound]; ENDCASE => ERROR ]; }; RemoteMap: PUBLIC PROC[space: Space.Handle, fh: IFSFile.FileHandle, base: CARDINAL] = { < 64k words)>> problem: IFSFile.Problem; notready: BOOL; finished: CONDITION _ [timeout: 0]; DoRead: ENTRY PROC [bytebase: LONG CARDINAL, bytesize: CARDINAL, buffer: LONG POINTER] = { notready _ TRUE; IFSFile.StartRead[fh, bytebase, bytesize, buffer, CallProc, 0]; WHILE notready DO WAIT finished; ENDLOOP; SELECT problem FROM ok => NULL; io, resources, other, credentials => RETURN WITH ERROR IFSFile.Error[problem]; ENDCASE => ERROR; }; CallProc: ENTRY IFSFile.Completer = { problem _ outcome; notready _ FALSE; NOTIFY finished; }; { bytebase: LONG CARDINAL _ base*Environment.bytesPerPage; buffer: LONG POINTER _ Space.LongPointer[space]; filelength: LONG CARDINAL _ IFSFile.GetLength[fh]; pagesize: CARDINAL; bytesize: LONG CARDINAL; Space.Map[space]; [size: pagesize] _ Space.GetAttributes[space]; bytesize _ LONG[pagesize] * Environment.bytesPerPage; IF bytebase + bytesize > filelength THEN { IF filelength >= bytebase THEN bytesize _ filelength - bytebase ELSE ERROR; -- base is a page number bigger than the file }; <> <> WHILE bytesize > 0 DO thisTimeSize: CARDINAL = Inline.LowHalf[MIN[bytesize, LONG[63*512]]]; DoRead[bytebase, thisTimeSize, buffer]; bytesize _ bytesize - thisTimeSize; bytebase _ bytebase + thisTimeSize; buffer _ buffer + (thisTimeSize/2); ENDLOOP; }}; NPages: CARDINAL = 4; NBytes: CARDINAL = NPages * Environment.bytesPerPage; BytesAtATime: PROC[len: LONG CARDINAL] RETURNS[nb: CARDINAL] = INLINE { RETURN[IF len > NBytes THEN NBytes ELSE Inline.LowHalf[len]]; }; RemoteCopy: PUBLIC PROC[fromHost, fromFilename, toHost, toFilename: LONG STRING, h: Subr.TTYProcs] RETURNS[length: LONG CARDINAL] = { createtime, len: LONG CARDINAL; fhRead, fhWrite: IFSFile.FileHandle _ NIL; buffer: ARRAY[0 .. NPages*Environment.wordsPerPage) OF WORD; base: CARDINAL _ 0; problem: IFSFile.Problem; notready: BOOL; finished: CONDITION _ [timeout: 0]; { ENABLE UNWIND => { IF fhRead ~= NIL THEN IFSFile.Close[fhRead]; fhRead _ NIL; IF fhWrite ~= NIL THEN IFSFile.Close[fhWrite]; fhWrite _ NIL; }; DoRead: ENTRY PROC = { notready _ TRUE; IFSFile.StartRead[fhRead, base*Environment.bytesPerPage, BytesAtATime[len], @buffer, CallProc, 0]; WHILE notready DO WAIT finished; ENDLOOP; SELECT problem FROM ok => NULL; io, resources, other, credentials => RETURN WITH ERROR IFSFile.Error[problem]; ENDCASE => ERROR; }; DoWrite: ENTRY PROC = { notready _ TRUE; IFSFile.StartWrite[fhWrite, base*Environment.bytesPerPage, BytesAtATime[len], @buffer, CallProc, 0]; WHILE notready DO WAIT finished; ENDLOOP; SELECT problem FROM ok => NULL; io, resources, other, credentials => RETURN WITH ERROR IFSFile.Error[problem]; ENDCASE => ERROR; }; CallProc: ENTRY IFSFile.Completer = { problem _ outcome; notready _ FALSE; NOTIFY finished; }; fhRead _ Open[fromHost, fromFilename, h, oldReadOnly]; fhWrite _ Open[toHost, toFilename, h, new]; [create: createtime] _ IFSFile.GetTimes[fhRead]; length _ len _ IFSFile.GetLength[fhRead]; IFSFile.SetLength[fhWrite, length]; WHILE len > 0 DO DoRead[]; DoWrite[]; len _ len - BytesAtATime[len]; base _ base + NPages; ENDLOOP; IFSFile.SetCreation[fhWrite, createtime]; IFSFile.Close[fhRead]; fhRead _ NIL; IFSFile.Close[fhWrite]; fhWrite _ NIL; }}; Retrieve: PUBLIC PROC[remoteHost, remoteFilename, localFilename: LONG STRING, h: Subr.TTYProcs] RETURNS[length: LONG CARDINAL, cap: File.Capability] = { npages, base: CARDINAL; buffer: LONG POINTER; len, create, read, write: LONG CARDINAL; sh: Stream.Handle _ NIL; fh: IFSFile.FileHandle _ NIL; space: Space.Handle _ Space.nullHandle; problem: IFSFile.Problem; notready: BOOL; finished: CONDITION _ [timeout: 0]; DoReadAndWrite: ENTRY PROC = { base _ 0; WHILE len > 0 DO notready _ TRUE; IFSFile.StartRead[fh, base*Environment.bytesPerPage, BytesAtATime[len], buffer, CallProc, 0]; WHILE notready DO WAIT finished; ENDLOOP; SELECT problem FROM ok => NULL; io, resources, other, credentials => RETURN WITH ERROR IFSFile.Error[problem]; ENDCASE => ERROR; Space.CopyOut[space, [cap, base+1]]; base _ base + NPages; len _ len - BytesAtATime[len]; Space.Kill[space]; ENDLOOP; }; CallProc: ENTRY IFSFile.Completer = { problem _ outcome; notready _ FALSE; NOTIFY finished; }; {ENABLE UNWIND => { IF sh ~= NIL THEN Stream.Delete[sh]; sh _ NIL; IF space ~= Space.nullHandle THEN Space.Delete[space]; space _ Space.nullHandle; IF fh ~= NIL THEN IFSFile.Close[fh]; fh _ NIL; }; fh _ Open[remoteHost, remoteFilename, h, oldReadOnly]; len _ length _ IFSFile.GetLength[fh]; npages _ Inline.LowHalf[length/Environment.bytesPerPage] + 2; cap _ Directory.CreateFile[localFilename, DCSFileTypes.tLeaderPage, npages ! Directory.Error => { IF type = fileAlreadyExists THEN { cap _ Directory.Lookup[fileName: localFilename, permissions: Directory.ignore]; File.SetSize[cap, npages]; } ELSE ERROR Subr.FileError[notFound]; CONTINUE }]; space _ Space.Create[NPages, Space.virtualMemory]; buffer _ Space.LongPointer[space]; Space.Map[space]; DoReadAndWrite[]; Space.Delete[space]; space _ Space.nullHandle; [read, write, create] _ IFSFile.GetTimes[fh]; sh _ FileStream.Create[cap]; FileStream.SetLength[sh, length]; FileStream.SetLeaderPropertiesForCapability[cap: cap, create: LOOPHOLE[create], write: LOOPHOLE[write], read: LOOPHOLE[read]]; Stream.Delete[sh]; sh _ NIL; IFSFile.Close[fh]; fh _ NIL; }}; Store: PUBLIC PROC[localFilename, remoteHost, remoteFilename: LONG STRING, h: Subr.TTYProcs] RETURNS[length: LONG CARDINAL] = { base: CARDINAL; buffer: LONG POINTER; len, create: LONG CARDINAL; fh: IFSFile.FileHandle _ NIL; space: Space.Handle _ Space.nullHandle; problem: IFSFile.Problem; notready: BOOL; finished: CONDITION _ [timeout: 0]; cap: File.Capability; directoryfilename: LONG STRING _ Subr.AllocateString[100]; DoReadAndWrite: ENTRY PROC = { base _ 0; WHILE len > 0 DO notready _ TRUE; Space.CopyIn[space, [cap, base+1]]; IFSFile.StartWrite[fh, base*Environment.bytesPerPage, BytesAtATime[len], buffer, CallProc, 0]; WHILE notready DO WAIT finished; ENDLOOP; SELECT problem FROM ok => NULL; io, resources, other, credentials => RETURN WITH ERROR IFSFile.Error[problem]; ENDCASE => ERROR; base _ base + NPages; len _ len - BytesAtATime[len]; Space.Kill[space]; ENDLOOP; }; CallProc: ENTRY IFSFile.Completer = { problem _ outcome; notready _ FALSE; NOTIFY finished; }; {ENABLE UNWIND => { IF space ~= Space.nullHandle THEN Space.Delete[space]; space _ Space.nullHandle; IF fh ~= NIL THEN IFSFile.Close[fh]; fh _ NIL; Subr.FreeString[directoryfilename]; }; fh _ Open[remoteHost, remoteFilename, h, new]; space _ Space.Create[NPages, Space.virtualMemory]; buffer _ Space.LongPointer[space]; Space.Map[space]; cap _ Directory.Lookup[fileName: localFilename, permissions: Directory.ignore]; [byteLength: length, createDate: create] _ Directory.GetProps[cap, directoryfilename]; len _ length; IFSFile.SetLength[fh, length]; DoReadAndWrite[]; Space.Delete[space]; space _ Space.nullHandle; IFSFile.SetCreation[fh, create]; IFSFile.Close[fh]; fh _ NIL; }; Subr.FreeString[directoryfilename]; }; PrintLeafProblem: PUBLIC PROC[problem: IFSFile.Problem] = { CWF.WF2["Leaf Error: %s, #%u in IFSFile.Problem.\n"L, (SELECT problem FROM ok => "ok"L, io => "io"L, resources => "resources"L, credentials => "credentials"L, illegalIO => "illegalIO"L, other => "other"L, ENDCASE => ERROR), @problem]; }; PrintLeafAccessFailure: PUBLIC PROC[reason: IFSFile.AccessFailure] = { CWF.WF2["Leaf Error: %s, #%u in IFSFile.AccessFailure.\n"L, (SELECT reason FROM ok => "ok"L, io => "io"L, notFound => "notFound"L, alreadyExists => "alreadyExists"L, accessDenied => "accessDenied"L, accessConflict => "accessConflict"L, illegalFileName => "illegalFileName"L, other => "other"L, ENDCASE => ERROR), @reason]; }; <<>> SetOtherPass: PROC[h: Subr.TTYProcs, error: STRING] = { CheckStarted[]; CWF.WF1["Error '%s'\nEnter Connect "L, error]; Subr.GetNameandPassword[connect, connseq.user2, connseq.password2, h]; <> <> IF connseq.user2.length = 0 AND connseq.password2.length = 0 THEN { IF h.Confirm[h.in, h.out, h.data, "\nType y to retry with different login name, n to abort ", 'n] = 'n THEN { CWF.WF0["No.\n"L]; SIGNAL Subr.AbortMyself; }; CWF.WF0["Yes.\nEnter "L]; Subr.GetNameandPassword[login, NIL, NIL, h]; CWF.WFCR[]; }; }; Connect: PROC[host: LONG STRING, h: Subr.TTYProcs, defaultUsername, defaultPassword, secondaryName, secondaryPassword: LONG STRING] RETURNS[IFSFile.FSInstance] = { StartLeaf[]; FOR i: CARDINAL IN [0..connseq.size) DO IF connseq[i].host ~= NIL AND LongString.EquivalentString[host, connseq[i].host] THEN RETURN[connseq[i].fsinstance]; ENDLOOP; connseq[connseq.size].fsinstance _ MakeFSInstance[host, h, defaultUsername, defaultPassword, secondaryName, secondaryPassword]; connseq[connseq.size].host _ Subr.CopyString[host]; IF connseq.size >= connseq.maxsize THEN { CWF.WF0["Error - to many conns\n"L]; RETURN[NIL]; }; connseq.size _ connseq.size + 1; RETURN[connseq[connseq.size-1].fsinstance]; }; ForceClosed: PROC[fsinstance: IFSFile.FSInstance] RETURNS[IFSFile.FSInstance] = { FOR i: CARDINAL IN [0 .. connseq.size) DO IF fsinstance = connseq[i].fsinstance THEN CloseAndFree[i]; ENDLOOP; RETURN[NIL]; }; CloseAndFree: PROC[i: CARDINAL] = { CWF.WF1["Closing Leaf connection to %s ... "L, connseq[i].host]; IFSFile.Logout[connseq[i].fsinstance]; CWF.WF0["closed.\n"L]; Subr.FreeString[connseq[i].host]; connseq[i].host _ NIL; connseq[i].fsinstance _ NIL; }; MakeFSInstance: PROC[host: LONG STRING, h: Subr.TTYProcs, defaultUsername, defaultPassword, secondaryName, secondaryPassword: LONG STRING] RETURNS[fsinstance: IFSFile.FSInstance] = { <> IF host = NIL THEN { CWF.WF0["Error: remote host has not been specified.\n"L]; RETURN[NIL]; }; CWF.WF1["Opening Leaf connection to %s.\n"L, host]; IF defaultUsername ~= NIL THEN { fsinstance _ IFSFile.Login[host, defaultUsername, defaultPassword, secondaryName, secondaryPassword ! IFSFile.UnableToLogin => IF reason = credentials THEN GOTO normal; ]; RETURN[fsinstance]; }; GOTO normal; EXITS normal => { n: LONG STRING _ Subr.AllocateString[50]; p: LONG STRING _ Subr.AllocateString[50]; {ENABLE UNWIND => {Subr.FreeString[n]; Subr.FreeString[p]}; AddUserName[NIL, NIL, h]; UserCredentialsUnsafe.GetUserCredentials[name: n, password: p]; fsinstance _ IFSFile.Login[ host, n, p, secondaryName, secondaryPassword ! IFSFile.UnableToLogin => IF reason = credentials THEN {SetPass[h, NIL]; RETRY} ]; }; -- of ENABLE UNWIND Subr.FreeString[n]; Subr.FreeString[p]; }; }; SetPass: PROC[h: Subr.TTYProcs, error: LONG STRING] = { CheckStarted[]; IF error ~= NIL THEN CWF.WF1["Error '%s'\n"L,error]; CWF.WF0["Enter "L]; Subr.GetNameandPassword[login, NIL, NIL, h]; CWF.WFCR[]; }; AddUserName: PROC[sto: LONG STRING, spat: STRING, h: Subr.TTYProcs] = { u: LONG STRING _ Subr.AllocateString[50]; p: LONG STRING _ Subr.AllocateString[50]; lengthzero: BOOL; {ENABLE UNWIND => {Subr.FreeString[u]; Subr.FreeString[p]}; UserCredentialsUnsafe.GetUserCredentials[name: NIL, password: p]; lengthzero _ p = NIL OR p.length = 0; DO <> UserCredentialsUnsafe.GetUserCredentials[name: u, password: NIL]; IF u = NIL OR u.length = 0 OR Subr.Any[u,' ] OR Subr.Prefix[u, "CedarUser"L] OR lengthzero THEN { lengthzero _ FALSE; CWF.WF0["Enter "L]; Subr.GetNameandPassword[login, NIL, NIL, h]; CWF.WFCR[]; } ELSE EXIT; ENDLOOP; IF sto ~= NIL THEN CWF.SWF1[sto,spat,u]; }; -- of ENABLE UNWIND Subr.FreeString[u]; Subr.FreeString[p]; }; StartLeaf: PROC = { longzone: UNCOUNTED ZONE; IF connseq ~= NIL THEN RETURN; longzone _ Subr.LongZone[]; connseq _ longzone.NEW[ConnSeqRecord[MAXCONNS]]; connseq.user2 _ Subr.AllocateString[50]; connseq.password2 _ Subr.AllocateString[50]; }; StopLeaf: PUBLIC PROC = { ENABLE UNWIND => connseq _ NIL; longzone: UNCOUNTED ZONE; IF connseq = NIL THEN RETURN; longzone _ Subr.LongZone[]; FOR i: CARDINAL IN [0..connseq.size) DO IF connseq[i].fsinstance ~= NIL THEN CloseAndFree[i]; ENDLOOP; Subr.FreeString[connseq.user2]; Subr.FreeString[connseq.password2]; longzone.FREE[@connseq]; <> }; CheckStarted: PROC = { IF connseq = NIL THEN ERROR; }; IFSFile.Initialize[]; }.