-- BringOverImpl.Mesa -- last edit February 4, 1983 2:42 pm -- last edit May 22, 1983 3:49 pm, Russ Atkinson -- changed short STRING into LONG STRING -- Pilot 6.0/ Mesa 7.0 -- Switch Meaning -- /a do NOT confirm any transfers!!! -- /o file retrieve only file "file" -- /p retrieve only the Public files -- /v verify mode - simply confirm that df file has no errors -- (e.g. file not found, etc.) Does not transfer anything. -- default is to retrieve both Public and non-Public files -- note that calling procedures in BringOverInterface will cause a start trap, -- which will run Exec.AddCommand [BringOver] -- this BringOver.~ can be run without any problems DIRECTORY BareBringOver: TYPE USING[State], CIFS: TYPE USING[Error], CWF: TYPE USING [SWF1, SWF2, SWF3, SWF4, WF0, WF1, WF2, WF3, WF4, WFC, WFCR], DFSubr: TYPE USING [AllocateDFSeq, CopyUsing, Criterion, DF, DFEntryProcType, DFSeq, FreeDFSeq, FreeUsingSeq, InterestingNestedDFProcType, IntersectUsing, NextDF, ParseStream, ReadInDir, UsingEmpty, UsingSeq, WriteOut], Directory: TYPE USING[DeleteFile, Error, Lookup, ignore, Rename, RemoveFile], File: TYPE USING[Capability, nullCapability, Unknown], FileStream: TYPE USING [SetLeaderPropertiesForCapability], FQ: TYPE USING[FileQuery, FileQueryBangH, Result], IO: TYPE USING[UserAbort], KernelFile: TYPE USING[MakeTemporary], LongString: TYPE USING [EquivalentString], Runtime: TYPE USING[GetBcdTime, GetBuildTime], Space: TYPE USING [Handle], UnsafeSTP: TYPE USING [FileInfo, GetFileInfo, Handle], STPSubr: TYPE USING [EnumerateForRetrieve, RetrieveProcType, WriteStreamToDisk], Stream: TYPE USING [Delete, Handle], Subr: TYPE USING [AbortMyself, AllocateString, CheckForModify, CopyString, debugflg, EndsIn, FreeString, GetCreateDateWithSpace, NewStream, numberofleaders, Prefix, Read, SetRemoteFilenameProp, strcpy, TTYProcs, Write]; -- should really be a monitor on BringOverState BringOverImpl: PROGRAM IMPORTS CIFS, CWF, DFSubr, Directory, File, FileStream, FQ, KernelFile, IO, LongString, Runtime, STP: UnsafeSTP, STPSubr, Stream, Subr EXPORTS BareBringOver = { MAXFILES: CARDINAL = 500; -- no mds usage -- exported to BareBringOver CheckThisFile: PUBLIC SIGNAL[checkfilename: LONG STRING] = CODE; RecursiveLoop: PUBLIC SIGNAL[loopfilename: LONG STRING] = CODE; BringOverDF: PUBLIC PROC[dfseq: DFSubr.DFSeq, state: BareBringOver.State, ldspace: Space.Handle, h: Subr.TTYProcs] = { nretrieved, nRenamed: CARDINAL _ 0; NestedDFFileProc: DFSubr.InterestingNestedDFProcType = { sh: Stream.Handle _ NIL; dfseq, dfseqInner: DFSubr.DFSeq _ NIL; newUsing: DFSubr.UsingSeq _ NIL; { ENABLE UNWIND => { IF sh ~= NIL THEN Stream.Delete[sh]; sh _ NIL; IF newUsing ~= NIL AND innerUsingSeq ~= NIL THEN DFSubr.FreeUsingSeq[newUsing]; DFSubr.FreeDFSeq[@dfseqInner]; DFSubr.FreeDFSeq[@dfseq]; }; -- don't continue if q typed, df not on disk, -- (wrong version on disk and there is a remote host) IF state.quit THEN RETURN; IF dfEntry ~= NIL AND (NOT dfEntry.presentonlocaldisk OR (dfEntry.need AND NOT EmptyString[dfEntry.host])) THEN { CWF.WF1["Warning - unable to analyze contents of %s\n"L, dfEntry.shortname]; RETURN; }; -- using list processing IF driverUsingSeq ~= NIL AND innerUsingSeq ~= NIL THEN { -- must do intersection newUsing _ DFSubr.IntersectUsing[driverUsingSeq, innerUsingSeq]; IF newUsing = NIL THEN RETURN; -- no intersection -- newUsing should be freed } ELSE IF innerUsingSeq ~= NIL THEN { -- driverUsingSeq = NIL -- this is an imported DF file with a using list newUsing _ DFSubr.CopyUsing[innerUsingSeq]; -- allows first level to be non-public as well publicOnly _ FALSE; -- newUsing should be freed } ELSE IF driverUsingSeq ~= NIL THEN { -- innerUsingSeq = NIL IF DFSubr.UsingEmpty[driverUsingSeq] THEN RETURN; -- already empty newUsing _ driverUsingSeq; -- newUsing should NOT be freed }; SIGNAL CheckThisFile[shortname]; CWF.WF1["\nBringOver of %s"L, shortname]; IF newUsing ~= NIL THEN CWF.WF0[" (with Using list)"L] ELSE IF publicOnly THEN CWF.WF0[" (Exports only)"L] ELSE IF state.verify THEN CWF.WF0[" (Verify)"L]; CWF.WFCR[]; IF dfEntry = NIL THEN { -- this calls RetrieveDF for those DF files that appear in no DFseq dfseqInner _ BuildFakeDFSeq[host, directory, shortname, version, createtime, criterion]; dfEntry _ @dfseqInner[0]; DFEntryProc[dfEntry]; }; -- don't continue if q typed, df not on disk, wrong version on disk IF state.quit THEN RETURN; IF dfEntry ~= NIL AND (NOT dfEntry.presentonlocaldisk OR (dfEntry.need AND NOT EmptyString[dfEntry.host])) THEN { CWF.WF1["Warning - unable to analyze contents of %s\n"L, dfEntry.shortname]; RETURN; }; sh _ Subr.NewStream[dfEntry.shortname, Subr.Read]; dfseq _ DFSubr.AllocateDFSeq[maxEntries: MAXFILES, zoneType: shared]; DFSubr.ParseStream[sh: sh, dfseq: dfseq, dffilename: shortname, using: newUsing, noremoteerrors: FALSE, forceReadonly: entryIsReadonly, omitNonPublic: publicOnly, h: h, interestingNestedDF: IF state.verify THEN NIL ELSE NestedDFFileProc, dfEntryProc: IF state.verify THEN NIL ELSE DFEntryProc, nLevel: nLevel + 1, ancestor: IF ancestor = NIL THEN shortname ELSE ancestor ! CheckThisFile => IF LongString.EquivalentString[shortname, checkfilename] THEN ERROR RecursiveLoop[checkfilename] ]; Stream.Delete[sh]; sh _ NIL; IF state.verify THEN { ProcessVerify[dfseq, h, ldspace, state]; GOTO leave; }; IF dfseq.trailingcomment ~= NIL AND newUsing = NIL THEN { -- only print if non-blank FOR i: CARDINAL IN [0 .. dfseq.trailingcomment.length) DO IF dfseq.trailingcomment[i] ~= '\n AND dfseq.trailingcomment[i] ~= ' THEN { CWF.WF1["%s"L, dfseq.trailingcomment]; EXIT; }; ENDLOOP; }; -- now append in command stream any @file.cm files in the DF file -- IF NOT state.quit AND newUsing = NIL THEN { -- stemp: STRING _ [100]; -- FOR i: CARDINAL IN [0 .. dfseq.size) DO -- df: DFSubr.DF _ @dfseq[i]; -- IF NOT df.atsign OR df.need OR NOT df.presentonlocaldisk THEN LOOP; -- IF NOT Subr.EndsIn[df.shortname, "cm"L] THEN LOOP; -- CWF.SWF1[stemp, "@%s\n"L, df.shortname]; -- Exec.AppendCommands[stemp]; -- ENDLOOP; -- }; IF Subr.debugflg THEN CWF.WF2["%u leaders read, dfseq.size = %u.\n"L, @Subr.numberofleaders, @dfseq.size]; IF newUsing ~= NIL AND innerUsingSeq ~= NIL THEN { FreeUsingSeqWithError[newUsing]; }; EXITS leave => NULL; }; CWF.WF1["End of BringOver of %s\n"L, shortname]; DFSubr.FreeDFSeq[@dfseqInner]; DFSubr.FreeDFSeq[@dfseq]; }; -- derived files: .bcd, .signals, .boot, .press -- source files are anything else DFEntryProc: DFSubr.DFEntryProcType = { broughtover, renamed: BOOL; IF state.quit THEN RETURN; -- called for every entry in the sequence, including @ df files -- these @ df files cause NestedDFFileProc to be called ALSO IF state.justReadOnlys AND NOT dfEntry.readonly AND NOT dfEntry.atsign THEN RETURN; IF state.justNonReadOnlys AND dfEntry.readonly THEN RETURN; IF state.justSources AND (Subr.EndsIn[dfEntry.shortname, ".Bcd"L] OR Subr.EndsIn[dfEntry.shortname, ".Signals"L] OR Subr.EndsIn[dfEntry.shortname, ".Boot"L] OR Subr.EndsIn[dfEntry.shortname, ".Press"L]) THEN RETURN; IF state.justObjects AND NOT dfEntry.atsign AND NOT (Subr.EndsIn[dfEntry.shortname, ".Bcd"L] OR Subr.EndsIn[dfEntry.shortname, ".Signals"L] OR Subr.EndsIn[dfEntry.shortname, ".Boot"L] OR Subr.EndsIn[dfEntry.shortname, ".Press"L]) THEN RETURN; [broughtover, renamed] _ RetrieveDF[dfEntry, ldspace, h, state]; IF broughtover THEN nretrieved _ nretrieved + 1; IF renamed THEN nRenamed _ nRenamed + 1; }; { df: DFSubr.DF; -- the main body FOR i: CARDINAL IN [0 .. dfseq.size) DO df _ @dfseq[i]; DFEntryProc[df]; IF df.presentonlocaldisk AND df.atsign THEN { NestedDFFileProc[host: df.host, directory: df.directory, shortname: df.shortname, ancestor: NIL, immediateParent: NIL, version: df.version, nLevel: 0, createtime: 0, driverUsingSeq: df.using, innerUsingSeq: NIL, dfEntry: df, entryIsReadonly: FALSE, publicOnly: state.publicOnly, criterion: none]; IF df.using ~= NIL THEN FreeUsingSeqWithError[df.using]; df.using _ NIL; }; ENDLOOP; IF nretrieved > 0 THEN { CWF.WF1["%u files retrieved.*n"L, @nretrieved]; } ELSE CWF.WF0["No files retrieved.*n"L]; IF nRenamed > 0 THEN CWF.WF1["%u files had '$$' appended to their names.\n"L, @nRenamed]; }}; EmptyString: PROC[s: LONG STRING] RETURNS[empty: BOOL] = { RETURN[s = NIL OR s.length = 0]; }; FreeUsingSeqWithError: PUBLIC PROC[newUsing: DFSubr.UsingSeq] = { IF NOT DFSubr.UsingEmpty[newUsing] THEN { CWF.WF0["Error - cannot find "L]; FOR i: CARDINAL IN [0 .. newUsing.size) DO IF newUsing[i] = NIL THEN LOOP; CWF.WF1["%s "L, newUsing[i]]; ENDLOOP; CWF.WF0["in any nested DF file.\n"L]; }; DFSubr.FreeUsingSeq[newUsing]; }; BuildFakeDFSeq: PROC[host, directory, shortname: LONG STRING, version: CARDINAL, createtime: LONG CARDINAL, criterion: DFSubr.Criterion] RETURNS[dfseq: DFSubr.DFSeq] = { df: DFSubr.DF; dfseq _ DFSubr.AllocateDFSeq[maxEntries: 1, zoneType: shared]; df _ DFSubr.NextDF[dfseq]; df.host _ IF host.length = 0 THEN NIL ELSE Subr.CopyString[host, dfseq.dfzone]; df.directory _ IF directory.length = 0 THEN NIL ELSE Subr.CopyString[directory, dfseq.dfzone]; df.shortname _ Subr.CopyString[shortname, dfseq.dfzone]; df.version _ version; df.createtime _ createtime; df.criterion _ criterion; df.atsign _ TRUE; }; -- df.need will be TRUE if the file is not found on the remote server or the user -- declines to bring it over RetrieveDF: PROC[df: DFSubr.DF, ldspace: Space.Handle, h: Subr.TTYProcs, state: BareBringOver.State] RETURNS[broughtover, renamed: BOOL] = { refused: BOOL _ FALSE; localCreateTime: LONG CARDINAL _ 0; remoteCreateTime: LONG CARDINAL _ 0; remoteVersion: CARDINAL; fres: FQ.Result; targetFileName: LONG STRING _ Subr.AllocateString[125]; sfn: LONG STRING _ Subr.AllocateString[100]; {ENABLE { File.Unknown => { CWF.WF1["ERROR File.Unknown - problem reading %s,\n"L, df.shortname]; GOTO out; }; UNWIND => {Subr.FreeString[targetFileName]; Subr.FreeString[sfn]}; }; broughtover _ renamed _ FALSE; df.presentonlocaldisk _ TRUE; df.cap _ Directory.Lookup[fileName: df.shortname, permissions: Directory.ignore ! Directory.Error => { df.presentonlocaldisk _ FALSE; IF EmptyString[df.host] THEN CWF.WF1["Error - cannot open %s.\n"L, df.shortname]; CONTINUE; }]; IF state.updateOnly AND NOT df.atsign AND NOT df.presentonlocaldisk THEN RETURN; -- not on local disk IF state.forceRetrieval THEN df.need _ TRUE ELSE [df.need, localCreateTime] _ ComputeNeed[df, ldspace]; IF NOT df.need OR EmptyString[df.host] THEN GO TO out; [fres: fres, remoteVersion: remoteVersion, remoteCreateTime: remoteCreateTime] _ FQ.FileQuery[df.host, df.directory, df.shortname, df.version, df.createtime, df.criterion = none AND df.createtime = 0, h, targetFileName, state.useCIFS]; SELECT fres FROM foundCorrectVersion => { CWF.SWF4[sfn, "<%s>%s%s%u"L, df.directory, df.shortname, IF Subr.Prefix[df.host, "maxc"L] THEN ";"L ELSE "!"L, @remoteVersion]; [broughtover, refused, renamed] _ RetrieveIfWanted[df, sfn, remoteCreateTime, localCreateTime, h, ldspace, state]; df.need _ refused; }; foundWrongVersion => { CWF.WF2["\nError - %s (%lt) not available -- will offer the latest."L, df.shortname, @df.createtime]; CWF.SWF3[sfn, "<%s>%s%sH"L, df.directory, df.shortname, IF Subr.Prefix[df.host, "maxc"L] THEN ";"L ELSE "!"L]; [broughtover, refused, renamed] _ RetrieveIfWanted[df, sfn, remoteCreateTime, localCreateTime, h, ldspace, state]; df.need _ refused; }; notFound => CWF.WF1["Error - %s: file not found.\n"L, targetFileName]; ENDCASE => ERROR; EXITS out => NULL}; -- of ENABLE UNWIND Subr.FreeString[targetFileName]; Subr.FreeString[sfn]; }; RetrieveIfWanted: PROC[df: DFSubr.DF, remoteName: LONG STRING, remoteCreateTime, localCreateTime: LONG CARDINAL, h: Subr.TTYProcs, ldspace: Space.Handle, state: BareBringOver.State] RETURNS[broughtover, refused, renamed: BOOL] = { stemp: LONG STRING _ Subr.AllocateString[100]; fullname: LONG STRING _ Subr.AllocateString[125]; {ENABLE UNWIND => {Subr.FreeString[stemp]; Subr.FreeString[fullname]}; -- this is only called once OneFile: STPSubr.RetrieveProcType = { skipRest _ TRUE; renamed _ ProceedWithBringOver[df, STP.GetFileInfo[stp], h, remoteCreateTime, localCreateTime, remoteStream, fullname, state]; broughtover _ TRUE; }; broughtover _ refused _ renamed _ FALSE; IF df.presentonlocaldisk THEN { IF localCreateTime = 0 THEN [create: localCreateTime] _ Subr.GetCreateDateWithSpace[df.cap, ldspace]; CWF.SWF1[stemp, "%lt"L, @localCreateTime] } ELSE { Subr.strcpy[stemp, "New"L]; localCreateTime _ 0; }; CWF.SWF2[fullname, "[%s]%s"L, df.host, remoteName]; CWF.WF2["\n%s (%lt)\n"L, fullname, @remoteCreateTime]; CWF.WF2[" to local file %s (%s)"L, df.shortname, stemp]; IF h.in.UserAbort[] THEN SIGNAL Subr.AbortMyself; IF NOT state.forceRetrieval AND ((df.criterion = update AND localCreateTime >= remoteCreateTime) -- not newer version OR localCreateTime = remoteCreateTime -- already here OR (df.newOnly AND df.presentonlocaldisk)) THEN { -- new only, already here CWF.WF0[" ... not retrieved.\n"L]; GO TO return; }; IF df.createtime > 0 AND remoteCreateTime ~= df.createtime THEN CWF.WF1["\n Error - you wanted (%lt) Retrieve it?"L, @df.createtime]; IF state.mustconfirm THEN { ch: CHAR; ch _ h.Confirm[h.in, h.out, h.data, " ", 'y]; IF ch = 'q OR ch = 'Q THEN { state.quit _ TRUE; GO TO return; }; IF ch = 'a THEN state.mustconfirm _ FALSE ELSE IF ch ~= 'y THEN { refused _ TRUE; GO TO return; }; }; -- this will retrieve it STPSubr.EnumerateForRetrieve[df.host, remoteName, OneFile, h]; EXITS return => {}}; -- of ENABLE UNWIND Subr.FreeString[stemp]; Subr.FreeString[fullname]; }; ProceedWithBringOver: PROC[df: DFSubr.DF, info: STP.FileInfo, h: Subr.TTYProcs, remoteCreateTime, localCreateDate: LONG CARDINAL, remotestream: Stream.Handle, fullname: LONG STRING, state: BareBringOver.State] RETURNS[renamed: BOOL] = { renamed _ FALSE; CWF.WF0[" ... "L]; IF df.presentonlocaldisk AND NOT Subr.CheckForModify[df.shortname, h] THEN { CWF.WF1[" ... %s not transferred (can't be modified).\n"L, df.shortname]; RETURN; }; -- the runtime calls are a hack to tell if this is BringOver -- being run from the boot file, in which case the new version -- cannot modify the BringOver in the boot file IF LongString.EquivalentString[df.shortname, "BringOver.Bcd"L] AND Runtime.GetBcdTime[] ~= Runtime.GetBuildTime[] THEN HandleBringOverSpecialCase[df]; -- check to see if local file is newer version -- don't rename if the file is a bcd file IF df.presentonlocaldisk AND localCreateDate > remoteCreateTime AND NOT Subr.EndsIn[df.shortname, ".bcd"L] THEN { RenameSpecialCase[df]; renamed _ TRUE; }; IF state.useCIFS THEN [df.cap,] _ STPSubr.WriteStreamToDisk[remotestream, df.shortname, info.size, h ! CIFS.Error => TRUSTED { IF code = fileBusy THEN { CWF.WF1[" ... %s not transferred (CIFS says file is busy).\n"L, df.shortname]; GOTO out }}] ELSE [df.cap,] _ STPSubr.WriteStreamToDisk[remotestream, df.shortname, info.size, h]; df.presentonlocaldisk _ TRUE; Subr.SetRemoteFilenameProp[df.cap, fullname]; FileStream.SetLeaderPropertiesForCapability[cap: df.cap, create: LOOPHOLE[remoteCreateTime]]; CWF.WF1["%lu bytes.\n"L, @info.size]; EXITS out => NULL; }; -- called whenever there is a local version that is newer than -- the one being retrieved RenameSpecialCase: PROC[df: DFSubr.DF] = { dollar: LONG STRING _ Subr.AllocateString[100]; {ENABLE UNWIND => {Subr.FreeString[dollar]}; CWF.SWF1[dollar, "%s$$"L, df.shortname]; Directory.DeleteFile[dollar ! Directory.Error => CONTINUE]; Directory.Rename[oldName: df.shortname, newName: dollar]; CWF.WF1["(local version renamed to %s)"L, dollar]; df.cap _ File.nullCapability; }; -- of ENABLE UNWIND Subr.FreeString[dollar]; }; -- this means BringOver is about to replace the BringOver.Bcd on the local disk -- by a different version. This kludge is required to avoid overwriting -- the backing file for the code segment for the loaded BringOver.Bcd HandleBringOverSpecialCase: PROC[df: DFSubr.DF] = { -- Directory.Error here indicates BringOver.Bcd was renamed or a messed up directory cap: File.Capability; cap _ Directory.Lookup[fileName: "BringOver.Bcd"L, permissions: Directory.ignore ! Directory.Error => GOTO out]; Directory.RemoveFile[fileName: "BringOver.Bcd"L, file: cap]; -- this begins a critical section where, if the system crashes, BringOver.Bcd is not on the disk KernelFile.MakeTemporary[cap]; df.presentonlocaldisk _ FALSE; -- just in case df.cap _ File.nullCapability; CWF.WF0["Warning - you have just retrieved a new version of BringOver.Bcd.\n"L]; CWF.WF0[" If you want to use the new version, you must either (1) type the\n"L]; CWF.WF0[" command 'Run BringOver.Bcd;' to the Executive, or\n"L]; CWF.WF0[" (2) boot this volume.\n"L]; EXITS out => NULL; }; ProcessVerify: PROC[dfseq: DFSubr.DFSeq, h: Subr.TTYProcs, ldspace: Space.Handle, state: BareBringOver.State] = { localdiskdate, remoteCreateTime: LONG CARDINAL; newerversions, fillinvers: BOOL; df: DFSubr.DF; fres: FQ.Result; remoteVersion: CARDINAL; targetFileName: LONG STRING _ Subr.AllocateString[125]; {ENABLE UNWIND => {Subr.FreeString[targetFileName]}; fillinvers _ newerversions _ FALSE; DFSubr.ReadInDir[dfseq]; FOR i: CARDINAL IN [0 .. dfseq.size) DO df _ @dfseq[i]; IF EmptyString[df.host] THEN { CWF.WF1["Warning - unable to verify %s on remote server.\n"L, df.shortname]; LOOP; }; IF h.in.UserAbort[] THEN SIGNAL Subr.AbortMyself; CWF.WFC['+]; [fres: fres, remoteVersion: remoteVersion, remoteCreateTime: remoteCreateTime] _ FQ.FileQuery[df.host, df.directory, df.shortname, df.version, df.createtime, df.criterion = none AND df.createtime = 0, h, targetFileName, state.useCIFS]; SELECT fres FROM foundCorrectVersion => { IF df.createtime > 0 AND df.version > 0 AND df.version ~= remoteVersion THEN CWF.WF4["%s Warning: Version !%u has date %lt, but DF file says !%u.\n"L, targetFileName, @remoteVersion, @remoteCreateTime, @df.version]; IF df.criterion = none AND (df.version = 0 OR df.version ~= remoteVersion) THEN { df.version _ remoteVersion; fillinvers _ TRUE; }; -- fill in for those with no create time IF df.createtime = 0 AND df.criterion = none AND df.version = 0 THEN { df.createtime _ remoteCreateTime; df.version _ remoteVersion; fillinvers _ TRUE; }; }; foundWrongVersion => CWF.WF2["\n%s of %lt not found.\n"L, targetFileName, @df.createtime]; notFound => CWF.WF1["\n%s not found.\n"L, targetFileName]; ENDCASE => ERROR; IF fres ~= notFound THEN { highfres: FQ.Result; highdate: LONG CARDINAL; highversion: CARDINAL; -- find out the highest version, and complain [fres: highfres, remoteVersion: highversion, remoteCreateTime: highdate] _ FQ.FileQueryBangH[df.host, df.directory, df.shortname, df.createtime, h, state.useCIFS]; IF fres = foundWrongVersion THEN { CWF.WF2["%s: highest version is %lt"L, targetFileName, @highdate]; df.createtime _ highdate; df.version _ highversion; newerversions _ TRUE; } ELSE IF highversion > remoteVersion THEN { CWF.WF3["%s: newer version !%u\n\tof %lt is available"L, targetFileName, @highversion, @highdate]; df.createtime _ highdate; df.version _ highversion; newerversions _ TRUE; }; }; IF df.createtime = 0 AND df.criterion ~= none THEN df.version _ 0; -- these are confusing to users IF df.presentonlocaldisk THEN [create: localdiskdate] _ Subr.GetCreateDateWithSpace[df.cap, ldspace]; IF df.presentonlocaldisk AND localdiskdate = remoteCreateTime AND fres = foundCorrectVersion THEN Subr.SetRemoteFilenameProp[df.cap, targetFileName]; ENDLOOP; IF newerversions OR fillinvers THEN { CWF.WF0["Storing new dffile with the newest versions \n"L]; CWF.WF0["and/ or filled in version numbers as 'NewDFFile.DF' ... "L]; IF Subr.CheckForModify["NewDFFile.DF"L, h] THEN { sh: Stream.Handle; sh _ Subr.NewStream["NewDFFile.DF"L, Subr.Write]; DFSubr.WriteOut[dfseq, NIL, sh, FALSE]; Stream.Delete[sh]; CWF.WF0["NewDFFile.DF created.\n"L]; }; }; }; -- of ENABLE UNWIND Subr.FreeString[targetFileName]; }; ComputeNeed: PROC[df: DFSubr.DF, ldspace: Space.Handle] RETURNS[need: BOOL, localCreateDate: LONG CARDINAL] = { IF df.createtime = 0 OR NOT df.presentonlocaldisk OR df.criterion = update THEN RETURN[TRUE, 0]; [create: localCreateDate] _ Subr.GetCreateDateWithSpace[df.cap, ldspace]; RETURN[localCreateDate ~= df.createtime, localCreateDate]; }; }.