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