Blame view

mh/mh_list.c 17.9 KB
1
/* GNU Mailutils -- a suite of utilities for electronic mail
2 3
   Copyright (C) 2003, 2005, 2006, 2007, 2009, 2010 Free Software
   Foundation, Inc.
4 5 6

   GNU Mailutils is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
7
   the Free Software Foundation; either version 3, or (at your option)
8 9 10 11 12 13 14 15
   any later version.

   GNU Mailutils 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 General Public License for more details.

   You should have received a copy of the GNU General Public License
16
   along with GNU Mailutils.  If not, see <http://www.gnu.org/licenses/>. */
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72

/* MH mhl command */

#include <mh.h>

/* *********************** Compiler for MHL formats *********************** */

typedef struct
{
  char *filename;
  int line;
}
locus_t;

enum mhl_type
{
  stmt_cleartext,
  stmt_component,
  stmt_variable
};

enum mhl_datatype
{
  dt_flag,
  dt_integer,
  dt_string,
  dt_format
};

typedef union mhl_value {
  char *str;
  int num;
  mh_format_t *fmt;
} mhl_value_t;

typedef struct mhl_variable
{
  int id;
  char *name;
  int type;
}
mhl_variable_t;  

typedef struct mhl_stmt mhl_stmt_t;
typedef struct mhl_stmt_variable mhl_stmt_variable_t;
typedef struct mhl_stmt_component mhl_stmt_component_t;

struct mhl_stmt_variable
{
  mhl_variable_t *id;
  mhl_value_t value;
};
  
struct mhl_stmt_component
{
  char *name;
73
  mu_list_t format;
74 75 76 77 78 79 80 81 82 83 84 85 86
};

struct mhl_stmt
{
  enum mhl_type type;
  union
  {
    char *cleartext;
    mhl_stmt_variable_t variable;
    mhl_stmt_component_t component;
  } v;
};

87
static mhl_variable_t *variable_lookup (char *name);
88 89 90 91 92 93 94 95 96 97 98 99 100 101

static mhl_stmt_t *
stmt_alloc (enum mhl_type type)
{
  mhl_stmt_t *p = xmalloc (sizeof (*p));
  p->type = type;
  return p;
}

static int
compdecl (char **str, char **compname)
{
  char *p;
  
102
  for (p = *str; *p && !mu_isspace (*p); p++)
103 104 105 106 107 108 109 110 111 112 113 114 115 116
    {
      if (*p == ':')
	{
	  int len = p - *str;
	  *compname = xmalloc (len + 1);
	  memcpy (*compname, *str, len);
	  (*compname)[len] = 0;
	  *str = p + 1;
	  return 0;
	}
    }
  return 1;
}

117
static void parse_variable (locus_t *loc, mu_list_t formlist, char *str);
118 119

static void
120
parse_cleartext (locus_t *loc, mu_list_t formlist, char *str)
121 122 123 124 125 126 127
{
  int len;
  mhl_stmt_t *stmt = stmt_alloc (stmt_cleartext);
  stmt->v.cleartext = strdup (str);
  len = strlen (stmt->v.cleartext);
  if (len > 0 && stmt->v.cleartext[len-1] == '\n')
    stmt->v.cleartext[len-1] = 0;
128
  mu_list_append (formlist, stmt);
129 130 131
}

static void
132
parse_component (locus_t *loc, mu_list_t formlist, char *compname, char *str)
133
{
134
  int rc;
135 136
  mhl_stmt_t *stmt = stmt_alloc (stmt_component);
  stmt->v.component.name = compname;
137
  if ((rc = mu_list_create (&stmt->v.component.format)) != 0)
138
    {
139
      mu_error ("%s:%d: mu_list_create: %s",
140
		loc->filename,
141 142
		loc->line,
		mu_strerror (rc));
143 144 145
      exit (1); /* FIXME */
    }
  parse_variable (loc, stmt->v.component.format, str);
146
  mu_list_append (formlist, stmt);
147 148 149
}

