]> diplodocus.org Git - nmh/blob - uip/msh.c
With -messageid random, make the part after the @ more resemble a
[nmh] / uip / msh.c
1
2 /*
3 * msh.c -- The nmh shell
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 /*
11 * TODO:
12 * Keep more status information in maildrop map
13 */
14
15 #include <h/mh.h>
16 #include <fcntl.h>
17 #include <h/signals.h>
18 #include <h/dropsbr.h>
19 #include <h/fmt_scan.h>
20 #include <h/scansbr.h>
21 #include <h/tws.h>
22 #include <h/mts.h>
23 #include <h/utils.h>
24
25 #include <termios.h>
26
27 #include <pwd.h>
28 #include <setjmp.h>
29 #include <signal.h>
30 #include <h/msh.h>
31 #include <h/vmhsbr.h>
32
33 #define QUOTE '\\' /* sigh */
34
35 static struct swit switches[] = {
36 #define IDSW 0
37 { "idstart number", -7 }, /* interface from bbc */
38 #define FDSW 1
39 { "idstop number", -6 }, /* .. */
40 #define QDSW 2
41 { "idquit number", -6 }, /* .. */
42 #define NMSW 3
43 { "idname BBoard", -6 }, /* .. */
44 #define PRMPTSW 4
45 { "prompt string", 0 },
46 #define SCANSW 5
47 { "scan", 0 },
48 #define NSCANSW 6
49 { "noscan", 0 },
50 #define READSW 7
51 { "vmhread fd", -7 },
52 #define WRITESW 8
53 { "vmhwrite fd", -8 },
54 #define PREADSW 9
55 { "popread fd", -7 },
56 #define PWRITSW 10
57 { "popwrite fd", -8 },
58 #define TCURSW 11
59 { "topcur", 0 },
60 #define NTCURSW 12
61 { "notopcur", 0 },
62 #define VERSIONSW 13
63 { "version", 0 },
64 #define HELPSW 14
65 { "help", 0 },
66 { NULL, 0 }
67 };
68
69 static int mbx_style = MMDF_FORMAT;
70
71 /*
72 * FOLDER
73 */
74 char*fmsh = NULL; /* folder instead of file */
75 int modified; /* command modified folder */
76 struct msgs *mp; /* used a lot */
77 static int nMsgs = 0;
78 struct Msg *Msgs = NULL; /* Msgs[0] not used */
79 static FILE *fp; /* input file */
80 static FILE *yp = NULL; /* temporary file */
81 static int mode; /* mode of file */
82 static int numfds = 0; /* number of files cached */
83 static int maxfds = 0; /* number of files cached to be cached */
84 static time_t mtime = (time_t) 0; /* mtime of file */
85
86 /*
87 * VMH
88 */
89 #define ALARM ((unsigned int) 10)
90 #define ttyN(c) ttyNaux ((c), NULL)
91
92 static int vmh = 0;
93
94 static int vmhpid = OK;
95 static int vmhfd0;
96 static int vmhfd1;
97 static int vmhfd2;
98
99 static int vmhtty = NOTOK;
100
101 #define SCAN 1
102 #define STATUS 2
103 #define DISPLAY 3
104 #define NWIN DISPLAY
105
106 static int topcur = 0;
107
108 static int numwins = 0;
109 static int windows[NWIN + 1];
110
111 static jmp_buf peerenv;
112
113 /*
114 * PARENT
115 */
116 static int pfd = NOTOK; /* fd parent is reading from */
117 static int ppid = 0; /* pid of parent */
118
119 /*
120 * COMMAND
121 */
122 int interactive; /* running from a /dev/tty */
123 int redirected; /* re-directing output */
124 FILE *sp = NULL; /* original stdout */
125
126 char *cmd_name; /* command being run */
127 char myfilter[BUFSIZ]; /* path to mhl.forward */
128
129 static char *myprompt = "(%s) ";/* prompting string */
130
131 /*
132 * BBOARDS
133 */
134 static int gap; /* gap in BBoard-ID:s */
135 static char *myname = NULL; /* BBoard name */
136 char *BBoard_ID = "BBoard-ID"; /* BBoard-ID constant */
137
138 /*
139 * SIGNALS
140 */
141 SIGNAL_HANDLER istat; /* original SIGINT */
142 static SIGNAL_HANDLER pstat; /* current SIGPIPE */
143 SIGNAL_HANDLER qstat; /* original SIGQUIT */
144
145 #ifdef SIGTSTP
146 SIGNAL_HANDLER tstat; /* original SIGTSTP */
147 #endif
148
149 int interrupted; /* SIGINT detected */
150 int broken_pipe; /* SIGPIPE detected */
151 int told_to_quit; /* SIGQUIT detected */
152
153 /*
154 * prototypes
155 */
156 void fsetup (char *);
157 void setup (char *);
158 void readids (int);
159 int readid (int);
160 void display_info (int);
161 int expand (char *);
162 void m_reset (void);
163 void seq_setcur (struct msgs *, int);
164 void padios (char *, char *, ...);
165 void padvise (char *, char *, ...);
166
167
168 /*
169 * static prototypes
170 */
171 static void msh (int);
172 static int read_map (char *, long);
173 static int read_file (long, int);
174
175 static void m_gMsgs (int);
176 static int check_folder (int);
177 static void scanrange (int, int);
178 static void scanstring (char *);
179 static void write_ids (void);
180 static void quit (void);
181 static int getargs (char *, struct swit *, struct Cmd *);
182 static int getcmds (struct swit *, struct Cmd *, int);
183 static int parse (char *, struct Cmd *);
184 static int init_io (struct Cmd *, int);
185 static int initaux_io (struct Cmd *);
186 static void fin_io (struct Cmd *, int);
187 static void finaux_io (struct Cmd *);
188 static void m_init (void);
189 static void intrser (int);
190 static void pipeser (int);
191 static void quitser (int);
192 static void alrmser (int);
193 static int pINI (void);
194 static int pQRY (char *, int);
195 static int pQRY1 (int);
196 static int pQRY2 (void);
197 static int pCMD (char *, struct swit *, struct Cmd *);
198 static int pFIN (void);
199 static int peerwait (void);
200 static int ttyNaux (struct Cmd *, char *);
201 static int ttyR (struct Cmd *);
202 static int winN (struct Cmd *, int, int);
203 static int winR (struct Cmd *);
204 static int winX (int);
205
206
207 int
208 main (int argc, char **argv)
209 {
210 int id = 0, scansw = 0, vmh1 = 0, vmh2 = 0;
211 char *cp, *file = NULL, *folder = NULL;
212 char **argp, **arguments, buf[BUFSIZ];
213
214 #ifdef LOCALE
215 setlocale(LC_ALL, "");
216 #endif
217 invo_name = r1bindex (argv[0], '/');
218
219 /* read user profile/context */
220 context_read();
221
222 mts_init (invo_name);
223 arguments = getarguments (invo_name, argc,argv, 1);
224 argp = arguments;
225
226 while ((cp = *argp++)) {
227 if (*cp == '-')
228 switch (smatch (++cp, switches)) {
229 case AMBIGSW:
230 ambigsw (cp, switches);
231 done (1);
232 case UNKWNSW:
233 adios (NULL, "-%s unknown", cp);
234
235 case HELPSW:
236 snprintf (buf, sizeof(buf), "%s [switches] file", invo_name);
237 print_help (buf, switches, 1);
238 done (0);
239 case VERSIONSW:
240 print_version(invo_name);
241 done (0);
242
243 case IDSW:
244 if (!(cp = *argp++) || *cp == '-')
245 adios (NULL, "missing argument to %s", argp[-2]);
246 if ((id = atoi (cp)) < 1)
247 adios (NULL, "bad argument %s %s", argp[-2], cp);
248 continue;
249 case FDSW:
250 if (!(cp = *argp++) || *cp == '-')
251 adios (NULL, "missing argument to %s", argp[-2]);
252 if ((pfd = atoi (cp)) <= 1)
253 adios (NULL, "bad argument %s %s", argp[-2], cp);
254 continue;
255 case QDSW:
256 if (!(cp = *argp++) || *cp == '-')
257 adios (NULL, "missing argument to %s", argp[-2]);
258 if ((ppid = atoi (cp)) <= 1)
259 adios (NULL, "bad argument %s %s", argp[-2], cp);
260 continue;
261 case NMSW:
262 if (!(myname = *argp++) || *myname == '-')
263 adios (NULL, "missing argument to %s", argp[-2]);
264 continue;
265
266 case SCANSW:
267 scansw++;
268 continue;
269 case NSCANSW:
270 scansw = 0;
271 continue;
272
273 case PRMPTSW:
274 if (!(myprompt = *argp++) || *myprompt == '-')
275 adios (NULL, "missing argument to %s", argp[-2]);
276 continue;
277
278 case READSW:
279 if (!(cp = *argp++) || *cp == '-')
280 adios (NULL, "missing argument to %s", argp[-2]);
281 if ((vmh1 = atoi (cp)) < 1)
282 adios (NULL, "bad argument %s %s", argp[-2], cp);
283 continue;
284 case WRITESW:
285 if (!(cp = *argp++) || *cp == '-')
286 adios (NULL, "missing argument to %s", argp[-2]);
287 if ((vmh2 = atoi (cp)) < 1)
288 adios (NULL, "bad argument %s %s", argp[-2], cp);
289 continue;
290
291 case PREADSW:
292 if (!(cp = *argp++) || *cp == '-')
293 adios (NULL, "missing argument to %s", argp[-2]);
294 continue;
295 case PWRITSW:
296 if (!(cp = *argp++) || *cp == '-')
297 adios (NULL, "missing argument to %s", argp[-2]);
298 continue;
299
300 case TCURSW:
301 topcur++;
302 continue;
303 case NTCURSW:
304 topcur = 0;
305 continue;
306 }
307 if (*cp == '+' || *cp == '@') {
308 if (folder)
309 adios (NULL, "only one folder at a time!");
310 else
311 folder = pluspath (cp);
312 }
313 else
314 if (file)
315 adios (NULL, "only one file at a time!");
316 else
317 file = cp;
318 }
319
320 if (!file && !folder)
321 file = "./msgbox";
322 if (file && folder)
323 adios (NULL, "use a file or a folder, not both");
324 strncpy (myfilter, etcpath (mhlforward), sizeof(myfilter));
325 #ifdef FIOCLEX
326 if (pfd > 1)
327 ioctl (pfd, FIOCLEX, NULL);
328 #endif /* FIOCLEX */
329
330 istat = SIGNAL2 (SIGINT, intrser);
331 qstat = SIGNAL2 (SIGQUIT, quitser);
332
333 sc_width (); /* MAGIC... */
334
335 if ((vmh = vmh1 && vmh2)) {
336 rcinit (vmh1, vmh2);
337 pINI ();
338 SIGNAL (SIGINT, SIG_IGN);
339 SIGNAL (SIGQUIT, SIG_IGN);
340 #ifdef SIGTSTP
341 tstat = SIGNAL (SIGTSTP, SIG_IGN);
342 #endif /* SIGTSTP */
343 }
344
345 if (folder)
346 fsetup (folder);
347 else
348 setup (file);
349 readids (id);
350 display_info (id > 0 ? scansw : 0);
351
352 msh (id > 0 ? scansw : 0);
353
354 m_reset ();
355
356 done (0);
357 return 1;
358 }
359
360
361 static struct swit mshcmds[] = {
362 #define ADVCMD 0
363 { "advance", -7 },
364 #define ALICMD 1
365 { "ali", 0 },
366 #define EXPLCMD 2
367 { "burst", 0 },
368 #define COMPCMD 3
369 { "comp", 0 },
370 #define DISTCMD 4
371 { "dist", 0 },
372 #define EXITCMD 5
373 { "exit", 0 },
374 #define FOLDCMD 6
375 { "folder", 0 },
376 #define FORWCMD 7
377 { "forw", 0 },
378 #define HELPCMD 8
379 { "help", 0 },
380 #define INCMD 9
381 { "inc", 0 },
382 #define MARKCMD 10
383 { "mark", 0 },
384 #define MAILCMD 11
385 { "mhmail", 0 },
386 #define MHNCMD 12
387 { "mhn", 0 },
388 #define MSGKCMD 13
389 { "msgchk", 0 },
390 #define NEXTCMD 14
391 { "next", 0 },
392 #define PACKCMD 15
393 { "packf", 0 },
394 #define PICKCMD 16
395 { "pick", 0 },
396 #define PREVCMD 17
397 { "prev", 0 },
398 #define QUITCMD 18
399 { "quit", 0 },
400 #define FILECMD 19
401 { "refile", 0 },
402 #define REPLCMD 20
403 { "repl", 0 },
404 #define RMMCMD 21
405 { "rmm", 0 },
406 #define SCANCMD 22
407 { "scan", 0 },
408 #define SENDCMD 23
409 { "send", 0 },
410 #define SHOWCMD 24
411 { "show", 0 },
412 #define SORTCMD 25
413 { "sortm", 0 },
414 #define WHATCMD 26
415 { "whatnow", 0 },
416 #define WHOMCMD 27
417 { "whom", 0 },
418 { NULL, 0 }
419 };
420
421
422 static void
423 msh (int scansw)
424 {
425 int i;
426 register char *cp, **ap;
427 char prompt[BUFSIZ], *vec[MAXARGS];
428 struct Cmd typein;
429 register struct Cmd *cmdp;
430 static int once_only = ADVCMD;
431
432 snprintf (prompt, sizeof(prompt), myprompt, invo_name);
433 cmdp = &typein;
434
435 for (;;) {
436 if (yp) {
437 fclose (yp);
438 yp = NULL;
439 }
440 if (vmh) {
441 if ((i = getcmds (mshcmds, cmdp, scansw)) == EOF) {
442 rcdone ();
443 return;
444 }
445 } else {
446 check_folder (scansw);
447 if ((i = getargs (prompt, mshcmds, cmdp)) == EOF) {
448 putchar ('\n');
449 return;
450 }
451 }
452 cmd_name = mshcmds[i].sw;
453
454 switch (i) {
455 case QUITCMD:
456 quit ();
457 return;
458
459 case ADVCMD:
460 if (once_only == ADVCMD)
461 once_only = i = SHOWCMD;
462 else
463 i = mp->curmsg != mp->hghmsg ? NEXTCMD : EXITCMD;
464 cmd_name = mshcmds[i].sw;
465 /* and fall... */
466
467 case EXITCMD:
468 case EXPLCMD:
469 case FOLDCMD:
470 case FORWCMD: /* sigh */
471 case MARKCMD:
472 case NEXTCMD:
473 case PACKCMD:
474 case PICKCMD:
475 case PREVCMD:
476 case RMMCMD:
477 case SHOWCMD:
478 case SCANCMD:
479 case SORTCMD:
480 if ((cp = context_find (cmd_name))) {
481 cp = getcpy (cp);
482 ap = brkstring (cp, " ", "\n");
483 ap = copyip (ap, vec, MAXARGS);
484 } else {
485 ap = vec;
486 }
487 break;
488
489 default:
490 cp = NULL;
491 ap = vec;
492 break;
493 }
494 copyip (cmdp->args + 1, ap, MAXARGS);
495
496 m_init ();
497
498 if (!vmh && init_io (cmdp, vmh) == NOTOK) {
499 if (cp != NULL)
500 free (cp);
501 continue;
502 }
503 modified = 0;
504 redirected = vmh || cmdp->direction != STDIO;
505
506 switch (i) {
507 case ALICMD:
508 case COMPCMD:
509 case INCMD:
510 case MAILCMD:
511 case MSGKCMD:
512 case SENDCMD:
513 case WHATCMD:
514 case WHOMCMD:
515 if (!vmh || ttyN (cmdp) != NOTOK)
516 forkcmd (vec, cmd_name);
517 break;
518
519 case DISTCMD:
520 if (!vmh || ttyN (cmdp) != NOTOK)
521 distcmd (vec);
522 break;
523
524 case EXPLCMD:
525 if (!vmh || winN (cmdp, DISPLAY, 1) != NOTOK)
526 explcmd (vec);
527 break;
528
529 case FILECMD:
530 if (!vmh
531 || (filehak (vec) == OK ? ttyN (cmdp)
532 : winN (cmdp, DISPLAY, 1)) != NOTOK)
533 filecmd (vec);
534 break;
535
536 case FOLDCMD:
537 if (!vmh || winN (cmdp, DISPLAY, 1) != NOTOK)
538 foldcmd (vec);
539 break;
540
541 case FORWCMD:
542 if (!vmh || ttyN (cmdp) != NOTOK)
543 forwcmd (vec);
544 break;
545
546 case HELPCMD:
547 if (!vmh || winN (cmdp, DISPLAY, 1) != NOTOK)
548 helpcmd (vec);
549 break;
550
551 case EXITCMD:
552 case MARKCMD:
553 if (!vmh || winN (cmdp, DISPLAY, 1) != NOTOK)
554 markcmd (vec);
555 break;
556
557 case MHNCMD:
558 if (!vmh || ttyN (cmdp) != NOTOK)
559 mhncmd (vec);
560 break;
561
562 case NEXTCMD:
563 case PREVCMD:
564 case SHOWCMD:
565 if (!vmh || winN (cmdp, DISPLAY, 1) != NOTOK)
566 showcmd (vec);
567 break;
568
569 case PACKCMD:
570 if (!vmh
571 || (packhak (vec) == OK ? ttyN (cmdp)
572 : winN (cmdp, DISPLAY, 1)) != NOTOK)
573 packcmd (vec);
574 break;
575
576 case PICKCMD:
577 if (!vmh || winN (cmdp, DISPLAY, 1) != NOTOK)
578 pickcmd (vec);
579 break;
580
581 case REPLCMD:
582 if (!vmh || ttyN (cmdp) != NOTOK)
583 replcmd (vec);
584 break;
585
586 case RMMCMD:
587 if (!vmh || winN (cmdp, DISPLAY, 1) != NOTOK)
588 rmmcmd (vec);
589 break;
590
591 case SCANCMD:
592 if (!vmh || winN (cmdp, DISPLAY, 1) != NOTOK)
593 scancmd (vec);
594 break;
595
596 case SORTCMD:
597 if (!vmh || winN (cmdp, DISPLAY, 1) != NOTOK)
598 sortcmd (vec);
599 break;
600
601 default:
602 padios (NULL, "no dispatch for %s", cmd_name);
603 }
604
605 if (vmh) {
606 if (vmhtty != NOTOK)
607 ttyR (cmdp);
608 if (vmhpid > OK)
609 winR (cmdp);
610 }
611 else
612 fin_io (cmdp, vmh);
613 if (cp != NULL)
614 free (cp);
615 if (i == EXITCMD) {
616 quit ();
617 return;
618 }
619 }
620 }
621
622
623 void
624 fsetup (char *folder)
625 {
626 register int msgnum;
627 char *maildir;
628 struct stat st;
629
630 maildir = m_maildir (folder);
631 if (chdir (maildir) == NOTOK)
632 padios (maildir, "unable to change directory to");
633
634 /* read folder and create message structure */
635 if (!(mp = folder_read (folder)))
636 padios (NULL, "unable to read folder %s", folder);
637
638 /* check for empty folder */
639 if (mp->nummsg == 0)
640 padios (NULL, "no messages in %s", folder);
641
642 mode = m_gmprot ();
643 mtime = stat (mp->foldpath, &st) != NOTOK ? st.st_mtime : 0;
644
645 m_gMsgs (mp->hghmsg);
646
647 for (msgnum = mp->lowmsg; msgnum <= mp->hghmsg; msgnum++) {
648 Msgs[msgnum].m_bboard_id = 0;
649 Msgs[msgnum].m_top = NOTOK;
650 Msgs[msgnum].m_start = Msgs[msgnum].m_stop = 0L;
651 Msgs[msgnum].m_scanl = NULL;
652 }
653
654 m_init ();
655
656 fmsh = getcpy (folder);
657
658 maxfds = OPEN_MAX / 2;
659
660 if ((maxfds -= 2) < 1)
661 maxfds = 1;
662 }
663
664
665 void
666 setup (char *file)
667 {
668 int i, msgp;
669 struct stat st;
670 if ((fp = fopen (file, "r")) == NULL)
671 padios (file, "unable to read");
672 #ifdef FIOCLEX
673 ioctl (fileno (fp), FIOCLEX, NULL);
674 #endif /* FIOCLEX */
675 if (fstat (fileno (fp), &st) != NOTOK) {
676 mode = (int) (st.st_mode & 0777), mtime = st.st_mtime;
677 msgp = read_map (file, (long) st.st_size);
678 }
679 else {
680 mode = m_gmprot (), mtime = 0;
681 msgp = 0;
682 }
683
684 if ((msgp = read_file (msgp ? Msgs[msgp].m_stop : 0L, msgp + 1)) < 1)
685 padios (NULL, "no messages in %s", myname ? myname : file);
686
687 if (!(mp = (struct msgs *) calloc ((size_t) 1, sizeof(*mp))))
688 padios (NULL, "unable to allocate folder storage");
689
690 if (!(mp->msgstats = calloc ((size_t) msgp + 3, sizeof(*(mp->msgstats)))))
691 padios (NULL, "unable to allocate message status storage");
692
693 mp->hghmsg = msgp;
694 mp->nummsg = msgp;
695 mp->lowmsg = 1;
696 mp->curmsg = 0;
697 mp->foldpath = getcpy (myname ? myname : file);
698 clear_folder_flags (mp);
699
700 stat (file, &st);
701 if (st.st_uid != getuid () || access (file, W_OK) == NOTOK)
702 set_readonly (mp);
703
704 mp->lowoff = 1;
705 mp->hghoff = mp->hghmsg + 1;
706
707 for (i = mp->lowmsg; i <= mp->hghmsg; i++) {
708 clear_msg_flags (mp, i);
709 set_exists (mp, i);
710 }
711 m_init ();
712
713 mp->msgattrs[0] = getcpy ("unseen");
714 mp->msgattrs[1] = NULL;
715
716 m_unknown (fp); /* the MAGIC invocation */
717 if (fmsh) {
718 free (fmsh);
719 fmsh = NULL;
720 }
721 }
722
723
724 static int
725 read_map (char *file, long size)
726 {
727 register int i, msgp;
728 register struct drop *dp, *mp;
729 struct drop *rp;
730
731 if ((i = map_read (file, size, &rp, 1)) == 0)
732 return 0;
733
734 m_gMsgs (i);
735
736 msgp = 1;
737 for (dp = rp + 1; i-- > 0; msgp++, dp++) {
738 mp = &Msgs[msgp].m_drop;
739 mp->d_id = dp->d_id;
740 mp->d_size = dp->d_size;
741 mp->d_start = dp->d_start;
742 mp->d_stop = dp->d_stop;
743 Msgs[msgp].m_scanl = NULL;
744 }
745 free ((char *) rp);
746
747 return (msgp - 1);
748 }
749
750
751 static int
752 read_file (long pos, int msgp)
753 {
754 register int i;
755 register struct drop *dp, *mp;
756 struct drop *rp;
757
758 if ((i = mbx_read (fp, pos, &rp, 1)) <= 0)
759 return (msgp - 1);
760
761 m_gMsgs ((msgp - 1) + i);
762
763 for (dp = rp; i-- > 0; msgp++, dp++) {
764 mp = &Msgs[msgp].m_drop;
765 mp->d_id = 0;
766 mp->d_size = dp->d_size;
767 mp->d_start = dp->d_start;
768 mp->d_stop = dp->d_stop;
769 Msgs[msgp].m_scanl = NULL;
770 }
771 free ((char *) rp);
772
773 return (msgp - 1);
774 }
775
776
777 static void
778 m_gMsgs (int n)
779 {
780 int nmsgs;
781
782 if (Msgs == NULL) {
783 nMsgs = n + MAXFOLDER / 2;
784 Msgs = (struct Msg *) calloc ((size_t) (nMsgs + 2), sizeof *Msgs);
785 if (Msgs == NULL)
786 padios (NULL, "unable to allocate Msgs structure");
787 return;
788 }
789
790 if (nMsgs >= n)
791 return;
792
793 nmsgs = nMsgs + n + MAXFOLDER / 2;
794 Msgs = (struct Msg *) mh_xrealloc ((char *) Msgs, (size_t) (nmsgs + 2) * sizeof *Msgs);
795 memset((char *) (Msgs + nMsgs + 2), 0, (size_t) ((nmsgs - nMsgs) * sizeof *Msgs));
796
797 nMsgs = nmsgs;
798 }
799
800
801 FILE *
802 msh_ready (int msgnum, int full)
803 {
804 register int msgp;
805 int fd;
806 char *cp;
807 NMH_UNUSED (full);
808
809 if (yp) {
810 fclose (yp);
811 yp = NULL;
812 }
813
814 if (fmsh) {
815 if ((fd = Msgs[msgnum].m_top) == NOTOK) {
816 if (numfds >= maxfds)
817 for (msgp = mp->lowmsg; msgp <= mp->hghmsg; msgp++)
818 if (Msgs[msgp].m_top != NOTOK) {
819 close (Msgs[msgp].m_top);
820 Msgs[msgp].m_top = NOTOK;
821 numfds--;
822 break;
823 }
824
825 if ((fd = open (cp = m_name (msgnum), O_RDONLY)) == NOTOK)
826 padios (cp, "unable to open message");
827 Msgs[msgnum].m_top = fd;
828 numfds++;
829 }
830
831 if ((fd = dup (fd)) == NOTOK)
832 padios ("cached message", "unable to dup");
833 if ((yp = fdopen (fd, "r")) == NULL)
834 padios (NULL, "unable to fdopen cached message");
835 fseek (yp, 0L, SEEK_SET);
836 return yp;
837 }
838
839 m_eomsbr ((int (*)()) 0); /* XXX */
840 fseek (fp, Msgs[msgnum].m_start, SEEK_SET);
841 return fp;
842 }
843
844
845 static int
846 check_folder (int scansw)
847 {
848 int seqnum, i, low, hgh, msgp;
849 struct stat st;
850
851 if (fmsh) {
852 if (stat (mp->foldpath, &st) == NOTOK)
853 padios (mp->foldpath, "unable to stat");
854 if (mtime == st.st_mtime)
855 return 0;
856 mtime = st.st_mtime;
857
858 low = mp->hghmsg + 1;
859 folder_free (mp); /* free folder/message structure */
860
861 if (!(mp = folder_read (fmsh)))
862 padios (NULL, "unable to re-read folder %s", fmsh);
863
864 hgh = mp->hghmsg;
865
866 for (msgp = mp->lowmsg; msgp <= mp->hghmsg; msgp++) {
867 if (Msgs[msgp].m_top != NOTOK) {
868 close (Msgs[msgp].m_top);
869 Msgs[msgp].m_top = NOTOK;
870 numfds--;
871 }
872 if (Msgs[msgp].m_scanl) {
873 free (Msgs[msgp].m_scanl);
874 Msgs[msgp].m_scanl = NULL;
875 }
876 }
877
878 m_init ();
879
880 if (modified || low > hgh)
881 return 1;
882 goto check_vmh;
883 }
884 if (fstat (fileno (fp), &st) == NOTOK)
885 padios (mp->foldpath, "unable to fstat");
886 if (mtime == st.st_mtime)
887 return 0;
888 mode = (int) (st.st_mode & 0777);
889 mtime = st.st_mtime;
890
891 if ((msgp = read_file (Msgs[mp->hghmsg].m_stop, mp->hghmsg + 1)) < 1)
892 padios (NULL, "no messages in %s", mp->foldpath); /* XXX */
893 if (msgp >= MAXFOLDER)
894 padios (NULL, "more than %d messages in %s", MAXFOLDER,
895 mp->foldpath);
896 if (msgp <= mp->hghmsg)
897 return 0; /* XXX */
898
899 if (!(mp = folder_realloc (mp, mp->lowoff, msgp)))
900 padios (NULL, "unable to allocate folder storage");
901
902 low = mp->hghmsg + 1, hgh = msgp;
903 seqnum = scansw ? seq_getnum (mp, "unseen") : -1;
904 for (i = mp->hghmsg + 1; i <= msgp; i++) {
905 set_exists(mp, i);
906 if (seqnum != -1)
907 add_sequence(mp, seqnum, i);
908 mp->nummsg++;
909 }
910 mp->hghmsg = msgp;
911 m_init ();
912
913 check_vmh: ;
914 if (vmh)
915 return 1;
916
917 advise (NULL, "new messages have arrived!\007");
918 if (scansw)
919 scanrange (low, hgh);
920
921 return 1;
922 }
923
924
925 static void
926 scanrange (int low, int hgh)
927 {
928 char buffer[BUFSIZ];
929
930 snprintf (buffer, sizeof(buffer), "%d-%d", low, hgh);
931 scanstring (buffer);
932 }
933
934
935 static void
936 scanstring (char *arg)
937 {
938 char *cp, **ap, *vec[MAXARGS];
939
940 /*
941 * This should be replace with a call to getarguments()
942 */
943 if ((cp = context_find (cmd_name = "scan"))) {
944 cp = getcpy (cp);
945 ap = brkstring (cp, " ", "\n");
946 ap = copyip (ap, vec, MAXARGS);
947 } else {
948 ap = vec;
949 }
950 *ap++ = arg;
951 *ap = NULL;
952 m_init ();
953 scancmd (vec);
954 if (cp != NULL)
955 free (cp);
956 }
957
958
959 void
960 readids (int id)
961 {
962 register int cur, seqnum, i=0, msgnum;
963
964 if (mp->curmsg == 0)
965 seq_setcur (mp, mp->lowmsg);
966 if (id <= 0 || (seqnum = seq_getnum (mp, "unseen")) == -1)
967 return;
968
969 for (msgnum = mp->hghmsg; msgnum >= mp->lowmsg; msgnum--)
970 add_sequence(mp, seqnum, msgnum);
971
972 if (id != 1) {
973 cur = mp->curmsg;
974
975 for (msgnum = mp->hghmsg; msgnum >= mp->lowmsg; msgnum--)
976 if (does_exist(mp, msgnum)) /* FIX */
977 if ((i = readid (msgnum)) > 0 && i < id) {
978 cur = msgnum + 1;
979 clear_sequence(mp, seqnum, msgnum);
980 break;
981 }
982 for (i = mp->lowmsg; i < msgnum; i++)
983 clear_sequence(mp, seqnum, i);
984
985 if (cur > mp->hghmsg)
986 cur = mp->hghmsg;
987
988 seq_setcur (mp, cur);
989 }
990
991 if ((gap = 1 < id && id < (i = readid (mp->lowmsg)) ? id : 0) && !vmh)
992 advise (NULL, "gap in ID:s, last seen %d, lowest present %d\n",
993 id - 1, i);
994 }
995
996
997 int
998 readid (int msgnum)
999 {
1000 int i, state;
1001 char *bp, buf[BUFSIZ], name[NAMESZ];
1002 register FILE *zp;
1003
1004 if (Msgs[msgnum].m_bboard_id)
1005 return Msgs[msgnum].m_bboard_id;
1006
1007 zp = msh_ready (msgnum, 0);
1008 for (state = FLD;;)
1009 switch (state = m_getfld (state, name, buf, sizeof(buf), zp)) {
1010 case FLD:
1011 case FLDEOF:
1012 case FLDPLUS:
1013 if (!mh_strcasecmp (name, BBoard_ID)) {
1014 bp = getcpy (buf);
1015 while (state == FLDPLUS) {
1016 state = m_getfld (state, name, buf, sizeof(buf), zp);
1017 bp = add (buf, bp);
1018 }
1019 i = atoi (bp);
1020 free (bp);
1021 if (i > 0)
1022 return (Msgs[msgnum].m_bboard_id = i);
1023 else
1024 continue;
1025 }
1026 while (state == FLDPLUS)
1027 state = m_getfld (state, name, buf, sizeof(buf), zp);
1028 if (state != FLDEOF)
1029 continue;
1030
1031 default:
1032 return 0;
1033 }
1034 }
1035
1036
1037 void
1038 display_info (int scansw)
1039 {
1040 int seqnum, sd;
1041
1042 interactive = isatty (fileno (stdout));
1043 if (sp == NULL) {
1044 if ((sd = dup (fileno (stdout))) == NOTOK)
1045 padios ("standard output", "unable to dup");
1046 #ifdef FIOCLEX
1047 ioctl (sd, FIOCLEX, NULL);
1048 #endif /* FIOCLEX */
1049 if ((sp = fdopen (sd, "w")) == NULL)
1050 padios ("standard output", "unable to fdopen");
1051 }
1052
1053 m_putenv ("mhfolder", mp->foldpath);
1054 if (vmh)
1055 return;
1056
1057 if (myname) {
1058 printf ("Reading ");
1059 if (SOprintf ("%s", myname))
1060 printf ("%s", myname);
1061 printf (", currently at message %d of %d\n",
1062 mp->curmsg, mp->hghmsg);
1063 }
1064 else {
1065 printf ("Reading ");
1066 if (fmsh)
1067 printf ("+%s", fmsh);
1068 else
1069 printf ("%s", mp->foldpath);
1070 printf (", currently at message %d of %d\n",
1071 mp->curmsg, mp->hghmsg);
1072 }
1073
1074 if (((seqnum = seq_getnum (mp, "unseen")) != -1)
1075 && scansw
1076 && in_sequence(mp, seqnum, mp->hghmsg))
1077 scanstring ("unseen");
1078 }
1079
1080
1081 static void
1082 write_ids (void)
1083 {
1084 int i = 0, seqnum, msgnum;
1085 char buffer[80];
1086
1087 if (pfd <= 1)
1088 return;
1089
1090 if ((seqnum = seq_getnum (mp, "unseen")) != -1)
1091 for (msgnum = mp->hghmsg; msgnum >= mp->lowmsg; msgnum--)
1092 if (!in_sequence(mp, seqnum, msgnum)) {
1093 if (Msgs[msgnum].m_bboard_id == 0)
1094 readid (msgnum);
1095 if ((i = Msgs[msgnum].m_bboard_id) > 0)
1096 break;
1097 }
1098
1099 snprintf (buffer, sizeof(buffer), "%d %d\n", i, Msgs[mp->hghmsg].m_bboard_id);
1100 write (pfd, buffer, sizeof(buffer));
1101 close (pfd);
1102 pfd = NOTOK;
1103 }
1104
1105
1106 static void
1107 quit (void)
1108 {
1109 int i, md, msgnum;
1110 char *cp, tmpfil[BUFSIZ];
1111 char map1[BUFSIZ], map2[BUFSIZ];
1112 struct stat st;
1113 FILE *dp;
1114
1115 if (!(mp->msgflags & MODIFIED) || is_readonly(mp) || fmsh) {
1116 if (vmh)
1117 rc2peer (RC_FIN, 0, NULL);
1118 return;
1119 }
1120
1121 if (vmh)
1122 ttyNaux (NULLCMD, "FAST");
1123 cp = NULL;
1124 if ((dp = lkfopen (mp->foldpath, "r")) == NULL) {
1125 advise (mp->foldpath, "unable to lock");
1126 if (vmh) {
1127 ttyR (NULLCMD);
1128 pFIN ();
1129 }
1130 return;
1131 }
1132 if (fstat (fileno (dp), &st) == NOTOK) {
1133 advise (mp->foldpath, "unable to stat");
1134 goto release;
1135 }
1136 if (mtime != st.st_mtime) {
1137 advise (NULL, "new messages have arrived, no update");
1138 goto release;
1139 }
1140 mode = (int) (st.st_mode & 0777);
1141
1142 if (mp->nummsg == 0) {
1143 cp = concat ("Zero file \"", mp->foldpath, "\"? ", NULL);
1144 if (getanswer (cp)) {
1145 if ((i = creat (mp->foldpath, mode)) != NOTOK)
1146 close (i);
1147 else
1148 advise (mp->foldpath, "error zero'ing");
1149 unlink (map_name (mp->foldpath));/* XXX */
1150 }
1151 goto release;
1152 }
1153
1154 cp = concat ("Update file \"", mp->foldpath, "\"? ", NULL);
1155 if (!getanswer (cp))
1156 goto release;
1157 strncpy (tmpfil, m_backup (mp->foldpath), sizeof(tmpfil));
1158 if ((md = mbx_open (tmpfil, mbx_style, st.st_uid, st.st_gid, mode)) == NOTOK) {
1159 advise (tmpfil, "unable to open");
1160 goto release;
1161 }
1162
1163 for (msgnum = mp->lowmsg; msgnum <= mp->hghmsg; msgnum++)
1164 if (does_exist(mp, msgnum) && pack (tmpfil, md, msgnum) == NOTOK) {
1165 mbx_close (tmpfil, md);
1166 unlink (tmpfil);
1167 unlink (map_name (tmpfil));
1168 goto release;
1169 }
1170 mbx_close (tmpfil, md);
1171
1172 if (rename (tmpfil, mp->foldpath) == NOTOK)
1173 admonish (mp->foldpath, "unable to rename %s to", tmpfil);
1174 else {
1175 strncpy (map1, map_name (tmpfil), sizeof(map1));
1176 strncpy (map2, map_name (mp->foldpath), sizeof(map2));
1177
1178 if (rename (map1, map2) == NOTOK) {
1179 admonish (map2, "unable to rename %s to", map1);
1180 unlink (map1);
1181 unlink (map2);
1182 }
1183 }
1184
1185 release: ;
1186 if (cp)
1187 free (cp);
1188 lkfclose (dp, mp->foldpath);
1189 if (vmh) {
1190 ttyR (NULLCMD);
1191 pFIN ();
1192 }
1193 }
1194
1195
1196 static int
1197 getargs (char *prompt, struct swit *sw, struct Cmd *cmdp)
1198 {
1199 int i;
1200 char *cp;
1201 static char buffer[BUFSIZ];
1202
1203 told_to_quit = 0;
1204 for (;;) {
1205 interrupted = 0;
1206 if (interactive) {
1207 printf ("%s", prompt);
1208 fflush (stdout);
1209 }
1210 for (cp = buffer; (i = getchar ()) != '\n';) {
1211 if (interrupted && !told_to_quit) {
1212 buffer[0] = '\0';
1213 putchar ('\n');
1214 break;
1215 }
1216 if (told_to_quit || i == EOF) {
1217 if (ppid > 0)
1218 #ifdef SIGEMT
1219 kill (ppid, SIGEMT);
1220 #else
1221 kill (ppid, SIGTERM);
1222 #endif
1223 return EOF;
1224 }
1225 if (cp < &buffer[sizeof buffer - 2])
1226 *cp++ = i;
1227 }
1228 *cp = 0;
1229
1230 if (buffer[0] == 0)
1231 continue;
1232 if (buffer[0] == '?') {
1233 printf ("commands:\n");
1234 print_sw (ALL, sw, "", stdout);
1235 printf ("type CTRL-D or use ``quit'' to leave %s\n",
1236 invo_name);
1237 continue;
1238 }
1239
1240 if (parse (buffer, cmdp) == NOTOK)
1241 continue;
1242
1243 switch (i = smatch (cmdp->args[0], sw)) {
1244 case AMBIGSW:
1245 ambigsw (cmdp->args[0], sw);
1246 continue;
1247 case UNKWNSW:
1248 printf ("say what: ``%s'' -- type ? (or help) for help\n",
1249 cmdp->args[0]);
1250 continue;
1251 default:
1252 return i;
1253 }
1254 }
1255 }
1256
1257
1258 static int
1259 getcmds (struct swit *sw, struct Cmd *cmdp, int scansw)
1260 {
1261 int i;
1262 struct record rcs, *rc;
1263
1264 rc = &rcs;
1265 initrc (rc);
1266
1267 for (;;)
1268 switch (peer2rc (rc)) {
1269 case RC_QRY:
1270 pQRY (rc->rc_data, scansw);
1271 break;
1272
1273 case RC_CMD:
1274 if ((i = pCMD (rc->rc_data, sw, cmdp)) != NOTOK)
1275 return i;
1276 break;
1277
1278 case RC_FIN:
1279 if (ppid > 0)
1280 #ifdef SIGEMT
1281 kill (ppid, SIGEMT);
1282 #else
1283 kill (ppid, SIGTERM);
1284 #endif
1285 return EOF;
1286
1287 case RC_XXX:
1288 padios (NULL, "%s", rc->rc_data);
1289
1290 default:
1291 fmt2peer (RC_ERR, "pLOOP protocol screw-up");
1292 done (1);
1293 }
1294 }
1295
1296
1297 static int
1298 parse (char *buffer, struct Cmd *cmdp)
1299 {
1300 int argp = 0;
1301 unsigned char c, *cp;
1302 char *pp;
1303
1304 cmdp->line[0] = 0;
1305 pp = cmdp->args[argp++] = cmdp->line;
1306 cmdp->redirect = NULL;
1307 cmdp->direction = STDIO;
1308 cmdp->stream = NULL;
1309
1310 for (cp = buffer; (c = *cp); cp++) {
1311 if (!isspace (c))
1312 break;
1313 }
1314 if (c == '\0') {
1315 if (vmh)
1316 fmt2peer (RC_EOF, "null command");
1317 return NOTOK;
1318 }
1319
1320 while ((c = *cp++)) {
1321 if (isspace (c)) {
1322 while (isspace (c))
1323 c = *cp++;
1324 if (c == 0)
1325 break;
1326 *pp++ = 0;
1327 cmdp->args[argp++] = pp;
1328 *pp = 0;
1329 }
1330
1331 switch (c) {
1332 case '"':
1333 for (;;) {
1334 switch (c = *cp++) {
1335 case 0:
1336 padvise (NULL, "unmatched \"");
1337 return NOTOK;
1338 case '"':
1339 break;
1340 case QUOTE:
1341 if ((c = *cp++) == 0)
1342 goto no_quoting;
1343 default:
1344 *pp++ = c;
1345 continue;
1346 }
1347 break;
1348 }
1349 continue;
1350
1351 case QUOTE:
1352 if ((c = *cp++) == 0) {
1353 no_quoting: ;
1354 padvise (NULL, "the newline character can not be quoted");
1355 return NOTOK;
1356 }
1357
1358 default: ;
1359 *pp++ = c;
1360 continue;
1361
1362 case '>':
1363 case '|':
1364 if (pp == cmdp->line) {
1365 padvise (NULL, "invalid null command");
1366 return NOTOK;
1367 }
1368 if (*cmdp->args[argp - 1] == 0)
1369 argp--;
1370 cmdp->direction = c == '>' ? CRTIO : PIPIO;
1371 if (cmdp->direction == CRTIO && (c = *cp) == '>') {
1372 cmdp->direction = APPIO;
1373 cp++;
1374 }
1375 cmdp->redirect = pp + 1;/* sigh */
1376 for (; (c = *cp); cp++)
1377 if (!isspace (c))
1378 break;
1379 if (c == 0) {
1380 padvise (NULL, cmdp->direction != PIPIO
1381 ? "missing name for redirect"
1382 : "invalid null command");
1383 return NOTOK;
1384 }
1385 strcpy (cmdp->redirect, cp);
1386 if (cmdp->direction != PIPIO) {
1387 for (; *cp; cp++)
1388 if (isspace (*cp)) {
1389 padvise (NULL, "bad name for redirect");
1390 return NOTOK;
1391 }
1392 if (expand (cmdp->redirect) == NOTOK)
1393 return NOTOK;
1394 }
1395 break;
1396 }
1397 break;
1398 }
1399
1400 *pp++ = 0;
1401 cmdp->args[argp] = NULL;
1402
1403 return OK;
1404 }
1405
1406
1407 int
1408 expand (char *redirect)
1409 {
1410 char *cp, *pp;
1411 char path[BUFSIZ];
1412 struct passwd *pw;
1413
1414 if (*redirect != '~')
1415 return OK;
1416
1417 if ((cp = strchr(pp = redirect + 1, '/')))
1418 *cp++ = 0;
1419 if (*pp == 0)
1420 pp = mypath;
1421 else
1422 if ((pw = getpwnam (pp)))
1423 pp = pw->pw_dir;
1424 else {
1425 padvise (NULL, "unknown user: %s", pp);
1426 return NOTOK;
1427 }
1428
1429 snprintf (path, sizeof(path), "%s/%s", pp, cp ? cp : "");
1430 strcpy (redirect, path);
1431 return OK;
1432 }
1433
1434
1435 static int
1436 init_io (struct Cmd *cmdp, int vio)
1437 {
1438 int io, result;
1439
1440 io = vmh;
1441
1442 vmh = vio;
1443 result = initaux_io (cmdp);
1444 vmh = io;
1445
1446 return result;
1447 }
1448
1449
1450 static int
1451 initaux_io (struct Cmd *cmdp)
1452 {
1453 char *mode;
1454
1455 switch (cmdp->direction) {
1456 case STDIO:
1457 return OK;
1458
1459 case CRTIO:
1460 case APPIO:
1461 mode = cmdp->direction == CRTIO ? "write" : "append";
1462 if ((cmdp->stream = fopen (cmdp->redirect, mode)) == NULL) {
1463 padvise (cmdp->redirect, "unable to %s ", mode);
1464 cmdp->direction = STDIO;
1465 return NOTOK;
1466 }
1467 break;
1468
1469 case PIPIO:
1470 if ((cmdp->stream = popen (cmdp->redirect, "w")) == NULL) {
1471 padvise (cmdp->redirect, "unable to pipe");
1472 cmdp->direction = STDIO;
1473 return NOTOK;
1474 }
1475 SIGNAL (SIGPIPE, pipeser);
1476 broken_pipe = 0;
1477 break;
1478
1479 default:
1480 padios (NULL, "unknown redirection for command");
1481 }
1482
1483 fflush (stdout);
1484 if (dup2 (fileno (cmdp->stream), fileno (stdout)) == NOTOK)
1485 padios ("standard output", "unable to dup2");
1486 clearerr (stdout);
1487
1488 return OK;
1489 }
1490
1491
1492 static void
1493 fin_io (struct Cmd *cmdp, int vio)
1494 {
1495 int io;
1496
1497 io = vmh;
1498 vmh = vio;
1499 finaux_io (cmdp);
1500 vmh = io;
1501 }
1502
1503
1504 static void
1505 finaux_io (struct Cmd *cmdp)
1506 {
1507 switch (cmdp->direction) {
1508 case STDIO:
1509 return;
1510
1511 case CRTIO:
1512 case APPIO:
1513 fflush (stdout);
1514 close (fileno (stdout));
1515 if (ferror (stdout))
1516 padvise (NULL, "problems writing %s", cmdp->redirect);
1517 fclose (cmdp->stream);
1518 break;
1519
1520 case PIPIO:
1521 fflush (stdout);
1522 close (fileno (stdout));
1523 pclose (cmdp->stream);
1524 SIGNAL (SIGPIPE, SIG_DFL);
1525 break;
1526
1527 default:
1528 padios (NULL, "unknown redirection for command");
1529 }
1530
1531 if (dup2 (fileno (sp), fileno (stdout)) == NOTOK)
1532 padios ("standard output", "unable to dup2");
1533 clearerr (stdout);
1534
1535 cmdp->direction = STDIO;
1536 }
1537
1538
1539 static void
1540 m_init (void)
1541 {
1542 int msgnum;
1543
1544 for (msgnum = mp->lowmsg; msgnum <= mp->hghmsg; msgnum++)
1545 unset_selected (mp, msgnum);
1546 mp->lowsel = mp->hghsel = mp->numsel = 0;
1547 }
1548
1549
1550 void
1551 m_reset (void)
1552 {
1553 write_ids ();
1554 folder_free (mp); /* free folder/message structure */
1555 myname = NULL;
1556 }
1557
1558
1559 void
1560 seq_setcur (struct msgs *mp, int msgnum)
1561 {
1562 if (mp->curmsg == msgnum)
1563 return;
1564
1565 if (mp->curmsg && Msgs[mp->curmsg].m_scanl) {
1566 free (Msgs[mp->curmsg].m_scanl);
1567 Msgs[mp->curmsg].m_scanl = NULL;
1568 }
1569 if (Msgs[msgnum].m_scanl) {
1570 free (Msgs[msgnum].m_scanl);
1571 Msgs[msgnum].m_scanl = NULL;
1572 }
1573
1574 mp->curmsg = msgnum;
1575 }
1576
1577
1578
1579 static void
1580 intrser (int i)
1581 {
1582 NMH_UNUSED (i);
1583 discard (stdout);
1584 interrupted++;
1585 }
1586
1587
1588 static void
1589 pipeser (int i)
1590 {
1591 NMH_UNUSED (i);
1592 if (broken_pipe++ == 0)
1593 fprintf (stderr, "broken pipe\n");
1594 told_to_quit++;
1595 interrupted++;
1596 }
1597
1598
1599 static void
1600 quitser (int i)
1601 {
1602 NMH_UNUSED (i);
1603 told_to_quit++;
1604 interrupted++;
1605 }
1606
1607
1608 static void
1609 alrmser (int i)
1610 {
1611 NMH_UNUSED (i);
1612 longjmp (peerenv, DONE);
1613 }
1614
1615
1616 static int
1617 pINI (void)
1618 {
1619 int i, vrsn;
1620 unsigned char *bp;
1621 struct record rcs, *rc;
1622
1623 rc = &rcs;
1624 initrc (rc);
1625
1626 switch (peer2rc (rc)) {
1627 case RC_INI:
1628 bp = rc->rc_data;
1629 while (isspace (*bp))
1630 bp++;
1631 if (sscanf (bp, "%d", &vrsn) != 1) {
1632 bad_init: ;
1633 fmt2peer (RC_ERR, "bad init \"%s\"", rc->rc_data);
1634 done (1);
1635 }
1636 if (vrsn != RC_VRSN) {
1637 fmt2peer (RC_ERR, "version %d unsupported", vrsn);
1638 done (1);
1639 }
1640
1641 while (*bp && !isspace (*bp))
1642 bp++;
1643 while (isspace (*bp))
1644 bp++;
1645 if (sscanf (bp, "%d", &numwins) != 1 || numwins <= 0)
1646 goto bad_init;
1647 if (numwins > NWIN)
1648 numwins = NWIN;
1649
1650 for (i = 1; i <= numwins; i++) {
1651 while (*bp && !isspace (*bp))
1652 bp++;
1653 while (isspace (*bp))
1654 bp++;
1655 if (sscanf (bp, "%d", &windows[i]) != 1 || windows[i] <= 0)
1656 goto bad_init;
1657 }
1658 rc2peer (RC_ACK, 0, NULL);
1659 return OK;
1660
1661 case RC_XXX:
1662 padios (NULL, "%s", rc->rc_data);
1663
1664 default:
1665 fmt2peer (RC_ERR, "pINI protocol screw-up");
1666 done (1); /* NOTREACHED */
1667 }
1668
1669 return 1; /* dead code to satisfy the compiler */
1670 }
1671
1672
1673 static int
1674 pQRY (char *str, int scansw)
1675 {
1676 NMH_UNUSED (str);
1677 if (pQRY1 (scansw) == NOTOK || pQRY2 () == NOTOK)
1678 return NOTOK;
1679
1680 rc2peer (RC_EOF, 0, NULL);
1681 return OK;
1682 }
1683
1684
1685 static int
1686 pQRY1 (int scansw)
1687 {
1688 int oldhgh;
1689 static int lastlow = 0,
1690 lastcur = 0,
1691 lasthgh = 0,
1692 lastnum = 0;
1693
1694 oldhgh = mp->hghmsg;
1695 if (check_folder (scansw) && oldhgh < mp->hghmsg) {
1696 switch (winX (STATUS)) {
1697 case NOTOK:
1698 return NOTOK;
1699
1700 case OK:
1701 printf ("new messages have arrived!");
1702 fflush (stdout);
1703 fflush (stderr);
1704 _exit (0); /* NOTREACHED */
1705
1706 default:
1707 lastlow = lastcur = lasthgh = lastnum = 0;
1708 break;
1709 }
1710
1711 switch (winX (DISPLAY)) {
1712 case NOTOK:
1713 return NOTOK;
1714
1715 case OK:
1716 scanrange (oldhgh + 1, mp->hghmsg);
1717 fflush (stdout);
1718 fflush (stderr);
1719 _exit (0); /* NOTREACHED */
1720
1721 default:
1722 break;
1723 }
1724 return OK;
1725 }
1726
1727 if (gap)
1728 switch (winX (STATUS)) {
1729 case NOTOK:
1730 return NOTOK;
1731
1732 case OK:
1733 printf ("%s: gap in ID:s, last seen %d, lowest present %d\n",
1734 myname ? myname : fmsh ? fmsh : mp->foldpath, gap - 1,
1735 readid (mp->lowmsg));
1736 fflush (stdout);
1737 fflush (stderr);
1738 _exit (0); /* NOTREACHED */
1739
1740 default:
1741 gap = 0;
1742 return OK;
1743 }
1744
1745 if (mp->lowmsg != lastlow
1746 || mp->curmsg != lastcur
1747 || mp->hghmsg != lasthgh
1748 || mp->nummsg != lastnum)
1749 switch (winX (STATUS)) {
1750 case NOTOK:
1751 return NOTOK;
1752
1753 case OK:
1754 foldcmd (NULL);
1755 fflush (stdout);
1756 fflush (stderr);
1757 _exit (0); /* NOTREACHED */
1758
1759 default:
1760 lastlow = mp->lowmsg;
1761 lastcur = mp->curmsg;
1762 lasthgh = mp->hghmsg;
1763 lastnum = mp->nummsg;
1764 return OK;
1765 }
1766
1767 return OK;
1768 }
1769
1770
1771 static int
1772 pQRY2 (void)
1773 {
1774 int i, j, k, msgnum, n;
1775 static int cur = 0,
1776 num = 0,
1777 lo = 0,
1778 hi = 0;
1779
1780 if (mp->nummsg == 0 && mp->nummsg != num)
1781 switch (winX (SCAN)) {
1782 case NOTOK:
1783 return NOTOK;
1784
1785 case OK:
1786 printf ("empty!");
1787 fflush (stdout);
1788 fflush (stderr);
1789 _exit (0); /* NOTREACHED */
1790
1791 default:
1792 num = mp->nummsg;
1793 return OK;
1794 }
1795 num = mp->nummsg;
1796
1797 i = 0;
1798 j = (k = windows[SCAN]) / 2;
1799 for (msgnum = mp->curmsg; msgnum <= mp->hghmsg; msgnum++)
1800 if (does_exist (mp, msgnum))
1801 i++;
1802 if (i-- > 0) {
1803 if (topcur)
1804 k = i >= k ? 1 : k - i;
1805 else
1806 k -= i > j ? j : i;
1807 }
1808
1809 i = j = 0;
1810 n = 1;
1811 for (msgnum = mp->curmsg; msgnum >= mp->lowmsg; msgnum--)
1812 if (does_exist (mp, msgnum)) {
1813 i = msgnum;
1814 if (j == 0)
1815 j = msgnum;
1816 if (n++ >= k)
1817 break;
1818 }
1819 for (msgnum = mp->curmsg + 1; msgnum <= mp->hghmsg; msgnum++)
1820 if (does_exist (mp, msgnum)) {
1821 if (i == 0)
1822 i = msgnum;
1823 j = msgnum;
1824 if (n++ >= windows[SCAN])
1825 break;
1826 }
1827 if (!topcur
1828 && lo > 0
1829 && hi > 0
1830 && does_exist (mp, lo)
1831 && does_exist (mp, hi)
1832 && (lo < mp->curmsg
1833 || (lo == mp->curmsg && lo == mp->lowmsg))
1834 && (mp->curmsg < hi
1835 || (hi == mp->curmsg && hi == mp->hghmsg))
1836 && hi - lo == j - i)
1837 i = lo, j = hi;
1838
1839 if (mp->curmsg != cur || modified)
1840 switch (winN (NULLCMD, SCAN, 0)) {
1841 case NOTOK:
1842 return NOTOK;
1843
1844 case OK:
1845 return OK;
1846
1847 default:
1848 scanrange (lo = i, hi = j);
1849 cur = mp->curmsg;
1850 winR (NULLCMD);
1851 return OK;
1852 }
1853
1854 return OK;
1855 }
1856
1857
1858 static int
1859 pCMD (char *str, struct swit *sw, struct Cmd *cmdp)
1860 {
1861 int i;
1862
1863 if (*str == '?')
1864 switch (winX (DISPLAY)) {
1865 case NOTOK:
1866 return NOTOK;
1867
1868 case OK:
1869 printf ("commands:\n");
1870 print_sw (ALL, sw, "", stdout);
1871 printf ("type ``quit'' to leave %s\n", invo_name);
1872 fflush (stdout);
1873 fflush (stderr);
1874 _exit (0); /* NOTREACHED */
1875
1876 default:
1877 rc2peer (RC_EOF, 0, NULL);
1878 return NOTOK;
1879 }
1880
1881 if (parse (str, cmdp) == NOTOK)
1882 return NOTOK;
1883
1884 switch (i = smatch (cmdp->args[0], sw)) {
1885 case AMBIGSW:
1886 switch (winX (DISPLAY)) {
1887 case NOTOK:
1888 return NOTOK;
1889
1890 case OK:
1891 ambigsw (cmdp->args[0], sw);
1892 fflush (stdout);
1893 fflush (stderr);
1894 _exit (0); /* NOTREACHED */
1895
1896 default:
1897 rc2peer (RC_EOF, 0, NULL);
1898 return NOTOK;
1899 }
1900
1901 case UNKWNSW:
1902 fmt2peer (RC_ERR,
1903 "say what: ``%s'' -- type ? (or help) for help",
1904 cmdp->args[0]);
1905 return NOTOK;
1906
1907 default:
1908 return i;
1909 }
1910 }
1911
1912
1913 static int
1914 pFIN (void)
1915 {
1916 int status;
1917
1918 switch (setjmp (peerenv)) {
1919 case OK:
1920 SIGNAL (SIGALRM, alrmser);
1921 alarm (ALARM);
1922
1923 status = peerwait ();
1924
1925 alarm (0);
1926 return status;
1927
1928 default:
1929 return NOTOK;
1930 }
1931 }
1932
1933
1934 static int
1935 peerwait (void)
1936 {
1937 struct record rcs, *rc;
1938
1939 rc = &rcs;
1940 initrc (rc);
1941
1942 switch (peer2rc (rc)) {
1943 case RC_QRY:
1944 case RC_CMD:
1945 rc2peer (RC_FIN, 0, NULL);
1946 return OK;
1947
1948 case RC_XXX:
1949 advise (NULL, "%s", rc->rc_data);
1950 return NOTOK;
1951
1952 default:
1953 fmt2peer (RC_FIN, "pLOOP protocol screw-up");
1954 return NOTOK;
1955 }
1956 }
1957
1958
1959 static int
1960 ttyNaux (struct Cmd *cmdp, char *s)
1961 {
1962 struct record rcs, *rc;
1963
1964 rc = &rcs;
1965 initrc (rc);
1966
1967 if (cmdp && init_io (cmdp, vmh) == NOTOK)
1968 return NOTOK;
1969
1970 /* XXX: fseek() too tricky for our own good */
1971 if (!fmsh)
1972 fseek (fp, 0L, SEEK_SET);
1973
1974 vmhtty = NOTOK;
1975 switch (rc2rc (RC_TTY, s ? strlen (s) : 0, s, rc)) {
1976 case RC_ACK:
1977 vmhtty = OK; /* fall */
1978 case RC_ERR:
1979 break;
1980
1981 case RC_XXX:
1982 padios (NULL, "%s", rc->rc_data);/* NOTREACHED */
1983
1984 default:
1985 fmt2peer (RC_ERR, "pTTY protocol screw-up");
1986 done (1); /* NOTREACHED */
1987 }
1988
1989 #ifdef SIGTSTP
1990 SIGNAL (SIGTSTP, tstat);
1991 #endif
1992 return vmhtty;
1993 }
1994
1995
1996 static int
1997 ttyR (struct Cmd *cmdp)
1998 {
1999 struct record rcs, *rc;
2000
2001 rc = &rcs;
2002
2003 #ifdef SIGTSTP
2004 SIGNAL (SIGTSTP, SIG_IGN);
2005 #endif
2006
2007 if (vmhtty != OK)
2008 return NOTOK;
2009
2010 initrc (rc);
2011
2012 if (cmdp)
2013 fin_io (cmdp, 0);
2014
2015 vmhtty = NOTOK;
2016 switch (rc2rc (RC_EOF, 0, NULL, rc)) {
2017 case RC_ACK:
2018 rc2peer (RC_EOF, 0, NULL);
2019 return OK;
2020
2021 case RC_XXX:
2022 padios (NULL, "%s", rc->rc_data);/* NOTREACHED */
2023
2024 default:
2025 fmt2peer (RC_ERR, "pTTY protocol screw-up");
2026 done (1); /* NOTREACHED */
2027 }
2028
2029 return 1; /* dead code to satisfy compiler */
2030 }
2031
2032
2033 static int
2034 winN (struct Cmd *cmdp, int n, int eof)
2035 {
2036 int i, pd[2];
2037 char buffer[BUFSIZ];
2038 struct record rcs, *rc;
2039
2040 rc = &rcs;
2041 if (vmhpid == NOTOK)
2042 return OK;
2043
2044 initrc (rc);
2045
2046 /* XXX: fseek() too tricky for our own good */
2047 if (!fmsh)
2048 fseek (fp, 0L, SEEK_SET);
2049
2050 vmhpid = OK;
2051
2052 snprintf (buffer, sizeof(buffer), "%d", n);
2053 switch (str2rc (RC_WIN, buffer, rc)) {
2054 case RC_ACK:
2055 break;
2056
2057 case RC_ERR:
2058 return NOTOK;
2059
2060 case RC_XXX:
2061 padios (NULL, "%s", rc->rc_data);
2062
2063 default:
2064 fmt2peer (RC_ERR, "pWIN protocol screw-up");
2065 done (1);
2066 }
2067
2068 if (pipe (pd) == NOTOK) {
2069 err2peer (RC_ERR, "pipe", "unable to");
2070 return NOTOK;
2071 }
2072
2073 switch (vmhpid = fork()) {
2074 case NOTOK:
2075 err2peer (RC_ERR, "fork", "unable to");
2076 close (pd[0]);
2077 close (pd[1]);
2078 return NOTOK;
2079
2080 case OK:
2081 close (pd[1]);
2082 SIGNAL (SIGPIPE, SIG_IGN);
2083 while ((i = read (pd[0], buffer, sizeof buffer)) > 0)
2084 switch (rc2rc (RC_DATA, i, buffer, rc)) {
2085 case RC_ACK:
2086 break;
2087
2088 case RC_ERR:
2089 _exit (1);
2090
2091 case RC_XXX:
2092 advise (NULL, "%s", rc->rc_data);
2093 _exit (2);
2094
2095 default:
2096 fmt2peer (RC_ERR, "pWIN protocol screw-up");
2097 _exit (2);
2098 }
2099 if (i == OK)
2100 switch (rc2rc (RC_EOF, 0, NULL, rc)) {
2101 case RC_ACK:
2102 if (eof)
2103 rc2peer (RC_EOF, 0, NULL);
2104 i = 0;
2105 break;
2106
2107 case RC_XXX:
2108 advise (NULL, "%s", rc->rc_data);
2109 i = 2;
2110 break;
2111
2112 default:
2113 fmt2peer (RC_ERR, "pWIN protocol screw-up");
2114 i = 2;
2115 break;
2116 }
2117 if (i == NOTOK)
2118 err2peer (RC_ERR, "pipe", "error reading from");
2119 close (pd[0]);
2120 _exit (i != NOTOK ? i : 1);
2121
2122 default:
2123 if ((vmhfd0 = dup (fileno (stdin))) == NOTOK)
2124 padios ("standard input", "unable to dup");
2125 if ((vmhfd1 = dup (fileno (stdout))) == NOTOK)
2126 padios ("standard output", "unable to dup");
2127 if ((vmhfd2 = dup (fileno (stderr))) == NOTOK)
2128 padios ("diagnostic output", "unable to dup");
2129
2130 close (0);
2131 if ((i = open ("/dev/null", O_RDONLY)) != NOTOK && i != fileno (stdin)) {
2132 dup2 (i, fileno (stdin));
2133 close (i);
2134 }
2135
2136 fflush (stdout);
2137 if (dup2 (pd[1], fileno (stdout)) == NOTOK)
2138 padios ("standard output", "unable to dup2");
2139 clearerr (stdout);
2140
2141 fflush (stderr);
2142 if (dup2 (pd[1], fileno (stderr)) == NOTOK)
2143 padios ("diagnostic output", "unable to dup2");
2144 clearerr (stderr);
2145
2146 if (cmdp && init_io (cmdp, 0) == NOTOK)
2147 return NOTOK;
2148 pstat = SIGNAL (SIGPIPE, pipeser);
2149 broken_pipe = 1;
2150
2151 close (pd[0]);
2152 close (pd[1]);
2153
2154 return vmhpid;
2155 }
2156 }
2157
2158
2159 static int
2160 winR (struct Cmd *cmdp)
2161 {
2162 int status;
2163
2164 if (vmhpid <= OK)
2165 return NOTOK;
2166
2167 if (cmdp)
2168 fin_io (cmdp, 0);
2169
2170 if (dup2 (vmhfd0, fileno (stdin)) == NOTOK)
2171 padios ("standard input", "unable to dup2");
2172 clearerr (stdin);
2173 close (vmhfd0);
2174
2175 fflush (stdout);
2176 if (dup2 (vmhfd1, fileno (stdout)) == NOTOK)
2177 padios ("standard output", "unable to dup2");
2178 clearerr (stdout);
2179 close (vmhfd1);
2180
2181 fflush (stderr);
2182 if (dup2 (vmhfd2, fileno (stderr)) == NOTOK)
2183 padios ("diagnostic output", "unable to dup2");
2184 clearerr (stderr);
2185 close (vmhfd2);
2186
2187 SIGNAL (SIGPIPE, pstat);
2188
2189 if ((status = pidwait (vmhpid, OK)) == 2)
2190 done (1);
2191
2192 vmhpid = OK;
2193 return (status == 0 ? OK : NOTOK);
2194 }
2195
2196
2197 static int
2198 winX (int n)
2199 {
2200 int i, pid, pd[2];
2201 char buffer[BUFSIZ];
2202 struct record rcs, *rc;
2203
2204 rc = &rcs;
2205 initrc (rc);
2206
2207 /* XXX: fseek() too tricky for our own good */
2208 if (!fmsh)
2209 fseek (fp, 0L, SEEK_SET);
2210
2211 snprintf (buffer, sizeof(buffer), "%d", n);
2212 switch (str2rc (RC_WIN, buffer, rc)) {
2213 case RC_ACK:
2214 break;
2215
2216 case RC_ERR:
2217 return NOTOK;
2218
2219 case RC_XXX:
2220 padios (NULL, "%s", rc->rc_data);
2221
2222 default:
2223 fmt2peer (RC_ERR, "pWIN protocol screw-up");
2224 done (1);
2225 }
2226
2227 if (pipe (pd) == NOTOK) {
2228 err2peer (RC_ERR, "pipe", "unable to");
2229 return NOTOK;
2230 }
2231
2232 switch (pid = fork ()) {
2233 case NOTOK:
2234 err2peer (RC_ERR, "fork", "unable to");
2235 close (pd[0]);
2236 close (pd[1]);
2237 return NOTOK;
2238
2239 case OK:
2240 close (fileno (stdin));
2241 if ((i = open ("/dev/null", O_RDONLY)) != NOTOK && i != fileno (stdin)) {
2242 dup2 (i, fileno (stdin));
2243 close (i);
2244 }
2245 dup2 (pd[1], fileno (stdout));
2246 dup2 (pd[1], fileno (stderr));
2247 close (pd[0]);
2248 close (pd[1]);
2249 vmhpid = NOTOK;
2250 return OK;
2251
2252 default:
2253 close (pd[1]);
2254 while ((i = read (pd[0], buffer, sizeof buffer)) > 0)
2255 switch (rc2rc (RC_DATA, i, buffer, rc)) {
2256 case RC_ACK:
2257 break;
2258
2259 case RC_ERR:
2260 close (pd[0]);
2261 pidwait (pid, OK);
2262 return NOTOK;
2263
2264 case RC_XXX:
2265 padios (NULL, "%s", rc->rc_data);
2266
2267 default:
2268 fmt2peer (RC_ERR, "pWIN protocol screw-up");
2269 done (1);
2270 }
2271 if (i == OK)
2272 switch (rc2rc (RC_EOF, 0, NULL, rc)) {
2273 case RC_ACK:
2274 break;
2275
2276 case RC_XXX:
2277 padios (NULL, "%s", rc->rc_data);
2278
2279 default:
2280 fmt2peer (RC_ERR, "pWIN protocol screw-up");
2281 done (1);
2282 }
2283 if (i == NOTOK)
2284 err2peer (RC_ERR, "pipe", "error reading from");
2285
2286 close (pd[0]);
2287 pidwait (pid, OK);
2288 return (i != NOTOK ? pid : NOTOK);
2289 }
2290 }
2291
2292
2293 void
2294 padios (char *what, char *fmt, ...)
2295 {
2296 va_list ap;
2297
2298 va_start(ap, fmt);
2299 if (vmh) {
2300 verr2peer (RC_FIN, what, fmt, ap);
2301 rcdone ();
2302 } else {
2303 advertise (what, NULL, fmt, ap);
2304 }
2305 va_end(ap);
2306
2307 done (1);
2308 }
2309
2310
2311 void
2312 padvise (char *what, char *fmt, ...)
2313 {
2314 va_list ap;
2315
2316 va_start(ap, fmt);
2317 if (vmh) {
2318 verr2peer (RC_ERR, what, fmt, ap);
2319 } else {
2320 advertise (what, NULL, fmt, ap);
2321 }
2322 va_end(ap);
2323 }