Blame view

libmu_sieve/prog.c 9.67 KB
Wojciech Polak authored
1
/* GNU Mailutils -- a suite of utilities for electronic mail
2 3
   Copyright (C) 1999, 2000, 2001, 2002, 2004, 2005, 2006, 2007, 2008,
   2009, 2010 Free Software Foundation, Inc.
4

5 6 7
   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
8
   version 3 of the License, or (at your option) any later version.
9

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

15
   You should have received a copy of the GNU Lesser General
16 17
   Public License along with this library.  If not, see
   <http://www.gnu.org/licenses/>. */
18 19 20 21 22 23 24

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif  
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
Wojciech Polak authored
25
#include <sieve-priv.h>
26 27

int
28
mu_sv_code (sieve_op_t *op)
29
{
30
  if (mu_sieve_machine->pc >= mu_sieve_machine->progsize)
31
    {
32 33 34 35 36
      size_t newsize = mu_sieve_machine->progsize + SIEVE_CODE_INCR;
      sieve_op_t *newprog = mu_sieve_mrealloc (mu_sieve_machine, 
                                               mu_sieve_machine->prog,
					       newsize *
					     sizeof mu_sieve_machine->prog[0]);
37 38
      if (!newprog)
	{
39
	  mu_sv_compile_error (&mu_sieve_locus, _("not enough memory"));
40 41
	  return 1;
	}
42 43
      mu_sieve_machine->prog = newprog;
      mu_sieve_machine->progsize = newsize;
44
    }
45
  mu_sieve_machine->prog[mu_sieve_machine->pc++] = *op;
46 47 48 49
  return 0;
}

int
50
mu_sv_code_instr (sieve_instr_t instr)
51 52 53 54
{
  sieve_op_t op;

  op.instr = instr;
55
  return mu_sv_code (&op);
56 57 58
}

int
59
mu_sv_code_handler (mu_sieve_handler_t handler)
60 61 62 63
{
  sieve_op_t op;

  op.handler = handler;
64
  return mu_sv_code (&op);
65 66 67
}

int
68
mu_sv_code_list (mu_list_t list)
69 70 71 72
{
  sieve_op_t op;

  op.list = list;
73
  return mu_sv_code (&op);
74 75 76
}

int
77
mu_sv_code_number (long num)
78 79 80 81
{
  sieve_op_t op;

  op.number = num;
82
  return mu_sv_code (&op);
83 84 85
}

int
86
mu_sv_code_string (const char *string)
87 88 89 90
{
  sieve_op_t op;

  op.string = string;
91
  return mu_sv_code (&op);
92 93
}

94 95 96
mu_sieve_tag_def_t *
find_tag (mu_sieve_tag_group_t *taglist, char *tagname,
	  mu_sieve_tag_checker_t *checker)
97
{
98 99
  *checker = NULL;
  
100 101 102
  if (!taglist)
    return NULL;
  
103 104
  for (; taglist->tags; taglist++)
    {
105
      mu_sieve_tag_def_t *def;
106 107 108 109 110 111 112
      for (def = taglist->tags; def->name; def++)
	if (strcmp (def->name, tagname) == 0)
	  {
	    *checker = taglist->checker;
	    return def;
	  }
    }
113 114 115
  return NULL;
}

116 117 118 119 120 121 122
static int
_compare_ptr (void *item, void *data)
{
  return item == data;
}

struct check_arg {
123
  const char *name;
124 125
  mu_list_t args;
  mu_list_t tags;
126 127 128 129 130 131
};

static int
_run_checker (void *item, void *data)
{
  struct check_arg *arg = data;
132
  return (*(mu_sieve_tag_checker_t)item) (arg->name, arg->tags, arg->args);
133 134
}

