/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 1999-2021 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 3, 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, see . */
#include
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. */
#define IS_READ 0x001
#define IS_OLD 0x010
#define IS_NEW 0x100
static int select_attribute;
static struct attr_tab {
char *name; /* Attribute name */
int code; /* Corresponding IS_.* flag */
size_t len; /* Minimum abbreviation length */
} attr_tab[] = {
{ "new", IS_NEW, 0 },
{ "old", IS_OLD, 0 },
{ "unread", IS_OLD, 0 },
{ "read", IS_READ, 0 },
{ NULL }
};
/* Attribute table handling */
/* Prepares the table for use. Computes minimum abbreviation lengths. */
static void
prepare_attrs (void)
{
struct attr_tab *p, *q;
for (p = attr_tab; p->name; p++)
{
const char *name = p->name;
size_t len = strlen (name);
size_t n = 1;
for (q = attr_tab; q->name; q++)
{
if (p != q)
{
const char *str = q->name;
size_t slen = strlen (str);
if (memcmp (name, str, n) == 0)
{
for (n++;
memcmp (name, str, n) == 0
&& n < len
&& n < slen; n++)
;
q->len = n < slen ? n : slen;
}
}
}
p->len = n < len ? n : len;
}
}
/* Translates the textual status representation to the corresponding
IS_.* flag */
static int
decode_attr (char const *arg)
{
struct attr_tab *p;
int len = strlen (arg);
int pretendents = 0;
for (p = attr_tab; p->name; p++)
{
const char *str = p->name;
if (str[0] == arg[0])
{
if (len < p->len)
pretendents++;
else if (len > strlen (str))
continue;
if (memcmp (str, arg, p->len) == 0)
return p->code;
}
}
if (pretendents)
mu_error (_("%s: ambiguous abbreviation"), arg);
else
mu_error (_("%s: unknown attribute"), arg);
return 0;
}
static void
cli_show_field (struct mu_parseopt *po, struct mu_option *opt, char const *arg)
{
show_field = mu_strdup (arg);
align = 0;
}
static void
cli_Quiet (struct mu_parseopt *po, struct mu_option *opt, char const *arg)
{
be_quiet += 2;
}
static void
cli_query (struct mu_parseopt *po, struct mu_option *opt, char const *arg)
{
be_quiet++;
show_query = 1;
}
static void
cli_status (struct mu_parseopt *po, struct mu_option *opt, char const *arg)
{
select_attribute = decode_attr (arg);
}
static struct mu_option frm_options[] = {
{ "debug", 'd', NULL, MU_OPTION_DEFAULT,
N_("enable debugging output"),
mu_c_incr, &frm_debug },
{ "field", 'f', N_("NAME"), MU_OPTION_DEFAULT,
N_("header field to display"),
mu_c_string, &show_field, cli_show_field },
{ "to", 'l', NULL, MU_OPTION_DEFAULT,
N_("include the To: information"),
mu_c_bool, &show_to },
{ "number", 'n', NULL, MU_OPTION_DEFAULT,
N_("display message numbers"),
mu_c_bool, &show_number },
{ "Quiet", 'Q', NULL, MU_OPTION_DEFAULT,
N_("do not display headers"),
mu_c_int, &be_quiet, cli_Quiet },
{ "query", 'q', NULL, MU_OPTION_DEFAULT,
N_("print a message if the mailbox contains some unread mail"),
mu_c_int, &be_quiet, cli_query },
{ "summary",'S', NULL, MU_OPTION_DEFAULT,
N_("print a summary of messages"),
mu_c_bool, &show_summary },
{ "status", 's', N_("STATUS"), 0,
/* TRANSLATORS: Please do *not* translate the words "new", "unread",
"old" and "read". They are keywords. */
N_("select messages with the specific attribute:"
" new, unread, old (same as unread) or read (or any unambiguous"
" abbreviation of these)"),
mu_c_string, NULL, cli_status},
{ "align", 't', NULL, MU_OPTION_DEFAULT,
N_("tidy mode: align subject lines"),
mu_c_bool, &align },
MU_OPTION_END
}, *options[] = { frm_options, NULL };
static struct mu_cli_setup cli = {
options,
NULL,
N_("GNU frm -- display From: lines."),
N_("[URL ...]"),
};
static char *capa[] = {
"debug",
"mailbox",
"locking",
NULL
};
static struct
{
size_t new;
size_t read;
size_t unread;
} counter;
static int selected;
static int
frm_select (size_t index, mu_message_t msg)
{
mu_header_t hdr = NULL;
mu_attribute_t attr = NULL;
mu_message_get_attribute (msg, &attr);
mu_message_get_header (msg, &hdr);
if (mu_attribute_is_read (attr))
counter.read++;
else if (mu_attribute_is_seen (attr))
counter.unread++;
else if (mu_attribute_is_recent (attr))
counter.new++;
if (((select_attribute & IS_READ) && (!mu_attribute_is_read (attr)))
|| ((select_attribute & IS_NEW) && (!mu_attribute_is_recent (attr)))
|| ((select_attribute & IS_OLD) && (!mu_attribute_is_seen (attr))))
return 0;
if (select_attribute)
selected++;
return !be_quiet;
}
/* This is a clone of the elm program call "frm". It is a good example on
how to use the observable(callback) of libmailbox. "frm" has to
be very interactive, it is not possible to call mu_mailbox_messages_count()
and wait for the scanning to finish before displaying. As soon as the scan
finds a new message we want to know about it. This is done by registering
an observable type MU_MAILBOX_MSG_ADD. The rest is formatting code. */
int
frm (char *mailbox_name)
{
size_t total;
int status;
selected = 0;
counter.new = counter.read = counter.unread = 0;
frm_scan (mailbox_name, frm_select, &total);
if (show_summary)
{
if (total == 0)
mu_printf (_("Folder contains no messages."));
else
{
char *delim = "";
mu_printf (_("Folder contains "));
if (counter.new)
{
mu_printf (ngettext ("%lu new message",
"%lu new messages",
counter.new),
(u_long) counter.new);
delim = ", ";
}
if (counter.unread)
{
mu_printf ("%s", delim);
mu_printf (ngettext ("%lu unread message",
"%lu unread messages",
counter.unread),
(u_long) counter.unread);
delim = ", ";
}
if (counter.read)
{
mu_printf ("%s", delim);
mu_printf (ngettext ("%lu read message",
"%lu read messages",
counter.read),
(u_long) counter.read);
}
/* TRANSLATORS: This dot finishes the sentence
"Folder contains XXX messages."
Leave it as it is unless your language requires to reorder
the parts of speach in the message
*/
mu_printf (_("."));
}
mu_printf ("\n");
}
else if (show_query)
{
if (total > 0)
mu_printf (_("There are messages in that folder.\n"));
else
mu_printf (_("No messages in that folder!\n"));
}
/* EXIT STATUS
Frm returns a zero status ("true") if messages matching `status' are
present. Frm returns 1 if no messages matching `status' are present,
but there are some messages, returns 2 if there are no messages at
all, or returns 3 if an error occurred. */
if (selected)
status = 0;
else if (total > 0)
status = 1;
else
status = 2;
return status;
}
int
main (int argc, char **argv)
{
int status = 0;
size_t s;
/* Native Language Support */
MU_APP_INIT_NLS ();
prepare_attrs ();
/* register the formats. */
mu_register_all_mbox_formats ();
mu_auth_register_module (&mu_auth_tls_module);
mu_cli (argc, argv, &cli, capa, NULL, &argc, &argv);
if (align && (s = util_getcols ()))
init_output (s);
else
init_output (0);
/* have an argument */
if (argc == 0)
status = frm (NULL);
else if (argc == 1)
status = frm (argv[0]);
else
{
int i;
for (i = 0; i < argc; i++)
{
mu_printf ("%s:\n", argv[i]);
status = frm (argv[i]);
}
}
return status;
}