Commit ec100ee6 ec100ee63e55ba60369363c72d6d2e1fbc3663a2 by Sergey Poznyakoff

Implement MH show utility.

* mh/show.c: New file.
* NEWS: Document changes.
* libmailutils/stream/message_stream.c (copy_trimmed_value): Fix
string length calculation to allow for empty lines.
* mh/.gitignore: Update.
* mh/Makefile.am: Build show.
* mh/TODO: Document show.
* mh/mh_getopt.h: Add new option codes.
* mh/mh_init.c (mh_expand_name): Change handling of the
last argument. All callers updated.
(mh_draft_name): Remove.
1 parent d7dec968
GNU mailutils NEWS -- history of user-visible changes. 2013-03-21
GNU mailutils NEWS -- history of user-visible changes. 2013-04-12
Copyright (C) 2002-2013 Free Software Foundation, Inc.
See the end of file for copying conditions.
......@@ -176,6 +176,10 @@ input folders of type IMAP or IMAPS. A sample usage is:
Note the `-truncate' option.
** MH show and msgchk
Implemented two new programs: show and msgchk.
** MH: multiple sources
The `inc' command is able to incorporate messages from several
......
......@@ -138,7 +138,8 @@ copy_trimmed_value (const char *str)
size_t len;
str = mu_str_skip_class (str, MU_CTYPE_SPACE);
len = strlen (str) - 1;
p = mu_str_skip_class_comp (str, MU_CTYPE_ENDLN);
len = p - str;
p = malloc (len + 1);
memcpy (p, str, len);
p[len] = 0;
......
......@@ -33,6 +33,7 @@ repl
rmf
rmm
scan
show
send
sortm
whatnow
......
......@@ -42,6 +42,7 @@ bin_PROGRAMS = \
rmf\
rmm\
scan\
show\
send\
sortm\
whatnow\
......
......@@ -42,6 +42,7 @@ State Nice Utility Comments
+ 20 sortm
+ 20 prompter
* 20 msgchk --date functionality not implemented
+ 20 show next, prev not implemented
Utilities In Alphabetical Order
===============================
......@@ -64,6 +65,7 @@ repl
rmf
rmm
send
show
sortm
whatnow
whom
......
......@@ -126,7 +126,7 @@ opt_handler (int key, char *arg, struct argp_state *state)
break;
case ARG_FILE:
wh_env.file = mh_expand_name (NULL, arg, 0);
wh_env.file = mh_expand_name (NULL, arg, NAME_ANY);
break;
case ARG_NODRAFTFOLDER:
......@@ -218,7 +218,7 @@ main (int argc, char **argv)
}
else if (folder_set)
{
wh_env.file = mh_expand_name (NULL, "draft", 0);
wh_env.file = mh_expand_name (NULL, "draft", NAME_ANY);
}
else
{
......
......@@ -459,7 +459,7 @@ action_print ()
}
else
{
char *p = mh_expand_name (NULL, mh_current_folder (), 0);
char *p = mh_expand_name (NULL, mh_current_folder (), NAME_ANY);
_scan (p, 1);
free (p);
}
......@@ -854,7 +854,8 @@ fixup_private (const char *name, const char *value, void *data)
int
action_pack ()
{
const char *folder_dir = mh_expand_name (NULL, mh_current_folder (), 0);
const char *folder_dir = mh_expand_name (NULL, mh_current_folder (),
NAME_ANY);
mu_mailbox_t mbox = mh_open_folder (mh_current_folder (), MU_STREAM_RDWR);
struct pack_tab *pack_tab;
size_t i, count, start;
......
......@@ -505,7 +505,7 @@ main (int argc, char **argv)
}
if (build_only || !draftfolder)
wh_env.file = mh_expand_name (NULL, "draft", 0);
wh_env.file = mh_expand_name (NULL, "draft", NAME_ANY);
else if (draftfolder)
{
if (mh_draft_message (draftfolder, draftmessage, &wh_env.file))
......
......@@ -306,9 +306,13 @@ size_t mh_msgset_first (mu_msgset_t msgset);
size_t mh_msgset_first_uid (mu_msgset_t msgset);
int mh_msgset_single_message (mu_msgset_t msgset);
#define NAME_ANY 0
#define NAME_FOLDER 1
#define NAME_FILE 2
char *mh_expand_name (const char *base, const char *name, int what);
char *mh_get_dir (void);
int mh_find_file (const char *name, char **resolved_name);
char *mh_expand_name (const char *base, const char *name, int is_folder);
void mh_quote (const char *in, char **out);
void mh_expand_aliases (mu_message_t msg, mu_address_t *addr_to,
mu_address_t *addr_cc,
......
......@@ -246,7 +246,7 @@ push_source (const char *name, int fail)
char *filename;
int ex = 0;
filename = mh_expand_name (NULL, name, 0);
filename = mh_expand_name (NULL, name, NAME_ANY);
if (stat (filename, &st))
{
if (fail)
......
......@@ -135,6 +135,7 @@ enum mh_arg {
ARG_NORMALIZE,
ARG_NOSERIALONLY,
ARG_NOSHOW,
ARG_NOSHOWPROC,
ARG_NOSTORE,
ARG_NOT,
ARG_NOTEXTFIELD,
......@@ -168,6 +169,7 @@ enum mh_arg {
ARG_SEQUENCE,
ARG_SERIALONLY,
ARG_SHOW,
ARG_SHOWPROC,
ARG_SOURCE,
ARG_SPLIT,
ARG_STORE,
......
......@@ -76,7 +76,7 @@ _mh_init_global_context ()
p = getenv ("CONTEXT");
if (!p)
p = MH_CONTEXT_FILE;
ctx_name = mh_expand_name (NULL, p, 0);
ctx_name = mh_expand_name (NULL, p, NAME_ANY);
mu_mh_context = mh_read_property_file (ctx_name, 0);
......
......@@ -437,7 +437,7 @@ mh_open_folder (const char *folder, int flags)
mu_mailbox_t mbox = NULL;
char *name;
name = mh_expand_name (NULL, folder, 1);
name = mh_expand_name (NULL, folder, NAME_FOLDER);
if ((flags & MU_STREAM_CREAT) && mh_check_folder (name, 1))
exit (0);
......@@ -482,7 +482,7 @@ mh_get_dir ()
}
char *
mh_expand_name (const char *base, const char *name, int is_folder)
mh_expand_name (const char *base, const char *name, int what)
{
char *p = NULL;
char *namep = NULL;
......@@ -495,11 +495,13 @@ mh_expand_name (const char *base, const char *name, int is_folder)
char *cwd = mu_getcwd ();
char *tmp = mh_safe_make_file_name (cwd, namep);
free (cwd);
if (what == NAME_FILE)
return tmp;
free (namep);
namep = tmp;
}
if (is_folder)
if (what == NAME_FOLDER)
{
if (memcmp (namep, "mh:/", 4) == 0)
return namep;
......@@ -510,7 +512,17 @@ mh_expand_name (const char *base, const char *name, int is_folder)
namep);
}
else if (namep[0] != '/')
mu_asprintf (&p, "%s/%s", base ? base : mu_folder_directory (), namep);
{
if (what == NAME_FILE)
{
char *cwd = mu_getcwd ();
p = mh_safe_make_file_name (cwd, namep);
free (cwd);
}
else
p = mh_safe_make_file_name (base ? base : mu_folder_directory (),
namep);
}
else
return namep;
......@@ -543,7 +555,7 @@ mh_find_file (const char *name, char **resolved_name)
return errno;
}
s = mh_expand_name (NULL, name, 0);
s = mh_expand_name (NULL, name, NAME_ANY);
if (access (s, R_OK) == 0)
{
*resolved_name = s;
......@@ -554,7 +566,8 @@ mh_find_file (const char *name, char **resolved_name)
_("cannot access %s: %s"), s, mu_strerror (errno));
free (s);
s = mh_expand_name (mh_global_profile_get ("mhetcdir", MHLIBDIR), name, 0);
s = mh_expand_name (mh_global_profile_get ("mhetcdir", MHLIBDIR), name,
NAME_ANY);
if (access (s, R_OK) == 0)
{
*resolved_name = s;
......@@ -690,7 +703,7 @@ mh_file_to_message (const char *folder, const char *file_name)
if (folder)
{
tmp_name = mh_expand_name (folder, file_name, 0);
tmp_name = mh_expand_name (folder, file_name, NAME_ANY);
msg = _file_to_message (tmp_name);
free (tmp_name);
}
......@@ -853,14 +866,6 @@ mh_annotate (mu_message_t msg, const char *field, const char *text, int date)
}
char *
mh_draft_name ()
{
return mh_expand_name (mh_global_profile_get ("Draft-Folder",
mu_folder_directory ()),
"draft", 0);
}
char *
mh_create_message_id (int subpart)
{
char *p;
......
......@@ -22,7 +22,7 @@ static char *
private_sequence_name (const char *name)
{
char *p;
char *mbox_dir = mh_expand_name (NULL, mh_current_folder (), 0);
char *mbox_dir = mh_expand_name (NULL, mh_current_folder (), NAME_ANY);
mu_asprintf (&p, "atr-%s-%s", name, mbox_dir);
free (mbox_dir);
return p;
......
......@@ -2978,7 +2978,7 @@ main (int argc, char **argv)
return 1;
}
input_file = mh_expand_name (mu_folder_directory (),
argc == 1 ? argv[0] : "draft", 0);
argc == 1 ? argv[0] : "draft", NAME_ANY);
message = mh_file_to_message (NULL, input_file);
if (!message)
return 1;
......
......@@ -418,14 +418,14 @@ main (int argc, char **argv)
}
if (build_only)
wh_env.file = mh_expand_name (draftfolder, "reply", 0);
wh_env.file = mh_expand_name (draftfolder, "reply", NAME_ANY);
else if (draftfolder)
{
if (mh_draft_message (draftfolder, draftmessage, &wh_env.file))
return 1;
}
else
wh_env.file = mh_expand_name (draftfolder, "draft", 0);
wh_env.file = mh_expand_name (draftfolder, "draft", NAME_ANY);
wh_env.draftfile = wh_env.file;
make_draft (mbox, DISP_REPLACE, &wh_env);
......
......@@ -196,7 +196,7 @@ main (int argc, char **argv)
name = cur_folder_path;
}
else
name = mh_expand_name (NULL, folder_name, 0);
name = mh_expand_name (NULL, folder_name, NAME_ANY);
if (recursive)
status = recrmf (name);
else
......
......@@ -325,7 +325,7 @@ elt_fixup (void *item, void *data)
{
struct list_elt *elt = item;
elt->file_name = mh_expand_name (draftfolder, elt->file_name, 0);
elt->file_name = mh_expand_name (draftfolder, elt->file_name, NAME_ANY);
if (checkdraft (elt->file_name))
exit (1);
elt->msg = mh_file_to_message (NULL, elt->file_name);
......@@ -357,7 +357,7 @@ read_mts_profile ()
{
mu_property_t local_profile;
name = mh_expand_name (MHLIBDIR, "mtstailor", 0);
name = mh_expand_name (MHLIBDIR, "mtstailor", NAME_ANY);
mts_profile = mh_read_property_file (name, 1);
name = mu_tilde_expansion ("~/.mtstailor", MU_HIERARCHY_DELIMITER, NULL);
......@@ -886,7 +886,8 @@ main (int argc, char **argv)
addfolder (dfolder, 0, NULL);
else
{
char *df = mh_expand_name (mu_folder_directory (), "draft", 0);
char *df = mh_expand_name (mu_folder_directory (), "draft",
NAME_ANY);
if (checkdraft (df))
exit (1);
if (!use_draft && !mh_usedraft (df))
......@@ -906,7 +907,7 @@ main (int argc, char **argv)
{
/* -draftfolder is supplied */
draftfolder = mh_expand_name (mu_folder_directory (),
draftfolder, 0);
draftfolder, NAME_ANY);
use_draft = 1;
mesg_list_fixup ();
if (mu_list_is_empty (mesg_list) || argc != 0)
......
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 2002, 2005, 2007-2012 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 <http://www.gnu.org/licenses/>. */
/* MH show command */
#include <mh.h>
static char doc[] = N_("GNU MH show")"\v"
N_("Use -help to obtain the list of traditional MH options.");
static char args_doc[] = N_("[+FOLDER] [MSGLIST]");
#define ARG_PASS ARG_MAX
static struct argp_option options[] = {
{"draft", ARG_DRAFT, NULL, 0,
N_("show the draft file") },
{"file", ARG_FILE, N_("FILE"), 0,
N_("show this file") },
{"header", ARG_HEADER, N_("BOOL"), OPTION_ARG_OPTIONAL,
N_("display a description of the message before the message itself") },
{"noheader", ARG_HEADER, NULL, OPTION_HIDDEN, "" },
{"showproc", ARG_SHOWPROC, N_("PROGRAM"), 0,
N_("use PROGRAM to show messages")},
{"noshowproc", ARG_NOSHOWPROC, NULL, 0,
N_("disable the use of the \"showproc:\" profile component") },
{"form", ARG_FORM, N_("FILE"), 0,
N_("read format from given file")},
{"moreproc", ARG_MOREPROC, N_("PROG"), 0,
N_("use this PROG as pager"), },
{"nomoreproc", ARG_NOMOREPROC, NULL, 0,
N_("disable the use of moreproc") },
{"length", ARG_LENGTH, N_("NUMBER"), 0,
N_("set output screen length")},
{"width", ARG_WIDTH, N_("NUMBER"), 0,
N_("set output width")},
{NULL}
};
/* Traditional MH options */
struct mh_option mh_option[] = {
{ "draft" },
{ "file", MH_OPT_ARG, "file" },
{ "header", MH_OPT_BOOL },
{ "showproc", MH_OPT_ARG, "program" },
{ "noshowproc" },
{ "form", MH_OPT_ARG, "formatfile"},
{ "width", MH_OPT_ARG, "number"},
{ "length", MH_OPT_ARG, "number"},
{ "moreproc", MH_OPT_ARG, "program"},
{ "nomoreproc" },
{ NULL }
};
int use_draft;
int use_showproc = 1;
int header_option = 1;
char *file;
const char *showproc;
char **showargv;
size_t showargc;
size_t showargmax;
const char *mhnproc;
static void
addarg (const char *arg)
{
if (showargc == showargmax)
showargv = mu_2nrealloc (showargv, &showargmax, sizeof showargv[0]);
showargv[showargc++] = (char*) arg;
}
static void
insarg (char *arg)
{
addarg (arg);
if (showargc > 2)
{
char *p = showargv[showargc-1];
memmove (showargv + 2, showargv + 1,
sizeof (showargv[0]) * (showargc - 2));
showargv[1] = p;
}
}
static const char *
findopt (int key)
{
struct argp_option *p;
for (p = options; p->name; p++)
if (p->key == key)
return p->name;
abort ();
}
static error_t
opt_handler (int key, char *arg, struct argp_state *state)
{
switch (key)
{
case ARG_FOLDER:
mh_set_current_folder (arg);
break;
case ARG_DRAFT:
if (use_draft || file)
argp_error (state, _("only one file at a time!"));
use_draft = 1;
break;
case ARG_FILE:
if (use_draft || file)
argp_error (state, _("only one file at a time!"));
file = mh_expand_name (NULL, arg, NAME_FILE);
break;
case ARG_HEADER:
header_option = is_true (arg);
break;
case ARG_NOHEADER:
header_option = 0;
break;
case ARG_SHOWPROC:
showproc = arg;
break;
case ARG_NOSHOWPROC:
use_showproc = 0;
break;
case ARG_NOMOREPROC:
case ARG_FORM:
case ARG_MOREPROC:
case ARG_LENGTH:
case ARG_WIDTH:
addarg (findopt (key));
if (arg)
addarg (arg);
break;
default:
return ARGP_ERR_UNKNOWN;
}
return 0;
}
static int
resolve_mime (size_t num, mu_message_t msg, void *data)
{
int *ismime = data;
mu_message_is_multipart (msg, ismime);
return *ismime;
}
static int
resolve_msg (size_t num, mu_message_t msg, void *data)
{
char *path = data;
mh_message_number (msg, &num);
addarg (mh_safe_make_file_name (path, mu_umaxtostr (0, num)));
return 0;
}
static int
adduid (size_t num, void *data)
{
addarg (mu_umaxtostr (0, num));
return 0;
}
static int
printheader (size_t num, void *data)
{
mu_printf ("(Message %s:%lu)\n", (char*) data, (unsigned long)num);
return 1;
}
static void
checkfile (char *file)
{
int ismime = 0;
if (!showproc)
{
mu_message_t msg = mh_file_to_message (NULL, file);
mu_message_is_multipart (msg, &ismime);
mu_message_unref (msg);
}
if (ismime)
{
showproc = mhnproc;
insarg ("-show");
addarg ("-file");
}
addarg (file);
}
int
main (int argc, char **argv)
{
int index = 0;
mu_mailbox_t mbox;
mu_msgset_t msgset;
const char *p;
/* Native Language Support */
MU_APP_INIT_NLS ();
showargmax = 2;
showargc = 1;
showargv = mu_calloc (showargmax, sizeof showargv[0]);
mh_argp_init ();
mh_argp_parse (&argc, &argv, 0, options, mh_option, args_doc, doc,
opt_handler, NULL, &index);
mbox = mh_open_folder (mh_current_folder (), MU_STREAM_RDWR);
argc -= index;
argv += index;
if (use_draft || file)
{
if (argc)
{
mu_error (_("only one file at a time!"));
exit (1);
}
}
else
mh_msgset_parse (&msgset, mbox, argc, argv, "cur");
/* 1. If !use_showproc set showproc to /bin/cat and go to X.
2. If -file or -draft is given
2.a. If showproc is set, go to 2.c
2.b. If the file is a multipart one, set showproc to mhn with -file
and -show options and go to X
2.c. Add file to the argument list and go to X.
3. Scan all messages to see if there are MIME ones. If so, set
showproc to mhn with the -show option, transfer message UIDs
to showproc arguments, and go to X
4. Otherwise set showproc from the showproc variable in .mh_profile
X. If showproc is not set, set it to PAGER ("/usr/bin/more" by default).
Y. Execute showproc with the collected argument list.
*/
if (!use_showproc)
showproc = "/bin/cat";
else
showproc = mh_global_profile_get ("showproc", NULL);
mhnproc = mh_global_profile_get ("mhnproc", "mhn");
if (use_draft || file)
{
if (file)
checkfile (file);
else
{
const char *dfolder = mh_global_profile_get ("Draft-Folder", NULL);
if (dfolder)
{
int ismime = 0;
mu_mailbox_close (mbox);
mu_mailbox_destroy (&mbox);
mbox = mh_open_folder (dfolder, MU_STREAM_RDWR);
mh_msgset_parse (&msgset, mbox, 0, NULL, "cur");
mu_msgset_foreach_message (msgset, resolve_mime, &ismime);
if (ismime)
{
showproc = mhnproc;
insarg ("-show");
addarg ("-file");
}
mu_msgset_foreach_message (msgset, resolve_msg,
(void *) dfolder);
}
else
checkfile (mh_expand_name (mu_folder_directory (),
"draft", NAME_ANY));
}
}
else
{
mu_url_t url;
size_t n;
int ismime = 0;
const char *path;
mu_mailbox_get_url (mbox, &url);
mu_url_sget_path (url, &path);
if (!showproc)
{
mu_msgset_foreach_message (msgset, resolve_mime, &ismime);
if (ismime)
{
showproc = mhnproc;
insarg ("-show");
}
}
if (ismime)
mu_msgset_foreach_msguid (msgset, adduid, NULL);
else
mu_msgset_foreach_message (msgset, resolve_msg, (void*) path);
p = mh_global_profile_get ("Unseen-Sequence", NULL);
if (p)
{
mh_seq_delete (mbox, p, msgset, 0);
mh_global_save_state ();
}
if (header_option && mu_msgset_count (msgset, &n) == 0 && n == 1)
mu_msgset_foreach_msguid (msgset, printheader, (void*) path);
}
mu_stream_flush (mu_strout);
mu_mailbox_close (mbox);
mu_mailbox_destroy (&mbox);
if (!showproc)
{
showproc = getenv ("PAGER");
if (!showproc)
showproc = "/usr/bin/more";
}
addarg (NULL);
p = strrchr (showproc, '/');
showargv[0] = (char*) (p ? p + 1 : showproc);
execvp (showproc, showargv);
mu_error (_("unable to exec %s: %s"), showargv[0], mu_strerror (errno));
return 1;
}
......@@ -108,7 +108,7 @@ main (int argc, char **argv)
return 1;
}
else
wh_env.draftfile = mh_expand_name (draftfolder, "draft", 0);
wh_env.draftfile = mh_expand_name (draftfolder, "draft", NAME_ANY);
wh_env.draftfile = wh_env.file;
return mh_whatnow (&wh_env, initial_edit);
......
......@@ -118,6 +118,6 @@ main (int argc, char **argv)
mu_folder_directory ());
return mh_whom (mh_expand_name (draft_folder, name, 0), check_recipients) ?
1 : 0;
return mh_whom (mh_expand_name (draft_folder, name, NAME_ANY),
check_recipients) ? 1 : 0;
}
......