]> diplodocus.org Git - nmh/blob - uip/refile.c
sbr/vector.c: Replace Nbby with <limits.h>'s CHAR_BIT.
[nmh] / uip / refile.c
1 /* refile.c -- move or link message(s) from a source folder
2 * -- into one or more destination folders
3 *
4 * This code is Copyright (c) 2002, by the authors of nmh. See the
5 * COPYRIGHT file in the root directory of the nmh distribution for
6 * complete copyright information.
7 */
8
9 #include <h/mh.h>
10 #include <h/utils.h>
11 #include <fcntl.h>
12
13 #define REFILE_SWITCHES \
14 X("draft", 0, DRAFTSW) \
15 X("link", 0, LINKSW) \
16 X("nolink", 0, NLINKSW) \
17 X("preserve", 0, PRESSW) \
18 X("nopreserve", 0, NPRESSW) \
19 X("retainsequences", 0, RETAINSEQSSW) \
20 X("noretainsequences", 0, NRETAINSEQSSW) \
21 X("unlink", 0, UNLINKSW) \
22 X("nounlink", 0, NUNLINKSW) \
23 X("src +folder", 0, SRCSW) \
24 X("file file", 0, FILESW) \
25 X("rmmproc program", 0, RPROCSW) \
26 X("normmproc", 0, NRPRCSW) \
27 X("version", 0, VERSIONSW) \
28 X("help", 0, HELPSW) \
29
30 #define X(sw, minchars, id) id,
31 DEFINE_SWITCH_ENUM(REFILE);
32 #undef X
33
34 #define X(sw, minchars, id) { sw, minchars, id },
35 DEFINE_SWITCH_ARRAY(REFILE, switches);
36 #undef X
37
38 static char maildir[BUFSIZ];
39
40 struct st_fold {
41 char *f_name;
42 struct msgs *f_mp;
43 };
44
45 /*
46 * static prototypes
47 */
48 static void opnfolds (struct msgs *, struct st_fold *, int);
49 static void clsfolds (struct st_fold *, int);
50 static void remove_files (int, char **);
51 static int m_file (struct msgs *, char *, int, struct st_fold *, int, int, int);
52 static void copy_seqs (struct msgs *, int, struct msgs *, int);
53
54
55 int
56 main (int argc, char **argv)
57 {
58 int linkf = 0, preserve = 0, retainseqs = 0, filep = 0;
59 int foldp = 0, isdf = 0, unlink_msgs = 0;
60 int i, msgnum;
61 char *cp, *folder = NULL, buf[BUFSIZ];
62 char **argp, **arguments;
63 char *filevec[NFOLDERS + 2];
64 char **files = &filevec[1]; /* leave room for remove_files:vec[0] */
65 struct st_fold folders[NFOLDERS + 1];
66 struct msgs_array msgs = { 0, 0, NULL };
67 struct msgs *mp;
68
69 if (nmh_init(argv[0], 1)) { return 1; }
70
71 arguments = getarguments (invo_name, argc, argv, 1);
72 argp = arguments;
73
74 /*
75 * Parse arguments
76 */
77 while ((cp = *argp++)) {
78 if (*cp == '-') {
79 switch (smatch (++cp, switches)) {
80 case AMBIGSW:
81 ambigsw (cp, switches);
82 done (1);
83 case UNKWNSW:
84 adios (NULL, "-%s unknown\n", cp);
85
86 case HELPSW:
87 snprintf (buf, sizeof(buf), "%s [msgs] [switches] +folder ...",
88 invo_name);
89 print_help (buf, switches, 1);
90 done (0);
91 case VERSIONSW:
92 print_version(invo_name);
93 done (0);
94
95 case LINKSW:
96 linkf++;
97 continue;
98 case NLINKSW:
99 linkf = 0;
100 continue;
101
102 case PRESSW:
103 preserve++;
104 continue;
105 case NPRESSW:
106 preserve = 0;
107 continue;
108
109 case RETAINSEQSSW:
110 retainseqs = 1;
111 continue;
112 case NRETAINSEQSSW:
113 retainseqs = 0;
114 continue;
115
116 case UNLINKSW:
117 unlink_msgs++;
118 continue;
119 case NUNLINKSW:
120 unlink_msgs = 0;
121 continue;
122
123 case SRCSW:
124 if (folder)
125 adios (NULL, "only one source folder at a time!");
126 if (!(cp = *argp++) || *cp == '-')
127 adios (NULL, "missing argument to %s", argp[-2]);
128 folder = path (*cp == '+' || *cp == '@' ? cp + 1 : cp,
129 *cp != '@' ? TFOLDER : TSUBCWF);
130 continue;
131 case DRAFTSW:
132 if (filep > NFOLDERS)
133 adios (NULL, "only %d files allowed!", NFOLDERS);
134 isdf = 0;
135 files[filep++] = getcpy (m_draft (NULL, NULL, 1, &isdf));
136 continue;
137 case FILESW:
138 if (filep > NFOLDERS)
139 adios (NULL, "only %d files allowed!", NFOLDERS);
140 if (!(cp = *argp++) || *cp == '-')
141 adios (NULL, "missing argument to %s", argp[-2]);
142 files[filep++] = path (cp, TFILE);
143 continue;
144
145 case RPROCSW:
146 if (!(rmmproc = *argp++) || *rmmproc == '-')
147 adios (NULL, "missing argument to %s", argp[-2]);
148 continue;
149 case NRPRCSW:
150 rmmproc = NULL;
151 continue;
152 }
153 }
154 if (*cp == '+' || *cp == '@') {
155 if (foldp > NFOLDERS)
156 adios (NULL, "only %d folders allowed!", NFOLDERS);
157 folders[foldp++].f_name =
158 pluspath (cp);
159 } else
160 app_msgarg(&msgs, cp);
161 }
162
163 if (!context_find ("path"))
164 free (path ("./", TFOLDER));
165 if (foldp == 0)
166 adios (NULL, "no folder specified");
167
168 /*
169 * We are refiling a file to the folders
170 */
171 if (filep > 0) {
172 if (folder || msgs.size)
173 adios (NULL, "use -file or some messages, not both");
174 opnfolds (NULL, folders, foldp);
175 for (i = 0; i < filep; i++)
176 if (m_file (0, files[i], 0, folders, foldp, preserve, 0))
177 done (1);
178 /* If -nolink, then "remove" files */
179 if (!linkf)
180 remove_files (filep, filevec);
181 done (0);
182 }
183
184 if (!msgs.size)
185 app_msgarg(&msgs, "cur");
186 if (!folder)
187 folder = getfolder (1);
188 strncpy (maildir, m_maildir (folder), sizeof(maildir));
189
190 if (chdir (maildir) == NOTOK)
191 adios (maildir, "unable to change directory to");
192
193 /* read source folder and create message structure */
194 if (!(mp = folder_read (folder, 1)))
195 adios (NULL, "unable to read folder %s", folder);
196
197 /* check for empty folder */
198 if (mp->nummsg == 0)
199 adios (NULL, "no messages in %s", folder);
200
201 /* parse the message range/sequence/name and set SELECTED */
202 for (msgnum = 0; msgnum < msgs.size; msgnum++)
203 if (!m_convert (mp, msgs.msgs[msgnum]))
204 done (1);
205 seq_setprev (mp); /* set the previous-sequence */
206
207 /* create folder structures for each destination folder */
208 opnfolds (mp, folders, foldp);
209
210 /* Link all the selected messages into destination folders.
211 *
212 * This causes the add hook to be run for messages that are
213 * linked into another folder. The refile hook is run for
214 * messages that are moved to another folder.
215 */
216 for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
217 if (is_selected (mp, msgnum)) {
218 cp = mh_xstrdup(m_name (msgnum));
219 if (m_file (mp, cp, retainseqs ? msgnum : 0, folders, foldp,
220 preserve, !linkf))
221 done (1);
222 free (cp);
223 }
224 }
225
226 /*
227 * Update this now, since folder_delmsgs() will save the context
228 * but doesn't have access to the folder name.
229 */
230
231 context_replace (pfolder, folder); /* update current folder */
232
233 /*
234 * Adjust "cur" if necessary
235 */
236
237 if (mp->hghsel != mp->curmsg
238 && (mp->numsel != mp->nummsg || linkf))
239 seq_setcur (mp, mp->hghsel);
240
241 /*
242 * Close destination folders now; if we are using private sequences
243 * we need to have all of our calls to seq_save() complete before we
244 * call context_save().
245 */
246
247 clsfolds (folders, foldp);
248
249 /* If -nolink, then "remove" messages from source folder.
250 *
251 * Note that folder_delmsgs does not call the delete hook
252 * because the message has already been handled above.
253 */
254 if (!linkf) {
255 folder_delmsgs (mp, unlink_msgs, 1);
256 } else {
257 seq_save (mp); /* synchronize message sequences */
258 context_save (); /* save the context file */
259 }
260
261 folder_free (mp); /* free folder structure */
262 done (0);
263 return 1;
264 }
265
266
267 /*
268 * Read all the destination folders and
269 * create folder structures for all of them.
270 */
271
272 static void
273 opnfolds (struct msgs *src_folder, struct st_fold *folders, int nfolders)
274 {
275 char nmaildir[BUFSIZ];
276 struct st_fold *fp, *ep;
277 struct msgs *mp;
278
279 for (fp = folders, ep = folders + nfolders; fp < ep; fp++) {
280 if (chdir (m_maildir ("")) < 0) {
281 advise (m_maildir (""), "chdir");
282 }
283 strncpy (nmaildir, m_maildir (fp->f_name), sizeof(nmaildir));
284
285 /*
286 * Null src_folder indicates that we are refiling a file to
287 * the folders, in which case we don't want to short-circuit
288 * fp->f_mp to any "source folder".
289 */
290 if (! src_folder || strcmp (src_folder->foldpath, nmaildir)) {
291 create_folder (nmaildir, 0, done);
292
293 if (chdir (nmaildir) == NOTOK)
294 adios (nmaildir, "unable to change directory to");
295 if (!(mp = folder_read (fp->f_name, 1)))
296 adios (NULL, "unable to read folder %s", fp->f_name);
297 mp->curmsg = 0;
298
299 fp->f_mp = mp;
300 } else {
301 /* Source and destination folders are the same. */
302 fp->f_mp = src_folder;
303 }
304
305 if (maildir[0] != '\0' && chdir (maildir) < 0) {
306 advise (maildir, "chdir");
307 }
308 }
309 }
310
311
312 /*
313 * Set the Previous-Sequence and then sychronize the
314 * sequence file, for each destination folder.
315 */
316
317 static void
318 clsfolds (struct st_fold *folders, int nfolders)
319 {
320 struct st_fold *fp, *ep;
321 struct msgs *mp;
322
323 for (fp = folders, ep = folders + nfolders; fp < ep; fp++) {
324 mp = fp->f_mp;
325 seq_setprev (mp);
326 seq_save (mp);
327 }
328 }
329
330
331 /*
332 * If you have a "rmmproc" defined, we called that
333 * to remove all the specified files. If "rmmproc"
334 * is not defined, then just unlink the files.
335 */
336
337 static void
338 remove_files (int filep, char **files)
339 {
340 int i, vecp;
341 char **vec, *program;
342
343 /* If rmmproc is defined, we use that */
344 if (rmmproc) {
345 vec = argsplit(rmmproc, &program, &vecp);
346 files++; /* Yes, we need to do this */
347 for (i = 0; i < filep; i++)
348 vec[vecp++] = files[i];
349 vec[vecp] = NULL; /* NULL terminate list */
350
351 fflush (stdout);
352 execvp (program, vec);
353 adios (rmmproc, "unable to exec");
354 }
355
356 /* Else just unlink the files */
357 files++; /* advance past filevec[0] */
358 for (i = 0; i < filep; i++) {
359 if (m_unlink (files[i]) == NOTOK)
360 admonish (files[i], "unable to unlink");
361 }
362 }
363
364
365 /*
366 * Link (or copy) the message into each of the destination folders.
367 * If oldmsgnum is not 0, call copy_seqs().
368 */
369
370 static int
371 m_file (struct msgs *mp, char *msgfile, int oldmsgnum,
372 struct st_fold *folders, int nfolders, int preserve, int refile)
373 {
374 int msgnum;
375 struct st_fold *fp, *ep;
376
377 for (fp = folders, ep = folders + nfolders; fp < ep; fp++) {
378 /*
379 * With same source and destination folder, don't indicate that
380 * the new message is selected so that 1) folder_delmsgs() doesn't
381 * delete it later and 2) it is not reflected in mp->hghsel, and
382 * therefore won't be assigned to be the current message.
383 */
384 if ((msgnum = folder_addmsg (&fp->f_mp, msgfile,
385 mp == fp->f_mp ? 0 : 1,
386 0, preserve, nfolders == 1 && refile,
387 maildir)) == -1)
388 return 1;
389 if (oldmsgnum) copy_seqs (mp, oldmsgnum, fp->f_mp, msgnum);
390 }
391 return 0;
392 }
393
394
395 /*
396 * Copy sequence information for a refiled message to its
397 * new folder. Skip the cur sequence.
398 */
399 static void
400 copy_seqs (struct msgs *oldmp, int oldmsgnum, struct msgs *newmp, int newmsgnum)
401 {
402 char **seq;
403 size_t seqnum;
404
405 for (seq = svector_strs (oldmp->msgattrs), seqnum = 0;
406 *seq && seqnum < svector_size (oldmp->msgattrs);
407 ++seq, ++seqnum) {
408 if (strcmp (current, *seq)) {
409 assert ((int) seqnum == seq_getnum (oldmp, *seq));
410 if (in_sequence (oldmp, seqnum, oldmsgnum)) {
411 seq_addmsg (newmp, *seq, newmsgnum,
412 is_seq_private (oldmp, seqnum) ? 0 : 1, 0);
413 }
414 }
415 }
416 }