-- Copyright (C) 1984  by Xerox Corporation. All rights reserved. 
-- NetDirFileNoDisk.mesa, HGM, 15-Jul-84  0:40:56

DIRECTORY
  Ascii USING [CR],
  CmFile USING [Handle, TableError],
  Heap USING [systemZone],
  Inline USING [LowHalf],
  Put USING [Text],
  String USING [AppendChar, AppendString, WordsForString],
  System USING [Pulses, GetClockPulses, PulsesToMicroseconds],
  StringLookUp USING [noMatch],
  Token USING [FreeTokenString, Item],

  Buffer USING [AccessHandle, DestroyPool, GetBuffer, MakePool, ReturnBuffer],
  Indirect USING [Close, NextValue, OpenSection],
  NameServerDefs USING [
    CacheEntry, nameToCacheRequest, addressToCacheRequest, hereIsCacheEntry],
  NetDirDefs,
  PupDefs USING [
    AppendPupAddress, GetPupAddress, PupNameTrouble, PupRouterBroadcastThis, PupRouterSendThis,
    PupBuffer, PupSocket, PupSocketDestroy, PupSocketMake,
    SecondsToTocks, SetPupContentsWords, MoveStringBodyToPupBuffer],
  PupTypes USING [fillInPupAddress, fillInSocketID, miscSrvSoc, PupAddress],
  ServerHeap USING [CopyString, Node],
  Stats USING [StatCounterIndex, StatBump];

