source: trunk/third/nmh/uip/mshcmds.c @ 12455

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