]> diplodocus.org Git - nmh/blob - uip/mshcmds.c
Just reworded the bit about '%s' being safe not to quote (it's only safe not to
[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 <zotnet/tws/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++] = "-show";
2391 vec[vecp++] = "-file";
2392 vec[vecp] = NULL;
2393 goto finish;
2394 }
2395
2396 if (nshow)
2397 proc = catproc;
2398 else
2399 if (strcmp (showproc, "mhl") == 0) {
2400 proc = mhlproc;
2401 mhl++;
2402 }
2403
2404 finish: ;
2405 seqnum = seq_getnum (mp, "unseen");
2406 vec[0] = r1bindex (proc, '/');
2407 if (mhl) {
2408 msgp = vecp;
2409 for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
2410 if (is_selected (mp, msgnum)) {
2411 vec[vecp++] = getcpy (m_name (msgnum));
2412 if (seqnum != -1)
2413 seq_delmsg (mp, "unseen", msgnum);
2414 }
2415 vec[vecp] = NULL;
2416 if (mp->numsel == 1 && headersw)
2417 show (mp->lowsel);
2418 mhlsbr (vecp, vec, mhl_action);
2419 m_eomsbr ((int (*)()) 0);
2420 while (msgp < vecp)
2421 free (vec[msgp++]);
2422 } else {
2423 interrupted = 0;
2424 for (msgnum = mp->lowsel;
2425 msgnum <= mp->hghsel && !interrupted;
2426 msgnum++)
2427 if (is_selected (mp, msgnum)) {
2428 switch (ask (msgnum)) {
2429 case NOTOK: /* QUIT */
2430 break;
2431
2432 case OK: /* INTR */
2433 continue;
2434
2435 default:
2436 if (mp->numsel == 1 && headersw)
2437 show (msgnum);
2438 if (nshow)
2439 copy_message (msgnum, stdout);
2440 else
2441 process (msgnum, proc, vecp, vec);
2442
2443 if (seqnum != -1)
2444 seq_delmsg (mp, "unseen", msgnum);
2445 continue;
2446 }
2447 break;
2448 }
2449 }
2450
2451 seq_setcur (mp, mp->hghsel);
2452 }
2453
2454
2455 static void
2456 show (int msgnum)
2457 {
2458 if (Msgs[msgnum].m_bboard_id == 0)
2459 readid (msgnum);
2460
2461 printf ("(Message %d", msgnum);
2462 if (Msgs[msgnum].m_bboard_id > 0)
2463 printf (", %s: %d", BBoard_ID, Msgs[msgnum].m_bboard_id);
2464 printf (")\n");
2465 }
2466
2467
2468
2469 static int
2470 eom_action (int c)
2471 {
2472 return (ftell (mhlfp) >= Msgs[mhlnum].m_stop);
2473 }
2474
2475
2476 static FILE *
2477 mhl_action (char *name)
2478 {
2479 int msgnum;
2480
2481 if ((msgnum = m_atoi (name)) < mp->lowmsg
2482 || msgnum > mp->hghmsg
2483 || !does_exist (mp, msgnum))
2484 return NULL;
2485 mhlnum = msgnum;
2486
2487 mhlfp = msh_ready (msgnum, 1);
2488 if (!fmsh)
2489 m_eomsbr (eom_action);
2490
2491 return mhlfp;
2492 }
2493
2494
2495
2496 static int
2497 ask (int msgnum)
2498 {
2499 char buf[BUFSIZ];
2500
2501 if (mp->numsel == 1 || !interactive || redirected)
2502 return DONE;
2503
2504 if (SOprintf ("Press <return> to list \"%d\"...", msgnum)) {
2505 if (mp->lowsel != msgnum)
2506 printf ("\n\n\n");
2507 printf ("Press <return> to list \"%d\"...", msgnum);
2508 }
2509 fflush (stdout);
2510 buf[0] = 0;
2511
2512 #ifndef BSD42
2513 read (fileno (stdout), buf, sizeof buf);
2514 #else /* BSD42 */
2515 switch (setjmp (sigenv)) {
2516 case OK:
2517 should_intr = 1;
2518 read (fileno (stdout), buf, sizeof buf);/* fall... */
2519
2520 default:
2521 should_intr = 0;
2522 break;
2523 }
2524 #endif /* BSD42 */
2525
2526 if (strchr(buf, '\n') == NULL)
2527 putchar ('\n');
2528
2529 if (told_to_quit) {
2530 told_to_quit = interrupted = 0;
2531 return NOTOK;
2532 }
2533 if (interrupted) {
2534 interrupted = 0;
2535 return OK;
2536 }
2537
2538 return DONE;
2539 }
2540
2541
2542 #include <h/mime.h>
2543
2544 static int
2545 is_nontext (int msgnum)
2546 {
2547 int result, state;
2548 char *bp, *cp, *dp;
2549 char buf[BUFSIZ], name[NAMESZ];
2550 FILE *fp;
2551
2552 if (Msgs[msgnum].m_flags & MHNCHK)
2553 return (Msgs[msgnum].m_flags & MHNYES);
2554 Msgs[msgnum].m_flags |= MHNCHK;
2555
2556 fp = msh_ready (msgnum, 1);
2557
2558 for (state = FLD;;)
2559 switch (state = m_getfld (state, name, buf, sizeof buf, fp)) {
2560 case FLD:
2561 case FLDPLUS:
2562 case FLDEOF:
2563 /*
2564 * Check Content-Type field
2565 */
2566 if (!strcasecmp (name, TYPE_FIELD)) {
2567 int passno;
2568 char c;
2569
2570 cp = add (buf, NULL);
2571 while (state == FLDPLUS) {
2572 state = m_getfld (state, name, buf, sizeof buf, fp);
2573 cp = add (buf, cp);
2574 }
2575 bp = cp;
2576 passno = 1;
2577
2578 again:
2579 for (; isspace (*bp); bp++)
2580 continue;
2581 if (*bp == '(') {
2582 int i;
2583
2584 for (bp++, i = 0;;) {
2585 switch (*bp++) {
2586 case '\0':
2587 invalid:
2588 result = 0;
2589 goto out;
2590 case '\\':
2591 if (*bp++ == '\0')
2592 goto invalid;
2593 continue;
2594 case '(':
2595 i++;
2596 /* and fall... */
2597 default:
2598 continue;
2599 case ')':
2600 if (--i < 0)
2601 break;
2602 continue;
2603 }
2604 break;
2605 }
2606 }
2607 if (passno == 2) {
2608 if (*bp != '/')
2609 goto invalid;
2610 bp++;
2611 passno = 3;
2612 goto again;
2613 }
2614 for (dp = bp; istoken (*dp); dp++)
2615 continue;
2616 c = *dp;
2617 *dp = '\0';
2618 if (!*bp)
2619 goto invalid;
2620 if (passno > 1) {
2621 if ((result = (strcasecmp (bp, "plain") != 0)))
2622 goto out;
2623 *dp = c;
2624 for (dp++; isspace (*dp); dp++)
2625 continue;
2626 if (*dp) {
2627 if ((result = !uprf (dp, "charset")))
2628 goto out;
2629 dp += sizeof "charset" - 1;
2630 while (isspace (*dp))
2631 dp++;
2632 if (*dp++ != '=')
2633 goto invalid;
2634 while (isspace (*dp))
2635 dp++;
2636 if (*dp == '"') {
2637 if ((bp = strchr(++dp, '"')))
2638 *bp = '\0';
2639 } else {
2640 for (bp = dp; *bp; bp++)
2641 if (isspace (*bp)) {
2642 *bp = '\0';
2643 break;
2644 }
2645 }
2646 } else {
2647 /* Default character set */
2648 dp = "US-ASCII";
2649 }
2650 /* Check the character set */
2651 result = !check_charset (dp, strlen (dp));
2652 } else {
2653 if (!(result = (strcasecmp (bp, "text") != 0))) {
2654 *dp = c;
2655 bp = dp;
2656 passno = 2;
2657 goto again;
2658 }
2659 }
2660 out:
2661 free (cp);
2662 if (result) {
2663 Msgs[msgnum].m_flags |= MHNYES;
2664 return result;
2665 }
2666 break;
2667 }
2668
2669 /*
2670 * Check Content-Transfer-Encoding field
2671 */
2672 if (!strcasecmp (name, ENCODING_FIELD)) {
2673 cp = add (buf, NULL);
2674 while (state == FLDPLUS) {
2675 state = m_getfld (state, name, buf, sizeof buf, fp);
2676 cp = add (buf, cp);
2677 }
2678 for (bp = cp; isspace (*bp); bp++)
2679 continue;
2680 for (dp = bp; istoken (*dp); dp++)
2681 continue;
2682 *dp = '\0';
2683 result = (strcasecmp (bp, "7bit")
2684 && strcasecmp (bp, "8bit")
2685 && strcasecmp (bp, "binary"));
2686
2687 free (cp);
2688 if (result) {
2689 Msgs[msgnum].m_flags |= MHNYES;
2690 return result;
2691 }
2692 break;
2693 }
2694
2695 /*
2696 * Just skip the rest of this header
2697 * field and go to next one.
2698 */
2699 while (state == FLDPLUS)
2700 state = m_getfld (state, name, buf, sizeof(buf), fp);
2701 break;
2702
2703 /*
2704 * We've passed the message header,
2705 * so message is just text.
2706 */
2707 default:
2708 return 0;
2709 }
2710 }
2711
2712
2713 static struct swit sortswit[] = {
2714 #define SODATE 0
2715 { "datefield field", 0 },
2716 #define SOSUBJ 1
2717 { "textfield field", 0 },
2718 #define SONSUBJ 2
2719 { "notextfield", 0 },
2720 #define SOLIMT 3
2721 { "limit days", 0 },
2722 #define SONLIMT 4
2723 { "nolimit", 0 },
2724 #define SOVERB 5
2725 { "verbose", 0 },
2726 #define SONVERB 6
2727 { "noverbose", 0 },
2728 #define SOHELP 7
2729 { "help", 0 },
2730 { NULL, 0 }
2731 };
2732
2733
2734 void
2735 sortcmd (char **args)
2736 {
2737 int msgp = 0, msgnum;
2738 char *cp, *datesw = NULL, *subjsw = NULL;
2739 char buf[BUFSIZ], *msgs[MAXARGS];
2740 struct tws tb;
2741
2742 if (fmsh) {
2743 forkcmd (args, cmd_name);
2744 return;
2745 }
2746
2747 while ((cp = *args++)) {
2748 if (*cp == '-')
2749 switch (smatch (++cp, sortswit)) {
2750 case AMBIGSW:
2751 ambigsw (cp, sortswit);
2752 return;
2753 case UNKWNSW:
2754 fprintf (stderr, "-%s unknown\n", cp);
2755 return;
2756 case SOHELP:
2757 snprintf (buf, sizeof(buf), "%s [msgs] [switches]", cmd_name);
2758 print_help (buf, sortswit, 1);
2759 return;
2760
2761 case SODATE:
2762 if (datesw) {
2763 advise (NULL, "only one date field at a time!");
2764 return;
2765 }
2766 if (!(datesw = *args++) || *datesw == '-') {
2767 advise (NULL, "missing argument to %s", args[-2]);
2768 return;
2769 }
2770 continue;
2771
2772 case SOSUBJ:
2773 if (subjsw) {
2774 advise (NULL, "only one text field at a time!");
2775 return;
2776 }
2777 if (!(subjsw = *args++) || *subjsw == '-') {
2778 advise (NULL, "missing argument to %s", args[-2]);
2779 return;
2780 }
2781 continue;
2782 case SONSUBJ:
2783 subjsw = (char *)0;
2784 continue;
2785
2786 case SOLIMT: /* too hard */
2787 if (!(cp = *args++) || *cp == '-') {
2788 advise (NULL, "missing argument to %s", args[-2]);
2789 return;
2790 }
2791 case SONLIMT:
2792 case SOVERB: /* not implemented */
2793 case SONVERB:
2794 continue;
2795 }
2796 if (*cp == '+' || *cp == '@') {
2797 advise (NULL, "sorry, no folders allowed!");
2798 return;
2799 }
2800 else
2801 msgs[msgp++] = cp;
2802 }
2803
2804 if (!msgp)
2805 msgs[msgp++] = "all";
2806 if (!datesw)
2807 datesw = "Date";
2808 for (msgnum = 0; msgnum < msgp; msgnum++)
2809 if (!m_convert (mp, msgs[msgnum]))
2810 return;
2811 seq_setprev (mp);
2812
2813 twscopy (&tb, dlocaltimenow ());
2814
2815 for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
2816 if (Msgs[msgnum].m_scanl) {
2817 free (Msgs[msgnum].m_scanl);
2818 Msgs[msgnum].m_scanl = NULL;
2819 }
2820 if (is_selected (mp, msgnum)) {
2821 if (get_fields (datesw, subjsw, msgnum, &Msgs[msgnum]))
2822 twscopy (&Msgs[msgnum].m_tb,
2823 msgnum != mp->lowsel ? &Msgs[msgnum - 1].m_tb : &tb);
2824 }
2825 else /* m_scaln is already NULL */
2826 twscopy (&Msgs[msgnum].m_tb, &tb);
2827 Msgs[msgnum].m_stats = mp->msgstats[msgnum - mp->lowoff];
2828 if (mp->curmsg == msgnum)
2829 Msgs[msgnum].m_stats |= CUR;
2830 }
2831
2832 qsort ((char *) &Msgs[mp->lowsel], mp->hghsel - mp->lowsel + 1,
2833 sizeof(struct Msg), (qsort_comp) (subjsw ? subsort : msgsort));
2834
2835 for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
2836 if (subjsw && Msgs[msgnum].m_scanl) {
2837 free (Msgs[msgnum].m_scanl); /* from subjsort */
2838 Msgs[msgnum].m_scanl = NULL;
2839 }
2840 mp->msgstats[msgnum - mp->lowoff] = Msgs[msgnum].m_stats & ~CUR;
2841 if (Msgs[msgnum].m_stats & CUR)
2842 seq_setcur (mp, msgnum);
2843 }
2844
2845 mp->msgflags |= MODIFIED;
2846 modified++;
2847 }
2848
2849
2850 /*
2851 * get_fields - parse message, and get date and subject if needed.
2852 * We'll use the msgp->m_tb tws struct for the date, and overload
2853 * the msgp->m_scanl field with our subject string.
2854 */
2855 static int
2856 get_fields (char *datesw, char *subjsw, int msgnum, struct Msg *msgp)
2857 {
2858 int state, gotdate = 0;
2859 char *bp, buf[BUFSIZ], name[NAMESZ];
2860 struct tws *tw = (struct tws *) 0;
2861 register FILE *zp;
2862
2863 zp = msh_ready (msgnum, 0);
2864 for (state = FLD;;) {
2865 switch (state = m_getfld (state, name, buf, sizeof buf, zp)) {
2866 case FLD:
2867 case FLDEOF:
2868 case FLDPLUS:
2869 if (!strcasecmp (name, datesw)) {
2870 bp = getcpy (buf);
2871 while (state == FLDPLUS) {
2872 state = m_getfld (state, name, buf, sizeof buf, zp);
2873 bp = add (buf, bp);
2874 }
2875 if ((tw = dparsetime (bp)) == NULL)
2876 admonish (NULL,
2877 "unable to parse %s field in message %d",
2878 datesw, msgnum);
2879 else
2880 twscopy (&(msgp->m_tb), tw);
2881 free (bp);
2882 if (!subjsw) /* not using this, or already done */
2883 break; /* all done! */
2884 gotdate++;
2885 }
2886 else if (subjsw && !strcasecmp(name, subjsw)) {
2887 bp = getcpy (buf);
2888 while (state == FLDPLUS) {
2889 state = m_getfld (state, name, buf, sizeof buf, zp);
2890 bp = add (buf, bp);
2891 }
2892 msgp->m_scanl = sosmash(subjsw, bp);
2893 if (gotdate)
2894 break; /* date done so we're done */
2895 else
2896 subjsw = (char *)0;/* subject done, need date */
2897 } else {
2898 while (state == FLDPLUS) /* flush this one */
2899 state = m_getfld (state, name, buf, sizeof buf, zp);
2900 }
2901 continue;
2902
2903 case BODY:
2904 case BODYEOF:
2905 case FILEEOF:
2906 break;
2907
2908 case LENERR:
2909 case FMTERR:
2910 admonish (NULL, "format error in message %d", msgnum);
2911 if (msgp->m_scanl) { /* this might need free'd */
2912 free (msgp->m_scanl); /* probably can't use subj anyway */
2913 msgp->m_scanl = NULL;
2914 }
2915 return NOTOK;
2916
2917 default:
2918 adios (NULL, "internal error -- you lose");
2919 }
2920 break;
2921 }
2922 if (tw)
2923 return OK; /* not an error if subj not found */
2924
2925 admonish (NULL, "no %s field in message %d", datesw, msgnum);
2926 return NOTOK; /* NOTOK means use some other date */
2927 }
2928
2929
2930 /*
2931 * sort routines
2932 */
2933
2934 static int
2935 msgsort (struct Msg *a, struct Msg *b)
2936 {
2937 return twsort (&a->m_tb, &b->m_tb);
2938 }
2939
2940
2941 static int
2942 subsort (struct Msg *a, struct Msg *b)
2943 {
2944 register int i;
2945
2946 if (a->m_scanl && b->m_scanl)
2947 if ((i = strcmp (a->m_scanl, b->m_scanl)))
2948 return (i);
2949
2950 return twsort (&a->m_tb, &b->m_tb);
2951 }
2952
2953
2954 /*
2955 * try to make the subject "canonical": delete leading "re:", everything
2956 * but letters & smash letters to lower case.
2957 */
2958 static char *
2959 sosmash (char *subj, char *s)
2960 {
2961 register char *cp, *dp, c;
2962
2963 if (s) {
2964 cp = s;
2965 dp = s; /* dst pointer */
2966 if (!strcasecmp (subj, "subject"))
2967 while ((c = *cp)) {
2968 if (! isspace(c)) {
2969 if(uprf(cp, "re:"))
2970 cp += 2;
2971 else {
2972 if (isalnum(c))
2973 *dp++ = isupper(c) ? tolower(c) : c;
2974 break;
2975 }
2976 }
2977 cp++;
2978 }
2979 while ((c = *cp++)) {
2980 if (isalnum(c))
2981 *dp++ = isupper(c) ? tolower(c) : c;
2982
2983 }
2984 *dp = '\0';
2985 }
2986 return s;
2987 }
2988
2989
2990 static int
2991 process (int msgnum, char *proc, int vecp, char **vec)
2992 {
2993 int child_id, status;
2994 char tmpfil[80];
2995 FILE *out;
2996
2997 if (fmsh) {
2998 strncpy (tmpfil, m_name (msgnum), sizeof(tmpfil));
2999 context_del (pfolder);
3000 context_replace (pfolder, fmsh);/* update current folder */
3001 seq_save (mp);
3002 context_save (); /* save the context file */
3003 goto ready;
3004 }
3005
3006 strncpy (tmpfil, m_scratch ("", invo_name), sizeof(tmpfil));
3007 if ((out = fopen (tmpfil, "w")) == NULL) {
3008 int olderr;
3009 extern int errno;
3010 char newfil[80];
3011
3012 olderr = errno;
3013 strncpy (newfil, m_tmpfil (invo_name), sizeof(newfil));
3014 if ((out = fopen (newfil, "w")) == NULL) {
3015 errno = olderr;
3016 advise (tmpfil, "unable to create temporary file");
3017 return NOTOK;
3018 } else {
3019 strncpy (tmpfil, newfil, sizeof(tmpfil));
3020 }
3021 }
3022 copy_message (msgnum, out);
3023 fclose (out);
3024
3025 ready: ;
3026 fflush (stdout);
3027 switch (child_id = fork ()) {
3028 case NOTOK:
3029 advise ("fork", "unable to");
3030 status = NOTOK;
3031 break;
3032
3033 case OK:
3034 closefds (3);
3035 SIGNAL (SIGINT, istat);
3036 SIGNAL (SIGQUIT, qstat);
3037
3038 vec[vecp++] = tmpfil;
3039 vec[vecp] = NULL;
3040
3041 execvp (proc, vec);
3042 fprintf (stderr, "unable to exec ");
3043 perror (proc);
3044 _exit (1);
3045
3046 default:
3047 status = pidXwait (child_id, NULL);
3048 break;
3049 }
3050
3051 if (!fmsh)
3052 unlink (tmpfil);
3053 return status;
3054 }
3055
3056
3057 static void
3058 copy_message (int msgnum, FILE *out)
3059 {
3060 long pos;
3061 static char buffer[BUFSIZ];
3062 register FILE *zp;
3063
3064 zp = msh_ready (msgnum, 1);
3065 if (fmsh) {
3066 while (fgets (buffer, sizeof buffer, zp) != NULL) {
3067 fputs (buffer, out);
3068 if (interrupted && out == stdout)
3069 break;
3070 }
3071 }
3072 else {
3073 pos = ftell (zp);
3074 while (fgets (buffer, sizeof buffer, zp) != NULL
3075 && pos < Msgs[msgnum].m_stop) {
3076 fputs (buffer, out);
3077 pos += (long) strlen (buffer);
3078 if (interrupted && out == stdout)
3079 break;
3080 }
3081 }
3082 }
3083
3084
3085 static void
3086 copy_digest (int msgnum, FILE *out)
3087 {
3088 char c;
3089 long pos;
3090 static char buffer[BUFSIZ];
3091 register FILE *zp;
3092
3093 c = '\n';
3094 zp = msh_ready (msgnum, 1);
3095 if (!fmsh)
3096 pos = ftell (zp);
3097 while (fgets (buffer, sizeof buffer, zp) != NULL
3098 && !fmsh && pos < Msgs[msgnum].m_stop) {
3099 if (c == '\n' && *buffer == '-')
3100 fputc (' ', out);
3101 fputs (buffer, out);
3102 c = buffer[strlen (buffer) - 1];
3103 if (!fmsh)
3104 pos += (long) strlen (buffer);
3105 if (interrupted && out == stdout)
3106 break;
3107 }
3108 }