root/usr/src/cmd/mailx/lex.c
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 * Copyright (c) 2016 by Delphix. All rights reserved.
 */

/*      Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
/*      All Rights Reserved   */

/*
 * University Copyright- Copyright (c) 1982, 1986, 1988
 * The Regents of the University of California
 * All Rights Reserved
 *
 * University Acknowledgment- Portions of this document are derived from
 * software developed by the University of California, Berkeley, and its
 * contributors.
 */

#include "rcv.h"
#include <locale.h>

/*
 * mailx -- a modified version of a University of California at Berkeley
 *      mail program
 *
 * Lexical processing of commands.
 */

#ifdef SIGCONT
static void             contin(int);
#endif
static int              isprefix(char *as1, char *as2);
static const struct cmd *lex(char word[]);
static int              Passeren(void);
static void             setmsize(int sz);

/*
 * Set up editing on the given file name.
 * If isedit is true, we are considered to be editing the file,
 * otherwise we are reading our mail which has signficance for
 * mbox and so forth.
 */

int
setfile(char *name, int isedit)
{
        FILE *ibuf;
        int i;
        static int shudclob;
        static char efile[PATHSIZE];
        char fortest[128];
        struct stat stbuf;
        int exrc = 1;
        int rc = -1;
        int fd = -1;

        if (!isedit && issysmbox)
                lockmail();
        if (stat(name, &stbuf) < 0 && errno == EOVERFLOW) {
                fprintf(stderr, gettext("mailbox %s is too large to"
                    " accept incoming mail\n"), name);
                goto doret;
        }
        if ((ibuf = fopen(name, "r")) == NULL) {
                extern int errno;
                int sverrno = errno;
                int filethere = (access(name, 0) == 0);
                errno = sverrno;
                if (exitflg)
                        goto doexit;    /* no mail, return error */
                if (isedit || filethere)
                        perror(name);
                else if (!Hflag) {
                        char *f = strrchr(name, '/');
                        if (f == NOSTR)
                                fprintf(stderr, gettext("No mail.\n"));
                        else
                                fprintf(stderr, gettext("No mail for %s\n"),
                                    f+1);
                }
                goto doret;
        }
        fstat(fileno(ibuf), &stbuf);
        if (stbuf.st_size == 0L || (stbuf.st_mode&S_IFMT) != S_IFREG) {
                if (exitflg)
                        goto doexit;    /* no mail, return error */
                if (isedit)
                        if (stbuf.st_size == 0L)
                                fprintf(stderr, gettext("%s: empty file\n"),
                                    name);
                        else
                                fprintf(stderr,
                                    gettext("%s: not a regular file\n"), name);
                else if (!Hflag) {
                        if (strrchr(name, '/') == NOSTR)
                                fprintf(stderr, gettext("No mail.\n"));
                        else
                                fprintf(stderr, gettext("No mail for %s\n"),
                                    strrchr(name, '/') + 1);
                }
                fclose(ibuf);
                goto doret;
        }

        if (fgets(fortest, sizeof (fortest), ibuf) == NULL) {
                perror(gettext("mailx: Unable to read from mail file"));
                goto doexit;
        }

        fseek(ibuf, (long)(BUFSIZ+1), 0);       /* flush input buffer */
        fseek(ibuf, 0L, 0);
        if (strncmp(fortest, "Forward to ", 11) == 0) {
                if (exitflg)
                        goto doexit;    /* no mail, return error */
                fprintf(stderr, gettext("Your mail is being forwarded to %s"),
                    fortest+11);
                fclose(ibuf);
                goto doret;
        }
        if (exitflg) {
                exrc = 0;
                goto doexit;    /* there is mail, return success */
        }

        /*
         * Looks like all will be well. Must hold signals
         * while we are reading the new file, else we will ruin
         * the message[] data structure.
         * Copy the messages into /tmp and set pointers.
         */

        holdsigs();
        if (shudclob) {
                /*
                 * Now that we know we can switch to the new file
                 * it's safe to close out the current file.
                 *
                 * If we're switching to the file we are currently
                 * editing, don't allow it to be removed as a side
                 * effect of closing it out.
                 */
                if (edit)
                        edstop(strcmp(editfile, name) == 0);
                else {
                        quit(strcmp(editfile, name) == 0);
                        if (issysmbox)
                                Verhogen();
                }
                fflush(stdout);
                fclose(itf);
                fclose(otf);
                free(message);
                space = 0;
        }
        readonly = 0;
        if (!isedit && issysmbox && !Hflag)
                readonly = Passeren() == -1;
        lock(ibuf, "r", 1);
        fstat(fileno(ibuf), &stbuf);
        utimep->actime = stbuf.st_atime;
        utimep->modtime = stbuf.st_mtime;

        if (!readonly)
                if ((i = open(name, O_WRONLY)) < 0)
                        readonly++;
                else
                        close(i);
        shudclob = 1;
        edit = isedit;
        nstrcpy(efile, PATHSIZE, name);
        editfile = efile;
#ifdef notdef
        if (name != mailname)
                nstrcpy(mailname, PATHSIZE, name);
#endif
        mailsize = fsize(ibuf);
        if ((fd = open(tempMesg, O_RDWR|O_CREAT|O_EXCL, 0600)) < 0 ||
            (otf = fdopen(fd, "w")) == NULL) {
                perror(tempMesg);
                if (!edit && issysmbox)
                        Verhogen();
                goto doexit;
        }
        if ((itf = fopen(tempMesg, "r")) == NULL) {
                perror(tempMesg);
                if (!edit && issysmbox)
                        Verhogen();
                goto doexit;
        }
        removefile(tempMesg);
        setptr(ibuf);
        setmsize(msgCount);
        fclose(ibuf);
        relsesigs();
        sawcom = 0;
        rc = 0;

doret:
        if (!isedit && issysmbox)
                unlockmail();
        return (rc);

doexit:
        if (!isedit && issysmbox)
                unlockmail();
        exit(exrc ? exrc : rpterr);
        /* NOTREACHED */
}

