Path: utzoo!utgpu!cs.utexas.edu!uunet!zephyr.ens.tek.com!tektronix!nosun!qiclab!pdxgate!eecs.cs.pdx.edu!kirkenda From: kirkenda@eecs.cs.pdx.edu (Steve Kirkendall) Newsgroups: alt.sources Subject: ELvis 1.4, part 6 of 8 Message-ID: <830@pdxgate.UUCP> Date: 3 Dec 90 21:33:56 GMT Sender: news@pdxgate.UUCP Reply-To: kirkenda@eecs.cs.pdx.edu (Steve Kirkendall) Organization: Portland State University, Portland, OR Lines: 3871 # --------------------------- cut here ---------------------------- # This is a shar archive. To unpack it, save it to a file, and delete # anything above the "cut here" line. Then run sh on the file. # # -rw-r--r-- 1 kirkenda 9893 Dec 2 17:57 modify.c # -rw-r--r-- 1 kirkenda 10230 Dec 2 17:57 move1.c # -rw-r--r-- 1 kirkenda 4445 Dec 2 17:57 move2.c # -rw-r--r-- 1 kirkenda 2697 Dec 2 17:57 move3.c # -rw-r--r-- 1 kirkenda 3699 Dec 2 17:57 move4.c # -rw-r--r-- 1 kirkenda 3940 Dec 2 17:57 move5.c # -rw-r--r-- 1 kirkenda 14362 Dec 2 17:57 opts.c # -rw-r--r-- 1 kirkenda 2306 Dec 2 17:57 recycle.c # -rw-r--r-- 1 kirkenda 19816 Dec 2 17:57 redraw.c # if test -f modify.c -a "$1" != -f then echo Will not overwrite modify.c else echo Extracting modify.c sed 's/^X//' >modify.c <<\eof X/* modify.c */ X X/* This file contains the low-level file modification functions: X * delete(frommark, tomark) - removes line or portions of lines X * add(frommark, text) - inserts new text X * change(frommark, tomark, text) - delete, then add X */ X X#include "config.h" X#include "vi.h" X X#ifdef DEBUG X# include Xstatic FILE *dbg; X X/*VARARGS1*/ Xdebout(msg, arg1, arg2, arg3, arg4, arg5) X char *msg, *arg1, *arg2, *arg3, *arg4, *arg5; X{ X if (!dbg) X { X dbg = fopen("debug.out", "w"); X setbuf(dbg, (FILE *)0); X } X fprintf(dbg, msg, arg1, arg2, arg3, arg4, arg5); X} X#endif /* DEBUG */ X X/* delete a range of text from the file */ Xvoid delete(frommark, tomark) X MARK frommark; /* first char to be deleted */ X MARK tomark; /* AFTER last char to be deleted */ X{ X int i; /* used to move thru logical blocks */ X REG char *scan; /* used to scan thru text of the blk */ X REG char *cpy; /* used when copying chars */ X BLK *blk; /* a text block */ X long l; /* a line number */ X MARK m; /* a traveling version of frommark */ X X#ifdef DEBUG X debout("delete(%ld.%d, %ld.%d)\n", markline(frommark), markidx(frommark), markline(tomark), markidx(tomark)); X#endif X X /* if not deleting anything, quit now */ X if (frommark == tomark) X { X return; X } X X /* This is a change */ X changes++; X significant = TRUE; X X /* if this is a multi-line change, then we'll have to redraw */ X if (markline(frommark) != markline(tomark)) X { X mustredraw = TRUE; X redrawrange(markline(frommark), markline(tomark), markline(frommark)); X } X X /* adjust marks 'a through 'z and '' as needed */ X l = markline(tomark); X for (i = 0; i < NMARKS; i++) X { X if (mark[i] < frommark) X { X continue; X } X else if (mark[i] < tomark) X { X mark[i] = MARK_UNSET; X } X else if (markline(mark[i]) == l) X { X if (markline(frommark) == l) X { X mark[i] -= markidx(tomark) - markidx(frommark); X } X else X { X mark[i] -= markidx(tomark); X } X } X else X { X mark[i] -= MARK_AT_LINE(l - markline(frommark)); X } X } X X /* Reporting... */ X if (markidx(frommark) == 0 && markidx(tomark) == 0) X { X rptlines = markline(tomark) - markline(frommark); X rptlabel = "deleted"; X } X X /* find the block containing frommark */ X l = markline(frommark); X for (i = 1; lnum[i] < l; i++) X { X } X X /* process each affected block... */ X for (m = frommark; X m < tomark && lnum[i] < INFINITY; X m = MARK_AT_LINE(lnum[i - 1] + 1)) X { X /* fetch the block */ X blk = blkget(i); X X /* find the mark in the block */ X scan = blk->c; X for (l = markline(m) - lnum[i - 1] - 1; l > 0; l--) X { X while (*scan++ != '\n') X { X } X } X scan += markidx(m); X X /* figure out where the changes to this block end */ X if (markline(tomark) > lnum[i]) X { X cpy = blk->c + BLKSIZE; X } X else if (markline(tomark) == markline(m)) X { X cpy = scan - markidx(m) + markidx(tomark); X } X else X { X cpy = scan; X for (l = markline(tomark) - markline(m); X l > 0; X l--) X { X while (*cpy++ != '\n') X { X } X } X cpy += markidx(tomark); X } X X /* delete the stuff by moving chars within this block */ X while (cpy < blk->c + BLKSIZE) X { X *scan++ = *cpy++; X } X while (scan < blk->c + BLKSIZE) X { X *scan++ = '\0'; X } X X /* adjust tomark to allow for lines deleted from this block */ X tomark -= MARK_AT_LINE(lnum[i] + 1 - markline(m)); X X /* if this block isn't empty now, then advance i */ X if (*blk->c) X { X i++; X } X X /* the buffer has changed. Update hdr and lnum. */ X blkdirty(blk); X } X X /* must have at least 1 line */ X if (nlines == 0) X { X blk = blkadd(1); X blk->c[0] = '\n'; X blkdirty(blk); X cursor = MARK_FIRST; X } X} X X X/* add some text at a specific place in the file */ Xvoid add(atmark, newtext) X MARK atmark; /* where to insert the new text */ X char *newtext; /* NUL-terminated string to insert */ X{ X REG char *scan; /* used to move through string */ X REG char *build; /* used while copying chars */ X int addlines; /* number of lines we're adding */ X int lastpart; /* size of last partial line */ X BLK *blk; /* the block to be modified */ X int blkno; /* the logical block# of (*blk) */ X REG char *newptr; /* where new text starts in blk */ X BLK buf; /* holds chars from orig blk */ X BLK linebuf; /* holds part of line that didn't fit */ X BLK *following; /* the BLK following the last BLK */ X int i; X long l; X X#ifdef DEBUG X debout("add(%ld.%d, \"%s\")\n", markline(atmark), markidx(atmark), newtext); X#endif X#ifdef lint X buf.c[0] = 0; X#endif X /* if not adding anything, return now */ X if (!*newtext) X { X return; X } X X /* This is a change */ X changes++; X significant = TRUE; X X /* count the number of lines in the new text */ X for (scan = newtext, lastpart = addlines = 0; *scan; ) X { X if (*scan++ == '\n') X { X addlines++; X lastpart = 0; X } X else X { X lastpart++; X } X } X X /* Reporting... */ X if (lastpart == 0 && markidx(atmark) == 0) X { X rptlines = addlines; X rptlabel = "added"; X } X X /* extract the line# from atmark */ X l = markline(atmark); X X /* if more than 0 lines, then we'll have to redraw the screen */ X if (addlines > 0) X { X mustredraw = TRUE; X if (markidx(atmark) == 0 && lastpart == 0) X { X redrawrange(l, l, l + addlines); X } X else X { X /* make sure the last line gets redrawn -- it was X * split, so its appearance has changed X */ X redrawrange(l, l + 1L, l + addlines + 1L); X } X } X X /* adjust marks 'a through 'z and '' as needed */ X for (i = 0; i < NMARKS; i++) X { X if (mark[i] < atmark) X { X /* earlier line, or earlier in same line: no change */ X continue; X } X else if (markline(mark[i]) > l) X { X /* later line: move down a whole number of lines */ X mark[i] += MARK_AT_LINE(addlines); X } X else X { X /* later in same line */ X if (addlines > 0) X { X /* multi-line add, which split this line: X * move down, and possibly left or right, X * depending on where the split was and how X * much text was inserted after the last \n X */ X mark[i] += MARK_AT_LINE(addlines) + lastpart - markidx(atmark); X } X else X { X /* totally within this line: move right */ X mark[i] += lastpart; X } X } X } X X /* get the block to be modified */ X for (blkno = 1; lnum[blkno] < l && lnum[blkno + 1] < INFINITY; blkno++) X { X } X blk = blkget(blkno); X buf = *blk; X X /* figure out where the new text starts */ X for (newptr = buf.c, l = markline(atmark) - lnum[blkno - 1] - 1; X l > 0; X l--) X { X while (*newptr++ != '\n') X { X } X } X newptr += markidx(atmark); X X /* keep start of old block */ X build = blk->c + (newptr - buf.c); X X /* fill this block (or blocks) from the newtext string */ X while (*newtext) X { X while (*newtext && build < blk->c + BLKSIZE - 1) X { X *build++ = *newtext++; X } X if (*newtext) X { X /* save the excess */ X for (scan = linebuf.c + BLKSIZE; X build > blk->c && build[-1] != '\n'; X ) X { X *--scan = *--build; X } X X /* write the block */ X while (build < blk->c + BLKSIZE) X { X *build++ = '\0'; X } X blkdirty(blk); X X /* add another block */ X blkno++; X blk = blkadd(blkno); X X /* copy in the excess from last time */ X for (build = blk->c; scan < linebuf.c + BLKSIZE; ) X { X *build++ = *scan++; X } X } X } X X /* fill this block(s) from remainder of orig block */ X while (newptr < buf.c + BLKSIZE && *newptr) X { X while (newptr < buf.c + BLKSIZE X && *newptr X && build < blk->c + BLKSIZE - 1) X { X *build++ = *newptr++; X } X if (newptr < buf.c + BLKSIZE && *newptr) X { X /* save the excess */ X for (scan = linebuf.c + BLKSIZE; X build > blk->c && build[-1] != '\n'; X ) X { X *--scan = *--build; X } X X /* write the block */ X while (build < blk->c + BLKSIZE) X { X *build++ = '\0'; X } X blkdirty(blk); X X /* add another block */ X blkno++; X blk = blkadd(blkno); X X /* copy in the excess from last time */ X for (build = blk->c; scan < linebuf.c + BLKSIZE; ) X { X *build++ = *scan++; X } X } X } X X /* see if we can combine our last block with the following block */ X if (lnum[blkno] < nlines && lnum[blkno + 1] - lnum[blkno] < (BLKSIZE >> 6)) X { X /* hey, we probably can! Get the following block & see... */ X following = blkget(blkno + 1); X if (strlen(following->c) + (build - blk->c) < BLKSIZE - 1) X { X /* we can! Copy text from following to blk */ X for (scan = following->c; *scan; ) X { X *build++ = *scan++; X } X while (build < blk->c + BLKSIZE) X { X *build++ = '\0'; X } X blkdirty(blk); X X /* pretend the following was the last blk */ X blk = following; X build = blk->c; X } X } X X /* that last block is dirty by now */ X while (build < blk->c + BLKSIZE) X { X *build++ = '\0'; X } X blkdirty(blk); X} X X X/* change the text of a file */ Xvoid change(frommark, tomark, newtext) X MARK frommark, tomark; X char *newtext; X{ X int i; X long l; X char *text; X BLK *blk; X X#ifdef DEBUG X debout("change(%ld.%d, %ld.%d, \"%s\")\n", markline(frommark), markidx(frommark), markline(tomark), markidx(tomark), newtext); X#endif X X /* optimize for single-character replacement */ X if (frommark + 1 == tomark && newtext[0] && !newtext[1] && newtext[0] != '\n') X { X /* find the block containing frommark */ X l = markline(frommark); X for (i = 1; lnum[i] < l; i++) X { X } X X /* get the block */ X blk = blkget(i); X X /* find the line within the block */ X for (text = blk->c, i = l - lnum[i - 1] - 1; i > 0; text++) X { X if (*text == '\n') X { X i--; X } X } X X /* replace the char */ X text += markidx(frommark); X if (*text == newtext[0]) X { X /* no change was needed - same char */ X return; X } X else if (*text != '\n') X { X /* This is a change */ X changes++; X significant = TRUE; X ChangeText X { X *text = newtext[0]; X blkdirty(blk); X } X return; X } X /* else it is a complex change involving newline... */ X } X X /* couldn't optimize, so do delete & add */ X ChangeText X { X delete(frommark, tomark); X add(frommark, newtext); X rptlabel = "changed"; X } X} eof if test `wc -c move1.c <<\eof X/* move1.c */ X X/* Author: X * Steve Kirkendall X * 14407 SW Teal Blvd. #C X * Beaverton, OR 97005 X * kirkenda@cs.pdx.edu X */ X X X/* This file contains most movement functions */ X X#include "config.h" X#include X#include "vi.h" X X#ifndef isascii X# define isascii(c) !((c) & ~0x7f) X#endif X XMARK m_updnto(m, cnt, cmd) X MARK m; /* movement is relative to this mark */ X long cnt; /* a numeric argument */ X{ X DEFAULT(cmd == 'G' ? nlines : 1L); X X /* move up or down 'cnt' lines */ X switch (cmd) X { X case ('P'&0x1f): X case '-': X case 'k': X m -= MARK_AT_LINE(cnt); X break; X X case 'G': X if (cnt < 1L || cnt > nlines) X { X msg("Only %ld lines", nlines); X return MARK_UNSET; X } X m = MARK_AT_LINE(cnt); X break; X X default: X m += MARK_AT_LINE(cnt); X } X X /* if that left us screwed up, then fail */ X if (m < MARK_FIRST || markline(m) > nlines) X { X return MARK_UNSET; X } X X return m; X} X X/*ARGSUSED*/ XMARK m_right(m, cnt) X MARK m; /* movement is relative to this mark */ X long cnt; /* a numeric argument */ X{ X int idx; /* index of the new cursor position */ X X DEFAULT(1); X X /* move to right, if that's OK */ X pfetch(markline(m)); X idx = markidx(m) + cnt; X if (idx < plen) X { X m += cnt; X } X else X { X return MARK_UNSET; X } X X return m; X} X X/*ARGSUSED*/ XMARK m_left(m, cnt) X MARK m; /* movement is relative to this mark */ X long cnt; /* a numeric argument */ X{ X DEFAULT(1); X X /* move to the left, if that's OK */ X if (markidx(m) >= cnt) X { X m -= cnt; X } X else X { X return MARK_UNSET; X } X X return m; X} X X/*ARGSUSED*/ XMARK m_tocol(m, cnt) X MARK m; /* movement is relative to this mark */ X long cnt; /* a numeric argument */ X{ X char *text; /* text of the line */ X int col; /* column number */ X int idx; /* index into the line */ X X DEFAULT(1); X X /* internally, columns are numbered 0..COLS-1, not 1..COLS */ X cnt--; X X /* if 0, that's easy */ X if (cnt == 0) X { X m &= ~(BLKSIZE - 1); X return m; X } X X /* find that column within the line */ X pfetch(markline(m)); X text = ptext; X for (col = idx = 0; col < cnt && *text; text++, idx++) X { X if (*text == '\t' && !*o_list) X { X col += *o_tabstop; X col -= col % *o_tabstop; X } X else if (UCHAR(*text) < ' ' || *text == '\177') X { X col += 2; X } X#ifndef NO_CHARATTR X else if (text[0] == '\\' && text[1] == 'f' && text[2] && *o_charattr) X { X text += 2; /* plus one more as part of for loop */ X } X#endif X else X { X col++; X } X } X if (!*text) X { X return MARK_UNSET; X } X else X { X m = (m & ~(BLKSIZE - 1)) + idx; X } X return m; X} X X/*ARGSUSED*/ XMARK m_front(m, cnt) X MARK m; /* movement is relative to this mark */ X long cnt; /* a numeric argument (ignored) */ X{ X char *scan; X X /* move to the first non-whitespace character */ X pfetch(markline(m)); X scan = ptext; X m &= ~(BLKSIZE - 1); X while (*scan == ' ' || *scan == '\t') X { X scan++; X m++; X } X X return m; X} X X/*ARGSUSED*/ XMARK m_rear(m, cnt) X MARK m; /* movement is relative to this mark */ X long cnt; /* a numeric argument (ignored) */ X{ X /* Try to move *EXTREMELY* far to the right. It is fervently hoped X * that other code will convert this to a more reasonable MARK before X * anything tries to actually use it. (See adjmove() in vi.c) X */ X return m | (BLKSIZE - 1); X} X X#ifndef NO_SENTENCE X/*ARGSUSED*/ XMARK m_fsentence(m, cnt) X MARK m; /* movement is relative to this mark */ X long cnt; /* a numeric argument */ X{ X REG char *text; X REG long l; X X DEFAULT(1); X X /* get the current line */ X l = markline(m); X pfetch(l); X text = ptext + markidx(m); X X /* for each requested sentence... */ X while (cnt-- > 0) X { X /* search forward for one of [.?!] followed by spaces or EOL */ X do X { X /* wrap at end of line */ X if (!text[0]) X { X if (l >= nlines) X { X return MARK_UNSET; X } X l++; X pfetch(l); X text = ptext; X } X else X { X text++; X } X } while (text[0] != '.' && text[0] != '?' && text[0] != '!' X || text[1] && (text[1] != ' ' || text[2] && text[2] != ' ')); X } X X /* construct a mark for this location */ X m = buildmark(text); X X /* move forward to the first word of the next sentence */ X m = m_fword(m, 1L); X X return m; X} X X/*ARGSUSED*/ XMARK m_bsentence(m, cnt) X MARK m; /* movement is relative to this mark */ X long cnt; /* a numeric argument */ X{ X REG char *text; /* used to scan thru text */ X REG long l; /* current line number */ X int flag; /* have we passed at least one word? */ X X DEFAULT(1); X X /* get the current line */ X l = markline(m); X pfetch(l); X text = ptext + markidx(m); X X /* for each requested sentence... */ X flag = TRUE; X while (cnt-- > 0) X { X /* search backward for one of [.?!] followed by spaces or EOL */ X do X { X /* wrap at beginning of line */ X if (text == ptext) X { X do X { X if (l <= 1) X { X return MARK_UNSET; X } X l--; X pfetch(l); X } while (!*ptext); X text = ptext + plen - 1; X } X else X { X text--; X } X X /* are we moving past a "word"? */ X if (text[0] >= '0') X { X flag = FALSE; X } X } while (flag || text[0] != '.' && text[0] != '?' && text[0] != '!' X || text[1] && (text[1] != ' ' || text[2] && text[2] != ' ')); X } X X /* construct a mark for this location */ X m = buildmark(text); X X /* move to the front of the following sentence */ X m = m_fword(m, 1L); X X return m; X} X#endif X X/*ARGSUSED*/ XMARK m_fparagraph(m, cnt) X MARK m; /* movement is relative to this mark */ X long cnt; /* a numeric argument */ X{ X char *text; X char *pscn; /* used to scan thru value of "paragraphs" option */ X long l; X X DEFAULT(1); X X for (l = markline(m); cnt > 0 && l++ < nlines; ) X { X text = fetchline(l); X if (!*text) X { X cnt--; X } X#ifndef NO_SENTENCE X else if (*text == '.') X { X for (pscn = o_paragraphs; pscn[0] && pscn[1]; pscn += 2) X { X if (pscn[0] == text[1] && pscn[1] == text[2]) X { X cnt--; X break; X } X } X } X#endif X } X if (l <= nlines) X { X m = MARK_AT_LINE(l); X } X else X { X m = MARK_LAST; X } X return m; X} X X/*ARGSUSED*/ XMARK m_bparagraph(m, cnt) X MARK m; /* movement is relative to this mark */ X long cnt; /* a numeric argument */ X{ X char *text; X char *pscn; /* used to scan thru value of "paragraph" option */ X long l; X X DEFAULT(1); X X for (l = markline(m); cnt > 0 && l-- > 1; ) X { X text = fetchline(l); X if (!*text) X { X cnt--; X } X#ifndef NO_SENTENCE X else if (*text == '.') X { X for (pscn = o_paragraphs; pscn[0] && pscn[1]; pscn += 2) X { X if (pscn[0] == text[1] && pscn[1] == text[2]) X { X cnt--; X break; X } X } X } X#endif X } X if (l >= 1) X { X m = MARK_AT_LINE(l); X } X else X { X m = MARK_FIRST; X } X return m; X} X X/*ARGSUSED*/ XMARK m_fsection(m, cnt, key) X MARK m; /* movement is relative to this mark */ X long cnt; /* (ignored) */ X int key; /* second key stroke - must be ']' */ X{ X char *text; X char *sscn; /* used to scan thru value of "sections" option */ X long l; X X /* make sure second key was ']' */ X if (key != ']') X { X return MARK_UNSET; X } X X for (l = markline(m); l++ < nlines; ) X { X text = fetchline(l); X if (*text == '{') X { X break; X } X#ifndef NO_SENTENCE X else if (*text == '.') X { X for (sscn = o_sections; sscn[0] && sscn[1]; sscn += 2) X { X if (sscn[0] == text[1] && sscn[1] == text[2]) X { X goto BreakBreak; X } X } X } X#endif X } XBreakBreak: X if (l <= nlines) X { X m = MARK_AT_LINE(l); X } X else X { X m = MARK_LAST; X } X return m; X} X X/*ARGSUSED*/ XMARK m_bsection(m, cnt, key) X MARK m; /* movement is relative to this mark */ X long cnt; /* (ignored) */ X int key; /* second key stroke - must be '[' */ X{ X char *text; X char *sscn; /* used to scan thru value of "sections" option */ X long l; X X /* make sure second key was '[' */ X if (key != '[') X { X return MARK_UNSET; X } X X for (l = markline(m); l-- > 1; ) X { X text = fetchline(l); X if (*text == '{') X { X break; X } X#ifndef NO_SENTENCE X else if (*text == '.') X { X for (sscn = o_sections; sscn[0] && sscn[1]; sscn += 2) X { X if (sscn[0] == text[1] && sscn[1] == text[2]) X { X goto BreakBreak; X } X } X } X#endif X } XBreakBreak: X if (l >= 1) X { X m = MARK_AT_LINE(l); X } X else X { X m = MARK_FIRST; X } X return m; X} X X X/*ARGSUSED*/ XMARK m_match(m, cnt) X MARK m; /* movement is relative to this mark */ X long cnt; /* a numeric argument (ignored) */ X{ X long l; X REG char *text; X REG char match; X REG char nest; X REG int count; X X /* get the current line */ X l = markline(m); X pfetch(l); X text = ptext + markidx(m); X X /* search forward within line for one of "[](){}" */ X for (match = '\0'; !match && *text; text++) X { X /* tricky way to recognize 'em in ASCII */ X nest = *text; X if ((nest & 0xdf) == ']' || (nest & 0xdf) == '[') X { X match = nest ^ ('[' ^ ']'); X } X else if ((nest & 0xfe) == '(') X { X match = nest ^ ('(' ^ ')'); X } X else X { X match = 0; X } X } X if (!match) X { X return MARK_UNSET; X } X text--; X X /* search forward or backward for match */ X if (match == '(' || match == '[' || match == '{') X { X /* search backward */ X for (count = 1; count > 0; ) X { X /* wrap at beginning of line */ X if (text == ptext) X { X do X { X if (l <= 1L) X { X return MARK_UNSET; X } X l--; X pfetch(l); X } while (!*ptext); X text = ptext + plen - 1; X } X else X { X text--; X } X X /* check the char */ X if (*text == match) X count--; X else if (*text == nest) X count++; X } X } X else X { X /* search forward */ X for (count = 1; count > 0; ) X { X /* wrap at end of line */ X if (!*text) X { X if (l >= nlines) X { X return MARK_UNSET; X } X l++; X pfetch(l); X text = ptext; X } X else X { X text++; X } X X /* check the char */ X if (*text == match) X count--; X else if (*text == nest) X count++; X } X } X X /* construct a mark for this place */ X m = buildmark(text); X return m; X} X X/*ARGSUSED*/ XMARK m_tomark(m, cnt, key) X MARK m; /* movement is relative to this mark */ X long cnt; /* (ignored) */ X int key; /* keystroke - the mark to move to */ X{ X /* mark '' is a special case */ X if (key == '\'' || key == '`') X { X if (mark[26] == MARK_UNSET) X { X return MARK_FIRST; X } X else X { X return mark[26]; X } X } X X /* if not a valid mark number, don't move */ X if (key < 'a' || key > 'z') X { X return MARK_UNSET; X } X X /* return the selected mark -- may be MARK_UNSET */ X if (!mark[key - 'a']) X { X msg("mark '%c is unset", key); X } X return mark[key - 'a']; X} X eof if test `wc -c move2.c <<\eof X/* move2.c */ X X/* Author: X * Steve Kirkendall X * 14407 SW Teal Blvd. #C X * Beaverton, OR 97005 X * kirkenda@cs.pdx.edu X */ X X X/* This function contains the movement functions that perform RE searching */ X X#include "config.h" X#include "vi.h" X#include "regexp.h" X Xextern long atol(); X Xstatic regexp *re; /* compiled version of the pattern to search for */ Xstatic prevsf; /* boolean: previous search direction was forward? */ X XMARK m_nsrch(m) X MARK m; /* where to start searching */ X{ X if (prevsf) X { X m = m_fsrch(m, (char *)0); X prevsf = TRUE; X } X else X { X m = m_bsrch(m, (char *)0); X prevsf = FALSE; X } X return m; X} X XMARK m_Nsrch(m) X MARK m; /* where to start searching */ X{ X if (prevsf) X { X m = m_bsrch(m, (char *)0); X prevsf = TRUE; X } X else X { X m = m_fsrch(m, (char *)0); X prevsf = FALSE; X } X return m; X} X XMARK m_fsrch(m, ptrn) X MARK m; /* where to start searching */ X char *ptrn; /* pattern to search for */ X{ X long l; /* line# of line to be searched */ X char *line; /* text of line to be searched */ X int wrapped;/* boolean: has our search wrapped yet? */ X int pos; /* where we are in the line */ X long delta; /* line offset, for things like "/foo/+1" */ X X /* remember: "previous search was forward" */ X prevsf = TRUE; X X delta = 0L; X if (ptrn && *ptrn) X { X /* locate the closing '/', if any */ X line = parseptrn(ptrn); X if (*line) X { X delta = atol(line); X } X ptrn++; X X /* free the previous pattern */ X if (re) free(re); X X /* compile the pattern */ X re = regcomp(ptrn); X if (!re) X { X return MARK_UNSET; X } X } X else if (!re) X { X msg("No previous expression"); X return MARK_UNSET; X } X X /* search forward for the pattern */ X pos = markidx(m) + 1; X pfetch(markline(m)); X if (pos >= plen) X { X pos = 0; X m = (m | (BLKSIZE - 1)) + 1; X } X wrapped = FALSE; X for (l = markline(m); l != markline(m) + 1 || !wrapped; l++) X { X /* wrap search */ X if (l > nlines) X { X /* if we wrapped once already, then the search failed */ X if (wrapped) X { X break; X } X X /* else maybe we should wrap now? */ X if (*o_wrapscan) X { X l = 0; X wrapped = TRUE; X continue; X } X else X { X break; X } X } X X /* get this line */ X line = fetchline(l); X X /* check this line */ X if (regexec(re, &line[pos], (pos == 0))) X { X /* match! */ X if (wrapped && *o_warn) X msg("(wrapped)"); X if (delta != 0L) X { X l += delta; X if (l < 1 || l > nlines) X { X msg("search offset too big"); X return MARK_UNSET; X } X return m_front(MARK_AT_LINE(l), 0L); X } X return MARK_AT_LINE(l) + (int)(re->startp[0] - line); X } X pos = 0; X } X X /* not found */ X msg(*o_wrapscan ? "Not found" : "Hit bottom without finding RE"); X return MARK_UNSET; X} X XMARK m_bsrch(m, ptrn) X MARK m; /* where to start searching */ X char *ptrn; /* pattern to search for */ X{ X long l; /* line# of line to be searched */ X char *line; /* text of line to be searched */ X int wrapped;/* boolean: has our search wrapped yet? */ X int pos; /* last acceptable idx for a match on this line */ X int last; /* remembered idx of the last acceptable match on this line */ X int try; /* an idx at which we strat searching for another match */ X X /* remember: "previous search was not forward" */ X prevsf = FALSE; X X if (ptrn && *ptrn) X { X /* locate the closing '?', if any */ X line = parseptrn(ptrn); X ptrn++; X X /* free the previous pattern, if any */ X if (re) free(re); X X /* compile the pattern */ X re = regcomp(ptrn); X if (!re) X { X return MARK_UNSET; X } X } X else if (!re) X { X msg("No previous expression"); X return MARK_UNSET; X } X X /* search backward for the pattern */ X pos = markidx(m); X wrapped = FALSE; X for (l = markline(m); l != markline(m) - 1 || !wrapped; l--) X { X /* wrap search */ X if (l < 1) X { X if (*o_wrapscan) X { X l = nlines + 1; X wrapped = TRUE; X continue; X } X else X { X break; X } X } X X /* get this line */ X line = fetchline(l); X X /* check this line */ X if (regexec(re, line, 1) && (int)(re->startp[0] - line) < pos) X { X /* match! now find the last acceptable one in this line */ X do X { X last = (int)(re->startp[0] - line); X try = (int)(re->endp[0] - line); X } while (try > 0 X && regexec(re, &line[try], FALSE) X && (int)(re->startp[0] - line) < pos); X X if (wrapped && *o_warn) X msg("(wrapped)"); X return MARK_AT_LINE(l) + last; X } X pos = BLKSIZE; X } X X /* not found */ X msg(*o_wrapscan ? "Not found" : "Hit top without finding RE"); X return MARK_UNSET; X} X eof if test `wc -c move3.c <<\eof X/* move3.c */ X X/* Author: X * Steve Kirkendall X * 14407 SW Teal Blvd. #C X * Beaverton, OR 97005 X * kirkenda@cs.pdx.edu X */ X X X/* This file contains movement functions that perform character searches */ X X#include "config.h" X#include "vi.h" X X#ifndef NO_CHARSEARCH Xstatic MARK (*prevfwdfn)(); /* function to search in same direction */ Xstatic MARK (*prevrevfn)(); /* function to search in opposite direction */ Xstatic char prev_key; /* sought cvhar from previous [fFtT] */ X XMARK m__ch(m, cnt, cmd) X MARK m; /* current position */ X long cnt; X char cmd; /* command: either ',' or ';' */ X{ X MARK (*tmp)(); X X if (!prevfwdfn) X { X msg("No previous f, F, t, or T command"); X return MARK_UNSET; X } X X if (cmd == ',') X { X m = (*prevrevfn)(m, cnt, prev_key); X X /* Oops! we didn't want to change the prev*fn vars! */ X tmp = prevfwdfn; X prevfwdfn = prevrevfn; X prevrevfn = tmp; X X return m; X } X else X { X return (*prevfwdfn)(m, cnt, prev_key); X } X} X X/* move forward within this line to next occurrence of key */ XMARK m_fch(m, cnt, key) X MARK m; /* where to search from */ X long cnt; X char key; /* what to search for */ X{ X REG char *text; X X DEFAULT(1); X X prevfwdfn = m_fch; X prevrevfn = m_Fch; X prev_key = key; X X pfetch(markline(m)); X text = ptext + markidx(m); X while (cnt-- > 0) X { X do X { X m++; X text++; X } while (*text && *text != key); X } X if (!*text) X { X return MARK_UNSET; X } X return m; X} X X/* move backward within this line to previous occurrence of key */ XMARK m_Fch(m, cnt, key) X MARK m; /* where to search from */ X long cnt; X char key; /* what to search for */ X{ X REG char *text; X X DEFAULT(1); X X prevfwdfn = m_Fch; X prevrevfn = m_fch; X prev_key = key; X X pfetch(markline(m)); X text = ptext + markidx(m); X while (cnt-- > 0) X { X do X { X m--; X text--; X } while (text >= ptext && *text != key); X } X if (text < ptext) X { X return MARK_UNSET; X } X return m; X} X X/* move forward within this line almost to next occurrence of key */ XMARK m_tch(m, cnt, key) X MARK m; /* where to search from */ X long cnt; X char key; /* what to search for */ X{ X /* skip the adjacent char */ X pfetch(markline(m)); X if (plen <= markidx(m)) X { X return MARK_UNSET; X } X m++; X X m = m_fch(m, cnt, key); X if (m == MARK_UNSET) X { X return MARK_UNSET; X } X X prevfwdfn = m_tch; X prevrevfn = m_Tch; X X return m - 1; X} X X/* move backward within this line almost to previous occurrence of key */ XMARK m_Tch(m, cnt, key) X MARK m; /* where to search from */ X long cnt; X char key; /* what to search for */ X{ X /* skip the adjacent char */ X if (markidx(m) == 0) X { X return MARK_UNSET; X } X m--; X X m = m_Fch(m, cnt, key); X if (m == MARK_UNSET) X { X return MARK_UNSET; X } X X prevfwdfn = m_Tch; X prevrevfn = m_tch; X X return m + 1; X} X#endif eof if test `wc -c move4.c <<\eof X/* move4.c */ X X/* Author: X * Steve Kirkendall X * 14407 SW Teal Blvd. #C X * Beaverton, OR 97005 X * kirkenda@cs.pdx.edu X */ X X X/* This file contains movement functions which are screen-relative */ X X#include "config.h" X#include "vi.h" X X/* This moves the cursor to a particular row on the screen */ X/*ARGSUSED*/ XMARK m_row(m, cnt, key) X MARK m; /* the cursor position */ X long cnt; /* the row we'll move to */ X int key; /* the keystroke of this move - H/L/M */ X{ X DEFAULT(1); X X /* calculate destination line based on key */ X cnt--; X switch (key) X { X case 'H': X cnt = topline + cnt; X break; X X case 'M': X cnt = topline + (LINES - 1) / 2; X break; X X case 'L': X cnt = botline - cnt; X break; X } X X /* return the mark of the destination line */ X return MARK_AT_LINE(cnt); X} X X X/* This function repositions the current line to show on a given row */ X/*ARGSUSED*/ XMARK m_z(m, cnt, key) X MARK m; /* the cursor */ X long cnt; /* the line number we're repositioning */ X int key; /* key struck after the z */ X{ X long newtop; X X /* Which line are we talking about? */ X if (cnt < 0 || cnt > nlines) X { X return MARK_UNSET; X } X if (cnt) X { X m = MARK_AT_LINE(cnt); X newtop = cnt; X } X else X { X newtop = markline(m); X } X X /* allow a "window size" number to be entered, but ignore it */ X while (key >= '0' && key <= '9') X { X key = getkey(0); X } X X /* figure out which line will have to be at the top of the screen */ X switch (key) X { X case '\n': X#if OSK X case '\l': X#else X case '\r': X#endif X case '+': X break; X X case '.': X case 'z': X newtop -= LINES / 2; X break; X X case '-': X newtop -= LINES - 1; X break; X X default: X return MARK_UNSET; X } X X /* make the new topline take effect */ X if (newtop >= 1) X { X topline = newtop; X } X else X { X topline = 1L; X } X mustredraw = TRUE; X X /* The cursor doesn't move */ X return m; X} X X X/* This function scrolls the screen. It does this by calling redraw() with X * an off-screen line as the argument. It will move the cursor if necessary X * so that the cursor is on the new screen. X */ X/*ARGSUSED*/ XMARK m_scroll(m, cnt, key) X MARK m; /* the cursor position */ X long cnt; /* for some keys: the number of lines to scroll */ X int key; /* keystroke that causes this movement */ X{ X MARK tmp; /* a temporary mark, used as arg to redraw() */ X X /* adjust cnt, and maybe *o_scroll, depending of key */ X switch (key) X { X case ctrl('F'): X case ctrl('B'): X DEFAULT(1); X mustredraw = TRUE; X cnt = cnt * (LINES - 1) - 1; /* keeps one old line on screen */ X break; X X case ctrl('E'): X case ctrl('Y'): X DEFAULT(1); X break; X X case ctrl('U'): X case ctrl('D'): X if (cnt == 0) /* default */ X { X cnt = *o_scroll; X } X else X { X if (cnt > LINES - 1) X { X cnt = LINES - 1; X } X *o_scroll = cnt; X } X break; X } X X /* scroll up or down, depending on key */ X switch (key) X { X case ctrl('B'): X case ctrl('Y'): X case ctrl('U'): X cnt = topline - cnt; X if (cnt < 1L) X { X cnt = 1L; X m = MARK_FIRST; X } X tmp = MARK_AT_LINE(cnt) + markidx(m); X redraw(tmp, FALSE); X if (markline(m) > botline) X { X m = MARK_AT_LINE(botline); X } X break; X X case ctrl('F'): X case ctrl('E'): X case ctrl('D'): X cnt = botline + cnt; X if (cnt > nlines) X { X cnt = nlines; X m = MARK_LAST; X } X tmp = MARK_AT_LINE(cnt) + markidx(m); X redraw(tmp, FALSE); X if (markline(m) < topline) X { X m = MARK_AT_LINE(topline); X } X break; X } X X /* arrange for ctrl-B and ctrl-F to redraw the smart line */ X if (key == ctrl('B') || key == ctrl('F')) X { X changes++; X X /* also, erase the statusline. This happens naturally for X * the scrolling commands, but the paging commands need to X * explicitly clear the statusline. X */ X msg(""); X } X X return m; X} eof if test `wc -c move5.c <<\eof X/* move5.c */ X X/* Author: X * Steve Kirkendall X * 14407 SW Teal Blvd. #C X * Beaverton, OR 97005 X * kirkenda@cs.pdx.edu X */ X X X/* This file contains the word-oriented movement functions */ X X#include X#include "config.h" X#include "vi.h" X X#ifndef isascii X# define isascii(c) !((c) & ~0x7f) X#endif X X XMARK m_fword(m, cnt, cmd) X MARK m; /* movement is relative to this mark */ X long cnt; /* a numeric argument */ X int cmd; /* either 'w' or 'W' */ X{ X REG long l; X REG char *text; X REG int i; X X DEFAULT(1); X X l = markline(m); X pfetch(l); X text = ptext + markidx(m); X while (cnt-- > 0) /* yes, ASSIGNMENT! */ X { X i = *text++; X X if (cmd == 'W') X { X /* include any non-whitespace */ X while (i && (!isascii(i) || !isspace(i))) X { X i = *text++; X } X } X else if (!isascii(i) || isalnum(i) || i == '_') X { X /* include an alphanumeric word */ X while (i && (!isascii(i) || isalnum(i) || i == '_')) X { X i = *text++; X } X } X else X { X /* include contiguous punctuation */ X while (i && isascii(i) && !isalnum(i) && !isspace(i)) X { X i = *text++; X } X } X X /* include trailing whitespace */ X while (!i || isascii(i) && isspace(i)) X { X /* did we hit the end of this line? */ X if (!i) X { X /* move to next line, if there is one */ X l++; X if (l > nlines) X { X return MARK_UNSET; X } X pfetch(l); X text = ptext; X } X X i = *text++; X } X text--; X } X X /* construct a MARK for this place */ X m = buildmark(text); X return m; X} X X XMARK m_bword(m, cnt, cmd) X MARK m; /* movement is relative to this mark */ X long cnt; /* a numeric argument */ X int cmd; /* either 'b' or 'B' */ X{ X REG long l; X REG char *text; X X DEFAULT(1); X X l = markline(m); X pfetch(l); X text = ptext + markidx(m); X while (cnt-- > 0) /* yes, ASSIGNMENT! */ X { X text--; X X /* include preceding whitespace */ X while (text < ptext || isascii(*text) && isspace(*text)) X { X /* did we hit the end of this line? */ X if (text < ptext) X { X /* move to preceding line, if there is one */ X l--; X if (l <= 0) X { X return MARK_UNSET; X } X pfetch(l); X text = ptext + plen - 1; X } X else X { X text--; X } X } X X if (cmd == 'B') X { X /* include any non-whitespace */ X while (text >= ptext && (!isascii(*text) || !isspace(*text))) X { X text--; X } X } X else if (!isascii(*text) || isalnum(*text) || *text == '_') X { X /* include an alphanumeric word */ X while (text >= ptext && (!isascii(*text) || isalnum(*text) || *text == '_')) X { X text--; X } X } X else X { X /* include contiguous punctuation */ X while (text >= ptext && isascii(*text) && !isalnum(*text) && !isspace(*text)) X { X text--; X } X } X text++; X } X X /* construct a MARK for this place */ X m = buildmark(text); X return m; X} X XMARK m_eword(m, cnt, cmd) X MARK m; /* movement is relative to this mark */ X long cnt; /* a numeric argument */ X int cmd; /* either 'e' or 'E' */ X{ X REG long l; X REG char *text; X REG int i; X X DEFAULT(1); X X l = markline(m); X pfetch(l); X text = ptext + markidx(m); X while (cnt-- > 0) /* yes, ASSIGNMENT! */ X { X text++; X i = *text++; X X /* include preceding whitespace */ X while (!i || isascii(i) && isspace(i)) X { X /* did we hit the end of this line? */ X if (!i) X { X /* move to next line, if there is one */ X l++; X if (l > nlines) X { X return MARK_UNSET; X } X pfetch(l); X text = ptext; X } X X i = *text++; X } X X if (cmd == 'E') X { X /* include any non-whitespace */ X while (i && (!isascii(i) || !isspace(i))) X { X i = *text++; X } X } X else if (!isascii(i) || isalnum(i) || i == '_') X { X /* include an alphanumeric word */ X while (i && (!isascii(i) || isalnum(i) || i == '_')) X { X i = *text++; X } X } X else X { X /* include contiguous punctuation */ X while (i && isascii(i) && !isalnum(i) && !isspace(i)) X { X i = *text++; X } X } X text -= 2; X } X X /* construct a MARK for this place */ X m = buildmark(text); X return m; X} eof if test `wc -c opts.c <<\eof X/* opts.c */ X X/* Author: X * Steve Kirkendall X * 14407 SW Teal Blvd. #C X * Beaverton, OR 97005 X * kirkenda@cs.pdx.edu X */ X X X/* This file contains the code that manages the run-time options -- The X * values that can be modified via the "set" command. X */ X X#include "config.h" X#include "vi.h" X#ifndef NULL X#define NULL (char *)0 X#endif Xextern char *getenv(); X X/* maximum width to permit for strings, including ="" */ X#define MAXWIDTH 20 X X/* These are the default values of all options */ Xchar o_autoindent[1] = {FALSE}; Xchar o_autoprint[1] = {TRUE}; Xchar o_autowrite[1] = {FALSE}; X#ifndef NO_ERRLIST Xchar o_cc[30] = {CC_COMMAND}; X#endif X#ifndef NO_CHARATTR Xchar o_charattr[1] = {FALSE}; X#endif Xchar o_columns[3] = {80, 32, 255}; X#ifndef NO_DIGRAPH Xchar o_digraph[1] = {FALSE}; X#endif Xchar o_directory[30] = TMPDIR; Xchar o_edcompatible[1] = {FALSE}; Xchar o_errorbells[1] = {TRUE}; Xchar o_exrefresh[1] = {TRUE}; X#ifndef NO_DIGRAPH Xchar o_flipcase[80] X# if CS_IBMPC X = {"\207\200\201\232\202\220\204\216\206\217\221\222\224\231\244\245"} X# endif X# if CS_LATIN1 X /* initialized by initopts() */ X# endif X ; X#endif X#ifndef NO_SENTENCE Xchar o_hideformat[1] = {FALSE}; X#endif Xchar o_ignorecase[1] = {FALSE}; X#ifndef NO_EXTENSIONS Xchar o_inputmode[1] = {FALSE}; X#endif Xchar o_keytime[3] = {2, 0, 5}; Xchar o_keywordprg[80] = {KEYWORDPRG}; Xchar o_lines[3] = {25, 2, 50}; /* More lines? Enlarge kbuf */ Xchar o_list[1] = {FALSE}; X#ifndef NO_MAGIC Xchar o_magic[1] = {TRUE}; X#endif X#ifndef NO_ERRLIST Xchar o_make[30] = {MAKE_COMMAND}; X#endif X#ifndef NO_MODELINE Xchar o_modeline[1] = {FALSE}; X#endif X#ifndef NO_SENTENCE Xchar o_paragraphs[30] = "PPppIPLPQP"; X#endif X#if MSDOS Xchar o_pcbios[1] = {TRUE}; X#endif Xchar o_readonly[1] = {FALSE}; Xchar o_report[3] = {5, 1, 127}; Xchar o_scroll[3] = {12, 1, 127}; X#ifndef NO_SENTENCE Xchar o_sections[30] = "NHSHSSSEse"; X#endif Xchar o_shell[60] = SHELL; Xchar o_shiftwidth[3] = {8, 1, 255}; X#ifndef NO_SHOWMATCH Xchar o_showmatch[1] = {FALSE}; X#endif X#ifndef NO_SHOWMODE Xchar o_smd[1] = {FALSE}; X#endif Xchar o_sidescroll[3] = {8, 1, 40}; Xchar o_sync[1] = {NEEDSYNC}; Xchar o_tabstop[3] = {8, 1, 40}; Xchar o_term[30] = "?"; Xchar o_vbell[1] = {TRUE}; Xchar o_warn[1] = {TRUE}; Xchar o_wrapmargin[3] = {0, 0, 255}; Xchar o_wrapscan[1] = {TRUE}; X X X/* The following describes the names & types of all options */ X#define BOOL 0 X#define NUM 1 X#define STR 2 X#define SET 0x01 /* this option has had its value altered */ X#define CANSET 0x02 /* this option can be set at any time */ X#define RCSET 0x06 /* this option can be set in a .exrc file only */ X#define MR 0x40 /* does this option affect the way text is displayed? */ Xstruct X{ X char *name; /* name of an option */ X char *nm; /* short name of an option */ X char type; /* type of an option */ X char flags; /* boolean: has this option been set? */ X char *value; /* value */ X} X opts[] = X{ X /* name type flags redraw value */ X { "autoindent", "ai", BOOL, CANSET , o_autoindent }, X { "autoprint", "ap", BOOL, CANSET , o_autoprint }, X { "autowrite", "aw", BOOL, CANSET , o_autowrite }, X#ifndef NO_ERRLIST X { "cc", "cc", STR, CANSET , o_cc }, X#endif X#ifndef NO_CHARATTR X { "charattr", "ca", BOOL, CANSET | MR, o_charattr }, X#endif X { "columns", "co", NUM, SET , o_columns }, X#ifndef NO_DIGRAPH X { "digraph", "dig", BOOL, CANSET , o_digraph }, X#endif X { "directory", "dir", STR, RCSET , o_directory }, X { "edcompatible","ed", BOOL, CANSET , o_edcompatible }, X { "errorbells", "eb", BOOL, CANSET , o_errorbells }, X { "exrefresh", "er", BOOL, CANSET , o_exrefresh }, X#ifndef NO_DIGRAPH X { "flipcase", "fc", STR, CANSET , o_flipcase }, X#endif X#ifndef NO_SENTENCE X { "hideformat", "hf", BOOL, CANSET | MR, o_hideformat }, X#endif X { "ignorecase", "ic", BOOL, CANSET , o_ignorecase }, X#ifndef NO_EXTENSIONS X { "inputmode", "im", BOOL, CANSET , o_inputmode }, X#endif X { "keytime", "kt", NUM, CANSET , o_keytime }, X { "keywordprg", "kp", STR, CANSET , o_keywordprg }, X { "lines", "ls", NUM, SET , o_lines }, X { "list", "li", BOOL, CANSET | MR, o_list }, X#ifndef NO_MAGIC X { "magic", "ma", BOOL, CANSET , o_magic }, X#endif X#ifndef NO_ERRLIST X { "make", "mk", STR, CANSET , o_make }, X#endif X#ifndef NO_MODELINE X { "modeline", "ml", BOOL, CANSET , o_modeline }, X#endif X#ifndef NO_SENTENCE X { "paragraphs", "pa", STR, CANSET , o_paragraphs }, X#endif X#if MSDOS X { "pcbios", "pc", BOOL, SET , o_pcbios }, X#endif X { "readonly", "ro", BOOL, CANSET , o_readonly }, X { "report", "re", NUM, CANSET , o_report }, X { "scroll", "sc", NUM, CANSET , o_scroll }, X#ifndef NO_SENTENCE X { "sections", "se", STR, CANSET , o_sections }, X#endif X { "shell", "sh", STR, CANSET , o_shell }, X#ifndef NO_SHOWMATCH X { "showmatch", "sm", BOOL, CANSET , o_showmatch }, X#endif X#ifndef NO_SHOWMODE X { "showmode", "smd", BOOL, CANSET , o_smd }, X#endif X { "shiftwidth", "sw", NUM, CANSET , o_shiftwidth }, X { "sidescroll", "ss", NUM, CANSET , o_sidescroll }, X { "sync", "sy", BOOL, CANSET , o_sync }, X { "tabstop", "ts", NUM, CANSET | MR, o_tabstop }, X { "term", "te", STR, SET , o_term }, X { "vbell", "vb", BOOL, CANSET , o_vbell }, X { "warn", "wa", BOOL, CANSET , o_warn }, X { "wrapmargin", "wm", NUM, CANSET , o_wrapmargin }, X { "wrapscan", "ws", BOOL, CANSET , o_wrapscan }, X { NULL, NULL, 0, CANSET, NULL } X}; X X X/* This function initializes certain options from environment variables, etc. */ Xvoid initopts() X{ X char *val; X int i; X X /* set some stuff from environment variables */ X#if MSDOS X if (val = getenv("COMSPEC")) /* yes, ASSIGNMENT! */ X#else X if (val = getenv("SHELL")) /* yes, ASSIGNMENT! */ X#endif X { X strcpy(o_shell, val); X } X X#if ANY_UNIX X if (val = getenv("TERM")) /* yes, ASSIGNMENT! */ X { X strcpy(o_term, val); X } X#endif X#if TOS X val = "vt52"; X strcpy(o_term, val); X#endif X#if MSDOS X if ((val = getenv("TERM")) /* yes, ASSIGNMENT! */ X && strcmp(val, "pcbios")) X { X strcpy(o_term, val); X o_pcbios[0] = 0; X } X else X { X strcpy(o_term, "pcbios"); X o_pcbios[0] = 1; X } X#endif X#if MSDOS || TOS X if ((val = getenv("TMP")) /* yes, ASSIGNMENT! */ X || (val = getenv("TEMP"))) X strcpy(o_directory, val); X#endif X X *o_scroll = LINES / 2 - 1; X X /* disable the vbell option if we don't know how to do a vbell */ X if (!has_VB) X { X for (i = 0; opts[i].value != o_vbell; i++) X { X } X opts[i].flags &= ~CANSET; X *o_vbell = FALSE; X } X X#ifndef NO_DIGRAPH X# ifdef CS_LATIN1 X for (i = 0, val = o_flipcase; i < 32; i++) X { X /* leave out the multiply/divide symbols */ X if (i == 23) X continue; X X /* add upper/lowercase pair */ X *val++ = i + 0xc0; X *val++ = i + 0xe0; X } X *val = '\0'; X# endif /* CS_LATIN1 */ X#endif /* not NO_DIGRAPH */ X} X X/* This function lists the current values of all options */ Xvoid dumpopts(all) X int all; /* boolean: dump all options, or just set ones? */ X{ X#ifndef NO_OPTCOLS X int i, j, k; X char nbuf[4]; /* used for converting numbers to ASCII */ X int widths[5]; /* width of each column, including gap */ X int ncols; /* number of columns */ X int nrows; /* number of options per column */ X int nset; /* number of options to be output */ X int width; /* width of a particular option */ X int todump[50]; /* indicies of options to be dumped */ X X /* step 1: count the number of set options */ X for (nset = i = 0; opts[i].name; i++) X { X if (all || (opts[i].flags & SET)) X { X todump[nset++] = i; X } X } X X /* step two: try to use as many columns as possible */ X for (ncols = (nset > 5 ? 5 : nset); ncols > 1; ncols--) X { X /* how many would go in this column? */ X nrows = (nset + ncols - 1) / ncols; X X /* figure out the width of each column */ X for (i = 0; i < ncols; i++) X { X widths[i] = 0; X for (j = 0, k = nrows * i; j < nrows && k < nset; j++, k++) X { X /* figure out the width of a particular option */ X switch (opts[todump[k]].type) X { X case BOOL: X if (!*opts[todump[k]].value) X width = 2; X else X width = 0; X break; X X case STR: X width = 3 + strlen(opts[todump[k]].value); X if (width > MAXWIDTH) X width = MAXWIDTH; X break; X X case NUM: X width = 4; X break; X } X width += strlen(opts[todump[k]].name); X X /* if this is the widest so far, widen col */ X if (width > widths[i]) X { X widths[i] = width; X } X } X X } X X /* if the total width is narrow enough, then use it */ X for (width = -2, i = 0; i < ncols; i++) X { X width += widths[i] + 2; X } X if (width < COLS - 1) X { X break; X } X } X X /* step 3: output the columns */ X nrows = (nset + ncols - 1) / ncols; X for (i = 0; i < nrows; i++) X { X for (j = 0; j < ncols; j++) X { X /* if we hit the end of the options, quit */ X k = i + j * nrows; X if (k >= nset) X { X break; X } X X /* output this option's value */ X width = 0; X switch (opts[todump[k]].type) X { X case BOOL: X if (!*opts[todump[k]].value) X { X qaddch('n'); X qaddch('o'); X width = 2; X } X qaddstr(opts[todump[k]].name); X width += strlen(opts[todump[k]].name); X break; X X case NUM: X sprintf(nbuf, "%-3d", UCHAR(*opts[todump[k]].value)); X qaddstr(opts[todump[k]].name); X qaddch('='); X qaddstr(nbuf); X width = 4 + strlen(opts[todump[k]].name); X break; X X case STR: X qaddstr(opts[todump[k]].name); X qaddch('='); X qaddch('"'); X strcpy(tmpblk.c, opts[todump[k]].value); X width = 3 + strlen(tmpblk.c); X if (width > MAXWIDTH) X { X width = MAXWIDTH; X strcpy(tmpblk.c + MAXWIDTH - 6, "..."); X } X qaddstr(tmpblk.c); X qaddch('"'); X width += strlen(opts[todump[k]].name); X break; X } X X /* pad the field to the correct size */ X if (k + nrows <= nset) X { X while (width < widths[j] + 2) X { X qaddch(' '); X width++; X } X } X } X addch('\n'); X exrefresh(); X } X#else X int i; X int col; X char nbuf[4]; X X for (i = col = 0; opts[i].name; i++) X { X /* if not set and not all, ignore this option */ X if (!all && !(opts[i].flags & SET)) X { X continue; X } X X /* align this option in one of the columns */ X if (col > 52) X { X addch('\n'); X col = 0; X } X else if (col > 26) X { X while (col < 52) X { X qaddch(' '); X col++; X } X } X else if (col > 0) X { X while (col < 26) X { X qaddch(' '); X col++; X } X } X X switch (opts[i].type) X { X case BOOL: X if (!*opts[i].value) X { X qaddch('n'); X qaddch('o'); X col += 2; X } X qaddstr(opts[i].name); X col += strlen(opts[i].name); X break; X X case NUM: X sprintf(nbuf, "%-3d", UCHAR(*opts[i].value)); X qaddstr(opts[i].name); X qaddch('='); X qaddstr(nbuf); X col += 4 + strlen(opts[i].name); X break; X X case STR: X qaddstr(opts[i].name); X qaddch('='); X qaddch('"'); X qaddstr(opts[i].value); X qaddch('"'); X col += 3 + strlen(opts[i].name) + strlen(opts[i].value); X break; X } X exrefresh(); X } X if (col > 0) X { X addch('\n'); X exrefresh(); X } X#endif X} X X#ifndef NO_MKEXRC X/* This function saves the current configuarion of options to a file */ Xvoid saveopts(fd) X int fd; /* file descriptor to write to */ X{ X int i; X char buf[256], *pos; X X /* write each set options */ X for (i = 0; opts[i].name; i++) X { X /* if unset or unsettable, ignore this option */ X if (!(opts[i].flags & SET) || !(opts[i].flags & CANSET)) X { X continue; X } X X strcpy(buf, "set "); X pos = &buf[4]; X switch (opts[i].type) X { X case BOOL: X if (!*opts[i].value) X { X *pos++='n'; X *pos++='o'; X } X strcpy(pos, opts[i].name); X strcat(pos, "\n"); X break; X X case NUM: X sprintf(pos, "%s=%-3d\n", opts[i].name, *opts[i].value & 0xff); X break; X X case STR: X sprintf(pos, "%s=\"%s\"\n", opts[i].name, opts[i].value); X break; X } X twrite(fd, buf, strlen(buf)); X } X} X#endif X X X/* This function changes the values of one or more options. */ Xvoid setopts(assignments) X char *assignments; /* a string containing option assignments */ X{ X char *name; /* name of variable in assignments */ X char *value; /* value of the variable */ X char *scan; /* used for moving through strings */ X int i, j; X X /* for each assignment... */ X for (name = assignments; *name; ) X { X /* skip whitespace */ X if (*name == ' ' || *name == '\t') X { X name++; X continue; X } X X /* find the value, if any */ X for (scan = name; *scan >= 'a' && *scan <= 'z'; scan++) X { X } X if (*scan == '=') X { X *scan++ = '\0'; X if (*scan == '"') X { X value = ++scan; X while (*scan && *scan != '"') X { X scan++; X } X if (*scan) X { X *scan++ = '\0'; X } X } X else X { X value = scan; X while (*scan && *scan != ' ' && *scan != '\t') X { X scan++; X } X if (*scan) X { X *scan++ = '\0'; X } X } X } X else X { X if (*scan) X { X *scan++ = '\0'; X } X value = NULL; X if (name[0] == 'n' && name[1] == 'o') X { X name += 2; X } X } X X /* find the variable */ X for (i = 0; X opts[i].name && strcmp(opts[i].name, name) && strcmp(opts[i].nm, name); X i++) X { X } X X /* change the variable */ X if (!opts[i].name) X { X msg("invalid option name \"%s\"", name); X } X else if ((opts[i].flags & CANSET) != CANSET) X { X msg("option \"%s\" can't be altered", name); X } X else if ((opts[i].flags & RCSET) != CANSET && nlines >= 1L) X { X msg("option \"%s\" can only be set in a %s file", name, EXRC); X } X else if (value) X { X switch (opts[i].type) X { X case BOOL: X msg("option \"[no]%s\" is boolean", name); X break; X X case NUM: X j = atoi(value); X if (j == 0 && *value != '0') X { X msg("option \"%s\" must have a numeric value", name); X } X else if (j < opts[i].value[1] || j > (opts[i].value[2] & 0xff)) X { X msg("option \"%s\" must have a value between %d and %d", X name, opts[i].value[1], opts[i].value[2] & 0xff); X } X else X { X *opts[i].value = atoi(value); X opts[i].flags |= SET; X } X break; X X case STR: X strcpy(opts[i].value, value); X opts[i].flags |= SET; X break; X } X if (opts[i].flags & MR) X { X mustredraw = TRUE; X } X } X else /* valid option, no value */ X { X if (opts[i].type == BOOL) X { X *opts[i].value = (name[-1] != 'o'); X opts[i].flags |= SET; X if (opts[i].flags & MR) X { X mustredraw = TRUE; X } X } X else X { X msg("option \"%s\" must be given a value", name); X } X } X X /* move on to the next option */ X name = scan; X } X X /* special processing ... */ X X /* if "readonly" then set the READONLY flag for this file */ X if (*o_readonly) X { X setflag(file, READONLY); X } X} eof if test `wc -c recycle.c <<\eof X/* recycle.c */ X X/* Author: X * Steve Kirkendall X * 14407 SW Teal Blvd. #C X * Beaverton, OR 97005 X * kirkenda@cs.pdx.edu X */ X X X/* This file contains the functions perform garbage collection and allocate X * reusable blocks. X */ X X#include "config.h" X#include "vi.h" X X#ifndef NO_RECYCLE X/* this whole file would have be skipped if NO_RECYCLE is defined */ X Xextern long lseek(); X X#define BTST(bitno, byte) ((byte) & (1 << (bitno))) X#define BSET(bitno, byte) ((byte) |= (1 << (bitno))) X#define BCLR(bitno, byte) ((byte) &= ~(1 << (bitno))) X X#define TST(blkno) ((blkno) < MAXBIT ? BTST((blkno) & 7, bitmap[(blkno) >> 3]) : 1) X#define SET(blkno) if ((blkno) < MAXBIT) BSET((blkno) & 7, bitmap[(blkno) >> 3]) X#define CLR(blkno) if ((blkno) < MAXBIT) BCLR((blkno) & 7, bitmap[(blkno) >> 3]) X X/* bitmap of free blocks in first 4096k of tmp file */ Xstatic unsigned char bitmap[512]; X#define MAXBIT (sizeof bitmap << 3) X X/* this function locates all free blocks in the current tmp file */ Xvoid garbage() X{ X int i; X BLK oldhdr; X X /* start by assuming every block is free */ X for (i = 0; i < sizeof bitmap; i++) X { X bitmap[i] = 255; X } X X /* header block isn't free */ X#ifndef lint X CLR(0); X#endif X X /* blocks needed for current hdr aren't free */ X for (i = 1; i < MAXBLKS; i++) X { X CLR(hdr.n[i]); X } X X /* blocks needed for undo version aren't free */ X lseek(tmpfd, 0L, 0); X if (read(tmpfd, &oldhdr, (unsigned)sizeof oldhdr) != sizeof oldhdr) X { X msg("garbage() failed to read oldhdr??"); X for (i = 0; i < sizeof bitmap; i++) X { X bitmap[i] = 0; X } X return; X } X for (i = 1; i < MAXBLKS; i++) X { X CLR(oldhdr.n[i]); X } X X /* blocks needed for cut buffers aren't free */ X for (i = cutneeds(&oldhdr) - 1; i >= 0; i--) X { X CLR(oldhdr.n[i]); X } X} X X/* This function allocates the first available block in the tmp file */ Xlong allocate() X{ X int i; X long offset; X X /* search for the first byte with a free bit set */ X for (i = 0; i < sizeof bitmap && bitmap[i] == 0; i++) X { X } X X /* if we hit the end of the bitmap, return the end of the file */ X if (i == sizeof bitmap) X { X offset = lseek(tmpfd, 0L, 2); X } X else /* compute the offset for the free block */ X { X for (i <<= 3; TST(i) == 0; i++) X { X } X offset = (long)i * (long)BLKSIZE; X X /* mark the block as "allocated" */ X CLR(i); X } X X return offset; X} X X#endif eof if test `wc -c redraw.c <<\eof X/* redraw.c */ X X/* Author: X * Steve Kirkendall X * 14407 SW Teal Blvd. #C X * Beaverton, OR 97005 X * kirkenda@cs.pdx.edu X */ X X X/* This file contains functions that draw text on the screen. The major entry X * points are: X * redrawrange() - called from modify.c to give hints about what parts X * of the screen need to be redrawn. X * redraw() - redraws the screen (or part of it) and positions X * the cursor where it belongs. X * idx2col() - converts a markidx() value to a logical column number. X */ X X#include "config.h" X#include "vi.h" X X/* This variable contains the line number that smartdrawtext() knows best */ Xstatic long smartlno; X X/* This function remebers where changes were made, so that the screen can be X * redraw in a more efficient manner. X */ Xstatic long redrawafter; /* line# of first line that must be redrawn */ Xstatic long preredraw; /* line# of last line changed, before change */ Xstatic long postredraw; /* line# of last line changed, after change */ Xvoid redrawrange(after, pre, post) X long after; /* lower bound of redrawafter */ X long pre; /* upper bound of preredraw */ X long post; /* upper bound of postredraw */ X{ X if (after == redrawafter) X { X /* multiple insertions/deletions at the same place -- combine X * them X */ X preredraw -= (post - pre); X if (postredraw < post) X { X preredraw += (post - postredraw); X postredraw = post; X } X if (redrawafter > preredraw) X { X redrawafter = preredraw; X } X if (redrawafter < 1L) X { X redrawafter = 0L; X preredraw = postredraw = INFINITY; X } X } X else if (postredraw > 0L) X { X /* multiple changes in different places -- redraw everything X * after "after". X */ X postredraw = preredraw = INFINITY; X if (after < redrawafter) X redrawafter = after; X } X else X { X /* first change */ X redrawafter = after; X preredraw = pre; X postredraw = post; X } X} X X X#ifndef NO_CHARATTR X/* see if a given line uses character attribute strings */ Xstatic int hasattr(lno, text) X long lno; /* the line# of the cursor */ X REG char *text; /* the text of the line, from fetchline */ X{ X static long plno; /* previous line number */ X static long chgs; /* previous value of changes counter */ X static int panswer;/* previous answer */ X char *scan; X X /* if charattr is off, then the answer is "no, it doesn't" */ X if (!*o_charattr) X { X chgs = 0; /* <- forces us to check if charattr is later set */ X return FALSE; X } X X /* if we already know the answer, return it... */ X if (lno == plno && chgs == changes) X { X return panswer; X } X X /* get the line & look for "\fX" */ X if (!text[0] || !text[1] || !text[2]) X { X panswer = FALSE; X } X else X { X for (scan = text; scan[2] && !(scan[0] == '\\' && scan[1] == 'f'); scan++) X { X } X panswer = (scan[2] != '\0'); X } X X /* save the results */ X plno = lno; X chgs = changes; X X /* return the results */ X return panswer; X} X#endif X X X X/* This function converts a MARK to a column number. It doesn't automatically X * adjust for leftcol; that must be done by the calling function X */ Xint idx2col(curs, text, inputting) X MARK curs; /* the line# & index# of the cursor */ X REG char *text; /* the text of the line, from fetchline */ X int inputting; /* boolean: called from input() ? */ X{ X static MARK pcursor;/* previous cursor, for possible shortcut */ X static MARK pcol; /* column number for pcol */ X static long chgs; /* previous value of changes counter */ X REG int col; /* used to count column numbers */ X REG int idx; /* used to count down the index */ X REG int i; X X /* for now, assume we have to start counting at the left edge */ X col = 0; X idx = markidx(curs); X X /* if the file hasn't changed & line number is the same & it has no X * embedded character attribute strings, can we do shortcuts? X */ X if (chgs == changes X && !((curs ^ pcursor) & ~(BLKSIZE - 1)) X#ifndef NO_CHARATTR X && !hasattr(markline(curs), text) X#endif X ) X { X /* no movement? */ X if (curs == pcursor) X { X /* return the column of the char; for tabs, return its last column */ X if (text[idx] == '\t' && !inputting && !*o_list) X { X return pcol + *o_tabstop - (pcol % *o_tabstop) - 1; X } X else X { X return pcol; X } X } X X /* movement to right? */ X if (curs > pcursor) X { X /* start counting from previous place */ X col = pcol; X idx = markidx(curs) - markidx(pcursor); X text += markidx(pcursor); X } X } X X /* count over to the char after the idx position */ X while (idx > 0 && (i = *text)) /* yes, ASSIGNMENT! */ X { X if (i == '\t' && !*o_list) X { X col += *o_tabstop; X col -= col % *o_tabstop; X } X else if (i >= '\0' && i < ' ' || i == '\177') X { X col += 2; X } X#ifndef NO_CHARATTR X else if (i == '\\' && text[1] == 'f' && text[2] && *o_charattr) X { X text += 2; /* plus one more at bottom of loop */ X idx -= 2; X } X#endif X else X { X col++; X } X text++; X idx--; X } X X /* save stuff to speed next call */ X pcursor = curs; X pcol = col; X chgs = changes; X X /* return the column of the char; for tabs, return its last column */ X if (*text == '\t' && !inputting && !*o_list) X { X return col + *o_tabstop - (col % *o_tabstop) - 1; X } X else X { X return col; X } X} X X X/* This function is similar to idx2col except that it takes care of sideways X * scrolling - for the given line, at least. X */ Xint mark2phys(m, text, inputting) X MARK m; /* a mark to convert */ X char *text; /* the line that m refers to */ X int inputting; /* boolean: caled from input() ? */ X{ X int i; X X i = idx2col(m, text, inputting); X while (i < leftcol) X { X leftcol -= *o_sidescroll; X mustredraw = TRUE; X redrawrange(1L, INFINITY, INFINITY); X qaddch('\r'); X /* drawtext(text); */ X } X while (i > rightcol) X { X leftcol += *o_sidescroll; X mustredraw = TRUE; X redrawrange(1L, INFINITY, INFINITY); X qaddch('\r'); X /* drawtext(text); */ X } X physcol = i - leftcol; X physrow = markline(m) - topline; X X return physcol; X} X X/* This function draws a single line of text on the screen. The screen's X * cursor is assumed to be located at the leftmost column of the appropriate X * row. X */ Xstatic void drawtext(text, clr) X REG char *text; /* the text to draw */ X int clr; /* boolean: do a clrtoeol? */ X{ X REG int col; /* column number */ X REG int i; X REG int tabstop; /* *o_tabstop */ X REG int limitcol; /* leftcol or leftcol + COLS */ X int abnormal; /* boolean: charattr != A_NORMAL? */ X X#ifndef NO_SENTENCE X /* if we're hiding format lines, and this is one of them, then hide it */ X if (*o_hideformat && *text == '.') X { X clrtoeol(); X#if OSK X qaddch('\l'); X#else X qaddch('\n'); X#endif X return; X } X#endif X X /* move some things into registers... */ X limitcol = leftcol; X tabstop = *o_tabstop; X abnormal = FALSE; X X#ifndef CRUNCH X if (clr) X clrtoeol(); X#endif X /* skip stuff that was scrolled off left edge */ X for (col = 0; X (i = *text) && col < limitcol; /* yes, ASSIGNMENT! */ X text++) X { X if (i == '\t' && !*o_list) X { X col = col + tabstop - (col % tabstop); X } X else if (i >= 0 && i < ' ' || i == '\177') X { X col += 2; X } X#ifndef NO_CHARATTR X else if (i == '\\' && text[1] == 'f' && text[2] && *o_charattr) X { X text += 2; /* plus one more as part of "for" loop */ X X /* since this attribute might carry over, we need it */ X switch (*text) X { X case 'R': X case 'P': X attrset(A_NORMAL); X abnormal = FALSE; X break; X X case 'B': X attrset(A_BOLD); X abnormal = TRUE; X break; X X case 'U': X attrset(A_UNDERLINE); X abnormal = TRUE; X break; X X case 'I': X attrset(A_ALTCHARSET); X abnormal = TRUE; X break; X } X } X#endif X else X { X col++; X } X } X X /* adjust for control char that was partially visible */ X while (col > limitcol) X { X qaddch(' '); X limitcol++; X } X X /* now for the visible characters */ X for (limitcol = leftcol + COLS; X (i = *text) && col < limitcol; X text++) X { X if (i == '\t' && !*o_list) X { X i = col + tabstop - (col % tabstop); X if (i < limitcol) X { X#ifdef CRUNCH X if (!clr && has_PT && !((i - leftcol) & 7)) X#else X if (has_PT && !((i - leftcol) & 7)) X#endif X { X do X { X qaddch('\t'); X col += 8; /* not exact! */ X } while (col < i); X col = i; /* NOW it is exact */ X } X else X { X do X { X qaddch(' '); X col++; X } while (col < i); X } X } X else /* tab ending after screen? next line! */ X { X col = limitcol; X if (has_AM) X { X addch('\n'); /* GB */ X } X } X } X else if (i >= 0 && i < ' ' || i == '\177') X { X col += 2; X qaddch('^'); X if (col <= limitcol) X { X qaddch(i ^ '@'); X } X } X#ifndef NO_CHARATTR X else if (i == '\\' && text[1] == 'f' && text[2] && *o_charattr) X { X text += 2; /* plus one more as part of "for" loop */ X switch (*text) X { X case 'R': X case 'P': X attrset(A_NORMAL); X abnormal = FALSE; X break; X X case 'B': X attrset(A_BOLD); X abnormal = TRUE; X break; X X case 'U': X attrset(A_UNDERLINE); X abnormal = TRUE; X break; X X case 'I': X attrset(A_ALTCHARSET); X abnormal = TRUE; X break; X } X } X#endif X else X { X col++; X qaddch(i); X } X } X X /* get ready for the next line */ X#ifndef NO_CHARATTR X if (abnormal) X { X attrset(A_NORMAL); X } X#endif X if (*o_list && col < limitcol) X { X qaddch('$'); X col++; X } X#ifdef CRUNCH X if (clr && col < limitcol) X { X clrtoeol(); X } X#endif X if (!has_AM || col < limitcol) X { X addch('\n'); X } X} X X X#ifndef CRUNCH Xstatic void nudgecursor(same, scan, new, lno) X int same; /* number of chars to be skipped over */ X char *scan; /* where the same chars end */ X char *new; /* where the visible part of the line starts */ X long lno; /* line number of this line */ X{ X if (same > 0) X { X if (same < 5) X { X /* move the cursor by overwriting */ X while (same > 0) X { X qaddch(scan[-same]); X same--; X } X } X else X { X /* move the cursor by calling move() */ X move((int)(lno - topline), (int)(scan - new)); X } X } X} X#endif /* not CRUNCH */ X X/* This function draws a single line of text on the screen, possibly with X * some cursor optimization. The cursor is repositioned before drawing X * begins, so its position before doesn't really matter. X */ Xstatic void smartdrawtext(text, lno) X REG char *text; /* the text to draw */ X long lno; /* line number of the text */ X{ X#ifdef CRUNCH X move((int)(lno - topline), 0); X drawtext(text, TRUE); X#else /* not CRUNCH */ X static char old[256]; /* how the line looked last time */ X char new[256]; /* how it looks now */ X char *build; /* used to put chars into new[] */ X char *scan; /* used for moving thru new[] or old[] */ X char *end; /* last non-blank changed char */ X char *shift; /* used to insert/delete chars */ X int same; /* length of a run of unchanged chars */ X int limitcol; X int col; X int i; X X# ifndef NO_CHARATTR X /* if this line has attributes, do it the dumb way instead */ X if (hasattr(lno, text)) X { X move((int)(lno - topline), 0); X drawtext(text, TRUE); X return; X } X# endif X# ifndef NO_SENTENCE X /* if this line is a format line, & we're hiding format lines, then X * let the dumb drawtext() function handle it X */ X if (*o_hideformat && *text == '.') X { X move((int)(lno - topline), 0); X drawtext(text, TRUE); X return; X } X# endif X X /* skip stuff that was scrolled off left edge */ X limitcol = leftcol; X for (col = 0; X (i = *text) && col < limitcol; /* yes, ASSIGNMENT! */ X text++) X { X if (i == '\t' && !*o_list) X { X col = col + *o_tabstop - (col % *o_tabstop); X } X else if (i >= 0 && i < ' ' || i == '\177') X { X col += 2; X } X else X { X col++; X } X } X X /* adjust for control char that was partially visible */ X build = new; X while (col > limitcol) X { X *build++ = ' '; X limitcol++; X } X X /* now for the visible characters */ X for (limitcol = leftcol + COLS; X (i = *text) && col < limitcol; X text++) X { X if (i == '\t' && !*o_list) X { X i = col + *o_tabstop - (col % *o_tabstop); X while (col < i && col < limitcol) X { X *build++ = ' '; X col++; X } X } X else if (i >= 0 && i < ' ' || i == '\177') X { X col += 2; X *build++ = '^'; X if (col <= limitcol) X { X *build++ = (i ^ '@'); X } X } X else X { X col++; X *build++ = i; X } X } X if (col < limitcol && *o_list) X { X *build++ = '$'; X col++; X } X end = build; X while (col < limitcol) X { X *build++ = ' '; X col++; X } X X /* locate the last non-blank character */ X while (end > new && end[-1] == ' ') X { X end--; X } X X /* can we optimize the displaying of this line? */ X if (lno != smartlno) X { X /* nope, can't optimize - different line */ X move((int)(lno - topline), 0); X for (scan = new, build = old; scan < end; ) X { X qaddch(*scan); X *build++ = *scan++; X } X if (end < new + COLS) X { X clrtoeol(); X while (build < old + COLS) X { X *build++ = ' '; X } X } X smartlno = lno; X return; X } X X /* skip any initial unchanged characters */ X for (scan = new, build = old; scan < end && *scan == *build; scan++, build++) X { X } X move((int)(lno - topline), (int)(scan - new)); X X /* The in-between characters must be changed */ X same = 0; X while (scan < end) X { X /* is this character a match? */ X if (scan[0] == build[0]) X { X same++; X } X else /* do we want to insert? */ X if (scan < end - 1 && scan[1] == build[0] && (has_IC || has_IM)) X { X nudgecursor(same, scan, new, lno); X same = 0; X X insch(*scan); X for (shift = old + COLS; --shift > build; ) X { X shift[0] = shift[-1]; X } X *build = *scan; X } X else /* do we want to delete? */ X if (build < old + COLS - 1 && scan[0] == build[1] && has_DC) X { X nudgecursor(same, scan, new, lno); X same = 0; X X delch(); X same++; X for (shift = build; shift < old + COLS - 1; shift++) X { X shift[0] = shift[1]; X } X *shift = ' '; X } X else /* we must overwrite */ X { X nudgecursor(same, scan, new, lno); X same = 0; X X addch(*scan); X *build = *scan; X } X X build++; X scan++; X } X X /* maybe clear to EOL */ X while (build < old + COLS && *build == ' ') X { X build++; X } X if (build < old + COLS) X { X nudgecursor(same, scan, new, lno); X same = 0; X X clrtoeol(); X while (build < old + COLS) X { X *build++ = ' '; X } X } X#endif /* not CRUNCH */ X} X X X/* This function is used in visual mode for drawing the screen (or just parts X * of the screen, if that's all thats needed). It also takes care of X * scrolling. X */ Xvoid redraw(curs, inputting) X MARK curs; /* where to leave the screen's cursor */ X int inputting; /* boolean: being called from input() ? */ X{ X char *text; /* a line of text to display */ X static long chgs; /* previous changes level */ X long l; X int i; X X /* if curs == MARK_UNSET, then we should reset internal vars */ X if (curs == MARK_UNSET) X { X if (topline < 1 || topline > nlines) X { X topline = 1L; X } X else X { X move(LINES - 1, 0); X clrtoeol(); X } X leftcol = 0; X mustredraw = TRUE; X redrawafter = INFINITY; X preredraw = 0L; X postredraw = 0L; X chgs = 0; X smartlno = 0L; X return; X } X X /* figure out which column the cursor will be in */ X l = markline(curs); X text = fetchline(l); X mark2phys(curs, text, inputting); X X /* adjust topline, if necessary, to get the cursor on the screen */ X if (l >= topline && l <= botline) X { X /* it is on the screen already */ X X /* if the file was changed but !mustredraw, then redraw line */ X if (chgs != changes && !mustredraw) X { X smartdrawtext(text, l); X } X } X else if (l < topline && l > topline - LINES && (has_SR || has_AL)) X { X /* near top - scroll down */ X if (!mustredraw) X { X move(0,0); X while (l < topline) X { X topline--; X if (has_SR) X { X do_SR(); X } X else X { X insertln(); X } X text = fetchline(topline); X drawtext(text, FALSE); X do_UP(); X } X X /* blank out the last line */ X move(LINES - 1, 0); X clrtoeol(); X } X else X { X topline = l; X redrawafter = INFINITY; X preredraw = 0L; X postredraw = 0L; X } X } X else if (l > topline && l < botline + LINES) X { X /* near bottom -- scroll up */ X if (!mustredraw X#if 1 X || redrawafter == preredraw && preredraw == botline && postredraw == l X#endif X ) X { X move(LINES - 1,0); X clrtoeol(); X while (l > botline) X { X topline++; /* <-- also adjusts botline */ X text = fetchline(botline); X drawtext(text, FALSE); X } X mustredraw = FALSE; X } X else X { X topline = l - (LINES - 2); X redrawafter = INFINITY; X preredraw = 0L; X postredraw = 0L; X } X } X else X { X /* distant line - center it & force a redraw */ X topline = l - (LINES / 2) - 1; X if (topline < 1) X { X topline = 1; X } X mustredraw = TRUE; X redrawafter = INFINITY; X preredraw = 0L; X postredraw = 0L; X } X X /* Now... do we really have to redraw? */ X if (mustredraw) X { X /* If redrawfter (and friends) aren't set, assume we should X * redraw everything. X */ X if (redrawafter == INFINITY) X { X redrawafter = 0L; X preredraw = postredraw = INFINITY; X } X X /* adjust smartlno to correspond with inserted/deleted lines */ X if (smartlno >= redrawafter) X { X if (smartlno < preredraw) X { X smartlno = 0L; X } X else X { X smartlno += (postredraw - preredraw); X } X } X X /* should we insert some lines into the screen? */ X if (preredraw < postredraw && preredraw <= botline) X { X /* lines were inserted into the file */ X X /* decide where insertion should start */ X if (preredraw < topline) X { X l = topline; X } X else X { X l = preredraw; X } X X /* insert the lines... maybe */ X if (l + postredraw - preredraw > botline || !has_AL) X { X /* Whoa! a whole screen full - just redraw */ X preredraw = postredraw = INFINITY; X } X else X { X /* really insert 'em */ X move((int)(l - topline), 0); X for (i = postredraw - preredraw; i > 0; i--) X { X insertln(); X } X X /* NOTE: the contents of those lines will be X * drawn as part of the regular redraw loop. X */ X X /* clear the last line */ X move(LINES - 1, 0); X clrtoeol(); X } X } X X /* do we want to delete some lines from the screen? */ X if (preredraw > postredraw && postredraw <= botline) X { X if (preredraw > botline || !has_DL) X { X postredraw = preredraw = INFINITY; X } X else /* we'd best delete some lines from the screen */ X { X /* clear the last line, so it doesn't look X * ugly as it gets pulled up into the screen X */ X move(LINES - 1, 0); X clrtoeol(); X X /* delete the lines */ X move((int)(postredraw - topline), 0); X for (l = postredraw; X l < preredraw && l <= botline; X l++) X { X deleteln(); X } X X /* draw the lines that are now newly visible X * at the bottom of the screen X */ X i = LINES - 1 + (postredraw - preredraw); X move(i, 0); X for (l = topline + i; l <= botline; l++) X { X /* clear this line */ X clrtoeol(); X X /* draw the line, or ~ for non-lines */ X if (l <= nlines) X { X text = fetchline(l); X drawtext(text, FALSE); X } X else X { X addstr("~\n"); X } X } X } X } X X /* redraw the current line */ X l = markline(curs); X pfetch(l); X smartdrawtext(ptext, l); X X /* decide where we should start redrawing from */ X if (redrawafter < topline) X { X l = topline; X } X else X { X l = redrawafter; X } X move((int)(l - topline), 0); X X /* draw the other lines */ X for (; l <= botline && l < postredraw; l++) X { X /* we already drew the current line, so skip it now */ X if (l == smartlno) X { X#if OSK X qaddch('\l'); X#else X qaddch('\n'); X#endif X continue; X } X X /* draw the line, or ~ for non-lines */ X if (l <= nlines) X { X text = fetchline(l); X drawtext(text, TRUE); X } X else X { X qaddch('~'); X clrtoeol(); X addch('\n'); X } X } X X mustredraw = FALSE; X } X X /* force total (non-partial) redraw next time if not set */ X redrawafter = INFINITY; X preredraw = 0L; X postredraw = 0L; X X /* move the cursor to where it belongs */ X move((int)(markline(curs) - topline), physcol); X wqrefresh(stdscr); X X chgs = changes; X} eof if test `wc -c