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