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