From a3869972cb27bf14f88558111f08871e893c0ab5 Mon Sep 17 00:00:00 2001 From: Bill Joy Date: Tue, 8 May 1979 23:20:22 -0800 Subject: [PATCH] BSD 2 development Work on file src/ex/ex_temp.c Work on file src/ex/ex_temp.h Work on file src/ex/ex_tty.c Work on file src/ex/ex_tty.h Synthesized-from: 2bsd --- src/ex/ex_temp.c | 531 +++++++++++++++++++++++++++++++++++++++++++++++ src/ex/ex_temp.h | 94 +++++++++ src/ex/ex_tty.c | 120 +++++++++++ src/ex/ex_tty.h | 97 +++++++++ 4 files changed, 842 insertions(+) create mode 100644 src/ex/ex_temp.c create mode 100644 src/ex/ex_temp.h create mode 100644 src/ex/ex_tty.c create mode 100644 src/ex/ex_tty.h diff --git a/src/ex/ex_temp.c b/src/ex/ex_temp.c new file mode 100644 index 0000000000..cf0ec58c11 --- /dev/null +++ b/src/ex/ex_temp.c @@ -0,0 +1,531 @@ +/* Copyright (c) 1979 Regents of the University of California */ +#include "ex.h" +#include "ex_temp.h" +#include "ex_vis.h" + +/* + * Editor temporary file routines. + * Very similar to those of ed, except uses 2 input buffers. + */ +#define READ 0 +#define WRITE 1 + +char tfname[40]; +char rfname[40]; +int havetmp; +short tfile = -1; +short rfile = -1; + +fileinit() +{ + register char *p; + register int i, j; + struct stat stbuf; + + if (tline == INCRMT * 3) + return; + cleanup(0); + close(tfile); + tline = INCRMT * 3; + blocks[0] = 1; + blocks[1] = 2; + blocks[2] = -1; + dirtcnt = 0; + iblock = -1; + iblock2 = -1; + oblock = -1; + CP(tfname, svalue(DIRECTORY)); + if (stat(tfname, &stbuf)) { +dumbness: + if (setexit() == 0) + filioerr(tfname); + else + putNFL(); + cleanup(1); + exit(1); + } + if ((stbuf.st_mode & S_IFMT) != S_IFDIR) { + errno = ENOTDIR; + goto dumbness; + } + ichanged = 0; + ichang2 = 0; + 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); + if (tfile < 0) + goto dumbness; + havetmp = 1; + close(tfile); + tfile = open(tfname, 2); + if (tfile < 0) + goto dumbness; +/* brk((char *)fendcore); */ +} + +cleanup(all) + bool all; +{ + if (havetmp) + unlink(tfname); + havetmp = 0; + if (all && rfile >= 0) { + unlink(rfname); + close(rfile); + rfile = -1; + } +} + +getline(tl) + line tl; +{ + register char *bp, *lp; + register int nl; + + lp = linebuf; + bp = getblock(tl, READ); + nl = nleft; + tl &= ~OFFMSK; + while (*lp++ = *bp++) + if (--nl == 0) { + bp = getblock(tl += INCRMT, READ); + nl = nleft; + } +} + +putline() +{ + register char *bp, *lp; + register int 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); +} + +int read(); +int write(); + +char * +getblock(atl, iof) + line atl; + int iof; +{ + register int bno, off; + + bno = (atl >> OFFBTS) & BLKMSK; + off = (atl << SHFT) & LBTMSK; + if (bno >= NMBLKS) + error(" 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, write); + ichang2 = 0; + iblock2 = bno; + blkio(bno, ibuff2, read); + hitin2 = 1; + return (ibuff2 + off); + } + hitin2 = 0; + if (ichanged) + blkio(iblock, ibuff, write); + ichanged = 0; + iblock = bno; + blkio(bno, ibuff, read); + return (ibuff + off); + } + if (oblock >= 0) + blkio(oblock, obuff, write); + oblock = bno; + return (obuff + off); +} + +blkio(b, buf, iofcn) + short b; + char *buf; + int (*iofcn)(); +{ + + lseek(tfile, (long) (unsigned) b * BUFSIZ, 0); + if ((*iofcn)(tfile, buf, BUFSIZ) != BUFSIZ) + filioerr(tfname); +} + +/* + * Synchronize the state of the temporary file in case + * a crash occurs. + */ +synctmp() +{ + register int cnt; + register line *bp, *a; + + if (dol == zero) + return; + if (ichanged) + blkio(iblock, ibuff, write); + ichanged = 0; + if (ichang2) + blkio(iblock2, ibuff2, write); + ichang2 = 0; + if (oblock != -1) + blkio(oblock, obuff, write); + time(&H.Time); + uid = getuid(); + *zero = (line) H.Time; + for (a = zero, bp = blocks; a <= dol; a += BUFSIZ / sizeof *a, bp++) { + if (*bp < 0) { + tline = (tline + OFFMSK) &~ OFFMSK; + *bp = ((tline >> OFFBTS) & BLKMSK); + tline += INCRMT; + oblock = *bp + 1; + bp[1] = -1; + } + lseek(tfile, (long) (unsigned) *bp * BUFSIZ, 0); + 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, 0l, 0); + if (write(tfile, (char *) &H, sizeof H) != sizeof H) + goto oops; +} + +TSYNC() +{ + + if (dirtcnt > 12) { + 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; +short rused[32]; +short rnleft; +short rblock; +short rnext; +char *rbufcp; + +regio(b, iofcn) + short b; + int (*iofcn)(); +{ + + if (rfile == -1) { + CP(rfname, tfname); + *(strend(rfname) - 7) = 'R'; + rfile = creat(rfname, 0600); + if (rfile < 0) +oops: + filioerr(rfname); + close(rfile); + rfile = open(rfname, 2); + if (rfile < 0) + goto oops; + } + lseek(rfile, (long) b * BUFSIZ, 0); + if ((*iofcn)(rfile, rbuf, BUFSIZ) != BUFSIZ) + goto oops; + rblock = b; +} + +REGblk() +{ + register int i, j, m; + + for (i = 0; i < sizeof rused / sizeof rused[0]; i++) { + m = rused[i] ^ 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("Out of register space (ugh)"); + /*NOTREACHED*/ +} + +struct strreg * +mapreg(c) + register int c; +{ + + if (isupper(c)) + c = tolower(c); + return (isdigit(c) ? &strregs[('z'-'a'+1)+(c-'0')] : &strregs[c-'a']); +} + +int shread(); + +KILLreg(c) + register int c; +{ + struct rbuf arbuf; + register struct strreg *sp; + + rbuf = &arbuf; + 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, shread); + rblock = rbuf->rb_next; + } +} + +/*VARARGS*/ +shread() +{ + 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(); + +putreg(c) + char c; +{ + struct rbuf arbuf; + register line *odot = dot; + register line *odol = dol; + register int cnt; + + deletenone(); + appendnone(); + rbuf = &arbuf; + rnleft = 0; + rblock = 0; + rnext = mapreg(c)->rg_first; + if (rnext == 0) { + if (inopen) { + splitw++; + vclean(); + vgoto(WECHO, 0); + } + vreg = -1; + error("Nothing in register %c", c); + } + if (inopen && partreg(c)) { + squish(); + addr1 = addr2 = dol; + } + ignore(append(getREG, addr2)); + if (inopen && partreg(c)) { + unddol = dol; + dol = odol; + dot = odot; + pragged(0); + } + cnt = undap2 - undap1; + killcnt(cnt); + notecnt = cnt; +} + +partreg(c) + char c; +{ + + return (mapreg(c)->rg_flags); +} + +notpart(c) + register int c; +{ + + if (c) + mapreg(c)->rg_flags = 0; +} + +getREG() +{ + 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; + } +} + +YANKreg(c) + register int c; +{ + struct rbuf arbuf; + register line *addr; + register struct strreg *sp; + + if (isdigit(c)) + kshift(); + if (islower(c)) + KILLreg(c); + strp = sp = mapreg(c); + sp->rg_flags = inopen && cursor && wcursor; + rbuf = &arbuf; + 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; + } + 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(); +} + +kshift() +{ + register int i; + + KILLreg('9'); + for (i = '8'; i >= '0'; i--) + copy(mapreg(i+1), mapreg(i), sizeof (struct strreg)); +} + +YANKline() +{ + 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; +} + +rbflush() +{ + register struct strreg *sp = strp; + + if (rblock == 0) + return; + regio(rblock, write); + if (sp->rg_first == 0) + sp->rg_first = rblock; + sp->rg_last = rblock; + sp->rg_nleft = rnleft; +} diff --git a/src/ex/ex_temp.h b/src/ex/ex_temp.h new file mode 100644 index 0000000000..438367a940 --- /dev/null +++ b/src/ex/ex_temp.h @@ -0,0 +1,94 @@ +/* Copyright (c) 1979 Regents of the University of California */ +/* + * The editor uses a temporary file for files being edited, in a structure + * similar to that of ed. The first block of the file is used for a header + * block which guides recovery after editor/system crashes. + * Lines are represented in core by a pointer into the temporary file which + * is packed into 16 bits. 15 of these bits index the temporary file, + * the 16'th is used by global commands. The parameters below control + * how much the 15 bits are shifted left before they index the temp file. + * Larger shifts give more slop in the temp file but allow larger files + * to be edited. + * + * The editor does not garbage collect the temporary file. When a new + * file is edited, the temporary file is rather discarded and a new one + * created for the new file. Garbage collection would be rather complicated + * in ex because of the general undo, and in any case would require more + * work when throwing lines away because marks would have be carefully + * checked before reallocating temporary file space. Said another way, + * each time you create a new line in the temporary file you get a unique + * number back, and this is a property used by marks. + * + * The following temp file parameters allow 256k bytes in the temporary + * file. By changing to the numbers in comments you can get 512k. + * By typedefing line to long (32 bit) integers you could get much more + * space in the temp file with (then) no waste. This would double core + * requirements and would probably require some editor debugging. + */ +#define BLKMSK 0777 /* 01777 */ +#define BNDRY 8 /* 16 */ +#define INCRMT 0200 /* 0100 */ +#define LBTMSK 0770 /* 0760 */ +#define NMBLKS 506 /* 1018 */ +#define OFFBTS 7 /* 6 */ +#define OFFMSK 0177 /* 077 */ +#define SHFT 2 /* 3 */ + +/* + * The editor uses three buffers into the temporary file (ed uses two + * and is very similar). These are two read buffers and one write buffer. + * Basically, the editor deals with the file as a sequence of 512 character + * blocks (BUFSIZ). Each block contains some number of lines (and lines + * can run across block boundaries. + * + * New lines are written into the last block in the temporary file + * which is in core as obuf. When a line is needed which isn't in obuf, + * then it is brought into an input buffer. As there are two, the choice + * is to take the buffer into which the last read (of the two) didn't go. + * Thus this is a 2 buffer LRU replacement strategy. Measurement + * shows that this saves roughly 25% of the buffer reads over a one + * input buffer strategy. Since the editor (on our VAX over 1 week) + * spends (spent) roughly 30% of its time in the system read routine, + * this can be a big help. + */ +bool hitin2; /* Last read hit was ibuff2 not ibuff */ +bool ichang2; /* Have actually changed ibuff2 */ +bool ichanged; /* Have actually changed ibuff */ +short iblock; /* Temp file block number of ibuff (or -1) */ +short iblock2; /* Temp file block number of ibuff2 (or -1) */ +short ninbuf; /* Number useful chars left in input buffer */ +short nleft; /* Number usable chars left in output buffer */ +short oblock; /* Temp file block number of obuff (or -1) */ +short tline; /* Current temp file ptr */ + +char ibuff[BUFSIZ]; +char ibuff2[BUFSIZ]; +char obuff[BUFSIZ]; + +/* + * Structure of the descriptor block which resides + * in the first block of the temporary file and is + * the guiding light for crash recovery. + * + * As the Blocks field below implies, there are temporary file blocks + * devoted to (some) image of the incore array of pointers into the temp + * file. Thus, to recover from a crash we use these indices to get the + * line pointers back, and then use the line pointers to get the text back. + * Except for possible lost lines due to sandbagged I/O, the entire + * file (at the time of the last editor "sync") can be recovered from + * the temp file. + */ + +/* This definition also appears in expreserve.c... beware */ +struct header { + time_t Time; /* Time temp file last updated */ + short Uid; + short Flines; /* Number of lines in file */ + char Savedfile[FNSIZE]; /* The current file name */ + short Blocks[LBLKS]; /* Blocks where line pointers stashed */ +} H; + +#define uid H.Uid +#define flines H.Flines +#define savedfile H.Savedfile +#define blocks H.Blocks diff --git a/src/ex/ex_tty.c b/src/ex/ex_tty.c new file mode 100644 index 0000000000..a5ffa181a0 --- /dev/null +++ b/src/ex/ex_tty.c @@ -0,0 +1,120 @@ +/* Copyright (c) 1979 Regents of the University of California */ +#include "ex.h" +#include "ex_tty.h" + +/* + * Terminal type initialization routines, + * and calculation of flags at entry or after + * a shell escape which may change them. + */ +short ospeed = -1; + +gettmode() +{ + + if (gtty(1, &tty) < 0) + return; + if (ospeed != tty.sg_ospeed) + value(SLOWOPEN) = tty.sg_ospeed < B1200; + ospeed = tty.sg_ospeed; + normf = tty.sg_flags; + UPPERCASE = (tty.sg_flags & LCASE) != 0; + GT = (tty.sg_flags & XTABS) != XTABS; + NONL = (tty.sg_flags & CRMOD) == 0; +} + +char *xPC; +char **sstrs[] = { + &AL, &BC, &CD, &CE, &CL, &CM, &DC, &DL, &DM, &DO, &ED, &EI, &HO, + &IC, &IM, &IP, &LL, &MA, &ND, &xPC, &SE, &SF, &SO, &SR, &TA, &UP, + &VB, &VS, &VE +}; +bool *sflags[] = { + &AM, &BS, &DA, &DB, &EO, &HZ, &IN, &MI, &NC, &OS, &UL, &XN +}; +setterm(type) + char *type; +{ + char *cgoto(); + register int unknown, i; + register int l; + + if (type[0] == 0) + type = "xx"; + unknown = 0; + if (tgetent(genbuf, type) != 1) { + unknown++; + CP(genbuf, "xx|dumb:"); + } + i = LINES = tgetnum("li"); + if (LINES <= 5) + LINES = 24; + if (LINES > 48) + LINES = 48; + l = LINES; + if (ospeed < B1200) + l /= 2; + else if (ospeed < B2400) + l = (l * 2) / 3; + options[WINDOW].ovalue = options[WINDOW].odefault = l - 1; + options[SCROLL].ovalue = options[SCROLL].odefault = l / 2; + COLUMNS = tgetnum("co"); + if (COLUMNS <= 20) + COLUMNS = 1000; + aoftspace = tspace; + zap(); + if (cgoto()[0] == 'O') + CA = 0, CM = 0; + else + CA = 1; + PC = xPC ? xPC[0] : 0; + aoftspace = tspace; + CP(ttytype, longname(genbuf, type)); + if (i <= 0) + LINES = 2; + termreset(); + value(REDRAW) = AL && DL; + value(OPTIMIZE) = !CA && !GT; + if (unknown) + serror("%s: Unknown terminal type", type); +} + +zap() +{ + register char *namp; + register bool **fp; + register char ***sp; + + namp = "ambsdadbeohzinmincosulxn"; + fp = sflags; + do { + *(*fp++) = tgetflag(namp); + namp += 2; + } while (*namp); + namp = "albccdceclcmdcdldmdoedeihoicimipllmandpcsesfsosrtaupvbvsve"; + sp = sstrs; + do { + *(*sp++) = tgetstr(namp, &aoftspace); + namp += 2; + } while (*namp); +} + +char * +longname(bp, def) + register char *bp; + char *def; +{ + register char *cp; + + while (*bp && *bp != ':' && *bp != '|') + bp++; + if (*bp == '|') { + bp++; + cp = bp; + while (*cp && *cp != ':' && *cp != '|') + cp++; + *cp = 0; + return (bp); + } + return (def); +} diff --git a/src/ex/ex_tty.h b/src/ex/ex_tty.h new file mode 100644 index 0000000000..a0f10e60d6 --- /dev/null +++ b/src/ex/ex_tty.h @@ -0,0 +1,97 @@ +/* Copyright (c) 1979 Regents of the University of California */ +/* + * Capabilities from termcap + * + * The description of terminals is a difficult business, and we only + * attempt to summarize the capabilities here; for a full description + * see the paper describing termcap. + * + * Capabilities from termcap are of three kinds - string valued options, + * numeric valued options, and boolean options. The string valued options + * are the most complicated, since they may include padding information, + * which we describe now. + * + * Intelligent terminals often require padding on intelligent operations + * at high (and sometimes even low) speed. This is specified by + * a number before the string in the capability, and has meaning for the + * capabilities which have a P at the front of their comment. + * This normally is a number of milliseconds to pad the operation. + * In the current system which has no true programmible delays, we + * do this by sending a sequence of pad characters (normally nulls, but + * specifiable as "pc"). In some cases, the pad is better computed + * as some number of milliseconds times the number of affected lines + * (to bottom of screen usually, except when terminals have insert modes + * which will shift several lines.) This is specified as '12*' e.g. + * before the capability to say 12 milliseconds per affected whatever + * (currently always line). Capabilities where this makes sense say P*. + */ +char tspace[128]; /* Space for capability strings */ +char *aoftspace; /* Address of tspace for relocation */ + +char *AL; /* P* Add new blank line */ +char *BC; /* Back cursor */ +char *CD; /* P* Clear to end of display */ +char *CE; /* P Clear to end of line */ +char *CL; /* P* Clear screen */ +char *CM; /* P Cursor motion */ +char *DC; /* P* Delete character */ +char *DL; /* P* Delete line sequence */ +char *DM; /* Delete mode (enter) */ +char *DO; /* Down line sequence */ +char *ED; /* End delete mode */ +char *EI; /* End insert mode */ +char *HO; /* Home cursor */ +char *IC; /* P Insert character */ +char *IM; /* Insert mode (give as ':im=:' if 'ic' */ +char *IP; /* P* Insert pad after char ins'd using IM+IE */ +char *LL; /* Quick to last line, column 0 */ +char *MA; /* Control character map for cmd mode */ +char *ND; /* Non-destructive space */ +char PC; /* Pad character */ +char *SE; /* Standout end (may leave space) */ +char *SF; /* P Scroll forwards */ +char *SO; /* Stand out begin (may leave space) */ +char *SR; /* P Scroll backwards */ +char *TA; /* P Tab (other than ^I or with padding) */ +char *UP; /* Upline */ +char *VB; /* Visible bell */ +char *VE; /* Visual end sequence */ +char *VS; /* Visual start sequence */ +bool AM; /* Automatic margins */ +bool BS; /* Backspace works */ +bool CA; /* Cursor addressible */ +bool DA; /* Display may be retained above */ +bool DB; /* Display may be retained below */ +bool EO; /* Can erase overstrikes with ' ' */ +bool GT; /* Gtty indicates tabs */ +bool HZ; /* Hazeltine ~ braindamage */ +bool IN; /* Insert-null blessing */ +bool MI; /* can move in insert mode */ +bool NC; /* No Cr - \r snds \r\n then eats \n (dm2500) */ +bool OS; /* Overstrike works */ +bool UL; /* Underlining works even though !os */ +bool XN; /* A newline gets eaten after wrap (concept) */ + /* X? is reserved for severely nauseous glitches */ + /* If there are enough of these we may need bit masks! */ + +/* + * From the tty modes... + */ +bool NONL; /* Terminal can't hack linefeeds doing a CR */ +bool UPPERCASE; /* Ick! */ +short LINES; /* Number of lines on screen */ +short COLUMNS; +short OCOLUMNS; /* Save COLUMNS for a hack in open mode */ + +short outcol; /* Where the cursor is */ +short outline; + +short destcol; /* Where the cursor should be */ +short destline; + +struct sgttyb tty; /* Always stty/gtty using this one structure */ +bool normtty; /* Have to restor normal mode from normf */ +int normf; /* Restore tty flags to this (someday) */ + +short WBOT; +short WECHO; -- 2.20.1