-- PDRemoteStreamImpl.mesa
-- Copyright (C) 1984, Xerox Corporation.  All rights reserved.
-- Michael Plass, September 11, 1984 4:00:48 pm PDT
	-- 
DIRECTORY IO, Stream, STP, Rope, PDRemoteStream, String;
	
PDRemoteStreamImpl: PROGRAM
	IMPORTS Rope, STP, String, Stream
	EXPORTS PDRemoteStream
	= BEGIN
		
	Error: PUBLIC ERROR [expl: LONG STRING, retryable: BOOL] = CODE;
		
	RopeFromString: PROC [string: LONG STRING] RETURNS [Rope.ROPE] = {
		i: INTEGER ← -1;
		fet: SAFE PROC RETURNS [CHAR] = TRUSTED {RETURN [string[i←i+1]]};
		IF string = NIL THEN RETURN [NIL];
		RETURN [Rope.FromProc[string.length, fet]];
		};
		
	Rs: PROC [string: LONG STRING] RETURNS [Rope.ROPE] = RopeFromString;
		
	AppendRope: PROC [string: LONG STRING, rope: Rope.ROPE] = {
		action: Rope.ActionType = TRUSTED {
			string[string.length] ← c;
			string.length ← string.length + 1;
			};
		[] ← rope.Map[action: action];
		};
		
	Retryable: PROC [code: STP.ErrorCode] RETURNS [BOOL] = {
		RETURN [SELECT code FROM connectionTimedOut, connectionRejected, connectionClosed, noConnection, noNameLookupResponse => TRUE, ENDCASE => FALSE]
		};
		
	Lookup: PUBLIC PROC [fileName: LONG STRING, createDate: LONG STRING, name, password: LONG STRING] RETURNS [bytes: INT] = {
		bytes ← MyLookup[fileName, createDate, name, password !
			STP.Error => TRUSTED {
				expl: LONG STRING ← [80];
				AppendRope[expl, error];
				ERROR Error[expl: expl, retryable: Retryable[code]]
				};
			];
		};
		
	ParseTest: PROC [fileName: Rope.ROPE] RETURNS [server, rest: Rope.ROPE] = {
		f: LONG STRING ← [80];
		s: LONG STRING ← [80];
		r: LONG STRING ← [80];
		AppendRope[f, fileName];
		ParseName[f, s, r];
		server ← Rs[s];
		rest ← Rs[r];
		};
		
	ParseName: PUBLIC PROC [fileName, server, rest: LONG STRING] = {
		state: NAT ← 0;
		server.length ← 0;
		rest.length ← 0;
		FOR i: NAT IN [0..fileName.length) DO
			c: CHAR ← fileName[i];
			charType: NAT ← SELECT c FROM
				'[ => 0,
				'/ => 1,
				'] => 2,
				'< => 3,
				'> => 4,
				'! => 5,
				IN ['0..'9], IN ['a..'z], IN ['A..'Z] => 6,
				'-, '+, '$, '., '' => 6
				ENDCASE => Error[expl: "Illegal character in filename", retryable: FALSE];
			SELECT state*7+charType FROM
				0*7+0 => state ← 1;
				0*7+1 => state ← 7;
				1*7+6 => {server[server.length] ← c; server.length ← server.length + 1};
				1*7+2 => state ← 2;
				2*7+3 => {state ← 3; rest[rest.length] ← '<; rest.length ← rest.length + 1};
				3*7+6,
				3*7+4 => {rest[rest.length] ← c; rest.length ← rest.length + 1};
				3*7+5 => {state ← 4; rest[rest.length] ← c; rest.length ← rest.length + 1};
				4*7+6 => {
					rest[rest.length] ← c; rest.length ← rest.length + 1;
					IF c IN ['0..'9] THEN state ← 5
					ELSE IF c = 'l OR c = 'L OR c = 'h OR c='H THEN state ← 6
					ELSE state ← 9
					};
				5*7+6 => {
					rest[rest.length] ← c; rest.length ← rest.length + 1;
					IF c NOT IN ['0..'9] THEN state ← 9
					};
				7*7+6 => {server[server.length] ← c; server.length ← server.length + 1};
				7*7+1 => {state ← 8; rest[rest.length] ← '<; rest.length ← rest.length + 1};
				7*8+6 => {rest[rest.length] ← c; rest.length ← rest.length + 1};
				7*8+1 => {rest[rest.length] ← '>; rest.length ← rest.length + 1};
				7*8+5 => {state ← 4; rest[rest.length] ← c; rest.length ← rest.length + 1};
				ENDCASE => Error[expl: "Illegal filename", retryable: FALSE];
			ENDLOOP;
		SELECT state FROM
			3, 8 => {rest[rest.length] ← '!; rest[rest.length+1] ← 'H; rest.length ← rest.length + 2};
			5, 6 => NULL;
			ENDCASE => Error[expl: "Illegal filename", retryable: FALSE];
		};
		
	MyLookup: PROC [fileName, createDate, name, password: LONG STRING] RETURNS [bytes: INT ← 0] = {
		server: LONG STRING ← [80];
		rest: LONG STRING ← [80];
		stp: STP.Handle ← STP.Create[];
		open: BOOL ← FALSE;
		BEGIN ENABLE UNWIND => {
			-- IF open THEN stp.Close[ ! STP.Error => TRUSTED {CONTINUE}];
			-- stp ← stp.Destroy
			};
			expandedName: LONG STRING ← [80];
			success: BOOLEAN ← FALSE;
			NoteFileProc: STP.NoteFileProcType = TRUSTED {
				fileInfo: STP.FileInfo ← stp.GetFileInfo[];
				continue ← no;
				success ← TRUE;
				AppendRope[expandedName, file];
				bytes ← fileInfo.size;
				createDate.length ← 0;
				AppendRope[createDate, fileInfo.create];
				};
			desiredProps: STP.DesiredProperties ← ALL[FALSE];
			desiredProps[directory] ← TRUE;
			desiredProps[nameBody] ← TRUE;
			desiredProps[version] ← TRUE;
			desiredProps[createDate] ← TRUE;
			desiredProps[size] ← TRUE;
			ParseName[fileName, server, rest];
			String.AppendChar[expandedName, '[ ];
			String.AppendString[expandedName, server];
			String.AppendChar[expandedName, '] ];
			[] ← stp.Open[Rs[server]];
			open ← TRUE;
			stp.Login[Rs[name], Rs[password]];
			stp.SetDesiredProperties[desiredProps];
			stp.Enumerate[Rs[rest], NoteFileProc];
			stp.Close[ ! STP.Error => TRUSTED {CONTINUE}];
			-- stp ← stp.Destroy;
			IF success THEN {String.Copy[to: fileName, from: expandedName]}
			ELSE Error[expl: "File not found", retryable: FALSE];
			END;
		};
		
	Read: PUBLIC PROC [fileName: LONG STRING, name, password: LONG STRING, action: PROC[Stream.Handle]] = {
		MyRead[fileName, name, password, action !
			STP.Error => TRUSTED {
				expl: LONG STRING ← [80];
				AppendRope[expl, error];
				ERROR Error[expl: expl, retryable: Retryable[code]]
				};
			];
		};
		
	MyRead: PROC [fileName: LONG STRING, name, password: LONG STRING, action: PROC[Stream.Handle]] = {
		server: LONG STRING ← [80];
		rest: LONG STRING ← [80];
		stp: STP.Handle ← STP.Create[];
		open: BOOL ← FALSE;
		stream: Stream.Handle;
		BEGIN ENABLE UNWIND => {
			-- IF open THEN stp.Close[ ! STP.Error => TRUSTED {CONTINUE}];
			-- stp ← stp.Destroy
			IF stream # NIL THEN {stream.Delete; stream ← NIL};
			};
			ParseName[fileName, server, rest];
			[] ← stp.Open[Rs[server]];
			open ← TRUE;
			stp.Login[Rs[name], Rs[password]];
			stream ← Stream.FromIOStreams[stp.CreateRemoteStream[Rs[rest], read], NIL];
			action[stream];
			stp.Close[ ! STP.Error => TRUSTED {CONTINUE}];
			-- stp ← stp.Destroy;
			stream.Delete;
			stream ← NIL
			END;
		};
		
	END.