Subversion Repositories planix.SVN

Rev

Blame | Last modification | View Log | RSS feed

/*
 * This code contains changes by
 *      Gunnar Ritter, Freiburg i. Br., Germany, 2002. All rights reserved.
 *
 * Conditions 1, 2, and 4 and the no-warranty notice below apply
 * to these changes.
 *
 *
 * Copyright (c) 1980, 1993
 *      The Regents of the University of California.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed by the University of
 *      California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *
 * Copyright(C) Caldera International Inc. 2001-2002. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *   Redistributions of source code and documentation must retain the
 *    above copyright notice, this list of conditions and the following
 *    disclaimer.
 *   Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *   All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed or owned by Caldera
 *      International, Inc.
 *   Neither the name of Caldera International, Inc. nor the names of
 *    other contributors may be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA
 * INTERNATIONAL, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL CALDERA INTERNATIONAL, INC. BE
 * LIABLE FOR ANY DIRECT, INDIRECT INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef lint
#ifdef  DOSCCS
static char sccsid[] = "@(#)ex_io.c     1.40 (gritter) 2/17/05";
#endif
#endif

/* from ex_io.c 7.11.1.1 (Berkeley) 8/12/86 */

#include "ex.h"
#include "ex_argv.h"
#include "ex_temp.h"
#include "ex_tty.h"
#include "ex_vis.h"

/*
 * File input/output, source, preserve and recover
 */

/*
 * Following remember where . was in the previous file for return
 * on file switching.
 */
int     altdot;
int     oldadot;
bool    wasalt;
short   isalt;

long    cntch;                  /* Count of characters on unit io */
#ifndef VMUNIX
short   cntln;                  /* Count of lines " */
#else
int     cntln;
#endif
long    cntnull;                /* Count of nulls " */
#ifndef BIT8
long    cntodd;                 /* Count of non-ascii characters " */
#endif

/*
 * Parse file name for command encoded by comm.
 * If comm is E then command is doomed and we are
 * parsing just so user won't have to retype the name.
 */
void 
filename(int comm)
{
        register int c = comm, d;
        register int i;

        d = getchar();
        if (endcmd(d)) {
                if (savedfile[0] == 0 && comm != 'f')
                        error(catgets(catd, 1, 72,
                                        "No file|No current filename"));
                CP(file, savedfile);
                wasalt = (isalt > 0) ? isalt-1 : 0;
                isalt = 0;
                oldadot = altdot;
                if (c == 'e' || c == 'E')
                        altdot = lineDOT();
                if (d == EOF)
                        ungetchar(d);
        } else {
                ungetchar(d);
                getone();
                eol();
                if (savedfile[0] == 0 && c != 'E' && c != 'e') {
                        c = 'e';
                        edited = 0;
                }
                wasalt = strcmp(file, altfile) == 0;
                oldadot = altdot;
                switch (c) {

                case 'f':
                        edited = 0;
                        /* fall into ... */

                case 'e':
                        if (savedfile[0]) {
                                altdot = lineDOT();
                                CP(altfile, savedfile);
                        }
                        CP(savedfile, file);
                        break;

                default:
                        if (file[0]) {
                                if (c != 'E')
                                        altdot = lineDOT();
                                CP(altfile, file);
                        }
                        break;
                }
        }
        if (hush && comm != 'f' || comm == 'E')
                return;
        if (file[0] != 0) {
                lprintf("\"%s\"", file);
                if (comm == 'f') {
                        if (value(READONLY))
                                printf(catgets(catd, 1, 73, " [Read only]"));
                        if (!edited)
                                printf(catgets(catd, 1, 74, " [Not edited]"));
                        if (tchng)
                                printf(catgets(catd, 1, 75, " [Modified]"));
                }
                flush();
        } else
                printf(catgets(catd, 1, 76, "No file "));
        if (comm == 'f') {
                if (!(i = lineDOL()))
                        i++;
                printf(catgets(catd, 1, 77,
                        " line %d of %d --%ld%%--"), lineDOT(), lineDOL(),
                    (long) 100 * lineDOT() / i);
        }
}

