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_temp.c   1.24 (gritter) 11/24/04";
#endif
#endif

/* from ex_temp.c       7.5.1.1 (Berkeley) 8/12/86 */

#include "ex.h"
#include "ex_temp.h"
#include "ex_vis.h"
#include "ex_tty.h"
#include <sys/wait.h>
#include <time.h>

/*
 * Editor temporary file routines.
 * Very similar to those of ed, except uses 2 input buffers.
 */
#define READ    0
#define WRITE   1

/*
 * Maximum number of attempts to create temporary file.
 */
#define ATTEMPTS        20

char    *tfname;
char    *rfname;
int     havetmp;
int     tfile = -1;
int     rfile = -1;

void
fileinit(void)
{
        register char *p;
        struct stat stbuf;
        register int i, j;
        pid_t mypid = getpid();
        char *tfend;
        int attempts = 0;

        CLOBBGRD(attempts);
        if (tline == INCRMT * (HBLKS+2))
                return;
        cleanup(0);
        if (tfile != -1)
                close(tfile);
        tline = INCRMT * (HBLKS+2);
        blocks[0] = HBLKS;
        blocks[1] = HBLKS+1;
        blocks[2] = -1;
        dirtcnt = 0;
        iblock = -1;
        iblock2 = -1;
        oblock = -1;
        tfname = realloc(tfname, strlen(svalue(DIRECTORY)) + 14);
        CP(tfname, svalue(DIRECTORY));
        if (stat(tfname, &stbuf)) {
dumbness:
                if (setexit() == 0)
                        filioerr(tfname);
                else
                        putNFL();
                cleanup(1);
                exitex(1);
        }
        if ((stbuf.st_mode & S_IFMT) != S_IFDIR) {
                errno = ENOTDIR;
                goto dumbness;
        }
        ichanged = 0;
        ichang2 = 0;
#ifdef  notdef  /* GR */
        ignore(strcat(tfname, "/ExXXXXX"));
        for (p = strend(tfname), i = 5, j = getpid(); i > 0; i--, j /= 10)
                *--p = j % 10 | '0';
        tfile = creat(tfname, 0600);
#else
        ignore(strcat(tfname, "/ExXXXXXXXXXX"));
        tfend = strend(tfname);
        do {
                for (p = tfend, i = 10, j = mypid + attempts;
                                i > 0; i--, j /= 10)
                        *--p = j % 10 | '0';
                tfile = open(tfname, O_CREAT|O_EXCL|O_RDWR
#ifdef  O_NOFOLLOW
                                |O_NOFOLLOW
#endif  /* O_NOFOLLOW */
                                , 0600);
        } while (tfile < 0 && attempts++ < ATTEMPTS);
#endif  /* !notdef */
        if (tfile < 0)
                goto dumbness;
#ifdef INCORB
        {
                extern bloc stilinc;            /* see below */
                stilinc = 0;
        }
#endif
        havetmp = 1;
/*      brk((char *)fendcore); */
}

void
cleanup(bool all)
{
        if (all) {
                putpad(TE);
                flush();
        }
        if (havetmp)
                unlink(tfname);
        havetmp = 0;
        if (all && rfile >= 0) {
                unlink(rfname);
                close(rfile);
                rfile = -1;
        }
}

void
getline(line tl)
{
        register char *bp, *lp;
        register bbloc nl;

        lp = linebuf;
        bp = getblock(tl, READ);
        nl = nleft;
        tl &= ~OFFMSK;
        while (*lp++ = *bp++)
                if (--nl == 0) {
                        bp = getblock(tl += INCRMT, READ);
                        nl = nleft;
                }
}

line
putline(void)
{
        register char *bp, *lp;
        register bbloc nl;
        line tl;

        dirtcnt++;
        lp = linebuf;
        change();
        tl = tline;
        bp = getblock(tl, WRITE);
        nl = nleft;
        tl &= ~OFFMSK;
        while (*bp = *lp++) {
                if (*bp++ == '\n') {
                        *--bp = 0;
                        linebp = lp;
                        break;
                }
                if (--nl == 0) {
                        bp = getblock(tl += INCRMT, WRITE);
                        nl = nleft;
                }
        }
        tl = tline;
        tline += (((lp - linebuf) + BNDRY - 1) >> SHFT) & 077776;
        return (tl);
}

