-- FileOp.mesa -- edited by Schroeder, March 16, 1981 6:04 PM -- edited by Brotz, February 13, 1981 11:44 AM DIRECTORY crD: FROM "CoreDefs", csD: FROM "CoreStreamDefs", dsD: FROM "DisplayDefs", exD: FROM "ExceptionDefs", FTPDefs, gsD: FROM "GlobalStorageDefs", InlineDefs, intCommon: FROM "IntCommon", inD: FROM "InteractorDefs", LaurelSendDefs, opD: FROM "OperationsDefs", ovD: FROM "OverviewDefs", SendDefs, Storage, String, Stream, TimeDefs, TimeExtraDefs; FileOp: PROGRAM IMPORTS crD, csD, dsD, exD, FTPDefs, gsD, InlineDefs, intC: intCommon, LaurelSendDefs, SendDefs, Storage, String, TimeDefs, TimeExtraDefs EXPORTS opD SHARES opD = PUBLIC BEGIN ftpInitialized: BOOLEAN _ FALSE; expandProtocol: LaurelSendDefs.ProtocolType = LaurelSendDefs.GetSendProtocol[]; -- utility procedures NameType: TYPE = {remote, dl, local, bad}; ParseName: PROCEDURE [name: STRING] RETURNS [t: NameType, part1, part2: STRING] = BEGIN start: [0..1]; i, upArrowIndex: CARDINAL; upArrowSeen: BOOLEAN _ FALSE; part1 _ NIL; part2 _ NIL; IF name = NIL OR name.length = 0 THEN GOTO bad; start _ IF name[0] = '@ THEN 1 ELSE 0; FOR i IN [start .. name.length) DO SELECT name[i] FROM '^ => {upArrowSeen _ TRUE; upArrowIndex _ i}; '* => GOTO bad; ENDCASE; ENDLOOP; SELECT TRUE FROM name[start] = '[ => --remote-- BEGIN t _ remote; [part1, i] _ MakeDelimitedString['[, '], start, name]; IF part1 = NIL THEN GOTO bad; [part2, ] _ MakeDelimitedString['], 0C, i, name]; IF part2 = NIL THEN GOTO bad; END; upArrowSeen => --dl-- BEGIN t _ dl; IF upArrowIndex+1 < name.length --chars after ^ must be registry-- THEN BEGIN [part1, ] _ MakeDelimitedString['., 0C, upArrowIndex+1, name]; IF part1 = NIL THEN GOTO bad; [part2, ] _ MakeDelimitedString[0C, 0C, start, name]; END ELSE BEGIN part1 _ MakeHeapString [intC.user.registry]; [part2, ] _ MakeDelimitedString[0C, 0C, start, name, part1.length+1]; String.AppendChar[part2, '.]; String.AppendString[part2, part1]; END; END; ENDCASE => --local-- BEGIN t _ local; [part1, ] _ MakeDelimitedString[0C, 0C, start, name]; END; EXITS bad => t _ bad; END; --of ParseName-- MakeDelimitedString: PRIVATE PROCEDURE [startChar, endChar: CHARACTER, start: CARDINAL, source: STRING, space: CARDINAL _ 0] RETURNS [target: STRING, next: CARDINAL] = -- If source[start] = startChar and endChar appears later in source, allocates a -- string for target and places in it all the intervening characters from -- source. Next is index of endChar in source. (startChar = NIL means target -- unconditionally begins with source[start]; endChar = NIL means target -- unconditionally ends with last character of source.) IF result would be of -- length 0 or startChar or endChar are not found then returns target = NIL. -- When a string is returned it will have space extra character slots at the end. BEGIN index, length: CARDINAL; target _ NIL; IF source.length <= start THEN RETURN; IF startChar # 0C THEN {IF source[start] # startChar THEN RETURN; start _ start + 1}; IF endChar # 0C THEN FOR next IN [start .. source.length) DO IF source[next] = endChar THEN EXIT; REPEAT FINISHED => RETURN; ENDLOOP ELSE next _ source.length; length _ next - start; IF length = 0 THEN RETURN; target _ Storage.String[length+space]; FOR index IN [0 .. length) DO target[index] _ source[index+start]; ENDLOOP; target.length _ length; END; -- of MakeDelimitedString -- MakeHeapString: PRIVATE PROCEDURE [input1: STRING, input2: STRING _ NIL, input3: STRING _ NIL, input4: STRING _ NIL] RETURNS [output: STRING] = -- Concatenate string bodies into a heap string BEGIN IF input1 = NIL THEN output _ NIL ELSE BEGIN length: CARDINAL _ input1.length; IF input2 # NIL THEN length _ length + input2.length; IF input3 # NIL THEN length _ length + input3.length; IF input4 # NIL THEN length _ length + input4.length; output _ Storage.String[length]; String.AppendString[output, input1]; IF input2 # NIL THEN String.AppendString[output, input2]; IF input3 # NIL THEN String.AppendString[output, input3]; IF input4 # NIL THEN String.AppendString[output, input4]; END; END; -- of MakeHeapString -- FreeErrorString: PUBLIC PROCEDURE [string: STRING] = {Storage.FreeString[string]}; MakeLocalFileErrorString: PRIVATE PROCEDURE [e: ovD.ErrorCode] RETURNS [STRING] = { RETURN [ MakeHeapString [ "Local file "L, SELECT e FROM ovD.illegalFilename => "name illegal."L, ovD.fileNotFound => "name not found."L, ovD.fileInUse => "already in use."L, ovD.diskFull => "would overfill disk."L, ovD.fileTooBig => "would be too big."L, ENDCASE => "disk error."L] ] }; MakeCantConnectMessage: PROCEDURE[h, fs: STRING] RETURNS [ovD.ErrorCode, STRING] = {RETURN[ovD.cantConnect, MakeHeapString["Can't connect to """L, h, """: "L, fs]]}; ReportBadName: PROCEDURE RETURNS [ovD.ErrorCode, STRING] = {RETURN[ovD.illegalFilename, MakeHeapString["Illegal name given."L]]}; MakeFTPError: PROCEDURE [h, m, e: STRING] RETURNS [s: STRING]= BEGIN IF e#NIL THEN Storage.FreeString[e]; s _ MakeHeapString ["FTP error from """L, h, """: "L, m]; END; InitializeFilePrimitives: PRIVATE PROCEDURE [fp: FTPDefs.FilePrimitives] = BEGIN fp.CreateFileSystem _ MyCreateFileSystem; fp.DestroyFileSystem _ MyDestroyFileSystem; fp.CloseFile _ MyCloseFile; END; SetUpFTP: PROCEDURE = {IF NOT ftpInitialized THEN {FTPDefs.FTPInitialize[]; ftpInitialized _ TRUE}}; TryNextCredentials: PROCEDURE[f: FTPDefs.FTPUser, try: CARDINAL] RETURNS [nextTry: CARDINAL] = --first try is #1, returns 0 when client should give up BEGIN pr, heapName: BOOLEAN _ TRUE; n, p: STRING; guestString: STRING = "Guest"L; nextTry _ try + 1; SELECT try FROM 1 => {n _ MakeHeapString[intC.user.name, "."L, intC.user.registry]; p _ intC.user.password}; 2 => {n _ intC.user.name; p _ intC.user.password; heapName _ FALSE}; 3 => {n _ p _ guestString; heapName _ FALSE}; 4 => {n _ intC.user.name; p _ intC.user.password; pr _ heapName _ FALSE}; ENDCASE => {nextTry _ 0; RETURN}; FTPDefs.FTPSetCredentials[f, IF pr THEN primary ELSE secondary, n, p]; IF heapName THEN Storage.FreeString[n]; END; --of TryNextCredentials-- -- procedures and declarations of my ftp file system MyCreateFileSystem: PRIVATE PROCEDURE [bufferSize: CARDINAL] RETURNS [fileSystem: FTPDefs.FileSystem] = {RETURN[NIL]}; MyDestroyFileSystem: PRIVATE PROCEDURE [fileSystem: FTPDefs.FileSystem] = {}; MyCloseFile: PRIVATE PROCEDURE [fileSystem: FTPDefs.FileSystem, fileHandle: FTPDefs.FileHandle, aborted: BOOLEAN] = {}; Expand: PROCEDURE [sourceName: STRING, AcceptBlock: PROC [POINTER, CARDINAL] RETURNS [ovD.ErrorCode], GetReady: PROC [LONG CARDINAL, TimeDefs.PackedTime] _ NIL ] RETURNS [ovD.ErrorCode, STRING] = BEGIN RetrieveLocalFile: PROCEDURE [fileName: STRING] RETURNS [code: ovD.ErrorCode, errorString: STRING] = -- retrieves from a local file BEGIN BlockAcceptor: PROCEDURE [b: Stream.Block] = BEGIN IF b.startIndex#0 AND b.startIndex#b.stopIndexPlusOne THEN exD.SysBug[]; code _ AcceptBlock[LOOPHOLE[InlineDefs.LowHalf[b.blockPointer],POINTER], InlineDefs.LowHalf[b.stopIndexPlusOne-b.startIndex] ]; SwingPendulum[]; IF code#ovD.ok THEN ERROR csD.Error[ovD.cancelCode]; END; sH: csD.StreamHandle _ NIL; fH: crD.UFileHandle _ NIL; length: LONG CARDINAL; errorString _ NIL; BEGIN ENABLE csD.Error => BEGIN IF reason#ovD.cancelCode THEN errorString _ MakeLocalFileErrorString[code_reason]; GOTO destroy; END; [code, fH] _ crD.OpenFile[intC.user, fileName, read]; IF code#ovD.ok THEN ERROR csD.Error[code]; sH _ csD.Open[fH, byte, read, 2]; length _ csD.GetLength[sH]; IF GetReady#NIL THEN BEGIN created: TimeDefs.PackedTime; [ , , created, code] _ crD.GetUFileTimes[fH]; IF code#ovD.ok THEN ERROR csD.Error[code]; GetReady[length, created]; END; csD.ReadStream[sH, length, BlockAcceptor]; EXITS destroy => NULL; END; --of enable-- IF sH#NIL THEN csD.Destroy[sH]; IF fH#NIL THEN [] _ crD.CloseFile[fH]; END; --RetrieveLocalFile-- RetrieveRemoteFile: PROCEDURE [dlExpansion: BOOLEAN, hostName, firstName: STRING] RETURNS [errorCode: ovD.ErrorCode, errorString: STRING] = BEGIN MyOpenFile: PRIVATE PROCEDURE [fileSystem: FTPDefs.FileSystem, file: STRING, mode: FTPDefs.Mode, fileTypePlease: BOOLEAN, info: FTPDefs.FileInfo] RETURNS [fileHandle: FTPDefs.FileHandle, fileType: FTPDefs.FileType] = BEGIN IF GetReady#NIL THEN BEGIN IF info=NIL THEN GetReady[0, TimeDefs.DefaultTime] ELSE GetReady[info.byteCount, TimeExtraDefs.PackedTimeFromString[info.creationDate]]; END; RETURN[NIL, unknown]; END; --MyOpenFile-- MyWriteFile: PROCEDURE [fileSystem: FTPDefs.FileSystem, fileHandle: FTPDefs.FileHandle, receiveBlock: PROC [UNSPECIFIED, POINTER, CARDINAL] RETURNS [CARDINAL], receiveBlockData: UNSPECIFIED] = BEGIN bytes: CARDINAL; bP: gsD.MemoryPagePtr _ gsD.GetMemoryPages[2]; DO ENABLE UNWIND => gsD.ReturnMemoryPages[2, bP]; bytes _ receiveBlock[receiveBlockData, bP, 512]; IF (errorCode_AcceptBlock[bP, bytes])#ovD.ok THEN ERROR FTPDefs.FTPError[noRoomForFile, NIL]; SwingPendulum[]; IF bytes=0 THEN EXIT; ENDLOOP; gsD.ReturnMemoryPages[2, bP]; END; -- of MyWriteFile -- -- variables for RetrieveRemoteFile ftpUser: FTPDefs.FTPUser; myFilePrimitivesObject: FTPDefs.FilePrimitivesObject; myFilePrimitives: FTPDefs.FilePrimitives = @myFilePrimitivesObject; -- code for RetrieveRemoteFile errorString _ NIL; SetUpFTP[]; InitializeFilePrimitives[myFilePrimitives]; myFilePrimitives.OpenFile _ MyOpenFile; myFilePrimitives.WriteFile _ MyWriteFile; ftpUser _ FTPDefs.FTPCreateUser [myFilePrimitives, FTPDefs.PupCommunicationPrimitives[]]; BEGIN -- block for EXITS try: CARDINAL _ 1; FTPDefs.FTPOpenConnection[ftpUser, hostName, files, NIL ! FTPDefs.FTPError => BEGIN [errorCode, errorString] _ MakeCantConnectMessage[hostName, message]; GOTO stop; END]; DO IF NOT dlExpansion THEN try _ TryNextCredentials[ftpUser, try]; IF try=0 THEN {errorCode _ ovD.ftpError; GOTO stop}; [] _ FTPDefs.FTPRetrieveFile[ftpUser, NIL, firstName, unknown ! FTPDefs.FTPError => BEGIN SELECT ftpError FROM noRoomForFile => GOTO freeOldErrorString; --errorCode set-- noSuchPrimaryUser, incorrectPrimaryPassword, noSuchSecondaryUser, incorrectSecondaryPassword, requestedAccessDenied => BEGIN IF try=2 THEN --save first credentials error message-- errorString _ MakeFTPError[hostName, message, errorString]; LOOP; END; ENDCASE; errorString _ MakeFTPError[hostName, message, errorString]; errorCode _ ovD.ftpError; EXIT END]; errorCode _ ovD.ok; GOTO freeOldErrorString; ENDLOOP; EXITS freeOldErrorString => IF errorString#NIL THEN {Storage.FreeString[errorString]; errorString_NIL}; stop => NULL; END; -- block for EXITS FTPDefs.FTPDestroyUser[ftpUser]; END; -- of RetrieveRemoteFile -- GVExpand: PROCEDURE [dlName: STRING] RETURNS [ec: ovD.ErrorCode, es: STRING] = BEGIN Work: PROCEDURE[n: STRING] = BEGIN IF workCalled THEN PutString[", "L] ELSE BEGIN IF GetReady#NIL THEN GetReady[0, TimeDefs.DefaultTime]; workCalled _ TRUE END; PutString[n]; END; -- of Work-- PutString: PROCEDURE [s: STRING] = BEGIN IF AcceptBlock[@s.text, s.length]#ovD.ok THEN {reason _ canceled; ERROR SendDefs.ExpandFailed}; SwingPendulum[]; END; --of PutString-- workCalled: BOOLEAN _ FALSE; reason: {canceled, failed, noGroup, noMatch, noRServers} _ failed; BEGIN --for exits-- SELECT SendDefs.Expand[dlName, Work ! SendDefs.ExpandFailed => IF workCalled THEN GOTO error ELSE RETRY] FROM ok => {ec _ ovD.ok; es _ NIL}; notFound => {reason _ noMatch; GOTO error}; individual => {reason _ noGroup; GOTO error}; allDown => {reason _ noRServers; GOTO error}; ENDCASE => exD.SysBug[]; PutString[""L ! SendDefs.ExpandFailed => GOTO error]; EXITS error => BEGIN es _ MakeHeapString["GV error: "L, SELECT reason FROM canceled => NIL, failed => "Communication problem."L, noMatch, noGroup => "DL name not found."L, noRServers => "No server for that registry responded."L, ENDCASE => ERROR]; ec _ SELECT reason FROM canceled => ovD.cancelCode, failed => ovD.ftpError, noGroup, noMatch => ovD.fileNotFound, noRServers => ovD.cantConnect, ENDCASE => ERROR; END; END; END; -- code for Expand -- erc: ovD.ErrorCode; firstPart, secondPart, ers: STRING; t: NameType; oldShape: dsD.CursorShape; [t, firstPart, secondPart] _ ParseName[sourceName]; [oldShape, , ] _ dsD.GetCursor[]; SELECT t FROM local => [erc, ers] _ RetrieveLocalFile [firstPart]; remote => [erc, ers] _ RetrieveRemoteFile[FALSE, firstPart, secondPart]; dl => IF expandProtocol = mtp THEN [erc, ers] _ RetrieveRemoteFile[TRUE, firstPart, secondPart] ELSE [erc, ers] _ GVExpand[secondPart]; ENDCASE => [erc, ers] _ ReportBadName[]; IF firstPart # NIL THEN Storage.FreeString[firstPart]; IF secondPart # NIL THEN Storage.FreeString[secondPart]; dsD.ChangeCursor[oldShape]; RETURN [erc, ers]; END; -- of Expand -- Stuff: PROCEDURE [ targetName: STRING, GetBlock: PROC RETURNS [POINTER, CARDINAL, ovD.ErrorCode], OverwriteOK: PROC RETURNS [BOOLEAN], createTime: TimeDefs.PackedTime _ TimeDefs.DefaultTime, callerFileType: FTPDefs.FileType _ binary] RETURNS [ovD.ErrorCode, STRING] = BEGIN StoreLocalFile: PROCEDURE [fileName: STRING] RETURNS [code: ovD.ErrorCode, errorString: STRING] = -- stores to a local file BEGIN sH: csD.StreamHandle _ NIL; fH: crD.UFileHandle _ NIL; bP: POINTER; bC: CARDINAL; newFile: BOOLEAN _ FALSE; errorString _ NIL; BEGIN ENABLE csD.Error => BEGIN errorString _ MakeLocalFileErrorString[code _ reason]; GOTO delete; END; [code, fH] _ crD.OpenFile[intC.user, fileName, read]; SELECT code FROM ovD.ok => BEGIN p, b: CARDINAL; [ , p, b] _ crD.UFileLength[fH]; newFile _ p = 0 AND b = 0; END; ovD.fileNotFound => newFile _ TRUE; ENDCASE => ERROR csD.Error[code]; [] _ crD.CloseFile[fH]; fH _ NIL; IF newFile OR OverwriteOK[] THEN BEGIN [code, fH] _ crD.OpenFile[intC.user, fileName, update]; IF code#ovD.ok THEN ERROR csD.Error[code]; sH _ csD.Open[fH, byte, overwrite, 2]; DO [bP, bC, code] _ GetBlock[]; IF code#ovD.ok THEN GOTO delete; csD.WriteBlock[sH, bP, 0, bC]; SwingPendulum[]; IF bC=0 THEN EXIT; ENDLOOP; IF createTime#TimeDefs.DefaultTime THEN BEGIN code _ crD.SetUFileTimes[uFH: fH, create: createTime]; IF code#ovD.ok THEN ERROR csD.Error[code]; END; csD.Checkpoint[sH]; GOTO close; END; code _ ovD.cancelCode; -- client canceled the file overwrite-- EXITS delete => IF fH#NIL THEN [] _ crD.DeleteFile[fH]; close => IF fH#NIL THEN [] _ crD.CloseFile[fH]; END; IF sH#NIL THEN csD.Destroy[sH]; END; --StoreLocalFile-- StoreRemoteFile: PROCEDURE[host, fileName: STRING] RETURNS [errorCode: ovD.ErrorCode, errorString: STRING] = BEGIN MyOpenFile: PRIVATE PROCEDURE [fileSystem: FTPDefs.FileSystem, file: STRING, mode: FTPDefs.Mode, fileTypePlease: BOOLEAN, info: FTPDefs.FileInfo] RETURNS [fileHandle: FTPDefs.FileHandle, fileType: FTPDefs.FileType] = BEGIN IF createTime#TimeDefs.DefaultTime AND info#NIL AND info.creationDate#NIL THEN TimeDefs.AppendDayTime [info.creationDate, TimeDefs.UnpackDT[createTime] ! TimeDefs.InvalidTime => CONTINUE]; RETURN[NIL, callerFileType]; END; --MyOpenFile-- MyReadFile: PROCEDURE [fileSystem: FTPDefs.FileSystem, fileHandle: FTPDefs.FileHandle, sendBlock: PROC [UNSPECIFIED, POINTER, CARDINAL], sendBlockData: UNSPECIFIED] = -- to be called by FTPStoreFile BEGIN bP: POINTER; bC: CARDINAL; DO [bP, bC, errorCode] _ GetBlock[]; IF errorCode#ovD.ok THEN ERROR FTPDefs.FTPError[fileDataError, NIL]; sendBlock[sendBlockData, bP, bC]; SwingPendulum[]; IF bC=0 THEN EXIT; ENDLOOP; END; --MyReadFile-- -- variables for StoreRemoteFile ftpUser: FTPDefs.FTPUser _ NIL; myFilePrimitivesObject: FTPDefs.FilePrimitivesObject; myFilePrimitives: FTPDefs.FilePrimitives = @myFilePrimitivesObject; -- code for StoreRemoteFile errorString _ NIL; SetUpFTP[]; InitializeFilePrimitives[myFilePrimitives]; myFilePrimitives.OpenFile _ MyOpenFile; myFilePrimitives.ReadFile _ MyReadFile; ftpUser _ FTPDefs.FTPCreateUser [myFilePrimitives, FTPDefs.PupCommunicationPrimitives[]]; BEGIN -- block for EXITS try: CARDINAL _ 1; FTPDefs.FTPOpenConnection[ftpUser, host, files, NIL ! FTPDefs.FTPError => BEGIN [errorCode, errorString] _ MakeCantConnectMessage[host, message]; GOTO stop; END]; DO try _ TryNextCredentials[ftpUser, try]; IF try=0 THEN {errorCode _ ovD.ftpError; GOTO stop}; [] _ FTPDefs.FTPStoreFile[ftpUser, NIL, fileName, callerFileType ! FTPDefs.FTPError => BEGIN SELECT ftpError FROM fileDataError => GOTO deleteOldErrorString; --errorCode already set-- noSuchPrimaryUser, incorrectPrimaryPassword, noSuchSecondaryUser, incorrectSecondaryPassword, requestedAccessDenied => BEGIN IF try = 2 THEN errorString _ MakeFTPError[host, message, errorString]; LOOP END; ENDCASE; errorString _ MakeFTPError[host, message, errorString]; errorCode _ ovD.ftpError; EXIT END]; errorCode _ ovD.ok; GOTO deleteOldErrorString; ENDLOOP; EXITS deleteOldErrorString => IF errorString#NIL THEN {Storage.FreeString[errorString]; errorString_NIL}; stop => NULL; END; -- block for EXITS IF ftpUser # NIL THEN FTPDefs.FTPDestroyUser[ftpUser]; END; -- of StoreRemoteFile-- --code for Stuff erc: ovD.ErrorCode; firstPart, secondPart, ers: STRING; t: NameType; oldShape: dsD.CursorShape; [t, firstPart, secondPart] _ ParseName[targetName]; [oldShape, , ] _ dsD.GetCursor[]; SELECT t FROM local => [erc, ers] _ StoreLocalFile [firstPart]; remote => [erc, ers] _ StoreRemoteFile[firstPart, secondPart]; ENDCASE => [erc, ers] _ ReportBadName[]; IF firstPart # NIL THEN Storage.FreeString[firstPart]; IF secondPart # NIL THEN Storage.FreeString[secondPart]; dsD.ChangeCursor[oldShape]; RETURN [erc, ers]; END; -- of Stuff -- pendulumState: {left, right} _ right; swingTime: CARDINAL _ inD.realTimeClock^; SwingPendulum: PROCEDURE = BEGIN leftPendulum: dsD.CursorBitMap = [177740B, 147140B, 150540B, 160340B, 162340B, 165340B, 150540B, 147140B, 142140B, 142140B, 144140B, 144140B, 154140B, 154140B, 140140B, 177740B]; rightPendulum: dsD.CursorBitMap = [177740B, 147140B, 150540B, 160340B, 162340B, 165340B, 150540B, 147140B, 142140B, 142140B, 141140B, 141140B, 141540B, 141540B, 140140B, 177740B]; IF inD.realTimeClock^ - swingTime < 4 THEN RETURN; swingTime _ inD.realTimeClock^; IF pendulumState = right THEN BEGIN pendulumState _ left; dsD.cursorBM^ _ leftPendulum; END ELSE BEGIN pendulumState _ right; dsD.cursorBM^ _ rightPendulum; END; END; -- of SwingPendulum -- END. -- of RetrieveOp -- (635)\f1 16131f0 1f1 26f0 1f1 28f0 1f1 18f0 1f1 47f0 1f1 15f0 1f1 64f0 1f1 52f0 1f1 14f0 1f1 27f0 1f1 19f0 1f1