/*
 * Get the argument words for a command into genbuf
 * expanding # and %.
 */
int 
getargs(void)
{
        register int c;
        register char *cp, *fp;
        static char fpatbuf[32];        /* hence limit on :next +/pat */

        pastwh();
        if (peekchar() == '+') {
                for (cp = fpatbuf;;) {
                        c = *cp++ = getchar();
                        if (cp >= &fpatbuf[sizeof(fpatbuf)])
                                error(catgets(catd, 1, 78, "Pattern too long"));
                        if (c == '\\' && isspace(peekchar()))
                                c = getchar();
                        if (c == EOF || isspace(c)) {
                                ungetchar(c);
                                *--cp = 0;
                                firstpat = &fpatbuf[1];
                                break;
                        }
                }
        }
        if (skipend())
                return (0);
        CP(genbuf, "echo "); cp = &genbuf[5];
        for (;;) {
                c = getchar();
                if (endcmd(c)) {
                        ungetchar(c);
                        break;
                }
                switch (c) {

                case '\\':
                        if (any(peekchar(), "#%|"))
                                c = getchar();
                        /* fall into... */

                default:
                        if (cp > &genbuf[LBSIZE - 2])
flong:
                                error(catgets(catd, 1, 79,
                                                "Argument buffer overflow"));
                        *cp++ = c;
                        break;

                case '#':
                        fp = altfile;
                        if (*fp == 0)
                                error(catgets(catd, 1, 80,
                                "No alternate filename@to substitute for #"));
                        goto filexp;

                case '%':
                        fp = savedfile;
                        if (*fp == 0)
                                error(catgets(catd, 1, 81,
                                "No current filename@to substitute for %%"));
filexp:
                        while (*fp) {
                                if (cp > &genbuf[LBSIZE - 2])
                                        goto flong;
                                *cp++ = *fp++;
                        }
                        break;
                }
        }
        *cp = 0;
        return (1);
}

/*
 * Scan genbuf for shell metacharacters.
 * Set is union of v7 shell and csh metas.
 */
int 
gscan(void)
{
        register char *cp;

        for (cp = genbuf; *cp; cp++)
                if (any(*cp, "~{[*?$`'\"\\"))
                        return (1);
        return (0);
}

/*
 * Glob the argument words in genbuf, or if no globbing
 * is implied, just split them up directly.
 */
void 
gglob(struct glob *gp)
{
        int pvec[2];
        register char **argv = gp->argv;
        register char *cp = gp->argspac;
        register int c;
        char ch;
        int nleft = NCARGS;

        gp->argc0 = 0;
        if (gscan() == 0) {
                register char *v = genbuf + 5;          /* strlen("echo ") */

                for (;;) {
                        while (isspace(*v&0377))
                                v++;
                        if (!*v)
                                break;
                        *argv++ = cp;
                        while (*v && !isspace(*v&0377))
                                *cp++ = *v++;
                        *cp++ = 0;
                        gp->argc0++;
                }
                *argv = 0;
                return;
        }
        if (pipe(pvec) < 0)
                error(catgets(catd, 1, 82, "Can't make pipe to glob"));
        pid = fork();
        io = pvec[0];
        if (pid < 0) {
                close(pvec[1]);
                error(catgets(catd, 1, 83, "Can't fork to do glob"));
        }
        if (pid == 0) {
                int oerrno;

                close(1);
                dup(pvec[1]);
                close(pvec[0]);
                close(2);       /* so errors don't mess up the screen */
                open("/dev/null", O_WRONLY);
                execl(svalue(SHELL), "sh", "-c", genbuf, (char *)0);
                oerrno = errno; close(1); dup(2); errno = oerrno;
                filioerr(svalue(SHELL));
        }
        close(pvec[1]);
        do {
                *argv = cp;
                for (;;) {
                        if (read(io, &ch, 1) != 1) {
                                close(io);
                                c = -1;
                        } else
                                c = ch & TRIM;
                        if (c <= 0 || isspace(c))
                                break;
                        *cp++ = c;
                        if (--nleft <= 0)
                                error(catgets(catd, 1, 84,
                                                        "Arg list too long"));
                }
                if (cp != *argv) {
                        --nleft;
                        *cp++ = 0;
                        gp->argc0++;
                        if (gp->argc0 >= NARGS)
                                error(catgets(catd, 1, 85,
                                                        "Arg list too long"));
                        argv++;
                }
        } while (c >= 0);
        waitfor();
        if (gp->argc0 == 0)
                error(catgets(catd, 1, 86, "No match"));
}