char *
getblock(line atl, int iof)
{
        register bbloc bno, off;
        
        bno = (atl >> OFFBTS) & BLKMSK;
        off = (atl << SHFT) & LBTMSK;
        if (bno >= NMBLKS)
                error(catgets(catd, 1, 183, " Tmp file too large"));
        nleft = BUFSIZ - off;
        if (bno == iblock) {
                ichanged |= iof;
                hitin2 = 0;
                return (ibuff + off);
        }
        if (bno == iblock2) {
                ichang2 |= iof;
                hitin2 = 1;
                return (ibuff2 + off);
        }
        if (bno == oblock)
                return (obuff + off);
        if (iof == READ) {
                if (hitin2 == 0) {
                        if (ichang2) {
                                blkio(iblock2, ibuff2, (ssize_t(*)())write);
                        }
                        ichang2 = 0;
                        iblock2 = bno;
                        blkio(bno, ibuff2, (ssize_t(*)())read);
                        hitin2 = 1;
                        return (ibuff2 + off);
                }
                hitin2 = 0;
                if (ichanged) {
                        blkio(iblock, ibuff, (ssize_t(*)())write);
                }
                ichanged = 0;
                iblock = bno;
                blkio(bno, ibuff, (ssize_t(*)())read);
                return (ibuff + off);
        }
        if (oblock >= 0) {
                        blkio(oblock, obuff, (ssize_t(*)())write);
        }
        oblock = bno;
        return (obuff + off);
}

#ifdef  INCORB
char    incorb[INCORB+1][BUFSIZ];
#define pagrnd(a)       ((char *)(((size_t)a)&~(BUFSIZ-1)))
bloc    stilinc;        /* up to here not written yet */
#endif

void
blkio(bloc b, char *buf, ssize_t (*iofcn)(int, void *, size_t))
{

#ifdef INCORB
        if (b < INCORB) {
                if (iofcn == (ssize_t(*)())read) {
                        copy(buf, pagrnd(incorb[b+1]), (size_t) BUFSIZ);
                        return;
                }
                copy(pagrnd(incorb[b+1]), buf, (size_t) BUFSIZ);
                if (laste) {
                        if (b >= stilinc)
                                stilinc = b + 1;
                        return;
                }
        } else if (stilinc)
                tflush();
#endif
        lseek(tfile, (off_t) ((b & BLKMSK) * BUFSIZ), SEEK_SET);
        if ((*iofcn)(tfile, buf, BUFSIZ) != BUFSIZ)
                filioerr(tfname);
}

#ifdef INCORB
void
tlaste(void)
{

        if (stilinc)
                dirtcnt = 0;
}

void
tflush(void)
{
        bbloc i = stilinc;
        
        stilinc = 0;
        lseek(tfile, (off_t) 0, SEEK_SET);
        if (write(tfile, pagrnd(incorb[1]), i * BUFSIZ) != (i * BUFSIZ))
                filioerr(tfname);
}
#endif

/*
 * Synchronize the state of the temporary file in case
 * a crash occurs.
 */
void
synctmp(void)
{
        register bbloc cnt;
        register line *a;
        register bloc *bp, *up;

#ifdef INCORB
        if (stilinc)
                return;
#endif
        if (dol == zero)
                return;
        if (ichanged)
                blkio(iblock, ibuff, (ssize_t(*)())write);
        ichanged = 0;
        if (ichang2)
                blkio(iblock2, ibuff2, (ssize_t(*)())write);
        ichang2 = 0;
        if (oblock != -1)
                blkio(oblock, obuff, (ssize_t(*)())write);
        time(&H.Time);
        uid = getuid();
        *zero = (line) H.Time;
        up = blocks + LBLKS;
        for (a = zero, bp = blocks; a <= dol; a += BUFSIZ / sizeof *a, bp++) {
                if (bp >= up)
                        error(catgets(catd, 1, 184, " Tmp file too large"));
                if (*bp < 0) {
                        tline = (tline + OFFMSK) &~ OFFMSK;
                        *bp = ((tline >> OFFBTS) & BLKMSK);
                        if (*bp > NMBLKS)
                                error(catgets(catd, 1, 185,
                                                " Tmp file too large"));
                        tline += INCRMT;
                        oblock = *bp + 1;
                        bp[1] = -1;
                }
                lseek(tfile, (off_t) ((*bp & BLKMSK) * BUFSIZ), SEEK_SET);
                cnt = ((dol - a) + 2) * sizeof (line);
                if (cnt > BUFSIZ)
                        cnt = BUFSIZ;
                if (write(tfile, (char *) a, cnt) != cnt) {
oops:
                        *zero = 0;
                        filioerr(tfname);
                }
                *zero = 0;
        }
        flines = lineDOL();
        lseek(tfile, (off_t) 0, SEEK_SET);
        if (write(tfile, (char *) &H, sizeof H) != sizeof H)
                goto oops;
#ifdef notdef
        /*
         * This will insure that exrecover gets as much
         * back after a crash as is absolutely possible,
         * but can result in pregnant pauses between commands
         * when the TSYNC call is made, so...
         */
        fsync(tfile);
#endif
}

