-- FQImpl.Mesa, last edit February 8, 1983 4:31 pm DIRECTORY ConvertUnsafe: TYPE USING[ToRope], CWF: TYPE USING [SWF2, SWF3, SWF4], DateAndTimeUnsafe: TYPE USING [Parse, Unintelligible], FileLookup: TYPE USING[LookupFile, Result], FQ: TYPE USING[Result], LongString: TYPE USING[StringToDecimal], Rope: TYPE USING[ROPE], UnsafeSTP: TYPE USING [DesiredProperties, Enumerate, Error, FileInfo, GetFileInfo, Handle, NoteFileProcType, SetDesiredProperties], STPSubr: TYPE USING [Connect, ForceClosed, HandleSTPError], Subr: TYPE USING [EndsIn, Prefix, strcpy, TTYProcs]; FQImpl: PROGRAM IMPORTS ConvertUnsafe, CWF, DateAndTimeUnsafe, FileLookup, LongString, STP: UnsafeSTP, STPSubr, Subr EXPORTS FQ = { -- be careful about useRopes: ropes are not available for BringOver in the boot file -- if createtime ~= 0 then -- looks for a file with that create time -- uses version number as a hint -- result is foundCorrectVersion if the file is found -- foundWrongVersion if a file is found but not that one -- notFound if the file or server does not exist at all -- if createtime = 0 and wantExplicitVersion THEN -- looks for a file with that version number -- result is foundCorrectVersion if the file is found -- notFound if the file or server does not exist at all -- if createtime = 0 and NOT wantExplicitVersion THEN -- looks for any file of that name (!H) -- result is foundCorrectVersion if the file is found -- notFound if the file or server does not exist at all -- foundCorrectVersion returns correct version and time -- targetFileName has a full filename including version number -- foundWrongVersion returns highest version and time -- targetFileName has a full filename without version number -- notFound returns no version and no time -- targetFileName has a full filename without version number -- unless wantExplicitVersion = TRUE, in which case it will have one FileQuery: PUBLIC PROC [host, directory, shortname: LONG STRING, version: CARDINAL, createtime: LONG CARDINAL, wantExplicitVersion: BOOL, h: Subr.TTYProcs, targetFileName: LONG STRING, useRopes: BOOL] RETURNS [fres: FQ.Result, remoteVersion: CARDINAL, remoteCreateTime, remoteByteLength: LONG CARDINAL] = { sfn: STRING _ [100]; result: FileLookup.Result; prefix: STRING _ [100]; tryBangH: BOOL _ FALSE; server, file: Rope.ROPE; CWF.SWF3[prefix, "[%s]<%s>%s"L, host, directory, shortname]; { IF NOT useRopes THEN GOTO skipThis; IF version > 0 AND (createtime ~= 0 OR wantExplicitVersion) THEN CWF.SWF3[sfn, "<%s>%s!%u"L, directory, shortname, @version] ELSE { tryBangH _ TRUE; CWF.SWF2[sfn, "<%s>%s!H"L, directory, shortname]; }; server _ ConvertUnsafe.ToRope[host]; file _ ConvertUnsafe.ToRope[sfn]; [result: result, version: remoteVersion, create: remoteCreateTime, count: remoteByteLength] _ FileLookup.LookupFile[server: server, file: file]; server _ file _ NIL; -- free as soon as possible SELECT result FROM ok => { IF createtime = 0 OR createtime = remoteCreateTime THEN { -- the file found has the right create date, or there was no create time but we found it CWF.SWF2[targetFileName, "%s!%d"L, prefix, @remoteVersion]; RETURN[foundCorrectVersion, remoteVersion, remoteCreateTime, remoteByteLength]; }; -- otherwise do the FTP search }; noSuchName => { -- this file does not exist IF tryBangH OR wantExplicitVersion THEN { -- we probed with !H, there is no file -- we probed with explicit version, there is no file FillInErrorTargetFile[targetFileName, prefix, wantExplicitVersion, version]; RETURN[notFound, 0, 0, 0]; }; -- if we probed with a version hint, do the FTP search }; noResponse, noSuchPort =>{ -- no file lookup server or it does not reply, do the FTP search }; ENDCASE => ERROR; EXITS skipThis => NULL; }; [fres, remoteVersion, remoteCreateTime, remoteByteLength] _ EnumerateWithSTP[host, directory, shortname, version, createtime, wantExplicitVersion, h, prefix, targetFileName]; }; EnumerateWithSTP: PROC[host, directory, shortname: LONG STRING, version: CARDINAL, createtime: LONG CARDINAL, wantExplicitVersion: BOOL, h: Subr.TTYProcs, prefix, targetFileName: LONG STRING] RETURNS[fres: FQ.Result, remoteVersion: CARDINAL, remoteCreateTime, remoteByteLength: LONG CARDINAL] = { stphandle: STP.Handle; found: BOOL; highdate: LONG CARDINAL _ 0; highversion: CARDINAL _ 0; foundFileName: STRING _ [100]; desiredProperties: STP.DesiredProperties _ ALL[FALSE]; -- NOTE: Desired properties have been set -- only called once unless createtime ~= 0 -- stphandle is passed in -- NoteFileProcType: TYPE = PROC [file: STRING] RETURNS [continue: Continue]; EnumProcessFile: STP.NoteFileProcType = { vers: CARDINAL _ 0; date: LONG CARDINAL; info: STP.FileInfo _ STP.GetFileInfo[stphandle]; continue _ yes; IF info.version ~= NIL AND info.version.length > 0 THEN vers _ LongString.StringToDecimal[info.version]; date _ DateAndTimeUnsafe.Parse[info.create ! DateAndTimeUnsafe.Unintelligible => { -- this can actually happen e.g. time = 0 (or 1937) date _ 0; CONTINUE; }; ].dt; IF vers > highversion THEN { highversion _ vers; highdate _ date; }; IF found THEN RETURN[yes]; -- if the date in the df file agrees with date on remote server IF createtime > 0 AND createtime = date THEN { remoteVersion _ vers; remoteCreateTime _ date; remoteByteLength _ info.size; fres _ foundCorrectVersion; found _ TRUE; Subr.strcpy[foundFileName, file]; RETURN; } -- if there is no date in the df file ELSE IF createtime = 0 THEN { remoteVersion _ vers; remoteCreateTime _ date; remoteByteLength _ info.size; found _ TRUE; Subr.strcpy[foundFileName, file]; RETURN; }; -- if we were given a date but it did not agree, keep looking }; { smashstp: STP.Handle _ NIL; vstring: STRING _ IF Subr.Prefix[host, "maxc"L] THEN ";"L ELSE "!"L; sfn: STRING _ [100]; found _ FALSE; remoteCreateTime _ remoteByteLength _ remoteVersion _ 0; IF version > 0 AND createtime = 0 AND wantExplicitVersion THEN CWF.SWF4[sfn, "<%s>%s%s%u"L, directory, shortname, vstring, @version] ELSE IF createtime = 0 THEN -- take highest version CWF.SWF3[sfn, "<%s>%s%sH"L, directory, shortname, vstring] ELSE -- search for all CWF.SWF3[sfn, "<%s>%s%s**"L, directory, shortname, vstring]; desiredProperties[directory] _ TRUE; desiredProperties[nameBody] _ TRUE; desiredProperties[version] _ TRUE; desiredProperties[createDate] _ TRUE; desiredProperties[size] _ TRUE; DO stphandle _ STPSubr.Connect[host: host, onlyOne: TRUE, h: h]; STP.SetDesiredProperties[stphandle, desiredProperties]; STP.Enumerate[stphandle, sfn, EnumProcessFile ! STP.Error => IF code = noSuchFile OR code = illegalFileName THEN { GOTO notFound } ELSE IF code = connectionClosed THEN { -- CWF.WF1["Connection to %s timed out.\n"L, df.host]; smashstp _ stphandle; CONTINUE; } ELSE IF STPSubr.HandleSTPError[stphandle, code, error, h] THEN RETRY ]; IF smashstp ~= NIL THEN smashstp _ STPSubr.ForceClosed[smashstp] ELSE EXIT; ENDLOOP; EXITS notFound => { FillInErrorTargetFile[targetFileName, prefix, wantExplicitVersion, version]; RETURN[notFound, 0, 0, 0]; -- no such file, did not enumerate }; }; IF found THEN { CWF.SWF2[targetFileName, "[%s]%s"L, host, foundFileName]; RETURN[foundCorrectVersion, remoteVersion, remoteCreateTime, remoteByteLength]; }; FillInErrorTargetFile[targetFileName, prefix, wantExplicitVersion, version]; RETURN[foundWrongVersion, highversion, highdate, 0]; -- enumerated but didn't find it }; FillInErrorTargetFile: PROC[targetFileName, prefix: LONG STRING, wantExplicitVersion: BOOL, version: CARDINAL] = { IF wantExplicitVersion THEN CWF.SWF2[targetFileName, "%s!%d"L, prefix, @version] ELSE Subr.strcpy[targetFileName, prefix]; }; -- just check the highest version -- if createtime > 0 then looks for !h in that version -- foundCorrectVersion if found -- foundWrongVersion if not found in that date -- notFound if not there at all -- if createtime = 0 then look for any !h -- foundCorrectVersion if found -- notFound if not there at all -- foundCorrectVersion returns correct version and time -- foundWrongVersion returns highest version and time -- notFound returns no version and no time FileQueryBangH: PUBLIC PROC [host, directory, shortname: LONG STRING, createtime: LONG CARDINAL, h: Subr.TTYProcs, useRopes: BOOL] RETURNS [fres: FQ.Result, remoteVersion: CARDINAL, remoteCreateTime, remoteByteLength: LONG CARDINAL] = { sfn: STRING _ [100]; result: FileLookup.Result; IF NOT useRopes THEN { [fres, remoteVersion, remoteCreateTime, remoteByteLength] _ EnumerateWithSTPBangH[host, directory, shortname, createtime, h]; RETURN; -- can't use FileLookup because of Ropes }; CWF.SWF2[sfn, "<%s>%s!H"L, directory, shortname]; [result: result, version: remoteVersion, create: remoteCreateTime, count: remoteByteLength] _ FileLookup.LookupFile[server: ConvertUnsafe.ToRope[host], file: ConvertUnsafe.ToRope[sfn]]; SELECT result FROM ok => { IF createtime ~= 0 AND createtime = remoteCreateTime THEN { -- the file found has the right create date RETURN[foundCorrectVersion, remoteVersion, remoteCreateTime, remoteByteLength]; } ELSE IF createtime = 0 THEN { -- there was no create time but we found it RETURN[foundCorrectVersion, remoteVersion, remoteCreateTime, remoteByteLength]; }; -- otherwise the file we found was the wrong version RETURN[foundWrongVersion, remoteVersion, remoteCreateTime, remoteByteLength]; }; noSuchName => { -- this file does not exist RETURN[notFound, 0, 0, 0]; }; noResponse, noSuchPort =>{ -- no file lookup server or it does not reply, do the FTP search [fres, remoteVersion, remoteCreateTime, remoteByteLength] _ EnumerateWithSTPBangH[host, directory, shortname, createtime, h]; }; ENDCASE => ERROR; }; EnumerateWithSTPBangH: PROC[host, directory, shortname: LONG STRING, createtime: LONG CARDINAL, h: Subr.TTYProcs] RETURNS[fres: FQ.Result, remoteVersion: CARDINAL, remoteCreateTime, remoteByteLength: LONG CARDINAL] = { stphandle: STP.Handle; desiredProperties: STP.DesiredProperties _ ALL[FALSE]; -- NOTE: Desired properties have been set -- only called once -- stphandle is passed in -- NoteFileProcType: TYPE = PROC [file: STRING] RETURNS [continue: Continue]; EnumProcessFile: STP.NoteFileProcType = { vers: CARDINAL _ 0; date: LONG CARDINAL; info: STP.FileInfo _ STP.GetFileInfo[stphandle]; continue _ yes; IF info.version ~= NIL AND info.version.length > 0 THEN vers _ LongString.StringToDecimal[info.version]; date _ DateAndTimeUnsafe.Parse[info.create ! DateAndTimeUnsafe.Unintelligible => { -- this can actually happen e.g. time = 0 (or 1937) date _ 0; CONTINUE; }; ].dt; remoteCreateTime _ date; remoteByteLength _ info.size; remoteVersion _ vers; -- if the date in the df file agrees with date on remote server IF createtime > 0 AND createtime = date THEN fres _ foundCorrectVersion ELSE IF createtime = 0 THEN -- if there is no date in the df file fres _ foundCorrectVersion ELSE -- if we were given a date but it did not agree fres _ foundWrongVersion; }; { smashstp: STP.Handle _ NIL; vstring: STRING _ IF Subr.Prefix[host, "maxc"L] THEN ";"L ELSE "!"L; sfn: STRING _ [100]; fres _ notFound; remoteCreateTime _ remoteByteLength _ remoteVersion _ 0; CWF.SWF3[sfn, "<%s>%s%sH"L, directory, shortname, vstring]; desiredProperties[version] _ TRUE; desiredProperties[createDate] _ TRUE; desiredProperties[size] _ TRUE; DO stphandle _ STPSubr.Connect[host: host, onlyOne: TRUE, h: h]; STP.SetDesiredProperties[stphandle, desiredProperties]; STP.Enumerate[stphandle, sfn, EnumProcessFile ! STP.Error => IF code = noSuchFile THEN GOTO notFound ELSE IF code = illegalFileName THEN { -- see if the file name works without the !* or ;* IF Subr.EndsIn[sfn, "!H"L] THEN { sfn.length _ sfn.length - 2; LOOP; -- try again without it (this is for Twinkle) } ELSE CONTINUE -- second try failed } ELSE IF code = connectionClosed THEN { -- CWF.WF1["Connection to %s timed out.\n"L, df.host]; smashstp _ stphandle; CONTINUE; } ELSE IF STPSubr.HandleSTPError[stphandle, code, error, h] THEN RETRY ]; IF smashstp ~= NIL THEN smashstp _ STPSubr.ForceClosed[smashstp] ELSE EXIT; ENDLOOP; EXITS notFound => RETURN[notFound, 0, 0, 0]; -- no such file, did not enumerate }; RETURN[fres, remoteVersion, remoteCreateTime, remoteByteLength]; }; }.