mirror of
https://github.com/neovim/neovim.git
synced 2025-09-27 13:38:34 +00:00
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.
This commit is contained in:
@@ -44,6 +44,12 @@
|
|||||||
#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"
|
||||||
|
|
||||||
|
|
||||||
|
/// Index in scriptin
|
||||||
|
static int curscript = 0;
|
||||||
|
FileDescriptor *scriptin[NSCRIPT] = { NULL };
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* These buffers are used for storing:
|
* These buffers are used for storing:
|
||||||
@@ -1244,10 +1250,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();
|
||||||
@@ -1297,7 +1306,7 @@ static void closescript(void)
|
|||||||
free_typebuf();
|
free_typebuf();
|
||||||
typebuf = saved_typebuf[curscript];
|
typebuf = saved_typebuf[curscript];
|
||||||
|
|
||||||
fclose(scriptin[curscript]);
|
file_free(scriptin[curscript]);
|
||||||
scriptin[curscript] = NULL;
|
scriptin[curscript] = NULL;
|
||||||
if (curscript > 0)
|
if (curscript > 0)
|
||||||
--curscript;
|
--curscript;
|
||||||
@@ -2346,7 +2355,6 @@ inchar (
|
|||||||
{
|
{
|
||||||
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
|
||||||
@@ -2364,45 +2372,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];
|
||||||
@@ -2415,21 +2416,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,6 +1,8 @@
|
|||||||
#ifndef NVIM_GETCHAR_H
|
#ifndef NVIM_GETCHAR_H
|
||||||
#define NVIM_GETCHAR_H
|
#define NVIM_GETCHAR_H
|
||||||
|
|
||||||
|
#include "nvim/os/fileio.h"
|
||||||
|
|
||||||
/* Values for "noremap" argument of ins_typebuf(). Also used for
|
/* Values for "noremap" argument of ins_typebuf(). Also used for
|
||||||
* map->m_noremap and menu->noremap[]. */
|
* map->m_noremap and menu->noremap[]. */
|
||||||
#define REMAP_YES 0 /* allow remapping */
|
#define REMAP_YES 0 /* allow remapping */
|
||||||
@@ -12,6 +14,12 @@
|
|||||||
#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"
|
||||||
#endif
|
#endif
|
||||||
|
@@ -913,9 +913,6 @@ 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 *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 */
|
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.
|
||||||
@@ -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_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)"));
|
||||||
|
@@ -1056,14 +1056,20 @@ scripterror:
|
|||||||
mch_errmsg("\"\n");
|
mch_errmsg("\"\n");
|
||||||
mch_exit(2);
|
mch_exit(2);
|
||||||
}
|
}
|
||||||
|
int error;
|
||||||
if (STRCMP(argv[0], "-") == 0) {
|
if (STRCMP(argv[0], "-") == 0) {
|
||||||
const int stdin_dup_fd = os_dup(STDIN_FILENO);
|
const int stdin_dup_fd = os_dup(OS_STDIN_FILENO);
|
||||||
FILE *const stdin_dup = fdopen(stdin_dup_fd, "r");
|
FileDescriptor *const stdin_dup = file_open_fd_new(
|
||||||
|
&error, stdin_dup_fd, false, 0);
|
||||||
|
assert(stdin_dup != NULL);
|
||||||
scriptin[0] = stdin_dup;
|
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(_("Cannot open for reading: \""));
|
||||||
mch_errmsg(argv[0]);
|
mch_errmsg(argv[0]);
|
||||||
mch_errmsg("\"\n");
|
mch_errmsg("\": ");
|
||||||
|
mch_errmsg(os_strerror(error));
|
||||||
|
mch_errmsg("\n");
|
||||||
mch_exit(2);
|
mch_exit(2);
|
||||||
}
|
}
|
||||||
save_typebuf();
|
save_typebuf();
|
||||||
|
@@ -45,7 +45,6 @@ int file_open(FileDescriptor *const ret_fp, const char *const fname,
|
|||||||
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
|
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
|
||||||
{
|
{
|
||||||
int os_open_flags = 0;
|
int os_open_flags = 0;
|
||||||
int fd;
|
|
||||||
TriState wr = kNone;
|
TriState wr = kNone;
|
||||||
#define FLAG(flags, flag, fcntl_flags, wrval, cond) \
|
#define FLAG(flags, flag, fcntl_flags, wrval, cond) \
|
||||||
do { \
|
do { \
|
||||||
@@ -70,13 +69,33 @@ int file_open(FileDescriptor *const ret_fp, const char *const fname,
|
|||||||
#endif
|
#endif
|
||||||
#undef FLAG
|
#undef FLAG
|
||||||
|
|
||||||
fd = os_open(fname, os_open_flags, mode);
|
const int fd = os_open(fname, os_open_flags, mode);
|
||||||
|
|
||||||
if (fd < 0) {
|
if (fd < 0) {
|
||||||
return fd;
|
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->fd = fd;
|
||||||
ret_fp->eof = false;
|
ret_fp->eof = false;
|
||||||
ret_fp->rv = rbuffer_new(kRWBufferSize);
|
ret_fp->rv = rbuffer_new(kRWBufferSize);
|
||||||
@@ -110,6 +129,29 @@ FileDescriptor *file_open_new(int *const error, const char *const fname,
|
|||||||
return fp;
|
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
|
/// Close file and free its buffer
|
||||||
///
|
///
|
||||||
/// @param[in,out] fp File to close.
|
/// @param[in,out] fp File to close.
|
||||||
|
@@ -13,6 +13,9 @@
|
|||||||
# include "nvim/os/unix_defs.h"
|
# include "nvim/os/unix_defs.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/// File descriptor number used for STDIN
|
||||||
|
enum { OS_STDIN_FILENO = STDIN_FILENO };
|
||||||
|
|
||||||
#define BASENAMELEN (NAME_MAX - 5)
|
#define BASENAMELEN (NAME_MAX - 5)
|
||||||
|
|
||||||
// Use the system path length if it makes sense.
|
// Use the system path length if it makes sense.
|
||||||
|
@@ -87,4 +87,8 @@ typedef SSIZE_T ssize_t;
|
|||||||
# define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
|
# define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef STDIN_FILENO
|
||||||
|
# define STDIN_FILENO 0
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif // NVIM_OS_WIN_DEFS_H
|
#endif // NVIM_OS_WIN_DEFS_H
|
||||||
|
Reference in New Issue
Block a user