135
int
136
mu_sv_code_command (mu_sieve_register_t *reg, mu_list_t arglist)
137
{
138 139 140 141
  mu_iterator_t itr;
  mu_list_t arg_list = NULL;
  mu_list_t tag_list = NULL;
  mu_list_t chk_list = NULL;
142
  mu_sieve_data_type *exp_arg;
143
  int rc, err = 0;
144
  static mu_sieve_data_type empty[] = { SVT_VOID };
145
  
146
  if (mu_sv_code_handler (reg->handler))
147 148 149 150 151 152
    return 1;

  exp_arg = reg->req_args ? reg->req_args : empty;

  if (arglist)
    {
153
      rc = mu_list_get_iterator (arglist, &itr);
154 155 156

      if (rc)
	{
157
	  mu_sv_compile_error (&mu_sieve_locus, 
158
                               _("cannot create iterator: %s"),
159
  		               mu_strerror (rc));
160 161 162
	  return 1;
	}
  
163
      for (mu_iterator_first (itr); !mu_iterator_is_done (itr); mu_iterator_next (itr))
164
	{
165 166
	  mu_sieve_value_t *val;
	  mu_sieve_runtime_tag_t tagrec, *tagptr;
167
	  
168
	  mu_iterator_current (itr, (void **)&val);
169 170 171
	  
	  if (val->type == SVT_TAG)
	    {
172 173
	      mu_sieve_tag_checker_t cf;
	      mu_sieve_tag_def_t *tag = find_tag (reg->tags, val->v.string, &cf);
174 175
	      if (!tag)
		{
176
		  mu_sv_compile_error (&mu_sieve_locus, 
Wojciech Polak authored
177
				       _("invalid tag name `%s' for `%s'"),
178
				       val->v.string, reg->name);
179 180 181 182
		  err = 1;
		  break;
		}
	      
183
	      if (!tag_list && (rc = mu_list_create (&tag_list)))
184
		{
185
		  mu_sv_compile_error (&mu_sieve_locus, 
186
                                       _("cannot create tag list: %s"),
187
			               mu_strerror (rc));
188 189 190 191
		  err = 1;
		  break;
		}
	      
192
	      tagrec.tag = tag->name;
193 194
	      if (tag->argtype != SVT_VOID)
		{
195
		  mu_iterator_next (itr);
196 197
		  if (mu_iterator_is_done (itr))
		    {
198
		      mu_sv_compile_error (&mu_sieve_locus, 
199 200 201 202 203
			   _("required argument for tag %s is missing"),
					   tag->name);
		      err = 1;
		      break;
		    }
204
		  mu_iterator_current (itr, (void **)&tagrec.arg);
Sergey Poznyakoff authored
205 206 207 208 209 210 211 212 213 214 215 216 217 218
		  if (tagrec.arg->type != tag->argtype)
		    {
		      mu_sv_compile_error (&mu_sieve_locus, 
					   _("type mismatch in argument to "
					     "tag `%s'"),
					   tag->name);
		      mu_sv_compile_error (&mu_sieve_locus, 
					   _("expected %s but passed %s"),
					   mu_sieve_type_str (tag->argtype),
					   mu_sieve_type_str
					                 (tagrec.arg->type));
		      err = 1;
		      break;
		    }
219 220 221 222
		}
	      else
		tagrec.arg = NULL;
	      
223
	      tagptr = mu_sieve_malloc (mu_sieve_machine, sizeof (*tagptr));
224
	      *tagptr = tagrec;
225
	      mu_list_append (tag_list, tagptr);
226 227 228

	      if (cf)
		{
229
		  if (!chk_list && (rc = mu_list_create (&chk_list)))
230
		    {
231
		      mu_sv_compile_error (&mu_sieve_locus, 
232
 			  	         _("cannot create check list: %s"),
233
					   mu_strerror (rc));
234 235 236
		      err = 1;
		      break;
		    }
237 238
		  if (mu_list_do (chk_list, _compare_ptr, cf) == 0)
		    mu_list_append (chk_list, cf);
239
		}
240 241 242
	    }
	  else if (*exp_arg == SVT_VOID)
	    {
243
	      mu_sv_compile_error (&mu_sieve_locus, 
Wojciech Polak authored
244
                                   _("too many arguments in call to `%s'"),
245
 			           reg->name);
246 247 248 249 250
	      err = 1;
	      break;
	    }
	  else
	    {
251 252 253 254
	      if (*exp_arg != val->type)
		{
		  if (*exp_arg == SVT_STRING_LIST && val->type == SVT_STRING)
		    {
255
		      mu_list_t list;
256

257 258
		      mu_list_create (&list);
		      mu_list_append (list, val->v.string);
259
		      mu_sieve_mfree (mu_sieve_machine, val);
260
		      val = mu_sieve_value_create (SVT_STRING_LIST, list);
261 262 263
		    }
		  else
		    {
264
		      mu_sv_compile_error (&mu_sieve_locus, 
265 266 267
                                   _("type mismatch in argument %lu to `%s'"),
				   (unsigned long) (exp_arg - reg->req_args + 1),
					   reg->name);
268
		      mu_sv_compile_error (&mu_sieve_locus, 
269
					   _("expected %s but passed %s"),
270 271
					   mu_sieve_type_str (*exp_arg),
					   mu_sieve_type_str (val->type));
272 273 274 275 276
		      err = 1;
		      break;
		    }
		}

277
	      if (!arg_list && (rc = mu_list_create (&arg_list)))
278
		{
279
		  mu_sv_compile_error (&mu_sieve_locus, 
280
                                       _("cannot create arg list: %s"),
281
			               mu_strerror (rc));
282 283 284 285
		  err = 1;
		  break;
		}
	      
286
	      mu_list_append (arg_list, val);
287 288 289
	      exp_arg++;
	    }	    
	}
290
      mu_iterator_destroy (&itr);
291 292 293 294 295 296
    }

  if (!err)
    {
      if (*exp_arg != SVT_VOID)
	{
297
	  mu_sv_compile_error (&mu_sieve_locus, 
Wojciech Polak authored
298
                               _("too few arguments in call to `%s'"),
299
			       reg->name);
300 301
	  err = 1;
	}
302 303 304 305 306 307 308 309

      if (chk_list)
	{
	  struct check_arg chk_arg;
      
	  chk_arg.name = reg->name;
	  chk_arg.tags = tag_list;
	  chk_arg.args = arg_list;
310
	  err = mu_list_do (chk_list, _run_checker, &chk_arg);
311
	}
312 313 314
    }
  
  if (!err)
315 316 317
    err = mu_sv_code_list (arg_list)
          || mu_sv_code_list (tag_list)
          || mu_sv_code_string (reg->name);
318 319 320

  if (err)
    {
321 322 323
      mu_list_destroy (&arg_list);
      mu_list_destroy (&tag_list);
      mu_list_destroy (&chk_list);
324 325 326 327 328 329
    }

  return err;
}

