[PATCH] Fix bug 6968 - vi cannot accept input from pipe

Jacob Wahlgren jacob.wahlgren at gmail.com
Tue Jul 14 16:13:46 UTC 2015


On July 9, 2015 9:18:41 PM GMT+02:00, Jacob Wahlgren 
<jacob.wahlgren at gmail.com> wrote:

>With this patch, vi can load an initial buffer from stdin and then
>start reading user input from /dev/tty. This is done by invoking it as
>'vi -'. Example from the bug report: 'ls -l dir1 | vi -'.

I've revised my previous patch to use more of the libbb functions and reduced it's
size considerably by merging the file_insert and fd_insert functions. I think this
version is much cleaner. Below is output from bloat-o-meter on my x86-64 machine.

function                                             old     new   delta
file_insert                                          363     466    +103
edit_file                                            892     928     +36
init_text_buffer                                     184     203     +19
.rodata                                            75322   75338     +16
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 4/0 up/down: 174/0)             Total: 174 bytes
    text	   data	    bss	    dec	    hex	filename
  455403	   3872	   9360	 468635	  7269b	busybox_old
  455579	   3872	   9360	 468811	  7274b	busybox_unstripped

>Don't we have something like open_file_or_stdin?
>See LONE_DASH spots, maybe.
There is a function called open_file_or_warn_stdin, which works, except that it would
make it impossible to open a file called "-". This is because LONE_DASH checks for both
bb_msg_standard_input (which I'm using in this patch as a sentinel value) and a literal
"-".

>I think we already have open_read_close().
I'm not sure how that function could be used in this context, because the length of
the input from a pipe is uknown ahead of time.

I had to change the signature of several functions to take a const filename, this is
to enable them to receive bb_msg_standard_input. They didn't need a non-const pointer
at all, so this shouldn't hurt.

Cheers,
Jacob

Signed-off-by: Jacob Wahlgren<jacob.wahlgren at gmail.com>
---
  editors/vi.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++--------------
  1 file changed, 70 insertions(+), 21 deletions(-)

diff --git a/editors/vi.c b/editors/vi.c
index 6ce513c..392243a 100644
--- a/editors/vi.c
+++ b/editors/vi.c
@@ -491,7 +491,7 @@ struct globals {
  } while (0)
  
  
-static void edit_file(char *);	// edit one file
+static void edit_file(const char *);	// edit one file
  static void do_cmd(int);	// execute a command
  static int next_tabstop(int);
  static void sync_cursor(char *, int *, int *);	// synchronize the screen cursor to dot
@@ -716,7 +716,7 @@ int vi_main(int argc, char **argv)
  
  /* read text from file or create an empty buf */
  /* will also update current_filename */
-static int init_text_buffer(char *fn)
+static int init_text_buffer(const char *fn)
  {
  	int rc;
  
@@ -735,6 +735,9 @@ static int init_text_buffer(char *fn)
  
  	if (fn != current_filename) {
  		free(current_filename);
+		if (fn == bb_msg_standard_input)
+			current_filename = NULL;
+		else
  			current_filename = xstrdup(fn);
  	}
  	rc = file_insert(fn, text, 1);
@@ -759,7 +762,7 @@ static int query_screen_dimensions(void)
  # define query_screen_dimensions() (0)
  #endif
  
-static void edit_file(char *fn)
+static void edit_file(const char *fn)
  {
  #if ENABLE_FEATURE_VI_YANKMARK
  #define cur_line edit_file__cur_line
@@ -770,9 +773,21 @@ static void edit_file(char *fn)
  #endif
  
  	editing = 1;	// 0 = exit, 1 = one file, 2 = multiple files
-	rawmode();
  	rows = 24;
  	columns = 80;
+
+	if (fn && LONE_DASH(fn)) {
+		int tmpfd;
+
+		init_text_buffer(bb_msg_standard_input);
+		tmpfd = xopen("/dev/tty", O_RDONLY);
+		xmove_fd(tmpfd, STDIN_FILENO);
+	} else {
+		init_text_buffer(fn);
+	}
+
+	rawmode();
+
  	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 */) {
@@ -791,8 +806,8 @@ static void edit_file(char *fn)
  		}
  	}
  #endif
+
  	new_screen(rows, columns);	// get memory for virtual screen
-	init_text_buffer(fn);
  
  #if ENABLE_FEATURE_VI_YANKMARK
  	YDreg = 26;			// default Yank/Delete reg
@@ -2905,22 +2920,32 @@ static char *get_input_line(const char *prompt)
  }
  
  // might reallocate text[]!
