<> <> <> <> 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] = { <> <name>> 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; }; }.