svn commit: trunk/busybox/editors

vda at busybox.net vda at busybox.net
Tue Jul 17 23:14:08 UTC 2007


Author: vda
Date: 2007-07-17 16:14:07 -0700 (Tue, 17 Jul 2007)
New Revision: 19147

Log:
vi: multiple fixes by Natanael Copa <natanael.copa at 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



Modified:
   trunk/busybox/editors/vi.c


Changeset:
Modified: trunk/busybox/editors/vi.c
===================================================================
--- trunk/busybox/editors/vi.c	2007-07-17 22:53:05 UTC (rev 19146)
+++ trunk/busybox/editors/vi.c	2007-07-17 23:14:07 UTC (rev 19147)
@@ -95,7 +95,7 @@
 /* busybox build system provides that, but it's better */
 /* to audit and fix the source */
 
-static int vi_setops;
+static smallint vi_setops;
 #define VI_AUTOINDENT 1
 #define VI_SHOWMATCH  2
 #define VI_IGNORECASE 4
@@ -122,7 +122,7 @@
 static int have_status_msg;     // is default edit status needed?
                                 // [don't make smallint!]
 static int last_status_cksum;   // hash of current status line
-static char *cfn;               // previous, current, and next file name
+static char *current_filename;               // current file name
 //static char *text, *end;        // pointers to the user data in memory
 static char *screen;            // pointer to the virtual screen buffer
 static int screensize;          //            and its size
@@ -134,8 +134,18 @@
 static char last_forward_char;  // last char searched for with 'f'
 
 #if ENABLE_FEATURE_VI_READONLY
-static smallint vi_readonly, readonly;
+//static smallint vi_readonly, readonly;
+static smallint readonly_mode = 0;
+#define SET_READONLY_FILE(flags) 	((flags) |= 0x01)
+#define SET_READONLY_MODE(flags) 	((flags) |= 0x02)
+#define UNSET_READONLY_FILE(flags)	((flags) &= 0xfe)
+#else
+#define readonly_mode 0
+#define SET_READONLY_FILE(flags)
+#define SET_READONLY_MODE(flags)
+#define UNSET_READONLY_FILE(flags)
 #endif
+
 #if ENABLE_FEATURE_VI_DOT_CMD
 static smallint adding2q;		// are we currently adding user input to q
 static char *last_modifying_cmd;	// last modifying cmd for "."
@@ -158,6 +168,7 @@
 struct globals {
 	/* many references - keep near the top of globals */
 	char *text, *end;       // pointers to the user data in memory
+	int text_size;		// size of the allocated buffer
 	char *dot;              // where all the action takes place
 #if ENABLE_FEATURE_VI_YANKMARK
 	char *reg[28];          // named register a-z, "D", and "U" 0-25,26,27
@@ -176,6 +187,7 @@
 };
 #define G (*ptr_to_globals)
 #define text           (G.text          )
+#define text_size      (G.text_size     )
 #define end            (G.end           )
 #define dot            (G.dot           )
 #define reg            (G.reg           )
@@ -189,8 +201,10 @@
 #define term_vi        (G.term_vi       )
 #define initial_cmds   (G.initial_cmds  )
 
+static int init_text_buffer(char *); // init from file or create new
 static void edit_file(char *);	// edit one file
 static void do_cmd(char);	// execute a command
+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
@@ -200,7 +214,6 @@
 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 int isblnk(char);	// is the char a blank or tab
 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
@@ -212,7 +225,6 @@
 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 *new_text(int);	// malloc memory for text[] buffer
 static char *char_insert(char *, char);	// insert the char c at 'p'
 static char *stupid_insert(char *, char);	// stupidly insert the char c at 'p'
 static char find_range(char **, char **, char);	// return pointers for an object
@@ -229,11 +241,10 @@
 static char readit(void);	// read (maybe cursor) key from stdin
 static char get_one_char(void);	// read 1 char from stdin
 static int file_size(const char *);   // what is the byte size of "fn"
-static int file_insert(char *, char *);
 #if ENABLE_FEATURE_VI_READONLY
