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