CopyFromVersionMapImpl.mesa
Copyright © 1985 by Xerox Corporation. All rights reserved.
Russ Atkinson (RRA) April 16, 1986 11:48:03 pm PST
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.0>VersionMap>Cedar";
cedarChestPrefix: PUBLIC ROPE ← "[Cedar]<CedarChest6.0>VersionMap>CedarChest";
daToolsPrefix: PUBLIC ROPE ← "[DATools]<DATools6.0>DAWorldAdministration>DAWorldMap";
ncpPrefix: PUBLIC ROPE ← "[Cyan]<NCP>CurrentNCP";
CopyCommandProc: Commander.CommandProc = {
[cmd: Handle] RETURNS [result: REF ← NIL, msg: ROPE ← NIL]
CommandObject = [in, out, err: STREAM, commandLine, command: ROPE, ...]
out: STREAM = cmd.out;
switches: Switches ← ALL[FALSE];
ProcessSwitches:
PROC [arg:
ROPE] = {
sense: BOOL ← TRUE;
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 ← cedarChestPrefix;
switches['d] => mapPrefix ← daToolsPrefix;
switches['n] => mapPrefix ← ncpPrefix;
switches['r] => mapPrefix ← 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:
BOOL ←
FALSE, doit:
BOOL ←
TRUE, zapOlder:
BOOL ←
FALSE] = {
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:
STREAM ←
NIL, verbose, doit, zapOlder:
BOOL] = {
foundFiles: INT ← 0;
attachedFiles: INT ← 0;
map: VersionMap.Map ← NIL;
maxNameLen: NAT = 128;
nameText: REF TEXT ← NEW[TEXT[maxNameLen]];
fullNameText: REF TEXT ← NEW[TEXT[maxNameLen]];
mapPrefix: ROPE ← NIL;
prefix: ROPE ← FS.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: BOOL ← FALSE;
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:
BOOL ←
FALSE, stop:
BOOL ←
TRUE] =
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: BOOL ← FALSE;
dateWarning: BOOL ← FALSE;
localWarning: BOOL ← FALSE;
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.