aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRon Yorston <rmy@pobox.com>2021-03-25 14:23:36 +0000
committerDenys Vlasenko <vda.linux@googlemail.com>2021-03-29 12:05:53 +0200
commit25d259264019e4171eddde570271a82b6dd0f79a (patch)
tree0d0e45744826109213c809e92b723a4c315541cc
parent776b56d774fec51e0ac3f9714adbdb2375c6bda7 (diff)
downloadbusybox-w32-25d259264019e4171eddde570271a82b6dd0f79a.tar.gz
busybox-w32-25d259264019e4171eddde570271a82b6dd0f79a.tar.bz2
busybox-w32-25d259264019e4171eddde570271a82b6dd0f79a.zip
vi: make buffer handling more vi-like
Vi places text affected by change/delete/yank operations into a buffer. The contents of such buffers can be restored with the put commands, 'p' or 'P'. These behave differently depending on whether the buffer contains whole lines or partial lines. For whole lines the text is copied into the file on the line before (P) or after (p) the current line. For partial lines the text is copied before or after the current cursor position. Whether an operation results in whole or partial lines depends on the command used. BusyBox vi treats any buffer with a newline as though it contained whole lines. This is incorrect. Deleting multiple words across a line boundary results in a buffer with a newline but not having whole lines. Rework how buffers are handled to behave more like vi. function old new delta static.text_yank 79 99 +20 colon 3092 3097 +5 edit_file 885 887 +2 yank_delete 127 112 -15 .rodata 105139 105101 -38 find_range 514 467 -47 do_cmd 5088 4842 -246 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 3/4 up/down: 27/-346) Total: -319 bytes Signed-off-by: Ron Yorston <rmy@pobox.com> Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
-rw-r--r--editors/vi.c162
1 files changed, 73 insertions, 89 deletions
diff --git a/editors/vi.c b/editors/vi.c
index 7a7247c10..ccf2870ab 100644
--- a/editors/vi.c
+++ b/editors/vi.c
@@ -254,6 +254,9 @@ enum {
254 BACK = -1, // code depends on "-1" for array index 254 BACK = -1, // code depends on "-1" for array index
255 LIMITED = 0, // char_search() only current line 255 LIMITED = 0, // char_search() only current line
256 FULL = 1, // char_search() to the end/beginning of entire text 256 FULL = 1, // char_search() to the end/beginning of entire text
257 PARTIAL = 0, // buffer contains partial line
258 WHOLE = 1, // buffer contains whole lines
259 MULTI = 2, // buffer may include newlines
257 260
258 S_BEFORE_WS = 1, // used in skip_thing() for moving "dot" 261 S_BEFORE_WS = 1, // used in skip_thing() for moving "dot"
259 S_TO_WS = 2, // used in skip_thing() for moving "dot" 262 S_TO_WS = 2, // used in skip_thing() for moving "dot"
@@ -343,6 +346,7 @@ struct globals {
343 smalluint YDreg;//,Ureg;// default delete register and orig line for "U" 346 smalluint YDreg;//,Ureg;// default delete register and orig line for "U"
344#define Ureg 27 347#define Ureg 27
345 char *reg[28]; // named register a-z, "D", and "U" 0-25,26,27 348 char *reg[28]; // named register a-z, "D", and "U" 0-25,26,27
349 char regtype[28]; // buffer type: WHOLE, MULTI or PARTIAL
346 char *mark[28]; // user marks points somewhere in text[]- a-z and previous context '' 350 char *mark[28]; // user marks points somewhere in text[]- a-z and previous context ''
347 char *context_start, *context_end; 351 char *context_start, *context_end;
348#endif 352#endif
@@ -452,6 +456,7 @@ struct globals {
452 456
453#define YDreg (G.YDreg ) 457#define YDreg (G.YDreg )
454//#define Ureg (G.Ureg ) 458//#define Ureg (G.Ureg )
459#define regtype (G.regtype )
455#define mark (G.mark ) 460#define mark (G.mark )
456#define context_start (G.context_start ) 461#define context_start (G.context_start )
457#define context_end (G.context_end ) 462#define context_end (G.context_end )
@@ -1314,7 +1319,8 @@ static void not_implemented(const char *s)
1314 1319
1315//----- Block insert/delete, undo ops -------------------------- 1320//----- Block insert/delete, undo ops --------------------------
1316#if ENABLE_FEATURE_VI_YANKMARK 1321#if ENABLE_FEATURE_VI_YANKMARK
1317static char *text_yank(char *p, char *q, int dest) // copy text into a register 1322// copy text into a register
1323static char *text_yank(char *p, char *q, int dest, int buftype)
1318{ 1324{
1319 int cnt = q - p; 1325 int cnt = q - p;
1320 if (cnt < 0) { // they are backwards- reverse them 1326 if (cnt < 0) { // they are backwards- reverse them
@@ -1323,6 +1329,7 @@ static char *text_yank(char *p, char *q, int dest) // copy text into a register
1323 } 1329 }
1324 free(reg[dest]); // if already a yank register, free it 1330 free(reg[dest]); // if already a yank register, free it
1325 reg[dest] = xstrndup(p, cnt + 1); 1331 reg[dest] = xstrndup(p, cnt + 1);
1332 regtype[dest] = buftype;
1326 return p; 1333 return p;
1327} 1334}
1328 1335
@@ -1819,12 +1826,11 @@ static void end_cmd_q(void)
1819#endif /* FEATURE_VI_DOT_CMD */ 1826#endif /* FEATURE_VI_DOT_CMD */
1820 1827
1821// copy text into register, then delete text. 1828// copy text into register, then delete text.
1822// if dist <= 0, do not include, or go past, a NewLine
1823// 1829//
1824#if !ENABLE_FEATURE_VI_UNDO 1830#if !ENABLE_FEATURE_VI_UNDO
1825#define yank_delete(a,b,c,d,e) yank_delete(a,b,c,d) 1831#define yank_delete(a,b,c,d,e) yank_delete(a,b,c,d)
1826#endif 1832#endif
1827static char *yank_delete(char *start, char *stop, int dist, int yf, int undo) 1833static char *yank_delete(char *start, char *stop, int buftype, int yf, int undo)
1828{ 1834{
1829 char *p; 1835 char *p;
1830 1836
@@ -1835,22 +1841,11 @@ static char *yank_delete(char *start, char *stop, int dist, int yf, int undo)
1835 start = stop; 1841 start = stop;
1836 stop = p; 1842 stop = p;
1837 } 1843 }
1838 if (dist <= 0) { 1844 if (buftype == PARTIAL && *start == '\n')
1839 // we cannot cross NL boundaries 1845 return start;
1840 p = start;
1841 if (*p == '\n')
1842 return p;
1843 // dont go past a NewLine
1844 for (; p + 1 <= stop; p++) {
1845 if (p[1] == '\n') {
1846 stop = p; // "stop" just before NewLine
1847 break;
1848 }
1849 }
1850 }
1851 p = start; 1846 p = start;
1852#if ENABLE_FEATURE_VI_YANKMARK 1847#if ENABLE_FEATURE_VI_YANKMARK
1853 text_yank(start, stop, YDreg); 1848 text_yank(start, stop, YDreg, buftype);
1854#endif 1849#endif
1855 if (yf == YANKDEL) { 1850 if (yf == YANKDEL) {
1856 p = text_hole_delete(start, stop, undo); 1851 p = text_hole_delete(start, stop, undo);
@@ -2521,7 +2516,7 @@ static void colon(char *buf)
2521 q = begin_line(dot); // assume .,. for the range 2516 q = begin_line(dot); // assume .,. for the range
2522 r = end_line(dot); 2517 r = end_line(dot);
2523 } 2518 }
2524 dot = yank_delete(q, r, 1, YANKDEL, ALLOW_UNDO); // save, then delete lines 2519 dot = yank_delete(q, r, WHOLE, YANKDEL, ALLOW_UNDO); // save, then delete lines
2525 dot_skip_over_ws(); 2520 dot_skip_over_ws();
2526 } else if (strncmp(cmd, "edit", i) == 0) { // Edit a file 2521 } else if (strncmp(cmd, "edit", i) == 0) { // Edit a file
2527 int size; 2522 int size;
@@ -2874,7 +2869,7 @@ static void colon(char *buf)
2874 q = begin_line(dot); // assume .,. for the range 2869 q = begin_line(dot); // assume .,. for the range
2875 r = end_line(dot); 2870 r = end_line(dot);
2876 } 2871 }
2877 text_yank(q, r, YDreg); 2872 text_yank(q, r, YDreg, WHOLE);
2878 li = count_lines(q, r); 2873 li = count_lines(q, r);
2879 status_line("Yank %d lines (%d chars) into [%c]", 2874 status_line("Yank %d lines (%d chars) into [%c]",
2880 li, strlen(reg[YDreg]), what_reg()); 2875 li, strlen(reg[YDreg]), what_reg());
@@ -3000,74 +2995,65 @@ static void do_cmd(int c);
3000static int find_range(char **start, char **stop, char c) 2995static int find_range(char **start, char **stop, char c)
3001{ 2996{
3002 char *save_dot, *p, *q, *t; 2997 char *save_dot, *p, *q, *t;
3003 int cnt, multiline = 0, forward; 2998 int buftype = -1;
3004 2999
3005 save_dot = dot; 3000 save_dot = dot;
3006 p = q = dot; 3001 p = q = dot;
3007 3002
3008 // will a 'G' command move forwards or backwards?
3009 forward = cmdcnt == 0 || cmdcnt > count_lines(text, dot);
3010
3011 if (strchr("cdy><", c)) { 3003 if (strchr("cdy><", c)) {
3012 // these cmds operate on whole lines 3004 // these cmds operate on whole lines
3013 p = q = begin_line(p); 3005 buftype = WHOLE;
3014 for (cnt = 1; cnt < cmdcnt; cnt++) { 3006 if (--cmdcnt > 0)
3015 q = next_line(q); 3007 do_cmd('j');
3016 }
3017 q = end_line(q);
3018 } else if (strchr("^%$0bBeEfth\b\177", c)) { 3008 } else if (strchr("^%$0bBeEfth\b\177", c)) {
3019 // These cmds operate on char positions 3009 // These cmds operate on char positions
3010 buftype = PARTIAL;
3020 do_cmd(c); // execute movement cmd 3011 do_cmd(c); // execute movement cmd
3021 q = dot; 3012 if (p == dot) // no movement is an error
3013 buftype = -1;
3022 } else if (strchr("wW", c)) { 3014 } else if (strchr("wW", c)) {
3015 buftype = MULTI;
3023 do_cmd(c); // execute movement cmd 3016 do_cmd(c); // execute movement cmd
3024 // step back one char, but not if we're at end of file 3017 // step back one char, but not if we're at end of file
3025 if (dot > p && !((dot == end - 2 && end[-1] == '\n') || dot == end - 1)) 3018 if (dot > p && !((dot == end - 2 && end[-1] == '\n') || dot == end - 1))
3026 dot--; 3019 dot--;
3027 q = dot; 3020 } else if (strchr("GHL+-jk{}\r\n", c)) {
3028 } else if (strchr("H-k{", c) || (c == 'G' && !forward)) { 3021 // these operate on whole lines
3029 // these operate on multi-lines backwards 3022 buftype = WHOLE;
3030 q = end_line(dot); // find NL
3031 do_cmd(c); // execute movement cmd
3032 dot_begin();
3033 p = dot;
3034 } else if (strchr("L+j}\r\n", c) || (c == 'G' && forward)) {
3035 // these operate on multi-lines forwards
3036 p = begin_line(dot);
3037 do_cmd(c); // execute movement cmd 3023 do_cmd(c); // execute movement cmd
3038 dot_end(); // find NL 3024 } else if (c == ' ' || c == 'l') {
3039 q = dot;
3040 } else /* if (c == ' ' || c == 'l') */ {
3041 // forward motion by character 3025 // forward motion by character
3042 int tmpcnt = (cmdcnt ?: 1); 3026 int tmpcnt = (cmdcnt ?: 1);
3027 buftype = PARTIAL;
3043 do_cmd(c); // execute movement cmd 3028 do_cmd(c); // execute movement cmd
3044 // exclude last char unless range isn't what we expected 3029 // exclude last char unless range isn't what we expected
3045 // this indicates we've hit EOL 3030 // this indicates we've hit EOL
3046 if (tmpcnt == dot - p) 3031 if (tmpcnt == dot - p)
3047 dot--; 3032 dot--;
3048 q = dot;
3049 } 3033 }
3034
3035 if (buftype == -1)
3036 return buftype;
3037
3038 q = dot;
3050 if (q < p) { 3039 if (q < p) {
3051 t = q; 3040 t = q;
3052 q = p; 3041 q = p;
3053 p = t; 3042 p = t;
3054 } 3043 }
3055 3044
3045 if (buftype == WHOLE) {
3046 p = begin_line(p);
3047 q = end_line(q);
3048 }
3049
3056 // backward char movements don't include start position 3050 // backward char movements don't include start position
3057 if (q > p && strchr("^0bBh\b\177", c)) q--; 3051 if (q > p && strchr("^0bBh\b\177", c)) q--;
3058 3052
3059 multiline = 0;
3060 for (t = p; t <= q; t++) {
3061 if (*t == '\n') {
3062 multiline = 1;
3063 break;
3064 }
3065 }
3066
3067 *start = p; 3053 *start = p;
3068 *stop = q; 3054 *stop = q;
3069 dot = save_dot; 3055 dot = save_dot;
3070 return multiline; 3056 return buftype;
3071} 3057}
3072 3058
3073//--------------------------------------------------------------------- 3059//---------------------------------------------------------------------
@@ -3132,7 +3118,7 @@ static void do_cmd(int c)
3132 } else { 3118 } else {
3133 if (1 <= c || Isprint(c)) { 3119 if (1 <= c || Isprint(c)) {
3134 if (c != 27) 3120 if (c != 27)
3135 dot = yank_delete(dot, dot, 0, YANKDEL, ALLOW_UNDO); // delete char 3121 dot = yank_delete(dot, dot, PARTIAL, YANKDEL, ALLOW_UNDO); // delete char
3136 dot = char_insert(dot, c, ALLOW_UNDO_CHAIN); // insert new char 3122 dot = char_insert(dot, c, ALLOW_UNDO_CHAIN); // insert new char
3137 } 3123 }
3138 goto dc1; 3124 goto dc1;
@@ -3312,7 +3298,7 @@ static void do_cmd(int c)
3312 break; 3298 break;
3313 } 3299 }
3314 // are we putting whole lines or strings 3300 // are we putting whole lines or strings
3315 if (strchr(p, '\n') != NULL) { 3301 if (regtype[YDreg] == WHOLE) {
3316 if (c == 'P') { 3302 if (c == 'P') {
3317 dot_begin(); // putting lines- Put above 3303 dot_begin(); // putting lines- Put above
3318 } 3304 }
@@ -3523,7 +3509,7 @@ static void do_cmd(int c)
3523 cnt = count_lines(text, dot); // remember what line we are on 3509 cnt = count_lines(text, dot); // remember what line we are on
3524 c1 = get_one_char(); // get the type of thing to delete 3510 c1 = get_one_char(); // get the type of thing to delete
3525 find_range(&p, &q, c1); 3511 find_range(&p, &q, c1);
3526 yank_delete(p, q, 1, YANKONLY, NO_UNDO); // save copy before change 3512 yank_delete(p, q, WHOLE, YANKONLY, NO_UNDO); // save copy before change
3527 p = begin_line(p); 3513 p = begin_line(p);
3528 q = end_line(q); 3514 q = end_line(q);
3529 i = count_lines(p, q); // # of lines we are shifting 3515 i = count_lines(p, q); // # of lines we are shifting
@@ -3576,7 +3562,7 @@ static void do_cmd(int c)
3576 save_dot = dot; 3562 save_dot = dot;
3577 dot = dollar_line(dot); // move to before NL 3563 dot = dollar_line(dot); // move to before NL
3578 // copy text into a register and delete 3564 // copy text into a register and delete
3579 dot = yank_delete(save_dot, dot, 0, YANKDEL, ALLOW_UNDO); // delete to e-o-l 3565 dot = yank_delete(save_dot, dot, PARTIAL, YANKDEL, ALLOW_UNDO); // delete to e-o-l
3580 if (c == 'C') 3566 if (c == 'C')
3581 goto dc_i; // start inserting 3567 goto dc_i; // start inserting
3582#if ENABLE_FEATURE_VI_DOT_CMD 3568#if ENABLE_FEATURE_VI_DOT_CMD
@@ -3682,7 +3668,7 @@ static void do_cmd(int c)
3682 break; 3668 break;
3683 case KEYCODE_DELETE: 3669 case KEYCODE_DELETE:
3684 if (dot < end - 1) 3670 if (dot < end - 1)
3685 dot = yank_delete(dot, dot, 1, YANKDEL, ALLOW_UNDO); 3671 dot = yank_delete(dot, dot, PARTIAL, YANKDEL, ALLOW_UNDO);
3686 break; 3672 break;
3687 case 'X': // X- delete char before dot 3673 case 'X': // X- delete char before dot
3688 case 'x': // x- delete the current char 3674 case 'x': // x- delete the current char
@@ -3694,7 +3680,7 @@ static void do_cmd(int c)
3694 if (dot[dir] != '\n') { 3680 if (dot[dir] != '\n') {
3695 if (c == 'X') 3681 if (c == 'X')
3696 dot--; // delete prev char 3682 dot--; // delete prev char
3697 dot = yank_delete(dot, dot, 0, YANKDEL, ALLOW_UNDO); // delete char 3683 dot = yank_delete(dot, dot, PARTIAL, YANKDEL, ALLOW_UNDO); // delete char
3698 } 3684 }
3699 } while (--cmdcnt > 0); 3685 } while (--cmdcnt > 0);
3700 end_cmd_q(); // stop adding to q 3686 end_cmd_q(); // stop adding to q
@@ -3754,21 +3740,29 @@ static void do_cmd(int c)
3754 case 'Y': // Y- Yank a line 3740 case 'Y': // Y- Yank a line
3755#endif 3741#endif
3756 { 3742 {
3757 int yf, ml, whole = 0; 3743#if ENABLE_FEATURE_VI_YANKMARK
3744 char *savereg = reg[YDreg];
3745#endif
3746 int yf, buftype = 0;
3758 yf = YANKDEL; // assume either "c" or "d" 3747 yf = YANKDEL; // assume either "c" or "d"
3759#if ENABLE_FEATURE_VI_YANKMARK 3748#if ENABLE_FEATURE_VI_YANKMARK
3760 if (c == 'y' || c == 'Y') 3749 if (c == 'y' || c == 'Y')
3761 yf = YANKONLY; 3750 yf = YANKONLY;
3762#endif 3751#endif
3763 c1 = 'y'; 3752 c1 = 'y';
3764 if (c != 'Y') 3753 if (c != 'Y') {
3765 c1 = get_one_char(); // get the type of thing to delete 3754 c1 = get_one_char(); // get the type of thing to delete
3755 if (c1 == 27) // ESC- user changed mind and wants out
3756 goto dc6;
3757 }
3766 // determine range, and whether it spans lines 3758 // determine range, and whether it spans lines
3767 ml = find_range(&p, &q, c1); 3759 buftype = find_range(&p, &q, c1);
3768 place_cursor(0, 0); 3760 place_cursor(0, 0);
3769 if (c1 == 27) { // ESC- user changed mind and wants out 3761 if (buftype == -1) { // invalid range
3770 c = c1 = 27; // Escape- do nothing 3762 indicate_error();
3771 } else if (c1 == 'w' || c1 == 'W') { 3763 goto dc6;
3764 }
3765 if (c1 == 'w' || c1 == 'W') {
3772 char *q0 = q; 3766 char *q0 = q;
3773 // don't include trailing WS as part of word 3767 // don't include trailing WS as part of word
3774 while (q > p && isspace(*q)) { 3768 while (q > p && isspace(*q)) {
@@ -3778,25 +3772,13 @@ static void do_cmd(int c)
3778 // for non-change operations WS after NL is not part of word 3772 // for non-change operations WS after NL is not part of word
3779 if (c != 'c' && q != p && *q != '\n') 3773 if (c != 'c' && q != p && *q != '\n')
3780 q = q0; 3774 q = q0;
3781 dot = yank_delete(p, q, ml, yf, ALLOW_UNDO); // delete word
3782 } else if (strchr("^0bBeEft%$ lh\b\177", c1)) {
3783 // partial line copy text into a register and delete
3784 dot = yank_delete(p, q, ml, yf, ALLOW_UNDO); // delete word
3785 } else if (strchr("cdykjGHL+-{}\r\n", c1)) {
3786 // whole line copy text into a register and delete
3787 dot = yank_delete(p, q, ml, yf, ALLOW_UNDO); // delete lines
3788 whole = 1;
3789 } else {
3790 // could not recognize object
3791 c = c1 = 27; // error-
3792 ml = 0;
3793 indicate_error();
3794 } 3775 }
3795 if (ml && whole) { 3776 dot = yank_delete(p, q, buftype, yf, ALLOW_UNDO); // delete word
3777 if (buftype == WHOLE) {
3796 if (c == 'c') { 3778 if (c == 'c') {
3797 dot = char_insert(dot, '\n', ALLOW_UNDO_CHAIN); 3779 dot = char_insert(dot, '\n', ALLOW_UNDO_CHAIN);
3798 // on the last line of file don't move to prev line 3780 // on the last line of file don't move to prev line
3799 if (whole && dot != (end-1)) { 3781 if (dot != (end-1)) {
3800 dot_prev(); 3782 dot_prev();
3801 } 3783 }
3802 } else if (c == 'd') { 3784 } else if (c == 'd') {
@@ -3804,16 +3786,17 @@ static void do_cmd(int c)
3804 dot_skip_over_ws(); 3786 dot_skip_over_ws();
3805 } 3787 }
3806 } 3788 }
3807 if (c1 != 27) { 3789 // if CHANGING, not deleting, start inserting after the delete
3808 // if CHANGING, not deleting, start inserting after the delete 3790 if (c == 'c') {
3809 if (c == 'c') { 3791 //strcpy(buf, "Change");
3810 strcpy(buf, "Change"); 3792 goto dc_i; // start inserting
3811 goto dc_i; // start inserting 3793 }
3812 } 3794#if ENABLE_FEATURE_VI_YANKMARK
3795 // only update status if a yank has actually happened
3796 if (reg[YDreg] != savereg) {
3813 if (c == 'd') { 3797 if (c == 'd') {
3814 strcpy(buf, "Delete"); 3798 strcpy(buf, "Delete");
3815 } 3799 }
3816#if ENABLE_FEATURE_VI_YANKMARK
3817 if (c == 'y' || c == 'Y') { 3800 if (c == 'y' || c == 'Y') {
3818 strcpy(buf, "Yank"); 3801 strcpy(buf, "Yank");
3819 } 3802 }
@@ -3825,9 +3808,10 @@ static void do_cmd(int c)
3825 } 3808 }
3826 status_line("%s %u lines (%u chars) using [%c]", 3809 status_line("%s %u lines (%u chars) using [%c]",
3827 buf, cnt, (unsigned)strlen(reg[YDreg]), what_reg()); 3810 buf, cnt, (unsigned)strlen(reg[YDreg]), what_reg());
3828#endif
3829 end_cmd_q(); // stop adding to q
3830 } 3811 }
3812#endif
3813 dc6:
3814 end_cmd_q(); // stop adding to q
3831 break; 3815 break;
3832 } 3816 }
3833 case 'k': // k- goto prev line, same col 3817 case 'k': // k- goto prev line, same col
@@ -4271,7 +4255,7 @@ static void edit_file(char *fn)
4271 // save a copy of the current line- for the 'U" command 4255 // save a copy of the current line- for the 'U" command
4272 if (begin_line(dot) != cur_line) { 4256 if (begin_line(dot) != cur_line) {
4273 cur_line = begin_line(dot); 4257 cur_line = begin_line(dot);
4274 text_yank(begin_line(dot), end_line(dot), Ureg); 4258 text_yank(begin_line(dot), end_line(dot), Ureg, PARTIAL);
4275 } 4259 }
4276#endif 4260#endif
4277#if ENABLE_FEATURE_VI_DOT_CMD 4261#if ENABLE_FEATURE_VI_DOT_CMD