]> diplodocus.org Git - nmh/blob - uip/mshcmds.c
Use pref_encoding() to select the default encoding for all headers.
[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 <h/msh.h>
18 #include <h/picksbr.h>
19 #include <h/utils.h>
20
21
22 static char delim3[] = "-------"; /* from burst.c */
23
24 static int mhlnum;
25 static FILE *mhlfp;
26
27 /*
28 * Type for a compare function for qsort. This keeps
29 * the compiler happy.
30 */
31 typedef int (*qsort_comp) (const void *, const void *);
32
33 /*
34 * static prototypes
35 */
36 static int burst (struct Msg *, int, int, int, int);
37 static void forw (char *, char *, int, char **);
38 static void rmm (void);
39 static void show (int);
40 static int eom_action (int);
41 static FILE *mhl_action (char *);
42 static int ask (int);
43 static int is_nontext (int);
44 static int get_fields (char *, char *, int, struct Msg *);
45 static int msgsort (struct Msg *, struct Msg *);
46 static int subsort (struct Msg *, struct Msg *);
47 static char *sosmash (char *, char *);
48 static int process (int, char *, int, char **);
49 static void copy_message (int, FILE *);
50 static void copy_digest (int, FILE *);
51
52 extern m_getfld_state_t gstate; /* use the gstate in scansbr.c */
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 scan_eom_action ((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) *bvector_bits (msgstat (mp,
1230 msgnum)),
1231 seq_bits (mp)));
1232 if (Msgs[msgnum].m_scanl)
1233 printf ("%s", Msgs[msgnum].m_scanl);
1234 }
1235 }
1236 }
1237
1238
1239 #define MHN_SWITCHES \
1240 X("auto", 0, MHNAUTOSW) \
1241 X("noauto", 0, MHNNAUTOSW) \
1242 X("debug", -5, MHNDEBUGSW) \
1243 X("form formfile", 4, MHNFORMSW) \
1244 X("headers", 0, MHNHEADSW) \
1245 X("noheaders", 0, MHNNHEADSW) \
1246 X("list", 0, MHNLISTSW) \
1247 X("nolist", 0, MHNNLISTSW) \
1248 X("part number", 0, MHNPARTSW) \
1249 X("realsize", 0, MHNSIZESW) \
1250 X("norealsize", 0, MHNNSIZESW) \
1251 X("rfc934mode", 0, MHNRFC934SW) \
1252 X("norfc934mode", 0, MHNNRFC934SW) \
1253 X("serialonly", 0, MHNSERIALSW) \
1254 X("noserialonly", 0, MHNNSERIALSW) \
1255 X("show", 0, MHNSHOWSW) \
1256 X("noshow", 0, MHNNSHOWSW) \
1257 X("store", 0, MHNSTORESW) \
1258 X("nostore", 0, MHNNSTORESW) \
1259 X("type content", 0, MHNTYPESW) \
1260 X("verbose", 0, MHNVERBSW) \
1261 X("noverbose", 0, MHNNVERBSW) \
1262 X("help", 0, MHNHELPSW) \
1263 X("moreproc program", -4, MHNPROGSW) \
1264 X("nomoreproc", -3, MHNNPROGSW) \
1265 X("length lines", -4, MHNLENSW) \
1266 X("width columns", -4, MHNWIDSW) \
1267
1268 #define X(sw, minchars, id) id,
1269 DEFINE_SWITCH_ENUM(MHN);
1270 #undef X
1271
1272 #define X(sw, minchars, id) { sw, minchars, id },
1273 DEFINE_SWITCH_ARRAY(MHN, mhnswit);
1274 #undef X
1275
1276
1277 void
1278 mhncmd (char **args)
1279 {
1280 int msgp = 0, vecp = 1;
1281 int msgnum;
1282 char *cp, buf[BUFSIZ];
1283 char *msgs[MAXARGS], *vec[MAXARGS];
1284
1285 if (fmsh) {
1286 forkcmd (args, cmd_name);
1287 return;
1288 }
1289 while ((cp = *args++)) {
1290 if (*cp == '-') {
1291 switch (smatch (++cp, mhnswit)) {
1292 case AMBIGSW:
1293 ambigsw (cp, mhnswit);
1294 return;
1295 case UNKWNSW:
1296 fprintf (stderr, "-%s unknown\n", cp);
1297 return;
1298 case MHNHELPSW:
1299 snprintf (buf, sizeof(buf), "%s [msgs] [switches]", cmd_name);
1300 print_help (buf, mhnswit, 1);
1301 return;
1302
1303 case MHNAUTOSW:
1304 case MHNNAUTOSW:
1305 case MHNDEBUGSW:
1306 case MHNHEADSW:
1307 case MHNNHEADSW:
1308 case MHNLISTSW:
1309 case MHNNLISTSW:
1310 case MHNSIZESW:
1311 case MHNNSIZESW:
1312 case MHNRFC934SW:
1313 case MHNNRFC934SW:
1314 case MHNSERIALSW:
1315 case MHNNSERIALSW:
1316 case MHNSHOWSW:
1317 case MHNNSHOWSW:
1318 case MHNSTORESW:
1319 case MHNNSTORESW:
1320 case MHNVERBSW:
1321 case MHNNVERBSW:
1322 case MHNNPROGSW:
1323 vec[vecp++] = --cp;
1324 continue;
1325
1326 case MHNFORMSW:
1327 case MHNPARTSW:
1328 case MHNTYPESW:
1329 case MHNPROGSW:
1330 case MHNLENSW:
1331 case MHNWIDSW:
1332 vec[vecp++] = --cp;
1333 if (!(cp = *args++) || *cp == '-') {
1334 advise (NULL, "missing argument to %s", args[-2]);
1335 return;
1336 }
1337 vec[vecp++] = cp;
1338 continue;
1339 }
1340 }
1341 if (*cp == '+' || *cp == '@') {
1342 advise (NULL, "sorry, no folders allowed!");
1343 return;
1344 } else {
1345 msgs[msgp++] = cp;
1346 }
1347 }
1348
1349 vec[0] = cmd_name;
1350 vec[vecp++] = "-file";
1351 vec[vecp] = NULL;
1352 if (!msgp)
1353 msgs[msgp++] = "cur";
1354 for (msgnum = 0; msgnum < msgp; msgnum++)
1355 if (!m_convert (mp, msgs[msgnum]))
1356 return;
1357 seq_setprev (mp);
1358
1359 interrupted = 0;
1360 for (msgnum = mp->lowsel;
1361 msgnum <= mp->hghsel && !interrupted;
1362 msgnum++)
1363 if (is_selected (mp, msgnum))
1364 if (process (msgnum, cmd_name, vecp, vec)) {
1365 unset_selected (mp, msgnum);
1366 mp->numsel--;
1367 }
1368
1369 seq_setcur (mp, mp->hghsel);
1370 }
1371
1372
1373 #define PACK_SWITCHES \
1374 X("file name", 0, PAFISW) \
1375 X("help", 0, PAHELP) \
1376
1377 #define X(sw, minchars, id) id,
1378 DEFINE_SWITCH_ENUM(PACK);
1379 #undef X
1380
1381 #define X(sw, minchars, id) { sw, minchars, id },
1382 DEFINE_SWITCH_ARRAY(PACK, packswit);
1383 #undef X
1384
1385 static int mbx_style = MMDF_FORMAT;
1386
1387 void
1388 packcmd (char **args)
1389 {
1390 int msgp = 0, md, msgnum;
1391 char *cp, *file = NULL;
1392 char buf[BUFSIZ], *msgs[MAXARGS];
1393 struct stat st;
1394
1395 if (fmsh) {
1396 forkcmd (args, cmd_name);
1397 return;
1398 }
1399
1400 while ((cp = *args++)) {
1401 if (*cp == '-')
1402 switch (smatch (++cp, packswit)) {
1403 case AMBIGSW:
1404 ambigsw (cp, packswit);
1405 return;
1406 case UNKWNSW:
1407 fprintf (stderr, "-%s unknown\n", cp);
1408 return;
1409 case PAHELP:
1410 snprintf (buf, sizeof(buf), "%s [msgs] [switches]", cmd_name);
1411 print_help (buf, packswit, 1);
1412 return;
1413
1414 case PAFISW:
1415 if (!(file = *args++) || *file == '-') {
1416 advise (NULL, "missing argument to %s", args[-2]);
1417 return;
1418 }
1419 continue;
1420 }
1421 if (*cp == '+' || *cp == '@') {
1422 advise (NULL, "sorry, no folders allowed!");
1423 return;
1424 }
1425 else
1426 msgs[msgp++] = cp;
1427 }
1428
1429 if (!file)
1430 file = "./msgbox";
1431 file = path (file, TFILE);
1432 if (stat (file, &st) == NOTOK) {
1433 if (errno != ENOENT) {
1434 advise (file, "error on file");
1435 goto done_pack;
1436 }
1437 md = getanswer (cp = concat ("Create file \"", file, "\"? ", NULL));
1438 free (cp);
1439 if (!md)
1440 goto done_pack;
1441 }
1442
1443 if (!msgp)
1444 msgs[msgp++] = "all";
1445 for (msgnum = 0; msgnum < msgp; msgnum++)
1446 if (!m_convert (mp, msgs[msgnum]))
1447 goto done_pack;
1448 seq_setprev (mp);
1449
1450 if ((md = mbx_open (file, mbx_style, getuid (), getgid (), m_gmprot ())) == NOTOK) {
1451 advise (file, "unable to open");
1452 goto done_pack;
1453 }
1454 for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
1455 if (is_selected (mp, msgnum))
1456 if (pack (file, md, msgnum) == NOTOK)
1457 break;
1458 mbx_close (file, md);
1459
1460 if (mp->hghsel != mp->curmsg)
1461 seq_setcur (mp, mp->lowsel);
1462
1463 done_pack: ;
1464 free (file);
1465 }
1466
1467
1468 int
1469 pack (char *mailbox, int md, int msgnum)
1470 {
1471 register FILE *zp;
1472
1473 if (Msgs[msgnum].m_bboard_id == 0)
1474 readid (msgnum);
1475
1476 zp = msh_ready (msgnum, 1);
1477 return mbx_write (mailbox, md, zp, Msgs[msgnum].m_bboard_id,
1478 0L, ftell (zp), Msgs[msgnum].m_stop, 1, 1);
1479 }
1480
1481
1482 int
1483 packhak (char **args)
1484 {
1485 int result;
1486 char *cp, *file = NULL;
1487
1488 while ((cp = *args++)) {
1489 if (*cp == '-')
1490 switch (smatch (++cp, packswit)) {
1491 case AMBIGSW:
1492 case UNKWNSW:
1493 case PAHELP:
1494 return NOTOK;
1495
1496 case PAFISW:
1497 if (!(file = *args++) || *file == '-')
1498 return NOTOK;
1499 continue;
1500 }
1501 if (*cp == '+' || *cp == '@')
1502 return NOTOK;
1503 }
1504
1505 file = path (file ? file : "./msgbox", TFILE);
1506 result = access (file, F_OK) == NOTOK ? OK : NOTOK;
1507 free (file);
1508
1509 return result;
1510 }
1511
1512
1513 #define PICK_SWITCHES \
1514 X("and", 0, PIANSW) \
1515 X("or", 0, PIORSW) \
1516 X("not", 0, PINTSW) \
1517 X("lbrace", 0, PILBSW) \
1518 X("rbrace", 0, PIRBSW) \
1519 X("cc pattern", 0, PICCSW) \
1520 X("date pattern", 0, PIDASW) \
1521 X("from pattern", 0, PIFRSW) \
1522 X("search pattern", 0, PISESW) \
1523 X("subject pattern", 0, PISUSW) \
1524 X("to pattern", 0, PITOSW) \
1525 X("-othercomponent pattern", 15, PIOTSW) \
1526 X("after date", 0, PIAFSW) \
1527 X("before date", 0, PIBFSW) \
1528 X("datefield field", 5, PIDFSW) \
1529 X("sequence name", 0, PISQSW) \
1530 X("public", 0, PIPUSW) \
1531 X("nopublic", 0, PINPUSW) \
1532 X("zero", 0, PIZRSW) \
1533 X("nozero", 0, PINZRSW) \
1534 X("list", 0, PILISW) \
1535 X("nolist", 0, PINLISW) \
1536 X("help", 0, PIHELP) \
1537
1538 #define X(sw, minchars, id) id,
1539 DEFINE_SWITCH_ENUM(PICK);
1540 #undef X
1541
1542 #define X(sw, minchars, id) { sw, minchars, id },
1543 DEFINE_SWITCH_ARRAY(PICK, pickswit);
1544 #undef X
1545
1546
1547 void
1548 pickcmd (char **args)
1549 {
1550 int zerosw = 1, msgp = 0;
1551 size_t seqp = 0;
1552 int vecp = 0, hi, lo, msgnum;
1553 char *cp, buf[BUFSIZ], *msgs[MAXARGS];
1554 char *seqs[NUMATTRS], *vec[MAXARGS];
1555 register FILE *zp;
1556
1557 while ((cp = *args++)) {
1558 if (*cp == '-') {
1559 if (*++cp == '-') {
1560 vec[vecp++] = --cp;
1561 goto pattern;
1562 }
1563 switch (smatch (cp, pickswit)) {
1564 case AMBIGSW:
1565 ambigsw (cp, pickswit);
1566 return;
1567 case UNKWNSW:
1568 fprintf (stderr, "-%s unknown\n", cp);
1569 return;
1570 case PIHELP:
1571 snprintf (buf, sizeof(buf), "%s [msgs] [switches]", cmd_name);
1572 print_help (buf, pickswit, 1);
1573 return;
1574
1575 case PICCSW:
1576 case PIDASW:
1577 case PIFRSW:
1578 case PISUSW:
1579 case PITOSW:
1580 case PIDFSW:
1581 case PIAFSW:
1582 case PIBFSW:
1583 case PISESW:
1584 vec[vecp++] = --cp;
1585 pattern: ;
1586 if (!(cp = *args++)) {/* allow -xyz arguments */
1587 advise (NULL, "missing argument to %s", args[-2]);
1588 return;
1589 }
1590 vec[vecp++] = cp;
1591 continue;
1592 case PIOTSW:
1593 advise (NULL, "internal error!");
1594 return;
1595 case PIANSW:
1596 case PIORSW:
1597 case PINTSW:
1598 case PILBSW:
1599 case PIRBSW:
1600 vec[vecp++] = --cp;
1601 continue;
1602
1603 case PISQSW:
1604 if (!(cp = *args++) || *cp == '-') {
1605 advise (NULL, "missing argument to %s", args[-2]);
1606 return;
1607 }
1608 if (seqp < NUMATTRS)
1609 seqs[seqp++] = cp;
1610 else {
1611 advise (NULL, "only %d sequences allowed!", NUMATTRS);
1612 return;
1613 }
1614 continue;
1615 case PIZRSW:
1616 zerosw++;
1617 continue;
1618 case PINZRSW:
1619 zerosw = 0;
1620 continue;
1621
1622 case PIPUSW: /* not implemented */
1623 case PINPUSW:
1624 case PILISW:
1625 case PINLISW:
1626 continue;
1627 }
1628 }
1629 if (*cp == '+' || *cp == '@') {
1630 advise (NULL, "sorry, no folders allowed!");
1631 return;
1632 }
1633 else
1634 msgs[msgp++] = cp;
1635 }
1636 vec[vecp] = NULL;
1637
1638 if (!msgp)
1639 msgs[msgp++] = "all";
1640 for (msgnum = 0; msgnum < msgp; msgnum++)
1641 if (!m_convert (mp, msgs[msgnum]))
1642 return;
1643 seq_setprev (mp);
1644
1645 interrupted = 0;
1646 if (!pcompile (vec, NULL))
1647 return;
1648
1649 lo = mp->lowsel;
1650 hi = mp->hghsel;
1651
1652 for (msgnum = mp->lowsel;
1653 msgnum <= mp->hghsel && !interrupted;
1654 msgnum++)
1655 if (is_selected (mp, msgnum)) {
1656 zp = msh_ready (msgnum, 1);
1657 if (pmatches (zp, msgnum, fmsh ? 0L : Msgs[msgnum].m_start,
1658 fmsh ? 0L : Msgs[msgnum].m_stop)) {
1659 if (msgnum < lo)
1660 lo = msgnum;
1661 if (msgnum > hi)
1662 hi = msgnum;
1663 }
1664 else {
1665 unset_selected (mp, msgnum);
1666 mp->numsel--;
1667 }
1668 }
1669
1670 if (interrupted)
1671 return;
1672
1673 mp->lowsel = lo;
1674 mp->hghsel = hi;
1675
1676 if (mp->numsel <= 0) {
1677 advise (NULL, "no messages match specification");
1678 return;
1679 }
1680
1681 seqs[seqp] = NULL;
1682 for (seqp = 0; seqs[seqp]; seqp++)
1683 if (!seq_addsel (mp, seqs[seqp], 0, zerosw))
1684 return;
1685
1686 printf ("%d hit%s\n", mp->numsel, mp->numsel == 1 ? "" : "s");
1687 }
1688
1689
1690 #define REPL_SWITCHES \
1691 X("annotate", 0, REANSW) \
1692 X("noannotate", 0, RENANSW) \
1693 X("cc type", 0, RECCSW) \
1694 X("nocc type", 0, RENCCSW) \
1695 X("draftfolder +folder", 0, REDFSW) \
1696 X("draftmessage msg", 0, REDMSW) \
1697 X("nodraftfolder", 0, RENDFSW) \
1698 X("editor editor", 0, REEDTSW) \
1699 X("noedit", 0, RENEDSW) \
1700 X("fcc +folder", 0, REFCCSW) \
1701 X("filter filterfile", 0, REFLTSW) \
1702 X("form formfile", 0, REFRMSW) \
1703 X("inplace", 0, REINSW) \
1704 X("noinplace", 0, RENINSW) \
1705 X("query", 0, REQUSW) \
1706 X("noquery", 0, RENQUSW) \
1707 X("whatnowproc program", 0, REWHTSW) \
1708 X("nowhatnow", 0, RENWTSW) \
1709 X("width columns", 0, REWIDSW) \
1710 X("help", 0, REHELP) \
1711
1712 #define X(sw, minchars, id) id,
1713 DEFINE_SWITCH_ENUM(REPL);
1714 #undef X
1715
1716 #define X(sw, minchars, id) { sw, minchars, id },
1717 DEFINE_SWITCH_ARRAY(REPL, replswit);
1718 #undef X
1719
1720
1721 void
1722 replcmd (char **args)
1723 {
1724 int vecp = 1;
1725 char *cp, *msg = NULL;
1726 char buf[BUFSIZ], *vec[MAXARGS];
1727
1728 if (fmsh) {
1729 forkcmd (args, cmd_name);
1730 return;
1731 }
1732
1733 while ((cp = *args++)) {
1734 if (*cp == '-')
1735 switch (smatch (++cp, replswit)) {
1736 case AMBIGSW:
1737 ambigsw (cp, replswit);
1738 return;
1739 case UNKWNSW:
1740 fprintf (stderr, "-%s unknown\n", cp);
1741 return;
1742 case REHELP:
1743 snprintf (buf, sizeof(buf), "%s [msgs] [switches]", cmd_name);
1744 print_help (buf, replswit, 1);
1745 return;
1746
1747 case REANSW: /* not implemented */
1748 case RENANSW:
1749 case REINSW:
1750 case RENINSW:
1751 continue;
1752
1753 case REQUSW:
1754 case RENQUSW:
1755 case RENDFSW:
1756 case RENEDSW:
1757 case RENWTSW:
1758 vec[vecp++] = --cp;
1759 continue;
1760
1761 case RECCSW:
1762 case RENCCSW:
1763 case REEDTSW:
1764 case REFCCSW:
1765 case REFLTSW:
1766 case REFRMSW:
1767 case REWIDSW:
1768 case REDFSW:
1769 case REDMSW:
1770 case REWHTSW:
1771 vec[vecp++] = --cp;
1772 if (!(cp = *args++) || *cp == '-') {
1773 advise (NULL, "missing argument to %s", args[-2]);
1774 return;
1775 }
1776 vec[vecp++] = cp;
1777 continue;
1778 }
1779 if (*cp == '+' || *cp == '@') {
1780 advise (NULL, "sorry, no folders allowed!");
1781 return;
1782 }
1783 else
1784 if (msg) {
1785 advise (NULL, "only one message at a time!");
1786 return;
1787 }
1788 else
1789 msg = cp;
1790 }
1791
1792 vec[0] = cmd_name;
1793 vec[vecp++] = "-file";
1794 vec[vecp] = NULL;
1795 if (!msg)
1796 msg = "cur";
1797 if (!m_convert (mp, msg))
1798 return;
1799 seq_setprev (mp);
1800
1801 if (mp->numsel > 1) {
1802 advise (NULL, "only one message at a time!");
1803 return;
1804 }
1805 process (mp->hghsel, cmd_name, vecp, vec);
1806 seq_setcur (mp, mp->hghsel);
1807 }
1808
1809
1810 #define RMM_SWITCHES \
1811 X("help", 0, RMHELP) \
1812
1813 #define X(sw, minchars, id) id,
1814 DEFINE_SWITCH_ENUM(RMM);
1815 #undef X
1816
1817 #define X(sw, minchars, id) { sw, minchars, id },
1818 DEFINE_SWITCH_ARRAY(RMM, rmmswit);
1819 #undef X
1820
1821
1822 void
1823 rmmcmd (char **args)
1824 {
1825 int msgp = 0, msgnum;
1826 char *cp, buf[BUFSIZ], *msgs[MAXARGS];
1827
1828 while ((cp = *args++)) {
1829 if (*cp == '-')
1830 switch (smatch (++cp, rmmswit)) {
1831 case AMBIGSW:
1832 ambigsw (cp, rmmswit);
1833 return;
1834 case UNKWNSW:
1835 fprintf (stderr, "-%s unknown\n", cp);
1836 return;
1837 case RMHELP:
1838 snprintf (buf, sizeof(buf), "%s [msgs] [switches]", cmd_name);
1839 print_help (buf, rmmswit, 1);
1840 return;
1841 }
1842 if (*cp == '+' || *cp == '@') {
1843 advise (NULL, "sorry, no folders allowed!");
1844 return;
1845 }
1846 else
1847 msgs[msgp++] = cp;
1848 }
1849
1850 if (!msgp)
1851 msgs[msgp++] = "cur";
1852 for (msgnum = 0; msgnum < msgp; msgnum++)
1853 if (!m_convert (mp, msgs[msgnum]))
1854 return;
1855 seq_setprev (mp);
1856
1857 rmm ();
1858 }
1859
1860
1861 static void
1862 rmm (void)
1863 {
1864 register int msgnum, vecp;
1865 register char *cp;
1866 char buffer[BUFSIZ], *vec[MAXARGS];
1867
1868 if (fmsh) {
1869 if (rmmproc) {
1870 if (mp->numsel > MAXARGS - 1) {
1871 advise (NULL, "more than %d messages for %s exec",
1872 MAXARGS - 1, rmmproc);
1873 return;
1874 }
1875 vecp = 0;
1876 for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
1877 if (is_selected (mp, msgnum))
1878 vec[vecp++] = getcpy (m_name (msgnum));
1879 vec[vecp] = NULL;
1880 forkcmd (vec, rmmproc);
1881 for (vecp = 0; vec[vecp]; vecp++)
1882 free (vec[vecp]);
1883 }
1884 else
1885 for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
1886 if (is_selected (mp, msgnum)) {
1887 strncpy (buffer, m_backup (cp = m_name (msgnum)), sizeof(buffer));
1888 if (rename (cp, buffer) == NOTOK)
1889 admonish (buffer, "unable to rename %s to", cp);
1890 }
1891 }
1892
1893 for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
1894 if (is_selected (mp, msgnum)) {
1895 set_deleted (mp, msgnum);
1896 unset_exists (mp, msgnum);
1897 }
1898
1899 if ((mp->nummsg -= mp->numsel) <= 0) {
1900 if (fmsh)
1901 admonish (NULL, "no messages remaining in +%s", fmsh);
1902 else
1903 admonish (NULL, "no messages remaining in %s", mp->foldpath);
1904 mp->lowmsg = mp->hghmsg = mp->nummsg = 0;
1905 }
1906 if (mp->lowsel == mp->lowmsg) {
1907 for (msgnum = mp->lowmsg + 1; msgnum <= mp->hghmsg; msgnum++)
1908 if (does_exist (mp, msgnum))
1909 break;
1910 mp->lowmsg = msgnum;
1911 }
1912 if (mp->hghsel == mp->hghmsg) {
1913 for (msgnum = mp->hghmsg - 1; msgnum >= mp->lowmsg; msgnum--)
1914 if (does_exist (mp, msgnum))
1915 break;
1916 mp->hghmsg = msgnum;
1917 }
1918
1919 mp->msgflags |= MODIFIED;
1920 modified++;
1921 }
1922
1923
1924 #define SCAN_SWITCHES \
1925 X("clear", 0, SCCLR) \
1926 X("noclear", 0, SCNCLR) \
1927 X("form formatfile", 0, SCFORM) \
1928 X("format string", 5, SCFMT) \
1929 X("header", 0, SCHEAD) \
1930 X("noheader", 0, SCNHEAD) \
1931 X("width columns", 0, SCWID) \
1932 X("help", 0, SCHELP) \
1933
1934 #define X(sw, minchars, id) id,
1935 DEFINE_SWITCH_ENUM(SCAN);
1936 #undef X
1937
1938 #define X(sw, minchars, id) { sw, minchars, id },
1939 DEFINE_SWITCH_ARRAY(SCAN, scanswit);
1940 #undef X
1941
1942
1943 void
1944 scancmd (char **args)
1945 {
1946 #define equiv(a,b) (a ? b && !strcmp (a, b) : !b)
1947
1948 int clearsw = 0, headersw = 0, width = 0, msgp = 0;
1949 int msgnum, optim, state;
1950 char *cp, *form = NULL, *format = NULL;
1951 char buf[BUFSIZ], *nfs, *msgs[MAXARGS];
1952 register FILE *zp;
1953 static int s_optim = 0;
1954 static char *s_form = NULL, *s_format = NULL;
1955
1956 while ((cp = *args++)) {
1957 if (*cp == '-')
1958 switch (smatch (++cp, scanswit)) {
1959 case AMBIGSW:
1960 ambigsw (cp, scanswit);
1961 return;
1962 case UNKWNSW:
1963 fprintf (stderr, "-%s unknown\n", cp);
1964 return;
1965 case SCHELP:
1966 snprintf (buf, sizeof(buf), "%s [msgs] [switches]", cmd_name);
1967 print_help (buf, scanswit, 1);
1968 return;
1969
1970 case SCCLR:
1971 clearsw++;
1972 continue;
1973 case SCNCLR:
1974 clearsw = 0;
1975 continue;
1976 case SCHEAD:
1977 headersw++;
1978 continue;
1979 case SCNHEAD:
1980 headersw = 0;
1981 continue;
1982 case SCFORM:
1983 if (!(form = *args++) || *form == '-') {
1984 advise (NULL, "missing argument to %s", args[-2]);
1985 return;
1986 }
1987 format = NULL;
1988 continue;
1989 case SCFMT:
1990 if (!(format = *args++) || *format == '-') {
1991 advise (NULL, "missing argument to %s", args[-2]);
1992 return;
1993 }
1994 form = NULL;
1995 continue;
1996 case SCWID:
1997 if (!(cp = *args++) || *cp == '-') {
1998 advise (NULL, "missing argument to %s", args[-2]);
1999 return;
2000 }
2001 width = atoi (cp);
2002 continue;
2003 }
2004 if (*cp == '+' || *cp == '@') {
2005 advise (NULL, "sorry, no folders allowed!");
2006 return;
2007 }
2008 else
2009 msgs[msgp++] = cp;
2010 }
2011
2012 if (!msgp)
2013 msgs[msgp++] = "all";
2014 for (msgnum = 0; msgnum < msgp; msgnum++)
2015 if (!m_convert (mp, msgs[msgnum]))
2016 return;
2017 seq_setprev (mp);
2018
2019 /* Get new format string */
2020 nfs = new_fs (form, format, FORMAT);
2021
2022 /* force scansbr to (re)compile format */
2023 if (scanl) {
2024 free (scanl);
2025 scanl = NULL;
2026 }
2027
2028 if (s_optim == 0) {
2029 s_optim = optim = 1;
2030 s_form = form ? getcpy (form) : NULL;
2031 s_format = format ? getcpy (format) : NULL;
2032
2033 }
2034 else
2035 optim = equiv (s_form, form) && equiv (s_format, format);
2036
2037 interrupted = 0;
2038 for (msgnum = mp->lowsel;
2039 msgnum <= mp->hghsel && !interrupted;
2040 msgnum++)
2041 if (is_selected (mp, msgnum)) {
2042 if (optim && Msgs[msgnum].m_scanl)
2043 printf ("%s", Msgs[msgnum].m_scanl);
2044 else {
2045
2046 zp = msh_ready (msgnum, 0);
2047 switch (state = scan (zp, msgnum, 0, nfs, width,
2048 msgnum == mp->curmsg,
2049 is_unseen (mp, msgnum),
2050 headersw ? (fmsh ? fmsh : mp->foldpath) : NULL,
2051 fmsh ? 0L : (long) (Msgs[msgnum].m_stop - Msgs[msgnum].m_start),
2052 1)) {
2053 case SCNMSG:
2054 case SCNENC:
2055 case SCNERR:
2056 if (optim)
2057 Msgs[msgnum].m_scanl = getcpy (scanl);
2058 break;
2059
2060 default:
2061 advise (NULL, "scan() botch (%d)", state);
2062 return;
2063
2064 case SCNEOF:
2065 printf ("%*d empty\n", DMAXFOLDER, msgnum);
2066 break;
2067 }
2068 }
2069 headersw = 0;
2070 }
2071
2072 if (clearsw)
2073 clear_screen ();
2074 }
2075
2076
2077 #define SHOW_SWITCHES \
2078 X("draft", 5, SHDRAFT) \
2079 X("form formfile", 4, SHFORM) \
2080 X("moreproc program", 4, SHPROG) \
2081 X("nomoreproc", 3, SHNPROG) \
2082 X("length lines", 4, SHLEN) \
2083 X("width columns", 4, SHWID) \
2084 X("showproc program", 4, SHSHOW) \
2085 X("noshowproc", 3, SHNSHOW) \
2086 X("header", 4, SHHEAD) \
2087 X("noheader", 3, SHNHEAD) \
2088 X("help", 0, SHHELP) \
2089
2090 #define X(sw, minchars, id) id,
2091 DEFINE_SWITCH_ENUM(SHOW);
2092 #undef X
2093
2094 #define X(sw, minchars, id) { sw, minchars, id },
2095 DEFINE_SWITCH_ARRAY(SHOW, showswit);
2096 #undef X
2097
2098
2099 void
2100 showcmd (char **args)
2101 {
2102 int headersw = 1, nshow = 0, msgp = 0, vecp = 1;
2103 int mhl = 0, seqnum = -1, mode = 0, i, msgnum;
2104 char *cp, *proc = showproc, buf[BUFSIZ];
2105 char *msgs[MAXARGS], *vec[MAXARGS];
2106
2107 if (!strcasecmp (cmd_name, "next"))
2108 mode = 1;
2109 else
2110 if (!strcasecmp (cmd_name, "prev"))
2111 mode = -1;
2112 while ((cp = *args++)) {
2113 if (*cp == '-')
2114 switch (i = smatch (++cp, showswit)) {
2115 case AMBIGSW:
2116 ambigsw (cp, showswit);
2117 return;
2118 case UNKWNSW:
2119 case SHNPROG:
2120 vec[vecp++] = --cp;
2121 continue;
2122 case SHHELP:
2123 snprintf (buf, sizeof(buf), "%s %s[switches] [switches for showproc]",
2124 cmd_name, mode ? NULL : "[msgs] ");
2125 print_help (buf, showswit, 1);
2126 return;
2127
2128 case SHFORM:
2129 case SHPROG:
2130 case SHLEN:
2131 case SHWID:
2132 vec[vecp++] = --cp;
2133 if (!(cp = *args++) || *cp == '-') {
2134 advise (NULL, "missing argument to %s", args[-2]);
2135 return;
2136 }
2137 vec[vecp++] = cp;
2138 continue;
2139 case SHHEAD:
2140 headersw++;
2141 continue;
2142 case SHNHEAD:
2143 headersw = 0;
2144 continue;
2145 case SHSHOW:
2146 if (!(proc = *args++) || *proc == '-') {
2147 advise (NULL, "missing argument to %s", args[-2]);
2148 return;
2149 }
2150 nshow = 0;
2151 continue;
2152 case SHNSHOW:
2153 nshow++;
2154 continue;
2155
2156 case SHDRAFT:
2157 advise (NULL, "sorry, -%s not allowed!", showswit[i].sw);
2158 return;
2159 }
2160 if (*cp == '+' || *cp == '@') {
2161 advise (NULL, "sorry, no folders allowed!");
2162 return;
2163 }
2164 else
2165 if (mode) {
2166 fprintf (stderr,
2167 "usage: %s [switches] [switches for showproc]\n",
2168 cmd_name);
2169 return;
2170 }
2171 else
2172 msgs[msgp++] = cp;
2173 }
2174 vec[vecp] = NULL;
2175
2176 if (!msgp)
2177 msgs[msgp++] = mode > 0 ? "next" : mode < 0 ? "prev" : "cur";
2178 for (msgnum = 0; msgnum < msgp; msgnum++)
2179 if (!m_convert (mp, msgs[msgnum]))
2180 return;
2181 seq_setprev (mp);
2182
2183 if (!nshow)
2184 for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
2185 if (is_selected (mp, msgnum) && is_nontext (msgnum)) {
2186 proc = showmimeproc;
2187 vec[vecp++] = "-file";
2188 vec[vecp] = NULL;
2189 goto finish;
2190 }
2191
2192 if (nshow)
2193 proc = catproc;
2194 else
2195 if (strcmp (showproc, "mhl") == 0) {
2196 proc = mhlproc;
2197 mhl++;
2198 }
2199
2200 finish: ;
2201 seqnum = seq_getnum (mp, "unseen");
2202 vec[0] = r1bindex (proc, '/');
2203 if (mhl) {
2204 msgp = vecp;
2205 for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
2206 if (is_selected (mp, msgnum)) {
2207 vec[vecp++] = getcpy (m_name (msgnum));
2208 if (seqnum != -1)
2209 seq_delmsg (mp, "unseen", msgnum);
2210 }
2211 vec[vecp] = NULL;
2212 if (mp->numsel == 1 && headersw)
2213 show (mp->lowsel);
2214 mhlsbr (vecp, vec, mhl_action);
2215 scan_eom_action ((int (*)()) 0);
2216 while (msgp < vecp)
2217 free (vec[msgp++]);
2218 } else {
2219 interrupted = 0;
2220 for (msgnum = mp->lowsel;
2221 msgnum <= mp->hghsel && !interrupted;
2222 msgnum++)
2223 if (is_selected (mp, msgnum)) {
2224 switch (ask (msgnum)) {
2225 case NOTOK: /* QUIT */
2226 break;
2227
2228 case OK: /* INTR */
2229 continue;
2230
2231 default:
2232 if (mp->numsel == 1 && headersw)
2233 show (msgnum);
2234 if (nshow)
2235 copy_message (msgnum, stdout);
2236 else
2237 process (msgnum, proc, vecp, vec);
2238
2239 if (seqnum != -1)
2240 seq_delmsg (mp, "unseen", msgnum);
2241 continue;
2242 }
2243 break;
2244 }
2245 }
2246
2247 seq_setcur (mp, mp->hghsel);
2248 }
2249
2250
2251 static void
2252 show (int msgnum)
2253 {
2254 if (Msgs[msgnum].m_bboard_id == 0)
2255 readid (msgnum);
2256
2257 printf ("(Message %d", msgnum);
2258 if (Msgs[msgnum].m_bboard_id > 0)
2259 printf (", %s: %d", BBoard_ID, Msgs[msgnum].m_bboard_id);
2260 printf (")\n");
2261 }
2262
2263
2264
2265 static int
2266 eom_action (int c)
2267 {
2268 NMH_UNUSED (c);
2269
2270 return (ftell (mhlfp) >= Msgs[mhlnum].m_stop);
2271 }
2272
2273
2274 static FILE *
2275 mhl_action (char *name)
2276 {
2277 int msgnum;
2278
2279 if ((msgnum = m_atoi (name)) < mp->lowmsg
2280 || msgnum > mp->hghmsg
2281 || !does_exist (mp, msgnum))
2282 return NULL;
2283 mhlnum = msgnum;
2284
2285 mhlfp = msh_ready (msgnum, 1);
2286 if (!fmsh)
2287 scan_eom_action (eom_action);
2288
2289 return mhlfp;
2290 }
2291
2292
2293
2294 static int
2295 ask (int msgnum)
2296 {
2297 char buf[BUFSIZ];
2298
2299 if (mp->numsel == 1 || !interactive || redirected)
2300 return DONE;
2301
2302 if (SOprintf ("Press <return> to list \"%d\"...", msgnum)) {
2303 if (mp->lowsel != msgnum)
2304 printf ("\n\n\n");
2305 printf ("Press <return> to list \"%d\"...", msgnum);
2306 }
2307 fflush (stdout);
2308 buf[0] = 0;
2309
2310 read (fileno (stdout), buf, sizeof buf);
2311
2312 if (strchr(buf, '\n') == NULL)
2313 putchar ('\n');
2314
2315 if (told_to_quit) {
2316 told_to_quit = interrupted = 0;
2317 return NOTOK;
2318 }
2319 if (interrupted) {
2320 interrupted = 0;
2321 return OK;
2322 }
2323
2324 return DONE;
2325 }
2326
2327
2328 #include <h/mime.h>
2329
2330 static int
2331 is_nontext (int msgnum)
2332 {
2333 int result, state;
2334 char *bp, *dp, *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 (;;) {
2345 int bufsz = sizeof buf;
2346 switch (state = m_getfld (&gstate, name, buf, &bufsz, fp)) {
2347 case FLD:
2348 case FLDPLUS:
2349 /*
2350 * Check Content-Type field
2351 */
2352 if (!strcasecmp (name, TYPE_FIELD)) {
2353 int passno;
2354 char c;
2355
2356 cp = add (buf, NULL);
2357 while (state == FLDPLUS) {
2358 bufsz = sizeof buf;
2359 state = m_getfld (&gstate, name, buf, &bufsz, fp);
2360 cp = add (buf, cp);
2361 }
2362 bp = cp;
2363 passno = 1;
2364
2365 again:
2366 for (; isspace ((unsigned char) *bp); bp++)
2367 continue;
2368 if (*bp == '(') {
2369 int i;
2370
2371 for (bp++, i = 0;;) {
2372 switch (*bp++) {
2373 case '\0':
2374 invalid:
2375 result = 0;
2376 goto out;
2377 case '\\':
2378 if (*bp++ == '\0')
2379 goto invalid;
2380 continue;
2381 case '(':
2382 i++;
2383 /* and fall... */
2384 default:
2385 continue;
2386 case ')':
2387 if (--i < 0)
2388 break;
2389 continue;
2390 }
2391 break;
2392 }
2393 }
2394 if (passno == 2) {
2395 if (*bp != '/')
2396 goto invalid;
2397 bp++;
2398 passno = 3;
2399 goto again;
2400 }
2401 for (dp = bp; istoken (*dp); dp++)
2402 continue;
2403 c = *dp;
2404 *dp = '\0';
2405 if (!*bp)
2406 goto invalid;
2407 if (passno > 1) {
2408 if ((result = (strcasecmp (bp, "plain") != 0)))
2409 goto out;
2410 *dp = c;
2411 for (dp++; isspace ((unsigned char) *dp); dp++)
2412 continue;
2413 if (*dp) {
2414 if ((result = !uprf (dp, "charset")))
2415 goto out;
2416 dp += sizeof "charset" - 1;
2417 while (isspace ((unsigned char) *dp))
2418 dp++;
2419 if (*dp++ != '=')
2420 goto invalid;
2421 while (isspace ((unsigned char) *dp))
2422 dp++;
2423 if (*dp == '"') {
2424 if ((bp = strchr(++dp, '"')))
2425 *bp = '\0';
2426 } else {
2427 for (bp = dp; *bp; bp++)
2428 if (isspace ((unsigned char) *bp)) {
2429 *bp = '\0';
2430 break;
2431 }
2432 }
2433 } else {
2434 /* Default character set */
2435 dp = "US-ASCII";
2436 }
2437 /* Check the character set */
2438 result = !check_charset (dp, strlen (dp));
2439 } else {
2440 if (!(result = (strcasecmp (bp, "text") != 0))) {
2441 *dp = c;
2442 bp = dp;
2443 passno = 2;
2444 goto again;
2445 }
2446 }
2447 out:
2448 free (cp);
2449 if (result) {
2450 Msgs[msgnum].m_flags |= MHNYES;
2451 return result;
2452 }
2453 break;
2454 }
2455
2456 /*
2457 * Check Content-Transfer-Encoding field
2458 */
2459 if (!strcasecmp (name, ENCODING_FIELD)) {
2460 cp = add (buf, NULL);
2461 while (state == FLDPLUS) {
2462 bufsz = sizeof buf;
2463 state = m_getfld (&gstate, name, buf, &bufsz, fp);
2464 cp = add (buf, cp);
2465 }
2466 for (bp = cp; isspace ((unsigned char) *bp); bp++)
2467 continue;
2468 for (dp = bp; istoken (*dp); dp++)
2469 continue;
2470 *dp = '\0';
2471 result = (strcasecmp (bp, "7bit")
2472 && strcasecmp (bp, "8bit")
2473 && strcasecmp (bp, "binary"));
2474
2475 free (cp);
2476 if (result) {
2477 Msgs[msgnum].m_flags |= MHNYES;
2478 return result;
2479 }
2480 break;
2481 }
2482
2483 /*
2484 * Just skip the rest of this header
2485 * field and go to next one.
2486 */
2487 while (state == FLDPLUS) {
2488 bufsz = sizeof buf;
2489 state = m_getfld (&gstate, name, buf, &bufsz, fp);
2490 }
2491 break;
2492
2493 /*
2494 * We've passed the message header,
2495 * so message is just text.
2496 */
2497 default:
2498 return 0;
2499 }
2500 }
2501 }
2502
2503
2504 #define SORT_SWITCHES \
2505 X("datefield field", 0, SODATE) \
2506 X("textfield field", 0, SOSUBJ) \
2507 X("notextfield", 0, SONSUBJ) \
2508 X("limit days", 0, SOLIMT) \
2509 X("nolimit", 0, SONLIMT) \
2510 X("verbose", 0, SOVERB) \
2511 X("noverbose", 0, SONVERB) \
2512 X("help", 0, SOHELP) \
2513
2514 #define X(sw, minchars, id) id,
2515 DEFINE_SWITCH_ENUM(SORT);
2516 #undef X
2517
2518 #define X(sw, minchars, id) { sw, minchars, id },
2519 DEFINE_SWITCH_ARRAY(SORT, sortswit);
2520 #undef X
2521
2522
2523 void
2524 sortcmd (char **args)
2525 {
2526 int msgp = 0, msgnum;
2527 char *cp, *datesw = NULL, *subjsw = NULL;
2528 char buf[BUFSIZ], *msgs[MAXARGS];
2529 struct tws tb;
2530
2531 if (fmsh) {
2532 forkcmd (args, cmd_name);
2533 return;
2534 }
2535
2536 while ((cp = *args++)) {
2537 if (*cp == '-')
2538 switch (smatch (++cp, sortswit)) {
2539 case AMBIGSW:
2540 ambigsw (cp, sortswit);
2541 return;
2542 case UNKWNSW:
2543 fprintf (stderr, "-%s unknown\n", cp);
2544 return;
2545 case SOHELP:
2546 snprintf (buf, sizeof(buf), "%s [msgs] [switches]", cmd_name);
2547 print_help (buf, sortswit, 1);
2548 return;
2549
2550 case SODATE:
2551 if (datesw) {
2552 advise (NULL, "only one date field at a time!");
2553 return;
2554 }
2555 if (!(datesw = *args++) || *datesw == '-') {
2556 advise (NULL, "missing argument to %s", args[-2]);
2557 return;
2558 }
2559 continue;
2560
2561 case SOSUBJ:
2562 if (subjsw) {
2563 advise (NULL, "only one text field at a time!");
2564 return;
2565 }
2566 if (!(subjsw = *args++) || *subjsw == '-') {
2567 advise (NULL, "missing argument to %s", args[-2]);
2568 return;
2569 }
2570 continue;
2571 case SONSUBJ:
2572 subjsw = (char *)0;
2573 continue;
2574
2575 case SOLIMT: /* too hard */
2576 if (!(cp = *args++) || *cp == '-') {
2577 advise (NULL, "missing argument to %s", args[-2]);
2578 return;
2579 }
2580 case SONLIMT:
2581 case SOVERB: /* not implemented */
2582 case SONVERB:
2583 continue;
2584 }
2585 if (*cp == '+' || *cp == '@') {
2586 advise (NULL, "sorry, no folders allowed!");
2587 return;
2588 }
2589 else
2590 msgs[msgp++] = cp;
2591 }
2592
2593 if (!msgp)
2594 msgs[msgp++] = "all";
2595 if (!datesw)
2596 datesw = "Date";
2597 for (msgnum = 0; msgnum < msgp; msgnum++)
2598 if (!m_convert (mp, msgs[msgnum]))
2599 return;
2600 seq_setprev (mp);
2601
2602 twscopy (&tb, dlocaltimenow ());
2603
2604 for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
2605 if (Msgs[msgnum].m_scanl) {
2606 free (Msgs[msgnum].m_scanl);
2607 Msgs[msgnum].m_scanl = NULL;
2608 }
2609 if (is_selected (mp, msgnum)) {
2610 if (get_fields (datesw, subjsw, msgnum, &Msgs[msgnum]))
2611 twscopy (&Msgs[msgnum].m_tb,
2612 msgnum != mp->lowsel ? &Msgs[msgnum - 1].m_tb : &tb);
2613 }
2614 else /* m_scaln is already NULL */
2615 twscopy (&Msgs[msgnum].m_tb, &tb);
2616 Msgs[msgnum].m_stats = bvector_create (0);
2617 bvector_copy (Msgs[msgnum].m_stats, msgstat (mp, msgnum));
2618 if (mp->curmsg == msgnum)
2619 bvector_set (Msgs[msgnum].m_stats, CUR);
2620 }
2621
2622 qsort ((char *) &Msgs[mp->lowsel], mp->hghsel - mp->lowsel + 1,
2623 sizeof(struct Msg), (qsort_comp) (subjsw ? subsort : msgsort));
2624
2625 for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
2626 if (subjsw && Msgs[msgnum].m_scanl) {
2627 free (Msgs[msgnum].m_scanl); /* from subjsort */
2628 Msgs[msgnum].m_scanl = NULL;
2629 }
2630 bvector_clear (Msgs[msgnum].m_stats, CUR);
2631 bvector_copy (msgstat (mp, msgnum), Msgs[msgnum].m_stats);
2632 if (bvector_at (Msgs[msgnum].m_stats, CUR))
2633 seq_setcur (mp, msgnum);
2634 }
2635
2636 mp->msgflags |= MODIFIED;
2637 modified++;
2638 }
2639
2640
2641 /*
2642 * get_fields - parse message, and get date and subject if needed.
2643 * We'll use the msgp->m_tb tws struct for the date, and overload
2644 * the msgp->m_scanl field with our subject string.
2645 */
2646 static int
2647 get_fields (char *datesw, char *subjsw, int msgnum, struct Msg *msgp)
2648 {
2649 int state, gotdate = 0;
2650 char *bp, buf[BUFSIZ], name[NAMESZ];
2651 struct tws *tw = (struct tws *) 0;
2652 register FILE *zp;
2653
2654 zp = msh_ready (msgnum, 0);
2655
2656 for (;;) {
2657 int bufsz = sizeof buf;
2658 switch (state = m_getfld (&gstate, name, buf, &bufsz, zp)) {
2659 case FLD:
2660 case FLDPLUS:
2661 if (!strcasecmp (name, datesw)) {
2662 bp = getcpy (buf);
2663 while (state == FLDPLUS) {
2664 bufsz = sizeof buf;
2665 state = m_getfld (&gstate, name, buf, &bufsz, zp);
2666 bp = add (buf, bp);
2667 }
2668 if ((tw = dparsetime (bp)) == NULL)
2669 admonish (NULL,
2670 "unable to parse %s field in message %d",
2671 datesw, msgnum);
2672 else
2673 twscopy (&(msgp->m_tb), tw);
2674 free (bp);
2675 if (!subjsw) /* not using this, or already done */
2676 break; /* all done! */
2677 gotdate++;
2678 }
2679 else if (subjsw && !strcasecmp(name, subjsw)) {
2680 bp = getcpy (buf);
2681 while (state == FLDPLUS) {
2682 bufsz = sizeof buf;
2683 state = m_getfld (&gstate, name, buf, &bufsz, zp);
2684 bp = add (buf, bp);
2685 }
2686 msgp->m_scanl = sosmash(subjsw, bp);
2687 if (gotdate)
2688 break; /* date done so we're done */
2689 else
2690 subjsw = (char *)0;/* subject done, need date */
2691 } else {
2692 while (state == FLDPLUS) { /* flush this one */
2693 bufsz = sizeof buf;
2694 state = m_getfld (&gstate, name, buf, &bufsz, zp);
2695 }
2696 }
2697 continue;
2698
2699 case BODY:
2700 case FILEEOF:
2701 break;
2702
2703 case LENERR:
2704 case FMTERR:
2705 admonish (NULL, "format error in message %d", msgnum);
2706 if (msgp->m_scanl) { /* this might need free'd */
2707 free (msgp->m_scanl); /* probably can't use subj anyway */
2708 msgp->m_scanl = NULL;
2709 }
2710 return NOTOK;
2711
2712 default:
2713 adios (NULL, "internal error -- you lose");
2714 }
2715 break;
2716 }
2717 if (tw)
2718 return OK; /* not an error if subj not found */
2719
2720 admonish (NULL, "no %s field in message %d", datesw, msgnum);
2721 return NOTOK; /* NOTOK means use some other date */
2722 }
2723
2724
2725 /*
2726 * sort routines
2727 */
2728
2729 static int
2730 msgsort (struct Msg *a, struct Msg *b)
2731 {
2732 return twsort (&a->m_tb, &b->m_tb);
2733 }
2734
2735
2736 static int
2737 subsort (struct Msg *a, struct Msg *b)
2738 {
2739 register int i;
2740
2741 if (a->m_scanl && b->m_scanl)
2742 if ((i = strcmp (a->m_scanl, b->m_scanl)))
2743 return (i);
2744
2745 return twsort (&a->m_tb, &b->m_tb);
2746 }
2747
2748
2749 /*
2750 * try to make the subject "canonical": delete leading "re:", everything
2751 * but letters & smash letters to lower case.
2752 */
2753 static char *
2754 sosmash (char *subj, char *s)
2755 {
2756 register char *cp, *dp;
2757 register unsigned char c;
2758
2759 if (s) {
2760 cp = s;
2761 dp = s; /* dst pointer */
2762 if (!strcasecmp (subj, "subject"))
2763 while ((c = *cp)) {
2764 if (! isspace(c)) {
2765 if(uprf(cp, "re:"))
2766 cp += 2;
2767 else {
2768 if (isalnum(c))
2769 *dp++ = isupper(c) ? tolower(c) : c;
2770 break;
2771 }
2772 }
2773 cp++;
2774 }
2775 while ((c = *cp++)) {
2776 if (isalnum(c))
2777 *dp++ = isupper(c) ? tolower(c) : c;
2778
2779 }
2780 *dp = '\0';
2781 }
2782 return s;
2783 }
2784
2785
2786 static int
2787 process (int msgnum, char *proc, int vecp, char **vec)
2788 {
2789 int child_id, status;
2790 char tmpfil[BUFSIZ];
2791 FILE *out;
2792 char *cp;
2793
2794 if (fmsh) {
2795 strncpy (tmpfil, m_name (msgnum), sizeof(tmpfil));
2796 context_del (pfolder);
2797 context_replace (pfolder, fmsh);/* update current folder */
2798 seq_save (mp);
2799 context_save (); /* save the context file */
2800 goto ready;
2801 }
2802
2803 cp = m_mktemp(invo_name, NULL, &out);
2804 if (cp == NULL) {
2805 /* Try again, but try to create under /tmp */
2806 int olderr = errno;
2807 cp = m_mktemp2(NULL, invo_name, NULL, &out);
2808 if (cp == NULL) {
2809 errno = olderr;
2810 advise (NULL, "unable to create temporary file");
2811 return NOTOK;
2812 }
2813 }
2814 copy_message (msgnum, out);
2815 fclose (out);
2816 strncpy(tmpfil, cp, sizeof(tmpfil));
2817
2818 ready: ;
2819 fflush (stdout);
2820 switch (child_id = fork ()) {
2821 case NOTOK:
2822 advise ("fork", "unable to");
2823 status = NOTOK;
2824 break;
2825
2826 case OK:
2827 closefds (3);
2828 SIGNAL (SIGINT, istat);
2829 SIGNAL (SIGQUIT, qstat);
2830
2831 vec[vecp++] = tmpfil;
2832 vec[vecp] = NULL;
2833
2834 execvp (proc, vec);
2835 fprintf (stderr, "unable to exec ");
2836 perror (proc);
2837 _exit (1);
2838
2839 default:
2840 status = pidXwait (child_id, NULL);
2841 break;
2842 }
2843
2844 if (!fmsh)
2845 unlink (tmpfil);
2846 return status;
2847 }
2848
2849
2850 static void
2851 copy_message (int msgnum, FILE *out)
2852 {
2853 long pos;
2854 static char buffer[BUFSIZ];
2855 register FILE *zp;
2856
2857 zp = msh_ready (msgnum, 1);
2858 if (fmsh) {
2859 while (fgets (buffer, sizeof buffer, zp) != NULL) {
2860 fputs (buffer, out);
2861 if (interrupted && out == stdout)
2862 break;
2863 }
2864 }
2865 else {
2866 pos = ftell (zp);
2867 while (fgets (buffer, sizeof buffer, zp) != NULL
2868 && pos < Msgs[msgnum].m_stop) {
2869 fputs (buffer, out);
2870 pos += (long) strlen (buffer);
2871 if (interrupted && out == stdout)
2872 break;
2873 }
2874 }
2875 }
2876
2877
2878 static void
2879 copy_digest (int msgnum, FILE *out)
2880 {
2881 char c;
2882 long pos = 0L;
2883 static char buffer[BUFSIZ];
2884 register FILE *zp;
2885
2886 c = '\n';
2887 zp = msh_ready (msgnum, 1);
2888 if (!fmsh)
2889 pos = ftell (zp);
2890 while (fgets (buffer, sizeof buffer, zp) != NULL
2891 && !fmsh && pos < Msgs[msgnum].m_stop) {
2892 if (c == '\n' && *buffer == '-')
2893 fputc (' ', out);
2894 fputs (buffer, out);
2895 c = buffer[strlen (buffer) - 1];
2896 if (!fmsh)
2897 pos += (long) strlen (buffer);
2898 if (interrupted && out == stdout)
2899 break;
2900 }
2901 }