DIRECTORY BasicTime USING [GMT, Now, nullGMT, Pack, Unpack, Unpacked], FS USING [Error, ErrorGroup, InfoProc, NameProc], FSBackdoor USING [ErrorCode, ProduceError, Version], FSName USING [BangStarFile, BangVersionFile, VersionFromRope], FSPseudoServers USING [AvoidRemoteCheck, PseudoServerList, TranslateForRead, TranslateForWrite], FSRemoteFile USING [ConfirmProc, Lookup, LookupResult], FSReport USING [UnknownFile], GVBasics USING [Connect], GVNames USING [ConnectInfo, GetConnect], IO USING [STREAM], Process USING [CheckForAbort, Detach, Seconds, SecondsToTicks], Rope USING [Cat, Concat, Equal, Fetch, Find, Flatten, Length, Match, ROPE], RuntimeError USING [UNCAUGHT], STP USING [Close, ConfirmProcType, Continue, Create, Delete, DesiredProperties, Enumerate, Error, ErrorCode, FileInfo, GetFileInfo, Handle, IsOpen, Login, NoteFileProcType, Open, Rename, Retrieve, SetDesiredProperties, SetDirectory, Store, ValidProperties], UserCredentials USING [Get]; FSRemoteFileImpl: CEDAR MONITOR IMPORTS BasicTime, FS, FSBackdoor, FSName, FSPseudoServers, FSRemoteFile, FSReport, GVNames, Process, Rope, RuntimeError, STP, UserCredentials EXPORTS FSRemoteFile = { GMT: TYPE = BasicTime.GMT; Handle: TYPE = STP.Handle; ROPE: TYPE = Rope.ROPE; PseudoServerList: TYPE = FSPseudoServers.PseudoServerList; STREAM: TYPE = IO.STREAM; Version: TYPE = FSBackdoor.Version; Delete: PUBLIC PROC [server, file: ROPE, wantedCreatedTime: GMT, proc: FSRemoteFile.ConfirmProc] = { Confirm: STP.ConfirmProcType = { info: STP.FileInfo = STP.GetFileInfo[h]; created: GMT = FTPTimeToGMT[info.create]; SELECT TRUE FROM matchFound => answer _ abort; NOT timeSearch OR wantedCreatedTime = created => { answer _ IF proc [FSName.VersionFromRope[info.version]] THEN do ELSE abort; matchFound _ TRUE; }; ENDCASE => answer _ skip; }; matchFound: BOOLEAN _ FALSE; timeSearch: BOOLEAN = (wantedCreatedTime # BasicTime.nullGMT); stpCode: STP.ErrorCode _ noSuchFile; h: Handle _ NIL; innerDelete: PROC RETURNS [ok: BOOL _ TRUE] = { tServer: ROPE _ FSPseudoServers.TranslateForWrite[UnbracketServer[server]]; h _ NIL; { ENABLE UNWIND => ReturnConnection[tServer, h]; h _ GetConnection[tServer, file, FALSE]; STP.Delete[h, file, Confirm ! STP.Error => IF code = noSuchFile THEN CONTINUE ]; IF NOT matchFound THEN { IF timeSearch THEN { file _ FSName.BangStarFile[file]; STP.Delete[h, file, Confirm]; }; IF NOT matchFound THEN { ReturnConnection[tServer, h]; stpCode _ noSuchFile; RETURN [FALSE]}; }; }; ReturnConnection[tServer, h]; }; IF innerDelete[ ! STP.Error => {stpCode _ code; CONTINUE}] THEN RETURN; ReportSTPError[stpCode, server, file, wantedCreatedTime]; }; EnumerateForInfo: PUBLIC PROC [server, pattern: ROPE, proc: FS.InfoProc] = { NoteInfo: LocalEnumProc = { info: STP.FileInfo = STP.GetFileInfo[h]; created: BasicTime.GMT = FTPTimeToGMT[info.create]; continue _ IF proc[Rope.Concat[serverB, file], NIL, created, info.size, 0] THEN yes ELSE no; }; serverB: ROPE = BracketServer[server]; InnerEnumerate[server, pattern, NoteInfo, FALSE]; }; EnumerateForNames: PUBLIC PROC [server, pattern: ROPE, proc: FS.NameProc] = { NoteName: LocalEnumProc = { continue _ IF proc[Rope.Concat[serverB, file]] THEN yes ELSE no; }; serverB: ROPE = BracketServer[server]; InnerEnumerate[server, pattern, NoteName, TRUE]; }; LocalEnumProc: TYPE = PROC [h: Handle, file: ROPE] RETURNS [continue: STP.Continue]; InnerEnumerate: PUBLIC PROC [server, pattern: ROPE, Note: LocalEnumProc, namesOnly: BOOL] = { stpCode: STP.ErrorCode _ noSuchFile; h: Handle _ NIL; notedOne: BOOL _ FALSE; innerNote: STP.NoteFileProcType = { Process.CheckForAbort[]; notedOne _ TRUE; IF NOT Rope.Match["<*", file] THEN file _ Rope.Concat["<>", file]; continue _ Note[h, file]; }; innerBlock: PROC [eachName: ROPE] = { h _ NIL; { ENABLE UNWIND => ReturnConnection[eachName, h]; h _ GetConnection[eachName, pattern, namesOnly]; STP.Enumerate[h, pattern, innerNote]; }; ReturnConnection[eachName, h]; }; serverList: LIST OF ROPE = FSPseudoServers.TranslateForRead[server]; replicated: BOOL = FSPseudoServers.AvoidRemoteCheck[server]; FOR each: LIST OF ROPE _ serverList, each.rest WHILE each # NIL DO innerBlock[each.first ! STP.Error => SELECT TRUE FROM code = noSuchFile => SELECT TRUE FROM replicated, notedOne => GO TO NotFound; ENDCASE => LOOP; notedOne => { stpCode _ code; EXIT}; each = serverList => { stpCode _ code; LOOP}; ENDCASE => LOOP; ]; RETURN; ENDLOOP; IF stpCode # noSuchFile THEN ReportSTPError[stpCode, server, pattern, BasicTime.nullGMT]; EXITS NotFound => {}; }; Info: PUBLIC PROC [server, file: ROPE, wantedCreatedTime: GMT] RETURNS [version: Version, bytes: INT, created: GMT] = { ReportError: PROC [stpCode: STP.ErrorCode] = { ReportSTPError[stpCode, server, file, wantedCreatedTime]; }; result: FSRemoteFile.LookupResult; serverList: LIST OF ROPE = FSPseudoServers.TranslateForRead[server]; replicated: BOOL = FSPseudoServers.AvoidRemoteCheck[server]; allDown: BOOL _ TRUE; FOR each: LIST OF ROPE _ serverList, each.rest WHILE each # NIL DO eachServer: ROPE _ each.first; last: BOOL _ each.rest = NIL; [result, version, created, bytes] _ FSRemoteFile.Lookup[eachServer, file]; IF result#noResponse THEN allDown _ FALSE; SELECT result FROM noSuchFile => { IF NOT replicated THEN LOOP; IF wantedCreatedTime = BasicTime.nullGMT THEN ReportError[noSuchFile]; EXIT; }; ok => { IF wantedCreatedTime = BasicTime.nullGMT THEN RETURN; IF wantedCreatedTime = created THEN RETURN; IF NOT replicated THEN LOOP; EXIT}; ENDCASE; ENDLOOP; [version, bytes, created] _ STPInfo[server, file, wantedCreatedTime]; }; Rename: PUBLIC PROC [server, fromFile: ROPE, fromCreated: GMT, toFile: ROPE, proc: FSRemoteFile.ConfirmProc] = { stpCode: STP.ErrorCode _ noSuchFile; version: Version = Info[server, fromFile, fromCreated].version; innerRename: PROC RETURNS [ok: BOOL _ TRUE] = { h: Handle _ NIL; tServer: ROPE _ FSPseudoServers.TranslateForWrite[server]; { ENABLE UNWIND => ReturnConnection[tServer, h]; h _ GetConnection[tServer, fromFile, TRUE]; fromFile _ FSName.BangVersionFile[fromFile, version]; STP.Rename[h, fromFile, toFile]; }; ReturnConnection[tServer, h]; }; IF proc[version] THEN { IF innerRename[! STP.Error => {stpCode _ code; CONTINUE}] THEN RETURN; ReportSTPError[stpCode, server, toFile, BasicTime.nullGMT]; }; }; Retrieve: PUBLIC PROC [server, file: ROPE, wantedCreatedTime: GMT, proc: PROC[fullGName: ROPE, bytes: INT, created: GMT] RETURNS [STREAM]] = { matchFound: BOOL _ FALSE; timeSearch: BOOL = (wantedCreatedTime # BasicTime.nullGMT); h: Handle _ NIL; stpCode: STP.ErrorCode _ noSuchFile; serverB: ROPE = BracketServer[server]; Confirm: STP.ConfirmProcType = { info: STP.FileInfo = STP.GetFileInfo[h]; created: GMT = FTPTimeToGMT[info.create]; SELECT TRUE FROM matchFound => answer _ abort; NOT timeSearch OR wantedCreatedTime = created => { localStream _ proc[Rope.Concat[serverB, file], info.size, created]; answer _ IF localStream = NIL THEN abort ELSE do; matchFound _ TRUE; }; ENDCASE => answer _ skip; }; innerRetrieve: PROC [eachServer: ROPE] RETURNS [success: BOOL _ TRUE] = { h _ NIL; { ENABLE UNWIND => ReturnConnection[eachServer, h]; h _ GetConnection[eachServer, file, FALSE]; STP.Retrieve[h, file, Confirm ! STP.Error => IF code = noSuchFile THEN CONTINUE ]; IF NOT matchFound THEN { IF timeSearch THEN { file _ FSName.BangStarFile[file]; STP.Retrieve[h, file, Confirm]; }; IF NOT matchFound THEN {stpCode _ noSuchFile; success _ FALSE}; }; }; ReturnConnection[eachServer, h]; }; serverList: LIST OF ROPE = FSPseudoServers.TranslateForRead[server]; replicated: BOOL = FSPseudoServers.AvoidRemoteCheck[server]; FOR each: LIST OF ROPE _ serverList, each.rest WHILE each # NIL DO IF innerRetrieve[each.first ! STP.Error => { IF each = serverList THEN stpCode _ code; IF each.rest = NIL THEN EXIT; IF stpCode = noSuchFile AND replicated THEN EXIT; LOOP} ] THEN RETURN ELSE IF replicated THEN EXIT; ENDLOOP; ReportSTPError[stpCode, server, file, wantedCreatedTime]; }; Store: PUBLIC PROC [server, file: ROPE, str: STREAM, created: GMT, proc: FSRemoteFile.ConfirmProc] = { h: Handle _ NIL; Note: STP.NoteFileProcType = { doIt: BOOL = proc [ FSName.VersionFromRope[ STP.GetFileInfo[h].version ] ]; continue _ IF doIt THEN yes ELSE no; }; innerStore: PROC RETURNS [ok: BOOL _ TRUE] = { tServer: ROPE _ FSPseudoServers.TranslateForWrite[server]; { ENABLE UNWIND => ReturnConnection[tServer, h]; h _ GetConnection[tServer, file, TRUE]; STP.Store[h, file, str, Note, unknown, created]; }; ReturnConnection[tServer, h]; }; stpCode: STP.ErrorCode _ noSuchFile; IF innerStore[ ! STP.Error => {stpCode _ code; CONTINUE}] THEN RETURN; ReportSTPError[stpCode, server, file, BasicTime.nullGMT]; }; FTPTimeToGMT: PUBLIC PROC [t: Rope.ROPE] RETURNS [time: BasicTime.GMT _ BasicTime.nullGMT] = { adjust: NAT _ 0; Digit: PROC [index: INT] RETURNS [CARDINAL] = { c: CHAR = Rope.Fetch[t, index-adjust]; SELECT c FROM ' => RETURN [0]; IN ['0..'9] => RETURN [c-'0]; ENDCASE => ERROR BadTime; }; DoubleDigit: PROC [index: INT] RETURNS [CARDINAL] = { RETURN [Digit[index]*10 + Digit[index+1]]; }; Char: PROC [index: INT] RETURNS [CHAR] = { c: CHAR = Rope.Fetch[t, index]; SELECT c FROM '-, '+, ' , IN ['a..'z], IN ['0..'9] => RETURN [c]; IN ['A..'Z] => RETURN [c + ('a-'A)]; ENDCASE => ERROR BadTime; }; Inner: PROC = { uT: BasicTime.Unpacked; yr: CARDINAL = DoubleDigit[7]; SELECT yr FROM < 50 => uT.year _ yr + 2000; ENDCASE => uT.year _ yr + 1900; IF Char[1] IN ['0..'9] THEN uT.day _ DoubleDigit[0] ELSE { uT.day _ Digit[0]; adjust _ 1}; uT.day _ DoubleDigit[0]; uT.hour _ DoubleDigit[10]; uT.minute _ DoubleDigit[13]; uT.second _ DoubleDigit[16]; uT.month _ SELECT Char[3] FROM 'j => IF Char[4] = 'a THEN January ELSE IF Char[5] = 'n THEN June ELSE July, 'f => February, 'm => IF Char[5] = 'r THEN March ELSE May, 'a => IF Char[4] = 'p THEN April ELSE August, 's => September, 'o => October, 'n => November, 'd => December, ENDCASE => ERROR BadTime; IF t.Length[] < 19 THEN { temp: BasicTime.Unpacked _ BasicTime.Unpack[BasicTime.Now[]]; uT.zone _ temp.zone; uT.dst _ temp.dst; } ELSE { uT.zone _ 60 * ( SELECT Char[19] FROM 'g => 0, 'e => 5, 'c => 6, 'm => 7, 'p => 8, '+ => Digit[20], '- => - Digit[20], ENDCASE => ERROR BadTime) ; uT.dst _ IF (Char[20] = 'd) THEN yes ELSE no; }; time _ BasicTime.Pack[uT]; }; Inner[ ! RuntimeError.UNCAUGHT => CONTINUE]; }; BadTime: ERROR = CODE; STPInfo: PROC [server, file: ROPE, wantedCreatedTime: GMT] RETURNS [version: Version, bytes: INT, created: GMT] = { h: Handle _ NIL; Note: STP.NoteFileProcType = { info: STP.FileInfo = STP.GetFileInfo[h]; created _ FTPTimeToGMT[info.create]; IF NOT timeSearch OR wantedCreatedTime = created THEN { version _ FSName.VersionFromRope[info.version]; bytes _ info.size; matchFound _ TRUE; continue _ IF timeSearch THEN no ELSE yes; } ELSE continue _ yes; }; matchFound: BOOL _ FALSE; timeSearch: BOOL = (wantedCreatedTime # BasicTime.nullGMT); stpCode: STP.ErrorCode _ noSuchFile; serverList: LIST OF ROPE = FSPseudoServers.TranslateForRead[server]; replicated: BOOL = FSPseudoServers.AvoidRemoteCheck[server]; innerSTPInfo: PROC [eachServer: ROPE] RETURNS [found: BOOL _ TRUE] = { ENABLE UNWIND => ReturnConnection[eachServer, h]; h _ NIL; h _ GetConnection[eachServer, file, FALSE]; STP.Enumerate[h, file, Note ! STP.Error => IF code = noSuchFile THEN CONTINUE ]; IF NOT matchFound THEN { IF timeSearch THEN { file _ FSName.BangStarFile[file]; STP.Enumerate[h, file, Note]; }; IF NOT matchFound THEN {stpCode _ noSuchFile; found _ FALSE}; }; ReturnConnection[eachServer, h]; }; FOR each: LIST OF ROPE _ serverList, each.rest WHILE each # NIL DO IF innerSTPInfo[each.first ! STP.Error => { IF each = serverList THEN stpCode _ code; IF each.rest = NIL THEN EXIT; IF stpCode = noSuchFile AND replicated THEN EXIT; LOOP}; ] THEN RETURN ELSE IF replicated THEN EXIT; ENDLOOP; ReportSTPError[stpCode, server, file, wantedCreatedTime]; }; MyDesiredProperties: TYPE = PACKED ARRAY STP.ValidProperties OF BoolDefaultFalse; BoolDefaultFalse: TYPE = BOOL _ FALSE; namesOnly: MyDesiredProperties = [directory: TRUE, nameBody: TRUE, version: TRUE]; nameSizeCreated: MyDesiredProperties = [ directory: TRUE, nameBody: TRUE, version: TRUE, createDate: TRUE, size: TRUE]; all: STP.DesiredProperties = ALL[TRUE]; -- stops sending of Desired-Property properties ConditionConnection: PROC[h: Handle, nameBody: ROPE, justNames: BOOL] = { IF NOT Rope.Match["<*", nameBody] THEN { STP.SetDirectory[h, " "]; STP.SetDesiredProperties[h, all]; } ELSE { STP.SetDirectory[h, NIL]; STP.SetDesiredProperties[h, IF justNames THEN namesOnly ELSE nameSizeCreated]; }; }; BracketServer: PROC[server: ROPE] RETURNS [ROPE] = { IF Rope.Match["[*", server] THEN RETURN [server] ELSE RETURN [ Rope.Cat[ "[", server, "]" ] ]; }; UnbracketServer: PROC[server: ROPE] RETURNS [ROPE] = { IF Rope.Match["[*", server] THEN RETURN [ Rope.Flatten[server, 1, Rope.Length[server]-2]] ELSE RETURN [ server ]; }; ReportSTPError: PUBLIC PROC [stpCode: STP.ErrorCode, server, file: ROPE, time: GMT] = { gName: ROPE = Rope.Concat[BracketServer[server], file]; e1: ROPE _ "Server for \""; e2: ROPE _ "\""; code: FSBackdoor.ErrorCode; NewError: PROC [group: FS.ErrorGroup, code: ATOM, explanation: ROPE] = { ERROR FS.Error[[group, code, Rope.Cat[e1, gName, "\"", explanation]]]; }; IF stpCode = noSuchFile THEN { FSReport.UnknownFile[gName, time]; -- raises FS.Error }; SELECT stpCode FROM noRouteToNetwork, noNameLookupResponse => { code _ serverInaccessible; e2 _ "\" is inaccessible"; }; connectionClosed => { code _ wentOffline; e2 _ "\" connection closed unexpectedly (wentOffline)"; }; connectionRejected => { code _ connectionRejected; e2 _ "\" rejected the connection attempt"; }; connectionTimedOut => { code _ connectionTimedOut; e2 _ "\" timed-out the connection"; }; accessDenied => { code _ accessDenied; e2 _ "\" denied file access permission"; }; requestRefused => { code _ quotaExceeded; e1 _ "Request refused (possibily no quota for storing) for \""; }; accessError => { code _ fileBusy; e1 _ "\""; e2 _ "\" is locked on the server"; }; illegalUserName => { code _ badCredentials; e1 _ "Credentials rejected when accessing \""; }; illegalFileName => { code _ illegalName; e2 _ "\" says that the file name is illegal"; }; noSuchHost => { code _ unknownServer; e1 _ "Couldn't find the server for \""; }; alreadyAConnection => NewError[bug, $alreadyAConnection, " already had a connection"]; noConnection => NewError[bug, $noConnection, " gave a noConnection error"]; illegalUserPassword => NewError[environment, $illegalUserPassword, " had an illegal user password"]; illegalUserAccount => NewError[environment, $illegalUserAccount, " had an illegal user account"]; illegalConnectName => NewError[environment, $illegalConnectName, " had an illegal connect name"]; illegalConnectPassword => NewError[environment, $illegalConnectPassword, " had an illegal connect password"]; credentailsMissing => NewError[environment, $credentailsMissing, " had missing credentails"]; protocolError => NewError[bug, $protocolError, " gave a protocol error to STP"]; noSuchFile => NewError[bug, $noSuchFile, " reported no such file"]; undefinedError => NewError[bug, $undefinedError, " gave STP an undefinedError"]; ENDCASE => ERROR; FSBackdoor.ProduceError[code, Rope.Cat[e1, gName, e2]]; }; maxSlots: CARDINAL = 9; -- only need enough for all used in last TimeOut seconds TimeOut: Process.Seconds _ 7; -- keep connection around for this long after use SlotArray: TYPE = ARRAY [0..maxSlots) OF Slot; Slot: TYPE = RECORD [ server: ROPE, h: Handle, used: BOOL ]; emptySlot: Slot = [NIL, NIL, FALSE]; new, reused, flushed: INT _ 0; -- statistics slot: REF SlotArray _ NEW[SlotArray _ ALL [emptySlot]]; haveSlotTimer: BOOL _ FALSE; ForTimeout: CONDITION _ [Process.SecondsToTicks[TimeOut]]; GrapevineCacheArray: TYPE = ARRAY [0..maxGVineCache) OF GrapevineCacheEntry; GrapevineCacheEntry: TYPE = RECORD[ name: ROPE _ NIL, connect: GVBasics.Connect ]; maxGVineCache: CARDINAL = 9; -- its quite expensive to find these out grapevineCachePut: CARDINAL _ 0; -- next victim grapevineCache: REF GrapevineCacheArray _ NEW[GrapevineCacheArray]; GetConnection: PROC [server, pattern: ROPE, justNames: BOOL] RETURNS [h: Handle] = { h _ LookForConnection[server]; IF h = NIL THEN { user, password: ROPE; h _ STP.Create[]; [user, password] _ UserCredentials.Get[]; STP.Login[h, user, password]; [] _ STP.Open[h, GetServerPupName[server]]; } ELSE { IF NOT STP.IsOpen[h] THEN { [] _ STP.Open[h, GetServerPupName[server]]; }; }; ConditionConnection[h, pattern, justNames]; }; GetServerPupName: PUBLIC PROC [server: ROPE] RETURNS [pupServer: ROPE] = { IF server.Find[".", 0, FALSE] > 0 THEN { info: GVNames.ConnectInfo; connect: GVBasics.Connect; foundInCache: BOOL; [found: foundInCache, connect: connect] _ FindInGrapevineCache[name: server]; IF foundInCache THEN RETURN[connect]; [info: info, connect: connect ] _ GVNames.GetConnect[server]; IF info = group OR info = individual THEN { AddToGrapevineCache[name: server, connect: connect]; RETURN[connect]; }; }; RETURN[server]; }; FindInGrapevineCache: ENTRY PROC [name: ROPE] RETURNS [found: BOOL _ FALSE, connect: GVBasics.Connect] = { FOR i: CARDINAL IN [0..maxGVineCache) DO IF Rope.Equal[name, grapevineCache[i].name, FALSE] THEN RETURN[TRUE, grapevineCache[i].connect]; ENDLOOP; }; AddToGrapevineCache: ENTRY PROC [name: ROPE, connect: GVBasics.Connect] = { grapevineCache[grapevineCachePut].name _ name; grapevineCache[grapevineCachePut].connect _ connect; IF (grapevineCachePut _ grapevineCachePut + 1) >= maxGVineCache THEN grapevineCachePut _ 0; }; LookForConnection: ENTRY PROC[server: ROPE] RETURNS [h: Handle] = { ENABLE UNWIND => NULL; FOR i: CARDINAL IN [0..maxSlots) DO IF slot[i] # emptySlot AND Rope.Equal[slot[i].server, server, FALSE] THEN { reused _ reused + 1; h _ slot[i].h; slot[i] _ emptySlot; RETURN; }; ENDLOOP; h _ NIL; new _ new + 1; }; ReturnConnection: PROC [server: ROPE, h: Handle] = { IF server = NIL OR h = NIL THEN RETURN; IF STP.IsOpen[h] AND NOT SaveConnection[server, h] THEN STP.Close[h ! STP.Error => CONTINUE ]; }; SaveConnection: ENTRY PROC [server: ROPE, h: Handle] RETURNS [BOOL] = { ENABLE UNWIND => NULL; FOR i: CARDINAL IN [0..maxSlots) DO IF slot[i] = emptySlot THEN { slot[i] _ [server, h, TRUE]; IF NOT haveSlotTimer THEN TRUSTED { haveSlotTimer _ TRUE; Process.Detach[FORK SlotTimer[]]; }; RETURN [TRUE]; }; ENDLOOP; RETURN [FALSE]; }; SlotTimer: PROC = { i: CARDINAL _ 0; h: Handle; DO [h, i] _ NextInterestingSlot[i]; IF h = NIL THEN RETURN; STP.Close[h ! STP.Error => LOOP ]; flushed _ flushed + 1; ENDLOOP; }; NextInterestingSlot: ENTRY PROC [start: CARDINAL] RETURNS [h: Handle, index: CARDINAL] = { ENABLE UNWIND => NULL; DO IF start = 0 THEN WAIT ForTimeout; FOR index IN [start..maxSlots) DO IF slot[index] # emptySlot THEN { IF slot[index].used THEN slot[index].used _ FALSE ELSE { -- here's one that needs to be closed h _ slot[index].h; slot[index] _ emptySlot; RETURN; }; }; ENDLOOP; FOR index IN [0 .. maxSlots) DO IF slot[index] # emptySlot THEN EXIT; -- need to stick around REPEAT FINISHED => { -- nothing more to do h _ NIL; index _ maxSlots; haveSlotTimer _ FALSE; RETURN; }; ENDLOOP; start _ 0; ENDLOOP; }; }. FSRemoteFileImpl.mesa Copyright c 1984, 1985, 1986 by Xerox Corporation. All rights reserved. HGM, February 8, 1984 11:55:19 pm PST Bob Hagmann, August 2, 1985 4:38:26 pm PDT Doug Wyatt, November 27, 1984 3:53:09 pm PST -- added allDown test to Info Russ Atkinson (RRA) April 8, 1986 12:59:54 pm PST Tim Diebert: February 11, 1986 9:39:22 am PST Hal Murray, June 24, 1986 9:11:54 pm PDT Exported to FSRemoteFile [file: ROPE] RETURNS [answer: {do, skip, abort}, localStream: STREAM] [file: ROPE] RETURNS [continue: BOOL] [file: ROPE] RETURNS [continue: BOOL] This helpful procedure allows enumeration for either names or information. RRA: this allows us to abort during an enumeration, which is non-trivial RRA: certain STP servers, like Nebula, do not prepend "<>" if they do not support directories. Since FS demands this syntax, we should make sure that it is here. If we do not get a match then we yield nothing. If replicated, then there is also no reason to try other servers, since replication should not affect presence. Otherwise, we go on to the next server, provided that we have not yielded something from this server. If we have seen at least one file then we do NOT try other servers, which would royally screw up the enumeration. If the auxilliary servers do not work, we report the error from the first server only. So we remember the error code here. Any other errors during auxiliiary severs searches just go on to try the next server in the list (if any). If we continue here, we have enumerated the pattern all right, and have returned the connection. If we continue here, we have an error of some kind during the enumeration, so we report it. No such file was the response. This is a positive response that the named file was not present on the named server. If we are NOT assuming replicated files, then we go look for another server. If we wanted any date, and we are assuming replicated files, then we should just give up right now, since we will not find a match. If we wanted a specific date, then we may just have a bad version number hint, so we exit to do the search the expensive way. We got a response that the file was present. We were requested to find any date, so the one we got was OK. We were asked for a specific date, and we got it, so its all OK. We were asked for a specific date, and the file was there, but the date was wrong, which means that the version # hint was bogus. If we assume the files are replicated, we should exit, since the version hint is wrong on the replicated servers as well. Otherwise, we loop, hoping that another server will have the correct hint and date as well. The line of code following fails if there is no response to a single packet info check. Tim D. IF allDown THEN ReportError[noNameLookupResponse]; This call is for the general case. It is more expensive, of course, since we have to grab a connection for it. However, STPInfo can handle bogus version # hints. [file: ROPE] RETURNS [answer: {do, skip, abort}, localStream: STREAM] [file: ROPE] RETURNS [continue: BOOL] In this case we have a single-character day of month with no leading blank. We accept this number, as well as adjusting the indexing. Yetch, FTPServer on AltoGateway does this. Use local zone info. Internal procedures [file: ROPE] RETURNS [continue: BOOL] If version part was missing from client's file name, then Enumerate will produce all versions, but we only want the !H version Property stuff no directory, so turn off directory defaulting and Desired-Property properties Internal procedures STP connection cacheing need a new connection already had a connection Names with "." are GVNames (Grapevine names), so ask Grapevine to look them up If successful, use the connect as the server name for STP.Open Bob Hagmann April 29, 1985 9:12:49 am PDT changes to: DIRECTORY, GetConnection Bob Hagmann April 29, 1985 10:03:03 am PDT changes to: DIRECTORY, GetConnection, GetServerPupName, LookForConnection, FSRemoteFileImpl Bob Hagmann May 6, 1985 4:13:27 pm PDT changes to: InnerEnumerate Bob Hagmann August 2, 1985 3:25:38 pm PDT changes to: maxSlots, TimeOut, GetServerPupName, FindInGrapevineCache, AddToGrapevineCache, LookForConnection, ForTimeout, GrapevineCacheEntry, maxGVineCache, grapevineCachePut, grapevineCache, GetConnection Bob Hagmann August 2, 1985 4:37:37 pm PDT changes to: grapevineCachePut Κϋ– "cedar" style˜code2šœ™Jšœ Οmœ=™HIcode1™%L™*L™JIcode™1M™-M™(—šΟk ˜ Lšœ žœžœžœ!˜LšœžœK˜`Lšœ žœ%˜7Lšœ žœ˜Lšœ žœ ˜Lšœžœ˜(Lšžœžœžœ˜Lšœžœ2˜?Lšœžœ;žœ˜KLšœ žœžœž˜Lšžœžœψ˜Lšœžœ˜—šœžœž˜Lšžœ žœežœ˜ŽLšžœ ˜Lšœ˜L˜Lšžœžœ žœ˜Lšœžœžœ˜Lšžœžœžœ˜Lšœžœ$˜:Lšžœžœžœžœ˜Lšœ žœ˜#L˜—šœ™š Οnœžœžœžœžœ%˜dšœ žœ˜ Kšœžœžœ*žœ™EKšœžœ žœ˜(Kšœ žœ˜)šžœžœž˜Kšœ˜šžœ žœ!˜2Kšœ žœ-žœžœ˜KKšœ žœ˜Kšœ˜—Kšžœ˜—Kšœ˜—Kšœ žœžœ˜Kšœ žœ+˜>Kšœ žœ˜$Kšœ žœ˜š œ žœžœžœžœ˜/Kšœ žœ>˜KKšœžœ˜˜Kšžœžœ!˜.Kšœ!žœ˜(šžœ˜Kš œžœ žœžœžœ˜4—šžœžœ žœ˜šžœ žœ˜Kšœ!˜!Kšžœ˜Kšœ˜—šžœžœ žœ˜Kšœ˜Kšœ˜Kšžœžœ˜—Kšœ˜—K˜—Kšœ˜Kšœ˜—Kš žœžœžœžœžœ˜GKšœ9˜9Kšœ˜—šŸœžœžœžœ˜Lšœ˜Kšœžœžœ žœ™%Kšœžœ žœ˜(Kšœžœ˜3šœ žœ"žœ˜JKšžœžœ˜—Kšœ˜—Kšœ žœ˜&Kšœ*žœ˜1Kšœ˜—šŸœžœžœžœ˜Mšœ˜Kšœžœžœ žœ™%Kšœ žœ"žœžœ˜@Kšœ˜—Kšœ žœ˜&Kšœ*žœ˜0Kšœž˜—Kš Ÿ œžœžœžœžœ žœ ˜Tš Ÿœžœžœžœ"žœ˜]KšœJ™JKšœ žœ˜$Kšœ žœ˜Kšœ žœžœ˜šœ žœ˜#šœ˜KšœH™H—Kšœ žœ˜šžœžœžœ ˜BKšœ’™’—Kšœ˜K˜—šœ žœ žœ˜%Kšœžœ˜˜Kšžœžœ"˜/Kšœ0˜0Kšžœ"˜%K˜—Kšœ˜K˜—Kšœ žœžœžœ,˜DKšœ žœ,˜<š žœžœžœžœžœžœž˜Bšœ˜šœžœ ˜šžœžœž˜šœ˜Kšœ‡™‡šžœžœž˜Kšœžœžœ ˜'Kšžœžœ˜——šœ ˜ Kšœq™qKšœžœ˜—šœ˜Kšœ{™{Kšœžœ˜—šžœ˜ Kšœj™jKšžœ˜———Kšœ˜—Kšœ`™`Kšžœ˜Kšžœ˜—Kšœ[™[Kšžœžœ=˜YKšžœ˜Kšœ˜—šŸœžœžœžœžœžœžœ žœ˜wšŸ œžœ žœ˜.Kšœ9˜9Kšœ˜—Kšœ"˜"Kšœ žœžœžœ,˜DKšœ žœ,˜Kšœ˜—Kšœ ˜ K˜—š žœžœžœžœžœžœž˜Bšžœ˜šœžœ ˜Kšžœžœ˜)Kšžœ žœžœžœ˜Kšžœžœ žœžœ˜1Kšžœ˜—Kš œžœžœžœžœ žœžœ˜+—Kšžœ˜—Kšœ9˜9Kšœ˜——™Kš œžœžœžœžœžœ˜QKšœžœžœžœ˜&Kšœ-žœ žœ žœ˜Ršœ(˜(Kš œ žœ žœ žœžœžœ˜N—Kšœžœžœžœ /˜WšŸœžœžœ žœ˜Išžœžœ˜!šžœ˜KšœN™NKšžœ˜Kšžœ˜!Kšœ˜—šžœ˜Kšžœžœ˜Kšžœžœ žœ žœ˜NKšœ˜——Kšœ˜——™š Ÿ œžœ žœžœžœ˜4šžœ˜Kšžœžœ ˜Kšžœžœ"˜-—Kšœ˜—š Ÿœžœ žœžœžœ˜6šžœ˜Kšžœžœ2˜=Kšžœžœ ˜—Kšœ˜—š Ÿœžœžœ žœžœžœ˜WKšœžœ,˜7Kšœžœ˜Kšœžœ˜Kšœ˜š Ÿœžœ žœžœžœ˜HKšžœžœ>˜FK˜—šžœžœ˜Kšœ$ ˜6Kšœ˜—šžœ ž˜šœ+˜+Kšœ˜Kšœ˜Kšœ˜—šœ˜Kšœ˜Kšœ7˜7Kšœ˜—šœ˜Kšœ˜Kšœ*˜*Kšœ˜—šœ˜Kšœ˜Kšœ#˜#Kšœ˜—šœ˜Kšœ˜Kšœ(˜(Kšœ˜—šœ˜Kšœ˜Kšœ?˜?Kšœ˜—šœ˜Kšœ˜Kšœ ˜ Kšœ"˜"Kšœ˜—šœ˜Kšœ˜Kšœ.˜.Kšœ˜—šœ˜Kšœ˜Kšœ-˜-Kšœ˜—šœ˜Kšœ˜Kšœ'˜'Kšœ˜—šœ˜Kšœ@˜@—šœ˜Kšœ;˜;—šœ˜KšœM˜M—šœ˜KšœK˜K—šœ˜KšœK˜K—šœ˜KšœS˜S—šœ˜KšœG˜G—šœ˜Kšœ?˜?—šœ ˜ Kšœ5˜5—šœ˜Kšœ>˜>—Kšžœžœ˜—Kšœ7˜7Kšœ˜——™Kšœ žœ 8˜PKšœ 1˜OKšœ žœžœžœ˜.šœžœžœ˜Kšœžœ˜ Kšœ ˜ Kšœž˜ Kšœ˜—Kšœžœžœžœ˜$Kšœžœ  ˜,Kšœžœ žœ žœ˜7Kšœžœžœ˜Kšœ ž œ%˜:Kšœžœžœžœ˜Lšœžœžœ˜#Kšœžœžœ˜Kšœ˜K˜—Kšœžœ (˜EKšœžœ ˜0Kšœžœžœ˜Cš Ÿ œžœžœ žœžœ˜TKšœ˜šžœž˜ šžœ˜Kšœ™Kšœžœ˜Kšœžœ ˜Kšœ)˜)Kšžœ˜Kšœžœ#˜+Kšœ˜—šžœ˜Kšœ™šžœžœžœ ž˜Kšœ+˜+K˜—Kšœ˜——Kšœ+˜+Kšœ˜—š Πbnœž œ žœžœ žœ˜Jšžœžœžœ˜(K™NKšœ˜Kšœ˜Kšœžœ˜KšœN˜NKšžœžœžœ ˜%šœ=˜=Kšœ6žœ™>—šžœžœžœ˜+Kšœ4˜4Kšžœ ˜K˜—K˜—Kšžœ ˜K˜—šŸœžœžœžœžœ žœžœ ˜jšžœžœžœž˜(Kš žœ*žœžœžœžœ˜`Kšžœ˜—K˜—šŸœžœžœžœ ˜KKšœ.˜.Kšœ4˜4Kšžœ>žœ˜[K˜—š Ÿœžœžœ žœžœ˜CKšžœžœžœ˜šžœžœžœž˜#šžœžœ$žœžœ˜LKšœ˜Kšœ˜Kšœ˜Kšžœ˜Kšœ˜—Kšžœ˜—Kšœžœ˜Kšœ˜Kšœ˜—šŸœžœ žœ˜4Kš žœ žœžœžœžœžœ˜'š žœžœ žœžœž˜7Kšžœ žœ žœ˜&—Kšœ˜—š Ÿœžœžœ žœ žœžœ˜GKšžœžœžœ˜šžœžœžœž˜#šžœžœ˜Kšœžœ˜šžœžœžœžœ˜#Kšœžœ˜Kšœžœ˜!Kšœ˜—Kšž œ˜Kšœ˜—Kšžœ˜—Kšžœ˜Kšœ˜—šŸ œžœ˜Kšœžœ˜Kšœ ˜ šž˜Kšœ ˜ Kšžœžœžœžœ˜Kšžœ žœ žœ˜"Kšœ˜Kšžœ˜—Kšœ˜—š Ÿœžœžœ žœžœžœ˜[Kšžœžœžœ˜šž˜Kšžœ žœžœ ˜"šžœžœž˜!šžœžœ˜!šžœ˜Kšžœž˜šžœ %˜,Kšœ˜Kšœ˜Kšžœ˜Kšœ˜——Kšœ˜—Kšžœ˜—šžœžœž˜Kšžœžœžœ ˜=šžœžœ ˜*Kšœžœ˜K˜Kšœžœ˜Kšžœ˜Kšœ˜—Kšžœ˜—K˜ Kšžœ˜—Kšœ˜——Kšœ˜™)Mšœ Οr™$—™*Mšœ ’O™[—M™™&Mšœ ’™—™)Mšœ ’ÙϗM™™)Mšœ ’™—M™—…—Ky