/* global to semaphores */
static char semfn[128];
static FILE *semfp = NULL;

/*
 *  return -1 if file is already being read, 0 otherwise
 */
static int
Passeren(void)
{
        char *home;

        if ((home = getenv("HOME")) == NULL)
                return (0);
        snprintf(semfn, sizeof (semfn), "%s%s", home, "/.Maillock");
        if ((semfp = fopen(semfn, "w")) == NULL) {
                fprintf(stderr,
            gettext("WARNING: Can't open mail lock file (%s).\n"), semfn);
                fprintf(stderr,
            gettext("\t Assuming you are not already reading mail.\n"));
                return (0);
        }
        if (lock(semfp, "w", 0) < 0) {
                if (errno == ENOLCK) {
                        fprintf(stderr,
gettext("WARNING: Unable to acquire mail lock, no record locks available.\n"));
                        fprintf(stderr,
                    gettext("\t Assuming you are not already reading mail.\n"));
                        return (0);
                }
                fprintf(stderr,
                    gettext("WARNING: You are already reading mail.\n"));
                fprintf(stderr,
                    gettext("\t This instance of mail is read only.\n"));
                fclose(semfp);
                semfp = NULL;
                return (-1);
        }
        return (0);
}

void
Verhogen(void)
{
        if (semfp != NULL) {
                unlink(semfn);
                fclose(semfp);
        }
}

/*
 * Interpret user commands one by one.  If standard input is not a tty,
 * print no prompt.
 */

static int      *msgvec;
static int      shudprompt;

