-- file CoreLeaf.Mesa (See also Core*.mesa.) -- Last edited by Butterfield, July 27, 1979 3:11 PM. -- Converted to Multiprocess Leaf by Brotz, February 11, 1981 11:12 PM -- Edited by Brotz, March 18, 1981 9:45 AM DIRECTORY crD: FROM "CoreDefs", exD: FROM "ExceptionDefs", gsD: FROM "GlobalStorageDefs", Inline, intCommon, Leaf, NameInfoDefs, NameInfoSpecialDefs, ovD: FROM "OverviewDefs", PupDefs, PupTypes, RetrieveDefs, Sequin, Storage, String, TimeDefs; CoreLeaf: PROGRAM IMPORTS exD, Inline, intC: intCommon, NameInfoDefs, NameInfoSpecialDefs, PupDefs, RetrieveDefs, Sequin, Storage, String, TimeDefs EXPORTS crD SHARES crD = BEGIN OPEN crD; leafOK: CARDINAL _ 0; LeafOpenFile: PUBLIC PROCEDURE [user: DMSUser, filename: UFilename, mode: OpenMode] RETURNS [erc: ovD.ErrorCode, uFH: UFileHandle] = -- Opens the specified file in the specified mode, and returns a handle to the opened file. -- Handles are usable until a CloseFile or DeleteFile. When OpenFile fails, i.e. ErrorCode -- # ok, uFH = NIL, is returned. -- The maximum size of a filename is 99 characters. -- -- Error Codes: diskError, diskFull, diskCorrupted, illegalFilename, fileInUse, fileTooBig. -- BEGIN update: BOOLEAN = (mode = update); eofPage: PageNumber; eofByte: PageByte; fileSSD, serverSSD: String.SubStringDescriptor; server: STRING _ [50]; file: STRING _ [99]; i: CARDINAL; sl: SequinListPtr; openRequest: Leaf.OpenRequest; resetRequest: Leaf.ResetRequest; paramsRequest: Leaf.ParamsRequest; answer: Leaf.Answer; buffer: Sequin.Buffer; madeTemp: BOOLEAN _ FALSE; erc _ ovD.ok; uFH _ NIL; BEGIN -- for EXITS -- IF ~intC.leafOk THEN GO TO Illegal; IF leafOK#12795 THEN BEGIN userName: STRING; mbr: NameInfoDefs.Membership; SELECT RetrieveDefs.MailboxState[intC.retrieveHandle] FROM badName, badPwd => GO TO Illegal; ENDCASE; userName _ Storage.String[intC.user.name.length+intC.user.registry.length+1]; String.AppendString[userName, intC.user.name]; String.AppendChar[userName, '.]; String.AppendString[userName, intC.user.registry]; mbr _ NameInfoDefs.IsMemberClosure["CoreLeafUsers.ms"L, userName]; NameInfoSpecialDefs.CleanUp[]; Storage.FreeString[userName]; SELECT mbr FROM yes, allDown => leafOK _ 12795; ENDCASE => GO TO Illegal; END; IF filename.length < 2 THEN GO TO Illegal; IF filename[1] = '] THEN BEGIN temp: STRING; IF intC.remoteFilePath = NIL THEN GO TO Illegal; temp _ Storage.String[filename.length + intC.remoteFilePath.length]; String.AppendString[temp, intC.remoteFilePath]; fileSSD _ String.SubStringDescriptor[filename, 2, filename.length - 2]; String.AppendSubString[temp, @fileSSD]; filename _ temp; madeTemp _ TRUE; END; -- parse filename into server plus file and obtain the sequin for this server. FOR i IN [1 .. filename.length) DO IF filename[i] = '] THEN EXIT; REPEAT FINISHED => GO TO Illegal; ENDLOOP; serverSSD _ String.SubStringDescriptor[filename, 1, i - 1]; String.AppendSubString[server, @serverSSD]; fileSSD _ String.SubStringDescriptor[filename, i + 1, filename.length - i - 1]; IF i = 1 OR fileSSD.length > 99 THEN GO TO Illegal; String.AppendSubString[file, @fileSSD]; IF (sl _ GetSequinAddress[server]) = NIL THEN GO TO Broken; IF sl.nFiles = 0 THEN BEGIN -- set up a new sequin and start with reset and params. sl.handle _ Sequin.Create[sl.address, Leaf.ptLeaf]; buffer _ Sequin.GetEmptyBuffer[]; resetRequest _ LOOPHOLE[buffer.data]; resetRequest^ _ Leaf.RequestObject[op: Leaf.resetOp, opSpecific: reset[]]; buffer.nBytes _ Leaf.resetOp.length; AddStringToBuffer[@buffer, user.name]; AddStringToBuffer[@buffer, user.password]; resetRequest.op.length _ buffer.nBytes; Sequin.Put[sl.handle, buffer ! Sequin.Broken => GO TO Broken]; buffer _ Sequin.Get[sl.handle ! Sequin.Broken => GO TO Broken]; answer _ LOOPHOLE[buffer.data]; WITH ans: answer SELECT answer.op.type FROM reset => NULL; error => {erc _ LeafToUError[ans.error]; GO TO NotMyNight}; ENDCASE => {erc _ ovD.cantConnect; GO TO NotMyNight}; Sequin.ReleaseBuffer[buffer]; buffer _ Sequin.GetEmptyBuffer[]; paramsRequest _ LOOPHOLE[buffer.data]; paramsRequest^ _ Leaf.RequestObject [op: Leaf.paramsOp, opSpecific: params [packetDataBytes: 2 * MIN[PupDefs.DataWordsPerPupBuffer[], PupTypes.maxDataWordsPerGatewayPup], fileLockTimeout: 60, -- 5 minutes -- connectionTimeout: 60]]; -- 5 minutes -- buffer.nBytes _ Leaf.paramsOp.length; Sequin.Put[sl.handle, buffer ! Sequin.Broken => GO TO Broken]; buffer _ Sequin.Get[sl.handle ! Sequin.Broken => GO TO Broken]; answer _ LOOPHOLE[buffer.data]; WITH ans: answer SELECT answer.op.type FROM params => NULL; error => {erc _ LeafToUError[ans.error]; GO TO NotMyNight}; ENDCASE => {erc _ ovD.cantConnect; GO TO NotMyNight}; Sequin.ReleaseBuffer[buffer]; END; -- we have a valid sequin set up; now try to open the file. buffer _ Sequin.GetEmptyBuffer[]; openRequest _ LOOPHOLE[buffer.data]; openRequest^ _ Leaf.RequestObject [op: Leaf.openOp, opSpecific: open[write: update, extend: update, create: update, vExplicit: no]]; buffer.nBytes _ Leaf.openOp.length; AddStringToBuffer[@buffer, user.name]; AddStringToBuffer[@buffer, user.password]; AddStringToBuffer[@buffer, ""L]; -- connect name -- AddStringToBuffer[@buffer, ""L]; -- connect password -- AddStringToBuffer[@buffer, file]; openRequest.op.length _ buffer.nBytes; Sequin.Put[sl.handle, buffer ! Sequin.Broken => GO TO Broken]; buffer _ Sequin.Get[sl.handle ! Sequin.Broken => GO TO Broken]; answer _ LOOPHOLE[buffer.data]; WITH ans: answer SELECT answer.op.type FROM open => BEGIN sl.nFiles _ sl.nFiles + 1; [eofPage, eofByte] _ MapLeafToUAddress[ans.eofAddress]; uFH _ Storage.Node[SIZE[leaf UFileObject]]; uFH^ _ UFileObject [access: mode, lastFilePage: eofPage, byteFF: eofByte, close: LeafCloseFile, delete: LeafDeleteFile, length: LeafFileLength, getTimes: LeafGetTimes, setTimes: LeafSetTimes, truncate: LeafTruncateFile, read: LeafReadPages, write: LeafWritePages, varpart: leaf[sequinList: sl, leafHandle: LOOPHOLE[ans.handle]]]; END; error => {erc _ LeafToUError[ans.error]; GO TO NotMyNight}; ENDCASE => {erc _ ovD.cantConnect; GO TO NotMyNight}; Sequin.ReleaseBuffer[buffer]; EXITS Illegal => erc _ ovD.illegalFilename; Broken => erc _ ovD.cantConnect; NotMyNight => BEGIN Sequin.ReleaseBuffer[buffer]; IF sl.nFiles = 0 THEN {Sequin.Destroy[sl.handle]; RemoveSequinList[sl]}; END; END; -- for EXITS -- IF madeTemp THEN Storage.FreeString[filename]; END; -- of LeafOpenFile -- -- Note: all of the following operations require a UFileHandle for a file which is in open state. -- They have no way of checking the validity of the UFileHandle. -- Terrible things will happen if they are given an invalid UFileHandle. LeafCloseFile: PUBLIC PROCEDURE [uFH: UFileHandle] RETURNS [erc: ovD.ErrorCode] = -- Closes the file associated with uFH. The handle is closed and should not be re-used -- by the caller. If handle = NIL this is a no-op. -- Error Codes: cantConnect, diskError, diskCorrupted. BEGIN sl: SequinListPtr; leafHandle: Leaf.Handle; buffer: Sequin.Buffer; closeRequest: Leaf.CloseRequest; answer: Leaf.Answer; WITH fh: uFH SELECT FROM leaf => {leafHandle _ LOOPHOLE[fh.leafHandle]; sl _ LOOPHOLE[fh.sequinList]}; ENDCASE => exD.SysBug[]; buffer _ Sequin.GetEmptyBuffer[]; closeRequest _ LOOPHOLE[buffer.data]; closeRequest^ _ Leaf.RequestObject[op: Leaf.closeOp, opSpecific: close[handle: leafHandle]]; buffer.nBytes _ Leaf.closeOp.length; BEGIN -- for EXITS -- Sequin.Put[sl.handle, buffer ! Sequin.Broken => GO TO Broken]; buffer _ Sequin.Get[sl.handle ! Sequin.Broken => GO TO Broken]; answer _ LOOPHOLE[buffer.data]; WITH ans: answer SELECT answer.op.type FROM close => erc _ ovD.ok; error => erc _ LeafToUError[ans.error]; ENDCASE => erc _ ovD.cantConnect; Sequin.ReleaseBuffer[buffer]; EXITS Broken => erc _ ovD.cantConnect; END; -- for EXITS -- sl.nFiles _ sl.nFiles - 1; IF sl.nFiles = 0 THEN Sequin.Destroy[sl.handle]; Storage.Free[uFH]; END; -- of LeafCloseFile -- LeafDeleteFile: PUBLIC PROCEDURE [uFH: UFileHandle] RETURNS [erc: ovD.ErrorCode] = -- Deletes the file associated with uFH. The handle is closed and should not be re-used -- by the caller. If handle = NIL this is a no-op. -- Error Codes: cantConnect, diskError, diskCorrupted. BEGIN sl: SequinListPtr; leafHandle: Leaf.Handle; buffer: Sequin.Buffer; deleteRequest: Leaf.DeleteRequest; answer: Leaf.Answer; WITH fh: uFH SELECT FROM leaf => {leafHandle _ LOOPHOLE[fh.leafHandle]; sl _ LOOPHOLE[fh.sequinList]}; ENDCASE => exD.SysBug[]; buffer _ Sequin.GetEmptyBuffer[]; deleteRequest _ LOOPHOLE[buffer.data]; deleteRequest^ _ Leaf.RequestObject[op: Leaf.deleteOp, opSpecific: delete[handle: leafHandle]]; buffer.nBytes _ Leaf.deleteOp.length; BEGIN -- for EXITS -- Sequin.Put[sl.handle, buffer ! Sequin.Broken => GO TO Broken]; buffer _ Sequin.Get[sl.handle ! Sequin.Broken => GO TO Broken]; answer _ LOOPHOLE[buffer.data]; WITH ans: answer SELECT answer.op.type FROM delete => erc _ ovD.ok; error => erc _ LeafToUError[ans.error]; ENDCASE => erc _ ovD.cantConnect; Sequin.ReleaseBuffer[buffer]; EXITS Broken => erc _ ovD.cantConnect; END; -- for EXITS -- sl.nFiles _ sl.nFiles - 1; IF sl.nFiles = 0 THEN Sequin.Destroy[sl.handle]; Storage.Free[uFH]; END; -- of LeafDeleteFile -- LeafFileLength: PUBLIC PROCEDURE [uFH: UFileHandle] RETURNS [erc: ovD.ErrorCode, lastPage: PageNumber, byteFF: PageByte] = -- Returns the first free byte in the file, i.e. its length, via (lastPage * 512 + byteFF). -- Any data that has been written since the file was opened is included. Empty files -- return [0,0]. -- Note: The name of this routine may be misleading. The length returned is a DMS file -- length; the page number component is a crD.PageNumber, not an AltoPageNumber. -- Error Codes: none. {RETURN[ovD.ok, uFH.lastFilePage, uFH.byteFF]}; LeafTruncateFile: PUBLIC PROCEDURE [lastPage: PageNumber, byteFF: PageByte, uFH: UFileHandle] RETURNS [erc: ovD.ErrorCode] = -- Shortens a file which has been opened by LeafOpenFile. The position has the same -- semantics as UFileLength. Do NOT use to LENGTHEN a file! File must have been -- opened in "update" mode. -- Error Codes: cantConnect, diskError, diskCorrupted. BEGIN sl: SequinListPtr; leafHandle: Leaf.Handle; buffer: Sequin.Buffer; truncateRequest: Leaf.TruncateRequest; answer: Leaf.Answer; WITH fh: uFH SELECT FROM leaf => {leafHandle _ LOOPHOLE[fh.leafHandle]; sl _ LOOPHOLE[fh.sequinList]}; ENDCASE => exD.SysBug[]; buffer _ Sequin.GetEmptyBuffer[]; truncateRequest _ LOOPHOLE[buffer.data]; truncateRequest^ _ Leaf.RequestObject [op: Leaf.truncateOp, opSpecific: truncate [handle: leafHandle, eofAddress: MapUToLeafAddress[lastPage, byteFF]]]; buffer.nBytes _ Leaf.truncateOp.length; BEGIN -- for EXITS -- Sequin.Put[sl.handle, buffer ! Sequin.Broken => GO TO Broken]; buffer _ Sequin.Get[sl.handle ! Sequin.Broken => GO TO Broken]; answer _ LOOPHOLE[buffer.data]; WITH ans: answer SELECT answer.op.type FROM truncate => erc _ ovD.ok; error => erc _ LeafToUError[ans.error]; ENDCASE => erc _ ovD.cantConnect; Sequin.ReleaseBuffer[buffer]; uFH.lastFilePage _ lastPage + byteFF / 512; uFH.byteFF _ byteFF MOD 512; EXITS Broken => erc _ ovD.cantConnect; END; -- for EXITS -- END; -- of LeafTruncateFile -- LeafReadPages: PROCEDURE [coreBuffer: gsD.MemoryPagePtr, byteCount: CARDINAL, pageNumber: PageNumber, uFH: UFileHandle] RETURNS[erc: ovD.ErrorCode, bytesRead: CARDINAL] = -- Reads up to "byteCount" bytes of data from the file associated with uFH beginning with -- page "pageNumber" into the memory page(s) beginning at "coreBuffer". The byteCount -- must be a multiple of 512 (an even page). The bytesRead returned is the count of bytes -- actually read and will be equal to the requested byteCount except at EOF, when it may -- be less. Note that if pageNumber is beyound the end-of-file then bytesRead = 0 is -- returned. -- Error Codes: diskError. BEGIN sl: SequinListPtr; leafHandle: Leaf.Handle; buffer: Sequin.Buffer; readRequest: Leaf.ReadRequest; answer: Leaf.Answer; answerBytes: CARDINAL; copyFrom, copyTo: POINTER; WITH fh: uFH SELECT FROM leaf => {leafHandle _ LOOPHOLE[fh.leafHandle]; sl _ LOOPHOLE[fh.sequinList]}; ENDCASE => exD.SysBug[]; IF pageNumber > uFH.lastFilePage OR byteCount < 512 THEN RETURN[ovD.ok, 0]; IF LONG[pageNumber] * 512 + byteCount > LONG[uFH.lastFilePage] * 512 + uFH.byteFF THEN IF (byteCount _ (uFH.lastFilePage - pageNumber) * 512 + uFH.byteFF) = 0 THEN RETURN[ovD.ok, 0]; buffer _ Sequin.GetEmptyBuffer[]; readRequest _ LOOPHOLE[buffer.data]; readRequest^ _ Leaf.RequestObject [op: Leaf.readOp, opSpecific: read [handle: leafHandle, address: MapUToLeafAddress[pageNumber, 0], length: byteCount]]; buffer.nBytes _ Leaf.readOp.length; BEGIN -- for EXITS -- Sequin.Put[sl.handle, buffer ! Sequin.Broken => GO TO Broken]; bytesRead _ 0; copyTo _ coreBuffer; UNTIL byteCount = 0 DO buffer _ Sequin.Get[sl.handle ! Sequin.Broken => GO TO Broken]; answer _ LOOPHOLE[buffer.data]; answerBytes _ buffer.nBytes - Leaf.readAns.length; WITH ans: answer SELECT answer.op.type FROM read => erc _ ovD.ok; error => {erc _ LeafToUError[ans.error]; GO TO CantConnect}; ENDCASE => {erc _ ovD.cantConnect; GO TO CantConnect}; copyFrom _ buffer.data + Leaf.readAns.length / 2; Inline.COPY[from: copyFrom, to: copyTo, nwords: (answerBytes + 1) / 2]; byteCount _ byteCount - answerBytes; bytesRead _ bytesRead + answerBytes; copyTo _ copyTo + answerBytes / 2; Sequin.ReleaseBuffer[buffer]; IF answerBytes MOD 2 = 1 AND byteCount # 0 THEN exD.SysBug[]; ENDLOOP; EXITS Broken => erc _ ovD.cantConnect; CantConnect => Sequin.ReleaseBuffer[buffer]; END; -- for EXITS -- END; -- of LeafReadPages -- LeafWritePages: PUBLIC PROCEDURE [coreBuffer: gsD.MemoryPagePtr, byteCount: CARDINAL, pageNumber: PageNumber, uFH: UFileHandle] RETURNS [erc: ovD.ErrorCode] = -- Writes byteCount of data from memory, beginning at coreBuffer, to pageNumber of the -- file uFH. The operation is legal only if the file was opened in update OpenMode. This -- procedure can be used to extend the file's size by writing beyond the current end. -- Note, however, that the file is never shortened. byteCount must always be a -- multiple of 512, an even page, except on the last page of the file. byteCount must not -- be 0. -- Regarding byteCount, let "remainder" be (byteCount MOD 512). If non-zero, remainder -- denotes the number of bytes supplied by the client to be written on the last page of the -- current transaction. The number of byte actually written will be the -- MAX[current-bytes-on-that-file-page, remainder]; the value of all bytes beyond -- remainder are indeterminate. -- Side effects: Updates the file size in the uFH as needed. -- Error Codes: diskFull, diskError, fileTooBig. BEGIN sl: SequinListPtr; leafHandle: Leaf.Handle; buffer: Sequin.Buffer; writeRequest: Leaf.WriteRequest; answer: Leaf.Answer; lastAddress, lastWrittenAddress: Leaf.FileAddress; writeBytes, maxWriteBytes, bytesWritten: CARDINAL; copyFrom: POINTER _ coreBuffer; packetsSent, packetsAcked: CARDINAL; packetsSentLimit: CARDINAL = (511 / (PupDefs.DataWordsPerPupBuffer[] * 2 - Leaf.writeOp.length)) + 1; WITH fh: uFH SELECT FROM leaf => {leafHandle _ LOOPHOLE[fh.leafHandle]; sl _ LOOPHOLE[fh.sequinList]}; ENDCASE => exD.SysBug[]; erc _ ovD.ok; BEGIN -- for EXITS -- bytesWritten _ 0; UNTIL byteCount = 0 DO packetsSent _ 0; UNTIL byteCount = 0 OR packetsSent = packetsSentLimit DO buffer _ Sequin.GetEmptyBuffer[]; maxWriteBytes _ buffer.maxBytes - Leaf.writeOp.length; writeBytes _ MIN[byteCount, maxWriteBytes]; writeRequest _ LOOPHOLE[buffer.data]; writeRequest^ _ Leaf.RequestObject [op: Leaf.writeOp, opSpecific: write [handle: leafHandle, address: MapUToLeafAddress[pageNumber, bytesWritten], length: writeBytes, writeBody: NULL]]; buffer.nBytes _ writeRequest.op.length _ writeBytes + Leaf.writeOp.length; Inline.COPY[from: copyFrom, to: @writeRequest.writeBody, nwords: (writeBytes + 1) / 2]; Sequin.Put[sl.handle, buffer ! Sequin.Broken => GO TO Broken]; packetsSent _ packetsSent + 1; copyFrom _ copyFrom + writeBytes / 2; byteCount _ byteCount - writeBytes; bytesWritten _ bytesWritten + writeBytes; IF byteCount > 0 AND writeBytes MOD 2 = 1 THEN exD.SysBug[]; ENDLOOP; FOR packetsAcked _ 0, packetsAcked + 1 UNTIL packetsAcked = packetsSent DO buffer _ Sequin.Get[sl.handle ! Sequin.Broken => GO TO Broken]; answer _ LOOPHOLE[buffer.data]; WITH ans: answer SELECT answer.op.type FROM write => erc _ ovD.ok; error => {erc _ LeafToUError[ans.error]; GO TO CantConnect}; ENDCASE => {erc _ ovD.cantConnect; GO TO CantConnect}; IF buffer.nBytes # Leaf.writeAns.length THEN exD.SysBug[]; Sequin.ReleaseBuffer[buffer]; ENDLOOP; ENDLOOP; lastAddress _ MapUToLeafAddress[uFH.lastFilePage, uFH.byteFF]; lastWrittenAddress _ MapUToLeafAddress[pageNumber, bytesWritten]; IF lastWrittenAddress.high > lastAddress.high OR lastWrittenAddress.high = lastAddress.high AND lastWrittenAddress.low > lastAddress.low THEN [uFH.lastFilePage, uFH.byteFF] _ MapLeafToUAddress[lastWrittenAddress]; EXITS Broken => erc _ ovD.cantConnect; CantConnect => Sequin.ReleaseBuffer[buffer]; END; -- for EXITS -- END; -- of LeafWritePages -- LeafGetTimes: PROCEDURE [uFH: UFileHandle] RETURNS [read, write, create: TimeDefs.PackedTime, ec: ovD.ErrorCode] = BEGIN read _ write _ create _ TimeDefs.CurrentDayTime[]; ec _ ovD.ok; END; -- of LeafGetTimes -- LeafSetTimes: PROCEDURE [uFH: UFileHandle, read, write, create: TimeDefs.PackedTime] RETURNS [ovD.ErrorCode] = BEGIN RETURN[ovD.ok]; END; -- of LeafSetTimes -- LeafToUError: PROCEDURE [error: Leaf.IfsError] RETURNS [erc: ovD.ErrorCode] = BEGIN SELECT error FROM ok => erc _ ovD.ok; userName, userPassword, filesOnly, connectName, connectPassword, createStreamFailed => erc _ ovD.cantConnect; createStreamFailed => erc _ ovD.diskError; allocExceeded, fileSystemFull => erc _ ovD.diskFull; fileBusy => erc _ ovD.fileInUse; fileNotFound, dirNotFound, accessDenied, fileUndeletable => erc _ ovD.fileNotFound; nameMalformed, illegalChar, illegalStar, illegalVersion, nameTooLong, illegalDIFAccess, fileAlreadyExists, fileUndeletable => erc _ ovD.illegalFilename; ENDCASE => exD.SysBug[]; END; -- of LeafToUError -- AddStringToBuffer: PROCEDURE [buffer: POINTER TO Sequin.Buffer, string: STRING] = BEGIN startWord: CARDINAL = (buffer.nBytes + 1) / 2; nWords: CARDINAL; IF string = NIL THEN string _ ""L; nWords _ (string.length + 1) / 2; buffer.data.words[startWord] _ string.length; Inline.COPY[from: @string.text, to: @buffer.data.words[startWord + 1], nwords: nWords]; buffer.nBytes _ buffer.nBytes + 2 * nWords + 2; END; -- of AddStringToBuffer -- MapLeafToUAddress: PROCEDURE [leafAddress: Leaf.FileAddress] RETURNS [page: PageNumber, byte: PageByte] = BEGIN page _ (leafAddress.high MOD 512) * 128 + leafAddress.low / 512; byte _ leafAddress.low MOD 512; END; -- of MapLeafToUAddress -- MapUToLeafAddress: PROCEDURE [page: PageNumber, byte: PageByte] RETURNS [leafAddress: Leaf.FileAddress] = BEGIN page _ page + byte / 512; byte _ byte MOD 512; leafAddress _ Leaf.FileAddress[high: page / 128, low: (page MOD 128) * 512 + byte]; END; -- of MapUToLeafAddress -- SequinList: TYPE = RECORD [next: SequinListPtr, server: STRING, address: PupDefs.PupAddress, handle: Sequin.Handle, nFiles: CARDINAL]; SequinListPtr: TYPE = POINTER TO SequinList; sequinListHead: SequinListPtr _ NIL; GetSequinList: PROCEDURE [name: STRING] RETURNS [sl: SequinListPtr] = BEGIN FOR sl _ sequinListHead, sl.next UNTIL sl = NIL OR String.EquivalentString[name, sl.server] DO ENDLOOP; END; -- of GetSequinList -- RemoveSequinList: PROCEDURE [sl: SequinListPtr] = BEGIN prevSl: SequinListPtr _ NIL; nextSl: SequinListPtr _ sequinListHead; UNTIL nextSl = NIL OR nextSl = sl DO prevSl _ nextSl; nextSl _ nextSl.next; ENDLOOP; IF nextSl = NIL THEN exD.SysBug[]; IF prevSl = NIL THEN sequinListHead _ nextSl.next ELSE prevSl.next _ nextSl.next; Storage.FreeString[sl.server]; Storage.Free[sl]; END; -- of RemoveSequinList -- GetSequinAddress: PROCEDURE [name: STRING] RETURNS [sl: SequinListPtr] = BEGIN pupAddress: PupDefs.PupAddress; sl _ GetSequinList[name]; IF sl # NIL THEN RETURN; PupDefs.GetPupAddress [@pupAddress, name ! PupDefs.PupNameTrouble => GO TO NameTrouble]; pupAddress.socket _ Leaf.leafSocket; sl _ Storage.Node[SIZE[SequinList]]; sl^ _ SequinList [next: sequinListHead, server: Storage.String[name.length], address: pupAddress, handle: NULL, nFiles: 0]; String.AppendString[sl.server, name]; sequinListHead _ sl; EXITS NameTrouble => NULL; END; -- of GetSequinAddress -- END. -- of CoreLeaf --z20638x0(529)\f1