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