/*
 * Parse one filename into file.
 */
struct glob G;
void
getone(void)
{
        register char *str;

        if (getargs() == 0)
missing:
                error(catgets(catd, 1, 87, "Missing filename"));
        gglob(&G);
        if (G.argc0 > 1)
                error(catgets(catd, 1, 88, "Ambiguous|Too many file names"));
        if ((str = G.argv[G.argc0 - 1]) == NULL)
                goto missing;
        if (strlen(str) > FNSIZE - 4)
                error(catgets(catd, 1, 89, "Filename too long"));
/* samef: */
        CP(file, str);
}

/*
 * Are these two really the same inode?
 */
int 
samei(struct stat *sp, char *cp)
{
        struct stat stb;

        if (stat(cp, &stb) < 0 || sp->st_dev != stb.st_dev)
                return (0);
        return (sp->st_ino == stb.st_ino);
}

/*
 * Read a file from the world.
 * C is command, 'e' if this really an edit (or a recover).
 */
void 
rop(int c)
{
        register int i;
        struct stat stbuf;
        char magic[4];
        static int ovro;        /* old value(READONLY) */
        static int denied;      /* 1 if READONLY was set due to file permissions */

        io = open(file, O_RDONLY);
        if (io < 0) {
                if (c == 'e' && errno == ENOENT) {
                        edited++;
                        /*
                         * If the user just did "ex foo" he is probably
                         * creating a new file.  Don't be an error, since
                         * this is ugly, and it screws up the + option.
                         *
                         * POSIX.2 specifies that this be done for all
                         * "edit" commands, not just for the first one.
                         */
                        if (1 || !seenprompt) {
                                printf(catgets(catd, 1, 90, " [New file]"));
                                noonl();
                                return;
                        }
                }
                failed = 1;
                syserror();
        }
        if (fstat(io, &stbuf))
                syserror();
        switch (stbuf.st_mode & S_IFMT) {

        case S_IFBLK:
                error(catgets(catd, 1, 91, " Block special file"));

        case S_IFCHR:
                if (isatty(io))
                        error(catgets(catd, 1, 92, " Teletype"));
                if (samei(&stbuf, "/dev/null"))
                        break;
                error(catgets(catd, 1, 93, " Character special file"));

        case S_IFDIR:
                error(catgets(catd, 1, 94, " Directory"));

#ifdef  S_IFSOCK
        case S_IFSOCK:
                error(catgets(catd, 1, 95, " Socket"));
#endif
#ifdef  S_IFIFO
        case S_IFIFO:
                error(catgets(catd, 1, 96, " Named pipe"));
#endif

        case S_IFREG:
                /*
                 * The magics are checked byte-wise now to avoid
                 * endianness problems. Some quite old types
                 * were omitted.
                 *
                 * Feel free too add more magics here, but do not
                 * make this a copy of the `file' program.
                 *
                 * GR
                 */
                i = read(io, magic, sizeof(magic));
                lseek(io, (off_t) 0, SEEK_SET);
                if (i != sizeof(magic))
                        break;
                switch (magic[0]&0377) {

                case 01:        /* big endian a.out */
                        if (magic[1] != 05 && magic[1] != 07
                                && magic[1] != 010 && magic[1] != 011
                                && magic[1] != 013 && magic[1] != 030
                                && magic[1] != 031)
                                break;
                        goto is_exec;
                case 0314:      /* Linux/ia32 QMAGIC */
                        if (magic[1] != 0 || magic[2] != 0144)
                                break;
                        goto is_exec;
                case 05:        /* data overlay on exec */
                case 07:        /* unshared */
                case 010:       /* shared text */
                case 011:       /* separate I/D */
                case 013:       /* VM/Unix demand paged */
                case 030:       /* PDP-11 Overlay shared */
                case 031:       /* PDP-11 Overlay sep I/D */
                        if (magic[1] == 01)
is_exec:
                                error(catgets(catd, 1, 97, " Executable"));
                        break;

                case 037:
                        switch (magic[1]&0377) {
                        case 036:       /* pack */
                        case 037:       /* compact */
                        case 0235:      /* compress */
                        case 0213:      /* gzip */
                        /*
                         * We omit bzip2 here since it has
                         * an ASCII header.
                         */
                                error(catgets(catd, 1, 98, " Compressed Data"));
                        }
                        break;

                case 0177:
                        if (magic[1] == 'E' && magic[2] == 'L'
                                        && magic[3] == 'F')
                                error(catgets(catd, 1, 99, " ELF object"));
                        break;

                default:
                        break;
                }
#ifdef  notdef
                /*
                 * We do not forbid the editing of portable archives
                 * because it is reasonable to edit them, especially
                 * if they are archives of text files.  This is
                 * especially useful if you archive source files together
                 * and copy them to another system with ~%take, since
                 * the files sometimes show up munged and must be fixed.
                 */
                case 0177545:
                case 0177555:
                        error(catgets(catd, 1, 100, " Archive"));

                default:
#ifdef mbb
                        /* C/70 has a 10 bit byte */
                        if (magic & 03401600)
#else
                        /* Everybody else has an 8 bit byte */
                        if (magic & 0100200)
#endif
                                error(catgets(catd, 1, 101, " Non-ascii file"));
                        break;
                }
#endif  /* notdef */
        }
        if (c != 'r') {
                if (value(READONLY) && denied) {
                        value(READONLY) = ovro;
                        denied = 0;
                }
                if ((stbuf.st_mode & 0222) == 0 || access(file, W_OK) < 0) {
                        ovro = value(READONLY);
                        denied = 1;
                        value(READONLY) = 1;
                }
        }
        if (value(READONLY) && !hush) {
                printf(catgets(catd, 1, 102, " [Read only]"));
                flush();
        }
        if (c == 'r')
                setdot();
        else
                setall();
        if (FIXUNDO && inopen && c == 'r')
                undap1 = undap2 = addr1 + 1;
        rop2();
        rop3(c);
}