static void
150
parse_variable (locus_t *loc, mu_list_t formlist, char *str)
151
{
152 153
  size_t i;
  struct mu_wordsplit ws;
154
  mh_format_t fmt;
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170
  int wsflags;
  
  if (strncmp (str, "ignores=", 8) == 0 && str[8] != '"')
    {
      /* A hack to allow for traditional use of "ignores=", i.e.
	 as a single statement on a line, without double-quotes around
	 the argument */
      wsflags = MU_WRDSF_NOCMD | MU_WRDSF_NOVAR | MU_WRDSF_NOSPLIT;
    }
  else
    {
      ws.ws_delim = ",";
      wsflags = MU_WRDSF_DEFFLAGS|MU_WRDSF_DELIM|
	        MU_WRDSF_WS|MU_WRDSF_RETURN_DELIMS;
    }
  if (mu_wordsplit (str, &ws, wsflags))
171
    {
172
      mu_error ("%s:%d: mu_wordsplit(%s): %s",
173 174
		loc->filename,
		loc->line,
175
		str,
176
		mu_wordsplit_strerror (&ws));
177 178 179
      exit (1);
    }

180
  for (i = 0; i < ws.ws_wordc; i++)
181
    {
182 183
      mhl_stmt_t *stmt;
      char *name = ws.ws_wordv[i];
184 185
      char *value = NULL;
      mhl_variable_t *var;
186 187 188 189
      
      value = strchr (name, '=');
      if (value)
	*value++ = 0;
190
      stmt = stmt_alloc (stmt_variable);
191 192 193
      var = variable_lookup (name);
      if (!var)
	{
194
	  mu_error (_("%s:%d: unknown variable: %s"),
195 196
		    loc->filename,
		    loc->line,
197
		    name);
198 199 200 201 202 203
	  exit (1);
	}

      if ((var->type == dt_flag && value)
	  || (var->type != dt_flag && !value))
	{
204
	  mu_error (_("%s:%d: wrong datatype for %s"),
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223
		    loc->filename,
		    loc->line,
		    var->name);
	  exit (1);
	}

      switch (var->type)
	{
	case dt_string:
	  stmt->v.variable.value.str = strdup (value);
	  break;

	case dt_integer:
	  stmt->v.variable.value.num = strtoul (value, NULL, 0);
	  break;

	case dt_format:
	  if (mh_format_parse (value, &fmt))
	    {
224
	      mu_error (_("%s:%d: bad format string"),
225 226 227 228 229 230 231 232 233 234 235 236 237
			loc->filename,
			loc->line);
	      exit (1);
	    }
	  stmt->v.variable.value.fmt = xmalloc (sizeof (mh_format_t));
	  *stmt->v.variable.value.fmt = fmt;
	  break;

	case dt_flag:
	  stmt->v.variable.value.num = strcmp (var->name, name) == 0;
	  break;
	}
      stmt->v.variable.id = var;
238
      mu_list_append (formlist, stmt);
239 240

      i++;
241
      if (i < ws.ws_wordc && ws.ws_wordv[i][0] != ',')
242
	{
243
	  mu_error (_("%s:%d: syntax error"), loc->filename, loc->line);
244 245 246
	  exit (1);
	}
    }
247
  mu_wordsplit_free (&ws);
248 249 250
}

static int
251
parse_line (locus_t *loc, mu_list_t formlist, char *str)
252 253 254 255 256 257 258 259 260 261 262 263
{
  char *compname;
  
  if (*str == ':')
    parse_cleartext (loc, formlist, str+1);
  else if (compdecl (&str, &compname) == 0)
    parse_component (loc, formlist, compname, str);
  else
    parse_variable (loc, formlist, str);
  return 0;
}

264
mu_list_t 
265 266
mhl_format_compile (char *name)
{
267
  mu_stream_t stream;
268
  mu_list_t formlist;
269
  char *buf = NULL;
270
  size_t size = 0, n;
271
  locus_t loc;
272
  int rc;
273 274 275

  rc = mu_file_stream_create (&stream, name, MU_STREAM_READ);
  if (rc)
276
    {
277
      mu_error (_("cannot open format file %s: %s"), name, mu_strerror (rc));
278 279 280
      return NULL;
    }

281
  if ((rc = mu_list_create (&formlist)) != 0)
282
    {
283
      mu_diag_funcall (MU_DIAG_ERROR, "mu_list_create", NULL, rc);
284
      mu_stream_destroy (&stream);
285 286 287 288 289
      return NULL;
    }

  loc.filename = name;
  loc.line = 1;
290
  while (mu_stream_getline (stream, &buf, &size, &n) == 0 && n > 0)
291
    {
292 293 294
      char *p = mu_str_stripws (buf);
      if (!(*p == 0 || *p == ';'))
	parse_line (&loc, formlist, p);
295 296 297 298
      loc.line++;
    }

  free (buf);
299
  mu_stream_destroy (&stream);
300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356
  
  return formlist;
}


