#include "scan.h"
#include "util.h"
#include "log.h"

#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <ctype.h>

static unsigned long charcount, charcurr;
static char *strscan;

ScanResult
scanString(char *str, char **inptr, int maxsize)
{
  char *tmpbuf;
  int sync;

  switch (**inptr) {
  case '"': /* quoted */

    /* count characters */
    for (tmpbuf=++(*inptr),charcount=0;
	 isQuotedChar(*tmpbuf)&&charcount<100000;
	 tmpbuf++,charcount++) {
      if (*tmpbuf=='\\') {
	tmpbuf++;
	if (*tmpbuf!='"'&&*tmpbuf!='\\')
	  return ScanBad;
      }
    }
    if (charcount>maxsize) {
      lprintf(LOG_NOTICE, "Client sent string that exceeded buffer size (%d bytes)",
	      maxsize);
      return ScanBad;
    }
    if (*tmpbuf!='"')
      return ScanBad;

    /* copy characters */
    for (charcurr=0; charcurr<charcount; charcurr++) {
      if (**inptr=='\\')
	(*inptr)++;
      str[charcurr] = **inptr;
      (*inptr)++;
    }
    str[charcurr] = 0;

    (*inptr)++;

    break;

  case '{': /* literal */

    strscan = str;
    for(tmpbuf=++(*inptr); *tmpbuf>='0'&&*tmpbuf<='9'; tmpbuf++);
    if (*tmpbuf=='+') {
      tmpbuf++;
      sync = 0;
    }
    else {
      sync = 1;
    }
    if (tmpbuf[0]!='}')
      return ScanBad;

    if (!isCRLF(++tmpbuf))
      return ScanBad;

    charcount = atol(*inptr);
    if (charcount>maxsize||charcount<0) {
      return ScanBad;
    }
    charcurr = 0;

    if (sync)
      return ScanMoreSync;

    return ScanMore;

  default:
    return ScanBad;
  }

  return ScanOk;
}

ScanResult
scanStringMore(char **inptr)
{
  if (charcurr>=charcount)
    return ScanOk;

  for (; charcurr<charcount; charcurr++) {

    if (!**inptr) {
      return ScanMore;
    }
    strscan[charcurr] = *((*inptr)++);
  }
  strscan[charcurr] = 0;

  if (!strcmp("", *inptr))
    return ScanMore;

  return ScanOk;
}

static char format[MAX_FORMAT_LEN];
static char *fmtptr;

static char *strs[MAX_STRS];
static int str_size[MAX_STRS];
static int num_strs;
static int curr_str;
static int scanning_str;

void
setupScan(char *fmt, int str_count, ...)
{
  va_list ap;

  va_start(ap, str_count);
  for (num_strs=0; num_strs<str_count; num_strs++) {
    strs[num_strs] = va_arg(ap, char*);
    str_size[num_strs] = va_arg(ap, int);
  }
  va_end(ap);
  
  strcpy(format, fmt);
  fmtptr = format;
  curr_str = 0;
  scanning_str = 0;
}

ScanResult
scanLine(char *buf)
{

  int escaped;
  char *inptr;

  escaped = 0;
  inptr = buf;

  if (scanning_str) {
    switch (scanStringMore(&inptr)) {
    case ScanBad:
      return ScanBad;
    case ScanMore:
      return ScanMore;
    case ScanMoreSync:
      return ScanMoreSync;
    case ScanOk:
      curr_str++;
      scanning_str = 0;
    }
  }

  while (*fmtptr&&*inptr) {

    if (escaped) {
      escaped = 0;
      if (*fmtptr!=*inptr)
	return ScanBad;
      inptr++;
      fmtptr++;
      continue;
    }

    switch (*fmtptr) {

    case '\\':
      escaped = 1;
      fmtptr++;
      break;

    case '%':
      fmtptr++;
      switch (*fmtptr) {
      case 's':
	fmtptr++;
	scanning_str = 1;
	switch(scanString(strs[curr_str], &inptr, str_size[curr_str])) {
	case ScanBad:
	  return ScanBad;
	case ScanMore:
	  scanning_str = 1;
	  return ScanMore;
	case ScanMoreSync:
	  scanning_str = 1;
	  return ScanMoreSync;
	case ScanOk:
	  curr_str++;
	}

	break;
      case 'i':
	return ScanOk;

	break;

      default:
	return ScanBad;
      }
      break;

    default:
      if (toupper(*fmtptr)!=toupper(*inptr))
	return ScanBad;
      fmtptr++;
      inptr++;
    }


  }

  if (!*fmtptr&&isCRLF(inptr))
    return ScanOk;

  return ScanBad;

}