void 
rop2(void)
{
        line *first, *last, *a;
        struct stat statb;

        deletenone();
        clrstats();
        first = addr2 + 1;
        if (fstat(io, &statb) < 0 || statb.st_blksize > LBSIZE)
                bsize = LBSIZE;
        else {
                bsize = statb.st_blksize;
                if (bsize <= 0)
                        bsize = LBSIZE;
        }
        ignore(append(getfile, addr2));
        last = dot;
        /*
         *      if the modelines variable is set,
         *      check the first and last five lines of the file
         *      for a mode line.
         */
        if (value(MODELINES)) {
                for (a=first; a<=last; a++) {
                        if (a==first+5 && last-first > 10)
                                a = last - 4;
                        getline(*a);
                        checkmodeline(linebuf);
                }
        }
}

/*
 * Io is finished, close the unit and print statistics.
 */
int 
iostats(void)
{

        fsync(io);
        close(io);
        io = -1;
        if (hush == 0) {
                if (value(TERSE))
                        printf(catgets(catd, 1, 103,
                                                " %d/%d"), cntln, (int)cntch);
                else
                        printf(catgets(catd, 1, 104,
                " %d line%s, %d character%s"), cntln, plural((long) cntln),
                            (int)cntch, plural((int)cntch));
                if (cntnull
#ifndef BIT8
                                || cntodd
#endif
                                ) {
                        printf(catgets(catd, 1, 105, " ("));
                        if (cntnull) {
                                printf(catgets(catd, 1, 106,
                                                "%d null"), (int)cntnull);
#ifndef BIT8
                                if (cntodd)
                                        printf(catgets(catd, 1, 107, ", "));
#endif
                        }
#ifndef BIT8
                        if (cntodd)
                                printf(catgets(catd, 1, 108,
                                        "%d non-ASCII"), (int)cntodd);
#endif
                        putchar(')');
                }
                noonl();
                flush();
        }
        return (cntnull != 0
#ifndef BIT8
                        || cntodd != 0
#endif
                        );
}

