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