void
commands(void)
{
        int eofloop;
        int n;
        char linebuf[LINESIZE];
        char line[LINESIZE];
        struct stat minfo;
        FILE *ibuf;

#ifdef SIGCONT
        sigset(SIGCONT, SIG_DFL);
#endif
        if (rcvmode && !sourcing) {
                if (sigset(SIGINT, SIG_IGN) != SIG_IGN)
                        sigset(SIGINT, stop);
                if (sigset(SIGHUP, SIG_IGN) != SIG_IGN)
                        sigset(SIGHUP, hangup);
        }
        for (;;) {
                setjmp(srbuf);

                /*
                 * Print the prompt, if needed.  Clear out
                 * string space, and flush the output.
                 */

                if (!rcvmode && !sourcing)
                        return;
                eofloop = 0;
top:
                if ((shudprompt = (intty && !sourcing)) != 0) {
                        if (prompt == NOSTR) {
                                if ((int)value("bsdcompat"))
                                        prompt = "& ";
                                else
                                        prompt = "";
                        }
#ifdef SIGCONT
                        sigset(SIGCONT, contin);
#endif
                        if (intty && value("autoinc") &&
                            stat(editfile, &minfo) >= 0 &&
                            minfo.st_size > mailsize) {
                                int OmsgCount, i;

                                OmsgCount = msgCount;
                                fseek(otf, 0L, 2);
                                holdsigs();
                                if (!edit && issysmbox)
                                        lockmail();
                                if ((ibuf = fopen(editfile, "r")) == NULL) {
                                        fprintf(stderr,
                                            gettext("Can't reopen %s\n"),
                                            editfile);
                                        if (!edit && issysmbox)
                                                unlockmail();
                                        exit(1);
                                        /* NOTREACHED */
                                }
                                if (edit || !issysmbox)
                                        lock(ibuf, "r", 1);
                                fseek(ibuf, mailsize, 0);
                                mailsize = fsize(ibuf);
                                setptr(ibuf);
                                setmsize(msgCount);
                                fclose(ibuf);
                                if (!edit && issysmbox)
                                        unlockmail();
                                if (msgCount-OmsgCount > 0) {
                                        printf(gettext(
                                            "New mail has arrived.\n"));
                                        if (msgCount - OmsgCount == 1)
                                                printf(gettext(
                                                    "Loaded 1 new message\n"));
                                        else
                                                printf(gettext(
                                                    "Loaded %d new messages\n"),
                                                    msgCount-OmsgCount);
                                        if (value("header") != NOSTR)
                                                for (i = OmsgCount+1;
                                                    i <= msgCount; i++) {
                                                        printhead(i);
                                                        sreset();
                                                }
                                }
                                relsesigs();
                        }
                        printf("%s", prompt);
                }
                flush();
                sreset();

                /*
                 * Read a line of commands from the current input
                 * and handle end of file specially.
                 */

                n = 0;
                linebuf[0] = '\0';
                for (;;) {
                        if (readline(input, line) <= 0) {
                                if (n != 0)
                                        break;
                                if (loading)
                                        return;
                                if (sourcing) {
                                        unstack();
                                        goto more;
                                }
                                if (value("ignoreeof") != NOSTR && shudprompt) {
                                        if (++eofloop < 25) {
                                                printf(gettext(
                                                    "Use \"quit\" to quit.\n"));
                                                goto top;
                                        }
                                }
                                return;
                        }
                        if ((n = strlen(line)) == 0)
                                break;
                        n--;
                        if (line[n] != '\\')
                                break;
                        line[n++] = ' ';
                        if (n > LINESIZE - (int)strlen(linebuf) - 1)
                                break;
                        strcat(linebuf, line);
                }
                n = LINESIZE - strlen(linebuf) - 1;
                if ((int)strlen(line) > n) {
                        printf(gettext(
                "Line plus continuation line too long:\n\t%s\n\nplus\n\t%s\n"),
                            linebuf, line);
                        if (loading)
                                return;
                        if (sourcing) {
                                unstack();
                                goto more;
                        }
                        return;
                }
                strncat(linebuf, line, n);
#ifdef SIGCONT
                sigset(SIGCONT, SIG_DFL);
#endif
                if (execute(linebuf, 0))
                        return;
more:;
        }
}

/*
 * Execute a single command.  If the command executed
 * is "quit," then return non-zero so that the caller
 * will know to return back to main, if it cares.
 * Contxt is non-zero if called while composing mail.
 */

