-- SModelImpl.Mesa, last edit February 25, 1983 2:18 pm by Schmidt
-- last edit April 12, 1983 12:47 pm by Paul Rovner
-- Pilot 6.0/ Mesa 7.0
-- options
-- /c alto-versions of DF stuff compatibility for Df files produced
-- /n do everything normally but DON'T store any files
-- /r ignore READONLY attribute
-- /v verify model - enumerate remote files to make sure
-- /s store back non-Readonly files even if create dates are ok
-- /t top level DF only
--
-- rules:
-- date in df file = date of local file
-- Then do nothing
-- date in df file ~= date of local file
-- If file is ReadOnly, give error message
-- Else Store the local file and update the date in the DF file
-- date in df file is omitted and file is on local disk
-- If file is ReadOnly Then fill in date and give warning
-- (don't store)
-- Else fill in date and transfer the file
--
-- verify mode:
-- if file would be transferred by above rules, go ahead and do it
-- if not, enumerate the remote server looking for the version
-- if this version of the file is not out there,
-- store it (even if there are already different versions out there)
DIRECTORY
Commander: TYPE USING[CommandProc, Register],
ConvertUnsafe: TYPE USING[ToRope],
CWF: TYPE USING [FWF1, FWF2, SetWriteProcedure, SWF1, SWF3, SWF4, WF0,
WF1, WF2, WF3, WF4, WFC, WFCR],
DFSubr: TYPE USING [AllocateDFSeq, DF, DFSeq, FreeDFSeq, LookupDF, NextDF,
ParseStream, ReadInDir, WriteOut],
Directory: TYPE USING [DeleteFile, Error, Handle, ignore, Lookup, Rename],
File: TYPE USING[Unknown],
FileStream: TYPE USING [GetLength, SetIndex, SetLeaderPropertiesForCapability],
FQ: TYPE USING[FileQuery, Result],
IO: TYPE USING[Handle, PutChar, PutFR, ResetUserAbort, string, UserAbort, UserAborted, PutRope, STREAM],
IOMisc: TYPE USING[AskUser],
LongString: TYPE USING [EquivalentString, StringToDecimal],
Resource: TYPE USING[Acquire, Release, AbortProc],
Rope: TYPE USING[Fetch, ROPE, Text, Cat],
RopeInline: TYPE USING[InlineFlatten],
Space: TYPE USING [Create, Delete, Handle, Map, nullHandle, virtualMemory],
UnsafeSTP: TYPE USING [Error, FileInfo, GetFileInfo, Handle,
SetDirectory],
STPSubr: TYPE USING [AddUserName, Connect, HandleSTPError, StopSTP, Store],
Stream: TYPE USING [Delete, Handle, PutChar],
String: TYPE USING [AppendString],
Subr: TYPE USING [AbortMyself, Any, CheckForModify, CopyString, debugflg,
EndsIn, errorflg, FileError, FreeString, GetCreateDateWithSpace, MakeTTYProcs,
NewStream,
numberofleaders, PackedTime, Prefix, PrintGreeting,
Read, SetRemoteFilenameProp,
strcpy, SubrStop, SubStrCopy, TTYProcs, Write],
System: TYPE USING [GetClockPulses, PulsesToMicroseconds],
Time: TYPE USING [Current],
UECP: TYPE USING[Argv, Parse],
ViewerClasses: TYPE USING[Viewer],
ViewerOps: TYPE USING[FindViewer, RestoreViewer];
SModelImpl: PROGRAM
IMPORTS Commander, ConvertUnsafe, CWF, DFSubr, Directory, File,
FileStream, FQ, IO, IOMisc, LongString, Resource, Rope, RopeInline, Space, STP: UnsafeSTP,
STPSubr, Stream, String,
Subr, System, Time, UECP, ViewerOps = {
MAXFILE: CARDINAL = 500;
OpType: TYPE = LONG POINTER TO OpTypeRecord;
OpTypeRecord: TYPE = RECORD[
askForConfirm: BOOL ← TRUE, -- /a
altoCompatibility: BOOL ← FALSE, -- /c
flipCameFrom: BOOL ← TRUE, -- /f
-- /l is used by SDD for the librarian
dontstorefiles: BOOL ← FALSE, -- /n
preCedar: BOOL ← FALSE, -- /p
ignorereadonly: BOOL ← FALSE, -- /r
forcestoreback: BOOL ← FALSE, -- /s
topLevelOnly: BOOL ← FALSE, -- /t
verify: BOOL ← FALSE, -- /v
quit: BOOL ← FALSE -- set by typing "q" to the question
];
-- MDS USAGE !!!
storeSh: Stream.Handle ← NIL;
stdout: IO.Handle;
-- endof MDS USAGE
abortProc: Resource.AbortProc = TRUSTED{
--PROC [data: REF ANY] RETURNS[abort: BOOL];
in: IO.STREAM = NARROW[data, IO.STREAM];
abort ← in.UserAbort[];
IF abort THEN in.ResetUserAbort[];
};
Main: Commander.CommandProc = TRUSTED {
--PROC [cmd: Handle]
ENABLE UNWIND => [] ← Resource.Release[$SModel];
h: Subr.TTYProcs;
in: IO.Handle = cmd.in;
out: IO.Handle = cmd.out;
success: BOOL ← FALSE;
otherOwner: Rope.ROPE ← NIL;
[success, otherOwner] ← Resource.Acquire[resource: $SModel,
owner: "SModel",
waitForIt: FALSE];
IF NOT success
THEN {
out.PutRope[Rope.Cat["Waiting for ", otherOwner, " to finish..."]];
[success, ] ← Resource.Acquire[resource: $SModel,
owner: "SModel",
waitForIt: TRUE,
abortProc: abortProc,
abortProcData: in
];
IF NOT success THEN {
out.PutRope["ABORTED\n"];
RETURN;
} ELSE out.PutRope["proceeding\n"];
};
h ← Subr.MakeTTYProcs[in, out, NIL, MyConfirm];
SModelUsingProcs[h, cmd.commandLine];
[] ← Resource.Release[$SModel];
};
SModelUsingProcs: PROC[h: Subr.TTYProcs, commandLine: Rope.ROPE] = {
tok: Rope.ROPE;
flat: Rope.Text;
opTypeRecord: OpTypeRecord ← [];
options: OpType ← @opTypeRecord;
starttime: Subr.PackedTime;
p: LONG CARDINAL;
argv: UECP.Argv ← UECP.Parse[commandLine];
Cleanup: PROC = {
STPSubr.StopSTP[];
Subr.SubrStop[];
};
{
ENABLE {
UNWIND => {
h.out.ResetUserAbort[];
Cleanup[];
};
STP.Error => {
CWF.WF0["FTP Error. "L];
IF error ~= NIL THEN
CWF.WF2["message: %s, code %u in Stp.Mesa\n"L, error, @code];
Subr.errorflg ← TRUE;
GOTO leave;
};
Subr.AbortMyself, IO.UserAborted => {
h.out.ResetUserAbort[];
CWF.WF0["SModel Aborted.\n"L];
GOTO leave;
};
};
p ← System.PulsesToMicroseconds[System.GetClockPulses[]];
storeSh ← NIL;
Subr.errorflg ← Subr.debugflg ← FALSE;
stdout ← h.out;
[] ← CWF.SetWriteProcedure[MyPutChar];
Subr.PrintGreeting["SModel"L];
Subr.numberofleaders ← 0;
starttime ← Time.Current[];
FOR parm: CARDINAL IN [1 .. argv.argc) DO
tok ← argv[parm];
IF tok.Fetch[0] = '- OR tok.Fetch[0] = '/ THEN
SELECT tok.Fetch[1] FROM
'a,'A => options.askForConfirm ← FALSE;
'c,'C => options.altoCompatibility ← TRUE;
'f,'F => options.flipCameFrom ← FALSE;
'n,'N => options.dontstorefiles ← TRUE;
'p,'P => options.preCedar ← options.verify ← TRUE;
'r,'R => options.ignorereadonly ← TRUE;
's,'S => options.forcestoreback ← TRUE; -- store back even if dates ok
't,'T => options.topLevelOnly ← TRUE;
'v,'V => options.verify ← TRUE;
ENDCASE => {
flat ← RopeInline.InlineFlatten[tok];
CWF.WF1["Unknown flag '%s'\n"L,LOOPHOLE[flat, LONG STRING]]
}
ELSE {
sdffile: STRING ← [100];
flat ← RopeInline.InlineFlatten[tok];
Subr.strcpy[sdffile, LOOPHOLE[flat]]; -- sdffile is modified later!
TopLevelDF[sdffile, options, h];
};
ENDLOOP;
EXITS
leave => NULL;
};
Cleanup[];
starttime ← Time.Current[] - starttime;
CWF.WF1["\nTotal elapsed time for SModel %lr."L,@starttime];
IF Subr.errorflg THEN CWF.WF0["\tErrors logged."L];
CWF.WFCR[];
p ← System.PulsesToMicroseconds[System.GetClockPulses[]] - p;
p ← p/1000;
-- CWF.WF1["\nMilliseconds: %lu\n"L, @p];
};
TopLevelDF: PROC[sdffile: STRING, options: OpType, h: Subr.TTYProcs] = {
dfseq: DFSubr.DFSeq ← NIL;
nstored: CARDINAL;
df: DFSubr.DF;
ldspace: Space.Handle ← Space.nullHandle;
Cleanup: PROC = {
IF storeSh ~= NIL THEN Stream.Delete[storeSh];
storeSh ← NIL;
DFSubr.FreeDFSeq[@dfseq];
IF ldspace ~= Space.nullHandle THEN Space.Delete[ldspace];
ldspace ← Space.nullHandle;
};
{
ENABLE UNWIND => Cleanup[];
IF options.preCedar AND options.topLevelOnly THEN {
CWF.WF0["The /p and /t options cannot be used together. Run SModel again with one or the other but not both.\n"L];
RETURN;
};
IF NOT Subr.Any[sdffile, '.] THEN String.AppendString[sdffile, ".DF"L];
IF NOT Subr.EndsIn[sdffile, ".df"L] THEN {
CWF.WF1["Error - %s does not end in '.DF'.\n"L, sdffile];
RETURN;
};
-- build fake entry
dfseq ← DFSubr.AllocateDFSeq[maxEntries: 1, zoneType: shared];
df ← DFSubr.NextDF[dfseq];
df.shortname ← Subr.CopyString[sdffile, dfseq.dfzone];
df.atsign ← TRUE;
df.presentonlocaldisk ← TRUE;
-- Subr.debugflg ← FALSE;
ldspace ← Space.Create[size: 1, parent: Space.virtualMemory];
Space.Map[ldspace];
IF options.verify THEN
CWF.WF0["Checking remote files.\n"L];
[nstored: nstored] ← RecursiveStore[dfseq, NIL, options, ldspace, h];
IF nstored > 0 AND NOT options.dontstorefiles THEN
CWF.WF1["%u files stored.\n"L, @nstored];
Cleanup[];
}};
-- topdfouter points to the entry in the outer df that indirects thru this DF
-- dfseqouter is allocated and filled with its contents
-- topdfouter may be NIL
RecursiveStore: PROC[dfseqouter: DFSubr.DFSeq, topdfouter: DFSubr.DF,
options: OpType, ldspace: Space.Handle, h: Subr.TTYProcs]
RETURNS[outofspace: BOOL, nstored: CARDINAL,
dffilemustbestored, alreadyPreCedar: BOOL] = {
dfouter: DFSubr.DF;
d, xferred, p: BOOL;
flipped: BOOL ← FALSE;
nstored ← 0;
dffilemustbestored ← outofspace ← FALSE;
alreadyPreCedar ← TRUE;
p ← FALSE;
FOR i: CARDINAL IN [0 .. dfseqouter.size) DO
IF NOT dfseqouter[i].presentonlocaldisk THEN {
IF NOT p THEN {
CWF.WF1["Warning - these file(s) are not on the local disk:\n\t%s"L,
dfseqouter[i].shortname];
p ← TRUE;
}
ELSE CWF.WF1[", %s"L, dfseqouter[i].shortname];
};
ENDLOOP;
IF p THEN CWF.WFCR[];
p ← FALSE;
FOR i: CARDINAL IN [0 .. dfseqouter.size) DO
dfouter ← @dfseqouter[i];
IF Subr.EndsIn[dfouter.shortname, ".DF"L]
AND NOT dfouter.atsign
AND NOT dfouter.readonly
AND NOT LongString.EquivalentString[dfouter.shortname, topdfouter.shortname]
THEN {
IF NOT p THEN {
CWF.WF2["Warning - these file(s) are not Imported nor Included in %s:\n\t%s"L,
topdfouter.shortname, dfouter.shortname];
p ← TRUE;
}
ELSE
CWF.WF1[", %s"L, dfouter.shortname];
};
ENDLOOP;
IF p THEN CWF.WFCR[];
FOR i: CARDINAL IN [0 .. dfseqouter.size) DO
dfouter ← @dfseqouter[i];
-- see if CameFrom
IF dfouter.cameFrom AND options.flipCameFrom THEN
flipped ← ChangeCameFromToReleaseAs[dfouter, dfseqouter] OR flipped;
IF options.preCedar THEN
p ← ConvertToPreCedar[dfouter, dfseqouter];
alreadyPreCedar ← alreadyPreCedar AND p;
-- is this an indirect DF file?
IF dfouter.atsign
AND dfouter.presentonlocaldisk
AND NOT dfouter.readonly
AND Subr.EndsIn[dfouter.shortname, ".DF"L]
AND (NOT options.topLevelOnly OR topdfouter = NIL) THEN {
dfseqinner: DFSubr.DFSeq ← NIL;
o,d, innerAlreadyPreCedar: BOOL;
n: CARDINAL;
sh: Stream.Handle;
{
ENABLE UNWIND => DFSubr.FreeDFSeq[@dfseqinner];
IF topdfouter ~= NIL THEN
CWF.WF1["\nNested SModel of %s.\n"L, dfouter.shortname];
dfseqinner ← DFSubr.AllocateDFSeq[maxEntries: MAXFILE, zoneType: shared];
sh ← Subr.NewStream[dfouter.shortname, Subr.Read
! Subr.FileError => GOTO err];
DFSubr.ParseStream[sh, dfseqinner, dfouter.shortname, dfouter.using,
FALSE, FALSE, FALSE, h];
Stream.Delete[sh];
IF h.in.UserAbort[] THEN SIGNAL Subr.AbortMyself;
DFSubr.ReadInDir[dfseqinner];
-- at this point all the Capabilities are filled in
[o,n,d, innerAlreadyPreCedar] ← RecursiveStore[dfseqinner, dfouter,
options, ldspace, h];
outofspace ← outofspace OR o;
-- the inner dffile changed, the outer one must be stored (only if not ~= or >)
dffilemustbestored ← dffilemustbestored OR (d AND dfouter.criterion = none);
nstored ← nstored + n;
IF options.verify THEN CWF.WFCR[];
IF options.quit THEN EXIT;
IF h.in.UserAbort[] THEN SIGNAL Subr.AbortMyself;
IF d OR n > 0 THEN {
sh: Stream.Handle;
dfinner: DFSubr.DF;
storeDFFailed: BOOL ← FALSE;
tempDFFileName: STRING ← [100];
viewer: ViewerClasses.Viewer;
dfouter.version ← 0;
dfouter.createtime ← Time.Current[];
IF NOT Subr.CheckForModify[dfouter.shortname, h] THEN
SIGNAL Subr.AbortMyself;
Subr.strcpy[tempDFFileName, dfouter.shortname];
IF options.preCedar AND NOT innerAlreadyPreCedar THEN
String.AppendString[tempDFFileName, "$$SModel.Temp$$"L]
ELSE {
sold: STRING ← [100];
CWF.SWF1[sold, "%s$"L, dfouter.shortname];
-- save the old df file
Directory.DeleteFile[fileName: sold ! Directory.Error => CONTINUE];
Directory.Rename[newName: sold, oldName: dfouter.shortname];
};
-- get the entry for the DF file
dfinner ← DFSubr.LookupDF[dfseqinner, dfouter.shortname];
IF dfinner ~= NIL THEN {
dfinner.createtime ← dfouter.createtime;
dfinner.version ← 0;
};
-- this writes out a new DF File, with self entry with the fake time
sh ← Subr.NewStream[tempDFFileName, Subr.Write];
DFSubr.WriteOut[dfseq: dfseqinner, topLevelFile: NIL,
outputStream: sh, print: FALSE,
altoCompatibility: options.altoCompatibility];
Stream.Delete[sh];
IF NOT options.preCedar OR innerAlreadyPreCedar THEN
CWF.WF1["New file on '%s'.\n"L, dfouter.shortname];
viewer ← ViewerOps.FindViewer[ConvertUnsafe.ToRope[tempDFFileName]];
IF viewer ~= NIL THEN {
IF viewer.newVersion THEN
CWF.WF1["Warning - you are already editing %s.\n"L, tempDFFileName]
ELSE
ViewerOps.RestoreViewer[viewer];
};
-- we must make the create dates agree between the one
-- we set in .df file and actual file we are about to store
dfouter.cap ← Directory.Lookup[fileName: tempDFFileName,
permissions: Directory.ignore];
IF dfinner ~= NIL THEN -- Rename doesn't change cap's
dfinner.cap ← dfouter.cap;
IF NOT options.dontstorefiles
AND NOT (dfouter.readonly AND NOT options.ignorereadonly)
AND NOT outofspace THEN {
-- make off by one second so if the store fails
-- then the next SModel will store the DF file since
-- the self-reference and create-time do not agree
FileStream.SetLeaderPropertiesForCapability[cap: dfouter.cap,
create: LOOPHOLE[dfouter.createtime+1]];
IF dfouter.directory ~= NIL THEN {
IF dfinner ~= NIL THEN {
Subr.FreeString[dfinner.host, dfseqinner.dfzone];
dfinner.host ← Subr.CopyString[dfouter.host, dfseqinner.dfzone];
Subr.FreeString[dfinner.directory, dfseqinner.dfzone];
dfinner.directory ← Subr.CopyString[dfouter.directory, dfseqinner.dfzone];
};
[] ← StoreBack[dfouter, dfouter.createtime, options, h
! STP.Error => IF code = requestRefused THEN {
CWF.WF1["\nError - %s\n"L, error];
GOTO ferr
}];
nstored ← nstored + 1;
}
ELSE IF dfinner ~= NIL THEN {
[] ← StoreBack[dfinner, dfinner.createtime, options, h
! STP.Error => IF code = requestRefused THEN {
CWF.WF1["\nError - %s\n"L, error];
GOTO ferr
}];
nstored ← nstored + 1;
};
-- ELSE don't know where to store it
EXITS
ferr => {
CWF.WF0["Delete extra files on remote directory and run SModel again.\n"L];
Subr.errorflg ← TRUE;
storeDFFailed ← TRUE;
};
};
-- since (presumably) the file was stored successfully, we set the
-- create time to the right time
IF NOT storeDFFailed THEN
FileStream.SetLeaderPropertiesForCapability[cap: dfouter.cap,
create: LOOPHOLE[dfouter.createtime]];
IF dfouter.directory ~= NIL
AND (dfouter.readonly AND NOT options.ignorereadonly) THEN {
CWF.WF1["Warning- you MUST STORE %s yourself since\n"L, dfouter.shortname];
CWF.WF0[" it was ReadOnly and was not stored.\n"L];
}
ELSE IF options.dontstorefiles THEN {
CWF.WF1["Warning- you MUST STORE %s yourself since\n"L, dfouter.shortname];
CWF.WF0[" SModel /n did not store it for you.\n"L];
};
-- reset outer DF entry if it was > or ~=
IF dfouter.criterion ~= none THEN {
dfouter.createtime ← 0;
dfouter.version ← 0;
};
IF options.preCedar AND NOT innerAlreadyPreCedar THEN
Directory.DeleteFile[tempDFFileName ! Directory.Error => CONTINUE];
DFSubr.FreeDFSeq[@dfseqinner];
IF topdfouter ~= NIL THEN
CWF.WF1["End of nested SModel of %s.\n\n"L, dfouter.shortname];
LOOP; -- since we have stored it, just go around
}
ELSE IF NOT options.dontstorefiles THEN
CWF.WF1["No files stored, %s not changed.\n"L, dfouter.shortname];
IF Subr.debugflg THEN
CWF.WF2["%u leaders read, dfseq.size = %u.\n"L,
@Subr.numberofleaders, @dfseqinner.size];
DFSubr.FreeDFSeq[@dfseqinner];
IF topdfouter ~= NIL THEN
CWF.WF1["End of nested SModel of %s.\n\n"L, dfouter.shortname];
EXITS
err => {
CWF.WF1["Error - can't open '%s'\n"L, dfouter.shortname];
Subr.errorflg ← TRUE;
};
}};
IF topdfouter = NIL THEN EXIT; -- skip this test if is top level DF
-- this is either a regular file, a ReadOnly @ DF file, or a non-ReadOnly @ DF file
-- that we did not store in the above nested section
[d, xferred] ← PossibleTransfer[dfouter, options,
topdfouter.shortname, ldspace, h
! STP.Error => IF code = requestRefused THEN {
CWF.WF1["\nError - %s\n"L, error];
CWF.WF0["Only some of the files have been transferred.\n"L];
CWF.WF0["Go and clean up your remote directories, then run SModel\n"L];
CWF.WF0["EXACTLY as you did this time.\n"L];
outofspace ← TRUE;
Subr.errorflg ← TRUE;
CONTINUE;
}];
IF dfouter.criterion ~= none THEN -- for the ~=, >, set version number to 0
dfouter.version ← 0;
dffilemustbestored ← dffilemustbestored OR d;
IF xferred THEN nstored ← nstored + 1;
IF outofspace OR options.quit THEN EXIT;
ENDLOOP;
IF flipped THEN {
CWF.WF0["\nSome CameFroms were changed to ReleaseAs clauses or deleted.\n"L];
dffilemustbestored ← TRUE;
};
};
ChangeCameFromToReleaseAs: PROC[df: DFSubr.DF, dfseq: DFSubr.DFSeq]
RETURNS[changed: BOOL] = {
IF df.cameFrom AND df.releaseHost ~= NIL THEN{
IF df.readonly THEN {
-- elim CameFrom on an Imports or any other ReadOnly
Subr.FreeString[df.releaseHost, dfseq.dfzone];
Subr.FreeString[df.releaseDirectory, dfseq.dfzone];
df.releaseHost ← df.releaseDirectory ← NIL;
}
ELSE {
t: LONG STRING;
t ← df.releaseHost;
df.releaseHost ← df.host;
df.host ← t;
t ← df.releaseDirectory;
df.releaseDirectory ← df.directory;
df.directory ← t;
IF df.createtime > 0 THEN
df.version ← 0; -- version is likely to be for releaseDirectory
};
df.cameFrom ← FALSE;
changed ← TRUE;
}
ELSE
changed ← FALSE;
RETURN;
};
ConvertToPreCedar: PROC[df: DFSubr.DF, dfseq: DFSubr.DFSeq]
RETURNS[alreadyPreCedar: BOOL] = {
IF NOT df.cameFrom
AND df.releaseHost ~= NIL
AND Subr.Prefix[df.releaseDirectory, "Cedar>"L] THEN {
temp: STRING ← [100];
directory: STRING ← [100];
alreadyPreCedar ← Subr.Prefix[df.directory, "PreCedar>"L];
Subr.FreeString[df.host, dfseq.dfzone];
Subr.FreeString[df.directory, dfseq.dfzone];
df.host ← Subr.CopyString["Indigo"L, dfseq.dfzone];
Subr.strcpy[directory, "PreCedar>"L];
Subr.SubStrCopy[temp, df.releaseDirectory, 6];
String.AppendString[directory, temp];
df.directory ← Subr.CopyString[directory, dfseq.dfzone];
}
ELSE alreadyPreCedar ← TRUE;
};
PossibleTransfer: PROC[df: DFSubr.DF, options: OpType, sdffile: LONG STRING,
ldspace: Space.Handle, h: Subr.TTYProcs] RETURNS[dffileshouldbestored, xferred: BOOL] = {
ENABLE File.Unknown => {
CWF.WF1["ERROR File.Unknown - problem reading %s.\n"L, df.shortname];
GOTO problem;
};
havedate: LONG CARDINAL ← 0;
xferred ← dffileshouldbestored ← FALSE;
IF options.verify THEN {
oldCreate: LONG CARDINAL ← 0;
found: BOOL;
IF df.presentonlocaldisk
AND NOT (df.readonly AND NOT options.ignorereadonly)
AND df.criterion = none THEN {
[create: havedate] ← Subr.GetCreateDateWithSpace[df.cap, ldspace];
oldCreate ← df.createtime;
df.createtime ← havedate;
};
found ← ProcessVerify[df, ldspace, options, h];
IF havedate ~= 0 THEN
df.createtime ← oldCreate;
IF found THEN { -- if found, set create time to point to version found
IF havedate ~= 0 THEN {
IF df.createtime > 0 THEN
df.createtime ← havedate -- occurs if DF file in error, but remote and local agree
ELSE IF df.presentonlocaldisk AND df.criterion = none
AND NOT (df.readonly AND NOT options.ignorereadonly) THEN
df.createtime ← havedate; -- no date in DF file, but we would have stored it
};
RETURN;
};
IF NOT df.presentonlocaldisk
OR df.criterion ~= none
OR (df.readonly AND NOT options.ignorereadonly) THEN {
Subr.errorflg ← TRUE;-- not found, for one of these reasons
RETURN;
};
};
-- skip those files we can't/ won't xfer
IF NOT df.presentonlocaldisk OR df.criterion ~= none THEN RETURN;
IF havedate = 0 THEN
[create: havedate] ← Subr.GetCreateDateWithSpace[df.cap, ldspace];
IF havedate = df.createtime
AND NOT options.forcestoreback
AND NOT options.verify THEN
RETURN; -- stop if dates agree and verify found it, if /v given
IF df.createtime = 0
AND (df.readonly AND NOT options.ignorereadonly) THEN {
CWF.WF1["Warning- %s is ReadOnly and has no create date in the DF file,\n"L,
df.shortname];
CWF.WF1["\t%lt has been filled in, but the file has not been transferred.\n"L,
@havedate];
df.createtime ← havedate;
dffileshouldbestored ← TRUE;
RETURN;
};
IF (df.readonly AND NOT options.ignorereadonly) THEN {
IF df.createtime = havedate THEN RETURN;
-- don't change the df file for this
CWF.WF1["Warning- %s is ReadOnly and the create dates of the file\n"L, df.shortname];
CWF.WF0["\ton the local disk and in DF file do not agree.\n"L];
-- dont transfer it, readonly file
RETURN;
};
-- actually transfer it
IF LongString.EquivalentString[df.shortname, sdffile] THEN {
dffileshouldbestored ← TRUE; -- unless it is the self-reference
df.version ← 0;
}
ELSE {
xferred ← StoreBack[df, havedate, options, h];
IF h.in.UserAbort[] THEN SIGNAL Subr.AbortMyself;
};
EXITS
problem => RETURN[FALSE, FALSE];
};
StoreBack: PROC[df: DFSubr.DF, localCreateDate: LONG CARDINAL,
options: OpType, h: Subr.TTYProcs] RETURNS[xferred: BOOL] = {
filename: STRING ← [125];
directory: STRING ← [100];
nbytes: LONG CARDINAL;
stphandle: STP.Handle;
info: STP.FileInfo;
WFstoreSh: PROC[ch: CHAR] = {
Stream.PutChar[storeSh, ch];
};
xferred ← FALSE;
IF df.host = NIL OR df.host.length = 0 THEN {
CWF.WF1["Warning - unable to store %s on remote server.\n"L, df.shortname];
RETURN;
};
IF storeSh = NIL THEN {
time: LONG CARDINAL;
appendfile: STRING ← [100];
STPSubr.AddUserName[appendfile, "%s-smodel.files$"L, h];
storeSh ← Subr.NewStream[appendfile, Subr.Write];
FileStream.SetIndex[storeSh, FileStream.GetLength[storeSh]]; -- sets to EOF
time ← Time.Current[];
CWF.FWF1[WFstoreSh, "\n(Last run on %lt)\n"L, @time];
IF options.verify THEN CWF.WFCR[];
CWF.WF1["Files to be transferred are recorded on '%s'\n"L, appendfile];
};
CWF.FWF2[WFstoreSh, "<%s>%s\n"L, df.directory, df.shortname];
IF NOT options.dontstorefiles THEN {
IF options.askForConfirm THEN {
ch: CHAR;
ch ← h.Confirm[h.in, h.out, h.data,
IO.PutFR["Store [%s]<%s>%s ", IO.string[df.host], IO.string[df.directory],
IO.string[df.shortname]], 'y];
IF ch = 'q OR ch = 'Q THEN {
options.quit ← TRUE;
RETURN;
};
IF ch = 'a THEN
options.askForConfirm ← FALSE
ELSE IF ch ~= 'y THEN
RETURN;
};
stphandle ← STPSubr.Connect[host: df.host, onlyOne: TRUE, h: h];
IF h.in.UserAbort[] THEN SIGNAL Subr.AbortMyself;
IF NOT options.askForConfirm THEN {
IF options.verify THEN CWF.WFCR[];
CWF.WF3["Store [%s]<%s>%s ... "L, df.host, df.directory, df.shortname];
};
Subr.strcpy[directory, df.directory]; -- required by MAXC
STP.SetDirectory[stphandle, directory];
-- remember: this Store done using DesiredProperties
nbytes ← STPSubr.Store[stphandle: stphandle, localCap: df.cap, remoteName: df.shortname,
createDate: localCreateDate, h: h
! STP.Error => IF STPSubr.HandleSTPError[stphandle, code, error, h] THEN RETRY];
info ← STP.GetFileInfo[stphandle];
-- CWF.WF1["%lu bytes.\n"L, @info.size]; info.size doesn't work
IF info.version = NIL OR info.version.length = 0 THEN { -- Juniper or Twinkle
CWF.SWF3[filename, "[%s]<%s>%s"L, df.host, info.directory, info.body];
df.version ← 0;
CWF.WF0["Done.\n"L];
}
ELSE {
df.version ← LongString.StringToDecimal[info.version];
CWF.SWF4[filename,
IF Subr.Prefix[df.host, "maxc"L] THEN "[%s]<%s>%s;%s"L ELSE "[%s]<%s>%s!%s"L,
df.host, info.directory, info.body, info.version];
CWF.WF1["!%s.\n"L, info.version];
};
Subr.SetRemoteFilenameProp[df.cap, filename];
xferred ← TRUE;
};
df.createtime ← localCreateDate; -- down here in case of signals
xferred ← TRUE; -- true if dontstorefiles is true
};
ProcessVerify: PROC[df: DFSubr.DF, ldspace: Space.Handle, options: OpType, h: Subr.TTYProcs]
RETURNS[found: BOOL] = {
localdiskdate: LONG CARDINAL ← 0;
remoteCreateTime: LONG CARDINAL ← 0;
remoteVersion: CARDINAL ← 0;
fres: FQ.Result;
targetFileName: STRING ← [125];
wantExplicitVersion: BOOL ← df.criterion = none AND df.createtime = 0;
found ← FALSE;
IF h.in.UserAbort[] THEN SIGNAL Subr.AbortMyself;
CWF.WFC['+];
IF options.preCedar AND NOT wantExplicitVersion THEN
df.version ← 0; -- /p will have wrong version hints
[fres: fres, remoteVersion: remoteVersion, remoteCreateTime: remoteCreateTime]
← FQ.FileQuery[df.host, df.directory, df.shortname, df.version, df.createtime,
wantExplicitVersion, h,
targetFileName];
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];
found ← TRUE;
df.version ← remoteVersion;
IF df.presentonlocaldisk THEN
[create: localdiskdate] ← Subr.GetCreateDateWithSpace[df.cap, ldspace];
IF df.createtime = 0 AND df.criterion ~= none
AND remoteCreateTime ~= localdiskdate AND localdiskdate ~= 0 THEN
-- the >, ~= entry on local disk does not match the
-- highest version number on remote dir
CWF.WF3["\n%s (Warning- highest version number is dated %lt, local copy is dated %lt.) "L,
targetFileName, @remoteCreateTime, @localdiskdate];
};
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 df.createtime = 0 AND df.criterion ~= none THEN
df.version ← 0; -- these are confusing to users
IF df.presentonlocaldisk AND localdiskdate = 0 THEN
[create: localdiskdate] ← Subr.GetCreateDateWithSpace[df.cap, ldspace];
IF df.presentonlocaldisk AND localdiskdate = remoteCreateTime
AND fres = foundCorrectVersion THEN
Subr.SetRemoteFilenameProp[df.cap, targetFileName];
};
MyConfirm: PROC[in, out: IO.Handle, data: REF ANY, msg: Rope.ROPE, dch: CHAR]
RETURNS[CHAR] = {
value: ATOM;
value ← IOMisc.AskUser[msg: msg, in: in, out: out,
keyList: LIST[$Yes, $No, $All, $Quit]]; -- order is important
SELECT value FROM
$All => RETURN['a];
$No => RETURN['n];
$Quit => RETURN['q];
$Yes => RETURN['y];
ENDCASE => ERROR;
};
MyPutChar: PROC[ch: CHAR] = {
stdout.PutChar[ch];
};
-- start code
Commander.Register[key: "SModel", proc: Main, doc: "store a consistent and complete .df model"];
}.