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