Renamed r1bindex() to mhbasename(), to make its function becomes clear.
[mmh] / uip / mhbuild.c
1 /*
2 ** mhbuild.c -- expand/translate MIME composition files
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 EBCDICSW  2
32         { "ebcdicsafe", 0 },
33 #define NEBCDICSW  3
34         { "noebcdicsafe", 0 },
35 #define HEADSW  4
36         { "headers", 0 },
37 #define NHEADSW  5
38         { "noheaders", 0 },
39 #define LISTSW  6
40         { "list", 0 },
41 #define NLISTSW  7
42         { "nolist", 0 },
43 #define SIZESW  8
44         { "realsize", 0 },
45 #define NSIZESW  9
46         { "norealsize", 0 },
47 #define RFC934SW  10
48         { "rfc934mode", 0 },
49 #define NRFC934SW  11
50         { "norfc934mode", 0 },
51 #define VERBSW  12
52         { "verbose", 0 },
53 #define NVERBSW  13
54         { "noverbose", 0 },
55 #define RCACHESW  14
56         { "rcache policy", 0 },
57 #define WCACHESW  15
58         { "wcache policy", 0 },
59 #define CONTENTIDSW  16
60         { "contentid", 0 },
61 #define NCONTENTIDSW  17
62         { "nocontentid", 0 },
63 #define VERSIONSW  18
64         { "version", 0 },
65 #define HELPSW  19
66         { "help", 0 },
67 #define DEBUGSW  20
68         { "debug", -5 },
69         { NULL, 0 }
70 };
71
72
73 /* mhbuildsbr.c */
74 extern char *tmp;  /* directory to place temp files */
75
76 /* mhcachesbr.c */
77 extern int rcachesw;
78 extern int wcachesw;
79 extern char *cache_public;
80 extern char *cache_private;
81
82 int debugsw = 0;
83 int verbosw = 0;
84
85 int ebcdicsw = 0;
86 int listsw   = 0;
87 int rfc934sw = 0;
88 int contentidsw = 1;
89
90 /*
91 ** Temporary files
92 */
93 static char infile[BUFSIZ];
94 static int unlink_infile  = 0;
95
96 static char outfile[BUFSIZ];
97 static int unlink_outfile = 0;
98
99 static void unlink_done(int) NORETURN;
100
101 /* mhbuildsbr.c */
102 CT build_mime(char *);
103 int output_message(CT, char *);
104 int output_message_fp(CT, FILE *, char*);
105
106 /* mhlistsbr.c */
107 int list_all_messages(CT *, int, int, int, int);
108
109 /* mhmisc.c */
110 void set_endian(void);
111
112 /* mhfree.c */
113 void free_content(CT);
114
115
116 int
117 main(int argc, char **argv)
118 {
119         int sizesw = 1, headsw = 1;
120         int *icachesw;
121         char *cp, buf[BUFSIZ];
122         char buffer[BUFSIZ], *compfile = NULL;
123         char **argp, **arguments;
124         CT ct, cts[2];
125         FILE *fp = NULL;
126         FILE *fp_out = NULL;
127
128         done = unlink_done;
129
130 #ifdef LOCALE
131         setlocale(LC_ALL, "");
132 #endif
133         invo_name = mhbasename(argv[0]);
134
135         /* read user profile/context */
136         context_read();
137
138         arguments = getarguments(invo_name, argc, argv, 1);
139         argp = arguments;
140
141         while ((cp = *argp++)) {
142                 if (cp[0] == '-' && cp[1] == '\0') {
143                         if (compfile)
144                                 adios(NULL, "cannot specify both standard input and a file");
145                         else
146                                 compfile = cp;
147                         listsw = 0;  /* turn off -list if using std in/out */
148                         verbosw = 0;  /* turn off -verbose listings */
149                         break;
150                 }
151                 if (*cp == '-') {
152                         switch (smatch(++cp, switches)) {
153                         case AMBIGSW:
154                                 ambigsw(cp, switches);
155                                 done(1);
156                         case UNKWNSW:
157                                 adios(NULL, "-%s unknown", cp);
158
159                         case HELPSW:
160                                 snprintf(buf, sizeof(buf), "%s [switches] file", invo_name);
161                                 print_help(buf, switches, 1);
162                                 done(1);
163                         case VERSIONSW:
164                                 print_version(invo_name);
165                                 done(1);
166
167                         case RCACHESW:
168                                 icachesw = &rcachesw;
169                                 goto do_cache;
170                         case WCACHESW:
171                                 icachesw = &wcachesw;
172                         do_cache: ;
173                                 if (!(cp = *argp++) || *cp == '-')
174                                         adios(NULL, "missing argument to %s",
175                                                         argp[-2]);
176                                 switch (*icachesw = smatch(cp, caches)) {
177                                 case AMBIGSW:
178                                         ambigsw(cp, caches);
179                                         done(1);
180                                 case UNKWNSW:
181                                         adios(NULL, "%s unknown", cp);
182                                 default:
183                                         break;
184                                 }
185                                 continue;
186
187                         case CHECKSW:
188                                 checksw++;
189                                 continue;
190                         case NCHECKSW:
191                                 checksw = 0;
192                                 continue;
193
194                         case EBCDICSW:
195                                 ebcdicsw++;
196                                 continue;
197                         case NEBCDICSW:
198                                 ebcdicsw = 0;
199                                 continue;
200
201                         case HEADSW:
202                                 headsw++;
203                                 continue;
204                         case NHEADSW:
205                                 headsw = 0;
206                                 continue;
207
208                         case LISTSW:
209                                 listsw++;
210                                 continue;
211                         case NLISTSW:
212                                 listsw = 0;
213                                 continue;
214
215                         case RFC934SW:
216                                 rfc934sw++;
217                                 continue;
218                         case NRFC934SW:
219                                 rfc934sw = 0;
220                                 continue;
221
222                         case SIZESW:
223                                 sizesw++;
224                                 continue;
225                         case NSIZESW:
226                                 sizesw = 0;
227                                 continue;
228
229                         case CONTENTIDSW:
230                                 contentidsw = 1;
231                                 continue;
232                         case NCONTENTIDSW:
233                                 contentidsw = 0;
234                                 continue;
235
236                         case VERBSW:
237                                 verbosw++;
238                                 continue;
239                         case NVERBSW:
240                                 verbosw = 0;
241                                 continue;
242                         case DEBUGSW:
243                                 debugsw = 1;
244                                 continue;
245                         }
246                 }
247                 if (compfile)
248                         adios(NULL, "only one composition file allowed");
249                 else
250                         compfile = cp;
251         }
252
253         set_endian();
254
255         if ((cp = getenv("MM_NOASK")) && !strcmp(cp, "1"))
256                 listsw  = 0;
257
258         /*
259         ** Check if we've specified an additional profile
260         */
261         if ((cp = getenv("MHBUILD"))) {
262                 if ((fp = fopen(cp, "r"))) {
263                         readconfig((struct node **) 0, fp, cp, 0);
264                         fclose(fp);
265                 } else {
266                         admonish("", "unable to read $MHBUILD profile (%s)",
267                                         cp);
268                 }
269         }
270
271         /*
272         ** Read the standard profile setup
273         */
274         if ((fp = fopen(cp = etcpath("mhn.defaults"), "r"))) {
275                 readconfig((struct node **) 0, fp, cp, 0);
276                 fclose(fp);
277         }
278
279         /* Check for public cache location */
280         if ((cache_public = context_find(nmhcache)) && *cache_public != '/')
281                 cache_public = NULL;
282
283         /* Check for private cache location */
284         if (!(cache_private = context_find(nmhprivcache)))
285                 cache_private = ".cache";
286         cache_private = getcpy(m_maildir(cache_private));
287
288         /*
289         ** Check for storage directory.  If defined, we
290         ** will store temporary files there.  Else we
291         ** store them in standard nmh directory.
292         */
293         if ((cp = context_find(nmhstorage)) && *cp)
294                 tmp = concat(cp, "/", invo_name, NULL);
295         else
296                 tmp = add(m_maildir(invo_name), NULL);
297
298         if (!context_find("path"))
299                 free(path("./", TFOLDER));
300
301         /* Check if we have a file to process */
302         if (!compfile)
303                 adios(NULL, "need to specify a %s composition file",
304                                 invo_name);
305
306         /*
307         ** Process the composition file from standard input.
308         */
309         if (compfile[0] == '-' && compfile[1] == '\0') {
310                 /* copy standard input to temporary file */
311                 strncpy(infile, m_mktemp(invo_name, NULL, &fp),
312                                 sizeof(infile));
313                 while (fgets(buffer, BUFSIZ, stdin))
314                         fputs(buffer, fp);
315                 fclose(fp);
316                 unlink_infile = 1;
317
318                 /* build the content structures for MIME message */
319                 ct = build_mime(infile);
320                 cts[0] = ct;
321                 cts[1] = NULL;
322
323                 /* output MIME message to this temporary file */
324                 strncpy(outfile, m_mktemp(invo_name, NULL, &fp_out),
325                                 sizeof(outfile));
326                 unlink_outfile = 1;
327
328                 /* output the message */
329                 output_message_fp(ct, fp_out, outfile);
330                 fclose(fp_out);
331
332                 /* output the temp file to standard output */
333                 if ((fp = fopen(outfile, "r")) == NULL)
334                         adios(outfile, "unable to open");
335                 while (fgets(buffer, BUFSIZ, fp))
336                         fputs(buffer, stdout);
337                 fclose(fp);
338
339                 unlink(infile);
340                 unlink_infile = 0;
341
342                 unlink(outfile);
343                 unlink_outfile = 0;
344
345                 free_content(ct);
346                 done(0);
347         }
348
349         /*
350         ** Process the composition file from a file.
351         */
352
353         /* build the content structures for MIME message */
354         ct = build_mime(compfile);
355         cts[0] = ct;
356         cts[1] = NULL;
357
358         /* output MIME message to this temporary file */
359         strncpy(outfile, m_mktemp2(compfile, invo_name, NULL, &fp_out),
360                         sizeof(outfile));
361         unlink_outfile = 1;
362
363         /* output the message */
364         output_message_fp(ct, fp_out, outfile);
365         fclose(fp_out);
366
367         /*
368         ** List the message info
369         */
370         if (listsw)
371                 list_all_messages(cts, headsw, sizesw, verbosw, debugsw);
372
373         /* Rename composition draft */
374         snprintf(buffer, sizeof(buffer), "%s.orig", m_backup(compfile));
375         if (rename(compfile, buffer) == NOTOK) {
376                 adios(compfile, "unable to rename comp draft %s to", buffer);
377         }
378
379         /* Rename output file to take its place */
380         if (rename(outfile, compfile) == NOTOK) {
381                 advise(outfile, "unable to rename output %s to", compfile);
382                 rename(buffer, compfile);
383                 done(1);
384         }
385         unlink_outfile = 0;
386
387         free_content(ct);
388         done(0);
389         return 1;
390 }
391
392
393 static void
394 unlink_done(int status)
395 {
396         /*
397         ** Check if we need to remove stray temporary files.
398         */
399         if (unlink_infile)
400                 unlink(infile);
401         if (unlink_outfile)
402                 unlink(outfile);
403
404         exit(status);
405 }