-static void update_ro_status(const char *);
+static int file_insert(const char *, char *, int);
 #else
-static ALWAYS_INLINE void update_ro_status(const char *name) {}
+static int file_insert(const char *, char *);
 #endif
 static int file_write(char *, char *, char *);
 static void place_cursor(int, int, int);
@@ -305,9 +316,6 @@
 	int c;
 	RESERVE_CONFIG_BUFFER(STATUS_BUFFER, STATUS_BUFFER_LEN);
 
-#if ENABLE_FEATURE_VI_YANKMARK
-	int i;
-#endif
 #if ENABLE_FEATURE_VI_USE_SIGNALS || ENABLE_FEATURE_VI_CRASHME
 	my_pid = getpid();
 #endif
@@ -320,19 +328,18 @@
 
 	status_buffer = STATUS_BUFFER;
 	last_status_cksum = 0;
+	text = NULL;
 
-#if ENABLE_FEATURE_VI_READONLY
-	vi_readonly = readonly = FALSE;
-	if (strncmp(argv[0], "view", 4) == 0) {
-		readonly = TRUE;
-		vi_readonly = TRUE;
+	if (ENABLE_FEATURE_VI_READONLY && strncmp(argv[0], "view", 4) == 0) {
+		SET_READONLY_MODE(readonly_mode);
 	}
-#endif
+
 	vi_setops = VI_AUTOINDENT | VI_SHOWMATCH | VI_IGNORECASE | VI_ERR_METHOD;
 #if ENABLE_FEATURE_VI_YANKMARK
-	for (i = 0; i < 28; i++) {
-		reg[i] = 0;
-	}					// init the yank regs
+	//for (i = 0; i < 28; i++) {
+	//	reg[i] = 0;
+	//}					// init the yank regs
+	memset(reg, 0, sizeof(reg));
 #endif
 #if ENABLE_FEATURE_VI_DOT_CMD || ENABLE_FEATURE_VI_YANKMARK
 	modifying_cmds = (char *) "aAcCdDiIJoOpPrRsxX<>~";	// cmds modifying text[]
@@ -357,8 +364,7 @@
 #endif
 #if ENABLE_FEATURE_VI_READONLY
 		case 'R':		// Read-only flag
-			readonly = TRUE;
-			vi_readonly = TRUE;
+			SET_READONLY_MODE(readonly_mode);
 			break;
 #endif
 			//case 'r':	// recover flag-  ignore- we don't use tmp file
@@ -384,14 +390,10 @@
 
 	//----- This is the main file handling loop --------------
 	if (optind >= argc) {
-		editing = 1;	// 0= exit,  1= one file,  2 = multiple files
 		edit_file(0);
 	} else {
 		for (; optind < argc; optind++) {
-			editing = 1;	// 0=exit, 1=one file, 2+ = many files
-			free(cfn);
-			cfn = xstrdup(argv[optind]);
-			edit_file(cfn);
+			edit_file(argv[optind]);
 		}
 	}
 	//-----------------------------------------------------------
@@ -399,10 +401,45 @@
 	return 0;
 }
 
