UserExecOpsImpl.mesa; Edited by Teitelman on June 6, 1983 1:17 pm
implements registered commands. See also viewerexecopsimpl
DIRECTORY
AMBridge USING [TVForReferent],
AMEventsExtra USING [Boot],
Atom USING [GetPName, Gensym],
ViewersSnapshot USING [Checkpoint, RollBack],
CedarVersion USING [major, minor, patch],
CIFS USING [Error, GetFC, Open, OpenFile, Close, read, GetPathname, Reset],
Commander USING [Register, CommandProc, Handle],
CommandProcOps USING [CheckForAbort, EventFailed, GetTheFile],
ConvertUnsafe USING [AppendRope, ToRope],
Directory USING [DeleteFile, Error, GetProperty, GetProps, Lookup, PutProperty, Rename, ignore, PropertyType],
File USING [Capability, GetSize],
FileStream USING [SetLeaderPropertiesForCapability],
FileIO USING [Open, OpenFailed, CapabilityFromStream, StreamFromCapability],
GVBasics,
GVNames USING [Authenticate],
IO USING [AppendStreams, card, Close, CR, CreateEditedStream, CreateProcsStream, CreateRefStreamProcs, DeliverWhenProc, EndOf, EraseChar, ESC, GetBlock, GetLength, GetOutputStreamRope, GetRefAny, GetToken, GreenwichMeanTime, IDProc, int, LookupData, PeekChar, Put, PutBlock, PutChar, PutF, PutRope, RIS, ROPE, rope, ROS, SetEcho, SetLength, Signal, SkipOver, SP, StoreData, STREAM, time, TokenProc, UserAborted, WhiteSpace],
List USING [DReverse, Sort, CompareProc, Compare],
MessageWindow USING [Blink, Append, Confirm],
NameInfoDefs,
PropertyTypes USING [tCreateDate],
Rope USING [Cat, Concat, Equal, Fetch, Find, IsEmpty, Length, Replace, Substr, ToRefText],
Runtime USING [GetBuildTime],
System USING [GreenwichMeanTime, GetGreenwichMeanTime, SecondsSinceEpoch],
Time USING [Current],
UserExec USING [CheckForAbort, CheckForFile, CommandProc, ExecHandle, GetNameAndPassword, GetStreams, GetTheFile, HistoryEvent, RegisterCommand, RegisterTransformation, RopeSubst, SetNameAndPassword, TransformProc, UserAbort, UserAborted],
UserExecPrivate USING [EventFailed, CallRegisteredProc, FileNotFound, GetRestOfStream, HistoryEventPrivateRecord, LookupCommand, ProcessProfile, RopeFromCMFile, StoreInSymTab, RunBCDFile],
ViewerOps USING [EnumerateViewers, EnumProc]
;
UserExecOpsImpl: CEDAR PROGRAM
IMPORTS AMBridge, AMEventsExtra, Atom, CedarVersion, CIFS, Commander, CommandProcOps, ConvertUnsafe, Directory, File, FileIO, FileStream, GVNames, IO, List, MessageWindow, Rope, Runtime, System, Time, UserExec, UserExecPrivate, ViewerOps, ViewersSnapshot
EXPORTS UserExec, UserExecPrivate
= BEGIN OPEN IO;
connecting concrete and opaque types
HistoryEventPrivateRecord:
PUBLIC
TYPE = UserExecPrivate.HistoryEventPrivateRecord;
to access inCommandFile and inCMFile in CommandsFrom
correct: ROPE; -- to be used in resume
Run, RunandCall
Run: Commander.CommandProc =
{
commandLineStream: STREAM = IO.RIS[cmd.commandLine];
anythingRun: BOOL ← FALSE;
file, error: ROPE;
DO
next: ROPE;
callDebuggerFirst: BOOL ← FALSE;
IF file = NIL THEN file ← IO.GetToken[commandLineStream, IO.IDProc];
next ← IO.GetToken[commandLineStream, IO.IDProc];
IF Rope.IsEmpty[file] THEN EXIT;
CommandProcOps.CheckForAbort[cmd];
IF Rope.Equal[next, "/D",
FALSE]
THEN {
callDebuggerFirst ← TRUE;
next ← NIL;
};
[, error] ← UserExecPrivate.RunBCDFile[fileName: file, callDebuggerFirst: callDebuggerFirst, out: cmd.out !
UserExecPrivate.FileNotFound => {
correct ← CommandProcOps.GetTheFile[file: name, defaultExt: defaultExt, handle: cmd];
RESUME[correct];
}
];
IF error # NIL THEN EXIT;
anythingRun ← TRUE;
file ← next;
ENDLOOP;
IF NOT anythingRun AND error = NIL THEN error ← "run what?";
IF error # NIL THEN CommandProcOps.EventFailed[cmd, error, file];
};
RunAndCall:
PUBLIC UserExec.CommandProc =
{
commandLineStream: STREAM = event.commandLineStream;
fileName, command, name, error: ROPE;
fileName ← IO.GetToken[commandLineStream, IO.IDProc];
[name, error] ← UserExecPrivate.RunBCDFile[fileName: fileName, out: UserExec.GetStreams[exec].out ! UserExecPrivate.FileNotFound =>
{correct ← UserExec.GetTheFile[file: name, defaultExt: defaultExt, event: event, exec: exec];
RESUME[correct];
}
];
IF error # NIL THEN UserExecPrivate.EventFailed[event, error, fileName];
fileName ← Rope.Substr[base: name, len: Rope.Length[name] - 4]; -- strip off the bcd
command ← UserExecPrivate.LookupCommand[fileName]; -- running the bcd may have registered a command.
IF command #
NIL
THEN {
UserExec.GetStreams[exec].out.PutChar['\n];
event.commandLine ← UserExecPrivate.GetRestOfStream[commandLineStream];
[] ← IO.RIS[event.commandLine, event.commandLineStream];
[] ← UserExecPrivate.CallRegisteredProc[command, event, exec];
};
};
Aliasing
AliasRecord: TYPE = RECORD [def: ROPE, args: LIST OF ROPE];
Alias: UserExec.CommandProc =
{
commandLineStream: STREAM = event.commandLineStream;
name, def: ROPE;
args: LIST OF ROPE ← NIL;
name ← IO.GetToken[commandLineStream, IO.TokenProc];
IO.SkipOver[commandLineStream, IO.WhiteSpace];
IF
NOT commandLineStream.EndOf[]
AND commandLineStream.PeekChar[] = '(
THEN {
FOR l:
LIST
OF
REF
ANY ←
NARROW[commandLineStream.GetRefAny[]], l.rest
UNTIL l =
NIL
DO
WITH l.first
SELECT
FROM
r: ROPE => args ← CONS[r, args];
a: ATOM => args ← CONS[Atom.GetPName[a], args];
ENDCASE => ERROR;
ENDLOOP;
TRUSTED {args ← LOOPHOLE[List.DReverse[LOOPHOLE[args]]]};
};
def ← UserExecPrivate.GetRestOfStream[in: commandLineStream, includeLast: FALSE];
RegisterAlias[name, args, def];
};
RegisterAlias:
PROCEDURE [name:
ROPE, args:
LIST
OF
ROPE, def:
ROPE] = {
PrintAlias:
PROC
RETURNS [doc:
ROPE] = {
out: STREAM = IO.ROS[];
out.PutRope["Alias "];
IF args #
NIL
THEN {
out.PutChar['[];
FOR l:
LIST
OF
ROPE ← args, l.rest
UNTIL l =
NIL
DO
out.PutRope[l.first];
IF l.rest # NIL THEN out.PutRope[", "];
ENDLOOP;
out.PutRope["]: "];
};
out.Put[rope[def]];
RETURN[IO.GetOutputStreamRope[out]];
};
UserExec.RegisterTransformation[name: name, proc: ExpandAlias, doc: PrintAlias[], clientData: NEW[AliasRecord ← [def: def, args: args]]];
};
ExpandAlias: UserExec.TransformProc = {
alias: REF AliasRecord ← NARROW[clientData];
synonyms: LIST OF REF SynonymRecord;
SynonymRecord: TYPE = RECORD[key, val: ROPE];
RopeMemb:
PROC [rope:
ROPE, lst:
LIST
OF
ROPE]
RETURNS[
BOOLEAN] = {
FOR l:
LIST
OF
ROPE ← lst, l.rest
UNTIL l =
NIL
DO
IF Rope.Equal[rope, l.first, FALSE] THEN RETURN[TRUE];
ENDLOOP;
RETURN[FALSE]
};
result ← alias.def;
FOR l:
LIST
OF
ROPE ← alias.args, l.rest
UNTIL l =
NIL
DO
new: ROPE ← IO.GetToken[event.commandLineStream, IO.IDProc];
IF RopeMemb[new, l.rest]
THEN {
-- e.g. args are (x y) and substituting y gorp.
dummy: ROPE = Atom.GetPName[Atom.Gensym[]];
synonyms ← CONS[NEW[SynonymRecord ← [new, dummy]], synonyms];
new ← dummy;
};
result ← UserExec.RopeSubst[old: l.first, new: new, base: result, case: FALSE];
ENDLOOP;
FOR l:
LIST
OF
REF SynonymRecord ← synonyms, l.rest
UNTIL l =
NIL
DO
result ← UserExec.RopeSubst[old: l.first.val, new: l.first.key, base: result, case: FALSE];
ENDLOOP;
result ← Rope.Cat[result, UserExecPrivate.GetRestOfStream[in: event.commandLineStream, includeLast: TRUE]]; -- used to be FALSE. changed to TRUE because was not getting CR at end of event.
};
File Operations: Delete, Rename, Copy, Fetch, List
DeleteFiles: UserExec.CommandProc =
{
commandLineStream: STREAM = event.commandLineStream;
out: STREAM = UserExec.GetStreams[exec].out;
file: ROPE;
someMissing: BOOL ← FALSE;
n: INT ← 0;
numPages: INT ← 0;
Delete:
PROC [file:
ROPE]
RETURNS[deleted:
BOOLEAN ←
FALSE] =
TRUSTED {
found: BOOL ← TRUE;
ls: LONG STRING = LOOPHOLE[Rope.ToRefText[file]];
cap: File.Capability;
cap ← Directory.Lookup[fileName: ls, permissions: Directory.ignore
! Directory.Error => {found ← FALSE; CONTINUE}];
IF found
THEN {
numPages ← File.GetSize[cap] + numPages;
deleted ← TRUE;
Directory.DeleteFile[ls !
Directory.Error => {deleted ← FALSE; CONTINUE}
];
};
IF deleted THEN out.PutF["*nDeleted: %g\n", rope[file]]
ELSE {
out.PutF["*n*m%g not found.*s\n", rope[file]];
UserExecPrivate.EventFailed[event: event, offender: file]; -- and go on.
};
};
DO
UserExec.CheckForAbort[exec];
file ← IO.GetToken[commandLineStream, IO.IDProc];
IF Rope.Length[file] = 0 THEN EXIT;
IF Delete[file] THEN n ← n + 1
ELSE someMissing ← TRUE;
ENDLOOP;
IF someMissing OR n > 1 THEN out.PutF["*nDeleted %d files, %d pages.", int[n], int[numPages]];
};
GetCreateDate:
PROC [file: File.Capability]
RETURNS [System.GreenwichMeanTime] =
TRUSTED {
createDate: System.GreenwichMeanTime;
Directory.GetProperty[file: file, property: PropertyTypes.tCreateDate,
propertyValue: DESCRIPTOR[@createDate, SIZE[System.GreenwichMeanTime]]];
RETURN [createDate];
};
SetCreateDate:
PROC [file: File.Capability, createDate: System.GreenwichMeanTime] =
TRUSTED {
Directory.PutProperty[file: file, property: PropertyTypes.tCreateDate,
propertyValue: DESCRIPTOR[@createDate, SIZE[System.GreenwichMeanTime]]];
};
RemoteNameProperty: Directory.PropertyType;
GetRemoteFileNameProp:
PROC[cap: File.Capability, remotename:
LONG
STRING] =
TRUSTED {
-- stolen from SubrImpl.mesa
mlen: CARDINAL ← remotename.maxlength;
this is erroneous, because reading the property will set the maxlength
IF mlen # 125 THEN ERROR;
Directory.GetProperty[cap, RemoteNameProperty,
DESCRIPTOR[remotename,
SIZE[StringBody[mlen]]] ! Directory.Error =>
IF type = invalidProperty THEN GOTO leave];
LOOPHOLE[remotename+1, LONG POINTER TO CARDINAL]^ ← mlen; -- reset the maxlength!!!
EXITS
leave => remotename.length ← 0;
};
SetRemoteFileNameProp:
PROC[cap: File.Capability, remotename:
LONG
STRING] =
TRUSTED {
-- remotename should be of the form [ivy]<directory>name!vers
IF remotename.maxlength ~= 125 THEN ERROR;
Directory.PutProperty[cap, RemoteNameProperty, DESCRIPTOR[remotename, SIZE[StringBody[remotename.maxlength]]], TRUE];
};
CopyFiles: UserExec.CommandProc =
{
DoCopyFiles[exec, event, event.commandLineStream];
DoCopyFiles:
PROC[exec: UserExec.ExecHandle, event: UserExec.HistoryEvent, stream:
STREAM] = {
out: STREAM = UserExec.GetStreams[exec].out;
buffer: REF TEXT;
bufferSize: CARDINAL = 512;
bytes, totalBytes, numberOfFiles: INT ← 0;
newF: STREAM;
newN, oldN, arrow: ROPE;
createDate: System.GreenwichMeanTime;
capability: File.Capability;
remoteName: ROPE;
CopyOneFile:
PROC = {
r: ROPE;
oldCap: File.Capability;
oldF: STREAM;
openFile: CIFS.OpenFile;
oldLen: INT;
r ← UserExec.GetTheFile[file: oldN, event: event, exec: exec];
IF r =
NIL
THEN {
ENABLE CIFS.Error => GOTO NotFound;
openFile ← CIFS.Open[name: oldN, mode: CIFS.read];
oldCap ← CIFS.GetFC[openFile];
oldF ← FileIO.StreamFromCapability[capability: oldCap, accessOptions: read, raw: TRUE];
EXITS
NotFound => {
out.PutF["*n*m%g not found.*s\n", rope[oldN]];
UserExecPrivate.EventFailed[event: event, offender: oldN];
GOTO Error;
};
};
numberOfFiles ← numberOfFiles + 1;
IF oldF =
NIL
THEN {
oldF ← FileIO.Open[fileName: r, accessOptions: read, createOptions: oldOnly, raw: TRUE];
oldCap ← FileIO.CapabilityFromStream[oldF];
};
createDate ← GetCreateDate[oldCap];
oldLen ← oldF.GetLength[];
IF newF =
NIL
THEN
-- first time -- newF ← FileIO.Open[fileName: newN, accessOptions: overwrite, createLength: oldLen ! FileIO.OpenFailed =>
IF why = notImplementedYet
THEN {
out.PutF["*n*m%g not implemented yet.*s\n", rope[newN]];
UserExecPrivate.EventFailed[event: event];
GOTO Error;
};
]
ELSE newF.SetLength[totalBytes + oldLen];
DO
bytes ← oldF.GetBlock[buffer];
IF bytes=0 THEN EXIT;
totalBytes ← totalBytes + bytes;
newF.PutBlock[buffer];
ENDLOOP;
oldF.Close[];
IF openFile #
NIL
THEN
TRUSTED {
remoteName ← CIFS.GetPathname[openFile]; -- the intent was to get the full path name, complete with version number. Unfortunately, CIFS does not currently support this
CIFS.Close[openFile];
CIFS.Reset[oldN]; -- otherwise there would be two copies on the disk!
};
EXITS
Error => RETURN
};
newN ← IO.GetToken[stream, IO.IDProc];
arrow ← IO.GetToken[stream, IO.IDProc];
IF Rope.Length[newN] = 0
OR
NOT Rope.Equal[arrow, "←"]
THEN
{
out.PutF["*eArgument missing or syntax error for copy!*s"];
RETURN;
};
buffer ← NEW[TEXT[bufferSize]];
WHILE Rope.Length[oldN ← IO.GetToken[stream, IO.IDProc]] # 0
DO
CopyOneFile[];
ENDLOOP;
IF newF = NIL THEN RETURN;
capability ← FileIO.CapabilityFromStream[newF];
newF.Close[];
out.PutF["*n*sCopied %d bytes", card[totalBytes]];
IF numberOfFiles = 1
THEN {
IF remoteName #
NIL
THEN
TRUSTED {
cap: File.Capability = Directory.Lookup[fileName: LOOPHOLE[Rope.ToRefText[newN]], permissions: Directory.ignore];
remoteNameString: LONG STRING ← [125];
ConvertUnsafe.AppendRope[to: remoteNameString, from: remoteName];
SetRemoteFileNameProp[cap, remoteNameString];
};
SetCreateDate[capability, createDate];
}
ELSE out.PutF[" from %g files", card[numberOfFiles]];
out.PutF[" to %g", rope[newN]];
FetchFiles: UserExec.CommandProc =
{
DO
i, j: INT;
localFile, remoteFile: ROPE;
remoteFile ← IO.GetToken[event.commandLineStream, IO.IDProc];
IF Rope.Length[remoteFile] = 0 THEN EXIT;
UserExec.CheckForAbort[exec];
IF (i ← Rope.Find[remoteFile, "/"]) # -1
THEN
UNTIL (j ← Rope.Find[s1: remoteFile, s2: "/", pos1: i + 1]) = -1
DO
i ← j;
ENDLOOP
ELSE
IF (i ← Rope.Find[remoteFile, ">"]) # -1
THEN
UNTIL (j ← Rope.Find[s1: remoteFile, s2: ">", pos1: i + 1]) = -1
DO
i ← j;
ENDLOOP
ELSE UserExecPrivate.EventFailed[event, Rope.Concat["Not a remote file: ", remoteFile], remoteFile];
IF (j ← Rope.Find[s1: remoteFile, s2: "!"]) = -1 THEN j ← Rope.Length[remoteFile];
localFile ← Rope.Substr[base: remoteFile, start: i + 1, len: j - (i + 1)];
DoCopyFiles[exec: exec, event: event, stream: IO.RIS[Rope.Cat[localFile," ← ", remoteFile]]];
ENDLOOP;
};
WhereFrom: UserExec.CommandProc =
{
commandLine: ROPE ← event.commandLine;
out: STREAM = UserExec.GetStreams[exec].out;
len: INT;
stream: IO.STREAM;
spellingCorrect: BOOL ← FALSE;
file: ROPE;
Lookup:
PROC [file:
ROPE] =
TRUSTED
-- Directory --
{
txt: REF TEXT;
name: LONG STRING;
cap: File.Capability;
failed: BOOLEAN ← FALSE;
IF spellingCorrect
THEN {
r: ROPE = UserExec.GetTheFile[file: file, event: event, exec: exec];
IF r = NIL THEN GOTO NotFound;
txt ← Rope.ToRefText[r];
}
ELSE IF UserExec.CheckForFile[file] THEN txt ← Rope.ToRefText[file]
ELSE GOTO NotFound;
name ← LOOPHOLE[txt];-- Directory.GetProps may write into name, e.g. to change lower case to capitals.
cap ← Directory.Lookup[fileName: name, permissions: Directory.ignore
! Directory.Error => {failed ← TRUE; CONTINUE}];
IF failed THEN ERROR -- because already did a CheckForFile
ELSE {
remoteName: LONG STRING ← [125];
GetRemoteFileNameProp[cap, remoteName];
IF remoteName.length # 0 THEN out.PutF["*n%g = %g\n", rope[file], rope[ConvertUnsafe.ToRope[remoteName]]]
ELSE out.PutF["*n*mNo remote path for %g\n", rope[file]];
};
EXITS
NotFound => {
out.PutF["*n*m%g not found*s", rope[file]];
UserExecPrivate.EventFailed[event: event, offender: file];
};
}; -- of Lookup
len ← Rope.Length[commandLine];
IF len > 1
AND Rope.Fetch[commandLine, len - 2] = '!
THEN {
-- special case check for ! here and in eval. If this occurs in several places, probably should handle it by filtering out at a higher point and setting the tryHarder field of the event to TRUE.
spellingCorrect ← TRUE;
commandLine ← Rope.Replace[base: commandLine, start: len - 2, len: 1];
};
stream ← IO.RIS[commandLine];
DO
file ← IO.GetToken[stream, IO.IDProc];
IF Rope.Length[file] = 0 THEN EXIT;
UserExec.CheckForAbort[exec];
Lookup[file];
ENDLOOP;
};
ListFiles: UserExec.CommandProc =
{
[] ← DoListFiles[event, exec];
};
ListByDates: UserExec.CommandProc =
{
compare: List.CompareProc =
TRUSTED {
x, y: REF FileRec;
x ← NARROW[ref1];
y ← NARROW[ref2];
RETURN[IF System.SecondsSinceEpoch[x.createDate] < System.SecondsSinceEpoch[y.createDate] THEN greater ELSE less]; -- more recently created files come first
};
[] ← DoListFiles[event, exec, compare];
};
FileRec: TYPE = RECORD[name: ROPE, createDate: System.GreenwichMeanTime, byteLength, numPages: LONG CARDINAL];
DoListFiles:
PROC [event: UserExec.HistoryEvent, exec: UserExec.ExecHandle, compare: List.CompareProc ←
NIL] = {
commandLine: ROPE ← event.commandLine;
out: STREAM = UserExec.GetStreams[exec].out;
len: INT;
numFiles, numPages: INT ← 0;
stream: IO.STREAM;
spellingCorrect: BOOL ← FALSE;
files: LIST OF REF FileRec;
file: ROPE;
Lookup:
PROC [file:
ROPE] =
TRUSTED
-- Directory --
{
txt: REF TEXT;
name: LONG STRING;
cap: File.Capability;
failed: BOOLEAN ← FALSE;
IF spellingCorrect
THEN {
r: ROPE = UserExec.GetTheFile[file: file, event: event, exec: exec];
IF r = NIL THEN GOTO NotFound;
txt ← Rope.ToRefText[r];
}
ELSE IF UserExec.CheckForFile[file] THEN txt ← Rope.ToRefText[file]
ELSE GOTO NotFound;
name ← LOOPHOLE[txt];-- Directory.GetProps may write into name, e.g. to change lower case to capitals.
cap ← Directory.Lookup[fileName: name, permissions: Directory.ignore
! Directory.Error => {failed ← TRUE; CONTINUE}];
IF failed THEN ERROR
ELSE {
createDate: IO.GreenwichMeanTime;
byteLength: LONG CARDINAL;
[, , createDate,byteLength,] ← Directory.GetProps[file: cap, name: name ! Directory.Error =>
IF type = invalidProperty
THEN {
out.PutF["*n*e%g has bad leader page*s", rope[file]];
GOTO Error;
}];
IF compare = NIL THEN Print[file, createDate, byteLength, File.GetSize[cap]] -- so user gets to see them as they are processed
ELSE files ← CONS[NEW[FileRec ← [name: file, createDate: createDate, byteLength: byteLength, numPages: File.GetSize[cap]]], files]; -- will be sorted first
};
EXITS
Error => {};
NotFound => {
out.PutF["*n*m%g not found*s", rope[file]];
UserExecPrivate.EventFailed[event: event, offender: file];
};
}; -- of Lookup
Print:
PROC [name:
ROPE, createDate:
IO.GreenwichMeanTime, byteLength, nPages:
LONG
CARDINAL] = {
out.PutF["*s*n%-30g %6d %g\n", rope[name], card[byteLength], time[createDate]];
numPages ← numPages + nPages;
numFiles ← numFiles + 1;
}; -- of Print
len ← Rope.Length[commandLine];
IF len > 1
AND Rope.Fetch[commandLine, len - 2] = '!
THEN {
-- special case check for ! here and in eval. If this occurs in several places, probably should handle it by filtering out at a higher point and setting the tryHarder field of the event to TRUE.
spellingCorrect ← TRUE;
commandLine ← Rope.Replace[base: commandLine, start: len - 2, len: 1];
};
stream ← IO.RIS[commandLine];
DO
file ← IO.GetToken[stream, IO.IDProc];
IF Rope.Length[file] = 0 THEN EXIT;
UserExec.CheckForAbort[exec];
Lookup[file];
ENDLOOP;
IF compare #
NIL
THEN
TRUSTED {
files ← LOOPHOLE[List.Sort[LOOPHOLE[files], compare]];
FOR lst:
LIST
OF
REF FileRec ← files, lst.rest
UNTIL lst =
NIL
DO
UserExec.CheckForAbort[exec];
Print[lst.first.name, lst.first.createDate, lst.first.byteLength, lst.first.numPages]
ENDLOOP;
};
out.PutF["*n*sTotal of %d files, %d pages", int[numFiles], int[numPages]];
};
Rename: UserExec.CommandProc =
{
commandLineStream: STREAM = event.commandLineStream;
out: STREAM = UserExec.GetStreams[exec].out;
oldName, newName, r: ROPE;
oldName ← IO.GetToken[commandLineStream, IO.IDProc];
newName ← IO.GetToken[commandLineStream, IO.IDProc];
IF Rope.Equal[newName, "←"]
THEN
{
newName ← oldName;
oldName ← IO.GetToken[commandLineStream, IO.IDProc];
};
IF Rope.Length[oldName] = 0 OR Rope.Length[newName] = 0 THEN UserExecPrivate.EventFailed[event: event, msg: "Empty file name"];
r ← UserExec.GetTheFile[file: oldName, event: event, exec: exec];
IF r = NIL THEN UserExecPrivate.EventFailed[event: event, msg: Rope.Concat[oldName, " Not Found"], offender: oldName];
oldName ← r;
RETURN[RenameFile[oldName, newName, out]];
};
RenameFile:
PUBLIC
PROC [oldName, newName:
ROPE, out:
STREAM]
RETURNS[success:
BOOL] = {
msg, r: ROPE;
{
Rename1:
PROC [old, new:
ROPE] =
TRUSTED
{
olds: LONG STRING ← [60];
news: LONG STRING ← [60];
ConvertUnsafe.AppendRope[to: olds, from: old];
ConvertUnsafe.AppendRope[to: news, from: new];
Directory.Rename[olds, news];
FileStream.SetLeaderPropertiesForCapability[cap: Directory.Lookup[news], write: Time.Current[]];
};
Delete1:
PROC [file:
ROPE] =
TRUSTED
{
Directory.DeleteFile[
LOOPHOLE[Rope.ToRefText[file]]
! Directory.Error => CONTINUE];
};
Rename1[oldName, newName ! Directory.Error =>
{
SELECT type
FROM
fileAlreadyExists =>
{
IF Rope.Equal[oldName, newName,
FALSE]
THEN
TRUSTED
{Delete1["$$scratch" ! Directory.Error =>
CONTINUE];
Rename1[oldName, "$$scratch"];
Rename1["$$scratch", newName];
CONTINUE;
};
r ← Rope.Concat[newName, "$$"];
IF Rope.Equal[r, oldName,
FALSE]
THEN
-- e.g. user is in fact performing rename foo.mumble ← foo.mumble$$
{Delete1[newName];
RETRY;
};
Delete1[r ! Directory.Error =>
CONTINUE];
Rename1[newName, r];
out.PutF["*s%g already existed, now renamed to %g\n", rope[newName], rope[r]];
RETRY;
};
fileNotFound => msg ← "File Not Found";
invalidFileName => msg ← "Invalid File Name";
ENDCASE => msg ← "Directory Error";
out.PutF["*e%g, ", rope[msg]];
GOTO fail;
};
];
out.PutF["%g renamed to %g", rope[oldName], rope[newName]];
RETURN[TRUE];
EXITS
fail => {out.PutRope["Unable to complete rename."]; RETURN[FALSE]};
};
};
Logging In
LoginProc: Commander.CommandProc =
{
errorMessage: ROPE = LoginFromStreams[cmd.in, cmd.out];
IF errorMessage # NIL THEN CommandProcOps.EventFailed[cmd, errorMessage];
UserExecPrivate.ProcessProfile[];
};
Login:
PUBLIC
PROC [in, out:
STREAM]
RETURNS[name, password:
ROPE] = {
errorMessage: ROPE = LoginFromStreams[in, out];
IF errorMessage #
NIL
THEN {
MessageWindow.Append[errorMessage, TRUE];
RETURN[NIL, NIL];
}
ELSE {
[name, password] ← UserExec.GetNameAndPassword[];
UserExecPrivate.ProcessProfile[];
};
};
LoginFromStreams:
PROC [in, out:
STREAM]
RETURNS[errorMessage:
ROPE] = {
name, password: ROPE;
UNTIL in.backingStream =
NIL
DO
in ← in.backingStream;
ENDLOOP;
in ← IO.CreateEditedStream[in: in, echoTo: out, deliverWhen: IODeliverWhen];
name ← UserExec.GetNameAndPassword[].name;
out.PutF["*sUser Name: " ! IO.Signal => IF ec = NotImplementedForThisStream THEN RESUME];
in.StoreData[key: $Count, data: NEW[INT ← Rope.Length[name]]];
IO.AppendStreams[in, RIS[name]];
name ← IO.GetToken[in, IO.IDProc !
IO.Signal =>
SELECT ec
FROM
Rubout => ERROR IO.UserAborted;
EmptyBuffer => RESUME;
ENDCASE;
];
out.PutRope["Password: "];
[] ← in.SetEcho[
IO.CreateProcsStream[
streamProcs: IO.CreateRefStreamProcs[putChar: PutStar, eraseChar: EraseStar],
backingStream: out,
streamData: NIL]];
password ← IO.GetToken[in, IO.IDProc !
IO.Signal =>
SELECT ec
FROM
Rubout => ERROR IO.UserAborted;
EmptyBuffer => RESUME;
ENDCASE;
];
IF (errorMessage ← AuthenticateUser[name, password]) # NIL THEN RETURN;
UserExec.SetNameAndPassword[name, password];
};
AuthenticateUser:
PROC [name, password:
ROPE]
RETURNS [msg:
ROPE ←
NIL] = {
IF Rope.Find[name, "."] = -1 THEN name ← Rope.Concat[name, ".pa"];
SELECT GVNames.Authenticate[name, password]
FROM
group => RETURN["... Can't login as group"];
notFound => RETURN[Rope.Concat[name, " invalid user name"]];
allDown => RETURN["Can't authenticate, no server responded"];
badPwd => RETURN["Invalid password"];
ENDCASE;
};
IODeliverWhen:
IO.DeliverWhenProc = {
count: REF INT = NARROW[stream.LookupData[$Count]];
count^ ← count^ - 1;
IF count^ > 0 THEN RETURN[FALSE] -- the idea is that if there are illegal characters, e.g. space, cr, etc. in user name, then don't want to have these terminate, or else could never change this again!
ELSE RETURN[
SELECT char
FROM
CR, SP, ESC => TRUE,
PutStar:
PROC [self:
STREAM, char:
CHARACTER] = {
SELECT char
FROM
CR, SP, ESC => NULL;
ENDCASE => self.backingStream.PutChar['*]
EraseStar:
PROC [self:
STREAM, char:
CHARACTER] = {
self.backingStream.EraseChar['*];
}; -- if default it, would print \ and char, causing two *'s.
Version, Daytime, User
TypeVersion: UserExec.CommandProc =
TRUSTED {
out: STREAM = UserExec.GetStreams[exec].out;
out.PutF[
"You are running Cedar %g.%g", IO.int[CedarVersion.major], IO.int[CedarVersion.minor]];
IF CedarVersion.patch # 0 THEN out.PutF[".%g", IO.int[CedarVersion.patch]];
out.PutF[" of %t\n", IO.time[Runtime.GetBuildTime[]]];
RETURN [TRUE] ;
};
TypeDaytime: UserExec.CommandProc =
TRUSTED {
out: STREAM = UserExec.GetStreams[exec].out;
time: System.GreenwichMeanTime = System.GetGreenwichMeanTime[];
out.PutF["The time is %g\n", IO.time[time]];
UserExecPrivate.StoreInSymTab[value: AMBridge.TVForReferent[NEW[System.GreenwichMeanTime ← time]], event: event, exec: exec];
RETURN [TRUE];
TypeUser: UserExec.CommandProc =
TRUSTED {
out: STREAM = UserExec.GetStreams[exec].out;
user: ROPE = UserExec.GetNameAndPassword[].name;
out.PutF["The logged-in user is %g\n", IO.rope[user]];
UserExecPrivate.StoreInSymTab[value: AMBridge.TVForReferent[NEW[ROPE ← user]], event: event, exec: exec];
RETURN [TRUE];
};
CheckPoint, Rollback, and BootCurrent
BootCurrent: UserExec.CommandProc =
TRUSTED {
IF NOT CheckForEdits[] THEN UserExec.UserAborted[exec];
AMEventsExtra.Boot[boot: [logical[]]];
};
RollBack: UserExec.CommandProc =
TRUSTED {
IF NOT CheckForEdits[] THEN UserExec.UserAborted[exec];
ViewersSnapshot.RollBack[];
};
CheckPoint: UserExec.CommandProc =
TRUSTED {
IF NOT CheckForEdits[] THEN UserExec.UserAborted[exec];
[] ← ViewersSnapshot.Checkpoint[];
CheckForEdits:
PROC
RETURNS[ok:
BOOL] = {
edits: BOOL ← FALSE;
proc: ViewerOps.EnumProc = {
IF v.newVersion THEN {edits ← TRUE; RETURN[FALSE]}; -- terminate
RETURN[TRUE];
};
ViewerOps.EnumerateViewers[proc];
IF NOT edits THEN RETURN[TRUE];
MessageWindow.Append["Confirm discard of edits . . .", TRUE];
MessageWindow.Blink[];
RETURN[MessageWindow.Confirm[]];
};
Miscellaneous
CommandsFrom: UserExec.TransformProc =
{
commandLineStream: STREAM = event.commandLineStream;
fileName: ROPE;
contents, name: ROPE;
fileName ← IO.GetToken[commandLineStream, IO.IDProc];
[contents, name] ← UserExecPrivate.RopeFromCMFile[file: fileName, event: event, exec: exec];
IF event #
NIL
THEN
{private: REF UserExecPrivate.HistoryEventPrivateRecord = event.privateStuff;
event.dontCorrect ← TRUE;
private.inCommandFile ← TRUE;
private.inCMFile ← (Rope.Find[s1: name, s2: ".cm", case: FALSE] # -1);
};
result ← Rope.Concat[contents, UserExecPrivate.GetRestOfStream[commandLineStream]];
}; -- of CommandsFrom
Initialization
TRUSTED {RemoteNameProperty ← LOOPHOLE[213B]};
Commander.Register["Login", LoginProc, "Supply user name and password."];
UserExec.RegisterCommand["Delete", DeleteFiles, "Delete a list of files."];
UserExec.RegisterCommand["Rename", Rename, "Renames a file", "Renames a file. Syntax is Rename old new, or Rename new ← old."];
UserExec.RegisterCommand["Copy", CopyFiles, "Copy contents of one or more files to another (new ← old1 old2 ...)."];
UserExec.RegisterCommand["Fetch", FetchFiles, "Copies contents of remote file(s) to corresponding local file(s)."];
UserExec.RegisterCommand["WhereFrom", WhereFrom, "Prints the name of the remote file(s) that the local file(s) came from, if any."];
UserExec.RegisterCommand["List", ListFiles, "Prints size, creation date for the indicated files."];
UserExec.RegisterCommand["ListByDates", ListByDates, "Prints size, creation date for the indicated files, sorted by create date."];
Commander.Register["Run", Run, "Load and Start the named programs.\n/d following a file name means just load the corresponding program, but do not start it. Instead, call the debugger, e.g. so that you can plant breakpoints. Proceeding from the corresponding action area will then start the program."];
UserExec.RegisterCommand["RunAndCall", RunAndCall, "Load and Start the named program. Then call the registered procedure, if any."];
UserExec.RegisterCommand["Alias", Alias, "Defines an Alias, Form is Alias (args) rest-of-line.", "Defines an Alias. Aliases provide a substitution macro facility for the first token on a line, e.g. Alias br bringover /o /a would cause 'br file df' to be transformed to 'bringover /o /a file df'. An optional argument list is permitted following the alias name, in which case tokens from the input line are substituted for the arguments, e.g. Alias both (file) compile file '; bind file would cause 'both foo' to be transformed to 'compile foo; bind foo'."];
UserExec.RegisterCommand["Version", TypeVersion, "Types the version number and build time of the Cedar boot file you are running."];
UserExec.RegisterCommand[ "Date", TypeDaytime, "Types today's date and time."];
UserExec.RegisterCommand["User", TypeUser, "Types the name of the logged-in user."];
UserExec.RegisterTransformation["@", CommandsFrom, "Treat the contents of the named file as a command file.", "If {file} ends in .cm (or {file} has no extension and there is a {file}.cm), treat the {file} as an old-style command file, i.e. when interpreting the contents, treat {fileName} by itself on a line as Run {fileName}. Similarly, treat @{fileName} by itself the same as CommandsFrom {fileName}. Interpreted {command}.~ to mean invoke the indicated registered command.
\nOtherwise, if {file} ends in .commands (or the {file} has no extension and there is a {file}.commands), treat the file as a new-style command file, i.e. treat the contents of the {file} the same as though they had been typed."];
UserExec.RegisterCommand["BootCurrent", BootCurrent, "Boots current volume."];
UserExec.RegisterCommand["RollBack", RollBack, "Does a rollback."];
UserExec.RegisterCommand["CheckPoint", CheckPoint, "Does a checkpoint."];
END.
August 12, 1982 4:15 pm fixed login to call userprofile.profilechanged
September 9, 1982 11:03 am fixed listfiles to catch Directory.Error[type: invalidProperty]
Edited on May 3, 1983 9:26 pm, by Teitelman
fixed copy command to take remote file names
changes to: CopyFiles, CopyOneFile (local of CopyFiles), ListFiles, DoList, Lookup (local of DoList), Print (local of DoList)
Edited on May 12, 1983 5:59 pm, by Teitelman
changes to: DIRECTORY, IMPORTS, CheckPoint, CheckForEdits
Edited on May 23, 1983 4:38 pm, by Teitelman
changes to: FetchFiles
Edited on June 1, 1983 4:12 pm, by Teitelman
changes to: DIRECTORY, RemoteNameProperty, GetRemoteFileNameProp, SetRemoteFileNameProp, CopyFiles, FetchFiles, WhereFrom, Lookup (local of WhereFrom), ListFiles, TRUSTED, Commander
Edited on June 6, 1983 1:17 pm, by Teitelman
changes to: AuthenticateUser