#include <stdio.h>

typedef struct _doc_list_elem {
  char docname[128] ;
  long count, dircount ;
  struct _doc_list_elem *next, *nextlevel ;
} doc_list_elem ;

typedef struct _site_list_elem {
  char domname[32] ;
  long count, domcount ;
  struct _site_list_elem *next, *nextlevel ;
} site_list_elem ;

typedef struct _filter_list_elem {
  int include ;
  int file ;
  int num_strings ;
  char **strings ;
  struct _filter_list_elem *next ;
} filter_list_elem ;

filter_list_elem *filter_list ;
int include_default ;
int leaves = 0 ;

void construct_filter(char *fname)
{
  FILE *fp ;
  int i, ok, include, file ;
  char *t, s[1024] ;
  filter_list_elem *new_elem, *prev_elem ;

  prev_elem = NULL ;

  fp = NULL ;
  fp = fopen(fname, "r") ;

  while (fp != NULL && !feof(fp)) {
    fgets(s, 1024, fp) ;
    if (!feof(fp)) {
      ok = 0 ;
      if (!strncmp(s, "exclude-all", 11))
	include_default = 0 ;
      else if (!strncmp(s, "include-file", 12)) {
	ok = 1 ;include = 1 ; file = 1 ;
      }
      else if (!strncmp(s, "exclude-file", 12)) {
	ok = 1 ; include = 0 ; file = 1 ;
      }
      else if (!strncmp(s, "include-host", 12)) {
	ok = 1 ; include = 1 ; file = 0 ;
      }
      else if (!strncmp(s, "exclude-host", 12)) {
	ok = 1 ; include = 0 ; file = 0 ;
      }
      if (ok) {
	new_elem = (filter_list_elem *)malloc(sizeof(filter_list_elem)) ;
	new_elem->include = include ;
	new_elem->file = file ;
	new_elem->num_strings = 0 ;
	t = s+12 ;
	while (isspace(*t)) t++ ;

	while (*t != '\0') {
	  new_elem->num_strings++ ;
	  i = 0 ;
	  while (t[i] != '\0' && !isspace(t[i])) i++ ;
	  if (new_elem->num_strings == 1)
	    new_elem->strings = (char **)malloc(sizeof(char *)) ;
	  else
	    new_elem->strings = 
	      (char **)realloc(new_elem->strings,
			       sizeof(char *)*new_elem->num_strings) ;
	  new_elem->strings[new_elem->num_strings-1] =
	    (char *)malloc(sizeof(char)*(i+1)) ;
	  strncpy(new_elem->strings[new_elem->num_strings-1],
		  t, i) ;
	  new_elem->strings[new_elem->num_strings-1][i] = '\0' ;
	  t += i;
	  while (isspace(*t)) t++ ;
	}
	if (prev_elem == NULL) 
	  filter_list = new_elem ;
	else
	  prev_elem->next = new_elem ;
	prev_elem = new_elem ;
      }
    }
  }
  fclose(fp) ;
}

int filter_check(char *host, char *request)
{
  int retval, matched, i ;
  filter_list_elem *elem_ptr ;
  
  retval = include_default ;
  elem_ptr = filter_list ;
  while (elem_ptr != NULL) {
    for (matched = 1, i = 0 ; matched && i < elem_ptr->num_strings ; i++) {
      if (elem_ptr->file) 
	matched = (strstr(request, elem_ptr->strings[i]) != NULL) ;
      else matched = (strstr(host, elem_ptr->strings[i]) != NULL) ;
    }
    if (matched) retval = elem_ptr->include ;
    elem_ptr = elem_ptr->next ;
  }
  return(retval) ;
}

int startdate=0, start_day, start_month, start_year ;
int enddate=0, end_day, end_month, end_year ;

void parse_date(char *s, int *day, int *month, int *year)
{
  int i ;
  char month_str[4], remainder_str[64] ;
  char *months[] = {"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug",
		      "Sep","Oct","Nov","Dec"} ;

  sscanf(s, "%d/%3s/%d:%s", day, &month_str, year, &remainder_str) ;
  for (i = 0 ; strncmp(month_str, months[i], 3) ; i++) ;
  *month = i ;
}

int date_check(char *datestr)
{
  int retval, day, mo, yr ;

  retval = 1 ;

  if (startdate || enddate) {
    parse_date(datestr, &day, &mo, &yr) ;
    if (startdate) 
      if (yr < start_year ||
	  yr == start_year && mo < start_month ||
	  yr == start_year && mo == start_month && day < start_day)
	retval = 0 ;
    if (retval && enddate)
      if (yr > end_year ||
	  yr == end_year && mo > end_month ||
	  yr == end_year && mo == end_month && day > end_day)
	retval = 0 ;
  }
  return(retval) ;
}

