/*
 *	Copyright 1988 by Rayan S. Zachariassen, all rights reserved.
 *	This will be free software, but only when it is finished.
 */
/*
 *	Lots of modifications (new guts, more or less..) by
 *	Matti Aarnio <mea@nic.funet.fi>  (copyright) 1992-2002
 */

/* LINTLIBRARY */

#include "mailer.h"
#include <stdio.h>
#include <sys/file.h>
#include <ctype.h>
#include "search.h"
#ifdef	HAVE_MMAP
#include <sys/mman.h>
#endif
#include <errno.h>

#include "libz.h"
#include "libc.h"
#include "libsh.h"

extern int deferit;
extern char *skip821address __((char *));

#ifdef HAVE_MMAP
extern void seq_remap __((struct file_map *, long newsize));
extern char *mfgets __((char *, int, struct file_map *));
#endif

extern struct spblk * _open_seq __((search_info *, const char *));

/*
 * Binary search of a file for keyword-value pairs.
 */

conscell *
search_bin(sip)
	search_info *sip;
{
	FILE *fp;
	register char *s;
	off_t	top, bot;
	int	i, retry;
	struct spblk *spl;
	struct file_map *fm;

#ifdef	HAVE_MMAP
	/* This  fstat()  for possible seq_remap() trigger causes a bit
	   more syscalls, than is really necessary.   Therefore it is
	   likely best to have "-m" option on the relation definitions
	   and live with that -- relation information does not pass to
	   the low-level drivers, thus these drivers don't know about
	   possible upper-level "-m"..					*/
#define NO_SEQREMAP
#ifndef NO_SEQREMAP
	struct stat fst;
#endif
#endif
	char fixbuf[BUFSIZ];

	if (sip->file == NULL)
	  return NULL;

	retry = 0;

#ifndef HAVE_MMAP
 reopen:
#endif
	spl = _open_seq(sip, "r");

	if (spl == NULL)
	  return NULL;

	fm = (struct file_map *)(spl->data);

	if (fm == NULL)
	  return NULL; /* Huh !? */

	fp = fm->fp;

#ifdef	HAVE_MMAP
	/* This  fstat()  for possible seq_remap() trigger causes a bit
	   more syscalls, than is really necessary.   Therefore it is
	   likely best to have "-m" option on the relation definitions
	   and live with that -- relation information does not pass to
	   the low-level drivers, thus these drivers don't know about
	   possible upper-level "-m"..					*/
#ifndef NO_SEQREMAP
	if (fstat(FILENO(fp),&fst) < 0) abort(); /* Will succeed, or crash.. */
	if (fst.st_mtime != fm->mtime ||
	    fst.st_size  != fm->size) {
		/* Changes at the original file, remap.. */
		seq_remap(fm,fst.st_size);
	}
#endif
	fm->pos = 0;	/* We have it mmap()ed incore, collect line
			   start offsets into an array for later use
			   on searches..				*/
	if (fm->size > 0 && fm->lines == 0 && fm->offsets == NULL) {
	  int linecnt = 0;
	  const char *buf = fm->membuf;
	  const char *eof = fm->membuf + fm->size;

	  for (;buf < eof; ++buf)
	    if (*buf == '\n')
	      ++linecnt;
	  buf = fm->membuf;
	  fm->offsets = (off_t*)emalloc(sizeof(off_t)*(linecnt+1));
	  linecnt = 0;
	  while (buf < eof) {
	    fm->offsets[linecnt] = (buf - (const char*)fm->membuf);
	    while (buf < eof && *buf != '\n') ++buf;
	    ++buf; /* Skip over the newline */
	    ++linecnt;
	  }
	  fm->lines = linecnt;
	}

	top = fm->lines-1;
	bot = 0;
	while (bot <= top) {
	  off_t mid = (top + bot) / 2;
	  char *cp;

	  fm->pos = fm->offsets[mid];
	  if (mfgets(fixbuf, sizeof (fixbuf), fm) == NULL) return NULL;
		
	  cp = skip821address(fixbuf);

	  if (*cp == '\0')
	    *(cp+1) = '\0';
	  else
	    *cp = '\0';
	  i = cistrcmp(sip->key, fixbuf);
	  if (i == 0) {
	    for (++cp; *cp; ++cp) {
	      int c = (*cp) & 0xFF;
	      if (isascii(c) && !isspace(c))
		break;
	    }
	    for (s = cp; *s != '\0'; ++s) {
	      int c = (*s) & 0xFF;
	      if (!isascii(c) || isspace(c))
		break;
	    }
	    return newstring(dupnstr(cp, s - cp), s - cp);
	  }
	  if (i < 0)
	    top = mid - 1;
	  else
	    bot = mid + 1;
	}
#endif

#ifndef	HAVE_MMAP
	bot = 0;
	fseek(fp, 0L, 2);	/* EOF */
	top = ftell(fp);
	for (;;) {
	  off_t mid = (top + bot)/2;
	  char *cp;
	  int c;

	  fseek(fp, mid, 0);
	  do {
	    c = getc(fp);
	    mid++;
	  } while (!ferror(fp) && c != EOF && c != '\n');

	  if (fgets(fixbuf, sizeof fixbuf, fp) == NULL) {
	    if (!retry && ferror(fp)) {
	      close_seq(sip, "search_bin");
	      ++retry;
	      goto reopen;
	    }
	    break;
	  }

	  cp = skip821address(fixbuf);

	  if (*cp == '\0')
	    *(cp+1) = '\0';
	  else
	    *cp = '\0';
	  i = cistrcmp(sip->key, fixbuf);
	  if (i < 0) {
	    if(top <= mid)
	      break;
	    top = mid;
	  } else if (i == 0) {
	    for (++cp; *cp; ++cp) {
	      int c = (*cp) & 0xFF;
	      if (isascii(c) && !isspace(c))
		break;
	    }
	    for (s = cp; *s != '\0'; ++s) {
	      int c = (*cp) & 0xFF;
	      if (!isascii(c) || isspace(c))
		break;
	    }
	    return newstring(dupnstr(cp, s - cp), s - cp);
	  } else
	    bot = mid;
	}
	fseek(fp, bot, 0);
	while (ftell(fp) < top) {
	  char *cp;

	  if (fgets(fixbuf, sizeof fixbuf, fp) == NULL)
	    return NULL;
	  for (cp = fixbuf; *cp; ++cp) {
	    int c = (*cp) & 0xFF;
	    if (!isascii(c) || isspace(c))
	      break;
	  }
	  if (*cp == '\0')
	    *(cp+1) = '\0';
	  else
	    *cp = '\0';
	  i = cistrcmp(sip->key, fixbuf);
	  if (i < 0)
	    return NULL;
	  else if (i == 0) {
	    for (++cp; *cp; ++cp) {
	      int c = (*cp) & 0xFF;
	      if (isascii(c) && !isspace(c))
		break;
	    }
	    for (s = cp; *s != '\0'; ++s) {
	      int c = (*s) & 0xFF;
	      if (!isascii(c) || isspace(c))
		break;
	    }
	    return newstring(dupnstr(cp, s - cp), s - cp);
	  }
	}
#endif
	return NULL;
}



syntax highlighted by Code2HTML, v. 0.9.1