From 65c41e6c2b5015ff0b0485aadd362c0883a02a85 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 16 Mar 2017 23:24:20 +0300 Subject: main: Make `-s -` read from stdin --- src/nvim/main.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/nvim/main.c b/src/nvim/main.c index 4416bd067c..99dd9fc18f 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -1056,7 +1056,9 @@ scripterror: mch_errmsg("\"\n"); mch_exit(2); } - if ((scriptin[0] = mch_fopen(argv[0], READBIN)) == NULL) { + if (STRCMP(argv[0], "-") == 0) { + scriptin[0] = stdin; + } else if ((scriptin[0] = mch_fopen(argv[0], READBIN)) == NULL) { mch_errmsg(_("Cannot open for reading: \"")); mch_errmsg(argv[0]); mch_errmsg("\"\n"); -- cgit From 0320a58082f922b54cb36ce57064c07a9e781aa8 Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 17 Mar 2017 00:04:03 +0300 Subject: functests: Check that `-s` works as expected --- test/functional/core/main_spec.lua | 60 ++++++++++++++++++++++++++++++++++++++ test/helpers.lua | 39 +++++++++++++++++++++++-- test/unit/preprocess.lua | 26 ++++------------- 3 files changed, 102 insertions(+), 23 deletions(-) create mode 100644 test/functional/core/main_spec.lua diff --git a/test/functional/core/main_spec.lua b/test/functional/core/main_spec.lua new file mode 100644 index 0000000000..50c6009b53 --- /dev/null +++ b/test/functional/core/main_spec.lua @@ -0,0 +1,60 @@ +local lfs = require('lfs') +local helpers = require('test.functional.helpers')(after_each) +local global_helpers = require('test.helpers') + +local eq = helpers.eq +local neq = helpers.neq +local sleep = helpers.sleep +local nvim_prog = helpers.nvim_prog +local write_file = helpers.write_file + +local popen_w = global_helpers.popen_w +local popen_r = global_helpers.popen_r + +describe('Command-line option', function() + describe('-s', function() + local fname = 'Xtest-functional-core-main-s' + local dollar_fname = '$' .. fname + before_each(function() + os.remove(fname) + os.remove(dollar_fname) + end) + after_each(function() + os.remove(fname) + os.remove(dollar_fname) + end) + it('treats - as stdin', function() + eq(nil, lfs.attributes(fname)) + local pipe = popen_w( + nvim_prog, '-u', 'NONE', '-i', 'NONE', '--headless', '-s', '-', + fname) + pipe:write(':call setline(1, "42")\n') + pipe:write(':wqall!\n') + pipe:close() + local max_sec = 10 + while max_sec > 0 do + local attrs = lfs.attributes(fname) + if attrs then + eq(#('42\n'), attrs.size) + break + else + max_sec = max_sec - 1 + sleep(1000) + end + end + neq(0, max_sec) + end) + it('does not expand $VAR', function() + eq(nil, lfs.attributes(fname)) + eq(true, not not dollar_fname:find('%$%w+')) + write_file(dollar_fname, ':call setline(1, "100500")\n:wqall!\n') + local pipe = popen_r( + nvim_prog, '-u', 'NONE', '-i', 'NONE', '--headless', '-s', dollar_fname, + fname) + local stdout = pipe:read('*a') + eq('', stdout) + local attrs = lfs.attributes(fname) + eq(#('100500\n'), attrs.size) + end) + end) +end) diff --git a/test/helpers.lua b/test/helpers.lua index e5224349c2..6ee95a4396 100644 --- a/test/helpers.lua +++ b/test/helpers.lua @@ -1,6 +1,38 @@ local assert = require('luassert') local lfs = require('lfs') +local quote_me = '[^.%w%+%-%@%_%/]' -- complement (needn't quote) +local function shell_quote(str) + if string.find(str, quote_me) or str == '' then + return '"' .. str:gsub('[$%%"\\]', '\\%0') .. '"' + else + return str + end +end + +local function argss_to_cmd(...) + local cmd = '' + for i = 1, select('#', ...) do + local arg = select(i, ...) + if type(arg) == 'string' then + cmd = cmd .. ' ' ..shell_quote(arg) + else + for _, arg in ipairs(arg) do + cmd = cmd .. ' ' .. shell_quote(arg) + end + end + end + return cmd +end + +local function popen_r(...) + return io.popen(argss_to_cmd(...), 'r') +end + +local function popen_w(...) + return io.popen(argss_to_cmd(...), 'w') +end + local check_logs_useless_lines = { ['Warning: noted but unhandled ioctl']=1, ['could cause spurious value errors to appear']=2, @@ -95,7 +127,7 @@ local uname = (function() return platform end - local status, f = pcall(io.popen, "uname -s") + local status, f = pcall(popen_r, 'uname', '-s') if status then platform = f:read("*l") f:close() @@ -215,7 +247,7 @@ local function check_cores(app) end local function which(exe) - local pipe = io.popen('which ' .. exe, 'r') + local pipe = popen_r('which', exe) local ret = pipe:read('*a') pipe:close() if ret == '' then @@ -238,4 +270,7 @@ return { check_cores = check_cores, hasenv = hasenv, which = which, + argss_to_cmd = argss_to_cmd, + popen_r = popen_r, + popen_w = popen_w, } diff --git a/test/unit/preprocess.lua b/test/unit/preprocess.lua index 363358d134..18b2284e3d 100644 --- a/test/unit/preprocess.lua +++ b/test/unit/preprocess.lua @@ -2,6 +2,10 @@ -- windows, will probably need quite a bit of adjustment to run there. local ffi = require("ffi") +local global_helpers = require('test.helpers') + +local popen_r = global_helpers.popen_r +local argss_to_cmd = global_helpers.argss_to_cmd local ccs = {} @@ -22,15 +26,6 @@ table.insert(ccs, {path = {"/usr/bin/env", "gcc-4.7"}, type = "gcc"}) table.insert(ccs, {path = {"/usr/bin/env", "clang"}, type = "clang"}) table.insert(ccs, {path = {"/usr/bin/env", "icc"}, type = "gcc"}) -local quote_me = '[^.%w%+%-%@%_%/]' -- complement (needn't quote) -local function shell_quote(str) - if string.find(str, quote_me) or str == '' then - return "'" .. string.gsub(str, "'", [['"'"']]) .. "'" - else - return str - end -end - -- parse Makefile format dependencies into a Lua table local function parse_make_deps(deps) -- remove line breaks and line concatenators @@ -149,16 +144,6 @@ function Gcc:add_to_include_path(...) end end -local function argss_to_cmd(...) - local cmd = '' - for i = 1, select('#', ...) do - for _, arg in ipairs(select(i, ...)) do - cmd = cmd .. ' ' .. shell_quote(arg) - end - end - return cmd -end - -- returns a list of the headers files upon which this file relies function Gcc:dependencies(hdr) local cmd = argss_to_cmd(self.path, {'-M', hdr}) .. ' 2>&1' @@ -173,9 +158,8 @@ function Gcc:dependencies(hdr) end local function repeated_call(...) - local cmd = argss_to_cmd(...) for _ = 1, 10 do - local stream = io.popen(cmd) + local stream = popen_r(...) local ret = stream:read('*a') stream:close() if ret then -- cgit From 0e9286a19ed51ba9a2d6bfd06432c90e36cad4bd Mon Sep 17 00:00:00 2001 From: ZyX Date: Fri, 17 Mar 2017 10:57:19 +0300 Subject: tests: Fix CI failures --- test/functional/core/main_spec.lua | 12 ++++++------ test/helpers.lua | 18 ++++++++++++++++-- test/unit/preprocess.lua | 36 ++++++++++++------------------------ 3 files changed, 34 insertions(+), 32 deletions(-) diff --git a/test/functional/core/main_spec.lua b/test/functional/core/main_spec.lua index 50c6009b53..a374b4c040 100644 --- a/test/functional/core/main_spec.lua +++ b/test/functional/core/main_spec.lua @@ -9,7 +9,7 @@ local nvim_prog = helpers.nvim_prog local write_file = helpers.write_file local popen_w = global_helpers.popen_w -local popen_r = global_helpers.popen_r +local repeated_popen_r = global_helpers.repeated_popen_r describe('Command-line option', function() describe('-s', function() @@ -48,13 +48,13 @@ describe('Command-line option', function() eq(nil, lfs.attributes(fname)) eq(true, not not dollar_fname:find('%$%w+')) write_file(dollar_fname, ':call setline(1, "100500")\n:wqall!\n') - local pipe = popen_r( + local pipe = repeated_popen_r( nvim_prog, '-u', 'NONE', '-i', 'NONE', '--headless', '-s', dollar_fname, fname) - local stdout = pipe:read('*a') - eq('', stdout) - local attrs = lfs.attributes(fname) - eq(#('100500\n'), attrs.size) + local stdout = pipe:read('*a') + eq('', stdout) + local attrs = lfs.attributes(fname) + eq(#('100500\n'), attrs.size) end) end) end) diff --git a/test/helpers.lua b/test/helpers.lua index 6ee95a4396..be83ff314b 100644 --- a/test/helpers.lua +++ b/test/helpers.lua @@ -17,8 +17,8 @@ local function argss_to_cmd(...) if type(arg) == 'string' then cmd = cmd .. ' ' ..shell_quote(arg) else - for _, arg in ipairs(arg) do - cmd = cmd .. ' ' .. shell_quote(arg) + for _, subarg in ipairs(arg) do + cmd = cmd .. ' ' .. shell_quote(subarg) end end end @@ -257,6 +257,19 @@ local function which(exe) end end +local function repeated_popen_r(...) + for _ = 1, 10 do + local stream = popen_r(...) + local ret = stream:read('*a') + stream:close() + if ret then + return ret + end + end + print('ERROR: Failed to execute ' .. argss_to_cmd(...) .. ': nil return after 10 attempts') + return nil +end + return { eq = eq, neq = neq, @@ -273,4 +286,5 @@ return { argss_to_cmd = argss_to_cmd, popen_r = popen_r, popen_w = popen_w, + repeated_popen_r = repeated_popen_r, } diff --git a/test/unit/preprocess.lua b/test/unit/preprocess.lua index 18b2284e3d..ac0c5a85c1 100644 --- a/test/unit/preprocess.lua +++ b/test/unit/preprocess.lua @@ -4,8 +4,8 @@ local ffi = require("ffi") local global_helpers = require('test.helpers') -local popen_r = global_helpers.popen_r local argss_to_cmd = global_helpers.argss_to_cmd +local repeated_popen_r = global_helpers.repeated_popen_r local ccs = {} @@ -157,28 +157,15 @@ function Gcc:dependencies(hdr) end end -local function repeated_call(...) - for _ = 1, 10 do - local stream = popen_r(...) - local ret = stream:read('*a') - stream:close() - if ret then - return ret - end - end - print('ERROR: preprocess.lua: Failed to execute ' .. cmd .. ': nil return after 10 attempts') - return nil -end - function Gcc:filter_standard_defines(defines) if not self.standard_defines then local pseudoheader_fname = 'tmp_empty_pseudoheader.h' local pseudoheader_file = io.open(pseudoheader_fname, 'w') pseudoheader_file:close() - local standard_defines = repeated_call(self.path, - self.preprocessor_extra_flags, - self.get_defines_extra_flags, - {pseudoheader_fname}) + local standard_defines = repeated_popen_r(self.path, + self.preprocessor_extra_flags, + self.get_defines_extra_flags, + {pseudoheader_fname}) os.remove(pseudoheader_fname) self.standard_defines = {} for line in standard_defines:gmatch('[^\n]+') do @@ -207,9 +194,9 @@ function Gcc:preprocess(previous_defines, ...) pseudoheader_file:flush() pseudoheader_file:close() - local defines = repeated_call(self.path, self.preprocessor_extra_flags, - self.get_defines_extra_flags, - {pseudoheader_fname}) + local defines = repeated_popen_r(self.path, self.preprocessor_extra_flags, + self.get_defines_extra_flags, + {pseudoheader_fname}) defines = self:filter_standard_defines(defines) -- lfs = require("lfs") @@ -218,9 +205,10 @@ function Gcc:preprocess(previous_defines, ...) -- io.stderr\write("CWD: #{lfs.currentdir!}\n") -- io.stderr\write("CMD: #{cmd}\n") - local declarations = repeated_call(self.path, self.preprocessor_extra_flags, - self.get_declarations_extra_flags, - {pseudoheader_fname}) + local declarations = repeated_popen_r(self.path, + self.preprocessor_extra_flags, + self.get_declarations_extra_flags, + {pseudoheader_fname}) os.remove(pseudoheader_fname) -- cgit From 3cd7bf31e2351eb2874f8431d290a3d36b0b075e Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 18 Mar 2017 00:16:23 +0300 Subject: tests: Fix repeated_popen_r usage, rename the function --- test/functional/core/main_spec.lua | 5 ++--- test/helpers.lua | 4 ++-- test/unit/preprocess.lua | 24 ++++++++++++------------ 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/test/functional/core/main_spec.lua b/test/functional/core/main_spec.lua index a374b4c040..cc781a59a1 100644 --- a/test/functional/core/main_spec.lua +++ b/test/functional/core/main_spec.lua @@ -9,7 +9,7 @@ local nvim_prog = helpers.nvim_prog local write_file = helpers.write_file local popen_w = global_helpers.popen_w -local repeated_popen_r = global_helpers.repeated_popen_r +local repeated_read_cmd = global_helpers.repeated_read_cmd describe('Command-line option', function() describe('-s', function() @@ -48,10 +48,9 @@ describe('Command-line option', function() eq(nil, lfs.attributes(fname)) eq(true, not not dollar_fname:find('%$%w+')) write_file(dollar_fname, ':call setline(1, "100500")\n:wqall!\n') - local pipe = repeated_popen_r( + local stdout = repeated_read_cmd( nvim_prog, '-u', 'NONE', '-i', 'NONE', '--headless', '-s', dollar_fname, fname) - local stdout = pipe:read('*a') eq('', stdout) local attrs = lfs.attributes(fname) eq(#('100500\n'), attrs.size) diff --git a/test/helpers.lua b/test/helpers.lua index be83ff314b..d739400c71 100644 --- a/test/helpers.lua +++ b/test/helpers.lua @@ -257,7 +257,7 @@ local function which(exe) end end -local function repeated_popen_r(...) +local function repeated_read_cmd(...) for _ = 1, 10 do local stream = popen_r(...) local ret = stream:read('*a') @@ -286,5 +286,5 @@ return { argss_to_cmd = argss_to_cmd, popen_r = popen_r, popen_w = popen_w, - repeated_popen_r = repeated_popen_r, + repeated_read_cmd = repeated_read_cmd, } diff --git a/test/unit/preprocess.lua b/test/unit/preprocess.lua index ac0c5a85c1..1073855a7d 100644 --- a/test/unit/preprocess.lua +++ b/test/unit/preprocess.lua @@ -5,7 +5,7 @@ local ffi = require("ffi") local global_helpers = require('test.helpers') local argss_to_cmd = global_helpers.argss_to_cmd -local repeated_popen_r = global_helpers.repeated_popen_r +local repeated_read_cmd = global_helpers.repeated_read_cmd local ccs = {} @@ -162,10 +162,10 @@ function Gcc:filter_standard_defines(defines) local pseudoheader_fname = 'tmp_empty_pseudoheader.h' local pseudoheader_file = io.open(pseudoheader_fname, 'w') pseudoheader_file:close() - local standard_defines = repeated_popen_r(self.path, - self.preprocessor_extra_flags, - self.get_defines_extra_flags, - {pseudoheader_fname}) + local standard_defines = repeated_read_cmd(self.path, + self.preprocessor_extra_flags, + self.get_defines_extra_flags, + {pseudoheader_fname}) os.remove(pseudoheader_fname) self.standard_defines = {} for line in standard_defines:gmatch('[^\n]+') do @@ -194,9 +194,9 @@ function Gcc:preprocess(previous_defines, ...) pseudoheader_file:flush() pseudoheader_file:close() - local defines = repeated_popen_r(self.path, self.preprocessor_extra_flags, - self.get_defines_extra_flags, - {pseudoheader_fname}) + local defines = repeated_read_cmd(self.path, self.preprocessor_extra_flags, + self.get_defines_extra_flags, + {pseudoheader_fname}) defines = self:filter_standard_defines(defines) -- lfs = require("lfs") @@ -205,10 +205,10 @@ function Gcc:preprocess(previous_defines, ...) -- io.stderr\write("CWD: #{lfs.currentdir!}\n") -- io.stderr\write("CMD: #{cmd}\n") - local declarations = repeated_popen_r(self.path, - self.preprocessor_extra_flags, - self.get_declarations_extra_flags, - {pseudoheader_fname}) + local declarations = repeated_read_cmd(self.path, + self.preprocessor_extra_flags, + self.get_declarations_extra_flags, + {pseudoheader_fname}) os.remove(pseudoheader_fname) -- cgit From 1ea7541f657e64c843b97b25ec75dd388ee916cc Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 18 Mar 2017 17:49:47 +0300 Subject: functests: Alter the order of checks Check whether `repeated_read_cmd` returned nil and did not actually run nvim or it did, but still returned nil for whatever reason. --- test/functional/core/main_spec.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/functional/core/main_spec.lua b/test/functional/core/main_spec.lua index cc781a59a1..59969538fa 100644 --- a/test/functional/core/main_spec.lua +++ b/test/functional/core/main_spec.lua @@ -51,9 +51,9 @@ describe('Command-line option', function() local stdout = repeated_read_cmd( nvim_prog, '-u', 'NONE', '-i', 'NONE', '--headless', '-s', dollar_fname, fname) - eq('', stdout) local attrs = lfs.attributes(fname) eq(#('100500\n'), attrs.size) + eq('', stdout) end) end) end) -- cgit From 38687ee394150061c24e45b44cbeb9cfbb4be142 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sat, 18 Mar 2017 17:54:04 +0300 Subject: functests: Make sure that line ending is LF and not CRLF --- test/functional/core/main_spec.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/functional/core/main_spec.lua b/test/functional/core/main_spec.lua index 59969538fa..60c89ec6c8 100644 --- a/test/functional/core/main_spec.lua +++ b/test/functional/core/main_spec.lua @@ -28,6 +28,7 @@ describe('Command-line option', function() local pipe = popen_w( nvim_prog, '-u', 'NONE', '-i', 'NONE', '--headless', '-s', '-', fname) + pipe:write(':set fileformat=unix\n') pipe:write(':call setline(1, "42")\n') pipe:write(':wqall!\n') pipe:close() @@ -47,7 +48,7 @@ describe('Command-line option', function() it('does not expand $VAR', function() eq(nil, lfs.attributes(fname)) eq(true, not not dollar_fname:find('%$%w+')) - write_file(dollar_fname, ':call setline(1, "100500")\n:wqall!\n') + write_file(dollar_fname, ':set fileformat=unix\n:call setline(1, "100500")\n:wqall!\n') local stdout = repeated_read_cmd( nvim_prog, '-u', 'NONE', '-i', 'NONE', '--headless', '-s', dollar_fname, fname) -- cgit From d4639ea6d9045edee5476ce50719a00575d565ce Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 19 Mar 2017 01:53:50 +0300 Subject: functests: Use Neovim instance and system() in place of lua io.popen --- test/functional/core/main_spec.lua | 56 +++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 25 deletions(-) diff --git a/test/functional/core/main_spec.lua b/test/functional/core/main_spec.lua index 60c89ec6c8..40c161c986 100644 --- a/test/functional/core/main_spec.lua +++ b/test/functional/core/main_spec.lua @@ -4,18 +4,30 @@ local global_helpers = require('test.helpers') local eq = helpers.eq local neq = helpers.neq -local sleep = helpers.sleep +local clear = helpers.clear +local funcs = helpers.funcs local nvim_prog = helpers.nvim_prog local write_file = helpers.write_file local popen_w = global_helpers.popen_w local repeated_read_cmd = global_helpers.repeated_read_cmd +local function nvim_prog_abs() + -- system(['build/bin/nvim']) does not work for whatever reason. It needs to + -- either be executable searched in $PATH or something starting with / or ./. + if nvim_prog:match('[/\\]') then + return funcs.fnamemodify(nvim_prog, ':p') + else + return nvim_prog + end +end + describe('Command-line option', function() describe('-s', function() local fname = 'Xtest-functional-core-main-s' local dollar_fname = '$' .. fname before_each(function() + clear() os.remove(fname) os.remove(dollar_fname) end) @@ -25,36 +37,30 @@ describe('Command-line option', function() end) it('treats - as stdin', function() eq(nil, lfs.attributes(fname)) - local pipe = popen_w( - nvim_prog, '-u', 'NONE', '-i', 'NONE', '--headless', '-s', '-', - fname) - pipe:write(':set fileformat=unix\n') - pipe:write(':call setline(1, "42")\n') - pipe:write(':wqall!\n') - pipe:close() - local max_sec = 10 - while max_sec > 0 do - local attrs = lfs.attributes(fname) - if attrs then - eq(#('42\n'), attrs.size) - break - else - max_sec = max_sec - 1 - sleep(1000) - end - end - neq(0, max_sec) + eq( + ':call setline(1, "42"):wqall!"'..fname..'" "'..fname..'" [New] 1L, 3C', + funcs.system( + {nvim_prog_abs(), '-u', 'NONE', '-i', 'NONE', '--headless', + '--cmd', 'set noswapfile shortmess+=IFW fileformats=unix', + '-s', '-', fname}, + {':call setline(1, "42")', ':wqall!', ''})) + eq(0, funcs.eval('v:shell_error')) + local attrs = lfs.attributes(fname) + eq(#('42\n'), attrs.size) end) it('does not expand $VAR', function() eq(nil, lfs.attributes(fname)) eq(true, not not dollar_fname:find('%$%w+')) - write_file(dollar_fname, ':set fileformat=unix\n:call setline(1, "100500")\n:wqall!\n') - local stdout = repeated_read_cmd( - nvim_prog, '-u', 'NONE', '-i', 'NONE', '--headless', '-s', dollar_fname, - fname) + write_file(dollar_fname, ':call setline(1, "100500")\n:wqall!\n') + eq( + ':call setline(1, "100500"):wqall!"'..fname..'" "'..fname..'" [New] 1L, 7C', + funcs.system( + {nvim_prog_abs(), '-u', 'NONE', '-i', 'NONE', '--headless', + '--cmd', 'set noswapfile shortmess+=IFW fileformats=unix', + '-s', dollar_fname, fname})) + eq(0, funcs.eval('v:shell_error')) local attrs = lfs.attributes(fname) eq(#('100500\n'), attrs.size) - eq('', stdout) end) end) end) -- cgit From 29654cfee7c9c230556e436ca3c17198bce82680 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 19 Mar 2017 03:33:55 +0300 Subject: functests: Fix testlint errors --- test/functional/core/main_spec.lua | 5 ----- 1 file changed, 5 deletions(-) diff --git a/test/functional/core/main_spec.lua b/test/functional/core/main_spec.lua index 40c161c986..ece4402703 100644 --- a/test/functional/core/main_spec.lua +++ b/test/functional/core/main_spec.lua @@ -1,17 +1,12 @@ local lfs = require('lfs') local helpers = require('test.functional.helpers')(after_each) -local global_helpers = require('test.helpers') local eq = helpers.eq -local neq = helpers.neq local clear = helpers.clear local funcs = helpers.funcs local nvim_prog = helpers.nvim_prog local write_file = helpers.write_file -local popen_w = global_helpers.popen_w -local repeated_read_cmd = global_helpers.repeated_read_cmd - local function nvim_prog_abs() -- system(['build/bin/nvim']) does not work for whatever reason. It needs to -- either be executable searched in $PATH or something starting with / or ./. -- cgit From d2268d5ebbbd472c9c4f303404dc5640208d3b3b Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 19 Mar 2017 14:13:07 +0300 Subject: functests: Do not check stdout, it is different on Windows --- test/functional/core/main_spec.lua | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/test/functional/core/main_spec.lua b/test/functional/core/main_spec.lua index ece4402703..c3aeb30398 100644 --- a/test/functional/core/main_spec.lua +++ b/test/functional/core/main_spec.lua @@ -32,13 +32,11 @@ describe('Command-line option', function() end) it('treats - as stdin', function() eq(nil, lfs.attributes(fname)) - eq( - ':call setline(1, "42"):wqall!"'..fname..'" "'..fname..'" [New] 1L, 3C', - funcs.system( - {nvim_prog_abs(), '-u', 'NONE', '-i', 'NONE', '--headless', - '--cmd', 'set noswapfile shortmess+=IFW fileformats=unix', - '-s', '-', fname}, - {':call setline(1, "42")', ':wqall!', ''})) + funcs.system( + {nvim_prog_abs(), '-u', 'NONE', '-i', 'NONE', '--headless', + '--cmd', 'set noswapfile shortmess+=IFW fileformats=unix', + '-s', '-', fname}, + {':call setline(1, "42")', ':wqall!', ''}) eq(0, funcs.eval('v:shell_error')) local attrs = lfs.attributes(fname) eq(#('42\n'), attrs.size) @@ -47,12 +45,10 @@ describe('Command-line option', function() eq(nil, lfs.attributes(fname)) eq(true, not not dollar_fname:find('%$%w+')) write_file(dollar_fname, ':call setline(1, "100500")\n:wqall!\n') - eq( - ':call setline(1, "100500"):wqall!"'..fname..'" "'..fname..'" [New] 1L, 7C', - funcs.system( - {nvim_prog_abs(), '-u', 'NONE', '-i', 'NONE', '--headless', - '--cmd', 'set noswapfile shortmess+=IFW fileformats=unix', - '-s', dollar_fname, fname})) + funcs.system( + {nvim_prog_abs(), '-u', 'NONE', '-i', 'NONE', '--headless', + '--cmd', 'set noswapfile shortmess+=IFW fileformats=unix', + '-s', dollar_fname, fname}) eq(0, funcs.eval('v:shell_error')) local attrs = lfs.attributes(fname) eq(#('100500\n'), attrs.size) -- cgit From fdfa1ed578afd41a68f05c88dc419d88051b7240 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 19 Mar 2017 16:09:48 +0300 Subject: main: Temporary fix assertion error This variant uses `fdopen()` which is not standard, but it fixes problem on my system. In next commit `scriptin` will use `FileDescriptor*` from os/fileio in place of `FILE*`. --- src/nvim/main.c | 4 +++- src/nvim/os/fs.c | 23 ++++++++++++++++++ test/functional/core/main_spec.lua | 48 ++++++++++++++++++++++++++++++++++++++ test/unit/os/fs_spec.lua | 16 +++++++++++++ 4 files changed, 90 insertions(+), 1 deletion(-) diff --git a/src/nvim/main.c b/src/nvim/main.c index 99dd9fc18f..ce0426bd8e 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -1057,7 +1057,9 @@ scripterror: mch_exit(2); } if (STRCMP(argv[0], "-") == 0) { - scriptin[0] = stdin; + const int stdin_dup_fd = os_dup(STDIN_FILENO); + FILE *const stdin_dup = fdopen(stdin_dup_fd, "r"); + scriptin[0] = stdin_dup; } else if ((scriptin[0] = mch_fopen(argv[0], READBIN)) == NULL) { mch_errmsg(_("Cannot open for reading: \"")); mch_errmsg(argv[0]); diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c index 2beeae7ec6..9ce50d4afd 100644 --- a/src/nvim/os/fs.c +++ b/src/nvim/os/fs.c @@ -430,6 +430,29 @@ int os_close(const int fd) return r; } +/// Duplicate file descriptor +/// +/// @param[in] fd File descriptor to duplicate. +/// +/// @return New file descriptor or libuv error code (< 0). +int os_dup(const int fd) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + int ret; +os_dup_dup: + ret = dup(fd); + if (ret < 0) { + const int error = os_translate_sys_error(errno); + errno = 0; + if (error == UV_EINTR) { + goto os_dup_dup; + } else { + return error; + } + } + return ret; +} + /// Read from a file /// /// Handles EINTR and ENOMEM, but not other errors. diff --git a/test/functional/core/main_spec.lua b/test/functional/core/main_spec.lua index c3aeb30398..bad5d72142 100644 --- a/test/functional/core/main_spec.lua +++ b/test/functional/core/main_spec.lua @@ -1,7 +1,9 @@ local lfs = require('lfs') local helpers = require('test.functional.helpers')(after_each) +local Screen = require('test.functional.ui.screen') local eq = helpers.eq +local feed = helpers.feed local clear = helpers.clear local funcs = helpers.funcs local nvim_prog = helpers.nvim_prog @@ -53,5 +55,51 @@ describe('Command-line option', function() local attrs = lfs.attributes(fname) eq(#('100500\n'), attrs.size) end) + it('does not crash after reading from stdin in non-headless mode', function() + local screen = Screen.new(40, 8) + screen:attach() + eq(nil, lfs.attributes(fname)) + funcs.termopen({ + nvim_prog_abs(), '-u', 'NONE', '-i', 'NONE', + '--cmd', 'set noswapfile shortmess+=IFW fileformats=unix', + '-s', '-' + }) + screen:expect([[ + ^ | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {2:[No Name] 0,0-1 All}| + | + | + ]], { + [1] = {foreground = 4210943, special = Screen.colors.Grey0}, + [2] = {special = Screen.colors.Grey0, bold = true, reverse = true} + }) + feed('i:cq') + screen:expect([[ + ^ | + [Process exited 1] | + | + | + | + | + | + | + ]]) + --[=[ Example of incorrect output: + screen:expect([[ + ^nvim: /var/tmp/portage/dev-libs/libuv-1.| + 10.2/work/libuv-1.10.2/src/unix/core.c:5| + 19: uv__close: Assertion `fd > STDERR_FI| + LENO' failed. | + | + [Process exited 6] | + | + | + ]]) + ]=] + end) end) end) diff --git a/test/unit/os/fs_spec.lua b/test/unit/os/fs_spec.lua index 860ebfdbcb..b03040260f 100644 --- a/test/unit/os/fs_spec.lua +++ b/test/unit/os/fs_spec.lua @@ -483,6 +483,22 @@ describe('fs function', function() end) end) + describe('os_dup', function() + itp('returns new file descriptor', function() + local dup0 = fs.os_dup(0) + local dup1 = fs.os_dup(1) + local dup2 = fs.os_dup(2) + local tbl = {[0]=true, [1]=true, [2]=true, + [tonumber(dup0)]=true, [tonumber(dup1)]=true, + [tonumber(dup2)]=true} + local i = 0 + for _, _ in pairs(tbl) do + i = i + 1 + end + eq(i, 6) -- All fds must be unique + end) + end) + describe('os_open', function() local new_file = 'test_new_file' local existing_file = 'unit-test-directory/test_existing.file' -- cgit From bd798a3267a496c644b339c45189b09e2a952014 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 19 Mar 2017 16:55:37 +0300 Subject: getchar: Use fileio instead of fdopen Problem: as fileio is cached and reads blocks this is going to wait until either EOF or reading enough characters to fill rbuffer. This is not good when reading user input from stdin as script. --- src/nvim/getchar.c | 90 ++++++++++++++++++++++++-------------------------- src/nvim/getchar.h | 8 +++++ src/nvim/globals.h | 4 +-- src/nvim/main.c | 14 +++++--- src/nvim/os/fileio.c | 48 +++++++++++++++++++++++++-- src/nvim/os/os_defs.h | 3 ++ src/nvim/os/win_defs.h | 4 +++ 7 files changed, 115 insertions(+), 56 deletions(-) diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index bae8ae6d91..b6e235146e 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -44,6 +44,12 @@ #include "nvim/event/loop.h" #include "nvim/os/input.h" #include "nvim/os/os.h" +#include "nvim/os/fileio.h" + + +/// Index in scriptin +static int curscript = 0; +FileDescriptor *scriptin[NSCRIPT] = { NULL }; /* * These buffers are used for storing: @@ -1244,10 +1250,13 @@ openscript ( ++curscript; /* use NameBuff for expanded name */ expand_env(name, NameBuff, MAXPATHL); - if ((scriptin[curscript] = mch_fopen((char *)NameBuff, READBIN)) == NULL) { - EMSG2(_(e_notopen), name); - if (curscript) - --curscript; + int error; + if ((scriptin[curscript] = file_open_new(&error, (char *)NameBuff, + kFileReadOnly, 0)) == NULL) { + emsgf(_(e_notopen_2), name, os_strerror(error)); + if (curscript) { + curscript--; + } return; } save_typebuf(); @@ -1297,7 +1306,7 @@ static void closescript(void) free_typebuf(); typebuf = saved_typebuf[curscript]; - fclose(scriptin[curscript]); + file_free(scriptin[curscript]); scriptin[curscript] = NULL; if (curscript > 0) --curscript; @@ -2346,7 +2355,6 @@ inchar ( { int len = 0; /* init for GCC */ int retesc = FALSE; /* return ESC with gotint */ - int script_char; if (wait_time == -1L || wait_time > 100L) { // flush output before waiting @@ -2364,45 +2372,38 @@ inchar ( } undo_off = FALSE; /* restart undo now */ - /* - * Get a character from a script file if there is one. - * If interrupted: Stop reading script files, close them all. - */ - script_char = -1; - while (scriptin[curscript] != NULL && script_char < 0 - && !ignore_script - ) { - - - if (got_int || (script_char = getc(scriptin[curscript])) < 0) { - /* Reached EOF. - * Careful: closescript() frees typebuf.tb_buf[] and buf[] may - * point inside typebuf.tb_buf[]. Don't use buf[] after this! */ + // Get a character from a script file if there is one. + // If interrupted: Stop reading script files, close them all. + ptrdiff_t read_size = -1; + while (scriptin[curscript] != NULL && read_size < 0 && !ignore_script) { + char script_char; + if (got_int + || (read_size = file_read(scriptin[curscript], &script_char, 1)) != 1) { + // Reached EOF or some error occurred. + // Careful: closescript() frees typebuf.tb_buf[] and buf[] may + // point inside typebuf.tb_buf[]. Don't use buf[] after this! closescript(); - /* - * When reading script file is interrupted, return an ESC to get - * back to normal mode. - * Otherwise return -1, because typebuf.tb_buf[] has changed. - */ - if (got_int) - retesc = TRUE; - else + // When reading script file is interrupted, return an ESC to get + // back to normal mode. + // Otherwise return -1, because typebuf.tb_buf[] has changed. + if (got_int) { + retesc = true; + } else { return -1; + } } else { buf[0] = (char_u)script_char; len = 1; } } - if (script_char < 0) { /* did not get a character from script */ - /* - * If we got an interrupt, skip all previously typed characters and - * return TRUE if quit reading script file. - * Stop reading typeahead when a single CTRL-C was read, - * fill_input_buf() returns this when not able to read from stdin. - * Don't use buf[] here, closescript() may have freed typebuf.tb_buf[] - * and buf may be pointing inside typebuf.tb_buf[]. - */ + if (read_size < 0) { // Did not get a character from script. + // If we got an interrupt, skip all previously typed characters and + // return TRUE if quit reading script file. + // Stop reading typeahead when a single CTRL-C was read, + // fill_input_buf() returns this when not able to read from stdin. + // Don't use buf[] here, closescript() may have freed typebuf.tb_buf[] + // and buf may be pointing inside typebuf.tb_buf[]. if (got_int) { #define DUM_LEN MAXMAPLEN * 3 + 3 char_u dum[DUM_LEN + 1]; @@ -2415,21 +2416,18 @@ inchar ( return retesc; } - /* - * Always flush the output characters when getting input characters - * from the user. - */ + // Always flush the output characters when getting input characters + // from the user. ui_flush(); - /* - * Fill up to a third of the buffer, because each character may be - * tripled below. - */ + // Fill up to a third of the buffer, because each character may be + // tripled below. len = os_inchar(buf, maxlen / 3, (int)wait_time, tb_change_cnt); } - if (typebuf_changed(tb_change_cnt)) + if (typebuf_changed(tb_change_cnt)) { return 0; + } return fix_input_buffer(buf, len); } diff --git a/src/nvim/getchar.h b/src/nvim/getchar.h index bdf65909b6..04517866ec 100644 --- a/src/nvim/getchar.h +++ b/src/nvim/getchar.h @@ -1,6 +1,8 @@ #ifndef NVIM_GETCHAR_H #define NVIM_GETCHAR_H +#include "nvim/os/fileio.h" + /* Values for "noremap" argument of ins_typebuf(). Also used for * map->m_noremap and menu->noremap[]. */ #define REMAP_YES 0 /* allow remapping */ @@ -12,6 +14,12 @@ #define KEYLEN_PART_MAP -2 /* keylen value for incomplete mapping */ #define KEYLEN_REMOVED 9999 /* keylen value for removed sequence */ +/// Maximum number of streams to read script from +enum { NSCRIPT = 15 }; + +/// Streams to read script from +extern FileDescriptor *scriptin[NSCRIPT]; + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "getchar.h.generated.h" #endif diff --git a/src/nvim/globals.h b/src/nvim/globals.h index f8c7c9d330..8babb06d56 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -913,9 +913,6 @@ EXTERN int do_redraw INIT(= FALSE); /* extra redraw once */ EXTERN int need_highlight_changed INIT(= true); EXTERN char *used_shada_file INIT(= NULL); // name of the ShaDa file to use -#define NSCRIPT 15 -EXTERN FILE *scriptin[NSCRIPT]; /* streams to read script from */ -EXTERN int curscript INIT(= 0); /* index in scriptin[] */ EXTERN FILE *scriptout INIT(= NULL); /* stream to write script to */ // volatile because it is used in a signal handler. @@ -1151,6 +1148,7 @@ EXTERN char_u e_norange[] INIT(= N_("E481: No range allowed")); EXTERN char_u e_noroom[] INIT(= N_("E36: Not enough room")); EXTERN char_u e_notmp[] INIT(= N_("E483: Can't get temp file name")); EXTERN char_u e_notopen[] INIT(= N_("E484: Can't open file %s")); +EXTERN char_u e_notopen_2[] INIT(= N_("E484: Can't open file %s: %s")); EXTERN char_u e_notread[] INIT(= N_("E485: Can't read file %s")); EXTERN char_u e_nowrtmsg[] INIT(= N_( "E37: No write since last change (add ! to override)")); diff --git a/src/nvim/main.c b/src/nvim/main.c index ce0426bd8e..da3ec4381e 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -1056,14 +1056,20 @@ scripterror: mch_errmsg("\"\n"); mch_exit(2); } + int error; if (STRCMP(argv[0], "-") == 0) { - const int stdin_dup_fd = os_dup(STDIN_FILENO); - FILE *const stdin_dup = fdopen(stdin_dup_fd, "r"); + const int stdin_dup_fd = os_dup(OS_STDIN_FILENO); + FileDescriptor *const stdin_dup = file_open_fd_new( + &error, stdin_dup_fd, false, 0); + assert(stdin_dup != NULL); scriptin[0] = stdin_dup; - } else if ((scriptin[0] = mch_fopen(argv[0], READBIN)) == NULL) { + } else if ((scriptin[0] = file_open_new( + &error, argv[0], kFileReadOnly, 0)) == NULL) { mch_errmsg(_("Cannot open for reading: \"")); mch_errmsg(argv[0]); - mch_errmsg("\"\n"); + mch_errmsg("\": "); + mch_errmsg(os_strerror(error)); + mch_errmsg("\n"); mch_exit(2); } save_typebuf(); diff --git a/src/nvim/os/fileio.c b/src/nvim/os/fileio.c index 775f2bd449..3742fd53de 100644 --- a/src/nvim/os/fileio.c +++ b/src/nvim/os/fileio.c @@ -45,7 +45,6 @@ int file_open(FileDescriptor *const ret_fp, const char *const fname, FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { int os_open_flags = 0; - int fd; TriState wr = kNone; #define FLAG(flags, flag, fcntl_flags, wrval, cond) \ do { \ @@ -70,13 +69,33 @@ int file_open(FileDescriptor *const ret_fp, const char *const fname, #endif #undef FLAG - fd = os_open(fname, os_open_flags, mode); + const int fd = os_open(fname, os_open_flags, mode); if (fd < 0) { return fd; } + return file_open_fd(ret_fp, fd, (wr == kTrue), mode); +} - ret_fp->wr = (wr == kTrue); +/// Wrap file descriptor with FileDescriptor structure +/// +/// @warning File descriptor wrapped like this must not be accessed by other +/// means. +/// +/// @param[out] ret_fp Address where information needed for reading from or +/// writing to a file is saved +/// @param[in] fd File descriptor to wrap. +/// @param[in] wr True if fd is opened for writing only, false if it is read +/// only. +/// @param[in] mode Permissions for the newly created file (ignored if flags +/// does not have FILE_CREATE\*). +/// +/// @return Error code (@see os_strerror()) or 0. Currently always returns 0. +int file_open_fd(FileDescriptor *const ret_fp, const int fd, + const bool wr, const int mode) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT +{ + ret_fp->wr = wr; ret_fp->fd = fd; ret_fp->eof = false; ret_fp->rv = rbuffer_new(kRWBufferSize); @@ -110,6 +129,29 @@ FileDescriptor *file_open_new(int *const error, const char *const fname, return fp; } +/// Like file_open_fd(), but allocate and return ret_fp +/// +/// @param[out] error Error code, @see os_strerror(). Is set to zero on +/// success. +/// @param[in] fd File descriptor to wrap. +/// @param[in] wr True if fd is opened for writing only, false if it is read +/// only. +/// @param[in] mode Permissions for the newly created file (ignored if flags +/// does not have FILE_CREATE\*). +/// +/// @return [allocated] Opened file or NULL in case of error. +FileDescriptor *file_open_fd_new(int *const error, const int fd, + const bool wr, const int mode) + FUNC_ATTR_NONNULL_ALL FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT +{ + FileDescriptor *const fp = xmalloc(sizeof(*fp)); + if ((*error = file_open_fd(fp, fd, wr, mode)) != 0) { + xfree(fp); + return NULL; + } + return fp; +} + /// Close file and free its buffer /// /// @param[in,out] fp File to close. diff --git a/src/nvim/os/os_defs.h b/src/nvim/os/os_defs.h index 14c210c69c..88d8f4b750 100644 --- a/src/nvim/os/os_defs.h +++ b/src/nvim/os/os_defs.h @@ -13,6 +13,9 @@ # include "nvim/os/unix_defs.h" #endif +/// File descriptor number used for STDIN +enum { OS_STDIN_FILENO = STDIN_FILENO }; + #define BASENAMELEN (NAME_MAX - 5) // Use the system path length if it makes sense. diff --git a/src/nvim/os/win_defs.h b/src/nvim/os/win_defs.h index 8de896c490..f3493a7eed 100644 --- a/src/nvim/os/win_defs.h +++ b/src/nvim/os/win_defs.h @@ -87,4 +87,8 @@ typedef SSIZE_T ssize_t; # define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) #endif +#ifndef STDIN_FILENO +# define STDIN_FILENO 0 +#endif + #endif // NVIM_OS_WIN_DEFS_H -- cgit From e78e75d85d91e9f14964465ea136b3899b774d6e Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 19 Mar 2017 17:29:48 +0300 Subject: fileio,main: Do not restart syscall at EAGAIN when reading for -s --- src/nvim/getchar.c | 4 ++-- src/nvim/main.c | 4 ++-- src/nvim/os/fileio.c | 38 +++++++++++++++++++++++++------------- src/nvim/os/fileio.h | 3 +++ src/nvim/os/fs.c | 27 ++++++++++++++++++++------- test/unit/os/fs_spec.lua | 6 +++--- 6 files changed, 55 insertions(+), 27 deletions(-) diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index b6e235146e..4ea8eb0513 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -2375,7 +2375,7 @@ inchar ( // Get a character from a script file if there is one. // If interrupted: Stop reading script files, close them all. ptrdiff_t read_size = -1; - while (scriptin[curscript] != NULL && read_size < 0 && !ignore_script) { + while (scriptin[curscript] != NULL && read_size <= 0 && !ignore_script) { char script_char; if (got_int || (read_size = file_read(scriptin[curscript], &script_char, 1)) != 1) { @@ -2397,7 +2397,7 @@ inchar ( } } - if (read_size < 0) { // Did not get a character from script. + if (read_size <= 0) { // Did not get a character from script. // If we got an interrupt, skip all previously typed characters and // return TRUE if quit reading script file. // Stop reading typeahead when a single CTRL-C was read, diff --git a/src/nvim/main.c b/src/nvim/main.c index da3ec4381e..8114164158 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -1060,11 +1060,11 @@ scripterror: if (STRCMP(argv[0], "-") == 0) { const int stdin_dup_fd = os_dup(OS_STDIN_FILENO); FileDescriptor *const stdin_dup = file_open_fd_new( - &error, stdin_dup_fd, false, 0); + &error, stdin_dup_fd, kFileReadOnly|kFileNonBlocking, 0); assert(stdin_dup != NULL); scriptin[0] = stdin_dup; } else if ((scriptin[0] = file_open_new( - &error, argv[0], kFileReadOnly, 0)) == NULL) { + &error, argv[0], kFileReadOnly|kFileNonBlocking, 0)) == NULL) { mch_errmsg(_("Cannot open for reading: \"")); mch_errmsg(argv[0]); mch_errmsg("\": "); diff --git a/src/nvim/os/fileio.c b/src/nvim/os/fileio.c index 3742fd53de..70ed49c3aa 100644 --- a/src/nvim/os/fileio.c +++ b/src/nvim/os/fileio.c @@ -74,7 +74,7 @@ int file_open(FileDescriptor *const ret_fp, const char *const fname, if (fd < 0) { return fd; } - return file_open_fd(ret_fp, fd, (wr == kTrue), mode); + return file_open_fd(ret_fp, fd, flags, mode); } /// Wrap file descriptor with FileDescriptor structure @@ -85,17 +85,24 @@ int file_open(FileDescriptor *const ret_fp, const char *const fname, /// @param[out] ret_fp Address where information needed for reading from or /// writing to a file is saved /// @param[in] fd File descriptor to wrap. -/// @param[in] wr True if fd is opened for writing only, false if it is read -/// only. +/// @param[in] flags Flags, @see FileOpenFlags. Currently reading from and +/// writing to the file at once is not supported, so either +/// FILE_WRITE_ONLY or FILE_READ_ONLY is required. /// @param[in] mode Permissions for the newly created file (ignored if flags /// does not have FILE_CREATE\*). /// /// @return Error code (@see os_strerror()) or 0. Currently always returns 0. int file_open_fd(FileDescriptor *const ret_fp, const int fd, - const bool wr, const int mode) + const int flags, const int mode) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { - ret_fp->wr = wr; + ret_fp->wr = !!(flags & (kFileCreate|kFileCreateOnly + |kFileTruncate + |kFileAppend + |kFileWriteOnly)); + ret_fp->non_blocking = !!(flags & kFileNonBlocking); + // Non-blocking writes not supported currently. + assert(!ret_fp->wr || !ret_fp->non_blocking); ret_fp->fd = fd; ret_fp->eof = false; ret_fp->rv = rbuffer_new(kRWBufferSize); @@ -134,18 +141,17 @@ FileDescriptor *file_open_new(int *const error, const char *const fname, /// @param[out] error Error code, @see os_strerror(). Is set to zero on /// success. /// @param[in] fd File descriptor to wrap. -/// @param[in] wr True if fd is opened for writing only, false if it is read -/// only. +/// @param[in] flags Flags, @see FileOpenFlags. /// @param[in] mode Permissions for the newly created file (ignored if flags /// does not have FILE_CREATE\*). /// /// @return [allocated] Opened file or NULL in case of error. FileDescriptor *file_open_fd_new(int *const error, const int fd, - const bool wr, const int mode) + const int flags, const int mode) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT { FileDescriptor *const fp = xmalloc(sizeof(*fp)); - if ((*error = file_open_fd(fp, fd, wr, mode)) != 0) { + if ((*error = file_open_fd(fp, fd, flags, mode)) != 0) { xfree(fp); return NULL; } @@ -224,7 +230,8 @@ static void file_rb_write_full_cb(RBuffer *const rv, FileDescriptor *const fp) return; } const size_t read_bytes = rbuffer_read(rv, writebuf, kRWBufferSize); - const ptrdiff_t wres = os_write(fp->fd, writebuf, read_bytes); + const ptrdiff_t wres = os_write(fp->fd, writebuf, read_bytes, + fp->non_blocking); if (wres != (ptrdiff_t)read_bytes) { if (wres >= 0) { fp->_error = UV_EIO; @@ -274,7 +281,7 @@ ptrdiff_t file_read(FileDescriptor *const fp, char *const ret_buf, }; assert(write_count == kRWBufferSize); const ptrdiff_t r_ret = os_readv(fp->fd, &fp->eof, iov, - ARRAY_SIZE(iov)); + ARRAY_SIZE(iov), fp->non_blocking); if (r_ret > 0) { if (r_ret > (ptrdiff_t)read_remaining) { rbuffer_produced(rv, (size_t)(r_ret - (ptrdiff_t)read_remaining)); @@ -290,7 +297,8 @@ ptrdiff_t file_read(FileDescriptor *const fp, char *const ret_buf, if (read_remaining >= kRWBufferSize) { // …otherwise leave RBuffer empty and populate only target buffer, // because filtering information through rbuffer will be more syscalls. - const ptrdiff_t r_ret = os_read(fp->fd, &fp->eof, buf, read_remaining); + const ptrdiff_t r_ret = os_read(fp->fd, &fp->eof, buf, read_remaining, + fp->non_blocking); if (r_ret >= 0) { read_remaining -= (size_t)r_ret; return (ptrdiff_t)(size - read_remaining); @@ -301,7 +309,7 @@ ptrdiff_t file_read(FileDescriptor *const fp, char *const ret_buf, size_t write_count; const ptrdiff_t r_ret = os_read(fp->fd, &fp->eof, rbuffer_write_ptr(rv, &write_count), - kRWBufferSize); + kRWBufferSize, fp->non_blocking); assert(write_count == kRWBufferSize); if (r_ret > 0) { rbuffer_produced(rv, (size_t)r_ret); @@ -311,6 +319,10 @@ ptrdiff_t file_read(FileDescriptor *const fp, char *const ret_buf, } #endif } + if (fp->non_blocking) { + // Allow only at most one os_read[v] call. + break; + } } return (ptrdiff_t)(size - read_remaining); } diff --git a/src/nvim/os/fileio.h b/src/nvim/os/fileio.h index 0b55cc695f..7c53cd4f07 100644 --- a/src/nvim/os/fileio.h +++ b/src/nvim/os/fileio.h @@ -14,6 +14,7 @@ typedef struct { RBuffer *rv; ///< Read or write buffer. bool wr; ///< True if file is in write mode. bool eof; ///< True if end of file was encountered. + bool non_blocking; ///< True if EAGAIN should not restart syscalls. } FileDescriptor; /// file_open() flags @@ -32,6 +33,8 @@ typedef enum { ///< kFileCreateOnly. kFileAppend = 64, ///< Append to the file. Implies kFileWriteOnly. Cannot ///< be used with kFileCreateOnly. + kFileNonBlocking = 128, ///< Do not restart read() or write() syscall if + ///< EAGAIN was encountered. } FileOpenFlags; static inline bool file_eof(const FileDescriptor *const fp) diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c index 9ce50d4afd..383a2ac682 100644 --- a/src/nvim/os/fs.c +++ b/src/nvim/os/fs.c @@ -462,10 +462,11 @@ os_dup_dup: /// to false. Initial value is ignored. /// @param[out] ret_buf Buffer to write to. May be NULL if size is zero. /// @param[in] size Amount of bytes to read. +/// @param[in] non_blocking Do not restart syscall if EAGAIN was encountered. /// /// @return Number of bytes read or libuv error code (< 0). -ptrdiff_t os_read(const int fd, bool *ret_eof, char *const ret_buf, - const size_t size) +ptrdiff_t os_read(const int fd, bool *const ret_eof, char *const ret_buf, + const size_t size, const bool non_blocking) FUNC_ATTR_WARN_UNUSED_RESULT { *ret_eof = false; @@ -485,7 +486,9 @@ ptrdiff_t os_read(const int fd, bool *ret_eof, char *const ret_buf, if (cur_read_bytes < 0) { const int error = os_translate_sys_error(errno); errno = 0; - if (error == UV_EINTR || error == UV_EAGAIN) { + if (non_blocking && error == UV_EAGAIN) { + break; + } else if (error == UV_EINTR || error == UV_EAGAIN) { continue; } else if (error == UV_ENOMEM && !did_try_to_free) { try_to_free_memory(); @@ -515,7 +518,11 @@ ptrdiff_t os_read(const int fd, bool *ret_eof, char *const ret_buf, /// may change, it is incorrect to use data it points to after /// os_readv(). /// @param[in] iov_size Number of buffers in iov. -ptrdiff_t os_readv(int fd, bool *ret_eof, struct iovec *iov, size_t iov_size) +/// @param[in] non_blocking Do not restart syscall if EAGAIN was encountered. +/// +/// @return Number of bytes read or libuv error code (< 0). +ptrdiff_t os_readv(const int fd, bool *const ret_eof, struct iovec *iov, + size_t iov_size, const bool non_blocking) FUNC_ATTR_NONNULL_ALL { *ret_eof = false; @@ -548,7 +555,9 @@ ptrdiff_t os_readv(int fd, bool *ret_eof, struct iovec *iov, size_t iov_size) } else if (cur_read_bytes < 0) { const int error = os_translate_sys_error(errno); errno = 0; - if (error == UV_EINTR || error == UV_EAGAIN) { + if (non_blocking && error == UV_EAGAIN) { + break; + } else if (error == UV_EINTR || error == UV_EAGAIN) { continue; } else if (error == UV_ENOMEM && !did_try_to_free) { try_to_free_memory(); @@ -568,9 +577,11 @@ ptrdiff_t os_readv(int fd, bool *ret_eof, struct iovec *iov, size_t iov_size) /// @param[in] fd File descriptor to write to. /// @param[in] buf Data to write. May be NULL if size is zero. /// @param[in] size Amount of bytes to write. +/// @param[in] non_blocking Do not restart syscall if EAGAIN was encountered. /// /// @return Number of bytes written or libuv error code (< 0). -ptrdiff_t os_write(const int fd, const char *const buf, const size_t size) +ptrdiff_t os_write(const int fd, const char *const buf, const size_t size, + const bool non_blocking) FUNC_ATTR_WARN_UNUSED_RESULT { if (buf == NULL) { @@ -588,7 +599,9 @@ ptrdiff_t os_write(const int fd, const char *const buf, const size_t size) if (cur_written_bytes < 0) { const int error = os_translate_sys_error(errno); errno = 0; - if (error == UV_EINTR || error == UV_EAGAIN) { + if (non_blocking && error == UV_EAGAIN) { + break; + } else if (error == UV_EINTR || error == UV_EAGAIN) { continue; } else { return error; diff --git a/test/unit/os/fs_spec.lua b/test/unit/os/fs_spec.lua index b03040260f..5f8be93aad 100644 --- a/test/unit/os/fs_spec.lua +++ b/test/unit/os/fs_spec.lua @@ -382,7 +382,7 @@ describe('fs function', function() buf = ffi.new('char[?]', size + 1, ('\0'):rep(size)) end local eof = ffi.new('bool[?]', 1, {true}) - local ret2 = fs.os_read(fd, eof, buf, size) + local ret2 = fs.os_read(fd, eof, buf, size, false) local ret1 = eof[0] local ret3 = '' if buf ~= nil then @@ -400,7 +400,7 @@ describe('fs function', function() end local iov = ffi.new('struct iovec[?]', #sizes, bufs) local eof = ffi.new('bool[?]', 1, {true}) - local ret2 = fs.os_readv(fd, eof, iov, #sizes) + local ret2 = fs.os_readv(fd, eof, iov, #sizes, false) local ret1 = eof[0] local ret3 = {} for i = 1,#sizes do @@ -410,7 +410,7 @@ describe('fs function', function() return ret1, ret2, ret3 end local function os_write(fd, data) - return fs.os_write(fd, data, data and #data or 0) + return fs.os_write(fd, data, data and #data or 0, false) end describe('os_path_exists', function() -- cgit From 88e1a17cde03bf35815136637ac3adf299f10cb2 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 19 Mar 2017 17:36:04 +0300 Subject: *: Fix linter errors --- src/nvim/getchar.c | 4 ++-- src/nvim/globals.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index 4ea8eb0513..8fd7cebbc9 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -2353,8 +2353,8 @@ inchar ( int tb_change_cnt ) { - int len = 0; /* init for GCC */ - int retesc = FALSE; /* return ESC with gotint */ + int len = 0; // Init for GCC. + int retesc = false; // Return ESC with gotint. if (wait_time == -1L || wait_time > 100L) { // flush output before waiting diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 8babb06d56..637e9e0a80 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -913,7 +913,7 @@ EXTERN int do_redraw INIT(= FALSE); /* extra redraw once */ EXTERN int need_highlight_changed INIT(= true); EXTERN char *used_shada_file INIT(= NULL); // name of the ShaDa file to use -EXTERN FILE *scriptout INIT(= NULL); /* stream to write script to */ +EXTERN FILE *scriptout INIT(= NULL); ///< Stream to write script to. // volatile because it is used in a signal handler. EXTERN volatile int got_int INIT(= false); // set to true when interrupt -- cgit From 83b39a4a027ec2b314e60b53c709611c770cc921 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 19 Mar 2017 18:24:20 +0300 Subject: os/fileio: Fix QB failure --- src/nvim/os/fileio.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/nvim/os/fileio.c b/src/nvim/os/fileio.c index 70ed49c3aa..7668a867a0 100644 --- a/src/nvim/os/fileio.c +++ b/src/nvim/os/fileio.c @@ -68,6 +68,10 @@ int file_open(FileDescriptor *const ret_fp, const char *const fname, FLAG(flags, kFileNoSymlink, O_NOFOLLOW, kNone, true); #endif #undef FLAG + // wr is used for kFileReadOnly flag, but on + // QB:neovim-qb-slave-ubuntu-12-04-64bit it still errors out with + // `error: variable ‘wr’ set but not used [-Werror=unused-but-set-variable]` + (void)wr; const int fd = os_open(fname, os_open_flags, mode); -- cgit From 7df4fc894183f19f61242706119ffd2c96016b54 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 19 Mar 2017 18:53:21 +0300 Subject: functests: Test -s errors --- test/functional/core/main_spec.lua | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/test/functional/core/main_spec.lua b/test/functional/core/main_spec.lua index bad5d72142..80838d1e9c 100644 --- a/test/functional/core/main_spec.lua +++ b/test/functional/core/main_spec.lua @@ -4,6 +4,7 @@ local Screen = require('test.functional.ui.screen') local eq = helpers.eq local feed = helpers.feed +local eval = helpers.eval local clear = helpers.clear local funcs = helpers.funcs local nvim_prog = helpers.nvim_prog @@ -22,6 +23,8 @@ end describe('Command-line option', function() describe('-s', function() local fname = 'Xtest-functional-core-main-s' + local fname_2 = fname .. '.2' + local nonexistent_fname = fname .. '.nonexistent' local dollar_fname = '$' .. fname before_each(function() clear() @@ -39,7 +42,7 @@ describe('Command-line option', function() '--cmd', 'set noswapfile shortmess+=IFW fileformats=unix', '-s', '-', fname}, {':call setline(1, "42")', ':wqall!', ''}) - eq(0, funcs.eval('v:shell_error')) + eq(0, eval('v:shell_error')) local attrs = lfs.attributes(fname) eq(#('42\n'), attrs.size) end) @@ -51,14 +54,13 @@ describe('Command-line option', function() {nvim_prog_abs(), '-u', 'NONE', '-i', 'NONE', '--headless', '--cmd', 'set noswapfile shortmess+=IFW fileformats=unix', '-s', dollar_fname, fname}) - eq(0, funcs.eval('v:shell_error')) + eq(0, eval('v:shell_error')) local attrs = lfs.attributes(fname) eq(#('100500\n'), attrs.size) end) it('does not crash after reading from stdin in non-headless mode', function() local screen = Screen.new(40, 8) screen:attach() - eq(nil, lfs.attributes(fname)) funcs.termopen({ nvim_prog_abs(), '-u', 'NONE', '-i', 'NONE', '--cmd', 'set noswapfile shortmess+=IFW fileformats=unix', @@ -101,5 +103,28 @@ describe('Command-line option', function() ]]) ]=] end) + it('errors out when trying to use nonexistent file with -s', function() + eq( + 'Cannot open for reading: "'..nonexistent_fname..'": no such file or directory\n', + funcs.system( + {nvim_prog_abs(), '-u', 'NONE', '-i', 'NONE', '--headless', + '--cmd', 'set noswapfile shortmess+=IFW fileformats=unix', + '--cmd', 'language C', + '-s', nonexistent_fname})) + eq(2, eval('v:shell_error')) + end) + it('errors out when trying to use -s twice', function() + write_file(fname, ':call setline(1, "1")\n:wqall!\n') + write_file(dollar_fname, ':call setline(1, "2")\n:wqall!\n') + eq( + 'Attempt to open script file again: "-s '..dollar_fname..'"\n', + funcs.system( + {nvim_prog_abs(), '-u', 'NONE', '-i', 'NONE', '--headless', + '--cmd', 'set noswapfile shortmess+=IFW fileformats=unix', + '--cmd', 'language C', + '-s', fname, '-s', dollar_fname, fname_2})) + eq(2, eval('v:shell_error')) + eq(nil, lfs.attributes(fname_2)) + end) end) end) -- cgit From ae4fae9d3efaa3be219f1e5646be1a7745c758ac Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 19 Mar 2017 19:16:44 +0300 Subject: unittests: Add tests for new fileio functions --- test/unit/os/fileio_spec.lua | 56 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/test/unit/os/fileio_spec.lua b/test/unit/os/fileio_spec.lua index 7a738ce85c..eaccdcb2a2 100644 --- a/test/unit/os/fileio_spec.lua +++ b/test/unit/os/fileio_spec.lua @@ -6,8 +6,10 @@ local itp = helpers.gen_itp(it) local eq = helpers.eq local ffi = helpers.ffi local cimport = helpers.cimport +local cppimport = helpers.cppimport -local m = cimport('./src/nvim/os/fileio.h') +local m = cimport('./src/nvim/os/os.h', './src/nvim/os/fileio.h') +cppimport('fcntl.h') local fcontents = '' for i = 0, 255 do @@ -58,6 +60,18 @@ local function file_open_new(fname, flags, mode) return ret1[0], ret2 end +local function file_open_fd(fd, flags, mode) + local ret2 = ffi.new('FileDescriptor') + local ret1 = m.file_open_fd(ret2, fd, flags, mode) + return ret1, ret2 +end + +local function file_open_fd_new(fd, flags, mode) + local ret1 = ffi.new('int[?]', 1, {0}) + local ret2 = ffi.gc(m.file_open_fd_new(ret1, fd, flags, mode), nil) + return ret1[0], ret2 +end + local function file_write(fp, buf) return m.file_write(fp, buf, #buf) end @@ -88,6 +102,46 @@ local function file_skip(fp, size) return m.file_skip(fp, size) end +describe('file_open_fd', function() + itp('can use file descriptor returned by os_open for reading', function() + local fd = m.os_open(file1, m.kO_RDONLY, 0) + local err, fp = file_open_fd(fd, m.kFileReadOnly, 0) + eq(0, err) + eq({#fcontents, fcontents}, {file_read(fp, #fcontents)}) + eq(0, m.file_close(fp)) + end) + itp('can use file descriptor returned by os_open for writing', function() + eq(nil, lfs.attributes(filec)) + local fd = m.os_open(filec, m.kO_WRONLY + m.kO_CREAT, 384) + local err, fp = file_open_fd(fd, m.kFileWriteOnly, 0) + eq(0, err) + eq(4, file_write(fp, 'test')) + eq(0, m.file_close(fp)) + eq(4, lfs.attributes(filec).size) + eq('test', io.open(filec):read('*a')) + end) +end) + +describe('file_open_fd_new', function() + itp('can use file descriptor returned by os_open for reading', function() + local fd = m.os_open(file1, m.kO_RDONLY, 0) + local err, fp = file_open_fd_new(fd, m.kFileReadOnly, 0) + eq(0, err) + eq({#fcontents, fcontents}, {file_read(fp, #fcontents)}) + eq(0, m.file_free(fp)) + end) + itp('can use file descriptor returned by os_open for writing', function() + eq(nil, lfs.attributes(filec)) + local fd = m.os_open(filec, m.kO_WRONLY + m.kO_CREAT, 384) + local err, fp = file_open_fd_new(fd, m.kFileWriteOnly, 0) + eq(0, err) + eq(4, file_write(fp, 'test')) + eq(0, m.file_free(fp)) + eq(4, lfs.attributes(filec).size) + eq('test', io.open(filec):read('*a')) + end) +end) + describe('file_open', function() itp('can create a rwx------ file with kFileCreate', function() local err, fp = file_open(filec, m.kFileCreate, 448) -- cgit From ca116df2606a0191f6600acfcd6088d5ce15ce6f Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 19 Mar 2017 19:28:16 +0300 Subject: main: Translate full -s error message, not part of it --- src/nvim/main.c | 18 ++++++++---------- src/nvim/message.c | 10 ++++------ 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/src/nvim/main.c b/src/nvim/main.c index 8114164158..efe7944fa4 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -1049,11 +1049,10 @@ static void command_line_scan(mparm_T *parmp) case 's': /* "-s {scriptin}" read from script file */ if (scriptin[0] != NULL) { scripterror: - mch_errmsg(_("Attempt to open script file again: \"")); - mch_errmsg(argv[-1]); - mch_errmsg(" "); - mch_errmsg(argv[0]); - mch_errmsg("\"\n"); + vim_snprintf((char *)IObuff, IOSIZE, + _("Attempt to open script file again: \"%s %s\"\n"), + argv[-1], argv[0]); + mch_errmsg((const char *)IObuff); mch_exit(2); } int error; @@ -1065,11 +1064,10 @@ scripterror: scriptin[0] = stdin_dup; } else if ((scriptin[0] = file_open_new( &error, argv[0], kFileReadOnly|kFileNonBlocking, 0)) == NULL) { - mch_errmsg(_("Cannot open for reading: \"")); - mch_errmsg(argv[0]); - mch_errmsg("\": "); - mch_errmsg(os_strerror(error)); - mch_errmsg("\n"); + vim_snprintf((char *)IObuff, IOSIZE, + _("Cannot open for reading: \"%s\": %s\n"), + argv[0], os_strerror(error)); + mch_errmsg((const char *)IObuff); mch_exit(2); } save_typebuf(); diff --git a/src/nvim/message.c b/src/nvim/message.c index 4cd0db21e8..c970f92666 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -2245,10 +2245,9 @@ static int do_more_prompt(int typed_char) * yet. When stderr can't be used, collect error messages until the GUI has * started and they can be displayed in a message box. */ -void mch_errmsg(char *str) +void mch_errmsg(const char *const str) + FUNC_ATTR_NONNULL_ALL { - int len; - #ifdef UNIX /* On Unix use stderr if it's a tty. * When not going to start the GUI also use stderr. @@ -2262,14 +2261,13 @@ void mch_errmsg(char *str) /* avoid a delay for a message that isn't there */ emsg_on_display = FALSE; - len = (int)STRLEN(str) + 1; + const size_t len = strlen(str) + 1; if (error_ga.ga_data == NULL) { ga_set_growsize(&error_ga, 80); error_ga.ga_itemsize = 1; } ga_grow(&error_ga, len); - memmove((char_u *)error_ga.ga_data + error_ga.ga_len, - (char_u *)str, len); + memmove(error_ga.ga_data + error_ga.ga_len, str, len); #ifdef UNIX /* remove CR characters, they are displayed */ { -- cgit From 99b4f25b990b8afe688bd8d7e5cf6dee209e1d31 Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 23 Mar 2017 21:04:45 +0300 Subject: functests: Do not run termopen test on Windows --- test/functional/core/main_spec.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/test/functional/core/main_spec.lua b/test/functional/core/main_spec.lua index 80838d1e9c..2c4d110f0f 100644 --- a/test/functional/core/main_spec.lua +++ b/test/functional/core/main_spec.lua @@ -59,6 +59,7 @@ describe('Command-line option', function() eq(#('100500\n'), attrs.size) end) it('does not crash after reading from stdin in non-headless mode', function() + if helpers.pending_win32(pending) then return end local screen = Screen.new(40, 8) screen:attach() funcs.termopen({ -- cgit From 62108c3b0be46936c83f6d4c98b44ceb5e6f77fd Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 23 Mar 2017 21:06:39 +0300 Subject: functests: Disable system(-s -) test on Windows Assume something with system() if second test hangs as well. Assume something with reading stdin if not. --- test/functional/core/main_spec.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/test/functional/core/main_spec.lua b/test/functional/core/main_spec.lua index 2c4d110f0f..1641149c87 100644 --- a/test/functional/core/main_spec.lua +++ b/test/functional/core/main_spec.lua @@ -36,6 +36,7 @@ describe('Command-line option', function() os.remove(dollar_fname) end) it('treats - as stdin', function() + if helpers.pending_win32(pending) then return end eq(nil, lfs.attributes(fname)) funcs.system( {nvim_prog_abs(), '-u', 'NONE', '-i', 'NONE', '--headless', -- cgit From 78bc52ea5397c092d01cd08296fe1dc85d998329 Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 3 Dec 2017 16:50:37 +0300 Subject: getchar: Move REMAP_… values into a enum MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/nvim/getchar.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/nvim/getchar.h b/src/nvim/getchar.h index 5c92067a23..38a2e75663 100644 --- a/src/nvim/getchar.h +++ b/src/nvim/getchar.h @@ -6,15 +6,15 @@ #include "nvim/buffer_defs.h" #include "nvim/ex_cmds_defs.h" -/// Values for "noremap" argument of ins_typebuf(). Also used for -/// map->m_noremap and menu->noremap[]. -/// @addtogroup REMAP_VALUES -/// @{ -#define REMAP_YES 0 ///< allow remapping -#define REMAP_NONE -1 ///< no remapping -#define REMAP_SCRIPT -2 ///< remap script-local mappings only -#define REMAP_SKIP -3 ///< no remapping for first char -/// @} +/// Values for "noremap" argument of ins_typebuf() +/// +/// Also used for map->m_noremap and menu->noremap[]. +enum { + REMAP_YES = 0, ///< Allow remapping. + REMAP_NONE = -1, ///< No remapping. + REMAP_SCRIPT = -2, ///< Remap script-local mappings only. + REMAP_SKIP = -3, ///< No remapping for first char. +} RemapValues; #define KEYLEN_PART_KEY -1 /* keylen value for incomplete key-code */ #define KEYLEN_PART_MAP -2 /* keylen value for incomplete mapping */ -- cgit From 387fbcd95cade4b0c037d18f404944676a59db09 Mon Sep 17 00:00:00 2001 From: b-r-o-c-k Date: Sat, 14 Apr 2018 14:21:36 -0500 Subject: win: Fix reading from stdin * Reading from stdin on Windows is fixed in the same way as it was in #8267. * The file_read function was returning without filling the destination buffer when it was called with a non-blocking file descriptor. --- src/nvim/main.c | 11 +++++++++++ src/nvim/os/fileio.c | 10 +++++----- test/functional/core/main_spec.lua | 1 - 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/nvim/main.c b/src/nvim/main.c index 067184c8c7..4f9a5a979f 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -1096,6 +1096,17 @@ scripterror: int error; if (STRCMP(argv[0], "-") == 0) { const int stdin_dup_fd = os_dup(STDIN_FILENO); +#ifdef WIN32 + // On Windows, replace the original stdin with the + // console input handle. + close(STDIN_FILENO); + const HANDLE conin_handle = + CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ, (LPSECURITY_ATTRIBUTES)NULL, + OPEN_EXISTING, 0, (HANDLE)NULL); + const int conin_fd = _open_osfhandle(conin_handle, _O_RDONLY); + assert(conin_fd == STDIN_FILENO); +#endif FileDescriptor *const stdin_dup = file_open_fd_new( &error, stdin_dup_fd, kFileReadOnly|kFileNonBlocking); assert(stdin_dup != NULL); diff --git a/src/nvim/os/fileio.c b/src/nvim/os/fileio.c index 0ef9307a5a..ccf35fd57c 100644 --- a/src/nvim/os/fileio.c +++ b/src/nvim/os/fileio.c @@ -282,6 +282,7 @@ ptrdiff_t file_read(FileDescriptor *const fp, char *const ret_buf, char *buf = ret_buf; size_t read_remaining = size; RBuffer *const rv = fp->rv; + bool called_read = false; while (read_remaining) { const size_t rv_size = rbuffer_size(rv); if (rv_size > 0) { @@ -289,7 +290,9 @@ ptrdiff_t file_read(FileDescriptor *const fp, char *const ret_buf, buf += rsize; read_remaining -= rsize; } - if (fp->eof) { + if (fp->eof + // Allow only at most one os_read[v] call. + || (called_read && fp->non_blocking)) { break; } if (read_remaining) { @@ -343,10 +346,7 @@ ptrdiff_t file_read(FileDescriptor *const fp, char *const ret_buf, } } #endif - } - if (fp->non_blocking) { - // Allow only at most one os_read[v] call. - break; + called_read = true; } } return (ptrdiff_t)(size - read_remaining); diff --git a/test/functional/core/main_spec.lua b/test/functional/core/main_spec.lua index 1641149c87..2c4d110f0f 100644 --- a/test/functional/core/main_spec.lua +++ b/test/functional/core/main_spec.lua @@ -36,7 +36,6 @@ describe('Command-line option', function() os.remove(dollar_fname) end) it('treats - as stdin', function() - if helpers.pending_win32(pending) then return end eq(nil, lfs.attributes(fname)) funcs.system( {nvim_prog_abs(), '-u', 'NONE', '-i', 'NONE', '--headless', -- cgit