From 891f9c44ccf15caccba072d9525f08c374772c62 Mon Sep 17 00:00:00 2001 From: Sergey Poznyakoff Date: Thu, 3 Mar 2005 11:42:20 +0000 Subject: Added to the repository --- frm/common.c | 599 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ frm/from.c | 157 ++++++++++++++++ 2 files changed, 756 insertions(+) create mode 100644 frm/common.c create mode 100644 frm/from.c (limited to 'frm') diff --git a/frm/common.c b/frm/common.c new file mode 100644 index 000000000..bc6ad1a3a --- /dev/null +++ b/frm/common.c @@ -0,0 +1,599 @@ +/* GNU Mailutils -- a suite of utilities for electronic mail + Copyright (C) 1999, 2000, 2001, 2002, 2003, + 2004, 2005 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 */ + +#include + +char *show_field; /* Show this header field instead of the default + `From: Subject:' pair. -f option */ +int show_to; /* Additionally display To: field. -l option */ +int show_number; /* Prefix each line with the message number. -n */ +int frm_debug; + + + +/* Get the number of columns on the screen + First try an ioctl() call, not all shells set the COLUMNS environ. + If ioctl does not succeed on stdout, try it on /dev/tty, as we + may work via a pipe. + + This function was taken from mail/util.c. It should probably reside + in the library */ +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; +} + + +/* Charset magic */ +static char *output_charset = NULL; + +const char * +get_charset () +{ + char *tmp; + + if (!output_charset) + { + char locale[32]; + + memset (locale, 0, sizeof (locale)); + + /* Try to deduce the charset from LC_ALL or LANG variables */ + + tmp = getenv ("LC_ALL"); + if (!tmp) + tmp = getenv ("LANG"); + + if (tmp) + { + char *sp = NULL; + char *lang; + char *terr; + + strncpy (locale, tmp, sizeof (locale) - 1); + + lang = strtok_r (locale, "_", &sp); + terr = strtok_r (NULL, ".", &sp); + output_charset = strtok_r (NULL, "@", &sp); + + if (output_charset) + output_charset = xstrdup (output_charset); + else + output_charset = mu_charset_lookup (lang, terr); + + if (!output_charset) + output_charset = "ASCII"; + } + } + return output_charset; +} + + +/* BIDI support (will be moved to lib when it's ready) */ +#ifdef HAVE_LIBFRIBIDI + +static int fb_charset_num = -1; +FriBidiChar *logical; +char *outstring; +size_t logical_size; + +void +alloc_logical (size_t size) +{ + logical = xmalloc (size * sizeof (logical[0])); + logical_size = size; + outstring = xmalloc (size); +} + +void +puts_bidi (char *string) +{ + if (fb_charset_num == -1) + { + fb_charset_num = fribidi_parse_charset (get_charset ()); + if (fb_charset_num && frm_debug) + mu_error (_("fribidi failed to recognize charset `%s'"), + get_charset ()); + } + + if (fb_charset_num == 0) + puts (string); + else + { + FriBidiStrIndex len; + FriBidiCharType base = FRIBIDI_TYPE_ON; + fribidi_boolean log2vis; + + static FriBidiChar *visual; + static size_t visual_size; + + + len = fribidi_charset_to_unicode (fb_charset_num, + string, strlen (string), + logical); + + if (len + 1 > visual_size) + { + visual_size = len + 1; + visual = xrealloc (visual, visual_size * sizeof *visual); + } + + /* Create a bidi string. */ + log2vis = fribidi_log2vis (logical, len, &base, + /* output */ + visual, NULL, NULL, NULL); + + if (log2vis) + { + FriBidiStrIndex idx, st; + FriBidiStrIndex new_len; + + for (idx = 0; idx < len;) + { + FriBidiStrIndex wid, inlen; + + wid = 3 * logical_size; + st = idx; + + if (fb_charset_num != FRIBIDI_CHARSET_CAP_RTL) + { + while (wid > 0 && idx < len) + wid -= fribidi_wcwidth (visual[idx++]); + } + else + { + while (wid > 0 && idx < len) + { + wid--; + idx++; + } + } + + if (wid < 0 && idx > st + 1) + idx--; + inlen = idx - st; + + new_len = fribidi_unicode_to_charset (fb_charset_num, + visual + st, inlen, + outstring); + printf ("%s", outstring); + } + putchar ('\n'); + } + else + { + /* Print the string as is */ + puts (string); + } + } +} +#else +# define alloc_logical(s) +# define puts_bidi puts +#endif + + +/* Output functions */ + +/* 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_bidi (linebuf); + linebuf[0] = 0; + linepos = 0; + curcol = nextstart = 0; + } + else + putchar ('\n'); + curfield = 0; +} + +void +format_field_simple (const char *fmt, ...) +{ + va_list ap; + if (curfield++) + putchar ('\t'); + va_start (ap, fmt); + vprintf (fmt, ap); + 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_bidi (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++]; +} + +void +init_output (size_t s) +{ + int i; + size_t width = 0; + + if (s == 0) + { + format_field = format_field_simple; + return; + } + + format_field = format_field_align; + + /* Allocate the line buffer */ + linemax = s * MB_LEN_MAX + 1; + linebuf = xmalloc (linemax); + alloc_logical (s); + + /* 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; +} + + +/* + FIXME: Generalize this function and move it + to `mailbox/locale.c'. Do the same with the one + from `from/from.c' and `mail/util.c'... +*/ +static char * +rfc2047_decode_wrapper (char *buf, size_t buflen) +{ + int rc; + char *tmp; + const char *charset = get_charset (); + + if (strcmp (charset, "ASCII") == 0) + return strdup (buf); + + rc = rfc2047_decode (charset, buf, &tmp); + if (rc) + { + if (frm_debug) + mu_error (_("Cannot decode line `%s': %s"), + buf, mu_strerror (rc)); + return strdup (buf); + } + + return tmp; +} + +/* Retrieve the Personal Name from the header To: or From: */ +static int +get_personal (header_t hdr, const char *field, char **personal) +{ + char *hfield; + int status; + + status = header_aget_value_unfold (hdr, field, &hfield); + if (status == 0) + { + address_t address = NULL; + char *s; + + address_create (&address, hfield); + + address_aget_personal (address, 1, &s); + address_destroy (&address); + if (s == NULL) + s = hfield; + else + free (hfield); + + *personal = rfc2047_decode_wrapper (s, strlen (s)); + free (s); + } + return status; +} + +/* Observer action used to perform mailbox scanning. See the comment + to frm_scan below. + + FIXME: The observer action paradygm does not allow for making + procedure-data closure, as it should. So, for the time being + the following static variables are used instead: */ + +static frm_select_t select_message; /* Message selection function */ +static size_t msg_index; /* Index (1-based) of the current + message */ + +/* 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; + + switch (type) + { + case MU_EVT_MESSAGE_ADD: + { + mailbox_t mbox = observer_get_owner (o); + message_t msg = NULL; + header_t hdr = NULL; + attribute_t attr = NULL; + + msg_index++; + + mailbox_get_message (mbox, msg_index, &msg); + message_get_attribute (msg, &attr); + message_get_header (msg, &hdr); + + if (!select_message (msg_index, msg)) + break; + + if (show_number) + format_field ("%4lu:", (u_long) msg_index); + + if (show_to) + { + char *hto; + status = get_personal (hdr, MU_HEADER_TO, &hto); + + if (status == 0) + { + format_field ("(%s)", hto); + free (hto); + } + else + format_field ("(none)"); + } + + if (show_field) /* FIXME: This should be also rfc2047_decode. */ + { + char *hfield; + status = header_aget_value_unfold (hdr, show_field, &hfield); + if (status == 0) + { + format_field ("%s", hfield); + free (hfield); + } + else + format_field (""); + } + else + { + char *tmp; + status = get_personal (hdr, MU_HEADER_FROM, &tmp); + if (status == 0) + { + format_field ("%s", tmp); + free (tmp); + } + else + format_field (""); + + status = header_aget_value_unfold (hdr, MU_HEADER_SUBJECT, + &tmp); + if (status == 0) + { + char *s = rfc2047_decode_wrapper (tmp, strlen (tmp)); + format_field ("%s", s); + free (s); + free (tmp); + } + } + print_line (); + break; + } + + case MU_EVT_MAILBOX_PROGRESS: + /* Noop. */ + break; + } + return 0; +} + +static void +frm_abort (mailbox_t *mbox) +{ + int status; + + if ((status = mailbox_close (*mbox)) != 0) + { + url_t url; + + mu_error (_("Could not close <%s>: %s."), + url_to_string (url), mu_strerror (status)); + exit (3); + } + + mailbox_destroy (mbox); + exit (3); +} + +/* Scan the mailbox MAILBOX_NAME using FUN as the selection function. + FUN takes as its argument message number and the message itself + (message_t). It returns non-zero if that message is to be displayed + and zero otherwise. + + Upon finishing scanning, the function places the overall number of + the messages processed into the memory location pointed to by + TOTAL */ +void +frm_scan (char *mailbox_name, frm_select_t fun, size_t *total) +{ + mailbox_t mbox; + int status; + url_t url; + + status = mailbox_create_default (&mbox, mailbox_name); + if (status != 0) + { + mu_error (_("Could not create mailbox <%s>: %s."), + mailbox_name ? mailbox_name : _("default"), + mu_strerror (status)); + exit (3); + } + + if (frm_debug) + { + mu_debug_t debug; + mailbox_get_debug (mbox, &debug); + mu_debug_set_level (debug, MU_DEBUG_TRACE|MU_DEBUG_PROT); + } + + mailbox_get_url (mbox, &url); + + status = mailbox_open (mbox, MU_STREAM_READ); + if (status == ENOENT) + *total = 0; + else if (status != 0) + { + mu_error (_("Could not open mailbox <%s>: %s."), + url_to_string (url), mu_strerror (status)); + frm_abort (&mbox); + } + else + { + observer_t observer; + observable_t observable; + + select_message = fun; + msg_index = 0; + + observer_create (&observer, mbox); + observer_set_action (observer, action, mbox); + mailbox_get_observable (mbox, &observable); + observable_attach (observable, MU_EVT_MESSAGE_ADD, observer); + + status = mailbox_scan (mbox, 1, total); + if (status != 0) + { + mu_error (_("Could not scan mailbox <%s>: %s."), + url_to_string (url), mu_strerror (status)); + frm_abort (&mbox); + } + + observable_detach (observable, observer); + observer_destroy (&observer, mbox); + + if ((status = mailbox_close (mbox)) != 0) + { + mu_error (_("Could not close <%s>: %s."), + url_to_string (url), mu_strerror (status)); + exit (3); + } + } + mailbox_destroy (&mbox); +} diff --git a/frm/from.c b/frm/from.c new file mode 100644 index 000000000..e8a09553b --- /dev/null +++ b/frm/from.c @@ -0,0 +1,157 @@ +/* GNU Mailutils -- a suite of utilities for electronic mail + Copyright (C) 2005 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 */ + +#include + +int count_only; +char *sender_option; +char *mailbox_name; + +const char *program_version = "from (" PACKAGE_STRING ")"; +static char doc[] = N_("GNU from -- display from and subject"); + +static struct argp_option options[] = { + {"count", 'c', NULL, 0, N_("Just print a count of messages and exit.")}, + {"sender", 's', N_("ADDRESS"), 0, + N_("Print only mail from addresses containing the supplied string") }, + {"file", 'f', N_("FILE"), 0, + N_("Read mail from FILE") }, + {"debug", 'd', NULL, 0, N_("Enable debugging output"), 0}, + {0, 0, 0, 0} +}; + +static error_t +parse_opt (int key, char *arg, struct argp_state *state) +{ + switch (key) + { + case 'c': + count_only = 1; + break; + + case 's': + sender_option = arg; + break; + + case 'f': + mailbox_name = arg; + break; + + case 'd': + frm_debug++; + break; + + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + +static struct argp argp = { + options, + parse_opt, + N_("[OPTIONS] [USER]"), + doc, +}; + +static const char *capa[] = { + "common", + "license", + "mailbox", +#ifdef WITH_TLS + "tls", +#endif + NULL +}; + +static int +from_select (size_t index, message_t msg) +{ + if (count_only) + return 0; + + if (sender_option) + { + int rc = 0; + header_t hdr = NULL; + char *sender; + message_get_header (msg, &hdr); + + if (header_aget_value_unfold (hdr, MU_HEADER_FROM, &sender) == 0) + { + if (strstr (sender, sender_option)) + rc = 1; + free (sender); + } + + return rc; + } + + return 1; +} + +int +main (int argc, char **argv) +{ + int c; + int status = 0; + size_t total; + + /* Native Language Support */ + mu_init_nls (); + + /* register the formats. */ + mu_register_all_mbox_formats (); + + mu_argp_init (program_version, NULL); +#ifdef WITH_TLS + mu_tls_init_client_argp (); +#endif + mu_argp_parse (&argp, &argc, &argv, 0, capa, &c, NULL); + + if (argc - c > 1) + { + mu_error (_("Too many arguments")); + exit (1); + } + else if (argc - c > 0) + { + if (mailbox_name) + { + mu_error (_("Both --from option and user name are specified")); + exit (1); + } + + mailbox_name = xmalloc (strlen (argv[c]) + 2); + mailbox_name[0] = '%'; + strcpy (mailbox_name + 1, argv[c]); + } + + init_output (0); + + frm_scan (mailbox_name, from_select, &total); + + if (count_only) + { + printf (ngettext ("There is %lu message in your incoming mailbox.\n", + "There are %lu messages in your incoming mailbox.\n", + total), + (unsigned long) total); + } + return 0; +} + -- cgit v1.2.1