void parse_entry(char *line, char **host, char **authuser, char **rfc931, 
		 char **datestr, char **request, char **status, char **bytes)
{
  char *s ;

  s = line ;
  *host = s ;
  while (*s != ' ') s++ ;
  *s = '\0' ;
  s++ ;
  
  *rfc931 = s ;
  while (*s != ' ') s++ ; 
  *s = '\0' ;
  s++ ;

  *authuser = s ;
  while (*s != ' ') s++ ; 
  *s = '\0' ;
  s++ ;

  s++ ; /* skip bracket */
  *datestr = s ;
  while (*s != ']') s++ ;
  *s = '\0' ;
  s++ ;

  while (*s != '"') s++ ;
  s++ ;
  *request = s ;
  while (*s != '"') s++ ;
  *s = '\0' ;
  s+= 2 ;

  *status = s ;
  while (*s != ' ') s++ ;
  *s = '\0' ;
  s++ ;

  *bytes = s ;
}

void add_to_count_structure(char *s, doc_list_elem **doclist)
{
  int file, i ;
  doc_list_elem *cur_root, *prev_root, *elem_ptr, *new_elem ;

  file = 0 ;
  cur_root = *doclist ;
  prev_root = NULL ;

  s += 1 ;
  while (!file) {
    i = 0 ; 
    while (s[i] != '/' && s[i] != ' ' && s[i] != '\0') i++ ;
    if (s[i] == '\0' || s[i] == ' ' || s[i+1] == '\0' || s[i+1] == ' ') 
      file = 1 ;

    elem_ptr = cur_root ;
    while (elem_ptr != NULL &&
	   !(strlen(elem_ptr->docname) == i &&
	     !strncmp(s, elem_ptr->docname, i)))
      elem_ptr = elem_ptr->next ;
    if (elem_ptr != NULL) {
      if (file)
	(elem_ptr->count)++ ;
      else {
	(elem_ptr->dircount)++ ;
	prev_root = elem_ptr ;
	cur_root = elem_ptr->nextlevel ;
      }
    }
    else {
      new_elem = (doc_list_elem *)malloc(sizeof(doc_list_elem)) ;
      strncpy(new_elem->docname, s, i) ;
      new_elem->docname[i] = '\0' ;
      new_elem->count = new_elem->dircount = 0;
      if (file)
	(new_elem->count)++ ;
      else 
	(new_elem->dircount)++ ;
      new_elem->next = cur_root ;
      if (prev_root == NULL)
	*doclist = new_elem ;
      else
	prev_root->nextlevel = new_elem ;
      prev_root = new_elem ;
      cur_root = NULL ;
      
    }
    if (!file) s += i+1 ;
  }
		  
}

void dump_count_structure(doc_list_elem *elem_ptr, char *prefstr, 
			  int prune_level)
{
  char newstr[1024] ;

  if (elem_ptr != NULL && (prune_level == -1 || prune_level > 0)) {
    if (!leaves ||
	(leaves && ((prune_level == -1 && elem_ptr->nextlevel == NULL) ||
		    prune_level == 1))) {
      printf("%s/%s %d %d", prefstr, elem_ptr->docname,
	     elem_ptr->count, elem_ptr->dircount) ;
      printf("\n") ;
    }
    sprintf(newstr, "%s/%s", prefstr, elem_ptr->docname) ;
    if (prune_level != -1) 
      dump_count_structure(elem_ptr->nextlevel, newstr, prune_level-1) ;
    else dump_count_structure(elem_ptr->nextlevel, newstr, prune_level) ;
    dump_count_structure(elem_ptr->next, prefstr, prune_level) ;
  }
}

void add_to_site_structure(char *sitestr, site_list_elem **sitelist)
{
  int site, i ;
  site_list_elem *cur_root, *prev_root, *elem_ptr, *new_elem ;
  char *s ;

  site = 0 ;
  cur_root = *sitelist ;
  prev_root = NULL ;

  i = 0 ;
  while (i < strlen(sitestr) && (isdigit(sitestr[i]) || sitestr[i] == '.'))
    i++ ;
  if (i < strlen(sitestr)) 
    s = sitestr+strlen(sitestr) ;
  else s = sitestr ;

  while (!site) {
    if (s == sitestr) site = 1 ;
    else {
      *s = '\0' ;
      while (s != sitestr && *(s-1) != '.') s-- ;
      if (s == sitestr) site = 1 ;
    }

    elem_ptr = cur_root ;
    while (elem_ptr != NULL &&
	   strcmp(s, elem_ptr->domname))
      elem_ptr = elem_ptr->next ;
    if (elem_ptr != NULL) {
      if (site)
	(elem_ptr->count)++ ;
      else {
	(elem_ptr->domcount)++ ;
	prev_root = elem_ptr ;
	cur_root = elem_ptr->nextlevel ;
      }
    }
    else {
      new_elem = (site_list_elem *)malloc(sizeof(site_list_elem)) ;
      strcpy(new_elem->domname, s) ;
      if (site)
	(new_elem->count)++ ;
      else
	(new_elem->domcount)++ ;
      new_elem->next = cur_root ;
      if (prev_root == NULL)
	*sitelist = new_elem ;
      else
	prev_root->nextlevel = new_elem ;
      prev_root = new_elem ;
      cur_root = NULL ;
    }

    if (s != sitestr) s-- ;
  }
}

