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