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