/* ********************** Destroy compiled MHL format ********************** */

static void
_destroy_value (enum mhl_datatype type, mhl_value_t *val)
{
  switch (type)
    {
    case dt_string:
      free (val->str);
      break;
	  
    case dt_flag:
    case dt_integer:
      break;

    case dt_format:
      mh_format_free (val->fmt);
      free (val->fmt);
      break;

    default:
      abort ();
    }
}

static int
_destroy_stmt (void *item, void *data)
{
  mhl_stmt_t *stmt = item;

  switch (stmt->type)
    {
    case stmt_cleartext:
      free (stmt->v.cleartext);
      break;

    case stmt_component:
      free (stmt->v.component.name);
      mhl_format_destroy (&stmt->v.component.format);
      break;

    case stmt_variable:
      _destroy_value (stmt->v.variable.id->type, &stmt->v.variable.value);
      break;
      
    default:
      abort ();
    }
  return 0;
}

void
357
mhl_format_destroy (mu_list_t *fmt)
358
{
359 360
  mu_list_do (*fmt, _destroy_stmt, NULL);
  mu_list_destroy (fmt);
361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384
}


/* *************************** Runtime functions *************************** */
/* Integer variables */
#define I_WIDTH           0
#define I_LENGTH          1 
#define I_OFFSET          2
#define I_OVERFLOWOFFSET  3
#define I_COMPWIDTH       4
#define I_MAX             5

/* Boolean (flag) variables */
#define B_UPPERCASE       0
#define B_CLEARSCREEN     1
#define B_BELL            2
#define B_NOCOMPONENT     3
#define B_CENTER          4
#define B_LEFTADJUST      5
#define B_COMPRESS        6
#define B_SPLIT           7
#define B_NEWLINE         8
#define B_ADDRFIELD       9
#define B_DATEFIELD      10
385
#define B_DECODE         12
386 387
#define B_DISABLE_BODY   13
#define B_MAX            14
388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418
		      
/* String variables */
#define S_OVERFLOWTEXT   0
#define S_COMPONENT      1
#define S_IGNORES        2
#define S_MAX            3
		      
/* Format variables */
#define F_FORMATFIELD    0
#define F_MAX            1

static mhl_variable_t vartab[] = {
  /* Integer variables */
  { I_WIDTH,          "width",          dt_integer  },
  { I_LENGTH,         "length",         dt_integer },
  { I_OFFSET,         "offset",         dt_integer },
  { I_OVERFLOWOFFSET, "overflowoffset", dt_integer },
  { I_COMPWIDTH,      "compwidth",      dt_integer },

  /* Boolean (flag) variables */
  { B_UPPERCASE,      "uppercase",      dt_flag },
  { B_CLEARSCREEN,    "clearscreen",    dt_flag },
  { B_BELL,           "bell",           dt_flag },
  { B_NOCOMPONENT,    "nocomponent",    dt_flag },
  { B_CENTER,         "center",         dt_flag },
  { B_LEFTADJUST,     "leftadjust",     dt_flag },
  { B_COMPRESS,       "compress",       dt_flag },
  { B_SPLIT,          "split",          dt_flag },
  { B_NEWLINE,        "newline",        dt_flag },
  { B_ADDRFIELD,      "addrfield",      dt_flag },
  { B_DATEFIELD,      "datefield",      dt_flag },
uid65697 authored
419 420
  { B_DECODE,         "decode",         dt_flag },
  
421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450
  /* String variables */
  { S_OVERFLOWTEXT,   "overflowtext",   dt_string },
  { S_COMPONENT,      "component",      dt_string },
  { S_IGNORES,        "ignores",        dt_string },
  
  /* Format variables */
  { F_FORMATFIELD,    "formatfield",    dt_format },
  { 0 }
};
	      
