Removed the draft message in favor for a consistent draft folder facility
[mmh] / uip / refile.c
1 /*
2  * refile.c -- move or link message(s) from a source folder
3  *          -- into one or more destination folders
4  *
5  * This code is Copyright (c) 2002, by the authors of nmh.  See the
6  * COPYRIGHT file in the root directory of the nmh distribution for
7  * complete copyright information.
8  */
9
10 #include <h/mh.h>
11 #include <h/utils.h>
12 #include <fcntl.h>
13 #include <errno.h>
14
15 static struct swit switches[] = {
16 #define LINKSW  0
17         { "link", 0 },
18 #define NLINKSW  1
19         { "nolink", 0 },
20 #define PRESSW  2
21         { "preserve", 0 },
22 #define NPRESSW  3
23         { "nopreserve", 0 },
24 #define UNLINKSW  4
25         { "unlink", 0 },
26 #define NUNLINKSW  5
27         { "nounlink", 0 },
28 #define SRCSW  6
29         { "src +folder", 0 },
30 #define FILESW  7
31         { "file file", 0 },
32 #define RPROCSW  8
33         { "rmmproc program", 0 },
34 #define NRPRCSW  9
35         { "normmproc", 0 },
36 #define VERSIONSW  10
37         { "version", 0 },
38 #define HELPSW  11
39         { "help", 0 },
40         { NULL, 0 }
41 };
42
43 static char maildir[BUFSIZ];
44
45 struct st_fold {
46         char *f_name;
47         struct msgs *f_mp;
48 };
49
50 /*
51  * static prototypes
52  */
53 static void opnfolds (struct st_fold *, int);
54 static void clsfolds (struct st_fold *, int);
55 static void remove_files (int, char **);
56 static int m_file (char *, struct st_fold *, int, int, int);
57
58
59 int
60 main (int argc, char **argv)
61 {
62         int linkf = 0, preserve = 0, filep = 0;
63         int foldp = 0, unlink_msgs = 0;
64         int i, msgnum;
65         char *cp, *folder = NULL, buf[BUFSIZ];
66         char **argp, **arguments;
67         char *filevec[NFOLDERS + 2];
68         char **files = &filevec[1];  /* leave room for remove_files:vec[0] */
69         struct st_fold folders[NFOLDERS + 1];
70         struct msgs_array msgs = { 0, 0, NULL };
71         struct msgs *mp;
72
73 #ifdef LOCALE
74         setlocale(LC_ALL, "");
75 #endif
76         invo_name = r1bindex (argv[0], '/');
77
78         /* read user profile/context */
79         context_read();
80
81         arguments = getarguments (invo_name, argc, argv, 1);
82         argp = arguments;
83
84         /*
85          * Parse arguments
86          */
87         while ((cp = *argp++)) {
88                 if (*cp == '-') {
89                         switch (smatch (++cp, switches)) {
90                         case AMBIGSW:
91                                 ambigsw (cp, switches);
92                                 done (1);
93                         case UNKWNSW:
94                                 adios (NULL, "-%s unknown\n", cp);
95
96                         case HELPSW:
97                                 snprintf (buf, sizeof(buf), "%s [msgs] [switches] +folder ...",
98                                                   invo_name);
99                                 print_help (buf, switches, 1);
100                                 done (1);
101                         case VERSIONSW:
102                                 print_version(invo_name);
103                                 done (1);
104
105                         case LINKSW:
106                                 linkf++;
107                                 continue;
108                         case NLINKSW:
109                                 linkf = 0;
110                                 continue;
111
112                         case PRESSW:
113                                 preserve++;
114                                 continue;
115                         case NPRESSW:
116                                 preserve = 0;
117                                 continue;
118
119                         case UNLINKSW:
120                                 unlink_msgs++;
121                                 continue;
122                         case NUNLINKSW:
123                                 unlink_msgs = 0;
124                                 continue;
125
126                         case SRCSW:
127                                 if (folder)
128                                         adios (NULL, "only one source folder at a time!");
129                                 if (!(cp = *argp++) || *cp == '-')
130                                         adios (NULL, "missing argument to %s", argp[-2]);
131                                 folder = path (*cp == '+' || *cp == '@' ? cp + 1 : cp,
132                                                            *cp != '@' ? TFOLDER : TSUBCWF);
133                                 continue;
134                         case FILESW:
135                                 if (filep > NFOLDERS)
136                                         adios (NULL, "only %d files allowed!", NFOLDERS);
137                                 if (!(cp = *argp++) || *cp == '-')
138                                         adios (NULL, "missing argument to %s", argp[-2]);
139                                 files[filep++] = path (cp, TFILE);
140                                 continue;
141
142                         case RPROCSW:
143                                 if (!(rmmproc = *argp++) || *rmmproc == '-')
144                                         adios (NULL, "missing argument to %s", argp[-2]);
145                                 continue;
146                         case NRPRCSW:
147                                 rmmproc = NULL;
148                                 continue;
149                         }
150                 }
151                 if (*cp == '+' || *cp == '@') {
152                         if (foldp > NFOLDERS)
153                                 adios (NULL, "only %d folders allowed!", NFOLDERS);
154                         folders[foldp++].f_name =
155                                 pluspath (cp);
156                 } else
157                                 app_msgarg(&msgs, cp);
158         }
159
160         if (!context_find ("path"))
161                 free (path ("./", TFOLDER));
162         if (foldp == 0)
163                 adios (NULL, "no folder specified");
164
165 #ifdef WHATNOW
166         if (!msgs.size && !foldp && !filep && (cp = getenv ("mhdraft")) && *cp)
167                 files[filep++] = cp;
168 #endif /* WHATNOW */
169
170         /*
171          * We are refiling a file to the folders
172          */
173         if (filep > 0) {
174                 if (folder || msgs.size)
175                         adios (NULL, "use -file or some messages, not both");
176                 opnfolds (folders, foldp);
177                 for (i = 0; i < filep; i++)
178                         if (m_file (files[i], folders, foldp, preserve, 0))
179                                 done (1);
180                 /* If -nolink, then "remove" files */
181                 if (!linkf)
182                         remove_files (filep, filevec);
183                 done (0);
184         }
185
186         if (!msgs.size)
187                 app_msgarg(&msgs, "cur");
188         if (!folder)
189                 folder = getfolder (1);
190         strncpy (maildir, m_maildir (folder), sizeof(maildir));
191
192         if (chdir (maildir) == NOTOK)
193                 adios (maildir, "unable to change directory to");
194
195         /* read source folder and create message structure */
196         if (!(mp = folder_read (folder)))
197                 adios (NULL, "unable to read folder %s", folder);
198
199         /* check for empty folder */
200         if (mp->nummsg == 0)
201                 adios (NULL, "no messages in %s", folder);
202
203         /* parse the message range/sequence/name and set SELECTED */
204         for (msgnum = 0; msgnum < msgs.size; msgnum++)
205                 if (!m_convert (mp, msgs.msgs[msgnum]))
206                         done (1);
207         seq_setprev (mp);  /* set the previous-sequence */
208
209         /* create folder structures for each destination folder */
210         opnfolds (folders, foldp);
211
212         /* Link all the selected messages into destination folders.
213          *
214          * This causes the add hook to be run for messages that are
215          * linked into another folder.  The refile hook is run for
216          * messages that are moved to another folder.
217          */
218         for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
219                 if (is_selected (mp, msgnum)) {
220                         cp = getcpy (m_name (msgnum));
221                         if (m_file (cp, folders, foldp, preserve, !linkf))
222                                 done (1);
223                         free (cp);
224                 }
225         }
226
227         /*
228          * This is a hack.  If we are using an external rmmproc,
229          * then save the current folder to the context file,
230          * so the external rmmproc will remove files from the correct
231          * directory.  This should be moved to folder_delmsgs().
232          */
233         if (rmmproc) {
234                 context_replace (pfolder, folder);
235                 context_save ();
236                 fflush (stdout);
237         }
238
239         /* If -nolink, then "remove" messages from source folder.
240          *
241          * Note that folder_delmsgs does not call the delete hook
242          * because the message has already been handled above.
243          */
244         if (!linkf) {
245                 folder_delmsgs (mp, unlink_msgs, 1);
246         }
247
248         clsfolds (folders, foldp);
249
250         if (mp->hghsel != mp->curmsg
251                 && (mp->numsel != mp->nummsg || linkf))
252                 seq_setcur (mp, mp->hghsel);
253         seq_save (mp);  /* synchronize message sequences */
254
255         context_replace (pfolder, folder);  /* update current folder   */
256         context_save ();  /* save the context file   */
257         folder_free (mp);  /* free folder structure   */
258         done (0);
259         return 1;
260 }
261
262
263 /*
264  * Read all the destination folders and
265  * create folder structures for all of them.
266  */
267
268 static void
269 opnfolds (struct st_fold *folders, int nfolders)
270 {
271         char nmaildir[BUFSIZ];
272         register struct st_fold *fp, *ep;
273         register struct msgs *mp;
274
275         for (fp = folders, ep = folders + nfolders; fp < ep; fp++) {
276                 chdir (m_maildir (""));
277                 strncpy (nmaildir, m_maildir (fp->f_name), sizeof(nmaildir));
278
279         create_folder (nmaildir, 0, done);
280
281                 if (chdir (nmaildir) == NOTOK)
282                         adios (nmaildir, "unable to change directory to");
283                 if (!(mp = folder_read (fp->f_name)))
284                         adios (NULL, "unable to read folder %s", fp->f_name);
285                 mp->curmsg = 0;
286
287                 fp->f_mp = mp;
288
289                 chdir (maildir);
290         }
291 }
292
293
294 /*
295  * Set the Previous-Sequence and then sychronize the
296  * sequence file, for each destination folder.
297  */
298
299 static void
300 clsfolds (struct st_fold *folders, int nfolders)
301 {
302         register struct st_fold *fp, *ep;
303         register struct msgs *mp;
304
305         for (fp = folders, ep = folders + nfolders; fp < ep; fp++) {
306                 mp = fp->f_mp;
307                 seq_setprev (mp);
308                 seq_save (mp);
309         }
310 }
311
312
313 /*
314  * If you have a "rmmproc" defined, we called that
315  * to remove all the specified files.  If "rmmproc"
316  * is not defined, then just unlink the files.
317  */
318
319 static void
320 remove_files (int filep, char **files)
321 {
322         int i;
323         char **vec;
324
325         /* If rmmproc is defined, we use that */
326         if (rmmproc) {
327                 vec = files++;  /* vec[0] = filevec[0] */
328                 files[filep] = NULL;  /* NULL terminate list */
329
330                 fflush (stdout);
331                 vec[0] = r1bindex (rmmproc, '/');
332                 execvp (rmmproc, vec);
333                 adios (rmmproc, "unable to exec");
334         }
335
336         /* Else just unlink the files */
337         files++;  /* advance past filevec[0] */
338         for (i = 0; i < filep; i++) {
339                 if (unlink (files[i]) == NOTOK)
340                         admonish (files[i], "unable to unlink");
341         }
342 }
343
344
345 /*
346  * Link (or copy) the message into each of
347  * the destination folders.
348  */
349
350 static int
351 m_file (char *msgfile, struct st_fold *folders, int nfolders, int preserve, int refile)
352 {
353         int msgnum;
354         struct st_fold *fp, *ep;
355
356         for (fp = folders, ep = folders + nfolders; fp < ep; fp++) {
357                 if ((msgnum = folder_addmsg (&fp->f_mp, msgfile, 1, 0, preserve, nfolders == 1 && refile, maildir)) == -1)
358                         return 1;
359         }
360         return 0;
361 }