int
execute(char linebuf[], int contxt)
{
        char word[LINESIZE];
        char *arglist[MAXARGC];
        const struct cmd *com;
        char *cp, *cp2;
        int c, e;
        int muvec[2];

        /*
         * Strip the white space away from the beginning
         * of the command, then scan out a word, which
         * consists of anything except digits and white space.
         *
         * Handle |, ! and # differently to get the correct
         * lexical conventions.
         */

        cp = linebuf;
        while (any(*cp, " \t"))
                cp++;
        cp2 = word;
        if (any(*cp, "!|#"))
                *cp2++ = *cp++;
        else
                while (*cp && !any(*cp, " \t0123456789$^.:/-+*'\""))
                        *cp2++ = *cp++;
        *cp2 = '\0';

        /*
         * Look up the command; if not found, complain.
         * Normally, a blank command would map to the
         * first command in the table; while sourcing,
         * however, we ignore blank lines to eliminate
         * confusion.
         */

        if (sourcing && equal(word, ""))
                return (0);
        com = lex(word);
        if (com == NONE) {
                fprintf(stderr, gettext("Unknown command: \"%s\"\n"), word);
                if (loading) {
                        cond = CANY;
                        return (1);
                }
                if (sourcing) {
                        cond = CANY;
                        unstack();
                }
                return (0);
        }

        /*
         * See if we should execute the command -- if a conditional
         * we always execute it, otherwise, check the state of cond.
         */

        if ((com->c_argtype & F) == 0)
                if (cond == CRCV && !rcvmode || cond == CSEND && rcvmode ||
                    cond == CTTY && !intty || cond == CNOTTY && intty)
                        return (0);

        /*
         * Special case so that quit causes a return to
         * main, who will call the quit code directly.
         * If we are in a source file, just unstack.
         */

        if ((uintptr_t)com->c_func == (uintptr_t)edstop) {
                if (sourcing) {
                        if (loading)
                                return (1);
                        unstack();
                        return (0);
                }
                return (1);
        }

        /*
         * Process the arguments to the command, depending
         * on the type it expects.  Default to an error.
         * If we are sourcing an interactive command, it's
         * an error.
         */

        if (!rcvmode && (com->c_argtype & M) == 0) {
                fprintf(stderr,
                    gettext("May not execute \"%s\" while sending\n"),
                    com->c_name);
                if (loading)
                        return (1);
                if (sourcing)
                        unstack();
                return (0);
        }
        if (sourcing && com->c_argtype & I) {
                fprintf(stderr,
                    gettext("May not execute \"%s\" while sourcing\n"),
                    com->c_name);
                rpterr = 1;
                if (loading)
                        return (1);
                unstack();
                return (0);
        }
        if (readonly && com->c_argtype & W) {
                fprintf(stderr, gettext(
                    "May not execute \"%s\" -- message file is read only\n"),
                    com->c_name);
                if (loading)
                        return (1);
                if (sourcing)
                        unstack();
                return (0);
        }
        if (contxt && com->c_argtype & R) {
                fprintf(stderr, gettext("Cannot recursively invoke \"%s\"\n"),
                    com->c_name);
                return (0);
        }
        e = 1;
        switch (com->c_argtype & ~(F|P|I|M|T|W|R)) {
        case MSGLIST:
                /*
                 * A message list defaulting to nearest forward
                 * legal message.
                 */
                if (msgvec == 0) {
                        fprintf(stderr,
                            gettext("Illegal use of \"message list\"\n"));
                        return (-1);
                }
                if ((c = getmsglist(cp, msgvec, com->c_msgflag)) < 0)
                        break;
                if (c  == 0)
                        if (msgCount == 0)
                                *msgvec = 0;
                        else {
                                *msgvec = first(com->c_msgflag,
                                    com->c_msgmask);
                                msgvec[1] = 0;
                        }
                if (*msgvec == 0) {
                        fprintf(stderr, gettext("No applicable messages\n"));
                        break;
                }
                e = (*com->c_func)(msgvec);
                break;

        case NDMLIST:
                /*
                 * A message operand with no defaults, but no error
                 * if none exists. There will be an error if the
                 * msgvec pointer is of zero value.
                 */
                if (msgvec == 0) {
                        fprintf(stderr,
                            gettext("Illegal use of \"message operand\"\n"));
                        return (-1);
                }
                if (getmessage(cp, msgvec, com->c_msgflag) < 0)
                        break;
                e = (*com->c_func)(msgvec);
                break;

        case STRLIST:
                /*
                 * Just the straight string, with
                 * leading blanks removed.
                 */
                while (any(*cp, " \t"))
                        cp++;
                e = (*com->c_func)(cp);
                break;

        case RAWLIST:
                /*
                 * A vector of strings, in shell style.
                 */
                if ((c = getrawlist(cp, arglist,
                    sizeof (arglist) / sizeof (*arglist))) < 0)
                        break;
                if (c < com->c_minargs) {
                        fprintf(stderr,
                            gettext("%s requires at least %d arg(s)\n"),
                            com->c_name, com->c_minargs);
                        break;
                }
                if (c > com->c_maxargs) {
                        fprintf(stderr,
                            gettext("%s takes no more than %d arg(s)\n"),
                            com->c_name, com->c_maxargs);
                        break;
                }
                e = (*com->c_func)(arglist);
                break;

        case NOLIST:
                /*
                 * Just the constant zero, for exiting,
                 * eg.
                 */
                e = (*com->c_func)(0);
                break;

        default:
                panic("Unknown argtype");
        }

        /*
         * Exit the current source file on
         * error.
         */

        if (e && loading)
                return (1);
        if (e && sourcing)
                unstack();
        if ((uintptr_t)com->c_func == (uintptr_t)edstop)
                return (1);
        if (value("autoprint") != NOSTR && com->c_argtype & P)
                if ((dot->m_flag & MDELETED) == 0) {
                        muvec[0] = dot - &message[0] + 1;
                        muvec[1] = 0;
                        type(muvec);
                }
        if (!sourcing && (com->c_argtype & T) == 0)
                sawcom = 1;
        return (0);
}