+/* 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 * 2;
+	if (text_size < 10240)
+		text_size = 10240;	// have a minimum size for new files
+	screenbegin = dot = end = text = xzalloc(text_size);
+	
+	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
+			USE_FEATURE_VI_READONLY(, 1));
+	}
+	file_modified = 0;
+	last_file_modified = -1;
+#if ENABLE_FEATURE_VI_YANKMARK
+	/* init the marks. */
+	memset(mark, 0, sizeof(mark));
+#endif
+	return rc;
+}	
+
 static void edit_file(char * fn)
 {
 	char c;
-	int cnt, size, ch;
+	int size;
 
 #if ENABLE_FEATURE_VI_USE_SIGNALS
 	int sig;
@@ -411,33 +448,19 @@
 	static char *cur_line;
 #endif
 
+	editing = 1;	// 0= exit,  1= one file, 2= multiple files
 	rawmode();
 	rows = 24;
 	columns = 80;
-	ch = -1;
+	size = 0;
 	if (ENABLE_FEATURE_VI_WIN_RESIZE)
 		get_terminal_width_height(0, &columns, &rows);
 	new_screen(rows, columns);	// get memory for virtual screen
+	init_text_buffer(fn);
 
-	cnt = file_size(fn);	// file size
-	size = 2 * cnt;		// 200% of file size
-	new_text(size);		// get a text[] buffer
-	screenbegin = dot = end = text;
-	if (fn != 0) {
-		ch = file_insert(fn, text);
-		update_ro_status(fn);
-	}
-	if (ch < 1) {
-		char_insert(text, '\n');	// start empty buf with dummy line
-	}
-	file_modified = 0;
-	last_file_modified = -1;
 #if ENABLE_FEATURE_VI_YANKMARK
 	YDreg = 26;			// default Yank/Delete reg
 	Ureg = 27;			// hold orig line for "U" cmd
-	for (cnt = 0; cnt < 28; cnt++) {
-		mark[cnt] = 0;
-	}					// init the marks
 	mark[26] = mark[27] = text;	// init "previous context"
 #endif
 
@@ -455,7 +478,6 @@
 	}
 #endif
 
-	editing = 1;
 	cmd_mode = 0;		// 0=command  1=insert  2='R'eplace
 	cmdcnt = 0;
 	tabstop = 8;
@@ -468,7 +490,6 @@
 	adding2q = 0;
 #endif
 	redraw(FALSE);			// dont force every col re-draw
-	show_status_line();
 
 #if ENABLE_FEATURE_VI_COLON
 	{
@@ -612,7 +633,7 @@
 {
 	//----- get the address' i.e., 1,3   'a,'b  -----
 	// get FIRST addr, if present
-	while (isblnk(*p))
+	while (isblank(*p))
 		p++;				// skip over leading spaces
 	if (*p == '%') {			// alias for 1,$
 		p++;
@@ -621,17 +642,17 @@
 		goto ga0;
 	}
 	p = get_one_address(p, b);
-	while (isblnk(*p))
+	while (isblank(*p))
 		p++;
 	if (*p == ',') {			// is there a address separator
 		p++;
-		while (isblnk(*p))
+		while (isblank(*p))
 			p++;
 		// get SECOND addr, if present
 		p = get_one_address(p, e);
 	}
  ga0:
-	while (isblnk(*p))
+	while (isblank(*p))
 		p++;				// skip over trailing spaces
 	return p;
 }
@@ -686,7 +707,7 @@
 	q = text;			// assume 1,$ for the range
 	r = end - 1;
 	li = count_lines(text, end - 1);
-	fn = cfn;			// default to current file
+	fn = current_filename;			// default to current file
 	memset(cmd, '\0', MAX_LINELEN);	// clear cmd[]
 	memset(args, '\0', MAX_LINELEN);	// clear args[]
 
@@ -704,7 +725,7 @@
 		*buf1++ = *buf++;
 	}
 	// get any ARGuments
-	while (isblnk(*buf))
+	while (isblank(*buf))
 		buf++;
 	strcpy(args, buf);
 	buf1 = last_char_is(cmd, '!');