void
rop3(int c)
{

        if (iostats() == 0 && c == 'e')
                edited++;
        if (c == 'e') {
                if (wasalt || firstpat) {
                        register line *addr = zero + oldadot;

                        if (addr > dol)
                                addr = dol;
                        if (firstpat) {
                                globp = (*firstpat) ? firstpat : "$";
                                commands(1,1);
                                firstpat = 0;
                        } else if (addr >= one) {
                                if (inopen)
                                        dot = addr;
                                markpr(addr);
                        } else
                                goto other;
                } else
other:
                        if (dol > zero) {
                                if (inopen)
                                        dot = one;
                                markpr(one);
                        }
                if(FIXUNDO)
                        undkind = UNDNONE;
                if (inopen) {
                        vcline = 0;
                        vreplace(0, TLINES, lineDOL());
                }
        }
}

/* Returns from edited() */
#define EDF     0                /* Edited file */
#define NOTEDF  -1              /* Not edited file */
#define PARTBUF 1               /* Write of partial buffer to Edited file */

/*
 * Is file the edited file?
 * Work here is that it is not considered edited
 * if this is a partial buffer, and distinguish
 * all cases.
 */
int 
edfile(void)
{

        if (!edited || !eq(file, savedfile))
                return (NOTEDF);
        return (addr1 == one && addr2 == dol ? EDF : PARTBUF);
}

/*
 * Write a file.
 */
void
wop(bool dofname)
/*bool dofname; /\* if 1 call filename, else use savedfile */
{
        register int c, exclam, nonexist;
        line *saddr1 = NULL, *saddr2 = NULL;
        struct stat stbuf;

        c = 0;
        exclam = 0;
        if (dofname) {
                if (peekchar() == '!')
                        exclam++, ignchar();
                ignore(skipwh());
                while (peekchar() == '>')
                        ignchar(), c++, ignore(skipwh());
                if (c != 0 && c != 2)
                        error(catgets(catd, 1, 109,
                                        "Write forms are 'w' and 'w>>'"));
                filename('w');
        } else {
                if (savedfile[0] == 0)
                        error(catgets(catd, 1, 110,
                                        "No file|No current filename"));
                saddr1=addr1;
                saddr2=addr2;
                addr1=one;
                addr2=dol;
                CP(file, savedfile);
                if (inopen) {
                        vclrech(0);
                        splitw++;
                }
                lprintf("\"%s\"", file);
        }
        nonexist = stat(file, &stbuf);
        switch (c) {

        case 0:
                if (!exclam && (!value(WRITEANY) || value(READONLY)))
                switch (edfile()) {
                
                case NOTEDF:
                        if (nonexist)
                                break;
                        if ((stbuf.st_mode & S_IFMT) == S_IFCHR) {
                                if (samei(&stbuf, "/dev/null"))
                                        break;
                                if (samei(&stbuf, "/dev/tty"))
                                        break;
                        }
                        io = open(file, O_WRONLY);
                        if (io < 0)
                                syserror();
                        if (!isatty(io))
                                serror(catgets(catd, 1, 111,
        " File exists| File exists - use \"w! %s\" to overwrite"), file);
                        close(io);
                        break;

                case EDF:
                        if (value(READONLY))
                                error(catgets(catd, 1, 112,
                                                " File is read only"));
                        break;

                case PARTBUF:
                        if (value(READONLY))
                                error(catgets(catd, 1, 113,
                                                " File is read only"));
                        error(catgets(catd, 1, 114,
                                " Use \"w!\" to write partial buffer"));
                }
cre:
/*
                synctmp();
*/
                io = creat(file, 0666);
                if (io < 0)
                        syserror();
                writing = 1;
                if (hush == 0)
                        if (nonexist)
                                printf(catgets(catd, 1, 115, " [New file]"));
                        else if (value(WRITEANY) && edfile() != EDF)
                                printf(catgets(catd, 1, 116,
                                                        " [Existing file]"));
                break;

        case 2:
                io = open(file, O_WRONLY);
                if (io < 0) {
                        if (exclam || value(WRITEANY))
                                goto cre;
                        syserror();
                }
                lseek(io, (off_t) 0, SEEK_END);
                break;
        }
        putfile(0);
        ignore(iostats());
        if (c != 2 && addr1 == one && addr2 == dol) {
                if (eq(file, savedfile))
                        edited = 1;
                synced();
        }
        if (!dofname) {
                addr1 = saddr1;
                addr2 = saddr2;
        }
        writing = 0;
}

