/* logical.c */

#include "ml.h"

#define SECONDS_PER_DAY (60*60*24)

static char lan[4];
static char lheure[64];

char *mois[12]= {
  "JAN", "FEB", "MAR", "APR", "MAY", "JUN",
  "JUL", "AUG", "SEP", "OCT", "NOV", "DEC",
};

static int dpm[]= {/*dec - dec */
  31, 30, 28, 31, 30, 31, 30, 31, 31, 31, 31, 30, 31
};

static int leap_dpm[]= {/*dec - dec */
  31, 30, 29, 31, 30, 31, 30, 31, 31, 31, 31, 30, 31
};


/*
 * Macro definitions. These are filter criteria which are expanded
 * at search time and contain more than one filter rule. for instance,
 * "conversant john" is the same as "from john or to john or cc john". 
 */

static LEAF recipient_macro[]= {
  { "CC",   NO_OP, FALSE, pred_field,   NULL },
  { "TO",   OR_OP, FALSE, pred_field,   NULL },
  { NULL,   NO_OP, FALSE, pred_simple,  NULL }
};  
  
static LEAF correspondent_macro[]= {
  { "FROM", NO_OP, FALSE, pred_field,   NULL },
  { "TO",   OR_OP, FALSE, pred_field,   NULL },
  { NULL,   NO_OP, FALSE, pred_simple,  NULL }
};  

static LEAF conversant_macro[]= {
  { "FROM", NO_OP, FALSE, pred_field,   NULL },
  { "TO",   OR_OP, FALSE, pred_field,   NULL },
  { "CC",   OR_OP, FALSE, pred_field,   NULL },
  { NULL,   NO_OP, FALSE, pred_simple,  NULL }
};  

static LEAFMACRO macro_petals[]= {
  { RECIPIENT_MACRO,     recipient_macro,     2 },
  { CORRESPONDENT_MACRO, correspondent_macro, 2 },
  { CONVERSANT_MACRO,    conversant_macro,    3 },
  { NULL,                NULL,                0 }
};


#ifdef __STDC__
Message_List *apply_op_to_operands(Mailbox *mailbox, BOOLEAN_OPERATOR op,
				   Message_List **list1,
				   Message_List **list2)
#else
Message_List *apply_op_to_operands(mailbox,op,list1,list2)
     Mailbox *mailbox;
     BOOLEAN_OPERATOR op;
     Message_List **list1;
     Message_List **list2;
#endif
{
  Message_List *result = NULL;

  switch(op) {
  case AND_OP:
    conjunct_and(mailbox,*list1,*list2);
    result = mailbox->found;
    mailbox->found = NULL;
    break;

    /* 
     * A no_op is treated as an or_op because we don't know which of the
     * two lists to preserve -- one of them will be empty. The other needs
     * to be returned. Or'ing the two gives us the right result, regardless.
     */

  case OR_OP:
  case NO_OP:
    conjunct_or(mailbox,*list1,*list2);
    result = mailbox->found;
    mailbox->found = NULL;
    break;

  default:
    /* this shouldn't ever happen. */
    break;
  }

  free_message_list(*list1,TRUE,FALSE);
  *list1 = NULL;
  free_message_list(*list2,TRUE,FALSE);
  *list2 = NULL;

  return(result);
}

#ifdef __STDC__
LEAFMACRO *macro_expansion_required(LEAF *leaf)
#else
LEAFMACRO *macro_expansion_required(leaf)
     LEAF *leaf;
#endif
{
  int i;
  LEAFMACRO *lm = macro_petals;
  char tmp[FILEBUFFLEN];

  strcpy(tmp, leaf->predicate);

  /*
   * We have either 
   *  predicate "FIELD"
   *      or
   *  predicate
   * In the former case we NUL terminate the predicate
   * for comparison to our macro predicates 
   */

  if (leaf->predicate_type == pred_field ||
      leaf->predicate_type == pred_dynamic_field) {
    /* select the predicate only - it is terminated by a space */
    strtok(tmp, " ");			/* NULL out the SPACE */
  }
  for (i = 0; lm->macro_name; ++i, ++lm)
    if (strcasecmp(lm->macro_name, tmp) == 0)
      return lm;
  return NIL;
}


#ifdef __STDC__
void do_imap_macro_mail_search(Mailbox *mailbox, Lview *parent, 
			       LEAFMACRO *lm, LEAF *petal)
#else
void do_imap_macro_mail_search(mailbox, parent, lm, petal)
     Mailbox *mailbox;
     Lview *parent;
     LEAFMACRO *lm;			/* The expansion rules */
     LEAF *petal;			/* The macro which we expand */
#endif     
{
  LEAF *macro= lm->leaves;
  int n_leaves= lm->n_leaves;
  int i;
  char field[FILEBUFFLEN], *fptr= field;
  Boolean local_ok= TRUE;
  Message_List *result = NULL;
  Message_List *current = NULL;

  /* see if our MACRO predicate requires a field */

  if (petal->predicate_type == pred_field ||
      petal->predicate_type == pred_dynamic_field) {
    /* extract the field */
    strcpy(field, petal->predicate);	/* "pred field" */
    fptr = strchr(field,(int) SPACECHAR) + 1;	/* skip the space */
  } else
    fptr = NULL;

  /* EXPAND our macro here one leaf at a time ... */

  for (i = 0; i < n_leaves; ++i, ++macro) {
    char pred[FILEBUFFLEN];		/* for expanded predicate */

    /* 
     * Contruct a search predicate with a field if necessary.
     * Our macros NEVER include them since they are dynamic 
     */

    strcpy(pred, macro->predicate);	/* get the MACRO predicate */
    if ((petal->predicate_type == pred_field ||
	 petal->predicate_type == pred_dynamic_field) &&
	macro->predicate_type == pred_field) { /* double check OK */
      strcat(pred, SPACESTR);		/* adjunct the FIELD */
      strcat(pred, fptr);
    }

    /*
     * Dispatch the search
     */

    free_message_list(mailbox->found,TRUE,FALSE);
    mailbox->found = NULL;

    local_ok = local_search(mailbox, parent, pred);
    if (local_ok == FALSE)
      remote_search(mailbox, parent, pred);

    /*
     * Save the current results.
     */

    current = mailbox->found;
    mailbox->found = NULL;

    /*
     * Now accumulate the results with any operands
     */

    if(macro->operator == NO_OP) {
      result = current;
      current = NULL;
    }
    else
      result = apply_op_to_operands(mailbox, macro->operator,
				    &result, &current);

  }    

  /* 
   * Our accumulated results are in "result".  We'll make it appear
   * as though this was a simple mailbox search, (results accumulated
   * into "mailbox->found") since that's what the dispatcher is expecting. 
   * We're done with the intermediate list. Free its memory.
   */

  free_message_list(current, TRUE, FALSE);
  mailbox->found = result;
  return;
}


#ifdef __STDC__
void remote_search(Mailbox *mailbox, Lview *parent, char *predicate)
#else
void remote_search(mailbox, parent, predicate)
     Mailbox *mailbox;
     Lview *parent;
     char *predicate;
#endif
{
  Message_List *result;

  free_message_list(mailbox->found,TRUE,FALSE);
  mailbox->found = NULL;

  mail_search(mailbox->mailstream,predicate);

  result = mailbox->found;
  mailbox->found = NULL;

  conjunct_and(mailbox,result,parent->message_list);
  free_message_list(result,TRUE,FALSE);
}

/*
 * Do the search on the given mail box 
 */

#ifdef __STDC__
void do_imap_mail_search(Mailbox *mailbox, Lview *parent, LEAF *leaf)
#else
void do_imap_mail_search(mailbox, parent, leaf)
     Mailbox *mailbox;
     Lview *parent;
     LEAF *leaf;
#endif
{
  Boolean local_ok = TRUE;

  /*
   * Some searches are dispatched to the server,
   * and others we do locally from our cache */

  free_message_list(mailbox->found,TRUE,FALSE);
  mailbox->found = NULL;

  local_ok = local_search(mailbox, parent, leaf->predicate);
  if (local_ok == FALSE)
    remote_search(mailbox, parent, leaf->predicate);
  return;
}


/*
 * Does a search on all search petals, and accumulates the results
 * into result 
 */

#ifdef __STDC__
Message_List *do_imap_search(Mailbox *mailbox, Lview *parent, LEAF *petal)
#else
Message_List *do_imap_search(mailbox, parent, petal)
     Mailbox *mailbox;
     Lview *parent;
     LEAF *petal;
#endif
{
  LEAF *leaf = petal;
  char *old_pred;
  char *new_pred;
  Message_List *current = NULL;
  Message_List *result = NULL;

  /* run down the petals accumulating the results of
   * each search */

  while (leaf) {
    LEAFMACRO *lm;

    new_pred = update_dynamic_predicates(leaf);

    if(new_pred) {
      /* Temporarily switch the predicates for the search */
      old_pred = leaf->predicate;
      leaf->predicate = new_pred;
    }
    else
      old_pred = NULL;

    if ((lm = macro_expansion_required(leaf)) != NIL)
      do_imap_macro_mail_search(mailbox, parent, lm, leaf);
    else
      do_imap_mail_search(mailbox, parent, leaf);

    if (old_pred) {
      fs_give((void **) &new_pred);
      leaf->predicate = old_pred;
    }

    /* If we have found something, hold on to it */

    current = mailbox->found;

    /* reset the search results */

    mailbox->found = NULL;

    /* Look for negation */

    if(leaf->atomic_not == TRUE) {
      negate_search(mailbox,current,parent->message_list);
      current = mailbox->found;
      mailbox->found = NULL;
    }      

    /* apply operation to the result, EG: a OR b */

    if(leaf->operator == NO_OP) {
      result = current;
      current = NULL;
    }
    else
      result = apply_op_to_operands(mailbox, leaf->operator,
				    &result, &current);

    leaf = leaf->next;
  }

  if(current)
    free_message_list(current,TRUE,FALSE);
  return(result);
}



#ifdef __STDC__
void set_leading_lan(void)
#else
void set_leading_lan()
#endif
{
  struct timeval tp;
  struct timezone tzp;
  char *date;

  gettimeofday(&tp, &tzp);
  date = ctime((time_t *) &tp.tv_sec);
  lan[0] = date[20];
  lan[1] = date[21];
  lan[2] = '\0';
}

#ifdef __STDC__
char *set_today(void)
#else
char *set_today()
#endif
{
  time_t horloge;
  struct tm *temps;
  struct timeval tp;
  struct timezone tzp;

  gettimeofday(&tp, &tzp);
  horloge =  (time_t)tp.tv_sec;
  temps = localtime(&horloge);
  sprintf(lheure, "\"%2d-%s-%s%2d\"", temps->tm_mday, 
	  mois[temps->tm_mon], lan, temps->tm_year);
  return lheure;
}


