
/*
 *  MBNODES.C - 10/22/87 - Grab routes from NET/ROM
 *  NN port call time file
 */

#include "mb.h"

#if nncmd

#define ntout 3    /* "got all data" timeout */

short nodetime;
PORTS *was;
char  *mp;
short nodedone;

/*
 *  Node definition record.
 *  One of these for each node found.
 */

#define no_visit  0x01  /* Node has been visited */
#define no_reach  0x02  /* Node can be reached   */
#define no_busy   0x08  /* Node was busy         */

typedef struct NODE_S
{
  char *call;           /* Call of this node          */
  char *name;
  byte state;           /* "visited" "can be reached" */
  byte hops;            /* Distance to this node      */
  struct NODE_S *found; /* Where we were when we found a path to this one */
  struct NODE_S *seen;  /* Where we were when we found this one */
  struct NODE_S *next;  /* Next node                  */
} NODE;

NODE *nodehd;
NODE *nodetl;
int  nodecnt;
NODE *path[8];
int  np;

/*
 *  Find a node.
 */

NODE *findnode(cp)
char *cp;
{
  register NODE *n;

  for (n = nodehd; n isnt NULL; n = n->next) if (match(cp, n->call)) return n;
  return NULL;
}

/*
 *  Find an unvisited node.
 *  The one with the lowest cost path from the node we are at.
 */

findunode()
{

  register int  h;
  register NODE *n;

/*
 *  Find node at min hops not yet visited.
 */

  if (nodedone) return false;

  h = 256;
  path[0] = NULL;
  for (n = nodehd; n isnt NULL; n = n->next)
  {
    if (n->state & no_reach) if (!(n->state & no_visit))
    if (n->hops < h)
    {
      h = n->hops;
      path[0] = n;
    }
  }

  if (path[0] is NULL) return false;

  for (np = 0; np < 7; np++)
  {
    if (path[np]->found is NULL) return false;
    if (path[np]->found is nodehd) return true;
    path[np + 1] = path[np]->found;
  }
  return false;
}

/*
 *  Add a node.
 */

NODE *addnode(tp)
char *tp;
{
  register NODE *n;
  register char *cp;
  register char *np;

  cp = tp;
  np = tp;
  for (; *tp; tp++) if (*tp is ':')
  {
    *tp = '\0';
    cp = tp + 1;
  }

  if ((n = findnode(cp)) isnt NULL)
  {
    if (n->name is NULL) if (np isnt cp)
    {
      n->name = (char *)mp; mp += (strlen(np) + 1);
      strcpy(n->name, np);
    }
    return n;
  }

  nodecnt++;

  n = (NODE *)mp; mp += sizeof(NODE);
  n->call = (char *)mp; mp += (strlen(cp) + 1);
  strcpy(n->call, cp);

  if (np is cp) n->name = NULL; else
  {
    n->name = (char *)mp; mp += (strlen(np) + 1);
    strcpy(n->name, np);
  }

  n->state = 0;
  n->hops  = 255;
  n->found = NULL;
  n->seen  = NULL;
  n->next  = NULL;
  if (nodehd is NULL) nodehd = n; else nodetl->next = n;
  nodetl   = n;
  return n;
}

/*
 *  Connect to the local NET/ROM node.
 */

clnode()
{
  port->mode = remote;
  outstr("C "); outstr(nodehd->call); outchar('\n');
  waitcmd();
  do
  {
    if (!getit(nodetime))
    {
     if (!(port->mode & idle)) distnc();
     port->mode = idle;
     return;
    }
  }
  while (!iscon(port->line));
}

/*
 *  Connect to a distant node, using NET/ROM.
 */

cnode()
{
  register int i;

  path[0]->state setbit no_visit;
  for (i = np; i >= 0; i--)
  {
    outstr("C "); outstr(path[i]->call); outchar('\n');
    if (getit(path[i]->hops * nodetime))
    {
      if (!match(port->fld[1], "CONNECTED"))
      {
        if (!(port->mode & idle)) distnc();
        port->mode = idle;
        return false;
      }
    }
    else
    {
      if (!(port->mode & idle)) distnc();
      port->mode = idle;
      return false;
    }
  }
  return true;
}

/*
 *  Get the known nodes, links, and paths to them from THIS node.
 */

