diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2009-08-14 22:53:52 +0300 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2009-08-14 22:53:52 +0300 |
commit | 9b0dee5fa917d5804aa2ca89a186ea8da64578ca (patch) | |
tree | d4725ee292d4d803da77d3a283b1accfeec27f39 /mail | |
parent | f2eb56bb7632e4044173a344964d6739a7ef4b7f (diff) | |
download | mailutils-9b0dee5fa917d5804aa2ca89a186ea8da64578ca.tar.gz mailutils-9b0dee5fa917d5804aa2ca89a186ea8da64578ca.tar.bz2 |
Mail: configurable `headers' output format.
* mail/from.c: Rewrite using format string.
* mail/mail.c (default_setup): Set default value for `headline'.
(main): Fix call to util_do_command.
* mail/mail.h [HAVE_STDARG_H]: Remove conditions.
(mail_compile_headline): New proto.
* mail/mailvar.c (mailvar_tab): New variable "headline".
* mail/util.c: Minor fixes.
* NEWS, doc/programs.texi: Update.
Diffstat (limited to 'mail')
-rw-r--r-- | mail/from.c | 518 | ||||
-rw-r--r-- | mail/mail.c | 3 | ||||
-rw-r--r-- | mail/mail.h | 8 | ||||
-rw-r--r-- | mail/mailvar.c | 4 | ||||
-rw-r--r-- | mail/util.c | 21 |
5 files changed, 457 insertions, 97 deletions
diff --git a/mail/from.c b/mail/from.c index add264347..31e2a9202 100644 --- a/mail/from.c +++ b/mail/from.c @@ -18,27 +18,220 @@ MA 02110-1301 USA */ #include "mail.h" +#include <mu_umaxtostr.h> -/* - * f[rom] [msglist] - */ +#define ALIGN_UNDEF -1 +#define ALIGN_RIGHT 0 +#define ALIGN_LEFT 1 -int -mail_from0 (msgset_t *mspec, mu_message_t msg, void *data) +struct header_call_args +{ + msgset_t *mspec; + mu_message_t msg; + size_t cols_rest; + char *buf; + size_t size; +}; + +struct header_segm +{ + struct header_segm *next; + int align; + size_t width; + void *data; + char *(*get) (struct header_call_args *args, void *data); +}; + +void +header_ensure_space (struct header_call_args *args, size_t size) +{ + if (size > args->size) + { + args->buf = xrealloc (args->buf, size); + args->size = size; + } +} + +static char * +header_buf_string_len (struct header_call_args *args, const char *str, + size_t len) +{ + header_ensure_space (args, len + 1); + memcpy (args->buf, str, len); + args->buf[len] = 0; + return args->buf; +} + +static char * +header_buf_string (struct header_call_args *args, const char *str) +{ + if (!str) + return header_buf_string_len (args, "", 0); + return header_buf_string_len (args, str, strlen (str)); +} + +static void +format_pad (size_t n) +{ + for (; n; n--) + fputc (' ', ofile); +} + +static void +format_headline (struct header_segm *seg, msgset_t *mspec, mu_message_t msg) +{ + int screen_cols = util_getcols () - 2; + int out_cols = 0; + struct header_call_args args; + + args.mspec = mspec; + args.msg = msg; + args.buf = NULL; + args.size = 0; + + for (; seg; seg = seg->next) + { + size_t width, len; + size_t cols_rest = screen_cols - out_cols; + char *p; + + args.cols_rest = cols_rest; + p = seg->get (&args, seg->data); + + if (!p) + p = ""; + len = strlen (p); + + if (seg->width) + width = seg->width; + else + width = len; + if (width > cols_rest) + width = cols_rest; + + if (len > width) + len = width; + + if (seg->align == ALIGN_RIGHT) + { + format_pad (width - len); + fprintf (ofile, "%*.*s", len, len, p); + } + else + { + fprintf (ofile, "%*.*s", len, len, p); + format_pad (width - len); + } + out_cols += width; + } + + fprintf (ofile, "\n"); + free (args.buf); +} + +static void +free_headline (struct header_segm *seg) +{ + while (seg) + { + struct header_segm *next = seg->next; + if (seg->data) + free (seg->data); + free (seg); + seg = next; + } +} + + +static char * +hdr_text (struct header_call_args *args, void *data) +{ + return data; +} + +static char * +hdr_cur (struct header_call_args *args, void *data) +{ + if (is_current_message (args->mspec->msg_part[0])) + return (char*) data; + return " "; +} + +/* %a */ +static char * +hdr_attr (struct header_call_args *args, void *data) { - mu_header_t hdr = NULL; - mu_envelope_t env; mu_attribute_t attr; - char *from = NULL, *subj = NULL, *fromp, *subjp; - int froml, subjl; - char date[80], st[10]; - int cols = util_getcols () - 6; - int cflag; - size_t m_size = 0, m_lines = 0; + char cflag; + + mu_message_get_attribute (args->msg, &attr); + + if (mu_attribute_is_userflag (attr, MAIL_ATTRIBUTE_MBOXED)) + cflag = 'M'; + else if (mu_attribute_is_userflag (attr, MAIL_ATTRIBUTE_PRESERVED)) + cflag = 'P'; + else if (mu_attribute_is_userflag (attr, MAIL_ATTRIBUTE_SAVED)) + cflag = '*'; + else if (mu_attribute_is_userflag (attr, MAIL_ATTRIBUTE_TAGGED)) + cflag = 'T'; + else if (mu_attribute_is_userflag (attr, MAIL_ATTRIBUTE_SHOWN)) + cflag = 'R'; + else if (mu_attribute_is_recent (attr)) + cflag = 'N'; + else if (!mu_attribute_is_read (attr)) + cflag = 'U'; + else + cflag = ' '; + return header_buf_string_len (args, &cflag, 1); +} + +/* %d */ +static char * +hdr_date (struct header_call_args *args, void *data) +{ + char date[80]; + mu_header_t hdr; + mu_message_get_header (args->msg, &hdr); + + date[0] = 0; + if (mailvar_get (NULL, "datefield", mailvar_type_boolean, 0) == 0 + && mu_header_get_value (hdr, MU_HEADER_DATE, + date, sizeof (date), NULL) == 0) + { + time_t t; + if (mu_parse_date (date, &t, NULL) == 0) + strftime (date, sizeof(date), "%a %b %e %H:%M", localtime (&t)); + else + date[0] = 0; + } + + if (date[0] == 0) + { + const char *p; + struct tm tm; + mu_timezone tz; + mu_envelope_t env; + + mu_message_get_envelope (args->msg, &env); + if (mu_envelope_sget_date (env, &p) == 0 + && mu_parse_ctime_date_time (&p, &tm, &tz) == 0) + strftime (date, sizeof(date), "%a %b %e %H:%M", &tm); + } + return header_buf_string (args, date); +} + +/* %f */ +static char * +hdr_from (struct header_call_args *args, void *data) +{ + char *from = NULL; + if (mailvar_get (NULL, "fromfield", mailvar_type_boolean, 0) == 0) { - mu_message_get_header (msg, &hdr); + mu_header_t hdr; + + mu_message_get_header (args->msg, &hdr); if (mu_header_aget_value_unfold (hdr, MU_HEADER_FROM, &from) == 0) { mu_address_t address = NULL; @@ -49,7 +242,8 @@ mail_from0 (msgset_t *mspec, mu_message_t msg, void *data) if (mu_address_sget_email (address, 1, &email) == 0) { - if (mailvar_get (NULL, "showto", mailvar_type_boolean, 0) == 0 + if (mailvar_get (NULL, "showto", + mailvar_type_boolean, 0) == 0 && mail_is_my_name (email)) { char *tmp; @@ -86,87 +280,263 @@ mail_from0 (msgset_t *mspec, mu_message_t msg, void *data) mu_envelope_t env = NULL; const char *sender = ""; - if (mu_message_get_envelope (msg, &env) == 0) + if (mu_message_get_envelope (args->msg, &env) == 0) mu_envelope_sget_sender (env, &sender); from = strdup (sender); } + header_buf_string (args, from); + free (from); + return args->buf; +} + +/* %l */ +static char * +hdr_lines (struct header_call_args *args, void *data) +{ + size_t m_lines; + char buf[UINTMAX_STRSIZE_BOUND]; + mu_message_lines (args->msg, &m_lines); + + return header_buf_string (args, umaxtostr (m_lines, buf)); +} + +/* %m */ +static char * +hdr_number (struct header_call_args *args, void *data) +{ + char buf[UINTMAX_STRSIZE_BOUND]; + return header_buf_string (args, umaxtostr (args->mspec->msg_part[0], buf)); +} + +/* %o */ +static char * +hdr_size (struct header_call_args *args, void *data) +{ + size_t m_size; + char buf[UINTMAX_STRSIZE_BOUND]; + mu_message_size (args->msg, &m_size); + + return header_buf_string (args, umaxtostr (m_size, buf)); +} + +/* %s */ +static char * +hdr_subject (struct header_call_args *args, void *data) +{ + mu_header_t hdr; + char *subj = NULL; + + mu_message_get_header (args->msg, &hdr); mu_header_aget_value_unfold (hdr, MU_HEADER_SUBJECT, &subj); util_rfc2047_decode (&subj); - mu_message_get_attribute (msg, &attr); + header_buf_string (args, subj); + free (subj); + return args->buf; +} + +/* %S */ +static char * +hdr_q_subject (struct header_call_args *args, void *data) +{ + mu_header_t hdr; + char *subj = NULL; + size_t len; + + if (args->cols_rest <= 2) + return "\"\""; - if (mu_attribute_is_userflag (attr, MAIL_ATTRIBUTE_MBOXED)) - cflag = 'M'; - else if (mu_attribute_is_userflag (attr, MAIL_ATTRIBUTE_PRESERVED)) - cflag = 'P'; - else if (mu_attribute_is_userflag (attr, MAIL_ATTRIBUTE_SAVED)) - cflag = '*'; - else if (mu_attribute_is_userflag (attr, MAIL_ATTRIBUTE_TAGGED)) - cflag = 'T'; - else if (mu_attribute_is_userflag (attr, MAIL_ATTRIBUTE_SHOWN)) - cflag = 'R'; - else if (mu_attribute_is_recent (attr)) - cflag = 'N'; - else if (!mu_attribute_is_read (attr)) - cflag = 'U'; - else - cflag = ' '; + mu_message_get_header (args->msg, &hdr); + mu_header_aget_value_unfold (hdr, MU_HEADER_SUBJECT, &subj); + if (!subj) + return ""; + util_rfc2047_decode (&subj); - date[0] = 0; - if (mailvar_get (NULL, "datefield", mailvar_type_boolean, 0) == 0 - && mu_header_get_value (hdr, MU_HEADER_DATE, date, sizeof (date), NULL) == 0) + len = strlen (subj); + if (len + 2 > args->cols_rest) + len = args->cols_rest - 2; + header_ensure_space (args, len + 3); + args->buf[0] = '"'; + memcpy (args->buf + 1, subj, len); + args->buf[len+1] = '"'; + args->buf[len+2] = 0; + free (subj); + return args->buf; +} + + +static struct header_segm * +new_header_segment (int align, size_t width, + void *data, + char *(*get) (struct header_call_args *, void *)) +{ + struct header_segm *seg = xmalloc (sizeof (*seg)); + seg->next = NULL; + seg->align = align; + seg->width = width; + seg->data = data; + seg->get = get; + return seg; +} + +struct header_segm * +compile_headline (const char *str) +{ + struct header_segm *head = NULL, *tail = NULL; + char *text; + int align; + size_t width; + +#define ALIGN_STRING (align == ALIGN_UNDEF ? ALIGN_LEFT : ALIGN_RIGHT) +#define ALIGN_NUMBER (align == ALIGN_UNDEF ? ALIGN_RIGHT : ALIGN_LEFT) +#define ATTACH(p) \ + do \ + { \ + if (!head) \ + head = p; \ + else \ + tail->next = p; \ + tail = p; \ + } \ + while (0) + + while (*str) { - time_t t; - if (mu_parse_date (date, &t, NULL) == 0) + struct header_segm *seg; + size_t len; + char *p = strchr (str, '%'); + if (!p) + len = strlen (str); + else + len = p - str; + if (len) { - strftime (date, sizeof(date), "%a %b %e %H:%M", localtime (&t)); + text = xmalloc (len + 1); + memcpy (text, str, len); + text[len] = 0; + seg = new_header_segment (ALIGN_LEFT, 0, text, hdr_text); + ATTACH (seg); + } + if (!p) + break; + + str = ++p; + + if (*str == '-') + { + str++; + align = ALIGN_LEFT; + } + else if (*str == '+') + { + str++; + align = ALIGN_RIGHT; } else - date[0] = 0; - } + align = ALIGN_UNDEF; + + if (mu_isdigit (*str)) + width = strtoul (str, (char**)&str, 10); + else + width = 0; - if (date[0] == 0) - { - const char *p; - struct tm tm; - mu_timezone tz; + switch (*str++) + { + case '%': + seg = new_header_segment (ALIGN_LEFT, 0, xstrdup ("%"), hdr_text); + break; + + case 'a': /* Message attributes. */ + seg = new_header_segment (ALIGN_STRING, width, NULL, hdr_attr); + break; - mu_message_get_envelope (msg, &env); - if (mu_envelope_sget_date (env, &p) == 0 - && mu_parse_ctime_date_time (&p, &tm, &tz) == 0) - strftime (date, sizeof(date), "%a %b %e %H:%M", &tm); + /* FIXME: %c The score of the message. */ + + case 'd': /* Message date */ + seg = new_header_segment (ALIGN_STRING, width, NULL, hdr_date); + break; + + /* FIXME: %e The indenting level in threaded mode. */ + + case 'f': /* Message sender */ + seg = new_header_segment (ALIGN_STRING, width, NULL, hdr_from); + break; + + /* FIXME: %i The message thread structure. */ + + case 'l': /* The number of lines of the message */ + seg = new_header_segment (ALIGN_NUMBER, width, NULL, hdr_lines); + break; + + case 'm': /* Message number */ + seg = new_header_segment (ALIGN_NUMBER, width, NULL, hdr_number); + break; + + case 'o': /* The number of octets (bytes) in the message */ + seg = new_header_segment (ALIGN_NUMBER, width, NULL, hdr_size); + break; + + case 's': /* Message subject (if any) */ + seg = new_header_segment (ALIGN_STRING, width, NULL, hdr_subject); + break; + + case 'S': /* Message subject (if any) in double quotes */ + seg = new_header_segment (ALIGN_STRING, width, NULL, hdr_q_subject); + break; + + /* FIXME: %t The position in threaded/sorted order. */ + + case '>': /* A `>' for the current message, otherwise ` ' */ + seg = new_header_segment (ALIGN_STRING, width, xstrdup (">"), hdr_cur); + break; + + case '<': /* A `<' for the current message, otherwise ` ' */ + seg = new_header_segment (ALIGN_STRING, width, xstrdup ("<"), hdr_cur); + break; + + default: + mu_error (_("unknown escape: %%%c"), str[-1]); + len = str - p; + text = xmalloc (len); + memcpy (text, p, len-1); + text[len-1] = 0; + seg = new_header_segment (ALIGN_STRING, width, text, hdr_text); + } + ATTACH (seg); } - - mu_message_size (msg, &m_size); - mu_message_lines (msg, &m_lines); - - snprintf (st, sizeof (st), "%3d/%-5d", m_lines, m_size); - - /* The "From" field will take a third of the screen. - Subject will take the rest. - FIXME: This is not quite correct that we use fixed sizes - 18, 16 for the other fields. - */ - froml = cols / 3; - subjl = cols - froml - strlen (st) - 16; - - fromp = from ? from : ""; - subjp = subj ? subj : fromp; - fprintf (ofile, "%c%c%4d %-18.18s %-16.16s %s %.*s\n", - is_current_message (mspec->msg_part[0]) ? '>' : ' ', cflag, - mspec->msg_part[0], - fromp, date, st, (subjl < 0) ? 0 : subjl, subjp); - - free (from); - free (subj); + return head; +#undef ALIGN_STRING +#undef ALIGN_NUMBER +#undef ATTACH +} + +/* FIXME: Should it be part of struct mailvar_variable for "headline"? */ +static struct header_segm *mail_header_line; + +void +mail_compile_headline (struct mailvar_variable *var) +{ + free_headline (mail_header_line); + mail_header_line = compile_headline (var->value.string); +} + + +/* + * f[rom] [msglist] + */ +int +mail_from0 (msgset_t *mspec, mu_message_t msg, void *data) +{ + format_headline (mail_header_line, mspec, msg); return 0; } int mail_from (int argc, char **argv) { - return util_foreach_msg (argc, argv, MSG_NODELETED|MSG_SILENT, mail_from0, NULL); + return util_foreach_msg (argc, argv, MSG_NODELETED|MSG_SILENT, + mail_from0, NULL); } diff --git a/mail/mail.c b/mail/mail.c index d633b035a..805b0e34d 100644 --- a/mail/mail.c +++ b/mail/mail.c @@ -287,6 +287,7 @@ static char *default_setup[] = { "set recursivealiases", "set noinplacealiases", "set fromfield", + "set headline=\"%>%a%4m %18f %16d %3l/%-5o %s\"", /* Start in mail reading mode */ "setq mode=read", @@ -364,7 +365,7 @@ main (int argc, char **argv) /* set defaults for execution */ for (i = 0; i < sizeof (default_setup)/sizeof (default_setup[0]); i++) - util_do_command (default_setup[i]); + util_do_command ("%s", default_setup[i]); util_do_command ("set screen=%d", util_getlines ()); util_do_command ("set columns=%d", util_getcols ()); diff --git a/mail/mail.h b/mail/mail.h index a600d4697..7eeb600a5 100644 --- a/mail/mail.h +++ b/mail/mail.h @@ -45,11 +45,7 @@ #endif #include <sys/wait.h> #include <sys/types.h> -#ifdef HAVE_STDARG_H -# include <stdarg.h> -#else -# include <varargs.h> -#endif +#include <stdarg.h> #include <signal.h> #include <confpaths.h> @@ -200,6 +196,8 @@ extern int mail_folders (int argc, char **argv); extern int mail_followup (int argc, char **argv); extern int mail_from (int argc, char **argv); extern int mail_from0 (msgset_t *mspec, mu_message_t msg, void *data); +extern void mail_compile_headline (struct mailvar_variable *var); + extern int mail_headers (int argc, char **argv); extern int mail_hold (int argc, char **argv); extern int mail_help (int argc, char **argv); diff --git a/mail/mailvar.c b/mail/mailvar.c index d101ec83d..e5cc5ce93 100644 --- a/mail/mailvar.c +++ b/mail/mailvar.c @@ -130,6 +130,10 @@ struct mailvar_symbol mailvar_tab[] = { { "header", }, MAILVAR_TYPEMASK (mailvar_type_boolean), N_("run the `headers' command after entering interactive mode") }, + { { "headline", }, + MAILVAR_TYPEMASK (mailvar_type_string), + N_("format string to use for the header summary"), + mail_compile_headline }, { { "hold", }, MAILVAR_TYPEMASK (mailvar_type_boolean), N_("hold the read or saved messages in the system mailbox") }, diff --git a/mail/util.c b/mail/util.c index 95ec9b61c..2f485cae1 100644 --- a/mail/util.c +++ b/mail/util.c @@ -468,7 +468,7 @@ util_get_homedir () char * util_fullpath (const char *inpath) { - return mu_tilde_expansion(inpath, "/", NULL); + return mu_tilde_expansion (inpath, "/", NULL); } char * @@ -661,7 +661,7 @@ util_slist_to_string (mu_list_t list, const char *delim) } void -util_strcat(char **dest, const char *str) +util_strcat (char **dest, const char *str) { if (!*dest) *dest = strdup (str); @@ -754,26 +754,13 @@ util_save_outgoing (mu_message_t msg, char *savefile) } } -#ifdef HAVE_STDARG_H void util_error (const char *format, ...) -#else -void -util_error (va_alist) - va_dcl -#endif { va_list ap; -#ifdef HAVE_STDARG_H - va_start(ap, format); -#else - char *format; - - va_start (ap); - format = va_arg (ap, char *); -#endif - + va_start (ap, format); + vfprintf (stderr, format, ap); fprintf (stderr, "\n"); |