CopyFromVersionMapImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Russ Atkinson (RRA) June 9, 1986 12:01:37 pm PDT
Spreitzer, April 24, 1986 12:05:36 pm PST
DIRECTORY
BasicTime USING [Period],
Commander USING [CommandProc, Register],
CommandTool USING [ArgumentVector, Failed, Parse],
CopyFromVersionMap,
FS USING [Copy, Error, ExpandName],
FSBackdoor USING [Entry, EntryPtr, Enumerate, highestVersion, TextRep],
IO USING [PutF, PutF1, PutRope, STREAM],
Process USING [CheckForAbort],
RefText USING [TrustTextAsRope],
Rope USING [AppendChars, Concat, Fetch, Flatten, FromRefText, Length, ROPE],
UserProfile USING [Token],
VersionMap USING [FetchName, GetPrefix, Map, MapEntry, RestoreMapFromFile];
CopyFromVersionMapImpl: CEDAR PROGRAM
IMPORTS BasicTime, Commander, CommandTool, FS, FSBackdoor, IO, Process, RefText, Rope, UserProfile, VersionMap
EXPORTS CopyFromVersionMap
SHARES VersionMap
= BEGIN
ROPE: TYPE = Rope.ROPE;
STREAM: TYPE = IO.STREAM;
Switches: TYPE = PACKED ARRAY CHAR['a..'z] OF BOOL;
cedarPrefix: PUBLIC ROPE ← "[Cedar]<Cedar6.1>VersionMap>Cedar";
cedarChestPrefix: PUBLIC ROPE ← "[Cedar]<CedarChest6.1>VersionMap>CedarChest";
daToolsPrefix: PUBLIC ROPE ← "[DATools]<DATools6.1>DAWorldAdministration>DAWorldMap";
CopyCommandProc: Commander.CommandProc = {
[cmd: Handle] RETURNS [result: REFNIL, msg: ROPENIL]
CommandObject = [in, out, err: STREAM, commandLine, command: ROPE, ...]
out: STREAM = cmd.out;
switches: Switches ← ALL[FALSE];
ProcessSwitches: PROC [arg: ROPE] = {
sense: BOOLTRUE;
FOR index: INT IN [0..Rope.Length[arg]) DO
char: CHAR ← Rope.Fetch[arg, index];
SELECT char FROM
'- => LOOP;
'~ => {sense ← NOT sense; LOOP};
IN ['a..'z] => switches[char] ← sense;
IN ['A..'Z] => switches[char + ('a-'A)] ← sense;
ENDCASE;
sense ← TRUE;
ENDLOOP;
};
ProcessArgument: PROC [arg: ROPE] = {
mapPrefix: ROPE ← arg;
Process.CheckForAbort[];
IF mapPrefix = NIL THEN
Try for some well-known prefixes
SELECT TRUE FROM
switches['c] =>
mapPrefix ← UserProfile.Token["CopyFromVersionMap.CedarChestPrefix", cedarChestPrefix];
switches['d] =>
mapPrefix ← UserProfile.Token["CopyFromVersionMap.DAToolsPrefix", daToolsPrefix];
switches['r] =>
mapPrefix ← UserProfile.Token["CopyFromVersionMap.CedarPrefix", cedarPrefix];
switches['u] =>
mapPrefix ← UserProfile.Token["CopyFromVersionMap.DefaultPrefix"];
ENDCASE;
IF mapPrefix = NIL THEN {msg ← "No map prefix seen."; RETURN};
argsProcessed ← argsProcessed + 1;
IF switches['w] THEN IO.PutRope[cmd.out, "-- Warnings only, no files to be attached.\n"];
Copy[log: cmd.out, mapPrefix: mapPrefix, verbose: switches['v], doit: NOT switches['w], zapOlder: switches['z]];
};
argsProcessed: NAT ← 0;
# of arguments processed
argv: CommandTool.ArgumentVector ← CommandTool.Parse[cmd: cmd, starExpand: FALSE
! CommandTool.Failed => {msg ← errorMsg; GO TO failed}];
When parsing the command line, be prepared for failure. The error is reported to the user
ProcessSwitches[UserProfile.Token["CopyFromVersionMap.DefaultSwitches"]];
Allows the user to specify personal defaults for the switches via the user's profile.
FOR i: NAT IN [1..argv.argc) DO
Each argument can either be a switch specification or a genuine argument to be processed. The first argument (argv[0]) is not examined, because by convention it is the name of the command as given by the user.
arg: ROPE = argv[i];
Process.CheckForAbort[];
It is a good idea to periodically check for a process abort request.
IF Rope.Length[arg] = 0 THEN LOOP;
Ignore null arguments (it is not easy to generate them, even).
IF Rope.Fetch[arg, 0] = '- THEN {
This argument sets switches for the remaining patterns. By convention, switches are normally "sticky", in that they stay set until explicitly changed.
ProcessSwitches[arg];
LOOP;
};
ProcessArgument[arg ! FS.Error => {msg ← error.explanation; GO TO failed}];
Perform whatever processing is necessary for a normal argument.
ENDLOOP;
IF argsProcessed = 0 THEN
The prefix must default
ProcessArgument[NIL ! FS.Error => {msg ← error.explanation; GO TO failed}];
IF argsProcessed = 0 THEN GO TO failed;
EXITS
failed => {result ← $Failure};
};
Copy: PUBLIC PROC [log: IO.STREAM, mapPrefix: ROPE, verbose: BOOLFALSE, doit: BOOLTRUE, zapOlder: BOOLFALSE] = {
EnumerateVersionMap[Rope.Concat[mapPrefix, "Source.VersionMap"], log, verbose, doit, zapOlder];
EnumerateVersionMap[Rope.Concat[mapPrefix, "Symbols.VersionMap"], log, verbose, doit, zapOlder];
};
EnumerateVersionMap: PROC [mapName: ROPE, st: STREAMNIL, verbose, doit, zapOlder: BOOL] = {
foundFiles: INT ← 0;
attachedFiles: INT ← 0;
map: VersionMap.Map ← NIL;
maxNameLen: NAT = 128;
nameText: REF TEXTNEW[TEXT[maxNameLen]];
fullNameText: REF TEXTNEW[TEXT[maxNameLen]];
mapPrefix: ROPENIL;
prefix: ROPEFS.ExpandName["$"].fullFName;
dstInit: NAT = Rope.Length[prefix]-5;
(don't count the "[]<>" or the "$")
nameText.length ← 0;
Just to be careful
fullNameText.length ← 0;
Just to be careful
[] ← Rope.AppendChars[nameText, prefix, 4, dstInit];
transfer the working directory into the start of the name buffer
IO.PutF1[st, "Working on %g ", [rope[mapName]] ];
map ← VersionMap.RestoreMapFromFile[mapName];
IO.PutF1[st, " (files: %g)\n", [integer[map.len]] ];
mapPrefix ← VersionMap.GetPrefix[map];
FOR mapIndex: NAT IN [0..map.len) DO
mapEntry: VersionMap.MapEntry = map[mapIndex];
mapVersion: NAT ← 0;
inVersion: BOOLFALSE;
dst: NAT ← dstInit;
FOR pos: INT ← mapEntry.index, pos+1 DO
c: CHAR ← Rope.Fetch[map.names, pos];
SELECT c FROM
'/, '> => {dst ← dstInit; LOOP};
'! => {inVersion ← TRUE; LOOP};
'\n => EXIT;
ENDCASE;
SELECT TRUE FROM
inVersion => IF c IN ['0..'9] AND mapVersion <= (LAST[NAT]-9)/10 THEN {
mapVersion ← mapVersion*10 + (c-'0);
LOOP;
};
dst < maxNameLen => {nameText[dst] ← c; dst ← dst + 1};
ENDCASE;
ENDLOOP;
nameText.length ← dst;
IF dst # dstInit THEN {
We have a valid short name, so look it up
matchProc: UNSAFE PROC [entry: FSBackdoor.EntryPtr] RETURNS [accept: BOOLFALSE, stop: BOOLTRUE] = TRUSTED {
WITH entry SELECT FROM
attached: LONG POINTER TO attached FSBackdoor.Entry => {
IF attached.created = mapEntry.created THEN {
Probably have found the appropriate entry, but we should look at the full names to make sure of the proper attachment.
attachedText: LONG POINTER TO FSBackdoor.TextRep = @entry[attached.attachedTo];
fDst: NAT ← 0;
IF NOT zapOlder THEN
we should not zap if attached to a later file
IF BasicTime.Period[from: attached.created, to: mapEntry.created] < 0 THEN {
indicate that we will not attach this one
found ← dateWarning ← TRUE;
RETURN;
};
fullNameText.length ← 0;
Just to be careful
IF Rope.Fetch[map.names, mapEntry.index] # '[ THEN {
[] ← Rope.AppendChars[fullNameText, mapPrefix];
fDst ← fullNameText.length;
};
FOR pos: INT ← mapEntry.index, pos+1 DO
c: CHAR ← Rope.Fetch[map.names, pos];
SELECT c FROM
'! => IF mapVersion = 0 THEN EXIT;
'\n => EXIT;
ENDCASE;
IF fDst < maxNameLen THEN {fullNameText[fDst] ← c; fDst ← fDst+1};
ENDLOOP;
fullNameText.length ← fDst;
FOR i: NAT IN [0..fDst) DO
IF attachedText[i] # fullNameText[i] THEN RETURN;
ENDLOOP;
found ← TRUE;
};
RETURN;
};
ENDCASE;
At this point we have a local name match
IF NOT zapOlder THEN {
we should not zap if attached to a local file
found ← localWarning ← TRUE;
RETURN;
};
};
found: BOOLFALSE;
dateWarning: BOOLFALSE;
localWarning: BOOLFALSE;
Process.CheckForAbort[];
FSBackdoor.Enumerate[volName: NIL, nameBodyPattern: RefText.TrustTextAsRope[nameText], localOnly: TRUE, allVersions: FALSE, version: FSBackdoor.highestVersion, matchProc: matchProc, acceptProc: NIL];
SELECT TRUE FROM
dateWarning => {
IO.PutF1[st, "File not attached, local version more recent.\n %g\n", [rope[VersionMap.FetchName[map, mapIndex]]]];
};
localWarning =>
IO.PutF1[st, "File not attached, unattached local version exists.\n %g\n", [rope[VersionMap.FetchName[map, mapIndex]]]];
found =>
foundFiles ← foundFiles + 1;
(NOT doit) AND NOT verbose => {
Short cut when warning and not verbose
};
ENDCASE => {
fullName: ROPE ← VersionMap.FetchName[map, mapIndex];
shortName: ROPE ← Rope.Flatten[Rope.FromRefText[nameText], dstInit];
IF verbose THEN {
Output the name & info
IO.PutF1[st, "%g\n", [rope[fullName]] ];
};
IF doit THEN {
[] ← FS.Copy[from: fullName, to: shortName, wantedCreatedTime: mapEntry.created, remoteCheck: FALSE, attach: TRUE];
attachedFiles ← attachedFiles + 1;
};
};
};
ENDLOOP;
IO.PutF[st, " foundFiles: %g, attachedFiles: %g\n",
[integer[foundFiles]], [integer[attachedFiles]] ];
};
doc: ROPE = "copy files from version maps
-c: CedarChest prefix default
-d: DATools prefix default
-n: NCP prefix default
-r: Cedar release prefix default
-v: verbose (list files to be attached)
-w: warning only (no files attached)
-z: zap files regardless of date
";
Commander.Register[
key: "///Commands/CopyFromVersionMap",
proc: CopyCommandProc,
doc: doc,
clientData: NIL,
interpreted: TRUE
];
END.