void
TSYNC(void)
{

        if (dirtcnt > MAXDIRT) {        /* mjm: 12 --> MAXDIRT */
#ifdef INCORB
                if (stilinc)
                        tflush();
#endif
                dirtcnt = 0;
                synctmp();
        }
}

/*
 * Named buffer routines.
 * These are implemented differently than the main buffer.
 * Each named buffer has a chain of blocks in the register file.
 * Each block contains roughly 508 chars of text,
 * and a previous and next block number.  We also have information
 * about which blocks came from deletes of multiple partial lines,
 * e.g. deleting a sentence or a LISP object.
 *
 * We maintain a free map for the temp file.  To free the blocks
 * in a register we must read the blocks to find how they are chained
 * together.
 *
 * BUG:         The default savind of deleted lines in numbered
 *              buffers may be rather inefficient; it hasn't been profiled.
 */
struct  strreg {
        short   rg_flags;
        short   rg_nleft;
        short   rg_first;
        short   rg_last;
} strregs[('z'-'a'+1) + ('9'-'0'+1)], *strp;

struct  rbuf {
        short   rb_prev;
        short   rb_next;
        char    rb_text[BUFSIZ - 2 * sizeof (short)];
} *rbuf, KILLrbuf, putrbuf, YANKrbuf, regrbuf;
#ifdef  VMUNIX
#ifdef  LARGEF
short   rused[4096];
#else   /* !LARGEF */
short   rused[256];
#endif  /* !LARGEF */
#else   /* !VMUNIX */
short   rused[32];
#endif  /* !VMUNIX */
short   rnleft;
short   rblock;
short   rnext;
char    *rbufcp;

void
regio(short b, ssize_t (*iofcn)(int, void *, size_t))
{
        register char *p;
        char *rfend;
        int attempts = 0;
        register int i, j;
        pid_t mypid = getpid();

        if (rfile == -1) {
                rfname = realloc(rfname, strlen(svalue(DIRECTORY)) + 14);
                CP(rfname, tfname);
                rfend = strend(rfname);
#ifdef  notdef  /* GR */
                *(rfend - 7) = 'R';
#else
                *(rfend - 12) = 'R';
#endif
                do {
                        for (p = rfend, i = 10, j = mypid + attempts;
                                        i > 0; i--, j /= 10)
                                *--p = j % 10 | '0';
                        rfile = open(rfname, O_CREAT|O_EXCL|O_RDWR
#ifdef  O_NOFOLLOW
                                        |O_NOFOLLOW
#endif  /* O_NOFOLLOW */
                                        , 0600);
                } while (rfile < 0 && attempts++ < ATTEMPTS);
                if (rfile < 0)
oops:
                        filioerr(rfname);
        }
        lseek(rfile, (off_t) ((b & BLKMSK) * BUFSIZ), SEEK_SET);
        if ((*iofcn)(rfile, rbuf, BUFSIZ) != BUFSIZ)
                goto oops;
        rblock = b;
}

int
REGblk(void)
{
        register int i, j, m;

        for (i = 0; i < sizeof rused / sizeof rused[0]; i++) {
                m = (rused[i] ^ 0177777) & 0177777;
                if (i == 0)
                        m &= ~1;
                if (m != 0) {
                        j = 0;
                        while ((m & 1) == 0)
                                j++, m >>= 1;
                        rused[i] |= (1 << j);
#ifdef RDEBUG
                        printf("allocating block %d\n", i * 16 + j);
#endif
                        return (i * 16 + j);
                }
        }
        error(catgets(catd, 1, 186, "Out of register space (ugh)"));
        /*NOTREACHED*/
        return 0;
}

struct  strreg *
mapreg(register int c)
{

        if (isupper(c))
                c = tolower(c);
        return (isdigit(c) ? &strregs[('z'-'a'+1)+(c-'0')] : &strregs[c-'a']);
}

void
KILLreg(register int c)
{
        register struct strreg *sp;

        rbuf = &KILLrbuf;
        sp = mapreg(c);
        rblock = sp->rg_first;
        sp->rg_first = sp->rg_last = 0;
        sp->rg_flags = sp->rg_nleft = 0;
        while (rblock != 0) {
#ifdef RDEBUG
                printf("freeing block %d\n", rblock);
#endif
                rused[rblock / 16] &= ~(1 << (rblock % 16));
                regio(rblock, (ssize_t (*)(int, void *, size_t))shread);
                rblock = rbuf->rb_next;
        }
}

ssize_t
shread(void)
{
        struct front { short a; short b; };

        if (read(rfile, (char *) rbuf, sizeof (struct front)) == sizeof (struct front))
                return (sizeof (struct rbuf));
        return (0);
}

int     getREG();