int
330
mu_sv_code_source (const char *name)
331 332 333
{
  char *s;
  
334 335
  if (mu_list_locate (mu_sieve_machine->source_list, 
                      (void*) name, (void **) &s))
336
    {
337 338
      s = mu_sieve_mstrdup (mu_sieve_machine, name);
      mu_list_append (mu_sieve_machine->source_list, s);
339 340
    }
  
341 342
  return mu_sv_code_instr (_mu_sv_instr_source)
	 || mu_sv_code_string (s);
343 344 345
}

int
346
mu_sv_code_line (size_t line)
347 348 349 350
{
  sieve_op_t op;

  op.line = line;
351 352
  return mu_sv_code_instr (_mu_sv_instr_line)
	 || mu_sv_code (&op);
353 354
}

355 356 357
static int sieve_source_changed;

void
358
mu_sv_change_source ()
359 360 361 362 363 364 365 366 367 368
{
  sieve_source_changed = 1;
}

static int
sieve_check_source_changed ()
{
  if (sieve_source_changed)
    {
      sieve_source_changed = 0;
369
      return mu_sv_code_source (mu_sieve_locus.source_file);
370 371 372 373
    }
  return 0;
}

374
int
375
mu_sv_code_action (mu_sieve_register_t *reg, mu_list_t arglist)
376
{
377
  return sieve_check_source_changed ()
378 379 380
         || mu_sv_code_line (mu_sieve_locus.source_line)
         || mu_sv_code_instr (_mu_sv_instr_action)
         || mu_sv_code_command (reg, arglist);
381 382 383
}

int
384
mu_sv_code_test (mu_sieve_register_t *reg, mu_list_t arglist)
385
{
386
  return sieve_check_source_changed ()
387 388 389
         || mu_sv_code_line (mu_sieve_locus.source_line)
         || mu_sv_code_instr (_mu_sv_instr_test)
         || mu_sv_code_command (reg, arglist);
390 391
}

392
void
393
mu_sv_code_anyof (size_t start)
394
{
395 396
  size_t end = mu_sieve_machine->pc;
  while (mu_sieve_machine->prog[start+1].pc != 0)
397
    {
398 399 400
      size_t next = mu_sieve_machine->prog[start+1].pc;
      mu_sieve_machine->prog[start].instr = _mu_sv_instr_brnz;
      mu_sieve_machine->prog[start+1].pc = end - start - 2;
401 402
      start = next;
    }
403 404
  mu_sieve_machine->prog[start].instr = _mu_sv_instr_nop;
  mu_sieve_machine->prog[start+1].instr = _mu_sv_instr_nop;
405 406 407
}

void
408
mu_sv_code_allof (size_t start)
409
{
410
  size_t end = mu_sieve_machine->pc;
411
  
412
  while (mu_sieve_machine->prog[start+1].pc != 0)
413
    {
414 415
      size_t next = mu_sieve_machine->prog[start+1].pc;
      mu_sieve_machine->prog[start+1].pc = end - start - 2;
416 417
      start = next;
    }
418 419
  mu_sieve_machine->prog[start].instr = _mu_sv_instr_nop;
  mu_sieve_machine->prog[start+1].instr = _mu_sv_instr_nop;
420 421
}