kirkenda@eecs.cs.pdx.edu (Steve Kirkendall) (08/25/90)
Archive-name: elvis1.3/part4 #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create: # modify.c # move1.c # move2.c # move3.c # move4.c # move5.c # nomagic.c # opts.c # pc.c # recycle.c # redraw.c # This archive created: Fri Aug 24 10:29:57 1990 export PATH; PATH=/bin:/usr/bin:$PATH if test -f 'modify.c' then echo shar: "will not over-write existing file 'modify.c'" else cat << \SHAR_EOF > 'modify.c' /* modify.c */ /* This file contains the low-level file modification functions: * delete(frommark, tomark) - removes line or portions of lines * add(frommark, text) - inserts new text * change(frommark, tomark, text) - delete, then add */ #include "config.h" #include "vi.h" /* delete a range of text from the file */ delete(frommark, tomark) MARK frommark; /* first char to be deleted */ MARK tomark; /* AFTER last char to be deleted */ { int i; /* used to move thru logical blocks */ register char *scan; /* used to scan thru text of the blk */ register char *cpy; /* used when copying chars */ BLK *blk; /* a text block */ long l; /* a line number */ MARK m; /* a traveling version of frommark */ /* if not deleting anything, quit now */ if (frommark == tomark) { return; } /* This is a change */ changes++; /* if this is a multi-line change, then we'll have to redraw */ if (markline(frommark) != markline(tomark)) { mustredraw = TRUE; redrawrange(markline(frommark), markline(tomark), markline(frommark)); } /* adjust marks 'a through 'z and '' as needed */ l = markline(tomark); for (i = 0; i < NMARKS; i++) { if (mark[i] < frommark) { continue; } else if (mark[i] < tomark) { mark[i] = MARK_UNSET; } else if (markline(mark[i]) == l) { if (markline(frommark) == l) { mark[i] -= markidx(tomark) - markidx(frommark); } else { mark[i] -= markidx(tomark); } } else { mark[i] -= MARK_AT_LINE(l - markline(frommark)); } } /* Reporting... */ if (markidx(frommark) == 0 && markidx(tomark) == 0) { rptlines = markline(tomark) - markline(frommark); rptlabel = "deleted"; } /* find the block containing frommark */ l = markline(frommark); for (i = 1; lnum[i] < l; i++) { } /* process each affected block... */ for (m = frommark; m < tomark && lnum[i] < INFINITY; m = MARK_AT_LINE(lnum[i - 1] + 1)) { /* fetch the block */ blk = blkget(i); /* find the mark in the block */ scan = blk->c; for (l = markline(m) - lnum[i - 1] - 1; l > 0; l--) { while (*scan++ != '\n') { } } scan += markidx(m); /* figure out where the changes to this block end */ if (markline(tomark) > lnum[i]) { cpy = blk->c + BLKSIZE; } else if (markline(tomark) == markline(m)) { cpy = scan - markidx(m) + markidx(tomark); } else { cpy = scan; for (l = markline(tomark) - markline(m); l > 0; l--) { while (*cpy++ != '\n') { } } cpy += markidx(tomark); } /* delete the stuff by moving chars within this block */ while (cpy < blk->c + BLKSIZE) { *scan++ = *cpy++; } while (scan < blk->c + BLKSIZE) { *scan++ = '\0'; } /* adjust tomark to allow for lines deleted from this block */ tomark -= MARK_AT_LINE(lnum[i] + 1 - markline(m)); /* if this block isn't empty now, then advance i */ if (*blk->c) { i++; } /* the buffer has changed. Update hdr and lnum. */ blkdirty(blk); } /* must have at least 1 line */ if (nlines == 0) { blk = blkadd(1); blk->c[0] = '\n'; blkdirty(blk); cursor = MARK_FIRST; } } /* add some text at a specific place in the file */ add(atmark, newtext) MARK atmark; /* where to insert the new text */ char *newtext; /* NUL-terminated string to insert */ { register char *scan; /* used to move through string */ register char *build; /* used while copying chars */ int addlines; /* number of lines we're adding */ int lastpart; /* size of last partial line */ BLK *blk; /* the block to be modified */ int blkno; /* the logical block# of (*blk) */ register char *newptr; /* where new text starts in blk */ BLK buf; /* holds chars from orig blk */ BLK linebuf; /* holds part of line that didn't fit */ BLK *following; /* the BLK following the last BLK */ int i; long l; /* if not adding anything, return now */ if (!*newtext) { return; } /* This is a change */ changes++; /* count the number of lines in the new text */ for (scan = newtext, lastpart = addlines = 0; *scan; ) { if (*scan++ == '\n') { addlines++; lastpart = 0; } else { lastpart++; } } /* Reporting... */ if (lastpart == 0 && markidx(atmark) == 0) { rptlines = addlines; rptlabel = "added"; } /* extract the line# from atmark */ l = markline(atmark); /* if more than 0 lines, then we'll have to redraw the screen */ if (addlines > 0) { mustredraw = TRUE; if (markidx(atmark) == 0) { redrawrange(l, l, l + addlines); } else { /* make sure the last line gets redrawn -- it was * split, so its appearance has changed */ redrawrange(l, l + 1L, l + addlines + 1L); } } /* adjust marks 'a through 'z and '' as needed */ for (i = 0; i < NMARKS; i++) { if (mark[i] < atmark) { continue; } else if (markline(mark[i]) > l) { mark[i] += MARK_AT_LINE(addlines); } else { mark[i] += MARK_AT_LINE(addlines) + lastpart; } } /* get the block to be modified */ for (blkno = 1; lnum[blkno] < l && lnum[blkno + 1] < INFINITY; blkno++) { } blk = blkget(blkno); buf = *blk; /* figure out where the new text starts */ for (newptr = buf.c, l = markline(atmark) - lnum[blkno - 1] - 1; l > 0; l--) { while (*newptr++ != '\n') { } } newptr += markidx(atmark); /* keep start of old block */ build = blk->c + (newptr - buf.c); /* fill this block (or blocks) from the newtext string */ while (*newtext) { while (*newtext && build < blk->c + BLKSIZE - 1) { *build++ = *newtext++; } if (*newtext) { /* save the excess */ for (scan = linebuf.c + BLKSIZE; build > blk->c && build[-1] != '\n'; ) { *--scan = *--build; } /* write the block */ while (build < blk->c + BLKSIZE) { *build++ = '\0'; } blkdirty(blk); /* add another block */ blkno++; blk = blkadd(blkno); /* copy in the excess from last time */ for (build = blk->c; scan < linebuf.c + BLKSIZE; ) { *build++ = *scan++; } } } /* fill this block(s) from remainder of orig block */ while (newptr < buf.c + BLKSIZE && *newptr) { while (newptr < buf.c + BLKSIZE && *newptr && build < blk->c + BLKSIZE - 1) { *build++ = *newptr++; } if (newptr < buf.c + BLKSIZE && *newptr) { /* save the excess */ for (scan = linebuf.c + BLKSIZE; build > blk->c && build[-1] != '\n'; ) { *--scan = *--build; } /* write the block */ while (build < blk->c + BLKSIZE) { *build++ = '\0'; } blkdirty(blk); /* add another block */ blkno++; blk = blkadd(blkno); /* copy in the excess from last time */ for (build = blk->c; scan < linebuf.c + BLKSIZE; ) { *build++ = *scan++; } } } /* see if we can combine our last block with the following block */ if (lnum[blkno] < nlines && lnum[blkno + 1] - lnum[blkno] < (BLKSIZE >> 6)) { /* hey, we probably can! Get the following block & see... */ following = blkget(blkno + 1); if (strlen(following->c) + (build - blk->c) < BLKSIZE - 1) { /* we can! Copy text from following to blk */ for (scan = following->c; *scan; ) { *build++ = *scan++; } while (build < blk->c + BLKSIZE) { *build++ = '\0'; } blkdirty(blk); /* pretend the following was the last blk */ blk = following; build = blk->c; } } /* that last block is dirty by now */ while (build < blk->c + BLKSIZE) { *build++ = '\0'; } blkdirty(blk); } /* change the text of a file */ change(frommark, tomark, newtext) MARK frommark, tomark; char *newtext; { int i; long l; char *text; BLK *blk; /* optimize for single-character replacement */ if (frommark + 1 == tomark && newtext[0] && !newtext[1] && newtext[0] != '\n') { /* find the block containing frommark */ l = markline(frommark); for (i = 1; lnum[i] < l; i++) { } /* get the block */ blk = blkget(i); /* find the line within the block */ for (text = blk->c, i = l - lnum[i - 1] - 1; i > 0; text++) { if (*text == '\n') { i--; } } /* replace the char */ text += markidx(frommark); if (*text == newtext[0]) { /* no change was needed - same char */ return; } else if (*text != '\n') { /* This is a change */ changes++; ChangeText { *text = newtext[0]; blkdirty(blk); } return; } /* else it is a complex change involving newline... */ } /* couldn't optimize, so do delete & add */ ChangeText { delete(frommark, tomark); add(frommark, newtext); rptlabel = "changed"; } } SHAR_EOF fi if test -f 'move1.c' then echo shar: "will not over-write existing file 'move1.c'" else cat << \SHAR_EOF > 'move1.c' /* m_1.c */ /* Author: * Steve Kirkendall * 16820 SW Tallac Way * Beaverton, OR 97006 * kirkenda@jove.cs.pdx.edu, or ...uunet!tektronix!psueea!jove!kirkenda */ /* This file contains most movement functions */ #include "config.h" #include <ctype.h> #include "vi.h" #ifndef isascii # define isascii(c) !((c) & ~0x7f) #endif /*ARGSUSED*/ MARK m_up(m, cnt) MARK m; /* movement is relative to this mark */ long cnt; /* a numeric argument */ { DEFAULT(1); /* if at top already, don't move */ if (markline(m) - cnt < 1) { return MARK_UNSET; } /* else move up one line */ m -= MARK_AT_LINE(cnt); return m; } /*ARGSUSED*/ MARK m_down(m, cnt) MARK m; /* movement is relative to this mark */ long cnt; /* a numeric argument */ { DEFAULT(1); /* if at bottom already, don't move */ if (markline(m) + cnt > nlines) { return MARK_UNSET; } /* else move down one line */ m += MARK_AT_LINE(cnt); /* adjust column number */ if (markidx(m) >= plen) { m = (m & ~(BLKSIZE - 1)); if (plen > 0) { m += plen - 1; } } return m; } /*ARGSUSED*/ MARK m_right(m, cnt) MARK m; /* movement is relative to this mark */ long cnt; /* a numeric argument */ { int idx; /* index of the new cursor position */ DEFAULT(1); /* move to right, if that's OK */ pfetch(markline(m)); idx = markidx(m) + cnt; if (idx < plen) { m += cnt; } else { return MARK_UNSET; } return m; } /*ARGSUSED*/ MARK m_left(m, cnt) MARK m; /* movement is relative to this mark */ long cnt; /* a numeric argument */ { DEFAULT(1); /* move to the left, if that's OK */ if (markidx(m) >= cnt) { m -= cnt; } else { return MARK_UNSET; } return m; } /*ARGSUSED*/ MARK m_toline(m, cnt) MARK m; /* movement is relative to this mark */ long cnt; /* a numeric line number */ { /* if no number specified, assume last line */ DEFAULT(nlines); /* if invalid line number, don't move */ if (cnt > nlines) { msg("Line numbers range from 1 to %ld", nlines); return MARK_UNSET; } /* move to first character of the selected line */ m = MARK_AT_LINE(cnt); return m; } /*ARGSUSED*/ MARK m_tocol(m, cnt) MARK m; /* movement is relative to this mark */ long cnt; /* a numeric argument */ { char *text; /* text of the line */ int col; /* column number */ int idx; /* index into the line */ DEFAULT(1); /* internally, columns are numbered 0..COLS-1, not 1..COLS */ cnt--; /* if 0, that's easy */ if (cnt == 0) { m &= ~(BLKSIZE - 1); return m; } /* find that column within the line */ pfetch(markline(m)); text = ptext; for (col = idx = 0; col < cnt && *text; text++, idx++) { if (*text == '\t' && !*o_list) { col += *o_tabstop; col -= col % *o_tabstop; } else if (UCHAR(*text) < ' ' || *text == '\177') { col += 2; } #ifndef NO_CHARATTR else if (text[0] == '\\' && text[1] == 'f' && text[2] && *o_charattr) { text += 2; /* plus one more as part of for loop */ } #endif else { col++; } } if (!*text) { return MARK_UNSET; } else { m = (m & ~(BLKSIZE - 1)) + idx; } return m; } /*ARGSUSED*/ MARK m_front(m, cnt) MARK m; /* movement is relative to this mark */ long cnt; /* a numeric argument (ignored) */ { char *scan; /* move to the first non-whitespace character */ pfetch(markline(m)); scan = ptext; m &= ~(BLKSIZE - 1); while (*scan == ' ' || *scan == '\t') { scan++; m++; } return m; } /*ARGSUSED*/ MARK m_rear(m, cnt) MARK m; /* movement is relative to this mark */ long cnt; /* a numeric argument (ignored) */ { /* Try to move *EXTREMELY* far to the right. It is fervently hoped * that other code will convert this to a more reasonable MARK before * anything tries to actually use it. (See adjmove() in vi.c) */ return m | (BLKSIZE - 1); } #ifndef NO_SENTENCE /*ARGSUSED*/ MARK m_fsentence(m, cnt) MARK m; /* movement is relative to this mark */ long cnt; /* a numeric argument */ { register char *text; register long l; DEFAULT(1); /* get the current line */ l = markline(m); pfetch(l); text = ptext + markidx(m); /* for each requested sentence... */ while (cnt-- > 0) { /* search forward for one of [.?!] followed by spaces or EOL */ do { /* wrap at end of line */ if (!text[0]) { if (l >= nlines) { return MARK_UNSET; } l++; pfetch(l); text = ptext; } else { text++; } } while (text[0] != '.' && text[0] != '?' && text[0] != '!' || text[1] && (text[1] != ' ' || text[2] && text[2] != ' ')); } /* construct a mark for this location */ m = buildmark(text); /* move forward to the first word of the next sentence */ m = m_fword(m, 1L); return m; } /*ARGSUSED*/ MARK m_bsentence(m, cnt) MARK m; /* movement is relative to this mark */ long cnt; /* a numeric argument */ { register char *text; /* used to scan thru text */ register long l; /* current line number */ int flag; /* have we passed at least one word? */ DEFAULT(1); /* get the current line */ l = markline(m); pfetch(l); text = ptext + markidx(m); /* for each requested sentence... */ flag = TRUE; while (cnt-- > 0) { /* search backward for one of [.?!] followed by spaces or EOL */ do { /* wrap at beginning of line */ if (text == ptext) { do { if (l <= 1) { return MARK_UNSET; } l--; pfetch(l); } while (!*ptext); text = ptext + plen - 1; } else { text--; } /* are we moving past a "word"? */ if (text[0] >= '0') { flag = FALSE; } } while (flag || text[0] != '.' && text[0] != '?' && text[0] != '!' || text[1] && (text[1] != ' ' || text[2] && text[2] != ' ')); } /* construct a mark for this location */ m = buildmark(text); /* move to the front of the following sentence */ m = m_fword(m, 1L); return m; } #endif /*ARGSUSED*/ MARK m_fparagraph(m, cnt) MARK m; /* movement is relative to this mark */ long cnt; /* a numeric argument */ { char *text; char *pscn; /* used to scan thru value of "paragraphs" option */ long l; DEFAULT(1); for (l = markline(m); cnt > 0 && l++ < nlines; ) { text = fetchline(l); if (!*text) { cnt--; } #ifndef NO_SENTENCE else if (*text == '.') { for (pscn = o_paragraphs; pscn[0] && pscn[1]; pscn += 2) { if (pscn[0] == text[1] && pscn[1] == text[2]) { cnt--; break; } } } #endif } if (l <= nlines) { m = MARK_AT_LINE(l); } else { m = MARK_LAST; } return m; } /*ARGSUSED*/ MARK m_bparagraph(m, cnt) MARK m; /* movement is relative to this mark */ long cnt; /* a numeric argument */ { char *text; char *pscn; /* used to scan thru value of "paragraph" option */ long l; DEFAULT(1); for (l = markline(m); cnt > 0 && l-- > 1; ) { text = fetchline(l); if (!*text) { cnt--; } #ifndef NO_SENTENCE else if (*text == '.') { for (pscn = o_paragraphs; pscn[0] && pscn[1]; pscn += 2) { if (pscn[0] == text[1] && pscn[1] == text[2]) { cnt--; break; } } } #endif } if (l >= 1) { m = MARK_AT_LINE(l); } else { m = MARK_FIRST; } return m; } /*ARGSUSED*/ MARK m_fsection(m, cnt, key) MARK m; /* movement is relative to this mark */ long cnt; /* (ignored) */ int key; /* second key stroke - must be ']' */ { char *text; char *sscn; /* used to scan thru value of "sections" option */ long l; /* make sure second key was ']' */ if (key != ']') { return MARK_UNSET; } for (l = markline(m); l++ < nlines; ) { text = fetchline(l); if (*text == '{') { break; } #ifndef NO_SENTENCE else if (*text == '.') { for (sscn = o_sections; sscn[0] && sscn[1]; sscn += 2) { if (sscn[0] == text[1] && sscn[1] == text[2]) { goto BreakBreak; } } } #endif } BreakBreak: if (l <= nlines) { m = MARK_AT_LINE(l); } else { m = MARK_LAST; } return m; } /*ARGSUSED*/ MARK m_bsection(m, cnt, key) MARK m; /* movement is relative to this mark */ long cnt; /* (ignored) */ int key; /* second key stroke - must be '[' */ { char *text; char *sscn; /* used to scan thru value of "sections" option */ long l; /* make sure second key was '[' */ if (key != '[') { return MARK_UNSET; } for (l = markline(m); l-- > 1; ) { text = fetchline(l); if (*text == '{') { break; } #ifndef NO_SENTENCE else if (*text == '.') { for (sscn = o_sections; sscn[0] && sscn[1]; sscn += 2) { if (sscn[0] == text[1] && sscn[1] == text[2]) { goto BreakBreak; } } } #endif } BreakBreak: if (l >= 1) { m = MARK_AT_LINE(l); } else { m = MARK_FIRST; } return m; } /*ARGSUSED*/ MARK m_match(m, cnt) MARK m; /* movement is relative to this mark */ long cnt; /* a numeric argument */ { long l; register char *text; register char match; register char nest; register int count; /* get the current line */ l = markline(m); pfetch(l); text = ptext + markidx(m); /* search forward within line for one of "[](){}" */ for (match = '\0'; !match && *text; text++) { /* tricky way to recognize 'em in ASCII */ nest = *text; if ((nest & 0xdf) == ']' || (nest & 0xdf) == '[') { match = nest ^ ('[' ^ ']'); } else if ((nest & 0xfe) == '(') { match = nest ^ ('(' ^ ')'); } else { match = 0; } } if (!match) { return MARK_UNSET; } text--; /* search forward or backward for match */ if (match == '(' || match == '[' || match == '{') { /* search backward */ for (count = 1; count > 0; ) { /* wrap at beginning of line */ if (text == ptext) { do { if (l <= 1L) { return MARK_UNSET; } l--; pfetch(l); } while (!*ptext); text = ptext + plen - 1; } else { text--; } /* check the char */ if (*text == match) count--; else if (*text == nest) count++; } } else { /* search forward */ for (count = 1; count > 0; ) { /* wrap at end of line */ if (!*text) { if (l >= nlines) { return MARK_UNSET; } l++; pfetch(l); text = ptext; } else { text++; } /* check the char */ if (*text == match) count--; else if (*text == nest) count++; } } /* construct a mark for this place */ m = buildmark(text); return m; } /*ARGSUSED*/ MARK m_tomark(m, cnt, key) MARK m; /* movement is relative to this mark */ long cnt; /* (ignored) */ int key; /* keystroke - the mark to move to */ { /* mark '' is a special case */ if (key == '\'' || key == '`') { if (mark[26] == MARK_UNSET) { return MARK_FIRST; } else { return mark[26]; } } /* if not a valid mark number, don't move */ if (key < 'a' || key > 'z') { return MARK_UNSET; } /* return the selected mark -- may be MARK_UNSET */ if (!mark[key - 'a']) { msg("mark '%c is unset", key); } return mark[key - 'a']; } SHAR_EOF fi if test -f 'move2.c' then echo shar: "will not over-write existing file 'move2.c'" else cat << \SHAR_EOF > 'move2.c' /* m_2.c */ /* Author: * Steve Kirkendall * 16820 SW Tallac Way * Beaverton, OR 97006 * kirkenda@jove.cs.pdx.edu, or ...uunet!tektronix!psueea!jove!kirkenda */ /* This function contains the mvoement functions that perform RE searching */ #include "config.h" #include "vi.h" #include "regexp.h" static regexp *re; /* compiled version of the pattern to search for */ static prevsf; /* boolean: previous search direction was forward? */ MARK m_nsrch(m) MARK m; /* where to start searching */ { if (prevsf) { m = m_fsrch(m, ""); prevsf = TRUE; } else { m = m_bsrch(m, ""); prevsf = FALSE; } return m; } MARK m_Nsrch(m) MARK m; /* where to start searching */ { if (prevsf) { m = m_bsrch(m, ""); prevsf = TRUE; } else { m = m_fsrch(m, ""); prevsf = FALSE; } return m; } MARK m_fsrch(m, ptrn) MARK m; /* where to start searching */ char *ptrn; /* pattern to search for */ { long l; /* line# of line to be searched */ char *line; /* text of line to be searched */ int wrapped;/* boolean: has our search wrapped yet? */ int pos; /* where we are in the line */ /* remember: "previous search was forward" */ prevsf = TRUE; if (ptrn && *ptrn) { /* free the previous pattern */ if (re) free(re); /* compile the pattern */ re = regcomp(ptrn); if (!re) { return MARK_UNSET; } } else if (!re) { msg("No previous expression"); return MARK_UNSET; } /* search forward for the pattern */ pos = markidx(m) + 1; pfetch(markline(m)); if (pos >= plen) { pos = 0; m = (m | (BLKSIZE - 1)) + 1; } wrapped = FALSE; for (l = markline(m); l != markline(m) + 1 || !wrapped; l++) { /* wrap search */ if (l > nlines) { /* if we wrapped once already, then the search failed */ if (wrapped) { break; } /* else maybe we should wrap now? */ if (*o_wrapscan) { l = 0; wrapped = TRUE; continue; } else { break; } } /* get this line */ line = fetchline(l); /* check this line */ if (regexec(re, &line[pos], (pos == 0))) { /* match! */ if (wrapped && *o_warn) msg("(wrapped)"); return MARK_AT_LINE(l) + (int)(re->startp[0] - line); } pos = 0; } /* not found */ msg(*o_wrapscan ? "Not found" : "Hit bottom without finding RE"); return MARK_UNSET; } MARK m_bsrch(m, ptrn) MARK m; /* where to start searching */ char *ptrn; /* pattern to search for */ { long l; /* line# of line to be searched */ char *line; /* text of line to be searched */ int wrapped;/* boolean: has our search wrapped yet? */ int pos; /* last acceptable idx for a match on this line */ int last; /* remembered idx of the last acceptable match on this line */ int try; /* an idx at which we strat searching for another match */ /* remember: "previous search was not forward" */ prevsf = FALSE; if (ptrn && *ptrn) { /* free the previous pattern, if any */ if (re) free(re); /* compile the pattern */ re = regcomp(ptrn); if (!re) { return MARK_UNSET; } } else if (!re) { msg("No previous expression"); return MARK_UNSET; } /* search backward for the pattern */ pos = markidx(m); wrapped = FALSE; for (l = markline(m); l != markline(m) - 1 || !wrapped; l--) { /* wrap search */ if (l < 1) { if (*o_wrapscan) { l = nlines + 1; wrapped = TRUE; continue; } else { break; } } /* get this line */ line = fetchline(l); /* check this line */ if (regexec(re, line, 1) && (int)(re->startp[0] - line) < pos) { /* match! now find the last acceptable one in this line */ do { last = (int)(re->startp[0] - line); try = (int)(re->endp[0] - line); } while (try > 0 && regexec(re, &line[try], FALSE) && (int)(re->startp[0] - line) < pos); if (wrapped && *o_warn) msg("(wrapped)"); return MARK_AT_LINE(l) + last; } pos = BLKSIZE; } /* not found */ msg(*o_wrapscan ? "Not found" : "Hit top without finding RE"); return MARK_UNSET; } SHAR_EOF fi if test -f 'move3.c' then echo shar: "will not over-write existing file 'move3.c'" else cat << \SHAR_EOF > 'move3.c' /* m_3.c */ /* Author: * Steve Kirkendall * 16820 SW Tallac Way * Beaverton, OR 97006 * kirkenda@jove.cs.pdx.edu, or ...uunet!tektronix!psueea!jove!kirkenda */ /* This file contains movement functions that perform character searches */ #include "config.h" #include "vi.h" #ifndef NO_CHARSEARCH static MARK (*prevfwdfn)(); /* function to search in same direction */ static MARK (*prevrevfn)(); /* function to search in opposite direction */ static char prev_key; /* sought cvhar from previous [fFtT] */ MARK m__ch(m, cnt, cmd) MARK m; /* current position */ long cnt; char cmd; /* command: either ',' or ';' */ { MARK (*tmp)(); if (!prevfwdfn) { msg("No previous f, F, t, or T command"); return MARK_UNSET; } if (cmd == ',') { m = (*prevrevfn)(m, cnt, prev_key); /* Oops! we didn't want to change the prev*fn vars! */ tmp = prevfwdfn; prevfwdfn = prevrevfn; prevrevfn = tmp; return m; } else { return (*prevfwdfn)(m, cnt, prev_key); } } /* move forward within this line to next occurrence of key */ MARK m_fch(m, cnt, key) MARK m; /* where to search from */ long cnt; char key; /* what to search for */ { register char *text; DEFAULT(1); prevfwdfn = m_fch; prevrevfn = m_Fch; prev_key = key; pfetch(markline(m)); text = ptext + markidx(m); while (cnt-- > 0) { do { m++; text++; } while (*text && *text != key); } if (!*text) { return MARK_UNSET; } return m; } /* move backward within this line to previous occurrence of key */ MARK m_Fch(m, cnt, key) MARK m; /* where to search from */ long cnt; char key; /* what to search for */ { register char *text; DEFAULT(1); prevfwdfn = m_Fch; prevrevfn = m_fch; prev_key = key; pfetch(markline(m)); text = ptext + markidx(m); while (cnt-- > 0) { do { m--; text--; } while (text >= ptext && *text != key); } if (text < ptext) { return MARK_UNSET; } return m; } /* move forward within this line almost to next occurrence of key */ MARK m_tch(m, cnt, key) MARK m; /* where to search from */ long cnt; char key; /* what to search for */ { /* skip the adjacent char */ pfetch(markline(m)); if (plen <= markidx(m)) { return MARK_UNSET; } m++; m = m_fch(m, cnt, key); if (m == MARK_UNSET) { return MARK_UNSET; } prevfwdfn = m_tch; prevrevfn = m_Tch; return m - 1; } /* move backward within this line almost to previous occurrence of key */ MARK m_Tch(m, cnt, key) MARK m; /* where to search from */ long cnt; char key; /* what to search for */ { /* skip the adjacent char */ if (markidx(m) == 0) { return MARK_UNSET; } m--; m = m_Fch(m, cnt, key); if (m == MARK_UNSET) { return MARK_UNSET; } prevfwdfn = m_Tch; prevrevfn = m_tch; return m + 1; } #endif SHAR_EOF fi if test -f 'move4.c' then echo shar: "will not over-write existing file 'move4.c'" else cat << \SHAR_EOF > 'move4.c' /* m_4.c */ /* Author: * Steve Kirkendall * 16820 SW Tallac Way * Beaverton, OR 97006 * kirkenda@jove.cs.pdx.edu, or ...uunet!tektronix!psueea!jove!kirkenda */ /* This file contains movement functions which are screen-relative */ #include "config.h" #include "vi.h" /* This m_s the cursor to a particular row on the screen */ /*ARGSUSED*/ MARK m_row(m, cnt, key) MARK m; /* the cursor position */ long cnt; /* the row we'll move to */ int key; /* the keystroke of this move - H/L/M */ { DEFAULT(1); /* calculate destination line based on key */ cnt--; switch (key) { case 'H': cnt = topline + cnt; break; case 'M': cnt = topline + (LINES - 1) / 2; break; case 'L': cnt = botline - cnt; break; } /* return the mark of the destination line */ return MARK_AT_LINE(cnt); } /* This function repositions the current line to show on a given row */ /*ARGSUSED*/ MARK m_z(m, cnt, key) MARK m; /* the cursor */ long cnt; /* the line number we're repositioning */ int key; /* key struck after the z */ { long newtop; /* Which line are we talking about? */ if (cnt < 0 || cnt > nlines) { return MARK_UNSET; } if (cnt) { m = MARK_AT_LINE(cnt); newtop = cnt; } else { newtop = markline(m); } /* allow a "window size" number to be entered, but ignore it */ while (key >= '0' && key <= '9') { key = getkey(0); } /* figure out which line will have to be at the top of the screen */ switch (key) { case '\n': case '\r': case '+': break; case '.': case 'z': newtop -= LINES / 2; break; case '-': newtop -= LINES - 1; break; default: return MARK_UNSET; } /* make the new topline take effect */ if (newtop >= 1) { topline = newtop; } else { topline = 1L; } mustredraw = TRUE; /* The cursor doesn't move */ return m; } /* This function scrolls the screen. It does this by calling redraw() with * an off-screen line as the argument. It will move the cursor if necessary * so that the cursor is on the new screen. */ /*ARGSUSED*/ MARK m_scroll(m, cnt, key) MARK m; /* the cursor position */ long cnt; /* for some keys: the number of lines to scroll */ int key; /* keystroke that causes this movement */ { MARK tmp; /* a temporary mark, used as arg to redraw() */ /* adjust cnt, and maybe *o_scroll, depending of key */ switch (key) { case ctrl('F'): case ctrl('B'): DEFAULT(1); mustredraw = TRUE; cnt = cnt * (LINES - 1) - 1; /* keeps one old line on screen */ break; case ctrl('E'): case ctrl('Y'): DEFAULT(1); break; case ctrl('U'): case ctrl('D'): if (cnt == 0) /* default */ { cnt = *o_scroll; } else { if (cnt > LINES - 1) { cnt = LINES - 1; } *o_scroll = cnt; } break; } /* scroll up or down, depending on key */ switch (key) { case ctrl('B'): case ctrl('Y'): case ctrl('U'): cnt = topline - cnt; if (cnt < 1L) { cnt = 1L; m = MARK_FIRST; } tmp = MARK_AT_LINE(cnt) + markidx(m); redraw(tmp, FALSE); if (markline(m) > botline) { m = MARK_AT_LINE(botline); } break; case ctrl('F'): case ctrl('E'): case ctrl('D'): cnt = botline + cnt; if (cnt > nlines) { cnt = nlines; m = MARK_LAST; } tmp = MARK_AT_LINE(cnt) + markidx(m); redraw(tmp, FALSE); if (markline(m) < topline) { m = MARK_AT_LINE(topline); } break; } /* arrange for ctrl-B and ctrl-F to redraw the smart line */ if (key == ctrl('B') || key == ctrl('F')) { changes++; } return m; } SHAR_EOF fi if test -f 'move5.c' then echo shar: "will not over-write existing file 'move5.c'" else cat << \SHAR_EOF > 'move5.c' /* m_5.c */ /* Author: * Steve Kirkendall * 16820 SW Tallac Way * Beaverton, OR 97006 * kirkenda@jove.cs.pdx.edu, or ...uunet!tektronix!psueea!jove!kirkenda */ /* This file contains the word-oriented movement functions */ #include <ctype.h> #include "config.h" #include "vi.h" #ifndef isascii # define isascii(c) !((c) & ~0x7f) #endif MARK m_fword(m, cnt) MARK m; /* movement is relative to this mark */ long cnt; /* a numeric argument */ { register long l; register char *text; register int i; DEFAULT(1); l = markline(m); pfetch(l); text = ptext + markidx(m); while (cnt-- > 0) /* yes, ASSIGNMENT! */ { i = *text++; /* if we hit the end of the line, continue with next line */ if (!isascii(i) || isalnum(i) || i == '_') { /* include an alphanumeric word */ while (i && (!isascii(i) || isalnum(i) || i == '_')) { i = *text++; } } else { /* include contiguous punctuation */ while (i && isascii(i) && !isalnum(i) && !isspace(i)) { i = *text++; } } /* include trailing whitespace */ while (!i || isascii(i) && isspace(i)) { /* did we hit the end of this line? */ if (!i) { /* move to next line, if there is one */ l++; if (l > nlines) { return MARK_UNSET; } pfetch(l); text = ptext; } i = *text++; } text--; } /* construct a MARK for this place */ m = buildmark(text); return m; } MARK m_bword(m, cnt) MARK m; /* movement is relative to this mark */ long cnt; /* a numeric argument */ { register long l; register char *text; DEFAULT(1); l = markline(m); pfetch(l); text = ptext + markidx(m); while (cnt-- > 0) /* yes, ASSIGNMENT! */ { text--; /* include preceding whitespace */ while (text < ptext || isascii(*text) && isspace(*text)) { /* did we hit the end of this line? */ if (text < ptext) { /* move to preceding line, if there is one */ l--; if (l <= 0) { return MARK_UNSET; } pfetch(l); text = ptext + plen - 1; } else { text--; } } if (!isascii(*text) || isalnum(*text) || *text == '_') { /* include an alphanumeric word */ while (text >= ptext && (!isascii(*text) || isalnum(*text) || *text == '_')) { text--; } } else { /* include contiguous punctuation */ while (text >= ptext && isascii(*text) && !isalnum(*text) && !isspace(*text)) { text--; } } text++; } /* construct a MARK for this place */ m = buildmark(text); return m; } MARK m_eword(m, cnt) MARK m; /* movement is relative to this mark */ long cnt; /* a numeric argument */ { register long l; register char *text; register int i; DEFAULT(1); l = markline(m); pfetch(l); text = ptext + markidx(m); while (cnt-- > 0) /* yes, ASSIGNMENT! */ { text++; i = *text++; /* include preceding whitespace */ while (!i || isascii(i) && isspace(i)) { /* did we hit the end of this line? */ if (!i) { /* move to next line, if there is one */ l++; if (l > nlines) { return MARK_UNSET; } pfetch(l); text = ptext; } i = *text++; } if (!isascii(i) || isalnum(i) || i == '_') { /* include an alphanumeric word */ while (i && (!isascii(i) || isalnum(i) || i == '_')) { i = *text++; } } else { /* include contiguous punctuation */ while (i && isascii(i) && !isalnum(i) && !isspace(i)) { i = *text++; } } text -= 2; } /* construct a MARK for this place */ m = buildmark(text); return m; } MARK m_fWord(m, cnt) MARK m; /* movement is relative to this mark */ long cnt; /* a numeric argument */ { register long l; register char *text; register int i; DEFAULT(1); l = markline(m); pfetch(l); text = ptext + markidx(m); while (cnt-- > 0) /* yes, ASSIGNMENT! */ { i = *text++; /* if we hit the end of the line, continue with next line */ /* include contiguous non-space characters */ while (i && !isspace(i)) { i = *text++; } /* include trailing whitespace */ while (!i || isascii(i) && isspace(i)) { /* did we hit the end of this line? */ if (!i) { /* move to next line, if there is one */ l++; if (l > nlines) { return MARK_UNSET; } pfetch(l); text = ptext; } i = *text++; } text--; } /* construct a MARK for this place */ m = buildmark(text); return m; } MARK m_bWord(m, cnt) MARK m; /* movement is relative to this mark */ long cnt; /* a numeric argument */ { register long l; register char *text; DEFAULT(1); l = markline(m); pfetch(l); text = ptext + markidx(m); while (cnt-- > 0) /* yes, ASSIGNMENT! */ { text--; /* include trailing whitespace */ while (text < ptext || isascii(*text) && isspace(*text)) { /* did we hit the end of this line? */ if (text < ptext) { /* move to next line, if there is one */ l--; if (l <= 0) { return MARK_UNSET; } pfetch(l); text = ptext + plen - 1; } else { text--; } } /* include contiguous non-whitespace */ while (text >= ptext && (!isascii(*text) || !isspace(*text))) { text--; } text++; } /* construct a MARK for this place */ m = buildmark(text); return m; } MARK m_eWord(m, cnt) MARK m; /* movement is relative to this mark */ long cnt; /* a numeric argument */ { register long l; register char *text; register int i; DEFAULT(1); l = markline(m); pfetch(l); text = ptext + markidx(m); while (cnt-- > 0) /* yes, ASSIGNMENT! */ { text++; i = *text++; /* include preceding whitespace */ while (!i || isascii(i) && isspace(i)) { /* did we hit the end of this line? */ if (!i) { /* move to next line, if there is one */ l++; if (l > nlines) { return MARK_UNSET; } pfetch(l); text = ptext; } i = *text++; } /* include contiguous non-whitespace */ while (i && (!isascii(i) || !isspace(i))) { i = *text++; } text -= 2; } /* construct a MARK for this place */ m = buildmark(text); return m; } SHAR_EOF fi if test -f 'nomagic.c' then echo shar: "will not over-write existing file 'nomagic.c'" else cat << \SHAR_EOF > 'nomagic.c' /* nomagic.c */ /* This contains versions of the regcomp() and regexec() functions which * do not recognize any metacharacters except ^ $ and \. They use the same * data structure Henry Spencer's package, so they can continue to use his * regsub() function. * * This file is meant to be #included in regexp.c; it should *NOT* be * compiled separately. The regexp.c file will check to see if NO_MAGIC * is defined, and if so then this file is used; if not, then the real * regexp functions are used. */ regexp *regcomp(exp) char *exp; { char *src; char *dest; regexp *re; int i; /* allocate a big enough regexp structure */ re = (regexp *)malloc(strlen(exp) + 1 + sizeof(struct regexp)); if (!re) { regerror("could not malloc a regexp structure"); return (regexp *)0; } /* initialize all fields of the structure */ for (i = 0; i < NSUBEXP; i++) { re->startp[i] = (char *)0; re->endp[i] = (char *)0; } re->regstart = 0; re->reganch = 0; re->regmust = &re->program[1]; re->regmlen = 0; re->program[0] = MAGIC; /* copy the string into it, translating ^ and $ as needed */ for (src = exp, dest = re->program + 1; *src; src++) { switch (*src) { case '^': if (src == exp) re->regstart = 1; else *dest++ = '^'; break; case '$': if (!src[1]) re->reganch = 1; else *dest++ = '$'; break; case '\\': if (src[1]) *dest++ = *++src; else { regerror("extra \\ at end of regular expression"); } break; default: *dest++ = *src; } } *dest = '\0'; re->regmlen = strlen(&re->program[1]); return re; } /* This "helper" function checks for a match at a given location. It returns * 1 if it matches, 0 if it doesn't match here but might match later on in the * string, or -1 if it could not possibly match */ static int reghelp(prog, string, bolflag) struct regexp *prog; char *string; int bolflag; { char *scan; char *str; /* if ^, then require bolflag */ if (prog->regstart && !bolflag) { return -1; } /* if it matches, then it will start here */ prog->startp[0] = string; /* compare, possibly ignoring case */ if (*o_ignorecase) { for (scan = &prog->program[1]; *scan; scan++, string++) if (tolower(*scan) != tolower(*string)) return *string ? 0 : -1; } else { for (scan = &prog->program[1]; *scan; scan++, string++) if (*scan != *string) return *string ? 0 : -1; } /* if $, then require string to end here, too */ if (prog->reganch && *string) { return 0; } /* if we get to here, it matches */ prog->endp[0] = string; return 1; } int regexec(prog, string, bolflag) struct regexp *prog; char *string; int bolflag; { int rc; /* keep trying to match it */ for (rc = reghelp(prog, string, bolflag); rc == 0; rc = reghelp(prog, string, 0)) { string++; } /* did we match? */ return rc == 1; } SHAR_EOF fi if test -f 'opts.c' then echo shar: "will not over-write existing file 'opts.c'" else cat << \SHAR_EOF > 'opts.c' /* opts.c */ /* Author: * Steve Kirkendall * 16820 SW Tallac Way * Beaverton, OR 97006 * kirkenda@jove.cs.pdx.edu, or ...uunet!tektronix!psueea!jove!kirkenda */ /* This file contains the code that manages the run-time options -- The * values that can be modified via the "set" command. */ #include "config.h" #include "vi.h" #ifndef NULL #define NULL (char *)0 #endif extern char *getenv(); /* These are the default values of all options */ char o_autoindent[1] = {FALSE}; char o_autowrite[1] = {FALSE}; #ifndef NO_CHARATTR char o_charattr[1] = {FALSE}; #endif char o_columns[3] = {80, 32, 255}; char o_directory[30] = TMPDIR; char o_errorbells[1] = {TRUE}; char o_exrefresh[1] = {TRUE}; #ifndef NO_SENTENCE char o_hideformat[1] = {FALSE}; #endif char o_ignorecase[1] = {FALSE}; #ifndef NO_EXTENSIONS char o_inputmode[1] = {FALSE}; #endif char o_keytime[3] = {2, 0, 5}; char o_keywordprg[80] = KEYWORDPRG; char o_lines[3] = {25, 2, 50}; /* More lines? Enlarge kbuf */ char o_list[1] = {FALSE}; #ifndef NO_MAGIC char o_magic[1] = {TRUE}; #endif #ifndef NO_SENTENCE char o_paragraphs[30] = "PPppPApa"; #endif #if MSDOS char o_pcbios[1] = {TRUE}; #endif char o_readonly[1] = {FALSE}; char o_report[3] = {5, 1, 127}; char o_scroll[3] = {12, 1, 127}; #ifndef NO_SENTENCE char o_sections[30] = "SEseSHsh"; #endif char o_shell[60] = "/bin/sh"; char o_shiftwidth[3] = {8, 1, 255}; #ifndef NO_SHOWMODE char o_showmode[1] = {FALSE}; #endif char o_sidescroll[3] = {8, 1, 40}; char o_sync[1] = {FALSE}; char o_tabstop[3] = {8, 1, 40}; char o_term[30] = "?"; char o_vbell[1] = {TRUE}; char o_warn[1] = {TRUE}; char o_wrapmargin[3] = {0, 0, 255}; char o_wrapscan[1] = {TRUE}; /* The following describes the names & types of all options */ #define BOOL 0 #define NUM 1 #define STR 2 #define SET 0x01 /* this option has had its value altered */ #define CANSET 0x02 /* this option can be set at any time */ #define RCSET 0x06 /* this option can be set in a .exrc file only */ #define MR 0x40 /* does this option affect the way text is displayed? */ struct { char *name; /* name of an option */ char *nm; /* short name of an option */ char type; /* type of an option */ char flags; /* boolean: has this option been set? */ char *value; /* value */ } opts[] = { /* name type flags redraw value */ { "autoindent", "ai", BOOL, CANSET , o_autoindent }, { "autowrite", "aw", BOOL, CANSET , o_autowrite }, #ifndef NO_CHARATTR { "charattr", "ca", BOOL, CANSET | MR, o_charattr }, #endif { "columns", "co", NUM, SET , o_columns }, { "directory", "dir", STR, RCSET , o_directory }, { "errorbells", "eb", BOOL, CANSET , o_errorbells }, { "exrefresh", "er", BOOL, CANSET , o_exrefresh }, #ifndef NO_SENTENCE { "hideformat", "hf", BOOL, CANSET | MR, o_hideformat }, #endif { "ignorecase", "ic", BOOL, CANSET , o_ignorecase }, #ifndef NO_EXTENSIONS { "inputmode", "im", BOOL, CANSET , o_inputmode }, #endif { "keytime", "kt", NUM, CANSET , o_keytime }, { "keywordprg", "kp", STR, CANSET , o_keywordprg }, { "lines", "ls", NUM, SET , o_lines }, { "list", "li", BOOL, CANSET | MR, o_list }, #ifndef NO_MAGIC { "magic", "ma", BOOL, CANSET , o_magic }, #endif #ifndef NO_SENTENCE { "paragraphs", "pa", STR, CANSET , o_paragraphs }, #endif #if MSDOS { "pcbios", "pc", BOOL, SET , o_pcbios }, #endif { "readonly", "ro", BOOL, CANSET , o_readonly }, { "report", "re", NUM, CANSET , o_report }, { "scroll", "sc", NUM, CANSET , o_scroll }, #ifndef NO_SENTENCE { "sections", "se", STR, CANSET , o_sections }, #endif { "shell", "sh", STR, CANSET , o_shell }, #ifndef NO_SHOWMODE { "showmode", "sho", BOOL, CANSET , o_showmode }, #endif { "shiftwidth", "sw", NUM, CANSET , o_shiftwidth }, { "sidescroll", "ss", NUM, CANSET , o_sidescroll }, { "sync", "sy", BOOL, CANSET , o_sync }, { "tabstop", "ts", NUM, CANSET | MR, o_tabstop }, { "term", "te", STR, SET , o_term }, { "vbell", "vb", BOOL, CANSET , o_vbell }, { "warn", "wa", BOOL, CANSET , o_warn }, { "wrapmargin", "wm", NUM, CANSET , o_wrapmargin }, { "wrapscan", "ws", BOOL, CANSET , o_wrapscan }, { NULL, NULL, 0, CANSET, NULL } }; /* This function initializes certain options from environment variables, etc. */ initopts() { char *val; int i; /* set some stuff from environment variables */ #if ANY_UNIX || TOS if (val = getenv("SHELL")) /* yes, ASSIGNMENT! */ { strcpy(o_shell, val); } if (val = getenv("TERM")) /* yes, ASSIGNMENT! */ { strcpy(o_term, val); } #endif #if MSDOS if (val = getenv("COMSPEC")) /* yes, ASSIGNMENT! */ { strcpy(o_shell, val); } if ((val = getenv("TERM")) /* yes, ASSIGNMENT! */ && strcmp(val, "pcbios")) { strcpy(o_term, val); o_pcbios[0] = 0; } else { strcpy(o_term, "pcbios"); o_pcbios[0] = 1; } #endif #if MSDOS || TOS if ((val = getenv("TMP")) /* yes, ASSIGNMENT! */ || (val = getenv("TEMP"))) strcpy(o_directory, val); #endif *o_scroll = LINES / 2 - 1; /* disable the vbell option if we don't know how to do a vbell */ if (!has_VB) { for (i = 0; opts[i].value != o_vbell; i++) { } opts[i].flags &= ~CANSET; *o_vbell = FALSE; } } /* This function lists the current values of all options */ dumpopts(all) int all; /* boolean: dump all options, or just set ones? */ { int i; int col; char nbuf[4]; for (i = col = 0; opts[i].name; i++) { /* if not set and not all, ignore this option */ if (!all && !(opts[i].flags & SET)) { continue; } /* align this option in one of the columns */ if (col > 52) { addch('\n'); col = 0; } else if (col > 26) { while (col < 52) { qaddch(' '); col++; } } else if (col > 0) { while (col < 26) { qaddch(' '); col++; } } switch (opts[i].type) { case BOOL: if (!*opts[i].value) { qaddch('n'); qaddch('o'); col += 2; } qaddstr(opts[i].name); col += strlen(opts[i].name); break; case NUM: sprintf(nbuf, "%-3d", UCHAR(*opts[i].value)); qaddstr(opts[i].name); qaddch('='); qaddstr(nbuf); col += 4 + strlen(opts[i].name); break; case STR: qaddstr(opts[i].name); qaddch('='); qaddch('"'); qaddstr(opts[i].value); qaddch('"'); col += 3 + strlen(opts[i].name) + strlen(opts[i].value); break; } exrefresh(); } if (col > 0) { addch('\n'); exrefresh(); } } /* This function saves the current configuarion of options to a file */ saveopts(fd) int fd; /* file descriptor to write to */ { int i; char buf[256], *pos; /* write each set options */ for (i = 0; opts[i].name; i++) { /* if unset or unsettable, ignore this option */ if (!(opts[i].flags & SET) || !(opts[i].flags & CANSET)) { continue; } strcpy(buf, "set "); pos = &buf[4]; switch (opts[i].type) { case BOOL: if (!*opts[i].value) { *pos++='n'; *pos++='o'; } strcpy(pos, opts[i].name); strcat(pos, "\n"); break; case NUM: sprintf(pos, "%s=%-3d\n", opts[i].name, *opts[i].value & 0xff); break; case STR: sprintf(pos, "%s=\"%s\"\n", opts[i].name, opts[i].value); break; } twrite(fd, buf, strlen(buf)); } } /* This function changes the values of one or more options. */ setopts(assignments) char *assignments; /* a string containing option assignments */ { char *name; /* name of variable in assignments */ char *value; /* value of the variable */ char *scan; /* used for moving through strings */ int i, j; /* for each assignment... */ for (name = assignments; *name; ) { /* skip whitespace */ if (*name == ' ' || *name == '\t') { name++; continue; } /* find the value, if any */ for (scan = name; *scan >= 'a' && *scan <= 'z'; scan++) { } if (*scan == '=') { *scan++ = '\0'; if (*scan == '"') { value = ++scan; while (*scan && *scan != '"') { scan++; } if (*scan) { *scan++ = '\0'; } } else { value = scan; while (*scan && *scan != ' ' && *scan != '\t') { scan++; } if (*scan) { *scan++ = '\0'; } } } else { if (*scan) { *scan++ = '\0'; } value = NULL; if (name[0] == 'n' && name[1] == 'o') { name += 2; } } /* find the variable */ for (i = 0; opts[i].name && strcmp(opts[i].name, name) && strcmp(opts[i].nm, name); i++) { } /* change the variable */ if (!opts[i].name) { msg("invalid option name \"%s\"", name); } else if ((opts[i].flags & CANSET) != CANSET) { msg("option \"%s\" can't be altered", name); } else if ((opts[i].flags & RCSET) != CANSET && nlines >= 1L) { msg("option \"%s\" can only be set in a %s file", name, EXRC); } else if (value) { switch (opts[i].type) { case BOOL: msg("option \"[no]%s\" is boolean", name); break; case NUM: j = atoi(value); if (j == 0 && *value != '0') { msg("option \"%s\" must have a numeric value", name); } else if (j < opts[i].value[1] || j > (opts[i].value[2] & 0xff)) { msg("option \"%s\" must have a value between %d and %d", name, opts[i].value[1], opts[i].value[2] & 0xff); } else { *opts[i].value = atoi(value); opts[i].flags |= SET; } break; case STR: strcpy(opts[i].value, value); opts[i].flags |= SET; break; } if (opts[i].flags & MR) { mustredraw = TRUE; } } else /* valid option, no value */ { if (opts[i].type == BOOL) { *opts[i].value = (name[-1] != 'o'); opts[i].flags |= SET; if (opts[i].flags & MR) { mustredraw = TRUE; } } else { msg("option \"%s\" must be given a value", name); } } /* move on to the next option */ name = scan; } } SHAR_EOF fi if test -f 'pc.c' then echo shar: "will not over-write existing file 'pc.c'" else cat << \SHAR_EOF > 'pc.c' /* pc.c */ /* Author: * Guntram Blohm * Buchenstrasse 19 * 7904 Erbach, West Germany * Tel. ++49-7305-6997 * sorry - no regular network connection */ /* This file implements the ibm pc bios interface. See IBM documentation * for details. * If TERM is set upon invocation of elvis, this code is ignored completely, * and the standard termcap functions are used, thus, even not-so-close * compatibles can run elvis. For close compatibles however, bios output * is much faster (and permits reverse scrolling, adding and deleting lines, * and much more ansi.sys isn't capable of). GB. */ #include "config.h" #include "vi.h" #if MSDOS #include <dos.h> static void video(); /* vmode contains the screen attribute index and is set by attrset.*/ int vmode; /* The following array contains attribute definitions for * color/monochrome attributes. Screen selects one of the sets. * Maybe i'll put them into elvis options one day. */ static int screen; static char attr[2][5] = { /* :se: :so: :VB: :ul: :as: */ { 0x17, 0x1d, 0x1e, 0x1a, 0x1c, }, /* color */ { 0x07, 0x70, 0x0f, 0x01, 0x0f, }, /* monochrome */ }; /* * bios interface functions for elvis - pc version */ /* cursor up: determine current position, decrement row, set position */ void v_up() { int dx; video(0x300,(int *)0,&dx); dx-=0x100; video(0x200,(int *)0,&dx); } #ifndef NO_CURSORSHAPE /* cursor big: set begin scan to end scan - 4 */ void v_cb() { int cx; video(0x300, &cx, (int *)0); cx=((cx&0xff)|(((cx&0xff)-4)<<8)); video(0x100, &cx, (int *)0); } /* cursor small: set begin scan to end scan - 1 */ void v_cs() { int cx; video(0x300, &cx, (int *)0); cx=((cx&0xff)|(((cx&0xff)-1)<<8)); video(0x100, &cx, (int *)0); } #endif /* clear to end: get cursor position and emit the aproppriate number * of spaces, without moving cursor. */ void v_ce() { int cx, dx; video(0x300,(int *)0,&dx); cx=COLS-(dx&0xff); video(0x920,&cx,(int *)0); } /* clear screen: clear all and set cursor home */ void v_cl() { int cx=0, dx=((LINES-1)<<8)+COLS-1; video(0x0600,&cx,&dx); dx=0; video(0x0200,&cx,&dx); } /* clear to bottom: get position, clear to eol, clear next line to end */ void v_cd() { int cx, dx, dxtmp; video(0x0300,(int *)0,&dx); dxtmp=(dx&0xff00)|(COLS-1); cx=dx; video(0x0600,&cx,&dxtmp); cx=(dx&0xff00)+0x100; dx=((LINES-1)<<8)+COLS-1; video(0x600,&cx,&dx); } /* add line: scroll rest of screen down */ void v_al() { int cx,dx; video(0x0300,(int *)0,&dx); cx=(dx&0xff00); dx=((LINES-1)<<8)+COLS-1; video(0x701,&cx,&dx); } /* delete line: scroll rest up */ void v_dl() { int cx,dx; video(0x0300,(int *)0,&dx); cx=(dx&0xff00)/*+0x100*/; dx=((LINES-1)<<8)+COLS-1; video(0x601,&cx,&dx); } /* scroll reverse: scroll whole screen */ void v_sr() { int cx=0, dx=((LINES-1)<<8)+COLS-1; video(0x0701,&cx,&dx); } /* set cursor */ void v_move(x,y) int x, y; { int dx=(y<<8)+x; video(0x200,(int *)0,&dx); } /* put character: set attribute first, then execute char. * Also remember if current line has changed. */ int v_put(ch) int ch; { int cx=1; ch&=0xff; if (ch>=' ') video(0x900|ch,&cx,(int *)0); video(0xe00|ch,(int *)0, (int *)0); if (ch=='\n') { exwrote = TRUE; video(0xe0d, (int *)0, (int *)0); } return ch; } /* determine number of screen columns. Also set attrset according * to monochrome/color screen. */ int v_cols() { union REGS regs; regs.h.ah=0x0f; int86(0x10, ®s, ®s); if (regs.h.al==7) /* monochrome mode ? */ screen=1; else screen=0; return regs.h.ah; } /* Getting the number of rows is hard. Most screens support 25 only, * EGA/VGA also support 43/50 lines, and some OEM's even more. * Unfortunately, there is no standard bios variable for the number * of lines, and the bios screen memory size is always rounded up * to 0x1000. So, we'll really have to cheat. * When using the screen memory size, keep in mind that each character * byte has an associated attribute byte. * * uses: word at 40:4c contains memory size * byte at 40:84 # of rows-1 (sometimes) * byte at 40:4a # of columns */ int v_rows() { int line, oldline; /* screen size less then 4K? then we have 25 lines only */ if (*(int far *)(0x0040004cl)<=4096) return 25; /* VEGA vga uses the bios byte at 0x40:0x84 for # of rows. * Use that byte, if it makes sense together with memory size. */ if ((((*(unsigned char far *)(0x0040004aL)*2* (*(unsigned char far *)(0x00400084L)+1))+0xfff)&(~0xfff))== *(unsigned int far *)(0x0040004cL)) return *(unsigned char far *)(0x00400084L)+1; /* uh oh. Emit '\n's until screen starts scrolling. */ v_move(oldline=0, 0); for (;;) { video(0xe0a,(int *)0,(int *)0); video(0x300,(int *)0,&line); line>>=8; if (oldline==line) return line+1; oldline=line; } } /* the REAL bios interface -- used internally only. */ static void video(ax, cx, dx) int ax, *cx, *dx; { union REGS regs; regs.x.ax=ax; if ((ax&0xff00)==0x600 || (ax&0xff00)==0x700) regs.h.bh=attr[screen][vmode]; else { regs.h.bh=0; regs.h.bl=attr[screen][vmode]; } if (cx) regs.x.cx=*cx; if (dx) regs.x.dx=*dx; int86(0x10, ®s, ®s); if (dx) *dx=regs.x.dx; if (cx) *cx=regs.x.cx; } /* The following function determines which character is used for * commandline-options by command.com. This system call is undocumented * and valid for versions < 4.00 only. */ int switchar() { union REGS regs; regs.x.ax=0x3700; int86(0x21, ®s, ®s); return regs.h.dl; } #endif SHAR_EOF fi if test -f 'recycle.c' then echo shar: "will not over-write existing file 'recycle.c'" else cat << \SHAR_EOF > 'recycle.c' /* recycle.c */ /* Author: * Steve Kirkendall * 16820 SW Tallac Way * Beaverton, OR 97006 * kirkenda@jove.cs.pdx.edu, or ...uunet!tektronix!psueea!jove!kirkenda */ /* This file contains the functions perform garbage collection and allocate * reusable blocks. */ #include "config.h" #include "vi.h" #ifndef NO_RECYCLE /* this whole file would have be skipped if NO_RECYCLE is defined */ extern long lseek(); #define BTST(bitno, byte) ((byte) & (1 << (bitno))) #define BSET(bitno, byte) ((byte) |= (1 << (bitno))) #define BCLR(bitno, byte) ((byte) &= ~(1 << (bitno))) #define TST(blkno) ((blkno) < MAXBIT ? BTST((blkno) & 7, bitmap[(blkno) >> 3]) : 1) #define SET(blkno) if ((blkno) < MAXBIT) BSET((blkno) & 7, bitmap[(blkno) >> 3]) #define CLR(blkno) if ((blkno) < MAXBIT) BCLR((blkno) & 7, bitmap[(blkno) >> 3]) /* bitmap of free blocks in first 4096k of tmp file */ static unsigned char bitmap[512]; #define MAXBIT (sizeof bitmap << 3) /* this function locates all free blocks in the current tmp file */ garbage() { int i; BLK oldhdr; /* start by assuming every block is free */ for (i = 0; i < sizeof bitmap; i++) { bitmap[i] = 255; } /* header block isn't free */ CLR(0); /* blocks needed for current hdr aren't free */ for (i = 1; i < MAXBLKS; i++) { CLR(hdr.n[i]); } /* blocks needed for undo version aren't free */ lseek(tmpfd, 0L, 0); if (read(tmpfd, &oldhdr, sizeof oldhdr) != sizeof oldhdr) { msg("garbage() failed to read oldhdr??"); for (i = 0; i < sizeof bitmap; i++) { bitmap[i] = 0; } return; } for (i = 1; i < MAXBLKS; i++) { CLR(oldhdr.n[i]); } /* blocks needed for cut buffers aren't free */ for (i = cutneeds(&oldhdr) - 1; i >= 0; i--) { CLR(oldhdr.n[i]); } } /* This function allocates the first available block in the tmp file */ long allocate() { int i; long offset; /* search for the first byte with a free bit set */ for (i = 0; i < sizeof bitmap && bitmap[i] == 0; i++) { } /* if we hit the end of the bitmap, return the end of the file */ if (i == sizeof bitmap) { offset = lseek(tmpfd, 0L, 2); } else /* compute the offset for the free block */ { for (i <<= 3; TST(i) == 0; i++) { } offset = (long)i * (long)BLKSIZE; /* mark the block as "allocated" */ CLR(i); } return offset; } #endif SHAR_EOF fi if test -f 'redraw.c' then echo shar: "will not over-write existing file 'redraw.c'" else cat << \SHAR_EOF > 'redraw.c' /* redraw.c */ /* Author: * Steve Kirkendall * 16820 SW Tallac Way * Beaverton, OR 97006 * kirkenda@jove.cs.pdx.edu, or ...uunet!tektronix!psueea!jove!kirkenda */ /* This file contains functions that draw text on the screen. The major entry * points are: * redrawrange() - called from modify.c to give hints about what parts * of the screen need to be redrawn. * redraw() - redraws the screen (or part of it) and positions * the cursor where it belongs. * idx2col() - converts a markidx() value to a logical column number. */ #include "config.h" #include "vi.h" /* This variable contains the line number that smartdrawtext() knows best */ static long smartlno; /* This function remebers where changes were made, so that the screen can be * redraw in a more efficient manner. */ redrawrange(after, pre, post) long after; /* lower bound of redrawafter */ long pre; /* upper bound of preredraw */ long post; /* upper bound of postredraw */ { long l; if (after < redrawafter) redrawafter = after; l = preredraw - postredraw + pre - post; if (post > postredraw) postredraw = post; preredraw = postredraw + l; } /* This function is used in visual mode for drawing the screen (or just parts * of the screen, if that's all thats needed). It also takes care of * scrolling. */ redraw(curs, inputting) MARK curs; /* where to leave the screen's cursor */ int inputting; /* boolean: being called from input() ? */ { char *text; /* a line of text to display */ static long chgs; /* previous changes level */ long l; int i; /* if curs == MARK_UNSET, then we should reset internal vars */ if (curs == MARK_UNSET) { if (topline < 1 || topline > nlines) { topline = 1L; } else { move(LINES - 1, 0); clrtoeol(); } leftcol = 0; mustredraw = TRUE; redrawafter = INFINITY; preredraw = 0L; postredraw = 0L; chgs = 0; smartlno = 0L; return; } /* figure out which column the cursor will be in */ l = markline(curs); text = fetchline(l); mark2phys(curs, text, inputting); /* adjust topline, if necessary, to get the cursor on the screen */ if (l >= topline && l <= botline) { /* it is on the screen already */ /* if the file was changed but !mustredraw, then redraw line */ if (chgs != changes && !mustredraw) { smartdrawtext(text, l); } } else if (l < topline && l > topline - LINES && (has_SR || has_AL)) { /* near top - scroll down */ if (!mustredraw) { move(0,0); while (l < topline) { topline--; if (has_SR) { do_SR(); } else { insertln(); } text = fetchline(topline); drawtext(text); do_UP(); } /* blank out the last line */ move(LINES - 1, 0); clrtoeol(); } else { topline = l; redrawafter = INFINITY; preredraw = 0L; postredraw = 0L; } } else if (l > topline && l < botline + LINES) { /* near bottom -- scroll up */ if (!mustredraw #if 1 || redrawafter == preredraw && preredraw == botline && postredraw == l #endif ) { move(LINES - 1,0); clrtoeol(); while (l > botline) { topline++; /* <-- also adjusts botline */ text = fetchline(botline); drawtext(text); } mustredraw = FALSE; } else { topline = l - (LINES - 2); redrawafter = INFINITY; preredraw = 0L; postredraw = 0L; } } else { /* distant line - center it & force a redraw */ topline = l - (LINES / 2) - 1; if (topline < 1) { topline = 1; } mustredraw = TRUE; redrawafter = INFINITY; preredraw = 0L; postredraw = 0L; } /* Now... do we really have to redraw? */ if (mustredraw) { /* If redrawfter (and friends) aren't set, assume we should * redraw everything. */ if (redrawafter == INFINITY) { redrawafter = 0L; preredraw = postredraw = INFINITY; } /* adjust smartlno to correspond with inserted/deleted lines */ if (smartlno >= redrawafter) { if (smartlno < preredraw) { smartlno = 0L; } else { smartlno += (postredraw - preredraw); } } /* should we insert some lines into the screen? */ if (preredraw < postredraw && preredraw <= botline) { /* lines were inserted into the file */ /* decide where insertion should start */ if (preredraw < topline) { l = topline; } else { l = preredraw; } /* insert the lines... maybe */ if (l + postredraw - preredraw > botline || !has_AL) { /* Whoa! a whole screen full - just redraw */ preredraw = postredraw = INFINITY; } else { /* really insert 'em */ move((int)(l - topline), 0); for (i = postredraw - preredraw; i > 0; i--) { insertln(); } /* NOTE: the contents of those lines will be * drawn as part of the regular redraw loop. */ /* clear the last line */ move(LINES - 1, 0); clrtoeol(); } } /* do we want to delete some lines from the screen? */ if (preredraw > postredraw && postredraw <= botline) { if (preredraw > botline || !has_DL) { postredraw = preredraw = INFINITY; } else /* we'd best delete some lines from the screen */ { /* clear the last line, so it doesn't look * ugly as it gets pulled up into the screen */ move(LINES - 1, 0); clrtoeol(); /* delete the lines */ move((int)(postredraw - topline), 0); for (l = postredraw; l < preredraw && l <= botline; l++) { deleteln(); } /* draw the lines that are now newly visible * at the bottom of the screen */ i = LINES - 1 + (postredraw - preredraw); move(i, 0); for (l = topline + i; l <= botline; l++) { /* clear this line */ clrtoeol(); /* draw the line, or ~ for non-lines */ if (l <= nlines) { text = fetchline(l); drawtext(text); } else { addstr("~\n"); } } } } /* redraw the current line */ l = markline(curs); pfetch(l); smartdrawtext(ptext, l); /* decide where we should start redrawing from */ if (redrawafter < topline) { l = topline; } else { l = redrawafter; } move((int)(l - topline), 0); /* draw the other lines */ for (; l <= botline && l < postredraw; l++) { /* we already drew the current line, so skip it now */ if (l == smartlno) { qaddch('\n'); continue; } /* clear this line */ clrtoeol(); /* draw the line, or ~ for non-lines */ if (l <= nlines) { text = fetchline(l); drawtext(text); } else { addstr("~\n"); } } mustredraw = FALSE; } /* force total (non-partial) redraw next time if not set */ redrawafter = INFINITY; preredraw = 0L; postredraw = 0L; /* move the cursor to where it belongs */ move((int)(markline(curs) - topline), physcol); wqrefresh(stdscr); chgs = changes; } /* This function converts a MARK to a column number. It doesn't automatically * adjust for leftcol; that must be done by the calling function */ int idx2col(curs, text, inputting) MARK curs; /* the line# & index# of the cursor */ register char *text; /* the text of the line, from fetchline */ int inputting; /* boolean: called from input() ? */ { static MARK pcursor;/* previous cursor, for possible shortcut */ static MARK pcol; /* column number for pcol */ static long chgs; /* previous value of changes counter */ register int col; /* used to count column numbers */ register int idx; /* used to count down the index */ register int i; /* for now, assume we have to start counting at the left edge */ col = 0; idx = markidx(curs); /* if the file hasn't changed & line number is the same & it has no * embedded character attribute strings, can we do shortcuts? */ if (chgs == changes && !((curs ^ pcursor) & ~(BLKSIZE - 1)) #ifndef NO_CHARATTR && !hasattr(markline(curs), text) #endif ) { /* no movement? */ if (curs == pcursor) { /* return the column of the char; for tabs, return its last column */ if (text[idx] == '\t' && !inputting && !*o_list) { return pcol + *o_tabstop - (pcol % *o_tabstop) - 1; } else { return pcol; } } /* movement to right? */ if (curs > pcursor) { /* start counting from previous place */ col = pcol; idx = markidx(curs) - markidx(pcursor); text += markidx(pcursor); } } /* count over to the char after the idx position */ while (idx > 0 && (i = *text)) /* yes, ASSIGNMENT! */ { if (i == '\t' && !*o_list) { col += *o_tabstop; col -= col % *o_tabstop; } else if (i >= '\0' && i < ' ' || i == '\177') { col += 2; } #ifndef NO_CHARATTR else if (i == '\\' && text[1] == 'f' && text[2] && *o_charattr) { text += 2; /* plus one more at bottom of loop */ idx -= 2; } #endif else { col++; } text++; idx--; } /* save stuff to speed next call */ pcursor = curs; pcol = col; chgs = changes; /* return the column of the char; for tabs, return its last column */ if (*text == '\t' && !inputting && !*o_list) { return col + *o_tabstop - (col % *o_tabstop) - 1; } else { return col; } } /* This function is similar to idx2col except that it takes care of sideways * scrolling - for the given line, at least. */ mark2phys(m, text, inputting) MARK m; /* a mark to convert */ char *text; /* the line that m refers to */ int inputting; /* boolean: caled from input() ? */ { int i; i = idx2col(cursor, text, inputting); while (i < leftcol) { leftcol -= *o_sidescroll; mustredraw = TRUE; redrawrange(1L, INFINITY, INFINITY); qaddch('\r'); /* drawtext(text); */ } while (i > rightcol) { leftcol += *o_sidescroll; mustredraw = TRUE; redrawrange(1L, INFINITY, INFINITY); qaddch('\r'); /* drawtext(text); */ } physcol = i - leftcol; physrow = markline(m) - topline; return physcol; } /* This function draws a single line of text on the screen. The screen's * cursor is assumed to be located at the leftmost column of the appropriate * row. */ drawtext(text) register char *text; /* the text to draw */ { register int col; /* column number */ register int i; register int tabstop; /* *o_tabstop */ register int limitcol; /* leftcol or leftcol + COLS */ int abnormal; /* boolean: charattr != A_NORMAL? */ #ifndef NO_SENTENCE /* if we're hiding format lines, and this is one of them, then hide it */ if (*o_hideformat && *text == '.') { clrtoeol(); qaddch('\n'); return; } #endif /* move some things into registers... */ limitcol = leftcol; tabstop = *o_tabstop; abnormal = FALSE; /* skip stuff that was scrolled off left edge */ for (col = 0; (i = *text) && col < limitcol; /* yes, ASSIGNMENT! */ text++) { if (i == '\t' && !*o_list) { col = col + tabstop - (col % tabstop); } else if (i >= 0 && i < ' ' || i == '\177') { col += 2; } #ifndef NO_CHARATTR else if (i == '\\' && text[1] == 'f' && text[2] && *o_charattr) { text += 2; /* plus one more as part of "for" loop */ /* since this attribute might carry over, we need it */ switch (*text) { case 'R': case 'P': attrset(A_NORMAL); abnormal = FALSE; break; case 'B': attrset(A_BOLD); abnormal = TRUE; break; case 'U': attrset(A_UNDERLINE); abnormal = TRUE; break; case 'I': attrset(A_ALTCHARSET); abnormal = TRUE; break; } } #endif else { col++; } } /* adjust for control char that was partially visible */ while (col > limitcol) { qaddch(' '); limitcol++; } /* now for the visible characters */ for (limitcol = leftcol + COLS; (i = *text) && col < limitcol; text++) { if (i == '\t' && !*o_list) { i = col + tabstop - (col % tabstop); if (i < limitcol) { if (has_PT && !((i - leftcol) & 7)) { do { qaddch('\t'); col += 8; /* not exact! */ } while (col < i); col = i; /* NOW it is exact */ } else { do { qaddch(' '); col++; } while (col < i); } } else /* tab ending after screen? next line! */ { col = limitcol; if (has_AM) { qaddch('\r'); /* GB */ qaddch('\n'); /* GB */ } } } else if (i >= 0 && i < ' ' || i == '\177') { col += 2; if (col < limitcol) { qaddch('^'); qaddch(i ^ '@'); } } #ifndef NO_CHARATTR else if (i == '\\' && text[1] == 'f' && text[2] && *o_charattr) { text += 2; /* plus one more as part of "for" loop */ switch (*text) { case 'R': case 'P': attrset(A_NORMAL); abnormal = FALSE; break; case 'B': attrset(A_BOLD); abnormal = TRUE; break; case 'U': attrset(A_UNDERLINE); abnormal = TRUE; break; case 'I': attrset(A_ALTCHARSET); abnormal = TRUE; break; } } #endif else { col++; qaddch(i); } } /* get ready for the next line */ #ifndef NO_CHARATTR if (abnormal) { attrset(A_NORMAL); } #endif if (*o_list && col < limitcol) { qaddch('$'); col++; } if (!has_AM || col < limitcol) { qaddch('\r'); qaddch('\n'); } } static nudgecursor(same, scan, new, lno) int same; /* number of chars to be skipped over */ char *scan; /* where the same chars end */ char *new; /* where the visible part of the line starts */ long lno; /* line number of this line */ { if (same > 0) { if (same < 5) { /* move the cursor by overwriting */ while (same > 0) { qaddch(scan[-same]); same--; } } else { /* move the cursor by calling move() */ move((int)(lno - topline), (int)(scan - new)); } } } /* This function draws a single line of text on the screen, possibly with * some cursor optimization. The cursor is repositioned before drawing * begins, so its position before doesn't really matter. */ smartdrawtext(text, lno) register char *text; /* the text to draw */ long lno; /* line number of the text */ { static char old[256]; /* how the line looked last time */ char new[256]; /* how it looks now */ char *build; /* used to put chars into new[] */ char *scan; /* used for moving thru new[] or old[] */ char *end; /* last non-blank changed char */ char *shift; /* used to insert/delete chars */ int same; /* length of a run of unchanged chars */ int limitcol; int col; int i; #ifndef NO_CHARATTR /* if this line has attributes, do it the dumb way instead */ if (hasattr(lno, text)) { move((int)(lno - topline), 0); clrtoeol(); drawtext(text); return; } #endif #ifndef NO_SENTENCE /* if this line is a format line, & we're hiding format lines, then * let the dumb drawtext() function handle it */ if (*o_hideformat && *text == '.') { move((int)(lno - topline), 0); clrtoeol(); drawtext(text); return; } #endif /* skip stuff that was scrolled off left edge */ limitcol = leftcol; for (col = 0; (i = *text) && col < limitcol; /* yes, ASSIGNMENT! */ text++) { if (i == '\t' && !*o_list) { col = col + *o_tabstop - (col % *o_tabstop); } else if (i >= 0 && i < ' ' || i == '\177') { col += 2; } else { col++; } } /* adjust for control char that was partially visible */ build = new; while (col > limitcol) { *build++ = ' '; limitcol++; } /* now for the visible characters */ for (limitcol = leftcol + COLS; (i = *text) && col < limitcol; text++) { if (i == '\t' && !*o_list) { i = col + *o_tabstop - (col % *o_tabstop); while (col < i && col < limitcol) { *build++ = ' '; col++; } } else if (i >= 0 && i < ' ' || i == '\177') { col += 2; *build++ = '^'; if (col < limitcol) { *build++ = (i ^ '@'); } } else { col++; *build++ = i; } } if (col < limitcol && *o_list) { *build++ = '$'; col++; } end = build; while (col < limitcol) { *build++ = ' '; col++; } /* locate the last non-blank character */ while (end > new && end[-1] == ' ') { end--; } /* can we optimize the displaying of this line? */ if (lno != smartlno) { /* nope, can't optimize - different line */ move((int)(lno - topline), 0); for (scan = new, build = old; scan < end; ) { qaddch(*scan); *build++ = *scan++; } if (end < new + COLS) { clrtoeol(); while (build < old + COLS) { *build++ = ' '; } } smartlno = lno; return; } /* skip any initial unchanged characters */ for (scan = new, build = old; scan < end && *scan == *build; scan++, build++) { } move((int)(lno - topline), (int)(scan - new)); /* The in-between characters must be changed */ same = 0; while (scan < end) { /* is this character a match? */ if (scan[0] == build[0]) { same++; } else /* do we want to insert? */ if (scan < end - 1 && scan[1] == build[0] && (has_IC || has_IM)) { nudgecursor(same, scan, new, lno); same = 0; insch(*scan); for (shift = old + COLS; --shift > build; ) { shift[0] = shift[-1]; } *build = *scan; } else /* do we want to delete? */ if (build < old + COLS - 1 && scan[0] == build[1] && has_DC) { nudgecursor(same, scan, new, lno); same = 0; delch(); same++; for (shift = build; shift < old + COLS - 1; shift++) { shift[0] = shift[1]; } *shift = ' '; } else /* we must overwrite */ { nudgecursor(same, scan, new, lno); same = 0; addch(*scan); *build = *scan; } build++; scan++; } /* maybe clear to EOL */ while (build < old + COLS && *build == ' ') { build++; } if (build < old + COLS) { nudgecursor(same, scan, new, lno); same = 0; clrtoeol(); while (build < old + COLS) { *build++ = ' '; } } } #ifndef NO_CHARATTR /* see if a given line uses character attribute strings */ int hasattr(lno, text) long lno; /* the line# of the cursor */ register char *text; /* the text of the line, from fetchline */ { static long plno; /* previous line number */ static long chgs; /* previous value of changes counter */ static int panswer;/* previous answer */ char *scan; /* if charattr is off, then the answer is "no, it doesn't" */ if (!*o_charattr) { chgs = 0; /* <- forces us to check if charattr is later set */ return FALSE; } /* if we already know the answer, return it... */ if (lno == plno && chgs == changes) { return panswer; } /* get the line & look for "\fX" */ if (!text[0] || !text[1] || !text[2]) { panswer = FALSE; } else { for (scan = text; scan[2] && !(scan[0] == '\\' && scan[1] == 'f'); scan++) { } panswer = (scan[2] != '\0'); } /* save the results */ plno = lno; chgs = changes; /* return the results */ return panswer; } #endif SHAR_EOF fi exit 0 # End of shell archive ------------------------------------------------------------------------------- Steve Kirkendall kirkenda@cs.pdx.edu uunet!tektronix!psueea!eecs!kirkenda