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