diff options
-rw-r--r-- | editors/vi.c | 1756 |
1 files changed, 878 insertions, 878 deletions
diff --git a/editors/vi.c b/editors/vi.c index ad6dd5c..ce6c3d8 100644 --- a/editors/vi.c +++ b/editors/vi.c @@ -19,7 +19,7 @@ */ static const char vi_Version[] = - "$Id: vi.c,v 1.22 2002/07/31 21:22:21 sandman Exp $"; + "$Id: vi.c,v 1.23 2002/08/21 13:02:24 aaronl Exp $"; /* * To compile for standalone use: @@ -202,7 +202,7 @@ static void do_cmd(Byte); // execute a command static void sync_cursor(Byte *, int *, int *); // synchronize the screen cursor to dot static Byte *begin_line(Byte *); // return pointer to cur line B-o-l static Byte *end_line(Byte *); // return pointer to cur line E-o-l -static Byte *dollar_line(Byte *); // return pointer to just before NL +extern inline Byte *dollar_line(Byte *); // return pointer to just before NL static Byte *prev_line(Byte *); // return pointer to prev line B-o-l static Byte *next_line(Byte *); // return pointer to next line B-o-l static Byte *end_screen(void); // get pointer to last char on screen @@ -232,7 +232,7 @@ static Byte *text_hole_delete(Byte *, Byte *); // at "p", delete a 'size' byte h static Byte *text_hole_make(Byte *, int); // at "p", make a 'size' byte hole static Byte *yank_delete(Byte *, Byte *, int, int); // yank text[] into register then delete static void show_help(void); // display some help info -static void print_literal(Byte *, Byte *); // copy s to buf, convert unprintable +extern inline void print_literal(Byte *, Byte *); // copy s to buf, convert unprintable static void rawmode(void); // set "raw" mode on tty static void cookmode(void); // return to "cooked" mode on tty static int mysleep(int); // sleep for 'h' 1/100 seconds @@ -296,7 +296,7 @@ static Byte *string_insert(Byte *, Byte *); // insert the string at 'p' static Byte *text_yank(Byte *, Byte *, int); // save copy of "p" into a register static Byte what_reg(void); // what is letter of current YDreg static void check_context(Byte); // remember context for '' command -static Byte *swap_context(Byte *); // goto new context for '' command +extern inline Byte *swap_context(Byte *); // goto new context for '' command #endif /* CONFIG_FEATURE_VI_YANKMARK */ #ifdef CONFIG_FEATURE_VI_CRASHME static void crash_dummy(); @@ -758,877 +758,6 @@ static void crash_test() } #endif /* CONFIG_FEATURE_VI_CRASHME */ -//--------------------------------------------------------------------- -//----- the Ascii Chart ----------------------------------------------- -// -// 00 nul 01 soh 02 stx 03 etx 04 eot 05 enq 06 ack 07 bel -// 08 bs 09 ht 0a nl 0b vt 0c np 0d cr 0e so 0f si -// 10 dle 11 dc1 12 dc2 13 dc3 14 dc4 15 nak 16 syn 17 etb -// 18 can 19 em 1a sub 1b esc 1c fs 1d gs 1e rs 1f us -// 20 sp 21 ! 22 " 23 # 24 $ 25 % 26 & 27 ' -// 28 ( 29 ) 2a * 2b + 2c , 2d - 2e . 2f / -// 30 0 31 1 32 2 33 3 34 4 35 5 36 6 37 7 -// 38 8 39 9 3a : 3b ; 3c < 3d = 3e > 3f ? -// 40 @ 41 A 42 B 43 C 44 D 45 E 46 F 47 G -// 48 H 49 I 4a J 4b K 4c L 4d M 4e N 4f O -// 50 P 51 Q 52 R 53 S 54 T 55 U 56 V 57 W -// 58 X 59 Y 5a Z 5b [ 5c \ 5d ] 5e ^ 5f _ -// 60 ` 61 a 62 b 63 c 64 d 65 e 66 f 67 g -// 68 h 69 i 6a j 6b k 6c l 6d m 6e n 6f o -// 70 p 71 q 72 r 73 s 74 t 75 u 76 v 77 w -// 78 x 79 y 7a z 7b { 7c | 7d } 7e ~ 7f del -//--------------------------------------------------------------------- - -//----- Execute a Vi Command ----------------------------------- -static void do_cmd(Byte c) -{ - Byte c1, *p, *q, *msg, buf[9], *save_dot; - int cnt, i, j, dir, yf; - - c1 = c; // quiet the compiler - cnt = yf = dir = 0; // quiet the compiler - p = q = save_dot = msg = buf; // quiet the compiler - memset(buf, '\0', 9); // clear buf - - /* if this is a cursor key, skip these checks */ - switch (c) { - case VI_K_UP: - case VI_K_DOWN: - case VI_K_LEFT: - case VI_K_RIGHT: - case VI_K_HOME: - case VI_K_END: - case VI_K_PAGEUP: - case VI_K_PAGEDOWN: - goto key_cmd_mode; - } - - if (cmd_mode == 2) { - // we are 'R'eplacing the current *dot with new char - if (*dot == '\n') { - // don't Replace past E-o-l - cmd_mode = 1; // convert to insert - } else { - if (1 <= c && c <= 127) { // only ASCII chars - if (c != 27) - dot = yank_delete(dot, dot, 0, YANKDEL); // delete char - dot = char_insert(dot, c); // insert new char - } - goto dc1; - } - } - if (cmd_mode == 1) { - // hitting "Insert" twice means "R" replace mode - if (c == VI_K_INSERT) goto dc5; - // insert the char c at "dot" - if (1 <= c && c <= 127) { - dot = char_insert(dot, c); // only ASCII chars - } - goto dc1; - } - -key_cmd_mode: - 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 -#ifdef CONFIG_FEATURE_VI_CRASHME - case 0x14: // dc4 ctrl-T - crashme = (crashme == 0) ? 1 : 0; - break; -#endif /* CONFIG_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 'u': // u- FIXME- there is no undo - //case 'v': // v- - default: // unrecognised command - buf[0] = c; - buf[1] = '\0'; - if (c <= ' ') { - buf[0] = '^'; - buf[1] = c + '@'; - buf[2] = '\0'; - } - ni((Byte *) buf); - end_cmd_q(); // stop adding to q - case 0x00: // nul- ignore - break; - case 2: // ctrl-B scroll up full screen - case VI_K_PAGEUP: // Cursor Key Page Up - dot_scroll(rows - 2, -1); - break; -#ifdef CONFIG_FEATURE_VI_USE_SIGNALS - case 0x03: // ctrl-C interrupt - longjmp(restart, 1); - break; - case 26: // ctrl-Z suspend - suspend_sig(SIGTSTP); - break; -#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */ - case 4: // ctrl-D scroll down half screen - dot_scroll((rows - 2) / 2, 1); - break; - case 5: // ctrl-E scroll down one line - dot_scroll(1, 1); - break; - case 6: // ctrl-F scroll down full screen - case VI_K_PAGEDOWN: // Cursor Key Page Down - dot_scroll(rows - 2, 1); - break; - case 7: // ctrl-G show current status - edit_status(); - break; - case 'h': // h- move left - case VI_K_LEFT: // cursor key Left - case 8: // ctrl-H- move left (This may be ERASE char) - case 127: // DEL- move left (This may be ERASE char) - if (cmdcnt-- > 1) { - do_cmd(c); - } // repeat cnt - dot_left(); - break; - case 10: // Newline ^J - case 'j': // j- goto next line, same col - case VI_K_DOWN: // cursor key Down - if (cmdcnt-- > 1) { - do_cmd(c); - } // repeat cnt - dot_next(); // go to next B-o-l - dot = move_to_col(dot, ccol + offset); // try stay in same col - break; - case 12: // ctrl-L force redraw whole screen - case 18: // ctrl-R force redraw - 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 - refresh(TRUE); // this will redraw the entire display - break; - case 13: // Carriage Return ^M - case '+': // +- goto next line - if (cmdcnt-- > 1) { - do_cmd(c); - } // repeat cnt - dot_next(); - dot_skip_over_ws(); - break; - case 21: // ctrl-U scroll up half screen - dot_scroll((rows - 2) / 2, -1); - break; - case 25: // ctrl-Y scroll up one line - dot_scroll(1, -1); - break; - case 27: // esc - if (cmd_mode == 0) - indicate_error(c); - cmd_mode = 0; // stop insrting - end_cmd_q(); - *status_buffer = '\0'; // clear status buffer - break; - case ' ': // move right - case 'l': // move right - case VI_K_RIGHT: // Cursor Key Right - if (cmdcnt-- > 1) { - do_cmd(c); - } // repeat cnt - dot_right(); - break; -#ifdef CONFIG_FEATURE_VI_YANKMARK - case '"': // "- name a register to use for Delete/Yank - c1 = get_one_char(); - c1 = tolower(c1); - if (islower(c1)) { - YDreg = c1 - 'a'; - } else { - indicate_error(c); - } - break; - case '\'': // '- goto a specific mark - c1 = get_one_char(); - c1 = tolower(c1); - if (islower(c1)) { - c1 = c1 - 'a'; - // get the b-o-l - q = mark[(int) c1]; - if (text <= q && q < end) { - dot = q; - dot_begin(); // go to B-o-l - dot_skip_over_ws(); - } - } else if (c1 == '\'') { // goto previous context - dot = swap_context(dot); // swap current and previous context - dot_begin(); // go to B-o-l - dot_skip_over_ws(); - } else { - indicate_error(c); - } - break; - case 'm': // m- Mark a line - // this is really stupid. If there are any inserts or deletes - // between text[0] and dot then this mark will not point to the - // correct location! It could be off by many lines! - // Well..., at least its quick and dirty. - c1 = get_one_char(); - c1 = tolower(c1); - if (islower(c1)) { - c1 = c1 - 'a'; - // remember the line - mark[(int) c1] = dot; - } else { - indicate_error(c); - } - break; - case 'P': // P- Put register before - case 'p': // p- put register after - p = reg[YDreg]; - if (p == 0) { - psbs("Nothing in register %c", what_reg()); - break; - } - // are we putting whole lines or strings - if (strchr((char *) p, '\n') != NULL) { - if (c == 'P') { - dot_begin(); // putting lines- Put above - } - if (c == 'p') { - // are we putting after very last line? - if (end_line(dot) == (end - 1)) { - dot = end; // force dot to end of text[] - } else { - dot_next(); // next line, then put before - } - } - } else { - if (c == 'p') - dot_right(); // move to right, can move to NL - } - dot = string_insert(dot, p); // insert the string - end_cmd_q(); // stop adding to q - break; - case 'U': // U- Undo; replace current line with original version - if (reg[Ureg] != 0) { - p = begin_line(dot); - q = end_line(dot); - p = text_hole_delete(p, q); // delete cur line - p = string_insert(p, reg[Ureg]); // insert orig line - dot = p; - dot_skip_over_ws(); - } - break; -#endif /* CONFIG_FEATURE_VI_YANKMARK */ - case '$': // $- goto end of line - case VI_K_END: // Cursor Key End - if (cmdcnt-- > 1) { - do_cmd(c); - } // repeat cnt - dot = end_line(dot + 1); - break; - case '%': // %- find matching char of pair () [] {} - for (q = dot; q < end && *q != '\n'; q++) { - if (strchr("()[]{}", *q) != NULL) { - // we found half of a pair - p = find_pair(q, *q); - if (p == NULL) { - indicate_error(c); - } else { - dot = p; - } - break; - } - } - if (*q == '\n') - indicate_error(c); - break; - case 'f': // f- forward to a user specified char - last_forward_char = get_one_char(); // get the search char - // - // dont seperate these two commands. 'f' depends on ';' - // - //**** fall thru to ... 'i' - case ';': // ;- look at rest of line for last forward char - if (cmdcnt-- > 1) { - do_cmd(';'); - } // repeat cnt - if (last_forward_char == 0) break; - q = dot + 1; - while (q < end - 1 && *q != '\n' && *q != last_forward_char) { - q++; - } - if (*q == last_forward_char) - dot = q; - break; - case '-': // -- goto prev line - if (cmdcnt-- > 1) { - do_cmd(c); - } // repeat cnt - dot_prev(); - dot_skip_over_ws(); - break; -#ifdef CONFIG_FEATURE_VI_DOT_CMD - case '.': // .- repeat the last modifying command - // Stuff the last_modifying_cmd back into stdin - // and let it be re-executed. - if (last_modifying_cmd != 0) { - ioq = ioq_start = (Byte *) xstrdup((char *) last_modifying_cmd); - } - break; -#endif /* CONFIG_FEATURE_VI_DOT_CMD */ -#ifdef CONFIG_FEATURE_VI_SEARCH - case '?': // /- search for a pattern - case '/': // /- search for a pattern - buf[0] = c; - buf[1] = '\0'; - q = get_input_line(buf); // get input line- use "status line" - if (strlen((char *) q) == 1) - goto dc3; // if no pat re-use old pat - if (strlen((char *) q) > 1) { // new pat- save it and find - // there is a new pat - if (last_search_pattern != 0) { - free(last_search_pattern); - } - last_search_pattern = (Byte *) xstrdup((char *) q); - goto dc3; // now find the pattern - } - // user changed mind and erased the "/"- do nothing - break; - case 'N': // N- backward search for last pattern - if (cmdcnt-- > 1) { - do_cmd(c); - } // repeat cnt - dir = BACK; // assume BACKWARD search - p = dot - 1; - if (last_search_pattern[0] == '?') { - dir = FORWARD; - p = dot + 1; - } - goto dc4; // now search for pattern - break; - case 'n': // n- repeat search for last pattern - // search rest of text[] starting at next char - // if search fails return orignal "p" not the "p+1" address - if (cmdcnt-- > 1) { - do_cmd(c); - } // repeat cnt - dc3: - if (last_search_pattern == 0) { - msg = (Byte *) "No previous regular expression"; - goto dc2; - } - if (last_search_pattern[0] == '/') { - dir = FORWARD; // assume FORWARD search - p = dot + 1; - } - if (last_search_pattern[0] == '?') { - dir = BACK; - p = dot - 1; - } - dc4: - q = char_search(p, last_search_pattern + 1, dir, FULL); - if (q != NULL) { - dot = q; // good search, update "dot" - msg = (Byte *) ""; - goto dc2; - } - // no pattern found between "dot" and "end"- continue at top - p = text; - if (dir == BACK) { - p = end - 1; - } - q = char_search(p, last_search_pattern + 1, dir, FULL); - if (q != NULL) { // found something - dot = q; // found new pattern- goto it - msg = (Byte *) "search hit BOTTOM, continuing at TOP"; - if (dir == BACK) { - msg = (Byte *) "search hit TOP, continuing at BOTTOM"; - } - } else { - msg = (Byte *) "Pattern not found"; - } - dc2: - psbs("%s", msg); - break; - case '{': // {- move backward paragraph - q = char_search(dot, (Byte *) "\n\n", BACK, FULL); - if (q != NULL) { // found blank line - dot = next_line(q); // move to next blank line - } - break; - case '}': // }- move forward paragraph - q = char_search(dot, (Byte *) "\n\n", FORWARD, FULL); - if (q != NULL) { // found blank line - dot = next_line(q); // move to next blank line - } - break; -#endif /* CONFIG_FEATURE_VI_SEARCH */ - case '0': // 0- goto begining of line - case '1': // 1- - case '2': // 2- - case '3': // 3- - case '4': // 4- - case '5': // 5- - case '6': // 6- - case '7': // 7- - case '8': // 8- - case '9': // 9- - if (c == '0' && cmdcnt < 1) { - dot_begin(); // this was a standalone zero - } else { - cmdcnt = cmdcnt * 10 + (c - '0'); // this 0 is part of a number - } - break; - case ':': // :- the colon mode commands - p = get_input_line((Byte *) ":"); // get input line- use "status line" -#ifdef CONFIG_FEATURE_VI_COLON - colon(p); // execute the command -#else /* CONFIG_FEATURE_VI_COLON */ - 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 && p[1] != '!') { - psbs("No write since last change (:quit! overrides)"); - } else { - editing = 0; - } - } else if (strncasecmp((char *) p, "write", cnt) == 0 || - strncasecmp((char *) p, "wq", cnt) == 0 || - strncasecmp((char *) p, "x", cnt) == 0) { - cnt = file_write(cfn, text, end - 1); - file_modified = FALSE; - psb("\"%s\" %dL, %dC", cfn, count_lines(text, end - 1), cnt); - if (p[0] == 'x' || 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 *) p); - } -#endif /* CONFIG_FEATURE_VI_COLON */ - break; - case '<': // <- Left shift something - case '>': // >- Right shift something - cnt = count_lines(text, dot); // remember what line we are on - c1 = get_one_char(); // get the type of thing to delete - find_range(&p, &q, c1); - (void) yank_delete(p, q, 1, YANKONLY); // save copy before change - p = begin_line(p); - q = end_line(q); - i = count_lines(p, q); // # of lines we are shifting - for ( ; i > 0; i--, p = next_line(p)) { - if (c == '<') { - // shift left- remove tab or 8 spaces - if (*p == '\t') { - // shrink buffer 1 char - (void) text_hole_delete(p, p); - } else if (*p == ' ') { - // we should be calculating columns, not just SPACE - for (j = 0; *p == ' ' && j < tabstop; j++) { - (void) text_hole_delete(p, p); - } - } - } else if (c == '>') { - // shift right -- add tab or 8 spaces - (void) char_insert(p, '\t'); - } - } - dot = find_line(cnt); // what line were we on - dot_skip_over_ws(); - end_cmd_q(); // stop adding to q - break; - case 'A': // A- append at e-o-l - dot_end(); // go to e-o-l - //**** fall thru to ... 'a' - case 'a': // a- append after current char - if (*dot != '\n') - dot++; - goto dc_i; - break; - case 'B': // B- back a blank-delimited Word - case 'E': // E- end of a blank-delimited word - case 'W': // W- forward a blank-delimited word - if (cmdcnt-- > 1) { - do_cmd(c); - } // repeat cnt - dir = FORWARD; - if (c == 'B') - dir = BACK; - if (c == 'W' || isspace(dot[dir])) { - dot = skip_thing(dot, 1, dir, S_TO_WS); - dot = skip_thing(dot, 2, dir, S_OVER_WS); - } - if (c != 'W') - dot = skip_thing(dot, 1, dir, S_BEFORE_WS); - break; - case 'C': // C- Change to e-o-l - case 'D': // D- delete to e-o-l - save_dot = dot; - dot = dollar_line(dot); // move to before NL - // copy text into a register and delete - dot = yank_delete(save_dot, dot, 0, YANKDEL); // delete to e-o-l - if (c == 'C') - goto dc_i; // start inserting -#ifdef CONFIG_FEATURE_VI_DOT_CMD - if (c == 'D') - end_cmd_q(); // stop adding to q -#endif /* CONFIG_FEATURE_VI_DOT_CMD */ - break; - 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_skip_over_ws(); - break; - case 'H': // H- goto top line on screen - dot = screenbegin; - if (cmdcnt > (rows - 1)) { - cmdcnt = (rows - 1); - } - if (cmdcnt-- > 1) { - do_cmd('+'); - } // repeat cnt - dot_skip_over_ws(); - break; - case 'I': // I- insert before first non-blank - dot_begin(); // 0 - dot_skip_over_ws(); - //**** fall thru to ... 'i' - case 'i': // i- insert before current char - case VI_K_INSERT: // Cursor Key Insert - dc_i: - cmd_mode = 1; // start insrting - psb("-- Insert --"); - break; - case 'J': // J- join current and next lines together - if (cmdcnt-- > 2) { - do_cmd(c); - } // repeat cnt - dot_end(); // move to NL - if (dot < end - 1) { // make sure not last char in text[] - *dot++ = ' '; // replace NL with space - while (isblnk(*dot)) { // delete leading WS - dot_delete(); - } - } - end_cmd_q(); // stop adding to q - break; - case 'L': // L- goto bottom line on screen - dot = end_screen(); - if (cmdcnt > (rows - 1)) { - cmdcnt = (rows - 1); - } - if (cmdcnt-- > 1) { - do_cmd('-'); - } // repeat cnt - dot_begin(); - dot_skip_over_ws(); - break; - 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 ESC -i - p = begin_line(dot); - if (p[-1] == '\n') { - dot_prev(); - case 'o': // o- open a empty line below; Yes, I know it is in the middle of the "if (..." - dot_end(); - dot = char_insert(dot, '\n'); - } else { - dot_begin(); // 0 - dot = char_insert(dot, '\n'); // i\n ESC - dot_prev(); // - - } - goto dc_i; - break; - case 'R': // R- continuous Replace char - dc5: - cmd_mode = 2; - psb("-- Replace --"); - break; - case 'X': // X- delete char before dot - case 'x': // x- delete the current char - case 's': // s- substitute the current char - if (cmdcnt-- > 1) { - do_cmd(c); - } // repeat cnt - dir = 0; - if (c == 'X') - dir = -1; - if (dot[dir] != '\n') { - if (c == 'X') - dot--; // delete prev char - dot = yank_delete(dot, dot, 0, YANKDEL); // delete char - } - if (c == 's') - goto dc_i; // start insrting - end_cmd_q(); // stop adding to q - break; - case 'Z': // Z- if modified, {write}; exit - // ZZ means to save file (if necessary), then exit - c1 = get_one_char(); - if (c1 != 'Z') { - indicate_error(c); - break; - } - if (file_modified -#ifdef CONFIG_FEATURE_VI_READONLY - && ! vi_readonly - && ! readonly -#endif /* CONFIG_FEATURE_VI_READONLY */ - ) { - cnt = file_write(cfn, text, end - 1); - if (cnt == (end - 1 - text + 1)) { - editing = 0; - } - } else { - editing = 0; - } - break; - case '^': // ^- move to first non-blank on line - dot_begin(); - dot_skip_over_ws(); - break; - case 'b': // b- back a word - case 'e': // e- end of word - if (cmdcnt-- > 1) { - do_cmd(c); - } // repeat cnt - dir = FORWARD; - if (c == 'b') - dir = BACK; - if ((dot + dir) < text || (dot + dir) > end - 1) - break; - dot += dir; - if (isspace(*dot)) { - dot = skip_thing(dot, (c == 'e') ? 2 : 1, dir, S_OVER_WS); - } - if (isalnum(*dot) || *dot == '_') { - dot = skip_thing(dot, 1, dir, S_END_ALNUM); - } else if (ispunct(*dot)) { - dot = skip_thing(dot, 1, dir, S_END_PUNCT); - } - break; - case 'c': // c- change something - case 'd': // d- delete something -#ifdef CONFIG_FEATURE_VI_YANKMARK - case 'y': // y- yank something - case 'Y': // Y- Yank a line -#endif /* CONFIG_FEATURE_VI_YANKMARK */ - yf = YANKDEL; // assume either "c" or "d" -#ifdef CONFIG_FEATURE_VI_YANKMARK - if (c == 'y' || c == 'Y') - yf = YANKONLY; -#endif /* CONFIG_FEATURE_VI_YANKMARK */ - c1 = 'y'; - if (c != 'Y') - c1 = get_one_char(); // get the type of thing to delete - find_range(&p, &q, c1); - if (c1 == 27) { // ESC- user changed mind and wants out - c = c1 = 27; // Escape- do nothing - } else if (strchr("wW", c1)) { - if (c == 'c') { - // don't include trailing WS as part of word - while (isblnk(*q)) { - if (q <= text || q[-1] == '\n') - break; - q--; - } - } - dot = yank_delete(p, q, 0, yf); // delete word - } 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)) { - // multiple line copy text into a register and delete - dot = yank_delete(p, q, 1, yf); // delete lines - if (c == 'c') { - dot = char_insert(dot, '\n'); - // on the last line of file don't move to prev line - if (dot != (end-1)) { - dot_prev(); - } - } else if (c == 'd') { - dot_begin(); - dot_skip_over_ws(); - } - } else { - // could not recognize object - c = c1 = 27; // error- - indicate_error(c); - } - if (c1 != 27) { - // if CHANGING, not deleting, start inserting after the delete - if (c == 'c') { - strcpy((char *) buf, "Change"); - goto dc_i; // start inserting - } - if (c == 'd') { - strcpy((char *) buf, "Delete"); - } -#ifdef CONFIG_FEATURE_VI_YANKMARK - if (c == 'y' || c == 'Y') { - strcpy((char *) buf, "Yank"); - } - p = reg[YDreg]; - q = p + strlen((char *) p); - for (cnt = 0; p <= q; p++) { - if (*p == '\n') - cnt++; - } - psb("%s %d lines (%d chars) using [%c]", - buf, cnt, strlen((char *) reg[YDreg]), what_reg()); -#endif /* CONFIG_FEATURE_VI_YANKMARK */ - end_cmd_q(); // stop adding to q - } - break; - case 'k': // k- goto prev line, same col - case VI_K_UP: // cursor key Up - if (cmdcnt-- > 1) { - do_cmd(c); - } // repeat cnt - dot_prev(); - dot = move_to_col(dot, ccol + offset); // try stay in same col - break; - case 'r': // r- replace the current char with user input - c1 = get_one_char(); // get the replacement char - if (*dot != '\n') { - *dot = c1; - file_modified = TRUE; // has the file been modified - } - 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); - } // repeat cnt - if (isalnum(*dot) || *dot == '_') { // we are on ALNUM - dot = skip_thing(dot, 1, FORWARD, S_END_ALNUM); - } else if (ispunct(*dot)) { // we are on PUNCT - dot = skip_thing(dot, 1, FORWARD, S_END_PUNCT); - } - if (dot < end - 1) - dot++; // move over word - if (isspace(*dot)) { - dot = skip_thing(dot, 2, FORWARD, S_OVER_WS); - } - break; - case 'z': // z- - c1 = get_one_char(); // get the replacement char - cnt = 0; - if (c1 == '.') - cnt = (rows - 2) / 2; // put dot at center - if (c1 == '-') - cnt = rows - 2; // put dot at bottom - screenbegin = begin_line(dot); // start dot at top - dot_scroll(cnt, -1); - break; - case '|': // |- move to column "cmdcnt" - dot = move_to_col(dot, cmdcnt - 1); // try to move to column - break; - case '~': // ~- flip the case of letters a-z -> A-Z - if (cmdcnt-- > 1) { - do_cmd(c); - } // repeat cnt - if (islower(*dot)) { - *dot = toupper(*dot); - file_modified = TRUE; // has the file been modified - } else if (isupper(*dot)) { - *dot = tolower(*dot); - file_modified = TRUE; // has the file been modified - } - dot_right(); - end_cmd_q(); // stop adding to q - break; - //----- The Cursor and Function Keys ----------------------------- - case VI_K_HOME: // Cursor Key Home - dot_begin(); - break; - // The Fn keys could point to do_macro which could translate them - case VI_K_FUN1: // Function Key F1 - case VI_K_FUN2: // Function Key F2 - case VI_K_FUN3: // Function Key F3 - case VI_K_FUN4: // Function Key F4 - case VI_K_FUN5: // Function Key F5 - case VI_K_FUN6: // Function Key F6 - case VI_K_FUN7: // Function Key F7 - case VI_K_FUN8: // Function Key F8 - case VI_K_FUN9: // Function Key F9 - case VI_K_FUN10: // Function Key F10 - case VI_K_FUN11: // Function Key F11 - case VI_K_FUN12: // Function Key F12 - break; - } - - dc1: - // if text[] just became empty, add back an empty line - if (end == text) { - (void) char_insert(text, '\n'); // start empty buf with dummy line - dot = text; - } - // it is OK for dot to exactly equal to end, otherwise check dot validity - if (dot != end) { - dot = bound_dot(dot); // make sure "dot" is valid - } -#ifdef CONFIG_FEATURE_VI_YANKMARK - check_context(c); // update the current context -#endif /* CONFIG_FEATURE_VI_YANKMARK */ - - if (!isdigit(c)) - cmdcnt = 0; // cmd was not a number, reset cmdcnt - cnt = dot - begin_line(dot); - // Try to stay off of the Newline - if (*dot == '\n' && cnt > 0 && cmd_mode == 0) - dot--; -} - //----- The Colon commands ------------------------------------- #ifdef CONFIG_FEATURE_VI_COLON static Byte *get_one_address(Byte * p, int *addr) // get colon addr, if present @@ -2318,7 +1447,7 @@ static Byte *end_line(Byte * p) // return pointer to NL of cur line line return (p); } -static Byte *dollar_line(Byte * p) // return pointer to just before NL line +extern inline Byte *dollar_line(Byte * p) // return pointer to just before NL line { while (p < end - 1 && *p != '\n') p++; // go to cur line E-o-l @@ -3010,7 +2139,7 @@ static void show_help(void) ); } -static void print_literal(Byte * buf, Byte * s) // copy s to buf, convert unprintable +extern inline void print_literal(Byte * buf, Byte * s) // copy s to buf, convert unprintable { Byte c, b[2]; @@ -3142,7 +2271,7 @@ static void check_context(Byte cmd) return; } -static Byte *swap_context(Byte * p) // goto new context for '' command make this the current context +extern inline Byte *swap_context(Byte * p) // goto new context for '' command make this the current context { Byte *tmp; @@ -3962,3 +3091,874 @@ static void refresh(int full_screen) if (offset != old_offset) old_offset = offset; } + +//--------------------------------------------------------------------- +//----- the Ascii Chart ----------------------------------------------- +// +// 00 nul 01 soh 02 stx 03 etx 04 eot 05 enq 06 ack 07 bel +// 08 bs 09 ht 0a nl 0b vt 0c np 0d cr 0e so 0f si +// 10 dle 11 dc1 12 dc2 13 dc3 14 dc4 15 nak 16 syn 17 etb +// 18 can 19 em 1a sub 1b esc 1c fs 1d gs 1e rs 1f us +// 20 sp 21 ! 22 " 23 # 24 $ 25 % 26 & 27 ' +// 28 ( 29 ) 2a * 2b + 2c , 2d - 2e . 2f / +// 30 0 31 1 32 2 33 3 34 4 35 5 36 6 37 7 +// 38 8 39 9 3a : 3b ; 3c < 3d = 3e > 3f ? +// 40 @ 41 A 42 B 43 C 44 D 45 E 46 F 47 G +// 48 H 49 I 4a J 4b K 4c L 4d M 4e N 4f O +// 50 P 51 Q 52 R 53 S 54 T 55 U 56 V 57 W +// 58 X 59 Y 5a Z 5b [ 5c \ 5d ] 5e ^ 5f _ +// 60 ` 61 a 62 b 63 c 64 d 65 e 66 f 67 g +// 68 h 69 i 6a j 6b k 6c l 6d m 6e n 6f o +// 70 p 71 q 72 r 73 s 74 t 75 u 76 v 77 w +// 78 x 79 y 7a z 7b { 7c | 7d } 7e ~ 7f del +//--------------------------------------------------------------------- + +//----- Execute a Vi Command ----------------------------------- +static void do_cmd(Byte c) +{ + Byte c1, *p, *q, *msg, buf[9], *save_dot; + int cnt, i, j, dir, yf; + + c1 = c; // quiet the compiler + cnt = yf = dir = 0; // quiet the compiler + p = q = save_dot = msg = buf; // quiet the compiler + memset(buf, '\0', 9); // clear buf + + /* if this is a cursor key, skip these checks */ + switch (c) { + case VI_K_UP: + case VI_K_DOWN: + case VI_K_LEFT: + case VI_K_RIGHT: + case VI_K_HOME: + case VI_K_END: + case VI_K_PAGEUP: + case VI_K_PAGEDOWN: + goto key_cmd_mode; + } + + if (cmd_mode == 2) { + // we are 'R'eplacing the current *dot with new char + if (*dot == '\n') { + // don't Replace past E-o-l + cmd_mode = 1; // convert to insert + } else { + if (1 <= c && c <= 127) { // only ASCII chars + if (c != 27) + dot = yank_delete(dot, dot, 0, YANKDEL); // delete char + dot = char_insert(dot, c); // insert new char + } + goto dc1; + } + } + if (cmd_mode == 1) { + // hitting "Insert" twice means "R" replace mode + if (c == VI_K_INSERT) goto dc5; + // insert the char c at "dot" + if (1 <= c && c <= 127) { + dot = char_insert(dot, c); // only ASCII chars + } + goto dc1; + } + +key_cmd_mode: + 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 +#ifdef CONFIG_FEATURE_VI_CRASHME + case 0x14: // dc4 ctrl-T + crashme = (crashme == 0) ? 1 : 0; + break; +#endif /* CONFIG_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 'u': // u- FIXME- there is no undo + //case 'v': // v- + default: // unrecognised command + buf[0] = c; + buf[1] = '\0'; + if (c <= ' ') { + buf[0] = '^'; + buf[1] = c + '@'; + buf[2] = '\0'; + } + ni((Byte *) buf); + end_cmd_q(); // stop adding to q + case 0x00: // nul- ignore + break; + case 2: // ctrl-B scroll up full screen + case VI_K_PAGEUP: // Cursor Key Page Up + dot_scroll(rows - 2, -1); + break; +#ifdef CONFIG_FEATURE_VI_USE_SIGNALS + case 0x03: // ctrl-C interrupt + longjmp(restart, 1); + break; + case 26: // ctrl-Z suspend + suspend_sig(SIGTSTP); + break; +#endif /* CONFIG_FEATURE_VI_USE_SIGNALS */ + case 4: // ctrl-D scroll down half screen + dot_scroll((rows - 2) / 2, 1); + break; + case 5: // ctrl-E scroll down one line + dot_scroll(1, 1); + break; + case 6: // ctrl-F scroll down full screen + case VI_K_PAGEDOWN: // Cursor Key Page Down + dot_scroll(rows - 2, 1); + break; + case 7: // ctrl-G show current status + edit_status(); + break; + case 'h': // h- move left + case VI_K_LEFT: // cursor key Left + case 8: // ctrl-H- move left (This may be ERASE char) + case 127: // DEL- move left (This may be ERASE char) + if (cmdcnt-- > 1) { + do_cmd(c); + } // repeat cnt + dot_left(); + break; + case 10: // Newline ^J + case 'j': // j- goto next line, same col + case VI_K_DOWN: // cursor key Down + if (cmdcnt-- > 1) { + do_cmd(c); + } // repeat cnt + dot_next(); // go to next B-o-l + dot = move_to_col(dot, ccol + offset); // try stay in same col + break; + case 12: // ctrl-L force redraw whole screen + case 18: // ctrl-R force redraw + 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 + refresh(TRUE); // this will redraw the entire display + break; + case 13: // Carriage Return ^M + case '+': // +- goto next line + if (cmdcnt-- > 1) { + do_cmd(c); + } // repeat cnt + dot_next(); + dot_skip_over_ws(); + break; + case 21: // ctrl-U scroll up half screen + dot_scroll((rows - 2) / 2, -1); + break; + case 25: // ctrl-Y scroll up one line + dot_scroll(1, -1); + break; + case 27: // esc + if (cmd_mode == 0) + indicate_error(c); + cmd_mode = 0; // stop insrting + end_cmd_q(); + *status_buffer = '\0'; // clear status buffer + break; + case ' ': // move right + case 'l': // move right + case VI_K_RIGHT: // Cursor Key Right + if (cmdcnt-- > 1) { + do_cmd(c); + } // repeat cnt + dot_right(); + break; +#ifdef CONFIG_FEATURE_VI_YANKMARK + case '"': // "- name a register to use for Delete/Yank + c1 = get_one_char(); + c1 = tolower(c1); + if (islower(c1)) { + YDreg = c1 - 'a'; + } else { + indicate_error(c); + } + break; + case '\'': // '- goto a specific mark + c1 = get_one_char(); + c1 = tolower(c1); + if (islower(c1)) { + c1 = c1 - 'a'; + // get the b-o-l + q = mark[(int) c1]; + if (text <= q && q < end) { + dot = q; + dot_begin(); // go to B-o-l + dot_skip_over_ws(); + } + } else if (c1 == '\'') { // goto previous context + dot = swap_context(dot); // swap current and previous context + dot_begin(); // go to B-o-l + dot_skip_over_ws(); + } else { + indicate_error(c); + } + break; + case 'm': // m- Mark a line + // this is really stupid. If there are any inserts or deletes + // between text[0] and dot then this mark will not point to the + // correct location! It could be off by many lines! + // Well..., at least its quick and dirty. + c1 = get_one_char(); + c1 = tolower(c1); + if (islower(c1)) { + c1 = c1 - 'a'; + // remember the line + mark[(int) c1] = dot; + } else { + indicate_error(c); + } + break; + case 'P': // P- Put register before + case 'p': // p- put register after + p = reg[YDreg]; + if (p == 0) { + psbs("Nothing in register %c", what_reg()); + break; + } + // are we putting whole lines or strings + if (strchr((char *) p, '\n') != NULL) { + if (c == 'P') { + dot_begin(); // putting lines- Put above + } + if (c == 'p') { + // are we putting after very last line? + if (end_line(dot) == (end - 1)) { + dot = end; // force dot to end of text[] + } else { + dot_next(); // next line, then put before + } + } + } else { + if (c == 'p') + dot_right(); // move to right, can move to NL + } + dot = string_insert(dot, p); // insert the string + end_cmd_q(); // stop adding to q + break; + case 'U': // U- Undo; replace current line with original version + if (reg[Ureg] != 0) { + p = begin_line(dot); + q = end_line(dot); + p = text_hole_delete(p, q); // delete cur line + p = string_insert(p, reg[Ureg]); // insert orig line + dot = p; + dot_skip_over_ws(); + } + break; +#endif /* CONFIG_FEATURE_VI_YANKMARK */ + case '$': // $- goto end of line + case VI_K_END: // Cursor Key End + if (cmdcnt-- > 1) { + do_cmd(c); + } // repeat cnt + dot = end_line(dot + 1); + break; + case '%': // %- find matching char of pair () [] {} + for (q = dot; q < end && *q != '\n'; q++) { + if (strchr("()[]{}", *q) != NULL) { + // we found half of a pair + p = find_pair(q, *q); + if (p == NULL) { + indicate_error(c); + } else { + dot = p; + } + break; + } + } + if (*q == '\n') + indicate_error(c); + break; + case 'f': // f- forward to a user specified char + last_forward_char = get_one_char(); // get the search char + // + // dont seperate these two commands. 'f' depends on ';' + // + //**** fall thru to ... 'i' + case ';': // ;- look at rest of line for last forward char + if (cmdcnt-- > 1) { + do_cmd(';'); + } // repeat cnt + if (last_forward_char == 0) break; + q = dot + 1; + while (q < end - 1 && *q != '\n' && *q != last_forward_char) { + q++; + } + if (*q == last_forward_char) + dot = q; + break; + case '-': // -- goto prev line + if (cmdcnt-- > 1) { + do_cmd(c); + } // repeat cnt + dot_prev(); + dot_skip_over_ws(); + break; +#ifdef CONFIG_FEATURE_VI_DOT_CMD + case '.': // .- repeat the last modifying command + // Stuff the last_modifying_cmd back into stdin + // and let it be re-executed. + if (last_modifying_cmd != 0) { + ioq = ioq_start = (Byte *) xstrdup((char *) last_modifying_cmd); + } + break; +#endif /* CONFIG_FEATURE_VI_DOT_CMD */ +#ifdef CONFIG_FEATURE_VI_SEARCH + case '?': // /- search for a pattern + case '/': // /- search for a pattern + buf[0] = c; + buf[1] = '\0'; + q = get_input_line(buf); // get input line- use "status line" + if (strlen((char *) q) == 1) + goto dc3; // if no pat re-use old pat + if (strlen((char *) q) > 1) { // new pat- save it and find + // there is a new pat + if (last_search_pattern != 0) { + free(last_search_pattern); + } + last_search_pattern = (Byte *) xstrdup((char *) q); + goto dc3; // now find the pattern + } + // user changed mind and erased the "/"- do nothing + break; + case 'N': // N- backward search for last pattern + if (cmdcnt-- > 1) { + do_cmd(c); + } // repeat cnt + dir = BACK; // assume BACKWARD search + p = dot - 1; + if (last_search_pattern[0] == '?') { + dir = FORWARD; + p = dot + 1; + } + goto dc4; // now search for pattern + break; + case 'n': // n- repeat search for last pattern + // search rest of text[] starting at next char + // if search fails return orignal "p" not the "p+1" address + if (cmdcnt-- > 1) { + do_cmd(c); + } // repeat cnt + dc3: + if (last_search_pattern == 0) { + msg = (Byte *) "No previous regular expression"; + goto dc2; + } + if (last_search_pattern[0] == '/') { + dir = FORWARD; // assume FORWARD search + p = dot + 1; + } + if (last_search_pattern[0] == '?') { + dir = BACK; + p = dot - 1; + } + dc4: + q = char_search(p, last_search_pattern + 1, dir, FULL); + if (q != NULL) { + dot = q; // good search, update "dot" + msg = (Byte *) ""; + goto dc2; + } + // no pattern found between "dot" and "end"- continue at top + p = text; + if (dir == BACK) { + p = end - 1; + } + q = char_search(p, last_search_pattern + 1, dir, FULL); + if (q != NULL) { // found something + dot = q; // found new pattern- goto it + msg = (Byte *) "search hit BOTTOM, continuing at TOP"; + if (dir == BACK) { + msg = (Byte *) "search hit TOP, continuing at BOTTOM"; + } + } else { + msg = (Byte *) "Pattern not found"; + } + dc2: + psbs("%s", msg); + break; + case '{': // {- move backward paragraph + q = char_search(dot, (Byte *) "\n\n", BACK, FULL); + if (q != NULL) { // found blank line + dot = next_line(q); // move to next blank line + } + break; + case '}': // }- move forward paragraph + q = char_search(dot, (Byte *) "\n\n", FORWARD, FULL); + if (q != NULL) { // found blank line + dot = next_line(q); // move to next blank line + } + break; +#endif /* CONFIG_FEATURE_VI_SEARCH */ + case '0': // 0- goto begining of line + case '1': // 1- + case '2': // 2- + case '3': // 3- + case '4': // 4- + case '5': // 5- + case '6': // 6- + case '7': // 7- + case '8': // 8- + case '9': // 9- + if (c == '0' && cmdcnt < 1) { + dot_begin(); // this was a standalone zero + } else { + cmdcnt = cmdcnt * 10 + (c - '0'); // this 0 is part of a number + } + break; + case ':': // :- the colon mode commands + p = get_input_line((Byte *) ":"); // get input line- use "status line" +#ifdef CONFIG_FEATURE_VI_COLON + colon(p); // execute the command +#else /* CONFIG_FEATURE_VI_COLON */ + 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 && p[1] != '!') { + psbs("No write since last change (:quit! overrides)"); + } else { + editing = 0; + } + } else if (strncasecmp((char *) p, "write", cnt) == 0 || + strncasecmp((char *) p, "wq", cnt) == 0 || + strncasecmp((char *) p, "x", cnt) == 0) { + cnt = file_write(cfn, text, end - 1); + file_modified = FALSE; + psb("\"%s\" %dL, %dC", cfn, count_lines(text, end - 1), cnt); + if (p[0] == 'x' || 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 *) p); + } +#endif /* CONFIG_FEATURE_VI_COLON */ + break; + case '<': // <- Left shift something + case '>': // >- Right shift something + cnt = count_lines(text, dot); // remember what line we are on + c1 = get_one_char(); // get the type of thing to delete + find_range(&p, &q, c1); + (void) yank_delete(p, q, 1, YANKONLY); // save copy before change + p = begin_line(p); + q = end_line(q); + i = count_lines(p, q); // # of lines we are shifting + for ( ; i > 0; i--, p = next_line(p)) { + if (c == '<') { + // shift left- remove tab or 8 spaces + if (*p == '\t') { + // shrink buffer 1 char + (void) text_hole_delete(p, p); + } else if (*p == ' ') { + // we should be calculating columns, not just SPACE + for (j = 0; *p == ' ' && j < tabstop; j++) { + (void) text_hole_delete(p, p); + } + } + } else if (c == '>') { + // shift right -- add tab or 8 spaces + (void) char_insert(p, '\t'); + } + } + dot = find_line(cnt); // what line were we on + dot_skip_over_ws(); + end_cmd_q(); // stop adding to q + break; + case 'A': // A- append at e-o-l + dot_end(); // go to e-o-l + //**** fall thru to ... 'a' + case 'a': // a- append after current char + if (*dot != '\n') + dot++; + goto dc_i; + break; + case 'B': // B- back a blank-delimited Word + case 'E': // E- end of a blank-delimited word + case 'W': // W- forward a blank-delimited word + if (cmdcnt-- > 1) { + do_cmd(c); + } // repeat cnt + dir = FORWARD; + if (c == 'B') + dir = BACK; + if (c == 'W' || isspace(dot[dir])) { + dot = skip_thing(dot, 1, dir, S_TO_WS); + dot = skip_thing(dot, 2, dir, S_OVER_WS); + } + if (c != 'W') + dot = skip_thing(dot, 1, dir, S_BEFORE_WS); + break; + case 'C': // C- Change to e-o-l + case 'D': // D- delete to e-o-l + save_dot = dot; + dot = dollar_line(dot); // move to before NL + // copy text into a register and delete + dot = yank_delete(save_dot, dot, 0, YANKDEL); // delete to e-o-l + if (c == 'C') + goto dc_i; // start inserting +#ifdef CONFIG_FEATURE_VI_DOT_CMD + if (c == 'D') + end_cmd_q(); // stop adding to q +#endif /* CONFIG_FEATURE_VI_DOT_CMD */ + break; + 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_skip_over_ws(); + break; + case 'H': // H- goto top line on screen + dot = screenbegin; + if (cmdcnt > (rows - 1)) { + cmdcnt = (rows - 1); + } + if (cmdcnt-- > 1) { + do_cmd('+'); + } // repeat cnt + dot_skip_over_ws(); + break; + case 'I': // I- insert before first non-blank + dot_begin(); // 0 + dot_skip_over_ws(); + //**** fall thru to ... 'i' + case 'i': // i- insert before current char + case VI_K_INSERT: // Cursor Key Insert + dc_i: + cmd_mode = 1; // start insrting + psb("-- Insert --"); + break; + case 'J': // J- join current and next lines together + if (cmdcnt-- > 2) { + do_cmd(c); + } // repeat cnt + dot_end(); // move to NL + if (dot < end - 1) { // make sure not last char in text[] + *dot++ = ' '; // replace NL with space + while (isblnk(*dot)) { // delete leading WS + dot_delete(); + } + } + end_cmd_q(); // stop adding to q + break; + case 'L': // L- goto bottom line on screen + dot = end_screen(); + if (cmdcnt > (rows - 1)) { + cmdcnt = (rows - 1); + } + if (cmdcnt-- > 1) { + do_cmd('-'); + } // repeat cnt + dot_begin(); + dot_skip_over_ws(); + break; + 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 ESC -i + p = begin_line(dot); + if (p[-1] == '\n') { + dot_prev(); + case 'o': // o- open a empty line below; Yes, I know it is in the middle of the "if (..." + dot_end(); + dot = char_insert(dot, '\n'); + } else { + dot_begin(); // 0 + dot = char_insert(dot, '\n'); // i\n ESC + dot_prev(); // - + } + goto dc_i; + break; + case 'R': // R- continuous Replace char + dc5: + cmd_mode = 2; + psb("-- Replace --"); + break; + case 'X': // X- delete char before dot + case 'x': // x- delete the current char + case 's': // s- substitute the current char + if (cmdcnt-- > 1) { + do_cmd(c); + } // repeat cnt + dir = 0; + if (c == 'X') + dir = -1; + if (dot[dir] != '\n') { + if (c == 'X') + dot--; // delete prev char + dot = yank_delete(dot, dot, 0, YANKDEL); // delete char + } + if (c == 's') + goto dc_i; // start insrting + end_cmd_q(); // stop adding to q + break; + case 'Z': // Z- if modified, {write}; exit + // ZZ means to save file (if necessary), then exit + c1 = get_one_char(); + if (c1 != 'Z') { + indicate_error(c); + break; + } + if (file_modified +#ifdef CONFIG_FEATURE_VI_READONLY + && ! vi_readonly + && ! readonly +#endif /* CONFIG_FEATURE_VI_READONLY */ + ) { + cnt = file_write(cfn, text, end - 1); + if (cnt == (end - 1 - text + 1)) { + editing = 0; + } + } else { + editing = 0; + } + break; + case '^': // ^- move to first non-blank on line + dot_begin(); + dot_skip_over_ws(); + break; + case 'b': // b- back a word + case 'e': // e- end of word + if (cmdcnt-- > 1) { + do_cmd(c); + } // repeat cnt + dir = FORWARD; + if (c == 'b') + dir = BACK; + if ((dot + dir) < text || (dot + dir) > end - 1) + break; + dot += dir; + if (isspace(*dot)) { + dot = skip_thing(dot, (c == 'e') ? 2 : 1, dir, S_OVER_WS); + } + if (isalnum(*dot) || *dot == '_') { + dot = skip_thing(dot, 1, dir, S_END_ALNUM); + } else if (ispunct(*dot)) { + dot = skip_thing(dot, 1, dir, S_END_PUNCT); + } + break; + case 'c': // c- change something + case 'd': // d- delete something +#ifdef CONFIG_FEATURE_VI_YANKMARK + case 'y': // y- yank something + case 'Y': // Y- Yank a line +#endif /* CONFIG_FEATURE_VI_YANKMARK */ + yf = YANKDEL; // assume either "c" or "d" +#ifdef CONFIG_FEATURE_VI_YANKMARK + if (c == 'y' || c == 'Y') + yf = YANKONLY; +#endif /* CONFIG_FEATURE_VI_YANKMARK */ + c1 = 'y'; + if (c != 'Y') + c1 = get_one_char(); // get the type of thing to delete + find_range(&p, &q, c1); + if (c1 == 27) { // ESC- user changed mind and wants out + c = c1 = 27; // Escape- do nothing + } else if (strchr("wW", c1)) { + if (c == 'c') { + // don't include trailing WS as part of word + while (isblnk(*q)) { + if (q <= text || q[-1] == '\n') + break; + q--; + } + } + dot = yank_delete(p, q, 0, yf); // delete word + } 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)) { + // multiple line copy text into a register and delete + dot = yank_delete(p, q, 1, yf); // delete lines + if (c == 'c') { + dot = char_insert(dot, '\n'); + // on the last line of file don't move to prev line + if (dot != (end-1)) { + dot_prev(); + } + } else if (c == 'd') { + dot_begin(); + dot_skip_over_ws(); + } + } else { + // could not recognize object + c = c1 = 27; // error- + indicate_error(c); + } + if (c1 != 27) { + // if CHANGING, not deleting, start inserting after the delete + if (c == 'c') { + strcpy((char *) buf, "Change"); + goto dc_i; // start inserting + } + if (c == 'd') { + strcpy((char *) buf, "Delete"); + } +#ifdef CONFIG_FEATURE_VI_YANKMARK + if (c == 'y' || c == 'Y') { + strcpy((char *) buf, "Yank"); + } + p = reg[YDreg]; + q = p + strlen((char *) p); + for (cnt = 0; p <= q; p++) { + if (*p == '\n') + cnt++; + } + psb("%s %d lines (%d chars) using [%c]", + buf, cnt, strlen((char *) reg[YDreg]), what_reg()); +#endif /* CONFIG_FEATURE_VI_YANKMARK */ + end_cmd_q(); // stop adding to q + } + break; + case 'k': // k- goto prev line, same col + case VI_K_UP: // cursor key Up + if (cmdcnt-- > 1) { + do_cmd(c); + } // repeat cnt + dot_prev(); + dot = move_to_col(dot, ccol + offset); // try stay in same col + break; + case 'r': // r- replace the current char with user input + c1 = get_one_char(); // get the replacement char + if (*dot != '\n') { + *dot = c1; + file_modified = TRUE; // has the file been modified + } + 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); + } // repeat cnt + if (isalnum(*dot) || *dot == '_') { // we are on ALNUM + dot = skip_thing(dot, 1, FORWARD, S_END_ALNUM); + } else if (ispunct(*dot)) { // we are on PUNCT + dot = skip_thing(dot, 1, FORWARD, S_END_PUNCT); + } + if (dot < end - 1) + dot++; // move over word + if (isspace(*dot)) { + dot = skip_thing(dot, 2, FORWARD, S_OVER_WS); + } + break; + case 'z': // z- + c1 = get_one_char(); // get the replacement char + cnt = 0; + if (c1 == '.') + cnt = (rows - 2) / 2; // put dot at center + if (c1 == '-') + cnt = rows - 2; // put dot at bottom + screenbegin = begin_line(dot); // start dot at top + dot_scroll(cnt, -1); + break; + case '|': // |- move to column "cmdcnt" + dot = move_to_col(dot, cmdcnt - 1); // try to move to column + break; + case '~': // ~- flip the case of letters a-z -> A-Z + if (cmdcnt-- > 1) { + do_cmd(c); + } // repeat cnt + if (islower(*dot)) { + *dot = toupper(*dot); + file_modified = TRUE; // has the file been modified + } else if (isupper(*dot)) { + *dot = tolower(*dot); + file_modified = TRUE; // has the file been modified + } + dot_right(); + end_cmd_q(); // stop adding to q + break; + //----- The Cursor and Function Keys ----------------------------- + case VI_K_HOME: // Cursor Key Home + dot_begin(); + break; + // The Fn keys could point to do_macro which could translate them + case VI_K_FUN1: // Function Key F1 + case VI_K_FUN2: // Function Key F2 + case VI_K_FUN3: // Function Key F3 + case VI_K_FUN4: // Function Key F4 + case VI_K_FUN5: // Function Key F5 + case VI_K_FUN6: // Function Key F6 + case VI_K_FUN7: // Function Key F7 + case VI_K_FUN8: // Function Key F8 + case VI_K_FUN9: // Function Key F9 + case VI_K_FUN10: // Function Key F10 + case VI_K_FUN11: // Function Key F11 + case VI_K_FUN12: // Function Key F12 + break; + } + + dc1: + // if text[] just became empty, add back an empty line + if (end == text) { + (void) char_insert(text, '\n'); // start empty buf with dummy line + dot = text; + } + // it is OK for dot to exactly equal to end, otherwise check dot validity + if (dot != end) { + dot = bound_dot(dot); // make sure "dot" is valid + } +#ifdef CONFIG_FEATURE_VI_YANKMARK + check_context(c); // update the current context +#endif /* CONFIG_FEATURE_VI_YANKMARK */ + + if (!isdigit(c)) + cmdcnt = 0; // cmd was not a number, reset cmdcnt + cnt = dot - begin_line(dot); + // Try to stay off of the Newline + if (*dot == '\n' && cnt > 0 && cmd_mode == 0) + dot--; +} |