Commit fdecadb1 fdecadb1c7863e7aafd7e3c387c6ef239c09a1ca by Sergey Poznyakoff

Added to the repository

1 parent 35a260bb
......@@ -68,13 +68,14 @@ libmh_a_SOURCES= \
mh_error.c\
mh_format.c\
mh_init.c\
mh_list.c\
mh_fmtgram.y\
mh_msgset.c\
mh_whatnow.c
noinst_HEADERS = mh.h mh_getopt.h
mhlib_DATA = replcomps
EXTRA_DIST = replcomps
mhlib_DATA = replcomps mhl.format
EXTRA_DIST = replcomps mhl.format
INCLUDES = -I${top_srcdir}/include -I${top_srcdir}/lib -I${top_srcdir}/mailbox @INTLINCS@
AM_CPPFLAGS = -DMHLIBDIR=\"$(mhlibdir)\"
......
/* 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: can't 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 (_("can't 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_MAX 11
/* 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 },
/* 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_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)
{
body_t body = NULL;
stream_t input = NULL;
char buf[128]; /* FIXME: Fixed size. Bad */
size_t n;
env->prefix = env->svar[S_COMPONENT];
message_get_body (env->msg, &body);
body_get_stream (body, &input);
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);
}
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 clearscreen, int bell,
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] = clearscreen;
env.bvar[B_BELL] = bell;
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;
}
; This is the default mhl.format file for Mailutils MH.
;
; GNU Mailutils -- a suite of utilities for electronic mail
; Copyright (C) 2003 Free Software Foundation, Inc.
; Distributed under GPL.
;
: -- using template mhl.format --
overflowtext="***",overflowoffset=5
leftadjust,compwidth=9
ignores="msgid,message-id,received"
Date:formatfield="%<(nodate{text})%{text}%|%(pretty{text})%>"
To:
cc:
:
From:
Subject:
:
extras:nocomponent
:
body:nocomponent,overflowtext="",overflowoffset=0,noleftadjust
; End of mhl.format
\ No newline at end of file