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