<> <> <> <> DIRECTORY BasicTime USING [Period], DFInternal USING [AbortDF, CheckAbort, Client, ClientDescriptor, DefaultInteractionProc, GetFileInfo, DoAbort, LocalFile, LocalFileInfo, RemoteFileInfo, ReportFSError, ShortName, SimpleInteraction, YesOrNo], DFOperations USING [AbortInteraction, BringOverAction, BringOverFilter, DFInfoInteraction, FileAction, FileInteraction, InfoInteraction, InteractionProc], DFUtilities USING [ClassifyFileExtension, Date, DateToRope, DifferenceOfUsingLists, DirectoryItem, FileItem, Filter, ImportsItem, IncludeItem, ParseFromStream, ProcessItemProc, RemoveVersionNumber, SortUsingList, SyntaxError, UsingEntry, UsingList], FS USING [Close, Copy, Error, ExpandName, Open, StreamOpen], IO USING [Close, GetIndex, PutFR, PutFR1, STREAM], Rope USING [Equal, Fetch, Length, ROPE, Run, Substr]; BringOverImpl: CEDAR PROGRAM IMPORTS BasicTime, DFInternal, DFUtilities, FS, IO, Rope EXPORTS DFOperations = BEGIN ROPE: TYPE = Rope.ROPE; STREAM: TYPE = IO.STREAM; BringOver: PUBLIC PROC [dfFile: ROPE, filter: DFOperations.BringOverFilter _ [], action: DFOperations.BringOverAction _ $enter, interact: DFOperations.InteractionProc _ NIL, clientData: REF ANY _ NIL, log: STREAM _ NIL] RETURNS [errors, warnings, filesActedUpon: INT _ 0] = { client: DFInternal.Client = NEW[DFInternal.ClientDescriptor _ [ (interact _ IF interact = NIL THEN DFInternal.DefaultInteractionProc ELSE interact), clientData, log ]]; retrievedFiles: REF DFUtilities.UsingList _ NIL; InRetrievedList: PROC [shortName: ROPE] RETURNS [BOOL] = { FOR i: NAT IN [0..retrievedFiles.nEntries) DO IF Rope.Equal[shortName, retrievedFiles[i].name, FALSE] THEN RETURN [TRUE]; ENDLOOP; RETURN [FALSE]; }; ConvertFilter: PROC [f: DFOperations.BringOverFilter] RETURNS [filter: DFUtilities.Filter] = { filter _ [filterA: VAL[f.filterA.ORD], filterB: VAL[f.filterB.ORD], filterC: VAL[f.filterC.ORD]]; IF f.list ~= NIL THEN { nEntries: NAT _ 0; i: NAT _ 0; FOR l: LIST OF ROPE _ f.list, l.rest UNTIL l = NIL DO nEntries _ nEntries.SUCC; ENDLOOP; filter.list _ NEW[DFUtilities.UsingList[nEntries]]; FOR l: LIST OF ROPE _ f.list, l.rest UNTIL l = NIL DO file: ROPE = DFUtilities.RemoveVersionNumber[l.first]; IF ~(filter.filterA = $all OR DFUtilities.ClassifyFileExtension[file] = filter.filterA) THEN NotFoundWarning[file]; filter.list.u[i] _ DFUtilities.UsingEntry[name: file]; i _ i.SUCC; ENDLOOP; filter.list.nEntries _ nEntries; DFUtilities.SortUsingList[usingList: filter.list, nearlySorted: FALSE]; }; }; NotFoundWarning: PROC [file: ROPE] = { warnings _ warnings.SUCC; DFInternal.SimpleInteraction[ client, NEW[DFOperations.InfoInteraction _ [ class: $warning, message: IO.PutFR1["'%g' could not be found in any nested DF file.", [rope[file]]] ]] ]; }; BringOverInner: PROC [dfFile: ROPE, date: DFUtilities.Date, filter: DFUtilities.Filter] RETURNS [retrieved: NAT _ 0] = { <> requested: NAT = IF filter.list ~= NIL THEN filter.list.nEntries ELSE NAT.LAST; <> iAllocatedRetrievedFiles: BOOL _ FALSE; directoryPath: ROPE _ NIL; Localize: PROC [remote: ROPE, date: DFUtilities.Date, action: DFOperations.BringOverAction, directory: ROPE _ NIL] RETURNS [localInfo: REF DFInternal.LocalFileInfo, dontProcess: BOOL _ FALSE] = { remoteInfo: REF DFInternal.RemoteFileInfo _ NEW[DFInternal.RemoteFileInfo _ [remote, date]]; AttachNeeded: PROC RETURNS [action: {alreadyLocal, doAttach, userSaidNo}] = { attach: BOOL _ TRUE; sameDate: BOOL = (localInfo.date.gmt = remoteInfo.date.gmt); IF DFInternal.LocalFile[remoteInfo.name] THEN RETURN[$alreadyLocal]; <> <> IF localInfo.date.format = $explicit THEN { pos: INT _ Rope.Run[localInfo.attachedTo, 0, remoteInfo.name, 0, FALSE]; IF pos < Rope.Length[localInfo.attachedTo] AND Rope.Fetch[localInfo.attachedTo, pos] # '! THEN GO TO needAttach; IF pos < Rope.Length[remoteInfo.name] AND Rope.Fetch[remoteInfo.name, pos] # '! THEN GO TO needAttach; <> SELECT date.format FROM $greaterThan => attach _ BasicTime.Period[from: localInfo.date.gmt, to: remoteInfo.date.gmt] > 0; ENDCASE => attach _ ~sameDate; EXITS needAttach => {}; }; IF ~attach THEN RETURN[$alreadyLocal]; IF ~sameDate AND ~DFInternal.YesOrNo[ client: client, message: IO.PutFR[ "%g {%g} to %g%g?", [rope[remoteInfo.name]], [rope[DFUtilities.DateToRope[remoteInfo.date]]], [rope[localInfo.name]], [rope[ IF localInfo.date.format = $explicit THEN IO.PutFR[ " (previously: %g, keep: %d) ", [rope[DFUtilities.DateToRope[localInfo.date]]], [cardinal[localInfo.keep]] ] ELSE NIL ]] ], default: TRUE ] THEN RETURN[$userSaidNo]; IF localInfo.date.format = $explicit AND localInfo.keep = 1 AND BasicTime.Period[from: remoteInfo.date.gmt, to: localInfo.date.gmt] > 0 AND DFUtilities.ClassifyFileExtension[localInfo.name] = $source AND ~DFInternal.YesOrNo[ client: client, message: IO.PutFR[ "Are you sure? (%g {%g} is older than %g {%g} and will overwrite it)", [rope[remoteInfo.name]], [rope[DFUtilities.DateToRope[remoteInfo.date]]], [rope[localInfo.name]], [rope[DFUtilities.DateToRope[localInfo.date]]] ], default: TRUE, blunder: TRUE ] THEN RETURN[$userSaidNo]; RETURN[$doAttach] }; SELECT Rope.Length[directory] FROM 0 => { <> remote _ DFInternal.ShortName[file: remote, keepVersion: FALSE]; }; Rope.Run[remote, 0, directory, 0, FALSE] => { <> remote _ DFUtilities.RemoveVersionNumber[Rope.Substr[remote, Rope.Length[directory]]]; }; ENDCASE => { <> DFInternal.DoAbort[client.log, IO.PutFR1["Bogus file name (%g).", [rope[remote]] ]]; }; localInfo _ NEW[DFInternal.LocalFileInfo _ [name: remote]]; DFInternal.GetFileInfo[info: localInfo, notFoundOK: TRUE, client: client ! FS.Error => GO TO quit]; localInfo.name _ DFUtilities.RemoveVersionNumber[localInfo.name]; IF ~(date.format = $explicit AND action = $enter) THEN DFInternal.GetFileInfo[info: remoteInfo, client: client ! FS.Error => GO TO quit]; SELECT AttachNeeded[] FROM $alreadyLocal => NULL; $doAttach => { DFInternal.SimpleInteraction[ client, NEW[DFOperations.FileInteraction _ [ remoteFile: remoteInfo.name, localFile: localInfo.name, dateFormat: SELECT remoteInfo.date.format FROM $explicit => $explicit, $greaterThan => $greaterThan, ENDCASE => $notEqual, date: remoteInfo.date.gmt, action: IF action = $check THEN $check ELSE $fetch ]] ]; IF action ~= $check THEN [] _ FS.Copy[ to: localInfo.name, from: remoteInfo.name, wantedCreatedTime: remoteInfo.date.gmt, setKeep: FALSE, keep: IF DFUtilities.ClassifyFileExtension[localInfo.name] = $source THEN 2 ELSE 1, remoteCheck: action ~= $enter, attach: TRUE ! FS.Error => {DFInternal.ReportFSError[error, localInfo, client]; GO TO quit} ]; filesActedUpon _ filesActedUpon.SUCC; }; $userSaidNo => dontProcess _ TRUE; ENDCASE; IF ~ dontProcess AND action = $fetch THEN { ENABLE FS.Error => {DFInternal.ReportFSError[error, remoteInfo, client]; GO TO quit}; FS.Close[FS.Open[localInfo.name]]; }; EXITS quit => {errors _ errors.SUCC; dontProcess _ TRUE}; }; DoOneItem: DFUtilities.ProcessItemProc = { DFInternal.CheckAbort[client]; IF retrievedFiles ~= NIL AND retrievedFiles.nEntries = retrievedFiles.length THEN RETURN[TRUE]; WITH item SELECT FROM directory: REF DFUtilities.DirectoryItem => { <> directoryPath _ FS.ExpandName[directory.path1].fullFName; }; file: REF DFUtilities.FileItem => { remoteName: ROPE = FS.ExpandName[file.name, directoryPath].fullFName; localInfo: REF DFInternal.LocalFileInfo = Localize[remoteName, file.date, action, directoryPath].localInfo; IF retrievedFiles ~= NIL THEN { <> short: ROPE = DFInternal.ShortName[localInfo.name]; retN: NAT = retrievedFiles.nEntries; IF InRetrievedList[short] THEN GO TO notNew; retrievedFiles[retN] _ [name: short]; retrievedFiles.nEntries _ retN+1; DFUtilities.SortUsingList[retrievedFiles, TRUE]; }; IF filter.list ~= NIL AND (retrieved _ retrieved.SUCC) = filter.list.nEntries THEN RETURN[TRUE]; EXITS notNew => {}; }; imports: REF DFUtilities.ImportsItem => { newFilter: DFUtilities.Filter _ [ comments: filter.comments, -- comments processing is unaffected by imports filterA: filter.filterA, -- source/derived distinction is unaffected by imports filterB: IF imports.form = $exports THEN $public ELSE filter.filterB, filterC: $all, -- if the top level passes imports, they can come from anywhere list: IF imports.form = $list THEN imports.list ELSE filter.list ]; SELECT TRUE FROM newFilter.list = NIL => <> [] _ BringOverInner[imports.path1, imports.date, newFilter]; newFilter.list.nEntries = 0 => <> NULL; ENDCASE => { <> retrievedBelow: NAT _ 0; newFilter.filterB _ $all; -- force a look at all files IF retrievedFiles # NIL AND retrievedFiles.nEntries # 0 THEN { <> newFilter.list _ DFUtilities.DifferenceOfUsingLists[newFilter.list, retrievedFiles]; IF newFilter.list = NIL OR newFilter.list.nEntries = 0 THEN GO TO noNeed; }; retrievedBelow _ BringOverInner[imports.path1, imports.date, newFilter]; IF filter.list ~= NIL AND retrievedBelow # 0 THEN { <> IF requested = (retrieved _ retrieved + retrievedBelow) THEN RETURN[TRUE]; IF imports.form = $list THEN { <> filter.list _ DFUtilities.DifferenceOfUsingLists[filter.list, newFilter.list]; <> RETURN[filter.list.nEntries = 0] }; }; EXITS noNeed => {}; }; }; include: REF DFUtilities.IncludeItem => retrieved _ BringOverInner[include.path1, include.date, filter] + retrieved; ENDCASE; }; dfLocalInfo: REF DFInternal.LocalFileInfo; dontProcess: BOOL; [dfLocalInfo, dontProcess] _ Localize[dfFile, date, $fetch]; IF ~dontProcess THEN { dfStream: STREAM = FS.StreamOpen[dfLocalInfo.name ! FS.Error => {DFInternal.ReportFSError[error, dfLocalInfo, client]; GO TO skip}; ]; DFInternal.SimpleInteraction[ client, NEW[DFOperations.DFInfoInteraction _ [action: $start, dfFile: dfFile]] ]; IF filter.list ~= NIL AND retrievedFiles = NIL THEN { retrievedFiles _ NEW[DFUtilities.UsingList[filter.list.nEntries]]; retrievedFiles.nEntries _ 0; iAllocatedRetrievedFiles _ TRUE; }; DFUtilities.ParseFromStream[dfStream, DoOneItem, filter ! DFUtilities.SyntaxError -- [reason: ROPE]-- => { errors _ errors.SUCC; DFInternal.SimpleInteraction[ client, NEW[DFOperations.InfoInteraction _ [ class: $error, message: IO.PutFR[ "Syntax error in '%g'[%d]: %g\NProcessing of this DF file aborted.", [rope[dfLocalInfo.name]], [cardinal[dfStream.GetIndex[]]], [rope[reason]] ] ]] ]; CONTINUE }; DFInternal.AbortDF => dfStream.Close[]; ]; dfStream.Close[]; IF iAllocatedRetrievedFiles THEN { IF retrievedFiles.nEntries ~= filter.list.nEntries THEN { <> diff: REF DFUtilities.UsingList; diff _ DFUtilities.DifferenceOfUsingLists[filter.list, retrievedFiles]; FOR i: NAT IN [0..diff.nEntries) DO IF filter.filterA = $all OR DFUtilities.ClassifyFileExtension[diff.u[i].name] = filter.filterA THEN NotFoundWarning[diff.u[i].name]; ENDLOOP; }; retrievedFiles _ NIL; }; DFInternal.SimpleInteraction[ client, NEW[DFOperations.DFInfoInteraction _ [action: $end, dfFile: dfFile]] ]; EXITS skip => errors _ errors.SUCC; }; }; <> [] _ BringOverInner[dfFile, [format: $explicit], ConvertFilter[filter] ! ABORTED => { DFInternal.SimpleInteraction[client, NEW[DFOperations.AbortInteraction _ [TRUE]]]; <> }; DFInternal.AbortDF => { errors _ errors.SUCC; DFInternal.SimpleInteraction[client, NEW[DFOperations.DFInfoInteraction _ [action: $abort, dfFile: dfFile]]]; CONTINUE }; ]; }; END.