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