getnode()
{
  register int i;
  register int ti;
  short base;
  short tcnt;
  NODE *temp[64];
  NODE *via;

  fprintf(was->fl, "\nAt Node: %s\n", path[0]->call);

  outstr("p\n");
  if (!getit(path[0]->hops * nodetime))
  {
    if (port->mode & timeout) fprintf(was->fl, "*** Timeout\n");
    if (!(port->mode & idle)) distnc();
    port->mode = idle;
    return false;
  }
  fprintf(was->fl, "\nParams: %s", port->line);
  while (getit(ntout)) fprintf(was->fl, "Params: %s", port->line);

/*
 *  Get the nodes known to this node.
 */

  outstr("n *\n");

/*
 *  Get line "Nodes ..."
 */

  if (!getit(path[0]->hops * nodetime))
  {
    if (port->mode & timeout) fprintf(was->fl, "*** Timeout\n");
    if (!(port->mode & idle)) distnc();
    port->mode = idle;
    return false;
  }

  if (match(port->fld[1], "NODE"))
  {
    path[0]->state setbit no_busy;
    distnc();
    port->mode = idle;
    return false;
  }

/*
 *  Add known nodes to link table for this node.
 *  Unknown number. Timeout to decide got 'em all.
 */

  tcnt = 0;
  if (getit(ntout)) do
  {
    for (i = 0; i < port->flds; i++)
    {
      temp[tcnt] = addnode(port->fld[i]);
      if (temp[tcnt]->seen is NULL) temp[tcnt]->seen = path[0];
      tcnt++;
    }
  }
  while(getit(ntout));

  if (port->mode & idle) return false;

/*
 *  For each target, get the adjacent nodes toward that target.
 */

  for (ti = 0; ti < tcnt; ti++)
  {
    outstr("n "); outstr(temp[ti]->call); outchar('\n');

/*
 *  Get line "Routes to ..."
 */

    if (!getit(path[0]->hops * nodetime))
    {
      if (port->mode & timeout) fprintf(was->fl, "*** Timeout\n");
      if (!(port->mode & idle)) distnc();
      port->mode = idle;
      return false;
    }

    if (!match(port->fld[1], "ROUTES"))
    {
      while(getit(ntout));
      fprintf(was->fl, "*** Node %s missing routes\n", temp[ti]->call);
    }
    else
    {
      addnode(port->fld[3]); /* Pick up node name ... */

/*
 *  Collect all the adjacent node lines.
 *  Unknown number. Timeout to decide got 'em all.
 */

      if (getit(ntout)) do
      {
        if (*port->fld[0] is '>') base = 1; else base = 0;
        if (i = atoi(port->fld[base]))
        {
          via = addnode(port->fld[base + 3]);
          if (via->found is NULL) via->found = path[0];
          if (via->hops > path[0]->hops + 1) via->hops = path[0]->hops + 1;
          via->state setbit no_reach;
          fprintf(was->fl, "  To: %-9.9s Via: %s", temp[ti]->call, port->line);
        }
      }
      while(getit(ntout));
    }

    if (port->mode & idle) return false;
  }
  return true;
}

/*
 *  Trace out the NET/ROM routes.
 */

donet()
{
  register int i;
  register PORTS *new;
  NODE  *from, *n;

  if (port->mode & sysop) { savecmd(); return; }

  if ((new = findport(*port->fld[1])) is NULL) { prtx(mnport); return; }

  if ((port->fl = fopen(port->fld[4], "w")) is NULL)
    { nofile(port->fld[4]); return; }

  nodecnt = 0;
  nodehd = NULL; nodetl = NULL;
  mp = tmp->scr;
  nodetime = atoi(port->fld[3]);

/*
 *  Put the local node into the node table.
 */

  addnode(port->fld[2]);
  nodehd->state setbit no_reach;  /* Local node is reachable */
  nodehd->state setbit no_visit;
  nodehd->hops = 1;
  nodehd->found = nodehd;
  nodehd->seen  = nodehd;

/*
 *  Remember current port.
 */

  was = port;
  ioport(new);

/*
 *  Visit the local node.
 */

  nodedone = false;
  path[0] = nodehd;
  clnode();
  if (port->mode & idle) { ioport(was); fclose(was->fl); return; }
  getnode();
  distnc();
  port->mode = idle;

/*
 *  Visit all nodes the local node knows about.
 */

  while (findunode())
  {
    clnode();
    if (!(port->mode & idle))
    {
      if (cnode()) getnode();
      distnc();
      port->mode = idle;
    }
  }

/*
 *  Put us back on the port we were on when we got here.
 */

  ioport(was);

/*
 *  Now we have all the data, print it.
 */

  curtim();
  fprintf(was->fl, "\n   Data collected at %s %s\n\n", l_date, l_time);

  for (n = nodehd; n isnt NULL; n = n->next)
  {
    if (n->name is NULL)
      fprintf(was->fl, "Node: %-9.9s,     No Alias, %3d hops, seen at %s",
        n->call, n->hops, n->seen->call);
    else
      fprintf(was->fl, "Node: %-9.9s, Alias %-6.6s, %3d hops, seen at %s",
        n->call, n->name, n->hops, n->seen->call);

    if (!(n->state & no_reach)) fprintf(was->fl, "  (Not reachable)");

    if (n->state & no_busy) fprintf(was->fl, "  (Was busy)");

    fprintf(was->fl, "\n");

  }

  i = (int)(mp - tmp->scr);
  fprintf(was->fl, "%d nodes, %d memory\n", nodecnt, i);
  fclose(was->fl);
}

/*
 *  Get a line from the current port.
 *  Handle disconnect and timeout.
 */

getit(tm)
int tm;
{
  register short ctime;

  ctime = port->ctime;
  port->ctime = tm;
  while(!getdat());
  port->ctime = ctime;

  switch(port->mode)
  {
    case timeout :
      port->mode = remote;
      return false;

    case forced  :
      nodedone = true;

    case discon  :
      distnc();
      port->mode = idle;
      return false;

    default :
      port->mode = remote;
      parse();
      return true;
  }
}
#endif
