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