Skip to content

Commit cb8d068

Browse files
committed
Fix SIGABRT when stdin is a regular file
1 parent c20496a commit cb8d068

File tree

1 file changed

+101
-29
lines changed

1 file changed

+101
-29
lines changed

src/os/io.c

Lines changed: 101 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@
88
#define BUF_SIZE 4096
99

1010
typedef struct {
11-
uint32_t rpos, wpos, apos;
11+
uint32_t rpos, wpos, apos, fpos;
1212
char_u data[BUF_SIZE];
1313
} input_buffer_T;
1414

15+
static uv_handle_type stdin_type;
1516
static int pending_signal = 0;
1617
static uv_thread_t thread;
1718
static uv_mutex_t mutex;
@@ -26,7 +27,9 @@ static void loop_running(uv_idle_t *, int);
2627
static void stop_loop(uv_async_t *, int);
2728
static void alloc_cb(uv_handle_t *, size_t, uv_buf_t *);
2829
static void read_cb(uv_stream_t *, ssize_t, const uv_buf_t *);
30+
static void fread_cb(uv_fs_t *);
2931
static void signal_cb(uv_signal_t *, int signum);
32+
static void relocate(void);
3033
static void io_lock(void);
3134
static void io_unlock(void);
3235
static void io_timedwait(uint64_t ms, bool *condition);
@@ -160,9 +163,11 @@ static void event_loop(void *arg)
160163
uv_loop_t *loop;
161164
uv_idle_t idler;
162165
uv_signal_t sint, shup, squit, sabrt, sterm, swinch;
163-
uv_pipe_t stdin_stream;
166+
uv_stream_t *stdin_stream = NULL;
167+
uv_fs_t *read_req = NULL;
164168

165-
in_buffer.wpos = in_buffer.rpos = in_buffer.apos = 0;
169+
170+
in_buffer.wpos = in_buffer.rpos = in_buffer.apos = in_buffer.fpos = 0;
166171
#ifdef DEBUG
167172
memset(&in_buffer.data, 0, BUF_SIZE);
168173
#endif
@@ -173,17 +178,30 @@ static void event_loop(void *arg)
173178
pthread_sigmask(SIG_SETMASK, &set, NULL);
174179

175180
loop = uv_loop_new();
181+
182+
/* Setup stdin_stream */
183+
if ((stdin_type = uv_guess_handle(read_cmd_fd)) == UV_FILE) {
184+
read_req = (uv_fs_t *)malloc(sizeof(uv_fs_t));
185+
in_buffer.apos = BUF_SIZE;
186+
uv_fs_read(loop, read_req, read_cmd_fd, in_buffer.data, BUF_SIZE,
187+
in_buffer.fpos, fread_cb);
188+
} else if (stdin_type == UV_FILE) {
189+
stdin_stream = (uv_stream_t *)malloc(sizeof(uv_tty_t));
190+
uv_tty_init(loop, (uv_tty_t *)stdin_stream, read_cmd_fd, 1);
191+
} else {
192+
/* FIXME setting fd to non-blocking is only needed on unix */
193+
// fcntl(read_cmd_fd, F_SETFL, fcntl(read_cmd_fd, F_GETFL, 0) | O_NONBLOCK);
194+
stdin_stream = (uv_stream_t *)malloc(sizeof(uv_pipe_t));
195+
uv_pipe_init(loop, (uv_pipe_t *)stdin_stream, 0);
196+
uv_pipe_open((uv_pipe_t *)stdin_stream, read_cmd_fd);
197+
}
198+
176199
/* Idler for signaling the main thread when the loop is running */
177200
uv_idle_init(loop, &idler);
178-
idler.data = &stdin_stream;
201+
idler.data = stdin_stream;
179202
uv_idle_start(&idler, loop_running);
180203
/* Async watcher used by the main thread to stop the loop */
181204
uv_async_init(loop, &stop_loop_async, stop_loop);
182-
/* stdin */
183-
/* FIXME setting fd to non-blocking is only needed on unix */
184-
fcntl(read_cmd_fd, F_SETFL, fcntl(read_cmd_fd, F_GETFL, 0) | O_NONBLOCK);
185-
uv_pipe_init(loop, &stdin_stream, 0);
186-
uv_pipe_open(&stdin_stream, read_cmd_fd);
187205
/* signals */
188206
uv_signal_init(loop, &sint);
189207
uv_signal_start(&sint, signal_cb, SIGINT);
@@ -199,6 +217,15 @@ static void event_loop(void *arg)
199217
uv_signal_start(&swinch, signal_cb, SIGWINCH);
200218
/* start processing events */
201219
uv_run(loop, UV_RUN_DEFAULT);
220+
221+
if (stdin_stream != NULL)
222+
free(stdin_stream);
223+
224+
if (read_req != NULL) {
225+
uv_cancel((uv_req_t *)read_req);
226+
free(read_req);
227+
}
228+
202229
/* free the event loop */
203230
uv_loop_delete(loop);
204231
}
@@ -208,7 +235,8 @@ static void loop_running(uv_idle_t *handle, int status)
208235
{
209236
uv_idle_stop(handle);
210237
io_lock();
211-
uv_read_start((uv_stream_t *)handle->data, alloc_cb, read_cb);
238+
if (stdin_type != UV_FILE)
239+
uv_read_start((uv_stream_t *)handle->data, alloc_cb, read_cb);
212240
io_notify(&running);
213241
io_unlock();
214242
}
@@ -240,8 +268,6 @@ static void alloc_cb(uv_handle_t *handle, size_t ssize, uv_buf_t *rv)
240268
*/
241269
static void read_cb(uv_stream_t *stream, ssize_t cnt, const uv_buf_t *buf)
242270
{
243-
uint32_t move_count;
244-
245271
io_lock();
246272

247273
if (cnt < 0) {
@@ -255,24 +281,10 @@ static void read_cb(uv_stream_t *stream, ssize_t cnt, const uv_buf_t *buf)
255281
io_notify(&activity);
256282
}
257283
} else if (cnt == UV_ENOBUFS) {
258-
if (in_buffer.apos > in_buffer.wpos) {
259-
/* Restore `apos` to `wpos` */
260-
in_buffer.apos = in_buffer.wpos;
261-
} else {
262-
if (in_buffer.rpos == 0) {
263-
/* Pause until the main thread consumes some data. */
264-
io_notify(&activity);
265-
io_wait(&input_consumed);
266-
}
267-
/* Move data to the 'left' as much as possible. */
268-
move_count = in_buffer.apos - in_buffer.rpos;
269-
memmove(in_buffer.data, in_buffer.data + in_buffer.rpos, move_count);
270-
in_buffer.apos -= in_buffer.rpos;
271-
in_buffer.wpos -= in_buffer.rpos;
272-
in_buffer.rpos = 0;
273-
}
284+
io_notify(&activity);
285+
relocate();
274286
} else {
275-
fprintf(stderr, "Unexpected error %ld\n", cnt);
287+
fprintf(stderr, "Unexpected error %s\n", uv_strerror(cnt));
276288
}
277289
io_unlock();
278290
return;
@@ -289,6 +301,43 @@ static void read_cb(uv_stream_t *stream, ssize_t cnt, const uv_buf_t *buf)
289301
io_unlock();
290302
}
291303

