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