From 822c3837f95a355f90d25aaabeb2445bb5eb1bf0 Mon Sep 17 00:00:00 2001 From: Eric Andersen Date: Mon, 7 May 2001 17:37:43 +0000 Subject: [PATCH] Another vi update from Sterling Huxley: - All of the ESC sequences are now in variables. This should make re-targeting for other terminals easier. - The initial screen draw does not force out every single column. Should be faster. - The place_cursor() routine trys to be smarter about moving the cursor. This is optional based on BB_FEATURE_VI_OPTIMIZE_CURSOR. - The 't' and 'f' intra-line positioning commands were added. They can now be used as targets in 'c' and 'd' commands, i.e., dfx - delete from dot to next 'x' dtx - delete from dot to the char before next 'x' - show_status_line() uses a static int checksum to remember what is currently displayed on the screen and not re-draw the status line unless it has changed. - Some of the code in refresh() was moved out to format_line(). refresh() trys to send out the smallest segment containing the changed chars rather than the whole line. - Added "flash" to the :set command to specify if error indication should be by flashing the screen or ringing the bell. - Changed the rawmode() routine so that it turns off the NL -> CR NL translation. On output of a NL, the OS will not add a CR. - If vi was started as "view", with global read-only mode, and another file is opened, the file is opened read-only rather than read+write. --- editors/vi.c | 580 +++++++++++++++++++++++++++++++-------------------- vi.c | 580 +++++++++++++++++++++++++++++++-------------------- 2 files changed, 704 insertions(+), 456 deletions(-) diff --git a/editors/vi.c b/editors/vi.c index 96fc96559..54f1d265c 100644 --- a/editors/vi.c +++ b/editors/vi.c @@ -19,13 +19,13 @@ */ char *vi_Version = - "$Id: vi.c,v 1.5 2001/04/26 15:56:47 andersen Exp $"; + "$Id: vi.c,v 1.6 2001/05/07 17:37:43 andersen Exp $"; /* * To compile for standalone use: * gcc -Wall -Os -s -DSTANDALONE -o vi vi.c * or - * gcc -Wall -Os -s -DSTANDALONE -DCRASHME -o vi vi.c # include testing features + * gcc -Wall -Os -s -DSTANDALONE -DBB_FEATURE_VI_CRASHME -o vi vi.c # include testing features * strip vi */ @@ -47,7 +47,7 @@ char *vi_Version = //---- Feature -------------- Bytes to immplement #ifdef STANDALONE -#define vi_main main +#define vi_main main #define BB_FEATURE_VI_COLON // 4288 #define BB_FEATURE_VI_YANKMARK // 1408 #define BB_FEATURE_VI_SEARCH // 1088 @@ -62,7 +62,7 @@ char *vi_Version = // To stop testing, wait until all to text[] is deleted, or // Ctrl-Z and kill -9 %1 // while in the editor Ctrl-T will toggle the crashme function on and off. -//#define BB_FEATURE_VI_CRASHME // randomly pick commands to execute +//#define BB_FEATURE_VI_CRASHME // randomly pick commands to execute #endif /* STANDALONE */ #ifndef STANDALONE @@ -144,7 +144,11 @@ static struct timeval tv; // use select() for small sleeps static char erase_char; // the users erase character static int rows, columns; // the terminal screen is this size static int crow, ccol, offset; // cursor is on Crow x Ccol with Horz Ofset -static char *SOs, *SOn; +static char *SOs, *SOn; // terminal standout start/normal ESC sequence +static char *bell; // terminal bell sequence +static char *Ceol, *Ceos; // Clear-end-of-line and Clear-end-of-screen ESC sequence +static char *CMrc; // Cursor motion arbitrary destination ESC sequence +static char *CMup, *CMdown; // Cursor motion up and down ESC sequence static Byte *status_buffer; // mesages to the user static Byte last_input_char; // last char read from user static Byte last_forward_char; // last char searched for with 'f' @@ -157,6 +161,9 @@ static Byte *dot; // where all the action takes place static int tabstop; static struct termios term_orig, term_vi; // remember what the cooked mode was +#ifdef BB_FEATURE_VI_OPTIMIZE_CURSOR +static int last_row; // where the cursor was last moved to +#endif /* BB_FEATURE_VI_OPTIMIZE_CURSOR */ #ifdef BB_FEATURE_VI_USE_SIGNALS static jmp_buf restart; // catch_sig() #endif /* BB_FEATURE_VI_USE_SIGNALS */ @@ -172,7 +179,7 @@ static Byte *ioq, *ioq_start; // pointer to string for get_one_char to "read" static Byte *modifying_cmds; // cmds that modify text[] #endif /* BB_FEATURE_VI_DOT_CMD || BB_FEATURE_VI_YANKMARK */ #ifdef BB_FEATURE_VI_READONLY -static int readonly; +static int vi_readonly, readonly; #endif /* BB_FEATURE_VI_READONLY */ #ifdef BB_FEATURE_VI_SETOPTS static int autoindent; @@ -234,7 +241,7 @@ static Byte get_one_char(void); // read 1 char from stdin static int file_size(Byte *); // what is the byte size of "fn" static int file_insert(Byte *, Byte *, int); static int file_write(Byte *, Byte *, Byte *); -static void place_cursor(int, int); +static void place_cursor(int, int, int); static void screen_erase(); static void clear_to_eol(void); static void clear_to_eos(void); @@ -249,6 +256,7 @@ static void psbs(char *, ...); // Print Status Buf in standout mode static void ni(Byte *); // display messages static void edit_status(void); // show file status on status line static void redraw(int); // force a full screen refresh +static void format_line(Byte*, Byte*, int); static void refresh(int); // update the terminal from screen[] #ifdef BB_FEATURE_VI_SEARCH @@ -261,9 +269,7 @@ static Byte *get_one_address(Byte *, int *); // get colon addr, if present static Byte *get_address(Byte *, int *, int *); // get two colon addrs, if present static void colon(Byte *); // execute the "colon" mode cmds #endif /* BB_FEATURE_VI_COLON */ -#if defined(BB_FEATURE_VI_SEARCH) || defined(BB_FEATURE_VI_COLON) static Byte *get_input_line(Byte *); // get input line- use "status line" -#endif /* BB_FEATURE_VI_SEARCH || BB_FEATURE_VI_COLON */ #ifdef BB_FEATURE_VI_USE_SIGNALS static void winch_sig(int); // catch window size changes static void suspend_sig(int); // catch ctrl-Z @@ -307,16 +313,23 @@ extern int vi_main(int argc, char **argv) int i; #endif /* BB_FEATURE_VI_YANKMARK */ + CMrc= "\033[%d;%dH"; // Terminal Crusor motion ESC sequence + CMup= "\033[A"; // move cursor up one line, same col + CMdown="\n"; // move cursor down one line, same col + Ceol= "\033[0K"; // Clear from cursor to end of line + Ceos= "\033[0J"; // Clear from cursor to end of screen SOs = "\033[7m"; // Terminal standout mode on SOn = "\033[0m"; // Terminal standout mode off + bell= "\007"; // Terminal bell sequence #ifdef BB_FEATURE_VI_CRASHME (void) srand((long) getpid()); #endif /* BB_FEATURE_VI_CRASHME */ status_buffer = (Byte *) malloc(200); // hold messages to user #ifdef BB_FEATURE_VI_READONLY - readonly = FALSE; + vi_readonly = readonly = FALSE; if (strncmp(argv[0], "view", 4) == 0) { readonly = TRUE; + vi_readonly = TRUE; } #endif /* BB_FEATURE_VI_READONLY */ #ifdef BB_FEATURE_VI_SETOPTS @@ -348,10 +361,10 @@ extern int vi_main(int argc, char **argv) readonly = TRUE; break; #endif /* BB_FEATURE_VI_READONLY */ - //case 'r': // recover flag- ignore- we don't use tmp file - //case 'x': // encryption flag- ignore - //case 'c': // execute command first - //case 'h': // help -- just use default + //case 'r': // recover flag- ignore- we don't use tmp file + //case 'x': // encryption flag- ignore + //case 'c': // execute command first + //case 'h': // help -- just use default default: show_help(); break; @@ -397,12 +410,12 @@ static void edit_file(Byte * fn) rawmode(); rows = 24; columns = 80; + ch= -1; #ifdef BB_FEATURE_VI_WIN_RESIZE window_size_get(0); #endif /* BB_FEATURE_VI_WIN_RESIZE */ new_screen(rows, columns); // get memory for virtual screen - ch= 0; cnt = file_size(fn); // file size size = 2 * cnt; // 200% of file size new_text(size); // get a text[] buffer @@ -484,7 +497,7 @@ static void edit_file(Byte * fn) ioq = ioq_start = last_modifying_cmd = 0; adding2q = 0; #endif /* BB_FEATURE_VI_DOT_CMD */ - redraw(TRUE); + redraw(FALSE); // dont force every col re-draw show_status_line(); //------This is the main Vi cmd handling loop ----------------------- @@ -534,7 +547,7 @@ static void edit_file(Byte * fn) } //------------------------------------------------------------------- - place_cursor(rows, 0); // go to bottom of screen + place_cursor(rows, 0, FALSE); // go to bottom of screen clear_to_eol(); // Erase to end of line cookmode(); } @@ -649,7 +662,7 @@ static void crash_dummy() i = (int) lrand48() % strlen(cmd); cm = cmd[i]; if (strchr(":\024", cm)) - goto cd0; // dont allow these commands + goto cd0; // dont allow colon or ctrl-T commands readbuffer[rbi++] = cm; // put cmd into input buffer // now we have the command- @@ -725,11 +738,9 @@ static void crash_test() if (strlen(msg) > 0) { alarm(0); - sprintf(buf, "\n\n%d: \'%c\' ", totalcmds, last_input_char); + sprintf(buf, "\n\n%d: \'%c\' %s\n\n\n%s[Hit return to continue]%s", + totalcmds, last_input_char, msg, SOs, SOn); write(1, buf, strlen(buf)); - write(1, msg, strlen(msg)); - write(1, "\n\n\n", 3); - write(1, "\033[7m[Hit return to continue]\033[0m", 32); while (read(0, d, 1) > 0) { if (d[0] == '\n' || d[0] == '\r') break; @@ -803,50 +814,49 @@ static void do_cmd(Byte c) } switch (c) { - //case 0x01: // soh - //case 0x09: // ht - //case 0x0b: // vt - //case 0x0e: // so - //case 0x0f: // si - //case 0x10: // dle - //case 0x11: // dc1 - //case 0x13: // dc3 + //case 0x01: // soh + //case 0x09: // ht + //case 0x0b: // vt + //case 0x0e: // so + //case 0x0f: // si + //case 0x10: // dle + //case 0x11: // dc1 + //case 0x13: // dc3 #ifdef BB_FEATURE_VI_CRASHME case 0x14: // dc4 ctrl-T crashme = (crashme == 0) ? 1 : 0; break; #endif /* BB_FEATURE_VI_CRASHME */ - //case 0x16: // syn - //case 0x17: // etb - //case 0x18: // can - //case 0x1c: // fs - //case 0x1d: // gs - //case 0x1e: // rs - //case 0x1f: // us - //case '!': // !- - //case '#': // #- - //case '&': // &- - //case '(': // (- - //case ')': // )- - //case '*': // *- - //case ',': // ,- - //case '=': // =- - //case '@': // @- - //case 'F': // F- - //case 'K': // K- - //case 'Q': // Q- - //case 'S': // S- - //case 'T': // T- - //case 'V': // V- - //case '[': // [- - //case '\\': // \- - //case ']': // ]- - //case '_': // _- - //case '`': // `- - //case 'g': // g- - //case 't': // t- + //case 0x16: // syn + //case 0x17: // etb + //case 0x18: // can + //case 0x1c: // fs + //case 0x1d: // gs + //case 0x1e: // rs + //case 0x1f: // us + //case '!': // !- + //case '#': // #- + //case '&': // &- + //case '(': // (- + //case ')': // )- + //case '*': // *- + //case ',': // ,- + //case '=': // =- + //case '@': // @- + //case 'F': // F- + //case 'K': // K- + //case 'Q': // Q- + //case 'S': // S- + //case 'T': // T- + //case 'V': // V- + //case '[': // [- + //case '\\': // \- + //case ']': // ]- + //case '_': // _- + //case '`': // `- + //case 'g': // g- //case 'u': // u- FIXME- there is no undo - //case 'v': // v- + //case 'v': // v- default: // unrecognised command buf[0] = c; buf[1] = '\0'; @@ -904,7 +914,7 @@ static void do_cmd(Byte c) break; case 12: // ctrl-L force redraw whole screen case 18: // ctrl-R force redraw - place_cursor(0, 0); // put cursor in correct place + place_cursor(0, 0, FALSE); // put cursor in correct place clear_to_eos(); // tel terminal to erase display (void) mysleep(10); screen_erase(); // erase the internal screen buffer @@ -924,7 +934,7 @@ static void do_cmd(Byte c) case 25: // ctrl-Y scroll up one line dot_scroll(1, -1); break; - case 0x1b: // esc + case 27: // esc if (cmd_mode == 0) indicate_error(c); cmd_mode = 0; // stop insrting @@ -1053,8 +1063,9 @@ static void do_cmd(Byte c) //**** fall thru to ... 'i' case ';': // ;- look at rest of line for last forward char if (cmdcnt-- > 1) { - do_cmd(c); + do_cmd(';'); } // repeat cnt + if (last_forward_char == 0) break; q = dot + 1; while (q < end - 1 && *q != '\n' && *q != last_forward_char) { q++; @@ -1182,41 +1193,37 @@ static void do_cmd(Byte c) } break; case ':': // :- the colon mode commands -#ifdef BB_FEATURE_VI_COLON p = get_input_line((Byte *) ":"); // get input line- use "status line" +#ifdef BB_FEATURE_VI_COLON colon(p); // execute the command #else /* BB_FEATURE_VI_COLON */ - *status_buffer = '\0'; // clear the status buffer - place_cursor(rows - 1, 0); // go to Status line, bottom of screen - clear_to_eol(); // clear the line - write(1, ":", 1); // write out the : prompt - for (cnt = 0; cnt < 8; cnt++) { - c1 = get_one_char(); - if (c1 == '\n' || c1 == '\r' || c1 == 27) { - break; - } - buf[cnt] = c1; - buf[cnt + 1] = '\0'; - write(1, buf + cnt, 1); // echo the char - } - cnt = strlen((char *) buf); - if (strncasecmp((char *) buf, "quit", cnt) == 0 || - strncasecmp((char *) buf, "q!", cnt) == 0) { // delete lines - if (file_modified == TRUE && buf[1] != '!') { + if (*p == ':') + p++; // move past the ':' + cnt = strlen((char *) p); + if (cnt <= 0) + break; + if (strncasecmp((char *) p, "quit", cnt) == 0 || + strncasecmp((char *) p, "q!", cnt) == 0) { // delete lines + if (file_modified == TRUE && p[1] != '!') { psbs("No write since last change (:quit! overrides)"); } else { editing = 0; } - } else if (strncasecmp((char *) buf, "write", cnt) == 0 || - strncasecmp((char *) buf, "wq", cnt) == 0) { + } else if (strncasecmp((char *) p, "write", cnt) == 0 || + strncasecmp((char *) p, "wq", cnt) == 0) { cnt = file_write(cfn, text, end - 1); file_modified = FALSE; psb("\"%s\" %dL, %dC", cfn, count_lines(text, end - 1), cnt); - if (buf[1] == 'q') { + if (p[1] == 'q') { editing = 0; } + } else if (strncasecmp((char *) p, "file", cnt) == 0 ) { + edit_status(); // show current file status + } else if (sscanf((char *) p, "%d", &j) > 0) { + dot = find_line(j); // go to line # j + dot_skip_over_ws(); } else { // unrecognised cmd - ni((Byte *) buf); + ni((Byte *) p); } #endif /* BB_FEATURE_VI_COLON */ break; @@ -1287,10 +1294,10 @@ static void do_cmd(Byte c) end_cmd_q(); // stop adding to q #endif /* BB_FEATURE_VI_DOT_CMD */ break; - case 'G': // G- goto to a line number (default= E-O-F) - dot = end - 1; // assume E-O-F + case 'G': // G- goto to a line number (default= E-O-F) + dot = end - 1; // assume E-O-F if (cmdcnt > 0) { - dot = find_line(cmdcnt); // what line is #cmdcnt + dot = find_line(cmdcnt); // what line is #cmdcnt } dot_skip_over_ws(); break; @@ -1338,13 +1345,13 @@ static void do_cmd(Byte c) dot_begin(); dot_skip_over_ws(); break; - case 'M': // M- goto middle line on screen + case 'M': // M- goto middle line on screen dot = screenbegin; for (cnt = 0; cnt < (rows-1) / 2; cnt++) dot = next_line(dot); break; case 'O': // O- open a empty line above - // 0i\n\033-i + // 0i\n ESC -i p = begin_line(dot); if (p[-1] == '\n') { dot_prev(); @@ -1353,7 +1360,7 @@ static void do_cmd(Byte c) dot = char_insert(dot, '\n'); } else { dot_begin(); // 0 - dot = char_insert(dot, '\n'); // i\n\033 + dot = char_insert(dot, '\n'); // i\n ESC dot_prev(); // - } goto dc_i; @@ -1390,6 +1397,7 @@ static void do_cmd(Byte c) } if (file_modified == TRUE #ifdef BB_FEATURE_VI_READONLY + && vi_readonly == FALSE && readonly == FALSE #endif /* BB_FEATURE_VI_READONLY */ ) { @@ -1452,7 +1460,7 @@ static void do_cmd(Byte c) } } dot = yank_delete(p, q, 0, yf); // delete word - } else if (strchr("^0bBeE$", c1)) { + } else if (strchr("^0bBeEft$", c1)) { // single line copy text into a register and delete dot = yank_delete(p, q, 0, yf); // delete word } else if (strchr("cdykjHL%+-{}\r\n", c1)) { @@ -1514,6 +1522,13 @@ static void do_cmd(Byte c) } end_cmd_q(); // stop adding to q break; + case 't': // t- move to char prior to next x + last_forward_char = get_one_char(); + do_cmd(';'); + if (*dot == last_forward_char) + dot_left(); + last_forward_char= 0; + break; case 'w': // w- forward a word if (cmdcnt-- > 1) { do_cmd(c); @@ -1699,19 +1714,19 @@ static void colon(Byte * buf) int useforce, forced; struct stat st_buf; - // :3154 // if (-e line 3154) goto it else stay put - // :4,33w! foo // write a portion of buffer to file "foo" - // :w // write all of buffer to current file - // :q // quit - // :q! // quit- dont care about modified file + // :3154 // if (-e line 3154) goto it else stay put + // :4,33w! foo // write a portion of buffer to file "foo" + // :w // write all of buffer to current file + // :q // quit + // :q! // quit- dont care about modified file // :'a,'z!sort -u // filter block through sort - // :'f // goto mark "f" - // :'fl // list literal the mark "f" line - // :.r bar // read file "bar" into buffer before dot + // :'f // goto mark "f" + // :'fl // list literal the mark "f" line + // :.r bar // read file "bar" into buffer before dot // :/123/,/abc/d // delete lines from "123" line to "abc" line - // :/xyz/ // goto the "xyz" line + // :/xyz/ // goto the "xyz" line // :s/find/replace/ // substitute pattern "find" with "replace" - // :! // run then return + // :! // run then return // if (strlen((char *) buf) <= 0) goto vc1; @@ -1725,8 +1740,8 @@ static void colon(Byte * buf) r = end - 1; li = count_lines(text, end - 1); fn = cfn; // default to current file - memset(cmd, '\0', BUFSIZ); // clear cmd[] - memset(args, '\0', BUFSIZ); // clear args[] + memset(cmd, '\0', BUFSIZ); // clear cmd[] + memset(args, '\0', BUFSIZ); // clear args[] // look for optional address(es) :. :1 :1,9 :'q,'a :% buf = get_address(buf, &b, &e); @@ -1745,7 +1760,7 @@ static void colon(Byte * buf) while (isblnk(*buf)) buf++; strcpy((char *) args, (char *) buf); - if (last_char_is((char *)cmd,'!')) { + if (last_char_is((char *)cmd, '!')) { useforce = TRUE; cmd[strlen((char *) cmd) - 1] = '\0'; // get rid of ! } @@ -1775,7 +1790,7 @@ static void colon(Byte * buf) } else if (strncmp((char *) cmd, "!", 1) == 0) { // run a cmd // :!ls run the (void) alarm(0); // wait for input- no alarms - place_cursor(rows - 1, 0); // go to Status line + place_cursor(rows - 1, 0, FALSE); // go to Status line clear_to_eol(); // clear the line cookmode(); system(orig_buf+1); // run the cmd @@ -1870,14 +1885,14 @@ static void colon(Byte * buf) #endif /* BB_FEATURE_VI_YANKMARK */ // how many lines in text[]? li = count_lines(text, end - 1); - psb("\"%s\"%s" + psb("\"%s\"%s" #ifdef BB_FEATURE_VI_READONLY "%s" #endif /* BB_FEATURE_VI_READONLY */ " %dL, %dC", cfn, (sr < 0 ? " [New file]" : ""), #ifdef BB_FEATURE_VI_READONLY - (readonly == TRUE ? " [Read only]" : ""), + ((vi_readonly == TRUE || readonly == TRUE) ? " [Read only]" : ""), #endif /* BB_FEATURE_VI_READONLY */ li, ch); } else if (strncasecmp((char *) cmd, "file", i) == 0) { // what File is this @@ -1896,7 +1911,7 @@ static void colon(Byte * buf) } } else if (strncasecmp((char *) cmd, "features", i) == 0) { // what features are available // print out values of all features - place_cursor(rows - 1, 0); // go to Status line, bottom of screen + place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen clear_to_eol(); // clear the line cookmode(); show_help(); @@ -1907,7 +1922,7 @@ static void colon(Byte * buf) q = begin_line(dot); // assume .,. for the range r = end_line(dot); } - place_cursor(rows - 1, 0); // go to Status line, bottom of screen + place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen clear_to_eol(); // clear the line write(1, "\r\n", 2); for (; q <= r; q++) { @@ -1973,13 +1988,13 @@ static void colon(Byte * buf) goto vc1; // nothing was inserted // how many lines in text[]? li = count_lines(q, q + ch - 1); - psb("\"%s\"" + psb("\"%s\"" #ifdef BB_FEATURE_VI_READONLY "%s" #endif /* BB_FEATURE_VI_READONLY */ " %dL, %dC", fn, #ifdef BB_FEATURE_VI_READONLY - (readonly == TRUE ? " [Read only]" : ""), + ((vi_readonly == TRUE || readonly == TRUE) ? " [Read only]" : ""), #endif /* BB_FEATURE_VI_READONLY */ li, ch); if (ch > 0) { @@ -2001,13 +2016,16 @@ static void colon(Byte * buf) i = 0; // offset into args if (strlen((char *) args) == 0) { // print out values of all options - place_cursor(rows - 1, 0); // go to Status line, bottom of screen + place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen clear_to_eol(); // clear the line printf("----------------------------------------\r\n"); #ifdef BB_FEATURE_VI_SETOPTS if (!autoindent) printf("no"); printf("autoindent "); + if (!err_method) + printf("no"); + printf("flash "); if (!ignorecase) printf("no"); printf("ignorecase "); @@ -2026,6 +2044,10 @@ static void colon(Byte * buf) strncasecmp((char *) args + i, "ai", 2) == 0) { autoindent = (i == 2) ? 0 : 1; } + if (strncasecmp((char *) args + i, "flash", 5) == 0 || + strncasecmp((char *) args + i, "fl", 2) == 0) { + err_method = (i == 2) ? 0 : 1; + } if (strncasecmp((char *) args + i, "ignorecase", 10) == 0 || strncasecmp((char *) args + i, "ic", 2) == 0) { ignorecase = (i == 2) ? 0 : 1; @@ -2096,7 +2118,7 @@ static void colon(Byte * buf) fn = args; } #ifdef BB_FEATURE_VI_READONLY - if (readonly == TRUE && useforce == FALSE) { + if ((vi_readonly == TRUE || readonly == TRUE) && useforce == FALSE) { psbs("\"%s\" File is read only", fn); goto vc3; } @@ -2452,10 +2474,18 @@ static Byte *bound_dot(Byte * p) // make sure text[0] <= P < "end" static Byte *new_screen(int ro, int co) { + int li; + if (screen != 0) free(screen); screensize = ro * co + 8; screen = (Byte *) malloc(screensize); + // initialize the new screen. assume this will be a empty file. + screen_erase(); + // non-existant text[] lines start with a tilde (~). + for (li = 1; li < ro - 1; li++) { + screen[(li * co) + 0] = '~'; + } return (screen); } @@ -2469,9 +2499,9 @@ static Byte *new_text(int size) } text = (Byte *) malloc(size + 8); memset(text, '\0', size); // clear new text[] - //text += 4; // leave some room for "oops" + //text += 4; // leave some room for "oops" textend = text + size - 1; - //textend -= 4; // leave some root for "oops" + //textend -= 4; // leave some root for "oops" return (text); } @@ -2596,7 +2626,7 @@ static Byte *char_insert(Byte * p, Byte c) // insert the char c at 'p' cmd_mode = 0; cmdcnt = 0; end_cmd_q(); // stop adding to q - *status_buffer = '\0'; // clear the status buffer + strcpy((char *) status_buffer, " "); // clear the status buffer if (p[-1] != '\n') { p--; } @@ -2667,7 +2697,7 @@ static Byte find_range(Byte ** start, Byte ** stop, Byte c) q = next_line(q); } q = end_line(q); - } else if (strchr("^%$0bBeE", c)) { + } else if (strchr("^%$0bBeEft", c)) { // These cmds operate on char positions do_cmd(c); // execute movement cmd q = dot; @@ -2931,6 +2961,7 @@ static void show_help(void) printf("\tNamed buffers with \"x\n"); #endif /* BB_FEATURE_VI_YANKMARK */ #ifdef BB_FEATURE_VI_READONLY + printf("\tReadonly if vi is called as \"view\"\n"); printf("\tReadonly with -R command line arg\n"); #endif /* BB_FEATURE_VI_READONLY */ #ifdef BB_FEATURE_VI_SET @@ -3111,6 +3142,7 @@ static void rawmode(void) term_vi = term_orig; term_vi.c_lflag &= (~ICANON & ~ECHO); // leave ISIG ON- allow intr's term_vi.c_iflag &= (~IXON & ~ICRNL); + term_vi.c_oflag &= (~ONLCR); term_vi.c_cc[VMIN] = 1; term_vi.c_cc[VTIME] = 0; erase_char = term_vi.c_cc[VERASE]; @@ -3172,7 +3204,7 @@ static void cont_sig(int sig) //----- Come here when we get a Suspend signal ------------------- static void suspend_sig(int sig) { - place_cursor(rows, 0); // go to bottom of screen + place_cursor(rows - 1, 0, FALSE); // go to bottom of screen clear_to_eol(); // Erase to end of line cookmode(); // terminal to "cooked" @@ -3181,7 +3213,7 @@ static void suspend_sig(int sig) kill(getpid(), SIGTSTP); } -//----- Come here when we get a signal -------------------- +//----- Come here when we get a signal --------------------------- static void catch_sig(int sig) { signal(SIGHUP, catch_sig); @@ -3382,7 +3414,6 @@ static Byte get_one_char() return (c); // return the char, where ever it came from } -#if defined(BB_FEATURE_VI_SEARCH) || defined(BB_FEATURE_VI_COLON) static Byte *get_input_line(Byte * prompt) // get input line- use "status line" { Byte buf[BUFSIZ]; @@ -3392,7 +3423,7 @@ static Byte *get_input_line(Byte * prompt) // get input line- use "status line" strcpy((char *) buf, (char *) prompt); *status_buffer = '\0'; // clear the status buffer - place_cursor(rows - 1, 0); // go to Status line, bottom of screen + place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen clear_to_eol(); // clear the line write(1, prompt, strlen((char *) prompt)); // write out the :, /, or ? prompt @@ -3421,7 +3452,6 @@ static Byte *get_input_line(Byte * prompt) // get input line- use "status line" obufp = (Byte *) strdup((char *) buf); return (obufp); } -#endif /* BB_FEATURE_VI_SEARCH || BB_FEATURE_VI_COLON */ static int file_size(Byte * fn) // what is the byte size of "fn" { @@ -3465,9 +3495,15 @@ static int file_insert(Byte * fn, Byte * p, int size) } // see if we can open the file +#ifdef BB_FEATURE_VI_READONLY + if (vi_readonly == TRUE) goto fi1; // do not try write-mode +#endif fd = open((char *) fn, O_RDWR); // assume read & write if (fd < 0) { // could not open for writing- maybe file is read only +#ifdef BB_FEATURE_VI_READONLY + fi1: +#endif fd = open((char *) fn, O_RDONLY); // try read-only if (fd < 0) { psbs("\"%s\" %s", fn, "could not open file"); @@ -3529,51 +3565,96 @@ static int file_write(Byte * fn, Byte * first, Byte * last) // 1,0 ... 1,79 // . ... . // . ... . -// 22,0 ... 22,79 -// 23,0 ... 23,79 status line +// 22,0 ... 22,79 +// 23,0 ... 23,79 status line // //----- Move the cursor to row x col (count from 0, not 1) ------- -static void place_cursor(int row, int col) +static void place_cursor(int row, int col, int opti) { - Byte buf[30]; + char cm1[BUFSIZ]; + char *cm; int l; +#ifdef BB_FEATURE_VI_OPTIMIZE_CURSOR + char cm2[BUFSIZ]; + Byte *screenp; + // char cm3[BUFSIZ]; + int Rrow= last_row; +#endif /* BB_FEATURE_VI_OPTIMIZE_CURSOR */ + + memset(cm1, '\0', BUFSIZ - 1); // clear the buffer - if (row < 0) - row = 0; - if (row >= rows) - row = rows - 1; - if (col < 0) - col = 0; - if (col >= columns) - col = columns - 1; - sprintf((char *) buf, "%c[%d;%dH", 0x1b, row + 1, col + 1); - l = strlen((char *) buf); - write(1, buf, l); + if (row < 0) row = 0; + if (row >= rows) row = rows - 1; + if (col < 0) col = 0; + if (col >= columns) col = columns - 1; + + //----- 1. Try the standard terminal ESC sequence + sprintf((char *) cm1, CMrc, row + 1, col + 1); + cm= cm1; + if (opti == FALSE) goto pc0; + +#ifdef BB_FEATURE_VI_OPTIMIZE_CURSOR + //----- find the minimum # of chars to move cursor ------------- + //----- 2. Try moving with discreet chars (Newline, [back]space, ...) + memset(cm2, '\0', BUFSIZ - 1); // clear the buffer + + // move to the correct row + while (row < Rrow) { + // the cursor has to move up + strcat(cm2, CMup); + Rrow--; + } + while (row > Rrow) { + // the cursor has to move down + strcat(cm2, CMdown); + Rrow++; + } + + // now move to the correct column + strcat(cm2, "\r"); // start at col 0 + // just send out orignal source char to get to correct place + screenp = &screen[row * columns]; // start of screen line + strncat(cm2, screenp, col); + + //----- 3. Try some other way of moving cursor + //--------------------------------------------- + + // pick the shortest cursor motion to send out + cm= cm1; + if (strlen(cm2) < strlen(cm)) { + cm= cm2; + } /* else if (strlen(cm3) < strlen(cm)) { + cm= cm3; + } */ +#endif /* BB_FEATURE_VI_OPTIMIZE_CURSOR */ + pc0: + l= strlen(cm); + if (l) write(1, cm, l); // move the cursor } //----- Erase from cursor to end of line ----------------------- static void clear_to_eol() { - write(1, "\033[0K", 4); // Erase from cursor to end of line + write(1, Ceol, strlen(Ceol)); // Erase from cursor to end of line } //----- Erase from cursor to end of screen ----------------------- static void clear_to_eos() { - write(1, "\033[0J", 4); // Erase from cursor to end of screen + write(1, Ceos, strlen(Ceos)); // Erase from cursor to end of screen } //----- Start standout mode ------------------------------------ static void standout_start() // send "start reverse video" sequence { - write(1, "\033[7m", 4); // Start reverse video mode + write(1, SOs, strlen(SOs)); // Start reverse video mode } //----- End standout mode -------------------------------------- static void standout_end() // send "end reverse video" sequence { - write(1, "\033[0m", 4); // End reverse video mode + write(1, SOn, strlen(SOn)); // End reverse video mode } //----- Flash the screen -------------------------------------- @@ -3588,7 +3669,7 @@ static void flash(int h) static void beep() { - write(1, "\007", 1); // send out a bell character + write(1, bell, strlen(bell)); // send out a bell character } static void indicate_error(char c) @@ -3608,25 +3689,25 @@ static void indicate_error(char c) //----- Erase the Screen[] memory ------------------------------ static void screen_erase() { - int i; - - for (i = 0; i < screensize; i++) { - screen[i] = '\0'; - } + memset(screen, ' ', screensize); // clear new screen } //----- Draw the status line at bottom of the screen ------------- static void show_status_line(void) { - int cnt; + static int last_cksum; + int l, cnt, cksum; cnt = strlen((char *) status_buffer); - place_cursor(rows - 1, 0); // put cursor on status line - if (cnt > 0) { + for (cksum= l= 0; l < cnt; l++) { cksum += (int)(status_buffer[l]); } + // don't write the status line unless it changes + if (cnt > 0 && last_cksum != cksum) { + last_cksum= cksum; // remember if we have seen this line + place_cursor(rows - 1, 0, FALSE); // put cursor on status line write(1, status_buffer, cnt); + clear_to_eol(); + place_cursor(crow, ccol, FALSE); // put cursor back in correct place } - clear_to_eol(); - place_cursor(crow, ccol); // put cursor back in correct place } //----- format the status buffer, the bottom line of screen ------ @@ -3636,10 +3717,10 @@ static void psbs(char *format, ...) va_list args; va_start(args, format); - strcpy((char *) status_buffer, "\033[7m"); // Terminal standout mode on + strcpy((char *) status_buffer, SOs); // Terminal standout mode on vsprintf((char *) status_buffer + strlen((char *) status_buffer), format, args); - strcat((char *) status_buffer, "\033[0m"); // Terminal standout mode off + strcat((char *) status_buffer, SOn); // Terminal standout mode off va_end(args); return; @@ -3686,7 +3767,7 @@ static void edit_status(void) // show file status on status line "%s line %d of %d --%d%%--", (cfn != 0 ? (char *) cfn : "No file"), #ifdef BB_FEATURE_VI_READONLY - (readonly == TRUE ? " [Read only]" : ""), + ((vi_readonly == TRUE || readonly == TRUE) ? " [Read only]" : ""), #endif /* BB_FEATURE_VI_READONLY */ (file_modified == TRUE ? " [modified]" : ""), cur, tot, percent); @@ -3695,12 +3776,50 @@ static void edit_status(void) // show file status on status line //----- Force refresh of all Lines ----------------------------- static void redraw(int full_screen) { - place_cursor(0, 0); // put cursor in correct place + place_cursor(0, 0, FALSE); // put cursor in correct place clear_to_eos(); // tel terminal to erase display screen_erase(); // erase the internal screen buffer refresh(full_screen); // this will redraw the entire display } +//----- Format a text[] line into a buffer --------------------- +static void format_line(Byte *dest, Byte *src, int li) +{ + int co; + Byte c; + + for (co= 0; co < MAX_SCR_COLS; co++) { + c= ' '; // assume blank + if (li > 0 && co == 0) { + c = '~'; // not first line, assume Tilde + } + // are there chars in text[] and have we gone past the end + if (text < end && src < end) { + c = *src++; + } + if (c == '\n') + break; + if (c < ' ' || c > '~') { + if (c == '\t') { + c = ' '; + // co % 8 != 7 + for (; (co % tabstop) != (tabstop - 1); co++) { + dest[co] = c; + } + } else { + dest[co++] = '^'; + c |= '@'; // make it visible + c &= 0x7f; // get rid of hi bit + } + } + // the co++ is done here so that the column will + // not be overwritten when we blank-out the rest of line + dest[co] = c; + if (src >= end) + break; + } +} + //----- Refresh the changed screen lines ----------------------- // Copy the source line from text[] into the buffer and note // if the current screenline is different from the new buffer. @@ -3709,97 +3828,102 @@ static void redraw(int full_screen) static void refresh(int full_screen) { static int old_offset; - int li, co, changed; - Byte c, buf[MAX_SCR_COLS]; + int li, changed; + Byte buf[MAX_SCR_COLS]; Byte *tp, *sp; // pointer into text[] and screen[] +#ifdef BB_FEATURE_VI_OPTIMIZE_CURSOR + int last_li= -2; // last line that changed- for optimizing cursor movement +#endif /* BB_FEATURE_VI_OPTIMIZE_CURSOR */ #ifdef BB_FEATURE_VI_WIN_RESIZE window_size_get(0); #endif /* BB_FEATURE_VI_WIN_RESIZE */ - sync_cursor(dot, &crow, &ccol); + sync_cursor(dot, &crow, &ccol); // where cursor will be (on "dot") tp = screenbegin; // index into text[] of top line + // compare text[] to screen[] and mark screen[] lines that need updating for (li = 0; li < rows - 1; li++) { - // format current text line into buf with "columns" wide - for (co = 0; co < columns + offset;) { - c = ' '; // assume blank - if (li > 0 && co == 0) { - c = '~'; // not first line, assume Tilde - } - // are there chars in text[] - // and have we gone past the end - if (text < end && tp < end) { - c = *tp++; - } - if (c == '\n') - break; - if (c < ' ' || c > '~') { - if (c == '\t') { - c = ' '; - // co % 8 != 7 - for (; (co % tabstop) != (tabstop - 1); co++) { - buf[co] = c; - } - } else { - buf[co++] = '^'; - c |= '@'; // make it visible - c &= 0x7f; // get rid of hi bit - } - } - // the co++ is done here so that the column will - // not be overwritten when we blank-out the rest of line - buf[co++] = c; - if (tp >= end) - break; - } - if (co >= columns + offset) { - // skip to the end of the current text[] line - while (tp < end && *tp++ != '\n'); - } - // try to keep the cursor near it's current position - // remember how many chars in this row- where the cursor sits - // blank out the rest of the buffer - while (co < MAX_SCR_COLS - 1) { - buf[co++] = ' '; - } - buf[co++] = 0; // NULL terminate the buffer + int cs, ce; // column start & end + memset(buf, ' ', MAX_SCR_COLS); // blank-out the buffer + buf[MAX_SCR_COLS-1] = 0; // NULL terminate the buffer + // format current text line into buf + format_line(buf, tp, li); - // if necessary, update virtual screen[] and terminal from buf[] + // skip to the end of the current text[] line + while (tp < end && *tp++ != '\n') /*no-op*/ ; + + // see if there are any changes between vitual screen and buf changed = FALSE; // assume no change + cs= 0; + ce= columns-1; sp = &screen[li * columns]; // start of screen line - for (co = 0; co < columns; co++) { - if (sp[co] != buf[co + offset]) { - sp[co] = buf[co + offset]; + if (full_screen == TRUE) { + // force re-draw of every single column from 0 - columns-1 + goto re0; + } + // compare newly formatted buffer with virtual screen + // look forward for first difference between buf and screen + for ( ; cs <= ce; cs++) { + if (buf[cs + offset] != sp[cs]) { changed = TRUE; // mark for redraw + break; } } - // if horz offset has changed, force a redraw - if (offset != old_offset) - changed = TRUE; - // write all marked screen lines out to terminal - if (changed == TRUE) { - place_cursor(li, 0); // put cursor in correct place - clear_to_eol(); // Erase to end of line - if (full_screen == FALSE) { - // don't redraw every column on terminal - // look backwards for last non-blank - for (co = columns + offset; co >= 0; co--) { - // break; - if (buf[co] != ' ') - break; - } - co++; - } else { - // redraw every column on terminal - co = columns; + // look backward for last difference between buf and screen + for ( ; ce >= cs; ce--) { + if (buf[ce + offset] != sp[ce]) { + changed = TRUE; // mark for redraw + break; } + } + // now, cs is index of first diff, and ce is index of last diff + + // if horz offset has changed, force a redraw + if (offset != old_offset) { + re0: + changed = TRUE; + } + + // make a sanity check of columns indexes + if (cs < 0) cs= 0; + if (ce > columns-1) ce= columns-1; + if (cs > ce) { cs= 0; ce= columns-1; } + // is there a change between vitual screen and buf + if (changed == TRUE) { + // copy changed part of buffer to virtual screen + memmove(sp+cs, buf+(cs+offset), ce-cs+1); + + // move cursor to column of first change + if (offset != old_offset) { + // opti_cur_move is still too stupid + // to handle offsets correctly + place_cursor(li, cs, FALSE); + } else { +#ifdef BB_FEATURE_VI_OPTIMIZE_CURSOR + // if this just the next line + // try to optimize cursor movement + // otherwise, use standard ESC sequence + place_cursor(li, cs, li == (last_li+1) ? TRUE : FALSE); + last_li= li; +#else /* BB_FEATURE_VI_OPTIMIZE_CURSOR */ + place_cursor(li, cs, FALSE); // use standard ESC sequence +#endif /* BB_FEATURE_VI_OPTIMIZE_CURSOR */ + } + // write line out to terminal - write(1, buf + offset, co); + write(1, sp+cs, ce-cs+1); +#ifdef BB_FEATURE_VI_OPTIMIZE_CURSOR + last_row = li; +#endif /* BB_FEATURE_VI_OPTIMIZE_CURSOR */ } } - place_cursor(crow, ccol); + place_cursor(crow, ccol, (crow == last_row) ? TRUE : FALSE); +#ifdef BB_FEATURE_VI_OPTIMIZE_CURSOR + last_row = crow; +#endif /* BB_FEATURE_VI_OPTIMIZE_CURSOR */ + if (offset != old_offset) old_offset = offset; } diff --git a/vi.c b/vi.c index 96fc96559..54f1d265c 100644 --- a/vi.c +++ b/vi.c @@ -19,13 +19,13 @@ */ char *vi_Version = - "$Id: vi.c,v 1.5 2001/04/26 15:56:47 andersen Exp $"; + "$Id: vi.c,v 1.6 2001/05/07 17:37:43 andersen Exp $"; /* * To compile for standalone use: * gcc -Wall -Os -s -DSTANDALONE -o vi vi.c * or - * gcc -Wall -Os -s -DSTANDALONE -DCRASHME -o vi vi.c # include testing features + * gcc -Wall -Os -s -DSTANDALONE -DBB_FEATURE_VI_CRASHME -o vi vi.c # include testing features * strip vi */ @@ -47,7 +47,7 @@ char *vi_Version = //---- Feature -------------- Bytes to immplement #ifdef STANDALONE -#define vi_main main +#define vi_main main #define BB_FEATURE_VI_COLON // 4288 #define BB_FEATURE_VI_YANKMARK // 1408 #define BB_FEATURE_VI_SEARCH // 1088 @@ -62,7 +62,7 @@ char *vi_Version = // To stop testing, wait until all to text[] is deleted, or // Ctrl-Z and kill -9 %1 // while in the editor Ctrl-T will toggle the crashme function on and off. -//#define BB_FEATURE_VI_CRASHME // randomly pick commands to execute +//#define BB_FEATURE_VI_CRASHME // randomly pick commands to execute #endif /* STANDALONE */ #ifndef STANDALONE @@ -144,7 +144,11 @@ static struct timeval tv; // use select() for small sleeps static char erase_char; // the users erase character static int rows, columns; // the terminal screen is this size static int crow, ccol, offset; // cursor is on Crow x Ccol with Horz Ofset -static char *SOs, *SOn; +static char *SOs, *SOn; // terminal standout start/normal ESC sequence +static char *bell; // terminal bell sequence +static char *Ceol, *Ceos; // Clear-end-of-line and Clear-end-of-screen ESC sequence +static char *CMrc; // Cursor motion arbitrary destination ESC sequence +static char *CMup, *CMdown; // Cursor motion up and down ESC sequence static Byte *status_buffer; // mesages to the user static Byte last_input_char; // last char read from user static Byte last_forward_char; // last char searched for with 'f' @@ -157,6 +161,9 @@ static Byte *dot; // where all the action takes place static int tabstop; static struct termios term_orig, term_vi; // remember what the cooked mode was +#ifdef BB_FEATURE_VI_OPTIMIZE_CURSOR +static int last_row; // where the cursor was last moved to +#endif /* BB_FEATURE_VI_OPTIMIZE_CURSOR */ #ifdef BB_FEATURE_VI_USE_SIGNALS static jmp_buf restart; // catch_sig() #endif /* BB_FEATURE_VI_USE_SIGNALS */ @@ -172,7 +179,7 @@ static Byte *ioq, *ioq_start; // pointer to string for get_one_char to "read" static Byte *modifying_cmds; // cmds that modify text[] #endif /* BB_FEATURE_VI_DOT_CMD || BB_FEATURE_VI_YANKMARK */ #ifdef BB_FEATURE_VI_READONLY -static int readonly; +static int vi_readonly, readonly; #endif /* BB_FEATURE_VI_READONLY */ #ifdef BB_FEATURE_VI_SETOPTS static int autoindent; @@ -234,7 +241,7 @@ static Byte get_one_char(void); // read 1 char from stdin static int file_size(Byte *); // what is the byte size of "fn" static int file_insert(Byte *, Byte *, int); static int file_write(Byte *, Byte *, Byte *); -static void place_cursor(int, int); +static void place_cursor(int, int, int); static void screen_erase(); static void clear_to_eol(void); static void clear_to_eos(void); @@ -249,6 +256,7 @@ static void psbs(char *, ...); // Print Status Buf in standout mode static void ni(Byte *); // display messages static void edit_status(void); // show file status on status line static void redraw(int); // force a full screen refresh +static void format_line(Byte*, Byte*, int); static void refresh(int); // update the terminal from screen[] #ifdef BB_FEATURE_VI_SEARCH @@ -261,9 +269,7 @@ static Byte *get_one_address(Byte *, int *); // get colon addr, if present static Byte *get_address(Byte *, int *, int *); // get two colon addrs, if present static void colon(Byte *); // execute the "colon" mode cmds #endif /* BB_FEATURE_VI_COLON */ -#if defined(BB_FEATURE_VI_SEARCH) || defined(BB_FEATURE_VI_COLON) static Byte *get_input_line(Byte *); // get input line- use "status line" -#endif /* BB_FEATURE_VI_SEARCH || BB_FEATURE_VI_COLON */ #ifdef BB_FEATURE_VI_USE_SIGNALS static void winch_sig(int); // catch window size changes static void suspend_sig(int); // catch ctrl-Z @@ -307,16 +313,23 @@ extern int vi_main(int argc, char **argv) int i; #endif /* BB_FEATURE_VI_YANKMARK */ + CMrc= "\033[%d;%dH"; // Terminal Crusor motion ESC sequence + CMup= "\033[A"; // move cursor up one line, same col + CMdown="\n"; // move cursor down one line, same col + Ceol= "\033[0K"; // Clear from cursor to end of line + Ceos= "\033[0J"; // Clear from cursor to end of screen SOs = "\033[7m"; // Terminal standout mode on SOn = "\033[0m"; // Terminal standout mode off + bell= "\007"; // Terminal bell sequence #ifdef BB_FEATURE_VI_CRASHME (void) srand((long) getpid()); #endif /* BB_FEATURE_VI_CRASHME */ status_buffer = (Byte *) malloc(200); // hold messages to user #ifdef BB_FEATURE_VI_READONLY - readonly = FALSE; + vi_readonly = readonly = FALSE; if (strncmp(argv[0], "view", 4) == 0) { readonly = TRUE; + vi_readonly = TRUE; } #endif /* BB_FEATURE_VI_READONLY */ #ifdef BB_FEATURE_VI_SETOPTS @@ -348,10 +361,10 @@ extern int vi_main(int argc, char **argv) readonly = TRUE; break; #endif /* BB_FEATURE_VI_READONLY */ - //case 'r': // recover flag- ignore- we don't use tmp file - //case 'x': // encryption flag- ignore - //case 'c': // execute command first - //case 'h': // help -- just use default + //case 'r': // recover flag- ignore- we don't use tmp file + //case 'x': // encryption flag- ignore + //case 'c': // execute command first + //case 'h': // help -- just use default default: show_help(); break; @@ -397,12 +410,12 @@ static void edit_file(Byte * fn) rawmode(); rows = 24; columns = 80; + ch= -1; #ifdef BB_FEATURE_VI_WIN_RESIZE window_size_get(0); #endif /* BB_FEATURE_VI_WIN_RESIZE */ new_screen(rows, columns); // get memory for virtual screen - ch= 0; cnt = file_size(fn); // file size size = 2 * cnt; // 200% of file size new_text(size); // get a text[] buffer @@ -484,7 +497,7 @@ static void edit_file(Byte * fn) ioq = ioq_start = last_modifying_cmd = 0; adding2q = 0; #endif /* BB_FEATURE_VI_DOT_CMD */ - redraw(TRUE); + redraw(FALSE); // dont force every col re-draw show_status_line(); //------This is the main Vi cmd handling loop ----------------------- @@ -534,7 +547,7 @@ static void edit_file(Byte * fn) } //------------------------------------------------------------------- - place_cursor(rows, 0); // go to bottom of screen + place_cursor(rows, 0, FALSE); // go to bottom of screen clear_to_eol(); // Erase to end of line cookmode(); } @@ -649,7 +662,7 @@ static void crash_dummy() i = (int) lrand48() % strlen(cmd); cm = cmd[i]; if (strchr(":\024", cm)) - goto cd0; // dont allow these commands + goto cd0; // dont allow colon or ctrl-T commands readbuffer[rbi++] = cm; // put cmd into input buffer // now we have the command- @@ -725,11 +738,9 @@ static void crash_test() if (strlen(msg) > 0) { alarm(0); - sprintf(buf, "\n\n%d: \'%c\' ", totalcmds, last_input_char); + sprintf(buf, "\n\n%d: \'%c\' %s\n\n\n%s[Hit return to continue]%s", + totalcmds, last_input_char, msg, SOs, SOn); write(1, buf, strlen(buf)); - write(1, msg, strlen(msg)); - write(1, "\n\n\n", 3); - write(1, "\033[7m[Hit return to continue]\033[0m", 32); while (read(0, d, 1) > 0) { if (d[0] == '\n' || d[0] == '\r') break; @@ -803,50 +814,49 @@ static void do_cmd(Byte c) } switch (c) { - //case 0x01: // soh - //case 0x09: // ht - //case 0x0b: // vt - //case 0x0e: // so - //case 0x0f: // si - //case 0x10: // dle - //case 0x11: // dc1 - //case 0x13: // dc3 + //case 0x01: // soh + //case 0x09: // ht + //case 0x0b: // vt + //case 0x0e: // so + //case 0x0f: // si + //case 0x10: // dle + //case 0x11: // dc1 + //case 0x13: // dc3 #ifdef BB_FEATURE_VI_CRASHME case 0x14: // dc4 ctrl-T crashme = (crashme == 0) ? 1 : 0; break; #endif /* BB_FEATURE_VI_CRASHME */ - //case 0x16: // syn - //case 0x17: // etb - //case 0x18: // can - //case 0x1c: // fs - //case 0x1d: // gs - //case 0x1e: // rs - //case 0x1f: // us - //case '!': // !- - //case '#': // #- - //case '&': // &- - //case '(': // (- - //case ')': // )- - //case '*': // *- - //case ',': // ,- - //case '=': // =- - //case '@': // @- - //case 'F': // F- - //case 'K': // K- - //case 'Q': // Q- - //case 'S': // S- - //case 'T': // T- - //case 'V': // V- - //case '[': // [- - //case '\\': // \- - //case ']': // ]- - //case '_': // _- - //case '`': // `- - //case 'g': // g- - //case 't': // t- + //case 0x16: // syn + //case 0x17: // etb + //case 0x18: // can + //case 0x1c: // fs + //case 0x1d: // gs + //case 0x1e: // rs + //case 0x1f: // us + //case '!': // !- + //case '#': // #- + //case '&': // &- + //case '(': // (- + //case ')': // )- + //case '*': // *- + //case ',': // ,- + //case '=': // =- + //case '@': // @- + //case 'F': // F- + //case 'K': // K- + //case 'Q': // Q- + //case 'S': // S- + //case 'T': // T- + //case 'V': // V- + //case '[': // [- + //case '\\': // \- + //case ']': // ]- + //case '_': // _- + //case '`': // `- + //case 'g': // g- //case 'u': // u- FIXME- there is no undo - //case 'v': // v- + //case 'v': // v- default: // unrecognised command buf[0] = c; buf[1] = '\0'; @@ -904,7 +914,7 @@ static void do_cmd(Byte c) break; case 12: // ctrl-L force redraw whole screen case 18: // ctrl-R force redraw - place_cursor(0, 0); // put cursor in correct place + place_cursor(0, 0, FALSE); // put cursor in correct place clear_to_eos(); // tel terminal to erase display (void) mysleep(10); screen_erase(); // erase the internal screen buffer @@ -924,7 +934,7 @@ static void do_cmd(Byte c) case 25: // ctrl-Y scroll up one line dot_scroll(1, -1); break; - case 0x1b: // esc + case 27: // esc if (cmd_mode == 0) indicate_error(c); cmd_mode = 0; // stop insrting @@ -1053,8 +1063,9 @@ static void do_cmd(Byte c) //**** fall thru to ... 'i' case ';': // ;- look at rest of line for last forward char if (cmdcnt-- > 1) { - do_cmd(c); + do_cmd(';'); } // repeat cnt + if (last_forward_char == 0) break; q = dot + 1; while (q < end - 1 && *q != '\n' && *q != last_forward_char) { q++; @@ -1182,41 +1193,37 @@ static void do_cmd(Byte c) } break; case ':': // :- the colon mode commands -#ifdef BB_FEATURE_VI_COLON p = get_input_line((Byte *) ":"); // get input line- use "status line" +#ifdef BB_FEATURE_VI_COLON colon(p); // execute the command #else /* BB_FEATURE_VI_COLON */ - *status_buffer = '\0'; // clear the status buffer - place_cursor(rows - 1, 0); // go to Status line, bottom of screen - clear_to_eol(); // clear the line - write(1, ":", 1); // write out the : prompt - for (cnt = 0; cnt < 8; cnt++) { - c1 = get_one_char(); - if (c1 == '\n' || c1 == '\r' || c1 == 27) { - break; - } - buf[cnt] = c1; - buf[cnt + 1] = '\0'; - write(1, buf + cnt, 1); // echo the char - } - cnt = strlen((char *) buf); - if (strncasecmp((char *) buf, "quit", cnt) == 0 || - strncasecmp((char *) buf, "q!", cnt) == 0) { // delete lines - if (file_modified == TRUE && buf[1] != '!') { + if (*p == ':') + p++; // move past the ':' + cnt = strlen((char *) p); + if (cnt <= 0) + break; + if (strncasecmp((char *) p, "quit", cnt) == 0 || + strncasecmp((char *) p, "q!", cnt) == 0) { // delete lines + if (file_modified == TRUE && p[1] != '!') { psbs("No write since last change (:quit! overrides)"); } else { editing = 0; } - } else if (strncasecmp((char *) buf, "write", cnt) == 0 || - strncasecmp((char *) buf, "wq", cnt) == 0) { + } else if (strncasecmp((char *) p, "write", cnt) == 0 || + strncasecmp((char *) p, "wq", cnt) == 0) { cnt = file_write(cfn, text, end - 1); file_modified = FALSE; psb("\"%s\" %dL, %dC", cfn, count_lines(text, end - 1), cnt); - if (buf[1] == 'q') { + if (p[1] == 'q') { editing = 0; } + } else if (strncasecmp((char *) p, "file", cnt) == 0 ) { + edit_status(); // show current file status + } else if (sscanf((char *) p, "%d", &j) > 0) { + dot = find_line(j); // go to line # j + dot_skip_over_ws(); } else { // unrecognised cmd - ni((Byte *) buf); + ni((Byte *) p); } #endif /* BB_FEATURE_VI_COLON */ break; @@ -1287,10 +1294,10 @@ static void do_cmd(Byte c) end_cmd_q(); // stop adding to q #endif /* BB_FEATURE_VI_DOT_CMD */ break; - case 'G': // G- goto to a line number (default= E-O-F) - dot = end - 1; // assume E-O-F + case 'G': // G- goto to a line number (default= E-O-F) + dot = end - 1; // assume E-O-F if (cmdcnt > 0) { - dot = find_line(cmdcnt); // what line is #cmdcnt + dot = find_line(cmdcnt); // what line is #cmdcnt } dot_skip_over_ws(); break; @@ -1338,13 +1345,13 @@ static void do_cmd(Byte c) dot_begin(); dot_skip_over_ws(); break; - case 'M': // M- goto middle line on screen + case 'M': // M- goto middle line on screen dot = screenbegin; for (cnt = 0; cnt < (rows-1) / 2; cnt++) dot = next_line(dot); break; case 'O': // O- open a empty line above - // 0i\n\033-i + // 0i\n ESC -i p = begin_line(dot); if (p[-1] == '\n') { dot_prev(); @@ -1353,7 +1360,7 @@ static void do_cmd(Byte c) dot = char_insert(dot, '\n'); } else { dot_begin(); // 0 - dot = char_insert(dot, '\n'); // i\n\033 + dot = char_insert(dot, '\n'); // i\n ESC dot_prev(); // - } goto dc_i; @@ -1390,6 +1397,7 @@ static void do_cmd(Byte c) } if (file_modified == TRUE #ifdef BB_FEATURE_VI_READONLY + && vi_readonly == FALSE && readonly == FALSE #endif /* BB_FEATURE_VI_READONLY */ ) { @@ -1452,7 +1460,7 @@ static void do_cmd(Byte c) } } dot = yank_delete(p, q, 0, yf); // delete word - } else if (strchr("^0bBeE$", c1)) { + } else if (strchr("^0bBeEft$", c1)) { // single line copy text into a register and delete dot = yank_delete(p, q, 0, yf); // delete word } else if (strchr("cdykjHL%+-{}\r\n", c1)) { @@ -1514,6 +1522,13 @@ static void do_cmd(Byte c) } end_cmd_q(); // stop adding to q break; + case 't': // t- move to char prior to next x + last_forward_char = get_one_char(); + do_cmd(';'); + if (*dot == last_forward_char) + dot_left(); + last_forward_char= 0; + break; case 'w': // w- forward a word if (cmdcnt-- > 1) { do_cmd(c); @@ -1699,19 +1714,19 @@ static void colon(Byte * buf) int useforce, forced; struct stat st_buf; - // :3154 // if (-e line 3154) goto it else stay put - // :4,33w! foo // write a portion of buffer to file "foo" - // :w // write all of buffer to current file - // :q // quit - // :q! // quit- dont care about modified file + // :3154 // if (-e line 3154) goto it else stay put + // :4,33w! foo // write a portion of buffer to file "foo" + // :w // write all of buffer to current file + // :q // quit + // :q! // quit- dont care about modified file // :'a,'z!sort -u // filter block through sort - // :'f // goto mark "f" - // :'fl // list literal the mark "f" line - // :.r bar // read file "bar" into buffer before dot + // :'f // goto mark "f" + // :'fl // list literal the mark "f" line + // :.r bar // read file "bar" into buffer before dot // :/123/,/abc/d // delete lines from "123" line to "abc" line - // :/xyz/ // goto the "xyz" line + // :/xyz/ // goto the "xyz" line // :s/find/replace/ // substitute pattern "find" with "replace" - // :! // run then return + // :! // run then return // if (strlen((char *) buf) <= 0) goto vc1; @@ -1725,8 +1740,8 @@ static void colon(Byte * buf) r = end - 1; li = count_lines(text, end - 1); fn = cfn; // default to current file - memset(cmd, '\0', BUFSIZ); // clear cmd[] - memset(args, '\0', BUFSIZ); // clear args[] + memset(cmd, '\0', BUFSIZ); // clear cmd[] + memset(args, '\0', BUFSIZ); // clear args[] // look for optional address(es) :. :1 :1,9 :'q,'a :% buf = get_address(buf, &b, &e); @@ -1745,7 +1760,7 @@ static void colon(Byte * buf) while (isblnk(*buf)) buf++; strcpy((char *) args, (char *) buf); - if (last_char_is((char *)cmd,'!')) { + if (last_char_is((char *)cmd, '!')) { useforce = TRUE; cmd[strlen((char *) cmd) - 1] = '\0'; // get rid of ! } @@ -1775,7 +1790,7 @@ static void colon(Byte * buf) } else if (strncmp((char *) cmd, "!", 1) == 0) { // run a cmd // :!ls run the (void) alarm(0); // wait for input- no alarms - place_cursor(rows - 1, 0); // go to Status line + place_cursor(rows - 1, 0, FALSE); // go to Status line clear_to_eol(); // clear the line cookmode(); system(orig_buf+1); // run the cmd @@ -1870,14 +1885,14 @@ static void colon(Byte * buf) #endif /* BB_FEATURE_VI_YANKMARK */ // how many lines in text[]? li = count_lines(text, end - 1); - psb("\"%s\"%s" + psb("\"%s\"%s" #ifdef BB_FEATURE_VI_READONLY "%s" #endif /* BB_FEATURE_VI_READONLY */ " %dL, %dC", cfn, (sr < 0 ? " [New file]" : ""), #ifdef BB_FEATURE_VI_READONLY - (readonly == TRUE ? " [Read only]" : ""), + ((vi_readonly == TRUE || readonly == TRUE) ? " [Read only]" : ""), #endif /* BB_FEATURE_VI_READONLY */ li, ch); } else if (strncasecmp((char *) cmd, "file", i) == 0) { // what File is this @@ -1896,7 +1911,7 @@ static void colon(Byte * buf) } } else if (strncasecmp((char *) cmd, "features", i) == 0) { // what features are available // print out values of all features - place_cursor(rows - 1, 0); // go to Status line, bottom of screen + place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen clear_to_eol(); // clear the line cookmode(); show_help(); @@ -1907,7 +1922,7 @@ static void colon(Byte * buf) q = begin_line(dot); // assume .,. for the range r = end_line(dot); } - place_cursor(rows - 1, 0); // go to Status line, bottom of screen + place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen clear_to_eol(); // clear the line write(1, "\r\n", 2); for (; q <= r; q++) { @@ -1973,13 +1988,13 @@ static void colon(Byte * buf) goto vc1; // nothing was inserted // how many lines in text[]? li = count_lines(q, q + ch - 1); - psb("\"%s\"" + psb("\"%s\"" #ifdef BB_FEATURE_VI_READONLY "%s" #endif /* BB_FEATURE_VI_READONLY */ " %dL, %dC", fn, #ifdef BB_FEATURE_VI_READONLY - (readonly == TRUE ? " [Read only]" : ""), + ((vi_readonly == TRUE || readonly == TRUE) ? " [Read only]" : ""), #endif /* BB_FEATURE_VI_READONLY */ li, ch); if (ch > 0) { @@ -2001,13 +2016,16 @@ static void colon(Byte * buf) i = 0; // offset into args if (strlen((char *) args) == 0) { // print out values of all options - place_cursor(rows - 1, 0); // go to Status line, bottom of screen + place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen clear_to_eol(); // clear the line printf("----------------------------------------\r\n"); #ifdef BB_FEATURE_VI_SETOPTS if (!autoindent) printf("no"); printf("autoindent "); + if (!err_method) + printf("no"); + printf("flash "); if (!ignorecase) printf("no"); printf("ignorecase "); @@ -2026,6 +2044,10 @@ static void colon(Byte * buf) strncasecmp((char *) args + i, "ai", 2) == 0) { autoindent = (i == 2) ? 0 : 1; } + if (strncasecmp((char *) args + i, "flash", 5) == 0 || + strncasecmp((char *) args + i, "fl", 2) == 0) { + err_method = (i == 2) ? 0 : 1; + } if (strncasecmp((char *) args + i, "ignorecase", 10) == 0 || strncasecmp((char *) args + i, "ic", 2) == 0) { ignorecase = (i == 2) ? 0 : 1; @@ -2096,7 +2118,7 @@ static void colon(Byte * buf) fn = args; } #ifdef BB_FEATURE_VI_READONLY - if (readonly == TRUE && useforce == FALSE) { + if ((vi_readonly == TRUE || readonly == TRUE) && useforce == FALSE) { psbs("\"%s\" File is read only", fn); goto vc3; } @@ -2452,10 +2474,18 @@ static Byte *bound_dot(Byte * p) // make sure text[0] <= P < "end" static Byte *new_screen(int ro, int co) { + int li; + if (screen != 0) free(screen); screensize = ro * co + 8; screen = (Byte *) malloc(screensize); + // initialize the new screen. assume this will be a empty file. + screen_erase(); + // non-existant text[] lines start with a tilde (~). + for (li = 1; li < ro - 1; li++) { + screen[(li * co) + 0] = '~'; + } return (screen); } @@ -2469,9 +2499,9 @@ static Byte *new_text(int size) } text = (Byte *) malloc(size + 8); memset(text, '\0', size); // clear new text[] - //text += 4; // leave some room for "oops" + //text += 4; // leave some room for "oops" textend = text + size - 1; - //textend -= 4; // leave some root for "oops" + //textend -= 4; // leave some root for "oops" return (text); } @@ -2596,7 +2626,7 @@ static Byte *char_insert(Byte * p, Byte c) // insert the char c at 'p' cmd_mode = 0; cmdcnt = 0; end_cmd_q(); // stop adding to q - *status_buffer = '\0'; // clear the status buffer + strcpy((char *) status_buffer, " "); // clear the status buffer if (p[-1] != '\n') { p--; } @@ -2667,7 +2697,7 @@ static Byte find_range(Byte ** start, Byte ** stop, Byte c) q = next_line(q); } q = end_line(q); - } else if (strchr("^%$0bBeE", c)) { + } else if (strchr("^%$0bBeEft", c)) { // These cmds operate on char positions do_cmd(c); // execute movement cmd q = dot; @@ -2931,6 +2961,7 @@ static void show_help(void) printf("\tNamed buffers with \"x\n"); #endif /* BB_FEATURE_VI_YANKMARK */ #ifdef BB_FEATURE_VI_READONLY + printf("\tReadonly if vi is called as \"view\"\n"); printf("\tReadonly with -R command line arg\n"); #endif /* BB_FEATURE_VI_READONLY */ #ifdef BB_FEATURE_VI_SET @@ -3111,6 +3142,7 @@ static void rawmode(void) term_vi = term_orig; term_vi.c_lflag &= (~ICANON & ~ECHO); // leave ISIG ON- allow intr's term_vi.c_iflag &= (~IXON & ~ICRNL); + term_vi.c_oflag &= (~ONLCR); term_vi.c_cc[VMIN] = 1; term_vi.c_cc[VTIME] = 0; erase_char = term_vi.c_cc[VERASE]; @@ -3172,7 +3204,7 @@ static void cont_sig(int sig) //----- Come here when we get a Suspend signal ------------------- static void suspend_sig(int sig) { - place_cursor(rows, 0); // go to bottom of screen + place_cursor(rows - 1, 0, FALSE); // go to bottom of screen clear_to_eol(); // Erase to end of line cookmode(); // terminal to "cooked" @@ -3181,7 +3213,7 @@ static void suspend_sig(int sig) kill(getpid(), SIGTSTP); } -//----- Come here when we get a signal -------------------- +//----- Come here when we get a signal --------------------------- static void catch_sig(int sig) { signal(SIGHUP, catch_sig); @@ -3382,7 +3414,6 @@ static Byte get_one_char() return (c); // return the char, where ever it came from } -#if defined(BB_FEATURE_VI_SEARCH) || defined(BB_FEATURE_VI_COLON) static Byte *get_input_line(Byte * prompt) // get input line- use "status line" { Byte buf[BUFSIZ]; @@ -3392,7 +3423,7 @@ static Byte *get_input_line(Byte * prompt) // get input line- use "status line" strcpy((char *) buf, (char *) prompt); *status_buffer = '\0'; // clear the status buffer - place_cursor(rows - 1, 0); // go to Status line, bottom of screen + place_cursor(rows - 1, 0, FALSE); // go to Status line, bottom of screen clear_to_eol(); // clear the line write(1, prompt, strlen((char *) prompt)); // write out the :, /, or ? prompt @@ -3421,7 +3452,6 @@ static Byte *get_input_line(Byte * prompt) // get input line- use "status line" obufp = (Byte *) strdup((char *) buf); return (obufp); } -#endif /* BB_FEATURE_VI_SEARCH || BB_FEATURE_VI_COLON */ static int file_size(Byte * fn) // what is the byte size of "fn" { @@ -3465,9 +3495,15 @@ static int file_insert(Byte * fn, Byte * p, int size) } // see if we can open the file +#ifdef BB_FEATURE_VI_READONLY + if (vi_readonly == TRUE) goto fi1; // do not try write-mode +#endif fd = open((char *) fn, O_RDWR); // assume read & write if (fd < 0) { // could not open for writing- maybe file is read only +#ifdef BB_FEATURE_VI_READONLY + fi1: +#endif fd = open((char *) fn, O_RDONLY); // try read-only if (fd < 0) { psbs("\"%s\" %s", fn, "could not open file"); @@ -3529,51 +3565,96 @@ static int file_write(Byte * fn, Byte * first, Byte * last) // 1,0 ... 1,79 // . ... . // . ... . -// 22,0 ... 22,79 -// 23,0 ... 23,79 status line +// 22,0 ... 22,79 +// 23,0 ... 23,79 status line // //----- Move the cursor to row x col (count from 0, not 1) ------- -static void place_cursor(int row, int col) +static void place_cursor(int row, int col, int opti) { - Byte buf[30]; + char cm1[BUFSIZ]; + char *cm; int l; +#ifdef BB_FEATURE_VI_OPTIMIZE_CURSOR + char cm2[BUFSIZ]; + Byte *screenp; + // char cm3[BUFSIZ]; + int Rrow= last_row; +#endif /* BB_FEATURE_VI_OPTIMIZE_CURSOR */ + + memset(cm1, '\0', BUFSIZ - 1); // clear the buffer - if (row < 0) - row = 0; - if (row >= rows) - row = rows - 1; - if (col < 0) - col = 0; - if (col >= columns) - col = columns - 1; - sprintf((char *) buf, "%c[%d;%dH", 0x1b, row + 1, col + 1); - l = strlen((char *) buf); - write(1, buf, l); + if (row < 0) row = 0; + if (row >= rows) row = rows - 1; + if (col < 0) col = 0; + if (col >= columns) col = columns - 1; + + //----- 1. Try the standard terminal ESC sequence + sprintf((char *) cm1, CMrc, row + 1, col + 1); + cm= cm1; + if (opti == FALSE) goto pc0; + +#ifdef BB_FEATURE_VI_OPTIMIZE_CURSOR + //----- find the minimum # of chars to move cursor ------------- + //----- 2. Try moving with discreet chars (Newline, [back]space, ...) + memset(cm2, '\0', BUFSIZ - 1); // clear the buffer + + // move to the correct row + while (row < Rrow) { + // the cursor has to move up + strcat(cm2, CMup); + Rrow--; + } + while (row > Rrow) { + // the cursor has to move down + strcat(cm2, CMdown); + Rrow++; + } + + // now move to the correct column + strcat(cm2, "\r"); // start at col 0 + // just send out orignal source char to get to correct place + screenp = &screen[row * columns]; // start of screen line + strncat(cm2, screenp, col); + + //----- 3. Try some other way of moving cursor + //--------------------------------------------- + + // pick the shortest cursor motion to send out + cm= cm1; + if (strlen(cm2) < strlen(cm)) { + cm= cm2; + } /* else if (strlen(cm3) < strlen(cm)) { + cm= cm3; + } */ +#endif /* BB_FEATURE_VI_OPTIMIZE_CURSOR */ + pc0: + l= strlen(cm); + if (l) write(1, cm, l); // move the cursor } //----- Erase from cursor to end of line ----------------------- static void clear_to_eol() { - write(1, "\033[0K", 4); // Erase from cursor to end of line + write(1, Ceol, strlen(Ceol)); // Erase from cursor to end of line } //----- Erase from cursor to end of screen ----------------------- static void clear_to_eos() { - write(1, "\033[0J", 4); // Erase from cursor to end of screen + write(1, Ceos, strlen(Ceos)); // Erase from cursor to end of screen } //----- Start standout mode ------------------------------------ static void standout_start() // send "start reverse video" sequence { - write(1, "\033[7m", 4); // Start reverse video mode + write(1, SOs, strlen(SOs)); // Start reverse video mode } //----- End standout mode -------------------------------------- static void standout_end() // send "end reverse video" sequence { - write(1, "\033[0m", 4); // End reverse video mode + write(1, SOn, strlen(SOn)); // End reverse video mode } //----- Flash the screen -------------------------------------- @@ -3588,7 +3669,7 @@ static void flash(int h) static void beep() { - write(1, "\007", 1); // send out a bell character + write(1, bell, strlen(bell)); // send out a bell character } static void indicate_error(char c) @@ -3608,25 +3689,25 @@ static void indicate_error(char c) //----- Erase the Screen[] memory ------------------------------ static void screen_erase() { - int i; - - for (i = 0; i < screensize; i++) { - screen[i] = '\0'; - } + memset(screen, ' ', screensize); // clear new screen } //----- Draw the status line at bottom of the screen ------------- static void show_status_line(void) { - int cnt; + static int last_cksum; + int l, cnt, cksum; cnt = strlen((char *) status_buffer); - place_cursor(rows - 1, 0); // put cursor on status line - if (cnt > 0) { + for (cksum= l= 0; l < cnt; l++) { cksum += (int)(status_buffer[l]); } + // don't write the status line unless it changes + if (cnt > 0 && last_cksum != cksum) { + last_cksum= cksum; // remember if we have seen this line + place_cursor(rows - 1, 0, FALSE); // put cursor on status line write(1, status_buffer, cnt); + clear_to_eol(); + place_cursor(crow, ccol, FALSE); // put cursor back in correct place } - clear_to_eol(); - place_cursor(crow, ccol); // put cursor back in correct place } //----- format the status buffer, the bottom line of screen ------ @@ -3636,10 +3717,10 @@ static void psbs(char *format, ...) va_list args; va_start(args, format); - strcpy((char *) status_buffer, "\033[7m"); // Terminal standout mode on + strcpy((char *) status_buffer, SOs); // Terminal standout mode on vsprintf((char *) status_buffer + strlen((char *) status_buffer), format, args); - strcat((char *) status_buffer, "\033[0m"); // Terminal standout mode off + strcat((char *) status_buffer, SOn); // Terminal standout mode off va_end(args); return; @@ -3686,7 +3767,7 @@ static void edit_status(void) // show file status on status line "%s line %d of %d --%d%%--", (cfn != 0 ? (char *) cfn : "No file"), #ifdef BB_FEATURE_VI_READONLY - (readonly == TRUE ? " [Read only]" : ""), + ((vi_readonly == TRUE || readonly == TRUE) ? " [Read only]" : ""), #endif /* BB_FEATURE_VI_READONLY */ (file_modified == TRUE ? " [modified]" : ""), cur, tot, percent); @@ -3695,12 +3776,50 @@ static void edit_status(void) // show file status on status line //----- Force refresh of all Lines ----------------------------- static void redraw(int full_screen) { - place_cursor(0, 0); // put cursor in correct place + place_cursor(0, 0, FALSE); // put cursor in correct place clear_to_eos(); // tel terminal to erase display screen_erase(); // erase the internal screen buffer refresh(full_screen); // this will redraw the entire display } +//----- Format a text[] line into a buffer --------------------- +static void format_line(Byte *dest, Byte *src, int li) +{ + int co; + Byte c; + + for (co= 0; co < MAX_SCR_COLS; co++) { + c= ' '; // assume blank + if (li > 0 && co == 0) { + c = '~'; // not first line, assume Tilde + } + // are there chars in text[] and have we gone past the end + if (text < end && src < end) { + c = *src++; + } + if (c == '\n') + break; + if (c < ' ' || c > '~') { + if (c == '\t') { + c = ' '; + // co % 8 != 7 + for (; (co % tabstop) != (tabstop - 1); co++) { + dest[co] = c; + } + } else { + dest[co++] = '^'; + c |= '@'; // make it visible + c &= 0x7f; // get rid of hi bit + } + } + // the co++ is done here so that the column will + // not be overwritten when we blank-out the rest of line + dest[co] = c; + if (src >= end) + break; + } +} + //----- Refresh the changed screen lines ----------------------- // Copy the source line from text[] into the buffer and note // if the current screenline is different from the new buffer. @@ -3709,97 +3828,102 @@ static void redraw(int full_screen) static void refresh(int full_screen) { static int old_offset; - int li, co, changed; - Byte c, buf[MAX_SCR_COLS]; + int li, changed; + Byte buf[MAX_SCR_COLS]; Byte *tp, *sp; // pointer into text[] and screen[] +#ifdef BB_FEATURE_VI_OPTIMIZE_CURSOR + int last_li= -2; // last line that changed- for optimizing cursor movement +#endif /* BB_FEATURE_VI_OPTIMIZE_CURSOR */ #ifdef BB_FEATURE_VI_WIN_RESIZE window_size_get(0); #endif /* BB_FEATURE_VI_WIN_RESIZE */ - sync_cursor(dot, &crow, &ccol); + sync_cursor(dot, &crow, &ccol); // where cursor will be (on "dot") tp = screenbegin; // index into text[] of top line + // compare text[] to screen[] and mark screen[] lines that need updating for (li = 0; li < rows - 1; li++) { - // format current text line into buf with "columns" wide - for (co = 0; co < columns + offset;) { - c = ' '; // assume blank - if (li > 0 && co == 0) { - c = '~'; // not first line, assume Tilde - } - // are there chars in text[] - // and have we gone past the end - if (text < end && tp < end) { - c = *tp++; - } - if (c == '\n') - break; - if (c < ' ' || c > '~') { - if (c == '\t') { - c = ' '; - // co % 8 != 7 - for (; (co % tabstop) != (tabstop - 1); co++) { - buf[co] = c; - } - } else { - buf[co++] = '^'; - c |= '@'; // make it visible - c &= 0x7f; // get rid of hi bit - } - } - // the co++ is done here so that the column will - // not be overwritten when we blank-out the rest of line - buf[co++] = c; - if (tp >= end) - break; - } - if (co >= columns + offset) { - // skip to the end of the current text[] line - while (tp < end && *tp++ != '\n'); - } - // try to keep the cursor near it's current position - // remember how many chars in this row- where the cursor sits - // blank out the rest of the buffer - while (co < MAX_SCR_COLS - 1) { - buf[co++] = ' '; - } - buf[co++] = 0; // NULL terminate the buffer + int cs, ce; // column start & end + memset(buf, ' ', MAX_SCR_COLS); // blank-out the buffer + buf[MAX_SCR_COLS-1] = 0; // NULL terminate the buffer + // format current text line into buf + format_line(buf, tp, li); - // if necessary, update virtual screen[] and terminal from buf[] + // skip to the end of the current text[] line + while (tp < end && *tp++ != '\n') /*no-op*/ ; + + // see if there are any changes between vitual screen and buf changed = FALSE; // assume no change + cs= 0; + ce= columns-1; sp = &screen[li * columns]; // start of screen line - for (co = 0; co < columns; co++) { - if (sp[co] != buf[co + offset]) { - sp[co] = buf[co + offset]; + if (full_screen == TRUE) { + // force re-draw of every single column from 0 - columns-1 + goto re0; + } + // compare newly formatted buffer with virtual screen + // look forward for first difference between buf and screen + for ( ; cs <= ce; cs++) { + if (buf[cs + offset] != sp[cs]) { changed = TRUE; // mark for redraw + break; } } - // if horz offset has changed, force a redraw - if (offset != old_offset) - changed = TRUE; - // write all marked screen lines out to terminal - if (changed == TRUE) { - place_cursor(li, 0); // put cursor in correct place - clear_to_eol(); // Erase to end of line - if (full_screen == FALSE) { - // don't redraw every column on terminal - // look backwards for last non-blank - for (co = columns + offset; co >= 0; co--) { - // break; - if (buf[co] != ' ') - break; - } - co++; - } else { - // redraw every column on terminal - co = columns; + // look backward for last difference between buf and screen + for ( ; ce >= cs; ce--) { + if (buf[ce + offset] != sp[ce]) { + changed = TRUE; // mark for redraw + break; } + } + // now, cs is index of first diff, and ce is index of last diff + + // if horz offset has changed, force a redraw + if (offset != old_offset) { + re0: + changed = TRUE; + } + + // make a sanity check of columns indexes + if (cs < 0) cs= 0; + if (ce > columns-1) ce= columns-1; + if (cs > ce) { cs= 0; ce= columns-1; } + // is there a change between vitual screen and buf + if (changed == TRUE) { + // copy changed part of buffer to virtual screen + memmove(sp+cs, buf+(cs+offset), ce-cs+1); + + // move cursor to column of first change + if (offset != old_offset) { + // opti_cur_move is still too stupid + // to handle offsets correctly + place_cursor(li, cs, FALSE); + } else { +#ifdef BB_FEATURE_VI_OPTIMIZE_CURSOR + // if this just the next line + // try to optimize cursor movement + // otherwise, use standard ESC sequence + place_cursor(li, cs, li == (last_li+1) ? TRUE : FALSE); + last_li= li; +#else /* BB_FEATURE_VI_OPTIMIZE_CURSOR */ + place_cursor(li, cs, FALSE); // use standard ESC sequence +#endif /* BB_FEATURE_VI_OPTIMIZE_CURSOR */ + } + // write line out to terminal - write(1, buf + offset, co); + write(1, sp+cs, ce-cs+1); +#ifdef BB_FEATURE_VI_OPTIMIZE_CURSOR + last_row = li; +#endif /* BB_FEATURE_VI_OPTIMIZE_CURSOR */ } } - place_cursor(crow, ccol); + place_cursor(crow, ccol, (crow == last_row) ? TRUE : FALSE); +#ifdef BB_FEATURE_VI_OPTIMIZE_CURSOR + last_row = crow; +#endif /* BB_FEATURE_VI_OPTIMIZE_CURSOR */ + if (offset != old_offset) old_offset = offset; }