Last Edited by: teitelman, April 20, 1983 4:18 pm
DIRECTORY
Directory USING [Error, Lookup, ignore],
IO USING [PutF, Flush, RIS, ROPE, rope, STREAM, UserAborted, GetSequence, CharProc],
MessageWindow USING [Append],
Resource USING [AbortProc, Acquire, Release],
Rope USING [Cat, Concat, Fetch, Find, IsEmpty, Length, Replace, ROPE, Substr, ToRefText],
Spell USING [AbortProc, ConfirmProc, defaultModes, Filter, GetMatchingFileList, GetMatchingList, GetTheFile, GetTheOne, InformProc, IsAPattern, Modes, SpellingGenerator, SpellingList],
SpellExtras USING [GetFileExtension],
TiogaOps USING [RegisterFileNameProc],
UserExec USING [CheckForFile, HistoryEvent, ExecHandle, CheckForAbort, UserAbort, UserAborted, Confirm, GetExecHandle, GetUserResponse, SetupAskUser, FinishAskUser, GetTheOne, GetTheFile, RopeSubst, Viewer, AcquireStreams, ReleaseStreams],
UserExecExtras USING [CorrectionDisabled],
UserExecPrivate USING [],
UserProfile USING [Boolean, ProfileChangedProc, CallWhenProfileChanges],
ViewerAbort USING [UserAbort],
ViewerTools USING [GetSelectedViewer]
;
UserExecUtilImpl: CEDAR MONITOR
IMPORTS IO, Resource, Rope, MessageWindow, UserExec, TiogaOps, UserExecExtras, UserProfile, Directory, Spell, SpellExtras, ViewerAbort, ViewerTools
EXPORTS UserExec
= BEGIN OPEN IO;
Viewer: TYPE = UserExec.Viewer;
ExecHandle: TYPE = UserExec.ExecHandle;
HistoryEvent: TYPE = UserExec.HistoryEvent;
GetTheOne, GetTheFile, etc.
terminateCorrection: ERROR = CODE;
GetTheOne: PUBLIC PROCEDURE[
unknown: ROPE,
spellingList: Spell.SpellingList ← NIL,
generator: Spell.SpellingGenerator ← NIL,
event: HistoryEvent,
exec: ExecHandle,
viewer: Viewer ← NIL,
filter: Spell.Filter ← NIL,
modes: Spell.Modes ← NIL
]
RETURNS [correct: ROPENIL] = {
Abort: Spell.AbortProc = {
hasResponded: BOOL;
value: ATOM;
UserExec.CheckForAbort[exec];
IF viewer # NIL AND ViewerAbort.UserAbort[viewer] THEN IO.UserAborted[viewer];
IF viewer # NIL THEN [hasResponded, value] ← UserExec.GetUserResponse[viewer];
IF hasResponded AND value = $No THEN ERROR terminateCorrection;
};
Confirm: Spell.ConfirmProc -- [msg: ROPE, timeout: INT, defaultConfirm: BOOL] RETURNS[yes: BOOL] -- = {
RETURN[
UserExec.Confirm[msg: msg, timeout: timeout, defaultConfirm: defaultConfirm, exec: exec, viewer: viewer]
]
};
Inform: Spell.InformProc --[msg: ROPE] -- = {
IF exec # NIL THEN
{out ← UserExec.AcquireStreams[exec].out; -- since this can be called from random place, must acquire and release streams rather than using GetStreams
out.PutF["*n*m%g*s\n", rope[msg]];
}
ELSE MessageWindow.Append[message: msg, clearFirst: TRUE];
};
out: IO.STREAM;
IF Rope.IsEmpty[unknown] OR UserExecExtras.CorrectionDisabled[event] THEN RETURN[NIL];
{ENABLE UNWIND => Finish[exec, viewer, out];
IF exec # NIL THEN viewer ← exec.viewer;
IF viewer # NIL THEN UserExec.SetupAskUser[viewer];
correct ← Spell.GetTheOne[unknown: unknown, spellingList: spellingList, generator: generator, filter: filter, abort: Abort, confirm: Confirm, inform: Inform, modes: modes ! terminateCorrection => CONTINUE];
IF exec # NIL THEN FixCommandLine[old: unknown, new: correct, event: event, exec: exec];
Finish[exec, viewer, out];
};
};
GetMatchingList: PUBLIC PROCEDURE[
unknown: ROPE,
spellingList: Spell.SpellingList ← NIL,
generator: Spell.SpellingGenerator ← NIL,
event: HistoryEvent,
exec: ExecHandle,
viewer: Viewer ← NIL,
filter: Spell.Filter ← NIL,
modes: Spell.Modes ← NIL
]
RETURNS [matching: LIST OF ROPENIL] = {
Abort: Spell.AbortProc = {
hasResponded: BOOL;
value: ATOM;
UserExec.CheckForAbort[exec];
IF viewer # NIL AND ViewerAbort.UserAbort[viewer] THEN IO.UserAborted[viewer];
IF viewer # NIL THEN [hasResponded, value] ← UserExec.GetUserResponse[viewer];
IF hasResponded AND value = $No THEN ERROR terminateCorrection;
};
Confirm: Spell.ConfirmProc -- [msg: ROPE, timeout: INT, defaultConfirm: BOOL] RETURNS[yes: BOOL] -- = {
RETURN[
UserExec.Confirm[msg: msg, timeout: timeout, defaultConfirm: defaultConfirm, exec: exec, viewer: viewer]
]
};
Inform: Spell.InformProc --[msg: ROPE] -- = {
IF exec # NIL THEN
{out ← UserExec.AcquireStreams[exec].out; -- since this can be called from random place, must acquire and release streams rather than using GetStreams
out.PutF["*n*m%g*s\n", rope[msg]];
}
ELSE MessageWindow.Append[message: msg, clearFirst: TRUE];
};
out: IO.STREAM;
IF Rope.IsEmpty[unknown] THEN RETURN[NIL];
IF NOT Spell.IsAPattern[unknown] THEN
{val: ROPE ← UserExec.GetTheOne[unknown: unknown, spellingList: spellingList, generator: generator, event: event, exec: exec, filter: filter, modes: modes];
IF val # NIL THEN RETURN[LIST[val]]
ELSE RETURN[NIL];
};
{ENABLE UNWIND => Finish[exec, viewer, out];
IF exec # NIL THEN viewer ← exec.viewer;
IF viewer # NIL THEN {
IF Spell.defaultModes.confirm >= patternMatch THEN UserExec.SetupAskUser[viewer]
ELSE viewer ← NIL; -- no point in putting up yes/no if confirmation won't be needed
};
matching ← Spell.GetMatchingList[pattern: unknown, spellingList: spellingList, generator: generator, filter: filter, abort: Abort, confirm: Confirm, inform: Inform, modes: modes ! terminateCorrection => CONTINUE];
Finish[exec, viewer, out];
};
};
CheckForFile: PUBLIC PROC [file: ROPE] RETURNS [found: BOOLEAN] = TRUSTED { -- Directory
fName: LONG STRING;
fName ← LOOPHOLE[Rope.ToRefText[file]];
found ← TRUE;
IF Rope.Length[file] = 0 THEN RETURN[FALSE];
[] ← Directory.Lookup[fileName: fName, permissions: Directory.ignore
! Directory.Error =>
{found ← FALSE; CONTINUE}
];
};
GetTheFile: PUBLIC PROC [file: ROPE, defaultExt: ROPENIL, event: HistoryEvent, exec: ExecHandle, viewer: Viewer ← NIL, modes: Spell.Modes ← NIL] RETURNS [name: ROPENIL] = {
i: INT;
out: IO.STREAM;
IF Rope.IsEmpty[file] THEN RETURN[NIL];
{ENABLE UNWIND => Finish[exec, viewer, out];
{
IF (i ← Rope.Find[s1: file, s2: "/"]) # -1
THEN
{IF i # 0 THEN file ← Rope.Substr[base: file, len: i] -- ignore switches
ELSE IF Rope.Find[s1: file, s2: "/", pos1: 1] = -1 THEN GOTO FullPath
ELSE RETURN[NIL];
};
IF Rope.Find[file, "["] # -1 OR Rope.Find[file, "<"] # -1 THEN GOTO FullPath;
EXITS
FullPath => RETURN[NIL];
};
IF CheckForFile[file] THEN RETURN[file];
IF (i ← Rope.Find[file, "."]) = -1 AND Rope.Length[defaultExt] # 0 THEN name ← Rope.Cat[file, ".", defaultExt]
ELSE name ← file;
IF CheckForFile[name] THEN RETURN[name];
IF UserExecExtras.CorrectionDisabled[event] THEN RETURN[NIL];
{
Abort: Spell.AbortProc = {
hasResponded: BOOL;
value: ATOM;
UserExec.CheckForAbort[exec];
IF viewer # NIL AND ViewerAbort.UserAbort[viewer] THEN IO.UserAborted[viewer];
IF viewer # NIL THEN [hasResponded, value] ← UserExec.GetUserResponse[viewer];
IF hasResponded AND value = $No THEN ERROR terminateCorrection;
};
Confirm: Spell.ConfirmProc -- [msg: ROPE, timeout: INT, defaultConfirm: BOOL] RETURNS[yes: BOOL] -- = {
RETURN[
UserExec.Confirm[msg: msg, timeout: timeout, defaultConfirm: defaultConfirm, exec: exec, viewer: viewer]
]
};
Inform: Spell.InformProc -- [msg: ROPE] -- = {
IF exec # NIL THEN
{out ← UserExec.AcquireStreams[exec].out; -- since this can be called from random place, must acquire and release streams rather than using GetStreams
out.PutF["*n*m%g*s\n", rope[msg]];
}
ELSE MessageWindow.Append[message: msg, clearFirst: TRUE];
};
IF exec # NIL THEN viewer ← exec.viewer;
IF viewer # NIL THEN UserExec.SetupAskUser[viewer];
name ← Spell.GetTheFile[unknown: file, defaultExt: defaultExt, abort: Abort, confirm: Confirm, inform: Inform, modes: modes ! terminateCorrection => CONTINUE];
IF name # NIL THEN {
i: INT;
new: ROPE = IF (i ← Rope.Find[file, "."]) = -1 AND (i ← Rope.Find[name, "."]) # -1 THEN Rope.Substr[base: name, len: i] ELSE name; -- if original file did not have an extension and name does, strip it off for purposes of correcting history.
IF exec # NIL THEN
{FixCommandLine[old: file, new: new, event: event, exec: exec];
};
};
Finish[exec, viewer, out];
};
};
};
GetMatchingFileList: PUBLIC PROC [file: ROPE, defaultExt: ROPENIL, event: HistoryEvent, exec: ExecHandle, viewer: Viewer ← NIL, modes: Spell.Modes ← NIL] RETURNS [fileList: LIST OF ROPENIL] = {
out: IO.STREAM;
IF Rope.IsEmpty[file] THEN RETURN[NIL];
IF NOT Spell.IsAPattern[file] THEN
{val: ROPE = UserExec.GetTheFile[file: file, defaultExt: defaultExt, event: event, exec: exec];
IF val # NIL THEN RETURN[LIST[val]]
ELSE RETURN[NIL];
};
{
ENABLE UNWIND => Finish[exec, viewer, out];
Abort: Spell.AbortProc = {
hasResponded: BOOL;
value: ATOM;
UserExec.CheckForAbort[exec];
IF viewer # NIL AND ViewerAbort.UserAbort[viewer] THEN IO.UserAborted[viewer];
IF viewer # NIL THEN [hasResponded, value] ← UserExec.GetUserResponse[viewer];
IF hasResponded AND value = $No THEN ERROR terminateCorrection;
};
Confirm: Spell.ConfirmProc -- [msg: ROPE, timeout: INT, defaultConfirm: BOOL] RETURNS[yes: BOOL] -- = {
RETURN[
UserExec.Confirm[msg: msg, timeout: timeout, defaultConfirm: defaultConfirm, exec: exec, viewer: viewer]
]
};
Inform: Spell.InformProc --[msg: ROPE] -- = {
IF exec # NIL THEN
{out ← UserExec.AcquireStreams[exec].out; -- since this can be called from random place, must acquire and release streams rather than using GetStreams
out.PutF["*n*m%g*s\n", rope[msg]];
}
ELSE MessageWindow.Append[message: msg, clearFirst: TRUE];
};
IF exec # NIL THEN viewer ← exec.viewer;
IF viewer # NIL THEN {
IF Spell.defaultModes.confirm >= patternMatch THEN UserExec.SetupAskUser[viewer]
ELSE viewer ← NIL; -- no point in putting up yes/no if confirmation won't be needed
};
fileList ← Spell.GetMatchingFileList[unknown: file, defaultExt: defaultExt, abort: Abort, inform: Inform, confirm: Confirm, modes: modes ! terminateCorrection => CONTINUE];
Finish[exec, viewer, out];
};
};
Finish: PROCEDURE [exec: UserExec.ExecHandle, viewer: Viewer, out: STREAM] = {
IF out # NIL THEN UserExec.ReleaseStreams[exec];
IF viewer # NIL THEN UserExec.FinishAskUser[viewer];
};
FixCommandLine: PROC [old, new: ROPE, event: HistoryEvent, exec: UserExec.ExecHandle] = {
IF event # NIL AND new # NIL THEN {
event.input ← UserExec.RopeSubst[old, new, event.input];
event.commandLine ← UserExec.RopeSubst[old, new, event.commandLine];
[] ← IO.RIS[UserExec.RopeSubst[old, new, GetRestOfStream[event.commandLineStream]], event.commandLineStream];
};
};
GetRestOfStream: PROC [in: STREAM] RETURNS[ROPE] =
{charProc: IO.CharProc = {RETURN[FALSE, TRUE]};
RETURN[IO.GetSequence[in, charProc]];
};
RopeSubst: PUBLIC PROC [old, new, base: ROPE, case: BOOLEANFALSE, allOccurrences: BOOLEANTRUE] RETURNS[ROPE] = {
lenOld: INT = Rope.Length[old];
lenNew: INT = Rope.Length[new];
i: INT ← 0;
WHILE (i ← Rope.Find[s1: base, s2: old, case: case, pos1: i]) # -1 DO
base ← Rope.Replace[base: base, start: i, len: Rope.Length[old], with: new];
IF ~allOccurrences THEN EXIT;
i ← i + lenNew;
ENDLOOP;
RETURN[base];
};
File Correction
viewerSpellDisabled: BOOLEAN;
SetViewerSpell: UserProfile.ProfileChangedProc = {
viewerSpellDisabled ← UserProfile.Boolean["Spell.viewerSpellDisabled", FALSE];
};
CorrectFileName: PROC [unknown: ROPE, viewer: Viewer] RETURNS [fileName: ROPENIL, search: ROPENIL] = {
exec: UserExec.ExecHandle;
out: IO.STREAM;
name: ROPE;
dot: INT;
IF viewerSpellDisabled THEN RETURN;
FOR i: INT IN [0..Rope.Length[unknown]) DO
SELECT Rope.Fetch[unknown, i] FROM
'/, '[, '< => RETURN[NIL];
ENDCASE;
ENDLOOP;
name ← unknown;
note: tioga has already checked whether unknown in its entirety exists.
IF (dot ← Rope.Find[unknown, "."]) # -1 THEN -- something following the dot. either this is a search rope, or a misspelled extension.
{
True: Spell.ConfirmProc = {RETURN[TRUE]};
search ← Rope.Substr[base: unknown, start: dot + 1];
IF SpellExtras.GetFileExtension[unknown: search, confirm: True] # NIL THEN search ← NIL -- thing following dot IS name of extension. drop through and correct on whole thing, e.g. UserExecPackagge.configg, or UserExecPackage.configg. We do the latter, even though at this point we might have the fileName in hand, in order that user gets a chance to response, be informed, etc
ELSE {
name ← Rope.Substr[base: unknown, len: dot];
IF UserExec.CheckForFile[fileName ← Rope.Concat[name, ".mesa"]] THEN RETURN; -- e.g. UserExec.RegisterCommand
thing following a dot is NOT the name of a known extension. Assume it is a search, and the thing preceding it the name of the file.
};
};
{
ENABLE UNWIND => IF viewer # NIL THEN Finish[exec, viewer, out];
Abort: Spell.AbortProc = {
hasResponded: BOOL;
value: ATOM;
UserExec.CheckForAbort[exec];
IF viewer # NIL AND ViewerAbort.UserAbort[viewer] THEN IO.UserAborted[viewer];
IF viewer # NIL THEN [hasResponded, value] ← UserExec.GetUserResponse[viewer];
IF hasResponded AND value = $No THEN ERROR terminateCorrection;
};
Confirm: Spell.ConfirmProc -- [msg: ROPE, timeout: INT, defaultConfirm: BOOL] RETURNS [yes: BOOL] -- = {
RETURN[UserExec.Confirm[msg: msg, timeout: timeout, defaultConfirm: defaultConfirm, exec: exec, viewer: viewer]]
};
Inform: Spell.InformProc --[msg: ROPE] -- = {
IF exec # NIL THEN
{out ← UserExec.AcquireStreams[exec].out; -- since this can be called from random place, must acquire and release streams rather than using GetStreams
out.PutF["*n*m%g*s\n", rope[msg]];
}
ELSE MessageWindow.Append[message: msg, clearFirst: TRUE];
};
IF viewer = NIL THEN viewer ← ViewerTools.GetSelectedViewer[];
IF viewer # NIL THEN {
exec ← UserExec.GetExecHandle[viewer: viewer];
IF viewer.menu # NIL THEN UserExec.SetupAskUser[viewer] ELSE viewer ← NIL;
};
fileName ← Spell.GetTheFile[unknown: name, defaultExt: "mesa", inform: Inform, confirm: Confirm, abort: IF viewer # NIL THEN Abort ELSE NIL !
terminateCorrection => CONTINUE;
IO.UserAborted => -- may not be under an exec!
{MessageWindow.Append["Aborted", TRUE];
GOTO Out}
];
IF fileName = NIL THEN -- one last try, maybe unknown really is the name of a file with an extension, but extension not known, or maybe it is the misspelled name of a file with no extension.
{fileName ← Spell.GetTheFile[unknown: unknown, inform: Inform, confirm: Confirm, abort: IF viewer # NIL THEN Abort ELSE NIL ! terminateCorrection => CONTINUE];
search ← NIL;
};
GOTO Out;
EXITS
Out => Finish[exec, viewer, out];
};
};
AcquireResource -- superceded by CommanderOps.Acquire. Nobody should be calling.
AcquireResource: PUBLIC PROC [resource: REF ANY, owner: Rope.ROPE, exec: UserExec.ExecHandle] RETURNS[newlyAcquired: BOOL] = {
abort: Resource.AbortProc = {
RETURN[UserExec.UserAbort[exec]];
};
out: STREAM;
success: BOOL;
ownedBy: ROPE;
[success, ownedBy] ← Resource.Acquire[resource: resource, waitForIt: FALSE, owner: owner, abortProc: abort];
UserExec.CheckForAbort[exec];
IF success THEN RETURN;
IF owner # NIL THEN {
out ← UserExec.AcquireStreams[exec].out;
out.PutF["*n*mWaiting for %g to finish...", rope[ownedBy]];
out.Flush[];
};
[success, ownedBy] ← Resource.Acquire[resource: resource, waitForIt: TRUE, owner: owner, abortProc: abort];
UserExec.CheckForAbort[exec];
IF owner # NIL THEN {
out.PutF["proceeding\n*s"];
out.Flush[];
UserExec.ReleaseStreams[exec];
};
RETURN[TRUE];
};
ReleaseResource: PUBLIC PROC [resource: REF ANY, doWhenReleased: PROC [REF ANY] ← NIL] RETURNS[released: BOOL] = TRUSTED { -- Process
IF doWhenReleased # NIL THEN ERROR;
RETURN[Resource.Release[resource]];
};
TiogaOps.RegisterFileNameProc[CorrectFileName];
UserProfile.CallWhenProfileChanges[SetViewerSpell];
END. -- of UserExecUtilImpl