#ifdef __STDC__
char *set_hier(void)
#else
char *set_hier()
#endif
{
  time_t horloge;
  struct tm *temps;
  struct timeval tp;
  struct timezone tzp;

  gettimeofday(&tp, &tzp);
  horloge =  (time_t)tp.tv_sec;
  horloge -= SECONDS_PER_DAY;
  temps = localtime(&horloge);
  sprintf(lheure, "\"%2d-%s-%s%2d\"", temps->tm_mday, 
	  mois[temps->tm_mon], lan, temps->tm_year);
  return lheure;
}

#ifdef __STDC__
char *set_mois_dernier(void)
#else
char *set_mois_dernier()
#endif
{
  time_t horloge;
  struct tm *temps;
  struct timeval tp;
  struct timezone tzp;
  int days_last_month;

  gettimeofday(&tp, &tzp);
  horloge =  (time_t)tp.tv_sec;
  temps = localtime(&horloge);
  /* Take us to the debut of last month */
					/* THe debut of this month */
  horloge = horloge - ((temps->tm_mday - 1)* SECONDS_PER_DAY); 
  /* Our month tables begin with dec, and tm_mon is 0 - 11, ie, tm_mon + 1
   * indexes THIS MONTH in our tables, alors tm_mon indexes LAST MONTH: */
  days_last_month = ((temps->tm_year % 4) == 0) ? 
    leap_dpm[temps->tm_mon] : dpm[temps->tm_mon];
  horloge -= days_last_month * SECONDS_PER_DAY;
  temps = localtime(&horloge);
  sprintf(lheure, "\"%2d-%s-%s%2d\"", temps->tm_mday, 
	  mois[temps->tm_mon], lan, temps->tm_year);
  return lheure;
}

#ifdef __STDC__
char *set_mois_courant(void)
#else
char *set_mois_courant()
#endif
{
  time_t horloge;
  struct tm *temps;
  struct timeval tp;
  struct timezone tzp;

  gettimeofday(&tp, &tzp);
  horloge =  (time_t)tp.tv_sec;
  temps = localtime(&horloge);
  sprintf(lheure, "\" 1-%s-%s%2d\"", mois[temps->tm_mon], 
	  lan, temps->tm_year);
  return lheure;
}

#ifdef __STDC__
char *set_january(void)
#else
char *set_january()
#endif
{
  time_t horloge;
  struct tm *temps;
  struct timeval tp;
  struct timezone tzp;

  gettimeofday(&tp, &tzp);
  horloge =  (time_t)tp.tv_sec;
  temps = localtime(&horloge);
  sprintf(lheure, "\" 1-JAN-%s%2d\"", lan, temps->tm_year);
  return lheure;
}

#ifdef __STDC__
char *set_february(void)
#else
char *set_february()
#endif
{
  time_t horloge;
  struct tm *temps;
  struct timeval tp;
  struct timezone tzp;
  int year;

  gettimeofday(&tp, &tzp);
  horloge =  (time_t)tp.tv_sec;
  temps = localtime(&horloge);
  year = temps->tm_year;

  sprintf(lheure, "\" 1-FEB-%s%2d\"", lan, year);
  return lheure;
}

#ifdef __STDC__
char *set_march(void)
#else
char *set_march()
#endif
{
  time_t horloge;
  struct tm *temps;
  struct timeval tp;
  struct timezone tzp;
  int year;

  gettimeofday(&tp, &tzp);
  horloge =  (time_t)tp.tv_sec;
  temps = localtime(&horloge);
  year = temps->tm_year;

  sprintf(lheure, "\" 1-MAR-%s%2d\"", lan, year);
  return lheure;
}

#ifdef __STDC__
char *set_april(void)
#else
char *set_april()
#endif
{
  time_t horloge;
  struct tm *temps;
  struct timeval tp;
  struct timezone tzp;
  int year;

  gettimeofday(&tp, &tzp);
  horloge =  (time_t)tp.tv_sec;
  temps = localtime(&horloge);
  year = temps->tm_year;

  sprintf(lheure, "\" 1-APR-%s%2d\"", lan, year);
  return lheure;
}

#ifdef __STDC__
char *set_may(void)
#else
char *set_may()
#endif
{
  time_t horloge;
  struct tm *temps;
  struct timeval tp;
  struct timezone tzp;
  int year;

  gettimeofday(&tp, &tzp);
  horloge =  (time_t)tp.tv_sec;
  temps = localtime(&horloge);
  year = temps->tm_year;

  sprintf(lheure, "\" 1-MAY-%s%2d\"", lan, year);
  return lheure;
}

#ifdef __STDC__
char *set_june(void)
#else
char *set_june()
#endif
{
  time_t horloge;
  struct tm *temps;
  struct timeval tp;
  struct timezone tzp;
  int year;

  gettimeofday(&tp, &tzp);
  horloge =  (time_t)tp.tv_sec;
  temps = localtime(&horloge);
  year = temps->tm_year;

  sprintf(lheure, "\" 1-JUN-%s%2d\"", lan, year);
  return lheure;
}

#ifdef __STDC__
char *set_july(void)
#else
char *set_july()
#endif
{
  time_t horloge;
  struct tm *temps;
  struct timeval tp;
  struct timezone tzp;
  int year;

  gettimeofday(&tp, &tzp);
  horloge =  (time_t)tp.tv_sec;
  temps = localtime(&horloge);
  year = temps->tm_year;

  sprintf(lheure, "\" 1-JUL-%s%2d\"", lan, year);
  return lheure;
}

#ifdef __STDC__
char *set_august(void)
#else
char *set_august()
#endif
{
  time_t horloge;
  struct tm *temps;
  struct timeval tp;
  struct timezone tzp;
  int year;

  gettimeofday(&tp, &tzp);
  horloge =  (time_t)tp.tv_sec;
  temps = localtime(&horloge);
  year = temps->tm_year;

  sprintf(lheure, "\" 1-AUG-%s%2d\"", lan, year);
  return lheure;
}

#ifdef __STDC__
char *set_september(void)
#else
char *set_september()
#endif
{
  time_t horloge;
  struct tm *temps;
  struct timeval tp;
  struct timezone tzp;
  int year;

  gettimeofday(&tp, &tzp);
  horloge =  (time_t)tp.tv_sec;
  temps = localtime(&horloge);
  year = temps->tm_year;
  sprintf(lheure, "\" 1-SEP-%s%2d\"", lan, year);
  return lheure;
}

#ifdef __STDC__
char *set_october(void)
#else
char *set_october()
#endif
{
  time_t horloge;
  struct tm *temps;
  struct timeval tp;
  struct timezone tzp;
  int year;

  gettimeofday(&tp, &tzp);
  horloge =  (time_t)tp.tv_sec;
  temps = localtime(&horloge);
  year = temps->tm_year;

  sprintf(lheure, "\" 1-OCT-%s%2d\"", lan, year);
  return lheure;
}

#ifdef __STDC__
char *set_november(void)
#else
char *set_november()
#endif
{
  time_t horloge;
  struct tm *temps;
  struct timeval tp;
  struct timezone tzp;
  int year;

  gettimeofday(&tp, &tzp);
  horloge =  (time_t)tp.tv_sec;
  temps = localtime(&horloge);
  year = temps->tm_year;

  sprintf(lheure, "\" 1-NOV-%s%2d\"", lan, year);
  return lheure;
}

#ifdef __STDC__
char *set_december(void)
#else
char *set_december()
#endif
{
  time_t horloge;
  struct tm *temps;
  struct timeval tp;
  struct timezone tzp;
  int year;

  gettimeofday(&tp, &tzp);
  horloge =  (time_t)tp.tv_sec;
  temps = localtime(&horloge);
  year = temps->tm_year;

  sprintf(lheure, "\" 1-DEC-%s%2d\"", lan, year);
  return lheure;
}

#ifdef __STDC__
char *set_sunday(void)
#else
char *set_sunday()
#endif
{
  time_t horloge;
  struct tm *temps;
  struct timeval tp;
  struct timezone tzp;

  gettimeofday(&tp, &tzp);
  horloge =  (time_t)tp.tv_sec;
  temps = localtime(&horloge);
  horloge -= temps->tm_wday * SECONDS_PER_DAY;
  temps = localtime(&horloge);

  sprintf(lheure, "\"%2d-%s-%s%2d\"", temps->tm_mday, mois[temps->tm_mon],
	  lan, temps->tm_year);
  return lheure;
}

#ifdef __STDC__
char *set_monday(void)
#else
char *set_monday()
#endif
{
  time_t horloge;
  struct tm *temps;
  struct timeval tp;
  struct timezone tzp;

  gettimeofday(&tp, &tzp);
  horloge =  (time_t)tp.tv_sec;
  temps = localtime(&horloge);
  horloge -= (temps->tm_wday - 1) * SECONDS_PER_DAY;
  temps = localtime(&horloge);

  sprintf(lheure, "\"%2d-%s-%s%2d\"", temps->tm_mday, mois[temps->tm_mon],
	  lan, temps->tm_year);
  return lheure;
}

#ifdef __STDC__
char *set_tuesday(void)
#else
char *set_tuesday()
#endif
{
  time_t horloge;
  struct tm *temps;
  struct timeval tp;
  struct timezone tzp;

  gettimeofday(&tp, &tzp);
  horloge =  (time_t)tp.tv_sec;
  temps = localtime(&horloge);
  horloge -= (temps->tm_wday - 2) * SECONDS_PER_DAY;
  temps = localtime(&horloge);

  sprintf(lheure, "\"%2d-%s-%s%2d\"", temps->tm_mday, mois[temps->tm_mon],
	  lan, temps->tm_year);
  return lheure;
}

#ifdef __STDC__
char *set_wednesday(void)
#else
char *set_wednesday()
#endif
{
  time_t horloge;
  struct tm *temps;
  struct timeval tp;
  struct timezone tzp;

  gettimeofday(&tp, &tzp);
  horloge =  (time_t)tp.tv_sec;
  temps = localtime(&horloge);
  horloge -= (temps->tm_wday - 3) * SECONDS_PER_DAY;
  temps = localtime(&horloge);

  sprintf(lheure, "\"%2d-%s-%s%2d\"", temps->tm_mday, mois[temps->tm_mon],
	  lan, temps->tm_year);
  return lheure;
}

#ifdef __STDC__
char *set_thursday(void)
#else
char *set_thursday()
#endif
{
  time_t horloge;
  struct tm *temps;
  struct timeval tp;
  struct timezone tzp;

  gettimeofday(&tp, &tzp);
  horloge =  (time_t)tp.tv_sec;
  temps = localtime(&horloge);
  horloge -= (temps->tm_wday - 4) * SECONDS_PER_DAY;
  temps = localtime(&horloge);

  sprintf(lheure, "\"%2d-%s-%s%2d\"", temps->tm_mday, mois[temps->tm_mon],
	  lan, temps->tm_year);
  return lheure;
}

#ifdef __STDC__
char *set_friday(void)
#else
char *set_friday()
#endif
{
  time_t horloge;
  struct tm *temps;
  struct timeval tp;
  struct timezone tzp;

  gettimeofday(&tp, &tzp);
  horloge =  (time_t)tp.tv_sec;
  temps = localtime(&horloge);
  horloge -= (temps->tm_wday - 5) * SECONDS_PER_DAY;
  temps = localtime(&horloge);

  sprintf(lheure, "\"%2d-%s-%s%2d\"", temps->tm_mday, mois[temps->tm_mon],
	  lan, temps->tm_year);
  return lheure;
}

