/*************************************************
*     Exim - an Internet mail transport agent    *
*************************************************/

/* Copyright (c) University of Cambridge 1995 - 2003 */
/* See the file NOTICE for conditions of use and distribution. */

#include "../exim.h"
#include "lf_functions.h"
#include "lsearch.h"



/*************************************************
*              Open entry point                  *
*************************************************/

/* See local README for interface description */

void *
lsearch_open(uschar *filename, uschar **errmsg)
{
FILE *f = Ufopen(filename, "rb");
if (f == NULL)
  {
  int save_errno = errno;
  *errmsg = string_open_failed(errno, "%s for linear search", filename);
  errno = save_errno;
  return NULL;
  }
return f;
}



/*************************************************
*             Check entry point                  *
*************************************************/

BOOL
lsearch_check(void *handle, uschar *filename, int modemask, uid_t *owners,
  gid_t *owngroups, uschar **errmsg)
{
return lf_check_file(fileno((FILE *)handle), filename, S_IFREG, modemask,
  owners, owngroups, "lsearch", errmsg) == 0;
}



/*************************************************
*    Internal function for lsearch/wildlsearch   *
*************************************************/

/* See local README for interface description, plus one additional argument:
wild is TRUE for wildlsearch. */

static int
internal_lsearch_find(void *handle, uschar *filename, uschar *keystring,
  int length, uschar **result, uschar **errmsg, BOOL wild)
{
FILE *f = (FILE *)handle;
uschar buffer[4096];

filename = filename;  /* Keep picky compilers happy */
errmsg = errmsg;

rewind(f);
while (Ufgets(buffer, sizeof(buffer), f) != NULL)
  {
  int ptr, size;
  int p = Ustrlen(buffer);
  int linekeylength;
  uschar *yield;
  uschar *s = buffer;

  while (p > 0 && isspace((uschar)buffer[p-1])) p--;
  buffer[p] = 0;

  if (buffer[0] == 0 || buffer[0] == '#' || isspace(buffer[0]))
    continue;

  /* If the key starts with ", read it as a quoted string. We don't use
  string_dequote() because that uses new store for the result, and we may
  be doing this many times in a long file. We know that the dequoted string
  must be shorter than the original, because we are removing the quotes, and
  also any escape sequences always turn two or more characters into one
  character. Therefore, we can store the new string in the same buffer. */

  if (*s == '\"')
    {
    uschar *t = s++;
    while (*s != 0 && *s != '\"')
      {
      if (*s == '\\') *t++ = string_interpret_escape(&s);
        else *t++ = *s;
      s++;
      }
    if (*s != 0) s++;               /* Past terminating " */
    linekeylength = t - buffer;
    }

  /* Otherwise it is terminated by a colon or white space */

  else
    {
    while (*s != 0 && *s != ':' && !isspace(*s)) s++;
    linekeylength = s - buffer;
    }

  /* A wild lsearch treats each key as a possible wildcarded string. */

  if (wild)
    {
    int rc;
    int save = buffer[linekeylength];
    uschar *list = buffer;

    buffer[linekeylength] = 0;
    rc = match_isinlist(keystring,
      &list,
      UCHAR_MAX+1,    /* Single-item list */
      NULL,           /* No anchor */
      NULL,           /* No caching of named lists */
      MCL_STRING,
      TRUE,  /* Caseless */
      NULL);
    buffer[linekeylength] = save;

    if (rc == FAIL) continue;
    if (rc == DEFER) return DEFER;
    }

  /* A non-wild lsearch treats each key as a litersl */

  else
    {
    if (linekeylength != length || strncmpic(buffer, keystring, length) != 0)
      continue;
    }

  /* The key has matched. Skip spaces after the key, and allow an optional
  colon - after the spaces. This is an odd specification, but it's for
  compatibility. */

  while (isspace((uschar)*s)) s++;
  if (*s == ':')
    {
    s++;
    while (isspace((uschar)*s)) s++;
    }

  size = 100;
  ptr = 0;
  yield = store_get(size);
  if (*s != 0)
    yield = string_cat(yield, &size, &ptr, s, Ustrlen(s));

  while (Ufgets(buffer, sizeof(buffer), f) != NULL)
    {
    p = Ustrlen(buffer);
    while (p > 0 && isspace((uschar)buffer[p-1])) p--;
    buffer[p] = 0;
    if (buffer[0] == 0 || buffer[0] == '#') continue;
    if (!isspace((uschar)buffer[0])) break;
    s = buffer;
    while (isspace((uschar)*s)) s++;
    *(--s) = ' ';
    yield = string_cat(yield, &size, &ptr, s, Ustrlen(s));
    }

  yield[ptr] = 0;
  *result = yield;
  return OK;
  }

return FAIL;
}


/*************************************************
*         Find entry point for lsearch           *
*************************************************/

/* See local README for interface description */

int
lsearch_find(void *handle, uschar *filename, uschar *keystring, int length,
  uschar **result, uschar **errmsg)
{
return internal_lsearch_find(handle, filename, keystring, length, result,
  errmsg, FALSE);
}



/*************************************************
*      Find entry point for wildlsearch          *
*************************************************/

/* See local README for interface description */

int
wildlsearch_find(void *handle, uschar *filename, uschar *keystring, int length,
  uschar **result, uschar **errmsg)
{
return internal_lsearch_find(handle, filename, keystring, length, result,
  errmsg, TRUE);
}



/*************************************************
*              Close entry point                 *
*************************************************/

/* See local README for interface description */

void
lsearch_close(void *handle)
{
fclose((FILE *)handle);
}

/* End of lookups/lsearch.c */
