#include "strerr.h"
#include "stralloc.h"
#include "getln.h"
#include "substdio.h"
#include "subfd.h"
#include "exit.h"
#include "fmt.h"
#include "byte.h"
#include "cdbmss.h"

#define FATAL "tcprules: fatal: "

unsigned long linenum = 0;
char *fntemp;
char *fn;

stralloc line = {0};
int match = 1;

stralloc address = {0};
stralloc data = {0};
stralloc key = {0};

struct cdbmss cdbmss;

void die_nomem() {
  strerr_die2x(111,FATAL,"out of memory");
}
void usage() {
  strerr_die1x(100,"tcprules: usage: tcprules rules.cdb rules.tmp");
}
void die_bad() {
  if (!stralloc_0(&line)) die_nomem();
  strerr_die3x(100,FATAL,"unable to parse this line: ",line.s);
}
void die_write() {
  strerr_die4sys(111,FATAL,"unable to write to ",fntemp,": ");
}

char strnum[FMT_ULONG];
stralloc sanum = {0};

void getnum(buf,len,u)
char *buf;
int len;
unsigned long *u;
{
  if (!stralloc_copyb(&sanum,buf,len)) die_nomem();
  if (!stralloc_0(&sanum)) die_nomem();
  if (sanum.s[scan_ulong(sanum.s,u)]) die_bad();
}

void doaddressdata()
{
  int i;
  int left;
  int right;
  unsigned long bot;
  unsigned long top;

  if (byte_chr(address.s,address.len,'@') == address.len) {
    i = byte_chr(address.s,address.len,'-');
    if (i < address.len) {
      left = byte_rchr(address.s,i,'.');
      if (left == i) left = 0; else ++left;

      ++i;
      right = i + byte_chr(address.s + i,address.len - i,'.');

      getnum(address.s + left,i - 1 - left,&bot);
      getnum(address.s + i,right - i,&top);
      if (top > 255) top = 255;

      while (bot <= top) {
	if (!stralloc_copyb(&key,address.s,left)) die_nomem();
	if (!stralloc_catb(&key,strnum,fmt_ulong(strnum,bot))) die_nomem();
	if (!stralloc_catb(&key,address.s + right,address.len - right)) die_nomem();
        if (cdbmss_add(&cdbmss,key.s,key.len,data.s,data.len) == -1) die_write();
	++bot;
      }

      return;
    }
  }

  if (!stralloc_copy(&key,&address)) die_nomem();
  if (cdbmss_add(&cdbmss,key.s,key.len,data.s,data.len) == -1) die_write();
}

void main(argc,argv)
int argc;
char **argv;
{
  int colon;
  char *x;
  int len;
  int fd;
  int i;
  char ch;

  fn = argv[1];
  if (!fn) usage();
  fntemp = argv[2];
  if (!fntemp) usage();

  fd = open_trunc(fntemp);
  if (fd == -1)
    strerr_die4sys(111,FATAL,"unable to create ",fntemp,": ");
  if (cdbmss_start(&cdbmss,fd) == -1) die_write();

  while (match) {
    if (getln(subfdin,&line,&match,'\n') == -1)
      strerr_die2sys(111,FATAL,"unable to read input: ");

    x = line.s; len = line.len;

    if (!len) break;
    if (x[0] == '#') continue;
    if (x[0] == '\n') continue;

    while (len) {
      ch = x[len - 1];
      if (ch != '\n') if (ch != ' ') if (ch != '\t') break;
      --len;
    }
    line.len = len; /* for die_bad() */

    colon = byte_chr(x,len,':');
    if (colon == len) continue;

    if (!stralloc_copyb(&address,x,colon)) die_nomem();
    if (!stralloc_copys(&data,"")) die_nomem();

    x += colon + 1; len -= colon + 1;

    if ((len >= 4) && byte_equal(x,4,"deny")) {
      if (!stralloc_catb(&data,"D",2)) die_nomem();
      x += 4; len -= 4;
    }
    else if ((len >= 5) && byte_equal(x,5,"allow")) {
      x += 5; len -= 5;
    }
    else
      die_bad();

    while (len)
      switch(*x) {
        case ',':
          i = byte_chr(x,len,'=');
          if (i == len) die_bad();
          if (!stralloc_catb(&data,"+",1)) die_nomem();
          if (!stralloc_catb(&data,x + 1,i)) die_nomem();
          x += i + 1; len -= i + 1;
          if (!len) die_bad();
          ch = *x;
          x += 1; len -= 1;
          i = byte_chr(x,len,ch);
          if (i == len) die_bad();
          if (!stralloc_catb(&data,x,i)) die_nomem();
          if (!stralloc_0(&data)) die_nomem();
          x += i + 1; len -= i + 1;
          break;
        default:
          die_bad();
      }

    doaddressdata();
  }

  if (cdbmss_finish(&cdbmss) == -1) die_write();
  if (fsync(fd) == -1) die_write();
  if (close(fd) == -1) die_write(); /* NFS stupidity */
  if (rename(fntemp,fn))
    strerr_die6sys(111,FATAL,"unable to move ",fntemp," to ",fn,": ");

  _exit(0);
}
