a665536fbeb14597ae279e511ee9667302e22a1e
[mmh] / uip / mshcmds.c
1
2 /*
3  * mshcmds.c -- command handlers in msh
4  *
5  * $Id$
6  */
7
8 #include <h/mh.h>
9 #include <h/signals.h>
10 #include <h/dropsbr.h>
11 #include <h/fmt_scan.h>
12 #include <h/scansbr.h>
13 #include <zotnet/tws/tws.h>
14 #include <zotnet/mts/mts.h>
15 #include <errno.h>
16 #include <setjmp.h>
17 #include <signal.h>
18 #include <h/msh.h>
19 #include <h/picksbr.h>
20
21 extern int errno;
22
23 static char delim3[] = "-------";       /* from burst.c */
24
25 static int mhlnum;
26 static FILE *mhlfp;
27
28 #if defined(NNTP) && defined(MPOP)
29 # undef MPOP
30 #endif
31
32 #ifdef MPOP
33 # ifdef BPOP
34 extern int pmsh;
35 extern char response[];
36 # endif
37 #endif /* MPOP */
38
39 /*
40  * Type for a compare function for qsort.  This keeps
41  * the compiler happy.
42  */
43 typedef int (*qsort_comp) (const void *, const void *);
44
45 /*
46  * prototypes
47  */
48 void clear_screen (void);   /* from termsbr.c */
49 int SOprintf (char *, ...); /* from termsbr.c */
50 int sc_width (void);        /* from termsbr.c */
51
52 /*
53  * static prototypes
54  */
55 static int burst (struct Msg *, int, int, int, int);
56 static void forw (char *, char *, int, char **);
57 static void rmm (void);
58 static void show (int);
59 static int eom_action (int);
60 static FILE *mhl_action (char *);
61 static int ask (int);
62 static int is_nontext (int);
63 static int get_fields (char *, char *, int, struct Msg *);
64 static int msgsort (struct Msg *, struct Msg *);
65 static int subsort (struct Msg *, struct Msg *);
66 static char *sosmash (char *, char *);
67 static int process (int, char *, int, char **);
68 static void copy_message (int, FILE *);
69 static void copy_digest (int, FILE *);
70
71
72 void
73 forkcmd (char **args, char *pgm)
74 {
75     int child_id;
76     char *vec[MAXARGS];
77
78     vec[0] = r1bindex (pgm, '/');
79     copyip (args, vec + 1, MAXARGS - 1);
80
81     if (fmsh) {
82         context_del (pfolder);
83         context_replace (pfolder, fmsh);/* update current folder   */
84         seq_save (mp);
85         context_save ();                /* save the context file   */
86     }
87     fflush (stdout);
88     switch (child_id = fork ()) {
89         case NOTOK: 
90             advise ("fork", "unable to");
91             return;
92
93         case OK: 
94             closefds (3);
95             SIGNAL (SIGINT, istat);
96             SIGNAL (SIGQUIT, qstat);
97
98             execvp (pgm, vec);
99             fprintf (stderr, "unable to exec ");
100             perror (cmd_name);
101             _exit (1);
102
103         default: 
104             pidXwait (child_id, NULL);
105             break;
106     }
107     if (fmsh) {                 /* assume the worst case */
108         mp->msgflags |= MODIFIED;
109         modified++;
110     }
111 }
112
113
114 static struct swit distswit[] = {
115 #define DIANSW                    0
116     { "annotate", 0 },
117 #define DINANSW                   1
118     { "noannotate", 0 },
119 #define DIDFSW                    2
120     { "draftfolder +folder", 0 },
121 #define DIDMSW                    3
122     { "draftmessage msg", 0 },
123 #define DINDFSW                   4
124     { "nodraftfolder", 0 },
125 #define DIEDTSW                   5
126     { "editor editor", 0 },
127 #define DINEDSW                   6
128     { "noedit", 0 },
129 #define DIFRMSW                   7
130     { "form formfile", 0 },
131 #define DIINSW                    8
132     { "inplace", 0 },
133 #define DININSW                   9
134     { "noinplace", 0 },
135 #define DIWHTSW                  10
136     { "whatnowproc program", 0 },
137 #define DINWTSW                  11
138     { "nowhatnowproc", 0 },
139 #define DIHELP                   12
140     { "help", 4 },
141     { NULL, 0 }
142 };
143
144
145 void
146 distcmd (char **args)
147 {
148     int vecp = 1;
149     char *cp, *msg = NULL;
150     char buf[BUFSIZ], *vec[MAXARGS];
151
152     if (fmsh) {
153         forkcmd (args, cmd_name);
154         return;
155     }
156
157     while ((cp = *args++)) {
158         if (*cp == '-')
159             switch (smatch (++cp, distswit)) {
160                 case AMBIGSW: 
161                     ambigsw (cp, distswit);
162                     return;
163                 case UNKWNSW: 
164                     fprintf (stderr, "-%s unknown\n", cp);
165                     return;
166                 case DIHELP: 
167                     snprintf (buf, sizeof(buf), "%s [msgs] [switches]", cmd_name);
168                     print_help (buf, distswit, 1);
169                     return;
170
171                 case DIANSW:    /* not implemented */
172                 case DINANSW: 
173                 case DIINSW: 
174                 case DININSW: 
175                     continue;
176
177                 case DINDFSW:
178                 case DINEDSW:
179                 case DINWTSW:
180                     vec[vecp++] = --cp;
181                     continue;
182
183                 case DIEDTSW: 
184                 case DIFRMSW: 
185                 case DIDFSW:
186                 case DIDMSW:
187                 case DIWHTSW:
188                     vec[vecp++] = --cp;
189                     if (!(cp = *args++) || *cp == '-') {
190                         advise (NULL, "missing argument to %s", args[-2]);
191                         return;
192                     }
193                     vec[vecp++] = cp;
194                     continue;
195             }
196         if (*cp == '+' || *cp == '@') {
197             advise (NULL, "sorry, no folders allowed!");
198             return;
199         }
200         else
201             if (msg) {
202                 advise (NULL, "only one message at a time!");
203                 return;
204             }
205             else
206                 msg = cp;
207     }
208
209     vec[0] = cmd_name;
210     vec[vecp++] = "-file";
211     vec[vecp] = NULL;
212     if (!msg)
213         msg = "cur";
214     if (!m_convert (mp, msg))
215         return;
216     seq_setprev (mp);
217
218     if (mp->numsel > 1) {
219         advise (NULL, "only one message at a time!");
220         return;
221     }
222     process (mp->hghsel, cmd_name, vecp, vec);
223     seq_setcur (mp, mp->hghsel);
224 }
225
226
227 static struct swit explswit[] = {
228 #define EXINSW         0
229     { "inplace", 0 },
230 #define EXNINSW        1
231     { "noinplace", 0 },
232 #define EXQISW         2
233     { "quiet", 0 },
234 #define EXNQISW        3
235     { "noquiet", 0 },
236 #define EXVBSW         4
237     { "verbose", 0 },
238 #define EXNVBSW        5
239     { "noverbose", 0 },
240 #define EXHELP         6
241     { "help", 4 },
242     { NULL, 0 }
243 };
244
245
246 void
247 explcmd (char **args)
248 {
249     int inplace = 0, quietsw = 0, verbosw = 0;
250     int msgp = 0, hi, msgnum;
251     char *cp, buf[BUFSIZ], *msgs[MAXARGS];
252     struct Msg *smsgs;
253
254     if (fmsh) {
255         forkcmd (args, cmd_name);
256         return;
257     }
258
259     while ((cp = *args++)) {
260         if (*cp == '-')
261             switch (smatch (++cp, explswit)) {
262                 case AMBIGSW: 
263                     ambigsw (cp, explswit);
264                     return;
265                 case UNKWNSW: 
266                     fprintf (stderr, "-%s unknown\n", cp);
267                     return;
268                 case EXHELP: 
269                     snprintf (buf, sizeof(buf), "%s [msgs] [switches]", cmd_name);
270                     print_help (buf, explswit, 1);
271                     return;
272
273                 case EXINSW: 
274                     inplace++;
275                     continue;
276                 case EXNINSW: 
277                     inplace = 0;
278                     continue;
279                 case EXQISW: 
280                     quietsw++;
281                     continue;
282                 case EXNQISW: 
283                     quietsw = 0;
284                     continue;
285                 case EXVBSW: 
286                     verbosw++;
287                     continue;
288                 case EXNVBSW: 
289                     verbosw = 0;
290                     continue;
291             }
292         if (*cp == '+' || *cp == '@') {
293             advise (NULL, "sorry, no folders allowed!");
294             return;
295         }
296         else
297             msgs[msgp++] = cp;
298     }
299
300     if (!msgp)
301         msgs[msgp++] = "cur";
302     for (msgnum = 0; msgnum < msgp; msgnum++)
303         if (!m_convert (mp, msgs[msgnum]))
304             return;
305     seq_setprev (mp);
306
307     smsgs = (struct Msg *)
308                 calloc ((size_t) (MAXFOLDER + 2), sizeof *smsgs);
309     if (smsgs == NULL)
310         adios (NULL, "unable to allocate folder storage");
311
312     hi = mp->hghmsg + 1;
313     interrupted = 0;
314     for (msgnum = mp->lowsel;
315             msgnum <= mp->hghsel && !interrupted;
316             msgnum++)
317         if (is_selected (mp, msgnum))
318             if (burst (smsgs, msgnum, inplace, quietsw, verbosw) != OK)
319                 break;
320
321     free ((char *) smsgs);
322
323     if (inplace)
324         seq_setcur (mp, mp->lowsel);
325     else
326         if (hi <= mp->hghmsg)
327             seq_setcur (mp, hi);
328
329     mp->msgflags |= MODIFIED;
330     modified++;
331 }
332
333
334 static int
335 burst (struct Msg *smsgs, int msgnum, int inplace, int quietsw, int verbosw)
336 {
337     int i, j, ld3, wasdlm, msgp;
338     long pos;
339     char c, buffer[BUFSIZ];
340     register FILE *zp;
341
342     ld3 = strlen (delim3);
343
344     if (Msgs[msgnum].m_scanl) {
345         free (Msgs[msgnum].m_scanl);
346         Msgs[msgnum].m_scanl = NULL;
347     }
348
349     pos = ftell (zp = msh_ready (msgnum, 1));
350     for (msgp = 0; msgp <= MAXFOLDER;) {
351         while (fgets (buffer, sizeof buffer, zp) != NULL
352                 && buffer[0] == '\n'
353                 && pos < Msgs[msgnum].m_stop)
354             pos += (long) strlen (buffer);
355         if (feof (zp) || pos >= Msgs[msgnum].m_stop)
356             break;
357         fseek (zp, pos, SEEK_SET);
358         smsgs[msgp].m_start = pos;
359
360         for (c = 0;
361                 pos < Msgs[msgnum].m_stop
362                 && fgets (buffer, sizeof buffer, zp) != NULL;
363                 c = buffer[0])
364             if (strncmp (buffer, delim3, ld3) == 0
365                     && (msgp == 1 || c == '\n')
366                     && peekc (zp) == '\n')
367                 break;
368             else
369                 pos += (long) strlen (buffer);
370
371         wasdlm = strncmp (buffer, delim3, ld3) == 0;
372         if (smsgs[msgp].m_start != pos)
373             smsgs[msgp++].m_stop = (c == '\n' && wasdlm) ? pos - 1 : pos;
374         if (feof (zp) || pos >= Msgs[msgnum].m_stop) {
375             if (wasdlm)
376                 smsgs[msgp - 1].m_stop -= ((long) strlen (buffer) + 1);
377             break;
378         }
379         pos += (long) strlen (buffer);
380     }
381
382     switch (msgp--) {           /* toss "End of XXX Digest" */
383         case 0: 
384             adios (NULL, "burst() botch -- you lose big");
385
386         case 1: 
387             if (!quietsw)
388                 printf ("message %d not in digest format\n", msgnum);
389             return OK;
390
391         default: 
392             if (verbosw)
393                 printf ("%d message%s exploded from digest %d\n",
394                         msgp, msgp != 1 ? "s" : "", msgnum);
395             break;
396     }
397
398     if ((i = msgp + mp->hghmsg) > MAXFOLDER) {
399         advise (NULL, "more than %d messages", MAXFOLDER);
400         return NOTOK;
401     }
402     if (!(mp = folder_realloc (mp, mp->lowoff, i)))
403         adios (NULL, "unable to allocate folder storage");
404
405     j = mp->hghmsg;
406     mp->hghmsg += msgp;
407     mp->nummsg += msgp;
408     if (mp->hghsel > msgnum)
409         mp->hghsel += msgp;
410
411     if (inplace)
412         for (i = mp->hghmsg; j > msgnum; i--, j--) {
413             if (verbosw)
414                 printf ("message %d becomes message %d\n", j, i);
415
416             Msgs[i].m_bboard_id = Msgs[j].m_bboard_id;
417             Msgs[i].m_top = Msgs[j].m_top;
418             Msgs[i].m_start = Msgs[j].m_start;
419             Msgs[i].m_stop = Msgs[j].m_stop;
420             Msgs[i].m_scanl = NULL;
421             if (Msgs[j].m_scanl) {
422                 free (Msgs[j].m_scanl);
423                 Msgs[j].m_scanl = NULL;
424             }
425             copy_msg_flags (mp, i, j);
426         }
427
428     if (Msgs[msgnum].m_bboard_id == 0)
429         readid (msgnum);
430
431     unset_selected (mp, msgnum);
432     i = inplace ? msgnum + msgp : mp->hghmsg;
433     for (j = msgp; j >= (inplace ? 0 : 1); i--, j--) {
434         if (verbosw && i != msgnum)
435             printf ("message %d of digest %d becomes message %d\n",
436                     j, msgnum, i);
437
438         Msgs[i].m_bboard_id = Msgs[msgnum].m_bboard_id;
439         Msgs[i].m_top = Msgs[j].m_top;
440         Msgs[i].m_start = smsgs[j].m_start;
441         Msgs[i].m_stop = smsgs[j].m_stop;
442         Msgs[i].m_scanl = NULL;
443         copy_msg_flags (mp, i, msgnum);
444     }
445
446     return OK;
447 }
448
449
450 static struct swit fileswit[] = {
451 #define FIDRFT               0
452     { "draft", 0 },
453 #define FILINK               1
454     { "link", 0 },
455 #define FINLINK              2
456     { "nolink", 0 },
457 #define FIPRES               3
458     { "preserve", 0 },
459 #define FINPRES              4
460     { "nopreserve", 0 },
461 #define FISRC                5
462     { "src +folder", 0 },
463 #define FIFILE               6
464     { "file file", 0 },
465 #define FIPROC               7
466     { "rmmproc program", 0 },
467 #define FINPRC               8
468     { "normmproc", 0 },
469 #define FIHELP               9
470     { "help", 4 },
471     { NULL, 0 }
472 };
473
474
475 void
476 filecmd (char **args)
477 {
478     int linksw = 0, msgp = 0;
479     int vecp = 1, i, msgnum;
480     char *cp, buf[BUFSIZ];
481     char *msgs[MAXARGS], *vec[MAXARGS];
482
483     if (fmsh) {
484         forkcmd (args, cmd_name);
485         return;
486     }
487
488     while ((cp = *args++)) {
489         if (*cp == '-')
490             switch (i = smatch (++cp, fileswit)) {
491                 case AMBIGSW: 
492                     ambigsw (cp, fileswit);
493                     return;
494                 case UNKWNSW: 
495                     fprintf (stderr, "-%s unknown\n", cp);
496                     return;
497                 case FIHELP: 
498                     snprintf (buf, sizeof(buf), "%s +folder... [msgs] [switches]", cmd_name);
499                     print_help (buf, fileswit, 1);
500                     return;
501
502                 case FILINK:
503                     linksw++;
504                     continue;
505                 case FINLINK: 
506                     linksw = 0;
507                     continue;
508
509                 case FIPRES: 
510                 case FINPRES: 
511                     continue;
512
513                 case FISRC: 
514                 case FIDRFT:
515                 case FIFILE: 
516                 case FIPROC:
517                 case FINPRC:
518                     advise (NULL, "sorry, -%s not allowed!", fileswit[i].sw);
519                     return;
520             }
521         if (*cp == '+' || *cp == '@')
522             vec[vecp++] = cp;
523         else
524             msgs[msgp++] = cp;
525     }
526
527     vec[0] = cmd_name;
528     vec[vecp++] = "-file";
529     vec[vecp] = NULL;
530     if (!msgp)
531         msgs[msgp++] = "cur";
532     for (msgnum = 0; msgnum < msgp; msgnum++)
533         if (!m_convert (mp, msgs[msgnum]))
534             return;
535     seq_setprev (mp);
536
537     interrupted = 0;
538     for (msgnum = mp->lowsel;
539             msgnum <= mp->hghsel && !interrupted;
540             msgnum++)
541         if (is_selected (mp, msgnum))
542             if (process (msgnum, fileproc, vecp, vec)) {
543                 unset_selected (mp, msgnum);
544                 mp->numsel--;
545             }
546
547     if (mp->numsel != mp->nummsg || linksw)
548         seq_setcur (mp, mp->hghsel);
549     if (!linksw)
550         rmm ();
551 }
552
553
554 int
555 filehak (char **args)
556 {
557     int result, vecp = 0;
558     char *cp, *cwd, *vec[MAXARGS];
559
560     while ((cp = *args++)) {
561         if (*cp == '-')
562             switch (smatch (++cp, fileswit)) {
563                 case AMBIGSW: 
564                 case UNKWNSW: 
565                 case FIHELP: 
566                     return NOTOK;
567
568                 case FILINK:
569                 case FINLINK: 
570                 case FIPRES: 
571                 case FINPRES: 
572                     continue;
573
574                 case FISRC: 
575                 case FIDRFT:
576                 case FIFILE: 
577                     return NOTOK;
578             }
579         if (*cp == '+' || *cp == '@')
580             vec[vecp++] = cp;
581     }
582     vec[vecp] = NULL;
583
584     result = NOTOK;
585     cwd = NULL;
586     for (vecp = 0; (cp = vec[vecp]) && result == NOTOK; vecp++) {
587         if (cwd == NULL)
588             cwd = getcpy (pwd ());
589         chdir (m_maildir (""));
590         cp = path (cp + 1, *cp == '+' ? TFOLDER : TSUBCWF);
591         if (access (m_maildir (cp), F_OK) == NOTOK)
592             result = OK;
593         free (cp);
594     }
595     if (cwd)
596         chdir (cwd);
597
598     return result;
599 }
600
601
602 static struct swit foldswit[] = {
603 #define FLALSW         0
604     { "all", 0 },
605 #define FLFASW         1
606     { "fast", 0 },
607 #define FLNFASW        2
608     { "nofast", 0 },
609 #define FLHDSW         3
610     { "header", 0 },
611 #define FLNHDSW        4
612     { "noheader", 0 },
613 #define FLPKSW         5
614     { "pack", 0 },
615 #define FLNPKSW        6
616     { "nopack", 0 },
617 #define FLRCSW         7
618     { "recurse", 0 },
619 #define FLNRCSW        8
620     { "norecurse", 0 },
621 #define FLTLSW         9
622     { "total", 0 },
623 #define FLNTLSW       10
624     { "nototal", 0 },
625 #define FLPRSW        11
626     { "print", 0 },
627 #define FLPUSW        12
628     { "push", 0 },
629 #define FLPOSW        13
630     { "pop", 0 },
631 #define FLLISW        14
632     { "list", 0 },
633 #define FLHELP        15
634     { "help", 4 },
635     { NULL, 0 }
636 };
637
638
639 void
640 foldcmd (char **args)
641 {
642     int fastsw = 0, headersw = 0, packsw = 0;
643     int hole, msgnum;
644     char *cp, *folder = NULL, *msg = NULL;
645     char buf[BUFSIZ], **vec = args;
646
647     if (args == NULL)
648         goto fast;
649
650     while ((cp = *args++)) {
651         if (*cp == '-')
652             switch (smatch (++cp, foldswit)) {
653                 case AMBIGSW: 
654                     ambigsw (cp, foldswit);
655                     return;
656                 case UNKWNSW: 
657                     fprintf (stderr, "-%s unknown\n", cp);
658                     return;
659                 case FLHELP: 
660                     snprintf (buf, sizeof(buf), "%s [+folder] [msg] [switches]", cmd_name);
661                     print_help (buf, foldswit, 1);
662                     return;
663
664                 case FLALSW:    /* not implemented */
665                 case FLRCSW: 
666                 case FLNRCSW: 
667                 case FLTLSW: 
668                 case FLNTLSW: 
669                 case FLPRSW:
670                 case FLPUSW:
671                 case FLPOSW:
672                 case FLLISW:
673                     continue;
674
675                 case FLFASW: 
676                     fastsw++;
677                     continue;
678                 case FLNFASW: 
679                     fastsw = 0;
680                     continue;
681                 case FLHDSW: 
682                     headersw++;
683                     continue;
684                 case FLNHDSW: 
685                     headersw = 0;
686                     continue;
687                 case FLPKSW: 
688                     packsw++;
689                     continue;
690                 case FLNPKSW: 
691                     packsw = 0;
692                     continue;
693             }
694         if (*cp == '+' || *cp == '@')
695             if (folder) {
696                 advise (NULL, "only one folder at a time!\n");
697                 return;
698             }
699             else
700                 folder = fmsh ? path (cp + 1, *cp == '+' ? TFOLDER : TSUBCWF)
701                             : cp + 1;
702         else
703             if (msg) {
704                 advise (NULL, "only one message at a time!\n");
705                 return;
706             }
707             else
708                 msg = cp;
709     }
710
711     if (folder) {
712         if (*folder == 0) {
713             advise (NULL, "null folder names are not permitted");
714             return;
715         }
716         if (fmsh) {
717             if (access (m_maildir (folder), R_OK) == NOTOK) {
718                 advise (folder, "unable to read");
719                 return;
720             }
721         }
722         else {
723             strncpy (buf, folder, sizeof(buf));
724             if (expand (buf) == NOTOK)
725                 return;
726             folder = buf;
727             if (access (folder, R_OK) == NOTOK) {
728                 advise (folder, "unable to read");
729                 return;
730             }
731         }
732         m_reset ();
733
734         if (fmsh)
735             fsetup (folder);
736         else
737             setup (folder);
738         readids (0);
739         display_info (0);
740     }
741
742     if (msg) {
743         if (!m_convert (mp, msg))
744             return;
745         seq_setprev (mp);
746
747         if (mp->numsel > 1) {
748             advise (NULL, "only one message at a time!");
749             return;
750         }
751         seq_setcur (mp, mp->hghsel);
752     }
753
754     if (packsw) {
755         if (fmsh) {
756             forkcmd (vec, cmd_name);
757             return;
758         }
759
760         if (mp->lowoff > 1 && !(mp = folder_realloc (mp, 1, mp->hghmsg)))
761             adios (NULL, "unable to allocate folder storage");
762
763         for (msgnum = mp->lowmsg, hole = 1; msgnum <= mp->hghmsg; msgnum++)
764             if (does_exist (mp, msgnum)) {
765                 if (msgnum != hole) {
766                     Msgs[hole].m_bboard_id = Msgs[msgnum].m_bboard_id;
767                     Msgs[hole].m_top = Msgs[msgnum].m_top;
768                     Msgs[hole].m_start = Msgs[msgnum].m_start;
769                     Msgs[hole].m_stop = Msgs[msgnum].m_stop;
770                     Msgs[hole].m_scanl = NULL;
771                     if (Msgs[msgnum].m_scanl) {
772                         free (Msgs[msgnum].m_scanl);
773                         Msgs[msgnum].m_scanl = NULL;
774                     }
775                     copy_msg_flags (mp, hole, msgnum);
776                     if (mp->curmsg == msgnum)
777                         seq_setcur (mp, hole);
778                 }
779                 hole++;
780             }
781         if (mp->nummsg > 0) {
782             mp->lowmsg = 1;
783             mp->hghmsg = hole - 1;
784         }
785         mp->msgflags |= MODIFIED;
786         modified++;
787     }
788
789 fast: ;
790     if (fastsw)
791         printf ("%s\n", fmsh ? fmsh : mp->foldpath);
792     else {
793         if (headersw)
794             printf ("\t\tFolder  %*s# of messages (%*srange%*s); cur%*smsg\n",
795                 DMAXFOLDER, "", DMAXFOLDER - 2, "", DMAXFOLDER - 2, "",
796                 DMAXFOLDER - 2, "");
797         printf (args ? "%22s  " : "%s ", fmsh ? fmsh : mp->foldpath);
798
799         /* check for empty folder */
800         if (mp->nummsg == 0) {
801             printf ("has   no messages%*s",
802                     mp->msgflags & OTHERS ? DMAXFOLDER * 2 + 4 : 0, "");
803         } else {
804             printf ("has %*d message%s (%*d-%*d)",
805                     DMAXFOLDER, mp->nummsg, mp->nummsg != 1 ? "s" : "",
806                     DMAXFOLDER, mp->lowmsg, DMAXFOLDER, mp->hghmsg);
807             if (mp->curmsg >= mp->lowmsg
808                     && mp->curmsg <= mp->hghmsg)
809                 printf ("; cur=%*d", DMAXFOLDER, mp->curmsg);
810         }
811         printf (".\n");
812     }
813 }
814
815
816 static struct swit forwswit[] = {
817 #define FOANSW                   0
818     { "annotate", 0 },
819 #define FONANSW                  1
820     { "noannotate", 0 },
821 #define FODFSW                   2
822     { "draftfolder +folder", 0 },
823 #define FODMSW                   3
824     { "draftmessage msg", 0 },
825 #define FONDFSW                  4
826     { "nodraftfolder", 0 },
827 #define FOEDTSW                  5
828     { "editor editor", 0 },
829 #define FONEDSW                  6
830     { "noedit", 0 },
831 #define FOFTRSW                  7
832     { "filter filterfile", 0 },
833 #define FOFRMSW                  8
834     { "form formfile", 0 },
835 #define FOFTSW                   9
836     { "format", 5 },
837 #define FONFTSW                 10
838     { "noformat", 7 },
839 #define FOINSW                  11
840     { "inplace", 0 },
841 #define FONINSW                 12
842     { "noinplace", 0 },
843 #define FOMISW                  13
844     { "mime", 0 },
845 #define FONMISW                 14
846     { "nomime", 0 },
847 #define FOWHTSW                 15
848     { "whatnowproc program", 0 },
849 #define FONWTSW                 16
850     { "nowhatnow", 0 },
851 #define FOHELP                  17
852     { "help", 4 },
853     { NULL, 0 }
854 };
855
856
857 void
858 forwcmd (char **args)
859 {
860     int msgp = 0, vecp = 1, msgnum;
861     char *cp, *filter = NULL, buf[BUFSIZ];
862     char *msgs[MAXARGS], *vec[MAXARGS];
863
864     if (fmsh) {
865         forkcmd (args, cmd_name);
866         return;
867     }
868
869     while ((cp = *args++)) {
870         if (*cp == '-')
871             switch (smatch (++cp, forwswit)) {
872                 case AMBIGSW: 
873                     ambigsw (cp, forwswit);
874                     return;
875                 case UNKWNSW: 
876                     fprintf (stderr, "-%s unknown\n", cp);
877                     return;
878                 case FOHELP: 
879                     snprintf (buf, sizeof(buf), "%s [msgs] [switches]", cmd_name);
880                     print_help (buf, forwswit, 1);
881                     return;
882
883                 case FOANSW:    /* not implemented */
884                 case FONANSW: 
885                 case FOINSW: 
886                 case FONINSW: 
887                 case FOMISW: 
888                 case FONMISW: 
889                     continue;
890
891                 case FONDFSW:
892                 case FONEDSW:
893                 case FONWTSW:
894                     vec[vecp++] = --cp;
895                     continue;
896
897                 case FOEDTSW: 
898                 case FOFRMSW: 
899                 case FODFSW:
900                 case FODMSW:
901                 case FOWHTSW:
902                     vec[vecp++] = --cp;
903                     if (!(cp = *args++) || *cp == '-') {
904                         advise (NULL, "missing argument to %s", args[-2]);
905                         return;
906                     }
907                     vec[vecp++] = cp;
908                     continue;
909                 case FOFTRSW: 
910                     if (!(filter = *args++) || *filter == '-') {
911                         advise (NULL, "missing argument to %s", args[-2]);
912                         return;
913                     }
914                     continue;
915                 case FOFTSW: 
916                     if (access (filter = myfilter, R_OK) == NOTOK) {
917                         advise (filter, "unable to read default filter file");
918                         return;
919                     }
920                     continue;
921                 case FONFTSW: 
922                     filter = NULL;
923                     continue;
924             }
925         if (*cp == '+' || *cp == '@') {
926             advise (NULL, "sorry, no folders allowed!");
927             return;
928         }
929         else
930             msgs[msgp++] = cp;
931     }
932
933                                         /* foil search of .mh_profile */
934     snprintf (buf, sizeof(buf), "%sXXXXXX", invo_name);
935 #ifdef HAVE_MKSTEMP
936     vec[0] = (char *)mkstemp (buf);
937 #else
938     vec[0] = (char *)mktemp (buf);
939 #endif
940     vec[vecp++] = "-file";
941     vec[vecp] = NULL;
942     if (!msgp)
943         msgs[msgp++] = "cur";
944     for (msgnum = 0; msgnum < msgp; msgnum++)
945         if (!m_convert (mp, msgs[msgnum]))
946             return;
947     seq_setprev (mp);
948
949     if (filter) {
950         strncpy (buf, filter, sizeof(buf));
951         if (expand (buf) == NOTOK)
952             return;
953         if (access (filter = getcpy (etcpath (buf)), R_OK) == NOTOK) {
954             advise (filter, "unable to read");
955             free (filter);
956             return;
957         }
958     }
959     forw (cmd_name, filter, vecp, vec);
960     seq_setcur (mp, mp->hghsel);
961     if (filter)
962         free (filter);
963 }
964
965
966 static void
967 forw (char *proc, char *filter, int vecp, char **vec)
968 {
969     int i, child_id, msgnum, msgcnt;
970     char tmpfil[80], *args[MAXARGS];
971     FILE *out;
972
973     strncpy (tmpfil, m_tmpfil (invo_name), sizeof(tmpfil));
974     interrupted = 0;
975     if (filter)
976         switch (child_id = fork ()) {
977             case NOTOK: 
978                 advise ("fork", "unable to");
979                 return;
980
981             case OK:            /* "trust me" */
982                 if (freopen (tmpfil, "w", stdout) == NULL) {
983                     fprintf (stderr, "unable to create ");
984                     perror (tmpfil);
985                     _exit (1);
986                 }
987                 args[0] = r1bindex (mhlproc, '/');
988                 i = 1;
989                 args[i++] = "-forwall";
990                 args[i++] = "-form";
991                 args[i++] = filter;
992                 for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
993                     if (is_selected (mp, msgnum))
994                         args[i++] = getcpy (m_name (msgnum));
995                 args[i] = NULL;
996                 mhlsbr (i, args, mhl_action);
997                 m_eomsbr ((int (*) ()) 0);
998                 fclose (stdout);
999                 _exit (0);
1000
1001             default: 
1002                 if (pidXwait (child_id, NULL))
1003                     interrupted++;
1004                 break;
1005         }
1006     else {
1007         if ((out = fopen (tmpfil, "w")) == NULL) {
1008             advise (tmpfil, "unable to create temporary file");
1009             return;
1010         }
1011
1012         msgcnt = 1;
1013         for (msgnum = mp->lowsel;
1014                 msgnum <= mp->hghsel && !interrupted;
1015                 msgnum++)
1016             if (is_selected (mp, msgnum)) {
1017                 fprintf (out, "\n\n-------");
1018                 if (msgnum == mp->lowsel)
1019                     fprintf (out, " Forwarded Message%s",
1020                             mp->numsel > 1 ? "s" : "");
1021                 else
1022                     fprintf (out, " Message %d", msgcnt);
1023                 fprintf (out, "\n\n");
1024                 copy_digest (msgnum, out);
1025                 msgcnt++;
1026             }
1027
1028         fprintf (out, "\n\n------- End of Forwarded Message%s\n",
1029                 mp->numsel > 1 ? "s" : "");
1030         fclose (out);
1031     }
1032
1033     fflush (stdout);
1034     if (!interrupted)
1035         switch (child_id = fork ()) {
1036             case NOTOK: 
1037                 advise ("fork", "unable to");
1038                 break;
1039
1040             case OK: 
1041                 closefds (3);
1042                 SIGNAL (SIGINT, istat);
1043                 SIGNAL (SIGQUIT, qstat);
1044
1045                 vec[vecp++] = tmpfil;
1046                 vec[vecp] = NULL;
1047
1048                 execvp (proc, vec);
1049                 fprintf (stderr, "unable to exec ");
1050                 perror (proc);
1051                 _exit (1);
1052
1053             default: 
1054                 pidXwait (child_id, NULL);
1055                 break;
1056         }
1057
1058     unlink (tmpfil);
1059 }
1060
1061
1062 static char *hlpmsg[] = {
1063     "The %s program emulates many of the commands found in the nmh",
1064     "system.  Instead of operating on nmh folders, commands to %s concern",
1065     "a single file.",
1066     "",
1067     "To see the list of commands available, just type a ``?'' followed by",
1068     "the RETURN key.  To find out what switches each command takes, type",
1069     "the name of the command followed by ``-help''.  To leave %s, use the",
1070     "``quit'' command.",
1071     "",
1072     "Although a lot of nmh commands are found in %s, not all are fully",
1073     "implemented.  %s will always recognize all legal switches for a",
1074     "given command though, and will let you know when you ask for an",
1075     "option that it is unable to perform.",
1076     "",
1077     "Running %s is fun, but using nmh from your shell is far superior.",
1078     "After you have familiarized yourself with the nmh style by using %s,",
1079     "you should try using nmh from the shell.  You can still use %s for",
1080     "message files that aren't in nmh format, such as BBoard files.",
1081     NULL
1082 };
1083
1084
1085 void
1086 helpcmd (char **args)
1087 {
1088     int i;
1089
1090     for (i = 0; hlpmsg[i]; i++) {
1091         printf (hlpmsg[i], invo_name);
1092         putchar ('\n');
1093     }
1094 }
1095
1096
1097 static struct swit markswit[] = {
1098 #define MADDSW             0
1099     { "add", 0 },
1100 #define MDELSW             1
1101     { "delete", 0 },
1102 #define MLSTSW             2
1103     { "list", 0 },
1104 #define MSEQSW             3
1105     { "sequence name", 0 },
1106 #define MPUBSW             4
1107     { "public", 0 },
1108 #define MNPUBSW            5
1109     { "nopublic", 0 },
1110 #define MZERSW             6
1111     { "zero", 0 },
1112 #define MNZERSW            7
1113     { "nozero", 0 },
1114 #define MHELP              8
1115     { "help", 4 },
1116 #define MDBUGSW            9
1117     { "debug", -5 },
1118     { NULL, 0 }
1119 };
1120
1121
1122 void
1123 markcmd (char **args)
1124 {
1125     int addsw = 0, deletesw = 0, debugsw = 0;
1126     int listsw = 0, zerosw = 0, seqp = 0;
1127     int msgp = 0, msgnum;
1128     char *cp, buf[BUFSIZ];
1129     char *seqs[NUMATTRS + 1], *msgs[MAXARGS];
1130
1131     while ((cp = *args++)) {
1132         if (*cp == '-') {
1133             switch (smatch (++cp, markswit)) {
1134                 case AMBIGSW: 
1135                     ambigsw (cp, markswit);
1136                     return;
1137                 case UNKWNSW: 
1138                     fprintf (stderr, "-%s unknown\n", cp);
1139                     return;
1140                 case MHELP: 
1141                     snprintf (buf, sizeof(buf), "%s [msgs] [switches]", cmd_name);
1142                     print_help (buf, markswit, 1);
1143                     return;
1144
1145                 case MADDSW: 
1146                     addsw++;
1147                     deletesw = listsw = 0;
1148                     continue;
1149                 case MDELSW: 
1150                     deletesw++;
1151                     addsw = listsw = 0;
1152                     continue;
1153                 case MLSTSW: 
1154                     listsw++;
1155                     addsw = deletesw = 0;
1156                     continue;
1157
1158                 case MSEQSW: 
1159                     if (!(cp = *args++) || *cp == '-') {
1160                         advise (NULL, "missing argument to %s", args[-2]);
1161                         return;
1162                     }
1163                     if (seqp < NUMATTRS)
1164                         seqs[seqp++] = cp;
1165                     else {
1166                         advise (NULL, "only %d sequences allowed!", NUMATTRS);
1167                         return;
1168                     }
1169                     continue;
1170
1171                 case MPUBSW:    /* not implemented */
1172                 case MNPUBSW: 
1173                     continue;
1174
1175                 case MDBUGSW: 
1176                     debugsw++;
1177                     continue;
1178
1179                 case MZERSW: 
1180                     zerosw++;
1181                     continue;
1182                 case MNZERSW: 
1183                     zerosw = 0;
1184                     continue;
1185             }
1186         }
1187         if (*cp == '+' || *cp == '@') {
1188             advise (NULL, "sorry, no folders allowed!");
1189             return;
1190         } else {
1191             msgs[msgp++] = cp;
1192         }
1193     }
1194
1195     if (!addsw && !deletesw && !listsw)
1196         if (seqp)
1197             addsw++;
1198         else
1199             if (debugsw)
1200                 listsw++;
1201             else {
1202                 seqs[seqp++] = "unseen";
1203                 deletesw++;
1204                 zerosw = 0;
1205                 if (!msgp)
1206                     msgs[msgp++] = "all";
1207             }
1208
1209     if (!msgp)
1210         msgs[msgp++] = listsw ? "all" :"cur";
1211     for (msgnum = 0; msgnum < msgp; msgnum++)
1212         if (!m_convert (mp, msgs[msgnum]))
1213             return;
1214
1215     if (debugsw) {
1216         printf ("invo_name=%s mypath=%s defpath=%s\n",
1217                 invo_name, mypath, defpath);
1218         printf ("ctxpath=%s context flags=%s\n",
1219                 ctxpath, snprintb (buf, sizeof(buf), (unsigned) ctxflags, DBITS));
1220         printf ("foldpath=%s flags=%s\n",
1221                 mp->foldpath,
1222                 snprintb (buf, sizeof(buf), (unsigned) mp->msgflags, FBITS));
1223         printf ("hghmsg=%d lowmsg=%d nummsg=%d curmsg=%d\n",
1224                 mp->hghmsg, mp->lowmsg, mp->nummsg, mp->curmsg);
1225         printf ("lowsel=%d hghsel=%d numsel=%d\n",
1226                 mp->lowsel, mp->hghsel, mp->numsel);
1227         printf ("lowoff=%d hghoff=%d\n", mp->lowoff, mp->hghoff);
1228     }
1229
1230     if (seqp == 0 && (addsw || deletesw)) {
1231         advise (NULL, "-%s requires at least one -sequence argument",
1232                 addsw ? "add" : "delete");
1233         return;
1234     }
1235     seqs[seqp] = NULL;
1236
1237     if (addsw) {
1238         for (seqp = 0; seqs[seqp]; seqp++)
1239             if (!seq_addsel (mp, seqs[seqp], 0, zerosw))
1240                 return;
1241     }
1242
1243     if (deletesw) {
1244         for (seqp = 0; seqs[seqp]; seqp++)
1245             if (!seq_delsel (mp, seqs[seqp], 0, zerosw))
1246                 return;
1247     }
1248
1249     /* Listing messages in sequences */
1250     if (listsw) {
1251         if (seqp) {
1252             /* list the given sequences */
1253             for (seqp = 0; seqs[seqp]; seqp++)
1254                 seq_print (mp, seqs[seqp]);
1255         } else {
1256             /* else list them all */
1257             seq_printall (mp);
1258         }
1259
1260         interrupted = 0;
1261         if (debugsw)
1262             for (msgnum = mp->lowsel;
1263                     msgnum <= mp->hghsel && !interrupted;
1264                     msgnum++)
1265                 if (is_selected (mp, msgnum)) {
1266                     printf ("%*d: id=%d top=%d start=%ld stop=%ld %s\n",
1267                         DMAXFOLDER,
1268                         msgnum,
1269                         Msgs[msgnum].m_bboard_id,
1270                         Msgs[msgnum].m_top,
1271                         (long) Msgs[msgnum].m_start,
1272                         (long) Msgs[msgnum].m_stop,
1273                         snprintb (buf, sizeof(buf),
1274                                 (unsigned) mp->msgstats[msgnum - mp->lowoff],
1275                                 seq_bits (mp)));
1276                     if (Msgs[msgnum].m_scanl)
1277                         printf ("%s", Msgs[msgnum].m_scanl);
1278                 }                           
1279     }
1280 }
1281
1282
1283 static struct swit mhnswit[] = {
1284 #define MHNAUTOSW           0
1285     { "auto", 0 },
1286 #define MHNNAUTOSW          1
1287     { "noauto", 0 },
1288 #define MHNDEBUGSW          2
1289     { "debug", -5 },
1290 #define MHNEBCDICSW         3
1291     { "ebcdicsafe", 0 },
1292 #define MHNNEBCDICSW        4
1293     { "noebcdicsafe", 0 },
1294 #define MHNFORMSW           5
1295     { "form formfile", 4 },
1296 #define MHNHEADSW           6
1297     { "headers", 0 },
1298 #define MHNNHEADSW          7
1299     { "noheaders", 0 },
1300 #define MHNLISTSW           8
1301     { "list", 0 },
1302 #define MHNNLISTSW          9
1303     { "nolist", 0 },
1304 #define MHNPARTSW          10
1305     { "part number", 0 },
1306 #define MHNSIZESW          11
1307     { "realsize", 0 },
1308 #define MHNNSIZESW         12
1309     { "norealsize", 0 },
1310 #define MHNRFC934SW        13
1311     { "rfc934mode", 0 },
1312 #define MHNNRFC934SW       14
1313     { "norfc934mode", 0 },
1314 #define MHNSERIALSW        15
1315     { "serialonly", 0 },
1316 #define MHNNSERIALSW       16
1317     { "noserialonly", 0 },
1318 #define MHNSHOWSW          17
1319     { "show", 0 },
1320 #define MHNNSHOWSW         18
1321     { "noshow", 0 },
1322 #define MHNSTORESW         19
1323     { "store", 0 },
1324 #define MHNNSTORESW        20
1325     { "nostore", 0 },
1326 #define MHNTYPESW          21
1327     { "type content", 0 },
1328 #define MHNVERBSW          22
1329     { "verbose", 0 },
1330 #define MHNNVERBSW         23
1331     { "noverbose", 0 },
1332 #define MHNHELPSW          24
1333     { "help", 4 },
1334 #define MHNPROGSW          25
1335     { "moreproc program", -4 },
1336 #define MHNNPROGSW         26
1337     { "nomoreproc", -3 },
1338 #define MHNLENSW           27
1339     { "length lines", -4 },
1340 #define MHNWIDSW           28
1341     { "width columns", -4 },
1342     { NULL, 0 }
1343 };
1344
1345
1346 void
1347 mhncmd (char **args)
1348 {
1349     int msgp = 0, vecp = 1;
1350     int msgnum;
1351     char *cp, buf[BUFSIZ];
1352     char *msgs[MAXARGS], *vec[MAXARGS];
1353
1354     if (fmsh) {
1355         forkcmd (args, cmd_name);
1356         return;
1357     }
1358     while ((cp = *args++)) {
1359         if (*cp == '-') {
1360             switch (smatch (++cp, mhnswit)) {
1361                 case AMBIGSW: 
1362                     ambigsw (cp, mhnswit);
1363                     return;
1364                 case UNKWNSW: 
1365                     fprintf (stderr, "-%s unknown\n", cp);
1366                     return;
1367                 case MHNHELPSW:
1368                     snprintf (buf, sizeof(buf), "%s [msgs] [switches]", cmd_name);
1369                     print_help (buf, mhnswit, 1);
1370                     return;
1371
1372                 case MHNAUTOSW:
1373                 case MHNNAUTOSW:
1374                 case MHNDEBUGSW:
1375                 case MHNEBCDICSW:
1376                 case MHNNEBCDICSW:
1377                 case MHNHEADSW:
1378                 case MHNNHEADSW:
1379                 case MHNLISTSW:
1380                 case MHNNLISTSW:
1381                 case MHNSIZESW:
1382                 case MHNNSIZESW:
1383                 case MHNRFC934SW:
1384                 case MHNNRFC934SW:
1385                 case MHNSERIALSW:
1386                 case MHNNSERIALSW:
1387                 case MHNSHOWSW:
1388                 case MHNNSHOWSW:
1389                 case MHNSTORESW:
1390                 case MHNNSTORESW:
1391                 case MHNVERBSW:
1392                 case MHNNVERBSW:
1393                 case MHNNPROGSW:
1394                     vec[vecp++] = --cp;
1395                     continue;
1396
1397                 case MHNFORMSW:
1398                 case MHNPARTSW:
1399                 case MHNTYPESW:
1400                 case MHNPROGSW:
1401                 case MHNLENSW:
1402                 case MHNWIDSW:
1403                     vec[vecp++] = --cp;
1404                     if (!(cp = *args++) || *cp == '-') {
1405                         advise (NULL, "missing argument to %s", args[-2]);
1406                         return;
1407                     }
1408                     vec[vecp++] = cp;
1409                     continue;
1410             }
1411         }
1412         if (*cp == '+' || *cp == '@') {
1413             advise (NULL, "sorry, no folders allowed!");
1414             return;
1415         } else {
1416             msgs[msgp++] = cp;
1417         }
1418     }
1419
1420     vec[0] = cmd_name;
1421     vec[vecp++] = "-file";
1422     vec[vecp] = NULL;
1423     if (!msgp)
1424         msgs[msgp++] = "cur";
1425     for (msgnum = 0; msgnum < msgp; msgnum++)
1426         if (!m_convert (mp, msgs[msgnum]))
1427             return;
1428     seq_setprev (mp);
1429
1430     interrupted = 0;
1431     for (msgnum = mp->lowsel;
1432             msgnum <= mp->hghsel && !interrupted;
1433             msgnum++)
1434         if (is_selected (mp, msgnum))
1435             if (process (msgnum, cmd_name, vecp, vec)) {
1436                 unset_selected (mp, msgnum);
1437                 mp->numsel--;
1438             }
1439
1440     seq_setcur (mp, mp->hghsel);
1441 }
1442
1443
1444 static struct swit packswit[] = {
1445 #define PAFISW         0
1446     { "file name", 0 },
1447 #define PAHELP         1
1448     { "help", 4 },
1449     { NULL, 0 }
1450 };
1451
1452 static mbx_style = MMDF_FORMAT;
1453
1454 void
1455 packcmd (char **args)
1456 {
1457     int msgp = 0, md, msgnum;
1458     char *cp, *file = NULL;
1459     char buf[BUFSIZ], *msgs[MAXARGS];
1460     struct stat st;
1461
1462     if (fmsh) {
1463         forkcmd (args, cmd_name);
1464         return;
1465     }
1466
1467     while ((cp = *args++)) {
1468         if (*cp == '-')
1469             switch (smatch (++cp, packswit)) {
1470                 case AMBIGSW: 
1471                     ambigsw (cp, packswit);
1472                     return;
1473                 case UNKWNSW: 
1474                     fprintf (stderr, "-%s unknown\n", cp);
1475                     return;
1476                 case PAHELP: 
1477                     snprintf (buf, sizeof(buf), "%s [msgs] [switches]", cmd_name);
1478                     print_help (buf, packswit, 1);
1479                     return;
1480
1481                 case PAFISW: 
1482                     if (!(file = *args++) || *file == '-') {
1483                         advise (NULL, "missing argument to %s", args[-2]);
1484                         return;
1485                     }
1486                     continue;
1487             }
1488         if (*cp == '+' || *cp == '@') {
1489             advise (NULL, "sorry, no folders allowed!");
1490             return;
1491         }
1492         else
1493             msgs[msgp++] = cp;
1494     }
1495
1496     if (!file)
1497         file = "./msgbox";
1498     file = path (file, TFILE);
1499     if (stat (file, &st) == NOTOK) {
1500         if (errno != ENOENT) {
1501             advise (file, "error on file");
1502             goto done_pack;
1503         }
1504         md = getanswer (cp = concat ("Create file \"", file, "\"? ", NULL));
1505         free (cp);
1506         if (!md)
1507             goto done_pack;
1508     }
1509
1510     if (!msgp)
1511         msgs[msgp++] = "all";
1512     for (msgnum = 0; msgnum < msgp; msgnum++)
1513         if (!m_convert (mp, msgs[msgnum]))
1514             goto done_pack;
1515     seq_setprev (mp);
1516
1517     if ((md = mbx_open (file, mbx_style, getuid (), getgid (), m_gmprot ())) == NOTOK) {
1518         advise (file, "unable to open");
1519         goto done_pack;
1520     }
1521     for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
1522         if (is_selected (mp, msgnum))
1523             if (pack (file, md, msgnum) == NOTOK)
1524                 break;
1525     mbx_close (file, md);
1526
1527     if (mp->hghsel != mp->curmsg)
1528         seq_setcur (mp, mp->lowsel);
1529
1530 done_pack: ;
1531     free (file);
1532 }
1533
1534
1535 int
1536 pack (char *mailbox, int md, int msgnum)
1537 {
1538     register FILE *zp;
1539
1540     if (Msgs[msgnum].m_bboard_id == 0)
1541         readid (msgnum);
1542
1543     zp = msh_ready (msgnum, 1);
1544     return mbx_write (mailbox, md, zp, Msgs[msgnum].m_bboard_id,
1545             0L, ftell (zp), Msgs[msgnum].m_stop, 1, 1);
1546 }
1547
1548
1549 int
1550 packhak (char **args)
1551 {
1552     int result;
1553     char *cp, *file = NULL;
1554
1555     while ((cp = *args++)) {
1556         if (*cp == '-')
1557             switch (smatch (++cp, packswit)) {
1558                 case AMBIGSW: 
1559                 case UNKWNSW: 
1560                 case PAHELP: 
1561                     return NOTOK;
1562
1563                 case PAFISW: 
1564                     if (!(file = *args++) || *file == '-') 
1565                         return NOTOK;
1566                     continue;
1567             }
1568         if (*cp == '+' || *cp == '@')
1569             return NOTOK;
1570     }
1571
1572     file = path (file ? file : "./msgbox", TFILE);
1573     result = access (file, F_OK) == NOTOK ? OK : NOTOK;
1574     free (file);
1575
1576     return result;
1577 }
1578
1579
1580 static struct swit pickswit[] = {
1581 #define PIANSW                0
1582     { "and", 0 },
1583 #define PIORSW                1
1584     { "or", 0 },
1585 #define PINTSW                2
1586     { "not", 0 },
1587 #define PILBSW                3
1588     { "lbrace", 0 },
1589 #define PIRBSW                4
1590     { "rbrace", 0 },
1591 #define PICCSW                5
1592     { "cc  pattern", 0 },
1593 #define PIDASW                6
1594     { "date  pattern", 0 },
1595 #define PIFRSW                7
1596     { "from  pattern", 0 },
1597 #define PISESW                8
1598     { "search  pattern", 0 },
1599 #define PISUSW                9
1600     { "subject  pattern", 0 },
1601 #define PITOSW               10
1602     { "to  pattern", 0 },
1603 #define PIOTSW               11
1604     { "-othercomponent  pattern", 15 },
1605 #define PIAFSW               12
1606     { "after date", 0 },
1607 #define PIBFSW               13
1608     { "before date", 0 },
1609 #define PIDFSW               14
1610     { "datefield field", 5 },
1611 #define PISQSW               15
1612     { "sequence name", 0 },
1613 #define PIPUSW               16
1614     { "public", 0 },
1615 #define PINPUSW              17
1616     { "nopublic", 0 },
1617 #define PIZRSW               18
1618     { "zero", 0 },
1619 #define PINZRSW              19
1620     { "nozero", 0 },
1621 #define PILISW               20
1622     { "list", 0 },
1623 #define PINLISW              21
1624     { "nolist", 0 },
1625 #define PIHELP               22
1626     { "help", 4 },
1627     { NULL, 0 }
1628 };
1629
1630
1631 void
1632 pickcmd (char **args)
1633 {
1634     int zerosw = 1, msgp = 0, seqp = 0;
1635     int vecp = 0, hi, lo, msgnum;
1636     char *cp, buf[BUFSIZ], *msgs[MAXARGS];
1637     char *seqs[NUMATTRS], *vec[MAXARGS];
1638     register FILE *zp;
1639
1640     while ((cp = *args++)) {
1641         if (*cp == '-') {
1642             if (*++cp == '-') {
1643                 vec[vecp++] = --cp;
1644                 goto pattern;
1645             }
1646             switch (smatch (cp, pickswit)) {
1647                 case AMBIGSW: 
1648                     ambigsw (cp, pickswit);
1649                     return;
1650                 case UNKWNSW: 
1651                     fprintf (stderr, "-%s unknown\n", cp);
1652                     return;
1653                 case PIHELP: 
1654                     snprintf (buf, sizeof(buf), "%s [msgs] [switches]", cmd_name);
1655                     print_help (buf, pickswit, 1);
1656                     return;
1657
1658                 case PICCSW: 
1659                 case PIDASW: 
1660                 case PIFRSW: 
1661                 case PISUSW: 
1662                 case PITOSW: 
1663                 case PIDFSW: 
1664                 case PIAFSW: 
1665                 case PIBFSW: 
1666                 case PISESW: 
1667                     vec[vecp++] = --cp;
1668 pattern: ;
1669                     if (!(cp = *args++)) {/* allow -xyz arguments */
1670                         advise (NULL, "missing argument to %s", args[-2]);
1671                         return;
1672                     }
1673                     vec[vecp++] = cp;
1674                     continue;
1675                 case PIOTSW: 
1676                     advise (NULL, "internal error!");
1677                     return;
1678                 case PIANSW: 
1679                 case PIORSW: 
1680                 case PINTSW: 
1681                 case PILBSW: 
1682                 case PIRBSW: 
1683                     vec[vecp++] = --cp;
1684                     continue;
1685
1686                 case PISQSW: 
1687                     if (!(cp = *args++) || *cp == '-') {
1688                         advise (NULL, "missing argument to %s", args[-2]);
1689                         return;
1690                     }
1691                     if (seqp < NUMATTRS)
1692                         seqs[seqp++] = cp;
1693                     else {
1694                         advise (NULL, "only %d sequences allowed!", NUMATTRS);
1695                         return;
1696                     }
1697                     continue;
1698                 case PIZRSW: 
1699                     zerosw++;
1700                     continue;
1701                 case PINZRSW: 
1702                     zerosw = 0;
1703                     continue;
1704
1705                 case PIPUSW:    /* not implemented */
1706                 case PINPUSW: 
1707                 case PILISW: 
1708                 case PINLISW: 
1709                     continue;
1710             }
1711         }
1712         if (*cp == '+' || *cp == '@') {
1713             advise (NULL, "sorry, no folders allowed!");
1714             return;
1715         }
1716         else
1717             msgs[msgp++] = cp;
1718     }
1719     vec[vecp] = NULL;
1720
1721     if (!msgp)
1722         msgs[msgp++] = "all";
1723     for (msgnum = 0; msgnum < msgp; msgnum++)
1724         if (!m_convert (mp, msgs[msgnum]))
1725             return;
1726     seq_setprev (mp);
1727
1728     interrupted = 0;
1729     if (!pcompile (vec, NULL))
1730         return;
1731
1732     lo = mp->lowsel;
1733     hi = mp->hghsel;
1734
1735     for (msgnum = mp->lowsel;
1736             msgnum <= mp->hghsel && !interrupted;
1737             msgnum++)
1738         if (is_selected (mp, msgnum)) {
1739             zp = msh_ready (msgnum, 1);
1740             if (pmatches (zp, msgnum, fmsh ? 0L : Msgs[msgnum].m_start,
1741                         fmsh ? 0L : Msgs[msgnum].m_stop)) {
1742                 if (msgnum < lo)
1743                     lo = msgnum;
1744                 if (msgnum > hi)
1745                     hi = msgnum;
1746             }
1747             else {
1748                 unset_selected (mp, msgnum);
1749                 mp->numsel--;
1750             }
1751         }
1752
1753     if (interrupted)
1754         return;
1755
1756     mp->lowsel = lo;
1757     mp->hghsel = hi;
1758
1759     if (mp->numsel <= 0) {
1760         advise (NULL, "no messages match specification");
1761         return;
1762     }
1763
1764     seqs[seqp] = NULL;
1765     for (seqp = 0; seqs[seqp]; seqp++)
1766         if (!seq_addsel (mp, seqs[seqp], 0, zerosw))
1767             return;
1768
1769     printf ("%d hit%s\n", mp->numsel, mp->numsel == 1 ? "" : "s");
1770 }
1771
1772
1773 static struct swit replswit[] = {
1774 #define REANSW                  0
1775     { "annotate", 0 },
1776 #define RENANSW                 1
1777     { "noannotate", 0 },
1778 #define RECCSW                  2
1779     { "cc type", 0 },
1780 #define RENCCSW                 3
1781     { "nocc type", 0 },
1782 #define REDFSW                  4
1783     { "draftfolder +folder", 0 },
1784 #define REDMSW                  5
1785     { "draftmessage msg", 0 },
1786 #define RENDFSW                 6
1787     { "nodraftfolder", 0 },
1788 #define REEDTSW                 7
1789     { "editor editor", 0 },
1790 #define RENEDSW                 8
1791     { "noedit", 0 },
1792 #define REFCCSW                 9
1793     { "fcc +folder", 0 },
1794 #define REFLTSW                10
1795     { "filter filterfile", 0 },
1796 #define REFRMSW                11
1797     { "form formfile", 0 },
1798 #define REINSW                 12
1799     { "inplace", 0 },
1800 #define RENINSW                13
1801     { "noinplace", 0 },
1802 #define REQUSW                 14
1803     { "query", 0 },
1804 #define RENQUSW                15
1805     { "noquery", 0 },
1806 #define REWHTSW                16
1807     { "whatnowproc program", 0 },
1808 #define RENWTSW                17
1809     { "nowhatnow", 0 },
1810 #define REWIDSW                19
1811     { "width columns", 0 },
1812 #define REHELP                 20
1813     { "help", 4 },
1814     { NULL, 0 }
1815 };
1816
1817
1818 void
1819 replcmd (char **args)
1820 {
1821     int vecp = 1;
1822     char *cp, *msg = NULL;
1823     char buf[BUFSIZ], *vec[MAXARGS];
1824
1825     if (fmsh) {
1826         forkcmd (args, cmd_name);
1827         return;
1828     }
1829
1830     while ((cp = *args++)) {
1831         if (*cp == '-')
1832             switch (smatch (++cp, replswit)) {
1833                 case AMBIGSW: 
1834                     ambigsw (cp, replswit);
1835                     return;
1836                 case UNKWNSW: 
1837                     fprintf (stderr, "-%s unknown\n", cp);
1838                     return;
1839                 case REHELP: 
1840                     snprintf (buf, sizeof(buf), "%s [msgs] [switches]", cmd_name);
1841                     print_help (buf, replswit, 1);
1842                     return;
1843
1844                 case REANSW:    /* not implemented */
1845                 case RENANSW: 
1846                 case REINSW: 
1847                 case RENINSW: 
1848                     continue;
1849
1850                 case REQUSW:
1851                 case RENQUSW:
1852                 case RENDFSW:
1853                 case RENEDSW:
1854                 case RENWTSW:
1855                     vec[vecp++] = --cp;
1856                     continue;
1857
1858                 case RECCSW: 
1859                 case RENCCSW: 
1860                 case REEDTSW: 
1861                 case REFCCSW: 
1862                 case REFLTSW:
1863                 case REFRMSW: 
1864                 case REWIDSW: 
1865                 case REDFSW:
1866                 case REDMSW:
1867                 case REWHTSW:
1868                     vec[vecp++] = --cp;
1869                     if (!(cp = *args++) || *cp == '-') {
1870                         advise (NULL, "missing argument to %s", args[-2]);
1871                         return;
1872                     }
1873                     vec[vecp++] = cp;
1874                     continue;
1875             }
1876         if (*cp == '+' || *cp == '@') {
1877             advise (NULL, "sorry, no folders allowed!");
1878             return;
1879         }
1880         else
1881             if (msg) {
1882                 advise (NULL, "only one message at a time!");
1883                 return;
1884             }
1885             else
1886                 msg = cp;
1887     }
1888
1889     vec[0] = cmd_name;
1890     vec[vecp++] = "-file";
1891     vec[vecp] = NULL;
1892     if (!msg)
1893         msg = "cur";
1894     if (!m_convert (mp, msg))
1895         return;
1896     seq_setprev (mp);
1897
1898     if (mp->numsel > 1) {
1899         advise (NULL, "only one message at a time!");
1900         return;
1901     }
1902     process (mp->hghsel, cmd_name, vecp, vec);
1903     seq_setcur (mp, mp->hghsel);
1904 }
1905
1906
1907 static struct swit rmmswit[] = {
1908 #define RMHELP    0
1909     { "help", 4 },
1910     { NULL, 0 }
1911 };
1912
1913
1914 void
1915 rmmcmd (char **args)
1916 {
1917     int msgp = 0, msgnum;
1918     char *cp, buf[BUFSIZ], *msgs[MAXARGS];
1919
1920     while ((cp = *args++)) {
1921         if (*cp == '-')
1922             switch (smatch (++cp, rmmswit)) {
1923                 case AMBIGSW: 
1924                     ambigsw (cp, rmmswit);
1925                     return;
1926                 case UNKWNSW: 
1927                     fprintf (stderr, "-%s unknown\n", cp);
1928                     return;
1929                 case RMHELP: 
1930                     snprintf (buf, sizeof(buf), "%s [msgs] [switches]", cmd_name);
1931                     print_help (buf, rmmswit, 1);
1932                     return;
1933             }
1934         if (*cp == '+' || *cp == '@') {
1935             advise (NULL, "sorry, no folders allowed!");
1936             return;
1937         }
1938         else
1939             msgs[msgp++] = cp;
1940     }
1941
1942     if (!msgp)
1943         msgs[msgp++] = "cur";
1944     for (msgnum = 0; msgnum < msgp; msgnum++)
1945         if (!m_convert (mp, msgs[msgnum]))
1946             return;
1947     seq_setprev (mp);
1948
1949     rmm ();
1950 }
1951
1952
1953 static void
1954 rmm (void)
1955 {
1956     register int msgnum, vecp;
1957     register char *cp;
1958     char buffer[BUFSIZ], *vec[MAXARGS];
1959
1960     if (fmsh) {
1961         if (rmmproc) {
1962             if (mp->numsel > MAXARGS - 1) {
1963                 advise (NULL, "more than %d messages for %s exec",
1964                         MAXARGS - 1, rmmproc);
1965                 return;
1966             }
1967             vecp = 0;
1968             for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
1969                 if (is_selected (mp, msgnum))
1970                     vec[vecp++] = getcpy (m_name (msgnum));
1971             vec[vecp] = NULL;
1972             forkcmd (vec, rmmproc);
1973             for (vecp = 0; vec[vecp]; vecp++)
1974                 free (vec[vecp]);
1975         }
1976         else
1977             for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
1978                 if (is_selected (mp, msgnum)) {
1979                     strncpy (buffer, m_backup (cp = m_name (msgnum)), sizeof(buffer));
1980                     if (rename (cp, buffer) == NOTOK)
1981                         admonish (buffer, "unable to rename %s to", cp);
1982                 }
1983     }
1984
1985     for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
1986         if (is_selected (mp, msgnum)) {
1987             set_deleted (mp, msgnum);
1988             unset_exists (mp, msgnum);
1989 #ifdef  MPOP
1990 #ifdef  BPOP
1991             if (pmsh && pop_dele (msgnum) != OK)
1992                 fprintf (stderr, "%s", response);
1993 #endif
1994 #endif /* MPOP */
1995         }
1996
1997     if ((mp->nummsg -= mp->numsel) <= 0) {
1998         if (fmsh)
1999             admonish (NULL, "no messages remaining in +%s", fmsh);
2000         else
2001             admonish (NULL, "no messages remaining in %s", mp->foldpath);
2002         mp->lowmsg = mp->hghmsg = mp->nummsg = 0;
2003     }
2004     if (mp->lowsel == mp->lowmsg) {
2005         for (msgnum = mp->lowmsg + 1; msgnum <= mp->hghmsg; msgnum++)
2006             if (does_exist (mp, msgnum))
2007                 break;
2008         mp->lowmsg = msgnum;
2009     }
2010     if (mp->hghsel == mp->hghmsg) {
2011         for (msgnum = mp->hghmsg - 1; msgnum >= mp->lowmsg; msgnum--)
2012             if (does_exist (mp, msgnum))
2013                 break;
2014         mp->hghmsg = msgnum;
2015     }
2016
2017     mp->msgflags |= MODIFIED;
2018     modified++;
2019 }
2020
2021
2022 static struct swit scanswit[] = {
2023 #define SCCLR              0
2024     { "clear", 0 },
2025 #define SCNCLR             1
2026     { "noclear", 0 },
2027 #define SCFORM             2
2028     { "form formatfile", 0 },
2029 #define SCFMT              3
2030     { "format string", 5 },
2031 #define SCHEAD             4
2032     { "header", 0 },
2033 #define SCNHEAD            5
2034     { "noheader", 0 },
2035 #define SCWID              6
2036     { "width columns", 0 },
2037 #define SCHELP             7
2038     { "help", 4 },
2039     { NULL, 0 }
2040 };
2041
2042
2043 void
2044 scancmd (char **args)
2045 {
2046 #define equiv(a,b)      (a ? b && !strcmp (a, b) : !b)
2047
2048     int clearsw = 0, headersw = 0, width = 0, msgp = 0;
2049     int msgnum, optim, state;
2050     char *cp, *form = NULL, *format = NULL;
2051     char buf[BUFSIZ], *nfs, *msgs[MAXARGS];
2052     register FILE *zp;
2053 #ifdef  MPOP
2054 #ifdef  BPOP
2055     static int p_optim = 0;
2056 #endif
2057 #endif /* MPOP */
2058     static int s_optim = 0;
2059     static char *s_form = NULL, *s_format = NULL;
2060
2061     while ((cp = *args++)) {
2062         if (*cp == '-')
2063             switch (smatch (++cp, scanswit)) {
2064                 case AMBIGSW: 
2065                     ambigsw (cp, scanswit);
2066                     return;
2067                 case UNKWNSW: 
2068                     fprintf (stderr, "-%s unknown\n", cp);
2069                     return;
2070                 case SCHELP: 
2071                     snprintf (buf, sizeof(buf), "%s [msgs] [switches]", cmd_name);
2072                     print_help (buf, scanswit, 1);
2073                     return;
2074
2075                 case SCCLR: 
2076                     clearsw++;
2077                     continue;
2078                 case SCNCLR: 
2079                     clearsw = 0;
2080                     continue;
2081                 case SCHEAD: 
2082                     headersw++;
2083                     continue;
2084                 case SCNHEAD: 
2085                     headersw = 0;
2086                     continue;
2087                 case SCFORM: 
2088                     if (!(form = *args++) || *form == '-') {
2089                         advise (NULL, "missing argument to %s", args[-2]);
2090                         return;
2091                     }
2092                     format = NULL;
2093                     continue;
2094                 case SCFMT: 
2095                     if (!(format = *args++) || *format == '-') {
2096                         advise (NULL, "missing argument to %s", args[-2]);
2097                         return;
2098                     }
2099                     form = NULL;
2100                     continue;
2101                 case SCWID: 
2102                     if (!(cp = *args++) || *cp == '-') {
2103                         advise (NULL, "missing argument to %s", args[-2]);
2104                         return;
2105                     }
2106                     width = atoi (cp);
2107                     continue;
2108             }
2109         if (*cp == '+' || *cp == '@') {
2110             advise (NULL, "sorry, no folders allowed!");
2111             return;
2112         }
2113         else
2114             msgs[msgp++] = cp;
2115     }
2116
2117     if (!msgp)
2118         msgs[msgp++] = "all";
2119     for (msgnum = 0; msgnum < msgp; msgnum++)
2120         if (!m_convert (mp, msgs[msgnum]))
2121             return;
2122     seq_setprev (mp);
2123
2124     /* Get new format string */
2125     nfs = new_fs (form, format, FORMAT);
2126
2127     /* force scansbr to (re)compile format */
2128     if (scanl) {
2129         free (scanl);
2130         scanl = NULL;
2131     }
2132
2133     if (s_optim == 0) {
2134         s_optim = optim = 1;
2135         s_form = form ? getcpy (form) : NULL;
2136         s_format = format ? getcpy (format) : NULL;
2137
2138 #ifdef MPOP
2139 #ifdef BPOP
2140         if (pmsh) {
2141             int i;
2142             char *dp, *ep, *fp;
2143
2144             if (width == 0)
2145                 width = sc_width ();
2146
2147             for (dp = nfs, i = 0; *dp; dp++, i++)
2148                 if (*dp == '\\' || *dp == '"' || *dp == '\n')
2149                     i++;
2150             i++;
2151             if ((ep = malloc ((unsigned) i)) == NULL)
2152                 adios (NULL, "out of memory");
2153             for (dp = nfs, fp = ep; *dp; dp++) {
2154                 if (*dp == '\n') {
2155                     *fp++ = '\\', *fp++ = 'n';
2156                     continue;
2157                 }
2158                 if (*dp == '"' || *dp == '\\')
2159                     *fp++ = '\\';
2160                 *fp++ = *dp;
2161             }
2162             *fp = NULL;
2163
2164             if (pop_command ("XTND SCAN %d \"%s\"", width, ep) == OK)
2165                 p_optim = 1;
2166
2167             free (ep);
2168         }
2169 #endif
2170 #endif  /* MPOP */
2171     }
2172     else
2173         optim = equiv (s_form, form) && equiv (s_format, format);
2174
2175 #ifdef  MPOP
2176 #ifdef  BPOP
2177     if (p_optim && optim) {
2178         for (msgnum = mp->lowmsg; msgnum <= mp->hghmsg; msgnum++)
2179             if (!is_selected(mp, msgnum) || Msgs[msgnum].m_scanl)
2180                 break;
2181         if (msgnum > mp->hghmsg && pop_command ("LIST") == OK) {
2182             fprintf (stderr, "Stand-by...");
2183             fflush (stderr);
2184
2185             for (;;) {
2186                 int     size;
2187
2188                 switch (pop_multiline ()) {
2189                     case NOTOK:
2190                         fprintf (stderr, "%s", response);
2191                         /* and fall... */
2192                     case DONE:
2193                         fprintf (stderr,"\n");
2194                         break;
2195
2196                     case OK:
2197                         if (sscanf (response, "%d %d", &msgnum, &size) == 2
2198                                 && mp->lowmsg <= msgnum
2199                                 && msgnum <= mp->hghmsg
2200                                 && (cp = strchr(response, '#'))
2201                                 && *++cp)
2202                             Msgs[msgnum].m_scanl = concat (cp, "\n", NULL);
2203                         continue;
2204                 }
2205                 break;
2206             }
2207         }
2208     }
2209 #endif
2210 #endif /* MPOP */
2211
2212     interrupted = 0;
2213     for (msgnum = mp->lowsel;
2214             msgnum <= mp->hghsel && !interrupted;
2215             msgnum++)
2216         if (is_selected (mp, msgnum)) {
2217             if (optim && Msgs[msgnum].m_scanl)
2218                 printf ("%s", Msgs[msgnum].m_scanl);
2219             else {
2220 #ifdef  MPOP
2221 #ifdef  BPOP
2222                 if (p_optim
2223                         && optim
2224                         && is_virtual (mp, msgnum)
2225                         && pop_command ("LIST %d", msgnum) == OK
2226                         && (cp = strchr(response, '#'))
2227                         && *++cp) {
2228                     Msgs[msgnum].m_scanl = concat (cp, "\n", NULL);
2229                     printf ("%s", Msgs[msgnum].m_scanl);                    
2230                     continue;
2231                 }
2232 #endif
2233 #endif /* MPOP */
2234
2235                 zp = msh_ready (msgnum, 0);
2236                 switch (state = scan (zp, msgnum, 0, nfs, width,
2237                         msgnum == mp->curmsg,
2238                         is_unseen (mp, msgnum),
2239                         headersw ? (fmsh ? fmsh : mp->foldpath) : NULL,
2240                         fmsh ? 0L : (long) (Msgs[msgnum].m_stop - Msgs[msgnum].m_start),
2241                         1)) {
2242                     case SCNMSG:
2243                     case SCNENC:
2244                     case SCNERR:
2245                         if (optim)
2246                             Msgs[msgnum].m_scanl = getcpy (scanl);
2247                         break;
2248
2249                     default:
2250                         advise (NULL, "scan() botch (%d)", state);
2251                         return;
2252
2253                     case SCNEOF:
2254                         printf ("%*d  empty\n", DMAXFOLDER, msgnum);
2255                         break;
2256                     }
2257             }
2258             headersw = 0;
2259         }
2260
2261     if (clearsw)
2262         clear_screen ();
2263 }
2264
2265
2266 static struct swit showswit[] = {
2267 #define SHDRAFT               0
2268     { "draft", 5 },
2269 #define SHFORM                1
2270     { "form formfile", 4 },
2271 #define SHPROG                2
2272     { "moreproc program", 4 },
2273 #define SHNPROG               3
2274     { "nomoreproc", 3 },
2275 #define SHLEN                 4
2276     { "length lines", 4 },
2277 #define SHWID                 5
2278     { "width columns", 4 },
2279 #define SHSHOW                6
2280     { "showproc program", 4 },
2281 #define SHNSHOW               7
2282     { "noshowproc", 3 },
2283 #define SHHEAD                8
2284     { "header", 4 },
2285 #define SHNHEAD               9
2286     { "noheader", 3 },
2287 #define SHHELP               10
2288     { "help", 4 },
2289     { NULL, 0 }
2290 };
2291
2292
2293 void
2294 showcmd (char **args)
2295 {
2296     int headersw = 1, nshow = 0, msgp = 0, vecp = 1;
2297     int mhl = 0, seqnum = -1, mode = 0, i, msgnum;
2298     char *cp, *proc = showproc, buf[BUFSIZ];
2299     char *msgs[MAXARGS], *vec[MAXARGS];
2300
2301     if (!strcasecmp (cmd_name, "next"))
2302         mode = 1;
2303     else
2304         if (!strcasecmp (cmd_name, "prev"))
2305             mode = -1;
2306     while ((cp = *args++)) {
2307         if (*cp == '-')
2308             switch (i = smatch (++cp, showswit)) {
2309                 case AMBIGSW: 
2310                     ambigsw (cp, showswit);
2311                     return;
2312                 case UNKWNSW: 
2313                 case SHNPROG:
2314                     vec[vecp++] = --cp;
2315                     continue;
2316                 case SHHELP: 
2317                     snprintf (buf, sizeof(buf), "%s %s[switches] [switches for showproc]",
2318                             cmd_name, mode ? NULL : "[msgs] ");
2319                     print_help (buf, showswit, 1);
2320                     return;
2321
2322                 case SHFORM: 
2323                 case SHPROG:
2324                 case SHLEN:
2325                 case SHWID:
2326                     vec[vecp++] = --cp;
2327                     if (!(cp = *args++) || *cp == '-') {
2328                         advise (NULL, "missing argument to %s", args[-2]);
2329                         return;
2330                     }
2331                     vec[vecp++] = cp;
2332                     continue;
2333                 case SHHEAD: 
2334                     headersw++;
2335                     continue;
2336                 case SHNHEAD: 
2337                     headersw = 0;
2338                     continue;
2339                 case SHSHOW: 
2340                     if (!(proc = *args++) || *proc == '-') {
2341                         advise (NULL, "missing argument to %s", args[-2]);
2342                         return;
2343                     }
2344                     nshow = 0;
2345                     continue;
2346                 case SHNSHOW: 
2347                     nshow++;
2348                     continue;
2349
2350                 case SHDRAFT: 
2351                     advise (NULL, "sorry, -%s not allowed!", showswit[i].sw);
2352                     return;
2353             }
2354         if (*cp == '+' || *cp == '@') {
2355             advise (NULL, "sorry, no folders allowed!");
2356             return;
2357         }
2358         else
2359             if (mode) {
2360                 fprintf (stderr,
2361                         "usage: %s [switches] [switches for showproc]\n",
2362                         cmd_name);
2363                 return;
2364             }
2365             else
2366                 msgs[msgp++] = cp;
2367     }
2368     vec[vecp] = NULL;
2369
2370     if (!msgp)
2371         msgs[msgp++] = mode > 0 ? "next" : mode < 0 ? "prev" : "cur";
2372     for (msgnum = 0; msgnum < msgp; msgnum++)
2373         if (!m_convert (mp, msgs[msgnum]))
2374             return;
2375     seq_setprev (mp);
2376
2377     if (!nshow && !getenv ("NOMHNPROC"))
2378         for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
2379             if (is_selected (mp, msgnum) && is_nontext (msgnum)) {
2380                 proc = showmimeproc;
2381                 vec[vecp++] = "-show";
2382                 vec[vecp++] = "-file";
2383                 vec[vecp] = NULL;
2384                 goto finish;
2385             }
2386
2387     if (nshow)
2388         proc = catproc;
2389     else
2390         if (strcmp (showproc, "mhl") == 0) {
2391             proc = mhlproc;
2392             mhl++;
2393         }
2394
2395 finish: ;
2396     seqnum = seq_getnum (mp, "unseen");
2397     vec[0] = r1bindex (proc, '/');
2398     if (mhl) {
2399         msgp = vecp;
2400         for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
2401             if (is_selected (mp, msgnum)) {
2402                 vec[vecp++] = getcpy (m_name (msgnum));
2403                 if (seqnum != -1)
2404                     seq_delmsg (mp, "unseen", msgnum);
2405             }
2406         vec[vecp] = NULL;
2407         if (mp->numsel == 1 && headersw)
2408             show (mp->lowsel);
2409         mhlsbr (vecp, vec, mhl_action);
2410         m_eomsbr ((int (*)()) 0);
2411         while (msgp < vecp)
2412             free (vec[msgp++]);
2413     } else {
2414         interrupted = 0;
2415         for (msgnum = mp->lowsel;
2416                 msgnum <= mp->hghsel && !interrupted;
2417                 msgnum++)
2418             if (is_selected (mp, msgnum)) {
2419                 switch (ask (msgnum)) {
2420                     case NOTOK: /* QUIT */
2421                         break;
2422
2423                     case OK:    /* INTR */
2424                         continue;
2425
2426                     default:
2427                         if (mp->numsel == 1 && headersw)
2428                             show (msgnum);
2429                         if (nshow)
2430                             copy_message (msgnum, stdout);
2431                         else
2432                             process (msgnum, proc, vecp, vec);
2433
2434                         if (seqnum != -1)
2435                             seq_delmsg (mp, "unseen", msgnum);
2436                         continue;
2437                 }
2438                 break;
2439             }
2440     }
2441
2442     seq_setcur (mp, mp->hghsel);
2443 }
2444
2445
2446 static void
2447 show (int msgnum)
2448 {
2449     if (Msgs[msgnum].m_bboard_id == 0)
2450         readid (msgnum);
2451
2452     printf ("(Message %d", msgnum);
2453     if (Msgs[msgnum].m_bboard_id > 0)
2454         printf (", %s: %d", BBoard_ID, Msgs[msgnum].m_bboard_id);
2455     printf (")\n");
2456 }
2457
2458
2459
2460 static int
2461 eom_action (int c)
2462 {
2463     return (ftell (mhlfp) >= Msgs[mhlnum].m_stop);
2464 }
2465
2466
2467 static FILE *
2468 mhl_action (char *name)
2469 {
2470     int msgnum;
2471
2472     if ((msgnum = m_atoi (name)) < mp->lowmsg
2473             || msgnum > mp->hghmsg
2474             || !does_exist (mp, msgnum))
2475         return NULL;
2476     mhlnum = msgnum;
2477
2478     mhlfp = msh_ready (msgnum, 1);
2479     if (!fmsh)
2480         m_eomsbr (eom_action);
2481
2482     return mhlfp;
2483 }
2484
2485
2486
2487 static int
2488 ask (int msgnum)
2489 {
2490     char buf[BUFSIZ];
2491
2492     if (mp->numsel == 1 || !interactive || redirected)
2493         return DONE;
2494
2495     if (SOprintf ("Press <return> to list \"%d\"...", msgnum)) {
2496         if (mp->lowsel != msgnum)
2497             printf ("\n\n\n");
2498         printf ("Press <return> to list \"%d\"...", msgnum);
2499     }
2500     fflush (stdout);
2501     buf[0] = 0;
2502
2503 #ifndef BSD42
2504     read (fileno (stdout), buf, sizeof buf);
2505 #else /* BSD42 */
2506     switch (setjmp (sigenv)) {
2507         case OK: 
2508             should_intr = 1;
2509             read (fileno (stdout), buf, sizeof buf);/* fall... */
2510
2511         default: 
2512             should_intr = 0;
2513             break;
2514     }
2515 #endif /* BSD42 */
2516
2517     if (strchr(buf, '\n') == NULL)
2518         putchar ('\n');
2519
2520     if (told_to_quit) {
2521         told_to_quit = interrupted = 0;
2522         return NOTOK;
2523     }
2524     if (interrupted) {
2525         interrupted = 0;
2526         return OK;
2527     }
2528
2529     return DONE;
2530 }
2531
2532
2533 #include <h/mime.h>
2534
2535 static int
2536 is_nontext (int msgnum)
2537 {
2538     int result, state;
2539     char *bp, *cp, *dp;
2540     char buf[BUFSIZ], name[NAMESZ];
2541     FILE *fp;
2542
2543     if (Msgs[msgnum].m_flags & MHNCHK)
2544         return (Msgs[msgnum].m_flags & MHNYES);
2545     Msgs[msgnum].m_flags |= MHNCHK;
2546
2547     fp = msh_ready (msgnum, 1);
2548
2549     for (state = FLD;;)
2550         switch (state = m_getfld (state, name, buf, sizeof buf, fp)) {
2551         case FLD:
2552         case FLDPLUS:
2553         case FLDEOF:
2554             /*
2555              * Check Content-Type field
2556              */
2557             if (!strcasecmp (name, TYPE_FIELD)) {
2558                 int passno;
2559                 char c;
2560
2561                 cp = add (buf, NULL);
2562                 while (state == FLDPLUS) {
2563                     state = m_getfld (state, name, buf, sizeof buf, fp);
2564                     cp = add (buf, cp);
2565                 }
2566                 bp = cp;
2567                 passno = 1;
2568
2569 again:
2570                 for (; isspace (*bp); bp++)
2571                     continue;
2572                 if (*bp == '(') {
2573                     int i;
2574
2575                     for (bp++, i = 0;;) {
2576                         switch (*bp++) {
2577                         case '\0':
2578 invalid:
2579                             result = 0;
2580                             goto out;
2581                         case '\\':
2582                             if (*bp++ == '\0')
2583                                 goto invalid;
2584                             continue;
2585                         case '(':
2586                             i++;
2587                             /* and fall... */
2588                         default:
2589                             continue;
2590                         case ')':
2591                             if (--i < 0)
2592                                 break;
2593                             continue;
2594                         }
2595                         break;
2596                     }
2597                 }
2598                 if (passno == 2) {
2599                     if (*bp != '/')
2600                         goto invalid;
2601                     bp++;
2602                     passno = 3;
2603                     goto again;
2604                 }
2605                 for (dp = bp; istoken (*dp); dp++)
2606                     continue;
2607                 c = *dp;
2608                 *dp = '\0';
2609                 if (!*bp)
2610                     goto invalid;
2611                 if (passno > 1) {
2612                     if ((result = (strcasecmp (bp, "plain") != 0)))
2613                         goto out;
2614                     *dp = c;
2615                     for (dp++; isspace (*dp); dp++)
2616                         continue;
2617                     if (*dp) {
2618                         if ((result = !uprf (dp, "charset")))
2619                             goto out;
2620                         dp += sizeof "charset" - 1;
2621                         while (isspace (*dp))
2622                             dp++;
2623                         if (*dp++ != '=')
2624                             goto invalid;
2625                         while (isspace (*dp))
2626                             dp++;
2627                         if (*dp == '"') {
2628                             if ((bp = strchr(++dp, '"')))
2629                                 *bp = '\0';
2630                         } else {
2631                             for (bp = dp; *bp; bp++)
2632                                 if (isspace (*bp)) {
2633                                     *bp = '\0';
2634                                     break;
2635                                 }
2636                         }
2637                     } else {
2638                         /* Default character set */
2639                         dp = "US-ASCII";
2640                     }
2641                     /* Check the character set */
2642                     result = !check_charset (dp, strlen (dp));
2643                 } else {
2644                     if (!(result = (strcasecmp (bp, "text") != 0))) {
2645                         *dp = c;
2646                         bp = dp;
2647                         passno = 2;
2648                         goto again;
2649                     }
2650                 }
2651 out:
2652                 free (cp);
2653                 if (result) {
2654                     Msgs[msgnum].m_flags |= MHNYES;
2655                     return result;
2656                 }
2657                 break;
2658             }
2659
2660             /*
2661              * Check Content-Transfer-Encoding field
2662              */
2663             if (!strcasecmp (name, ENCODING_FIELD)) {
2664                 cp = add (buf, NULL);
2665                 while (state == FLDPLUS) {
2666                     state = m_getfld (state, name, buf, sizeof buf, fp);
2667                     cp = add (buf, cp);
2668                 }
2669                 for (bp = cp; isspace (*bp); bp++)
2670                     continue;
2671                 for (dp = bp; istoken (*dp); dp++)
2672                     continue;
2673                 *dp = '\0';
2674                 result = (strcasecmp (bp, "7bit")
2675                        && strcasecmp (bp, "8bit")
2676                        && strcasecmp (bp, "binary"));
2677
2678                 free (cp);
2679                 if (result) {
2680                     Msgs[msgnum].m_flags |= MHNYES;
2681                     return result;
2682                 }
2683                 break;
2684             }
2685
2686             /*
2687              * Just skip the rest of this header
2688              * field and go to next one.
2689              */
2690             while (state == FLDPLUS)
2691                 state = m_getfld (state, name, buf, sizeof(buf), fp);
2692             break;
2693
2694             /*
2695              * We've passed the message header,
2696              * so message is just text.
2697              */
2698         default:
2699             return 0;
2700         }
2701 }
2702
2703
2704 static struct swit sortswit[] = {
2705 #define SODATE               0
2706     { "datefield field", 0 },
2707 #define SOSUBJ               1
2708     { "textfield field", 0 },
2709 #define SONSUBJ              2
2710     { "notextfield", 0 },
2711 #define SOLIMT               3
2712     { "limit days", 0 },
2713 #define SONLIMT              4
2714     { "nolimit", 0 },
2715 #define SOVERB               5
2716     { "verbose", 0 },
2717 #define SONVERB              6
2718     { "noverbose", 0 },
2719 #define SOHELP               7
2720     { "help", 4 },
2721     { NULL, 0 }
2722 };
2723
2724
2725 void
2726 sortcmd (char **args)
2727 {
2728     int msgp = 0, msgnum;
2729     char *cp, *datesw = NULL, *subjsw = NULL;
2730     char buf[BUFSIZ], *msgs[MAXARGS];
2731     struct tws tb;
2732
2733     if (fmsh) {
2734         forkcmd (args, cmd_name);
2735         return;
2736     }
2737
2738     while ((cp = *args++)) {
2739         if (*cp == '-')
2740             switch (smatch (++cp, sortswit)) {
2741                 case AMBIGSW: 
2742                     ambigsw (cp, sortswit);
2743                     return;
2744                 case UNKWNSW: 
2745                     fprintf (stderr, "-%s unknown\n", cp);
2746                     return;
2747                 case SOHELP: 
2748                     snprintf (buf, sizeof(buf), "%s [msgs] [switches]", cmd_name);
2749                     print_help (buf, sortswit, 1);
2750                     return;
2751
2752                 case SODATE: 
2753                     if (datesw) {
2754                         advise (NULL, "only one date field at a time!");
2755                         return;
2756                     }
2757                     if (!(datesw = *args++) || *datesw == '-') {
2758                         advise (NULL, "missing argument to %s", args[-2]);
2759                         return;
2760                     }
2761                     continue;
2762
2763                 case SOSUBJ:
2764                     if (subjsw) {
2765                         advise (NULL, "only one text field at a time!");
2766                         return;
2767                     }
2768                     if (!(subjsw = *args++) || *subjsw == '-') {
2769                         advise (NULL, "missing argument to %s", args[-2]);
2770                         return;
2771                     }
2772                     continue;
2773                 case SONSUBJ:
2774                     subjsw = (char *)0;
2775                     continue;
2776
2777                 case SOLIMT:            /* too hard */
2778                     if (!(cp = *args++) || *cp == '-') {
2779                         advise (NULL, "missing argument to %s", args[-2]);
2780                         return;
2781                     }
2782                 case SONLIMT:
2783                 case SOVERB:            /* not implemented */
2784                 case SONVERB: 
2785                     continue;
2786             }
2787         if (*cp == '+' || *cp == '@') {
2788             advise (NULL, "sorry, no folders allowed!");
2789             return;
2790         }
2791         else
2792             msgs[msgp++] = cp;
2793     }
2794
2795     if (!msgp)
2796         msgs[msgp++] = "all";
2797     if (!datesw)
2798         datesw = "Date";
2799     for (msgnum = 0; msgnum < msgp; msgnum++)
2800         if (!m_convert (mp, msgs[msgnum]))
2801             return;
2802     seq_setprev (mp);
2803
2804     twscopy (&tb, dlocaltimenow ());
2805
2806     for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
2807         if (Msgs[msgnum].m_scanl) {
2808             free (Msgs[msgnum].m_scanl);
2809             Msgs[msgnum].m_scanl = NULL;
2810         }
2811         if (is_selected (mp, msgnum)) {
2812             if (get_fields (datesw, subjsw, msgnum, &Msgs[msgnum]))
2813                 twscopy (&Msgs[msgnum].m_tb,
2814                         msgnum != mp->lowsel ? &Msgs[msgnum - 1].m_tb : &tb);
2815         }
2816         else                    /* m_scaln is already NULL */
2817             twscopy (&Msgs[msgnum].m_tb, &tb);
2818         Msgs[msgnum].m_stats = mp->msgstats[msgnum - mp->lowoff];
2819         if (mp->curmsg == msgnum)
2820             Msgs[msgnum].m_stats |= CUR;
2821     }
2822
2823     qsort ((char *) &Msgs[mp->lowsel], mp->hghsel - mp->lowsel + 1,
2824            sizeof(struct Msg), (qsort_comp) (subjsw ? subsort : msgsort));
2825
2826     for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
2827         if (subjsw && Msgs[msgnum].m_scanl) {
2828             free (Msgs[msgnum].m_scanl);        /* from subjsort */
2829             Msgs[msgnum].m_scanl = NULL;
2830         }
2831         mp->msgstats[msgnum - mp->lowoff] = Msgs[msgnum].m_stats & ~CUR;
2832         if (Msgs[msgnum].m_stats & CUR)
2833             seq_setcur (mp, msgnum);
2834     }
2835             
2836     mp->msgflags |= MODIFIED;
2837     modified++;
2838 }
2839
2840
2841 /* 
2842  * get_fields - parse message, and get date and subject if needed.
2843  * We'll use the msgp->m_tb tws struct for the date, and overload
2844  * the msgp->m_scanl field with our subject string.
2845  */
2846 static int
2847 get_fields (char *datesw, char *subjsw, int msgnum, struct Msg *msgp)
2848 {
2849     int state, gotdate = 0;
2850     char *bp, buf[BUFSIZ], name[NAMESZ];
2851     struct tws *tw = (struct tws *) 0;
2852     register FILE *zp;
2853
2854     zp = msh_ready (msgnum, 0);
2855     for (state = FLD;;) {
2856         switch (state = m_getfld (state, name, buf, sizeof buf, zp)) {
2857             case FLD: 
2858             case FLDEOF: 
2859             case FLDPLUS: 
2860                 if (!strcasecmp (name, datesw)) {
2861                     bp = getcpy (buf);
2862                     while (state == FLDPLUS) {
2863                         state = m_getfld (state, name, buf, sizeof buf, zp);
2864                         bp = add (buf, bp);
2865                     }
2866                     if ((tw = dparsetime (bp)) == NULL)
2867                         admonish (NULL,
2868                                 "unable to parse %s field in message %d",
2869                                 datesw, msgnum);
2870                     else
2871                         twscopy (&(msgp->m_tb), tw);
2872                     free (bp);
2873                     if (!subjsw)        /* not using this, or already done */
2874                         break;          /* all done! */
2875                     gotdate++;
2876                 }
2877                 else if (subjsw && !strcasecmp(name, subjsw)) {
2878                     bp = getcpy (buf);
2879                     while (state == FLDPLUS) {
2880                         state = m_getfld (state, name, buf, sizeof buf, zp);
2881                         bp = add (buf, bp);
2882                     }
2883                     msgp->m_scanl = sosmash(subjsw, bp);
2884                     if (gotdate)
2885                         break;          /* date done so we're done */
2886                     else
2887                         subjsw = (char *)0;/* subject done, need date */
2888                 } else {
2889                     while (state == FLDPLUS)    /* flush this one */
2890                         state = m_getfld (state, name, buf, sizeof buf, zp);
2891                 }
2892                 continue;
2893
2894             case BODY: 
2895             case BODYEOF: 
2896             case FILEEOF: 
2897                 break;
2898
2899             case LENERR: 
2900             case FMTERR: 
2901                 admonish (NULL, "format error in message %d", msgnum);
2902                 if (msgp->m_scanl) {    /* this might need free'd */
2903                     free (msgp->m_scanl); /* probably can't use subj anyway */
2904                     msgp->m_scanl = NULL;
2905                 }
2906                 return NOTOK;
2907
2908             default: 
2909                 adios (NULL, "internal error -- you lose");
2910         }
2911         break;
2912     }
2913     if (tw)
2914         return OK;      /* not an error if subj not found */
2915
2916     admonish (NULL, "no %s field in message %d", datesw, msgnum);
2917     return NOTOK;       /* NOTOK means use some other date */
2918 }
2919
2920
2921 /*
2922  * sort routines
2923  */
2924
2925 static int
2926 msgsort (struct Msg *a, struct Msg *b)
2927 {
2928     return twsort (&a->m_tb, &b->m_tb);
2929 }
2930
2931
2932 static int
2933 subsort (struct Msg *a, struct Msg *b)
2934 {
2935         register int i;
2936
2937         if (a->m_scanl && b->m_scanl)
2938             if ((i = strcmp (a->m_scanl, b->m_scanl)))
2939                 return (i);
2940
2941         return twsort (&a->m_tb, &b->m_tb);
2942 }
2943
2944
2945 /*
2946  * try to make the subject "canonical": delete leading "re:", everything
2947  * but letters & smash letters to lower case. 
2948  */
2949 static char *
2950 sosmash (char *subj, char *s)
2951 {
2952     register char *cp, *dp, c;
2953
2954     if (s) {
2955         cp = s;
2956         dp = s; /* dst pointer */
2957         if (!strcasecmp (subj, "subject"))
2958             while ((c = *cp)) {
2959                 if (! isspace(c)) {
2960                     if(uprf(cp, "re:"))
2961                         cp += 2;
2962                     else {
2963                         if (isalnum(c))
2964                             *dp++ = isupper(c) ? tolower(c) : c;
2965                         break;
2966                     }
2967                 }
2968                 cp++;
2969             }
2970         while ((c = *cp++)) {
2971             if (isalnum(c))
2972                 *dp++ = isupper(c) ? tolower(c) : c;
2973
2974         }
2975         *dp = '\0';
2976     }
2977     return s;
2978 }
2979
2980
2981 static int
2982 process (int msgnum, char *proc, int vecp, char **vec)
2983 {
2984     int child_id, status;
2985     char tmpfil[80];
2986     FILE *out;
2987
2988     if (fmsh) {
2989         strncpy (tmpfil, m_name (msgnum), sizeof(tmpfil));
2990         context_del (pfolder);
2991         context_replace (pfolder, fmsh);/* update current folder   */
2992         seq_save (mp);
2993         context_save ();                /* save the context file   */
2994         goto ready;
2995     }
2996
2997     strncpy (tmpfil, m_scratch ("", invo_name), sizeof(tmpfil));
2998     if ((out = fopen (tmpfil, "w")) == NULL) {
2999         int olderr;
3000         extern int errno;
3001         char newfil[80];
3002
3003         olderr = errno;
3004         strncpy (newfil, m_tmpfil (invo_name), sizeof(newfil));
3005         if ((out = fopen (newfil, "w")) == NULL) {
3006             errno = olderr;
3007             advise (tmpfil, "unable to create temporary file");
3008             return NOTOK;
3009         } else {
3010             strncpy (tmpfil, newfil, sizeof(tmpfil));
3011         }
3012     }
3013     copy_message (msgnum, out);
3014     fclose (out);
3015
3016 ready: ;
3017     fflush (stdout);
3018     switch (child_id = fork ()) {
3019         case NOTOK: 
3020             advise ("fork", "unable to");
3021             status = NOTOK;
3022             break;
3023             
3024         case OK: 
3025             closefds (3);
3026             SIGNAL (SIGINT, istat);
3027             SIGNAL (SIGQUIT, qstat);
3028
3029             vec[vecp++] = tmpfil;
3030             vec[vecp] = NULL;
3031
3032             execvp (proc, vec);
3033             fprintf (stderr, "unable to exec ");
3034             perror (proc);
3035             _exit (1);
3036
3037         default: 
3038             status = pidXwait (child_id, NULL);
3039             break;
3040     }
3041
3042     if (!fmsh)
3043         unlink (tmpfil);
3044     return status;
3045 }
3046
3047
3048 static void
3049 copy_message (int msgnum, FILE *out)
3050 {
3051     long pos;
3052     static char buffer[BUFSIZ];
3053     register FILE *zp;
3054
3055     zp = msh_ready (msgnum, 1);
3056     if (fmsh) {
3057         while (fgets (buffer, sizeof buffer, zp) != NULL) {
3058             fputs (buffer, out);
3059             if (interrupted && out == stdout)
3060                 break;
3061         }
3062     }
3063     else {
3064         pos = ftell (zp);
3065         while (fgets (buffer, sizeof buffer, zp) != NULL
3066                 && pos < Msgs[msgnum].m_stop) {
3067             fputs (buffer, out);
3068             pos += (long) strlen (buffer);
3069             if (interrupted && out == stdout)
3070                 break;
3071         }
3072     }
3073 }
3074
3075
3076 static void
3077 copy_digest (int msgnum, FILE *out)
3078 {
3079     char c;
3080     long pos;
3081     static char buffer[BUFSIZ];
3082     register FILE *zp;
3083
3084     c = '\n';
3085     zp = msh_ready (msgnum, 1);
3086     if (!fmsh)
3087         pos = ftell (zp);
3088     while (fgets (buffer, sizeof buffer, zp) != NULL
3089             && !fmsh && pos < Msgs[msgnum].m_stop) {
3090         if (c == '\n' && *buffer == '-')
3091             fputc (' ', out);
3092         fputs (buffer, out);
3093         c = buffer[strlen (buffer) - 1];
3094         if (!fmsh)
3095             pos += (long) strlen (buffer);
3096         if (interrupted && out == stdout)
3097             break;
3098     }
3099 }