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