#ifndef __lint
static const char rcsid[] = "@(#) $Header: findpath.c,v 1.21 94/10/06 16:15:41 deyke Exp $";
#endif

#include <ctype.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

#ifdef ibm032
#include <sys/file.h>
#define SEEK_END L_XTND
#endif

#include "bbs.h"
#include "calc_crc.h"
#include "callvalid.h"

#define HASH_SIZE 1009

struct node {
  struct node *next;            /* Linked list pointer */
  struct neighbor *neighbors;   /* List of neighbors */
  char name[1];                 /* Node name */
};

struct neighbor {
  struct neighbor *next;        /* Linked list pointer */
  struct node *node;            /* Pointer to node entry */
  long date;                    /* Date of last usage */
};

static const char datafile[] = "findpath_data";
static const char tempfile[] = "findpath_temp";
static long currdate;
static long timeout;
static struct node *nodes[HASH_SIZE];

/*---------------------------------------------------------------------------*/

static struct node *nodeptr(const char *name)
{

  int hash;
  struct node *node;

  for (node = nodes[hash = calc_crc_16(name) % HASH_SIZE];
       node && strcmp(node->name, name);
       node = node->next)
    ;
  if (!node) {
    node = (struct node *) malloc(sizeof(struct node) + strlen(name));
    node->next = nodes[hash];
    node->neighbors = 0;
    strcpy(node->name, name);
    nodes[hash] = node;
  }
  return node;
}

/*---------------------------------------------------------------------------*/

static void add_neighbor(struct node *node, struct node *neighbor, long date)
{
  struct neighbor *nb;

  for (nb = node->neighbors; nb && (nb->node != neighbor); nb = nb->next) ;
  if (!nb) {
    nb = (struct neighbor *) malloc(sizeof(struct neighbor));
    nb->next = node->neighbors;
    nb->node = neighbor;
    nb->date = date;
    node->neighbors = nb;
    return;
  }
  if (nb->date < date) nb->date = date;
}

/*---------------------------------------------------------------------------*/

static void add_route(const char *name1, const char *name2, long date)
{
  int cmp;

  if (date < timeout) return;
  if (date > currdate) date = currdate;
  cmp = strcmp(name1, name2);
  if (cmp < 0)
    add_neighbor(nodeptr(name1), nodeptr(name2), date);
  else if (cmp > 0)
    add_neighbor(nodeptr(name2), nodeptr(name1), date);
}

/*---------------------------------------------------------------------------*/

static char *parseheader(char *line, long *date)
{

  char *p, *q;
  int tmp;
  int year, month, day, hour, minute;
  struct tm tm;

  if (sscanf(line, "R:%02d%02d%02d/%02d%02d", &year, &month, &day, &hour, &minute) != 5) return NULL;
  if (day > 31) {
    tmp = day;
    day = year;
    year = tmp;
  }
  if (year   < 80 || year   > 99 ||
      month  <  1 || month  > 12 ||
      day    <  1 || day    > 31 ||
      hour   <  0 || hour   > 23 ||
      minute <  0 || minute > 59) return NULL;
  if (!(p = strchr(line, '@'))) return NULL;
  p++;
  while (*p == ':' || isspace(*p & 0xff)) p++;
  for (q = p; isalnum(*q & 0xff); q++)
    if (*q >= 'A' && *q <= 'Z') *q = tolower(*q);
  *q = 0;
  if (!callvalid(p)) return NULL;
  tm.tm_sec = 0;
  tm.tm_min = minute;
  tm.tm_hour = hour;
  tm.tm_mday = day;
  tm.tm_mon = month - 1;
  tm.tm_year = year;
  tm.tm_isdst = 0;
  *date = mktime(&tm);
  return p;
}

/*---------------------------------------------------------------------------*/

static void getfilename(int mesg, char *filename)
{
  sprintf(filename,
	  "%02x/%02x/%02x/%02x",
	  (mesg >> 24) & 0xff,
	  (mesg >> 16) & 0xff,
	  (mesg >>  8) & 0xff,
	  (mesg      ) & 0xff);
}

/*---------------------------------------------------------------------------*/

int main(void)
{

  FILE * fp;
  char *cp;
  char *host;
  char filename[1024];
  char hostname[1024];
  char line[1024];
  char name1[1024];
  char name2[1024];
  char prev[1024];
  int findex;
  int i;
  int lastmesg;
  long date;
  long metric;
  struct index index;
  struct neighbor *nb;
  struct node *node;

  putenv("TZ=UTC0");
  time(&currdate);
  timeout = currdate - 365L * 24L * 60L * 60L;

  gethostname(hostname, sizeof(hostname));
  if ((cp = strchr(hostname, '.'))) *cp = 0;

  if (chdir(WRKDIR)) return 1;

  if ((findex = open(INDEXFILE, O_RDONLY, 0644)) < 0) return 1;
  if (lseek(findex, -sizeof(struct index), SEEK_END) < 0) return 1;
  if (read(findex, &index, sizeof(struct index)) != sizeof(struct index)) return 1;
  close(findex);

  lastmesg = 0;
  fp = fopen(datafile, "r");
  if (fp) {
    if (fgets(line, sizeof(line), fp)) lastmesg = atoi(line);
    while (fgets(line, sizeof(line), fp) && sscanf(line, "%s%s%ld", name1, name2, &date) == 3)
      add_route(name1, name2, date);
    fclose(fp);
  }

  i = lastmesg - 20;
  if (i < 1) i = 1;
  lastmesg = 0;
  for (; i <= index.mesg; i++) {
    getfilename(i, filename);
    if (!(fp = fopen(filename, "r"))) continue;
    strcpy(prev, hostname);
    while (fgets(line, sizeof(line), fp) && (host = parseheader(line, &date))) {
      add_route(prev, host, date);
      strcpy(prev, host);
      lastmesg = i;
    }
    fclose(fp);
  }

  fp = fopen(tempfile, "w");
  if (fp) fprintf(fp, "%d\n", lastmesg);
  for (i = 0; i < HASH_SIZE; i++)
    for (node = nodes[i]; node; node = node->next)
      for (nb = node->neighbors; nb; nb = nb->next) {
	if (fp) fprintf(fp, "%s %s %ld\n", node->name, nb->node->name, nb->date);
	if (strcmp(node->name, hostname) && strcmp(nb->node->name, hostname)) {
	  metric = 500L * ((currdate - nb->date) / (30L * 24L * 60L * 60L) + 1L);
	  printf("%s\t%s(%ld)\n", node->name, nb->node->name, metric);
	  printf("%s\t%s(%ld)\n", nb->node->name, node->name, metric);
	}
      }
  if (fp) {
    fclose(fp);
    rename(tempfile, datafile);
  }

  return 0;
}

