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