#ifdef __STDC__
char *set_saturday(void)
#else
char *set_saturday()
#endif
{
  time_t horloge;
  struct tm *temps;
  struct timeval tp;
  struct timezone tzp;

  gettimeofday(&tp, &tzp);
  horloge =  (time_t)tp.tv_sec;
  temps = localtime(&horloge);
  horloge -= (temps->tm_wday - 6) * SECONDS_PER_DAY;
  temps = localtime(&horloge);

  sprintf(lheure, "\"%2d-%s-%s%2d\"", temps->tm_mday, mois[temps->tm_mon],
	  lan, temps->tm_year);
  return lheure;
}

#ifdef __STDC__
char *set_lastsunday(void)
#else
char *set_lastsunday()
#endif
{
  time_t horloge;
  struct tm *temps;
  struct timeval tp;
  struct timezone tzp;

  gettimeofday(&tp, &tzp);
  horloge =  (time_t)tp.tv_sec;
  temps = localtime(&horloge);

  horloge = horloge - (temps->tm_wday * SECONDS_PER_DAY)
    - (7 * SECONDS_PER_DAY); 
  temps = localtime(&horloge);
  sprintf(lheure, "\"%2d-%s-%s%2d\"", temps->tm_mday, 
	  mois[temps->tm_mon], lan, temps->tm_year);
  return lheure;
}

#ifdef __STDC__
char *set_lastmonday(void)
#else
char *set_lastmonday()
#endif
{
  time_t horloge;
  struct tm *temps;
  struct timeval tp;
  struct timezone tzp;

  gettimeofday(&tp, &tzp);
  horloge =  (time_t)tp.tv_sec;
  temps = localtime(&horloge);

  horloge = horloge - (temps->tm_wday * SECONDS_PER_DAY)
    - (6 * SECONDS_PER_DAY); 
  temps = localtime(&horloge);
  sprintf(lheure, "\"%2d-%s-%s%2d\"", temps->tm_mday, 
	  mois[temps->tm_mon], lan, temps->tm_year);
  return lheure;
}

#ifdef __STDC__
char *set_lasttuesday(void)
#else
char *set_lasttuesday()
#endif
{
  time_t horloge;
  struct tm *temps;
  struct timeval tp;
  struct timezone tzp;

  gettimeofday(&tp, &tzp);
  horloge =  (time_t)tp.tv_sec;
  temps = localtime(&horloge);

  horloge = horloge - (temps->tm_wday * SECONDS_PER_DAY)
    - (5 * SECONDS_PER_DAY); 
  temps = localtime(&horloge);
  sprintf(lheure, "\"%2d-%s-%s%2d\"", temps->tm_mday, 
	  mois[temps->tm_mon], lan, temps->tm_year);
  return lheure;
}

#ifdef __STDC__
char *set_lastwednesday(void)
#else
char *set_lastwednesday()
#endif
{
  time_t horloge;
  struct tm *temps;
  struct timeval tp;
  struct timezone tzp;

  gettimeofday(&tp, &tzp);
  horloge =  (time_t)tp.tv_sec;
  temps = localtime(&horloge);

  horloge = horloge - (temps->tm_wday * SECONDS_PER_DAY)
    - (4 * SECONDS_PER_DAY); 
  temps = localtime(&horloge);
  sprintf(lheure, "\"%2d-%s-%s%2d\"", temps->tm_mday, 
	  mois[temps->tm_mon], lan, temps->tm_year);
  return lheure;
}

#ifdef __STDC__
char *set_lastthursday(void)
#else
char *set_lastthursday()
#endif
{
  time_t horloge;
  struct tm *temps;
  struct timeval tp;
  struct timezone tzp;

  gettimeofday(&tp, &tzp);
  horloge =  (time_t)tp.tv_sec;
  temps = localtime(&horloge);

  horloge = horloge - (temps->tm_wday * SECONDS_PER_DAY)
    - (3 * SECONDS_PER_DAY); 
  temps = localtime(&horloge);
  sprintf(lheure, "\"%2d-%s-%s%2d\"", temps->tm_mday, 
	  mois[temps->tm_mon], lan, temps->tm_year);
  return lheure;
}

#ifdef __STDC__
char *set_lastfriday(void)
#else
char *set_lastfriday()
#endif
{
  time_t horloge;
  struct tm *temps;
  struct timeval tp;
  struct timezone tzp;

  gettimeofday(&tp, &tzp);
  horloge =  (time_t)tp.tv_sec;
  temps = localtime(&horloge);

  horloge = horloge - (temps->tm_wday * SECONDS_PER_DAY)
    - (2 * SECONDS_PER_DAY); 
  temps = localtime(&horloge);
  sprintf(lheure, "\"%2d-%s-%s%2d\"", temps->tm_mday, 
	  mois[temps->tm_mon], lan, temps->tm_year);
  return lheure;
}

#ifdef __STDC__
char *set_lastsaturday(void)
#else
char *set_lastsaturday()
#endif
{
  time_t horloge;
  struct tm *temps;
  struct timeval tp;
  struct timezone tzp;

  gettimeofday(&tp, &tzp);
  horloge =  (time_t)tp.tv_sec;
  temps = localtime(&horloge);

  horloge = horloge - (temps->tm_wday * SECONDS_PER_DAY)
    - (1 * SECONDS_PER_DAY); 
  temps = localtime(&horloge);
  sprintf(lheure, "\"%2d-%s-%s%2d\"", temps->tm_mday, 
	  mois[temps->tm_mon], lan, temps->tm_year);
  return lheure;
}

#ifdef __STDC__
void conjunct_and(Mailbox *mailbox, Message_List *list1, Message_List *list2)
#else
void conjunct_and(mailbox,list1, list2)
     Mailbox *mailbox;
     Message_List *list1;
     Message_List *list2;
#endif
{
  Message_List *ptr1;
  Message_List *ptr2;
  Boolean found_it = FALSE;

  free_message_list(mailbox->found, TRUE, FALSE); 
  mailbox->found = NULL;

  for(ptr1 = list1; ptr1; ptr1 = ptr1->next) {
    found_it = FALSE;
    for(ptr2 = list2; ptr2; ptr2 = ptr2->next) {
      if(ptr1->message == ptr2->message) {
	found_it = TRUE;
	break;
      }
    }
    if(found_it == TRUE)
      mm_searched(mailbox->mailstream,ptr1->message->msgno);
  }
  return;
}

#ifdef __STDC__
void conjunct_or(Mailbox *mailbox, Message_List *list1, Message_List *list2)
#else
void conjunct_or(mailbox,list1, list2)
     Mailbox *mailbox;
     Message_List *list1;
     Message_List *list2;
#endif
{
  Message_List *ptr;
  Message_List *ptr2;

  /*
   * Not easy. First we have to combine the two lists into an intermediate
   * list, and then weed out duplicates. To do this, efficiently,
   * we sort the intermediate list, since there may be no logical ordering.
   */
  
  free_message_list(mailbox->found, TRUE, FALSE); 
  mailbox->found = NULL;

  for(ptr = list1; ptr; ptr = ptr->next)
    mm_searched(mailbox->mailstream,ptr->message->msgno);
  for(ptr = list2; ptr; ptr = ptr->next)
    mm_searched(mailbox->mailstream,ptr->message->msgno);

  ptr = sort_message_list(mailbox->found,REP_ASCENDING);
  mailbox->found = NULL;
  for(ptr2 = ptr; ptr2; ptr2 = ptr2->next) {
    if((ptr2->next != NULL) 
       && (ptr2->message != NULL)
       && (ptr2->next->message != NULL)
       && (ptr2->next->message->msgno == ptr2->message->msgno))
      ptr2 = ptr2->next;
    if(ptr2->message)
      mm_searched(mailbox->mailstream,ptr2->message->msgno);
  }
  free_message_list(ptr,TRUE,FALSE);
  return;
}

#ifdef __STDC__
void negate_search(Mailbox *mailbox, Message_List *list1, Message_List *list2)
#else
void negate_search(mailbox,list1,list2)
     Mailbox *mailbox;
     Message_List *list1;
     Message_List *list2;
#endif
{
  Message_List *ptr;
  Message_List *message_list;
  Boolean found_it = FALSE;

  for(message_list = list2;
      message_list; message_list = message_list->next) {
    found_it = FALSE;
    for(ptr = list1; ptr; ptr = ptr->next) {
      if(ptr->message == message_list->message) {
	found_it = TRUE;
	break;
      }
    }
    if(found_it == FALSE)
	mm_searched(mailbox->mailstream, message_list->message->msgno);
  }
  free_message_list(list1,TRUE,FALSE);
  return;
}


/*
 * Each of the functions below return TRUE unless the search should
 * be defaulted to the server. 
 */

#ifdef __STDC__
Boolean local_new_search(Mailbox *mailbox, Message_List *parent)
#else
Boolean local_new_search(mailbox,parent)
     Mailbox *mailbox;
     Message_List *parent;
#endif
{
  Message_List *message_list;
  MESSAGECACHE *elt;

  for(message_list = parent; message_list; message_list = message_list->next) {
    if(message_list->message && message_list->message->longcache) {
      elt = &(message_list->message->longcache->elt);
      if((! elt->seen) && elt->recent) 
	mm_searched(mailbox->mailstream, message_list->message->msgno);
    }
  }
  return(TRUE);
}

/*
 * Do a search on the mailbox cache for deleted messages 
 */

#ifdef __STDC__
Boolean local_deleted_search(Mailbox *mailbox, Message_List *parent)
#else
Boolean local_deleted_search(mailbox,parent)
     Mailbox *mailbox;
     Message_List *parent;
#endif
{
  Message_List *message_list;
  MESSAGECACHE *elt;

  for(message_list = parent; message_list; message_list = message_list->next) {
    if(message_list->message && message_list->message->longcache) {
      elt = &(message_list->message->longcache->elt);
      if(elt->deleted)
	mm_searched(mailbox->mailstream, message_list->message->msgno);
    }
  }
  return TRUE;
}

#ifdef __STDC__
Boolean local_undeleted_search(Mailbox *mailbox, Message_List *parent)
#else
Boolean local_undeleted_search(mailbox,parent)
     Mailbox *mailbox;
     Message_List *parent;
#endif
{
  Message_List *message_list;
  MESSAGECACHE *elt;

  for(message_list = parent; message_list; message_list = message_list->next) {
    if(message_list->message && message_list->message->longcache) {
      elt = &(message_list->message->longcache->elt);
      if(! elt->deleted)
	mm_searched(mailbox->mailstream, message_list->message->msgno);
    }
  }
  return TRUE;
}

#ifdef __STDC__
Boolean local_flagged_search(Mailbox *mailbox, Message_List *parent)
#else
Boolean local_flagged_search(mailbox,parent)
     Mailbox *mailbox;
     Message_List *parent;
