mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
Merge #8276 'startup: Make -s - read from stdin'
This commit is contained in:
commit
7a13611ba2
@ -48,8 +48,14 @@
|
|||||||
#include "nvim/event/loop.h"
|
#include "nvim/event/loop.h"
|
||||||
#include "nvim/os/input.h"
|
#include "nvim/os/input.h"
|
||||||
#include "nvim/os/os.h"
|
#include "nvim/os/os.h"
|
||||||
|
#include "nvim/os/fileio.h"
|
||||||
#include "nvim/api/private/handle.h"
|
#include "nvim/api/private/handle.h"
|
||||||
|
|
||||||
|
|
||||||
|
/// Index in scriptin
|
||||||
|
static int curscript = 0;
|
||||||
|
FileDescriptor *scriptin[NSCRIPT] = { NULL };
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* These buffers are used for storing:
|
* These buffers are used for storing:
|
||||||
* - stuffed characters: A command that is translated into another command.
|
* - stuffed characters: A command that is translated into another command.
|
||||||
@ -1243,10 +1249,13 @@ openscript (
|
|||||||
++curscript;
|
++curscript;
|
||||||
/* use NameBuff for expanded name */
|
/* use NameBuff for expanded name */
|
||||||
expand_env(name, NameBuff, MAXPATHL);
|
expand_env(name, NameBuff, MAXPATHL);
|
||||||
if ((scriptin[curscript] = mch_fopen((char *)NameBuff, READBIN)) == NULL) {
|
int error;
|
||||||
EMSG2(_(e_notopen), name);
|
if ((scriptin[curscript] = file_open_new(&error, (char *)NameBuff,
|
||||||
if (curscript)
|
kFileReadOnly, 0)) == NULL) {
|
||||||
--curscript;
|
emsgf(_(e_notopen_2), name, os_strerror(error));
|
||||||
|
if (curscript) {
|
||||||
|
curscript--;
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
save_typebuf();
|
save_typebuf();
|
||||||
@ -1296,7 +1305,7 @@ static void closescript(void)
|
|||||||
free_typebuf();
|
free_typebuf();
|
||||||
typebuf = saved_typebuf[curscript];
|
typebuf = saved_typebuf[curscript];
|
||||||
|
|
||||||
fclose(scriptin[curscript]);
|
file_free(scriptin[curscript], false);
|
||||||
scriptin[curscript] = NULL;
|
scriptin[curscript] = NULL;
|
||||||
if (curscript > 0)
|
if (curscript > 0)
|
||||||
--curscript;
|
--curscript;
|
||||||
@ -2336,9 +2345,8 @@ inchar (
|
|||||||
int tb_change_cnt
|
int tb_change_cnt
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
int len = 0; /* init for GCC */
|
int len = 0; // Init for GCC.
|
||||||
int retesc = FALSE; /* return ESC with gotint */
|
int retesc = false; // Return ESC with gotint.
|
||||||
int script_char;
|
|
||||||
|
|
||||||
if (wait_time == -1L || wait_time > 100L) {
|
if (wait_time == -1L || wait_time > 100L) {
|
||||||
// flush output before waiting
|
// flush output before waiting
|
||||||
@ -2356,45 +2364,38 @@ inchar (
|
|||||||
}
|
}
|
||||||
undo_off = FALSE; /* restart undo now */
|
undo_off = FALSE; /* restart undo now */
|
||||||
|
|
||||||
/*
|
// Get a character from a script file if there is one.
|
||||||
* Get a character from a script file if there is one.
|
// If interrupted: Stop reading script files, close them all.
|
||||||
* If interrupted: Stop reading script files, close them all.
|
ptrdiff_t read_size = -1;
|
||||||
*/
|
while (scriptin[curscript] != NULL && read_size <= 0 && !ignore_script) {
|
||||||
script_char = -1;
|
char script_char;
|
||||||
while (scriptin[curscript] != NULL && script_char < 0
|
if (got_int
|
||||||
&& !ignore_script
|
|| (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!
|
||||||
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! */
|
|
||||||
closescript();
|
closescript();
|
||||||
/*
|
// When reading script file is interrupted, return an ESC to get
|
||||||
* When reading script file is interrupted, return an ESC to get
|
// back to normal mode.
|
||||||
* back to normal mode.
|
// Otherwise return -1, because typebuf.tb_buf[] has changed.
|
||||||
* Otherwise return -1, because typebuf.tb_buf[] has changed.
|
if (got_int) {
|
||||||
*/
|
retesc = true;
|
||||||
if (got_int)
|
} else {
|
||||||
retesc = TRUE;
|
|
||||||
else
|
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
buf[0] = (char_u)script_char;
|
buf[0] = (char_u)script_char;
|
||||||
len = 1;
|
len = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (script_char < 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
|
||||||
* If we got an interrupt, skip all previously typed characters and
|
// return TRUE if quit reading script file.
|
||||||
* return TRUE if quit reading script file.
|
// Stop reading typeahead when a single CTRL-C was read,
|
||||||
* Stop reading typeahead when a single CTRL-C was read,
|
// fill_input_buf() returns this when not able to read from stdin.
|
||||||
* fill_input_buf() returns this when not able to read from stdin.
|
// Don't use buf[] here, closescript() may have freed typebuf.tb_buf[]
|
||||||
* Don't use buf[] here, closescript() may have freed typebuf.tb_buf[]
|
// and buf may be pointing inside typebuf.tb_buf[].
|
||||||
* and buf may be pointing inside typebuf.tb_buf[].
|
|
||||||
*/
|
|
||||||
if (got_int) {
|
if (got_int) {
|
||||||
#define DUM_LEN MAXMAPLEN * 3 + 3
|
#define DUM_LEN MAXMAPLEN * 3 + 3
|
||||||
char_u dum[DUM_LEN + 1];
|
char_u dum[DUM_LEN + 1];
|
||||||
@ -2407,21 +2408,18 @@ inchar (
|
|||||||
return retesc;
|
return retesc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// Always flush the output characters when getting input characters
|
||||||
* Always flush the output characters when getting input characters
|
// from the user.
|
||||||
* from the user.
|
|
||||||
*/
|
|
||||||
ui_flush();
|
ui_flush();
|
||||||
|
|
||||||
/*
|
// Fill up to a third of the buffer, because each character may be
|
||||||
* Fill up to a third of the buffer, because each character may be
|
// tripled below.
|
||||||
* tripled below.
|
|
||||||
*/
|
|
||||||
len = os_inchar(buf, maxlen / 3, (int)wait_time, tb_change_cnt);
|
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 0;
|
||||||
|
}
|
||||||
|
|
||||||
return fix_input_buffer(buf, len);
|
return fix_input_buffer(buf, len);
|
||||||
}
|
}
|
||||||
|
@ -1,24 +1,30 @@
|
|||||||
#ifndef NVIM_GETCHAR_H
|
#ifndef NVIM_GETCHAR_H
|
||||||
#define NVIM_GETCHAR_H
|
#define NVIM_GETCHAR_H
|
||||||
|
|
||||||
|
#include "nvim/os/fileio.h"
|
||||||
#include "nvim/types.h"
|
#include "nvim/types.h"
|
||||||
#include "nvim/buffer_defs.h"
|
#include "nvim/buffer_defs.h"
|
||||||
#include "nvim/ex_cmds_defs.h"
|
#include "nvim/ex_cmds_defs.h"
|
||||||
|
|
||||||
/// Values for "noremap" argument of ins_typebuf(). Also used for
|
/// Values for "noremap" argument of ins_typebuf()
|
||||||
/// map->m_noremap and menu->noremap[].
|
///
|
||||||
/// @addtogroup REMAP_VALUES
|
/// Also used for map->m_noremap and menu->noremap[].
|
||||||
/// @{
|
enum {
|
||||||
#define REMAP_YES 0 ///< allow remapping
|
REMAP_YES = 0, ///< Allow remapping.
|
||||||
#define REMAP_NONE -1 ///< no remapping
|
REMAP_NONE = -1, ///< No remapping.
|
||||||
#define REMAP_SCRIPT -2 ///< remap script-local mappings only
|
REMAP_SCRIPT = -2, ///< Remap script-local mappings only.
|
||||||
#define REMAP_SKIP -3 ///< no remapping for first char
|
REMAP_SKIP = -3, ///< No remapping for first char.
|
||||||
/// @}
|
} RemapValues;
|
||||||
|
|
||||||
#define KEYLEN_PART_KEY -1 /* keylen value for incomplete key-code */
|
#define KEYLEN_PART_KEY -1 /* keylen value for incomplete key-code */
|
||||||
#define KEYLEN_PART_MAP -2 /* keylen value for incomplete mapping */
|
#define KEYLEN_PART_MAP -2 /* keylen value for incomplete mapping */
|
||||||
#define KEYLEN_REMOVED 9999 /* keylen value for removed sequence */
|
#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
|
#ifdef INCLUDE_GENERATED_DECLARATIONS
|
||||||
# include "getchar.h.generated.h"
|
# include "getchar.h.generated.h"
|
||||||
|
@ -834,10 +834,7 @@ EXTERN int do_redraw INIT(= FALSE); /* extra redraw once */
|
|||||||
EXTERN int need_highlight_changed INIT(= true);
|
EXTERN int need_highlight_changed INIT(= true);
|
||||||
EXTERN char *used_shada_file INIT(= NULL); // name of the ShaDa file to use
|
EXTERN char *used_shada_file INIT(= NULL); // name of the ShaDa file to use
|
||||||
|
|
||||||
#define NSCRIPT 15
|
EXTERN FILE *scriptout INIT(= NULL); ///< Stream to write script to.
|
||||||
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.
|
// volatile because it is used in a signal handler.
|
||||||
EXTERN volatile int got_int INIT(= false); // set to true when interrupt
|
EXTERN volatile int got_int INIT(= false); // set to true when interrupt
|
||||||
@ -1085,6 +1082,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_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_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[] 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_notread[] INIT(= N_("E485: Can't read file %s"));
|
||||||
EXTERN char_u e_nowrtmsg[] INIT(= N_(
|
EXTERN char_u e_nowrtmsg[] INIT(= N_(
|
||||||
"E37: No write since last change (add ! to override)"));
|
"E37: No write since last change (add ! to override)"));
|
||||||
|
@ -787,7 +787,8 @@ static void command_line_scan(mparm_T *parmp)
|
|||||||
mch_exit(0);
|
mch_exit(0);
|
||||||
} else if (STRICMP(argv[0] + argv_idx, "api-info") == 0) {
|
} else if (STRICMP(argv[0] + argv_idx, "api-info") == 0) {
|
||||||
FileDescriptor fp;
|
FileDescriptor fp;
|
||||||
const int fof_ret = file_open_fd(&fp, STDOUT_FILENO, true);
|
const int fof_ret = file_open_fd(&fp, STDOUT_FILENO,
|
||||||
|
kFileWriteOnly);
|
||||||
msgpack_packer *p = msgpack_packer_new(&fp, msgpack_file_write);
|
msgpack_packer *p = msgpack_packer_new(&fp, msgpack_file_write);
|
||||||
|
|
||||||
if (fof_ret != 0) {
|
if (fof_ret != 0) {
|
||||||
@ -1085,17 +1086,36 @@ static void command_line_scan(mparm_T *parmp)
|
|||||||
case 's': /* "-s {scriptin}" read from script file */
|
case 's': /* "-s {scriptin}" read from script file */
|
||||||
if (scriptin[0] != NULL) {
|
if (scriptin[0] != NULL) {
|
||||||
scripterror:
|
scripterror:
|
||||||
mch_errmsg(_("Attempt to open script file again: \""));
|
vim_snprintf((char *)IObuff, IOSIZE,
|
||||||
mch_errmsg(argv[-1]);
|
_("Attempt to open script file again: \"%s %s\"\n"),
|
||||||
mch_errmsg(" ");
|
argv[-1], argv[0]);
|
||||||
mch_errmsg(argv[0]);
|
mch_errmsg((const char *)IObuff);
|
||||||
mch_errmsg("\"\n");
|
|
||||||
mch_exit(2);
|
mch_exit(2);
|
||||||
}
|
}
|
||||||
if ((scriptin[0] = mch_fopen(argv[0], READBIN)) == NULL) {
|
int error;
|
||||||
mch_errmsg(_("Cannot open for reading: \""));
|
if (STRCMP(argv[0], "-") == 0) {
|
||||||
mch_errmsg(argv[0]);
|
const int stdin_dup_fd = os_dup(STDIN_FILENO);
|
||||||
mch_errmsg("\"\n");
|
#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);
|
||||||
|
scriptin[0] = stdin_dup;
|
||||||
|
} else if ((scriptin[0] = file_open_new(
|
||||||
|
&error, argv[0], kFileReadOnly|kFileNonBlocking, 0)) == NULL) {
|
||||||
|
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);
|
mch_exit(2);
|
||||||
}
|
}
|
||||||
save_typebuf();
|
save_typebuf();
|
||||||
|
@ -2351,10 +2351,9 @@ static int do_more_prompt(int typed_char)
|
|||||||
* yet. When stderr can't be used, collect error messages until the GUI has
|
* yet. When stderr can't be used, collect error messages until the GUI has
|
||||||
* started and they can be displayed in a message box.
|
* 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
|
#ifdef UNIX
|
||||||
/* On Unix use stderr if it's a tty.
|
/* On Unix use stderr if it's a tty.
|
||||||
* When not going to start the GUI also use stderr.
|
* When not going to start the GUI also use stderr.
|
||||||
@ -2368,14 +2367,13 @@ void mch_errmsg(char *str)
|
|||||||
/* avoid a delay for a message that isn't there */
|
/* avoid a delay for a message that isn't there */
|
||||||
emsg_on_display = FALSE;
|
emsg_on_display = FALSE;
|
||||||
|
|
||||||
len = (int)STRLEN(str) + 1;
|
const size_t len = strlen(str) + 1;
|
||||||
if (error_ga.ga_data == NULL) {
|
if (error_ga.ga_data == NULL) {
|
||||||
ga_set_growsize(&error_ga, 80);
|
ga_set_growsize(&error_ga, 80);
|
||||||
error_ga.ga_itemsize = 1;
|
error_ga.ga_itemsize = 1;
|
||||||
}
|
}
|
||||||
ga_grow(&error_ga, len);
|
ga_grow(&error_ga, len);
|
||||||
memmove((char_u *)error_ga.ga_data + error_ga.ga_len,
|
memmove(error_ga.ga_data + error_ga.ga_len, str, len);
|
||||||
(char_u *)str, len);
|
|
||||||
#ifdef UNIX
|
#ifdef UNIX
|
||||||
/* remove CR characters, they are displayed */
|
/* remove CR characters, they are displayed */
|
||||||
{
|
{
|
||||||
|
@ -83,7 +83,7 @@ int file_open(FileDescriptor *const ret_fp, const char *const fname,
|
|||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
return fd;
|
return fd;
|
||||||
}
|
}
|
||||||
return file_open_fd(ret_fp, fd, (wr == kTrue));
|
return file_open_fd(ret_fp, fd, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wrap file descriptor with FileDescriptor structure
|
/// Wrap file descriptor with FileDescriptor structure
|
||||||
@ -94,14 +94,23 @@ int file_open(FileDescriptor *const ret_fp, const char *const fname,
|
|||||||
/// @param[out] ret_fp Address where information needed for reading from or
|
/// @param[out] ret_fp Address where information needed for reading from or
|
||||||
/// writing to a file is saved
|
/// writing to a file is saved
|
||||||
/// @param[in] fd File descriptor to wrap.
|
/// @param[in] fd File descriptor to wrap.
|
||||||
/// @param[in] wr True if fd is opened for writing only, false if it is read
|
/// @param[in] flags Flags, @see FileOpenFlags. Currently reading from and
|
||||||
/// only.
|
/// writing to the file at once is not supported, so either
|
||||||
|
/// FILE_WRITE_ONLY or FILE_READ_ONLY is required.
|
||||||
///
|
///
|
||||||
/// @return Error code (@see os_strerror()) or 0. Currently always returns 0.
|
/// @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)
|
int file_open_fd(FileDescriptor *const ret_fp, const int fd,
|
||||||
|
const int flags)
|
||||||
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
|
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->fd = fd;
|
||||||
ret_fp->eof = false;
|
ret_fp->eof = false;
|
||||||
ret_fp->rv = rbuffer_new(kRWBufferSize);
|
ret_fp->rv = rbuffer_new(kRWBufferSize);
|
||||||
@ -138,15 +147,17 @@ FileDescriptor *file_open_new(int *const error, const char *const fname,
|
|||||||
///
|
///
|
||||||
/// @param[out] error Error code, or 0 on success. @see os_strerror()
|
/// @param[out] error Error code, or 0 on success. @see os_strerror()
|
||||||
/// @param[in] fd File descriptor to wrap.
|
/// @param[in] fd File descriptor to wrap.
|
||||||
/// @param[in] wr True if fd is opened for writing only, false if it is read
|
/// @param[in] flags Flags, @see FileOpenFlags.
|
||||||
/// 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.
|
/// @return [allocated] Opened file or NULL in case of error.
|
||||||
FileDescriptor *file_open_fd_new(int *const error, const int fd, const bool wr)
|
FileDescriptor *file_open_fd_new(int *const error, const int fd,
|
||||||
|
const int flags)
|
||||||
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT
|
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_MALLOC FUNC_ATTR_WARN_UNUSED_RESULT
|
||||||
{
|
{
|
||||||
FileDescriptor *const fp = xmalloc(sizeof(*fp));
|
FileDescriptor *const fp = xmalloc(sizeof(*fp));
|
||||||
if ((*error = file_open_fd(fp, fd, wr)) != 0) {
|
if ((*error = file_open_fd(fp, fd, flags)) != 0) {
|
||||||
xfree(fp);
|
xfree(fp);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@ -244,7 +255,8 @@ static void file_rb_write_full_cb(RBuffer *const rv, FileDescriptor *const fp)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const size_t read_bytes = rbuffer_read(rv, writebuf, kRWBufferSize);
|
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 != (ptrdiff_t)read_bytes) {
|
||||||
if (wres >= 0) {
|
if (wres >= 0) {
|
||||||
fp->_error = UV_EIO;
|
fp->_error = UV_EIO;
|
||||||
@ -270,6 +282,7 @@ ptrdiff_t file_read(FileDescriptor *const fp, char *const ret_buf,
|
|||||||
char *buf = ret_buf;
|
char *buf = ret_buf;
|
||||||
size_t read_remaining = size;
|
size_t read_remaining = size;
|
||||||
RBuffer *const rv = fp->rv;
|
RBuffer *const rv = fp->rv;
|
||||||
|
bool called_read = false;
|
||||||
while (read_remaining) {
|
while (read_remaining) {
|
||||||
const size_t rv_size = rbuffer_size(rv);
|
const size_t rv_size = rbuffer_size(rv);
|
||||||
if (rv_size > 0) {
|
if (rv_size > 0) {
|
||||||
@ -277,7 +290,9 @@ ptrdiff_t file_read(FileDescriptor *const fp, char *const ret_buf,
|
|||||||
buf += rsize;
|
buf += rsize;
|
||||||
read_remaining -= 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;
|
break;
|
||||||
}
|
}
|
||||||
if (read_remaining) {
|
if (read_remaining) {
|
||||||
@ -294,7 +309,7 @@ ptrdiff_t file_read(FileDescriptor *const fp, char *const ret_buf,
|
|||||||
};
|
};
|
||||||
assert(write_count == kRWBufferSize);
|
assert(write_count == kRWBufferSize);
|
||||||
const ptrdiff_t r_ret = os_readv(fp->fd, &fp->eof, iov,
|
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 > 0) {
|
||||||
if (r_ret > (ptrdiff_t)read_remaining) {
|
if (r_ret > (ptrdiff_t)read_remaining) {
|
||||||
rbuffer_produced(rv, (size_t)(r_ret - (ptrdiff_t)read_remaining));
|
rbuffer_produced(rv, (size_t)(r_ret - (ptrdiff_t)read_remaining));
|
||||||
@ -310,7 +325,8 @@ ptrdiff_t file_read(FileDescriptor *const fp, char *const ret_buf,
|
|||||||
if (read_remaining >= kRWBufferSize) {
|
if (read_remaining >= kRWBufferSize) {
|
||||||
// …otherwise leave RBuffer empty and populate only target buffer,
|
// …otherwise leave RBuffer empty and populate only target buffer,
|
||||||
// because filtering information through rbuffer will be more syscalls.
|
// 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) {
|
if (r_ret >= 0) {
|
||||||
read_remaining -= (size_t)r_ret;
|
read_remaining -= (size_t)r_ret;
|
||||||
return (ptrdiff_t)(size - read_remaining);
|
return (ptrdiff_t)(size - read_remaining);
|
||||||
@ -321,7 +337,7 @@ ptrdiff_t file_read(FileDescriptor *const fp, char *const ret_buf,
|
|||||||
size_t write_count;
|
size_t write_count;
|
||||||
const ptrdiff_t r_ret = os_read(fp->fd, &fp->eof,
|
const ptrdiff_t r_ret = os_read(fp->fd, &fp->eof,
|
||||||
rbuffer_write_ptr(rv, &write_count),
|
rbuffer_write_ptr(rv, &write_count),
|
||||||
kRWBufferSize);
|
kRWBufferSize, fp->non_blocking);
|
||||||
assert(write_count == kRWBufferSize);
|
assert(write_count == kRWBufferSize);
|
||||||
if (r_ret > 0) {
|
if (r_ret > 0) {
|
||||||
rbuffer_produced(rv, (size_t)r_ret);
|
rbuffer_produced(rv, (size_t)r_ret);
|
||||||
@ -330,6 +346,7 @@ ptrdiff_t file_read(FileDescriptor *const fp, char *const ret_buf,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
called_read = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return (ptrdiff_t)(size - read_remaining);
|
return (ptrdiff_t)(size - read_remaining);
|
||||||
|
@ -14,6 +14,7 @@ typedef struct {
|
|||||||
RBuffer *rv; ///< Read or write buffer.
|
RBuffer *rv; ///< Read or write buffer.
|
||||||
bool wr; ///< True if file is in write mode.
|
bool wr; ///< True if file is in write mode.
|
||||||
bool eof; ///< True if end of file was encountered.
|
bool eof; ///< True if end of file was encountered.
|
||||||
|
bool non_blocking; ///< True if EAGAIN should not restart syscalls.
|
||||||
} FileDescriptor;
|
} FileDescriptor;
|
||||||
|
|
||||||
/// file_open() flags
|
/// file_open() flags
|
||||||
@ -32,6 +33,8 @@ typedef enum {
|
|||||||
///< kFileCreateOnly.
|
///< kFileCreateOnly.
|
||||||
kFileAppend = 64, ///< Append to the file. Implies kFileWriteOnly. Cannot
|
kFileAppend = 64, ///< Append to the file. Implies kFileWriteOnly. Cannot
|
||||||
///< be used with kFileCreateOnly.
|
///< be used with kFileCreateOnly.
|
||||||
|
kFileNonBlocking = 128, ///< Do not restart read() or write() syscall if
|
||||||
|
///< EAGAIN was encountered.
|
||||||
} FileOpenFlags;
|
} FileOpenFlags;
|
||||||
|
|
||||||
static inline bool file_eof(const FileDescriptor *const fp)
|
static inline bool file_eof(const FileDescriptor *const fp)
|
||||||
|
@ -436,6 +436,29 @@ int os_close(const int fd)
|
|||||||
return r;
|
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
|
/// Read from a file
|
||||||
///
|
///
|
||||||
/// Handles EINTR and ENOMEM, but not other errors.
|
/// Handles EINTR and ENOMEM, but not other errors.
|
||||||
@ -445,10 +468,11 @@ int os_close(const int fd)
|
|||||||
/// to false. Initial value is ignored.
|
/// to false. Initial value is ignored.
|
||||||
/// @param[out] ret_buf Buffer to write to. May be NULL if size is zero.
|
/// @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] 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).
|
/// @return Number of bytes read or libuv error code (< 0).
|
||||||
ptrdiff_t os_read(const int fd, bool *ret_eof, char *const ret_buf,
|
ptrdiff_t os_read(const int fd, bool *const ret_eof, char *const ret_buf,
|
||||||
const size_t size)
|
const size_t size, const bool non_blocking)
|
||||||
FUNC_ATTR_WARN_UNUSED_RESULT
|
FUNC_ATTR_WARN_UNUSED_RESULT
|
||||||
{
|
{
|
||||||
*ret_eof = false;
|
*ret_eof = false;
|
||||||
@ -468,7 +492,9 @@ ptrdiff_t os_read(const int fd, bool *ret_eof, char *const ret_buf,
|
|||||||
if (cur_read_bytes < 0) {
|
if (cur_read_bytes < 0) {
|
||||||
const int error = os_translate_sys_error(errno);
|
const int error = os_translate_sys_error(errno);
|
||||||
errno = 0;
|
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;
|
continue;
|
||||||
} else if (error == UV_ENOMEM && !did_try_to_free) {
|
} else if (error == UV_ENOMEM && !did_try_to_free) {
|
||||||
try_to_free_memory();
|
try_to_free_memory();
|
||||||
@ -498,7 +524,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
|
/// may change, it is incorrect to use data it points to after
|
||||||
/// os_readv().
|
/// os_readv().
|
||||||
/// @param[in] iov_size Number of buffers in iov.
|
/// @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
|
FUNC_ATTR_NONNULL_ALL
|
||||||
{
|
{
|
||||||
*ret_eof = false;
|
*ret_eof = false;
|
||||||
@ -531,7 +561,9 @@ ptrdiff_t os_readv(int fd, bool *ret_eof, struct iovec *iov, size_t iov_size)
|
|||||||
} else if (cur_read_bytes < 0) {
|
} else if (cur_read_bytes < 0) {
|
||||||
const int error = os_translate_sys_error(errno);
|
const int error = os_translate_sys_error(errno);
|
||||||
errno = 0;
|
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;
|
continue;
|
||||||
} else if (error == UV_ENOMEM && !did_try_to_free) {
|
} else if (error == UV_ENOMEM && !did_try_to_free) {
|
||||||
try_to_free_memory();
|
try_to_free_memory();
|
||||||
@ -551,9 +583,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] fd File descriptor to write to.
|
||||||
/// @param[in] buf Data to write. May be NULL if size is zero.
|
/// @param[in] buf Data to write. May be NULL if size is zero.
|
||||||
/// @param[in] size Amount of bytes to write.
|
/// @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).
|
/// @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
|
FUNC_ATTR_WARN_UNUSED_RESULT
|
||||||
{
|
{
|
||||||
if (buf == NULL) {
|
if (buf == NULL) {
|
||||||
@ -571,7 +605,9 @@ ptrdiff_t os_write(const int fd, const char *const buf, const size_t size)
|
|||||||
if (cur_written_bytes < 0) {
|
if (cur_written_bytes < 0) {
|
||||||
const int error = os_translate_sys_error(errno);
|
const int error = os_translate_sys_error(errno);
|
||||||
errno = 0;
|
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;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
return error;
|
return error;
|
||||||
|
131
test/functional/core/main_spec.lua
Normal file
131
test/functional/core/main_spec.lua
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
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 eval = helpers.eval
|
||||||
|
local clear = helpers.clear
|
||||||
|
local funcs = helpers.funcs
|
||||||
|
local nvim_prog = helpers.nvim_prog
|
||||||
|
local write_file = helpers.write_file
|
||||||
|
|
||||||
|
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 fname_2 = fname .. '.2'
|
||||||
|
local nonexistent_fname = fname .. '.nonexistent'
|
||||||
|
local dollar_fname = '$' .. fname
|
||||||
|
before_each(function()
|
||||||
|
clear()
|
||||||
|
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))
|
||||||
|
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, 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, ':call setline(1, "100500")\n:wqall!\n')
|
||||||
|
funcs.system(
|
||||||
|
{nvim_prog_abs(), '-u', 'NONE', '-i', 'NONE', '--headless',
|
||||||
|
'--cmd', 'set noswapfile shortmess+=IFW fileformats=unix',
|
||||||
|
'-s', dollar_fname, fname})
|
||||||
|
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()
|
||||||
|
if helpers.pending_win32(pending) then return end
|
||||||
|
local screen = Screen.new(40, 8)
|
||||||
|
screen:attach()
|
||||||
|
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<CR><C-\\><C-n>')
|
||||||
|
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)
|
||||||
|
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)
|
@ -1,6 +1,38 @@
|
|||||||
local assert = require('luassert')
|
local assert = require('luassert')
|
||||||
local lfs = require('lfs')
|
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 _, subarg in ipairs(arg) do
|
||||||
|
cmd = cmd .. ' ' .. shell_quote(subarg)
|
||||||
|
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 = {
|
local check_logs_useless_lines = {
|
||||||
['Warning: noted but unhandled ioctl']=1,
|
['Warning: noted but unhandled ioctl']=1,
|
||||||
['could cause spurious value errors to appear']=2,
|
['could cause spurious value errors to appear']=2,
|
||||||
@ -121,7 +153,7 @@ local uname = (function()
|
|||||||
return platform
|
return platform
|
||||||
end
|
end
|
||||||
|
|
||||||
local status, f = pcall(io.popen, "uname -s")
|
local status, f = pcall(popen_r, 'uname', '-s')
|
||||||
if status then
|
if status then
|
||||||
platform = f:read("*l")
|
platform = f:read("*l")
|
||||||
f:close()
|
f:close()
|
||||||
@ -253,7 +285,7 @@ local function check_cores(app, force)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local function which(exe)
|
local function which(exe)
|
||||||
local pipe = io.popen('which ' .. exe, 'r')
|
local pipe = popen_r('which', exe)
|
||||||
local ret = pipe:read('*a')
|
local ret = pipe:read('*a')
|
||||||
pipe:close()
|
pipe:close()
|
||||||
if ret == '' then
|
if ret == '' then
|
||||||
@ -263,6 +295,19 @@ local function which(exe)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function repeated_read_cmd(...)
|
||||||
|
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
|
||||||
|
|
||||||
local function shallowcopy(orig)
|
local function shallowcopy(orig)
|
||||||
if type(orig) ~= 'table' then
|
if type(orig) ~= 'table' then
|
||||||
return orig
|
return orig
|
||||||
@ -569,6 +614,7 @@ end
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
REMOVE_THIS = REMOVE_THIS,
|
REMOVE_THIS = REMOVE_THIS,
|
||||||
|
argss_to_cmd = argss_to_cmd,
|
||||||
check_cores = check_cores,
|
check_cores = check_cores,
|
||||||
check_logs = check_logs,
|
check_logs = check_logs,
|
||||||
concat_tables = concat_tables,
|
concat_tables = concat_tables,
|
||||||
@ -590,6 +636,9 @@ return {
|
|||||||
mergedicts_copy = mergedicts_copy,
|
mergedicts_copy = mergedicts_copy,
|
||||||
neq = neq,
|
neq = neq,
|
||||||
ok = ok,
|
ok = ok,
|
||||||
|
popen_r = popen_r,
|
||||||
|
popen_w = popen_w,
|
||||||
|
repeated_read_cmd = repeated_read_cmd,
|
||||||
shallowcopy = shallowcopy,
|
shallowcopy = shallowcopy,
|
||||||
table_flatten = table_flatten,
|
table_flatten = table_flatten,
|
||||||
tmpname = tmpname,
|
tmpname = tmpname,
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
#define _GNU_SOURCE
|
#ifndef _GNU_SOURCE
|
||||||
|
# define _GNU_SOURCE
|
||||||
|
#endif
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
static const mode_t kS_IFMT = S_IFMT;
|
static const mode_t kS_IFMT = S_IFMT;
|
||||||
|
@ -779,7 +779,7 @@ local function gen_itp(it)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local function cppimport(path)
|
local function cppimport(path)
|
||||||
return cimport(Paths.test_include_path .. '/' .. path)
|
return cimport(Paths.test_source_path .. '/test/includes/pre/' .. path)
|
||||||
end
|
end
|
||||||
|
|
||||||
cimport('./src/nvim/types.h', './src/nvim/main.h', './src/nvim/os/time.h')
|
cimport('./src/nvim/types.h', './src/nvim/main.h', './src/nvim/os/time.h')
|
||||||
|
@ -113,7 +113,7 @@ end
|
|||||||
describe('file_open_fd', function()
|
describe('file_open_fd', function()
|
||||||
itp('can use file descriptor returned by os_open for reading', function()
|
itp('can use file descriptor returned by os_open for reading', function()
|
||||||
local fd = m.os_open(file1, m.kO_RDONLY, 0)
|
local fd = m.os_open(file1, m.kO_RDONLY, 0)
|
||||||
local err, fp = file_open_fd(fd, false)
|
local err, fp = file_open_fd(fd, m.kFileReadOnly)
|
||||||
eq(0, err)
|
eq(0, err)
|
||||||
eq({#fcontents, fcontents}, {file_read(fp, #fcontents)})
|
eq({#fcontents, fcontents}, {file_read(fp, #fcontents)})
|
||||||
eq(0, m.file_close(fp, false))
|
eq(0, m.file_close(fp, false))
|
||||||
@ -121,7 +121,7 @@ describe('file_open_fd', function()
|
|||||||
itp('can use file descriptor returned by os_open for writing', function()
|
itp('can use file descriptor returned by os_open for writing', function()
|
||||||
eq(nil, lfs.attributes(filec))
|
eq(nil, lfs.attributes(filec))
|
||||||
local fd = m.os_open(filec, m.kO_WRONLY + m.kO_CREAT, 384)
|
local fd = m.os_open(filec, m.kO_WRONLY + m.kO_CREAT, 384)
|
||||||
local err, fp = file_open_fd(fd, true)
|
local err, fp = file_open_fd(fd, m.kFileWriteOnly)
|
||||||
eq(0, err)
|
eq(0, err)
|
||||||
eq(4, file_write(fp, 'test'))
|
eq(4, file_write(fp, 'test'))
|
||||||
eq(0, m.file_close(fp, false))
|
eq(0, m.file_close(fp, false))
|
||||||
@ -133,7 +133,7 @@ end)
|
|||||||
describe('file_open_fd_new', function()
|
describe('file_open_fd_new', function()
|
||||||
itp('can use file descriptor returned by os_open for reading', function()
|
itp('can use file descriptor returned by os_open for reading', function()
|
||||||
local fd = m.os_open(file1, m.kO_RDONLY, 0)
|
local fd = m.os_open(file1, m.kO_RDONLY, 0)
|
||||||
local err, fp = file_open_fd_new(fd, false)
|
local err, fp = file_open_fd_new(fd, m.kFileReadOnly)
|
||||||
eq(0, err)
|
eq(0, err)
|
||||||
eq({#fcontents, fcontents}, {file_read(fp, #fcontents)})
|
eq({#fcontents, fcontents}, {file_read(fp, #fcontents)})
|
||||||
eq(0, m.file_free(fp, false))
|
eq(0, m.file_free(fp, false))
|
||||||
@ -141,7 +141,7 @@ describe('file_open_fd_new', function()
|
|||||||
itp('can use file descriptor returned by os_open for writing', function()
|
itp('can use file descriptor returned by os_open for writing', function()
|
||||||
eq(nil, lfs.attributes(filec))
|
eq(nil, lfs.attributes(filec))
|
||||||
local fd = m.os_open(filec, m.kO_WRONLY + m.kO_CREAT, 384)
|
local fd = m.os_open(filec, m.kO_WRONLY + m.kO_CREAT, 384)
|
||||||
local err, fp = file_open_fd_new(fd, true)
|
local err, fp = file_open_fd_new(fd, m.kFileWriteOnly)
|
||||||
eq(0, err)
|
eq(0, err)
|
||||||
eq(4, file_write(fp, 'test'))
|
eq(4, file_write(fp, 'test'))
|
||||||
eq(0, m.file_free(fp, false))
|
eq(0, m.file_free(fp, false))
|
||||||
|
@ -390,7 +390,7 @@ describe('fs.c', function()
|
|||||||
buf = ffi.new('char[?]', size + 1, ('\0'):rep(size))
|
buf = ffi.new('char[?]', size + 1, ('\0'):rep(size))
|
||||||
end
|
end
|
||||||
local eof = ffi.new('bool[?]', 1, {true})
|
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 ret1 = eof[0]
|
||||||
local ret3 = ''
|
local ret3 = ''
|
||||||
if buf ~= nil then
|
if buf ~= nil then
|
||||||
@ -408,7 +408,7 @@ describe('fs.c', function()
|
|||||||
end
|
end
|
||||||
local iov = ffi.new('struct iovec[?]', #sizes, bufs)
|
local iov = ffi.new('struct iovec[?]', #sizes, bufs)
|
||||||
local eof = ffi.new('bool[?]', 1, {true})
|
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 ret1 = eof[0]
|
||||||
local ret3 = {}
|
local ret3 = {}
|
||||||
for i = 1,#sizes do
|
for i = 1,#sizes do
|
||||||
@ -418,7 +418,7 @@ describe('fs.c', function()
|
|||||||
return ret1, ret2, ret3
|
return ret1, ret2, ret3
|
||||||
end
|
end
|
||||||
local function os_write(fd, data)
|
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
|
end
|
||||||
|
|
||||||
describe('os_path_exists', function()
|
describe('os_path_exists', function()
|
||||||
@ -491,6 +491,22 @@ describe('fs.c', function()
|
|||||||
end)
|
end)
|
||||||
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()
|
describe('os_open', function()
|
||||||
local new_file = 'test_new_file'
|
local new_file = 'test_new_file'
|
||||||
local existing_file = 'unit-test-directory/test_existing.file'
|
local existing_file = 'unit-test-directory/test_existing.file'
|
||||||
|
@ -2,6 +2,10 @@
|
|||||||
-- windows, will probably need quite a bit of adjustment to run there.
|
-- windows, will probably need quite a bit of adjustment to run there.
|
||||||
|
|
||||||
local ffi = require("ffi")
|
local ffi = require("ffi")
|
||||||
|
local global_helpers = require('test.helpers')
|
||||||
|
|
||||||
|
local argss_to_cmd = global_helpers.argss_to_cmd
|
||||||
|
local repeated_read_cmd = global_helpers.repeated_read_cmd
|
||||||
|
|
||||||
local ccs = {}
|
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", "clang"}, type = "clang"})
|
||||||
table.insert(ccs, {path = {"/usr/bin/env", "icc"}, type = "gcc"})
|
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
|
-- parse Makefile format dependencies into a Lua table
|
||||||
local function parse_make_deps(deps)
|
local function parse_make_deps(deps)
|
||||||
-- remove line breaks and line concatenators
|
-- remove line breaks and line concatenators
|
||||||
@ -149,16 +144,6 @@ function Gcc:add_to_include_path(...)
|
|||||||
end
|
end
|
||||||
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
|
-- returns a list of the headers files upon which this file relies
|
||||||
function Gcc:dependencies(hdr)
|
function Gcc:dependencies(hdr)
|
||||||
local cmd = argss_to_cmd(self.path, {'-M', hdr}) .. ' 2>&1'
|
local cmd = argss_to_cmd(self.path, {'-M', hdr}) .. ' 2>&1'
|
||||||
@ -172,29 +157,15 @@ function Gcc:dependencies(hdr)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function repeated_call(...)
|
|
||||||
local cmd = argss_to_cmd(...)
|
|
||||||
for _ = 1, 10 do
|
|
||||||
local stream = io.popen(cmd)
|
|
||||||
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)
|
function Gcc:filter_standard_defines(defines)
|
||||||
if not self.standard_defines then
|
if not self.standard_defines then
|
||||||
local pseudoheader_fname = 'tmp_empty_pseudoheader.h'
|
local pseudoheader_fname = 'tmp_empty_pseudoheader.h'
|
||||||
local pseudoheader_file = io.open(pseudoheader_fname, 'w')
|
local pseudoheader_file = io.open(pseudoheader_fname, 'w')
|
||||||
pseudoheader_file:close()
|
pseudoheader_file:close()
|
||||||
local standard_defines = repeated_call(self.path,
|
local standard_defines = repeated_read_cmd(self.path,
|
||||||
self.preprocessor_extra_flags,
|
self.preprocessor_extra_flags,
|
||||||
self.get_defines_extra_flags,
|
self.get_defines_extra_flags,
|
||||||
{pseudoheader_fname})
|
{pseudoheader_fname})
|
||||||
os.remove(pseudoheader_fname)
|
os.remove(pseudoheader_fname)
|
||||||
self.standard_defines = {}
|
self.standard_defines = {}
|
||||||
for line in standard_defines:gmatch('[^\n]+') do
|
for line in standard_defines:gmatch('[^\n]+') do
|
||||||
@ -223,9 +194,9 @@ function Gcc:preprocess(previous_defines, ...)
|
|||||||
pseudoheader_file:flush()
|
pseudoheader_file:flush()
|
||||||
pseudoheader_file:close()
|
pseudoheader_file:close()
|
||||||
|
|
||||||
local defines = repeated_call(self.path, self.preprocessor_extra_flags,
|
local defines = repeated_read_cmd(self.path, self.preprocessor_extra_flags,
|
||||||
self.get_defines_extra_flags,
|
self.get_defines_extra_flags,
|
||||||
{pseudoheader_fname})
|
{pseudoheader_fname})
|
||||||
defines = self:filter_standard_defines(defines)
|
defines = self:filter_standard_defines(defines)
|
||||||
|
|
||||||
-- lfs = require("lfs")
|
-- lfs = require("lfs")
|
||||||
@ -234,9 +205,10 @@ function Gcc:preprocess(previous_defines, ...)
|
|||||||
-- io.stderr\write("CWD: #{lfs.currentdir!}\n")
|
-- io.stderr\write("CWD: #{lfs.currentdir!}\n")
|
||||||
-- io.stderr\write("CMD: #{cmd}\n")
|
-- io.stderr\write("CMD: #{cmd}\n")
|
||||||
|
|
||||||
local declarations = repeated_call(self.path, self.preprocessor_extra_flags,
|
local declarations = repeated_read_cmd(self.path,
|
||||||
self.get_declarations_extra_flags,
|
self.preprocessor_extra_flags,
|
||||||
{pseudoheader_fname})
|
self.get_declarations_extra_flags,
|
||||||
|
{pseudoheader_fname})
|
||||||
|
|
||||||
os.remove(pseudoheader_fname)
|
os.remove(pseudoheader_fname)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user