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