#endif
{
  Message_List *message_list;
  MESSAGECACHE *elt;

  for(message_list = parent; message_list; message_list = message_list->next) {
    if(message_list->message && message_list->message->longcache) {
      elt = &(message_list->message->longcache->elt);
      if(elt->flagged)
	mm_searched(mailbox->mailstream, message_list->message->msgno);
    }
  }
  return TRUE;
}

#ifdef __STDC__
Boolean local_unflagged_search(Mailbox *mailbox, Message_List *parent)
#else
Boolean local_unflagged_search(mailbox,parent)
     Mailbox *mailbox;
     Message_List *parent;
#endif
{
  Message_List *message_list;
  MESSAGECACHE *elt;

  for(message_list = parent; message_list; message_list = message_list->next) {
    if(message_list->message && message_list->message->longcache) {
      elt = &(message_list->message->longcache->elt);
      if(! elt->flagged)
	mm_searched(mailbox->mailstream, message_list->message->msgno);
    }
  }
  return TRUE;
}

#ifdef __STDC__
Boolean local_seen_search(Mailbox *mailbox, Message_List *parent)
#else
Boolean local_seen_search(mailbox,parent)
     Mailbox *mailbox;
     Message_List *parent;
#endif
{
  Message_List *message_list;
  MESSAGECACHE *elt;

  for(message_list = parent; message_list; message_list = message_list->next) {
    if(message_list->message && message_list->message->longcache) {
      elt = &(message_list->message->longcache->elt);
      if(elt->seen)
	mm_searched(mailbox->mailstream, message_list->message->msgno);
    }
  }
  return TRUE;
}

#ifdef __STDC__
Boolean local_unseen_search(Mailbox *mailbox, Message_List *parent)
#else
Boolean local_unseen_search(mailbox,parent)
     Mailbox *mailbox;
     Message_List *parent;
#endif
{
  Message_List *message_list;
  MESSAGECACHE *elt;

  for(message_list = parent; message_list; message_list = message_list->next) {
    if(message_list->message && message_list->message->longcache) {
      elt = &(message_list->message->longcache->elt);
      if(! elt->seen)
	mm_searched(mailbox->mailstream, message_list->message->msgno);
    }
  }
  return TRUE;
}

#ifdef __STDC__
Boolean local_answered_search(Mailbox *mailbox, Message_List *parent)
#else
Boolean local_answered_search(mailbox,parent)
     Mailbox *mailbox;
     Message_List *parent;
#endif
{
  Message_List *message_list;
  MESSAGECACHE *elt;

  for(message_list = parent; message_list; message_list = message_list->next) {
    if(message_list->message && message_list->message->longcache) {
      elt = &(message_list->message->longcache->elt);
      if(elt->answered)
	mm_searched(mailbox->mailstream, message_list->message->msgno);
    }
  }
  return TRUE;
}

#ifdef __STDC__
Boolean local_unanswered_search(Mailbox *mailbox, Message_List *parent)
#else
Boolean local_unanswered_search(mailbox,parent)
     Mailbox *mailbox;
     Message_List *parent;
#endif
{
  Message_List *message_list;
  MESSAGECACHE *elt;

  for(message_list = parent; message_list; message_list = message_list->next) {
    if(message_list->message && message_list->message->longcache) {
      elt = &(message_list->message->longcache->elt);
      if(! elt->answered)
	mm_searched(mailbox->mailstream, message_list->message->msgno);
    }
  }
  return TRUE;
}

#ifdef __STDC__
Boolean local_recent_search(Mailbox *mailbox, Message_List *parent)
#else
Boolean local_recent_search(mailbox,parent)
     Mailbox *mailbox;
     Message_List *parent;
#endif
{
  Message_List *message_list;
  MESSAGECACHE *elt;

  for(message_list = parent; message_list; message_list = message_list->next) {
    if(message_list->message && message_list->message->longcache) {
      elt = &(message_list->message->longcache->elt);
      if(elt->recent)
	mm_searched(mailbox->mailstream, message_list->message->msgno);
    }
  }
  return TRUE;
}

#ifdef __STDC__
Boolean local_old_search(Mailbox *mailbox, Message_List *parent)
#else
Boolean local_old_search(mailbox,parent)
     Mailbox *mailbox;
     Message_List *parent;
#endif
{
  Message_List *message_list;
  MESSAGECACHE *elt;

  for(message_list = parent; message_list; message_list = message_list->next) {
    if(message_list->message && message_list->message->longcache) {
      elt = &(message_list->message->longcache->elt);
      if(! elt->recent)
	mm_searched(mailbox->mailstream, message_list->message->msgno);
    }
  }
  return TRUE;
}


/*
 * Here we have the text searches:
 *    SUBJECT
 *    FROM
 *    TEXT
 *    BODY
 *    HEADER
 *    TO
 *    CC
 *    REPLY-TO
 *    SENDER
 *    MESSAGE-ID
 *    IN-REPLY-TO
 *    REPLY-TO
 *    return-path
 *    remail
 *    > n     if rfc822_size > n, then true ...
 */


#ifdef __STDC__
Boolean local_subject_search(Mailbox *mailbox, Message_List *parent, 
			     char *pattern)
#else
Boolean local_subject_search(mailbox,parent,pattern)
     Mailbox *mailbox;
     Message_List *parent;
     char *pattern;
#endif
{
  Message_List *message_list;
  char *subject;
  int len;

  for(message_list = parent; message_list; message_list = message_list->next) {
    if(message_list->message) {
      subject = message_list->message->subject;
      if(subject) {
	len = strlen(subject);
	if((len) && (search(subject, len, pattern, strlen(pattern))))
	  mm_searched(mailbox->mailstream, message_list->message->msgno);
      }
    }
  }
  return TRUE;
}


#ifdef __STDC__
Boolean local_body_search(Mailbox *mailbox, Message_List *parent, 
			  char *pattern)
#else
Boolean local_body_search(mailbox,parent,pattern)
     Mailbox *mailbox;
     Message_List *parent;
     char *pattern;
#endif
{
  return(FALSE);
}


#ifdef __STDC__
Boolean local_header_search(Mailbox *mailbox, Message_List *parent, 
			    char *pattern)
#else
Boolean local_header_search(mailbox,parent,pattern)
     Mailbox *mailbox;
     Message_List *parent;
     char *pattern;
#endif
{
  return(FALSE);
}

#ifdef __STDC__
Boolean local_text_search(Mailbox *mailbox, Message_List *parent, 
			  char *pattern)
#else
Boolean local_text_search(mailbox,parent,pattern)
     Mailbox *mailbox;
     Message_List *parent;
     char *pattern;
#endif
{
  return(FALSE);
}

#ifdef __STDC__
Boolean local_msgid_search(Mailbox *mailbox, Message_List *parent, 
			   char *pattern)
#else
Boolean local_msgid_search(mailbox,parent,pattern)
     Mailbox *mailbox;
     Message_List *parent;
     char *pattern;
#endif
{
  Message_List *message_list;
  char *msgid;

  for(message_list = parent; message_list; message_list = message_list->next) {
    if((message_list->message) && (message_list->message->envelope)) {
      msgid = message_list->message->envelope->message_id;
      if((msgid) && (strlen(msgid)) 
	 && search(msgid,strlen(msgid),pattern,strlen(pattern)))
	mm_searched(mailbox->mailstream, message_list->message->msgno);
    }
  }
  return TRUE;
}

#ifdef __STDC__
Boolean local_size_plus_grand_que(Mailbox *mailbox, Message_List *parent, 
				  char *pattern)
#else
Boolean local_size_plus_grand_que(mailbox,parent,pattern)
     Mailbox *mailbox;
     Message_List *parent;
     char *pattern;
#endif
{
  Message_List *message_list;
  MESSAGECACHE *elt;
  unsigned long size = (unsigned long) atol(pattern);
  
  for(message_list = parent; message_list; message_list = message_list->next) {
    if(message_list->message && message_list->message->longcache) {
      elt = &(message_list->message->longcache->elt);
      if((elt) && (elt->rfc822_size > size))
	mm_searched(mailbox->mailstream, message_list->message->msgno);
    }
  }
  return TRUE;
}

#ifdef __STDC__
Boolean local_inreplyto_search(Mailbox *mailbox, Message_List *parent, 
			       char *pattern)
#else
Boolean local_inreplyto_search(mailbox,parent,pattern)
     Mailbox *mailbox;
     Message_List *parent;
     char *pattern;
#endif
{
  Message_List *message_list;
  char *in_reply_to;

  for(message_list = parent; message_list; message_list = message_list->next) {
    if(message_list->message && message_list->message->envelope) {
      in_reply_to = message_list->message->envelope->in_reply_to;
      if((in_reply_to) && (strlen(in_reply_to))
	 && search(in_reply_to,strlen(in_reply_to), pattern,strlen(pattern)))
	mm_searched(mailbox->mailstream, message_list->message->msgno);
    }
  }
  return TRUE;
}

#ifdef __STDC__
Boolean local_on_search(Mailbox *mailbox, Message_List *parent, char *pattern)
#else
Boolean local_on_search(mailbox,parent,pattern)
     Mailbox *mailbox;
     Message_List *parent;
     char *pattern;
#endif
{
  Message_List *message_list;
  unsigned long date, msg_date;
  MESSAGECACHE *elt;
  MESSAGECACHE tmp_elt;

  if (!mail_parse_date (&tmp_elt, pattern))
    return(FALSE);
  date = (tmp_elt.year << 9) + (tmp_elt.month << 5) + tmp_elt.day;

  for(message_list = parent; message_list; message_list = message_list->next) {
    if(message_list->message && message_list->message->longcache) {
      elt = &(message_list->message->longcache->elt);
      msg_date = (elt->year << 9) + (elt->month << 5) + elt->day;
      if (msg_date == date) 		/* message on the date */
	mm_searched(mailbox->mailstream, message_list->message->msgno);
    }
  }
  return TRUE;
}

#ifdef __STDC__
Boolean local_since_search(Mailbox *mailbox, Message_List *parent, 
			   char *pattern)
#else
Boolean local_since_search(mailbox,parent,pattern)
     Mailbox *mailbox;
     Message_List *parent;
     char *pattern;
#endif
{
  Message_List *message_list;
  unsigned long date, msg_date;
  MESSAGECACHE *elt;
  MESSAGECACHE tmp_elt;

  if (!mail_parse_date (&tmp_elt, pattern))
    return(TRUE);
  date = (tmp_elt.year << 9) + (tmp_elt.month << 5) + tmp_elt.day;
  
  for(message_list = parent; message_list; message_list = message_list->next) {
    if(message_list->message && message_list->message->longcache) {
      elt = &(message_list->message->longcache->elt);
      msg_date = (elt->year << 9) + (elt->month << 5) + elt->day;
      if (msg_date >= date) 
	mm_searched(mailbox->mailstream, message_list->message->msgno);
    }
  }
  return TRUE;
}

