* (mh_strcasecmp): Rename the private strcasecmp function to mh_strcasecmp.
[mmh] / uip / mshcmds.c
1
2 /*
3  * mshcmds.c -- command handlers in msh
4  *
5  * $Id$
6  *
7  * This code is Copyright (c) 2002, by the authors of nmh.  See the
8  * COPYRIGHT file in the root directory of the nmh distribution for
9  * complete copyright information.
10  */
11
12 #include <h/mh.h>
13 #include <h/signals.h>
14 #include <h/dropsbr.h>
15 #include <h/fmt_scan.h>
16 #include <h/scansbr.h>
17 #include <h/tws.h>
18 #include <h/mts.h>
19 #include <errno.h>
20 #include <setjmp.h>
21 #include <signal.h>
22 #include <h/msh.h>
23 #include <h/picksbr.h>
24 #include <h/utils.h>
25
26
27 static char delim3[] = "-------";       /* from burst.c */
28
29 static int mhlnum;
30 static FILE *mhlfp;
31
32 #if defined(NNTP) && defined(MPOP)
33 # undef MPOP
34 #endif
35
36 #ifdef MPOP
37 # ifdef BPOP
38 extern int pmsh;
39 extern char response[];
40 # endif
41 #endif /* MPOP */
42
43 /*
44  * Type for a compare function for qsort.  This keeps
45  * the compiler happy.
46  */
47 typedef int (*qsort_comp) (const void *, const void *);
48
49 /*
50  * prototypes
51  */
52 void clear_screen (void);   /* from termsbr.c */
53 int SOprintf (char *, ...); /* from termsbr.c */
54 int sc_width (void);        /* from termsbr.c */
55
56 /*
57  * static prototypes
58  */
59 static int burst (struct Msg *, int, int, int, int);
60 static void forw (char *, char *, int, char **);
61 static void rmm (void);
62 static void show (int);
63 static int eom_action (int);
64 static FILE *mhl_action (char *);
65 static int ask (int);
66 static int is_nontext (int);
67 static int get_fields (char *, char *, int, struct Msg *);
68 static int msgsort (struct Msg *, struct Msg *);
69 static int subsort (struct Msg *, struct Msg *);
70 static char *sosmash (char *, char *);
71 static int process (int, char *, int, char **);
72 static void copy_message (int, FILE *);
73 static void copy_digest (int, FILE *);
74
75 /* from mhlsbr.c */
76 int mhlsbr (int, char **, FILE *(*)());
77
78 void
79 forkcmd (char **args, char *pgm)
80 {
81     int child_id;
82     char *vec[MAXARGS];
83
84     vec[0] = r1bindex (pgm, '/');
85     copyip (args, vec + 1, MAXARGS - 1);
86
87     if (fmsh) {
88         context_del (pfolder);
89         context_replace (pfolder, fmsh);/* update current folder   */
90         seq_save (mp);
91         context_save ();                /* save the context file   */
92     }
93     fflush (stdout);
94     switch (child_id = fork ()) {
95         case NOTOK: 
96             advise ("fork", "unable to");
97             return;
98
99         case OK: 
100             closefds (3);
101             SIGNAL (SIGINT, istat);
102             SIGNAL (SIGQUIT, qstat);
103
104             execvp (pgm, vec);
105             fprintf (stderr, "unable to exec ");
106             perror (cmd_name);
107             _exit (1);
108
109         default: 
110             pidXwait (child_id, NULL);
111             break;
112     }
113     if (fmsh) {                 /* assume the worst case */
114         mp->msgflags |= MODIFIED;
115         modified++;
116     }
117 }
118
119
120 static struct swit distswit[] = {
121 #define DIANSW                    0
122     { "annotate", 0 },
123 #define DINANSW                   1
124     { "noannotate", 0 },
125 #define DIDFSW                    2
126     { "draftfolder +folder", 0 },
127 #define DIDMSW                    3
128     { "draftmessage msg", 0 },
129 #define DINDFSW                   4
130     { "nodraftfolder", 0 },
131 #define DIEDTSW                   5
132     { "editor editor", 0 },
133 #define DINEDSW                   6
134     { "noedit", 0 },
135 #define DIFRMSW                   7
136     { "form formfile", 0 },
137 #define DIINSW                    8
138     { "inplace", 0 },
139 #define DININSW                   9
140     { "noinplace", 0 },
141 #define DIWHTSW                  10
142     { "whatnowproc program", 0 },
143 #define DINWTSW                  11
144     { "nowhatnowproc", 0 },
145 #define DIHELP                   12
146     { "help", 0 },
147     { NULL, 0 }
148 };
149
150
151 void
152 distcmd (char **args)
153 {
154     int vecp = 1;
155     char *cp, *msg = NULL;
156     char buf[BUFSIZ], *vec[MAXARGS];
157
158     if (fmsh) {
159         forkcmd (args, cmd_name);
160         return;
161     }
162
163     while ((cp = *args++)) {
164         if (*cp == '-')
165             switch (smatch (++cp, distswit)) {
166                 case AMBIGSW: 
167                     ambigsw (cp, distswit);
168                     return;
169                 case UNKWNSW: 
170                     fprintf (stderr, "-%s unknown\n", cp);
171                     return;
172                 case DIHELP: 
173                     snprintf (buf, sizeof(buf), "%s [msgs] [switches]", cmd_name);
174                     print_help (buf, distswit, 1);
175                     return;
176
177                 case DIANSW:    /* not implemented */
178                 case DINANSW: 
179                 case DIINSW: 
180                 case DININSW: 
181                     continue;
182
183                 case DINDFSW:
184                 case DINEDSW:
185                 case DINWTSW:
186                     vec[vecp++] = --cp;
187                     continue;
188
189                 case DIEDTSW: 
190                 case DIFRMSW: 
191                 case DIDFSW:
192                 case DIDMSW:
193                 case DIWHTSW:
194                     vec[vecp++] = --cp;
195                     if (!(cp = *args++) || *cp == '-') {
196                         advise (NULL, "missing argument to %s", args[-2]);
197                         return;
198                     }
199                     vec[vecp++] = cp;
200                     continue;
201             }
202         if (*cp == '+' || *cp == '@') {
203             advise (NULL, "sorry, no folders allowed!");
204             return;
205         }
206         else
207             if (msg) {
208                 advise (NULL, "only one message at a time!");
209                 return;
210             }
211             else
212                 msg = cp;
213     }
214
215     vec[0] = cmd_name;
216     vec[vecp++] = "-file";
217     vec[vecp] = NULL;
218     if (!msg)
219         msg = "cur";
220     if (!m_convert (mp, msg))
221         return;
222     seq_setprev (mp);
223
224     if (mp->numsel > 1) {
225         advise (NULL, "only one message at a time!");
226         return;
227     }
228     process (mp->hghsel, cmd_name, vecp, vec);
229     seq_setcur (mp, mp->hghsel);
230 }
231
232
233 static struct swit explswit[] = {
234 #define EXINSW         0
235     { "inplace", 0 },
236 #define EXNINSW        1
237     { "noinplace", 0 },
238 #define EXQISW         2
239     { "quiet", 0 },
240 #define EXNQISW        3
241     { "noquiet", 0 },
242 #define EXVBSW         4
243     { "verbose", 0 },
244 #define EXNVBSW        5
245     { "noverbose", 0 },
246 #define EXHELP         6
247     { "help", 0 },
248     { NULL, 0 }
249 };
250
251
252 void
253 explcmd (char **args)
254 {
255     int inplace = 0, quietsw = 0, verbosw = 0;
256     int msgp = 0, hi, msgnum;
257     char *cp, buf[BUFSIZ], *msgs[MAXARGS];
258     struct Msg *smsgs;
259
260     if (fmsh) {
261         forkcmd (args, cmd_name);
262         return;
263     }
264
265     while ((cp = *args++)) {
266         if (*cp == '-')
267             switch (smatch (++cp, explswit)) {
268                 case AMBIGSW: 
269                     ambigsw (cp, explswit);
270                     return;
271                 case UNKWNSW: 
272                     fprintf (stderr, "-%s unknown\n", cp);
273                     return;
274                 case EXHELP: 
275                     snprintf (buf, sizeof(buf), "%s [msgs] [switches]", cmd_name);
276                     print_help (buf, explswit, 1);
277                     return;
278
279                 case EXINSW: 
280                     inplace++;
281                     continue;
282                 case EXNINSW: 
283                     inplace = 0;
284                     continue;
285                 case EXQISW: 
286                     quietsw++;
287                     continue;
288                 case EXNQISW: 
289                     quietsw = 0;
290                     continue;
291                 case EXVBSW: 
292                     verbosw++;
293                     continue;
294                 case EXNVBSW: 
295                     verbosw = 0;
296                     continue;
297             }
298         if (*cp == '+' || *cp == '@') {
299             advise (NULL, "sorry, no folders allowed!");
300             return;
301         }
302         else
303             msgs[msgp++] = cp;
304     }
305
306     if (!msgp)
307         msgs[msgp++] = "cur";
308     for (msgnum = 0; msgnum < msgp; msgnum++)
309         if (!m_convert (mp, msgs[msgnum]))
310             return;
311     seq_setprev (mp);
312
313     smsgs = (struct Msg *)
314                 calloc ((size_t) (MAXFOLDER + 2), sizeof *smsgs);
315     if (smsgs == NULL)
316         adios (NULL, "unable to allocate folder storage");
317
318     hi = mp->hghmsg + 1;
319     interrupted = 0;
320     for (msgnum = mp->lowsel;
321             msgnum <= mp->hghsel && !interrupted;
322             msgnum++)
323         if (is_selected (mp, msgnum))
324             if (burst (smsgs, msgnum, inplace, quietsw, verbosw) != OK)
325                 break;
326
327     free ((char *) smsgs);
328
329     if (inplace)
330         seq_setcur (mp, mp->lowsel);
331     else
332         if (hi <= mp->hghmsg)
333             seq_setcur (mp, hi);
334
335     mp->msgflags |= MODIFIED;
336     modified++;
337 }
338
339
340 static int
341 burst (struct Msg *smsgs, int msgnum, int inplace, int quietsw, int verbosw)
342 {
343     int i, j, ld3, wasdlm, msgp;
344     long pos;
345     char c, buffer[BUFSIZ];
346     register FILE *zp;
347
348     ld3 = strlen (delim3);
349
350     if (Msgs[msgnum].m_scanl) {
351         free (Msgs[msgnum].m_scanl);
352         Msgs[msgnum].m_scanl = NULL;
353     }
354
355     pos = ftell (zp = msh_ready (msgnum, 1));
356     for (msgp = 0; msgp <= MAXFOLDER;) {
357         while (fgets (buffer, sizeof buffer, zp) != NULL
358                 && buffer[0] == '\n'
359                 && pos < Msgs[msgnum].m_stop)
360             pos += (long) strlen (buffer);
361         if (feof (zp) || pos >= Msgs[msgnum].m_stop)
362             break;
363         fseek (zp, pos, SEEK_SET);
364         smsgs[msgp].m_start = pos;
365
366         for (c = 0;
367                 pos < Msgs[msgnum].m_stop
368                 && fgets (buffer, sizeof buffer, zp) != NULL;
369                 c = buffer[0])
370             if (strncmp (buffer, delim3, ld3) == 0
371                     && (msgp == 1 || c == '\n')
372                     && peekc (zp) == '\n')
373                 break;
374             else
375                 pos += (long) strlen (buffer);
376
377         wasdlm = strncmp (buffer, delim3, ld3) == 0;
378         if (smsgs[msgp].m_start != pos)
379             smsgs[msgp++].m_stop = (c == '\n' && wasdlm) ? pos - 1 : pos;
380         if (feof (zp) || pos >= Msgs[msgnum].m_stop) {
381             if (wasdlm)
382                 smsgs[msgp - 1].m_stop -= ((long) strlen (buffer) + 1);
383             break;
384         }
385         pos += (long) strlen (buffer);
386     }
387
388     switch (msgp--) {           /* toss "End of XXX Digest" */
389         case 0: 
390             adios (NULL, "burst() botch -- you lose big");
391
392         case 1: 
393             if (!quietsw)
394                 printf ("message %d not in digest format\n", msgnum);
395             return OK;
396
397         default: 
398             if (verbosw)
399                 printf ("%d message%s exploded from digest %d\n",
400                         msgp, msgp != 1 ? "s" : "", msgnum);
401             break;
402     }
403
404     if ((i = msgp + mp->hghmsg) > MAXFOLDER) {
405         advise (NULL, "more than %d messages", MAXFOLDER);
406         return NOTOK;
407     }
408     if (!(mp = folder_realloc (mp, mp->lowoff, i)))
409         adios (NULL, "unable to allocate folder storage");
410
411     j = mp->hghmsg;
412     mp->hghmsg += msgp;
413     mp->nummsg += msgp;
414     if (mp->hghsel > msgnum)
415         mp->hghsel += msgp;
416
417     if (inplace)
418         for (i = mp->hghmsg; j > msgnum; i--, j--) {
419             if (verbosw)
420                 printf ("message %d becomes message %d\n", j, i);
421
422             Msgs[i].m_bboard_id = Msgs[j].m_bboard_id;
423             Msgs[i].m_top = Msgs[j].m_top;
424             Msgs[i].m_start = Msgs[j].m_start;
425             Msgs[i].m_stop = Msgs[j].m_stop;
426             Msgs[i].m_scanl = NULL;
427             if (Msgs[j].m_scanl) {
428                 free (Msgs[j].m_scanl);
429                 Msgs[j].m_scanl = NULL;
430             }
431             copy_msg_flags (mp, i, j);
432         }
433
434     if (Msgs[msgnum].m_bboard_id == 0)
435         readid (msgnum);
436
437     unset_selected (mp, msgnum);
438     i = inplace ? msgnum + msgp : mp->hghmsg;
439     for (j = msgp; j >= (inplace ? 0 : 1); i--, j--) {
440         if (verbosw && i != msgnum)
441             printf ("message %d of digest %d becomes message %d\n",
442                     j, msgnum, i);
443
444         Msgs[i].m_bboard_id = Msgs[msgnum].m_bboard_id;
445         Msgs[i].m_top = Msgs[j].m_top;
446         Msgs[i].m_start = smsgs[j].m_start;
447         Msgs[i].m_stop = smsgs[j].m_stop;
448         Msgs[i].m_scanl = NULL;
449         copy_msg_flags (mp, i, msgnum);
450     }
451
452     return OK;
453 }
454
455
456 static struct swit fileswit[] = {
457 #define FIDRFT               0
458     { "draft", 0 },
459 #define FILINK               1
460     { "link", 0 },
461 #define FINLINK              2
462     { "nolink", 0 },
463 #define FIPRES               3
464     { "preserve", 0 },
465 #define FINPRES              4
466     { "nopreserve", 0 },
467 #define FISRC                5
468     { "src +folder", 0 },
469 #define FIFILE               6
470     { "file file", 0 },
471 #define FIPROC               7
472     { "rmmproc program", 0 },
473 #define FINPRC               8
474     { "normmproc", 0 },
475 #define FIHELP               9
476     { "help", 0 },
477     { NULL, 0 }
478 };
479
480
481 void
482 filecmd (char **args)
483 {
484     int linksw = 0, msgp = 0;
485     int vecp = 1, i, msgnum;
486     char *cp, buf[BUFSIZ];
487     char *msgs[MAXARGS], *vec[MAXARGS];
488
489     if (fmsh) {
490         forkcmd (args, cmd_name);
491         return;
492     }
493
494     while ((cp = *args++)) {
495         if (*cp == '-')
496             switch (i = smatch (++cp, fileswit)) {
497                 case AMBIGSW: 
498                     ambigsw (cp, fileswit);
499                     return;
500                 case UNKWNSW: 
501                     fprintf (stderr, "-%s unknown\n", cp);
502                     return;
503                 case FIHELP: 
504                     snprintf (buf, sizeof(buf), "%s +folder... [msgs] [switches]", cmd_name);
505                     print_help (buf, fileswit, 1);
506                     return;
507
508                 case FILINK:
509                     linksw++;
510                     continue;
511                 case FINLINK: 
512                     linksw = 0;
513                     continue;
514
515                 case FIPRES: 
516                 case FINPRES: 
517                     continue;
518
519                 case FISRC: 
520                 case FIDRFT:
521                 case FIFILE: 
522                 case FIPROC:
523                 case FINPRC:
524                     advise (NULL, "sorry, -%s not allowed!", fileswit[i].sw);
525                     return;
526             }
527         if (*cp == '+' || *cp == '@')
528             vec[vecp++] = cp;
529         else
530             msgs[msgp++] = cp;
531     }
532
533     vec[0] = cmd_name;
534     vec[vecp++] = "-file";
535     vec[vecp] = NULL;
536     if (!msgp)
537         msgs[msgp++] = "cur";
538     for (msgnum = 0; msgnum < msgp; msgnum++)
539         if (!m_convert (mp, msgs[msgnum]))
540             return;
541     seq_setprev (mp);
542
543     interrupted = 0;
544     for (msgnum = mp->lowsel;
545             msgnum <= mp->hghsel && !interrupted;
546             msgnum++)
547         if (is_selected (mp, msgnum))
548             if (process (msgnum, fileproc, vecp, vec)) {
549                 unset_selected (mp, msgnum);
550                 mp->numsel--;
551             }
552
553     if (mp->numsel != mp->nummsg || linksw)
554         seq_setcur (mp, mp->hghsel);
555     if (!linksw)
556         rmm ();
557 }
558
559
560 int
561 filehak (char **args)
562 {
563     int result, vecp = 0;
564     char *cp, *cwd, *vec[MAXARGS];
565
566     while ((cp = *args++)) {
567         if (*cp == '-')
568             switch (smatch (++cp, fileswit)) {
569                 case AMBIGSW: 
570                 case UNKWNSW: 
571                 case FIHELP: 
572                     return NOTOK;
573
574                 case FILINK:
575                 case FINLINK: 
576                 case FIPRES: 
577                 case FINPRES: 
578                     continue;
579
580                 case FISRC: 
581                 case FIDRFT:
582                 case FIFILE: 
583                     return NOTOK;
584             }
585         if (*cp == '+' || *cp == '@')
586             vec[vecp++] = cp;
587     }
588     vec[vecp] = NULL;
589
590     result = NOTOK;
591     cwd = NULL;
592     for (vecp = 0; (cp = vec[vecp]) && result == NOTOK; vecp++) {
593         if (cwd == NULL)
594             cwd = getcpy (pwd ());
595         chdir (m_maildir (""));
596         cp = 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             ep = mh_xmalloc ((unsigned) i);
2165             for (dp = nfs, fp = ep; *dp; dp++) {
2166                 if (*dp == '\n') {
2167                     *fp++ = '\\', *fp++ = 'n';
2168                     continue;
2169                 }
2170                 if (*dp == '"' || *dp == '\\')
2171                     *fp++ = '\\';
2172                 *fp++ = *dp;
2173             }
2174             *fp = NULL;
2175
2176             if (pop_command ("XTND SCAN %d \"%s\"", width, ep) == OK)
2177                 p_optim = 1;
2178
2179             free (ep);
2180         }
2181 #endif
2182 #endif  /* MPOP */
2183     }
2184     else
2185         optim = equiv (s_form, form) && equiv (s_format, format);
2186
2187 #ifdef  MPOP
2188 #ifdef  BPOP
2189     if (p_optim && optim) {
2190         for (msgnum = mp->lowmsg; msgnum <= mp->hghmsg; msgnum++)
2191             if (!is_selected(mp, msgnum) || Msgs[msgnum].m_scanl)
2192                 break;
2193         if (msgnum > mp->hghmsg && pop_command ("LIST") == OK) {
2194             fprintf (stderr, "Stand-by...");
2195             fflush (stderr);
2196
2197             for (;;) {
2198                 int     size;
2199
2200                 switch (pop_multiline ()) {
2201                     case NOTOK:
2202                         fprintf (stderr, "%s", response);
2203                         /* and fall... */
2204                     case DONE:
2205                         fprintf (stderr,"\n");
2206                         break;
2207
2208                     case OK:
2209                         if (sscanf (response, "%d %d", &msgnum, &size) == 2
2210                                 && mp->lowmsg <= msgnum
2211                                 && msgnum <= mp->hghmsg
2212                                 && (cp = strchr(response, '#'))
2213                                 && *++cp)
2214                             Msgs[msgnum].m_scanl = concat (cp, "\n", NULL);
2215                         continue;
2216                 }
2217                 break;
2218             }
2219         }
2220     }
2221 #endif
2222 #endif /* MPOP */
2223
2224     interrupted = 0;
2225     for (msgnum = mp->lowsel;
2226             msgnum <= mp->hghsel && !interrupted;
2227             msgnum++)
2228         if (is_selected (mp, msgnum)) {
2229             if (optim && Msgs[msgnum].m_scanl)
2230                 printf ("%s", Msgs[msgnum].m_scanl);
2231             else {
2232 #ifdef  MPOP
2233 #ifdef  BPOP
2234                 if (p_optim
2235                         && optim
2236                         && is_virtual (mp, msgnum)
2237                         && pop_command ("LIST %d", msgnum) == OK
2238                         && (cp = strchr(response, '#'))
2239                         && *++cp) {
2240                     Msgs[msgnum].m_scanl = concat (cp, "\n", NULL);
2241                     printf ("%s", Msgs[msgnum].m_scanl);                    
2242                     continue;
2243                 }
2244 #endif
2245 #endif /* MPOP */
2246
2247                 zp = msh_ready (msgnum, 0);
2248                 switch (state = scan (zp, msgnum, 0, nfs, width,
2249                         msgnum == mp->curmsg,
2250                         is_unseen (mp, msgnum),
2251                         headersw ? (fmsh ? fmsh : mp->foldpath) : NULL,
2252                         fmsh ? 0L : (long) (Msgs[msgnum].m_stop - Msgs[msgnum].m_start),
2253                         1)) {
2254                     case SCNMSG:
2255                     case SCNENC:
2256                     case SCNERR:
2257                         if (optim)
2258                             Msgs[msgnum].m_scanl = getcpy (scanl);
2259                         break;
2260
2261                     default:
2262                         advise (NULL, "scan() botch (%d)", state);
2263                         return;
2264
2265                     case SCNEOF:
2266                         printf ("%*d  empty\n", DMAXFOLDER, msgnum);
2267                         break;
2268                     }
2269             }
2270             headersw = 0;
2271         }
2272
2273     if (clearsw)
2274         clear_screen ();
2275 }
2276
2277
2278 static struct swit showswit[] = {
2279 #define SHDRAFT               0
2280     { "draft", 5 },
2281 #define SHFORM                1
2282     { "form formfile", 4 },
2283 #define SHPROG                2
2284     { "moreproc program", 4 },
2285 #define SHNPROG               3
2286     { "nomoreproc", 3 },
2287 #define SHLEN                 4
2288     { "length lines", 4 },
2289 #define SHWID                 5
2290     { "width columns", 4 },
2291 #define SHSHOW                6
2292     { "showproc program", 4 },
2293 #define SHNSHOW               7
2294     { "noshowproc", 3 },
2295 #define SHHEAD                8
2296     { "header", 4 },
2297 #define SHNHEAD               9
2298     { "noheader", 3 },
2299 #define SHHELP               10
2300     { "help", 0 },
2301     { NULL, 0 }
2302 };
2303
2304
2305 void
2306 showcmd (char **args)
2307 {
2308     int headersw = 1, nshow = 0, msgp = 0, vecp = 1;
2309     int mhl = 0, seqnum = -1, mode = 0, i, msgnum;
2310     char *cp, *proc = showproc, buf[BUFSIZ];
2311     char *msgs[MAXARGS], *vec[MAXARGS];
2312
2313     if (!mh_strcasecmp (cmd_name, "next"))
2314         mode = 1;
2315     else
2316         if (!mh_strcasecmp (cmd_name, "prev"))
2317             mode = -1;
2318     while ((cp = *args++)) {
2319         if (*cp == '-')
2320             switch (i = smatch (++cp, showswit)) {
2321                 case AMBIGSW: 
2322                     ambigsw (cp, showswit);
2323                     return;
2324                 case UNKWNSW: 
2325                 case SHNPROG:
2326                     vec[vecp++] = --cp;
2327                     continue;
2328                 case SHHELP: 
2329                     snprintf (buf, sizeof(buf), "%s %s[switches] [switches for showproc]",
2330                             cmd_name, mode ? NULL : "[msgs] ");
2331                     print_help (buf, showswit, 1);
2332                     return;
2333
2334                 case SHFORM: 
2335                 case SHPROG:
2336                 case SHLEN:
2337                 case SHWID:
2338                     vec[vecp++] = --cp;
2339                     if (!(cp = *args++) || *cp == '-') {
2340                         advise (NULL, "missing argument to %s", args[-2]);
2341                         return;
2342                     }
2343                     vec[vecp++] = cp;
2344                     continue;
2345                 case SHHEAD: 
2346                     headersw++;
2347                     continue;
2348                 case SHNHEAD: 
2349                     headersw = 0;
2350                     continue;
2351                 case SHSHOW: 
2352                     if (!(proc = *args++) || *proc == '-') {
2353                         advise (NULL, "missing argument to %s", args[-2]);
2354                         return;
2355                     }
2356                     nshow = 0;
2357                     continue;
2358                 case SHNSHOW: 
2359                     nshow++;
2360                     continue;
2361
2362                 case SHDRAFT: 
2363                     advise (NULL, "sorry, -%s not allowed!", showswit[i].sw);
2364                     return;
2365             }
2366         if (*cp == '+' || *cp == '@') {
2367             advise (NULL, "sorry, no folders allowed!");
2368             return;
2369         }
2370         else
2371             if (mode) {
2372                 fprintf (stderr,
2373                         "usage: %s [switches] [switches for showproc]\n",
2374                         cmd_name);
2375                 return;
2376             }
2377             else
2378                 msgs[msgp++] = cp;
2379     }
2380     vec[vecp] = NULL;
2381
2382     if (!msgp)
2383         msgs[msgp++] = mode > 0 ? "next" : mode < 0 ? "prev" : "cur";
2384     for (msgnum = 0; msgnum < msgp; msgnum++)
2385         if (!m_convert (mp, msgs[msgnum]))
2386             return;
2387     seq_setprev (mp);
2388
2389     if (!nshow && !getenv ("NOMHNPROC"))
2390         for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
2391             if (is_selected (mp, msgnum) && is_nontext (msgnum)) {
2392                 proc = showmimeproc;
2393                 vec[vecp++] = "-file";
2394                 vec[vecp] = NULL;
2395                 goto finish;
2396             }
2397
2398     if (nshow)
2399         proc = catproc;
2400     else
2401         if (strcmp (showproc, "mhl") == 0) {
2402             proc = mhlproc;
2403             mhl++;
2404         }
2405
2406 finish: ;
2407     seqnum = seq_getnum (mp, "unseen");
2408     vec[0] = r1bindex (proc, '/');
2409     if (mhl) {
2410         msgp = vecp;
2411         for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
2412             if (is_selected (mp, msgnum)) {
2413                 vec[vecp++] = getcpy (m_name (msgnum));
2414                 if (seqnum != -1)
2415                     seq_delmsg (mp, "unseen", msgnum);
2416             }
2417         vec[vecp] = NULL;
2418         if (mp->numsel == 1 && headersw)
2419             show (mp->lowsel);
2420         mhlsbr (vecp, vec, mhl_action);
2421         m_eomsbr ((int (*)()) 0);
2422         while (msgp < vecp)
2423             free (vec[msgp++]);
2424     } else {
2425         interrupted = 0;
2426         for (msgnum = mp->lowsel;
2427                 msgnum <= mp->hghsel && !interrupted;
2428                 msgnum++)
2429             if (is_selected (mp, msgnum)) {
2430                 switch (ask (msgnum)) {
2431                     case NOTOK: /* QUIT */
2432                         break;
2433
2434                     case OK:    /* INTR */
2435                         continue;
2436
2437                     default:
2438                         if (mp->numsel == 1 && headersw)
2439                             show (msgnum);
2440                         if (nshow)
2441                             copy_message (msgnum, stdout);
2442                         else
2443                             process (msgnum, proc, vecp, vec);
2444
2445                         if (seqnum != -1)
2446                             seq_delmsg (mp, "unseen", msgnum);
2447                         continue;
2448                 }
2449                 break;
2450             }
2451     }
2452
2453     seq_setcur (mp, mp->hghsel);
2454 }
2455
2456
2457 static void
2458 show (int msgnum)
2459 {
2460     if (Msgs[msgnum].m_bboard_id == 0)
2461         readid (msgnum);
2462
2463     printf ("(Message %d", msgnum);
2464     if (Msgs[msgnum].m_bboard_id > 0)
2465         printf (", %s: %d", BBoard_ID, Msgs[msgnum].m_bboard_id);
2466     printf (")\n");
2467 }
2468
2469
2470
2471 static int
2472 eom_action (int c)
2473 {
2474     return (ftell (mhlfp) >= Msgs[mhlnum].m_stop);
2475 }
2476
2477
2478 static FILE *
2479 mhl_action (char *name)
2480 {
2481     int msgnum;
2482
2483     if ((msgnum = m_atoi (name)) < mp->lowmsg
2484             || msgnum > mp->hghmsg
2485             || !does_exist (mp, msgnum))
2486         return NULL;
2487     mhlnum = msgnum;
2488
2489     mhlfp = msh_ready (msgnum, 1);
2490     if (!fmsh)
2491         m_eomsbr (eom_action);
2492
2493     return mhlfp;
2494 }
2495
2496
2497
2498 static int
2499 ask (int msgnum)
2500 {
2501     char buf[BUFSIZ];
2502
2503     if (mp->numsel == 1 || !interactive || redirected)
2504         return DONE;
2505
2506     if (SOprintf ("Press <return> to list \"%d\"...", msgnum)) {
2507         if (mp->lowsel != msgnum)
2508             printf ("\n\n\n");
2509         printf ("Press <return> to list \"%d\"...", msgnum);
2510     }
2511     fflush (stdout);
2512     buf[0] = 0;
2513
2514 #ifndef BSD42
2515     read (fileno (stdout), buf, sizeof buf);
2516 #else /* BSD42 */
2517     switch (setjmp (sigenv)) {
2518         case OK: 
2519             should_intr = 1;
2520             read (fileno (stdout), buf, sizeof buf);/* fall... */
2521
2522         default: 
2523             should_intr = 0;
2524             break;
2525     }
2526 #endif /* BSD42 */
2527
2528     if (strchr(buf, '\n') == NULL)
2529         putchar ('\n');
2530
2531     if (told_to_quit) {
2532         told_to_quit = interrupted = 0;
2533         return NOTOK;
2534     }
2535     if (interrupted) {
2536         interrupted = 0;
2537         return OK;
2538     }
2539
2540     return DONE;
2541 }
2542
2543
2544 #include <h/mime.h>
2545
2546 static int
2547 is_nontext (int msgnum)
2548 {
2549     int result, state;
2550     char *bp, *cp, *dp;
2551     char buf[BUFSIZ], name[NAMESZ];
2552     FILE *fp;
2553
2554     if (Msgs[msgnum].m_flags & MHNCHK)
2555         return (Msgs[msgnum].m_flags & MHNYES);
2556     Msgs[msgnum].m_flags |= MHNCHK;
2557
2558     fp = msh_ready (msgnum, 1);
2559
2560     for (state = FLD;;)
2561         switch (state = m_getfld (state, name, buf, sizeof buf, fp)) {
2562         case FLD:
2563         case FLDPLUS:
2564         case FLDEOF:
2565             /*
2566              * Check Content-Type field
2567              */
2568             if (!mh_strcasecmp (name, TYPE_FIELD)) {
2569                 int passno;
2570                 char c;
2571
2572                 cp = add (buf, NULL);
2573                 while (state == FLDPLUS) {
2574                     state = m_getfld (state, name, buf, sizeof buf, fp);
2575                     cp = add (buf, cp);
2576                 }
2577                 bp = cp;
2578                 passno = 1;
2579
2580 again:
2581                 for (; isspace (*bp); bp++)
2582                     continue;
2583                 if (*bp == '(') {
2584                     int i;
2585
2586                     for (bp++, i = 0;;) {
2587                         switch (*bp++) {
2588                         case '\0':
2589 invalid:
2590                             result = 0;
2591                             goto out;
2592                         case '\\':
2593                             if (*bp++ == '\0')
2594                                 goto invalid;
2595                             continue;
2596                         case '(':
2597                             i++;
2598                             /* and fall... */
2599                         default:
2600                             continue;
2601                         case ')':
2602                             if (--i < 0)
2603                                 break;
2604                             continue;
2605                         }
2606                         break;
2607                     }
2608                 }
2609                 if (passno == 2) {
2610                     if (*bp != '/')
2611                         goto invalid;
2612                     bp++;
2613                     passno = 3;
2614                     goto again;
2615                 }
2616                 for (dp = bp; istoken (*dp); dp++)
2617                     continue;
2618                 c = *dp;
2619                 *dp = '\0';
2620                 if (!*bp)
2621                     goto invalid;
2622                 if (passno > 1) {
2623                     if ((result = (mh_strcasecmp (bp, "plain") != 0)))
2624                         goto out;
2625                     *dp = c;
2626                     for (dp++; isspace (*dp); dp++)
2627                         continue;
2628                     if (*dp) {
2629                         if ((result = !uprf (dp, "charset")))
2630                             goto out;
2631                         dp += sizeof "charset" - 1;
2632                         while (isspace (*dp))
2633                             dp++;
2634                         if (*dp++ != '=')
2635                             goto invalid;
2636                         while (isspace (*dp))
2637                             dp++;
2638                         if (*dp == '"') {
2639                             if ((bp = strchr(++dp, '"')))
2640                                 *bp = '\0';
2641                         } else {
2642                             for (bp = dp; *bp; bp++)
2643                                 if (isspace (*bp)) {
2644                                     *bp = '\0';
2645                                     break;
2646                                 }
2647                         }
2648                     } else {
2649                         /* Default character set */
2650                         dp = "US-ASCII";
2651                     }
2652                     /* Check the character set */
2653                     result = !check_charset (dp, strlen (dp));
2654                 } else {
2655                     if (!(result = (mh_strcasecmp (bp, "text") != 0))) {
2656                         *dp = c;
2657                         bp = dp;
2658                         passno = 2;
2659                         goto again;
2660                     }
2661                 }
2662 out:
2663                 free (cp);
2664                 if (result) {
2665                     Msgs[msgnum].m_flags |= MHNYES;
2666                     return result;
2667                 }
2668                 break;
2669             }
2670
2671             /*
2672              * Check Content-Transfer-Encoding field
2673              */
2674             if (!mh_strcasecmp (name, ENCODING_FIELD)) {
2675                 cp = add (buf, NULL);
2676                 while (state == FLDPLUS) {
2677                     state = m_getfld (state, name, buf, sizeof buf, fp);
2678                     cp = add (buf, cp);
2679                 }
2680                 for (bp = cp; isspace (*bp); bp++)
2681                     continue;
2682                 for (dp = bp; istoken (*dp); dp++)
2683                     continue;
2684                 *dp = '\0';
2685                 result = (mh_strcasecmp (bp, "7bit")
2686                        && mh_strcasecmp (bp, "8bit")
2687                        && mh_strcasecmp (bp, "binary"));
2688
2689                 free (cp);
2690                 if (result) {
2691                     Msgs[msgnum].m_flags |= MHNYES;
2692                     return result;
2693                 }
2694                 break;
2695             }
2696
2697             /*
2698              * Just skip the rest of this header
2699              * field and go to next one.
2700              */
2701             while (state == FLDPLUS)
2702                 state = m_getfld (state, name, buf, sizeof(buf), fp);
2703             break;
2704
2705             /*
2706              * We've passed the message header,
2707              * so message is just text.
2708              */
2709         default:
2710             return 0;
2711         }
2712 }
2713
2714
2715 static struct swit sortswit[] = {
2716 #define SODATE               0
2717     { "datefield field", 0 },
2718 #define SOSUBJ               1
2719     { "textfield field", 0 },
2720 #define SONSUBJ              2
2721     { "notextfield", 0 },
2722 #define SOLIMT               3
2723     { "limit days", 0 },
2724 #define SONLIMT              4
2725     { "nolimit", 0 },
2726 #define SOVERB               5
2727     { "verbose", 0 },
2728 #define SONVERB              6
2729     { "noverbose", 0 },
2730 #define SOHELP               7
2731     { "help", 0 },
2732     { NULL, 0 }
2733 };
2734
2735
2736 void
2737 sortcmd (char **args)
2738 {
2739     int msgp = 0, msgnum;
2740     char *cp, *datesw = NULL, *subjsw = NULL;
2741     char buf[BUFSIZ], *msgs[MAXARGS];
2742     struct tws tb;
2743
2744     if (fmsh) {
2745         forkcmd (args, cmd_name);
2746         return;
2747     }
2748
2749     while ((cp = *args++)) {
2750         if (*cp == '-')
2751             switch (smatch (++cp, sortswit)) {
2752                 case AMBIGSW: 
2753                     ambigsw (cp, sortswit);
2754                     return;
2755                 case UNKWNSW: 
2756                     fprintf (stderr, "-%s unknown\n", cp);
2757                     return;
2758                 case SOHELP: 
2759                     snprintf (buf, sizeof(buf), "%s [msgs] [switches]", cmd_name);
2760                     print_help (buf, sortswit, 1);
2761                     return;
2762
2763                 case SODATE: 
2764                     if (datesw) {
2765                         advise (NULL, "only one date field at a time!");
2766                         return;
2767                     }
2768                     if (!(datesw = *args++) || *datesw == '-') {
2769                         advise (NULL, "missing argument to %s", args[-2]);
2770                         return;
2771                     }
2772                     continue;
2773
2774                 case SOSUBJ:
2775                     if (subjsw) {
2776                         advise (NULL, "only one text field at a time!");
2777                         return;
2778                     }
2779                     if (!(subjsw = *args++) || *subjsw == '-') {
2780                         advise (NULL, "missing argument to %s", args[-2]);
2781                         return;
2782                     }
2783                     continue;
2784                 case SONSUBJ:
2785                     subjsw = (char *)0;
2786                     continue;
2787
2788                 case SOLIMT:            /* too hard */
2789                     if (!(cp = *args++) || *cp == '-') {
2790                         advise (NULL, "missing argument to %s", args[-2]);
2791                         return;
2792                     }
2793                 case SONLIMT:
2794                 case SOVERB:            /* not implemented */
2795                 case SONVERB: 
2796                     continue;
2797             }
2798         if (*cp == '+' || *cp == '@') {
2799             advise (NULL, "sorry, no folders allowed!");
2800             return;
2801         }
2802         else
2803             msgs[msgp++] = cp;
2804     }
2805
2806     if (!msgp)
2807         msgs[msgp++] = "all";
2808     if (!datesw)
2809         datesw = "Date";
2810     for (msgnum = 0; msgnum < msgp; msgnum++)
2811         if (!m_convert (mp, msgs[msgnum]))
2812             return;
2813     seq_setprev (mp);
2814
2815     twscopy (&tb, dlocaltimenow ());
2816
2817     for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
2818         if (Msgs[msgnum].m_scanl) {
2819             free (Msgs[msgnum].m_scanl);
2820             Msgs[msgnum].m_scanl = NULL;
2821         }
2822         if (is_selected (mp, msgnum)) {
2823             if (get_fields (datesw, subjsw, msgnum, &Msgs[msgnum]))
2824                 twscopy (&Msgs[msgnum].m_tb,
2825                         msgnum != mp->lowsel ? &Msgs[msgnum - 1].m_tb : &tb);
2826         }
2827         else                    /* m_scaln is already NULL */
2828             twscopy (&Msgs[msgnum].m_tb, &tb);
2829         Msgs[msgnum].m_stats = mp->msgstats[msgnum - mp->lowoff];
2830         if (mp->curmsg == msgnum)
2831             Msgs[msgnum].m_stats |= CUR;
2832     }
2833
2834     qsort ((char *) &Msgs[mp->lowsel], mp->hghsel - mp->lowsel + 1,
2835            sizeof(struct Msg), (qsort_comp) (subjsw ? subsort : msgsort));
2836
2837     for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
2838         if (subjsw && Msgs[msgnum].m_scanl) {
2839             free (Msgs[msgnum].m_scanl);        /* from subjsort */
2840             Msgs[msgnum].m_scanl = NULL;
2841         }
2842         mp->msgstats[msgnum - mp->lowoff] = Msgs[msgnum].m_stats & ~CUR;
2843         if (Msgs[msgnum].m_stats & CUR)
2844             seq_setcur (mp, msgnum);
2845     }
2846             
2847     mp->msgflags |= MODIFIED;
2848     modified++;
2849 }
2850
2851
2852 /* 
2853  * get_fields - parse message, and get date and subject if needed.
2854  * We'll use the msgp->m_tb tws struct for the date, and overload
2855  * the msgp->m_scanl field with our subject string.
2856  */
2857 static int
2858 get_fields (char *datesw, char *subjsw, int msgnum, struct Msg *msgp)
2859 {
2860     int state, gotdate = 0;
2861     char *bp, buf[BUFSIZ], name[NAMESZ];
2862     struct tws *tw = (struct tws *) 0;
2863     register FILE *zp;
2864
2865     zp = msh_ready (msgnum, 0);
2866     for (state = FLD;;) {
2867         switch (state = m_getfld (state, name, buf, sizeof buf, zp)) {
2868             case FLD: 
2869             case FLDEOF: 
2870             case FLDPLUS: 
2871                 if (!mh_strcasecmp (name, datesw)) {
2872                     bp = getcpy (buf);
2873                     while (state == FLDPLUS) {
2874                         state = m_getfld (state, name, buf, sizeof buf, zp);
2875                         bp = add (buf, bp);
2876                     }
2877                     if ((tw = dparsetime (bp)) == NULL)
2878                         admonish (NULL,
2879                                 "unable to parse %s field in message %d",
2880                                 datesw, msgnum);
2881                     else
2882                         twscopy (&(msgp->m_tb), tw);
2883                     free (bp);
2884                     if (!subjsw)        /* not using this, or already done */
2885                         break;          /* all done! */
2886                     gotdate++;
2887                 }
2888                 else if (subjsw && !mh_strcasecmp(name, subjsw)) {
2889                     bp = getcpy (buf);
2890                     while (state == FLDPLUS) {
2891                         state = m_getfld (state, name, buf, sizeof buf, zp);
2892                         bp = add (buf, bp);
2893                     }
2894                     msgp->m_scanl = sosmash(subjsw, bp);
2895                     if (gotdate)
2896                         break;          /* date done so we're done */
2897                     else
2898                         subjsw = (char *)0;/* subject done, need date */
2899                 } else {
2900                     while (state == FLDPLUS)    /* flush this one */
2901                         state = m_getfld (state, name, buf, sizeof buf, zp);
2902                 }
2903                 continue;
2904
2905             case BODY: 
2906             case BODYEOF: 
2907             case FILEEOF: 
2908                 break;
2909
2910             case LENERR: 
2911             case FMTERR: 
2912                 admonish (NULL, "format error in message %d", msgnum);
2913                 if (msgp->m_scanl) {    /* this might need free'd */
2914                     free (msgp->m_scanl); /* probably can't use subj anyway */
2915                     msgp->m_scanl = NULL;
2916                 }
2917                 return NOTOK;
2918
2919             default: 
2920                 adios (NULL, "internal error -- you lose");
2921         }
2922         break;
2923     }
2924     if (tw)
2925         return OK;      /* not an error if subj not found */
2926
2927     admonish (NULL, "no %s field in message %d", datesw, msgnum);
2928     return NOTOK;       /* NOTOK means use some other date */
2929 }
2930
2931
2932 /*
2933  * sort routines
2934  */
2935
2936 static int
2937 msgsort (struct Msg *a, struct Msg *b)
2938 {
2939     return twsort (&a->m_tb, &b->m_tb);
2940 }
2941
2942
2943 static int
2944 subsort (struct Msg *a, struct Msg *b)
2945 {
2946         register int i;
2947
2948         if (a->m_scanl && b->m_scanl)
2949             if ((i = strcmp (a->m_scanl, b->m_scanl)))
2950                 return (i);
2951
2952         return twsort (&a->m_tb, &b->m_tb);
2953 }
2954
2955
2956 /*
2957  * try to make the subject "canonical": delete leading "re:", everything
2958  * but letters & smash letters to lower case. 
2959  */
2960 static char *
2961 sosmash (char *subj, char *s)
2962 {
2963     register char *cp, *dp, c;
2964
2965     if (s) {
2966         cp = s;
2967         dp = s; /* dst pointer */
2968         if (!mh_strcasecmp (subj, "subject"))
2969             while ((c = *cp)) {
2970                 if (! isspace(c)) {
2971                     if(uprf(cp, "re:"))
2972                         cp += 2;
2973                     else {
2974                         if (isalnum(c))
2975                             *dp++ = isupper(c) ? tolower(c) : c;
2976                         break;
2977                     }
2978                 }
2979                 cp++;
2980             }
2981         while ((c = *cp++)) {
2982             if (isalnum(c))
2983                 *dp++ = isupper(c) ? tolower(c) : c;
2984
2985         }
2986         *dp = '\0';
2987     }
2988     return s;
2989 }
2990
2991
2992 static int
2993 process (int msgnum, char *proc, int vecp, char **vec)
2994 {
2995     int child_id, status;
2996     char tmpfil[80];
2997     FILE *out;
2998
2999     if (fmsh) {
3000         strncpy (tmpfil, m_name (msgnum), sizeof(tmpfil));
3001         context_del (pfolder);
3002         context_replace (pfolder, fmsh);/* update current folder   */
3003         seq_save (mp);
3004         context_save ();                /* save the context file   */
3005         goto ready;
3006     }
3007
3008     strncpy (tmpfil, m_scratch ("", invo_name), sizeof(tmpfil));
3009     if ((out = fopen (tmpfil, "w")) == NULL) {
3010         int olderr;
3011         char newfil[80];
3012
3013         olderr = errno;
3014         strncpy (newfil, m_tmpfil (invo_name), sizeof(newfil));
3015         if ((out = fopen (newfil, "w")) == NULL) {
3016             errno = olderr;
3017             advise (tmpfil, "unable to create temporary file");
3018             return NOTOK;
3019         } else {
3020             strncpy (tmpfil, newfil, sizeof(tmpfil));
3021         }
3022     }
3023     copy_message (msgnum, out);
3024     fclose (out);
3025
3026 ready: ;
3027     fflush (stdout);
3028     switch (child_id = fork ()) {
3029         case NOTOK: 
3030             advise ("fork", "unable to");
3031             status = NOTOK;
3032             break;
3033             
3034         case OK: 
3035             closefds (3);
3036             SIGNAL (SIGINT, istat);
3037             SIGNAL (SIGQUIT, qstat);
3038
3039             vec[vecp++] = tmpfil;
3040             vec[vecp] = NULL;
3041
3042             execvp (proc, vec);
3043             fprintf (stderr, "unable to exec ");
3044             perror (proc);
3045             _exit (1);
3046
3047         default: 
3048             status = pidXwait (child_id, NULL);
3049             break;
3050     }
3051
3052     if (!fmsh)
3053         unlink (tmpfil);
3054     return status;
3055 }
3056
3057
3058 static void
3059 copy_message (int msgnum, FILE *out)
3060 {
3061     long pos;
3062     static char buffer[BUFSIZ];
3063     register FILE *zp;
3064
3065     zp = msh_ready (msgnum, 1);
3066     if (fmsh) {
3067         while (fgets (buffer, sizeof buffer, zp) != NULL) {
3068             fputs (buffer, out);
3069             if (interrupted && out == stdout)
3070                 break;
3071         }
3072     }
3073     else {
3074         pos = ftell (zp);
3075         while (fgets (buffer, sizeof buffer, zp) != NULL
3076                 && pos < Msgs[msgnum].m_stop) {
3077             fputs (buffer, out);
3078             pos += (long) strlen (buffer);
3079             if (interrupted && out == stdout)
3080                 break;
3081         }
3082     }
3083 }
3084
3085
3086 static void
3087 copy_digest (int msgnum, FILE *out)
3088 {
3089     char c;
3090     long pos;
3091     static char buffer[BUFSIZ];
3092     register FILE *zp;
3093
3094     c = '\n';
3095     zp = msh_ready (msgnum, 1);
3096     if (!fmsh)
3097         pos = ftell (zp);
3098     while (fgets (buffer, sizeof buffer, zp) != NULL
3099             && !fmsh && pos < Msgs[msgnum].m_stop) {
3100         if (c == '\n' && *buffer == '-')
3101             fputc (' ', out);
3102         fputs (buffer, out);
3103         c = buffer[strlen (buffer) - 1];
3104         if (!fmsh)
3105             pos += (long) strlen (buffer);
3106         if (interrupted && out == stdout)
3107             break;
3108     }
3109 }