#ifdef SIGCONT
/*
 * When we wake up after ^Z, reprint the prompt.
 */
static void
contin(int s __unused)
{
        if (shudprompt)
                printf("%s", prompt);
        fflush(stdout);
}
#endif

/*
 * Branch here on hangup signal and simulate quit.
 */
void
hangup(int s __unused)
{

        holdsigs();
#ifdef OLD_BSD_SIGS
        sigignore(SIGHUP);
#endif
        if (edit) {
                if (setjmp(srbuf))
                        exit(rpterr);
                edstop(0);
        } else {
                if (issysmbox)
                        Verhogen();
                if (value("exit") != NOSTR)
                        exit(1);
                else
                        quit(0);
        }
        exit(rpterr);
}

/*
 * Set the size of the message vector used to construct argument
 * lists to message list functions.
 */

static void
setmsize(int sz)
{

        if (msgvec != (int *)0)
                free(msgvec);
        if (sz < 1)
                sz = 1; /* need at least one cell for terminating 0 */
        if ((msgvec = (int *)
            calloc((unsigned)(sz + 1), sizeof (*msgvec))) == NULL)
                panic("Failed to allocate memory for message vector");
}

/*
 * Find the correct command in the command table corresponding
 * to the passed command "word"
 */

static const struct cmd *
lex(char word[])
{
        const struct cmd *cp;

        for (cp = &cmdtab[0]; cp->c_name != NOSTR; cp++)
                if (isprefix(word, cp->c_name))
                        return (cp);
        return (NONE);
}

/*
 * Determine if as1 is a valid prefix of as2.
 */
static int
isprefix(char *as1, char *as2)
{
        char *s1, *s2;

        s1 = as1;
        s2 = as2;
        while (*s1++ == *s2)
                if (*s2++ == '\0')
                        return (1);
        return (*--s1 == '\0');
}

/*
 * The following gets called on receipt of a rubout.  This is
 * to abort printout of a command, mainly.
 * Dispatching here when command() is inactive crashes rcv.
 * Close all open files except 0, 1, 2, and the temporary.
 * The special call to getuserid() is needed so it won't get
 * annoyed about losing its open file.
 * Also, unstack all source files.
 */

static int      inithdr;                /* am printing startup headers */

void
stop(int s)
{
        NODE *head;

        noreset = 0;
        if (!inithdr)
                sawcom++;
        inithdr = 0;
        while (sourcing)
                unstack();
        (void) getuserid((char *)0);
        for (head = fplist; head != (NODE *)NULL; head = head->next) {
                if (head->fp == stdin || head->fp == stdout)
                        continue;
                if (head->fp == itf || head->fp == otf)
                        continue;
                if (head->fp == stderr)
                        continue;
                if (head->fp == semfp)
                        continue;
                if (head->fp == pipef) {
                        npclose(pipef);
                        pipef = NULL;
                        continue;
                }
                fclose(head->fp);
        }
        if (image >= 0) {
                close(image);
                image = -1;
        }
        if (s) {
                fprintf(stderr, gettext("Interrupt\n"));
                fflush(stderr);
#ifdef OLD_BSD_SIGS
                sigrelse(s);
#endif
        }
        longjmp(srbuf, 1);
}

/*
 * Announce the presence of the current mailx version,
 * give the message count, and print a header listing.
 */

#define GREETING        "%s  Type ? for help.\n"