#ifdef __STDC__
Boolean local_before_search(Mailbox *mailbox, Message_List *parent, 
			    char *pattern)
#else
Boolean local_before_search(mailbox,parent,pattern)
     Mailbox *mailbox;
     Message_List *parent;
     char *pattern;
#endif
{
  Message_List *message_list;
  unsigned long date, msg_date;
  MESSAGECACHE *elt;
  MESSAGECACHE tmp_elt;

  if (!mail_parse_date (&tmp_elt, pattern))
    return(TRUE);
  date = (tmp_elt.year << 9) + (tmp_elt.month << 5) + tmp_elt.day;

  for(message_list = parent; message_list; message_list = message_list->next) {
    if(message_list->message && message_list->message->longcache) {
      elt = &(message_list->message->longcache->elt);
      msg_date = (elt->year << 9) + (elt->month << 5) + elt->day;
      if (msg_date < date) 
	mm_searched(mailbox->mailstream, message_list->message->msgno);
    }
  }
  return TRUE;
}

#ifdef __STDC__
Boolean local_from_search(Mailbox *mailbox, Message_List *parent, 
			  char *pattern)
#else
Boolean local_from_search(mailbox,parent,pattern)
     Mailbox *mailbox;
     Message_List *parent;
     char *pattern;
#endif
{
  Message_List *message_list;
  char addr[FILEBUFFLEN];
  ADDRESS *base;
  int len;

  for(message_list = parent; message_list; message_list = message_list->next) {
    if(message_list->message && message_list->message->envelope) {
      base = message_list->message->envelope->from;
      local_make_addr_str(base,addr);
      len = strlen(addr);
      if((len) && (search(addr,len,pattern,strlen(pattern)))) 
	mm_searched(mailbox->mailstream, message_list->message->msgno);
    }
  }
  return(TRUE);
}

#ifdef __STDC__
Boolean local_to_search(Mailbox *mailbox, Message_List *parent, char *pattern)
#else
Boolean local_to_search(mailbox,parent,pattern)
     Mailbox *mailbox;
     Message_List *parent;
     char *pattern;
#endif
{
  Message_List *message_list;
  char addr[FILEBUFFLEN];
  ADDRESS *base;
  int len;

  for(message_list = parent; message_list; message_list = message_list->next) {
    if(message_list->message && message_list->message->envelope) {
      base = message_list->message->envelope->to;
      while(base) {
	local_make_addr_str(base,addr);
	len = strlen(addr);
	if((len) && (search(addr,len,pattern,strlen(pattern)))) {
	  mm_searched(mailbox->mailstream, message_list->message->msgno);
	  break;
	}
	else
	  base = base->next;
      }
    }
  }
  return(TRUE);
}

#ifdef __STDC__
Boolean local_cc_search(Mailbox *mailbox, Message_List *parent, char *pattern)
#else
Boolean local_cc_search(mailbox,parent,pattern)
     Mailbox *mailbox;
     Message_List *parent;
     char *pattern;
#endif
{
  Message_List *message_list;
  char addr[FILEBUFFLEN];
  ADDRESS *base;
  int len;

  for(message_list = parent; message_list; message_list = message_list->next) {
    if(message_list->message && message_list->message->envelope) {
      base = message_list->message->envelope->cc;
      while(base) {
	local_make_addr_str(base,addr);
	len = strlen(addr);
	if((len) && (search(addr,len,pattern,strlen(pattern)))) {
	  mm_searched(mailbox->mailstream, message_list->message->msgno);
	  break;
	}
	else
	  base = base->next;
      }
    }
  }
  return(TRUE);
}

#ifdef __STDC__
Boolean local_reply_to_search(Mailbox *mailbox, Message_List *parent, 
			      char *pattern)
#else
Boolean local_reply_to_search(mailbox,parent,pattern)
     Mailbox *mailbox;
     Message_List *parent;
     char *pattern;
#endif
{
  Message_List *message_list;
  char addr[FILEBUFFLEN];
  ADDRESS *base;
  int len;

  for(message_list = parent; message_list; message_list = message_list->next) {
    if(message_list->message && message_list->message->envelope) {
      base = message_list->message->envelope->reply_to;
      while(base) {
	local_make_addr_str(base,addr);
	len = strlen(addr);
	if((len) && (search(addr,len,pattern,strlen(pattern)))) {
	  mm_searched(mailbox->mailstream, message_list->message->msgno);
	  break;
	}
	else
	  base = base->next;
      }
    }
  }
  return(TRUE);
}

#ifdef __STDC__
Boolean local_sender_search(Mailbox *mailbox, Message_List *parent, 
			    char *pattern)
#else
Boolean local_sender_search(mailbox,parent,pattern)
     Mailbox *mailbox;
     Message_List *parent;
     char *pattern;
#endif
{
  Message_List *message_list;
  char addr[FILEBUFFLEN];
  ADDRESS *base;
  int len;

  for(message_list = parent; message_list; message_list = message_list->next) {
    if(message_list->message && message_list->message->envelope) {
      base = message_list->message->envelope->sender;
      while(base) {
	local_make_addr_str(base,addr);
	len = strlen(addr);
	if((len) && (search(addr,len,pattern,strlen(pattern)))) {
	  mm_searched(mailbox->mailstream, message_list->message->msgno);
	  break;
	}
	else
	  base = base->next;
      }
    }
  }
  return(TRUE);
}


#ifdef __STDC__
Boolean local_replyto_search(Mailbox *mailbox, Message_List *parent, 
			     char *pattern)
#else
Boolean local_replyto_search(mailbox,parent,pattern)
     Mailbox *mailbox;
     Message_List *parent;
     char *pattern;
#endif
{
  Message_List *message_list;
  char addr[FILEBUFFLEN];
  ADDRESS *base;
  int len;

  for(message_list = parent; message_list; message_list = message_list->next) {
    if(message_list->message && message_list->message->envelope) {
      base = message_list->message->envelope->reply_to;
      while(base) {
	local_make_addr_str(base,addr);
	len = strlen(addr);
	if((len) && (search(addr,len,pattern,strlen(pattern)))) {
	  mm_searched(mailbox->mailstream, message_list->message->msgno);
	  break;
	}
	else
	  base = base->next;
      }
    }
  }
  return(TRUE);
}


/*
 * IF the predicate type requires a dynamic field, ie, one whose
 * value is set at search time, then set its value. We remake the
 * predicate/field pair, and return it to be used in search 
 */


DYNAMIC dynamic_table[]= {
  { 
    NULL,
    XtNfilterToday,
    "TODAY",
    set_today },

  { 
    NULL, 
    XtNfilterYesterday,
    "YESTERDAY",
    set_hier },

  { 
    NULL, 
    XtNfilterLastMonth,
    "LASTMONTH",
    set_mois_dernier },

  { 
    NULL, 
    XtNfilterThisMonth,
    "THISMONTH",
    set_mois_courant },

  { 
    NULL, 
    XtNfilterJanuary,
    "JANUARY",
    set_january },

  { 
    NULL, 
    XtNfilterFebruary,
    "FEBRUARY",
    set_february },

  { 
    NULL, 
    XtNfilterMarch,
    "MARCH",
    set_march },

  { 
    NULL, 
    XtNfilterApril,
    "APRIL",
    set_april },

  { 
    NULL, 
    XtNfilterMay,
    "MAY",
    set_may },

  { 
    NULL, 
    XtNfilterJune,
    "JUNE",
    set_june },

  { 
    NULL, 
    XtNfilterJuly,
    "JULY",
    set_july },

  { 
    NULL, 
    XtNfilterAugust,
    "AUGUST",
    set_august },

  { 
    NULL,
    XtNfilterSeptember,
    "SEPTEMBER",
    set_september },

  { 
    NULL, 
    XtNfilterOctober,
    "OCTOBER",
    set_october },

  { 
    NULL, 
    XtNfilterNovember,
    "NOVEMBER",
    set_november },

  { 
    NULL, 
    XtNfilterDecember,
    "DECEMBER",
    set_december },

  { 
    NULL, 
    XtNfilterSunday,
    "SUNDAY",
    set_sunday },

  { 
    NULL,
    XtNfilterMonday,
    "MONDAY",
    set_monday },

  { 
    NULL,
    XtNfilterTuesday,
    "TUESDAY",
    set_tuesday },

  { 
    NULL,
    XtNfilterWednesday,
    "WEDNESDAY",
    set_wednesday },

  { 
    NULL, 
    XtNfilterThursday,
    "THURSDAY",
    set_thursday },

  { 
    NULL,
    XtNfilterFriday,
    "FRIDAY",
    set_friday },

  { 
    NULL,
    XtNfilterSaturday,
    "SATURDAY",
    set_saturday },

  { 
    NULL,
    XtNfilterLastSunday,
    "LASTSUNDAY",
    set_lastsunday },

  { 
    NULL,
    XtNfilterLastMonday,
    "LASTMONDAY",
    set_lastmonday },

  { 
    NULL,
    XtNfilterLastTuesday,
    "LASTTUESDAY",
    set_lasttuesday },

  { 
    NULL, 
    XtNfilterLastWednesday,
    "LASTWEDNESDAY",
    set_lastwednesday },

  { 
    NULL,
    XtNfilterLastThursday,
    "LASTTHURSDAY",
    set_lastthursday },

  { 
    NULL,
    XtNfilterLastFriday,
    "LASTFRIDAY",
    set_lastfriday },

  { 
    NULL,
    XtNfilterLastSaturday,
    "LASTSATURDAY",
    set_lastsaturday },

  { NULL, NULL, NULL, NULL }
};


/* Simple search dispatchers (no args). */

SEARCHERS local_chercheurs_simple[]= {
  { "NEW",                  local_new_search          },
  { "OLD",                  local_old_search          }, 
  { "DELETED",              local_deleted_search      },
  { "UNDELETED",            local_undeleted_search    },
  { "SEEN",                 local_seen_search         },
  { "UNSEEN",               local_unseen_search       },
  { "RECENT",               local_recent_search       },
  { "ANSWERED",             local_answered_search     },
  { "UNANSWERED",           local_unanswered_search   },
  { "FLAGGED",              local_flagged_search      },
  { "UNFLAGGED",            local_unflagged_search    },
  { NULL,                   NULL                      }
};

/* These require a search pattern */

SEARCHERS local_chercheurs_grands[] = {
  { "SUBJECT",              local_subject_search       },
  { "FROM",                 local_from_search          },
  { "TO",                   local_to_search            },
  { "CC",                   local_cc_search            },
  { "ON",                   local_on_search            },
  { "SINCE",                local_since_search         },
  { "BEFORE",               local_before_search        },
  { "MESSAGE-ID",           local_msgid_search         },
  { "SENDER",               local_sender_search        },
  { "IN-REPLY-TO",          local_inreplyto_search     },
  { "LARGER",               local_size_plus_grand_que  },
  { "HEADER",               local_header_search        },
  { "TEXT",                 local_text_search          },
  { "BODY",                 local_body_search          },
  { NULL,                   NULL                       }
};

