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