busybox/editors/vi.c

4153 lines
112 KiB
C
Raw Normal View History

/* vi: set sw=4 ts=4: */
/*
* tiny vi.c: A small 'vi' clone
* Copyright (C) 2000, 2001 Sterling Huxley <sterling@europa.com>
*
* Licensed under GPLv2 or later, see file LICENSE in this source tree.
*/
/*
* Things To Do:
* EXINIT
* $HOME/.exrc and ./.exrc
* add magic to search /foo.*bar
* add :help command
* :map macros
* if mark[] values were line numbers rather than pointers
* it would be easier to change the mark when add/delete lines
* More intelligence in refresh()
* ":r !cmd" and "!cmd" to filter text through an external command
* A true "undo" facility
* An "ex" line oriented mode- maybe using "cmdedit"
*/
//config:config VI
//config: bool "vi"
//config: default y
//config: help
//config: 'vi' is a text editor. More specifically, it is the One True
//config: text editor <grin>. It does, however, have a rather steep
//config: learning curve. If you are not already comfortable with 'vi'
//config: you may wish to use something else.
//config:
//config:config FEATURE_VI_MAX_LEN
//config: int "Maximum screen width in vi"
//config: range 256 16384
//config: default 4096
//config: depends on VI
//config: help
//config: Contrary to what you may think, this is not eating much.
//config: Make it smaller than 4k only if you are very limited on memory.
//config:
//config:config FEATURE_VI_8BIT
//config: bool "Allow vi to display 8-bit chars (otherwise shows dots)"
//config: default n
//config: depends on VI
//config: help
//config: If your terminal can display characters with high bit set,
//config: you may want to enable this. Note: vi is not Unicode-capable.
//config: If your terminal combines several 8-bit bytes into one character
//config: (as in Unicode mode), this will not work properly.
//config:
//config:config FEATURE_VI_COLON
//config: bool "Enable \":\" colon commands (no \"ex\" mode)"
//config: default y
//config: depends on VI
//config: help
//config: Enable a limited set of colon commands for vi. This does not
//config: provide an "ex" mode.
//config:
//config:config FEATURE_VI_YANKMARK
//config: bool "Enable yank/put commands and mark cmds"
//config: default y
//config: depends on VI
//config: help
//config: This will enable you to use yank and put, as well as mark in
//config: busybox vi.
//config:
//config:config FEATURE_VI_SEARCH
//config: bool "Enable search and replace cmds"
//config: default y
//config: depends on VI
//config: help
//config: Select this if you wish to be able to do search and replace in
//config: busybox vi.
//config:
//config:config FEATURE_VI_REGEX_SEARCH
//config: bool "Enable regex in search and replace"
//config: default n # Uses GNU regex, which may be unavailable. FIXME
//config: depends on FEATURE_VI_SEARCH
//config: help
//config: Use extended regex search.
//config:
//config:config FEATURE_VI_USE_SIGNALS
//config: bool "Catch signals"
//config: default y
//config: depends on VI
//config: help
//config: Selecting this option will make busybox vi signal aware. This will
//config: make busybox vi support SIGWINCH to deal with Window Changes, catch
//config: Ctrl-Z and Ctrl-C and alarms.
//config:
//config:config FEATURE_VI_DOT_CMD
//config: bool "Remember previous cmd and \".\" cmd"
//config: default y
//config: depends on VI
//config: help
//config: Make busybox vi remember the last command and be able to repeat it.
//config:
//config:config FEATURE_VI_READONLY
//config: bool "Enable -R option and \"view\" mode"
//config: default y
//config: depends on VI
//config: help
//config: Enable the read-only command line option, which allows the user to
//config: open a file in read-only mode.
//config:
//config:config FEATURE_VI_SETOPTS
//config: bool "Enable set-able options, ai ic showmatch"
//config: default y
//config: depends on VI
//config: help
//config: Enable the editor to set some (ai, ic, showmatch) options.
//config:
//config:config FEATURE_VI_SET
//config: bool "Support for :set"
//config: default y
//config: depends on VI
//config: help
//config: Support for ":set".
//config:
//config:config FEATURE_VI_WIN_RESIZE
//config: bool "Handle window resize"
//config: default y
//config: depends on VI
//config: help
//config: Make busybox vi behave nicely with terminals that get resized.
//config:
//config:config FEATURE_VI_ASK_TERMINAL
//config: bool "Use 'tell me cursor position' ESC sequence to measure window"
//config: default y
//config: depends on VI
//config: help
//config: If terminal size can't be retrieved and $LINES/$COLUMNS are not set,
//config: this option makes vi perform a last-ditch effort to find it:
//config: position cursor to 999,999 and ask terminal to report real
//config: cursor position using "ESC [ 6 n" escape sequence, then read stdin.
//config:
//config: This is not clean but helps a lot on serial lines and such.
//config:
//config:config FEATURE_VI_OPTIMIZE_CURSOR
//config: bool "Optimize cursor movement"
//config: default y
//config: depends on VI
//config: help
//config: This will make the cursor movement faster, but requires more memory
//config: and it makes the applet a tiny bit larger.
//applet:IF_VI(APPLET(vi, BB_DIR_BIN, BB_SUID_DROP))
//kbuild:lib-$(CONFIG_VI) += vi.o
//usage:#define vi_trivial_usage
//usage: "[OPTIONS] [FILE]..."
//usage:#define vi_full_usage "\n\n"
//usage: "Edit FILE\n"
//usage: IF_FEATURE_VI_COLON(
//usage: "\n -c Initial command to run ($EXINIT also available)"
//usage: )
//usage: IF_FEATURE_VI_READONLY(
//usage: "\n -R Read-only"
//usage: )
//usage: "\n -H Short help regarding available features"
#include "libbb.h"
/* Should be after libbb.h: on some systems regex.h needs sys/types.h: */
#if ENABLE_FEATURE_VI_REGEX_SEARCH
# include <regex.h>
#endif
/* the CRASHME code is unmaintained, and doesn't currently build */
#define ENABLE_FEATURE_VI_CRASHME 0
#if ENABLE_LOCALE_SUPPORT
#if ENABLE_FEATURE_VI_8BIT
//FIXME: this does not work properly for Unicode anyway
# define Isprint(c) (isprint)(c)
#else
# define Isprint(c) isprint_asciionly(c)
#endif
#else
/* 0x9b is Meta-ESC */
#if ENABLE_FEATURE_VI_8BIT
# define Isprint(c) ((unsigned char)(c) >= ' ' && (c) != 0x7f && (unsigned char)(c) != 0x9b)
#else
# define Isprint(c) ((unsigned char)(c) >= ' ' && (unsigned char)(c) < 0x7f)
#endif
#endif
enum {
MAX_TABSTOP = 32, // sanity limit
// User input len. Need not be extra big.
// Lines in file being edited *can* be bigger than this.
MAX_INPUT_LEN = 128,
// Sanity limits. We have only one buffer of this size.
MAX_SCR_COLS = CONFIG_FEATURE_VI_MAX_LEN,
MAX_SCR_ROWS = CONFIG_FEATURE_VI_MAX_LEN,
};
/* vt102 typical ESC sequence */
/* terminal standout start/normal ESC sequence */
#define SOs "\033[7m"
#define SOn "\033[0m"
/* terminal bell sequence */
#define bell "\007"
/* Clear-end-of-line and Clear-end-of-screen ESC sequence */
#define Ceol "\033[K"
#define Ceos "\033[J"
/* Cursor motion arbitrary destination ESC sequence */
#define CMrc "\033[%u;%uH"
/* Cursor motion up and down ESC sequence */
#define CMup "\033[A"
#define CMdown "\n"
#if ENABLE_FEATURE_VI_DOT_CMD || ENABLE_FEATURE_VI_YANKMARK
// cmds modifying text[]
// vda: removed "aAiIs" as they switch us into insert mode
// and remembering input for replay after them makes no sense
static const char modifying_cmds[] = "cCdDJoOpPrRxX<>~";
#endif
enum {
YANKONLY = FALSE,
YANKDEL = TRUE,
FORWARD = 1, // code depends on "1" for array index
BACK = -1, // code depends on "-1" for array index
LIMITED = 0, // how much of text[] in char_search
FULL = 1, // how much of text[] in char_search
S_BEFORE_WS = 1, // used in skip_thing() for moving "dot"
S_TO_WS = 2, // used in skip_thing() for moving "dot"
S_OVER_WS = 3, // used in skip_thing() for moving "dot"
S_END_PUNCT = 4, // used in skip_thing() for moving "dot"
2007-03-07 17:35:43 +08:00
S_END_ALNUM = 5, // used in skip_thing() for moving "dot"
};
/* vi.c expects chars to be unsigned. */
/* busybox build system provides that, but it's better */
/* to audit and fix the source */
struct globals {
/* many references - keep near the top of globals */
char *text, *end; // pointers to the user data in memory
char *dot; // where all the action takes place
int text_size; // size of the allocated buffer
/* the rest */
smallint vi_setops;
#define VI_AUTOINDENT 1
#define VI_SHOWMATCH 2
#define VI_IGNORECASE 4
#define VI_ERR_METHOD 8
#define autoindent (vi_setops & VI_AUTOINDENT)
#define showmatch (vi_setops & VI_SHOWMATCH )
#define ignorecase (vi_setops & VI_IGNORECASE)
/* indicate error with beep or flash */
#define err_method (vi_setops & VI_ERR_METHOD)
#if ENABLE_FEATURE_VI_READONLY
smallint readonly_mode;
2007-08-16 18:35:17 +08:00
#define SET_READONLY_FILE(flags) ((flags) |= 0x01)
#define SET_READONLY_MODE(flags) ((flags) |= 0x02)
#define UNSET_READONLY_FILE(flags) ((flags) &= 0xfe)
vi: multiple fixes by Natanael Copa <natanael.copa@gmail.com> * the puzzling message on error is replaced with strerror(errno) so it should be even more detailed and smaller at the same time. * merged code in edit_file() and code for ':edit <file>' in colon() into new func init_text_buffer(). Was horribly duplicate. Moved most of error/sanity checking to file_insert(). Result is that you get a proper validation (prevent reading /dev/*) and error messages for ':r <file>' * renamed 'cfn' to 'current_filename' for improved readability * merged smallint vi_readonly and readonly into bitfields into readonly_mode to save space. * added text_size variable to keep track how big the text buffer is. This is used to fix a buffer overflow. To reproduce bug: ./busybox vi TODO :r Makefile vi segfaults due to no buffer checking is done at all. som redesign is needed here but i added a check in text_hole_make() to aviod the segfault at least. * removed isblnk() and use isblank(3) instead. * fixed compiler warning by displaying the return code for :!<command> This makes things bigger than needed but since the patch reduces the overall size... (see below) * new func next_tabstop(int) merges some duplicate code. There are more cuplicode here but i couldnt find a good way to merge them. * Fix *ANNOYING* placement of cursor on '\t' characters. To reproduce: echo -e "\thello" > file1 ./busybox vi file1 Try to insert some text at the beginning of line. Text will be inserted but cursor is blinking somewhere else. The patch should make busybox vi behave more like original vi(m). Costs a few bytes but its worth it imho. * new_text() is moved into init_text_buffer() * the previously added update_ro_status() was moved info file_insert due to duplication removal mentioned above. function old new delta init_text_buffer - 245 +245 file_insert 312 420 +108 next_tabstop - 82 +82 text_hole_make 154 171 +17 do_cmd 5093 5100 +7 static.cmd_mode_indicator - 5 +5 refresh 1248 1253 +5 current_filename - 4 +4 yank_delete 161 164 +3 what_reg 96 99 +3 end_cmd_q 78 81 +3 char_insert 440 442 +2 readonly_mode - 1 +1 vi_readonly 1 - -1 setops 154 153 -1 readonly 1 - -1 vi_setops 4 1 -3 string_insert 161 158 -3 cfn 4 - -4 show_status_line 532 514 -18 readit 519 500 -19 move_to_col 161 138 -23 vi_main 495 433 -62 isblnk 75 - -75 .rodata 4751 4655 -96 edit_file 892 787 -105 new_text 125 - -125 update_ro_status 131 - -131 colon 3848 3667 -181 ------------------------------------------------------------------------------ (add/remove: 5/6 grow/shrink: 8/10 up/down: 485/-848) Total: -363 bytes text data bss dec hex filename 34751 873 4260 39884 9bcc busybox_old 34439 877 4260 39576 9a98 busybox_unstripped
2007-07-18 07:14:07 +08:00
#else
#define SET_READONLY_FILE(flags) ((void)0)
#define SET_READONLY_MODE(flags) ((void)0)
#define UNSET_READONLY_FILE(flags) ((void)0)
#endif
smallint editing; // >0 while we are editing a file
// [code audit says "can be 0, 1 or 2 only"]
smallint cmd_mode; // 0=command 1=insert 2=replace
int file_modified; // buffer contents changed (counter, not flag!)
int last_file_modified; // = -1;
int fn_start; // index of first cmd line file name
int save_argc; // how many file names on cmd line
int cmdcnt; // repetition count
unsigned rows, columns; // the terminal screen is this size
#if ENABLE_FEATURE_VI_ASK_TERMINAL
int get_rowcol_error;
#endif
int crow, ccol; // cursor is on Crow x Ccol
int offset; // chars scrolled off the screen to the left
int have_status_msg; // is default edit status needed?
// [don't make smallint!]
int last_status_cksum; // hash of current status line
char *current_filename;
char *screenbegin; // index into text[], of top line on the screen
char *screen; // pointer to the virtual screen buffer
int screensize; // and its size
int tabstop;
int last_forward_char; // last char searched for with 'f' (int because of Unicode)
char erase_char; // the users erase character
char last_input_char; // last char read from user
vi: multiple fixes by Natanael Copa <natanael.copa@gmail.com> * the puzzling message on error is replaced with strerror(errno) so it should be even more detailed and smaller at the same time. * merged code in edit_file() and code for ':edit <file>' in colon() into new func init_text_buffer(). Was horribly duplicate. Moved most of error/sanity checking to file_insert(). Result is that you get a proper validation (prevent reading /dev/*) and error messages for ':r <file>' * renamed 'cfn' to 'current_filename' for improved readability * merged smallint vi_readonly and readonly into bitfields into readonly_mode to save space. * added text_size variable to keep track how big the text buffer is. This is used to fix a buffer overflow. To reproduce bug: ./busybox vi TODO :r Makefile vi segfaults due to no buffer checking is done at all. som redesign is needed here but i added a check in text_hole_make() to aviod the segfault at least. * removed isblnk() and use isblank(3) instead. * fixed compiler warning by displaying the return code for :!<command> This makes things bigger than needed but since the patch reduces the overall size... (see below) * new func next_tabstop(int) merges some duplicate code. There are more cuplicode here but i couldnt find a good way to merge them. * Fix *ANNOYING* placement of cursor on '\t' characters. To reproduce: echo -e "\thello" > file1 ./busybox vi file1 Try to insert some text at the beginning of line. Text will be inserted but cursor is blinking somewhere else. The patch should make busybox vi behave more like original vi(m). Costs a few bytes but its worth it imho. * new_text() is moved into init_text_buffer() * the previously added update_ro_status() was moved info file_insert due to duplication removal mentioned above. function old new delta init_text_buffer - 245 +245 file_insert 312 420 +108 next_tabstop - 82 +82 text_hole_make 154 171 +17 do_cmd 5093 5100 +7 static.cmd_mode_indicator - 5 +5 refresh 1248 1253 +5 current_filename - 4 +4 yank_delete 161 164 +3 what_reg 96 99 +3 end_cmd_q 78 81 +3 char_insert 440 442 +2 readonly_mode - 1 +1 vi_readonly 1 - -1 setops 154 153 -1 readonly 1 - -1 vi_setops 4 1 -3 string_insert 161 158 -3 cfn 4 - -4 show_status_line 532 514 -18 readit 519 500 -19 move_to_col 161 138 -23 vi_main 495 433 -62 isblnk 75 - -75 .rodata 4751 4655 -96 edit_file 892 787 -105 new_text 125 - -125 update_ro_status 131 - -131 colon 3848 3667 -181 ------------------------------------------------------------------------------ (add/remove: 5/6 grow/shrink: 8/10 up/down: 485/-848) Total: -363 bytes text data bss dec hex filename 34751 873 4260 39884 9bcc busybox_old 34439 877 4260 39576 9a98 busybox_unstripped
2007-07-18 07:14:07 +08:00
#if ENABLE_FEATURE_VI_DOT_CMD
smallint adding2q; // are we currently adding user input to q
int lmc_len; // length of last_modifying_cmd
char *ioq, *ioq_start; // pointer to string for get_one_char to "read"
#endif
#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
int last_row; // where the cursor was last moved to
#endif
#if ENABLE_FEATURE_VI_USE_SIGNALS || ENABLE_FEATURE_VI_CRASHME
int my_pid;
#endif
#if ENABLE_FEATURE_VI_SEARCH
char *last_search_pattern; // last pattern from a '/' or '?' search
#endif
/* former statics */
#if ENABLE_FEATURE_VI_YANKMARK
char *edit_file__cur_line;
#endif
int refresh__old_offset;
int format_edit_status__tot;
/* a few references only */
#if ENABLE_FEATURE_VI_YANKMARK
int YDreg, Ureg; // default delete register and orig line for "U"
char *reg[28]; // named register a-z, "D", and "U" 0-25,26,27
char *mark[28]; // user marks points somewhere in text[]- a-z and previous context ''
char *context_start, *context_end;
#endif
#if ENABLE_FEATURE_VI_USE_SIGNALS
sigjmp_buf restart; // catch_sig()
#endif
struct termios term_orig, term_vi; // remember what the cooked mode was
#if ENABLE_FEATURE_VI_COLON
char *initial_cmds[3]; // currently 2 entries, NULL terminated
#endif
// Should be just enough to hold a key sequence,
2008-10-14 18:25:05 +08:00
// but CRASHME mode uses it as generated command buffer too
#if ENABLE_FEATURE_VI_CRASHME
2008-10-19 03:04:37 +08:00
char readbuffer[128];
2008-10-14 18:25:05 +08:00
#else
char readbuffer[KEYCODE_BUFFER_SIZE];
2008-10-14 18:25:05 +08:00
#endif
#define STATUS_BUFFER_LEN 200
char status_buffer[STATUS_BUFFER_LEN]; // messages to the user
#if ENABLE_FEATURE_VI_DOT_CMD
char last_modifying_cmd[MAX_INPUT_LEN]; // last modifying cmd for "."
#endif
char get_input_line__buf[MAX_INPUT_LEN]; /* former static */
char scr_out_buf[MAX_SCR_COLS + MAX_TABSTOP * 2];
};
#define G (*ptr_to_globals)
#define text (G.text )
vi: multiple fixes by Natanael Copa <natanael.copa@gmail.com> * the puzzling message on error is replaced with strerror(errno) so it should be even more detailed and smaller at the same time. * merged code in edit_file() and code for ':edit <file>' in colon() into new func init_text_buffer(). Was horribly duplicate. Moved most of error/sanity checking to file_insert(). Result is that you get a proper validation (prevent reading /dev/*) and error messages for ':r <file>' * renamed 'cfn' to 'current_filename' for improved readability * merged smallint vi_readonly and readonly into bitfields into readonly_mode to save space. * added text_size variable to keep track how big the text buffer is. This is used to fix a buffer overflow. To reproduce bug: ./busybox vi TODO :r Makefile vi segfaults due to no buffer checking is done at all. som redesign is needed here but i added a check in text_hole_make() to aviod the segfault at least. * removed isblnk() and use isblank(3) instead. * fixed compiler warning by displaying the return code for :!<command> This makes things bigger than needed but since the patch reduces the overall size... (see below) * new func next_tabstop(int) merges some duplicate code. There are more cuplicode here but i couldnt find a good way to merge them. * Fix *ANNOYING* placement of cursor on '\t' characters. To reproduce: echo -e "\thello" > file1 ./busybox vi file1 Try to insert some text at the beginning of line. Text will be inserted but cursor is blinking somewhere else. The patch should make busybox vi behave more like original vi(m). Costs a few bytes but its worth it imho. * new_text() is moved into init_text_buffer() * the previously added update_ro_status() was moved info file_insert due to duplication removal mentioned above. function old new delta init_text_buffer - 245 +245 file_insert 312 420 +108 next_tabstop - 82 +82 text_hole_make 154 171 +17 do_cmd 5093 5100 +7 static.cmd_mode_indicator - 5 +5 refresh 1248 1253 +5 current_filename - 4 +4 yank_delete 161 164 +3 what_reg 96 99 +3 end_cmd_q 78 81 +3 char_insert 440 442 +2 readonly_mode - 1 +1 vi_readonly 1 - -1 setops 154 153 -1 readonly 1 - -1 vi_setops 4 1 -3 string_insert 161 158 -3 cfn 4 - -4 show_status_line 532 514 -18 readit 519 500 -19 move_to_col 161 138 -23 vi_main 495 433 -62 isblnk 75 - -75 .rodata 4751 4655 -96 edit_file 892 787 -105 new_text 125 - -125 update_ro_status 131 - -131 colon 3848 3667 -181 ------------------------------------------------------------------------------ (add/remove: 5/6 grow/shrink: 8/10 up/down: 485/-848) Total: -363 bytes text data bss dec hex filename 34751 873 4260 39884 9bcc busybox_old 34439 877 4260 39576 9a98 busybox_unstripped
2007-07-18 07:14:07 +08:00
#define text_size (G.text_size )
#define end (G.end )
#define dot (G.dot )
#define reg (G.reg )
#define vi_setops (G.vi_setops )
#define editing (G.editing )
#define cmd_mode (G.cmd_mode )
#define file_modified (G.file_modified )
#define last_file_modified (G.last_file_modified )
#define fn_start (G.fn_start )
#define save_argc (G.save_argc )
#define cmdcnt (G.cmdcnt )
#define rows (G.rows )
#define columns (G.columns )
#define crow (G.crow )
#define ccol (G.ccol )
#define offset (G.offset )
#define status_buffer (G.status_buffer )
#define have_status_msg (G.have_status_msg )
#define last_status_cksum (G.last_status_cksum )
#define current_filename (G.current_filename )
#define screen (G.screen )
#define screensize (G.screensize )
#define screenbegin (G.screenbegin )
#define tabstop (G.tabstop )
#define last_forward_char (G.last_forward_char )
#define erase_char (G.erase_char )
#define last_input_char (G.last_input_char )
#if ENABLE_FEATURE_VI_READONLY
#define readonly_mode (G.readonly_mode )
#else
#define readonly_mode 0
#endif
#define adding2q (G.adding2q )
#define lmc_len (G.lmc_len )
#define ioq (G.ioq )
#define ioq_start (G.ioq_start )
#define last_row (G.last_row )
#define my_pid (G.my_pid )
#define last_search_pattern (G.last_search_pattern)
#define edit_file__cur_line (G.edit_file__cur_line)
#define refresh__old_offset (G.refresh__old_offset)
#define format_edit_status__tot (G.format_edit_status__tot)
#define YDreg (G.YDreg )
#define Ureg (G.Ureg )
#define mark (G.mark )
#define context_start (G.context_start )
#define context_end (G.context_end )
#define restart (G.restart )
#define term_orig (G.term_orig )
#define term_vi (G.term_vi )
#define initial_cmds (G.initial_cmds )
#define readbuffer (G.readbuffer )
#define scr_out_buf (G.scr_out_buf )
#define last_modifying_cmd (G.last_modifying_cmd )
#define get_input_line__buf (G.get_input_line__buf)
#define INIT_G() do { \
SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
last_file_modified = -1; \
2008-10-29 21:16:28 +08:00
/* "" but has space for 2 chars: */ \
IF_FEATURE_VI_SEARCH(last_search_pattern = xzalloc(2);) \
} while (0)
vi: multiple fixes by Natanael Copa <natanael.copa@gmail.com> * the puzzling message on error is replaced with strerror(errno) so it should be even more detailed and smaller at the same time. * merged code in edit_file() and code for ':edit <file>' in colon() into new func init_text_buffer(). Was horribly duplicate. Moved most of error/sanity checking to file_insert(). Result is that you get a proper validation (prevent reading /dev/*) and error messages for ':r <file>' * renamed 'cfn' to 'current_filename' for improved readability * merged smallint vi_readonly and readonly into bitfields into readonly_mode to save space. * added text_size variable to keep track how big the text buffer is. This is used to fix a buffer overflow. To reproduce bug: ./busybox vi TODO :r Makefile vi segfaults due to no buffer checking is done at all. som redesign is needed here but i added a check in text_hole_make() to aviod the segfault at least. * removed isblnk() and use isblank(3) instead. * fixed compiler warning by displaying the return code for :!<command> This makes things bigger than needed but since the patch reduces the overall size... (see below) * new func next_tabstop(int) merges some duplicate code. There are more cuplicode here but i couldnt find a good way to merge them. * Fix *ANNOYING* placement of cursor on '\t' characters. To reproduce: echo -e "\thello" > file1 ./busybox vi file1 Try to insert some text at the beginning of line. Text will be inserted but cursor is blinking somewhere else. The patch should make busybox vi behave more like original vi(m). Costs a few bytes but its worth it imho. * new_text() is moved into init_text_buffer() * the previously added update_ro_status() was moved info file_insert due to duplication removal mentioned above. function old new delta init_text_buffer - 245 +245 file_insert 312 420 +108 next_tabstop - 82 +82 text_hole_make 154 171 +17 do_cmd 5093 5100 +7 static.cmd_mode_indicator - 5 +5 refresh 1248 1253 +5 current_filename - 4 +4 yank_delete 161 164 +3 what_reg 96 99 +3 end_cmd_q 78 81 +3 char_insert 440 442 +2 readonly_mode - 1 +1 vi_readonly 1 - -1 setops 154 153 -1 readonly 1 - -1 vi_setops 4 1 -3 string_insert 161 158 -3 cfn 4 - -4 show_status_line 532 514 -18 readit 519 500 -19 move_to_col 161 138 -23 vi_main 495 433 -62 isblnk 75 - -75 .rodata 4751 4655 -96 edit_file 892 787 -105 new_text 125 - -125 update_ro_status 131 - -131 colon 3848 3667 -181 ------------------------------------------------------------------------------ (add/remove: 5/6 grow/shrink: 8/10 up/down: 485/-848) Total: -363 bytes text data bss dec hex filename 34751 873 4260 39884 9bcc busybox_old 34439 877 4260 39576 9a98 busybox_unstripped
2007-07-18 07:14:07 +08:00
static int init_text_buffer(char *); // init from file or create new
static void edit_file(char *); // edit one file
static void do_cmd(int); // execute a command
vi: multiple fixes by Natanael Copa <natanael.copa@gmail.com> * the puzzling message on error is replaced with strerror(errno) so it should be even more detailed and smaller at the same time. * merged code in edit_file() and code for ':edit <file>' in colon() into new func init_text_buffer(). Was horribly duplicate. Moved most of error/sanity checking to file_insert(). Result is that you get a proper validation (prevent reading /dev/*) and error messages for ':r <file>' * renamed 'cfn' to 'current_filename' for improved readability * merged smallint vi_readonly and readonly into bitfields into readonly_mode to save space. * added text_size variable to keep track how big the text buffer is. This is used to fix a buffer overflow. To reproduce bug: ./busybox vi TODO :r Makefile vi segfaults due to no buffer checking is done at all. som redesign is needed here but i added a check in text_hole_make() to aviod the segfault at least. * removed isblnk() and use isblank(3) instead. * fixed compiler warning by displaying the return code for :!<command> This makes things bigger than needed but since the patch reduces the overall size... (see below) * new func next_tabstop(int) merges some duplicate code. There are more cuplicode here but i couldnt find a good way to merge them. * Fix *ANNOYING* placement of cursor on '\t' characters. To reproduce: echo -e "\thello" > file1 ./busybox vi file1 Try to insert some text at the beginning of line. Text will be inserted but cursor is blinking somewhere else. The patch should make busybox vi behave more like original vi(m). Costs a few bytes but its worth it imho. * new_text() is moved into init_text_buffer() * the previously added update_ro_status() was moved info file_insert due to duplication removal mentioned above. function old new delta init_text_buffer - 245 +245 file_insert 312 420 +108 next_tabstop - 82 +82 text_hole_make 154 171 +17 do_cmd 5093 5100 +7 static.cmd_mode_indicator - 5 +5 refresh 1248 1253 +5 current_filename - 4 +4 yank_delete 161 164 +3 what_reg 96 99 +3 end_cmd_q 78 81 +3 char_insert 440 442 +2 readonly_mode - 1 +1 vi_readonly 1 - -1 setops 154 153 -1 readonly 1 - -1 vi_setops 4 1 -3 string_insert 161 158 -3 cfn 4 - -4 show_status_line 532 514 -18 readit 519 500 -19 move_to_col 161 138 -23 vi_main 495 433 -62 isblnk 75 - -75 .rodata 4751 4655 -96 edit_file 892 787 -105 new_text 125 - -125 update_ro_status 131 - -131 colon 3848 3667 -181 ------------------------------------------------------------------------------ (add/remove: 5/6 grow/shrink: 8/10 up/down: 485/-848) Total: -363 bytes text data bss dec hex filename 34751 873 4260 39884 9bcc busybox_old 34439 877 4260 39576 9a98 busybox_unstripped
2007-07-18 07:14:07 +08:00
static int next_tabstop(int);
static void sync_cursor(char *, int *, int *); // synchronize the screen cursor to dot
static char *begin_line(char *); // return pointer to cur line B-o-l
static char *end_line(char *); // return pointer to cur line E-o-l
static char *prev_line(char *); // return pointer to prev line B-o-l
static char *next_line(char *); // return pointer to next line B-o-l
static char *end_screen(void); // get pointer to last char on screen
static int count_lines(char *, char *); // count line from start to stop
static char *find_line(int); // find begining of line #li
static char *move_to_col(char *, int); // move "p" to column l
static void dot_left(void); // move dot left- dont leave line
static void dot_right(void); // move dot right- dont leave line
static void dot_begin(void); // move dot to B-o-l
static void dot_end(void); // move dot to E-o-l
static void dot_next(void); // move dot to next line B-o-l
static void dot_prev(void); // move dot to prev line B-o-l
static void dot_scroll(int, int); // move the screen up or down
static void dot_skip_over_ws(void); // move dot pat WS
static void dot_delete(void); // delete the char at 'dot'
static char *bound_dot(char *); // make sure text[0] <= P < "end"
static char *new_screen(int, int); // malloc virtual screen memory
static char *char_insert(char *, char); // insert the char c at 'p'
// might reallocate text[]! use p += stupid_insert(p, ...),
// and be careful to not use pointers into potentially freed text[]!
static uintptr_t stupid_insert(char *, char); // stupidly insert the char c at 'p'
static int find_range(char **, char **, char); // return pointers for an object
static int st_test(char *, int, int, char *); // helper for skip_thing()
static char *skip_thing(char *, int, int, int); // skip some object
static char *find_pair(char *, char); // find matching pair () [] {}
static char *text_hole_delete(char *, char *); // at "p", delete a 'size' byte hole
// might reallocate text[]! use p += text_hole_make(p, ...),
// and be careful to not use pointers into potentially freed text[]!
static uintptr_t text_hole_make(char *, int); // at "p", make a 'size' byte hole
static char *yank_delete(char *, char *, int, int); // yank text[] into register then delete
static void show_help(void); // display some help info
static void rawmode(void); // set "raw" mode on tty
static void cookmode(void); // return to "cooked" mode on tty
// sleep for 'h' 1/100 seconds, return 1/0 if stdin is (ready for read)/(not ready)
static int mysleep(int);
static int readit(void); // read (maybe cursor) key from stdin
static int get_one_char(void); // read 1 char from stdin
static int file_size(const char *); // what is the byte size of "fn"
#if !ENABLE_FEATURE_VI_READONLY
#define file_insert(fn, p, update_ro_status) file_insert(fn, p)
#endif
// file_insert might reallocate text[]!
static int file_insert(const char *, char *, int);
static int file_write(char *, char *, char *);
#if !ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
#define place_cursor(a, b, optimize) place_cursor(a, b)
#endif
static void place_cursor(int, int, int);
static void screen_erase(void);
static void clear_to_eol(void);
static void clear_to_eos(void);
static void go_bottom_and_clear_to_eol(void);
static void standout_start(void); // send "start reverse video" sequence
static void standout_end(void); // send "end reverse video" sequence
static void flash(int); // flash the terminal screen
static void show_status_line(void); // put a message on the bottom line
static void status_line(const char *, ...); // print to status buf
static void status_line_bold(const char *, ...);
static void not_implemented(const char *); // display "Not implemented" message
static int format_edit_status(void); // format file status on status line
static void redraw(int); // force a full screen refresh
static char* format_line(char* /*, int*/);
static void refresh(int); // update the terminal from screen[]
static void Indicate_Error(void); // use flash or beep to indicate error
#define indicate_error(c) Indicate_Error()
static void Hit_Return(void);
#if ENABLE_FEATURE_VI_SEARCH
static char *char_search(char *, const char *, int, int); // search for pattern starting at p
#endif
#if ENABLE_FEATURE_VI_COLON
static char *get_one_address(char *, int *); // get colon addr, if present
static char *get_address(char *, int *, int *); // get two colon addrs, if present
static void colon(char *); // execute the "colon" mode cmds
#endif
#if ENABLE_FEATURE_VI_USE_SIGNALS
static void winch_sig(int); // catch window size changes
static void suspend_sig(int); // catch ctrl-Z
static void catch_sig(int); // catch ctrl-C and alarm time-outs
#endif
#if ENABLE_FEATURE_VI_DOT_CMD
static void start_new_cmd_q(char); // new queue for command
static void end_cmd_q(void); // stop saving input chars
#else
#define end_cmd_q() ((void)0)
#endif
#if ENABLE_FEATURE_VI_SETOPTS
static void showmatching(char *); // show the matching pair () [] {}
#endif
#if ENABLE_FEATURE_VI_YANKMARK || (ENABLE_FEATURE_VI_COLON && ENABLE_FEATURE_VI_SEARCH) || ENABLE_FEATURE_VI_CRASHME
// might reallocate text[]! use p += string_insert(p, ...),
// and be careful to not use pointers into potentially freed text[]!
static uintptr_t string_insert(char *, const char *); // insert the string at 'p'
#endif
#if ENABLE_FEATURE_VI_YANKMARK
static char *text_yank(char *, char *, int); // save copy of "p" into a register
static char what_reg(void); // what is letter of current YDreg
static void check_context(char); // remember context for '' command
#endif
#if ENABLE_FEATURE_VI_CRASHME
static void crash_dummy();
static void crash_test();
static int crashme = 0;
#endif
static void write1(const char *out)
{
fputs(out, stdout);
}
int vi_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int vi_main(int argc, char **argv)
{
2001-04-05 03:29:48 +08:00
int c;
INIT_G();
#if ENABLE_FEATURE_VI_USE_SIGNALS || ENABLE_FEATURE_VI_CRASHME
my_pid = getpid();
#endif
#if ENABLE_FEATURE_VI_CRASHME
srand((long) my_pid);
#endif
2007-07-19 06:03:40 +08:00
#ifdef NO_SUCH_APPLET_YET
/* If we aren't "vi", we are "view" */
if (ENABLE_FEATURE_VI_READONLY && applet_name[2]) {
vi: multiple fixes by Natanael Copa <natanael.copa@gmail.com> * the puzzling message on error is replaced with strerror(errno) so it should be even more detailed and smaller at the same time. * merged code in edit_file() and code for ':edit <file>' in colon() into new func init_text_buffer(). Was horribly duplicate. Moved most of error/sanity checking to file_insert(). Result is that you get a proper validation (prevent reading /dev/*) and error messages for ':r <file>' * renamed 'cfn' to 'current_filename' for improved readability * merged smallint vi_readonly and readonly into bitfields into readonly_mode to save space. * added text_size variable to keep track how big the text buffer is. This is used to fix a buffer overflow. To reproduce bug: ./busybox vi TODO :r Makefile vi segfaults due to no buffer checking is done at all. som redesign is needed here but i added a check in text_hole_make() to aviod the segfault at least. * removed isblnk() and use isblank(3) instead. * fixed compiler warning by displaying the return code for :!<command> This makes things bigger than needed but since the patch reduces the overall size... (see below) * new func next_tabstop(int) merges some duplicate code. There are more cuplicode here but i couldnt find a good way to merge them. * Fix *ANNOYING* placement of cursor on '\t' characters. To reproduce: echo -e "\thello" > file1 ./busybox vi file1 Try to insert some text at the beginning of line. Text will be inserted but cursor is blinking somewhere else. The patch should make busybox vi behave more like original vi(m). Costs a few bytes but its worth it imho. * new_text() is moved into init_text_buffer() * the previously added update_ro_status() was moved info file_insert due to duplication removal mentioned above. function old new delta init_text_buffer - 245 +245 file_insert 312 420 +108 next_tabstop - 82 +82 text_hole_make 154 171 +17 do_cmd 5093 5100 +7 static.cmd_mode_indicator - 5 +5 refresh 1248 1253 +5 current_filename - 4 +4 yank_delete 161 164 +3 what_reg 96 99 +3 end_cmd_q 78 81 +3 char_insert 440 442 +2 readonly_mode - 1 +1 vi_readonly 1 - -1 setops 154 153 -1 readonly 1 - -1 vi_setops 4 1 -3 string_insert 161 158 -3 cfn 4 - -4 show_status_line 532 514 -18 readit 519 500 -19 move_to_col 161 138 -23 vi_main 495 433 -62 isblnk 75 - -75 .rodata 4751 4655 -96 edit_file 892 787 -105 new_text 125 - -125 update_ro_status 131 - -131 colon 3848 3667 -181 ------------------------------------------------------------------------------ (add/remove: 5/6 grow/shrink: 8/10 up/down: 485/-848) Total: -363 bytes text data bss dec hex filename 34751 873 4260 39884 9bcc busybox_old 34439 877 4260 39576 9a98 busybox_unstripped
2007-07-18 07:14:07 +08:00
SET_READONLY_MODE(readonly_mode);
}
2007-07-19 06:03:40 +08:00
#endif
vi: multiple fixes by Natanael Copa <natanael.copa@gmail.com> * the puzzling message on error is replaced with strerror(errno) so it should be even more detailed and smaller at the same time. * merged code in edit_file() and code for ':edit <file>' in colon() into new func init_text_buffer(). Was horribly duplicate. Moved most of error/sanity checking to file_insert(). Result is that you get a proper validation (prevent reading /dev/*) and error messages for ':r <file>' * renamed 'cfn' to 'current_filename' for improved readability * merged smallint vi_readonly and readonly into bitfields into readonly_mode to save space. * added text_size variable to keep track how big the text buffer is. This is used to fix a buffer overflow. To reproduce bug: ./busybox vi TODO :r Makefile vi segfaults due to no buffer checking is done at all. som redesign is needed here but i added a check in text_hole_make() to aviod the segfault at least. * removed isblnk() and use isblank(3) instead. * fixed compiler warning by displaying the return code for :!<command> This makes things bigger than needed but since the patch reduces the overall size... (see below) * new func next_tabstop(int) merges some duplicate code. There are more cuplicode here but i couldnt find a good way to merge them. * Fix *ANNOYING* placement of cursor on '\t' characters. To reproduce: echo -e "\thello" > file1 ./busybox vi file1 Try to insert some text at the beginning of line. Text will be inserted but cursor is blinking somewhere else. The patch should make busybox vi behave more like original vi(m). Costs a few bytes but its worth it imho. * new_text() is moved into init_text_buffer() * the previously added update_ro_status() was moved info file_insert due to duplication removal mentioned above. function old new delta init_text_buffer - 245 +245 file_insert 312 420 +108 next_tabstop - 82 +82 text_hole_make 154 171 +17 do_cmd 5093 5100 +7 static.cmd_mode_indicator - 5 +5 refresh 1248 1253 +5 current_filename - 4 +4 yank_delete 161 164 +3 what_reg 96 99 +3 end_cmd_q 78 81 +3 char_insert 440 442 +2 readonly_mode - 1 +1 vi_readonly 1 - -1 setops 154 153 -1 readonly 1 - -1 vi_setops 4 1 -3 string_insert 161 158 -3 cfn 4 - -4 show_status_line 532 514 -18 readit 519 500 -19 move_to_col 161 138 -23 vi_main 495 433 -62 isblnk 75 - -75 .rodata 4751 4655 -96 edit_file 892 787 -105 new_text 125 - -125 update_ro_status 131 - -131 colon 3848 3667 -181 ------------------------------------------------------------------------------ (add/remove: 5/6 grow/shrink: 8/10 up/down: 485/-848) Total: -363 bytes text data bss dec hex filename 34751 873 4260 39884 9bcc busybox_old 34439 877 4260 39576 9a98 busybox_unstripped
2007-07-18 07:14:07 +08:00
2007-09-23 05:18:46 +08:00
vi_setops = VI_AUTOINDENT | VI_SHOWMATCH | VI_IGNORECASE;
2007-03-21 08:03:42 +08:00
// 1- process $HOME/.exrc file (not inplemented yet)
// 2- process EXINIT variable from environment
// 3- process command line args
#if ENABLE_FEATURE_VI_COLON
2007-03-21 08:03:42 +08:00
{
char *p = getenv("EXINIT");
if (p && *p)
initial_cmds[0] = xstrndup(p, MAX_INPUT_LEN);
2007-03-21 08:03:42 +08:00
}
#endif
while ((c = getopt(argc, argv, "hCRH" IF_FEATURE_VI_COLON("c:"))) != -1) {
switch (c) {
#if ENABLE_FEATURE_VI_CRASHME
case 'C':
crashme = 1;
break;
#endif
#if ENABLE_FEATURE_VI_READONLY
case 'R': // Read-only flag
vi: multiple fixes by Natanael Copa <natanael.copa@gmail.com> * the puzzling message on error is replaced with strerror(errno) so it should be even more detailed and smaller at the same time. * merged code in edit_file() and code for ':edit <file>' in colon() into new func init_text_buffer(). Was horribly duplicate. Moved most of error/sanity checking to file_insert(). Result is that you get a proper validation (prevent reading /dev/*) and error messages for ':r <file>' * renamed 'cfn' to 'current_filename' for improved readability * merged smallint vi_readonly and readonly into bitfields into readonly_mode to save space. * added text_size variable to keep track how big the text buffer is. This is used to fix a buffer overflow. To reproduce bug: ./busybox vi TODO :r Makefile vi segfaults due to no buffer checking is done at all. som redesign is needed here but i added a check in text_hole_make() to aviod the segfault at least. * removed isblnk() and use isblank(3) instead. * fixed compiler warning by displaying the return code for :!<command> This makes things bigger than needed but since the patch reduces the overall size... (see below) * new func next_tabstop(int) merges some duplicate code. There are more cuplicode here but i couldnt find a good way to merge them. * Fix *ANNOYING* placement of cursor on '\t' characters. To reproduce: echo -e "\thello" > file1 ./busybox vi file1 Try to insert some text at the beginning of line. Text will be inserted but cursor is blinking somewhere else. The patch should make busybox vi behave more like original vi(m). Costs a few bytes but its worth it imho. * new_text() is moved into init_text_buffer() * the previously added update_ro_status() was moved info file_insert due to duplication removal mentioned above. function old new delta init_text_buffer - 245 +245 file_insert 312 420 +108 next_tabstop - 82 +82 text_hole_make 154 171 +17 do_cmd 5093 5100 +7 static.cmd_mode_indicator - 5 +5 refresh 1248 1253 +5 current_filename - 4 +4 yank_delete 161 164 +3 what_reg 96 99 +3 end_cmd_q 78 81 +3 char_insert 440 442 +2 readonly_mode - 1 +1 vi_readonly 1 - -1 setops 154 153 -1 readonly 1 - -1 vi_setops 4 1 -3 string_insert 161 158 -3 cfn 4 - -4 show_status_line 532 514 -18 readit 519 500 -19 move_to_col 161 138 -23 vi_main 495 433 -62 isblnk 75 - -75 .rodata 4751 4655 -96 edit_file 892 787 -105 new_text 125 - -125 update_ro_status 131 - -131 colon 3848 3667 -181 ------------------------------------------------------------------------------ (add/remove: 5/6 grow/shrink: 8/10 up/down: 485/-848) Total: -363 bytes text data bss dec hex filename 34751 873 4260 39884 9bcc busybox_old 34439 877 4260 39576 9a98 busybox_unstripped
2007-07-18 07:14:07 +08:00
SET_READONLY_MODE(readonly_mode);
break;
#endif
#if ENABLE_FEATURE_VI_COLON
2007-03-21 08:03:42 +08:00
case 'c': // cmd line vi command
if (*optarg)
initial_cmds[initial_cmds[0] != 0] = xstrndup(optarg, MAX_INPUT_LEN);
2007-03-21 08:03:42 +08:00
break;
#endif
case 'H':
show_help();
/* fall through */
default:
2008-03-24 10:18:03 +08:00
bb_show_usage();
return 1;
}
}
// The argv array can be used by the ":next" and ":rewind" commands
// save optind.
fn_start = optind; // remember first file name for :next and :rew
save_argc = argc;
//----- This is the main file handling loop --------------
while (1) {
edit_file(argv[optind]); /* param might be NULL */
if (++optind >= argc)
break;
}
//-----------------------------------------------------------
return 0;
}
vi: multiple fixes by Natanael Copa <natanael.copa@gmail.com> * the puzzling message on error is replaced with strerror(errno) so it should be even more detailed and smaller at the same time. * merged code in edit_file() and code for ':edit <file>' in colon() into new func init_text_buffer(). Was horribly duplicate. Moved most of error/sanity checking to file_insert(). Result is that you get a proper validation (prevent reading /dev/*) and error messages for ':r <file>' * renamed 'cfn' to 'current_filename' for improved readability * merged smallint vi_readonly and readonly into bitfields into readonly_mode to save space. * added text_size variable to keep track how big the text buffer is. This is used to fix a buffer overflow. To reproduce bug: ./busybox vi TODO :r Makefile vi segfaults due to no buffer checking is done at all. som redesign is needed here but i added a check in text_hole_make() to aviod the segfault at least. * removed isblnk() and use isblank(3) instead. * fixed compiler warning by displaying the return code for :!<command> This makes things bigger than needed but since the patch reduces the overall size... (see below) * new func next_tabstop(int) merges some duplicate code. There are more cuplicode here but i couldnt find a good way to merge them. * Fix *ANNOYING* placement of cursor on '\t' characters. To reproduce: echo -e "\thello" > file1 ./busybox vi file1 Try to insert some text at the beginning of line. Text will be inserted but cursor is blinking somewhere else. The patch should make busybox vi behave more like original vi(m). Costs a few bytes but its worth it imho. * new_text() is moved into init_text_buffer() * the previously added update_ro_status() was moved info file_insert due to duplication removal mentioned above. function old new delta init_text_buffer - 245 +245 file_insert 312 420 +108 next_tabstop - 82 +82 text_hole_make 154 171 +17 do_cmd 5093 5100 +7 static.cmd_mode_indicator - 5 +5 refresh 1248 1253 +5 current_filename - 4 +4 yank_delete 161 164 +3 what_reg 96 99 +3 end_cmd_q 78 81 +3 char_insert 440 442 +2 readonly_mode - 1 +1 vi_readonly 1 - -1 setops 154 153 -1 readonly 1 - -1 vi_setops 4 1 -3 string_insert 161 158 -3 cfn 4 - -4 show_status_line 532 514 -18 readit 519 500 -19 move_to_col 161 138 -23 vi_main 495 433 -62 isblnk 75 - -75 .rodata 4751 4655 -96 edit_file 892 787 -105 new_text 125 - -125 update_ro_status 131 - -131 colon 3848 3667 -181 ------------------------------------------------------------------------------ (add/remove: 5/6 grow/shrink: 8/10 up/down: 485/-848) Total: -363 bytes text data bss dec hex filename 34751 873 4260 39884 9bcc busybox_old 34439 877 4260 39576 9a98 busybox_unstripped
2007-07-18 07:14:07 +08:00
/* read text from file or create an empty buf */
/* will also update current_filename */
static int init_text_buffer(char *fn)
{
int rc;
int size = file_size(fn); // file size. -1 means does not exist.
/* allocate/reallocate text buffer */
free(text);
text_size = size + 10240;
vi: multiple fixes by Natanael Copa <natanael.copa@gmail.com> * the puzzling message on error is replaced with strerror(errno) so it should be even more detailed and smaller at the same time. * merged code in edit_file() and code for ':edit <file>' in colon() into new func init_text_buffer(). Was horribly duplicate. Moved most of error/sanity checking to file_insert(). Result is that you get a proper validation (prevent reading /dev/*) and error messages for ':r <file>' * renamed 'cfn' to 'current_filename' for improved readability * merged smallint vi_readonly and readonly into bitfields into readonly_mode to save space. * added text_size variable to keep track how big the text buffer is. This is used to fix a buffer overflow. To reproduce bug: ./busybox vi TODO :r Makefile vi segfaults due to no buffer checking is done at all. som redesign is needed here but i added a check in text_hole_make() to aviod the segfault at least. * removed isblnk() and use isblank(3) instead. * fixed compiler warning by displaying the return code for :!<command> This makes things bigger than needed but since the patch reduces the overall size... (see below) * new func next_tabstop(int) merges some duplicate code. There are more cuplicode here but i couldnt find a good way to merge them. * Fix *ANNOYING* placement of cursor on '\t' characters. To reproduce: echo -e "\thello" > file1 ./busybox vi file1 Try to insert some text at the beginning of line. Text will be inserted but cursor is blinking somewhere else. The patch should make busybox vi behave more like original vi(m). Costs a few bytes but its worth it imho. * new_text() is moved into init_text_buffer() * the previously added update_ro_status() was moved info file_insert due to duplication removal mentioned above. function old new delta init_text_buffer - 245 +245 file_insert 312 420 +108 next_tabstop - 82 +82 text_hole_make 154 171 +17 do_cmd 5093 5100 +7 static.cmd_mode_indicator - 5 +5 refresh 1248 1253 +5 current_filename - 4 +4 yank_delete 161 164 +3 what_reg 96 99 +3 end_cmd_q 78 81 +3 char_insert 440 442 +2 readonly_mode - 1 +1 vi_readonly 1 - -1 setops 154 153 -1 readonly 1 - -1 vi_setops 4 1 -3 string_insert 161 158 -3 cfn 4 - -4 show_status_line 532 514 -18 readit 519 500 -19 move_to_col 161 138 -23 vi_main 495 433 -62 isblnk 75 - -75 .rodata 4751 4655 -96 edit_file 892 787 -105 new_text 125 - -125 update_ro_status 131 - -131 colon 3848 3667 -181 ------------------------------------------------------------------------------ (add/remove: 5/6 grow/shrink: 8/10 up/down: 485/-848) Total: -363 bytes text data bss dec hex filename 34751 873 4260 39884 9bcc busybox_old 34439 877 4260 39576 9a98 busybox_unstripped
2007-07-18 07:14:07 +08:00
screenbegin = dot = end = text = xzalloc(text_size);
2007-07-20 06:50:47 +08:00
vi: multiple fixes by Natanael Copa <natanael.copa@gmail.com> * the puzzling message on error is replaced with strerror(errno) so it should be even more detailed and smaller at the same time. * merged code in edit_file() and code for ':edit <file>' in colon() into new func init_text_buffer(). Was horribly duplicate. Moved most of error/sanity checking to file_insert(). Result is that you get a proper validation (prevent reading /dev/*) and error messages for ':r <file>' * renamed 'cfn' to 'current_filename' for improved readability * merged smallint vi_readonly and readonly into bitfields into readonly_mode to save space. * added text_size variable to keep track how big the text buffer is. This is used to fix a buffer overflow. To reproduce bug: ./busybox vi TODO :r Makefile vi segfaults due to no buffer checking is done at all. som redesign is needed here but i added a check in text_hole_make() to aviod the segfault at least. * removed isblnk() and use isblank(3) instead. * fixed compiler warning by displaying the return code for :!<command> This makes things bigger than needed but since the patch reduces the overall size... (see below) * new func next_tabstop(int) merges some duplicate code. There are more cuplicode here but i couldnt find a good way to merge them. * Fix *ANNOYING* placement of cursor on '\t' characters. To reproduce: echo -e "\thello" > file1 ./busybox vi file1 Try to insert some text at the beginning of line. Text will be inserted but cursor is blinking somewhere else. The patch should make busybox vi behave more like original vi(m). Costs a few bytes but its worth it imho. * new_text() is moved into init_text_buffer() * the previously added update_ro_status() was moved info file_insert due to duplication removal mentioned above. function old new delta init_text_buffer - 245 +245 file_insert 312 420 +108 next_tabstop - 82 +82 text_hole_make 154 171 +17 do_cmd 5093 5100 +7 static.cmd_mode_indicator - 5 +5 refresh 1248 1253 +5 current_filename - 4 +4 yank_delete 161 164 +3 what_reg 96 99 +3 end_cmd_q 78 81 +3 char_insert 440 442 +2 readonly_mode - 1 +1 vi_readonly 1 - -1 setops 154 153 -1 readonly 1 - -1 vi_setops 4 1 -3 string_insert 161 158 -3 cfn 4 - -4 show_status_line 532 514 -18 readit 519 500 -19 move_to_col 161 138 -23 vi_main 495 433 -62 isblnk 75 - -75 .rodata 4751 4655 -96 edit_file 892 787 -105 new_text 125 - -125 update_ro_status 131 - -131 colon 3848 3667 -181 ------------------------------------------------------------------------------ (add/remove: 5/6 grow/shrink: 8/10 up/down: 485/-848) Total: -363 bytes text data bss dec hex filename 34751 873 4260 39884 9bcc busybox_old 34439 877 4260 39576 9a98 busybox_unstripped
2007-07-18 07:14:07 +08:00
if (fn != current_filename) {
free(current_filename);
current_filename = xstrdup(fn);
}
if (size < 0) {
// file dont exist. Start empty buf with dummy line
char_insert(text, '\n');
rc = 0;
} else {
rc = file_insert(fn, text, 1);
vi: multiple fixes by Natanael Copa <natanael.copa@gmail.com> * the puzzling message on error is replaced with strerror(errno) so it should be even more detailed and smaller at the same time. * merged code in edit_file() and code for ':edit <file>' in colon() into new func init_text_buffer(). Was horribly duplicate. Moved most of error/sanity checking to file_insert(). Result is that you get a proper validation (prevent reading /dev/*) and error messages for ':r <file>' * renamed 'cfn' to 'current_filename' for improved readability * merged smallint vi_readonly and readonly into bitfields into readonly_mode to save space. * added text_size variable to keep track how big the text buffer is. This is used to fix a buffer overflow. To reproduce bug: ./busybox vi TODO :r Makefile vi segfaults due to no buffer checking is done at all. som redesign is needed here but i added a check in text_hole_make() to aviod the segfault at least. * removed isblnk() and use isblank(3) instead. * fixed compiler warning by displaying the return code for :!<command> This makes things bigger than needed but since the patch reduces the overall size... (see below) * new func next_tabstop(int) merges some duplicate code. There are more cuplicode here but i couldnt find a good way to merge them. * Fix *ANNOYING* placement of cursor on '\t' characters. To reproduce: echo -e "\thello" > file1 ./busybox vi file1 Try to insert some text at the beginning of line. Text will be inserted but cursor is blinking somewhere else. The patch should make busybox vi behave more like original vi(m). Costs a few bytes but its worth it imho. * new_text() is moved into init_text_buffer() * the previously added update_ro_status() was moved info file_insert due to duplication removal mentioned above. function old new delta init_text_buffer - 245 +245 file_insert 312 420 +108 next_tabstop - 82 +82 text_hole_make 154 171 +17 do_cmd 5093 5100 +7 static.cmd_mode_indicator - 5 +5 refresh 1248 1253 +5 current_filename - 4 +4 yank_delete 161 164 +3 what_reg 96 99 +3 end_cmd_q 78 81 +3 char_insert 440 442 +2 readonly_mode - 1 +1 vi_readonly 1 - -1 setops 154 153 -1 readonly 1 - -1 vi_setops 4 1 -3 string_insert 161 158 -3 cfn 4 - -4 show_status_line 532 514 -18 readit 519 500 -19 move_to_col 161 138 -23 vi_main 495 433 -62 isblnk 75 - -75 .rodata 4751 4655 -96 edit_file 892 787 -105 new_text 125 - -125 update_ro_status 131 - -131 colon 3848 3667 -181 ------------------------------------------------------------------------------ (add/remove: 5/6 grow/shrink: 8/10 up/down: 485/-848) Total: -363 bytes text data bss dec hex filename 34751 873 4260 39884 9bcc busybox_old 34439 877 4260 39576 9a98 busybox_unstripped
2007-07-18 07:14:07 +08:00
}
file_modified = 0;
last_file_modified = -1;
#if ENABLE_FEATURE_VI_YANKMARK
/* init the marks. */
memset(mark, 0, sizeof(mark));
#endif
return rc;
2007-07-20 06:50:47 +08:00
}
vi: multiple fixes by Natanael Copa <natanael.copa@gmail.com> * the puzzling message on error is replaced with strerror(errno) so it should be even more detailed and smaller at the same time. * merged code in edit_file() and code for ':edit <file>' in colon() into new func init_text_buffer(). Was horribly duplicate. Moved most of error/sanity checking to file_insert(). Result is that you get a proper validation (prevent reading /dev/*) and error messages for ':r <file>' * renamed 'cfn' to 'current_filename' for improved readability * merged smallint vi_readonly and readonly into bitfields into readonly_mode to save space. * added text_size variable to keep track how big the text buffer is. This is used to fix a buffer overflow. To reproduce bug: ./busybox vi TODO :r Makefile vi segfaults due to no buffer checking is done at all. som redesign is needed here but i added a check in text_hole_make() to aviod the segfault at least. * removed isblnk() and use isblank(3) instead. * fixed compiler warning by displaying the return code for :!<command> This makes things bigger than needed but since the patch reduces the overall size... (see below) * new func next_tabstop(int) merges some duplicate code. There are more cuplicode here but i couldnt find a good way to merge them. * Fix *ANNOYING* placement of cursor on '\t' characters. To reproduce: echo -e "\thello" > file1 ./busybox vi file1 Try to insert some text at the beginning of line. Text will be inserted but cursor is blinking somewhere else. The patch should make busybox vi behave more like original vi(m). Costs a few bytes but its worth it imho. * new_text() is moved into init_text_buffer() * the previously added update_ro_status() was moved info file_insert due to duplication removal mentioned above. function old new delta init_text_buffer - 245 +245 file_insert 312 420 +108 next_tabstop - 82 +82 text_hole_make 154 171 +17 do_cmd 5093 5100 +7 static.cmd_mode_indicator - 5 +5 refresh 1248 1253 +5 current_filename - 4 +4 yank_delete 161 164 +3 what_reg 96 99 +3 end_cmd_q 78 81 +3 char_insert 440 442 +2 readonly_mode - 1 +1 vi_readonly 1 - -1 setops 154 153 -1 readonly 1 - -1 vi_setops 4 1 -3 string_insert 161 158 -3 cfn 4 - -4 show_status_line 532 514 -18 readit 519 500 -19 move_to_col 161 138 -23 vi_main 495 433 -62 isblnk 75 - -75 .rodata 4751 4655 -96 edit_file 892 787 -105 new_text 125 - -125 update_ro_status 131 - -131 colon 3848 3667 -181 ------------------------------------------------------------------------------ (add/remove: 5/6 grow/shrink: 8/10 up/down: 485/-848) Total: -363 bytes text data bss dec hex filename 34751 873 4260 39884 9bcc busybox_old 34439 877 4260 39576 9a98 busybox_unstripped
2007-07-18 07:14:07 +08:00
#if ENABLE_FEATURE_VI_WIN_RESIZE
static int query_screen_dimensions(void)
{
int err = get_terminal_width_height(STDIN_FILENO, &columns, &rows);
if (rows > MAX_SCR_ROWS)
rows = MAX_SCR_ROWS;
if (columns > MAX_SCR_COLS)
columns = MAX_SCR_COLS;
return err;
}
#else
# define query_screen_dimensions() (0)
#endif
static void edit_file(char *fn)
{
#if ENABLE_FEATURE_VI_YANKMARK
#define cur_line edit_file__cur_line
#endif
int c;
#if ENABLE_FEATURE_VI_USE_SIGNALS
int sig;
#endif
editing = 1; // 0 = exit, 1 = one file, 2 = multiple files
rawmode();
rows = 24;
columns = 80;
IF_FEATURE_VI_ASK_TERMINAL(G.get_rowcol_error =) query_screen_dimensions();
#if ENABLE_FEATURE_VI_ASK_TERMINAL
if (G.get_rowcol_error /* TODO? && no input on stdin */) {
uint64_t k;
write1("\033[999;999H" "\033[6n");
fflush_all();
k = read_key(STDIN_FILENO, readbuffer, /*timeout_ms:*/ 100);
if ((int32_t)k == KEYCODE_CURSOR_POS) {
uint32_t rc = (k >> 32);
columns = (rc & 0x7fff);
if (columns > MAX_SCR_COLS)
columns = MAX_SCR_COLS;
rows = ((rc >> 16) & 0x7fff);
if (rows > MAX_SCR_ROWS)
rows = MAX_SCR_ROWS;
}
}
#endif
new_screen(rows, columns); // get memory for virtual screen
vi: multiple fixes by Natanael Copa <natanael.copa@gmail.com> * the puzzling message on error is replaced with strerror(errno) so it should be even more detailed and smaller at the same time. * merged code in edit_file() and code for ':edit <file>' in colon() into new func init_text_buffer(). Was horribly duplicate. Moved most of error/sanity checking to file_insert(). Result is that you get a proper validation (prevent reading /dev/*) and error messages for ':r <file>' * renamed 'cfn' to 'current_filename' for improved readability * merged smallint vi_readonly and readonly into bitfields into readonly_mode to save space. * added text_size variable to keep track how big the text buffer is. This is used to fix a buffer overflow. To reproduce bug: ./busybox vi TODO :r Makefile vi segfaults due to no buffer checking is done at all. som redesign is needed here but i added a check in text_hole_make() to aviod the segfault at least. * removed isblnk() and use isblank(3) instead. * fixed compiler warning by displaying the return code for :!<command> This makes things bigger than needed but since the patch reduces the overall size... (see below) * new func next_tabstop(int) merges some duplicate code. There are more cuplicode here but i couldnt find a good way to merge them. * Fix *ANNOYING* placement of cursor on '\t' characters. To reproduce: echo -e "\thello" > file1 ./busybox vi file1 Try to insert some text at the beginning of line. Text will be inserted but cursor is blinking somewhere else. The patch should make busybox vi behave more like original vi(m). Costs a few bytes but its worth it imho. * new_text() is moved into init_text_buffer() * the previously added update_ro_status() was moved info file_insert due to duplication removal mentioned above. function old new delta init_text_buffer - 245 +245 file_insert 312 420 +108 next_tabstop - 82 +82 text_hole_make 154 171 +17 do_cmd 5093 5100 +7 static.cmd_mode_indicator - 5 +5 refresh 1248 1253 +5 current_filename - 4 +4 yank_delete 161 164 +3 what_reg 96 99 +3 end_cmd_q 78 81 +3 char_insert 440 442 +2 readonly_mode - 1 +1 vi_readonly 1 - -1 setops 154 153 -1 readonly 1 - -1 vi_setops 4 1 -3 string_insert 161 158 -3 cfn 4 - -4 show_status_line 532 514 -18 readit 519 500 -19 move_to_col 161 138 -23 vi_main 495 433 -62 isblnk 75 - -75 .rodata 4751 4655 -96 edit_file 892 787 -105 new_text 125 - -125 update_ro_status 131 - -131 colon 3848 3667 -181 ------------------------------------------------------------------------------ (add/remove: 5/6 grow/shrink: 8/10 up/down: 485/-848) Total: -363 bytes text data bss dec hex filename 34751 873 4260 39884 9bcc busybox_old 34439 877 4260 39576 9a98 busybox_unstripped
2007-07-18 07:14:07 +08:00
init_text_buffer(fn);
#if ENABLE_FEATURE_VI_YANKMARK
YDreg = 26; // default Yank/Delete reg
Ureg = 27; // hold orig line for "U" cmd
mark[26] = mark[27] = text; // init "previous context"
#endif
last_forward_char = last_input_char = '\0';
crow = 0;
ccol = 0;
#if ENABLE_FEATURE_VI_USE_SIGNALS
signal(SIGINT, catch_sig);
signal(SIGWINCH, winch_sig);
signal(SIGTSTP, suspend_sig);
sig = sigsetjmp(restart, 1);
if (sig != 0) {
screenbegin = dot = text;
}
#endif
cmd_mode = 0; // 0=command 1=insert 2='R'eplace
cmdcnt = 0;
tabstop = 8;
offset = 0; // no horizontal offset
c = '\0';
#if ENABLE_FEATURE_VI_DOT_CMD
2002-11-28 19:27:31 +08:00
free(ioq_start);
ioq = ioq_start = NULL;
lmc_len = 0;
adding2q = 0;
#endif
#if ENABLE_FEATURE_VI_COLON
2007-03-21 08:03:42 +08:00
{
char *p, *q;
int n = 0;
while ((p = initial_cmds[n]) != NULL) {
2007-03-21 08:03:42 +08:00
do {
q = p;
p = strchr(q, '\n');
2007-03-21 08:03:42 +08:00
if (p)
2007-04-12 08:32:05 +08:00
while (*p == '\n')
2007-03-21 08:03:42 +08:00
*p++ = '\0';
if (*q)
colon(q);
} while (p);
free(initial_cmds[n]);
initial_cmds[n] = NULL;
n++;
}
}
#endif
redraw(FALSE); // dont force every col re-draw
//------This is the main Vi cmd handling loop -----------------------
while (editing > 0) {
#if ENABLE_FEATURE_VI_CRASHME
if (crashme > 0) {
if ((end - text) > 1) {
crash_dummy(); // generate a random command
} else {
crashme = 0;
string_insert(text, "\n\n##### Ran out of text to work on. #####\n\n"); // insert the string
dot = text;
refresh(FALSE);
}
}
#endif
last_input_char = c = get_one_char(); // get a cmd from user
#if ENABLE_FEATURE_VI_YANKMARK
// save a copy of the current line- for the 'U" command
if (begin_line(dot) != cur_line) {
cur_line = begin_line(dot);
text_yank(begin_line(dot), end_line(dot), Ureg);
}
#endif
#if ENABLE_FEATURE_VI_DOT_CMD
// These are commands that change text[].
// Remember the input for the "." command
if (!adding2q && ioq_start == NULL
&& cmd_mode == 0 // command mode
&& c > '\0' // exclude NUL and non-ASCII chars
&& c < 0x7f // (Unicode and such)
&& strchr(modifying_cmds, c)
) {
start_new_cmd_q(c);
}
#endif
do_cmd(c); // execute the user command
// poll to see if there is input already waiting. if we are
// not able to display output fast enough to keep up, skip
// the display update until we catch up with input.
if (!readbuffer[0] && mysleep(0) == 0) {
// no input pending - so update output
refresh(FALSE);
show_status_line();
}
#if ENABLE_FEATURE_VI_CRASHME
if (crashme > 0)
crash_test(); // test editor variables
#endif
}
//-------------------------------------------------------------------
go_bottom_and_clear_to_eol();
cookmode();
#undef cur_line
}
2002-08-21 21:02:24 +08:00
//----- The Colon commands -------------------------------------
#if ENABLE_FEATURE_VI_COLON
static char *get_one_address(char *p, int *addr) // get colon addr, if present
{
2002-08-21 21:02:24 +08:00
int st;
char *q;
IF_FEATURE_VI_YANKMARK(char c;)
IF_FEATURE_VI_SEARCH(char *pat;)
2002-08-21 21:02:24 +08:00
*addr = -1; // assume no addr
if (*p == '.') { // the current line
p++;
q = begin_line(dot);
*addr = count_lines(text, q);
}
#if ENABLE_FEATURE_VI_YANKMARK
else if (*p == '\'') { // is this a mark addr
2002-08-21 21:02:24 +08:00
p++;
c = tolower(*p);
p++;
if (c >= 'a' && c <= 'z') {
// we have a mark
c = c - 'a';
q = mark[(unsigned char) c];
2002-08-21 21:02:24 +08:00
if (q != NULL) { // is mark valid
*addr = count_lines(text, q);
}
}
}
#endif
#if ENABLE_FEATURE_VI_SEARCH
else if (*p == '/') { // a search pattern
q = strchrnul(++p, '/');
pat = xstrndup(p, q - p); // save copy of pattern
p = q;
2002-08-21 21:02:24 +08:00
if (*p == '/')
p++;
q = char_search(dot, pat, FORWARD, FULL);
if (q != NULL) {
*addr = count_lines(text, q);
}
free(pat);
}
#endif
else if (*p == '$') { // the last line in file
2002-08-21 21:02:24 +08:00
p++;
q = begin_line(end - 1);
*addr = count_lines(text, q);
} else if (isdigit(*p)) { // specific line number
sscanf(p, "%d%n", addr, &st);
2002-08-21 21:02:24 +08:00
p += st;
} else {
// unrecognized address - assume -1
2002-08-21 21:02:24 +08:00
*addr = -1;
}
return p;
2002-08-21 21:02:24 +08:00
}
static char *get_address(char *p, int *b, int *e) // get two colon addrs, if present
2002-08-21 21:02:24 +08:00
{
//----- get the address' i.e., 1,3 'a,'b -----
// get FIRST addr, if present
vi: multiple fixes by Natanael Copa <natanael.copa@gmail.com> * the puzzling message on error is replaced with strerror(errno) so it should be even more detailed and smaller at the same time. * merged code in edit_file() and code for ':edit <file>' in colon() into new func init_text_buffer(). Was horribly duplicate. Moved most of error/sanity checking to file_insert(). Result is that you get a proper validation (prevent reading /dev/*) and error messages for ':r <file>' * renamed 'cfn' to 'current_filename' for improved readability * merged smallint vi_readonly and readonly into bitfields into readonly_mode to save space. * added text_size variable to keep track how big the text buffer is. This is used to fix a buffer overflow. To reproduce bug: ./busybox vi TODO :r Makefile vi segfaults due to no buffer checking is done at all. som redesign is needed here but i added a check in text_hole_make() to aviod the segfault at least. * removed isblnk() and use isblank(3) instead. * fixed compiler warning by displaying the return code for :!<command> This makes things bigger than needed but since the patch reduces the overall size... (see below) * new func next_tabstop(int) merges some duplicate code. There are more cuplicode here but i couldnt find a good way to merge them. * Fix *ANNOYING* placement of cursor on '\t' characters. To reproduce: echo -e "\thello" > file1 ./busybox vi file1 Try to insert some text at the beginning of line. Text will be inserted but cursor is blinking somewhere else. The patch should make busybox vi behave more like original vi(m). Costs a few bytes but its worth it imho. * new_text() is moved into init_text_buffer() * the previously added update_ro_status() was moved info file_insert due to duplication removal mentioned above. function old new delta init_text_buffer - 245 +245 file_insert 312 420 +108 next_tabstop - 82 +82 text_hole_make 154 171 +17 do_cmd 5093 5100 +7 static.cmd_mode_indicator - 5 +5 refresh 1248 1253 +5 current_filename - 4 +4 yank_delete 161 164 +3 what_reg 96 99 +3 end_cmd_q 78 81 +3 char_insert 440 442 +2 readonly_mode - 1 +1 vi_readonly 1 - -1 setops 154 153 -1 readonly 1 - -1 vi_setops 4 1 -3 string_insert 161 158 -3 cfn 4 - -4 show_status_line 532 514 -18 readit 519 500 -19 move_to_col 161 138 -23 vi_main 495 433 -62 isblnk 75 - -75 .rodata 4751 4655 -96 edit_file 892 787 -105 new_text 125 - -125 update_ro_status 131 - -131 colon 3848 3667 -181 ------------------------------------------------------------------------------ (add/remove: 5/6 grow/shrink: 8/10 up/down: 485/-848) Total: -363 bytes text data bss dec hex filename 34751 873 4260 39884 9bcc busybox_old 34439 877 4260 39576 9a98 busybox_unstripped
2007-07-18 07:14:07 +08:00
while (isblank(*p))
2002-08-21 21:02:24 +08:00
p++; // skip over leading spaces
if (*p == '%') { // alias for 1,$
p++;
*b = 1;
*e = count_lines(text, end-1);
goto ga0;
}
p = get_one_address(p, b);
vi: multiple fixes by Natanael Copa <natanael.copa@gmail.com> * the puzzling message on error is replaced with strerror(errno) so it should be even more detailed and smaller at the same time. * merged code in edit_file() and code for ':edit <file>' in colon() into new func init_text_buffer(). Was horribly duplicate. Moved most of error/sanity checking to file_insert(). Result is that you get a proper validation (prevent reading /dev/*) and error messages for ':r <file>' * renamed 'cfn' to 'current_filename' for improved readability * merged smallint vi_readonly and readonly into bitfields into readonly_mode to save space. * added text_size variable to keep track how big the text buffer is. This is used to fix a buffer overflow. To reproduce bug: ./busybox vi TODO :r Makefile vi segfaults due to no buffer checking is done at all. som redesign is needed here but i added a check in text_hole_make() to aviod the segfault at least. * removed isblnk() and use isblank(3) instead. * fixed compiler warning by displaying the return code for :!<command> This makes things bigger than needed but since the patch reduces the overall size... (see below) * new func next_tabstop(int) merges some duplicate code. There are more cuplicode here but i couldnt find a good way to merge them. * Fix *ANNOYING* placement of cursor on '\t' characters. To reproduce: echo -e "\thello" > file1 ./busybox vi file1 Try to insert some text at the beginning of line. Text will be inserted but cursor is blinking somewhere else. The patch should make busybox vi behave more like original vi(m). Costs a few bytes but its worth it imho. * new_text() is moved into init_text_buffer() * the previously added update_ro_status() was moved info file_insert due to duplication removal mentioned above. function old new delta init_text_buffer - 245 +245 file_insert 312 420 +108 next_tabstop - 82 +82 text_hole_make 154 171 +17 do_cmd 5093 5100 +7 static.cmd_mode_indicator - 5 +5 refresh 1248 1253 +5 current_filename - 4 +4 yank_delete 161 164 +3 what_reg 96 99 +3 end_cmd_q 78 81 +3 char_insert 440 442 +2 readonly_mode - 1 +1 vi_readonly 1 - -1 setops 154 153 -1 readonly 1 - -1 vi_setops 4 1 -3 string_insert 161 158 -3 cfn 4 - -4 show_status_line 532 514 -18 readit 519 500 -19 move_to_col 161 138 -23 vi_main 495 433 -62 isblnk 75 - -75 .rodata 4751 4655 -96 edit_file 892 787 -105 new_text 125 - -125 update_ro_status 131 - -131 colon 3848 3667 -181 ------------------------------------------------------------------------------ (add/remove: 5/6 grow/shrink: 8/10 up/down: 485/-848) Total: -363 bytes text data bss dec hex filename 34751 873 4260 39884 9bcc busybox_old 34439 877 4260 39576 9a98 busybox_unstripped
2007-07-18 07:14:07 +08:00
while (isblank(*p))
2002-08-21 21:02:24 +08:00
p++;
if (*p == ',') { // is there a address separator
2002-08-21 21:02:24 +08:00
p++;
vi: multiple fixes by Natanael Copa <natanael.copa@gmail.com> * the puzzling message on error is replaced with strerror(errno) so it should be even more detailed and smaller at the same time. * merged code in edit_file() and code for ':edit <file>' in colon() into new func init_text_buffer(). Was horribly duplicate. Moved most of error/sanity checking to file_insert(). Result is that you get a proper validation (prevent reading /dev/*) and error messages for ':r <file>' * renamed 'cfn' to 'current_filename' for improved readability * merged smallint vi_readonly and readonly into bitfields into readonly_mode to save space. * added text_size variable to keep track how big the text buffer is. This is used to fix a buffer overflow. To reproduce bug: ./busybox vi TODO :r Makefile vi segfaults due to no buffer checking is done at all. som redesign is needed here but i added a check in text_hole_make() to aviod the segfault at least. * removed isblnk() and use isblank(3) instead. * fixed compiler warning by displaying the return code for :!<command> This makes things bigger than needed but since the patch reduces the overall size... (see below) * new func next_tabstop(int) merges some duplicate code. There are more cuplicode here but i couldnt find a good way to merge them. * Fix *ANNOYING* placement of cursor on '\t' characters. To reproduce: echo -e "\thello" > file1 ./busybox vi file1 Try to insert some text at the beginning of line. Text will be inserted but cursor is blinking somewhere else. The patch should make busybox vi behave more like original vi(m). Costs a few bytes but its worth it imho. * new_text() is moved into init_text_buffer() * the previously added update_ro_status() was moved info file_insert due to duplication removal mentioned above. function old new delta init_text_buffer - 245 +245 file_insert 312 420 +108 next_tabstop - 82 +82 text_hole_make 154 171 +17 do_cmd 5093 5100 +7 static.cmd_mode_indicator - 5 +5 refresh 1248 1253 +5 current_filename - 4 +4 yank_delete 161 164 +3 what_reg 96 99 +3 end_cmd_q 78 81 +3 char_insert 440 442 +2 readonly_mode - 1 +1 vi_readonly 1 - -1 setops 154 153 -1 readonly 1 - -1 vi_setops 4 1 -3 string_insert 161 158 -3 cfn 4 - -4 show_status_line 532 514 -18 readit 519 500 -19 move_to_col 161 138 -23 vi_main 495 433 -62 isblnk 75 - -75 .rodata 4751 4655 -96 edit_file 892 787 -105 new_text 125 - -125 update_ro_status 131 - -131 colon 3848 3667 -181 ------------------------------------------------------------------------------ (add/remove: 5/6 grow/shrink: 8/10 up/down: 485/-848) Total: -363 bytes text data bss dec hex filename 34751 873 4260 39884 9bcc busybox_old 34439 877 4260 39576 9a98 busybox_unstripped
2007-07-18 07:14:07 +08:00
while (isblank(*p))
2002-08-21 21:02:24 +08:00
p++;
// get SECOND addr, if present
p = get_one_address(p, e);
}
ga0:
vi: multiple fixes by Natanael Copa <natanael.copa@gmail.com> * the puzzling message on error is replaced with strerror(errno) so it should be even more detailed and smaller at the same time. * merged code in edit_file() and code for ':edit <file>' in colon() into new func init_text_buffer(). Was horribly duplicate. Moved most of error/sanity checking to file_insert(). Result is that you get a proper validation (prevent reading /dev/*) and error messages for ':r <file>' * renamed 'cfn' to 'current_filename' for improved readability * merged smallint vi_readonly and readonly into bitfields into readonly_mode to save space. * added text_size variable to keep track how big the text buffer is. This is used to fix a buffer overflow. To reproduce bug: ./busybox vi TODO :r Makefile vi segfaults due to no buffer checking is done at all. som redesign is needed here but i added a check in text_hole_make() to aviod the segfault at least. * removed isblnk() and use isblank(3) instead. * fixed compiler warning by displaying the return code for :!<command> This makes things bigger than needed but since the patch reduces the overall size... (see below) * new func next_tabstop(int) merges some duplicate code. There are more cuplicode here but i couldnt find a good way to merge them. * Fix *ANNOYING* placement of cursor on '\t' characters. To reproduce: echo -e "\thello" > file1 ./busybox vi file1 Try to insert some text at the beginning of line. Text will be inserted but cursor is blinking somewhere else. The patch should make busybox vi behave more like original vi(m). Costs a few bytes but its worth it imho. * new_text() is moved into init_text_buffer() * the previously added update_ro_status() was moved info file_insert due to duplication removal mentioned above. function old new delta init_text_buffer - 245 +245 file_insert 312 420 +108 next_tabstop - 82 +82 text_hole_make 154 171 +17 do_cmd 5093 5100 +7 static.cmd_mode_indicator - 5 +5 refresh 1248 1253 +5 current_filename - 4 +4 yank_delete 161 164 +3 what_reg 96 99 +3 end_cmd_q 78 81 +3 char_insert 440 442 +2 readonly_mode - 1 +1 vi_readonly 1 - -1 setops 154 153 -1 readonly 1 - -1 vi_setops 4 1 -3 string_insert 161 158 -3 cfn 4 - -4 show_status_line 532 514 -18 readit 519 500 -19 move_to_col 161 138 -23 vi_main 495 433 -62 isblnk 75 - -75 .rodata 4751 4655 -96 edit_file 892 787 -105 new_text 125 - -125 update_ro_status 131 - -131 colon 3848 3667 -181 ------------------------------------------------------------------------------ (add/remove: 5/6 grow/shrink: 8/10 up/down: 485/-848) Total: -363 bytes text data bss dec hex filename 34751 873 4260 39884 9bcc busybox_old 34439 877 4260 39576 9a98 busybox_unstripped
2007-07-18 07:14:07 +08:00
while (isblank(*p))
2002-08-21 21:02:24 +08:00
p++; // skip over trailing spaces
return p;
2002-08-21 21:02:24 +08:00
}
#if ENABLE_FEATURE_VI_SET && ENABLE_FEATURE_VI_SETOPTS
static void setops(const char *args, const char *opname, int flg_no,
const char *short_opname, int opt)
{
const char *a = args + flg_no;
int l = strlen(opname) - 1; /* opname have + ' ' */
// maybe strncmp? we had tons of erroneous strncasecmp's...
if (strncasecmp(a, opname, l) == 0
|| strncasecmp(a, short_opname, 2) == 0
) {
if (flg_no)
vi_setops &= ~opt;
else
vi_setops |= opt;
}
}
#endif
// buf must be no longer than MAX_INPUT_LEN!
static void colon(char *buf)
2002-08-21 21:02:24 +08:00
{
char c, *orig_buf, *buf1, *q, *r;
char *fn, cmd[MAX_INPUT_LEN], args[MAX_INPUT_LEN];
- merge -r15463:15564 from busybox_scratch branch through these changesets: ------------------------------------------------------------------------ r15465 | aldot | 2006-06-21 20:48:06 +0200 (Wed, 21 Jun 2006) | 3 lines - use CONFIG_BUSYBOX_EXEC_PATH as before it one was broken by a recent revert. - use xchdir() since all is invain if it fails there anyways, supposedly ------------------------------------------------------------------------ r15466 | aldot | 2006-06-21 20:55:16 +0200 (Wed, 21 Jun 2006) | 2 lines - adjust docs to take CONFIG_BUSYBOX_EXEC_PATH into account. ------------------------------------------------------------------------ r15467 | aldot | 2006-06-21 21:31:24 +0200 (Wed, 21 Jun 2006) | 18 lines - partial fallout of my TREE_USED touchup against gcc-4.2: rip unused vars, save s 144 bytes text data bss dec hex filename 862434 10156 645924 1518514 172bb2 busybox.old 862322 10156 645892 1518370 172b22 busybox function old new delta z_len 4 - -4 textend 4 - -4 part_nb 4 - -4 insize 4 - -4 ifile_size 4 - -4 do_link 4 - -4 new_text 70 60 -10 ipaddr_list_link 33 23 -10 gzip_main 898 822 -76 ------------------------------------------------------------------------------ (add/remove: 0/6 grow/shrink: 0/3 up/down: 0/-120) Total: -120 bytes ------------------------------------------------------------------------ r15468 | aldot | 2006-06-21 21:43:05 +0200 (Wed, 21 Jun 2006) | 19 lines - remove useless global exports function old new delta rpm_main 940 1601 +661 rpm_getstring 107 112 +5 rpm_getint 148 153 +5 loop_through_files 103 106 +3 fileaction_dobackup 115 113 -2 fileaction_list 5 - -5 rpm_getcount 42 - -42 extract_cpio_gz 161 - -161 rpm_gettags 504 - -504 ------------------------------------------------------------------------------ (add/remove: 0/4 grow/shrink: 4/1 up/down: 674/-714) Total: -40 bytes text data bss dec hex filename 862322 10156 645892 1518370 172b22 busybox.old 862290 10156 645892 1518338 172b02 busybox ------------------------------------------------------------------------ r15555 | aldot | 2006-06-30 14:10:11 +0200 (Fri, 30 Jun 2006) | 22 lines - shrink syslog a little bit, move a big buffer (for 'line') off the bss, fold s emaphore stuff into single caller manually. stats: function old new delta logMessage 395 427 +32 message 1245 1257 +12 opts - 4 +4 small 1 - -1 local_logging 4 - -4 doRemoteLog 4 - -4 circular_logging 4 - -4 syslogd_main 1299 1285 -14 static.res 36 16 -20 .rodata 186650 186586 -64 static.line 1025 - -1025 ------------------------------------------------------------------------------ (add/remove: 1/5 grow/shrink: 2/3 up/down: 48/-1136) Total: -1088 bytes cow@s37:~/src/busybox_scratch$ size sysklogd/syslogd.o{.orig,} text data bss dec hex filename 3723 348 5242 9313 2461 sysklogd/syslogd.o.orig 3697 348 4188 8233 2029 sysklogd/syslogd.o ============================================================================== Overall bloatcheck for the changeset mentioned above: function old new delta rpm_main 953 1608 +655 logMessage 395 427 +32 message 1245 1257 +12 opts - 4 +4 rpm_getstring 107 110 +3 rpm_getint 148 151 +3 loop_through_files 103 104 +1 small 1 - -1 fileaction_dobackup 115 113 -2 z_len 4 - -4 textend 4 - -4 part_nb 4 - -4 local_logging 4 - -4 insize 4 - -4 ifile_size 4 - -4 do_link 4 - -4 doRemoteLog 4 - -4 circular_logging 4 - -4 fileaction_list 5 - -5 new_text 70 60 -10 ipaddr_list_link 33 23 -10 clear_bufs 31 21 -10 syslogd_main 1287 1273 -14 builtin_help 190 176 -14 static.res 36 16 -20 builtin_source 229 199 -30 rpm_getcount 42 - -42 gzip_main 842 786 -56 .rodata 227176 227112 -64 lash_main 609 527 -82 busy_loop 3883 3739 -144 extract_cpio_gz 155 - -155 rpm_gettags 501 - -501 static.line 1025 - -1025 ------------------------------------------------------------------------------ (add/remove: 1/15 grow/shrink: 6/12 up/down: 710/-2221) Total: -1511 bytes
2006-08-21 01:35:13 +08:00
int i, l, li, ch, b, e;
int useforce, forced = FALSE;
2002-08-21 21:02:24 +08:00
// :3154 // if (-e line 3154) goto it else stay put
// :4,33w! foo // write a portion of buffer to file "foo"
// :w // write all of buffer to current file
// :q // quit
// :q! // quit- dont care about modified file
// :'a,'z!sort -u // filter block through sort
// :'f // goto mark "f"
// :'fl // list literal the mark "f" line
// :.r bar // read file "bar" into buffer before dot
// :/123/,/abc/d // delete lines from "123" line to "abc" line
// :/xyz/ // goto the "xyz" line
// :s/find/replace/ // substitute pattern "find" with "replace"
// :!<cmd> // run <cmd> then return
//
if (!buf[0])
goto ret;
2002-08-21 21:02:24 +08:00
if (*buf == ':')
buf++; // move past the ':'
- merge -r15463:15564 from busybox_scratch branch through these changesets: ------------------------------------------------------------------------ r15465 | aldot | 2006-06-21 20:48:06 +0200 (Wed, 21 Jun 2006) | 3 lines - use CONFIG_BUSYBOX_EXEC_PATH as before it one was broken by a recent revert. - use xchdir() since all is invain if it fails there anyways, supposedly ------------------------------------------------------------------------ r15466 | aldot | 2006-06-21 20:55:16 +0200 (Wed, 21 Jun 2006) | 2 lines - adjust docs to take CONFIG_BUSYBOX_EXEC_PATH into account. ------------------------------------------------------------------------ r15467 | aldot | 2006-06-21 21:31:24 +0200 (Wed, 21 Jun 2006) | 18 lines - partial fallout of my TREE_USED touchup against gcc-4.2: rip unused vars, save s 144 bytes text data bss dec hex filename 862434 10156 645924 1518514 172bb2 busybox.old 862322 10156 645892 1518370 172b22 busybox function old new delta z_len 4 - -4 textend 4 - -4 part_nb 4 - -4 insize 4 - -4 ifile_size 4 - -4 do_link 4 - -4 new_text 70 60 -10 ipaddr_list_link 33 23 -10 gzip_main 898 822 -76 ------------------------------------------------------------------------------ (add/remove: 0/6 grow/shrink: 0/3 up/down: 0/-120) Total: -120 bytes ------------------------------------------------------------------------ r15468 | aldot | 2006-06-21 21:43:05 +0200 (Wed, 21 Jun 2006) | 19 lines - remove useless global exports function old new delta rpm_main 940 1601 +661 rpm_getstring 107 112 +5 rpm_getint 148 153 +5 loop_through_files 103 106 +3 fileaction_dobackup 115 113 -2 fileaction_list 5 - -5 rpm_getcount 42 - -42 extract_cpio_gz 161 - -161 rpm_gettags 504 - -504 ------------------------------------------------------------------------------ (add/remove: 0/4 grow/shrink: 4/1 up/down: 674/-714) Total: -40 bytes text data bss dec hex filename 862322 10156 645892 1518370 172b22 busybox.old 862290 10156 645892 1518338 172b02 busybox ------------------------------------------------------------------------ r15555 | aldot | 2006-06-30 14:10:11 +0200 (Fri, 30 Jun 2006) | 22 lines - shrink syslog a little bit, move a big buffer (for 'line') off the bss, fold s emaphore stuff into single caller manually. stats: function old new delta logMessage 395 427 +32 message 1245 1257 +12 opts - 4 +4 small 1 - -1 local_logging 4 - -4 doRemoteLog 4 - -4 circular_logging 4 - -4 syslogd_main 1299 1285 -14 static.res 36 16 -20 .rodata 186650 186586 -64 static.line 1025 - -1025 ------------------------------------------------------------------------------ (add/remove: 1/5 grow/shrink: 2/3 up/down: 48/-1136) Total: -1088 bytes cow@s37:~/src/busybox_scratch$ size sysklogd/syslogd.o{.orig,} text data bss dec hex filename 3723 348 5242 9313 2461 sysklogd/syslogd.o.orig 3697 348 4188 8233 2029 sysklogd/syslogd.o ============================================================================== Overall bloatcheck for the changeset mentioned above: function old new delta rpm_main 953 1608 +655 logMessage 395 427 +32 message 1245 1257 +12 opts - 4 +4 rpm_getstring 107 110 +3 rpm_getint 148 151 +3 loop_through_files 103 104 +1 small 1 - -1 fileaction_dobackup 115 113 -2 z_len 4 - -4 textend 4 - -4 part_nb 4 - -4 local_logging 4 - -4 insize 4 - -4 ifile_size 4 - -4 do_link 4 - -4 doRemoteLog 4 - -4 circular_logging 4 - -4 fileaction_list 5 - -5 new_text 70 60 -10 ipaddr_list_link 33 23 -10 clear_bufs 31 21 -10 syslogd_main 1287 1273 -14 builtin_help 190 176 -14 static.res 36 16 -20 builtin_source 229 199 -30 rpm_getcount 42 - -42 gzip_main 842 786 -56 .rodata 227176 227112 -64 lash_main 609 527 -82 busy_loop 3883 3739 -144 extract_cpio_gz 155 - -155 rpm_gettags 501 - -501 static.line 1025 - -1025 ------------------------------------------------------------------------------ (add/remove: 1/15 grow/shrink: 6/12 up/down: 710/-2221) Total: -1511 bytes
2006-08-21 01:35:13 +08:00
li = ch = i = 0;
2002-08-21 21:02:24 +08:00
b = e = -1;
q = text; // assume 1,$ for the range
r = end - 1;
li = count_lines(text, end - 1);
fn = current_filename;
2002-08-21 21:02:24 +08:00
// look for optional address(es) :. :1 :1,9 :'q,'a :%
buf = get_address(buf, &b, &e);
// remember orig command line
orig_buf = buf;
// get the COMMAND into cmd[]
buf1 = cmd;
while (*buf != '\0') {
if (isspace(*buf))
break;
*buf1++ = *buf++;
}
*buf1 = '\0';
2002-08-21 21:02:24 +08:00
// get any ARGuments
vi: multiple fixes by Natanael Copa <natanael.copa@gmail.com> * the puzzling message on error is replaced with strerror(errno) so it should be even more detailed and smaller at the same time. * merged code in edit_file() and code for ':edit <file>' in colon() into new func init_text_buffer(). Was horribly duplicate. Moved most of error/sanity checking to file_insert(). Result is that you get a proper validation (prevent reading /dev/*) and error messages for ':r <file>' * renamed 'cfn' to 'current_filename' for improved readability * merged smallint vi_readonly and readonly into bitfields into readonly_mode to save space. * added text_size variable to keep track how big the text buffer is. This is used to fix a buffer overflow. To reproduce bug: ./busybox vi TODO :r Makefile vi segfaults due to no buffer checking is done at all. som redesign is needed here but i added a check in text_hole_make() to aviod the segfault at least. * removed isblnk() and use isblank(3) instead. * fixed compiler warning by displaying the return code for :!<command> This makes things bigger than needed but since the patch reduces the overall size... (see below) * new func next_tabstop(int) merges some duplicate code. There are more cuplicode here but i couldnt find a good way to merge them. * Fix *ANNOYING* placement of cursor on '\t' characters. To reproduce: echo -e "\thello" > file1 ./busybox vi file1 Try to insert some text at the beginning of line. Text will be inserted but cursor is blinking somewhere else. The patch should make busybox vi behave more like original vi(m). Costs a few bytes but its worth it imho. * new_text() is moved into init_text_buffer() * the previously added update_ro_status() was moved info file_insert due to duplication removal mentioned above. function old new delta init_text_buffer - 245 +245 file_insert 312 420 +108 next_tabstop - 82 +82 text_hole_make 154 171 +17 do_cmd 5093 5100 +7 static.cmd_mode_indicator - 5 +5 refresh 1248 1253 +5 current_filename - 4 +4 yank_delete 161 164 +3 what_reg 96 99 +3 end_cmd_q 78 81 +3 char_insert 440 442 +2 readonly_mode - 1 +1 vi_readonly 1 - -1 setops 154 153 -1 readonly 1 - -1 vi_setops 4 1 -3 string_insert 161 158 -3 cfn 4 - -4 show_status_line 532 514 -18 readit 519 500 -19 move_to_col 161 138 -23 vi_main 495 433 -62 isblnk 75 - -75 .rodata 4751 4655 -96 edit_file 892 787 -105 new_text 125 - -125 update_ro_status 131 - -131 colon 3848 3667 -181 ------------------------------------------------------------------------------ (add/remove: 5/6 grow/shrink: 8/10 up/down: 485/-848) Total: -363 bytes text data bss dec hex filename 34751 873 4260 39884 9bcc busybox_old 34439 877 4260 39576 9a98 busybox_unstripped
2007-07-18 07:14:07 +08:00
while (isblank(*buf))
2002-08-21 21:02:24 +08:00
buf++;
strcpy(args, buf);
useforce = FALSE;
buf1 = last_char_is(cmd, '!');
2002-08-21 21:02:24 +08:00
if (buf1) {
useforce = TRUE;
*buf1 = '\0'; // get rid of !
}
if (b >= 0) {
// if there is only one addr, then the addr
// is the line number of the single line the
// user wants. So, reset the end
// pointer to point at end of the "b" line
q = find_line(b); // what line is #b
r = end_line(q);
li = 1;
}
if (e >= 0) {
// we were given two addrs. change the
// end pointer to the addr given by user.
r = find_line(e); // what line is #e
r = end_line(r);
li = e - b + 1;
}
// ------------ now look for the command ------------
i = strlen(cmd);
2002-08-21 21:02:24 +08:00
if (i == 0) { // :123CR goto line #123
if (b >= 0) {
dot = find_line(b); // what line is #b
dot_skip_over_ws();
}
}
#if ENABLE_FEATURE_ALLOW_EXEC
else if (cmd[0] == '!') { // run a cmd
vi: multiple fixes by Natanael Copa <natanael.copa@gmail.com> * the puzzling message on error is replaced with strerror(errno) so it should be even more detailed and smaller at the same time. * merged code in edit_file() and code for ':edit <file>' in colon() into new func init_text_buffer(). Was horribly duplicate. Moved most of error/sanity checking to file_insert(). Result is that you get a proper validation (prevent reading /dev/*) and error messages for ':r <file>' * renamed 'cfn' to 'current_filename' for improved readability * merged smallint vi_readonly and readonly into bitfields into readonly_mode to save space. * added text_size variable to keep track how big the text buffer is. This is used to fix a buffer overflow. To reproduce bug: ./busybox vi TODO :r Makefile vi segfaults due to no buffer checking is done at all. som redesign is needed here but i added a check in text_hole_make() to aviod the segfault at least. * removed isblnk() and use isblank(3) instead. * fixed compiler warning by displaying the return code for :!<command> This makes things bigger than needed but since the patch reduces the overall size... (see below) * new func next_tabstop(int) merges some duplicate code. There are more cuplicode here but i couldnt find a good way to merge them. * Fix *ANNOYING* placement of cursor on '\t' characters. To reproduce: echo -e "\thello" > file1 ./busybox vi file1 Try to insert some text at the beginning of line. Text will be inserted but cursor is blinking somewhere else. The patch should make busybox vi behave more like original vi(m). Costs a few bytes but its worth it imho. * new_text() is moved into init_text_buffer() * the previously added update_ro_status() was moved info file_insert due to duplication removal mentioned above. function old new delta init_text_buffer - 245 +245 file_insert 312 420 +108 next_tabstop - 82 +82 text_hole_make 154 171 +17 do_cmd 5093 5100 +7 static.cmd_mode_indicator - 5 +5 refresh 1248 1253 +5 current_filename - 4 +4 yank_delete 161 164 +3 what_reg 96 99 +3 end_cmd_q 78 81 +3 char_insert 440 442 +2 readonly_mode - 1 +1 vi_readonly 1 - -1 setops 154 153 -1 readonly 1 - -1 vi_setops 4 1 -3 string_insert 161 158 -3 cfn 4 - -4 show_status_line 532 514 -18 readit 519 500 -19 move_to_col 161 138 -23 vi_main 495 433 -62 isblnk 75 - -75 .rodata 4751 4655 -96 edit_file 892 787 -105 new_text 125 - -125 update_ro_status 131 - -131 colon 3848 3667 -181 ------------------------------------------------------------------------------ (add/remove: 5/6 grow/shrink: 8/10 up/down: 485/-848) Total: -363 bytes text data bss dec hex filename 34751 873 4260 39884 9bcc busybox_old 34439 877 4260 39576 9a98 busybox_unstripped
2007-07-18 07:14:07 +08:00
int retcode;
2002-08-21 21:02:24 +08:00
// :!ls run the <cmd>
go_bottom_and_clear_to_eol();
2002-08-21 21:02:24 +08:00
cookmode();
vi: multiple fixes by Natanael Copa <natanael.copa@gmail.com> * the puzzling message on error is replaced with strerror(errno) so it should be even more detailed and smaller at the same time. * merged code in edit_file() and code for ':edit <file>' in colon() into new func init_text_buffer(). Was horribly duplicate. Moved most of error/sanity checking to file_insert(). Result is that you get a proper validation (prevent reading /dev/*) and error messages for ':r <file>' * renamed 'cfn' to 'current_filename' for improved readability * merged smallint vi_readonly and readonly into bitfields into readonly_mode to save space. * added text_size variable to keep track how big the text buffer is. This is used to fix a buffer overflow. To reproduce bug: ./busybox vi TODO :r Makefile vi segfaults due to no buffer checking is done at all. som redesign is needed here but i added a check in text_hole_make() to aviod the segfault at least. * removed isblnk() and use isblank(3) instead. * fixed compiler warning by displaying the return code for :!<command> This makes things bigger than needed but since the patch reduces the overall size... (see below) * new func next_tabstop(int) merges some duplicate code. There are more cuplicode here but i couldnt find a good way to merge them. * Fix *ANNOYING* placement of cursor on '\t' characters. To reproduce: echo -e "\thello" > file1 ./busybox vi file1 Try to insert some text at the beginning of line. Text will be inserted but cursor is blinking somewhere else. The patch should make busybox vi behave more like original vi(m). Costs a few bytes but its worth it imho. * new_text() is moved into init_text_buffer() * the previously added update_ro_status() was moved info file_insert due to duplication removal mentioned above. function old new delta init_text_buffer - 245 +245 file_insert 312 420 +108 next_tabstop - 82 +82 text_hole_make 154 171 +17 do_cmd 5093 5100 +7 static.cmd_mode_indicator - 5 +5 refresh 1248 1253 +5 current_filename - 4 +4 yank_delete 161 164 +3 what_reg 96 99 +3 end_cmd_q 78 81 +3 char_insert 440 442 +2 readonly_mode - 1 +1 vi_readonly 1 - -1 setops 154 153 -1 readonly 1 - -1 vi_setops 4 1 -3 string_insert 161 158 -3 cfn 4 - -4 show_status_line 532 514 -18 readit 519 500 -19 move_to_col 161 138 -23 vi_main 495 433 -62 isblnk 75 - -75 .rodata 4751 4655 -96 edit_file 892 787 -105 new_text 125 - -125 update_ro_status 131 - -131 colon 3848 3667 -181 ------------------------------------------------------------------------------ (add/remove: 5/6 grow/shrink: 8/10 up/down: 485/-848) Total: -363 bytes text data bss dec hex filename 34751 873 4260 39884 9bcc busybox_old 34439 877 4260 39576 9a98 busybox_unstripped
2007-07-18 07:14:07 +08:00
retcode = system(orig_buf + 1); // run the cmd
if (retcode)
printf("\nshell returned %i\n\n", retcode);
2002-08-21 21:02:24 +08:00
rawmode();
Hit_Return(); // let user see results
}
#endif
else if (cmd[0] == '=' && !cmd[1]) { // where is the address
2002-08-21 21:02:24 +08:00
if (b < 0) { // no addr given- use defaults
b = e = count_lines(text, dot);
}
status_line("%d", b);
} else if (strncmp(cmd, "delete", i) == 0) { // delete lines
2002-08-21 21:02:24 +08:00
if (b < 0) { // no addr given- use defaults
q = begin_line(dot); // assume .,. for the range
r = end_line(dot);
}
2002-08-21 21:02:24 +08:00
dot = yank_delete(q, r, 1, YANKDEL); // save, then delete lines
dot_skip_over_ws();
} else if (strncmp(cmd, "edit", i) == 0) { // Edit a file
2002-08-21 21:02:24 +08:00
// don't edit, if the current file has been modified
if (file_modified && !useforce) {
status_line_bold("No write since last change (:edit! overrides)");
goto ret;
2002-08-21 21:02:24 +08:00
}
if (args[0]) {
2002-08-21 21:02:24 +08:00
// the user supplied a file name
fn = args;
vi: multiple fixes by Natanael Copa <natanael.copa@gmail.com> * the puzzling message on error is replaced with strerror(errno) so it should be even more detailed and smaller at the same time. * merged code in edit_file() and code for ':edit <file>' in colon() into new func init_text_buffer(). Was horribly duplicate. Moved most of error/sanity checking to file_insert(). Result is that you get a proper validation (prevent reading /dev/*) and error messages for ':r <file>' * renamed 'cfn' to 'current_filename' for improved readability * merged smallint vi_readonly and readonly into bitfields into readonly_mode to save space. * added text_size variable to keep track how big the text buffer is. This is used to fix a buffer overflow. To reproduce bug: ./busybox vi TODO :r Makefile vi segfaults due to no buffer checking is done at all. som redesign is needed here but i added a check in text_hole_make() to aviod the segfault at least. * removed isblnk() and use isblank(3) instead. * fixed compiler warning by displaying the return code for :!<command> This makes things bigger than needed but since the patch reduces the overall size... (see below) * new func next_tabstop(int) merges some duplicate code. There are more cuplicode here but i couldnt find a good way to merge them. * Fix *ANNOYING* placement of cursor on '\t' characters. To reproduce: echo -e "\thello" > file1 ./busybox vi file1 Try to insert some text at the beginning of line. Text will be inserted but cursor is blinking somewhere else. The patch should make busybox vi behave more like original vi(m). Costs a few bytes but its worth it imho. * new_text() is moved into init_text_buffer() * the previously added update_ro_status() was moved info file_insert due to duplication removal mentioned above. function old new delta init_text_buffer - 245 +245 file_insert 312 420 +108 next_tabstop - 82 +82 text_hole_make 154 171 +17 do_cmd 5093 5100 +7 static.cmd_mode_indicator - 5 +5 refresh 1248 1253 +5 current_filename - 4 +4 yank_delete 161 164 +3 what_reg 96 99 +3 end_cmd_q 78 81 +3 char_insert 440 442 +2 readonly_mode - 1 +1 vi_readonly 1 - -1 setops 154 153 -1 readonly 1 - -1 vi_setops 4 1 -3 string_insert 161 158 -3 cfn 4 - -4 show_status_line 532 514 -18 readit 519 500 -19 move_to_col 161 138 -23 vi_main 495 433 -62 isblnk 75 - -75 .rodata 4751 4655 -96 edit_file 892 787 -105 new_text 125 - -125 update_ro_status 131 - -131 colon 3848 3667 -181 ------------------------------------------------------------------------------ (add/remove: 5/6 grow/shrink: 8/10 up/down: 485/-848) Total: -363 bytes text data bss dec hex filename 34751 873 4260 39884 9bcc busybox_old 34439 877 4260 39576 9a98 busybox_unstripped
2007-07-18 07:14:07 +08:00
} else if (current_filename && current_filename[0]) {
2002-08-21 21:02:24 +08:00
// no user supplied name- use the current filename
vi: multiple fixes by Natanael Copa <natanael.copa@gmail.com> * the puzzling message on error is replaced with strerror(errno) so it should be even more detailed and smaller at the same time. * merged code in edit_file() and code for ':edit <file>' in colon() into new func init_text_buffer(). Was horribly duplicate. Moved most of error/sanity checking to file_insert(). Result is that you get a proper validation (prevent reading /dev/*) and error messages for ':r <file>' * renamed 'cfn' to 'current_filename' for improved readability * merged smallint vi_readonly and readonly into bitfields into readonly_mode to save space. * added text_size variable to keep track how big the text buffer is. This is used to fix a buffer overflow. To reproduce bug: ./busybox vi TODO :r Makefile vi segfaults due to no buffer checking is done at all. som redesign is needed here but i added a check in text_hole_make() to aviod the segfault at least. * removed isblnk() and use isblank(3) instead. * fixed compiler warning by displaying the return code for :!<command> This makes things bigger than needed but since the patch reduces the overall size... (see below) * new func next_tabstop(int) merges some duplicate code. There are more cuplicode here but i couldnt find a good way to merge them. * Fix *ANNOYING* placement of cursor on '\t' characters. To reproduce: echo -e "\thello" > file1 ./busybox vi file1 Try to insert some text at the beginning of line. Text will be inserted but cursor is blinking somewhere else. The patch should make busybox vi behave more like original vi(m). Costs a few bytes but its worth it imho. * new_text() is moved into init_text_buffer() * the previously added update_ro_status() was moved info file_insert due to duplication removal mentioned above. function old new delta init_text_buffer - 245 +245 file_insert 312 420 +108 next_tabstop - 82 +82 text_hole_make 154 171 +17 do_cmd 5093 5100 +7 static.cmd_mode_indicator - 5 +5 refresh 1248 1253 +5 current_filename - 4 +4 yank_delete 161 164 +3 what_reg 96 99 +3 end_cmd_q 78 81 +3 char_insert 440 442 +2 readonly_mode - 1 +1 vi_readonly 1 - -1 setops 154 153 -1 readonly 1 - -1 vi_setops 4 1 -3 string_insert 161 158 -3 cfn 4 - -4 show_status_line 532 514 -18 readit 519 500 -19 move_to_col 161 138 -23 vi_main 495 433 -62 isblnk 75 - -75 .rodata 4751 4655 -96 edit_file 892 787 -105 new_text 125 - -125 update_ro_status 131 - -131 colon 3848 3667 -181 ------------------------------------------------------------------------------ (add/remove: 5/6 grow/shrink: 8/10 up/down: 485/-848) Total: -363 bytes text data bss dec hex filename 34751 873 4260 39884 9bcc busybox_old 34439 877 4260 39576 9a98 busybox_unstripped
2007-07-18 07:14:07 +08:00
// fn = current_filename; was set by default
2002-08-21 21:02:24 +08:00
} else {
// no user file name, no current name- punt
status_line_bold("No current filename");
goto ret;
2002-08-21 21:02:24 +08:00
}
vi: multiple fixes by Natanael Copa <natanael.copa@gmail.com> * the puzzling message on error is replaced with strerror(errno) so it should be even more detailed and smaller at the same time. * merged code in edit_file() and code for ':edit <file>' in colon() into new func init_text_buffer(). Was horribly duplicate. Moved most of error/sanity checking to file_insert(). Result is that you get a proper validation (prevent reading /dev/*) and error messages for ':r <file>' * renamed 'cfn' to 'current_filename' for improved readability * merged smallint vi_readonly and readonly into bitfields into readonly_mode to save space. * added text_size variable to keep track how big the text buffer is. This is used to fix a buffer overflow. To reproduce bug: ./busybox vi TODO :r Makefile vi segfaults due to no buffer checking is done at all. som redesign is needed here but i added a check in text_hole_make() to aviod the segfault at least. * removed isblnk() and use isblank(3) instead. * fixed compiler warning by displaying the return code for :!<command> This makes things bigger than needed but since the patch reduces the overall size... (see below) * new func next_tabstop(int) merges some duplicate code. There are more cuplicode here but i couldnt find a good way to merge them. * Fix *ANNOYING* placement of cursor on '\t' characters. To reproduce: echo -e "\thello" > file1 ./busybox vi file1 Try to insert some text at the beginning of line. Text will be inserted but cursor is blinking somewhere else. The patch should make busybox vi behave more like original vi(m). Costs a few bytes but its worth it imho. * new_text() is moved into init_text_buffer() * the previously added update_ro_status() was moved info file_insert due to duplication removal mentioned above. function old new delta init_text_buffer - 245 +245 file_insert 312 420 +108 next_tabstop - 82 +82 text_hole_make 154 171 +17 do_cmd 5093 5100 +7 static.cmd_mode_indicator - 5 +5 refresh 1248 1253 +5 current_filename - 4 +4 yank_delete 161 164 +3 what_reg 96 99 +3 end_cmd_q 78 81 +3 char_insert 440 442 +2 readonly_mode - 1 +1 vi_readonly 1 - -1 setops 154 153 -1 readonly 1 - -1 vi_setops 4 1 -3 string_insert 161 158 -3 cfn 4 - -4 show_status_line 532 514 -18 readit 519 500 -19 move_to_col 161 138 -23 vi_main 495 433 -62 isblnk 75 - -75 .rodata 4751 4655 -96 edit_file 892 787 -105 new_text 125 - -125 update_ro_status 131 - -131 colon 3848 3667 -181 ------------------------------------------------------------------------------ (add/remove: 5/6 grow/shrink: 8/10 up/down: 485/-848) Total: -363 bytes text data bss dec hex filename 34751 873 4260 39884 9bcc busybox_old 34439 877 4260 39576 9a98 busybox_unstripped
2007-07-18 07:14:07 +08:00
if (init_text_buffer(fn) < 0)
goto ret;
2002-08-21 21:02:24 +08:00
#if ENABLE_FEATURE_VI_YANKMARK
if (Ureg >= 0 && Ureg < 28) {
2002-08-21 21:02:24 +08:00
free(reg[Ureg]); // free orig line reg- for 'U'
reg[Ureg] = NULL;
2002-08-21 21:02:24 +08:00
}
if (YDreg >= 0 && YDreg < 28) {
2002-08-21 21:02:24 +08:00
free(reg[YDreg]); // free default yank/delete register
reg[YDreg] = NULL;
2002-08-21 21:02:24 +08:00
}
#endif
2002-08-21 21:02:24 +08:00
// how many lines in text[]?
li = count_lines(text, end - 1);
status_line("\"%s\"%s"
IF_FEATURE_VI_READONLY("%s")
vi: multiple fixes by Natanael Copa <natanael.copa@gmail.com> * the puzzling message on error is replaced with strerror(errno) so it should be even more detailed and smaller at the same time. * merged code in edit_file() and code for ':edit <file>' in colon() into new func init_text_buffer(). Was horribly duplicate. Moved most of error/sanity checking to file_insert(). Result is that you get a proper validation (prevent reading /dev/*) and error messages for ':r <file>' * renamed 'cfn' to 'current_filename' for improved readability * merged smallint vi_readonly and readonly into bitfields into readonly_mode to save space. * added text_size variable to keep track how big the text buffer is. This is used to fix a buffer overflow. To reproduce bug: ./busybox vi TODO :r Makefile vi segfaults due to no buffer checking is done at all. som redesign is needed here but i added a check in text_hole_make() to aviod the segfault at least. * removed isblnk() and use isblank(3) instead. * fixed compiler warning by displaying the return code for :!<command> This makes things bigger than needed but since the patch reduces the overall size... (see below) * new func next_tabstop(int) merges some duplicate code. There are more cuplicode here but i couldnt find a good way to merge them. * Fix *ANNOYING* placement of cursor on '\t' characters. To reproduce: echo -e "\thello" > file1 ./busybox vi file1 Try to insert some text at the beginning of line. Text will be inserted but cursor is blinking somewhere else. The patch should make busybox vi behave more like original vi(m). Costs a few bytes but its worth it imho. * new_text() is moved into init_text_buffer() * the previously added update_ro_status() was moved info file_insert due to duplication removal mentioned above. function old new delta init_text_buffer - 245 +245 file_insert 312 420 +108 next_tabstop - 82 +82 text_hole_make 154 171 +17 do_cmd 5093 5100 +7 static.cmd_mode_indicator - 5 +5 refresh 1248 1253 +5 current_filename - 4 +4 yank_delete 161 164 +3 what_reg 96 99 +3 end_cmd_q 78 81 +3 char_insert 440 442 +2 readonly_mode - 1 +1 vi_readonly 1 - -1 setops 154 153 -1 readonly 1 - -1 vi_setops 4 1 -3 string_insert 161 158 -3 cfn 4 - -4 show_status_line 532 514 -18 readit 519 500 -19 move_to_col 161 138 -23 vi_main 495 433 -62 isblnk 75 - -75 .rodata 4751 4655 -96 edit_file 892 787 -105 new_text 125 - -125 update_ro_status 131 - -131 colon 3848 3667 -181 ------------------------------------------------------------------------------ (add/remove: 5/6 grow/shrink: 8/10 up/down: 485/-848) Total: -363 bytes text data bss dec hex filename 34751 873 4260 39884 9bcc busybox_old 34439 877 4260 39576 9a98 busybox_unstripped
2007-07-18 07:14:07 +08:00
" %dL, %dC", current_filename,
(file_size(fn) < 0 ? " [New file]" : ""),
IF_FEATURE_VI_READONLY(
vi: multiple fixes by Natanael Copa <natanael.copa@gmail.com> * the puzzling message on error is replaced with strerror(errno) so it should be even more detailed and smaller at the same time. * merged code in edit_file() and code for ':edit <file>' in colon() into new func init_text_buffer(). Was horribly duplicate. Moved most of error/sanity checking to file_insert(). Result is that you get a proper validation (prevent reading /dev/*) and error messages for ':r <file>' * renamed 'cfn' to 'current_filename' for improved readability * merged smallint vi_readonly and readonly into bitfields into readonly_mode to save space. * added text_size variable to keep track how big the text buffer is. This is used to fix a buffer overflow. To reproduce bug: ./busybox vi TODO :r Makefile vi segfaults due to no buffer checking is done at all. som redesign is needed here but i added a check in text_hole_make() to aviod the segfault at least. * removed isblnk() and use isblank(3) instead. * fixed compiler warning by displaying the return code for :!<command> This makes things bigger than needed but since the patch reduces the overall size... (see below) * new func next_tabstop(int) merges some duplicate code. There are more cuplicode here but i couldnt find a good way to merge them. * Fix *ANNOYING* placement of cursor on '\t' characters. To reproduce: echo -e "\thello" > file1 ./busybox vi file1 Try to insert some text at the beginning of line. Text will be inserted but cursor is blinking somewhere else. The patch should make busybox vi behave more like original vi(m). Costs a few bytes but its worth it imho. * new_text() is moved into init_text_buffer() * the previously added update_ro_status() was moved info file_insert due to duplication removal mentioned above. function old new delta init_text_buffer - 245 +245 file_insert 312 420 +108 next_tabstop - 82 +82 text_hole_make 154 171 +17 do_cmd 5093 5100 +7 static.cmd_mode_indicator - 5 +5 refresh 1248 1253 +5 current_filename - 4 +4 yank_delete 161 164 +3 what_reg 96 99 +3 end_cmd_q 78 81 +3 char_insert 440 442 +2 readonly_mode - 1 +1 vi_readonly 1 - -1 setops 154 153 -1 readonly 1 - -1 vi_setops 4 1 -3 string_insert 161 158 -3 cfn 4 - -4 show_status_line 532 514 -18 readit 519 500 -19 move_to_col 161 138 -23 vi_main 495 433 -62 isblnk 75 - -75 .rodata 4751 4655 -96 edit_file 892 787 -105 new_text 125 - -125 update_ro_status 131 - -131 colon 3848 3667 -181 ------------------------------------------------------------------------------ (add/remove: 5/6 grow/shrink: 8/10 up/down: 485/-848) Total: -363 bytes text data bss dec hex filename 34751 873 4260 39884 9bcc busybox_old 34439 877 4260 39576 9a98 busybox_unstripped
2007-07-18 07:14:07 +08:00
((readonly_mode) ? " [Readonly]" : ""),
)
2002-08-21 21:02:24 +08:00
li, ch);
} else if (strncmp(cmd, "file", i) == 0) { // what File is this
2002-08-21 21:02:24 +08:00
if (b != -1 || e != -1) {
status_line_bold("No address allowed on this command");
goto ret;
}
if (args[0]) {
2002-08-21 21:02:24 +08:00
// user wants a new filename
vi: multiple fixes by Natanael Copa <natanael.copa@gmail.com> * the puzzling message on error is replaced with strerror(errno) so it should be even more detailed and smaller at the same time. * merged code in edit_file() and code for ':edit <file>' in colon() into new func init_text_buffer(). Was horribly duplicate. Moved most of error/sanity checking to file_insert(). Result is that you get a proper validation (prevent reading /dev/*) and error messages for ':r <file>' * renamed 'cfn' to 'current_filename' for improved readability * merged smallint vi_readonly and readonly into bitfields into readonly_mode to save space. * added text_size variable to keep track how big the text buffer is. This is used to fix a buffer overflow. To reproduce bug: ./busybox vi TODO :r Makefile vi segfaults due to no buffer checking is done at all. som redesign is needed here but i added a check in text_hole_make() to aviod the segfault at least. * removed isblnk() and use isblank(3) instead. * fixed compiler warning by displaying the return code for :!<command> This makes things bigger than needed but since the patch reduces the overall size... (see below) * new func next_tabstop(int) merges some duplicate code. There are more cuplicode here but i couldnt find a good way to merge them. * Fix *ANNOYING* placement of cursor on '\t' characters. To reproduce: echo -e "\thello" > file1 ./busybox vi file1 Try to insert some text at the beginning of line. Text will be inserted but cursor is blinking somewhere else. The patch should make busybox vi behave more like original vi(m). Costs a few bytes but its worth it imho. * new_text() is moved into init_text_buffer() * the previously added update_ro_status() was moved info file_insert due to duplication removal mentioned above. function old new delta init_text_buffer - 245 +245 file_insert 312 420 +108 next_tabstop - 82 +82 text_hole_make 154 171 +17 do_cmd 5093 5100 +7 static.cmd_mode_indicator - 5 +5 refresh 1248 1253 +5 current_filename - 4 +4 yank_delete 161 164 +3 what_reg 96 99 +3 end_cmd_q 78 81 +3 char_insert 440 442 +2 readonly_mode - 1 +1 vi_readonly 1 - -1 setops 154 153 -1 readonly 1 - -1 vi_setops 4 1 -3 string_insert 161 158 -3 cfn 4 - -4 show_status_line 532 514 -18 readit 519 500 -19 move_to_col 161 138 -23 vi_main 495 433 -62 isblnk 75 - -75 .rodata 4751 4655 -96 edit_file 892 787 -105 new_text 125 - -125 update_ro_status 131 - -131 colon 3848 3667 -181 ------------------------------------------------------------------------------ (add/remove: 5/6 grow/shrink: 8/10 up/down: 485/-848) Total: -363 bytes text data bss dec hex filename 34751 873 4260 39884 9bcc busybox_old 34439 877 4260 39576 9a98 busybox_unstripped
2007-07-18 07:14:07 +08:00
free(current_filename);
current_filename = xstrdup(args);
2002-08-21 21:02:24 +08:00
} else {
// user wants file status info
last_status_cksum = 0; // force status update
}
} else if (strncmp(cmd, "features", i) == 0) { // what features are available
2002-08-21 21:02:24 +08:00
// print out values of all features
go_bottom_and_clear_to_eol();
2002-08-21 21:02:24 +08:00
cookmode();
show_help();
rawmode();
Hit_Return();
} else if (strncmp(cmd, "list", i) == 0) { // literal print line
2002-08-21 21:02:24 +08:00
if (b < 0) { // no addr given- use defaults
q = begin_line(dot); // assume .,. for the range
r = end_line(dot);
}
go_bottom_and_clear_to_eol();
puts("\r");
2002-08-21 21:02:24 +08:00
for (; q <= r; q++) {
int c_is_no_print;
2002-08-21 21:02:24 +08:00
c = *q;
c_is_no_print = (c & 0x80) && !Isprint(c);
if (c_is_no_print) {
c = '.';
2002-08-21 21:02:24 +08:00
standout_start();
}
2002-08-21 21:02:24 +08:00
if (c == '\n') {
write1("$\r");
} else if (c < ' ' || c == 127) {
bb_putchar('^');
if (c == 127)
c = '?';
else
c += '@';
}
bb_putchar(c);
if (c_is_no_print)
2002-08-21 21:02:24 +08:00
standout_end();
}
2002-08-21 21:02:24 +08:00
Hit_Return();
} else if (strncmp(cmd, "quit", i) == 0 // quit
|| strncmp(cmd, "next", i) == 0 // edit next file
) {
int n;
2002-08-21 21:02:24 +08:00
if (useforce) {
// force end of argv list
if (*cmd == 'q') {
optind = save_argc;
}
editing = 0;
goto ret;
}
2002-08-21 21:02:24 +08:00
// don't exit if the file been modified
if (file_modified) {
status_line_bold("No write since last change (:%s! overrides)",
2002-08-21 21:02:24 +08:00
(*cmd == 'q' ? "quit" : "next"));
goto ret;
}
2002-08-21 21:02:24 +08:00
// are there other file to edit
n = save_argc - optind - 1;
if (*cmd == 'q' && n > 0) {
status_line_bold("%d more file(s) to edit", n);
goto ret;
}
if (*cmd == 'n' && n <= 0) {
status_line_bold("No more files to edit");
goto ret;
}
2002-08-21 21:02:24 +08:00
editing = 0;
} else if (strncmp(cmd, "read", i) == 0) { // read file into text[]
2002-08-21 21:02:24 +08:00
fn = args;
if (!fn[0]) {
status_line_bold("No filename given");
goto ret;
}
2002-08-21 21:02:24 +08:00
if (b < 0) { // no addr given- use defaults
q = begin_line(dot); // assume "dot"
}
2002-08-21 21:02:24 +08:00
// read after current line- unless user said ":0r foo"
if (b != 0)
q = next_line(q);
{ // dance around potentially-reallocated text[]
uintptr_t ofs = q - text;
ch = file_insert(fn, q, 0);
q = text + ofs;
}
2002-08-21 21:02:24 +08:00
if (ch < 0)
goto ret; // nothing was inserted
2002-08-21 21:02:24 +08:00
// how many lines in text[]?
li = count_lines(q, q + ch - 1);
status_line("\"%s\""
IF_FEATURE_VI_READONLY("%s")
2002-08-21 21:02:24 +08:00
" %dL, %dC", fn,
IF_FEATURE_VI_READONLY((readonly_mode ? " [Readonly]" : ""),)
2002-08-21 21:02:24 +08:00
li, ch);
if (ch > 0) {
// if the insert is before "dot" then we need to update
if (q <= dot)
dot += ch;
/*file_modified++; - done by file_insert */
}
} else if (strncmp(cmd, "rewind", i) == 0) { // rewind cmd line args
if (file_modified && !useforce) {
status_line_bold("No write since last change (:rewind! overrides)");
2002-08-21 21:02:24 +08:00
} else {
// reset the filenames to edit
optind = fn_start - 1;
editing = 0;
}
#if ENABLE_FEATURE_VI_SET
} else if (strncmp(cmd, "set", i) == 0) { // set or clear features
#if ENABLE_FEATURE_VI_SETOPTS
2007-03-21 08:03:42 +08:00
char *argp;
#endif
2002-08-21 21:02:24 +08:00
i = 0; // offset into args
2007-03-21 08:03:42 +08:00
// only blank is regarded as args delmiter. What about tab '\t' ?
if (!args[0] || strcasecmp(args, "all") == 0) {
2002-08-21 21:02:24 +08:00
// print out values of all options
#if ENABLE_FEATURE_VI_SETOPTS
status_line_bold(
"%sautoindent "
"%sflash "
"%signorecase "
"%sshowmatch "
"tabstop=%u",
autoindent ? "" : "no",
err_method ? "" : "no",
ignorecase ? "" : "no",
showmatch ? "" : "no",
tabstop
);
#endif
goto ret;
}
#if ENABLE_FEATURE_VI_SETOPTS
argp = args;
while (*argp) {
if (strncmp(argp, "no", 2) == 0)
2007-03-21 08:03:42 +08:00
i = 2; // ":set noautoindent"
setops(argp, "autoindent ", i, "ai", VI_AUTOINDENT);
setops(argp, "flash " , i, "fl", VI_ERR_METHOD);
2007-03-21 08:03:42 +08:00
setops(argp, "ignorecase ", i, "ic", VI_IGNORECASE);
setops(argp, "showmatch " , i, "sm", VI_SHOWMATCH );
if (strncmp(argp + i, "tabstop=", 8) == 0) {
int t = 0;
sscanf(argp + i+8, "%u", &t);
if (t > 0 && t <= MAX_TABSTOP)
tabstop = t;
2007-03-21 08:03:42 +08:00
}
argp = skip_non_whitespace(argp);
argp = skip_whitespace(argp);
2002-08-21 21:02:24 +08:00
}
#endif /* FEATURE_VI_SETOPTS */
#endif /* FEATURE_VI_SET */
#if ENABLE_FEATURE_VI_SEARCH
} else if (cmd[0] == 's') { // substitute a pattern with a replacement pattern
char *F, *R, *flags;
size_t len_F, len_R;
int gflag; // global replace flag
2002-08-21 21:02:24 +08:00
// F points to the "find" pattern
// R points to the "replace" pattern
// replace the cmd line delimiters "/" with NULs
2002-08-21 21:02:24 +08:00
c = orig_buf[1]; // what is the delimiter
F = orig_buf + 2; // start of "find"
R = strchr(F, c); // middle delimiter
if (!R)
goto colon_s_fail;
len_F = R - F;
2002-08-21 21:02:24 +08:00
*R++ = '\0'; // terminate "find"
flags = strchr(R, c);
if (!flags)
goto colon_s_fail;
len_R = flags - R;
*flags++ = '\0'; // terminate "replace"
gflag = *flags;
2002-08-21 21:02:24 +08:00
q = begin_line(q);
if (b < 0) { // maybe :s/foo/bar/
q = begin_line(dot); // start with cur line
b = count_lines(text, q); // cur line number
2002-08-21 21:02:24 +08:00
}
if (e < 0)
e = b; // maybe :.s/foo/bar/
2002-08-21 21:02:24 +08:00
for (i = b; i <= e; i++) { // so, :20,23 s \0 find \0 replace \0
char *ls = q; // orig line start
char *found;
vc4:
found = char_search(q, F, FORWARD, LIMITED); // search cur line only for "find"
if (found) {
uintptr_t bias;
// we found the "find" pattern - delete it
text_hole_delete(found, found + len_F - 1);
2002-08-21 21:02:24 +08:00
// inset the "replace" patern
bias = string_insert(found, R); // insert the string
found += bias;
ls += bias;
/*q += bias; - recalculated anyway */
2002-08-21 21:02:24 +08:00
// check for "global" :s/foo/bar/g
if (gflag == 'g') {
if ((found + len_R) < end_line(ls)) {
q = found + len_R;
2002-08-21 21:02:24 +08:00
goto vc4; // don't let q move past cur line
}
}
}
2002-08-21 21:02:24 +08:00
q = next_line(ls);
}
#endif /* FEATURE_VI_SEARCH */
} else if (strncmp(cmd, "version", i) == 0) { // show software version
status_line(BB_VER " " BB_BT);
} else if (strncmp(cmd, "write", i) == 0 // write text to file
|| strncmp(cmd, "wq", i) == 0
|| strncmp(cmd, "wn", i) == 0
|| (cmd[0] == 'x' && !cmd[1])
) {
2002-08-21 21:02:24 +08:00
// is there a file name to write to?
if (args[0]) {
2002-08-21 21:02:24 +08:00
fn = args;
}
#if ENABLE_FEATURE_VI_READONLY
vi: multiple fixes by Natanael Copa <natanael.copa@gmail.com> * the puzzling message on error is replaced with strerror(errno) so it should be even more detailed and smaller at the same time. * merged code in edit_file() and code for ':edit <file>' in colon() into new func init_text_buffer(). Was horribly duplicate. Moved most of error/sanity checking to file_insert(). Result is that you get a proper validation (prevent reading /dev/*) and error messages for ':r <file>' * renamed 'cfn' to 'current_filename' for improved readability * merged smallint vi_readonly and readonly into bitfields into readonly_mode to save space. * added text_size variable to keep track how big the text buffer is. This is used to fix a buffer overflow. To reproduce bug: ./busybox vi TODO :r Makefile vi segfaults due to no buffer checking is done at all. som redesign is needed here but i added a check in text_hole_make() to aviod the segfault at least. * removed isblnk() and use isblank(3) instead. * fixed compiler warning by displaying the return code for :!<command> This makes things bigger than needed but since the patch reduces the overall size... (see below) * new func next_tabstop(int) merges some duplicate code. There are more cuplicode here but i couldnt find a good way to merge them. * Fix *ANNOYING* placement of cursor on '\t' characters. To reproduce: echo -e "\thello" > file1 ./busybox vi file1 Try to insert some text at the beginning of line. Text will be inserted but cursor is blinking somewhere else. The patch should make busybox vi behave more like original vi(m). Costs a few bytes but its worth it imho. * new_text() is moved into init_text_buffer() * the previously added update_ro_status() was moved info file_insert due to duplication removal mentioned above. function old new delta init_text_buffer - 245 +245 file_insert 312 420 +108 next_tabstop - 82 +82 text_hole_make 154 171 +17 do_cmd 5093 5100 +7 static.cmd_mode_indicator - 5 +5 refresh 1248 1253 +5 current_filename - 4 +4 yank_delete 161 164 +3 what_reg 96 99 +3 end_cmd_q 78 81 +3 char_insert 440 442 +2 readonly_mode - 1 +1 vi_readonly 1 - -1 setops 154 153 -1 readonly 1 - -1 vi_setops 4 1 -3 string_insert 161 158 -3 cfn 4 - -4 show_status_line 532 514 -18 readit 519 500 -19 move_to_col 161 138 -23 vi_main 495 433 -62 isblnk 75 - -75 .rodata 4751 4655 -96 edit_file 892 787 -105 new_text 125 - -125 update_ro_status 131 - -131 colon 3848 3667 -181 ------------------------------------------------------------------------------ (add/remove: 5/6 grow/shrink: 8/10 up/down: 485/-848) Total: -363 bytes text data bss dec hex filename 34751 873 4260 39884 9bcc busybox_old 34439 877 4260 39576 9a98 busybox_unstripped
2007-07-18 07:14:07 +08:00
if (readonly_mode && !useforce) {
status_line_bold("\"%s\" File is read only", fn);
goto ret;
}
#endif
2002-08-21 21:02:24 +08:00
// how many lines in text[]?
li = count_lines(q, r);
ch = r - q + 1;
// see if file exists- if not, its just a new file request
if (useforce) {
// if "fn" is not write-able, chmod u+w
// sprintf(syscmd, "chmod u+w %s", fn);
// system(syscmd);
forced = TRUE;
}
2002-08-21 21:02:24 +08:00
l = file_write(fn, q, r);
if (useforce && forced) {
// chmod u-w
// sprintf(syscmd, "chmod u-w %s", fn);
// system(syscmd);
forced = FALSE;
}
if (l < 0) {
if (l == -1)
status_line_bold("\"%s\" %s", fn, strerror(errno));
} else {
status_line("\"%s\" %dL, %dC", fn, li, l);
if (q == text && r == end - 1 && l == ch) {
file_modified = 0;
last_file_modified = -1;
}
if ((cmd[0] == 'x' || cmd[1] == 'q' || cmd[1] == 'n'
|| cmd[0] == 'X' || cmd[1] == 'Q' || cmd[1] == 'N'
)
&& l == ch
) {
editing = 0;
}
}
#if ENABLE_FEATURE_VI_YANKMARK
} else if (strncmp(cmd, "yank", i) == 0) { // yank lines
2002-08-21 21:02:24 +08:00
if (b < 0) { // no addr given- use defaults
q = begin_line(dot); // assume .,. for the range
r = end_line(dot);
}
2002-08-21 21:02:24 +08:00
text_yank(q, r, YDreg);
li = count_lines(q, r);
status_line("Yank %d lines (%d chars) into [%c]",
li, strlen(reg[YDreg]), what_reg());
#endif
2002-08-21 21:02:24 +08:00
} else {
// cmd unknown
not_implemented(cmd);
2002-08-21 21:02:24 +08:00
}
ret:
2002-08-21 21:02:24 +08:00
dot = bound_dot(dot); // make sure "dot" is valid
return;
#if ENABLE_FEATURE_VI_SEARCH
colon_s_fail:
status_line(":s expression missing delimiters");
2002-08-21 21:02:24 +08:00
#endif
}
#endif /* FEATURE_VI_COLON */
2002-08-21 21:02:24 +08:00
static void Hit_Return(void)
{
int c;
2002-08-21 21:02:24 +08:00
2007-12-23 10:36:51 +08:00
standout_start();
write1("[Hit return to continue]");
2007-12-23 10:36:51 +08:00
standout_end();
while ((c = get_one_char()) != '\n' && c != '\r')
continue;
2002-08-21 21:02:24 +08:00
redraw(TRUE); // force redraw all
}
2007-07-18 07:22:49 +08:00
static int next_tabstop(int col)
{
vi: multiple fixes by Natanael Copa <natanael.copa@gmail.com> * the puzzling message on error is replaced with strerror(errno) so it should be even more detailed and smaller at the same time. * merged code in edit_file() and code for ':edit <file>' in colon() into new func init_text_buffer(). Was horribly duplicate. Moved most of error/sanity checking to file_insert(). Result is that you get a proper validation (prevent reading /dev/*) and error messages for ':r <file>' * renamed 'cfn' to 'current_filename' for improved readability * merged smallint vi_readonly and readonly into bitfields into readonly_mode to save space. * added text_size variable to keep track how big the text buffer is. This is used to fix a buffer overflow. To reproduce bug: ./busybox vi TODO :r Makefile vi segfaults due to no buffer checking is done at all. som redesign is needed here but i added a check in text_hole_make() to aviod the segfault at least. * removed isblnk() and use isblank(3) instead. * fixed compiler warning by displaying the return code for :!<command> This makes things bigger than needed but since the patch reduces the overall size... (see below) * new func next_tabstop(int) merges some duplicate code. There are more cuplicode here but i couldnt find a good way to merge them. * Fix *ANNOYING* placement of cursor on '\t' characters. To reproduce: echo -e "\thello" > file1 ./busybox vi file1 Try to insert some text at the beginning of line. Text will be inserted but cursor is blinking somewhere else. The patch should make busybox vi behave more like original vi(m). Costs a few bytes but its worth it imho. * new_text() is moved into init_text_buffer() * the previously added update_ro_status() was moved info file_insert due to duplication removal mentioned above. function old new delta init_text_buffer - 245 +245 file_insert 312 420 +108 next_tabstop - 82 +82 text_hole_make 154 171 +17 do_cmd 5093 5100 +7 static.cmd_mode_indicator - 5 +5 refresh 1248 1253 +5 current_filename - 4 +4 yank_delete 161 164 +3 what_reg 96 99 +3 end_cmd_q 78 81 +3 char_insert 440 442 +2 readonly_mode - 1 +1 vi_readonly 1 - -1 setops 154 153 -1 readonly 1 - -1 vi_setops 4 1 -3 string_insert 161 158 -3 cfn 4 - -4 show_status_line 532 514 -18 readit 519 500 -19 move_to_col 161 138 -23 vi_main 495 433 -62 isblnk 75 - -75 .rodata 4751 4655 -96 edit_file 892 787 -105 new_text 125 - -125 update_ro_status 131 - -131 colon 3848 3667 -181 ------------------------------------------------------------------------------ (add/remove: 5/6 grow/shrink: 8/10 up/down: 485/-848) Total: -363 bytes text data bss dec hex filename 34751 873 4260 39884 9bcc busybox_old 34439 877 4260 39576 9a98 busybox_unstripped
2007-07-18 07:14:07 +08:00
return col + ((tabstop - 1) - (col % tabstop));
}
2002-08-21 21:02:24 +08:00
//----- Synchronize the cursor to Dot --------------------------
static NOINLINE void sync_cursor(char *d, int *row, int *col)
2002-08-21 21:02:24 +08:00
{
char *beg_cur; // begin and end of "d" line
char *tp;
2002-08-21 21:02:24 +08:00
int cnt, ro, co;
beg_cur = begin_line(d); // first char of cur line
if (beg_cur < screenbegin) {
2007-12-23 10:36:51 +08:00
// "d" is before top line on screen
2002-08-21 21:02:24 +08:00
// how many lines do we have to move
cnt = count_lines(beg_cur, screenbegin);
sc1:
2002-08-21 21:02:24 +08:00
screenbegin = beg_cur;
if (cnt > (rows - 1) / 2) {
// we moved too many lines. put "dot" in middle of screen
for (cnt = 0; cnt < (rows - 1) / 2; cnt++) {
screenbegin = prev_line(screenbegin);
}
}
2007-12-23 10:36:51 +08:00
} else {
char *end_scr; // begin and end of screen
end_scr = end_screen(); // last char of screen
if (beg_cur > end_scr) {
// "d" is after bottom line on screen
// how many lines do we have to move
cnt = count_lines(end_scr, beg_cur);
if (cnt > (rows - 1) / 2)
goto sc1; // too many lines
for (ro = 0; ro < cnt - 1; ro++) {
// move screen begin the same amount
screenbegin = next_line(screenbegin);
// now, move the end of screen
end_scr = next_line(end_scr);
end_scr = end_line(end_scr);
}
}
2002-08-21 21:02:24 +08:00
}
// "d" is on screen- find out which row
tp = screenbegin;
for (ro = 0; ro < rows - 1; ro++) { // drive "ro" to correct row
if (tp == beg_cur)
break;
tp = next_line(tp);
}
2002-08-21 21:02:24 +08:00
// find out what col "d" is on
co = 0;
while (tp < d) { // drive "co" to correct column
if (*tp == '\n') //vda || *tp == '\0')
2002-08-21 21:02:24 +08:00
break;
if (*tp == '\t') {
// handle tabs like real vi
if (d == tp && cmd_mode) {
vi: multiple fixes by Natanael Copa <natanael.copa@gmail.com> * the puzzling message on error is replaced with strerror(errno) so it should be even more detailed and smaller at the same time. * merged code in edit_file() and code for ':edit <file>' in colon() into new func init_text_buffer(). Was horribly duplicate. Moved most of error/sanity checking to file_insert(). Result is that you get a proper validation (prevent reading /dev/*) and error messages for ':r <file>' * renamed 'cfn' to 'current_filename' for improved readability * merged smallint vi_readonly and readonly into bitfields into readonly_mode to save space. * added text_size variable to keep track how big the text buffer is. This is used to fix a buffer overflow. To reproduce bug: ./busybox vi TODO :r Makefile vi segfaults due to no buffer checking is done at all. som redesign is needed here but i added a check in text_hole_make() to aviod the segfault at least. * removed isblnk() and use isblank(3) instead. * fixed compiler warning by displaying the return code for :!<command> This makes things bigger than needed but since the patch reduces the overall size... (see below) * new func next_tabstop(int) merges some duplicate code. There are more cuplicode here but i couldnt find a good way to merge them. * Fix *ANNOYING* placement of cursor on '\t' characters. To reproduce: echo -e "\thello" > file1 ./busybox vi file1 Try to insert some text at the beginning of line. Text will be inserted but cursor is blinking somewhere else. The patch should make busybox vi behave more like original vi(m). Costs a few bytes but its worth it imho. * new_text() is moved into init_text_buffer() * the previously added update_ro_status() was moved info file_insert due to duplication removal mentioned above. function old new delta init_text_buffer - 245 +245 file_insert 312 420 +108 next_tabstop - 82 +82 text_hole_make 154 171 +17 do_cmd 5093 5100 +7 static.cmd_mode_indicator - 5 +5 refresh 1248 1253 +5 current_filename - 4 +4 yank_delete 161 164 +3 what_reg 96 99 +3 end_cmd_q 78 81 +3 char_insert 440 442 +2 readonly_mode - 1 +1 vi_readonly 1 - -1 setops 154 153 -1 readonly 1 - -1 vi_setops 4 1 -3 string_insert 161 158 -3 cfn 4 - -4 show_status_line 532 514 -18 readit 519 500 -19 move_to_col 161 138 -23 vi_main 495 433 -62 isblnk 75 - -75 .rodata 4751 4655 -96 edit_file 892 787 -105 new_text 125 - -125 update_ro_status 131 - -131 colon 3848 3667 -181 ------------------------------------------------------------------------------ (add/remove: 5/6 grow/shrink: 8/10 up/down: 485/-848) Total: -363 bytes text data bss dec hex filename 34751 873 4260 39884 9bcc busybox_old 34439 877 4260 39576 9a98 busybox_unstripped
2007-07-18 07:14:07 +08:00
break;
}
2008-06-23 00:38:53 +08:00
co = next_tabstop(co);
} else if ((unsigned char)*tp < ' ' || *tp == 0x7f) {
co++; // display as ^X, use 2 columns
2002-08-21 21:02:24 +08:00
}
co++;
tp++;
}
2002-08-21 21:02:24 +08:00
// "co" is the column where "dot" is.
// The screen has "columns" columns.
// The currently displayed columns are 0+offset -- columns+ofset
// |-------------------------------------------------------------|
// ^ ^ ^
// offset | |------- columns ----------------|
//
// If "co" is already in this range then we do not have to adjust offset
// but, we do have to subtract the "offset" bias from "co".
// If "co" is outside this range then we have to change "offset".
// If the first char of a line is a tab the cursor will try to stay
// in column 7, but we have to set offset to 0.
if (co < 0 + offset) {
offset = co;
}
2002-08-21 21:02:24 +08:00
if (co >= columns + offset) {
offset = co - columns + 1;
}
2002-08-21 21:02:24 +08:00
// if the first char of the line is a tab, and "dot" is sitting on it
// force offset to 0.
if (d == beg_cur && *d == '\t') {
offset = 0;
}
co -= offset;
2002-08-21 21:02:24 +08:00
*row = ro;
*col = co;
}
2002-08-21 21:02:24 +08:00
//----- Text Movement Routines ---------------------------------
2007-12-23 10:36:51 +08:00
static char *begin_line(char *p) // return pointer to first char cur line
{
2007-12-23 10:36:51 +08:00
if (p > text) {
p = memrchr(text, '\n', p - text);
if (!p)
return text;
return p + 1;
}
return p;
2002-08-21 21:02:24 +08:00
}
2008-06-23 00:38:53 +08:00
static char *end_line(char *p) // return pointer to NL of cur line
2002-08-21 21:02:24 +08:00
{
2007-12-23 10:36:51 +08:00
if (p < end - 1) {
p = memchr(p, '\n', end - p - 1);
if (!p)
return end - 1;
}
return p;
}
2007-12-23 10:36:51 +08:00
static char *dollar_line(char *p) // return pointer to just before NL line
{
2007-12-23 10:36:51 +08:00
p = end_line(p);
2002-08-21 21:02:24 +08:00
// Try to stay off of the Newline
if (*p == '\n' && (p - begin_line(p)) > 0)
p--;
return p;
}
2007-12-23 10:36:51 +08:00
static char *prev_line(char *p) // return pointer first char prev line
{
2002-08-21 21:02:24 +08:00
p = begin_line(p); // goto begining of cur line
2008-06-23 00:38:53 +08:00
if (p > text && p[-1] == '\n')
2002-08-21 21:02:24 +08:00
p--; // step to prev line
p = begin_line(p); // goto begining of prev line
return p;
2002-08-21 21:02:24 +08:00
}
2007-12-23 10:36:51 +08:00
static char *next_line(char *p) // return pointer first char next line
2002-08-21 21:02:24 +08:00
{
p = end_line(p);
2008-06-23 00:38:53 +08:00
if (p < end - 1 && *p == '\n')
2002-08-21 21:02:24 +08:00
p++; // step to next line
return p;
2002-08-21 21:02:24 +08:00
}
2002-08-21 21:02:24 +08:00
//----- Text Information Routines ------------------------------
static char *end_screen(void)
2002-08-21 21:02:24 +08:00
{
char *q;
2002-08-21 21:02:24 +08:00
int cnt;
2002-08-21 21:02:24 +08:00
// find new bottom line
q = screenbegin;
for (cnt = 0; cnt < rows - 2; cnt++)
q = next_line(q);
q = end_line(q);
return q;
2002-08-21 21:02:24 +08:00
}
2007-12-23 10:36:51 +08:00
// count line from start to stop
static int count_lines(char *start, char *stop)
2002-08-21 21:02:24 +08:00
{
char *q;
2002-08-21 21:02:24 +08:00
int cnt;
2007-12-23 10:36:51 +08:00
if (stop < start) { // start and stop are backwards- reverse them
2002-08-21 21:02:24 +08:00
q = start;
start = stop;
stop = q;
}
2002-08-21 21:02:24 +08:00
cnt = 0;
2007-12-23 10:36:51 +08:00
stop = end_line(stop);
while (start <= stop && start <= end - 1) {
start = end_line(start);
if (*start == '\n')
2002-08-21 21:02:24 +08:00
cnt++;
2007-12-23 10:36:51 +08:00
start++;
}
return cnt;
2002-08-21 21:02:24 +08:00
}
static char *find_line(int li) // find begining of line #li
2002-08-21 21:02:24 +08:00
{
char *q;
2002-08-21 21:02:24 +08:00
for (q = text; li > 1; li--) {
q = next_line(q);
}
return q;
2002-08-21 21:02:24 +08:00
}
2002-08-21 21:02:24 +08:00
//----- Dot Movement Routines ----------------------------------
static void dot_left(void)
{
if (dot > text && dot[-1] != '\n')
dot--;
}
static void dot_right(void)
{
if (dot < end - 1 && *dot != '\n')
dot++;
}
static void dot_begin(void)
{
dot = begin_line(dot); // return pointer to first char cur line
}
static void dot_end(void)
{
dot = end_line(dot); // return pointer to last char cur line
}
static char *move_to_col(char *p, int l)
2002-08-21 21:02:24 +08:00
{
int co;
p = begin_line(p);
co = 0;
while (co < l && p < end) {
if (*p == '\n') //vda || *p == '\0')
2002-08-21 21:02:24 +08:00
break;
if (*p == '\t') {
vi: multiple fixes by Natanael Copa <natanael.copa@gmail.com> * the puzzling message on error is replaced with strerror(errno) so it should be even more detailed and smaller at the same time. * merged code in edit_file() and code for ':edit <file>' in colon() into new func init_text_buffer(). Was horribly duplicate. Moved most of error/sanity checking to file_insert(). Result is that you get a proper validation (prevent reading /dev/*) and error messages for ':r <file>' * renamed 'cfn' to 'current_filename' for improved readability * merged smallint vi_readonly and readonly into bitfields into readonly_mode to save space. * added text_size variable to keep track how big the text buffer is. This is used to fix a buffer overflow. To reproduce bug: ./busybox vi TODO :r Makefile vi segfaults due to no buffer checking is done at all. som redesign is needed here but i added a check in text_hole_make() to aviod the segfault at least. * removed isblnk() and use isblank(3) instead. * fixed compiler warning by displaying the return code for :!<command> This makes things bigger than needed but since the patch reduces the overall size... (see below) * new func next_tabstop(int) merges some duplicate code. There are more cuplicode here but i couldnt find a good way to merge them. * Fix *ANNOYING* placement of cursor on '\t' characters. To reproduce: echo -e "\thello" > file1 ./busybox vi file1 Try to insert some text at the beginning of line. Text will be inserted but cursor is blinking somewhere else. The patch should make busybox vi behave more like original vi(m). Costs a few bytes but its worth it imho. * new_text() is moved into init_text_buffer() * the previously added update_ro_status() was moved info file_insert due to duplication removal mentioned above. function old new delta init_text_buffer - 245 +245 file_insert 312 420 +108 next_tabstop - 82 +82 text_hole_make 154 171 +17 do_cmd 5093 5100 +7 static.cmd_mode_indicator - 5 +5 refresh 1248 1253 +5 current_filename - 4 +4 yank_delete 161 164 +3 what_reg 96 99 +3 end_cmd_q 78 81 +3 char_insert 440 442 +2 readonly_mode - 1 +1 vi_readonly 1 - -1 setops 154 153 -1 readonly 1 - -1 vi_setops 4 1 -3 string_insert 161 158 -3 cfn 4 - -4 show_status_line 532 514 -18 readit 519 500 -19 move_to_col 161 138 -23 vi_main 495 433 -62 isblnk 75 - -75 .rodata 4751 4655 -96 edit_file 892 787 -105 new_text 125 - -125 update_ro_status 131 - -131 colon 3848 3667 -181 ------------------------------------------------------------------------------ (add/remove: 5/6 grow/shrink: 8/10 up/down: 485/-848) Total: -363 bytes text data bss dec hex filename 34751 873 4260 39884 9bcc busybox_old 34439 877 4260 39576 9a98 busybox_unstripped
2007-07-18 07:14:07 +08:00
co = next_tabstop(co);
} else if (*p < ' ' || *p == 127) {
co++; // display as ^X, use 2 columns
}
co++;
p++;
}
return p;
2002-08-21 21:02:24 +08:00
}
2002-08-21 21:02:24 +08:00
static void dot_next(void)
{
dot = next_line(dot);
}
2002-08-21 21:02:24 +08:00
static void dot_prev(void)
{
dot = prev_line(dot);
}
2002-08-21 21:02:24 +08:00
static void dot_scroll(int cnt, int dir)
{
char *q;
2002-08-21 21:02:24 +08:00
for (; cnt > 0; cnt--) {
if (dir < 0) {
// scroll Backwards
2007-12-23 10:36:51 +08:00
// ctrl-Y scroll up one line
2002-08-21 21:02:24 +08:00
screenbegin = prev_line(screenbegin);
} else {
// scroll Forwards
2007-12-23 10:36:51 +08:00
// ctrl-E scroll down one line
2002-08-21 21:02:24 +08:00
screenbegin = next_line(screenbegin);
}
2002-08-21 21:02:24 +08:00
}
// make sure "dot" stays on the screen so we dont scroll off
if (dot < screenbegin)
dot = screenbegin;
q = end_screen(); // find new bottom line
if (dot > q)
dot = begin_line(q); // is dot is below bottom line?
dot_skip_over_ws();
}
static void dot_skip_over_ws(void)
{
// skip WS
while (isspace(*dot) && *dot != '\n' && dot < end - 1)
dot++;
}
static void dot_delete(void) // delete the char at 'dot'
{
text_hole_delete(dot, dot);
2002-08-21 21:02:24 +08:00
}
2007-12-23 10:36:51 +08:00
static char *bound_dot(char *p) // make sure text[0] <= P < "end"
2002-08-21 21:02:24 +08:00
{
if (p >= end && end > text) {
p = end - 1;
indicate_error('1');
}
if (p < text) {
p = text;
indicate_error('2');
}
return p;
2002-08-21 21:02:24 +08:00
}
//----- Helper Utility Routines --------------------------------
//----------------------------------------------------------------
//----- Char Routines --------------------------------------------
/* Chars that are part of a word-
* 0123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
* Chars that are Not part of a word (stoppers)
* !"#$%&'()*+,-./:;<=>?@[\]^`{|}~
* Chars that are WhiteSpace
* TAB NEWLINE VT FF RETURN SPACE
* DO NOT COUNT NEWLINE AS WHITESPACE
*/
static char *new_screen(int ro, int co)
2002-08-21 21:02:24 +08:00
{
int li;
2002-11-28 19:27:31 +08:00
free(screen);
2002-08-21 21:02:24 +08:00
screensize = ro * co + 8;
2006-12-20 07:36:04 +08:00
screen = xmalloc(screensize);
2002-08-21 21:02:24 +08:00
// initialize the new screen. assume this will be a empty file.
screen_erase();
// non-existent text[] lines start with a tilde (~).
2002-08-21 21:02:24 +08:00
for (li = 1; li < ro - 1; li++) {
screen[(li * co) + 0] = '~';
}
return screen;
2002-08-21 21:02:24 +08:00
}
#if ENABLE_FEATURE_VI_SEARCH
# if ENABLE_FEATURE_VI_REGEX_SEARCH
2002-08-21 21:02:24 +08:00
// search for pattern starting at p
static char *char_search(char *p, const char *pat, int dir, int range)
2002-08-21 21:02:24 +08:00
{
char *q;
struct re_pattern_buffer preg;
int i;
int size;
2002-08-21 21:02:24 +08:00
re_syntax_options = RE_SYNTAX_POSIX_EXTENDED;
preg.translate = 0;
preg.fastmap = 0;
preg.buffer = 0;
preg.allocated = 0;
2002-08-21 21:02:24 +08:00
// assume a LIMITED forward search
q = next_line(p);
q = end_line(q);
q = end - 1;
if (dir == BACK) {
q = prev_line(p);
q = text;
}
// count the number of chars to search over, forward or backward
size = q - p;
if (size < 0)
size = p - q;
// RANGE could be negative if we are searching backwards
range = q - p;
q = (char *)re_compile_pattern(pat, strlen(pat), (struct re_pattern_buffer *)&preg);
2002-08-21 21:02:24 +08:00
if (q != 0) {
// The pattern was not compiled
status_line_bold("bad search pattern: \"%s\": %s", pat, q);
2002-08-21 21:02:24 +08:00
i = 0; // return p if pattern not compiled
goto cs1;
}
q = p;
if (range < 0) {
q = p - size;
if (q < text)
q = text;
}
// search for the compiled pattern, preg, in p[]
// range < 0- search backward
// range > 0- search forward
// 0 < start < size
// re_search() < 0 not found or error
// re_search() > 0 index of found pattern
// struct pattern char int int int struct reg
// re_search (*pattern_buffer, *string, size, start, range, *regs)
i = re_search(&preg, q, size, 0, range, 0);
if (i == -1) {
p = 0;
i = 0; // return NULL if pattern not found
}
cs1:
2002-08-21 21:02:24 +08:00
if (dir == FORWARD) {
p = p + i;
} else {
p = p - i;
}
return p;
}
# else
# if ENABLE_FEATURE_VI_SETOPTS
static int mycmp(const char *s1, const char *s2, int len)
{
if (ignorecase) {
return strncasecmp(s1, s2, len);
}
return strncmp(s1, s2, len);
}
# else
# define mycmp strncmp
# endif
static char *char_search(char *p, const char *pat, int dir, int range)
{
char *start, *stop;
int len;
len = strlen(pat);
if (dir == FORWARD) {
stop = end - 1; // assume range is p - end-1
if (range == LIMITED)
stop = next_line(p); // range is to next line
for (start = p; start < stop; start++) {
if (mycmp(start, pat, len) == 0) {
return start;
}
}
} else if (dir == BACK) {
stop = text; // assume range is text - p
if (range == LIMITED)
stop = prev_line(p); // range is to prev line
for (start = p - len; start >= stop; start--) {
if (mycmp(start, pat, len) == 0) {
return start;
}
}
}
// pattern not found
return NULL;
}
# endif
#endif /* FEATURE_VI_SEARCH */
static char *char_insert(char *p, char c) // insert the char c at 'p'
{
2002-08-21 21:02:24 +08:00
if (c == 22) { // Is this an ctrl-V?
p += stupid_insert(p, '^'); // use ^ to indicate literal next
2002-08-21 21:02:24 +08:00
refresh(FALSE); // show the ^
c = get_one_char();
*p = c;
p++;
file_modified++;
2002-08-21 21:02:24 +08:00
} else if (c == 27) { // Is this an ESC?
cmd_mode = 0;
cmdcnt = 0;
end_cmd_q(); // stop adding to q
last_status_cksum = 0; // force status update
if ((p[-1] != '\n') && (dot > text)) {
2002-08-21 21:02:24 +08:00
p--;
}
} else if (c == erase_char || c == 8 || c == 127) { // Is this a BS
2002-08-21 21:02:24 +08:00
// 123456789
if ((p[-1] != '\n') && (dot>text)) {
2002-08-21 21:02:24 +08:00
p--;
p = text_hole_delete(p, p); // shrink buffer 1 char
}
2002-08-21 21:02:24 +08:00
} else {
#if ENABLE_FEATURE_VI_SETOPTS
2002-08-21 21:02:24 +08:00
// insert a char into text[]
char *sp; // "save p"
#endif
2002-08-21 21:02:24 +08:00
if (c == 13)
c = '\n'; // translate \r to \n
#if ENABLE_FEATURE_VI_SETOPTS
2002-08-21 21:02:24 +08:00
sp = p; // remember addr of insert
#endif
p += 1 + stupid_insert(p, c); // insert the char
#if ENABLE_FEATURE_VI_SETOPTS
2002-08-21 21:02:24 +08:00
if (showmatch && strchr(")]}", *sp) != NULL) {
showmatching(sp);
}
if (autoindent && c == '\n') { // auto indent the new line
char *q;
size_t len;
q = prev_line(p); // use prev line as template
len = strspn(q, " \t"); // space or tab
if (len) {
2009-04-01 19:24:04 +08:00
uintptr_t bias;
bias = text_hole_make(p, len);
p += bias;
q += bias;
memcpy(p, q, len);
2009-04-01 19:24:04 +08:00
p += len;
2002-08-21 21:02:24 +08:00
}
}
#endif
}
return p;
}
// might reallocate text[]! use p += stupid_insert(p, ...),
// and be careful to not use pointers into potentially freed text[]!
static uintptr_t stupid_insert(char *p, char c) // stupidly insert the char c at 'p'
{
uintptr_t bias;
bias = text_hole_make(p, 1);
p += bias;
*p = c;
//file_modified++; - done by text_hole_make()
return bias;
}
static int find_range(char **start, char **stop, char c)
{
char *save_dot, *p, *q, *t;
int cnt, multiline = 0;
2002-08-21 21:02:24 +08:00
save_dot = dot;
p = q = dot;
2002-08-21 21:02:24 +08:00
if (strchr("cdy><", c)) {
// these cmds operate on whole lines
p = q = begin_line(p);
for (cnt = 1; cnt < cmdcnt; cnt++) {
q = next_line(q);
}
q = end_line(q);
} else if (strchr("^%$0bBeEfth\b\177", c)) {
2002-08-21 21:02:24 +08:00
// These cmds operate on char positions
do_cmd(c); // execute movement cmd
q = dot;
} else if (strchr("wW", c)) {
do_cmd(c); // execute movement cmd
2006-01-25 08:08:53 +08:00
// if we are at the next word's first char
// step back one char
// but check the possibilities when it is true
if (dot > text && ((isspace(dot[-1]) && !isspace(dot[0]))
2006-01-25 08:08:53 +08:00
|| (ispunct(dot[-1]) && !ispunct(dot[0]))
|| (isalnum(dot[-1]) && !isalnum(dot[0]))))
dot--; // move back off of next word
2002-08-21 21:02:24 +08:00
if (dot > text && *dot == '\n')
dot--; // stay off NL
q = dot;
} else if (strchr("H-k{", c)) {
// these operate on multi-lines backwards
q = end_line(dot); // find NL
do_cmd(c); // execute movement cmd
dot_begin();
p = dot;
} else if (strchr("L+j}\r\n", c)) {
// these operate on multi-lines forwards
p = begin_line(dot);
do_cmd(c); // execute movement cmd
dot_end(); // find NL
q = dot;
} else {
// nothing -- this causes any other values of c to
// represent the one-character range under the
// cursor. this is correct for ' ' and 'l', but
// perhaps no others.
//
2002-08-21 21:02:24 +08:00
}
if (q < p) {
t = q;
q = p;
p = t;
2002-08-21 21:02:24 +08:00
}
// backward char movements don't include start position
if (q > p && strchr("^0bBh\b\177", c)) q--;
multiline = 0;
for (t = p; t <= q; t++) {
if (*t == '\n') {
multiline = 1;
break;
}
}
*start = p;
*stop = q;
2002-08-21 21:02:24 +08:00
dot = save_dot;
return multiline;
}
static int st_test(char *p, int type, int dir, char *tested)
{
char c, c0, ci;
2002-08-21 21:02:24 +08:00
int test, inc;
2002-08-21 21:02:24 +08:00
inc = dir;
c = c0 = p[0];
ci = p[inc];
test = 0;
2002-08-21 21:02:24 +08:00
if (type == S_BEFORE_WS) {
c = ci;
test = (!isspace(c) || c == '\n');
}
2002-08-21 21:02:24 +08:00
if (type == S_TO_WS) {
c = c0;
test = (!isspace(c) || c == '\n');
}
2002-08-21 21:02:24 +08:00
if (type == S_OVER_WS) {
c = c0;
test = isspace(c);
2002-08-21 21:02:24 +08:00
}
if (type == S_END_PUNCT) {
c = ci;
test = ispunct(c);
2002-08-21 21:02:24 +08:00
}
if (type == S_END_ALNUM) {
c = ci;
test = (isalnum(c) || c == '_');
2002-08-21 21:02:24 +08:00
}
*tested = c;
return test;
}
static char *skip_thing(char *p, int linecnt, int dir, int type)
{
char c;
2002-08-21 21:02:24 +08:00
while (st_test(p, type, dir, &c)) {
// make sure we limit search to correct number of lines
if (c == '\n' && --linecnt < 1)
break;
if (dir >= 0 && p >= end - 1)
break;
if (dir < 0 && p <= text)
break;
p += dir; // move to next char
}
return p;
}
2002-08-21 21:02:24 +08:00
// find matching char of pair () [] {}
static char *find_pair(char *p, const char c)
{
char match, *q;
2002-08-21 21:02:24 +08:00
int dir, level;
2002-08-21 21:02:24 +08:00
match = ')';
level = 1;
dir = 1; // assume forward
switch (c) {
case '(': match = ')'; break;
case '[': match = ']'; break;
case '{': match = '}'; break;
case ')': match = '('; dir = -1; break;
case ']': match = '['; dir = -1; break;
case '}': match = '{'; dir = -1; break;
2002-08-21 21:02:24 +08:00
}
for (q = p + dir; text <= q && q < end; q += dir) {
// look for match, count levels of pairs (( ))
if (*q == c)
level++; // increase pair levels
if (*q == match)
level--; // reduce pair level
if (level == 0)
break; // found matching pair
}
if (level != 0)
q = NULL; // indicate no match
return q;
}
#if ENABLE_FEATURE_VI_SETOPTS
2002-08-21 21:02:24 +08:00
// show the matching char of a pair, () [] {}
2007-12-23 10:36:51 +08:00
static void showmatching(char *p)
{
char *q, *save_dot;
2002-08-21 21:02:24 +08:00
// we found half of a pair
q = find_pair(p, *p); // get loc of matching char
if (q == NULL) {
indicate_error('3'); // no matching char
} else {
// "q" now points to matching pair
save_dot = dot; // remember where we are
dot = q; // go to new loc
refresh(FALSE); // let the user see it
mysleep(40); // give user some time
2002-08-21 21:02:24 +08:00
dot = save_dot; // go back to old loc
refresh(FALSE);
}
}
#endif /* FEATURE_VI_SETOPTS */
// open a hole in text[]
// might reallocate text[]! use p += text_hole_make(p, ...),
// and be careful to not use pointers into potentially freed text[]!
static uintptr_t text_hole_make(char *p, int size) // at "p", make a 'size' byte hole
{
uintptr_t bias = 0;
2002-08-21 21:02:24 +08:00
if (size <= 0)
return bias;
end += size; // adjust the new END
if (end >= (text + text_size)) {
char *new_text;
text_size += end - (text + text_size) + 10240;
new_text = xrealloc(text, text_size);
bias = (new_text - text);
screenbegin += bias;
dot += bias;
end += bias;
p += bias;
#if ENABLE_FEATURE_VI_YANKMARK
{
int i;
for (i = 0; i < ARRAY_SIZE(mark); i++)
if (mark[i])
mark[i] += bias;
}
#endif
text = new_text;
2002-08-21 21:02:24 +08:00
}
2008-06-27 12:06:13 +08:00
memmove(p + size, p, end - size - p);
2002-08-21 21:02:24 +08:00
memset(p, ' ', size); // clear new hole
file_modified++;
return bias;
}
2002-08-21 21:02:24 +08:00
// close a hole in text[]
static char *text_hole_delete(char *p, char *q) // delete "p" through "q", inclusive
{
char *src, *dest;
2002-08-21 21:02:24 +08:00
int cnt, hole_size;
2002-08-21 21:02:24 +08:00
// move forwards, from beginning
// assume p <= q
src = q + 1;
dest = p;
if (q < p) { // they are backward- swap them
src = p + 1;
dest = q;
}
hole_size = q - p + 1;
cnt = end - src;
if (src < text || src > end)
goto thd0;
if (dest < text || dest >= end)
goto thd0;
if (src >= end)
goto thd_atend; // just delete the end of the buffer
memmove(dest, src, cnt);
thd_atend:
2002-08-21 21:02:24 +08:00
end = end - hole_size; // adjust the new END
if (dest >= end)
dest = end - 1; // make sure dest in below end-1
if (end <= text)
dest = end = text; // keep pointers valid
file_modified++;
thd0:
return dest;
}
2002-08-21 21:02:24 +08:00
// copy text into register, then delete text.
// if dist <= 0, do not include, or go past, a NewLine
//
static char *yank_delete(char *start, char *stop, int dist, int yf)
{
char *p;
2002-08-21 21:02:24 +08:00
// make sure start <= stop
if (start > stop) {
// they are backwards, reverse them
p = start;
start = stop;
stop = p;
}
if (dist <= 0) {
// we cannot cross NL boundaries
2002-08-21 21:02:24 +08:00
p = start;
if (*p == '\n')
return p;
2002-08-21 21:02:24 +08:00
// dont go past a NewLine
for (; p + 1 <= stop; p++) {
if (p[1] == '\n') {
stop = p; // "stop" just before NewLine
break;
}
}
}
2002-08-21 21:02:24 +08:00
p = start;
#if ENABLE_FEATURE_VI_YANKMARK
2002-08-21 21:02:24 +08:00
text_yank(start, stop, YDreg);
#endif
2002-08-21 21:02:24 +08:00
if (yf == YANKDEL) {
p = text_hole_delete(start, stop);
} // delete lines
return p;
}
2002-08-21 21:02:24 +08:00
static void show_help(void)
{
2002-08-21 21:02:24 +08:00
puts("These features are available:"
#if ENABLE_FEATURE_VI_SEARCH
2002-08-21 21:02:24 +08:00
"\n\tPattern searches with / and ?"
#endif
#if ENABLE_FEATURE_VI_DOT_CMD
2002-08-21 21:02:24 +08:00
"\n\tLast command repeat with \'.\'"
#endif
#if ENABLE_FEATURE_VI_YANKMARK
2007-10-02 02:04:42 +08:00
"\n\tLine marking with 'x"
"\n\tNamed buffers with \"x"
#endif
#if ENABLE_FEATURE_VI_READONLY
//not implemented: "\n\tReadonly if vi is called as \"view\""
//redundant: usage text says this too: "\n\tReadonly with -R command line arg"
#endif
#if ENABLE_FEATURE_VI_SET
2002-08-21 21:02:24 +08:00
"\n\tSome colon mode commands with \':\'"
#endif
#if ENABLE_FEATURE_VI_SETOPTS
2002-08-21 21:02:24 +08:00
"\n\tSettable options with \":set\""
#endif
#if ENABLE_FEATURE_VI_USE_SIGNALS
2002-08-21 21:02:24 +08:00
"\n\tSignal catching- ^C"
"\n\tJob suspend and resume with ^Z"
#endif
#if ENABLE_FEATURE_VI_WIN_RESIZE
2002-08-21 21:02:24 +08:00
"\n\tAdapt to window re-sizes"
#endif
2002-08-21 21:02:24 +08:00
);
}
#if ENABLE_FEATURE_VI_DOT_CMD
static void start_new_cmd_q(char c)
2002-08-21 21:02:24 +08:00
{
// get buffer for new cmd
// if there is a current cmd count put it in the buffer first
if (cmdcnt > 0) {
lmc_len = sprintf(last_modifying_cmd, "%d%c", cmdcnt, c);
} else { // just save char c onto queue
last_modifying_cmd[0] = c;
lmc_len = 1;
}
2002-08-21 21:02:24 +08:00
adding2q = 1;
}
2002-08-21 21:02:24 +08:00
static void end_cmd_q(void)
{
#if ENABLE_FEATURE_VI_YANKMARK
2002-08-21 21:02:24 +08:00
YDreg = 26; // go back to default Yank/Delete reg
#endif
2002-08-21 21:02:24 +08:00
adding2q = 0;
}
#endif /* FEATURE_VI_DOT_CMD */
#if ENABLE_FEATURE_VI_YANKMARK \
|| (ENABLE_FEATURE_VI_COLON && ENABLE_FEATURE_VI_SEARCH) \
|| ENABLE_FEATURE_VI_CRASHME
// might reallocate text[]! use p += string_insert(p, ...),
// and be careful to not use pointers into potentially freed text[]!
static uintptr_t string_insert(char *p, const char *s) // insert the string at 'p'
2002-08-21 21:02:24 +08:00
{
uintptr_t bias;
int i;
2002-08-21 21:02:24 +08:00
i = strlen(s);
bias = text_hole_make(p, i);
p += bias;
memcpy(p, s, i);
#if ENABLE_FEATURE_VI_YANKMARK
{
int cnt;
for (cnt = 0; *s != '\0'; s++) {
if (*s == '\n')
cnt++;
}
status_line("Put %d lines (%d chars) from [%c]", cnt, i, what_reg());
}
#endif
return bias;
}
#endif
#if ENABLE_FEATURE_VI_YANKMARK
static char *text_yank(char *p, char *q, int dest) // copy text into a register
{
int cnt = q - p;
if (cnt < 0) { // they are backwards- reverse them
p = q;
cnt = -cnt;
}
free(reg[dest]); // if already a yank register, free it
reg[dest] = xstrndup(p, cnt + 1);
return p;
}
static char what_reg(void)
{
char c;
2002-08-21 21:02:24 +08:00
c = 'D'; // default to D-reg
if (0 <= YDreg && YDreg <= 25)
c = 'a' + (char) YDreg;
2002-08-21 21:02:24 +08:00
if (YDreg == 26)
c = 'D';
if (YDreg == 27)
c = 'U';
return c;
}
static void check_context(char cmd)
{
2002-08-21 21:02:24 +08:00
// A context is defined to be "modifying text"
// Any modifying command establishes a new context.
2002-08-21 21:02:24 +08:00
if (dot < context_start || dot > context_end) {
if (strchr(modifying_cmds, cmd) != NULL) {
2002-08-21 21:02:24 +08:00
// we are trying to modify text[]- make this the current context
mark[27] = mark[26]; // move cur to prev
mark[26] = dot; // move local to cur
context_start = prev_line(prev_line(dot));
context_end = next_line(next_line(dot));
//loiter= start_loiter= now;
}
}
2002-08-21 21:02:24 +08:00
}
2007-12-23 10:36:51 +08:00
static char *swap_context(char *p) // goto new context for '' command make this the current context
2002-08-21 21:02:24 +08:00
{
char *tmp;
2002-08-21 21:02:24 +08:00
// the current context is in mark[26]
// the previous context is in mark[27]
// only swap context if other context is valid
if (text <= mark[27] && mark[27] <= end - 1) {
tmp = mark[27];
mark[27] = mark[26];
mark[26] = tmp;
p = mark[26]; // where we are going- previous context
context_start = prev_line(prev_line(prev_line(p)));
context_end = next_line(next_line(next_line(p)));
}
return p;
}
#endif /* FEATURE_VI_YANKMARK */
2002-08-21 21:02:24 +08:00
//----- Set terminal attributes --------------------------------
static void rawmode(void)
{
2002-08-21 21:02:24 +08:00
tcgetattr(0, &term_orig);
term_vi = term_orig;
term_vi.c_lflag &= (~ICANON & ~ECHO); // leave ISIG on - allow intr's
2002-08-21 21:02:24 +08:00
term_vi.c_iflag &= (~IXON & ~ICRNL);
term_vi.c_oflag &= (~ONLCR);
term_vi.c_cc[VMIN] = 1;
term_vi.c_cc[VTIME] = 0;
erase_char = term_vi.c_cc[VERASE];
tcsetattr_stdin_TCSANOW(&term_vi);
}
2002-08-21 21:02:24 +08:00
static void cookmode(void)
{
fflush_all();
tcsetattr_stdin_TCSANOW(&term_orig);
}
#if ENABLE_FEATURE_VI_USE_SIGNALS
//----- Come here when we get a window resize signal ---------
2008-07-05 17:18:54 +08:00
static void winch_sig(int sig UNUSED_PARAM)
{
int save_errno = errno;
// FIXME: do it in main loop!!!
2002-08-21 21:02:24 +08:00
signal(SIGWINCH, winch_sig);
query_screen_dimensions();
2002-08-21 21:02:24 +08:00
new_screen(rows, columns); // get memory for virtual screen
redraw(TRUE); // re-draw the screen
errno = save_errno;
}
2002-08-21 21:02:24 +08:00
//----- Come here when we get a continue signal -------------------
2008-07-05 17:18:54 +08:00
static void cont_sig(int sig UNUSED_PARAM)
{
int save_errno = errno;
rawmode(); // terminal to "raw"
last_status_cksum = 0; // force status update
redraw(TRUE); // re-draw the screen
2002-08-21 21:02:24 +08:00
signal(SIGTSTP, suspend_sig);
signal(SIGCONT, SIG_DFL);
//kill(my_pid, SIGCONT); // huh? why? we are already "continued"...
errno = save_errno;
}
2002-08-21 21:02:24 +08:00
//----- Come here when we get a Suspend signal -------------------
2008-07-05 17:18:54 +08:00
static void suspend_sig(int sig UNUSED_PARAM)
{
int save_errno = errno;
go_bottom_and_clear_to_eol();
cookmode(); // terminal to "cooked"
2002-08-21 21:02:24 +08:00
signal(SIGCONT, cont_sig);
signal(SIGTSTP, SIG_DFL);
kill(my_pid, SIGTSTP);
errno = save_errno;
}
2002-08-21 21:02:24 +08:00
//----- Come here when we get a signal ---------------------------
static void catch_sig(int sig)
{
2002-08-21 21:02:24 +08:00
signal(SIGINT, catch_sig);
siglongjmp(restart, sig);
}
#endif /* FEATURE_VI_USE_SIGNALS */
static int mysleep(int hund) // sleep for 'hund' 1/100 seconds or stdin ready
{
struct pollfd pfd[1];
pfd[0].fd = STDIN_FILENO;
pfd[0].events = POLLIN;
return safe_poll(pfd, 1, hund*10) > 0;
}
2002-08-21 21:02:24 +08:00
//----- IO Routines --------------------------------------------
static int readit(void) // read (maybe cursor) key from stdin
{
int c;
fflush_all();
c = read_key(STDIN_FILENO, readbuffer, /*timeout off:*/ -2);
if (c == -1) { // EOF/error
go_bottom_and_clear_to_eol();
cookmode(); // terminal to "cooked"
bb_error_msg_and_die("can't read user input");
}
return c;
}
2002-08-21 21:02:24 +08:00
//----- IO Routines --------------------------------------------
static int get_one_char(void)
{
int c;
#if ENABLE_FEATURE_VI_DOT_CMD
2002-08-21 21:02:24 +08:00
if (!adding2q) {
// we are not adding to the q.
// but, we may be reading from a q
if (ioq == 0) {
// there is no current q, read from STDIN
c = readit(); // get the users input
} else {
// there is a queue to get chars from first
// careful with correct sign expansion!
c = (unsigned char)*ioq++;
2002-08-21 21:02:24 +08:00
if (c == '\0') {
// the end of the q, read from STDIN
free(ioq_start);
ioq_start = ioq = 0;
c = readit(); // get the users input
}
}
} else {
// adding STDIN chars to q
c = readit(); // get the users input
if (lmc_len >= MAX_INPUT_LEN - 1) {
status_line_bold("last_modifying_cmd overrun");
} else {
// add new char to q
last_modifying_cmd[lmc_len++] = c;
}
}
#else
2002-08-21 21:02:24 +08:00
c = readit(); // get the users input
#endif /* FEATURE_VI_DOT_CMD */
return c;
}
// Get input line (uses "status line" area)
static char *get_input_line(const char *prompt)
{
// char [MAX_INPUT_LEN]
#define buf get_input_line__buf
int c;
2002-08-21 21:02:24 +08:00
int i;
strcpy(buf, prompt);
last_status_cksum = 0; // force status update
go_bottom_and_clear_to_eol();
write1(prompt); // write out the :, /, or ? prompt
i = strlen(buf);
while (i < MAX_INPUT_LEN) {
c = get_one_char();
2002-08-21 21:02:24 +08:00
if (c == '\n' || c == '\r' || c == 27)
break; // this is end of input
if (c == erase_char || c == 8 || c == 127) {
2006-01-25 08:08:53 +08:00
// user wants to erase prev char
buf[--i] = '\0';
write1("\b \b"); // erase char on screen
if (i <= 0) // user backs up before b-o-l, exit
2002-08-21 21:02:24 +08:00
break;
} else if (c > 0 && c < 256) { // exclude Unicode
// (TODO: need to handle Unicode)
buf[i] = c;
buf[++i] = '\0';
bb_putchar(c);
2002-08-21 21:02:24 +08:00
}
}
refresh(FALSE);
return buf;
#undef buf
}
vi: multiple fixes by Natanael Copa <natanael.copa@gmail.com> * the puzzling message on error is replaced with strerror(errno) so it should be even more detailed and smaller at the same time. * merged code in edit_file() and code for ':edit <file>' in colon() into new func init_text_buffer(). Was horribly duplicate. Moved most of error/sanity checking to file_insert(). Result is that you get a proper validation (prevent reading /dev/*) and error messages for ':r <file>' * renamed 'cfn' to 'current_filename' for improved readability * merged smallint vi_readonly and readonly into bitfields into readonly_mode to save space. * added text_size variable to keep track how big the text buffer is. This is used to fix a buffer overflow. To reproduce bug: ./busybox vi TODO :r Makefile vi segfaults due to no buffer checking is done at all. som redesign is needed here but i added a check in text_hole_make() to aviod the segfault at least. * removed isblnk() and use isblank(3) instead. * fixed compiler warning by displaying the return code for :!<command> This makes things bigger than needed but since the patch reduces the overall size... (see below) * new func next_tabstop(int) merges some duplicate code. There are more cuplicode here but i couldnt find a good way to merge them. * Fix *ANNOYING* placement of cursor on '\t' characters. To reproduce: echo -e "\thello" > file1 ./busybox vi file1 Try to insert some text at the beginning of line. Text will be inserted but cursor is blinking somewhere else. The patch should make busybox vi behave more like original vi(m). Costs a few bytes but its worth it imho. * new_text() is moved into init_text_buffer() * the previously added update_ro_status() was moved info file_insert due to duplication removal mentioned above. function old new delta init_text_buffer - 245 +245 file_insert 312 420 +108 next_tabstop - 82 +82 text_hole_make 154 171 +17 do_cmd 5093 5100 +7 static.cmd_mode_indicator - 5 +5 refresh 1248 1253 +5 current_filename - 4 +4 yank_delete 161 164 +3 what_reg 96 99 +3 end_cmd_q 78 81 +3 char_insert 440 442 +2 readonly_mode - 1 +1 vi_readonly 1 - -1 setops 154 153 -1 readonly 1 - -1 vi_setops 4 1 -3 string_insert 161 158 -3 cfn 4 - -4 show_status_line 532 514 -18 readit 519 500 -19 move_to_col 161 138 -23 vi_main 495 433 -62 isblnk 75 - -75 .rodata 4751 4655 -96 edit_file 892 787 -105 new_text 125 - -125 update_ro_status 131 - -131 colon 3848 3667 -181 ------------------------------------------------------------------------------ (add/remove: 5/6 grow/shrink: 8/10 up/down: 485/-848) Total: -363 bytes text data bss dec hex filename 34751 873 4260 39884 9bcc busybox_old 34439 877 4260 39576 9a98 busybox_unstripped
2007-07-18 07:14:07 +08:00
static int file_size(const char *fn) // what is the byte size of "fn"
{
2002-08-21 21:02:24 +08:00
struct stat st_buf;
int cnt;
2002-08-21 21:02:24 +08:00
cnt = -1;
if (fn && stat(fn, &st_buf) == 0) // see if file exists
2002-08-21 21:02:24 +08:00
cnt = (int) st_buf.st_size;
return cnt;
}
// might reallocate text[]!
static int file_insert(const char *fn, char *p, int update_ro_status)
{
int cnt = -1;
int fd, size;
vi: multiple fixes by Natanael Copa <natanael.copa@gmail.com> * the puzzling message on error is replaced with strerror(errno) so it should be even more detailed and smaller at the same time. * merged code in edit_file() and code for ':edit <file>' in colon() into new func init_text_buffer(). Was horribly duplicate. Moved most of error/sanity checking to file_insert(). Result is that you get a proper validation (prevent reading /dev/*) and error messages for ':r <file>' * renamed 'cfn' to 'current_filename' for improved readability * merged smallint vi_readonly and readonly into bitfields into readonly_mode to save space. * added text_size variable to keep track how big the text buffer is. This is used to fix a buffer overflow. To reproduce bug: ./busybox vi TODO :r Makefile vi segfaults due to no buffer checking is done at all. som redesign is needed here but i added a check in text_hole_make() to aviod the segfault at least. * removed isblnk() and use isblank(3) instead. * fixed compiler warning by displaying the return code for :!<command> This makes things bigger than needed but since the patch reduces the overall size... (see below) * new func next_tabstop(int) merges some duplicate code. There are more cuplicode here but i couldnt find a good way to merge them. * Fix *ANNOYING* placement of cursor on '\t' characters. To reproduce: echo -e "\thello" > file1 ./busybox vi file1 Try to insert some text at the beginning of line. Text will be inserted but cursor is blinking somewhere else. The patch should make busybox vi behave more like original vi(m). Costs a few bytes but its worth it imho. * new_text() is moved into init_text_buffer() * the previously added update_ro_status() was moved info file_insert due to duplication removal mentioned above. function old new delta init_text_buffer - 245 +245 file_insert 312 420 +108 next_tabstop - 82 +82 text_hole_make 154 171 +17 do_cmd 5093 5100 +7 static.cmd_mode_indicator - 5 +5 refresh 1248 1253 +5 current_filename - 4 +4 yank_delete 161 164 +3 what_reg 96 99 +3 end_cmd_q 78 81 +3 char_insert 440 442 +2 readonly_mode - 1 +1 vi_readonly 1 - -1 setops 154 153 -1 readonly 1 - -1 vi_setops 4 1 -3 string_insert 161 158 -3 cfn 4 - -4 show_status_line 532 514 -18 readit 519 500 -19 move_to_col 161 138 -23 vi_main 495 433 -62 isblnk 75 - -75 .rodata 4751 4655 -96 edit_file 892 787 -105 new_text 125 - -125 update_ro_status 131 - -131 colon 3848 3667 -181 ------------------------------------------------------------------------------ (add/remove: 5/6 grow/shrink: 8/10 up/down: 485/-848) Total: -363 bytes text data bss dec hex filename 34751 873 4260 39884 9bcc busybox_old 34439 877 4260 39576 9a98 busybox_unstripped
2007-07-18 07:14:07 +08:00
struct stat statbuf;
/* Validate file */
if (stat(fn, &statbuf) < 0) {
status_line_bold("\"%s\" %s", fn, strerror(errno));
vi: multiple fixes by Natanael Copa <natanael.copa@gmail.com> * the puzzling message on error is replaced with strerror(errno) so it should be even more detailed and smaller at the same time. * merged code in edit_file() and code for ':edit <file>' in colon() into new func init_text_buffer(). Was horribly duplicate. Moved most of error/sanity checking to file_insert(). Result is that you get a proper validation (prevent reading /dev/*) and error messages for ':r <file>' * renamed 'cfn' to 'current_filename' for improved readability * merged smallint vi_readonly and readonly into bitfields into readonly_mode to save space. * added text_size variable to keep track how big the text buffer is. This is used to fix a buffer overflow. To reproduce bug: ./busybox vi TODO :r Makefile vi segfaults due to no buffer checking is done at all. som redesign is needed here but i added a check in text_hole_make() to aviod the segfault at least. * removed isblnk() and use isblank(3) instead. * fixed compiler warning by displaying the return code for :!<command> This makes things bigger than needed but since the patch reduces the overall size... (see below) * new func next_tabstop(int) merges some duplicate code. There are more cuplicode here but i couldnt find a good way to merge them. * Fix *ANNOYING* placement of cursor on '\t' characters. To reproduce: echo -e "\thello" > file1 ./busybox vi file1 Try to insert some text at the beginning of line. Text will be inserted but cursor is blinking somewhere else. The patch should make busybox vi behave more like original vi(m). Costs a few bytes but its worth it imho. * new_text() is moved into init_text_buffer() * the previously added update_ro_status() was moved info file_insert due to duplication removal mentioned above. function old new delta init_text_buffer - 245 +245 file_insert 312 420 +108 next_tabstop - 82 +82 text_hole_make 154 171 +17 do_cmd 5093 5100 +7 static.cmd_mode_indicator - 5 +5 refresh 1248 1253 +5 current_filename - 4 +4 yank_delete 161 164 +3 what_reg 96 99 +3 end_cmd_q 78 81 +3 char_insert 440 442 +2 readonly_mode - 1 +1 vi_readonly 1 - -1 setops 154 153 -1 readonly 1 - -1 vi_setops 4 1 -3 string_insert 161 158 -3 cfn 4 - -4 show_status_line 532 514 -18 readit 519 500 -19 move_to_col 161 138 -23 vi_main 495 433 -62 isblnk 75 - -75 .rodata 4751 4655 -96 edit_file 892 787 -105 new_text 125 - -125 update_ro_status 131 - -131 colon 3848 3667 -181 ------------------------------------------------------------------------------ (add/remove: 5/6 grow/shrink: 8/10 up/down: 485/-848) Total: -363 bytes text data bss dec hex filename 34751 873 4260 39884 9bcc busybox_old 34439 877 4260 39576 9a98 busybox_unstripped
2007-07-18 07:14:07 +08:00
goto fi0;
}
if (!S_ISREG(statbuf.st_mode)) {
vi: multiple fixes by Natanael Copa <natanael.copa@gmail.com> * the puzzling message on error is replaced with strerror(errno) so it should be even more detailed and smaller at the same time. * merged code in edit_file() and code for ':edit <file>' in colon() into new func init_text_buffer(). Was horribly duplicate. Moved most of error/sanity checking to file_insert(). Result is that you get a proper validation (prevent reading /dev/*) and error messages for ':r <file>' * renamed 'cfn' to 'current_filename' for improved readability * merged smallint vi_readonly and readonly into bitfields into readonly_mode to save space. * added text_size variable to keep track how big the text buffer is. This is used to fix a buffer overflow. To reproduce bug: ./busybox vi TODO :r Makefile vi segfaults due to no buffer checking is done at all. som redesign is needed here but i added a check in text_hole_make() to aviod the segfault at least. * removed isblnk() and use isblank(3) instead. * fixed compiler warning by displaying the return code for :!<command> This makes things bigger than needed but since the patch reduces the overall size... (see below) * new func next_tabstop(int) merges some duplicate code. There are more cuplicode here but i couldnt find a good way to merge them. * Fix *ANNOYING* placement of cursor on '\t' characters. To reproduce: echo -e "\thello" > file1 ./busybox vi file1 Try to insert some text at the beginning of line. Text will be inserted but cursor is blinking somewhere else. The patch should make busybox vi behave more like original vi(m). Costs a few bytes but its worth it imho. * new_text() is moved into init_text_buffer() * the previously added update_ro_status() was moved info file_insert due to duplication removal mentioned above. function old new delta init_text_buffer - 245 +245 file_insert 312 420 +108 next_tabstop - 82 +82 text_hole_make 154 171 +17 do_cmd 5093 5100 +7 static.cmd_mode_indicator - 5 +5 refresh 1248 1253 +5 current_filename - 4 +4 yank_delete 161 164 +3 what_reg 96 99 +3 end_cmd_q 78 81 +3 char_insert 440 442 +2 readonly_mode - 1 +1 vi_readonly 1 - -1 setops 154 153 -1 readonly 1 - -1 vi_setops 4 1 -3 string_insert 161 158 -3 cfn 4 - -4 show_status_line 532 514 -18 readit 519 500 -19 move_to_col 161 138 -23 vi_main 495 433 -62 isblnk 75 - -75 .rodata 4751 4655 -96 edit_file 892 787 -105 new_text 125 - -125 update_ro_status 131 - -131 colon 3848 3667 -181 ------------------------------------------------------------------------------ (add/remove: 5/6 grow/shrink: 8/10 up/down: 485/-848) Total: -363 bytes text data bss dec hex filename 34751 873 4260 39884 9bcc busybox_old 34439 877 4260 39576 9a98 busybox_unstripped
2007-07-18 07:14:07 +08:00
// This is not a regular file
status_line_bold("\"%s\" Not a regular file", fn);
vi: multiple fixes by Natanael Copa <natanael.copa@gmail.com> * the puzzling message on error is replaced with strerror(errno) so it should be even more detailed and smaller at the same time. * merged code in edit_file() and code for ':edit <file>' in colon() into new func init_text_buffer(). Was horribly duplicate. Moved most of error/sanity checking to file_insert(). Result is that you get a proper validation (prevent reading /dev/*) and error messages for ':r <file>' * renamed 'cfn' to 'current_filename' for improved readability * merged smallint vi_readonly and readonly into bitfields into readonly_mode to save space. * added text_size variable to keep track how big the text buffer is. This is used to fix a buffer overflow. To reproduce bug: ./busybox vi TODO :r Makefile vi segfaults due to no buffer checking is done at all. som redesign is needed here but i added a check in text_hole_make() to aviod the segfault at least. * removed isblnk() and use isblank(3) instead. * fixed compiler warning by displaying the return code for :!<command> This makes things bigger than needed but since the patch reduces the overall size... (see below) * new func next_tabstop(int) merges some duplicate code. There are more cuplicode here but i couldnt find a good way to merge them. * Fix *ANNOYING* placement of cursor on '\t' characters. To reproduce: echo -e "\thello" > file1 ./busybox vi file1 Try to insert some text at the beginning of line. Text will be inserted but cursor is blinking somewhere else. The patch should make busybox vi behave more like original vi(m). Costs a few bytes but its worth it imho. * new_text() is moved into init_text_buffer() * the previously added update_ro_status() was moved info file_insert due to duplication removal mentioned above. function old new delta init_text_buffer - 245 +245 file_insert 312 420 +108 next_tabstop - 82 +82 text_hole_make 154 171 +17 do_cmd 5093 5100 +7 static.cmd_mode_indicator - 5 +5 refresh 1248 1253 +5 current_filename - 4 +4 yank_delete 161 164 +3 what_reg 96 99 +3 end_cmd_q 78 81 +3 char_insert 440 442 +2 readonly_mode - 1 +1 vi_readonly 1 - -1 setops 154 153 -1 readonly 1 - -1 vi_setops 4 1 -3 string_insert 161 158 -3 cfn 4 - -4 show_status_line 532 514 -18 readit 519 500 -19 move_to_col 161 138 -23 vi_main 495 433 -62 isblnk 75 - -75 .rodata 4751 4655 -96 edit_file 892 787 -105 new_text 125 - -125 update_ro_status 131 - -131 colon 3848 3667 -181 ------------------------------------------------------------------------------ (add/remove: 5/6 grow/shrink: 8/10 up/down: 485/-848) Total: -363 bytes text data bss dec hex filename 34751 873 4260 39884 9bcc busybox_old 34439 877 4260 39576 9a98 busybox_unstripped
2007-07-18 07:14:07 +08:00
goto fi0;
}
2002-08-21 21:02:24 +08:00
if (p < text || p > end) {
status_line_bold("Trying to insert file outside of memory");
2002-08-21 21:02:24 +08:00
goto fi0;
}
// read file to buffer
fd = open(fn, O_RDONLY);
2002-08-21 21:02:24 +08:00
if (fd < 0) {
status_line_bold("\"%s\" %s", fn, strerror(errno));
goto fi0;
2002-08-21 21:02:24 +08:00
}
vi: multiple fixes by Natanael Copa <natanael.copa@gmail.com> * the puzzling message on error is replaced with strerror(errno) so it should be even more detailed and smaller at the same time. * merged code in edit_file() and code for ':edit <file>' in colon() into new func init_text_buffer(). Was horribly duplicate. Moved most of error/sanity checking to file_insert(). Result is that you get a proper validation (prevent reading /dev/*) and error messages for ':r <file>' * renamed 'cfn' to 'current_filename' for improved readability * merged smallint vi_readonly and readonly into bitfields into readonly_mode to save space. * added text_size variable to keep track how big the text buffer is. This is used to fix a buffer overflow. To reproduce bug: ./busybox vi TODO :r Makefile vi segfaults due to no buffer checking is done at all. som redesign is needed here but i added a check in text_hole_make() to aviod the segfault at least. * removed isblnk() and use isblank(3) instead. * fixed compiler warning by displaying the return code for :!<command> This makes things bigger than needed but since the patch reduces the overall size... (see below) * new func next_tabstop(int) merges some duplicate code. There are more cuplicode here but i couldnt find a good way to merge them. * Fix *ANNOYING* placement of cursor on '\t' characters. To reproduce: echo -e "\thello" > file1 ./busybox vi file1 Try to insert some text at the beginning of line. Text will be inserted but cursor is blinking somewhere else. The patch should make busybox vi behave more like original vi(m). Costs a few bytes but its worth it imho. * new_text() is moved into init_text_buffer() * the previously added update_ro_status() was moved info file_insert due to duplication removal mentioned above. function old new delta init_text_buffer - 245 +245 file_insert 312 420 +108 next_tabstop - 82 +82 text_hole_make 154 171 +17 do_cmd 5093 5100 +7 static.cmd_mode_indicator - 5 +5 refresh 1248 1253 +5 current_filename - 4 +4 yank_delete 161 164 +3 what_reg 96 99 +3 end_cmd_q 78 81 +3 char_insert 440 442 +2 readonly_mode - 1 +1 vi_readonly 1 - -1 setops 154 153 -1 readonly 1 - -1 vi_setops 4 1 -3 string_insert 161 158 -3 cfn 4 - -4 show_status_line 532 514 -18 readit 519 500 -19 move_to_col 161 138 -23 vi_main 495 433 -62 isblnk 75 - -75 .rodata 4751 4655 -96 edit_file 892 787 -105 new_text 125 - -125 update_ro_status 131 - -131 colon 3848 3667 -181 ------------------------------------------------------------------------------ (add/remove: 5/6 grow/shrink: 8/10 up/down: 485/-848) Total: -363 bytes text data bss dec hex filename 34751 873 4260 39884 9bcc busybox_old 34439 877 4260 39576 9a98 busybox_unstripped
2007-07-18 07:14:07 +08:00
size = statbuf.st_size;
p += text_hole_make(p, size);
cnt = safe_read(fd, p, size);
2002-08-21 21:02:24 +08:00
if (cnt < 0) {
status_line_bold("\"%s\" %s", fn, strerror(errno));
2002-08-21 21:02:24 +08:00
p = text_hole_delete(p, p + size - 1); // un-do buffer insert
} else if (cnt < size) {
// There was a partial read, shrink unused space text[]
p = text_hole_delete(p + cnt, p + (size - cnt) - 1); // un-do buffer insert
status_line_bold("can't read all of file \"%s\"", fn);
2002-08-21 21:02:24 +08:00
}
if (cnt >= size)
file_modified++;
vi: multiple fixes by Natanael Copa <natanael.copa@gmail.com> * the puzzling message on error is replaced with strerror(errno) so it should be even more detailed and smaller at the same time. * merged code in edit_file() and code for ':edit <file>' in colon() into new func init_text_buffer(). Was horribly duplicate. Moved most of error/sanity checking to file_insert(). Result is that you get a proper validation (prevent reading /dev/*) and error messages for ':r <file>' * renamed 'cfn' to 'current_filename' for improved readability * merged smallint vi_readonly and readonly into bitfields into readonly_mode to save space. * added text_size variable to keep track how big the text buffer is. This is used to fix a buffer overflow. To reproduce bug: ./busybox vi TODO :r Makefile vi segfaults due to no buffer checking is done at all. som redesign is needed here but i added a check in text_hole_make() to aviod the segfault at least. * removed isblnk() and use isblank(3) instead. * fixed compiler warning by displaying the return code for :!<command> This makes things bigger than needed but since the patch reduces the overall size... (see below) * new func next_tabstop(int) merges some duplicate code. There are more cuplicode here but i couldnt find a good way to merge them. * Fix *ANNOYING* placement of cursor on '\t' characters. To reproduce: echo -e "\thello" > file1 ./busybox vi file1 Try to insert some text at the beginning of line. Text will be inserted but cursor is blinking somewhere else. The patch should make busybox vi behave more like original vi(m). Costs a few bytes but its worth it imho. * new_text() is moved into init_text_buffer() * the previously added update_ro_status() was moved info file_insert due to duplication removal mentioned above. function old new delta init_text_buffer - 245 +245 file_insert 312 420 +108 next_tabstop - 82 +82 text_hole_make 154 171 +17 do_cmd 5093 5100 +7 static.cmd_mode_indicator - 5 +5 refresh 1248 1253 +5 current_filename - 4 +4 yank_delete 161 164 +3 what_reg 96 99 +3 end_cmd_q 78 81 +3 char_insert 440 442 +2 readonly_mode - 1 +1 vi_readonly 1 - -1 setops 154 153 -1 readonly 1 - -1 vi_setops 4 1 -3 string_insert 161 158 -3 cfn 4 - -4 show_status_line 532 514 -18 readit 519 500 -19 move_to_col 161 138 -23 vi_main 495 433 -62 isblnk 75 - -75 .rodata 4751 4655 -96 edit_file 892 787 -105 new_text 125 - -125 update_ro_status 131 - -131 colon 3848 3667 -181 ------------------------------------------------------------------------------ (add/remove: 5/6 grow/shrink: 8/10 up/down: 485/-848) Total: -363 bytes text data bss dec hex filename 34751 873 4260 39884 9bcc busybox_old 34439 877 4260 39576 9a98 busybox_unstripped
2007-07-18 07:14:07 +08:00
close(fd);
fi0:
#if ENABLE_FEATURE_VI_READONLY
if (update_ro_status
&& ((access(fn, W_OK) < 0) ||
/* root will always have access()
* so we check fileperms too */
!(statbuf.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH))
)
) {
2007-07-20 06:50:47 +08:00
SET_READONLY_FILE(readonly_mode);
}
#endif
vi: multiple fixes by Natanael Copa <natanael.copa@gmail.com> * the puzzling message on error is replaced with strerror(errno) so it should be even more detailed and smaller at the same time. * merged code in edit_file() and code for ':edit <file>' in colon() into new func init_text_buffer(). Was horribly duplicate. Moved most of error/sanity checking to file_insert(). Result is that you get a proper validation (prevent reading /dev/*) and error messages for ':r <file>' * renamed 'cfn' to 'current_filename' for improved readability * merged smallint vi_readonly and readonly into bitfields into readonly_mode to save space. * added text_size variable to keep track how big the text buffer is. This is used to fix a buffer overflow. To reproduce bug: ./busybox vi TODO :r Makefile vi segfaults due to no buffer checking is done at all. som redesign is needed here but i added a check in text_hole_make() to aviod the segfault at least. * removed isblnk() and use isblank(3) instead. * fixed compiler warning by displaying the return code for :!<command> This makes things bigger than needed but since the patch reduces the overall size... (see below) * new func next_tabstop(int) merges some duplicate code. There are more cuplicode here but i couldnt find a good way to merge them. * Fix *ANNOYING* placement of cursor on '\t' characters. To reproduce: echo -e "\thello" > file1 ./busybox vi file1 Try to insert some text at the beginning of line. Text will be inserted but cursor is blinking somewhere else. The patch should make busybox vi behave more like original vi(m). Costs a few bytes but its worth it imho. * new_text() is moved into init_text_buffer() * the previously added update_ro_status() was moved info file_insert due to duplication removal mentioned above. function old new delta init_text_buffer - 245 +245 file_insert 312 420 +108 next_tabstop - 82 +82 text_hole_make 154 171 +17 do_cmd 5093 5100 +7 static.cmd_mode_indicator - 5 +5 refresh 1248 1253 +5 current_filename - 4 +4 yank_delete 161 164 +3 what_reg 96 99 +3 end_cmd_q 78 81 +3 char_insert 440 442 +2 readonly_mode - 1 +1 vi_readonly 1 - -1 setops 154 153 -1 readonly 1 - -1 vi_setops 4 1 -3 string_insert 161 158 -3 cfn 4 - -4 show_status_line 532 514 -18 readit 519 500 -19 move_to_col 161 138 -23 vi_main 495 433 -62 isblnk 75 - -75 .rodata 4751 4655 -96 edit_file 892 787 -105 new_text 125 - -125 update_ro_status 131 - -131 colon 3848 3667 -181 ------------------------------------------------------------------------------ (add/remove: 5/6 grow/shrink: 8/10 up/down: 485/-848) Total: -363 bytes text data bss dec hex filename 34751 873 4260 39884 9bcc busybox_old 34439 877 4260 39576 9a98 busybox_unstripped
2007-07-18 07:14:07 +08:00
return cnt;
}
vi: multiple fixes by Natanael Copa <natanael.copa@gmail.com> * the puzzling message on error is replaced with strerror(errno) so it should be even more detailed and smaller at the same time. * merged code in edit_file() and code for ':edit <file>' in colon() into new func init_text_buffer(). Was horribly duplicate. Moved most of error/sanity checking to file_insert(). Result is that you get a proper validation (prevent reading /dev/*) and error messages for ':r <file>' * renamed 'cfn' to 'current_filename' for improved readability * merged smallint vi_readonly and readonly into bitfields into readonly_mode to save space. * added text_size variable to keep track how big the text buffer is. This is used to fix a buffer overflow. To reproduce bug: ./busybox vi TODO :r Makefile vi segfaults due to no buffer checking is done at all. som redesign is needed here but i added a check in text_hole_make() to aviod the segfault at least. * removed isblnk() and use isblank(3) instead. * fixed compiler warning by displaying the return code for :!<command> This makes things bigger than needed but since the patch reduces the overall size... (see below) * new func next_tabstop(int) merges some duplicate code. There are more cuplicode here but i couldnt find a good way to merge them. * Fix *ANNOYING* placement of cursor on '\t' characters. To reproduce: echo -e "\thello" > file1 ./busybox vi file1 Try to insert some text at the beginning of line. Text will be inserted but cursor is blinking somewhere else. The patch should make busybox vi behave more like original vi(m). Costs a few bytes but its worth it imho. * new_text() is moved into init_text_buffer() * the previously added update_ro_status() was moved info file_insert due to duplication removal mentioned above. function old new delta init_text_buffer - 245 +245 file_insert 312 420 +108 next_tabstop - 82 +82 text_hole_make 154 171 +17 do_cmd 5093 5100 +7 static.cmd_mode_indicator - 5 +5 refresh 1248 1253 +5 current_filename - 4 +4 yank_delete 161 164 +3 what_reg 96 99 +3 end_cmd_q 78 81 +3 char_insert 440 442 +2 readonly_mode - 1 +1 vi_readonly 1 - -1 setops 154 153 -1 readonly 1 - -1 vi_setops 4 1 -3 string_insert 161 158 -3 cfn 4 - -4 show_status_line 532 514 -18 readit 519 500 -19 move_to_col 161 138 -23 vi_main 495 433 -62 isblnk 75 - -75 .rodata 4751 4655 -96 edit_file 892 787 -105 new_text 125 - -125 update_ro_status 131 - -131 colon 3848 3667 -181 ------------------------------------------------------------------------------ (add/remove: 5/6 grow/shrink: 8/10 up/down: 485/-848) Total: -363 bytes text data bss dec hex filename 34751 873 4260 39884 9bcc busybox_old 34439 877 4260 39576 9a98 busybox_unstripped
2007-07-18 07:14:07 +08:00
static int file_write(char *fn, char *first, char *last)
{
2002-08-21 21:02:24 +08:00
int fd, cnt, charcnt;
if (fn == 0) {
status_line_bold("No current filename");
return -2;
2002-08-21 21:02:24 +08:00
}
/* By popular request we do not open file with O_TRUNC,
* but instead ftruncate() it _after_ successful write.
* Might reduce amount of data lost on power fail etc.
*/
fd = open(fn, (O_WRONLY | O_CREAT), 0666);
2002-08-21 21:02:24 +08:00
if (fd < 0)
return -1;
2002-08-21 21:02:24 +08:00
cnt = last - first + 1;
charcnt = full_write(fd, first, cnt);
ftruncate(fd, charcnt);
2002-08-21 21:02:24 +08:00
if (charcnt == cnt) {
// good write
//file_modified = FALSE;
2002-08-21 21:02:24 +08:00
} else {
charcnt = 0;
}
close(fd);
return charcnt;
}
2002-08-21 21:02:24 +08:00
//----- Terminal Drawing ---------------------------------------
// The terminal is made up of 'rows' line of 'columns' columns.
// classically this would be 24 x 80.
2002-08-21 21:02:24 +08:00
// screen coordinates
// 0,0 ... 0,79
// 1,0 ... 1,79
// . ... .
// . ... .
// 22,0 ... 22,79
// 23,0 ... 23,79 <- status line
2002-08-21 21:02:24 +08:00
//----- Move the cursor to row x col (count from 0, not 1) -------
static void place_cursor(int row, int col, int optimize)
{
char cm1[sizeof(CMrc) + sizeof(int)*3 * 2];
#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
enum {
SZ_UP = sizeof(CMup),
SZ_DN = sizeof(CMdown),
SEQ_SIZE = SZ_UP > SZ_DN ? SZ_UP : SZ_DN,
};
char cm2[SEQ_SIZE * 5 + 32]; // bigger than worst case size
#endif
2002-08-21 21:02:24 +08:00
char *cm;
2002-08-21 21:02:24 +08:00
if (row < 0) row = 0;
if (row >= rows) row = rows - 1;
if (col < 0) col = 0;
if (col >= columns) col = columns - 1;
2002-08-21 21:02:24 +08:00
//----- 1. Try the standard terminal ESC sequence
sprintf(cm1, CMrc, row + 1, col + 1);
cm = cm1;
2002-08-21 21:02:24 +08:00
#if ENABLE_FEATURE_VI_OPTIMIZE_CURSOR
if (optimize && col < 16) {
char *screenp;
int Rrow = last_row;
int diff = Rrow - row;
if (diff < -5 || diff > 5)
goto skip;
//----- find the minimum # of chars to move cursor -------------
//----- 2. Try moving with discreet chars (Newline, [back]space, ...)
cm2[0] = '\0';
// move to the correct row
while (row < Rrow) {
// the cursor has to move up
strcat(cm2, CMup);
Rrow--;
}
while (row > Rrow) {
// the cursor has to move down
strcat(cm2, CMdown);
Rrow++;
}
2002-08-21 21:02:24 +08:00
// now move to the correct column
strcat(cm2, "\r"); // start at col 0
// just send out orignal source char to get to correct place
screenp = &screen[row * columns]; // start of screen line
strncat(cm2, screenp, col);
2002-08-21 21:02:24 +08:00
// pick the shortest cursor motion to send out
if (strlen(cm2) < strlen(cm)) {
cm = cm2;
}
skip: ;
}
last_row = row;
#endif /* FEATURE_VI_OPTIMIZE_CURSOR */
write1(cm);
}
2002-08-21 21:02:24 +08:00
//----- Erase from cursor to end of line -----------------------
static void clear_to_eol(void)
{
write1(Ceol); // Erase from cursor to end of line
2002-08-21 21:02:24 +08:00
}
static void go_bottom_and_clear_to_eol(void)
{
place_cursor(rows - 1, 0, FALSE); // go to bottom of screen
clear_to_eol(); // erase to end of line
}
2002-08-21 21:02:24 +08:00
//----- Erase from cursor to end of screen -----------------------
static void clear_to_eos(void)
2002-08-21 21:02:24 +08:00
{
write1(Ceos); // Erase from cursor to end of screen
}
2002-08-21 21:02:24 +08:00
//----- Start standout mode ------------------------------------
static void standout_start(void) // send "start reverse video" sequence
{
write1(SOs); // Start reverse video mode
}
2002-08-21 21:02:24 +08:00
//----- End standout mode --------------------------------------
static void standout_end(void) // send "end reverse video" sequence
{
write1(SOn); // End reverse video mode
}
2002-08-21 21:02:24 +08:00
//----- Flash the screen --------------------------------------
static void flash(int h)
{
2002-08-21 21:02:24 +08:00
standout_start(); // send "start reverse video" sequence
redraw(TRUE);
mysleep(h);
2002-08-21 21:02:24 +08:00
standout_end(); // send "end reverse video" sequence
redraw(TRUE);
}
static void Indicate_Error(void)
2002-08-21 21:02:24 +08:00
{
#if ENABLE_FEATURE_VI_CRASHME
2002-08-21 21:02:24 +08:00
if (crashme > 0)
return; // generate a random command
#endif
if (!err_method) {
write1(bell); // send out a bell character
2002-08-21 21:02:24 +08:00
} else {
flash(10);
}
}
2002-08-21 21:02:24 +08:00
//----- Screen[] Routines --------------------------------------
//----- Erase the Screen[] memory ------------------------------
static void screen_erase(void)
{
2002-08-21 21:02:24 +08:00
memset(screen, ' ', screensize); // clear new screen
}
static int bufsum(char *buf, int count)
{
int sum = 0;
char *e = buf + count;
while (buf < e)
sum += (unsigned char) *buf++;
return sum;
}
2002-08-21 21:02:24 +08:00
//----- Draw the status line at bottom of the screen -------------
static void show_status_line(void)
{
int cnt = 0, cksum = 0;
// either we already have an error or status message, or we
// create one.
if (!have_status_msg) {
cnt = format_edit_status();
cksum = bufsum(status_buffer, cnt);
}
if (have_status_msg || ((cnt > 0 && last_status_cksum != cksum))) {
last_status_cksum = cksum; // remember if we have seen this line
go_bottom_and_clear_to_eol();
write1(status_buffer);
if (have_status_msg) {
if (((int)strlen(status_buffer) - (have_status_msg - 1)) >
(columns - 1) ) {
have_status_msg = 0;
Hit_Return();
}
have_status_msg = 0;
}
2002-08-21 21:02:24 +08:00
place_cursor(crow, ccol, FALSE); // put cursor back in correct place
}
fflush_all();
2002-08-21 21:02:24 +08:00
}
2002-08-21 21:02:24 +08:00
//----- format the status buffer, the bottom line of screen ------
// format status buffer, with STANDOUT mode
static void status_line_bold(const char *format, ...)
2002-08-21 21:02:24 +08:00
{
va_list args;
2002-08-21 21:02:24 +08:00
va_start(args, format);
strcpy(status_buffer, SOs); // Terminal standout mode on
vsprintf(status_buffer + sizeof(SOs)-1, format, args);
strcat(status_buffer, SOn); // Terminal standout mode off
2002-08-21 21:02:24 +08:00
va_end(args);
have_status_msg = 1 + sizeof(SOs) + sizeof(SOn) - 2;
}
// format status buffer
static void status_line(const char *format, ...)
{
2002-08-21 21:02:24 +08:00
va_list args;
2002-08-21 21:02:24 +08:00
va_start(args, format);
vsprintf(status_buffer, format, args);
2002-08-21 21:02:24 +08:00
va_end(args);
have_status_msg = 1;
}
// copy s to buf, convert unprintable
static void print_literal(char *buf, const char *s)
{
char *d;
unsigned char c;
buf[0] = '\0';
if (!s[0])
s = "(NULL)";
d = buf;
for (; *s; s++) {
int c_is_no_print;
c = *s;
c_is_no_print = (c & 0x80) && !Isprint(c);
if (c_is_no_print) {
strcpy(d, SOn);
d += sizeof(SOn)-1;
c = '.';
}
if (c < ' ' || c == 0x7f) {
*d++ = '^';
c |= '@'; /* 0x40 */
if (c == 0x7f)
c = '?';
}
*d++ = c;
*d = '\0';
if (c_is_no_print) {
strcpy(d, SOs);
d += sizeof(SOs)-1;
}
if (*s == '\n') {
*d++ = '$';
*d = '\0';
}
if (d - buf > MAX_INPUT_LEN - 10) // paranoia
break;
}
}
static void not_implemented(const char *s)
{
char buf[MAX_INPUT_LEN];
2002-08-21 21:02:24 +08:00
print_literal(buf, s);
status_line_bold("\'%s\' is not implemented", buf);
}
// show file status on status line
static int format_edit_status(void)
{
static const char cmd_mode_indicator[] ALIGN1 = "-IR-";
#define tot format_edit_status__tot
int cur, percent, ret, trunc_at;
// file_modified is now a counter rather than a flag. this
// helps reduce the amount of line counting we need to do.
// (this will cause a mis-reporting of modified status
// once every MAXINT editing operations.)
// it would be nice to do a similar optimization here -- if
// we haven't done a motion that could have changed which line
// we're on, then we shouldn't have to do this count_lines()
2002-08-21 21:02:24 +08:00
cur = count_lines(text, dot);
// reduce counting -- the total lines can't have
// changed if we haven't done any edits.
if (file_modified != last_file_modified) {
tot = cur + count_lines(dot, end - 1) - 1;
last_file_modified = file_modified;
}
2002-08-21 21:02:24 +08:00
// current line percent
// ------------- ~~ ----------
// total lines 100
if (tot > 0) {
percent = (100 * cur) / tot;
} else {
cur = tot = 0;
percent = 100;
}
trunc_at = columns < STATUS_BUFFER_LEN-1 ?
columns : STATUS_BUFFER_LEN-1;
ret = snprintf(status_buffer, trunc_at+1,
#if ENABLE_FEATURE_VI_READONLY
"%c %s%s%s %d/%d %d%%",
#else
"%c %s%s %d/%d %d%%",
#endif
vi: multiple fixes by Natanael Copa <natanael.copa@gmail.com> * the puzzling message on error is replaced with strerror(errno) so it should be even more detailed and smaller at the same time. * merged code in edit_file() and code for ':edit <file>' in colon() into new func init_text_buffer(). Was horribly duplicate. Moved most of error/sanity checking to file_insert(). Result is that you get a proper validation (prevent reading /dev/*) and error messages for ':r <file>' * renamed 'cfn' to 'current_filename' for improved readability * merged smallint vi_readonly and readonly into bitfields into readonly_mode to save space. * added text_size variable to keep track how big the text buffer is. This is used to fix a buffer overflow. To reproduce bug: ./busybox vi TODO :r Makefile vi segfaults due to no buffer checking is done at all. som redesign is needed here but i added a check in text_hole_make() to aviod the segfault at least. * removed isblnk() and use isblank(3) instead. * fixed compiler warning by displaying the return code for :!<command> This makes things bigger than needed but since the patch reduces the overall size... (see below) * new func next_tabstop(int) merges some duplicate code. There are more cuplicode here but i couldnt find a good way to merge them. * Fix *ANNOYING* placement of cursor on '\t' characters. To reproduce: echo -e "\thello" > file1 ./busybox vi file1 Try to insert some text at the beginning of line. Text will be inserted but cursor is blinking somewhere else. The patch should make busybox vi behave more like original vi(m). Costs a few bytes but its worth it imho. * new_text() is moved into init_text_buffer() * the previously added update_ro_status() was moved info file_insert due to duplication removal mentioned above. function old new delta init_text_buffer - 245 +245 file_insert 312 420 +108 next_tabstop - 82 +82 text_hole_make 154 171 +17 do_cmd 5093 5100 +7 static.cmd_mode_indicator - 5 +5 refresh 1248 1253 +5 current_filename - 4 +4 yank_delete 161 164 +3 what_reg 96 99 +3 end_cmd_q 78 81 +3 char_insert 440 442 +2 readonly_mode - 1 +1 vi_readonly 1 - -1 setops 154 153 -1 readonly 1 - -1 vi_setops 4 1 -3 string_insert 161 158 -3 cfn 4 - -4 show_status_line 532 514 -18 readit 519 500 -19 move_to_col 161 138 -23 vi_main 495 433 -62 isblnk 75 - -75 .rodata 4751 4655 -96 edit_file 892 787 -105 new_text 125 - -125 update_ro_status 131 - -131 colon 3848 3667 -181 ------------------------------------------------------------------------------ (add/remove: 5/6 grow/shrink: 8/10 up/down: 485/-848) Total: -363 bytes text data bss dec hex filename 34751 873 4260 39884 9bcc busybox_old 34439 877 4260 39576 9a98 busybox_unstripped
2007-07-18 07:14:07 +08:00
cmd_mode_indicator[cmd_mode & 3],
(current_filename != NULL ? current_filename : "No file"),
#if ENABLE_FEATURE_VI_READONLY
vi: multiple fixes by Natanael Copa <natanael.copa@gmail.com> * the puzzling message on error is replaced with strerror(errno) so it should be even more detailed and smaller at the same time. * merged code in edit_file() and code for ':edit <file>' in colon() into new func init_text_buffer(). Was horribly duplicate. Moved most of error/sanity checking to file_insert(). Result is that you get a proper validation (prevent reading /dev/*) and error messages for ':r <file>' * renamed 'cfn' to 'current_filename' for improved readability * merged smallint vi_readonly and readonly into bitfields into readonly_mode to save space. * added text_size variable to keep track how big the text buffer is. This is used to fix a buffer overflow. To reproduce bug: ./busybox vi TODO :r Makefile vi segfaults due to no buffer checking is done at all. som redesign is needed here but i added a check in text_hole_make() to aviod the segfault at least. * removed isblnk() and use isblank(3) instead. * fixed compiler warning by displaying the return code for :!<command> This makes things bigger than needed but since the patch reduces the overall size... (see below) * new func next_tabstop(int) merges some duplicate code. There are more cuplicode here but i couldnt find a good way to merge them. * Fix *ANNOYING* placement of cursor on '\t' characters. To reproduce: echo -e "\thello" > file1 ./busybox vi file1 Try to insert some text at the beginning of line. Text will be inserted but cursor is blinking somewhere else. The patch should make busybox vi behave more like original vi(m). Costs a few bytes but its worth it imho. * new_text() is moved into init_text_buffer() * the previously added update_ro_status() was moved info file_insert due to duplication removal mentioned above. function old new delta init_text_buffer - 245 +245 file_insert 312 420 +108 next_tabstop - 82 +82 text_hole_make 154 171 +17 do_cmd 5093 5100 +7 static.cmd_mode_indicator - 5 +5 refresh 1248 1253 +5 current_filename - 4 +4 yank_delete 161 164 +3 what_reg 96 99 +3 end_cmd_q 78 81 +3 char_insert 440 442 +2 readonly_mode - 1 +1 vi_readonly 1 - -1 setops 154 153 -1 readonly 1 - -1 vi_setops 4 1 -3 string_insert 161 158 -3 cfn 4 - -4 show_status_line 532 514 -18 readit 519 500 -19 move_to_col 161 138 -23 vi_main 495 433 -62 isblnk 75 - -75 .rodata 4751 4655 -96 edit_file 892 787 -105 new_text 125 - -125 update_ro_status 131 - -131 colon 3848 3667 -181 ------------------------------------------------------------------------------ (add/remove: 5/6 grow/shrink: 8/10 up/down: 485/-848) Total: -363 bytes text data bss dec hex filename 34751 873 4260 39884 9bcc busybox_old 34439 877 4260 39576 9a98 busybox_unstripped
2007-07-18 07:14:07 +08:00
(readonly_mode ? " [Readonly]" : ""),
#endif
vi: multiple fixes by Natanael Copa <natanael.copa@gmail.com> * the puzzling message on error is replaced with strerror(errno) so it should be even more detailed and smaller at the same time. * merged code in edit_file() and code for ':edit <file>' in colon() into new func init_text_buffer(). Was horribly duplicate. Moved most of error/sanity checking to file_insert(). Result is that you get a proper validation (prevent reading /dev/*) and error messages for ':r <file>' * renamed 'cfn' to 'current_filename' for improved readability * merged smallint vi_readonly and readonly into bitfields into readonly_mode to save space. * added text_size variable to keep track how big the text buffer is. This is used to fix a buffer overflow. To reproduce bug: ./busybox vi TODO :r Makefile vi segfaults due to no buffer checking is done at all. som redesign is needed here but i added a check in text_hole_make() to aviod the segfault at least. * removed isblnk() and use isblank(3) instead. * fixed compiler warning by displaying the return code for :!<command> This makes things bigger than needed but since the patch reduces the overall size... (see below) * new func next_tabstop(int) merges some duplicate code. There are more cuplicode here but i couldnt find a good way to merge them. * Fix *ANNOYING* placement of cursor on '\t' characters. To reproduce: echo -e "\thello" > file1 ./busybox vi file1 Try to insert some text at the beginning of line. Text will be inserted but cursor is blinking somewhere else. The patch should make busybox vi behave more like original vi(m). Costs a few bytes but its worth it imho. * new_text() is moved into init_text_buffer() * the previously added update_ro_status() was moved info file_insert due to duplication removal mentioned above. function old new delta init_text_buffer - 245 +245 file_insert 312 420 +108 next_tabstop - 82 +82 text_hole_make 154 171 +17 do_cmd 5093 5100 +7 static.cmd_mode_indicator - 5 +5 refresh 1248 1253 +5 current_filename - 4 +4 yank_delete 161 164 +3 what_reg 96 99 +3 end_cmd_q 78 81 +3 char_insert 440 442 +2 readonly_mode - 1 +1 vi_readonly 1 - -1 setops 154 153 -1 readonly 1 - -1 vi_setops 4 1 -3 string_insert 161 158 -3 cfn 4 - -4 show_status_line 532 514 -18 readit 519 500 -19 move_to_col 161 138 -23 vi_main 495 433 -62 isblnk 75 - -75 .rodata 4751 4655 -96 edit_file 892 787 -105 new_text 125 - -125 update_ro_status 131 - -131 colon 3848 3667 -181 ------------------------------------------------------------------------------ (add/remove: 5/6 grow/shrink: 8/10 up/down: 485/-848) Total: -363 bytes text data bss dec hex filename 34751 873 4260 39884 9bcc busybox_old 34439 877 4260 39576 9a98 busybox_unstripped
2007-07-18 07:14:07 +08:00
(file_modified ? " [Modified]" : ""),
cur, tot, percent);
if (ret >= 0 && ret < trunc_at)
return ret; /* it all fit */
return trunc_at; /* had to truncate */
#undef tot
}
2002-08-21 21:02:24 +08:00
//----- Force refresh of all Lines -----------------------------
static void redraw(int full_screen)
{
2002-08-21 21:02:24 +08:00
place_cursor(0, 0, FALSE); // put cursor in correct place
clear_to_eos(); // tell terminal to erase display
2002-08-21 21:02:24 +08:00
screen_erase(); // erase the internal screen buffer
last_status_cksum = 0; // force status update
2002-08-21 21:02:24 +08:00
refresh(full_screen); // this will redraw the entire display
show_status_line();
2002-08-21 21:02:24 +08:00
}
2002-08-21 21:02:24 +08:00
//----- Format a text[] line into a buffer ---------------------
static char* format_line(char *src /*, int li*/)
2002-08-21 21:02:24 +08:00
{
unsigned char c;
int co;
int ofs = offset;
char *dest = scr_out_buf; // [MAX_SCR_COLS + MAX_TABSTOP * 2]
c = '~'; // char in col 0 in non-existent lines is '~'
co = 0;
while (co < columns + tabstop) {
// have we gone past the end?
if (src < end) {
2002-08-21 21:02:24 +08:00
c = *src++;
if (c == '\n')
break;
if ((c & 0x80) && !Isprint(c)) {
c = '.';
}
if (c < ' ' || c == 0x7f) {
if (c == '\t') {
c = ' ';
// co % 8 != 7
while ((co % tabstop) != (tabstop - 1)) {
dest[co++] = c;
}
} else {
dest[co++] = '^';
if (c == 0x7f)
c = '?';
else
c += '@'; // Ctrl-X -> 'X'
2002-08-21 21:02:24 +08:00
}
}
}
dest[co++] = c;
// discard scrolled-off-to-the-left portion,
// in tabstop-sized pieces
if (ofs >= tabstop && co >= tabstop) {
memmove(dest, dest + tabstop, co);
co -= tabstop;
ofs -= tabstop;
}
2002-08-21 21:02:24 +08:00
if (src >= end)
break;
}
// check "short line, gigantic offset" case
if (co < ofs)
ofs = co;
// discard last scrolled off part
co -= ofs;
dest += ofs;
// fill the rest with spaces
if (co < columns)
memset(&dest[co], ' ', columns - co);
return dest;
}
2002-08-21 21:02:24 +08:00
//----- Refresh the changed screen lines -----------------------
// Copy the source line from text[] into the buffer and note
// if the current screenline is different from the new buffer.
// If they differ then that line needs redrawing on the terminal.
//
static void refresh(int full_screen)
{
#define old_offset refresh__old_offset
2002-08-21 21:02:24 +08:00
int li, changed;
char *tp, *sp; // pointer into text[] and screen[]
if (ENABLE_FEATURE_VI_WIN_RESIZE IF_FEATURE_VI_ASK_TERMINAL(&& !G.get_rowcol_error) ) {
unsigned c = columns, r = rows;
query_screen_dimensions();
full_screen |= (c - columns) | (r - rows);
}
2002-08-21 21:02:24 +08:00
sync_cursor(dot, &crow, &ccol); // where cursor will be (on "dot")
tp = screenbegin; // index into text[] of top line
2002-08-21 21:02:24 +08:00
// compare text[] to screen[] and mark screen[] lines that need updating
for (li = 0; li < rows - 1; li++) {
int cs, ce; // column start & end
2007-12-23 10:36:51 +08:00
char *out_buf;
// format current text line
out_buf = format_line(tp /*, li*/);
2002-08-21 21:02:24 +08:00
// skip to the end of the current text[] line
2007-12-23 10:36:51 +08:00
if (tp < end) {
char *t = memchr(tp, '\n', end - tp);
if (!t) t = end - 1;
tp = t + 1;
}
2002-08-21 21:02:24 +08:00
// see if there are any changes between vitual screen and out_buf
2002-08-21 21:02:24 +08:00
changed = FALSE; // assume no change
cs = 0;
ce = columns - 1;
2002-08-21 21:02:24 +08:00
sp = &screen[li * columns]; // start of screen line
if (full_screen) {
// force re-draw of every single column from 0 - columns-1
goto re0;
}
// compare newly formatted buffer with virtual screen
// look forward for first difference between buf and screen
for (; cs <= ce; cs++) {
if (out_buf[cs] != sp[cs]) {
2002-08-21 21:02:24 +08:00
changed = TRUE; // mark for redraw
break;
}
}
// look backward for last difference between out_buf and screen
for (; ce >= cs; ce--) {
if (out_buf[ce] != sp[ce]) {
2002-08-21 21:02:24 +08:00
changed = TRUE; // mark for redraw
break;
}
}
// now, cs is index of first diff, and ce is index of last diff
// if horz offset has changed, force a redraw
if (offset != old_offset) {
re0:
2002-08-21 21:02:24 +08:00
changed = TRUE;
}
// make a sanity check of columns indexes
if (cs < 0) cs = 0;
if (ce > columns - 1) ce = columns - 1;
if (cs > ce) { cs = 0; ce = columns - 1; }
// is there a change between vitual screen and out_buf
2002-08-21 21:02:24 +08:00
if (changed) {
// copy changed part of buffer to virtual screen
memcpy(sp+cs, out_buf+cs, ce-cs+1);
2002-08-21 21:02:24 +08:00
// move cursor to column of first change
//if (offset != old_offset) {
// // place_cursor is still too stupid
// // to handle offsets correctly
// place_cursor(li, cs, FALSE);
//} else {
place_cursor(li, cs, TRUE);
//}
2002-08-21 21:02:24 +08:00
// write line out to terminal
fwrite(&sp[cs], ce - cs + 1, 1, stdout);
2002-08-21 21:02:24 +08:00
}
}
place_cursor(crow, ccol, TRUE);
old_offset = offset;
#undef old_offset
}
2002-08-21 21:02:24 +08:00
//---------------------------------------------------------------------
//----- 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
//---------------------------------------------------------------------
2002-08-21 21:02:24 +08:00
//----- Execute a Vi Command -----------------------------------
static void do_cmd(int c)
{
char *p, *q, *save_dot;
char buf[12];
int dir;
int cnt, i, j;
int c1;
// c1 = c; // quiet the compiler
// cnt = yf = 0; // quiet the compiler
// p = q = save_dot = buf; // quiet the compiler
memset(buf, '\0', sizeof(buf));
show_status_line();
2002-08-21 21:02:24 +08:00
/* if this is a cursor key, skip these checks */
switch (c) {
case KEYCODE_UP:
case KEYCODE_DOWN:
case KEYCODE_LEFT:
case KEYCODE_RIGHT:
case KEYCODE_HOME:
case KEYCODE_END:
case KEYCODE_PAGEUP:
case KEYCODE_PAGEDOWN:
case KEYCODE_DELETE:
2002-08-21 21:02:24 +08:00
goto key_cmd_mode;
}
2002-08-21 21:02:24 +08:00
if (cmd_mode == 2) {
// flip-flop Insert/Replace mode
if (c == KEYCODE_INSERT)
goto dc_i;
2002-08-21 21:02:24 +08:00
// 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 || Isprint(c)) {
2002-08-21 21:02:24 +08:00
if (c != 27)
dot = yank_delete(dot, dot, 0, YANKDEL); // delete char
dot = char_insert(dot, c); // insert new char
}
goto dc1;
}
}
2002-08-21 21:02:24 +08:00
if (cmd_mode == 1) {
// hitting "Insert" twice means "R" replace mode
if (c == KEYCODE_INSERT) goto dc5;
2002-08-21 21:02:24 +08:00
// insert the char c at "dot"
if (1 <= c || Isprint(c)) {
dot = char_insert(dot, c);
2002-08-21 21:02:24 +08:00
}
goto dc1;
}
2002-08-21 21:02:24 +08:00
key_cmd_mode:
2002-08-21 21:02:24 +08:00
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
#if ENABLE_FEATURE_VI_CRASHME
2002-08-21 21:02:24 +08:00
case 0x14: // dc4 ctrl-T
crashme = (crashme == 0) ? 1 : 0;
break;
#endif
2002-08-21 21:02:24 +08:00
//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 'F': // F-
//case 'K': // K-
//case 'Q': // Q-
//case 'S': // S-
//case 'T': // T-
//case 'V': // V-
//case '[': // [-
//case '\\': // \-
//case ']': // ]-
//case '_': // _-
//case '`': // `-
2002-08-21 21:02:24 +08:00
//case 'u': // u- FIXME- there is no undo
//case 'v': // v-
default: // unrecognized command
2002-08-21 21:02:24 +08:00
buf[0] = c;
buf[1] = '\0';
not_implemented(buf);
2002-08-21 21:02:24 +08:00
end_cmd_q(); // stop adding to q
case 0x00: // nul- ignore
break;
case 2: // ctrl-B scroll up full screen
case KEYCODE_PAGEUP: // Cursor Key Page Up
2002-08-21 21:02:24 +08:00
dot_scroll(rows - 2, -1);
break;
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 KEYCODE_PAGEDOWN: // Cursor Key Page Down
2002-08-21 21:02:24 +08:00
dot_scroll(rows - 2, 1);
break;
case 7: // ctrl-G show current status
last_status_cksum = 0; // force status update
2002-08-21 21:02:24 +08:00
break;
case 'h': // h- move left
case KEYCODE_LEFT: // cursor key Left
case 8: // ctrl-H- move left (This may be ERASE char)
case 0x7f: // DEL- move left (This may be ERASE char)
do {
dot_left();
} while (--cmdcnt > 0);
2002-08-21 21:02:24 +08:00
break;
case 10: // Newline ^J
case 'j': // j- goto next line, same col
case KEYCODE_DOWN: // cursor key Down
do {
dot_next(); // go to next B-o-l
// try stay in same col
dot = move_to_col(dot, ccol + offset);
} while (--cmdcnt > 0);
2002-08-21 21:02:24 +08:00
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
mysleep(10);
2002-08-21 21:02:24 +08:00
screen_erase(); // erase the internal screen buffer
last_status_cksum = 0; // force status update
2002-08-21 21:02:24 +08:00
refresh(TRUE); // this will redraw the entire display
break;
case 13: // Carriage Return ^M
case '+': // +- goto next line
do {
dot_next();
dot_skip_over_ws();
} while (--cmdcnt > 0);
2002-08-21 21:02:24 +08:00
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();
last_status_cksum = 0; // force status update
2002-08-21 21:02:24 +08:00
break;
case ' ': // move right
case 'l': // move right
case KEYCODE_RIGHT: // Cursor Key Right
do {
dot_right();
} while (--cmdcnt > 0);
2002-08-21 21:02:24 +08:00
break;
#if ENABLE_FEATURE_VI_YANKMARK
2002-08-21 21:02:24 +08:00
case '"': // "- name a register to use for Delete/Yank
c1 = (get_one_char() | 0x20) - 'a'; // | 0x20 is tolower()
if ((unsigned)c1 <= 25) { // a-z?
YDreg = c1;
2002-08-21 21:02:24 +08:00
} else {
indicate_error(c);
}
break;
case '\'': // '- goto a specific mark
c1 = (get_one_char() | 0x20) - 'a';
if ((unsigned)c1 <= 25) { // a-z?
2002-08-21 21:02:24 +08:00
// get the b-o-l
q = mark[c1];
2002-08-21 21:02:24 +08:00
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() | 0x20) - 'a';
if ((unsigned)c1 <= 25) { // a-z?
2002-08-21 21:02:24 +08:00
// remember the line
mark[c1] = dot;
2002-08-21 21:02:24 +08:00
} else {
indicate_error(c);
}
break;
case 'P': // P- Put register before
case 'p': // p- put register after
p = reg[YDreg];
if (p == NULL) {
status_line_bold("Nothing in register %c", what_reg());
2002-08-21 21:02:24 +08:00
break;
}
// are we putting whole lines or strings
if (strchr(p, '\n') != NULL) {
2002-08-21 21:02:24 +08:00
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
}
string_insert(dot, p); // insert the string
2002-08-21 21:02:24 +08:00
end_cmd_q(); // stop adding to q
break;
case 'U': // U- Undo; replace current line with original version
if (reg[Ureg] != NULL) {
2002-08-21 21:02:24 +08:00
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
2002-08-21 21:02:24 +08:00
dot = p;
dot_skip_over_ws();
}
break;
#endif /* FEATURE_VI_YANKMARK */
2002-08-21 21:02:24 +08:00
case '$': // $- goto end of line
case KEYCODE_END: // Cursor Key End
for (;;) {
dot = end_line(dot);
if (--cmdcnt <= 0)
break;
dot_next();
}
2002-08-21 21:02:24 +08:00
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 separate these two commands. 'f' depends on ';'
2002-08-21 21:02:24 +08:00
//
2008-02-11 19:44:38 +08:00
//**** fall through to ... ';'
2002-08-21 21:02:24 +08:00
case ';': // ;- look at rest of line for last forward char
do {
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;
} while (--cmdcnt > 0);
2002-08-21 21:02:24 +08:00
break;
case ',': // repeat latest 'f' in opposite direction
if (last_forward_char == 0)
break;
do {
q = dot - 1;
while (q >= text && *q != '\n' && *q != last_forward_char) {
q--;
}
if (q >= text && *q == last_forward_char)
dot = q;
} while (--cmdcnt > 0);
break;
2002-08-21 21:02:24 +08:00
case '-': // -- goto prev line
do {
dot_prev();
dot_skip_over_ws();
} while (--cmdcnt > 0);
2002-08-21 21:02:24 +08:00
break;
#if ENABLE_FEATURE_VI_DOT_CMD
2002-08-21 21:02:24 +08:00
case '.': // .- repeat the last modifying command
// Stuff the last_modifying_cmd back into stdin
// and let it be re-executed.
if (lmc_len > 0) {
last_modifying_cmd[lmc_len] = 0;
ioq = ioq_start = xstrdup(last_modifying_cmd);
2002-08-21 21:02:24 +08:00
}
break;
#endif
#if ENABLE_FEATURE_VI_SEARCH
2002-08-21 21:02:24 +08:00
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 (q[0] && !q[1]) {
if (last_search_pattern[0])
last_search_pattern[0] = c;
goto dc3; // if no pat re-use old pat
}
if (q[0]) { // strlen(q) > 1: new pat- save it and find
2002-08-21 21:02:24 +08:00
// there is a new pat
2002-11-28 19:27:31 +08:00
free(last_search_pattern);
last_search_pattern = xstrdup(q);
2002-08-21 21:02:24 +08:00
goto dc3; // now find the pattern
}
// user changed mind and erased the "/"- do nothing
break;
case 'N': // N- backward search for last pattern
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
do {
const char *msg;
dc3:
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 = NULL;
goto dc2;
}
// no pattern found between "dot" and "end"- continue at top
p = text;
2002-08-21 21:02:24 +08:00
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 = "search hit BOTTOM, continuing at TOP";
if (dir == BACK) {
msg = "search hit TOP, continuing at BOTTOM";
}
} else {
msg = "Pattern not found";
2002-08-21 21:02:24 +08:00
}
dc2:
if (msg)
status_line_bold("%s", msg);
} while (--cmdcnt > 0);
2002-08-21 21:02:24 +08:00
break;
case '{': // {- move backward paragraph
q = char_search(dot, "\n\n", BACK, FULL);
2002-08-21 21:02:24 +08:00
if (q != NULL) { // found blank line
dot = next_line(q); // move to next blank line
}
break;
case '}': // }- move forward paragraph
q = char_search(dot, "\n\n", FORWARD, FULL);
2002-08-21 21:02:24 +08:00
if (q != NULL) { // found blank line
dot = next_line(q); // move to next blank line
}
break;
#endif /* FEATURE_VI_SEARCH */
2002-08-21 21:02:24 +08:00
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-
2002-08-21 21:02:24 +08:00
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(":"); // get input line- use "status line"
#if ENABLE_FEATURE_VI_COLON
2002-08-21 21:02:24 +08:00
colon(p); // execute the command
#else
2002-08-21 21:02:24 +08:00
if (*p == ':')
p++; // move past the ':'
cnt = strlen(p);
2002-08-21 21:02:24 +08:00
if (cnt <= 0)
break;
if (strncmp(p, "quit", cnt) == 0
|| strncmp(p, "q!", cnt) == 0 // delete lines
) {
2002-08-21 21:02:24 +08:00
if (file_modified && p[1] != '!') {
status_line_bold("No write since last change (:quit! overrides)");
2002-08-21 21:02:24 +08:00
} else {
editing = 0;
}
} else if (strncmp(p, "write", cnt) == 0
|| strncmp(p, "wq", cnt) == 0
|| strncmp(p, "wn", cnt) == 0
|| (p[0] == 'x' && !p[1])
) {
vi: multiple fixes by Natanael Copa <natanael.copa@gmail.com> * the puzzling message on error is replaced with strerror(errno) so it should be even more detailed and smaller at the same time. * merged code in edit_file() and code for ':edit <file>' in colon() into new func init_text_buffer(). Was horribly duplicate. Moved most of error/sanity checking to file_insert(). Result is that you get a proper validation (prevent reading /dev/*) and error messages for ':r <file>' * renamed 'cfn' to 'current_filename' for improved readability * merged smallint vi_readonly and readonly into bitfields into readonly_mode to save space. * added text_size variable to keep track how big the text buffer is. This is used to fix a buffer overflow. To reproduce bug: ./busybox vi TODO :r Makefile vi segfaults due to no buffer checking is done at all. som redesign is needed here but i added a check in text_hole_make() to aviod the segfault at least. * removed isblnk() and use isblank(3) instead. * fixed compiler warning by displaying the return code for :!<command> This makes things bigger than needed but since the patch reduces the overall size... (see below) * new func next_tabstop(int) merges some duplicate code. There are more cuplicode here but i couldnt find a good way to merge them. * Fix *ANNOYING* placement of cursor on '\t' characters. To reproduce: echo -e "\thello" > file1 ./busybox vi file1 Try to insert some text at the beginning of line. Text will be inserted but cursor is blinking somewhere else. The patch should make busybox vi behave more like original vi(m). Costs a few bytes but its worth it imho. * new_text() is moved into init_text_buffer() * the previously added update_ro_status() was moved info file_insert due to duplication removal mentioned above. function old new delta init_text_buffer - 245 +245 file_insert 312 420 +108 next_tabstop - 82 +82 text_hole_make 154 171 +17 do_cmd 5093 5100 +7 static.cmd_mode_indicator - 5 +5 refresh 1248 1253 +5 current_filename - 4 +4 yank_delete 161 164 +3 what_reg 96 99 +3 end_cmd_q 78 81 +3 char_insert 440 442 +2 readonly_mode - 1 +1 vi_readonly 1 - -1 setops 154 153 -1 readonly 1 - -1 vi_setops 4 1 -3 string_insert 161 158 -3 cfn 4 - -4 show_status_line 532 514 -18 readit 519 500 -19 move_to_col 161 138 -23 vi_main 495 433 -62 isblnk 75 - -75 .rodata 4751 4655 -96 edit_file 892 787 -105 new_text 125 - -125 update_ro_status 131 - -131 colon 3848 3667 -181 ------------------------------------------------------------------------------ (add/remove: 5/6 grow/shrink: 8/10 up/down: 485/-848) Total: -363 bytes text data bss dec hex filename 34751 873 4260 39884 9bcc busybox_old 34439 877 4260 39576 9a98 busybox_unstripped
2007-07-18 07:14:07 +08:00
cnt = file_write(current_filename, text, end - 1);
if (cnt < 0) {
if (cnt == -1)
status_line_bold("Write error: %s", strerror(errno));
} else {
file_modified = 0;
last_file_modified = -1;
status_line("\"%s\" %dL, %dC", current_filename, count_lines(text, end - 1), cnt);
if (p[0] == 'x' || p[1] == 'q' || p[1] == 'n'
|| p[0] == 'X' || p[1] == 'Q' || p[1] == 'N'
) {
editing = 0;
}
2002-08-21 21:02:24 +08:00
}
} else if (strncmp(p, "file", cnt) == 0) {
last_status_cksum = 0; // force status update
} else if (sscanf(p, "%d", &j) > 0) {
2002-08-21 21:02:24 +08:00
dot = find_line(j); // go to line # j
dot_skip_over_ws();
} else { // unrecognized cmd
not_implemented(p);
2002-08-21 21:02:24 +08:00
}
#endif /* !FEATURE_VI_COLON */
2002-08-21 21:02:24 +08:00
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);
yank_delete(p, q, 1, YANKONLY); // save copy before change
2002-08-21 21:02:24 +08:00
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
text_hole_delete(p, p);
2002-08-21 21:02:24 +08:00
} else if (*p == ' ') {
// we should be calculating columns, not just SPACE
for (j = 0; *p == ' ' && j < tabstop; j++) {
text_hole_delete(p, p);
2002-08-21 21:02:24 +08:00
}
}
} else if (c == '>') {
// shift right -- add tab or 8 spaces
char_insert(p, '\t');
2002-08-21 21:02:24 +08:00
}
}
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
2008-02-11 19:44:38 +08:00
//**** fall through to ... 'a'
2002-08-21 21:02:24 +08:00
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
dir = FORWARD;
if (c == 'B')
dir = BACK;
do {
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);
} while (--cmdcnt > 0);
2002-08-21 21:02:24 +08:00
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
#if ENABLE_FEATURE_VI_DOT_CMD
2002-08-21 21:02:24 +08:00
if (c == 'D')
end_cmd_q(); // stop adding to q
#endif
2002-08-21 21:02:24 +08:00
break;
case 'g': // 'gg' goto a line number (vim) (default: very first line)
c1 = get_one_char();
if (c1 != 'g') {
buf[0] = 'g';
buf[1] = c1; // TODO: if Unicode?
buf[2] = '\0';
not_implemented(buf);
break;
}
if (cmdcnt == 0)
cmdcnt = 1;
/* fall through */
2002-08-21 21:02:24 +08:00
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 > 0) {
2002-08-21 21:02:24 +08:00
do_cmd('+');
}
2002-08-21 21:02:24 +08:00
dot_skip_over_ws();
break;
case 'I': // I- insert before first non-blank
dot_begin(); // 0
dot_skip_over_ws();
2008-02-11 19:44:38 +08:00
//**** fall through to ... 'i'
2002-08-21 21:02:24 +08:00
case 'i': // i- insert before current char
case KEYCODE_INSERT: // Cursor Key Insert
dc_i:
cmd_mode = 1; // start inserting
2002-08-21 21:02:24 +08:00
break;
case 'J': // J- join current and next lines together
do {
dot_end(); // move to NL
if (dot < end - 1) { // make sure not last char in text[]
*dot++ = ' '; // replace NL with space
file_modified++;
while (isblank(*dot)) { // delete leading WS
dot_delete();
}
2002-08-21 21:02:24 +08:00
}
} while (--cmdcnt > 0);
2002-08-21 21:02:24 +08:00
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 > 0) {
2002-08-21 21:02:24 +08:00
do_cmd('-');
}
2002-08-21 21:02:24 +08:00
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:
2002-08-21 21:02:24 +08:00
cmd_mode = 2;
break;
case KEYCODE_DELETE:
c = 'x';
// fall through
2002-08-21 21:02:24 +08:00
case 'X': // X- delete char before dot
case 'x': // x- delete the current char
case 's': // s- substitute the current char
dir = 0;
if (c == 'X')
dir = -1;
do {
if (dot[dir] != '\n') {
if (c == 'X')
dot--; // delete prev char
dot = yank_delete(dot, dot, 0, YANKDEL); // delete char
}
} while (--cmdcnt > 0);
2002-08-21 21:02:24 +08:00
end_cmd_q(); // stop adding to q
if (c == 's')
goto dc_i; // start inserting
2002-08-21 21:02:24 +08:00
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) {
vi: multiple fixes by Natanael Copa <natanael.copa@gmail.com> * the puzzling message on error is replaced with strerror(errno) so it should be even more detailed and smaller at the same time. * merged code in edit_file() and code for ':edit <file>' in colon() into new func init_text_buffer(). Was horribly duplicate. Moved most of error/sanity checking to file_insert(). Result is that you get a proper validation (prevent reading /dev/*) and error messages for ':r <file>' * renamed 'cfn' to 'current_filename' for improved readability * merged smallint vi_readonly and readonly into bitfields into readonly_mode to save space. * added text_size variable to keep track how big the text buffer is. This is used to fix a buffer overflow. To reproduce bug: ./busybox vi TODO :r Makefile vi segfaults due to no buffer checking is done at all. som redesign is needed here but i added a check in text_hole_make() to aviod the segfault at least. * removed isblnk() and use isblank(3) instead. * fixed compiler warning by displaying the return code for :!<command> This makes things bigger than needed but since the patch reduces the overall size... (see below) * new func next_tabstop(int) merges some duplicate code. There are more cuplicode here but i couldnt find a good way to merge them. * Fix *ANNOYING* placement of cursor on '\t' characters. To reproduce: echo -e "\thello" > file1 ./busybox vi file1 Try to insert some text at the beginning of line. Text will be inserted but cursor is blinking somewhere else. The patch should make busybox vi behave more like original vi(m). Costs a few bytes but its worth it imho. * new_text() is moved into init_text_buffer() * the previously added update_ro_status() was moved info file_insert due to duplication removal mentioned above. function old new delta init_text_buffer - 245 +245 file_insert 312 420 +108 next_tabstop - 82 +82 text_hole_make 154 171 +17 do_cmd 5093 5100 +7 static.cmd_mode_indicator - 5 +5 refresh 1248 1253 +5 current_filename - 4 +4 yank_delete 161 164 +3 what_reg 96 99 +3 end_cmd_q 78 81 +3 char_insert 440 442 +2 readonly_mode - 1 +1 vi_readonly 1 - -1 setops 154 153 -1 readonly 1 - -1 vi_setops 4 1 -3 string_insert 161 158 -3 cfn 4 - -4 show_status_line 532 514 -18 readit 519 500 -19 move_to_col 161 138 -23 vi_main 495 433 -62 isblnk 75 - -75 .rodata 4751 4655 -96 edit_file 892 787 -105 new_text 125 - -125 update_ro_status 131 - -131 colon 3848 3667 -181 ------------------------------------------------------------------------------ (add/remove: 5/6 grow/shrink: 8/10 up/down: 485/-848) Total: -363 bytes text data bss dec hex filename 34751 873 4260 39884 9bcc busybox_old 34439 877 4260 39576 9a98 busybox_unstripped
2007-07-18 07:14:07 +08:00
if (ENABLE_FEATURE_VI_READONLY && readonly_mode) {
status_line_bold("\"%s\" File is read only", current_filename);
2006-10-04 03:56:34 +08:00
break;
}
vi: multiple fixes by Natanael Copa <natanael.copa@gmail.com> * the puzzling message on error is replaced with strerror(errno) so it should be even more detailed and smaller at the same time. * merged code in edit_file() and code for ':edit <file>' in colon() into new func init_text_buffer(). Was horribly duplicate. Moved most of error/sanity checking to file_insert(). Result is that you get a proper validation (prevent reading /dev/*) and error messages for ':r <file>' * renamed 'cfn' to 'current_filename' for improved readability * merged smallint vi_readonly and readonly into bitfields into readonly_mode to save space. * added text_size variable to keep track how big the text buffer is. This is used to fix a buffer overflow. To reproduce bug: ./busybox vi TODO :r Makefile vi segfaults due to no buffer checking is done at all. som redesign is needed here but i added a check in text_hole_make() to aviod the segfault at least. * removed isblnk() and use isblank(3) instead. * fixed compiler warning by displaying the return code for :!<command> This makes things bigger than needed but since the patch reduces the overall size... (see below) * new func next_tabstop(int) merges some duplicate code. There are more cuplicode here but i couldnt find a good way to merge them. * Fix *ANNOYING* placement of cursor on '\t' characters. To reproduce: echo -e "\thello" > file1 ./busybox vi file1 Try to insert some text at the beginning of line. Text will be inserted but cursor is blinking somewhere else. The patch should make busybox vi behave more like original vi(m). Costs a few bytes but its worth it imho. * new_text() is moved into init_text_buffer() * the previously added update_ro_status() was moved info file_insert due to duplication removal mentioned above. function old new delta init_text_buffer - 245 +245 file_insert 312 420 +108 next_tabstop - 82 +82 text_hole_make 154 171 +17 do_cmd 5093 5100 +7 static.cmd_mode_indicator - 5 +5 refresh 1248 1253 +5 current_filename - 4 +4 yank_delete 161 164 +3 what_reg 96 99 +3 end_cmd_q 78 81 +3 char_insert 440 442 +2 readonly_mode - 1 +1 vi_readonly 1 - -1 setops 154 153 -1 readonly 1 - -1 vi_setops 4 1 -3 string_insert 161 158 -3 cfn 4 - -4 show_status_line 532 514 -18 readit 519 500 -19 move_to_col 161 138 -23 vi_main 495 433 -62 isblnk 75 - -75 .rodata 4751 4655 -96 edit_file 892 787 -105 new_text 125 - -125 update_ro_status 131 - -131 colon 3848 3667 -181 ------------------------------------------------------------------------------ (add/remove: 5/6 grow/shrink: 8/10 up/down: 485/-848) Total: -363 bytes text data bss dec hex filename 34751 873 4260 39884 9bcc busybox_old 34439 877 4260 39576 9a98 busybox_unstripped
2007-07-18 07:14:07 +08:00
cnt = file_write(current_filename, text, end - 1);
if (cnt < 0) {
if (cnt == -1)
status_line_bold("Write error: %s", strerror(errno));
} else if (cnt == (end - 1 - text + 1)) {
2002-08-21 21:02:24 +08:00
editing = 0;
}
} else {
editing = 0;
}
2002-08-21 21:02:24 +08:00
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
dir = FORWARD;
if (c == 'b')
dir = BACK;
do {
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);
}
} while (--cmdcnt > 0);
2002-08-21 21:02:24 +08:00
break;
case 'c': // c- change something
case 'd': // d- delete something
#if ENABLE_FEATURE_VI_YANKMARK
2002-08-21 21:02:24 +08:00
case 'y': // y- yank something
case 'Y': // Y- Yank a line
#endif
{
int yf, ml, whole = 0;
2002-08-21 21:02:24 +08:00
yf = YANKDEL; // assume either "c" or "d"
#if ENABLE_FEATURE_VI_YANKMARK
2002-08-21 21:02:24 +08:00
if (c == 'y' || c == 'Y')
yf = YANKONLY;
#endif
2002-08-21 21:02:24 +08:00
c1 = 'y';
if (c != 'Y')
c1 = get_one_char(); // get the type of thing to delete
// determine range, and whether it spans lines
ml = find_range(&p, &q, c1);
2002-08-21 21:02:24 +08:00
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
vi: multiple fixes by Natanael Copa <natanael.copa@gmail.com> * the puzzling message on error is replaced with strerror(errno) so it should be even more detailed and smaller at the same time. * merged code in edit_file() and code for ':edit <file>' in colon() into new func init_text_buffer(). Was horribly duplicate. Moved most of error/sanity checking to file_insert(). Result is that you get a proper validation (prevent reading /dev/*) and error messages for ':r <file>' * renamed 'cfn' to 'current_filename' for improved readability * merged smallint vi_readonly and readonly into bitfields into readonly_mode to save space. * added text_size variable to keep track how big the text buffer is. This is used to fix a buffer overflow. To reproduce bug: ./busybox vi TODO :r Makefile vi segfaults due to no buffer checking is done at all. som redesign is needed here but i added a check in text_hole_make() to aviod the segfault at least. * removed isblnk() and use isblank(3) instead. * fixed compiler warning by displaying the return code for :!<command> This makes things bigger than needed but since the patch reduces the overall size... (see below) * new func next_tabstop(int) merges some duplicate code. There are more cuplicode here but i couldnt find a good way to merge them. * Fix *ANNOYING* placement of cursor on '\t' characters. To reproduce: echo -e "\thello" > file1 ./busybox vi file1 Try to insert some text at the beginning of line. Text will be inserted but cursor is blinking somewhere else. The patch should make busybox vi behave more like original vi(m). Costs a few bytes but its worth it imho. * new_text() is moved into init_text_buffer() * the previously added update_ro_status() was moved info file_insert due to duplication removal mentioned above. function old new delta init_text_buffer - 245 +245 file_insert 312 420 +108 next_tabstop - 82 +82 text_hole_make 154 171 +17 do_cmd 5093 5100 +7 static.cmd_mode_indicator - 5 +5 refresh 1248 1253 +5 current_filename - 4 +4 yank_delete 161 164 +3 what_reg 96 99 +3 end_cmd_q 78 81 +3 char_insert 440 442 +2 readonly_mode - 1 +1 vi_readonly 1 - -1 setops 154 153 -1 readonly 1 - -1 vi_setops 4 1 -3 string_insert 161 158 -3 cfn 4 - -4 show_status_line 532 514 -18 readit 519 500 -19 move_to_col 161 138 -23 vi_main 495 433 -62 isblnk 75 - -75 .rodata 4751 4655 -96 edit_file 892 787 -105 new_text 125 - -125 update_ro_status 131 - -131 colon 3848 3667 -181 ------------------------------------------------------------------------------ (add/remove: 5/6 grow/shrink: 8/10 up/down: 485/-848) Total: -363 bytes text data bss dec hex filename 34751 873 4260 39884 9bcc busybox_old 34439 877 4260 39576 9a98 busybox_unstripped
2007-07-18 07:14:07 +08:00
while (isblank(*q)) {
2002-08-21 21:02:24 +08:00
if (q <= text || q[-1] == '\n')
break;
q--;
}
}
dot = yank_delete(p, q, ml, yf); // delete word
} else if (strchr("^0bBeEft%$ lh\b\177", c1)) {
// partial line copy text into a register and delete
dot = yank_delete(p, q, ml, yf); // delete word
} else if (strchr("cdykjHL+-{}\r\n", c1)) {
// whole line copy text into a register and delete
dot = yank_delete(p, q, ml, yf); // delete lines
whole = 1;
} else {
// could not recognize object
c = c1 = 27; // error-
ml = 0;
indicate_error(c);
}
if (ml && whole) {
2002-08-21 21:02:24 +08:00
if (c == 'c') {
dot = char_insert(dot, '\n');
// on the last line of file don't move to prev line
if (whole && dot != (end-1)) {
2002-08-21 21:02:24 +08:00
dot_prev();
}
2002-08-21 21:02:24 +08:00
} else if (c == 'd') {
dot_begin();
dot_skip_over_ws();
}
}
2002-08-21 21:02:24 +08:00
if (c1 != 27) {
// if CHANGING, not deleting, start inserting after the delete
if (c == 'c') {
strcpy(buf, "Change");
2002-08-21 21:02:24 +08:00
goto dc_i; // start inserting
}
2002-08-21 21:02:24 +08:00
if (c == 'd') {
strcpy(buf, "Delete");
2002-08-21 21:02:24 +08:00
}
#if ENABLE_FEATURE_VI_YANKMARK
2002-08-21 21:02:24 +08:00
if (c == 'y' || c == 'Y') {
strcpy(buf, "Yank");
2002-08-21 21:02:24 +08:00
}
p = reg[YDreg];
q = p + strlen(p);
2002-08-21 21:02:24 +08:00
for (cnt = 0; p <= q; p++) {
if (*p == '\n')
cnt++;
}
status_line("%s %d lines (%d chars) using [%c]",
buf, cnt, strlen(reg[YDreg]), what_reg());
#endif
2002-08-21 21:02:24 +08:00
end_cmd_q(); // stop adding to q
}
2002-08-21 21:02:24 +08:00
break;
}
2002-08-21 21:02:24 +08:00
case 'k': // k- goto prev line, same col
case KEYCODE_UP: // cursor key Up
do {
dot_prev();
dot = move_to_col(dot, ccol + offset); // try stay in same col
} while (--cmdcnt > 0);
2002-08-21 21:02:24 +08:00
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++;
}
2002-08-21 21:02:24 +08:00
end_cmd_q(); // stop adding to q
break;
case 't': // t- move to char prior to next x
2006-01-25 08:08:53 +08:00
last_forward_char = get_one_char();
do_cmd(';');
if (*dot == last_forward_char)
dot_left();
last_forward_char = 0;
2002-08-21 21:02:24 +08:00
break;
case 'w': // w- forward a word
do {
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);
}
} while (--cmdcnt > 0);
2002-08-21 21:02:24 +08:00
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
do {
if (islower(*dot)) {
*dot = toupper(*dot);
file_modified++;
} else if (isupper(*dot)) {
*dot = tolower(*dot);
file_modified++;
}
dot_right();
} while (--cmdcnt > 0);
2002-08-21 21:02:24 +08:00
end_cmd_q(); // stop adding to q
break;
//----- The Cursor and Function Keys -----------------------------
case KEYCODE_HOME: // Cursor Key Home
2002-08-21 21:02:24 +08:00
dot_begin();
break;
// The Fn keys could point to do_macro which could translate them
#if 0
case KEYCODE_FUN1: // Function Key F1
case KEYCODE_FUN2: // Function Key F2
case KEYCODE_FUN3: // Function Key F3
case KEYCODE_FUN4: // Function Key F4
case KEYCODE_FUN5: // Function Key F5
case KEYCODE_FUN6: // Function Key F6
case KEYCODE_FUN7: // Function Key F7
case KEYCODE_FUN8: // Function Key F8
case KEYCODE_FUN9: // Function Key F9
case KEYCODE_FUN10: // Function Key F10
case KEYCODE_FUN11: // Function Key F11
case KEYCODE_FUN12: // Function Key F12
2002-08-21 21:02:24 +08:00
break;
#endif
}
dc1:
2002-08-21 21:02:24 +08:00
// if text[] just became empty, add back an empty line
if (end == text) {
char_insert(text, '\n'); // start empty buf with dummy line
2002-08-21 21:02:24 +08:00
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
}
#if ENABLE_FEATURE_VI_YANKMARK
2002-08-21 21:02:24 +08:00
check_context(c); // update the current context
#endif
2002-08-21 21:02:24 +08:00
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--;
}
/* NB! the CRASHME code is unmaintained, and doesn't currently build */
#if ENABLE_FEATURE_VI_CRASHME
static int totalcmds = 0;
static int Mp = 85; // Movement command Probability
static int Np = 90; // Non-movement command Probability
static int Dp = 96; // Delete command Probability
static int Ip = 97; // Insert command Probability
static int Yp = 98; // Yank command Probability
static int Pp = 99; // Put command Probability
static int M = 0, N = 0, I = 0, D = 0, Y = 0, P = 0, U = 0;
static const char chars[20] = "\t012345 abcdABCD-=.$";
static const char *const words[20] = {
"this", "is", "a", "test",
"broadcast", "the", "emergency", "of",
"system", "quick", "brown", "fox",
"jumped", "over", "lazy", "dogs",
"back", "January", "Febuary", "March"
};
static const char *const lines[20] = {
"You should have received a copy of the GNU General Public License\n",
"char c, cm, *cmd, *cmd1;\n",
"generate a command by percentages\n",
"Numbers may be typed as a prefix to some commands.\n",
"Quit, discarding changes!\n",
"Forced write, if permission originally not valid.\n",
"In general, any ex or ed command (such as substitute or delete).\n",
"I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
"Please get w/ me and I will go over it with you.\n",
"The following is a list of scheduled, committed changes.\n",
"1. Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
"Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
"Any question about transactions please contact Sterling Huxley.\n",
"I will try to get back to you by Friday, December 31.\n",
"This Change will be implemented on Friday.\n",
"Let me know if you have problems accessing this;\n",
"Sterling Huxley recently added you to the access list.\n",
"Would you like to go to lunch?\n",
"The last command will be automatically run.\n",
"This is too much english for a computer geek.\n",
};
static char *multilines[20] = {
"You should have received a copy of the GNU General Public License\n",
"char c, cm, *cmd, *cmd1;\n",
"generate a command by percentages\n",
"Numbers may be typed as a prefix to some commands.\n",
"Quit, discarding changes!\n",
"Forced write, if permission originally not valid.\n",
"In general, any ex or ed command (such as substitute or delete).\n",
"I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
"Please get w/ me and I will go over it with you.\n",
"The following is a list of scheduled, committed changes.\n",
"1. Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
"Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
"Any question about transactions please contact Sterling Huxley.\n",
"I will try to get back to you by Friday, December 31.\n",
"This Change will be implemented on Friday.\n",
"Let me know if you have problems accessing this;\n",
"Sterling Huxley recently added you to the access list.\n",
"Would you like to go to lunch?\n",
"The last command will be automatically run.\n",
"This is too much english for a computer geek.\n",
};
// create a random command to execute
static void crash_dummy()
{
static int sleeptime; // how long to pause between commands
char c, cm, *cmd, *cmd1;
int i, cnt, thing, rbi, startrbi, percent;
// "dot" movement commands
cmd1 = " \n\r\002\004\005\006\025\0310^$-+wWeEbBhjklHL";
// is there already a command running?
if (readbuffer[0] > 0)
goto cd1;
cd0:
readbuffer[0] = 'X';
startrbi = rbi = 1;
sleeptime = 0; // how long to pause between commands
memset(readbuffer, '\0', sizeof(readbuffer));
// generate a command by percentages
percent = (int) lrand48() % 100; // get a number from 0-99
if (percent < Mp) { // Movement commands
// available commands
cmd = cmd1;
M++;
} else if (percent < Np) { // non-movement commands
cmd = "mz<>\'\""; // available commands
N++;
} else if (percent < Dp) { // Delete commands
cmd = "dx"; // available commands
D++;
} else if (percent < Ip) { // Inset commands
cmd = "iIaAsrJ"; // available commands
I++;
} else if (percent < Yp) { // Yank commands
cmd = "yY"; // available commands
Y++;
} else if (percent < Pp) { // Put commands
cmd = "pP"; // available commands
P++;
} else {
// We do not know how to handle this command, try again
U++;
goto cd0;
}
// randomly pick one of the available cmds from "cmd[]"
i = (int) lrand48() % strlen(cmd);
cm = cmd[i];
if (strchr(":\024", cm))
goto cd0; // dont allow colon or ctrl-T commands
readbuffer[rbi++] = cm; // put cmd into input buffer
// now we have the command-
// there are 1, 2, and multi char commands
// find out which and generate the rest of command as necessary
if (strchr("dmryz<>\'\"", cm)) { // 2-char commands
cmd1 = " \n\r0$^-+wWeEbBhjklHL";
if (cm == 'm' || cm == '\'' || cm == '\"') { // pick a reg[]
cmd1 = "abcdefghijklmnopqrstuvwxyz";
}
thing = (int) lrand48() % strlen(cmd1); // pick a movement command
c = cmd1[thing];
readbuffer[rbi++] = c; // add movement to input buffer
}
if (strchr("iIaAsc", cm)) { // multi-char commands
if (cm == 'c') {
// change some thing
thing = (int) lrand48() % strlen(cmd1); // pick a movement command
c = cmd1[thing];
readbuffer[rbi++] = c; // add movement to input buffer
}
thing = (int) lrand48() % 4; // what thing to insert
cnt = (int) lrand48() % 10; // how many to insert
for (i = 0; i < cnt; i++) {
if (thing == 0) { // insert chars
readbuffer[rbi++] = chars[((int) lrand48() % strlen(chars))];
} else if (thing == 1) { // insert words
strcat(readbuffer, words[(int) lrand48() % 20]);
strcat(readbuffer, " ");
sleeptime = 0; // how fast to type
} else if (thing == 2) { // insert lines
strcat(readbuffer, lines[(int) lrand48() % 20]);
sleeptime = 0; // how fast to type
} else { // insert multi-lines
strcat(readbuffer, multilines[(int) lrand48() % 20]);
sleeptime = 0; // how fast to type
}
}
strcat(readbuffer, "\033");
}
readbuffer[0] = strlen(readbuffer + 1);
cd1:
totalcmds++;
if (sleeptime > 0)
mysleep(sleeptime); // sleep 1/100 sec
}
// test to see if there are any errors
static void crash_test()
{
static time_t oldtim;
time_t tim;
char d[2], msg[80];
msg[0] = '\0';
if (end < text) {
strcat(msg, "end<text ");
}
if (end > textend) {
strcat(msg, "end>textend ");
}
if (dot < text) {
strcat(msg, "dot<text ");
}
if (dot > end) {
strcat(msg, "dot>end ");
}
if (screenbegin < text) {
strcat(msg, "screenbegin<text ");
}
if (screenbegin > end - 1) {
strcat(msg, "screenbegin>end-1 ");
}
if (msg[0]) {
2002-12-04 05:48:15 +08:00
printf("\n\n%d: \'%c\' %s\n\n\n%s[Hit return to continue]%s",
totalcmds, last_input_char, msg, SOs, SOn);
fflush_all();
while (safe_read(STDIN_FILENO, d, 1) > 0) {
if (d[0] == '\n' || d[0] == '\r')
break;
}
}
tim = time(NULL);
if (tim >= (oldtim + 3)) {
sprintf(status_buffer,
"Tot=%d: M=%d N=%d I=%d D=%d Y=%d P=%d U=%d size=%d",
totalcmds, M, N, I, D, Y, P, U, end - text + 1);
oldtim = tim;
}
}
#endif