Reformated comments and long lines
[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 ...", invo_name);
98                                 print_help (buf, switches, 1);
99                                 done (1);
100                         case VERSIONSW:
101                                 print_version(invo_name);
102                                 done (1);
103
104                         case LINKSW:
105                                 linkf++;
106                                 continue;
107                         case NLINKSW:
108                                 linkf = 0;
109                                 continue;
110
111                         case PRESSW:
112                                 preserve++;
113                                 continue;
114                         case NPRESSW:
115                                 preserve = 0;
116                                 continue;
117
118                         case UNLINKSW:
119                                 unlink_msgs++;
120                                 continue;
121                         case NUNLINKSW:
122                                 unlink_msgs = 0;
123                                 continue;
124
125                         case SRCSW:
126                                 if (folder)
127                                         adios (NULL, "only one source folder at a time!");
128                                 if (!(cp = *argp++) || *cp == '-')
129                                         adios (NULL, "missing argument to %s", argp[-2]);
130                                 folder = path (*cp == '+' || *cp == '@' ?
131                                                 cp + 1 : cp,
132                                                 *cp != '@' ?
133                                                 TFOLDER : TSUBCWF);
134                                 continue;
135                         case FILESW:
136                                 if (filep > NFOLDERS)
137                                         adios (NULL, "only %d files allowed!",
138                                                         NFOLDERS);
139                                 if (!(cp = *argp++) || *cp == '-')
140                                         adios (NULL, "missing argument to %s",
141                                                         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",
148                                                         argp[-2]);
149                                 continue;
150                         case NRPRCSW:
151                                 rmmproc = NULL;
152                                 continue;
153                         }
154                 }
155                 if (*cp == '+' || *cp == '@') {
156                         if (foldp > NFOLDERS)
157                                 adios (NULL, "only %d folders allowed!",
158                                                 NFOLDERS);
159                         folders[foldp++].f_name =
160                                 pluspath (cp);
161                 } else
162                                 app_msgarg(&msgs, cp);
163         }
164
165         if (!context_find ("path"))
166                 free (path ("./", TFOLDER));
167         if (foldp == 0)
168                 adios (NULL, "no folder specified");
169
170 #ifdef WHATNOW
171         if (!msgs.size && !foldp && !filep && (cp = getenv ("mhdraft")) && *cp)
172                 files[filep++] = cp;
173 #endif /* WHATNOW */
174
175         /*
176         ** We are refiling a file to the folders
177         */
178         if (filep > 0) {
179                 if (folder || msgs.size)
180                         adios (NULL, "use -file or some messages, not both");
181                 opnfolds (folders, foldp);
182                 for (i = 0; i < filep; i++)
183                         if (m_file (files[i], folders, foldp, preserve, 0))
184                                 done (1);
185                 /* If -nolink, then "remove" files */
186                 if (!linkf)
187                         remove_files (filep, filevec);
188                 done (0);
189         }
190
191         if (!msgs.size)
192                 app_msgarg(&msgs, "cur");
193         if (!folder)
194                 folder = getfolder (1);
195         strncpy (maildir, m_maildir (folder), sizeof(maildir));
196
197         if (chdir (maildir) == NOTOK)
198                 adios (maildir, "unable to change directory to");
199
200         /* read source folder and create message structure */
201         if (!(mp = folder_read (folder)))
202                 adios (NULL, "unable to read folder %s", folder);
203
204         /* check for empty folder */
205         if (mp->nummsg == 0)
206                 adios (NULL, "no messages in %s", folder);
207
208         /* parse the message range/sequence/name and set SELECTED */
209         for (msgnum = 0; msgnum < msgs.size; msgnum++)
210                 if (!m_convert (mp, msgs.msgs[msgnum]))
211                         done (1);
212         seq_setprev (mp);  /* set the previous-sequence */
213
214         /* create folder structures for each destination folder */
215         opnfolds (folders, foldp);
216
217         /* Link all the selected messages into destination folders.
218         **
219         ** This causes the add hook to be run for messages that are
220         ** linked into another folder.  The refile hook is run for
221         ** messages that are moved to another folder.
222         */
223         for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
224                 if (is_selected (mp, msgnum)) {
225                         cp = getcpy (m_name (msgnum));
226                         if (m_file (cp, folders, foldp, preserve, !linkf))
227                                 done (1);
228                         free (cp);
229                 }
230         }
231
232         /*
233         ** This is a hack.  If we are using an external rmmproc,
234         ** then save the current folder to the context file,
235         ** so the external rmmproc will remove files from the correct
236         ** directory.  This should be moved to folder_delmsgs().
237         */
238         if (rmmproc) {
239                 context_replace (pfolder, folder);
240                 context_save ();
241                 fflush (stdout);
242         }
243
244         /*
245         ** If -nolink, then "remove" messages from source folder.
246         **
247         ** Note that folder_delmsgs does not call the delete hook
248         ** because the message has already been handled above.
249         */
250         if (!linkf) {
251                 folder_delmsgs (mp, unlink_msgs, 1);
252         }
253
254         clsfolds (folders, foldp);
255
256         if (mp->hghsel != mp->curmsg
257                 && (mp->numsel != mp->nummsg || linkf))
258                 seq_setcur (mp, mp->hghsel);
259         seq_save (mp);  /* synchronize message sequences */
260
261         context_replace (pfolder, folder);  /* update current folder   */
262         context_save ();  /* save the context file   */
263         folder_free (mp);  /* free folder structure   */
264         done (0);
265         return 1;
266 }
267
268
269 /*
270 ** Read all the destination folders and
271 ** create folder structures for all of them.
272 */
273
274 static void
275 opnfolds (struct st_fold *folders, int nfolders)
276 {
277         char nmaildir[BUFSIZ];
278         register struct st_fold *fp, *ep;
279         register struct msgs *mp;
280
281         for (fp = folders, ep = folders + nfolders; fp < ep; fp++) {
282                 chdir (m_maildir (""));
283                 strncpy (nmaildir, m_maildir (fp->f_name), sizeof(nmaildir));
284
285         create_folder (nmaildir, 0, done);
286
287                 if (chdir (nmaildir) == NOTOK)
288                         adios (nmaildir, "unable to change directory to");
289                 if (!(mp = folder_read (fp->f_name)))
290                         adios (NULL, "unable to read folder %s", fp->f_name);
291                 mp->curmsg = 0;
292
293                 fp->f_mp = mp;
294
295                 chdir (maildir);
296         }
297 }
298
299
300 /*
301 ** Set the Previous-Sequence and then sychronize the
302 ** sequence file, for each destination folder.
303 */
304
305 static void
306 clsfolds (struct st_fold *folders, int nfolders)
307 {
308         register struct st_fold *fp, *ep;
309         register struct msgs *mp;
310
311         for (fp = folders, ep = folders + nfolders; fp < ep; fp++) {
312                 mp = fp->f_mp;
313                 seq_setprev (mp);
314                 seq_save (mp);
315         }
316 }
317
318
319 /*
320 ** If you have a "rmmproc" defined, we called that
321 ** to remove all the specified files.  If "rmmproc"
322 ** is not defined, then just unlink the files.
323 */
324
325 static void
326 remove_files (int filep, char **files)
327 {
328         int i;
329         char **vec;
330
331         /* If rmmproc is defined, we use that */
332         if (rmmproc) {
333                 vec = files++;  /* vec[0] = filevec[0] */
334                 files[filep] = NULL;  /* NULL terminate list */
335
336                 fflush (stdout);
337                 vec[0] = r1bindex (rmmproc, '/');
338                 execvp (rmmproc, vec);
339                 adios (rmmproc, "unable to exec");
340         }
341
342         /* Else just unlink the files */
343         files++;  /* advance past filevec[0] */
344         for (i = 0; i < filep; i++) {
345                 if (unlink (files[i]) == NOTOK)
346                         admonish (files[i], "unable to unlink");
347         }
348 }
349
350
351 /*
352 ** Link (or copy) the message into each of
353 ** the destination folders.
354 */
355
356 static int
357 m_file (char *msgfile, struct st_fold *folders, int nfolders, int preserve,
358                 int refile)
359 {
360         int msgnum;
361         struct st_fold *fp, *ep;
362
363         for (fp = folders, ep = folders + nfolders; fp < ep; fp++) {
364                 if ((msgnum = folder_addmsg (&fp->f_mp, msgfile, 1, 0,
365                                 preserve, nfolders == 1 && refile, maildir))
366                                 == -1)
367                         return 1;
368         }
369         return 0;
370 }