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