Use the same mh_hostname() function from test/common.h in mhsign(1)
[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 #include <unistd.h>
15 #include <locale.h>
16 #include <sysexits.h>
17
18 static struct swit switches[] = {
19 #define LINKSW  0
20         { "link", 0 },
21 #define NLINKSW  1
22         { "nolink", 2 },
23 #define SRCSW  2
24         { "src +folder", 0 },
25 #define FILESW  3
26         { "file file", 0 },
27 #define VERSIONSW  4
28         { "Version", 0 },
29 #define HELPSW  5
30         { "help", 0 },
31         { NULL, 0 }
32 };
33
34 char *version=VERSION;
35
36 static char maildir[BUFSIZ];
37
38 struct st_fold {
39         char *f_name;
40         struct msgs *f_mp;
41 };
42
43 /*
44 ** static prototypes
45 */
46 static void opnfolds(struct st_fold *, int);
47 static void clsfolds(struct st_fold *, int);
48 static int m_file(char *, struct st_fold *, int, int);
49
50
51 int
52 main(int argc, char **argv)
53 {
54         int linkf = 0, filep = 0;
55         int foldp = 0;
56         int i, msgnum;
57         char *cp, *folder = NULL, buf[BUFSIZ];
58         char **argp, **arguments;
59         char *filevec[NFOLDERS + 1];
60         char **files = filevec;
61         struct st_fold folders[NFOLDERS + 1];
62         struct msgs_array msgs = { 0, 0, NULL };
63         struct msgs *mp;
64
65         setlocale(LC_ALL, "");
66         invo_name = mhbasename(argv[0]);
67
68         context_read();
69
70         arguments = getarguments(invo_name, argc, argv, 1);
71         argp = arguments;
72
73         /*
74         ** Parse arguments
75         */
76         while ((cp = *argp++)) {
77                 if (*cp == '-') {
78                         switch (smatch(++cp, switches)) {
79                         case AMBIGSW:
80                                 ambigsw(cp, switches);
81                                 exit(EX_USAGE);
82                         case UNKWNSW:
83                                 adios(EX_USAGE, NULL, "-%s unknown\n", cp);
84
85                         case HELPSW:
86                                 snprintf(buf, sizeof(buf), "%s [msgs] [switches] +folder ...", invo_name);
87                                 print_help(buf, switches, 1);
88                                 exit(argc == 2 ? EX_OK : EX_USAGE);
89                         case VERSIONSW:
90                                 print_version(invo_name);
91                                 exit(argc == 2 ? EX_OK : EX_USAGE);
92
93                         case LINKSW:
94                                 linkf++;
95                                 continue;
96                         case NLINKSW:
97                                 linkf = 0;
98                                 continue;
99
100                         case SRCSW:
101                                 if (folder)
102                                         adios(EX_USAGE, NULL, "only one source folder at a time!");
103                                 if (!(cp = *argp++) || *cp == '-')
104                                         adios(EX_USAGE, NULL, "missing argument to %s",
105                                                         argp[-2]);
106                                 folder = mh_xstrdup(expandfol(cp));
107                                 continue;
108                         case FILESW:
109                                 if (filep > NFOLDERS)
110                                         adios(EX_USAGE, NULL, "only %d files allowed!",
111                                                         NFOLDERS);
112                                 if (!(cp = *argp++) || *cp == '-')
113                                         adios(EX_USAGE, NULL, "missing argument to %s",
114                                                         argp[-2]);
115                                 files[filep++] = mh_xstrdup(expanddir(cp));
116                                 continue;
117                         }
118                 }
119                 if (*cp == '+' || *cp == '@') {
120                         if (foldp > NFOLDERS)
121                                 adios(EX_USAGE, NULL, "only %d folders allowed!",
122                                                 NFOLDERS);
123                         folders[foldp++].f_name = mh_xstrdup(expandfol(cp));
124                 } else
125                         app_msgarg(&msgs, cp);
126         }
127
128         if (foldp == 0)
129                 adios(EX_USAGE, NULL, "no folder specified");
130
131         if (filep > 0) {
132                 /*
133                 ** We are refiling one or more files (-file) to the folders
134                 */
135                 if (msgs.size) {
136                         adios(EX_USAGE, NULL, "use -file or msgs, not both");
137                 }
138                 if (folder) {
139                         adios(EX_USAGE, NULL, "use -file or -src, not both");
140                 }
141                 opnfolds(folders, foldp);
142                 for (i = 0; i < filep; i++) {
143                         if (m_file(files[i], folders, foldp, 0)) {
144                                 exit(EX_IOERR);
145                         }
146                 }
147                 /* If -nolink, then unlink files */
148                 if (!linkf) {
149                         int i;
150                         char **files = filevec;
151
152                         for (i = 0; i < filep; i++) {
153                                 if (unlink(files[i]) == NOTOK) {
154                                         admonish(files[i], "unable to unlink");
155                                 }
156                         }
157                 }
158                 exit(EX_OK);
159         }
160
161         /*
162         ** We are refiling messages to the folders
163         */
164         if (!msgs.size) {
165                 app_msgarg(&msgs, seq_cur);
166         }
167         if (!folder) {
168                 folder = getcurfol();
169         }
170
171         strncpy(maildir, toabsdir(folder), sizeof(maildir));
172         if (chdir(maildir) == NOTOK) {
173                 adios(EX_OSERR, maildir, "unable to change directory to");
174         }
175
176         /* read source folder and create message structure */
177         if (!(mp = folder_read(folder))) {
178                 adios(EX_IOERR, NULL, "unable to read folder %s", folder);
179         }
180
181         if (mp->nummsg == 0) {
182                 adios(EX_DATAERR, NULL, "no messages in %s", folder);
183         }
184
185         /* parse the message range/sequence/name and set SELECTED */
186         for (msgnum = 0; msgnum < msgs.size; msgnum++) {
187                 if (!m_convert(mp, msgs.msgs[msgnum])) {
188                         exit(EX_SOFTWARE);
189                 }
190         }
191         seq_setprev(mp);
192
193         /* create folder structures for each destination folder */
194         opnfolds(folders, foldp);
195
196         /*
197         ** Link all the selected messages into destination folders.
198         **
199         ** This causes the add hook to be run for messages that are
200         ** linked into another folder.  The refile hook is run for
201         ** messages that are moved to another folder.
202         */
203         for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
204                 if (is_selected(mp, msgnum)) {
205                         cp = mh_xstrdup(m_name(msgnum));
206                         if (m_file(cp, folders, foldp, !linkf)) {
207                                 exit(EX_IOERR);
208                         }
209                         mh_free0(&cp);
210                 }
211         }
212
213         /*
214         ** If -nolink, then remove (= unlink) messages from source folder.
215         **
216         ** Note that folder_delmsgs does not call the delete hook
217         ** because the message has already been handled above.
218         */
219         if (!linkf) {
220                 folder_delmsgs(mp, 0);
221         }
222
223         clsfolds(folders, foldp);
224
225         if (linkf) {
226                 seq_setcur(mp, mp->hghsel);
227         }
228         seq_save(mp);
229
230         context_save();
231         folder_free(mp);
232         return 0;
233 }
234
235
236 /*
237 ** Read all the destination folders and
238 ** create folder structures for all of them.
239 */
240 static void
241 opnfolds(struct st_fold *folders, int nfolders)
242 {
243         char nmaildir[BUFSIZ];
244         struct st_fold *fp, *ep;
245         struct msgs *mp;
246
247         for (fp = folders, ep = folders + nfolders; fp < ep; fp++) {
248                 chdir(toabsdir("+"));
249                 strncpy(nmaildir, toabsdir(fp->f_name), sizeof(nmaildir));
250
251                 create_folder(nmaildir, 0, exit);
252
253                 if (chdir(nmaildir) == NOTOK) {
254                         adios(EX_OSERR, nmaildir, "unable to change directory to");
255                 }
256                 if (!(mp = folder_read(fp->f_name))) {
257                         adios(EX_IOERR, NULL, "unable to read folder %s", fp->f_name);
258                 }
259                 mp->curmsg = 0;
260
261                 fp->f_mp = mp;
262
263                 chdir(maildir);
264         }
265 }
266
267
268 /*
269 ** Set the Previous-Sequence and then sychronize the
270 ** sequence file, for each destination folder.
271 */
272 static void
273 clsfolds(struct st_fold *folders, int nfolders)
274 {
275         struct st_fold *fp, *ep;
276         struct msgs *mp;
277
278         for (fp = folders, ep = folders + nfolders; fp < ep; fp++) {
279                 mp = fp->f_mp;
280                 seq_setprev(mp);
281                 seq_save(mp);
282         }
283 }
284
285
286 /*
287 ** Link (or copy) the message into each of
288 ** the destination folders.
289 */
290 static int
291 m_file(char *msgfile, struct st_fold *folders, int nfolders, int refile)
292 {
293         int msgnum;
294         struct st_fold *fp, *ep;
295
296         for (fp = folders, ep = folders + nfolders; fp < ep; fp++) {
297                 if ((msgnum = folder_addmsg(&fp->f_mp, msgfile, 1, 0, 0,
298                                 nfolders == 1 && refile, maildir)) == -1) {
299                         return 1;
300                 }
301         }
302         return 0;
303 }