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