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