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