-- PDRemoteStreamImpl.mesa
-- Copyright (C) 1984, Xerox Corporation.  All rights reserved.
-- Michael Plass, 14-Sep-84 14:48:44
-- Tim Diebert,  5-Sep-86 14:05:45
-- 
DIRECTORY Stream, STP, PDRemoteStream, String;
   
PDRemoteStreamImpl: PROGRAM
   IMPORTS STP, String, Stream
   EXPORTS PDRemoteStream
   = BEGIN
      
   Error: PUBLIC ERROR [expl: LONG STRING, retryable: BOOL] = CODE;
      
   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 => {
            ERROR Error[expl: error, retryable: Retryable[code]]
            };
         ];
      };
      
   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 ← NIL;
      open: BOOL ← FALSE;
      expandedName: LONG STRING ← [80];
      success: BOOLEAN ← FALSE;
      NoteFileProc: STP.NoteFileProcType = {
         fileInfo: STP.FileInfo ← stp.GetFileInfo[];
         continue ← no;
         success ← TRUE;
         String.AppendString[expandedName, file];
         bytes ← fileInfo.size;
         String.Copy[to: createDate, from: fileInfo.create];
         };
      ParseName[fileName, server, rest];
      stp ← STP.Create[];
      BEGIN ENABLE UNWIND => {
         -- IF open THEN stp.Close[ ! STP.Error => {CONTINUE}];
         stp ← stp.Destroy
         };
         desiredProps: STP.DesiredProperties ← ALL[FALSE];
         desiredProps[directory] ← TRUE;
         desiredProps[nameBody] ← TRUE;
         desiredProps[version] ← TRUE;
         desiredProps[createDate] ← TRUE;
         desiredProps[size] ← TRUE;
         String.AppendChar[expandedName, '[ ];
         String.AppendString[expandedName, server];
         String.AppendChar[expandedName, '] ];
         [] ← stp.Open[server];
         open ← TRUE;
         stp.Login[name, password];
         stp.SetDesiredProperties[desiredProps];
         stp.Enumerate[rest, NoteFileProc];
         -- stp.Close[ ! STP.Error => {CONTINUE}];
         END;
      stp ← stp.Destroy;
      IF success THEN {String.Copy[to: fileName, from: expandedName]}
      ELSE Error[expl: "File not found", retryable: FALSE];
      };
      
   Read: PUBLIC PROC [fileName: LONG STRING, name, password: LONG STRING, action: PROC[Stream.Handle]] = {
      MyRead[fileName, name, password, action !
         STP.Error => {
            ERROR Error[expl: error, 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 ← NIL;
      open: BOOL ← FALSE;
      stream: Stream.Handle ← NIL;
      ParseName[fileName, server, rest];
      stp ← STP.Create[];
      BEGIN ENABLE UNWIND => {
         -- IF open THEN stp.Close[ ! STP.Error => {CONTINUE}];
         IF stream # NIL THEN {stream.Delete; stream ← NIL};
         stp ← stp.Destroy;
         };
         [] ← stp.Open[server];
         open ← TRUE;
         stp.Login[name, password];
         stream ← stp.CreateRemoteStream[rest, read];
         action[stream];
         -- stp.Close[ ! STP.Error => {CONTINUE}];
         END;
      stream.Delete;
      stream ← NIL;
      stp ← stp.Destroy;
      };
      
   END.