+// read from stdin if fn = bb_msg_standard_input
  static int file_insert(const char *fn, char *p, int initial)
  {
-	int cnt = -1;
-	int fd, size;
+	int cnt = 0;
+	int total = 0;
+	int unused_size = 0;
+	int fd;
  	struct stat statbuf;
  
  	if (p < text || p > end) {
  		status_line_bold("Trying to insert file outside of memory");
-		return cnt;
+		return -1;
+	} else if (fn == NULL) {
+		return -1;
  	}
  
+	// We can't use LONE_DASH because it checks for "-" too
+	if (fn == bb_msg_standard_input)
+		fd = STDIN_FILENO;
+	else
  		fd = open(fn, O_RDONLY);
+
  	if (fd < 0) {
  		if (!initial)
  			status_line_bold_errno(fn);
-		return cnt;
+		return fd;
  	}
  
  	/* Validate file */
@@ -2928,26 +2953,49 @@ static int file_insert(const char *fn, char *p, int initial)
  		status_line_bold_errno(fn);
  		goto fi;
  	}
-	if (!S_ISREG(statbuf.st_mode)) {
-		status_line_bold("'%s' is not a regular file", fn);
+
+	if (!S_ISREG (statbuf.st_mode)
+	 && !S_ISFIFO(statbuf.st_mode)) {
+		status_line_bold("'%s' is not a regular file or pipe", fn);
  		goto fi;
  	}
-	size = (statbuf.st_size < INT_MAX ? (int)statbuf.st_size : INT_MAX);
-	p += text_hole_make(p, size);
-	cnt = full_read(fd, p, size);
-	if (cnt < 0) {
-		status_line_bold_errno(fn);
-		p = text_hole_delete(p, p + size - 1, NO_UNDO);	// un-do buffer insert
-	} else if (cnt < size) {
-		// There was a partial read, shrink unused space
-		p = text_hole_delete(p + cnt, p + size - 1, NO_UNDO);
+
+	do {
+		p += text_hole_make(p + total, BUFSIZ);
+		cnt = full_read(fd, p + total, BUFSIZ);
+		total += cnt;
+	} while (cnt == BUFSIZ);
+
+	if (cnt < 0
+	 || (S_ISREG(statbuf.st_mode)
+	  && total < statbuf.st_size)) {
+		if (total) {
+			// Un-do buffer insert
+			if (cnt < 0)
+				unused_size = total - 1;
+			else
+				unused_size = total - cnt - 1;
+		}
+		total = cnt;
+	} else {
+		// Delete unused part of buffer
+		p += total;
+		unused_size = BUFSIZ - cnt - 1;
+	}
+
+	if (unused_size) {
+		text_hole_delete(p, p + unused_size, NO_UNDO);
+		if (cnt < 0)
  			status_line_bold("can't read '%s'", fn);
  	}
+
   fi:
  	close(fd);
  
+
  #if ENABLE_FEATURE_VI_READONLY
  	if (initial
+	 && fn != bb_msg_standard_input
  	 && ((access(fn, W_OK) < 0) ||
  		/* root will always have access()
  		 * so we check fileperms too */
@@ -2957,7 +3005,8 @@ static int file_insert(const char *fn, char *p, int initial)
  		SET_READONLY_FILE(readonly_mode);
  	}
  #endif
-	return cnt;
+
+	return total;	// negative if a read failed
  }
  
  static int file_write(char *fn, char *first, char *last)



More information about the busybox mailing list