#ifdef __STDC__
char *update_dynamic_predicates(LEAF *leaf)
#else
char *update_dynamic_predicates(leaf)
     LEAF *leaf;
#endif
{
  char tmp[FILEBUFFLEN];
  char unquoted[FILEBUFFLEN];
  char *field;
  char *dynamic_value;
  DYNAMIC *dyn;
  int len;
  char *nouveau;

  if (leaf->predicate_type != pred_dynamic_field)
    return NULL;

  /* Isolate the field */

  field = strchr(leaf->predicate,(int) SPACECHAR) + 1;
  if(*field == DQUOTECHAR)
    field ++;
  strcpy(unquoted,field);
  if(unquoted[strlen(unquoted) - 1] == DQUOTECHAR)
    unquoted[strlen(unquoted) - 1] = NUL_TERM;


  /* dispatch on field value */

  for (dynamic_value = NULL, dyn = dynamic_table; dyn->value; ++dyn) {
    if (strcasecmp(unquoted, dyn->localized) == STRMATCH) {
      dynamic_value = (*dyn->set_dynamic)();
      break;
    }
  }

  if (!dynamic_value) {
    /* Dynamic values NOT required in dynamic fields */
    return NULL;
  }

  /*
   * Remake predicate with dynamic value and protocol verb (not localized). 
   */

  strcpy(tmp, leaf->predicate);
  strtok(tmp, " ");			/* NULL terminate predicate */
  len = strlen(tmp) + strlen(dynamic_value) + 2; /* SPACE and NULL too */
  nouveau = (char *)fs_get(len);
  sprintf(nouveau, "%s %s", tmp, dynamic_value);
  return nouveau;
}

/*
 * The dispatcher 
 */

#ifdef __STDC__
void local_select_field(char *str, char *obuf)
#else
void local_select_field(str, obuf)
     char *str;
     char *obuf;
#endif
{

   /* clear any leading SPACES */
   while (*str == SPACECHAR) 
     ++str;
 
   /* Now a possibly leading \" */
   if (*str && *str == DQUOTECHAR) 
     ++str;
 
   /* copy the rest into out output buffer */

  strcpy(obuf, str);
  /* now step on the last " */
  if(obuf[strlen(obuf) - 1] == DQUOTECHAR)
    obuf[strlen(obuf) - 1] = NUL_TERM;
  return;
}

#ifdef __STDC__
Boolean local_search(Mailbox *mailbox, Lview *lview, char *predicate)
#else
Boolean local_search(mailbox, lview,  predicate)
     Mailbox *mailbox;
     Lview *lview;
     char *predicate;
#endif
{
  char token[PARSEBUFLEN];
  char field[PARSEBUFLEN];

  char *chemise= token;
  SEARCHERS *chs;
  SEARCHERS *gchs;

  /* strip off the chemise */
  while (*predicate && *predicate != SPACECHAR)
    *chemise++ = *predicate++;
  *chemise = NUL_TERM;
  
  /* OK, find the local chercheur */
  for (chs = local_chercheurs_simple; chs->token; ++chs) {
    if (strcasecmp(chs->token, token) == STRMATCH) {
      return (*chs->chercheur)(mailbox, lview->message_list);
    }
  }
  for (gchs = local_chercheurs_grands; gchs->token; ++gchs) {
    if (strcasecmp(gchs->token, token) == STRMATCH) {
      local_select_field(predicate, field);
      return (*gchs->chercheur)(mailbox, lview->message_list, field);
    }
  }
  return FALSE;
}



/* Action parsing routines and tables. */


ACTIONREC actlist[] = {
{  "delete",    NULL,  XtNactionDelete,   DELETE_ACT,   FALSE, FALSE, TRUE },
{  "expunge",   NULL,  XtNactionExpunge,  EXPUNGE_ACT,   FALSE, FALSE, TRUE },
{  "copy",      NULL,  XtNactionCopy,     COPY_ACT,   FALSE, TRUE,  TRUE },
{  "move",      NULL,  XtNactionMove,     MOVE_ACT,   FALSE, TRUE,  TRUE },
{  "save",      NULL,  XtNactionSave,     SAVE_ACT,   TRUE,  TRUE,  TRUE },
{  "shell",     NULL,  XtNactionShell,    SHELL_ACT,   TRUE,  TRUE,  TRUE },
{  "flag",      NULL,  XtNactionFlag,     FLAG_ACT,   FALSE, FALSE, TRUE },
{  "set",       NULL,  XtNactionSet,      SET_ACT,   FALSE, TRUE,  TRUE },
{  "unset",     NULL,  XtNactionUnset,    UNSET_ACT,   FALSE, TRUE,  TRUE },
{  "reply",     NULL,  XtNactionReply,    REPLY_ACT,   FALSE, TRUE,  FALSE },
{  "print",     NULL,  XtNactionPrint,    PRINT_ACT,   TRUE,  FALSE, TRUE },
{  "select",    NULL,  XtNactionSelect,   SELECT_ACT,   FALSE, FALSE, TRUE },
{  "new",       NULL,  XtNactionNew,      NEW_ACT,   FALSE, FALSE, TRUE },
{  "unseen",    NULL,  XtNactionUnseen,   UNSEEN_ACT,   FALSE, FALSE, TRUE },
{  "unselect",  NULL,  XtNactionUnselect, UNSELECT_ACT,   FALSE, FALSE, TRUE },
{  "read",      NULL,  XtNactionRead,     READ_ACT,   FALSE, FALSE, TRUE },
{  NULL,        NULL,  NULL,              NO_ACT,   FALSE, FALSE, FALSE }
};


HEADERREC hdrlist[] = {
{  "noheader",   NULL, XtNactionNoHeader,   HEADER_NONE },
{  "partheader", NULL, XtNactionPartHeader, HEADER_PART },
{  "fullheader", NULL, XtNactionFullHeader, HEADER_FULL },
{  NULL,         NULL, NULL,                HEADER_UNKNOWN }
};


#ifdef __STDC__
void init_action_and_header_list(void)
#else
void init_action_and_header_list()
#endif
{
  int i;
  for(i = 0; actlist[i].name != NULL; i ++ )
    actlist[i].localized = MLGetLocalized(actlist[i].resource,actlist[i].name);
  for(i = 0; hdrlist[i].name != NULL; i ++)
    hdrlist[i].localized = MLGetLocalized(hdrlist[i].resource,hdrlist[i].name);
  return;
}




#ifdef __STDC__
ACTIONREC *find_action(char *s)
#else
ACTIONREC *find_action(s)
     char *s;
#endif
{
  ACTIONREC *a;

  for(a = actlist; (a->name != NULL); a ++ ) {
    if(strncasecmp(s,a->localized,strlen(a->localized)) == 0)
      return(a);
  }
  return(NULL);
}

#ifdef __STDC__
HEADERREC *get_hdrlist(char *s)
#else
HEADERREC *get_hdrlist(s)
     char *s;
#endif
{
  HEADERREC *h;
  for(h = hdrlist; (h->name != NULL); h ++ ) {
    if(strncasecmp(s,h->localized,strlen(h->localized)) == 0)
      return(h);
  }

  return(NULL);
}

#ifdef __STDC__
void free_actions(char **ss)
#else
void free_actions(ss)
     char ** ss;
#endif
{
  int n;
  for(n = 0; ss[n] != NULL; n ++)
    fs_give((void **) &(ss[n]));
  fs_give((void **) ss);
  return;
}


#ifdef __STDC__
char **get_actions(char *s)
#else
char **get_actions(s) 
     char *s;
#endif
{
  int len, n;
  char *ptr = s;
  char *endptr;
  char *zeroptr;
  char **ret = NULL;
  char buf[FILEBUFFLEN];

  len = count_separators(s);
  if(!len)
    return(NULL);
  ret = (char **) fs_get((len + 1) * sizeof(char *));
  n = 0;
  while(*ptr != NUL_TERM) {
    while(isspace(*((unsigned char *)ptr)))
      ptr ++;
    endptr = strchr(ptr,ACTION_SEPARATOR);
    if(endptr == NULL)
      break;
    strncpy(buf,ptr,(int) (endptr - ptr));
    buf[endptr-ptr] = NUL_TERM;
    for(zeroptr = &buf[strlen(buf)-1]; 
	(isspace(*((unsigned char *)zeroptr))); zeroptr --)
      *zeroptr = NUL_TERM;
    if(! strlen(buf))
      break;
    ret[n] = cpystr(buf);
    n ++;
    ptr = (++endptr);
  }
  ret[n] = NULL;

  return(ret);
}

#ifdef __STDC__
int count_separators(char *s)
#else
int count_separators(s)
     char *s;
#endif
{
  int n = 0;
  char *p;
  for(p = s; (*p) ; p ++ )
    if(*p == ACTION_SEPARATOR)
      n ++;
  return(n);
}



/* Warning: we dispose of the calling string in this call */

#ifdef __STDC__
char *clean_and_parse_action(char *s)
#else
char *clean_and_parse_action(s)
     char *s;
#endif
{
  char buffer[FILEBUFFLEN];
  char warn[FILEBUFFLEN];
  char *ptr = s;
  char *ret = NULL;
  char *end_of_arg = NULL;
  int errors = 0;
  char *qptr = NULL;
  ACTIONREC *a = NULL;
  HEADERREC *h = NULL;
  *buffer = NUL_TERM;

  while(*ptr) {
    while(isspace(*((unsigned char *) ptr)))
      ptr ++;
    a = find_action(ptr);
    if(a == NULL || a->implemented == FALSE) {
      if(strlen(ptr)) {
	end_of_arg = strpbrk(ptr," \t\n;");
	if(end_of_arg)
	  *end_of_arg = NUL_TERM;
	sprintf(warn,MLGetLocalized(XtNmsgUnknownAction,MsgUnknownAction),ptr);
	mm_log(warn,WARN);
	errors ++;
      }
      buffer[strlen(buffer)] = NUL_TERM;
      break;
    }
    else {
      ptr += strlen(a->localized);
      strcat(buffer,a->localized);
      strcat(buffer,SPACESTR);
      if( a->arg == FALSE && a->option == FALSE ) {
	ptr = strchr(ptr,(int) ACTION_SEPARATOR);
	if(ptr) {
	  strcat(buffer,"; ");
	  ptr ++;
	  continue;
	}
	else {
	  strcat(buffer, "; ");
	  break;
	}
      }
      if(a->option == TRUE ) {
	while(isspace(*((unsigned char *)ptr)))
	  ptr ++;
	h = get_hdrlist(ptr);
	if(h) {
	  ptr += strlen(h->localized);
	  strcat(buffer,h->localized);
	  strcat(buffer,SPACESTR);
	}
	else {
	  strcat(buffer,hdrlist[HEADER_PART].localized);
	  strcat(buffer,SPACESTR);
	}
      }
      if(a->arg == FALSE) {
	ptr = strchr(ptr,(int) ACTION_SEPARATOR);
	if(ptr) {
	  strcat(buffer,"; ");
	  ptr ++;
	  continue;
	}
	else {
	  strcat(buffer, "; ");
	  break;
	}
      }
      while(isspace(*((unsigned char *)ptr)))
	ptr ++;
      if((! *ptr) || (*ptr == ACTION_SEPARATOR)) {
	sprintf(warn,MLGetLocalized(XtNmsgActionRequiresArg,
				    MsgActionRequiresArg),a->localized);
	mm_log(warn,WARN);
	errors ++;
	break;
      }
      if(*ptr == DQUOTECHAR) {
	qptr = strchr(ptr+1, DQUOTECHAR);
	if(! qptr) {
	  mm_log(MLGetLocalized(XtNmsgMissingQuote,MsgMissingQuote),WARN);
	  errors ++;
	  break;
	}
	end_of_arg = strpbrk(qptr," \t\n;");
      }
      else
	end_of_arg = strpbrk(ptr," \t\n;");
      if(! end_of_arg) {
	strcat(buffer,(char *)ptr);
	strcat(buffer," ; ");
	break;
      }
      else {
	strncat(buffer,ptr,(int) (end_of_arg - ptr));
	strcat(buffer," ; ");
	ptr = end_of_arg;
	while((isspace(*((unsigned char *)ptr))) 
	      || (*ptr == ACTION_SEPARATOR))	
	  ptr ++;
	continue;
      }
    }
  }

  fs_give((void **) &s);
  if(errors) {
    ret = cpystr(EMPTYSTR);
    return(ret);
  }
  
  if(strlen(buffer))
    ret = cpystr(buffer);
  else
    ret = cpystr(EMPTYSTR);
  return(ret);
}




