#! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh 'stevie/normal.c' <<'END_OF_FILE' X/* $Header: /nw/tony/src/stevie/src/RCS/normal.c,v 1.25 89/08/06 09:50:25 tony Exp $ X * X * Contains the main routine for processing characters in command mode. X * Communicates closely with the code in ops.c to handle the operators. X */ X X#include "stevie.h" X#include "ops.h" X X/* X * Generally speaking, every command in normal() should either clear any X * pending operator (with CLEAROP), or set the motion type variable. X */ X X#define CLEAROP (operator=NOP) /* clear any pending operator */ X Xint operator = NOP; /* current pending operator */ Xint mtype; /* type of the current cursor motion */ Xbool_t mincl; /* true if char motion is inclusive */ XLPTR startop; /* cursor pos. at start of operator */ X X/* X * Operators can have counts either before the operator, or between the X * operator and the following cursor motion as in: X * X * d3w or 3dw X * X * If a count is given before the operator, it is saved in opnum. If X * normal() is called with a pending operator, the count in opnum (if X * present) overrides any count that came later. X */ Xstatic int opnum = 0; X X#define DEFAULT1(x) (((x) == 0) ? 1 : (x)) X X/* X * normal(c) X * X * Execute a command in command mode. X * X * This is basically a big switch with the cases arranged in rough categories X * in the following order: X * X * 1. File positioning commands X * 2. Control commands (e.g. ^G, Z, screen redraw, etc) X * 3. Character motions X * 4. Search commands (of various kinds) X * 5. Edit commands (e.g. J, x, X) X * 6. Insert commands (e.g. i, o, O, A) X * 7. Operators X * 8. Abbreviations (e.g. D, C) X * 9. Marks X */ Xvoid Xnormal(c) Xregister int c; X{ X register int n; X register char *s; /* temporary variable for misc. strings */ X bool_t flag = FALSE; X int type = 0; /* used in some operations to modify type */ X int dir = FORWARD; /* search direction */ X int nchar = NUL; X bool_t finish_op; X X /* X * If there is an operator pending, then the command we take X * this time will terminate it. Finish_op tells us to finish X * the operation before returning this time (unless the operation X * was cancelled. X */ X finish_op = (operator != NOP); X X /* X * If we're in the middle of an operator AND we had a count before X * the operator, then that count overrides the current value of X * Prenum. What this means effectively, is that commands like X * "3dw" get turned into "d3w" which makes things fall into place X * pretty neatly. X */ X if (finish_op) { X if (opnum != 0) X Prenum = opnum; X } else X opnum = 0; X X u_lcheck(); /* clear the "line undo" buffer if we've moved */ X X switch (c & 0xff) { X X /* X * Screen positioning commands X */ X case CTRL('D'): X CLEAROP; X if (Prenum) X P(P_SS) = (Prenum > Rows-1) ? Rows-1 : Prenum; X scrollup(P(P_SS)); X onedown(P(P_SS)); X updatescreen(); X break; X X case CTRL('U'): X CLEAROP; X if (Prenum) X P(P_SS) = (Prenum > Rows-1) ? Rows-1 : Prenum; X scrolldown(P(P_SS)); X oneup(P(P_SS)); X updatescreen(); X break; X X /* X * This is kind of a hack. If we're moving by one page, the calls X * to stuffin() do exactly the right thing in terms of leaving X * some context, and so on. If a count was given, we don't have X * to worry about these issues. X */ X case CTRL('F'): X CLEAROP; X n = DEFAULT1(Prenum); X if (n > 1) { X if ( ! onedown(Rows * n) ) X beep(); X cursupdate(); X } else { X /* screenclear(); */ X stuffin("Lz\nM"); X } X break; X X case CTRL('B'): X CLEAROP; X n = DEFAULT1(Prenum); X if (n > 1) { X if ( ! oneup(Rows * n) ) X beep(); X cursupdate(); X } else { X /* screenclear(); */ X stuffin("Hz-M"); X } X break; X X case CTRL('E'): X CLEAROP; X scrollup(DEFAULT1(Prenum)); X updatescreen(); X break; X X case CTRL('Y'): X CLEAROP; X scrolldown(DEFAULT1(Prenum)); X updatescreen(); X break; X X case 'z': X CLEAROP; X switch (vgetc()) { X case NL: /* put Curschar at top of screen */ X case CR: X *Topchar = *Curschar; X Topchar->index = 0; X updatescreen(); X break; X X case '.': /* put Curschar in middle of screen */ X n = Rows/2; X goto dozcmd; X X case '-': /* put Curschar at bottom of screen */ X n = Rows-1; X /* fall through */ X X dozcmd: X { X register LPTR *lp = Curschar; X register int l = 0; X X while ((l < n) && (lp != NULL)) { X l += plines(lp); X *Topchar = *lp; X lp = prevline(lp); X } X } X Topchar->index = 0; X updatescreen(); X break; X X default: X beep(); X } X break; X X /* X * Control commands X */ X case ':': X CLEAROP; X if ((s = getcmdln(c)) != NULL) X docmdln(s); X break; X X case K_HELP: X CLEAROP; X if (vi_help()) { X screenclear(); X updatescreen(); X } X break; X X case CTRL('L'): X CLEAROP; X screenclear(); X updatescreen(); X break; X X X case CTRL('O'): /* ignored */ X /* X * A command that's ignored can be useful. We use it at X * times when we want to postpone redraws. By stuffing X * in a control-o, redraws get suspended until the editor X * gets back around to processing input. X */ X break; X X case CTRL('G'): X CLEAROP; X fileinfo(); X break; X X case K_CCIRCM: /* shorthand command */ X CLEAROP; X#ifdef TAGSTACK X /* If tag stacking compiled in & enabled, this is an untag. X * Otherwise, or if tag stack empty, edit alternate file. X * "untage" is so interpreted by dountag(). X */ X if (P(P_TG)) X stuffin(":untage\n"); X else X#endif X stuffin(":e #\n"); X break; X X case 'Z': /* write, if changed, and exit */ X if (vgetc() != 'Z') { X beep(); X break; X } X doxit(); X break; X X /* X * Macro evaluates true if char 'c' is a valid identifier character X */ X# define IDCHAR(c) (isalpha(c) || isdigit(c) || (c) == '_') X X case CTRL(']'): /* :ta to current identifier */ X CLEAROP; X { X char ch; X LPTR save; X X save = *Curschar; X /* X * First back up to start of identifier. This X * doesn't match the real vi but I like it a X * little better and it shouldn't bother anyone. X */ X ch = gchar(Curschar); X while (IDCHAR(ch)) { X if (!oneleft()) X break; X ch = gchar(Curschar); X } X if (!IDCHAR(ch)) X oneright(); X X stuffin(":ta "); X /* X * Now grab the chars in the identifier X */ X ch = gchar(Curschar); X while (IDCHAR(ch)) { X stuffin(mkstr(ch)); X if (!oneright()) X break; X ch = gchar(Curschar); X } X stuffin("\n"); X X *Curschar = save; /* restore, in case of error */ X } X break; X X /* X * Character motion commands X */ X case 'G': X mtype = MLINE; X *Curschar = *gotoline(Prenum); X beginline(TRUE); X break; X X case 'H': X mtype = MLINE; X *Curschar = *Topchar; X for (n = Prenum; n && onedown(1) ;n--) X ; X beginline(TRUE); X break; X X case 'M': X mtype = MLINE; X *Curschar = *Topchar; X for (n = 0; n < Rows/2 && onedown(1) ;n++) X ; X beginline(TRUE); X break; X X case 'L': X mtype = MLINE; X *Curschar = *prevline(Botchar); X for (n = Prenum; n && oneup(1) ;n--) X ; X beginline(TRUE); X break; X X case 'l': X case K_RARROW: X case ' ': X mtype = MCHAR; X mincl = FALSE; X n = DEFAULT1(Prenum); X while (n--) { X if ( ! oneright() ) { X beep(); X break; X } X } X set_want_col = TRUE; X break; X X case 'h': X case K_LARROW: X case CTRL('H'): X mtype = MCHAR; X mincl = FALSE; X n = DEFAULT1(Prenum); X while (n--) { X if ( ! oneleft() ) { X beep(); X break; X } X } X set_want_col = TRUE; X break; X X case '-': X flag = TRUE; X /* fall through */ X X case 'k': X case K_UARROW: X case CTRL('P'): X mtype = MLINE; X if ( ! oneup(DEFAULT1(Prenum)) ) X beep(); X if (flag) X beginline(TRUE); X break; X X case '+': X case CR: X case NL: X flag = TRUE; X /* fall through */ X X case 'j': X case K_DARROW: X case CTRL('N'): X mtype = MLINE; X if ( ! onedown(DEFAULT1(Prenum)) ) X beep(); X if (flag) X beginline(TRUE); X break; X X /* X * This is a strange motion command that helps make operators X * more logical. It is actually implemented, but not documented X * in the real 'vi'. This motion command actually refers to "the X * current line". Commands like "dd" and "yy" are really an alternate X * form of "d_" and "y_". It does accept a count, so "d3_" works to X * delete 3 lines. X */ X case '_': X lineop: X mtype = MLINE; X onedown(DEFAULT1(Prenum)-1); X break; X X case '|': X mtype = MCHAR; X mincl = TRUE; X beginline(FALSE); X if (Prenum > 0) X *Curschar = *coladvance(Curschar, Prenum-1); X Curswant = Prenum - 1; X break; X X /* X * Word Motions X */ X X case 'B': X type = 1; X /* fall through */ X X case 'b': X mtype = MCHAR; X mincl = FALSE; X set_want_col = TRUE; X for (n = DEFAULT1(Prenum); n > 0 ;n--) { X LPTR *pos; X X if ((pos = bck_word(Curschar, type)) == NULL) { X beep(); X CLEAROP; X break; X } else X *Curschar = *pos; X } X break; X X case 'W': X type = 1; X /* fall through */ X X case 'w': X /* X * This is a little strange. To match what the real vi X * does, we effectively map 'cw' to 'ce', and 'cW' to 'cE'. X * This seems impolite at first, but it's really more X * what we mean when we say 'cw'. X */ X if (operator == CHANGE) X goto doecmd; X X mtype = MCHAR; X mincl = FALSE; X set_want_col = TRUE; X for (n = DEFAULT1(Prenum); n > 0 ;n--) { X LPTR *pos; X X if ((pos = fwd_word(Curschar, type)) == NULL) { X beep(); X CLEAROP; X break; X } else X *Curschar = *pos; X } X break; X X case 'E': X type = 1; X /* fall through */ X X case 'e': X doecmd: X mtype = MCHAR; X mincl = TRUE; X set_want_col = TRUE; X for (n = DEFAULT1(Prenum); n > 0 ;n--) { X LPTR *pos; X X /* X * The first motion gets special treatment if we're X * do a 'CHANGE'. X */ X if (n == DEFAULT1(Prenum)) X pos = end_word(Curschar,type,operator==CHANGE); X else X pos = end_word(Curschar, type, FALSE); X X if (pos == NULL) { X beep(); X CLEAROP; X break; X } else X *Curschar = *pos; X } X break; X X case '$': X mtype = MCHAR; X mincl = TRUE; X while ( oneright() ) X ; X Curswant = 999; /* so we stay at the end */ X break; X X case '^': X mtype = MCHAR; X mincl = FALSE; X beginline(TRUE); X break; X X case '0': X mtype = MCHAR; X mincl = TRUE; X beginline(FALSE); X break; X X /* X * Searches of various kinds X */ X case '?': X case '/': X s = getcmdln(c); /* get the search string */ X X /* X * If they backspaced out of the search command, X * just bag everything. X */ X if (s == NULL) { X CLEAROP; X break; X } X X mtype = MCHAR; X mincl = FALSE; X set_want_col = TRUE; X X /* X * If no string given, pass NULL to repeat the prior search. X * If the search fails, abort any pending operator. X */ X if (!dosearch( X (c == '/') ? FORWARD : BACKWARD, X (*s == NUL) ? NULL : s X )) X CLEAROP; X break; X X case 'n': X mtype = MCHAR; X mincl = FALSE; X set_want_col = TRUE; X if (!repsearch(0)) X CLEAROP; X break; X X case 'N': X mtype = MCHAR; X mincl = FALSE; X set_want_col = TRUE; X if (!repsearch(1)) X CLEAROP; X break; X X /* X * Character searches X */ X case 'T': X dir = BACKWARD; X /* fall through */ X X case 't': X type = 1; X goto docsearch; X X case 'F': X dir = BACKWARD; X /* fall through */ X X case 'f': X docsearch: X mtype = MCHAR; X mincl = TRUE; X set_want_col = TRUE; X if ((nchar = vgetc()) == ESC) /* search char */ X break; X X for (n = DEFAULT1(Prenum); n > 0 ;n--) { X if (!searchc(nchar, dir, type)) { X CLEAROP; X beep(); X break; X } X } X break; X X case ',': X flag = 1; X /* fall through */ X X case ';': X mtype = MCHAR; X mincl = TRUE; X set_want_col = TRUE; X for (n = DEFAULT1(Prenum); n > 0 ;n--) { X if (!crepsearch(flag)) { X CLEAROP; X beep(); X break; X } X } X break; X X case '(': /* sentence searches */ X dir = BACKWARD; X /* fall through */ X X case ')': X mtype = MCHAR; X mincl = FALSE; X set_want_col = TRUE; X if (findsent(dir) == NULL) { X beep(); X CLEAROP; X } X break; X X case '{': /* paragraph searches */ X dir = BACKWARD; X /* fall through */ X X case '}': X mtype = MCHAR; X mincl = FALSE; X set_want_col = TRUE; X if (!findpara(dir)) { X beep(); X CLEAROP; X } X break; X X case '[': /* function searches */ X dir = BACKWARD; X /* fall through */ X X case ']': X mtype = MLINE; X set_want_col = TRUE; X if (vgetc() != c) { X beep(); X CLEAROP; X break; X } X X if (!findfunc(dir)) { X beep(); X CLEAROP; X } X break; X X case '%': X mtype = MCHAR; X mincl = TRUE; X { X LPTR *pos; X X if ((pos = showmatch()) == NULL) { X beep(); X CLEAROP; X } else { X setpcmark(); X *Curschar = *pos; X set_want_col = TRUE; X } X } X break; X X /* X * Edits X */ X case '.': /* repeat last change (usually) */ X /* X * If a delete is in effect, we let '.' help out the same X * way that '_' helps for some line operations. It's like X * an 'l', but subtracts one from the count and is inclusive. X */ X if (operator == DELETE || operator == CHANGE) { X if (Prenum != 0) { X n = DEFAULT1(Prenum) - 1; X while (n--) X if (! oneright()) X break; X } X mtype = MCHAR; X mincl = TRUE; X } else { /* a normal 'redo' */ X CLEAROP; X stuffin(Redobuff); X } X break; X X case 'u': X case K_UNDO: X CLEAROP; X u_undo(); X break; X X case 'U': X CLEAROP; X u_lundo(); X break; X X case 'x': X CLEAROP; X if (lineempty()) { /* can't do it on a blank line */ X beep(); X break; X } X if (Prenum) X stuffnum(Prenum); X stuffin("d."); X break; X X case 'X': X CLEAROP; X if (!oneleft()) X beep(); X else { X strcpy(Redobuff, "X"); X u_saveline(); X delchar(TRUE); X updateline(); X } X break; X X case 'r': X CLEAROP; X if (lineempty()) { /* Nothing to replace */ X beep(); X break; X } X if ((nchar = vgetc()) == ESC) X break; X X if (nchar==CR || nchar==NL) { X stuffin("R\n\033"); X break; X } X X if (nchar & 0x80) { X beep(); X break; X } X u_saveline(); X X /* Change current character. */ X pchar(Curschar, nchar); X X /* Save stuff necessary to redo it */ X sprintf(Redobuff, "r%c", nchar); X X CHANGED; X updateline(); X break; X X case '~': /* swap case */ X if (!P(P_TO)) { X CLEAROP; X if (lineempty()) { X beep(); X break; X } X c = gchar(Curschar); X X if (isalpha(c)) { X if (islower(c)) X c = toupper(c); X else X c = tolower(c); X } X u_saveline(); X X pchar(Curschar, c); /* Change current character. */ X oneright(); X X strcpy(Redobuff, "~"); X X CHANGED; X updateline(); X } X#ifdef TILDEOP X else { X if (operator == TILDE) /* handle '~~' */ X goto lineop; X if (Prenum != 0) X opnum = Prenum; X startop = *Curschar; X operator = TILDE; X } X#endif X X break; X X case 'J': X CLEAROP; X X u_save(Curschar->linep->prev, Curschar->linep->next->next); X X if (!dojoin(TRUE)) X beep(); X X strcpy(Redobuff, "J"); X updatescreen(); X break; X X /* X * Inserts X */ X case 'A': X set_want_col = TRUE; X while (oneright()) X ; X /* fall through */ X X case 'a': X CLEAROP; X /* Works just like an 'i'nsert on the next character. */ X if (!lineempty()) X inc(Curschar); X u_saveline(); X startinsert(mkstr(c), FALSE); X break; X X case 'I': X beginline(TRUE); X /* fall through */ X X case 'i': X case K_INSERT: X CLEAROP; X u_saveline(); X startinsert(mkstr(c), FALSE); X break; X X case 'o': X CLEAROP; X u_save(Curschar->linep, Curschar->linep->next); X opencmd(FORWARD, TRUE); X startinsert("o", TRUE); X break; X X case 'O': X CLEAROP; X u_save(Curschar->linep->prev, Curschar->linep); X opencmd(BACKWARD, TRUE); X startinsert("O", TRUE); X break; X X case 'R': X CLEAROP; X u_saveline(); X startinsert("R", FALSE); X break; X X /* X * Operators X */ X case 'd': X if (operator == DELETE) /* handle 'dd' */ X goto lineop; X if (Prenum != 0) X opnum = Prenum; X startop = *Curschar; X operator = DELETE; X break; X X case 'c': X if (operator == CHANGE) /* handle 'cc' */ X goto lineop; X if (Prenum != 0) X opnum = Prenum; X startop = *Curschar; X operator = CHANGE; X break; X X case 'y': X if (operator == YANK) /* handle 'yy' */ X goto lineop; X if (Prenum != 0) X opnum = Prenum; X startop = *Curschar; X operator = YANK; X break; X X case '>': X if (operator == RSHIFT) /* handle >> */ X goto lineop; X if (Prenum != 0) X opnum = Prenum; X startop = *Curschar; X operator = RSHIFT; X break; X X case '<': X if (operator == LSHIFT) /* handle << */ X goto lineop; X if (Prenum != 0) X opnum = Prenum; X startop = *Curschar; /* save current position */ X operator = LSHIFT; X break; X X case '!': X if (operator == FILTER) /* handle '!!' */ X goto lineop; X if (Prenum != 0) X opnum = Prenum; X startop = *Curschar; X operator = FILTER; X break; X X case 'p': X doput(FORWARD); X break; X X case 'P': X doput(BACKWARD); X break; X X /* X * Abbreviations X */ X case 'D': X stuffin("d$"); X break; X X case 'Y': X if (Prenum) X stuffnum(Prenum); X stuffin("yy"); X break; X X case 'C': X stuffin("c$"); X break; X X case 's': /* substitute characters */ X if (lineempty()) { /* can't do it on a blank line */ X CLEAROP; X beep(); X break; X } X if (Prenum) X stuffnum(Prenum); X stuffin("c."); X break; X X /* X * Marks X */ X case 'm': X CLEAROP; X if (!setmark(vgetc())) X beep(); X break; X X case '\'': X flag = TRUE; X /* fall through */ X X case '`': X { X LPTR mtmp, *mark; X X mark = getmark(vgetc()); X if (mark == NULL) { X beep(); X CLEAROP; X } else { X mtmp = *mark; X setpcmark(); X *Curschar = mtmp; X if (flag) X beginline(TRUE); X } X mtype = flag ? MLINE : MCHAR; X mincl = FALSE; /* ignored if not MCHAR */ X set_want_col = TRUE; X } X break; X X default: X CLEAROP; X beep(); X break; X } X X /* X * If an operation is pending, handle it... X */ X if (finish_op) { /* we just finished an operator */ X if (operator == NOP) /* ... but it was cancelled */ X return; X X switch (operator) { X X case LSHIFT: X case RSHIFT: X doshift(operator, c, nchar, Prenum); X break; X X case DELETE: X dodelete(c, nchar, Prenum); X break; X X case YANK: X (void) doyank(); /* no redo on yank... */ X break; X X case CHANGE: X dochange(c, nchar, Prenum); X break; X X case FILTER: X dofilter(c, nchar, Prenum); X break; X X#ifdef TILDEOP X case TILDE: X dotilde(c, nchar, Prenum); X break; X#endif X X default: X beep(); X } X operator = NOP; X } X} END_OF_FILE if test 17903 -ne `wc -c <'stevie/normal.c'`; then echo shar: \"'stevie/normal.c'\" unpacked with wrong size! fi # end of 'stevie/normal.c' fi if test -f 'stevie/screen.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'stevie/screen.c'\" else echo shar: Extracting \"'stevie/screen.c'\" \(15945 characters\) sed "s/^X//" >'stevie/screen.c' <<'END_OF_FILE' X/* $Header: /nw/tony/src/stevie/src/RCS/screen.c,v 1.8 89/08/02 09:26:33 tony Exp $ X * X * Routines to manipulate the screen representations. X */ X X#include "stevie.h" X X/* X * This gets set if we ignored an update request while input was pending. X * We check this when the input is drained to see if the screen should be X * updated. X */ Xbool_t need_redraw = FALSE; X X/* X * The following variable is set (in filetonext) to the number of physical X * lines taken by the line the cursor is on. We use this to avoid extra X * calls to plines(). The optimized routines lfiletonext() and lnexttoscreen() X * make sure that the size of the cursor line hasn't changed. If so, lines X * below the cursor will move up or down and we need to call the routines X * filetonext() and nexttoscreen() to examine the entire screen. X */ Xstatic int Cline_size; /* size (in rows) of the cursor line */ Xstatic int Cline_row; /* starting row of the cursor line */ X Xstatic char *mkline(); /* calculate line string for "number" mode */ X X/* X * filetonext() X * X * Based on the current value of Topchar, transfer a screenfull of X * stuff from Filemem to Nextscreen, and update Botchar. X */ X Xstatic void Xfiletonext() X{ X register int row, col; X register char *screenp = Nextscreen; X LPTR memp; X LPTR save; /* save pos. in case line won't fit */ X register char *endscreen; X register char *nextrow; X char extra[16]; X int nextra = 0; X register int c; X int n; X bool_t done; /* if TRUE, we hit the end of the file */ X bool_t didline; /* if TRUE, we finished the last line */ X int srow; /* starting row of the current line */ X int lno; /* number of the line we're doing */ X int coff; /* column offset */ X X coff = P(P_NU) ? 8 : 0; X X save = memp = *Topchar; X X if (P(P_NU)) X lno = cntllines(Filemem, Topchar); X X /* X * The number of rows shown is Rows-1. X * The last line is the status/command line. X */ X endscreen = &screenp[(Rows-1)*Columns]; X X done = didline = FALSE; X srow = row = col = 0; X /* X * We go one past the end of the screen so we can find out if the X * last line fit on the screen or not. X */ X while ( screenp <= endscreen && !done) { X X X if (P(P_NU) && col == 0 && memp.index == 0) { X strcpy(extra, mkline(lno++)); X nextra = 8; X } X X /* Get the next character to put on the screen. */ X X /* The 'extra' array contains the extra stuff that is */ X /* inserted to represent special characters (tabs, and */ X /* other non-printable stuff. The order in the 'extra' */ X /* array is reversed. */ X X if ( nextra > 0 ) X c = extra[--nextra]; X else { X c = (unsigned)(0xff & gchar(&memp)); X if (inc(&memp) == -1) X done = 1; X /* when getting a character from the file, we */ X /* may have to turn it into something else on */ X /* the way to putting it into 'Nextscreen'. */ X if ( c == TAB && !P(P_LS) ) { X strcpy(extra," "); X /* tab amount depends on current column */ X nextra = ((P(P_TS)-1) - (col - coff)%P(P_TS)); X c = ' '; X } X else if ( c == NUL && P(P_LS) ) { X extra[0] = NUL; X nextra = 1; X c = '$'; X } else if ( (n = chars[c].ch_size) > 1 ) { X char *p; X nextra = 0; X p = chars[c].ch_str; X /* copy 'ch-str'ing into 'extra' in reverse */ X while ( n > 1 ) X extra[nextra++] = p[--n]; X c = p[0]; X } X } X X if (screenp == endscreen) { X /* X * We're one past the end of the screen. If the X * current character is null, then we really did X * finish, so set didline = TRUE. In either case, X * break out because we're done. X */ X dec(&memp); X if (memp.index != 0 && c == NUL) { X didline = TRUE; X inc(&memp); X } X break; X } X X if ( c == NUL ) { X srow = ++row; X /* X * Save this position in case the next line won't X * fit on the screen completely. X */ X save = memp; X /* get pointer to start of next row */ X nextrow = &Nextscreen[row*Columns]; X /* blank out the rest of this row */ X while ( screenp != nextrow ) X *screenp++ = ' '; X col = 0; X continue; X } X if ( col >= Columns ) { X row++; X col = 0; X } X /* store the character in Nextscreen */ X *screenp++ = c; X col++; X } X /* X * If we didn't hit the end of the file, and we didn't finish X * the last line we were working on, then the line didn't fit. X */ X if (!done && !didline) { X /* X * Clear the rest of the screen and mark the unused lines. X */ X screenp = &Nextscreen[srow * Columns]; X while (screenp < endscreen) X *screenp++ = ' '; X for (; srow < (Rows-1) ;srow++) X Nextscreen[srow * Columns] = '@'; X *Botchar = save; X return; X } X /* make sure the rest of the screen is blank */ X while ( screenp < endscreen ) X *screenp++ = ' '; X /* put '~'s on rows that aren't part of the file. */ X if ( col != 0 ) X row++; X while ( row < Rows ) { X Nextscreen[row*Columns] = '~'; X row++; X } X if (done) /* we hit the end of the file */ X *Botchar = *Fileend; X else X *Botchar = memp; /* FIX - prev? */ X} X X/* X * nexttoscreen X * X * Transfer the contents of Nextscreen to the screen, using Realscreen X * to avoid unnecessary output. X */ Xstatic void Xnexttoscreen() X{ X register char *np = Nextscreen; X register char *rp = Realscreen; X register char *endscreen; X register int row = 0, col = 0; X int gorow = -1, gocol = -1; X X if (anyinput()) { X need_redraw = TRUE; X return; X } X X endscreen = &np[(Rows-1)*Columns]; X X CUROFF; /* disable cursor */ X X for ( ; np < endscreen ; np++,rp++ ) { X /* If desired screen (contents of Nextscreen) does not */ X /* match what's really there, put it there. */ X if ( *np != *rp ) { X /* if we are positioned at the right place, */ X /* we don't have to use windgoto(). */ X if (gocol != col || gorow != row) { X /* X * If we're just off by one, don't send X * an entire esc. seq. (this happens a lot!) X */ X if (gorow == row && gocol+1 == col) { X outchar(*(np-1)); X gocol++; X } else X windgoto(gorow=row,gocol=col); X } X outchar(*rp = *np); X gocol++; X } X if ( ++col >= Columns ) { X col = 0; X row++; X } X } X CURON; /* enable cursor again */ X} X X/* X * lfiletonext() - like filetonext() but only for cursor line X * X * Returns true if the size of the cursor line (in rows) hasn't changed. X * This determines whether or not we need to call filetonext() to examine X * the entire screen for changes. X */ Xstatic bool_t Xlfiletonext() X{ X register int row, col; X register char *screenp; X LPTR memp; X register char *nextrow; X char extra[16]; X int nextra = 0; X register int c; X int n; X bool_t eof; X int lno; /* number of the line we're doing */ X int coff; /* column offset */ X X coff = P(P_NU) ? 8 : 0; X X /* X * This should be done more efficiently. X */ X if (P(P_NU)) X lno = cntllines(Filemem, Curschar); X X screenp = Nextscreen + (Cline_row * Columns); X X memp = *Curschar; X memp.index = 0; X X eof = FALSE; X col = 0; X row = Cline_row; X X while (!eof) { X X if (P(P_NU) && col == 0 && memp.index == 0) { X strcpy(extra, mkline(lno)); X nextra = 8; X } X X /* Get the next character to put on the screen. */ X X /* The 'extra' array contains the extra stuff that is */ X /* inserted to represent special characters (tabs, and */ X /* other non-printable stuff. The order in the 'extra' */ X /* array is reversed. */ X X if ( nextra > 0 ) X c = extra[--nextra]; X else { X c = (unsigned)(0xff & gchar(&memp)); X if (inc(&memp) == -1) X eof = TRUE; X /* when getting a character from the file, we */ X /* may have to turn it into something else on */ X /* the way to putting it into 'Nextscreen'. */ X if ( c == TAB && !P(P_LS) ) { X strcpy(extra," "); X /* tab amount depends on current column */ X nextra = ((P(P_TS)-1) - (col - coff)%P(P_TS)); X c = ' '; X } else if ( c == NUL && P(P_LS) ) { X extra[0] = NUL; X nextra = 1; X c = '$'; X } else if ( c != NUL && (n=chars[c].ch_size) > 1 ) { X char *p; X nextra = 0; X p = chars[c].ch_str; X /* copy 'ch-str'ing into 'extra' in reverse */ X while ( n > 1 ) X extra[nextra++] = p[--n]; X c = p[0]; X } X } X X if ( c == NUL ) { X row++; X /* get pointer to start of next row */ X nextrow = &Nextscreen[row*Columns]; X /* blank out the rest of this row */ X while ( screenp != nextrow ) X *screenp++ = ' '; X col = 0; X break; X } X X if ( col >= Columns ) { X row++; X col = 0; X } X /* store the character in Nextscreen */ X *screenp++ = c; X col++; X } X return ((row - Cline_row) == Cline_size); X} X X/* X * lnexttoscreen X * X * Like nexttoscreen() but only for the cursor line. X */ Xstatic void Xlnexttoscreen() X{ X register char *np = Nextscreen + (Cline_row * Columns); X register char *rp = Realscreen + (Cline_row * Columns); X register char *endline; X register int row, col; X int gorow = -1, gocol = -1; X X if (anyinput()) { X need_redraw = TRUE; X return; X } X X endline = np + (Cline_size * Columns); X X row = Cline_row; X col = 0; X X CUROFF; /* disable cursor */ X X for ( ; np < endline ; np++,rp++ ) { X /* If desired screen (contents of Nextscreen) does not */ X /* match what's really there, put it there. */ X if ( *np != *rp ) { X /* if we are positioned at the right place, */ X /* we don't have to use windgoto(). */ X if (gocol != col || gorow != row) { X /* X * If we're just off by one, don't send X * an entire esc. seq. (this happens a lot!) X */ X if (gorow == row && gocol+1 == col) { X outchar(*(np-1)); X gocol++; X } else X windgoto(gorow=row,gocol=col); X } X outchar(*rp = *np); X gocol++; X } X if ( ++col >= Columns ) { X col = 0; X row++; X } X } X CURON; /* enable cursor again */ X} X Xstatic char * Xmkline(n) Xregister int n; X{ X static char lbuf[9]; X register int i = 2; X X strcpy(lbuf, " "); X X lbuf[i++] = (n % 10) + '0'; X n /= 10; X if (n != 0) { X lbuf[i++] = (n % 10) + '0'; X n /= 10; X } X if (n != 0) { X lbuf[i++] = (n % 10) + '0'; X n /= 10; X } X if (n != 0) { X lbuf[i++] = (n % 10) + '0'; X n /= 10; X } X if (n != 0) { X lbuf[i++] = (n % 10) + '0'; X n /= 10; X } X return lbuf; X} X X/* X * updateline() - update the line the cursor is on X * X * Updateline() is called after changes that only affect the line that X * the cursor is on. This improves performance tremendously for normal X * insert mode operation. The only thing we have to watch for is when X * the cursor line grows or shrinks around a row boundary. This means X * we have to repaint other parts of the screen appropriately. If X * lfiletonext() returns FALSE, the size of the cursor line (in rows) X * has changed and we have to call updatescreen() to do a complete job. X */ Xvoid Xupdateline() X{ X if (!lfiletonext()) X updatescreen(); /* bag it, do the whole screen */ X else X lnexttoscreen(); X} X Xvoid Xupdatescreen() X{ X extern bool_t interactive; X X if (interactive) { X filetonext(); X nexttoscreen(); X } X} X X/* X * prt_line() - print the given line X */ Xvoid Xprt_line(s) Xchar *s; X{ X register int si = 0; X register int c; X register int col = 0; X X char extra[16]; X int nextra = 0; X int n; X X for (;;) { X X if ( nextra > 0 ) X c = extra[--nextra]; X else { X c = s[si++]; X if ( c == TAB && !P(P_LS) ) { X strcpy(extra, " "); X /* tab amount depends on current column */ X nextra = (P(P_TS) - 1) - col%P(P_TS); X c = ' '; X } else if ( c == NUL && P(P_LS) ) { X extra[0] = NUL; X nextra = 1; X c = '$'; X } else if ( c != NUL && (n=chars[c].ch_size) > 1 ) { X char *p; X X nextra = 0; X p = chars[c].ch_str; X /* copy 'ch-str'ing into 'extra' in reverse */ X while ( n > 1 ) X extra[nextra++] = p[--n]; X c = p[0]; X } X } X X if ( c == NUL ) X break; X X outchar(c); X col++; X } X} X Xvoid Xscreenclear() X{ X register char *rp, *np; X register char *end; X X CLS; /* clear the display */ X X rp = Realscreen; X end = Realscreen + Rows * Columns; X np = Nextscreen; X X /* blank out the stored screens */ X while (rp != end) X *rp++ = *np++ = ' '; X} X Xvoid Xcursupdate() X{ X register LPTR *p; X register int icnt, c, nlines; X register int i; X int didinc; X X if (bufempty()) { /* special case - file is empty */ X *Topchar = *Filemem; X *Curschar = *Filemem; X } else if ( LINEOF(Curschar) < LINEOF(Topchar) ) { X nlines = cntllines(Curschar,Topchar); X /* if the cursor is above the top of */ X /* the screen, put it at the top of the screen.. */ X *Topchar = *Curschar; X Topchar->index = 0; X /* ... and, if we weren't very close to begin with, */ X /* we scroll so that the line is close to the middle. */ X if ( nlines > Rows/3 ) { X for (i=0, p = Topchar; i < Rows/3 ;i++, *Topchar = *p) X if ((p = prevline(p)) == NULL) X break; X } else X s_ins(0, nlines-1); X updatescreen(); X } X else if (LINEOF(Curschar) >= LINEOF(Botchar)) { X nlines = cntllines(Botchar,Curschar); X /* If the cursor is off the bottom of the screen, */ X /* put it at the top of the screen.. */ X /* ... and back up */ X if ( nlines > Rows/3 ) { X p = Curschar; X for (i=0; i < (2*Rows)/3 ;i++) X if ((p = prevline(p)) == NULL) X break; X *Topchar = *p; X } else { X scrollup(nlines); X } X updatescreen(); X } X X Cursrow = Curscol = Cursvcol = 0; X for ( p=Topchar; p->linep != Curschar->linep ;p = nextline(p) ) X Cursrow += plines(p); X X Cline_row = Cursrow; X Cline_size = plines(p); X X if (P(P_NU)) X Curscol = 8; X X for (i=0; i <= Curschar->index ;i++) { X c = Curschar->linep->s[i]; X /* A tab gets expanded, depending on the current column */ X if ( c == TAB && !P(P_LS) ) X icnt = P(P_TS) - (Cursvcol % P(P_TS)); X else X icnt = chars[(unsigned)(c & 0xff)].ch_size; X Curscol += icnt; X Cursvcol += icnt; X if ( Curscol >= Columns ) { X Curscol -= Columns; X Cursrow++; X didinc = TRUE; X } X else X didinc = FALSE; X } X if (didinc) X Cursrow--; X X if (c == TAB && State == NORMAL && !P(P_LS)) { X Curscol--; X Cursvcol--; X } else { X Curscol -= icnt; X Cursvcol -= icnt; X } X if (Curscol < 0) X Curscol += Columns; X X if (set_want_col) { X Curswant = Cursvcol; X set_want_col = FALSE; X } X} X X/* X * The rest of the routines in this file perform screen manipulations. X * The given operation is performed physically on the screen. The X * corresponding change is also made to the internal screen image. X * In this way, the editor anticipates the effect of editing changes X * on the appearance of the screen. That way, when we call screenupdate X * a complete redraw isn't usually necessary. Another advantage is that X * we can keep adding code to anticipate screen changes, and in the X * meantime, everything still works. X */ X X/* X * s_ins(row, nlines) - insert 'nlines' lines at 'row' X */ Xvoid Xs_ins(row, nlines) Xint row; Xint nlines; X{ X register char *s, *d; /* src & dest for block copy */ X register char *e; /* end point for copy */ X register int i; X X if ( ! CANIL ) /* can't do it */ X return; X X /* X * It "looks" better if we do all the inserts at once X */ X SAVCUR; /* save position */ X windgoto(row, 0); X X CRTIL( row, nlines ); X X windgoto(Rows-1, 0); /* delete any garbage that may have */ X CLEOL; /* been shifted to the bottom line */ X RESCUR; /* restore the cursor position */ X X /* X * Now do a block move to update the internal screen image X */ X d = Realscreen + (Columns * (Rows - 1)) - 1; X s = d - (Columns * nlines); X e = Realscreen + (Columns * row); X X while (s >= e) X *d-- = *s--; X X /* X * Clear the inserted lines X */ X s = Realscreen + (row * Columns); X e = s + (nlines * Columns); X while (s < e) X *s++ = ' '; X} X X/* X * s_del(row, nlines) - delete 'nlines' lines at 'row' X */ Xvoid Xs_del(row, nlines) Xint row; Xint nlines; X{ X register char *s, *d, *e; X register int i; X X#ifndef BIOS X if ( ! CANDL ) return; /* can't do it */ X#endif X X /* delete the lines */ X SAVCUR; /* save position */ X X windgoto (Rows-1, 0); /* go to status line */ X CLEOL; /* Clear it */ X windgoto (row, 0); /* Go to 1st line-to-del */ X CRTDL( row, nlines ); /* Delete the lines */ X RESCUR; /* Restore the cursor */ X X /* X * do a block move to update the internal image X */ X d = Realscreen + (row * Columns); X s = d + (nlines * Columns); X e = Realscreen + ((Rows - 1) * Columns); X X while (s < e) X *d++ = *s++; X X while (d < e) /* clear the lines at the bottom */ X *d++ = ' '; X} END_OF_FILE if test 15945 -ne `wc -c <'stevie/screen.c'`; then echo shar: \"'stevie/screen.c'\" unpacked with wrong size! fi # end of 'stevie/screen.c' fi echo shar: End of archive 9 \(of 13\). cp /dev/null ark9isdone MISSING="" for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked all 13 archives. rm -f ark[1-9]isdone ark[1-9][0-9]isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive. exit 0