Logo Search packages:      
Sourcecode: mailutils version File versions

mh_list.c

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

   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
   the Free Software Foundation; either version 2, or (at your option)
   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
   along with GNU Mailutils; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA  */

/* 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;
  list_t format;
};

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

static mhl_variable_t *variable_lookup __P((char *name));

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;
  
  for (p = *str; *p && !isspace (*p); p++)
    {
      if (*p == ':')
      {
        int len = p - *str;
        *compname = xmalloc (len + 1);
        memcpy (*compname, *str, len);
        (*compname)[len] = 0;
        *str = p + 1;
        return 0;
      }
    }
  return 1;
}

static void parse_variable (locus_t *loc, list_t formlist, char *str);

static void
parse_cleartext (locus_t *loc, list_t formlist, char *str)
{
  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;
  list_append (formlist, stmt);
}

static void
parse_component (locus_t *loc, list_t formlist, char *compname, char *str)
{
  mhl_stmt_t *stmt = stmt_alloc (stmt_component);
  stmt->v.component.name = compname;
  if (list_create (&stmt->v.component.format))
    {
      mh_error (_("%s:%d: cannot create list"),
            loc->filename,
            loc->line);
      exit (1); /* FIXME */
    }
  parse_variable (loc, stmt->v.component.format, str);
  list_append (formlist, stmt);
}

static void
parse_variable (locus_t *loc, list_t formlist, char *str)
{
  int i;
  int argc;
  char **argv;
  mh_format_t fmt;
  
  if (argcv_get (str, ",=", NULL, &argc, &argv))
    {
      mh_error (_("%s:%d: cannot split string %s"),
            loc->filename,
            loc->line,
            str);
      exit (1);
    }

  for (i = 0; i < argc; i++)
    {
      mhl_stmt_t *stmt = stmt_alloc (stmt_variable);
      char *name = argv[i];
      char *value = NULL;
      mhl_variable_t *var;
      
      var = variable_lookup (name);
      if (!var)
      {
        mh_error (_("%s:%d: unknown variable: %s"),
                loc->filename,
                loc->line,
                argv[i]);
        exit (1);
      }

      if (i + 1 < argc && argv[i+1][0] == '=')
      {
        i++;
        value = argv[++i];
      }

      if ((var->type == dt_flag && value)
        || (var->type != dt_flag && !value))
      {
        mh_error (_("%s:%d: wrong datatype for %s"),
                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))
          {
            mh_error (_("%s:%d: bad format string"),
                  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;
      list_append (formlist, stmt);

      i++;
      if (i < argc && argv[i][0] != ',')
      {
        mh_error (_("%s:%d: syntax error"), loc->filename, loc->line);
        exit (1);
      }
    }

  argcv_free (argc, argv);
}

static int
parse_line (locus_t *loc, list_t formlist, char *str)
{
  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;
}

list_t 
mhl_format_compile (char *name)
{
  FILE *fp;
  list_t formlist;
  char *buf = NULL;
  size_t n = 0;
  locus_t loc;
  
  fp = fopen (name, "r");
  if (!fp)
    {
      mh_error (_("Cannot open file %s: %s"), name, mu_strerror (errno));
      return NULL;
    }

  if (list_create (&formlist))
    {
      fclose (fp);
      mh_error (_("Cannot create list"));
      return NULL;
    }

  loc.filename = name;
  loc.line = 1;
  while (getline (&buf, &n, fp) > 0)
    {
      char *p = buf;

      while (*p && isspace (*p))
      ;

      if (*p == 0 || *p == ';')
      continue;

      parse_line (&loc, formlist, p);
      loc.line++;
    }

  free (buf);
  fclose (fp);
  
  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
mhl_format_destroy (list_t *fmt)
{
  list_do (*fmt, _destroy_stmt, NULL);
  list_destroy (fmt);
}


/* *************************** 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
#define B_DECODE         12
#define B_DISABLE_BODY   13
#define B_MAX            14
                  
/* 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 },
  { B_DECODE,         "decode",         dt_flag },
  
  /* 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
{
  message_t msg;
  stream_t output;
  list_t printed_fields;  /* A list of printed header names */
  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)
{
  return strcasecmp (item, date) == 0;
}

int
header_is_printed (struct eval_env *env, char *name)
{
  return list_do (env->printed_fields, _comp_name, name) == 1;
}

int
want_header (struct eval_env *env, char *name)
{
  char *p, *str = env->svar[S_IGNORES];

  for (p = strchrnul (str, ','); *str; p = strchrnul (str, ','))
    {
      if (strncasecmp (name, str, p - str) == 0)
      return 0;
      str = p;
      if (*str)
      str++;
    }
  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]);
            stream_sequential_write (env->output,
                               env->svar[S_OVERFLOWTEXT], l);
            env->pos += l;
          }
      }
      else
      {
        if (env->prefix && !env->bvar[B_NOCOMPONENT])
          {
            goto_offset (env, env->ivar[I_OFFSET]);
            
            stream_sequential_write (env->output, env->prefix,
                               strlen (env->prefix));
            env->pos += strlen (env->prefix);
            
            goto_offset (env, nloff);
          }
      }
      
      if (env->pos + len > env->ivar[I_WIDTH])
      {
        ovf = 1;
        len = env->ivar[I_WIDTH] - env->pos;
      }
      
      stream_sequential_write (env->output, str, len);
      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++;
      }
      else
      {
        char *p = strchrnul (str, '\n');
        ovf_print (env, str, p - str, nloff);
        str = p;
        if (*p)
          {
            newline (env);
            for (str++; *str && isspace (*str); str++)
            ;
          }
      }
    }
  while (*str);
}