@@ -738,12 +759,15 @@
 	}
 #if ENABLE_FEATURE_ALLOW_EXEC
 	else if (strncmp(cmd, "!", 1) == 0) {	// run a cmd
+		int retcode;
 		// :!ls   run the <cmd>
 		alarm(0);		// wait for input- no alarms
 		place_cursor(rows - 1, 0, FALSE);	// go to Status line
 		clear_to_eol();			// clear the line
 		cookmode();
-		system(orig_buf + 1);		// run the cmd
+		retcode = system(orig_buf + 1);	// run the cmd
+		if (retcode)
+			printf("\nshell returned %i\n\n", retcode);
 		rawmode();
 		Hit_Return();			// let user see results
 		alarm(3);		// done waiting for input
@@ -762,9 +786,6 @@
 		dot = yank_delete(q, r, 1, YANKDEL);	// save, then delete lines
 		dot_skip_over_ws();
 	} else if (strncasecmp(cmd, "edit", i) == 0) {	// Edit a file
-		int sr;
-		struct stat st_buf;
-		sr= 0;
 		// don't edit, if the current file has been modified
 		if (file_modified && ! useforce) {
 			psbs("No write since last change (:edit! overrides)");
@@ -773,58 +794,18 @@
 		if (args[0]) {
 			// the user supplied a file name
 			fn = args;
-		} else if (cfn && cfn[0]) {
+		} else if (current_filename && current_filename[0]) {
 			// no user supplied name- use the current filename
-			fn = cfn;
-			goto vc5;
+			// fn = current_filename;  was set by default
 		} else {
 			// no user file name, no current name- punt
 			psbs("No current filename");
 			goto vc1;
 		}
 
-		// see if file exists- if not, its just a new file request
-		sr = stat(fn, &st_buf);
-		if (sr < 0) {
-			// This is just a request for a new file creation.
-			// The file_insert below will fail but we get
-			// an empty buffer with a file name.  Then the "write"
-			// command can do the create.
-		} else {
-			if ((st_buf.st_mode & S_IFREG) == 0) {
-				// This is not a regular file
-				psbs("\"%s\" is not a regular file", fn);
-				goto vc1;
-			}
-			if ((st_buf.st_mode & (S_IRUSR | S_IRGRP | S_IROTH)) == 0) {
-				// dont have any read permissions
-				psbs("\"%s\" is not readable", fn);
-				goto vc1;
-			}
-		}
+		if (init_text_buffer(fn) < 0)
+			goto vc1;
 
-		// There is a read-able regular file
-		// make this the current file
-		q = xstrdup(fn);	// save the cfn
-		free(cfn);		// free the old name
-		cfn = q;			// remember new cfn
-
- vc5:
-		// delete all the contents of text[]
-		new_text(2 * file_size(fn));
-		screenbegin = dot = end = text;
-
-		// insert new file
-		ch = file_insert(fn, text);
-		update_ro_status(fn);
-
-		if (ch < 1) {
-			// start empty buf with dummy line
-			char_insert(text, '\n');
-			ch = 1;
-		}
-		file_modified = 0;
-		last_file_modified = -1;
 #if ENABLE_FEATURE_VI_YANKMARK
 		if (Ureg >= 0 && Ureg < 28 && reg[Ureg] != 0) {
 			free(reg[Ureg]);	//   free orig line reg- for 'U'
@@ -834,21 +815,16 @@
 			free(reg[YDreg]);	//   free default yank/delete register
 			reg[YDreg]= 0;
 		}
-		for (li = 0; li < 28; li++) {
-			mark[li] = 0;
-		}				// init the marks
 #endif
 		// how many lines in text[]?
 		li = count_lines(text, end - 1);
 		psb("\"%s\"%s"
-#if ENABLE_FEATURE_VI_READONLY
-			"%s"
-#endif
-			" %dL, %dC", cfn,
-			(sr < 0 ? " [New file]" : ""),
-#if ENABLE_FEATURE_VI_READONLY
-			((vi_readonly || readonly) ? " [Read only]" : ""),
-#endif
+			USE_FEATURE_VI_READONLY("%s")
+			" %dL, %dC", current_filename,
+			(file_size(fn) < 0 ? " [New file]" : ""),
+			USE_FEATURE_VI_READONLY(
+				((readonly_mode) ? " [Readonly]" : ""),
+			)
 			li, ch);
 	} else if (strncasecmp(cmd, "file", i) == 0) {	// what File is this
 		if (b != -1 || e != -1) {
@@ -857,8 +833,8 @@
 		}
 		if (args[0]) {
 			// user wants a new filename
-			free(cfn);
-			cfn = xstrdup(args);
+			free(current_filename);
+			current_filename = xstrdup(args);
 		} else {
 			// user wants file status info
 			last_status_cksum = 0;	// force status update
@@ -944,7 +920,7 @@
 		// read after current line- unless user said ":0r foo"
 		if (b != 0)
 			q = next_line(q);
-		ch = file_insert(fn, q);
+		ch = file_insert(fn, q  USE_FEATURE_VI_READONLY(, 0));
 		if (ch < 0)
 			goto vc1;	// nothing was inserted
 		// how many lines in text[]?
@@ -952,7 +928,7 @@
 		psb("\"%s\""
 			USE_FEATURE_VI_READONLY("%s")
 			" %dL, %dC", fn,
-			USE_FEATURE_VI_READONLY(((vi_readonly || readonly) ? " [Read only]" : ""),)
+			USE_FEATURE_VI_READONLY((readonly_mode ? " [Readonly]" : ""),)
 			li, ch);
 		if (ch > 0) {
 			// if the insert is before "dot" then we need to update
@@ -1080,7 +1056,7 @@
 			fn = args;
 		}
 #if ENABLE_FEATURE_VI_READONLY
-		if ((vi_readonly || readonly) && !useforce) {
+		if (readonly_mode && !useforce) {
 			psbs("\"%s\" File is read only", fn);
 			goto vc3;
 		}
@@ -1104,7 +1080,7 @@
 		}
 		if (l < 0) {
 			if (l == -1)
-				psbs("Write error: %s", strerror(errno));
+				psbs("\"%s\" %s", fn, strerror(errno));
 		} else {
 			psb("\"%s\" %dL, %dC", fn, li, l);
 			if (q == text && r == end - 1 && l == ch) {
@@ -1158,6 +1134,10 @@
 	redraw(TRUE);		// force redraw all
 }
 
+static int next_tabstop(int col) { //vda
+	return col + ((tabstop - 1) - (col % tabstop));
+}
+
 //----- Synchronize the cursor to Dot --------------------------
 static void sync_cursor(char * d, int *row, int *col)
 {
@@ -1210,8 +1190,11 @@
 		if (*tp == '\n' || *tp == '\0')
 			break;
 		if (*tp == '\t') {
-			//         7       - (co %    8  )
-			co += ((tabstop - 1) - (co % tabstop));
+			if (d == tp && cmd_mode) { /* handle tabs like real vi */
+				break;
+			} else {
+				co = next_tabstop(co);
+			}
 		} else if (*tp < ' ' || *tp == 127) {
 			co++;		// display as ^X, use 2 columns
 		}
@@ -1365,8 +1348,7 @@
 		if (*p == '\n' || *p == '\0')
 			break;
 		if (*p == '\t') {
-			//         7       - (co %    8  )
-			co += ((tabstop - 1) - (co % tabstop));
+			co = next_tabstop(co);
 		} else if (*p < ' ' || *p == 127) {
 			co++;		// display as ^X, use 2 columns
 		}
@@ -1462,28 +1444,15 @@
 	return screen;
 }
 
-static char *new_text(int size)
-{
-	if (size < 10240)
-		size = 10240;	// have a minimum size for new files
-	free(text);
-	text = xmalloc(size + 8);
-	memset(text, '\0', size);	// clear new text[]
-	//text += 4;		// leave some room for "oops"
-	return text;
-}
-
 #if ENABLE_FEATURE_VI_SEARCH
 static int mycmp(const char * s1, const char * s2, int len)
 {
 	int i;
 
 	i = strncmp(s1, s2, len);
-#if ENABLE_FEATURE_VI_SETOPTS
-	if (ignorecase) {
+	if (ENABLE_FEATURE_VI_SETOPTS && ignorecase) {
 		i = strncasecmp(s1, s2, len);
 	}
-#endif
 	return i;
 }
 
@@ -1621,7 +1590,7 @@
 			char *q;
 
 			q = prev_line(p);	// use prev line as templet
-			for (; isblnk(*q); q++) {
+			for (; isblank(*q); q++) {
 				p = stupid_insert(p, *q);	// insert the char
 			}
 		}
@@ -1828,11 +1797,14 @@
 	src = p;
 	dest = p + size;
 	cnt = end - src;	// the rest of buffer
-	if (memmove(dest, src, cnt) != dest) {
+	if ( ((end + size) >= (text + text_size)) // TODO: realloc here
+			|| memmove(dest, src, cnt) != dest) {
 		psbs("can't create room for new characters");
+		p = NULL;
+		goto thm0;
 	}
 	memset(p, ' ', size);	// clear new hole
-	end = end + size;	// adjust the new END
+	end += size;		// adjust the new END
 	file_modified++;	// has the file been modified
  thm0:
 	return p;
@@ -2010,15 +1982,16 @@
 	int cnt, i;
 
 	i = strlen(s);
-	p = text_hole_make(p, i);
-	strncpy(p, s, i);
-	for (cnt = 0; *s != '\0'; s++) {
-		if (*s == '\n')
-			cnt++;
-	}
+	if (text_hole_make(p, i)) {
+		strncpy(p, s, i);
+		for (cnt = 0; *s != '\0'; s++) {
+			if (*s == '\n')
+				cnt++;
+		}
 #if ENABLE_FEATURE_VI_YANKMARK
-	psb("Put %d lines (%d chars) from [%c]", cnt, i, what_reg());
+		psb("Put %d lines (%d chars) from [%c]", cnt, i, what_reg());
 #endif
+	}
 	return p;
 }
 #endif
@@ -2094,11 +2067,6 @@
 }
 #endif /* FEATURE_VI_YANKMARK */
 
-static int isblnk(char c) // is the char a blank or tab
-{
-	return (c == ' ' || c == '\t');
-}
-
 //----- Set terminal attributes --------------------------------
 static void rawmode(void)
 {
@@ -2240,14 +2208,9 @@
 		if (n < 0) {
 			if (errno == EINTR)
 				goto ri0;	// interrupted sys call
-			if (errno == EBADF)
+			if (errno == EBADF || errno == EFAULT || errno == EINVAL
+					|| errno == EIO)	
 				editing = 0;
-			if (errno == EFAULT)
-				editing = 0;
-			if (errno == EINVAL)
-				editing = 0;
-			if (errno == EIO)
-				editing = 0;
 			errno = 0;
 		}
 		if (n <= 0)
@@ -2393,7 +2356,7 @@
 	return obufp;
 }
 
-static int file_size(const char * fn) // what is the byte size of "fn"
+static int file_size(const char *fn) // what is the byte size of "fn"
 {
 	struct stat st_buf;
 	int cnt;
@@ -2404,16 +2367,30 @@
 	return cnt;
 }
 
-static int file_insert(char *fn, char *p)
+static int file_insert(const char * fn, char *p
+		USE_FEATURE_VI_READONLY(, int update_ro_status))
 {
 	int cnt = -1;
 	int fd, size;
-	
-	size = file_size(fn);
-	if (size < 0) {
-		psbs("File does not exist");
+	struct stat statbuf;
+
+	/* Validate file */
+	if (stat(fn, &statbuf) < 0) {
+		psbs("\"%s\" %s", fn, strerror(errno));
 		goto fi0;
 	}
+	if ((statbuf.st_mode & S_IFREG) == 0) {
+		// This is not a regular file
+		psbs("\"%s\" Not a regular file", fn);
+		goto fi0;
+	}
+	/* // this check is done by open()
+	if ((statbuf.st_mode & (S_IRUSR | S_IRGRP | S_IROTH)) == 0) {
+		// dont have any read permissions
+		psbs("\"%s\" Not readable", fn);
+		goto fi0;
+	}
+	*/
 	if (p < text || p > end) {
 		psbs("Trying to insert file outside of memory");
 		goto fi0;
@@ -2422,16 +2399,17 @@
 	// read file to buffer
 	fd = open(fn, O_RDONLY);
 	if (fd < 0) {
-		psbs("\"%s\" %s", fn, "cannot open file");
+		psbs("\"%s\" %s", fn, strerror(errno));
 		goto fi0;
 	}
+	size = statbuf.st_size;
 	p = text_hole_make(p, size);
+	if (p == NULL)
+		goto fi0;
 	cnt = read(fd, p, size);
-	close(fd);
 	if (cnt < 0) {
-		cnt = -1;
+		psbs("\"%s\" %s", fn, strerror(errno));
 		p = text_hole_delete(p, p + size - 1);	// un-do buffer insert
-		psbs("cannot read file \"%s\"", fn);
 	} 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
@@ -2439,23 +2417,20 @@
 	}
 	if (cnt >= size)
 		file_modified++;
+	close(fd);
  fi0:
-	return cnt;
-}
-
-#if ENABLE_FEATURE_VI_READONLY
-static void update_ro_status(const char *fn)
-{
-	struct stat sb;
-	if (stat(fn, &sb) == 0) {
-		readonly = vi_readonly || (access(fn, W_OK) < 0) ||
+	if (ENABLE_FEATURE_VI_READONLY && update_ro_status
+			&& ((access(fn, W_OK) < 0) ||
 			/* root will always have access()
 			 * so we check fileperms too */
-			!(sb.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH));
+			!(statbuf.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH))))
+	{
+		SET_READONLY_FILE(readonly_mode);	
 	}
+	return cnt;
 }
-#endif
 
+
 static int file_write(char * fn, char * first, char * last)
 {
 	int fd, cnt, charcnt;
@@ -2687,7 +2662,7 @@
 static int format_edit_status(void)	// show file status on status line
 {
 	static int tot;
-
+	static const char cmd_mode_indicator[] = "-IR-";
 	int cur, percent, ret, trunc_at;
 
 	// file_modified is now a counter rather than a flag.  this
@@ -2726,12 +2701,12 @@
 #else
 		"%c %s%s %d/%d %d%%",
 #endif
-		(cmd_mode ? (cmd_mode == 2 ? 'R':'I'):'-'),
-		(cfn != 0 ? cfn : "No file"),
+		cmd_mode_indicator[cmd_mode & 3],
+		(current_filename != NULL ? current_filename : "No file"),
 #if ENABLE_FEATURE_VI_READONLY
-		((vi_readonly || readonly) ? " [Read-only]" : ""),
+		(readonly_mode ? " [Readonly]" : ""),
 #endif
-		(file_modified ? " [modified]" : ""),
+		(file_modified ? " [Modified]" : ""),
 		cur, tot, percent);
 
 	if (ret >= 0 && ret < trunc_at)
@@ -3391,14 +3366,14 @@
 		        || strncasecmp(p, "wn", cnt) == 0
 		        || strncasecmp(p, "x", cnt) == 0
 		) {
-			cnt = file_write(cfn, text, end - 1);
+			cnt = file_write(current_filename, text, end - 1);
 			if (cnt < 0) {
 				if (cnt == -1)
 					psbs("Write error: %s", strerror(errno));
 			} else {
 				file_modified = 0;
 				last_file_modified = -1;
-				psb("\"%s\" %dL, %dC", cfn, count_lines(text, end - 1), cnt);
+				psb("\"%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'
 				) {
@@ -3516,7 +3491,7 @@
 		if (dot < end - 1) {	// make sure not last char in text[]
 			*dot++ = ' ';	// replace NL with space
 			file_modified++;
-			while (isblnk(*dot)) {	// delete leading WS
+			while (isblank(*dot)) {	// delete leading WS
 				dot_delete();
 			}
 		}
@@ -3583,13 +3558,11 @@
 			break;
 		}
 		if (file_modified) {
-#if ENABLE_FEATURE_VI_READONLY
-			if (vi_readonly || readonly) {
-				psbs("\"%s\" File is read only", cfn);
+			if (ENABLE_FEATURE_VI_READONLY && readonly_mode) {
+				psbs("\"%s\" File is read only", current_filename);
 				break;
 			}
-#endif
-			cnt = file_write(cfn, text, end - 1);
+			cnt = file_write(current_filename, text, end - 1);
 			if (cnt < 0) {
 				if (cnt == -1)
 					psbs("Write error: %s", strerror(errno));
@@ -3644,7 +3617,7 @@
 		} else if (strchr("wW", c1)) {
 			if (c == 'c') {
 				// don't include trailing WS as part of word
-				while (isblnk(*q)) {
+				while (isblank(*q)) {
 					if (q <= text || q[-1] == '\n')
 						break;
 					q--;




More information about the busybox-cvs mailing list