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