-- CatalogUtil.Mesa
-- Gifford, June 16, 1982 11:17 am
-- Schroeder, February 3, 1983 1:36 pm
DIRECTORY
CatalogComm,
CIFS USING [Connect, Login],
ConvertUnsafe USING [AppendRope],
FileIO USING [Open, OpenFailed],
IO USING [Close, EndOfStream, Error, GetChar, GetToken, IDProc, Handle, int,
PutChar, PutF, PutFR, rope, SetEcho, Signal, string],
LongString USING [AppendChar],
Process USING [Pause, SecondsToTicks, Yield],
Rope USING [Compare, Fetch, Find, FromChar, Length, ROPE, Substr],
Runtime USING [BoundsFault],
Storage USING [FreeString, StringLength],
STP USING [Connect, Create, Destroy, Enumerate, Error, ErrorCode, FileInfo,
GetFileInfo, Handle, Login, NoteFileProcType, Open],
UserExec USING [ResetUserAbort, UserAbort];
CatalogUtil: PROGRAM
IMPORTS CatalogComm, CIFS, ConvertUnsafe, FileIO, IO, LongString, Process, Rope, Runtime, Storage, STP, UserExec
EXPORTS CatalogComm = {
GetLine: PUBLIC PROC[h: CatalogComm.Handle, sh: IO.Handle, line: LONG STRING] RETURNS [noteof: BOOL] = {
char: CHAR;
eos: BOOL ← FALSE;
line.length ← 0;
DO
char ← sh.GetChar[ ! IO.Error, IO.EndOfStream => GOTO lose];
IF char='\n THEN EXIT;
IF eos THEN LOOP;
line[line.length] ← char;
line.length ← SUCC[line.length];
eos ← line.length>=line.maxlength;
ENDLOOP;
IF line.length >= line.maxlength THEN {
line.length ← line.maxlength;
h.out.PutF["*nGetLine-- line '%s' is too long.\n", IO.string[line]];
};
RETURN[TRUE];
EXITS
lose => RETURN[FALSE];
};
FindFirst: PUBLIC PROC [s: Rope.ROPE, c: CHAR, start: INT] RETURNS [loc: INT] = {
loc ← Rope.Find[s1: s, s2: Rope.FromChar[c], pos1: start];
IF loc=s.Length[] THEN SIGNAL CatalogComm.NoneFound;
};
FindLast: PUBLIC PROC [s: Rope.ROPE, c: CHAR] RETURNS [loc: INT] = {
FOR loc ← s.Length[] - 1, PRED[loc] DO
IF s.Fetch[loc]=c THEN EXIT;
IF loc = 0 THEN {SIGNAL CatalogComm.NoneFound; EXIT; };
ENDLOOP;
};
SubStringCopy: PUBLIC PROC [s: Rope.ROPE, start, stop: INT] RETURNS [ns: Rope.ROPE] = {
IF start>stop THEN RETURN[NIL];
RETURN[s.Substr[start: start, len: stop-start+1]];
};
LFindFirst: PUBLIC PROC [s: LONG STRING, c: CHAR, start: NAT] RETURNS [loc: NAT] = {
loc ← start;
WHILE loc<s.length DO
IF s[loc]=c THEN RETURN[loc];
loc ← loc + 1;
ENDLOOP;
SIGNAL CatalogComm.NoneFound;
};
LFindLast: PUBLIC PROC [s: LONG STRING, c: CHAR] RETURNS [loc: NAT] = {
loc ← s.length - 1;
DO
IF s[loc]=c THEN RETURN[loc];
IF loc = 0 THEN {SIGNAL CatalogComm.NoneFound; RETURN [loc]; };
loc ← loc - 1;
ENDLOOP;
};
LSubStringAppend: PUBLIC PROC [from, to: LONG STRING, start, stop: NAT] = {
IF start>stop THEN RETURN;
FOR i: NAT IN [start..stop] DO
LongString.AppendChar[s: to, c: from[i]];
ENDLOOP;
};
CatalogList: PUBLIC PROC [h: CatalogComm.Handle, key: Rope.ROPE, entry: Rope.ROPE, depth: INTEGER, dirsOnly: BOOL, comment: Rope.ROPE ← NIL] = {
tt: LIST OF REF CatalogComm.Directory;
pp: LIST OF REF;
t: REF CatalogComm.Directory;
p: REF CatalogComm.Node;
FOR tt ← h.root, tt.rest WHILE tt#NIL DO
t ← tt.first;
IF Rope.Compare[s1: t.name, s2: key, case: FALSE]=equal THEN EXIT;
REPEAT
FINISHED => ERROR CatalogComm.CatalogError[cantFindDirectory];
ENDLOOP;
IF dirsOnly THEN h.out.PutF["%4d %4d %4d %4d ", IO.int[t.fileCount], IO.int[t.directoryCount], IO.int[t.linkCount], IO.int[t.commentCount]];
THROUGH [0..depth-2) DO h.out.PutChar[' ]; ENDLOOP;
h.out.PutF["%s ", IO.rope[entry]];
IF comment.Length[]>0 THEN h.out.PutF["%s", IO.rope[comment]];
h.out.PutChar['\n];
FOR pp ← t.chain, pp.rest WHILE pp#NIL DO
Process.Yield[];
p ← NARROW[pp.first];
SELECT p.what FROM
file, link => {
IF dirsOnly THEN LOOP;
THROUGH [0..depth) DO h.out.PutChar[' ]; ENDLOOP;
h.out.PutF["%s ", IO.rope[p.name]];
IF p.target.Length[]>0 THEN h.out.PutF["%s ", IO.rope[p.target]];
IF p.comment.Length[]>0 THEN h.out.PutF["%s ", IO.rope[p.comment]];
h.out.PutChar['\n];
};
directory => CatalogList[h: h, key: p.directory, entry: p.name, depth: depth+2, dirsOnly: dirsOnly, comment: p.comment];
ENDCASE => ERROR;
ENDLOOP;
};
FreeWorld: PUBLIC PROC [h: CatalogComm.Handle] = {
a, b: LIST OF REF CatalogComm.Directory;
c, d: LIST OF REF;
e: REF CatalogComm.Node;
FOR a ← h.root, b WHILE a#NIL DO
b ← a.rest;
IF a.first#NIL THEN {
FOR c ← a.first.chain, d WHILE c#NIL DO
Process.Yield[];
d ← c.rest;
e ← NARROW[c.first];
IF e#NIL THEN FREE[@e];
FREE[@c];
ENDLOOP;
FREE[@a.first];
};
FREE[@a];
ENDLOOP;
h.root ← NIL;
};
CatalogErrorString: PUBLIC PROC [error: CatalogComm.CatalogErrorType] RETURNS [Rope.ROPE] = {
str: ARRAY CatalogComm.CatalogErrorType OF Rope.ROPE = [
"nilDirectory", "nilFilename", "cantFindDirectory",
"badDirectoryName", "parentNotFound", "unknownTopLevelDirectory",
"topLevelDirectoryFoundTwice", "cantCreateLocalFile", "unKnownError",
"implementationRestriction", "cantEnumerate", "dirdirFoundTwice",
"catalogAborted", "cantResume"
];
RETURN [str[error]];
};
HandleSTPError: PUBLIC PROC[h: CatalogComm.Handle, stphandle: STP.Handle, stpError: STP.ErrorCode,
message: STRING] RETURNS [retryit: BOOL] ={
oldEcho: IO.Handle;
locname, locpassword: Rope.ROPE;
Cred: TYPE = {login, connect, none};
cred: Cred ← none;
resume: BOOL;
IF stpError = illegalUserPassword OR stpError = illegalUserName OR
stpError = illegalUserAccount OR stpError = credentailsMissing THEN cred ← login
ELSE IF stpError = illegalConnectName OR stpError = illegalConnectPassword OR
stpError = accessDenied THEN cred ← connect;
IF cred=none OR stphandle=NIL THEN RETURN[FALSE];
-- if user hits DEL, then don't resume (give up)
{
ENABLE IO.Signal => TRUSTED {GOTO BailOut};
resume ← TRUE;
h.out.PutF["Catalog: STP Error: %s\n", IO.string[message]];
h.out.PutF["Hit DEL to abort request or enter name and password\n"];
h.out.PutF["Name: "];
locname ← h.in.GetToken[IO.IDProc];
h.out.PutF["\nPassword: "];
oldEcho ← h.in.SetEcho[NIL];
locpassword ← h.in.GetToken[IO.IDProc];
[] ← h.in.SetEcho[oldEcho];
h.out.PutF["\nThanks!"];
-- set credentials
EXITS
BailOut => resume ← FALSE;
};
IF NOT resume THEN RETURN[FALSE];
{
ENABLE Runtime.BoundsFault => GOTO Fault;
nameString: STRING ← [100];
passwordString: STRING ← [100];
ConvertUnsafe.AppendRope[to: nameString, from: locname];
ConvertUnsafe.AppendRope[to: passwordString, from: locpassword];
IF cred=login THEN {
h.name ← locname;
h.password ← locpassword;
CIFS.Login[name: locname, password: locpassword];
STP.Login[stphandle, nameString, passwordString];
}
ELSE {
STP.Connect[stphandle, nameString, passwordString];
CIFS.Connect[name: locname, password: locpassword];
};
EXITS
Fault => {
h.out.PutF["Catalog: Name or password string too long!\n"];
resume ← FALSE;
};
};
RETURN[resume];
};
MAXNTRIES: CARDINAL = 3;
NSECPAUSE: CARDINAL = 10;
Connect: PUBLIC PROC[h: CatalogComm.Handle, host: Rope.ROPE] RETURNS [stphandle: STP.Handle] = {
herald: STRING;
shorthost: STRING ← [30];
user: STRING ← [40];
password: STRING ← [40];
ntries: CARDINAL ← 0;
stphandle ← NIL;
{ ENABLE UNWIND => IF stphandle#NIL THEN [] ← STP.Destroy[stphandle ! STP.Error => CONTINUE];
IF host.Length[] = 0 THEN {
h.out.PutF["Error: remote host has not been specified.\n"];
RETURN[NIL];
};
h.out.PutF["Opening connection to %s.\n", IO.rope[host]];
ConvertUnsafe.AppendRope[to: shorthost, from: host ! Runtime.BoundsFault => {
h.out.PutF["Catalog: host name too long!\n"];
GOTO GiveUp;
}];
stphandle ← STP.Create[];
herald ← STP.Open[stphandle, shorthost ! STP.Error => IF code = connectionRejected THEN {
h.out.PutF["Connection rejected by %s.\n", IO.rope[host]];
ntries ← ntries + 1;
IF ntries < MAXNTRIES THEN {
h.out.PutF["Will pause and try again.\n"];
IF CatalogComm.CheckForAbort[h] THEN ERROR CatalogComm.CatalogError[catalogAbort];
Process.Pause[Process.SecondsToTicks[NSECPAUSE]];
IF CatalogComm.CheckForAbort[h] THEN ERROR CatalogComm.CatalogError[catalogAbort];
RETRY;
}
ELSE h.out.PutF["Connection rejected too many times.\n"];
}];
h.out.PutF["%s\n", IO.string[herald]];
Storage.FreeString[herald];
{
ENABLE Runtime.BoundsFault => {
h.out.PutF["Catalog: name or password too long!\n"];
[] ← STP.Destroy[stphandle ! STP.Error => CONTINUE];
stphandle ← NIL;
CONTINUE;
};
spassword: STRING ← [80];
suser: STRING ← [80];
ConvertUnsafe.AppendRope[to: suser, from: h.name];
ConvertUnsafe.AppendRope[to: spassword, from: h.password];
STP.Login[stphandle, suser, spassword];
}
EXITS
GiveUp => NULL;
};
};
CheckForAbort: PUBLIC PROC [h: CatalogComm.Handle] RETURNS [BOOL] = {
a: BOOL ← UserExec.UserAbort[h.eh];
IF a THEN UserExec.ResetUserAbort[h.eh];
RETURN[a];
};
EnumFromRemoteFile: PUBLIC PROC [h: CatalogComm.Handle] = {
lsfn: STRING ← [100];
stphandle: STP.Handle ← NIL;
locEnumStream: IO.Handle ← NIL;
aborted: BOOL ← FALSE;
{ ENABLE UNWIND => {
IF stphandle#NIL THEN [] ← STP.Destroy[stphandle ! STP.Error => CONTINUE];
IF locEnumStream#NIL THEN {
locEnumStream.Close[! IO.Error => CONTINUE];
h.out.PutF["Remember to delete %s!\n", IO.rope[h.locEnumFileName]];
};
};
-- NoteFileProcType: TYPE = PROCEDURE [file: STRING] RETURNS [continue: Continue];
EnumProcessFile: STP.NoteFileProcType = {
info: STP.FileInfo ← STP.GetFileInfo[stphandle];
IF Storage.StringLength[info.version]#0 THEN locEnumStream.PutF["<%s>%s!%s\n", IO.string[info.directory], IO.string[info.body], IO.string[info.version]]
ELSE locEnumStream.PutF["<%s>%s\n", IO.string[info.directory], IO.string[info.body]];
CatalogComm.ParseFile[h: h, vf: info];
IF CatalogComm.CheckForAbort[h] THEN aborted ← TRUE;
RETURN[IF aborted THEN no ELSE yes];
};
locEnumStream ← FileIO.Open[h.locEnumFileName, overwrite ! FileIO.OpenFailed => CHECKED {
ERROR CatalogComm.CatalogError[cantCreateLocalFile];
}];
h.out.PutF["Catalog: creating local enumeration file: %s\n", IO.rope[h.locEnumFileName]];
lsfn.length ← 0;
ConvertUnsafe.AppendRope[to: lsfn, from: IO.PutFR["<%s>**.**", IO.rope[h.directory]] ! Runtime.BoundsFault => {
h.out.PutF["Catalog: Enumeration pattern too long!\n"];
ERROR CatalogComm.CatalogError[implementationRestriction];
}];
FOR i: NAT IN [0..lsfn.length) DO
IF lsfn[i] = '/ THEN lsfn[i] ← '>;
ENDLOOP;
stphandle ← CatalogComm.Connect[h, h.filesystem];
h.out.PutF["Catalog: Enumerating [%s]%s\n", IO.rope[h.filesystem], IO.string[lsfn]];
STP.Enumerate[stphandle, lsfn, EnumProcessFile ! STP.Error =>
IF CatalogComm.HandleSTPError[h: h, stphandle: stphandle, stpError: code, message: error] THEN RETRY ELSE REJECT
];
locEnumStream.Close[];
locEnumStream ← NIL;
h.out.PutF["Closing connection to %s ... ", IO.rope[h.filesystem]];
[] ← STP.Destroy[stphandle ! STP.Error => CONTINUE];
stphandle ← NIL;
h.out.PutF["closed.\n"];
IF aborted THEN {
h.out.PutF["Remember to delete %s!\n", IO.rope[h.locEnumFileName]];
ERROR CatalogComm.CatalogError[catalogAbort];
};
};
};
}.
10-Feb-82 9:20:33, Stewart, carved out from CatalogImpl
March 21, 1982 3:52 pm, Stewart, added HandleSTPError
25-Mar-82 15:56:04, Stewart, added Connect
March 26, 1982 4:25 pm, Stewart, bulletproofing
June 16, 1982 10:04 am, Gifford, new BTree Dir System