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