summaryrefslogtreecommitdiffabout
path: root/frm
authorSergey Poznyakoff <gray@gnu.org.ua>2005-02-27 20:13:11 (GMT)
committer Sergey Poznyakoff <gray@gnu.org.ua>2005-02-27 20:13:11 (GMT)
commitb4c656a1d9eb814463b064ecff74848b901f55f4 (patch) (side-by-side diff)
treee37a6676afdfc205130bf6d8cfdd11a52d2f8c42 /frm
parent00770353c05ec929e71fd6e0f7530a31825c0595 (diff)
downloadmailutils-b4c656a1d9eb814463b064ecff74848b901f55f4.tar.gz
mailutils-b4c656a1d9eb814463b064ecff74848b901f55f4.tar.bz2
Implemented -t. Changed implementation of -Q.
Rewritten action() to correctly handle multibyte characters and to provide for eventual using the BIDI algorithm. (print_line,format_field,format_field_simple) (format_field_align): New functions (util_getcols): Measure /dev/tty if unable to open stdout. (to work correctly with piped output). (rfc2047_decode_wrapper): Cache the determined locale. (get_personal): Do not limit the returned string length. Do not attempt to decode the raw header text, since parse822 will skip non-ascii characters, decode the already obtained personal part instead.
Diffstat (limited to 'frm') (more/less context) (ignore whitespace changes)
-rw-r--r--frm/frm.c384
1 files changed, 258 insertions, 126 deletions
diff --git a/frm/frm.c b/frm/frm.c
index f203b2e..576d061 100644
--- a/frm/frm.c
+++ b/frm/frm.c
@@ -24,6 +24,7 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
+#include <fcntl.h>
#include <sys/types.h>
#ifdef HAVE_TERMIOS_H
# include <termios.h>
@@ -31,6 +32,16 @@
#include <sys/ioctl.h>
#include <sys/stat.h>
+#ifdef HAVE_ICONV_H
+# include <iconv.h>
+#endif
+#ifndef MB_LEN_MAX
+# define MB_LEN_MAX 4
+#endif
+
+#include <mbswidth.h>
+#include <xalloc.h>
+
#include <mailutils/address.h>
#include <mailutils/argp.h>
#include <mailutils/attribute.h>
@@ -50,16 +61,17 @@
#include <mailutils/mutil.h>
#include <mailutils/mime.h>
-static char *show_field;
-static int show_to;
-static int show_from = 1;
-static int show_subject = 1;
-static int show_number;
-static int show_summary;
-static int be_quiet;
-static int align = 1;
-static int show_query;
-static int dbug;
+static char *show_field; /* Show this header field instead of the default
+ `From: Subject:' pair. -f option */
+static int show_to; /* Additionally display To: field. -l option */
+static int show_number; /* Prefix each line with the message number. -n */
+static int show_summary; /* Summarize the number of messages by message
+ status in each mailbox. -S option */
+static int be_quiet; /* Quiet mode. -q option. */
+static int show_query; /* Additional flag toggled by -q to display
+ a one-line summary for each mailbox */
+static int align = 0; /* Tidy mode. -t option. */
+static int dbug; /* Debug level. -d option.*/
#define IS_READ 0x001
#define IS_OLD 0x010
@@ -179,10 +191,139 @@ static struct argp_option options[] = {
{"query", 'q', NULL, 0, N_("Print a message if the mailbox contains some unread mail"), 0},
{"summary",'S', NULL, 0, N_("Print a summary of messages"), 0},
{"status", 's', N_("STATUS"), 0, attr_help, 0},
- {"align", 't', NULL, 0, N_("Try to align"), 0},
+ {"align", 't', NULL, 0, N_("Tidy mode: align subject lines"), 0},
{0, 0, 0, 0}
};
+/* Number of columns in output:
+
+ Maximum 4 message number, to, from, subject -ln
+ Default 2 from, subject [none]
+ Minimum 1 FIELD -f FIELD
+*/
+
+static int numfields; /* Number of output fields */
+static int fieldwidth[4]; /* Field start positions */
+static char *linebuf; /* Output line buffer */
+static size_t linemax; /* Size of linebuf */
+static size_t linepos; /* Position in the output line buffer */
+static int curfield; /* Current output field */
+static int nextstart; /* Start position of the next field */
+static int curcol; /* Current output column */
+
+typedef void (*fmt_formatter) (const char *fmt, ...);
+
+static fmt_formatter format_field;
+
+void
+print_line ()
+{
+ if (linebuf)
+ {
+ puts (linebuf);
+ linebuf[0] = 0;
+ linepos = 0;
+ curcol = nextstart = 0;
+ curfield = 0;
+ }
+ else
+ putchar ('\n');
+}
+
+void
+format_field_simple (const char *fmt, ...)
+{
+ va_list ap;
+ va_start (ap, fmt);
+ vprintf (fmt, ap);
+ putchar (' ');
+ va_end (ap);
+}
+
+void
+format_field_align (const char *fmt, ...)
+{
+ size_t n, width;
+ va_list ap;
+
+ va_start (ap, fmt);
+ if (nextstart != 0)
+ {
+ if (curcol >= nextstart)
+ {
+ if (curfield == numfields - 1)
+ {
+ puts (linebuf);
+ linepos = 0;
+ printf ("%*s", nextstart, "");
+ }
+ else
+ {
+ linebuf[linepos++] = ' ';
+ curcol++;
+ }
+ }
+ else if (nextstart != curcol)
+ {
+ /* align to field start */
+ n = snprintf (linebuf + linepos, linemax - linepos,
+ "%*s", nextstart - curcol, "");
+ linepos += n;
+ curcol = nextstart;
+ }
+ }
+
+ n = vsnprintf (linebuf + linepos, linemax - linepos, fmt, ap);
+ va_end (ap);
+
+ /* Compute output width */
+ if (curfield == numfields - 1)
+ {
+ for ( ; n > 0; n--)
+ {
+ int c = linebuf[linepos + n];
+ linebuf[linepos + n] = 0;
+ width = mbswidth (linebuf + linepos, 0);
+ if (width <= fieldwidth[curfield])
+ break;
+ linebuf[linepos + n] = c;
+ }
+ }
+ else
+ width = mbswidth (linebuf + linepos, 0);
+
+ /* Increment counters */
+ linepos += n;
+ curcol += width;
+ nextstart += fieldwidth[curfield++];
+}
+
+/*
+ * Get the number of columns on the screen
+ * First try an ioctl() call not all shells set the COLUMNS environ.
+ * This function was taken from mail/util.c.
+ */
+int
+util_getcols (void)
+{
+ struct winsize ws;
+
+ ws.ws_col = ws.ws_row = 0;
+ if (ioctl (1, TIOCGWINSZ, (char *) &ws) < 0)
+ {
+ int fd = open ("/dev/tty", O_RDWR);
+ ioctl (fd, TIOCGWINSZ, (char *) &ws);
+ close (fd);
+ }
+ if (ws.ws_row == 0)
+ {
+ const char *columns = getenv ("COLUMNS");
+ if (columns)
+ ws.ws_col = strtol (columns, NULL, 10);
+ }
+ return ws.ws_col;
+}
+
static error_t
parse_opt (int key, char *arg, struct argp_state *state)
{
@@ -194,8 +335,6 @@ parse_opt (int key, char *arg, struct argp_state *state)
case 'f':
show_field = arg;
- show_from = 0;
- show_subject = 0;
align = 0;
break;
@@ -210,11 +349,6 @@ parse_opt (int key, char *arg, struct argp_state *state)
case 'Q':
/* Very silent. */
be_quiet += 2;
- if (freopen ("/dev/null", "w", stdout) == NULL)
- {
- perror (_("Cannot be very quiet"));
- exit (3);
- }
break;
case 'q':
@@ -233,6 +367,42 @@ parse_opt (int key, char *arg, struct argp_state *state)
case 't':
align = 1;
break;
+
+ case ARGP_KEY_FINI:
+ if (align && (linemax = util_getcols ()))
+ {
+ int i;
+ size_t width = 0;
+
+ format_field = format_field_align;
+
+ /* Allocate the line buffer */
+ linemax = linemax * MB_LEN_MAX + 1;
+ linebuf = xmalloc (linemax);
+
+ /* Set up column widths */
+ if (show_number)
+ fieldwidth[numfields++] = 5;
+
+ if (show_to)
+ fieldwidth[numfields++] = 20;
+
+ if (show_field)
+ fieldwidth[numfields++] = 0;
+ else
+ {
+ fieldwidth[numfields++] = 20;
+ fieldwidth[numfields++] = 0;
+ }
+
+ for (i = 0; i < numfields; i++)
+ width += fieldwidth[i];
+
+ fieldwidth[numfields-1] = util_getcols () - width;
+ }
+ else
+ format_field = format_field_simple;
+ break;
default:
return ARGP_ERR_UNKNOWN;
@@ -267,36 +437,43 @@ static const char *frm_argp_capa[] = {
static char *
rfc2047_decode_wrapper (char *buf, size_t buflen)
{
- char locale[32];
- char *charset = NULL;
- char *tmp;
int rc;
+ char *tmp;
+ static char *charset = NULL;
- memset (locale, 0, sizeof (locale));
+ if (!charset)
+ {
+ char locale[32];
- /* Try to deduce the charset from LC_ALL or LANG variables */
+ memset (locale, 0, sizeof (locale));
- tmp = getenv ("LC_ALL");
- if (!tmp)
- tmp = getenv ("LANG");
+ /* Try to deduce the charset from LC_ALL or LANG variables */
- if (tmp)
- {
- char *sp = NULL;
- char *lang;
- char *terr;
+ tmp = getenv ("LC_ALL");
+ if (!tmp)
+ tmp = getenv ("LANG");
- strncpy (locale, tmp, sizeof (locale) - 1);
+ if (tmp)
+ {
+ char *sp = NULL;
+ char *lang;
+ char *terr;
- lang = strtok_r (locale, "_", &sp);
- terr = strtok_r (NULL, ".", &sp);
- charset = strtok_r (NULL, "@", &sp);
+ strncpy (locale, tmp, sizeof (locale) - 1);
+
+ lang = strtok_r (locale, "_", &sp);
+ terr = strtok_r (NULL, ".", &sp);
+ charset = strtok_r (NULL, "@", &sp);
+
+ if (!charset)
+ charset = mu_charset_lookup (lang, terr);
- if (!charset)
- charset = mu_charset_lookup (lang, terr);
+ if (!charset)
+ charset = "ASCII";
+ }
}
-
- if (!charset)
+
+ if (strcmp (charset, "ASCII") == 0)
return strdup (buf);
rc = rfc2047_decode (charset, buf, &tmp);
@@ -313,69 +490,46 @@ rfc2047_decode_wrapper (char *buf, size_t buflen)
/* Retrieve the Personal Name from the header To: or From: */
static int
-get_personal (header_t hdr, const char *field, char *personal, size_t buflen)
+get_personal (header_t hdr, const char *field, char **personal)
{
- char hfield[512];
+ char *hfield;
int status;
- /* Empty string. */
- *hfield = '\0';
-
- status = header_get_value_unfold (hdr, field, hfield, sizeof (hfield), NULL);
+ status = header_aget_value_unfold (hdr, field, &hfield);
if (status == 0)
{
address_t address = NULL;
- size_t len = 0;
-
- char *s = rfc2047_decode_wrapper (hfield, strlen (hfield));
- address_create (&address, s);
- free (s);
+ char *s;
+
+ address_create (&address, hfield);
- address_get_personal (address, 1, personal, buflen, &len);
+ address_aget_personal (address, 1, &s);
address_destroy (&address);
+ if (s == NULL)
+ s = hfield;
+ else
+ free (hfield);
- if (len == 0)
- strncpy (personal, hfield, buflen)[buflen - 1] = '\0';
+ *personal = rfc2047_decode_wrapper (s, strlen (s));
+ free (s);
}
return status;
}
-static struct {
+static struct
+{
size_t index;
size_t new;
size_t read;
size_t unread;
} counter;
-/*
- * Get the number of columns on the screen
- * First try an ioctl() call not all shells set the COLUMNS environ.
- * This function was taken from mail/util.c.
- */
-int
-util_getcols (void)
-{
- struct winsize ws;
-
- ws.ws_col = ws.ws_row = 0;
- if ((ioctl(1, TIOCGWINSZ, (char *) &ws) < 0) || ws.ws_row == 0)
- {
- const char *columns = getenv ("COLUMNS");
- if (columns)
- ws.ws_col = strtol (columns, NULL, 10);
- }
-
- /* FIXME: Should we exit()/abort() if col <= 0 ? */
- return ws.ws_col;
-}
-
/* Observable action is being called on discovery of each message. */
/* FIXME: The format of the display is poorly done, please correct. */
static int
action (observer_t o, size_t type)
{
int status;
- int col_cnt = 0;
switch (type)
{
@@ -412,79 +566,57 @@ action (observer_t o, size_t type)
break;
if (show_number)
- {
- printf ("%4lu: ", (u_long) counter.index);
- col_cnt += 6;
- }
-
- if (show_field) /* FIXME: This should be also rfc2047_decode. */
- {
- char hfield[256];
- status = header_get_value_unfold (hdr, show_field, hfield,
- sizeof (hfield), NULL);
- if (status == 0)
- printf ("%s", hfield);
- }
+ format_field ("%4lu: ", (u_long) counter.index);
if (show_to)
{
- char hto[16];
- status = get_personal (hdr, MU_HEADER_TO, hto, sizeof (hto));
+ char *hto;
+ status = get_personal (hdr, MU_HEADER_TO, &hto);
if (status == 0)
{
- printf ("(%s) ", hto);
- col_cnt += strlen (hto) + 3;
+ format_field ("(%s) ", hto);
+ free (hto);
}
else
- {
- printf ("(-----) ");
- col_cnt += 8;
- }
+ format_field ("(none)");
}
- if (show_from)
+ if (show_field) /* FIXME: This should be also rfc2047_decode. */
{
- char hfrom[32];
- status = get_personal (hdr, MU_HEADER_FROM, hfrom, sizeof (hfrom));
+ char *hfield;
+ status = header_aget_value_unfold (hdr, show_field, &hfield);
if (status == 0)
{
- printf ("%s\t", hfrom);
- col_cnt += strlen (hfrom) + 4; // tab=4, sigh.
+ format_field ("%s", hfield);
+ free (hfield);
}
else
+ format_field ("");
+ }
+ else
+ {
+ char *tmp;
+ status = get_personal (hdr, MU_HEADER_FROM, &tmp);
+ if (status == 0)
{
- printf ("-----\t");
- col_cnt += 9;
+ format_field ("%s", tmp);
+ free (tmp);
}
- }
-
- /*
- A temporary fix for (correct) displaying:
- util_getcols() - col_cnt - ~1. It's ugly, I know.
- */
+ else
+ format_field ("");
- if (show_subject)
- {
- char *hsubject;
status = header_aget_value_unfold (hdr, MU_HEADER_SUBJECT,
- &hsubject);
+ &tmp);
if (status == 0)
{
- char *s = rfc2047_decode_wrapper (hsubject, strlen (hsubject));
- char out[80];
- int fspace = util_getcols () - col_cnt - 1;
-
- fspace = (fspace > (sizeof (out) - 1))
- ? (sizeof (out) - 1) : fspace;
-
- memset (out, 0, sizeof (out));
- strncpy (out, s, fspace);
- printf ("%s", out);
+ char *s = rfc2047_decode_wrapper (tmp, strlen (tmp));
+ format_field ("%s", s);
free (s);
+ free (tmp);
}
}
- putchar ('\n');
+ print_line ();
break;
}

Return to:

Send suggestions and report system problems to the System administrator.