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