Rearranged whitespace (and comments) in all the code!
[mmh] / uip / mhtest.c
1 /*
2  * mhtest.c -- test harness for MIME routines
3  *
4  * This code is Copyright (c) 2002, by the authors of nmh.  See the
5  * COPYRIGHT file in the root directory of the nmh distribution for
6  * complete copyright information.
7  */
8
9 #include <h/mh.h>
10 #include <fcntl.h>
11 #include <h/signals.h>
12 #include <h/md5.h>
13 #include <errno.h>
14 #include <signal.h>
15 #include <h/mts.h>
16 #include <h/tws.h>
17 #include <h/mime.h>
18 #include <h/mhparse.h>
19 #include <h/mhcachesbr.h>
20 #include <h/utils.h>
21
22 #ifdef HAVE_SYS_WAIT_H
23 # include <sys/wait.h>
24 #endif
25
26 static struct swit switches[] = {
27 #define CHECKSW  0
28         { "check", 0 },
29 #define NCHECKSW  1
30         { "nocheck", 0 },
31 #define VERBSW  2
32         { "verbose", 0 },
33 #define NVERBSW  3
34         { "noverbose", 0 },
35 #define FILESW  4
36         { "file file", 0 },
37 #define OUTFILESW  5
38         { "outfile file", 0 },
39 #define PARTSW  6
40         { "part number", 0 },
41 #define TYPESW  7
42         { "type content", 0 },
43 #define RCACHESW  8
44         { "rcache policy", 0 },
45 #define WCACHESW  9
46         { "wcache policy", 0 },
47 #define VERSIONSW  10
48         { "version", 0 },
49 #define HELPSW  11
50         { "help", 0 },
51
52 /*
53  * switches for debugging
54  */
55 #define DEBUGSW  12
56         { "debug", -5 },
57         { NULL, 0 }
58 };
59
60
61 int ebcdicsw = 0;  /* hack for linking purposes */
62
63 /* mhparse.c */
64 extern char *tmp;  /* directory to place temp files */
65
66 /* mhcachesbr.c */
67 extern int rcachesw;
68 extern int wcachesw;
69 extern char *cache_public;
70 extern char *cache_private;
71
72 /* mhmisc.c */
73 extern int npart;
74 extern int ntype;
75 extern char *parts[NPARTS + 1];
76 extern char *types[NTYPES + 1];
77 extern int userrs;
78
79 /*
80  * This is currently needed to keep mhparse happy.
81  * This needs to be changed.
82  */
83 pid_t xpid  = 0;
84
85 int debugsw = 0;
86 int verbosw = 0;
87
88 #define quitser pipeser
89
90 /* mhparse.c */
91 CT parse_mime (char *);
92
93 /* mhoutsbr.c */
94 int output_message (CT, char *);
95
96 /* mhmisc.c */
97 int part_ok (CT, int);
98 int type_ok (CT, int);
99 void set_endian (void);
100 void flush_errors (void);
101
102 /* mhfree.c */
103 void free_content (CT);
104 extern CT *cts;
105 void freects_done (int) NORETURN;
106
107 /*
108  * static prototypes
109  */
110 static int write_content (CT *, char *);
111 static RETSIGTYPE pipeser (int);
112
113
114 int
115 main (int argc, char **argv)
116 {
117         int msgnum, *icachesw;
118         char *cp, *file = NULL, *folder = NULL;
119         char *maildir, buf[100], *outfile = NULL;
120         char **argp, **arguments;
121         struct msgs_array msgs = { 0, 0, NULL };
122         struct msgs *mp = NULL;
123         CT ct, *ctp;
124
125         done=freects_done;
126
127 #ifdef LOCALE
128         setlocale(LC_ALL, "");
129 #endif
130         invo_name = r1bindex (argv[0], '/');
131
132         /* read user profile/context */
133         context_read();
134
135         arguments = getarguments (invo_name, argc, argv, 1);
136         argp = arguments;
137
138         /*
139          * Parse arguments
140          */
141         while ((cp = *argp++)) {
142                 if (*cp == '-') {
143                         switch (smatch (++cp, switches)) {
144                         case AMBIGSW:
145                                 ambigsw (cp, switches);
146                                 done (1);
147                         case UNKWNSW:
148                                 adios (NULL, "-%s unknown", cp);
149
150                         case HELPSW:
151                                 snprintf (buf, sizeof(buf), "%s [+folder] [msgs] [switches]",
152                                                 invo_name);
153                                 print_help (buf, switches, 1);
154                                 done (1);
155                         case VERSIONSW:
156                                 print_version(invo_name);
157                                 done (1);
158
159                         case RCACHESW:
160                                 icachesw = &rcachesw;
161                                 goto do_cache;
162                         case WCACHESW:
163                                 icachesw = &wcachesw;
164 do_cache:
165                                 if (!(cp = *argp++) || *cp == '-')
166                                         adios (NULL, "missing argument to %s", argp[-2]);
167                                 switch (*icachesw = smatch (cp, caches)) {
168                                 case AMBIGSW:
169                                         ambigsw (cp, caches);
170                                         done (1);
171                                 case UNKWNSW:
172                                         adios (NULL, "%s unknown", cp);
173                                 default:
174                                         break;
175                                 }
176                                 continue;
177
178                         case CHECKSW:
179                                 checksw++;
180                                 continue;
181                         case NCHECKSW:
182                                 checksw = 0;
183                                 continue;
184
185                         case PARTSW:
186                                 if (!(cp = *argp++) || *cp == '-')
187                                         adios (NULL, "missing argument to %s", argp[-2]);
188                                 if (npart >= NPARTS)
189                                         adios (NULL, "too many parts (starting with %s), %d max",
190                                                    cp, NPARTS);
191                                 parts[npart++] = cp;
192                                 continue;
193
194                         case TYPESW:
195                                 if (!(cp = *argp++) || *cp == '-')
196                                         adios (NULL, "missing argument to %s", argp[-2]);
197                                 if (ntype >= NTYPES)
198                                         adios (NULL, "too many types (starting with %s), %d max",
199                                                    cp, NTYPES);
200                                 types[ntype++] = cp;
201                                 continue;
202
203                         case FILESW:
204                                 if (!(cp = *argp++) || (*cp == '-' && cp[1]))
205                                         adios (NULL, "missing argument to %s", argp[-2]);
206                                 file = *cp == '-' ? cp : path (cp, TFILE);
207                                 continue;
208
209                         case OUTFILESW:
210                                 if (!(cp = *argp++) || (*cp == '-' && cp[1]))
211                                         adios (NULL, "missing argument to %s", argp[-2]);
212                                 outfile = *cp == '-' ? cp : path (cp, TFILE);
213                                 continue;
214
215                         case VERBSW:
216                                 verbosw = 1;
217                                 continue;
218                         case NVERBSW:
219                                 verbosw = 0;
220                                 continue;
221                         case DEBUGSW:
222                                 debugsw = 1;
223                                 continue;
224                         }
225                 }
226                 if (*cp == '+' || *cp == '@') {
227                         if (folder)
228                                 adios (NULL, "only one folder at a time!");
229                         else
230                                 folder = pluspath (cp);
231                 } else
232                                 app_msgarg(&msgs, cp);
233         }
234
235         /* null terminate the list of acceptable parts/types */
236         parts[npart] = NULL;
237         types[ntype] = NULL;
238
239         set_endian ();
240
241         if (outfile == NULL)
242                 adios (NULL, "must specify output file");
243
244         /* Check for public cache location */
245         if ((cache_public = context_find (nmhcache)) && *cache_public != '/')
246                 cache_public = NULL;
247
248         /* Check for private cache location */
249         if (!(cache_private = context_find (nmhprivcache)))
250                 cache_private = ".cache";
251         cache_private = getcpy (m_maildir (cache_private));
252
253         /*
254          * Check for storage directory.  If specified,
255          * then store temporary files there.  Else we
256          * store them in standard nmh directory.
257          */
258         if ((cp = context_find (nmhstorage)) && *cp)
259                 tmp = concat (cp, "/", invo_name, NULL);
260         else
261                 tmp = add (m_maildir (invo_name), NULL);
262
263         if (!context_find ("path"))
264                 free (path ("./", TFOLDER));
265
266         if (file && msgs.size)
267                 adios (NULL, "cannot specify msg and file at same time!");
268
269         /*
270          * check if message is coming from file
271          */
272         if (file) {
273                 if (!(cts = (CT *) calloc ((size_t) 2, sizeof(*cts))))
274                         adios (NULL, "out of memory");
275                 ctp = cts;
276
277                 if ((ct = parse_mime (file)));
278                         *ctp++ = ct;
279         } else {
280                 /*
281                  * message(s) are coming from a folder
282                  */
283                 if (!msgs.size)
284                         app_msgarg(&msgs, "cur");
285                 if (!folder)
286                         folder = getfolder (1);
287                 maildir = m_maildir (folder);
288
289                 if (chdir (maildir) == NOTOK)
290                         adios (maildir, "unable to change directory to");
291
292                 /* read folder and create message structure */
293                 if (!(mp = folder_read (folder)))
294                         adios (NULL, "unable to read folder %s", folder);
295
296                 /* check for empty folder */
297                 if (mp->nummsg == 0)
298                         adios (NULL, "no messages in %s", folder);
299
300                 /* parse all the message ranges/sequences and set SELECTED */
301                 for (msgnum = 0; msgnum < msgs.size; msgnum++)
302                         if (!m_convert (mp, msgs.msgs[msgnum]))
303                                 done (1);
304                 seq_setprev (mp);  /* set the previous-sequence */
305
306                 if (!(cts = (CT *) calloc ((size_t) (mp->numsel + 1), sizeof(*cts))))
307                         adios (NULL, "out of memory");
308                 ctp = cts;
309
310                 for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
311                         if (is_selected(mp, msgnum)) {
312                                 char *msgnam;
313
314                                 msgnam = m_name (msgnum);
315                                 if ((ct = parse_mime (msgnam)))
316                                         *ctp++ = ct;
317                         }
318                 }
319         }
320
321         if (!*cts)
322                 done (1);
323
324         userrs = 1;
325         SIGNAL (SIGQUIT, quitser);
326         SIGNAL (SIGPIPE, pipeser);
327
328         /*
329          * Get the associated umask for the relevant contents.
330          */
331         for (ctp = cts; *ctp; ctp++) {
332                 struct stat st;
333
334                 ct = *ctp;
335                 if (type_ok (ct, 1) && !ct->c_umask) {
336                         if (stat (ct->c_file, &st) != NOTOK)
337                                 ct->c_umask = ~(st.st_mode & 0777);
338                         else
339                                 ct->c_umask = ~m_gmprot();
340                 }
341         }
342
343         /*
344          * Write the content to a file
345          */
346         write_content (cts, outfile);
347
348         /* Now free all the structures for the content */
349         for (ctp = cts; *ctp; ctp++)
350                 free_content (*ctp);
351
352         free ((char *) cts);
353         cts = NULL;
354
355         /* If reading from a folder, do some updating */
356         if (mp) {
357                 context_replace (pfolder, folder);  /* update current folder */
358                 seq_setcur (mp, mp->hghsel);  /* update current message */
359                 seq_save (mp);  /* synchronize sequences  */
360                 context_save ();  /* save the context file  */
361         }
362
363         done (0);
364         return 1;
365 }
366
367
368 static int
369 write_content (CT *cts, char *outfile)
370 {
371         CT ct, *ctp;
372
373         for (ctp = cts; *ctp; ctp++) {
374                 ct = *ctp;
375                 output_message (ct, outfile);
376         }
377
378         flush_errors ();
379         return OK;
380 }
381
382
383 static RETSIGTYPE
384 pipeser (int i)
385 {
386         if (i == SIGQUIT) {
387                 unlink ("core");
388                 fflush (stdout);
389                 fprintf (stderr, "\n");
390                 fflush (stderr);
391         }
392
393         done (1);
394         /* NOTREACHED */
395 }