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