1 /*
2 * Copyright (c) 2013 Holger Weiss <holger@weiss.in-berlin.de>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
19 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25 * POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 #if HAVE_CONFIG_H
29 # include <config.h>
30 #endif
31
32 #include <errno.h>
33 #include <unistd.h> /* Required by <fcntl.h> on some systems. */
34 #include <fcntl.h>
35 #include <stdlib.h>
36 #include <time.h>
37
38 #include "input.h"
39 #include "log.h"
40 #include "system.h"
41 #include "util.h"
42 #include "wrappers.h"
43
44 static void read_cb(EV_P_ ev_io *, int);
45
46 /*
47 * Exported functions.
48 */
49
50 input_state *
input_start(char separator)51 input_start(char separator)
52 {
53 int flags;
54
55 input_state *input = xmalloc(sizeof(input_state));
56
57 debug("Starting standard input reader");
58
59 input->watcher.data = input;
60 input->buffer = buffer_new();
61 input->read_handler = NULL;
62 input->eof_handler = NULL;
63 input->fd = STDIN_FILENO;
64 input->separator = separator;
65 input->eof = false;
66
67 if ((flags = fcntl(input->fd, F_GETFL, 0)) == -1)
68 die("Cannot get standard input descriptor flags: %m");
69 if (fcntl(input->fd, F_SETFL, flags | O_NONBLOCK) == -1)
70 die("Cannot mark standard input as non-blocking: %m");
71
72 ev_io_init(&input->watcher, read_cb, input->fd, EV_READ);
73
74 return input;
75 }
76
77 void
input_read_chunk(input_state * input,void handle_read (input_state *,char *))78 input_read_chunk(input_state *input, void handle_read(input_state *, char *))
79 {
80 debug("Got request to read a chunk from standard input");
81
82 if (!input->eof) {
83 input->read_handler = handle_read;
84 ev_io_start(EV_DEFAULT_UC_ &input->watcher);
85 ev_invoke(EV_DEFAULT_UC_ &input->watcher, EV_CUSTOM);
86 } else {
87 debug("There's no (more) data available");
88 if (input->eof_handler != NULL)
89 input->eof_handler(input);
90 input_stop(input);
91 }
92 }
93
94 void
input_stop(input_state * input)95 input_stop(input_state *input)
96 {
97 debug("Stopping standard input reader");
98
99 if (ev_is_active(&input->watcher))
100 ev_io_stop(EV_DEFAULT_UC_ &input->watcher);
101
102 buffer_free(input->buffer);
103 free(input);
104 }
105
106 void
input_on_eof(input_state * input,void handle_eof (input_state *))107 input_on_eof(input_state *input, void handle_eof(input_state *))
108 {
109 input->eof_handler = handle_eof;
110 }
111
112 /*
113 * Static functions.
114 */
115
116 static void
read_cb(EV_P_ ev_io * w,int revents)117 read_cb(EV_P_ ev_io *w, int revents __attribute__((__unused__)))
118 {
119 input_state *input = w->data;
120 char *chunk;
121 ssize_t n;
122
123 do {
124 if ((chunk = buffer_read_chunk(input->buffer, input->separator))
125 != NULL) {
126 debug("Got complete chunk from standard input");
127 ev_io_stop(EV_A_ w);
128 input->read_handler(input, chunk);
129 break;
130 }
131 if ((n = read(input->fd, input->input, sizeof(input->input)))
132 < 0) {
133 if (errno == EAGAIN
134 #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
135 || errno == EWOULDBLOCK
136 #endif
137 || errno == EINTR)
138 debug("Cannot read from standard input: %m");
139 else
140 die("Cannot read from standard input: %m");
141 } else if (n == 0) {
142 size_t len = buffer_size(input->buffer);
143
144 debug("Got EOF from standard input");
145 input->eof = true;
146 if (len > 0) {
147 debug("Providing the available data");
148 ev_io_stop(EV_A_ w);
149 chunk = xmalloc(len + 1);
150 (void)buffer_read(input->buffer, chunk, len);
151 chunk[len] = '\0';
152 input->read_handler(input, chunk);
153 } else {
154 debug("No (more) data available");
155 if (input->eof_handler != NULL)
156 input->eof_handler(input);
157 input_stop(input);
158 }
159 } else {
160 debug("Got %zd bytes from standard input", n);
161 buffer_append(input->buffer, input->input, (size_t)n);
162 }
163 } while (n > 0);
164 }
165
166 /* vim:set joinspaces noexpandtab textwidth=80 cinoptions=(4,u0: */
167