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