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