Removed unused function seek_home().
[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     int i;
1079     NMH_UNUSED (args);
1080
1081     for (i = 0; hlpmsg[i]; i++) {
1082         printf (hlpmsg[i], invo_name);
1083         putchar ('\n');
1084     }
1085 }
1086
1087
1088 static struct swit markswit[] = {
1089 #define MADDSW             0
1090     { "add", 0 },
1091 #define MDELSW             1
1092     { "delete", 0 },
1093 #define MLSTSW             2
1094     { "list", 0 },
1095 #define MSEQSW             3
1096     { "sequence name", 0 },
1097 #define MPUBSW             4
1098     { "public", 0 },
1099 #define MNPUBSW            5
1100     { "nopublic", 0 },
1101 #define MZERSW             6
1102     { "zero", 0 },
1103 #define MNZERSW            7
1104     { "nozero", 0 },
1105 #define MHELP              8
1106     { "help", 0 },
1107 #define MDBUGSW            9
1108     { "debug", -5 },
1109     { NULL, 0 }
1110 };
1111
1112
1113 void
1114 markcmd (char **args)
1115 {
1116     int addsw = 0, deletesw = 0, debugsw = 0;
1117     int listsw = 0, zerosw = 0;
1118     size_t seqp = 0;
1119     int msgp = 0, msgnum;
1120     char *cp, buf[BUFSIZ];
1121     char *seqs[NUMATTRS + 1], *msgs[MAXARGS];
1122
1123     while ((cp = *args++)) {
1124         if (*cp == '-') {
1125             switch (smatch (++cp, markswit)) {
1126                 case AMBIGSW: 
1127                     ambigsw (cp, markswit);
1128                     return;
1129                 case UNKWNSW: 
1130                     fprintf (stderr, "-%s unknown\n", cp);
1131                     return;
1132                 case MHELP: 
1133                     snprintf (buf, sizeof(buf), "%s [msgs] [switches]", cmd_name);
1134                     print_help (buf, markswit, 1);
1135                     return;
1136
1137                 case MADDSW: 
1138                     addsw++;
1139                     deletesw = listsw = 0;
1140                     continue;
1141                 case MDELSW: 
1142                     deletesw++;
1143                     addsw = listsw = 0;
1144                     continue;
1145                 case MLSTSW: 
1146                     listsw++;
1147                     addsw = deletesw = 0;
1148                     continue;
1149
1150                 case MSEQSW: 
1151                     if (!(cp = *args++) || *cp == '-') {
1152                         advise (NULL, "missing argument to %s", args[-2]);
1153                         return;
1154                     }
1155                     if (seqp < NUMATTRS)
1156                         seqs[seqp++] = cp;
1157                     else {
1158                         advise (NULL, "only %d sequences allowed!", NUMATTRS);
1159                         return;
1160                     }
1161                     continue;
1162
1163                 case MPUBSW:    /* not implemented */
1164                 case MNPUBSW: 
1165                     continue;
1166
1167                 case MDBUGSW: 
1168                     debugsw++;
1169                     continue;
1170
1171                 case MZERSW: 
1172                     zerosw++;
1173                     continue;
1174                 case MNZERSW: 
1175                     zerosw = 0;
1176                     continue;
1177             }
1178         }
1179         if (*cp == '+' || *cp == '@') {
1180             advise (NULL, "sorry, no folders allowed!");
1181             return;
1182         } else {
1183             msgs[msgp++] = cp;
1184         }
1185     }
1186
1187     if (!addsw && !deletesw && !listsw) {
1188         if (seqp)
1189             addsw++;
1190         else
1191             if (debugsw)
1192                 listsw++;
1193             else {
1194                 seqs[seqp++] = "unseen";
1195                 deletesw++;
1196                 zerosw = 0;
1197                 if (!msgp)
1198                     msgs[msgp++] = "all";
1199             }
1200     }
1201
1202     if (!msgp)
1203         msgs[msgp++] = listsw ? "all" :"cur";
1204     for (msgnum = 0; msgnum < msgp; msgnum++)
1205         if (!m_convert (mp, msgs[msgnum]))
1206             return;
1207
1208     if (debugsw) {
1209         printf ("invo_name=%s mypath=%s defpath=%s\n",
1210                 invo_name, mypath, defpath);
1211         printf ("ctxpath=%s context flags=%s\n",
1212                 ctxpath, snprintb (buf, sizeof(buf), (unsigned) ctxflags, DBITS));
1213         printf ("foldpath=%s flags=%s\n",
1214                 mp->foldpath,
1215                 snprintb (buf, sizeof(buf), (unsigned) mp->msgflags, FBITS));
1216         printf ("hghmsg=%d lowmsg=%d nummsg=%d curmsg=%d\n",
1217                 mp->hghmsg, mp->lowmsg, mp->nummsg, mp->curmsg);
1218         printf ("lowsel=%d hghsel=%d numsel=%d\n",
1219                 mp->lowsel, mp->hghsel, mp->numsel);
1220         printf ("lowoff=%d hghoff=%d\n", mp->lowoff, mp->hghoff);
1221     }
1222
1223     if (seqp == 0 && (addsw || deletesw)) {
1224         advise (NULL, "-%s requires at least one -sequence argument",
1225                 addsw ? "add" : "delete");
1226         return;
1227     }
1228     seqs[seqp] = NULL;
1229
1230     if (addsw) {
1231         for (seqp = 0; seqs[seqp]; seqp++)
1232             if (!seq_addsel (mp, seqs[seqp], 0, zerosw))
1233                 return;
1234     }
1235
1236     if (deletesw) {
1237         for (seqp = 0; seqs[seqp]; seqp++)
1238             if (!seq_delsel (mp, seqs[seqp], 0, zerosw))
1239                 return;
1240     }
1241
1242     /* Listing messages in sequences */
1243     if (listsw) {
1244         if (seqp) {
1245             /* list the given sequences */
1246             for (seqp = 0; seqs[seqp]; seqp++)
1247                 seq_print (mp, seqs[seqp]);
1248         } else {
1249             /* else list them all */
1250             seq_printall (mp);
1251         }
1252
1253         interrupted = 0;
1254         if (debugsw)
1255             for (msgnum = mp->lowsel;
1256                     msgnum <= mp->hghsel && !interrupted;
1257                     msgnum++)
1258                 if (is_selected (mp, msgnum)) {
1259                     printf ("%*d: id=%d top=%d start=%ld stop=%ld %s\n",
1260                         DMAXFOLDER,
1261                         msgnum,
1262                         Msgs[msgnum].m_bboard_id,
1263                         Msgs[msgnum].m_top,
1264                         (long) Msgs[msgnum].m_start,
1265                         (long) Msgs[msgnum].m_stop,
1266                         snprintb (buf, sizeof(buf),
1267                                 (unsigned) mp->msgstats[msgnum - mp->lowoff],
1268                                 seq_bits (mp)));
1269                     if (Msgs[msgnum].m_scanl)
1270                         printf ("%s", Msgs[msgnum].m_scanl);
1271                 }                           
1272     }
1273 }
1274
1275
1276 static struct swit mhnswit[] = {
1277 #define MHNAUTOSW           0
1278     { "auto", 0 },
1279 #define MHNNAUTOSW          1
1280     { "noauto", 0 },
1281 #define MHNDEBUGSW          2
1282     { "debug", -5 },
1283 #define MHNEBCDICSW         3
1284     { "ebcdicsafe", 0 },
1285 #define MHNNEBCDICSW        4
1286     { "noebcdicsafe", 0 },
1287 #define MHNFORMSW           5
1288     { "form formfile", 4 },
1289 #define MHNHEADSW           6
1290     { "headers", 0 },
1291 #define MHNNHEADSW          7
1292     { "noheaders", 0 },
1293 #define MHNLISTSW           8
1294     { "list", 0 },
1295 #define MHNNLISTSW          9
1296     { "nolist", 0 },
1297 #define MHNPARTSW          10
1298     { "part number", 0 },
1299 #define MHNSIZESW          11
1300     { "realsize", 0 },
1301 #define MHNNSIZESW         12
1302     { "norealsize", 0 },
1303 #define MHNRFC934SW        13
1304     { "rfc934mode", 0 },
1305 #define MHNNRFC934SW       14
1306     { "norfc934mode", 0 },
1307 #define MHNSERIALSW        15
1308     { "serialonly", 0 },
1309 #define MHNNSERIALSW       16
1310     { "noserialonly", 0 },
1311 #define MHNSHOWSW          17
1312     { "show", 0 },
1313 #define MHNNSHOWSW         18
1314     { "noshow", 0 },
1315 #define MHNSTORESW         19
1316     { "store", 0 },
1317 #define MHNNSTORESW        20
1318     { "nostore", 0 },
1319 #define MHNTYPESW          21
1320     { "type content", 0 },
1321 #define MHNVERBSW          22
1322     { "verbose", 0 },
1323 #define MHNNVERBSW         23
1324     { "noverbose", 0 },
1325 #define MHNHELPSW          24
1326     { "help", 0 },
1327 #define MHNPROGSW          25
1328     { "moreproc program", -4 },
1329 #define MHNNPROGSW         26
1330     { "nomoreproc", -3 },
1331 #define MHNLENSW           27
1332     { "length lines", -4 },
1333 #define MHNWIDSW           28
1334     { "width columns", -4 },
1335     { NULL, 0 }
1336 };
1337
1338
1339 void
1340 mhncmd (char **args)
1341 {
1342     int msgp = 0, vecp = 1;
1343     int msgnum;
1344     char *cp, buf[BUFSIZ];
1345     char *msgs[MAXARGS], *vec[MAXARGS];
1346
1347     if (fmsh) {
1348         forkcmd (args, cmd_name);
1349         return;
1350     }
1351     while ((cp = *args++)) {
1352         if (*cp == '-') {
1353             switch (smatch (++cp, mhnswit)) {
1354                 case AMBIGSW: 
1355                     ambigsw (cp, mhnswit);
1356                     return;
1357                 case UNKWNSW: 
1358                     fprintf (stderr, "-%s unknown\n", cp);
1359                     return;
1360                 case MHNHELPSW:
1361                     snprintf (buf, sizeof(buf), "%s [msgs] [switches]", cmd_name);
1362                     print_help (buf, mhnswit, 1);
1363                     return;
1364
1365                 case MHNAUTOSW:
1366                 case MHNNAUTOSW:
1367                 case MHNDEBUGSW:
1368                 case MHNEBCDICSW:
1369                 case MHNNEBCDICSW:
1370                 case MHNHEADSW:
1371                 case MHNNHEADSW:
1372                 case MHNLISTSW:
1373                 case MHNNLISTSW:
1374                 case MHNSIZESW:
1375                 case MHNNSIZESW:
1376                 case MHNRFC934SW:
1377                 case MHNNRFC934SW:
1378                 case MHNSERIALSW:
1379                 case MHNNSERIALSW:
1380                 case MHNSHOWSW:
1381                 case MHNNSHOWSW:
1382                 case MHNSTORESW:
1383                 case MHNNSTORESW:
1384                 case MHNVERBSW:
1385                 case MHNNVERBSW:
1386                 case MHNNPROGSW:
1387                     vec[vecp++] = --cp;
1388                     continue;
1389
1390                 case MHNFORMSW:
1391                 case MHNPARTSW:
1392                 case MHNTYPESW:
1393                 case MHNPROGSW:
1394                 case MHNLENSW:
1395                 case MHNWIDSW:
1396                     vec[vecp++] = --cp;
1397                     if (!(cp = *args++) || *cp == '-') {
1398                         advise (NULL, "missing argument to %s", args[-2]);
1399                         return;
1400                     }
1401                     vec[vecp++] = cp;
1402                     continue;
1403             }
1404         }
1405         if (*cp == '+' || *cp == '@') {
1406             advise (NULL, "sorry, no folders allowed!");
1407             return;
1408         } else {
1409             msgs[msgp++] = cp;
1410         }
1411     }
1412
1413     vec[0] = cmd_name;
1414     vec[vecp++] = "-file";
1415     vec[vecp] = NULL;
1416     if (!msgp)
1417         msgs[msgp++] = "cur";
1418     for (msgnum = 0; msgnum < msgp; msgnum++)
1419         if (!m_convert (mp, msgs[msgnum]))
1420             return;
1421     seq_setprev (mp);
1422
1423     interrupted = 0;
1424     for (msgnum = mp->lowsel;
1425             msgnum <= mp->hghsel && !interrupted;
1426             msgnum++)
1427         if (is_selected (mp, msgnum))
1428             if (process (msgnum, cmd_name, vecp, vec)) {
1429                 unset_selected (mp, msgnum);
1430                 mp->numsel--;
1431             }
1432
1433     seq_setcur (mp, mp->hghsel);
1434 }
1435
1436
1437 static struct swit packswit[] = {
1438 #define PAFISW         0
1439     { "file name", 0 },
1440 #define PAHELP         1
1441     { "help", 0 },
1442     { NULL, 0 }
1443 };
1444
1445 static int mbx_style = MMDF_FORMAT;
1446
1447 void
1448 packcmd (char **args)
1449 {
1450     int msgp = 0, md, msgnum;
1451     char *cp, *file = NULL;
1452     char buf[BUFSIZ], *msgs[MAXARGS];
1453     struct stat st;
1454
1455     if (fmsh) {
1456         forkcmd (args, cmd_name);
1457         return;
1458     }
1459
1460     while ((cp = *args++)) {
1461         if (*cp == '-')
1462             switch (smatch (++cp, packswit)) {
1463                 case AMBIGSW: 
1464                     ambigsw (cp, packswit);
1465                     return;
1466                 case UNKWNSW: 
1467                     fprintf (stderr, "-%s unknown\n", cp);
1468                     return;
1469                 case PAHELP: 
1470                     snprintf (buf, sizeof(buf), "%s [msgs] [switches]", cmd_name);
1471                     print_help (buf, packswit, 1);
1472                     return;
1473
1474                 case PAFISW: 
1475                     if (!(file = *args++) || *file == '-') {
1476                         advise (NULL, "missing argument to %s", args[-2]);
1477                         return;
1478                     }
1479                     continue;
1480             }
1481         if (*cp == '+' || *cp == '@') {
1482             advise (NULL, "sorry, no folders allowed!");
1483             return;
1484         }
1485         else
1486             msgs[msgp++] = cp;
1487     }
1488
1489     if (!file)
1490         file = "./msgbox";
1491     file = path (file, TFILE);
1492     if (stat (file, &st) == NOTOK) {
1493         if (errno != ENOENT) {
1494             advise (file, "error on file");
1495             goto done_pack;
1496         }
1497         md = getanswer (cp = concat ("Create file \"", file, "\"? ", NULL));
1498         free (cp);
1499         if (!md)
1500             goto done_pack;
1501     }
1502
1503     if (!msgp)
1504         msgs[msgp++] = "all";
1505     for (msgnum = 0; msgnum < msgp; msgnum++)
1506         if (!m_convert (mp, msgs[msgnum]))
1507             goto done_pack;
1508     seq_setprev (mp);
1509
1510     if ((md = mbx_open (file, mbx_style, getuid (), getgid (), m_gmprot ())) == NOTOK) {
1511         advise (file, "unable to open");
1512         goto done_pack;
1513     }
1514     for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
1515         if (is_selected (mp, msgnum))
1516             if (pack (file, md, msgnum) == NOTOK)
1517                 break;
1518     mbx_close (file, md);
1519
1520     if (mp->hghsel != mp->curmsg)
1521         seq_setcur (mp, mp->lowsel);
1522
1523 done_pack: ;
1524     free (file);
1525 }
1526
1527
1528 int
1529 pack (char *mailbox, int md, int msgnum)
1530 {
1531     register FILE *zp;
1532
1533     if (Msgs[msgnum].m_bboard_id == 0)
1534         readid (msgnum);
1535
1536     zp = msh_ready (msgnum, 1);
1537     return mbx_write (mailbox, md, zp, Msgs[msgnum].m_bboard_id,
1538             0L, ftell (zp), Msgs[msgnum].m_stop, 1, 1);
1539 }
1540
1541
1542 int
1543 packhak (char **args)
1544 {
1545     int result;
1546     char *cp, *file = NULL;
1547
1548     while ((cp = *args++)) {
1549         if (*cp == '-')
1550             switch (smatch (++cp, packswit)) {
1551                 case AMBIGSW: 
1552                 case UNKWNSW: 
1553                 case PAHELP: 
1554                     return NOTOK;
1555
1556                 case PAFISW: 
1557                     if (!(file = *args++) || *file == '-') 
1558                         return NOTOK;
1559                     continue;
1560             }
1561         if (*cp == '+' || *cp == '@')
1562             return NOTOK;
1563     }
1564
1565     file = path (file ? file : "./msgbox", TFILE);
1566     result = access (file, F_OK) == NOTOK ? OK : NOTOK;
1567     free (file);
1568
1569     return result;
1570 }
1571
1572
1573 static struct swit pickswit[] = {
1574 #define PIANSW                0
1575     { "and", 0 },
1576 #define PIORSW                1
1577     { "or", 0 },
1578 #define PINTSW                2
1579     { "not", 0 },
1580 #define PILBSW                3
1581     { "lbrace", 0 },
1582 #define PIRBSW                4
1583     { "rbrace", 0 },
1584 #define PICCSW                5
1585     { "cc  pattern", 0 },
1586 #define PIDASW                6
1587     { "date  pattern", 0 },
1588 #define PIFRSW                7
1589     { "from  pattern", 0 },
1590 #define PISESW                8
1591     { "search  pattern", 0 },
1592 #define PISUSW                9
1593     { "subject  pattern", 0 },
1594 #define PITOSW               10
1595     { "to  pattern", 0 },
1596 #define PIOTSW               11
1597     { "-othercomponent  pattern", 15 },
1598 #define PIAFSW               12
1599     { "after date", 0 },
1600 #define PIBFSW               13
1601     { "before date", 0 },
1602 #define PIDFSW               14
1603     { "datefield field", 5 },
1604 #define PISQSW               15
1605     { "sequence name", 0 },
1606 #define PIPUSW               16
1607     { "public", 0 },
1608 #define PINPUSW              17
1609     { "nopublic", 0 },
1610 #define PIZRSW               18
1611     { "zero", 0 },
1612 #define PINZRSW              19
1613     { "nozero", 0 },
1614 #define PILISW               20
1615     { "list", 0 },
1616 #define PINLISW              21
1617     { "nolist", 0 },
1618 #define PIHELP               22
1619     { "help", 0 },
1620     { NULL, 0 }
1621 };
1622
1623
1624 void
1625 pickcmd (char **args)
1626 {
1627     int zerosw = 1, msgp = 0;
1628     size_t seqp = 0;
1629     int vecp = 0, hi, lo, msgnum;
1630     char *cp, buf[BUFSIZ], *msgs[MAXARGS];
1631     char *seqs[NUMATTRS], *vec[MAXARGS];
1632     register FILE *zp;
1633
1634     while ((cp = *args++)) {
1635         if (*cp == '-') {
1636             if (*++cp == '-') {
1637                 vec[vecp++] = --cp;
1638                 goto pattern;
1639             }
1640             switch (smatch (cp, pickswit)) {
1641                 case AMBIGSW: 
1642                     ambigsw (cp, pickswit);
1643                     return;
1644                 case UNKWNSW: 
1645                     fprintf (stderr, "-%s unknown\n", cp);
1646                     return;
1647                 case PIHELP: 
1648                     snprintf (buf, sizeof(buf), "%s [msgs] [switches]", cmd_name);
1649                     print_help (buf, pickswit, 1);
1650                     return;
1651
1652                 case PICCSW: 
1653                 case PIDASW: 
1654                 case PIFRSW: 
1655                 case PISUSW: 
1656                 case PITOSW: 
1657                 case PIDFSW: 
1658                 case PIAFSW: 
1659                 case PIBFSW: 
1660                 case PISESW: 
1661                     vec[vecp++] = --cp;
1662 pattern: ;
1663                     if (!(cp = *args++)) {/* allow -xyz arguments */
1664                         advise (NULL, "missing argument to %s", args[-2]);
1665                         return;
1666                     }
1667                     vec[vecp++] = cp;
1668                     continue;
1669                 case PIOTSW: 
1670                     advise (NULL, "internal error!");
1671                     return;
1672                 case PIANSW: 
1673                 case PIORSW: 
1674                 case PINTSW: 
1675                 case PILBSW: 
1676                 case PIRBSW: 
1677                     vec[vecp++] = --cp;
1678                     continue;
1679
1680                 case PISQSW: 
1681                     if (!(cp = *args++) || *cp == '-') {
1682                         advise (NULL, "missing argument to %s", args[-2]);
1683                         return;
1684                     }
1685                     if (seqp < NUMATTRS)
1686                         seqs[seqp++] = cp;
1687                     else {
1688                         advise (NULL, "only %d sequences allowed!", NUMATTRS);
1689                         return;
1690                     }
1691                     continue;
1692                 case PIZRSW: 
1693                     zerosw++;
1694                     continue;
1695                 case PINZRSW: 
1696                     zerosw = 0;
1697                     continue;
1698
1699                 case PIPUSW:    /* not implemented */
1700                 case PINPUSW: 
1701                 case PILISW: 
1702                 case PINLISW: 
1703                     continue;
1704             }
1705         }
1706         if (*cp == '+' || *cp == '@') {
1707             advise (NULL, "sorry, no folders allowed!");
1708             return;
1709         }
1710         else
1711             msgs[msgp++] = cp;
1712     }
1713     vec[vecp] = NULL;
1714
1715     if (!msgp)
1716         msgs[msgp++] = "all";
1717     for (msgnum = 0; msgnum < msgp; msgnum++)
1718         if (!m_convert (mp, msgs[msgnum]))
1719             return;
1720     seq_setprev (mp);
1721
1722     interrupted = 0;
1723     if (!pcompile (vec, NULL))
1724         return;
1725
1726     lo = mp->lowsel;
1727     hi = mp->hghsel;
1728
1729     for (msgnum = mp->lowsel;
1730             msgnum <= mp->hghsel && !interrupted;
1731             msgnum++)
1732         if (is_selected (mp, msgnum)) {
1733             zp = msh_ready (msgnum, 1);
1734             if (pmatches (zp, msgnum, fmsh ? 0L : Msgs[msgnum].m_start,
1735                         fmsh ? 0L : Msgs[msgnum].m_stop)) {
1736                 if (msgnum < lo)
1737                     lo = msgnum;
1738                 if (msgnum > hi)
1739                     hi = msgnum;
1740             }
1741             else {
1742                 unset_selected (mp, msgnum);
1743                 mp->numsel--;
1744             }
1745         }
1746
1747     if (interrupted)
1748         return;
1749
1750     mp->lowsel = lo;
1751     mp->hghsel = hi;
1752
1753     if (mp->numsel <= 0) {
1754         advise (NULL, "no messages match specification");
1755         return;
1756     }
1757
1758     seqs[seqp] = NULL;
1759     for (seqp = 0; seqs[seqp]; seqp++)
1760         if (!seq_addsel (mp, seqs[seqp], 0, zerosw))
1761             return;
1762
1763     printf ("%d hit%s\n", mp->numsel, mp->numsel == 1 ? "" : "s");
1764 }
1765
1766
1767 static struct swit replswit[] = {
1768 #define REANSW                  0
1769     { "annotate", 0 },
1770 #define RENANSW                 1
1771     { "noannotate", 0 },
1772 #define RECCSW                  2
1773     { "cc type", 0 },
1774 #define RENCCSW                 3
1775     { "nocc type", 0 },
1776 #define REDFSW                  4
1777     { "draftfolder +folder", 0 },
1778 #define REDMSW                  5
1779     { "draftmessage msg", 0 },
1780 #define RENDFSW                 6
1781     { "nodraftfolder", 0 },
1782 #define REEDTSW                 7
1783     { "editor editor", 0 },
1784 #define RENEDSW                 8
1785     { "noedit", 0 },
1786 #define REFCCSW                 9
1787     { "fcc +folder", 0 },
1788 #define REFLTSW                10
1789     { "filter filterfile", 0 },
1790 #define REFRMSW                11
1791     { "form formfile", 0 },
1792 #define REINSW                 12
1793     { "inplace", 0 },
1794 #define RENINSW                13
1795     { "noinplace", 0 },
1796 #define REQUSW                 14
1797     { "query", 0 },
1798 #define RENQUSW                15
1799     { "noquery", 0 },
1800 #define REWHTSW                16
1801     { "whatnowproc program", 0 },
1802 #define RENWTSW                17
1803     { "nowhatnow", 0 },
1804 #define REWIDSW                19
1805     { "width columns", 0 },
1806 #define REHELP                 20
1807     { "help", 0 },
1808     { NULL, 0 }
1809 };
1810
1811
1812 void
1813 replcmd (char **args)
1814 {
1815     int vecp = 1;
1816     char *cp, *msg = NULL;
1817     char buf[BUFSIZ], *vec[MAXARGS];
1818
1819     if (fmsh) {
1820         forkcmd (args, cmd_name);
1821         return;
1822     }
1823
1824     while ((cp = *args++)) {
1825         if (*cp == '-')
1826             switch (smatch (++cp, replswit)) {
1827                 case AMBIGSW: 
1828                     ambigsw (cp, replswit);
1829                     return;
1830                 case UNKWNSW: 
1831                     fprintf (stderr, "-%s unknown\n", cp);
1832                     return;
1833                 case REHELP: 
1834                     snprintf (buf, sizeof(buf), "%s [msgs] [switches]", cmd_name);
1835                     print_help (buf, replswit, 1);
1836                     return;
1837
1838                 case REANSW:    /* not implemented */
1839                 case RENANSW: 
1840                 case REINSW: 
1841                 case RENINSW: 
1842                     continue;
1843
1844                 case REQUSW:
1845                 case RENQUSW:
1846                 case RENDFSW:
1847                 case RENEDSW:
1848                 case RENWTSW:
1849                     vec[vecp++] = --cp;
1850                     continue;
1851
1852                 case RECCSW: 
1853                 case RENCCSW: 
1854                 case REEDTSW: 
1855                 case REFCCSW: 
1856                 case REFLTSW:
1857                 case REFRMSW: 
1858                 case REWIDSW: 
1859                 case REDFSW:
1860                 case REDMSW:
1861                 case REWHTSW:
1862                     vec[vecp++] = --cp;
1863                     if (!(cp = *args++) || *cp == '-') {
1864                         advise (NULL, "missing argument to %s", args[-2]);
1865                         return;
1866                     }
1867                     vec[vecp++] = cp;
1868                     continue;
1869             }
1870         if (*cp == '+' || *cp == '@') {
1871             advise (NULL, "sorry, no folders allowed!");
1872             return;
1873         }
1874         else
1875             if (msg) {
1876                 advise (NULL, "only one message at a time!");
1877                 return;
1878             }
1879             else
1880                 msg = cp;
1881     }
1882
1883     vec[0] = cmd_name;
1884     vec[vecp++] = "-file";
1885     vec[vecp] = NULL;
1886     if (!msg)
1887         msg = "cur";
1888     if (!m_convert (mp, msg))
1889         return;
1890     seq_setprev (mp);
1891
1892     if (mp->numsel > 1) {
1893         advise (NULL, "only one message at a time!");
1894         return;
1895     }
1896     process (mp->hghsel, cmd_name, vecp, vec);
1897     seq_setcur (mp, mp->hghsel);
1898 }
1899
1900
1901 static struct swit rmmswit[] = {
1902 #define RMHELP    0
1903     { "help", 0 },
1904     { NULL, 0 }
1905 };
1906
1907
1908 void
1909 rmmcmd (char **args)
1910 {
1911     int msgp = 0, msgnum;
1912     char *cp, buf[BUFSIZ], *msgs[MAXARGS];
1913
1914     while ((cp = *args++)) {
1915         if (*cp == '-')
1916             switch (smatch (++cp, rmmswit)) {
1917                 case AMBIGSW: 
1918                     ambigsw (cp, rmmswit);
1919                     return;
1920                 case UNKWNSW: 
1921                     fprintf (stderr, "-%s unknown\n", cp);
1922                     return;
1923                 case RMHELP: 
1924                     snprintf (buf, sizeof(buf), "%s [msgs] [switches]", cmd_name);
1925                     print_help (buf, rmmswit, 1);
1926                     return;
1927             }
1928         if (*cp == '+' || *cp == '@') {
1929             advise (NULL, "sorry, no folders allowed!");
1930             return;
1931         }
1932         else
1933             msgs[msgp++] = cp;
1934     }
1935
1936     if (!msgp)
1937         msgs[msgp++] = "cur";
1938     for (msgnum = 0; msgnum < msgp; msgnum++)
1939         if (!m_convert (mp, msgs[msgnum]))
1940             return;
1941     seq_setprev (mp);
1942
1943     rmm ();
1944 }
1945
1946
1947 static void
1948 rmm (void)
1949 {
1950     register int msgnum, vecp;
1951     register char *cp;
1952     char buffer[BUFSIZ], *vec[MAXARGS];
1953
1954     if (fmsh) {
1955         if (rmmproc) {
1956             if (mp->numsel > MAXARGS - 1) {
1957                 advise (NULL, "more than %d messages for %s exec",
1958                         MAXARGS - 1, rmmproc);
1959                 return;
1960             }
1961             vecp = 0;
1962             for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
1963                 if (is_selected (mp, msgnum))
1964                     vec[vecp++] = getcpy (m_name (msgnum));
1965             vec[vecp] = NULL;
1966             forkcmd (vec, rmmproc);
1967             for (vecp = 0; vec[vecp]; vecp++)
1968                 free (vec[vecp]);
1969         }
1970         else
1971             for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
1972                 if (is_selected (mp, msgnum)) {
1973                     strncpy (buffer, m_backup (cp = m_name (msgnum)), sizeof(buffer));
1974                     if (rename (cp, buffer) == NOTOK)
1975                         admonish (buffer, "unable to rename %s to", cp);
1976                 }
1977     }
1978
1979     for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
1980         if (is_selected (mp, msgnum)) {
1981             set_deleted (mp, msgnum);
1982             unset_exists (mp, msgnum);
1983         }
1984
1985     if ((mp->nummsg -= mp->numsel) <= 0) {
1986         if (fmsh)
1987             admonish (NULL, "no messages remaining in +%s", fmsh);
1988         else
1989             admonish (NULL, "no messages remaining in %s", mp->foldpath);
1990         mp->lowmsg = mp->hghmsg = mp->nummsg = 0;
1991     }
1992     if (mp->lowsel == mp->lowmsg) {
1993         for (msgnum = mp->lowmsg + 1; msgnum <= mp->hghmsg; msgnum++)
1994             if (does_exist (mp, msgnum))
1995                 break;
1996         mp->lowmsg = msgnum;
1997     }
1998     if (mp->hghsel == mp->hghmsg) {
1999         for (msgnum = mp->hghmsg - 1; msgnum >= mp->lowmsg; msgnum--)
2000             if (does_exist (mp, msgnum))
2001                 break;
2002         mp->hghmsg = msgnum;
2003     }
2004
2005     mp->msgflags |= MODIFIED;
2006     modified++;
2007 }
2008
2009
2010 static struct swit scanswit[] = {
2011 #define SCCLR              0
2012     { "clear", 0 },
2013 #define SCNCLR             1
2014     { "noclear", 0 },
2015 #define SCFORM             2
2016     { "form formatfile", 0 },
2017 #define SCFMT              3
2018     { "format string", 5 },
2019 #define SCHEAD             4
2020     { "header", 0 },
2021 #define SCNHEAD            5
2022     { "noheader", 0 },
2023 #define SCWID              6
2024     { "width columns", 0 },
2025 #define SCHELP             7
2026     { "help", 0 },
2027     { NULL, 0 }
2028 };
2029
2030
2031 void
2032 scancmd (char **args)
2033 {
2034 #define equiv(a,b)      (a ? b && !strcmp (a, b) : !b)
2035
2036     int clearsw = 0, headersw = 0, width = 0, msgp = 0;
2037     int msgnum, optim, state;
2038     char *cp, *form = NULL, *format = NULL;
2039     char buf[BUFSIZ], *nfs, *msgs[MAXARGS];
2040     register FILE *zp;
2041     static int s_optim = 0;
2042     static char *s_form = NULL, *s_format = NULL;
2043
2044     while ((cp = *args++)) {
2045         if (*cp == '-')
2046             switch (smatch (++cp, scanswit)) {
2047                 case AMBIGSW: 
2048                     ambigsw (cp, scanswit);
2049                     return;
2050                 case UNKWNSW: 
2051                     fprintf (stderr, "-%s unknown\n", cp);
2052                     return;
2053                 case SCHELP: 
2054                     snprintf (buf, sizeof(buf), "%s [msgs] [switches]", cmd_name);
2055                     print_help (buf, scanswit, 1);
2056                     return;
2057
2058                 case SCCLR: 
2059                     clearsw++;
2060                     continue;
2061                 case SCNCLR: 
2062                     clearsw = 0;
2063                     continue;
2064                 case SCHEAD: 
2065                     headersw++;
2066                     continue;
2067                 case SCNHEAD: 
2068                     headersw = 0;
2069                     continue;
2070                 case SCFORM: 
2071                     if (!(form = *args++) || *form == '-') {
2072                         advise (NULL, "missing argument to %s", args[-2]);
2073                         return;
2074                     }
2075                     format = NULL;
2076                     continue;
2077                 case SCFMT: 
2078                     if (!(format = *args++) || *format == '-') {
2079                         advise (NULL, "missing argument to %s", args[-2]);
2080                         return;
2081                     }
2082                     form = NULL;
2083                     continue;
2084                 case SCWID: 
2085                     if (!(cp = *args++) || *cp == '-') {
2086                         advise (NULL, "missing argument to %s", args[-2]);
2087                         return;
2088                     }
2089                     width = atoi (cp);
2090                     continue;
2091             }
2092         if (*cp == '+' || *cp == '@') {
2093             advise (NULL, "sorry, no folders allowed!");
2094             return;
2095         }
2096         else
2097             msgs[msgp++] = cp;
2098     }
2099
2100     if (!msgp)
2101         msgs[msgp++] = "all";
2102     for (msgnum = 0; msgnum < msgp; msgnum++)
2103         if (!m_convert (mp, msgs[msgnum]))
2104             return;
2105     seq_setprev (mp);
2106
2107     /* Get new format string */
2108     nfs = new_fs (form, format, FORMAT);
2109
2110     /* force scansbr to (re)compile format */
2111     if (scanl) {
2112         free (scanl);
2113         scanl = NULL;
2114     }
2115
2116     if (s_optim == 0) {
2117         s_optim = optim = 1;
2118         s_form = form ? getcpy (form) : NULL;
2119         s_format = format ? getcpy (format) : NULL;
2120
2121     }
2122     else
2123         optim = equiv (s_form, form) && equiv (s_format, format);
2124
2125     interrupted = 0;
2126     for (msgnum = mp->lowsel;
2127             msgnum <= mp->hghsel && !interrupted;
2128             msgnum++)
2129         if (is_selected (mp, msgnum)) {
2130             if (optim && Msgs[msgnum].m_scanl)
2131                 printf ("%s", Msgs[msgnum].m_scanl);
2132             else {
2133
2134                 zp = msh_ready (msgnum, 0);
2135                 switch (state = scan (zp, msgnum, 0, nfs, width,
2136                         msgnum == mp->curmsg,
2137                         is_unseen (mp, msgnum),
2138                         headersw ? (fmsh ? fmsh : mp->foldpath) : NULL,
2139                         fmsh ? 0L : (long) (Msgs[msgnum].m_stop - Msgs[msgnum].m_start),
2140                         1)) {
2141                     case SCNMSG:
2142                     case SCNENC:
2143                     case SCNERR:
2144                         if (optim)
2145                             Msgs[msgnum].m_scanl = getcpy (scanl);
2146                         break;
2147
2148                     default:
2149                         advise (NULL, "scan() botch (%d)", state);
2150                         return;
2151
2152                     case SCNEOF:
2153                         printf ("%*d  empty\n", DMAXFOLDER, msgnum);
2154                         break;
2155                     }
2156             }
2157             headersw = 0;
2158         }
2159
2160     if (clearsw)
2161         clear_screen ();
2162 }
2163
2164
2165 static struct swit showswit[] = {
2166 #define SHDRAFT               0
2167     { "draft", 5 },
2168 #define SHFORM                1
2169     { "form formfile", 4 },
2170 #define SHPROG                2
2171     { "moreproc program", 4 },
2172 #define SHNPROG               3
2173     { "nomoreproc", 3 },
2174 #define SHLEN                 4
2175     { "length lines", 4 },
2176 #define SHWID                 5
2177     { "width columns", 4 },
2178 #define SHSHOW                6
2179     { "showproc program", 4 },
2180 #define SHNSHOW               7
2181     { "noshowproc", 3 },
2182 #define SHHEAD                8
2183     { "header", 4 },
2184 #define SHNHEAD               9
2185     { "noheader", 3 },
2186 #define SHHELP               10
2187     { "help", 0 },
2188     { NULL, 0 }
2189 };
2190
2191
2192 void
2193 showcmd (char **args)
2194 {
2195     int headersw = 1, nshow = 0, msgp = 0, vecp = 1;
2196     int mhl = 0, seqnum = -1, mode = 0, i, msgnum;
2197     char *cp, *proc = showproc, buf[BUFSIZ];
2198     char *msgs[MAXARGS], *vec[MAXARGS];
2199
2200     if (!mh_strcasecmp (cmd_name, "next"))
2201         mode = 1;
2202     else
2203         if (!mh_strcasecmp (cmd_name, "prev"))
2204             mode = -1;
2205     while ((cp = *args++)) {
2206         if (*cp == '-')
2207             switch (i = smatch (++cp, showswit)) {
2208                 case AMBIGSW: 
2209                     ambigsw (cp, showswit);
2210                     return;
2211                 case UNKWNSW: 
2212                 case SHNPROG:
2213                     vec[vecp++] = --cp;
2214                     continue;
2215                 case SHHELP: 
2216                     snprintf (buf, sizeof(buf), "%s %s[switches] [switches for showproc]",
2217                             cmd_name, mode ? NULL : "[msgs] ");
2218                     print_help (buf, showswit, 1);
2219                     return;
2220
2221                 case SHFORM: 
2222                 case SHPROG:
2223                 case SHLEN:
2224                 case SHWID:
2225                     vec[vecp++] = --cp;
2226                     if (!(cp = *args++) || *cp == '-') {
2227                         advise (NULL, "missing argument to %s", args[-2]);
2228                         return;
2229                     }
2230                     vec[vecp++] = cp;
2231                     continue;
2232                 case SHHEAD: 
2233                     headersw++;
2234                     continue;
2235                 case SHNHEAD: 
2236                     headersw = 0;
2237                     continue;
2238                 case SHSHOW: 
2239                     if (!(proc = *args++) || *proc == '-') {
2240                         advise (NULL, "missing argument to %s", args[-2]);
2241                         return;
2242                     }
2243                     nshow = 0;
2244                     continue;
2245                 case SHNSHOW: 
2246                     nshow++;
2247                     continue;
2248
2249                 case SHDRAFT: 
2250                     advise (NULL, "sorry, -%s not allowed!", showswit[i].sw);
2251                     return;
2252             }
2253         if (*cp == '+' || *cp == '@') {
2254             advise (NULL, "sorry, no folders allowed!");
2255             return;
2256         }
2257         else
2258             if (mode) {
2259                 fprintf (stderr,
2260                         "usage: %s [switches] [switches for showproc]\n",
2261                         cmd_name);
2262                 return;
2263             }
2264             else
2265                 msgs[msgp++] = cp;
2266     }
2267     vec[vecp] = NULL;
2268
2269     if (!msgp)
2270         msgs[msgp++] = mode > 0 ? "next" : mode < 0 ? "prev" : "cur";
2271     for (msgnum = 0; msgnum < msgp; msgnum++)
2272         if (!m_convert (mp, msgs[msgnum]))
2273             return;
2274     seq_setprev (mp);
2275
2276     if (!nshow)
2277         for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
2278             if (is_selected (mp, msgnum) && is_nontext (msgnum)) {
2279                 proc = showmimeproc;
2280                 vec[vecp++] = "-file";
2281                 vec[vecp] = NULL;
2282                 goto finish;
2283             }
2284
2285     if (nshow)
2286         proc = catproc;
2287     else
2288         if (strcmp (showproc, "mhl") == 0) {
2289             proc = mhlproc;
2290             mhl++;
2291         }
2292
2293 finish: ;
2294     seqnum = seq_getnum (mp, "unseen");
2295     vec[0] = r1bindex (proc, '/');
2296     if (mhl) {
2297         msgp = vecp;
2298         for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
2299             if (is_selected (mp, msgnum)) {
2300                 vec[vecp++] = getcpy (m_name (msgnum));
2301                 if (seqnum != -1)
2302                     seq_delmsg (mp, "unseen", msgnum);
2303             }
2304         vec[vecp] = NULL;
2305         if (mp->numsel == 1 && headersw)
2306             show (mp->lowsel);
2307         mhlsbr (vecp, vec, mhl_action);
2308         m_eomsbr ((int (*)()) 0);
2309         while (msgp < vecp)
2310             free (vec[msgp++]);
2311     } else {
2312         interrupted = 0;
2313         for (msgnum = mp->lowsel;
2314                 msgnum <= mp->hghsel && !interrupted;
2315                 msgnum++)
2316             if (is_selected (mp, msgnum)) {
2317                 switch (ask (msgnum)) {
2318                     case NOTOK: /* QUIT */
2319                         break;
2320
2321                     case OK:    /* INTR */
2322                         continue;
2323
2324                     default:
2325                         if (mp->numsel == 1 && headersw)
2326                             show (msgnum);
2327                         if (nshow)
2328                             copy_message (msgnum, stdout);
2329                         else
2330                             process (msgnum, proc, vecp, vec);
2331
2332                         if (seqnum != -1)
2333                             seq_delmsg (mp, "unseen", msgnum);
2334                         continue;
2335                 }
2336                 break;
2337             }
2338     }
2339
2340     seq_setcur (mp, mp->hghsel);
2341 }
2342
2343
2344 static void
2345 show (int msgnum)
2346 {
2347     if (Msgs[msgnum].m_bboard_id == 0)
2348         readid (msgnum);
2349
2350     printf ("(Message %d", msgnum);
2351     if (Msgs[msgnum].m_bboard_id > 0)
2352         printf (", %s: %d", BBoard_ID, Msgs[msgnum].m_bboard_id);
2353     printf (")\n");
2354 }
2355
2356
2357
2358 static int
2359 eom_action (int c)
2360 {
2361     NMH_UNUSED (c);
2362
2363     return (ftell (mhlfp) >= Msgs[mhlnum].m_stop);
2364 }
2365
2366
2367 static FILE *
2368 mhl_action (char *name)
2369 {
2370     int msgnum;
2371
2372     if ((msgnum = m_atoi (name)) < mp->lowmsg
2373             || msgnum > mp->hghmsg
2374             || !does_exist (mp, msgnum))
2375         return NULL;
2376     mhlnum = msgnum;
2377
2378     mhlfp = msh_ready (msgnum, 1);
2379     if (!fmsh)
2380         m_eomsbr (eom_action);
2381
2382     return mhlfp;
2383 }
2384
2385
2386
2387 static int
2388 ask (int msgnum)
2389 {
2390     char buf[BUFSIZ];
2391
2392     if (mp->numsel == 1 || !interactive || redirected)
2393         return DONE;
2394
2395     if (SOprintf ("Press <return> to list \"%d\"...", msgnum)) {
2396         if (mp->lowsel != msgnum)
2397             printf ("\n\n\n");
2398         printf ("Press <return> to list \"%d\"...", msgnum);
2399     }
2400     fflush (stdout);
2401     buf[0] = 0;
2402
2403     read (fileno (stdout), buf, sizeof buf);
2404
2405     if (strchr(buf, '\n') == NULL)
2406         putchar ('\n');
2407
2408     if (told_to_quit) {
2409         told_to_quit = interrupted = 0;
2410         return NOTOK;
2411     }
2412     if (interrupted) {
2413         interrupted = 0;
2414         return OK;
2415     }
2416
2417     return DONE;
2418 }
2419
2420
2421 #include <h/mime.h>
2422
2423 static int
2424 is_nontext (int msgnum)
2425 {
2426     int result, state;
2427     unsigned char *bp, *dp;
2428     char *cp;
2429     char buf[BUFSIZ], name[NAMESZ];
2430     FILE *fp;
2431
2432     if (Msgs[msgnum].m_flags & MHNCHK)
2433         return (Msgs[msgnum].m_flags & MHNYES);
2434     Msgs[msgnum].m_flags |= MHNCHK;
2435
2436     fp = msh_ready (msgnum, 1);
2437
2438     for (state = FLD;;)
2439         switch (state = m_getfld (state, name, buf, sizeof buf, fp)) {
2440         case FLD:
2441         case FLDPLUS:
2442         case FLDEOF:
2443             /*
2444              * Check Content-Type field
2445              */
2446             if (!mh_strcasecmp (name, TYPE_FIELD)) {
2447                 int passno;
2448                 char c;
2449
2450                 cp = add (buf, NULL);
2451                 while (state == FLDPLUS) {
2452                     state = m_getfld (state, name, buf, sizeof buf, fp);
2453                     cp = add (buf, cp);
2454                 }
2455                 bp = cp;
2456                 passno = 1;
2457
2458 again:
2459                 for (; isspace (*bp); bp++)
2460                     continue;
2461                 if (*bp == '(') {
2462                     int i;
2463
2464                     for (bp++, i = 0;;) {
2465                         switch (*bp++) {
2466                         case '\0':
2467 invalid:
2468                             result = 0;
2469                             goto out;
2470                         case '\\':
2471                             if (*bp++ == '\0')
2472                                 goto invalid;
2473                             continue;
2474                         case '(':
2475                             i++;
2476                             /* and fall... */
2477                         default:
2478                             continue;
2479                         case ')':
2480                             if (--i < 0)
2481                                 break;
2482                             continue;
2483                         }
2484                         break;
2485                     }
2486                 }
2487                 if (passno == 2) {
2488                     if (*bp != '/')
2489                         goto invalid;
2490                     bp++;
2491                     passno = 3;
2492                     goto again;
2493                 }
2494                 for (dp = bp; istoken (*dp); dp++)
2495                     continue;
2496                 c = *dp;
2497                 *dp = '\0';
2498                 if (!*bp)
2499                     goto invalid;
2500                 if (passno > 1) {
2501                     if ((result = (mh_strcasecmp (bp, "plain") != 0)))
2502                         goto out;
2503                     *dp = c;
2504                     for (dp++; isspace (*dp); dp++)
2505                         continue;
2506                     if (*dp) {
2507                         if ((result = !uprf (dp, "charset")))
2508                             goto out;
2509                         dp += sizeof "charset" - 1;
2510                         while (isspace (*dp))
2511                             dp++;
2512                         if (*dp++ != '=')
2513                             goto invalid;
2514                         while (isspace (*dp))
2515                             dp++;
2516                         if (*dp == '"') {
2517                             if ((bp = strchr(++dp, '"')))
2518                                 *bp = '\0';
2519                         } else {
2520                             for (bp = dp; *bp; bp++)
2521                                 if (isspace (*bp)) {
2522                                     *bp = '\0';
2523                                     break;
2524                                 }
2525                         }
2526                     } else {
2527                         /* Default character set */
2528                         dp = "US-ASCII";
2529                     }
2530                     /* Check the character set */
2531                     result = !check_charset (dp, strlen (dp));
2532                 } else {
2533                     if (!(result = (mh_strcasecmp (bp, "text") != 0))) {
2534                         *dp = c;
2535                         bp = dp;
2536                         passno = 2;
2537                         goto again;
2538                     }
2539                 }
2540 out:
2541                 free (cp);
2542                 if (result) {
2543                     Msgs[msgnum].m_flags |= MHNYES;
2544                     return result;
2545                 }
2546                 break;
2547             }
2548
2549             /*
2550              * Check Content-Transfer-Encoding field
2551              */
2552             if (!mh_strcasecmp (name, ENCODING_FIELD)) {
2553                 cp = add (buf, NULL);
2554                 while (state == FLDPLUS) {
2555                     state = m_getfld (state, name, buf, sizeof buf, fp);
2556                     cp = add (buf, cp);
2557                 }
2558                 for (bp = cp; isspace (*bp); bp++)
2559                     continue;
2560                 for (dp = bp; istoken (*dp); dp++)
2561                     continue;
2562                 *dp = '\0';
2563                 result = (mh_strcasecmp (bp, "7bit")
2564                        && mh_strcasecmp (bp, "8bit")
2565                        && mh_strcasecmp (bp, "binary"));
2566
2567                 free (cp);
2568                 if (result) {
2569                     Msgs[msgnum].m_flags |= MHNYES;
2570                     return result;
2571                 }
2572                 break;
2573             }
2574
2575             /*
2576              * Just skip the rest of this header
2577              * field and go to next one.
2578              */
2579             while (state == FLDPLUS)
2580                 state = m_getfld (state, name, buf, sizeof(buf), fp);
2581             break;
2582
2583             /*
2584              * We've passed the message header,
2585              * so message is just text.
2586              */
2587         default:
2588             return 0;
2589         }
2590 }
2591
2592
2593 static struct swit sortswit[] = {
2594 #define SODATE               0
2595     { "datefield field", 0 },
2596 #define SOSUBJ               1
2597     { "textfield field", 0 },
2598 #define SONSUBJ              2
2599     { "notextfield", 0 },
2600 #define SOLIMT               3
2601     { "limit days", 0 },
2602 #define SONLIMT              4
2603     { "nolimit", 0 },
2604 #define SOVERB               5
2605     { "verbose", 0 },
2606 #define SONVERB              6
2607     { "noverbose", 0 },
2608 #define SOHELP               7
2609     { "help", 0 },
2610     { NULL, 0 }
2611 };
2612
2613
2614 void
2615 sortcmd (char **args)
2616 {
2617     int msgp = 0, msgnum;
2618     char *cp, *datesw = NULL, *subjsw = NULL;
2619     char buf[BUFSIZ], *msgs[MAXARGS];
2620     struct tws tb;
2621
2622     if (fmsh) {
2623         forkcmd (args, cmd_name);
2624         return;
2625     }
2626
2627     while ((cp = *args++)) {
2628         if (*cp == '-')
2629             switch (smatch (++cp, sortswit)) {
2630                 case AMBIGSW: 
2631                     ambigsw (cp, sortswit);
2632                     return;
2633                 case UNKWNSW: 
2634                     fprintf (stderr, "-%s unknown\n", cp);
2635                     return;
2636                 case SOHELP: 
2637                     snprintf (buf, sizeof(buf), "%s [msgs] [switches]", cmd_name);
2638                     print_help (buf, sortswit, 1);
2639                     return;
2640
2641                 case SODATE: 
2642                     if (datesw) {
2643                         advise (NULL, "only one date field at a time!");
2644                         return;
2645                     }
2646                     if (!(datesw = *args++) || *datesw == '-') {
2647                         advise (NULL, "missing argument to %s", args[-2]);
2648                         return;
2649                     }
2650                     continue;
2651
2652                 case SOSUBJ:
2653                     if (subjsw) {
2654                         advise (NULL, "only one text field at a time!");
2655                         return;
2656                     }
2657                     if (!(subjsw = *args++) || *subjsw == '-') {
2658                         advise (NULL, "missing argument to %s", args[-2]);
2659                         return;
2660                     }
2661                     continue;
2662                 case SONSUBJ:
2663                     subjsw = (char *)0;
2664                     continue;
2665
2666                 case SOLIMT:            /* too hard */
2667                     if (!(cp = *args++) || *cp == '-') {
2668                         advise (NULL, "missing argument to %s", args[-2]);
2669                         return;
2670                     }
2671                 case SONLIMT:
2672                 case SOVERB:            /* not implemented */
2673                 case SONVERB: 
2674                     continue;
2675             }
2676         if (*cp == '+' || *cp == '@') {
2677             advise (NULL, "sorry, no folders allowed!");
2678             return;
2679         }
2680         else
2681             msgs[msgp++] = cp;
2682     }
2683
2684     if (!msgp)
2685         msgs[msgp++] = "all";
2686     if (!datesw)
2687         datesw = "Date";
2688     for (msgnum = 0; msgnum < msgp; msgnum++)
2689         if (!m_convert (mp, msgs[msgnum]))
2690             return;
2691     seq_setprev (mp);
2692
2693     twscopy (&tb, dlocaltimenow ());
2694
2695     for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
2696         if (Msgs[msgnum].m_scanl) {
2697             free (Msgs[msgnum].m_scanl);
2698             Msgs[msgnum].m_scanl = NULL;
2699         }
2700         if (is_selected (mp, msgnum)) {
2701             if (get_fields (datesw, subjsw, msgnum, &Msgs[msgnum]))
2702                 twscopy (&Msgs[msgnum].m_tb,
2703                         msgnum != mp->lowsel ? &Msgs[msgnum - 1].m_tb : &tb);
2704         }
2705         else                    /* m_scaln is already NULL */
2706             twscopy (&Msgs[msgnum].m_tb, &tb);
2707         Msgs[msgnum].m_stats = mp->msgstats[msgnum - mp->lowoff];
2708         if (mp->curmsg == msgnum)
2709             Msgs[msgnum].m_stats |= CUR;
2710     }
2711
2712     qsort ((char *) &Msgs[mp->lowsel], mp->hghsel - mp->lowsel + 1,
2713            sizeof(struct Msg), (qsort_comp) (subjsw ? subsort : msgsort));
2714
2715     for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
2716         if (subjsw && Msgs[msgnum].m_scanl) {
2717             free (Msgs[msgnum].m_scanl);        /* from subjsort */
2718             Msgs[msgnum].m_scanl = NULL;
2719         }
2720         mp->msgstats[msgnum - mp->lowoff] = Msgs[msgnum].m_stats & ~CUR;
2721         if (Msgs[msgnum].m_stats & CUR)
2722             seq_setcur (mp, msgnum);
2723     }
2724             
2725     mp->msgflags |= MODIFIED;
2726     modified++;
2727 }
2728
2729
2730 /* 
2731  * get_fields - parse message, and get date and subject if needed.
2732  * We'll use the msgp->m_tb tws struct for the date, and overload
2733  * the msgp->m_scanl field with our subject string.
2734  */
2735 static int
2736 get_fields (char *datesw, char *subjsw, int msgnum, struct Msg *msgp)
2737 {
2738     int state, gotdate = 0;
2739     char *bp, buf[BUFSIZ], name[NAMESZ];
2740     struct tws *tw = (struct tws *) 0;
2741     register FILE *zp;
2742
2743     zp = msh_ready (msgnum, 0);
2744     for (state = FLD;;) {
2745         switch (state = m_getfld (state, name, buf, sizeof buf, zp)) {
2746             case FLD: 
2747             case FLDEOF: 
2748             case FLDPLUS: 
2749                 if (!mh_strcasecmp (name, datesw)) {
2750                     bp = getcpy (buf);
2751                     while (state == FLDPLUS) {
2752                         state = m_getfld (state, name, buf, sizeof buf, zp);
2753                         bp = add (buf, bp);
2754                     }
2755                     if ((tw = dparsetime (bp)) == NULL)
2756                         admonish (NULL,
2757                                 "unable to parse %s field in message %d",
2758                                 datesw, msgnum);
2759                     else
2760                         twscopy (&(msgp->m_tb), tw);
2761                     free (bp);
2762                     if (!subjsw)        /* not using this, or already done */
2763                         break;          /* all done! */
2764                     gotdate++;
2765                 }
2766                 else if (subjsw && !mh_strcasecmp(name, subjsw)) {
2767                     bp = getcpy (buf);
2768                     while (state == FLDPLUS) {
2769                         state = m_getfld (state, name, buf, sizeof buf, zp);
2770                         bp = add (buf, bp);
2771                     }
2772                     msgp->m_scanl = sosmash(subjsw, bp);
2773                     if (gotdate)
2774                         break;          /* date done so we're done */
2775                     else
2776                         subjsw = (char *)0;/* subject done, need date */
2777                 } else {
2778                     while (state == FLDPLUS)    /* flush this one */
2779                         state = m_getfld (state, name, buf, sizeof buf, zp);
2780                 }
2781                 continue;
2782
2783             case BODY: 
2784             case BODYEOF: 
2785             case FILEEOF: 
2786                 break;
2787
2788             case LENERR: 
2789             case FMTERR: 
2790                 admonish (NULL, "format error in message %d", msgnum);
2791                 if (msgp->m_scanl) {    /* this might need free'd */
2792                     free (msgp->m_scanl); /* probably can't use subj anyway */
2793                     msgp->m_scanl = NULL;
2794                 }
2795                 return NOTOK;
2796
2797             default: 
2798                 adios (NULL, "internal error -- you lose");
2799         }
2800         break;
2801     }
2802     if (tw)
2803         return OK;      /* not an error if subj not found */
2804
2805     admonish (NULL, "no %s field in message %d", datesw, msgnum);
2806     return NOTOK;       /* NOTOK means use some other date */
2807 }
2808
2809
2810 /*
2811  * sort routines
2812  */
2813
2814 static int
2815 msgsort (struct Msg *a, struct Msg *b)
2816 {
2817     return twsort (&a->m_tb, &b->m_tb);
2818 }
2819
2820
2821 static int
2822 subsort (struct Msg *a, struct Msg *b)
2823 {
2824         register int i;
2825
2826         if (a->m_scanl && b->m_scanl)
2827             if ((i = strcmp (a->m_scanl, b->m_scanl)))
2828                 return (i);
2829
2830         return twsort (&a->m_tb, &b->m_tb);
2831 }
2832
2833
2834 /*
2835  * try to make the subject "canonical": delete leading "re:", everything
2836  * but letters & smash letters to lower case. 
2837  */
2838 static char *
2839 sosmash (char *subj, char *s)
2840 {
2841     register char *cp, *dp;
2842     register unsigned char c;
2843
2844     if (s) {
2845         cp = s;
2846         dp = s; /* dst pointer */
2847         if (!mh_strcasecmp (subj, "subject"))
2848             while ((c = *cp)) {
2849                 if (! isspace(c)) {
2850                     if(uprf(cp, "re:"))
2851                         cp += 2;
2852                     else {
2853                         if (isalnum(c))
2854                             *dp++ = isupper(c) ? tolower(c) : c;
2855                         break;
2856                     }
2857                 }
2858                 cp++;
2859             }
2860         while ((c = *cp++)) {
2861             if (isalnum(c))
2862                 *dp++ = isupper(c) ? tolower(c) : c;
2863
2864         }
2865         *dp = '\0';
2866     }
2867     return s;
2868 }
2869
2870
2871 static int
2872 process (int msgnum, char *proc, int vecp, char **vec)
2873 {
2874     int child_id, status;
2875     char tmpfil[BUFSIZ];
2876     FILE *out;
2877     char *cp;
2878
2879     if (fmsh) {
2880         strncpy (tmpfil, m_name (msgnum), sizeof(tmpfil));
2881         context_del (pfolder);
2882         context_replace (pfolder, fmsh);/* update current folder   */
2883         seq_save (mp);
2884         context_save ();                /* save the context file   */
2885         goto ready;
2886     }
2887
2888     cp = m_mktemp(invo_name, NULL, &out);
2889     if (cp == NULL) {
2890         /* Try again, but try to create under /tmp */
2891         int olderr = errno;
2892         cp = m_mktemp2(NULL, invo_name, NULL, &out);
2893         if (cp == NULL) {
2894             errno = olderr;
2895             advise (NULL, "unable to create temporary file");
2896             return NOTOK;
2897         }
2898     }
2899     copy_message (msgnum, out);
2900     fclose (out);
2901     strncpy(tmpfil, cp, sizeof(tmpfil));
2902
2903 ready: ;
2904     fflush (stdout);
2905     switch (child_id = fork ()) {
2906         case NOTOK: 
2907             advise ("fork", "unable to");
2908             status = NOTOK;
2909             break;
2910             
2911         case OK: 
2912             closefds (3);
2913             SIGNAL (SIGINT, istat);
2914             SIGNAL (SIGQUIT, qstat);
2915
2916             vec[vecp++] = tmpfil;
2917             vec[vecp] = NULL;
2918
2919             execvp (proc, vec);
2920             fprintf (stderr, "unable to exec ");
2921             perror (proc);
2922             _exit (1);
2923
2924         default: 
2925             status = pidXwait (child_id, NULL);
2926             break;
2927     }
2928
2929     if (!fmsh)
2930         unlink (tmpfil);
2931     return status;
2932 }
2933
2934
2935 static void
2936 copy_message (int msgnum, FILE *out)
2937 {
2938     long pos;
2939     static char buffer[BUFSIZ];
2940     register FILE *zp;
2941
2942     zp = msh_ready (msgnum, 1);
2943     if (fmsh) {
2944         while (fgets (buffer, sizeof buffer, zp) != NULL) {
2945             fputs (buffer, out);
2946             if (interrupted && out == stdout)
2947                 break;
2948         }
2949     }
2950     else {
2951         pos = ftell (zp);
2952         while (fgets (buffer, sizeof buffer, zp) != NULL
2953                 && pos < Msgs[msgnum].m_stop) {
2954             fputs (buffer, out);
2955             pos += (long) strlen (buffer);
2956             if (interrupted && out == stdout)
2957                 break;
2958         }
2959     }
2960 }
2961
2962
2963 static void
2964 copy_digest (int msgnum, FILE *out)
2965 {
2966     char c;
2967     long pos = 0L;
2968     static char buffer[BUFSIZ];
2969     register FILE *zp;
2970
2971     c = '\n';
2972     zp = msh_ready (msgnum, 1);
2973     if (!fmsh)
2974         pos = ftell (zp);
2975     while (fgets (buffer, sizeof buffer, zp) != NULL
2976             && !fmsh && pos < Msgs[msgnum].m_stop) {
2977         if (c == '\n' && *buffer == '-')
2978             fputc (' ', out);
2979         fputs (buffer, out);
2980         c = buffer[strlen (buffer) - 1];
2981         if (!fmsh)
2982             pos += (long) strlen (buffer);
2983         if (interrupted && out == stdout)
2984             break;
2985     }
2986 }