static void
newline (struct eval_env *env)
{
  stream_sequential_write (env->output, "\n", 1);
  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])
      stream_sequential_write (env->output, "\a", 1);
      if (env->bvar[B_CLEARSCREEN])
      stream_sequential_write (env->output, "\f", 1);
      env->nlines = 0;
    }
}

static void
goto_offset (struct eval_env *env, int count)
{
  for (; env->pos < count; env->pos++)
    stream_sequential_write (env->output, " ", 1);
}

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;
    }
    
  if (env->bvar[B_DECODE])
    {
      if (mh_decode_2047 (val, &p) == 0)
      val = p;
    }
  
  if (env->bvar[B_UPPERCASE])
    {
      for (p = val; *p; p++)
      *p = toupper (*p);
    }

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

  if (env->bvar[B_LEFTADJUST])
    {
      for (p = val; *p && isspace (*p); p++)
      ;
    }
  else
    p = val;
  
  print (env, p, env->ivar[I_COMPWIDTH]);
  return 0;
}

int
eval_component (struct eval_env *env, char *name)
{
  header_t hdr;
  char *val;
  
  message_get_header (env->msg, &hdr);
  if (header_aget_value (hdr, name, &val))
    return 0;

  list_append (env->printed_fields, name);
  print_header_value (env, val);
  free (val);
  return 0;
}

int
eval_body (struct eval_env *env)
{
  stream_t input = NULL;
  stream_t dstr = NULL;
  char buf[128]; /* FIXME: Fixed size. Bad */
  size_t n;
  body_t body = NULL;

  if (env->bvar[B_DISABLE_BODY])
    return 0;
  
  env->prefix = env->svar[S_COMPONENT];

  message_get_body (env->msg, &body);
  body_get_stream (body, &input);

  if (env->bvar[B_DECODE])
    {
      header_t hdr;
      char *encoding = NULL;

      message_get_header (env->msg, &hdr);
      header_aget_value (hdr, MU_HEADER_CONTENT_TRANSFER_ENCODING, &encoding);
      if (encoding)
      {
        int rc = filter_create(&dstr, input, encoding,
                         MU_FILTER_DECODE, MU_STREAM_READ);
        if (rc == 0)
          input = dstr;
        free (encoding);
      }
    }
  
  stream_seek (input, 0, SEEK_SET);
  while (stream_sequential_readline (input, buf, sizeof buf, &n) == 0
       && n > 0)
    {
      buf[n] = 0;
      print (env, buf, 0);
    }
  if (dstr)
    stream_destroy (&dstr, stream_get_owner (dstr));
  return 0;
}

int
eval_extras (struct eval_env *env)
{
  header_t hdr;
  size_t i, num;
  char buf[512];

  message_get_header (env->msg, &hdr);
  header_get_field_count (hdr, &num);
  for (i = 1; i <= num; i++)
    {
      header_get_field_name (hdr, i, buf, sizeof buf, NULL);
      if (want_header (env, buf)
        && !header_is_printed (env, buf))
      {
        goto_offset (env, env->ivar[I_OFFSET]);
        print (env, buf, 0);
        print (env, ":", 0);
        header_get_field_value (hdr, i, buf, sizeof buf, NULL);
        print_header_value (env, buf);
        if (env->bvar[B_NEWLINE])
          newline (env);
      }
    }
  return 0;
}

int
eval_comp (struct eval_env *env, char *compname, list_t format)
{
  struct eval_env lenv = *env;
  
  list_do (format, eval_stmt, &lenv);

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

  if (!lenv.svar[S_COMPONENT])
    lenv.svar[S_COMPONENT] = compname;
  
  if (!lenv.bvar[B_NOCOMPONENT])
    {
      print (&lenv, lenv.svar[S_COMPONENT], 0);
      if (strcmp (compname, "body"))
      print (&lenv, ":", 0);
    }

  if (strcmp (compname, "extras") == 0)
    eval_extras (&lenv);
  else if (strcmp (compname, "body") == 0)
    eval_body (&lenv);
  else
    eval_component (&lenv, compname);
  
  if (lenv.bvar[B_NEWLINE])
    newline (&lenv);

  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
mhl_format_run (list_t fmt,
            int width, int length, int flags,
            message_t msg, stream_t output)
{
  int rc;
  struct eval_env env;

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

  env.bvar[B_NEWLINE] = 1;
  list_create (&env.printed_fields);
  env.ivar[I_WIDTH] = width;
  env.ivar[I_LENGTH] = length;
  env.bvar[B_CLEARSCREEN] = flags & MHL_CLEARSCREEN;
  env.bvar[B_BELL] = flags & MHL_BELL;
  env.bvar[B_DECODE] = flags & MHL_DECODE;
  env.bvar[B_DISABLE_BODY] = flags & MHL_DISABLE_BODY;
  env.pos = 0;
  env.nlines = 0;
  env.msg = msg;
  env.output = output;
  rc = list_do (fmt, eval_stmt, &env);
  list_destroy (&env.printed_fields);
  return rc;
}

Generated by  Doxygen 1.6.0   Back to index