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