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