void dump_site_structure(site_list_elem *elem_ptr, char *prefstr, 
			 int prune_level)
{
  char newstr[1024] ;

  if (elem_ptr != NULL && (prune_level != 0)) {
    if (!leaves ||
	(leaves && ((prune_level == -1 && elem_ptr->nextlevel == NULL) ||
		    prune_level == 1))) {
      if (strlen(prefstr) == 0) 
	printf("%s ", elem_ptr->domname) ;
      else
	printf("%s.%s ", elem_ptr->domname, prefstr) ;
      printf("%d %d", elem_ptr->count, elem_ptr->domcount) ;
      printf("\n") ;
    }
    if (strlen(prefstr) == 0)
      sprintf(newstr, "%s", elem_ptr->domname) ;
    else 
      sprintf(newstr, "%s.%s", elem_ptr->domname, prefstr) ;
    if (prune_level != -1)
      dump_site_structure(elem_ptr->nextlevel, newstr, prune_level-1) ;
    else dump_site_structure(elem_ptr->nextlevel, newstr, prune_level) ;
    dump_site_structure(elem_ptr->next, prefstr, prune_level) ;
  }
}

char cur_datestr[80] ;
int cur_datetotal ;

void date_inc(char *datestr)
{
  if (strlen(cur_datestr) == 0) {
    strncat(cur_datestr, datestr, 11) ;
    cur_datestr[11] = '\0'; 
    cur_datetotal = 1 ;
  }
  else if (!strncmp(cur_datestr, datestr, 11)) {
    cur_datetotal++ ;
  }
  else {
    printf("%s %d\n", cur_datestr, cur_datetotal) ;
    cur_datetotal = 1 ;
    strncpy(cur_datestr, datestr, 11) ;
    cur_datestr[11] = '\0' ;
  }
}

main(int argc, char **argv)
{
  char *logfname = NULL, *filtfname = NULL ;
  FILE *fp ;
  int i, bysite = 0, daytotal=0 ;
  char s[10240], *t ;
  doc_list_elem *doc_list = NULL ;
  site_list_elem *site_list = NULL ;
  
  char *cmpstr, *host, *authuser, *rfc931, *datestr, *request, *status, *bytes;
  int prune_level = -1 ;

  i = 1 ;
  while (i < argc) {
    if (!strcmp(argv[i], "-sdate")) {
      parse_date(argv[i+1], &start_day, &start_month, &start_year) ;
      startdate = 1 ;
      i+= 2 ;
    }
    else if (!strcmp(argv[i], "-edate")) {
      parse_date(argv[i+1], &end_day, &end_month, &end_year) ;
      enddate = 1 ;
      i += 2 ;
    }
    else if (!strcmp(argv[i], "-prune")) {
      prune_level = atoi(argv[i+1]) ;
      i += 2 ;
    }
    else if (!strcmp(argv[i], "-bysite")) {
      bysite = 1 ;
      i++ ;
    }
    else if (!strcmp(argv[i], "-leaves")) {
      leaves = 1 ;
      i++ ; 
    }
    else if (!strcmp(argv[i], "-daytotal")) {
      strcat(cur_datestr, "") ;
      cur_datetotal = 0 ;
      daytotal = 1 ;
      i++ ;
    }
    else if (logfname == NULL) {
      logfname = argv[i] ;
      i++ ;
    }
    else {
      filtfname = argv[i] ;
      i++ ;
    }
  } 
   
  fp = fopen(logfname, "r") ;

  include_default = 1 ;
  filter_list = NULL ;
  if (filtfname != NULL) construct_filter(filtfname) ;

  while (!feof(fp)) {
    fgets(s, 10240, fp) ;
    if (!feof(fp)) {
      parse_entry(s, &host, &authuser, &rfc931, &datestr, &request, 
		  &status, &bytes) ;
      t = request ;
      if ((!strcmp(status, "200") || !strcmp(status, "304"))  &&
	  !strncmp(t, "GET", 3)) {
	t += 4 ;
	i = 0 ;
	while (*(t+i) != ' ' && *(t+i) != '?') i++ ;
	t[i] = '\0' ;
	if (filter_check(host, t) && date_check(datestr)) {
	  if (bysite)
	    add_to_site_structure(host, &site_list) ;
	  else if (daytotal)
	    date_inc(datestr) ;
	  else
	    add_to_count_structure(t, &doc_list) ;
	}
      }
    }
  }
  if (daytotal)
    printf("%s %d\n", cur_datestr, cur_datetotal) ;
  else if (bysite)
    dump_site_structure(site_list, "", prune_level) ;
  else 
    dump_count_structure(doc_list, "", prune_level) ;
}
