changeset 9147:053bc64433ec v7.4.1857

commit https://github.com/vim/vim/commit/9f5842e63fc63d438cbffcec503e072a06f74dc2 Author: Bram Moolenaar <Bram@vim.org> Date: Sun May 29 16:17:08 2016 +0200 patch 7.4.1857 Problem: When a channel appends to a buffer that is 'nomodifiable' there is an error but appending is done anyway. Solution: Add the 'modifiable' option. Refuse to write to a 'nomodifiable' when the value is 1.
author Christian Brabandt <cb@256bit.org>
date Sun, 29 May 2016 16:30:06 +0200
parents 23a544d11064
children 8aa940057382
files runtime/doc/channel.txt src/channel.c src/structs.h src/testdir/test_channel.vim src/version.c
diffstat 5 files changed, 109 insertions(+), 17 deletions(-) [+]
line wrap: on
line diff
--- a/runtime/doc/channel.txt
+++ b/runtime/doc/channel.txt
@@ -1,4 +1,4 @@
-*channel.txt*      For Vim version 7.4.  Last change: 2016 May 24
+*channel.txt*      For Vim version 7.4.  Last change: 2016 May 29
 
 
 		  VIM REFERENCE MANUAL	  by Bram Moolenaar
@@ -578,8 +578,8 @@ See |job_setoptions()| and |ch_setoption
 "exit_cb": handler	Callback for when the job ends.  The arguments are the
 			job and the exit status.
 			Vim checks about every 10 seconds for jobs that ended.
-			The callback can also be triggered by calling
-			|job_status()|.
+			The check also be triggered by calling |job_status()|,
+			which may then invoke the exit_cb handler.
 			Note that data can be buffered, callbacks may still be
 			called after the process ends.
 							*job-timeout*
@@ -625,18 +625,22 @@ See |job_setoptions()| and |ch_setoption
 "out_io": "null"	disconnect stdout (goes to /dev/null)
 "out_io": "pipe"	stdout is connected to the channel (default)
 "out_io": "file"	stdout writes to a file
-"out_io": "buffer" 	stdout appends to a buffer
+"out_io": "buffer" 	stdout appends to a buffer (see below)
 "out_name": "/path/file" the name of the file or buffer to write to
 "out_buf": number	the number of the buffer to write to
+"out_modifiable": 0	when writing to a buffer, 'modifiable' will be off
+			(see below)
 
 				*job-err_io* *err_name* *err_buf*
 "err_io": "out"		stderr messages to go to stdout
 "err_io": "null"	disconnect stderr  (goes to /dev/null)
 "err_io": "pipe"	stderr is connected to the channel (default)
 "err_io": "file"	stderr writes to a file
-"err_io": "buffer" 	stderr appends to a buffer
+"err_io": "buffer" 	stderr appends to a buffer (see below)
 "err_name": "/path/file" the name of the file or buffer to write to
 "err_buf": number	the number of the buffer to write to
+"err_modifiable": 0	when writing to a buffer, 'modifiable' will be off
+			(see below)
 
 "block_write": number	only for testing: pretend every other write to stdin
 			will block
@@ -663,6 +667,15 @@ used to get the buffer number.
 For a new buffer 'buftype' is set to "nofile" and 'bufhidden' to "hide".  If
 you prefer other settings, create the buffer first and pass the buffer number.
 
+The "out_modifiable" and "err_modifiable" options can be used to set the
+'modifiable' option off, or write to a buffer that has 'modifiable' off.  That
+means that lines will be appended to the buffer, but the user can't easily
+change the buffer.
+
+When an existing buffer is to be written where 'modifiable' is off and the
+"out_modifiable" or "err_modifiable" options is not zero, an error is given
+and the buffer will not be written to.
+
 When the buffer written to is displayed in a window and the cursor is in the
 first column of the last line, the cursor will be moved to the newly added
 line and the window is scrolled up to show the cursor if needed.
--- a/src/channel.c
+++ b/src/channel.c
@@ -1209,9 +1209,20 @@ channel_set_options(channel_T *channel, 
 	}
 	if (buf != NULL)
 	{
-	    ch_logs(channel, "writing out to buffer '%s'",
+	    if (opt->jo_set & JO_OUT_MODIFIABLE)
+		channel->ch_part[PART_OUT].ch_nomodifiable =
+						!opt->jo_modifiable[PART_OUT];
+
+	    if (!buf->b_p_ma && !channel->ch_part[PART_OUT].ch_nomodifiable)
+	    {
+		EMSG(_(e_modifiable));
+	    }
+	    else
+	    {
+		ch_logs(channel, "writing out to buffer '%s'",
 						       (char *)buf->b_ffname);
-	    channel->ch_part[PART_OUT].ch_buffer = buf;
+		channel->ch_part[PART_OUT].ch_buffer = buf;
+	    }
 	}
     }
 