void
putreg(int c)
{
        register line *odot = dot;
        register line *odol = dol;
        register int cnt;

        deletenone();
        appendnone();
        rbuf = &putrbuf;
        rnleft = 0;
        rblock = 0;
        rnext = mapreg(c)->rg_first;
        if (rnext == 0) {
                if (inopen) {
                        splitw++;
                        vclean();
                        vgoto(WECHO, 0);
                }
                vreg = -1;
                error(catgets(catd, 1, 187, "Nothing in register %c"), c);
        }
        if (inopen && partreg(c)) {
                if (!FIXUNDO) {
                        splitw++; vclean(); vgoto(WECHO, 0); vreg = -1;
                        error(catgets(catd, 1, 188,
                                "Can't put partial line inside macro"));
                }
                squish();
                addr1 = addr2 = dol;
        }
        cnt = append(getREG, addr2);
        if (inopen && partreg(c)) {
                unddol = dol;
                dol = odol;
                dot = odot;
                pragged(0);
        }
        killcnt(cnt);
        notecnt = cnt;
}

int
partreg(int c)
{

        return (mapreg(c)->rg_flags);
}

void
notpart(register int c)
{

        if (c)
                mapreg(c)->rg_flags = 0;
}

int
getREG(void)
{
        register char *lp = linebuf;
        register int c;

        for (;;) {
                if (rnleft == 0) {
                        if (rnext == 0)
                                return (EOF);
                        regio(rnext, read);
                        rnext = rbuf->rb_next;
                        rbufcp = rbuf->rb_text;
                        rnleft = sizeof rbuf->rb_text;
                }
                c = *rbufcp;
                if (c == 0)
                        return (EOF);
                rbufcp++, --rnleft;
                if (c == '\n') {
                        *lp++ = 0;
                        return (0);
                }
                *lp++ = c;
        }
}

void
YANKreg(register int c)
{
        register line *addr;
        register struct strreg *sp;
        char savelb[LBSIZE];

        if (isdigit(c))
                kshift();
        if (islower(c))
                KILLreg(c);
        strp = sp = mapreg(c);
        sp->rg_flags = inopen && cursor && wcursor;
        rbuf = &YANKrbuf;
        if (sp->rg_last) {
                regio(sp->rg_last, read);
                rnleft = sp->rg_nleft;
                rbufcp = &rbuf->rb_text[sizeof rbuf->rb_text - rnleft];
        } else {
                rblock = 0;
                rnleft = 0;
        }
        CP(savelb,linebuf);
        for (addr = addr1; addr <= addr2; addr++) {
                getline(*addr);
                if (sp->rg_flags) {
                        if (addr == addr2)
                                *wcursor = 0;
                        if (addr == addr1)
                                strcpy(linebuf, cursor);
                }
                YANKline();
        }
        rbflush();
        killed();
        CP(linebuf,savelb);
}

void
kshift(void)
{
        register int i;

        KILLreg('9');
        for (i = '8'; i >= '0'; i--)
                copy(mapreg(i+1), mapreg(i), sizeof (struct strreg));
}

void
YANKline(void)
{
        register char *lp = linebuf;
        register struct rbuf *rp = rbuf;
        register int c;

        do {
                c = *lp++;
                if (c == 0)
                        c = '\n';
                if (rnleft == 0) {
                        rp->rb_next = REGblk();
                        rbflush();
                        rblock = rp->rb_next;
                        rp->rb_next = 0;
                        rp->rb_prev = rblock;
                        rnleft = sizeof rp->rb_text;
                        rbufcp = rp->rb_text;
                }
                *rbufcp++ = c;
                --rnleft;
        } while (c != '\n');
        if (rnleft)
                *rbufcp = 0;
}

void
rbflush(void)
{
        register struct strreg *sp = strp;

        if (rblock == 0)
                return;
        regio(rblock, (ssize_t (*)(int, void *, size_t))write);
        if (sp->rg_first == 0)
                sp->rg_first = rblock;
        sp->rg_last = rblock;
        sp->rg_nleft = rnleft;
}

/* Register c to char buffer buf of size buflen */
void
regbuf(char c, char *buf, int buflen)
{
        register char *p, *lp;

        rbuf = &regrbuf;
        rnleft = 0;
        rblock = 0;
        rnext = mapreg(c)->rg_first;
        if (rnext==0) {
                *buf = 0;
                error(catgets(catd, 1, 189, "Nothing in register %c"),c);
        }
        p = buf;
        while (getREG()==0) {
                for (lp=linebuf; *lp;) {
                        if (p >= &buf[buflen])
                                error(catgets(catd, 1, 190,
                                        "Register too long@to fit in memory"));
                        *p++ = *lp++;
                }
                *p++ = '\n';
        }
        if (partreg(c)) p--;
        *p = '\0';
        getDOT();
}