<> <> <> 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 [AllocateString, EndsIn, FreeString, Prefix, strcpy, TTYProcs]; FQImpl: PROGRAM IMPORTS ConvertUnsafe, CWF, DateAndTimeUnsafe, FileLookup, LongString, STP: UnsafeSTP, STPSubr, Subr EXPORTS FQ = { 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] = { <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> <> result: FileLookup.Result; tryBangH: BOOL _ FALSE; server, file: Rope.ROPE; sfn: LONG STRING _ Subr.AllocateString[100]; prefix: LONG STRING _ Subr.AllocateString[100]; Cleanup: PROC = {Subr.FreeString[sfn]; Subr.FreeString[prefix]}; {ENABLE UNWIND => Cleanup[]; CWF.SWF3[prefix, "[%s]<%s>%s"L, host, directory, shortname]; {-- begin block for exit skipThis 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 { <> CWF.SWF2[targetFileName, "%s!%d"L, prefix, @remoteVersion]; Cleanup[]; RETURN[ foundCorrectVersion, remoteVersion, remoteCreateTime, remoteByteLength]; }; <> }; noSuchName => { <> IF tryBangH OR wantExplicitVersion THEN { <> <> FillInErrorTargetFile[targetFileName, prefix, wantExplicitVersion, version]; Cleanup[]; RETURN[notFound, 0, 0, 0]; }; <> }; noResponse, noSuchPort =>{ <> }; ENDCASE => ERROR; EXITS skipThis => NULL; }; [fres, remoteVersion, remoteCreateTime, remoteByteLength] _ EnumerateWithSTP[host, directory, shortname, version, createtime, wantExplicitVersion, h, prefix, targetFileName]; }; -- of ENABLE UNWIND Cleanup[]; }; 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; desiredProperties: STP.DesiredProperties _ ALL[FALSE]; smashstp: STP.Handle _ NIL; vstring: STRING _ IF Subr.Prefix[host, "maxc"L] THEN ";"L ELSE "!"L; sfn: LONG STRING _ Subr.AllocateString[100]; foundFileName: LONG STRING _ Subr.AllocateString[100]; CleanupStrings: PROC = {Subr.FreeString[sfn]; Subr.FreeString[foundFileName]}; 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 createtime > 0 AND createtime = date THEN { remoteVersion _ vers; remoteCreateTime _ date; remoteByteLength _ info.size; fres _ foundCorrectVersion; found _ TRUE; Subr.strcpy[foundFileName, file]; RETURN; } <> ELSE IF createtime = 0 THEN { remoteVersion _ vers; remoteCreateTime _ date; remoteByteLength _ info.size; found _ TRUE; Subr.strcpy[foundFileName, file]; RETURN; }; <> }; {ENABLE UNWIND => CleanupStrings[]; 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 => SELECT code FROM noSuchFile, illegalFileName => GOTO notFound; connectionClosed => {smashstp _ stphandle; CONTINUE}; ENDCASE => IF STPSubr.HandleSTPError[stphandle, code, error, h] THEN RETRY ]; IF smashstp ~= NIL THEN smashstp _ STPSubr.ForceClosed[smashstp] ELSE EXIT; ENDLOOP; IF found THEN { CWF.SWF2[targetFileName, "[%s]%s"L, host, foundFileName]; CleanupStrings[]; RETURN[foundCorrectVersion, remoteVersion, remoteCreateTime, remoteByteLength]; }; FillInErrorTargetFile[targetFileName, prefix, wantExplicitVersion, version]; CleanupStrings[]; RETURN[foundWrongVersion, highversion, highdate, 0]; -- enumerated but didn't find it EXITS notFound => { FillInErrorTargetFile[targetFileName, prefix, wantExplicitVersion, version]; CleanupStrings[]; RETURN[notFound, 0, 0, 0]; -- no such file, did not enumerate }; }; -- of ENABLE UNWIND }; 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]; }; 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] = { <> < 0 then looks for !h in that version>> <> <> <> <> <> <> <> <> <> sfn: LONG STRING _ NIL; 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 }; sfn _ Subr.AllocateString[100]; {ENABLE UNWIND => {Subr.FreeString[sfn]}; 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 => { fres _ foundWrongVersion; IF (createtime ~= 0 AND createtime = remoteCreateTime) OR createtime = 0 THEN <> fres _ foundCorrectVersion; GO TO return; }; noSuchName => { <> fres _ notFound; remoteVersion _ remoteCreateTime _ remoteByteLength _ 0; GO TO return; }; noResponse, noSuchPort =>{ <> [fres, remoteVersion, remoteCreateTime, remoteByteLength] _ EnumerateWithSTPBangH[host, directory, shortname, createtime, h]; }; ENDCASE => ERROR; EXITS return => {}; }; -- of ENABLE UNWIND Subr.FreeString[sfn]; }; 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]; smashstp: STP.Handle _ NIL; vstring: STRING _ [1]; sfn: LONG STRING _ Subr.AllocateString[100]; 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 => { <> date _ 0; CONTINUE}; ].dt; remoteCreateTime _ date; remoteByteLength _ info.size; remoteVersion _ vers; <> 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; }; vstring.length _ 1; vstring[0] _ IF Subr.Prefix[host, "maxc"L] THEN '; ELSE '!; {ENABLE UNWIND => {Subr.FreeString[sfn]}; 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 => SELECT code FROM noSuchFile => GOTO notFound; illegalFileName => { <> 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 }; connectionClosed => { smashstp _ stphandle; CONTINUE; } ENDCASE => IF STPSubr.HandleSTPError[stphandle, code, error, h] THEN RETRY ]; IF smashstp ~= NIL THEN smashstp _ STPSubr.ForceClosed[smashstp] ELSE EXIT; ENDLOOP; EXITS notFound => { remoteVersion _ remoteCreateTime _ remoteByteLength _ 0; fres _ notFound; -- no such file, did not enumerate }; }; -- of ENABLE UNWIND Subr.FreeString[sfn]; }; }.