void
announce(void)
{
        int vec[2], mdot;
        extern const char *const version;

        if (!Hflag && value("quiet") == NOSTR)
                printf(gettext(GREETING), version);
        mdot = newfileinfo(1);
        vec[0] = mdot;
        vec[1] = 0;
        dot = &message[mdot - 1];
        if (msgCount > 0 && !noheader) {
                inithdr++;
                headers(vec);
                inithdr = 0;
        }
}

/*
 * Announce information about the file we are editing.
 * Return a likely place to set dot.
 */
int
newfileinfo(int start)
{
        struct message *mp;
        int u, n, mdot, d, s;
        char fname[BUFSIZ], zname[BUFSIZ], *ename;

        if (Hflag)
                return (1);             /* fake it--return message 1 */
        for (mp = &message[start - 1]; mp < &message[msgCount]; mp++)
                if ((mp->m_flag & (MNEW|MREAD)) == MNEW)
                        break;
        if (mp >= &message[msgCount])
                for (mp = &message[start - 1]; mp < &message[msgCount]; mp++)
                        if ((mp->m_flag & MREAD) == 0)
                                break;
        if (mp < &message[msgCount])
                mdot = mp - &message[0] + 1;
        else
                mdot = 1;
        n = u = d = s = 0;
        for (mp = &message[start - 1]; mp < &message[msgCount]; mp++) {
                if (mp->m_flag & MNEW)
                        n++;
                if ((mp->m_flag & MREAD) == 0)
                        u++;
                if (mp->m_flag & MDELETED)
                        d++;
                if (mp->m_flag & MSAVED)
                        s++;
        }
        ename = origname;
        if (getfold(fname) >= 0) {
                nstrcat(fname, sizeof (fname), "/");
                if (strncmp(fname, editfile, strlen(fname)) == 0) {
                        snprintf(zname, sizeof (zname),
                            "+%s", editfile + strlen(fname));
                        ename = zname;
                }
        }
        printf("\"%s\": ", ename);
        if (msgCount == 1)
                printf(gettext("1 message"));
        else
                printf(gettext("%d messages"), msgCount);
        if (n > 0)
                printf(gettext(" %d new"), n);
        if (u-n > 0)
                printf(gettext(" %d unread"), u);
        if (d > 0)
                printf(gettext(" %d deleted"), d);
        if (s > 0)
                printf(gettext(" %d saved"), s);
        if (readonly)
                printf(gettext(" [Read only]"));
        printf("\n");
        return (mdot);
}

/*
 * Print the current version number.
 */

int
pversion(char *s __unused)
{
        printf("%s\n", version);
        return (0);
}

/*
 * Load a file of user definitions.
 */
void
load(char *name)
{
        FILE *in, *oldin;

        if ((in = fopen(name, "r")) == NULL)
                return;
        oldin = input;
        input = in;
        loading = 1;
        sourcing = 1;
        commands();
        loading = 0;
        sourcing = 0;
        input = oldin;
        fclose(in);
}

/*
 * Incorporate any new mail into the current session.
 *
 * XXX - Since autoinc works on "edited" files as well as the
 * system mailbox, this probably ought to as well.
 */

int
inc(void)
{
        FILE *ibuf;
        int mdot;
        struct stat stbuf;
        int firstnewmsg = msgCount + 1;

        if (edit) {
                fprintf(stderr, gettext("Not in system mailbox\n"));
                return (-1);
        }
        if (((ibuf = fopen(mailname, "r")) == NULL) ||
            (fstat(fileno(ibuf), &stbuf) < 0) || stbuf.st_size == 0L ||
            stbuf.st_size == mailsize || (stbuf.st_mode&S_IFMT) != S_IFREG) {
                if (strrchr(mailname, '/') == NOSTR)
                        fprintf(stderr, gettext("No new mail.\n"));
                else
                        fprintf(stderr, gettext("No new mail for %s\n"),
                            strrchr(mailname, '/')+1);
                if (ibuf != NULL)
                        fclose(ibuf);
                return (-1);
        }

        fseek(otf, 0L, 2);
        holdsigs();
        if (issysmbox)
                lockmail();
        fseek(ibuf, mailsize, 0);
        mailsize = fsize(ibuf);
        setptr(ibuf);
        setmsize(msgCount);
        fclose(ibuf);
        if (issysmbox)
                unlockmail();
        relsesigs();
        mdot = newfileinfo(firstnewmsg);
        dot = &message[mdot - 1];
        sawcom = 0;
        return (0);
}