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