/* Filter parsing routines and tables. */

static STOKEN search_tokens[]= {
  { 
    NULL, 
    XtNfilterTo,
    "TO",					/* Token string */
    TRUE,					/* True if field required */
    STRSEARCH,
  },				/* search type: local / server */
  { 
    NULL, 
    XtNfilterFrom,
    "FROM",
    TRUE,
    STRSEARCH,
  },
  { 
    NULL, 
    XtNfilterCc,
    "CC",
    TRUE,
    STRSEARCH,
  },
  { 
    NULL,
    XtNfilterCorrespondent,
    CORRESPONDENT_MACRO,
    TRUE,
    STRSEARCH,
  },
  { 
    NULL,
    XtNfilterRecipient,
    RECIPIENT_MACRO,
    TRUE,
    STRSEARCH,
  },
  { 
    NULL,
    XtNfilterConversant,
    CONVERSANT_MACRO,
    TRUE,
    STRSEARCH,
  },
  {
    NULL, 
    XtNfilterSubject,
    "SUBJECT",
    TRUE,
    STRSEARCH,
  },
  { 
    NULL, 
    XtNfilterText,
    "TEXT",
    TRUE,
    STRSEARCH,
  },
  { 
    NULL, 
    XtNfilterHeader,
    "HEADER",
    TRUE,
    STRSEARCH,
  },
  { 
    NULL, 
    XtNfilterBody,
    "BODY",
    TRUE,
    STRSEARCH,
  },
  { 
    NULL, 
    XtNfilterMessageId,
    "MESSAGE-ID",
    TRUE,
    STRSEARCH,
  },
  { 
    NULL, 
    XtNfilterSender,
    "SENDER",
    TRUE,
    STRSEARCH,
  },
  { 
    NULL, 
    XtNfilterReplyTo,
    "REPLY-TO",
    TRUE,
    STRSEARCH,
  },
  { 
    NULL, 
    XtNfilterInReplyTo,
    "IN-REPLY-TO",
    TRUE,
    STRSEARCH,
  },
  { 
    NULL, 
    XtNfilterSince,
    "SINCE",
    TRUE,
    TIMESEARCH,				/* TIME attribute */
  },
  { 
    NULL, 
    XtNfilterBefore,
    "BEFORE",
    TRUE,
    TIMESEARCH,
  },
  {
    NULL, 
    XtNfilterOn,
    "ON",
    TRUE,
    TIMESEARCH,
  },
  { 
    NULL, 
    XtNfilterLarger,
    "LARGER", 
    TRUE,
    ATOMIC+NUMERIC,
  },
  { 
    NULL, 
    XtNfilterNew,
    "NEW",
    FALSE,
    NONE,
  },
  {
    NULL, 
    XtNfilterOld,
    "OLD", 
    FALSE,
    NONE,
  },
  { 
    NULL, 
    XtNfilterDeleted,
    "DELETED", 
    FALSE,
    NONE,
  },
  { 
    NULL, 
    XtNfilterUndeleted,
    "UNDELETED", 
    FALSE,
    NONE,
  },
  {
    NULL, 
    XtNfilterSeen,
    "SEEN", 
    FALSE,
    NONE,
  },
  { 
    NULL, 
    XtNfilterUnseen,
    "UNSEEN", 
    FALSE,
    NONE,
  },
  { 
    NULL, 
    XtNfilterRecent,
    "RECENT", 
    FALSE,
    NONE,
  },
  { 
    NULL, 
    XtNfilterAnswered,
    "ANSWERED", 
    FALSE,
    NONE,
  },
  { 
    NULL, 
    XtNfilterUnanswered,
    "UNANSWERED", 
    FALSE,
    NONE,
  },
  { 
    NULL, 
    XtNfilterFlagged,
    "FLAGGED", 
    FALSE,
    NONE,
  },
  { 
    NULL, 
    XtNfilterUnflagged,
    "UNFLAGGED", 
    FALSE,
    NONE,
  },
  { 
    NULL, 
    NULL,
    NULL, 
    FALSE, 
    NONE,
  }
};

BOOLEAN_LIST boolean_tokens[]= {
  { NULL, XtNfilterAnd, "AND", AND_OP },
  { NULL, XtNfilterOr,  "OR",  OR_OP  },
  { NULL, XtNfilterNot, "NOT", NOT_OP },
  { NULL, NULL,         NULL,  NO_OP  }
};


#ifdef __STDC__
void init_logical_view_structures(void)
#else
void init_logical_view_structures()
#endif
{
  int i;
  for(i = 0; search_tokens[i].token != NULL; i ++)
    search_tokens[i].localized = MLGetLocalized(search_tokens[i].resource,
						search_tokens[i].token);
  for(i = 0; dynamic_table[i].value != NULL; i ++)
    dynamic_table[i].localized = MLGetLocalized(dynamic_table[i].resource,
						dynamic_table[i].value);

  for(i = 0; boolean_tokens[i].operator != NULL; i ++)
    boolean_tokens[i].localized = MLGetLocalized(boolean_tokens[i].resource,
						 boolean_tokens[i].operator);


}


/* return pointer to last char in a string */

#ifdef __STDC__
char *last(char *str)
#else
char *last(str)
     char *str;
#endif
{
  int len = strlen(str);
  return &str[len - 1];
}

/* Match str to a token in tlist.
 * Return a STOKEN pointer, or NULL */

#ifdef __STDC__
STOKEN * found_token(STOKEN *tlist, char *token, parse_errors *err)
#else
STOKEN * found_token(tlist, token, err)
     STOKEN *tlist;
     char *token;
     parse_errors *err;
#endif
{
  int i;

  for (i = 0; tlist[i].token; ++i)
    if (strcasecmp(token, tlist[i].localized) == STRMATCH)
      return &tlist[i];

  *err = illegal_token;
  return NULL;
}

#ifdef __STDC__
BOOLEAN_OPERATOR found_boolean(BOOLEAN_LIST *blist, char *str)
#else
BOOLEAN_OPERATOR found_boolean(blist, str)
     BOOLEAN_LIST *blist;
     char *str;
#endif
{
  int i;
  
  for (i = 0; boolean_tokens[i].operator; ++i)
    if (strcasecmp(str, boolean_tokens[i].localized) == STRMATCH) {
      return boolean_tokens[i].boole;
    }
  return NO_OP;
}
     
#ifdef __STDC__
char *make_predicate(char *token, char *field, Boolean needs_field, 
		     unsigned long attributes)
#else
char *make_predicate(token, field, needs_field, attributes)
     char *token, *field;
     Boolean needs_field;
     unsigned long attributes;
#endif
{
  int len;
  char *string = NULL;
  Boolean quoted;

  if (needs_field == FALSE)
    return(cpystr(token));

  len = strlen(token) + strlen(field) + 8;
  string = (char *) fs_get(len);

  quoted = (*field == DQUOTECHAR);
  if ((quoted == TRUE) || (attributes & ATOMIC)) 
    sprintf(string,"%s %s", token, field);
  else 
    sprintf(string,"%s \"%s\"", token, field);
  return string;
}

/* place null at end of field, and return pointer to beginnig
   of the field.
   n_excess will count surrounding parenthesis and any whitespace
   we step on to terminate the field .*/

#ifdef __STDC__
char *set_form_end(char *field, int *n_excess, parse_errors *err)
#else
char *set_form_end(field, n_excess, err)
     char *field;
     int *n_excess;
     parse_errors *err;
#endif
{
  char c, *cp= field, *cp0= field;
  
  *n_excess = 0;
  
  if (*cp == DQUOTECHAR) {			/* search for '"' */
    ++cp;				/* skip initial '"' */
    while ((c = *cp++) != NUL_TERM) {
      if (c == DQUOTECHAR) {
	*cp = NUL_TERM;			/* tie it off */
	return cp0;			/* found our matching " */
      }
    }
    
    /*
     * We should never reach this error because "" parsing is done
     * before we parse for structure detail */
    *err = missing_dquote;
    return NULL;			/* This should never happen */
  }
  /* OK, search for whitespace or a terminating ')' */
  while ((c = *cp) != NUL_TERM) {
    if ((c == SPACECHAR) || (c == TABCHAR)) {
      *cp = NUL_TERM;			/* tie it off */
      ++n_excess;			/* stepped on white-space */
    }
    ++cp;
  }
  return cp0;
}


#ifdef __STDC__
Boolean numeric_field(char *field, parse_errors *err)
#else
Boolean numeric_field(field, err)
     char *field;
     parse_errors *err;
#endif
{
  int i;

  if (*field == '\"') ++field;
  i = atoi(field);
  if (i <= 0) {
    *err = illegal_numeric_field;
    return FALSE;
  }
  return TRUE;
}

#ifdef __STDC__
Boolean select_field(char **fptr, STOKEN *tptr, char *field, 
		     int *len_trouve, parse_errors *err)
#else
Boolean select_field(fptr, tptr, field, len_trouve, err)
     char **fptr;
     STOKEN *tptr;
     char *field;
     int *len_trouve;
     parse_errors *err;