/*
 * Extract the next line from the io stream.
 */
char *nextip;

int
getfile(void)
{
        register short c;
        register char *lp, *fp;

        lp = linebuf;
        fp = nextip;
        do {
                if (--ninbuf < 0) {
                        ninbuf = read(io, genbuf, bsize) - 1;
                        if (ninbuf < 0) {
                                if (lp != linebuf) {
                                        lp++;
                                        printf(catgets(catd, 1, 117,
                                                " [Incomplete last line]"));
                                        break;
                                }
                                return (EOF);
                        }
                        fp = genbuf;
                        cntch += ninbuf+1;
                }
                if (lp >= &linebuf[LBSIZE]) {
                        synced();
                        error(catgets(catd, 1, 118, " Line too long"));
                }
                c = *fp++;
                if (c == 0) {
                        cntnull++;
#ifndef BIT8
                        continue;
#else
                        c = 0200;
#endif
                }
#ifndef BIT8
                if (c & QUOTE) {
                        cntodd++;
                        c &= TRIM;
                        if (c == 0)
                                continue;
                }
#endif
                *lp++ = c;
        } while (c != '\n');
        *--lp = 0;
        nextip = fp;
        cntln++;
        return (0);
}

/*
 * Write a range onto the io stream.
 */
void
putfile(int isfilter)
{
        line *a1;
        register char *fp, *lp;
        register int nib;
        struct stat statb;

        a1 = addr1;
        clrstats();
        cntln = fixedzero ? 0 : addr2 - a1 + 1;
        if (cntln == 0 || fixedzero)
                return;
        if (fstat(io, &statb) < 0 || statb.st_blksize > LBSIZE)
                bsize = LBSIZE;
        else {
                bsize = statb.st_blksize;
                if (bsize <= 0)
                        bsize = LBSIZE;
        }
        nib = bsize;
        fp = genbuf;
        do {
                getline(*a1++);
                lp = linebuf;
                for (;;) {
                        if (--nib < 0) {
                                nib = fp - genbuf;
                                if (write(io, genbuf, nib) != nib) {
                                        wrerror();
                                }
                                cntch += nib;
                                nib = bsize - 1;
                                fp = genbuf;
                        }
                        if ((*fp++ = *lp++) == 0) {
                                fp[-1] = '\n';
                                break;
                        }
                }
        } while (a1 <= addr2);
        nib = fp - genbuf;
        if (write(io, genbuf, nib) != nib) {
                wrerror();
        }
        cntch += nib;
}

/*
 * A write error has occurred;  if the file being written was
 * the edited file then we consider it to have changed since it is
 * now likely scrambled.
 */
void
wrerror(void)
{

        if (eq(file, savedfile) && edited)
                change();
        syserror();
}

/*
 * Source command, handles nested sources.
 * Traps errors since it mungs unit 0 during the source.
 */
short slevel;
short ttyindes;

