-- Copyright (C) 1982, 1984, 1985  by Xerox Corporation. All rights reserved. 
-- InitParms.mesa, HGM, 10-Mar-85  5:54:04
-- From OthelloFTP.mesa of 11-Dec-82 16:01:37

DIRECTORY
  Boot USING [EthernetBootFileNumber, Location],
  Device USING [Type],
  DeviceTypes USING [ethernet, ethernetOne],
  Environment USING [bytesPerPage, bytesPerWord],
  Format USING [HostNumber, StringProc],
  Heap USING [systemZone],
  HostNumbers USING [HostNumber],
  NSConstants USING [bootServerSocket],
  OthelloDefs USING [
    AbortingCommand, CheckUserAbort, CommandProcessor, GetName,
    IndexTooLarge, MyNameIs, Question, ReadShortNumber, RegisterCommandProc,
    WriteChar, WriteLine, WriteLongNumber, WriteOctal, WriteString, Yes],
  Process USING [Pause, SecondsToTicks],
  Space USING [ScratchMap, Unmap],
  STP USING [
    Close, Create, CreateRemoteStream, Enumerate, Error,
    ErrorCode, FileInfo, GetFileInfo, Handle, IsOpen, Login, NextFileName, 
    NoteFileProcType, Open, SetDirectory, SetHost],
  Stream USING [EndOfStream, Handle],
  String USING [AppendCharAndGrow, AppendString, CopyToNewString, Length],
  System USING [
    broadcastHostNumber, GreenwichMeanTime,
    NetworkAddress, nullNetworkNumber],
  Time USING [Append, Unpack],
  Unformat USING [Error, HostNumber],
  
  Indirect USING [GetParmFileName],
  OthelloForgot USING [],
  Parms USING [Error, GetBootLocation, GetMaxChars, GetParms, WriteBootLocation, WriteParms],
  PhoneFace USING [phoneLine];

