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