-- SModelImpl.Mesa -- last edit February 25, 1983 2:18 pm by Schmidt -- last edit April 12, 1983 12:47 pm by Paul Rovner -- last edit May 22, 1983 1:39 pm by Russ Atkinson -- used AllocateString instead of STRING _ [...] to reduce size of local frames -- last edit June 24, 1983 5:04 pm by Doug Wyatt -- "PreCedar" option generalized: see ConvertToPreCedar -- 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 [AppendString, 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], Subr: TYPE USING [ AbortMyself, AllocateString, Any, CheckForModify, CopyString, debugflg, EndsIn, errorflg, FileError, FreeString, GetCreateDateWithSpace, MakeTTYProcs, NewStream, numberofleaders, PackedTime, Prefix, PrintGreeting, Read, SetRemoteFilenameProp, strcpy, SubrStop, 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, 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: LONG STRING _ Subr.AllocateString[100]; {ENABLE UNWIND => {Subr.FreeString[sdffile]}; flat _ RopeInline.InlineFlatten[tok]; Subr.strcpy[sdffile, LOOPHOLE[flat]]; -- sdffile is modified later! TopLevelDF[sdffile, options, h]; Subr.FreeString[sdffile]}; }; 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: LONG 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 LongString.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; tempDFFileName: LONG STRING _ Subr.AllocateString[100]; sold: LONG STRING _ Subr.AllocateString[100]; {ENABLE UNWIND => {Subr.FreeString[tempDFFileName]; Subr.FreeString[sold]}; 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; viewer: ViewerClasses.Viewer; dfouter.version _ 0; dfouter.createtime _ Time.Current[]; IF NOT Subr.CheckForModify[dfouter.shortname, h] THEN SIGNAL Subr.AbortMyself; tempDFFileName.length _ 0; Subr.strcpy[tempDFFileName, dfouter.shortname]; IF options.preCedar AND NOT innerAlreadyPreCedar THEN LongString.AppendString[tempDFFileName, "$$SModel.Temp$$"L] ELSE { sold.length _ 0; 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; }; }; -- of ENABLE UNWIND Subr.FreeString[tempDFFileName]; Subr.FreeString[sold]; }; -- of RecursiveStore 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 df.cameFrom OR df.releaseHost = NIL THEN alreadyPreCedar _ TRUE ELSE { directory: LONG STRING _ Subr.AllocateString[100]; {ENABLE UNWIND => {Subr.FreeString[directory]}; -- If ReleaseAs [host]Y>, then make Directory [host]Y> -- alreadyPreCedar if Directory is already [host]Y> LongString.AppendString[directory, "Pre"L]; LongString.AppendString[directory, df.releaseDirectory]; alreadyPreCedar _ (LongString.EquivalentString[df.host, df.releaseHost] AND LongString.EquivalentString[df.directory, directory]); IF NOT alreadyPreCedar THEN { Subr.FreeString[df.host, dfseq.dfzone]; Subr.FreeString[df.directory, dfseq.dfzone]; df.host _ Subr.CopyString[df.releaseHost, dfseq.dfzone]; df.directory _ Subr.CopyString[directory, dfseq.dfzone]; }; }; Subr.FreeString[directory]; }; }; 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, directory, appendfile: LONG STRING _ NIL; 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; }; filename _ Subr.AllocateString[125]; directory _ Subr.AllocateString[100]; appendfile _ Subr.AllocateString[100]; {ENABLE UNWIND => { Subr.FreeString[filename]; Subr.FreeString[directory]; Subr.FreeString[appendfile]}; IF storeSh = NIL THEN { time: LONG CARDINAL; 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; GO TO return; }; IF ch = 'a THEN options.askForConfirm _ FALSE ELSE IF ch ~= 'y THEN GO TO 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 EXITS return => {}; }; -- of ENABLE UNWIND Subr.FreeString[filename]; Subr.FreeString[directory]; Subr.FreeString[appendfile]; }; -- of StoreBack 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: LONG STRING _ NIL; 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 targetFileName _ Subr.AllocateString[125]; {ENABLE UNWIND => {Subr.FreeString[targetFileName]}; [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]; }; Subr.FreeString[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"]; }.