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