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