Initial revision
[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     vec[0] = (char *)mktemp (buf);
936     vec[vecp++] = "-file";
937     vec[vecp] = NULL;
938     if (!msgp)
939         msgs[msgp++] = "cur";
940     for (msgnum = 0; msgnum < msgp; msgnum++)
941         if (!m_convert (mp, msgs[msgnum]))
942             return;
943     seq_setprev (mp);
944
945     if (filter) {
946         strncpy (buf, filter, sizeof(buf));
947         if (expand (buf) == NOTOK)
948             return;
949         if (access (filter = getcpy (etcpath (buf)), R_OK) == NOTOK) {
950             advise (filter, "unable to read");
951             free (filter);
952             return;
953         }
954     }
955     forw (cmd_name, filter, vecp, vec);
956     seq_setcur (mp, mp->hghsel);
957     if (filter)
958         free (filter);
959 }
960
961
962 static void
963 forw (char *proc, char *filter, int vecp, char **vec)
964 {
965     int i, child_id, msgnum, msgcnt;
966     char tmpfil[80], *args[MAXARGS];
967     FILE *out;
968
969     strncpy (tmpfil, m_tmpfil (invo_name), sizeof(tmpfil));
970     interrupted = 0;
971     if (filter)
972         switch (child_id = fork ()) {
973             case NOTOK: 
974                 advise ("fork", "unable to");
975                 return;
976
977             case OK:            /* "trust me" */
978                 if (freopen (tmpfil, "w", stdout) == NULL) {
979                     fprintf (stderr, "unable to create ");
980                     perror (tmpfil);
981                     _exit (1);
982                 }
983                 args[0] = r1bindex (mhlproc, '/');
984                 i = 1;
985                 args[i++] = "-forwall";
986                 args[i++] = "-form";
987                 args[i++] = filter;
988                 for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
989                     if (is_selected (mp, msgnum))
990                         args[i++] = getcpy (m_name (msgnum));
991                 args[i] = NULL;
992                 mhlsbr (i, args, mhl_action);
993                 m_eomsbr ((int (*) ()) 0);
994                 fclose (stdout);
995                 _exit (0);
996
997             default: 
998                 if (pidXwait (child_id, NULL))
999                     interrupted++;
1000                 break;
1001         }
1002     else {
1003         if ((out = fopen (tmpfil, "w")) == NULL) {
1004             advise (tmpfil, "unable to create temporary file");
1005             return;
1006         }
1007
1008         msgcnt = 1;
1009         for (msgnum = mp->lowsel;
1010                 msgnum <= mp->hghsel && !interrupted;
1011                 msgnum++)
1012             if (is_selected (mp, msgnum)) {
1013                 fprintf (out, "\n\n-------");
1014                 if (msgnum == mp->lowsel)
1015                     fprintf (out, " Forwarded Message%s",
1016                             mp->numsel > 1 ? "s" : "");
1017                 else
1018                     fprintf (out, " Message %d", msgcnt);
1019                 fprintf (out, "\n\n");
1020                 copy_digest (msgnum, out);
1021                 msgcnt++;
1022             }
1023
1024         fprintf (out, "\n\n------- End of Forwarded Message%s\n",
1025                 mp->numsel > 1 ? "s" : "");
1026         fclose (out);
1027     }
1028
1029     fflush (stdout);
1030     if (!interrupted)
1031         switch (child_id = fork ()) {
1032             case NOTOK: 
1033                 advise ("fork", "unable to");
1034                 break;
1035
1036             case OK: 
1037                 closefds (3);
1038                 SIGNAL (SIGINT, istat);
1039                 SIGNAL (SIGQUIT, qstat);
1040
1041                 vec[vecp++] = tmpfil;
1042                 vec[vecp] = NULL;
1043
1044                 execvp (proc, vec);
1045                 fprintf (stderr, "unable to exec ");
1046                 perror (proc);
1047                 _exit (1);
1048
1049             default: 
1050                 pidXwait (child_id, NULL);
1051                 break;
1052         }
1053
1054     unlink (tmpfil);
1055 }
1056
1057
1058 static char *hlpmsg[] = {
1059     "The %s program emulates many of the commands found in the nmh",
1060     "system.  Instead of operating on nmh folders, commands to %s concern",
1061     "a single file.",
1062     "",
1063     "To see the list of commands available, just type a ``?'' followed by",
1064     "the RETURN key.  To find out what switches each command takes, type",
1065     "the name of the command followed by ``-help''.  To leave %s, use the",
1066     "``quit'' command.",
1067     "",
1068     "Although a lot of nmh commands are found in %s, not all are fully",
1069     "implemented.  %s will always recognize all legal switches for a",
1070     "given command though, and will let you know when you ask for an",
1071     "option that it is unable to perform.",
1072     "",
1073     "Running %s is fun, but using nmh from your shell is far superior.",
1074     "After you have familiarized yourself with the nmh style by using %s,",
1075     "you should try using nmh from the shell.  You can still use %s for",
1076     "message files that aren't in nmh format, such as BBoard files.",
1077     NULL
1078 };
1079
1080
1081 void
1082 helpcmd (char **args)
1083 {
1084     int i;
1085
1086     for (i = 0; hlpmsg[i]; i++) {
1087         printf (hlpmsg[i], invo_name);
1088         putchar ('\n');
1089     }
1090 }
1091
1092
1093 static struct swit markswit[] = {
1094 #define MADDSW             0
1095     { "add", 0 },
1096 #define MDELSW             1
1097     { "delete", 0 },
1098 #define MLSTSW             2
1099     { "list", 0 },
1100 #define MSEQSW             3
1101     { "sequence name", 0 },
1102 #define MPUBSW             4
1103     { "public", 0 },
1104 #define MNPUBSW            5
1105     { "nopublic", 0 },
1106 #define MZERSW             6
1107     { "zero", 0 },
1108 #define MNZERSW            7
1109     { "nozero", 0 },
1110 #define MHELP              8
1111     { "help", 4 },
1112 #define MDBUGSW            9
1113     { "debug", -5 },
1114     { NULL, 0 }
1115 };
1116
1117
1118 void
1119 markcmd (char **args)
1120 {
1121     int addsw = 0, deletesw = 0, debugsw = 0;
1122     int listsw = 0, zerosw = 0, seqp = 0;
1123     int msgp = 0, msgnum;
1124     char *cp, buf[BUFSIZ];
1125     char *seqs[NUMATTRS + 1], *msgs[MAXARGS];
1126
1127     while ((cp = *args++)) {
1128         if (*cp == '-') {
1129             switch (smatch (++cp, markswit)) {
1130                 case AMBIGSW: 
1131                     ambigsw (cp, markswit);
1132                     return;
1133                 case UNKWNSW: 
1134                     fprintf (stderr, "-%s unknown\n", cp);
1135                     return;
1136                 case MHELP: 
1137                     snprintf (buf, sizeof(buf), "%s [msgs] [switches]", cmd_name);
1138                     print_help (buf, markswit, 1);
1139                     return;
1140
1141                 case MADDSW: 
1142                     addsw++;
1143                     deletesw = listsw = 0;
1144                     continue;
1145                 case MDELSW: 
1146                     deletesw++;
1147                     addsw = listsw = 0;
1148                     continue;
1149                 case MLSTSW: 
1150                     listsw++;
1151                     addsw = deletesw = 0;
1152                     continue;
1153
1154                 case MSEQSW: 
1155                     if (!(cp = *args++) || *cp == '-') {
1156                         advise (NULL, "missing argument to %s", args[-2]);
1157                         return;
1158                     }
1159                     if (seqp < NUMATTRS)
1160                         seqs[seqp++] = cp;
1161                     else {
1162                         advise (NULL, "only %d sequences allowed!", NUMATTRS);
1163                         return;
1164                     }
1165                     continue;
1166
1167                 case MPUBSW:    /* not implemented */
1168                 case MNPUBSW: 
1169                     continue;
1170
1171                 case MDBUGSW: 
1172                     debugsw++;
1173                     continue;
1174
1175                 case MZERSW: 
1176                     zerosw++;
1177                     continue;
1178                 case MNZERSW: 
1179                     zerosw = 0;
1180                     continue;
1181             }
1182         }
1183         if (*cp == '+' || *cp == '@') {
1184             advise (NULL, "sorry, no folders allowed!");
1185             return;
1186         } else {
1187             msgs[msgp++] = cp;
1188         }
1189     }
1190
1191     if (!addsw && !deletesw && !listsw)
1192         if (seqp)
1193             addsw++;
1194         else
1195             if (debugsw)
1196                 listsw++;
1197             else {
1198                 seqs[seqp++] = "unseen";
1199                 deletesw++;
1200                 zerosw = 0;
1201                 if (!msgp)
1202                     msgs[msgp++] = "all";
1203             }
1204
1205     if (!msgp)
1206         msgs[msgp++] = listsw ? "all" :"cur";
1207     for (msgnum = 0; msgnum < msgp; msgnum++)
1208         if (!m_convert (mp, msgs[msgnum]))
1209             return;
1210
1211     if (debugsw) {
1212         printf ("invo_name=%s mypath=%s defpath=%s\n",
1213                 invo_name, mypath, defpath);
1214         printf ("ctxpath=%s context flags=%s\n",
1215                 ctxpath, snprintb (buf, sizeof(buf), (unsigned) ctxflags, DBITS));
1216         printf ("foldpath=%s flags=%s\n",
1217                 mp->foldpath,
1218                 snprintb (buf, sizeof(buf), (unsigned) mp->msgflags, FBITS));
1219         printf ("hghmsg=%d lowmsg=%d nummsg=%d curmsg=%d\n",
1220                 mp->hghmsg, mp->lowmsg, mp->nummsg, mp->curmsg);
1221         printf ("lowsel=%d hghsel=%d numsel=%d\n",
1222                 mp->lowsel, mp->hghsel, mp->numsel);
1223         printf ("lowoff=%d hghoff=%d\n", mp->lowoff, mp->hghoff);
1224     }
1225
1226     if (seqp == 0 && (addsw || deletesw)) {
1227         advise (NULL, "-%s requires at least one -sequence argument",
1228                 addsw ? "add" : "delete");
1229         return;
1230     }
1231     seqs[seqp] = NULL;
1232
1233     if (addsw) {
1234         for (seqp = 0; seqs[seqp]; seqp++)
1235             if (!seq_addsel (mp, seqs[seqp], 0, zerosw))
1236                 return;
1237     }
1238
1239     if (deletesw) {
1240         for (seqp = 0; seqs[seqp]; seqp++)
1241             if (!seq_delsel (mp, seqs[seqp], 0, zerosw))
1242                 return;
1243     }
1244
1245     /* Listing messages in sequences */
1246     if (listsw) {
1247         if (seqp) {
1248             /* list the given sequences */
1249             for (seqp = 0; seqs[seqp]; seqp++)
1250                 seq_print (mp, seqs[seqp]);
1251         } else {
1252             /* else list them all */
1253             seq_printall (mp);
1254         }
1255
1256         interrupted = 0;
1257         if (debugsw)
1258             for (msgnum = mp->lowsel;
1259                     msgnum <= mp->hghsel && !interrupted;
1260                     msgnum++)
1261                 if (is_selected (mp, msgnum)) {
1262                     printf ("%*d: id=%d top=%d start=%ld stop=%ld %s\n",
1263                         DMAXFOLDER,
1264                         msgnum,
1265                         Msgs[msgnum].m_bboard_id,
1266                         Msgs[msgnum].m_top,
1267                         (long) Msgs[msgnum].m_start,
1268                         (long) Msgs[msgnum].m_stop,
1269                         snprintb (buf, sizeof(buf),
1270                                 (unsigned) mp->msgstats[msgnum - mp->lowoff],
1271                                 seq_bits (mp)));
1272                     if (Msgs[msgnum].m_scanl)
1273                         printf ("%s", Msgs[msgnum].m_scanl);
1274                 }                           
1275     }
1276 }
1277
1278
1279 static struct swit mhnswit[] = {
1280 #define MHNAUTOSW           0
1281     { "auto", 0 },
1282 #define MHNNAUTOSW          1
1283     { "noauto", 0 },
1284 #define MHNDEBUGSW          2
1285     { "debug", -5 },
1286 #define MHNEBCDICSW         3
1287     { "ebcdicsafe", 0 },
1288 #define MHNNEBCDICSW        4
1289     { "noebcdicsafe", 0 },
1290 #define MHNFORMSW           5
1291     { "form formfile", 4 },
1292 #define MHNHEADSW           6
1293     { "headers", 0 },
1294 #define MHNNHEADSW          7
1295     { "noheaders", 0 },
1296 #define MHNLISTSW           8
1297     { "list", 0 },
1298 #define MHNNLISTSW          9
1299     { "nolist", 0 },
1300 #define MHNPARTSW          10
1301     { "part number", 0 },
1302 #define MHNSIZESW          11
1303     { "realsize", 0 },
1304 #define MHNNSIZESW         12
1305     { "norealsize", 0 },
1306 #define MHNRFC934SW        13
1307     { "rfc934mode", 0 },
1308 #define MHNNRFC934SW       14
1309     { "norfc934mode", 0 },
1310 #define MHNSERIALSW        15
1311     { "serialonly", 0 },
1312 #define MHNNSERIALSW       16
1313     { "noserialonly", 0 },
1314 #define MHNSHOWSW          17
1315     { "show", 0 },
1316 #define MHNNSHOWSW         18
1317     { "noshow", 0 },
1318 #define MHNSTORESW         19
1319     { "store", 0 },
1320 #define MHNNSTORESW        20
1321     { "nostore", 0 },
1322 #define MHNTYPESW          21
1323     { "type content", 0 },
1324 #define MHNVERBSW          22
1325     { "verbose", 0 },
1326 #define MHNNVERBSW         23
1327     { "noverbose", 0 },
1328 #define MHNHELPSW          24
1329     { "help", 4 },
1330 #define MHNPROGSW          25
1331     { "moreproc program", -4 },
1332 #define MHNNPROGSW         26
1333     { "nomoreproc", -3 },
1334 #define MHNLENSW           27
1335     { "length lines", -4 },
1336 #define MHNWIDSW           28
1337     { "width columns", -4 },
1338     { NULL, 0 }
1339 };
1340
1341
1342 void
1343 mhncmd (char **args)
1344 {
1345     int msgp = 0, vecp = 1;
1346     int msgnum;
1347     char *cp, buf[BUFSIZ];
1348     char *msgs[MAXARGS], *vec[MAXARGS];
1349
1350     if (fmsh) {
1351         forkcmd (args, cmd_name);
1352         return;
1353     }
1354     while ((cp = *args++)) {
1355         if (*cp == '-') {
1356             switch (smatch (++cp, mhnswit)) {
1357                 case AMBIGSW: 
1358                     ambigsw (cp, mhnswit);
1359                     return;
1360                 case UNKWNSW: 
1361                     fprintf (stderr, "-%s unknown\n", cp);
1362                     return;
1363                 case MHNHELPSW:
1364                     snprintf (buf, sizeof(buf), "%s [msgs] [switches]", cmd_name);
1365                     print_help (buf, mhnswit, 1);
1366                     return;
1367
1368                 case MHNAUTOSW:
1369                 case MHNNAUTOSW:
1370                 case MHNDEBUGSW:
1371                 case MHNEBCDICSW:
1372                 case MHNNEBCDICSW:
1373                 case MHNHEADSW:
1374                 case MHNNHEADSW:
1375                 case MHNLISTSW:
1376                 case MHNNLISTSW:
1377                 case MHNSIZESW:
1378                 case MHNNSIZESW:
1379                 case MHNRFC934SW:
1380                 case MHNNRFC934SW:
1381                 case MHNSERIALSW:
1382                 case MHNNSERIALSW:
1383                 case MHNSHOWSW:
1384                 case MHNNSHOWSW:
1385                 case MHNSTORESW:
1386                 case MHNNSTORESW:
1387                 case MHNVERBSW:
1388                 case MHNNVERBSW:
1389                 case MHNNPROGSW:
1390                     vec[vecp++] = --cp;
1391                     continue;
1392
1393                 case MHNFORMSW:
1394                 case MHNPARTSW:
1395                 case MHNTYPESW:
1396                 case MHNPROGSW:
1397                 case MHNLENSW:
1398                 case MHNWIDSW:
1399                     vec[vecp++] = --cp;
1400                     if (!(cp = *args++) || *cp == '-') {
1401                         advise (NULL, "missing argument to %s", args[-2]);
1402                         return;
1403                     }
1404                     vec[vecp++] = cp;
1405                     continue;
1406             }
1407         }
1408         if (*cp == '+' || *cp == '@') {
1409             advise (NULL, "sorry, no folders allowed!");
1410             return;
1411         } else {
1412             msgs[msgp++] = cp;
1413         }
1414     }
1415
1416     vec[0] = cmd_name;
1417     vec[vecp++] = "-file";
1418     vec[vecp] = NULL;
1419     if (!msgp)
1420         msgs[msgp++] = "cur";
1421     for (msgnum = 0; msgnum < msgp; msgnum++)
1422         if (!m_convert (mp, msgs[msgnum]))
1423             return;
1424     seq_setprev (mp);
1425
1426     interrupted = 0;
1427     for (msgnum = mp->lowsel;
1428             msgnum <= mp->hghsel && !interrupted;
1429             msgnum++)
1430         if (is_selected (mp, msgnum))
1431             if (process (msgnum, cmd_name, vecp, vec)) {
1432                 unset_selected (mp, msgnum);
1433                 mp->numsel--;
1434             }
1435
1436     seq_setcur (mp, mp->hghsel);
1437 }
1438
1439
1440 static struct swit packswit[] = {
1441 #define PAFISW         0
1442     { "file name", 0 },
1443 #define PAHELP         1
1444     { "help", 4 },
1445     { NULL, 0 }
1446 };
1447
1448 static mbx_style = MMDF_FORMAT;
1449
1450 void
1451 packcmd (char **args)
1452 {
1453     int msgp = 0, md, msgnum;
1454     char *cp, *file = NULL;
1455     char buf[BUFSIZ], *msgs[MAXARGS];
1456     struct stat st;
1457
1458     if (fmsh) {
1459         forkcmd (args, cmd_name);
1460         return;
1461     }
1462
1463     while ((cp = *args++)) {
1464         if (*cp == '-')
1465             switch (smatch (++cp, packswit)) {
1466                 case AMBIGSW: 
1467                     ambigsw (cp, packswit);
1468                     return;
1469                 case UNKWNSW: 
1470                     fprintf (stderr, "-%s unknown\n", cp);
1471                     return;
1472                 case PAHELP: 
1473                     snprintf (buf, sizeof(buf), "%s [msgs] [switches]", cmd_name);
1474                     print_help (buf, packswit, 1);
1475                     return;
1476
1477                 case PAFISW: 
1478                     if (!(file = *args++) || *file == '-') {
1479                         advise (NULL, "missing argument to %s", args[-2]);
1480                         return;
1481                     }
1482                     continue;
1483             }
1484         if (*cp == '+' || *cp == '@') {
1485             advise (NULL, "sorry, no folders allowed!");
1486             return;
1487         }
1488         else
1489             msgs[msgp++] = cp;
1490     }
1491
1492     if (!file)
1493         file = "./msgbox";
1494     file = path (file, TFILE);
1495     if (stat (file, &st) == NOTOK) {
1496         if (errno != ENOENT) {
1497             advise (file, "error on file");
1498             goto done_pack;
1499         }
1500         md = getanswer (cp = concat ("Create file \"", file, "\"? ", NULL));
1501         free (cp);
1502         if (!md)
1503             goto done_pack;
1504     }
1505
1506     if (!msgp)
1507         msgs[msgp++] = "all";
1508     for (msgnum = 0; msgnum < msgp; msgnum++)
1509         if (!m_convert (mp, msgs[msgnum]))
1510             goto done_pack;
1511     seq_setprev (mp);
1512
1513     if ((md = mbx_open (file, mbx_style, getuid (), getgid (), m_gmprot ())) == NOTOK) {
1514         advise (file, "unable to open");
1515         goto done_pack;
1516     }
1517     for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
1518         if (is_selected (mp, msgnum))
1519             if (pack (file, md, msgnum) == NOTOK)
1520                 break;
1521     mbx_close (file, md);
1522
1523     if (mp->hghsel != mp->curmsg)
1524         seq_setcur (mp, mp->lowsel);
1525
1526 done_pack: ;
1527     free (file);
1528 }
1529
1530
1531 int
1532 pack (char *mailbox, int md, int msgnum)
1533 {
1534     register FILE *zp;
1535
1536     if (Msgs[msgnum].m_bboard_id == 0)
1537         readid (msgnum);
1538
1539     zp = msh_ready (msgnum, 1);
1540     return mbx_write (mailbox, md, zp, Msgs[msgnum].m_bboard_id,
1541             0L, ftell (zp), Msgs[msgnum].m_stop, 1, 1);
1542 }
1543
1544
1545 int
1546 packhak (char **args)
1547 {
1548     int result;
1549     char *cp, *file = NULL;
1550
1551     while ((cp = *args++)) {
1552         if (*cp == '-')
1553             switch (smatch (++cp, packswit)) {
1554                 case AMBIGSW: 
1555                 case UNKWNSW: 
1556                 case PAHELP: 
1557                     return NOTOK;
1558
1559                 case PAFISW: 
1560                     if (!(file = *args++) || *file == '-') 
1561                         return NOTOK;
1562                     continue;
1563             }
1564         if (*cp == '+' || *cp == '@')
1565             return NOTOK;
1566     }
1567
1568     file = path (file ? file : "./msgbox", TFILE);
1569     result = access (file, F_OK) == NOTOK ? OK : NOTOK;
1570     free (file);
1571
1572     return result;
1573 }
1574
1575
1576 static struct swit pickswit[] = {
1577 #define PIANSW                0
1578     { "and", 0 },
1579 #define PIORSW                1
1580     { "or", 0 },
1581 #define PINTSW                2
1582     { "not", 0 },
1583 #define PILBSW                3
1584     { "lbrace", 0 },
1585 #define PIRBSW                4
1586     { "rbrace", 0 },
1587 #define PICCSW                5
1588     { "cc  pattern", 0 },
1589 #define PIDASW                6
1590     { "date  pattern", 0 },
1591 #define PIFRSW                7
1592     { "from  pattern", 0 },
1593 #define PISESW                8
1594     { "search  pattern", 0 },
1595 #define PISUSW                9
1596     { "subject  pattern", 0 },
1597 #define PITOSW               10
1598     { "to  pattern", 0 },
1599 #define PIOTSW               11
1600     { "-othercomponent  pattern", 15 },
1601 #define PIAFSW               12
1602     { "after date", 0 },
1603 #define PIBFSW               13
1604     { "before date", 0 },
1605 #define PIDFSW               14
1606     { "datefield field", 5 },
1607 #define PISQSW               15
1608     { "sequence name", 0 },
1609 #define PIPUSW               16
1610     { "public", 0 },
1611 #define PINPUSW              17
1612     { "nopublic", 0 },
1613 #define PIZRSW               18
1614     { "zero", 0 },
1615 #define PINZRSW              19
1616     { "nozero", 0 },
1617 #define PILISW               20
1618     { "list", 0 },
1619 #define PINLISW              21
1620     { "nolist", 0 },
1621 #define PIHELP               22
1622     { "help", 4 },
1623     { NULL, 0 }
1624 };
1625
1626
1627 void
1628 pickcmd (char **args)
1629 {
1630     int zerosw = 1, msgp = 0, seqp = 0;
1631     int vecp = 0, hi, lo, msgnum;
1632     char *cp, buf[BUFSIZ], *msgs[MAXARGS];
1633     char *seqs[NUMATTRS], *vec[MAXARGS];
1634     register FILE *zp;
1635
1636     while ((cp = *args++)) {
1637         if (*cp == '-') {
1638             if (*++cp == '-') {
1639                 vec[vecp++] = --cp;
1640                 goto pattern;
1641             }
1642             switch (smatch (cp, pickswit)) {
1643                 case AMBIGSW: 
1644                     ambigsw (cp, pickswit);
1645                     return;
1646                 case UNKWNSW: 
1647                     fprintf (stderr, "-%s unknown\n", cp);
1648                     return;
1649                 case PIHELP: 
1650                     snprintf (buf, sizeof(buf), "%s [msgs] [switches]", cmd_name);
1651                     print_help (buf, pickswit, 1);
1652                     return;
1653
1654                 case PICCSW: 
1655                 case PIDASW: 
1656                 case PIFRSW: 
1657                 case PISUSW: 
1658                 case PITOSW: 
1659                 case PIDFSW: 
1660                 case PIAFSW: 
1661                 case PIBFSW: 
1662                 case PISESW: 
1663                     vec[vecp++] = --cp;
1664 pattern: ;
1665                     if (!(cp = *args++)) {/* allow -xyz arguments */
1666                         advise (NULL, "missing argument to %s", args[-2]);
1667                         return;
1668                     }
1669                     vec[vecp++] = cp;
1670                     continue;
1671                 case PIOTSW: 
1672                     advise (NULL, "internal error!");
1673                     return;
1674                 case PIANSW: 
1675                 case PIORSW: 
1676                 case PINTSW: 
1677                 case PILBSW: 
1678                 case PIRBSW: 
1679                     vec[vecp++] = --cp;
1680                     continue;
1681
1682                 case PISQSW: 
1683                     if (!(cp = *args++) || *cp == '-') {
1684                         advise (NULL, "missing argument to %s", args[-2]);
1685                         return;
1686                     }
1687                     if (seqp < NUMATTRS)
1688                         seqs[seqp++] = cp;
1689                     else {
1690                         advise (NULL, "only %d sequences allowed!", NUMATTRS);
1691                         return;
1692                     }
1693                     continue;
1694                 case PIZRSW: 
1695                     zerosw++;
1696                     continue;
1697                 case PINZRSW: 
1698                     zerosw = 0;
1699                     continue;
1700
1701                 case PIPUSW:    /* not implemented */
1702                 case PINPUSW: 
1703                 case PILISW: 
1704                 case PINLISW: 
1705                     continue;
1706             }
1707         }
1708         if (*cp == '+' || *cp == '@') {
1709             advise (NULL, "sorry, no folders allowed!");
1710             return;
1711         }
1712         else
1713             msgs[msgp++] = cp;
1714     }
1715     vec[vecp] = NULL;
1716
1717     if (!msgp)
1718         msgs[msgp++] = "all";
1719     for (msgnum = 0; msgnum < msgp; msgnum++)
1720         if (!m_convert (mp, msgs[msgnum]))
1721             return;
1722     seq_setprev (mp);
1723
1724     interrupted = 0;
1725     if (!pcompile (vec, NULL))
1726         return;
1727
1728     lo = mp->lowsel;
1729     hi = mp->hghsel;
1730
1731     for (msgnum = mp->lowsel;
1732             msgnum <= mp->hghsel && !interrupted;
1733             msgnum++)
1734         if (is_selected (mp, msgnum)) {
1735             zp = msh_ready (msgnum, 1);
1736             if (pmatches (zp, msgnum, fmsh ? 0L : Msgs[msgnum].m_start,
1737                         fmsh ? 0L : Msgs[msgnum].m_stop)) {
1738                 if (msgnum < lo)
1739                     lo = msgnum;
1740                 if (msgnum > hi)
1741                     hi = msgnum;
1742             }
1743             else {
1744                 unset_selected (mp, msgnum);
1745                 mp->numsel--;
1746             }
1747         }
1748
1749     if (interrupted)
1750         return;
1751
1752     mp->lowsel = lo;
1753     mp->hghsel = hi;
1754
1755     if (mp->numsel <= 0) {
1756         advise (NULL, "no messages match specification");
1757         return;
1758     }
1759
1760     seqs[seqp] = NULL;
1761     for (seqp = 0; seqs[seqp]; seqp++)
1762         if (!seq_addsel (mp, seqs[seqp], 0, zerosw))
1763             return;
1764
1765     printf ("%d hit%s\n", mp->numsel, mp->numsel == 1 ? "" : "s");
1766 }
1767
1768
1769 static struct swit replswit[] = {
1770 #define REANSW                  0
1771     { "annotate", 0 },
1772 #define RENANSW                 1
1773     { "noannotate", 0 },
1774 #define RECCSW                  2
1775     { "cc type", 0 },
1776 #define RENCCSW                 3
1777     { "nocc type", 0 },
1778 #define REDFSW                  4
1779     { "draftfolder +folder", 0 },
1780 #define REDMSW                  5
1781     { "draftmessage msg", 0 },
1782 #define RENDFSW                 6
1783     { "nodraftfolder", 0 },
1784 #define REEDTSW                 7
1785     { "editor editor", 0 },
1786 #define RENEDSW                 8
1787     { "noedit", 0 },
1788 #define REFCCSW                 9
1789     { "fcc +folder", 0 },
1790 #define REFLTSW                10
1791     { "filter filterfile", 0 },
1792 #define REFRMSW                11
1793     { "form formfile", 0 },
1794 #define REINSW                 12
1795     { "inplace", 0 },
1796 #define RENINSW                13
1797     { "noinplace", 0 },
1798 #define REQUSW                 14
1799     { "query", 0 },
1800 #define RENQUSW                15
1801     { "noquery", 0 },
1802 #define REWHTSW                16
1803     { "whatnowproc program", 0 },
1804 #define RENWTSW                17
1805     { "nowhatnow", 0 },
1806 #define REWIDSW                19
1807     { "width columns", 0 },
1808 #define REHELP                 20
1809     { "help", 4 },
1810     { NULL, 0 }
1811 };
1812
1813
1814 void
1815 replcmd (char **args)
1816 {
1817     int vecp = 1;
1818     char *cp, *msg = NULL;
1819     char buf[BUFSIZ], *vec[MAXARGS];
1820
1821     if (fmsh) {
1822         forkcmd (args, cmd_name);
1823         return;
1824     }
1825
1826     while ((cp = *args++)) {
1827         if (*cp == '-')
1828             switch (smatch (++cp, replswit)) {
1829                 case AMBIGSW: 
1830                     ambigsw (cp, replswit);
1831                     return;
1832                 case UNKWNSW: 
1833                     fprintf (stderr, "-%s unknown\n", cp);
1834                     return;
1835                 case REHELP: 
1836                     snprintf (buf, sizeof(buf), "%s [msgs] [switches]", cmd_name);
1837                     print_help (buf, replswit, 1);
1838                     return;
1839
1840                 case REANSW:    /* not implemented */
1841                 case RENANSW: 
1842                 case REINSW: 
1843                 case RENINSW: 
1844                     continue;
1845
1846                 case REQUSW:
1847                 case RENQUSW:
1848                 case RENDFSW:
1849                 case RENEDSW:
1850                 case RENWTSW:
1851                     vec[vecp++] = --cp;
1852                     continue;
1853
1854                 case RECCSW: 
1855                 case RENCCSW: 
1856                 case REEDTSW: 
1857                 case REFCCSW: 
1858                 case REFLTSW:
1859                 case REFRMSW: 
1860                 case REWIDSW: 
1861                 case REDFSW:
1862                 case REDMSW:
1863                 case REWHTSW:
1864                     vec[vecp++] = --cp;
1865                     if (!(cp = *args++) || *cp == '-') {
1866                         advise (NULL, "missing argument to %s", args[-2]);
1867                         return;
1868                     }
1869                     vec[vecp++] = cp;
1870                     continue;
1871             }
1872         if (*cp == '+' || *cp == '@') {
1873             advise (NULL, "sorry, no folders allowed!");
1874             return;
1875         }
1876         else
1877             if (msg) {
1878                 advise (NULL, "only one message at a time!");
1879                 return;
1880             }
1881             else
1882                 msg = cp;
1883     }
1884
1885     vec[0] = cmd_name;
1886     vec[vecp++] = "-file";
1887     vec[vecp] = NULL;
1888     if (!msg)
1889         msg = "cur";
1890     if (!m_convert (mp, msg))
1891         return;
1892     seq_setprev (mp);
1893
1894     if (mp->numsel > 1) {
1895         advise (NULL, "only one message at a time!");
1896         return;
1897     }
1898     process (mp->hghsel, cmd_name, vecp, vec);
1899     seq_setcur (mp, mp->hghsel);
1900 }
1901
1902
1903 static struct swit rmmswit[] = {
1904 #define RMHELP    0
1905     { "help", 4 },
1906     { NULL, 0 }
1907 };
1908
1909
1910 void
1911 rmmcmd (char **args)
1912 {
1913     int msgp = 0, msgnum;
1914     char *cp, buf[BUFSIZ], *msgs[MAXARGS];
1915
1916     while ((cp = *args++)) {
1917         if (*cp == '-')
1918             switch (smatch (++cp, rmmswit)) {
1919                 case AMBIGSW: 
1920                     ambigsw (cp, rmmswit);
1921                     return;
1922                 case UNKWNSW: 
1923                     fprintf (stderr, "-%s unknown\n", cp);
1924                     return;
1925                 case RMHELP: 
1926                     snprintf (buf, sizeof(buf), "%s [msgs] [switches]", cmd_name);
1927                     print_help (buf, rmmswit, 1);
1928                     return;
1929             }
1930         if (*cp == '+' || *cp == '@') {
1931             advise (NULL, "sorry, no folders allowed!");
1932             return;
1933         }
1934         else
1935             msgs[msgp++] = cp;
1936     }
1937
1938     if (!msgp)
1939         msgs[msgp++] = "cur";
1940     for (msgnum = 0; msgnum < msgp; msgnum++)
1941         if (!m_convert (mp, msgs[msgnum]))
1942             return;
1943     seq_setprev (mp);
1944
1945     rmm ();
1946 }
1947
1948
1949 static void
1950 rmm (void)
1951 {
1952     register int msgnum, vecp;
1953     register char *cp;
1954     char buffer[BUFSIZ], *vec[MAXARGS];
1955
1956     if (fmsh) {
1957         if (rmmproc) {
1958             if (mp->numsel > MAXARGS - 1) {
1959                 advise (NULL, "more than %d messages for %s exec",
1960                         MAXARGS - 1, rmmproc);
1961                 return;
1962             }
1963             vecp = 0;
1964             for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
1965                 if (is_selected (mp, msgnum))
1966                     vec[vecp++] = getcpy (m_name (msgnum));
1967             vec[vecp] = NULL;
1968             forkcmd (vec, rmmproc);
1969             for (vecp = 0; vec[vecp]; vecp++)
1970                 free (vec[vecp]);
1971         }
1972         else
1973             for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
1974                 if (is_selected (mp, msgnum)) {
1975                     strncpy (buffer, m_backup (cp = m_name (msgnum)), sizeof(buffer));
1976                     if (rename (cp, buffer) == NOTOK)
1977                         admonish (buffer, "unable to rename %s to", cp);
1978                 }
1979     }
1980
1981     for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
1982         if (is_selected (mp, msgnum)) {
1983             set_deleted (mp, msgnum);
1984             unset_exists (mp, msgnum);
1985 #ifdef  MPOP
1986 #ifdef  BPOP
1987             if (pmsh && pop_dele (msgnum) != OK)
1988                 fprintf (stderr, "%s", response);
1989 #endif
1990 #endif /* MPOP */
1991         }
1992
1993     if ((mp->nummsg -= mp->numsel) <= 0) {
1994         if (fmsh)
1995             admonish (NULL, "no messages remaining in +%s", fmsh);
1996         else
1997             admonish (NULL, "no messages remaining in %s", mp->foldpath);
1998         mp->lowmsg = mp->hghmsg = mp->nummsg = 0;
1999     }
2000     if (mp->lowsel == mp->lowmsg) {
2001         for (msgnum = mp->lowmsg + 1; msgnum <= mp->hghmsg; msgnum++)
2002             if (does_exist (mp, msgnum))
2003                 break;
2004         mp->lowmsg = msgnum;
2005     }
2006     if (mp->hghsel == mp->hghmsg) {
2007         for (msgnum = mp->hghmsg - 1; msgnum >= mp->lowmsg; msgnum--)
2008             if (does_exist (mp, msgnum))
2009                 break;
2010         mp->hghmsg = msgnum;
2011     }
2012
2013     mp->msgflags |= MODIFIED;
2014     modified++;
2015 }
2016
2017
2018 static struct swit scanswit[] = {
2019 #define SCCLR              0
2020     { "clear", 0 },
2021 #define SCNCLR             1
2022     { "noclear", 0 },
2023 #define SCFORM             2
2024     { "form formatfile", 0 },
2025 #define SCFMT              3
2026     { "format string", 5 },
2027 #define SCHEAD             4
2028     { "header", 0 },
2029 #define SCNHEAD            5
2030     { "noheader", 0 },
2031 #define SCWID              6
2032     { "width columns", 0 },
2033 #define SCHELP             7
2034     { "help", 4 },
2035     { NULL, 0 }
2036 };
2037
2038
2039 void
2040 scancmd (char **args)
2041 {
2042 #define equiv(a,b)      (a ? b && !strcmp (a, b) : !b)
2043
2044     int clearsw = 0, headersw = 0, width = 0, msgp = 0;
2045     int msgnum, optim, state;
2046     char *cp, *form = NULL, *format = NULL;
2047     char buf[BUFSIZ], *nfs, *msgs[MAXARGS];
2048     register FILE *zp;
2049 #ifdef  MPOP
2050 #ifdef  BPOP
2051     static int p_optim = 0;
2052 #endif
2053 #endif /* MPOP */
2054     static int s_optim = 0;
2055     static char *s_form = NULL, *s_format = NULL;
2056
2057     while ((cp = *args++)) {
2058         if (*cp == '-')
2059             switch (smatch (++cp, scanswit)) {
2060                 case AMBIGSW: 
2061                     ambigsw (cp, scanswit);
2062                     return;
2063                 case UNKWNSW: 
2064                     fprintf (stderr, "-%s unknown\n", cp);
2065                     return;
2066                 case SCHELP: 
2067                     snprintf (buf, sizeof(buf), "%s [msgs] [switches]", cmd_name);
2068                     print_help (buf, scanswit, 1);
2069                     return;
2070
2071                 case SCCLR: 
2072                     clearsw++;
2073                     continue;
2074                 case SCNCLR: 
2075                     clearsw = 0;
2076                     continue;
2077                 case SCHEAD: 
2078                     headersw++;
2079                     continue;
2080                 case SCNHEAD: 
2081                     headersw = 0;
2082                     continue;
2083                 case SCFORM: 
2084                     if (!(form = *args++) || *form == '-') {
2085                         advise (NULL, "missing argument to %s", args[-2]);
2086                         return;
2087                     }
2088                     format = NULL;
2089                     continue;
2090                 case SCFMT: 
2091                     if (!(format = *args++) || *format == '-') {
2092                         advise (NULL, "missing argument to %s", args[-2]);
2093                         return;
2094                     }
2095                     form = NULL;
2096                     continue;
2097                 case SCWID: 
2098                     if (!(cp = *args++) || *cp == '-') {
2099                         advise (NULL, "missing argument to %s", args[-2]);
2100                         return;
2101                     }
2102                     width = atoi (cp);
2103                     continue;
2104             }
2105         if (*cp == '+' || *cp == '@') {
2106             advise (NULL, "sorry, no folders allowed!");
2107             return;
2108         }
2109         else
2110             msgs[msgp++] = cp;
2111     }
2112
2113     if (!msgp)
2114         msgs[msgp++] = "all";
2115     for (msgnum = 0; msgnum < msgp; msgnum++)
2116         if (!m_convert (mp, msgs[msgnum]))
2117             return;
2118     seq_setprev (mp);
2119
2120     /* Get new format string */
2121     nfs = new_fs (form, format, FORMAT);
2122
2123     /* force scansbr to (re)compile format */
2124     if (scanl) {
2125         free (scanl);
2126         scanl = NULL;
2127     }
2128
2129     if (s_optim == 0) {
2130         s_optim = optim = 1;
2131         s_form = form ? getcpy (form) : NULL;
2132         s_format = format ? getcpy (format) : NULL;
2133
2134 #ifdef MPOP
2135 #ifdef BPOP
2136         if (pmsh) {
2137             int i;
2138             char *dp, *ep, *fp;
2139
2140             if (width == 0)
2141                 width = sc_width ();
2142
2143             for (dp = nfs, i = 0; *dp; dp++, i++)
2144                 if (*dp == '\\' || *dp == '"' || *dp == '\n')
2145                     i++;
2146             i++;
2147             if ((ep = malloc ((unsigned) i)) == NULL)
2148                 adios (NULL, "out of memory");
2149             for (dp = nfs, fp = ep; *dp; dp++) {
2150                 if (*dp == '\n') {
2151                     *fp++ = '\\', *fp++ = 'n';
2152                     continue;
2153                 }
2154                 if (*dp == '"' || *dp == '\\')
2155                     *fp++ = '\\';
2156                 *fp++ = *dp;
2157             }
2158             *fp = NULL;
2159
2160             if (pop_command ("XTND SCAN %d \"%s\"", width, ep) == OK)
2161                 p_optim = 1;
2162
2163             free (ep);
2164         }
2165 #endif
2166 #endif  /* MPOP */
2167     }
2168     else
2169         optim = equiv (s_form, form) && equiv (s_format, format);
2170
2171 #ifdef  MPOP
2172 #ifdef  BPOP
2173     if (p_optim && optim) {
2174         for (msgnum = mp->lowmsg; msgnum <= mp->hghmsg; msgnum++)
2175             if (!is_selected(mp, msgnum) || Msgs[msgnum].m_scanl)
2176                 break;
2177         if (msgnum > mp->hghmsg && pop_command ("LIST") == OK) {
2178             fprintf (stderr, "Stand-by...");
2179             fflush (stderr);
2180
2181             for (;;) {
2182                 int     size;
2183
2184                 switch (pop_multiline ()) {
2185                     case NOTOK:
2186                         fprintf (stderr, "%s", response);
2187                         /* and fall... */
2188                     case DONE:
2189                         fprintf (stderr,"\n");
2190                         break;
2191
2192                     case OK:
2193                         if (sscanf (response, "%d %d", &msgnum, &size) == 2
2194                                 && mp->lowmsg <= msgnum
2195                                 && msgnum <= mp->hghmsg
2196                                 && (cp = strchr(response, '#'))
2197                                 && *++cp)
2198                             Msgs[msgnum].m_scanl = concat (cp, "\n", NULL);
2199                         continue;
2200                 }
2201                 break;
2202             }
2203         }
2204     }
2205 #endif
2206 #endif /* MPOP */
2207
2208     interrupted = 0;
2209     for (msgnum = mp->lowsel;
2210             msgnum <= mp->hghsel && !interrupted;
2211             msgnum++)
2212         if (is_selected (mp, msgnum)) {
2213             if (optim && Msgs[msgnum].m_scanl)
2214                 printf ("%s", Msgs[msgnum].m_scanl);
2215             else {
2216 #ifdef  MPOP
2217 #ifdef  BPOP
2218                 if (p_optim
2219                         && optim
2220                         && is_virtual (mp, msgnum)
2221                         && pop_command ("LIST %d", msgnum) == OK
2222                         && (cp = strchr(response, '#'))
2223                         && *++cp) {
2224                     Msgs[msgnum].m_scanl = concat (cp, "\n", NULL);
2225                     printf ("%s", Msgs[msgnum].m_scanl);                    
2226                     continue;
2227                 }
2228 #endif
2229 #endif /* MPOP */
2230
2231                 zp = msh_ready (msgnum, 0);
2232                 switch (state = scan (zp, msgnum, 0, nfs, width,
2233                         msgnum == mp->curmsg,
2234                         is_unseen (mp, msgnum),
2235                         headersw ? (fmsh ? fmsh : mp->foldpath) : NULL,
2236                         fmsh ? 0L : (long) (Msgs[msgnum].m_stop - Msgs[msgnum].m_start),
2237                         1)) {
2238                     case SCNMSG:
2239                     case SCNENC:
2240                     case SCNERR:
2241                         if (optim)
2242                             Msgs[msgnum].m_scanl = getcpy (scanl);
2243                         break;
2244
2245                     default:
2246                         advise (NULL, "scan() botch (%d)", state);
2247                         return;
2248
2249                     case SCNEOF:
2250                         printf ("%*d  empty\n", DMAXFOLDER, msgnum);
2251                         break;
2252                     }
2253             }
2254             headersw = 0;
2255         }
2256
2257     if (clearsw)
2258         clear_screen ();
2259 }
2260
2261
2262 static struct swit showswit[] = {
2263 #define SHDRAFT               0
2264     { "draft", 5 },
2265 #define SHFORM                1
2266     { "form formfile", 4 },
2267 #define SHPROG                2
2268     { "moreproc program", 4 },
2269 #define SHNPROG               3
2270     { "nomoreproc", 3 },
2271 #define SHLEN                 4
2272     { "length lines", 4 },
2273 #define SHWID                 5
2274     { "width columns", 4 },
2275 #define SHSHOW                6
2276     { "showproc program", 4 },
2277 #define SHNSHOW               7
2278     { "noshowproc", 3 },
2279 #define SHHEAD                8
2280     { "header", 4 },
2281 #define SHNHEAD               9
2282     { "noheader", 3 },
2283 #define SHHELP               10
2284     { "help", 4 },
2285     { NULL, 0 }
2286 };
2287
2288
2289 void
2290 showcmd (char **args)
2291 {
2292     int headersw = 1, nshow = 0, msgp = 0, vecp = 1;
2293     int mhl = 0, seqnum = -1, mode = 0, i, msgnum;
2294     char *cp, *proc = showproc, buf[BUFSIZ];
2295     char *msgs[MAXARGS], *vec[MAXARGS];
2296
2297     if (!strcasecmp (cmd_name, "next"))
2298         mode = 1;
2299     else
2300         if (!strcasecmp (cmd_name, "prev"))
2301             mode = -1;
2302     while ((cp = *args++)) {
2303         if (*cp == '-')
2304             switch (i = smatch (++cp, showswit)) {
2305                 case AMBIGSW: 
2306                     ambigsw (cp, showswit);
2307                     return;
2308                 case UNKWNSW: 
2309                 case SHNPROG:
2310                     vec[vecp++] = --cp;
2311                     continue;
2312                 case SHHELP: 
2313                     snprintf (buf, sizeof(buf), "%s %s[switches] [switches for showproc]",
2314                             cmd_name, mode ? NULL : "[msgs] ");
2315                     print_help (buf, showswit, 1);
2316                     return;
2317
2318                 case SHFORM: 
2319                 case SHPROG:
2320                 case SHLEN:
2321                 case SHWID:
2322                     vec[vecp++] = --cp;
2323                     if (!(cp = *args++) || *cp == '-') {
2324                         advise (NULL, "missing argument to %s", args[-2]);
2325                         return;
2326                     }
2327                     vec[vecp++] = cp;
2328                     continue;
2329                 case SHHEAD: 
2330                     headersw++;
2331                     continue;
2332                 case SHNHEAD: 
2333                     headersw = 0;
2334                     continue;
2335                 case SHSHOW: 
2336                     if (!(proc = *args++) || *proc == '-') {
2337                         advise (NULL, "missing argument to %s", args[-2]);
2338                         return;
2339                     }
2340                     nshow = 0;
2341                     continue;
2342                 case SHNSHOW: 
2343                     nshow++;
2344                     continue;
2345
2346                 case SHDRAFT: 
2347                     advise (NULL, "sorry, -%s not allowed!", showswit[i].sw);
2348                     return;
2349             }
2350         if (*cp == '+' || *cp == '@') {
2351             advise (NULL, "sorry, no folders allowed!");
2352             return;
2353         }
2354         else
2355             if (mode) {
2356                 fprintf (stderr,
2357                         "usage: %s [switches] [switches for showproc]\n",
2358                         cmd_name);
2359                 return;
2360             }
2361             else
2362                 msgs[msgp++] = cp;
2363     }
2364     vec[vecp] = NULL;
2365
2366     if (!msgp)
2367         msgs[msgp++] = mode > 0 ? "next" : mode < 0 ? "prev" : "cur";
2368     for (msgnum = 0; msgnum < msgp; msgnum++)
2369         if (!m_convert (mp, msgs[msgnum]))
2370             return;
2371     seq_setprev (mp);
2372
2373     if (!nshow && !getenv ("NOMHNPROC"))
2374         for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
2375             if (is_selected (mp, msgnum) && is_nontext (msgnum)) {
2376                 proc = showmimeproc;
2377                 vec[vecp++] = "-show";
2378                 vec[vecp++] = "-file";
2379                 vec[vecp] = NULL;
2380                 goto finish;
2381             }
2382
2383     if (nshow)
2384         proc = catproc;
2385     else
2386         if (strcmp (showproc, "mhl") == 0) {
2387             proc = mhlproc;
2388             mhl++;
2389         }
2390
2391 finish: ;
2392     seqnum = seq_getnum (mp, "unseen");
2393     vec[0] = r1bindex (proc, '/');
2394     if (mhl) {
2395         msgp = vecp;
2396         for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
2397             if (is_selected (mp, msgnum)) {
2398                 vec[vecp++] = getcpy (m_name (msgnum));
2399                 if (seqnum != -1)
2400                     seq_delmsg (mp, "unseen", msgnum);
2401             }
2402         vec[vecp] = NULL;
2403         if (mp->numsel == 1 && headersw)
2404             show (mp->lowsel);
2405         mhlsbr (vecp, vec, mhl_action);
2406         m_eomsbr ((int (*)()) 0);
2407         while (msgp < vecp)
2408             free (vec[msgp++]);
2409     } else {
2410         interrupted = 0;
2411         for (msgnum = mp->lowsel;
2412                 msgnum <= mp->hghsel && !interrupted;
2413                 msgnum++)
2414             if (is_selected (mp, msgnum)) {
2415                 switch (ask (msgnum)) {
2416                     case NOTOK: /* QUIT */
2417                         break;
2418
2419                     case OK:    /* INTR */
2420                         continue;
2421
2422                     default:
2423                         if (mp->numsel == 1 && headersw)
2424                             show (msgnum);
2425                         if (nshow)
2426                             copy_message (msgnum, stdout);
2427                         else
2428                             process (msgnum, proc, vecp, vec);
2429
2430                         if (seqnum != -1)
2431                             seq_delmsg (mp, "unseen", msgnum);
2432                         continue;
2433                 }
2434                 break;
2435             }
2436     }
2437
2438     seq_setcur (mp, mp->hghsel);
2439 }
2440
2441
2442 static void
2443 show (int msgnum)
2444 {
2445     if (Msgs[msgnum].m_bboard_id == 0)
2446         readid (msgnum);
2447
2448     printf ("(Message %d", msgnum);
2449     if (Msgs[msgnum].m_bboard_id > 0)
2450         printf (", %s: %d", BBoard_ID, Msgs[msgnum].m_bboard_id);
2451     printf (")\n");
2452 }
2453
2454
2455
2456 static int
2457 eom_action (int c)
2458 {
2459     return (ftell (mhlfp) >= Msgs[mhlnum].m_stop);
2460 }
2461
2462
2463 static FILE *
2464 mhl_action (char *name)
2465 {
2466     int msgnum;
2467
2468     if ((msgnum = m_atoi (name)) < mp->lowmsg
2469             || msgnum > mp->hghmsg
2470             || !does_exist (mp, msgnum))
2471         return NULL;
2472     mhlnum = msgnum;
2473
2474     mhlfp = msh_ready (msgnum, 1);
2475     if (!fmsh)
2476         m_eomsbr (eom_action);
2477
2478     return mhlfp;
2479 }
2480
2481
2482
2483 static int
2484 ask (int msgnum)
2485 {
2486     char buf[BUFSIZ];
2487
2488     if (mp->numsel == 1 || !interactive || redirected)
2489         return DONE;
2490
2491     if (SOprintf ("Press <return> to list \"%d\"...", msgnum)) {
2492         if (mp->lowsel != msgnum)
2493             printf ("\n\n\n");
2494         printf ("Press <return> to list \"%d\"...", msgnum);
2495     }
2496     fflush (stdout);
2497     buf[0] = 0;
2498
2499 #ifndef BSD42
2500     read (fileno (stdout), buf, sizeof buf);
2501 #else /* BSD42 */
2502     switch (setjmp (sigenv)) {
2503         case OK: 
2504             should_intr = 1;
2505             read (fileno (stdout), buf, sizeof buf);/* fall... */
2506
2507         default: 
2508             should_intr = 0;
2509             break;
2510     }
2511 #endif /* BSD42 */
2512
2513     if (strchr(buf, '\n') == NULL)
2514         putchar ('\n');
2515
2516     if (told_to_quit) {
2517         told_to_quit = interrupted = 0;
2518         return NOTOK;
2519     }
2520     if (interrupted) {
2521         interrupted = 0;
2522         return OK;
2523     }
2524
2525     return DONE;
2526 }
2527
2528
2529 #include <h/mime.h>
2530
2531 static int
2532 is_nontext (int msgnum)
2533 {
2534     int result, state;
2535     char *bp, *cp, *dp;
2536     char buf[BUFSIZ], name[NAMESZ];
2537     FILE *fp;
2538
2539     if (Msgs[msgnum].m_flags & MHNCHK)
2540         return (Msgs[msgnum].m_flags & MHNYES);
2541     Msgs[msgnum].m_flags |= MHNCHK;
2542
2543     fp = msh_ready (msgnum, 1);
2544
2545     for (state = FLD;;)
2546         switch (state = m_getfld (state, name, buf, sizeof buf, fp)) {
2547         case FLD:
2548         case FLDPLUS:
2549         case FLDEOF:
2550             /*
2551              * Check Content-Type field
2552              */
2553             if (!strcasecmp (name, TYPE_FIELD)) {
2554                 int passno;
2555                 char c;
2556
2557                 cp = add (buf, NULL);
2558                 while (state == FLDPLUS) {
2559                     state = m_getfld (state, name, buf, sizeof buf, fp);
2560                     cp = add (buf, cp);
2561                 }
2562                 bp = cp;
2563                 passno = 1;
2564
2565 again:
2566                 for (; isspace (*bp); bp++)
2567                     continue;
2568                 if (*bp == '(') {
2569                     int i;
2570
2571                     for (bp++, i = 0;;) {
2572                         switch (*bp++) {
2573                         case '\0':
2574 invalid:
2575                             result = 0;
2576                             goto out;
2577                         case '\\':
2578                             if (*bp++ == '\0')
2579                                 goto invalid;
2580                             continue;
2581                         case '(':
2582                             i++;
2583                             /* and fall... */
2584                         default:
2585                             continue;
2586                         case ')':
2587                             if (--i < 0)
2588                                 break;
2589                             continue;
2590                         }
2591                         break;
2592                     }
2593                 }
2594                 if (passno == 2) {
2595                     if (*bp != '/')
2596                         goto invalid;
2597                     bp++;
2598                     passno = 3;
2599                     goto again;
2600                 }
2601                 for (dp = bp; istoken (*dp); dp++)
2602                     continue;
2603                 c = *dp;
2604                 *dp = '\0';
2605                 if (!*bp)
2606                     goto invalid;
2607                 if (passno > 1) {
2608                     if ((result = (strcasecmp (bp, "plain") != 0)))
2609                         goto out;
2610                     *dp = c;
2611                     for (dp++; isspace (*dp); dp++)
2612                         continue;
2613                     if (*dp) {
2614                         if ((result = !uprf (dp, "charset")))
2615                             goto out;
2616                         dp += sizeof "charset" - 1;
2617                         while (isspace (*dp))
2618                             dp++;
2619                         if (*dp++ != '=')
2620                             goto invalid;
2621                         while (isspace (*dp))
2622                             dp++;
2623                         if (*dp == '"') {
2624                             if ((bp = strchr(++dp, '"')))
2625                                 *bp = '\0';
2626                         } else {
2627                             for (bp = dp; *bp; bp++)
2628                                 if (isspace (*bp)) {
2629                                     *bp = '\0';
2630                                     break;
2631                                 }
2632                         }
2633                     } else {
2634                         /* Default character set */
2635                         dp = "US-ASCII";
2636                     }
2637                     /* Check the character set */
2638                     result = !check_charset (dp, strlen (dp));
2639                 } else {
2640                     if (!(result = (strcasecmp (bp, "text") != 0))) {
2641                         *dp = c;
2642                         bp = dp;
2643                         passno = 2;
2644                         goto again;
2645                     }
2646                 }
2647 out:
2648                 free (cp);
2649                 if (result) {
2650                     Msgs[msgnum].m_flags |= MHNYES;
2651                     return result;
2652                 }
2653                 break;
2654             }
2655
2656             /*
2657              * Check Content-Transfer-Encoding field
2658              */
2659             if (!strcasecmp (name, ENCODING_FIELD)) {
2660                 cp = add (buf, NULL);
2661                 while (state == FLDPLUS) {
2662                     state = m_getfld (state, name, buf, sizeof buf, fp);
2663                     cp = add (buf, cp);
2664                 }
2665                 for (bp = cp; isspace (*bp); bp++)
2666                     continue;
2667                 for (dp = bp; istoken (*dp); dp++)
2668                     continue;
2669                 *dp = '\0';
2670                 result = (strcasecmp (bp, "7bit")
2671                        && strcasecmp (bp, "8bit")
2672                        && strcasecmp (bp, "binary"));
2673
2674                 free (cp);
2675                 if (result) {
2676                     Msgs[msgnum].m_flags |= MHNYES;
2677                     return result;
2678                 }
2679                 break;
2680             }
2681
2682             /*
2683              * Just skip the rest of this header
2684              * field and go to next one.
2685              */
2686             while (state == FLDPLUS)
2687                 state = m_getfld (state, name, buf, sizeof(buf), fp);
2688             break;
2689
2690             /*
2691              * We've passed the message header,
2692              * so message is just text.
2693              */
2694         default:
2695             return 0;
2696         }
2697 }
2698
2699
2700 static struct swit sortswit[] = {
2701 #define SODATE               0
2702     { "datefield field", 0 },
2703 #define SOSUBJ               1
2704     { "textfield field", 0 },
2705 #define SONSUBJ              2
2706     { "notextfield", 0 },
2707 #define SOLIMT               3
2708     { "limit days", 0 },
2709 #define SONLIMT              4
2710     { "nolimit", 0 },
2711 #define SOVERB               5
2712     { "verbose", 0 },
2713 #define SONVERB              6
2714     { "noverbose", 0 },
2715 #define SOHELP               7
2716     { "help", 4 },
2717     { NULL, 0 }
2718 };
2719
2720
2721 void
2722 sortcmd (char **args)
2723 {
2724     int msgp = 0, msgnum;
2725     char *cp, *datesw = NULL, *subjsw = NULL;
2726     char buf[BUFSIZ], *msgs[MAXARGS];
2727     struct tws tb;
2728
2729     if (fmsh) {
2730         forkcmd (args, cmd_name);
2731         return;
2732     }
2733
2734     while ((cp = *args++)) {
2735         if (*cp == '-')
2736             switch (smatch (++cp, sortswit)) {
2737                 case AMBIGSW: 
2738                     ambigsw (cp, sortswit);
2739                     return;
2740                 case UNKWNSW: 
2741                     fprintf (stderr, "-%s unknown\n", cp);
2742                     return;
2743                 case SOHELP: 
2744                     snprintf (buf, sizeof(buf), "%s [msgs] [switches]", cmd_name);
2745                     print_help (buf, sortswit, 1);
2746                     return;
2747
2748                 case SODATE: 
2749                     if (datesw) {
2750                         advise (NULL, "only one date field at a time!");
2751                         return;
2752                     }
2753                     if (!(datesw = *args++) || *datesw == '-') {
2754                         advise (NULL, "missing argument to %s", args[-2]);
2755                         return;
2756                     }
2757                     continue;
2758
2759                 case SOSUBJ:
2760                     if (subjsw) {
2761                         advise (NULL, "only one text field at a time!");
2762                         return;
2763                     }
2764                     if (!(subjsw = *args++) || *subjsw == '-') {
2765                         advise (NULL, "missing argument to %s", args[-2]);
2766                         return;
2767                     }
2768                     continue;
2769                 case SONSUBJ:
2770                     subjsw = (char *)0;
2771                     continue;
2772
2773                 case SOLIMT:            /* too hard */
2774                     if (!(cp = *args++) || *cp == '-') {
2775                         advise (NULL, "missing argument to %s", args[-2]);
2776                         return;
2777                     }
2778                 case SONLIMT:
2779                 case SOVERB:            /* not implemented */
2780                 case SONVERB: 
2781                     continue;
2782             }
2783         if (*cp == '+' || *cp == '@') {
2784             advise (NULL, "sorry, no folders allowed!");
2785             return;
2786         }
2787         else
2788             msgs[msgp++] = cp;
2789     }
2790
2791     if (!msgp)
2792         msgs[msgp++] = "all";
2793     if (!datesw)
2794         datesw = "Date";
2795     for (msgnum = 0; msgnum < msgp; msgnum++)
2796         if (!m_convert (mp, msgs[msgnum]))
2797             return;
2798     seq_setprev (mp);
2799
2800     twscopy (&tb, dlocaltimenow ());
2801
2802     for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
2803         if (Msgs[msgnum].m_scanl) {
2804             free (Msgs[msgnum].m_scanl);
2805             Msgs[msgnum].m_scanl = NULL;
2806         }
2807         if (is_selected (mp, msgnum)) {
2808             if (get_fields (datesw, subjsw, msgnum, &Msgs[msgnum]))
2809                 twscopy (&Msgs[msgnum].m_tb,
2810                         msgnum != mp->lowsel ? &Msgs[msgnum - 1].m_tb : &tb);
2811         }
2812         else                    /* m_scaln is already NULL */
2813             twscopy (&Msgs[msgnum].m_tb, &tb);
2814         Msgs[msgnum].m_stats = mp->msgstats[msgnum - mp->lowoff];
2815         if (mp->curmsg == msgnum)
2816             Msgs[msgnum].m_stats |= CUR;
2817     }
2818
2819     qsort ((char *) &Msgs[mp->lowsel], mp->hghsel - mp->lowsel + 1,
2820            sizeof(struct Msg), (qsort_comp) (subjsw ? subsort : msgsort));
2821
2822     for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
2823         if (subjsw && Msgs[msgnum].m_scanl) {
2824             free (Msgs[msgnum].m_scanl);        /* from subjsort */
2825             Msgs[msgnum].m_scanl = NULL;
2826         }
2827         mp->msgstats[msgnum - mp->lowoff] = Msgs[msgnum].m_stats & ~CUR;
2828         if (Msgs[msgnum].m_stats & CUR)
2829             seq_setcur (mp, msgnum);
2830     }
2831             
2832     mp->msgflags |= MODIFIED;
2833     modified++;
2834 }
2835
2836
2837 /* 
2838  * get_fields - parse message, and get date and subject if needed.
2839  * We'll use the msgp->m_tb tws struct for the date, and overload
2840  * the msgp->m_scanl field with our subject string.
2841  */
2842 static int
2843 get_fields (char *datesw, char *subjsw, int msgnum, struct Msg *msgp)
2844 {
2845     int state, gotdate = 0;
2846     char *bp, buf[BUFSIZ], name[NAMESZ];
2847     struct tws *tw = (struct tws *) 0;
2848     register FILE *zp;
2849
2850     zp = msh_ready (msgnum, 0);
2851     for (state = FLD;;) {
2852         switch (state = m_getfld (state, name, buf, sizeof buf, zp)) {
2853             case FLD: 
2854             case FLDEOF: 
2855             case FLDPLUS: 
2856                 if (!strcasecmp (name, datesw)) {
2857                     bp = getcpy (buf);
2858                     while (state == FLDPLUS) {
2859                         state = m_getfld (state, name, buf, sizeof buf, zp);
2860                         bp = add (buf, bp);
2861                     }
2862                     if ((tw = dparsetime (bp)) == NULL)
2863                         admonish (NULL,
2864                                 "unable to parse %s field in message %d",
2865                                 datesw, msgnum);
2866                     else
2867                         twscopy (&(msgp->m_tb), tw);
2868                     free (bp);
2869                     if (!subjsw)        /* not using this, or already done */
2870                         break;          /* all done! */
2871                     gotdate++;
2872                 }
2873                 else if (subjsw && !strcasecmp(name, subjsw)) {
2874                     bp = getcpy (buf);
2875                     while (state == FLDPLUS) {
2876                         state = m_getfld (state, name, buf, sizeof buf, zp);
2877                         bp = add (buf, bp);
2878                     }
2879                     msgp->m_scanl = sosmash(subjsw, bp);
2880                     if (gotdate)
2881                         break;          /* date done so we're done */
2882                     else
2883                         subjsw = (char *)0;/* subject done, need date */
2884                 } else {
2885                     while (state == FLDPLUS)    /* flush this one */
2886                         state = m_getfld (state, name, buf, sizeof buf, zp);
2887                 }
2888                 continue;
2889
2890             case BODY: 
2891             case BODYEOF: 
2892             case FILEEOF: 
2893                 break;
2894
2895             case LENERR: 
2896             case FMTERR: 
2897                 admonish (NULL, "format error in message %d", msgnum);
2898                 if (msgp->m_scanl) {    /* this might need free'd */
2899                     free (msgp->m_scanl); /* probably can't use subj anyway */
2900                     msgp->m_scanl = NULL;
2901                 }
2902                 return NOTOK;
2903
2904             default: 
2905                 adios (NULL, "internal error -- you lose");
2906         }
2907         break;
2908     }
2909     if (tw)
2910         return OK;      /* not an error if subj not found */
2911
2912     admonish (NULL, "no %s field in message %d", datesw, msgnum);
2913     return NOTOK;       /* NOTOK means use some other date */
2914 }
2915
2916
2917 /*
2918  * sort routines
2919  */
2920
2921 static int
2922 msgsort (struct Msg *a, struct Msg *b)
2923 {
2924     return twsort (&a->m_tb, &b->m_tb);
2925 }
2926
2927
2928 static int
2929 subsort (struct Msg *a, struct Msg *b)
2930 {
2931         register int i;
2932
2933         if (a->m_scanl && b->m_scanl)
2934             if ((i = strcmp (a->m_scanl, b->m_scanl)))
2935                 return (i);
2936
2937         return twsort (&a->m_tb, &b->m_tb);
2938 }
2939
2940
2941 /*
2942  * try to make the subject "canonical": delete leading "re:", everything
2943  * but letters & smash letters to lower case. 
2944  */
2945 static char *
2946 sosmash (char *subj, char *s)
2947 {
2948     register char *cp, *dp, c;
2949
2950     if (s) {
2951         cp = s;
2952         dp = s; /* dst pointer */
2953         if (!strcasecmp (subj, "subject"))
2954             while ((c = *cp)) {
2955                 if (! isspace(c)) {
2956                     if(uprf(cp, "re:"))
2957                         cp += 2;
2958                     else {
2959                         if (isalnum(c))
2960                             *dp++ = isupper(c) ? tolower(c) : c;
2961                         break;
2962                     }
2963                 }
2964                 cp++;
2965             }
2966         while ((c = *cp++)) {
2967             if (isalnum(c))
2968                 *dp++ = isupper(c) ? tolower(c) : c;
2969
2970         }
2971         *dp = '\0';
2972     }
2973     return s;
2974 }
2975
2976
2977 static int
2978 process (int msgnum, char *proc, int vecp, char **vec)
2979 {
2980     int child_id, status;
2981     char tmpfil[80];
2982     FILE *out;
2983
2984     if (fmsh) {
2985         strncpy (tmpfil, m_name (msgnum), sizeof(tmpfil));
2986         context_del (pfolder);
2987         context_replace (pfolder, fmsh);/* update current folder   */
2988         seq_save (mp);
2989         context_save ();                /* save the context file   */
2990         goto ready;
2991     }
2992
2993     strncpy (tmpfil, m_scratch ("", invo_name), sizeof(tmpfil));
2994     if ((out = fopen (tmpfil, "w")) == NULL) {
2995         int olderr;
2996         extern int errno;
2997         char newfil[80];
2998
2999         olderr = errno;
3000         strncpy (newfil, m_tmpfil (invo_name), sizeof(newfil));
3001         if ((out = fopen (newfil, "w")) == NULL) {
3002             errno = olderr;
3003             advise (tmpfil, "unable to create temporary file");
3004             return NOTOK;
3005         } else {
3006             strncpy (tmpfil, newfil, sizeof(tmpfil));
3007         }
3008     }
3009     copy_message (msgnum, out);
3010     fclose (out);
3011
3012 ready: ;
3013     fflush (stdout);
3014     switch (child_id = fork ()) {
3015         case NOTOK: 
3016             advise ("fork", "unable to");
3017             status = NOTOK;
3018             break;
3019             
3020         case OK: 
3021             closefds (3);
3022             SIGNAL (SIGINT, istat);
3023             SIGNAL (SIGQUIT, qstat);
3024
3025             vec[vecp++] = tmpfil;
3026             vec[vecp] = NULL;
3027
3028             execvp (proc, vec);
3029             fprintf (stderr, "unable to exec ");
3030             perror (proc);
3031             _exit (1);
3032
3033         default: 
3034             status = pidXwait (child_id, NULL);
3035             break;
3036     }
3037
3038     if (!fmsh)
3039         unlink (tmpfil);
3040     return status;
3041 }
3042
3043
3044 static void
3045 copy_message (int msgnum, FILE *out)
3046 {
3047     long pos;
3048     static char buffer[BUFSIZ];
3049     register FILE *zp;
3050
3051     zp = msh_ready (msgnum, 1);
3052     if (fmsh) {
3053         while (fgets (buffer, sizeof buffer, zp) != NULL) {
3054             fputs (buffer, out);
3055             if (interrupted && out == stdout)
3056                 break;
3057         }
3058     }
3059     else {
3060         pos = ftell (zp);
3061         while (fgets (buffer, sizeof buffer, zp) != NULL
3062                 && pos < Msgs[msgnum].m_stop) {
3063             fputs (buffer, out);
3064             pos += (long) strlen (buffer);
3065             if (interrupted && out == stdout)
3066                 break;
3067         }
3068     }
3069 }
3070
3071
3072 static void
3073 copy_digest (int msgnum, FILE *out)
3074 {
3075     char c;
3076     long pos;
3077     static char buffer[BUFSIZ];
3078     register FILE *zp;
3079
3080     c = '\n';
3081     zp = msh_ready (msgnum, 1);
3082     if (!fmsh)
3083         pos = ftell (zp);
3084     while (fgets (buffer, sizeof buffer, zp) != NULL
3085             && !fmsh && pos < Msgs[msgnum].m_stop) {
3086         if (c == '\n' && *buffer == '-')
3087             fputc (' ', out);
3088         fputs (buffer, out);
3089         c = buffer[strlen (buffer) - 1];
3090         if (!fmsh)
3091             pos += (long) strlen (buffer);
3092         if (interrupted && out == stdout)
3093             break;
3094     }
3095 }