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