TiogaFileWatcherImpl.mesa
Last edited by Doug Wyatt, December 15, 1983 7:15 pm
DIRECTORY
FS USING [ComponentPositions, Error, ExpandName],
FSExtras USING [CreateEvent, NextCreateEvent],
MessageWindow USING [Append, Blink],
Process USING [Detach],
Rope USING [Cat, Equal, Fetch, Length, ROPE, Run],
TEditDocumentPrivate USING [DoLoadFile],
ViewerOps USING [EnumerateViewers, EnumProc];
TiogaFileWatcherImpl: CEDAR PROGRAM
IMPORTS FS, FSExtras, MessageWindow, Process, Rope, TEditDocumentPrivate, ViewerOps
= BEGIN
ROPE: TYPE = Rope.ROPE;
highestVersion: CARDINAL ~ CARDINAL.LAST;
ExtensionType: TYPE = {nil, tip, style};
Info: TYPE = RECORD[
name: ROPENIL, -- full FName
len: INT ← 0, -- length of name, omitting version part
version: CARDINAL ← highestVersion, -- version number
type: ExtensionType ← nil -- extension type
];
ParseFileName: PROC[name: ROPE] RETURNS[info: Info ← []] = {
cp: FS.ComponentPositions;
[fullFName: name, cp: cp] ← FS.ExpandName[name ! FS.Error => GOTO Fail];
info.name ← name; -- full name
info.len ← cp.ext.start+cp.ext.length; -- length up to end of extension
IF cp.ver.length>0 THEN { -- parse version number
start: INT ~ cp.ver.start;
len: INT ~ cp.ver.length;
version: CARDINAL ← 0;
FOR i: INT IN[start..start+len) DO
version ← version*10+(name.Fetch[i]-'0);
ENDLOOP;
info.version ← version;
};
IF cp.ext.length>0 THEN { -- look for special extensions
start: INT ~ cp.ext.start;
len: INT ~ cp.ext.length;
MatchExtension: PROC[x: ROPE] RETURNS[BOOL] = {
RETURN[Rope.Length[x]=len AND Rope.Run[s1: name, pos1: start, s2: x, case: FALSE]=len]
};
SELECT TRUE FROM
MatchExtension["tip"] => info.type ← tip;
MatchExtension["style"] => info.type ← style;
ENDCASE;
};
EXITS Fail => RETURN;
};
NamesMatch: PROC[a, b: Info] RETURNS[BOOL] = {
RETURN[a.len=b.len AND Rope.Run[s1: a.name, s2: b.name, case: FALSE]>=a.len];
};
stop: BOOLFALSE;
TiogaFileWatcher: PROC = {
event: REF READONLY FSExtras.CreateEvent ← NIL;
old, new: Info;
DO
editingOldVersion: BOOLFALSE;
action: ViewerOps.EnumProc -- [v: Viewer] -- = {
IF v.class.flavor#$Text THEN RETURN;
Crock: if v.saveInProgress, SaveViewer may have just written the file but not fixed up v.file and v.newVersion yet. Since SaveViewer doesn't lock the viewer there seems to be no good way to synchronize this properly.
IF v.destroyed OR v.saveInProgress THEN RETURN;
Assume the following convention for v.name and v.file in a $Text viewer v:
v.file always contains the full FName obtained from FS.GetName
If an explicit version was requested, then v.name matches v.file; otherwise, v.name contains v.file with the version part omitted.
IF Rope.Equal[s1: v.name, s2: v.file, case: FALSE] THEN RETURN; -- explicit version
old ← ParseFileName[v.file];
IF NamesMatch[old, new] AND new.version>old.version THEN {
IF v.destroyed THEN NULL
ELSE IF v.newVersion THEN editingOldVersion ← TRUE
ELSE [] ← TEditDocumentPrivate.DoLoadFile[parent: v, fileName: v.name];
};
};
event ← FSExtras.NextCreateEvent[event];
IF stop THEN EXIT;
new ← ParseFileName[event.fName];
SELECT new.type FROM
tip => xxx;
style => xxx;
ENDCASE;
ViewerOps.EnumerateViewers[action];
IF editingOldVersion THEN {
MessageWindow.Append[message: Rope.Cat[
"Warning: ", new.name, " was created while you were editing an older version."],
clearFirst: TRUE];
MessageWindow.Blink[];
};
ENDLOOP;
};
Start: PROC = {
stop ← FALSE;
TRUSTED { Process.Detach[FORK TiogaFileWatcher] };
};
Stop: PROC = { stop ← TRUE };
Start[];
END.