]> diplodocus.org Git - nmh/blob - uip/msh.c
Use *ct->c_ceclosefnx() instead of clsoe_encoding() in show_content_aux().
[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
1083 if (!(mp->msgflags & MODIFIED) || is_readonly(mp) || fmsh) {
1084 if (vmh)
1085 rc2peer (RC_FIN, 0, NULL);
1086 return;
1087 }
1088
1089 if (vmh)
1090 ttyNaux (NULLCMD, "FAST");
1091 cp = NULL;
1092 if ((dp = lkfopendata (mp->foldpath, "r")) == NULL) {
1093 advise (mp->foldpath, "unable to lock");
1094 if (vmh) {
1095 ttyR (NULLCMD);
1096 pFIN ();
1097 }
1098 return;
1099 }
1100 if (fstat (fileno (dp), &st) == NOTOK) {
1101 advise (mp->foldpath, "unable to stat");
1102 goto release;
1103 }
1104 if (mtime != st.st_mtime) {
1105 advise (NULL, "new messages have arrived, no update");
1106 goto release;
1107 }
1108 mode = (int) (st.st_mode & 0777);
1109
1110 if (mp->nummsg == 0) {
1111 cp = concat ("Zero file \"", mp->foldpath, "\"? ", NULL);
1112 if (getanswer (cp)) {
1113 if ((i = creat (mp->foldpath, mode)) != NOTOK)
1114 close (i);
1115 else
1116 advise (mp->foldpath, "error zero'ing");
1117 (void) m_unlink (map_name (mp->foldpath));/* XXX */
1118 }
1119 goto release;
1120 }
1121
1122 cp = concat ("Update file \"", mp->foldpath, "\"? ", NULL);
1123 if (!getanswer (cp))
1124 goto release;
1125 strncpy (tmpfil, m_backup (mp->foldpath), sizeof(tmpfil));
1126 if ((md = mbx_open (tmpfil, mbx_style, st.st_uid, st.st_gid, mode)) == NOTOK) {
1127 advise (tmpfil, "unable to open");
1128 goto release;
1129 }
1130
1131 for (msgnum = mp->lowmsg; msgnum <= mp->hghmsg; msgnum++)
1132 if (does_exist(mp, msgnum) && pack (tmpfil, md, msgnum) == NOTOK) {
1133 mbx_close (tmpfil, md);
1134 (void) m_unlink (tmpfil);
1135 (void) m_unlink (map_name (tmpfil));
1136 goto release;
1137 }
1138 mbx_close (tmpfil, md);
1139
1140 if (rename (tmpfil, mp->foldpath) == NOTOK)
1141 admonish (mp->foldpath, "unable to rename %s to", tmpfil);
1142 else {
1143 strncpy (map1, map_name (tmpfil), sizeof(map1));
1144 strncpy (map2, map_name (mp->foldpath), sizeof(map2));
1145
1146 if (rename (map1, map2) == NOTOK) {
1147 admonish (map2, "unable to rename %s to", map1);
1148 (void) m_unlink (map1);
1149 (void) m_unlink (map2);
1150 }
1151 }
1152
1153 release: ;
1154 if (cp)
1155 free (cp);
1156 lkfclosedata (dp, mp->foldpath);
1157 if (vmh) {
1158 ttyR (NULLCMD);
1159 pFIN ();
1160 }
1161 }
1162
1163
1164 static int
1165 getargs (char *prompt, struct swit *sw, struct Cmd *cmdp)
1166 {
1167 int i;
1168 char *cp;
1169 static char buffer[BUFSIZ];
1170
1171 told_to_quit = 0;
1172 for (;;) {
1173 interrupted = 0;
1174 if (interactive) {
1175 printf ("%s", prompt);
1176 fflush (stdout);
1177 }
1178 for (cp = buffer; (i = getchar ()) != '\n';) {
1179 if (interrupted && !told_to_quit) {
1180 buffer[0] = '\0';
1181 putchar ('\n');
1182 break;
1183 }
1184 if (told_to_quit || i == EOF) {
1185 if (ppid > 0)
1186 #ifdef SIGEMT
1187 kill (ppid, SIGEMT);
1188 #else
1189 kill (ppid, SIGTERM);
1190 #endif
1191 return EOF;
1192 }
1193 if (cp < &buffer[sizeof buffer - 2])
1194 *cp++ = i;
1195 }
1196 *cp = 0;
1197
1198 if (buffer[0] == 0)
1199 continue;
1200 if (buffer[0] == '?') {
1201 printf ("commands:\n");
1202 print_sw (ALL, sw, "", stdout);
1203 printf ("type CTRL-D or use ``quit'' to leave %s\n",
1204 invo_name);
1205 continue;
1206 }
1207
1208 if (parse (buffer, cmdp) == NOTOK)
1209 continue;
1210
1211 switch (i = smatch (cmdp->args[0], sw)) {
1212 case AMBIGSW:
1213 ambigsw (cmdp->args[0], sw);
1214 continue;
1215 case UNKWNSW:
1216 printf ("say what: ``%s'' -- type ? (or help) for help\n",
1217 cmdp->args[0]);
1218 continue;
1219 default:
1220 return i;
1221 }
1222 }
1223 }
1224
1225
1226 static int
1227 getcmds (struct swit *sw, struct Cmd *cmdp, int scansw)
1228 {
1229 int i;
1230 struct record rcs, *rc;
1231
1232 rc = &rcs;
1233 initrc (rc);
1234
1235 for (;;)
1236 switch (peer2rc (rc)) {
1237 case RC_QRY:
1238 pQRY (rc->rc_data, scansw);
1239 break;
1240
1241 case RC_CMD:
1242 if ((i = pCMD (rc->rc_data, sw, cmdp)) != NOTOK)
1243 return i;
1244 break;
1245
1246 case RC_FIN:
1247 if (ppid > 0)
1248 #ifdef SIGEMT
1249 kill (ppid, SIGEMT);
1250 #else
1251 kill (ppid, SIGTERM);
1252 #endif
1253 return EOF;
1254
1255 case RC_XXX:
1256 padios (NULL, "%s", rc->rc_data);
1257
1258 default:
1259 fmt2peer (RC_ERR, "pLOOP protocol screw-up");
1260 done (1);
1261 }
1262 }
1263
1264
1265 static int
1266 parse (char *buffer, struct Cmd *cmdp)
1267 {
1268 int argp = 0;
1269 char c, *cp, *pp;
1270
1271 cmdp->line[0] = 0;
1272 pp = cmdp->args[argp++] = cmdp->line;
1273 cmdp->redirect = NULL;
1274 cmdp->direction = STDIO;
1275 cmdp->stream = NULL;
1276
1277 for (cp = buffer; (c = *cp); cp++) {
1278 if (!isspace ((unsigned char) c))
1279 break;
1280 }
1281 if (c == '\0') {
1282 if (vmh)
1283 fmt2peer (RC_EOF, "null command");
1284 return NOTOK;
1285 }
1286
1287 while ((c = *cp++)) {
1288 if (isspace ((unsigned char) c)) {
1289 while (isspace ((unsigned char) c))
1290 c = *cp++;
1291 if (c == 0)
1292 break;
1293 *pp++ = 0;
1294 cmdp->args[argp++] = pp;
1295 *pp = 0;
1296 }
1297
1298 switch (c) {
1299 case '"':
1300 for (;;) {
1301 switch (c = *cp++) {
1302 case 0:
1303 padvise (NULL, "unmatched \"");
1304 return NOTOK;
1305 case '"':
1306 break;
1307 case QUOTE:
1308 if ((c = *cp++) == 0)
1309 goto no_quoting;
1310 default:
1311 *pp++ = c;
1312 continue;
1313 }
1314 break;
1315 }
1316 continue;
1317
1318 case QUOTE:
1319 if ((c = *cp++) == 0) {
1320 no_quoting: ;
1321 padvise (NULL, "the newline character can not be quoted");
1322 return NOTOK;
1323 }
1324
1325 default: ;
1326 *pp++ = c;
1327 continue;
1328
1329 case '>':
1330 case '|':
1331 if (pp == cmdp->line) {
1332 padvise (NULL, "invalid null command");
1333 return NOTOK;
1334 }
1335 if (*cmdp->args[argp - 1] == 0)
1336 argp--;
1337 cmdp->direction = c == '>' ? CRTIO : PIPIO;
1338 if (cmdp->direction == CRTIO && (c = *cp) == '>') {
1339 cmdp->direction = APPIO;
1340 cp++;
1341 }
1342 cmdp->redirect = pp + 1;/* sigh */
1343 for (; (c = *cp); cp++)
1344 if (!isspace ((unsigned char) c))
1345 break;
1346 if (c == 0) {
1347 padvise (NULL, cmdp->direction != PIPIO
1348 ? "missing name for redirect"
1349 : "invalid null command");
1350 return NOTOK;
1351 }
1352 strcpy (cmdp->redirect, cp);
1353 if (cmdp->direction != PIPIO) {
1354 for (; *cp; cp++)
1355 if (isspace ((unsigned char) *cp)) {
1356 padvise (NULL, "bad name for redirect");
1357 return NOTOK;
1358 }
1359 if (expand (cmdp->redirect) == NOTOK)
1360 return NOTOK;
1361 }
1362 break;
1363 }
1364 break;
1365 }
1366
1367 *pp++ = 0;
1368 cmdp->args[argp] = NULL;
1369
1370 return OK;
1371 }
1372
1373
1374 int
1375 expand (char *redirect)
1376 {
1377 char *cp, *pp;
1378 char path[BUFSIZ];
1379 struct passwd *pw;
1380
1381 if (*redirect != '~')
1382 return OK;
1383
1384 if ((cp = strchr(pp = redirect + 1, '/')))
1385 *cp++ = 0;
1386 if (*pp == 0)
1387 pp = mypath;
1388 else
1389 if ((pw = getpwnam (pp)))
1390 pp = pw->pw_dir;
1391 else {
1392 padvise (NULL, "unknown user: %s", pp);
1393 return NOTOK;
1394 }
1395
1396 snprintf (path, sizeof(path), "%s/%s", pp, cp ? cp : "");
1397 strcpy (redirect, path);
1398 return OK;
1399 }
1400
1401
1402 static int
1403 init_io (struct Cmd *cmdp, int vio)
1404 {
1405 int io, result;
1406
1407 io = vmh;
1408
1409 vmh = vio;
1410 result = initaux_io (cmdp);
1411 vmh = io;
1412
1413 return result;
1414 }
1415
1416
1417 static int
1418 initaux_io (struct Cmd *cmdp)
1419 {
1420 char *mode;
1421
1422 switch (cmdp->direction) {
1423 case STDIO:
1424 return OK;
1425
1426 case CRTIO:
1427 case APPIO:
1428 mode = cmdp->direction == CRTIO ? "write" : "append";
1429 if ((cmdp->stream = fopen (cmdp->redirect, mode)) == NULL) {
1430 padvise (cmdp->redirect, "unable to %s ", mode);
1431 cmdp->direction = STDIO;
1432 return NOTOK;
1433 }
1434 break;
1435
1436 case PIPIO:
1437 if ((cmdp->stream = popen (cmdp->redirect, "w")) == NULL) {
1438 padvise (cmdp->redirect, "unable to pipe");
1439 cmdp->direction = STDIO;
1440 return NOTOK;
1441 }
1442 SIGNAL (SIGPIPE, pipeser);
1443 broken_pipe = 0;
1444 break;
1445
1446 default:
1447 padios (NULL, "unknown redirection for command");
1448 }
1449
1450 fflush (stdout);
1451 if (dup2 (fileno (cmdp->stream), fileno (stdout)) == NOTOK)
1452 padios ("standard output", "unable to dup2");
1453 clearerr (stdout);
1454
1455 return OK;
1456 }
1457
1458
1459 static void
1460 fin_io (struct Cmd *cmdp, int vio)
1461 {
1462 int io;
1463
1464 io = vmh;
1465 vmh = vio;
1466 finaux_io (cmdp);
1467 vmh = io;
1468 }
1469
1470
1471 static void
1472 finaux_io (struct Cmd *cmdp)
1473 {
1474 switch (cmdp->direction) {
1475 case STDIO:
1476 return;
1477
1478 case CRTIO:
1479 case APPIO:
1480 fflush (stdout);
1481 close (fileno (stdout));
1482 if (ferror (stdout))
1483 padvise (NULL, "problems writing %s", cmdp->redirect);
1484 fclose (cmdp->stream);
1485 break;
1486
1487 case PIPIO:
1488 fflush (stdout);
1489 close (fileno (stdout));
1490 pclose (cmdp->stream);
1491 SIGNAL (SIGPIPE, SIG_DFL);
1492 break;
1493
1494 default:
1495 padios (NULL, "unknown redirection for command");
1496 }
1497
1498 if (dup2 (fileno (sp), fileno (stdout)) == NOTOK)
1499 padios ("standard output", "unable to dup2");
1500 clearerr (stdout);
1501
1502 cmdp->direction = STDIO;
1503 }
1504
1505
1506 static void
1507 m_init (void)
1508 {
1509 int msgnum;
1510
1511 for (msgnum = mp->lowmsg; msgnum <= mp->hghmsg; msgnum++)
1512 unset_selected (mp, msgnum);
1513 mp->lowsel = mp->hghsel = mp->numsel = 0;
1514 }
1515
1516
1517 void
1518 m_reset (void)
1519 {
1520 write_ids ();
1521 folder_free (mp); /* free folder/message structure */
1522 myname = NULL;
1523 }
1524
1525
1526 void
1527 seq_setcur (struct msgs *mp, int msgnum)
1528 {
1529 if (mp->curmsg == msgnum)
1530 return;
1531
1532 if (mp->curmsg && Msgs[mp->curmsg].m_scanl) {
1533 free (Msgs[mp->curmsg].m_scanl);
1534 Msgs[mp->curmsg].m_scanl = NULL;
1535 }
1536 if (Msgs[msgnum].m_scanl) {
1537 free (Msgs[msgnum].m_scanl);
1538 Msgs[msgnum].m_scanl = NULL;
1539 }
1540
1541 mp->curmsg = msgnum;
1542 }
1543
1544
1545
1546 static void
1547 intrser (int i)
1548 {
1549 NMH_UNUSED (i);
1550 discard (stdout);
1551 interrupted++;
1552 }
1553
1554
1555 static void
1556 pipeser (int i)
1557 {
1558 NMH_UNUSED (i);
1559 if (broken_pipe++ == 0)
1560 fprintf (stderr, "broken pipe\n");
1561 told_to_quit++;
1562 interrupted++;
1563 }
1564
1565
1566 static void
1567 quitser (int i)
1568 {
1569 NMH_UNUSED (i);
1570 told_to_quit++;
1571 interrupted++;
1572 }
1573
1574
1575 static void
1576 alrmser (int i)
1577 {
1578 NMH_UNUSED (i);
1579 longjmp (peerenv, DONE);
1580 }
1581
1582
1583 static int
1584 pINI (void)
1585 {
1586 int i, vrsn;
1587 char *bp;
1588 struct record rcs, *rc;
1589
1590 rc = &rcs;
1591 initrc (rc);
1592
1593 switch (peer2rc (rc)) {
1594 case RC_INI:
1595 bp = rc->rc_data;
1596 while (isspace ((unsigned char) *bp))
1597 bp++;
1598 if (sscanf (bp, "%d", &vrsn) != 1) {
1599 bad_init: ;
1600 fmt2peer (RC_ERR, "bad init \"%s\"", rc->rc_data);
1601 done (1);
1602 }
1603 if (vrsn != RC_VRSN) {
1604 fmt2peer (RC_ERR, "version %d unsupported", vrsn);
1605 done (1);
1606 }
1607
1608 while (*bp && !isspace ((unsigned char) *bp))
1609 bp++;
1610 while (isspace ((unsigned char) *bp))
1611 bp++;
1612 if (sscanf (bp, "%d", &numwins) != 1 || numwins <= 0)
1613 goto bad_init;
1614 if (numwins > NWIN)
1615 numwins = NWIN;
1616
1617 for (i = 1; i <= numwins; i++) {
1618 while (*bp && !isspace ((unsigned char) *bp))
1619 bp++;
1620 while (isspace ((unsigned char) *bp))
1621 bp++;
1622 if (sscanf (bp, "%d", &windows[i]) != 1 || windows[i] <= 0)
1623 goto bad_init;
1624 }
1625 rc2peer (RC_ACK, 0, NULL);
1626 return OK;
1627
1628 case RC_XXX:
1629 padios (NULL, "%s", rc->rc_data);
1630
1631 default:
1632 fmt2peer (RC_ERR, "pINI protocol screw-up");
1633 done (1); /* NOTREACHED */
1634 }
1635
1636 return 1; /* dead code to satisfy the compiler */
1637 }
1638
1639
1640 static int
1641 pQRY (char *str, int scansw)
1642 {
1643 NMH_UNUSED (str);
1644 if (pQRY1 (scansw) == NOTOK || pQRY2 () == NOTOK)
1645 return NOTOK;
1646
1647 rc2peer (RC_EOF, 0, NULL);
1648 return OK;
1649 }
1650
1651
1652 static int
1653 pQRY1 (int scansw)
1654 {
1655 int oldhgh;
1656 static int lastlow = 0,
1657 lastcur = 0,
1658 lasthgh = 0,
1659 lastnum = 0;
1660
1661 oldhgh = mp->hghmsg;
1662 if (check_folder (scansw) && oldhgh < mp->hghmsg) {
1663 switch (winX (STATUS)) {
1664 case NOTOK:
1665 return NOTOK;
1666
1667 case OK:
1668 printf ("new messages have arrived!");
1669 fflush (stdout);
1670 fflush (stderr);
1671 _exit (0); /* NOTREACHED */
1672
1673 default:
1674 lastlow = lastcur = lasthgh = lastnum = 0;
1675 break;
1676 }
1677
1678 switch (winX (DISPLAY)) {
1679 case NOTOK:
1680 return NOTOK;
1681
1682 case OK:
1683 scanrange (oldhgh + 1, mp->hghmsg);
1684 fflush (stdout);
1685 fflush (stderr);
1686 _exit (0); /* NOTREACHED */
1687
1688 default:
1689 break;
1690 }
1691 return OK;
1692 }
1693
1694 if (gap)
1695 switch (winX (STATUS)) {
1696 case NOTOK:
1697 return NOTOK;
1698
1699 case OK:
1700 printf ("%s: gap in ID:s, last seen %d, lowest present %d\n",
1701 myname ? myname : fmsh ? fmsh : mp->foldpath, gap - 1,
1702 readid (mp->lowmsg));
1703 fflush (stdout);
1704 fflush (stderr);
1705 _exit (0); /* NOTREACHED */
1706
1707 default:
1708 gap = 0;
1709 return OK;
1710 }
1711
1712 if (mp->lowmsg != lastlow
1713 || mp->curmsg != lastcur
1714 || mp->hghmsg != lasthgh
1715 || mp->nummsg != lastnum)
1716 switch (winX (STATUS)) {
1717 case NOTOK:
1718 return NOTOK;
1719
1720 case OK:
1721 foldcmd (NULL);
1722 fflush (stdout);
1723 fflush (stderr);
1724 _exit (0); /* NOTREACHED */
1725
1726 default:
1727 lastlow = mp->lowmsg;
1728 lastcur = mp->curmsg;
1729 lasthgh = mp->hghmsg;
1730 lastnum = mp->nummsg;
1731 return OK;
1732 }
1733
1734 return OK;
1735 }
1736
1737
1738 static int
1739 pQRY2 (void)
1740 {
1741 int i, j, k, msgnum, n;
1742 static int cur = 0,
1743 num = 0,
1744 lo = 0,
1745 hi = 0;
1746
1747 if (mp->nummsg == 0 && mp->nummsg != num)
1748 switch (winX (SCAN)) {
1749 case NOTOK:
1750 return NOTOK;
1751
1752 case OK:
1753 printf ("empty!");
1754 fflush (stdout);
1755 fflush (stderr);
1756 _exit (0); /* NOTREACHED */
1757
1758 default:
1759 num = mp->nummsg;
1760 return OK;
1761 }
1762 num = mp->nummsg;
1763
1764 i = 0;
1765 j = (k = windows[SCAN]) / 2;
1766 for (msgnum = mp->curmsg; msgnum <= mp->hghmsg; msgnum++)
1767 if (does_exist (mp, msgnum))
1768 i++;
1769 if (i-- > 0) {
1770 if (topcur)
1771 k = i >= k ? 1 : k - i;
1772 else
1773 k -= i > j ? j : i;
1774 }
1775
1776 i = j = 0;
1777 n = 1;
1778 for (msgnum = mp->curmsg; msgnum >= mp->lowmsg; msgnum--)
1779 if (does_exist (mp, msgnum)) {
1780 i = msgnum;
1781 if (j == 0)
1782 j = msgnum;
1783 if (n++ >= k)
1784 break;
1785 }
1786 for (msgnum = mp->curmsg + 1; msgnum <= mp->hghmsg; msgnum++)
1787 if (does_exist (mp, msgnum)) {
1788 if (i == 0)
1789 i = msgnum;
1790 j = msgnum;
1791 if (n++ >= windows[SCAN])
1792 break;
1793 }
1794 if (!topcur
1795 && lo > 0
1796 && hi > 0
1797 && does_exist (mp, lo)
1798 && does_exist (mp, hi)
1799 && (lo < mp->curmsg
1800 || (lo == mp->curmsg && lo == mp->lowmsg))
1801 && (mp->curmsg < hi
1802 || (hi == mp->curmsg && hi == mp->hghmsg))
1803 && hi - lo == j - i)
1804 i = lo, j = hi;
1805
1806 if (mp->curmsg != cur || modified)
1807 switch (winN (NULLCMD, SCAN, 0)) {
1808 case NOTOK:
1809 return NOTOK;
1810
1811 case OK:
1812 return OK;
1813
1814 default:
1815 scanrange (lo = i, hi = j);
1816 cur = mp->curmsg;
1817 winR (NULLCMD);
1818 return OK;
1819 }
1820
1821 return OK;
1822 }
1823
1824
1825 static int
1826 pCMD (char *str, struct swit *sw, struct Cmd *cmdp)
1827 {
1828 int i;
1829
1830 if (*str == '?')
1831 switch (winX (DISPLAY)) {
1832 case NOTOK:
1833 return NOTOK;
1834
1835 case OK:
1836 printf ("commands:\n");
1837 print_sw (ALL, sw, "", stdout);
1838 printf ("type ``quit'' to leave %s\n", invo_name);
1839 fflush (stdout);
1840 fflush (stderr);
1841 _exit (0); /* NOTREACHED */
1842
1843 default:
1844 rc2peer (RC_EOF, 0, NULL);
1845 return NOTOK;
1846 }
1847
1848 if (parse (str, cmdp) == NOTOK)
1849 return NOTOK;
1850
1851 switch (i = smatch (cmdp->args[0], sw)) {
1852 case AMBIGSW:
1853 switch (winX (DISPLAY)) {
1854 case NOTOK:
1855 return NOTOK;
1856
1857 case OK:
1858 ambigsw (cmdp->args[0], sw);
1859 fflush (stdout);
1860 fflush (stderr);
1861 _exit (0); /* NOTREACHED */
1862
1863 default:
1864 rc2peer (RC_EOF, 0, NULL);
1865 return NOTOK;
1866 }
1867
1868 case UNKWNSW:
1869 fmt2peer (RC_ERR,
1870 "say what: ``%s'' -- type ? (or help) for help",
1871 cmdp->args[0]);
1872 return NOTOK;
1873
1874 default:
1875 return i;
1876 }
1877 }
1878
1879
1880 static int
1881 pFIN (void)
1882 {
1883 int status;
1884
1885 switch (setjmp (peerenv)) {
1886 case OK:
1887 SIGNAL (SIGALRM, alrmser);
1888 alarm (ALARM);
1889
1890 status = peerwait ();
1891
1892 alarm (0);
1893 return status;
1894
1895 default:
1896 return NOTOK;
1897 }
1898 }
1899
1900
1901 static int
1902 peerwait (void)
1903 {
1904 struct record rcs, *rc;
1905
1906 rc = &rcs;
1907 initrc (rc);
1908
1909 switch (peer2rc (rc)) {
1910 case RC_QRY:
1911 case RC_CMD:
1912 rc2peer (RC_FIN, 0, NULL);
1913 return OK;
1914
1915 case RC_XXX:
1916 advise (NULL, "%s", rc->rc_data);
1917 return NOTOK;
1918
1919 default:
1920 fmt2peer (RC_FIN, "pLOOP protocol screw-up");
1921 return NOTOK;
1922 }
1923 }
1924
1925
1926 static int
1927 ttyNaux (struct Cmd *cmdp, char *s)
1928 {
1929 struct record rcs, *rc;
1930
1931 rc = &rcs;
1932 initrc (rc);
1933
1934 if (cmdp && init_io (cmdp, vmh) == NOTOK)
1935 return NOTOK;
1936
1937 /* XXX: fseek() too tricky for our own good */
1938 if (!fmsh)
1939 fseek (fp, 0L, SEEK_SET);
1940
1941 vmhtty = NOTOK;
1942 switch (rc2rc (RC_TTY, s ? strlen (s) : 0, s, rc)) {
1943 case RC_ACK:
1944 vmhtty = OK; /* fall */
1945 case RC_ERR:
1946 break;
1947
1948 case RC_XXX:
1949 padios (NULL, "%s", rc->rc_data);/* NOTREACHED */
1950
1951 default:
1952 fmt2peer (RC_ERR, "pTTY protocol screw-up");
1953 done (1); /* NOTREACHED */
1954 }
1955
1956 #ifdef SIGTSTP
1957 SIGNAL (SIGTSTP, tstat);
1958 #endif
1959 return vmhtty;
1960 }
1961
1962
1963 static int
1964 ttyR (struct Cmd *cmdp)
1965 {
1966 struct record rcs, *rc;
1967
1968 rc = &rcs;
1969
1970 #ifdef SIGTSTP
1971 SIGNAL (SIGTSTP, SIG_IGN);
1972 #endif
1973
1974 if (vmhtty != OK)
1975 return NOTOK;
1976
1977 initrc (rc);
1978
1979 if (cmdp)
1980 fin_io (cmdp, 0);
1981
1982 vmhtty = NOTOK;
1983 switch (rc2rc (RC_EOF, 0, NULL, rc)) {
1984 case RC_ACK:
1985 rc2peer (RC_EOF, 0, NULL);
1986 return OK;
1987
1988 case RC_XXX:
1989 padios (NULL, "%s", rc->rc_data);/* NOTREACHED */
1990
1991 default:
1992 fmt2peer (RC_ERR, "pTTY protocol screw-up");
1993 done (1); /* NOTREACHED */
1994 }
1995
1996 return 1; /* dead code to satisfy compiler */
1997 }
1998
1999
2000 static int
2001 winN (struct Cmd *cmdp, int n, int eof)
2002 {
2003 int i, pd[2];
2004 char buffer[BUFSIZ];
2005 struct record rcs, *rc;
2006
2007 rc = &rcs;
2008 if (vmhpid == NOTOK)
2009 return OK;
2010
2011 initrc (rc);
2012
2013 /* XXX: fseek() too tricky for our own good */
2014 if (!fmsh)
2015 fseek (fp, 0L, SEEK_SET);
2016
2017 vmhpid = OK;
2018
2019 snprintf (buffer, sizeof(buffer), "%d", n);
2020 switch (str2rc (RC_WIN, buffer, rc)) {
2021 case RC_ACK:
2022 break;
2023
2024 case RC_ERR:
2025 return NOTOK;
2026
2027 case RC_XXX:
2028 padios (NULL, "%s", rc->rc_data);
2029
2030 default:
2031 fmt2peer (RC_ERR, "pWIN protocol screw-up");
2032 done (1);
2033 }
2034
2035 if (pipe (pd) == NOTOK) {
2036 err2peer (RC_ERR, "pipe", "unable to");
2037 return NOTOK;
2038 }
2039
2040 switch (vmhpid = fork()) {
2041 case NOTOK:
2042 err2peer (RC_ERR, "fork", "unable to");
2043 close (pd[0]);
2044 close (pd[1]);
2045 return NOTOK;
2046
2047 case OK:
2048 close (pd[1]);
2049 SIGNAL (SIGPIPE, SIG_IGN);
2050 while ((i = read (pd[0], buffer, sizeof buffer)) > 0)
2051 switch (rc2rc (RC_DATA, i, buffer, rc)) {
2052 case RC_ACK:
2053 break;
2054
2055 case RC_ERR:
2056 _exit (1);
2057
2058 case RC_XXX:
2059 advise (NULL, "%s", rc->rc_data);
2060 _exit (2);
2061
2062 default:
2063 fmt2peer (RC_ERR, "pWIN protocol screw-up");
2064 _exit (2);
2065 }
2066 if (i == OK)
2067 switch (rc2rc (RC_EOF, 0, NULL, rc)) {
2068 case RC_ACK:
2069 if (eof)
2070 rc2peer (RC_EOF, 0, NULL);
2071 i = 0;
2072 break;
2073
2074 case RC_XXX:
2075 advise (NULL, "%s", rc->rc_data);
2076 i = 2;
2077 break;
2078
2079 default:
2080 fmt2peer (RC_ERR, "pWIN protocol screw-up");
2081 i = 2;
2082 break;
2083 }
2084 if (i == NOTOK)
2085 err2peer (RC_ERR, "pipe", "error reading from");
2086 close (pd[0]);
2087 _exit (i != NOTOK ? i : 1);
2088
2089 default:
2090 if ((vmhfd0 = dup (fileno (stdin))) == NOTOK)
2091 padios ("standard input", "unable to dup");
2092 if ((vmhfd1 = dup (fileno (stdout))) == NOTOK)
2093 padios ("standard output", "unable to dup");
2094 if ((vmhfd2 = dup (fileno (stderr))) == NOTOK)
2095 padios ("diagnostic output", "unable to dup");
2096
2097 close (0);
2098 if ((i = open ("/dev/null", O_RDONLY)) != NOTOK && i != fileno (stdin)) {
2099 dup2 (i, fileno (stdin));
2100 close (i);
2101 }
2102
2103 fflush (stdout);
2104 if (dup2 (pd[1], fileno (stdout)) == NOTOK)
2105 padios ("standard output", "unable to dup2");
2106 clearerr (stdout);
2107
2108 fflush (stderr);
2109 if (dup2 (pd[1], fileno (stderr)) == NOTOK)
2110 padios ("diagnostic output", "unable to dup2");
2111 clearerr (stderr);
2112
2113 if (cmdp && init_io (cmdp, 0) == NOTOK)
2114 return NOTOK;
2115 pstat = SIGNAL (SIGPIPE, pipeser);
2116 broken_pipe = 1;
2117
2118 close (pd[0]);
2119 close (pd[1]);
2120
2121 return vmhpid;
2122 }
2123 }
2124
2125
2126 static int
2127 winR (struct Cmd *cmdp)
2128 {
2129 int status;
2130
2131 if (vmhpid <= OK)
2132 return NOTOK;
2133
2134 if (cmdp)
2135 fin_io (cmdp, 0);
2136
2137 if (dup2 (vmhfd0, fileno (stdin)) == NOTOK)
2138 padios ("standard input", "unable to dup2");
2139 clearerr (stdin);
2140 close (vmhfd0);
2141
2142 fflush (stdout);
2143 if (dup2 (vmhfd1, fileno (stdout)) == NOTOK)
2144 padios ("standard output", "unable to dup2");
2145 clearerr (stdout);
2146 close (vmhfd1);
2147
2148 fflush (stderr);
2149 if (dup2 (vmhfd2, fileno (stderr)) == NOTOK)
2150 padios ("diagnostic output", "unable to dup2");
2151 clearerr (stderr);
2152 close (vmhfd2);
2153
2154 SIGNAL (SIGPIPE, pstat);
2155
2156 if ((status = pidwait (vmhpid, OK)) == 2)
2157 done (1);
2158
2159 vmhpid = OK;
2160 return (status == 0 ? OK : NOTOK);
2161 }
2162
2163
2164 static int
2165 winX (int n)
2166 {
2167 int i, pid, pd[2];
2168 char buffer[BUFSIZ];
2169 struct record rcs, *rc;
2170
2171 rc = &rcs;
2172 initrc (rc);
2173
2174 /* XXX: fseek() too tricky for our own good */
2175 if (!fmsh)
2176 fseek (fp, 0L, SEEK_SET);
2177
2178 snprintf (buffer, sizeof(buffer), "%d", n);
2179 switch (str2rc (RC_WIN, buffer, rc)) {
2180 case RC_ACK:
2181 break;
2182
2183 case RC_ERR:
2184 return NOTOK;
2185
2186 case RC_XXX:
2187 padios (NULL, "%s", rc->rc_data);
2188
2189 default:
2190 fmt2peer (RC_ERR, "pWIN protocol screw-up");
2191 done (1);
2192 }
2193
2194 if (pipe (pd) == NOTOK) {
2195 err2peer (RC_ERR, "pipe", "unable to");
2196 return NOTOK;
2197 }
2198
2199 switch (pid = fork ()) {
2200 case NOTOK:
2201 err2peer (RC_ERR, "fork", "unable to");
2202 close (pd[0]);
2203 close (pd[1]);
2204 return NOTOK;
2205
2206 case OK:
2207 close (fileno (stdin));
2208 if ((i = open ("/dev/null", O_RDONLY)) != NOTOK && i != fileno (stdin)) {
2209 dup2 (i, fileno (stdin));
2210 close (i);
2211 }
2212 dup2 (pd[1], fileno (stdout));
2213 dup2 (pd[1], fileno (stderr));
2214 close (pd[0]);
2215 close (pd[1]);
2216 vmhpid = NOTOK;
2217 return OK;
2218
2219 default:
2220 close (pd[1]);
2221 while ((i = read (pd[0], buffer, sizeof buffer)) > 0)
2222 switch (rc2rc (RC_DATA, i, buffer, rc)) {
2223 case RC_ACK:
2224 break;
2225
2226 case RC_ERR:
2227 close (pd[0]);
2228 pidwait (pid, OK);
2229 return NOTOK;
2230
2231 case RC_XXX:
2232 padios (NULL, "%s", rc->rc_data);
2233
2234 default:
2235 fmt2peer (RC_ERR, "pWIN protocol screw-up");
2236 done (1);
2237 }
2238 if (i == OK)
2239 switch (rc2rc (RC_EOF, 0, NULL, rc)) {
2240 case RC_ACK:
2241 break;
2242
2243 case RC_XXX:
2244 padios (NULL, "%s", rc->rc_data);
2245
2246 default:
2247 fmt2peer (RC_ERR, "pWIN protocol screw-up");
2248 done (1);
2249 }
2250 if (i == NOTOK)
2251 err2peer (RC_ERR, "pipe", "error reading from");
2252
2253 close (pd[0]);
2254 pidwait (pid, OK);
2255 return (i != NOTOK ? pid : NOTOK);
2256 }
2257 }
2258
2259
2260 void
2261 padios (char *what, char *fmt, ...)
2262 {
2263 va_list ap;
2264
2265 va_start(ap, fmt);
2266 if (vmh) {
2267 verr2peer (RC_FIN, what, fmt, ap);
2268 rcdone ();
2269 } else {
2270 advertise (what, NULL, fmt, ap);
2271 }
2272 va_end(ap);
2273
2274 done (1);
2275 }
2276
2277
2278 void
2279 padvise (char *what, char *fmt, ...)
2280 {
2281 va_list ap;
2282
2283 va_start(ap, fmt);
2284 if (vmh) {
2285 verr2peer (RC_ERR, what, fmt, ap);
2286 } else {
2287 advertise (what, NULL, fmt, ap);
2288 }
2289 va_end(ap);
2290 }