Mercurial > hg > Applications > mh
diff uip/msh.c @ 0:bce86c4163a3
Initial revision
author | kono |
---|---|
date | Mon, 18 Apr 2005 23:46:02 +0900 |
parents | |
children | 441a2190cfae |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/uip/msh.c Mon Apr 18 23:46:02 2005 +0900 @@ -0,0 +1,2820 @@ +/* msh.c - The MH shell (sigh) */ +#ifndef lint +static char ident[] = "@(#)$Id$"; +#endif /* lint */ + +/* TODO: + Keep more status information in maildrop map + */ + +#include "../h/mh.h" +#include "../h/dropsbr.h" +#include "../h/formatsbr.h" +#include "../h/scansbr.h" +#include "../zotnet/tws.h" +#include <stdio.h> +#include "../zotnet/mts.h" +#include <ctype.h> +#include <sys/types.h> +#include <sys/stat.h> +#ifndef SYS5 +#include <sgtty.h> +#else /* SYS5 */ +#include <termio.h> +#ifndef NOIOCTLH +#include <sys/ioctl.h> +#endif /* NOIOCTLH */ +#endif /* SYS5 */ +#include <pwd.h> +#include <setjmp.h> +#include <signal.h> +#include "../h/mshsbr.h" +#include "../h/vmhsbr.h" +#ifdef LOCALE +#include <locale.h> +#endif +#ifdef READLINE +#include <readline/readline.h> +#endif +#ifndef MIME +#define MIMEminc(a) (a) +#else +#define MIMEminc(a) 0 +#endif + +#define QUOTE '\\' /* sigh */ + + +/* */ + +static struct swit switches[] = { +#define IDSW 0 + "idstart number", -7, /* interface from bbc */ +#define FDSW 1 + "idstop number", -6, /* .. */ +#define QDSW 2 + "idquit number", -6, /* .. */ +#define NMSW 3 + "idname BBoard", -6, /* .. */ + +#define PRMPTSW 4 + "prompt string", 0, + +#define SCANSW 5 + "scan", 0, +#define NSCANSW 6 + "noscan", 0, + +#define READSW 7 + "vmhread fd", -7, +#define WRITESW 8 + "vmhwrite fd", -8, + +#define PREADSW 9 + "popread fd", -7, +#define PWRITSW 10 + "popwrite fd", -8, + +#define TCURSW 11 + "topcur", 0, +#define NTCURSW 12 + "notopcur", 0, + +#define HELPSW 13 + "help", 4, + + NULL, 0 +}; + +/* */ + /* FOLDER */ +char *fmsh = NULL; /* folder instead of file */ +int modified; /* command modified folder */ +struct msgs *mp; /* used a lot */ +static int nMsgs = 0; +struct Msg *Msgs = NULL; /* Msgs[0] not used */ +static FILE *fp; /* input file */ +static FILE *yp = NULL; /* temporary file */ +static int mode; /* mode of file */ +static int numfds = 0; /* number of files cached */ +static int maxfds = 0; /* number of files cached to be cached */ +static time_t mtime = (time_t) 0;/* mtime of file */ + + + /* VMH */ +#define ALARM ((unsigned int) 10) +#define ttyN(c) ttyNaux ((c), NULLCP) + +static int vmh = 0; + +static int vmhpid = OK; +static int vmhfd0; +static int vmhfd1; +static int vmhfd2; + +static int vmhtty = NOTOK; + +#define SCAN 1 +#define STATUS 2 +#define DISPLAY 3 +#define NWIN DISPLAY + +static int topcur = 0; + +static int numwins = 0; +static int windows[NWIN + 1]; + +static jmp_buf peerenv; + +void padios (), padvise (); +static TYPESIG alrmser (); + + +#if defined(BPOP) || defined(NNTP) + /* POP */ + +int pmsh = 0; /* BPOP enabled */ + +extern char response[]; +#endif /* BPOP || NNTP */ + + + /* PARENT */ +static int pfd = NOTOK; /* fd parent is reading from */ +static int ppid = 0; /* pid of parent */ + + + /* COMMAND */ +int interactive; /* running from a /dev/tty */ +int redirected; /* re-directing output */ +FILE *sp = NULL; /* original stdout */ + +char *cmd_name; /* command being run */ + +char myfilter[BUFSIZ]; /* path to mhl.forward */ + +static char *myprompt = "(%s) ";/* prompting string */ + + + /* BBOARDS */ +static int gap; /* gap in BBoard-ID:s */ + +static char *myname = NULL; /* BBoard name */ + +char *BBoard_ID = "BBoard-ID";/* BBoard-ID constant */ + + /* SIGNALS */ +TYPESIG (*istat) (); /* original SIGINT */ +static TYPESIG (*pstat) (); /* current SIGPIPE */ +TYPESIG (*qstat) (); /* original SIGQUIT */ +#ifdef SIGTSTP +static TYPESIG (*tstat) (); /* original SIGTSTP */ +#endif /* SIGTSTP */ +int interrupted; /* SIGINT detected */ +int broken_pipe; /* SIGPIPE detected */ +int told_to_quit; /* SIGQUIT detected */ + +#if defined(BSD42) || defined(SVR4) +int should_intr; /* signal handler should interrupt call */ +jmp_buf sigenv; /* the environment pointer */ +#endif /* BSD42 || SVR4 */ + +static TYPESIG intrser (), pipeser (), quitser (); + + +#ifndef __STDC__ +#ifdef SYS5 +struct passwd *getpwnam (); +#endif /* SYS5 */ +#endif + +static int read_map(), read_file(), check_folder(), getargs(), parse(); +static int getcmds(), init_io(), initaux_io(), finaux_io(), peerwait(); +static int pINI(), pQRY(), pQRY1(), pQRY2(), pCMD(), pFIN(); +static int ttyR(), ttyNaux(), winN(), winR(), winX(); +static msh(), m_gMsgs(), scanrange(), scanstring(), quit(); +static fin_io(), m_init(); +#if defined(BPOP) || defined(NNTP) +static int read_pop(); +#endif +/* */ + +/* ARGSUSED */ + +main (argc, argv) +int argc; +char **argv; +{ + int id = 0, + scansw = 0, + vmh1 = 0, + vmh2 = 0; +#if defined(BPOP) || defined(NNTP) + int pmsh1 = 0, + pmsh2 = 0; +#endif /* BPOP || NNTP */ + char *cp, + *file = NULL, + *folder = NULL, + **ap, + **argp, + buf[80], + *arguments[MAXARGS]; + +#ifdef LOCALE + setlocale(LC_ALL, ""); +#endif +#ifdef JAPAN + ml_init(); +#endif /* JAPAN */ + invo_name = r1bindex (argv[0], '/'); + mts_init (invo_name); + if ((cp = m_find (invo_name)) != NULL) { + ap = brkstring (cp = getcpy (cp), " ", "\n"); + ap = copyip (ap, arguments); + } + else + ap = arguments; + (void) copyip (argv + 1, ap); + argp = arguments; + +/* */ + + while (cp = *argp++) { + if (*cp == '-') + switch (smatch (++cp, switches)) { + case AMBIGSW: + ambigsw (cp, switches); + done (1); + case UNKWNSW: + adios (NULLCP, "-%s unknown", cp); + case HELPSW: + (void) sprintf (buf, "%s [switches] file", invo_name); + help (buf, switches); + done (1); + + case IDSW: + if (!(cp = *argp++) || *cp == '-') + adios (NULLCP, "missing argument to %s", argp[-2]); + if ((id = atoi (cp)) < 1) + adios (NULLCP, "bad argument %s %s", argp[-2], cp); + continue; + case FDSW: + if (!(cp = *argp++) || *cp == '-') + adios (NULLCP, "missing argument to %s", argp[-2]); + if ((pfd = atoi (cp)) <= 1) + adios (NULLCP, "bad argument %s %s", argp[-2], cp); + continue; + case QDSW: + if (!(cp = *argp++) || *cp == '-') + adios (NULLCP, "missing argument to %s", argp[-2]); + if ((ppid = atoi (cp)) <= 1) + adios (NULLCP, "bad argument %s %s", argp[-2], cp); + continue; + case NMSW: + if (!(myname = *argp++) || *myname == '-') + adios (NULLCP, "missing argument to %s", argp[-2]); + continue; + + case SCANSW: + scansw++; + continue; + case NSCANSW: + scansw = 0; + continue; + + case PRMPTSW: + if (!(myprompt = *argp++) || *myprompt == '-') + adios (NULLCP, "missing argument to %s", argp[-2]); + continue; + + case READSW: + if (!(cp = *argp++) || *cp == '-') + adios (NULLCP, "missing argument to %s", argp[-2]); + if ((vmh1 = atoi (cp)) < 1) + adios (NULLCP, "bad argument %s %s", argp[-2], cp); + continue; + case WRITESW: + if (!(cp = *argp++) || *cp == '-') + adios (NULLCP, "missing argument to %s", argp[-2]); + if ((vmh2 = atoi (cp)) < 1) + adios (NULLCP, "bad argument %s %s", argp[-2], cp); + continue; + + case PREADSW: + if (!(cp = *argp++) || *cp == '-') + adios (NULLCP, "missing argument to %s", argp[-2]); +#if defined(BPOP) || defined(NNTP) + if ((pmsh1 = atoi (cp)) < 1) + adios (NULLCP, "bad argument %s %s", argp[-2], cp); +#endif /* BPOP || NNTP */ + continue; + case PWRITSW: + if (!(cp = *argp++) || *cp == '-') + adios (NULLCP, "missing argument to %s", argp[-2]); +#if defined(BPOP) || defined(NNTP) + if ((pmsh2 = atoi (cp)) < 1) + adios (NULLCP, "bad argument %s %s", argp[-2], cp); +#endif /* BPOP || NNTP */ + continue; + + case TCURSW: + topcur++; + continue; + case NTCURSW: + topcur = 0; + continue; + } + if (*cp == '+' || *cp == '@') { + if (folder) + adios (NULLCP, "only one folder at a time!"); + else + folder = path (cp + 1, *cp == '+' ? TFOLDER : TSUBCWF); + } + else + if (file) + adios (NULLCP, "only one file at a time!"); + else + file = cp; + } + +/* */ + + if (!file && !folder) + file = "./msgbox"; + if (file && folder) + adios (NULLCP, "use a file or a folder, not both"); + (void) strcpy (myfilter, libpath (mhlforward)); +#ifdef FIOCLEX + if (pfd > 1) + (void) ioctl (pfd, FIOCLEX, NULLCP); +#endif /* FIOCLEX */ + +#if defined(BSD42) || defined(SVR4) + should_intr = 0; +#endif /* BSD42 || SVR4 */ + setsigx (istat, SIGINT, intrser); + setsigx (qstat, SIGQUIT, quitser); + + (void) sc_width (); /* MAGIC... */ + + if (vmh = vmh1 && vmh2) { + (void) rcinit (vmh1, vmh2); + (void) pINI (); + (void) signal (SIGINT, SIG_IGN); + (void) signal (SIGQUIT, SIG_IGN); +#ifdef SIGTSTP + tstat = signal (SIGTSTP, SIG_IGN); +#endif /* SIGTSTP */ + } + +#if defined(BPOP) || defined(NNTP) + if (pmsh = pmsh1 && pmsh2) { + cp = getenv ("MHPOPDEBUG"); +#ifdef NNTP + if (pop_set (pmsh1, pmsh2, cp && *cp, myname) == NOTOK) +#else /* NNTP */ + if (pop_set (pmsh1, pmsh2, cp && *cp) == NOTOK) +#endif /* NNTP */ + padios (NULLCP, "%s", response); + if (folder) + file = folder, folder = NULL; + } +#endif /* BPOP || NNTP */ + + if (folder) + fsetup (folder); + else + setup (file); + readids (id); + display_info (id > 0 ? scansw : 0); + + msh (id > 0 ? scansw : 0); + + m_reset (); + + done (0); +} + +/* */ + +static struct swit mshcmds[] = { +#define ADVCMD 0 + "advance", -7, +#define ALICMD 1 + "ali", 0, +#define EXPLCMD 2 + "burst", 0, +#define COMPCMD 3 + "comp", 0, +#define DISTCMD 4 + "dist", 0, +#define EXITCMD 5 + "exit", 0, +#define FOLDCMD 6 + "folder", 0, +#define FORWCMD 7 + "forw", 0, +#define HELPCMD 8 + "help", 0, +#define INCMD 9 + "inc", 0, +#define MARKCMD 10 + "mark", 0, +#define MAILCMD 11 + "mhmail", 0, +#define MHNCMD 12 + "mhn", MIMEminc(-3), +#define MSGKCMD 13 + "msgchk", 0, +#define NEXTCMD 14 + "next", 0, +#define PACKCMD 15 + "packf", 0, +#define PICKCMD 16 + "pick", 0, +#define PREVCMD 17 + "prev", 0, +#define QUITCMD 18 + "quit", 0, +#define FILECMD 19 + "refile", 0, +#define REPLCMD 20 + "repl", 0, +#define RMMCMD 21 + "rmm", 0, +#define SCANCMD 22 + "scan", 0, +#define SENDCMD 23 + "send", 0, +#define SHOWCMD 24 + "show", 0, +#define SORTCMD 25 + "sortm", 0, +#define WHATCMD 26 + "whatnow", 0, +#define WHOMCMD 27 + "whom", 0, + + NULL, 0 +}; + +/* */ + +static msh (scansw) +int scansw; +{ + int i; + register char *cp, + **ap; + char prompt[BUFSIZ], + *vec[MAXARGS]; + struct Cmd typein; + register struct Cmd *cmdp; + static int once_only = ADVCMD; + + (void) sprintf (prompt, myprompt, invo_name); + cmdp = &typein; + +#ifdef READLINE + (void) initialize_readline(); +#endif + for (;;) { + if (yp) { + (void) fclose (yp); + yp = NULL; + } + if (vmh) { + if ((i = getcmds (mshcmds, cmdp, scansw)) == EOF) { + (void) rcdone (); + return; + } + } + else { + (void) check_folder (scansw); + if ((i = getargs (prompt, mshcmds, cmdp)) == EOF) { + (void) putchar ('\n'); + return; + } + } + cmd_name = mshcmds[i].sw; + + switch (i) { + case QUITCMD: + quit (); + return; + + case ADVCMD: + if (once_only == ADVCMD) + once_only = i = SHOWCMD; + else + i = mp -> curmsg != mp -> hghmsg ? NEXTCMD : EXITCMD; + cmd_name = mshcmds[i].sw; + /* and fall... */ + + case EXITCMD: + case EXPLCMD: + case FOLDCMD: + case FORWCMD: /* sigh */ + case MARKCMD: + case NEXTCMD: + case PACKCMD: + case PICKCMD: + case PREVCMD: + case RMMCMD: + case SHOWCMD: + case SCANCMD: + case SORTCMD: + if ((cp = m_find (cmd_name)) != NULL) { + ap = brkstring (cp = getcpy (cp), " ", "\n"); + ap = copyip (ap, vec); + } + else + ap = vec; + break; + + default: + cp = NULL; + ap = vec; + break; + } + (void) copyip (cmdp -> args + 1, ap); + + m_init (); + + if (!vmh && init_io (cmdp, vmh) == NOTOK) { + if (cp != NULL) + free (cp); + continue; + } + modified = 0; + redirected = vmh || cmdp -> direction != STDIO; + + switch (i) { + case ALICMD: + case COMPCMD: + case INCMD: + case MAILCMD: + case MSGKCMD: + case SENDCMD: + case WHATCMD: + case WHOMCMD: + if (!vmh || ttyN (cmdp) != NOTOK) + forkcmd (vec, cmd_name); + break; + + case DISTCMD: + if (!vmh || ttyN (cmdp) != NOTOK) + distcmd (vec); + break; + + case EXPLCMD: + if (!vmh || winN (cmdp, DISPLAY, 1) != NOTOK) + explcmd (vec); + break; + + case FILECMD: + if (!vmh + || (filehak (vec) == OK ? ttyN (cmdp) + : winN (cmdp, DISPLAY, 1)) != NOTOK) + filecmd (vec); + break; + + case FOLDCMD: + if (!vmh || winN (cmdp, DISPLAY, 1) != NOTOK) + foldcmd (vec); + break; + + case FORWCMD: + if (!vmh || ttyN (cmdp) != NOTOK) + forwcmd (vec); + break; + + case HELPCMD: + if (!vmh || winN (cmdp, DISPLAY, 1) != NOTOK) + helpcmd (vec); + break; + + case EXITCMD: + case MARKCMD: + if (!vmh || winN (cmdp, DISPLAY, 1) != NOTOK) + markcmd (vec); + break; + + case MHNCMD: +#ifdef MIME + if (!vmh || ttyN (cmdp) != NOTOK) + mhncmd (vec); +#endif + break; + + case NEXTCMD: + case PREVCMD: + case SHOWCMD: + if (!vmh || winN (cmdp, DISPLAY, 1) != NOTOK) + showcmd (vec); + break; + + case PACKCMD: + if (!vmh + || (packhak (vec) == OK ? ttyN (cmdp) + : winN (cmdp, DISPLAY, 1)) != NOTOK) + packcmd (vec); + break; + + case PICKCMD: + if (!vmh || winN (cmdp, DISPLAY, 1) != NOTOK) + pickcmd (vec); + break; + + case REPLCMD: + if (!vmh || ttyN (cmdp) != NOTOK) + replcmd (vec); + break; + + case RMMCMD: + if (!vmh || winN (cmdp, DISPLAY, 1) != NOTOK) + rmmcmd (vec); + break; + + case SCANCMD: + if (!vmh || winN (cmdp, DISPLAY, 1) != NOTOK) + scancmd (vec); + break; + + case SORTCMD: + if (!vmh || winN (cmdp, DISPLAY, 1) != NOTOK) + sortcmd (vec); + break; + + default: + padios (NULLCP, "no dispatch for %s", cmd_name); + } + + if (vmh) { + if (vmhtty != NOTOK) + (void) ttyR (cmdp); + if (vmhpid > OK) + (void) winR (cmdp); + } + else + fin_io (cmdp, vmh); + if (cp != NULL) + free (cp); + if (i == EXITCMD) { + quit (); + return; + } + } +} + +/* */ + +fsetup (folder) +char *folder; +{ + register int msgnum; + char *maildir; + struct stat st; + + maildir = m_maildir (folder); + if (chdir (maildir) == NOTOK) + padios (maildir, "unable to change directory to"); + if (!(mp = m_gmsg (folder))) + padios (NULLCP, "unable to read folder %s", folder); + if (mp -> hghmsg == 0) + padios (NULLCP, "no messages in %s", folder); + + mode = m_gmprot (); + mtime = stat (mp -> foldpath, &st) != NOTOK ? st.st_mtime : 0; + + m_gMsgs (mp -> hghmsg); + + for (msgnum = mp -> lowmsg; msgnum <= mp -> hghmsg; msgnum++) { + Msgs[msgnum].m_bboard_id = 0; + Msgs[msgnum].m_top = NOTOK; + Msgs[msgnum].m_start = Msgs[msgnum].m_stop = 0L; + Msgs[msgnum].m_scanl = NULL; + } + + m_init (); + + fmsh = getcpy (folder); + +#if !defined(BSD42) && !defined(linux) && defined(_NFILE) + maxfds = _NFILE / 2; +#else /* BSD42 || linux */ + maxfds = getdtablesize () / 2; +#endif /* BSD42 || linux */ + if ((maxfds -= 2) < 1) + maxfds = 1; +} + +/* */ + +setup (file) +char *file; +{ + int i, + msgp; +#if defined(BPOP) || defined(NNTP) + char tmpfil[BUFSIZ]; +#endif /* BPOP || NNTP */ + struct stat st; + +#if defined(BPOP) || defined(NNTP) + if (pmsh) { + (void) strcpy (tmpfil, m_tmpfil (invo_name)); + if ((fp = fopen (tmpfil, "w+")) == NULL) + padios (tmpfil, "unable to create"); + (void) unlink (tmpfil); + } + else +#endif /* BPOP || NNTP */ + if ((fp = fopen (file, "r")) == NULL) + padios (file, "unable to read"); +#ifdef FIOCLEX + (void) ioctl (fileno (fp), FIOCLEX, NULLCP); +#endif /* FIOCLEX */ + if (fstat (fileno (fp), &st) != NOTOK) { + mode = (int) (st.st_mode & 0777), mtime = st.st_mtime; + msgp = read_map (file, (long) st.st_size); + } + else { + mode = m_gmprot (), mtime = 0; + msgp = 0; + } + + if ((msgp = read_file (msgp ? Msgs[msgp].m_stop : 0L, msgp + 1)) < 1) + padios (NULLCP, "no messages in %s", myname ? myname : file); + + mp = (struct msgs *) calloc ((unsigned) 1, MHSIZE (mp, 1, msgp + 1)); + if (mp == NULL) + padios (NULLCP, "unable to allocate folder storage"); + + mp -> hghmsg = msgp; + mp -> nummsg = msgp; + mp -> lowmsg = 1; + mp -> curmsg = 0; + + mp -> foldpath = getcpy (myname ? myname : file); + mp -> msgflags = 0; +#if defined(BPOP) || defined(NNTP) + if (pmsh) + mp -> msgflags |= READONLY; + else { +#endif /* BPOP || NNTP */ + (void) stat (file, &st); + if (st.st_uid != getuid () || access (file, 02) == NOTOK) + mp -> msgflags |= READONLY; +#if defined(BPOP) || defined(NNTP) + } +#endif /* BPOP || NNTP */ + mp -> lowoff = 1; + mp -> hghoff = mp -> hghmsg + 1; + +#ifdef MTR + mp -> msgstats = (short *) + calloc ((unsigned) 1, MHSIZEX (mp, mp -> lowmsg, mp -> hghmsg)); + if (mp -> msgstats == NULL) + padios (NULLCP, "unable to allocate messages storage"); + mp -> msgstats = (mp -> msgbase = mp -> msgstats) - mp -> lowoff; + if (mp -> msgstats < (short *)0) + padios (NULLCP, "setup() botch -- you lose big"); +#endif /* MTR */ +#if defined(BPOP) || defined(NNTP) + if (pmsh) { +#ifndef NNTP + for (i = mp -> lowmsg; i <= mp -> hghmsg; i++) { + Msgs[i].m_top = i; + mp -> msgstats[i] = EXISTS | VIRTUAL; + } +#else /* NNTP */ + for (i = mp -> lowmsg; i <= mp -> hghmsg; i++) { + if (Msgs[i].m_top) /* set in read_pop() */ + mp -> msgstats[i] = EXISTS | VIRTUAL; + } +#endif /* NNTP */ + } + else +#endif /* BPOP || NNTP */ + for (i = mp -> lowmsg; i <= mp -> hghmsg; i++) + mp -> msgstats[i] = EXISTS; + m_init (); + + mp -> msgattrs[0] = getcpy ("unseen"); + mp -> msgattrs[1] = NULL; + + m_unknown (fp); /* the MAGIC invocation */ + if (fmsh) { + free (fmsh); + fmsh = NULL; + } +} + +/* */ + +static int read_map (file, size) +char *file; +long size; +{ + register int i, + msgp; + register struct drop *dp, + *mp; + struct drop *rp; + +#if defined(BPOP) || defined(NNTP) + if (pmsh) + return read_pop (); +#endif /* BPOP || NNTP */ + + if ((i = map_read (file, size, &rp, 1)) == 0) + return 0; + + m_gMsgs (i); + + msgp = 1; + for (dp = rp + 1; i-- > 0; msgp++, dp++) { + mp = &Msgs[msgp].m_drop; + mp -> d_id = dp -> d_id; + mp -> d_size = dp -> d_size; + mp -> d_start = dp -> d_start; + mp -> d_stop = dp -> d_stop; + Msgs[msgp].m_scanl = NULL; + } + free ((char *) rp); + + return (msgp - 1); +} + +/* */ + +static int read_file (pos, msgp) +register long pos; +register int msgp; +{ + register int i; + register struct drop *dp, + *mp; + struct drop *rp; + +#if defined(BPOP) || defined(NNTP) + if (pmsh) + return (msgp - 1); +#endif /* BPOP || NNTP */ + + if ((i = mbx_read (fp, pos, &rp, 1)) <= 0) + return (msgp - 1); + + m_gMsgs ((msgp - 1) + i); + + for (dp = rp; i-- > 0; msgp++, dp++) { + mp = &Msgs[msgp].m_drop; + mp -> d_id = 0; + mp -> d_size = dp -> d_size; + mp -> d_start = dp -> d_start; + mp -> d_stop = dp -> d_stop; + Msgs[msgp].m_scanl = NULL; + } + free ((char *) rp); + + return (msgp - 1); +} + +/* */ + +#if defined(BPOP) || defined(NNTP) +#ifdef NNTP +static int pop_base = 0; + +static int pop_statmsg (s) +register char *s; +{ + register int i, n; + + n = (i = atoi (s)) - pop_base; /* s="nnn header-line..." */ + Msgs[n].m_top = Msgs[n].m_bboard_id = i; +} + +#endif /* NNTP */ +static int read_pop () { + int nmsgs, + nbytes; + + if (pop_stat (&nmsgs, &nbytes) == NOTOK) + padios (NULLCP, "%s", response); + + m_gMsgs (nmsgs); + +#ifdef NNTP /* this makes read_pop() do some real work... */ + pop_base = nbytes - 1; /* nmsgs=last-first+1, nbytes=first */ + pop_exists (pop_statmsg); +#endif /* NNTP */ + return nmsgs; +} + + +static int pop_action (s) +register char *s; +{ + fprintf (yp, "%s\n", s); +} +#endif /* BPOP || NNTP */ + +/* */ + +static m_gMsgs (n) +int n; +{ + int nmsgs; + + if (Msgs == NULL) { + nMsgs = n + MAXFOLDER / 2; + Msgs = (struct Msg *) calloc ((unsigned) (nMsgs + 2), sizeof *Msgs); + if (Msgs == NULL) + padios (NULLCP, "unable to allocate Msgs structure"); + return; + } + + if (nMsgs >= n) + return; + + nmsgs = nMsgs + n + MAXFOLDER / 2; + Msgs = (struct Msg *) realloc ((char *) Msgs, + (unsigned) (nmsgs + 2) * sizeof *Msgs); + if (Msgs == NULL) + padios (NULLCP, "unable to reallocate Msgs structure"); + bzero ((char *) (Msgs + nMsgs + 2), + (unsigned) ((nmsgs - nMsgs) * sizeof *Msgs)); + + nMsgs = nmsgs; +} + +/* */ + +FILE *msh_ready (msgnum, full) +register int msgnum; +int full; +{ + register int msgp; + int fd; + long pos1, + pos2; + char *cp, + tmpfil[BUFSIZ]; + + if (yp) { + (void) fclose (yp); + yp = NULL; + } + + if (fmsh) { + if ((fd = Msgs[msgnum].m_top) == NOTOK) { + if (numfds >= maxfds) + for (msgp = mp -> lowmsg; msgp <= mp -> hghmsg; msgp++) + if (Msgs[msgp].m_top != NOTOK) { + (void) close (Msgs[msgp].m_top); + Msgs[msgp].m_top = NOTOK; + numfds--; + break; + } + + if ((fd = open (cp = m_name (msgnum), 0)) == NOTOK) + padios (cp, "unable to open message"); + Msgs[msgnum].m_top = fd; + numfds++; + } + + if ((fd = dup (fd)) == NOTOK) + padios ("cached message", "unable to dup"); + if ((yp = fdopen (fd, "r")) == NULL) + padios (NULLCP, "unable to fdopen cached message"); + (void) fseek (yp, 0L, 0); + return yp; + } + +#if defined(BPOP) || defined(NNTP) + if (pmsh && (mp -> msgstats[msgnum] & VIRTUAL)) { + if (Msgs[msgnum].m_top == 0) + padios (NULLCP, "msh_ready (%d, %d) botch", msgnum, full); + if (!full) { + (void) strcpy (tmpfil, m_tmpfil (invo_name)); + if ((yp = fopen (tmpfil, "w+")) == NULL) + padios (tmpfil, "unable to create"); + (void) unlink (tmpfil); + + if (pop_top (Msgs[msgnum].m_top, 4, pop_action) == NOTOK) +#ifndef NNTP + padios (NULLCP, "%s", response); +#else /* NNTP */ + { + if (strncmp(response, "423 ", 4) == 0) + /* 423 no such article number in this group */ + fprintf (yp, "Expired:\n\n"); /* dummy */ + else + padios (NULLCP, "%s", response); + } +#endif /* NNTP */ + + m_eomsbr ((int (*)()) 0); /* XXX */ + msg_style = MS_DEFAULT; /* .. */ + (void) fseek (yp, 0L, 0); + return yp; + } + + (void) fseek (fp, 0L, 2); + (void) fwrite (mmdlm1, 1, strlen (mmdlm1), fp); + if (fflush (fp)) + padios ("temporary file", "write error on"); + (void) fseek (fp, 0L, 2); + pos1 = ftell (fp); + + yp = fp; + if (pop_retr (Msgs[msgnum].m_top, pop_action) == NOTOK) +#ifndef NNTP + padios (NULLCP, "%s", response); +#else /* NNTP */ + { + if (strncmp(response, "423 ", 4) == 0) + /* 423 no such article number in this group */ + fprintf (yp, "Expired:\n\n"); /* dummy */ + else + padios (NULLCP, "%s", response); + } +#endif /* NNTP */ + yp = NULL; + + (void) fseek (fp, 0L, 2); + pos2 = ftell (fp); + (void) fwrite (mmdlm2, 1, strlen (mmdlm2), fp); + if (fflush (fp)) + padios ("temporary file", "write error on"); + + Msgs[msgnum].m_start = pos1; + Msgs[msgnum].m_stop = pos2; + + mp -> msgstats[msgnum] &= ~VIRTUAL; + } +#endif /* BPOP || NNTP */ + + m_eomsbr ((int (*)()) 0); /* XXX */ + (void) fseek (fp, Msgs[msgnum].m_start, 0); + return fp; +} + +/* */ + +static int check_folder (scansw) +int scansw; +{ + int flags, + i, + low, + hgh, + msgp; + struct stat st; + +#if defined(BPOP) || defined(NNTP) + if (pmsh) + return 0; +#endif /* BPOP || NNTP */ + + if (fmsh) { + if (stat (mp -> foldpath, &st) == NOTOK) + padios (mp -> foldpath, "unable to stat"); + if (mtime == st.st_mtime) + return 0; + mtime = st.st_mtime; + + low = mp -> hghmsg + 1; + m_fmsg (mp); + + if (!(mp = m_gmsg (fmsh))) + padios (NULLCP, "unable to re-read folder %s", fmsh); + + hgh = mp -> hghmsg; + + for (msgp = mp -> lowmsg; msgp <= mp -> hghmsg; msgp++) { + if (Msgs[msgp].m_top != NOTOK) { + (void) close (Msgs[msgp].m_top); + Msgs[msgp].m_top = NOTOK; + numfds--; + } + if (Msgs[msgp].m_scanl) { + free (Msgs[msgp].m_scanl); + Msgs[msgp].m_scanl = NULL; + } + } + + m_init (); + + if (modified || low > hgh) + return 1; + goto check_vmh; + } + if (fstat (fileno (fp), &st) == NOTOK) + padios (mp -> foldpath, "unable to fstat"); + if (mtime == st.st_mtime) + return 0; + mode = (int) (st.st_mode & 0777); + mtime = st.st_mtime; + + if ((msgp = read_file (Msgs[mp -> hghmsg].m_stop, mp -> hghmsg + 1)) < 1) + padios (NULLCP, "no messages in %s", mp -> foldpath); /* XXX */ + if (msgp <= mp -> hghmsg) + return 0; /* XXX */ + + if ((mp = m_remsg (mp, 0, msgp)) == NULL) + padios (NULLCP, "unable to allocate folder storage"); + + low = mp -> hghmsg + 1, hgh = msgp; + flags = scansw ? m_seqflag (mp, "unseen") : 0; + for (i = mp -> hghmsg + 1; i <= msgp; i++) { + mp -> msgstats[i] = EXISTS | flags; + mp -> nummsg++; + } + mp -> hghmsg = msgp; + m_init (); + +check_vmh: ; + if (vmh) + return 1; + + advise (NULLCP, "new messages have arrived!\007"); + if (scansw) + scanrange (low, hgh); + + return 1; +} + +/* */ + +static scanrange (low, hgh) +int low, + hgh; +{ + char buffer[BUFSIZ]; + + (void) sprintf (buffer, "%d-%d", low, hgh); + scanstring (buffer); +} + + +static scanstring (arg) +char *arg; +{ + char *cp, + **ap, + *vec[MAXARGS]; + + if ((cp = m_find (cmd_name = "scan")) != NULL) { + ap = brkstring (cp = getcpy (cp), " ", "\n"); + ap = copyip (ap, vec); + } + else + ap = vec; + *ap++ = arg; + *ap = NULL; + m_init (); + scancmd (vec); + if (cp != NULL) + free (cp); +} + +/* */ + +readids (id) +int id; +{ + register int cur, + flags, + i, + msgnum; + + if (mp -> curmsg == 0) + m_setcur (mp, mp -> lowmsg); + if (id <= 0 || (flags = m_seqflag (mp, "unseen")) == 0) + return; + + for (msgnum = mp -> hghmsg; msgnum >= mp -> lowmsg; msgnum--) + mp -> msgstats[msgnum] |= flags; + + if (id != 1) { + cur = mp -> curmsg; + + for (msgnum = mp -> hghmsg; msgnum >= mp -> lowmsg; msgnum--) + if (mp -> msgstats[msgnum] & EXISTS) /* FIX */ + if ((i = readid (msgnum)) > 0 && i < id) { + cur = msgnum + 1; + mp -> msgstats[msgnum] &= ~flags; + break; + } + for (i = mp -> lowmsg; i < msgnum; i++) + mp -> msgstats[i] &= ~flags; + + if (cur > mp -> hghmsg) + cur = mp -> hghmsg; + + m_setcur (mp, cur); + } + + if ((gap = 1 < id && id < (i = readid (mp -> lowmsg)) ? id : 0) && !vmh) + advise (NULLCP, "gap in ID:s, last seen %d, lowest present %d\n", + id - 1, i); +} + +/* */ + +int readid (msgnum) +int msgnum; +{ + int i, + state; +#if defined(BPOP) || defined(NNTP) + int arg1, + arg2, + arg3; +#endif /* BPOP || NNTP */ + char *bp, + buf[BUFSIZ], + name[NAMESZ]; + register FILE *zp; + + if (Msgs[msgnum].m_bboard_id) + return Msgs[msgnum].m_bboard_id; +#if defined(BPOP) || defined(NNTP) + if (pmsh) { + if (Msgs[msgnum].m_top == 0) + padios (NULLCP, "readid (%d) botch", msgnum); + if (pop_list (Msgs[msgnum].m_top, (int *) 0, &arg1, &arg2, &arg3) == OK + && arg3 > 0) + return (Msgs[msgnum].m_bboard_id = arg3); + } +#endif /* BPOP || NNTP */ + + zp = msh_ready (msgnum, 0); + for (state = FLD;;) + switch (state = m_getfld (state, name, buf, sizeof buf, zp)) { + case FLD: + case FLDEOF: + case FLDPLUS: + if (uleq (name, BBoard_ID)) { + bp = getcpy (buf); + while (state == FLDPLUS) { + state = m_getfld (state, name, buf, sizeof buf, zp); + bp = add (buf, bp); + } + i = atoi (bp); + free (bp); + if (i > 0) + return (Msgs[msgnum].m_bboard_id = i); + else + continue; + } + while (state == FLDPLUS) + state = m_getfld (state, name, buf, sizeof buf, zp); + if (state != FLDEOF) + continue; + + default: + return 0; + } +} + +/* */ + +display_info (scansw) +int scansw; +{ + int flags, + sd; + + interactive = isatty (fileno (stdout)); + if (sp == NULL) { + if ((sd = dup (fileno (stdout))) == NOTOK) + padios ("standard output", "unable to dup"); +#ifndef BSD42 /* XXX */ +#ifdef FIOCLEX + (void) ioctl (sd, FIOCLEX, NULL); +#endif /* FIOCLEX */ +#endif /* not BSD42 */ + if ((sp = fdopen (sd, "w")) == NULL) + padios ("standard output", "unable to fdopen"); + } + + (void) m_putenv ("mhfolder", mp -> foldpath); + if (vmh) + return; + + if (myname) { + printf ("Reading "); + if (SOprintf ("%s", myname)) + printf ("%s", myname); + printf (", currently at message %d of %d\n", + mp -> curmsg, mp -> hghmsg); + } + else { + printf ("Reading "); + if (fmsh) + printf ("+%s", fmsh); + else + printf ("%s", mp -> foldpath); + printf (", currently at message %d of %d\n", + mp -> curmsg, mp -> hghmsg); + } + + if ((flags = m_seqflag (mp, "unseen")) + && scansw + && (mp -> msgstats[mp -> hghmsg] & flags)) + scanstring ("unseen"); +} + +/* */ + +static write_ids () { + int i = 0, + flags, + msgnum; + char buffer[80]; + + if (pfd <= 1) + return; + + if (flags = m_seqflag (mp, "unseen")) + for (msgnum = mp -> hghmsg; msgnum >= mp -> lowmsg; msgnum--) + if (!(mp -> msgstats[msgnum] & flags)) { + if (Msgs[msgnum].m_bboard_id == 0) + (void) readid (msgnum); + if ((i = Msgs[msgnum].m_bboard_id) > 0) + break; + } + + (void) sprintf (buffer, "%d %d\n", i, Msgs[mp -> hghmsg].m_bboard_id); + (void) write (pfd, buffer, sizeof buffer); + (void) close (pfd); + pfd = NOTOK; +} + +/* */ + +static quit () { + int i, + md, + msgnum; + char *cp, + tmpfil[BUFSIZ], + map1[BUFSIZ], + map2[BUFSIZ]; + struct stat st; + FILE *dp; + + if (!(mp -> msgflags & MODIFIED) || mp -> msgflags & READONLY || fmsh) { + if (vmh) + (void) rc2peer (RC_FIN, 0, NULLCP); + return; + } + + if (vmh) + (void) ttyNaux (NULLCMD, "FAST"); + cp = NULL; + if ((dp = lkfopen (mp -> foldpath, "r")) == NULL) { + advise (mp -> foldpath, "unable to lock"); + if (vmh) { + (void) ttyR (NULLCMD); + (void) pFIN (); + } + return; + } + if (fstat (fileno (dp), &st) == NOTOK) { + advise (mp -> foldpath, "unable to stat"); + goto release; + } + if (mtime != st.st_mtime) { + advise (NULLCP, "new messages have arrived, no update"); + goto release; + } + mode = (int) (st.st_mode & 0777); + + if (mp -> nummsg == 0) { + cp = concat ("Zero file \"", mp -> foldpath, "\"? ", NULLCP); + if (getanswer (cp)) { + if ((i = creat (mp -> foldpath, mode)) != NOTOK) + (void) close (i); + else + advise (mp -> foldpath, "error zero'ing"); + (void) unlink (map_name (mp -> foldpath));/* XXX */ + } + goto release; + } + + cp = concat ("Update file \"", mp -> foldpath, "\"? ", NULLCP); + if (!getanswer (cp)) + goto release; + (void) strcpy (tmpfil, m_backup (mp -> foldpath)); + if ((md = mbx_open (tmpfil, st.st_uid, st.st_gid, mode)) == NOTOK) { + advise (tmpfil, "unable to open"); + goto release; + } + + for (msgnum = mp -> lowmsg; msgnum <= mp -> hghmsg; msgnum++) + if (mp -> msgstats[msgnum] & EXISTS + && pack (tmpfil, md, msgnum) == NOTOK) { + (void) mbx_close (tmpfil, md); + (void) unlink (tmpfil); + (void) unlink (map_name (tmpfil)); + goto release; + } + (void) mbx_close (tmpfil, md); + + if (rename (tmpfil, mp -> foldpath) == NOTOK) + admonish (mp -> foldpath, "unable to rename %s to", tmpfil); + else { + (void) strcpy (map1, map_name (tmpfil)); + (void) strcpy (map2, map_name (mp -> foldpath)); + + if (rename (map1, map2) == NOTOK) { + admonish (map2, "unable to rename %s to", map1); + (void) unlink (map1); + (void) unlink (map2); + } + } + +release: ; + if (cp) + free (cp); + (void) lkfclose (dp, mp -> foldpath); + if (vmh) { + (void) ttyR (NULLCMD); + (void) pFIN (); + } +} + +/* */ + +#ifdef READLINE +char * +command_comp(text, state) +char *text; +int state; +{ + static int list_index, len; + char *name; + + if (!state) { + list_index = 0; + len = strlen(text); + } + while ((name = mshcmds[list_index].sw) != NULLCP) { + if (mshcmds[list_index++].minchars < 0) + continue; + if (strncmp(name, text, len) == 0) + return getcpy(name); + } + return NULLCP; +} + +#ifdef SYS5DIR +#include <dirent.h> +#else +#include <direct.h> +#endif + +char * +folder_comp(text, state) +char *text; +int state; +{ + static DIR *dp; + static char *dirname = NULLCP; + static char *filename = NULLCP; + static char *foldername = NULLCP; + static char *subdir = NULLCP; + static int len; +#ifdef SYS5DIR + struct dirent *entry = (struct dirent *)0; +#else + struct direct *entry = (struct direct *)0; +#endif + char *p; + + if (!state) { + foldername = getcpy(text); + if ((p = rindex(text, '/')) != NULL) { + foldername[p-text+1] = '\0'; + filename = getcpy(p+1); + dirname = m_mailpath(path(foldername+1, + *text == '+' ? TFOLDER : TSUBCWF)); + } else { + foldername[1] = '\0'; + filename = getcpy(text+1); + dirname = m_mailpath(*text == '+' ? "" : path("./", TSUBCWF)); + } + dp = opendir(dirname); + len = strlen(filename); + subdir = NULLCP; + } + if (subdir && *subdir) { + p = subdir; + subdir = NULLCP; + return p; + } + while (dp && (entry = readdir(dp))) { + if ((len == 0 + && (entry->d_name[0] != '.' || + (entry->d_name[1] && + (entry->d_name[1] != '.' || entry->d_name[2])))) + || + (len != 0 + && strncmp(filename, entry->d_name, len) == 0)) { + struct stat st; + if (lstat(add(entry->d_name, add("/", add(dirname, NULLCP))), &st) + != NOTOK + && (st.st_mode & S_IFDIR)) { + if (st.st_nlink > 2) + subdir = add("/", add(entry->d_name, + add(foldername, NULLCP))); + break; + } + } + } + if (entry) + return add(entry->d_name, add(foldername, NULLCP)); + + if (dp) { + closedir(dp); + dp = (DIR *)0; + } + if (dirname) { + free(dirname); + dirname = NULLCP; + } + if (filename) { + free(filename); + filename = NULLCP; + } + if (foldername) { + free(foldername); + foldername = NULLCP; + } + if (subdir) { + free(subdir); + subdir = NULLCP; + } + return NULLCP; +} + +char ** +msh_completion(text, start, end) +char *text; +int start, end; +{ + char **matches = NULLVP; + + if (start == 0) + matches = completion_matches(text, command_comp); + else if (text[0] == '+' || text[0] == '@') + matches = completion_matches(text, folder_comp); + + return matches; +} + +initialize_readline() +{ + rl_readline_name = invo_name; + rl_basic_word_break_characters = " \t\n\"\\'`$><=;|&{("; + rl_attempted_completion_function = (CPPFunction *)msh_completion; +} +#endif /* READLINE */ + +static int getargs (prompt, sw, cmdp) +char *prompt; +struct swit *sw; +struct Cmd *cmdp; +{ + int i; + char *cp; + static char buffer[BUFSIZ]; + + told_to_quit = 0; + for (;;) { + interrupted = 0; +#if defined(BSD42) || defined(SVR4) + switch (setjmp (sigenv)) { + case OK: + should_intr = 1; + break; + + default: + should_intr = 0; + if (interrupted && !told_to_quit) { + (void) putchar ('\n'); + continue; + } + if (ppid > 0) + (void) kill (ppid, SIGEMT); + return EOF; + } +#endif /* BSD42 || SVR4 */ +#ifdef READLINE + if (!(cp = readline(interactive ? prompt : ""))) { +#if !defined(BSD42) && !defined(SVR4) + if (ppid > 0) + (void) kill (ppid, SIGEMT); + return EOF; +#else /* BSD42 || SVR4 */ + longjmp (sigenv, DONE); +#endif /* BSD42 || SVR4 */ + } + if (!*cp) + continue; + add_history(cp); + strncpy(buffer, cp, sizeof buffer - 2); + buffer[sizeof buffer - 2] = '\0'; + free(cp); +#else /* READLINE */ + if (interactive) { + printf ("%s", prompt); + (void) fflush (stdout); + } + for (cp = buffer; (i = getchar ()) != '\n';) { +#if !defined(BSD42) && !defined(SVR4) + if (interrupted && !told_to_quit) { + buffer[0] = '\0'; + (void) putchar ('\n'); + break; + } + if (told_to_quit || i == EOF) { + if (ppid > 0) + (void) kill (ppid, SIGEMT); + return EOF; + } +#else /* BSD42 || SVR4 */ + if (i == EOF) + longjmp (sigenv, DONE); +#endif /* BSD42 || SVR4 */ + if (cp < &buffer[sizeof buffer - 2]) + *cp++ = i; + } + *cp = 0; + + if (buffer[0] == 0) + continue; +#endif /* READLINE */ + if (buffer[0] == '?') { + printf ("commands:\n"); + printsw (ALL, sw, ""); + printf ("type CTRL-D or use ``quit'' to leave %s\n", + invo_name); + continue; + } + + if (parse (buffer, cmdp) == NOTOK) + continue; + + switch (i = smatch (cmdp -> args[0], sw)) { + case AMBIGSW: + ambigsw (cmdp -> args[0], sw); + continue; + case UNKWNSW: + printf ("say what: ``%s'' -- type ? (or help) for help\n", + cmdp -> args[0]); + continue; + default: +#if defined(BSD42) || defined(SVR4) + should_intr = 0; +#endif /* BSD42 || SVR4 */ + return i; + } + } +} + +/* */ + +static int getcmds (sw, cmdp, scansw) +struct swit *sw; +struct Cmd *cmdp; +int scansw; +{ + int i; + struct record rcs, + *rc = &rcs; + + initrc (rc); + + for (;;) + switch (peer2rc (rc)) { + case RC_QRY: + (void) pQRY (rc -> rc_data, scansw); + break; + + case RC_CMD: + if ((i = pCMD (rc -> rc_data, sw, cmdp)) != NOTOK) + return i; + break; + + case RC_FIN: + if (ppid > 0) + (void) kill (ppid, SIGEMT); + return EOF; + + case RC_XXX: + padios (NULLCP, "%s", rc -> rc_data); + + default: + (void) fmt2peer (RC_ERR, "pLOOP protocol screw-up"); + done (1); + } +} + +/* */ + +static int parse (buffer, cmdp) +char *buffer; +struct Cmd *cmdp; +{ + int argp = 0; + char c, + *cp, + *pp; + + cmdp -> line[0] = 0; + pp = cmdp -> args[argp++] = cmdp -> line; + cmdp -> redirect = NULL; + cmdp -> direction = STDIO; + cmdp -> stream = NULL; + + for (cp = buffer; c = *cp; cp++) + if (!isspace ((unsigned char) c)) + break; + if (c == 0) { + if (vmh) + (void) fmt2peer (RC_EOF, "null command"); + return NOTOK; + } + + while (c = *cp++) { + if (isspace ((unsigned char) c)) { + while (isspace ((unsigned char) c)) + c = *cp++; + if (c == 0) + break; + *pp++ = 0; + cmdp -> args[argp++] = pp; + *pp = 0; + } + + switch (c) { + case '"': + for (;;) { + switch (c = *cp++) { + case 0: + padvise (NULLCP, "unmatched \""); + return NOTOK; + case '"': + break; + case QUOTE: + if ((c = *cp++) == 0) + goto no_quoting; + default: + *pp++ = c; + continue; + } + break; + } + continue; + + case QUOTE: + if ((c = *cp++) == 0) { + no_quoting: ; + padvise (NULLCP, "the newline character can not be quoted"); + return NOTOK; + } + + default: ; + *pp++ = c; + continue; + + case '>': + case '|': + if (pp == cmdp -> line) { + padvise (NULLCP, "invalid null command"); + return NOTOK; + } + if (*cmdp -> args[argp - 1] == 0) + argp--; + cmdp -> direction = c == '>' ? CRTIO : PIPIO; + if (cmdp -> direction == CRTIO && (c = *cp) == '>') { + cmdp -> direction = APPIO; + cp++; + } + cmdp -> redirect = pp + 1;/* sigh */ + for (; c = *cp; cp++) + if (!isspace ((unsigned char) c)) + break; + if (c == 0) { + padvise (NULLCP, cmdp -> direction != PIPIO + ? "missing name for redirect" + : "invalid null command"); + return NOTOK; + } + (void) strcpy (cmdp -> redirect, cp); + if (cmdp -> direction != PIPIO) { + for (; *cp; cp++) + if (isspace ((unsigned char) *cp)) { + padvise (NULLCP, "bad name for redirect"); + return NOTOK; + } + if (expand (cmdp -> redirect) == NOTOK) + return NOTOK; + } + break; + } + break; + } + + *pp++ = 0; + cmdp -> args[argp] = NULL; + + return OK; +} + +/* */ + +int expand (redirect) +char *redirect; +{ + char *cp, + *pp; + char path[BUFSIZ]; + struct passwd *pw; + + if (*redirect != '~') + return OK; + + if (cp = index (pp = redirect + 1, '/')) + *cp++ = 0; + if (*pp == 0) + pp = mypath; + else + if (pw = getpwnam (pp)) + pp = pw -> pw_dir; + else { + padvise (NULLCP, "unknown user: %s", pp); + return NOTOK; + } + + (void) sprintf (path, "%s/%s", pp, cp ? cp : ""); + (void) strcpy (redirect, path); + return OK; +} + +/* */ + +static int init_io (cmdp, vio) +register struct Cmd *cmdp; +int vio; +{ + int io, + result; + + io = vmh; + + vmh = vio; + result = initaux_io (cmdp); + vmh = io; + + return result; +} + + +static int initaux_io (cmdp) +register struct Cmd *cmdp; +{ + char *mode; + + switch (cmdp -> direction) { + case STDIO: + return OK; + + case CRTIO: + case APPIO: + mode = cmdp -> direction == CRTIO ? "write" : "append"; + if ((cmdp -> stream = fopen (cmdp -> redirect, mode)) == NULL) { + padvise (cmdp -> redirect, "unable to %s ", mode); + cmdp -> direction = STDIO; + return NOTOK; + } + break; + + case PIPIO: + if ((cmdp -> stream = popen (cmdp -> redirect, "w")) == NULL) { + padvise (cmdp -> redirect, "unable to pipe"); + cmdp -> direction = STDIO; + return NOTOK; + } + (void) signal (SIGPIPE, pipeser); + broken_pipe = 0; + break; + + default: + padios (NULLCP, "unknown redirection for command"); + } + + (void) fflush (stdout); + if (dup2 (fileno (cmdp -> stream), fileno (stdout)) == NOTOK) + padios ("standard output", "unable to dup2"); + clearerr (stdout); + + return OK; +} + +/* */ + +static fin_io (cmdp, vio) +register struct Cmd *cmdp; +int vio; +{ + int io; + + io = vmh; + + vmh = vio; + finaux_io (cmdp); + vmh = io; +} + + +static int finaux_io (cmdp) +register struct Cmd *cmdp; +{ + switch (cmdp -> direction) { + case STDIO: + return; + + case CRTIO: + case APPIO: + (void) fflush (stdout); + (void) close (fileno (stdout)); + if (ferror (stdout)) + padvise (NULLCP, "problems writing %s", cmdp -> redirect); + (void) fclose (cmdp -> stream); + break; + + case PIPIO: + (void) fflush (stdout); + (void) close (fileno (stdout)); + (void) pclose (cmdp -> stream); + (void) signal (SIGPIPE, SIG_DFL); + break; + + default: + padios (NULLCP, "unknown redirection for command"); + } + + if (dup2 (fileno (sp), fileno (stdout)) == NOTOK) + padios ("standard output", "unable to dup2"); + clearerr (stdout); + + cmdp -> direction = STDIO; +} + +/* */ + +static m_init () { + int msgnum; + + for (msgnum = mp -> lowmsg; msgnum <= mp -> hghmsg; msgnum++) + mp -> msgstats[msgnum] &= ~SELECTED; + mp -> lowsel = mp -> hghsel = mp -> numsel = 0; +} + + +m_reset () { + write_ids (); + m_fmsg (mp); + myname = NULL; +#if defined(BPOP) || defined(NNTP) + if (pmsh) { + (void) pop_done (); + pmsh = 0; + } +#endif /* BPOP || NNTP */ +} + +/* */ + +void m_setcur (mp, msgnum) +register struct msgs *mp; +register int msgnum; +{ + if (mp -> curmsg == msgnum) + return; + + if (mp -> curmsg && Msgs[mp -> curmsg].m_scanl) { + free (Msgs[mp -> curmsg].m_scanl); + Msgs[mp -> curmsg].m_scanl = NULL; + } + if (Msgs[msgnum].m_scanl) { + free (Msgs[msgnum].m_scanl); + Msgs[msgnum].m_scanl = NULL; + } + + mp -> curmsg = msgnum; +} + +/* */ + +/* ARGSUSED */ + +static TYPESIG intrser (i) +int i; +{ +#ifndef BSD42 + (void) signal (SIGINT, intrser); +#endif /* not BSD42 */ + + discard (stdout); + + interrupted++; +#if defined(BSD42) || defined(SVR4) + if (should_intr) + longjmp (sigenv, NOTOK); +#endif /* BSD42 || SVR4 */ +} + + +/* ARGSUSED */ + +static TYPESIG pipeser (i) +int i; +{ +#ifndef BSD42 + (void) signal (SIGPIPE, pipeser); +#endif /* not BSD42 */ + + if (broken_pipe++ == 0) + fprintf (stderr, "broken pipe\n"); + told_to_quit++; + interrupted++; +#if defined(BSD42) || defined(SVR4) + if (should_intr) + longjmp (sigenv, NOTOK); +#endif /* BSD42 || SVR4 */ +} + + +/* ARGSUSED */ + +static TYPESIG quitser (i) +int i; +{ +#ifndef BSD42 + (void) signal (SIGQUIT, quitser); +#endif /* BSD42 */ + + told_to_quit++; + interrupted++; +#if defined(BSD42) || defined(SVR4) + if (should_intr) + longjmp (sigenv, NOTOK); +#endif /* BSD42 || SVR4 */ +} + +/* */ + +static int pINI () { + int i, + vrsn; + char *bp; + struct record rcs, + *rc = &rcs; + + initrc (rc); + + switch (peer2rc (rc)) { + case RC_INI: + bp = rc -> rc_data; + while (isspace (*bp)) + bp++; + if (sscanf (bp, "%d", &vrsn) != 1) { + bad_init: ; + (void) fmt2peer (RC_ERR, "bad init \"%s\"", rc -> rc_data); + done (1); + } + if (vrsn != RC_VRSN) { + (void) fmt2peer (RC_ERR, "version %d unsupported", vrsn); + done (1); + } + + while (*bp && !isspace (*bp)) + bp++; + while (isspace (*bp)) + bp++; + if (sscanf (bp, "%d", &numwins) != 1 || numwins <= 0) + goto bad_init; + if (numwins > NWIN) + numwins = NWIN; + + for (i = 1; i <= numwins; i++) { + while (*bp && !isspace (*bp)) + bp++; + while (isspace (*bp)) + bp++; + if (sscanf (bp, "%d", &windows[i]) != 1 || windows[i] <= 0) + goto bad_init; + } + (void) rc2peer (RC_ACK, 0, NULLCP); + return OK; + + case RC_XXX: + padios (NULLCP, "%s", rc -> rc_data); + + default: + (void) fmt2peer (RC_ERR, "pINI protocol screw-up"); + done (1); /* NOTREACHED */ + } +} + +/* */ + +/* ARGSUSED */ + +static int pQRY (str, scansw) +char *str; +int scansw; +{ + if (pQRY1 (scansw) == NOTOK || pQRY2 () == NOTOK) + return NOTOK; + + (void) rc2peer (RC_EOF, 0, NULLCP); + return OK; +} + +/* */ + +static int pQRY1 (scansw) +int scansw; +{ + int oldhgh; + static int lastlow = 0, + lastcur = 0, + lasthgh = 0, + lastnum = 0; + + oldhgh = mp -> hghmsg; + if (check_folder (scansw) && oldhgh < mp -> hghmsg) { + switch (winX (STATUS)) { + case NOTOK: + return NOTOK; + + case OK: + printf ("new messages have arrived!"); + (void) fflush (stdout); + (void) fflush (stderr); + _exit (0); /* NOTREACHED */ + + default: + lastlow = lastcur = lasthgh = lastnum = 0; + break; + } + + switch (winX (DISPLAY)) { + case NOTOK: + return NOTOK; + + case OK: + scanrange (oldhgh + 1, mp -> hghmsg); + (void) fflush (stdout); + (void) fflush (stderr); + _exit (0); /* NOTREACHED */ + + default: + break; + } + return OK; + } + + if (gap) + switch (winX (STATUS)) { + case NOTOK: + return NOTOK; + + case OK: + printf ("%s: gap in ID:s, last seen %d, lowest present %d\n", + myname ? myname : fmsh ? fmsh : mp -> foldpath, gap - 1, + readid (mp -> lowmsg)); + (void) fflush (stdout); + (void) fflush (stderr); + _exit (0); /* NOTREACHED */ + + default: + gap = 0; + return OK; + } + + if (mp -> lowmsg != lastlow + || mp -> curmsg != lastcur + || mp -> hghmsg != lasthgh + || mp -> nummsg != lastnum) + switch (winX (STATUS)) { + case NOTOK: + return NOTOK; + + case OK: + foldcmd (NULLVP); + (void) fflush (stdout); + (void) fflush (stderr); + _exit (0); /* NOTREACHED */ + + default: + lastlow = mp -> lowmsg; + lastcur = mp -> curmsg; + lasthgh = mp -> hghmsg; + lastnum = mp -> nummsg; + return OK; + } + + return OK; +} + +/* */ + +static int pQRY2 () { + int i, + j, + k, + msgnum, + n; + static int cur = 0, + num = 0, + lo = 0, + hi = 0; + + if (mp -> nummsg == 0 && mp -> nummsg != num) + switch (winX (SCAN)) { + case NOTOK: + return NOTOK; + + case OK: + printf ("empty!"); + (void) fflush (stdout); + (void) fflush (stderr); + _exit (0); /* NOTREACHED */ + + default: + num = mp -> nummsg; + return OK; + } + num = mp -> nummsg; + + i = 0; + j = (k = windows[SCAN]) / 2; + for (msgnum = mp -> curmsg; msgnum <= mp -> hghmsg; msgnum++) + if (mp -> msgstats[msgnum] & EXISTS) + i++; + if (i-- > 0) + if (topcur) + k = i >= k ? 1 : k - i; + else + k -= i > j ? j : i; + + i = j = 0; + n = 1; + for (msgnum = mp -> curmsg; msgnum >= mp -> lowmsg; msgnum--) + if (mp -> msgstats[msgnum] & EXISTS) { + i = msgnum; + if (j == 0) + j = msgnum; + if (n++ >= k) + break; + } + for (msgnum = mp -> curmsg + 1; msgnum <= mp -> hghmsg; msgnum++) + if (mp -> msgstats[msgnum] & EXISTS) { + if (i == 0) + i = msgnum; + j = msgnum; + if (n++ >= windows[SCAN]) + break; + } + if (!topcur + && lo > 0 + && hi > 0 + && mp -> msgstats[lo] & EXISTS + && mp -> msgstats[hi] & EXISTS + && (lo < mp -> curmsg + || (lo == mp -> curmsg && lo == mp -> lowmsg)) + && (mp -> curmsg < hi + || (hi == mp -> curmsg && hi == mp -> hghmsg)) + && hi - lo == j - i) + i = lo, j = hi; + + if (mp -> curmsg != cur || modified) + switch (winN (NULLCMD, SCAN, 0)) { + case NOTOK: + return NOTOK; + + case OK: + return OK; + + default: + scanrange (lo = i, hi = j); + cur = mp -> curmsg; + (void) winR (NULLCMD); + return OK; + } + + return OK; +} + +/* */ + +static int pCMD (str, sw, cmdp) +char *str; +struct swit *sw; +struct Cmd *cmdp; +{ + int i; + + if (*str == '?') + switch (winX (DISPLAY)) { + case NOTOK: + return NOTOK; + + case OK: + printf ("commands:\n"); + printsw (ALL, sw, ""); + printf ("type ``quit'' to leave %s\n", invo_name); + (void) fflush (stdout); + (void) fflush (stderr); + _exit (0); /* NOTREACHED */ + + default: + (void) rc2peer (RC_EOF, 0, NULLCP); + return NOTOK; + } + + if (parse (str, cmdp) == NOTOK) + return NOTOK; + + switch (i = smatch (cmdp -> args[0], sw)) { + case AMBIGSW: + switch (winX (DISPLAY)) { + case NOTOK: + return NOTOK; + + case OK: + ambigsw (cmdp -> args[0], sw); + (void) fflush (stdout); + (void) fflush (stderr); + _exit (0); /* NOTREACHED */ + + default: + (void) rc2peer (RC_EOF, 0, NULLCP); + return NOTOK; + } + + case UNKWNSW: + (void) fmt2peer (RC_ERR, + "say what: ``%s'' -- type ? (or help) for help", + cmdp -> args[0]); + return NOTOK; + + default: + return i; + } +} + +/* */ + +static int pFIN () { + int status; + + switch (setjmp (peerenv)) { + case OK: + (void) signal (SIGALRM, alrmser); + (void) alarm (ALARM); + + status = peerwait (); + + (void) alarm (0); + return status; + + default: + return NOTOK; + } +} + + +static int peerwait () { + struct record rcs, + *rc = &rcs; + + initrc (rc); + + switch (peer2rc (rc)) { + case RC_QRY: + case RC_CMD: + (void) rc2peer (RC_FIN, 0, NULLCP); + return OK; + + case RC_XXX: + advise (NULLCP, "%s", rc -> rc_data); + return NOTOK; + + default: + (void) fmt2peer (RC_FIN, "pLOOP protocol screw-up"); + return NOTOK; + } +} + + +/* ARGSUSED */ + +static TYPESIG alrmser (i) +int i; +{ + longjmp (peerenv, DONE); +} + +/* */ + +static int ttyNaux (cmdp, s) +register struct Cmd *cmdp; +char *s; +{ + struct record rcs, + *rc = &rcs; + + initrc (rc); + + if (cmdp && init_io (cmdp, vmh) == NOTOK) + return NOTOK; + + if (!fmsh) + (void) fseek (fp, 0L, 0);/* XXX: fseek() too tricky for our own good */ + + vmhtty = NOTOK; + switch (rc2rc (RC_TTY, s ? strlen (s) : 0, s, rc)) { + case RC_ACK: + vmhtty = OK; /* fall */ + case RC_ERR: + break; + + case RC_XXX: + padios (NULLCP, "%s", rc -> rc_data);/* NOTREACHED */ + + default: + (void) fmt2peer (RC_ERR, "pTTY protocol screw-up"); + done (1); /* NOTREACHED */ + } + +#ifdef SIGTSTP + (void) signal (SIGTSTP, tstat); +#endif /* SIGTSTP */ + return vmhtty; +} + +/* */ + +static int ttyR (cmdp) +register struct Cmd *cmdp; +{ + struct record rcs, + *rc = &rcs; + +#ifdef SIGTSTP + (void) signal (SIGTSTP, SIG_IGN); +#endif /* SIGTSTP */ + + if (vmhtty != OK) + return NOTOK; + + initrc (rc); + + if (cmdp) + fin_io (cmdp, 0); + + vmhtty = NOTOK; + switch (rc2rc (RC_EOF, 0, NULLCP, rc)) { + case RC_ACK: + (void) rc2peer (RC_EOF, 0, NULLCP); + return OK; + + case RC_XXX: + padios (NULLCP, "%s", rc -> rc_data);/* NOTREACHED */ + + default: + (void) fmt2peer (RC_ERR, "pTTY protocol screw-up"); + done (1); /* NOTREACHED */ + } +} + +/* */ + +static int winN (cmdp, n, eof) +register struct Cmd *cmdp; +int n, + eof; +{ + int i, + pd[2]; + char buffer[BUFSIZ]; + struct record rcs, + *rc = &rcs; + + if (vmhpid == NOTOK) + return OK; + + initrc (rc); + + if (!fmsh) + (void) fseek (fp, 0L, 0);/* XXX: fseek() too tricky for our own good */ + + vmhpid = OK; + + (void) sprintf (buffer, "%d", n); + switch (str2rc (RC_WIN, buffer, rc)) { + case RC_ACK: + break; + + case RC_ERR: + return NOTOK; + + case RC_XXX: + padios (NULLCP, "%s", rc -> rc_data); + + default: + (void) fmt2peer (RC_ERR, "pWIN protocol screw-up"); + done (1); + } + + if (pipe (pd) == NOTOK) { + (void) err2peer (RC_ERR, "pipe", "unable to"); + return NOTOK; + } + + switch (vmhpid = fork ()) { + case NOTOK: + (void) err2peer (RC_ERR, "fork", "unable to"); + (void) close (pd[0]); + (void) close (pd[1]); + return NOTOK; + + case OK: + (void) close (pd[1]); + (void) signal (SIGPIPE, SIG_IGN); + while ((i = read (pd[0], buffer, sizeof buffer)) > 0) + switch (rc2rc (RC_DATA, i, buffer, rc)) { + case RC_ACK: + break; + + case RC_ERR: + _exit (1); + + case RC_XXX: + advise (NULLCP, "%s", rc -> rc_data); + _exit (2); + + default: + (void) fmt2peer (RC_ERR, "pWIN protocol screw-up"); + _exit (2); + } + if (i == OK) + switch (rc2rc (RC_EOF, 0, NULLCP, rc)) { + case RC_ACK: + if (eof) + (void) rc2peer (RC_EOF, 0, NULLCP); + i = 0; + break; + + case RC_XXX: + advise (NULLCP, "%s", rc -> rc_data); + i = 2; + break; + + default: + (void) fmt2peer (RC_ERR, "pWIN protocol screw-up"); + i = 2; + break; + } + if (i == NOTOK) + (void) err2peer (RC_ERR, "pipe", "error reading from"); + (void) close (pd[0]); + _exit (i != NOTOK ? i : 1); + + default: + if ((vmhfd0 = dup (fileno (stdin))) == NOTOK) + padios ("standard input", "unable to dup"); + if ((vmhfd1 = dup (fileno (stdout))) == NOTOK) + padios ("standard output", "unable to dup"); + if ((vmhfd2 = dup (fileno (stderr))) == NOTOK) + padios ("diagnostic output", "unable to dup"); + + (void) close (0); + if ((i = open ("/dev/null", 0)) != NOTOK && i != fileno (stdin)) { + (void) dup2 (i, fileno (stdin)); + (void) close (i); + } + + (void) fflush (stdout); + if (dup2 (pd[1], fileno (stdout)) == NOTOK) + padios ("standard output", "unable to dup2"); + clearerr (stdout); + + (void) fflush (stderr); + if (dup2 (pd[1], fileno (stderr)) == NOTOK) + padios ("diagnostic output", "unable to dup2"); + clearerr (stderr); + + if (cmdp && init_io (cmdp, 0) == NOTOK) + return NOTOK; + pstat = signal (SIGPIPE, pipeser); + broken_pipe = 1; + + (void) close (pd[0]); + (void) close (pd[1]); + + return vmhpid; + } +} + +/* */ + +static int winR (cmdp) +register struct Cmd *cmdp; +{ + int status; + + if (vmhpid <= OK) + return NOTOK; + + if (cmdp) + fin_io (cmdp, 0); + + if (dup2 (vmhfd0, fileno (stdin)) == NOTOK) + padios ("standard input", "unable to dup2"); + clearerr (stdin); + (void) close (vmhfd0); + + (void) fflush (stdout); + if (dup2 (vmhfd1, fileno (stdout)) == NOTOK) + padios ("standard output", "unable to dup2"); + clearerr (stdout); + (void) close (vmhfd1); + + (void) fflush (stderr); + if (dup2 (vmhfd2, fileno (stderr)) == NOTOK) + padios ("diagnostic output", "unable to dup2"); + clearerr (stderr); + (void) close (vmhfd2); + + (void) signal (SIGPIPE, pstat); + + if ((status = pidwait (vmhpid, OK)) == 2) + done (1); + + vmhpid = OK; + return (status == 0 ? OK : NOTOK); +} + +/* */ + +static int winX (n) +int n; +{ + int i, + pid, + pd[2]; + char buffer[BUFSIZ]; + struct record rcs, + *rc = &rcs; + + initrc (rc); + + if (!fmsh) + (void) fseek (fp, 0L, 0);/* XXX: fseek() too tricky for our own good */ + + (void) sprintf (buffer, "%d", n); + switch (str2rc (RC_WIN, buffer, rc)) { + case RC_ACK: + break; + + case RC_ERR: + return NOTOK; + + case RC_XXX: + padios (NULLCP, "%s", rc -> rc_data); + + default: + (void) fmt2peer (RC_ERR, "pWIN protocol screw-up"); + done (1); + } + + if (pipe (pd) == NOTOK) { + (void) err2peer (RC_ERR, "pipe", "unable to"); + return NOTOK; + } + + switch (pid = fork ()) { + case NOTOK: + (void) err2peer (RC_ERR, "fork", "unable to"); + (void) close (pd[0]); + (void) close (pd[1]); + return NOTOK; + + case OK: + (void) close (fileno (stdin)); + if ((i = open ("/dev/null", 0)) != NOTOK && i != fileno (stdin)) { + (void) dup2 (i, fileno (stdin)); + (void) close (i); + } + (void) dup2 (pd[1], fileno (stdout)); + (void) dup2 (pd[1], fileno (stderr)); + (void) close (pd[0]); + (void) close (pd[1]); + vmhpid = NOTOK; + return OK; + + default: + (void) close (pd[1]); + while ((i = read (pd[0], buffer, sizeof buffer)) > 0) + switch (rc2rc (RC_DATA, i, buffer, rc)) { + case RC_ACK: + break; + + case RC_ERR: + (void) close (pd[0]); + (void) pidwait (pid, OK); + return NOTOK; + + case RC_XXX: + padios (NULLCP, "%s", rc -> rc_data); + + default: + (void) fmt2peer (RC_ERR, "pWIN protocol screw-up"); + done (1); + } + if (i == OK) + switch (rc2rc (RC_EOF, 0, NULLCP, rc)) { + case RC_ACK: + break; + + case RC_XXX: + padios (NULLCP, "%s", rc -> rc_data); + + default: + (void) fmt2peer (RC_ERR, "pWIN protocol screw-up"); + done (1); + } + if (i == NOTOK) + (void) err2peer (RC_ERR, "pipe", "error reading from"); + + (void) close (pd[0]); + (void) pidwait (pid, OK); + return (i != NOTOK ? pid : NOTOK); + } +} + +/* */ + +/* VARARGS2 */ + +void padios (what, fmt, a, b, c, d, e, f) +char *what, + *fmt, + *a, + *b, + *c, + *d, + *e, + *f; +{ + if (vmh) { + (void) err2peer (RC_FIN, what, fmt, a, b, c, d, e, f); + (void) rcdone (); + } + else + advise (what, fmt, a, b, c, d, e, f); + + done (1); +} + + +/* VARARGS2 */ + +void padvise (what, fmt, a, b, c, d, e, f) +char *what, + *fmt, + *a, + *b, + *c, + *d, + *e, + *f; +{ + if (vmh) + (void) err2peer (RC_ERR, what, fmt, a, b, c, d, e, f); + else + advise (what, fmt, a, b, c, d, e, f); +}