aboutsummaryrefslogtreecommitdiff
path: root/editors
diff options
context:
space:
mode:
Diffstat (limited to 'editors')
-rw-r--r--editors/awk.c38
-rw-r--r--editors/sed.c97
-rw-r--r--editors/vi.c201
3 files changed, 166 insertions, 170 deletions
diff --git a/editors/awk.c b/editors/awk.c
index 71abca215..42f6ef866 100644
--- a/editors/awk.c
+++ b/editors/awk.c
@@ -696,6 +696,10 @@ static char nextchar(char **s)
696 pps = *s; 696 pps = *s;
697 if (c == '\\') 697 if (c == '\\')
698 c = bb_process_escape_sequence((const char**)s); 698 c = bb_process_escape_sequence((const char**)s);
699 /* Example awk statement:
700 * s = "abc\"def"
701 * we must treat \" as "
702 */
699 if (c == '\\' && *s == pps) { /* unrecognized \z? */ 703 if (c == '\\' && *s == pps) { /* unrecognized \z? */
700 c = *(*s); /* yes, fetch z */ 704 c = *(*s); /* yes, fetch z */
701 if (c) 705 if (c)
@@ -704,6 +708,15 @@ static char nextchar(char **s)
704 return c; 708 return c;
705} 709}
706 710
711/* TODO: merge with strcpy_and_process_escape_sequences()?
712 */
713static void unescape_string_in_place(char *s1)
714{
715 char *s = s1;
716 while ((*s1 = nextchar(&s)) != '\0')
717 s1++;
718}
719
707static ALWAYS_INLINE int isalnum_(int c) 720static ALWAYS_INLINE int isalnum_(int c)
708{ 721{
709 return (isalnum(c) || c == '_'); 722 return (isalnum(c) || c == '_');
@@ -1799,6 +1812,18 @@ static void handle_special(var *v)
1799 is_f0_split = FALSE; 1812 is_f0_split = FALSE;
1800 1813
1801 } else if (v == intvar[FS]) { 1814 } else if (v == intvar[FS]) {
1815 /*
1816 * The POSIX-2008 standard says that changing FS should have no effect on the
1817 * current input line, but only on the next one. The language is:
1818 *
1819 * > Before the first reference to a field in the record is evaluated, the record
1820 * > shall be split into fields, according to the rules in Regular Expressions,
1821 * > using the value of FS that was current at the time the record was read.
1822 *
1823 * So, split up current line before assignment to FS:
1824 */
1825 split_f0();
1826
1802 mk_splitter(getvar_s(v), &fsplitter); 1827 mk_splitter(getvar_s(v), &fsplitter);
1803 1828
1804 } else if (v == intvar[RS]) { 1829 } else if (v == intvar[RS]) {
@@ -2992,7 +3017,7 @@ static int awk_exit(int r)
2992 * otherwise return 0 */ 3017 * otherwise return 0 */
2993static int is_assignment(const char *expr) 3018static int is_assignment(const char *expr)
2994{ 3019{
2995 char *exprc, *val, *s, *s1; 3020 char *exprc, *val;
2996 3021
2997 if (!isalnum_(*expr) || (val = strchr(expr, '=')) == NULL) { 3022 if (!isalnum_(*expr) || (val = strchr(expr, '=')) == NULL) {
2998 return FALSE; 3023 return FALSE;
@@ -3002,10 +3027,7 @@ static int is_assignment(const char *expr)
3002 val = exprc + (val - expr); 3027 val = exprc + (val - expr);
3003 *val++ = '\0'; 3028 *val++ = '\0';
3004 3029
3005 s = s1 = val; 3030 unescape_string_in_place(val);
3006 while ((*s1 = nextchar(&s)) != '\0')
3007 s1++;
3008
3009 setvar_u(newvar(exprc), val); 3031 setvar_u(newvar(exprc), val);
3010 free(exprc); 3032 free(exprc);
3011 return TRUE; 3033 return TRUE;
@@ -3118,8 +3140,10 @@ int awk_main(int argc, char **argv)
3118 opt = getopt32(argv, "F:v:f:W:", &opt_F, &list_v, &list_f, NULL); 3140 opt = getopt32(argv, "F:v:f:W:", &opt_F, &list_v, &list_f, NULL);
3119 argv += optind; 3141 argv += optind;
3120 argc -= optind; 3142 argc -= optind;
3121 if (opt & 0x1) 3143 if (opt & 0x1) { /* -F */
3122 setvar_s(intvar[FS], opt_F); // -F 3144 unescape_string_in_place(opt_F);
3145 setvar_s(intvar[FS], opt_F);
3146 }
3123 while (list_v) { /* -v */ 3147 while (list_v) { /* -v */
3124 if (!is_assignment(llist_pop(&list_v))) 3148 if (!is_assignment(llist_pop(&list_v)))
3125 bb_show_usage(); 3149 bb_show_usage();
diff --git a/editors/sed.c b/editors/sed.c
index a2df93165..070af611a 100644
--- a/editors/sed.c
+++ b/editors/sed.c
@@ -53,7 +53,9 @@
53 * Todo: 53 * Todo:
54 * - Create a wrapper around regex to make libc's regex conform with sed 54 * - Create a wrapper around regex to make libc's regex conform with sed
55 * 55 *
56 * Reference http://www.opengroup.org/onlinepubs/007904975/utilities/sed.html 56 * Reference
57 * http://www.opengroup.org/onlinepubs/007904975/utilities/sed.html
58 * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/sed.html
57 */ 59 */
58 60
59//usage:#define sed_trivial_usage 61//usage:#define sed_trivial_usage
@@ -63,7 +65,7 @@
63//usage: " -e CMD Add CMD to sed commands to be executed" 65//usage: " -e CMD Add CMD to sed commands to be executed"
64//usage: "\n -f FILE Add FILE contents to sed commands to be executed" 66//usage: "\n -f FILE Add FILE contents to sed commands to be executed"
65//usage: "\n -i[SFX] Edit files in-place (otherwise sends to stdout)" 67//usage: "\n -i[SFX] Edit files in-place (otherwise sends to stdout)"
66//usage: "\n Optionally backs files up, appending SFX" 68//usage: "\n Optionally back files up, appending SFX"
67//usage: "\n -n Suppress automatic printing of pattern space" 69//usage: "\n -n Suppress automatic printing of pattern space"
68//usage: "\n -r Use extended regex syntax" 70//usage: "\n -r Use extended regex syntax"
69//usage: "\n" 71//usage: "\n"
@@ -492,8 +494,10 @@ static const char *parse_cmd_args(sed_cmd_t *sed_cmd, const char *cmdstr)
492 } 494 }
493 /* handle edit cmds: (a)ppend, (i)nsert, and (c)hange */ 495 /* handle edit cmds: (a)ppend, (i)nsert, and (c)hange */
494 else if (idx <= IDX_c) { /* a,i,c */ 496 else if (idx <= IDX_c) { /* a,i,c */
495 if ((sed_cmd->end_line || sed_cmd->end_match) && sed_cmd->cmd != 'c') 497 if (idx < IDX_c) { /* a,i */
496 bb_error_msg_and_die("only a beginning address can be specified for edit commands"); 498 if (sed_cmd->end_line || sed_cmd->end_match)
499 bb_error_msg_and_die("command '%c' uses only one address", sed_cmd->cmd);
500 }
497 for (;;) { 501 for (;;) {
498 if (*cmdstr == '\n' || *cmdstr == '\\') { 502 if (*cmdstr == '\n' || *cmdstr == '\\') {
499 cmdstr++; 503 cmdstr++;
@@ -510,8 +514,10 @@ static const char *parse_cmd_args(sed_cmd_t *sed_cmd, const char *cmdstr)
510 } 514 }
511 /* handle file cmds: (r)ead */ 515 /* handle file cmds: (r)ead */
512 else if (idx <= IDX_w) { /* r,w */ 516 else if (idx <= IDX_w) { /* r,w */
513 if (sed_cmd->end_line || sed_cmd->end_match) 517 if (idx < IDX_w) { /* r */
514 bb_error_msg_and_die("command only uses one address"); 518 if (sed_cmd->end_line || sed_cmd->end_match)
519 bb_error_msg_and_die("command '%c' uses only one address", sed_cmd->cmd);
520 }
515 cmdstr += parse_file_cmd(/*sed_cmd,*/ cmdstr, &sed_cmd->string); 521 cmdstr += parse_file_cmd(/*sed_cmd,*/ cmdstr, &sed_cmd->string);
516 if (sed_cmd->cmd == 'w') { 522 if (sed_cmd->cmd == 'w') {
517 sed_cmd->sw_file = xfopen_for_write(sed_cmd->string); 523 sed_cmd->sw_file = xfopen_for_write(sed_cmd->string);
@@ -673,7 +679,7 @@ static void do_subst_w_backrefs(char *line, char *replace)
673 679
674 /* go through the replacement string */ 680 /* go through the replacement string */
675 for (i = 0; replace[i]; i++) { 681 for (i = 0; replace[i]; i++) {
676 /* if we find a backreference (\1, \2, etc.) print the backref'ed * text */ 682 /* if we find a backreference (\1, \2, etc.) print the backref'ed text */
677 if (replace[i] == '\\') { 683 if (replace[i] == '\\') {
678 unsigned backref = replace[++i] - '0'; 684 unsigned backref = replace[++i] - '0';
679 if (backref <= 9) { 685 if (backref <= 9) {
@@ -707,8 +713,10 @@ static void do_subst_w_backrefs(char *line, char *replace)
707static int do_subst_command(sed_cmd_t *sed_cmd, char **line_p) 713static int do_subst_command(sed_cmd_t *sed_cmd, char **line_p)
708{ 714{
709 char *line = *line_p; 715 char *line = *line_p;
710 int altered = 0;
711 unsigned match_count = 0; 716 unsigned match_count = 0;
717 bool altered = 0;
718 bool prev_match_empty = 1;
719 bool tried_at_eol = 0;
712 regex_t *current_regex; 720 regex_t *current_regex;
713 721
714 current_regex = sed_cmd->sub_match; 722 current_regex = sed_cmd->sub_match;
@@ -735,50 +743,75 @@ static int do_subst_command(sed_cmd_t *sed_cmd, char **line_p)
735 743
736 /* Now loop through, substituting for matches */ 744 /* Now loop through, substituting for matches */
737 do { 745 do {
746 int start = G.regmatch[0].rm_so;
747 int end = G.regmatch[0].rm_eo;
738 int i; 748 int i;
739 749
740 /* Work around bug in glibc regexec, demonstrated by:
741 * echo " a.b" | busybox sed 's [^ .]* x g'
742 * The match_count check is so not to break
743 * echo "hi" | busybox sed 's/^/!/g'
744 */
745 if (!G.regmatch[0].rm_so && !G.regmatch[0].rm_eo && match_count) {
746 pipe_putc(*line++);
747 goto next;
748 }
749
750 match_count++; 750 match_count++;
751 751
752 /* If we aren't interested in this match, output old line to 752 /* If we aren't interested in this match, output old line to
753 end of match and continue */ 753 * end of match and continue */
754 if (sed_cmd->which_match 754 if (sed_cmd->which_match
755 && (sed_cmd->which_match != match_count) 755 && (sed_cmd->which_match != match_count)
756 ) { 756 ) {
757 for (i = 0; i < G.regmatch[0].rm_eo; i++) 757 for (i = 0; i < end; i++)
758 pipe_putc(*line++);
759 /* Null match? Print one more char */
760 if (start == end && *line)
758 pipe_putc(*line++); 761 pipe_putc(*line++);
759 goto next; 762 goto next;
760 } 763 }
761 764
762 /* print everything before the match */ 765 /* Print everything before the match */
763 for (i = 0; i < G.regmatch[0].rm_so; i++) 766 for (i = 0; i < start; i++)
764 pipe_putc(line[i]); 767 pipe_putc(line[i]);
765 768
766 /* then print the substitution string */ 769 /* Then print the substitution string,
767 do_subst_w_backrefs(line, sed_cmd->string); 770 * unless we just matched empty string after non-empty one.
771 * Example: string "cccd", pattern "c*", repl "R":
772 * result is "RdR", not "RRdR": first match "ccc",
773 * second is "" before "d", third is "" after "d".
774 * Second match is NOT replaced!
775 */
776 if (prev_match_empty || start != 0 || start != end) {
777 //dbg("%d %d %d", prev_match_empty, start, end);
778 dbg("inserting replacement at %d in '%s'", start, line);
779 do_subst_w_backrefs(line, sed_cmd->string);
780 /* Flag that something has changed */
781 altered = 1;
782 } else {
783 dbg("NOT inserting replacement at %d in '%s'", start, line);
784 }
785
786 /* If matched string is empty (f.e. "c*" pattern),
787 * copy verbatim one char after it before attempting more matches
788 */
789 prev_match_empty = (start == end);
790 if (prev_match_empty) {
791 if (!line[end]) {
792 tried_at_eol = 1;
793 } else {
794 pipe_putc(line[end]);
795 end++;
796 }
797 }
768 798
769 /* advance past the match */ 799 /* Advance past the match */
770 line += G.regmatch[0].rm_eo; 800 dbg("line += %d", end);
771 /* flag that something has changed */ 801 line += end;
772 altered++;
773 802
774 /* if we're not doing this globally, get out now */ 803 /* if we're not doing this globally, get out now */
775 if (sed_cmd->which_match != 0) 804 if (sed_cmd->which_match != 0)
776 break; 805 break;
777 next: 806 next:
778 if (*line == '\0') 807 /* Exit if we are at EOL and already tried matching at it */
779 break; 808 if (*line == '\0') {
809 if (tried_at_eol)
810 break;
811 tried_at_eol = 1;
812 }
780 813
781//maybe (G.regmatch[0].rm_eo ? REG_NOTBOL : 0) instead of unconditional REG_NOTBOL? 814//maybe (end ? REG_NOTBOL : 0) instead of unconditional REG_NOTBOL?
782 } while (regexec(current_regex, line, 10, G.regmatch, REG_NOTBOL) != REG_NOMATCH); 815 } while (regexec(current_regex, line, 10, G.regmatch, REG_NOTBOL) != REG_NOMATCH);
783 816
784 /* Copy rest of string into output pipeline */ 817 /* Copy rest of string into output pipeline */
@@ -1127,7 +1160,7 @@ static void process_files(void)
1127 case 's': 1160 case 's':
1128 if (!do_subst_command(sed_cmd, &pattern_space)) 1161 if (!do_subst_command(sed_cmd, &pattern_space))
1129 break; 1162 break;
1130 dbg("do_subst_command succeeeded:'%s'", pattern_space); 1163 dbg("do_subst_command succeeded:'%s'", pattern_space);
1131 substituted |= 1; 1164 substituted |= 1;
1132 1165
1133 /* handle p option */ 1166 /* handle p option */
diff --git a/editors/vi.c b/editors/vi.c
index 6fae221ac..821583ec1 100644
--- a/editors/vi.c
+++ b/editors/vi.c
@@ -136,14 +136,6 @@
136//config: cursor position using "ESC [ 6 n" escape sequence, then read stdin. 136//config: cursor position using "ESC [ 6 n" escape sequence, then read stdin.
137//config: 137//config:
138//config: This is not clean but helps a lot on serial lines and such. 138//config: This is not clean but helps a lot on serial lines and such.
139//config:
140//config:config FEATURE_VI_OPTIMIZE_CURSOR
141//config: bool "Optimize cursor movement"
142//config: default y
143//config: depends on VI
144//config: help
145//config: This will make the cursor movement faster, but requires more memory
146//config: and it makes the applet a tiny bit larger.
147 139
148//applet:IF_VI(APPLET(vi, BB_DIR_BIN, BB_SUID_DROP)) 140//applet:IF_VI(APPLET(vi, BB_DIR_BIN, BB_SUID_DROP))
149 141
@@ -154,12 +146,12 @@
154//usage:#define vi_full_usage "\n\n" 146//usage:#define vi_full_usage "\n\n"
155//usage: "Edit FILE\n" 147//usage: "Edit FILE\n"
156//usage: IF_FEATURE_VI_COLON( 148//usage: IF_FEATURE_VI_COLON(
157//usage: "\n -c Initial command to run ($EXINIT also available)" 149//usage: "\n -c CMD Initial command to run ($EXINIT also available)"
158//usage: ) 150//usage: )
159//usage: IF_FEATURE_VI_READONLY( 151//usage: IF_FEATURE_VI_READONLY(
160//usage: "\n -R Read-only" 152//usage: "\n -R Read-only"
161//usage: ) 153//usage: )
162//usage: "\n -H Short help regarding available features" 154//usage: "\n -H List available features"
163 155
164#include "libbb.h" 156#include "libbb.h"
165/* Should be after libbb.h: on some systems regex.h needs sys/types.h: */ 157/* Should be after libbb.h: on some systems regex.h needs sys/types.h: */
@@ -202,20 +194,29 @@ enum {
202 MAX_SCR_ROWS = CONFIG_FEATURE_VI_MAX_LEN, 194 MAX_SCR_ROWS = CONFIG_FEATURE_VI_MAX_LEN,
203}; 195};
204 196
205/* vt102 typical ESC sequence */ 197/* VT102 ESC sequences.
206/* terminal standout start/normal ESC sequence */ 198 * See "Xterm Control Sequences"
207#define SOs "\033[7m" 199 * http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
208#define SOn "\033[0m" 200 */
209/* terminal bell sequence */ 201/* Inverse/Normal text */
210#define bell "\007" 202#define ESC_BOLD_TEXT "\033[7m"
211/* Clear-end-of-line and Clear-end-of-screen ESC sequence */ 203#define ESC_NORM_TEXT "\033[0m"
212#define Ceol "\033[K" 204/* Bell */
213#define Ceos "\033[J" 205#define ESC_BELL "\007"
214/* Cursor motion arbitrary destination ESC sequence */ 206/* Clear-to-end-of-line */
215#define CMrc "\033[%u;%uH" 207#define ESC_CLEAR2EOL "\033[K"
216/* Cursor motion up and down ESC sequence */ 208/* Clear-to-end-of-screen.
217#define CMup "\033[A" 209 * (We use default param here.
218#define CMdown "\n" 210 * Full sequence is "ESC [ <num> J",
211 * <num> is 0/1/2 = "erase below/above/all".)
212 */
213#define ESC_CLEAR2EOS "\033[J"
214/* Cursor to given coordinate (1,1: top left) */
215#define ESC_SET_CURSOR_POS "\033[%u;%uH"
216//UNUSED
217///* Cursor up and down */
218//#define ESC_CURSOR_UP "\033[A"
219//#define ESC_CURSOR_DOWN "\n"
219 220
220#if ENABLE_FEATURE_VI_DOT_CMD || ENABLE_FEATURE_VI_YANKMARK 221#if ENABLE_FEATURE_VI_DOT_CMD || ENABLE_FEATURE_VI_YANKMARK
221// cmds modifying text[] 222// cmds modifying text[]
@@ -303,9 +304,6 @@ struct globals {
303 int lmc_len; // length of last_modifying_cmd 304 int lmc_len; // length of last_modifying_cmd
304 char *ioq, *ioq_start; // pointer to string for get_one_char to "read" 305 char *ioq, *ioq_start; // pointer to string for get_one_char to "read"
305#endif 306#endif
306#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
307 int last_row; // where the cursor was last moved to
308#endif
309#if ENABLE_FEATURE_VI_USE_SIGNALS || ENABLE_FEATURE_VI_CRASHME 307#if ENABLE_FEATURE_VI_USE_SIGNALS || ENABLE_FEATURE_VI_CRASHME
310 int my_pid; 308 int my_pid;
311#endif 309#endif
@@ -389,7 +387,6 @@ struct globals {
389#define lmc_len (G.lmc_len ) 387#define lmc_len (G.lmc_len )
390#define ioq (G.ioq ) 388#define ioq (G.ioq )
391#define ioq_start (G.ioq_start ) 389#define ioq_start (G.ioq_start )
392#define last_row (G.last_row )
393#define my_pid (G.my_pid ) 390#define my_pid (G.my_pid )
394#define last_search_pattern (G.last_search_pattern) 391#define last_search_pattern (G.last_search_pattern)
395 392
@@ -470,10 +467,7 @@ static int file_size(const char *); // what is the byte size of "fn"
470// file_insert might reallocate text[]! 467// file_insert might reallocate text[]!
471static int file_insert(const char *, char *, int); 468static int file_insert(const char *, char *, int);
472static int file_write(char *, char *, char *); 469static int file_write(char *, char *, char *);
473#if !ENABLE_FEATURE_VI_OPTIMIZE_CURSOR 470static void place_cursor(int, int);
474#define place_cursor(a, b, optimize) place_cursor(a, b)
475#endif
476static void place_cursor(int, int, int);
477static void screen_erase(void); 471static void screen_erase(void);
478static void clear_to_eol(void); 472static void clear_to_eol(void);
479static void clear_to_eos(void); 473static void clear_to_eos(void);
@@ -558,7 +552,8 @@ int vi_main(int argc, char **argv)
558 } 552 }
559#endif 553#endif
560 554
561 vi_setops = VI_AUTOINDENT | VI_SHOWMATCH | VI_IGNORECASE; 555 // autoindent is not default in vim 7.3
556 vi_setops = /*VI_AUTOINDENT |*/ VI_SHOWMATCH | VI_IGNORECASE;
562 // 1- process $HOME/.exrc file (not inplemented yet) 557 // 1- process $HOME/.exrc file (not inplemented yet)
563 // 2- process EXINIT variable from environment 558 // 2- process EXINIT variable from environment
564 // 3- process command line args 559 // 3- process command line args
@@ -584,7 +579,7 @@ int vi_main(int argc, char **argv)
584#if ENABLE_FEATURE_VI_COLON 579#if ENABLE_FEATURE_VI_COLON
585 case 'c': // cmd line vi command 580 case 'c': // cmd line vi command
586 if (*optarg) 581 if (*optarg)
587 initial_cmds[initial_cmds[0] != 0] = xstrndup(optarg, MAX_INPUT_LEN); 582 initial_cmds[initial_cmds[0] != NULL] = xstrndup(optarg, MAX_INPUT_LEN);
588 break; 583 break;
589#endif 584#endif
590 case 'H': 585 case 'H':
@@ -599,15 +594,19 @@ int vi_main(int argc, char **argv)
599 // The argv array can be used by the ":next" and ":rewind" commands 594 // The argv array can be used by the ":next" and ":rewind" commands
600 argv += optind; 595 argv += optind;
601 argc -= optind; 596 argc -= optind;
602 save_argc = argc;
603 optind = 0;
604 597
605 //----- This is the main file handling loop -------------- 598 //----- This is the main file handling loop --------------
599 save_argc = argc;
600 optind = 0;
601 // "Save cursor, use alternate screen buffer, clear screen"
602 write1("\033[?1049h");
606 while (1) { 603 while (1) {
607 edit_file(argv[optind]); /* param might be NULL */ 604 edit_file(argv[optind]); /* param might be NULL */
608 if (++optind >= argc) 605 if (++optind >= argc)
609 break; 606 break;
610 } 607 }
608 // "Use normal screen buffer, restore cursor"
609 write1("\033[?1049l");
611 //----------------------------------------------------------- 610 //-----------------------------------------------------------
612 611
613 return 0; 612 return 0;
@@ -1191,7 +1190,7 @@ static void colon(char *buf)
1191 char *argp; 1190 char *argp;
1192#endif 1191#endif
1193 i = 0; // offset into args 1192 i = 0; // offset into args
1194 // only blank is regarded as args delmiter. What about tab '\t' ? 1193 // only blank is regarded as args delimiter. What about tab '\t'?
1195 if (!args[0] || strcasecmp(args, "all") == 0) { 1194 if (!args[0] || strcasecmp(args, "all") == 0) {
1196 // print out values of all options 1195 // print out values of all options
1197#if ENABLE_FEATURE_VI_SETOPTS 1196#if ENABLE_FEATURE_VI_SETOPTS
@@ -2176,7 +2175,7 @@ static void show_help(void)
2176 "\n\tPattern searches with / and ?" 2175 "\n\tPattern searches with / and ?"
2177#endif 2176#endif
2178#if ENABLE_FEATURE_VI_DOT_CMD 2177#if ENABLE_FEATURE_VI_DOT_CMD
2179 "\n\tLast command repeat with \'.\'" 2178 "\n\tLast command repeat with ."
2180#endif 2179#endif
2181#if ENABLE_FEATURE_VI_YANKMARK 2180#if ENABLE_FEATURE_VI_YANKMARK
2182 "\n\tLine marking with 'x" 2181 "\n\tLine marking with 'x"
@@ -2187,7 +2186,7 @@ static void show_help(void)
2187 //redundant: usage text says this too: "\n\tReadonly with -R command line arg" 2186 //redundant: usage text says this too: "\n\tReadonly with -R command line arg"
2188#endif 2187#endif
2189#if ENABLE_FEATURE_VI_SET 2188#if ENABLE_FEATURE_VI_SET
2190 "\n\tSome colon mode commands with \':\'" 2189 "\n\tSome colon mode commands with :"
2191#endif 2190#endif
2192#if ENABLE_FEATURE_VI_SETOPTS 2191#if ENABLE_FEATURE_VI_SETOPTS
2193 "\n\tSettable options with \":set\"" 2192 "\n\tSettable options with \":set\""
@@ -2601,107 +2600,56 @@ static int file_write(char *fn, char *first, char *last)
2601// 23,0 ... 23,79 <- status line 2600// 23,0 ... 23,79 <- status line
2602 2601
2603//----- Move the cursor to row x col (count from 0, not 1) ------- 2602//----- Move the cursor to row x col (count from 0, not 1) -------
2604static void place_cursor(int row, int col, int optimize) 2603static void place_cursor(int row, int col)
2605{ 2604{
2606 char cm1[sizeof(CMrc) + sizeof(int)*3 * 2]; 2605 char cm1[sizeof(ESC_SET_CURSOR_POS) + sizeof(int)*3 * 2];
2607#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
2608 enum {
2609 SZ_UP = sizeof(CMup),
2610 SZ_DN = sizeof(CMdown),
2611 SEQ_SIZE = SZ_UP > SZ_DN ? SZ_UP : SZ_DN,
2612 };
2613 char cm2[SEQ_SIZE * 5 + 32]; // bigger than worst case size
2614#endif
2615 char *cm;
2616 2606
2617 if (row < 0) row = 0; 2607 if (row < 0) row = 0;
2618 if (row >= rows) row = rows - 1; 2608 if (row >= rows) row = rows - 1;
2619 if (col < 0) col = 0; 2609 if (col < 0) col = 0;
2620 if (col >= columns) col = columns - 1; 2610 if (col >= columns) col = columns - 1;
2621 2611
2622 //----- 1. Try the standard terminal ESC sequence 2612 sprintf(cm1, ESC_SET_CURSOR_POS, row + 1, col + 1);
2623 sprintf(cm1, CMrc, row + 1, col + 1); 2613 write1(cm1);
2624 cm = cm1;
2625
2626#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
2627 if (optimize && col < 16) {
2628 char *screenp;
2629 int Rrow = last_row;
2630 int diff = Rrow - row;
2631
2632 if (diff < -5 || diff > 5)
2633 goto skip;
2634
2635 //----- find the minimum # of chars to move cursor -------------
2636 //----- 2. Try moving with discreet chars (Newline, [back]space, ...)
2637 cm2[0] = '\0';
2638
2639 // move to the correct row
2640 while (row < Rrow) {
2641 // the cursor has to move up
2642 strcat(cm2, CMup);
2643 Rrow--;
2644 }
2645 while (row > Rrow) {
2646 // the cursor has to move down
2647 strcat(cm2, CMdown);
2648 Rrow++;
2649 }
2650
2651 // now move to the correct column
2652 strcat(cm2, "\r"); // start at col 0
2653 // just send out orignal source char to get to correct place
2654 screenp = &screen[row * columns]; // start of screen line
2655 strncat(cm2, screenp, col);
2656
2657 // pick the shortest cursor motion to send out
2658 if (strlen(cm2) < strlen(cm)) {
2659 cm = cm2;
2660 }
2661 skip: ;
2662 }
2663 last_row = row;
2664#endif /* FEATURE_VI_OPTIMIZE_CURSOR */
2665 write1(cm);
2666} 2614}
2667 2615
2668//----- Erase from cursor to end of line ----------------------- 2616//----- Erase from cursor to end of line -----------------------
2669static void clear_to_eol(void) 2617static void clear_to_eol(void)
2670{ 2618{
2671 write1(Ceol); // Erase from cursor to end of line 2619 write1(ESC_CLEAR2EOL);
2672} 2620}
2673 2621
2674static void go_bottom_and_clear_to_eol(void) 2622static void go_bottom_and_clear_to_eol(void)
2675{ 2623{
2676 place_cursor(rows - 1, 0, FALSE); // go to bottom of screen 2624 place_cursor(rows - 1, 0);
2677 clear_to_eol(); // erase to end of line 2625 clear_to_eol();
2678} 2626}
2679 2627
2680//----- Erase from cursor to end of screen ----------------------- 2628//----- Erase from cursor to end of screen -----------------------
2681static void clear_to_eos(void) 2629static void clear_to_eos(void)
2682{ 2630{
2683 write1(Ceos); // Erase from cursor to end of screen 2631 write1(ESC_CLEAR2EOS);
2684} 2632}
2685 2633
2686//----- Start standout mode ------------------------------------ 2634//----- Start standout mode ------------------------------------
2687static void standout_start(void) // send "start reverse video" sequence 2635static void standout_start(void)
2688{ 2636{
2689 write1(SOs); // Start reverse video mode 2637 write1(ESC_BOLD_TEXT);
2690} 2638}
2691 2639
2692//----- End standout mode -------------------------------------- 2640//----- End standout mode --------------------------------------
2693static void standout_end(void) // send "end reverse video" sequence 2641static void standout_end(void)
2694{ 2642{
2695 write1(SOn); // End reverse video mode 2643 write1(ESC_NORM_TEXT);
2696} 2644}
2697 2645
2698//----- Flash the screen -------------------------------------- 2646//----- Flash the screen --------------------------------------
2699static void flash(int h) 2647static void flash(int h)
2700{ 2648{
2701 standout_start(); // send "start reverse video" sequence 2649 standout_start();
2702 redraw(TRUE); 2650 redraw(TRUE);
2703 mysleep(h); 2651 mysleep(h);
2704 standout_end(); // send "end reverse video" sequence 2652 standout_end();
2705 redraw(TRUE); 2653 redraw(TRUE);
2706} 2654}
2707 2655
@@ -2712,7 +2660,7 @@ static void Indicate_Error(void)
2712 return; // generate a random command 2660 return; // generate a random command
2713#endif 2661#endif
2714 if (!err_method) { 2662 if (!err_method) {
2715 write1(bell); // send out a bell character 2663 write1(ESC_BELL);
2716 } else { 2664 } else {
2717 flash(10); 2665 flash(10);
2718 } 2666 }
@@ -2758,7 +2706,7 @@ static void show_status_line(void)
2758 } 2706 }
2759 have_status_msg = 0; 2707 have_status_msg = 0;
2760 } 2708 }
2761 place_cursor(crow, ccol, FALSE); // put cursor back in correct place 2709 place_cursor(crow, ccol); // put cursor back in correct place
2762 } 2710 }
2763 fflush_all(); 2711 fflush_all();
2764} 2712}
@@ -2770,12 +2718,12 @@ static void status_line_bold(const char *format, ...)
2770 va_list args; 2718 va_list args;
2771 2719
2772 va_start(args, format); 2720 va_start(args, format);
2773 strcpy(status_buffer, SOs); // Terminal standout mode on 2721 strcpy(status_buffer, ESC_BOLD_TEXT);
2774 vsprintf(status_buffer + sizeof(SOs)-1, format, args); 2722 vsprintf(status_buffer + sizeof(ESC_BOLD_TEXT)-1, format, args);
2775 strcat(status_buffer, SOn); // Terminal standout mode off 2723 strcat(status_buffer, ESC_NORM_TEXT);
2776 va_end(args); 2724 va_end(args);
2777 2725
2778 have_status_msg = 1 + sizeof(SOs) + sizeof(SOn) - 2; 2726 have_status_msg = 1 + sizeof(ESC_BOLD_TEXT) + sizeof(ESC_NORM_TEXT) - 2;
2779} 2727}
2780 2728
2781// format status buffer 2729// format status buffer
@@ -2807,8 +2755,8 @@ static void print_literal(char *buf, const char *s)
2807 c = *s; 2755 c = *s;
2808 c_is_no_print = (c & 0x80) && !Isprint(c); 2756 c_is_no_print = (c & 0x80) && !Isprint(c);
2809 if (c_is_no_print) { 2757 if (c_is_no_print) {
2810 strcpy(d, SOn); 2758 strcpy(d, ESC_NORM_TEXT);
2811 d += sizeof(SOn)-1; 2759 d += sizeof(ESC_NORM_TEXT)-1;
2812 c = '.'; 2760 c = '.';
2813 } 2761 }
2814 if (c < ' ' || c == 0x7f) { 2762 if (c < ' ' || c == 0x7f) {
@@ -2820,8 +2768,8 @@ static void print_literal(char *buf, const char *s)
2820 *d++ = c; 2768 *d++ = c;
2821 *d = '\0'; 2769 *d = '\0';
2822 if (c_is_no_print) { 2770 if (c_is_no_print) {
2823 strcpy(d, SOs); 2771 strcpy(d, ESC_BOLD_TEXT);
2824 d += sizeof(SOs)-1; 2772 d += sizeof(ESC_BOLD_TEXT)-1;
2825 } 2773 }
2826 if (*s == '\n') { 2774 if (*s == '\n') {
2827 *d++ = '$'; 2775 *d++ = '$';
@@ -2903,8 +2851,8 @@ static int format_edit_status(void)
2903//----- Force refresh of all Lines ----------------------------- 2851//----- Force refresh of all Lines -----------------------------
2904static void redraw(int full_screen) 2852static void redraw(int full_screen)
2905{ 2853{
2906 place_cursor(0, 0, FALSE); // put cursor in correct place 2854 place_cursor(0, 0);
2907 clear_to_eos(); // tell terminal to erase display 2855 clear_to_eos();
2908 screen_erase(); // erase the internal screen buffer 2856 screen_erase(); // erase the internal screen buffer
2909 last_status_cksum = 0; // force status update 2857 last_status_cksum = 0; // force status update
2910 refresh(full_screen); // this will redraw the entire display 2858 refresh(full_screen); // this will redraw the entire display
@@ -3044,22 +2992,13 @@ static void refresh(int full_screen)
3044 if (changed) { 2992 if (changed) {
3045 // copy changed part of buffer to virtual screen 2993 // copy changed part of buffer to virtual screen
3046 memcpy(sp+cs, out_buf+cs, ce-cs+1); 2994 memcpy(sp+cs, out_buf+cs, ce-cs+1);
3047 2995 place_cursor(li, cs);
3048 // move cursor to column of first change
3049 //if (offset != old_offset) {
3050 // // place_cursor is still too stupid
3051 // // to handle offsets correctly
3052 // place_cursor(li, cs, FALSE);
3053 //} else {
3054 place_cursor(li, cs, TRUE);
3055 //}
3056
3057 // write line out to terminal 2996 // write line out to terminal
3058 fwrite(&sp[cs], ce - cs + 1, 1, stdout); 2997 fwrite(&sp[cs], ce - cs + 1, 1, stdout);
3059 } 2998 }
3060 } 2999 }
3061 3000
3062 place_cursor(crow, ccol, TRUE); 3001 place_cursor(crow, ccol);
3063 3002
3064 old_offset = offset; 3003 old_offset = offset;
3065#undef old_offset 3004#undef old_offset
@@ -3229,9 +3168,9 @@ static void do_cmd(int c)
3229 break; 3168 break;
3230 case 12: // ctrl-L force redraw whole screen 3169 case 12: // ctrl-L force redraw whole screen
3231 case 18: // ctrl-R force redraw 3170 case 18: // ctrl-R force redraw
3232 place_cursor(0, 0, FALSE); // put cursor in correct place 3171 place_cursor(0, 0);
3233 clear_to_eos(); // tel terminal to erase display 3172 clear_to_eos();
3234 mysleep(10); 3173 //mysleep(10); // why???
3235 screen_erase(); // erase the internal screen buffer 3174 screen_erase(); // erase the internal screen buffer
3236 last_status_cksum = 0; // force status update 3175 last_status_cksum = 0; // force status update
3237 refresh(TRUE); // this will redraw the entire display 3176 refresh(TRUE); // this will redraw the entire display
@@ -4150,7 +4089,7 @@ static void crash_test()
4150 4089
4151 if (msg[0]) { 4090 if (msg[0]) {
4152 printf("\n\n%d: \'%c\' %s\n\n\n%s[Hit return to continue]%s", 4091 printf("\n\n%d: \'%c\' %s\n\n\n%s[Hit return to continue]%s",
4153 totalcmds, last_input_char, msg, SOs, SOn); 4092 totalcmds, last_input_char, msg, ESC_BOLD_TEXT, ESC_NORM_TEXT);
4154 fflush_all(); 4093 fflush_all();
4155 while (safe_read(STDIN_FILENO, d, 1) > 0) { 4094 while (safe_read(STDIN_FILENO, d, 1) > 0) {
4156 if (d[0] == '\n' || d[0] == '\r') 4095 if (d[0] == '\n' || d[0] == '\r')