/* GNU Mailutils -- a suite of utilities for electronic mail
   Copyright (C) 1999-2025 Free Software Foundation, Inc.

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 3 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General
   Public License along with this library.  If not, see
   <http://www.gnu.org/licenses/>. */

#include <mailutils/sieve.h>
#include <mailutils/assoc.h>
#include <mailutils/locus.h>
#include <mailutils/yyloc.h>
#include <mailutils/cstr.h>
#include <setjmp.h>
#include <string.h>
#include <regex.h>

typedef void (*sieve_instr_t) (mu_sieve_machine_t mach);

typedef union
{
  sieve_instr_t instr;
  mu_sieve_handler_t handler;
  mu_sieve_value_t *val;
  mu_sieve_comparator_t comp;
  long number;
  size_t pc;
  size_t line;
  int inum;
  char *string;
  unsigned unum;
} sieve_op_t;


#define MU_SV_SAVED_ERR_STATE 0x01
#define MU_SV_SAVED_DBG_STATE 0x02
#define MU_SV_SAVED_STATE     0x80

enum mu_sieve_state
  {
    mu_sieve_state_init,
    mu_sieve_state_error,
    mu_sieve_state_compiled,
    mu_sieve_state_running,
    mu_sieve_state_disass
  };

struct mu_sieve_machine
{
  /* Static data */
  struct mu_locus_range locus; /* Approximate location in the code */
  int changeloc:1;             /* If set during code generation phase, the
				  _mu_i_sv_instr_locus will be emitted before
				  next instruction */
  
  mu_list_t memory_pool;     /* Pool of allocated memory objects */
  mu_list_t destr_list;      /* List of destructor functions */

  /* Symbol space: */
  mu_opool_t string_pool;    /* String constants */
  mu_list_t registry;        /* Tests, Actions, Comparators */

  char **idspace;            /* Source and identifier names */
  size_t idcount;
  size_t idmax;
  
  mu_sieve_string_t *stringspace;
  size_t stringcount;
  size_t stringmax;
  
  mu_sieve_value_t *valspace;
  size_t valcount;
  size_t valmax;
  
  size_t progsize;           /* Number of allocated program cells */
  sieve_op_t *prog;          /* Compiled program */

  /* Runtime data */
  enum mu_sieve_state state; /* Machine state */
  size_t pc;                 /* Current program counter */
  long reg;                  /* Numeric register */

  /* Support for variables (RFC 5229) */
  mu_assoc_t vartab;         /* Table of variables */
  char *match_string;        /* The string used in the most recent match */
  regmatch_t *match_buf;     /* Offsets of parenthesized groups */
  size_t match_count;        /* Actual number of elements used in match_buf */
  size_t match_max;          /* Total number of elements available in match_buf */
  mu_list_t init_var;        /* List of variable initializers */
  
  /* Call environment */
  const char *identifier;    /* Name of action or test being executed */
  size_t argstart;           /* Index of the first argument in valspace */
  size_t argcount;           /* Number of positional arguments */
  size_t tagcount;           /* Number of tagged arguments */
  mu_sieve_comparator_t comparator; /* Comparator (for tests) */

  int dry_run;               /* Dry-run mode */
  jmp_buf errbuf;            /* Target location for non-local exits */

  mu_assoc_t exenv;          /* Execution environment (RFC 5183) */

  mu_mailbox_t mailbox;      /* Mailbox to operate upon */
  size_t    msgno;           /* Current message number */
  mu_message_t msg;          /* Current message */
  int action_count;          /* Number of actions executed over this message */

  /* Fileinto */
  mu_assoc_t mailboxes;
  
  /* Stream state info */
  int state_flags;
  int err_mode;
  struct mu_locus_range err_locus;
  int dbg_mode;
  struct mu_locus_range dbg_locus;
  
  /* User supplied data */
  mu_stream_t errstream;
  mu_stream_t dbgstream;
  
  mu_sieve_action_log_t logger;
  
  mu_mailer_t mailer;
  char *daemon_email;
  void *data;
};

enum mu_sieve_node_type
  {
    mu_sieve_node_noop,
    mu_sieve_node_false,
    mu_sieve_node_true,
    mu_sieve_node_test,
    mu_sieve_node_action,
    mu_sieve_node_cond,
    mu_sieve_node_anyof,
    mu_sieve_node_allof,
    mu_sieve_node_not,
    mu_sieve_node_end,
  };

