<> <> <> DIRECTORY BasicTime, Commander, DefaultRemoteNames, DFOperations, DFUtilities, FS, IO, MessageWindow, Process, ReleaseToolVerify, Rope, SymTab, ViewerIO; UnReleaseToolDriver: CEDAR PROGRAM IMPORTS BasicTime, Commander, DefaultRemoteNames, DFUtilities, FS, IO, MessageWindow, Process, ReleaseToolVerify, Rope, SymTab, ViewerIO = BEGIN CommentItem: TYPE = DFUtilities.CommentItem; Date: TYPE = DFUtilities.Date; DirectoryItem: TYPE = DFUtilities.DirectoryItem; FileItem: TYPE = DFUtilities.FileItem; Filter: TYPE = DFUtilities.Filter; ImportsItem: TYPE = DFUtilities.ImportsItem; IncludeItem: TYPE = DFUtilities.IncludeItem; LORA: TYPE = LIST OF REF ANY; ROPE: TYPE = Rope.ROPE; STREAM: TYPE = IO.STREAM; WhiteSpaceItem: TYPE = DFUtilities.WhiteSpaceItem; copyAll: BOOL _ FALSE; <> UnReleaseToolCommand: Commander.CommandProc = { debugging, moving: BOOL _ FALSE; inV,outV: STREAM _ NIL; inStream: STREAM _ NIL; dfTable: SymTab.Ref _ NIL; dfFile: ROPE _ "CurrentCedar.df"; mapPrefix: ROPE _ "Cedar"; releaseDirectory: ROPE _ NIL; releaseHost: ROPE _ NIL; unDir: ROPE _ "[Indigo]"; ris: STREAM = IO.RIS[cmd.commandLine]; sourceMapName,symbolsMapName: ROPE _ NIL; bcdCache: ReleaseToolVerify.BcdCache _ NIL; Cleanup: PROC = { IF bcdCache # NIL THEN {ReleaseToolVerify.FlushBcdCache[bcdCache]; bcdCache _ NIL}; IF inStream # NIL THEN {IO.Close[inStream]; inStream _ NIL}; IF inV # NIL THEN {IO.Close[inV]; inV _ NIL}; IF outV # NIL THEN {IO.Close[outV]; outV _ NIL}; }; TimedMessage: PROC [msg: ROPE] = { IO.PutF[outV, msg, [time[BasicTime.Now[]]]]; }; WITH cmd.procData.clientData SELECT FROM list: LIST OF REF ANY => { WHILE list # NIL DO SELECT list.first FROM $debug => debugging _ TRUE; $move => moving _ TRUE; ENDCASE; list _ list.rest; ENDLOOP; }; ENDCASE; <> <> unDir _ IO.GetTokenRope[ris, IO.IDProc ! IO.EndOfStream => CONTINUE].token; dfFile _ DefaultExtension[ IO.GetTokenRope[ris, IO.IDProc ! IO.EndOfStream => CONTINUE].token, ".df"]; mapPrefix _ IO.GetTokenRope[ris, IO.IDProc ! IO.EndOfStream => CONTINUE].token; releaseHost _ IO.GetTokenRope[ris, IO.IDProc ! IO.EndOfStream => CONTINUE].token; releaseDirectory _ IO.GetTokenRope[ris, IO.IDProc ! IO.EndOfStream => CONTINUE].token; inStream _ FS.StreamOpen[dfFile]; [inV,outV] _ ViewerIO.CreateViewerStreams[ "UnReleaseTool.log", NIL, "UnReleaseTool.log", FALSE]; {ENABLE UNWIND => { TimedMessage["\n\n**** Aborting at %g ****\n"]; Cleanup[]; }; IF moving THEN { list: DFTableEntryList _ NIL; TimedMessage["\n\nFile Moving Phase of UnReleaseTool starting at %g\n"]; [dfTable, list] _ BuildDFTable[dfFile, outV, unDir]; TimedMessage["\nDF table built at %g\n"]; MoveFiles[dfTable, list, outV, unDir, debugging]; TimedMessage["\n\nFile Moving Phase of UnReleaseTool ending at %g\n"]; }; }; Cleanup[]; }; showInfo: BOOL _ FALSE; Interact: DFOperations.InteractionProc = { <<[interaction: REF, clientData: REF]>> <> out: STREAM = NARROW[clientData]; IF showInfo THEN WITH interaction SELECT FROM info: REF DFOperations.InfoInteraction => { IO.PutRope[out, info.message]; }; dfInfo: REF DFOperations.DFInfoInteraction => { IO.PutRope[out, dfInfo.message]; }; abort: REF DFOperations.AbortInteraction => { Process.CheckForAbort[]; }; ENDCASE; }; DefaultExtension: PROC [name: ROPE, ext: ROPE] RETURNS [ROPE] = { len: INT = Rope.Length[name]; eLen: INT = Rope.Length[ext]; pos, bang, dot: INT _ len; WHILE pos > 0 DO posM: INT = pos-1; SELECT Rope.Fetch[name, posM] FROM '! => bang _ dot _ posM; '. => dot _ posM; '>, '/, '] => EXIT; ENDCASE; pos _ posM; ENDLOOP; IF bang = len AND (bang-dot # eLen OR Rope.Run[name, dot, ext, 0, FALSE] # eLen) THEN name _ Rope.Concat[name, ext]; RETURN [name]; }; DFTableEntry: TYPE = REF DFTableEntryRep; DFTableEntryList: TYPE = LIST OF DFTableEntry; DFTableEntryRep: TYPE = RECORD [ sourceName: ROPE, tempName: ROPE, destName: ROPE, time: BasicTime.GMT ]; BuildDFTable: PROC [topDF: ROPE, log: STREAM, unDir: ROPE _ NIL] RETURNS [tab: SymTab.Ref _ NIL, list: DFTableEntryList _ NIL] = { <> now: BasicTime.GMT = BasicTime.Now[]; index: INT _ 0; tail: DFTableEntryList _ NIL; EachDF: PROC [dfName: ROPE, dfDate: Date _ []] = { EachDFPass[dfName, dfDate]; }; EachDFPass: PROC [dfName: ROPE, dfDate: Date _ []] = { inStream: STREAM _ NIL; currentDir: REF DirectoryItem _ NIL; EachItem: DFUtilities.ProcessItemProc = { stop _ FALSE; WITH item SELECT FROM dirItem: REF DirectoryItem => { currentDir _ dirItem; }; inclItem: REF IncludeItem => { EachDF[inclItem.path1, inclItem.date]; }; ENDCASE; }; Cleanup: PROC = { IF inStream # NIL THEN {IO.Close[inStream]; inStream _ NIL}; }; <> entry: DFTableEntry _ NIL; stripped: ROPE = RemoveVersion[dfName]; realName: ROPE _ NIL; key: ROPE _ NIL; [realName, dfDate] _ MakeExplicit[dfName, dfDate]; IF realName = NIL THEN { IO.PutF[log, "\n**** DF file not found: %g", [rope[dfName]]]; GO TO skipIt }; inStream _ FS.StreamOpen[dfName _ realName]; key _ RemoveVersion[dfName]; WITH SymTab.Fetch[tab, key].val SELECT FROM e: DFTableEntry => { <> entry _ e; }; ENDCASE => { <> destName: ROPE _ dfName; tempName: ROPE _ Rope.Concat["Temp.", ShortName[dfName]]; IF unDir # NIL THEN { destName _ Rope.Concat[unDir, RemoveRoot[destName]]; }; entry _ NEW[DFTableEntryRep _ [ sourceName: key, tempName: tempName, destName: destName, time: BasicTime.Update[now, index] ]]; IO.PutF[log, "\nDF table entry for: %g", [rope[dfName]]]; IO.PutF[log, "\n (temp: %g, dest: %g)", [rope[tempName]], [rope[destName]]]; index _ index + 1; IF tail = NIL THEN list _ tail _ LIST[entry] ELSE {tail.rest _ LIST[entry]; tail _ tail.rest}; [] _ SymTab.Store[tab, key, entry]; }; {ENABLE UNWIND => Cleanup[]; filter: Filter _ [filterA: source, filterB: public, filterC: defining]; <> DFUtilities.ParseFromStream[ inStream, EachItem, filter ! DFUtilities.SyntaxError => { IO.PutF[log, "\n**** Syntax Error: %g", [rope[reason]]]; CONTINUE; }]; }; Cleanup[]; EXITS skipIt => {}; }; tab _ SymTab.Create[151, FALSE]; EachDF[topDF]; }; MakeExplicit: PROC [name: ROPE, date: Date] RETURNS [realName: ROPE _ NIL, realDate: Date] = { realDate.format _ explicit; [fullFName: realName, created: realDate.gmt] _ FS.FileInfo[name, date.gmt ! FS.Error => IF error.group # bug THEN CONTINUE]; IF realName = NIL THEN [fullFName: realName, created: realDate.gmt] _ FS.FileInfo[RemoveVersion[name], date.gmt ! FS.Error => IF error.group # bug THEN CONTINUE]; }; RemoveVersion: PROC [name: ROPE] RETURNS [ROPE] = { pos: INT _ Rope.Length[name]; WHILE (pos _ pos - 1) > 0 DO SELECT Rope.Fetch[name, pos] FROM '! => RETURN [Rope.Flatten[name, 0, pos]]; '>, '], '. => EXIT; ENDCASE; ENDLOOP; RETURN [name]; }; ShortName: PROC [name: ROPE] RETURNS [ROPE] = { pos: INT _ Rope.Length[name]; bang: INT _ Rope.Length[name]; WHILE (pos _ pos - 1) > 0 DO SELECT Rope.Fetch[name, pos] FROM '! => bang _ pos; '>, '] => RETURN [Rope.Flatten[name, pos+1, bang-pos-1]]; ENDCASE; ENDLOOP; RETURN [Rope.Flatten[name, 0, bang]]; }; RemoveRoot: PROC [name: ROPE] RETURNS [ROPE] = { pos: INT _ Rope.SkipTo[name _ RemoveVersion[name], 0, ">"]; IF pos = Rope.Length[name] THEN RETURN [name]; RETURN [Rope.Flatten[name, pos+1]]; }; GetPrefix: PROC [fileName: ROPE] RETURNS [ROPE] = { pos: INT _ Rope.Length[fileName]; WHILE pos > 0 DO pos _ pos - 1; SELECT Rope.Fetch[fileName, pos] FROM '], '> => EXIT; ENDCASE; ENDLOOP; IF pos = 0 THEN RETURN [NIL]; RETURN [Rope.Flatten[fileName, 0, pos+1]]; }; FindDFName: PROC [table: SymTab.Ref, name: ROPE, date: Date] RETURNS [destName: ROPE, destDate: Date, tempName: ROPE _ NIL] = { destName _ RemoveVersion[name]; destDate _ date; WITH SymTab.Fetch[table, destName].val SELECT FROM entry: DFTableEntry => { destName _ entry.destName; destDate.format _ explicit; destDate.gmt _ entry.time; tempName _ entry.tempName; }; ENDCASE; }; FindNonDF: PROC [table: SymTab.Ref, name: ROPE, date: Date] RETURNS [realName: ROPE _ NIL, realDate: Date _ []] = { stripped: ROPE _ RemoveVersion[name]; WITH SymTab.Fetch[table, stripped].val SELECT FROM entry: DFTableEntry => { <> }; ENDCASE => { [realName, realDate] _ MakeExplicit[name, date]; }; }; MoveFiles: PROC [table: SymTab.Ref, list: DFTableEntryList, log: STREAM, unDir: ROPE, debugging: BOOL] = { totalCount: INT _ 0; FOR each: DFTableEntryList _ list, each.rest WHILE each # NIL DO totalCount _ MoveDFContents[each.first.sourceName, table, log, unDir, totalCount, debugging]; ENDLOOP; FOR each: DFTableEntryList _ list, each.rest WHILE each # NIL DO totalCount _ MoveDFTemp[each.first, log, totalCount, debugging]; ENDLOOP; }; MoveDFContents: PROC [dfName: ROPE, table: SymTab.Ref, log: STREAM, unDir: ROPE, count: INT, debugging: BOOL] RETURNS [INT _ 0] = { entry: DFTableEntry _ NIL; inStream,outStream: STREAM _ NIL; currentDirItem: REF DirectoryItem; forcedDirItem: REF DirectoryItem; currentSourcePrefix: ROPE _ NIL; listHead,listTail: LORA _ NIL; lastDirItem: LORA _ NIL; systemPrefix: ROPE = DefaultRemoteNames.Get[].current; systemPrefixLen: INT = Rope.Length[systemPrefix]; movingThisDir: BOOL _ FALSE; AddToList: PROC [item: REF] = { <> new: LORA _ LIST[item]; IF listTail = NIL THEN listHead _ new ELSE listTail.rest _ new; listTail _ new; WITH item SELECT FROM dirItem: REF DirectoryItem => { IF lastDirItem # NIL THEN { <> lastDirItem.first _ item; lastDirItem.rest _ NIL; listTail _ lastDirItem; } ELSE lastDirItem _ new; }; file: REF FileItem => { lastDirItem _ NIL }; imports: REF ImportsItem => { lastDirItem _ NIL }; include: REF IncludeItem => { lastDirItem _ NIL }; ENDCASE; IF lastDirItem = NIL THEN FlushList[]; }; FlushList: PROC = { <> FOR each: LORA _ listHead, each.rest WHILE each # NIL DO IF each # NIL THEN DFUtilities.WriteItemToStream[outStream, each.first]; ENDLOOP; <> listTail _ NIL; WHILE listHead # NIL DO lag: LORA _ listHead; listHead _ lag.rest; lag.rest _ NIL; ENDLOOP; }; EachItem: DFUtilities.ProcessItemProc = { WITH item SELECT FROM dirItem: REF DirectoryItem => { currentSourcePrefix _ dirItem.path1; dirItem.path2 _ NIL; dirItem.path2IsCameFrom _ FALSE; IF unDir # NIL THEN { dirItem.path2 _ Rope.Concat[unDir, RemoveRoot[currentSourcePrefix]]; }; currentDirItem _ dirItem; IF copyAll THEN movingThisDir _ TRUE ELSE movingThisDir _ Rope.Run[currentSourcePrefix, 0, systemPrefix, 0, FALSE] # systemPrefixLen; IF movingThisDir THEN { <> dirItem.path1 _ dirItem.path2; }; }; file: REF FileItem => { date: Date _ file.date; sourceName: ROPE _ Rope.Concat[currentSourcePrefix, file.name]; noVersion: ROPE _ file.name _ RemoveVersion[file.name]; destExists: BOOL _ FALSE; realName,tempName: ROPE _ NIL; [realName, date] _ FindNonDF[table, sourceName, file.date]; IF forcedDirItem # NIL THEN { AddToList[currentDirItem]; forcedDirItem _ NIL; }; IF realName = NIL THEN { destName: ROPE _ NIL; newPrefix: ROPE _ NIL; <> [destName, date, tempName] _ FindDFName[table, sourceName, date]; IF NOT Rope.Match["*.df", destName, FALSE] THEN { IO.PutF[log, "\n**** File not found: %g", [rope[sourceName]]]; GO TO skip1 }; IO.PutF[log, "\nFuture copy %g (%g)\n to %g", [rope[sourceName]], [rope[tempName]], [rope[destName]]]; file.date _ date; newPrefix _ GetPrefix[destName]; IF newPrefix # NIL THEN { <> forcedDirItem _ NEW[DirectoryItem _ [ path1: newPrefix, path2: newPrefix, path2IsCameFrom: FALSE, exported: TRUE, readOnly: FALSE]]; AddToList[forcedDirItem]; }; GO TO skip1; }; <> file.date _ date; IF movingThisDir THEN { newName: ROPE _ Rope.Concat[unDir, RemoveRoot[realName]]; FileCopy[realName, date, newName, log, debugging]; }; MessageWindow.Append[ IO.PutFR[" %g files checked.", [integer[count _ count + 1]]], TRUE]; EXITS skip1 => {}; }; imports: REF ImportsItem => { destName, tempName: ROPE; destDate: Date; [destName, destDate, tempName] _ FindDFName[table, imports.path1, imports.date]; IF tempName = NIL THEN { <> [destName, destDate] _ MakeExplicit[imports.path1, imports.date]; IF destName = NIL THEN IO.PutF[log, "\n**** File not found: %g", [rope[imports.path1]]] ELSE {imports.path1 _ destName; imports.date _ destDate}; } ELSE { <> imports.path2 _ NIL; imports.path1 _ destName; imports.date _ [format: notEqual]; }; }; include: REF IncludeItem => { destName, tempName: ROPE; destDate: Date; [destName, destDate, tempName] _ FindDFName[table, include.path1, include.date]; IF tempName = NIL THEN { <> [destName, destDate] _ MakeExplicit[include.path1, include.date]; IF destName = NIL THEN IO.PutF[log, "\n**** File not found: %g", [rope[include.path1]]] ELSE {include.path1 _ destName; include.date _ destDate}; } ELSE { <> include.path2IsCameFrom _ FALSE; include.path2 _ destName; include.path1 _ destName; include.date _ [format: notEqual]; }; }; ENDCASE; AddToList[item] }; WITH SymTab.Fetch[table, dfName].val SELECT FROM e: DFTableEntry => entry _ e; ENDCASE => { IO.PutF[log, "\n**** DF file not in table: %g", [rope[dfName]]]; GO TO quit }; inStream _ FS.StreamOpen[dfName ! FS.Error => IF error.group # bug THEN { IO.PutF[log, "\n**** DF file not found: %g", [rope[error.explanation]]]; GO TO quit }; ]; outStream _ FS.StreamOpen[entry.tempName, create ! FS.Error => IF error.group # bug THEN { IO.PutF[log, "\n**** temp DF file not opened: %g", [rope[error.explanation]]]; IO.Close[inStream]; GO TO quit }; ]; IO.PutF[log, "\n\nMoving contents of %g (%g)", [rope[dfName]], [rope[entry.tempName]]]; DFUtilities.ParseFromStream[inStream, EachItem, [comments: TRUE] ! DFUtilities.SyntaxError => { IO.PutF[log, "\n**** Syntax Error: %g", [rope[reason]]]; CONTINUE; }]; FlushList[]; IO.Close[inStream]; FS.SetByteCountAndCreatedTime[FS.OpenFileFromStream[outStream], -1, entry.time]; IO.Close[outStream]; RETURN [count]; EXITS quit => {}; }; MoveDFTemp: PROC [entry: DFTableEntry, log: STREAM, count: INT, debugging: BOOL] RETURNS [INT] = { IO.PutF[log, "\nCopy %g (%g)\n to %g", [rope[entry.sourceName]], [rope[entry.tempName]], [rope[entry.destName]]]; IF debugging THEN IO.PutRope[log, "\n (not copied, debugging)"] ELSE [] _ FS.Copy[entry.tempName, entry.destName ! FS.Error => IF error.group # bug THEN { IO.PutF[log, "\n**** Copy failed: %g", [rope[error.explanation]]]; CONTINUE; }; ]; MessageWindow.Append[ IO.PutFR[" %g files moved.", [integer[count _ count + 1]]], TRUE]; RETURN [count]; }; FileCopy: PROC [sourceName: ROPE, date: Date, destName: ROPE, log: STREAM, debugging: BOOL] = { tempName: ROPE _ "UnReleaseTool.Temp$"; gmt: BasicTime.GMT; realDestName: ROPE _ NIL; [fullFName: realDestName, created: gmt] _ FS.FileInfo[destName, date.gmt ! FS.Error => IF error.group # bug THEN CONTINUE]; IO.PutF[log, "\nCopy %g\n to %g", [rope[sourceName]], [rope[destName]]]; IF realDestName # NIL THEN { IO.PutRope[log, "\n (not copied, already exists)"]; RETURN; }; IF date.gmt # BasicTime.nullGMT AND ReleaseToolVerify.IsInFileCache[sourceName, date] THEN { <> tempName _ sourceName; IO.PutRope[log, " (from cache) "]; } ELSE { <> IF NOT debugging THEN tempName _ FS.Copy[sourceName, tempName ! FS.Error => IF error.group # bug THEN { IO.PutF[log, "\n**** Copy failed: %g", [rope[error.explanation]]]; GO TO bugOut; }; ]; }; IF debugging THEN { <> IO.PutRope[log, "\n (not copied, debugging)"]; } ELSE { <> destName _ FS.Copy[tempName, destName ! FS.Error => IF error.group # bug THEN { IO.PutF[log, "\n**** Copy failed: %g", [rope[error.explanation]]]; GO TO bugOut; }]; }; EXITS bugOut => {}; }; Commander.Register[ "TestUnRelease", UnReleaseToolCommand, "tests the file moving phase of the UnReleaseTool without moving files. The command line has, in order (all optional): ", LIST[$debug, $move]]; Commander.Register[ "UnRelease", UnReleaseToolCommand, "moves the files in a release to the prerelease directory. We assume that the version maps are valid, and that the files have been verified (although no state is retained between verification and moving). The command line has, in order (all optional): ", LIST[$move]]; END.