NetDirFileNoDisk: PROGRAM
  IMPORTS
    CmFile, Heap, Inline, Put, String, System, Token,
    Buffer, Indirect, PupDefs, Stats, ServerHeap
  EXPORTS NameServerDefs =
  BEGIN OPEN NetDirDefs;

  PupAddress: TYPE = PupTypes.PupAddress;
  CacheEntry: TYPE = NameServerDefs.CacheEntry;

  verbose: BOOLEAN = TRUE;
  useCount: CARDINAL ← 0;

  statMsScanningFile: PUBLIC Stats.StatCounterIndex;

  z: UNCOUNTED ZONE = Heap.systemZone;

  Chain: TYPE = LONG POINTER TO ChainSlot;
  ChainSlot: TYPE = RECORD [
    next: Chain,
    dest: PupTypes.PupAddress,
    target: LONG STRING];
  helpers: Chain ← NIL;
  
  -- Don't try broadcasting if all helpers timeout or you might get an answer from a D0.
  -- NameLookup normally times out in 20 seconds. We should try the secondary server sooner.

  SearchNetDirForName: PUBLIC PROCEDURE [key: LONG STRING, ce: CacheEntry]
    RETURNS [hit: BOOLEAN ← FALSE] =
    BEGIN OPEN PupDefs;
    pulses: System.Pulses ← System.GetClockPulses[];
    pool: Buffer.AccessHandle ← Buffer.MakePool[send: 1, receive: 2];
    who: PupTypes.PupAddress ← PupTypes.fillInPupAddress;
    soc: PupSocket;
    b: PupBuffer;
    helper: Chain ← helpers;
    try: CARDINAL ← 6;
    IF helper # NIL THEN BEGIN who ← helper.dest; try ← 2; END;
    soc ← PupSocketMake[PupTypes.fillInSocketID, who, SecondsToTocks[5]];
    UNTIL hit DO
      IF try = 0 THEN
        BEGIN
        IF helper = NIL THEN EXIT;
        helper ← helper.next;
        IF helper = NIL THEN EXIT;
        who ← helper.dest;
        try ← 6;
        END;
      try ← try - 1;
      b ← Buffer.GetBuffer[pup, pool, send];
      b.pup.pupType ← NameServerDefs.nameToCacheRequest;
      b.pup.pupID ← [try, try];
      MoveStringBodyToPupBuffer[b, key];
      b.pup.dest ← who;
      b.pup.dest.socket ← PupTypes.miscSrvSoc;
      b.pup.source ← soc.getLocalAddress[];
      IF helper = NIL THEN PupRouterBroadcastThis[b] ELSE PupRouterSendThis[b];
      UNTIL hit DO
        b ← soc.get[];
        IF b = NIL THEN EXIT;
        SELECT b.pup.pupType FROM
          NameServerDefs.hereIsCacheEntry =>
            BEGIN
            hit ← TRUE;
	    CopyCacheEntryFromPacket[ce, b];
            END;
          nameError =>
	    BEGIN
	    hit ← TRUE;
	    CopyName[ce, key];
	    END;
          ENDCASE => NULL;
        Buffer.ReturnBuffer[b];
	ENDLOOP;
      ENDLOOP;
    PupSocketDestroy[soc];
    Buffer.DestroyPool[pool];
    pulses ← System.Pulses[System.GetClockPulses[] - pulses];
    Stats.StatBump[  -- This might overflow 60 serconds
      statMsScanningFile, Inline.LowHalf[
      System.PulsesToMicroseconds[pulses]/1000]];
    RETURN[LENGTH[ce.names] # 0];
    END;

  SearchNetDirForAddress: PUBLIC PROCEDURE [key: PupAddress, ce: CacheEntry]
    RETURNS [hit: BOOLEAN ← FALSE] =
    BEGIN OPEN PupDefs;
    pulses: System.Pulses ← System.GetClockPulses[];
    pool: Buffer.AccessHandle ← Buffer.MakePool[send: 1, receive: 10];
    who: PupTypes.PupAddress ← PupTypes.fillInPupAddress;
    soc: PupSocket;
    b: PupBuffer;
    helper: Chain ← helpers;
    try: CARDINAL ← 6;
    temp: STRING = [100];
    IF helper # NIL THEN BEGIN who ← helper.dest; try ← 2; END;
    soc ← PupSocketMake[PupTypes.fillInSocketID, who, SecondsToTocks[5]];
    UNTIL hit DO
      IF try = 0 THEN
        BEGIN
        IF helper = NIL THEN EXIT;
        helper ← helper.next;
        IF helper = NIL THEN EXIT;
        who ← helper.dest;
        try ← 6;
        END;
      try ← try - 1;
      b ← Buffer.GetBuffer[pup, pool, send];
      b.pup.pupType ← NameServerDefs.addressToCacheRequest;
      b.pup.pupID ← [try, try];
      b.pup.address ← key;
      SetPupContentsWords[b, SIZE[PupAddress]];
      b.pup.dest ← who;
      b.pup.dest.socket ← PupTypes.miscSrvSoc;
      b.pup.source ← soc.getLocalAddress[];
      IF helper = NIL THEN PupRouterBroadcastThis[b] ELSE PupRouterSendThis[b];
      UNTIL hit DO
        b ← soc.get[];
        IF b = NIL THEN EXIT;
        SELECT b.pup.pupType FROM
          NameServerDefs.hereIsCacheEntry =>
            BEGIN
            hit ← TRUE;
	    CopyCacheEntryFromPacket[ce, b];
            END;
          nameError =>
	    BEGIN
	    hit ← TRUE;
	    CopyAddress[ce, key];
	    END;
          ENDCASE => NULL;
        Buffer.ReturnBuffer[b];
	ENDLOOP;
      ENDLOOP;
    PupSocketDestroy[soc];
    Buffer.DestroyPool[pool];
    pulses ← System.Pulses[System.GetClockPulses[] - pulses];
    Stats.StatBump[  -- This might overflow 60 serconds
      statMsScanningFile, Inline.LowHalf[
      System.PulsesToMicroseconds[pulses]/1000]];
    IF LENGTH[ce.addrs] # 0 THEN RETURN[TRUE];
    [] ← SearchNetDirForName[temp, ce];
    RETURN[LENGTH[ce.names] # 0];
    END;

  CopyCacheEntryFromPacket: PROCEDURE [ce: CacheEntry, b: PupDefs.PupBuffer] =
    BEGIN
    p: LONG POINTER ← @b.pup.pupWords;
    n: CARDINAL ← 0;
    version: CARDINAL;
    names: CARDINAL;
    addrs: CARDINAL;
    words: CARDINAL;
    version ← (p+n)↑;  -- File version number
    n ← n + SIZE[CARDINAL];
    names ← (p+n)↑;
    n ← n + SIZE[CARDINAL];
    IF names # 0 THEN 
      BEGIN
      words ← SIZE[LONG STRING, names];
      ce.size ← ce.size + words;
      ce.names ← DESCRIPTOR[ServerHeap.Node[words], names];
      END;
    FOR i: CARDINAL IN [0..names) DO
      s: LONG STRING = LOOPHOLE[p+n];
      words: CARDINAL ← String.WordsForString[s.length];
      ce.names[i] ← ServerHeap.CopyString[s];
      ce.size ← ce.size + words;
      n ← n + words;
      ENDLOOP;
    addrs ← (p+n)↑;
    n ← n + SIZE[CARDINAL];
    IF addrs # 0 THEN 
      BEGIN
      words ← SIZE[PupAddress, addrs];
      ce.size ← ce.size + words;
      ce.addrs ← DESCRIPTOR[ServerHeap.Node[words], addrs];
      END;
    FOR i: CARDINAL IN [0..addrs) DO
      a: LONG POINTER TO PupAddress ← LOOPHOLE[(p+n)];
      ce.addrs[i] ← a↑;
      n ← n + SIZE[PupAddress];
      ENDLOOP;
    END;

  CopyName: PROCEDURE [ce: CacheEntry, key: LONG STRING] =
    BEGIN
    words: CARDINAL = SIZE[LONG STRING];
    ce.names ← DESCRIPTOR[ServerHeap.Node[words], 1];
    ce.names[0] ← ServerHeap.CopyString[key];
    ce.size ← ce.size + words + String.WordsForString[key.length];
    END;

  CopyAddress: PROCEDURE [ce: CacheEntry, key: PupAddress] =
    BEGIN
    words: CARDINAL = SIZE[PupAddress];
    ce.addrs ← DESCRIPTOR[ServerHeap.Node[words], 1];
    ce.addrs[0] ← key;
    ce.size ← ce.size + words;
    END;

  AppendPseudoString: PROCEDURE [
    e: LONG STRING, p: LONG DESCRIPTOR FOR PACKED ARRAY OF CHARACTER] =
    BEGIN
    FOR i: CARDINAL IN [0..MIN[e.maxlength - e.length, LENGTH[p]]) DO
      String.AppendChar[e, p[i]]; ENDLOOP;
    END;
  
  OpenDirectoryFiles, CloseDirectoryFiles: PUBLIC PROCEDURE = BEGIN END;

  UpdatePicture: PUBLIC PROCEDURE = BEGIN END;

  ScanParameterFile: PROCEDURE =
    BEGIN
    cmFile: CmFile.Handle;
    Option: TYPE = {helper};
    NextValue: PROCEDURE [
      h: CmFile.Handle, table: LONG DESCRIPTOR FOR ARRAY Option OF LONG STRING]
      RETURNS [Option] = LOOPHOLE[Indirect.NextValue];
    optionTable: ARRAY Option OF LONG STRING ← [helper: "Helper"L];
    cmFile ← Indirect.OpenSection["NameServer"L];
    IF cmFile = NIL THEN RETURN;
    DO
      option: Option;
      option ← NextValue[
        cmFile, DESCRIPTOR[optionTable] !
        CmFile.TableError =>
          BEGIN
	  IF name[0] # '; THEN Message["Unrecognized parameter: ", name];
	  RETRY;
	  END];
      SELECT option FROM
        LOOPHOLE[StringLookUp.noMatch] => EXIT;
        helper =>
	  BEGIN
	  text: STRING = [20];
	  temp: LONG STRING ← Token.Item[cmFile, FALSE];
	  new: Chain ← z.NEW[ChainSlot];
	  new↑ ← [NIL, PupTypes.fillInPupAddress, z.NEW[StringBody[temp.length]]];
	  String.AppendString[new.target, temp];
	  [] ← Token.FreeTokenString[temp];
	  PupDefs.GetPupAddress[@new.dest, new.target ! PupDefs.PupNameTrouble => CONTINUE];
	  IF helpers = NIL THEN helpers ← new
	  ELSE
	    BEGIN
	    FOR finger: Chain ← helpers, finger.next DO
	      IF finger.next = NIL THEN
	        BEGIN
		finger.next ← new;
		EXIT;
		END;
	      ENDLOOP;
	    END;
	  String.AppendString[text, " ("L];
	  PupDefs.AppendPupAddress[text, new.dest];
	  String.AppendString[text, ")"L];
          Message["Helper for NetDir requests is "L, new.target, text];
	  END;
        ENDCASE => ERROR;
      ENDLOOP;
    Indirect.Close[cmFile];
    END;

  Message: PROCEDURE [one, two, three, four: LONG STRING ← NIL] =
    BEGIN
    text: STRING = [200];
    String.AppendString[text, one];
    IF two # NIL THEN String.AppendString[text, two];
    IF three # NIL THEN String.AppendString[text, three];
    IF four # NIL THEN String.AppendString[text, four];
    LogString[text];
    END;

  LogString: PROCEDURE [text: LONG STRING] =
    BEGIN
    String.AppendChar[text, '.];
    String.AppendChar[text, Ascii.CR];
    Put.Text[NIL, text];
    END;

  ScanParameterFile[];
  END.