304+
static void fread_cb(uv_fs_t *req)
305+
{
306+
uint32_t available;
307+
308+
uv_fs_req_cleanup(req);
309+
io_lock();
310+
311+
if (req->result <= 0) {
312+
if (req->result == 0) {
313+
/* EOF, stop the event loop and signal the main thread. This will cause
314+
* vim to exit */
315+
if (!eof) {
316+
/* Dont close the loop if it was already closed in `io_stop` */
317+
eof = true;
318+
uv_stop(req->loop);
319+
io_notify(&activity);
320+
}
321+
} else {
322+
fprintf(stderr, "Unexpected error %s\n", uv_strerror(req->result));
323+
}
324+
io_unlock();
325+
return;
326+
}
327+
328+
in_buffer.wpos += req->result;
329+
in_buffer.fpos += req->result;
330+
io_notify(&activity);
331+
relocate();
332+
io_unlock();
333+
334+
available = BUF_SIZE - in_buffer.apos;
335+
/* Read more */
336+
uv_fs_read(req->loop, req, read_cmd_fd, in_buffer.data + in_buffer.apos,
337+
available, in_buffer.fpos, fread_cb);
338+
in_buffer.apos += available;
339+
}
340+
292341
static void signal_cb(uv_signal_t *handle, int signum)
293342
{
294343
io_lock();
@@ -298,6 +347,29 @@ static void signal_cb(uv_signal_t *handle, int signum)
298347
io_unlock();
299348
}
300349

350+
static void relocate()
351+
{
352+
uint32_t move_count;
353+
354+
if (in_buffer.apos > in_buffer.wpos) {
355+
/* Restore `apos` to `wpos` */
356+
in_buffer.apos = in_buffer.wpos;
357+
return;
358+
}
359+
360+
if (in_buffer.rpos == 0) {
361+
/* Pause until the main thread consumes some data. */
362+
io_wait(&input_consumed);
363+
}
364+
365+
/* Move data to the 'left' as much as possible. */
366+
move_count = in_buffer.apos - in_buffer.rpos;
367+
memmove(in_buffer.data, in_buffer.data + in_buffer.rpos, move_count);
368+
in_buffer.apos -= in_buffer.rpos;
369+
in_buffer.wpos -= in_buffer.rpos;
370+
in_buffer.rpos = 0;
371+
}
372+
301373
/* Helpers for dealing with io synchronization */
302374
static void io_lock()
303375
{

0 commit comments

Comments
 (0)