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