Added all of the MH sources, including RCS files, in
[mmh] / docs / historical / mh-6.8.5 / uip / forw.c
1 /* forw.c - forward messages */
2 #ifndef lint
3 static char ident[] = "@(#)$Id: forw.c,v 1.15 1995/12/06 21:07:03 jromine Exp $";
4 #endif  /* lint */
5
6 #include "../h/mh.h"
7 #include "../h/formatsbr.h"
8 #include "../zotnet/tws.h"
9 #include <stdio.h>
10 #include <sys/types.h>
11 #include <sys/stat.h>
12 #ifdef LOCALE
13 #include        <locale.h>
14 #endif
15
16 #ifndef MIME
17 #define MIMEminc(a)     (a)
18 #else   /* MIME */
19 #define MIMEminc(a)     0
20 #endif  /* MIME */
21
22 #define IFORMAT "digest-issue-%s"
23 #define VFORMAT "digest-volume-%s"
24
25 static  mhl_draft(), copy_draft(), build_form();
26 /* \f */
27
28 static struct swit switches[] = {
29 #define ANNOSW  0
30     "annotate", 0,
31 #define NANNOSW 1
32     "noannotate", 0,
33
34 #define DFOLDSW 2
35     "draftfolder +folder", 0,
36 #define DMSGSW  3
37     "draftmessage msg", 0,
38 #define NDFLDSW 4
39     "nodraftfolder", 0,
40
41 #define EDITRSW 5
42     "editor editor", 0,
43 #define NEDITSW 6
44     "noedit", 0,
45
46 #define FILTSW  7
47     "filter filterfile", 0,
48 #define FORMSW  8
49     "form formfile", 0,
50
51 #define FRMTSW  9
52     "format", 5,
53 #define NFRMTSW 10
54     "noformat", 7,
55
56 #define INPLSW  11
57     "inplace", 0,
58 #define NINPLSW 12
59     "noinplace", 0,
60
61 #define MIMESW  13
62     "mime", MIMEminc(-4),
63 #define NMIMESW 14
64     "nomime", MIMEminc(-6),
65
66 #define DGSTSW  15
67     "digest list", 0,
68 #define ISSUESW 16
69     "issue number", 0,
70 #define VOLUMSW 17
71     "volume number", 0,
72
73 #define WHATSW  18
74     "whatnowproc program", 0,
75 #define NWHATSW 19
76     "nowhatnowproc", 0,
77
78 #define HELPSW  20
79     "help", 4,
80
81 #define FILESW  21
82     "file file", -4,            /* interface from msh */
83
84 #define DASHSW  22
85     "dashmunging", -4,          /* interface to mhl */
86 #define NDASHSW 23
87     "nodashmunging", -6,
88
89 #ifdef  MHE
90 #define BILDSW  24
91     "build", -5,                /* interface from mhe */
92 #endif  /* MHE */
93
94     NULL, 0
95 };
96
97 /* \f */
98
99 static struct swit aqrnl[] = {
100 #define NOSW    0
101     "quit", 0,
102 #define YESW    1
103     "replace", 0,
104 #define LISTDSW 2
105     "list", 0,
106 #define REFILSW 3
107     "refile +folder", 0,
108 #define NEWSW   4
109     "new", 0,
110
111     NULL, 0
112 };
113
114
115 static struct swit aqrl[] = {
116     "quit", 0,
117     "replace", 0,
118     "list", 0,
119     "refile +folder", 0,
120
121     NULL, 0
122 };
123
124 /* \f */
125
126 static char drft[BUFSIZ];
127
128 static char delim3[] =
129     "\n------------------------------------------------------------\n\n";
130 static char delim4[] = "\n------------------------------\n\n";
131
132
133 static struct msgs *mp = NULL;          /* used a lot */
134
135
136 long    time ();
137 off_t   lseek();
138
139 /* \f */
140
141 /* ARGSUSED */
142
143 main (argc, argv)
144 int     argc;
145 char   *argv[];
146 {
147     int     msgp = 0,
148             anot = 0,
149             inplace = 0,
150             mime = 0,
151             issue = 0,
152             volume = 0,
153             dashflg = 1,
154 #ifdef  MHE
155             buildsw = 0,
156 #endif  /* MHE */
157             nedit = 0,
158             nwhat = 0,
159             i,
160             in,
161             out,
162             isdf = 0,
163             msgnum;
164     char   *cp,
165            *cwd,
166            *maildir,
167            *dfolder = NULL,
168            *dmsg = NULL,
169            *digest = NULL,
170            *ed = NULL,
171            *file = NULL,
172            *filter = NULL,
173            *folder = NULL,
174            *form = NULL,
175             buf[100],
176             value[10],
177           **ap,
178           **argp,
179            *arguments[MAXARGS],
180            *msgs[MAXARGS];
181     struct stat st;
182
183 #ifdef LOCALE
184         setlocale(LC_ALL, "");
185 #endif
186     invo_name = r1bindex (argv[0], '/');
187     if ((cp = m_find (invo_name)) != NULL) {
188         ap = brkstring (cp = getcpy (cp), " ", "\n");
189         ap = copyip (ap, arguments);
190     }
191     else
192         ap = arguments;
193     (void) copyip (argv + 1, ap);
194     argp = arguments;
195
196 /* \f */
197
198     while (cp = *argp++) {
199         if (*cp == '-')
200             switch (smatch (++cp, switches)) {
201                 case AMBIGSW: 
202                     ambigsw (cp, switches);
203                     done (1);
204                 case UNKWNSW: 
205                     adios (NULLCP, "-%s unknown", cp);
206                 case HELPSW: 
207                     (void) sprintf (buf, "%s [+folder] [msgs] [switches]",
208                         invo_name);
209                     help (buf, switches);
210                     done (1);
211
212                 case ANNOSW: 
213                     anot++;
214                     continue;
215                 case NANNOSW: 
216                     anot = 0;
217                     continue;
218
219                 case EDITRSW: 
220                     if (!(ed = *argp++) || *ed == '-')
221                         adios (NULLCP, "missing argument to %s", argp[-2]);
222                     nedit = 0;
223                     continue;
224                 case NEDITSW:
225                     nedit++;
226                     continue;
227
228                 case WHATSW: 
229                     if (!(whatnowproc = *argp++) || *whatnowproc == '-')
230                         adios (NULLCP, "missing argument to %s", argp[-2]);
231                     nwhat = 0;
232                     continue;
233 #ifdef  MHE
234                 case BILDSW:
235                     buildsw++;  /* fall... */
236 #endif  /* MHE */
237                 case NWHATSW: 
238                     nwhat++;
239                     continue;
240
241                 case FILESW: 
242                     if (file)
243                         adios (NULLCP, "only one file at a time!");
244                     if (!(cp = *argp++) || *cp == '-')
245                         adios (NULLCP, "missing argument to %s", argp[-2]);
246                     file = path (cp, TFILE);
247                     continue;
248                 case FILTSW:
249                     if (!(cp = *argp++) || *cp == '-')
250                         adios (NULLCP, "missing argument to %s", argp[-2]);
251                     filter = getcpy (libpath (cp));
252                     mime = 0;
253                     continue;
254                 case FORMSW: 
255                     if (!(form = *argp++) || *form == '-')
256                         adios (NULLCP, "missing argument to %s", argp[-2]);
257                     continue;
258
259                 case FRMTSW:
260                     filter = getcpy (libpath (mhlforward));
261                     continue;
262                 case NFRMTSW:
263                     filter = NULL;
264                     continue;
265
266                 case INPLSW: 
267                     inplace++;
268                     continue;
269                 case NINPLSW: 
270                     inplace = 0;
271                     continue;
272
273                 case MIMESW:
274 #ifdef  MIME
275                     mime++;
276                     filter = NULL;
277 #endif
278                     continue;
279                 case NMIMESW: 
280                     mime = 0;
281                     continue;
282
283                 case DGSTSW: 
284                     if (!(digest = *argp++) || *digest == '-')
285                         adios (NULLCP, "missing argument to %s", argp[-2]);
286                     mime = 0;
287                     continue;
288                 case ISSUESW:
289                     if (!(cp = *argp++) || *cp == '-')
290                         adios (NULLCP, "missing argument to %s", argp[-2]);
291                     if ((issue = atoi (cp)) < 1)
292                         adios (NULLCP, "bad argument %s %s", argp[-2], cp);
293                     continue;
294                 case VOLUMSW:
295                     if (!(cp = *argp++) || *cp == '-')
296                         adios (NULLCP, "missing argument to %s", argp[-2]);
297                     if ((volume = atoi (cp)) < 1)
298                         adios (NULLCP, "bad argument %s %s", argp[-2], cp);
299                     continue;
300
301                 case DFOLDSW: 
302                     if (dfolder)
303                         adios (NULLCP, "only one draft folder at a time!");
304                     if (!(cp = *argp++) || *cp == '-')
305                         adios (NULLCP, "missing argument to %s", argp[-2]);
306                     dfolder = path (*cp == '+' || *cp == '@' ? cp + 1 : cp,
307                                     *cp != '@' ? TFOLDER : TSUBCWF);
308                     continue;
309                 case DMSGSW:
310                     if (dmsg)
311                         adios (NULLCP, "only one draft message at a time!");
312                     if (!(dmsg = *argp++) || *dmsg == '-')
313                         adios (NULLCP, "missing argument to %s", argp[-2]);
314                     continue;
315                 case NDFLDSW: 
316                     dfolder = NULL;
317                     isdf = NOTOK;
318                     continue;
319
320                 case DASHSW: 
321                     dashflg++;
322                     continue;
323                 case NDASHSW: 
324                     dashflg = 0;
325                     continue;
326             }
327         if (*cp == '+' || *cp == '@') {
328             if (folder)
329                 adios (NULLCP, "only one folder at a time!");
330             else
331                 folder = path (cp + 1, *cp == '+' ? TFOLDER : TSUBCWF);
332         }
333         else
334             msgs[msgp++] = cp;
335     }
336
337 /* \f */
338
339     cwd = getcpy (pwd ());
340
341     if (!m_find ("path"))
342         free (path ("./", TFOLDER));
343     if (file && (msgp || folder))
344         adios (NULLCP, "can't mix files and folders/msgs");
345
346 try_it_again: ;
347 #ifndef MHE
348     (void) strcpy (drft, m_draft (dfolder, dmsg, NOUSE, &isdf));
349     if (stat (drft, &st) != NOTOK) {
350 #else   /* MHE */
351     (void) strcpy (drft, buildsw ? m_maildir ("draft")
352                           : m_draft (dfolder, NULLCP, NOUSE, &isdf));
353     if (!buildsw && stat (drft, &st) != NOTOK) {
354 #endif  /* MHE */
355         printf ("Draft \"%s\" exists (%ld bytes).", drft, (long) st.st_size);
356         for (i = LISTDSW; i != YESW;) {
357             if (!(argp = getans ("\nDisposition? ", isdf ? aqrnl : aqrl)))
358                 done (1);
359             switch (i = smatch (*argp, isdf ? aqrnl : aqrl)) {
360                 case NOSW: 
361                     done (0);
362                 case NEWSW: 
363                     dmsg = NULL;
364                     goto try_it_again;
365                 case YESW: 
366                     break;
367                 case LISTDSW: 
368                     (void) showfile (++argp, drft);
369                     break;
370                 case REFILSW: 
371                     if (refile (++argp, drft) == 0)
372                         i = YESW;
373                     break;
374                 default: 
375                     advise (NULLCP, "say what?");
376                     break;
377             }
378         }
379     }
380
381 /* \f */
382
383     if (file) {
384         anot = 0;
385         goto go_to_it;
386     }
387
388     if (!msgp)
389         msgs[msgp++] = "cur";
390     if (!folder)
391         folder = m_getfolder ();
392     maildir = m_maildir (folder);
393
394     if (chdir (maildir) == NOTOK)
395         adios (maildir, "unable to change directory to");
396     if (!(mp = m_gmsg (folder)))
397         adios (NULLCP, "unable to read folder %s", folder);
398     if (mp -> hghmsg == 0)
399         adios (NULLCP, "no messages in %s", folder);
400
401     for (msgnum = 0; msgnum < msgp; msgnum++)
402         if (!m_convert (mp, msgs[msgnum]))
403             done (1);
404     m_setseq (mp);
405
406 /* \f */
407
408 go_to_it: ;
409     if (filter && access (filter, 04) == NOTOK)
410         adios (filter, "unable to read");
411
412     if (digest) {
413         if (issue == 0) {
414             (void) sprintf (buf, IFORMAT, digest);
415             if (volume == 0
416                     && (cp = m_find (buf))
417                     && ((issue = atoi (cp)) < 0))
418                 issue = 0;
419             issue++;
420         }
421         if (volume == 0)
422             (void) sprintf (buf, VFORMAT, digest);
423             if ((cp = m_find (buf)) == NULL || (volume = atoi (cp)) <= 0)
424                 volume = 1;
425         if (!form)
426             form = digestcomps;
427         in = build_form (form, digest, volume, issue);
428     }
429     else
430         if (form) {
431             if ((in = open (libpath (form), 0)) == NOTOK)
432                 adios (form, "unable to open form file");
433         }
434         else {
435             if ((in = open (libpath (forwcomps), 0)) == NOTOK)
436                 adios (forwcomps, "unable to open default components file");
437             form = forwcomps;
438         }
439
440     if ((out = creat (drft, m_gmprot ())) == NOTOK)
441         adios (drft, "unable to create");
442
443     cpydata (in, out, form, drft);
444     (void) close (in);
445
446 /* \f */
447
448     if (file) {
449         if ((in = open (file, 0)) == NOTOK)
450             adios (file, "unable to open");
451         cpydata (in, out, file, drft);
452         (void) close (in);
453         (void) close (out);
454         goto edit_it;
455     }
456
457     if (filter)
458         mhl_draft (out, digest, volume, issue, drft, filter, dashflg);
459     else
460         copy_draft (out, digest, drft, volume, issue, mime);
461     (void) close (out);
462
463     if (digest) {
464         (void) sprintf (buf, IFORMAT, digest);
465         (void) sprintf (value, "%d", issue);
466         m_replace (buf, getcpy (value));
467         (void) sprintf (buf, VFORMAT, digest);
468         (void) sprintf (value, "%d", volume);
469         m_replace (buf, getcpy (value));
470     }
471
472     m_replace (pfolder, folder);
473     if (mp -> lowsel != mp -> curmsg)
474         m_setcur (mp, mp -> lowsel);
475     m_sync (mp);
476     m_update ();
477
478 edit_it: ;
479     if (nwhat)
480         done (0);
481     (void) what_now (ed, nedit, NOUSE, drft, NULLCP, 0, mp,
482         anot ? "Forwarded" : NULLCP, inplace, cwd);
483     done (1);
484 }
485
486 /* \f */
487
488 static  mhl_draft  (out, digest, volume, issue, file, filter, dashflg)
489 int     out,
490         volume,
491         issue,
492         dashflg;
493 register char   *digest,
494                 *file,
495                 *filter;
496 {
497     int     i,
498             child_id,
499             msgnum,
500             pd[2];
501     char   *vec[MAXARGS];
502     char    buf1[BUFSIZ];
503     char    buf2[BUFSIZ];
504     
505     if (pipe (pd) == NOTOK)
506         adios ("pipe", "unable to create");
507
508     vec[0] = r1bindex (mhlproc, '/');
509
510     for (i = 0; (child_id = fork ()) == NOTOK && i < 5; i++)
511         sleep (5);
512     switch (child_id) {
513         case NOTOK: 
514             adios ("fork", "unable to");
515
516         case OK: 
517             (void) close (pd[0]);
518             (void) dup2 (pd[1], 1);
519             (void) close (pd[1]);
520
521             i = 1;
522             vec[i++] = "-forwall";
523             vec[i++] = "-form";
524             vec[i++] = filter;
525             if (digest) {
526                 vec[i++] = "-digest";
527                 vec[i++] = digest;
528                 vec[i++] = "-issue";
529                 sprintf(buf1, "%d", issue); vec[i++] = buf1;
530                 vec[i++] = "-volume";
531                 sprintf(buf2, "%d", volume); vec[i++] = buf2;
532             }
533             vec[i++] = dashflg ? "-dashmunging" : "-nodashmunging";
534             if (mp -> numsel >= MAXARGS - i)
535                 adios (NULLCP, "more than %d messages for %s exec",
536                         vec[0], MAXARGS - i);
537             for (msgnum = mp -> lowsel; msgnum <= mp -> hghsel; msgnum++)
538                 if (mp -> msgstats[msgnum] & SELECTED)
539                     vec[i++] = getcpy (m_name (msgnum));
540             vec[i] = NULL;
541
542             execvp (mhlproc, vec);
543             fprintf (stderr, "unable to exec ");
544             perror (mhlproc);
545             _exit (-1);
546
547         default: 
548             (void) close (pd[1]);
549             cpydata (pd[0], out, vec[0], file);
550             (void) close (pd[0]);
551             (void) pidXwait (child_id, mhlproc);
552             break;
553     }
554 }
555
556 /* \f */
557
558 static  copy_draft (out, digest, file, volume, issue, mime)
559 int     out,
560         volume,
561         issue,
562         mime;
563 register char   *digest,
564                 *file;
565 {
566     int     fd,i,
567             msgcnt,
568             msgnum;
569     register char  *bp,
570                    *msgnam;
571     char    buffer[BUFSIZ];
572
573 #ifdef MIME
574     if (mime) {
575         (void) sprintf (buffer, "#forw [forwarded message%s] +%s",
576                         mp -> numsel == 1 ? "" : "s", mp -> foldpath);
577         (void) write (out, buffer, strlen (buffer));
578         for (msgnum = mp -> lowsel; msgnum <= mp -> hghsel; msgnum++)
579             if (mp -> msgstats[msgnum] & SELECTED) {
580                 (void) sprintf (buffer, " %s", m_name (msgnum));
581                 (void) write (out, buffer, strlen (buffer));
582             }
583         (void) write (out, "\n", 1);
584
585         return;
586     }
587 #endif /* MIME */
588
589     msgcnt = 1;
590     for (msgnum = mp -> lowsel; msgnum <= mp -> hghsel; msgnum++)
591         if (mp -> msgstats[msgnum] & SELECTED) {
592             if (digest)
593                 (void) strcpy (buffer,
594                         msgnum == mp -> lowsel ? delim3 : delim4);
595             else {
596                 (void) strcpy (bp = buffer, "\n-------"), bp += strlen (bp);
597                 if (msgnum == mp -> lowsel)
598                     (void) sprintf (bp, " Forwarded Message%s",
599                             mp -> numsel > 1 ? "s" : "");
600                 else
601                     (void) sprintf (bp, " Message %d", msgcnt);
602                 bp += strlen (bp);
603                 (void) strcpy (bp, "\n\n");
604             }
605             (void) write (out, buffer, strlen (buffer));
606
607             if ((fd = open (msgnam = m_name (msgnum), 0)) == NOTOK) {
608                 admonish (msgnam, "unable to read message");
609                 continue;
610             }
611             cpydgst (fd, out, msgnam, file);
612             (void) close (fd);
613
614             msgcnt++;
615         }
616
617     if (digest)
618         (void) strcpy (buffer, delim4);
619     else
620         (void) sprintf (buffer, "\n------- End of Forwarded Message%s\n\n",
621                 mp -> numsel > 1 ? "s" : "");
622     (void) write (out, buffer, strlen (buffer));
623
624     if (digest) {
625         (void) sprintf (buffer, "End of %s Digest [Volume %d Issue %d]\n", digest, volume, issue);
626         i = strlen (buffer);
627         for (bp = buffer + i; i > 1; i--)
628             *bp++ = '*';
629         *bp++ = '\n';
630         *bp = 0;
631         (void) write (out, buffer, strlen (buffer));
632     }
633 }
634
635 /* \f */
636
637 static int  build_form (form, digest, volume, issue)
638 register char  *form,
639                *digest;
640 int     volume,
641         issue;
642 {
643     int     in;
644     int     fmtsize;
645     register char *nfs;
646     char   *line,
647             tmpfil[BUFSIZ];
648     register    FILE *tmp;
649     register struct comp *cptr;
650     struct format *fmt;
651     int     dat[5];
652
653     nfs = new_fs (form, NULLCP, NULLCP);
654     fmtsize = strlen (nfs) + 256;
655     (void) fmt_compile (nfs, &fmt);
656
657     FINDCOMP (cptr, "digest");
658     if (cptr)
659         cptr->c_text = digest;
660     FINDCOMP (cptr, "date");
661     if (cptr)
662         cptr->c_text = getcpy(dtimenow ());
663
664     dat[0] = issue;
665     dat[1] = volume;
666     dat[2] = 0;
667     dat[3] = fmtsize;
668     dat[4] = 0;
669
670     (void) strcpy (tmpfil, m_tmpfil (invo_name));
671     if ((tmp = fopen (tmpfil, "w+")) == NULL)
672         adios (tmpfil, "unable to create");
673     (void) unlink (tmpfil);
674     if ((in = dup (fileno (tmp))) == NOTOK)
675         adios ("dup", "unable to");
676
677     if ((line = malloc ((unsigned) fmtsize)) == NULLCP)
678         adios (NULLCP, "unable to allocate format line storage");
679     (void) fmtscan (fmt, line, fmtsize, dat);
680     (void) fputs (line, tmp);
681     (void) free (line);
682     if (fclose (tmp))
683         adios (tmpfil, "error writing");
684
685     (void) lseek (in, (off_t)0, 0);
686     return in;
687 }