From 31caae20ebbd514730fdbf4ac82c61daf54c4686 Mon Sep 17 00:00:00 2001 From: Maxime Coste Date: Sat, 14 Mar 2020 12:58:22 +1100 Subject: [PATCH] Allow reading from fifo in readonly buffers readonly is supposed to prevent the user from modifying the buffer and it can be useful to generate a readonly fifo buffer. Fixes #3398 --- src/buffer_utils.cc | 50 +++++++++++-------- src/commands.cc | 8 +-- .../regression/3398-readonly-fifo-failure/cmd | 1 + .../regression/3398-readonly-fifo-failure/out | 1 + test/regression/3398-readonly-fifo-failure/rc | 2 + .../3398-readonly-fifo-failure/script | 8 +++ 6 files changed, 45 insertions(+), 25 deletions(-) create mode 100644 test/regression/3398-readonly-fifo-failure/cmd create mode 100644 test/regression/3398-readonly-fifo-failure/out create mode 100644 test/regression/3398-readonly-fifo-failure/rc create mode 100644 test/regression/3398-readonly-fifo-failure/script diff --git a/src/buffer_utils.cc b/src/buffer_utils.cc index cbead31d..0162822c 100644 --- a/src/buffer_utils.cc +++ b/src/buffer_utils.cc @@ -143,32 +143,38 @@ Buffer* create_fifo_buffer(String name, int fd, Buffer::Flags flags, bool scroll char data[buffer_size]; BufferCoord insert_coord = m_buffer.back_coord(); const int fifo = fd(); - do + { - const ssize_t count = ::read(fifo, data, buffer_size); - if (count <= 0) + auto restore_flags = on_scope_end([this, flags=m_buffer.flags()] { m_buffer.flags() = flags; }); + m_buffer.flags() &= ~Buffer::Flags::ReadOnly; + do { - closed = true; - break; - } - - auto pos = m_buffer.back_coord(); - const bool prevent_scrolling = pos == BufferCoord{0,0} and not m_scroll; - if (prevent_scrolling) - pos = m_buffer.next(pos); - - m_buffer.insert(pos, StringView(data, data+count)); - - if (prevent_scrolling) - { - m_buffer.erase({0,0}, m_buffer.next({0,0})); - // in the other case, the buffer will have automatically - // inserted a \n to guarantee its invariant. - if (data[count-1] == '\n') - m_buffer.insert(m_buffer.end_coord(), "\n"); + + const ssize_t count = ::read(fifo, data, buffer_size); + if (count <= 0) + { + closed = true; + break; + } + + auto pos = m_buffer.back_coord(); + const bool prevent_scrolling = pos == BufferCoord{0,0} and not m_scroll; + if (prevent_scrolling) + pos = m_buffer.next(pos); + + m_buffer.insert(pos, StringView(data, data+count)); + + if (prevent_scrolling) + { + m_buffer.erase({0,0}, m_buffer.next({0,0})); + // in the other case, the buffer will have automatically + // inserted a \n to guarantee its invariant. + if (data[count-1] == '\n') + m_buffer.insert(m_buffer.end_coord(), "\n"); + } } + while (++loop < max_loop and fd_readable(fifo)); } - while (++loop < max_loop and fd_readable(fifo)); if (insert_coord != m_buffer.back_coord()) m_buffer.run_hook_in_own_context( diff --git a/src/commands.cc b/src/commands.cc index ee45bcde..09e73ce3 100644 --- a/src/commands.cc +++ b/src/commands.cc @@ -472,14 +472,16 @@ const ParameterDesc write_params{ void do_write_buffer(Context& context, Optional filename, WriteFlags flags, bool atomic = false) { Buffer& buffer = context.buffer(); + const bool is_file = (bool)(buffer.flags() & Buffer::Flags::File); - if (not filename and !(buffer.flags() & Buffer::Flags::File)) + if (not filename and !is_file) throw runtime_error("cannot write a non file buffer without a filename"); + const bool is_readonly = (bool)(context.buffer().flags() & Buffer::Flags::ReadOnly); // if the buffer is in read-only mode and we try to save it directly // or we try to write to it indirectly using e.g. a symlink, throw an error - if ((context.buffer().flags() & Buffer::Flags::ReadOnly) - and (not filename or real_path(*filename) == buffer.name())) + if (is_file and is_readonly and + (not filename or real_path(*filename) == buffer.name())) throw runtime_error("cannot overwrite the buffer when in readonly mode"); auto effective_filename = not filename ? buffer.name() : parse_filename(*filename); diff --git a/test/regression/3398-readonly-fifo-failure/cmd b/test/regression/3398-readonly-fifo-failure/cmd new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/test/regression/3398-readonly-fifo-failure/cmd @@ -0,0 +1 @@ + diff --git a/test/regression/3398-readonly-fifo-failure/out b/test/regression/3398-readonly-fifo-failure/out new file mode 100644 index 00000000..907b3081 --- /dev/null +++ b/test/regression/3398-readonly-fifo-failure/out @@ -0,0 +1 @@ +blah diff --git a/test/regression/3398-readonly-fifo-failure/rc b/test/regression/3398-readonly-fifo-failure/rc new file mode 100644 index 00000000..67641227 --- /dev/null +++ b/test/regression/3398-readonly-fifo-failure/rc @@ -0,0 +1,2 @@ +nop %sh{ mkfifo test-fifo; ( printf 'blah' > test-fifo ) /dev/null 2>&1 & } +edit -readonly -fifo test-fifo out diff --git a/test/regression/3398-readonly-fifo-failure/script b/test/regression/3398-readonly-fifo-failure/script new file mode 100644 index 00000000..f86cd7c2 --- /dev/null +++ b/test/regression/3398-readonly-fifo-failure/script @@ -0,0 +1,8 @@ +ui_out '{ "jsonrpc": "2.0", "method": "set_ui_options", "params": [{}] }' +ui_out '{ "jsonrpc": "2.0", "method": "draw", "params": [[[{ "face": { "fg": "black", "bg": "cyan", "attributes": [] }, "contents": "\u000a" }]], { "fg": "default", "bg": "default", "attributes": [] }, { "fg": "blue", "bg": "default", "attributes": [] }] }' +ui_out '{ "jsonrpc": "2.0", "method": "menu_hide", "params": [] }' +ui_out '{ "jsonrpc": "2.0", "method": "info_hide", "params": [] }' +ui_out '{ "jsonrpc": "2.0", "method": "draw_status", "params": [[], [{ "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "out 1:1 " }, { "face": { "fg": "black", "bg": "yellow", "attributes": [] }, "contents": "[scratch][fifo]" }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": " " }, { "face": { "fg": "blue", "bg": "default", "attributes": [] }, "contents": "1 sel" }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": " - client0@[kak-tests]" }], { "fg": "cyan", "bg": "default", "attributes": [] }] }' +ui_out '{ "jsonrpc": "2.0", "method": "set_cursor", "params": ["buffer", { "line": 0, "column": 0 }] }' +ui_out '{ "jsonrpc": "2.0", "method": "refresh", "params": [true] }' +ui_out '{ "jsonrpc": "2.0", "method": "draw", "params": [[[{ "face": { "fg": "black", "bg": "white", "attributes": [] }, "contents": "b" }, { "face": { "fg": "default", "bg": "default", "attributes": [] }, "contents": "lah\u000a" }]], { "fg": "default", "bg": "default", "attributes": [] }, { "fg": "blue", "bg": "default", "attributes": [] }] }'