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