void
source(char *fil, bool okfail)
{
        JMP_BUF osetexit;
        register int saveinp, ointty, oerrno;
        char *saveglobp, *saveinput;
        char    saveinline[BUFSIZ];
        int savepeekc, savelastc;

        signal(SIGINT, SIG_IGN);
        saveinp = dup(0);
        savepeekc = peekc;
        savelastc = lastc;
        saveglobp = globp;
        saveinput = input;
        if (input)
                strcpy(saveinline, input);
        peekc = 0; lastc = 0; globp = 0; input = 0;
        if (saveinp < 0)
                error(catgets(catd, 1, 119, "Too many nested sources"));
        if (slevel <= 0)
                ttyindes = saveinp;
        close(0);
        if (open(fil, O_RDONLY) < 0) {
                oerrno = errno;
                setrupt();
                dup(saveinp);
                close(saveinp);
                input = saveinput;
                if (input)
                        strcpy(input, saveinline);
                lastc = savelastc;
                errno = oerrno;
                if (!okfail)
                        filioerr(fil);
                return;
        }
        slevel++;
        ointty = intty;
        intty = isatty(0);
        oprompt = value(PROMPT);
        value(PROMPT) &= intty;
        getexit(osetexit);
        setrupt();
        if (setexit() == 0)
                commands(1, 1);
        else if (slevel > 1) {
                close(0);
                dup(saveinp);
                close(saveinp);
                input = saveinput;
                if (input)
                        strcpy(input, saveinline);
                lastc = savelastc;
                slevel--;
                resexit(osetexit);
                reset();
        }
        intty = ointty;
        value(PROMPT) = oprompt;
        close(0);
        dup(saveinp);
        close(saveinp);
        globp = saveglobp;
        input = saveinput;
        if (input)
                strcpy(input, saveinline);
        peekc = savepeekc;
        lastc = savelastc;
        slevel--;
        resexit(osetexit);
}

/*
 * Clear io statistics before a read or write.
 */
void
clrstats(void)
{

        ninbuf = 0;
        cntch = 0;
        cntln = 0;
        cntnull = 0;
#ifndef BIT8
        cntodd = 0;
#endif
}

/* It's so wonderful how we all speak the same language... */
# define index strchr
# define rindex strrchr

void
checkmodeline(char *lin)
{
        char *beg, *end;
        char cmdbuf[BUFSIZ];

        beg = index(lin, ':');
        if (beg == NULL)
                return;
        if (&beg[-2] < lin)
                return;
        if (!((beg[-2] == 'e' && beg[-1] == 'x')
             || (beg[-2] == 'v' && beg[-1] == 'i'))) return;
        strncpy(cmdbuf, beg+1, sizeof cmdbuf);
        end = rindex(cmdbuf, ':');
        if (end == NULL)
                return;
        *end = 0;
        globp = cmdbuf;
        commands(1, 1);
}

#ifdef  MB
int
mbtowi(int *cp, const char *s, size_t n)
{
        wchar_t wc;
        int     i;

        i = mbtowc(&wc, s, n);
        if (i >= 0 && widthok(wc) && !(wc & 0x70000000))
                *cp = wc;
        else {
                *cp = *s&0377 | INVBIT;
                i = 1;
        }
        return i;
}

int
widthok(int c)
{
        return inopen ? wcwidth(c) <= 2 : 1;
}
#endif  /* MB */

int
GETWC(char *mb)
{
        int     c, n;

        n = 1;
        mb[0] = c = getchar();
        mb[1] = '\0';
#ifdef  MB
        if (mb_cur_max > 1 && c & 0200 && c != EOF) {
                int     m;
                wchar_t wc;
                while ((m = mbtowc(&wc, mb, mb_cur_max)) < 0 && n<mb_cur_max) {
                        mb[n++] = c = getchar();
                        mb[n] = '\0';
                        if (c == '\n' || c == EOF)
                                break;
                }
                if (m != n || c & 0x70000000)
                        error("illegal multibyte sequence");
                return wc;
        } else
#endif  /* MB */
                return c;
}