static mhl_variable_t *
variable_lookup (char *name)
{
  mhl_variable_t *p;

  for (p = vartab; p->name; p++)
    {
      if (p->type == dt_flag
	  && memcmp (name, "no", 2) == 0
	  && strcmp (p->name, name + 2) == 0)
	return p;
	  
      if (strcmp (p->name, name) == 0)
	return p;
    }
  return NULL;
}

struct eval_env
{
451 452 453
  mu_message_t msg;
  mu_stream_t output;
  mu_list_t printed_fields;  /* A list of printed header names */
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470
  int pos;
  int nlines;
  int ivar[I_MAX];
  int bvar[B_MAX];
  char *svar[S_MAX];
  mh_format_t *fvar[F_MAX];
  char *prefix;
};

static int eval_stmt (void *item, void *data);
static void newline (struct eval_env *env);
static void goto_offset (struct eval_env *env, int count);
static void print (struct eval_env *env, char *str, int nloff);

static int
_comp_name (void *item, void *date)
{
471
  return mu_c_strcasecmp (item, date) == 0;
472 473 474
}

int
475
header_is_printed (struct eval_env *env, const char *name)
476
{
477
  return mu_list_do (env->printed_fields, _comp_name, (void*) name) == 1;
478 479 480
}

int
481
want_header (struct eval_env *env, const char *name)
482
{
Sergey Poznyakoff authored
483
  const char *p, *str;
484

Sergey Poznyakoff authored
485
  for (str = env->svar[S_IGNORES], p = name; *str; str++)
486
    {
Sergey Poznyakoff authored
487 488 489 490 491 492 493 494 495 496 497
      if (p)
	{
	  if (*p == 0 && *str == ',')
	    break;
	  if (mu_tolower (*p) == mu_tolower (*str))
	    p++;
	  else
	    p = NULL;
	}
      else if (*str == ',')
	p = name;
498
    }
Sergey Poznyakoff authored
499 500
  if (p && *p == 0)
    return 0;
501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544
  return 1;
}

static int
eval_var (struct eval_env *env, mhl_stmt_variable_t *var)
{
  switch (var->id->type)
    {
    case dt_flag:
      env->bvar[var->id->id] = var->value.num;
      break;
      
    case dt_integer:
      env->ivar[var->id->id] = var->value.num;
      break;
      
    case dt_string:
      env->svar[var->id->id] = var->value.str;
      break;

    case dt_format:
      env->fvar[var->id->id] = var->value.fmt;
      break;

    default:
      abort ();
    }
  return 0;
}

static void
ovf_print (struct eval_env *env, char *str, int size, int nloff)
{
  int ovf = 0;

  while (size)
    {
      int len = size;
      if (ovf)
	{
	  goto_offset (env, env->ivar[I_OVERFLOWOFFSET]);
	  if (env->svar[S_OVERFLOWTEXT])
	    {
	      int l = strlen (env->svar[S_OVERFLOWTEXT]);
545 546
	      mu_stream_write (env->output,
			       env->svar[S_OVERFLOWTEXT], l, NULL);
547 548 549 550 551 552 553 554 555
	      env->pos += l;
	    }
	}
      else
	{
	  if (env->prefix && !env->bvar[B_NOCOMPONENT])
	    {
	      goto_offset (env, env->ivar[I_OFFSET]);
	      
556 557
	      mu_stream_write (env->output, env->prefix,
			       strlen (env->prefix), NULL);
558 559
	      env->pos += strlen (env->prefix);
	    }
560
	  goto_offset (env, nloff);
561 562 563 564 565 566 567 568
	}
      
      if (env->pos + len > env->ivar[I_WIDTH])
	{
	  ovf = 1;
	  len = env->ivar[I_WIDTH] - env->pos;
	}
      
569
      mu_stream_write (env->output, str, len, NULL);
570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587
      env->pos += len;
      if (env->pos >= env->ivar[I_WIDTH])
	newline (env);
      str += len;
      size -= len;
    }
}

