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