struct mu_sieve_node
{
  struct mu_sieve_node *prev, *next;
  enum mu_sieve_node_type type;
  struct mu_locus_range locus;
  union
  {
    struct mu_sieve_node *node;
    struct
    {
      struct mu_sieve_node *expr;
      struct mu_sieve_node *iftrue;
      struct mu_sieve_node *iffalse;
    } cond;
    struct
    {
      mu_sieve_registry_t *reg;
      size_t argstart;
      size_t argcount;
      size_t tagcount;
      mu_sieve_comparator_t comparator; /* Comparator (for tests) */
    } command;
  } v;
};

struct mu_sieve_node_list
{
  struct mu_sieve_node *head, *tail;
};

struct mu_sieve_variable_initializer
{
  char *name;
  char *value;
};

int mu_sieve_yyerror (const char *s);
int mu_sieve_yylex (void);

int mu_i_sv_lex_begin (const char *name);
int mu_i_sv_lex_begin_string (const char *buf, int bufsize,
			      struct mu_locus_point const *pt);
void mu_i_sv_lex_finish (void);

extern mu_sieve_machine_t mu_sieve_machine;

void mu_i_sv_code (struct mu_sieve_machine *mach, sieve_op_t op);

int mu_i_sv_locus (struct mu_sieve_machine *mach, struct mu_locus_range *lr);
void mu_i_sv_code_action (struct mu_sieve_machine *mach,
			  struct mu_sieve_node *node);
void mu_i_sv_code_test (struct mu_sieve_machine *mach,
			struct mu_sieve_node *node);

/* Opcodes */
void _mu_i_sv_instr_action (mu_sieve_machine_t mach);
void _mu_i_sv_instr_test (mu_sieve_machine_t mach);
void _mu_i_sv_instr_not (mu_sieve_machine_t mach);
void _mu_i_sv_instr_branch (mu_sieve_machine_t mach);
void _mu_i_sv_instr_brz (mu_sieve_machine_t mach);
void _mu_i_sv_instr_brnz (mu_sieve_machine_t mach);
void _mu_i_sv_instr_locus (mu_sieve_machine_t mach);

int mu_i_sv_load_add_dir (mu_sieve_machine_t mach, const char *name);

void mu_i_sv_register_standard_actions (mu_sieve_machine_t mach);
void mu_i_sv_register_standard_tests (mu_sieve_machine_t mach);
void mu_i_sv_register_standard_comparators (mu_sieve_machine_t mach);

void mu_i_sv_error (mu_sieve_machine_t mach);

void mu_i_sv_debug (mu_sieve_machine_t mach, size_t pc, const char *fmt, ...)
  MU_PRINTFLIKE(3,4);
void mu_i_sv_debug_command (mu_sieve_machine_t mach, size_t pc,
			    char const *what);
void mu_i_sv_trace (mu_sieve_machine_t mach, const char *what);

void mu_i_sv_valf (mu_sieve_machine_t mach, mu_stream_t str,
		   mu_sieve_value_t *val);

typedef int (*mu_i_sv_interp_t) (char const *, size_t, char **, void *);

int mu_i_sv_string_expand (char const *input,
			   mu_i_sv_interp_t interp, void *data, char **ret);

int mu_i_sv_expand_encoded_char (char const *input, size_t len, char **exp, void *data);

int mu_sieve_require_encoded_character (mu_sieve_machine_t mach,
					const char *name);

void mu_i_sv_2nrealloc (mu_sieve_machine_t mach,
			void **pptr, size_t *pnmemb, size_t size);


mu_sieve_value_t *mu_i_sv_mach_arg (mu_sieve_machine_t mach, size_t n);
mu_sieve_value_t *mu_i_sv_mach_tagn (mu_sieve_machine_t mach, size_t n);
void mu_i_sv_lint_command (struct mu_sieve_machine *mach,
			   struct mu_sieve_node *node);


size_t  mu_i_sv_string_create (mu_sieve_machine_t mach, char *str);

size_t mu_i_sv_id_num (mu_sieve_machine_t mach, char const *name);
char *mu_i_sv_id_str (mu_sieve_machine_t mach, size_t n);
void mu_i_sv_free_idspace (mu_sieve_machine_t mach);

void mu_i_sv_copy_variables (mu_sieve_machine_t child,
			     mu_sieve_machine_t parent);
int mu_i_sv_expand_variables (char const *input, size_t len,
			      char **exp, void *data);
void mu_i_sv_init_variables (mu_sieve_machine_t mach);
int mu_i_sv_registry_add (mu_sieve_machine_t mach, const char *name,
			  mu_sieve_registry_t **retreg);



