1 #include <stdlib.h>
2 #include <unistd.h>
3 #include <string.h>
4 #include <ctype.h>
5 #include <fcntl.h>
6 #include <poll.h>
7 #include <errno.h>
8 #include "context.h"
9
init_event(ctx_t * ctx)10 void init_event(ctx_t * ctx) {
11 ctx->event.line = NULL;
12 ctx->event.line_len = 0;
13 ctx->event.line_cap = 0;
14
15 ctx->event.args = NULL;
16 ctx->event.args_len = 0;
17 ctx->event.args_cap = 0;
18 }
19
cleanup_event(ctx_t * ctx)20 void cleanup_event(ctx_t * ctx) {
21 free(ctx->event.line);
22 free(ctx->event.args);
23 }
24
25 #define ARGS_MIN_CAP 8
event_args_push(ctx_t * ctx,char * arg)26 void event_args_push(ctx_t * ctx, char * arg) {
27 if (ctx->event.args_len == ctx->event.args_cap) {
28 size_t new_cap = ctx->event.args_cap * 2;
29 if (new_cap == 0) new_cap = ARGS_MIN_CAP;
30
31 char ** new_args = realloc(ctx->event.args, sizeof (char *) * new_cap);
32 if (new_args == NULL) {
33 log_error("event_args_push: failed to grow args array for option stream line\n");
34 exit_fail(ctx);
35 }
36
37 ctx->event.args = new_args;
38 ctx->event.args_cap = new_cap;
39 }
40
41 ctx->event.args[ctx->event.args_len++] = arg;
42 }
43
44 #define LINE_MIN_RESERVE 1024
event_line_reserve(ctx_t * ctx)45 void event_line_reserve(ctx_t * ctx) {
46 if (ctx->event.line_cap - ctx->event.line_len < LINE_MIN_RESERVE) {
47 size_t new_cap = ctx->event.line_cap * 2;
48 if (new_cap == 0) new_cap = LINE_MIN_RESERVE;
49
50 char * new_line = realloc(ctx->event.line, sizeof (char) * new_cap);
51 if (new_line == NULL) {
52 log_error("event_line_reserve: failed to grow line buffer for option stream line\n");
53 exit_fail(ctx);
54 }
55
56 ctx->event.line = new_line;
57 ctx->event.line_cap = new_cap;
58 }
59 }
60
61 enum parse_state {
62 BEFORE_ARG,
63 ARG_START,
64 QUOTED_ARG,
65 UNQUOTED_ARG
66 };
event_stdin_line(ctx_t * ctx,char * line)67 void event_stdin_line(ctx_t * ctx, char * line) {
68 char * arg_start = NULL;
69 char quote_char = '\0';
70
71 log_debug(ctx, "event_stdin_line: got line '%s'\n", line);
72
73 ctx->event.args_len = 0;
74
75 enum parse_state state = BEFORE_ARG;
76 while (*line != '\0') {
77 switch (state) {
78 case BEFORE_ARG:
79 if (isspace(*line)) {
80 line++;
81 } else {
82 state = ARG_START;
83 }
84 break;
85
86 case ARG_START:
87 if (*line == '"' || *line == '\'') {
88 quote_char = *line;
89 line++;
90
91 arg_start = line;
92 state = QUOTED_ARG;
93 } else {
94 arg_start = line;
95 state = UNQUOTED_ARG;
96 }
97 break;
98
99 case QUOTED_ARG:
100 if (*line != quote_char) {
101 line++;
102 } else {
103 *line = '\0';
104 event_args_push(ctx, arg_start);
105 line++;
106 state = BEFORE_ARG;
107 }
108 break;
109
110 case UNQUOTED_ARG:
111 if (!isspace(*line)) {
112 line++;
113 } else {
114 *line = '\0';
115 event_args_push(ctx, arg_start);
116 line++;
117 state = BEFORE_ARG;
118 }
119 break;
120 }
121 }
122
123 if (state == QUOTED_ARG) {
124 log_error("event_stdin_line: unmatched quote in argument\n");
125 }
126
127 if (state == QUOTED_ARG || state == UNQUOTED_ARG) {
128 event_args_push(ctx, arg_start);
129 }
130
131 log_debug(ctx, "event_stdin_line: parsed %zd arguments\n", ctx->event.args_len);
132
133 parse_opt(ctx, ctx->event.args_len, ctx->event.args);
134 }
135
event_stdin_data(ctx_t * ctx)136 void event_stdin_data(ctx_t * ctx) {
137 while (true) {
138 event_line_reserve(ctx);
139
140 size_t cap = ctx->event.line_cap;
141 size_t len = ctx->event.line_len;
142 ssize_t num = read(STDIN_FILENO, ctx->event.line + len, cap - len);
143 if (num == -1 && errno == EWOULDBLOCK) {
144 break;
145 } else if (num == -1) {
146 log_error("event_stdin_data: failed to read data from stdin\n");
147 exit_fail(ctx);
148 } else {
149 ctx->event.line_len += num;
150 }
151 }
152
153 char * line = ctx->event.line;
154 size_t len = ctx->event.line_len;
155 for (size_t i = 0; i < len; i++) {
156 if (line[i] == '\0') {
157 line[i] = ' ';
158 } else if (line[i] == '\n') {
159 line[i] = '\0';
160 event_stdin_line(ctx, line);
161 memmove(line, line + (i + 1), len - (i + 1));
162 ctx->event.line_len -= i + 1;
163 break;
164 }
165 }
166 }
167
event_loop(ctx_t * ctx)168 void event_loop(ctx_t * ctx) {
169 struct pollfd fds[2];
170 fds[0].fd = wl_display_get_fd(ctx->wl.display);
171 fds[0].events = POLLIN;
172 fds[0].revents = 0;
173 fds[1].fd = STDIN_FILENO;
174 fds[1].events = POLLIN;
175 fds[1].revents = 0;
176
177 if (!ctx->opt.stream) {
178 // disable polling for stdin if stream mode not enabled
179 fds[1].fd = -1;
180 }
181
182 int flags = fcntl(STDIN_FILENO, F_GETFL, 0);
183 flags |= O_NONBLOCK;
184 fcntl(STDIN_FILENO, F_SETFL, flags);
185
186 while (poll(fds, 2, -1) >= 0 && !ctx->wl.closing) {
187 if (fds[0].revents & POLLIN) {
188 if (wl_display_dispatch(ctx->wl.display) == -1) {
189 ctx->wl.closing = true;
190 }
191 }
192
193 if (fds[1].revents & POLLIN) {
194 event_stdin_data(ctx);
195 }
196
197 fds[0].revents = 0;
198 fds[1].revents = 0;
199 wl_display_flush(ctx->wl.display);
200 }
201 }
202