#endif
{
  char c;
  int loc_len= 0;
  char *loc_str, *str= field;

  
  /* Anything left? */
  if (!*field) {
    *err = missing_field;
    return FALSE;
  }

  loc_str = set_form_end(str, &loc_len, err); 
  if (!loc_str) 
    return FALSE;

  /* Is it a \". Note that any surrounding parenthesis have
     already been counted in set_form_end(). */
  if (*loc_str == DQUOTECHAR) {			/* quoted field */
    ++loc_str;
    loc_len += 1;
    while (((c = *loc_str++)) != NUL_TERM) {	/* cherche '"' termination */
      loc_len += 1;
      if (c == DQUOTECHAR) {
	*len_trouve = loc_len;
	*fptr = str;
	return TRUE;
      }
    }
    *err = missing_dquote;
    return FALSE;
  }
      
  /* Take next variable, ie, break on white-space or end-of-string */
  while (((c = *loc_str++)) != NUL_TERM)
    if ((c == SPACECHAR) || (c == TABCHAR)) 
      break;
    else 
      loc_len += 1;
  *len_trouve = loc_len;
  *fptr = str;
  return TRUE;
}

#ifdef __STDC__
int fix_whitespace(char *str)
#else
int fix_whitespace(str)
     char *str;
#endif
{
  char *src, *dst;
  char c;
  int ws= 1;				/* to clear leading whitespace */

  src = dst = str;
  /* Compact all whitespace */
  while (((c = *src++)) != NUL_TERM) {
    if (c == LFCHAR)
      continue;				/* skip RETURN */
    if ((c == SPACECHAR) || (c == TABCHAR)) {
      ws += 1;				/* count whitespace */
      if (ws > 1) continue;		/* skip it */
      c = SPACECHAR;				/* use SPACE */
    } else
      ws = 0;
    *dst++ = c;
  }
  *dst = NUL_TERM;
}

/* refuse blank names */

#ifdef __STDC__
Boolean whitespace_name(char *str)
#else
Boolean whitespace_name(str)
     char *str;
#endif
{
  char c;

  while (((c = *str++)) != NUL_TERM)
    if ((c != SPACECHAR) && (c != TABCHAR))
      return FALSE;
  return TRUE;
}


/*
 * add a new leaf to our tree. */


#ifdef __STDC__
void add_leaf_to_tree(LEAF **root, char *ctoken, char *cfield, 
		      BOOLEAN_OPERATOR operator, STOKEN *tptr,  
		      Boolean found_not)
#else
void add_leaf_to_tree(root, ctoken, cfield, operator, tptr, found_not)
     LEAF **root;
     char *ctoken;
     char *cfield;
     BOOLEAN_OPERATOR operator;
     STOKEN *tptr;
     Boolean found_not;
#endif
{
  LEAF *leaf = (LEAF *) fs_get(sizeof(LEAF));
  LEAF *prev;

  leaf->predicate = make_predicate(ctoken, cfield, 
				   tptr->field_required,
				   tptr->field_attributes);
  leaf->operator = operator;
  leaf->atomic_not = found_not;

  leaf->predicate_type = (tptr->field_required ? pred_field : pred_simple);

  /* some predicate fields are dynamic at search time */

  if ((leaf->predicate_type == pred_field) 
      && (tptr->field_attributes & TIMESEARCH))
    leaf->predicate_type = pred_dynamic_field;

  /* Adjoin the leaf to the tree */

  if (*root == NULL)
    *root = leaf;	
  else {
    for(prev = *root; prev->next; prev = prev->next)
      ;
    prev->next = leaf;	
  }

  leaf->next = NULL;		
}

/*
 * dispose of the tree */

#ifdef __STDC__
void cut_down_the_tree(LEAF *leafs)
#else
void cut_down_the_tree(leafs)
     LEAF *leafs;
#endif
{
  LEAF *feuille;

  /* dispose of the tree */
  while (leafs) {
    feuille = leafs;
    fs_give((void **) &feuille->predicate);	/* the string */
    leafs = feuille->next;		/* next leaf */
    fs_give((void **) &feuille);			/* the structure */
  }
}


/*
 * create IMAP search filter from the tree's leaf's */

#ifdef __STDC__
void move_leafs_to_filter(LEAF **search_tree, char **search_text, 
			  LEAF *leafs,  char *text)
#else
void move_leafs_to_filter(search_tree, search_text, leafs, text)
     LEAF **search_tree;
     char **search_text;
     LEAF *leafs;
     char *text;
#endif
{
  /* the search_filter.name is set up by the caller to lv_parse_filter */
  *search_tree = leafs;
  *search_text = text;
}    

/* augment the filter by len */    

#ifdef __STDC__
char *advance_filter(char *filter, int *filter_len, int len)
#else
char *advance_filter(filter, filter_len, len)
     char *filter;
     int  *filter_len;
     int len;
#endif
{
  *filter_len -= len;			/* decrement by string length  */
  if (*filter_len > 1) {
    filter += len + 1;			/* next list item */
    *filter_len -= 1;			/* advance one char more */
  } else
    filter += len;			/* points to NULL termination */
  return filter;
}

/*
 * Here we parse the filter text provided by the user
 * Returns: parse_success if parse does not fail. Otherwise an ERROR 
 */

#define TOKEN_STOPS " \t"
#define ERRMSGLEN 32

#ifdef __STDC__
parse_errors lv_parse_filter(char *name_text, char *filter_text, 
			     LEAF **search_tree, char **search_text, 
			     char *errmsg)
#else
parse_errors lv_parse_filter(name_text, filter_text, search_tree, 
			     search_text, errmsg)
     char *name_text;
     char *filter_text;
     LEAF **search_tree;
     char **search_text;
     char *errmsg;		
#endif
{
  char *search_string;			/* destination  */
  STOKEN *tokens= search_tokens;	/* our tokens */
  char *filter= filter_text;		/* source to parse */
  char *ctoken;				/* current token */
  STOKEN *tptr;				/* token block  */
  char *field;				/* for a field */
  int filter_len;			/* chars in filter */
  int field_len;
  int other_len;
  parse_errors err, rval;		/* in places, used for error return */
  BOOLEAN_OPERATOR operator = NO_OP;	/* AND_OP / OR_OP / NO_OP */
  BOOLEAN_OPERATOR not_op   = NO_OP;
  LEAF *arbe= NULL;			/* the tree which grows on C street */
  int next_term= TOKEN;			/* initially a TOKEN */
  int parsing;				/* counts terms */
  Boolean looping= TRUE;		/* keeps it going */
  Boolean found_not = FALSE;
  char *ptr0;
  char *ptr9;

  /* Make sure the name is not white-space */
  if (whitespace_name(name_text)) {
    return invalid_name;
  }

  fix_whitespace(filter);	        /* clear unnecessary whitespace */

  filter_len = strlen(filter);		/* what's is there */
  operator = NO_OP;
  search_string = cpystr(filter);	/* save a copy */

  *errmsg = NUL_TERM;			/* no message yet */
  strcpy(errmsg, MLGetLocalized(XtNmsgFilterParseError,
				 MsgFilterParseError));
  /* Main parse loop... */

  for (parsing = 0; looping; ++parsing) {

    /*
     * Here we expect to find a predicate like "FROM" or "DELETED" 
     */

    ptr0 = filter;
    if(*ptr0 == DQUOTECHAR) {
      ptr0 ++;
      while (*ptr0 && *ptr0 != DQUOTECHAR)
	ptr0 ++;
    }
    ptr9 = strpbrk(ptr0, TOKEN_STOPS);
    if(ptr9)
      *ptr9 = NUL_TERM;
    ctoken = filter;
    if(*ctoken == NUL_TERM)
      ctoken = NULL;
    
     /* !@#$%^&*()#%@&^% strtok! */
    /*ctoken = strtok(filter, TOKEN_STOPS);  first token */

    if (!ctoken) {
      if (parsing == 0) {		/* First time through */
	err =  empty_filter;		/* no filter provided */
      } 
      else
	if (next_term == GEORGES_BOOLE) { /* Final term a predicate */
	  err = no_error;
	} 
	else
	  err =  missing_predicate;	/* No predicate after AND/OR */
      break;
    }

    /* check for token or Boolean operator */

    switch (next_term) {

    case TOKEN:

      /* First check for NOT. It's a boolean, but will be located here. */

      not_op = found_boolean(boolean_tokens, ctoken);

      if (not_op == NOT_OP) {
	found_not = TRUE;
	other_len = strlen(ctoken);
	filter = advance_filter(filter, &filter_len, other_len);

	ptr0 = filter;
	if(*ptr0 == DQUOTECHAR) {
	  ptr0 ++;
	  while (*ptr0 && *ptr0 != DQUOTECHAR)
	    ptr0 ++;
	}
	ptr9 = strpbrk(ptr0, TOKEN_STOPS);
	if(ptr9)
	  *ptr9 = NUL_TERM;
	ctoken = filter;
	if(*ctoken == NUL_TERM)
	  ctoken = NULL;

	/* !@#$%^&*()#%@&^% strtok! */
	/* ctoken = strtok(filter, TOKEN_STOPS);  first token */
      }
      else
	found_not = FALSE;

      /* select a token from our tokens[] */
      if ((tptr = found_token(tokens, ctoken, &err)) == NULL) {
	looping = FALSE;
	strcat(errmsg, ctoken);
	continue;
      } 

      else {
	other_len = strlen(ctoken);	/* includes parenthesis, not */
 	next_term = GEORGES_BOOLE;	/* Boolean next en haut.*/
      }
      filter = advance_filter(filter, &filter_len, other_len);
      break;

    case GEORGES_BOOLE:
      /* 
       * We find the operator which binds the NEXT predicate with
       * it predecessors 
       */
      operator = found_boolean(boolean_tokens, ctoken);
      if ((operator == NO_OP) || (operator == NOT_OP)) {
	err =  expected_boolean;
	looping = FALSE;
	strcat(errmsg, ctoken);
	continue;
      } 
      else {
	other_len = strlen(ctoken);
	next_term = TOKEN;		/* token on next pass */
      }

      filter = advance_filter(filter, &filter_len, other_len);
      continue;
    }

    /* check for <field> */

    if (tptr->field_required) {

      if (!select_field(&field, tptr, filter, &field_len, &err)) {
	looping = FALSE;		/* err set in select_field */
	strcat(errmsg, ctoken);
	continue;
      }

      if (tptr->field_attributes & NUMERIC &&
	  !numeric_field(field, &err)) {
	looping = FALSE;
	strcat(errmsg, field);
	continue;
      }

      filter_len -= field_len;		/* decrement chars remaining */
      filter += field_len;		/* skip field in string */
      *filter = '\0';		 	/* terminate predicate */
      if (filter_len > 0) {		/* then not at end */
	++filter;			/* to next term */
	--filter_len;			/* one less character */
      }
    } 

    else {
      field = "";			/* No field - filter is OK. */
    }
    /* place in our parsing tree */

    add_leaf_to_tree(&arbe, ctoken, field, operator, tptr, found_not);

  }

  /* 
   *  chase down the tree and build the various search filters 
   */

  if (err == no_error) {
    move_leafs_to_filter(search_tree, search_text, arbe, search_string);
    rval = parse_success;
  } 
  else {
    cut_down_the_tree(arbe);		/* dispose of the tree */
    fs_give((void **) &search_string);		/* And the string */
    rval = err;
  }
  return rval;
}




syntax highlighted by Code2HTML, v. 0.9.1