-- 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];
};
}.