Path: utzoo!utgpu!news-server.csri.toronto.edu!rutgers!sun-barr!lll-winken!ncis.tis.llnl.gov!blackbird!lonex.radc.af.mil!wlbr!roger.imsd.contel.com!mh From: mh@roger.imsd.contel.com (Mike H.) Newsgroups: comp.windows.news Subject: NeWS version of elvis (the vi clone) part 5 of 9 Message-ID: <1991Jan11.020504.27314@wlbr.imsd.contel.com> Date: 11 Jan 91 02:05:04 GMT References: Sender: news@wlbr.imsd.contel.com (news) Distribution: comp Organization: Contel FSD, Westlake Village, CA Lines: 2551 Nntp-Posting-Host: roger.imsd.contel.com #! /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 the files: # main.c # misc.c # modify.c # move1.c # move2.c # move3.c # move4.c # move5.c # This archive created: Thu Jan 10 17:21:08 1991 export PATH; PATH=/bin:$PATH if test -f 'main.c' then echo shar: will not over-write existing file "'main.c'" else cat << \SHAR_EOF > 'main.c' /* main.c */ /* Author: * Steve Kirkendall * 14407 SW Teal Blvd. #C * Beaverton, OR 97005 * kirkenda@cs.pdx.edu */ /* This file contains the main() function of vi */ #include "config.h" #include #include #include "vi.h" extern trapint(); /* defined below */ extern char *getenv(); jmp_buf jmpenv; #ifndef NO_DIGRAPH static init_digraphs(); #endif /*---------------------------------------------------------------------*/ void main(argc, argv) int argc; char *argv[]; { int i; char *cmd = (char *)0; char *tag = (char *)0; char *err = (char *)0; char *str; #if MSDOS || TOS char firstarg[256]; #else char *firstarg; #endif /* set mode to MODE_VI or MODE_EX depending on program name */ switch (argv[0][strlen(argv[0]) - 1]) { case 'x': /* "ex" */ mode = MODE_EX; break; case 'w': /* "view" */ mode = MODE_VI; *o_readonly = TRUE; break; #ifndef NO_EXTENSIONS case 't': /* "edit" or "input" */ mode = MODE_VI; *o_inputmode = TRUE; break; #endif default: /* "vi" or "elvis" */ mode = MODE_VI; } #ifndef DEBUG # ifdef SIGQUIT /* normally, we ignore SIGQUIT. SIGINT is trapped later */ signal(SIGQUIT, SIG_IGN); # endif #endif /* temporarily ignore SIGINT */ signal(SIGINT, SIG_IGN); /* Before we fire up whatever form of curses we are doing let special version of elvis get any flags it needs Customflags removes any custom flags from argv so the standard elvis does'nt have to know about them */ Customflags(argv, &argc); /* start curses */ Initscr(); cbreak(); noecho(); scrollok(stdscr, TRUE); /* initialize the options */ initopts(); /* map the arrow keys. The KU,KD,KL,and KR variables correspond to * the :ku=: (etc.) termcap capabilities. The variables are defined * as part of the curses package. */ if (has_KU) mapkey(has_KU, "k", WHEN_VICMD|WHEN_INMV, ""); if (has_KD) mapkey(has_KD, "j", WHEN_VICMD|WHEN_INMV, ""); if (has_KL) mapkey(has_KL, "h", WHEN_VICMD|WHEN_INMV, ""); if (has_KR) mapkey(has_KR, "l", WHEN_VICMD|WHEN_INMV, ""); if (has_HM) mapkey(has_HM, "^", WHEN_VICMD|WHEN_INMV, ""); if (has_EN) mapkey(has_EN, "$", WHEN_VICMD|WHEN_INMV, ""); if (has_PU) mapkey(has_PU, "\002", WHEN_VICMD|WHEN_INMV, ""); if (has_PD) mapkey(has_PD, "\006", WHEN_VICMD|WHEN_INMV, ""); #if MSDOS if (*o_pcbios) { mapkey("#R", "i", WHEN_VICMD|WHEN_INMV, ""); mapkey("#S", "x", WHEN_VICMD|WHEN_INMV, ""); mapkey("#s", "B", WHEN_VICMD|WHEN_INMV, "^"); mapkey("#t", "W", WHEN_VICMD|WHEN_INMV, "^"); } #else if (ERASEKEY != '\177') { mapkey("\177", "x", WHEN_VICMD|WHEN_INMV, ""); } #endif #ifndef NO_DIGRAPH init_digraphs(); #endif /* NO_DIGRAPH */ /* process any flags */ for (i = 1; i < argc && *argv[i] == '-'; i++) { switch (argv[i][1]) { case 'R': /* readonly */ *o_readonly = TRUE; break; case 'r': /* recover */ msg("Use the `virec` program to recover lost files"); endmsgs(); refresh(); endwin(); exit(0); break; case 't': /* tag */ if (argv[i][2]) { tag = argv[i] + 2; } else { i++; tag = argv[i]; } break; case 'v': /* vi mode */ mode = MODE_VI; break; case 'e': /* ex mode */ mode = MODE_EX; break; #ifndef NO_EXTENSIONS case 'i': /* input mode */ *o_inputmode = TRUE; break; #endif #ifndef NO_ERRLIST case 'm': /* use "errlist" as the errlist */ if (argv[i][2]) { err = argv[i] + 2; } else if (i + 1 < argc) { i++; err = argv[i]; } else { err = ""; } break; #endif default: msg("Ignoring unknown flag \"%s\"", argv[i]); } } /* if we were given an initial ex command, save it... */ if (i < argc && *argv[i] == '+') { if (argv[i][1]) { cmd = argv[i++] + 1; } else { cmd = "$"; /* "vi + file" means start at EOF */ i++; } } /* the remaining args are file names. */ nargs = argc - i; if (nargs > 0) { #if ! ( MSDOS || TOS ) firstarg = argv[i]; #endif strcpy(args, argv[i]); while (++i < argc && strlen(args) + 1 + strlen(argv[i]) < sizeof args) { strcat(args, " "); strcat(args, argv[i]); } } #if ! ( MSDOS || TOS ) else { firstarg = ""; } #endif argno = 0; #if MSDOS || TOS if (nargs > 0) { strcpy(args, wildcard(args)); nargs = 1; for (i = 0; args[i]; i++) { if (args[i] == ' ') { nargs++; } } for (i = 0; args[i] && args[i] != ' '; i++) { firstarg[i] = args[i]; } firstarg[i] = '\0'; } else { firstarg[0] = '\0'; } #endif /* perform the .exrc files and EXINIT environment variable */ #ifdef SYSEXRC doexrc(SYSEXRC); #endif #ifdef HMEXRC str = getenv("HOME"); if (str) { sprintf(tmpblk.c, "%s%c%s", str, SLASH, HMEXRC); doexrc(tmpblk.c); } #endif doexrc(EXRC); #ifdef EXINIT str = getenv(EXINIT); if (str) { exstring(str, strlen(str)); } #endif /* search for a tag (or an error) now, if desired */ blkinit(); if (tag) { cmd_tag(MARK_FIRST, MARK_FIRST, CMD_TAG, 0, tag); } #ifndef NO_ERRLIST else if (err) { cmd_errlist(MARK_FIRST, MARK_FIRST, CMD_ERRLIST, 0, err); } #endif /* if no tag/err, or tag failed, then start with first arg */ if (tmpfd < 0 && Tmpstart(firstarg) == 0 && *origname) { ChangeText { } clrflag(file, MODIFIED); } /* now we do the immediate ex command that we noticed before */ if (cmd) { doexcmd(cmd); } /* repeatedly call ex() or vi() (depending on the mode) until the * mode is set to MODE_QUIT */ while (mode != MODE_QUIT) { if (setjmp(jmpenv)) { /* Maybe we just aborted a change? */ abortdo(); } #if TURBOC signal(SIGINT, (void(*)()) trapint); #else signal(SIGINT, trapint); #endif switch (mode) { case MODE_VI: Vi(); break; case MODE_EX: Ex(); break; #ifdef DEBUG default: msg("mode = %d?", mode); mode = MODE_QUIT; #endif } } /* free up the cut buffers */ cutend(); /* end curses */ #ifndef NO_CURSORSHAPE if (has_CQ) do_CQ(); #endif endmsgs(); move(LINES - 1, 0); clrtoeol(); refresh(); endwin(); exit(0); /*NOTREACHED*/ } /*ARGSUSED*/ int trapint(signo) int signo; { Resume_curses(FALSE); abortdo(); #if OSK sigmask(-1); #endif #if TURBO_C signal(signo, (void (*)())trapint); #else signal(signo, trapint); #endif longjmp(jmpenv, 1); return 0; } #ifndef NO_DIGRAPH /* This stuff us used to build the default digraphs table. */ static char digtable[][4] = { # if CS_IBMPC "C,\200", "u\"\1", "e'\2", "a^\3", "a\"\4", "a`\5", "a@\6", "c,\7", "e^\10", "e\"\211", "e`\12", "i\"\13", "i^\14", "i`\15", "A\"\16", "A@\17", "E'\20", "ae\21", "AE\22", "o^\23", "o\"\24", "o`\25", "u^\26", "u`\27", "y\"\30", "O\"\31", "U\"\32", "a'\240", "i'!", "o'\"", "u'#", "n~$", "N~%", "a-&", "o-'", "~?(", "~!-", "\"<.", "\">/", # if CS_SPECIAL "2/+", "4/,", "^+;", "^q<", "^c=", "^r>", "^t?", "pp]", "^^^", "oo_", "*a`", "*ba", "*pc", "*Sd", "*se", "*uf", "*tg", "*Ph", "*Ti", "*Oj", "*dk", "*Hl", "*hm", "*En", "*No", "eqp", "pmq", "ger", "les", "*It", "*iu", "*/v", "*=w", "sq{", "^n|", "^2}", "^3~", "^_\377", # endif /* CS_SPECIAL */ # endif /* CS_IBMPC */ # if CS_LATIN1 "~!!", "a-*", "\">+", "o-:", "\"<>", "~??", "A`@", "A'A", "A^B", "A~C", "A\"D", "A@E", "AEF", "C,G", "E`H", "E'I", "E^J", "E\"K", "I`L", "I'M", "I^N", "I\"O", "-DP", "N~Q", "O`R", "O'S", "O^T", "O~U", "O\"V", "O/X", "U`Y", "U'Z", "U^[", "U\"\\", "Y'_", "a``", "a'a", "a^b", "a~c", "a\"d", "a@e", "aef", "c,g", "e`h", "e'i", "e^j", "e\"k", "i`l", "i'm", "i^n", "i\"o", "-dp", "n~q", "o`r", "o's", "o^t", "o~u", "o\"v", "o/x", "u`y", "u'z", "u^{", "u\"|", "y'~", # endif /* CS_LATIN1 */ "" }; static init_digraphs() { int i; for (i = 0; *digtable[i]; i++) { do_digraph(FALSE, digtable[i]); } do_digraph(FALSE, (char *)0); } #endif /* NO_DIGRAPH */ SHAR_EOF fi # end of overwriting check if test -f 'misc.c' then echo shar: will not over-write existing file "'misc.c'" else cat << \SHAR_EOF > 'misc.c' /* misc.c */ /* Author: * Steve Kirkendall * 14407 SW Teal Blvd. #C * Beaverton, OR 97005 * kirkenda@cs.pdx.edu */ /* This file contains functions which didn't seem happy anywhere else */ #include "config.h" #include "vi.h" /* find a particular line & return a pointer to a copy of its text */ char *fetchline(line) long line; /* line number of the line to fetch */ { int i; REG char *scan; /* used to search for the line in a BLK */ long l; /* line number counter */ static BLK buf; /* holds ONLY the selected line (as string) */ REG char *cpy; /* used while copying the line */ static long nextline; /* } These four variables are used */ static long chglevel; /* } to implement a shortcut when */ static char *nextscan; /* } consecutive lines are fetched */ static long nextlnum; /* } */ /* can we do a shortcut? */ if (changes == chglevel && line == nextline) { scan = nextscan; } else { /* scan lnum[] to determine which block its in */ for (i = 1; line > lnum[i]; i++) { } nextlnum = lnum[i]; /* fetch text of the block containing that line */ scan = blkget(i)->c; /* find the line in the block */ for (l = lnum[i - 1]; ++l < line; ) { while (*scan++ != '\n') { } } } /* copy it into a block by itself, with no newline */ for (cpy = buf.c; *scan != '\n'; ) { *cpy++ = *scan++; } *cpy = '\0'; /* maybe speed up the next call to fetchline() ? */ if (line < nextlnum) { nextline = line + 1; chglevel = changes; nextscan = scan + 1; } else { nextline = 0; } /* Calls to fetchline() interfere with calls to pfetch(). Make sure * that pfetch() resets itself on its next invocation. */ pchgs = 0L; /* Return a pointer to the line's text */ return buf.c; } /* error message from the regexp code */ void regerror(txt) char *txt; /* an error message */ { msg("RE error: %s", txt); } /* This function is equivelent to the pfetch() macro */ void pfetch(l) long l; /* line number of line to fetch */ { if(l != pline || changes != pchgs) { pline = (l); ptext = fetchline(pline); plen = strlen(ptext); pchgs = changes; } } SHAR_EOF fi # end of overwriting check 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" #ifdef DEBUG # include static FILE *dbg; /*VARARGS1*/ debout(msg, arg1, arg2, arg3, arg4, arg5) char *msg, *arg1, *arg2, *arg3, *arg4, *arg5; { if (!dbg) { dbg = fopen("debug.out", "w"); setbuf(dbg, (FILE *)0); } fprintf(dbg, msg, arg1, arg2, arg3, arg4, arg5); } #endif /* DEBUG */ /* delete a range of text from the file */ void 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 */ REG char *scan; /* used to scan thru text of the blk */ REG char *cpy; /* used when copying chars */ BLK *blk; /* a text block */ long l; /* a line number */ MARK m; /* a traveling version of frommark */ #ifdef DEBUG debout("delete(%ld.%d, %ld.%d)\n", markline(frommark), markidx(frommark), markline(tomark), markidx(tomark)); #endif /* if not deleting anything, quit now */ if (frommark == tomark) { return; } /* This is a change */ changes++; significant = TRUE; /* 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 */ void add(atmark, newtext) MARK atmark; /* where to insert the new text */ char *newtext; /* NUL-terminated string to insert */ { REG char *scan; /* used to move through string */ REG 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) */ REG 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; #ifdef DEBUG debout("add(%ld.%d, \"%s\")\n", markline(atmark), markidx(atmark), newtext); #endif #ifdef lint buf.c[0] = 0; #endif /* if not adding anything, return now */ if (!*newtext) { return; } /* This is a change */ changes++; significant = TRUE; /* 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 && lastpart == 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) { /* earlier line, or earlier in same line: no change */ continue; } else if (markline(mark[i]) > l) { /* later line: move down a whole number of lines */ mark[i] += MARK_AT_LINE(addlines); } else { /* later in same line */ if (addlines > 0) { /* multi-line add, which split this line: * move down, and possibly left or right, * depending on where the split was and how * much text was inserted after the last \n */ mark[i] += MARK_AT_LINE(addlines) + lastpart - markidx(atmark); } else { /* totally within this line: move right */ mark[i] += 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 */ void change(frommark, tomark, newtext) MARK frommark, tomark; char *newtext; { int i; long l; char *text; BLK *blk; #ifdef DEBUG debout("change(%ld.%d, %ld.%d, \"%s\")\n", markline(frommark), markidx(frommark), markline(tomark), markidx(tomark), newtext); #endif /* 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++; significant = TRUE; 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 # end of overwriting check if test -f 'move1.c' then echo shar: will not over-write existing file "'move1.c'" else cat << \SHAR_EOF > 'move1.c' /* move1.c */ /* Author: * Steve Kirkendall * 14407 SW Teal Blvd. #C * Beaverton, OR 97005 * kirkenda@cs.pdx.edu */ /* This file contains most movement functions */ #include "config.h" #include #include "vi.h" #ifndef isascii # define isascii(c) !((c) & ~0x7f) #endif MARK m_updnto(m, cnt, cmd) MARK m; /* movement is relative to this mark */ long cnt; /* a numeric argument */ { DEFAULT(cmd == 'G' ? nlines : 1L); /* move up or down 'cnt' lines */ switch (cmd) { case ('P'&0x1f): case '-': case 'k': m -= MARK_AT_LINE(cnt); break; case 'G': if (cnt < 1L || cnt > nlines) { msg("Only %ld lines", nlines); return MARK_UNSET; } m = MARK_AT_LINE(cnt); break; default: m += MARK_AT_LINE(cnt); } /* if that left us screwed up, then fail */ if (m < MARK_FIRST || markline(m) > nlines) { return MARK_UNSET; } 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_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 */ { REG char *text; REG 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 */ { REG char *text; /* used to scan thru text */ REG 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 (ignored) */ { long l; REG char *text; REG char match; REG char nest; REG 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 # end of overwriting check if test -f 'move2.c' then echo shar: will not over-write existing file "'move2.c'" else cat << \SHAR_EOF > 'move2.c' /* move2.c */ /* Author: * Steve Kirkendall * 14407 SW Teal Blvd. #C * Beaverton, OR 97005 * kirkenda@cs.pdx.edu */ /* This function contains the movement functions that perform RE searching */ #include "config.h" #include "vi.h" #include "regexp.h" extern long atol(); 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, (char *)0); prevsf = TRUE; } else { m = m_bsrch(m, (char *)0); prevsf = FALSE; } return m; } MARK m_Nsrch(m) MARK m; /* where to start searching */ { if (prevsf) { m = m_bsrch(m, (char *)0); prevsf = TRUE; } else { m = m_fsrch(m, (char *)0); 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 */ long delta; /* line offset, for things like "/foo/+1" */ /* remember: "previous search was forward" */ prevsf = TRUE; delta = 0L; if (ptrn && *ptrn) { /* locate the closing '/', if any */ line = parseptrn(ptrn); if (*line) { delta = atol(line); } 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)"); if (delta != 0L) { l += delta; if (l < 1 || l > nlines) { msg("search offset too big"); return MARK_UNSET; } return m_front(MARK_AT_LINE(l), 0L); } 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) { /* locate the closing '?', if any */ line = parseptrn(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 # end of overwriting check if test -f 'move3.c' then echo shar: will not over-write existing file "'move3.c'" else cat << \SHAR_EOF > 'move3.c' /* move3.c */ /* Author: * Steve Kirkendall * 14407 SW Teal Blvd. #C * Beaverton, OR 97005 * kirkenda@cs.pdx.edu */ /* 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 */ { REG 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 */ { REG 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 # end of overwriting check if test -f 'move4.c' then echo shar: will not over-write existing file "'move4.c'" else cat << \SHAR_EOF > 'move4.c' /* move4.c */ /* Author: * Steve Kirkendall * 14407 SW Teal Blvd. #C * Beaverton, OR 97005 * kirkenda@cs.pdx.edu */ /* This file contains movement functions which are screen-relative */ #include "config.h" #include "vi.h" /* This moves 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': #if OSK case '\l': #else case '\r': #endif 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++; /* also, erase the statusline. This happens naturally for * the scrolling commands, but the paging commands need to * explicitly clear the statusline. */ msg(""); } return m; } SHAR_EOF fi # end of overwriting check if test -f 'move5.c' then echo shar: will not over-write existing file "'move5.c'" else cat << \SHAR_EOF > 'move5.c' /* move5.c */ /* Author: * Steve Kirkendall * 14407 SW Teal Blvd. #C * Beaverton, OR 97005 * kirkenda@cs.pdx.edu */ /* This file contains the word-oriented movement functions */ #include #include "config.h" #include "vi.h" #ifndef isascii # define isascii(c) !((c) & ~0x7f) #endif MARK m_fword(m, cnt, cmd) MARK m; /* movement is relative to this mark */ long cnt; /* a numeric argument */ int cmd; /* either 'w' or 'W' */ { REG long l; REG char *text; REG int i; DEFAULT(1); l = markline(m); pfetch(l); text = ptext + markidx(m); while (cnt-- > 0) /* yes, ASSIGNMENT! */ { i = *text++; if (cmd == 'W') { /* include any non-whitespace */ while (i && (!isascii(i) || !isspace(i))) { i = *text++; } } else 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, cmd) MARK m; /* movement is relative to this mark */ long cnt; /* a numeric argument */ int cmd; /* either 'b' or 'B' */ { REG long l; REG 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 (cmd == 'B') { /* include any non-whitespace */ while (text >= ptext && (!isascii(*text) || !isspace(*text))) { text--; } } else 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, cmd) MARK m; /* movement is relative to this mark */ long cnt; /* a numeric argument */ int cmd; /* either 'e' or 'E' */ { REG long l; REG char *text; REG 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 (cmd == 'E') { /* include any non-whitespace */ while (i && (!isascii(i) || !isspace(i))) { i = *text++; } } else 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; } SHAR_EOF fi # end of overwriting check # End of shell archive exit 0