DIRECTORY CIFS: TYPE USING[Close, create, GetFC, Open, OpenFile, replace, write], ConvertUnsafe: TYPE USING[ToRope], CWF: TYPE USING [SWF1, SWF4, WF0, WF1, WF2, WFCR], DCSFileTypes: TYPE USING [tLeaderPage], Directory: TYPE USING [CreateFile, DeleteFile, Error, Handle, ignore, Lookup, Rename], Environment: TYPE USING [bytesPerPage], File: TYPE USING [Capability, nullCapability, Permissions, read, SetSize], FileStream: TYPE USING [Create, GetLength, SetLeaderPropertiesForCapability], FQ: TYPE USING[FileQuery, Result], Heap: TYPE USING[systemZone], Inline: TYPE USING [BITNOT, LowHalf], IO: TYPE USING[card, PutF, string, UserAbort], LongString: TYPE USING [AppendChar, EquivalentString], Process: TYPE USING [Pause, SecondsToTicks], Space: TYPE USING [Create, Delete, Handle, LongPointer, Map, nullHandle, virtualMemory], UnsafeSTP: TYPE USING [Connect, Create, CreateRemoteStream, DesiredProperties, Destroy, Error, ErrorCode, FileInfo, GetFileInfo, Handle, Login, NextFileName, Open, SetDesiredProperties, Type], UnsafeSTPOps: TYPE USING [FindFileType, Handle, SetPListItem], STPSubr: TYPE USING [RetrieveProcType, StpState], Stream: TYPE USING [Delete, EndOfStream, GetBlock, Handle, PutBlock], Subr: TYPE USING [AbortMyself, AllocateString, Any, CopyString, CursorInWindow, EndsIn, errorflg, FileError, FreeString, GetCreateDate, GetNameandPassword, LongZone, NewStream, Prefix, Read, SetRemoteFilenameProp, strcpy, SubStrCopy, TTYProcs, Write], UserCredentialsUnsafe: TYPE USING[GetUserCredentials], UserTerminal: TYPE USING [cursor, CursorArray, GetCursorPattern, SetCursorPattern]; STPSubrImpl: PROGRAM IMPORTS CIFS, ConvertUnsafe, CWF, Directory, File, FileStream, FQ, Heap, Inline, IO, LongString, Process, Space, STP: UnsafeSTP, STPOps: UnsafeSTPOps, Stream, Subr, UserCredentialsUnsafe, UserTerminal EXPORTS STPSubr SHARES File = { useCIFS: BOOL _ TRUE; connseq: LONG POINTER TO ConnSeqRecord _ NIL; maxNumberOfTries: CARDINAL _ 10; -- # tries to get connected if rejecting MAXCONNS: CARDINAL = 8; ConnSeqRecord: TYPE = RECORD[ size: CARDINAL _ 0, -- # of connections body: SEQUENCE maxsize: CARDINAL OF ConnTable ]; ConnTable: TYPE = RECORD[ stphandle: STP.Handle _ NIL, host: LONG STRING _ NIL ]; SetUseOfCIFS: PUBLIC PROC[shouldUseCIFS: BOOL] = { useCIFS _ shouldUseCIFS; -- in a procedure to make sure the module is started }; EnumerateForRetrieve: PUBLIC PROC[host, filePattern: LONG STRING, enumProc: STPSubr.RetrieveProcType, h: Subr.TTYProcs, onlyOne: BOOL] ={ stp: STP.Handle; remoteStream: Stream.Handle _ NIL; remoteName: LONG STRING _ NIL; skipRest, tryAgain: BOOL; desiredProperties: STP.DesiredProperties _ ALL[FALSE]; shortFilePattern: LONG STRING _ Subr.AllocateString[100]; Cleanup: PROC = { IF remoteName ~= NIL THEN Heap.systemZone.FREE[@remoteName]; IF remoteStream ~= NIL THEN Stream.Delete[remoteStream]; remoteStream _ NIL; }; TryIt: PROC = { skipRest _ FALSE; remoteName _ NIL; remoteStream _ STP.CreateRemoteStream[stp, shortFilePattern, read ! STP.Error => IF HandleSTPError[stp, code, error, h] THEN RETRY ]; DO ENABLE UNWIND => Cleanup[]; IF remoteName ~= NIL THEN Heap.systemZone.FREE[@remoteName]; remoteName _ STP.NextFileName[remoteStream]; IF remoteName = NIL THEN EXIT; IF skipRest THEN LOOP; skipRest _ enumProc[remoteName, stp, remoteStream]; ENDLOOP; Cleanup[]; }; {ENABLE UNWIND => Subr.FreeString[shortFilePattern]; Subr.strcpy[shortFilePattern, filePattern]; desiredProperties[directory] _ TRUE; desiredProperties[nameBody] _ TRUE; desiredProperties[version] _ TRUE; desiredProperties[createDate] _ TRUE; desiredProperties[size] _ TRUE; DO tryAgain _ FALSE; stp _ Connect[host: host, h: h, onlyOne: onlyOne]; STP.SetDesiredProperties[stp, desiredProperties]; TryIt[ ! STP.Error => IF HandleSTPError[stp, code, error, h] THEN { tryAgain _ TRUE; CONTINUE; } ELSE IF code = connectionClosed THEN { CWF.WF1["Connection to %s timed out.\n"L, host]; tryAgain _ TRUE; CONTINUE; }; ]; IF NOT tryAgain THEN EXIT; stp _ ForceClosed[stp]; IF h.in.UserAbort[] THEN SIGNAL Subr.AbortMyself; ENDLOOP; }; -- of ENABLE UNWIND Subr.FreeString[shortFilePattern]; }; GeneralOpen: PUBLIC PROC[filename: LONG STRING, h: Subr.TTYProcs, access: File.Permissions, fileType: STP.Type, -- FileType -- createtime: LONG CARDINAL] RETURNS[sh: Stream.Handle, stphandle: STP.Handle] = { tryAgain: BOOL; desiredProperties: STP.DesiredProperties _ ALL[FALSE]; host, sfn: LONG STRING _ NIL; sh _ NIL; IF filename[0] ~= '[ AND filename[0] ~= '< THEN { sh _ Subr.NewStream[filename, access]; RETURN[sh, NIL]; }; host _ Subr.AllocateString[40]; sfn _ Subr.AllocateString[100]; {ENABLE UNWIND => {Subr.FreeString[host]; Subr.FreeString[sfn]}; Subr.strcpy[sfn, filename]; StripHost[host, sfn]; StartSTP[]; DO tryAgain _ FALSE; stphandle _ Connect[host, h, FALSE]; desiredProperties[directory] _ TRUE; desiredProperties[nameBody] _ TRUE; desiredProperties[version] _ TRUE; desiredProperties[createDate] _ TRUE; desiredProperties[size] _ TRUE; STP.SetDesiredProperties[stphandle, desiredProperties]; sh _ STP.CreateRemoteStream[stp: stphandle, file: sfn, access: IF access = Subr.Read THEN read ELSE write, fileType: IF access = Subr.Read THEN unknown ELSE (IF fileType = unknown THEN GetFileType[sfn] ELSE fileType), creation: LOOPHOLE[createtime] ! STP.Error => IF code = noSuchFile THEN { CWF.WF2["Error - %s: %s.\n\n"L, filename, error]; ERROR Subr.FileError[notFound]; } ELSE IF code = connectionClosed THEN { CWF.WF1["Connection to %s timed out.\n"L, host]; tryAgain _ TRUE; CONTINUE; } ELSE IF HandleSTPError[stphandle,code,error,h] THEN RETRY]; IF NOT tryAgain THEN EXIT; stphandle _ ForceClosed[stphandle]; IF h.in.UserAbort[] THEN SIGNAL Subr.AbortMyself; ENDLOOP; }; -- of ENABLE UNWIND Subr.FreeString[host]; Subr.FreeString[sfn]; }; PatternGeneralOpen: PUBLIC PROC [filepattern: LONG STRING, proc: STPSubr.RetrieveProcType, h: Subr.TTYProcs] = { host, sfn: LONG STRING _ NIL; IF filepattern[0] ~= '[ AND filepattern[0] ~= '< THEN { sh: Stream.Handle; sh _ Subr.NewStream[filepattern, Subr.Read ! Subr.FileError => GOTO err]; [] _ proc[filepattern, NIL, sh]; Stream.Delete[sh]; RETURN; EXITS err => Subr.errorflg _ TRUE; }; host _ Subr.AllocateString[40]; sfn _ Subr.AllocateString[100]; {ENABLE UNWIND => {Subr.FreeString[host]; Subr.FreeString[sfn]}; Subr.strcpy[sfn, filepattern]; StripHost[host, sfn]; EnumerateForRetrieve[host, sfn, proc, h, TRUE ! STP.Error => IF code = noSuchFile THEN { CWF.WF2["Error - %s: %s.\n\n"L, filepattern, error]; GOTO err; } ]; EXITS err => Subr.errorflg _ TRUE}; -- of ENABLE UNWIND Subr.FreeString[host]; Subr.FreeString[sfn]; }; CachedOpen: PUBLIC PROC[host, directory, shortname: LONG STRING, version: CARDINAL, wantcreatetime: LONG CARDINAL, h: Subr.TTYProcs, stpState: STPSubr.StpState, wantExplicitVersion, onlyOne, tryDollars: BOOL] RETURNS[sh: Stream.Handle, cap: File.Capability] = { cap _ CachedRetrieve[host, directory, shortname, version, wantcreatetime, h, stpState, wantExplicitVersion, onlyOne, tryDollars]; sh _ NIL; IF cap ~= File.nullCapability THEN sh _ FileStream.Create[[cap.fID, File.read]] ELSE sh _ Subr.NewStream[shortname, Subr.Read]; RETURN[sh, cap]; }; CachedRetrieve: PUBLIC PROC[host, directory, shortname: LONG STRING, version: CARDINAL, wantcreatetime: LONG CARDINAL, h: Subr.TTYProcs, stpState: STPSubr.StpState, wantExplicitVersion, onlyOne, tryDollars: BOOL] RETURNS[cap: File.Capability _ File.nullCapability] = { localOnly: BOOL _ host = NIL OR host.length = 0; localcreatetime: LONG CARDINAL _ 0; fileNotRetrieved: BOOL _ FALSE; { cap _ Directory.Lookup[fileName: shortname, permissions: Directory.ignore ! Directory.Error => GOTO notonlocal]; localcreatetime _ Subr.GetCreateDate[cap]; IF localcreatetime = wantcreatetime OR (wantcreatetime = 0 AND localOnly) THEN RETURN; EXITS notonlocal => NULL; }; IF tryDollars THEN { sDollar: LONG STRING _ Subr.AllocateString[100]; checkLocalDate: BOOL _ TRUE; {ENABLE UNWIND => Subr.FreeString[sDollar]; CWF.SWF1[sDollar, "%s$$"L, shortname]; cap _ Directory.Lookup[fileName: sDollar, permissions: Directory.ignore ! Directory.Error => {checkLocalDate _ FALSE; CONTINUE}]; }; Subr.FreeString[sDollar]; IF checkLocalDate THEN { localcreatetime _ Subr.GetCreateDate[cap]; IF localcreatetime = wantcreatetime THEN RETURN; }; }; IF NOT localOnly THEN { targetFileName: LONG STRING _ Subr.AllocateString[125]; localname: LONG STRING _ Subr.AllocateString[100]; dollar: LONG STRING _ Subr.AllocateString[100]; sfn: LONG STRING _ Subr.AllocateString[100]; remoteVersion: CARDINAL; remoteCreateTime: LONG CARDINAL; OneFile: STPSubr.RetrieveProcType = { info: STP.FileInfo _ STP.GetFileInfo[stp]; skipRest _ TRUE; IF tryDollars AND localcreatetime ~= 0 THEN CWF.SWF1[localname, "%s$$"L, shortname] ELSE { IF stpState.checkForOverwrite AND localcreatetime ~= 0 THEN SELECT CheckForOverwrite[shortname, localcreatetime, targetFileName, remoteCreateTime, h] FROM no => { fileNotRetrieved _ TRUE; RETURN; }; substitute => { cap _ Directory.Lookup[fileName: shortname, permissions: Directory.ignore ! Directory.Error => CONTINUE]; RETURN; }; yes => NULL; all => stpState.checkForOverwrite _ FALSE; ENDCASE => ERROR; Subr.strcpy[localname, shortname]; }; CWF.WF1["Retrieving %s "L, targetFileName]; IF tryDollars AND localcreatetime ~= 0 THEN CWF.WF1["(as %s)"L, localname] ELSE IF remoteCreateTime < localcreatetime THEN { CWF.SWF1[dollar, "%s$$"L, shortname]; Directory.DeleteFile[dollar ! Directory.Error => CONTINUE]; Directory.Rename[oldName: shortname, newName: dollar]; CWF.WF1["(local version renamed to %s)"L, dollar]; }; CWF.WF0[" ... "L]; [cap,] _ WriteStreamToDisk[remoteStream, localname, info.size, h]; Subr.SetRemoteFilenameProp[cap, targetFileName]; FileStream.SetLeaderPropertiesForCapability[cap: cap, create: LOOPHOLE[remoteCreateTime]]; CWF.WF1["%lu bytes.\n"L, @info.size]; RETURN; }; {ENABLE UNWIND => { Subr.FreeString[targetFileName]; Subr.FreeString[localname]; Subr.FreeString[dollar]; Subr.FreeString[sfn]; }; fres: FQ.Result; [fres: fres, remoteVersion: remoteVersion, remoteCreateTime: remoteCreateTime] _ FQ.FileQuery[host, directory, shortname, version, wantcreatetime, wantExplicitVersion, h, targetFileName]; SELECT fres FROM foundCorrectVersion => { vstring: STRING; IF h.in.UserAbort[] THEN SIGNAL Subr.AbortMyself; IF localcreatetime = remoteCreateTime THEN GO TO return; -- already on disk vstring _ IF Subr.Prefix[host, "maxc"L] THEN ";"L ELSE "!"L; CWF.SWF4[sfn, "<%s>%s%s%u"L, directory, shortname, vstring, @remoteVersion]; EnumerateForRetrieve[host, sfn, OneFile, h, onlyOne]; IF fileNotRetrieved THEN cap _ File.nullCapability; }; foundWrongVersion => ERROR Subr.FileError[wrongVersion]; notFound => ERROR Subr.FileError[notFound]; ENDCASE => ERROR; EXITS return => {}}; -- of ENABLE UNWIND Subr.FreeString[targetFileName]; Subr.FreeString[localname]; Subr.FreeString[dollar]; Subr.FreeString[sfn]; }; -- of IF NOT localOnly }; CheckForOverwrite: PROC [localname: LONG STRING, localcreate: LONG CARDINAL, remotename: LONG STRING, remotecreate: LONG CARDINAL, h: Subr.TTYProcs] RETURNS[answer: {all, yes, no, substitute}] = { h.out.PutF["Ok to retrieve %s, dated %t,\n\tand overwrite %s of %t ", IO.string[remotename], IO.card[remotecreate], IO.string[localname], IO.card[localcreate]]; SELECT h.Confirm[h.in, h.out, h.data, "", 'y] FROM 'q => SIGNAL Subr.AbortMyself; 'y => answer _ yes; 'l => answer _ substitute; 'a => answer _ all; ENDCASE => answer _ no; }; StartSTP: PROC = { longzone: UNCOUNTED ZONE; IF connseq ~= NIL THEN RETURN; longzone _ Subr.LongZone[]; connseq _ longzone.NEW[ConnSeqRecord[MAXCONNS]]; }; StopSTP: PUBLIC PROC = { ENABLE UNWIND => connseq _ NIL; longzone: UNCOUNTED ZONE; IF connseq = NIL THEN RETURN; longzone _ Subr.LongZone[]; FOR i: CARDINAL IN [0..connseq.size) DO IF connseq[i].stphandle ~= NIL THEN CloseAndFree[i]; ENDLOOP; longzone.FREE[@connseq]; }; Connect: PUBLIC PROC[host: LONG STRING, h: Subr.TTYProcs, onlyOne: BOOL] RETURNS[stphandle: STP.Handle] = { free: CARDINAL; StartSTP[]; IF onlyOne THEN { IF connseq.size > 0 AND connseq[0].host ~= NIL AND NOT LongString.EquivalentString[host, connseq[0].host] THEN { CloseAndFree[0]; IF connseq.size = 1 THEN connseq.size _ 0; }; }; free _ connseq.size; { FOR i: CARDINAL IN [0..connseq.size) DO IF connseq[i].host ~= NIL AND connseq[i].stphandle ~= NIL AND LongString.EquivalentString[host,connseq[i].host] AND LOOPHOLE[connseq[i].stphandle, STPOps.Handle].remoteStream = NIL THEN { free _ i; GOTO leave; }; IF connseq[i].host = NIL THEN free _ i; ENDLOOP; IF free = connseq.size THEN { IF connseq.size >= connseq.maxsize THEN { CWF.WF0["Error - to many conns\n"L]; RETURN[NIL]; }; connseq.size _ connseq.size + 1; }; connseq[free] _ [NIL, NIL]; -- in case MakeSTPHandle signals connseq[free].stphandle _ MakeSTPHandle[host, h]; connseq[free].host _ Subr.CopyString[host]; EXITS leave => NULL; }; STP.SetDesiredProperties[connseq[free].stphandle, ALL[FALSE]]; RETURN[connseq[free].stphandle]; }; ForceClosed: PUBLIC PROC[stphandle: STP.Handle] RETURNS[STP.Handle] = { FOR i: CARDINAL IN [0 .. connseq.size) DO IF stphandle = connseq[i].stphandle THEN CloseAndFree[i]; ENDLOOP; RETURN[NIL]; }; CloseAndFree: PROC[i: CARDINAL] = { CWF.WF1["Closing connection to %s ... "L, connseq[i].host]; connseq[i].stphandle _ STP.Destroy[connseq[i].stphandle ! STP.Error => CONTINUE]; CWF.WF0["closed.\n"L]; Subr.FreeString[connseq[i].host]; connseq[i].host _ NIL; }; CheckStarted: PROC = { IF connseq = NIL THEN ERROR; }; NPAGEBUFFER: CARDINAL = 6; WriteStreamToDisk: PUBLIC PROC[remotesh: Stream.Handle, localfilename: LONG STRING, byteLengthHint: LONG CARDINAL, h: Subr.TTYProcs] RETURNS[cap: File.Capability, nbytes: LONG CARDINAL] = { nxfer, npages: CARDINAL; stopit, flip: BOOL _ FALSE; space: Space.Handle _ Space.nullHandle; localsh: Stream.Handle _ NIL; buffer: LONG POINTER _ NIL; ca: UserTerminal.CursorArray _ ALL[0]; cursorX, cursorY: INTEGER; openFile: CIFS.OpenFile; ftp: UserTerminal.CursorArray _ [ 177400B, 177400B, 177400B, 177400B, 177400B, 177400B, 177400B, 177400B, 000377B, 000377B, 000377B, 000377B, 000377B, 000377B, 000377B, 000377B]; Cleanup: PROC = { IF space ~= Space.nullHandle THEN Space.Delete[space]; space _ Space.nullHandle; IF localsh ~= NIL THEN Stream.Delete[localsh]; localsh _ NIL; IF flip THEN UserTerminal.SetCursorPattern[ca]; IF useCIFS AND openFile ~= NIL THEN CIFS.Close[openFile]; openFile _ NIL; }; npages _ Inline.LowHalf[byteLengthHint/Environment.bytesPerPage] + 2; nbytes _ 0; cap _ File.nullCapability; IF useCIFS THEN { openFile _ CIFS.Open[ConvertUnsafe.ToRope[localfilename], CIFS.replace+CIFS.create+CIFS.write]; cap _ CIFS.GetFC[openFile]; } ELSE { cap _ Directory.CreateFile[localfilename, DCSFileTypes.tLeaderPage, npages ! Directory.Error => { IF type = fileAlreadyExists THEN { cap _ Directory.Lookup[fileName: localfilename, permissions: Directory.ignore]; IF npages > 2 THEN File.SetSize[cap, npages]; } ELSE ERROR Subr.FileError[notFound]; CONTINUE }]; }; localsh _ FileStream.Create[[cap.fID, Subr.Write]]; space _ Space.Create[NPAGEBUFFER, Space.virtualMemory]; Space.Map[space]; buffer _ Space.LongPointer[space]; IF Subr.CursorInWindow[h] THEN { [cursorX, cursorY] _ UserTerminal.cursor^; ca _ UserTerminal.GetCursorPattern[]; UserTerminal.SetCursorPattern[ftp]; flip _ TRUE; }; WHILE NOT stopit DO ENABLE UNWIND => Cleanup[]; [bytesTransferred: nxfer] _ Stream.GetBlock[remotesh, [buffer, 0, NPAGEBUFFER*Environment.bytesPerPage] ! STP.Error => IF code = noSuchFile THEN { CWF.WF1["\n\tError - %s not found.\n"L, localfilename]; EXIT; }; Stream.EndOfStream => { stopit _ TRUE; nxfer _ nextIndex; CONTINUE } ]; IF nxfer = 0 THEN EXIT; Stream.PutBlock[localsh, [buffer, 0, nxfer]]; nbytes _ nbytes + nxfer; IF flip AND cursorX = UserTerminal.cursor^.x AND cursorY = UserTerminal.cursor^.y THEN { bits: UserTerminal.CursorArray _ UserTerminal.GetCursorPattern[]; FOR i: CARDINAL IN [0..16) DO bits[i] _ Inline.BITNOT[bits[i]]; ENDLOOP; UserTerminal.SetCursorPattern[bits]; }; ENDLOOP; Cleanup[]; }; Store: PUBLIC PROC [stphandle: STP.Handle, remoteName: LONG STRING, localCap: File.Capability, createDate: LONG CARDINAL, h: Subr.TTYProcs] RETURNS[nbytes: LONG CARDINAL] = { ft: STP.Type; localStream, remoteStream: Stream.Handle _ NIL; len: LONG CARDINAL; lstr: LONG STRING _ Subr.AllocateString[40]; remName: LONG STRING _ Subr.AllocateString[100]; Cleanup: PROC = { IF localStream ~= NIL THEN Stream.Delete[localStream]; localStream _ NIL; IF remoteStream ~= NIL THEN Stream.Delete[remoteStream]; remoteStream _ NIL; Subr.FreeString[lstr]; Subr.FreeString[remName] }; {ENABLE UNWIND => Cleanup[]; desiredProperties: STP.DesiredProperties _ ALL[FALSE]; nbytes _ 0; Subr.strcpy[remName, remoteName]; ft _ GetFileType[remoteName]; localStream _ FileStream.Create[[localCap.fID, Subr.Read]]; IF ft = unknown THEN ft _ STPOps.FindFileType[localStream]; desiredProperties[directory] _ TRUE; desiredProperties[nameBody] _ TRUE; desiredProperties[version] _ TRUE; STP.SetDesiredProperties[stphandle, desiredProperties]; remoteStream _ STP.CreateRemoteStream[stp: stphandle, file: remName, access: write, fileType: ft, creation: LOOPHOLE[createDate] ! STP.Error => IF HandleSTPError[stphandle, code, error, h] THEN RETRY]; len _ FileStream.GetLength[localStream]; CWF.SWF1[lstr, "%lu"L, @len]; STPOps.SetPListItem[LOOPHOLE[stphandle, STPOps.Handle].plist, "Size"L, lstr]; nbytes _ WriteDiskToStream[localStream, remoteStream, h]; }; -- of ENABLE UNWIND Cleanup[]; }; WriteDiskToStream: PROC [localsh, remotesh: Stream.Handle, h: Subr.TTYProcs] RETURNS[nbytes: LONG CARDINAL] = { nxfer: CARDINAL; stopit, flip: BOOL _ FALSE; space: Space.Handle _ Space.nullHandle; buffer: LONG POINTER _ NIL; cursorX, cursorY: INTEGER; ca: UserTerminal.CursorArray _ ALL[0]; ftp: UserTerminal.CursorArray _ [ 177400B, 177400B, 177400B, 177400B, 177400B, 177400B, 177400B, 177400B, 000377B, 000377B, 000377B, 000377B, 000377B, 000377B, 000377B, 000377B]; Cleanup: PROC = { IF space ~= Space.nullHandle THEN Space.Delete[space]; space _ Space.nullHandle; IF flip THEN UserTerminal.SetCursorPattern[ca]; }; nbytes _ 0; space _ Space.Create[NPAGEBUFFER, Space.virtualMemory]; Space.Map[space]; buffer _ Space.LongPointer[space]; IF Subr.CursorInWindow[h] THEN { [cursorX, cursorY] _ UserTerminal.cursor^; ca _ UserTerminal.GetCursorPattern[]; UserTerminal.SetCursorPattern[ftp]; flip _ TRUE; }; WHILE NOT stopit DO ENABLE UNWIND => Cleanup[]; [bytesTransferred: nxfer] _ Stream.GetBlock[localsh, [buffer, 0, NPAGEBUFFER*Environment.bytesPerPage] ! Stream.EndOfStream => { stopit _ TRUE; nxfer _ nextIndex; CONTINUE } ]; IF nxfer = 0 THEN EXIT; Stream.PutBlock[remotesh, [buffer, 0, nxfer]]; nbytes _ nbytes + nxfer; IF flip AND cursorX = UserTerminal.cursor^.x AND cursorY = UserTerminal.cursor^.y THEN { bits: UserTerminal.CursorArray _ UserTerminal.GetCursorPattern[]; FOR i: CARDINAL IN [0..16) DO bits[i] _ Inline.BITNOT[bits[i]]; ENDLOOP; UserTerminal.SetCursorPattern[bits]; }; ENDLOOP; Cleanup[]; }; StripHost: PROC[host, sfn: LONG STRING] = { IF sfn[0] ~= '[ THEN { Subr.strcpy[host, "Ivy"L]; RETURN; }; host.length _ 0; FOR i: CARDINAL IN [1..sfn.length) DO IF sfn[i] = '] THEN { FOR j: CARDINAL IN [1 .. i-1] DO LongString.AppendChar[host, sfn[j]]; ENDLOOP; Subr.SubStrCopy[sfn, sfn, i+1]; RETURN; }; ENDLOOP; CWF.WF1["Error - %s is not a valid remote file name.\n"L, sfn]; }; NSECPAUSE: CARDINAL = 10; -- # seconds to wait SetNumberOfConnectTries: PUBLIC PROC[nTries: CARDINAL] = { maxNumberOfTries _ nTries; }; MakeSTPHandle: PUBLIC PROC [host: LONG STRING, h: Subr.TTYProcs] RETURNS[stphandle: STP.Handle] = { herald, shorthost: LONG STRING _ NIL; ntries: CARDINAL _ 0; IF host = NIL OR host.length = 0 THEN { CWF.WF0["Error: remote host has not been specified.\n"L]; RETURN[NIL]; }; shorthost _ Subr.AllocateString[40]; {ENABLE UNWIND => Subr.FreeString[shorthost]; stphandle _ STP.Create[]; CWF.WF1["Opening connection to %s.\n"L, host]; Subr.strcpy[shorthost, host]; herald _ STP.Open[stphandle, shorthost ! STP.Error => IF code = connectionRejected THEN { CWF.WF1["Connection rejected by %s.\n"L, host]; ntries _ ntries + 1; IF ntries < maxNumberOfTries THEN { CWF.WF0["Will pause and try again. (Type control-DEL to abort.)\n"L]; IF h.in.UserAbort[] THEN SIGNAL Subr.AbortMyself; Process.Pause[Process.SecondsToTicks[NSECPAUSE]]; IF h.in.UserAbort[] THEN SIGNAL Subr.AbortMyself; RETRY; } ELSE CWF.WF0["Connection rejected too many times.\n"L]; }]; CWF.WF1["%s\n"L,herald]; Heap.systemZone.FREE[@herald]; STPLogin[stphandle]; }; -- of ENABLE UNWIND Subr.FreeString[shorthost]; }; STPLogin: PROC[stphandle: STP.Handle] = { n: LONG STRING _ Subr.AllocateString[50]; p: LONG STRING _ Subr.AllocateString[50]; {ENABLE UNWIND => {Subr.FreeString[n]; Subr.FreeString[p]}; UserCredentialsUnsafe.GetUserCredentials[name: n, password: p]; STP.Login[stphandle, n, p]; }; -- of ENABLE UNWIND Subr.FreeString[n]; Subr.FreeString[p]; }; HandleSTPError: PUBLIC PROC [stphandle: STP.Handle, stpError: STP.ErrorCode, message: LONG STRING, h: Subr.TTYProcs] RETURNS [retryit: BOOL] ={ SELECT stpError FROM illegalUserPassword, illegalUserName, illegalUserAccount, credentailsMissing => SetPass[stphandle, h, message]; illegalConnectName, illegalConnectPassword, accessDenied => SetOtherPass[stphandle, h, message]; ENDCASE => RETURN [FALSE]; RETURN[TRUE]; }; SetPass: PROC[stphandle: STP.Handle, h: Subr.TTYProcs, error: LONG STRING] = { IF error ~= NIL THEN CWF.WF1["Error - %s\n"L,error]; CWF.WF0["Enter "L]; Subr.GetNameandPassword[login, NIL, NIL, h]; CWF.WFCR[]; IF stphandle ~= NIL THEN STPLogin[stphandle]; }; SetOtherPass: PROC[stphandle: STP.Handle, h: Subr.TTYProcs, error: LONG STRING] = { user2: LONG STRING _ Subr.AllocateString[40]; -- must remain short for STP, dammitt! password2: LONG STRING _ Subr.AllocateString[40]; -- must remain short for STP, dammitt! {ENABLE UNWIND => {Subr.FreeString[user2]; Subr.FreeString[password2]}; IF error ~= NIL THEN CWF.WF1["Error - %s\n"L,error]; CWF.WF0["Enter Connect "L]; Subr.GetNameandPassword[connect, user2, password2, h]; IF stphandle ~= NIL AND user2.length > 0 THEN STP.Connect[stphandle, user2, IF password2.length = 0 THEN NIL ELSE password2]; IF user2.length = 0 AND password2.length = 0 THEN { IF h.Confirm[h.in, h.out, h.data, "\nType y to retry with different login name, n to abort ", 'n] = 'n THEN SIGNAL Subr.AbortMyself; CWF.WF0["Enter "L]; Subr.GetNameandPassword[login, NIL, NIL, h]; CWF.WFCR[]; STPLogin[stphandle]; }; }; -- of ENABLE UNWIND Subr.FreeString[user2]; Subr.FreeString[password2]; }; AddUserName: PUBLIC PROC[sto: LONG STRING, spat: LONG STRING, h: Subr.TTYProcs] = { u: LONG STRING _ Subr.AllocateString[40]; -- must remain short for STP, dammitt! p: LONG STRING _ Subr.AllocateString[40]; -- must remain short for STP, dammitt! lengthzero: BOOL; {ENABLE UNWIND => {Subr.FreeString[u]; Subr.FreeString[p]}; UserCredentialsUnsafe.GetUserCredentials[name: NIL, password: p]; lengthzero _ p = NIL OR p.length = 0; DO UserCredentialsUnsafe.GetUserCredentials[name: u, password: NIL]; IF u = NIL OR u.length = 0 OR Subr.Any[u,' ] OR Subr.Prefix[u, "CedarUser"L] OR lengthzero THEN { lengthzero _ FALSE; CWF.WF0["Enter "L]; Subr.GetNameandPassword[login, NIL, NIL, h]; CWF.WFCR[]; } ELSE EXIT; ENDLOOP; IF sto ~= NIL THEN CWF.SWF1[sto,spat,u]; }; -- of ENABLE UNWIND Subr.FreeString[u]; Subr.FreeString[p]; }; GetFileType: PROC[filename: LONG STRING] RETURNS[filetype: STP.Type] = { old: CARDINAL _ filename.length; FOR i: CARDINAL IN [0..old) DO IF filename[i] = '! OR filename[i] = '; THEN { filename.length _ i; EXIT; }; ENDLOOP; filetype _ unknown; IF Subr.EndsIn[filename,".mesa"L] OR Subr.EndsIn[filename,".bravo"L] OR Subr.EndsIn[filename,".config"L] OR Subr.EndsIn[filename,".cm"L] OR Subr.EndsIn[filename, ".mail"L] OR Subr.EndsIn[filename, ".df"L] THEN filetype _ text; IF Subr.EndsIn[filename,".bcd"L] OR Subr.EndsIn[filename,".press"L] OR Subr.EndsIn[filename,".run"L] THEN filetype _ binary; filename.length _ old; }; }. pSTPSubrImpl.Mesa last edit March 8, 1983 12:37 pm last edit May 23, 1983 5:59 pm, Russ Atkinson reduced use of STRING in favor of LONG STRING MDS USAGE !!! endof MDS USAGE !!! connection table we use the username and password from Profile.userName, userPassword nested procedure needed to handle retrys of entire enumerates may generate STP.Error! if not found, raises Subr.FileError, for both remote and local files filename looks like [host]name local case remote file name CWF.WF1["%s ... "L, filename]; just like GeneralOpen but the filename can be a pattern, e.g. with * in it local case may raise Subr.FileError this case can only happen if the user said no to the retrieval in which case the existing file is used try with $$ on end look on remote server the file on the local disk is used the procedures below can be called without using the Connect[] stphandles may raise CIFS.Error[fileBusy] only flips if not moved code from Cursor.Invert this will read the entire local file to check for 8-th bit code from Cursor.Invert takes sfn, strips off the host, puts the host in "host" leaves the remainder in sfn userName and userPassword may be NIL at this point can be called by programs that are not using the connection table in this module can be called by programs that are not using the connection table in this module can be called by programs that are not using the connection table in this module by convention, if not connect name and no password is supplied then we abort can be called by programs that are not using the connection table in this module note: temporarily alters the length of the string to avoid copying ÊG˜J˜šœ™Jšœ ™ šœ-™-Jšœ-™-——J˜šÏk ˜ Jšœœœ7˜GJšœœœ ˜"Jšœœœœ˜2Jšœœœ˜'Jšœ œœA˜VJšœ œœ˜'Jšœœœ:˜JJšœ œœ7˜MJšœœœ˜"Jšœœœ ˜Jšœœœœ ˜%Jšœœœ ˜.Jšœ œœ ˜6Jšœ œœ˜,JšœœœG˜Xšœ œœC˜X˜1J˜7——Jšœœœ&˜>Jšœ œœ˜1Jšœœœ3˜Ešœœœ@˜P˜LJ˜AJ˜——Jšœœœ˜6Jšœœœ;˜SJ˜—šœ ˜šœœœœ˜HJšœœœ ˜7J˜G—Jšœ˜Jšœ ˜J˜—šœ ™ Jšœ œœ˜Jš œ œœœœ˜-JšœœÏc(˜IJšœ™J˜—šœ™Jšœœ˜JšœD™Dšœœœ˜Jšœœž˜'Jšœœ œœ ˜-J˜—šœ œœ˜Jšœ œ œ˜Jšœœœ˜J˜J˜——šÏn œœœœ˜2Jšœž4˜MJ˜J˜—š Ÿœœœœœ˜AJšœ?œ˜GJšœœ˜Jšœœ˜"Jšœ œœœ˜Jšœœ˜Jšœœœœ˜6Jšœœœ˜9J˜šŸœœ˜Jšœœœœ˜™>Jšœ'™'J˜*——Jšœ ˜J˜—J˜š Ÿœœœœœ˜EJšœ œœœ˜CJšœFœ˜LJšœ0˜7Jšœ œ œœ˜0Jšœœœ˜#Jšœœœ˜˜˜IJšœœ ˜&—J˜*šœ"˜$Jšœœ œœ˜2—Jšœœ˜J˜—Jšœ™šœ œ˜Jšœ œœ˜0Jšœœœ˜šœœœ˜+Jšœ#˜&˜GJšœ'œœ˜9—J˜—J˜šœœ˜J˜*Jšœ"œœ˜0J˜—J˜—Jšœ™šœœ œ˜Jšœœœ˜7Jšœ œœ˜2Jšœœœ˜/Jšœœœ˜,Jšœœ˜Jšœœœ˜ J˜˜%Jšœœ œ˜*Jšœ œ˜šœ œœ˜,Jšœ$˜'šœ˜Jšœ˜Jšœ˜šœ˜šœ.˜4Jšœ%˜)—˜ Jšœœ˜Jšœ˜J˜—˜˜IJšœœ˜—Jšœ"™"Jšœ˜J˜—Jšœœ˜ Jšœ$œ˜*Jšœœ˜—J˜"J˜——Jšœ(˜+šœ œ˜+Jšœ˜šœœ$œ˜1Jšœ"˜%Jšœ1œ˜;J˜6Jšœ/˜2J˜——Jšœ˜˜)J˜—J˜0˜6Jšœœ˜$—Jšœ"˜%Jšœ˜J˜—šœ˜šœ˜ J˜Jšœ˜ J˜—J˜š Ÿ œœœ œ œœ ˜Gšœœœ˜)šœ"œ˜)J˜—Jšœ˜—Jšœœ˜ J˜—J˜šŸ œœœ˜#Jšœ8˜;šœœ˜7Jšœœ œ˜—Jšœ˜J˜!Jšœœ˜J˜—J˜J˜šŸ œœ˜Jšœ œœœ˜J˜—J˜JšœI™IJ˜Jš œœ˜J˜Jšœ™šŸœœœ˜8Jš œœœœœ˜;J˜Jšœœœ˜8Jšœœ˜Jšœœœ˜J˜'Jšœœ˜Jšœœœœ˜Jšœœ˜&Jšœœ˜Jšœ œ ˜˜!J˜#J˜#J˜#J˜$J˜JšŸœœ˜Jšœœ˜6J˜Jšœ œœ˜.Jšœ œ˜Jšœœ#˜/Jš œ œ œœœ˜9Jšœ œ˜J˜J˜—J˜EJ˜ J˜šœ œ˜šœ œ*˜9Jšœ œœ˜%—Jšœœ˜J˜—šœ˜˜J˜šœœ˜"˜0J˜—Jšœ œ˜-J˜—Jšœœ˜$Jš˜—J˜—J˜—J˜3Jšœ œ˜7J˜J˜"šœœ˜ J˜*J˜%J˜#Jšœœ˜ J˜—šœœ˜Jšœœ˜˜6Jšœ œ˜1šœœ œœ˜*Jšœ4˜7Jšœ˜J˜˜Jšœ œ˜J˜Jš˜J˜——J˜—Jšœ œœ˜J˜-J˜Jšœ™Jšœœ"˜-šœ"œ˜+Jšœ™˜Ašœœœ ˜Jšœœ ˜!Jšœ˜—J˜$—J˜—Jšœ˜—J˜ J˜—J˜šŸœœ˜Jš œ œœœ)œœ˜xJšœ œœ˜"Jšœœ˜ Jšœ+œ˜/Jšœœœ˜Jšœœœ˜,Jšœ œœ˜0J˜šŸœœ˜Jšœœœ˜6Jšœœ˜Jšœœœ˜8Jšœœ˜J˜/J˜J˜—šœœœ˜Jšœœœœ˜6J˜ J˜!J˜J˜;Jšœ:™:Jšœœ'˜;Jšœœ˜$Jšœœ˜#Jšœœ˜"Jšœ4˜7šœœ2˜DJšœ'œ ˜;šœœ œ ˜1Jšœ œœ˜——J˜(Jšœ˜Jšœœ1˜MJ˜9Jšœžœ˜—J˜ J˜—J˜šŸœ˜Jšœ5œ œœ˜WJšœœ˜Jšœœœ˜J˜'Jšœœœœ˜Jšœœ˜Jšœœ˜&˜!J˜#J˜#J˜#J˜$J˜J˜—šŸœœ˜Jšœœ˜6J˜Jšœœ#˜/J˜J˜—J˜ Jšœ œ˜7J˜J˜"šœœ˜ J˜*J˜%J˜#Jšœœ˜ J˜—šœœ˜Jšœœ˜˜5Jšœ œ˜1˜Jšœ œ˜J˜Jš˜J˜—J˜—Jšœ œœ˜J˜.J˜Jšœœ"˜-šœ"œ˜+Jšœ™˜Ašœœœ ˜Jšœœ ˜!Jšœ˜—J˜$—J˜—Jšœ˜—J˜ J˜—J˜šŸ œœ œœ˜+Jšœ7™7Jšœ™šœœ˜J˜Jšœ˜J˜—J˜šœœœ˜%šœ œ˜šœœœ ˜ J˜$Jšœ˜—J˜Jšœ˜J˜—Jšœ˜—Jšœ<˜?J˜—J˜Jš œœž˜.J˜šŸœœœ œ˜:J˜J˜J˜—šŸ œœ˜Jš œœœœ œ ˜HJšœœ œ˜%Jšœœ˜šœœœœ˜'Jšœ6˜9Jšœœ˜ J˜—Jšœ$˜$šœœœ˜-Jšœ œ ˜Jšœ+˜.J˜šœ œ˜&šœœ œœ˜2Jšœ,˜/J˜šœœ˜#JšœC˜FJšœœœ˜1Jšœ% œ˜1Jšœœœ˜1Jšœ˜J˜—Jšœœ/˜7J˜——Jšœ˜Jšœœ ˜Jšœ2™2J˜Jšœž˜—J˜J˜—J˜šŸœœ œ ˜)Jšœœœ˜)Jšœœœ˜)Jšœœœ-˜;J˜?Jšœ˜Jšœž˜J˜'J˜—J˜šŸœœ˜Jš œ œœœœ˜XJšœ œ˜JšœP™Pšœ˜šœO˜OJ˜—šœ;˜;J˜$—J˜—Jšœœ˜ J˜—J˜š Ÿœœ œ"œœ˜NJšœP™PJšœ œœœ˜4Jšœ˜Jšœœœ˜,Jšœœ˜ Jšœœœ˜-J˜—J˜š Ÿ œœ œ"œœ˜SJšœP™PJšœœœž&˜TJšœ œœž&˜Xšœœœ9˜GJšœ œœœ˜4Jšœ˜J˜6šœœœœ˜.šœ˜Jšœœœœ ˜1——JšœL™Lšœœœ˜3šœ˜!šœEœ˜JJšœ˜——Jšœ˜Jšœœœ˜,Jšœœ˜ J˜J˜—Jšœž˜—J˜3J˜—J˜šŸ œœœœœœœ˜SJšœP™PJšœœœž&˜PJšœœœž&˜PJšœ œ˜šœœœ-˜;Jšœ/œ˜AJšœœœ˜%š˜Jšœ<œ˜Ašœ˜ Jšœ˜Jšœ˜Jšœ˜Jšœ ˜ šœ˜Jšœ œ˜Jšœ˜Jšœœœ˜,Jšœœ˜ J˜—Jšœœ˜ —Jšœ˜—Jšœœœœ˜(Jšœž˜—J˜'J˜—J˜š Ÿ œœ œœœ œ ˜HJšœB™BJšœœ˜ šœœœ ˜šœœœ˜.J˜Jšœ˜J˜—Jšœ˜—J˜šœ œ ˜DJšœ"œ˜CJšœ!œœ˜IJ˜—šœœ ˜CJšœœ˜8—J˜J˜—J˜J˜J˜—…—^,‚ã