InitParms: PROGRAM
  IMPORTS
    Format, Heap, OthelloDefs, Process, Space, STP, Stream, String, Time, Unformat,
    Indirect, Parms
  EXPORTS System, OthelloForgot =
  BEGIN
  
  HostNumber: PUBLIC TYPE = HostNumbers.HostNumber;

  host, userName, userPassword: LONG STRING ← NIL;
  directory, file: LONG STRING ← NIL;
  bootFileNumber: LONG STRING;
  
  --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  -- String/Credentials Commands
  --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  Directory: PROC = {
    OthelloDefs.MyNameIs[
      myNameIs: "Directory"L,
      myHelpIs: "Set Default FTP directory"L];
    OthelloDefs.GetName["Directory: "L, @directory]};

  GetUserNamePassword: PROC = {
    OthelloDefs.MyNameIs[
      myNameIs: "Login"L, myHelpIs: "Set user name-password"L];
    OthelloDefs.GetName["User: "L,     @userName];
    OthelloDefs.GetName["Password: "L, @userPassword, stars]};

  SmashPassword: PUBLIC PROCEDURE =
    BEGIN
    Heap.systemZone.FREE[@userName];
    Heap.systemZone.FREE[@userPassword]
    END;

  --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  -- Set BootLocation Command
  --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  SetBootLocation: PROC =
    BEGIN
    where: Boot.Location;
    OthelloDefs.MyNameIs[
      myNameIs: "Set Boot Location", myHelpIs: "Set Boot Location in EERom"L];
    SELECT TRUE FROM
      OthelloDefs.Yes["NS/10MB? "L] =>
        BEGIN
        bfn: HostNumber;
	board: CARDINAL ← 0;
        OthelloDefs.GetName["NS Boot File Number: "L, @bootFileNumber];
        GetAddress[@bfn, bootFileNumber !
          Unformat.Error => OthelloDefs.AbortingCommand["Can't parse that one (No CH)"L]];
        board ← OthelloDefs.ReadShortNumber[
          "Board: "L, 0, 3, board];
        where ← [
          deviceType: DeviceTypes.ethernet,
          deviceOrdinal: board,
	  vp: ethernet [ [
            bfn: [bfn],
            address: [
              net: System.nullNetworkNumber,
              host: System.broadcastHostNumber,
              socket: NSConstants.bootServerSocket] ]]];
        END;
      OthelloDefs.Yes["NS/Phone? "L] =>
        BEGIN
        bfn: HostNumber;
	line: CARDINAL ← 0;
        OthelloDefs.GetName["NS Boot File Number: "L, @bootFileNumber];
        GetAddress[@bfn, bootFileNumber !
          Unformat.Error => OthelloDefs.AbortingCommand["Can't parse that one (No CH)"L]];
        line ← OthelloDefs.ReadShortNumber[
          "Line: "L, 0, 3, line];
        where ← [
          deviceType: PhoneFace.phoneLine,
          deviceOrdinal: line,
	  vp: ethernet [ [
            bfn: [bfn],
            address: [
              net: System.nullNetworkNumber,
              host: System.broadcastHostNumber,
              socket: NSConstants.bootServerSocket] ]]];
        END;
      OthelloDefs.Yes["Pup/3MB? "L] =>
        BEGIN
        bfn: CARDINAL ← OthelloDefs.ReadShortNumber[
          "Pup Boot File Number: "L, 0, 177777B, 140000B];
	board: CARDINAL ← 0;
        board ← OthelloDefs.ReadShortNumber[
          "Board: "L, 0, 3, board];
        where ← [
          deviceType: DeviceTypes.ethernetOne,
          deviceOrdinal: board,
          vp: ethernetOne [
            bootFileNumber: bfn, net: 0, host: 0 ] ];
        END;
      ENDCASE => OthelloDefs.AbortingCommand["No other options implemented."L];
    Parms.WriteBootLocation[where ! Parms.Error =>
      BEGIN
      OthelloDefs.WriteString["Trouble Writing BootLocation: "L];
      OthelloDefs.WriteLine[reason];
      CONTINUE;
      END;];
    OthelloDefs.WriteLine["done"L];
    END;
    
  --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  -- Fetch Parm File Command
  --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  FetchParmFile: PROC =
    BEGIN
    GetString: PROC [s: LONG STRING] =
      BEGIN
      OthelloDefs.WriteString["storing..."L];
      Parms.WriteParms[s ! Parms.Error =>
        BEGIN
        OthelloDefs.WriteString["Trouble Writing Parms: "L];
        OthelloDefs.WriteLine[reason];
        CONTINUE;
        END;];
      END;
    OthelloDefs.MyNameIs[
      myNameIs: "Fetch Parm File", myHelpIs: "Fetch Parameter File from FileServer and store it in EERom"L];
    OthelloDefs.GetName["Parameter file: "L, @file
    ! OthelloDefs.Question => {
      OthelloDefs.WriteLine["Filename"L]; RESUME}];
    IF ~ConnectionOpen[] AND ~ReOpen[] THEN
      BEGIN
      IF host # NIL THEN Open[];
      IF ~ConnectionOpen[] THEN OthelloDefs.AbortingCommand["Please open a connection"L];
      END;
    Retrieve[destination: [string[GetString]]];
    OthelloDefs.WriteLine["done"L];
    END;
    
  --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  -- Type Parm File Command
  --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  TypeParmFile: PROC =
    BEGIN
    s: LONG STRING ← NIL;
    BEGIN ENABLE UNWIND => Heap.systemZone.FREE[@s];
    boot: Boot.Location;
    write: System.GreenwichMeanTime;
    goodBoot, goodParms: BOOLEAN ← TRUE;
    OthelloDefs.MyNameIs[
      myNameIs: "Type Parm File", myHelpIs: "Type Parameter File from EERom"L];
    s ← Heap.systemZone.NEW[StringBody[Parms.GetMaxChars[]]];
    [boot, write] ← Parms.GetBootLocation[ ! Parms.Error =>
      BEGIN
      OthelloDefs.WriteString["Can't read BootLocation: "L];
      OthelloDefs.WriteString[reason];
      OthelloDefs.WriteLine["."L];
      goodBoot ← FALSE;
      CONTINUE;
      END];
    IF goodBoot THEN
      BEGIN
      OthelloDefs.WriteString["Boot Location Written: "L];
      WriteTime[write];
      OthelloDefs.WriteLine["."L];
      OthelloDefs.WriteString["Boot Info: "L];
      SELECT boot.deviceType FROM
        DeviceTypes.ethernet =>
	  BEGIN
          OthelloDefs.WriteString["NS/10MB Boot File #"L];
	  WriteBFN[boot.ethernetRequest.bfn];
          OthelloDefs.WriteString[", Board: "L];
          OthelloDefs.WriteLongNumber[boot.deviceOrdinal];
	  END;
        PhoneFace.phoneLine =>
	  BEGIN
          OthelloDefs.WriteString["NS/Phone Boot File #"L];
	  WriteBFN[boot.ethernetRequest.bfn];
          OthelloDefs.WriteString[", Line: "L];
          OthelloDefs.WriteLongNumber[boot.deviceOrdinal];
	  END;
        DeviceTypes.ethernetOne =>
	  BEGIN
          OthelloDefs.WriteString["Pup/3MB Boot File #"L];
	  OthelloDefs.WriteOctal[boot.bootFileNumber];
          OthelloDefs.WriteString[", Board: "L];
          OthelloDefs.WriteLongNumber[boot.deviceOrdinal];
	  END;
	ENDCASE =>
	  BEGIN
          p: POINTER TO ARRAY [0..0) OF WORD = LOOPHOLE[@boot];
          FOR i: CARDINAL IN [0..SIZE[Boot.Location]) DO
            IF i # 0 THEN OthelloDefs.WriteString[", "L];
	    OthelloDefs.WriteOctal[p[i]];
            ENDLOOP;
	  END;
      OthelloDefs.WriteLine["."L];
      END;
    OthelloDefs.WriteLine[""L];
    [write] ← Parms.GetParms[s ! Parms.Error =>
      BEGIN
      OthelloDefs.WriteString["Can't read Parms: "L];
      OthelloDefs.WriteString[reason];
      OthelloDefs.WriteLine["."L];
      goodParms ← FALSE;
      CONTINUE;
      END];
    IF goodParms THEN
      BEGIN
      OthelloDefs.WriteString["Parms Written: "L];
      WriteTime[write];
      OthelloDefs.WriteLine["."L];
      OthelloDefs.WriteString["Text length: "L];
      OthelloDefs.WriteLongNumber[s.length];
      OthelloDefs.WriteString[".  (Max is "L];
      OthelloDefs.WriteLongNumber[Parms.GetMaxChars[]];
      OthelloDefs.WriteLine[".)"L];
      OthelloDefs.WriteLine[s];
      END;
    OthelloDefs.WriteLine[""L];
    Heap.systemZone.FREE[@s];
    END;
    END;
    
  --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  -- IO Routines
  --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  WriteTime: PROCEDURE [t: System.GreenwichMeanTime] =
    BEGIN
    temp: STRING = [40];
    Time.Append[temp, Time.Unpack[t], TRUE];
    OthelloDefs.WriteString[temp];
    END;

  WriteBFN: PROCEDURE [bfn: Boot.EthernetBootFileNumber] =
    BEGIN
    Append: Format.StringProc = BEGIN String.AppendString[temp, s]; END;
    temp: STRING = [40];
    Format.HostNumber[Append, bfn, octal];
    OthelloDefs.WriteString[temp];
    END;

  GetAddress: PROCEDURE [host: POINTER TO HostNumber, s: LONG STRING] =
    BEGIN
    host↑ ← Unformat.HostNumber[s, octal];
    END;

  --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  -- MISC Stuff/Commands
  --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  userOpened: BOOLEAN ← FALSE;
  OpenCmd: PROC = {
    OthelloDefs.MyNameIs[myNameIs: "Open"L, myHelpIs: "STP Open"L];
    CloseCmd[];
    OthelloDefs.GetName["Open connection to "L, @host];
    Open[]; userOpened ← TRUE};

  ReOpen: PROC RETURNS [BOOLEAN] = {
    IF userOpened=FALSE THEN RETURN[FALSE];
    Open[]; RETURN[TRUE]};
    
  CloseCmd: PROC = {
    OthelloDefs.MyNameIs[myNameIs: "Close"L, myHelpIs: "STP Close"L];
    userOpened ← FALSE; Close[]};
    
  RemoteList: PROC = {
    OthelloDefs.MyNameIs[
      myNameIs: "List Remote Files"L, myHelpIs: "List Remote Files"L];
    IF ~ConnectionOpen[] AND ~ReOpen[] THEN
      OthelloDefs.AbortingCommand["Please open a connection"L];
    OthelloDefs.GetName["Pattern: "L, @file];
    IF String.Length[file] = 0 THEN
      String.AppendCharAndGrow[@file, '*, Heap.systemZone];
    ListFiles[]};

  --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  -- Central commands
  --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  commandProcessor: OthelloDefs.CommandProcessor ← [FtpCommands];

  FtpCommands: PROC [index: CARDINAL] = {
    SELECT index FROM
      0 => TypeParmFile[];
      1 => SetBootLocation[];
      2 => FetchParmFile[];
      3 => Directory[];
      4 => GetUserNamePassword[];
      5 => OpenCmd[];
      6 => CloseCmd[];
      7 => RemoteList[];
      ENDCASE => OthelloDefs.IndexTooLarge};


  --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  -- STP Stuff/Commands
  --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  stp: STP.Handle ← NIL;
  
  ConnectionOpen: PROC RETURNS [BOOLEAN] = {
    RETURN[stp # NIL AND STP.IsOpen[stp]]};
    
  -- all callers close the connection first
  Open: PROC = {
    herald: LONG STRING ← NIL;
    IF stp = NIL THEN stp ← STP.Create[];
    DO
      herald ← STP.Open[stp, host
      ! STP.Error => SELECT code FROM
          connectionTimedOut, connectionClosed, noRouteToNetwork,
          noNameLookupResponse, connectionRejected => {
	    OthelloDefs.WriteLine[error]; CONTINUE};
          ENDCASE => OthelloDefs.AbortingCommand[error]];
      IF herald # NIL THEN EXIT;
      Process.Pause[Process.SecondsToTicks[10]];
      OthelloDefs.CheckUserAbort[];
      ENDLOOP;
    OthelloDefs.WriteLine[herald];
    Heap.systemZone.FREE[@herald];
    STP.SetHost[stp, host]};
  
  Close: PROC = {
    IF ~ConnectionOpen[] THEN RETURN;
    STP.Close[stp ! STP.Error => OthelloDefs.AbortingCommand[error]];
    OthelloDefs.WriteLine["Connection closed"L]};
  
  -- could mess with directories.
  -- who cares
  ListFiles: PROC = {
    ListOne: STP.NoteFileProcType = {
      info: STP.FileInfo = STP.GetFileInfo[stp];
      OthelloDefs.WriteString[file];
      THROUGH [file.length..80-info.create.length) DO
        OthelloDefs.WriteChar[' ] ENDLOOP;
      OthelloDefs.WriteLine[info.create];
      --OthelloDefs.WriteChar[' ];
      --OthelloDefs.WriteLine[info.author];
      --OthelloDefs.WriteChar[' ];
      --OthelloDefs.WriteLongNumber[info.size];
      --OthelloDefs.WriteLine[" bytes]"L];
      OthelloDefs.CheckUserAbort[];
      RETURN[yes]};
    STP.Login[stp, userName, userPassword];
    STP.SetDirectory[stp, directory];
    STP.Enumerate[stp, file, ListOne
    ! STP.Error => OthelloDefs.AbortingCommand[error]]};

  Destination: TYPE = RECORD [
    SELECT type: * FROM
      string               => [stringProc: PROC [LONG STRING]],
      ENDCASE];
      
  StartFeedback: SIGNAL = CODE;
  Retrieve: PROC [destination: Destination] =
    BEGIN
    rs: Stream.Handle;
    Cleanup: PROC = BEGIN rs.delete[rs]; END;
    rs ← GetReadStream[! STP.Error => OthelloDefs.AbortingCommand[error]];
    IF rs=NIL THEN RETURN;
    GrabBits[rs, destination
    ! STP.Error => OthelloDefs.AbortingCommand[error];
      StartFeedback => {
        OthelloDefs.WriteString["Fetching..."L];
        RESUME};
      UNWIND => Cleanup[]; ];
    Cleanup[];
    END;
    
  GetReadStream: PROC RETURNS [rs: Stream.Handle] = {
    STP.Login[stp, userName, userPassword];
    STP.SetDirectory[stp, directory];
    rs ← STP.CreateRemoteStream[stp, file, read];
    rs.options.signalEndOfStream ← TRUE;
    DO 
      info: STP.FileInfo; s: LONG STRING;
      s ← STP.NextFileName[rs ! UNWIND => rs.delete[rs]];
      IF s = NIL THEN {rs.delete[rs]; RETURN[NIL]};
      info ← STP.GetFileInfo[stp ! UNWIND => rs.delete[rs]];
      OthelloDefs.WriteString[s]; Heap.systemZone.FREE[@s];
      OthelloDefs.WriteChar['[]; OthelloDefs.WriteString[info.create];
      OthelloDefs.WriteString[", bytes = "]; OthelloDefs.WriteLongNumber[info.size];
      OthelloDefs.WriteChar[']];
      IF OthelloDefs.Yes[" [Confirm]: "L ! UNWIND => rs.delete[rs]] THEN EXIT;
      ENDLOOP;
    RETURN};
    

  bufPages: CARDINAL     = 8;
  
  GrabBits: PROC [
    rs: Stream.Handle, destination: Destination] = {
    WITH destination SELECT FROM
      string => {
	SIGNAL StartFeedback;
	DO
          stringOverhead: CARDINAL = SIZE[StringBody]*Environment.bytesPerWord;
          string: LONG STRING = Space.ScratchMap[bufPages];
          string↑ ← [
	    length: 0,
	    maxlength: bufPages*Environment.bytesPerPage - stringOverhead,
	    text: ];
	  WHILE string.length < string.maxlength DO
            got: CARDINAL;
	    [bytesTransferred: got] ← rs.get[
	      rs,
	      [blockPointer: LOOPHOLE[@string.text],
	       startIndex: string.length,
	       stopIndexPlusOne: string.maxlength],
	      rs.options
	      ! Stream.EndOfStream => {
	          got ← 0; string.length ← string.length + nextIndex; CONTINUE};
                UNWIND => [] ← Space.Unmap[string]];
	    IF got = 0 THEN {
	      stringProc[string! UNWIND => [] ← Space.Unmap[string]];
	      [] ← Space.Unmap[string]; RETURN};
            string.length ← string.length + got;
            ENDLOOP;
          [] ← Space.Unmap[string];
          OthelloDefs.AbortingCommand["Command file too long!"L];
          ENDLOOP};
      ENDCASE => ERROR};
      
  --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  -- initialization
  --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  bootFileNumber ← String.CopyToNewString["25200002000", Heap.systemZone];
  host ← String.CopyToNewString["Indigo", Heap.systemZone];
  directory ← String.CopyToNewString["Portola>Parameters", Heap.systemZone];
  file ← String.CopyToNewString[Indirect.GetParmFileName[], Heap.systemZone];
  OthelloDefs.RegisterCommandProc[@commandProcessor];
  END.....