@@ -1236,9 +1247,19 @@ channel_set_options(channel_T *channel, 
 	    buf = find_buffer(opt->jo_io_name[PART_ERR], TRUE);
 	if (buf != NULL)
 	{
-	    ch_logs(channel, "writing err to buffer '%s'",
+	    if (opt->jo_set & JO_ERR_MODIFIABLE)
+		channel->ch_part[PART_ERR].ch_nomodifiable =
+						!opt->jo_modifiable[PART_ERR];
+	    if (!buf->b_p_ma && !channel->ch_part[PART_ERR].ch_nomodifiable)
+	    {
+		EMSG(_(e_modifiable));
+	    }
+	    else
+	    {
+		ch_logs(channel, "writing err to buffer '%s'",
 						       (char *)buf->b_ffname);
-	    channel->ch_part[PART_ERR].ch_buffer = buf;
+		channel->ch_part[PART_ERR].ch_buffer = buf;
+	    }
 	}
     }
 
@@ -2107,11 +2128,23 @@ invoke_one_time_callback(
 }
 
     static void
-append_to_buffer(buf_T *buffer, char_u *msg, channel_T *channel)
+append_to_buffer(buf_T *buffer, char_u *msg, channel_T *channel, int part)
 {
     buf_T	*save_curbuf = curbuf;
     linenr_T    lnum = buffer->b_ml.ml_line_count;
     int		save_write_to = buffer->b_write_to_channel;
+    chanpart_T  *ch_part = &channel->ch_part[part];
+    int		save_p_ma = buffer->b_p_ma;
+
+    if (!buffer->b_p_ma && !ch_part->ch_nomodifiable)
+    {
+	if (!ch_part->ch_nomod_error)
+	{
+	    ch_error(channel, "Buffer is not modifiable, cannot append");
+	    ch_part->ch_nomod_error = TRUE;
+	}
+	return;
+    }
 
     /* If the buffer is also used as input insert above the last
      * line. Don't write these lines. */
@@ -2124,6 +2157,7 @@ append_to_buffer(buf_T *buffer, char_u *
     /* Append to the buffer */
     ch_logn(channel, "appending line %d to buffer", (int)lnum + 1);
 
+    buffer->b_p_ma = TRUE;
     curbuf = buffer;
     u_sync(TRUE);
     /* ignore undo failure, undo is not very useful here */
@@ -2132,6 +2166,10 @@ append_to_buffer(buf_T *buffer, char_u *
     ml_append(lnum, msg, 0, FALSE);
     appended_lines_mark(lnum, 1L);
     curbuf = save_curbuf;
+    if (ch_part->ch_nomodifiable)
+	buffer->b_p_ma = FALSE;
+    else
+	buffer->b_p_ma = save_p_ma;
 
     if (buffer->b_nwindows > 0)
     {
@@ -2359,7 +2397,7 @@ may_invoke_callback(channel_T *channel, 
 		/* JSON or JS mode: re-encode the message. */
 		msg = json_encode(listtv, ch_mode);
 	    if (msg != NULL)
-		append_to_buffer(buffer, msg, channel);
+		append_to_buffer(buffer, msg, channel, part);
 	}
 
 	if (callback != NULL)
@@ -3915,6 +3953,16 @@ get_job_options(typval_T *tv, jobopt_T *
 		    return FAIL;
 		}
 	    }
+	    else if (STRCMP(hi->hi_key, "out_modifiable") == 0
+		    || STRCMP(hi->hi_key, "err_modifiable") == 0)
+	    {
+		part = part_from_char(*hi->hi_key);
+
+		if (!(supported & JO_OUT_IO))
+		    break;
+		opt->jo_set |= JO_OUT_MODIFIABLE << (part - PART_OUT);
+		opt->jo_modifiable[part] = get_tv_number(item);
+	    }
 	    else if (STRCMP(hi->hi_key, "in_top") == 0
 		    || STRCMP(hi->hi_key, "in_bot") == 0)
 	    {
--- a/src/structs.h
+++ b/src/structs.h
@@ -1401,6 +1401,8 @@ typedef struct {
     partial_T	*ch_partial;
 
     buf_T	*ch_buffer;	/* buffer to read from or write to */
+    int		ch_nomodifiable; /* TRUE when buffer can be 'nomodifiable' */
+    int		ch_nomod_error;	/* TRUE when e_modifiable was given */
     int		ch_buf_append;	/* write appended lines instead top-bot */
     linenr_T	ch_buf_top;	/* next line to send */
     linenr_T	ch_buf_bot;	/* last line to send */
@@ -1477,6 +1479,8 @@ struct channel_S {
 #define JO_IN_BUF	    0x4000000	/* "in_buf" (JO_OUT_BUF << 2) */
 #define JO_CHANNEL	    0x8000000	/* "channel" */
 #define JO_BLOCK_WRITE	    0x10000000	/* "block_write" */
+#define JO_OUT_MODIFIABLE   0x20000000	/* "out_modifiable" */
+#define JO_ERR_MODIFIABLE   0x40000000	/* "err_modifiable" (JO_OUT_ << 1) */
 #define JO_ALL		    0x7fffffff
 
 #define JO_MODE_ALL	(JO_MODE + JO_IN_MODE + JO_OUT_MODE + JO_ERR_MODE)
@@ -1500,6 +1504,7 @@ typedef struct
     char_u	jo_io_name_buf[4][NUMBUFLEN];
     char_u	*jo_io_name[4];	/* not allocated! */
     int		jo_io_buf[4];
+    int		jo_modifiable[4];
     channel_T	*jo_channel;
 
     linenr_T	jo_in_top;
--- a/src/testdir/test_channel.vim
+++ b/src/testdir/test_channel.vim
@@ -676,7 +676,7 @@ func Test_nl_write_both_file()
   endtry
 endfunc
 
-func Run_test_pipe_to_buffer(use_name)
+func Run_test_pipe_to_buffer(use_name, nomod)
   if !has('job')
     return
   endif
@@ -691,6 +691,9 @@ func Run_test_pipe_to_buffer(use_name)
     quit
     let firstline = ''
   endif
+  if a:nomod
+    let options['out_modifiable'] = 0
+  endif
   let job = job_start(s:python . " test_channel_pipe.py", options)
   call assert_equal("run", job_status(job))
   try
@@ -705,6 +708,11 @@ func Run_test_pipe_to_buffer(use_name)
       $del
     endif
     call assert_equal([firstline, 'line one', 'line two', 'this', 'AND this', 'Goodbye!'], getline(1, '$'))
+    if a:nomod
+      call assert_equal(0, &modifiable)
+    else
+      call assert_equal(1, &modifiable)
+    endif
     bwipe!
   finally
     call job_stop(job)
@@ -712,14 +720,18 @@ func Run_test_pipe_to_buffer(use_name)
 endfunc
 
 func Test_pipe_to_buffer_name()
-  call Run_test_pipe_to_buffer(1)
+  call Run_test_pipe_to_buffer(1, 0)
 endfunc
 
 func Test_pipe_to_buffer_nr()
-  call Run_test_pipe_to_buffer(0)
+  call Run_test_pipe_to_buffer(0, 0)
 endfunc
 
-func Run_test_pipe_err_to_buffer(use_name)
+func Test_pipe_to_buffer_name_nomod()
+  call Run_test_pipe_to_buffer(1, 1)
+endfunc
+
+func Run_test_pipe_err_to_buffer(use_name, nomod)
   if !has('job')
     return
   endif
@@ -734,6 +746,9 @@ func Run_test_pipe_err_to_buffer(use_nam
     quit
     let firstline = ''
   endif
+  if a:nomod
+    let options['err_modifiable'] = 0
+  endif
   let job = job_start(s:python . " test_channel_pipe.py", options)
   call assert_equal("run", job_status(job))
   try
@@ -745,6 +760,11 @@ func Run_test_pipe_err_to_buffer(use_nam
     sp pipe-err
     call s:waitFor('line("$") >= 5')
     call assert_equal([firstline, 'line one', 'line two', 'this', 'AND this'], getline(1, '$'))
+    if a:nomod
+      call assert_equal(0, &modifiable)
+    else
+      call assert_equal(1, &modifiable)
+    endif
     bwipe!
   finally
     call job_stop(job)
@@ -752,11 +772,15 @@ func Run_test_pipe_err_to_buffer(use_nam
 endfunc
 
 func Test_pipe_err_to_buffer_name()
-  call Run_test_pipe_err_to_buffer(1)
+  call Run_test_pipe_err_to_buffer(1, 0)
 endfunc
   
 func Test_pipe_err_to_buffer_nr()
-  call Run_test_pipe_err_to_buffer(0)
+  call Run_test_pipe_err_to_buffer(0, 0)
+endfunc
+  
+func Test_pipe_err_to_buffer_name_nomod()
+  call Run_test_pipe_err_to_buffer(1, 1)
 endfunc
   
 func Test_pipe_both_to_buffer()
--- a/src/version.c
+++ b/src/version.c
@@ -754,6 +754,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1857,
+/**/
     1856,
 /**/
     1855,