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. ...@@ -3236,11 +3236,18 @@ prints the message, immediately following last deleted one.
3236 @table @samp 3236 @table @samp
3237 @item save [[@var{msglist}] @var{file}] 3237 @item save [[@var{msglist}] @var{file}]
3238 @itemx s [[@var{msglist}] @var{file}] 3238 @itemx s [[@var{msglist}] @var{file}]
3239 Takes a message list and a file name and appends each message in turn to 3239 Takes a message list and a file name or mailbox URL and appends each
3240 the end of the file. The name of file and number of characters appended 3240 message in turn to the end of that file or mailbox. Mailbox URLs
3241 to it is echoed on the terminal. Each saved message is marked for 3241 begin with mailbox type specifier, such as @samp{mbox://},
3242 deletion as if with @code{delete} command, unless the variable 3242 @samp{maildir://}, etc. The name of file or mailbox and number of
3243 @code{keepsave} is set. 3243 lines and characters appended to it is echoed on the terminal. When
3244 writing to file, the numbers represent exact number of lines and
3245 characters appended to the file. When @var{file} specifies a mailbox,
3246 these numbers may differ by the amount of lines/characters needed to
3247 represent message envelope for that specific mailbox type.
3248
3249 Each saved message is marked for deletion as if with @code{delete}
3250 command, unless the variable @code{keepsave} is set.
3244 @item Save [@var{msglist}] 3251 @item Save [@var{msglist}]
3245 @itemx S [@var{msglist}] 3252 @itemx S [@var{msglist}]
3246 Like @code{save}, but the file to append messages to is named after the 3253 Like @code{save}, but the file to append messages to is named after the
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
16 along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */ 16 along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */
17 17
18 #include "mail.h" 18 #include "mail.h"
19 #include <mailutils/locker.h>
19 20
20 /* 21 /*
21 * c[opy] [file] 22 * c[opy] [file]
...@@ -23,59 +24,33 @@ ...@@ -23,59 +24,33 @@
23 * C[opy] [msglist] 24 * C[opy] [msglist]
24 */ 25 */
25 26
26 /* 27 struct append_stat
27 * mail_copy0() is shared between mail_copy() and mail_save().
28 * argc, argv -- argument count & vector
29 * mark -- whether we should mark the message as saved.
30 */
31 int
32 mail_copy0 (int argc, char **argv, int mark)
33 { 28 {
34 mu_message_t msg; 29 size_t size;
35 mu_mailbox_t mbx; 30 size_t lines;
36 char *filename = NULL; 31 };
37 msgset_t *msglist = NULL, *mp;
38 int sender = 0;
39 size_t total_size = 0, total_lines = 0, size;
40 int status;
41
42 if (mu_isupper (argv[0][0]))
43 sender = 1;
44 else if (argc >= 2)
45 filename = mail_expand_name (argv[--argc]);
46 else
47 filename = mu_strdup ("mbox");
48
49 if (msgset_parse (argc, argv, MSG_NODELETED|MSG_SILENT, &msglist))
50 {
51 if (filename)
52 free (filename);
53 return 1;
54 }
55
56 if (sender)
57 filename = util_outfolder_name (util_get_sender (msglist->msg_part[0], 1));
58
59 if (!filename)
60 {
61 msgset_free (msglist);
62 return 1;
63 }
64 32
33 static int
34 append_to_mailbox (char const *filename, msgset_t *msglist, int mark,
35 struct append_stat *totals)
36 {
37 int status;
38 mu_mailbox_t mbx;
39 msgset_t *mp;
40 size_t size;
41 mu_message_t msg;
42
65 if ((status = mu_mailbox_create_default (&mbx, filename)) != 0) 43 if ((status = mu_mailbox_create_default (&mbx, filename)) != 0)
66 { 44 {
67 mu_error (_("Cannot create mailbox %s: %s"), filename, 45 mu_error (_("Cannot create mailbox %s: %s"), filename,
68 mu_strerror (status)); 46 mu_strerror (status));
69 free (filename);
70 msgset_free (msglist);
71 return 1; 47 return 1;
72 } 48 }
73 if ((status = mu_mailbox_open (mbx, MU_STREAM_WRITE | MU_STREAM_CREAT)) != 0) 49 if ((status = mu_mailbox_open (mbx, MU_STREAM_WRITE | MU_STREAM_CREAT)) != 0)
74 { 50 {
75 mu_error (_("Cannot open mailbox %s: %s"), filename, 51 mu_error (_("Cannot open mailbox %s: %s"), filename,
76 mu_strerror (status)); 52 mu_strerror (status));
77 free (filename); 53 mu_mailbox_destroy (&mbx);
78 msgset_free (msglist);
79 return 1; 54 return 1;
80 } 55 }
81 56
...@@ -93,9 +68,9 @@ mail_copy0 (int argc, char **argv, int mark) ...@@ -93,9 +68,9 @@ mail_copy0 (int argc, char **argv, int mark)
93 } 68 }
94 69
95 mu_message_size (msg, &size); 70 mu_message_size (msg, &size);
96 total_size += size; 71 totals->size += size;
97 mu_message_lines (msg, &size); 72 mu_message_lines (msg, &size);
98 total_lines += size; 73 totals->lines += size;
99 74
100 if (mark) 75 if (mark)
101 { 76 {
...@@ -104,13 +79,179 @@ mail_copy0 (int argc, char **argv, int mark) ...@@ -104,13 +79,179 @@ mail_copy0 (int argc, char **argv, int mark)
104 mu_attribute_set_userflag (attr, MAIL_ATTRIBUTE_SAVED); 79 mu_attribute_set_userflag (attr, MAIL_ATTRIBUTE_SAVED);
105 } 80 }
106 } 81 }
82
83 mu_mailbox_close (mbx);
84 mu_mailbox_destroy (&mbx);
85 return 0;
86 }
87
88 static int
89 append_to_file (char const *filename, msgset_t *msglist, int mark,
90 struct append_stat *totals)
91 {
92 int status;
93 msgset_t *mp;
94 mu_stream_t ostr, mstr;
95 mu_off_t size;
96 size_t lines;
97 mu_message_t msg;
98 mu_locker_t locker;
99
100 status = mu_file_stream_create (&ostr, filename,
101 MU_STREAM_CREAT|MU_STREAM_APPEND);
102 if (status)
103 {
104 mu_error (_("Cannot open output file %s: %s"),
105 filename, mu_strerror (status));
106 return 1;
107 }
108
109 status = mu_locker_create (&locker, filename,
110 MU_LOCKER_KERNEL|MU_LOCKER_RETRY);
111 if (status)
112 {
113 mu_error (_("Cannot create locker %s: %s"),
114 filename, mu_strerror (status));
115 mu_stream_unref (ostr);
116 return 1;
117 }
118 mu_locker_lock_mode (locker, mu_lck_exc);
119
120 status = mu_locker_lock (locker);
121 if (status)
122 {
123 mu_error (_("Cannot lock %s: %s"),
124 filename, mu_strerror (status));
125 mu_locker_destroy (&locker);
126 mu_stream_unref (ostr);
127 return 1;
128 }
129
130 for (mp = msglist; mp; mp = mp->next)
131 {
132 mu_envelope_t env;
133 const char *s, *d;
134 int n;
135
136 status = util_get_message (mbox, mp->msg_part[0], &msg);
137 if (status)
138 break;
107 139
140 status = mu_message_get_envelope (msg, &env);
141 if (status)
142 {
143 mu_error (_("Cannot get envelope: %s"), mu_strerror (status));
144 break;
145 }
146
147 status = mu_envelope_sget_sender (env, &s);
148 if (status)
149 {
150 mu_error (_("Cannot get envelope sender: %s"), mu_strerror (status));
151 break;
152 }
153
154 status = mu_envelope_sget_date (env, &d);
155 if (status)
156 {
157 mu_error (_("Cannot get envelope date: %s"), mu_strerror (status));
158 break;
159 }
160
161 status = mu_stream_printf (ostr, "From %s %s\n%n", s, d, &n);
162 if (status)
163 {
164 mu_error (_("Write error: %s"), mu_strerror (status));
165 break;
166 }
167
168 totals->lines++;
169 totals->size += n;
170
171 status = mu_message_get_streamref (msg, &mstr);
172 if (status)
173 {
174 mu_error (_("Cannot get message: %s"), mu_strerror (status));
175 break;
176 }
177
178 status = mu_stream_copy (ostr, mstr, 0, &size);
179 if (status)
180 {
181 mu_error (_("Cannot append message: %s"), mu_strerror (status));
182 break;
183 }
184
185 mu_stream_unref (mstr);
186
187 mu_stream_write (ostr, "\n", 1, NULL);
188
189 totals->size += size + 1;
190 mu_message_lines (msg, &lines);
191 totals->lines += lines + 1;
192
193 if (mark)
194 {
195 mu_attribute_t attr;
196 mu_message_get_attribute (msg, &attr);
197 mu_attribute_set_userflag (attr, MAIL_ATTRIBUTE_SAVED);
198 }
199 }
200
201 mu_stream_close (ostr);
202 mu_stream_unref (ostr);
203
204 mu_locker_unlock (locker);
205 mu_locker_destroy (&locker);
206
207 return 0;
208 }
209
210 /*
211 * mail_copy0() is shared between mail_copy() and mail_save().
212 * argc, argv -- argument count & vector
213 * mark -- whether we should mark the message as saved.
214 */
215 int
216 mail_copy0 (int argc, char **argv, int mark)
217 {
218 char *filename = NULL;
219 msgset_t *msglist = NULL;
220 int sender = 0;
221 struct append_stat totals = { 0, 0 };
222 int status;
223
224 if (mu_isupper (argv[0][0]))
225 sender = 1;
226 else if (argc >= 2)
227 filename = mail_expand_name (argv[--argc]);
228 else
229 filename = mu_strdup ("mbox");
230
231 if (msgset_parse (argc, argv, MSG_NODELETED|MSG_SILENT, &msglist))
232 {
233 if (filename)
234 free (filename);
235 return 1;
236 }
237
238 if (sender)
239 filename = util_outfolder_name (util_get_sender (msglist->msg_part[0], 1));
240
241 if (!filename)
242 {
243 msgset_free (msglist);
244 return 1;
245 }
246
247 if (mu_is_proto (filename))
248 status = append_to_mailbox (filename, msglist, mark, &totals);
249 else
250 status = append_to_file (filename, msglist, mark, &totals);
251
108 if (status == 0) 252 if (status == 0)
109 mu_printf ("\"%s\" %3lu/%-5lu\n", filename, 253 mu_printf ("\"%s\" %3lu/%-5lu\n", filename,
110 (unsigned long) total_lines, (unsigned long) total_size); 254 (unsigned long) totals.lines, (unsigned long) totals.size);
111
112 mu_mailbox_close (mbx);
113 mu_mailbox_destroy (&mbx);
114 255
115 free (filename); 256 free (filename);
116 msgset_free (msglist); 257 msgset_free (msglist);
......
...@@ -66,7 +66,7 @@ mail_test "headers" \ ...@@ -66,7 +66,7 @@ mail_test "headers" \
66 # Save messages to the third mailbox 66 # Save messages to the third mailbox
67 mail_command "set folder=\"$MU_FOLDER_DIR\"" 67 mail_command "set folder=\"$MU_FOLDER_DIR\""
68 mail_test "save 1 2 +three" \ 68 mail_test "save 1 2 +three" \
69 "\"$MU_FOLDER_DIR/three\" 28/968" 69 "\"$MU_FOLDER_DIR/three\" 32/1067"
70 70
71 mail_test "headers" \ 71 mail_test "headers" \
72 ">* 1 Sergey Poznyakoff Tue Jul 16 12:11 12/390 MBOX"\ 72 ">* 1 Sergey Poznyakoff Tue Jul 16 12:11 12/390 MBOX"\
...@@ -84,7 +84,7 @@ mail_test "headers" \ ...@@ -84,7 +84,7 @@ mail_test "headers" \
84 # Test uppercase commands (Save and Copy) 84 # Test uppercase commands (Save and Copy)
85 mail_command "set outfolder=\"$MU_FOLDER_DIR\"" 85 mail_command "set outfolder=\"$MU_FOLDER_DIR\""
86 mail_test "Save" \ 86 mail_test "Save" \
87 "\"$MU_FOLDER_DIR/gray\" 12/390" 87 "\"$MU_FOLDER_DIR/gray\" 14/438"
88 88
89 mail_test "file \"$MU_FOLDER_DIR/gray\"" \ 89 mail_test "file \"$MU_FOLDER_DIR/gray\"" \
90 "Held 2 messages in $MU_FOLDER_DIR/three" 90 "Held 2 messages in $MU_FOLDER_DIR/three"
......