static void
print (struct eval_env *env, char *str, int nloff)
{
  do
    {
      if (*str == '\n')
	{
	  newline (env);
	  str++;
	}
Sergey Poznyakoff authored
588
      else if (*str)
589
	{
Sergey Poznyakoff authored
590 591 592 593
	  size_t size = strcspn (str, "\n");
	  ovf_print (env, str, size, nloff);
	  str += size;
	  if (*str == '\n')
594 595
	    {
	      newline (env);
Sergey Poznyakoff authored
596
	      str = mu_str_skip_class (str + 1, MU_CTYPE_SPACE);
597 598 599 600 601 602 603 604 605
	    }
	}
    }
  while (*str);
}

static void
newline (struct eval_env *env)
{
606
  mu_stream_write (env->output, "\n", 1, NULL);
607 608 609 610 611
  env->pos = 0;
  if (env->ivar[I_LENGTH] && ++env->nlines >= env->ivar[I_LENGTH])
    {
      /* FIXME: Better to write it directly on the terminal */
      if (env->bvar[B_BELL])
612
	mu_stream_write (env->output, "\a", 1, NULL);
613
      if (env->bvar[B_CLEARSCREEN])
614
	mu_stream_write (env->output, "\f", 1, NULL);
615 616 617 618 619 620 621 622
      env->nlines = 0;
    }
}

static void
goto_offset (struct eval_env *env, int count)
{
  for (; env->pos < count; env->pos++)
623
    mu_stream_write (env->output, " ", 1, NULL);
624 625 626 627 628 629 630 631 632 633 634 635 636 637
}

int
print_header_value (struct eval_env *env, char *val)
{
  char *p;

  if (env->fvar[F_FORMATFIELD])
    {
      if (mh_format_str (env->fvar[F_FORMATFIELD], val,
			 env->ivar[I_WIDTH], &p))
	val = p;
    }
    
uid65697 authored
638 639 640 641 642 643
  if (env->bvar[B_DECODE])
    {
      if (mh_decode_2047 (val, &p) == 0)
	val = p;
    }
  
644
  if (env->bvar[B_UPPERCASE])
Sergey Poznyakoff authored
645 646 647 648
    {
      for (p = val; *p; p++)
	*p = mu_toupper (*p);
    }
649 650 651 652 653 654 655 656

  if (env->bvar[B_COMPRESS])
    for (p = val; *p; p++)
      if (*p == '\n')
	*p = ' ';

  if (env->bvar[B_LEFTADJUST])
    {
657
      for (p = val; *p && mu_isspace (*p); p++)
658 659 660 661 662 663 664 665 666
	;
    }
  else
    p = val;
  
  print (env, p, env->ivar[I_COMPWIDTH]);
  return 0;
}

667 668 669 670 671 672 673 674 675 676 677
void
print_component_name (struct eval_env *env)
{
  if (!env->bvar[B_NOCOMPONENT])
    {
      print (env, env->svar[S_COMPONENT], 0);
      if (mu_c_strcasecmp (env->svar[S_COMPONENT], "body"))
	print (env, ": ", 0);
    }
}

678 679 680
int
eval_component (struct eval_env *env, char *name)
{
681
  mu_header_t hdr;
682
  char *val;
683

684
  mu_message_get_header (env->msg, &hdr);
685
  if (mu_header_aget_value (hdr, name, &val))
686 687
    return 0;

688
  print_component_name (env);
689
  mu_list_append (env->printed_fields, name);
690 691
  print_header_value (env, val);
  free (val);
692 693
  if (env->bvar[B_NEWLINE])
    newline (env);
694 695 696 697 698 699
  return 0;
}

int
eval_body (struct eval_env *env)
{
700 701
  mu_stream_t input = NULL;
  mu_stream_t dstr = NULL;
702 703
  char buf[128]; /* FIXME: Fixed size. Bad */
  size_t n;
704
  mu_body_t body = NULL;
705 706
  int nl;
  
707 708
  if (env->bvar[B_DISABLE_BODY])
    return 0;
709

710
  env->prefix = env->svar[S_COMPONENT];
711

712
  mu_message_get_body (env->msg, &body);
713
  mu_body_get_streamref (body, &input);
714 715 716

  if (env->bvar[B_DECODE])
    {
717
      mu_header_t hdr;
718 719
      char *encoding = NULL;

720
      mu_message_get_header (env->msg, &hdr);
721
      mu_header_aget_value (hdr, MU_HEADER_CONTENT_TRANSFER_ENCODING, &encoding);
722 723
      if (encoding)
	{
724 725 726
	  int rc = mu_filter_create (&dstr, input, encoding,
				     MU_FILTER_DECODE, 
				     MU_STREAM_READ);
727 728 729 730 731 732
	  if (rc == 0)
	    input = dstr;
	  free (encoding);
	}
    }
  
733
  while (mu_stream_readline (input, buf, sizeof buf, &n) == 0
734 735
	 && n > 0)
    {
736
      goto_offset (env, env->ivar[I_OFFSET]);
737
      print (env, buf, 0);
738
      nl = buf[n-1] == '\n';
739
    }
740
  mu_stream_destroy (&input);
741 742
  if (!nl && env->bvar[B_NEWLINE])
    newline (env);
743 744 745 746 747 748
  return 0;
}

