Commit eea2c4aa eea2c4aa6f4383f3288ddc0c9e5084e36d31d9ac by Sergey Poznyakoff

mail: the "copy" command operates on files by default.

Last argument is treated as a mailbox only if it begins with
a mailbox scheme (mbox://, maildir://, etc).

* mail/copy.c (append_to_mailbox)
(append_to_file): New functions.
(mail_copy0): Depending on whether mailbox scheme is specified,
call one of the above functions.
* doc/texinfo/programs.texi (mail): Fix documentation of the copy
command.
* mail/testsuite/mail/write.exp: Update line/character counts for
[Ss]ave commands.
1 parent be9580f6
......@@ -3236,11 +3236,18 @@ prints the message, immediately following last deleted one.
@table @samp
@item save [[@var{msglist}] @var{file}]
@itemx s [[@var{msglist}] @var{file}]
Takes a message list and a file name and appends each message in turn to
the end of the file. The name of file and number of characters appended
to it is echoed on the terminal. Each saved message is marked for
deletion as if with @code{delete} command, unless the variable
@code{keepsave} is set.
Takes a message list and a file name or mailbox URL and appends each
message in turn to the end of that file or mailbox. Mailbox URLs
begin with mailbox type specifier, such as @samp{mbox://},
@samp{maildir://}, etc. The name of file or mailbox and number of
lines and characters appended to it is echoed on the terminal. When
writing to file, the numbers represent exact number of lines and
characters appended to the file. When @var{file} specifies a mailbox,
these numbers may differ by the amount of lines/characters needed to
represent message envelope for that specific mailbox type.
Each saved message is marked for deletion as if with @code{delete}
command, unless the variable @code{keepsave} is set.
@item Save [@var{msglist}]
@itemx S [@var{msglist}]
Like @code{save}, but the file to append messages to is named after the
......
......@@ -16,6 +16,7 @@
along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */
#include "mail.h"
#include <mailutils/locker.h>
/*
* c[opy] [file]
......@@ -23,59 +24,33 @@
* C[opy] [msglist]
*/
/*
* mail_copy0() is shared between mail_copy() and mail_save().
* argc, argv -- argument count & vector
* mark -- whether we should mark the message as saved.
*/
int
mail_copy0 (int argc, char **argv, int mark)
struct append_stat
{
mu_message_t msg;
mu_mailbox_t mbx;
char *filename = NULL;
msgset_t *msglist = NULL, *mp;
int sender = 0;
size_t total_size = 0, total_lines = 0, size;
int status;
if (mu_isupper (argv[0][0]))
sender = 1;
else if (argc >= 2)
filename = mail_expand_name (argv[--argc]);
else
filename = mu_strdup ("mbox");
size_t size;
size_t lines;
};
if (msgset_parse (argc, argv, MSG_NODELETED|MSG_SILENT, &msglist))
{
if (filename)
free (filename);
return 1;
}
if (sender)
filename = util_outfolder_name (util_get_sender (msglist->msg_part[0], 1));
if (!filename)
{
msgset_free (msglist);
return 1;
}
static int
append_to_mailbox (char const *filename, msgset_t *msglist, int mark,
struct append_stat *totals)
{
int status;
mu_mailbox_t mbx;
msgset_t *mp;
size_t size;
mu_message_t msg;
if ((status = mu_mailbox_create_default (&mbx, filename)) != 0)
{
mu_error (_("Cannot create mailbox %s: %s"), filename,
mu_strerror (status));
free (filename);
msgset_free (msglist);
return 1;
}
if ((status = mu_mailbox_open (mbx, MU_STREAM_WRITE | MU_STREAM_CREAT)) != 0)
{
mu_error (_("Cannot open mailbox %s: %s"), filename,
mu_strerror (status));
free (filename);
msgset_free (msglist);
mu_mailbox_destroy (&mbx);
return 1;
}
......@@ -93,9 +68,9 @@ mail_copy0 (int argc, char **argv, int mark)
}
mu_message_size (msg, &size);
total_size += size;
totals->size += size;
mu_message_lines (msg, &size);
total_lines += size;
totals->lines += size;
if (mark)
{
......@@ -105,12 +80,178 @@ mail_copy0 (int argc, char **argv, int mark)
}
}
if (status == 0)
mu_printf ("\"%s\" %3lu/%-5lu\n", filename,
(unsigned long) total_lines, (unsigned long) total_size);
mu_mailbox_close (mbx);
mu_mailbox_destroy (&mbx);
return 0;
}
static int
append_to_file (char const *filename, msgset_t *msglist, int mark,
struct append_stat *totals)
{
int status;
msgset_t *mp;
mu_stream_t ostr, mstr;
mu_off_t size;
size_t lines;
mu_message_t msg;
mu_locker_t locker;
status = mu_file_stream_create (&ostr, filename,
MU_STREAM_CREAT|MU_STREAM_APPEND);
if (status)
{
mu_error (_("Cannot open output file %s: %s"),
filename, mu_strerror (status));
return 1;
}
status = mu_locker_create (&locker, filename,
MU_LOCKER_KERNEL|MU_LOCKER_RETRY);
if (status)
{
mu_error (_("Cannot create locker %s: %s"),
filename, mu_strerror (status));
mu_stream_unref (ostr);
return 1;
}
mu_locker_lock_mode (locker, mu_lck_exc);
status = mu_locker_lock (locker);
if (status)
{
mu_error (_("Cannot lock %s: %s"),
filename, mu_strerror (status));
mu_locker_destroy (&locker);
mu_stream_unref (ostr);
return 1;
}
for (mp = msglist; mp; mp = mp->next)
{
mu_envelope_t env;
const char *s, *d;
int n;
status = util_get_message (mbox, mp->msg_part[0], &msg);
if (status)
break;
status = mu_message_get_envelope (msg, &env);
if (status)
{
mu_error (_("Cannot get envelope: %s"), mu_strerror (status));
break;
}
status = mu_envelope_sget_sender (env, &s);
if (status)
{
mu_error (_("Cannot get envelope sender: %s"), mu_strerror (status));
break;
}
status = mu_envelope_sget_date (env, &d);
if (status)
{
mu_error (_("Cannot get envelope date: %s"), mu_strerror (status));
break;
}
status = mu_stream_printf (ostr, "From %s %s\n%n", s, d, &n);
if (status)
{
mu_error (_("Write error: %s"), mu_strerror (status));
break;
}
totals->lines++;
totals->size += n;
status = mu_message_get_streamref (msg, &mstr);
if (status)
{
mu_error (_("Cannot get message: %s"), mu_strerror (status));
break;
}
status = mu_stream_copy (ostr, mstr, 0, &size);
if (status)
{
mu_error (_("Cannot append message: %s"), mu_strerror (status));
break;
}
mu_stream_unref (mstr);
mu_stream_write (ostr, "\n", 1, NULL);
totals->size += size + 1;
mu_message_lines (msg, &lines);
totals->lines += lines + 1;
if (mark)
{
mu_attribute_t attr;
mu_message_get_attribute (msg, &attr);
mu_attribute_set_userflag (attr, MAIL_ATTRIBUTE_SAVED);
}
}
mu_stream_close (ostr);
mu_stream_unref (ostr);
mu_locker_unlock (locker);
mu_locker_destroy (&locker);
return 0;
}
/*
* mail_copy0() is shared between mail_copy() and mail_save().
* argc, argv -- argument count & vector
* mark -- whether we should mark the message as saved.
*/
int
mail_copy0 (int argc, char **argv, int mark)
{
char *filename = NULL;
msgset_t *msglist = NULL;
int sender = 0;
struct append_stat totals = { 0, 0 };
int status;
if (mu_isupper (argv[0][0]))
sender = 1;
else if (argc >= 2)
filename = mail_expand_name (argv[--argc]);
else
filename = mu_strdup ("mbox");
if (msgset_parse (argc, argv, MSG_NODELETED|MSG_SILENT, &msglist))
{
if (filename)
free (filename);
return 1;
}
if (sender)
filename = util_outfolder_name (util_get_sender (msglist->msg_part[0], 1));
if (!filename)
{
msgset_free (msglist);
return 1;
}
if (mu_is_proto (filename))
status = append_to_mailbox (filename, msglist, mark, &totals);
else
status = append_to_file (filename, msglist, mark, &totals);
if (status == 0)
mu_printf ("\"%s\" %3lu/%-5lu\n", filename,
(unsigned long) totals.lines, (unsigned long) totals.size);
free (filename);
msgset_free (msglist);
......
......@@ -66,7 +66,7 @@ mail_test "headers" \
# Save messages to the third mailbox
mail_command "set folder=\"$MU_FOLDER_DIR\""
mail_test "save 1 2 +three" \
"\"$MU_FOLDER_DIR/three\" 28/968"
"\"$MU_FOLDER_DIR/three\" 32/1067"
mail_test "headers" \
">* 1 Sergey Poznyakoff Tue Jul 16 12:11 12/390 MBOX"\
......@@ -84,7 +84,7 @@ mail_test "headers" \
# Test uppercase commands (Save and Copy)
mail_command "set outfolder=\"$MU_FOLDER_DIR\""
mail_test "Save" \
"\"$MU_FOLDER_DIR/gray\" 12/390"
"\"$MU_FOLDER_DIR/gray\" 14/438"
mail_test "file \"$MU_FOLDER_DIR/gray\"" \
"Held 2 messages in $MU_FOLDER_DIR/three"
......