]> diplodocus.org Git - nmh/blob - uip/mshcmds.c
Started revising m_getfld() code to replace direct buffer
[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 int bufsz = sizeof buf;
2440 switch (state = m_getfld (state, name, buf, &bufsz, 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 bufsz = sizeof buf;
2454 state = m_getfld (state, name, buf, &bufsz, fp);
2455 cp = add (buf, cp);
2456 }
2457 bp = cp;
2458 passno = 1;
2459
2460 again:
2461 for (; isspace (*bp); bp++)
2462 continue;
2463 if (*bp == '(') {
2464 int i;
2465
2466 for (bp++, i = 0;;) {
2467 switch (*bp++) {
2468 case '\0':
2469 invalid:
2470 result = 0;
2471 goto out;
2472 case '\\':
2473 if (*bp++ == '\0')
2474 goto invalid;
2475 continue;
2476 case '(':
2477 i++;
2478 /* and fall... */
2479 default:
2480 continue;
2481 case ')':
2482 if (--i < 0)
2483 break;
2484 continue;
2485 }
2486 break;
2487 }
2488 }
2489 if (passno == 2) {
2490 if (*bp != '/')
2491 goto invalid;
2492 bp++;
2493 passno = 3;
2494 goto again;
2495 }
2496 for (dp = bp; istoken (*dp); dp++)
2497 continue;
2498 c = *dp;
2499 *dp = '\0';
2500 if (!*bp)
2501 goto invalid;
2502 if (passno > 1) {
2503 if ((result = (mh_strcasecmp (bp, "plain") != 0)))
2504 goto out;
2505 *dp = c;
2506 for (dp++; isspace (*dp); dp++)
2507 continue;
2508 if (*dp) {
2509 if ((result = !uprf (dp, "charset")))
2510 goto out;
2511 dp += sizeof "charset" - 1;
2512 while (isspace (*dp))
2513 dp++;
2514 if (*dp++ != '=')
2515 goto invalid;
2516 while (isspace (*dp))
2517 dp++;
2518 if (*dp == '"') {
2519 if ((bp = strchr(++dp, '"')))
2520 *bp = '\0';
2521 } else {
2522 for (bp = dp; *bp; bp++)
2523 if (isspace (*bp)) {
2524 *bp = '\0';
2525 break;
2526 }
2527 }
2528 } else {
2529 /* Default character set */
2530 dp = "US-ASCII";
2531 }
2532 /* Check the character set */
2533 result = !check_charset (dp, strlen (dp));
2534 } else {
2535 if (!(result = (mh_strcasecmp (bp, "text") != 0))) {
2536 *dp = c;
2537 bp = dp;
2538 passno = 2;
2539 goto again;
2540 }
2541 }
2542 out:
2543 free (cp);
2544 if (result) {
2545 Msgs[msgnum].m_flags |= MHNYES;
2546 return result;
2547 }
2548 break;
2549 }
2550
2551 /*
2552 * Check Content-Transfer-Encoding field
2553 */
2554 if (!mh_strcasecmp (name, ENCODING_FIELD)) {
2555 cp = add (buf, NULL);
2556 while (state == FLDPLUS) {
2557 bufsz = sizeof buf;
2558 state = m_getfld (state, name, buf, &bufsz, fp);
2559 cp = add (buf, cp);
2560 }
2561 for (bp = cp; isspace (*bp); bp++)
2562 continue;
2563 for (dp = bp; istoken (*dp); dp++)
2564 continue;
2565 *dp = '\0';
2566 result = (mh_strcasecmp (bp, "7bit")
2567 && mh_strcasecmp (bp, "8bit")
2568 && mh_strcasecmp (bp, "binary"));
2569
2570 free (cp);
2571 if (result) {
2572 Msgs[msgnum].m_flags |= MHNYES;
2573 return result;
2574 }
2575 break;
2576 }
2577
2578 /*
2579 * Just skip the rest of this header
2580 * field and go to next one.
2581 */
2582 while (state == FLDPLUS) {
2583 bufsz = sizeof buf;
2584 state = m_getfld (state, name, buf, &bufsz, fp);
2585 }
2586 break;
2587
2588 /*
2589 * We've passed the message header,
2590 * so message is just text.
2591 */
2592 default:
2593 return 0;
2594 }
2595 }
2596 }
2597
2598
2599 static struct swit sortswit[] = {
2600 #define SODATE 0
2601 { "datefield field", 0 },
2602 #define SOSUBJ 1
2603 { "textfield field", 0 },
2604 #define SONSUBJ 2
2605 { "notextfield", 0 },
2606 #define SOLIMT 3
2607 { "limit days", 0 },
2608 #define SONLIMT 4
2609 { "nolimit", 0 },
2610 #define SOVERB 5
2611 { "verbose", 0 },
2612 #define SONVERB 6
2613 { "noverbose", 0 },
2614 #define SOHELP 7
2615 { "help", 0 },
2616 { NULL, 0 }
2617 };
2618
2619
2620 void
2621 sortcmd (char **args)
2622 {
2623 int msgp = 0, msgnum;
2624 char *cp, *datesw = NULL, *subjsw = NULL;
2625 char buf[BUFSIZ], *msgs[MAXARGS];
2626 struct tws tb;
2627
2628 if (fmsh) {
2629 forkcmd (args, cmd_name);
2630 return;
2631 }
2632
2633 while ((cp = *args++)) {
2634 if (*cp == '-')
2635 switch (smatch (++cp, sortswit)) {
2636 case AMBIGSW:
2637 ambigsw (cp, sortswit);
2638 return;
2639 case UNKWNSW:
2640 fprintf (stderr, "-%s unknown\n", cp);
2641 return;
2642 case SOHELP:
2643 snprintf (buf, sizeof(buf), "%s [msgs] [switches]", cmd_name);
2644 print_help (buf, sortswit, 1);
2645 return;
2646
2647 case SODATE:
2648 if (datesw) {
2649 advise (NULL, "only one date field at a time!");
2650 return;
2651 }
2652 if (!(datesw = *args++) || *datesw == '-') {
2653 advise (NULL, "missing argument to %s", args[-2]);
2654 return;
2655 }
2656 continue;
2657
2658 case SOSUBJ:
2659 if (subjsw) {
2660 advise (NULL, "only one text field at a time!");
2661 return;
2662 }
2663 if (!(subjsw = *args++) || *subjsw == '-') {
2664 advise (NULL, "missing argument to %s", args[-2]);
2665 return;
2666 }
2667 continue;
2668 case SONSUBJ:
2669 subjsw = (char *)0;
2670 continue;
2671
2672 case SOLIMT: /* too hard */
2673 if (!(cp = *args++) || *cp == '-') {
2674 advise (NULL, "missing argument to %s", args[-2]);
2675 return;
2676 }
2677 case SONLIMT:
2678 case SOVERB: /* not implemented */
2679 case SONVERB:
2680 continue;
2681 }
2682 if (*cp == '+' || *cp == '@') {
2683 advise (NULL, "sorry, no folders allowed!");
2684 return;
2685 }
2686 else
2687 msgs[msgp++] = cp;
2688 }
2689
2690 if (!msgp)
2691 msgs[msgp++] = "all";
2692 if (!datesw)
2693 datesw = "Date";
2694 for (msgnum = 0; msgnum < msgp; msgnum++)
2695 if (!m_convert (mp, msgs[msgnum]))
2696 return;
2697 seq_setprev (mp);
2698
2699 twscopy (&tb, dlocaltimenow ());
2700
2701 for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
2702 if (Msgs[msgnum].m_scanl) {
2703 free (Msgs[msgnum].m_scanl);
2704 Msgs[msgnum].m_scanl = NULL;
2705 }
2706 if (is_selected (mp, msgnum)) {
2707 if (get_fields (datesw, subjsw, msgnum, &Msgs[msgnum]))
2708 twscopy (&Msgs[msgnum].m_tb,
2709 msgnum != mp->lowsel ? &Msgs[msgnum - 1].m_tb : &tb);
2710 }
2711 else /* m_scaln is already NULL */
2712 twscopy (&Msgs[msgnum].m_tb, &tb);
2713 Msgs[msgnum].m_stats = mp->msgstats[msgnum - mp->lowoff];
2714 if (mp->curmsg == msgnum)
2715 Msgs[msgnum].m_stats |= CUR;
2716 }
2717
2718 qsort ((char *) &Msgs[mp->lowsel], mp->hghsel - mp->lowsel + 1,
2719 sizeof(struct Msg), (qsort_comp) (subjsw ? subsort : msgsort));
2720
2721 for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
2722 if (subjsw && Msgs[msgnum].m_scanl) {
2723 free (Msgs[msgnum].m_scanl); /* from subjsort */
2724 Msgs[msgnum].m_scanl = NULL;
2725 }
2726 mp->msgstats[msgnum - mp->lowoff] = Msgs[msgnum].m_stats & ~CUR;
2727 if (Msgs[msgnum].m_stats & CUR)
2728 seq_setcur (mp, msgnum);
2729 }
2730
2731 mp->msgflags |= MODIFIED;
2732 modified++;
2733 }
2734
2735
2736 /*
2737 * get_fields - parse message, and get date and subject if needed.
2738 * We'll use the msgp->m_tb tws struct for the date, and overload
2739 * the msgp->m_scanl field with our subject string.
2740 */
2741 static int
2742 get_fields (char *datesw, char *subjsw, int msgnum, struct Msg *msgp)
2743 {
2744 int state, gotdate = 0;
2745 char *bp, buf[BUFSIZ], name[NAMESZ];
2746 struct tws *tw = (struct tws *) 0;
2747 register FILE *zp;
2748
2749 zp = msh_ready (msgnum, 0);
2750 for (state = FLD;;) {
2751 int bufsz = sizeof buf;
2752 switch (state = m_getfld (state, name, buf, &bufsz, zp)) {
2753 case FLD:
2754 case FLDEOF:
2755 case FLDPLUS:
2756 if (!mh_strcasecmp (name, datesw)) {
2757 bp = getcpy (buf);
2758 while (state == FLDPLUS) {
2759 bufsz = sizeof buf;
2760 state = m_getfld (state, name, buf, &bufsz, zp);
2761 bp = add (buf, bp);
2762 }
2763 if ((tw = dparsetime (bp)) == NULL)
2764 admonish (NULL,
2765 "unable to parse %s field in message %d",
2766 datesw, msgnum);
2767 else
2768 twscopy (&(msgp->m_tb), tw);
2769 free (bp);
2770 if (!subjsw) /* not using this, or already done */
2771 break; /* all done! */
2772 gotdate++;
2773 }
2774 else if (subjsw && !mh_strcasecmp(name, subjsw)) {
2775 bp = getcpy (buf);
2776 while (state == FLDPLUS) {
2777 bufsz = sizeof buf;
2778 state = m_getfld (state, name, buf, &bufsz, zp);
2779 bp = add (buf, bp);
2780 }
2781 msgp->m_scanl = sosmash(subjsw, bp);
2782 if (gotdate)
2783 break; /* date done so we're done */
2784 else
2785 subjsw = (char *)0;/* subject done, need date */
2786 } else {
2787 while (state == FLDPLUS) { /* flush this one */
2788 bufsz = sizeof buf;
2789 state = m_getfld (state, name, buf, &bufsz, zp);
2790 }
2791 }
2792 continue;
2793
2794 case BODY:
2795 case BODYEOF:
2796 case FILEEOF:
2797 break;
2798
2799 case LENERR:
2800 case FMTERR:
2801 admonish (NULL, "format error in message %d", msgnum);
2802 if (msgp->m_scanl) { /* this might need free'd */
2803 free (msgp->m_scanl); /* probably can't use subj anyway */
2804 msgp->m_scanl = NULL;
2805 }
2806 return NOTOK;
2807
2808 default:
2809 adios (NULL, "internal error -- you lose");
2810 }
2811 break;
2812 }
2813 if (tw)
2814 return OK; /* not an error if subj not found */
2815
2816 admonish (NULL, "no %s field in message %d", datesw, msgnum);
2817 return NOTOK; /* NOTOK means use some other date */
2818 }
2819
2820
2821 /*
2822 * sort routines
2823 */
2824
2825 static int
2826 msgsort (struct Msg *a, struct Msg *b)
2827 {
2828 return twsort (&a->m_tb, &b->m_tb);
2829 }
2830
2831
2832 static int
2833 subsort (struct Msg *a, struct Msg *b)
2834 {
2835 register int i;
2836
2837 if (a->m_scanl && b->m_scanl)
2838 if ((i = strcmp (a->m_scanl, b->m_scanl)))
2839 return (i);
2840
2841 return twsort (&a->m_tb, &b->m_tb);
2842 }
2843
2844
2845 /*
2846 * try to make the subject "canonical": delete leading "re:", everything
2847 * but letters & smash letters to lower case.
2848 */
2849 static char *
2850 sosmash (char *subj, char *s)
2851 {
2852 register char *cp, *dp;
2853 register unsigned char c;
2854
2855 if (s) {
2856 cp = s;
2857 dp = s; /* dst pointer */
2858 if (!mh_strcasecmp (subj, "subject"))
2859 while ((c = *cp)) {
2860 if (! isspace(c)) {
2861 if(uprf(cp, "re:"))
2862 cp += 2;
2863 else {
2864 if (isalnum(c))
2865 *dp++ = isupper(c) ? tolower(c) : c;
2866 break;
2867 }
2868 }
2869 cp++;
2870 }
2871 while ((c = *cp++)) {
2872 if (isalnum(c))
2873 *dp++ = isupper(c) ? tolower(c) : c;
2874
2875 }
2876 *dp = '\0';
2877 }
2878 return s;
2879 }
2880
2881
2882 static int
2883 process (int msgnum, char *proc, int vecp, char **vec)
2884 {
2885 int child_id, status;
2886 char tmpfil[BUFSIZ];
2887 FILE *out;
2888 char *cp;
2889
2890 if (fmsh) {
2891 strncpy (tmpfil, m_name (msgnum), sizeof(tmpfil));
2892 context_del (pfolder);
2893 context_replace (pfolder, fmsh);/* update current folder */
2894 seq_save (mp);
2895 context_save (); /* save the context file */
2896 goto ready;
2897 }
2898
2899 cp = m_mktemp(invo_name, NULL, &out);
2900 if (cp == NULL) {
2901 /* Try again, but try to create under /tmp */
2902 int olderr = errno;
2903 cp = m_mktemp2(NULL, invo_name, NULL, &out);
2904 if (cp == NULL) {
2905 errno = olderr;
2906 advise (NULL, "unable to create temporary file");
2907 return NOTOK;
2908 }
2909 }
2910 copy_message (msgnum, out);
2911 fclose (out);
2912 strncpy(tmpfil, cp, sizeof(tmpfil));
2913
2914 ready: ;
2915 fflush (stdout);
2916 switch (child_id = fork ()) {
2917 case NOTOK:
2918 advise ("fork", "unable to");
2919 status = NOTOK;
2920 break;
2921
2922 case OK:
2923 closefds (3);
2924 SIGNAL (SIGINT, istat);
2925 SIGNAL (SIGQUIT, qstat);
2926
2927 vec[vecp++] = tmpfil;
2928 vec[vecp] = NULL;
2929
2930 execvp (proc, vec);
2931 fprintf (stderr, "unable to exec ");
2932 perror (proc);
2933 _exit (1);
2934
2935 default:
2936 status = pidXwait (child_id, NULL);
2937 break;
2938 }
2939
2940 if (!fmsh)
2941 unlink (tmpfil);
2942 return status;
2943 }
2944
2945
2946 static void
2947 copy_message (int msgnum, FILE *out)
2948 {
2949 long pos;
2950 static char buffer[BUFSIZ];
2951 register FILE *zp;
2952
2953 zp = msh_ready (msgnum, 1);
2954 if (fmsh) {
2955 while (fgets (buffer, sizeof buffer, zp) != NULL) {
2956 fputs (buffer, out);
2957 if (interrupted && out == stdout)
2958 break;
2959 }
2960 }
2961 else {
2962 pos = ftell (zp);
2963 while (fgets (buffer, sizeof buffer, zp) != NULL
2964 && pos < Msgs[msgnum].m_stop) {
2965 fputs (buffer, out);
2966 pos += (long) strlen (buffer);
2967 if (interrupted && out == stdout)
2968 break;
2969 }
2970 }
2971 }
2972
2973
2974 static void
2975 copy_digest (int msgnum, FILE *out)
2976 {
2977 char c;
2978 long pos = 0L;
2979 static char buffer[BUFSIZ];
2980 register FILE *zp;
2981
2982 c = '\n';
2983 zp = msh_ready (msgnum, 1);
2984 if (!fmsh)
2985 pos = ftell (zp);
2986 while (fgets (buffer, sizeof buffer, zp) != NULL
2987 && !fmsh && pos < Msgs[msgnum].m_stop) {
2988 if (c == '\n' && *buffer == '-')
2989 fputc (' ', out);
2990 fputs (buffer, out);
2991 c = buffer[strlen (buffer) - 1];
2992 if (!fmsh)
2993 pos += (long) strlen (buffer);
2994 if (interrupted && out == stdout)
2995 break;
2996 }
2997 }