int
eval_extras (struct eval_env *env)
{
749
  mu_header_t hdr;
750
  size_t i, num;
751
  char *str;
752

753
  print_component_name (env);
754
  mu_message_get_header (env->msg, &hdr);
755
  mu_header_get_field_count (hdr, &num);
756 757
  for (i = 1; i <= num; i++)
    {
758 759 760
      mu_header_aget_field_name (hdr, i, &str);
      if (want_header (env, str)
	  && !header_is_printed (env, str))
761 762
	{
	  goto_offset (env, env->ivar[I_OFFSET]);
763 764 765 766 767 768
	  print (env, str, 0);
	  print (env, ": ", 0);
	  free (str);
	  mu_header_aget_field_value (hdr, i, &str);
	  print_header_value (env, str);
	  if (i < num && env->bvar[B_NEWLINE])
769 770
	    newline (env);
	}
771
      free (str);
772
    }
773 774
  if (env->bvar[B_NEWLINE])
    newline (env);
775 776 777 778
  return 0;
}

int
779
eval_comp (struct eval_env *env, char *compname, mu_list_t format)
780 781 782
{
  struct eval_env lenv = *env;
  
783
  mu_list_do (format, eval_stmt, &lenv);
784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831

  goto_offset (&lenv, lenv.ivar[I_OFFSET]);

  if (!lenv.svar[S_COMPONENT])
    lenv.svar[S_COMPONENT] = compname;
  
  if (strcmp (compname, "extras") == 0)
    eval_extras (&lenv);
  else if (strcmp (compname, "body") == 0)
    eval_body (&lenv);
  else
    eval_component (&lenv, compname);
  
  env->pos = lenv.pos;
  env->nlines = lenv.nlines;
  
  return 0;
}

static int
eval_stmt (void *item, void *data)
{
  mhl_stmt_t *stmt = item;
  struct eval_env *env = data;

  switch (stmt->type)
    {
    case stmt_cleartext:
      print (env, stmt->v.cleartext, 0);
      newline (env);
      break;
      
    case stmt_component:
      eval_comp (env, stmt->v.component.name, stmt->v.component.format);
      break;
      
    case stmt_variable:
      eval_var (env, &stmt->v.variable);
      break;
      
    default:
      abort ();
    }

  return 0;
}

int
832
mhl_format_run (mu_list_t fmt,
833
		int width, int length, int flags,
834
		mu_message_t msg, mu_stream_t output)
835 836 837 838 839 840 841 842
{
  int rc;
  struct eval_env env;

  /* Initialize the environment */
  memset (&env, 0, sizeof (env));

  env.bvar[B_NEWLINE] = 1;
843
  mu_list_create (&env.printed_fields);
844 845
  env.ivar[I_WIDTH] = width;
  env.ivar[I_LENGTH] = length;
846 847 848
  env.bvar[B_CLEARSCREEN] = flags & MHL_CLEARSCREEN;
  env.bvar[B_BELL] = flags & MHL_BELL;
  env.bvar[B_DECODE] = flags & MHL_DECODE;
849
  env.bvar[B_DISABLE_BODY] = flags & MHL_DISABLE_BODY;
850 851 852 853
  env.pos = 0;
  env.nlines = 0;
  env.msg = msg;
  env.output = output;
854 855
  rc = mu_list_do (fmt, eval_stmt, &env);
  mu_list_destroy (&env.printed_fields);
856 857
  return rc;
}