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