/* * 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 #include /* * 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 = ®rbuf; 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(); }